diff --git a/.changeset/config.json b/.changeset/config.json new file mode 100644 index 0000000..fe6791c --- /dev/null +++ b/.changeset/config.json @@ -0,0 +1,16 @@ +{ + "$schema": "https://unpkg.com/@changesets/config@3.1.1/schema.json", + "changelog": "@changesets/cli/changelog", + "commit": false, + "fixed": [], + "linked": [], + "access": "public", + "baseBranch": "main", + "updateInternalDependencies": "patch", + "ignore": [ + "agentic-react-vite-playground", + "agentic-react-webpack-playground", + "agentic-react-next-playground", + "agentic-react-nx-module-federation-playground" + ] +} diff --git a/.changeset/custom-tuning-modal-api.md b/.changeset/custom-tuning-modal-api.md new file mode 100644 index 0000000..ad88c47 --- /dev/null +++ b/.changeset/custom-tuning-modal-api.md @@ -0,0 +1,8 @@ +--- +"@agentic-react/core": minor +"@agentic-react/vite": minor +"@agentic-react/webpack": minor +"@agentic-react/next": minor +--- + +Add customizable tuning modal slots, wrapper extensions, and adapter toolkit configuration support. diff --git a/.cursor/mcp.json b/.cursor/mcp.json index 21d4741..f5ac82a 100644 --- a/.cursor/mcp.json +++ b/.cursor/mcp.json @@ -1,7 +1,7 @@ { "mcpServers": { - "user-profile-app": { - "url": "http://localhost:3000/sse" + "vite-react-app": { + "url": "http://127.0.0.1:51423/sse" } } -} \ No newline at end of file +} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..fa274ed --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,51 @@ +name: Release + +on: + push: + branches: [main] + +concurrency: + group: release-${{ github.ref }} + cancel-in-progress: false + +permissions: + contents: write + pull-requests: write + id-token: write + +jobs: + release: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup PNPM + uses: pnpm/action-setup@v4 + with: + version: 10 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: 22 + registry-url: https://registry.npmjs.org + cache: pnpm + + - name: Install dependencies + run: pnpm install --frozen-lockfile + + - name: Build packages + run: pnpm run build + + - name: Create version PR or publish + uses: changesets/action@v1 + with: + version: pnpm run version-packages + publish: pnpm run release + title: "chore: version packages" + commit: "chore: version packages" + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + NPM_TOKEN: ${{ secrets.NPM_TOKEN }} + NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} diff --git a/.gitignore b/.gitignore index 6bbc3b7..b9f700b 100644 --- a/.gitignore +++ b/.gitignore @@ -64,6 +64,13 @@ typings/ # next.js build output .next +.vite-react-mcp-webpack/ +**/.vite-react-mcp-webpack/ + +# Playwright output +test-results/ +playwright-report/ +blob-report/ dist @@ -86,8 +93,6 @@ playground/package-lock.json playground/node_modules playground/pnpm-lock.yaml -pnpm-lock.yaml - # for scripts clones @@ -103,4 +108,4 @@ dist/ **/.yalc/** yalc.lock **/yalc.lock -.yalc/ \ No newline at end of file +.yalc/ diff --git a/.npmignore b/.npmignore new file mode 100644 index 0000000..57e3388 --- /dev/null +++ b/.npmignore @@ -0,0 +1,38 @@ +# Workspace-only files. Publishable packages live under packages/* and use +# their own package.json "files" allowlists. +playground/ +packages/ +scripts/ + +# Local dependencies and generated artifacts +node_modules/ +dist/ +coverage/ +.nyc_output/ +.turbo/ +.vite-inspect/ +test-results/ +playwright-report/ +blob-report/ + +# Local app/tooling state +.cursor/ +.idea/ +.cache/ +.npm/ +.yalc/ +**/.yalc/ +yalc.lock +**/yalc.lock + +# Logs and packed tarballs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +*.tgz + +# Environment and OS files +.env +.DS_Store +*.timestamp-*.* diff --git a/CHANGELOG b/CHANGELOG index e382bf1..61fc49b 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,7 +1,15 @@ +## 0.1.0 + +Initial Agentic React scoped monorepo release under `@agentic-react/*`. + +- Publish `@agentic-react/core` as the bundler-agnostic runtime and MCP primitive package. +- Publish `@agentic-react/vite`, `@agentic-react/webpack`, and `@agentic-react/next` as local-dev adapter packages. +- Rename the browser runtime globals, generated files, and playground packages to Agentic React names. + ## 0.3.0 Introduce Custom Tools - where users can define custom functions and declare as MCP tools. ## 0.2.4 -Introduce 4 tools running in browser runtime to get context of React Fibre tree. \ No newline at end of file +Introduce 4 tools running in browser runtime to get context of React Fibre tree. diff --git a/README.md b/README.md index 00f2d95..fd42657 100644 --- a/README.md +++ b/README.md @@ -1,205 +1,171 @@ -# vite-react-mcp +# Agentic React -[![npm version][npm-version-src]][npm-version-href] -[![npm downloads][npm-downloads-src]][npm-downloads-href] +Agentic React is a monorepo for React runtime inspection and local-dev MCP integrations. The packages are published under the `@agentic-react` namespace. -A Vite plugin that creates an MCP server to help LLMs understand your React App context +## Demos -## Features +### Single Select -- `highlight-component` - - description: Highlight React component based on the component name. - - params: - - `componentName`: string + -  +### Multiselect -- `get-component-states` - - description: Get the React component props, states, and contexts in JSON structure format. - - params: - - `componentName`: string + -  +## Packages -- `get-component-tree` - - description: Get the React component tree of the current page in ASCII format. - - params: - - `allComponent`: boolean, if truthy, return a tree for all components instead of your self-defined components only. +- `@agentic-react/core`: bundler-agnostic browser runtime, React selection toolkit, and MCP primitives. +- `@agentic-react/vite`: Vite adapter for local dev. +- `@agentic-react/webpack`: Webpack adapter for local dev. +- `@agentic-react/next`: Next.js adapter for local dev. -  +For full local-dev features, install the adapter for your bundler. The adapter depends on `@agentic-react/core` internally, so app users do not need to import both packages. -- `get-unnecessary-rerenders` - - description: Get the wasted re-rendered components of the current page. - - params: - - `timeframe`: number, if present, only get unnecessary renders within the last `timeframe` seconds. If not, get all unnecessary renders happened on the current page. - - `allComponent`: boolean, if truthy, get unnecessary renders for all components instead of self-defined components only. +## Local Dev Usage -  - -- Custom Tools - - You can now define your own tool functions in JS/TS in your Vite project, and inject it - to the plugin. - -  - -## Getting Started - -### Installation +### Vite ```bash -pnpm install vite-react-mcp -D +pnpm install @agentic-react/vite -D ``` -You also need `@babel/preset-react` installed, as this plugins traverses AST to collect your React components names. +```ts +// vite.config.ts +import { defineConfig } from 'vite'; +import AgenticReact from '@agentic-react/vite'; -```bash -pnpm install @babel/preset-react +export default defineConfig({ + plugins: [AgenticReact()], +}); ``` -### Usage +The Vite adapter injects the core browser runtime, attaches the runtime bridge to the dev server, and exposes MCP over: -#### Built-in tools +```text +http://localhost:/mcp +``` -```ts -// vite.config.ts -import ReactMCP from 'vite-react-mcp' +### Webpack -export default defineConfig({ - plugins: [ReactMCP()], -}) +```bash +pnpm install @agentic-react/webpack -D ``` -#### Custom tools +```js +// webpack.config.mjs +import withAgenticReactWebpack from '@agentic-react/webpack'; -Define your own tool in your Vite project, e.g. +export default (env, argv) => + withAgenticReactWebpack(config, { mode: argv.mode }); +``` -```ts -// any ts file in your Vite project -import type { ToolResultValue } from 'vite-react-mcp'; +The Webpack adapter prepends a generated runtime entry, attaches the runtime bridge to webpack-dev-server, and exposes MCP at: -export default function myCustomTool(args: { message: string }): ToolResultValue { - const { message } = args; - console.log(`[custom-tool/log1] ${message}`); - return { - success: true, - message: `Log1 received: ${message}`, - }; -} +```text +http://localhost:/mcp ``` -```ts -// vite.config.ts -import ReactMCP from 'vite-react-mcp' -import log1 from 'path/to/your/module' +### Next.js -export default defineConfig({ - plugins: [ReactMCP({ - customTools: [ - { - name: 'log1', - description: 'Log1', - schema: z.object({ - message: z.string(), - }), - clientFunction: log1, - } - ] - })], -}) +```bash +pnpm install @agentic-react/next -D ``` -Then run your app in dev. -> Note: vite-react-mcp is meant to be used in dev environment only +```js +// next.config.mjs +import withAgenticReactNext from '@agentic-react/next'; -At this point, you already can access `window.__VITE_REACT_MCP_TOOLS__` to use the tools in Developer panel on your browser. +export default withAgenticReactNext(nextConfig); +``` -To expose it as an MCP server, setup MCP configuration in your MCP client. +The Next adapter injects the browser runtime through Next's Webpack config and starts a local bridge server. By default MCP is available at: -- For Cursor, create a `./cursor/mcp.json` at the root level of your react project. +```text +http://127.0.0.1:51426/mcp +``` - ```json - { - "mcpServers": { - "vite-react-mcp": { - "url": "http://localhost:3000/sse" - } - } - } - ``` +## Runtime-Only Usage - Make sure the port is the same as your react app +Use `@agentic-react/core` directly when you only need browser-side selection/runtime APIs and do not need local source-file lookup or MCP dev-server wiring. -- For Claude Desktop, it requires a bit of workaround. If you are interested, you can take a look at [this thread](https://github.com/orgs/modelcontextprotocol/discussions/16). +```bash +pnpm install @agentic-react/core +``` - The reason is Claude MCP Client does execution based on command, while what we have here is HTTP based API. You need to write a script acting as a bridge to make it look like execution based. +```ts +import { createSelectionToolkit } from '@agentic-react/core'; +const toolkit = createSelectionToolkit(); +toolkit.enable(); +``` -### How it works +Runtime-only mode can inspect the live browser tree, but it cannot read or edit local files. Source lookup and local MCP transport are the adapter layer's job. -This plugin bridges an MCP server with your React app's runtime, enabling LLMs to inspect and interact with your components in a live browser session. Here's the full picture: +## What Adapters Add -#### Architecture overview +`@agentic-react/core` runs in the browser and can select elements, inspect React fibers, highlight components, format selection context, and expose `window.__AGENTIC_REACT__` / `window.__AGENTIC_REACT_TOOLS__`. -``` -MCP Client (Cursor, etc.) - │ - │ SSE + HTTP POST (/sse, /messages) - ▼ -Vite Dev Server (Node.js) - ├── MCP Server (handles tool listing & calls) - └── HMR WebSocket - │ - │ custom events via Vite HMR - ▼ - Browser - ├── overlay.js (tool implementations + WebSocket listeners) - ├── bippy (React fiber access via __REACT_DEVTOOLS_GLOBAL_HOOK__) - └── window.__VITE_REACT_MCP_TOOLS__ (tool registry) -``` +Bundler adapters add local-dev capabilities that the runtime cannot know on its own: -#### Step by step +- inject the core runtime automatically +- attach a local MCP Streamable HTTP `/mcp` endpoint +- bridge MCP calls from Node to the browser runtime +- provide local source-root context for source lookup +- keep dev-only tooling out of production bundles -1. **Babel AST pass (build time)** — During Vite's `transform` hook, each `.js/.jsx/.ts/.tsx` file is parsed with `@babel/preset-react` to collect all user-defined React component names (function components, class components, `memo`/`forwardRef` wrappers, etc.). These names are stored and later injected into the page as `window.__REACT_COMPONENTS__`. +## Custom Tools -2. **Browser-side injection (runtime)** — At dev startup, scripts are injected into the HTML ``: - - The collected component names are set on `window.__REACT_COMPONENTS__`. - - `overlay.js` is loaded as a module. It uses [bippy](https://github.com/nicholascostadev/bippy) to install a `__REACT_DEVTOOLS_GLOBAL_HOOK__` on `window`, giving direct access to the React fiber tree **without requiring the React DevTools browser extension**. It also hooks into React's commit lifecycle (`onCommitFiberRoot`) to continuously track fiber roots and detect unnecessary re-renders. - - The overlay exposes all built-in tool functions (highlight, tree, states, re-renders) on `window.__VITE_REACT_MCP_TOOLS__` and registers Vite HMR listeners (`import.meta.hot.on(...)`) for each tool. +Adapters accept browser-side custom tools. Import shared types from the adapter package you use: -3. **MCP server (SSE transport)** — When the Vite dev server starts, the plugin attaches two HTTP endpoints: - - `GET /sse` — Establishes a long-lived Server-Sent Events connection with the MCP client. - - `POST /messages?sessionId=` — Receives JSON-RPC tool-call requests from the MCP client. +```ts +import type { ToolResultValue } from '@agentic-react/vite'; - The MCP server advertises all built-in and custom tools (with JSON Schema descriptions derived from their Zod schemas) so any MCP-compatible client can discover and invoke them. +export default function logMessage(args: { message: string }): ToolResultValue { + return { + success: true, + message: `Received: ${args.message}`, + }; +} +``` -4. **Tool call flow** — When an MCP client invokes a tool (e.g. `highlight-component`): - 1. The MCP server receives the JSON-RPC request via the `/messages` endpoint. - 2. It validates the arguments against the tool's Zod schema. - 3. It sends a custom event through Vite's HMR WebSocket to the browser (e.g. `highlight-component` with the serialized args). - 4. The browser-side HMR listener picks up the event, executes the tool function against the live React fiber tree, and sends the result back via another HMR event (e.g. `highlight-component-response`). - 5. The MCP server awaits this response, wraps it in a JSON-RPC result, and streams it back to the MCP client over SSE. +```ts +import { defineConfig } from 'vite'; +import AgenticReact from '@agentic-react/vite'; +import { z } from 'zod'; +import logMessage from './src/tools/logMessage'; -5. **Custom tools** — User-defined tools follow the same WebSocket round-trip. Their handler functions are registered in the browser at startup via dynamic `import()` or inline injection, and corresponding HMR listeners are created automatically. +export default defineConfig({ + plugins: [ + AgenticReact({ + customTools: [ + { + name: 'log-message', + description: 'Log a message in the browser runtime', + schema: z.object({ message: z.string() }), + clientFunction: logMessage, + }, + ], + }), + ], +}); +``` -### Test +## Development ```bash -pnpm run playground +pnpm run build +pnpm run playground:vite +pnpm run playground:webpack +pnpm run playground:next +pnpm run playground:nx-mf ``` -The playground contains a simple user profile application to test React component interactions. +For e2e automation, Playwright uses fixed local ports configured in the playground package configs. ## Acknowledgement -This project is inspired by [vite-plugin-vue-mcp](https://github.com/webfansplz/vite-plugin-vue-mcp). Thanks for the awesome idea bridging mcp and devtools. - +This project is inspired by [vite-plugin-vue-mcp](https://github.com/webfansplz/vite-plugin-vue-mcp). ## License MIT - -[npm-version-src]: https://img.shields.io/npm/v/vite-react-mcp?style=flat&colorA=080f12&colorB=1fa669 -[npm-version-href]: https://npmjs.com/package/vite-react-mcp -[npm-downloads-src]: https://img.shields.io/npm/dm/vite-react-mcp?style=flat&colorA=080f12&colorB=1fa669 -[npm-downloads-href]: https://npmjs.com/package/vite-react-mcp diff --git a/biome.json b/biome.json index 7d37368..7b5f320 100644 --- a/biome.json +++ b/biome.json @@ -3,19 +3,27 @@ "vcs": { "enabled": false, "clientKind": "git", - "useIgnoreFile": false + "useIgnoreFile": true }, "files": { "ignoreUnknown": false, "include": [ - "src/**/*.ts", - "src/**/*.js", + "packages/**/*.ts", + "packages/**/*.js", "playground/**/*.ts", "playground/**/*.tsx", "*.ts", "*.tsx" ], - "ignore": ["node_modules", "dist", "coverage", "css-to-tailwind.ts"] + "ignore": [ + "node_modules", + "dist", + "coverage", + ".next", + "playground/**/.next/**", + "playground/**/test-results/**", + "css-to-tailwind.ts" + ] }, "formatter": { "enabled": true, diff --git a/package.json b/package.json index 93d01e5..b6357fc 100644 --- a/package.json +++ b/package.json @@ -1,58 +1,56 @@ { - "name": "vite-react-mcp", + "name": "agentic-react-monorepo", + "private": true, "type": "module", - "version": "0.3.0", - "description": "Vite plugin for React MCP", - "main": "dist/index.js", - "workspaces": [ - "packages/*", - "playground/*" - ], "scripts": { - "test": "echo \"Error: no test specified\" && exit 1", - "dev": "pnpm -r run start", - "build": "npx shx rm -rf dist && tsc && npx shx cp CHANGELOG dist/CHANGELOG", - "build:src": "npx shx rm -rf dist && tsc --project tsconfig.json --outDir dist --rootDir src && npx shx cp CHANGELOG dist/CHANGELOG", - "playground": "pnpm --filter user-profile-app run start", + "build": "node scripts/with-build-lock.mjs pnpm -r --filter './packages/**' run build", + "build:src": "pnpm --filter @agentic-react/core run build:src", + "dev": "pnpm -r --parallel --if-present run start", + "playground": "pnpm --filter agentic-react-vite-playground run start", + "playground:vite": "pnpm --filter agentic-react-vite-playground run start", + "playground:next": "pnpm --filter agentic-react-next-playground run start", + "playground:webpack": "pnpm --filter agentic-react-webpack-playground run start", + "playground:nx-mf": "pnpm -C playground/agentic-react-nx-module-federation-playground run start", "format": "pnpm biome format --write", "lint": "pnpm biome lint --write", - "check": "pnpm biome check --write" + "check": "pnpm biome check --write", + "changeset": "changeset", + "changeset:status": "changeset status --since=origin/main", + "version-packages": "changeset version", + "release": "changeset publish", + "test": "pnpm run test:e2e", + "test:e2e": "node scripts/run-e2e.mjs" }, - "keywords": [ - "vite", - "react", - "mcp", - "devtools", - "llm", - "plugin" - ], - "author": { - "name": "jazelly", - "email": "xzha4350@gmail.com" + "pnpm": { + "overrides": { + "@remix-run/router": "1.23.2", + "caniuse-lite": "1.0.30001793", + "fast-uri": "3.1.2", + "js-yaml": "4.1.1", + "read-yaml-file@1.1.0>js-yaml": "3.14.1", + "lodash": "4.18.1", + "minimatch": "3.1.5", + "next": "15.5.18", + "path-to-regexp@>=8.0.0 <8.4.0": "8.4.2", + "picomatch@2.3.1": "2.3.2", + "picomatch@4.0.2": "4.0.4", + "postcss": "8.5.15", + "qs": "6.15.2", + "rollup": "4.60.4", + "serialize-javascript": "7.0.5", + "uuid": "11.1.1", + "vite": "6.4.2", + "webpack": "5.107.1", + "webpack-dev-server": "5.2.4", + "ws": "8.21.0", + "yaml": "1.10.3" + } }, - "files": [ - "dist" - ], - "license": "MIT", "packageManager": "pnpm@10.6.5", - "peerDependencies": { - "@babel/preset-react": ">=6", - "vite": ">=4" - }, "devDependencies": { - "@babel/core": "^7.26.10", - "@babel/types": "^7.27.0", "@biomejs/biome": "1.9.4", - "@rollup/pluginutils": "^5.1.4", - "@types/react-reconciler": "0.26.7", + "@changesets/cli": "^2.31.0", "shx": "0.4.0", - "typescript": "^5.8.2", - "vite": "^6.2.3" - }, - "dependencies": { - "@modelcontextprotocol/sdk": "1.7.0", - "bippy": "0.3.8", - "zod": "^3.22.4", - "zod-to-json-schema": "^3.23.5" + "typescript": "^5.8.2" } } diff --git a/packages/core/LICENSE b/packages/core/LICENSE new file mode 100644 index 0000000..102207d --- /dev/null +++ b/packages/core/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2023 webfansplz + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/packages/core/README.md b/packages/core/README.md new file mode 100644 index 0000000..13a8e7b --- /dev/null +++ b/packages/core/README.md @@ -0,0 +1,20 @@ +# @agentic-react/core + +Bundler-agnostic Agentic React runtime primitives. + +Use this package when you need browser-side React selection and inspection without Vite, Webpack, or Next.js adapter wiring. + +```ts +import { createSelectionToolkit } from '@agentic-react/core'; + +const toolkit = createSelectionToolkit(); +toolkit.enable(); +``` + +For full local-dev MCP features, install an adapter instead: + +- `@agentic-react/vite` +- `@agentic-react/webpack` +- `@agentic-react/next` + +Adapters depend on `@agentic-react/core` internally and add runtime injection, bridge transport, MCP endpoints, and source-root context. diff --git a/packages/core/package.json b/packages/core/package.json new file mode 100644 index 0000000..abac7b5 --- /dev/null +++ b/packages/core/package.json @@ -0,0 +1,84 @@ +{ + "name": "@agentic-react/core", + "type": "module", + "version": "0.1.0", + "description": "Bundler-agnostic Agentic React runtime selection and MCP primitives", + "main": "dist/index.js", + "types": "dist/index.d.ts", + "scripts": { + "build": "shx rm -rf dist && tsc && shx cp -r src/assets dist/assets && shx cp ../../CHANGELOG dist/CHANGELOG", + "build:src": "shx rm -rf dist && tsc --project tsconfig.json --outDir dist --rootDir src && shx cp -r src/assets dist/assets && shx cp ../../CHANGELOG dist/CHANGELOG" + }, + "keywords": [ + "agentic-react", + "react", + "mcp", + "visual-edit", + "agents", + "devtools", + "llm", + "runtime", + "selection" + ], + "author": { + "name": "jazelly", + "email": "xzha4350@gmail.com" + }, + "files": [ + "dist", + "LICENSE", + "README.md" + ], + "publishConfig": { + "access": "public" + }, + "license": "MIT", + "devDependencies": { + "@types/react-reconciler": "0.26.7" + }, + "dependencies": { + "@modelcontextprotocol/sdk": "1.29.0", + "bippy": "^0.5.39", + "zod": "^3.22.4", + "zod-to-json-schema": "^3.23.5", + "ws": "^8.21.0" + }, + "exports": { + ".": { + "types": "./dist/index.d.ts", + "default": "./dist/index.js" + }, + "./bridge": { + "types": "./dist/bridge/server.d.ts", + "default": "./dist/bridge/server.js" + }, + "./mcp": { + "types": "./dist/mcp/index.d.ts", + "default": "./dist/mcp/index.js" + }, + "./overlay": { + "types": "./dist/overlay.d.ts", + "default": "./dist/overlay.js" + }, + "./select": { + "types": "./dist/select.d.ts", + "default": "./dist/select.js" + }, + "./shared/const": { + "types": "./dist/shared/const.d.ts", + "default": "./dist/shared/const.js" + }, + "./shared/custom-tools-script": { + "types": "./dist/shared/custom_tools_script.d.ts", + "default": "./dist/shared/custom_tools_script.js" + }, + "./shared/protocol": { + "types": "./dist/shared/protocol.d.ts", + "default": "./dist/shared/protocol.js" + }, + "./shared/types": { + "types": "./dist/shared/types.d.ts", + "default": "./dist/shared/types.js" + } + } +} diff --git a/packages/core/src/assets/agentic-react-toolkit-logo.png b/packages/core/src/assets/agentic-react-toolkit-logo.png new file mode 100644 index 0000000..029bf21 Binary files /dev/null and b/packages/core/src/assets/agentic-react-toolkit-logo.png differ diff --git a/packages/core/src/bridge/server.ts b/packages/core/src/bridge/server.ts new file mode 100644 index 0000000..467c009 --- /dev/null +++ b/packages/core/src/bridge/server.ts @@ -0,0 +1,289 @@ +import { randomUUID } from 'node:crypto'; +import { type WebSocket, WebSocketServer } from 'ws'; +import { + BRIDGE_WS_PATH, + type BridgeMessage, + type BridgeRequestEvent, +} from '../shared/protocol.js'; + +interface PendingRequest { + resolve: (value: unknown) => void; + reject: (error: Error) => void; + socket: WebSocket; + timeoutId: NodeJS.Timeout; +} + +const DEFAULT_TIMEOUT_MS = 10000; +const BRIDGE_CONNECT_POLL_MS = 50; + +interface RuntimeBridgeRequestOptions { + acceptResponse?: (value: unknown) => boolean; + broadcast?: boolean; + timeoutMs?: number; +} + +const isBridgeMessage = (value: unknown): value is BridgeMessage => { + if (!value || typeof value !== 'object') { + return false; + } + + const message = value as { type?: unknown }; + return ( + message.type === 'bridge:request' || message.type === 'bridge:response' + ); +}; + +export class RuntimeBridgeServer { + private wsServer: WebSocketServer | null = null; + private activeSocket: WebSocket | null = null; + private readonly pendingRequests = new Map(); + private readonly sockets = new Set(); + + attach(httpServer: any) { + this.wsServer = new WebSocketServer({ noServer: true }); + + httpServer.on('upgrade', (request, socket, head) => { + const requestUrl = request.url || ''; + const pathname = requestUrl.split('?')[0]; + + if (pathname !== BRIDGE_WS_PATH || !this.wsServer) { + return; + } + + this.wsServer.handleUpgrade(request, socket, head, (websocket) => { + this.wsServer?.emit('connection', websocket, request); + }); + }); + + this.wsServer.on('connection', (websocket) => { + this.sockets.add(websocket); + this.activeSocket = websocket; + + websocket.on('message', (messageBuffer) => { + try { + const parsedMessage = JSON.parse(messageBuffer.toString()) as unknown; + + if (!isBridgeMessage(parsedMessage)) { + return; + } + + if (parsedMessage.type !== 'bridge:response') { + return; + } + + const pendingRequest = this.pendingRequests.get(parsedMessage.id); + if (!pendingRequest) { + return; + } + + this.pendingRequests.delete(parsedMessage.id); + clearTimeout(pendingRequest.timeoutId); + + if (parsedMessage.ok) { + pendingRequest.resolve(parsedMessage.payload); + return; + } + + pendingRequest.reject( + new Error(parsedMessage.error || 'Bridge response failed'), + ); + } catch (_error) { + // ignore malformed response + } + }); + + websocket.on('close', () => { + this.sockets.delete(websocket); + if (this.activeSocket === websocket) { + this.activeSocket = null; + } + + for (const [requestId, pendingRequest] of this.pendingRequests) { + if (pendingRequest.socket !== websocket) { + continue; + } + clearTimeout(pendingRequest.timeoutId); + pendingRequest.reject( + new Error('Bridge connection closed before receiving response'), + ); + this.pendingRequests.delete(requestId); + } + }); + }); + } + + async request( + event: BridgeRequestEvent, + payload: unknown, + optionsOrTimeoutMs: + | RuntimeBridgeRequestOptions + | number = DEFAULT_TIMEOUT_MS, + ): Promise { + const options = + typeof optionsOrTimeoutMs === 'number' + ? { timeoutMs: optionsOrTimeoutMs } + : optionsOrTimeoutMs; + const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS; + const sockets = await this.waitForOpenSockets(timeoutMs); + const orderedSockets = this.orderSocketsByActivity(sockets); + + if (options.broadcast) { + const responses = await Promise.allSettled( + orderedSockets.map((socket) => + this.requestFromSocket(socket, event, payload, timeoutMs), + ), + ); + const fulfilledResponses = responses + .filter( + (response): response is PromiseFulfilledResult => + response.status === 'fulfilled', + ) + .map((response) => response.value); + const acceptedResponse = fulfilledResponses.find( + (response) => + !options.acceptResponse || options.acceptResponse(response), + ); + if (acceptedResponse !== undefined) { + return acceptedResponse; + } + if (fulfilledResponses.length > 0) { + return fulfilledResponses[0]; + } + const rejectedResponse = responses.find( + (response): response is PromiseRejectedResult => + response.status === 'rejected', + ); + throw rejectedResponse?.reason instanceof Error + ? rejectedResponse.reason + : new Error(`Bridge request failed for ${event}`); + } + + let firstResponse: unknown; + let hasFirstResponse = false; + let lastError: Error | null = null; + + for (const socket of orderedSockets) { + try { + const response = await this.requestFromSocket( + socket, + event, + payload, + timeoutMs, + ); + if (!hasFirstResponse) { + firstResponse = response; + hasFirstResponse = true; + } + if (!options.acceptResponse || options.acceptResponse(response)) { + return response; + } + } catch (error) { + lastError = + error instanceof Error ? error : new Error('Bridge request failed'); + } + } + + if (hasFirstResponse) { + return firstResponse; + } + if (lastError) { + throw lastError; + } + throw new Error(`Bridge request failed for ${event}`); + } + + private async requestFromSocket( + socket: WebSocket, + event: BridgeRequestEvent, + payload: unknown, + timeoutMs: number, + ): Promise { + const requestId = randomUUID(); + + return new Promise((resolve, reject) => { + const timeoutId = setTimeout(() => { + this.pendingRequests.delete(requestId); + reject(new Error(`Bridge request timed out for ${event}`)); + }, timeoutMs); + + this.pendingRequests.set(requestId, { + resolve, + reject, + socket, + timeoutId, + }); + + const message: BridgeMessage = { + type: 'bridge:request', + id: requestId, + event, + payload, + }; + + try { + socket.send(JSON.stringify(message)); + } catch (error) { + this.pendingRequests.delete(requestId); + clearTimeout(timeoutId); + reject( + error instanceof Error ? error : new Error('Bridge send failed'), + ); + } + }); + } + + private getOpenSockets(): WebSocket[] { + const sockets = Array.from(this.sockets).filter( + (socket) => socket.readyState === socket.OPEN, + ); + if ( + this.activeSocket && + this.activeSocket.readyState !== this.activeSocket.OPEN + ) { + this.activeSocket = null; + } + return sockets; + } + + private orderSocketsByActivity(sockets: WebSocket[]): WebSocket[] { + const activeSocket = + this.activeSocket && + this.activeSocket.readyState === this.activeSocket.OPEN + ? this.activeSocket + : null; + const newestFirst = [...sockets].reverse(); + + if (!activeSocket) { + return newestFirst; + } + + return [ + activeSocket, + ...newestFirst.filter((socket) => socket !== activeSocket), + ]; + } + + private async waitForOpenSockets(timeoutMs: number): Promise { + const openSockets = this.getOpenSockets(); + if (openSockets.length > 0) { + return openSockets; + } + + return new Promise((resolve, reject) => { + const startedAt = Date.now(); + const intervalId = setInterval(() => { + const nextOpenSockets = this.getOpenSockets(); + if (nextOpenSockets.length > 0) { + clearInterval(intervalId); + resolve(nextOpenSockets); + return; + } + + if (Date.now() - startedAt >= timeoutMs) { + clearInterval(intervalId); + reject(new Error('No active runtime bridge connection')); + } + }, BRIDGE_CONNECT_POLL_MS); + }); + } +} diff --git a/src/core/tools/component_highlighter.ts b/packages/core/src/core/tools/component_highlighter.ts similarity index 96% rename from src/core/tools/component_highlighter.ts rename to packages/core/src/core/tools/component_highlighter.ts index e37706d..b9b7386 100644 --- a/src/core/tools/component_highlighter.ts +++ b/packages/core/src/core/tools/component_highlighter.ts @@ -1,6 +1,6 @@ -import { target } from '../../shared/const'; -import { InvalidInputError } from '../../shared/errors'; -import { getDOMNodesByComponentName } from './util'; +import { target } from '../../shared/const.js'; +import { InvalidInputError } from '../../shared/errors.js'; +import { getDOMNodesByComponentName } from './util.js'; export const highlightComponent = ( componentName: string, diff --git a/src/core/tools/component_state_viewer.ts b/packages/core/src/core/tools/component_state_viewer.ts similarity index 88% rename from src/core/tools/component_state_viewer.ts rename to packages/core/src/core/tools/component_state_viewer.ts index 241af06..5246d71 100644 --- a/src/core/tools/component_state_viewer.ts +++ b/packages/core/src/core/tools/component_state_viewer.ts @@ -1,12 +1,12 @@ import type { Fiber } from 'bippy'; -import { FiberNotFoundError } from '../../shared/errors'; +import { FiberNotFoundError } from '../../shared/errors.js'; import { getCurrentContexts, getCurrentProps, getCurrentStates, -} from '../../shared/util'; -import type { HookNode } from '../../types/internal'; -import { getFibersByComponentName } from './util'; +} from '../../shared/util.js'; +import type { HookNode } from '../../types/internal.js'; +import { getFibersByComponentName } from './util.js'; interface ComponentStateAndContext { props: Fiber['memoizedProps']; diff --git a/src/core/tools/component_viewer.ts b/packages/core/src/core/tools/component_viewer.ts similarity index 62% rename from src/core/tools/component_viewer.ts rename to packages/core/src/core/tools/component_viewer.ts index 4abf567..7beede3 100644 --- a/src/core/tools/component_viewer.ts +++ b/packages/core/src/core/tools/component_viewer.ts @@ -1,7 +1,13 @@ import type { Fiber } from 'bippy'; -import { FiberRootsNotFoundError } from '../../shared/errors'; -import { getAllFiberRoots, getCurrentProps, getDisplayNameForFiber } from '../../shared/util'; -import type { ComponentTreeNode } from '../../types/internal'; +import { FiberRootsNotFoundError } from '../../shared/errors.js'; +import { getAllFiberRoots, getDisplayNameForFiber } from '../../shared/util.js'; +import type { ComponentTreeNode } from '../../types/internal.js'; +import { isProjectOwnedFiber } from './source_ownership.js'; + +interface ComponentTreeNodeWithOwnership extends ComponentTreeNode { + isProjectOwned: boolean; + children: ComponentTreeNodeWithOwnership[]; +} /** * Recursively build a component tree with name from a fiber tree @@ -9,16 +15,20 @@ import type { ComponentTreeNode } from '../../types/internal'; * @param prevResult The previous result of the component tree * @returns The component tree */ -const buildComponentTree = (fiber: Fiber, prevResult: ComponentTreeNode) => { +const buildComponentTree = async ( + fiber: Fiber, + prevResult: ComponentTreeNodeWithOwnership, +) => { let fiberBase = fiber; while (fiberBase) { const componentName = getDisplayNameForFiber(fiberBase); const nodeResult = { name: componentName, children: [], + isProjectOwned: await isProjectOwnedFiber(fiberBase), }; prevResult.children.push(nodeResult); - buildComponentTree(fiberBase.child, nodeResult); + await buildComponentTree(fiberBase.child, nodeResult); fiberBase = fiberBase.sibling; } }; @@ -29,17 +39,15 @@ const buildComponentTree = (fiber: Fiber, prevResult: ComponentTreeNode) => { * @param trimTreeNode The tree node to trim to */ const trimComponentTree = ( - oldTreeNode: ComponentTreeNode, - trimTreeNode: ComponentTreeNode, + oldTreeNode: ComponentTreeNodeWithOwnership, + trimTreeNode: ComponentTreeNodeWithOwnership, ) => { let nextAttachNode = trimTreeNode; - if ( - oldTreeNode.name === 'createRoot()' || - window.__REACT_COMPONENTS__.includes(oldTreeNode.name) - ) { + if (oldTreeNode.name === 'createRoot()' || oldTreeNode.isProjectOwned) { nextAttachNode = { name: oldTreeNode.name, children: [], + isProjectOwned: oldTreeNode.isProjectOwned, }; trimTreeNode.children.push(nextAttachNode); } @@ -49,17 +57,24 @@ const trimComponentTree = ( } }; +const stripOwnership = ( + node: ComponentTreeNodeWithOwnership, +): ComponentTreeNode => ({ + name: node.name, + children: node.children.map(stripOwnership), +}); + /** * Given the component tree of current page * @param matchId An identifier to filter the component tree. Only components' name containing this id will be included */ -export const getComponentTree = ({ +export const getComponentTree = async ({ allComponents = false, debugMode = false, }: { allComponents?: boolean; debugMode?: boolean; -} = {}) => { +} = {}): Promise => { // Get all fiber roots const roots = getAllFiberRoots(); if (!roots || roots.length === 0) { @@ -70,12 +85,13 @@ export const getComponentTree = ({ console.debug('getComponentTree - roots', roots); } - const result: ComponentTreeNode = { + const result: ComponentTreeNodeWithOwnership = { name: '__BASE__', children: [], + isProjectOwned: false, }; for (const root of roots) { - buildComponentTree(root.current, result); + await buildComponentTree(root.current, result); } if (debugMode) { @@ -85,6 +101,7 @@ export const getComponentTree = ({ const trimmedResult = { name: '__BASE__', children: [], + isProjectOwned: false, }; if (debugMode) { @@ -99,7 +116,7 @@ export const getComponentTree = ({ if (debugMode) { console.debug('getComponentTree - trimmed result', trimmedResult); } - finalResult = trimmedResult.children[0]; + finalResult = trimmedResult.children[0] ?? trimmedResult; } // there will always a be a root node under __BASE__ @@ -107,5 +124,5 @@ export const getComponentTree = ({ finalResult.name = 'root'; } - return finalResult; + return stripOwnership(finalResult); }; diff --git a/packages/core/src/core/tools/selection_context.ts b/packages/core/src/core/tools/selection_context.ts new file mode 100644 index 0000000..5815f39 --- /dev/null +++ b/packages/core/src/core/tools/selection_context.ts @@ -0,0 +1,1061 @@ +import { + type Fiber, + getDisplayName, + getFiberFromHostInstance, + getFiberStack, + isCompositeFiber, + isInstrumentationActive, + traverseFiber, +} from 'bippy'; +import { + type StackFrame, + formatOwnerStack, + getOwnerStack, + getSource, + hasDebugStack, + isSourceFile, + normalizeFileName, + parseStack, +} from 'bippy/source'; +import { buildSelectionSourcePreview } from '../../shared/selection_context_format.js'; +import type { + SelectionContext, + SelectionExternalComponent, + SelectionResolvedSource, + SelectionSourceTraceFrame, + SelectionStackFrame, +} from '../../shared/types.js'; +import { getDisplayNameForFiber } from '../../shared/util.js'; +import { createElementSelector } from './selection_selector.js'; + +const MAX_HTML_PREVIEW_LENGTH = 1200; +const MAX_STACK_FRAMES = 24; +const MAX_SOURCE_FRAMES = 12; + +const NON_COMPONENT_PREFIXES = [ + '_', + '$', + 'motion.', + 'styled.', + 'Styled(', + 'chakra.', + 'ark.', + 'Primitive.', + 'Slot.', +]; + +const INTERNAL_COMPONENT_NAMES = new Set([ + 'Suspense', + 'Fragment', + 'StrictMode', + 'Profiler', + 'SuspenseList', + 'Activity', + 'Cache', + 'root', + 'createRoot()', + 'SlotClone', + 'InnerLayoutRouter', + 'RedirectErrorBoundary', + 'RedirectBoundary', + 'HTTPAccessFallbackErrorBoundary', + 'HTTPAccessFallbackBoundary', + 'LoadingBoundary', + 'ErrorBoundary', + 'InnerScrollAndFocusHandler', + 'ScrollAndFocusHandler', + 'RenderFromTemplateContext', + 'OuterLayoutRouter', + 'body', + 'html', + 'DevRootHTTPAccessFallbackBoundary', + 'AppDevOverlayErrorBoundary', + 'AppDevOverlay', + 'HotReload', + 'Router', + 'ErrorBoundaryHandler', + 'AppRouter', + 'ServerRoot', + 'SegmentStateProvider', + 'RootErrorBoundary', + 'LoadableComponent', + 'MotionDOMComponent', +]); + +const SERVER_COMPONENT_URL_PREFIXES = ['about://React/', 'rsc://React/']; +const SYMBOLICATION_TIMEOUT_MS = 5000; +const NODE_MODULES_PATTERN = /(?:^|[/\\])node_modules[/\\]/g; +const VITE_OPTIMIZED_DEPS_PATTERN = /[/\\]\.vite[/\\]deps[^/\\]*[/\\]/g; +const FILE_EXTENSION_PATTERN = /\.[mc]?[jt]sx?$/i; +const VITE_INTERNAL_CHUNK_PATTERN = /^chunk-[A-Za-z0-9_-]+$/; +const PATH_SEPARATOR_PATTERN = /[/\\]/; +const NAME_AT_VERSION_PATTERN = /^(.+?)@v?\d/; +const INTRINSIC_ELEMENT_NAMES = new Set([ + 'a', + 'abbr', + 'address', + 'area', + 'article', + 'aside', + 'audio', + 'b', + 'base', + 'bdi', + 'bdo', + 'blockquote', + 'body', + 'br', + 'button', + 'canvas', + 'caption', + 'cite', + 'code', + 'col', + 'colgroup', + 'data', + 'datalist', + 'dd', + 'del', + 'details', + 'dfn', + 'dialog', + 'div', + 'dl', + 'dt', + 'em', + 'embed', + 'fieldset', + 'figcaption', + 'figure', + 'footer', + 'form', + 'h1', + 'h2', + 'h3', + 'h4', + 'h5', + 'h6', + 'head', + 'header', + 'hgroup', + 'hr', + 'html', + 'i', + 'iframe', + 'img', + 'input', + 'ins', + 'kbd', + 'label', + 'legend', + 'li', + 'link', + 'main', + 'map', + 'mark', + 'menu', + 'meta', + 'meter', + 'nav', + 'noscript', + 'object', + 'ol', + 'optgroup', + 'option', + 'output', + 'p', + 'picture', + 'pre', + 'progress', + 'q', + 'rp', + 'rt', + 'ruby', + 's', + 'samp', + 'script', + 'search', + 'section', + 'select', + 'slot', + 'small', + 'source', + 'span', + 'strong', + 'style', + 'sub', + 'summary', + 'sup', + 'svg', + 'table', + 'tbody', + 'td', + 'template', + 'textarea', + 'tfoot', + 'th', + 'thead', + 'time', + 'title', + 'tr', + 'track', + 'u', + 'ul', + 'var', + 'video', + 'wbr', +]); + +const isIntrinsicElementName = (name: string | null): boolean => + Boolean(name && INTRINSIC_ELEMENT_NAMES.has(name)); + +const isUsefulComponentName = (name: string | null): boolean => { + if (!name) return false; + if (isIntrinsicElementName(name)) return false; + if (INTERNAL_COMPONENT_NAMES.has(name)) return false; + for (const prefix of NON_COMPONENT_PREFIXES) { + if (name.startsWith(prefix)) return false; + } + return true; +}; + +const isSourceComponentName = (name: string | null): boolean => { + if (!isUsefulComponentName(name)) return false; + if (!name) return false; + if (name.length < 2) return false; + if (name[0] !== name[0].toUpperCase()) return false; + if (name.endsWith('Provider') || name.endsWith('Context')) return false; + return true; +}; + +const inferComponentNameFromSource = ( + filePath: string, + functionName: string | null, +): string | null => { + if (functionName && isUsefulComponentName(functionName)) { + return functionName; + } + + const fileName = + filePath + .split('/') + .pop() + ?.replace(/\.[^.]+$/, '') ?? ''; + return /^[A-Z][A-Za-z0-9]*$/.test(fileName) ? fileName : null; +}; + +const normalizeSourceFilePath = (fileName: string): string => { + let normalizedFileName = normalizeFileName(fileName); + normalizedFileName = normalizedFileName.replace( + /^(?:\.\/)?\/?\([a-z][a-z0-9-]*\)\//, + '', + ); + if (normalizedFileName.startsWith('./')) { + normalizedFileName = normalizedFileName.slice(2); + } + return normalizedFileName.replace(/\?.*$/, ''); +}; + +const safeDecodeUriComponent = (value: string): string => { + try { + return decodeURIComponent(value); + } catch (_error) { + return value; + } +}; + +const splitPathSegments = (path: string): string[] => + path.split(PATH_SEPARATOR_PATTERN).filter(Boolean); + +const isProjectSourceFrame = (fileName: string): boolean => { + if (!isSourceFile(fileName)) { + return false; + } + + const normalizedFileName = normalizeSourceFilePath(fileName); + const pathSegments = normalizedFileName.split('/').filter(Boolean); + return ( + !pathSegments.includes('node_modules') && + !pathSegments.includes('.vite') && + !normalizedFileName.includes('/@vite/') + ); +}; + +const isExternalSourceFrame = (fileName: string): boolean => { + const normalizedFileName = normalizeSourceFilePath(fileName); + const pathSegments = normalizedFileName.split('/').filter(Boolean); + const looksLikeJavaScriptSource = /\.(?:[cm]?js|jsx?|tsx?)$/i.test( + normalizedFileName, + ); + + if (!looksLikeJavaScriptSource) { + return false; + } + + return ( + pathSegments.includes('node_modules') || + pathSegments.includes('.vite') || + normalizedFileName.includes('/@vite/') + ); +}; + +const normalizeDisplayComponentName = (name: string | null): string | null => { + if (!name) return null; + const memoMatch = name.match(/^Memo\((.+)\)$/); + if (memoMatch) return memoMatch[1]; + const forwardRefMatch = name.match(/^ForwardRef\((.+)\)$/); + if (forwardRefMatch) return forwardRefMatch[1]; + return name; +}; + +const extractPackageNameFromFilePath = (filePath: string): string | null => { + const readNodeModulesPackage = (afterMarker: string): string | null => { + const [firstSegment, secondSegment] = splitPathSegments(afterMarker); + if (!firstSegment || firstSegment.startsWith('.')) return null; + if (!firstSegment.startsWith('@')) return firstSegment; + return secondSegment ? `${firstSegment}/${secondSegment}` : null; + }; + + const readViteOptimizedDependencyPackage = ( + afterMarker: string, + ): string | null => { + const firstSegment = splitPathSegments(afterMarker)[0]; + if (!firstSegment) return null; + + const packageStem = firstSegment.replace(FILE_EXTENSION_PATTERN, ''); + if (VITE_INTERNAL_CHUNK_PATTERN.test(packageStem)) return null; + if (!packageStem.startsWith('@')) return packageStem; + + const scopeBoundaryIndex = packageStem.indexOf('_'); + if (scopeBoundaryIndex === -1) return null; + return `${packageStem.slice(0, scopeBoundaryIndex)}/${packageStem.slice( + scopeBoundaryIndex + 1, + )}`; + }; + + const extractAfterLastMarker = ( + input: string, + pattern: RegExp, + read: (afterMarker: string) => string | null, + ): string | null => { + pattern.lastIndex = 0; + let lastMatch: RegExpExecArray | null = null; + let match = pattern.exec(input); + while (match !== null) { + lastMatch = match; + match = pattern.exec(input); + } + pattern.lastIndex = 0; + if (!lastMatch) return null; + return read(input.slice(lastMatch.index + lastMatch[0].length)); + }; + + const extractNameAtVersion = (segment: string | undefined): string | null => + segment?.match(NAME_AT_VERSION_PATTERN)?.[1] ?? null; + + const extractVersionedPackageFromUrl = ( + rawFilePath: string, + ): string | null => { + let url: URL; + try { + url = new URL(rawFilePath); + } catch (_error) { + return null; + } + + const segments = splitPathSegments(url.pathname).map( + safeDecodeUriComponent, + ); + for (const [index, segment] of segments.entries()) { + if (segment.startsWith('@')) { + const name = extractNameAtVersion(segments[index + 1]); + if (name) return `${segment}/${name}`; + continue; + } + + const name = extractNameAtVersion(segment); + if (name) return name; + } + return null; + }; + + const normalizedFileName = normalizeSourceFilePath(filePath); + const decodedFileName = safeDecodeUriComponent(normalizedFileName); + + const vitePackage = extractAfterLastMarker( + decodedFileName, + VITE_OPTIMIZED_DEPS_PATTERN, + readViteOptimizedDependencyPackage, + ); + if (vitePackage) { + return vitePackage; + } + + const nodeModulesPackage = extractAfterLastMarker( + decodedFileName, + NODE_MODULES_PATTERN, + readNodeModulesPackage, + ); + if (nodeModulesPackage) { + return nodeModulesPackage; + } + + return extractVersionedPackageFromUrl(filePath); +}; + +const isServerComponentUrl = (url: string): boolean => + SERVER_COMPONENT_URL_PREFIXES.some((prefix) => url.startsWith(prefix)); + +const devirtualizeServerUrl = (url: string): string => { + for (const prefix of SERVER_COMPONENT_URL_PREFIXES) { + if (!url.startsWith(prefix)) continue; + const environmentEndIndex = url.indexOf('/', prefix.length); + const querySuffixIndex = url.lastIndexOf('?'); + if (environmentEndIndex > -1 && querySuffixIndex > -1) { + return decodeURI(url.slice(environmentEndIndex + 1, querySuffixIndex)); + } + } + return url; +}; + +let cachedNextBasePath: string | undefined; + +const getNextBasePath = (): string => { + if (cachedNextBasePath !== undefined) return cachedNextBasePath; + + const source = document.querySelector( + 'script[src*="/_next/"]', + )?.src; + const pathname = source ? new URL(source).pathname : ''; + const assetPathIndex = pathname.indexOf('/_next/'); + cachedNextBasePath = + assetPathIndex > 0 ? pathname.slice(0, assetPathIndex) : ''; + return cachedNextBasePath; +}; + +const checkIsNextProject = (): boolean => + typeof document !== 'undefined' && + Boolean( + document.getElementById('__NEXT_DATA__') || + document.querySelector('nextjs-portal'), + ); + +const findNearestFiberElement = (element: Element): Element | null => { + let currentElement: Element | null = element; + while (currentElement) { + if (getFiberFromHostInstance(currentElement)) { + return currentElement; + } + currentElement = currentElement.parentElement; + } + return null; +}; + +const truncateText = (text: string, maxLength: number): string => { + if (text.length <= maxLength) return text; + return `${text.slice(0, maxLength)}...`; +}; + +const getDomPreview = (element: Element): string => { + if ('outerHTML' in element && typeof element.outerHTML === 'string') { + return truncateText(element.outerHTML, MAX_HTML_PREVIEW_LENGTH); + } + + const tagName = element.tagName ? element.tagName.toLowerCase() : 'unknown'; + return `<${tagName}>`; +}; + +const mapOwnerStackFrame = (stackFrame: StackFrame): SelectionStackFrame => { + return { + functionName: stackFrame.functionName || null, + fileName: stackFrame.fileName + ? normalizeSourceFilePath(stackFrame.fileName) + : null, + lineNumber: stackFrame.lineNumber ?? null, + columnNumber: stackFrame.columnNumber ?? null, + }; +}; + +const symbolicateServerFrames = async ( + frames: StackFrame[], +): Promise => { + const serverFrameIndices: number[] = []; + const requestFrames: Array<{ + file: string; + methodName: string; + line1: number | null; + column1: number | null; + arguments: string[]; + }> = []; + + for (let frameIndex = 0; frameIndex < frames.length; frameIndex++) { + const frame = frames[frameIndex]; + if (!frame.isServer || !frame.fileName) continue; + + serverFrameIndices.push(frameIndex); + requestFrames.push({ + file: devirtualizeServerUrl(frame.fileName), + methodName: frame.functionName ?? '', + line1: frame.lineNumber ?? null, + column1: frame.columnNumber ?? null, + arguments: [], + }); + } + + if (requestFrames.length === 0) return frames; + + const controller = new AbortController(); + const timeout = setTimeout( + () => controller.abort(), + SYMBOLICATION_TIMEOUT_MS, + ); + + try { + const response = await fetch( + `${getNextBasePath()}/__nextjs_original-stack-frames`, + { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + frames: requestFrames, + isServer: true, + isEdgeServer: false, + isAppDirectory: true, + }), + signal: controller.signal, + }, + ); + + if (!response.ok) return frames; + + const results = (await response.json()) as Array<{ + status: string; + value?: { + originalStackFrame: { + file: string | null; + line1: number | null; + column1: number | null; + ignored: boolean; + } | null; + }; + }>; + const resolvedFrames = [...frames]; + + for ( + let resultIndex = 0; + resultIndex < serverFrameIndices.length; + resultIndex++ + ) { + const result = results[resultIndex]; + if (result?.status !== 'fulfilled') continue; + + const resolved = result.value?.originalStackFrame; + if (!resolved?.file || resolved.ignored) continue; + + const originalFrameIndex = serverFrameIndices[resultIndex]; + resolvedFrames[originalFrameIndex] = { + ...frames[originalFrameIndex], + fileName: resolved.file, + lineNumber: resolved.line1 ?? undefined, + columnNumber: resolved.column1 ?? undefined, + isSymbolicated: true, + }; + } + + return resolvedFrames; + } catch (_error) { + return frames; + } finally { + clearTimeout(timeout); + } +}; + +const extractServerFramesFromDebugStack = ( + rootFiber: Fiber, +): Map => { + const serverFramesByName = new Map(); + + traverseFiber( + rootFiber, + (currentFiber) => { + if (!hasDebugStack(currentFiber)) return false; + + const debugStack = currentFiber._debugStack?.stack; + if (!debugStack) return false; + + const ownerStack = formatOwnerStack(debugStack); + if (!ownerStack) return false; + + for (const frame of parseStack(ownerStack)) { + if (!frame.functionName || !frame.fileName) continue; + if (!isServerComponentUrl(frame.fileName)) continue; + if (serverFramesByName.has(frame.functionName)) continue; + + serverFramesByName.set(frame.functionName, { + ...frame, + isServer: true, + }); + } + return false; + }, + true, + ); + + return serverFramesByName; +}; + +const enrichServerFrameLocations = ( + rootFiber: Fiber, + frames: StackFrame[], +): StackFrame[] => { + const hasUnresolvedServerFrames = frames.some( + (frame) => frame.isServer && !frame.fileName && frame.functionName, + ); + if (!hasUnresolvedServerFrames) return frames; + + const serverFramesByName = extractServerFramesFromDebugStack(rootFiber); + if (serverFramesByName.size === 0) return frames; + + return frames.map((frame) => { + if (!frame.isServer || frame.fileName || !frame.functionName) return frame; + const resolved = serverFramesByName.get(frame.functionName); + if (!resolved) return frame; + return { + ...frame, + fileName: resolved.fileName, + lineNumber: resolved.lineNumber, + columnNumber: resolved.columnNumber, + }; + }); +}; + +export const getSelectionElementComponentName = ( + element: Element, +): string | null => { + if (!isInstrumentationActive()) return null; + const resolvedElement = findNearestFiberElement(element); + if (!resolvedElement) return null; + const fiber = getFiberFromHostInstance(resolvedElement); + if (!fiber) return null; + + let currentFiber = fiber.return; + while (currentFiber) { + if (isCompositeFiber(currentFiber)) { + const name = getDisplayName(currentFiber.type); + if (name && isUsefulComponentName(name)) { + return name; + } + } + currentFiber = currentFiber.return; + } + + return null; +}; + +const getFiberComponentRankByName = (element: Element): Map => { + const rankByName = new Map(); + if (!isInstrumentationActive()) return rankByName; + + const resolvedElement = findNearestFiberElement(element); + if (!resolvedElement) return rankByName; + const fiber = getFiberFromHostInstance(resolvedElement); + if (!fiber) return rankByName; + + let currentFiber = fiber.return; + let rank = 0; + while (currentFiber) { + if (isCompositeFiber(currentFiber)) { + const name = normalizeDisplayComponentName( + getDisplayName(currentFiber.type), + ); + if (name && !rankByName.has(name)) { + rankByName.set(name, rank); + } + rank += 1; + } + currentFiber = currentFiber.return; + } + + return rankByName; +}; + +const stackFrameCache = new WeakMap>(); + +const buildStackFramesForElement = async ( + nearestFiberElement: Element, +): Promise => { + const fiber = getFiberFromHostInstance(nearestFiberElement); + if (!fiber) { + return []; + } + + const buildFiberStackFrames = async (): Promise => { + const fallbackFiberStack = getFiberStack(fiber).slice(0, MAX_STACK_FRAMES); + const stackFrames: SelectionStackFrame[] = []; + + for (const stackFiber of fallbackFiberStack) { + const source = await getSource(stackFiber).catch(() => null); + stackFrames.push({ + functionName: getDisplayNameForFiber(stackFiber), + fileName: source?.fileName + ? normalizeSourceFilePath(source.fileName) + : null, + lineNumber: source?.lineNumber ?? null, + columnNumber: source?.columnNumber ?? null, + }); + } + + return stackFrames; + }; + + try { + const ownerStack = checkIsNextProject() + ? await symbolicateServerFrames( + enrichServerFrameLocations(fiber, await getOwnerStack(fiber)), + ) + : await getOwnerStack(fiber); + const ownerStackFrames = ownerStack + .slice(0, MAX_STACK_FRAMES) + .map(mapOwnerStackFrame); + const fiberStackFrames = await buildFiberStackFrames(); + const unresolvedOwnerFrameNames = new Set( + ownerStackFrames + .filter((stackFrame) => !stackFrame.fileName && stackFrame.functionName) + .map((stackFrame) => stackFrame.functionName as string), + ); + const seenFrameKeys = new Set( + ownerStackFrames.map( + (stackFrame) => + `${stackFrame.functionName ?? ''}:${stackFrame.fileName ?? ''}:${stackFrame.lineNumber ?? ''}:${stackFrame.columnNumber ?? ''}`, + ), + ); + const supplementalFiberFrames = fiberStackFrames.filter((stackFrame) => { + if ( + stackFrame.fileName && + isExternalSourceFrame(stackFrame.fileName) && + stackFrame.functionName && + unresolvedOwnerFrameNames.has(stackFrame.functionName) + ) { + return false; + } + + const frameKey = `${stackFrame.functionName ?? ''}:${stackFrame.fileName ?? ''}:${stackFrame.lineNumber ?? ''}:${stackFrame.columnNumber ?? ''}`; + if (seenFrameKeys.has(frameKey)) { + return false; + } + seenFrameKeys.add(frameKey); + return true; + }); + + return [...ownerStackFrames, ...supplementalFiberFrames].slice( + 0, + MAX_STACK_FRAMES, + ); + } catch (_error) { + return buildFiberStackFrames(); + } +}; + +const buildStackFrames = ( + nearestFiberElement: Element, +): Promise => { + const cachedStackFrames = stackFrameCache.get(nearestFiberElement); + if (cachedStackFrames) { + return cachedStackFrames; + } + + const stackFrames = buildStackFramesForElement(nearestFiberElement); + stackFrameCache.set(nearestFiberElement, stackFrames); + return stackFrames; +}; + +const resolveSourceFrames = ( + stackFrames: SelectionStackFrame[], + preferredComponentName: string | null, + componentRankByName: Map, +): SelectionResolvedSource[] => { + const resolvedSources: SelectionResolvedSource[] = []; + const seenSourceKeys = new Set(); + const sourceFrames = stackFrames.filter( + (stackFrame) => + stackFrame.fileName && isProjectSourceFrame(stackFrame.fileName), + ); + + const getFrameComponentName = (stackFrame: SelectionStackFrame) => + stackFrame.fileName + ? inferComponentNameFromSource( + stackFrame.fileName, + stackFrame.functionName, + ) + : null; + + const getFiberRank = (stackFrame: SelectionStackFrame): number => { + const componentName = getFrameComponentName(stackFrame); + if (!componentName) return Number.POSITIVE_INFINITY; + return componentRankByName.get(componentName) ?? Number.POSITIVE_INFINITY; + }; + + const sortedSourceFrames = [...sourceFrames].sort((left, right) => { + const leftRank = getFiberRank(left); + const rightRank = getFiberRank(right); + if (leftRank !== rightRank) { + return leftRank - rightRank; + } + + const leftComponentName = getFrameComponentName(left); + const rightComponentName = getFrameComponentName(right); + const leftIsPreferred = leftComponentName === preferredComponentName; + const rightIsPreferred = rightComponentName === preferredComponentName; + if (leftIsPreferred !== rightIsPreferred) { + return leftIsPreferred ? -1 : 1; + } + + const leftIsSourceComponent = isSourceComponentName(left.functionName); + const rightIsSourceComponent = isSourceComponentName(right.functionName); + if (leftIsSourceComponent !== rightIsSourceComponent) { + return leftIsSourceComponent ? -1 : 1; + } + + return 0; + }); + + for (const stackFrame of sortedSourceFrames) { + if (!stackFrame.fileName) { + continue; + } + + const sourceKey = `${stackFrame.fileName}:${stackFrame.lineNumber ?? 0}:${stackFrame.columnNumber ?? 0}`; + if (seenSourceKeys.has(sourceKey)) { + continue; + } + + const componentName = inferComponentNameFromSource( + stackFrame.fileName, + stackFrame.functionName, + ); + seenSourceKeys.add(sourceKey); + resolvedSources.push({ + filePath: stackFrame.fileName, + lineNumber: stackFrame.lineNumber, + columnNumber: stackFrame.columnNumber, + componentName, + }); + + if (resolvedSources.length >= MAX_SOURCE_FRAMES) { + break; + } + } + + return resolvedSources; +}; + +const resolveExternalComponent = ( + stackFrames: SelectionStackFrame[], + selectedComponentName: string | null, + resolvedSources: SelectionResolvedSource[], +): SelectionExternalComponent | null => { + const selectedDisplayName = normalizeDisplayComponentName( + selectedComponentName, + ); + if (!selectedDisplayName) { + return null; + } + + const externalFrames = stackFrames.filter( + (stackFrame) => + stackFrame.fileName && + isExternalSourceFrame(stackFrame.fileName) && + isUsefulComponentName(stackFrame.functionName), + ); + if (externalFrames.length === 0) { + return null; + } + + const selectedExternalFrame = externalFrames.find( + (stackFrame) => + normalizeDisplayComponentName(stackFrame.functionName) === + selectedDisplayName, + ); + if (!selectedExternalFrame?.fileName) { + return null; + } + + return { + componentName: + normalizeDisplayComponentName(selectedExternalFrame.functionName) || + selectedDisplayName, + packageName: extractPackageNameFromFilePath(selectedExternalFrame.fileName), + filePath: normalizeSourceFilePath(selectedExternalFrame.fileName), + usedBy: resolvedSources[0] ?? null, + }; +}; + +const formatTraceFrameKey = ( + frame: Pick< + SelectionSourceTraceFrame, + 'kind' | 'componentName' | 'filePath' | 'lineNumber' | 'columnNumber' + >, +): string => + `${frame.kind}:${frame.componentName ?? ''}:${frame.filePath}:${frame.lineNumber ?? ''}:${frame.columnNumber ?? ''}`; + +const mapStackFrameToTraceFrame = ( + stackFrame: SelectionStackFrame, +): SelectionSourceTraceFrame | null => { + if (!stackFrame.fileName) return null; + + if (isExternalSourceFrame(stackFrame.fileName)) { + return { + kind: 'external', + componentName: + normalizeDisplayComponentName(stackFrame.functionName) ?? null, + packageName: extractPackageNameFromFilePath(stackFrame.fileName), + filePath: normalizeSourceFilePath(stackFrame.fileName), + lineNumber: stackFrame.lineNumber, + columnNumber: stackFrame.columnNumber, + }; + } + + if (!isProjectSourceFrame(stackFrame.fileName)) { + return null; + } + + const filePath = normalizeSourceFilePath(stackFrame.fileName); + return { + kind: 'project', + componentName: inferComponentNameFromSource( + filePath, + stackFrame.functionName, + ), + packageName: null, + filePath, + lineNumber: stackFrame.lineNumber, + columnNumber: stackFrame.columnNumber, + }; +}; + +const buildSourceTrace = ( + stackFrames: SelectionStackFrame[], + resolvedSources: SelectionResolvedSource[], +): SelectionSourceTraceFrame[] => { + const traceFrames: SelectionSourceTraceFrame[] = []; + const seenTraceFrameKeys = new Set(); + let previousExternalPackageName: string | null = null; + + const addTraceFrame = (frame: SelectionSourceTraceFrame | null) => { + if (!frame?.filePath) return; + + if ( + frame.kind === 'external' && + frame.packageName && + frame.packageName === previousExternalPackageName + ) { + return; + } + + const traceFrameKey = formatTraceFrameKey(frame); + if (seenTraceFrameKeys.has(traceFrameKey)) return; + + seenTraceFrameKeys.add(traceFrameKey); + traceFrames.push(frame); + previousExternalPackageName = + frame.kind === 'external' ? frame.packageName : null; + }; + + for (const stackFrame of stackFrames) { + addTraceFrame(mapStackFrameToTraceFrame(stackFrame)); + if (traceFrames.length >= MAX_SOURCE_FRAMES) { + break; + } + } + + const hasProjectFrame = traceFrames.some( + (traceFrame) => traceFrame.kind === 'project', + ); + + if (!hasProjectFrame) { + for (const resolvedSource of resolvedSources) { + addTraceFrame({ + kind: 'project', + componentName: resolvedSource.componentName, + packageName: null, + filePath: resolvedSource.filePath, + lineNumber: resolvedSource.lineNumber, + columnNumber: resolvedSource.columnNumber, + }); + if (traceFrames.length >= MAX_SOURCE_FRAMES) { + break; + } + } + } + + return traceFrames; +}; + +const getComponentName = ( + stackFrames: SelectionStackFrame[], +): string | null => { + for (const stackFrame of stackFrames) { + if ( + stackFrame.fileName && + isProjectSourceFrame(stackFrame.fileName) && + isSourceComponentName(stackFrame.functionName) + ) { + return stackFrame.functionName; + } + } + + for (const stackFrame of stackFrames) { + if (isUsefulComponentName(stackFrame.functionName)) { + return stackFrame.functionName; + } + } + + return null; +}; + +export const buildSelectionContextForElement = async ( + selectedElement: Element, +): Promise => { + const nearestFiberElement = findNearestFiberElement(selectedElement); + const stackFrames = nearestFiberElement + ? await buildStackFrames(nearestFiberElement) + : []; + const componentRankByName = getFiberComponentRankByName(selectedElement); + const displayComponentName = + getSelectionElementComponentName(selectedElement); + const stackComponentName = getComponentName(stackFrames); + const fallbackComponentName = stackComponentName || displayComponentName; + const resolvedSources = resolveSourceFrames( + stackFrames, + fallbackComponentName, + componentRankByName, + ); + const sourceComponentName = + resolvedSources.find((source) => source.componentName)?.componentName ?? + null; + const componentName = + sourceComponentName || stackComponentName || displayComponentName; + const externalComponent = resolveExternalComponent( + stackFrames, + componentName, + resolvedSources, + ); + const sourceTrace = buildSourceTrace(stackFrames, resolvedSources); + const selectionContext = { + domPreview: getDomPreview(selectedElement), + sourcePreview: null, + selector: createElementSelector(selectedElement), + componentName, + externalComponent, + stackFrames, + resolvedSources, + sourceTrace, + sourceSnippets: [], + capturedAt: Date.now(), + }; + + return { + ...selectionContext, + sourcePreview: buildSelectionSourcePreview(selectionContext), + }; +}; diff --git a/packages/core/src/core/tools/selection_dom.ts b/packages/core/src/core/tools/selection_dom.ts new file mode 100644 index 0000000..004c33b --- /dev/null +++ b/packages/core/src/core/tools/selection_dom.ts @@ -0,0 +1,138 @@ +const VIEWPORT_COVERAGE_THRESHOLD = 0.9; +const OVERLAY_Z_INDEX_THRESHOLD = 1000; +const DEV_TOOLS_OVERLAY_Z_INDEX_THRESHOLD = 2147483600; +const ELEMENT_POSITION_CACHE_DISTANCE_THRESHOLD_PX = 2; +const ELEMENT_POSITION_THROTTLE_MS = 16; + +interface PositionCache { + clientX: number; + clientY: number; + element: Element | null; + timestamp: number; +} + +let cache: PositionCache | null = null; + +const isRootElement = (element: Element): boolean => { + const tagName = element.tagName.toLowerCase(); + return tagName === 'html' || tagName === 'body'; +}; + +const hasTransparentBackground = ( + computedStyle: CSSStyleDeclaration, +): boolean => { + const backgroundColor = computedStyle.backgroundColor; + return ( + backgroundColor === 'transparent' || backgroundColor === 'rgba(0, 0, 0, 0)' + ); +}; + +const isDevToolsOverlay = (computedStyle: CSSStyleDeclaration): boolean => { + const zIndex = Number.parseInt(computedStyle.zIndex, 10); + return ( + computedStyle.pointerEvents === 'none' && + computedStyle.position === 'fixed' && + !Number.isNaN(zIndex) && + zIndex >= DEV_TOOLS_OVERLAY_Z_INDEX_THRESHOLD + ); +}; + +const isFullViewportOverlay = ( + element: Element, + computedStyle: CSSStyleDeclaration, +): boolean => { + const position = computedStyle.position; + if (position !== 'fixed' && position !== 'absolute') return false; + + const coversViewport = + element.clientWidth / window.innerWidth >= VIEWPORT_COVERAGE_THRESHOLD && + element.clientHeight / window.innerHeight >= VIEWPORT_COVERAGE_THRESHOLD; + if (!coversViewport) return false; + + if (hasTransparentBackground(computedStyle)) return true; + if (Number.parseFloat(computedStyle.opacity) < 0.1) return true; + + const zIndex = Number.parseInt(computedStyle.zIndex, 10); + return !Number.isNaN(zIndex) && zIndex > OVERLAY_Z_INDEX_THRESHOLD; +}; + +const isVisibleElement = ( + element: Element, + computedStyle: CSSStyleDeclaration, +): boolean => + computedStyle.display !== 'none' && + computedStyle.visibility !== 'hidden' && + computedStyle.opacity !== '0' && + element.getClientRects().length > 0; + +const isWithinThreshold = ( + x1: number, + y1: number, + x2: number, + y2: number, +): boolean => + Math.abs(x1 - x2) <= ELEMENT_POSITION_CACHE_DISTANCE_THRESHOLD_PX && + Math.abs(y1 - y2) <= ELEMENT_POSITION_CACHE_DISTANCE_THRESHOLD_PX; + +export const isSelectableElement = ( + element: Element, + isOverlayElement: (element: Element) => boolean, +): boolean => { + if (isRootElement(element)) return false; + if (isOverlayElement(element)) return false; + + const computedStyle = window.getComputedStyle(element); + if (!isVisibleElement(element, computedStyle)) return false; + if (isDevToolsOverlay(computedStyle)) return false; + if (isFullViewportOverlay(element, computedStyle)) return false; + + return true; +}; + +export const getSelectableElementAtPosition = ( + clientX: number, + clientY: number, + isOverlayElement: (element: Element) => boolean, +): Element | null => { + if (!Number.isFinite(clientX) || !Number.isFinite(clientY)) return null; + + const now = performance.now(); + if (cache) { + const isPositionClose = isWithinThreshold( + clientX, + clientY, + cache.clientX, + cache.clientY, + ); + const isWithinThrottle = + now - cache.timestamp < ELEMENT_POSITION_THROTTLE_MS; + + if (isPositionClose || isWithinThrottle) { + return cache.element; + } + } + + let result: Element | null = null; + const topElement = document.elementFromPoint(clientX, clientY); + if (topElement && isSelectableElement(topElement, isOverlayElement)) { + result = topElement; + } else { + const elementsAtPoint = document.elementsFromPoint(clientX, clientY); + for (const candidateElement of elementsAtPoint) { + if ( + candidateElement !== topElement && + isSelectableElement(candidateElement, isOverlayElement) + ) { + result = candidateElement; + break; + } + } + } + + cache = { clientX, clientY, element: result, timestamp: now }; + return result; +}; + +export const clearSelectableElementCache = (): void => { + cache = null; +}; diff --git a/packages/core/src/core/tools/selection_selector.ts b/packages/core/src/core/tools/selection_selector.ts new file mode 100644 index 0000000..d77a452 --- /dev/null +++ b/packages/core/src/core/tools/selection_selector.ts @@ -0,0 +1,363 @@ +const FINDER_TIMEOUT_MS = 200; +const MAX_SELECTOR_COMBINATIONS = 10000; +const SELECTOR_ATTR_VALUE_MAX_LENGTH_CHARS = 120; + +interface SelectorNode { + name: string; + penalty: number; +} + +const PREFERRED_SELECTOR_ATTRIBUTE_NAMES = new Set([ + 'data-testid', + 'data-test-id', + 'data-test', + 'data-cy', + 'data-qa', + 'aria-label', + 'role', + 'name', + 'title', + 'alt', +]); + +const ACCEPTED_ATTR_NAMES = new Set([ + 'role', + 'name', + 'aria-label', + 'rel', + 'href', +]); + +const escapeCssIdentifier = (value: string): string => { + if (typeof CSS !== 'undefined' && typeof CSS.escape === 'function') { + return CSS.escape(value); + } + return value.replace(/[^a-zA-Z0-9_-]/g, (character) => `\\${character}`); +}; + +const isWordLike = (text: string): boolean => { + if (!/^[a-z\-]{3,}$/i.test(text)) return false; + const segments = text.split(/-|[A-Z]/); + for (const segment of segments) { + if (segment.length <= 2) return false; + if (/[^aeiou]{4,}/i.test(segment)) return false; + } + return true; +}; + +const isPreferredAttributeValueSafe = (value: string): boolean => + value.length > 0 && value.length <= SELECTOR_ATTR_VALUE_MAX_LENGTH_CHARS; + +const isAcceptedAttr = ( + attributeName: string, + attributeValue: string, +): boolean => { + const nameIsAccepted = + ACCEPTED_ATTR_NAMES.has(attributeName) || + (attributeName.startsWith('data-') && isWordLike(attributeName)); + const valueIsAccepted = + (isWordLike(attributeValue) && + attributeValue.length < SELECTOR_ATTR_VALUE_MAX_LENGTH_CHARS) || + (attributeValue.startsWith('#') && isWordLike(attributeValue.slice(1))); + return nameIsAccepted && valueIsAccepted; +}; + +const getFinderRoot = (element: Element): Element => + element.ownerDocument.body ?? element.ownerDocument.documentElement; + +const isSelectorUniqueForElement = ( + element: Element, + selector: string, +): boolean => { + try { + const matchingElements = element.ownerDocument.querySelectorAll(selector); + return matchingElements.length === 1 && matchingElements[0] === element; + } catch (_error) { + return false; + } +}; + +const createFastElementSelector = (element: Element): string | null => { + if (element instanceof HTMLElement && element.id) { + const idSelector = `#${escapeCssIdentifier(element.id)}`; + if (isSelectorUniqueForElement(element, idSelector)) return idSelector; + } + + for (const attributeName of PREFERRED_SELECTOR_ATTRIBUTE_NAMES) { + const attributeValue = element.getAttribute(attributeName); + if (!attributeValue) continue; + if (!isPreferredAttributeValueSafe(attributeValue)) continue; + + const quotedValue = JSON.stringify(attributeValue); + const attributeOnlySelector = `[${attributeName}=${quotedValue}]`; + if (isSelectorUniqueForElement(element, attributeOnlySelector)) { + return attributeOnlySelector; + } + + const tagSelector = `${element.tagName.toLowerCase()}${attributeOnlySelector}`; + if (isSelectorUniqueForElement(element, tagSelector)) { + return tagSelector; + } + } + + return null; +}; + +const getChildIndex = ( + element: Element, + filterTagName?: string, +): number | undefined => { + const parentNode = element.parentNode; + if (!parentNode) return undefined; + + let sibling = parentNode.firstChild; + if (!sibling) return undefined; + + let position = 0; + while (sibling) { + if ( + sibling.nodeType === Node.ELEMENT_NODE && + (filterTagName === undefined || + (sibling as Element).tagName.toLowerCase() === filterTagName) + ) { + position++; + } + if (sibling === element) break; + sibling = sibling.nextSibling; + } + return position; +}; + +const formatNthChild = (tagName: string, childPosition: number): string => + tagName === 'html' ? 'html' : `${tagName}:nth-child(${childPosition})`; + +const formatNthOfType = (tagName: string, typePosition: number): string => + tagName === 'html' ? 'html' : `${tagName}:nth-of-type(${typePosition})`; + +const collectCandidateNodes = (element: Element): SelectorNode[] => { + const candidates: SelectorNode[] = []; + const elementId = element.getAttribute('id'); + const elementTagName = element.tagName.toLowerCase(); + + if (elementId && isWordLike(elementId)) { + candidates.push({ + name: `#${escapeCssIdentifier(elementId)}`, + penalty: 0, + }); + } + + for (const className of Array.from(element.classList)) { + if (isWordLike(className)) { + candidates.push({ + name: `.${escapeCssIdentifier(className)}`, + penalty: 1, + }); + } + } + + for (const attribute of Array.from(element.attributes)) { + if (isAcceptedAttr(attribute.name, attribute.value)) { + candidates.push({ + name: `[${escapeCssIdentifier(attribute.name)}="${escapeCssIdentifier(attribute.value)}"]`, + penalty: 2, + }); + } + } + + candidates.push({ name: elementTagName, penalty: 5 }); + + const typePosition = getChildIndex(element, elementTagName); + if (typePosition !== undefined) { + candidates.push({ + name: formatNthOfType(elementTagName, typePosition), + penalty: 10, + }); + } + + const childPosition = getChildIndex(element); + if (childPosition !== undefined) { + candidates.push({ + name: formatNthChild(elementTagName, childPosition), + penalty: 50, + }); + } + + return candidates; +}; + +const buildSelectorString = (path: SelectorNode[]): string => { + let result = path[0].name; + for (let index = 1; index < path.length; index++) { + result = `${path[index].name} > ${result}`; + } + return result; +}; + +const collectCombinations = ( + stack: SelectorNode[][], + budget = MAX_SELECTOR_COMBINATIONS, + currentPath: SelectorNode[] = [], +): SelectorNode[][] => { + if (budget <= 0) return []; + if (stack.length === 0) return [currentPath]; + + const results: SelectorNode[][] = []; + for (const selectorNode of stack[0]) { + const remainingBudget = budget - results.length; + if (remainingBudget <= 0) break; + results.push( + ...collectCombinations(stack.slice(1), remainingBudget, [ + ...currentPath, + selectorNode, + ]), + ); + } + return results; +}; + +const calculatePenalty = (path: SelectorNode[]): number => + path.reduce((total, selectorNode) => total + selectorNode.penalty, 0); + +const comparePenalty = (pathA: SelectorNode[], pathB: SelectorNode[]): number => + calculatePenalty(pathA) - calculatePenalty(pathB); + +const resolveRootDocument = ( + rootNode: Element | Document, + targetElement: Element, +): Element | Document => { + const attachedRoot = targetElement.getRootNode?.(); + if (attachedRoot instanceof ShadowRoot) { + return attachedRoot as unknown as Document; + } + if (rootNode.nodeType === Node.DOCUMENT_NODE) return rootNode; + return (rootNode as Element).ownerDocument; +}; + +const isSelectorUnique = ( + selectorPath: SelectorNode[], + rootDocument: Element | Document, +): boolean => + rootDocument.querySelectorAll(buildSelectorString(selectorPath)).length === 1; + +const buildFallbackPath = ( + targetElement: Element, + rootDocument: Element | Document, +): SelectorNode[] | undefined => { + let currentElement: Element | null = targetElement; + const path: SelectorNode[] = []; + + while (currentElement && currentElement !== rootDocument) { + const currentTagName = currentElement.tagName.toLowerCase(); + const typePosition = getChildIndex(currentElement, currentTagName); + if (typePosition === undefined) return undefined; + + path.push({ + name: formatNthOfType(currentTagName, typePosition), + penalty: 10, + }); + currentElement = currentElement.parentElement; + } + + return isSelectorUnique(path, rootDocument) ? path : undefined; +}; + +const findUniqueSelector = ( + targetElement: Element, + root: Element | Document, +): string | null => { + if (targetElement.nodeType !== Node.ELEMENT_NODE) return null; + if (targetElement.tagName.toLowerCase() === 'html') return 'html'; + + const rootDocument = resolveRootDocument(root, targetElement); + const startTime = Date.now(); + const ancestorStack: SelectorNode[][] = []; + let currentElement: Element | null = targetElement; + let depth = 0; + let foundPath: SelectorNode[] | undefined; + + while (currentElement && currentElement !== rootDocument && !foundPath) { + ancestorStack.push(collectCandidateNodes(currentElement)); + currentElement = currentElement.parentElement; + depth++; + + if (depth >= 3) { + const candidatePaths = collectCombinations(ancestorStack); + candidatePaths.sort(comparePenalty); + + for (const candidatePath of candidatePaths) { + if (Date.now() - startTime > FINDER_TIMEOUT_MS) { + const fallbackPath = buildFallbackPath(targetElement, rootDocument); + return fallbackPath ? buildSelectorString(fallbackPath) : null; + } + if (isSelectorUnique(candidatePath, rootDocument)) { + foundPath = candidatePath; + break; + } + } + } + } + + if (!foundPath && depth < 3) { + const remainingPaths = collectCombinations(ancestorStack); + remainingPaths.sort(comparePenalty); + for (const candidatePath of remainingPaths) { + if (Date.now() - startTime > FINDER_TIMEOUT_MS) break; + if (isSelectorUnique(candidatePath, rootDocument)) { + foundPath = candidatePath; + break; + } + } + } + + return foundPath ? buildSelectorString(foundPath) : null; +}; + +const createNthChildSelector = (element: Element): string => { + const segments: string[] = []; + const root = getFinderRoot(element); + let currentElement: Element | null = element; + + while (currentElement) { + if (currentElement instanceof HTMLElement && currentElement.id) { + segments.unshift(`#${escapeCssIdentifier(currentElement.id)}`); + break; + } + + const parentElement = currentElement.parentElement; + if (!parentElement) { + segments.unshift(currentElement.tagName.toLowerCase()); + break; + } + + const siblings = Array.from(parentElement.children); + const siblingIndex = siblings.indexOf(currentElement); + const nthChild = siblingIndex >= 0 ? siblingIndex + 1 : 1; + segments.unshift( + `${currentElement.tagName.toLowerCase()}:nth-child(${nthChild})`, + ); + + if (parentElement === root) { + segments.unshift(root.tagName.toLowerCase()); + break; + } + + currentElement = parentElement; + } + + return segments.join(' > '); +}; + +export const createElementSelector = (element: Element): string => { + const fastSelector = createFastElementSelector(element); + if (fastSelector) return fastSelector; + + try { + const selector = findUniqueSelector(element, getFinderRoot(element)); + if (selector) return selector; + } catch (_error) { + // Unusual DOMs can throw while querying selectors. The nth-child path is slower + // to read, but deterministic and still points back to the selected element. + } + + return createNthChildSelector(element); +}; diff --git a/packages/core/src/core/tools/selection_toolkit.ts b/packages/core/src/core/tools/selection_toolkit.ts new file mode 100644 index 0000000..498e023 --- /dev/null +++ b/packages/core/src/core/tools/selection_toolkit.ts @@ -0,0 +1,3348 @@ +import { __AGENTIC_REACT_CONFIG__, target } from '../../shared/const.js'; +import { DEFAULT_TOOLKIT_ICON_DATA_URL } from '../../shared/default_toolkit_icon.js'; +import { SOURCE_LOOKUP_PATH } from '../../shared/protocol.js'; +import { + buildSelectionSourcePreview, + buildWebContextText, +} from '../../shared/selection_context_format.js'; +import { + buildBrowserSourceCandidates, + isAllowedProjectSourcePath, + normalizeSourceRoot, + toAbsoluteSourcePath, +} from '../../shared/source_path.js'; +import type { + SelectionContext, + SelectionResolvedSource, + ToolkitConfig, + ToolkitPosition, + ToolkitTuningModalStyle, + ToolkitTuningModalStyleSlot, + TuningModalActions, + TuningModalContext, + TuningModalExtension, + TuningModalExtensionCleanup, +} from '../../shared/types.js'; +import { + buildSelectionContextForElement, + getSelectionElementComponentName, +} from './selection_context.js'; +import { + clearSelectableElementCache, + getSelectableElementAtPosition, +} from './selection_dom.js'; + +interface ToolkitRuntimeOptions { + initialConfig?: ToolkitConfig; +} + +interface ToolkitRuntimeResult { + showToolkit: () => void; + hideToolkit: () => void; + setToolkitConfig: (config: Partial) => ToolkitConfig; + enterSelectionMode: () => void; + exitSelectionMode: () => void; + setSelectionMode: (enabled: boolean) => void; + getLastSelectionContext: () => SelectionContext | null; + copyLastSelectionContext: (format?: 'text' | 'json') => Promise<{ + success: boolean; + copied: boolean; + format: 'text' | 'json'; + context?: SelectionContext; + error?: string; + }>; + registerTuningModalExtension: (extension: TuningModalExtension) => () => void; +} + +const DEFAULT_TOOLKIT_CONFIG: Required> & { + iconUrl: string | null; +} = { + enabled: true, + defaultVisible: true, + defaultExpanded: false, + position: 'bottom-right', + offset: { + x: 20, + y: 20, + }, + accentColor: '#111827', + zIndex: 2147483000, + iconUrl: DEFAULT_TOOLKIT_ICON_DATA_URL, + tuningModal: {}, +}; + +const DEFAULT_SNIPPET_CONTEXT_LINES = 8; +const DEFAULT_MAX_SNIPPET_FILES = 3; +const LAUNCHER_SIZE = 58; +const SELECTION_CONFIRMATION_MS = 1100; +const SELECTION_STYLE_ID = 'agentic-react-selection-styles'; +const CONTRAST_LIGHT = 'rgba(255, 255, 255, 0.98)'; +const CONTRAST_DARK = 'rgba(15, 23, 42, 0.98)'; +const SELECTED_LABEL_MAX_LENGTH = 96; +const TUNING_PANEL_MARGIN = 12; +const TUNING_PANEL_GAP = 8; +const TUNING_FONT_WEIGHT_OPTIONS = [ + '100', + '200', + '300', + '400', + '500', + '600', + '700', + '800', + '900', +]; +const TUNING_FONT_FAMILY_OPTIONS = [ + 'inherit', + 'system-ui', + 'Inter', + 'Arial', + 'Georgia', + 'Times New Roman', + 'monospace', +]; +const TUNING_DISPLAY_OPTIONS = [ + 'block', + 'inline-block', + 'flex', + 'inline-flex', + 'grid', + 'inline-grid', +]; +const TUNING_FLEX_DIRECTION_OPTIONS = [ + 'row', + 'row-reverse', + 'column', + 'column-reverse', +]; +const TUNING_JUSTIFY_OPTIONS = [ + 'flex-start', + 'center', + 'flex-end', + 'space-between', + 'space-around', + 'space-evenly', +]; +const TUNING_ALIGN_OPTIONS = [ + 'stretch', + 'flex-start', + 'center', + 'flex-end', + 'baseline', +]; + +interface RgbaColor { + r: number; + g: number; + b: number; + a: number; +} + +interface OverlayActionElements { + actionsElement: HTMLDivElement; + tuneButtonElement: HTMLButtonElement; + deleteButtonElement: HTMLButtonElement; +} + +interface TuningSession { + anchorElement: HTMLElement; + element: Element; + selectionContext: SelectionContext; + setSelectionContext: (selectionContext: SelectionContext) => void; +} + +const mergeToolkitConfig = ( + config: ToolkitConfig | undefined, + nextConfig: Partial | undefined, +): Required> & { iconUrl: string | null } => { + const baseConfig = { + ...DEFAULT_TOOLKIT_CONFIG, + ...(config || {}), + iconUrl: + config?.iconUrl !== undefined + ? config.iconUrl + : DEFAULT_TOOLKIT_CONFIG.iconUrl, + }; + + if (!nextConfig) { + return { + ...baseConfig, + offset: { + ...DEFAULT_TOOLKIT_CONFIG.offset, + ...(baseConfig.offset || {}), + }, + tuningModal: { + ...DEFAULT_TOOLKIT_CONFIG.tuningModal, + ...(baseConfig.tuningModal || {}), + classNames: { + ...(baseConfig.tuningModal?.classNames || {}), + }, + styles: { + ...(baseConfig.tuningModal?.styles || {}), + }, + tokens: { + ...(baseConfig.tuningModal?.tokens || {}), + }, + }, + }; + } + + return { + ...baseConfig, + ...nextConfig, + iconUrl: + nextConfig.iconUrl !== undefined + ? nextConfig.iconUrl + : baseConfig.iconUrl, + offset: { + ...DEFAULT_TOOLKIT_CONFIG.offset, + ...(baseConfig.offset || {}), + ...(nextConfig.offset || {}), + }, + tuningModal: { + ...DEFAULT_TOOLKIT_CONFIG.tuningModal, + ...(baseConfig.tuningModal || {}), + ...(nextConfig.tuningModal || {}), + classNames: { + ...(baseConfig.tuningModal?.classNames || {}), + ...(nextConfig.tuningModal?.classNames || {}), + }, + styles: { + ...(baseConfig.tuningModal?.styles || {}), + ...(nextConfig.tuningModal?.styles || {}), + }, + tokens: { + ...(baseConfig.tuningModal?.tokens || {}), + ...(nextConfig.tuningModal?.tokens || {}), + }, + }, + }; +}; + +const setPosition = ( + element: HTMLElement, + position: ToolkitPosition, + xOffset: number, + yOffset: number, +) => { + element.style.top = ''; + element.style.right = ''; + element.style.bottom = ''; + element.style.left = ''; + + if (position === 'top-left') { + element.style.top = `${yOffset}px`; + element.style.left = `${xOffset}px`; + return; + } + + if (position === 'top-right') { + element.style.top = `${yOffset}px`; + element.style.right = `${xOffset}px`; + return; + } + + if (position === 'bottom-left') { + element.style.bottom = `${yOffset}px`; + element.style.left = `${xOffset}px`; + return; + } + + element.style.bottom = `${yOffset}px`; + element.style.right = `${xOffset}px`; +}; + +const toKebabCase = (value: string): string => + value.replace(/[A-Z]/g, (letter) => `-${letter.toLowerCase()}`); + +const getTuningTokenName = (tokenName: string): string => + `--agentic-react-tuning-${toKebabCase(tokenName)}`; + +const applyStyleDeclaration = ( + element: HTMLElement, + styleDeclaration: ToolkitTuningModalStyle | undefined, +) => { + if (!styleDeclaration) return; + + for (const [propertyName, propertyValue] of Object.entries( + styleDeclaration, + )) { + if (propertyName.startsWith('--') || propertyName.includes('-')) { + element.style.setProperty(propertyName, String(propertyValue)); + } else { + (element.style as CSSStyleDeclaration & Record)[ + propertyName + ] = String(propertyValue); + } + } +}; + +const parseCssColor = (color: string): RgbaColor | null => { + const normalizedColor = color.trim().toLowerCase(); + if ( + !normalizedColor || + normalizedColor === 'transparent' || + normalizedColor === 'currentcolor' + ) { + return null; + } + + const colorMatch = normalizedColor.match(/^rgba?\((.+)\)$/); + if (!colorMatch) { + return null; + } + + const colorParts = colorMatch[1] + .replace(/\s*\/\s*/, ' ') + .split(/[,\s]+/) + .filter(Boolean); + + if (colorParts.length < 3) { + return null; + } + + const parseChannel = (channel: string): number | null => { + if (channel.endsWith('%')) { + const percentValue = Number.parseFloat(channel); + return Number.isFinite(percentValue) + ? Math.round((Math.max(0, Math.min(100, percentValue)) / 100) * 255) + : null; + } + + const value = Number.parseFloat(channel); + return Number.isFinite(value) ? Math.max(0, Math.min(255, value)) : null; + }; + + const parseAlpha = (alpha: string | undefined): number => { + if (!alpha) return 1; + + if (alpha.endsWith('%')) { + const percentValue = Number.parseFloat(alpha); + return Number.isFinite(percentValue) + ? Math.max(0, Math.min(1, percentValue / 100)) + : 1; + } + + const value = Number.parseFloat(alpha); + return Number.isFinite(value) ? Math.max(0, Math.min(1, value)) : 1; + }; + + const r = parseChannel(colorParts[0]); + const g = parseChannel(colorParts[1]); + const b = parseChannel(colorParts[2]); + + if (r === null || g === null || b === null) { + return null; + } + + return { + r, + g, + b, + a: parseAlpha(colorParts[3]), + }; +}; + +const getRelativeLuminance = ({ r, g, b }: RgbaColor): number => { + const toLinearChannel = (channel: number): number => { + const normalizedChannel = channel / 255; + return normalizedChannel <= 0.03928 + ? normalizedChannel / 12.92 + : ((normalizedChannel + 0.055) / 1.055) ** 2.4; + }; + + return ( + 0.2126 * toLinearChannel(r) + + 0.7152 * toLinearChannel(g) + + 0.0722 * toLinearChannel(b) + ); +}; + +const getEffectiveBackgroundColor = (element: Element): RgbaColor | null => { + let currentElement: Element | null = element; + + while (currentElement) { + const computedStyle = window.getComputedStyle(currentElement); + const backgroundColor = parseCssColor(computedStyle.backgroundColor); + if (backgroundColor && backgroundColor.a > 0.05) { + return backgroundColor; + } + + currentElement = currentElement.parentElement; + } + + return parseCssColor(window.getComputedStyle(document.body).backgroundColor); +}; + +const applyHoverContrastForElement = ( + hoverElement: HTMLElement, + element: Element, +) => { + const backgroundColor = getEffectiveBackgroundColor(element); + const isDarkSurface = backgroundColor + ? getRelativeLuminance(backgroundColor) < 0.46 + : false; + + hoverElement.style.setProperty( + '--agentic-react-hover-inner', + isDarkSurface ? CONTRAST_LIGHT : CONTRAST_DARK, + ); + hoverElement.style.setProperty( + '--agentic-react-hover-outer', + isDarkSurface ? CONTRAST_DARK : CONTRAST_LIGHT, + ); + hoverElement.style.setProperty( + '--agentic-react-hover-fill', + isDarkSurface ? 'rgba(255, 255, 255, 0.14)' : 'rgba(15, 23, 42, 0.08)', + ); +}; + +const normalizeElementLabel = (value: string | null | undefined): string => { + if (!value) return ''; + return value.replace(/\s+/g, ' ').trim(); +}; + +const truncateElementLabel = ( + value: string, + maxLength = SELECTED_LABEL_MAX_LENGTH, +): string => { + if (value.length <= maxLength) return value; + return `${value.slice(0, maxLength - 1).trimEnd()}...`; +}; + +const getLabelledByText = (element: Element): string => { + const labelledBy = element.getAttribute('aria-labelledby'); + if (!labelledBy) return ''; + + const labelText = labelledBy + .split(/\s+/) + .map((id) => document.getElementById(id)?.textContent) + .filter(Boolean) + .join(' '); + return normalizeElementLabel(labelText); +}; + +const getAssociatedFormLabelText = (element: Element): string => { + if ( + element instanceof HTMLInputElement || + element instanceof HTMLSelectElement || + element instanceof HTMLTextAreaElement || + element instanceof HTMLButtonElement + ) { + const labels = Array.from(element.labels ?? []); + return normalizeElementLabel( + labels.map((labelElement) => labelElement.textContent).join(' '), + ); + } + + return ''; +}; + +const getMeaningfulVisibleText = (element: Element): string => { + const textContent = normalizeElementLabel(element.textContent); + if (!textContent) return ''; + + const repeatedWhitespaceText = textContent.replace(/\s+/g, ''); + if (repeatedWhitespaceText.length < 2) return ''; + return textContent; +}; + +const formatDomElementLabel = (element: Element, label: string): string => { + const tagName = element.tagName.toLowerCase(); + if (!label) return tagName; + + if (/^(button|a|input|select|textarea|img)$/i.test(tagName)) { + return `${tagName}: ${label}`; + } + + return label; +}; + +const formatSourceLocationLabel = ( + selectionContext?: SelectionContext | null, +): string => { + const source = + selectionContext?.resolvedSources[0] ?? + selectionContext?.externalComponent?.usedBy ?? + selectionContext?.sourceTrace.find((frame) => frame.kind === 'project'); + if (!source?.filePath) return ''; + + const fileName = source.filePath.split('/').pop() || source.filePath; + return source.lineNumber ? `${fileName}:${source.lineNumber}` : fileName; +}; + +const formatComponentLocationLabel = ( + componentName?: string | null, + sourceLocationLabel?: string | null, +): string => { + if (!componentName) return sourceLocationLabel || ''; + + const componentLabel = `<${componentName}>`; + return sourceLocationLabel + ? `${componentLabel} in ${sourceLocationLabel}` + : componentLabel; +}; + +const buildTuningTargetLabel = (selectionContext: SelectionContext): string => + formatComponentLocationLabel( + selectionContext.componentName || + selectionContext.externalComponent?.componentName, + formatSourceLocationLabel(selectionContext), + ) || + selectionContext.selector || + 'the selected element'; + +const clampNumber = (value: number, min: number, max: number): number => + Math.max(min, Math.min(max, value)); + +const rgbStringToHex = (colorValue: string): string => { + const match = colorValue.match(/rgba?\(\s*(\d+)[,\s]+(\d+)[,\s]+(\d+)/i); + if (!match) return '#111827'; + + return `#${[match[1], match[2], match[3]] + .map((part) => + clampNumber(Number(part), 0, 255).toString(16).padStart(2, '0'), + ) + .join('')}`; +}; + +const hexToRgbString = (hexValue: string): string => { + const normalizedHex = hexValue.replace('#', '').trim(); + if (!/^[0-9a-f]{6}$/i.test(normalizedHex)) return 'rgb(17, 24, 39)'; + + const red = Number.parseInt(normalizedHex.slice(0, 2), 16); + const green = Number.parseInt(normalizedHex.slice(2, 4), 16); + const blue = Number.parseInt(normalizedHex.slice(4, 6), 16); + return `rgb(${red}, ${green}, ${blue})`; +}; + +const formatAlpha = (alphaValue: number): string => + Number(alphaValue.toFixed(2)).toString(); + +const formatRgbaColor = ({ r, g, b, a }: RgbaColor): string => + `rgba(${r}, ${g}, ${b}, ${formatAlpha(a)})`; + +const cssColorToRgbaString = ( + colorValue: string, + fallbackColor: RgbaColor = { r: 0, g: 0, b: 0, a: 0 }, +): string => formatRgbaColor(parseCssColor(colorValue) || fallbackColor); + +const hexToRgbaString = (hexValue: string): string => { + const rgbColor = parseCssColor(hexToRgbString(hexValue)); + return rgbColor ? formatRgbaColor(rgbColor) : 'rgba(17, 24, 39, 1)'; +}; + +const hasTuneableText = (element: Element): boolean => { + const tagName = element.tagName.toLowerCase(); + if ( + /^(a|button|input|label|legend|option|p|span|strong|em|small|textarea|h[1-6])$/.test( + tagName, + ) + ) { + return true; + } + + if (getMeaningfulVisibleText(element)) { + return true; + } + + return Array.from(element.children).some((childElement) => + hasTuneableText(childElement), + ); +}; + +const hasTuneableLayout = ( + element: Element, + computedStyle: CSSStyleDeclaration, +) => { + const tagName = element.tagName.toLowerCase(); + if (/^(flex|inline-flex|grid|inline-grid)$/.test(computedStyle.display)) { + return true; + } + + if ( + /^(article|aside|body|div|fieldset|footer|form|header|label|li|main|nav|ol|section|table|tbody|td|tfoot|th|thead|tr|ul)$/.test( + tagName, + ) + ) { + return true; + } + + return element.children.length > 0; +}; + +const normalizeCssNumber = (value: string, fallbackValue: number): number => { + const parsedValue = Number.parseFloat(value); + return Number.isFinite(parsedValue) ? parsedValue : fallbackValue; +}; + +const buildSelectedElementLabel = ( + element: Element, + selectionContext?: SelectionContext | null, + componentNameOverride?: string | null, +): string => { + const componentName = + componentNameOverride || + selectionContext?.componentName || + selectionContext?.externalComponent?.componentName; + const sourceLocationLabel = formatSourceLocationLabel(selectionContext); + const componentLocationLabel = formatComponentLocationLabel( + componentName, + sourceLocationLabel, + ); + + const labelCandidates = [ + componentLocationLabel, + element.getAttribute('aria-label'), + getLabelledByText(element), + getAssociatedFormLabelText(element), + element.getAttribute('alt'), + element.getAttribute('title'), + element.getAttribute('placeholder'), + element.getAttribute('name'), + element.id ? `#${element.id}` : '', + ]; + + for (const labelCandidate of labelCandidates) { + const normalizedLabel = normalizeElementLabel(labelCandidate); + if (!normalizedLabel) continue; + + if (normalizedLabel === componentLocationLabel) { + return truncateElementLabel(normalizedLabel); + } + + return truncateElementLabel( + formatDomElementLabel(element, normalizedLabel), + ); + } + + if (sourceLocationLabel) { + return truncateElementLabel(sourceLocationLabel); + } + + const visibleTextLabel = getMeaningfulVisibleText(element); + if (visibleTextLabel) { + return truncateElementLabel( + formatDomElementLabel(element, visibleTextLabel), + ); + } + + return truncateElementLabel( + sourceLocationLabel || element.tagName.toLowerCase(), + ); +}; + +const getSourceRoot = (): string | undefined => + target[__AGENTIC_REACT_CONFIG__]?.sourceRoot; + +const toTextContext = (selectionContext: SelectionContext): string => + buildWebContextText(selectionContext, { + sourceRoot: getSourceRoot(), + }); + +const copyText = async (text: string): Promise => { + if (target.navigator?.clipboard?.writeText) { + try { + await target.navigator.clipboard.writeText(text); + return true; + } catch (_error) { + // fallback below + } + } + + try { + const textAreaElement = document.createElement('textarea'); + textAreaElement.value = text; + textAreaElement.style.position = 'fixed'; + textAreaElement.style.left = '-9999px'; + document.body.appendChild(textAreaElement); + textAreaElement.select(); + const didCopy = document.execCommand('copy'); + textAreaElement.remove(); + return didCopy; + } catch (_error) { + return false; + } +}; + +const createOverlayIconButton = ( + label: string, + iconPathMarkup: string, +): HTMLButtonElement => { + const buttonElement = document.createElement('button'); + const iconElement = document.createElementNS( + 'http://www.w3.org/2000/svg', + 'svg', + ); + + buttonElement.setAttribute('type', 'button'); + buttonElement.setAttribute('aria-label', label); + buttonElement.setAttribute('title', label); + buttonElement.style.width = '24px'; + buttonElement.style.height = '24px'; + buttonElement.style.border = '1px solid rgba(255, 255, 255, 0.86)'; + buttonElement.style.borderRadius = '6px'; + buttonElement.style.background = 'rgba(15, 23, 42, 0.96)'; + buttonElement.style.color = '#ffffff'; + buttonElement.style.display = 'inline-flex'; + buttonElement.style.alignItems = 'center'; + buttonElement.style.justifyContent = 'center'; + buttonElement.style.padding = '0'; + buttonElement.style.cursor = 'pointer'; + buttonElement.style.pointerEvents = 'auto'; + + iconElement.setAttribute('viewBox', '0 0 24 24'); + iconElement.setAttribute('width', '14'); + iconElement.setAttribute('height', '14'); + iconElement.setAttribute('fill', 'none'); + iconElement.setAttribute('stroke', 'currentColor'); + iconElement.setAttribute('stroke-width', '2'); + iconElement.setAttribute('stroke-linecap', 'round'); + iconElement.setAttribute('stroke-linejoin', 'round'); + iconElement.setAttribute('aria-hidden', 'true'); + iconElement.innerHTML = iconPathMarkup; + buttonElement.appendChild(iconElement); + + return buttonElement; +}; + +const createOverlayActions = (): OverlayActionElements => { + const actionsElement = document.createElement('div'); + const tuneButtonElement = createOverlayIconButton( + 'Adjust selection', + '', + ); + const deleteButtonElement = createOverlayIconButton( + 'Delete selection', + '', + ); + + actionsElement.setAttribute('data-agentic-react-selected-actions', 'true'); + actionsElement.style.position = 'absolute'; + actionsElement.style.pointerEvents = 'auto'; + actionsElement.style.display = 'inline-flex'; + actionsElement.style.gap = '4px'; + actionsElement.style.alignItems = 'center'; + + actionsElement.appendChild(tuneButtonElement); + actionsElement.appendChild(deleteButtonElement); + + return { + actionsElement, + tuneButtonElement, + deleteButtonElement, + }; +}; + +const getNormalizedSourceRoot = (): string => + normalizeSourceRoot(getSourceRoot()); + +const extractRawSourceFromViteModule = (moduleText: string): string | null => { + const trimmedText = moduleText.trim(); + const defaultExportMatch = trimmedText.match( + /^export\s+default\s+((?:"(?:\\.|[^"])*")|(?:'(?:\\.|[^'])*'));\s*(?:\/\/# sourceMappingURL=[\s\S]*)?$/, + ); + + if (!defaultExportMatch) { + return null; + } + + const exportedValueText = defaultExportMatch[1]?.trim(); + if (!exportedValueText) { + return null; + } + + if ( + (exportedValueText.startsWith('"') && exportedValueText.endsWith('"')) || + (exportedValueText.startsWith("'") && exportedValueText.endsWith("'")) + ) { + try { + const normalizedQuotedText = exportedValueText.startsWith("'") + ? `"${exportedValueText + .slice(1, -1) + .replace(/\\/g, '\\\\') + .replace(/"/g, '\\"')}"` + : exportedValueText; + return JSON.parse(normalizedQuotedText); + } catch (_error) { + return null; + } + } + + return null; +}; + +const normalizeSourceTextFromViteResponse = ( + responseText: string, +): string | null => { + const rawSource = extractRawSourceFromViteModule(responseText); + if (rawSource) { + return rawSource; + } + + if (responseText.includes('/node_modules/.vite/deps/')) { + return null; + } + + return responseText; +}; + +const placeResolvedSourceFirst = ( + resolvedSources: SelectionResolvedSource[], + preferredSource: SelectionResolvedSource, +): SelectionResolvedSource[] => [ + preferredSource, + ...resolvedSources.filter( + (source) => + source.filePath !== preferredSource.filePath || + source.componentName !== preferredSource.componentName, + ), +]; + +const enrichSelectionContextSourceLocation = async ( + selectionContext: SelectionContext, +): Promise => { + if (!selectionContext.componentName || !selectionContext.selector) { + return selectionContext; + } + + try { + const lookupUrl = new URL(SOURCE_LOOKUP_PATH, window.location.origin); + lookupUrl.searchParams.set('component', selectionContext.componentName); + lookupUrl.searchParams.set('selector', selectionContext.selector); + const response = await fetch(lookupUrl, { cache: 'no-store' }); + if (!response.ok) { + return selectionContext; + } + + const source = (await response.json()) as SelectionResolvedSource; + if (!source.filePath || !source.componentName) { + return selectionContext; + } + + const nextSelectionContext = { + ...selectionContext, + externalComponent: selectionContext.externalComponent + ? { + ...selectionContext.externalComponent, + usedBy: source, + } + : null, + resolvedSources: placeResolvedSourceFirst( + selectionContext.resolvedSources, + source, + ), + }; + + return { + ...nextSelectionContext, + sourcePreview: buildSelectionSourcePreview(nextSelectionContext), + }; + } catch (_error) { + return selectionContext; + } +}; + +const buildSourceSnippet = ( + sourceText: string, + lineNumber: number, + contextLines: number, +): { startLine: number; endLine: number; snippet: string } | null => { + const lines = sourceText.split(/\r?\n/); + if (lines.length === 0) { + return null; + } + + const safeLineNumber = Math.max(1, Math.min(lineNumber, lines.length)); + const startLine = Math.max(1, safeLineNumber - contextLines); + const endLine = Math.min(lines.length, safeLineNumber + contextLines); + const snippet = lines + .slice(startLine - 1, endLine) + .join('\n') + .trim(); + + if (!snippet) { + return null; + } + + return { + startLine, + endLine, + snippet, + }; +}; + +export const createSelectionToolkit = ( + options: ToolkitRuntimeOptions = {}, +): ToolkitRuntimeResult => { + let toolkitConfig = mergeToolkitConfig(options.initialConfig, undefined); + let isToolkitVisible = toolkitConfig.defaultVisible && toolkitConfig.enabled; + let isPanelOpen = toolkitConfig.defaultExpanded; + let isSelectionMode = false; + let isMultiSelectionMode = false; + let lastSelectionContext: SelectionContext | null = null; + let multiSelectionContexts: SelectionContext[] = []; + + const toolkitRootElement = document.createElement('div'); + const launcherButtonElement = document.createElement('button'); + const launcherIconElement = document.createElement('img'); + + const panelElement = document.createElement('div'); + const selectButtonElement = document.createElement('button'); + const multiselectButtonElement = document.createElement('button'); + const doneButtonElement = document.createElement('button'); + const clearAllButtonElement = document.createElement('button'); + const clearAllIconElement = document.createElementNS( + 'http://www.w3.org/2000/svg', + 'svg', + ); + const clearAllLabelElement = document.createElement('span'); + const statusElement = document.createElement('div'); + const hoverElement = document.createElement('div'); + const hoverLabelElement = document.createElement('div'); + const selectedElement = document.createElement('div'); + const selectedLabelElement = document.createElement('div'); + const selectedActions = createOverlayActions(); + const tuningModalElement = document.createElement('div'); + const tuningSurfaceElement = document.createElement('div'); + const tuningPanelElement = document.createElement('div'); + const tuningArrowElement = document.createElement('div'); + const tuningTitleElement = document.createElement('div'); + const tuningBodyElement = document.createElement('div'); + const tuningCloseButtonElement = document.createElement('button'); + const dimElements = Array.from({ length: 4 }, () => + document.createElement('div'), + ); + let selectedTargetElement: Element | null = null; + let dimTargetElement: Element | null = null; + let hoverLabelTargetElement: Element | null = null; + let hoverLabelRequestId = 0; + let dimHideTimeout: number | null = null; + let selectedPulseTimeout: number | null = null; + let activeTuningSession: TuningSession | null = null; + let tuningModalExtensions: TuningModalExtension[] = []; + const tuningExtensionCleanups: Array<() => void> = []; + let multiSelectedOverlays: Array<{ + element: Element; + overlayElement: HTMLDivElement; + labelElement: HTMLDivElement; + actions: OverlayActionElements; + selectionContext: SelectionContext; + pulseTimeout: number | null; + }> = []; + + toolkitRootElement.setAttribute('data-agentic-react-toolkit', 'true'); + toolkitRootElement.style.position = 'fixed'; + toolkitRootElement.style.display = 'flex'; + toolkitRootElement.style.flexDirection = 'column'; + toolkitRootElement.style.alignItems = 'flex-end'; + toolkitRootElement.style.gap = '10px'; + toolkitRootElement.style.fontFamily = 'ui-sans-serif, system-ui, sans-serif'; + toolkitRootElement.style.pointerEvents = 'auto'; + + launcherButtonElement.setAttribute('type', 'button'); + launcherButtonElement.setAttribute( + 'aria-label', + 'Open Agentic React toolkit', + ); + launcherButtonElement.setAttribute('data-agentic-react-launcher', 'true'); + launcherButtonElement.style.width = `${LAUNCHER_SIZE}px`; + launcherButtonElement.style.height = `${LAUNCHER_SIZE}px`; + launcherButtonElement.style.borderRadius = '999px'; + launcherButtonElement.style.border = 'none'; + launcherButtonElement.style.background = 'transparent'; + launcherButtonElement.style.boxShadow = + 'var(--agentic-react-launcher-shadow)'; + launcherButtonElement.style.cursor = 'pointer'; + launcherButtonElement.style.display = 'flex'; + launcherButtonElement.style.alignItems = 'center'; + launcherButtonElement.style.justifyContent = 'center'; + launcherButtonElement.style.padding = '0'; + launcherButtonElement.style.overflow = 'hidden'; + launcherButtonElement.style.transition = + 'transform 140ms ease, box-shadow 140ms ease, filter 140ms ease, background-color 140ms ease'; + launcherButtonElement.style.willChange = 'transform, box-shadow, filter'; + + launcherIconElement.style.width = '100%'; + launcherIconElement.style.height = '100%'; + launcherIconElement.style.objectFit = 'cover'; + launcherIconElement.style.display = 'block'; + + launcherButtonElement.appendChild(launcherIconElement); + + panelElement.style.display = 'none'; + panelElement.style.flexDirection = 'column'; + panelElement.style.gap = '8px'; + panelElement.style.background = 'rgba(255,255,255,0.96)'; + panelElement.style.border = '1px solid rgba(0,0,0,0.12)'; + panelElement.style.borderRadius = '10px'; + panelElement.style.padding = '10px'; + panelElement.style.minWidth = '206px'; + panelElement.style.maxWidth = '280px'; + panelElement.style.boxShadow = '0 16px 35px rgba(15, 23, 42, 0.26)'; + + const createButton = (buttonElement: HTMLButtonElement, label: string) => { + buttonElement.textContent = label; + buttonElement.style.padding = '8px 12px'; + buttonElement.style.borderRadius = '8px'; + buttonElement.style.border = '1px solid rgba(255,255,255,0.2)'; + buttonElement.style.background = toolkitConfig.accentColor; + buttonElement.style.color = '#ffffff'; + buttonElement.style.fontSize = '12px'; + buttonElement.style.fontWeight = '600'; + buttonElement.style.cursor = 'pointer'; + buttonElement.style.boxShadow = '0 8px 24px rgba(0,0,0,0.2)'; + buttonElement.style.minWidth = '96px'; + }; + + const createTrashIconButton = () => { + clearAllButtonElement.setAttribute('type', 'button'); + clearAllButtonElement.setAttribute('aria-label', 'Clear all selections'); + clearAllButtonElement.setAttribute('title', 'Clear all selections'); + clearAllButtonElement.setAttribute('data-agentic-react-clear-all', 'true'); + clearAllButtonElement.style.padding = '8px 12px'; + clearAllButtonElement.style.borderRadius = '8px'; + clearAllButtonElement.style.border = '1px solid rgba(255,255,255,0.2)'; + clearAllButtonElement.style.background = '#334155'; + clearAllButtonElement.style.color = '#ffffff'; + clearAllButtonElement.style.cursor = 'pointer'; + clearAllButtonElement.style.boxShadow = '0 8px 24px rgba(0,0,0,0.2)'; + clearAllButtonElement.style.minWidth = '96px'; + clearAllButtonElement.style.height = '34px'; + clearAllButtonElement.style.display = 'none'; + clearAllButtonElement.style.alignItems = 'center'; + clearAllButtonElement.style.justifyContent = 'center'; + clearAllButtonElement.style.gap = '6px'; + clearAllButtonElement.style.fontSize = '12px'; + clearAllButtonElement.style.fontWeight = '600'; + + clearAllIconElement.setAttribute('viewBox', '0 0 24 24'); + clearAllIconElement.setAttribute('width', '16'); + clearAllIconElement.setAttribute('height', '16'); + clearAllIconElement.setAttribute('fill', 'none'); + clearAllIconElement.setAttribute('stroke', 'currentColor'); + clearAllIconElement.setAttribute('stroke-width', '2'); + clearAllIconElement.setAttribute('stroke-linecap', 'round'); + clearAllIconElement.setAttribute('stroke-linejoin', 'round'); + clearAllIconElement.setAttribute('aria-hidden', 'true'); + clearAllIconElement.innerHTML = + ''; + + clearAllLabelElement.textContent = 'Clear all'; + clearAllLabelElement.style.lineHeight = '1'; + clearAllLabelElement.style.whiteSpace = 'nowrap'; + + clearAllButtonElement.appendChild(clearAllIconElement); + clearAllButtonElement.appendChild(clearAllLabelElement); + }; + + createButton(selectButtonElement, 'Select'); + createButton(multiselectButtonElement, 'Multiselect'); + createButton(doneButtonElement, 'Done'); + createTrashIconButton(); + + doneButtonElement.style.background = '#dc2626'; + doneButtonElement.style.color = '#ffffff'; + doneButtonElement.style.display = 'none'; + + statusElement.style.fontSize = '11px'; + statusElement.style.color = '#111827'; + statusElement.style.background = 'rgba(255, 255, 255, 0.95)'; + statusElement.style.border = '1px solid rgba(0,0,0,0.1)'; + statusElement.style.borderRadius = '8px'; + statusElement.style.padding = '6px 8px'; + statusElement.style.maxWidth = '280px'; + statusElement.style.display = 'none'; + + panelElement.appendChild(selectButtonElement); + panelElement.appendChild(multiselectButtonElement); + panelElement.appendChild(doneButtonElement); + panelElement.appendChild(clearAllButtonElement); + panelElement.appendChild(statusElement); + + toolkitRootElement.appendChild(panelElement); + toolkitRootElement.appendChild(launcherButtonElement); + + hoverElement.setAttribute('data-agentic-react-hover', 'true'); + hoverElement.style.position = 'fixed'; + hoverElement.style.pointerEvents = 'none'; + hoverElement.style.display = 'none'; + hoverElement.style.setProperty('--agentic-react-hover-inner', CONTRAST_DARK); + hoverElement.style.setProperty('--agentic-react-hover-outer', CONTRAST_LIGHT); + hoverElement.style.setProperty( + '--agentic-react-hover-fill', + 'rgba(15, 23, 42, 0.08)', + ); + hoverElement.appendChild(hoverLabelElement); + + hoverLabelElement.setAttribute('data-agentic-react-hover-label', 'true'); + hoverLabelElement.style.position = 'absolute'; + hoverLabelElement.style.pointerEvents = 'none'; + hoverLabelElement.style.maxWidth = 'min(420px, calc(100vw - 24px))'; + hoverLabelElement.style.overflow = 'hidden'; + hoverLabelElement.style.textOverflow = 'ellipsis'; + hoverLabelElement.style.whiteSpace = 'nowrap'; + + selectedElement.setAttribute('data-agentic-react-selected', 'true'); + selectedElement.style.position = 'fixed'; + selectedElement.style.pointerEvents = 'none'; + selectedElement.style.display = 'none'; + selectedElement.style.background = 'transparent'; + selectedElement.appendChild(selectedLabelElement); + selectedElement.appendChild(selectedActions.actionsElement); + + selectedLabelElement.setAttribute( + 'data-agentic-react-selected-label', + 'true', + ); + selectedLabelElement.style.position = 'absolute'; + selectedLabelElement.style.pointerEvents = 'none'; + selectedLabelElement.style.maxWidth = 'min(260px, calc(100vw - 24px))'; + selectedLabelElement.style.overflow = 'hidden'; + selectedLabelElement.style.textOverflow = 'ellipsis'; + selectedLabelElement.style.whiteSpace = 'nowrap'; + + tuningModalElement.setAttribute('data-agentic-react-tuning-modal', 'true'); + tuningModalElement.style.position = 'fixed'; + tuningModalElement.style.inset = '0'; + tuningModalElement.style.display = 'none'; + tuningModalElement.style.background = 'transparent'; + tuningModalElement.style.pointerEvents = 'auto'; + + tuningSurfaceElement.setAttribute( + 'data-agentic-react-tuning-surface', + 'true', + ); + tuningSurfaceElement.style.position = 'fixed'; + tuningSurfaceElement.style.pointerEvents = 'auto'; + tuningSurfaceElement.style.width = 'fit-content'; + tuningSurfaceElement.style.maxWidth = 'calc(100vw - 32px)'; + + tuningPanelElement.setAttribute('data-agentic-react-tuning-panel', 'true'); + tuningPanelElement.style.position = 'relative'; + tuningPanelElement.style.width = 'min(420px, calc(100vw - 32px))'; + tuningPanelElement.style.maxHeight = 'min(50vh, calc(100vh - 24px))'; + tuningPanelElement.style.overflowY = 'auto'; + tuningPanelElement.style.overscrollBehavior = 'contain'; + tuningPanelElement.style.background = + 'var(--agentic-react-tuning-panel-background, #ffffff)'; + tuningPanelElement.style.border = + 'var(--agentic-react-tuning-panel-border, 1px solid rgba(15, 23, 42, 0.14))'; + tuningPanelElement.style.borderRadius = + 'var(--agentic-react-tuning-panel-radius, 10px)'; + tuningPanelElement.style.boxShadow = + 'var(--agentic-react-tuning-panel-shadow, 0 22px 60px rgba(15, 23, 42, 0.3))'; + tuningPanelElement.style.padding = '12px'; + tuningPanelElement.style.color = + 'var(--agentic-react-tuning-panel-color, #111827)'; + tuningPanelElement.style.fontFamily = 'ui-sans-serif, system-ui, sans-serif'; + tuningPanelElement.style.pointerEvents = 'auto'; + + tuningArrowElement.setAttribute('aria-hidden', 'true'); + tuningArrowElement.style.position = 'fixed'; + tuningArrowElement.style.width = '10px'; + tuningArrowElement.style.height = '10px'; + tuningArrowElement.style.background = + 'var(--agentic-react-tuning-panel-background, #ffffff)'; + tuningArrowElement.style.transform = 'rotate(45deg)'; + tuningArrowElement.style.pointerEvents = 'none'; + + tuningTitleElement.style.fontSize = '13px'; + tuningTitleElement.style.fontWeight = '700'; + tuningTitleElement.style.marginBottom = '8px'; + + tuningBodyElement.style.display = 'flex'; + tuningBodyElement.style.flexDirection = 'column'; + tuningBodyElement.style.gap = '8px'; + + tuningCloseButtonElement.setAttribute('type', 'button'); + tuningCloseButtonElement.setAttribute('aria-label', 'Close tuning options'); + tuningCloseButtonElement.textContent = 'Close'; + tuningCloseButtonElement.style.marginTop = '12px'; + tuningCloseButtonElement.style.width = '100%'; + tuningCloseButtonElement.style.padding = '8px 10px'; + tuningCloseButtonElement.style.border = + 'var(--agentic-react-tuning-control-border, 1px solid rgba(15, 23, 42, 0.14))'; + tuningCloseButtonElement.style.borderRadius = + 'var(--agentic-react-tuning-control-radius, 8px)'; + tuningCloseButtonElement.style.background = + 'var(--agentic-react-tuning-secondary-button-background, #f8fafc)'; + tuningCloseButtonElement.style.color = + 'var(--agentic-react-tuning-secondary-button-color, #111827)'; + tuningCloseButtonElement.style.cursor = 'pointer'; + + tuningPanelElement.appendChild(tuningTitleElement); + tuningPanelElement.appendChild(tuningBodyElement); + tuningPanelElement.appendChild(tuningCloseButtonElement); + tuningModalElement.appendChild(tuningArrowElement); + tuningSurfaceElement.appendChild(tuningPanelElement); + tuningModalElement.appendChild(tuningSurfaceElement); + + const getTuningModalConfig = () => toolkitConfig.tuningModal || {}; + + const applyTuningTokens = (element: HTMLElement) => { + const tokens = getTuningModalConfig().tokens || {}; + for (const [tokenName, tokenValue] of Object.entries(tokens)) { + element.style.setProperty( + getTuningTokenName(tokenName), + String(tokenValue), + ); + } + }; + + const applyTuningSlotStyles = ( + element: HTMLElement, + ...slots: ToolkitTuningModalStyleSlot[] + ) => { + const tuningModalConfig = getTuningModalConfig(); + for (const slot of slots) { + const className = tuningModalConfig.classNames?.[slot]; + if (className) { + element.classList.add( + ...className.split(/\s+/).filter((name) => name.length > 0), + ); + } + applyStyleDeclaration(element, tuningModalConfig.styles?.[slot]); + } + }; + + const applyTuningModalStaticStyles = () => { + applyTuningTokens(tuningModalElement); + applyTuningSlotStyles(tuningModalElement, 'root'); + applyTuningSlotStyles(tuningSurfaceElement, 'surface'); + applyTuningSlotStyles(tuningPanelElement, 'panel'); + applyTuningSlotStyles(tuningArrowElement, 'arrow'); + applyTuningSlotStyles(tuningTitleElement, 'title'); + applyTuningSlotStyles(tuningBodyElement, 'body'); + applyTuningSlotStyles(tuningCloseButtonElement, 'closeButton'); + }; + + applyTuningModalStaticStyles(); + + for (const dimElement of dimElements) { + dimElement.setAttribute('data-agentic-react-dim', 'true'); + dimElement.style.position = 'fixed'; + dimElement.style.pointerEvents = 'none'; + dimElement.style.display = 'none'; + dimElement.style.background = 'rgba(15, 23, 42, 0.28)'; + dimElement.style.backdropFilter = 'saturate(0.8)'; + } + + const mountSelectionStyles = () => { + if (document.getElementById(SELECTION_STYLE_ID)) { + return; + } + + const styleElement = document.createElement('style'); + styleElement.id = SELECTION_STYLE_ID; + styleElement.textContent = ` + [data-agentic-react-selected="true"] { + box-sizing: border-box; + border: 2px solid rgba(255, 255, 255, 0.98); + box-shadow: + 0 0 0 2px rgba(15, 23, 42, 0.96), + 0 0 0 6px rgba(255, 255, 255, 0.96), + 0 0 0 9px var(--agentic-react-selection-accent, #111827), + 0 16px 38px rgba(15, 23, 42, 0.22); + transform-origin: center; + animation: agentic-react-selected-idle-pulse 2200ms cubic-bezier(0.37, 0, 0.63, 1) infinite; + backface-visibility: hidden; + will-change: transform, box-shadow; + } + + [data-agentic-react-selected-label="true"], + [data-agentic-react-hover-label="true"] { + box-sizing: border-box; + left: 0; + bottom: calc(100% + 6px); + padding: 4px 6px; + border: 1px solid rgba(255, 255, 255, 0.86); + border-radius: 6px; + background: rgba(15, 23, 42, 0.96); + color: #ffffff; + box-shadow: + 0 0 0 1px rgba(15, 23, 42, 0.72), + 0 10px 24px rgba(15, 23, 42, 0.28); + font-family: ui-sans-serif, system-ui, sans-serif; + font-size: 10px; + font-weight: 700; + line-height: 1.2; + letter-spacing: 0; + } + + [data-agentic-react-hover-label="true"] { + background: rgba(15, 23, 42, 0.9); + box-shadow: + 0 0 0 1px rgba(255, 255, 255, 0.44), + 0 8px 18px rgba(15, 23, 42, 0.24); + } + + [data-agentic-react-selected-label="true"][data-align="left"], + [data-agentic-react-hover-label="true"][data-align="left"] { + left: 0; + } + + [data-agentic-react-selected-actions="true"] { + right: 0; + bottom: calc(100% + 6px); + } + + [data-agentic-react-hover="true"] { + box-sizing: border-box; + border: 2px solid var(--agentic-react-hover-inner, rgba(15, 23, 42, 0.98)); + background: var(--agentic-react-hover-fill, rgba(15, 23, 42, 0.08)); + box-shadow: + 0 0 0 2px var(--agentic-react-hover-outer, rgba(255, 255, 255, 0.98)), + 0 0 0 5px var(--agentic-react-selection-accent, #111827), + inset 0 0 0 1px rgba(255, 255, 255, 0.18), + 0 12px 30px rgba(15, 23, 42, 0.2); + transform: translateZ(0); + transform-origin: center; + animation: agentic-react-hover-pulse 2000ms cubic-bezier(0.37, 0, 0.63, 1) infinite; + backface-visibility: hidden; + will-change: transform, box-shadow; + } + + [data-agentic-react-launcher="true"] { + box-sizing: border-box; + transform: translateZ(0); + -webkit-tap-highlight-color: transparent; + } + + [data-agentic-react-launcher="true"]:hover { + background: rgba(255, 255, 255, 0.16) !important; + box-shadow: var(--agentic-react-launcher-hover-shadow) !important; + filter: brightness(1.08) saturate(1.08); + transform: translateY(-2px) scale(1.035) !important; + } + + [data-agentic-react-launcher="true"]:focus-visible { + box-shadow: var(--agentic-react-launcher-focus-shadow) !important; + outline: none; + transform: translateY(-1px) scale(1.02) !important; + } + + [data-agentic-react-launcher="true"]:active { + filter: brightness(0.98) saturate(1.02); + transform: translateY(0) scale(0.99) !important; + } + + [data-agentic-react-selected="true"].agentic-react-selection-pulse { + animation: agentic-react-selection-pulse ${SELECTION_CONFIRMATION_MS}ms cubic-bezier(0.22, 1, 0.36, 1); + } + + @keyframes agentic-react-selected-idle-pulse { + 0%, + 100% { + transform: scale(1); + box-shadow: + 0 0 0 2px rgba(15, 23, 42, 0.96), + 0 0 0 6px rgba(255, 255, 255, 0.96), + 0 0 0 9px var(--agentic-react-selection-accent, #111827), + 0 16px 38px rgba(15, 23, 42, 0.22); + } + 42% { + transform: scale(1.009); + box-shadow: + 0 0 0 3px rgba(15, 23, 42, 0.98), + 0 0 0 7px rgba(255, 255, 255, 0.97), + 0 0 0 11px var(--agentic-react-selection-accent, #111827), + 0 17px 40px rgba(15, 23, 42, 0.24); + } + 70% { + transform: scale(0.999); + box-shadow: + 0 0 0 2px rgba(15, 23, 42, 0.96), + 0 0 0 6px rgba(255, 255, 255, 0.94), + 0 0 0 8px var(--agentic-react-selection-accent, #111827), + 0 15px 36px rgba(15, 23, 42, 0.21); + } + } + + @keyframes agentic-react-hover-pulse { + 0%, + 100% { + transform: translateZ(0) scale(1); + box-shadow: + 0 0 0 2px var(--agentic-react-hover-outer, rgba(255, 255, 255, 0.98)), + 0 0 0 5px var(--agentic-react-selection-accent, #111827), + inset 0 0 0 1px rgba(255, 255, 255, 0.18), + 0 12px 30px rgba(15, 23, 42, 0.2); + } + 44% { + transform: translateZ(0) scale(1.01); + box-shadow: + 0 0 0 3px var(--agentic-react-hover-outer, rgba(255, 255, 255, 0.98)), + 0 0 0 7px var(--agentic-react-selection-accent, #111827), + inset 0 0 0 1px rgba(255, 255, 255, 0.22), + 0 14px 34px rgba(15, 23, 42, 0.22); + } + 72% { + transform: translateZ(0) scale(0.999); + box-shadow: + 0 0 0 2px var(--agentic-react-hover-outer, rgba(255, 255, 255, 0.98)), + 0 0 0 5px var(--agentic-react-selection-accent, #111827), + inset 0 0 0 1px rgba(255, 255, 255, 0.16), + 0 11px 28px rgba(15, 23, 42, 0.19); + } + } + + @keyframes agentic-react-selection-pulse { + 0% { + transform: scale(1); + box-shadow: + 0 0 0 2px rgba(15, 23, 42, 0.96), + 0 0 0 6px rgba(255, 255, 255, 0.96), + 0 0 0 9px var(--agentic-react-selection-accent, #111827), + 0 16px 38px rgba(15, 23, 42, 0.22); + } + 38% { + transform: scale(1.018); + box-shadow: + 0 0 0 3px rgba(15, 23, 42, 0.98), + 0 0 0 8px rgba(255, 255, 255, 0.98), + 0 0 0 13px var(--agentic-react-selection-accent, #111827), + 0 19px 44px rgba(15, 23, 42, 0.26); + } + 72% { + transform: scale(0.998); + box-shadow: + 0 0 0 2px rgba(15, 23, 42, 0.96), + 0 0 0 6px rgba(255, 255, 255, 0.94), + 0 0 0 8px var(--agentic-react-selection-accent, #111827), + 0 15px 36px rgba(15, 23, 42, 0.21); + } + 100% { + transform: scale(1); + box-shadow: + 0 0 0 2px rgba(15, 23, 42, 0.96), + 0 0 0 6px rgba(255, 255, 255, 0.96), + 0 0 0 9px var(--agentic-react-selection-accent, #111827), + 0 16px 38px rgba(15, 23, 42, 0.22); + } + } + `; + (document.head || document.documentElement).appendChild(styleElement); + }; + + const updateStatus = (message: string, shouldDisplay = true) => { + statusElement.textContent = message; + statusElement.style.display = shouldDisplay ? 'block' : 'none'; + }; + + const renderMultiSelectionControls = () => { + const hasSelections = multiSelectionContexts.length > 0; + doneButtonElement.style.display = isMultiSelectionMode ? 'block' : 'none'; + clearAllButtonElement.style.display = isMultiSelectionMode + ? 'flex' + : 'none'; + clearAllButtonElement.disabled = !hasSelections; + clearAllButtonElement.style.opacity = hasSelections ? '1' : '0.45'; + clearAllButtonElement.style.cursor = hasSelections + ? 'pointer' + : 'not-allowed'; + clearAllButtonElement.style.background = hasSelections + ? '#334155' + : '#64748b'; + }; + + const renderPanelVisibility = () => { + renderMultiSelectionControls(); + panelElement.style.display = isPanelOpen ? 'flex' : 'none'; + }; + + const renderLauncherLogo = () => { + launcherIconElement.src = + (toolkitConfig.iconUrl || '').trim() || DEFAULT_TOOLKIT_ICON_DATA_URL; + }; + + const updateToolkitStyle = () => { + toolkitRootElement.style.zIndex = String(toolkitConfig.zIndex); + setPosition( + toolkitRootElement, + toolkitConfig.position, + toolkitConfig.offset.x || 0, + toolkitConfig.offset.y || 0, + ); + + panelElement.style.borderColor = `${toolkitConfig.accentColor}33`; + launcherButtonElement.style.setProperty( + '--agentic-react-launcher-shadow', + `0 18px 38px rgba(15, 23, 42, 0.34), 0 8px 18px ${toolkitConfig.accentColor}3d, 0 0 0 1px rgba(255, 255, 255, 0.2)`, + ); + launcherButtonElement.style.setProperty( + '--agentic-react-launcher-hover-shadow', + `0 24px 48px rgba(15, 23, 42, 0.42), 0 12px 28px ${toolkitConfig.accentColor}66, 0 0 0 4px ${toolkitConfig.accentColor}24, 0 0 0 1px rgba(255, 255, 255, 0.42)`, + ); + launcherButtonElement.style.setProperty( + '--agentic-react-launcher-focus-shadow', + `0 20px 42px rgba(15, 23, 42, 0.38), 0 10px 24px ${toolkitConfig.accentColor}5c, 0 0 0 4px ${toolkitConfig.accentColor}33, 0 0 0 1px rgba(255, 255, 255, 0.5)`, + ); + + const buttons = [selectButtonElement, multiselectButtonElement]; + for (const buttonElement of buttons) { + if (!buttonElement.disabled) { + buttonElement.style.background = toolkitConfig.accentColor; + } + buttonElement.style.boxShadow = '0 8px 24px rgba(0,0,0,0.2)'; + } + doneButtonElement.style.background = '#dc2626'; + doneButtonElement.style.color = '#ffffff'; + doneButtonElement.style.boxShadow = '0 8px 24px rgba(0,0,0,0.2)'; + clearAllButtonElement.style.boxShadow = '0 8px 24px rgba(0,0,0,0.2)'; + + hoverElement.style.zIndex = String(Math.max(0, toolkitConfig.zIndex - 2)); + hoverElement.style.setProperty( + '--agentic-react-selection-accent', + toolkitConfig.accentColor, + ); + selectedElement.style.zIndex = String( + Math.max(0, toolkitConfig.zIndex - 1), + ); + selectedElement.style.setProperty( + '--agentic-react-selection-accent', + toolkitConfig.accentColor, + ); + for (const { overlayElement } of multiSelectedOverlays) { + overlayElement.style.zIndex = String( + Math.max(0, toolkitConfig.zIndex - 1), + ); + overlayElement.style.setProperty( + '--agentic-react-selection-accent', + toolkitConfig.accentColor, + ); + } + for (const dimElement of dimElements) { + dimElement.style.zIndex = String(Math.max(0, toolkitConfig.zIndex - 3)); + } + applyTuningModalStaticStyles(); + renderLauncherLogo(); + renderPanelVisibility(); + }; + + const applyElementRect = (overlayElement: HTMLElement, rect: DOMRect) => { + overlayElement.style.left = `${rect.left}px`; + overlayElement.style.top = `${rect.top}px`; + overlayElement.style.width = `${rect.width}px`; + overlayElement.style.height = `${rect.height}px`; + }; + + const updateOverlayLabelForElement = ( + labelElement: HTMLElement, + element: Element, + rect: DOMRect, + selectionContext?: SelectionContext | null, + ) => { + const componentNameOverride = selectionContext + ? null + : getSelectionElementComponentName(element); + labelElement.textContent = buildSelectedElementLabel( + element, + selectionContext, + componentNameOverride, + ); + labelElement.dataset.align = 'left'; + labelElement.style.maxWidth = `${Math.max( + 80, + Math.min(420, window.innerWidth - 24, window.innerWidth - rect.left - 12), + )}px`; + }; + + const closeTuningModal = () => { + for (const cleanup of tuningExtensionCleanups.splice(0)) { + try { + cleanup(); + } catch (_error) { + // Extension cleanup should not prevent the built-in modal from closing. + } + } + activeTuningSession = null; + tuningModalElement.style.display = 'none'; + tuningBodyElement.replaceChildren(); + tuningSurfaceElement.replaceChildren(tuningPanelElement); + }; + + const positionTuningPanel = () => { + if (!activeTuningSession?.anchorElement.isConnected) { + closeTuningModal(); + return; + } + + const anchorRect = + activeTuningSession.anchorElement.getBoundingClientRect(); + const surfaceRect = tuningSurfaceElement.getBoundingClientRect(); + const panelRect = tuningPanelElement.getBoundingClientRect(); + const surfaceWidth = surfaceRect.width || panelRect.width; + const surfaceHeight = surfaceRect.height || panelRect.height; + const viewportWidth = window.innerWidth; + const viewportHeight = window.innerHeight; + const maxLeft = Math.max( + TUNING_PANEL_MARGIN, + viewportWidth - TUNING_PANEL_MARGIN - surfaceWidth, + ); + const maxTop = Math.max( + TUNING_PANEL_MARGIN, + viewportHeight - TUNING_PANEL_MARGIN - surfaceHeight, + ); + const rightLeft = anchorRect.right + TUNING_PANEL_GAP; + const leftLeft = anchorRect.left - TUNING_PANEL_GAP - surfaceWidth; + const canFitRight = + rightLeft + surfaceWidth <= viewportWidth - TUNING_PANEL_MARGIN; + const canFitLeft = leftLeft >= TUNING_PANEL_MARGIN; + const placeOnRight = + canFitRight || + (!canFitLeft && viewportWidth - anchorRect.right >= anchorRect.left); + const unclampedLeft = placeOnRight ? rightLeft : leftLeft; + const left = clampNumber(unclampedLeft, TUNING_PANEL_MARGIN, maxLeft); + const anchorCenterY = anchorRect.top + anchorRect.height / 2; + const top = clampNumber( + anchorCenterY - surfaceHeight / 2, + TUNING_PANEL_MARGIN, + maxTop, + ); + const arrowTop = clampNumber( + anchorCenterY - top - 5, + 14, + Math.max(14, surfaceHeight - 18), + ); + + tuningSurfaceElement.dataset.placement = placeOnRight ? 'right' : 'left'; + tuningSurfaceElement.style.left = `${Math.round(left)}px`; + tuningSurfaceElement.style.top = `${Math.round(top)}px`; + tuningSurfaceElement.style.right = ''; + tuningSurfaceElement.style.bottom = ''; + tuningArrowElement.style.top = `${Math.round(top + arrowTop)}px`; + tuningArrowElement.style.left = placeOnRight + ? `${Math.round(left - 6)}px` + : `${Math.round(left + surfaceWidth - 4)}px`; + tuningArrowElement.style.right = ''; + tuningArrowElement.style.borderLeft = placeOnRight + ? '1px solid rgba(15, 23, 42, 0.14)' + : 'none'; + tuningArrowElement.style.borderBottom = placeOnRight + ? '1px solid rgba(15, 23, 42, 0.14)' + : 'none'; + tuningArrowElement.style.borderRight = placeOnRight + ? 'none' + : '1px solid rgba(15, 23, 42, 0.14)'; + tuningArrowElement.style.borderTop = placeOnRight + ? 'none' + : '1px solid rgba(15, 23, 42, 0.14)'; + }; + + const addTuningPrompt = (prompt: string) => { + if (!activeTuningSession) return; + + const nextTuningPrompts = [ + ...(activeTuningSession.selectionContext.tuningPrompts || []), + prompt, + ]; + const nextSelectionContext = { + ...activeTuningSession.selectionContext, + tuningPrompts: nextTuningPrompts, + }; + + activeTuningSession.selectionContext = nextSelectionContext; + activeTuningSession.setSelectionContext(nextSelectionContext); + updateStatus(`Added tuning prompt: ${prompt}`); + }; + + const buildTuningModalContext = ( + session: TuningSession, + computedStyle: CSSStyleDeclaration, + targetLabel: string, + targetTagName: string, + ): TuningModalContext => ({ + element: session.element, + tagName: targetTagName, + targetLabel, + computedStyle, + selectionContext: session.selectionContext, + }); + + const buildTuningModalActions = (): TuningModalActions => ({ + addPrompt: addTuningPrompt, + close: closeTuningModal, + requestReposition: positionTuningPanel, + }); + + const pushTuningExtensionCleanup = (cleanup: TuningModalExtensionCleanup) => { + if (typeof cleanup === 'function') { + tuningExtensionCleanups.push(cleanup); + } + }; + + const renderTuningExtensionSlot = ( + slot: 'beforeFields' | 'afterFields' | 'footer', + container: HTMLElement, + context: TuningModalContext, + actions: TuningModalActions, + ) => { + for (const extension of tuningModalExtensions) { + const renderSlot = extension[slot]; + if (!renderSlot) continue; + + pushTuningExtensionCleanup( + renderSlot({ + container, + context, + actions, + }), + ); + } + }; + + const renderTuningExtensionWrappers = ( + context: TuningModalContext, + actions: TuningModalActions, + ) => { + for (const extension of tuningModalExtensions) { + pushTuningExtensionCleanup( + extension.wrapModal?.({ + surfaceElement: tuningSurfaceElement, + panelElement: tuningPanelElement, + context, + actions, + }), + ); + } + }; + + const registerTuningModalExtension = (extension: TuningModalExtension) => { + tuningModalExtensions = [ + ...tuningModalExtensions.filter( + (registeredExtension) => registeredExtension.id !== extension.id, + ), + extension, + ]; + + return () => { + tuningModalExtensions = tuningModalExtensions.filter( + (registeredExtension) => registeredExtension.id !== extension.id, + ); + }; + }; + + const createTuningSectionTitle = (titleText: string) => { + const sectionTitleElement = document.createElement('div'); + sectionTitleElement.textContent = titleText; + sectionTitleElement.style.marginTop = '4px'; + sectionTitleElement.style.paddingTop = '8px'; + sectionTitleElement.style.borderTop = '1px solid rgba(15, 23, 42, 0.08)'; + sectionTitleElement.style.fontSize = '11px'; + sectionTitleElement.style.fontWeight = '800'; + sectionTitleElement.style.letterSpacing = '0'; + sectionTitleElement.style.color = + 'var(--agentic-react-tuning-section-color, #334155)'; + applyTuningSlotStyles(sectionTitleElement, 'sectionTitle'); + return sectionTitleElement; + }; + + const styleTuningControl = ( + controlElement: HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement, + ) => { + controlElement.style.width = '100%'; + controlElement.style.minWidth = '0'; + controlElement.style.boxSizing = 'border-box'; + controlElement.style.border = + 'var(--agentic-react-tuning-control-border, 1px solid rgba(15, 23, 42, 0.14))'; + controlElement.style.borderRadius = + 'var(--agentic-react-tuning-control-radius, 8px)'; + controlElement.style.background = + 'var(--agentic-react-tuning-control-background, #ffffff)'; + controlElement.style.color = + 'var(--agentic-react-tuning-control-color, #111827)'; + controlElement.style.fontSize = '12px'; + controlElement.style.fontFamily = 'ui-sans-serif, system-ui, sans-serif'; + applyTuningSlotStyles(controlElement, 'control'); + }; + + const createTuningRow = ( + labelText: string, + controlElement: HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement, + suffixText = '', + ) => { + const rowElement = document.createElement('label'); + const labelElement = document.createElement('span'); + const controlWrapElement = document.createElement('span'); + const suffixElement = document.createElement('span'); + + rowElement.style.display = 'grid'; + rowElement.style.gridTemplateColumns = '128px minmax(0, 1fr)'; + rowElement.style.alignItems = 'center'; + rowElement.style.gap = '12px'; + rowElement.style.fontSize = '12px'; + rowElement.style.fontWeight = '600'; + + labelElement.textContent = labelText; + labelElement.style.color = + 'var(--agentic-react-tuning-label-color, #111827)'; + labelElement.style.lineHeight = '1.2'; + controlElement.setAttribute('aria-label', labelText); + styleTuningControl(controlElement); + + const createStepperButton = (label: string, direction: -1 | 1) => { + const buttonElement = document.createElement('button'); + buttonElement.type = 'button'; + buttonElement.textContent = direction > 0 ? '+' : '-'; + buttonElement.setAttribute('aria-label', `${label} ${labelText}`); + buttonElement.style.width = '26px'; + buttonElement.style.height = '32px'; + buttonElement.style.border = + 'var(--agentic-react-tuning-control-border, 1px solid rgba(15, 23, 42, 0.14))'; + buttonElement.style.borderRadius = + 'var(--agentic-react-tuning-control-radius, 8px)'; + buttonElement.style.background = + 'var(--agentic-react-tuning-secondary-button-background, #f8fafc)'; + buttonElement.style.color = + 'var(--agentic-react-tuning-secondary-button-color, #111827)'; + buttonElement.style.cursor = 'pointer'; + buttonElement.style.fontSize = '14px'; + buttonElement.style.fontWeight = '800'; + buttonElement.style.lineHeight = '1'; + buttonElement.addEventListener('click', (event) => { + event.preventDefault(); + if (!(controlElement instanceof HTMLInputElement)) return; + + try { + if (direction > 0) { + controlElement.stepUp(); + } else { + controlElement.stepDown(); + } + } catch (_error) { + const step = Number(controlElement.step) || 1; + const nextValue = + (Number(controlElement.value) || 0) + step * direction; + controlElement.value = String(nextValue); + } + + controlElement.dispatchEvent( + new Event('change', { bubbles: true, cancelable: true }), + ); + }); + applyTuningSlotStyles(buttonElement, 'stepperButton'); + return buttonElement; + }; + + if (controlElement instanceof HTMLInputElement) { + const isColorInput = controlElement.type === 'color'; + controlElement.style.height = isColorInput ? '30px' : '32px'; + controlElement.style.width = isColorInput ? '34px' : '100%'; + controlElement.style.minWidth = isColorInput ? '34px' : '0'; + controlElement.style.padding = isColorInput ? '3px 4px' : '6px 8px'; + applyTuningSlotStyles( + controlElement, + isColorInput ? 'colorInput' : 'numberInput', + ); + } else if (controlElement instanceof HTMLSelectElement) { + controlElement.style.height = '32px'; + controlElement.style.padding = '6px 8px'; + applyTuningSlotStyles(controlElement, 'select'); + } else { + controlElement.style.minHeight = '54px'; + controlElement.style.padding = '8px'; + controlElement.style.resize = 'vertical'; + applyTuningSlotStyles(controlElement, 'textarea'); + } + + const isNumberInput = + controlElement instanceof HTMLInputElement && + controlElement.type === 'number'; + const isColorInput = + controlElement instanceof HTMLInputElement && + controlElement.type === 'color'; + controlWrapElement.style.display = 'grid'; + controlWrapElement.style.gridTemplateColumns = isColorInput + ? suffixText + ? '34px minmax(0, 1fr)' + : '34px' + : isNumberInput + ? suffixText + ? 'minmax(0, 1fr) auto auto auto' + : 'minmax(0, 1fr) auto auto' + : suffixText + ? 'minmax(0, 1fr) auto' + : '1fr'; + controlWrapElement.style.alignItems = 'center'; + controlWrapElement.style.gap = '6px'; + controlWrapElement.appendChild(controlElement); + + if (isNumberInput) { + controlWrapElement.appendChild(createStepperButton('Decrement', -1)); + controlWrapElement.appendChild(createStepperButton('Increment', 1)); + } + + if (suffixText) { + suffixElement.textContent = suffixText; + suffixElement.style.fontSize = '12px'; + suffixElement.style.color = '#475569'; + if (isColorInput) { + suffixElement.setAttribute('data-agentic-react-color-value', 'true'); + suffixElement.style.minWidth = '0'; + suffixElement.style.overflow = 'hidden'; + suffixElement.style.textOverflow = 'ellipsis'; + suffixElement.style.whiteSpace = 'nowrap'; + suffixElement.style.fontFamily = + 'ui-monospace, SFMono-Regular, monospace'; + controlElement.addEventListener('input', () => { + suffixElement.textContent = hexToRgbaString(controlElement.value); + }); + controlElement.addEventListener('change', () => { + suffixElement.textContent = hexToRgbaString(controlElement.value); + }); + } + applyTuningSlotStyles(suffixElement, 'suffix'); + controlWrapElement.appendChild(suffixElement); + } + + rowElement.appendChild(labelElement); + rowElement.appendChild(controlWrapElement); + applyTuningSlotStyles(rowElement, 'row'); + applyTuningSlotStyles(labelElement, 'label'); + applyTuningSlotStyles(controlWrapElement, 'controlWrap'); + return rowElement; + }; + + const createTuningInput = ( + name: string, + value: string, + inputType = 'text', + ) => { + const inputElement = document.createElement('input'); + inputElement.type = inputType; + inputElement.name = name; + inputElement.value = value; + return inputElement; + }; + + const createTuningNumberInput = ( + name: string, + value: number, + min: number, + max: number, + step: number, + ) => { + const inputElement = createTuningInput(name, String(value), 'number'); + inputElement.min = String(min); + inputElement.max = String(max); + inputElement.step = String(step); + return inputElement; + }; + + const createTuningSelect = ( + name: string, + values: string[], + selectedValue: string, + ) => { + const selectElement = document.createElement('select'); + selectElement.name = name; + + for (const value of values) { + const optionElement = document.createElement('option'); + optionElement.value = value; + optionElement.textContent = value; + selectElement.appendChild(optionElement); + } + + selectElement.value = values.includes(selectedValue) + ? selectedValue + : values[0] || ''; + return selectElement; + }; + + const openTuningModal = (session: TuningSession) => { + closeTuningModal(); + activeTuningSession = session; + tuningBodyElement.replaceChildren(); + + const computedStyle = window.getComputedStyle(session.element); + const targetLabel = buildTuningTargetLabel(session.selectionContext); + const targetTagName = session.element.tagName.toLowerCase(); + const supportsTextTuning = hasTuneableText(session.element); + const supportsElementTuning = session.element instanceof HTMLElement; + const supportsLayoutTuning = + supportsElementTuning && + hasTuneableLayout(session.element, computedStyle); + const targetRect = session.element.getBoundingClientRect(); + const tuningContext = buildTuningModalContext( + session, + computedStyle, + targetLabel, + targetTagName, + ); + const tuningActions = buildTuningModalActions(); + + tuningTitleElement.textContent = `Tune ${targetLabel}`; + + const targetTagElement = document.createElement('div'); + targetTagElement.setAttribute( + 'data-agentic-react-tuning-target-tag', + 'true', + ); + targetTagElement.textContent = targetTagName; + targetTagElement.style.display = 'inline-flex'; + targetTagElement.style.alignItems = 'center'; + targetTagElement.style.width = 'fit-content'; + targetTagElement.style.maxWidth = '100%'; + targetTagElement.style.marginBottom = '2px'; + targetTagElement.style.padding = '4px 8px'; + targetTagElement.style.border = '1px solid rgba(15, 23, 42, 0.1)'; + targetTagElement.style.borderRadius = '999px'; + targetTagElement.style.background = '#f8fafc'; + targetTagElement.style.color = '#0f172a'; + targetTagElement.style.fontFamily = + 'ui-monospace, SFMono-Regular, monospace'; + targetTagElement.style.fontSize = '12px'; + targetTagElement.style.fontWeight = '700'; + applyTuningSlotStyles(targetTagElement, 'targetTag'); + tuningBodyElement.appendChild(targetTagElement); + + const customPromptFormElement = document.createElement('form'); + const customPromptInputElement = document.createElement('textarea'); + const customPromptButtonElement = document.createElement('button'); + customPromptFormElement.style.display = 'grid'; + customPromptFormElement.style.gridTemplateColumns = 'minmax(0, 1fr) auto'; + customPromptFormElement.style.gap = '8px'; + customPromptFormElement.style.alignItems = 'end'; + customPromptInputElement.name = 'custom-tuning-prompt'; + customPromptInputElement.placeholder = 'Describe these changes...'; + customPromptInputElement.setAttribute( + 'aria-label', + 'Describe custom tuning changes', + ); + styleTuningControl(customPromptInputElement); + customPromptInputElement.style.minHeight = '42px'; + customPromptInputElement.style.padding = '8px'; + customPromptInputElement.style.resize = 'vertical'; + applyTuningSlotStyles( + customPromptInputElement, + 'textarea', + 'customPromptInput', + ); + customPromptButtonElement.type = 'submit'; + customPromptButtonElement.textContent = 'Add'; + customPromptButtonElement.style.height = '32px'; + customPromptButtonElement.style.padding = '0 12px'; + customPromptButtonElement.style.border = + 'var(--agentic-react-tuning-primary-button-border, 1px solid rgba(15, 23, 42, 0.14))'; + customPromptButtonElement.style.borderRadius = + 'var(--agentic-react-tuning-control-radius, 8px)'; + customPromptButtonElement.style.background = + 'var(--agentic-react-tuning-primary-button-background, #111827)'; + customPromptButtonElement.style.color = + 'var(--agentic-react-tuning-primary-button-color, #ffffff)'; + customPromptButtonElement.style.cursor = 'pointer'; + customPromptButtonElement.style.fontSize = '12px'; + customPromptButtonElement.style.fontWeight = '700'; + applyTuningSlotStyles(customPromptButtonElement, 'customPromptButton'); + applyTuningSlotStyles(customPromptFormElement, 'customPromptForm'); + customPromptFormElement.addEventListener('submit', (event) => { + event.preventDefault(); + const customPrompt = customPromptInputElement.value.trim(); + if (!customPrompt) return; + addTuningPrompt(customPrompt); + customPromptInputElement.value = ''; + }); + customPromptFormElement.appendChild(customPromptInputElement); + customPromptFormElement.appendChild(customPromptButtonElement); + tuningBodyElement.appendChild(customPromptFormElement); + + const beforeFieldsElement = document.createElement('div'); + beforeFieldsElement.setAttribute( + 'data-agentic-react-tuning-slot', + 'before-fields', + ); + renderTuningExtensionSlot( + 'beforeFields', + beforeFieldsElement, + tuningContext, + tuningActions, + ); + if (beforeFieldsElement.hasChildNodes()) { + tuningBodyElement.appendChild(beforeFieldsElement); + } + + tuningBodyElement.appendChild(createTuningSectionTitle('Visual')); + + if (supportsTextTuning) { + const textColorInputElement = createTuningInput( + 'text-color', + rgbStringToHex(computedStyle.color), + 'color', + ); + textColorInputElement.addEventListener('change', () => { + addTuningPrompt( + `Change ${targetLabel} text color to ${hexToRgbString( + textColorInputElement.value, + )}.`, + ); + }); + tuningBodyElement.appendChild( + createTuningRow( + 'Text color', + textColorInputElement, + cssColorToRgbaString(computedStyle.color, { + r: 17, + g: 24, + b: 39, + a: 1, + }), + ), + ); + } + + if (supportsElementTuning) { + const backgroundColorInputElement = createTuningInput( + 'background-color', + rgbStringToHex(computedStyle.backgroundColor), + 'color', + ); + backgroundColorInputElement.addEventListener('change', () => { + addTuningPrompt( + `Change ${targetLabel} background color to ${hexToRgbString( + backgroundColorInputElement.value, + )}.`, + ); + }); + tuningBodyElement.appendChild( + createTuningRow( + 'Background', + backgroundColorInputElement, + cssColorToRgbaString(computedStyle.backgroundColor), + ), + ); + + const opacityInputElement = createTuningNumberInput( + 'opacity', + Number(normalizeCssNumber(computedStyle.opacity, 1).toFixed(2)), + 0, + 1, + 0.05, + ); + opacityInputElement.addEventListener('change', () => { + const opacity = Number( + clampNumber(Number(opacityInputElement.value) || 1, 0, 1).toFixed(2), + ); + opacityInputElement.value = String(opacity); + addTuningPrompt(`Change ${targetLabel} opacity to ${opacity}.`); + }); + tuningBodyElement.appendChild( + createTuningRow('Opacity', opacityInputElement), + ); + } + + if (supportsTextTuning) { + tuningBodyElement.appendChild(createTuningSectionTitle('Typography')); + const currentFontFamily = + computedStyle.fontFamily.split(',')[0]?.replace(/["']/g, '').trim() || + 'inherit'; + const fontFamilySelectElement = createTuningSelect( + 'font-family', + Array.from( + new Set([currentFontFamily, ...TUNING_FONT_FAMILY_OPTIONS]), + ).filter(Boolean), + currentFontFamily, + ); + fontFamilySelectElement.addEventListener('change', () => { + addTuningPrompt( + `Change ${targetLabel} font family to ${fontFamilySelectElement.value}.`, + ); + }); + tuningBodyElement.appendChild( + createTuningRow('Font', fontFamilySelectElement), + ); + + const fontSizeInputElement = document.createElement('input'); + fontSizeInputElement.type = 'number'; + fontSizeInputElement.name = 'font-size'; + fontSizeInputElement.min = '8'; + fontSizeInputElement.max = '72'; + fontSizeInputElement.step = '1'; + fontSizeInputElement.value = String( + Math.round(Number.parseFloat(computedStyle.fontSize) || 16), + ); + fontSizeInputElement.addEventListener('change', () => { + const fontSize = clampNumber( + Number(fontSizeInputElement.value) || 16, + 8, + 72, + ); + fontSizeInputElement.value = String(fontSize); + addTuningPrompt(`Change ${targetLabel} font size to ${fontSize}px.`); + }); + tuningBodyElement.appendChild( + createTuningRow('Font size', fontSizeInputElement, 'px'), + ); + + const fontWeight = computedStyle.fontWeight || '400'; + const fontWeightSelectElement = createTuningSelect( + 'font-weight', + Array.from(new Set([fontWeight, ...TUNING_FONT_WEIGHT_OPTIONS])), + fontWeight, + ); + fontWeightSelectElement.addEventListener('change', () => { + addTuningPrompt( + `Change ${targetLabel} font weight to ${fontWeightSelectElement.value}.`, + ); + }); + tuningBodyElement.appendChild( + createTuningRow('Font weight', fontWeightSelectElement), + ); + } + + if (supportsElementTuning) { + tuningBodyElement.appendChild(createTuningSectionTitle('Box')); + const widthInputElement = createTuningNumberInput( + 'width', + Math.round( + targetRect.width || normalizeCssNumber(computedStyle.width, 0), + ), + 0, + 2000, + 1, + ); + widthInputElement.addEventListener('change', () => { + const width = clampNumber( + Number(widthInputElement.value) || 0, + 0, + 2000, + ); + widthInputElement.value = String(width); + addTuningPrompt(`Change ${targetLabel} width to ${width}px.`); + }); + tuningBodyElement.appendChild( + createTuningRow('Width', widthInputElement, 'px'), + ); + + const heightInputElement = createTuningNumberInput( + 'height', + Math.round( + targetRect.height || normalizeCssNumber(computedStyle.height, 0), + ), + 0, + 2000, + 1, + ); + heightInputElement.addEventListener('change', () => { + const height = clampNumber( + Number(heightInputElement.value) || 0, + 0, + 2000, + ); + heightInputElement.value = String(height); + addTuningPrompt(`Change ${targetLabel} height to ${height}px.`); + }); + tuningBodyElement.appendChild( + createTuningRow('Height', heightInputElement, 'px'), + ); + + const paddingInputElement = createTuningNumberInput( + 'padding', + Math.round(normalizeCssNumber(computedStyle.paddingTop, 0)), + 0, + 240, + 1, + ); + paddingInputElement.addEventListener('change', () => { + const padding = clampNumber( + Number(paddingInputElement.value) || 0, + 0, + 240, + ); + paddingInputElement.value = String(padding); + addTuningPrompt(`Change ${targetLabel} padding to ${padding}px.`); + }); + tuningBodyElement.appendChild( + createTuningRow('Padding', paddingInputElement, 'px'), + ); + + const marginInputElement = createTuningNumberInput( + 'margin', + Math.round(normalizeCssNumber(computedStyle.marginTop, 0)), + -240, + 240, + 1, + ); + marginInputElement.addEventListener('change', () => { + const margin = clampNumber( + Number(marginInputElement.value) || 0, + -240, + 240, + ); + marginInputElement.value = String(margin); + addTuningPrompt(`Change ${targetLabel} margin to ${margin}px.`); + }); + tuningBodyElement.appendChild( + createTuningRow('Margin', marginInputElement, 'px'), + ); + } + + if (supportsLayoutTuning) { + tuningBodyElement.appendChild(createTuningSectionTitle('Layout')); + const displaySelectElement = createTuningSelect( + 'display', + Array.from(new Set([computedStyle.display, ...TUNING_DISPLAY_OPTIONS])), + computedStyle.display, + ); + displaySelectElement.addEventListener('change', () => { + addTuningPrompt( + `Change ${targetLabel} display layout to ${displaySelectElement.value}.`, + ); + }); + tuningBodyElement.appendChild( + createTuningRow('Display', displaySelectElement), + ); + + const directionSelectElement = createTuningSelect( + 'layout-direction', + TUNING_FLEX_DIRECTION_OPTIONS, + computedStyle.flexDirection, + ); + directionSelectElement.addEventListener('change', () => { + addTuningPrompt( + `Change ${targetLabel} layout direction to ${directionSelectElement.value}.`, + ); + }); + tuningBodyElement.appendChild( + createTuningRow('Layout direction', directionSelectElement), + ); + + const distributionSelectElement = createTuningSelect( + 'distribution', + Array.from( + new Set([computedStyle.justifyContent, ...TUNING_JUSTIFY_OPTIONS]), + ), + computedStyle.justifyContent, + ); + distributionSelectElement.addEventListener('change', () => { + addTuningPrompt( + `Change ${targetLabel} distribution to ${distributionSelectElement.value}.`, + ); + }); + tuningBodyElement.appendChild( + createTuningRow('Distribution', distributionSelectElement), + ); + + const alignmentSelectElement = createTuningSelect( + 'alignment', + Array.from( + new Set([computedStyle.alignItems, ...TUNING_ALIGN_OPTIONS]), + ), + computedStyle.alignItems, + ); + alignmentSelectElement.addEventListener('change', () => { + addTuningPrompt( + `Change ${targetLabel} alignment to ${alignmentSelectElement.value}.`, + ); + }); + tuningBodyElement.appendChild( + createTuningRow('Alignment', alignmentSelectElement), + ); + + const spacingInputElement = createTuningNumberInput( + 'spacing', + Math.round(normalizeCssNumber(computedStyle.gap, 0)), + 0, + 240, + 1, + ); + spacingInputElement.addEventListener('change', () => { + const spacing = clampNumber( + Number(spacingInputElement.value) || 0, + 0, + 240, + ); + spacingInputElement.value = String(spacing); + addTuningPrompt(`Change ${targetLabel} spacing to ${spacing}px.`); + }); + tuningBodyElement.appendChild( + createTuningRow('Spacing', spacingInputElement, 'px'), + ); + } + + const afterFieldsElement = document.createElement('div'); + afterFieldsElement.setAttribute( + 'data-agentic-react-tuning-slot', + 'after-fields', + ); + renderTuningExtensionSlot( + 'afterFields', + afterFieldsElement, + tuningContext, + tuningActions, + ); + if (afterFieldsElement.hasChildNodes()) { + tuningBodyElement.appendChild(afterFieldsElement); + } + + const footerElement = document.createElement('div'); + footerElement.setAttribute('data-agentic-react-tuning-slot', 'footer'); + renderTuningExtensionSlot( + 'footer', + footerElement, + tuningContext, + tuningActions, + ); + if (footerElement.hasChildNodes()) { + tuningBodyElement.appendChild(footerElement); + } + + if (!supportsTextTuning && !supportsElementTuning) { + const emptyElement = document.createElement('div'); + emptyElement.textContent = 'No tuning options available.'; + emptyElement.style.fontSize = '12px'; + emptyElement.style.color = '#475569'; + tuningBodyElement.appendChild(emptyElement); + } + + renderTuningExtensionWrappers(tuningContext, tuningActions); + tuningModalElement.style.zIndex = String(toolkitConfig.zIndex + 1); + tuningModalElement.style.display = 'block'; + positionTuningPanel(); + }; + + const hideDimOverlay = () => { + dimTargetElement = null; + if (dimHideTimeout !== null) { + window.clearTimeout(dimHideTimeout); + dimHideTimeout = null; + } + + for (const dimElement of dimElements) { + dimElement.style.display = 'none'; + } + }; + + const showDimForElement = (element: Element) => { + const rect = element.getBoundingClientRect(); + const viewportWidth = window.innerWidth; + const viewportHeight = window.innerHeight; + const topHeight = Math.max(0, rect.top); + const bottomTop = Math.min(viewportHeight, Math.max(0, rect.bottom)); + const leftWidth = Math.max(0, rect.left); + const rightLeft = Math.min(viewportWidth, Math.max(0, rect.right)); + const middleTop = Math.max(0, rect.top); + const middleHeight = Math.max( + 0, + Math.min(viewportHeight, rect.bottom) - middleTop, + ); + + dimTargetElement = element; + + const [topElement, rightElement, bottomElement, leftElement] = dimElements; + topElement.style.display = 'block'; + topElement.style.left = '0px'; + topElement.style.top = '0px'; + topElement.style.width = `${viewportWidth}px`; + topElement.style.height = `${topHeight}px`; + + rightElement.style.display = 'block'; + rightElement.style.left = `${rightLeft}px`; + rightElement.style.top = `${middleTop}px`; + rightElement.style.width = `${Math.max(0, viewportWidth - rightLeft)}px`; + rightElement.style.height = `${middleHeight}px`; + + bottomElement.style.display = 'block'; + bottomElement.style.left = '0px'; + bottomElement.style.top = `${bottomTop}px`; + bottomElement.style.width = `${viewportWidth}px`; + bottomElement.style.height = `${Math.max(0, viewportHeight - bottomTop)}px`; + + leftElement.style.display = 'block'; + leftElement.style.left = '0px'; + leftElement.style.top = `${middleTop}px`; + leftElement.style.width = `${leftWidth}px`; + leftElement.style.height = `${middleHeight}px`; + }; + + const scheduleDimHide = () => { + if (dimHideTimeout !== null) { + window.clearTimeout(dimHideTimeout); + } + + dimHideTimeout = window.setTimeout(() => { + hideDimOverlay(); + }, SELECTION_CONFIRMATION_MS); + }; + + const hideHoverOverlay = () => { + hoverElement.style.display = 'none'; + hoverLabelElement.textContent = ''; + hoverLabelTargetElement = null; + hoverLabelRequestId += 1; + }; + + const showHoverForElement = (element: Element | null) => { + if (!element || toolkitRootElement.contains(element)) { + hideHoverOverlay(); + if (isSelectionMode) { + hideDimOverlay(); + } + return; + } + + const rect = element.getBoundingClientRect(); + const computedStyle = window.getComputedStyle(element); + hoverElement.style.display = 'block'; + hoverElement.style.borderRadius = computedStyle.borderRadius || '6px'; + updateOverlayLabelForElement(hoverLabelElement, element, rect); + if (hoverLabelTargetElement !== element) { + hoverLabelTargetElement = element; + hoverLabelRequestId += 1; + const requestId = hoverLabelRequestId; + void buildSelectionContextForElement(element) + .then((selectionContext) => { + if ( + requestId !== hoverLabelRequestId || + hoverLabelTargetElement !== element || + hoverElement.style.display === 'none' + ) { + return null; + } + + updateOverlayLabelForElement( + hoverLabelElement, + element, + element.getBoundingClientRect(), + selectionContext, + ); + + return enrichSelectionContextSourceLocation(selectionContext); + }) + .then((selectionContext) => { + if (!selectionContext) { + return; + } + + if ( + requestId !== hoverLabelRequestId || + hoverLabelTargetElement !== element || + hoverElement.style.display === 'none' + ) { + return; + } + + updateOverlayLabelForElement( + hoverLabelElement, + element, + element.getBoundingClientRect(), + selectionContext, + ); + }) + .catch(() => { + // Best effort only; the immediate DOM label remains visible. + }); + } + applyHoverContrastForElement(hoverElement, element); + applyElementRect(hoverElement, rect); + showDimForElement(element); + }; + + const hideSelectedOverlay = () => { + selectedTargetElement = null; + selectedElement.style.display = 'none'; + selectedLabelElement.textContent = ''; + selectedElement.classList.remove('agentic-react-selection-pulse'); + if (selectedPulseTimeout !== null) { + window.clearTimeout(selectedPulseTimeout); + selectedPulseTimeout = null; + } + }; + + const createMultiSelectedOverlay = ( + element: Element, + selectionContext: SelectionContext, + ) => { + const overlayElement = document.createElement('div'); + const labelElement = document.createElement('div'); + const actions = createOverlayActions(); + + overlayElement.setAttribute('data-agentic-react-selected', 'true'); + overlayElement.setAttribute('data-agentic-react-multi-selected', 'true'); + overlayElement.style.position = 'fixed'; + overlayElement.style.pointerEvents = 'none'; + overlayElement.style.display = 'none'; + overlayElement.style.background = 'transparent'; + overlayElement.style.zIndex = String(Math.max(0, toolkitConfig.zIndex - 1)); + overlayElement.style.setProperty( + '--agentic-react-selection-accent', + toolkitConfig.accentColor, + ); + + labelElement.setAttribute('data-agentic-react-selected-label', 'true'); + labelElement.style.position = 'absolute'; + labelElement.style.pointerEvents = 'none'; + labelElement.style.maxWidth = 'min(260px, calc(100vw - 24px))'; + labelElement.style.overflow = 'hidden'; + labelElement.style.textOverflow = 'ellipsis'; + labelElement.style.whiteSpace = 'nowrap'; + + overlayElement.appendChild(labelElement); + overlayElement.appendChild(actions.actionsElement); + document.body.appendChild(overlayElement); + + const overlay = { + element, + overlayElement, + labelElement, + actions, + selectionContext, + pulseTimeout: null, + }; + + actions.tuneButtonElement.addEventListener('click', (event) => { + event.preventDefault(); + event.stopPropagation(); + openTuningModal({ + anchorElement: actions.tuneButtonElement, + element, + selectionContext: overlay.selectionContext, + setSelectionContext: (nextSelectionContext) => { + multiSelectionContexts = multiSelectionContexts.map((context) => + context === overlay.selectionContext + ? nextSelectionContext + : context, + ); + overlay.selectionContext = nextSelectionContext; + }, + }); + }); + + actions.deleteButtonElement.addEventListener('click', (event) => { + event.preventDefault(); + event.stopPropagation(); + removeMultiSelectedOverlay(overlay); + updateStatus( + `${multiSelectionContexts.length} selected. Continue selecting or click Done.`, + ); + }); + + return overlay; + }; + + const updateMultiSelectedOverlay = ( + overlay: (typeof multiSelectedOverlays)[number], + ) => { + const rect = overlay.element.getBoundingClientRect(); + const computedStyle = window.getComputedStyle(overlay.element); + overlay.overlayElement.style.display = 'block'; + overlay.overlayElement.style.borderRadius = + computedStyle.borderRadius || '6px'; + updateOverlayLabelForElement( + overlay.labelElement, + overlay.element, + rect, + overlay.selectionContext, + ); + applyElementRect(overlay.overlayElement, rect); + }; + + const pulseMultiSelectedOverlay = ( + overlay: (typeof multiSelectedOverlays)[number], + ) => { + overlay.overlayElement.classList.remove('agentic-react-selection-pulse'); + void overlay.overlayElement.offsetWidth; + overlay.overlayElement.classList.add('agentic-react-selection-pulse'); + + if (overlay.pulseTimeout !== null) { + window.clearTimeout(overlay.pulseTimeout); + } + overlay.pulseTimeout = window.setTimeout(() => { + overlay.overlayElement.classList.remove('agentic-react-selection-pulse'); + overlay.pulseTimeout = null; + }, SELECTION_CONFIRMATION_MS); + }; + + const showMultiSelectedForElement = ( + element: Element, + selectionContext: SelectionContext, + shouldPulse = false, + ) => { + if (toolkitRootElement.contains(element)) { + return; + } + + let overlay = multiSelectedOverlays.find( + (candidateOverlay) => candidateOverlay.element === element, + ); + if (!overlay) { + overlay = createMultiSelectedOverlay(element, selectionContext); + multiSelectedOverlays.push(overlay); + } else { + overlay.selectionContext = selectionContext; + } + + updateMultiSelectedOverlay(overlay); + + if (shouldPulse) { + pulseMultiSelectedOverlay(overlay); + } + }; + + const clearMultiSelectedOverlays = () => { + for (const overlay of multiSelectedOverlays) { + if (overlay.pulseTimeout !== null) { + window.clearTimeout(overlay.pulseTimeout); + } + overlay.overlayElement.remove(); + } + multiSelectedOverlays = []; + }; + + const removeMultiSelectedOverlay = ( + overlay: (typeof multiSelectedOverlays)[number], + ) => { + if (overlay.pulseTimeout !== null) { + window.clearTimeout(overlay.pulseTimeout); + } + overlay.overlayElement.remove(); + multiSelectedOverlays = multiSelectedOverlays.filter( + (candidateOverlay) => candidateOverlay !== overlay, + ); + multiSelectionContexts = multiSelectionContexts.filter( + (selectionContext) => + selectionContext !== overlay.selectionContext && + selectionContext.selector !== overlay.selectionContext.selector, + ); + renderMultiSelectionControls(); + }; + + const clearMultiSelections = () => { + multiSelectionContexts = []; + clearMultiSelectedOverlays(); + closeTuningModal(); + renderMultiSelectionControls(); + }; + + const showSelectedForElement = ( + element: Element, + shouldPulse = false, + selectionContext: SelectionContext | null = lastSelectionContext, + ) => { + if (toolkitRootElement.contains(element)) { + hideSelectedOverlay(); + return; + } + + const rect = element.getBoundingClientRect(); + const computedStyle = window.getComputedStyle(element); + selectedTargetElement = element; + selectedElement.style.display = 'block'; + selectedElement.style.borderRadius = computedStyle.borderRadius || '6px'; + updateOverlayLabelForElement( + selectedLabelElement, + element, + rect, + selectionContext, + ); + applyElementRect(selectedElement, rect); + + if (!shouldPulse) { + return; + } + + selectedElement.classList.remove('agentic-react-selection-pulse'); + void selectedElement.offsetWidth; + selectedElement.classList.add('agentic-react-selection-pulse'); + + if (selectedPulseTimeout !== null) { + window.clearTimeout(selectedPulseTimeout); + } + selectedPulseTimeout = window.setTimeout(() => { + selectedElement.classList.remove('agentic-react-selection-pulse'); + selectedPulseTimeout = null; + }, SELECTION_CONFIRMATION_MS); + }; + + const updateSelectionOverlays = () => { + if (selectedTargetElement?.isConnected) { + showSelectedForElement(selectedTargetElement); + } else if (selectedTargetElement) { + hideSelectedOverlay(); + } + + multiSelectedOverlays = multiSelectedOverlays.filter((overlay) => { + if (!overlay.element.isConnected) { + if (overlay.pulseTimeout !== null) { + window.clearTimeout(overlay.pulseTimeout); + } + overlay.overlayElement.remove(); + return false; + } + + updateMultiSelectedOverlay(overlay); + return true; + }); + + if (dimTargetElement?.isConnected) { + showDimForElement(dimTargetElement); + } else if (dimTargetElement) { + hideDimOverlay(); + } + + if (activeTuningSession) { + positionTuningPanel(); + } + }; + + const isToolkitElement = (element: Element) => + toolkitRootElement.contains(element) || + tuningModalElement.contains(element) || + hoverElement.contains(element) || + selectedElement.contains(element) || + multiSelectedOverlays.some(({ overlayElement }) => + overlayElement.contains(element), + ) || + dimElements.some((dimElement) => dimElement.contains(element)); + + const isToolkitNode = (node: Node) => + toolkitRootElement.contains(node) || + tuningModalElement.contains(node) || + hoverElement.contains(node) || + selectedElement.contains(node) || + multiSelectedOverlays.some(({ overlayElement }) => + overlayElement.contains(node), + ) || + dimElements.some((dimElement) => dimElement.contains(node)); + + const getSelectableElement = (clientX: number, clientY: number) => + getSelectableElementAtPosition(clientX, clientY, isToolkitElement); + + const onMouseMove = (mouseEvent: MouseEvent) => { + if (!isSelectionMode) return; + + showHoverForElement( + getSelectableElement(mouseEvent.clientX, mouseEvent.clientY), + ); + }; + + const onSelect = async (mouseEvent: MouseEvent) => { + const clickTarget = mouseEvent.target; + if (!isSelectionMode) { + if ( + isPanelOpen && + clickTarget instanceof Node && + !toolkitRootElement.contains(clickTarget) + ) { + isPanelOpen = false; + renderPanelVisibility(); + } + return; + } + + if (clickTarget instanceof Node && isToolkitNode(clickTarget)) { + return; + } + + const selectedTarget = getSelectableElement( + mouseEvent.clientX, + mouseEvent.clientY, + ); + + if (!selectedTarget) { + return; + } + + mouseEvent.preventDefault(); + mouseEvent.stopPropagation(); + mouseEvent.stopImmediatePropagation(); + + let didCaptureSelection = false; + + try { + const selectionContext = await enrichSelectionContextSourceLocation( + await buildSelectionContextForElement(selectedTarget), + ); + lastSelectionContext = selectionContext; + if (isMultiSelectionMode) { + hideSelectedOverlay(); + showMultiSelectedForElement(selectedTarget, selectionContext, true); + } else { + showSelectedForElement(selectedTarget, true, selectionContext); + } + showDimForElement(selectedTarget); + scheduleDimHide(); + didCaptureSelection = true; + isPanelOpen = true; + renderPanelVisibility(); + const capturedSelectionLabel = `${selectionContext.componentName || 'selection'}${ + selectionContext.selector ? ` (${selectionContext.selector})` : '' + }`; + if (isMultiSelectionMode) { + const existingSelectionIndex = multiSelectionContexts.findIndex( + (context) => context.selector === selectionContext.selector, + ); + if (existingSelectionIndex >= 0) { + multiSelectionContexts[existingSelectionIndex] = selectionContext; + } else { + multiSelectionContexts.push(selectionContext); + } + renderMultiSelectionControls(); + updateStatus( + `Added ${capturedSelectionLabel}. ${multiSelectionContexts.length} selected. Click Done to copy all.`, + ); + } else { + const copyResult = await copyLastSelectionContext('text'); + updateStatus( + copyResult.success + ? `Captured and copied ${capturedSelectionLabel}` + : `Captured ${capturedSelectionLabel}. ${copyResult.error || 'Failed to copy context.'}`, + ); + } + } catch (error) { + const message = + error instanceof Error + ? error.message + : 'Failed to capture selection context'; + updateStatus(message); + } finally { + if (isMultiSelectionMode && didCaptureSelection) { + isSelectionMode = true; + multiselectButtonElement.textContent = 'Selecting...'; + launcherButtonElement.setAttribute( + 'aria-label', + 'Multiselect mode active for Agentic React toolkit', + ); + } else { + isSelectionMode = false; + selectButtonElement.textContent = 'Select'; + multiselectButtonElement.textContent = 'Multiselect'; + doneButtonElement.style.display = 'none'; + launcherButtonElement.setAttribute( + 'aria-label', + 'Open Agentic React toolkit', + ); + } + hideHoverOverlay(); + if (!didCaptureSelection) { + hideDimOverlay(); + } + clearSelectableElementCache(); + } + }; + + const enterSelectionMode = () => { + isSelectionMode = true; + isMultiSelectionMode = false; + clearMultiSelections(); + clearSelectableElementCache(); + hideSelectedOverlay(); + isPanelOpen = true; + renderPanelVisibility(); + selectButtonElement.textContent = 'Selecting...'; + multiselectButtonElement.textContent = 'Multiselect'; + doneButtonElement.style.display = 'none'; + launcherButtonElement.setAttribute( + 'aria-label', + 'Selection mode active for Agentic React toolkit', + ); + updateStatus( + 'Selection mode enabled. Click any element to capture context.', + ); + }; + + const enterMultiSelectionMode = () => { + isSelectionMode = true; + isMultiSelectionMode = true; + clearMultiSelections(); + clearSelectableElementCache(); + hideSelectedOverlay(); + isPanelOpen = true; + renderPanelVisibility(); + selectButtonElement.textContent = 'Select'; + multiselectButtonElement.textContent = 'Selecting...'; + doneButtonElement.style.display = 'block'; + launcherButtonElement.setAttribute( + 'aria-label', + 'Multiselect mode active for Agentic React toolkit', + ); + updateStatus('Multiselect enabled. Select elements, then click Done.'); + }; + + const exitSelectionMode = () => { + isSelectionMode = false; + isMultiSelectionMode = false; + clearMultiSelections(); + selectButtonElement.textContent = 'Select'; + multiselectButtonElement.textContent = 'Multiselect'; + doneButtonElement.style.display = 'none'; + launcherButtonElement.setAttribute( + 'aria-label', + 'Open Agentic React toolkit', + ); + closeTuningModal(); + hideHoverOverlay(); + hideDimOverlay(); + clearSelectableElementCache(); + }; + + const togglePanel = () => { + isPanelOpen = !isPanelOpen; + if (!isPanelOpen) { + exitSelectionMode(); + } + renderPanelVisibility(); + }; + + const showToolkit = () => { + isToolkitVisible = true; + toolkitRootElement.style.display = toolkitConfig.enabled ? 'flex' : 'none'; + }; + + const hideToolkit = () => { + isToolkitVisible = false; + toolkitRootElement.style.display = 'none'; + exitSelectionMode(); + hideSelectedOverlay(); + closeTuningModal(); + }; + + const setToolkitConfig = (config: Partial) => { + toolkitConfig = mergeToolkitConfig(toolkitConfig, config); + updateToolkitStyle(); + + if (!toolkitConfig.enabled) { + hideToolkit(); + } else if (isToolkitVisible) { + showToolkit(); + } + + return toolkitConfig; + }; + + const getLastSelectionContext = (): SelectionContext | null => { + return lastSelectionContext; + }; + + const enrichSelectionContextSourceSnippets = async ( + selectionContext: SelectionContext, + ): Promise => { + let enrichedSelectionContext = + await enrichSelectionContextSourceLocation(selectionContext); + + if (enrichedSelectionContext.sourceSnippets.length > 0) { + return enrichedSelectionContext; + } + + const sourceSnippets: SelectionContext['sourceSnippets'] = []; + const resolvedSources = enrichedSelectionContext.resolvedSources.slice( + 0, + DEFAULT_MAX_SNIPPET_FILES, + ); + const normalizedSourceRoot = getNormalizedSourceRoot(); + + for (const resolvedSource of resolvedSources) { + if ( + !isAllowedProjectSourcePath( + resolvedSource.filePath, + normalizedSourceRoot, + ) + ) { + continue; + } + + if (!resolvedSource.lineNumber || resolvedSource.lineNumber < 1) { + continue; + } + + const candidateUrls = buildBrowserSourceCandidates( + resolvedSource.filePath, + normalizedSourceRoot, + ); + if (candidateUrls.length === 0) { + continue; + } + + for (const candidateUrl of candidateUrls) { + try { + const response = await fetch(candidateUrl, { cache: 'no-store' }); + if (!response.ok) { + continue; + } + + const sourceTextResponse = await response.text(); + const sourceText = + normalizeSourceTextFromViteResponse(sourceTextResponse); + if (!sourceText) { + continue; + } + const snippetPayload = buildSourceSnippet( + sourceText, + resolvedSource.lineNumber, + DEFAULT_SNIPPET_CONTEXT_LINES, + ); + + if (!snippetPayload) { + continue; + } + + sourceSnippets.push({ + filePath: + toAbsoluteSourcePath( + resolvedSource.filePath, + normalizedSourceRoot, + ) ?? resolvedSource.filePath, + startLine: snippetPayload.startLine, + endLine: snippetPayload.endLine, + snippet: snippetPayload.snippet, + }); + break; + } catch (_error) { + // best effort only + } + } + } + + if (sourceSnippets.length === 0) { + return enrichedSelectionContext; + } + + enrichedSelectionContext = { + ...enrichedSelectionContext, + sourceSnippets, + }; + return { + ...enrichedSelectionContext, + sourcePreview: buildSelectionSourcePreview(enrichedSelectionContext), + }; + }; + + const copySelectionContexts = async ( + selectionContexts: SelectionContext[], + ): Promise<{ + success: boolean; + copied: boolean; + contexts: SelectionContext[]; + error?: string; + }> => { + if (selectionContexts.length === 0) { + return { + success: false, + copied: false, + contexts: [], + error: 'No selections found', + }; + } + + const textToCopy = selectionContexts + .map( + (selectionContext, index) => + `Selection ${index + 1}\n${toTextContext(selectionContext)}`, + ) + .join('\n\n---\n\n'); + const copied = await copyText(textToCopy); + + return { + success: copied, + copied, + contexts: selectionContexts, + error: copied ? undefined : 'Failed to copy selections to clipboard', + }; + }; + + const copyLastSelectionContext = async ( + format: 'text' | 'json' = 'text', + ): Promise<{ + success: boolean; + copied: boolean; + format: 'text' | 'json'; + context?: SelectionContext; + error?: string; + }> => { + if (!lastSelectionContext) { + return { + success: false, + copied: false, + format, + error: 'No selection context found', + }; + } + + lastSelectionContext = + await enrichSelectionContextSourceSnippets(lastSelectionContext); + + const textToCopy = + format === 'json' + ? JSON.stringify(lastSelectionContext, null, 2) + : toTextContext(lastSelectionContext); + const copied = await copyText(textToCopy); + + if (!copied) { + return { + success: false, + copied: false, + format, + context: lastSelectionContext, + error: 'Failed to copy context to clipboard', + }; + } + + updateStatus('Copied last selection context'); + + return { + success: true, + copied: true, + format, + context: lastSelectionContext, + }; + }; + + selectedActions.tuneButtonElement.addEventListener('click', (event) => { + event.preventDefault(); + event.stopPropagation(); + if (!selectedTargetElement || !lastSelectionContext) { + updateStatus('No selection to adjust.'); + return; + } + + openTuningModal({ + anchorElement: selectedActions.tuneButtonElement, + element: selectedTargetElement, + selectionContext: lastSelectionContext, + setSelectionContext: (nextSelectionContext) => { + lastSelectionContext = nextSelectionContext; + }, + }); + }); + + selectedActions.deleteButtonElement.addEventListener('click', (event) => { + event.preventDefault(); + event.stopPropagation(); + lastSelectionContext = null; + hideSelectedOverlay(); + hideDimOverlay(); + closeTuningModal(); + updateStatus('Cleared selected context.'); + }); + + tuningCloseButtonElement.addEventListener('click', () => { + closeTuningModal(); + }); + + tuningModalElement.addEventListener('click', (event) => { + if (event.target === tuningModalElement) { + closeTuningModal(); + } + }); + + launcherButtonElement.addEventListener('click', () => { + togglePanel(); + }); + + selectButtonElement.addEventListener('click', () => { + if (isSelectionMode) { + exitSelectionMode(); + } else { + enterSelectionMode(); + } + }); + + multiselectButtonElement.addEventListener('click', () => { + if (isMultiSelectionMode) { + exitSelectionMode(); + } else { + enterMultiSelectionMode(); + } + }); + + clearAllButtonElement.addEventListener('click', () => { + if (!isMultiSelectionMode) { + return; + } + + if (multiSelectionContexts.length === 0) { + updateStatus('No selections to clear.'); + return; + } + + clearMultiSelections(); + hideDimOverlay(); + updateStatus('Cleared all selections. Continue selecting or click Done.'); + }); + + doneButtonElement.addEventListener('click', () => { + void (async () => { + const selectionContextsToCopy = multiSelectionContexts.slice(); + const copyResult = await copySelectionContexts(selectionContextsToCopy); + const copiedMessage = `Copied ${copyResult.contexts.length} selected ${ + copyResult.contexts.length === 1 ? 'context' : 'contexts' + }`; + if (copyResult.success) { + lastSelectionContext = + copyResult.contexts[copyResult.contexts.length - 1] ?? + lastSelectionContext; + } else { + updateStatus(copyResult.error || 'Failed to copy selections'); + } + exitSelectionMode(); + isPanelOpen = true; + renderPanelVisibility(); + if (copyResult.success) { + updateStatus(copiedMessage); + } + })(); + }); + + const mountToolkit = () => { + if (!document.body) { + return false; + } + + mountSelectionStyles(); + updateToolkitStyle(); + toolkitRootElement.style.display = isToolkitVisible ? 'flex' : 'none'; + + for (const dimElement of dimElements) { + if (!dimElement.isConnected) { + document.body.appendChild(dimElement); + } + } + if (!toolkitRootElement.isConnected) { + document.body.appendChild(toolkitRootElement); + } + if (!hoverElement.isConnected) { + document.body.appendChild(hoverElement); + } + if (!selectedElement.isConnected) { + document.body.appendChild(selectedElement); + } + if (!tuningModalElement.isConnected) { + document.body.appendChild(tuningModalElement); + } + return true; + }; + + if (!mountToolkit()) { + document.addEventListener( + 'DOMContentLoaded', + () => { + mountToolkit(); + }, + { once: true }, + ); + } + + document.addEventListener('mousemove', onMouseMove, true); + document.addEventListener('click', onSelect, true); + document.addEventListener('scroll', updateSelectionOverlays, true); + window.addEventListener('resize', updateSelectionOverlays); + + return { + showToolkit, + hideToolkit, + setToolkitConfig, + enterSelectionMode, + exitSelectionMode, + setSelectionMode: (enabled: boolean) => { + if (enabled) { + enterSelectionMode(); + } else { + exitSelectionMode(); + } + }, + getLastSelectionContext, + copyLastSelectionContext, + registerTuningModalExtension, + }; +}; diff --git a/packages/core/src/core/tools/source_ownership.ts b/packages/core/src/core/tools/source_ownership.ts new file mode 100644 index 0000000..6dded14 --- /dev/null +++ b/packages/core/src/core/tools/source_ownership.ts @@ -0,0 +1,38 @@ +import type { Fiber } from 'bippy'; +import { getSource, isSourceFile, normalizeFileName } from 'bippy/source'; + +const PATH_SEPARATOR_PATTERN = /[/\\]/; + +const normalizeSourceFilePath = (fileName: string): string => { + let normalizedFileName = normalizeFileName(fileName); + normalizedFileName = normalizedFileName.replace( + /^(?:\.\/)?\/?\([a-z][a-z0-9-]*\)\//, + '', + ); + if (normalizedFileName.startsWith('./')) { + normalizedFileName = normalizedFileName.slice(2); + } + return normalizedFileName.replace(/\?.*$/, ''); +}; + +const splitPathSegments = (path: string): string[] => + path.split(PATH_SEPARATOR_PATTERN).filter(Boolean); + +export const isProjectSourceFrame = (fileName: string): boolean => { + if (!isSourceFile(fileName)) { + return false; + } + + const normalizedFileName = normalizeSourceFilePath(fileName); + const pathSegments = splitPathSegments(normalizedFileName); + return ( + !pathSegments.includes('node_modules') && + !pathSegments.includes('.vite') && + !normalizedFileName.includes('/@vite/') + ); +}; + +export const isProjectOwnedFiber = async (fiber: Fiber): Promise => { + const source = await getSource(fiber).catch(() => null); + return Boolean(source?.fileName && isProjectSourceFrame(source.fileName)); +}; diff --git a/src/core/tools/track_wasted_render.ts b/packages/core/src/core/tools/track_wasted_render.ts similarity index 95% rename from src/core/tools/track_wasted_render.ts rename to packages/core/src/core/tools/track_wasted_render.ts index 94ba9ea..2d97268 100644 --- a/src/core/tools/track_wasted_render.ts +++ b/packages/core/src/core/tools/track_wasted_render.ts @@ -7,14 +7,15 @@ import { shouldFilterFiber, traverseProps, } from 'bippy'; -import { store, wastedRenderFiberInfo } from '../../shared/store'; +import { store, wastedRenderFiberInfo } from '../../shared/store.js'; import { getDisplayName, getDisplayNameForFiber, getNearestFiberWithStateNode, isReactElement, -} from '../../shared/util'; -import { flashStateNode } from './util'; +} from '../../shared/util.js'; +import { isProjectOwnedFiber } from './source_ownership.js'; +import { flashStateNode } from './util.js'; const stringifyValueExceptObject = (value: any): string | null => { if (value === null) return 'null'; @@ -329,7 +330,7 @@ export const collectUnnecessaryRender = (fiber: Fiber) => { } }; -export const queryWastedRender = ( +export const queryWastedRender = async ( timeframe: number, { allComponents = false, @@ -343,12 +344,12 @@ export const queryWastedRender = ( start = Date.now() - timeframe * 1000; for (const wastedRender of wastedRenderFiberInfo.values()) { - if ( - wastedRender.collectedAt >= start && - wastedRender.collectedAt <= end && - wastedRender.fiber.return && - (allComponents || window.__REACT_COMPONENTS__.includes(wastedRender.name)) - ) { + const isWithinTimeframe = + wastedRender.collectedAt >= start && wastedRender.collectedAt <= end; + const isIncludedComponent = + allComponents || (await isProjectOwnedFiber(wastedRender.fiber)); + + if (isWithinTimeframe && wastedRender.fiber.return && isIncludedComponent) { result.push(wastedRender); } } diff --git a/src/core/tools/util.ts b/packages/core/src/core/tools/util.ts similarity index 97% rename from src/core/tools/util.ts rename to packages/core/src/core/tools/util.ts index 5c129a0..0d2753e 100644 --- a/src/core/tools/util.ts +++ b/packages/core/src/core/tools/util.ts @@ -1,10 +1,10 @@ import type { Fiber } from 'bippy'; -import { getAllFiberRoots } from '../../shared/util'; +import { getAllFiberRoots } from '../../shared/util.js'; import { findComponentsInFiber, getNearestFiberWithStateNode, -} from '../../shared/util'; -import type { ComponentTreeNode } from '../../types/internal'; +} from '../../shared/util.js'; +import type { ComponentTreeNode } from '../../types/internal.js'; export function getFibersByComponentName(componentName: string): Fiber[] { const result: Fiber[] = []; diff --git a/packages/core/src/global.d.ts b/packages/core/src/global.d.ts new file mode 100644 index 0000000..be1ac27 --- /dev/null +++ b/packages/core/src/global.d.ts @@ -0,0 +1,61 @@ +import type { + AgenticReactConfig, + SelectionContext, + ToolResultValue, + ToolkitConfig, + TuningModalExtension, +} from './shared/types.js'; +import type { WastedRenderFiberInfo } from './types/internal.js'; + +type AgenticReactTools = { + highlightComponent: ( + componentName: string, + options?: { debugMode?: boolean }, + ) => unknown[]; + getComponentTree: (options?: { + allComponents?: boolean; + debugMode?: boolean; + }) => Promise; + getComponentStates: ( + componentName: string, + options?: { debugMode?: boolean }, + ) => unknown; + getUnnecessaryRenderedComponents: ( + timeframe?: number, + options?: { + allComponents?: boolean; + debugMode?: boolean; + }, + ) => Promise; + registerCustomTool: ( + name: string, + handler: (args: unknown) => ToolResultValue | Promise, + ) => void; +}; + +type AgenticReactRuntime = { + showToolkit: () => void; + hideToolkit: () => void; + setToolkitConfig: (config: Partial) => ToolkitConfig; + enterSelectionMode: () => void; + exitSelectionMode: () => void; + setSelectionMode: (enabled: boolean) => void; + getLastSelectionContext: () => SelectionContext | null; + copyLastSelectionContext: (format?: 'text' | 'json') => Promise<{ + success: boolean; + copied: boolean; + format: 'text' | 'json'; + context?: SelectionContext; + error?: string; + }>; + registerTuningModalExtension: (extension: TuningModalExtension) => () => void; +}; + +declare global { + interface Window { + __AGENTIC_REACT_CONFIG__?: AgenticReactConfig; + __AGENTIC_REACT_BRIDGE_URL__?: string; + __AGENTIC_REACT_TOOLS__: AgenticReactTools; + __AGENTIC_REACT__: AgenticReactRuntime; + } +} diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts new file mode 100644 index 0000000..3b3cc68 --- /dev/null +++ b/packages/core/src/index.ts @@ -0,0 +1,36 @@ +export { RuntimeBridgeServer } from './bridge/server.js'; +export { createStreamableHttpMcpHandler, initMcpServer } from './mcp/index.js'; +export { + buildSelectionContextForElement, + buildSelectionContextSummary, + buildSelectionSourcePreview, + clearSelectableElementCache, + createElementSelector, + createSelectionToolkit, + getSelectableElementAtPosition, + isSelectableElement, +} from './select.js'; +export type { + AgenticReactConfig, + CustomClientFunction, + CustomTool, + JsonValue, + SelectionContext, + SelectionResolvedSource, + SelectionSourceSnippet, + SelectionStackFrame, + ToolkitConfig, + ToolkitOffset, + ToolkitPosition, + ToolkitTuningModalConfig, + ToolkitTuningModalStyle, + ToolkitTuningModalStyleSlot, + ToolkitTuningModalStyleValue, + ToolResultValue, + TuningModalActions, + TuningModalContext, + TuningModalExtension, + TuningModalExtensionCleanup, + TuningModalSlotRenderArgs, + TuningModalWrapArgs, +} from './shared/types.js'; diff --git a/packages/core/src/mcp/index.ts b/packages/core/src/mcp/index.ts new file mode 100644 index 0000000..50a863f --- /dev/null +++ b/packages/core/src/mcp/index.ts @@ -0,0 +1,1055 @@ +import { randomUUID } from 'node:crypto'; +import fs from 'node:fs'; +import type { IncomingMessage, ServerResponse } from 'node:http'; +import path from 'node:path'; +import { Server } from '@modelcontextprotocol/sdk/server/index.js'; +import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js'; +import { + CallToolRequestSchema, + type CallToolResult, + ListToolsRequestSchema, + isInitializeRequest, +} from '@modelcontextprotocol/sdk/types.js'; +import { z } from 'zod'; +import zodToJsonSchema from 'zod-to-json-schema'; +import { getVersionString } from '../shared/node_util.js'; +import type { BridgeRequestEvent } from '../shared/protocol.js'; +import { + buildSelectionContextSummary, + buildSelectionSourcePreview, +} from '../shared/selection_context_format.js'; +import { + classifySourcePath, + isAllowedProjectSourcePath, + normalizeSourceRoot, +} from '../shared/source_path.js'; +import type { + CustomTool, + SelectionContext, + SelectionResolvedSource, + SelectionSourceSnippet, + ToolResultValue, +} from '../shared/types.js'; +import { + CopyLastSelectionContextSchema, + GetComponentStatesSchema, + GetComponentTreeSchema, + GetHtmlElementsSchema, + GetLastSelectionContextSchema, + GetReactSourceCodeSchema, + GetUnnecessaryRerendersSchema, + HighlightComponentSchema, + SetSelectionModeSchema, +} from './schema.js'; + +const builtInTools = [ + { + name: 'highlight-component', + description: 'Highlight React component based on the component name.', + inputSchema: zodToJsonSchema(HighlightComponentSchema), + }, + { + name: 'get-component-tree', + description: + 'Get the React component tree of the current page in ASCII format.', + inputSchema: zodToJsonSchema(GetComponentTreeSchema), + }, + { + name: 'get-component-states', + description: + 'Get the React component props, states, and contexts in JSON structure format.', + inputSchema: zodToJsonSchema(GetComponentStatesSchema), + }, + { + name: 'get-unnecessary-rerenders', + description: 'Get the wasted re-rendered components of the current page', + inputSchema: zodToJsonSchema(GetUnnecessaryRerendersSchema), + }, + { + name: 'set-selection-mode', + description: + 'Enable or disable browser element selection mode for context capture.', + inputSchema: zodToJsonSchema(SetSelectionModeSchema), + }, + { + name: 'get-last-selection-context', + description: + 'Get the latest captured selection context, optionally enriched with source snippets from the project filesystem.', + inputSchema: zodToJsonSchema(GetLastSelectionContextSchema), + }, + { + name: 'copy-last-selection-context', + description: + 'Copy the latest captured selection context from the browser runtime into clipboard.', + inputSchema: zodToJsonSchema(CopyLastSelectionContextSchema), + }, + { + name: 'get-html-elements', + description: + 'Find matching HTML elements by search strings and return deterministic candidates.', + inputSchema: zodToJsonSchema(GetHtmlElementsSchema), + }, + { + name: 'get-react-source-code', + description: + 'Find matching elements using search strings and return deterministic React source context for the best candidate.', + inputSchema: zodToJsonSchema(GetReactSourceCodeSchema), + }, +] as const; + +type BuiltInToolName = (typeof builtInTools)[number]['name']; + +const builtInToolNames = new Set(builtInTools.map((tool) => tool.name)); + +function isBuiltInToolName(name: string): name is BuiltInToolName { + return builtInToolNames.has(name as BuiltInToolName); +} + +function formatToolResult(result: ToolResultValue): string { + if (typeof result === 'string') { + return result; + } + if (result === undefined) { + return 'Custom tool executed successfully.'; + } + try { + return JSON.stringify(result); + } catch (_error) { + return String(result); + } +} + +const toTextResponse = (data: unknown): CallToolResult => { + return { + content: [{ type: 'text', text: JSON.stringify(data) }], + }; +}; + +type NodeMcpRequest = IncomingMessage & { body?: unknown }; + +type CreateMcpServer = () => Server; + +type StreamableHttpMcpHandler = ( + req: NodeMcpRequest, + res: ServerResponse, +) => Promise; + +const getMcpSessionId = (req: IncomingMessage): string | null => { + const header = req.headers['mcp-session-id']; + if (Array.isArray(header)) { + return header[0] || null; + } + return header || null; +}; + +const readJsonRequestBody = async (req: NodeMcpRequest): Promise => { + if (req.body !== undefined) { + return req.body; + } + + let rawBody = ''; + for await (const chunk of req) { + rawBody += Buffer.isBuffer(chunk) ? chunk.toString('utf8') : String(chunk); + } + + if (!rawBody.trim()) { + return undefined; + } + + return JSON.parse(rawBody); +}; + +const writeJsonRpcError = ( + res: ServerResponse, + statusCode: number, + code: number, + message: string, +) => { + if (res.headersSent || res.writableEnded) { + return; + } + res.statusCode = statusCode; + res.setHeader('content-type', 'application/json'); + res.end( + JSON.stringify({ + jsonrpc: '2.0', + error: { code, message }, + id: null, + }), + ); +}; + +export function createStreamableHttpMcpHandler( + createMcpServer: CreateMcpServer, +): StreamableHttpMcpHandler { + const transports = new Map(); + + return async (req, res) => { + const method = req.method || 'GET'; + if (method !== 'GET' && method !== 'POST' && method !== 'DELETE') { + writeJsonRpcError(res, 405, -32000, 'Method not allowed.'); + return; + } + + let parsedBody: unknown; + if (method === 'POST') { + try { + parsedBody = await readJsonRequestBody(req); + } catch (_error) { + writeJsonRpcError(res, 400, -32700, 'Parse error.'); + return; + } + } + + const sessionId = getMcpSessionId(req); + let transport = sessionId === null ? undefined : transports.get(sessionId); + + try { + if (!transport && sessionId) { + writeJsonRpcError( + res, + 404, + -32000, + 'Session not found for MCP Streamable HTTP transport.', + ); + return; + } + + if (!transport) { + if (method !== 'POST' || !isInitializeRequest(parsedBody)) { + writeJsonRpcError( + res, + 400, + -32000, + 'Bad Request: No valid MCP session ID provided.', + ); + return; + } + + const server = createMcpServer(); + transport = new StreamableHTTPServerTransport({ + sessionIdGenerator: () => randomUUID(), + onsessioninitialized: (initializedSessionId) => { + transports.set(initializedSessionId, transport); + }, + }); + + transport.onclose = () => { + const initializedSessionId = transport?.sessionId; + if (initializedSessionId) { + transports.delete(initializedSessionId); + } + }; + + await server.connect(transport); + } + + await transport.handleRequest(req, res, parsedBody); + } catch (_error) { + writeJsonRpcError(res, 500, -32603, 'Internal server error.'); + } + }; +} + +const resolveAbsoluteSourcePath = ( + rootDir: string, + filePath: string, +): string | null => { + const normalizedRootDir = fs.realpathSync(rootDir); + const normalizedSourceRoot = normalizeSourceRoot( + normalizedRootDir.replace(/\\/g, '/'), + ); + + if (!isAllowedProjectSourcePath(filePath, normalizedSourceRoot)) { + return null; + } + + const classifiedPath = classifySourcePath(filePath, normalizedSourceRoot); + const candidates: string[] = []; + + if (classifiedPath.kind === 'vite-fs') { + const fileSystemPath = classifiedPath.normalizedPath.slice('/@fs'.length); + candidates.push(path.resolve(fileSystemPath)); + } else if (classifiedPath.kind === 'fs-absolute') { + candidates.push(path.resolve(classifiedPath.normalizedPath)); + } else if (classifiedPath.kind === 'vite-root-relative') { + candidates.push(path.resolve(rootDir, `.${classifiedPath.normalizedPath}`)); + } else if (classifiedPath.kind === 'project-relative') { + candidates.push(path.resolve(rootDir, classifiedPath.normalizedPath)); + + const nxWebpackSourcePathMatch = classifiedPath.normalizedPath.match( + /^([^/]+)\/(?:\.\/)?src\/(.+)$/, + ); + if (nxWebpackSourcePathMatch) { + const [, projectName, sourcePath] = nxWebpackSourcePathMatch; + candidates.push( + path.resolve(rootDir, 'apps', projectName, 'src', sourcePath), + ); + } + } + + for (const candidate of candidates) { + const resolvedCandidate = path.resolve(candidate); + if (!fs.existsSync(resolvedCandidate)) { + continue; + } + + const stat = fs.statSync(resolvedCandidate); + if (!stat.isFile()) { + continue; + } + + const realCandidate = fs.realpathSync(resolvedCandidate); + const relativeToRoot = path.relative(normalizedRootDir, realCandidate); + const isInsideRoot = + relativeToRoot === '' || + (!relativeToRoot.startsWith('..') && !path.isAbsolute(relativeToRoot)); + if (!isInsideRoot) { + continue; + } + + const pathSegments = relativeToRoot.split(path.sep); + if ( + pathSegments.includes('node_modules') || + pathSegments.includes('.vite') + ) { + continue; + } + + return realCandidate; + } + + return null; +}; + +const getSourceSnippet = ( + filePath: string, + lineNumber: number | null, + contextLines: number, +): SelectionSourceSnippet | null => { + try { + const rawFileContent = fs.readFileSync(filePath, 'utf8'); + const fileLines = rawFileContent.split(/\r?\n/); + const referenceLine = Math.max( + 1, + Math.min(fileLines.length, lineNumber || 1), + ); + const startLine = Math.max(1, referenceLine - contextLines); + const endLine = Math.min(fileLines.length, referenceLine + contextLines); + + const snippet = fileLines + .slice(startLine - 1, endLine) + .map((lineContent, index) => { + const absoluteLine = startLine + index; + return `${absoluteLine}: ${lineContent}`; + }) + .join('\n'); + + return { + filePath, + startLine, + endLine, + snippet, + }; + } catch (_error) { + return null; + } +}; + +const LOCAL_COMPONENT_NAME_PATTERN = /^[A-Z][A-Za-z0-9_$]*$/; +const IGNORED_LOCAL_USAGE_COMPONENTS = new Set([ + 'Button', + 'Card', + 'Card2', + 'CardBody', + 'CardBody2', + 'ChakraComponent2', + 'HStack', + 'VStack', + 'Box', + 'Flex', + 'Stack', + 'Text', + 'Heading', + 'RenderedRoute', +]); + +const isExternalPath = (filePath: string | null): boolean => + Boolean( + filePath && + (filePath.includes('/node_modules/') || + filePath.includes('node_modules/') || + filePath.includes('/.vite/') || + filePath.includes('/@vite/')), + ); + +const inferLocalUsageComponentName = ( + selectionContext: SelectionContext, +): string | null => { + for (const stackFrame of selectionContext.stackFrames) { + const componentName = stackFrame.functionName; + if (!componentName || !LOCAL_COMPONENT_NAME_PATTERN.test(componentName)) { + continue; + } + if (IGNORED_LOCAL_USAGE_COMPONENTS.has(componentName)) { + continue; + } + if (isExternalPath(stackFrame.fileName)) { + continue; + } + return componentName; + } + + return null; +}; + +const walkProjectSourceFiles = ( + directory: string, + visit: (filePath: string) => boolean, +): boolean => { + let entries: fs.Dirent[]; + try { + entries = fs.readdirSync(directory, { withFileTypes: true }); + } catch (_error) { + return false; + } + + for (const entry of entries) { + if ( + entry.name === 'node_modules' || + entry.name === '.git' || + entry.name === 'dist' || + entry.name === 'tmp' + ) { + continue; + } + + const entryPath = path.join(directory, entry.name); + if (entry.isDirectory()) { + if (walkProjectSourceFiles(entryPath, visit)) { + return true; + } + continue; + } + + if (!/\.(?:jsx?|tsx?)$/i.test(entry.name)) { + continue; + } + + if (visit(entryPath)) { + return true; + } + } + + return false; +}; + +const findComponentSourceInProject = ( + rootDir: string, + componentName: string, +): SelectionResolvedSource | null => { + const declarationPattern = new RegExp( + `(?:function\\s+${componentName}\\b|const\\s+${componentName}\\s*=|class\\s+${componentName}\\b)`, + ); + let matchedSource: SelectionResolvedSource | null = null; + const normalizedRootDir = fs.realpathSync(rootDir); + + walkProjectSourceFiles(rootDir, (filePath) => { + let sourceText: string; + try { + sourceText = fs.readFileSync(filePath, 'utf8'); + } catch (_error) { + return false; + } + + if (!declarationPattern.test(sourceText)) { + return false; + } + + const lines = sourceText.split(/\r?\n/); + const matchIndex = lines.findIndex((line) => declarationPattern.test(line)); + const realFilePath = fs.realpathSync(filePath); + const relativeFilePath = path + .relative(normalizedRootDir, realFilePath) + .replace(/\\/g, '/'); + + matchedSource = { + filePath: relativeFilePath, + lineNumber: matchIndex >= 0 ? matchIndex + 1 : 1, + columnNumber: null, + componentName, + }; + return true; + }); + + return matchedSource; +}; + +const stripCssEscapes = (value: string): string => + value.replace(/\\([^\r\n])/g, '$1'); + +const pushSourceHint = (hints: string[], hint: string | null | undefined) => { + if (!hint || hint.length < 3) { + return; + } + if (hints.includes(hint)) { + return; + } + hints.push(hint); +}; + +const extractSelectionSourceHints = ( + selectionContext: SelectionContext, +): string[] => { + const hints: string[] = []; + const selector = selectionContext.selector ?? ''; + const idMatch = selector.match(/#((?:\\.|[^\s>+~.#:[\]])+)/); + if (idMatch?.[1]) { + pushSourceHint(hints, stripCssEscapes(idMatch[1])); + } + + const classHints = Array.from( + selector.matchAll(/\.((?:\\.|[^\s>+~.#:[\]])+)/g), + (match) => stripCssEscapes(match[1]), + ); + for (const classHint of classHints.reverse()) { + pushSourceHint(hints, classHint); + } + + for (const attrMatch of selector.matchAll( + /\[[^\]=\s]+=(?:"([^"]*)"|'([^']*)'|([^\]\s]+))\]/g, + )) { + pushSourceHint( + hints, + stripCssEscapes(attrMatch[1] ?? attrMatch[2] ?? attrMatch[3]), + ); + } + + const domPreview = selectionContext.domPreview; + for (const attrName of [ + 'id', + 'data-testid', + 'data-test-id', + 'data-test', + 'data-cy', + 'data-qa', + 'aria-label', + 'class', + 'name', + 'title', + ]) { + const attrPattern = new RegExp(`${attrName}=["']([^"']+)["']`, 'i'); + const attrMatch = domPreview.match(attrPattern); + pushSourceHint(hints, attrMatch?.[1]); + } + + return hints; +}; + +const refineLocalUsageSourceLine = ( + rootDir: string, + selectionContext: SelectionContext, + localUsageSource: SelectionResolvedSource, +): SelectionResolvedSource => { + const hints = extractSelectionSourceHints(selectionContext); + if (hints.length === 0) { + return localUsageSource; + } + + const absoluteSourcePath = resolveAbsoluteSourcePath( + rootDir, + localUsageSource.filePath, + ); + if (!absoluteSourcePath) { + return localUsageSource; + } + + let sourceText: string; + try { + sourceText = fs.readFileSync(absoluteSourcePath, 'utf8'); + } catch (_error) { + return localUsageSource; + } + + const sourceLines = sourceText.split(/\r?\n/); + const searchStartIndex = Math.max(0, (localUsageSource.lineNumber ?? 1) - 1); + const searchRanges = [ + sourceLines.slice(searchStartIndex).map((line, index) => ({ + line, + lineIndex: searchStartIndex + index, + })), + sourceLines.slice(0, searchStartIndex).map((line, lineIndex) => ({ + line, + lineIndex, + })), + ]; + + for (const hint of hints) { + for (const searchRange of searchRanges) { + const matchedLine = searchRange.find(({ line }) => line.includes(hint)); + if (matchedLine) { + return { + ...localUsageSource, + lineNumber: matchedLine.lineIndex + 1, + }; + } + } + } + + return localUsageSource; +}; + +const placeResolvedSourceFirst = ( + resolvedSources: SelectionResolvedSource[], + preferredSource: SelectionResolvedSource, +): SelectionResolvedSource[] => [ + preferredSource, + ...resolvedSources.filter( + (source) => + source.filePath !== preferredSource.filePath || + source.componentName !== preferredSource.componentName, + ), +]; + +const findLocalSelectionSource = ( + rootDir: string, + selectionContext: SelectionContext, +): SelectionResolvedSource | null => { + const directProjectSource = selectionContext.resolvedSources.find( + (source) => source.filePath && !isExternalPath(source.filePath), + ); + if (directProjectSource) { + return directProjectSource; + } + + const externalUsageSource = selectionContext.externalComponent?.usedBy; + if (externalUsageSource) { + return externalUsageSource; + } + + const componentName = + selectionContext.componentName && + LOCAL_COMPONENT_NAME_PATTERN.test(selectionContext.componentName) && + !IGNORED_LOCAL_USAGE_COMPONENTS.has(selectionContext.componentName) + ? selectionContext.componentName + : inferLocalUsageComponentName(selectionContext); + if (!componentName) { + return null; + } + + return findComponentSourceInProject(rootDir, componentName); +}; + +const enrichSelectionContextSourceLocations = ( + rootDir: string, + selectionContext: SelectionContext, +): SelectionContext => { + const localUsageSource = findLocalSelectionSource(rootDir, selectionContext); + if (!localUsageSource) { + return selectionContext; + } + + const refinedLocalUsageSource = refineLocalUsageSourceLine( + rootDir, + selectionContext, + localUsageSource, + ); + const nextResolvedSources = placeResolvedSourceFirst( + selectionContext.resolvedSources, + refinedLocalUsageSource, + ); + + const nextSelectionContext = { + ...selectionContext, + externalComponent: selectionContext.externalComponent + ? { + ...selectionContext.externalComponent, + usedBy: refinedLocalUsageSource, + } + : null, + resolvedSources: nextResolvedSources, + }; + + return { + ...nextSelectionContext, + sourcePreview: buildSelectionSourcePreview(nextSelectionContext, { + sourceRoot: rootDir, + }), + }; +}; + +const enrichSelectionContextWithSnippets = ( + rootDir: string, + selectionContext: SelectionContext, + contextLines: number, + maxFiles: number, +): SelectionContext => { + const enrichedSelectionContext = enrichSelectionContextSourceLocations( + rootDir, + selectionContext, + ); + const sourceSnippets: SelectionSourceSnippet[] = []; + const seenFilePaths = new Set(); + + for (const resolvedSource of enrichedSelectionContext.resolvedSources) { + const sourceFilePath = resolvedSource.filePath; + if (!sourceFilePath || seenFilePaths.has(sourceFilePath)) { + continue; + } + + if (sourceSnippets.length >= maxFiles) { + break; + } + + const absoluteSourcePath = resolveAbsoluteSourcePath( + rootDir, + sourceFilePath, + ); + if (!absoluteSourcePath) { + continue; + } + + const sourceSnippet = getSourceSnippet( + absoluteSourcePath, + resolvedSource.lineNumber, + contextLines, + ); + + if (!sourceSnippet) { + continue; + } + + sourceSnippets.push({ + ...sourceSnippet, + filePath: sourceFilePath, + }); + seenFilePaths.add(sourceFilePath); + } + + return { + ...enrichedSelectionContext, + sourceSnippets, + sourcePreview: buildSelectionSourcePreview( + { + ...enrichedSelectionContext, + sourceSnippets, + }, + { + sourceRoot: rootDir, + }, + ), + }; +}; + +const parseSelectionContextResponse = (response: { + context: SelectionContext | null; +}): SelectionContext | null => { + if (!response || !response.context) { + return null; + } + + return { + ...response.context, + externalComponent: response.context.externalComponent ?? null, + sourcePreview: response.context.sourcePreview ?? null, + sourceSnippets: Array.isArray(response.context.sourceSnippets) + ? response.context.sourceSnippets + : [], + }; +}; + +export interface McpRuntimeBridge { + request: ( + event: BridgeRequestEvent, + payload: unknown, + options?: { + acceptResponse?: (value: unknown) => boolean; + broadcast?: boolean; + timeoutMs?: number; + }, + ) => Promise; +} + +const requestRuntime = async ( + bridge: McpRuntimeBridge, + event: BridgeRequestEvent, + args: unknown, + options?: { + acceptResponse?: (value: unknown) => boolean; + broadcast?: boolean; + timeoutMs?: number; + }, +): Promise => { + const response = await bridge.request(event, args, options); + return response as T; +}; + +export function initMcpServer( + bridge: McpRuntimeBridge, + rootDir: string, + customTools: CustomTool[] = [], +): Server { + const server = new Server( + { + name: 'agentic-react', + version: getVersionString(), + }, + { + capabilities: { + tools: {}, + }, + }, + ); + + server.setRequestHandler(ListToolsRequestSchema, async () => { + const dynamicTools = customTools.map((tool) => ({ + name: tool.name, + description: tool.description, + inputSchema: zodToJsonSchema(tool.schema), + })); + + return { + tools: [...builtInTools, ...dynamicTools], + }; + }); + + server.setRequestHandler( + CallToolRequestSchema, + async (request): Promise => { + try { + if (isBuiltInToolName(request.params.name)) { + switch (request.params.name) { + case 'highlight-component': { + const args = HighlightComponentSchema.parse( + request.params.arguments, + ); + const response = await requestRuntime( + bridge, + 'highlight-component', + args, + ); + return { + content: [{ type: 'text', text: response }], + }; + } + + case 'get-component-tree': { + const args = GetComponentTreeSchema.parse( + request.params.arguments, + ); + const response = await requestRuntime( + bridge, + 'get-component-tree', + args, + ); + return { + content: [{ type: 'text', text: response }], + }; + } + + case 'get-component-states': { + const args = GetComponentStatesSchema.parse( + request.params.arguments, + ); + const response = await requestRuntime( + bridge, + 'get-component-states', + args, + ); + return { + content: [{ type: 'text', text: response }], + }; + } + + case 'get-unnecessary-rerenders': { + const args = GetUnnecessaryRerendersSchema.parse( + request.params.arguments, + ); + + const response = await requestRuntime( + bridge, + 'get-unnecessary-rerenders', + args, + ); + return { + content: [{ type: 'text', text: response }], + }; + } + + case 'set-selection-mode': { + const args = SetSelectionModeSchema.parse( + request.params.arguments, + ); + const response = await requestRuntime<{ + success: boolean; + enabled: boolean; + }>(bridge, 'set-selection-mode', args); + + return toTextResponse(response); + } + + case 'get-last-selection-context': { + const args = GetLastSelectionContextSchema.parse( + request.params.arguments, + ); + + const browserResponse = await requestRuntime<{ + context: SelectionContext | null; + }>(bridge, 'get-last-selection-context', args, { + acceptResponse: (response) => + Boolean( + response && + typeof response === 'object' && + 'context' in response && + response.context, + ), + }); + + const selectionContext = + parseSelectionContextResponse(browserResponse); + if (!selectionContext) { + return toTextResponse({ + success: false, + message: 'No selection context has been captured yet.', + context: null, + }); + } + + const enrichedSelectionContext = args.includeSourceSnippets + ? enrichSelectionContextWithSnippets( + rootDir, + selectionContext, + args.contextLines, + args.maxFiles, + ) + : enrichSelectionContextSourceLocations( + rootDir, + selectionContext, + ); + + return toTextResponse({ + success: true, + summary: buildSelectionContextSummary( + enrichedSelectionContext, + { + sourceRoot: rootDir, + }, + ), + context: enrichedSelectionContext, + }); + } + + case 'copy-last-selection-context': { + const args = CopyLastSelectionContextSchema.parse( + request.params.arguments, + ); + + const response = await requestRuntime<{ + success: boolean; + copied: boolean; + format: 'text' | 'json'; + context?: SelectionContext; + error?: string; + }>(bridge, 'copy-last-selection-context', args, { + acceptResponse: (response) => + Boolean( + response && + typeof response === 'object' && + 'context' in response && + response.context, + ), + }); + + return toTextResponse(response); + } + + case 'get-html-elements': { + const args = GetHtmlElementsSchema.parse( + request.params.arguments, + ); + const response = await requestRuntime( + bridge, + 'get-html-elements', + args, + ); + return toTextResponse(response); + } + + case 'get-react-source-code': { + const args = GetReactSourceCodeSchema.parse( + request.params.arguments, + ); + const response = (await requestRuntime<{ + success: boolean; + reason?: string; + chosenMatch?: { + selector: string | null; + query: string; + }; + context?: SelectionContext | null; + }>(bridge, 'get-react-source-code', args)) || { + success: false, + }; + + if (!response.success || !response.context) { + return toTextResponse(response); + } + + const enrichedContext = args.includeSourceSnippets + ? enrichSelectionContextWithSnippets( + rootDir, + response.context, + args.contextLines, + args.maxFiles, + ) + : enrichSelectionContextSourceLocations( + rootDir, + response.context, + ); + + return toTextResponse({ + success: true, + chosenMatch: response.chosenMatch, + summary: buildSelectionContextSummary(enrichedContext, { + sourceRoot: rootDir, + }), + context: enrichedContext, + }); + } + + default: + throw new Error(`Unknown tool: ${request.params.name}`); + } + } + + const customTool = customTools.find( + (tool) => tool.name === request.params.name, + ); + if (customTool) { + const parsedArgs = customTool.schema.parse(request.params.arguments); + const result = await requestRuntime( + bridge, + 'custom-tool', + { + args: parsedArgs, + name: customTool.name, + }, + { broadcast: true }, + ); + return { + content: [{ type: 'text', text: formatToolResult(result) }], + }; + } + + throw new Error(`Unknown tool: ${request.params.name}`); + } catch (error) { + if (error instanceof z.ZodError) { + throw new Error(`Invalid input: ${JSON.stringify(error.errors)}`); + } + throw error; + } + }, + ); + + return server; +} diff --git a/packages/core/src/mcp/schema.ts b/packages/core/src/mcp/schema.ts new file mode 100644 index 0000000..71dd1d6 --- /dev/null +++ b/packages/core/src/mcp/schema.ts @@ -0,0 +1,117 @@ +import { z } from 'zod'; + +export const HighlightComponentSchema = z.object({ + componentName: z.string().describe('The name of the component to highlight'), +}); + +export const GetComponentTreeSchema = z.object({ + allComponents: z + .boolean() + .default(false) + .describe( + 'Whether to get tree for all components, instead of just your self-defined ones', + ), +}); + +export const GetComponentStatesSchema = z.object({ + componentName: z + .string() + .describe('The name of the component to get the states of'), +}); + +export const GetUnnecessaryRerendersSchema = z.object({ + timeframe: z + .number() + .optional() + .describe( + 'The timeframe to query wasted re-renders, in unit of seconds, e.g. 10 means last 10 seconds', + ), + allComponents: z + .boolean() + .default(false) + .describe( + 'Whether to query wasted re-renders for all components. Must be explicitly stated to set this to true', + ), +}); + +export const SetSelectionModeSchema = z.object({ + enabled: z + .boolean() + .describe('Whether to enable element selection mode in the browser'), +}); + +export const GetLastSelectionContextSchema = z.object({ + includeSourceSnippets: z + .boolean() + .default(true) + .describe( + 'Whether source snippets should be included in the response payload', + ), + contextLines: z + .number() + .int() + .min(0) + .max(80) + .default(10) + .describe('How many lines to include before and after the resolved line'), + maxFiles: z + .number() + .int() + .min(1) + .max(40) + .default(8) + .describe('Maximum number of source files to include in source snippets'), +}); + +export const CopyLastSelectionContextSchema = z.object({ + format: z + .enum(['text', 'json']) + .default('text') + .describe('Clipboard payload format'), +}); + +export const GetHtmlElementsSchema = z.object({ + queries: z + .array(z.string().min(1)) + .min(1) + .describe('List of search strings used to find matching DOM elements'), + maxMatches: z + .number() + .int() + .min(1) + .max(40) + .default(10) + .describe('Maximum number of matching elements to return'), +}); + +export const GetReactSourceCodeSchema = z.object({ + queries: z + .array(z.string().min(1)) + .min(1) + .describe('Ordered list of search strings to resolve a target element'), + maxMatches: z + .number() + .int() + .min(1) + .max(20) + .default(5) + .describe('Maximum number of candidate elements to resolve'), + includeSourceSnippets: z + .boolean() + .default(true) + .describe('Whether source snippets should be included in the response'), + contextLines: z + .number() + .int() + .min(0) + .max(80) + .default(8) + .describe('How many lines to include before and after resolved lines'), + maxFiles: z + .number() + .int() + .min(1) + .max(20) + .default(6) + .describe('Maximum number of source files to include in snippets'), +}); diff --git a/packages/core/src/overlay.js b/packages/core/src/overlay.js new file mode 100644 index 0000000..8d76354 --- /dev/null +++ b/packages/core/src/overlay.js @@ -0,0 +1,436 @@ +import * as bippy from 'bippy'; +import { highlightComponent } from './core/tools/component_highlighter.js'; +import { getComponentStates } from './core/tools/component_state_viewer.js'; +import { getComponentTree } from './core/tools/component_viewer.js'; +import { buildSelectionContextForElement } from './core/tools/selection_context.js'; +import { createElementSelector } from './core/tools/selection_selector.js'; +import { createSelectionToolkit } from './core/tools/selection_toolkit.js'; +import { + collectUnnecessaryRender, + queryWastedRender, +} from './core/tools/track_wasted_render.js'; +import { + __AGENTIC_REACT_BRIDGE_URL__, + __AGENTIC_REACT_CONFIG__, + __AGENTIC_REACT_TOOLS__, + __AGENTIC_REACT__, + target, +} from './shared/const.js'; +import { BRIDGE_GLOBAL_CONFIG_KEY, BRIDGE_WS_PATH } from './shared/protocol.js'; +import { fiberRoots, store } from './shared/store.js'; + +const customToolHandlers = new Map(); + +const registerCustomTool = (name, handler) => { + customToolHandlers.set(name, handler); +}; + +const agenticReactConfig = target[__AGENTIC_REACT_CONFIG__] || {}; +const selectionToolkit = createSelectionToolkit({ + initialConfig: agenticReactConfig.toolkit, +}); + +const runtimeApi = { + showToolkit: () => selectionToolkit.showToolkit(), + hideToolkit: () => selectionToolkit.hideToolkit(), + setToolkitConfig: (config) => selectionToolkit.setToolkitConfig(config), + enterSelectionMode: () => selectionToolkit.enterSelectionMode(), + exitSelectionMode: () => selectionToolkit.exitSelectionMode(), + setSelectionMode: (enabled) => selectionToolkit.setSelectionMode(enabled), + getLastSelectionContext: () => selectionToolkit.getLastSelectionContext(), + copyLastSelectionContext: (format = 'text') => + selectionToolkit.copyLastSelectionContext(format), + registerTuningModalExtension: (extension) => + selectionToolkit.registerTuningModalExtension(extension), +}; + +const builtInTools = { + highlightComponent, + getComponentTree, + getComponentStates, + getUnnecessaryRenderedComponents: queryWastedRender, + registerCustomTool, +}; + +if (!Object.hasOwn(target, __AGENTIC_REACT_TOOLS__)) { + Object.defineProperty(target, __AGENTIC_REACT_TOOLS__, { + value: builtInTools, + writable: false, + configurable: true, + }); +} else { + Object.assign(target[__AGENTIC_REACT_TOOLS__], builtInTools); +} +if (!Object.hasOwn(target, __AGENTIC_REACT__)) { + Object.defineProperty(target, __AGENTIC_REACT__, { + value: runtimeApi, + writable: false, + configurable: true, + }); +} else { + Object.assign(target[__AGENTIC_REACT__], runtimeApi); +} + +bippy.instrument({ + name: 'agentic-react', + onCommitFiberRoot: (renderId, root) => { + if (fiberRoots.has(renderId)) { + fiberRoots.get(renderId).add(root); + } else { + fiberRoots.set(renderId, new Set([root])); + } + + bippy.traverseRenderedFibers(root.current || root, (fiber, phase) => { + if (phase === 'update') { + collectUnnecessaryRender(fiber); + } + }); + + store.currentCommitFrameId += 1; + }, +}); + +const normalizeQuery = (query) => query.trim().toLowerCase(); + +const getElementClassName = (element) => { + if (typeof element.className === 'string') { + return element.className; + } + return element.getAttribute?.('class') || ''; +}; + +const getElementSearchText = (element) => { + const parts = []; + parts.push(element.tagName?.toLowerCase() || ''); + parts.push(element.id || ''); + parts.push(getElementClassName(element)); + parts.push(element.textContent || ''); + + for (const attrName of ['name', 'aria-label', 'placeholder', 'title']) { + const attrValue = element.getAttribute?.(attrName); + if (attrValue) { + parts.push(attrValue); + } + } + + return parts.join(' ').toLowerCase(); +}; + +const getElementDepth = (element) => { + let depth = 0; + let current = element; + while (current?.parentElement) { + depth += 1; + current = current.parentElement; + } + return depth; +}; + +const getElementMatchScore = (element, query) => { + const id = normalizeQuery(element.id || ''); + if (id === query) return 0; + + for (const attrName of ['name', 'aria-label', 'placeholder', 'title']) { + const attrValue = normalizeQuery(element.getAttribute?.(attrName) || ''); + if (attrValue === query) return 5; + } + + if (id.includes(query)) return 10; + + for (const attrName of ['name', 'aria-label', 'placeholder', 'title']) { + const attrValue = normalizeQuery(element.getAttribute?.(attrName) || ''); + if (attrValue.includes(query)) return 20; + } + + const className = normalizeQuery(getElementClassName(element)); + if (className.includes(query)) return 30; + + const text = normalizeQuery(element.textContent || ''); + if (text.includes(query)) return 80; + + return 100; +}; + +const findHtmlElements = (queries, maxMatches = 10) => { + const normalizedQueries = (queries || []) + .map((query) => normalizeQuery(String(query || ''))) + .filter(Boolean); + + if (normalizedQueries.length === 0) { + return []; + } + + const matches = []; + const seenElements = new Set(); + + for (const query of normalizedQueries) { + const allElements = document.querySelectorAll('*'); + for (const element of allElements) { + if (seenElements.has(element)) { + continue; + } + + const searchText = getElementSearchText(element); + if (!searchText.includes(query)) { + continue; + } + + seenElements.add(element); + matches.push({ + query, + element, + score: getElementMatchScore(element, query), + depth: getElementDepth(element), + selector: createElementSelector(element), + domPreview: ( + element.outerHTML || `<${element.tagName.toLowerCase()}>` + ).slice(0, 800), + }); + } + } + + return matches + .sort((left, right) => { + if (left.score !== right.score) { + return left.score - right.score; + } + return right.depth - left.depth; + }) + .slice(0, maxMatches); +}; + +const handleBridgeRequest = async (event, payload) => { + switch (event) { + case 'highlight-component': { + if (typeof payload?.componentName !== 'string') { + throw new Error('Invalid args: missing componentName'); + } + let response = 'Action failed'; + try { + const components = target.__AGENTIC_REACT_TOOLS__.highlightComponent( + payload.componentName, + ); + if (components.length > 0) { + response = `Found and highlighted ${components.length} components`; + } + } catch (error) { + response = `Error: ${error?.message || 'Unknown error'}`; + } + return response; + } + + case 'get-component-tree': { + return JSON.stringify( + await target.__AGENTIC_REACT_TOOLS__.getComponentTree(payload || {}), + ); + } + + case 'get-component-states': { + if (typeof payload?.componentName !== 'string') { + throw new Error('Invalid args: missing componentName'); + } + return JSON.stringify( + target.__AGENTIC_REACT_TOOLS__.getComponentStates( + payload.componentName, + ), + ); + } + + case 'get-unnecessary-rerenders': { + const wastedRenders = + await target.__AGENTIC_REACT_TOOLS__.getUnnecessaryRenderedComponents( + payload?.timeframe, + { + allComponents: Boolean(payload?.allComponents), + debugMode: Boolean(payload?.debugMode), + }, + ); + return JSON.stringify(wastedRenders); + } + + case 'set-selection-mode': { + if (typeof payload?.enabled !== 'boolean') { + throw new Error('Invalid args: missing enabled'); + } + target.__AGENTIC_REACT__.setSelectionMode(payload.enabled); + return { success: true, enabled: payload.enabled }; + } + + case 'get-last-selection-context': { + return { + context: target.__AGENTIC_REACT__.getLastSelectionContext(), + }; + } + + case 'copy-last-selection-context': { + const format = payload?.format === 'json' ? 'json' : 'text'; + return await target.__AGENTIC_REACT__.copyLastSelectionContext(format); + } + + case 'get-html-elements': { + const maxMatches = + typeof payload?.maxMatches === 'number' ? payload.maxMatches : 10; + const matches = findHtmlElements(payload?.queries || [], maxMatches).map( + (match) => ({ + query: match.query, + selector: match.selector, + domPreview: match.domPreview, + }), + ); + + return { + success: true, + count: matches.length, + matches, + }; + } + + case 'get-react-source-code': { + const maxMatches = + typeof payload?.maxMatches === 'number' ? payload.maxMatches : 5; + const matches = findHtmlElements(payload?.queries || [], maxMatches); + + if (matches.length === 0) { + return { + success: false, + reason: 'No element matched the provided search strings', + matches: [], + context: null, + }; + } + + const chosenMatch = matches[0]; + const context = await buildSelectionContextForElement( + chosenMatch.element, + ); + + return { + success: true, + matches: matches.map((match) => ({ + query: match.query, + selector: match.selector, + domPreview: match.domPreview, + })), + chosenMatch: { + query: chosenMatch.query, + selector: chosenMatch.selector, + }, + context, + }; + } + + case 'custom-tool': { + if (typeof payload?.name !== 'string') { + throw new Error('Invalid args: missing custom tool name'); + } + + const handler = customToolHandlers.get(payload.name); + if (typeof handler !== 'function') { + throw new Error(`Custom tool "${payload.name}" is not registered`); + } + + return await handler(payload.args); + } + + default: + throw new Error(`Unsupported bridge event: ${event}`); + } +}; + +const getBridgeUrl = () => { + const configuredUrl = + target[__AGENTIC_REACT_BRIDGE_URL__] || target[BRIDGE_GLOBAL_CONFIG_KEY]; + if (typeof configuredUrl === 'string' && configuredUrl.length > 0) { + return configuredUrl; + } + + const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:'; + return `${protocol}//${window.location.host}${BRIDGE_WS_PATH}`; +}; + +const connectBridge = () => { + let socket; + let shouldReconnect = true; + + const closeBridgeForUnload = () => { + shouldReconnect = false; + try { + socket?.close(); + } catch (_error) { + // noop + } + }; + + window.addEventListener('pagehide', closeBridgeForUnload); + window.addEventListener('beforeunload', closeBridgeForUnload); + + const establishConnection = () => { + if (!shouldReconnect) { + return; + } + + try { + socket = new WebSocket(getBridgeUrl()); + } catch (_error) { + if (shouldReconnect) { + setTimeout(establishConnection, 1000); + } + return; + } + + socket.addEventListener('message', async (event) => { + let message; + try { + message = JSON.parse(event.data); + } catch (_error) { + return; + } + + if (message?.type !== 'bridge:request') { + return; + } + + try { + const payload = await handleBridgeRequest( + message.event, + message.payload, + ); + socket.send( + JSON.stringify({ + type: 'bridge:response', + id: message.id, + ok: true, + payload, + }), + ); + } catch (error) { + socket.send( + JSON.stringify({ + type: 'bridge:response', + id: message.id, + ok: false, + error: error?.message || 'Bridge request failed', + }), + ); + } + }); + + socket.addEventListener('close', () => { + if (shouldReconnect) { + setTimeout(establishConnection, 1000); + } + }); + + socket.addEventListener('error', () => { + try { + socket.close(); + } catch (_error) { + // noop + } + }); + }; + + establishConnection(); +}; + +connectBridge(); diff --git a/packages/core/src/select.ts b/packages/core/src/select.ts new file mode 100644 index 0000000..63b25c9 --- /dev/null +++ b/packages/core/src/select.ts @@ -0,0 +1,31 @@ +export { buildSelectionContextForElement } from './core/tools/selection_context.js'; +export { createElementSelector } from './core/tools/selection_selector.js'; +export { + clearSelectableElementCache, + getSelectableElementAtPosition, + isSelectableElement, +} from './core/tools/selection_dom.js'; +export { createSelectionToolkit } from './core/tools/selection_toolkit.js'; +export { + buildSelectionContextSummary, + buildSelectionSourcePreview, +} from './shared/selection_context_format.js'; +export type { + SelectionContext, + SelectionResolvedSource, + SelectionSourceSnippet, + SelectionStackFrame, + ToolkitConfig, + ToolkitOffset, + ToolkitPosition, + ToolkitTuningModalConfig, + ToolkitTuningModalStyle, + ToolkitTuningModalStyleSlot, + ToolkitTuningModalStyleValue, + TuningModalActions, + TuningModalContext, + TuningModalExtension, + TuningModalExtensionCleanup, + TuningModalSlotRenderArgs, + TuningModalWrapArgs, +} from './shared/types.js'; diff --git a/src/shared/const.ts b/packages/core/src/shared/const.ts similarity index 94% rename from src/shared/const.ts rename to packages/core/src/shared/const.ts index 2e6f367..d7fa498 100644 --- a/src/shared/const.ts +++ b/packages/core/src/shared/const.ts @@ -1,4 +1,7 @@ -export const __VITE_REACT_MCP_TOOLS__ = '__VITE_REACT_MCP_TOOLS__'; +export const __AGENTIC_REACT_TOOLS__ = '__AGENTIC_REACT_TOOLS__'; +export const __AGENTIC_REACT__ = '__AGENTIC_REACT__'; +export const __AGENTIC_REACT_CONFIG__ = '__AGENTIC_REACT_CONFIG__'; +export const __AGENTIC_REACT_BRIDGE_URL__ = '__AGENTIC_REACT_BRIDGE_URL__'; export const target = ( typeof window !== 'undefined' diff --git a/packages/core/src/shared/custom_tools_script.ts b/packages/core/src/shared/custom_tools_script.ts new file mode 100644 index 0000000..5e761e5 --- /dev/null +++ b/packages/core/src/shared/custom_tools_script.ts @@ -0,0 +1,108 @@ +import path from 'node:path'; +import type { CustomTool } from './types.js'; + +export type ClientImportSpecifierResolver = ( + filePathOrSpecifier: string, +) => string; + +export const toRelativeImportSpecifier = ( + fromDirectory: string, + filePath: string, +): string => { + const relativePath = path + .relative(fromDirectory, filePath) + .replace(/\\/g, '/'); + return relativePath.startsWith('.') ? relativePath : `./${relativePath}`; +}; + +export const toBundledClientImportSpecifier = ( + rootDir: string, + fromDirectory: string, + filePathOrSpecifier: string, +): string => { + const normalized = filePathOrSpecifier.trim().replace(/\\/g, '/'); + + if ( + normalized.startsWith('http://') || + normalized.startsWith('https://') || + /^[a-zA-Z][a-zA-Z0-9+.-]*:\/\//.test(normalized) + ) { + return normalized; + } + + if (path.isAbsolute(normalized)) { + return toRelativeImportSpecifier(fromDirectory, normalized); + } + + if (normalized.startsWith('./') || normalized.startsWith('../')) { + return toRelativeImportSpecifier( + fromDirectory, + path.resolve(rootDir, normalized), + ); + } + + if (normalized.startsWith('/')) { + return toRelativeImportSpecifier( + fromDirectory, + path.resolve(rootDir, `.${normalized}`), + ); + } + + return normalized; +}; + +export function generateCustomToolsScript( + customTools: CustomTool[], + resolveClientImportSpecifier: ClientImportSpecifierResolver, +): string { + const toolRegistrations = customTools + .map((tool) => { + const safeToolName = JSON.stringify(tool.name); + if (typeof tool.clientFunction === 'function') { + const functionSource = tool.clientFunction.toString(); + if (functionSource.includes('[native code]')) { + return ` + console.error('[agentic-react] Unable to register custom tool', ${safeToolName}, ': native functions are not supported as clientFunction'); + `; + } + return ` + registerTool(${safeToolName}, (${functionSource})); + `; + } + + const importSpecifier = resolveClientImportSpecifier(tool.clientFunction); + const safeSpecifier = JSON.stringify(importSpecifier); + return ` + import(${safeSpecifier}) + .then((module) => { + const handler = module.default ?? module; + registerTool(${safeToolName}, handler); + }) + .catch((error) => { + console.error('[agentic-react] Failed to load custom tool', ${safeToolName}, ':', error); + }); + `; + }) + .join('\n'); + + return ` + const registerTool = (name, handler) => { + const tryRegister = (attempt = 0) => { + const registry = window.__AGENTIC_REACT_TOOLS__; + if (registry?.registerCustomTool) { + registry.registerCustomTool(name, handler); + return; + } + if (attempt >= 40) { + console.warn('[agentic-react] Custom tool registration timed out for', name); + return; + } + setTimeout(() => tryRegister(attempt + 1), 50); + }; + + tryRegister(); + }; + + ${toolRegistrations} + `; +} diff --git a/packages/core/src/shared/default_toolkit_icon.ts b/packages/core/src/shared/default_toolkit_icon.ts new file mode 100644 index 0000000..c539336 --- /dev/null +++ b/packages/core/src/shared/default_toolkit_icon.ts @@ -0,0 +1,2 @@ +export const DEFAULT_TOOLKIT_ICON_DATA_URL: string = + 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAHQAAAB0CAYAAABUmhYnAAAACXBIWXMAAAsTAAALEwEAmpwYAAAgAElEQVR42u19B3hUZfb+3O/eACISJjOTSQIhkMzcmQmgUgQUSEhPqCr2gh3FgthYu67rrn0t2Na67tq72MEudgEpunZZ3b9df66CIknm+7/nfPfO3JlMS7Eu8zz3SZl2732/c75z3tNcrk2PTY9Nj02PTY9Nj049NOsQLtdpOHbWXdXVRsaDXsOvjb130+MXBhBgAJidCZgeesTAdolNIP8cIKqbnRLAsrLqPgMDzYOKzLpt/MH6qT6zZm9fsPZwX6Bhgb+8/g++ITgCtQuKwnWH+oI1s73hSVP9wdqxweDUga7KnXul/kosFrVgxKbb3yOP05QUJt1Q/5YNm3srake5g1VzvMHaywqDTY8DuA8LAnXrvIGGNl+wUfIRapaFoSnSb06RhUH8brbg72bpM5vwfL30BerbvcHGdfj5vjdYt9hTUXWhN1C1f6FZu2Xx6NF9O54Lg7tJcjv/iO1v9kP4KsYHPIH6uZ5A7e3eipp/eyvqpJdAM22AFIjeAB317QC4Db9vBMAbfRX1G718NNDR6g3WE+jt/B6zUb0/9jkNBHQU0vwBpPwWgHugPzRuSCKQHc5v0yM9kPEbVxSpKfNV1BwBgJ70VNR9TwB6g01RT6BBAtwo/tfmAVAF5TVt7iHV7fllk6L9Bm0b3bxkbLRv8RjZp2iU7F04EsfWsaOPf6TczD9K9i3ZJtpv4Lho/8ETogOGVLW7h1bjs+qwAAjsBkhzE6S5JVoIoCG966ARlngCkw7xhLYrcS4065w3PdIBOXr06DxfoKYRqvROSNW3CsRGSAxABIAF5bWtBMAWpdtFNyseLXv5tpR6QaUU7rDUBpjSRUe+fQSTfk88ND5Mfp9eEJF5nhFYBCPlFoPGRweUVbVjsbRaKjzqw0KyVPXXnkD1De7yiRMci0/bBGxsX1Kqq7KysldhoG5Pb6D2FS+pvGATqc4oSYynoraNpK9v8TYyzzuCwVPAWcAMIFBC6iBg7WNAOP7/tIcZ/+kEG/8zPMNYykmKsZhaAWwbJDdaqFQzgK57qjBUM8NhqIn/VQPKqaqEL9Q4C/vWSgVig6VKa9sgiaQ+GUS6wS5LmrKDFMoRyEyfZ0t2gKWYwO1bMkYOGDKpHefYir23ndQxgYtFuLQgWF2fbuv4H5BKtYoLhkweB/CeIYMEBkzUS0CW17b1L90O+91WcTWa31MgpgLTzAiuSAA4gHMy+dxwjrzwSFJZYk0yxibf5zGrww5Qxe+cDFBSOXjERLc3ULfQU9GIPYpVKxkk7f1LJ/CeyNKYcHPDPQxoKA2QZobnSJ1bB/8vCM0xXNJeTgsRGoaBxe/rCiomn+gKNPf+HUtrfK/0VUxu8gQb3vVCKmmFE5j5gycqIC3jJXdAMklcdyU1w2fZwLJRFZC9sCXkY5/FtbTCeGtnaQ3Wvwzp3fp3CGrc6PEOrT3PA9UK65HBhKsQJaND3Ryzh9Vnpr3S7ORhqV93KIVKDscMM3KJCoZOJoucDCi4VnXrvUMmz0ukK3/77ojLH2oagj3yWQ/UK7kC8CHb4C/iBkW6uT+a3T/yc/l/budDwOq4pn4Dx0uyBbyshqGJArW3ustH5zvvyW/wwZSdy1M+uQ6r9WNPsJnBLBhaEyVnv2cMne4ClfR3vuNv5+85nYP9d5CNp97+rUkLsbR6g0SE1K30DKkJOe/Nb8j4USfsq2icDRX7o0fRca35ZROlDvO/+2BmASgTmJmeTwt+roCayiq2DCdydcg+8BJBARVcEGj4FITJpN+SpMYs2UKz4WjQdCSV7WQFkhpS1qrZs1KZnwHUDtIXTAGSmca6DeWoctMbUKSCBf63BbYXb2xfrf9uQMWkmb8RUNUJessbTlZWLNyRQF078ao960vmqGpTgZoSpK5YyJ07XyJIaE+F9Rv1BBt/hLTu/CtXvxaYFfUneq39kvYQsvwygunuhqrNploz7aXppD0ruOlBF0kulYhZx2HWDpthYVMkCD541Gs2bSwI1u7wK5VUywDi8Bb2inKm7qIU3bD9SrooIsATwVScK5n+6jnzp7dqu+TCZKYN9YJwWrDpOdvdoXuxWdFoqXxVbEfBhvUFEZsy/NWAqk7EF6ibhX2CIhTtJJlOMFkK+8P66zVYujYfgqMcx9D40RdH77Ic1Z358xz5uQIKMqRXKa6hLPF67N9xzXTtsX21fwCSug2R+4qAMJs+c5dXD/+VgKpIA39w8jiY5v+1CIP23og3OiXT1d+UfQuHyYsuvVauXLVGvrpshVy+/DX5Kg76+cory+Xsg47lG6H420wqrickLguQ+ZnUrxmzZknyCLS5R54kl+GaluFali1XP199dYV8beVqeeY5l8i8AWSMqYXNahj3pi/2VIrecFA+2LCm2Kz2OgiIXyxRy+Urqy6Cmn2L45bY9PsWjUmg7wwPyANjoNx1n8MkPR5/8hl53/0Py/sfWiwffOQxede9D8o333xbfv75F7KwfKx0bQHL0FOpbhxJNt6vWSQ9/yTVbPOp9LutxmPPWa8tCMefi702HGekUn6O/VxYHbHvDDteq/7WaaFuXiEHD5sgN/zwPRbq6/Lu+x6UDz68RD6A474HHpVPPvUsX3Pd9D1xDwbxvYjvrbB+S7clSW31MflQd/evA1Cz6W4vcnXIz6QTTJYWA+C4RIk84phT5Pfrv5MDh4yWkGkp+pRJvQ9UsGuA3H/O0XLjhg2y1ByHv31SiCKpAVitX4XU8LeWN0h9Xi/8xHu1vkOgxhDW0oulRq8lkCDdmqsQrylVIBglUtP86jPotVqhej0tNlLv9Ln4fv5c+j89vwVe168cv+Mz8wYqEHuX8ucK+nx6LX0fntdJOkWxjITHMmjN0/fia9E3GyJF78F8HRWhcfzcjnsczPfA4IUaSqAS88uq+N5xxMlsOOUXUr3qC6Fij/GqQDTFLi3pSNxvGFCtRB561Elyw/frZNmwydLVL4BIRSUI+eHSBbAOnneCbP1xgxwU2la69jhW9jr2r1IMGiO1wHZSP2GhFDsdLrXNsACm7i/1U66Q+uhpUniHSf3wv0gDh0DGgjaySeadfLnUp+zPoOq7zpf68ZdKYU6UomSk1I86T+oHnsKgiYk74XOulPrk3RlAfZ8FUj/mAqkNHid1vN7Ad+p7HgcQy6Vo2Vfqp/4N3zlVLdBD/yiNI/7CRpwrUiMj517PoE3f+UDp0gfxNeXh3Fx4b2hULT+3/a4HpQSUMyXwP3c5+N9gQxRarpWYNed29rPtm0WhljHgZpFlVw+rtiaqe4andE8SAP1hvRxcOQkXW8Gcpy29Bx3+BwXo8Frp+uMNstcNT0pRAWnfqkkat78ixbEXMqDGYbiRdy2Xeu0eAGm01C97QBpX3A9wcaPqZ0vjbjw393SWTv0kgHDrC1IfM0WKsnHS+OezUpx7sxTk7M+aJ/V7V0qx65EAdKjU/3Kj1G94CgtoAl4/Xeo3PyeN065iLSIO+qPU8bmiYW+oWlzPZYukceWDUvcBtJrZ0rznJQZt2k4HxEAjy9a12VAZ3HoyP7fDbnP4HqSSUAK1VyHRhLXtRBF6zcY17tH1+T+X+tVsN8UXaHzGY0lnH4cRlBzaigE6/yT5IwAdMpwkNIiQ0zAEiofz3jLniOPlRgA60ISEujxSJ/VHapJVrhfqr0RJf6+BSlUCXLYc6XV0YCEJUsN4b+y1epF6L6lQei29T/jVufUpVc+ROmWV64+pXDJy+DmjWL2WvxOf27dMvZZeBxXMeyhUbig4hkGbQVKYV8rXRNdGWig00pLQ3ecw2HkdJNQyGnHviHjgKE2ohVTv+T+T6rVUbbDxGEW217UhwJvRzTC8SgoPxx66HntoSelWDJqAOa9D3bpcW8j99j9S/rjhB1kamcA3glZ/zN2hm2AbN/ST/rZVOxkZnkjcgEowiqznbGPG4zB86DWeFK+1F2LyawsqE19bEJfCyDb1DFrLlD1wLZtLHepeAFiXyy0rAqOVhO5BElocM4pSkhI4TzeH3iiPCummkYaqn1r18gd7h9aZsGq/xJcqVVtQmRlQklCjVO627xF8cQ8/+oS84+775V233invxs+bb7tHvv7GW/Lzzz6XvqGwkCGVwh3uZm6QmYWnTeFv5ufiDjkIA5Is7JNllROwGDfIl15eJm+54155zz0PyDtxbbfdeZ9c8tiTfM31ZDDpA6VRkB5Q+n4K8hPvTcng2E+fxe02fkLVq1YK4prX+8iqxQa+WZKLknrlxf3QhVf8HeC9KVetXiNXbtwoV639t1wFX235itfghx6DFQ8LsSAHMG03gxZTQbjrZEQyiG7nZ4ezfpbyQ8vl3Pkny5WrX5crcR2vfbBWrtzwI7sxa954Q5557kIASWo1wz2y91PcS9J4CGy0Kau36cCfSPUqMIvNlgleyxmmjLycpchmiuAuuLDHuTaDqn3oLenabmeopgL8f5BiVLKBYqdnQtWxK0MHFoFSk5UprezMUhpK9HUBDrs98Ju13oPjiydTCgylzdB19SE121e6YJy57lyOv8GIYR9nBqy/mWXRx7MhKOTmqahhahDB8bcHj5jq/imkVPmcgYZ7STop0y2B2stIVMfjhGTqUzhJ94+QxmPvSQNug8CFC1yEnk3N0o0lwwfGib5VoxR1sHRrdsHv9bBcLZ+UALG3gPwslJ3b2lfJYIJVLEBJitAEKSbtJEX9HlIfNx3nGlTAJoBqpuRy6RoEFoIx8zBp3LvC4nAjvG+KHAIQIsFA2ga1OBAcqsuJNC3oYSm16L2KuslUhqCkc1K0q6EkQTcQRo/++PtSVO+mLM1Me4sNJrkQw2qkft1jUn/9Ryk+kdLAIej3RSulPg/+aHA7BSx9B70nP02YzF48eK0oQub9HsdIcSPcmhXfSeM/+MzPpNTfa4e7skxqE3ZUUutknVKdIy0OSKQ+/RCp37fSovjMNFojE7Amu3TkxhRyvU3je4Mqxxf0oJQqQFHncReZ1JSuSNKZGKU3kyQylD6IjJstfLASH39PCnLsO0hAqGNEhiQviGyHp9ZK8Slu9kNvSv2sW6Rx3u1Su2elNN6LSvE1gFj2lRRHny+FfzjUcamDznOcC6lm2qvJ/9zrWCke+0CKL/Def0upPfq+NC6+Vxp/uUmKO16V4mP8f9U3UoydoVR7pv3aAlRYgMa0hLuzi55sDiWlAJP30uJIw7weklLbEKobiQ16A1lfKAqK5m6EpHiOJbSSJVSv3j27hNJzoOH0826R+ucA8+xblAqj92Hf4t9HNktx+vXSWP2d1AGs/uibSiXrJSpLIua6VCrSAQSGfv3TUockivc2Sg0giqpdsNCGs1oX+GyxOTTCcRdLQd95w2PK980QOBBOCV20Mm50uUNdCJrHpdRPfmmwfiVlTfaAlFqhsWDTXwvDU7FJN7Ypy9bMMbKRGlCWUOyhojqLhNINJH516DgpXvxK6jjEwFFKWuC8xwh03Gzav7ThdVJc9rCSrHehio88VwqbhKDvAFlgzDpc6su+kNqXeM3NL4AGnKUWB51HvvIziVZkQ4ay+Ba/LcW/NkqBRcB8sTtNQnaSyk2U0K5lEPZD0VQhlT0SeR+qn9FdKeWVMKiysQAf+G+STloxiT5iNj8u9R5KVJ1YkgOgZODAkiVp0z+CSvzr3YpQT2ZcbGKAgMbzgnjYlf8nDahS/cI7IcX4TjA++pEXSH0t1PM7G6Q4AmATG2V/f9I58DnSc6ddLXXs1fqswxThn7CXOo4CC9AZFqCxXONUmfm5SSlxwlT9RkXLXrP+jm4SDVayV7hpd5bOUGN7v0HjkvjaXFJCUkgobpa+xFK5vTKoXLIQIVVih0Ol+AYStWChovQ8lanVfIHl1iAiQvueseRtqX8LMK4C93r6tayyxQufwIqdrYynDm6J45zxHfTd2twz+DPEfqeoKEzCdzuuuyBJQsmKjgW2zRy2o9T/o6q7QgpNmo1fo3J9qJPk6ZoxFJ5yR2FoKrsqed4tHZZjrumQSc64vYcuyWEPpZuHm2TsfKS6qUcjCkN8bOymprk5RNbTPjh0vNRvfE7qMHjo0Ba9LgUIf/YzU0Q+OgBKC2POqdJYj+8+4HQVjkv+7vwklWtL6IBkQM3OF0lZKSuFbByBNw/WHNRVtauqw9CEwhds/pKNIfCMMT4z+SbkdyKPhy6SJPSx9y2VW5reerQA1WceIg0Aqp14mZIsOwCeSWVZEibORiTlQwWouP5x9je1AZEU1mdHa5gl9LAz+buN/U9SsVBvZepF4NxD733NcV9COSz0DMYRth0YpVC78DDMhge7KKFWCYPZuCvp76JwC9TteDan0xbL5nSELUAjbBTpWfdQOOcIaovaPaWAf6hfbO2h7nBm9oYNINzcky5nl0R/YLUKw8EQ0hfei712cNzoSqMOBX0GGCz9zOuUNbz9XJxLaQrtkLiHCieg/YM5pI1mMY7wmv6Dt+PWAL5Q0xclwyaVdgFUy7oNNV3jD08jCqq1F2oitXyzC3k7STfbltAlDkDdkdQ3lgClPCMQCuINIhBWK6s3PwuYoBf1A0+TGgwp8dzHUkRqET8dJfVHQDcSGQFJF1n8XwJUgJUSNz4jjbV4z7iZli8aSS1pDkBFSgnNlVjoaHMwc2Q2tXMdaqhuVmfVrmb3/UEbmNf9MIgoWVq4I53Pj01nFBFTlHYPNTtEIajIVhARsXK91CrGqzipU0rdijZjPxJqUm/YS+Z9EAWjtF6KCbNU+sgWeE8lFsZLn0iDJA6ZEZpAbLNwhMphSvi8MO/1+sCRUjyDBbEM5MKg0WoxpeOLY4AeHN9DU1Gj7o7MUGoJjv+d5xtBvR3a/GG05wk3Xa7wzBlQKyMhWB8Bb9taBECJiO8YwDY7aRgl+aGPO/fQSHq1RFJKBs4FtytGBykhnFtE4Nk3FzFUDaEszv0JV0EqYcn+q03qLfvhf/kqoM1B7XypTwBX++o3Ulu1Turjd+BgtiDmiNgom6qDu8D/q9pJGh9Jlc2QvIg6nGeEA+VixsFSpDSKco0CpSLuw2TDtPnZ22h6KbFZR1Zta7krocY9iiLTeP+kaHoc0O4lNosEYsHict3h1D5bvmJ3BFSo2At86/8B0JOvBAgDVBIX7YX9AUQZ1On46TKvdndOH9E+kHxTjYORPnI82J6TLsV+ijDWqcg5mvsnqd28VBlKD2Fvnb6/NGp2g1qvxnlFFEmPzxWuflIceqbU8Z2Uj8Q+qGdY+oUXs3IJ0NfitTRZ98xsgKp9vv/giVEyTtFY5NuSis7so6r9GhUbnceAhlpaEyMrKfJXO1HR1QFQkraYcZJ0YQVWZgHAE4PQc2H5d1Jb/C4SwWaDJDhH6pcukvrDoPle+T8p1vwo9XegZt+XDJaGvVL7Siq68DP108ChkZQTsQ82SQfwvD++FZXaivVSX/ofqd/0DHzWq6S+M4yge16T4m28busGlZbitjMmUhiFMSv3YIeVG8wBtMzuHsdciTUaOE76Q83tBGpJpKlT2faaRcY/WBSZzv6nkVACmAbQFGpYWEcHct6bAlDn59i5sKRqydIMgZg/4CQpln4GVQqm5702qf0XQJCUvd4mjSc+lGLxB1J7C4Bhz9NPgHsz72yp7XeiFLsfJcUuRyIhbL5KCtsThPyhf5b6/LPBVr2Lz8J7HsYiWbRGGiu+B4sUZanU/h/Axndpy/G/U5FpOGlHK0xXomjBZMaoA/XnULkpF3xnCqWCqA7fWvoRUivCPlpU2XCUU/iyJ4GhmSEc2beLIlO5HDDRIAp1Yf90nCjRbRZTJKpsQJMSoilGSnHPyYh3XnyP1Fd/r1yO99ukeBNAPvFvKfY9keOWwpzEITBx+8tSBwhit6MhTf3VjSeflYPVpYqRogQy+pvUJ+X/NO+nCAdEb0TZNrCEt5Zi2xlS3xE0310rwAcD1NdBE34ZZakXd70q9dnHqWgOPkc4c41iKheSfe/KDBKahtzPlMlANCCECol5rcXApNisv7JTgA4K1g5E7PO7YmzClDfkyjd7oOGEmcDl6o/ae+ggR/Z7RAWbt2qQ+pUPQSW2S40sUpK6U6+WgnJun/sURP3nAFEFlCkjz5iPnFsAri1cpCxadCdh8r5whCIRmOMtU34wLEa++fjJfuUZ1zKtaOAnJ2oTF4x92YBqFyuxkJr2kWKfE6R2B4LWa9tkHpH6D/8LdOQhaqGQFNqJZGTlOpmiTqncND0c3MoSNohgQL4RGal+s+GRHI0iK5gdaRgHXd1OEjpgyMRox8wEs5OUVop46GPktuwW43K5Og2WqA7J01f9l4PM4oE34F4cJzVIjspgL0CY7GreGwUMHk7NBPhixbdQl99KLTJZZTWw86+y58UgJFkjSK1X7QwpHKNSOfNDylWhlM3Bo6XxwsdSe7NNBbNpr9ztSEVIQDuoLPpiVrd6DTIsLntIio/apSBtABZKeMLKJaIFFJPQ1+IB7qw8rtlRKlP8Ttke7vKadgK0KNS8IrdwmmXhlgxrmM5vDE9pR/VxNH2qSaq9M8veQIAid5WIBY0AtaInzAjNP1fdqDVwKeadp2g2Up3k/5GbQgCMauF9zkDMU9usVBoX3aXcmcPOgjU8SL2HfFfcZOPwP7N61uHCGO9A2p/+CN9xDpcvsmSRpJKU77VAahQ4v+EJLDCAd/sLzExxTJX2cZJqkkIyzmjRTZ8jNdgAHIL720NKigqsQAKMIuOBVYpp6m/mkN+UmlAQKSxd6hpDhipipB/nlmtk6WR/ZdO+ZBDBZWkj6yqjhHYwjrIEvPl1AWmAtTEm76UkCqyN2PFQNnLEi18gV2gPRYTnJyV/kaFEN/SqhznDQJyBcoY130Pa31VqloCk3B4KfFOpA1myb0GaXsexBvsvfmer90/Xs6oXdsHSFsheuOdVZSGfgYz5N/H6O15RgYQO2YaVam8uR4wWuUPkSukLLmRbwAXA86Bye923jDPytfwuJgGkULtkGOWDDyByAeHMH0pDM0pyBrQw3HyUH4AWR6a1og2pAtQdyrEEL8MFMCBIsEZNSK+XEbAmkoCKk/wA4UFEQuBG6FMOVAVJpMKSHXmSAlK9KEsw3iWwWqWA20E5QaxeIcVcBFW/NwwaGDKrNwJwHG+0qeN19Tc9x6UNpO4pqZskb9qBbPEKfKZOrsysIxxRmaSbT+eG89BHIEFtObaHZV8jwWwiExcGjLK8p5EqA8A5O9Ed6aGy/iA3kaQMBqIBy7eeGsweH7UlNNxygj8yA4BO3UjhGy0lKZ/cJSTzimNLmaQRe56xFEYOrFU2cJAJICiLjyTuhqXqNbEM+I60GVN8VGB03eNSJ7V4y4sq9mhHSCAp+hnXKOJg9Y9xMP9lHat+5GC5/qfr4gFrWmikzq9erFTtba84gAyn1E6U1cAq9izUy9CejgUitgTAtFCx2PKefF9q5gT1uVkSxURWfjdslx8qQOGLVoSnjs4Z0JJhU04iQLGPxgAVnW7XlkyNWYzPiZdIjcBbtp4dfP3YC9S+SG7J+Xcq14KAyUSa03579s1MHogrH1Z1Le6IJcF47tK7mTDQiWxgINvhU7ar35ElyK7KxXfxHsy8Lb2P1PSFd6m99O9PWEBksBkIUDKCjr9M6t/jPJpmQ81frrIbyHelc6PCKS1dQL4TmYDuOKDkhxKgpeUtYzoD6MlFANRvAaras6RbTTk6zJxOAtW2x1F80QLSo9GeuTv8unKkX0IdUtYdq0DOAUrRAscdYfDIrRGvfQvjSLKBJBr3VknXpArxUz/tb0pCIY0GGUROQFdtUBJ66pVKpXqtPRe8rU65Q0QsENnQuI/6zIJI6q2D9uv+sG6RVM1qH4FzcdBpKp8J18LG3Yw56jNyAjSDgRTLqncCWp87oIWRlhOLKuMS6rIkVKQxr3Pu9ZOv0j2N4y4BoDBSTr1WRVLI97viAUXVnXyFZRCZHW+ElbUn/ni1IhHAyZLzr9/yjKoOI0kjUn3b7WHYKOk01thqt1VJ7BtkHG3khGr2TZmIB6DXLea9kwwujRbKNY92yPTja7bOibIZ2P8liUaIjavccC36+bfzItJR+siEflYNlp54EB0kdLuuAeqLtCwoqpxJRtFG7jHEe2i4g4+UW8TATEhwZsux72CZB8vUmLCziodugZsxvF7qL33GlJt28mXs91GFGu29rBLJxaBCpvLx4G6/ktpLX3GKibhpKefqUpSDc3HZyEEIDfm5lIitk3GE/Vp/M6q4XrJ8KTept4rYkKsjEJXhAPo9K1QeEwLibBhtM025Vfb3c9BbEQ/6Hxbyvq8vR2gNNaUEqAsg5005AFbucrV1dCrR2kxbIhEn6CfYgEaHjGkK5QxoUWXTwUpCp7WqSEumhOFsWX9mhwA3UXV5lOBsh8/YasSNnQiAX/xM7ad3vAwpmsXqUCNgKdJBRgyqsDmpmowaRF1E8/4cXdEfAXtDzFB/FbMU1KuBrN/7X5c64qgGDvHwW1Lf+w8wqhS7Q8W/OogBg6IjpKKRjEaWqjjoFOVjnnG9soRpMRPwkGQiKIxbX1IM1stwsWr3sqxlLA7K+d3+MEX9eSJdTLROL6GUMEaAgqRvLRnWmEPExSIWUE+xiyIWWohYYEBFl1ucpsgpQiWaeAS+46Td4ikoHsswGYYk6BuXcoKz9u5GaVz1qNRBv2nuChXWun8NWB08t80M9Vlk2Cy8i/N+BIgEZoK8FhGANBSSLN5zt6xV+ba0b1rBcEGlEHNOkXnkS179EAyhMmVFl41FDvDnKhN/IPjdAqhSuDX6VUukAJ/MxtiNz+MzGyymqzIp629VPODg7jqQCUZRPhELVVFi7/yhxq+Ltm725UD/KUCLUGyKVSCLIlN4VXSlRiMt4M4UlMlJGQu0PxEXCykTiIroFCr7VEU+yE81Lrmb9zf9thfVvkcxUQpQV6AXA6lgIhlGTYWlq/Y+di3Iuqb9jUIUDDcAABYKSURBVPe4iBXTDLPU68PqkHQNP/LVb+EbT2ZeWPSxJJGsYEit8c+nIdnvslrXyOB58C2WfM7pJT/T3ufthDZQf8YiR15ujvctFVOUbLcUMPXHEvoODSPKhc9l8R0YqQpiKtEPRDNRth/VNgp3D7UJt4qVhJWC0iG3x84AJBeHohpIcBY3IEb51ga2JjVifeDMU/8E8efrpDjwZGlst70ylCjmeeNTAJHyjipUTlDvgQySylrAAug3RAWyfeBH0SuB3QuUWRgNe3KMVVxwK/bS12ANr4Nh1aYMpGWIw14O92jmwZw+w8H15JxeR7SFJTS2DYWzuHSpjMxEH5Qr97BgaLACac6ScPPSjm0SMpUNVlb3Q/PFT0i8adQGGybdVBsdJfT9pKy/pL2WVjztt+TqEDDjYKC88KXMW/0D0lfWqqwEClyTZUz73dutyt0ACPpi0Ir3r1b78E3PImi9lI0n7ZbnELRewdES/QmE715X72GG6msVCDfIwHobLs59SEh7DkbaKljENbuqBG9yQVJZ3za4sbzcdOR8Jyk/S93ST+pvj+IlhM8oi6ThtlwD3DG00RDjWRhF3O3K8AzvRgvx1OEzY0lSGmd+mqb9tNeRWq7CTSXLk0JkFOEYNYXb3Qi0wNGQb6Tf/DxUHfbXVevZQtUAlgvgMgP1gTqIbNDgvmjki75HTNIPIOLB2V6zGGE0SPuc0xVBAFXM/u6xFynif/YJKkhAAYJ0C9WuPpthp3GGUmT9daIGKKlTCmWNIJ+ojffQcOOfc42HxgwjT6jxGiLo/aEpnMIZa/WWoutkpkLflOEzktDF72fJKbI+j6SB1O8fLmEpor2V45a0J5LqgxHEDBT5s0QJUh4RuT6PoAwCqZdiBGjFkdaBrD8dRpd29wp2UwzqTUTAgRViA4msaDJyyH+kz67dTRlAlz+o8peS63pSZv0dolrmJBcr5XehsMtRuEScOgBtL2Tqr36P3FNQYgR94/xijrhMaeubkCSWa+QgQ/iMAX0vMXM+DbnPKgd+KksS8nu00VNUwNrujkI/bWefDiILzr+VVXEeygQprZOlDWALtKQxzrhB6l9YIG1uRUScn2MXPpFhNngMamHgmiz9hEN+HMbLUh+qdwA03OlZMR17VAStJDEm5tH+p3bLThQuWUnWZvVEIoKLYelSxbYrWyQla8GvKmYVVgoKGUWiwx5qpgATxs2QsVKDJSqe/Ejd9Pw0ozfocwj84q2l8eAa5mW1oy/gwDnzvyhnIGrOwP6pDxmvAM2UtU8L4ZYXmI0SWzc5Eq3Tl22whN6TawV35lEiyiAKqzROWLiFFDoLNL4XCIzt35laUX5Rf5SBF4ZbPimpnE61FVGR9cRSqVuzI2VoZSzEAS1Nf9FMmiMctt2O6qZevURRfBnOhVQ0p6GQan3+EwaQ28ShnEJ7G7zvqu+kGL+9WkgZOFb+HDo3kAvMy047KA236yh9JLXNmfPOgt9wl0kFuyMMGUTUrITzcsNNi7pQVmglW0emLlJqt7mtt6MUQnTSEEpZwb3Yzvor7cCXJtxUpuYOUJXUxNzklaTNtI/5bl4VCCBCQqckL1R2c8wSHKvYaV6a6rNUxU74LsoQJOKB8opE6siJSLBy51qJ1iHZXf/dHoNCUxgR0mzzU4JYeMrxna9As/ZRrxXojmUu9A+mX00ZfCmnyo3lFAFQvSpz0ww2iHSqup7HmQHa0ReqTp2eSMZFxOmj/q04+0/MQ2rK+5KD1/pZ/8T/3CqlJNsNJUBJhQJI8R3eP+dM1RYuzUJgvhnnyn5oD0hoQpJ12URkKtC04iaJAuyxXZZQd3nVCJRDbCR2omCo3VvBTAtoVh/UmfW3OIsfGrupquDXoJt63EVJN7WjW8BkNlF8qFvRq3fhfVcR9CAkXvkGpYkHqxrTdEF053dTfeg+x1v1oada3z0s9T0osM41OUmsC4A6hYOSw6hPPaWeIOl9TXV1dZ+u9FqIvRjpnM/6eY51UxvN+Mrmj4pM8zktYiFu5e6WWMGdn6JGk1Ru074qCxBdM9mdSGhZ48xer+TELMokMKj8b/lXiiig8vt9wChR/BWMk061nlT7Se5JOmm3AukaEtcMuEvGnsekVbn2HpooobgXMUCzV3CLVJouX43fQnSljTSlL9zcjaaOlj/qxvwVKikEf9i2BdWI5jxsLpSxx4IqJ9wts1HEaSvwEdEMQ1sNIO5/Q92kWG1nKLFpIyU/U/TkeGQOrAXbQ40z9j9Vcbu0b+4M4+iNdVKjNJNzkPFQvJWjNL8y3uWamztWcvaDgUIlVtnbzVLx02zFyTMUOZ/ZbcnSVSwhwkKJYeR7wuOobJ7YjcYZ1jTBYTUVMJf/y+1Vslq7oYzNCePUX0RRf5OzWLk2cOw+PKvyfRBv5Han9DnU9ZosWgISr6EyQoppMrvzPPKWYEzF2q16VBK3Ngn5uVSGQVTf4+8g2xBccX65StrmwEBQSTm1UB0/kzMADSpl9FZauVXh9FaxDWhCfWgnAY0dqhs4DRvwg+6Dx/Hyad0fhmeBGm65TcVHW9rI4ur6SEhn0wyHhCatepHMkZIPOfUgpv70Fz7DTZuDBGqEtSBhAh2y9V2OQE7tM6D52hXolz/EJRJcZmEnXdscLOXZlqJ58jm3gexXoTCD9rx9YcWOqOXEbAqZ6ahkMx59R/HF+5+icn4zFgjHVW5ionXmzHmRju6jFnEDx1IObhtryMopR/RA8ylL7VbWNZNTC5M5ihBON8JnpHJNVfC7+L2k1nDpc2pUvctgxa0SYMhczwODoz+BarE1G5RE0v/vgaqbOZepvIS+f87zsDIfuLExJVKjqQZHcYjkp5yipz+WxrNYNLQ4KL3k3FsVGZLl+pKtXOF2UH8ZBuOJdPsotEoBZt+Q74kObp+VhupLerKJo+YNT1lKZD0sLYzxGJkgpSKjb5rYjlSnRhAANG+Jat5IgwT0DFl+CfsKqV44+Po1j7P1Kp7/XN28P/0DSV2z1U1kSQrHy/xTlSO4rTAdST4SogXUMNXO6LejHdwzn0idqrb/8SxXeRNTxRnwmcgMui7SOlDpBhp85C1axc0bdUdwOpcIS7yBI9rRIvWHeHRSt0WVzRf3YAPHGFm/ZyEBivKI3KU0aZgqtSJFUJgn0T/yjnRN2JU7PVMXaxu0zG1aVZtyJtMRK6VUFg3ZBLxPxkJbkRR7dxqrmzVDWMVkifNl/xWfi26hnOdkT6PI0KTD7nNIPjp8Aumagu7V6ItPI0DoWp0GjpZD/m2CdKoc3O/84YbhPdnd2hbxXmj3+XKRBSpP5nWkpoh0Lkt+vFFwX1igReWjsOKqZMmTa2XR9P2kv6RS+gOYxgBQXf1zGJVl97Zl4yUYBzHl/pYpvTSU2PTYTqy2Pzdjr9w430qTkgwQ//4AJKq4UpbMPlEW378SM2hGyuLAWNmHmh+T+s+h559wjNMir4LVbbjlyp+gCbI1qA65RhZzROOwMpAJTjWrBtbQgIDnX3iJhwX88P16uSEald9jgM0G+h0/L7jkWgY0J0l1zE3LaCHndyLXydkQOWOBUShhmA5lJ17791swIGGd/GE9jo0/yh/a23GN62Rb60a5eMlTsqAUQxZovEgOHoJQTRuVdIZb/ls0rD7yk/ae94aalygpbW7b3K57ySAZ9iiPAw87jvuvn3bmBfKwo0+W8489Vc5f8EdMhDgBE4kexoCBdQB9PE9W0rkhhqWqiJkpqIzzpQlDBByhM7dqQmxLFVudsfJ50zGMwIz5mbFk7tjnmikGF4RTficPEcC4ksDIGr6u6/55pzwI0y3mLzhNzj/udDn3qJPln846X8102YXmtgxMMbelY+ewzan0PtzMvC1qjM79CadDqBUyABNqfSY2axPNkM2GKJXra/lmWrqPR2VhzAeB+P26b+XAwaPV9KQ8mgpBbb37y/0PPIqnQgwiQHnMh08REDzmo0DVkLitEgjyQbnDZoB9Ra4RpZwnnqxUoKxXtxVsptdubrW0od95zEdIWczE6VLqKO1bFCSnulCKd5J6pN+JxnOraU6CPpcSuMn9oXPDdxpEnGP/j1hjPlqm7sXXEp8KUSDLK+ypEAenGcTj3Kpwr1RUpZ2YOVi2a5EIVvgTz29RoHrMpoV+q6EGjSSmlSXStHyxAaVBPKSGhgyvZtXay0dzW0bwrBOerARVNSiMMR97LpC9FqBzSSnK9YIYjHPSZdwjgW4o+Z/G6ShEohROSLF+xFnSmHeOyu4b1SwNKiGkMBdVhiEzTz8BjFGoiluyCoqLHnS6WijojcsV4VTAS4Fv9GLQ0RtXK0PitlmFyUqoV9n7eGuyEsr20ZFT4DsJYINaxVHDDtI8CNGFz/2Hmtuyiz23ZQRfG20z4TENjrktA9MDalVpDxhazXsnNcv0R5oP+Dlmt/BK6YdJeqCi3vajXRmVtm1WrKZDOEvKhXOYHVZyfLJSFV8sXRyPlML4izk0WQmAllqTlfJoslI5Jitt2QhXAknNNFmJ3Ju5aDND/dzr0SqueKTKL7p8kWJxkPCs3w234+DTFHN00pVS0GQljLiiyUri70jHPAedqmkv2/FwjlmKneexRIoz/4nnn+TJSoLGcN30HNe+cIkG5RnhO7WG2ayO9cvuk/rf7MlKe2Oy0ovxUVk0bAfXxNeMxWBa6niHTIC67WmF2wDMJivm2bLoZxxsp1bMgHDdNJ9Vq4hEsmieXXCbtPry6OJEfPbZoAgA7T1ERRLIYHIVywPmLnBMVgLlRgQ8D7MLqKlHVLYwQHVFYVK9r1K5VPLAdB7t46Ra+bUWlUiuDGXq2ZOV6H12eQLlB9Fzds8/bqZRrFQzvZ5+J9VNn9vHcmno87nMv1h1K6Nzx8IJWcPspu4EQF1F/H9Sza5eZTK4pRqVNXO3OWlVLn0HNcTAmCzuQ4R7+lVJqCbUjTaq3fBNzeaLC0PTyFfCeOZatc8ltzVjQIvlETCCSEKHYjwjtw1F9nxf+Hw04uOQecdDQjeoyUqQXt3jKPZ1zlApSD36MWadxqxeM8kCdrTKSWUhuyOO8ZKm+tvtMKDckcTzsaYVk59ZOUZNVpq5y4E8wqQvipfp2lxYGJGRVfzcrD0P6QCos8wBuc8kGKxqveHGg36JCYVKFfi33Bz5uy/RJo79tC2/bGIHR56mCdFMlqbtZ/PFffLJJ/LDDz+SH330H/75/vsfyB/gtvwLM0S3KBmhKt3c4U7kLOU69T7b67NPU+qQvAUN4ivfRn766Wfy2/9+Iz/AEJ6PPvoI1/WhXLt2LU+Lam9rk2Mn78ASa4+sZmYp3iEMocmWjXQP0b3tHz/zZMKOWfbuipphoAU/x0lxp6vNS8aldGVoxe4y+zC58Mrr5YULr8a036vlhZdeIy+67Bp53kVXypHbTVPMStZJSWZuBcZZsxPNDH0Lc+tCzaDCut62dpb8q3UtF/HPa/naLrniOuytBzCxEOv15I4PWVfkezO3IIdNsnzw4InuX3gorK1662aiFSs3vEf35WhykbBNa7nYnPc7jqLYofoRdL30Lvfn0xPkoiuf51ZD7dR1FCsq02UfftZOwqHelfYJcp4Qt0zlaYTUZLp+xC8pnc4EJM4/KjAbjuLe9JiJhhON2iMo2eG30vlJ/SorsDJ25LG1C0c9ReaA6Caw3UpmyzFFxO4hRNdA16aOypgVb9gTEq20TLon1OatkAQA9wmAbvSGm6b+SoaqJ00uDDWdrcaBNCeCmjWJLPebmLvq7TqYoluvcza6sBdzOKZmKXvSx5pMSWdJpHl/p2D8Wh5aHNTGyzggGyRQG0E0j0rDJMUnCWmWBHcZlHwzC/g9L6WZABXOji1uNQNNsyST3BMfWbSUpxWuP/zXJplpQG1aWMiFTk0YFt4Y7Vsc53xt8kE49hRbJdt7jci6+jNJa9eBE7mCleOCscNhGufWjmFaD/eknf33UO2Rv2YwHaCqTb0g1HAWTdejQTKkYqgS3BmB6QioOuxoh8h0w92ZW6l1rot0V9R9rt2/1EE5zeRn+pj/Jn+zec5vAUwHqOpE8wP1872hFppoGKUhp+4hVTJO5oeTMtzCHSRAdEKCumYAmd3cf1NvIbZUUloKKuABZksrW7PBpm89ZuP2v8Y9MzuoVhrogEDDdFzIlzSmgpo9IIuNjSXVejQe/xS5NI/oZs+8ntgfc5Jky/ih1rS87RADZDa+6wlVj/ktgtnBpQGZH8Yg9mU8MgQXSMNO+2M0MTMnPWQF/5KAOoEUrGLH8qhILGKm82iAjn/LbQtzL9L9NT/szipo8lAQrL/UxxGa5ihl4lNknqXVahbsNJC0jI2ufm4gzYxZevb501B099AaGprTyjFNs+lHb6ju5NNiZMFvY8/M0U+1YqmB2umQ1rX+0FQ1lhg97Qdgn+nlGxHvVe/umPH3U6nJZGMrV4NHWHs+We9kF0DjkK0Aq7653UcWfqBhZWG4blsHkJrrd/aIGUv9zNFe7ClX+NhXnaIsQICbXzYhmmcDm292GtCYX/tTSa/bKZGqbTgmNkZRTNQWi5aYjd9j1ucZxaOn9f09g5nE/0q+QDdWsCfU9LRfdc+OUoCXRkLT/trLt1VsyHhyXaroYQnNdYK9fR4UvyQgqc0MA0nTG6FiPWb9/f5IzbA4J/uL87I/v79Ke6wv1LATjpU8gS/YzPUcxAnDzYkS00SSQGkrLssydvqqogfBFB18XCWJNidNnUjQb6/dkkhM3sX5UlDabHjBN6y+KSloobn+9x6OVRwI9PaEavbAvOlXfTwdnlVxlKxipIyS1EaptI7GL9o32ZbeVI33k63mGJGR0R2KA0g/yQon94NGJ6PHQZsiSZqiZK0TSeAJ1i31mJNn4uz1/0WpzBaG0+zf3aHJTZ5Azd2eYMN6Ne9LgUv7LAbrtVG7Okp37A21bJdRxAFWh/o7qbNKvhOwYNJ71CDzXhh627dkDAiBiVQhwEZbYbA5aqtVLLhvvObkm7yRmqrEGWS/Hwu2p42meLExWtQVBOtO8ATrl3nJgmRg6bBzmVh6WzFsr53avhDFRjUhJMkENlnOed7Eg1wLOnqDKKfX4j1Rms8J9d7mKa9rpc+E29Gugs5T+fvw/RuwwJ4vCFTPd5dPGBw/ZbkJyE5IbFx1YUaJNzxplC9YcwL2q6cQsfiG9i++4TyCkUBuZjWo5mzCeiaGJti4kV0J+zDpf00b6SeXGiDDws8MVotU2Xbq4BKEYOMX3mDdo16z7mh3ZMKwxIStTaq1G/5rBwND8w4eXewO1kxBpsSZICvugRpcDVW8zoumTCy9HJpC3jCVbcQO7v+LYzo/p6SPxiA3QwIbvwOAqzxm7Z3eYO1J/lBtTb/A1r7EU5Ha/7Cx87OB67JntUGCi4vMum18ocmzfGb93OJQ04lFocZzisyGSzDN75ISlOQhQHAJSjjOLQw1nOwza+YWBqt2KIbkFw6t9TsmFaX4zk3S+POAy7xoj95s52duksRfOLhu+bYW0LkccenTNgG46bHpsemx6bHp0cnH/wdtSkyyDr7dfQAAAABJRU5ErkJggg=='; diff --git a/src/shared/errors.ts b/packages/core/src/shared/errors.ts similarity index 100% rename from src/shared/errors.ts rename to packages/core/src/shared/errors.ts diff --git a/packages/core/src/shared/node_util.ts b/packages/core/src/shared/node_util.ts new file mode 100644 index 0000000..7974859 --- /dev/null +++ b/packages/core/src/shared/node_util.ts @@ -0,0 +1,27 @@ +// This files must be run in node runtime + +import { readFileSync } from 'node:fs'; +import { dirname, resolve } from 'node:path'; +import { fileURLToPath } from 'node:url'; + +const __dirname = dirname(fileURLToPath(import.meta.url)); + +let packageVersion: string | undefined; + +/** + * Read package.json and get the version string + * @param packageVersion + * @returns + */ +export function getVersionString() { + try { + packageVersion ??= JSON.parse( + readFileSync(resolve(__dirname, '..', '..', 'package.json')).toString(), + ).version; + } catch (error) { + console.error('Error reading package.json', error); + packageVersion = 'unknown'; + } + + return packageVersion; +} diff --git a/packages/core/src/shared/protocol.ts b/packages/core/src/shared/protocol.ts new file mode 100644 index 0000000..392c0da --- /dev/null +++ b/packages/core/src/shared/protocol.ts @@ -0,0 +1,31 @@ +export type BridgeRequestEvent = + | 'highlight-component' + | 'get-component-tree' + | 'get-component-states' + | 'get-unnecessary-rerenders' + | 'set-selection-mode' + | 'get-last-selection-context' + | 'copy-last-selection-context' + | 'get-html-elements' + | 'get-react-source-code' + | 'custom-tool'; + +export type BridgeMessage = + | { + type: 'bridge:request'; + id: string; + event: BridgeRequestEvent; + payload: unknown; + } + | { + type: 'bridge:response'; + id: string; + ok: boolean; + payload?: unknown; + error?: string; + }; + +export const BRIDGE_WS_PATH = '/__agentic_react_bridge'; +export const SOURCE_LOOKUP_PATH = '/__agentic_react_source'; + +export const BRIDGE_GLOBAL_CONFIG_KEY = '__AGENTIC_REACT_BRIDGE_URL__'; diff --git a/packages/core/src/shared/selection_context_format.ts b/packages/core/src/shared/selection_context_format.ts new file mode 100644 index 0000000..a253949 --- /dev/null +++ b/packages/core/src/shared/selection_context_format.ts @@ -0,0 +1,250 @@ +import { toAbsoluteSourcePath } from './source_path.js'; +import type { + SelectionContext, + SelectionResolvedSource, + SelectionSourceSnippet, + SelectionSourceTraceFrame, +} from './types.js'; + +export const WEB_CONTEXT_TAG = 'web_context'; +export const WEB_CONTEXT_TYPE_REACT_COMPONENT_LOCATION = + 'react_component_location'; + +interface SelectionContextFormatOptions { + sourceRoot?: string; +} + +const getPrimarySource = ( + selectionContext: SelectionContext, +): SelectionResolvedSource | SelectionSourceTraceFrame | null => + selectionContext.resolvedSources[0] ?? + selectionContext.externalComponent?.usedBy ?? + selectionContext.sourceTrace.find((frame) => frame.kind === 'project') ?? + null; + +export const buildWebContextText = ( + selectionContext: SelectionContext, + options: SelectionContextFormatOptions = {}, +): string => { + const lines: string[] = []; + const componentName = + selectionContext.componentName || + selectionContext.externalComponent?.componentName; + const primarySource = getPrimarySource(selectionContext); + const source = primarySource + ? formatSourceLocation(primarySource, options) + : null; + const details = buildSelectionContextSummary(selectionContext, options); + + if (componentName) { + lines.push(`component: ${componentName}`); + } + + if (selectionContext.selector) { + lines.push(`selector: ${selectionContext.selector}`); + } + + if (source) { + lines.push(`source: ${source}`); + } + + if (details) { + lines.push('', 'details:', details); + } + + return `<${WEB_CONTEXT_TAG} type="${WEB_CONTEXT_TYPE_REACT_COMPONENT_LOCATION}">\n${lines.join('\n')}\n${WEB_CONTEXT_TAG}>`; +}; + +const formatDisplayFilePath = ( + filePath: string, + options: SelectionContextFormatOptions = {}, +): string => + toAbsoluteSourcePath(filePath, options.sourceRoot) ?? + (filePath.startsWith('/src/') ? filePath.slice(1) : filePath); + +const formatSourceLocation = ( + source: Pick< + SelectionResolvedSource, + 'filePath' | 'lineNumber' | 'columnNumber' + >, + options: SelectionContextFormatOptions = {}, +): string => { + const filePath = formatDisplayFilePath(source.filePath, options); + const line = source.lineNumber != null ? `:${source.lineNumber}` : ''; + const column = source.columnNumber != null ? `:${source.columnNumber}` : ''; + return `${filePath}${line}${column}`; +}; + +const formatComponentSourceLine = ( + source: SelectionResolvedSource, + options: SelectionContextFormatOptions = {}, +): string => { + const location = formatSourceLocation(source, options); + return source.componentName + ? ` in ${source.componentName} (at ${location})` + : ` in ${location}`; +}; + +const formatExternalComponentLine = ( + selectionContext: SelectionContext, + options: SelectionContextFormatOptions = {}, +): string | null => { + const externalComponent = selectionContext.externalComponent; + if (!externalComponent) { + return null; + } + + const packageName = externalComponent.packageName + ? ` from ${externalComponent.packageName}` + : ''; + const usedBy = externalComponent.usedBy; + const usedByText = usedBy + ? ` used by ${usedBy.componentName ?? 'local component'} at ${formatSourceLocation(usedBy, options)}` + : ''; + + return `selected external component: <${externalComponent.componentName}>${packageName}${usedByText}`; +}; + +const formatSourceSnippet = ( + sourceSnippet: SelectionSourceSnippet, + options: SelectionContextFormatOptions = {}, +): string => + [ + `Source (${formatDisplayFilePath(sourceSnippet.filePath, options)}:${sourceSnippet.startLine}-${sourceSnippet.endLine})`, + sourceSnippet.snippet, + ].join('\n'); + +const formatTraceFrameLine = ( + traceFrame: SelectionSourceTraceFrame, + index: number, + options: SelectionContextFormatOptions = {}, +): string => { + const arrow = index === 0 ? ' ' : ' -> '; + const location = formatSourceLocation(traceFrame, options); + const componentName = traceFrame.componentName + ? `<${traceFrame.componentName}>` + : ''; + + if (traceFrame.kind === 'external') { + const packageName = traceFrame.packageName + ? ` from ${traceFrame.packageName}` + : ''; + const sourceLocation = traceFrame.packageName ? '' : ` at ${location}`; + return `${arrow}${componentName}${packageName}${sourceLocation}`; + } + + return traceFrame.componentName + ? `${arrow}${traceFrame.componentName} at ${location}` + : `${arrow}${location}`; +}; + +const buildSourceTracePreview = ( + sourceTrace: SelectionSourceTraceFrame[], + options: SelectionContextFormatOptions = {}, +): string => { + if (sourceTrace.length === 0) return ''; + + return [ + 'source trace:', + ...sourceTrace.map((traceFrame, index) => + formatTraceFrameLine(traceFrame, index, options), + ), + ].join('\n'); +}; + +export const buildComponentSourceChain = ( + resolvedSources: SelectionResolvedSource[], + options: SelectionContextFormatOptions = {}, +): string => { + const lines: string[] = []; + const seenSourceKeys = new Set(); + + for (const source of resolvedSources) { + if (!source.filePath) { + continue; + } + + const sourceKey = `${source.filePath}:${source.componentName ?? ''}`; + if (seenSourceKeys.has(sourceKey)) { + continue; + } + + seenSourceKeys.add(sourceKey); + lines.push(formatComponentSourceLine(source, options)); + } + + return lines.join('\n'); +}; + +export const buildSelectionSourcePreview = ( + selectionContext: SelectionContext, + options: SelectionContextFormatOptions = {}, +): string | null => { + const snippetText = selectionContext.sourceSnippets + .map((sourceSnippet) => formatSourceSnippet(sourceSnippet, options)) + .join('\n\n'); + const sourceChain = buildComponentSourceChain( + selectionContext.resolvedSources, + options, + ); + const sourceTrace = buildSourceTracePreview( + selectionContext.sourceTrace ?? [], + options, + ); + const externalComponentLine = formatExternalComponentLine( + selectionContext, + options, + ); + const shouldUseSourceTrace = + Boolean(externalComponentLine) || + Boolean( + selectionContext.sourceTrace?.some( + (traceFrame) => traceFrame.kind === 'project', + ), + ); + const preview = [ + snippetText, + externalComponentLine, + shouldUseSourceTrace && sourceTrace ? sourceTrace : sourceChain, + ] + .filter(Boolean) + .join('\n'); + + return preview || null; +}; + +export const buildSelectionContextSummary = ( + selectionContext: SelectionContext, + options: SelectionContextFormatOptions = {}, +): string => { + const summaryLines: string[] = []; + const sourcePreview = + options.sourceRoot || !selectionContext.sourcePreview + ? buildSelectionSourcePreview(selectionContext, options) + : selectionContext.sourcePreview; + + if (sourcePreview) { + summaryLines.push(sourcePreview); + } else { + summaryLines.push(selectionContext.domPreview); + } + + if (selectionContext.componentName) { + summaryLines.push(`component: ${selectionContext.componentName}`); + } + + if (selectionContext.tuningPrompts?.length) { + summaryLines.push( + [ + 'tuning prompts:', + ...selectionContext.tuningPrompts.map((prompt) => `- ${prompt}`), + ].join('\n'), + ); + } + + if (selectionContext.selector) { + summaryLines.push(`selector: ${selectionContext.selector}`); + } + + return summaryLines.join('\n'); +}; diff --git a/packages/core/src/shared/source_path.ts b/packages/core/src/shared/source_path.ts new file mode 100644 index 0000000..6074f69 --- /dev/null +++ b/packages/core/src/shared/source_path.ts @@ -0,0 +1,243 @@ +const SOURCE_FILE_EXTENSION_PATTERN = /\.(jsx?|tsx?)$/i; +const PROTOCOL_PATTERN = /^[a-zA-Z][a-zA-Z0-9+.-]*:\/\//; +const WINDOWS_ABSOLUTE_PATH_PATTERN = /^[a-zA-Z]:\//; + +export type SourcePathKind = + | 'invalid' + | 'url' + | 'vite-fs' + | 'fs-absolute' + | 'vite-root-relative' + | 'project-relative'; + +export interface SourcePathClassification { + kind: SourcePathKind; + normalizedPath: string; + isSourceFile: boolean; + withinSourceRoot: boolean; +} + +export const normalizeSourceRoot = (sourceRoot?: string): string => + (sourceRoot || '').trim().replace(/\\/g, '/').replace(/\/$/, ''); + +const normalizeSourcePath = (filePath: string): string => + filePath + .trim() + .replace(/\\/g, '/') + .replace(/[?#].*$/, ''); + +const hasRestrictedPathSegment = (normalizedPath: string): boolean => { + const pathSegments = normalizedPath.split('/').filter(Boolean); + return ( + pathSegments.includes('node_modules') || pathSegments.includes('.vite') + ); +}; + +const hasParentTraversal = (normalizedPath: string): boolean => + normalizedPath.split('/').includes('..'); + +const isWindowsAbsolutePath = (normalizedPath: string): boolean => + WINDOWS_ABSOLUTE_PATH_PATTERN.test(normalizedPath); + +const isWithinSourceRoot = ( + normalizedPath: string, + normalizedSourceRoot: string, +): boolean => { + if (!normalizedSourceRoot) { + return false; + } + + return ( + normalizedPath === normalizedSourceRoot || + normalizedPath.startsWith(`${normalizedSourceRoot}/`) + ); +}; + +export const classifySourcePath = ( + filePath: string, + sourceRoot?: string, +): SourcePathClassification => { + const normalizedSourceRoot = normalizeSourceRoot(sourceRoot); + const normalizedPath = normalizeSourcePath(filePath || ''); + + if (!normalizedPath || normalizedPath.includes('\0')) { + return { + kind: 'invalid', + normalizedPath, + isSourceFile: false, + withinSourceRoot: false, + }; + } + + if (PROTOCOL_PATTERN.test(normalizedPath)) { + return { + kind: 'url', + normalizedPath, + isSourceFile: false, + withinSourceRoot: false, + }; + } + + const isSourceFile = + SOURCE_FILE_EXTENSION_PATTERN.test(normalizedPath) && + !hasRestrictedPathSegment(normalizedPath); + + if (!isSourceFile) { + return { + kind: 'invalid', + normalizedPath, + isSourceFile: false, + withinSourceRoot: false, + }; + } + + if (normalizedPath.startsWith('/@fs/')) { + const fileSystemPath = normalizedPath.slice('/@fs'.length); + return { + kind: 'vite-fs', + normalizedPath, + isSourceFile, + withinSourceRoot: isWithinSourceRoot( + fileSystemPath, + normalizedSourceRoot, + ), + }; + } + + if (isWindowsAbsolutePath(normalizedPath)) { + return { + kind: 'fs-absolute', + normalizedPath, + isSourceFile, + withinSourceRoot: isWithinSourceRoot( + normalizedPath, + normalizedSourceRoot, + ), + }; + } + + if (normalizedPath.startsWith('/')) { + const withinSourceRoot = isWithinSourceRoot( + normalizedPath, + normalizedSourceRoot, + ); + return { + kind: withinSourceRoot ? 'fs-absolute' : 'vite-root-relative', + normalizedPath, + isSourceFile, + withinSourceRoot: withinSourceRoot || !hasParentTraversal(normalizedPath), + }; + } + + return { + kind: 'project-relative', + normalizedPath, + isSourceFile, + withinSourceRoot: !hasParentTraversal(normalizedPath), + }; +}; + +const toViteFsUrl = (fileSystemPath: string): string => + fileSystemPath.startsWith('/') + ? `/@fs${fileSystemPath}` + : `/@fs/${fileSystemPath}`; + +const joinNormalizedPath = (basePath: string, childPath: string): string => + `${basePath.replace(/\/$/, '')}/${childPath.replace(/^\//, '')}`; + +export const toAbsoluteSourcePath = ( + filePath: string, + sourceRoot?: string, +): string | null => { + const normalizedSourceRoot = normalizeSourceRoot(sourceRoot); + const classifiedPath = classifySourcePath(filePath, normalizedSourceRoot); + + if (!classifiedPath.isSourceFile || !classifiedPath.withinSourceRoot) { + return null; + } + + if (classifiedPath.kind === 'vite-fs') { + return classifiedPath.normalizedPath.slice('/@fs'.length); + } + + if (classifiedPath.kind === 'fs-absolute') { + return classifiedPath.normalizedPath; + } + + if (!normalizedSourceRoot) { + return null; + } + + if (classifiedPath.kind === 'vite-root-relative') { + return joinNormalizedPath( + normalizedSourceRoot, + classifiedPath.normalizedPath, + ); + } + + if (classifiedPath.kind === 'project-relative') { + const sanitizedPath = classifiedPath.normalizedPath + .replace(/^\.?\//, '') + .replace(/\/\.\//g, '/'); + const sourceRootName = normalizedSourceRoot + .split('/') + .filter(Boolean) + .pop(); + if (sourceRootName && sanitizedPath.startsWith(`${sourceRootName}/src/`)) { + return joinNormalizedPath( + normalizedSourceRoot, + sanitizedPath.slice(sourceRootName.length + 1), + ); + } + + return joinNormalizedPath(normalizedSourceRoot, sanitizedPath); + } + + return null; +}; + +export const buildBrowserSourceCandidates = ( + filePath: string, + sourceRoot?: string, +): string[] => { + const classifiedPath = classifySourcePath(filePath, sourceRoot); + + if (!classifiedPath.isSourceFile || !classifiedPath.withinSourceRoot) { + return []; + } + + if (classifiedPath.kind === 'vite-fs') { + return [ + `${classifiedPath.normalizedPath}?raw`, + classifiedPath.normalizedPath, + ]; + } + + if (classifiedPath.kind === 'fs-absolute') { + const viteFsUrl = toViteFsUrl(classifiedPath.normalizedPath); + return [`${viteFsUrl}?raw`, viteFsUrl]; + } + + if (classifiedPath.kind === 'vite-root-relative') { + return [ + `${classifiedPath.normalizedPath}?raw`, + classifiedPath.normalizedPath, + ]; + } + + if (classifiedPath.kind === 'project-relative') { + const sanitizedPath = classifiedPath.normalizedPath.replace(/^\.?\//, ''); + const rootRelativePath = `/${sanitizedPath}`; + return [`${rootRelativePath}?raw`, rootRelativePath]; + } + + return []; +}; + +export const isAllowedProjectSourcePath = ( + filePath: string, + sourceRoot?: string, +): boolean => { + const classifiedPath = classifySourcePath(filePath, sourceRoot); + return classifiedPath.isSourceFile && classifiedPath.withinSourceRoot; +}; diff --git a/src/shared/store.ts b/packages/core/src/shared/store.ts similarity index 70% rename from src/shared/store.ts rename to packages/core/src/shared/store.ts index 01c445a..075d695 100644 --- a/src/shared/store.ts +++ b/packages/core/src/shared/store.ts @@ -1,5 +1,5 @@ -import type { WastedRenderFiberInfo } from '../types/internal'; -import type { FiberRoot } from '../types/react'; +import type { WastedRenderFiberInfo } from '../types/internal.js'; +import type { FiberRoot } from '../types/react.js'; // Closure contexts go here export const wastedRenderFiberInfo: Map = diff --git a/packages/core/src/shared/types.ts b/packages/core/src/shared/types.ts new file mode 100644 index 0000000..8d8e877 --- /dev/null +++ b/packages/core/src/shared/types.ts @@ -0,0 +1,181 @@ +import type { ZodTypeAny } from 'zod'; + +export type JsonValue = + | string + | number + | boolean + | null + | { [key: string]: JsonValue } + | JsonValue[]; + +export type ToolResultValue = string | JsonValue | undefined; + +export interface ToolkitOffset { + x?: number; + y?: number; +} + +export type ToolkitPosition = + | 'top-left' + | 'top-right' + | 'bottom-left' + | 'bottom-right'; + +export type ToolkitTuningModalStyleSlot = + | 'root' + | 'surface' + | 'panel' + | 'arrow' + | 'title' + | 'body' + | 'targetTag' + | 'customPromptForm' + | 'customPromptInput' + | 'customPromptButton' + | 'sectionTitle' + | 'row' + | 'label' + | 'controlWrap' + | 'control' + | 'colorInput' + | 'numberInput' + | 'stepperButton' + | 'select' + | 'textarea' + | 'suffix' + | 'closeButton'; + +export type ToolkitTuningModalStyleValue = string | number; + +export type ToolkitTuningModalStyle = Record< + string, + ToolkitTuningModalStyleValue +>; + +export interface ToolkitTuningModalConfig { + classNames?: Partial>; + styles?: Partial< + Record + >; + tokens?: Record; +} + +export interface ToolkitConfig { + enabled?: boolean; + defaultVisible?: boolean; + defaultExpanded?: boolean; + position?: ToolkitPosition; + offset?: ToolkitOffset; + accentColor?: string; + zIndex?: number; + iconUrl?: string; + tuningModal?: ToolkitTuningModalConfig; +} + +export interface AgenticReactConfig { + toolkit?: ToolkitConfig; + sourceRoot?: string; +} + +export interface SelectionStackFrame { + functionName: string | null; + fileName: string | null; + lineNumber: number | null; + columnNumber: number | null; +} + +export interface SelectionResolvedSource { + filePath: string; + lineNumber: number | null; + columnNumber: number | null; + componentName: string | null; +} + +export interface SelectionSourceTraceFrame { + kind: 'external' | 'project'; + componentName: string | null; + packageName: string | null; + filePath: string; + lineNumber: number | null; + columnNumber: number | null; +} + +export interface SelectionExternalComponent { + componentName: string; + packageName: string | null; + filePath: string | null; + usedBy: SelectionResolvedSource | null; +} + +export interface SelectionSourceSnippet { + filePath: string; + startLine: number; + endLine: number; + snippet: string; +} + +export interface SelectionContext { + domPreview: string; + sourcePreview: string | null; + selector: string | null; + componentName: string | null; + externalComponent: SelectionExternalComponent | null; + stackFrames: SelectionStackFrame[]; + resolvedSources: SelectionResolvedSource[]; + sourceTrace: SelectionSourceTraceFrame[]; + sourceSnippets: SelectionSourceSnippet[]; + tuningPrompts?: string[]; + capturedAt: number; +} + +export interface TuningModalContext { + element: Element; + tagName: string; + targetLabel: string; + computedStyle: CSSStyleDeclaration; + selectionContext: SelectionContext; +} + +export interface TuningModalActions { + addPrompt: (prompt: string) => void; + close: () => void; + requestReposition: () => void; +} + +export interface TuningModalSlotRenderArgs { + container: HTMLElement; + context: TuningModalContext; + actions: TuningModalActions; +} + +export interface TuningModalWrapArgs { + surfaceElement: HTMLElement; + panelElement: HTMLElement; + context: TuningModalContext; + actions: TuningModalActions; +} + +export type TuningModalExtensionCleanup = undefined | (() => void); + +export interface TuningModalExtension { + id: string; + beforeFields?: ( + args: TuningModalSlotRenderArgs, + ) => TuningModalExtensionCleanup; + afterFields?: ( + args: TuningModalSlotRenderArgs, + ) => TuningModalExtensionCleanup; + footer?: (args: TuningModalSlotRenderArgs) => TuningModalExtensionCleanup; + wrapModal?: (args: TuningModalWrapArgs) => TuningModalExtensionCleanup; +} + +export type CustomClientFunction = + | string + | ((args: unknown) => ToolResultValue | Promise); + +export interface CustomTool { + name: string; + description: string; + schema: ZodTypeAny; + clientFunction: CustomClientFunction; +} diff --git a/src/shared/util.ts b/packages/core/src/shared/util.ts similarity index 98% rename from src/shared/util.ts rename to packages/core/src/shared/util.ts index d91ba71..a4788e2 100644 --- a/src/shared/util.ts +++ b/packages/core/src/shared/util.ts @@ -4,8 +4,8 @@ import type { Props, ReactDevToolsGlobalHook, } from 'bippy'; -import type { HookNode } from '../types/internal'; -import type { FiberRoot, ReactDevtools } from '../types/react'; +import type { HookNode } from '../types/internal.js'; +import type { FiberRoot, ReactDevtools } from '../types/react.js'; import { CONCURRENT_MODE_NUMBER, CONCURRENT_MODE_SYMBOL_STRING, @@ -29,9 +29,9 @@ import { STRICT_MODE_NUMBER, STRICT_MODE_SYMBOL_STRING, target, -} from './const'; -import { ReactTypeOfWork } from './const'; -import { fiberRoots } from './store'; +} from './const.js'; +import { ReactTypeOfWork } from './const.js'; +import { fiberRoots } from './store.js'; let rdtHook: ReactDevToolsGlobalHook | ReactDevtools | null = null; export let usingRealHook = false; diff --git a/src/types/internal.ts b/packages/core/src/types/internal.ts similarity index 100% rename from src/types/internal.ts rename to packages/core/src/types/internal.ts diff --git a/src/types/react.ts b/packages/core/src/types/react.ts similarity index 100% rename from src/types/react.ts rename to packages/core/src/types/react.ts diff --git a/tsconfig.json b/packages/core/tsconfig.json similarity index 95% rename from tsconfig.json rename to packages/core/tsconfig.json index a1fba4e..971c50b 100644 --- a/tsconfig.json +++ b/packages/core/tsconfig.json @@ -9,6 +9,7 @@ "resolveJsonModule": true, "allowJs": true, "strict": true, + "skipLibCheck": true, "strictNullChecks": false, "noImplicitAny": false, "outDir": "dist", diff --git a/packages/next/LICENSE b/packages/next/LICENSE new file mode 100644 index 0000000..102207d --- /dev/null +++ b/packages/next/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2023 webfansplz + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/packages/next/README.md b/packages/next/README.md new file mode 100644 index 0000000..326bbc3 --- /dev/null +++ b/packages/next/README.md @@ -0,0 +1,15 @@ +# @agentic-react/next + +Next.js adapter for Agentic React local-dev MCP integration. + +```bash +pnpm install @agentic-react/next -D +``` + +```js +import withAgenticReactNext from '@agentic-react/next'; + +export default withAgenticReactNext(nextConfig); +``` + +This adapter injects `@agentic-react/core` through Next's Webpack config and starts a local bridge server for MCP. By default the bridge runs at `http://127.0.0.1:51426/mcp`. diff --git a/packages/next/package.json b/packages/next/package.json new file mode 100644 index 0000000..b77ee10 --- /dev/null +++ b/packages/next/package.json @@ -0,0 +1,53 @@ +{ + "name": "@agentic-react/next", + "type": "module", + "version": "0.1.0", + "description": "Next.js adapter for Agentic React local dev integration", + "main": "dist/index.js", + "types": "dist/index.d.ts", + "scripts": { + "build": "shx rm -rf dist && tsc" + }, + "keywords": [ + "agentic-react", + "next", + "react", + "mcp", + "visual-edit", + "agents", + "devtools", + "plugin" + ], + "author": { + "name": "jazelly", + "email": "xzha4350@gmail.com" + }, + "files": [ + "dist", + "LICENSE", + "README.md" + ], + "publishConfig": { + "access": "public" + }, + "license": "MIT", + "peerDependencies": { + "next": ">=13", + "webpack": ">=5" + }, + "peerDependenciesMeta": { + "webpack": { + "optional": true + } + }, + "dependencies": { + "@agentic-react/core": "workspace:*", + "@modelcontextprotocol/sdk": "1.29.0" + }, + "exports": { + ".": { + "types": "./dist/index.d.ts", + "default": "./dist/index.js" + } + } +} diff --git a/packages/next/src/index.ts b/packages/next/src/index.ts new file mode 100644 index 0000000..8fe2d8d --- /dev/null +++ b/packages/next/src/index.ts @@ -0,0 +1,312 @@ +import fs from 'node:fs'; +import http from 'node:http'; +import { createRequire } from 'node:module'; +import path from 'node:path'; +import { RuntimeBridgeServer } from '@agentic-react/core/bridge'; +import { + createStreamableHttpMcpHandler, + initMcpServer, +} from '@agentic-react/core/mcp'; +import { + __AGENTIC_REACT_BRIDGE_URL__, + __AGENTIC_REACT_CONFIG__, +} from '@agentic-react/core/shared/const'; +import { + generateCustomToolsScript, + toBundledClientImportSpecifier, + toRelativeImportSpecifier, +} from '@agentic-react/core/shared/custom-tools-script'; +import { BRIDGE_WS_PATH } from '@agentic-react/core/shared/protocol'; +import type { + CustomTool, + ToolkitConfig, +} from '@agentic-react/core/shared/types'; + +interface NextWebpackContext { + dev: boolean; + isServer: boolean; + webpack: { + DefinePlugin: new (definitions: Record) => unknown; + }; +} + +interface NextConfig { + webpack?: (config: any, context: NextWebpackContext) => any; + [key: string]: unknown; +} + +export interface AgenticReactNextOptions { + customTools?: CustomTool[]; + rootDir?: string; + toolkit?: ToolkitConfig; + bridgeUrl?: string; + server?: { + enabled?: boolean; + host?: string; + port?: number; + }; +} + +interface BridgeServerState { + started: boolean; + server?: http.Server; +} + +const DEFAULT_BRIDGE_HOST = '127.0.0.1'; +const DEFAULT_BRIDGE_PORT = 51426; +const NEXT_BRIDGE_STATE_KEY = Symbol.for('agentic-react.nextBridgeServers'); + +const require = createRequire(import.meta.url); + +const getCoreDistPath = () => + path.dirname(require.resolve('@agentic-react/core/overlay')); + +const writeNextClientEntry = ( + rootDir: string, + bridgeUrl: string | null, + customTools: CustomTool[], + toolkitConfig: ToolkitConfig, +): string => { + const coreDistPath = getCoreDistPath(); + const generatedDirectory = path.join(rootDir, '.next/agentic-react'); + const generatedEntryPath = path.join(generatedDirectory, 'client-entry.mjs'); + const constSpecifier = toRelativeImportSpecifier( + generatedDirectory, + path.join(coreDistPath, 'shared/const.js'), + ); + const protocolSpecifier = toRelativeImportSpecifier( + generatedDirectory, + path.join(coreDistPath, 'shared/protocol.js'), + ); + const overlaySpecifier = toRelativeImportSpecifier( + generatedDirectory, + path.join(coreDistPath, 'overlay.js'), + ); + const customToolsScript = generateCustomToolsScript( + customTools, + (specifier) => + toBundledClientImportSpecifier(rootDir, generatedDirectory, specifier), + ); + + const entrySource = ` +import { + ${__AGENTIC_REACT_BRIDGE_URL__}, + ${__AGENTIC_REACT_CONFIG__}, +} from ${JSON.stringify(constSpecifier)}; +import { BRIDGE_WS_PATH } from ${JSON.stringify(protocolSpecifier)}; + +if (typeof window !== 'undefined') { + const existingAgenticReactConfig = window[${__AGENTIC_REACT_CONFIG__}] || {}; + window[${__AGENTIC_REACT_CONFIG__}] = { + ...existingAgenticReactConfig, + sourceRoot: existingAgenticReactConfig.sourceRoot || ${JSON.stringify(rootDir)}, + toolkit: { + ...(existingAgenticReactConfig.toolkit || {}), + ...${JSON.stringify(toolkitConfig)}, + }, + }; + + if (!window[${__AGENTIC_REACT_BRIDGE_URL__}]) { + const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:'; + window[${__AGENTIC_REACT_BRIDGE_URL__}] = + ${JSON.stringify(bridgeUrl || '')} || + \`\${protocol}//\${window.location.host}\${BRIDGE_WS_PATH}\`; + } +} + +void import(${JSON.stringify(overlaySpecifier)}); + +${customToolsScript} +`; + + fs.mkdirSync(generatedDirectory, { recursive: true }); + if ( + !fs.existsSync(generatedEntryPath) || + fs.readFileSync(generatedEntryPath, 'utf8') !== entrySource + ) { + fs.writeFileSync(generatedEntryPath, entrySource); + } + + return generatedEntryPath; +}; + +const prependEntry = (entryValue: unknown, prependPath: string): unknown => { + if (typeof entryValue === 'string') { + return [prependPath, entryValue]; + } + + if (Array.isArray(entryValue)) { + return entryValue.includes(prependPath) + ? entryValue + : [prependPath, ...entryValue]; + } + + if (entryValue && typeof entryValue === 'object') { + const entryObject = entryValue as Record; + const nextEntryObject: Record = {}; + for (const [key, value] of Object.entries(entryObject)) { + if (key.startsWith('webpack-hot-middleware') || key === prependPath) { + nextEntryObject[key] = value; + continue; + } + + nextEntryObject[key] = prependEntry(value, prependPath); + } + return nextEntryObject; + } + + return entryValue; +}; + +const getBridgeServerRegistry = (): Map => { + const globalRecord = globalThis as Record< + symbol, + Map + >; + if (!globalRecord[NEXT_BRIDGE_STATE_KEY]) { + globalRecord[NEXT_BRIDGE_STATE_KEY] = new Map(); + } + return globalRecord[NEXT_BRIDGE_STATE_KEY]; +}; + +const createBridgeHttpServer = (rootDir: string, customTools: CustomTool[]) => { + const runtimeBridge = new RuntimeBridgeServer(); + const handleMcpRequest = createStreamableHttpMcpHandler(() => + initMcpServer(runtimeBridge, rootDir, customTools), + ); + + const httpServer = http.createServer(async (req, res) => { + const requestUrl = req.url || '/'; + const requestPath = requestUrl.split('?')[0]; + + if (requestPath === '/health') { + res.statusCode = 200; + res.setHeader('content-type', 'application/json'); + res.end(JSON.stringify({ ok: true })); + return; + } + + if (requestPath === '/mcp') { + await handleMcpRequest(req, res); + return; + } + + res.statusCode = 404; + res.end('Not Found'); + }); + + runtimeBridge.attach(httpServer); + return httpServer; +}; + +const startNextBridgeServer = ( + options: AgenticReactNextOptions, +): string | null => { + if (options.server?.enabled === false) { + return options.bridgeUrl || null; + } + + const host = options.server?.host || DEFAULT_BRIDGE_HOST; + const port = options.server?.port || DEFAULT_BRIDGE_PORT; + const bridgeUrl = + options.bridgeUrl || `ws://${host}:${port}${BRIDGE_WS_PATH}`; + const registryKey = `${host}:${port}`; + const registry = getBridgeServerRegistry(); + const existingState = registry.get(registryKey); + + if (existingState) { + return bridgeUrl; + } + + const serverState: BridgeServerState = { started: false }; + registry.set(registryKey, serverState); + + const rootDir = options.rootDir || process.cwd(); + const httpServer = createBridgeHttpServer(rootDir, options.customTools || []); + serverState.server = httpServer; + + httpServer.once('error', (error) => { + serverState.started = false; + registry.delete(registryKey); + console.warn( + `[agentic-react] Next bridge server failed on ${host}:${port}:`, + error instanceof Error ? error.message : error, + ); + }); + + httpServer.listen(port, host, () => { + serverState.started = true; + console.info( + `[agentic-react] Next MCP bridge listening on http://${host}:${port}`, + ); + }); + + return bridgeUrl; +}; + +export const withAgenticReactNext = ( + nextConfig: NextConfig = {}, + options: AgenticReactNextOptions = {}, +): NextConfig => { + return { + ...nextConfig, + webpack: (config: any, context: NextWebpackContext) => { + const nextConfigWebpack = nextConfig.webpack; + const transformedConfig = + typeof nextConfigWebpack === 'function' + ? nextConfigWebpack(config, context) + : config; + + if (!context.dev || context.isServer) { + return transformedConfig; + } + + const rootDir = options.rootDir || process.cwd(); + const bridgeUrl = startNextBridgeServer(options); + const entryPath = writeNextClientEntry( + rootDir, + bridgeUrl, + options.customTools || [], + options.toolkit || {}, + ); + const originalEntry = transformedConfig.entry; + + transformedConfig.entry = async () => { + const resolvedEntry = + typeof originalEntry === 'function' + ? await originalEntry() + : originalEntry; + + return prependEntry(resolvedEntry, entryPath); + }; + + return transformedConfig; + }, + }; +}; + +export default withAgenticReactNext; +export type { + AgenticReactConfig, + CustomClientFunction, + CustomTool, + JsonValue, + SelectionContext, + SelectionResolvedSource, + SelectionSourceSnippet, + SelectionStackFrame, + ToolkitConfig, + ToolkitOffset, + ToolkitPosition, + ToolkitTuningModalConfig, + ToolkitTuningModalStyle, + ToolkitTuningModalStyleSlot, + ToolkitTuningModalStyleValue, + ToolResultValue, + TuningModalActions, + TuningModalContext, + TuningModalExtension, + TuningModalExtensionCleanup, + TuningModalSlotRenderArgs, + TuningModalWrapArgs, +} from '@agentic-react/core/shared/types'; diff --git a/packages/next/tsconfig.json b/packages/next/tsconfig.json new file mode 100644 index 0000000..d956eb8 --- /dev/null +++ b/packages/next/tsconfig.json @@ -0,0 +1,17 @@ +{ + "compilerOptions": { + "target": "es2018", + "lib": ["esnext", "dom"], + "module": "esnext", + "moduleResolution": "bundler", + "declaration": true, + "strict": true, + "skipLibCheck": true, + "strictNullChecks": false, + "noImplicitAny": false, + "outDir": "dist", + "esModuleInterop": true + }, + "include": ["src/**/*.ts"], + "exclude": ["dist", "node_modules"] +} diff --git a/packages/vite/LICENSE b/packages/vite/LICENSE new file mode 100644 index 0000000..102207d --- /dev/null +++ b/packages/vite/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2023 webfansplz + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/packages/vite/README.md b/packages/vite/README.md new file mode 100644 index 0000000..d9c1f72 --- /dev/null +++ b/packages/vite/README.md @@ -0,0 +1,18 @@ +# @agentic-react/vite + +Vite adapter for Agentic React local-dev MCP integration. + +```bash +pnpm install @agentic-react/vite -D +``` + +```ts +import { defineConfig } from 'vite'; +import AgenticReact from '@agentic-react/vite'; + +export default defineConfig({ + plugins: [AgenticReact()], +}); +``` + +This adapter injects `@agentic-react/core` into the browser runtime, attaches the local runtime bridge to the Vite dev server, and exposes MCP over Streamable HTTP at `/mcp`. diff --git a/packages/vite/package.json b/packages/vite/package.json new file mode 100644 index 0000000..2e7ca6d --- /dev/null +++ b/packages/vite/package.json @@ -0,0 +1,50 @@ +{ + "name": "@agentic-react/vite", + "type": "module", + "version": "0.1.0", + "description": "Vite adapter for Agentic React local dev integration", + "main": "dist/index.js", + "types": "dist/index.d.ts", + "scripts": { + "build": "shx rm -rf dist && tsc" + }, + "keywords": [ + "agentic-react", + "vite", + "react", + "mcp", + "visual-edit", + "agents", + "devtools", + "plugin" + ], + "author": { + "name": "jazelly", + "email": "xzha4350@gmail.com" + }, + "files": [ + "dist", + "LICENSE", + "README.md" + ], + "publishConfig": { + "access": "public" + }, + "license": "MIT", + "peerDependencies": { + "vite": ">=4" + }, + "devDependencies": { + "vite": "^6.4.2" + }, + "dependencies": { + "@agentic-react/core": "workspace:*", + "@modelcontextprotocol/sdk": "1.29.0" + }, + "exports": { + ".": { + "types": "./dist/index.d.ts", + "default": "./dist/index.js" + } + } +} diff --git a/packages/vite/src/index.ts b/packages/vite/src/index.ts new file mode 100644 index 0000000..e3f071f --- /dev/null +++ b/packages/vite/src/index.ts @@ -0,0 +1,203 @@ +import { createRequire } from 'node:module'; +import path from 'node:path'; +import { RuntimeBridgeServer } from '@agentic-react/core/bridge'; +import { + createStreamableHttpMcpHandler, + initMcpServer, +} from '@agentic-react/core/mcp'; +import { + __AGENTIC_REACT_BRIDGE_URL__, + __AGENTIC_REACT_CONFIG__, +} from '@agentic-react/core/shared/const'; +import { generateCustomToolsScript } from '@agentic-react/core/shared/custom-tools-script'; +import { BRIDGE_WS_PATH } from '@agentic-react/core/shared/protocol'; +import type { + CustomTool, + ToolkitConfig, +} from '@agentic-react/core/shared/types'; +import type { Server } from '@modelcontextprotocol/sdk/server/index.js'; +import { type ViteDevServer, normalizePath } from 'vite'; +import type { Plugin, ResolvedConfig } from 'vite'; + +const require = createRequire(import.meta.url); + +function getAgenticReactCorePath() { + return normalizePath( + path.dirname(require.resolve('@agentic-react/core/overlay')), + ); +} + +const agenticReactCorePath = getAgenticReactCorePath(); +const agenticReactResourceSymbol = '?__agentic-react-resource'; +const agenticReactImportee = 'virtual:agentic-react'; +const resolvedAgenticReact = `\0${agenticReactImportee}`; + +function instrumentViteDevServer( + viteDevServer: ViteDevServer, + createMcpServer: () => Server, +) { + viteDevServer.middlewares.use( + '/mcp', + createStreamableHttpMcpHandler(createMcpServer), + ); +} + +export interface AgenticReactOptions { + customTools?: CustomTool[]; + toolkit?: ToolkitConfig; +} + +function AgenticReact(options: AgenticReactOptions = {}): Plugin { + let config: ResolvedConfig; + const customTools = options.customTools || []; + const toolkitConfig = options.toolkit || {}; + + return { + name: 'agentic-react', + enforce: 'pre', + apply: 'serve', + + configureServer(viteDevServer: ViteDevServer) { + const runtimeBridge = new RuntimeBridgeServer(); + if (viteDevServer.httpServer) { + runtimeBridge.attach(viteDevServer.httpServer); + } + + const createMcpServer = () => + initMcpServer(runtimeBridge, viteDevServer.config.root, customTools); + instrumentViteDevServer(viteDevServer, createMcpServer); + + setTimeout(() => { + console.info( + 'Agentic React MCP server is running on port', + viteDevServer.config.server.port, + ); + }, 1000); + }, + + async resolveId(importee) { + if (importee === agenticReactImportee) { + return resolvedAgenticReact; + } + if (importee.startsWith(`${agenticReactImportee}:`)) { + const resolved = importee.replace( + `${agenticReactImportee}:`, + `${agenticReactCorePath}/`, + ); + return `${resolved}${agenticReactResourceSymbol}`; + } + }, + + configResolved(resolvedConfig) { + config = resolvedConfig; + }, + + transformIndexHtml() { + const registerAgenticReactConfigScript = ` + window.${__AGENTIC_REACT_CONFIG__} = ${JSON.stringify({ + toolkit: toolkitConfig, + sourceRoot: config.root, + })}; + `; + + const bridgeUrlScript = ` + (function(){ + var protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:'; + window.${__AGENTIC_REACT_BRIDGE_URL__} = protocol + '//' + window.location.host + '${BRIDGE_WS_PATH}'; + })(); + `; + + const customToolsScript = generateCustomToolsScript( + customTools, + (specifier) => toClientImportSpecifier(specifier, config), + ); + + return [ + { + tag: 'script', + injectTo: 'head-prepend', + attrs: { type: 'text/javascript' }, + children: registerAgenticReactConfigScript, + }, + { + tag: 'script', + injectTo: 'head-prepend', + attrs: { type: 'text/javascript' }, + children: bridgeUrlScript, + }, + { + tag: 'script', + injectTo: 'head-prepend', + attrs: { + type: 'module', + src: `${config.base || '/'}@id/${agenticReactImportee}:overlay.js`, + }, + }, + { + tag: 'script', + injectTo: 'head-prepend', + attrs: { type: 'module' }, + children: customToolsScript, + }, + ]; + }, + }; +} + +function toClientImportSpecifier( + filePathOrSpecifier: string, + resolvedConfig: ResolvedConfig, +): string { + const normalized = normalizePath(filePathOrSpecifier); + + if ( + normalized.startsWith('/@fs/') || + normalized.startsWith('http://') || + normalized.startsWith('https://') + ) { + return normalized; + } + + if (path.isAbsolute(normalized)) { + return `/@fs/${normalized}`; + } + + if (normalized.startsWith('./') || normalized.startsWith('../')) { + const absolutePath = normalizePath( + path.resolve(resolvedConfig.root, normalized), + ); + return `/@fs/${absolutePath}`; + } + + if (normalized.startsWith('/')) { + return normalized; + } + + return `/@id/${normalized}`; +} + +export default AgenticReact; +export type { + AgenticReactConfig, + CustomClientFunction, + CustomTool, + JsonValue, + SelectionContext, + SelectionResolvedSource, + SelectionSourceSnippet, + SelectionStackFrame, + ToolkitConfig, + ToolkitOffset, + ToolkitPosition, + ToolkitTuningModalConfig, + ToolkitTuningModalStyle, + ToolkitTuningModalStyleSlot, + ToolkitTuningModalStyleValue, + ToolResultValue, + TuningModalActions, + TuningModalContext, + TuningModalExtension, + TuningModalExtensionCleanup, + TuningModalSlotRenderArgs, + TuningModalWrapArgs, +} from '@agentic-react/core/shared/types'; diff --git a/packages/vite/tsconfig.json b/packages/vite/tsconfig.json new file mode 100644 index 0000000..d956eb8 --- /dev/null +++ b/packages/vite/tsconfig.json @@ -0,0 +1,17 @@ +{ + "compilerOptions": { + "target": "es2018", + "lib": ["esnext", "dom"], + "module": "esnext", + "moduleResolution": "bundler", + "declaration": true, + "strict": true, + "skipLibCheck": true, + "strictNullChecks": false, + "noImplicitAny": false, + "outDir": "dist", + "esModuleInterop": true + }, + "include": ["src/**/*.ts"], + "exclude": ["dist", "node_modules"] +} diff --git a/packages/webpack/LICENSE b/packages/webpack/LICENSE new file mode 100644 index 0000000..102207d --- /dev/null +++ b/packages/webpack/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2023 webfansplz + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/packages/webpack/README.md b/packages/webpack/README.md new file mode 100644 index 0000000..d0b30d7 --- /dev/null +++ b/packages/webpack/README.md @@ -0,0 +1,16 @@ +# @agentic-react/webpack + +Webpack adapter for Agentic React local-dev MCP integration. + +```bash +pnpm install @agentic-react/webpack -D +``` + +```js +import withAgenticReactWebpack from '@agentic-react/webpack'; + +export default (env, argv) => + withAgenticReactWebpack(config, { mode: argv.mode }); +``` + +This adapter injects `@agentic-react/core` through a generated browser entry, attaches the local runtime bridge to webpack-dev-server, and exposes MCP over Streamable HTTP at `/mcp`. diff --git a/packages/webpack/package.json b/packages/webpack/package.json new file mode 100644 index 0000000..2b61f36 --- /dev/null +++ b/packages/webpack/package.json @@ -0,0 +1,47 @@ +{ + "name": "@agentic-react/webpack", + "type": "module", + "version": "0.1.0", + "description": "Webpack adapter for Agentic React local dev integration", + "main": "dist/index.js", + "types": "dist/index.d.ts", + "scripts": { + "build": "shx rm -rf dist && tsc" + }, + "keywords": [ + "agentic-react", + "webpack", + "react", + "mcp", + "visual-edit", + "agents", + "devtools", + "plugin" + ], + "author": { + "name": "jazelly", + "email": "xzha4350@gmail.com" + }, + "files": [ + "dist", + "LICENSE", + "README.md" + ], + "publishConfig": { + "access": "public" + }, + "license": "MIT", + "peerDependencies": { + "webpack": ">=5" + }, + "dependencies": { + "@agentic-react/core": "workspace:*", + "@modelcontextprotocol/sdk": "1.29.0" + }, + "exports": { + ".": { + "types": "./dist/index.d.ts", + "default": "./dist/index.js" + } + } +} diff --git a/packages/webpack/src/index.ts b/packages/webpack/src/index.ts new file mode 100644 index 0000000..7e9598d --- /dev/null +++ b/packages/webpack/src/index.ts @@ -0,0 +1,400 @@ +import fs from 'node:fs'; +import { createRequire } from 'node:module'; +import path from 'node:path'; +import { RuntimeBridgeServer } from '@agentic-react/core/bridge'; +import { + createStreamableHttpMcpHandler, + initMcpServer, +} from '@agentic-react/core/mcp'; +import { + __AGENTIC_REACT_BRIDGE_URL__, + __AGENTIC_REACT_CONFIG__, +} from '@agentic-react/core/shared/const'; +import { + generateCustomToolsScript, + toBundledClientImportSpecifier, + toRelativeImportSpecifier, +} from '@agentic-react/core/shared/custom-tools-script'; +import { SOURCE_LOOKUP_PATH } from '@agentic-react/core/shared/protocol'; +import type { + CustomTool, + SelectionResolvedSource, + ToolkitConfig, +} from '@agentic-react/core/shared/types'; +import type { Server } from '@modelcontextprotocol/sdk/server/index.js'; + +interface WebpackEnv { + mode?: string; +} + +export interface AgenticReactWebpackOptions { + customTools?: CustomTool[]; + rootDir?: string; + toolkit?: ToolkitConfig; +} + +type WebpackDevServer = { + server?: unknown; +}; + +const require = createRequire(import.meta.url); + +const getCoreDistPath = () => + path.dirname(require.resolve('@agentic-react/core/overlay')); + +const writeWebpackClientEntry = ( + rootDir: string, + customTools: CustomTool[], + toolkitConfig: ToolkitConfig, +): string => { + const coreDistPath = getCoreDistPath(); + const generatedDirectory = path.join(rootDir, '.agentic-react-webpack'); + const generatedEntryPath = path.join(generatedDirectory, 'client-entry.mjs'); + const constSpecifier = toRelativeImportSpecifier( + generatedDirectory, + path.join(coreDistPath, 'shared/const.js'), + ); + const protocolSpecifier = toRelativeImportSpecifier( + generatedDirectory, + path.join(coreDistPath, 'shared/protocol.js'), + ); + const overlaySpecifier = toRelativeImportSpecifier( + generatedDirectory, + path.join(coreDistPath, 'overlay.js'), + ); + const customToolsScript = generateCustomToolsScript( + customTools, + (specifier) => + toBundledClientImportSpecifier(rootDir, generatedDirectory, specifier), + ); + + const entrySource = ` +import { + ${__AGENTIC_REACT_BRIDGE_URL__}, + ${__AGENTIC_REACT_CONFIG__}, +} from ${JSON.stringify(constSpecifier)}; +import { BRIDGE_WS_PATH } from ${JSON.stringify(protocolSpecifier)}; + +if (typeof window !== 'undefined') { + const existingAgenticReactConfig = window[${__AGENTIC_REACT_CONFIG__}] || {}; + window[${__AGENTIC_REACT_CONFIG__}] = { + ...existingAgenticReactConfig, + sourceRoot: existingAgenticReactConfig.sourceRoot || ${JSON.stringify(rootDir)}, + toolkit: { + ...(existingAgenticReactConfig.toolkit || {}), + ...${JSON.stringify(toolkitConfig)}, + }, + }; + + if (!window[${__AGENTIC_REACT_BRIDGE_URL__}]) { + const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:'; + window[${__AGENTIC_REACT_BRIDGE_URL__}] = + \`\${protocol}//\${window.location.host}\${BRIDGE_WS_PATH}\`; + } +} + +void import(${JSON.stringify(overlaySpecifier)}); + +${customToolsScript} +`; + + fs.mkdirSync(generatedDirectory, { recursive: true }); + if ( + !fs.existsSync(generatedEntryPath) || + fs.readFileSync(generatedEntryPath, 'utf8') !== entrySource + ) { + fs.writeFileSync(generatedEntryPath, entrySource); + } + + return generatedEntryPath; +}; + +const prependWebpackEntry = ( + entryValue: unknown, + prependPath: string, +): unknown => { + if (typeof entryValue === 'string') { + return [prependPath, entryValue]; + } + + if (Array.isArray(entryValue)) { + return entryValue.includes(prependPath) + ? entryValue + : [prependPath, ...entryValue]; + } + + if (entryValue && typeof entryValue === 'object') { + const entryObject = entryValue as Record; + const nextEntryObject: Record = {}; + for (const [key, value] of Object.entries(entryObject)) { + nextEntryObject[key] = prependWebpackEntry(value, prependPath); + } + return nextEntryObject; + } + + return entryValue; +}; + +const LOCAL_COMPONENT_NAME_PATTERN = /^[A-Z][A-Za-z0-9_$]*$/; + +const stripCssEscapes = (value: string): string => + value.replace(/\\([^\r\n])/g, '$1'); + +const pushSourceHint = (hints: string[], hint: string | null | undefined) => { + if (!hint || hint.length < 3 || hints.includes(hint)) return; + hints.push(hint); +}; + +const extractSelectorHints = (selector: string): string[] => { + const hints: string[] = []; + const idMatch = selector.match(/#((?:\\.|[^\s>+~.#:[\]])+)/); + pushSourceHint(hints, idMatch?.[1] ? stripCssEscapes(idMatch[1]) : null); + + const classHints = Array.from( + selector.matchAll(/\.((?:\\.|[^\s>+~.#:[\]])+)/g), + (match) => stripCssEscapes(match[1]), + ); + for (const classHint of classHints.reverse()) { + pushSourceHint(hints, classHint); + } + + return hints; +}; + +const isSourceFileName = (fileName: string): boolean => + /\.(?:jsx?|tsx?)$/i.test(fileName); + +const walkProjectSourceFiles = ( + directory: string, + visit: (filePath: string) => boolean, +): boolean => { + let entries: fs.Dirent[]; + try { + entries = fs.readdirSync(directory, { withFileTypes: true }); + } catch (_error) { + return false; + } + + for (const entry of entries) { + if ( + entry.name === 'node_modules' || + entry.name === '.git' || + entry.name === 'dist' || + entry.name === 'tmp' + ) { + continue; + } + + const entryPath = path.join(directory, entry.name); + if (entry.isDirectory()) { + if (walkProjectSourceFiles(entryPath, visit)) return true; + continue; + } + + if (!isSourceFileName(entry.name)) continue; + if (visit(entryPath)) return true; + } + + return false; +}; + +const findComponentSourceInProject = ( + rootDir: string, + componentName: string, + selector: string, +): SelectionResolvedSource | null => { + if (!LOCAL_COMPONENT_NAME_PATTERN.test(componentName)) return null; + + const declarationPattern = new RegExp( + `(?:function\\s+${componentName}\\b|const\\s+${componentName}\\s*=|class\\s+${componentName}\\b)`, + ); + const hints = extractSelectorHints(selector); + const normalizedRootDir = fs.realpathSync(rootDir); + let matchedSource: SelectionResolvedSource | null = null; + + walkProjectSourceFiles(rootDir, (filePath) => { + let sourceText: string; + try { + sourceText = fs.readFileSync(filePath, 'utf8'); + } catch (_error) { + return false; + } + + if (!declarationPattern.test(sourceText)) return false; + + const lines = sourceText.split(/\r?\n/); + const declarationIndex = lines.findIndex((line) => + declarationPattern.test(line), + ); + let lineIndex = declarationIndex >= 0 ? declarationIndex : 0; + const searchStartIndex = Math.max(0, lineIndex); + const searchRanges = [ + lines.slice(searchStartIndex).map((line, index) => ({ + line, + lineIndex: searchStartIndex + index, + })), + lines.slice(0, searchStartIndex).map((line, index) => ({ + line, + lineIndex: index, + })), + ]; + + for (const hint of hints) { + for (const searchRange of searchRanges) { + const matchedLine = searchRange.find(({ line }) => line.includes(hint)); + if (matchedLine) { + lineIndex = matchedLine.lineIndex; + break; + } + } + if (lineIndex !== declarationIndex) break; + } + + matchedSource = { + filePath: path + .relative(normalizedRootDir, fs.realpathSync(filePath)) + .replace(/\\/g, '/'), + lineNumber: lineIndex + 1, + columnNumber: null, + componentName, + }; + return true; + }); + + return matchedSource; +}; + +const createSourceLookupMiddleware = (rootDir: string) => ({ + name: 'agentic-react-source-lookup', + path: SOURCE_LOOKUP_PATH, + middleware: (req: any, res: any) => { + if (req.method !== 'GET') { + res.statusCode = 405; + res.end('Method Not Allowed'); + return; + } + + const requestUrl = new URL( + req.url || SOURCE_LOOKUP_PATH, + 'http://127.0.0.1', + ); + const componentName = requestUrl.searchParams.get('component') || ''; + const selector = requestUrl.searchParams.get('selector') || ''; + const source = findComponentSourceInProject( + rootDir, + componentName, + selector, + ); + + if (!source) { + res.statusCode = 404; + res.end('Not Found'); + return; + } + + res.setHeader('Content-Type', 'application/json; charset=utf-8'); + res.end(JSON.stringify(source)); + }, +}); + +const createMcpMiddlewares = (createMcpServer: () => Server) => { + return [ + { + name: 'agentic-react-mcp', + path: '/mcp', + middleware: createStreamableHttpMcpHandler(createMcpServer), + }, + ]; +}; + +export const withAgenticReactWebpack = ( + config: Record, + env: WebpackEnv = {}, + options: AgenticReactWebpackOptions = {}, +) => { + const isDevelopmentMode = env.mode ? env.mode === 'development' : true; + if (!isDevelopmentMode) { + return config; + } + + const nextConfig = { ...config }; + const rootDir = + options.rootDir || + (typeof nextConfig.context === 'string' + ? nextConfig.context + : process.cwd()); + const entryPath = writeWebpackClientEntry( + rootDir, + options.customTools || [], + options.toolkit || {}, + ); + const runtimeBridge = new RuntimeBridgeServer(); + const createMcpServer = () => + initMcpServer(runtimeBridge, rootDir, options.customTools || []); + const mcpMiddlewares = [ + createSourceLookupMiddleware(rootDir), + ...createMcpMiddlewares(createMcpServer), + ]; + + nextConfig.entry = prependWebpackEntry(nextConfig.entry, entryPath); + + const devServer = + nextConfig.devServer && typeof nextConfig.devServer === 'object' + ? { ...(nextConfig.devServer as Record) } + : {}; + const existingSetupMiddlewares = devServer.setupMiddlewares; + const existingOnListening = devServer.onListening; + let bridgeAttached = false; + + devServer.setupMiddlewares = ( + middlewares: unknown[], + webpackDevServer: WebpackDevServer, + ) => { + const configuredMiddlewares = + typeof existingSetupMiddlewares === 'function' + ? existingSetupMiddlewares(middlewares, webpackDevServer) + : middlewares; + + return [...mcpMiddlewares, ...configuredMiddlewares]; + }; + + devServer.onListening = (webpackDevServer: WebpackDevServer) => { + if (!bridgeAttached && webpackDevServer.server) { + runtimeBridge.attach(webpackDevServer.server); + bridgeAttached = true; + } + + if (typeof existingOnListening === 'function') { + existingOnListening(webpackDevServer); + } + }; + + nextConfig.devServer = devServer; + return nextConfig; +}; + +export default withAgenticReactWebpack; +export type { + AgenticReactConfig, + CustomClientFunction, + CustomTool, + JsonValue, + SelectionContext, + SelectionResolvedSource, + SelectionSourceSnippet, + SelectionStackFrame, + ToolkitConfig, + ToolkitOffset, + ToolkitPosition, + ToolkitTuningModalConfig, + ToolkitTuningModalStyle, + ToolkitTuningModalStyleSlot, + ToolkitTuningModalStyleValue, + ToolResultValue, + TuningModalActions, + TuningModalContext, + TuningModalExtension, + TuningModalExtensionCleanup, + TuningModalSlotRenderArgs, + TuningModalWrapArgs, +} from '@agentic-react/core/shared/types'; diff --git a/packages/webpack/tsconfig.json b/packages/webpack/tsconfig.json new file mode 100644 index 0000000..c8d64ee --- /dev/null +++ b/packages/webpack/tsconfig.json @@ -0,0 +1,15 @@ +{ + "compilerOptions": { + "target": "es2018", + "lib": ["esnext", "dom"], + "module": "esnext", + "moduleResolution": "bundler", + "declaration": true, + "strict": true, + "skipLibCheck": true, + "outDir": "dist", + "esModuleInterop": true + }, + "include": ["src/**/*.ts"], + "exclude": ["dist", "node_modules"] +} diff --git a/playground/agentic-react-next-playground/app/globals.css b/playground/agentic-react-next-playground/app/globals.css new file mode 100644 index 0000000..673ba52 --- /dev/null +++ b/playground/agentic-react-next-playground/app/globals.css @@ -0,0 +1,17 @@ +html, +body { + margin: 0; + min-width: 320px; + background: #f5f7fb; +} + +body { + color: #152033; + font-family: + Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, + "Segoe UI", sans-serif; +} + +* { + box-sizing: border-box; +} diff --git a/playground/agentic-react-next-playground/app/layout.jsx b/playground/agentic-react-next-playground/app/layout.jsx new file mode 100644 index 0000000..1156aa6 --- /dev/null +++ b/playground/agentic-react-next-playground/app/layout.jsx @@ -0,0 +1,14 @@ +import 'antd/dist/reset.css'; +import './globals.css'; + +export const metadata = { + title: 'Next Playground', +}; + +export default function RootLayout({ children }) { + return ( + + {children} + + ); +} diff --git a/playground/agentic-react-next-playground/app/page.jsx b/playground/agentic-react-next-playground/app/page.jsx new file mode 100644 index 0000000..c748835 --- /dev/null +++ b/playground/agentic-react-next-playground/app/page.jsx @@ -0,0 +1,403 @@ +'use client'; + +import { useMemo, useState } from 'react'; +import { + ArrowsClockwise, + BellRinging, + BracketsCurly, + ChartLineUp, + CheckCircle, + Code, + Database, + GitBranch, + MagnifyingGlass, + RocketLaunch, + ShieldCheck, +} from '@phosphor-icons/react'; +import { + Alert, + Badge, + Button, + Card, + Col, + ConfigProvider, + Descriptions, + Divider, + Flex, + Form, + Input, + Layout, + Menu, + Progress, + Row, + Segmented, + Space, + Statistic, + Table, + Tabs, + Tag, + Timeline, + Typography, + theme, +} from 'antd'; + +const { Content, Header, Sider } = Layout; +const { Text, Title } = Typography; + +const navigation = [ + { key: 'overview', icon: , label: 'Overview' }, + { key: 'selectors', icon: , label: 'Selectors' }, + { key: 'pipelines', icon: , label: 'Pipelines' }, + { key: 'security', icon: , label: 'Security' }, +]; + +const selectorRows = [ + { + key: 'copy-target', + selector: '#next-copy-target', + owner: 'OperationsNarrative', + confidence: 98, + status: 'release gate', + }, + { + key: 'counter', + selector: '#next-counter', + owner: 'RunbookReplayAction', + confidence: 94, + status: 'stateful', + }, + { + key: 'title', + selector: '#next-title', + owner: 'IncidentCommandHeader', + confidence: 91, + status: 'narrative anchor', + }, +]; + +const incidents = [ + 'Checkout conversion route promoted after hydration variance dropped below the release threshold', + 'Customer-risk panel retained stable selectors after Ant Design token overrides were applied', + 'Runbook replay captured source context from the incident narrative and primary action', + 'Selection source test content for Next.', +]; + +function AntShell({ children }) { + return ( + + {children} + + ); +} + +function IconBadge({ icon, tone = '#2557d6' }) { + return ( + + {icon} + + ); +} + +function MetricTile({ icon, label, value, suffix, tone }) { + return ( + + + + + + + ); +} + +function SectionCard({ title, icon, extra, children }) { + return ( + + + {title} + + } + extra={extra} + > + {children} + + ); +} + +function SelectorTable({ rows }) { + const columns = [ + { + title: 'Selector', + dataIndex: 'selector', + render: (value) => {value}, + }, + { + title: 'Owner', + dataIndex: 'owner', + }, + { + title: 'Confidence', + dataIndex: 'confidence', + render: (value) => , + }, + { + title: 'Status', + dataIndex: 'status', + render: (value) => {value}, + }, + ]; + + return ; +} + +export default function HomePage() { + const [count, setCount] = useState(0); + const [mode, setMode] = useState('Live'); + const [activeNav, setActiveNav] = useState('overview'); + + const health = useMemo(() => Math.min(99, 87 + count), [count]); + + return ( + + + + + } tone="#7dd3fc" /> + Next Ops + + setActiveNav(key)} + /> + + + Runtime bridge + + + Inspecting command-center routes, release gates, and selector provenance. + + + + + + + + + + + Production selector lab + + + + + + + + + + Next.js + Ant Design + Phosphor + + Next Playground + + + Selection source test content for Next. + + + This command center models a real release-readiness workflow: + selector confidence, hydration safety, customer-risk telemetry, + runbook replay, and incident audit trails are all rendered through + nested Ant Design components. + + + } + onClick={() => setCount((value) => value + 1)} + size="large" + type="primary" + > + Count: {count} + + + + + + + + + Release intelligence workspace + + + The seeded data represents a mature product engineering scenario: + a customer-facing deployment is moving through verification while + selectors, source context, alerts, and ownership are reviewed in one + operational surface. + + + + + + + + + } label="Selector health" value={health} suffix="%" tone="#2557d6" /> + + + } label="Hydrated islands" value={12 + count} tone="#7c3aed" /> + + + } label="Open notices" value={3} tone="#b76e00" /> + + + + + + } + extra={{mode}} + > + + + + + }> + ({ + color: index === incidents.length - 1 ? 'green' : 'blue', + content: item, + }))} + /> + ), + }, + { + key: 'form', + label: 'Runbook', + children: ( + + + } defaultValue="#next-copy-target" /> + + + } defaultValue="HomePage" /> + + + ), + }, + ]} + /> + + + + + + + }> + + {[ + 'Hydration stable on account overview, billing, and entitlement routes', + 'Runtime globals injected before runbook replay begins', + 'Copy context available for selectors used in release gates', + 'Selection mode verified against source-owned narrative and action components', + ].map((item) => ( + + + + {item} + + + ))} + + + + + + + + + + + + ); +} diff --git a/playground/agentic-react-next-playground/e2e/runtime.spec.js b/playground/agentic-react-next-playground/e2e/runtime.spec.js new file mode 100644 index 0000000..d5e7af3 --- /dev/null +++ b/playground/agentic-react-next-playground/e2e/runtime.spec.js @@ -0,0 +1,146 @@ +import { expect, test } from '@playwright/test'; + +const bridgePort = process.env.AGENTIC_REACT_NEXT_BRIDGE_PORT || '51426'; +const MCP_SERVER_URL = `http://127.0.0.1:${bridgePort}/mcp`; + +const createMcpClient = async () => { + const [{ Client }, { StreamableHTTPClientTransport }] = await Promise.all([ + import('@modelcontextprotocol/sdk/client/index.js'), + import('@modelcontextprotocol/sdk/client/streamableHttp.js'), + ]); + const client = new Client({ + name: 'next-playground-mcp-e2e', + version: '0.0.0', + }); + const transport = new StreamableHTTPClientTransport(new URL(MCP_SERVER_URL)); + await client.connect(transport); + return { client, transport }; +}; + +const parseToolResponse = (result) => { + const textContent = result.content.find((content) => content.type === 'text'); + if (!textContent || typeof textContent.text !== 'string') { + return null; + } + return JSON.parse(textContent.text); +}; + +test('next playground injects runtime globals', async ({ page }) => { + await page.goto('/'); + await page.waitForFunction(() => window.__AGENTIC_REACT__); + + const runtimeShape = await page.evaluate(() => ({ + hasRuntime: Boolean(window.__AGENTIC_REACT__), + hasTools: Boolean(window.__AGENTIC_REACT_TOOLS__), + hasSelectionMode: typeof window.__AGENTIC_REACT__?.setSelectionMode, + hasGetContext: typeof window.__AGENTIC_REACT__?.getLastSelectionContext, + hasCopyContext: typeof window.__AGENTIC_REACT__?.copyLastSelectionContext, + hasRegisterTuningModalExtension: + typeof window.__AGENTIC_REACT__?.registerTuningModalExtension, + tuningSurfaceClassName: + window.__AGENTIC_REACT_CONFIG__?.toolkit?.tuningModal?.classNames + ?.surface, + tuningPanelClassName: + window.__AGENTIC_REACT_CONFIG__?.toolkit?.tuningModal?.classNames?.panel, + unregisterType: (() => { + const unregister = + window.__AGENTIC_REACT__?.registerTuningModalExtension({ + id: 'next-runtime-smoke', + }); + unregister?.(); + return typeof unregister; + })(), + })); + + expect(runtimeShape.hasRuntime).toBe(true); + expect(runtimeShape.hasTools).toBe(true); + expect(runtimeShape.hasSelectionMode).toBe('function'); + expect(runtimeShape.hasGetContext).toBe('function'); + expect(runtimeShape.hasCopyContext).toBe('function'); + expect(runtimeShape.hasRegisterTuningModalExtension).toBe('function'); + expect(runtimeShape.tuningSurfaceClassName).toBe( + 'next-playground-tuning-surface', + ); + expect(runtimeShape.tuningPanelClassName).toBe( + 'next-playground-tuning-panel', + ); + expect(runtimeShape.unregisterType).toBe('function'); +}); + +test('next playground MCP tools return expected outcomes', async ({ page }) => { + await page.goto('/'); + await page.waitForFunction(() => window.__AGENTIC_REACT__); + + const { client, transport } = await createMcpClient(); + try { + const htmlMatchesRaw = await client.callTool({ + name: 'get-html-elements', + arguments: { + queries: ['next-copy-target'], + maxMatches: 3, + }, + }); + const htmlMatches = parseToolResponse(htmlMatchesRaw); + expect(htmlMatches.success).toBe(true); + expect(htmlMatches.count).toBeGreaterThan(0); + expect(htmlMatches.matches[0].selector).toBe('#next-copy-target'); + + const reactSourceRaw = await client.callTool({ + name: 'get-react-source-code', + arguments: { + queries: ['next-copy-target'], + maxMatches: 3, + includeSourceSnippets: true, + }, + }); + const reactSource = parseToolResponse(reactSourceRaw); + expect(reactSource.success).toBe(true); + expect(reactSource.chosenMatch.selector).toBe('#next-copy-target'); + expect(reactSource.context).toBeTruthy(); + + const noMatchRaw = await client.callTool({ + name: 'get-react-source-code', + arguments: { + queries: ['text-not-present-in-next-playground'], + }, + }); + const noMatch = parseToolResponse(noMatchRaw); + expect(noMatch.success).toBe(false); + expect(noMatch.reason).toContain('No element matched'); + expect(noMatch.context).toBeNull(); + + const enableSelectionRaw = await client.callTool({ + name: 'set-selection-mode', + arguments: { enabled: true }, + }); + const enableSelection = parseToolResponse(enableSelectionRaw); + expect(enableSelection.success).toBe(true); + expect(enableSelection.enabled).toBe(true); + + await page.locator('#next-copy-target').click(); + await page.waitForFunction(() => + window.__AGENTIC_REACT__?.getLastSelectionContext(), + ); + const contextRaw = await client.callTool({ + name: 'get-last-selection-context', + arguments: { + includeSourceSnippets: true, + contextLines: 3, + maxFiles: 3, + }, + }); + const context = parseToolResponse(contextRaw); + expect(context.success).toBe(true); + expect(context.context.selector).toBe('#next-copy-target'); + + const copyRaw = await client.callTool({ + name: 'copy-last-selection-context', + arguments: { format: 'json' }, + }); + const copyResponse = parseToolResponse(copyRaw); + expect(copyResponse.success).toBe(true); + expect(copyResponse.copied).toBe(true); + } finally { + await transport.close(); + } +}); diff --git a/playground/agentic-react-next-playground/next.config.mjs b/playground/agentic-react-next-playground/next.config.mjs new file mode 100644 index 0000000..33a7fd8 --- /dev/null +++ b/playground/agentic-react-next-playground/next.config.mjs @@ -0,0 +1,45 @@ +import withAgenticReactNext from '@agentic-react/next'; + +const bridgePort = Number(process.env.AGENTIC_REACT_NEXT_BRIDGE_PORT || 51426); + +const nextConfig = { + allowedDevOrigins: ['127.0.0.1'], + reactStrictMode: true, +}; + +export default withAgenticReactNext(nextConfig, { + server: { + port: bridgePort, + }, + toolkit: { + tuningModal: { + classNames: { + surface: 'next-playground-tuning-surface', + panel: 'next-playground-tuning-panel', + control: 'next-playground-tuning-control', + }, + tokens: { + panelRadius: '12px', + controlRadius: '9px', + primaryButtonBackground: '#7c2d12', + primaryButtonColor: '#ffffff', + panelShadow: '0 24px 72px rgba(124, 45, 18, 0.2)', + }, + styles: { + surface: { + filter: 'drop-shadow(0 18px 40px rgba(124, 45, 18, 0.14))', + }, + panel: { + border: '1px solid rgba(124, 45, 18, 0.24)', + }, + targetTag: { + background: '#fff7ed', + color: '#7c2d12', + }, + sectionTitle: { + color: '#7c2d12', + }, + }, + }, + }, +}); diff --git a/playground/agentic-react-next-playground/package.json b/playground/agentic-react-next-playground/package.json new file mode 100644 index 0000000..d2e2bc7 --- /dev/null +++ b/playground/agentic-react-next-playground/package.json @@ -0,0 +1,21 @@ +{ + "name": "agentic-react-next-playground", + "private": true, + "type": "module", + "scripts": { + "start": "next dev -H 127.0.0.1 -p 51424", + "build": "next build", + "test:e2e": "pnpm -C ../.. run build && env -u NO_COLOR playwright test" + }, + "dependencies": { + "@phosphor-icons/react": "^2.1.10", + "antd": "^6.3.7", + "next": "15.5.18", + "react": "19.1.1", + "react-dom": "19.1.1", + "@agentic-react/next": "workspace:*" + }, + "devDependencies": { + "@playwright/test": "^1.59.1" + } +} diff --git a/playground/agentic-react-next-playground/playwright.config.js b/playground/agentic-react-next-playground/playwright.config.js new file mode 100644 index 0000000..f80955a --- /dev/null +++ b/playground/agentic-react-next-playground/playwright.config.js @@ -0,0 +1,128 @@ +import path from 'node:path'; +import fs from 'node:fs'; +import net from 'node:net'; +import os from 'node:os'; +import { fileURLToPath } from 'node:url'; +import { defineConfig } from '@playwright/test'; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = path.dirname(__filename); +const host = '127.0.0.1'; +const portStateKey = Buffer.from(__dirname).toString('base64url'); +const portStatePath = path.join( + os.tmpdir(), + `agentic-react-next-playground-${portStateKey}.json`, +); +const portLockPath = path.join( + os.tmpdir(), + `agentic-react-next-playground-${portStateKey}.lock`, +); + +const canListen = (port) => + new Promise((resolve) => { + const server = net.createServer(); + server.once('error', () => resolve(false)); + server.listen(port, host, () => { + server.close(() => resolve(true)); + }); + }); + +const findAvailablePort = async (preferredPort) => { + for (let port = preferredPort; port < preferredPort + 1000; port += 1) { + if (await canListen(port)) { + return port; + } + } + + throw new Error( + `Could not find an available Next bridge port starting at ${preferredPort}.`, + ); +}; + +const readPortState = () => { + try { + const state = JSON.parse(fs.readFileSync(portStatePath, 'utf8')); + if (Date.now() - state.createdAt < 10 * 60 * 1000) { + return Number(state.port); + } + } catch (_error) { + return null; + } + + return null; +}; + +const writePortState = (port) => { + fs.mkdirSync(path.dirname(portStatePath), { recursive: true }); + fs.writeFileSync( + portStatePath, + JSON.stringify({ createdAt: Date.now(), port }, null, 2), + ); +}; + +const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms)); + +const waitForPortState = async () => { + for (let attempt = 0; attempt < 100; attempt += 1) { + const port = readPortState(); + if (port) { + return port; + } + await sleep(100); + } + + throw new Error('Timed out waiting for Next bridge port allocation.'); +}; + +const allocatePlaywrightBridgePort = async () => { + const existingPort = readPortState(); + if (existingPort) { + return existingPort; + } + + fs.mkdirSync(path.dirname(portStatePath), { recursive: true }); + let lockHandle; + try { + lockHandle = fs.openSync(portLockPath, 'wx'); + } catch (_error) { + return await waitForPortState(); + } + + try { + const port = await findAvailablePort( + Number(process.env.AGENTIC_REACT_NEXT_BRIDGE_PORT || 51426), + ); + writePortState(port); + return port; + } finally { + fs.closeSync(lockHandle); + fs.rmSync(portLockPath, { force: true }); + } +}; + +const bridgePort = await allocatePlaywrightBridgePort(); +process.env.AGENTIC_REACT_NEXT_BRIDGE_PORT = String(bridgePort); + +export default defineConfig({ + testDir: './e2e', + timeout: 30000, + expect: { + timeout: 5000, + }, + use: { + baseURL: 'http://127.0.0.1:51424', + permissions: ['clipboard-read', 'clipboard-write'], + trace: 'on-first-retry', + }, + webServer: { + command: 'pnpm start', + cwd: __dirname, + env: { + ...process.env, + AGENTIC_REACT_NEXT_BRIDGE_PORT: String(bridgePort), + }, + url: 'http://127.0.0.1:51424', + timeout: 180000, + reuseExistingServer: false, + }, +}); diff --git a/playground/agentic-react-next-playground/public/generated/runtime-command-center.png b/playground/agentic-react-next-playground/public/generated/runtime-command-center.png new file mode 100644 index 0000000..bf1ac60 Binary files /dev/null and b/playground/agentic-react-next-playground/public/generated/runtime-command-center.png differ diff --git a/playground/agentic-react-nx-module-federation-playground/.agentic-react-webpack/client-entry.mjs b/playground/agentic-react-nx-module-federation-playground/.agentic-react-webpack/client-entry.mjs new file mode 100644 index 0000000..203f364 --- /dev/null +++ b/playground/agentic-react-nx-module-federation-playground/.agentic-react-webpack/client-entry.mjs @@ -0,0 +1,47 @@ + +import { + __AGENTIC_REACT_BRIDGE_URL__, + __AGENTIC_REACT_CONFIG__, +} from "../../../packages/core/dist/shared/const.js"; +import { BRIDGE_WS_PATH } from "../../../packages/core/dist/shared/protocol.js"; + +if (typeof window !== 'undefined') { + const existingAgenticReactConfig = window[__AGENTIC_REACT_CONFIG__] || {}; + window[__AGENTIC_REACT_CONFIG__] = { + ...existingAgenticReactConfig, + sourceRoot: existingAgenticReactConfig.sourceRoot || "/Users/jazelly/Desktop/github/my-proj/vite-react-mcp/playground/agentic-react-nx-module-federation-playground", + toolkit: { + ...(existingAgenticReactConfig.toolkit || {}), + ...{"tuningModal":{"classNames":{"surface":"nx-shell-tuning-surface","panel":"nx-shell-tuning-panel","control":"nx-shell-tuning-control"},"tokens":{"panelRadius":"12px","controlRadius":"9px","primaryButtonBackground":"#4338ca","primaryButtonColor":"#ffffff","panelShadow":"0 24px 72px rgba(67, 56, 202, 0.22)"},"styles":{"surface":{"filter":"drop-shadow(0 18px 40px rgba(67, 56, 202, 0.15))"},"panel":{"border":"1px solid rgba(67, 56, 202, 0.24)"},"targetTag":{"background":"#eef2ff","color":"#4338ca"},"sectionTitle":{"color":"#4338ca"}}}}, + }, + }; + + if (!window[__AGENTIC_REACT_BRIDGE_URL__]) { + const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:'; + window[__AGENTIC_REACT_BRIDGE_URL__] = + `${protocol}//${window.location.host}${BRIDGE_WS_PATH}`; + } +} + +void import("../../../packages/core/dist/overlay.js"); + + + const registerTool = (name, handler) => { + const tryRegister = (attempt = 0) => { + const registry = window.__AGENTIC_REACT_TOOLS__; + if (registry?.registerCustomTool) { + registry.registerCustomTool(name, handler); + return; + } + if (attempt >= 40) { + console.warn('[agentic-react] Custom tool registration timed out for', name); + return; + } + setTimeout(() => tryRegister(attempt + 1), 50); + }; + + tryRegister(); + }; + + + diff --git a/playground/agentic-react-nx-module-federation-playground/.eslintrc.json b/playground/agentic-react-nx-module-federation-playground/.eslintrc.json new file mode 100644 index 0000000..84232aa --- /dev/null +++ b/playground/agentic-react-nx-module-federation-playground/.eslintrc.json @@ -0,0 +1,11 @@ +{ + "root": true, + "ignorePatterns": ["**/*"], + "plugins": ["@nrwl/nx"], + "overrides": [ + { + "files": ["*.ts", "*.tsx", "*.js", "*.jsx"], + "rules": {} + } + ] +} diff --git a/playground/agentic-react-nx-module-federation-playground/apps/catalog/.babelrc b/playground/agentic-react-nx-module-federation-playground/apps/catalog/.babelrc new file mode 100644 index 0000000..3c53b4f --- /dev/null +++ b/playground/agentic-react-nx-module-federation-playground/apps/catalog/.babelrc @@ -0,0 +1,17 @@ +{ + "presets": [ + [ + "@babel/preset-env", + { + "targets": ">0.25%, not dead" + } + ], + [ + "@babel/preset-react", + { + "runtime": "automatic" + } + ], + "@babel/preset-typescript" + ] +} diff --git a/playground/agentic-react-nx-module-federation-playground/apps/catalog/module-federation.config.js b/playground/agentic-react-nx-module-federation-playground/apps/catalog/module-federation.config.js new file mode 100644 index 0000000..ff4c580 --- /dev/null +++ b/playground/agentic-react-nx-module-federation-playground/apps/catalog/module-federation.config.js @@ -0,0 +1,17 @@ +module.exports = { + name: 'catalog', + exposes: { + './Routes': './src/app/Routes.tsx', + }, + shared: (libraryName, defaultConfig) => { + if (['react', 'react-dom', 'react-router-dom'].includes(libraryName)) { + return { + ...defaultConfig, + singleton: true, + strictVersion: true, + }; + } + + return defaultConfig; + }, +}; diff --git a/playground/agentic-react-nx-module-federation-playground/apps/catalog/project.json b/playground/agentic-react-nx-module-federation-playground/apps/catalog/project.json new file mode 100644 index 0000000..b958518 --- /dev/null +++ b/playground/agentic-react-nx-module-federation-playground/apps/catalog/project.json @@ -0,0 +1,66 @@ +{ + "name": "catalog", + "$schema": "../../node_modules/nx/schemas/project-schema.json", + "sourceRoot": "apps/catalog/src", + "projectType": "application", + "targets": { + "build": { + "executor": "@nrwl/webpack:webpack", + "outputs": ["{options.outputPath}"], + "defaultConfiguration": "production", + "options": { + "compiler": "babel", + "outputPath": "dist/apps/catalog", + "index": "apps/catalog/src/index.html", + "baseHref": "/", + "main": "apps/catalog/src/main.ts", + "polyfills": "apps/catalog/src/polyfills.ts", + "tsConfig": "apps/catalog/tsconfig.app.json", + "assets": ["apps/catalog/src/assets"], + "styles": ["apps/catalog/src/styles.css"], + "scripts": [], + "webpackConfig": "apps/catalog/webpack.config.js" + }, + "configurations": { + "development": { + "extractLicenses": false, + "optimization": false, + "sourceMap": true, + "vendorChunk": true + }, + "production": { + "extractLicenses": true, + "optimization": true, + "sourceMap": false, + "vendorChunk": false + } + } + }, + "serve": { + "executor": "@nrwl/webpack:dev-server", + "defaultConfiguration": "development", + "options": { + "buildTarget": "catalog:build", + "host": "127.0.0.1", + "port": 4201 + }, + "configurations": { + "development": { + "buildTarget": "catalog:build:development" + }, + "production": { + "buildTarget": "catalog:build:production" + } + } + }, + "dev": { + "executor": "@nrwl/webpack:dev-server", + "options": { + "buildTarget": "catalog:build:development", + "host": "127.0.0.1", + "port": 4201 + } + } + }, + "tags": ["scope:remote", "type:app"] +} diff --git a/playground/agentic-react-nx-module-federation-playground/apps/catalog/src/app/Routes.tsx b/playground/agentic-react-nx-module-federation-playground/apps/catalog/src/app/Routes.tsx new file mode 100644 index 0000000..718f856 --- /dev/null +++ b/playground/agentic-react-nx-module-federation-playground/apps/catalog/src/app/Routes.tsx @@ -0,0 +1,16 @@ +import { ChakraProvider } from '@chakra-ui/react'; +import { hearthHubTheme } from '@mfplayground/hearthhub-ui'; +import { Route, Routes } from 'react-router-dom'; +import { CatalogIndex } from './pages/CatalogIndex'; +import { ProductDetail } from './pages/ProductDetail'; + +export default function CatalogRoutes() { + return ( + + + } /> + } /> + + + ); +} diff --git a/playground/agentic-react-nx-module-federation-playground/apps/catalog/src/app/components/CatalogSummaryCards.tsx b/playground/agentic-react-nx-module-federation-playground/apps/catalog/src/app/components/CatalogSummaryCards.tsx new file mode 100644 index 0000000..7fcd255 --- /dev/null +++ b/playground/agentic-react-nx-module-federation-playground/apps/catalog/src/app/components/CatalogSummaryCards.tsx @@ -0,0 +1,29 @@ +import { SimpleGrid, Text } from '@chakra-ui/react'; +import { ServerStackIcon, ShieldCheckIcon } from '@heroicons/react/24/outline'; +import { + MetricCard, + SectionCard, + servicePlans, +} from '@mfplayground/hearthhub-ui'; + +export function CatalogSummaryCards() { + return ( + + + catalog/Routes + service marketplace module + + + + + Chakra tables, plan links, badges, and progress rows + + source aware + + + ); +} diff --git a/playground/agentic-react-nx-module-federation-playground/apps/catalog/src/app/components/MarketplaceQualityBar.tsx b/playground/agentic-react-nx-module-federation-playground/apps/catalog/src/app/components/MarketplaceQualityBar.tsx new file mode 100644 index 0000000..2773d48 --- /dev/null +++ b/playground/agentic-react-nx-module-federation-playground/apps/catalog/src/app/components/MarketplaceQualityBar.tsx @@ -0,0 +1,24 @@ +import { Divider, List, ListIcon, ListItem, Text } from '@chakra-ui/react'; +import { CheckCircleIcon, SparklesIcon } from '@heroicons/react/24/outline'; +import { SectionCard, vendorSignals } from '@mfplayground/hearthhub-ui'; + +export function MarketplaceQualityBar() { + return ( + + + {vendorSignals.map((item) => ( + + + {item} + + ))} + + + + The catalog remote can evolve pricing, taxonomy, and service + merchandising independently while the shell preserves a stable customer + navigation model. + + + ); +} diff --git a/playground/agentic-react-nx-module-federation-playground/apps/catalog/src/app/components/ServicePlanTable.tsx b/playground/agentic-react-nx-module-federation-playground/apps/catalog/src/app/components/ServicePlanTable.tsx new file mode 100644 index 0000000..f3b8ac9 --- /dev/null +++ b/playground/agentic-react-nx-module-federation-playground/apps/catalog/src/app/components/ServicePlanTable.tsx @@ -0,0 +1,73 @@ +import { + Badge, + Button, + Icon, + Table, + Tbody, + Td, + Text, + Th, + Thead, + Tr, +} from '@chakra-ui/react'; +import { + ArrowTopRightOnSquareIcon, + CubeIcon, +} from '@heroicons/react/24/outline'; +import { AppIcon, SectionCard, servicePlans } from '@mfplayground/hearthhub-ui'; +import { Link } from 'react-router-dom'; + +export function ServicePlanTable() { + return ( + + + + + Plan + Owner + Price + Status + Open + + + + {servicePlans.map((plan) => ( + + + } + size="sm" + to={plan.id} + variant="link" + > + {plan.name} + + + {plan.technicalName} + + + {plan.owner} + {plan.price} + + + {plan.status} + + + + + + + ))} + + + + ); +} diff --git a/playground/agentic-react-nx-module-federation-playground/apps/catalog/src/app/pages/CatalogIndex.tsx b/playground/agentic-react-nx-module-federation-playground/apps/catalog/src/app/pages/CatalogIndex.tsx new file mode 100644 index 0000000..3d512f6 --- /dev/null +++ b/playground/agentic-react-nx-module-federation-playground/apps/catalog/src/app/pages/CatalogIndex.tsx @@ -0,0 +1,31 @@ +import { Grid, Stack } from '@chakra-ui/react'; +import { WrenchScrewdriverIcon } from '@heroicons/react/24/outline'; +import { RemotePageHeader } from '@mfplayground/hearthhub-ui'; +import { LegacyRuntimeBadge } from '@mfplayground/legacy-react-widget'; +import { CatalogSummaryCards } from '../components/CatalogSummaryCards'; +import { MarketplaceQualityBar } from '../components/MarketplaceQualityBar'; +import { ServicePlanTable } from '../components/ServicePlanTable'; + +export function CatalogIndex() { + return ( + + } + icon={WrenchScrewdriverIcon} + label="Catalog remote" + title="Federated product routes." + > + The services marketplace team owns the plan inventory, vendor + availability, enrollment copy, and detail pages that help a household + choose practical coverage before a repair becomes urgent. + + + + + + + + + + ); +} diff --git a/playground/agentic-react-nx-module-federation-playground/apps/catalog/src/app/pages/ProductDetail.tsx b/playground/agentic-react-nx-module-federation-playground/apps/catalog/src/app/pages/ProductDetail.tsx new file mode 100644 index 0000000..9698f06 --- /dev/null +++ b/playground/agentic-react-nx-module-federation-playground/apps/catalog/src/app/pages/ProductDetail.tsx @@ -0,0 +1,76 @@ +import { + Badge, + Button, + Card, + CardBody, + Grid, + HStack, + Heading, + Icon, + Progress, + Text, +} from '@chakra-ui/react'; +import { ArrowLeftIcon, CurrencyDollarIcon } from '@heroicons/react/24/outline'; +import { SectionCard, servicePlans } from '@mfplayground/hearthhub-ui'; +import { Link, useParams } from 'react-router-dom'; + +export function ProductDetail() { + const params = useParams(); + const selectedPlan = + servicePlans.find((plan) => plan.id === params.productId) ?? + servicePlans[0]; + + return ( + + + + + Remote detail + + + {selectedPlan.name} + + + {selectedPlan.summary} + + + {selectedPlan.status} + {selectedPlan.price} + + } + mt={6} + to="/catalog" + > + Back to services + + + + + + + {selectedPlan.technicalName} + {selectedPlan.readiness}% + + + + This route is intentionally owned by the catalog remote so runtime MCP + tools can inspect a real customer workflow across the host boundary + while preserving service ownership, plan copy, and source-context + routing. + + + + ); +} diff --git a/playground/agentic-react-nx-module-federation-playground/apps/catalog/src/assets/.gitkeep b/playground/agentic-react-nx-module-federation-playground/apps/catalog/src/assets/.gitkeep new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/playground/agentic-react-nx-module-federation-playground/apps/catalog/src/assets/.gitkeep @@ -0,0 +1 @@ + diff --git a/playground/agentic-react-nx-module-federation-playground/apps/catalog/src/bootstrap.tsx b/playground/agentic-react-nx-module-federation-playground/apps/catalog/src/bootstrap.tsx new file mode 100644 index 0000000..3d2a462 --- /dev/null +++ b/playground/agentic-react-nx-module-federation-playground/apps/catalog/src/bootstrap.tsx @@ -0,0 +1,16 @@ +import { createRoot } from 'react-dom/client'; +import { MemoryRouter } from 'react-router-dom'; +import Routes from './app/Routes'; +import './styles.css'; + +const rootElement = document.getElementById('root'); + +if (!rootElement) { + throw new Error('Missing #root element for catalog remote'); +} + +createRoot(rootElement).render( + + + , +); diff --git a/playground/agentic-react-nx-module-federation-playground/apps/catalog/src/index.html b/playground/agentic-react-nx-module-federation-playground/apps/catalog/src/index.html new file mode 100644 index 0000000..c6b5fa7 --- /dev/null +++ b/playground/agentic-react-nx-module-federation-playground/apps/catalog/src/index.html @@ -0,0 +1,12 @@ + + + + + Nx Catalog Remote + + + + + + + diff --git a/playground/agentic-react-nx-module-federation-playground/apps/catalog/src/main.ts b/playground/agentic-react-nx-module-federation-playground/apps/catalog/src/main.ts new file mode 100644 index 0000000..b93c7a0 --- /dev/null +++ b/playground/agentic-react-nx-module-federation-playground/apps/catalog/src/main.ts @@ -0,0 +1 @@ +import('./bootstrap'); diff --git a/playground/agentic-react-nx-module-federation-playground/apps/catalog/src/polyfills.ts b/playground/agentic-react-nx-module-federation-playground/apps/catalog/src/polyfills.ts new file mode 100644 index 0000000..cb0ff5c --- /dev/null +++ b/playground/agentic-react-nx-module-federation-playground/apps/catalog/src/polyfills.ts @@ -0,0 +1 @@ +export {}; diff --git a/playground/agentic-react-nx-module-federation-playground/apps/catalog/src/styles.css b/playground/agentic-react-nx-module-federation-playground/apps/catalog/src/styles.css new file mode 100644 index 0000000..f4f93b2 --- /dev/null +++ b/playground/agentic-react-nx-module-federation-playground/apps/catalog/src/styles.css @@ -0,0 +1,127 @@ +:root { + color: #172033; + background: #f7f8fb; + font-family: + Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, + "Segoe UI", sans-serif; +} + +body { + margin: 0; + background: #f7f8fb; +} + +.remote-panel { + margin: 24px; + padding: 32px; + border: 1px solid #dbe3ef; + border-radius: 8px; + background: #ffffff; +} + +.remote-header { + display: flex; + align-items: flex-start; + justify-content: space-between; + gap: 24px; +} + +.section-label { + margin: 0 0 10px; + color: #2563eb; + font-size: 13px; + font-weight: 800; + letter-spacing: 0; + text-transform: uppercase; +} + +h2 { + margin: 0; + color: #0f172a; + font-size: clamp(30px, 4vw, 48px); + line-height: 1; +} + +.product-table { + width: 100%; + margin-top: 28px; + overflow: hidden; + border: 1px solid #dbe3ef; + border-radius: 8px; + border-collapse: separate; + border-spacing: 0; +} + +.product-table caption { + position: absolute; + width: 1px; + height: 1px; + overflow: hidden; + clip: rect(0 0 0 0); +} + +.product-row th, +.product-row td { + min-height: 56px; + border-top: 1px solid #e5edf6; + color: #334155; + padding: 0 18px; + text-align: left; + vertical-align: middle; +} + +.product-row:first-child th { + border-top: 0; +} + +.product-row.heading { + background: #f8fafc; + color: #64748b; + font-size: 13px; + font-weight: 800; + text-transform: uppercase; +} + +.product-row:not(.heading):hover td { + background: #eff6ff; +} + +.product-row a { + color: #1d4ed8; + font-weight: 800; + text-decoration: none; +} + +.remote-copy { + max-width: 680px; + color: #475569; + font-size: 18px; + line-height: 1.7; +} + +.back-link { + color: #1d4ed8; + font-weight: 800; +} + +@media (max-width: 720px) { + .remote-panel { + margin: 0; + padding: 28px; + } + + .remote-header { + flex-direction: column; + } + + .product-row { + min-height: 84px; + } + + .product-row th, + .product-row td { + display: block; + min-height: 0; + padding: 10px 18px; + } +} diff --git a/playground/agentic-react-nx-module-federation-playground/apps/catalog/tsconfig.app.json b/playground/agentic-react-nx-module-federation-playground/apps/catalog/tsconfig.app.json new file mode 100644 index 0000000..4588365 --- /dev/null +++ b/playground/agentic-react-nx-module-federation-playground/apps/catalog/tsconfig.app.json @@ -0,0 +1,9 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "outDir": "../../dist/out-tsc", + "types": [] + }, + "files": ["src/main.ts", "src/polyfills.ts"], + "include": ["src/**/*.ts", "src/**/*.tsx"] +} diff --git a/playground/agentic-react-nx-module-federation-playground/apps/catalog/webpack.config.js b/playground/agentic-react-nx-module-federation-playground/apps/catalog/webpack.config.js new file mode 100644 index 0000000..eea1bba --- /dev/null +++ b/playground/agentic-react-nx-module-federation-playground/apps/catalog/webpack.config.js @@ -0,0 +1,74 @@ +const path = require('node:path'); +const { composePlugins, withNx } = require('@nrwl/webpack'); +const { withReact } = require('@nrwl/react'); +const { merge } = require('webpack-merge'); +const withModuleFederation = require('../../module-federation'); +const { replaceDefinePlugin } = require('../../webpack.config.base'); +const moduleFederationConfig = require('./module-federation.config'); +const workspaceRoot = path.resolve(__dirname, '../..'); +const toolkit = { + tuningModal: { + classNames: { + surface: 'nx-catalog-tuning-surface', + panel: 'nx-catalog-tuning-panel', + control: 'nx-catalog-tuning-control', + }, + tokens: { + panelRadius: '12px', + controlRadius: '9px', + primaryButtonBackground: '#be123c', + primaryButtonColor: '#ffffff', + panelShadow: '0 24px 72px rgba(190, 18, 60, 0.2)', + }, + styles: { + surface: { + filter: 'drop-shadow(0 18px 40px rgba(190, 18, 60, 0.14))', + }, + panel: { + border: '1px solid rgba(190, 18, 60, 0.24)', + }, + targetTag: { + background: '#fff1f2', + color: '#be123c', + }, + sectionTitle: { + color: '#be123c', + }, + }, + }, +}; + +module.exports = composePlugins( + withNx(), + withReact(), + async (config, nxContext) => { + const federatedModules = await withModuleFederation( + moduleFederationConfig, + ); + const { default: withAgenticReactWebpack } = await import('@agentic-react/webpack'); + const mergedConfig = replaceDefinePlugin( + merge(federatedModules(config, nxContext), { + devServer: { + historyApiFallback: true, + client: { + overlay: false, + }, + }, + }), + ); + + return withAgenticReactWebpack( + mergedConfig, + { + mode: + nxContext?.context?.configurationName === 'production' + ? 'production' + : 'development', + }, + { + rootDir: workspaceRoot, + toolkit, + }, + ); + }, +); diff --git a/playground/agentic-react-nx-module-federation-playground/apps/profile/.babelrc b/playground/agentic-react-nx-module-federation-playground/apps/profile/.babelrc new file mode 100644 index 0000000..3c53b4f --- /dev/null +++ b/playground/agentic-react-nx-module-federation-playground/apps/profile/.babelrc @@ -0,0 +1,17 @@ +{ + "presets": [ + [ + "@babel/preset-env", + { + "targets": ">0.25%, not dead" + } + ], + [ + "@babel/preset-react", + { + "runtime": "automatic" + } + ], + "@babel/preset-typescript" + ] +} diff --git a/playground/agentic-react-nx-module-federation-playground/apps/profile/module-federation.config.js b/playground/agentic-react-nx-module-federation-playground/apps/profile/module-federation.config.js new file mode 100644 index 0000000..c8ea3f9 --- /dev/null +++ b/playground/agentic-react-nx-module-federation-playground/apps/profile/module-federation.config.js @@ -0,0 +1,17 @@ +module.exports = { + name: 'profile', + exposes: { + './Routes': './src/app/Routes.tsx', + }, + shared: (libraryName, defaultConfig) => { + if (['react', 'react-dom', 'react-router-dom'].includes(libraryName)) { + return { + ...defaultConfig, + singleton: true, + strictVersion: true, + }; + } + + return defaultConfig; + }, +}; diff --git a/playground/agentic-react-nx-module-federation-playground/apps/profile/project.json b/playground/agentic-react-nx-module-federation-playground/apps/profile/project.json new file mode 100644 index 0000000..4b1e866 --- /dev/null +++ b/playground/agentic-react-nx-module-federation-playground/apps/profile/project.json @@ -0,0 +1,66 @@ +{ + "name": "profile", + "$schema": "../../node_modules/nx/schemas/project-schema.json", + "sourceRoot": "apps/profile/src", + "projectType": "application", + "targets": { + "build": { + "executor": "@nrwl/webpack:webpack", + "outputs": ["{options.outputPath}"], + "defaultConfiguration": "production", + "options": { + "compiler": "babel", + "outputPath": "dist/apps/profile", + "index": "apps/profile/src/index.html", + "baseHref": "/", + "main": "apps/profile/src/main.ts", + "polyfills": "apps/profile/src/polyfills.ts", + "tsConfig": "apps/profile/tsconfig.app.json", + "assets": ["apps/profile/src/assets"], + "styles": ["apps/profile/src/styles.css"], + "scripts": [], + "webpackConfig": "apps/profile/webpack.config.js" + }, + "configurations": { + "development": { + "extractLicenses": false, + "optimization": false, + "sourceMap": true, + "vendorChunk": true + }, + "production": { + "extractLicenses": true, + "optimization": true, + "sourceMap": false, + "vendorChunk": false + } + } + }, + "serve": { + "executor": "@nrwl/webpack:dev-server", + "defaultConfiguration": "development", + "options": { + "buildTarget": "profile:build", + "host": "127.0.0.1", + "port": 4202 + }, + "configurations": { + "development": { + "buildTarget": "profile:build:development" + }, + "production": { + "buildTarget": "profile:build:production" + } + } + }, + "dev": { + "executor": "@nrwl/webpack:dev-server", + "options": { + "buildTarget": "profile:build:development", + "host": "127.0.0.1", + "port": 4202 + } + } + }, + "tags": ["scope:remote", "type:app"] +} diff --git a/playground/agentic-react-nx-module-federation-playground/apps/profile/src/app/Routes.tsx b/playground/agentic-react-nx-module-federation-playground/apps/profile/src/app/Routes.tsx new file mode 100644 index 0000000..00e6f4f --- /dev/null +++ b/playground/agentic-react-nx-module-federation-playground/apps/profile/src/app/Routes.tsx @@ -0,0 +1,14 @@ +import { ChakraProvider } from '@chakra-ui/react'; +import { hearthHubTheme } from '@mfplayground/hearthhub-ui'; +import { Route, Routes } from 'react-router-dom'; +import { ProfileIndex } from './pages/ProfileIndex'; + +export default function ProfileRoutes() { + return ( + + + } /> + + + ); +} diff --git a/playground/agentic-react-nx-module-federation-playground/apps/profile/src/app/components/ExternalDependencySelectionProbe.tsx b/playground/agentic-react-nx-module-federation-playground/apps/profile/src/app/components/ExternalDependencySelectionProbe.tsx new file mode 100644 index 0000000..cbb2d3f --- /dev/null +++ b/playground/agentic-react-nx-module-federation-playground/apps/profile/src/app/components/ExternalDependencySelectionProbe.tsx @@ -0,0 +1,44 @@ +import { + Button, + Card, + CardBody, + HStack, + Heading, + Text, +} from '@chakra-ui/react'; +import { BoltIcon } from '@heroicons/react/24/outline'; +import { AppIcon } from '@mfplayground/hearthhub-ui'; + +export function ExternalDependencySelectionProbe() { + return ( + + + + + + + External dependency selection + + This local profile component intentionally renders a Chakra UI + button so selector tests can attribute a package component back + to the project file that uses it. + + + + + Review access note + + + + + ); +} diff --git a/playground/agentic-react-nx-module-federation-playground/apps/profile/src/app/components/HouseholdContacts.tsx b/playground/agentic-react-nx-module-federation-playground/apps/profile/src/app/components/HouseholdContacts.tsx new file mode 100644 index 0000000..35e3cec --- /dev/null +++ b/playground/agentic-react-nx-module-federation-playground/apps/profile/src/app/components/HouseholdContacts.tsx @@ -0,0 +1,33 @@ +import { Card, CardBody, HStack, Heading, Stack } from '@chakra-ui/react'; +import { UsersIcon } from '@heroicons/react/24/outline'; +import { IconBadge, householdMembers } from '@mfplayground/hearthhub-ui'; +import { ProfileMemberCard } from './ProfileMemberCard'; + +export function HouseholdContacts() { + return ( + + + + + Household contacts + + + {householdMembers.map((member) => ( + + ))} + + + + ); +} diff --git a/playground/agentic-react-nx-module-federation-playground/apps/profile/src/app/components/HouseholdProfileSummary.tsx b/playground/agentic-react-nx-module-federation-playground/apps/profile/src/app/components/HouseholdProfileSummary.tsx new file mode 100644 index 0000000..0d0050f --- /dev/null +++ b/playground/agentic-react-nx-module-federation-playground/apps/profile/src/app/components/HouseholdProfileSummary.tsx @@ -0,0 +1,81 @@ +import { + Card, + CardBody, + Divider, + HStack, + Heading, + List, + ListIcon, + ListItem, + Tag, + TagLabel, + Text, + VStack, +} from '@chakra-ui/react'; +import { + CheckCircleIcon, + HomeModernIcon, + KeyIcon, +} from '@heroicons/react/24/outline'; +import { IconBadge, householdPreferences } from '@mfplayground/hearthhub-ui'; + +export function HouseholdProfileSummary() { + return ( + + + + + + + Household profile summary + + + + Two-story family home, home office on the ground floor, nursery + upstairs, mixed indoor/outdoor access, and recurring seasonal care + through the Family Plus plan. + + + + {[ + 'Side gate access', + 'Pet-aware visits', + 'Quiet hours', + 'Text first', + ].map((tag) => ( + + {tag} + + ))} + + + + + + + + + Access and care preferences + + + {householdPreferences.map((preference) => ( + + + {preference} + + ))} + + + + + ); +} diff --git a/playground/agentic-react-nx-module-federation-playground/apps/profile/src/app/components/PreferencePanel.tsx b/playground/agentic-react-nx-module-federation-playground/apps/profile/src/app/components/PreferencePanel.tsx new file mode 100644 index 0000000..bd2b891 --- /dev/null +++ b/playground/agentic-react-nx-module-federation-playground/apps/profile/src/app/components/PreferencePanel.tsx @@ -0,0 +1,32 @@ +import { Card, CardBody, HStack, Heading, Text } from '@chakra-ui/react'; +import { AppIcon, type IconComponent } from '@mfplayground/hearthhub-ui'; +import type { ReactNode } from 'react'; + +export function PreferencePanel({ + children, + icon, + title, +}: { + children: ReactNode; + icon: IconComponent; + title: string; +}) { + return ( + + + + + {title} + + + {children} + + + + ); +} diff --git a/playground/agentic-react-nx-module-federation-playground/apps/profile/src/app/components/ProfileMemberCard.tsx b/playground/agentic-react-nx-module-federation-playground/apps/profile/src/app/components/ProfileMemberCard.tsx new file mode 100644 index 0000000..374f3f2 --- /dev/null +++ b/playground/agentic-react-nx-module-federation-playground/apps/profile/src/app/components/ProfileMemberCard.tsx @@ -0,0 +1,54 @@ +import { Avatar, Box, CardBody, HStack, Heading, Text } from '@chakra-ui/react'; +import { SparklesIcon } from '@heroicons/react/24/outline'; +import { IconBadge } from '@mfplayground/hearthhub-ui'; + +export function ProfileMemberCard({ + focus, + name, + note, + role, +}: { + focus: string; + name: string; + note: string; + role: string; +}) { + const memberId = name.toLowerCase().replace(/\s+/g, '-'); + const selectionId = `profile-member-${memberId}`; + + return ( + + + + + + + + {name} + + + {role} + + + {focus} + + + {note} + + + + + + + + ); +} diff --git a/playground/agentic-react-nx-module-federation-playground/apps/profile/src/app/pages/ProfileIndex.tsx b/playground/agentic-react-nx-module-federation-playground/apps/profile/src/app/pages/ProfileIndex.tsx new file mode 100644 index 0000000..abe6d91 --- /dev/null +++ b/playground/agentic-react-nx-module-federation-playground/apps/profile/src/app/pages/ProfileIndex.tsx @@ -0,0 +1,81 @@ +import { SimpleGrid, Stack } from '@chakra-ui/react'; +import { + AcademicCapIcon, + BoltIcon, + MapPinIcon, + ShieldCheckIcon, + UserCircleIcon, +} from '@heroicons/react/24/outline'; +import { + MetricCard, + RemotePageHeader, + householdMembers, +} from '@mfplayground/hearthhub-ui'; +import { ExternalDependencySelectionProbe } from '../components/ExternalDependencySelectionProbe'; +import { HouseholdContacts } from '../components/HouseholdContacts'; +import { HouseholdProfileSummary } from '../components/HouseholdProfileSummary'; +import { PreferencePanel } from '../components/PreferencePanel'; + +export function ProfileIndex() { + return ( + + + The profile remote owns the household context that makes every service + visit feel prepared: authorized contacts, access instructions, care + preferences, pet notes, and inspection-safe member cards for MCP + selection across a remote boundary. + + + + + + + + + + + + + + + + + + The Sam Rivera card keeps a stable id for cross-remote MCP selection + while still being wrapped by production-like Chakra layout, avatar, + typography, and Heroicon components. + + + Nested Chakra cards, tags, stats, lists, avatars, and icon wrappers + remain visible to component tree, selection context, highlight, and + source-code tools. + + + Household notes are specific enough for service coordination but + scoped to operational needs rather than exposing unnecessary personal + detail. + + + The profile remote can change household data capture without requiring + the services catalog team to redeploy its marketplace routes. + + + + ); +} diff --git a/playground/agentic-react-nx-module-federation-playground/apps/profile/src/assets/.gitkeep b/playground/agentic-react-nx-module-federation-playground/apps/profile/src/assets/.gitkeep new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/playground/agentic-react-nx-module-federation-playground/apps/profile/src/assets/.gitkeep @@ -0,0 +1 @@ + diff --git a/playground/agentic-react-nx-module-federation-playground/apps/profile/src/bootstrap.tsx b/playground/agentic-react-nx-module-federation-playground/apps/profile/src/bootstrap.tsx new file mode 100644 index 0000000..6f31849 --- /dev/null +++ b/playground/agentic-react-nx-module-federation-playground/apps/profile/src/bootstrap.tsx @@ -0,0 +1,16 @@ +import { createRoot } from 'react-dom/client'; +import { MemoryRouter } from 'react-router-dom'; +import Routes from './app/Routes'; +import './styles.css'; + +const rootElement = document.getElementById('root'); + +if (!rootElement) { + throw new Error('Missing #root element for profile remote'); +} + +createRoot(rootElement).render( + + + , +); diff --git a/playground/agentic-react-nx-module-federation-playground/apps/profile/src/index.html b/playground/agentic-react-nx-module-federation-playground/apps/profile/src/index.html new file mode 100644 index 0000000..a537381 --- /dev/null +++ b/playground/agentic-react-nx-module-federation-playground/apps/profile/src/index.html @@ -0,0 +1,12 @@ + + + + + Nx Profile Remote + + + + + + + diff --git a/playground/agentic-react-nx-module-federation-playground/apps/profile/src/main.ts b/playground/agentic-react-nx-module-federation-playground/apps/profile/src/main.ts new file mode 100644 index 0000000..b93c7a0 --- /dev/null +++ b/playground/agentic-react-nx-module-federation-playground/apps/profile/src/main.ts @@ -0,0 +1 @@ +import('./bootstrap'); diff --git a/playground/agentic-react-nx-module-federation-playground/apps/profile/src/polyfills.ts b/playground/agentic-react-nx-module-federation-playground/apps/profile/src/polyfills.ts new file mode 100644 index 0000000..cb0ff5c --- /dev/null +++ b/playground/agentic-react-nx-module-federation-playground/apps/profile/src/polyfills.ts @@ -0,0 +1 @@ +export {}; diff --git a/playground/agentic-react-nx-module-federation-playground/apps/profile/src/styles.css b/playground/agentic-react-nx-module-federation-playground/apps/profile/src/styles.css new file mode 100644 index 0000000..2352566 --- /dev/null +++ b/playground/agentic-react-nx-module-federation-playground/apps/profile/src/styles.css @@ -0,0 +1,60 @@ +:root { + color: #172033; + background: #f7f8fb; + font-family: + Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, + "Segoe UI", sans-serif; +} + +body { + margin: 0; + background: #f7f8fb; +} + +.profile-panel { + margin: 24px; + padding: 32px; + border: 1px solid #dbe3ef; + border-radius: 8px; + background: #ffffff; +} + +.section-label { + margin: 0 0 10px; + color: #2563eb; + font-size: 13px; + font-weight: 800; + letter-spacing: 0; + text-transform: uppercase; +} + +h2 { + margin: 0; + color: #0f172a; + font-size: clamp(30px, 4vw, 48px); + line-height: 1; +} + +.profile-list { + display: grid; + gap: 12px; + margin-top: 28px; +} + +.profile-list article { + display: flex; + align-items: center; + justify-content: space-between; + min-height: 64px; + border: 1px solid #dbe3ef; + border-radius: 8px; + padding: 0 18px; +} + +.profile-list strong { + color: #111827; +} + +.profile-list span { + color: #475569; +} diff --git a/playground/agentic-react-nx-module-federation-playground/apps/profile/tsconfig.app.json b/playground/agentic-react-nx-module-federation-playground/apps/profile/tsconfig.app.json new file mode 100644 index 0000000..4588365 --- /dev/null +++ b/playground/agentic-react-nx-module-federation-playground/apps/profile/tsconfig.app.json @@ -0,0 +1,9 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "outDir": "../../dist/out-tsc", + "types": [] + }, + "files": ["src/main.ts", "src/polyfills.ts"], + "include": ["src/**/*.ts", "src/**/*.tsx"] +} diff --git a/playground/agentic-react-nx-module-federation-playground/apps/profile/webpack.config.js b/playground/agentic-react-nx-module-federation-playground/apps/profile/webpack.config.js new file mode 100644 index 0000000..5366f09 --- /dev/null +++ b/playground/agentic-react-nx-module-federation-playground/apps/profile/webpack.config.js @@ -0,0 +1,74 @@ +const path = require('node:path'); +const { composePlugins, withNx } = require('@nrwl/webpack'); +const { withReact } = require('@nrwl/react'); +const { merge } = require('webpack-merge'); +const withModuleFederation = require('../../module-federation'); +const { replaceDefinePlugin } = require('../../webpack.config.base'); +const moduleFederationConfig = require('./module-federation.config'); +const workspaceRoot = path.resolve(__dirname, '../..'); +const toolkit = { + tuningModal: { + classNames: { + surface: 'nx-profile-tuning-surface', + panel: 'nx-profile-tuning-panel', + control: 'nx-profile-tuning-control', + }, + tokens: { + panelRadius: '12px', + controlRadius: '9px', + primaryButtonBackground: '#0f766e', + primaryButtonColor: '#ffffff', + panelShadow: '0 24px 72px rgba(15, 118, 110, 0.22)', + }, + styles: { + surface: { + filter: 'drop-shadow(0 18px 40px rgba(15, 118, 110, 0.15))', + }, + panel: { + border: '1px solid rgba(15, 118, 110, 0.24)', + }, + targetTag: { + background: '#ecfeff', + color: '#0f766e', + }, + sectionTitle: { + color: '#0f766e', + }, + }, + }, +}; + +module.exports = composePlugins( + withNx(), + withReact(), + async (config, nxContext) => { + const federatedModules = await withModuleFederation( + moduleFederationConfig, + ); + const { default: withAgenticReactWebpack } = await import('@agentic-react/webpack'); + const mergedConfig = replaceDefinePlugin( + merge(federatedModules(config, nxContext), { + devServer: { + historyApiFallback: true, + client: { + overlay: false, + }, + }, + }), + ); + + return withAgenticReactWebpack( + mergedConfig, + { + mode: + nxContext?.context?.configurationName === 'production' + ? 'production' + : 'development', + }, + { + rootDir: workspaceRoot, + toolkit, + }, + ); + }, +); diff --git a/playground/agentic-react-nx-module-federation-playground/apps/shell/.babelrc b/playground/agentic-react-nx-module-federation-playground/apps/shell/.babelrc new file mode 100644 index 0000000..3c53b4f --- /dev/null +++ b/playground/agentic-react-nx-module-federation-playground/apps/shell/.babelrc @@ -0,0 +1,17 @@ +{ + "presets": [ + [ + "@babel/preset-env", + { + "targets": ">0.25%, not dead" + } + ], + [ + "@babel/preset-react", + { + "runtime": "automatic" + } + ], + "@babel/preset-typescript" + ] +} diff --git a/playground/agentic-react-nx-module-federation-playground/apps/shell/module-federation.config.js b/playground/agentic-react-nx-module-federation-playground/apps/shell/module-federation.config.js new file mode 100644 index 0000000..fda5094 --- /dev/null +++ b/playground/agentic-react-nx-module-federation-playground/apps/shell/module-federation.config.js @@ -0,0 +1,15 @@ +module.exports = { + name: 'shell', + remotes: ['catalog', 'profile'], + shared: (libraryName, defaultConfig) => { + if (['react', 'react-dom', 'react-router-dom'].includes(libraryName)) { + return { + ...defaultConfig, + singleton: true, + strictVersion: true, + }; + } + + return defaultConfig; + }, +}; diff --git a/playground/agentic-react-nx-module-federation-playground/apps/shell/project.json b/playground/agentic-react-nx-module-federation-playground/apps/shell/project.json new file mode 100644 index 0000000..863d529 --- /dev/null +++ b/playground/agentic-react-nx-module-federation-playground/apps/shell/project.json @@ -0,0 +1,67 @@ +{ + "name": "shell", + "$schema": "../../node_modules/nx/schemas/project-schema.json", + "sourceRoot": "apps/shell/src", + "projectType": "application", + "implicitDependencies": ["catalog", "profile"], + "targets": { + "build": { + "executor": "@nrwl/webpack:webpack", + "outputs": ["{options.outputPath}"], + "defaultConfiguration": "production", + "options": { + "compiler": "babel", + "outputPath": "dist/apps/shell", + "index": "apps/shell/src/index.html", + "baseHref": "/", + "main": "apps/shell/src/main.ts", + "polyfills": "apps/shell/src/polyfills.ts", + "tsConfig": "apps/shell/tsconfig.app.json", + "assets": ["apps/shell/src/assets"], + "styles": ["apps/shell/src/styles.css"], + "scripts": [], + "webpackConfig": "apps/shell/webpack.config.js" + }, + "configurations": { + "development": { + "extractLicenses": false, + "optimization": false, + "sourceMap": true, + "vendorChunk": true + }, + "production": { + "extractLicenses": true, + "optimization": true, + "sourceMap": false, + "vendorChunk": false + } + } + }, + "serve": { + "executor": "@nrwl/webpack:dev-server", + "defaultConfiguration": "development", + "options": { + "buildTarget": "shell:build", + "host": "127.0.0.1", + "port": 4200 + }, + "configurations": { + "development": { + "buildTarget": "shell:build:development" + }, + "production": { + "buildTarget": "shell:build:production" + } + } + }, + "dev": { + "executor": "@nrwl/webpack:dev-server", + "options": { + "buildTarget": "shell:build:development", + "host": "127.0.0.1", + "port": 4200 + } + } + }, + "tags": ["scope:host", "type:app"] +} diff --git a/playground/agentic-react-nx-module-federation-playground/apps/shell/src/app/App.tsx b/playground/agentic-react-nx-module-federation-playground/apps/shell/src/app/App.tsx new file mode 100644 index 0000000..28fb2ac --- /dev/null +++ b/playground/agentic-react-nx-module-federation-playground/apps/shell/src/app/App.tsx @@ -0,0 +1,59 @@ +import { ChakraProvider, Text } from '@chakra-ui/react'; +import { PuzzlePieceIcon, TruckIcon } from '@heroicons/react/24/outline'; +import { SectionCard, hearthHubTheme } from '@mfplayground/hearthhub-ui'; +import { Suspense, lazy } from 'react'; +import { Navigate, Route, Routes } from 'react-router-dom'; +import { AppFrame } from './components/AppFrame'; +import { Diagnostics } from './pages/Diagnostics'; +import { ShellHome } from './pages/ShellHome'; + +const CatalogRoutes = lazy(() => import('catalog/Routes')); +const ProfileRoutes = lazy(() => import('profile/Routes')); + +export function App() { + return ( + + + + } /> + + Loading services catalog... + + } + > + + + } + /> + + Loading household profile... + + } + > + + + } + /> + } /> + } /> + + + + ); +} diff --git a/playground/agentic-react-nx-module-federation-playground/apps/shell/src/app/components/AppFrame.tsx b/playground/agentic-react-nx-module-federation-playground/apps/shell/src/app/components/AppFrame.tsx new file mode 100644 index 0000000..c352a70 --- /dev/null +++ b/playground/agentic-react-nx-module-federation-playground/apps/shell/src/app/components/AppFrame.tsx @@ -0,0 +1,287 @@ +import { + Avatar, + Box, + Button, + Circle, + Divider, + Flex, + HStack, + Heading, + Icon, + Input, + InputGroup, + InputLeftElement, + Text, +} from '@chakra-ui/react'; +import { + BellIcon, + ChatBubbleLeftRightIcon, + ChevronDownIcon, + HomeModernIcon, + LifebuoyIcon, + MagnifyingGlassIcon, + ShieldCheckIcon, +} from '@heroicons/react/24/outline'; +import { + AppIcon, + type IconComponent, + primaryNavigation, + secondaryNavigation, +} from '@mfplayground/hearthhub-ui'; +import type { ReactNode } from 'react'; +import { Link, useLocation } from 'react-router-dom'; + +function SidebarLink({ + count, + icon, + isActive, + label, + to, +}: { + count?: string; + icon: IconComponent; + isActive?: boolean; + label: string; + to?: string; +}) { + const content = ( + + + + + {label} + + + {count ? ( + + {count} + + ) : null} + + ); + + return to ? ( + + {content} + + ) : ( + content + ); +} + +function Sidebar() { + const location = useLocation(); + + return ( + + + + + HearthHub + + + + + + + + Oakridge Properties + + + Portfolio · 24 homes + + + + + + + {primaryNavigation.map((item) => ( + + ))} + + + + + + {secondaryNavigation.map((item) => ( + + ))} + + + + + + + + Need help now? + + + Talk to our care team 24/7. + + + + + Contact Support + + + + + + + + Jennifer Park + + + Admin + + + + + + ); +} + +function TopBar() { + return ( + + + + + Good morning, Jennifer + + + Here's what's happening at Oakridge Properties. + + + + + + + + + + + + + + + + } + > + New Request + + + + ); +} + +function MobileHeader() { + return ( + + + + + HearthHub + + + + New Request + + + ); +} + +export function AppFrame({ children }: { children: ReactNode }) { + return ( + + + + + + + {children} + + + + ); +} diff --git a/playground/agentic-react-nx-module-federation-playground/apps/shell/src/app/components/dashboard/HeroPanel.tsx b/playground/agentic-react-nx-module-federation-playground/apps/shell/src/app/components/dashboard/HeroPanel.tsx new file mode 100644 index 0000000..490d459 --- /dev/null +++ b/playground/agentic-react-nx-module-federation-playground/apps/shell/src/app/components/dashboard/HeroPanel.tsx @@ -0,0 +1,40 @@ +import { Box, Button, Heading, Icon, Text } from '@chakra-ui/react'; +import { ChevronDownIcon } from '@heroicons/react/24/outline'; +import { Link } from 'react-router-dom'; +import commandCenterImage from '../../../assets/hearthhub-command-center.png'; + +export function HeroPanel() { + return ( + + + Command Center + + + All systems. Every home. + + + Monitor visits, manage vendors, and keep your homes running smoothly. + + + } + to="/diagnostics" + variant="outline" + > + View Operations + + + ); +} diff --git a/playground/agentic-react-nx-module-federation-playground/apps/shell/src/app/components/dashboard/RightRail.tsx b/playground/agentic-react-nx-module-federation-playground/apps/shell/src/app/components/dashboard/RightRail.tsx new file mode 100644 index 0000000..a507b59 --- /dev/null +++ b/playground/agentic-react-nx-module-federation-playground/apps/shell/src/app/components/dashboard/RightRail.tsx @@ -0,0 +1,251 @@ +import { + Avatar, + Badge, + Box, + Button, + Circle, + Divider, + HStack, + Heading, + Icon, + Progress, + Stack, + Text, +} from '@chakra-ui/react'; +import { + CalendarDaysIcon, + CheckCircleIcon, + ExclamationTriangleIcon, +} from '@heroicons/react/24/outline'; +import { + AppIcon, + ShellPanel, + issueTriage, + readinessTasks, + routeOwners, +} from '@mfplayground/hearthhub-ui'; + +function UpcomingVisitCard() { + return ( + } + title="Upcoming Visit" + > + + + + MAY + + + 28 + + + WED + + + + + HVAC Seasonal Tune-Up + Scheduled + + 9:00 - 11:00 AM + 123 Maple Ridge Dr + + + + Technician: Alex Ramirez + + + + + + + View Visit + + + Reschedule + + + + ); +} + +function SeasonalReadinessCard() { + return ( + + View all + + } + title="Seasonal Readiness" + > + + + + + 72% + + + Ready + + + + + You're on track for summer. + + 2 of 7 seasonal tasks remaining. + + + + } spacing={2}> + {readinessTasks.map(([task, date, state]) => ( + + + {state === 'open' ? ( + + ) : ( + + )} + {task} + + + {date} + + + ))} + + + View Seasonal Checklist + + + ); +} + +function IssueTriageCard() { + return ( + + View all (3) + + } + title="Issue Triage" + > + + {issueTriage.map(([issue, home, level, time]) => ( + + + + ! + + + + {issue} + + + {home} + + + + + + {level} + + + {time} + + + + ))} + + + Go to Requests + + + ); +} + +function RouteOwnershipCard() { + return ( + + View map + + } + title="Route Ownership" + > + + {routeOwners.map(([owner, count, progress]) => ( + + + + {owner} + + + {count} + + + + {progress}% + + + ))} + + + Manage Routes + + + ); +} + +export function RightRail() { + return ( + + + + + + + ); +} diff --git a/playground/agentic-react-nx-module-federation-playground/apps/shell/src/app/components/dashboard/ServicePlansPanel.tsx b/playground/agentic-react-nx-module-federation-playground/apps/shell/src/app/components/dashboard/ServicePlansPanel.tsx new file mode 100644 index 0000000..061fa39 --- /dev/null +++ b/playground/agentic-react-nx-module-federation-playground/apps/shell/src/app/components/dashboard/ServicePlansPanel.tsx @@ -0,0 +1,57 @@ +import { + Badge, + Box, + Button, + Circle, + Divider, + HStack, + Stack, + Text, +} from '@chakra-ui/react'; +import { AppIcon, ShellPanel, servicePlans } from '@mfplayground/hearthhub-ui'; +import { Link } from 'react-router-dom'; + +export function ServicePlansPanel() { + return ( + + Manage + + } + title="Service Plans" + > + } spacing={3}> + {servicePlans.map((plan) => ( + + + + + + + {plan.displayName} + + {plan.homes} + + + + + {plan.status} + + + ))} + + + View All Plans + + + ); +} diff --git a/playground/agentic-react-nx-module-federation-playground/apps/shell/src/app/components/dashboard/StatsStrip.tsx b/playground/agentic-react-nx-module-federation-playground/apps/shell/src/app/components/dashboard/StatsStrip.tsx new file mode 100644 index 0000000..ee0f0c7 --- /dev/null +++ b/playground/agentic-react-nx-module-federation-playground/apps/shell/src/app/components/dashboard/StatsStrip.tsx @@ -0,0 +1,64 @@ +import { + Badge, + Box, + HStack, + SimpleGrid, + Stat, + StatHelpText, + StatLabel, + StatNumber, +} from '@chakra-ui/react'; +import { shellStats } from '@mfplayground/hearthhub-ui'; + +function StatTile({ + delta, + help, + label, + tone, + value, +}: (typeof shellStats)[number]) { + const deltaColor = + tone === 'green' + ? 'green' + : tone === 'red' + ? 'red' + : tone === 'gold' + ? 'yellow' + : 'gray'; + + return ( + + + + {label} + + + {value} + + {delta} + + + + {help} + + + + ); +} + +export function StatsStrip() { + return ( + + {shellStats.map((stat) => ( + + ))} + + ); +} diff --git a/playground/agentic-react-nx-module-federation-playground/apps/shell/src/app/components/dashboard/TrustedVendorsPanel.tsx b/playground/agentic-react-nx-module-federation-playground/apps/shell/src/app/components/dashboard/TrustedVendorsPanel.tsx new file mode 100644 index 0000000..82174b3 --- /dev/null +++ b/playground/agentic-react-nx-module-federation-playground/apps/shell/src/app/components/dashboard/TrustedVendorsPanel.tsx @@ -0,0 +1,103 @@ +import { + Box, + Button, + Circle, + HStack, + Table, + Tbody, + Td, + Text, + Th, + Thead, + Tr, +} from '@chakra-ui/react'; +import { + AppIcon, + ShellPanel, + trustedVendors, +} from '@mfplayground/hearthhub-ui'; + +export function TrustedVendorsPanel() { + return ( + + View all + + } + title="Trusted Vendors" + > + + + + Vendor + Categories + Rating + Perf. + Last + + + + {trustedVendors.map((vendor) => ( + + + + + + + + {vendor.name} + + {vendor.category} + + + + + + + {vendor.serviceIcons.map((serviceIcon) => ( + + {serviceIcon} + + ))} + + + + + {vendor.rating} + + ★★★★★ + + + + + + {vendor.performance}% + + + On-time + + + {vendor.lastVisit} + + ))} + + + + Browse All Vendors + + + ); +} diff --git a/playground/agentic-react-nx-module-federation-playground/apps/shell/src/app/components/dashboard/VisitTable.tsx b/playground/agentic-react-nx-module-federation-playground/apps/shell/src/app/components/dashboard/VisitTable.tsx new file mode 100644 index 0000000..04a8676 --- /dev/null +++ b/playground/agentic-react-nx-module-federation-playground/apps/shell/src/app/components/dashboard/VisitTable.tsx @@ -0,0 +1,125 @@ +import { + Avatar, + Badge, + Button, + HStack, + Icon, + Tab, + TabList, + TabPanel, + TabPanels, + Table, + Tabs, + Tbody, + Td, + Text, + Th, + Thead, + Tr, +} from '@chakra-ui/react'; +import { CalendarDaysIcon, ChevronDownIcon } from '@heroicons/react/24/outline'; +import { AppIcon, ShellPanel, visitRows } from '@mfplayground/hearthhub-ui'; + +export function VisitTable() { + return ( + + + + + {' '} + Upcoming Visits + + Recent Activity + Messages + Documents + + + + + {['Next 7 Days', 'Next 30 Days', 'All'].map((filter, index) => ( + + {filter} + + ))} + {['All Homes', 'All Services', 'All Vendors'].map((filter) => ( + } + size="sm" + variant="outline" + > + {filter} + + ))} + + + + + Date + Time + Service + Home + Vendor + Technician + Status + + + + {visitRows.map((visit) => ( + + {visit.date} + {visit.time} + {visit.service} + {visit.home} + {visit.vendor} + + + + {visit.tech} + + + + + {visit.status} + + + + ))} + + + + View All Visits + + + + Recent service notes, remote deploys, and approval events appear + here. + + + Unread homeowner and vendor messages are grouped by property and + visit. + + + Invoices, inspection reports, and plan documents are attached to + each route. + + + + + ); +} diff --git a/playground/agentic-react-nx-module-federation-playground/apps/shell/src/app/pages/Diagnostics.tsx b/playground/agentic-react-nx-module-federation-playground/apps/shell/src/app/pages/Diagnostics.tsx new file mode 100644 index 0000000..6e94dd2 --- /dev/null +++ b/playground/agentic-react-nx-module-federation-playground/apps/shell/src/app/pages/Diagnostics.tsx @@ -0,0 +1,76 @@ +import { + Box, + Divider, + List, + ListIcon, + ListItem, + SimpleGrid, + Stack, + Tag, + TagLabel, + Text, +} from '@chakra-ui/react'; +import { + ArrowPathIcon, + CheckCircleIcon, + Cog6ToothIcon, + CubeTransparentIcon, + PuzzlePieceIcon, +} from '@heroicons/react/24/outline'; +import { AppIcon, SectionCard } from '@mfplayground/hearthhub-ui'; +import { LegacyRuntimeBadge } from '@mfplayground/legacy-react-widget'; + +export function Diagnostics() { + return ( + + + + Runtime board + + + Production diagnostics for the federated HearthHub app. + + + Developers use this board to confirm that customer-facing screens + still expose accurate component trees, stable selection targets, and + route ownership after each remote ships. + + + + + + window.__AGENTIC_REACT__ + available in the shell host and remotes + + + + {[ + 'catalog/Routes owns service plans', + 'profile/Routes owns household context', + ].map((item) => ( + + + {item} + + ))} + + + + + React 18.2.0 and React Router 6.4.2 remain strict singletons. + + + + module-federation.js enforced + + + + + ); +} diff --git a/playground/agentic-react-nx-module-federation-playground/apps/shell/src/app/pages/ShellHome.tsx b/playground/agentic-react-nx-module-federation-playground/apps/shell/src/app/pages/ShellHome.tsx new file mode 100644 index 0000000..69466dc --- /dev/null +++ b/playground/agentic-react-nx-module-federation-playground/apps/shell/src/app/pages/ShellHome.tsx @@ -0,0 +1,28 @@ +import { Grid, Stack } from '@chakra-ui/react'; +import { HeroPanel } from '../components/dashboard/HeroPanel'; +import { RightRail } from '../components/dashboard/RightRail'; +import { ServicePlansPanel } from '../components/dashboard/ServicePlansPanel'; +import { StatsStrip } from '../components/dashboard/StatsStrip'; +import { TrustedVendorsPanel } from '../components/dashboard/TrustedVendorsPanel'; +import { VisitTable } from '../components/dashboard/VisitTable'; + +export function ShellHome() { + return ( + + + + + + + + + + + + + ); +} diff --git a/playground/agentic-react-nx-module-federation-playground/apps/shell/src/app/remotes.d.ts b/playground/agentic-react-nx-module-federation-playground/apps/shell/src/app/remotes.d.ts new file mode 100644 index 0000000..5ee9892 --- /dev/null +++ b/playground/agentic-react-nx-module-federation-playground/apps/shell/src/app/remotes.d.ts @@ -0,0 +1,13 @@ +declare module 'catalog/Routes' { + import type { ComponentType } from 'react'; + + const Routes: ComponentType; + export default Routes; +} + +declare module 'profile/Routes' { + import type { ComponentType } from 'react'; + + const Routes: ComponentType; + export default Routes; +} diff --git a/playground/agentic-react-nx-module-federation-playground/apps/shell/src/assets.d.ts b/playground/agentic-react-nx-module-federation-playground/apps/shell/src/assets.d.ts new file mode 100644 index 0000000..fc781e8 --- /dev/null +++ b/playground/agentic-react-nx-module-federation-playground/apps/shell/src/assets.d.ts @@ -0,0 +1,4 @@ +declare module '*.png' { + const src: string; + export default src; +} diff --git a/playground/agentic-react-nx-module-federation-playground/apps/shell/src/assets/.gitkeep b/playground/agentic-react-nx-module-federation-playground/apps/shell/src/assets/.gitkeep new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/playground/agentic-react-nx-module-federation-playground/apps/shell/src/assets/.gitkeep @@ -0,0 +1 @@ + diff --git a/playground/agentic-react-nx-module-federation-playground/apps/shell/src/assets/hearthhub-command-center.png b/playground/agentic-react-nx-module-federation-playground/apps/shell/src/assets/hearthhub-command-center.png new file mode 100644 index 0000000..f5c240a Binary files /dev/null and b/playground/agentic-react-nx-module-federation-playground/apps/shell/src/assets/hearthhub-command-center.png differ diff --git a/playground/agentic-react-nx-module-federation-playground/apps/shell/src/assets/hearthhub-operations.png b/playground/agentic-react-nx-module-federation-playground/apps/shell/src/assets/hearthhub-operations.png new file mode 100644 index 0000000..604002e Binary files /dev/null and b/playground/agentic-react-nx-module-federation-playground/apps/shell/src/assets/hearthhub-operations.png differ diff --git a/playground/agentic-react-nx-module-federation-playground/apps/shell/src/bootstrap.tsx b/playground/agentic-react-nx-module-federation-playground/apps/shell/src/bootstrap.tsx new file mode 100644 index 0000000..8d5a90d --- /dev/null +++ b/playground/agentic-react-nx-module-federation-playground/apps/shell/src/bootstrap.tsx @@ -0,0 +1,19 @@ +import { createRoot } from 'react-dom/client'; +import { RouterProvider, createBrowserRouter } from 'react-router-dom'; +import { App } from './app/App'; +import './styles.css'; + +const router = createBrowserRouter([ + { + path: '*', + element: , + }, +]); + +const rootElement = document.getElementById('root'); + +if (!rootElement) { + throw new Error('Missing #root element for Nx module federation shell'); +} + +createRoot(rootElement).render(); diff --git a/playground/agentic-react-nx-module-federation-playground/apps/shell/src/index.html b/playground/agentic-react-nx-module-federation-playground/apps/shell/src/index.html new file mode 100644 index 0000000..f223517 --- /dev/null +++ b/playground/agentic-react-nx-module-federation-playground/apps/shell/src/index.html @@ -0,0 +1,12 @@ + + + + + Nx Module Federation Shell + + + + + + + diff --git a/playground/agentic-react-nx-module-federation-playground/apps/shell/src/main.ts b/playground/agentic-react-nx-module-federation-playground/apps/shell/src/main.ts new file mode 100644 index 0000000..b93c7a0 --- /dev/null +++ b/playground/agentic-react-nx-module-federation-playground/apps/shell/src/main.ts @@ -0,0 +1 @@ +import('./bootstrap'); diff --git a/playground/agentic-react-nx-module-federation-playground/apps/shell/src/polyfills.ts b/playground/agentic-react-nx-module-federation-playground/apps/shell/src/polyfills.ts new file mode 100644 index 0000000..cb0ff5c --- /dev/null +++ b/playground/agentic-react-nx-module-federation-playground/apps/shell/src/polyfills.ts @@ -0,0 +1 @@ +export {}; diff --git a/playground/agentic-react-nx-module-federation-playground/apps/shell/src/styles.css b/playground/agentic-react-nx-module-federation-playground/apps/shell/src/styles.css new file mode 100644 index 0000000..9376b71 --- /dev/null +++ b/playground/agentic-react-nx-module-federation-playground/apps/shell/src/styles.css @@ -0,0 +1,194 @@ +:root { + color: #172033; + background: #f7f8fb; + font-family: + Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, + "Segoe UI", sans-serif; +} + +* { + box-sizing: border-box; +} + +body { + margin: 0; + min-width: 320px; + background: + linear-gradient(135deg, rgba(59, 130, 246, 0.12), transparent 32rem), + linear-gradient(180deg, #f7f8fb 0%, #eef2f7 100%); +} + +button, +a { + font: inherit; +} + +.shell { + width: min(1120px, calc(100% - 32px)); + margin: 0 auto; + padding: 24px 0 48px; +} + +.topbar { + display: flex; + align-items: center; + justify-content: space-between; + gap: 24px; + min-height: 64px; +} + +.brand { + color: #111827; + font-weight: 800; + text-decoration: none; +} + +nav { + display: flex; + flex-wrap: wrap; + justify-content: flex-end; + gap: 8px; +} + +nav a { + border-radius: 8px; + color: #475569; + font-size: 14px; + font-weight: 700; + padding: 9px 12px; + text-decoration: none; +} + +nav a.active { + background: #111827; + color: #ffffff; +} + +.workspace-panel { + margin-top: 48px; + padding: 48px; + border: 1px solid rgba(148, 163, 184, 0.36); + border-radius: 8px; + background: rgba(255, 255, 255, 0.84); + box-shadow: 0 24px 80px rgba(15, 23, 42, 0.08); +} + +.section-label { + margin: 0 0 14px; + color: #2563eb; + font-size: 13px; + font-weight: 800; + letter-spacing: 0; + text-transform: uppercase; +} + +h1, +h2 { + max-width: 780px; + margin: 0; + color: #0f172a; + font-size: clamp(36px, 6vw, 68px); + line-height: 0.98; +} + +h2 { + font-size: clamp(30px, 4vw, 48px); +} + +.hero-copy { + max-width: 760px; + margin: 24px 0 0; + color: #475569; + font-size: 18px; + line-height: 1.7; +} + +.team-switcher { + display: flex; + flex-wrap: wrap; + gap: 8px; + margin: 32px 0; +} + +.team-switcher button { + min-height: 40px; + border: 1px solid #cbd5e1; + border-radius: 8px; + background: #ffffff; + color: #475569; + cursor: pointer; + font-size: 14px; + font-weight: 800; + padding: 0 14px; +} + +.team-switcher button.selected { + border-color: #1d4ed8; + background: #1d4ed8; + color: #ffffff; +} + +.summary-grid, +.diagnostic-list { + display: grid; + grid-template-columns: repeat(3, minmax(0, 1fr)); + gap: 16px; +} + +.summary-grid article, +.diagnostic-list > div { + min-height: 144px; + border: 1px solid #dbe3ef; + border-radius: 8px; + background: #ffffff; + padding: 20px; +} + +.summary-grid span, +.diagnostic-list span { + display: block; + color: #64748b; + font-size: 13px; + font-weight: 800; + margin-bottom: 10px; +} + +.summary-grid strong, +.diagnostic-list strong { + display: block; + color: #111827; + font-size: 20px; + line-height: 1.2; +} + +.summary-grid p { + color: #475569; + line-height: 1.6; + margin: 16px 0 0; +} + +.diagnostic-list { + grid-template-columns: repeat(2, minmax(0, 1fr)); + margin-top: 28px; +} + +@media (max-width: 760px) { + .topbar { + align-items: flex-start; + flex-direction: column; + } + + nav { + justify-content: flex-start; + } + + .workspace-panel { + margin-top: 28px; + padding: 28px; + } + + .summary-grid, + .diagnostic-list { + grid-template-columns: 1fr; + } +} diff --git a/playground/agentic-react-nx-module-federation-playground/apps/shell/tsconfig.app.json b/playground/agentic-react-nx-module-federation-playground/apps/shell/tsconfig.app.json new file mode 100644 index 0000000..4588365 --- /dev/null +++ b/playground/agentic-react-nx-module-federation-playground/apps/shell/tsconfig.app.json @@ -0,0 +1,9 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "outDir": "../../dist/out-tsc", + "types": [] + }, + "files": ["src/main.ts", "src/polyfills.ts"], + "include": ["src/**/*.ts", "src/**/*.tsx"] +} diff --git a/playground/agentic-react-nx-module-federation-playground/apps/shell/webpack.config.js b/playground/agentic-react-nx-module-federation-playground/apps/shell/webpack.config.js new file mode 100644 index 0000000..612c63a --- /dev/null +++ b/playground/agentic-react-nx-module-federation-playground/apps/shell/webpack.config.js @@ -0,0 +1,81 @@ +const path = require('node:path'); +const { composePlugins, withNx } = require('@nrwl/webpack'); +const { withReact } = require('@nrwl/react'); +const { merge } = require('webpack-merge'); +const withModuleFederation = require('../../module-federation'); +const { replaceDefinePlugin } = require('../../webpack.config.base'); +const moduleFederationConfig = require('./module-federation.config'); +const workspaceRoot = path.resolve(__dirname, '../..'); +const toolkit = { + tuningModal: { + classNames: { + surface: 'nx-shell-tuning-surface', + panel: 'nx-shell-tuning-panel', + control: 'nx-shell-tuning-control', + }, + tokens: { + panelRadius: '12px', + controlRadius: '9px', + primaryButtonBackground: '#4338ca', + primaryButtonColor: '#ffffff', + panelShadow: '0 24px 72px rgba(67, 56, 202, 0.22)', + }, + styles: { + surface: { + filter: 'drop-shadow(0 18px 40px rgba(67, 56, 202, 0.15))', + }, + panel: { + border: '1px solid rgba(67, 56, 202, 0.24)', + }, + targetTag: { + background: '#eef2ff', + color: '#4338ca', + }, + sectionTitle: { + color: '#4338ca', + }, + }, + }, +}; + +module.exports = composePlugins( + withNx(), + withReact(), + async (config, nxContext) => { + const federatedModules = await withModuleFederation( + moduleFederationConfig, + ); + const { default: withAgenticReactWebpack } = await import('@agentic-react/webpack'); + const mergedConfig = replaceDefinePlugin( + merge(federatedModules(config, nxContext), { + devServer: { + historyApiFallback: true, + client: { + overlay: false, + }, + }, + resolve: { + fallback: { + fs: false, + net: false, + tls: false, + }, + }, + }), + ); + + return withAgenticReactWebpack( + mergedConfig, + { + mode: + nxContext?.context?.configurationName === 'production' + ? 'production' + : 'development', + }, + { + rootDir: workspaceRoot, + toolkit, + }, + ); + }, +); diff --git a/playground/agentic-react-nx-module-federation-playground/babel.config.json b/playground/agentic-react-nx-module-federation-playground/babel.config.json new file mode 100644 index 0000000..3c53b4f --- /dev/null +++ b/playground/agentic-react-nx-module-federation-playground/babel.config.json @@ -0,0 +1,17 @@ +{ + "presets": [ + [ + "@babel/preset-env", + { + "targets": ">0.25%, not dead" + } + ], + [ + "@babel/preset-react", + { + "runtime": "automatic" + } + ], + "@babel/preset-typescript" + ] +} diff --git a/playground/agentic-react-nx-module-federation-playground/e2e/runtime.spec.js b/playground/agentic-react-nx-module-federation-playground/e2e/runtime.spec.js new file mode 100644 index 0000000..3d1a896 --- /dev/null +++ b/playground/agentic-react-nx-module-federation-playground/e2e/runtime.spec.js @@ -0,0 +1,293 @@ +import { expect, test } from '@playwright/test'; + +const shellPort = process.env.NX_MF_SHELL_PORT || '4200'; +const catalogPort = process.env.NX_MF_CATALOG_PORT || '4201'; +const profilePort = process.env.NX_MF_PROFILE_PORT || '4202'; +const MCP_SERVER_URL = `http://127.0.0.1:${shellPort}/mcp`; +const REMOTE_ENTRY_URLS = [ + [`http://127.0.0.1:${catalogPort}/remoteEntry.js`, 'var catalog'], + [`http://127.0.0.1:${profilePort}/remoteEntry.js`, 'var profile'], +]; + +const waitForRemoteEntries = async (request) => { + for (const [remoteEntryUrl, expectedContainer] of REMOTE_ENTRY_URLS) { + await expect + .poll( + async () => { + const response = await request.get(remoteEntryUrl).catch(() => null); + if (response?.status() !== 200) { + return false; + } + return (await response.text()).includes(expectedContainer); + }, + { timeout: 30000 }, + ) + .toBe(true); + } +}; + +const createMcpClient = async () => { + const [{ Client }, { StreamableHTTPClientTransport }] = await Promise.all([ + import('@modelcontextprotocol/sdk/client/index.js'), + import('@modelcontextprotocol/sdk/client/streamableHttp.js'), + ]); + const client = new Client({ + name: 'nx-module-federation-playground-mcp-e2e', + version: '0.0.0', + }); + const transport = new StreamableHTTPClientTransport(new URL(MCP_SERVER_URL)); + await client.connect(transport); + return { client, transport }; +}; + +const parseToolResponse = (result) => { + const textContent = result.content.find((content) => content.type === 'text'); + if (!textContent || typeof textContent.text !== 'string') { + return null; + } + return JSON.parse(textContent.text); +}; + +const getToolText = (result) => { + const textContent = result.content.find((content) => content.type === 'text'); + if (!textContent || typeof textContent.text !== 'string') { + return ''; + } + return textContent.text; +}; + +const expectProfileMemberCardSource = (context) => { + expect(context.resolvedSources).toContainEqual( + expect.objectContaining({ + componentName: 'ProfileMemberCard', + filePath: expect.stringContaining('ProfileMemberCard.tsx'), + }), + ); + expect(context.sourceSnippets).toContainEqual( + expect.objectContaining({ + filePath: expect.stringContaining('ProfileMemberCard.tsx'), + snippet: expect.stringContaining('function ProfileMemberCard'), + }), + ); + expect(context.sourceSnippets).toContainEqual( + expect.objectContaining({ + filePath: expect.stringContaining('ProfileMemberCard.tsx'), + snippet: expect.stringContaining('profile-member-${memberId}'), + }), + ); +}; + +const selectRemoteProfileMember = async (page, request) => { + await waitForRemoteEntries(request); + await page.goto('/profile'); + await expect(page.getByText('Federated team profile routes.')).toBeVisible(); + await page.waitForFunction(() => window.__AGENTIC_REACT__); + + await page.evaluate(() => window.__AGENTIC_REACT__.setSelectionMode(true)); + await page.locator('#profile-member-sam-rivera').click(); + + const didCapture = await page.evaluate(() => + Boolean(window.__AGENTIC_REACT__?.getLastSelectionContext()), + ); + if (!didCapture) { + await page.evaluate(() => window.__AGENTIC_REACT__.setSelectionMode(true)); + await page.locator('#profile-member-sam-rivera').click(); + } + + await page.waitForFunction(() => + window.__AGENTIC_REACT__?.getLastSelectionContext(), + ); +}; + +test('nx module federation playground injects runtime globals', async ({ + page, +}) => { + await page.goto('/'); + await page.waitForFunction(() => window.__AGENTIC_REACT__); + + const runtimeShape = await page.evaluate(() => ({ + hasRuntime: Boolean(window.__AGENTIC_REACT__), + hasTools: Boolean(window.__AGENTIC_REACT_TOOLS__), + hasSelectionMode: typeof window.__AGENTIC_REACT__?.setSelectionMode, + hasGetContext: typeof window.__AGENTIC_REACT__?.getLastSelectionContext, + hasCopyContext: typeof window.__AGENTIC_REACT__?.copyLastSelectionContext, + hasRegisterTuningModalExtension: + typeof window.__AGENTIC_REACT__?.registerTuningModalExtension, + tuningSurfaceClassName: + window.__AGENTIC_REACT_CONFIG__?.toolkit?.tuningModal?.classNames + ?.surface, + tuningPanelClassName: + window.__AGENTIC_REACT_CONFIG__?.toolkit?.tuningModal?.classNames?.panel, + unregisterType: (() => { + const unregister = + window.__AGENTIC_REACT__?.registerTuningModalExtension({ + id: 'nx-runtime-smoke', + }); + unregister?.(); + return typeof unregister; + })(), + })); + + expect(runtimeShape.hasRuntime).toBe(true); + expect(runtimeShape.hasTools).toBe(true); + expect(runtimeShape.hasSelectionMode).toBe('function'); + expect(runtimeShape.hasGetContext).toBe('function'); + expect(runtimeShape.hasCopyContext).toBe('function'); + expect(runtimeShape.hasRegisterTuningModalExtension).toBe('function'); + expect(runtimeShape.tuningSurfaceClassName).toBe('nx-shell-tuning-surface'); + expect(runtimeShape.tuningPanelClassName).toBe('nx-shell-tuning-panel'); + expect(runtimeShape.unregisterType).toBe('function'); +}); + +test('nx module federation host exposes remote React context to MCP', async ({ + page, + request, +}) => { + await waitForRemoteEntries(request); + await page.goto('/catalog'); + await expect(page.getByText('Federated product routes.')).toBeVisible(); + await expect(page.getByText('React Router 6.4.2 route island')).toBeVisible(); + await page.goto('/profile'); + await expect(page.getByText('Federated team profile routes.')).toBeVisible(); + await expect(page.getByText('Component inspection')).toBeVisible(); + await page.waitForFunction(() => window.__AGENTIC_REACT__); + + const { client, transport } = await createMcpClient(); + try { + const treeRaw = await client.callTool({ + name: 'get-component-tree', + arguments: { + allComponents: true, + }, + }); + const tree = parseToolResponse(treeRaw); + expect(JSON.stringify(tree)).toContain('ProfileRoutes'); + } finally { + await transport.close(); + } +}); + +test('nx module federation selection captures remote component source context', async ({ + page, + request, +}) => { + await selectRemoteProfileMember(page, request); + + const selectionContext = await page.evaluate(() => + window.__AGENTIC_REACT__.getLastSelectionContext(), + ); + + expect(selectionContext.componentName).toBe('ProfileMemberCard'); + expect(selectionContext.selector).toBe('#profile-member-sam-rivera'); + expect(selectionContext.resolvedSources).toContainEqual( + expect.objectContaining({ + componentName: 'ProfileMemberCard', + filePath: expect.stringContaining('ProfileMemberCard.tsx'), + }), + ); + + const { client, transport } = await createMcpClient(); + try { + const selectedContextRaw = await client.callTool({ + name: 'get-last-selection-context', + arguments: { + includeSourceSnippets: true, + contextLines: 12, + maxFiles: 3, + }, + }); + const selectedContext = parseToolResponse(selectedContextRaw); + expect(selectedContext.success).toBe(true); + expect(selectedContext.context.componentName).toBe('ProfileMemberCard'); + expect(selectedContext.context.selector).toBe('#profile-member-sam-rivera'); + expect(selectedContext.summary).toContain('ProfileMemberCard'); + expect(selectedContext.summary).toContain('function ProfileMemberCard'); + expectProfileMemberCardSource(selectedContext.context); + + const reactSourceRaw = await client.callTool({ + name: 'get-react-source-code', + arguments: { + queries: ['article profile-member-sam-rivera'], + maxMatches: 3, + includeSourceSnippets: true, + contextLines: 12, + maxFiles: 3, + }, + }); + const reactSource = parseToolResponse(reactSourceRaw); + expect(reactSource.success).toBe(true); + expect(reactSource.chosenMatch.selector).toBe( + '#profile-member-sam-rivera', + ); + expect(reactSource.context.componentName).toBe('ProfileMemberCard'); + expect(reactSource.summary).toContain('function ProfileMemberCard'); + expectProfileMemberCardSource(reactSource.context); + + const highlightRaw = await client.callTool({ + name: 'highlight-component', + arguments: { componentName: 'ProfileMemberCard' }, + }); + expect(getToolText(highlightRaw)).toContain('highlighted'); + } finally { + await transport.close(); + } +}); + +test('nx selection attributes external package components to local usage source', async ({ + page, + request, +}) => { + await waitForRemoteEntries(request); + await page.goto('/profile'); + await expect(page.getByText('Federated team profile routes.')).toBeVisible(); + await page.waitForFunction(() => window.__AGENTIC_REACT__); + + await page.evaluate(() => window.__AGENTIC_REACT__.setSelectionMode(true)); + await page.locator('#profile-external-component-probe').click(); + await page.waitForFunction(() => + window.__AGENTIC_REACT__?.getLastSelectionContext(), + ); + + const { client, transport } = await createMcpClient(); + try { + const selectedContextRaw = await client.callTool({ + name: 'get-last-selection-context', + arguments: { + includeSourceSnippets: true, + contextLines: 24, + maxFiles: 2, + }, + }); + const selectedContext = parseToolResponse(selectedContextRaw); + const selectionContext = selectedContext.context; + + expect(selectionContext.externalComponent).toEqual( + expect.objectContaining({ + componentName: expect.any(String), + packageName: expect.stringMatching(/^@chakra-ui\//), + usedBy: expect.objectContaining({ + componentName: 'ExternalDependencySelectionProbe', + filePath: expect.stringContaining( + 'ExternalDependencySelectionProbe.tsx', + ), + }), + }), + ); + expect(selectionContext.sourcePreview).toContain( + 'selected external component:', + ); + expect(selectionContext.sourcePreview).toContain( + 'ExternalDependencySelectionProbe.tsx', + ); + expect(selectionContext.sourceSnippets).toContainEqual( + expect.objectContaining({ + filePath: expect.stringContaining( + 'ExternalDependencySelectionProbe.tsx', + ), + snippet: expect.stringContaining('Review access note'), + }), + ); + } finally { + await transport.close(); + } +}); diff --git a/playground/agentic-react-nx-module-federation-playground/libs/hearthhub-ui/project.json b/playground/agentic-react-nx-module-federation-playground/libs/hearthhub-ui/project.json new file mode 100644 index 0000000..bcb2ac6 --- /dev/null +++ b/playground/agentic-react-nx-module-federation-playground/libs/hearthhub-ui/project.json @@ -0,0 +1,8 @@ +{ + "name": "hearthhub-ui", + "$schema": "../../node_modules/nx/schemas/project-schema.json", + "sourceRoot": "libs/hearthhub-ui/src", + "projectType": "library", + "targets": {}, + "tags": ["scope:shared", "type:ui"] +} diff --git a/playground/agentic-react-nx-module-federation-playground/libs/hearthhub-ui/src/components.tsx b/playground/agentic-react-nx-module-federation-playground/libs/hearthhub-ui/src/components.tsx new file mode 100644 index 0000000..ebf342f --- /dev/null +++ b/playground/agentic-react-nx-module-federation-playground/libs/hearthhub-ui/src/components.tsx @@ -0,0 +1,199 @@ +import { + Badge, + Box, + Card, + CardBody, + Flex, + HStack, + Heading, + Icon, + Stat, + StatHelpText, + StatLabel, + StatNumber, + Text, +} from '@chakra-ui/react'; +import type { ComponentType, ReactNode, SVGProps } from 'react'; + +export type IconComponent = ComponentType>; + +export function AppIcon({ + icon, + size = 5, +}: { icon: IconComponent; size?: number }) { + return ; +} + +export function IconBadge({ + bg = 'hearth.50', + color = 'hearth.700', + icon, + size = '40px', +}: { + bg?: string; + color?: string; + icon: IconComponent; + size?: string; +}) { + return ( + + + + ); +} + +export function ShellPanel({ + action, + children, + title, +}: { + action?: ReactNode; + children: ReactNode; + title: string; +}) { + return ( + + + + {title} + {action} + + {children} + + + ); +} + +export function SectionCard({ + children, + icon, + title, +}: { + children: ReactNode; + icon: IconComponent; + title: string; +}) { + return ( + + + + + {title} + + {children} + + + ); +} + +export function RemotePageHeader({ + action, + children, + icon, + label, + title, +}: { + action?: ReactNode; + children: ReactNode; + icon: IconComponent; + label: string; + title: string; +}) { + return ( + + + + + {label} + + + {title} + + + {children} + + + {action} + + ); +} + +export function MetricCard({ + help, + label, + value, +}: { + help: string; + label: string; + value: string; +}) { + return ( + + + + + {label} + + {value} + {help} + + + + ); +} + +export function StatusBadge({ value }: { value: string }) { + const normalized = value.toLowerCase(); + const colorScheme = + normalized.includes('active') || + normalized.includes('confirmed') || + normalized.includes('stable') + ? 'green' + : normalized.includes('high') + ? 'red' + : 'orange'; + + return {value}; +} diff --git a/playground/agentic-react-nx-module-federation-playground/libs/hearthhub-ui/src/data.ts b/playground/agentic-react-nx-module-federation-playground/libs/hearthhub-ui/src/data.ts new file mode 100644 index 0000000..3c5ce03 --- /dev/null +++ b/playground/agentic-react-nx-module-federation-playground/libs/hearthhub-ui/src/data.ts @@ -0,0 +1,284 @@ +import { + Bars3BottomLeftIcon, + CalendarDaysIcon, + ClipboardDocumentCheckIcon, + Cog6ToothIcon, + CreditCardIcon, + DocumentTextIcon, + EnvelopeIcon, + FolderIcon, + HomeModernIcon, + PuzzlePieceIcon, + ShieldCheckIcon, + SparklesIcon, + WrenchScrewdriverIcon, +} from '@heroicons/react/24/outline'; +import type { IconComponent } from './components'; + +export const primaryNavigation = [ + { to: '/', label: 'Home', icon: HomeModernIcon }, + { to: '/catalog', label: 'Services', icon: WrenchScrewdriverIcon }, + { to: '/profile', label: 'Household', icon: PuzzlePieceIcon }, + { to: '/diagnostics', label: 'Diagnostics', icon: Bars3BottomLeftIcon }, +]; + +export const secondaryNavigation = [ + { label: 'Visits', count: '8', icon: CalendarDaysIcon }, + { label: 'Requests', count: '3', icon: ClipboardDocumentCheckIcon }, + { label: 'Vendors', icon: ShieldCheckIcon }, + { label: 'Subscriptions', icon: Bars3BottomLeftIcon }, + { label: 'Documents', icon: DocumentTextIcon }, + { label: 'Messages', count: '2', icon: EnvelopeIcon }, + { label: 'Billing', icon: CreditCardIcon }, + { label: 'Reports', icon: FolderIcon }, + { label: 'Settings', icon: Cog6ToothIcon }, +]; + +export const shellStats = [ + { + label: 'Open Requests', + value: '3', + delta: '+1', + help: '2 urgent', + tone: 'red', + }, + { + label: 'Upcoming Visits', + value: '8', + delta: '+2', + help: 'Next 7 days', + tone: 'red', + }, + { + label: 'Active Subscriptions', + value: '12', + delta: '-', + help: 'Across 24 homes', + tone: 'gray', + }, + { + label: 'Monthly Spend', + value: '$18,540', + delta: '↓ 8%', + help: 'vs Apr 2025', + tone: 'green', + }, + { + label: 'Vendor Rating', + value: '4.8', + delta: '☆', + help: 'Average', + tone: 'gold', + }, +]; + +export type Vendor = { + category: string; + color: string; + icon: IconComponent; + lastVisit: string; + name: string; + performance: number; + rating: string; + serviceIcons: string[]; +}; + +export const trustedVendors: Vendor[] = [ + { + name: 'Evergreen HVAC', + category: 'HVAC', + rating: '4.9', + performance: 98, + lastVisit: 'May 20, 2025', + icon: HomeModernIcon, + color: 'hearth.700', + serviceIcons: ['❄', '🔥', '⚙'], + }, + { + name: 'Pinnacle Plumbing', + category: 'Plumbing', + rating: '4.8', + performance: 96, + lastVisit: 'May 18, 2025', + icon: WrenchScrewdriverIcon, + color: 'gray.600', + serviceIcons: ['💧', '🔧'], + }, + { + name: 'Brightview Electric', + category: 'Electrical', + rating: '4.7', + performance: 94, + lastVisit: 'May 15, 2025', + icon: SparklesIcon, + color: 'gold.500', + serviceIcons: ['⚡', '▣'], + }, + { + name: 'GreenScape Lawns', + category: 'Landscaping', + rating: '4.8', + performance: 97, + lastVisit: 'May 12, 2025', + icon: ShieldCheckIcon, + color: 'green.600', + serviceIcons: ['🌿', '▲'], + }, +]; + +export const servicePlans = [ + { + id: 'family-plus', + name: 'Family Plus seasonal care', + displayName: 'Home Essentials Plan', + technicalName: 'React Router 6.4.2 route island', + owner: 'services marketplace', + status: 'Enrolling', + price: '$89/mo', + readiness: 92, + homes: '12 homes', + icon: HomeModernIcon, + color: 'gold.600', + summary: + 'Quarterly HVAC, plumbing, appliance, and safety checks bundled with emergency booking priority.', + }, + { + id: 'leak-watch', + name: 'Leak watch response', + displayName: 'HVAC Protection Plan', + technicalName: 'Remote plan detail boundary', + owner: 'home protection', + status: 'Active', + price: '$24/mo', + readiness: 81, + homes: '10 homes', + icon: WrenchScrewdriverIcon, + color: 'teal.600', + summary: + 'Moisture sensor dispatch, same-day plumber coordination, and repair documentation for insurance claims.', + }, + { + id: 'plumbing-care', + name: 'Plumbing Care Plan', + displayName: 'Plumbing Care Plan', + technicalName: 'Catalog remote service path', + owner: 'home protection', + status: 'Active', + price: '$34/mo', + readiness: 84, + homes: '8 homes', + icon: ShieldCheckIcon, + color: 'blue.600', + summary: + 'Annual plumbing inspection, fixture leak response, water heater reminders, and vetted provider coordination.', + }, + { + id: 'electrical-safety', + name: 'Electrical Safety Plan', + displayName: 'Electrical Safety Plan', + technicalName: 'Catalog remote commerce path', + owner: 'new home onboarding', + status: 'Expiring Soon', + price: '$42/mo', + readiness: 68, + homes: '6 homes', + icon: SparklesIcon, + color: 'orange.500', + summary: + 'Outlet inspection, panel review, smoke detector checks, and priority scheduling for electrical repairs.', + }, +]; + +export const visitRows = [ + { + date: 'May 28, 2025', + time: '9:00 - 11:00 AM', + service: 'HVAC Seasonal Tune-Up', + home: '123 Maple Ridge Dr', + vendor: 'Evergreen HVAC', + tech: 'Alex Ramirez', + status: 'Scheduled', + }, + { + date: 'May 29, 2025', + time: '1:00 - 3:00 PM', + service: 'Plumbing Inspection', + home: '456 Hillcrest Ave', + vendor: 'Pinnacle Plumbing', + tech: 'Jordan Lee', + status: 'Scheduled', + }, + { + date: 'May 30, 2025', + time: '10:00 AM - 12:00 PM', + service: 'Electrical Safety Check', + home: '789 Cedar Ln', + vendor: 'Brightview Electric', + tech: 'Taylor Morgan', + status: 'Scheduled', + }, + { + date: 'May 31, 2025', + time: '8:00 - 10:00 AM', + service: 'Lawn Treatment', + home: '321 Oakwood Dr', + vendor: 'GreenScape Lawns', + tech: 'Casey Patel', + status: 'Confirmed', + }, +]; + +export const readinessTasks = [ + ['AC system tune-up', 'Completed May 4', 'done'], + ['Gutter cleaning', 'Completed May 10', 'done'], + ['Deck inspection', 'Due Jun 1', 'warning'], + ['Pool opening', 'Due Jun 7', 'open'], + ['Irrigation check', 'Due Jun 14', 'open'], +] as const; + +export const issueTriage = [ + ['No cool air on 2nd floor', '123 Maple Ridge Dr', 'High', 'Just now'], + ['Kitchen faucet leak', '456 Hillcrest Ave', 'Medium', '1h ago'], + ['Outdoor outlet not working', '789 Cedar Ln', 'Low', '3h ago'], +] as const; + +export const routeOwners = [ + ['Alex Ramirez', '8 visits', 95], + ['Jordan Lee', '7 visits', 88], + ['Taylor Morgan', '6 visits', 81], + ['Casey Patel', '5 visits', 76], +] as const; + +export const vendorSignals = [ + 'Background-checked providers with household access notes', + 'Arrival windows synced to the shell work queue', + 'Service detail pages owned by the catalog remote team', +]; + +export const householdMembers = [ + { + name: 'Mira Chen', + role: 'Primary homeowner', + focus: 'Service approval and budget thresholds', + note: 'Prefers text confirmations before technicians enter the home office.', + }, + { + name: 'Owen Park', + role: 'Weekend coordinator', + focus: 'Vendor access and visit handoff', + note: 'Available for Saturday arrivals and outdoor project sign-off.', + }, + { + name: 'Sam Rivera', + role: 'Care logistics lead', + focus: 'Component inspection', + note: 'Keeps inspection notes current for source-context and selector checks.', + }, +]; + +export const householdPreferences = [ + 'Technicians can use the side gate after arrival is confirmed.', + 'Quiet hours are 1:00 PM to 3:00 PM for remote work and childcare.', + 'Use fragrance-free cleaning supplies in bedrooms and nursery.', + 'Pets must remain upstairs during plumbing and electrical visits.', +]; diff --git a/playground/agentic-react-nx-module-federation-playground/libs/hearthhub-ui/src/index.ts b/playground/agentic-react-nx-module-federation-playground/libs/hearthhub-ui/src/index.ts new file mode 100644 index 0000000..d809aa8 --- /dev/null +++ b/playground/agentic-react-nx-module-federation-playground/libs/hearthhub-ui/src/index.ts @@ -0,0 +1,3 @@ +export * from './components'; +export * from './data'; +export * from './theme'; diff --git a/playground/agentic-react-nx-module-federation-playground/libs/hearthhub-ui/src/theme.ts b/playground/agentic-react-nx-module-federation-playground/libs/hearthhub-ui/src/theme.ts new file mode 100644 index 0000000..f95d7c5 --- /dev/null +++ b/playground/agentic-react-nx-module-federation-playground/libs/hearthhub-ui/src/theme.ts @@ -0,0 +1,45 @@ +import { extendTheme } from '@chakra-ui/react'; + +export const hearthHubTheme = extendTheme({ + fonts: { + body: 'Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif', + heading: + 'Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif', + }, + radii: { + md: '8px', + lg: '8px', + xl: '8px', + }, + colors: { + hearth: { + 50: '#eef8f3', + 100: '#d7ede2', + 500: '#177052', + 600: '#075f45', + 700: '#064b39', + 800: '#06372c', + 900: '#03281f', + }, + ember: { + 50: '#fff0eb', + 100: '#ffd9cc', + 500: '#dc5135', + 700: '#9d341f', + }, + gold: { + 50: '#fff8e6', + 100: '#f8e7b3', + 500: '#c28b22', + 700: '#7d5615', + }, + }, + styles: { + global: { + body: { + bg: '#f6f4ee', + color: '#111827', + }, + }, + }, +}); diff --git a/playground/agentic-react-nx-module-federation-playground/libs/hearthhub-ui/tsconfig.lib.json b/playground/agentic-react-nx-module-federation-playground/libs/hearthhub-ui/tsconfig.lib.json new file mode 100644 index 0000000..2bef5a8 --- /dev/null +++ b/playground/agentic-react-nx-module-federation-playground/libs/hearthhub-ui/tsconfig.lib.json @@ -0,0 +1,8 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "outDir": "../../dist/out-tsc", + "types": [] + }, + "include": ["src/**/*.ts", "src/**/*.tsx"] +} diff --git a/playground/agentic-react-nx-module-federation-playground/libs/legacy-react-widget/project.json b/playground/agentic-react-nx-module-federation-playground/libs/legacy-react-widget/project.json new file mode 100644 index 0000000..013614c --- /dev/null +++ b/playground/agentic-react-nx-module-federation-playground/libs/legacy-react-widget/project.json @@ -0,0 +1,8 @@ +{ + "name": "legacy-react-widget", + "$schema": "../../node_modules/nx/schemas/project-schema.json", + "sourceRoot": "libs/legacy-react-widget/src", + "projectType": "library", + "targets": {}, + "tags": ["scope:shared", "type:legacy"] +} diff --git a/playground/agentic-react-nx-module-federation-playground/libs/legacy-react-widget/src/LegacyRuntimeBadge.tsx b/playground/agentic-react-nx-module-federation-playground/libs/legacy-react-widget/src/LegacyRuntimeBadge.tsx new file mode 100644 index 0000000..0a322fb --- /dev/null +++ b/playground/agentic-react-nx-module-federation-playground/libs/legacy-react-widget/src/LegacyRuntimeBadge.tsx @@ -0,0 +1,37 @@ +import type { ReactElement } from 'react'; + +export function LegacyRuntimeBadge(): ReactElement { + return ( + + + Legacy dependency + + + React 17 package present + + + ); +} diff --git a/playground/agentic-react-nx-module-federation-playground/libs/legacy-react-widget/src/index.ts b/playground/agentic-react-nx-module-federation-playground/libs/legacy-react-widget/src/index.ts new file mode 100644 index 0000000..70c24fd --- /dev/null +++ b/playground/agentic-react-nx-module-federation-playground/libs/legacy-react-widget/src/index.ts @@ -0,0 +1 @@ +export { LegacyRuntimeBadge } from './LegacyRuntimeBadge'; diff --git a/playground/agentic-react-nx-module-federation-playground/libs/legacy-react-widget/tsconfig.lib.json b/playground/agentic-react-nx-module-federation-playground/libs/legacy-react-widget/tsconfig.lib.json new file mode 100644 index 0000000..2bef5a8 --- /dev/null +++ b/playground/agentic-react-nx-module-federation-playground/libs/legacy-react-widget/tsconfig.lib.json @@ -0,0 +1,8 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "outDir": "../../dist/out-tsc", + "types": [] + }, + "include": ["src/**/*.ts", "src/**/*.tsx"] +} diff --git a/playground/agentic-react-nx-module-federation-playground/module-federation.js b/playground/agentic-react-nx-module-federation-playground/module-federation.js new file mode 100644 index 0000000..ad4149d --- /dev/null +++ b/playground/agentic-react-nx-module-federation-playground/module-federation.js @@ -0,0 +1,121 @@ +const { createProjectGraphAsync } = require('@nrwl/devkit'); +const webpack = require('webpack'); + +const rootPackage = require('./package.json'); + +const sharedVersions = { + ...rootPackage.dependencies, + ...rootPackage.devDependencies, +}; + +const singletonPackages = new Set(['react', 'react-dom', 'react-router-dom']); +const projectPortEnvKeys = { + catalog: 'NX_MF_CATALOG_PORT', + profile: 'NX_MF_PROFILE_PORT', + shell: 'NX_MF_SHELL_PORT', +}; + +const getProjectServePort = (project, projectName) => { + const portEnvKey = projectPortEnvKeys[projectName]; + if (portEnvKey && process.env[portEnvKey]) { + return process.env[portEnvKey]; + } + + const serve = project?.data?.targets?.serve || project?.targets?.serve; + const dev = project?.data?.targets?.dev || project?.targets?.dev; + return serve?.options?.port || dev?.options?.port; +}; + +const getRemoteDefinition = (remote, graph, isDevelopment) => { + if (Array.isArray(remote)) { + const [name, url] = remote; + return [name, `${name}@${url.replace(/\/$/, '')}/remoteEntry.js`]; + } + + if (!isDevelopment) { + return [remote, `${remote}@/${remote}/remoteEntry.js`]; + } + + const remoteProject = graph.nodes[remote]; + const port = getProjectServePort(remoteProject, remote); + + if (!port) { + throw new Error( + `Cannot resolve module federation remote "${remote}" because its project.json has no serve/dev port.`, + ); + } + + return [remote, `${remote}@http://127.0.0.1:${port}/remoteEntry.js`]; +}; + +const getSharedConfig = (moduleFederationConfig) => { + const shared = {}; + + for (const packageName of singletonPackages) { + shared[packageName] = { + singleton: true, + strictVersion: true, + requiredVersion: sharedVersions[packageName], + }; + } + + if (typeof moduleFederationConfig.shared === 'function') { + for (const packageName of Object.keys(shared)) { + shared[packageName] = moduleFederationConfig.shared( + packageName, + shared[packageName], + ); + } + } + + return shared; +}; + +const withModuleFederation = async (moduleFederationConfig) => { + const graph = await createProjectGraphAsync(); + + return (config, nxContext = {}) => { + const isProduction = + nxContext?.context?.configurationName === 'production' || + config.mode === 'production'; + const remotes = Object.fromEntries( + (moduleFederationConfig.remotes || []).map((remote) => + getRemoteDefinition(remote, graph, !isProduction), + ), + ); + const projectPort = getProjectServePort( + graph.nodes[moduleFederationConfig.name], + moduleFederationConfig.name, + ); + const publicPath = + !isProduction && projectPort + ? `http://127.0.0.1:${projectPort}/` + : 'auto'; + + return { + ...config, + output: { + ...config.output, + publicPath, + scriptType: 'text/javascript', + uniqueName: moduleFederationConfig.name, + }, + optimization: { + ...config.optimization, + runtimeChunk: false, + }, + plugins: [ + ...(config.plugins || []), + new webpack.container.ModuleFederationPlugin({ + name: moduleFederationConfig.name, + filename: 'remoteEntry.js', + exposes: moduleFederationConfig.exposes || {}, + remotes, + shared: getSharedConfig(moduleFederationConfig), + }), + ], + }; + }; +}; + +module.exports = withModuleFederation; diff --git a/playground/agentic-react-nx-module-federation-playground/nx.json b/playground/agentic-react-nx-module-federation-playground/nx.json new file mode 100644 index 0000000..b601bde --- /dev/null +++ b/playground/agentic-react-nx-module-federation-playground/nx.json @@ -0,0 +1,33 @@ +{ + "npmScope": "mfplayground", + "affected": { + "defaultBase": "main" + }, + "workspaceLayout": { + "appsDir": "apps", + "libsDir": "libs" + }, + "tasksRunnerOptions": { + "default": { + "runner": "nx/tasks-runners/default", + "options": { + "cacheableOperations": ["build", "lint", "test"] + } + } + }, + "targetDefaults": { + "build": { + "dependsOn": ["^build"], + "inputs": ["production", "^production"] + } + }, + "namedInputs": { + "default": ["{projectRoot}/**/*", "sharedGlobals"], + "production": [ + "default", + "!{projectRoot}/**/*.spec.ts", + "!{projectRoot}/**/*.spec.tsx" + ], + "sharedGlobals": [] + } +} diff --git a/playground/agentic-react-nx-module-federation-playground/package.json b/playground/agentic-react-nx-module-federation-playground/package.json new file mode 100644 index 0000000..4178fa0 --- /dev/null +++ b/playground/agentic-react-nx-module-federation-playground/package.json @@ -0,0 +1,54 @@ +{ + "name": "agentic-react-nx-module-federation-playground", + "private": true, + "version": "0.0.0", + "scripts": { + "start": "node ./scripts/start-dev.mjs", + "start:shell": "nx run shell:serve", + "start:catalog": "nx run catalog:serve", + "start:profile": "nx run profile:serve", + "build": "nx run-many --target=build --projects=catalog,profile,shell --configuration=production", + "build:packages": "pnpm -C ../.. run build", + "test:e2e": "pnpm run build:packages && env -u NO_COLOR playwright test" + }, + "dependencies": { + "@chakra-ui/react": "2.10.9", + "@emotion/react": "catalog:", + "@emotion/styled": "catalog:", + "@heroicons/react": "^2.2.0", + "framer-motion": "^12.38.0", + "react": "18.2.0", + "react-17-compat": "npm:react@17.0.2", + "react-dom": "18.2.0", + "react-router-dom": "6.30.3", + "@agentic-react/webpack": "workspace:*" + }, + "devDependencies": { + "@babel/core": "7.20.2", + "@babel/preset-env": "7.20.2", + "@babel/preset-react": "7.18.6", + "@babel/preset-typescript": "7.18.6", + "@modelcontextprotocol/sdk": "1.29.0", + "@nrwl/devkit": "15.9.7", + "@nrwl/eslint-plugin-nx": "15.9.7", + "@nrwl/js": "15.9.7", + "@nrwl/react": "15.9.7", + "@nrwl/webpack": "15.9.7", + "@nrwl/workspace": "15.9.7", + "@playwright/test": "^1.59.1", + "@types/react": "18.3.23", + "@types/react-dom": "18.3.7", + "@typescript-eslint/eslint-plugin": "5.36.1", + "@typescript-eslint/parser": "5.36.1", + "babel-loader": "10.0.0", + "eslint": "8.23.1", + "html-webpack-plugin": "5.6.3", + "nx": "15.9.7", + "ts-loader": "9.4.1", + "typescript": "4.8.4", + "webpack": "5.107.1", + "webpack-cli": "6.0.1", + "webpack-dev-server": "5.2.4", + "webpack-merge": "5.8.0" + } +} diff --git a/playground/agentic-react-nx-module-federation-playground/playwright.config.mjs b/playground/agentic-react-nx-module-federation-playground/playwright.config.mjs new file mode 100644 index 0000000..2c24dc8 --- /dev/null +++ b/playground/agentic-react-nx-module-federation-playground/playwright.config.mjs @@ -0,0 +1,166 @@ +import path from 'node:path'; +import fs from 'node:fs'; +import net from 'node:net'; +import os from 'node:os'; +import { fileURLToPath } from 'node:url'; +import { defineConfig } from '@playwright/test'; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = path.dirname(__filename); +const host = '127.0.0.1'; +const portStateKey = Buffer.from(__dirname).toString('base64url'); +const portStatePath = path.join( + os.tmpdir(), + `agentic-react-nx-mf-playground-${portStateKey}.json`, +); +const portLockPath = path.join( + os.tmpdir(), + `agentic-react-nx-mf-playground-${portStateKey}.lock`, +); + +const canListen = (port) => + new Promise((resolve) => { + const server = net.createServer(); + server.once('error', () => resolve(false)); + server.listen(port, host, () => { + server.close(() => resolve(true)); + }); + }); + +const findAvailablePort = async (preferredPort, reservedPorts) => { + for (let port = preferredPort; port < preferredPort + 1000; port += 1) { + if (reservedPorts.has(port)) { + continue; + } + + if (await canListen(port)) { + reservedPorts.add(port); + return port; + } + } + + throw new Error( + `Could not find an available Nx module federation playground port starting at ${preferredPort}.`, + ); +}; + +const readPortState = () => { + try { + const state = JSON.parse(fs.readFileSync(portStatePath, 'utf8')); + if (Date.now() - state.createdAt < 10 * 60 * 1000) { + return { + catalogPort: Number(state.catalogPort), + profilePort: Number(state.profilePort), + shellPort: Number(state.shellPort), + }; + } + } catch (_error) { + return null; + } + + return null; +}; + +const writePortState = ({ catalogPort, profilePort, shellPort }) => { + fs.mkdirSync(path.dirname(portStatePath), { recursive: true }); + fs.writeFileSync( + portStatePath, + JSON.stringify( + { catalogPort, createdAt: Date.now(), profilePort, shellPort }, + null, + 2, + ), + ); +}; + +const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms)); + +const waitForPortState = async () => { + for (let attempt = 0; attempt < 100; attempt += 1) { + const ports = readPortState(); + if (ports) { + return ports; + } + await sleep(100); + } + + throw new Error( + 'Timed out waiting for Nx module federation playground port allocation.', + ); +}; + +const allocatePorts = async () => { + const reservedPorts = new Set(); + const shellPort = await findAvailablePort( + Number(process.env.NX_MF_SHELL_PORT || 4200), + reservedPorts, + ); + const catalogPort = await findAvailablePort( + Number(process.env.NX_MF_CATALOG_PORT || 4201), + reservedPorts, + ); + const profilePort = await findAvailablePort( + Number(process.env.NX_MF_PROFILE_PORT || 4202), + reservedPorts, + ); + + return { catalogPort, profilePort, shellPort }; +}; + +const allocatePlaywrightPorts = async () => { + const existingPorts = readPortState(); + if (existingPorts) { + return existingPorts; + } + + fs.mkdirSync(path.dirname(portStatePath), { recursive: true }); + let lockHandle; + try { + lockHandle = fs.openSync(portLockPath, 'wx'); + } catch (_error) { + return await waitForPortState(); + } + + try { + const ports = await allocatePorts(); + writePortState(ports); + return ports; + } finally { + fs.closeSync(lockHandle); + fs.rmSync(portLockPath, { force: true }); + } +}; + +const ports = await allocatePlaywrightPorts(); +const { catalogPort, profilePort, shellPort } = ports; + +process.env.NX_MF_SHELL_PORT = String(shellPort); +process.env.NX_MF_CATALOG_PORT = String(catalogPort); +process.env.NX_MF_PROFILE_PORT = String(profilePort); +const shellUrl = `http://${host}:${shellPort}`; + +export default defineConfig({ + testDir: './e2e', + timeout: 45000, + expect: { + timeout: 10000, + }, + use: { + baseURL: shellUrl, + permissions: ['clipboard-read', 'clipboard-write'], + trace: 'on-first-retry', + }, + webServer: { + command: 'pnpm start', + cwd: __dirname, + env: { + ...process.env, + NX_MF_CATALOG_PORT: String(catalogPort), + NX_MF_PROFILE_PORT: String(profilePort), + NX_MF_SHELL_PORT: String(shellPort), + }, + url: shellUrl, + timeout: 240000, + reuseExistingServer: false, + }, +}); diff --git a/playground/agentic-react-nx-module-federation-playground/scripts/start-dev.mjs b/playground/agentic-react-nx-module-federation-playground/scripts/start-dev.mjs new file mode 100644 index 0000000..82503e6 --- /dev/null +++ b/playground/agentic-react-nx-module-federation-playground/scripts/start-dev.mjs @@ -0,0 +1,40 @@ +import { spawn } from 'node:child_process'; + +const projects = [ + ['catalog', process.env.NX_MF_CATALOG_PORT || '4201'], + ['profile', process.env.NX_MF_PROFILE_PORT || '4202'], + ['shell', process.env.NX_MF_SHELL_PORT || '4200'], +]; + +const children = projects.map(([project, port]) => + spawn('pnpm', ['exec', 'nx', 'run', `${project}:serve`, `--port=${port}`], { + env: process.env, + stdio: 'inherit', + }), +); + +const stopChildren = () => { + for (const child of children) { + if (!child.killed) { + child.kill('SIGTERM'); + } + } +}; + +for (const signal of ['SIGINT', 'SIGTERM']) { + process.on(signal, () => { + stopChildren(); + process.exit(0); + }); +} + +for (const child of children) { + child.on('exit', (code, signal) => { + if (code === 0 || signal === 'SIGTERM') { + return; + } + + stopChildren(); + process.exit(code || 1); + }); +} diff --git a/playground/agentic-react-nx-module-federation-playground/tsconfig.base.json b/playground/agentic-react-nx-module-federation-playground/tsconfig.base.json new file mode 100644 index 0000000..59309b4 --- /dev/null +++ b/playground/agentic-react-nx-module-federation-playground/tsconfig.base.json @@ -0,0 +1,30 @@ +{ + "compileOnSave": false, + "compilerOptions": { + "rootDir": ".", + "sourceMap": true, + "declaration": false, + "moduleResolution": "node", + "emitDecoratorMetadata": true, + "experimentalDecorators": true, + "importHelpers": true, + "target": "es2015", + "module": "esnext", + "lib": ["dom", "dom.iterable", "es2020"], + "skipLibCheck": true, + "skipDefaultLibCheck": true, + "baseUrl": ".", + "jsx": "react-jsx", + "allowSyntheticDefaultImports": true, + "esModuleInterop": true, + "paths": { + "@mfplayground/hearthhub-ui": [ + "libs/hearthhub-ui/src/index.ts" + ], + "@mfplayground/legacy-react-widget": [ + "libs/legacy-react-widget/src/index.ts" + ] + } + }, + "exclude": ["node_modules", "tmp"] +} diff --git a/playground/agentic-react-nx-module-federation-playground/webpack.config.base.js b/playground/agentic-react-nx-module-federation-playground/webpack.config.base.js new file mode 100644 index 0000000..5f40197 --- /dev/null +++ b/playground/agentic-react-nx-module-federation-playground/webpack.config.base.js @@ -0,0 +1,33 @@ +const webpack = require('webpack'); + +const allowedEnvPrefixes = ['REACT_APP_', 'NX_']; + +const getAllowedEnvironment = () => { + const definitions = { + 'process.env.NODE_ENV': JSON.stringify( + process.env.NODE_ENV || 'development', + ), + }; + + for (const [key, value] of Object.entries(process.env)) { + if (allowedEnvPrefixes.some((prefix) => key.startsWith(prefix))) { + definitions[`process.env.${key}`] = JSON.stringify(value); + } + } + + return definitions; +}; + +const replaceDefinePlugin = (config) => ({ + ...config, + plugins: [ + ...(config.plugins || []).filter( + (plugin) => plugin?.constructor?.name !== 'DefinePlugin', + ), + new webpack.DefinePlugin(getAllowedEnvironment()), + ], +}); + +module.exports = { + replaceDefinePlugin, +}; diff --git a/playground/user-profile-app/.gitignore b/playground/agentic-react-vite-playground/.gitignore similarity index 90% rename from playground/user-profile-app/.gitignore rename to playground/agentic-react-vite-playground/.gitignore index 4d29575..e7aaa32 100644 --- a/playground/user-profile-app/.gitignore +++ b/playground/agentic-react-vite-playground/.gitignore @@ -7,6 +7,8 @@ # testing /coverage +/playwright-report +/test-results # production /build diff --git a/playground/agentic-react-vite-playground/e2e/selection-context.spec.js b/playground/agentic-react-vite-playground/e2e/selection-context.spec.js new file mode 100644 index 0000000..e206fed --- /dev/null +++ b/playground/agentic-react-vite-playground/e2e/selection-context.spec.js @@ -0,0 +1,981 @@ +import { expect, test } from '@playwright/test'; +import { Client } from '@modelcontextprotocol/sdk/client/index.js'; +import { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js'; + +const MCP_SERVER_URL = 'http://127.0.0.1:51423/mcp'; + +const createMcpClient = async () => { + const client = new Client({ + name: 'agentic-react-e2e', + version: '0.0.0', + }); + const transport = new StreamableHTTPClientTransport(new URL(MCP_SERVER_URL)); + await client.connect(transport); + return { client, transport }; +}; + +const parseToolResponse = (result) => { + const textContent = result.content.find((content) => content.type === 'text'); + if (!textContent || typeof textContent.text !== 'string') { + return null; + } + return JSON.parse(textContent.text); +}; + +const getToolText = (result) => { + const textContent = result.content.find((content) => content.type === 'text'); + if (!textContent || typeof textContent.text !== 'string') { + return ''; + } + return textContent.text; +}; + +const selectProfileEmailField = async (page) => { + await page.goto('/profile/1'); + await page.getByRole('button', { name: 'Edit Profile', exact: true }).click(); + + await page.waitForFunction(() => window.__AGENTIC_REACT__); + await page.evaluate(() => window.__AGENTIC_REACT__.setSelectionMode(true)); + await page.locator('#profile-field-email').click(); + + const didCapture = await page + .waitForFunction( + () => Boolean(window.__AGENTIC_REACT__?.getLastSelectionContext()), + null, + { timeout: 1000 }, + ) + .then(() => true) + .catch(() => false); + if (!didCapture) { + await page.evaluate(() => window.__AGENTIC_REACT__.setSelectionMode(true)); + await page.locator('#profile-field-email').click(); + await page.waitForFunction(() => + Boolean(window.__AGENTIC_REACT__?.getLastSelectionContext()), + ); + } +}; + +const getToolkitRoot = (page) => + page.locator('[data-agentic-react-toolkit="true"]'); + +const openToolkitPanel = async (page) => { + const toolkitRoot = getToolkitRoot(page); + await toolkitRoot.waitFor(); + const selectButton = toolkitRoot.getByRole('button', { + name: 'Select', + exact: true, + }); + if (await selectButton.isVisible()) { + return; + } + + const launcherButton = toolkitRoot.locator( + 'button[aria-label*="Agentic React toolkit"]', + ); + + await launcherButton.first().click({ force: true }); + + if (!(await selectButton.isVisible())) { + await launcherButton.first().click({ force: true }); + } + + await expect(selectButton).toBeVisible(); +}; + +const captureWithToolkitUi = async (page, selector) => { + const toolkitRoot = getToolkitRoot(page); + await openToolkitPanel(page); + await toolkitRoot.getByRole('button', { name: 'Select', exact: true }).click(); + await page.locator(selector).click(); + await page.waitForFunction( + (selectedSelector) => + window.__AGENTIC_REACT__?.getLastSelectionContext()?.selector === + selectedSelector, + selector, + ); + await expect( + toolkitRoot.getByText('Captured', { exact: false }), + ).toBeVisible(); +}; + +test('copying without a selection returns a no-selection response', async ({ + page, +}) => { + await page.goto('/profile/1'); + await page.waitForFunction(() => window.__AGENTIC_REACT__); + + const copyResult = await page.evaluate(() => + window.__AGENTIC_REACT__.copyLastSelectionContext('json'), + ); + + expect(copyResult.success).toBe(false); + expect(copyResult.error).toBe('No selection context found'); +}); + +test('selecting a profile field captures its React source context', async ({ + page, +}) => { + await selectProfileEmailField(page); + + const context = await page.waitForFunction(() => { + const selectionContext = + window.__AGENTIC_REACT__?.getLastSelectionContext(); + if (!selectionContext) return null; + return selectionContext; + }); + const selectionContext = await context.jsonValue(); + + expect(selectionContext.componentName).toBe('ProfileField'); + expect(selectionContext.selector).toBe('#profile-field-email'); + expect(selectionContext.resolvedSources).toContainEqual( + expect.objectContaining({ + filePath: expect.stringContaining( + 'src/components/UserProfile/ProfileField.jsx', + ), + }), + ); +}); + +test('toolkit select copies the selected context automatically', async ({ + page, +}) => { + await page.goto('/profile/1'); + await page.getByRole('button', { name: 'Edit Profile', exact: true }).click(); + + await captureWithToolkitUi(page, '#profile-field-email'); + + const clipboardText = await page.evaluate(() => + navigator.clipboard.readText(), + ); + expect(clipboardText).toContain('component: ProfileField'); + expect(clipboardText).toContain('selector: #profile-field-email'); + expect(clipboardText).toContain( + '', + ); + expect(clipboardText).toContain(''); + expect(clipboardText).toContain('source: '); + expect(clipboardText).toContain( + '/playground/agentic-react-vite-playground/src/components/UserProfile/ProfileField.jsx', + ); +}); + +test('toolkit multiselect appends selections and copies all on done', async ({ + page, +}, testInfo) => { + await page.goto('/profile/1'); + await page.waitForFunction(() => window.__AGENTIC_REACT__); + + const toolkitRoot = getToolkitRoot(page); + await openToolkitPanel(page); + await expect( + toolkitRoot.getByRole('button', { name: 'Copy', exact: true }), + ).toHaveCount(0); + + await toolkitRoot + .getByRole('button', { name: 'Multiselect', exact: true }) + .click(); + const doneButton = toolkitRoot.getByRole('button', { + name: 'Done', + exact: true, + }); + const clearAllButton = toolkitRoot.getByRole('button', { + name: 'Clear all selections', + exact: true, + }); + await expect(doneButton).toBeVisible(); + await expect(clearAllButton).toBeVisible(); + await expect(clearAllButton).toContainText('Clear all'); + await expect(clearAllButton).toBeDisabled(); + + const doneStyles = await doneButton.evaluate((element) => { + const styles = window.getComputedStyle(element); + return { + backgroundColor: styles.backgroundColor, + color: styles.color, + }; + }); + expect(doneStyles.backgroundColor).toBe('rgb(220, 38, 38)'); + expect(doneStyles.color).toBe('rgb(255, 255, 255)'); + + await page.locator('#profile-display-email-value').click(); + await page.waitForFunction( + () => + window.__AGENTIC_REACT__?.getLastSelectionContext()?.selector === + '#profile-display-email-value', + ); + await expect( + page.locator('[data-agentic-react-multi-selected="true"]'), + ).toHaveCount(1); + await page.locator('#profile-header-occupation-value').click(); + await page.waitForFunction( + () => + window.__AGENTIC_REACT__?.getLastSelectionContext()?.selector === + '#profile-header-occupation-value', + ); + await expect( + page.locator('[data-agentic-react-multi-selected="true"]'), + ).toHaveCount(2); + await expect(clearAllButton).toBeEnabled(); + + await clearAllButton.click(); + await expect( + page.locator('[data-agentic-react-multi-selected="true"]'), + ).toHaveCount(0); + await expect(clearAllButton).toBeDisabled(); + await expect(toolkitRoot).toContainText('Cleared all selections'); + + await page.locator('#profile-display-email-value').click(); + await page.waitForFunction( + () => + window.__AGENTIC_REACT__?.getLastSelectionContext()?.selector === + '#profile-display-email-value', + ); + await page.locator('#profile-header-occupation-value').click(); + await page.waitForFunction( + () => + window.__AGENTIC_REACT__?.getLastSelectionContext()?.selector === + '#profile-header-occupation-value', + ); + await expect( + page.locator('[data-agentic-react-multi-selected="true"]'), + ).toHaveCount(2); + + const multiSelectedOverlays = page.locator( + '[data-agentic-react-multi-selected="true"]', + ); + const firstTuneButton = multiSelectedOverlays + .first() + .getByRole('button', { name: 'Adjust selection', exact: true }); + await firstTuneButton.click({ force: true }); + const tuningModal = page.locator('[data-agentic-react-tuning-modal="true"]'); + const tuningSurface = page.locator( + '[data-agentic-react-tuning-surface="true"]', + ); + const tuningPanel = page.locator('[data-agentic-react-tuning-panel="true"]'); + await expect(tuningModal.getByLabel('Text color')).toBeVisible(); + await expect(tuningModal.getByLabel('Background')).toBeVisible(); + await expect(tuningModal.getByLabel('Opacity', { exact: true })).toBeVisible(); + await expect( + tuningModal.getByLabel('Font', { exact: true }), + ).toHaveCount(1); + await expect( + tuningModal.getByLabel('Font size', { exact: true }), + ).toHaveCount(1); + await expect( + tuningModal.getByLabel('Font weight', { exact: true }), + ).toHaveCount(1); + await expect(tuningModal.getByLabel('Width', { exact: true })).toHaveCount(1); + await expect(tuningModal.getByLabel('Height', { exact: true })).toHaveCount(1); + await expect(tuningModal.getByLabel('Padding', { exact: true })).toHaveCount(1); + await expect(tuningModal.getByLabel('Margin', { exact: true })).toHaveCount(1); + await expect( + tuningModal.getByLabel('Describe custom tuning changes'), + ).toBeVisible(); + await expect(tuningSurface).toHaveClass(/vite-playground-tuning-surface/); + await expect(tuningPanel).toBeVisible(); + await expect(tuningPanel).toHaveClass(/vite-playground-tuning-panel/); + await expect(tuningModal.getByLabel('Text color')).toHaveClass( + /vite-playground-tuning-control/, + ); + const tuningStyleOverrides = await tuningPanel.evaluate((element) => { + const styles = window.getComputedStyle(element); + const targetTagElement = element.querySelector( + '[data-agentic-react-tuning-target-tag="true"]', + ); + const targetTagStyles = targetTagElement + ? window.getComputedStyle(targetTagElement) + : null; + return { + borderRadius: styles.borderRadius, + borderTopColor: styles.borderTopColor, + maxHeight: styles.maxHeight, + overflowY: styles.overflowY, + targetTagBackground: targetTagStyles?.backgroundColor || '', + targetTagColor: targetTagStyles?.color || '', + }; + }); + expect(tuningStyleOverrides.borderRadius).toBe('14px'); + expect(tuningStyleOverrides.borderTopColor).toBe( + 'rgba(15, 118, 110, 0.22)', + ); + expect(tuningStyleOverrides.maxHeight).toBe('360px'); + expect(tuningStyleOverrides.overflowY).toBe('auto'); + expect(tuningStyleOverrides.targetTagBackground).toBe('rgb(236, 254, 255)'); + expect(tuningStyleOverrides.targetTagColor).toBe('rgb(15, 118, 110)'); + await expect( + tuningModal + .locator('[data-agentic-react-color-value="true"]') + .filter({ hasText: 'rgba(' }) + .first(), + ).toBeVisible(); + const tuningPanelPlacement = await tuningPanel.evaluate((element) => { + const panelRect = element.getBoundingClientRect(); + const anchorRect = document + .querySelector( + '[data-agentic-react-multi-selected="true"] [aria-label="Adjust selection"]', + ) + ?.getBoundingClientRect(); + + return { + panel: { + left: panelRect.left, + top: panelRect.top, + right: panelRect.right, + bottom: panelRect.bottom, + }, + anchor: anchorRect + ? { + left: anchorRect.left, + right: anchorRect.right, + } + : null, + viewport: { + width: window.innerWidth, + height: window.innerHeight, + }, + }; + }); + expect(tuningPanelPlacement.panel.left).toBeGreaterThanOrEqual(-0.5); + expect(tuningPanelPlacement.panel.top).toBeGreaterThanOrEqual(-0.5); + expect(tuningPanelPlacement.panel.right).toBeLessThanOrEqual( + tuningPanelPlacement.viewport.width + 0.5, + ); + expect(tuningPanelPlacement.panel.bottom).toBeLessThanOrEqual( + tuningPanelPlacement.viewport.height + 0.5, + ); + if (!tuningPanelPlacement.anchor) { + throw new Error('Expected tuning panel to be anchored to the tune button'); + } + expect( + Math.min( + Math.abs( + tuningPanelPlacement.panel.left - tuningPanelPlacement.anchor.right, + ), + Math.abs( + tuningPanelPlacement.anchor.left - tuningPanelPlacement.panel.right, + ), + ), + ).toBeLessThanOrEqual(16); + const tuningScreenshotPath = + process.env.AGENTIC_REACT_TUNING_SCREENSHOT_PATH; + const tuningScreenshot = await page.screenshot({ + ...(tuningScreenshotPath ? { path: tuningScreenshotPath } : {}), + fullPage: false, + }); + await testInfo.attach('tuning-modal-positioning', { + body: tuningScreenshot, + contentType: 'image/png', + }); + const textColorInput = tuningModal.locator('input[name="text-color"]'); + await textColorInput.fill('#336699'); + await textColorInput.dispatchEvent('change'); + await expect(toolkitRoot).toContainText('rgb(51, 102, 153)'); + await expect( + tuningModal.locator('[data-agentic-react-color-value="true"]').first(), + ).toContainText('rgba(51, 102, 153, 1)'); + const opacityInput = tuningModal.locator('input[name="opacity"]'); + await opacityInput.fill('0.8'); + await opacityInput.dispatchEvent('change'); + await expect(toolkitRoot).toContainText('opacity to 0.8'); + await tuningModal + .getByRole('button', { name: 'Increment Font size', exact: true }) + .click(); + await expect(toolkitRoot).toContainText('font size to 17px'); + await tuningPanel.evaluate((element) => { + element.scrollTop = element.scrollHeight; + }); + await tuningModal.locator('select[name="font-weight"]').selectOption('700'); + await expect(toolkitRoot).toContainText('font weight to 700'); + const paddingInput = tuningModal.locator('input[name="padding"]'); + await paddingInput.fill('12'); + await paddingInput.dispatchEvent('change'); + await expect(toolkitRoot).toContainText('padding to 12px'); + await tuningModal + .getByRole('button', { name: 'Close tuning options', exact: true }) + .click(); + + await multiSelectedOverlays + .nth(1) + .getByRole('button', { name: 'Delete selection', exact: true }) + .click({ force: true }); + await expect(multiSelectedOverlays).toHaveCount(1); + + await doneButton.click(); + await expect( + page.locator('[data-agentic-react-multi-selected="true"]'), + ).toHaveCount(0); + + const clipboardText = await page.evaluate(() => + navigator.clipboard.readText(), + ); + expect(clipboardText).toContain('Selection 1'); + expect(clipboardText).toContain( + '', + ); + expect(clipboardText).toContain(''); + expect(clipboardText).toContain('selector: #profile-display-email-value'); + expect(clipboardText).toContain('tuning prompts:'); + expect(clipboardText).toContain('text color to rgb(51, 102, 153).'); + expect(clipboardText).toContain('opacity to 0.8.'); + expect(clipboardText).toContain('font size to 17px.'); + expect(clipboardText).toContain('font weight to 700.'); + expect(clipboardText).toContain('padding to 12px.'); + expect(clipboardText).not.toContain('Selection 2'); + expect(clipboardText).not.toContain( + 'selector: #profile-header-occupation-value', + ); +}); + +test('copying the selected context includes a playground source snippet', async ({ + page, +}) => { + await selectProfileEmailField(page); + await page.waitForFunction(() => + window.__AGENTIC_REACT__?.getLastSelectionContext(), + ); + + const copyResult = await page.evaluate(() => + window.__AGENTIC_REACT__.copyLastSelectionContext('json'), + ); + + expect(copyResult.success).toBe(true); + expect(copyResult.context.sourceSnippets).toContainEqual( + expect.objectContaining({ + filePath: expect.stringContaining( + 'src/components/UserProfile/ProfileField.jsx', + ), + snippet: expect.stringContaining('profile-field-${name}'), + }), + ); + expect(copyResult.context.sourcePreview).toContain('profile-field-${name}'); + expect(copyResult.context.sourcePreview).toContain('source trace:'); + expect(copyResult.context.sourcePreview).toContain( + 'ProfileField at src/components/UserProfile/ProfileField.jsx', + ); + expect(copyResult.context.sourcePreview).toContain( + 'src/components/UserProfile/ProfileContent.jsx', + ); + expect( + copyResult.context.sourceSnippets.map(({ filePath }) => filePath).join('\n'), + ).not.toContain('node_modules'); +}); + +test('toolkit tuning exposes layout controls for container elements', async ({ + page, +}) => { + await page.goto('/profile/1'); + await page.waitForFunction(() => window.__AGENTIC_REACT__); + await page.evaluate(() => { + const fixtureElement = document.createElement('div'); + fixtureElement.id = 'layout-tuning-fixture'; + fixtureElement.style.display = 'flex'; + fixtureElement.style.alignItems = 'center'; + fixtureElement.style.justifyContent = 'space-between'; + fixtureElement.style.gap = '10px'; + fixtureElement.style.margin = '12px'; + fixtureElement.style.padding = '12px'; + fixtureElement.innerHTML = + 'Layout fixtureAction'; + document.body.prepend(fixtureElement); + }); + + const toolkitRoot = getToolkitRoot(page); + await openToolkitPanel(page); + await toolkitRoot.getByRole('button', { name: 'Select', exact: true }).click(); + await page.locator('#layout-tuning-fixture').click(); + await page.waitForFunction(() => + window.__AGENTIC_REACT__?.getLastSelectionContext(), + ); + + await page + .locator('[data-agentic-react-selected="true"]') + .getByRole('button', { name: 'Adjust selection', exact: true }) + .click({ force: true }); + + const tuningModal = page.locator('[data-agentic-react-tuning-modal="true"]'); + await expect( + tuningModal.getByLabel('Layout direction', { exact: true }), + ).toBeVisible(); + await expect( + tuningModal.getByLabel('Distribution', { exact: true }), + ).toBeVisible(); + await expect( + tuningModal.getByLabel('Alignment', { exact: true }), + ).toBeVisible(); + await expect(tuningModal.getByLabel('Spacing', { exact: true })).toBeVisible(); + + await tuningModal + .locator('select[name="layout-direction"]') + .selectOption('column'); + await expect(toolkitRoot).toContainText('layout direction to column'); +}); + +test('toolkit tuning modal extensions can wrap and add modal content', async ({ + page, +}) => { + await page.goto('/profile/1'); + await page.waitForFunction(() => window.__AGENTIC_REACT__); + await page.evaluate(() => { + window.__AGENTIC_REACT__.registerTuningModalExtension({ + id: 'e2e-custom-tuning-modal', + beforeFields({ container, context }) { + const element = document.createElement('div'); + element.setAttribute('data-e2e-tuning-before', 'true'); + element.textContent = `Custom before ${context.tagName}`; + container.appendChild(element); + }, + afterFields({ container, actions }) { + const button = document.createElement('button'); + button.type = 'button'; + button.setAttribute('data-e2e-tuning-prompt', 'true'); + button.textContent = 'Add glow prompt'; + button.addEventListener('click', () => { + actions.addPrompt('Make the selected element glow.'); + }); + container.appendChild(button); + }, + footer({ container }) { + const element = document.createElement('div'); + element.setAttribute('data-e2e-tuning-footer', 'true'); + element.textContent = 'Custom footer'; + container.appendChild(element); + }, + wrapModal({ surfaceElement, panelElement }) { + const shell = document.createElement('div'); + shell.setAttribute('data-e2e-tuning-wrapper', 'true'); + shell.style.border = '2px solid rgb(14, 165, 233)'; + shell.style.padding = '4px'; + shell.style.background = 'rgb(240, 249, 255)'; + surfaceElement.replaceChildren(shell); + shell.appendChild(panelElement); + + return () => { + surfaceElement.replaceChildren(panelElement); + }; + }, + }); + }); + + const toolkitRoot = getToolkitRoot(page); + await openToolkitPanel(page); + await toolkitRoot.getByRole('button', { name: 'Select', exact: true }).click(); + await page.locator('#profile-display-email-value').click(); + await page.waitForFunction(() => + window.__AGENTIC_REACT__?.getLastSelectionContext(), + ); + + await page + .locator('[data-agentic-react-selected="true"]') + .getByRole('button', { name: 'Adjust selection', exact: true }) + .click({ force: true }); + + const tuningModal = page.locator('[data-agentic-react-tuning-modal="true"]'); + const tuningSurface = page.locator( + '[data-agentic-react-tuning-surface="true"]', + ); + const tuningPanel = page.locator('[data-agentic-react-tuning-panel="true"]'); + await expect( + tuningSurface.locator('[data-e2e-tuning-wrapper="true"]'), + ).toBeVisible(); + await expect( + tuningModal.locator('[data-e2e-tuning-before="true"]'), + ).toContainText('Custom before'); + await expect( + tuningModal.locator('[data-e2e-tuning-footer="true"]'), + ).toContainText('Custom footer'); + await expect(tuningModal.getByLabel('Text color')).toBeVisible(); + await expect(tuningModal.getByLabel('Opacity', { exact: true })).toBeVisible(); + await expect(tuningModal.getByLabel('Width', { exact: true })).toBeVisible(); + + const surfacePlacement = await tuningSurface.evaluate((element) => { + const rect = element.getBoundingClientRect(); + return { + left: rect.left, + top: rect.top, + right: rect.right, + bottom: rect.bottom, + viewportWidth: window.innerWidth, + viewportHeight: window.innerHeight, + }; + }); + expect(surfacePlacement.left).toBeGreaterThanOrEqual(-0.5); + expect(surfacePlacement.top).toBeGreaterThanOrEqual(-0.5); + expect(surfacePlacement.right).toBeLessThanOrEqual( + surfacePlacement.viewportWidth + 0.5, + ); + expect(surfacePlacement.bottom).toBeLessThanOrEqual( + surfacePlacement.viewportHeight + 0.5, + ); + + await tuningModal + .locator('[data-e2e-tuning-prompt="true"]') + .click({ force: true }); + await expect(toolkitRoot).toContainText('Make the selected element glow.'); + await expect(tuningPanel).toBeVisible(); +}); + +test('selecting an element without an id still returns a usable fallback selector', async ({ + page, +}) => { + await page.goto('/profile/1'); + await page.getByRole('button', { name: 'Edit Profile', exact: true }).click(); + await expect(page.locator('#profile-field-email')).toBeVisible(); + await page.waitForFunction(() => window.__AGENTIC_REACT__); + + await page.evaluate(() => window.__AGENTIC_REACT__.enterSelectionMode()); + await page.locator('label', { hasText: 'Email' }).first().click(); + + const context = await page.waitForFunction(() => + window.__AGENTIC_REACT__?.getLastSelectionContext(), + ); + const selectionContext = await context.jsonValue(); + + expect(selectionContext.componentName).toBe('ProfileField'); + expect(selectionContext.selector).toBeTruthy(); + + const selectorMatchesSelection = await page.evaluate((selector) => { + if (!selector) return false; + return document.querySelector(selector) !== null; + }, selectionContext.selector); + + expect(selectorMatchesSelection).toBe(true); +}); + +test('copying root-relative source context does not request filesystem /src fallback', async ({ + page, +}) => { + let requestedAbsoluteSrcFallback = false; + + await selectProfileEmailField(page); + await page.waitForFunction(() => + window.__AGENTIC_REACT__?.getLastSelectionContext(), + ); + await page.route('**/src/components/UserProfile/ProfileField.jsx**', (route) => + route.fulfill({ status: 404, body: '' }), + ); + await page.route( + '**/@fs/src/components/UserProfile/ProfileField.jsx**', + (route) => { + requestedAbsoluteSrcFallback = true; + return route.fulfill({ status: 404, body: '' }); + }, + ); + + const copyResult = await page.evaluate(() => + window.__AGENTIC_REACT__.copyLastSelectionContext('json'), + ); + + expect(copyResult.success).toBe(true); + expect(requestedAbsoluteSrcFallback).toBe(false); +}); + +test('MCP tools return no-selection and selected-context responses', async ({ + page, +}) => { + await page.goto('/profile/1'); + await page.waitForFunction(() => window.__AGENTIC_REACT__); + + const { client, transport } = await createMcpClient(); + let transportClosed = false; + try { + const noSelectionRaw = await client.callTool({ + name: 'get-last-selection-context', + arguments: { + includeSourceSnippets: true, + }, + }); + const noSelection = parseToolResponse(noSelectionRaw); + expect(noSelection.success).toBe(false); + expect(noSelection.context).toBeNull(); + expect(noSelection.message).toContain('No selection context'); + + await transport.close(); + transportClosed = true; + + await selectProfileEmailField(page); + await page.waitForFunction(() => + window.__AGENTIC_REACT__?.getLastSelectionContext(), + ); + + const selectionMcp = await createMcpClient(); + try { + const selectedContextRaw = await selectionMcp.client.callTool({ + name: 'get-last-selection-context', + arguments: { + includeSourceSnippets: true, + contextLines: 3, + maxFiles: 2, + }, + }); + const selectedContext = parseToolResponse(selectedContextRaw); + expect(selectedContext.success).toBe(true); + expect(selectedContext.context.componentName).toBe('ProfileField'); + expect(selectedContext.context.sourceSnippets.length).toBeGreaterThan(0); + expect(selectedContext.summary).toContain('ProfileField'); + } finally { + await selectionMcp.transport.close(); + } + } finally { + if (!transportClosed) { + await transport.close(); + } + } +}); + +test('MCP get-html-elements and get-react-source-code return deterministic matches and source context', async ({ + page, +}) => { + await page.goto('/profile/1'); + await page.getByRole('button', { name: 'Edit Profile', exact: true }).click(); + await page.waitForFunction(() => window.__AGENTIC_REACT__); + + const { client, transport } = await createMcpClient(); + try { + const htmlMatchesRaw = await client.callTool({ + name: 'get-html-elements', + arguments: { + queries: ['profile-field-email'], + maxMatches: 3, + }, + }); + const htmlMatches = parseToolResponse(htmlMatchesRaw); + + expect(htmlMatches.success).toBe(true); + expect(htmlMatches.count).toBeGreaterThan(0); + expect(htmlMatches.matches[0].selector).toBe('#profile-field-email'); + + const reactSourceRaw = await client.callTool({ + name: 'get-react-source-code', + arguments: { + queries: ['profile-field-email'], + maxMatches: 3, + includeSourceSnippets: true, + contextLines: 4, + maxFiles: 3, + }, + }); + const reactSource = parseToolResponse(reactSourceRaw); + + expect(reactSource.success).toBe(true); + expect(reactSource.chosenMatch.selector).toBe('#profile-field-email'); + expect(reactSource.context.componentName).toBe('ProfileField'); + expect(reactSource.context.sourceSnippets).toContainEqual( + expect.objectContaining({ + filePath: expect.stringContaining( + 'src/components/UserProfile/ProfileField.jsx', + ), + }), + ); + expect(reactSource.summary).toContain('ProfileField'); + } finally { + await transport.close(); + } +}); + +test('MCP get-react-source-code returns no-match payload when queries miss all elements', async ({ + page, +}) => { + await page.goto('/profile/1'); + await page.waitForFunction(() => window.__AGENTIC_REACT__); + + const { client, transport } = await createMcpClient(); + try { + const missRaw = await client.callTool({ + name: 'get-react-source-code', + arguments: { + queries: ['this-does-not-exist-anywhere'], + }, + }); + const miss = parseToolResponse(missRaw); + expect(miss.success).toBe(false); + expect(miss.reason).toContain('No element matched'); + expect(miss.context).toBeNull(); + } finally { + await transport.close(); + } +}); + +test('toolkit bottom-right UI can capture reusable component source context across parents', async ({ + page, +}) => { + await page.goto('/profile/1'); + await page.waitForFunction(() => window.__AGENTIC_REACT__); + + const toolkitRoot = getToolkitRoot(page); + const toolkitPosition = await toolkitRoot.evaluate((element) => { + const styles = window.getComputedStyle(element); + return { + position: styles.position, + right: styles.right, + bottom: styles.bottom, + }; + }); + expect(toolkitPosition.position).toBe('fixed'); + expect(toolkitPosition.right).toBe('20px'); + expect(toolkitPosition.bottom).toBe('20px'); + + await captureWithToolkitUi(page, '#profile-display-email-value'); + + const profileFieldMcp = await createMcpClient(); + try { + const profileFieldContextRaw = await profileFieldMcp.client.callTool({ + name: 'get-last-selection-context', + arguments: { + includeSourceSnippets: true, + contextLines: 4, + maxFiles: 4, + }, + }); + const profileFieldContext = parseToolResponse(profileFieldContextRaw); + expect(profileFieldContext.success).toBe(true); + expect(profileFieldContext.context.selector).toBe('#profile-display-email-value'); + expect(profileFieldContext.context.resolvedSources).toContainEqual( + expect.objectContaining({ + filePath: expect.stringContaining('src/components/Common/LabeledValue.jsx'), + }), + ); + expect(profileFieldContext.context.resolvedSources).toContainEqual( + expect.objectContaining({ + filePath: expect.stringContaining('src/components/UserProfile/ProfileField.jsx'), + }), + ); + } finally { + await profileFieldMcp.transport.close(); + } + + await captureWithToolkitUi(page, '#profile-header-occupation-value'); + + const profileHeaderMcp = await createMcpClient(); + try { + const profileHeaderContextRaw = await profileHeaderMcp.client.callTool({ + name: 'get-last-selection-context', + arguments: { + includeSourceSnippets: true, + contextLines: 4, + maxFiles: 4, + }, + }); + const profileHeaderContext = parseToolResponse(profileHeaderContextRaw); + expect(profileHeaderContext.success).toBe(true); + expect(profileHeaderContext.context.selector).toBe( + '#profile-header-occupation-value', + ); + expect(profileHeaderContext.context.resolvedSources).toContainEqual( + expect.objectContaining({ + filePath: expect.stringContaining('src/components/Common/LabeledValue.jsx'), + }), + ); + expect(profileHeaderContext.context.resolvedSources).toContainEqual( + expect.objectContaining({ + filePath: expect.stringContaining('src/components/UserProfile/ProfileHeader.jsx'), + }), + ); + } finally { + await profileHeaderMcp.transport.close(); + } +}); + +test('MCP built-in tooling endpoints all return expected outcomes', async ({ + page, +}) => { + await page.goto('/profile/1'); + await page.waitForFunction(() => window.__AGENTIC_REACT__); + + const { client, transport } = await createMcpClient(); + let transportClosed = false; + try { + const highlightRaw = await client.callTool({ + name: 'highlight-component', + arguments: { componentName: 'ProfileField' }, + }); + const highlightText = getToolText(highlightRaw); + expect(highlightText).toContain('highlighted'); + + const treeRaw = await client.callTool({ + name: 'get-component-tree', + arguments: { allComponents: false }, + }); + const treeText = getToolText(treeRaw); + const tree = JSON.parse(treeText); + expect(tree).toBeTruthy(); + expect(JSON.stringify(tree)).toContain('UserProfile'); + + const statesRaw = await client.callTool({ + name: 'get-component-states', + arguments: { componentName: 'ProfileField' }, + }); + const statesText = getToolText(statesRaw); + const states = JSON.parse(statesText); + expect(states).toBeTruthy(); + expect(Object.keys(states).length).toBeGreaterThan(0); + + const rerendersRaw = await client.callTool({ + name: 'get-unnecessary-rerenders', + arguments: { timeframe: 60, allComponents: true }, + }); + const rerendersText = getToolText(rerendersRaw); + const rerenders = JSON.parse(rerendersText); + expect(Array.isArray(rerenders)).toBe(true); + + await transport.close(); + transportClosed = true; + + await page.getByRole('button', { name: 'Edit Profile', exact: true }).click(); + await expect(page.locator('#profile-field-email')).toBeVisible(); + await page.evaluate(() => window.__AGENTIC_REACT__.setSelectionMode(true)); + await page.locator('#profile-field-email').click(); + await page.waitForFunction( + () => + window.__AGENTIC_REACT__?.getLastSelectionContext()?.selector === + '#profile-field-email', + ); + const selectionMcp = await createMcpClient(); + try { + const copiedRaw = await selectionMcp.client.callTool({ + name: 'copy-last-selection-context', + arguments: { format: 'json' }, + }); + const copied = parseToolResponse(copiedRaw); + expect(typeof copied.success).toBe('boolean'); + expect(typeof copied.copied).toBe('boolean'); + expect(copied.format).toBe('json'); + expect(copied.context).toBeTruthy(); + if (!copied.success) { + expect(copied.error).toContain('Failed to copy context'); + } + } finally { + await selectionMcp.transport.close(); + } + } finally { + if (!transportClosed) { + await transport.close(); + } + } +}); + +test('MCP custom tools execute inside the browser runtime', async ({ page }) => { + await page.goto('/profile/1'); + await page.waitForFunction(() => window.__AGENTIC_REACT__); + + const { client, transport } = await createMcpClient(); + try { + const customToolRaw = await client.callTool({ + name: 'log1', + arguments: { message: 'browser-runtime-check' }, + }); + const customToolResult = parseToolResponse(customToolRaw); + + expect(customToolResult.success).toBe(true); + expect(customToolResult.runtime).toBe('browser'); + expect(customToolResult.message).toBe( + 'Log1 received: browser-runtime-check', + ); + + const browserCalls = await page.evaluate( + () => window.__AGENTIC_REACT_CUSTOM_TOOL_CALLS__ || [], + ); + expect(browserCalls).toContain('browser-runtime-check'); + } finally { + await transport.close(); + } +}); diff --git a/playground/user-profile-app/index.html b/playground/agentic-react-vite-playground/index.html similarity index 100% rename from playground/user-profile-app/index.html rename to playground/agentic-react-vite-playground/index.html diff --git a/playground/user-profile-app/package.json b/playground/agentic-react-vite-playground/package.json similarity index 79% rename from playground/user-profile-app/package.json rename to playground/agentic-react-vite-playground/package.json index cecc694..a29c3a7 100644 --- a/playground/user-profile-app/package.json +++ b/playground/agentic-react-vite-playground/package.json @@ -1,9 +1,11 @@ { - "name": "user-profile-app", + "name": "agentic-react-vite-playground", "type": "module", "scripts": { "start": "vite", "dev": "vite", + "e2e": "env -u NO_COLOR playwright test", + "test:e2e": "pnpm -C ../.. run build && pnpm run e2e", "build": "vite build", "preview": "vite preview" }, @@ -22,11 +24,12 @@ }, "devDependencies": { "@babel/preset-react": "^7.26.3", + "@playwright/test": "^1.59.1", "@types/react": "catalog:", "@types/react-dom": "catalog:", "@vitejs/plugin-react": "^4.3.4", - "vite": "^6.2.3", - "vite-react-mcp": "workspace:*" + "vite": "^6.4.2", + "@agentic-react/vite": "workspace:*" }, "browserslist": { "production": [ diff --git a/playground/agentic-react-vite-playground/playwright.config.js b/playground/agentic-react-vite-playground/playwright.config.js new file mode 100644 index 0000000..9af2921 --- /dev/null +++ b/playground/agentic-react-vite-playground/playwright.config.js @@ -0,0 +1,26 @@ +import { defineConfig, devices } from '@playwright/test'; + +export default defineConfig({ + testDir: './e2e', + timeout: 30000, + expect: { + timeout: 5000, + }, + use: { + baseURL: 'http://127.0.0.1:51423', + permissions: ['clipboard-read', 'clipboard-write'], + trace: 'retain-on-failure', + }, + webServer: { + command: 'pnpm dev --host 127.0.0.1', + url: 'http://127.0.0.1:51423', + reuseExistingServer: false, + timeout: 30000, + }, + projects: [ + { + name: 'chromium', + use: { ...devices['Desktop Chrome'] }, + }, + ], +}); diff --git a/playground/agentic-react-vite-playground/public/agentic-react-logo.png b/playground/agentic-react-vite-playground/public/agentic-react-logo.png new file mode 100644 index 0000000..029bf21 Binary files /dev/null and b/playground/agentic-react-vite-playground/public/agentic-react-logo.png differ diff --git a/playground/user-profile-app/public/favicon.ico b/playground/agentic-react-vite-playground/public/favicon.ico similarity index 100% rename from playground/user-profile-app/public/favicon.ico rename to playground/agentic-react-vite-playground/public/favicon.ico diff --git a/playground/agentic-react-vite-playground/public/generated/customer-operations-directory.png b/playground/agentic-react-vite-playground/public/generated/customer-operations-directory.png new file mode 100644 index 0000000..1363402 Binary files /dev/null and b/playground/agentic-react-vite-playground/public/generated/customer-operations-directory.png differ diff --git a/playground/user-profile-app/public/index.html b/playground/agentic-react-vite-playground/public/index.html similarity index 100% rename from playground/user-profile-app/public/index.html rename to playground/agentic-react-vite-playground/public/index.html diff --git a/playground/user-profile-app/public/logo192.png b/playground/agentic-react-vite-playground/public/logo192.png similarity index 100% rename from playground/user-profile-app/public/logo192.png rename to playground/agentic-react-vite-playground/public/logo192.png diff --git a/playground/user-profile-app/public/logo512.png b/playground/agentic-react-vite-playground/public/logo512.png similarity index 100% rename from playground/user-profile-app/public/logo512.png rename to playground/agentic-react-vite-playground/public/logo512.png diff --git a/playground/user-profile-app/public/manifest.json b/playground/agentic-react-vite-playground/public/manifest.json similarity index 100% rename from playground/user-profile-app/public/manifest.json rename to playground/agentic-react-vite-playground/public/manifest.json diff --git a/playground/user-profile-app/public/robots.txt b/playground/agentic-react-vite-playground/public/robots.txt similarity index 100% rename from playground/user-profile-app/public/robots.txt rename to playground/agentic-react-vite-playground/public/robots.txt diff --git a/playground/user-profile-app/src/App.css b/playground/agentic-react-vite-playground/src/App.css similarity index 76% rename from playground/user-profile-app/src/App.css rename to playground/agentic-react-vite-playground/src/App.css index 78dbf57..46ca728 100644 --- a/playground/user-profile-app/src/App.css +++ b/playground/agentic-react-vite-playground/src/App.css @@ -1,23 +1,16 @@ .App { min-height: 100vh; - background-color: #f5f5f5; - padding: 20px 0; + background-color: #f8fafc; } body { margin: 0; - font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, + font-family: Inter, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; } -@media (max-width: 600px) { - .App { - padding: 10px 0; - } -} - .App-logo { height: 40vmin; pointer-events: none; diff --git a/playground/agentic-react-vite-playground/src/App.jsx b/playground/agentic-react-vite-playground/src/App.jsx new file mode 100644 index 0000000..292c32a --- /dev/null +++ b/playground/agentic-react-vite-playground/src/App.jsx @@ -0,0 +1,104 @@ +import React from 'react'; +import { BrowserRouter as Router, Routes, Route, Navigate } from 'react-router-dom'; +import './App.css'; +import { UserProfile, UserList } from './components'; +import { ThemeProvider, createTheme } from '@mui/material/styles'; +import CssBaseline from '@mui/material/CssBaseline'; +import { UserProvider } from './context/UserContext'; + +const theme = createTheme({ + shape: { + borderRadius: 8, + }, + palette: { + background: { + default: '#f8fafc', + paper: '#ffffff', + }, + primary: { + main: '#0f766e', + }, + secondary: { + main: '#7c3aed', + }, + success: { + main: '#15803d', + }, + warning: { + main: '#b45309', + }, + }, + typography: { + fontFamily: + 'Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif', + h3: { + fontSize: 'clamp(2rem, 4vw, 3.2rem)', + fontWeight: 800, + letterSpacing: 0, + lineHeight: 1, + }, + h4: { + fontWeight: 800, + letterSpacing: 0, + }, + h5: { + fontWeight: 800, + letterSpacing: 0, + }, + h6: { + fontWeight: 800, + letterSpacing: 0, + }, + button: { + fontWeight: 800, + letterSpacing: 0, + textTransform: 'none', + }, + }, + components: { + MuiButton: { + styleOverrides: { + root: { + minHeight: 40, + }, + }, + }, + MuiCard: { + styleOverrides: { + root: { + borderRadius: 8, + }, + }, + }, + MuiPaper: { + styleOverrides: { + root: { + backgroundImage: 'none', + }, + }, + }, + }, +}); + +const App = function () { + return ( + + + + + + + } /> + } /> + } /> + } /> + + + + + + ); +} + +export default App; +// For babel test diff --git a/playground/user-profile-app/src/App.test.js b/playground/agentic-react-vite-playground/src/App.test.js similarity index 100% rename from playground/user-profile-app/src/App.test.js rename to playground/agentic-react-vite-playground/src/App.test.js diff --git a/playground/agentic-react-vite-playground/src/components/Common/LabeledValue.jsx b/playground/agentic-react-vite-playground/src/components/Common/LabeledValue.jsx new file mode 100644 index 0000000..614566a --- /dev/null +++ b/playground/agentic-react-vite-playground/src/components/Common/LabeledValue.jsx @@ -0,0 +1,45 @@ +import React, { memo } from 'react'; + +const LabeledValue = memo( + ({ + label, + value, + idBase = null, + containerStyle = {}, + labelStyle = {}, + valueStyle = {}, + }) => { + const rootId = idBase || undefined; + const valueId = idBase ? `${idBase}-value` : undefined; + + return ( + + + {label} + + + {value} + + + ); + }, +); + +LabeledValue.displayName = 'LabeledValue'; + +export default LabeledValue; diff --git a/playground/user-profile-app/src/components/UserList/EmptyResult.jsx b/playground/agentic-react-vite-playground/src/components/UserList/EmptyResult.jsx similarity index 100% rename from playground/user-profile-app/src/components/UserList/EmptyResult.jsx rename to playground/agentic-react-vite-playground/src/components/UserList/EmptyResult.jsx diff --git a/playground/agentic-react-vite-playground/src/components/UserList/UserList.jsx b/playground/agentic-react-vite-playground/src/components/UserList/UserList.jsx new file mode 100644 index 0000000..87a2c29 --- /dev/null +++ b/playground/agentic-react-vite-playground/src/components/UserList/UserList.jsx @@ -0,0 +1,135 @@ +import React, { useState } from 'react'; +import { + Box, + Divider, + Grid, + List, + Stack, + Typography, +} from '@mui/material'; +import GroupsIcon from '@mui/icons-material/Groups'; +import PersonSearchIcon from '@mui/icons-material/PersonSearch'; +import TaskAltIcon from '@mui/icons-material/TaskAlt'; +import TuneIcon from '@mui/icons-material/Tune'; +import { useNavigate } from 'react-router-dom'; +import UserSearch from './UserSearch'; +import UserRow from './UserRow'; +import EmptyResult from './EmptyResult'; +import { mockUsers } from './mockData'; +import { + AppSurface, + DataPanel, + MetricCard, + PageHeader, + StatusChip, + ToolbarSurface, + VisualBriefing, +} from '../ui/MuiPlaygroundPrimitives'; + +const UserList = () => { + const [searchTerm, setSearchTerm] = useState(''); + const navigate = useNavigate(); + + const handleSearchChange = (event) => { + setSearchTerm(event.target.value); + }; + + const handleUserClick = (userId) => { + navigate(`/profile/${userId}`); + }; + + const filteredUsers = mockUsers.filter((user) => + user.firstName.toLowerCase().includes(searchTerm.toLowerCase()) || + user.lastName.toLowerCase().includes(searchTerm.toLowerCase()) || + user.occupation.toLowerCase().includes(searchTerm.toLowerCase()) || + user.location.toLowerCase().includes(searchTerm.toLowerCase()) + ); + + return ( + + } + actions={}>Runtime ready} + /> + + + + Tier 1 contacts verified + Escalation owners assigned + Profile edits audited + + + + + + } label="People indexed" value={mockUsers.length} /> + + + } + label="Filtered results" + value={filteredUsers.length} + tone="secondary" + /> + + + } label="Selector surface" value="MUI wrapped" tone="warning" /> + + + + + + + + + + Directory command bar + + + Search by name, role, location, or operating domain; results route to editable profiles with validated contact fields. + + + + + + + + {searchTerm ? `Query: ${searchTerm}` : 'All users'}} + > + + {filteredUsers.length > 0 ? ( + filteredUsers.map((user, index) => ( + + handleUserClick(user.id)} + /> + {index < filteredUsers.length - 1 && + + } + + )) + ) : ( + + )} + + + + ); +}; + + +export default UserList; diff --git a/playground/agentic-react-vite-playground/src/components/UserList/UserRow.jsx b/playground/agentic-react-vite-playground/src/components/UserList/UserRow.jsx new file mode 100644 index 0000000..24c09a9 --- /dev/null +++ b/playground/agentic-react-vite-playground/src/components/UserList/UserRow.jsx @@ -0,0 +1,80 @@ +import React from 'react'; +import { + Avatar, + Box, + Chip, + ListItem, + ListItemAvatar, + ListItemText, + Stack, +} from '@mui/material'; +import ApartmentIcon from '@mui/icons-material/Apartment'; +import ChevronRightIcon from '@mui/icons-material/ChevronRight'; +import PlaceIcon from '@mui/icons-material/Place'; +import LabeledValue from '../Common/LabeledValue'; + +const UserRow = ({ user, onClick }) => { + return ( + } + sx={{ + px: 2.5, + py: 2, + transition: 'background-color 0.2s, transform 0.2s', + '&:hover': { + backgroundColor: 'rgba(15, 118, 110, 0.06)', + transform: 'translateX(2px)', + }, + }} + > + + + {user.firstName[0]}{user.lastName[0]} + + + + + {user.firstName} {user.lastName} + + + + } + secondary={ + + + Role>} + value={user.occupation} + labelStyle={{ alignItems: 'center', display: 'flex', gap: '4px', fontSize: '0.72rem' }} + valueStyle={{ marginTop: '0', fontSize: '0.9rem', fontWeight: 700 }} + /> + Location>} + value={user.location} + labelStyle={{ alignItems: 'center', display: 'flex', gap: '4px', fontSize: '0.72rem' }} + valueStyle={{ marginTop: '0', fontSize: '0.9rem', fontWeight: 700 }} + /> + + + } + /> + + ); +}; + +export default UserRow; diff --git a/playground/agentic-react-vite-playground/src/components/UserList/UserSearch.jsx b/playground/agentic-react-vite-playground/src/components/UserList/UserSearch.jsx new file mode 100644 index 0000000..349e44c --- /dev/null +++ b/playground/agentic-react-vite-playground/src/components/UserList/UserSearch.jsx @@ -0,0 +1,26 @@ +import React from 'react'; +import { InputAdornment, TextField } from '@mui/material'; +import SearchIcon from '@mui/icons-material/Search'; + +const UserSearch = ({ searchTerm, handleSearchChange }) => { + return ( + + + + ), + }} + /> + ); +}; + + +export default UserSearch; diff --git a/playground/user-profile-app/src/components/UserList/index.jsx b/playground/agentic-react-vite-playground/src/components/UserList/index.jsx similarity index 100% rename from playground/user-profile-app/src/components/UserList/index.jsx rename to playground/agentic-react-vite-playground/src/components/UserList/index.jsx diff --git a/playground/agentic-react-vite-playground/src/components/UserList/mockData.js b/playground/agentic-react-vite-playground/src/components/UserList/mockData.js new file mode 100644 index 0000000..72d1000 --- /dev/null +++ b/playground/agentic-react-vite-playground/src/components/UserList/mockData.js @@ -0,0 +1,53 @@ +// Mock user data +export const mockUsers = [ + { + id: 1, + firstName: 'Avery', + lastName: 'Stone', + email: 'avery.stone@northstar.health', + phone: '(415) 610-1842', + bio: 'Owns the patient intake web surfaces for a multi-region healthcare platform, with a focus on accessible form design, service recovery dashboards, and measurable conversion improvements.', + location: 'San Francisco, CA', + occupation: 'Clinical Experience Lead', + }, + { + id: 2, + firstName: 'Priya', + lastName: 'Nair', + email: 'priya.nair@ledgerworks.io', + phone: '(212) 884-7301', + bio: 'Coordinates merchant onboarding, KYC exception review, and high-value account escalations for a regulated payments platform serving marketplace customers.', + location: 'New York, NY', + occupation: 'Payments Operations Manager', + }, + { + id: 3, + firstName: 'Mateo', + lastName: 'Johnson', + email: 'mateo.johnson@atlasgrid.energy', + phone: '(512) 449-2018', + bio: 'Maintains outage communication workflows, field dispatch views, and executive reliability summaries for distributed energy infrastructure teams.', + location: 'Austin, TX', + occupation: 'Grid Reliability Analyst', + }, + { + id: 4, + firstName: 'Elena', + lastName: 'Williams', + email: 'elena.williams@harborfreightlogistics.com', + phone: '(206) 730-9188', + bio: 'Leads shipment exception triage across ocean freight, customs holds, and warehouse appointment queues with daily SLA reporting to enterprise shippers.', + location: 'Seattle, WA', + occupation: 'Logistics Program Director', + }, + { + id: 5, + firstName: 'Noah', + lastName: 'Brown', + email: 'noah.brown@civiccloud.gov', + phone: '(312) 507-4410', + bio: 'Runs constituent support analytics, public records routing, and secure case escalation workflows for a municipal digital services organization.', + location: 'Chicago, IL', + occupation: 'Civic Services Architect', + }, +]; diff --git a/playground/user-profile-app/src/components/UserProfile/ProfileContent.jsx b/playground/agentic-react-vite-playground/src/components/UserProfile/ProfileContent.jsx similarity index 89% rename from playground/user-profile-app/src/components/UserProfile/ProfileContent.jsx rename to playground/agentic-react-vite-playground/src/components/UserProfile/ProfileContent.jsx index 92ae0d8..2425358 100644 --- a/playground/user-profile-app/src/components/UserProfile/ProfileContent.jsx +++ b/playground/agentic-react-vite-playground/src/components/UserProfile/ProfileContent.jsx @@ -1,5 +1,5 @@ import React from 'react'; -import { Box, Button, Grid, CardContent } from '@mui/material'; +import { Box, Button, CardContent, Grid, Stack } from '@mui/material'; import EditIcon from '@mui/icons-material/Edit'; import SaveIcon from '@mui/icons-material/Save'; import CancelIcon from '@mui/icons-material/Cancel'; @@ -14,7 +14,6 @@ const ProfileContent = () => { } return ( - <> @@ -77,7 +76,7 @@ const ProfileContent = () => { {!editMode && ( - + { )} {editMode && ( - + { > Cancel - + )} - > ); }; -export default ProfileContent; \ No newline at end of file +export default ProfileContent; diff --git a/playground/agentic-react-vite-playground/src/components/UserProfile/ProfileField.jsx b/playground/agentic-react-vite-playground/src/components/UserProfile/ProfileField.jsx new file mode 100644 index 0000000..fcf4a67 --- /dev/null +++ b/playground/agentic-react-vite-playground/src/components/UserProfile/ProfileField.jsx @@ -0,0 +1,51 @@ +import React, { memo } from 'react'; +import { Paper, TextField } from '@mui/material'; +import { useProfile } from '../../context/ProfileContext'; +import LabeledValue from '../Common/LabeledValue'; + +const ProfileField = memo(({ + label, + name, + editMode, + multiline = false, + rows = 1 +}) => { + const { tempData, profileData, handleInputChange } = useProfile(); + const value = editMode ? tempData[name] : profileData[name]; + if (editMode) { + const commonProps = { + 'data-ext-id': `profile-field-${name}`, + id: `profile-field-${name}`, + name: name, + value: value, + onChange: handleInputChange, + }; + + return ( + + ); + } + + return ( + + + + ); +}); +ProfileField.displayName = 'ProfileField'; + +export default ProfileField; diff --git a/playground/user-profile-app/src/components/UserProfile/ProfileHeader.jsx b/playground/agentic-react-vite-playground/src/components/UserProfile/ProfileHeader.jsx similarity index 50% rename from playground/user-profile-app/src/components/UserProfile/ProfileHeader.jsx rename to playground/agentic-react-vite-playground/src/components/UserProfile/ProfileHeader.jsx index 0988b69..c1079f0 100644 --- a/playground/user-profile-app/src/components/UserProfile/ProfileHeader.jsx +++ b/playground/agentic-react-vite-playground/src/components/UserProfile/ProfileHeader.jsx @@ -1,8 +1,10 @@ import React from 'react'; -import { Box, Typography, Avatar, IconButton, Divider } from '@mui/material'; +import { Avatar, Box, Divider, IconButton, Stack, Typography } from '@mui/material'; import EditIcon from '@mui/icons-material/Edit'; import SaveIcon from '@mui/icons-material/Save'; import CancelIcon from '@mui/icons-material/Cancel'; +import WorkIcon from '@mui/icons-material/Work'; +import LabeledValue from '../Common/LabeledValue'; const ProfileHeader = ({ profileData, @@ -13,43 +15,61 @@ const ProfileHeader = ({ }) => { return ( <> - - + + {profileData.firstName[0]}{profileData.lastName[0]} - + {profileData.firstName} {profileData.lastName} - - {profileData.occupation} - + Role>} + value={profileData.occupation} + containerStyle={{ marginTop: '6px' }} + labelStyle={{ alignItems: 'center', display: 'flex', gap: '4px', fontSize: '0.72rem' }} + valueStyle={{ marginTop: '2px', fontSize: '0.95rem', fontWeight: 700 }} + /> - + {!editMode ? ( ) : ( - + @@ -57,10 +77,11 @@ const ProfileHeader = ({ color="error" onClick={onCancel} aria-label="cancel edit" + sx={{ border: '1px solid', borderColor: 'divider' }} > - + )} @@ -70,4 +91,4 @@ const ProfileHeader = ({ ProfileHeader.displayName = 'ProfileHeader'; -export default ProfileHeader; \ No newline at end of file +export default ProfileHeader; diff --git a/playground/user-profile-app/src/components/UserProfile/ProfileNotification.jsx b/playground/agentic-react-vite-playground/src/components/UserProfile/ProfileNotification.jsx similarity index 72% rename from playground/user-profile-app/src/components/UserProfile/ProfileNotification.jsx rename to playground/agentic-react-vite-playground/src/components/UserProfile/ProfileNotification.jsx index fa107a7..9ef2b2d 100644 --- a/playground/user-profile-app/src/components/UserProfile/ProfileNotification.jsx +++ b/playground/agentic-react-vite-playground/src/components/UserProfile/ProfileNotification.jsx @@ -1,14 +1,14 @@ import React from 'react'; import { Snackbar, Alert } from '@mui/material'; -const ProfileNotification = ({ - open, - onClose +const ProfileNotification = ({ + open, + onClose, }) => { return ( - @@ -21,4 +21,4 @@ const ProfileNotification = ({ ProfileNotification.displayName = 'ProfileNotification'; -export default ProfileNotification; \ No newline at end of file +export default ProfileNotification; diff --git a/playground/agentic-react-vite-playground/src/components/UserProfile/index.jsx b/playground/agentic-react-vite-playground/src/components/UserProfile/index.jsx new file mode 100644 index 0000000..53d8f7e --- /dev/null +++ b/playground/agentic-react-vite-playground/src/components/UserProfile/index.jsx @@ -0,0 +1,128 @@ +import React, { useState } from 'react'; +import { Box, Button, Grid } from '@mui/material'; +import { useParams, useNavigate } from 'react-router-dom'; +import ArrowBackIcon from '@mui/icons-material/ArrowBack'; +import BadgeIcon from '@mui/icons-material/Badge'; +import CheckCircleIcon from '@mui/icons-material/CheckCircle'; +import MailIcon from '@mui/icons-material/Mail'; +import ManageAccountsIcon from '@mui/icons-material/ManageAccounts'; +import PlaceIcon from '@mui/icons-material/Place'; +import ProfileHeader from './ProfileHeader'; +import ProfileContent from './ProfileContent'; +import ProfileNotification from './ProfileNotification'; +import { useUserProfileData } from '../../hooks/useUserProfileData'; +import { ProfileProvider } from '../../context/ProfileContext'; +import { + AppSurface, + DataPanel, + MetricCard, + PageHeader, + StatusChip, + VisualBriefing, +} from '../ui/MuiPlaygroundPrimitives'; +const UserProfile = () => { + const { userId } = useParams(); + const navigate = useNavigate(); + const { + profileData, + editMode, + isLoading, + handleEditToggle, + handleSave: handleSaveFromHook, + handleCancel, + } = useUserProfileData(userId); + + const [openSnackbar, setOpenSnackbar] = useState(false); + + const handleSave = () => { + const success = handleSaveFromHook(); + if (success) { + setOpenSnackbar(true); + } + }; + + const handleCloseSnackbar = () => { + setOpenSnackbar(false); + }; + + const handleBackToList = () => { + navigate('/users'); + }; + + if (isLoading) { + return Loading...; + } + + if (!profileData) { + return null; + } + + return ( + + } + actions={ + } + onClick={handleBackToList} + variant="outlined" + > + Back to User List + + } + /> + + + + + + } label="Role" value={profileData.occupation} /> + + + } label="Location" value={profileData.location} tone="secondary" /> + + + } label="Contact" value="Verified" tone="success" /> + + + + }> + {editMode ? 'Editing' : 'Saved'} + + } + > + + + + + + + + + ); +}; + +// Specifically use a uniquely-suffixed displayName to distinguish from MUI components +UserProfile.displayName = 'UserProfile'; + +export default UserProfile; diff --git a/playground/user-profile-app/src/components/index.js b/playground/agentic-react-vite-playground/src/components/index.js similarity index 100% rename from playground/user-profile-app/src/components/index.js rename to playground/agentic-react-vite-playground/src/components/index.js diff --git a/playground/agentic-react-vite-playground/src/components/ui/MuiPlaygroundPrimitives.jsx b/playground/agentic-react-vite-playground/src/components/ui/MuiPlaygroundPrimitives.jsx new file mode 100644 index 0000000..0dcd4b0 --- /dev/null +++ b/playground/agentic-react-vite-playground/src/components/ui/MuiPlaygroundPrimitives.jsx @@ -0,0 +1,206 @@ +import React from 'react'; +import { + Avatar, + Box, + Card, + CardContent, + Chip, + Container, + Divider, + Paper, + Stack, + Typography, +} from '@mui/material'; + +export function AppSurface({ children }) { + return ( + + {children} + + ); +} + +export function PageHeader({ eyebrow, title, description, icon, actions }) { + return ( + + + + + {icon} + + + + {eyebrow} + + + {title} + + + {description} + + + + {actions ? {actions} : null} + + + ); +} + +export function VisualBriefing({ imageSrc, title, body, children }) { + return ( + + + + {title} + + {body} + + {children ? {children} : null} + + + ); +} + +export function MetricCard({ icon, label, value, tone = 'primary' }) { + return ( + + + + + {icon} + + + + {label} + + {value} + + + + + ); +} + +export function ToolbarSurface({ children }) { + return ( + + {children} + + ); +} + +export function DataPanel({ title, subtitle, actions, children }) { + return ( + + + + {title} + {subtitle ? ( + + {subtitle} + + ) : null} + + {actions ? {actions} : null} + + + {children} + + ); +} + +export function StatusChip({ children, color = 'default', icon }) { + return ( + + ); +} diff --git a/playground/user-profile-app/src/context/ProfileContext.jsx b/playground/agentic-react-vite-playground/src/context/ProfileContext.jsx similarity index 100% rename from playground/user-profile-app/src/context/ProfileContext.jsx rename to playground/agentic-react-vite-playground/src/context/ProfileContext.jsx diff --git a/playground/user-profile-app/src/context/UserContext.jsx b/playground/agentic-react-vite-playground/src/context/UserContext.jsx similarity index 100% rename from playground/user-profile-app/src/context/UserContext.jsx rename to playground/agentic-react-vite-playground/src/context/UserContext.jsx diff --git a/playground/user-profile-app/src/hooks/useUserProfileData.js b/playground/agentic-react-vite-playground/src/hooks/useUserProfileData.js similarity index 100% rename from playground/user-profile-app/src/hooks/useUserProfileData.js rename to playground/agentic-react-vite-playground/src/hooks/useUserProfileData.js diff --git a/playground/user-profile-app/src/index.css b/playground/agentic-react-vite-playground/src/index.css similarity index 100% rename from playground/user-profile-app/src/index.css rename to playground/agentic-react-vite-playground/src/index.css diff --git a/playground/user-profile-app/src/index.jsx b/playground/agentic-react-vite-playground/src/index.jsx similarity index 100% rename from playground/user-profile-app/src/index.jsx rename to playground/agentic-react-vite-playground/src/index.jsx diff --git a/playground/user-profile-app/src/logo.svg b/playground/agentic-react-vite-playground/src/logo.svg similarity index 100% rename from playground/user-profile-app/src/logo.svg rename to playground/agentic-react-vite-playground/src/logo.svg diff --git a/playground/user-profile-app/src/setupTests.js b/playground/agentic-react-vite-playground/src/setupTests.js similarity index 100% rename from playground/user-profile-app/src/setupTests.js rename to playground/agentic-react-vite-playground/src/setupTests.js diff --git a/playground/agentic-react-vite-playground/tools/log1.ts b/playground/agentic-react-vite-playground/tools/log1.ts new file mode 100644 index 0000000..0c0f87e --- /dev/null +++ b/playground/agentic-react-vite-playground/tools/log1.ts @@ -0,0 +1,23 @@ +import type { ToolResultValue } from '@agentic-react/vite'; + +export default function myCustomTool(args: { + message: string; +}): ToolResultValue { + const { message } = args; + const runtime = typeof window === 'undefined' ? 'node' : 'browser'; + if (typeof window !== 'undefined') { + const targetWindow = window as typeof window & { + __AGENTIC_REACT_CUSTOM_TOOL_CALLS__?: string[]; + }; + targetWindow.__AGENTIC_REACT_CUSTOM_TOOL_CALLS__ = [ + ...(targetWindow.__AGENTIC_REACT_CUSTOM_TOOL_CALLS__ || []), + message, + ]; + } + console.info(`[custom-tool/log1] ${message}`); + return { + success: true, + message: `Log1 received: ${message}`, + runtime, + }; +} diff --git a/playground/agentic-react-vite-playground/vite.config.js b/playground/agentic-react-vite-playground/vite.config.js new file mode 100644 index 0000000..a3d8fb0 --- /dev/null +++ b/playground/agentic-react-vite-playground/vite.config.js @@ -0,0 +1,64 @@ +import { defineConfig } from 'vite'; +import react from '@vitejs/plugin-react'; +import path from 'path'; +import AgenticReact from '@agentic-react/vite'; +import { z } from 'zod'; +import log1 from './tools/log1.js'; + +export default defineConfig({ + plugins: [ + react(), + AgenticReact({ + customTools: [ + { + name: 'log1', + description: 'Log1', + schema: z.object({ + message: z.string(), + }), + clientFunction: log1, + }, + ], + toolkit: { + iconUrl: '/agentic-react-logo.png', + tuningModal: { + classNames: { + surface: 'vite-playground-tuning-surface', + panel: 'vite-playground-tuning-panel', + control: 'vite-playground-tuning-control', + }, + tokens: { + panelRadius: '14px', + controlRadius: '10px', + primaryButtonBackground: '#0f766e', + primaryButtonColor: '#ffffff', + panelShadow: '0 24px 72px rgba(15, 118, 110, 0.22)', + }, + styles: { + surface: { + filter: 'drop-shadow(0 18px 40px rgba(15, 118, 110, 0.16))', + }, + panel: { + border: '1px solid rgba(15, 118, 110, 0.22)', + }, + targetTag: { + background: '#ecfeff', + color: '#0f766e', + }, + sectionTitle: { + color: '#0f766e', + }, + }, + }, + }, + }), + ], + resolve: { + alias: { + '@': path.resolve(__dirname, './src'), + }, + }, + server: { + port: 51423, + }, +}); diff --git a/playground/agentic-react-webpack-playground/.agentic-react-webpack/client-entry.mjs b/playground/agentic-react-webpack-playground/.agentic-react-webpack/client-entry.mjs new file mode 100644 index 0000000..d780609 --- /dev/null +++ b/playground/agentic-react-webpack-playground/.agentic-react-webpack/client-entry.mjs @@ -0,0 +1,47 @@ + +import { + __AGENTIC_REACT_BRIDGE_URL__, + __AGENTIC_REACT_CONFIG__, +} from "../../../packages/core/dist/shared/const.js"; +import { BRIDGE_WS_PATH } from "../../../packages/core/dist/shared/protocol.js"; + +if (typeof window !== 'undefined') { + const existingAgenticReactConfig = window[__AGENTIC_REACT_CONFIG__] || {}; + window[__AGENTIC_REACT_CONFIG__] = { + ...existingAgenticReactConfig, + sourceRoot: existingAgenticReactConfig.sourceRoot || "/Users/jazelly/Desktop/github/my-proj/vite-react-mcp/playground/agentic-react-webpack-playground", + toolkit: { + ...(existingAgenticReactConfig.toolkit || {}), + ...{"tuningModal":{"classNames":{"surface":"webpack-playground-tuning-surface","panel":"webpack-playground-tuning-panel","control":"webpack-playground-tuning-control"},"tokens":{"panelRadius":"12px","controlRadius":"9px","primaryButtonBackground":"#1d4ed8","primaryButtonColor":"#ffffff","panelShadow":"0 24px 72px rgba(29, 78, 216, 0.22)"},"styles":{"surface":{"filter":"drop-shadow(0 18px 40px rgba(29, 78, 216, 0.16))"},"panel":{"border":"1px solid rgba(29, 78, 216, 0.24)"},"targetTag":{"background":"#eff6ff","color":"#1d4ed8"},"sectionTitle":{"color":"#1d4ed8"}}}}, + }, + }; + + if (!window[__AGENTIC_REACT_BRIDGE_URL__]) { + const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:'; + window[__AGENTIC_REACT_BRIDGE_URL__] = + `${protocol}//${window.location.host}${BRIDGE_WS_PATH}`; + } +} + +void import("../../../packages/core/dist/overlay.js"); + + + const registerTool = (name, handler) => { + const tryRegister = (attempt = 0) => { + const registry = window.__AGENTIC_REACT_TOOLS__; + if (registry?.registerCustomTool) { + registry.registerCustomTool(name, handler); + return; + } + if (attempt >= 40) { + console.warn('[agentic-react] Custom tool registration timed out for', name); + return; + } + setTimeout(() => tryRegister(attempt + 1), 50); + }; + + tryRegister(); + }; + + + diff --git a/playground/agentic-react-webpack-playground/e2e/runtime.spec.js b/playground/agentic-react-webpack-playground/e2e/runtime.spec.js new file mode 100644 index 0000000..6fc4d94 --- /dev/null +++ b/playground/agentic-react-webpack-playground/e2e/runtime.spec.js @@ -0,0 +1,227 @@ +import { expect, test } from '@playwright/test'; + +const MCP_SERVER_URL = `http://127.0.0.1:${process.env.AGENTIC_REACT_WEBPACK_PLAYGROUND_PORT || '51425'}/mcp`; + +const createMcpClient = async () => { + const [{ Client }, { StreamableHTTPClientTransport }] = await Promise.all([ + import('@modelcontextprotocol/sdk/client/index.js'), + import('@modelcontextprotocol/sdk/client/streamableHttp.js'), + ]); + const client = new Client({ + name: 'webpack-playground-mcp-e2e', + version: '0.0.0', + }); + const transport = new StreamableHTTPClientTransport(new URL(MCP_SERVER_URL)); + await client.connect(transport); + return { client, transport }; +}; + +const parseToolResponse = (result) => { + const textContent = result.content.find((content) => content.type === 'text'); + if (!textContent || typeof textContent.text !== 'string') { + return null; + } + return JSON.parse(textContent.text); +}; + +test('webpack playground injects runtime globals', async ({ page }) => { + await page.goto('/'); + await page.waitForFunction(() => window.__AGENTIC_REACT__); + + const runtimeShape = await page.evaluate(() => ({ + hasRuntime: Boolean(window.__AGENTIC_REACT__), + hasTools: Boolean(window.__AGENTIC_REACT_TOOLS__), + hasSelectionMode: typeof window.__AGENTIC_REACT__?.setSelectionMode, + hasGetContext: typeof window.__AGENTIC_REACT__?.getLastSelectionContext, + hasCopyContext: typeof window.__AGENTIC_REACT__?.copyLastSelectionContext, + hasRegisterTuningModalExtension: + typeof window.__AGENTIC_REACT__?.registerTuningModalExtension, + tuningSurfaceClassName: + window.__AGENTIC_REACT_CONFIG__?.toolkit?.tuningModal?.classNames + ?.surface, + tuningPanelClassName: + window.__AGENTIC_REACT_CONFIG__?.toolkit?.tuningModal?.classNames?.panel, + unregisterType: (() => { + const unregister = + window.__AGENTIC_REACT__?.registerTuningModalExtension({ + id: 'webpack-runtime-smoke', + }); + unregister?.(); + return typeof unregister; + })(), + })); + + expect(runtimeShape.hasRuntime).toBe(true); + expect(runtimeShape.hasTools).toBe(true); + expect(runtimeShape.hasSelectionMode).toBe('function'); + expect(runtimeShape.hasGetContext).toBe('function'); + expect(runtimeShape.hasCopyContext).toBe('function'); + expect(runtimeShape.hasRegisterTuningModalExtension).toBe('function'); + expect(runtimeShape.tuningSurfaceClassName).toBe( + 'webpack-playground-tuning-surface', + ); + expect(runtimeShape.tuningPanelClassName).toBe( + 'webpack-playground-tuning-panel', + ); + expect(runtimeShape.unregisterType).toBe('function'); +}); + +test('webpack playground MCP tools return expected outcomes', async ({ + page, +}) => { + await page.goto('/'); + await page.waitForFunction(() => window.__AGENTIC_REACT__); + + const { client, transport } = await createMcpClient(); + try { + const htmlMatchesRaw = await client.callTool({ + name: 'get-html-elements', + arguments: { + queries: ['html'], + maxMatches: 3, + }, + }); + const htmlMatches = parseToolResponse(htmlMatchesRaw); + expect(htmlMatches.success).toBe(true); + expect(htmlMatches.count).toBeGreaterThan(0); + expect(htmlMatches.matches[0].selector).toBeTruthy(); + + const reactSourceRaw = await client.callTool({ + name: 'get-react-source-code', + arguments: { + queries: ['html'], + maxMatches: 3, + includeSourceSnippets: true, + }, + }); + const reactSource = parseToolResponse(reactSourceRaw); + expect(reactSource.success).toBe(true); + expect(reactSource.chosenMatch.selector).toBeTruthy(); + expect(reactSource.context).toBeTruthy(); + + const noMatchRaw = await client.callTool({ + name: 'get-react-source-code', + arguments: { + queries: ['text-not-present-in-webpack-playground'], + }, + }); + const noMatch = parseToolResponse(noMatchRaw); + expect(noMatch.success).toBe(false); + expect(noMatch.reason).toContain('No element matched'); + expect(noMatch.context).toBeNull(); + + const enableSelectionRaw = await client.callTool({ + name: 'set-selection-mode', + arguments: { enabled: true }, + }); + const enableSelection = parseToolResponse(enableSelectionRaw); + expect(enableSelection.success).toBe(true); + expect(enableSelection.enabled).toBe(true); + + const contextRaw = await client.callTool({ + name: 'get-last-selection-context', + arguments: { + includeSourceSnippets: true, + contextLines: 3, + maxFiles: 3, + }, + }); + const context = parseToolResponse(contextRaw); + expect(context.success).toBe(false); + expect(context.context).toBeNull(); + + const copyRaw = await client.callTool({ + name: 'copy-last-selection-context', + arguments: { format: 'json' }, + }); + const copyResponse = parseToolResponse(copyRaw); + expect(copyResponse.success).toBe(false); + expect(copyResponse.copied).toBe(false); + expect(copyResponse.error).toContain('No selection context found'); + } finally { + await transport.close(); + } +}); + +test('webpack selection captures the primary CTA as userland AppContent source', async ({ + page, +}) => { + await page.goto('/'); + await page.waitForFunction(() => window.__AGENTIC_REACT__); + + await page.evaluate(() => window.__AGENTIC_REACT__.setSelectionMode(true)); + + const primaryCta = page.locator('.primary-cta'); + await expect(primaryCta).toBeVisible(); + const ctaBox = await primaryCta.boundingBox(); + expect(ctaBox).toBeTruthy(); + await page.mouse.move( + ctaBox.x + ctaBox.width / 2, + ctaBox.y + ctaBox.height / 2, + ); + + const hoverLabel = page.locator('[data-agentic-react-hover-label="true"]'); + await expect(hoverLabel).toHaveText(' in App.jsx:489'); + const hoverLabelBox = await hoverLabel.boundingBox(); + expect(hoverLabelBox).toBeTruthy(); + expect(hoverLabelBox.y + hoverLabelBox.height).toBeLessThanOrEqual( + ctaBox.y, + ); + + await primaryCta.click(); + await page.waitForFunction( + () => + window.__AGENTIC_REACT__?.getLastSelectionContext()?.selector === + '.hero > .hero-copy > .primary-cta', + ); + const selectedLabel = page.locator( + '[data-agentic-react-selected-label="true"]', + ); + await expect(selectedLabel).toHaveText(' in App.jsx:489'); + const selectedLabelBox = await selectedLabel.boundingBox(); + expect(selectedLabelBox).toBeTruthy(); + expect(selectedLabelBox.y + selectedLabelBox.height).toBeLessThanOrEqual( + ctaBox.y, + ); + + const runtimeContext = await page.evaluate(() => + window.__AGENTIC_REACT__.getLastSelectionContext(), + ); + expect(runtimeContext.componentName).toBe('AppContent'); + expect(runtimeContext.externalComponent).toBeNull(); + expect(runtimeContext.resolvedSources).toContainEqual( + expect.objectContaining({ + componentName: 'AppContent', + filePath: expect.stringContaining('src/App.jsx'), + lineNumber: 489, + }), + ); + + const { client, transport } = await createMcpClient(); + try { + const selectedContextRaw = await client.callTool({ + name: 'get-last-selection-context', + arguments: { + includeSourceSnippets: true, + contextLines: 3, + maxFiles: 2, + }, + }); + const selectedContext = parseToolResponse(selectedContextRaw); + expect(selectedContext.success).toBe(true); + expect(selectedContext.summary).toContain('component: AppContent'); + expect(selectedContext.summary).toContain('src/App.jsx:489'); + expect(selectedContext.summary).not.toContain( + 'selected external component:', + ); + expect(selectedContext.summary).not.toContain('component: a'); + expect(selectedContext.context.sourceSnippets).toContainEqual( + expect.objectContaining({ + filePath: 'src/App.jsx', + snippet: expect.stringContaining('className="primary-cta"'), + }), + ); + } finally { + await transport.close(); + } +}); diff --git a/playground/agentic-react-webpack-playground/hyperframes/multiselect-plugin-demo/AGENTS.md b/playground/agentic-react-webpack-playground/hyperframes/multiselect-plugin-demo/AGENTS.md new file mode 100644 index 0000000..6876b63 --- /dev/null +++ b/playground/agentic-react-webpack-playground/hyperframes/multiselect-plugin-demo/AGENTS.md @@ -0,0 +1,62 @@ +# HyperFrames Composition Project + +## Skills + +This project uses AI agent skills for framework-specific patterns. Install them if not already present: + +```bash +npx skills add heygen-com/hyperframes +``` + +Skills encode patterns like `window.__timelines` registration, `data-*` attribute semantics, Tailwind v4 browser-runtime styling for `--tailwind` projects, and shader-compatible CSS rules that are not in generic web docs. Using them produces correct compositions from the start. + +## Commands + +```bash +npm run dev # start the preview server (long-running — keep it alive in background) +npm run check # lint + validate + inspect +npm run render # render to MP4 +npm run publish # publish and get a shareable link +npx hyperframes docs # reference docs in terminal +``` + +> **`npm run dev` is a long-running server, not a one-shot command.** It blocks until stopped. +> Always run it as a background process so it stays alive while you edit compositions. +> Running it in the foreground will time out and kill the server, breaking the browser preview. + +## Project Structure + +- `index.html` — main composition (root timeline) +- `compositions/` — sub-compositions referenced via `data-composition-src` +- `assets/` — media files (video, audio, images) +- `meta.json` — project metadata (id, name) +- `transcript.json` — whisper word-level transcript (if generated) + +## Linting — Always Run After Changes + +After creating or editing any `.html` composition, run the full check before considering the task complete: + +```bash +npm run check +``` + +Fix all errors before presenting the result. + +## Key Rules + +1. Every timed element needs `data-start`, `data-duration`, and `data-track-index` +2. Visible timed elements **must** have `class="clip"` — the framework uses this for visibility control +3. GSAP timelines must be paused and registered on `window.__timelines`: + ```js + window.__timelines = window.__timelines || {}; + window.__timelines["composition-id"] = gsap.timeline({ paused: true }); + ``` +4. Videos use `muted` with a separate `` element for the audio track +5. Sub-compositions use `data-composition-src="compositions/file.html"` +6. Only deterministic logic — no `Date.now()`, no `Math.random()`, no network fetches + +## Documentation + +Full docs: https://hyperframes.heygen.com/introduction + +Machine-readable index for AI tools: https://hyperframes.heygen.com/llms.txt diff --git a/playground/agentic-react-webpack-playground/hyperframes/multiselect-plugin-demo/CLAUDE.md b/playground/agentic-react-webpack-playground/hyperframes/multiselect-plugin-demo/CLAUDE.md new file mode 100644 index 0000000..2359316 --- /dev/null +++ b/playground/agentic-react-webpack-playground/hyperframes/multiselect-plugin-demo/CLAUDE.md @@ -0,0 +1,85 @@ +# HyperFrames Composition Project + +## Skills — USE THESE FIRST + +**Always invoke the relevant skill before writing or modifying compositions.** Skills encode framework-specific patterns (e.g., `window.__timelines` registration, `data-*` attribute semantics, shader-compatible CSS rules) that are NOT in generic web docs. Skipping them produces broken compositions. + +| Skill | Command | When to use | +| -------------------------- | ------------------------- | ------------------------------------------------------------------------------------------------- | +| **hyperframes** | `/hyperframes` | Creating or editing HTML compositions, captions, TTS, audio-reactive animation, marker highlights | +| **hyperframes-cli** | `/hyperframes-cli` | Dev-loop CLI: init, lint, inspect, preview, render, doctor | +| **hyperframes-media** | `/hyperframes-media` | Asset preprocessing: tts (Kokoro), transcribe (Whisper), remove-background (u2net) | +| **hyperframes-registry** | `/hyperframes-registry` | Installing blocks and components via `hyperframes add` | +| **website-to-hyperframes** | `/website-to-hyperframes` | Capturing a URL and turning it into a video — full website-to-video pipeline | +| **tailwind** | `/tailwind` | Tailwind v4 browser-runtime styles for projects created with `hyperframes init --tailwind` | +| **gsap** | `/gsap` | GSAP animations for HyperFrames — tweens, timelines, easing, performance | +| **animejs** | `/animejs` | Anime.js animations registered on `window.__hfAnime` | +| **css-animations** | `/css-animations` | CSS keyframes that HyperFrames can pause and seek | +| **lottie** | `/lottie` | `lottie-web` and dotLottie players registered on `window.__hfLottie` | +| **three** | `/three` | Three.js scenes rendered from HyperFrames `hf-seek` events | +| **waapi** | `/waapi` | Web Animations API motion driven through `document.getAnimations()` | + +> **Skills not available?** Ask the user to run `npx hyperframes skills` and restart their +> agent session, or install manually: `npx skills add heygen-com/hyperframes`. + +## Commands + +```bash +npm run dev # start the preview server (long-running — keep it alive in background) +npm run check # lint + validate + inspect +npm run render # render to MP4 +npm run publish # publish and get a shareable link +npx hyperframes lint --verbose # include info-level findings +npx hyperframes lint --json # machine-readable output for CI +npx hyperframes docs # reference docs in terminal +``` + +> **`npm run dev` is a long-running server, not a one-shot command.** It blocks until stopped. +> In Claude Code, always run it with `run_in_background: true`. Never run it as a foreground +> command — it will time out and the server will die, breaking the browser preview. + +## Documentation + +**For quick reference**, use the local CLI docs command (no network required): + +```bash +npx hyperframes docs +``` + +Topics: `data-attributes`, `gsap`, `compositions`, `rendering`, `examples`, `troubleshooting` + +**For full documentation**, discover pages via the machine-readable index — do NOT guess URLs: + +``` +https://hyperframes.heygen.com/llms.txt +``` + +## Project Structure + +- `index.html` — main composition (root timeline) +- `compositions/` — sub-compositions referenced via `data-composition-src` +- `meta.json` — project metadata (id, name) +- `transcript.json` — whisper word-level transcript (if generated) + +## Linting — ALWAYS RUN AFTER CHANGES + +After creating or editing any `.html` composition, **always** run the full check before considering the task complete: + +```bash +npm run check +``` + +Fix all errors before presenting the result. Inspect warnings should be reviewed before rendering. + +## Key Rules + +1. Every timed element needs `data-start`, `data-duration`, and `data-track-index` +2. Elements with timing **MUST** have `class="clip"` — the framework uses this for visibility control +3. Timelines must be paused and registered on `window.__timelines`: + ```js + window.__timelines = window.__timelines || {}; + window.__timelines["composition-id"] = gsap.timeline({ paused: true }); + ``` +4. Videos use `muted` with a separate `` element for the audio track +5. Sub-compositions use `data-composition-src="compositions/file.html"` to reference other HTML files +6. Only deterministic logic — no `Date.now()`, no `Math.random()`, no network fetches diff --git a/playground/agentic-react-webpack-playground/hyperframes/multiselect-plugin-demo/DESIGN.md b/playground/agentic-react-webpack-playground/hyperframes/multiselect-plugin-demo/DESIGN.md new file mode 100644 index 0000000..10f9bdc --- /dev/null +++ b/playground/agentic-react-webpack-playground/hyperframes/multiselect-plugin-demo/DESIGN.md @@ -0,0 +1,31 @@ +## Style Prompt + +Use the real webpack issue-tracking playground as the visual source of truth. The animation should feel like a precise plugin demo over a production-grade dark issue board: minimal overlays, crisp cursor motion, and camera moves that focus attention on the actual Agentic React toolkit, real selected elements, and real tuning modal states captured from the playground. + +## Colors + +- Canvas: `#070707`, inherited from the ISSUEFLOW playground. +- Foreground: `#f4f2ed` for readable overlay labels. +- Muted foreground: `#8d8d8d` for secondary labels. +- Toolkit accent: `#111827`, inherited from the Agentic React selection toolkit. +- Tuning accent: `#1d4ed8`, inherited from the webpack playground tuning modal configuration. + +## Typography + +- Product capture: as rendered by the playground, primarily `Inter, system-ui, sans-serif`. +- Overlay labels: `Inter, system-ui, sans-serif`. + +## Motion + +- Start with the real issue board at full frame. +- Zoom into the actual toolkit before opening and choosing Multiselect. +- Pan back to the real table selections as each captured element is added. +- Hold on real tuning modal captures for each selected element. +- Return to the real toolkit, click Done, then zoom back out. + +## What NOT to Do + +- Do not redraw the issue board by hand. +- Do not replace the actual toolkit with a mock toolkit. +- Do not use marketing-style title cards. +- Do not cover the selected table elements or tuning modal with explanatory text. diff --git a/playground/agentic-react-webpack-playground/hyperframes/multiselect-plugin-demo/assets/01-workspace.png b/playground/agentic-react-webpack-playground/hyperframes/multiselect-plugin-demo/assets/01-workspace.png new file mode 100644 index 0000000..1cdbc20 Binary files /dev/null and b/playground/agentic-react-webpack-playground/hyperframes/multiselect-plugin-demo/assets/01-workspace.png differ diff --git a/playground/agentic-react-webpack-playground/hyperframes/multiselect-plugin-demo/assets/02-toolkit-open.png b/playground/agentic-react-webpack-playground/hyperframes/multiselect-plugin-demo/assets/02-toolkit-open.png new file mode 100644 index 0000000..ab7c44e Binary files /dev/null and b/playground/agentic-react-webpack-playground/hyperframes/multiselect-plugin-demo/assets/02-toolkit-open.png differ diff --git a/playground/agentic-react-webpack-playground/hyperframes/multiselect-plugin-demo/assets/03-multiselect-active.png b/playground/agentic-react-webpack-playground/hyperframes/multiselect-plugin-demo/assets/03-multiselect-active.png new file mode 100644 index 0000000..8808c3f Binary files /dev/null and b/playground/agentic-react-webpack-playground/hyperframes/multiselect-plugin-demo/assets/03-multiselect-active.png differ diff --git a/playground/agentic-react-webpack-playground/hyperframes/multiselect-plugin-demo/assets/04-first-selection.png b/playground/agentic-react-webpack-playground/hyperframes/multiselect-plugin-demo/assets/04-first-selection.png new file mode 100644 index 0000000..1db8693 Binary files /dev/null and b/playground/agentic-react-webpack-playground/hyperframes/multiselect-plugin-demo/assets/04-first-selection.png differ diff --git a/playground/agentic-react-webpack-playground/hyperframes/multiselect-plugin-demo/assets/05-second-selection.png b/playground/agentic-react-webpack-playground/hyperframes/multiselect-plugin-demo/assets/05-second-selection.png new file mode 100644 index 0000000..2be0ea7 Binary files /dev/null and b/playground/agentic-react-webpack-playground/hyperframes/multiselect-plugin-demo/assets/05-second-selection.png differ diff --git a/playground/agentic-react-webpack-playground/hyperframes/multiselect-plugin-demo/assets/06-third-selection.png b/playground/agentic-react-webpack-playground/hyperframes/multiselect-plugin-demo/assets/06-third-selection.png new file mode 100644 index 0000000..f4c47f3 Binary files /dev/null and b/playground/agentic-react-webpack-playground/hyperframes/multiselect-plugin-demo/assets/06-third-selection.png differ diff --git a/playground/agentic-react-webpack-playground/hyperframes/multiselect-plugin-demo/assets/07-tune-1.png b/playground/agentic-react-webpack-playground/hyperframes/multiselect-plugin-demo/assets/07-tune-1.png new file mode 100644 index 0000000..e98adce Binary files /dev/null and b/playground/agentic-react-webpack-playground/hyperframes/multiselect-plugin-demo/assets/07-tune-1.png differ diff --git a/playground/agentic-react-webpack-playground/hyperframes/multiselect-plugin-demo/assets/08-tune-2.png b/playground/agentic-react-webpack-playground/hyperframes/multiselect-plugin-demo/assets/08-tune-2.png new file mode 100644 index 0000000..4af192d Binary files /dev/null and b/playground/agentic-react-webpack-playground/hyperframes/multiselect-plugin-demo/assets/08-tune-2.png differ diff --git a/playground/agentic-react-webpack-playground/hyperframes/multiselect-plugin-demo/assets/09-tune-3.png b/playground/agentic-react-webpack-playground/hyperframes/multiselect-plugin-demo/assets/09-tune-3.png new file mode 100644 index 0000000..ee8dfd6 Binary files /dev/null and b/playground/agentic-react-webpack-playground/hyperframes/multiselect-plugin-demo/assets/09-tune-3.png differ diff --git a/playground/agentic-react-webpack-playground/hyperframes/multiselect-plugin-demo/assets/10-done.png b/playground/agentic-react-webpack-playground/hyperframes/multiselect-plugin-demo/assets/10-done.png new file mode 100644 index 0000000..f7c7f1f Binary files /dev/null and b/playground/agentic-react-webpack-playground/hyperframes/multiselect-plugin-demo/assets/10-done.png differ diff --git a/playground/agentic-react-webpack-playground/hyperframes/multiselect-plugin-demo/assets/11-return-to-toolkit.png b/playground/agentic-react-webpack-playground/hyperframes/multiselect-plugin-demo/assets/11-return-to-toolkit.png new file mode 100644 index 0000000..f4c47f3 Binary files /dev/null and b/playground/agentic-react-webpack-playground/hyperframes/multiselect-plugin-demo/assets/11-return-to-toolkit.png differ diff --git a/playground/agentic-react-webpack-playground/hyperframes/multiselect-plugin-demo/hyperframes.json b/playground/agentic-react-webpack-playground/hyperframes/multiselect-plugin-demo/hyperframes.json new file mode 100644 index 0000000..5fb1d6d --- /dev/null +++ b/playground/agentic-react-webpack-playground/hyperframes/multiselect-plugin-demo/hyperframes.json @@ -0,0 +1,9 @@ +{ + "$schema": "https://hyperframes.heygen.com/schema/hyperframes.json", + "registry": "https://raw.githubusercontent.com/heygen-com/hyperframes/main/registry", + "paths": { + "blocks": "compositions", + "components": "compositions/components", + "assets": "assets" + } +} diff --git a/playground/agentic-react-webpack-playground/hyperframes/multiselect-plugin-demo/index.html b/playground/agentic-react-webpack-playground/hyperframes/multiselect-plugin-demo/index.html new file mode 100644 index 0000000..0c7f08c --- /dev/null +++ b/playground/agentic-react-webpack-playground/hyperframes/multiselect-plugin-demo/index.html @@ -0,0 +1,269 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/playground/agentic-react-webpack-playground/hyperframes/multiselect-plugin-demo/meta.json b/playground/agentic-react-webpack-playground/hyperframes/multiselect-plugin-demo/meta.json new file mode 100644 index 0000000..0fc1512 --- /dev/null +++ b/playground/agentic-react-webpack-playground/hyperframes/multiselect-plugin-demo/meta.json @@ -0,0 +1,5 @@ +{ + "id": "multiselect-plugin-demo", + "name": "multiselect-plugin-demo", + "createdAt": "2026-06-05T03:33:39.829Z" +} \ No newline at end of file diff --git a/playground/agentic-react-webpack-playground/hyperframes/multiselect-plugin-demo/package.json b/playground/agentic-react-webpack-playground/hyperframes/multiselect-plugin-demo/package.json new file mode 100644 index 0000000..64dbee1 --- /dev/null +++ b/playground/agentic-react-webpack-playground/hyperframes/multiselect-plugin-demo/package.json @@ -0,0 +1,11 @@ +{ + "name": "multiselect-plugin-demo", + "private": true, + "type": "module", + "scripts": { + "dev": "npx --yes hyperframes@0.6.73 preview", + "check": "npx --yes hyperframes@0.6.73 lint && npx --yes hyperframes@0.6.73 validate && npx --yes hyperframes@0.6.73 inspect", + "render": "npx --yes hyperframes@0.6.73 render", + "publish": "npx --yes hyperframes@0.6.73 publish" + } +} diff --git a/playground/agentic-react-webpack-playground/hyperframes/multiselect-plugin-demo/renders/multiselect-plugin-demo-draft.mp4 b/playground/agentic-react-webpack-playground/hyperframes/multiselect-plugin-demo/renders/multiselect-plugin-demo-draft.mp4 new file mode 100644 index 0000000..13644d9 Binary files /dev/null and b/playground/agentic-react-webpack-playground/hyperframes/multiselect-plugin-demo/renders/multiselect-plugin-demo-draft.mp4 differ diff --git a/playground/agentic-react-webpack-playground/package.json b/playground/agentic-react-webpack-playground/package.json new file mode 100644 index 0000000..cc8ab96 --- /dev/null +++ b/playground/agentic-react-webpack-playground/package.json @@ -0,0 +1,30 @@ +{ + "name": "agentic-react-webpack-playground", + "private": true, + "type": "module", + "scripts": { + "start": "webpack serve --config webpack.config.mjs --mode development", + "build": "webpack --config webpack.config.mjs --mode production", + "test:e2e": "pnpm -C ../.. run build && env -u NO_COLOR playwright test" + }, + "dependencies": { + "@mantine/core": "^8.3.9", + "@mantine/hooks": "^8.3.9", + "@tabler/icons-react": "^3.43.0", + "react": "19.1.1", + "react-dom": "19.1.1", + "@agentic-react/webpack": "workspace:*" + }, + "devDependencies": { + "@babel/core": "7.27.4", + "@babel/preset-react": "7.27.1", + "@playwright/test": "^1.59.1", + "babel-loader": "10.0.0", + "css-loader": "^7.1.4", + "html-webpack-plugin": "5.6.3", + "style-loader": "^4.0.0", + "webpack": "5.107.1", + "webpack-cli": "6.0.1", + "webpack-dev-server": "5.2.4" + } +} diff --git a/playground/agentic-react-webpack-playground/playwright.config.mjs b/playground/agentic-react-webpack-playground/playwright.config.mjs new file mode 100644 index 0000000..de442e0 --- /dev/null +++ b/playground/agentic-react-webpack-playground/playwright.config.mjs @@ -0,0 +1,129 @@ +import path from 'node:path'; +import fs from 'node:fs'; +import net from 'node:net'; +import os from 'node:os'; +import { fileURLToPath } from 'node:url'; +import { defineConfig } from '@playwright/test'; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = path.dirname(__filename); +const host = '127.0.0.1'; +const portStateKey = Buffer.from(__dirname).toString('base64url'); +const portStatePath = path.join( + os.tmpdir(), + `agentic-react-webpack-playground-${portStateKey}.json`, +); +const portLockPath = path.join( + os.tmpdir(), + `agentic-react-webpack-playground-${portStateKey}.lock`, +); + +const canListen = (port) => + new Promise((resolve) => { + const server = net.createServer(); + server.once('error', () => resolve(false)); + server.listen(port, host, () => { + server.close(() => resolve(true)); + }); + }); + +const findAvailablePort = async (preferredPort) => { + for (let port = preferredPort; port < preferredPort + 1000; port += 1) { + if (await canListen(port)) { + return port; + } + } + + throw new Error( + `Could not find an available Webpack playground port starting at ${preferredPort}.`, + ); +}; + +const readPortState = () => { + try { + const state = JSON.parse(fs.readFileSync(portStatePath, 'utf8')); + if (Date.now() - state.createdAt < 10 * 60 * 1000) { + return Number(state.port); + } + } catch (_error) { + return null; + } + + return null; +}; + +const writePortState = (port) => { + fs.mkdirSync(path.dirname(portStatePath), { recursive: true }); + fs.writeFileSync( + portStatePath, + JSON.stringify({ createdAt: Date.now(), port }, null, 2), + ); +}; + +const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms)); + +const waitForPortState = async () => { + for (let attempt = 0; attempt < 100; attempt += 1) { + const port = readPortState(); + if (port) { + return port; + } + await sleep(100); + } + + throw new Error('Timed out waiting for Webpack playground port allocation.'); +}; + +const allocatePlaywrightPort = async () => { + const existingPort = readPortState(); + if (existingPort) { + return existingPort; + } + + fs.mkdirSync(path.dirname(portStatePath), { recursive: true }); + let lockHandle; + try { + lockHandle = fs.openSync(portLockPath, 'wx'); + } catch (_error) { + return await waitForPortState(); + } + + try { + const port = await findAvailablePort( + Number(process.env.AGENTIC_REACT_WEBPACK_PLAYGROUND_PORT || 51425), + ); + writePortState(port); + return port; + } finally { + fs.closeSync(lockHandle); + fs.rmSync(portLockPath, { force: true }); + } +}; + +const playgroundPort = await allocatePlaywrightPort(); +process.env.AGENTIC_REACT_WEBPACK_PLAYGROUND_PORT = String(playgroundPort); +const playgroundUrl = `http://${host}:${playgroundPort}`; + +export default defineConfig({ + testDir: './e2e', + timeout: 30000, + expect: { + timeout: 5000, + }, + use: { + baseURL: playgroundUrl, + permissions: ['clipboard-read', 'clipboard-write'], + trace: 'on-first-retry', + }, + webServer: { + command: 'pnpm start', + cwd: __dirname, + env: { + ...process.env, + AGENTIC_REACT_WEBPACK_PLAYGROUND_PORT: String(playgroundPort), + }, + url: playgroundUrl, + timeout: 180000, + reuseExistingServer: false, + }, +}); diff --git a/playground/agentic-react-webpack-playground/public/generated/webpack-runtime-console.png b/playground/agentic-react-webpack-playground/public/generated/webpack-runtime-console.png new file mode 100644 index 0000000..10c296c Binary files /dev/null and b/playground/agentic-react-webpack-playground/public/generated/webpack-runtime-console.png differ diff --git a/playground/agentic-react-webpack-playground/public/index.html b/playground/agentic-react-webpack-playground/public/index.html new file mode 100644 index 0000000..182b164 --- /dev/null +++ b/playground/agentic-react-webpack-playground/public/index.html @@ -0,0 +1,11 @@ + + + + + + Webpack Playground + + + + + diff --git a/playground/agentic-react-webpack-playground/src/App.css b/playground/agentic-react-webpack-playground/src/App.css new file mode 100644 index 0000000..4e4f595 --- /dev/null +++ b/playground/agentic-react-webpack-playground/src/App.css @@ -0,0 +1,1051 @@ +:root { + color-scheme: dark; + background: #070707; + font-synthesis: none; + text-rendering: geometricPrecision; +} + +* { + box-sizing: border-box; +} + +html { + scroll-behavior: smooth; +} + +body { + margin: 0; + min-width: 320px; + background: #070707; +} + +button, +a { + font: inherit; +} + +.app-page { + min-height: 100vh; + overflow: hidden; + background: + radial-gradient(circle at 72% 12%, rgba(255, 255, 255, 0.08), transparent 28%), + linear-gradient(#070707, #080808 54%, #050505); + color: #f4f2ed; +} + +.top-nav { + position: sticky; + top: 0; + z-index: 30; + display: grid; + grid-template-columns: 1fr auto 1fr; + align-items: center; + height: 66px; + padding: 0 clamp(18px, 18vw, 260px); + border-bottom: 1px solid rgba(255, 255, 255, 0.14); + background: rgba(7, 7, 7, 0.86); + backdrop-filter: blur(18px); +} + +.brand, +.top-links, +.nav-actions, +.workspace-actions, +.toolbar-actions, +.runtime-actions { + display: flex; + align-items: center; +} + +.brand { + width: max-content; + gap: 9px; + color: #f4f2ed; + font-size: 12px; + font-weight: 800; + letter-spacing: 0; + text-decoration: none; +} + +.brand-mark { + position: relative; + width: 13px; + height: 13px; +} + +.brand-mark span { + position: absolute; + left: 0; + width: 11px; + height: 2px; + border-radius: 999px; + background: #f4f2ed; +} + +.brand-mark span:nth-child(1) { + top: 1px; +} + +.brand-mark span:nth-child(2) { + top: 5px; + left: 3px; +} + +.brand-mark span:nth-child(3) { + top: 9px; + left: 1px; +} + +.top-links { + gap: 30px; +} + +.top-links a, +.text-button { + border: 0; + background: transparent; + color: #858585; + font-size: 12px; + text-decoration: none; + cursor: pointer; +} + +.top-links a:hover, +.text-button:hover { + color: #f4f2ed; +} + +.nav-actions { + justify-content: flex-end; + gap: 14px; +} + +.icon-button, +.outline-button, +.primary-cta, +.toolbar-actions button, +.workspace-topbar button, +.workspace-actions button, +.runtime-actions button { + display: inline-flex; + align-items: center; + justify-content: center; + gap: 8px; + border: 1px solid rgba(255, 255, 255, 0.22); + background: transparent; + color: #f4f2ed; + cursor: pointer; +} + +.icon-button { + width: 28px; + height: 28px; + border: 0; + color: #8d8d8d; +} + +.outline-button { + height: 34px; + padding: 0 16px; + font-size: 12px; +} + +.hero { + position: relative; + min-height: calc(100vh - 66px); + padding: 74px clamp(18px, 18vw, 260px) 0; + border-bottom: 1px solid rgba(255, 255, 255, 0.14); +} + +.hero::after, +.analytics-band::before { + position: absolute; + inset: auto 0 0; + height: 110px; + border-top: 1px solid rgba(255, 255, 255, 0.1); + background-image: repeating-linear-gradient( + 135deg, + rgba(255, 255, 255, 0.065) 0, + rgba(255, 255, 255, 0.065) 1px, + transparent 1px, + transparent 7px + ); + content: ''; + opacity: 0.38; +} + +.hero-copy { + position: relative; + z-index: 2; + max-width: 590px; +} + +.hero h1, +.analytics-header h2, +.runtime-card h2 { + margin: 0; + color: #f4f2ed; + font-size: clamp(40px, 4.35vw, 58px); + font-weight: 760; + line-height: 0.98; + letter-spacing: 0; +} + +.hero p, +.analytics-header p, +.runtime-card p { + max-width: 520px; + margin: 26px 0 0; + color: #8d8d8d; + font-size: 15px; + line-height: 1.75; +} + +.primary-cta { + width: max-content; + height: 50px; + margin-top: 32px; + padding: 0 25px; + border-color: #f4f2ed; + background: #f4f2ed; + color: #080808; + font-size: 13px; + font-weight: 720; + text-decoration: none; +} + +.wireframe-orbit { + position: absolute; + top: 70px; + right: clamp(42px, 22vw, 400px); + width: 320px; + height: 260px; + transform: rotate(-22deg) skew(-8deg); + opacity: 0.48; +} + +.wireframe-orbit span { + position: absolute; + border: 1px solid rgba(255, 255, 255, 0.12); + background-image: repeating-linear-gradient( + 45deg, + rgba(255, 255, 255, 0.12) 0, + rgba(255, 255, 255, 0.12) 1px, + transparent 1px, + transparent 9px + ); + box-shadow: inset 0 0 28px rgba(255, 255, 255, 0.05); +} + +.wireframe-orbit span:nth-child(1) { + inset: 4px 72px 58px 78px; +} + +.wireframe-orbit span:nth-child(2) { + inset: 62px 24px 12px 128px; +} + +.wireframe-orbit span:nth-child(3) { + inset: 40px 118px 78px 18px; +} + +.workspace-shell { + position: relative; + z-index: 3; + margin-top: 46px; + border: 1px solid rgba(255, 255, 255, 0.16); + background: + linear-gradient(rgba(255, 255, 255, 0.028), rgba(255, 255, 255, 0)), + #101113; + box-shadow: 0 40px 130px rgba(0, 0, 0, 0.55); +} + +.workspace-topbar { + display: flex; + align-items: center; + justify-content: space-between; + height: 52px; + padding: 0 16px; + border-bottom: 1px solid rgba(255, 255, 255, 0.12); +} + +.workspace-topbar button, +.workspace-actions button, +.toolbar-actions button, +.runtime-actions button { + height: 30px; + padding: 0 11px; + border-color: rgba(255, 255, 255, 0.13); + background: rgba(255, 255, 255, 0.03); + color: #a5a5a5; + font-size: 12px; +} + +.workspace-topbar > button, +.workspace-actions button:last-child { + width: 30px; + padding: 0; +} + +.command-bar { + display: flex; + align-items: center; + gap: 8px; + width: min(360px, 42vw); + height: 30px; + padding: 0 12px; + border: 1px solid rgba(255, 255, 255, 0.11); + background: #0b0c0d; + color: #777; + font-size: 12px; +} + +.workspace-actions { + gap: 8px; +} + +.workspace-grid { + display: grid; + grid-template-columns: 190px minmax(300px, 1fr) 250px; + min-height: 392px; +} + +.workspace-sidebar, +.inspector, +.issue-list { + min-width: 0; +} + +.workspace-sidebar, +.inspector { + background: rgba(255, 255, 255, 0.018); +} + +.workspace-sidebar { + padding: 18px 12px; + border-right: 1px solid rgba(255, 255, 255, 0.12); +} + +.sidebar-heading { + display: flex; + align-items: center; + gap: 10px; +} + +.workspace-avatar, +.avatar { + display: grid; + place-items: center; + flex: 0 0 auto; + border-radius: 50%; + background: #f4f2ed; + color: #070707; + font-weight: 800; +} + +.workspace-avatar { + width: 26px; + height: 26px; + font-size: 12px; +} + +.sidebar-heading p, +.list-toolbar p { + margin: 0; + color: #dedbd4; + font-size: 13px; + font-weight: 750; +} + +.sidebar-heading span, +.list-toolbar span { + color: #747474; + font-size: 12px; +} + +.sidebar-search { + display: grid; + grid-template-columns: auto 1fr auto; + align-items: center; + gap: 8px; + height: 34px; + margin: 18px 0; + padding: 0 10px; + border: 1px solid rgba(255, 255, 255, 0.11); + background: rgba(255, 255, 255, 0.03); + color: #777; + font-size: 12px; +} + +.sidebar-search kbd { + color: #595959; + font-size: 10px; +} + +.sidebar-list, +.project-list, +.issue-rows, +.activity-list { + display: grid; + gap: 4px; +} + +.sidebar-item, +.project-item { + display: flex; + align-items: center; + justify-content: space-between; + width: 100%; + min-height: 31px; + border: 0; + background: transparent; + color: #7c7c7c; + font-size: 12px; + text-align: left; + cursor: pointer; +} + +.sidebar-item { + padding: 0 9px; +} + +.sidebar-item.is-active { + background: rgba(255, 255, 255, 0.07); + color: #f4f2ed; +} + +.sidebar-item strong { + color: #777; + font-size: 11px; +} + +.sidebar-divider { + height: 1px; + margin: 16px 0; + background: rgba(255, 255, 255, 0.1); +} + +.project-item { + justify-content: flex-start; + gap: 9px; + padding: 0 6px; +} + +.issue-list { + background: + linear-gradient(rgba(255, 255, 255, 0.028), transparent 38%), + #0e0f10; +} + +.list-toolbar { + display: flex; + align-items: center; + justify-content: space-between; + min-height: 70px; + padding: 16px 18px; + border-bottom: 1px solid rgba(255, 255, 255, 0.1); +} + +.toolbar-actions { + gap: 8px; +} + +.toolbar-actions button:last-child { + border-color: rgba(255, 255, 255, 0.22); + color: #ededed; +} + +.list-tabs { + display: flex; + gap: 18px; + height: 42px; + padding: 0 18px; + border-bottom: 1px solid rgba(255, 255, 255, 0.1); +} + +.list-tabs button { + border: 0; + border-bottom: 1px solid transparent; + background: transparent; + color: #767676; + font-size: 12px; + cursor: pointer; +} + +.list-tabs .is-selected { + border-bottom-color: #f4f2ed; + color: #f4f2ed; +} + +.table-head, +.issue-row { + display: grid; + grid-template-columns: + minmax(0, 1.7fr) + minmax(0, 0.7fr) + minmax(0, 0.5fr) + minmax(0, 0.52fr) + minmax(0, 0.5fr); + align-items: center; + column-gap: 10px; + min-width: 0; +} + +.table-head { + min-height: 34px; + padding: 0 18px; + border-bottom: 1px solid rgba(255, 255, 255, 0.08); + color: #616161; + font-size: 11px; +} + +.issue-row { + width: 100%; + min-height: 47px; + padding: 0 18px; + border: 0; + border-bottom: 1px solid rgba(255, 255, 255, 0.07); + background: transparent; + color: #858585; + font-size: 12px; + text-align: left; + cursor: pointer; +} + +.table-head > span, +.issue-row > span { + min-width: 0; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.issue-row:hover, +.issue-row.is-selected { + background: + repeating-linear-gradient( + 135deg, + rgba(255, 255, 255, 0.035) 0, + rgba(255, 255, 255, 0.035) 1px, + transparent 1px, + transparent 7px + ), + rgba(255, 255, 255, 0.04); +} + +.issue-title-cell { + display: flex; + align-items: center; + min-width: 0; + gap: 10px; +} + +.issue-title-cell span { + min-width: 0; +} + +.issue-title-cell strong, +.issue-title-cell em { + display: block; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.issue-title-cell strong { + color: #bdbdbd; + font-style: normal; + font-size: 12px; +} + +.issue-title-cell em { + margin-top: 2px; + color: #777; + font-style: normal; +} + +.status-dot { + display: inline-block; + width: 8px; + height: 8px; + border-radius: 50%; + box-shadow: 0 0 16px currentColor; +} + +.status-dot-pink { + background: #ff3d8b; + color: #ff3d8b; +} + +.status-dot-violet { + background: #a78bfa; + color: #a78bfa; +} + +.status-dot-amber { + background: #f5a524; + color: #f5a524; +} + +.status-dot-green { + background: #21d07a; + color: #21d07a; +} + +.status-dot-slate { + background: #727784; + color: #727784; +} + +.inspector { + padding: 16px; + border-left: 1px solid rgba(255, 255, 255, 0.12); +} + +.inspector-actions { + display: flex; + justify-content: flex-end; + gap: 8px; +} + +.inspector-actions button { + display: grid; + place-items: center; + width: 30px; + height: 30px; + border: 1px solid rgba(255, 255, 255, 0.1); + background: transparent; + color: #777; +} + +.inspector-status { + display: flex; + align-items: center; + gap: 8px; + margin-top: 24px; + color: #8d8d8d; + font-size: 12px; +} + +.inspector h2 { + margin: 14px 0 0; + color: #f4f2ed; + font-size: 21px; + line-height: 1.2; + letter-spacing: 0; +} + +.inspector p { + margin: 14px 0 0; + color: #858585; + font-size: 12px; + line-height: 1.65; +} + +.detail-grid { + display: grid; + grid-template-columns: 76px 1fr; + row-gap: 14px; + margin-top: 24px; + padding: 18px 0; + border-top: 1px solid rgba(255, 255, 255, 0.1); + border-bottom: 1px solid rgba(255, 255, 255, 0.1); + font-size: 12px; +} + +.detail-grid span { + color: #696969; +} + +.detail-grid strong { + color: #d7d4ce; +} + +.inspector-card, +.runtime-card, +.quote-card, +.feature-card { + border: 1px solid rgba(255, 255, 255, 0.12); + background: rgba(255, 255, 255, 0.025); +} + +.inspector-card { + display: grid; + gap: 9px; + margin-top: 18px; + padding: 14px; +} + +.card-title { + display: flex; + align-items: center; + gap: 8px; + color: #d7d4ce; + font-size: 12px; + font-weight: 720; +} + +.inspector-card code { + overflow: hidden; + color: #777; + font-family: "SFMono-Regular", Consolas, monospace; + font-size: 11px; + text-overflow: ellipsis; + white-space: nowrap; +} + +.activity-list { + margin-top: 18px; +} + +.activity-item { + display: grid; + grid-template-columns: auto 1fr auto; + align-items: center; + gap: 8px; + min-height: 32px; + color: #777; + font-size: 11px; +} + +.activity-item em { + color: #555; + font-style: normal; +} + +.analytics-band { + position: relative; + padding: 116px clamp(18px, 18vw, 260px) 150px; + border-bottom: 1px solid rgba(255, 255, 255, 0.14); +} + +.analytics-band::before { + inset: 0 0 auto; + height: 112px; +} + +.section-label { + color: #696969; + font-size: 11px; + font-weight: 800; + letter-spacing: 0.08em; + text-transform: uppercase; +} + +.analytics-header { + display: grid; + grid-template-columns: minmax(320px, 560px) minmax(260px, 520px); + align-items: end; + gap: 80px; + margin-top: 18px; +} + +.analytics-header h2, +.runtime-card h2 { + font-size: clamp(34px, 4.4vw, 56px); +} + +.analytics-header p, +.runtime-card p { + margin: 0; +} + +.feature-grid { + display: grid; + grid-template-columns: repeat(3, minmax(0, 1fr)); + gap: 1px; + margin-top: 64px; + border: 1px solid rgba(255, 255, 255, 0.12); + background: rgba(255, 255, 255, 0.12); +} + +.feature-card { + min-height: 230px; + padding: 34px; + border: 0; + background: #090909; +} + +.feature-card svg { + color: #f4f2ed; +} + +.feature-card h3 { + margin: 34px 0 0; + color: #f4f2ed; + font-size: 20px; + letter-spacing: 0; +} + +.feature-card p { + margin: 14px 0 0; + color: #858585; + font-size: 14px; + line-height: 1.65; +} + +.selection-panel { + display: grid; + grid-template-columns: minmax(280px, 520px) minmax(320px, 1fr); + gap: 0; + padding: 0 clamp(18px, 18vw, 260px); + border-bottom: 1px solid rgba(255, 255, 255, 0.14); + background-image: repeating-linear-gradient( + 135deg, + rgba(255, 255, 255, 0.045) 0, + rgba(255, 255, 255, 0.045) 1px, + transparent 1px, + transparent 8px + ); +} + +.quote-card, +.runtime-card { + min-height: 300px; + border-top: 0; + border-bottom: 0; + background: #090909; +} + +.quote-card { + padding: 46px; +} + +.quote-card p { + margin: 0; + color: #d8d6cf; + font-size: clamp(18px, 2.4vw, 28px); + line-height: 1.42; +} + +.quote-card > div { + display: flex; + align-items: center; + gap: 12px; + margin-top: 34px; +} + +.avatar { + width: 34px; + height: 34px; + font-size: 12px; +} + +.quote-card strong, +.quote-card em { + display: block; +} + +.quote-card strong { + color: #f4f2ed; + font-size: 13px; +} + +.quote-card em { + margin-top: 3px; + color: #777; + font-size: 12px; + font-style: normal; +} + +.runtime-card { + padding: 46px; + border-left: 0; +} + +.runtime-card h2 { + margin-top: 42px; +} + +.runtime-actions { + gap: 10px; + margin-top: 30px; +} + +.runtime-actions button:first-child { + border-color: #f4f2ed; + background: #f4f2ed; + color: #080808; +} + +.footer { + display: flex; + align-items: center; + justify-content: space-between; + min-height: 70px; + padding: 0 clamp(18px, 18vw, 260px); + color: #777; + font-size: 12px; +} + +@media (max-width: 1180px) { + .top-nav { + grid-template-columns: 1fr auto; + } + + .top-links { + display: none; + } + + .workspace-grid { + grid-template-columns: 190px minmax(360px, 1fr); + } + + .inspector { + grid-column: 1 / -1; + border-top: 1px solid rgba(255, 255, 255, 0.12); + border-left: 0; + } + + .analytics-header, + .selection-panel { + grid-template-columns: 1fr; + } + + .runtime-card { + border-top: 0; + border-left: 1px solid rgba(255, 255, 255, 0.12); + } +} + +@media (max-width: 760px) { + .top-nav { + height: 58px; + padding: 0 16px; + } + + .nav-actions .text-button, + .nav-actions .icon-button { + display: none; + } + + .hero { + min-height: auto; + padding: 52px 16px 0; + } + + .wireframe-orbit { + top: 86px; + right: -94px; + width: 230px; + height: 210px; + opacity: 0.28; + } + + .workspace-shell { + margin-top: 44px; + overflow: hidden; + } + + .workspace-topbar, + .workspace-grid { + min-width: 0; + } + + .workspace-topbar { + display: grid; + grid-template-columns: auto minmax(0, 1fr) auto; + gap: 8px; + height: auto; + padding: 10px; + } + + .command-bar { + width: auto; + min-width: 0; + } + + .workspace-grid { + grid-template-columns: minmax(0, 1fr); + min-height: 0; + } + + .workspace-sidebar { + border-right: 0; + border-bottom: 1px solid rgba(255, 255, 255, 0.12); + } + + .sidebar-list { + grid-template-columns: repeat(2, minmax(0, 1fr)); + } + + .project-list { + grid-template-columns: repeat(3, minmax(0, 1fr)); + } + + .sidebar-item, + .project-item { + min-width: 0; + } + + .project-item span, + .sidebar-item span { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + } + + .list-toolbar { + align-items: flex-start; + gap: 12px; + min-height: 0; + } + + .toolbar-actions { + flex: 0 0 auto; + } + + .table-head { + display: none; + } + + .issue-row { + display: flex; + flex-wrap: wrap; + align-items: flex-start; + gap: 8px 10px; + min-width: 0; + min-height: 0; + padding: 14px 18px; + } + + .issue-title-cell { + flex: 1 1 100%; + max-width: 100%; + } + + .issue-row > span:not(.issue-title-cell) { + min-width: 0; + padding: 3px 7px; + border: 1px solid rgba(255, 255, 255, 0.09); + color: #8d8d8d; + font-size: 11px; + } + + .inspector { + grid-column: auto; + border-top: 0; + border-left: 0; + } + + .analytics-band { + padding: 90px 16px 112px; + } + + .feature-grid { + grid-template-columns: 1fr; + } + + .selection-panel { + padding: 0 16px; + } + + .quote-card, + .runtime-card { + padding: 30px 22px; + } + + .footer { + padding: 0 16px; + } +} diff --git a/playground/agentic-react-webpack-playground/src/App.jsx b/playground/agentic-react-webpack-playground/src/App.jsx new file mode 100644 index 0000000..37728c7 --- /dev/null +++ b/playground/agentic-react-webpack-playground/src/App.jsx @@ -0,0 +1,517 @@ +import '@mantine/core/styles.css'; +import './App.css'; + +import { useMemo, useState } from 'react'; +import { MantineProvider, createTheme } from '@mantine/core'; +import { + IconArchive, + IconArrowRight, + IconBell, + IconBolt, + IconBrandReact, + IconCalendarDue, + IconChartBar, + IconCheck, + IconCircle, + IconCircleDashed, + IconCode, + IconCommand, + IconDots, + IconFilter, + IconFlag, + IconGitBranch, + IconLayoutSidebarLeftCollapse, + IconMessage, + IconMoon, + IconPlus, + IconSearch, + IconSettings, + IconSparkles, + IconUsers, +} from '@tabler/icons-react'; + +const theme = createTheme({ + fontFamily: + 'Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif', + primaryColor: 'gray', +}); + +const navItems = [ + ['Inbox', '12'], + ['Issues', '84'], + ['Views', '7'], + ['Roadmap', '3'], + ['Archive', ''], +]; + +const issueRows = [ + { + id: 'ISS-142', + title: 'Unify workspace filters across list and board views', + team: 'Platform', + status: 'In Progress', + priority: 'P1', + assignee: 'Maya', + due: 'Today', + tone: 'pink', + }, + { + id: 'ISS-139', + title: 'Move project health rollups into the issue detail rail', + team: 'Product', + status: 'Review', + priority: 'P2', + assignee: 'Noah', + due: 'May 30', + tone: 'violet', + }, + { + id: 'ISS-138', + title: 'Add keyboard shortcuts to cycle through triage queues', + team: 'Client', + status: 'Todo', + priority: 'P3', + assignee: 'Iris', + due: 'Jun 2', + tone: 'amber', + }, + { + id: 'ISS-135', + title: 'Expose source context when a selected row opens the inspector', + team: 'Runtime', + status: 'In Progress', + priority: 'P1', + assignee: 'Sam', + due: 'Jun 3', + tone: 'green', + }, + { + id: 'ISS-131', + title: 'Preserve issue ordering after bulk reassignment', + team: 'Data', + status: 'Backlog', + priority: 'P4', + assignee: 'June', + due: 'Jun 6', + tone: 'slate', + }, + { + id: 'ISS-128', + title: 'Create saved view for launch-blocking project work', + team: 'Growth', + status: 'Review', + priority: 'P2', + assignee: 'Leo', + due: 'Jun 9', + tone: 'amber', + }, + { + id: 'ISS-125', + title: 'Render html fixture metadata in the webpack playground', + team: 'MCP', + status: 'Done', + priority: 'P3', + assignee: 'Ari', + due: 'Done', + tone: 'green', + }, +]; + +const featureCards = [ + { + title: 'Issue command center', + body: 'Plan, assign, comment, resolve, and review work from one dense project surface.', + icon: IconCommand, + }, + { + title: 'Team workflows', + body: 'Every status change, owner handoff, and milestone shift stays visible in context.', + icon: IconUsers, + }, + { + title: 'Live analytics', + body: 'Cycle time, priority drift, throughput, and workload stay close to the issue list.', + icon: IconChartBar, + }, +]; + +const activity = [ + ['Maya moved ISS-142 to In Progress', '2m'], + ['Noah linked ISS-139 to the mobile launch milestone', '14m'], + ['Sam attached source context from webpack App.jsx', '31m'], + ['Iris added a follow-up comment to ISS-138', '48m'], +]; + +function BrandMark() { + return ( + + + + + + ); +} + +function Dot({ tone = 'slate' }) { + return ; +} + +function TopNav() { + return ( + + + + ISSUEFLOW + + + + Workspace + Issues + Analytics + Source + + + + + + + + Log in + + + Sign up + + + + ); +} + +function Sidebar() { + return ( + + ); +} + +function IssueList({ selectedIssueId, onSelectIssue }) { + return ( + + + + Issues + 84 open across 6 teams + + + + + Filter + + + + New issue + + + + + + {['Active', 'Backlog', 'My issues', 'Blocked'].map((label, index) => ( + + {label} + + ))} + + + + Issue + Status + Priority + Owner + Due + + + + {issueRows.map((issue) => ( + onSelectIssue(issue.id)} + type="button" + > + + + + {issue.id} + {issue.title} + + + {issue.status} + {issue.priority} + {issue.assignee} + {issue.due} + + ))} + + + ); +} + +function Inspector({ issue }) { + return ( + + ); +} + +function WorkspacePreview() { + const [selectedIssueId, setSelectedIssueId] = useState(issueRows[0].id); + const selectedIssue = useMemo( + () => issueRows.find((issue) => issue.id === selectedIssueId) || issueRows[0], + [selectedIssueId], + ); + + return ( + + + + + + + + Command center + + + + + Archive + + + + + + + + + + + + + + ); +} + +function AnalyticsPanel() { + return ( + + Built for issue velocity + + Less process. More progress. + + The workspace behaves like a dense production app: list filtering, + issue selection, ownership metadata, source context, and activity all + stay visible without leaving the page. + + + + + {featureCards.map(({ title, body, icon: Icon }) => ( + + + {title} + {body} + + ))} + + + ); +} + +function SelectionPanel() { + return ( + + + + "We replaced scattered planning docs with one issue workspace. The + team can see the next decision, the owner, and the source context + without asking where anything lives." + + + JK + + Jamie Kim + Engineering Lead, Acme Corp + + + + + + + + React selection demo + + Point the toolkit at any issue row. + + Use the Agentic React floating selector to capture this issue + management UI. The webpack runtime exposes component state, html + selectors, and source snippets from the same app surface. + + + + + Enable selection + + + + Inspect html panel + + + + + ); +} + +function AppContent() { + return ( + + + + + + + + + + + Issue tracking for teams that ship fast + + Plan work, manage ownership, and resolve project issues without + slowing down the team. + + + Start + + + + + + + + + + + + ); +} + +export default function App() { + return ( + + + + ); +} diff --git a/playground/agentic-react-webpack-playground/src/index.jsx b/playground/agentic-react-webpack-playground/src/index.jsx new file mode 100644 index 0000000..993828c --- /dev/null +++ b/playground/agentic-react-webpack-playground/src/index.jsx @@ -0,0 +1,6 @@ +import { createRoot } from 'react-dom/client'; +import App from './App'; + +const rootElement = document.getElementById('root'); +const root = createRoot(rootElement); +root.render(); diff --git a/playground/agentic-react-webpack-playground/webpack.config.mjs b/playground/agentic-react-webpack-playground/webpack.config.mjs new file mode 100644 index 0000000..452895a --- /dev/null +++ b/playground/agentic-react-webpack-playground/webpack.config.mjs @@ -0,0 +1,101 @@ +import path from 'node:path'; +import { fileURLToPath } from 'node:url'; +import HtmlWebpackPlugin from 'html-webpack-plugin'; +import withAgenticReactWebpack from '@agentic-react/webpack'; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = path.dirname(__filename); +const devServerPort = Number( + process.env.AGENTIC_REACT_WEBPACK_PLAYGROUND_PORT || + process.env.PORT || + 51425, +); +const toolkit = { + tuningModal: { + classNames: { + surface: 'webpack-playground-tuning-surface', + panel: 'webpack-playground-tuning-panel', + control: 'webpack-playground-tuning-control', + }, + tokens: { + panelRadius: '12px', + controlRadius: '9px', + primaryButtonBackground: '#1d4ed8', + primaryButtonColor: '#ffffff', + panelShadow: '0 24px 72px rgba(29, 78, 216, 0.22)', + }, + styles: { + surface: { + filter: 'drop-shadow(0 18px 40px rgba(29, 78, 216, 0.16))', + }, + panel: { + border: '1px solid rgba(29, 78, 216, 0.24)', + }, + targetTag: { + background: '#eff6ff', + color: '#1d4ed8', + }, + sectionTitle: { + color: '#1d4ed8', + }, + }, + }, +}; + +const baseConfig = { + mode: 'development', + entry: './src/index.jsx', + output: { + filename: 'bundle.js', + path: path.resolve(__dirname, 'dist'), + clean: true, + }, + module: { + rules: [ + { + test: /\.jsx?$/, + exclude: /node_modules/, + use: { + loader: 'babel-loader', + options: { + presets: [ + [ + '@babel/preset-react', + { runtime: 'automatic', development: true }, + ], + ], + }, + }, + }, + { + test: /\.css$/i, + use: ['style-loader', 'css-loader'], + }, + ], + }, + resolve: { + extensions: ['.js', '.jsx'], + fullySpecified: false, + }, + plugins: [ + new HtmlWebpackPlugin({ + template: './public/index.html', + }), + ], + devServer: { + host: '127.0.0.1', + port: devServerPort, + hot: true, + }, +}; + +export default (env = {}, argv = {}) => + withAgenticReactWebpack( + baseConfig, + { + mode: argv.mode || env.mode || 'development', + }, + { + toolkit, + }, + ); diff --git a/playground/demo/demo1-single-select.mp4 b/playground/demo/demo1-single-select.mp4 new file mode 100644 index 0000000..eb43d6f Binary files /dev/null and b/playground/demo/demo1-single-select.mp4 differ diff --git a/playground/demo/demo2-multiselect.mp4 b/playground/demo/demo2-multiselect.mp4 new file mode 100644 index 0000000..13644d9 Binary files /dev/null and b/playground/demo/demo2-multiselect.mp4 differ diff --git a/playground/shared/mcp-bridge-server.mjs b/playground/shared/mcp-bridge-server.mjs new file mode 100644 index 0000000..1f4bd23 --- /dev/null +++ b/playground/shared/mcp-bridge-server.mjs @@ -0,0 +1,42 @@ +import http from 'node:http'; +import { + RuntimeBridgeServer, + createStreamableHttpMcpHandler, + initMcpServer, +} from '@agentic-react/core'; + +const port = Number( + process.env.AGENTIC_REACT_SERVER_PORT || 51426, +); +const host = process.env.AGENTIC_REACT_SERVER_HOST || '127.0.0.1'; +const rootDir = process.env.AGENTIC_REACT_ROOT_DIR || process.cwd(); + +const runtimeBridge = new RuntimeBridgeServer(); +const handleMcpRequest = createStreamableHttpMcpHandler(() => + initMcpServer(runtimeBridge, rootDir, []), +); + +const httpServer = http.createServer(async (req, res) => { + const requestUrl = req.url || '/'; + const requestPath = requestUrl.split('?')[0]; + + if (requestPath === '/health') { + res.statusCode = 200; + res.setHeader('content-type', 'application/json'); + res.end(JSON.stringify({ ok: true })); + return; + } + + if (requestPath === '/mcp') { + await handleMcpRequest(req, res); + return; + } + + res.statusCode = 404; + res.end('Not Found'); +}); + +runtimeBridge.attach(httpServer); +httpServer.listen(port, host, () => { + console.log(`[agentic-react] standalone MCP bridge listening on http://${host}:${port}`); +}); diff --git a/playground/user-profile-app/src/App.jsx b/playground/user-profile-app/src/App.jsx deleted file mode 100644 index 4aa8015..0000000 --- a/playground/user-profile-app/src/App.jsx +++ /dev/null @@ -1,41 +0,0 @@ -import React from 'react'; -import { BrowserRouter as Router, Routes, Route, Navigate } from 'react-router-dom'; -import './App.css'; -import { UserProfile, UserList } from './components'; -import { ThemeProvider, createTheme } from '@mui/material/styles'; -import CssBaseline from '@mui/material/CssBaseline'; -import { UserProvider } from './context/UserContext'; - -const theme = createTheme({ - palette: { - primary: { - main: '#1976d2', - }, - secondary: { - main: '#dc004e', - }, - }, -}); - -const App = function () { - return ( - - - - - - - } /> - } /> - } /> - } /> - - - - - - ); -} - -export default App; -// For babel test \ No newline at end of file diff --git a/playground/user-profile-app/src/components/UserList/UserList.jsx b/playground/user-profile-app/src/components/UserList/UserList.jsx deleted file mode 100644 index 6a0c6a1..0000000 --- a/playground/user-profile-app/src/components/UserList/UserList.jsx +++ /dev/null @@ -1,73 +0,0 @@ -import React, { useState } from 'react'; -import { - Box, - Typography, - Paper, - List, - Divider, - Container -} from '@mui/material'; -import { useNavigate } from 'react-router-dom'; -import UserSearch from './UserSearch'; -import UserRow from './UserRow'; -import EmptyResult from './EmptyResult'; -import { mockUsers } from './mockData'; - -const UserList = () => { - const [searchTerm, setSearchTerm] = useState(''); - const navigate = useNavigate(); - - const handleSearchChange = (event) => { - setSearchTerm(event.target.value); - }; - - const handleUserClick = (userId) => { - navigate(`/profile/${userId}`); - }; - - const filteredUsers = mockUsers.filter(user => - user.firstName.toLowerCase().includes(searchTerm.toLowerCase()) || - user.lastName.toLowerCase().includes(searchTerm.toLowerCase()) || - user.occupation.toLowerCase().includes(searchTerm.toLowerCase()) - ); - - return ( - - - - User Directory - - - - - - - - - {filteredUsers.length > 0 ? ( - filteredUsers.map((user, index) => ( - - handleUserClick(user.id)} - /> - {index < filteredUsers.length - 1 && - - } - - )) - ) : ( - - )} - - - - - ); -}; - - -export default UserList; \ No newline at end of file diff --git a/playground/user-profile-app/src/components/UserList/UserRow.jsx b/playground/user-profile-app/src/components/UserList/UserRow.jsx deleted file mode 100644 index 32320d2..0000000 --- a/playground/user-profile-app/src/components/UserList/UserRow.jsx +++ /dev/null @@ -1,48 +0,0 @@ -import React from 'react'; -import { - ListItem, - ListItemAvatar, - ListItemText, - Avatar, - Typography -} from '@mui/material'; - -const UserRow = ({ user, onClick }) => { - return ( - - - - {user.firstName[0]}{user.lastName[0]} - - - - - {user.occupation} - - {user.location} - - } - /> - - ); -}; - -export default UserRow; \ No newline at end of file diff --git a/playground/user-profile-app/src/components/UserList/UserSearch.jsx b/playground/user-profile-app/src/components/UserList/UserSearch.jsx deleted file mode 100644 index a6a0123..0000000 --- a/playground/user-profile-app/src/components/UserList/UserSearch.jsx +++ /dev/null @@ -1,27 +0,0 @@ -import React from 'react'; -import { Box, TextField, InputAdornment } from '@mui/material'; -import SearchIcon from '@mui/icons-material/Search'; - -const UserSearch = ({ searchTerm, handleSearchChange }) => { - return ( - - - - - ), - }} - /> - - ); -}; - - -export default UserSearch; \ No newline at end of file diff --git a/playground/user-profile-app/src/components/UserList/mockData.js b/playground/user-profile-app/src/components/UserList/mockData.js deleted file mode 100644 index 033b4d1..0000000 --- a/playground/user-profile-app/src/components/UserList/mockData.js +++ /dev/null @@ -1,53 +0,0 @@ -// Mock user data -export const mockUsers = [ - { - id: 1, - firstName: 'John', - lastName: 'Doe', - email: 'john.doe@example.com', - phone: '(555) 123-4567', - bio: 'Software developer with a passion for React and UI/UX design.', - location: 'San Francisco, CA', - occupation: 'Senior Frontend Developer', - }, - { - id: 2, - firstName: 'Jane', - lastName: 'Smith', - email: 'jane.smith@example.com', - phone: '(555) 987-6543', - bio: 'Full stack developer specialized in Node.js and React.', - location: 'New York, NY', - occupation: 'Full Stack Developer', - }, - { - id: 3, - firstName: 'Michael', - lastName: 'Johnson', - email: 'michael.j@example.com', - phone: '(555) 555-5555', - bio: 'UX designer with 5 years of experience in mobile and web applications.', - location: 'Austin, TX', - occupation: 'UX Designer', - }, - { - id: 4, - firstName: 'Sarah', - lastName: 'Williams', - email: 'sarah.w@example.com', - phone: '(555) 222-3333', - bio: 'Project manager with a background in software development.', - location: 'Seattle, WA', - occupation: 'Project Manager', - }, - { - id: 5, - firstName: 'David', - lastName: 'Brown', - email: 'david.brown@example.com', - phone: '(555) 444-9999', - bio: 'DevOps engineer specializing in cloud infrastructure and CI/CD pipelines.', - location: 'Chicago, IL', - occupation: 'DevOps Engineer', - }, -]; diff --git a/playground/user-profile-app/src/components/UserProfile/ProfileField.jsx b/playground/user-profile-app/src/components/UserProfile/ProfileField.jsx deleted file mode 100644 index e6e97d7..0000000 --- a/playground/user-profile-app/src/components/UserProfile/ProfileField.jsx +++ /dev/null @@ -1,60 +0,0 @@ -import React, { memo } from 'react'; -import { useProfile } from '../../context/ProfileContext'; - -const ProfileField = memo(({ - label, - name, - editMode, - multiline = false, - rows = 1 -}) => { - const { tempData, profileData, handleInputChange } = useProfile(); - const value = editMode ? tempData[name] : profileData[name]; - if (editMode) { - const commonProps = { - 'data-ext-id': `profile-field-${name}`, - id: `profile-field-${name}`, - name: name, - value: value, - onChange: handleInputChange, - style: { - width: '100%', - padding: '16.5px 14px', - borderRadius: '4px', - border: '1px solid rgba(0, 0, 0, 0.23)', - boxSizing: 'border-box', - fontFamily: 'inherit', - fontSize: '1rem', - marginTop: '8px', - marginBottom: '4px' - } - }; - - return ( - - {label} - {multiline ? ( - - ) : ( - - )} - - ); - } - - return ( - - {label} - {value} - - ); -}); -ProfileField.displayName = 'ProfileField'; - -export default ProfileField; \ No newline at end of file diff --git a/playground/user-profile-app/src/components/UserProfile/index.jsx b/playground/user-profile-app/src/components/UserProfile/index.jsx deleted file mode 100644 index b24c432..0000000 --- a/playground/user-profile-app/src/components/UserProfile/index.jsx +++ /dev/null @@ -1,85 +0,0 @@ -import React, { useState } from 'react'; -import { Box, Typography, Paper, Button } from '@mui/material'; -import { useParams, useNavigate } from 'react-router-dom'; -import ArrowBackIcon from '@mui/icons-material/ArrowBack'; -import ProfileHeader from './ProfileHeader'; -import ProfileContent from './ProfileContent'; -import ProfileNotification from './ProfileNotification'; -import { useUserProfileData } from '../../hooks/useUserProfileData'; -import { ProfileProvider } from '../../context/ProfileContext'; -const UserProfile = () => { - const { userId } = useParams(); - const navigate = useNavigate(); - const { - profileData, - editMode, - isLoading, - handleEditToggle, - handleSave: handleSaveFromHook, - handleCancel, - } = useUserProfileData(userId); - - const [openSnackbar, setOpenSnackbar] = useState(false); - - const handleSave = () => { - const success = handleSaveFromHook(); - if (success) { - setOpenSnackbar(true); - } - }; - - const handleCloseSnackbar = () => { - setOpenSnackbar(false); - }; - - const handleBackToList = () => { - navigate('/users'); - }; - - if (isLoading) { - return Loading...; - } - - if (!profileData) { - return null; - } - - return ( - - } - onClick={handleBackToList} - sx={{ mb: 2 }} - > - Back to User List - - - - User Profile - - - - - - - - - - - - ); -}; - -// Specifically use a uniquely-suffixed displayName to distinguish from MUI components -UserProfile.displayName = 'UserProfile'; - -export default UserProfile; \ No newline at end of file diff --git a/playground/user-profile-app/tools/log1.ts b/playground/user-profile-app/tools/log1.ts deleted file mode 100644 index 4f80000..0000000 --- a/playground/user-profile-app/tools/log1.ts +++ /dev/null @@ -1,10 +0,0 @@ -import type { ToolResultValue } from 'vite-react-mcp'; - -export default function myCustomTool(args: { message: string }): ToolResultValue { - const { message } = args; - console.log(`[custom-tool/log1] ${message}`); - return { - success: true, - message: `Log1 received: ${message}`, - }; -} \ No newline at end of file diff --git a/playground/user-profile-app/vite.config.js b/playground/user-profile-app/vite.config.js deleted file mode 100644 index f6bca62..0000000 --- a/playground/user-profile-app/vite.config.js +++ /dev/null @@ -1,29 +0,0 @@ -import { defineConfig } from 'vite'; -import react from '@vitejs/plugin-react'; -import path from 'path'; -import ReactMCP from 'vite-react-mcp'; -import { z } from 'zod'; -import log1 from './tools/log1.js'; - -export default defineConfig({ - plugins: [react(), ReactMCP({ - customTools: [ - { - name: 'log1', - description: 'Log1', - schema: z.object({ - message: z.string(), - }), - clientFunction: log1, - } - ] - })], - resolve: { - alias: { - '@': path.resolve(__dirname, './src'), - }, - }, - server: { - port: 3000, - }, -}); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml new file mode 100644 index 0000000..8ab4a82 --- /dev/null +++ b/pnpm-lock.yaml @@ -0,0 +1,14739 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +catalogs: + default: + '@emotion/react': + specifier: ^11.11.0 + version: 11.14.0 + '@emotion/styled': + specifier: ^11.11.0 + version: 11.14.1 + '@mui/material': + specifier: ^5.14.0 + version: 5.18.0 + '@testing-library/jest-dom': + specifier: ^5.16.5 + version: 5.17.0 + '@testing-library/react': + specifier: ^13.4.0 + version: 13.4.0 + '@testing-library/user-event': + specifier: ^13.5.0 + version: 13.5.0 + '@types/react': + specifier: ^18.3.0 + version: 18.3.23 + '@types/react-dom': + specifier: ^18.3.0 + version: 18.3.7 + react: + specifier: ^18.3.0 + version: 18.3.1 + react-dom: + specifier: ^18.3.0 + version: 18.3.1 + react-router-dom: + specifier: ^6.30.3 + version: 6.30.3 + +overrides: + '@remix-run/router': 1.23.2 + caniuse-lite: 1.0.30001793 + fast-uri: 3.1.2 + js-yaml: 4.1.1 + read-yaml-file@1.1.0>js-yaml: 3.14.1 + lodash: 4.18.1 + minimatch: 3.1.5 + next: 15.5.18 + path-to-regexp@>=8.0.0 <8.4.0: 8.4.2 + picomatch@2.3.1: 2.3.2 + picomatch@4.0.2: 4.0.4 + postcss: 8.5.15 + qs: 6.15.2 + rollup: 4.60.4 + serialize-javascript: 7.0.5 + uuid: 11.1.1 + vite: 6.4.2 + webpack: 5.107.1 + webpack-dev-server: 5.2.4 + ws: 8.21.0 + yaml: 1.10.3 + +importers: + + .: + devDependencies: + '@biomejs/biome': + specifier: 1.9.4 + version: 1.9.4 + '@changesets/cli': + specifier: ^2.31.0 + version: 2.31.0(@types/node@24.0.13) + shx: + specifier: 0.4.0 + version: 0.4.0 + typescript: + specifier: ^5.8.2 + version: 5.8.3 + + packages/core: + dependencies: + '@modelcontextprotocol/sdk': + specifier: 1.29.0 + version: 1.29.0(zod@3.25.76) + bippy: + specifier: ^0.5.39 + version: 0.5.39(react@19.1.1) + ws: + specifier: 8.21.0 + version: 8.21.0 + zod: + specifier: ^3.22.4 + version: 3.25.76 + zod-to-json-schema: + specifier: ^3.23.5 + version: 3.25.2(zod@3.25.76) + devDependencies: + '@types/react-reconciler': + specifier: 0.26.7 + version: 0.26.7 + + packages/next: + dependencies: + '@agentic-react/core': + specifier: workspace:* + version: link:../core + '@modelcontextprotocol/sdk': + specifier: 1.29.0 + version: 1.29.0(zod@3.25.76) + next: + specifier: 15.5.18 + version: 15.5.18(@playwright/test@1.59.1)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(sass@1.99.0) + webpack: + specifier: 5.107.1 + version: 5.107.1(webpack-cli@6.0.1) + + packages/vite: + dependencies: + '@agentic-react/core': + specifier: workspace:* + version: link:../core + '@modelcontextprotocol/sdk': + specifier: 1.29.0 + version: 1.29.0(zod@3.25.76) + devDependencies: + vite: + specifier: 6.4.2 + version: 6.4.2(@types/node@24.0.13)(less@4.1.3)(sass@1.99.0)(stylus@0.55.0)(terser@5.46.2)(yaml@1.10.3) + + packages/webpack: + dependencies: + '@agentic-react/core': + specifier: workspace:* + version: link:../core + '@modelcontextprotocol/sdk': + specifier: 1.29.0 + version: 1.29.0(zod@3.25.76) + webpack: + specifier: 5.107.1 + version: 5.107.1(webpack-cli@6.0.1) + + playground/agentic-react-next-playground: + dependencies: + '@agentic-react/next': + specifier: workspace:* + version: link:../../packages/next + '@phosphor-icons/react': + specifier: ^2.1.10 + version: 2.1.10(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + antd: + specifier: ^6.3.7 + version: 6.3.7(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + next: + specifier: 15.5.18 + version: 15.5.18(@playwright/test@1.59.1)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(sass@1.99.0) + react: + specifier: 19.1.1 + version: 19.1.1 + react-dom: + specifier: 19.1.1 + version: 19.1.1(react@19.1.1) + devDependencies: + '@playwright/test': + specifier: ^1.59.1 + version: 1.59.1 + + playground/agentic-react-nx-module-federation-playground: + dependencies: + '@agentic-react/webpack': + specifier: workspace:* + version: link:../../packages/webpack + '@chakra-ui/react': + specifier: 2.10.9 + version: 2.10.9(@emotion/react@11.14.0(@types/react@18.3.23)(react@18.2.0))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.23)(react@18.2.0))(@types/react@18.3.23)(react@18.2.0))(@types/react@18.3.23)(framer-motion@12.38.0(@emotion/is-prop-valid@1.3.1)(react-dom@18.2.0(react@18.2.0))(react@18.2.0))(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@emotion/react': + specifier: 'catalog:' + version: 11.14.0(@types/react@18.3.23)(react@18.2.0) + '@emotion/styled': + specifier: 'catalog:' + version: 11.14.1(@emotion/react@11.14.0(@types/react@18.3.23)(react@18.2.0))(@types/react@18.3.23)(react@18.2.0) + '@heroicons/react': + specifier: ^2.2.0 + version: 2.2.0(react@18.2.0) + framer-motion: + specifier: ^12.38.0 + version: 12.38.0(@emotion/is-prop-valid@1.3.1)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + react: + specifier: 18.2.0 + version: 18.2.0 + react-17-compat: + specifier: npm:react@17.0.2 + version: react@17.0.2 + react-dom: + specifier: 18.2.0 + version: 18.2.0(react@18.2.0) + react-router-dom: + specifier: 6.30.3 + version: 6.30.3(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + devDependencies: + '@babel/core': + specifier: 7.20.2 + version: 7.20.2 + '@babel/preset-env': + specifier: 7.20.2 + version: 7.20.2(@babel/core@7.20.2) + '@babel/preset-react': + specifier: 7.18.6 + version: 7.18.6(@babel/core@7.20.2) + '@babel/preset-typescript': + specifier: 7.18.6 + version: 7.18.6(@babel/core@7.20.2) + '@modelcontextprotocol/sdk': + specifier: 1.29.0 + version: 1.29.0(zod@3.25.76) + '@nrwl/devkit': + specifier: 15.9.7 + version: 15.9.7(nx@15.9.7) + '@nrwl/eslint-plugin-nx': + specifier: 15.9.7 + version: 15.9.7(@typescript-eslint/parser@5.36.1(eslint@8.23.1)(typescript@4.8.4))(eslint@8.23.1)(nx@15.9.7)(typescript@4.8.4) + '@nrwl/js': + specifier: 15.9.7 + version: 15.9.7(@babel/traverse@7.29.0)(nx@15.9.7)(typescript@4.8.4) + '@nrwl/react': + specifier: 15.9.7 + version: 15.9.7(@babel/traverse@7.29.0)(eslint@8.23.1)(nx@15.9.7)(typescript@4.8.4)(webpack@5.107.1) + '@nrwl/webpack': + specifier: 15.9.7 + version: 15.9.7(@babel/traverse@7.29.0)(@types/node@24.0.13)(html-webpack-plugin@5.6.3(webpack@5.107.1))(nx@15.9.7)(typescript@4.8.4)(webpack-cli@6.0.1) + '@nrwl/workspace': + specifier: 15.9.7 + version: 15.9.7 + '@playwright/test': + specifier: ^1.59.1 + version: 1.59.1 + '@types/react': + specifier: 18.3.23 + version: 18.3.23 + '@types/react-dom': + specifier: 18.3.7 + version: 18.3.7(@types/react@18.3.23) + '@typescript-eslint/eslint-plugin': + specifier: 5.36.1 + version: 5.36.1(@typescript-eslint/parser@5.36.1(eslint@8.23.1)(typescript@4.8.4))(eslint@8.23.1)(typescript@4.8.4) + '@typescript-eslint/parser': + specifier: 5.36.1 + version: 5.36.1(eslint@8.23.1)(typescript@4.8.4) + babel-loader: + specifier: 10.0.0 + version: 10.0.0(@babel/core@7.20.2)(webpack@5.107.1) + eslint: + specifier: 8.23.1 + version: 8.23.1 + html-webpack-plugin: + specifier: 5.6.3 + version: 5.6.3(webpack@5.107.1) + nx: + specifier: 15.9.7 + version: 15.9.7 + ts-loader: + specifier: 9.4.1 + version: 9.4.1(typescript@4.8.4)(webpack@5.107.1) + typescript: + specifier: 4.8.4 + version: 4.8.4 + webpack: + specifier: 5.107.1 + version: 5.107.1(webpack-cli@6.0.1) + webpack-cli: + specifier: 6.0.1 + version: 6.0.1(webpack-dev-server@5.2.4)(webpack@5.107.1) + webpack-dev-server: + specifier: 5.2.4 + version: 5.2.4(tslib@2.8.1)(webpack-cli@6.0.1)(webpack@5.107.1) + webpack-merge: + specifier: 5.8.0 + version: 5.8.0 + + playground/agentic-react-vite-playground: + dependencies: + '@emotion/react': + specifier: 'catalog:' + version: 11.14.0(@types/react@18.3.23)(react@18.3.1) + '@emotion/styled': + specifier: ^11.11.0 + version: 11.14.1(@emotion/react@11.14.0(@types/react@18.3.23)(react@18.3.1))(@types/react@18.3.23)(react@18.3.1) + '@mui/icons-material': + specifier: ^5.14.0 + version: 5.18.0(@mui/material@5.18.0(@emotion/react@11.14.0(@types/react@18.3.23)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.23)(react@18.3.1))(@types/react@18.3.23)(react@18.3.1))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@types/react@18.3.23)(react@18.3.1) + '@mui/material': + specifier: 'catalog:' + version: 5.18.0(@emotion/react@11.14.0(@types/react@18.3.23)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.23)(react@18.3.1))(@types/react@18.3.23)(react@18.3.1))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@testing-library/jest-dom': + specifier: 'catalog:' + version: 5.17.0 + '@testing-library/react': + specifier: 'catalog:' + version: 13.4.0(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@testing-library/user-event': + specifier: 'catalog:' + version: 13.5.0(@testing-library/dom@8.20.1) + react: + specifier: 'catalog:' + version: 18.3.1 + react-dom: + specifier: 'catalog:' + version: 18.3.1(react@18.3.1) + react-router-dom: + specifier: 'catalog:' + version: 6.30.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + zod: + specifier: ^3.24.2 + version: 3.25.76 + devDependencies: + '@agentic-react/vite': + specifier: workspace:* + version: link:../../packages/vite + '@babel/preset-react': + specifier: ^7.26.3 + version: 7.27.1(@babel/core@7.27.4) + '@playwright/test': + specifier: ^1.59.1 + version: 1.59.1 + '@types/react': + specifier: 'catalog:' + version: 18.3.23 + '@types/react-dom': + specifier: 'catalog:' + version: 18.3.7(@types/react@18.3.23) + '@vitejs/plugin-react': + specifier: ^4.3.4 + version: 4.6.0(vite@6.4.2(@types/node@24.0.13)(less@4.1.3)(sass@1.99.0)(stylus@0.55.0)(terser@5.46.2)(yaml@1.10.3)) + vite: + specifier: 6.4.2 + version: 6.4.2(@types/node@24.0.13)(less@4.1.3)(sass@1.99.0)(stylus@0.55.0)(terser@5.46.2)(yaml@1.10.3) + + playground/agentic-react-webpack-playground: + dependencies: + '@agentic-react/webpack': + specifier: workspace:* + version: link:../../packages/webpack + '@mantine/core': + specifier: ^8.3.9 + version: 8.3.9(@mantine/hooks@8.3.9(react@19.1.1))(@types/react@19.1.8)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@mantine/hooks': + specifier: ^8.3.9 + version: 8.3.9(react@19.1.1) + '@tabler/icons-react': + specifier: ^3.43.0 + version: 3.43.0(react@19.1.1) + react: + specifier: 19.1.1 + version: 19.1.1 + react-dom: + specifier: 19.1.1 + version: 19.1.1(react@19.1.1) + devDependencies: + '@babel/core': + specifier: 7.27.4 + version: 7.27.4 + '@babel/preset-react': + specifier: 7.27.1 + version: 7.27.1(@babel/core@7.27.4) + '@playwright/test': + specifier: ^1.59.1 + version: 1.59.1 + babel-loader: + specifier: 10.0.0 + version: 10.0.0(@babel/core@7.27.4)(webpack@5.107.1) + css-loader: + specifier: ^7.1.4 + version: 7.1.4(webpack@5.107.1) + html-webpack-plugin: + specifier: 5.6.3 + version: 5.6.3(webpack@5.107.1) + style-loader: + specifier: ^4.0.0 + version: 4.0.0(webpack@5.107.1) + webpack: + specifier: 5.107.1 + version: 5.107.1(webpack-cli@6.0.1) + webpack-cli: + specifier: 6.0.1 + version: 6.0.1(webpack-dev-server@5.2.4)(webpack@5.107.1) + webpack-dev-server: + specifier: 5.2.4 + version: 5.2.4(tslib@2.8.1)(webpack-cli@6.0.1)(webpack@5.107.1) + +packages: + + '@adobe/css-tools@4.4.3': + resolution: {integrity: sha512-VQKMkwriZbaOgVCby1UDY/LDk5fIjhQicCvVPFqfe+69fWaPWydbWJ3wRt59/YzIwda1I81loas3oCoHxnqvdA==} + + '@ampproject/remapping@2.3.0': + resolution: {integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==} + engines: {node: '>=6.0.0'} + + '@ant-design/colors@8.0.1': + resolution: {integrity: sha512-foPVl0+SWIslGUtD/xBr1p9U4AKzPhNYEseXYRRo5QSzGACYZrQbe11AYJbYfAWnWSpGBx6JjBmSeugUsD9vqQ==} + + '@ant-design/cssinjs-utils@2.1.2': + resolution: {integrity: sha512-5fTHQ158jJJ5dC/ECeyIdZUzKxE/mpEMRZxthyG1sw/AKRHKgJBg00Yi6ACVXgycdje7KahRNvNET/uBccwCnA==} + peerDependencies: + react: '>=18' + react-dom: '>=18' + + '@ant-design/cssinjs@2.1.2': + resolution: {integrity: sha512-2Hy8BnCEH31xPeSLbhhB2ctCPXE2ZnASdi+KbSeS79BNbUhL9hAEe20SkUk+BR8aKTmqb6+FKFruk7w8z0VoRQ==} + peerDependencies: + react: '>=16.0.0' + react-dom: '>=16.0.0' + + '@ant-design/fast-color@3.0.1': + resolution: {integrity: sha512-esKJegpW4nckh0o6kV3Tkb7NPIZYbPnnFxmQDUmL08ukXZAvV85TZBr70eGuke/CIArLaP6aw8lt9KILjnWuOw==} + engines: {node: '>=8.x'} + + '@ant-design/icons-svg@4.4.2': + resolution: {integrity: sha512-vHbT+zJEVzllwP+CM+ul7reTEfBR0vgxFe7+lREAsAA7YGsYpboiq2sQNeQeRvh09GfQgs/GyFEvZpJ9cLXpXA==} + + '@ant-design/icons@6.2.2': + resolution: {integrity: sha512-zlJtE7AMbG12TeYVPhtBXwNpFInNy8mjLzcIm+0BPw16/b8ODG87YJ1G37VIF5VFscdgfsf6EweAFPTobu/3iQ==} + engines: {node: '>=8'} + peerDependencies: + react: '>=16.0.0' + react-dom: '>=16.0.0' + + '@ant-design/react-slick@2.0.0': + resolution: {integrity: sha512-HMS9sRoEmZey8LsE/Yo6+klhlzU12PisjrVcydW3So7RdklyEd2qehyU6a7Yp+OYN72mgsYs3NFCyP2lCPFVqg==} + peerDependencies: + react: ^0.14.0 || ^15.0.1 || ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + react-dom: ^0.14.0 || ^15.0.1 || ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.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.0': + resolution: {integrity: sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==} + engines: {node: '>=6.9.0'} + + '@babel/compat-data@7.28.0': + resolution: {integrity: sha512-60X7qkglvrap8mn1lh2ebxXdZYtUcpd7gsmy9kLaBJ4i/WdY8PqTSdxyA8qraikqKQK5C1KRBKXqznrVapyNaw==} + engines: {node: '>=6.9.0'} + + '@babel/compat-data@7.29.3': + resolution: {integrity: sha512-LIVqM46zQWZhj17qA8wb4nW/ixr2y1Nw+r1etiAWgRM6U1IqP+LNhL1yg440jYZR72jCWcWbLWzIosH+uP1fqg==} + engines: {node: '>=6.9.0'} + + '@babel/core@7.20.2': + resolution: {integrity: sha512-w7DbG8DtMrJcFOi4VrLm+8QM4az8Mo+PuLBKLp2zrYRCow8W/f9xiXm5sN53C8HksCyDQwCKha9JiDoIyPjT2g==} + engines: {node: '>=6.9.0'} + + '@babel/core@7.27.4': + resolution: {integrity: sha512-bXYxrXFubeYdvB0NhD/NBB3Qi6aZeV20GOWVI47t2dkecCEoneR4NPVcb7abpXDEvejgrUfFtG6vG/zxAKmg+g==} + engines: {node: '>=6.9.0'} + + '@babel/generator@7.28.0': + resolution: {integrity: sha512-lJjzvrbEeWrhB4P3QBsH7tey117PjLZnDbLiQEKjQ/fNJTjuq4HSqgFA+UNSwZT8D7dxxbnuSBMsa1lrWzKlQg==} + engines: {node: '>=6.9.0'} + + '@babel/generator@7.29.1': + resolution: {integrity: sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw==} + 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-compilation-targets@7.27.2': + resolution: {integrity: sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==} + engines: {node: '>=6.9.0'} + + '@babel/helper-compilation-targets@7.28.6': + resolution: {integrity: sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA==} + engines: {node: '>=6.9.0'} + + '@babel/helper-create-class-features-plugin@7.29.3': + resolution: {integrity: sha512-RpLYy2sb51oNLjuu1iD3bwBqCBWUzjO0ocp+iaCP/lJtb2CPLcnC2Fftw+4sAzaMELGeWTgExSKADbdo0GFVzA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/helper-create-regexp-features-plugin@7.28.5': + resolution: {integrity: sha512-N1EhvLtHzOvj7QQOUCCS3NrPJP8c5W6ZXCHDn7Yialuy1iu4r5EmIYkXlKNqT99Ciw+W0mDqWoR6HWMZlFP3hw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/helper-define-polyfill-provider@0.3.3': + resolution: {integrity: sha512-z5aQKU4IzbqCC1XH0nAqfsFLMVSo22SBKUc0BxGrLkolTdPTructy0ToNnlO2zA4j9Q/7pjMZf0DSY+DSTYzww==} + peerDependencies: + '@babel/core': ^7.4.0-0 + + '@babel/helper-define-polyfill-provider@0.6.8': + resolution: {integrity: sha512-47UwBLPpQi1NoWzLuHNjRoHlYXMwIJoBf7MFou6viC/sIHWYygpvr0B6IAyh5sBdA2nr2LPIRww8lfaUVQINBA==} + peerDependencies: + '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0 + + '@babel/helper-environment-visitor@7.24.7': + resolution: {integrity: sha512-DoiN84+4Gnd0ncbBOM9AZENV4a5ZiL39HYMyZJGZ/AZEykHYdJw0wW3kdcsh9/Kn+BRXHLkkklZ51ecPKmI1CQ==} + engines: {node: '>=6.9.0'} + + '@babel/helper-globals@7.28.0': + resolution: {integrity: sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-member-expression-to-functions@7.28.5': + resolution: {integrity: sha512-cwM7SBRZcPCLgl8a7cY0soT1SptSzAlMH39vwiRpOQkJlh53r5hdHwLSCZpQdVLT39sZt+CRpNwYG4Y2v77atg==} + 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.28.6': + resolution: {integrity: sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-module-transforms@7.27.3': + resolution: {integrity: sha512-dSOvYwvyLsWBeIRyOeHXp5vPj5l1I011r52FM1+r1jCERv+aFXYk4whgQccYEGYxK2H3ZAIA8nuPkQ0HaUo3qg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/helper-module-transforms@7.28.6': + resolution: {integrity: sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/helper-optimise-call-expression@7.27.1': + resolution: {integrity: sha512-URMGH08NzYFhubNSGJrpUEphGKQwMQYBySzat5cAByY1/YgIRkULnIy3tAMeszlL/so2HbeilYloUmSpd7GdVw==} + 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.28.6': + resolution: {integrity: sha512-S9gzZ/bz83GRysI7gAD4wPT/AI3uCnY+9xn+Mx/KPs2JwHJIz1W8PZkg2cqyt3RNOBM8ejcXhV6y8Og7ly/Dug==} + 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.0.0 + + '@babel/helper-replace-supers@7.28.6': + resolution: {integrity: sha512-mq8e+laIk94/yFec3DxSjCRD2Z0TAjhVbEJY3UQrlwVo15Lmt7C2wAUbK4bjnTs4APkwsYLTahXRraQXhb1WCg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/helper-skip-transparent-expression-wrappers@7.27.1': + resolution: {integrity: sha512-Tub4ZKEXqbPjXgWLl2+3JpQAYBJ8+ikpQ2Ocj/q/r0LwE3UhENh7EUabyHjz2kCEsrRY83ew2DQdHluuiDQFzg==} + engines: {node: '>=6.9.0'} + + '@babel/helper-string-parser@7.27.1': + resolution: {integrity: sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==} + 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.28.5': + resolution: {integrity: sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==} + engines: {node: '>=6.9.0'} + + '@babel/helper-validator-option@7.27.1': + resolution: {integrity: sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==} + engines: {node: '>=6.9.0'} + + '@babel/helper-wrap-function@7.28.6': + resolution: {integrity: sha512-z+PwLziMNBeSQJonizz2AGnndLsP2DeGHIxDAn+wdHOGuo4Fo1x1HBPPXeE9TAOPHNNWQKCSlA2VZyYyyibDnQ==} + engines: {node: '>=6.9.0'} + + '@babel/helpers@7.27.6': + resolution: {integrity: sha512-muE8Tt8M22638HU31A3CgfSUciwz1fhATfoVai05aPXGor//CdWDCbnlY1yvBPo07njuVOCNGCSp/GTt12lIug==} + engines: {node: '>=6.9.0'} + + '@babel/helpers@7.29.2': + resolution: {integrity: sha512-HoGuUs4sCZNezVEKdVcwqmZN8GoHirLUcLaYVNBK2J0DadGtdcqgr3BCbvH8+XUo4NGjNl3VOtSjEKNzqfFgKw==} + engines: {node: '>=6.9.0'} + + '@babel/parser@7.28.0': + resolution: {integrity: sha512-jVZGvOxOuNSsuQuLRTh13nU0AogFlw32w/MT+LV6D3sP5WdbW61E77RnkbaO2dUvmPAYrBDJXGn5gGS6tH4j8g==} + engines: {node: '>=6.0.0'} + hasBin: true + + '@babel/parser@7.29.3': + resolution: {integrity: sha512-b3ctpQwp+PROvU/cttc4OYl4MzfJUWy6FZg+PMXfzmt/+39iHVF0sDfqay8TQM3JA2EUOyKcFZt75jWriQijsA==} + engines: {node: '>=6.0.0'} + hasBin: true + + '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@7.27.1': + resolution: {integrity: sha512-g4L7OYun04N1WyqMNjldFwlfPCLVkgB54A/YCXICZYBsvJJE3kByKv9c9+R/nAfmIfjl2rKYLNyMHboYbZaWaA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@7.27.1': + resolution: {integrity: sha512-oO02gcONcD5O1iTLi/6frMJBIwWEHceWGSGqrpCmEL8nogiS6J9PBlE48CaK20/Jx1LuRml9aDftLgdjXT8+Cw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.13.0 + + '@babel/plugin-proposal-async-generator-functions@7.20.7': + resolution: {integrity: sha512-xMbiLsn/8RK7Wq7VeVytytS2L6qE69bXPB10YCmMdDZbKF4okCqY74pI/jJQ/8U0b/F6NrT2+14b8/P9/3AMGA==} + engines: {node: '>=6.9.0'} + deprecated: This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-async-generator-functions instead. + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-proposal-class-properties@7.18.6': + resolution: {integrity: sha512-cumfXOF0+nzZrrN8Rf0t7M+tF6sZc7vhQwYQck9q1/5w2OExlD+b4v4RpMJFaV1Z7WcDRgO6FqvxqxGlwo+RHQ==} + engines: {node: '>=6.9.0'} + deprecated: This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-class-properties instead. + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-proposal-class-static-block@7.21.0': + resolution: {integrity: sha512-XP5G9MWNUskFuP30IfFSEFB0Z6HzLIUcjYM4bYOPHXl7eiJ9HFv8tWj6TXTN5QODiEhDZAeI4hLok2iHFFV4hw==} + engines: {node: '>=6.9.0'} + deprecated: This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-class-static-block instead. + peerDependencies: + '@babel/core': ^7.12.0 + + '@babel/plugin-proposal-decorators@7.29.0': + resolution: {integrity: sha512-CVBVv3VY/XRMxRYq5dwr2DS7/MvqPm23cOCjbwNnVrfOqcWlnefua1uUs0sjdKOGjvPUG633o07uWzJq4oI6dA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-proposal-dynamic-import@7.18.6': + resolution: {integrity: sha512-1auuwmK+Rz13SJj36R+jqFPMJWyKEDd7lLSdOj4oJK0UTgGueSAtkrCvz9ewmgyU/P941Rv2fQwZJN8s6QruXw==} + engines: {node: '>=6.9.0'} + deprecated: This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-dynamic-import instead. + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-proposal-export-namespace-from@7.18.9': + resolution: {integrity: sha512-k1NtHyOMvlDDFeb9G5PhUXuGj8m/wiwojgQVEhJ/fsVsMCpLyOP4h0uGEjYJKrRI+EVPlb5Jk+Gt9P97lOGwtA==} + engines: {node: '>=6.9.0'} + deprecated: This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-export-namespace-from instead. + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-proposal-json-strings@7.18.6': + resolution: {integrity: sha512-lr1peyn9kOdbYc0xr0OdHTZ5FMqS6Di+H0Fz2I/JwMzGmzJETNeOFq2pBySw6X/KFL5EWDjlJuMsUGRFb8fQgQ==} + engines: {node: '>=6.9.0'} + deprecated: This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-json-strings instead. + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-proposal-logical-assignment-operators@7.20.7': + resolution: {integrity: sha512-y7C7cZgpMIjWlKE5T7eJwp+tnRYM89HmRvWM5EQuB5BoHEONjmQ8lSNmBUwOyy/GFRsohJED51YBF79hE1djug==} + engines: {node: '>=6.9.0'} + deprecated: This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-logical-assignment-operators instead. + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-proposal-nullish-coalescing-operator@7.18.6': + resolution: {integrity: sha512-wQxQzxYeJqHcfppzBDnm1yAY0jSRkUXR2z8RePZYrKwMKgMlE8+Z6LUno+bd6LvbGh8Gltvy74+9pIYkr+XkKA==} + engines: {node: '>=6.9.0'} + deprecated: This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-nullish-coalescing-operator instead. + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-proposal-numeric-separator@7.18.6': + resolution: {integrity: sha512-ozlZFogPqoLm8WBr5Z8UckIoE4YQ5KESVcNudyXOR8uqIkliTEgJ3RoketfG6pmzLdeZF0H/wjE9/cCEitBl7Q==} + engines: {node: '>=6.9.0'} + deprecated: This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-numeric-separator instead. + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-proposal-object-rest-spread@7.20.7': + resolution: {integrity: sha512-d2S98yCiLxDVmBmE8UjGcfPvNEUbA1U5q5WxaWFUGRzJSVAZqm5W6MbPct0jxnegUZ0niLeNX+IOzEs7wYg9Dg==} + engines: {node: '>=6.9.0'} + deprecated: This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-object-rest-spread instead. + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-proposal-optional-catch-binding@7.18.6': + resolution: {integrity: sha512-Q40HEhs9DJQyaZfUjjn6vE8Cv4GmMHCYuMGIWUnlxH6400VGxOuwWsPt4FxXxJkC/5eOzgn0z21M9gMT4MOhbw==} + engines: {node: '>=6.9.0'} + deprecated: This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-optional-catch-binding instead. + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-proposal-optional-chaining@7.21.0': + resolution: {integrity: sha512-p4zeefM72gpmEe2fkUr/OnOXpWEf8nAgk7ZYVqqfFiyIG7oFfVZcCrU64hWn5xp4tQ9LkV4bTIa5rD0KANpKNA==} + engines: {node: '>=6.9.0'} + deprecated: This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-optional-chaining instead. + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-proposal-private-methods@7.18.6': + resolution: {integrity: sha512-nutsvktDItsNn4rpGItSNV2sz1XwS+nfU0Rg8aCx3W3NOKVzdMjJRu0O5OkgDp3ZGICSTbgRpxZoWsxoKRvbeA==} + engines: {node: '>=6.9.0'} + deprecated: This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-private-methods instead. + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-proposal-private-property-in-object@7.21.11': + resolution: {integrity: sha512-0QZ8qP/3RLDVBwBFoWAwCtgcDZJVwA5LUJRZU8x2YFfKNuFq161wK3cuGrALu5yiPu+vzwTAg/sMWVNeWeNyaw==} + engines: {node: '>=6.9.0'} + deprecated: This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-private-property-in-object instead. + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-proposal-unicode-property-regex@7.18.6': + resolution: {integrity: sha512-2BShG/d5yoZyXZfVePH91urL5wTG6ASZU9M4o03lKK8u8UW1y08OMttBSOADTcJrnPMpvDXRG3G8fyLh4ovs8w==} + engines: {node: '>=4'} + deprecated: This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-unicode-property-regex instead. + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-async-generators@7.8.4': + resolution: {integrity: sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-class-properties@7.12.13': + resolution: {integrity: sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-class-static-block@7.14.5': + resolution: {integrity: sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-decorators@7.28.6': + resolution: {integrity: sha512-71EYI0ONURHJBL4rSFXnITXqXrrY8q4P0q006DPfN+Rk+ASM+++IBXem/ruokgBZR8YNEWZ8R6B+rCb8VcUTqA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-dynamic-import@7.8.3': + resolution: {integrity: sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ==} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-export-namespace-from@7.8.3': + resolution: {integrity: sha512-MXf5laXo6c1IbEbegDmzGPwGNTsHZmEy6QGznu5Sh2UCWvueywb2ee+CCE4zQiZstxU9BMoQO9i6zUFSY0Kj0Q==} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-import-assertions@7.28.6': + resolution: {integrity: sha512-pSJUpFHdx9z5nqTSirOCMtYVP2wFgoWhP0p3g8ONK/4IHhLIBd0B9NYqAvIUAhq+OkhO4VM1tENCt0cjlsNShw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-json-strings@7.8.3': + resolution: {integrity: sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-jsx@7.27.1': + resolution: {integrity: sha512-y8YTNIeKoyhGd9O0Jiyzyyqk8gdjnumGTQPsz0xOZOQ2RmkVJeZ1vmmfIvFEKqucBG6axJGBZDE/7iI5suUI/w==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-logical-assignment-operators@7.10.4': + resolution: {integrity: sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-nullish-coalescing-operator@7.8.3': + resolution: {integrity: sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-numeric-separator@7.10.4': + resolution: {integrity: sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-object-rest-spread@7.8.3': + resolution: {integrity: sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-optional-catch-binding@7.8.3': + resolution: {integrity: sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-optional-chaining@7.8.3': + resolution: {integrity: sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-private-property-in-object@7.14.5': + resolution: {integrity: sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-top-level-await@7.14.5': + resolution: {integrity: sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-typescript@7.28.6': + resolution: {integrity: sha512-+nDNmQye7nlnuuHDboPbGm00Vqg3oO8niRRL27/4LYHUsHYh0zJ1xWOz0uRwNFmM1Avzk8wZbc6rdiYhomzv/A==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@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.0.0-0 + + '@babel/plugin-transform-async-to-generator@7.28.6': + resolution: {integrity: sha512-ilTRcmbuXjsMmcZ3HASTe4caH5Tpo93PkTxF9oG2VZsSWsahydmcEHhix9Ik122RcTnZnUzPbmux4wh1swfv7g==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-block-scoped-functions@7.27.1': + resolution: {integrity: sha512-cnqkuOtZLapWYZUYM5rVIdv1nXYuFVIltZ6ZJ7nIj585QsjKM5dhL2Fu/lICXZ1OyIAFc7Qy+bvDAtTXqGrlhg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-block-scoping@7.28.6': + resolution: {integrity: sha512-tt/7wOtBmwHPNMPu7ax4pdPz6shjFrmHDghvNC+FG9Qvj7D6mJcoRQIF5dy4njmxR941l6rgtvfSB2zX3VlUIw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-classes@7.28.6': + resolution: {integrity: sha512-EF5KONAqC5zAqT783iMGuM2ZtmEBy+mJMOKl2BCvPZ2lVrwvXnB6o+OBWCS+CoeCCpVRF2sA2RBKUxvT8tQT5Q==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-computed-properties@7.28.6': + resolution: {integrity: sha512-bcc3k0ijhHbc2lEfpFHgx7eYw9KNXqOerKWfzbxEHUGKnS3sz9C4CNL9OiFN1297bDNfUiSO7DaLzbvHQQQ1BQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-destructuring@7.28.5': + resolution: {integrity: sha512-Kl9Bc6D0zTUcFUvkNuQh4eGXPKKNDOJQXVyyM4ZAQPMveniJdxi8XMJwLo+xSoW3MIq81bD33lcUe9kZpl0MCw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-dotall-regex@7.28.6': + resolution: {integrity: sha512-SljjowuNKB7q5Oayv4FoPzeB74g3QgLt8IVJw9ADvWy3QnUb/01aw8I4AVv8wYnPvQz2GDDZ/g3GhcNyDBI4Bg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-duplicate-keys@7.27.1': + resolution: {integrity: sha512-MTyJk98sHvSs+cvZ4nOauwTTG1JeonDjSGvGGUNHreGQns+Mpt6WX/dVzWBHgg+dYZhkC4X+zTDfkTU+Vy9y7Q==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-exponentiation-operator@7.28.6': + resolution: {integrity: sha512-WitabqiGjV/vJ0aPOLSFfNY1u9U3R7W36B03r5I2KoNix+a3sOhJ3pKFB3R5It9/UiK78NiO0KE9P21cMhlPkw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-for-of@7.27.1': + resolution: {integrity: sha512-BfbWFFEJFQzLCQ5N8VocnCtA8J1CLkNTe2Ms2wocj75dd6VpiqS5Z5quTYcUoo4Yq+DN0rtikODccuv7RU81sw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-function-name@7.27.1': + resolution: {integrity: sha512-1bQeydJF9Nr1eBCMMbC+hdwmRlsv5XYOMu03YSWFwNs0HsAmtSxxF1fyuYPqemVldVyFmlCU7w8UE14LupUSZQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-literals@7.27.1': + resolution: {integrity: sha512-0HCFSepIpLTkLcsi86GG3mTUzxV5jpmbv97hTETW3yzrAij8aqlD36toB1D0daVFJM8NK6GvKO0gslVQmm+zZA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-member-expression-literals@7.27.1': + resolution: {integrity: sha512-hqoBX4dcZ1I33jCSWcXrP+1Ku7kdqXf1oeah7ooKOIiAdKQ+uqftgCFNOSzA5AMS2XIHEYeGFg4cKRCdpxzVOQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-modules-amd@7.27.1': + resolution: {integrity: sha512-iCsytMg/N9/oFq6n+gFTvUYDZQOMK5kEdeYxmxt91fcJGycfxVP9CnrxoliM0oumFERba2i8ZtwRUCMhvP1LnA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-modules-commonjs@7.28.6': + resolution: {integrity: sha512-jppVbf8IV9iWWwWTQIxJMAJCWBuuKx71475wHwYytrRGQ2CWiDvYlADQno3tcYpS/T2UUWFQp3nVtYfK/YBQrA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-modules-systemjs@7.29.4': + resolution: {integrity: sha512-N7QmZ0xRZfjHOfZeQLJjwgX2zS9pdGHSVl/cjSGlo4dXMqvurfxXDMKY4RqEKzPozV78VMcd0lxyG13mlbKc4w==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-modules-umd@7.27.1': + resolution: {integrity: sha512-iQBE/xC5BV1OxJbp6WG7jq9IWiD+xxlZhLrdwpPkTX3ydmXdvoCpyfJN7acaIBZaOqTfr76pgzqBJflNbeRK+w==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-named-capturing-groups-regex@7.29.0': + resolution: {integrity: sha512-1CZQA5KNAD6ZYQLPw7oi5ewtDNxH/2vuCh+6SmvgDfhumForvs8a1o9n0UrEoBD8HU4djO2yWngTQlXl1NDVEQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/plugin-transform-new-target@7.27.1': + resolution: {integrity: sha512-f6PiYeqXQ05lYq3TIfIDu/MtliKUbNwkGApPUvyo6+tc7uaR4cPjPe7DFPr15Uyycg2lZU6btZ575CuQoYh7MQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-object-super@7.27.1': + resolution: {integrity: sha512-SFy8S9plRPbIcxlJ8A6mT/CxFdJx/c04JEctz4jf8YZaVS2px34j7NXRrlGlHkN/M2gnpL37ZpGRGVFLd3l8Ng==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-optional-chaining@7.28.6': + resolution: {integrity: sha512-A4zobikRGJTsX9uqVFdafzGkqD30t26ck2LmOzAuLL8b2x6k3TIqRiT2xVvA9fNmFeTX484VpsdgmKNA0bS23w==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-parameters@7.27.7': + resolution: {integrity: sha512-qBkYTYCb76RRxUM6CcZA5KRu8K4SM8ajzVeUgVdMVO9NN9uI/GaVmBg/WKJJGnNokV9SY8FxNOVWGXzqzUidBg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-property-literals@7.27.1': + resolution: {integrity: sha512-oThy3BCuCha8kDZ8ZkgOg2exvPYUlprMukKQXI1r1pJ47NCvxfkEy8vK+r/hT9nF0Aa4H1WUPZZjHTFtAhGfmQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-react-constant-elements@7.27.1': + resolution: {integrity: sha512-edoidOjl/ZxvYo4lSBOQGDSyToYVkTAwyVoa2tkuYTSmjrB1+uAedoL5iROVLXkxH+vRgA7uP4tMg2pUJpZ3Ug==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-react-display-name@7.28.0': + resolution: {integrity: sha512-D6Eujc2zMxKjfa4Zxl4GHMsmhKKZ9VpcqIchJLvwTxad9zWIYulwYItBovpDOoNLISpcZSXoDJ5gaGbQUDqViA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-react-jsx-development@7.27.1': + resolution: {integrity: sha512-ykDdF5yI4f1WrAolLqeF3hmYU12j9ntLQl/AOG1HAS21jxyg1Q0/J/tpREuYLfatGdGmXp/3yS0ZA76kOlVq9Q==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@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.0.0-0 + + '@babel/plugin-transform-react-jsx-source@7.27.1': + resolution: {integrity: sha512-zbwoTsBruTeKB9hSq73ha66iFeJHuaFkUbwvqElnygoNbj/jHRsSeokowZFN3CZ64IvEqcmmkVe89OPXc7ldAw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-react-jsx@7.27.1': + resolution: {integrity: sha512-2KH4LWGSrJIkVf5tSiBFYuXDAoWRq2MMwgivCf+93dd0GQi8RXLjKA/0EvRnVV5G0hrHczsquXuD01L8s6dmBw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-react-pure-annotations@7.27.1': + resolution: {integrity: sha512-JfuinvDOsD9FVMTHpzA/pBLisxpv1aSf+OIV8lgH3MuWrks19R27e6a6DipIg4aX1Zm9Wpb04p8wljfKrVSnPA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-regenerator@7.29.0': + resolution: {integrity: sha512-FijqlqMA7DmRdg/aINBSs04y8XNTYw/lr1gJ2WsmBnnaNw1iS43EPkJW+zK7z65auG3AWRFXWj+NcTQwYptUog==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-reserved-words@7.27.1': + resolution: {integrity: sha512-V2ABPHIJX4kC7HegLkYoDpfg9PVmuWy/i6vUM5eGK22bx4YVFD3M5F0QQnWQoDs6AGsUWTVOopBiMFQgHaSkVw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-runtime@7.29.0': + resolution: {integrity: sha512-jlaRT5dJtMaMCV6fAuLbsQMSwz/QkvaHOHOSXRitGGwSpR1blCY4KUKoyP2tYO8vJcqYe8cEj96cqSztv3uF9w==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-shorthand-properties@7.27.1': + resolution: {integrity: sha512-N/wH1vcn4oYawbJ13Y/FxcQrWk63jhfNa7jef0ih7PHSIHX2LB7GWE1rkPrOnka9kwMxb6hMl19p7lidA+EHmQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-spread@7.28.6': + resolution: {integrity: sha512-9U4QObUC0FtJl05AsUcodau/RWDytrU6uKgkxu09mLR9HLDAtUMoPuuskm5huQsoktmsYpI+bGmq+iapDcriKA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@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.0.0-0 + + '@babel/plugin-transform-template-literals@7.27.1': + resolution: {integrity: sha512-fBJKiV7F2DxZUkg5EtHKXQdbsbURW3DZKQUWphDum0uRP6eHGGa/He9mc0mypL680pb+e/lDIthRohlv8NCHkg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-typeof-symbol@7.27.1': + resolution: {integrity: sha512-RiSILC+nRJM7FY5srIyc4/fGIwUhyDuuBSdWn4y6yT6gm652DpCHZjIipgn6B7MQ1ITOUnAKWixEUjQRIBIcLw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-typescript@7.28.6': + resolution: {integrity: sha512-0YWL2RFxOqEm9Efk5PvreamxPME8OyY0wM5wh5lHjF+VtVhdneCWGzZeSqzOfiobVqQaNCd2z0tQvnI9DaPWPw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-unicode-escapes@7.27.1': + resolution: {integrity: sha512-Ysg4v6AmF26k9vpfFuTZg8HRfVWzsh1kVfowA23y9j/Gu6dOuahdUVhkLqpObp3JIv27MLSii6noRnuKN8H0Mg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-unicode-regex@7.27.1': + resolution: {integrity: sha512-xvINq24TRojDuyt6JGtHmkVkrfVV3FPT16uytxImLeBZqW3/H52yN+kM1MGuyPkIQxrzKwPHs5U/MP3qKyzkGw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/preset-env@7.20.2': + resolution: {integrity: sha512-1G0efQEWR1EHkKvKHqbG+IN/QdgwfByUpM5V5QroDzGV2t3S/WXNQd693cHiHTlCFMpr9B6FkPFXDA2lQcKoDg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/preset-modules@0.1.6': + resolution: {integrity: sha512-ID2yj6K/4lKfhuU3+EX4UvNbIt7eACFbHmNUjzA+ep+B5971CknnA/9DEWKbRokfbbtblxxxXFJJrH47UEAMVg==} + peerDependencies: + '@babel/core': ^7.0.0-0 || ^8.0.0-0 <8.0.0 + + '@babel/preset-react@7.18.6': + resolution: {integrity: sha512-zXr6atUmyYdiWRVLOZahakYmOBHtWc2WGCkP8PYTgZi0iJXDY2CN180TdrIW4OGOAdLc7TifzDIvtx6izaRIzg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/preset-react@7.27.1': + resolution: {integrity: sha512-oJHWh2gLhU9dW9HHr42q0cI0/iHHXTLGe39qvpAZZzagHy0MzYLCnCVV0symeRvzmjHyVU7mw2K06E6u/JwbhA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/preset-typescript@7.18.6': + resolution: {integrity: sha512-s9ik86kXBAnD760aybBucdpnLsAt0jK1xqJn2juOn9lkOvSHV60os5hxoVJsPzMQxvnUJFAlkont2DvvaYEBtQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/runtime@7.29.2': + resolution: {integrity: sha512-JiDShH45zKHWyGe4ZNVRrCjBz8Nh9TMmZG1kh4QTK8hCBTWBi8Da+i7s1fJw7/lYpM4ccepSNfqzZ/QvABBi5g==} + engines: {node: '>=6.9.0'} + + '@babel/template@7.27.2': + resolution: {integrity: sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==} + engines: {node: '>=6.9.0'} + + '@babel/template@7.28.6': + resolution: {integrity: sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==} + engines: {node: '>=6.9.0'} + + '@babel/traverse@7.28.0': + resolution: {integrity: sha512-mGe7UK5wWyh0bKRfupsUchrQGqvDbZDbKJw+kcRGSmdHVYrv+ltd0pnpDTVpiTqnaBru9iEvA8pz8W46v0Amwg==} + engines: {node: '>=6.9.0'} + + '@babel/traverse@7.29.0': + resolution: {integrity: sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA==} + engines: {node: '>=6.9.0'} + + '@babel/types@7.29.0': + resolution: {integrity: sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==} + engines: {node: '>=6.9.0'} + + '@biomejs/biome@1.9.4': + resolution: {integrity: sha512-1rkd7G70+o9KkTn5KLmDYXihGoTaIGO9PIIN2ZB7UJxFrWw04CZHPYiMRjYsaDvVV7hP1dYNRLxSANLaBFGpog==} + engines: {node: '>=14.21.3'} + hasBin: true + + '@biomejs/cli-darwin-arm64@1.9.4': + resolution: {integrity: sha512-bFBsPWrNvkdKrNCYeAp+xo2HecOGPAy9WyNyB/jKnnedgzl4W4Hb9ZMzYNbf8dMCGmUdSavlYHiR01QaYR58cw==} + engines: {node: '>=14.21.3'} + cpu: [arm64] + os: [darwin] + + '@biomejs/cli-darwin-x64@1.9.4': + resolution: {integrity: sha512-ngYBh/+bEedqkSevPVhLP4QfVPCpb+4BBe2p7Xs32dBgs7rh9nY2AIYUL6BgLw1JVXV8GlpKmb/hNiuIxfPfZg==} + engines: {node: '>=14.21.3'} + cpu: [x64] + os: [darwin] + + '@biomejs/cli-linux-arm64-musl@1.9.4': + resolution: {integrity: sha512-v665Ct9WCRjGa8+kTr0CzApU0+XXtRgwmzIf1SeKSGAv+2scAlW6JR5PMFo6FzqqZ64Po79cKODKf3/AAmECqA==} + engines: {node: '>=14.21.3'} + cpu: [arm64] + os: [linux] + + '@biomejs/cli-linux-arm64@1.9.4': + resolution: {integrity: sha512-fJIW0+LYujdjUgJJuwesP4EjIBl/N/TcOX3IvIHJQNsAqvV2CHIogsmA94BPG6jZATS4Hi+xv4SkBBQSt1N4/g==} + engines: {node: '>=14.21.3'} + cpu: [arm64] + os: [linux] + + '@biomejs/cli-linux-x64-musl@1.9.4': + resolution: {integrity: sha512-gEhi/jSBhZ2m6wjV530Yy8+fNqG8PAinM3oV7CyO+6c3CEh16Eizm21uHVsyVBEB6RIM8JHIl6AGYCv6Q6Q9Tg==} + engines: {node: '>=14.21.3'} + cpu: [x64] + os: [linux] + + '@biomejs/cli-linux-x64@1.9.4': + resolution: {integrity: sha512-lRCJv/Vi3Vlwmbd6K+oQ0KhLHMAysN8lXoCI7XeHlxaajk06u7G+UsFSO01NAs5iYuWKmVZjmiOzJ0OJmGsMwg==} + engines: {node: '>=14.21.3'} + cpu: [x64] + os: [linux] + + '@biomejs/cli-win32-arm64@1.9.4': + resolution: {integrity: sha512-tlbhLk+WXZmgwoIKwHIHEBZUwxml7bRJgk0X2sPyNR3S93cdRq6XulAZRQJ17FYGGzWne0fgrXBKpl7l4M87Hg==} + engines: {node: '>=14.21.3'} + cpu: [arm64] + os: [win32] + + '@biomejs/cli-win32-x64@1.9.4': + resolution: {integrity: sha512-8Y5wMhVIPaWe6jw2H+KlEm4wP/f7EW3810ZLmDlrEEy5KvBsb9ECEfu/kMWD484ijfQ8+nIi0giMgu9g1UAuuA==} + engines: {node: '>=14.21.3'} + cpu: [x64] + os: [win32] + + '@chakra-ui/anatomy@2.3.6': + resolution: {integrity: sha512-TjmjyQouIZzha/l8JxdBZN1pKZTj7sLpJ0YkFnQFyqHcbfWggW9jKWzY1E0VBnhtFz/xF3KC6UAVuZVSJx+y0g==} + + '@chakra-ui/hooks@2.4.5': + resolution: {integrity: sha512-601fWfHE2i7UjaxK/9lDLlOni6vk/I+04YDbM0BrelJy+eqxdlOmoN8Z6MZ3PzFh7ofERUASor+vL+/HaCaZ7w==} + peerDependencies: + react: '>=18' + + '@chakra-ui/react@2.10.9': + resolution: {integrity: sha512-lhdcgoocOiURwBNR3L8OioCNIaGCZqRfuKioLyaQLjOanl4jr0PQclsGb+w0cmito252vEWpsz2xRqF7y+Flrw==} + peerDependencies: + '@emotion/react': '>=11' + '@emotion/styled': '>=11' + framer-motion: '>=4.0.0' + react: '>=18' + react-dom: '>=18' + + '@chakra-ui/styled-system@2.12.4': + resolution: {integrity: sha512-oa07UG7Lic5hHSQtGRiMEnYjuhIa8lszyuVhZjZqR2Ap3VMF688y1MVPJ1pK+8OwY5uhXBgVd5c0+rI8aBZlwg==} + + '@chakra-ui/theme-tools@2.2.9': + resolution: {integrity: sha512-PcbYL19lrVvEc7Oydy//jsy/MO/rZz1DvLyO6AoI+bI/+Kwz9WfOKsspbulEhRg5COayE0R/IZPsskXZ7Mp4bA==} + peerDependencies: + '@chakra-ui/styled-system': '>=2.0.0' + + '@chakra-ui/theme@3.4.9': + resolution: {integrity: sha512-GAom2SjSdRWTcX76/2yJOFJsOWHQeBgaynCUNBsHq62OafzvELrsSHDUw0bBqBb1c2ww0CclIvGilPup8kXBFA==} + peerDependencies: + '@chakra-ui/styled-system': '>=2.8.0' + + '@chakra-ui/utils@2.2.5': + resolution: {integrity: sha512-KTBCK+M5KtXH6p54XS39ImQUMVtAx65BoZDoEms3LuObyTo1+civ1sMm4h3nRT320U6H5H7D35WnABVQjqU/4g==} + peerDependencies: + react: '>=16.8.0' + + '@changesets/apply-release-plan@7.1.1': + resolution: {integrity: sha512-9qPCm/rLx/xoOFXIHGB229+4GOL76S4MC+7tyOuTsR6+1jYlfFDQORdvwR5hDA6y4FL2BPt3qpbcQIS+dW85LA==} + + '@changesets/assemble-release-plan@6.0.10': + resolution: {integrity: sha512-rSDcqdJ9KbVyjpBIuCidhvZNIiVt1XaIYp73ycVQRIA5n/j6wQaEk0ChRLMUQ1vkxZe51PTQ9OIhbg6HQMW45A==} + + '@changesets/changelog-git@0.2.1': + resolution: {integrity: sha512-x/xEleCFLH28c3bQeQIyeZf8lFXyDFVn1SgcBiR2Tw/r4IAWlk1fzxCEZ6NxQAjF2Nwtczoen3OA2qR+UawQ8Q==} + + '@changesets/cli@2.31.0': + resolution: {integrity: sha512-AhI4enNTgHu2IZr6K4WZyf0EPch4XVMn1yOMFmCD9gsfBGqMYaHXls5HyDv6/CL5axVQABz68eG30eCtbr2wFg==} + hasBin: true + + '@changesets/config@3.1.4': + resolution: {integrity: sha512-pf0bvD/v6WI2cRlZ6hzpjtZdSlXDXMAJ+Iz7xfFzV4ZxJ8OGGAON+1qYc99ZPrijnt4xp3VGG7eNvAOGS24V1Q==} + + '@changesets/errors@0.2.0': + resolution: {integrity: sha512-6BLOQUscTpZeGljvyQXlWOItQyU71kCdGz7Pi8H8zdw6BI0g3m43iL4xKUVPWtG+qrrL9DTjpdn8eYuCQSRpow==} + + '@changesets/get-dependents-graph@2.1.4': + resolution: {integrity: sha512-ZsS00x6WvmHq3sQv8oCMwL0f/z3wbXCVuSVTJwCnnmbC/iBdNJGFx1EcbMG4PC6sXRyH69liM4A2WKXzn/kRPg==} + + '@changesets/get-release-plan@4.0.16': + resolution: {integrity: sha512-2K5Om6CrMPm45rtvckfzWo7e9jOVCKLCnXia5eUPaURH7/LWzri7pK1TycdzAuAtehLkW7VPbWLCSExTHmiI6g==} + + '@changesets/get-version-range-type@0.4.0': + resolution: {integrity: sha512-hwawtob9DryoGTpixy1D3ZXbGgJu1Rhr+ySH2PvTLHvkZuQ7sRT4oQwMh0hbqZH1weAooedEjRsbrWcGLCeyVQ==} + + '@changesets/git@3.0.4': + resolution: {integrity: sha512-BXANzRFkX+XcC1q/d27NKvlJ1yf7PSAgi8JG6dt8EfbHFHi4neau7mufcSca5zRhwOL8j9s6EqsxmT+s+/E6Sw==} + + '@changesets/logger@0.1.1': + resolution: {integrity: sha512-OQtR36ZlnuTxKqoW4Sv6x5YIhOmClRd5pWsjZsddYxpWs517R0HkyiefQPIytCVh4ZcC5x9XaG8KTdd5iRQUfg==} + + '@changesets/parse@0.4.3': + resolution: {integrity: sha512-ZDmNc53+dXdWEv7fqIUSgRQOLYoUom5Z40gmLgmATmYR9NbL6FJJHwakcCpzaeCy+1D0m0n7mT4jj2B/MQPl7A==} + + '@changesets/pre@2.0.2': + resolution: {integrity: sha512-HaL/gEyFVvkf9KFg6484wR9s0qjAXlZ8qWPDkTyKF6+zqjBe/I2mygg3MbpZ++hdi0ToqNUF8cjj7fBy0dg8Ug==} + + '@changesets/read@0.6.7': + resolution: {integrity: sha512-D1G4AUYGrBEk8vj8MGwf75k9GpN6XL3wg8i42P2jZZwFLXnlr2Pn7r9yuQNbaMCarP7ZQWNJbV6XLeysAIMhTA==} + + '@changesets/should-skip-package@0.1.2': + resolution: {integrity: sha512-qAK/WrqWLNCP22UDdBTMPH5f41elVDlsNyat180A33dWxuUDyNpg6fPi/FyTZwRriVjg0L8gnjJn2F9XAoF0qw==} + + '@changesets/types@4.1.0': + resolution: {integrity: sha512-LDQvVDv5Kb50ny2s25Fhm3d9QSZimsoUGBsUioj6MC3qbMUCuC8GPIvk/M6IvXx3lYhAs0lwWUQLb+VIEUCECw==} + + '@changesets/types@6.1.0': + resolution: {integrity: sha512-rKQcJ+o1nKNgeoYRHKOS07tAMNd3YSN0uHaJOZYjBAgxfV7TUE7JE+z4BzZdQwb5hKaYbayKN5KrYV7ODb2rAA==} + + '@changesets/write@0.4.0': + resolution: {integrity: sha512-CdTLvIOPiCNuH71pyDu3rA+Q0n65cmAbXnwWH84rKGiFumFzkmHNT8KHTMEchcxN+Kl8I54xGUhJ7l3E7X396Q==} + + '@cspotcode/source-map-support@0.8.1': + resolution: {integrity: sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==} + engines: {node: '>=12'} + + '@discoveryjs/json-ext@0.6.3': + resolution: {integrity: sha512-4B4OijXeVNOPZlYA2oEwWOTkzyltLao+xbotHQeqN++Rv27Y6s818+n2Qkp8q+Fxhn0t/5lA5X1Mxktud8eayQ==} + engines: {node: '>=14.17.0'} + + '@emnapi/runtime@1.10.0': + resolution: {integrity: sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA==} + + '@emotion/babel-plugin@11.13.5': + resolution: {integrity: sha512-pxHCpT2ex+0q+HH91/zsdHkw/lXd468DIN2zvfvLtPKLLMo6gQj7oLObq8PhkrxOZb/gGCq03S3Z7PDhS8pduQ==} + + '@emotion/cache@11.14.0': + resolution: {integrity: sha512-L/B1lc/TViYk4DcpGxtAVbx0ZyiKM5ktoIyafGkH6zg/tj+mA+NE//aPYKG0k8kCHSHVJrpLpcAlOBEXQ3SavA==} + + '@emotion/hash@0.8.0': + resolution: {integrity: sha512-kBJtf7PH6aWwZ6fka3zQ0p6SBYzx4fl1LoZXE2RrnYST9Xljm7WfKJrU4g/Xr3Beg72MLrp1AWNUmuYJTL7Cow==} + + '@emotion/hash@0.9.2': + resolution: {integrity: sha512-MyqliTZGuOm3+5ZRSaaBGP3USLw6+EGykkwZns2EPC5g8jJ4z9OrdZY9apkl3+UP9+sdz76YYkwCKP5gh8iY3g==} + + '@emotion/is-prop-valid@1.3.1': + resolution: {integrity: sha512-/ACwoqx7XQi9knQs/G0qKvv5teDMhD7bXYns9N/wM8ah8iNb8jZ2uNO0YOgiq2o2poIvVtJS2YALasQuMSQ7Kw==} + + '@emotion/memoize@0.9.0': + resolution: {integrity: sha512-30FAj7/EoJ5mwVPOWhAyCX+FPfMDrVecJAM+Iw9NRoSl4BBAQeqj4cApHHUXOVvIPgLVDsCFoz/hGD+5QQD1GQ==} + + '@emotion/react@11.14.0': + resolution: {integrity: sha512-O000MLDBDdk/EohJPFUqvnp4qnHeYkVP5B0xEG0D/L7cOKP9kefu2DXn8dj74cQfsEzUqh+sr1RzFqiL1o+PpA==} + peerDependencies: + '@types/react': '*' + react: '>=16.8.0' + peerDependenciesMeta: + '@types/react': + optional: true + + '@emotion/serialize@1.3.3': + resolution: {integrity: sha512-EISGqt7sSNWHGI76hC7x1CksiXPahbxEOrC5RjmFRJTqLyEK9/9hZvBbiYn70dw4wuwMKiEMCUlR6ZXTSWQqxA==} + + '@emotion/sheet@1.4.0': + resolution: {integrity: sha512-fTBW9/8r2w3dXWYM4HCB1Rdp8NLibOw2+XELH5m5+AkWiL/KqYX6dc0kKYlaYyKjrQ6ds33MCdMPEwgs2z1rqg==} + + '@emotion/styled@11.14.1': + resolution: {integrity: sha512-qEEJt42DuToa3gurlH4Qqc1kVpNq8wO8cJtDzU46TjlzWjDlsVyevtYCRijVq3SrHsROS+gVQ8Fnea108GnKzw==} + peerDependencies: + '@emotion/react': ^11.0.0-rc.0 + '@types/react': '*' + react: '>=16.8.0' + peerDependenciesMeta: + '@types/react': + optional: true + + '@emotion/unitless@0.10.0': + resolution: {integrity: sha512-dFoMUuQA20zvtVTuxZww6OHoJYgrzfKM1t52mVySDJnMSEa08ruEvdYQbhvyu6soU+NeLVd3yKfTfT0NeV6qGg==} + + '@emotion/unitless@0.7.5': + resolution: {integrity: sha512-OWORNpfjMsSSUBVrRBVGECkhWcULOAJz9ZW8uK9qgxD+87M7jHRcvh/A96XXNhXTLmKcoYSQtBEX7lHMO7YRwg==} + + '@emotion/use-insertion-effect-with-fallbacks@1.2.0': + resolution: {integrity: sha512-yJMtVdH59sxi/aVJBpk9FQq+OR8ll5GT8oWd57UpeaKEVGab41JWaCFA7FRLoMLloOZF/c/wsPoe+bfGmRKgDg==} + peerDependencies: + react: '>=16.8.0' + + '@emotion/utils@1.4.2': + resolution: {integrity: sha512-3vLclRofFziIa3J2wDh9jjbkUz9qk5Vi3IZ/FSTKViB0k+ef0fPV7dYrUIugbgupYDx7v9ud/SjrtEP8Y4xLoA==} + + '@emotion/weak-memoize@0.4.0': + resolution: {integrity: sha512-snKqtPW01tN0ui7yu9rGv69aJXr/a/Ywvl11sUjNtEcRc+ng/mQriFL0wLXMef74iHa/EkftbDzU9F8iFbH+zg==} + + '@esbuild/aix-ppc64@0.25.6': + resolution: {integrity: sha512-ShbM/3XxwuxjFiuVBHA+d3j5dyac0aEVVq1oluIDf71hUw0aRF59dV/efUsIwFnR6m8JNM2FjZOzmaZ8yG61kw==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [aix] + + '@esbuild/android-arm64@0.25.6': + resolution: {integrity: sha512-hd5zdUarsK6strW+3Wxi5qWws+rJhCCbMiC9QZyzoxfk5uHRIE8T287giQxzVpEvCwuJ9Qjg6bEjcRJcgfLqoA==} + engines: {node: '>=18'} + cpu: [arm64] + os: [android] + + '@esbuild/android-arm@0.25.6': + resolution: {integrity: sha512-S8ToEOVfg++AU/bHwdksHNnyLyVM+eMVAOf6yRKFitnwnbwwPNqKr3srzFRe7nzV69RQKb5DgchIX5pt3L53xg==} + engines: {node: '>=18'} + cpu: [arm] + os: [android] + + '@esbuild/android-x64@0.25.6': + resolution: {integrity: sha512-0Z7KpHSr3VBIO9A/1wcT3NTy7EB4oNC4upJ5ye3R7taCc2GUdeynSLArnon5G8scPwaU866d3H4BCrE5xLW25A==} + engines: {node: '>=18'} + cpu: [x64] + os: [android] + + '@esbuild/darwin-arm64@0.25.6': + resolution: {integrity: sha512-FFCssz3XBavjxcFxKsGy2DYK5VSvJqa6y5HXljKzhRZ87LvEi13brPrf/wdyl/BbpbMKJNOr1Sd0jtW4Ge1pAA==} + engines: {node: '>=18'} + cpu: [arm64] + os: [darwin] + + '@esbuild/darwin-x64@0.25.6': + resolution: {integrity: sha512-GfXs5kry/TkGM2vKqK2oyiLFygJRqKVhawu3+DOCk7OxLy/6jYkWXhlHwOoTb0WqGnWGAS7sooxbZowy+pK9Yg==} + engines: {node: '>=18'} + cpu: [x64] + os: [darwin] + + '@esbuild/freebsd-arm64@0.25.6': + resolution: {integrity: sha512-aoLF2c3OvDn2XDTRvn8hN6DRzVVpDlj2B/F66clWd/FHLiHaG3aVZjxQX2DYphA5y/evbdGvC6Us13tvyt4pWg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [freebsd] + + '@esbuild/freebsd-x64@0.25.6': + resolution: {integrity: sha512-2SkqTjTSo2dYi/jzFbU9Plt1vk0+nNg8YC8rOXXea+iA3hfNJWebKYPs3xnOUf9+ZWhKAaxnQNUf2X9LOpeiMQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [freebsd] + + '@esbuild/linux-arm64@0.25.6': + resolution: {integrity: sha512-b967hU0gqKd9Drsh/UuAm21Khpoh6mPBSgz8mKRq4P5mVK8bpA+hQzmm/ZwGVULSNBzKdZPQBRT3+WuVavcWsQ==} + engines: {node: '>=18'} + cpu: [arm64] + os: [linux] + + '@esbuild/linux-arm@0.25.6': + resolution: {integrity: sha512-SZHQlzvqv4Du5PrKE2faN0qlbsaW/3QQfUUc6yO2EjFcA83xnwm91UbEEVx4ApZ9Z5oG8Bxz4qPE+HFwtVcfyw==} + engines: {node: '>=18'} + cpu: [arm] + os: [linux] + + '@esbuild/linux-ia32@0.25.6': + resolution: {integrity: sha512-aHWdQ2AAltRkLPOsKdi3xv0mZ8fUGPdlKEjIEhxCPm5yKEThcUjHpWB1idN74lfXGnZ5SULQSgtr5Qos5B0bPw==} + engines: {node: '>=18'} + cpu: [ia32] + os: [linux] + + '@esbuild/linux-loong64@0.25.6': + resolution: {integrity: sha512-VgKCsHdXRSQ7E1+QXGdRPlQ/e08bN6WMQb27/TMfV+vPjjTImuT9PmLXupRlC90S1JeNNW5lzkAEO/McKeJ2yg==} + engines: {node: '>=18'} + cpu: [loong64] + os: [linux] + + '@esbuild/linux-mips64el@0.25.6': + resolution: {integrity: sha512-WViNlpivRKT9/py3kCmkHnn44GkGXVdXfdc4drNmRl15zVQ2+D2uFwdlGh6IuK5AAnGTo2qPB1Djppj+t78rzw==} + engines: {node: '>=18'} + cpu: [mips64el] + os: [linux] + + '@esbuild/linux-ppc64@0.25.6': + resolution: {integrity: sha512-wyYKZ9NTdmAMb5730I38lBqVu6cKl4ZfYXIs31Baf8aoOtB4xSGi3THmDYt4BTFHk7/EcVixkOV2uZfwU3Q2Jw==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [linux] + + '@esbuild/linux-riscv64@0.25.6': + resolution: {integrity: sha512-KZh7bAGGcrinEj4qzilJ4hqTY3Dg2U82c8bv+e1xqNqZCrCyc+TL9AUEn5WGKDzm3CfC5RODE/qc96OcbIe33w==} + engines: {node: '>=18'} + cpu: [riscv64] + os: [linux] + + '@esbuild/linux-s390x@0.25.6': + resolution: {integrity: sha512-9N1LsTwAuE9oj6lHMyyAM+ucxGiVnEqUdp4v7IaMmrwb06ZTEVCIs3oPPplVsnjPfyjmxwHxHMF8b6vzUVAUGw==} + engines: {node: '>=18'} + cpu: [s390x] + os: [linux] + + '@esbuild/linux-x64@0.25.6': + resolution: {integrity: sha512-A6bJB41b4lKFWRKNrWoP2LHsjVzNiaurf7wyj/XtFNTsnPuxwEBWHLty+ZE0dWBKuSK1fvKgrKaNjBS7qbFKig==} + engines: {node: '>=18'} + cpu: [x64] + os: [linux] + + '@esbuild/netbsd-arm64@0.25.6': + resolution: {integrity: sha512-IjA+DcwoVpjEvyxZddDqBY+uJ2Snc6duLpjmkXm/v4xuS3H+3FkLZlDm9ZsAbF9rsfP3zeA0/ArNDORZgrxR/Q==} + engines: {node: '>=18'} + cpu: [arm64] + os: [netbsd] + + '@esbuild/netbsd-x64@0.25.6': + resolution: {integrity: sha512-dUXuZr5WenIDlMHdMkvDc1FAu4xdWixTCRgP7RQLBOkkGgwuuzaGSYcOpW4jFxzpzL1ejb8yF620UxAqnBrR9g==} + engines: {node: '>=18'} + cpu: [x64] + os: [netbsd] + + '@esbuild/openbsd-arm64@0.25.6': + resolution: {integrity: sha512-l8ZCvXP0tbTJ3iaqdNf3pjaOSd5ex/e6/omLIQCVBLmHTlfXW3zAxQ4fnDmPLOB1x9xrcSi/xtCWFwCZRIaEwg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openbsd] + + '@esbuild/openbsd-x64@0.25.6': + resolution: {integrity: sha512-hKrmDa0aOFOr71KQ/19JC7az1P0GWtCN1t2ahYAf4O007DHZt/dW8ym5+CUdJhQ/qkZmI1HAF8KkJbEFtCL7gw==} + engines: {node: '>=18'} + cpu: [x64] + os: [openbsd] + + '@esbuild/openharmony-arm64@0.25.6': + resolution: {integrity: sha512-+SqBcAWoB1fYKmpWoQP4pGtx+pUUC//RNYhFdbcSA16617cchuryuhOCRpPsjCblKukAckWsV+aQ3UKT/RMPcA==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openharmony] + + '@esbuild/sunos-x64@0.25.6': + resolution: {integrity: sha512-dyCGxv1/Br7MiSC42qinGL8KkG4kX0pEsdb0+TKhmJZgCUDBGmyo1/ArCjNGiOLiIAgdbWgmWgib4HoCi5t7kA==} + engines: {node: '>=18'} + cpu: [x64] + os: [sunos] + + '@esbuild/win32-arm64@0.25.6': + resolution: {integrity: sha512-42QOgcZeZOvXfsCBJF5Afw73t4veOId//XD3i+/9gSkhSV6Gk3VPlWncctI+JcOyERv85FUo7RxuxGy+z8A43Q==} + engines: {node: '>=18'} + cpu: [arm64] + os: [win32] + + '@esbuild/win32-ia32@0.25.6': + resolution: {integrity: sha512-4AWhgXmDuYN7rJI6ORB+uU9DHLq/erBbuMoAuB4VWJTu5KtCgcKYPynF0YI1VkBNuEfjNlLrFr9KZPJzrtLkrQ==} + engines: {node: '>=18'} + cpu: [ia32] + os: [win32] + + '@esbuild/win32-x64@0.25.6': + resolution: {integrity: sha512-NgJPHHbEpLQgDH2MjQu90pzW/5vvXIZ7KOnPyNBm92A6WgZ/7b6fJyUBjoumLqeOQQGqY2QjQxRo97ah4Sj0cA==} + engines: {node: '>=18'} + cpu: [x64] + os: [win32] + + '@eslint-community/eslint-utils@4.9.1': + resolution: {integrity: sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 + + '@eslint/eslintrc@1.4.1': + resolution: {integrity: sha512-XXrH9Uarn0stsyldqDYq8r++mROmWRI1xKMXa640Bb//SY1+ECYX6VzT6Lcx5frD0V30XieqJ0oX9I2Xj5aoMA==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + '@floating-ui/core@1.7.5': + resolution: {integrity: sha512-1Ih4WTWyw0+lKyFMcBHGbb5U5FtuHJuujoyyr5zTaWS5EYMeT6Jb2AuDeftsCsEuchO+mM2ij5+q9crhydzLhQ==} + + '@floating-ui/dom@1.7.6': + resolution: {integrity: sha512-9gZSAI5XM36880PPMm//9dfiEngYoC6Am2izES1FF406YFsjvyBMmeJ2g4SAju3xWwtuynNRFL2s9hgxpLI5SQ==} + + '@floating-ui/react-dom@2.1.8': + resolution: {integrity: sha512-cC52bHwM/n/CxS87FH0yWdngEZrjdtLW/qVruo68qg+prK7ZQ4YGdut2GyDVpoGeAYe/h899rVeOVm6Oi40k2A==} + peerDependencies: + react: '>=16.8.0' + react-dom: '>=16.8.0' + + '@floating-ui/react@0.27.19': + resolution: {integrity: sha512-31B8h5mm8YxotlE7/AU/PhNAl8eWxAmjL/v2QOxroDNkTFLk3Uu82u63N3b6TXa4EGJeeZLVcd/9AlNlVqzeog==} + peerDependencies: + react: '>=17.0.0' + react-dom: '>=17.0.0' + + '@floating-ui/utils@0.2.11': + resolution: {integrity: sha512-RiB/yIh78pcIxl6lLMG0CgBXAZ2Y0eVHqMPYugu+9U0AeT6YBeiJpf7lbdJNIugFP5SIjwNRgo4DhR1Qxi26Gg==} + + '@heroicons/react@2.2.0': + resolution: {integrity: sha512-LMcepvRaS9LYHJGsF0zzmgKCUim/X3N/DQKc4jepAXJ7l8QxJ1PmxJzqplF2Z3FE4PqBAIGyJAQ/w4B5dsqbtQ==} + peerDependencies: + react: '>= 16 || ^19.0.0-rc' + + '@hono/node-server@1.19.14': + resolution: {integrity: sha512-GwtvgtXxnWsucXvbQXkRgqksiH2Qed37H9xHZocE5sA3N8O8O8/8FA3uclQXxXVzc9XBZuEOMK7+r02FmSpHtw==} + engines: {node: '>=18.14.1'} + peerDependencies: + hono: ^4 + + '@humanwhocodes/config-array@0.10.7': + resolution: {integrity: sha512-MDl6D6sBsaV452/QSdX+4CXIjZhIcI0PELsxUjk4U828yd58vk3bTIvk/6w5FY+4hIy9sLW0sfrV7K7Kc++j/w==} + engines: {node: '>=10.10.0'} + deprecated: Use @eslint/config-array instead + + '@humanwhocodes/gitignore-to-minimatch@1.0.2': + resolution: {integrity: sha512-rSqmMJDdLFUsyxR6FMtD00nfQKKLFb1kv+qBbOVKqErvloEIJLo5bDTJTQNTYgeyp78JsA7u/NPi5jT1GR/MuA==} + + '@humanwhocodes/module-importer@1.0.1': + resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==} + engines: {node: '>=12.22'} + + '@humanwhocodes/object-schema@1.2.1': + resolution: {integrity: sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==} + deprecated: Use @eslint/object-schema instead + + '@img/colour@1.1.0': + resolution: {integrity: sha512-Td76q7j57o/tLVdgS746cYARfSyxk8iEfRxewL9h4OMzYhbW4TAcppl0mT4eyqXddh6L/jwoM75mo7ixa/pCeQ==} + engines: {node: '>=18'} + + '@img/sharp-darwin-arm64@0.34.5': + resolution: {integrity: sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [arm64] + os: [darwin] + + '@img/sharp-darwin-x64@0.34.5': + resolution: {integrity: sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [x64] + os: [darwin] + + '@img/sharp-libvips-darwin-arm64@1.2.4': + resolution: {integrity: sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g==} + cpu: [arm64] + os: [darwin] + + '@img/sharp-libvips-darwin-x64@1.2.4': + resolution: {integrity: sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg==} + cpu: [x64] + os: [darwin] + + '@img/sharp-libvips-linux-arm64@1.2.4': + resolution: {integrity: sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw==} + cpu: [arm64] + os: [linux] + + '@img/sharp-libvips-linux-arm@1.2.4': + resolution: {integrity: sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==} + cpu: [arm] + os: [linux] + + '@img/sharp-libvips-linux-ppc64@1.2.4': + resolution: {integrity: sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA==} + cpu: [ppc64] + os: [linux] + + '@img/sharp-libvips-linux-riscv64@1.2.4': + resolution: {integrity: sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA==} + cpu: [riscv64] + os: [linux] + + '@img/sharp-libvips-linux-s390x@1.2.4': + resolution: {integrity: sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ==} + cpu: [s390x] + os: [linux] + + '@img/sharp-libvips-linux-x64@1.2.4': + resolution: {integrity: sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==} + cpu: [x64] + os: [linux] + + '@img/sharp-libvips-linuxmusl-arm64@1.2.4': + resolution: {integrity: sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==} + cpu: [arm64] + os: [linux] + + '@img/sharp-libvips-linuxmusl-x64@1.2.4': + resolution: {integrity: sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==} + cpu: [x64] + os: [linux] + + '@img/sharp-linux-arm64@0.34.5': + resolution: {integrity: sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [arm64] + os: [linux] + + '@img/sharp-linux-arm@0.34.5': + resolution: {integrity: sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [arm] + os: [linux] + + '@img/sharp-linux-ppc64@0.34.5': + resolution: {integrity: sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [ppc64] + os: [linux] + + '@img/sharp-linux-riscv64@0.34.5': + resolution: {integrity: sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [riscv64] + os: [linux] + + '@img/sharp-linux-s390x@0.34.5': + resolution: {integrity: sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [s390x] + os: [linux] + + '@img/sharp-linux-x64@0.34.5': + resolution: {integrity: sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [x64] + os: [linux] + + '@img/sharp-linuxmusl-arm64@0.34.5': + resolution: {integrity: sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [arm64] + os: [linux] + + '@img/sharp-linuxmusl-x64@0.34.5': + resolution: {integrity: sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [x64] + os: [linux] + + '@img/sharp-wasm32@0.34.5': + resolution: {integrity: sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [wasm32] + + '@img/sharp-win32-arm64@0.34.5': + resolution: {integrity: sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [arm64] + os: [win32] + + '@img/sharp-win32-ia32@0.34.5': + resolution: {integrity: sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [ia32] + os: [win32] + + '@img/sharp-win32-x64@0.34.5': + resolution: {integrity: sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [x64] + os: [win32] + + '@inquirer/external-editor@1.0.3': + resolution: {integrity: sha512-RWbSrDiYmO4LbejWY7ttpxczuwQyZLBUyygsA9Nsv95hpzUWwnNTVQmAq3xuh7vNwCp07UTmE5i11XAEExx4RA==} + engines: {node: '>=18'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@jest/diff-sequences@30.0.1': + resolution: {integrity: sha512-n5H8QLDJ47QqbCNn5SuFjCRDrOLEZ0h8vAHCK5RL9Ls7Xa8AQLa/YxAc9UjFqoEDM48muwtBGjtMY5cr0PLDCw==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + '@jest/expect-utils@30.0.4': + resolution: {integrity: sha512-EgXecHDNfANeqOkcak0DxsoVI4qkDUsR7n/Lr2vtmTBjwLPBnnPOF71S11Q8IObWzxm2QgQoY6f9hzrRD3gHRA==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + '@jest/get-type@30.0.1': + resolution: {integrity: sha512-AyYdemXCptSRFirI5EPazNxyPwAL0jXt3zceFjaj8NFiKP9pOi0bfXonf6qkf82z2t3QWPeLCWWw4stPBzctLw==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + '@jest/pattern@30.0.1': + resolution: {integrity: sha512-gWp7NfQW27LaBQz3TITS8L7ZCQ0TLvtmI//4OwlQRx4rnWxcPNIYjxZpDcN4+UlGxgm3jS5QPz8IPTCkb59wZA==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + '@jest/schemas@30.0.1': + resolution: {integrity: sha512-+g/1TKjFuGrf1Hh0QPCv0gISwBxJ+MQSNXmG9zjHy7BmFhtoJ9fdNhWJp3qUKRi93AOZHXtdxZgJ1vAtz6z65w==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + '@jest/types@30.0.1': + resolution: {integrity: sha512-HGwoYRVF0QSKJu1ZQX0o5ZrUrrhj0aOOFA8hXrumD7SIzjouevhawbTjmXdwOmURdGluU9DM/XvGm3NyFoiQjw==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + '@jridgewell/gen-mapping@0.3.12': + resolution: {integrity: sha512-OuLGC46TjB5BbN1dH8JULVVZY4WTdkF7tV9Ys6wLL1rubZnCMstOhNHueU5bLCrnRuDhKPDM4g6sw4Bel5Gzqg==} + + '@jridgewell/resolve-uri@3.1.2': + resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} + engines: {node: '>=6.0.0'} + + '@jridgewell/source-map@0.3.11': + resolution: {integrity: sha512-ZMp1V8ZFcPG5dIWnQLr3NSI1MiCU7UETdS/A0G8V/XWHvJv3ZsFqutJn1Y5RPmAPX6F3BiE397OqveU/9NCuIA==} + + '@jridgewell/sourcemap-codec@1.5.4': + resolution: {integrity: sha512-VT2+G1VQs/9oz078bLrYbecdZKs912zQlkelYpuf+SXF+QvZDYJlbx/LSx+meSAwdDFnF8FVXW92AVjjkVmgFw==} + + '@jridgewell/trace-mapping@0.3.29': + resolution: {integrity: sha512-uw6guiW/gcAGPDhLmd77/6lW8QLeiV5RUTsAX46Db6oLhGaVj4lhnPwb184s1bkc8kdVg/+h988dro8GRDpmYQ==} + + '@jridgewell/trace-mapping@0.3.9': + resolution: {integrity: sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==} + + '@jsonjoy.com/base64@1.1.2': + resolution: {integrity: sha512-q6XAnWQDIMA3+FTiOYajoYqySkO+JSat0ytXGSuRdq9uXE7o92gzuQwQM14xaCRlBLGq3v5miDGC4vkVTn54xA==} + engines: {node: '>=10.0'} + peerDependencies: + tslib: '2' + + '@jsonjoy.com/base64@17.67.0': + resolution: {integrity: sha512-5SEsJGsm15aP8TQGkDfJvz9axgPwAEm98S5DxOuYe8e1EbfajcDmgeXXzccEjh+mLnjqEKrkBdjHWS5vFNwDdw==} + engines: {node: '>=10.0'} + peerDependencies: + tslib: '2' + + '@jsonjoy.com/buffers@1.2.1': + resolution: {integrity: sha512-12cdlDwX4RUM3QxmUbVJWqZ/mrK6dFQH4Zxq6+r1YXKXYBNgZXndx2qbCJwh3+WWkCSn67IjnlG3XYTvmvYtgA==} + engines: {node: '>=10.0'} + peerDependencies: + tslib: '2' + + '@jsonjoy.com/buffers@17.67.0': + resolution: {integrity: sha512-tfExRpYxBvi32vPs9ZHaTjSP4fHAfzSmcahOfNxtvGHcyJel+aibkPlGeBB+7AoC6hL7lXIE++8okecBxx7lcw==} + engines: {node: '>=10.0'} + peerDependencies: + tslib: '2' + + '@jsonjoy.com/codegen@1.0.0': + resolution: {integrity: sha512-E8Oy+08cmCf0EK/NMxpaJZmOxPqM+6iSe2S4nlSBrPZOORoDJILxtbSUEDKQyTamm/BVAhIGllOBNU79/dwf0g==} + engines: {node: '>=10.0'} + peerDependencies: + tslib: '2' + + '@jsonjoy.com/codegen@17.67.0': + resolution: {integrity: sha512-idnkUplROpdBOV0HMcwhsCUS5TRUi9poagdGs70A6S4ux9+/aPuKbh8+UYRTLYQHtXvAdNfQWXDqZEx5k4Dj2Q==} + engines: {node: '>=10.0'} + peerDependencies: + tslib: '2' + + '@jsonjoy.com/fs-core@4.57.2': + resolution: {integrity: sha512-SVjwklkpIV5wrynpYtuYnfYH1QF4/nDuLBX7VXdb+3miglcAgBVZb/5y0cOsehRV/9Vb+3UqhkMq3/NR3ztdkQ==} + engines: {node: '>=10.0'} + peerDependencies: + tslib: '2' + + '@jsonjoy.com/fs-fsa@4.57.2': + resolution: {integrity: sha512-fhO8+iR2I+OCw668ISDJdn1aArc9zx033sWejIyzQ8RBeXa9bDSaUeA3ix0poYOfrj1KdOzytmYNv2/uLDfV6g==} + engines: {node: '>=10.0'} + peerDependencies: + tslib: '2' + + '@jsonjoy.com/fs-node-builtins@4.57.2': + resolution: {integrity: sha512-xhiegylRmhw43Ki2HO1ZBL7DQ5ja/qpRsL29VtQ2xuUHiuDGbgf2uD4p9Qd8hJI5P6RCtGYD50IXHXVq/Ocjcg==} + engines: {node: '>=10.0'} + peerDependencies: + tslib: '2' + + '@jsonjoy.com/fs-node-to-fsa@4.57.2': + resolution: {integrity: sha512-18LmWTSONhoAPW+IWRuf8w/+zRolPFGPeGwMxlAhhfY11EKzX+5XHDBPAw67dBF5dxDErHJbl40U+3IXSDRXSQ==} + engines: {node: '>=10.0'} + peerDependencies: + tslib: '2' + + '@jsonjoy.com/fs-node-utils@4.57.2': + resolution: {integrity: sha512-rsPSJgekz43IlNbLyAM/Ab+ouYLWGp5DDBfYBNNEqDaSpsbXfthBn29Q4muFA9L0F+Z3mKo+CWlgSCXrf+mOyQ==} + engines: {node: '>=10.0'} + peerDependencies: + tslib: '2' + + '@jsonjoy.com/fs-node@4.57.2': + resolution: {integrity: sha512-nX2AdL6cOFwLdju9G4/nbRnYevmCJbh7N7hvR3gGm97Cs60uEjyd0rpR+YBS7cTg175zzl22pGKXR5USaQMvKg==} + engines: {node: '>=10.0'} + peerDependencies: + tslib: '2' + + '@jsonjoy.com/fs-print@4.57.2': + resolution: {integrity: sha512-wK9NSow48i4DbDl9F1CQE5TqnyZOJ04elU3WFG5aJ76p+YxO/ulyBBQvKsessPxdo381Bc2pcEoyPujMOhcRqQ==} + engines: {node: '>=10.0'} + peerDependencies: + tslib: '2' + + '@jsonjoy.com/fs-snapshot@4.57.2': + resolution: {integrity: sha512-GdduDZuoP5V/QCgJkx9+BZ6SC0EZ/smXAdTS7PfMqgMTGXLlt/bH/FqMYaqB9JmLf05sJPtO0XRbAwwkEEPbVw==} + engines: {node: '>=10.0'} + peerDependencies: + tslib: '2' + + '@jsonjoy.com/json-pack@1.21.0': + resolution: {integrity: sha512-+AKG+R2cfZMShzrF2uQw34v3zbeDYUqnQ+jg7ORic3BGtfw9p/+N6RJbq/kkV8JmYZaINknaEQ2m0/f693ZPpg==} + engines: {node: '>=10.0'} + peerDependencies: + tslib: '2' + + '@jsonjoy.com/json-pack@17.67.0': + resolution: {integrity: sha512-t0ejURcGaZsn1ClbJ/3kFqSOjlryd92eQY465IYrezsXmPcfHPE/av4twRSxf6WE+TkZgLY+71vCZbiIiFKA/w==} + engines: {node: '>=10.0'} + peerDependencies: + tslib: '2' + + '@jsonjoy.com/json-pointer@1.0.2': + resolution: {integrity: sha512-Fsn6wM2zlDzY1U+v4Nc8bo3bVqgfNTGcn6dMgs6FjrEnt4ZCe60o6ByKRjOGlI2gow0aE/Q41QOigdTqkyK5fg==} + engines: {node: '>=10.0'} + peerDependencies: + tslib: '2' + + '@jsonjoy.com/json-pointer@17.67.0': + resolution: {integrity: sha512-+iqOFInH+QZGmSuaybBUNdh7yvNrXvqR+h3wjXm0N/3JK1EyyFAeGJvqnmQL61d1ARLlk/wJdFKSL+LHJ1eaUA==} + engines: {node: '>=10.0'} + peerDependencies: + tslib: '2' + + '@jsonjoy.com/util@1.9.0': + resolution: {integrity: sha512-pLuQo+VPRnN8hfPqUTLTHk126wuYdXVxE6aDmjSeV4NCAgyxWbiOIeNJVtID3h1Vzpoi9m4jXezf73I6LgabgQ==} + engines: {node: '>=10.0'} + peerDependencies: + tslib: '2' + + '@jsonjoy.com/util@17.67.0': + resolution: {integrity: sha512-6+8xBaz1rLSohlGh68D1pdw3AwDi9xydm8QNlAFkvnavCJYSze+pxoW2VKP8p308jtlMRLs5NTHfPlZLd4w7ew==} + engines: {node: '>=10.0'} + peerDependencies: + tslib: '2' + + '@leichtgewicht/ip-codec@2.0.5': + resolution: {integrity: sha512-Vo+PSpZG2/fmgmiNzYK9qWRh8h/CHrwD0mo1h1DzL4yzHNSfWYujGTYsWGreD000gcgmZ7K4Ys6Tx9TxtsKdDw==} + + '@mantine/core@8.3.9': + resolution: {integrity: sha512-ivj0Crn5N521cI2eWZBsBGckg0ZYRqfOJz5vbbvYmfj65bp0EdsyqZuOxXzIcn2aUScQhskfvzyhV5XIUv81PQ==} + peerDependencies: + '@mantine/hooks': 8.3.9 + react: ^18.x || ^19.x + react-dom: ^18.x || ^19.x + + '@mantine/hooks@8.3.9': + resolution: {integrity: sha512-Dfz7W0+K1cq4Gb1WFQCZn8tsMXkLH6MV409wZR/ToqsxdNDUMJ/xxbfnwEXWEZjXNJd1wDETHgc+cZG2lTe3Xw==} + peerDependencies: + react: ^18.x || ^19.x + + '@manypkg/find-root@1.1.0': + resolution: {integrity: sha512-mki5uBvhHzO8kYYix/WRy2WX8S3B5wdVSc9D6KcU5lQNglP2yt58/VfLuAK49glRXChosY8ap2oJ1qgma3GUVA==} + + '@manypkg/get-packages@1.1.3': + resolution: {integrity: sha512-fo+QhuU3qE/2TQMQmbVMqaQ6EWbMhi4ABWP+O4AM1NqPBuy0OrApV5LO6BrrgnhtAHS2NH6RrVk9OL181tTi8A==} + + '@modelcontextprotocol/sdk@1.29.0': + resolution: {integrity: sha512-zo37mZA9hJWpULgkRpowewez1y6ML5GsXJPY8FI0tBBCd77HEvza4jDqRKOXgHNn867PVGCyTdzqpz0izu5ZjQ==} + engines: {node: '>=18'} + peerDependencies: + '@cfworker/json-schema': ^4.1.1 + zod: ^3.25 || ^4.0 + peerDependenciesMeta: + '@cfworker/json-schema': + optional: true + + '@mui/core-downloads-tracker@5.18.0': + resolution: {integrity: sha512-jbhwoQ1AY200PSSOrNXmrFCaSDSJWP7qk6urkTmIirvRXDROkqe+QwcLlUiw/PrREwsIF/vm3/dAXvjlMHF0RA==} + + '@mui/icons-material@5.18.0': + resolution: {integrity: sha512-1s0vEZj5XFXDMmz3Arl/R7IncFqJ+WQ95LDp1roHWGDE2oCO3IS4/hmiOv1/8SD9r6B7tv9GLiqVZYHo+6PkTg==} + engines: {node: '>=12.0.0'} + peerDependencies: + '@mui/material': ^5.0.0 + '@types/react': ^17.0.0 || ^18.0.0 || ^19.0.0 + react: ^17.0.0 || ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + + '@mui/material@5.18.0': + resolution: {integrity: sha512-bbH/HaJZpFtXGvWg3TsBWG4eyt3gah3E7nCNU8GLyRjVoWcA91Vm/T+sjHfUcwgJSw9iLtucfHBoq+qW/T30aA==} + engines: {node: '>=12.0.0'} + peerDependencies: + '@emotion/react': ^11.5.0 + '@emotion/styled': ^11.3.0 + '@types/react': ^17.0.0 || ^18.0.0 || ^19.0.0 + react: ^17.0.0 || ^18.0.0 || ^19.0.0 + react-dom: ^17.0.0 || ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + '@emotion/react': + optional: true + '@emotion/styled': + optional: true + '@types/react': + optional: true + + '@mui/private-theming@5.17.1': + resolution: {integrity: sha512-XMxU0NTYcKqdsG8LRmSoxERPXwMbp16sIXPcLVgLGII/bVNagX0xaheWAwFv8+zDK7tI3ajllkuD3GZZE++ICQ==} + engines: {node: '>=12.0.0'} + peerDependencies: + '@types/react': ^17.0.0 || ^18.0.0 || ^19.0.0 + react: ^17.0.0 || ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + + '@mui/styled-engine@5.18.0': + resolution: {integrity: sha512-BN/vKV/O6uaQh2z5rXV+MBlVrEkwoS/TK75rFQ2mjxA7+NBo8qtTAOA4UaM0XeJfn7kh2wZ+xQw2HAx0u+TiBg==} + engines: {node: '>=12.0.0'} + peerDependencies: + '@emotion/react': ^11.4.1 + '@emotion/styled': ^11.3.0 + react: ^17.0.0 || ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + '@emotion/react': + optional: true + '@emotion/styled': + optional: true + + '@mui/system@5.18.0': + resolution: {integrity: sha512-ojZGVcRWqWhu557cdO3pWHloIGJdzVtxs3rk0F9L+x55LsUjcMUVkEhiF7E4TMxZoF9MmIHGGs0ZX3FDLAf0Xw==} + engines: {node: '>=12.0.0'} + peerDependencies: + '@emotion/react': ^11.5.0 + '@emotion/styled': ^11.3.0 + '@types/react': ^17.0.0 || ^18.0.0 || ^19.0.0 + react: ^17.0.0 || ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + '@emotion/react': + optional: true + '@emotion/styled': + optional: true + '@types/react': + optional: true + + '@mui/types@7.2.24': + resolution: {integrity: sha512-3c8tRt/CbWZ+pEg7QpSwbdxOk36EfmhbKf6AGZsD1EcLDLTSZoxxJ86FVtcjxvjuhdyBiWKSTGZFaXCnidO2kw==} + peerDependencies: + '@types/react': ^17.0.0 || ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + + '@mui/utils@5.17.1': + resolution: {integrity: sha512-jEZ8FTqInt2WzxDV8bhImWBqeQRD99c/id/fq83H0ER9tFl+sfZlaAoCdznGvbSQQ9ividMxqSV2c7cC1vBcQg==} + engines: {node: '>=12.0.0'} + peerDependencies: + '@types/react': ^17.0.0 || ^18.0.0 || ^19.0.0 + react: ^17.0.0 || ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + + '@next/env@15.5.18': + resolution: {integrity: sha512-hAV85Ckd9QR6RvH04MEKwsfLTksvFpO47j9xwtoIuvuPnlwecpSi+uZTtm8HirVbtlI2Fnz//xpcSTjFdyJk+g==} + + '@next/swc-darwin-arm64@15.5.18': + resolution: {integrity: sha512-w0WvQf1n+txiwns/9pwIQteCJpZTbxzO2SE0FLcwuD4v0WEh1JPOjdyxWL21XwJsdpx8cFRjyzxzCS/siP7HcQ==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [darwin] + + '@next/swc-darwin-x64@15.5.18': + resolution: {integrity: sha512-znn71QmDuxm+BOaglihMZfvyySMnNljkVIY5Z2TCssBmm+WqL6c19VhtH5ktFkHa8EZ2bnTUpcNcmNSQsg67og==} + engines: {node: '>= 10'} + cpu: [x64] + os: [darwin] + + '@next/swc-linux-arm64-gnu@15.5.18': + resolution: {integrity: sha512-yPPe5MNL+igZUa+OsqQJisqSfh6oarIuA1Q0BDxljGJhRQyZeP+WRHh7rs/jZUGMh5aY0YdIjXZG0VohkKkUdw==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + + '@next/swc-linux-arm64-musl@15.5.18': + resolution: {integrity: sha512-glaCczEWIrHsokFZ3pP08U4BpKxwIdnT+txdOM32OBgpL9Yw4aqx8NejmgtZQZOdstQ5f0L3CasIZudzCuD+nw==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + + '@next/swc-linux-x64-gnu@15.5.18': + resolution: {integrity: sha512-oUfg2EgJmU3R0OCOWiokGFUTvZiPfXtriXiuF3YNxRoROCdgvTedHIzYoeKH34gsZxS/V7mHbfq2hpAHwhH1/A==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + + '@next/swc-linux-x64-musl@15.5.18': + resolution: {integrity: sha512-JLxSP3KTd9iu/bvUMQxH7RJo9xKSHf55/6RPE4a6FTSZygGn7uvZbCej0AHXydwkggQGSD9UddSjwv6Xz5ESfA==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + + '@next/swc-win32-arm64-msvc@15.5.18': + resolution: {integrity: sha512-ir1v7enP52K2HNz3tQQvwF+x7VNxBk1ciiZ18WBPvxf4C59IqdfmHPJYK3vH7rSxpuCVw/8C712wTXNAtEp+NA==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [win32] + + '@next/swc-win32-x64-msvc@15.5.18': + resolution: {integrity: sha512-LIu5me6QTANCd25E7I5uIEfvgQ06RK7tvHAbYo3zCb3VpxQEPvMcSpd87NwUABDT6MbGPdEGR5VRiK4PPTJhQg==} + engines: {node: '>= 10'} + cpu: [x64] + os: [win32] + + '@noble/hashes@1.4.0': + resolution: {integrity: sha512-V1JJ1WTRUqHHrOSh597hURcMqVKVGL/ea3kv0gSnEdsEZ0/+VyPghM1lMNGc00z7CIQorSvbKpuJkxvuHbvdbg==} + engines: {node: '>= 16'} + + '@nodelib/fs.scandir@2.1.5': + resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} + engines: {node: '>= 8'} + + '@nodelib/fs.stat@2.0.5': + resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==} + engines: {node: '>= 8'} + + '@nodelib/fs.walk@1.2.8': + resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} + engines: {node: '>= 8'} + + '@nrwl/cli@15.9.7': + resolution: {integrity: sha512-1jtHBDuJzA57My5nLzYiM372mJW0NY6rFKxlWt5a0RLsAZdPTHsd8lE3Gs9XinGC1jhXbruWmhhnKyYtZvX/zA==} + + '@nrwl/devkit@15.9.7': + resolution: {integrity: sha512-Sb7Am2TMT8AVq8e+vxOlk3AtOA2M0qCmhBzoM1OJbdHaPKc0g0UgSnWRml1kPGg5qfPk72tWclLoZJ5/ut0vTg==} + peerDependencies: + nx: '>= 14.1 <= 16' + + '@nrwl/eslint-plugin-nx@15.9.7': + resolution: {integrity: sha512-vbWayEFtTqIrpv32dc1cQeNpGeUATkQJc7It6SZDaJlp2JzNVe8kgFfSx4wbxfvzpRMIUmM/3s+J70/mABGsHw==} + peerDependencies: + '@typescript-eslint/parser': ^5.29.0 + eslint-config-prettier: ^8.1.0 + peerDependenciesMeta: + eslint-config-prettier: + optional: true + + '@nrwl/js@15.9.7': + resolution: {integrity: sha512-YHPYJP1by/YH+nO2MXBpsgQDx+QVRwnxAYcfr9240YbwCZcPwl1k7sDOruzMxzbIwbadVdkNxm9tNxhwoXK3TA==} + + '@nrwl/linter@15.9.7': + resolution: {integrity: sha512-PSbdBodqpbw1jmCWGLB1kxKRM8wpbonhZ3V133aLwb8P9c4q0aN7b3Z9VvtNKSS9eaLxYLu50BGiOahMIf4VXg==} + peerDependencies: + eslint: ^8.0.0 + peerDependenciesMeta: + eslint: + optional: true + + '@nrwl/nx-darwin-arm64@15.9.7': + resolution: {integrity: sha512-aBUgnhlkrgC0vu0fK6eb9Vob7eFnkuknrK+YzTjmLrrZwj7FGNAeyGXSlyo1dVokIzjVKjJg2saZZ0WQbfuCJw==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [darwin] + + '@nrwl/nx-darwin-x64@15.9.7': + resolution: {integrity: sha512-L+elVa34jhGf1cmn38Z0sotQatmLovxoASCIw5r1CBZZeJ5Tg7Y9nOwjRiDixZxNN56hPKXm6xl9EKlVHVeKlg==} + engines: {node: '>= 10'} + cpu: [x64] + os: [darwin] + + '@nrwl/nx-linux-arm-gnueabihf@15.9.7': + resolution: {integrity: sha512-pqmfqqEUGFu6PmmHKyXyUw1Al0Ki8PSaR0+ndgCAb1qrekVDGDfznJfaqxN0JSLeolPD6+PFtLyXNr9ZyPFlFg==} + engines: {node: '>= 10'} + cpu: [arm] + os: [linux] + + '@nrwl/nx-linux-arm64-gnu@15.9.7': + resolution: {integrity: sha512-NYOa/eRrqmM+In5g3M0rrPVIS9Z+q6fvwXJYf/KrjOHqqan/KL+2TOfroA30UhcBrwghZvib7O++7gZ2hzwOnA==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + + '@nrwl/nx-linux-arm64-musl@15.9.7': + resolution: {integrity: sha512-zyStqjEcmbvLbejdTOrLUSEdhnxNtdQXlmOuymznCzYUEGRv+4f7OAepD3yRoR0a/57SSORZmmGQB7XHZoYZJA==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + + '@nrwl/nx-linux-x64-gnu@15.9.7': + resolution: {integrity: sha512-saNK5i2A8pKO3Il+Ejk/KStTApUpWgCxjeUz9G+T8A+QHeDloZYH2c7pU/P3jA9QoNeKwjVO9wYQllPL9loeVg==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + + '@nrwl/nx-linux-x64-musl@15.9.7': + resolution: {integrity: sha512-extIUThYN94m4Vj4iZggt6hhMZWQSukBCo8pp91JHnDcryBg7SnYmnikwtY1ZAFyyRiNFBLCKNIDFGkKkSrZ9Q==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + + '@nrwl/nx-win32-arm64-msvc@15.9.7': + resolution: {integrity: sha512-GSQ54hJ5AAnKZb4KP4cmBnJ1oC4ILxnrG1mekxeM65c1RtWg9NpBwZ8E0gU3xNrTv8ZNsBeKi/9UhXBxhsIh8A==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [win32] + + '@nrwl/nx-win32-x64-msvc@15.9.7': + resolution: {integrity: sha512-x6URof79RPd8AlapVbPefUD3ynJZpmah3tYaYZ9xZRMXojVtEHV8Qh5vysKXQ1rNYJiiB8Ah6evSKWLbAH60tw==} + engines: {node: '>= 10'} + cpu: [x64] + os: [win32] + + '@nrwl/react@15.9.7': + resolution: {integrity: sha512-GFCrbSDNLV/zlc84cb0tYGoV9SO8FQFv0noyeVvL+FzLCv7Ry7KF9gr9CRJxJa6VpuRre9VoiYU0Zq/xQPb27Q==} + + '@nrwl/tao@15.9.7': + resolution: {integrity: sha512-OBnHNvQf3vBH0qh9YnvBQQWyyFZ+PWguF6dJ8+1vyQYlrLVk/XZ8nJ4ukWFb+QfPv/O8VBmqaofaOI9aFC4yTw==} + hasBin: true + + '@nrwl/webpack@15.9.7': + resolution: {integrity: sha512-sFLl+9pgZexFrs1fLO05Bctji7E0kKsr8x7nM4EmB9Tb892JWaGvOCTfoklRoZ+YyI2xpOqAhKiwNcm2X9A9dw==} + + '@nrwl/workspace@15.9.7': + resolution: {integrity: sha512-tOYYReTgUtP66mtbl6rGfs83il6ywb99GIsyCGINWNBVuU3WE44Io0MCvfirDwlxwSIWouDr64qhGqqzTo3Z9g==} + + '@parcel/watcher-android-arm64@2.5.6': + resolution: {integrity: sha512-YQxSS34tPF/6ZG7r/Ih9xy+kP/WwediEUsqmtf0cuCV5TPPKw/PQHRhueUo6JdeFJaqV3pyjm0GdYjZotbRt/A==} + engines: {node: '>= 10.0.0'} + cpu: [arm64] + os: [android] + + '@parcel/watcher-darwin-arm64@2.5.6': + resolution: {integrity: sha512-Z2ZdrnwyXvvvdtRHLmM4knydIdU9adO3D4n/0cVipF3rRiwP+3/sfzpAwA/qKFL6i1ModaabkU7IbpeMBgiVEA==} + engines: {node: '>= 10.0.0'} + cpu: [arm64] + os: [darwin] + + '@parcel/watcher-darwin-x64@2.5.6': + resolution: {integrity: sha512-HgvOf3W9dhithcwOWX9uDZyn1lW9R+7tPZ4sug+NGrGIo4Rk1hAXLEbcH1TQSqxts0NYXXlOWqVpvS1SFS4fRg==} + engines: {node: '>= 10.0.0'} + cpu: [x64] + os: [darwin] + + '@parcel/watcher-freebsd-x64@2.5.6': + resolution: {integrity: sha512-vJVi8yd/qzJxEKHkeemh7w3YAn6RJCtYlE4HPMoVnCpIXEzSrxErBW5SJBgKLbXU3WdIpkjBTeUNtyBVn8TRng==} + engines: {node: '>= 10.0.0'} + cpu: [x64] + os: [freebsd] + + '@parcel/watcher-linux-arm-glibc@2.5.6': + resolution: {integrity: sha512-9JiYfB6h6BgV50CCfasfLf/uvOcJskMSwcdH1PHH9rvS1IrNy8zad6IUVPVUfmXr+u+Km9IxcfMLzgdOudz9EQ==} + engines: {node: '>= 10.0.0'} + cpu: [arm] + os: [linux] + + '@parcel/watcher-linux-arm-musl@2.5.6': + resolution: {integrity: sha512-Ve3gUCG57nuUUSyjBq/MAM0CzArtuIOxsBdQ+ftz6ho8n7s1i9E1Nmk/xmP323r2YL0SONs1EuwqBp2u1k5fxg==} + engines: {node: '>= 10.0.0'} + cpu: [arm] + os: [linux] + + '@parcel/watcher-linux-arm64-glibc@2.5.6': + resolution: {integrity: sha512-f2g/DT3NhGPdBmMWYoxixqYr3v/UXcmLOYy16Bx0TM20Tchduwr4EaCbmxh1321TABqPGDpS8D/ggOTaljijOA==} + engines: {node: '>= 10.0.0'} + cpu: [arm64] + os: [linux] + + '@parcel/watcher-linux-arm64-musl@2.5.6': + resolution: {integrity: sha512-qb6naMDGlbCwdhLj6hgoVKJl2odL34z2sqkC7Z6kzir8b5W65WYDpLB6R06KabvZdgoHI/zxke4b3zR0wAbDTA==} + engines: {node: '>= 10.0.0'} + cpu: [arm64] + os: [linux] + + '@parcel/watcher-linux-x64-glibc@2.5.6': + resolution: {integrity: sha512-kbT5wvNQlx7NaGjzPFu8nVIW1rWqV780O7ZtkjuWaPUgpv2NMFpjYERVi0UYj1msZNyCzGlaCWEtzc+exjMGbQ==} + engines: {node: '>= 10.0.0'} + cpu: [x64] + os: [linux] + + '@parcel/watcher-linux-x64-musl@2.5.6': + resolution: {integrity: sha512-1JRFeC+h7RdXwldHzTsmdtYR/Ku8SylLgTU/reMuqdVD7CtLwf0VR1FqeprZ0eHQkO0vqsbvFLXUmYm/uNKJBg==} + engines: {node: '>= 10.0.0'} + cpu: [x64] + os: [linux] + + '@parcel/watcher-win32-arm64@2.5.6': + resolution: {integrity: sha512-3ukyebjc6eGlw9yRt678DxVF7rjXatWiHvTXqphZLvo7aC5NdEgFufVwjFfY51ijYEWpXbqF5jtrK275z52D4Q==} + engines: {node: '>= 10.0.0'} + cpu: [arm64] + os: [win32] + + '@parcel/watcher-win32-ia32@2.5.6': + resolution: {integrity: sha512-k35yLp1ZMwwee3Ez/pxBi5cf4AoBKYXj00CZ80jUz5h8prpiaQsiRPKQMxoLstNuqe2vR4RNPEAEcjEFzhEz/g==} + engines: {node: '>= 10.0.0'} + cpu: [ia32] + os: [win32] + + '@parcel/watcher-win32-x64@2.5.6': + resolution: {integrity: sha512-hbQlYcCq5dlAX9Qx+kFb0FHue6vbjlf0FrNzSKdYK2APUf7tGfGxQCk2ihEREmbR6ZMc0MVAD5RIX/41gpUzTw==} + engines: {node: '>= 10.0.0'} + cpu: [x64] + os: [win32] + + '@parcel/watcher@2.0.4': + resolution: {integrity: sha512-cTDi+FUDBIUOBKEtj+nhiJ71AZVlkAsQFuGQTun5tV9mwQBQgZvhCzG+URPQc8myeN32yRVZEfVAPCs1RW+Jvg==} + engines: {node: '>= 10.0.0'} + + '@parcel/watcher@2.5.6': + resolution: {integrity: sha512-tmmZ3lQxAe/k/+rNnXQRawJ4NjxO2hqiOLTHvWchtGZULp4RyFeh6aU4XdOYBFe2KE1oShQTv4AblOs2iOrNnQ==} + engines: {node: '>= 10.0.0'} + + '@peculiar/asn1-cms@2.7.0': + resolution: {integrity: sha512-hew63shtzzvBcSHbhm+cyAmKe6AIfinT9hzEqSPjDC6opTTMKmTkQ0gHuN2KsWlvqiKw1S/fS94fhag/FJkioQ==} + + '@peculiar/asn1-csr@2.7.0': + resolution: {integrity: sha512-VVsAyGqErT9D1SY4aEqozThXMVI+ssVRiv2DDeYuvpBKLIgZ3hYs3Ay3u/VSoKq6ESFi9cf6rf3IOOzfwh7oMA==} + + '@peculiar/asn1-ecc@2.7.0': + resolution: {integrity: sha512-n7KEs/Q/wrB415cxy4fHOBhegp4NdJ15fkJPwcB/3/8iNBQC2L/N7SChJPKDJPZGYH0jD4Tg4/0vnHmwghnbKw==} + + '@peculiar/asn1-pfx@2.7.0': + resolution: {integrity: sha512-V/nrlQVmhg7lYAsM7E13UDL5erAwFv6kCIVFqNaMIHSVi7dngcT839JkRTkQBqznMG98l2XjxYk74ZztAohZzA==} + + '@peculiar/asn1-pkcs8@2.7.0': + resolution: {integrity: sha512-9GTl1nE8Mx1kTZ+7QyYatDyKsm34QcWRBFkY1iPvWC3X4Dona5s/tlLiQsx5WzVdZqiMBZNYT0buyw4/vbhnjw==} + + '@peculiar/asn1-pkcs9@2.7.0': + resolution: {integrity: sha512-Bh7m+OuIaSEllPQcSd9OSp93F4ROWH7sbITWV8MI+8dwsjE5111/87VxiWVvYFKyww3vp39geLv9ENqhwWHcew==} + + '@peculiar/asn1-rsa@2.7.0': + resolution: {integrity: sha512-/qvENQrXyTZURjMqSeofHul0JJt2sNSzSwk36pl2olkHbaioMQgrASDZAlHXl0xUlnVbHj0uGgOrBMTb5x2aJQ==} + + '@peculiar/asn1-schema@2.7.0': + resolution: {integrity: sha512-W8ZfWzLmQnrcky+eh3tni4IozMdqBDiHWU0N+vve/UGjMaUs8c0L7A2oEdkBXS8rTpWDpK/aoI3DG/L/hxmxPg==} + + '@peculiar/asn1-x509-attr@2.7.0': + resolution: {integrity: sha512-NS8e7SOgXipkzUPLF/sce7ukpMpWjhxYsH0n6Y+bHYo4TTxOb95Zv7hqwSuL212mj5YxovjdOKQOgH1As3E94w==} + + '@peculiar/asn1-x509@2.7.0': + resolution: {integrity: sha512-mUn9RRrkGDnG4ALfunDmzyRW5dg+sWCj/pfnCCqEHYbkGxEpvUt6iVJv8Yw1cyp6SWZ26ZE5oSmI5SqEaen15g==} + + '@peculiar/utils@2.0.3': + resolution: {integrity: sha512-+oL3HPFRIZ1St2K50lWCXiioIgSoxzz7R1J3uF6neO2yl1sgmpgY6XXJH4BdpoDkMWznQTeYF6oWNDZLCdQ4eQ==} + + '@peculiar/x509@1.14.3': + resolution: {integrity: sha512-C2Xj8FZ0uHWeCXXqX5B4/gVFQmtSkiuOolzAgutjTfseNOHT3pUjljDZsTSxXFGgio54bCzVFqmEOUrIVk8RDA==} + engines: {node: '>=20.0.0'} + + '@phenomnomnominal/tsquery@4.1.1': + resolution: {integrity: sha512-jjMmK1tnZbm1Jq5a7fBliM4gQwjxMU7TFoRNwIyzwlO+eHPRCFv/Nv+H/Gi1jc3WR7QURG8D5d0Tn12YGrUqBQ==} + peerDependencies: + typescript: ^3 || ^4 + + '@phosphor-icons/react@2.1.10': + resolution: {integrity: sha512-vt8Tvq8GLjheAZZYa+YG/pW7HDbov8El/MANW8pOAz4eGxrwhnbfrQZq0Cp4q8zBEu8NIhHdnr+r8thnfRSNYA==} + engines: {node: '>=10'} + peerDependencies: + react: '>= 16.8' + react-dom: '>= 16.8' + + '@playwright/test@1.59.1': + resolution: {integrity: sha512-PG6q63nQg5c9rIi4/Z5lR5IVF7yU5MqmKaPOe0HSc0O2cX1fPi96sUQu5j7eo4gKCkB2AnNGoWt7y4/Xx3Kcqg==} + engines: {node: '>=18'} + hasBin: true + + '@popperjs/core@2.11.8': + resolution: {integrity: sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==} + + '@rc-component/async-validator@5.1.0': + resolution: {integrity: sha512-n4HcR5siNUXRX23nDizbZBQPO0ZM/5oTtmKZ6/eqL0L2bo747cklFdZGRN2f+c9qWGICwDzrhW0H7tE9PptdcA==} + engines: {node: '>=14.x'} + + '@rc-component/cascader@1.14.0': + resolution: {integrity: sha512-Ip9356xwZUR2nbW5PRVGif4B/bDve4pLa/N+PGbvBaTnjbvmN4PFMBGQSmlDlzKP1ovxaYMvwF/dI9lXNLT4iQ==} + peerDependencies: + react: '>=18.0.0' + react-dom: '>=18.0.0' + + '@rc-component/checkbox@2.0.0': + resolution: {integrity: sha512-3CXGPpAR9gsPKeO2N78HAPOzU30UdemD6HGJoWVJOpa6WleaGB5kzZj3v6bdTZab31YuWgY/RxV3VKPctn0DwQ==} + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' + + '@rc-component/collapse@1.2.0': + resolution: {integrity: sha512-ZRYSKSS39qsFx93p26bde7JUZJshsUBEQRlRXPuJYlAiNX0vyYlF5TsAm8JZN3LcF8XvKikdzPbgAtXSbkLUkw==} + peerDependencies: + react: '>=18.0.0' + react-dom: '>=18.0.0' + + '@rc-component/color-picker@3.1.1': + resolution: {integrity: sha512-OHaCHLHszCegdXmIq2ZRIZBN/EtpT6Wm8SG/gpzLATHbVKc/avvuKi+zlOuk05FTWvgaMmpxAko44uRJ3M+2pg==} + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' + + '@rc-component/context@2.0.1': + resolution: {integrity: sha512-HyZbYm47s/YqtP6pKXNMjPEMaukyg7P0qVfgMLzr7YiFNMHbK2fKTAGzms9ykfGHSfyf75nBbgWw+hHkp+VImw==} + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' + + '@rc-component/dialog@1.8.4': + resolution: {integrity: sha512-Ay6PM7phkTkquplG8fWfUGFZ2GTLx9diTl4f0d8Eqxd7W1u1KjE9AQooFQHOHnhZf0Ya3z51+5EKCWHmt/dNEw==} + peerDependencies: + react: '>=18.0.0' + react-dom: '>=18.0.0' + + '@rc-component/drawer@1.4.2': + resolution: {integrity: sha512-1ib+fZEp6FBu+YvcIktm+nCQ+Q+qIpwpoaJH6opGr4ofh2QMq+qdr5DLC4oCf5qf3pcWX9lUWPYX652k4ini8Q==} + peerDependencies: + react: '>=18.0.0' + react-dom: '>=18.0.0' + + '@rc-component/dropdown@1.0.2': + resolution: {integrity: sha512-6PY2ecUSYhDPhkNHHb4wfeAya04WhpmUSKzdR60G+kMNVUCX2vjT/AgTS0Lz0I/K6xrPMJ3enQbwVpeN3sHCgg==} + peerDependencies: + react: '>=16.11.0' + react-dom: '>=16.11.0' + + '@rc-component/form@1.8.1': + resolution: {integrity: sha512-8O7TB55Fi2mWIGvSnwZjk8jFqVNYyKDAswglwGShcbndxqzKz4cHwNtNaLjZlAeRge9wcB0LL8IWsC/Bl18raQ==} + engines: {node: '>=8.x'} + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' + + '@rc-component/image@1.9.0': + resolution: {integrity: sha512-khF7w7xkBH5B1bsBcI1FSUZdkyd1aqpl2eYyILCqCzzQH3XdfehGUaZTnptyaJJfs09/R5hv9jXWyazOMFIClQ==} + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' + + '@rc-component/input-number@1.6.2': + resolution: {integrity: sha512-Gjcq7meZlCOiWN1t1xCC+7/s85humHVokTBI7PJgTfoyw5OWF74y3e6P8PHX104g9+b54jsodFIzyaj6p8LI9w==} + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' + + '@rc-component/input@1.1.2': + resolution: {integrity: sha512-Q61IMR47piUBudgixJ30CciKIy9b1H95qe7GgEKOmSJVJXvFRWJllJfQry9tif+MX2cWFXWJf/RXz4kaCeq/Fg==} + peerDependencies: + react: '>=16.0.0' + react-dom: '>=16.0.0' + + '@rc-component/mentions@1.6.0': + resolution: {integrity: sha512-KIkQNP6habNuTsLhUv0UGEOwG67tlmE7KNIJoQZZNggEZl5lQJTytFDb69sl5CK3TDdISCTjKP3nGEBKgT61CQ==} + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' + + '@rc-component/menu@1.2.0': + resolution: {integrity: sha512-VWwDuhvYHSnTGj4n6bV3ISrLACcPAzdPOq3d0BzkeiM5cve8BEYfvkEhNoM0PLzv51jpcejeyrLXeMVIJ+QJlg==} + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' + + '@rc-component/mini-decimal@1.1.3': + resolution: {integrity: sha512-bk/FJ09fLf+NLODMAFll6CfYrHPBioTedhW6lxDBuuWucJEqFUd4l/D/5JgIi3dina6sYahB8iuPAZTNz2pMxw==} + engines: {node: '>=8.x'} + + '@rc-component/motion@1.3.2': + resolution: {integrity: sha512-itfd+GztzJYAb04Z4RkEub1TbJAfZc2Iuy8p44U44xD1F5+fNYFKI3897ijlbIyfvXkTmMm+KGcjkQQGMHywEQ==} + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' + + '@rc-component/mutate-observer@2.0.1': + resolution: {integrity: sha512-AyarjoLU5YlxuValRi+w8JRH2Z84TBbFO2RoGWz9d8bSu0FqT8DtugH3xC3BV7mUwlmROFauyWuXFuq4IFbH+w==} + engines: {node: '>=8.x'} + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' + + '@rc-component/notification@1.2.0': + resolution: {integrity: sha512-OX3J+zVU7rvoJCikjrfW7qOUp7zlDeFBK2eA3SFbGSkDqo63Sl4Ss8A04kFP+fxHSxMDIS9jYVEZtU1FNCFuBA==} + engines: {node: '>=8.x'} + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' + + '@rc-component/overflow@1.0.1': + resolution: {integrity: sha512-syfmgAABaHCnCDzPwHZ/2tuvIcpOO3jefYZMmfkN+pmo8HKTzsfhS57vxo4ksPdN0By+uWVJhJWNFozNBxi2eA==} + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' + + '@rc-component/pagination@1.2.0': + resolution: {integrity: sha512-YcpUFE8dMLfSo6OARJlK6DbHHvrxz7pMGPGmC/caZSJJz6HRKHC1RPP001PRHCvG9Z/veD039uOQmazVuLJzlw==} + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' + + '@rc-component/picker@1.9.1': + resolution: {integrity: sha512-9FBYYsvH3HMLICaPDA/1Th5FLaDkFa7qAtangIdlhKb3ZALaR745e9PsOhheJb6asS4QXc12ffiAcjdkZ4C5/g==} + engines: {node: '>=12.x'} + peerDependencies: + date-fns: '>= 2.x' + dayjs: '>= 1.x' + luxon: '>= 3.x' + moment: '>= 2.x' + react: '>=16.9.0' + react-dom: '>=16.9.0' + peerDependenciesMeta: + date-fns: + optional: true + dayjs: + optional: true + luxon: + optional: true + moment: + optional: true + + '@rc-component/portal@2.2.0': + resolution: {integrity: sha512-oc6FlA+uXCMiwArHsJyHcIkX4q6uKyndrPol2eWX8YPkAnztHOPsFIRtmWG4BMlGE5h7YIRE3NiaJ5VS8Lb1QQ==} + engines: {node: '>=12.x'} + peerDependencies: + react: '>=18.0.0' + react-dom: '>=18.0.0' + + '@rc-component/progress@1.0.2': + resolution: {integrity: sha512-WZUnH9eGxH1+xodZKqdrHke59uyGZSWgj5HBM5Kwk5BrTMuAORO7VJ2IP5Qbm9aH3n9x3IcesqHHR0NWPBC7fQ==} + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' + + '@rc-component/qrcode@1.1.1': + resolution: {integrity: sha512-LfLGNymzKdUPjXUbRP+xOhIWY4jQ+YMj5MmWAcgcAq1Ij8XP7tRmAXqyuv96XvLUBE/5cA8hLFl9eO1JQMujrA==} + engines: {node: '>=8.x'} + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' + + '@rc-component/rate@1.0.1': + resolution: {integrity: sha512-bkXxeBqDpl5IOC7yL7GcSYjQx9G8H+6kLYQnNZWeBYq2OYIv1MONd6mqKTjnnJYpV0cQIU2z3atdW0j1kttpTw==} + engines: {node: '>=8.x'} + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' + + '@rc-component/resize-observer@1.1.2': + resolution: {integrity: sha512-t/Bb0W8uvL4PYKAB3YcChC+DlHh0Wt5kM7q/J+0qpVEUMLe7Hk5zuvc9km0hMnTFPSx5Z7Wu/fzCLN6erVLE8Q==} + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' + + '@rc-component/segmented@1.3.0': + resolution: {integrity: sha512-5J/bJ01mbDnoA6P/FW8SxUvKn+OgUSTZJPzCNnTBntG50tzoP7DydGhqxp7ggZXZls7me3mc2EQDXakU3iTVFg==} + peerDependencies: + react: '>=16.0.0' + react-dom: '>=16.0.0' + + '@rc-component/select@1.6.15': + resolution: {integrity: sha512-SyVCWnqxCQZZcQvQJ/CxSjx2bGma6ds/HtnpkIfZVnt6RoEgbqUmHgD6vrzNarNXwbLXerwVzWwq8F3d1sst7g==} + engines: {node: '>=8.x'} + peerDependencies: + react: '*' + react-dom: '*' + + '@rc-component/slider@1.0.1': + resolution: {integrity: sha512-uDhEPU1z3WDfCJhaL9jfd2ha/Eqpdfxsn0Zb0Xcq1NGQAman0TWaR37OWp2vVXEOdV2y0njSILTMpTfPV1454g==} + engines: {node: '>=8.x'} + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' + + '@rc-component/steps@1.2.2': + resolution: {integrity: sha512-/yVIZ00gDYYPHSY0JP+M+s3ZvuXLu2f9rEjQqiUDs7EcYsUYrpJ/1bLj9aI9R7MBR3fu/NGh6RM9u2qGfqp+Nw==} + engines: {node: '>=8.x'} + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' + + '@rc-component/switch@1.0.3': + resolution: {integrity: sha512-Jgi+EbOBquje/XNdofr7xbJQZPYJP+BlPfR0h+WN4zFkdtB2EWqEfvkXJWeipflwjWip0/17rNbxEAqs8hVHfw==} + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' + + '@rc-component/table@1.9.1': + resolution: {integrity: sha512-FVI5ZS/GdB3BcgexfCYKi3iHhZS3Fr59EtsxORszYGrfpH1eWr33eDNSYkVfLI6tfJ7vftJDd9D5apfFWqkdJg==} + engines: {node: '>=8.x'} + peerDependencies: + react: '>=18.0.0' + react-dom: '>=18.0.0' + + '@rc-component/tabs@1.7.0': + resolution: {integrity: sha512-J48cs2iBi7Ho3nptBxxIqizEliUC+ExE23faspUQKGQ550vaBlv3aGF8Epv/UB1vFWeoJDTW/dNzgIU0Qj5i/w==} + engines: {node: '>=8.x'} + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' + + '@rc-component/textarea@1.1.2': + resolution: {integrity: sha512-9rMUEODWZDMovfScIEHXWlVZuPljZ2pd1LKNjslJVitn4SldEzq5vO1CL3yy3Dnib6zZal2r2DPtjy84VVpF6A==} + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' + + '@rc-component/tooltip@1.4.0': + resolution: {integrity: sha512-8Rx5DCctIlLI4raR0I0xHjVTf1aF48+gKCNeAAo5bmF5VoR5YED+A/XEqzXv9KKqrJDRcd3Wndpxh2hyzrTtSg==} + peerDependencies: + react: '>=18.0.0' + react-dom: '>=18.0.0' + + '@rc-component/tour@2.3.0': + resolution: {integrity: sha512-K04K9r32kUC+auBSQfr+Fss4SpSIS9JGe56oq/ALAX0p+i2ylYOI1MgR83yBY7v96eO6ZFXcM/igCQmubps0Ow==} + engines: {node: '>=8.x'} + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' + + '@rc-component/tree-select@1.8.0': + resolution: {integrity: sha512-iYsPq3nuLYvGqdvFAW+l+I9ASRIOVbMXyA8FGZg2lGym/GwkaWeJGzI4eJ7c9IOEhRj0oyfIN4S92Fl3J05mjQ==} + peerDependencies: + react: '*' + react-dom: '*' + + '@rc-component/tree@1.2.4': + resolution: {integrity: sha512-5Gli43+m4R7NhpYYz3Z61I6LOw9yI6CNChxgVtvrO6xB1qML7iE6QMLVMB3+FTjo2yF6uFdAHtqWPECz/zbX5w==} + engines: {node: '>=10.x'} + peerDependencies: + react: '*' + react-dom: '*' + + '@rc-component/trigger@3.9.0': + resolution: {integrity: sha512-X8btpwfrT27AgrZVOz4swclhEHTZcqaHeQMXXBgveagOiakTa36uObXbdwerXffgV8G9dH1fAAE0DHtVQs8EHg==} + engines: {node: '>=8.x'} + peerDependencies: + react: '>=18.0.0' + react-dom: '>=18.0.0' + + '@rc-component/upload@1.1.0': + resolution: {integrity: sha512-LIBV90mAnUE6VK5N4QvForoxZc4XqEYZimcp7fk+lkE4XwHHyJWxpIXQQwMU8hJM+YwBbsoZkGksL1sISWHQxw==} + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' + + '@rc-component/util@1.10.1': + resolution: {integrity: sha512-q++9S6rUa5Idb/xIBNz6jtvumw5+O5YV5V0g4iK9mn9jWs4oGJheE3ZN1kAnE723AXyaD8v95yeOASmdk8Jnng==} + peerDependencies: + react: '>=18.0.0' + react-dom: '>=18.0.0' + + '@rc-component/virtual-list@1.0.2': + resolution: {integrity: sha512-uvTol/mH74FYsn5loDGJxo+7kjkO4i+y4j87Re1pxJBs0FaeuMuLRzQRGaXwnMcV1CxpZLi2Z56Rerj2M00fjQ==} + engines: {node: '>=8.x'} + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' + + '@remix-run/router@1.23.2': + resolution: {integrity: sha512-Ic6m2U/rMjTkhERIa/0ZtXJP17QUi2CbWE7cqx4J58M8aA3QTfW+2UlQ4psvTX9IO1RfNVhK3pcpdjej7L+t2w==} + engines: {node: '>=14.0.0'} + + '@rolldown/pluginutils@1.0.0-beta.19': + resolution: {integrity: sha512-3FL3mnMbPu0muGOCaKAhhFEYmqv9eTfPSJRJmANrCwtgK8VuxpsZDGK+m0LYAGoyO8+0j5uRe4PeyPDK1yA/hA==} + + '@rollup/rollup-android-arm-eabi@4.60.4': + resolution: {integrity: sha512-F5QXMSiFebS9hKZj02XhWLLnRpJ3B3AROP0tWbFBSj+6kCbg5m9j5JoHKd4mmSVy5mS/IMQloYgYxCuJC0fxEQ==} + cpu: [arm] + os: [android] + + '@rollup/rollup-android-arm64@4.60.4': + resolution: {integrity: sha512-GxxTKApUpzRhof7poWvCJHRF51C67u1R7D6DiluBE8wKU1u5GWE8t+v81JvJYtbawoBFX1hLv5Ei4eVjkWokaw==} + cpu: [arm64] + os: [android] + + '@rollup/rollup-darwin-arm64@4.60.4': + resolution: {integrity: sha512-tua0TaJxMOB1R0V0RS1jFZ/RpURFDJIOR2A6jWwQeawuFyS4gBW+rntLRaQd0EQ4bd6Vp44Z2rXW+YYDBsj6IA==} + cpu: [arm64] + os: [darwin] + + '@rollup/rollup-darwin-x64@4.60.4': + resolution: {integrity: sha512-CSKq7MsP+5PFIcydhAiR1K0UhEI1A2jWXVKHPCBZ151yOutENwvnPocgVHkivu2kviURtCEB6zUQw0vs8RrhMg==} + cpu: [x64] + os: [darwin] + + '@rollup/rollup-freebsd-arm64@4.60.4': + resolution: {integrity: sha512-+O8OkVdyvXMtJEciu2wS/pzm1IxntEEQx3z5TAVy4l32G0etZn+RsA48ARRrFm6Ri8fvqPQfgrvNxSjKAbnd3g==} + cpu: [arm64] + os: [freebsd] + + '@rollup/rollup-freebsd-x64@4.60.4': + resolution: {integrity: sha512-Iw3oMskH3AfNuhU0MSN7vNbdi4me/NiYo2azqPz/Le16zHSa+3RRmliCMWWQmh4lcndccU40xcJuTYJZxNo/lw==} + cpu: [x64] + os: [freebsd] + + '@rollup/rollup-linux-arm-gnueabihf@4.60.4': + resolution: {integrity: sha512-EIPRXTVQpHyF8WOo219AD2yEltPehLTcTMz2fn6JsatLYSzQf00hj3rulF+yauOlF9/FtM2WpkT/hJh/KJFGhA==} + cpu: [arm] + os: [linux] + + '@rollup/rollup-linux-arm-musleabihf@4.60.4': + resolution: {integrity: sha512-J3Yh9PzzF1Ovah2At+lHiGQdsYgArxBbXv/zHfSyaiFQEqvNv7DcW98pCrmdjCZBrqBiKrKKe2V+aaSGWuBe/w==} + cpu: [arm] + os: [linux] + + '@rollup/rollup-linux-arm64-gnu@4.60.4': + resolution: {integrity: sha512-BFDEZMYfUvLn37ONE1yMBojPxnMlTFsdyNoqncT0qFq1mAfllL+ATMMJd8TeuVMiX84s1KbcxcZbXInmcO2mRg==} + cpu: [arm64] + os: [linux] + + '@rollup/rollup-linux-arm64-musl@4.60.4': + resolution: {integrity: sha512-pc9EYOSlOgdQ2uPl1o9PF6/kLSgaUosia7gOuS8mB69IxJvlclko1MECXysjs5ryez1/5zjYqx3+xYU0TU6R1A==} + cpu: [arm64] + os: [linux] + + '@rollup/rollup-linux-loong64-gnu@4.60.4': + resolution: {integrity: sha512-NxnomyxYerDh5n4iLrNa+sH+Z+U4BMEE46V2PgQ/hoB909i8gV1M5wPojWg9fk1jWpO3IQnOs20K4wyZuFLEFQ==} + cpu: [loong64] + os: [linux] + + '@rollup/rollup-linux-loong64-musl@4.60.4': + resolution: {integrity: sha512-nbJnQ8a3z1mtmrwImCYhc6BGpThAyYVRQxw9uKSKG4wR6aAYno9sVjJ0zaZcW9BPJX1GbrDPf+SvdWjgTuDmnw==} + cpu: [loong64] + os: [linux] + + '@rollup/rollup-linux-ppc64-gnu@4.60.4': + resolution: {integrity: sha512-2EU6acNrQLd8tYvo/LXW535wupT3m6fo7HKo6lr7ktQoItxTyOL1ZCR/GfGCuXl2vR+zmfI6eRXkSemafv+iVg==} + cpu: [ppc64] + os: [linux] + + '@rollup/rollup-linux-ppc64-musl@4.60.4': + resolution: {integrity: sha512-WeBtoMuaMxiiIrO2IYP3xs6GMWkJP2C0EoT8beTLkUPmzV1i/UcOSVw1d5r9KBODtHKilG5yFxsGRnBbK3wJ4A==} + cpu: [ppc64] + os: [linux] + + '@rollup/rollup-linux-riscv64-gnu@4.60.4': + resolution: {integrity: sha512-FJHFfqpKUI3A10WrWKiFbBZ7yVbGT4q4B5o1qKFFojqpaYoh9LrQgqWCmmcxQzVSXYtyB5bzkXrYzlHTs21MYA==} + cpu: [riscv64] + os: [linux] + + '@rollup/rollup-linux-riscv64-musl@4.60.4': + resolution: {integrity: sha512-mcEl6CUT5IAUmQf1m9FYSmVqCJlpQ8r8eyftFUHG8i9OhY7BkBXSUdnLH5DOf0wCOjcP9v/QO93zpmF1SptCCw==} + cpu: [riscv64] + os: [linux] + + '@rollup/rollup-linux-s390x-gnu@4.60.4': + resolution: {integrity: sha512-ynt3JxVd2w2buzoKDWIyiV1pJW93xlQic1THVLXilz429oijRpSHivZAgp65KBu+cMcgf1eVVjdnTLvPxgCuoQ==} + cpu: [s390x] + os: [linux] + + '@rollup/rollup-linux-x64-gnu@4.60.4': + resolution: {integrity: sha512-Boiz5+MsaROEWDf+GGEwF8VMHGhlUoQMtIPjOgA5fv4osupqTVnJteQNKJwUcnUog2G55jYXH7KZFFiJe0TEzQ==} + cpu: [x64] + os: [linux] + + '@rollup/rollup-linux-x64-musl@4.60.4': + resolution: {integrity: sha512-+qfSY27qIrFfI/Hom04KYFw3GKZSGU4lXus51wsb5EuySfFlWRwjkKWoE9emgRw/ukoT4Udsj4W/+xxG8VbPKg==} + cpu: [x64] + os: [linux] + + '@rollup/rollup-openbsd-x64@4.60.4': + resolution: {integrity: sha512-VpTfOPHgVXEBeeR8hZ2O0F3aSso+JDWqTWmTmzcQKted54IAdUVbxE+j/MVxUsKa8L20HJhv3vUezVPoquqWjA==} + cpu: [x64] + os: [openbsd] + + '@rollup/rollup-openharmony-arm64@4.60.4': + resolution: {integrity: sha512-IPOsh5aRYuLv/nkU51X10Bf75Bsf6+gZdx1X+QP5QM6lIJFHHqbHLG0uJn/hWthzo13UAc2umiUorqZy3axoZg==} + cpu: [arm64] + os: [openharmony] + + '@rollup/rollup-win32-arm64-msvc@4.60.4': + resolution: {integrity: sha512-4QzE9E81OohJ/HKzHhsqU+zcYYojVOXlFMs1DdyMT6qXl/niOH7AVElmmEdUNHHS/oRkc++d5k6Vy85zFs0DEw==} + cpu: [arm64] + os: [win32] + + '@rollup/rollup-win32-ia32-msvc@4.60.4': + resolution: {integrity: sha512-zTPgT1YuHHcd+Tmx7h8aml0FWFVelV5N54oHow9SLj+GfoDy/huQ+UV396N/C7KpMDMiPspRktzM1/0r1usYEA==} + cpu: [ia32] + os: [win32] + + '@rollup/rollup-win32-x64-gnu@4.60.4': + resolution: {integrity: sha512-DRS4G7mi9lJxqEDezIkKCaUIKCrLUUDCUaCsTPCi/rtqaC6D/jjwslMQyiDU50Ka0JKpeXeRBFBAXwArY52vBw==} + cpu: [x64] + os: [win32] + + '@rollup/rollup-win32-x64-msvc@4.60.4': + resolution: {integrity: sha512-QVTUovf40zgTqlFVrKA1uXMVvU2QWEFWfAH8Wdc48IxLvrJMQVMBRjuQyUpzZCDkakImib9eVazbWlC6ksWtJw==} + cpu: [x64] + os: [win32] + + '@sinclair/typebox@0.34.37': + resolution: {integrity: sha512-2TRuQVgQYfy+EzHRTIvkhv2ADEouJ2xNS/Vq+W5EuuewBdOrvATvljZTxHWZSTYr2sTjTHpGvucaGAt67S2akw==} + + '@svgr/babel-plugin-add-jsx-attribute@6.5.1': + resolution: {integrity: sha512-9PYGcXrAxitycIjRmZB+Q0JaN07GZIWaTBIGQzfaZv+qr1n8X1XUEJ5rZ/vx6OVD9RRYlrNnXWExQXcmZeD/BQ==} + engines: {node: '>=10'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@svgr/babel-plugin-remove-jsx-attribute@8.0.0': + resolution: {integrity: sha512-BcCkm/STipKvbCl6b7QFrMh/vx00vIP63k2eM66MfHJzPr6O2U0jYEViXkHJWqXqQYjdeA9cuCl5KWmlwjDvbA==} + engines: {node: '>=14'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@svgr/babel-plugin-remove-jsx-empty-expression@8.0.0': + resolution: {integrity: sha512-5BcGCBfBxB5+XSDSWnhTThfI9jcO5f0Ai2V24gZpG+wXF14BzwxxdDb4g6trdOux0rhibGs385BeFMSmxtS3uA==} + engines: {node: '>=14'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@svgr/babel-plugin-replace-jsx-attribute-value@6.5.1': + resolution: {integrity: sha512-8DPaVVE3fd5JKuIC29dqyMB54sA6mfgki2H2+swh+zNJoynC8pMPzOkidqHOSc6Wj032fhl8Z0TVn1GiPpAiJg==} + engines: {node: '>=10'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@svgr/babel-plugin-svg-dynamic-title@6.5.1': + resolution: {integrity: sha512-FwOEi0Il72iAzlkaHrlemVurgSQRDFbk0OC8dSvD5fSBPHltNh7JtLsxmZUhjYBZo2PpcU/RJvvi6Q0l7O7ogw==} + engines: {node: '>=10'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@svgr/babel-plugin-svg-em-dimensions@6.5.1': + resolution: {integrity: sha512-gWGsiwjb4tw+ITOJ86ndY/DZZ6cuXMNE/SjcDRg+HLuCmwpcjOktwRF9WgAiycTqJD/QXqL2f8IzE2Rzh7aVXA==} + engines: {node: '>=10'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@svgr/babel-plugin-transform-react-native-svg@6.5.1': + resolution: {integrity: sha512-2jT3nTayyYP7kI6aGutkyfJ7UMGtuguD72OjeGLwVNyfPRBD8zQthlvL+fAbAKk5n9ZNcvFkp/b1lZ7VsYqVJg==} + engines: {node: '>=10'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@svgr/babel-plugin-transform-svg-component@6.5.1': + resolution: {integrity: sha512-a1p6LF5Jt33O3rZoVRBqdxL350oge54iZWHNI6LJB5tQ7EelvD/Mb1mfBiZNAan0dt4i3VArkFRjA4iObuNykQ==} + engines: {node: '>=12'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@svgr/babel-preset@6.5.1': + resolution: {integrity: sha512-6127fvO/FF2oi5EzSQOAjo1LE3OtNVh11R+/8FXa+mHx1ptAaS4cknIjnUA7e6j6fwGGJ17NzaTJFUwOV2zwCw==} + engines: {node: '>=10'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@svgr/core@6.5.1': + resolution: {integrity: sha512-/xdLSWxK5QkqG524ONSjvg3V/FkNyCv538OIBdQqPNaAta3AsXj/Bd2FbvR87yMbXO2hFSWiAe/Q6IkVPDw+mw==} + engines: {node: '>=10'} + + '@svgr/hast-util-to-babel-ast@6.5.1': + resolution: {integrity: sha512-1hnUxxjd83EAxbL4a0JDJoD3Dao3hmjvyvyEV8PzWmLK3B9m9NPlW7GKjFyoWE8nM7HnXzPcmmSyOW8yOddSXw==} + engines: {node: '>=10'} + + '@svgr/plugin-jsx@6.5.1': + resolution: {integrity: sha512-+UdQxI3jgtSjCykNSlEMuy1jSRQlGC7pqBCPvkG/2dATdWo082zHTTK3uhnAju2/6XpE6B5mZ3z4Z8Ns01S8Gw==} + engines: {node: '>=10'} + peerDependencies: + '@svgr/core': ^6.0.0 + + '@svgr/plugin-svgo@6.5.1': + resolution: {integrity: sha512-omvZKf8ixP9z6GWgwbtmP9qQMPX4ODXi+wzbVZgomNFsUIlHA1sf4fThdwTWSsZGgvGAG6yE+b/F5gWUkcZ/iQ==} + engines: {node: '>=10'} + peerDependencies: + '@svgr/core': '*' + + '@svgr/webpack@6.5.1': + resolution: {integrity: sha512-cQ/AsnBkXPkEK8cLbv4Dm7JGXq2XrumKnL1dRpJD9rIO2fTIlJI9a1uCciYG1F2aUsox/hJQyNGbt3soDxSRkA==} + engines: {node: '>=10'} + + '@swc/helpers@0.5.15': + resolution: {integrity: sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==} + + '@tabler/icons-react@3.43.0': + resolution: {integrity: sha512-rXUuCQEeRbEk3lJxs3gwzdtaaITSwc/JUbp+AkqsGff5uBpzZw7eKPDk53xKoKLyjrbj82Ai4GuVG0kO89Jf5g==} + peerDependencies: + react: '>= 16' + + '@tabler/icons@3.43.0': + resolution: {integrity: sha512-qXwS17Op9jqr3Asvu31fejyw8+OnRDKH7oR8nQXyUgW1pI44ET8OKG9kssy+XIvvAIyej6gZdGmviNUn1VMfPw==} + + '@testing-library/dom@8.20.1': + resolution: {integrity: sha512-/DiOQ5xBxgdYRC8LNk7U+RWat0S3qRLeIw3ZIkMQ9kkVlRmwD/Eg8k8CqIpD6GW7u20JIUOfMKbxtiLutpjQ4g==} + engines: {node: '>=12'} + + '@testing-library/jest-dom@5.17.0': + resolution: {integrity: sha512-ynmNeT7asXyH3aSVv4vvX4Rb+0qjOhdNHnO/3vuZNqPmhDpV/+rCSGwQ7bLcmU2cJ4dvoheIO85LQj0IbJHEtg==} + engines: {node: '>=8', npm: '>=6', yarn: '>=1'} + + '@testing-library/react@13.4.0': + resolution: {integrity: sha512-sXOGON+WNTh3MLE9rve97ftaZukN3oNf2KjDy7YTx6hcTO2uuLHuCGynMDhFwGw/jYf4OJ2Qk0i4i79qMNNkyw==} + engines: {node: '>=12'} + peerDependencies: + react: ^18.0.0 + react-dom: ^18.0.0 + + '@testing-library/user-event@13.5.0': + resolution: {integrity: sha512-5Kwtbo3Y/NowpkbRuSepbyMFkZmHgD+vPzYB/RJ4oxt5Gj/avFFBYjhw27cqSVPVw/3a67NK1PbiIr9k4Gwmdg==} + engines: {node: '>=10', npm: '>=6'} + peerDependencies: + '@testing-library/dom': '>=7.21.4' + + '@tsconfig/node10@1.0.12': + resolution: {integrity: sha512-UCYBaeFvM11aU2y3YPZ//O5Rhj+xKyzy7mvcIoAjASbigy8mHMryP5cK7dgjlz2hWxh1g5pLw084E0a/wlUSFQ==} + + '@tsconfig/node12@1.0.11': + resolution: {integrity: sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==} + + '@tsconfig/node14@1.0.3': + resolution: {integrity: sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==} + + '@tsconfig/node16@1.0.4': + resolution: {integrity: sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==} + + '@types/aria-query@5.0.4': + resolution: {integrity: sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==} + + '@types/babel__core@7.20.5': + resolution: {integrity: sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==} + + '@types/babel__generator@7.27.0': + resolution: {integrity: sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==} + + '@types/babel__template@7.4.4': + resolution: {integrity: sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==} + + '@types/babel__traverse@7.20.7': + resolution: {integrity: sha512-dkO5fhS7+/oos4ciWxyEyjWe48zmG6wbCheo/G2ZnHx4fs3EU6YC6UM8rk56gAjNJ9P3MTH2jo5jb92/K6wbng==} + + '@types/body-parser@1.19.6': + resolution: {integrity: sha512-HLFeCYgz89uk22N5Qg3dvGvsv46B8GLvKKo1zKG4NybA8U2DiEO3w9lqGg29t/tfLRJpJ6iQxnVw4OnB7MoM9g==} + + '@types/bonjour@3.5.13': + resolution: {integrity: sha512-z9fJ5Im06zvUL548KvYNecEVlA7cVDkGUi6kZusb04mpyEFKCIZJvloCcmpmLaIahDpOQGHaHmG6imtPMmPXGQ==} + + '@types/connect-history-api-fallback@1.5.4': + resolution: {integrity: sha512-n6Cr2xS1h4uAulPRdlw6Jl6s1oG8KrVilPN2yUITEs+K48EzMJJ3W1xy8K5eWuFvjp3R74AOIGSmp2UfBJ8HFw==} + + '@types/connect@3.4.38': + resolution: {integrity: sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==} + + '@types/estree@1.0.8': + resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} + + '@types/express-serve-static-core@4.19.8': + resolution: {integrity: sha512-02S5fmqeoKzVZCHPZid4b8JH2eM5HzQLZWN2FohQEy/0eXTq8VXZfSN6Pcr3F6N9R/vNrj7cpgbhjie6m/1tCA==} + + '@types/express@4.17.25': + resolution: {integrity: sha512-dVd04UKsfpINUnK0yBoYHDF3xu7xVH4BuDotC/xGuycx4CgbP48X/KF/586bcObxT0HENHXEU8Nqtu6NR+eKhw==} + + '@types/html-minifier-terser@6.1.0': + resolution: {integrity: sha512-oh/6byDPnL1zeNXFrDXFLyZjkr1MsBG667IM792caf1L2UPOOMf65NFzjUH/ltyfwjAGfs1rsX1eftK0jC/KIg==} + + '@types/http-errors@2.0.5': + resolution: {integrity: sha512-r8Tayk8HJnX0FztbZN7oVqGccWgw98T/0neJphO91KkmOzug1KkofZURD4UaD5uH8AqcFLfdPErnBod0u71/qg==} + + '@types/http-proxy@1.17.17': + resolution: {integrity: sha512-ED6LB+Z1AVylNTu7hdzuBqOgMnvG/ld6wGCG8wFnAzKX5uyW2K3WD52v0gnLCTK/VLpXtKckgWuyScYK6cSPaw==} + + '@types/istanbul-lib-coverage@2.0.6': + resolution: {integrity: sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==} + + '@types/istanbul-lib-report@3.0.3': + resolution: {integrity: sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==} + + '@types/istanbul-reports@3.0.4': + resolution: {integrity: sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==} + + '@types/jest@30.0.0': + resolution: {integrity: sha512-XTYugzhuwqWjws0CVz8QpM36+T+Dz5mTEBKhNs/esGLnCIlGdRy+Dq78NRjd7ls7r8BC8ZRMOrKlkO1hU0JOwA==} + + '@types/json-schema@7.0.15': + resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} + + '@types/lodash.mergewith@4.6.9': + resolution: {integrity: sha512-fgkoCAOF47K7sxrQ7Mlud2TH023itugZs2bUg8h/KzT+BnZNrR2jAOmaokbLunHNnobXVWOezAeNn/lZqwxkcw==} + + '@types/lodash@4.17.24': + resolution: {integrity: sha512-gIW7lQLZbue7lRSWEFql49QJJWThrTFFeIMJdp3eH4tKoxm1OvEPg02rm4wCCSHS0cL3/Fizimb35b7k8atwsQ==} + + '@types/mime@1.3.5': + resolution: {integrity: sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==} + + '@types/node@12.20.55': + resolution: {integrity: sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ==} + + '@types/node@24.0.13': + resolution: {integrity: sha512-Qm9OYVOFHFYg3wJoTSrz80hoec5Lia/dPp84do3X7dZvLikQvM1YpmvTBEdIr/e+U8HTkFjLHLnl78K/qjf+jQ==} + + '@types/parse-json@4.0.2': + resolution: {integrity: sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==} + + '@types/prop-types@15.7.15': + resolution: {integrity: sha512-F6bEyamV9jKGAFBEmlQnesRPGOQqS2+Uwi0Em15xenOxHaf2hv6L8YCVn3rPdPJOiJfPiCnLIRyvwVaqMY3MIw==} + + '@types/qs@6.15.0': + resolution: {integrity: sha512-JawvT8iBVWpzTrz3EGw9BTQFg3BQNmwERdKE22vlTxawwtbyUSlMppvZYKLZzB5zgACXdXxbD3m1bXaMqP/9ow==} + + '@types/range-parser@1.2.7': + resolution: {integrity: sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==} + + '@types/react-dom@18.3.7': + resolution: {integrity: sha512-MEe3UeoENYVFXzoXEWsvcpg6ZvlrFNlOQ7EOsvhI3CfAXwzPfO8Qwuxd40nepsYKqyyVQnTdEfv68q91yLcKrQ==} + peerDependencies: + '@types/react': ^18.0.0 + + '@types/react-reconciler@0.26.7': + resolution: {integrity: sha512-mBDYl8x+oyPX/VBb3E638N0B7xG+SPk/EAMcVPeexqus/5aTpTphQi0curhhshOqRrc9t6OPoJfEUkbymse/lQ==} + + '@types/react-transition-group@4.4.12': + resolution: {integrity: sha512-8TV6R3h2j7a91c+1DXdJi3Syo69zzIZbz7Lg5tORM5LEJG7X/E6a1V3drRyBRZq7/utz7A+c4OgYLiLcYGHG6w==} + peerDependencies: + '@types/react': '*' + + '@types/react@18.3.23': + resolution: {integrity: sha512-/LDXMQh55EzZQ0uVAZmKKhfENivEvWz6E+EYzh+/MCjMhNsotd+ZHhBGIjFDTi6+fz0OhQQQLbTgdQIxxCsC0w==} + + '@types/react@19.1.8': + resolution: {integrity: sha512-AwAfQ2Wa5bCx9WP8nZL2uMZWod7J7/JSplxbTmBQ5ms6QpqNYm672H0Vu9ZVKVngQ+ii4R/byguVEUZQyeg44g==} + + '@types/retry@0.12.2': + resolution: {integrity: sha512-XISRgDJ2Tc5q4TRqvgJtzsRkFYNJzZrhTdtMoGVBttwzzQJkPnS3WWTFc7kuDRoPtPakl+T+OfdEUjYJj7Jbow==} + + '@types/semver@7.7.1': + resolution: {integrity: sha512-FmgJfu+MOcQ370SD0ev7EI8TlCAfKYU+B4m5T3yXc1CiRN94g/SZPtsCkk506aUDtlMnFZvasDwHHUcZUEaYuA==} + + '@types/send@0.17.6': + resolution: {integrity: sha512-Uqt8rPBE8SY0RK8JB1EzVOIZ32uqy8HwdxCnoCOsYrvnswqmFZ/k+9Ikidlk/ImhsdvBsloHbAlewb2IEBV/Og==} + + '@types/send@1.2.1': + resolution: {integrity: sha512-arsCikDvlU99zl1g69TcAB3mzZPpxgw0UQnaHeC1Nwb015xp8bknZv5rIfri9xTOcMuaVgvabfIRA7PSZVuZIQ==} + + '@types/serve-index@1.9.4': + resolution: {integrity: sha512-qLpGZ/c2fhSs5gnYsQxtDEq3Oy8SXPClIXkW5ghvAvsNuVSA8k+gCONcUCS/UjLEYvYps+e8uBtfgXgvhwfNug==} + + '@types/serve-static@1.15.10': + resolution: {integrity: sha512-tRs1dB+g8Itk72rlSI2ZrW6vZg0YrLI81iQSTkMmOqnqCaNr/8Ek4VwWcN5vZgCYWbg/JJSGBlUaYGAOP73qBw==} + + '@types/sockjs@0.3.36': + resolution: {integrity: sha512-MK9V6NzAS1+Ud7JV9lJLFqW85VbC9dq3LmwZCuBe4wBDgKC0Kj/jd8Xl+nSviU+Qc3+m7umHHyHg//2KSa0a0Q==} + + '@types/stack-utils@2.0.3': + resolution: {integrity: sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==} + + '@types/testing-library__jest-dom@5.14.9': + resolution: {integrity: sha512-FSYhIjFlfOpGSRyVoMBMuS3ws5ehFQODymf3vlI7U1K8c7PHwWwFY7VREfmsuzHSOnoKs/9/Y983ayOs7eRzqw==} + + '@types/ws@8.18.1': + resolution: {integrity: sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==} + + '@types/yargs-parser@21.0.3': + resolution: {integrity: sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==} + + '@types/yargs@17.0.33': + resolution: {integrity: sha512-WpxBCKWPLr4xSsHgz511rFJAM+wS28w2zEO1QDNY5zM/S8ok70NNfztH0xwhqKyaK0OHCbN98LDAZuy1ctxDkA==} + + '@typescript-eslint/eslint-plugin@5.36.1': + resolution: {integrity: sha512-iC40UK8q1tMepSDwiLbTbMXKDxzNy+4TfPWgIL661Ym0sD42vRcQU93IsZIrmi+x292DBr60UI/gSwfdVYexCA==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + '@typescript-eslint/parser': ^5.0.0 + eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + + '@typescript-eslint/parser@5.36.1': + resolution: {integrity: sha512-/IsgNGOkBi7CuDfUbwt1eOqUXF9WGVBW9dwEe1pi+L32XrTsZIgmDFIi2RxjzsvB/8i+MIf5JIoTEH8LOZ368A==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + + '@typescript-eslint/scope-manager@5.36.1': + resolution: {integrity: sha512-pGC2SH3/tXdu9IH3ItoqciD3f3RRGCh7hb9zPdN2Drsr341zgd6VbhP5OHQO/reUqihNltfPpMpTNihFMarP2w==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + '@typescript-eslint/scope-manager@5.62.0': + resolution: {integrity: sha512-VXuvVvZeQCQb5Zgf4HAxc04q5j+WrNAtNh9OwCsCgpKqESMTu3tF/jhZ3xG6T4NZwWl65Bg8KuS2uEvhSfLl0w==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + '@typescript-eslint/type-utils@5.36.1': + resolution: {integrity: sha512-xfZhfmoQT6m3lmlqDvDzv9TiCYdw22cdj06xY0obSznBsT///GK5IEZQdGliXpAOaRL34o8phEvXzEo/VJx13Q==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: '*' + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + + '@typescript-eslint/types@5.36.1': + resolution: {integrity: sha512-jd93ShpsIk1KgBTx9E+hCSEuLCUFwi9V/urhjOWnOaksGZFbTOxAT47OH2d4NLJnLhkVD+wDbB48BuaycZPLBg==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + '@typescript-eslint/types@5.62.0': + resolution: {integrity: sha512-87NVngcbVXUahrRTqIK27gD2t5Cu1yuCXxbLcFtCzZGlfyVWWh8mLHkoxzjsB6DDNnvdL+fW8MiwPEJyGJQDgQ==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + '@typescript-eslint/typescript-estree@5.36.1': + resolution: {integrity: sha512-ih7V52zvHdiX6WcPjsOdmADhYMDN15SylWRZrT2OMy80wzKbc79n8wFW0xpWpU0x3VpBz/oDgTm2xwDAnFTl+g==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + + '@typescript-eslint/typescript-estree@5.62.0': + resolution: {integrity: sha512-CmcQ6uY7b9y694lKdRB8FEel7JbU/40iSAPomu++SjLMntB+2Leay2LO6i8VnJk58MtE9/nQSFIH6jpyRWyYzA==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + + '@typescript-eslint/utils@5.36.1': + resolution: {integrity: sha512-lNj4FtTiXm5c+u0pUehozaUWhh7UYKnwryku0nxJlYUEWetyG92uw2pr+2Iy4M/u0ONMKzfrx7AsGBTCzORmIg==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 + + '@typescript-eslint/utils@5.62.0': + resolution: {integrity: sha512-n8oxjeb5aIbPFEtmQxQYOLI0i9n5ySBEY/ZEHHZqKQSFnxio1rv6dthascc9dLuwrL0RC5mPCxB7vnAVGAYWAQ==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 + + '@typescript-eslint/visitor-keys@5.36.1': + resolution: {integrity: sha512-ojB9aRyRFzVMN3b5joSYni6FAS10BBSCAfKJhjJAV08t/a95aM6tAhz+O1jF+EtgxktuSO3wJysp2R+Def/IWQ==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + '@typescript-eslint/visitor-keys@5.62.0': + resolution: {integrity: sha512-07ny+LHRzQXepkGg6w0mFY41fVUNBrL2Roj/++7V1txKugfjm/Ci/qSND03r2RhlJhJYMcTn9AhhSSqQp0Ysyw==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + '@vitejs/plugin-react@4.6.0': + resolution: {integrity: sha512-5Kgff+m8e2PB+9j51eGHEpn5kUzRKH2Ry0qGoe8ItJg7pqnkPrYPkDQZGgGmTa0EGarHrkjLvOdU3b1fzI8otQ==} + engines: {node: ^14.18.0 || >=16.0.0} + peerDependencies: + vite: 6.4.2 + + '@webassemblyjs/ast@1.14.1': + resolution: {integrity: sha512-nuBEDgQfm1ccRp/8bCQrx1frohyufl4JlbMMZ4P1wpeOfDhF6FQkxZJ1b/e+PLwr6X1Nhw6OLme5usuBWYBvuQ==} + + '@webassemblyjs/floating-point-hex-parser@1.13.2': + resolution: {integrity: sha512-6oXyTOzbKxGH4steLbLNOu71Oj+C8Lg34n6CqRvqfS2O71BxY6ByfMDRhBytzknj9yGUPVJ1qIKhRlAwO1AovA==} + + '@webassemblyjs/helper-api-error@1.13.2': + resolution: {integrity: sha512-U56GMYxy4ZQCbDZd6JuvvNV/WFildOjsaWD3Tzzvmw/mas3cXzRJPMjP83JqEsgSbyrmaGjBfDtV7KDXV9UzFQ==} + + '@webassemblyjs/helper-buffer@1.14.1': + resolution: {integrity: sha512-jyH7wtcHiKssDtFPRB+iQdxlDf96m0E39yb0k5uJVhFGleZFoNw1c4aeIcVUPPbXUVJ94wwnMOAqUHyzoEPVMA==} + + '@webassemblyjs/helper-numbers@1.13.2': + resolution: {integrity: sha512-FE8aCmS5Q6eQYcV3gI35O4J789wlQA+7JrqTTpJqn5emA4U2hvwJmvFRC0HODS+3Ye6WioDklgd6scJ3+PLnEA==} + + '@webassemblyjs/helper-wasm-bytecode@1.13.2': + resolution: {integrity: sha512-3QbLKy93F0EAIXLh0ogEVR6rOubA9AoZ+WRYhNbFyuB70j3dRdwH9g+qXhLAO0kiYGlg3TxDV+I4rQTr/YNXkA==} + + '@webassemblyjs/helper-wasm-section@1.14.1': + resolution: {integrity: sha512-ds5mXEqTJ6oxRoqjhWDU83OgzAYjwsCV8Lo/N+oRsNDmx/ZDpqalmrtgOMkHwxsG0iI//3BwWAErYRHtgn0dZw==} + + '@webassemblyjs/ieee754@1.13.2': + resolution: {integrity: sha512-4LtOzh58S/5lX4ITKxnAK2USuNEvpdVV9AlgGQb8rJDHaLeHciwG4zlGr0j/SNWlr7x3vO1lDEsuePvtcDNCkw==} + + '@webassemblyjs/leb128@1.13.2': + resolution: {integrity: sha512-Lde1oNoIdzVzdkNEAWZ1dZ5orIbff80YPdHx20mrHwHrVNNTjNr8E3xz9BdpcGqRQbAEa+fkrCb+fRFTl/6sQw==} + + '@webassemblyjs/utf8@1.13.2': + resolution: {integrity: sha512-3NQWGjKTASY1xV5m7Hr0iPeXD9+RDobLll3T9d2AO+g3my8xy5peVyjSag4I50mR1bBSN/Ct12lo+R9tJk0NZQ==} + + '@webassemblyjs/wasm-edit@1.14.1': + resolution: {integrity: sha512-RNJUIQH/J8iA/1NzlE4N7KtyZNHi3w7at7hDjvRNm5rcUXa00z1vRz3glZoULfJ5mpvYhLybmVcwcjGrC1pRrQ==} + + '@webassemblyjs/wasm-gen@1.14.1': + resolution: {integrity: sha512-AmomSIjP8ZbfGQhumkNvgC33AY7qtMCXnN6bL2u2Js4gVCg8fp735aEiMSBbDR7UQIj90n4wKAFUSEd0QN2Ukg==} + + '@webassemblyjs/wasm-opt@1.14.1': + resolution: {integrity: sha512-PTcKLUNvBqnY2U6E5bdOQcSM+oVP/PmrDY9NzowJjislEjwP/C4an2303MCVS2Mg9d3AJpIGdUFIQQWbPds0Sw==} + + '@webassemblyjs/wasm-parser@1.14.1': + resolution: {integrity: sha512-JLBl+KZ0R5qB7mCnud/yyX08jWFw5MsoalJ1pQ4EdFlgj9VdXKGuENGsiCIjegI1W7p91rUlcB/LB5yRJKNTcQ==} + + '@webassemblyjs/wast-printer@1.14.1': + resolution: {integrity: sha512-kPSSXE6De1XOR820C90RIo2ogvZG+c3KiHzqUoO/F34Y2shGzesfqv7o57xrxovZJH/MetF5UjroJ/R/3isoiw==} + + '@webpack-cli/configtest@3.0.1': + resolution: {integrity: sha512-u8d0pJ5YFgneF/GuvEiDA61Tf1VDomHHYMjv/wc9XzYj7nopltpG96nXN5dJRstxZhcNpV1g+nT6CydO7pHbjA==} + engines: {node: '>=18.12.0'} + peerDependencies: + webpack: 5.107.1 + webpack-cli: 6.x.x + + '@webpack-cli/info@3.0.1': + resolution: {integrity: sha512-coEmDzc2u/ffMvuW9aCjoRzNSPDl/XLuhPdlFRpT9tZHmJ/039az33CE7uH+8s0uL1j5ZNtfdv0HkfaKRBGJsQ==} + engines: {node: '>=18.12.0'} + peerDependencies: + webpack: 5.107.1 + webpack-cli: 6.x.x + + '@webpack-cli/serve@3.0.1': + resolution: {integrity: sha512-sbgw03xQaCLiT6gcY/6u3qBDn01CWw/nbaXl3gTdTFuJJ75Gffv3E3DBpgvY2fkkrdS1fpjaXNOmJlnbtKauKg==} + engines: {node: '>=18.12.0'} + peerDependencies: + webpack: 5.107.1 + webpack-cli: 6.x.x + webpack-dev-server: '*' + peerDependenciesMeta: + webpack-dev-server: + optional: true + + '@xtuc/ieee754@1.2.0': + resolution: {integrity: sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==} + + '@xtuc/long@4.2.2': + resolution: {integrity: sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==} + + '@yarnpkg/lockfile@1.1.0': + resolution: {integrity: sha512-GpSwvyXOcOOlV70vbnzjj4fW5xW/FdUF6nQEt1ENy7m4ZCczi1+/buVUPAqmGfqznsORNFzUMjctTIp8a9tuCQ==} + + '@yarnpkg/parsers@3.0.0-rc.46': + resolution: {integrity: sha512-aiATs7pSutzda/rq8fnuPwTglyVwjM22bNnK2ZgjrpAjQHSSl3lztd2f9evst1W/qnC58DRz7T7QndUDumAR4Q==} + engines: {node: '>=14.15.0'} + + '@zag-js/dom-query@0.31.1': + resolution: {integrity: sha512-oiuohEXAXhBxpzzNm9k2VHGEOLC1SXlXSbRPcfBZ9so5NRQUA++zCE7cyQJqGLTZR0t3itFLlZqDbYEXRrefwg==} + + '@zag-js/element-size@0.31.1': + resolution: {integrity: sha512-4T3yvn5NqqAjhlP326Fv+w9RqMIBbNN9H72g5q2ohwzhSgSfZzrKtjL4rs9axY/cw9UfMfXjRjEE98e5CMq7WQ==} + + '@zag-js/focus-visible@0.31.1': + resolution: {integrity: sha512-dbLksz7FEwyFoANbpIlNnd3bVm0clQSUsnP8yUVQucStZPsuWjCrhL2jlAbGNrTrahX96ntUMXHb/sM68TibFg==} + + '@zkochan/js-yaml@0.0.6': + resolution: {integrity: sha512-nzvgl3VfhcELQ8LyVrYOru+UtAy1nrygk2+AGbTm8a5YcO6o8lSjAT+pfg3vJWxIoZKOUhrK6UU7xW/+00kQrg==} + hasBin: true + + abab@2.0.6: + resolution: {integrity: sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==} + deprecated: Use your platform's native atob() and btoa() methods instead + + accepts@1.3.8: + resolution: {integrity: sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==} + engines: {node: '>= 0.6'} + + accepts@2.0.0: + resolution: {integrity: sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==} + engines: {node: '>= 0.6'} + + acorn-import-phases@1.0.4: + resolution: {integrity: sha512-wKmbr/DDiIXzEOiWrTTUcDm24kQ2vGfZQvM2fwg2vXqR5uW6aapr7ObPtj1th32b9u90/Pf4AItvdTh42fBmVQ==} + engines: {node: '>=10.13.0'} + peerDependencies: + acorn: ^8.14.0 + + acorn-jsx@5.3.2: + resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} + peerDependencies: + acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 + + acorn-walk@8.3.5: + resolution: {integrity: sha512-HEHNfbars9v4pgpW6SO1KSPkfoS0xVOM/9UzkJltjlsHZmJasxg8aXkuZa7SMf8vKGIBhpUsPluQSqhJFCqebw==} + engines: {node: '>=0.4.0'} + + acorn@8.16.0: + resolution: {integrity: sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==} + engines: {node: '>=0.4.0'} + hasBin: true + + ajv-formats@2.1.1: + resolution: {integrity: sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==} + peerDependencies: + ajv: ^8.0.0 + peerDependenciesMeta: + ajv: + optional: true + + ajv-formats@3.0.1: + resolution: {integrity: sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==} + peerDependencies: + ajv: ^8.0.0 + peerDependenciesMeta: + ajv: + optional: true + + ajv-keywords@3.5.2: + resolution: {integrity: sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==} + peerDependencies: + ajv: ^6.9.1 + + ajv-keywords@5.1.0: + resolution: {integrity: sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==} + peerDependencies: + ajv: ^8.8.2 + + ajv@6.15.0: + resolution: {integrity: sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==} + + ajv@8.20.0: + resolution: {integrity: sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==} + + ansi-colors@4.1.3: + resolution: {integrity: sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==} + engines: {node: '>=6'} + + ansi-html-community@0.0.8: + resolution: {integrity: sha512-1APHAyr3+PCamwNw3bXCPp4HFLONZt/yIH0sZp0/469KWNTEy+qN5jQ3GVX6DMZ1UXAi34yVwtTeaG/HpBuuzw==} + engines: {'0': node >= 0.8.0} + hasBin: true + + ansi-regex@5.0.1: + resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} + engines: {node: '>=8'} + + ansi-styles@4.3.0: + resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} + engines: {node: '>=8'} + + ansi-styles@5.2.0: + resolution: {integrity: sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==} + engines: {node: '>=10'} + + antd@6.3.7: + resolution: {integrity: sha512-WTHi4bHVNKpYXLHESzU0Tts7rRNQeL84Bph9dfI3Qw7mHbTulExDcYKNHny5CTXcrBBOpraXbU9miBAwUR5vaw==} + peerDependencies: + react: '>=18.0.0' + react-dom: '>=18.0.0' + + anymatch@3.1.3: + resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} + engines: {node: '>= 8'} + + arg@4.1.3: + resolution: {integrity: sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==} + + argparse@1.0.10: + resolution: {integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==} + + argparse@2.0.1: + resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} + + aria-hidden@1.2.6: + resolution: {integrity: sha512-ik3ZgC9dY/lYVVM++OISsaYDeg1tb0VtP5uL3ouh1koGOaUMDPpbFIei4JkFimWUFPn90sbMNMXQAIVOlnYKJA==} + engines: {node: '>=10'} + + aria-query@5.1.3: + resolution: {integrity: sha512-R5iJ5lkuHybztUfuOAznmboyjWq8O6sqNqtK7CLOqdydi54VNbORp49mb14KbWgG1QD3JFO9hJdZ+y4KutfdOQ==} + + aria-query@5.3.2: + resolution: {integrity: sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==} + engines: {node: '>= 0.4'} + + array-buffer-byte-length@1.0.2: + resolution: {integrity: sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==} + engines: {node: '>= 0.4'} + + array-flatten@1.1.1: + resolution: {integrity: sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==} + + array-union@2.1.0: + resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==} + engines: {node: '>=8'} + + array-union@3.0.1: + resolution: {integrity: sha512-1OvF9IbWwaeiM9VhzYXVQacMibxpXOMYVNIvMtKRyX9SImBXpKcFr8XvFDeEslCyuH/t6KRt7HEO94AlP8Iatw==} + engines: {node: '>=12'} + + asn1js@3.0.10: + resolution: {integrity: sha512-S2s3aOytiKdFRdulw2qPE51MzjzVOisppcVv7jVFR+Kw0kxwvFrDcYA0h7Ndqbmj0HkMIXYWaoj7fli8kgx1eg==} + engines: {node: '>=12.0.0'} + + async@3.2.6: + resolution: {integrity: sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==} + + asynckit@0.4.0: + resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} + + atob@2.1.2: + resolution: {integrity: sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==} + engines: {node: '>= 4.5.0'} + hasBin: true + + autoprefixer@10.5.0: + resolution: {integrity: sha512-FMhOoZV4+qR6aTUALKX2rEqGG+oyATvwBt9IIzVR5rMa2HRWPkxf+P+PAJLD1I/H5/II+HuZcBJYEFBpq39ong==} + engines: {node: ^10 || ^12 || >=14} + hasBin: true + peerDependencies: + postcss: 8.5.15 + + available-typed-arrays@1.0.7: + resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==} + engines: {node: '>= 0.4'} + + axios@1.16.0: + resolution: {integrity: sha512-6hp5CwvTPlN2A31g5dxnwAX0orzM7pmCRDLnZSX772mv8WDqICwFjowHuPs04Mc8deIld1+ejhtaMn5vp6b+1w==} + + babel-loader@10.0.0: + resolution: {integrity: sha512-z8jt+EdS61AMw22nSfoNJAZ0vrtmhPRVi6ghL3rCeRZI8cdNYFiV5xeV3HbE7rlZZNmGH8BVccwWt8/ED0QOHA==} + engines: {node: ^18.20.0 || ^20.10.0 || >=22.0.0} + peerDependencies: + '@babel/core': ^7.12.0 + webpack: 5.107.1 + + babel-loader@9.2.1: + resolution: {integrity: sha512-fqe8naHt46e0yIdkjUZYqddSXfej3AHajX+CSO5X7oy0EmPc6o5Xh+RClNoHjnieWz9AW4kZxW9yyFMhVB1QLA==} + engines: {node: '>= 14.15.0'} + peerDependencies: + '@babel/core': ^7.12.0 + webpack: 5.107.1 + + babel-plugin-const-enum@1.2.0: + resolution: {integrity: sha512-o1m/6iyyFnp9MRsK1dHF3bneqyf3AlM2q3A/YbgQr2pCat6B6XJVDv2TXqzfY2RYUi4mak6WAksSBPlyYGx9dg==} + peerDependencies: + '@babel/core': ^7.0.0-0 + + babel-plugin-macros@2.8.0: + resolution: {integrity: sha512-SEP5kJpfGYqYKpBrj5XU3ahw5p5GOHJ0U5ssOSQ/WBVdwkD2Dzlce95exQTs3jOVWPPKLBN2rlEWkCK7dSmLvg==} + + babel-plugin-macros@3.1.0: + resolution: {integrity: sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg==} + engines: {node: '>=10', npm: '>=6'} + + babel-plugin-polyfill-corejs2@0.3.3: + resolution: {integrity: sha512-8hOdmFYFSZhqg2C/JgLUQ+t52o5nirNwaWM2B9LWteozwIvM14VSwdsCAUET10qT+kmySAlseadmfeeSWFCy+Q==} + peerDependencies: + '@babel/core': ^7.0.0-0 + + babel-plugin-polyfill-corejs2@0.4.17: + resolution: {integrity: sha512-aTyf30K/rqAsNwN76zYrdtx8obu0E4KoUME29B1xj+B3WxgvWkp943vYQ+z8Mv3lw9xHXMHpvSPOBxzAkIa94w==} + peerDependencies: + '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0 + + babel-plugin-polyfill-corejs3@0.13.0: + resolution: {integrity: sha512-U+GNwMdSFgzVmfhNm8GJUX88AadB3uo9KpJqS3FaqNIPKgySuvMb+bHPsOmmuWyIcuqZj/pzt1RUIUZns4y2+A==} + peerDependencies: + '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0 + + babel-plugin-polyfill-corejs3@0.6.0: + resolution: {integrity: sha512-+eHqR6OPcBhJOGgsIar7xoAB1GcSwVUA3XjAd7HJNzOXT4wv6/H7KIdA/Nc60cvUlDbKApmqNvD1B1bzOt4nyA==} + peerDependencies: + '@babel/core': ^7.0.0-0 + + babel-plugin-polyfill-regenerator@0.4.1: + resolution: {integrity: sha512-NtQGmyQDXjQqQ+IzRkBVwEOz9lQ4zxAQZgoAYEtU9dJjnl1Oc98qnN7jcp+bE7O7aYzVpavXE3/VKXNzUbh7aw==} + peerDependencies: + '@babel/core': ^7.0.0-0 + + babel-plugin-polyfill-regenerator@0.6.8: + resolution: {integrity: sha512-M762rNHfSF1EV3SLtnCJXFoQbbIIz0OyRwnCmV0KPC7qosSfCO0QLTSuJX3ayAebubhE6oYBAYPrBA5ljowaZg==} + peerDependencies: + '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0 + + babel-plugin-transform-typescript-metadata@0.3.2: + resolution: {integrity: sha512-mWEvCQTgXQf48yDqgN7CH50waTyYBeP2Lpqx4nNWab9sxEpdXVeKgfj1qYI2/TgUPQtNFZ85i3PemRtnXVYYJg==} + peerDependencies: + '@babel/core': ^7 + '@babel/traverse': ^7 + peerDependenciesMeta: + '@babel/traverse': + optional: true + + balanced-match@1.0.2: + resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + + base64-js@1.5.1: + resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} + + baseline-browser-mapping@2.10.27: + resolution: {integrity: sha512-zEs/ufmZoUd7WftKpKyXaT6RFxpQ5Qm9xytKRHvJfxFV9DFJkZph9RvJ1LcOUi0Z1ZVijMte65JbILeV+8QQEA==} + engines: {node: '>=6.0.0'} + hasBin: true + + batch@0.6.1: + resolution: {integrity: sha512-x+VAiMRL6UPkx+kudNvxTl6hB2XNNCG2r+7wixVfIYwu/2HKRXimwQyaumLjMveWvT2Hkd/cAJw+QBMfJ/EKVw==} + + better-path-resolve@1.0.0: + resolution: {integrity: sha512-pbnl5XzGBdrFU/wT4jqmJVPn2B6UHPBOhzMQkY/SPUPB6QtUXtmBHBIwCbXJol93mOpGMnQyP/+BB19q04xj7g==} + engines: {node: '>=4'} + + big.js@5.2.2: + resolution: {integrity: sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==} + + binary-extensions@2.3.0: + resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==} + engines: {node: '>=8'} + + bippy@0.5.39: + resolution: {integrity: sha512-8hE8rKSl8JWyeaY+JjpnmceWAZPpLEyzOZQpWXM5Rc7861c5WotMJHy2aRZKZrGA8nMpvLNF01t4yQQ+HcZG3w==} + peerDependencies: + react: '>=17.0.1' + + bl@4.1.0: + resolution: {integrity: sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==} + + body-parser@1.20.5: + resolution: {integrity: sha512-3grm+/2tUOvu2cjJkvsIxrv/wVpfXQW4PsQHYm7yk4vfpu7Ekl6nEsYBoJUL6qDwZUx8wUhQ8tR2qz+ad9c9OA==} + engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16} + + body-parser@2.2.2: + resolution: {integrity: sha512-oP5VkATKlNwcgvxi0vM0p/D3n2C3EReYVX+DNYs5TjZFn/oQt2j+4sVJtSMr18pdRr8wjTcBl6LoV+FUwzPmNA==} + engines: {node: '>=18'} + + bonjour-service@1.3.0: + resolution: {integrity: sha512-3YuAUiSkWykd+2Azjgyxei8OWf8thdn8AITIog2M4UICzoqfjlqr64WIjEXZllf/W6vK1goqleSR6brGomxQqA==} + + boolbase@1.0.0: + resolution: {integrity: sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==} + + brace-expansion@1.1.14: + resolution: {integrity: sha512-MWPGfDxnyzKU7rNOW9SP/c50vi3xrmrua/+6hfPbCS2ABNWfx24vPidzvC7krjU/RTo235sV776ymlsMtGKj8g==} + + braces@3.0.3: + resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} + engines: {node: '>=8'} + + browserslist@4.25.1: + resolution: {integrity: sha512-KGj0KoOMXLpSNkkEI6Z6mShmQy0bc1I+T7K9N81k4WWMrfz+6fQ6es80B/YLAeRoKvjYE1YSHHOW1qe9xIVzHw==} + 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==} + engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} + hasBin: true + + buffer-from@1.1.2: + resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} + + buffer@5.7.1: + resolution: {integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==} + + bundle-name@4.1.0: + resolution: {integrity: sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q==} + engines: {node: '>=18'} + + bytes@3.1.2: + resolution: {integrity: sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==} + engines: {node: '>= 0.8'} + + bytestreamjs@2.0.1: + resolution: {integrity: sha512-U1Z/ob71V/bXfVABvNr/Kumf5VyeQRBEm6Txb0PQ6S7V5GpBM3w4Cbqz/xPDicR5tN0uvDifng8C+5qECeGwyQ==} + engines: {node: '>=6.0.0'} + + call-bind-apply-helpers@1.0.2: + resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==} + engines: {node: '>= 0.4'} + + call-bind@1.0.8: + resolution: {integrity: sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==} + engines: {node: '>= 0.4'} + + call-bound@1.0.4: + resolution: {integrity: sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==} + engines: {node: '>= 0.4'} + + callsites@3.1.0: + resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} + engines: {node: '>=6'} + + camel-case@4.1.2: + resolution: {integrity: sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw==} + + camelcase@6.3.0: + resolution: {integrity: sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==} + engines: {node: '>=10'} + + caniuse-api@3.0.0: + resolution: {integrity: sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw==} + + caniuse-lite@1.0.30001793: + resolution: {integrity: sha512-iwSsYWaCOoh26cV8NwNRViHlrfUvYsHDfRVcbtmw0Kg6PJIZZXwMkj1442FYLBGkeUf1juAsU3DTfxW579mrPA==} + + chalk@3.0.0: + resolution: {integrity: sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==} + engines: {node: '>=8'} + + chalk@4.1.2: + resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} + engines: {node: '>=10'} + + chardet@2.1.1: + resolution: {integrity: sha512-PsezH1rqdV9VvyNhxxOW32/d75r01NY7TQCmOqomRo15ZSOKbpTFVsfjghxo6JloQUCGnH4k1LGu0R4yCLlWQQ==} + + chokidar@3.6.0: + resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==} + engines: {node: '>= 8.10.0'} + + chokidar@4.0.3: + resolution: {integrity: sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==} + engines: {node: '>= 14.16.0'} + + chrome-trace-event@1.0.4: + resolution: {integrity: sha512-rNjApaLzuwaOTjCiT8lSDdGN1APCiqkChLMJxJPWLunPAt5fy8xgU9/jNOchV84wfIxrA0lRQB7oCT8jrn/wrQ==} + engines: {node: '>=6.0'} + + ci-info@4.3.0: + resolution: {integrity: sha512-l+2bNRMiQgcfILUi33labAZYIWlH1kWDp+ecNo5iisRKrbm0xcRyCww71/YU0Fkw0mAFpz9bJayXPjey6vkmaQ==} + engines: {node: '>=8'} + + clean-css@5.3.3: + resolution: {integrity: sha512-D5J+kHaVb/wKSFcyyV75uCn8fiY4sV38XJoe4CUyGQ+mOU/fMVYUdH1hJC+CJQ5uY3EnW27SbJYS4X8BiLrAFg==} + engines: {node: '>= 10.0'} + + cli-cursor@3.1.0: + resolution: {integrity: sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==} + engines: {node: '>=8'} + + cli-spinners@2.6.1: + resolution: {integrity: sha512-x/5fWmGMnbKQAaNwN+UZlV79qBLM9JFnJuJ03gIi5whrob0xV0ofNVHy9DhwGdsMJQc2OKv0oGmLzvaqvAVv+g==} + engines: {node: '>=6'} + + client-only@0.0.1: + resolution: {integrity: sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==} + + cliui@7.0.4: + resolution: {integrity: sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==} + + cliui@8.0.1: + resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==} + engines: {node: '>=12'} + + clone-deep@4.0.1: + resolution: {integrity: sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==} + engines: {node: '>=6'} + + clsx@2.1.1: + resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==} + engines: {node: '>=6'} + + color-convert@2.0.1: + resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} + engines: {node: '>=7.0.0'} + + color-name@1.1.4: + resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + + color2k@2.0.3: + resolution: {integrity: sha512-zW190nQTIoXcGCaU08DvVNFTmQhUpnJfVuAKfWqUQkflXKpaDdpaYoM0iluLS9lgJNHyBF58KKA2FBEwkD7wog==} + + colord@2.9.3: + resolution: {integrity: sha512-jeC1axXpnb0/2nn/Y1LPuLdgXBLH7aDcHu4KEKfqw3CUhX7ZpfBSlPKyqXE6btIgEzfWtrX3/tyBCaCvXvMkOw==} + + colorette@2.0.20: + resolution: {integrity: sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==} + + combined-stream@1.0.8: + resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} + engines: {node: '>= 0.8'} + + commander@12.1.0: + resolution: {integrity: sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==} + engines: {node: '>=18'} + + commander@2.20.3: + resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==} + + commander@7.2.0: + resolution: {integrity: sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==} + engines: {node: '>= 10'} + + commander@8.3.0: + resolution: {integrity: sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==} + engines: {node: '>= 12'} + + common-path-prefix@3.0.0: + resolution: {integrity: sha512-QE33hToZseCH3jS0qN96O/bSh3kaw/h+Tq7ngyY9eWDUnTlTNUyqfqvCXioLe5Na5jFsL78ra/wuBU4iuEgd4w==} + + compressible@2.0.18: + resolution: {integrity: sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==} + engines: {node: '>= 0.6'} + + compression@1.8.1: + resolution: {integrity: sha512-9mAqGPHLakhCLeNyxPkK4xVo746zQ/czLH1Ky+vkitMnWfWZps8r0qXuwhwizagCRttsL4lfG4pIOvaWLpAP0w==} + engines: {node: '>= 0.8.0'} + + compute-scroll-into-view@3.1.1: + resolution: {integrity: sha512-VRhuHOLoKYOy4UbilLbUzbYg93XLjv2PncJC50EuTWPA3gaja1UjBsUP/D/9/juV3vQFr6XBEzn9KCAHdUvOHw==} + + concat-map@0.0.1: + resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} + + confusing-browser-globals@1.0.11: + resolution: {integrity: sha512-JsPKdmh8ZkmnHxDk55FZ1TqVLvEQTvoByJZRN9jzI0UjxK/QgAmsphz7PGtqgPieQZ/CQcHWXCR7ATDNhGe+YA==} + + connect-history-api-fallback@2.0.0: + resolution: {integrity: sha512-U73+6lQFmfiNPrYbXqr6kZ1i1wiRqXnp2nhMsINseWXO8lDau0LGEffJ8kQi4EjLZympVgRdvqjAgiZ1tgzDDA==} + engines: {node: '>=0.8'} + + content-disposition@0.5.4: + resolution: {integrity: sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==} + engines: {node: '>= 0.6'} + + content-disposition@1.1.0: + resolution: {integrity: sha512-5jRCH9Z/+DRP7rkvY83B+yGIGX96OYdJmzngqnw2SBSxqCFPd0w2km3s5iawpGX8krnwSGmF0FW5Nhr0Hfai3g==} + engines: {node: '>=18'} + + content-type@1.0.5: + resolution: {integrity: sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==} + engines: {node: '>= 0.6'} + + content-type@2.0.0: + resolution: {integrity: sha512-j/O/d7GcZCyNl7/hwZAb606rzqkyvaDctLmckbxLzHvFBzTJHuGEdodATcP3yIRoDrLHkIATJuvzbFlp/ki2cQ==} + engines: {node: '>=18'} + + convert-source-map@1.9.0: + resolution: {integrity: sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==} + + convert-source-map@2.0.0: + resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} + + cookie-signature@1.0.7: + resolution: {integrity: sha512-NXdYc3dLr47pBkpUCHtKSwIOQXLVn8dZEuywboCOJY/osA0wFSLlSawr3KN8qXJEyX66FcONTH8EIlVuK0yyFA==} + + cookie-signature@1.2.2: + resolution: {integrity: sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==} + engines: {node: '>=6.6.0'} + + cookie@0.7.2: + resolution: {integrity: sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==} + engines: {node: '>= 0.6'} + + copy-anything@2.0.6: + resolution: {integrity: sha512-1j20GZTsvKNkc4BY3NpMOM8tt///wY3FpIzozTOFO2ffuZcV61nojHXVKIy3WM+7ADCy5FVhdZYHYDdgTU0yJw==} + + copy-to-clipboard@3.3.3: + resolution: {integrity: sha512-2KV8NhB5JqC3ky0r9PMCAZKbUHSwtEo4CwCs0KXgruG43gX5PMqDEBbVU4OUzw2MuAWUfsuFmWvEKG5QRfSnJA==} + + copy-webpack-plugin@10.2.4: + resolution: {integrity: sha512-xFVltahqlsRcyyJqQbDY6EYTtyQZF9rf+JPjwHObLdPFMEISqkFkr7mFoVOC6BfYS/dNThyoQKvziugm+OnwBg==} + engines: {node: '>= 12.20.0'} + peerDependencies: + webpack: 5.107.1 + + core-js-compat@3.49.0: + resolution: {integrity: sha512-VQXt1jr9cBz03b331DFDCCP90b3fanciLkgiOoy8SBHy06gNf+vQ1A3WFLqG7I8TipYIKeYK9wxd0tUrvHcOZA==} + + core-util-is@1.0.3: + resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==} + + cors@2.8.6: + resolution: {integrity: sha512-tJtZBBHA6vjIAaF6EnIaq6laBBP9aq/Y3ouVJjEfoHbRBcHBAHYcMh/w8LDrk2PvIMMq8gmopa5D4V8RmbrxGw==} + engines: {node: '>= 0.10'} + + cosmiconfig@6.0.0: + resolution: {integrity: sha512-xb3ZL6+L8b9JLLCx3ZdoZy4+2ECphCMo2PwqgP1tlfVq6M6YReyzBJtvWWtbDSpNr9hn96pkCiZqUcFEc+54Qg==} + engines: {node: '>=8'} + + cosmiconfig@7.1.0: + resolution: {integrity: sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==} + engines: {node: '>=10'} + + create-require@1.1.1: + resolution: {integrity: sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==} + + cross-spawn@6.0.6: + resolution: {integrity: sha512-VqCUuhcd1iB+dsv8gxPttb5iZh/D0iubSP21g36KXdEuf6I5JiioesUVjpCdHV9MZRUfVFlvwtIUyPfxo5trtw==} + engines: {node: '>=4.8'} + + cross-spawn@7.0.6: + resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} + engines: {node: '>= 8'} + + css-declaration-sorter@6.4.1: + resolution: {integrity: sha512-rtdthzxKuyq6IzqX6jEcIzQF/YqccluefyCYheovBOLhFT/drQA9zj/UbRAa9J7C0o6EG6u3E6g+vKkay7/k3g==} + engines: {node: ^10 || ^12 || >=14} + peerDependencies: + postcss: 8.5.15 + + css-loader@6.11.0: + resolution: {integrity: sha512-CTJ+AEQJjq5NzLga5pE39qdiSV56F8ywCIsqNIRF0r7BDgWsN25aazToqAFg7ZrtA/U016xudB3ffgweORxX7g==} + engines: {node: '>= 12.13.0'} + peerDependencies: + '@rspack/core': 0.x || 1.x + webpack: 5.107.1 + peerDependenciesMeta: + '@rspack/core': + optional: true + webpack: + optional: true + + css-loader@7.1.4: + resolution: {integrity: sha512-vv3J9tlOl04WjiMvHQI/9tmIrCxVrj6PFbHemBB1iihpeRbi/I4h033eoFIhwxBBqLhI0KYFS7yvynBFhIZfTw==} + engines: {node: '>= 18.12.0'} + peerDependencies: + '@rspack/core': 0.x || ^1.0.0 || ^2.0.0-0 + webpack: 5.107.1 + peerDependenciesMeta: + '@rspack/core': + optional: true + webpack: + optional: true + + css-minimizer-webpack-plugin@3.4.1: + resolution: {integrity: sha512-1u6D71zeIfgngN2XNRJefc/hY7Ybsxd74Jm4qngIXyUEk7fss3VUzuHxLAq/R8NAba4QU9OUSaMZlbpRc7bM4Q==} + engines: {node: '>= 12.13.0'} + peerDependencies: + '@parcel/css': '*' + clean-css: '*' + csso: '*' + esbuild: '*' + webpack: 5.107.1 + peerDependenciesMeta: + '@parcel/css': + optional: true + clean-css: + optional: true + csso: + optional: true + esbuild: + optional: true + + css-select@4.3.0: + resolution: {integrity: sha512-wPpOYtnsVontu2mODhA19JrqWxNsfdatRKd64kmpRbQgh1KtItko5sTnEpPdpSaJszTOhEMlF/RPz28qj4HqhQ==} + + css-tree@1.1.3: + resolution: {integrity: sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q==} + engines: {node: '>=8.0.0'} + + css-what@6.2.2: + resolution: {integrity: sha512-u/O3vwbptzhMs3L1fQE82ZSLHQQfto5gyZzwteVIEyeaY5Fc7R4dapF/BvRoSYFeqfBk4m0V1Vafq5Pjv25wvA==} + engines: {node: '>= 6'} + + css.escape@1.5.1: + resolution: {integrity: sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg==} + + css@3.0.0: + resolution: {integrity: sha512-DG9pFfwOrzc+hawpmqX/dHYHJG+Bsdb0klhyi1sDneOgGOXy9wQIC8hzyVp1e4NRYDBdxcylvywPkkXCHAzTyQ==} + + cssesc@3.0.0: + resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==} + engines: {node: '>=4'} + hasBin: true + + cssnano-preset-default@5.2.14: + resolution: {integrity: sha512-t0SFesj/ZV2OTylqQVOrFgEh5uanxbO6ZAdeCrNsUQ6fVuXwYTxJPNAGvGTxHbD68ldIJNec7PyYZDBrfDQ+6A==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: 8.5.15 + + cssnano-utils@3.1.0: + resolution: {integrity: sha512-JQNR19/YZhz4psLX/rQ9M83e3z2Wf/HdJbryzte4a3NSuafyp9w/I4U+hx5C2S9g41qlstH7DEWnZaaj83OuEA==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: 8.5.15 + + cssnano@5.1.15: + resolution: {integrity: sha512-j+BKgDcLDQA+eDifLx0EO4XSA56b7uut3BQFH+wbSaSTuGLuiyTa/wbRYthUXX8LC9mLg+WWKe8h+qJuwTAbHw==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: 8.5.15 + + csso@4.2.0: + resolution: {integrity: sha512-wvlcdIbf6pwKEk7vHj8/Bkc0B4ylXZruLvOgs9doS5eOsOpuodOV2zJChSpkp+pRpYQLQMeF04nr3Z68Sta9jA==} + engines: {node: '>=8.0.0'} + + csstype@3.2.3: + resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==} + + dayjs@1.11.20: + resolution: {integrity: sha512-YbwwqR/uYpeoP4pu043q+LTDLFBLApUP6VxRihdfNTqu4ubqMlGDLd6ErXhEgsyvY0K6nCs7nggYumAN+9uEuQ==} + + debug@2.6.9: + resolution: {integrity: sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + debug@3.1.0: + resolution: {integrity: sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + debug@4.4.1: + resolution: {integrity: sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + debug@4.4.3: + resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + decode-uri-component@0.2.2: + resolution: {integrity: sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ==} + engines: {node: '>=0.10'} + + deep-equal@2.2.3: + resolution: {integrity: sha512-ZIwpnevOurS8bpT4192sqAowWM76JDKSHYzMLty3BZGSswgq6pBaH3DhCSW5xVAZICZyKdOBPjwww5wfgT/6PA==} + engines: {node: '>= 0.4'} + + deep-is@0.1.4: + resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} + + deepmerge@4.3.1: + resolution: {integrity: sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==} + engines: {node: '>=0.10.0'} + + default-browser-id@5.0.1: + resolution: {integrity: sha512-x1VCxdX4t+8wVfd1so/9w+vQ4vx7lKd2Qp5tDRutErwmR85OgmfX7RlLRMWafRMY7hbEiXIbudNrjOAPa/hL8Q==} + engines: {node: '>=18'} + + default-browser@5.5.0: + resolution: {integrity: sha512-H9LMLr5zwIbSxrmvikGuI/5KGhZ8E2zH3stkMgM5LpOWDutGM2JZaj460Udnf1a+946zc7YBgrqEWwbk7zHvGw==} + engines: {node: '>=18'} + + define-data-property@1.1.4: + resolution: {integrity: sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==} + engines: {node: '>= 0.4'} + + define-lazy-prop@2.0.0: + resolution: {integrity: sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==} + engines: {node: '>=8'} + + define-lazy-prop@3.0.0: + resolution: {integrity: sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==} + engines: {node: '>=12'} + + define-properties@1.2.1: + resolution: {integrity: sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==} + engines: {node: '>= 0.4'} + + delayed-stream@1.0.0: + resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} + engines: {node: '>=0.4.0'} + + depd@1.1.2: + resolution: {integrity: sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==} + engines: {node: '>= 0.6'} + + depd@2.0.0: + resolution: {integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==} + engines: {node: '>= 0.8'} + + destroy@1.2.0: + resolution: {integrity: sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==} + engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16} + + detect-indent@6.1.0: + resolution: {integrity: sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA==} + engines: {node: '>=8'} + + detect-libc@2.1.2: + resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==} + engines: {node: '>=8'} + + detect-node-es@1.1.0: + resolution: {integrity: sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==} + + detect-node@2.1.0: + resolution: {integrity: sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==} + + diff@4.0.4: + resolution: {integrity: sha512-X07nttJQkwkfKfvTPG/KSnE2OMdcUCao6+eXF3wmnIQRn2aPAHH3VxDbDOdegkd6JbPsXqShpvEOHfAT+nCNwQ==} + engines: {node: '>=0.3.1'} + + dir-glob@3.0.1: + resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==} + engines: {node: '>=8'} + + dns-packet@5.6.1: + resolution: {integrity: sha512-l4gcSouhcgIKRvyy99RNVOgxXiicE+2jZoNmaNmZ6JXiGajBOJAesk1OBlJuM5k2c+eudGdLxDqXuPCKIj6kpw==} + engines: {node: '>=6'} + + doctrine@3.0.0: + resolution: {integrity: sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==} + engines: {node: '>=6.0.0'} + + dom-accessibility-api@0.5.16: + resolution: {integrity: sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==} + + dom-converter@0.2.0: + resolution: {integrity: sha512-gd3ypIPfOMr9h5jIKq8E3sHOTCjeirnl0WK5ZdS1AW0Odt0b1PaWaHdJ4Qk4klv+YB9aJBS7mESXjFoDQPu6DA==} + + dom-helpers@5.2.1: + resolution: {integrity: sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==} + + dom-serializer@1.4.1: + resolution: {integrity: sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==} + + domelementtype@2.3.0: + resolution: {integrity: sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==} + + domhandler@4.3.1: + resolution: {integrity: sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==} + engines: {node: '>= 4'} + + domutils@2.8.0: + resolution: {integrity: sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==} + + dot-case@3.0.4: + resolution: {integrity: sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==} + + dotenv@10.0.0: + resolution: {integrity: sha512-rlBi9d8jpv9Sf1klPjNfFAuWDjKLwTIJJ/VxtoTwIR6hnZxcEOQCZg2oIL3MWBYw5GpUDKOEnND7LXTbIpQ03Q==} + engines: {node: '>=10'} + + dunder-proto@1.0.1: + resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} + engines: {node: '>= 0.4'} + + duplexer@0.1.2: + resolution: {integrity: sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==} + + ee-first@1.1.1: + resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==} + + ejs@3.1.10: + resolution: {integrity: sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==} + engines: {node: '>=0.10.0'} + hasBin: true + + electron-to-chromium@1.5.182: + resolution: {integrity: sha512-Lv65Btwv9W4J9pyODI6EWpdnhfvrve/us5h1WspW8B2Fb0366REPtY3hX7ounk1CkV/TBjWCEvCBBbYbmV0qCA==} + + electron-to-chromium@1.5.352: + resolution: {integrity: sha512-9wHk8x6dyuimoe18EdiDPWKExNdxYqo4fn4FwOVVper6RxT3cmpBwBkWWfSOCYJjQdIco/nPhJhNLmn4Ufg1Yg==} + + emoji-regex@8.0.0: + resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} + + emojis-list@3.0.0: + resolution: {integrity: sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==} + engines: {node: '>= 4'} + + encodeurl@2.0.0: + resolution: {integrity: sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==} + engines: {node: '>= 0.8'} + + end-of-stream@1.4.5: + resolution: {integrity: sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==} + + enhanced-resolve@5.22.0: + resolution: {integrity: sha512-xYcDWrpELkFzz9SpZ3PlI6Eu6eD93Yf0WLDRxikGhWJ3MAir2SNZTIVCVZqZ/NUyx8AdMc2gT9C0gPiw18kG+A==} + engines: {node: '>=10.13.0'} + + enquirer@2.3.6: + resolution: {integrity: sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==} + engines: {node: '>=8.6'} + + enquirer@2.4.1: + resolution: {integrity: sha512-rRqJg/6gd538VHvR3PSrdRBb/1Vy2YfzHqzvbhGIQpDRKIa4FgV/54b5Q1xYSxOOwKvjXweS26E0Q+nAMwp2pQ==} + engines: {node: '>=8.6'} + + entities@2.2.0: + resolution: {integrity: sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==} + + entities@4.5.0: + resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==} + engines: {node: '>=0.12'} + + envinfo@7.21.0: + resolution: {integrity: sha512-Lw7I8Zp5YKHFCXL7+Dz95g4CcbMEpgvqZNNq3AmlT5XAV6CgAAk6gyAMqn2zjw08K9BHfcNuKrMiCPLByGafow==} + engines: {node: '>=4'} + hasBin: true + + errno@0.1.8: + resolution: {integrity: sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A==} + hasBin: true + + error-ex@1.3.2: + resolution: {integrity: sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==} + + es-define-property@1.0.1: + resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==} + engines: {node: '>= 0.4'} + + es-errors@1.3.0: + resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} + engines: {node: '>= 0.4'} + + es-get-iterator@1.1.3: + resolution: {integrity: sha512-sPZmqHBe6JIiTfN5q2pEi//TwxmAFHwj/XEuYjTuse78i8KxaqMTTzxPoFKuzRpDpTJ+0NAbpfenkmH2rePtuw==} + + es-module-lexer@2.1.0: + resolution: {integrity: sha512-n27zTYMjYu1aj4MjCWzSP7G9r75utsaoc8m61weK+W8JMBGGQybd43GstCXZ3WNmSFtGT9wi59qQTW6mhTR5LQ==} + + es-object-atoms@1.1.1: + resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==} + engines: {node: '>= 0.4'} + + es-set-tostringtag@2.1.0: + resolution: {integrity: sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==} + engines: {node: '>= 0.4'} + + esbuild@0.25.6: + resolution: {integrity: sha512-GVuzuUwtdsghE3ocJ9Bs8PNoF13HNQ5TXbEi2AhvVb8xU1Iwt9Fos9FEamfoee+u/TOsn7GUWc04lz46n2bbTg==} + engines: {node: '>=18'} + hasBin: true + + escalade@3.2.0: + resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} + engines: {node: '>=6'} + + escape-html@1.0.3: + resolution: {integrity: sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==} + + escape-string-regexp@1.0.5: + resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==} + engines: {node: '>=0.8.0'} + + escape-string-regexp@2.0.0: + resolution: {integrity: sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==} + engines: {node: '>=8'} + + escape-string-regexp@4.0.0: + resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} + engines: {node: '>=10'} + + eslint-scope@5.1.1: + resolution: {integrity: sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==} + engines: {node: '>=8.0.0'} + + eslint-scope@7.2.2: + resolution: {integrity: sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + eslint-utils@3.0.0: + resolution: {integrity: sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==} + engines: {node: ^10.0.0 || ^12.0.0 || >= 14.0.0} + peerDependencies: + eslint: '>=5' + + eslint-visitor-keys@2.1.0: + resolution: {integrity: sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==} + engines: {node: '>=10'} + + eslint-visitor-keys@3.4.3: + resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + eslint@8.23.1: + resolution: {integrity: sha512-w7C1IXCc6fNqjpuYd0yPlcTKKmHlHHktRkzmBPZ+7cvNBQuiNjx0xaMTjAJGCafJhQkrFJooREv0CtrVzmHwqg==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + deprecated: This version is no longer supported. Please see https://eslint.org/version-support for other options. + hasBin: true + + 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==} + engines: {node: '>=0.10'} + + esrecurse@4.3.0: + resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==} + engines: {node: '>=4.0'} + + estraverse@4.3.0: + resolution: {integrity: sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==} + engines: {node: '>=4.0'} + + estraverse@5.3.0: + resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} + engines: {node: '>=4.0'} + + esutils@2.0.3: + resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} + engines: {node: '>=0.10.0'} + + etag@1.8.1: + resolution: {integrity: sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==} + engines: {node: '>= 0.6'} + + eventemitter3@4.0.7: + resolution: {integrity: sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==} + + events@3.3.0: + resolution: {integrity: sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==} + engines: {node: '>=0.8.x'} + + eventsource-parser@3.1.0: + resolution: {integrity: sha512-kJezFj9YFAMLeORyi7aCLxLbD5/qWMQnoMVlVPyHIll7lgRJCc3JVln9Vgl9nwQi0YkMnhdGTMNn7CkRRAptMg==} + engines: {node: '>=18.0.0'} + + eventsource@3.0.7: + resolution: {integrity: sha512-CRT1WTyuQoD771GW56XEZFQ/ZoSfWid1alKGDYMmkt2yl8UXrVR4pspqWNEcqKvVIzg6PAltWjxcSSPrboA4iA==} + engines: {node: '>=18.0.0'} + + execa@1.0.0: + resolution: {integrity: sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==} + engines: {node: '>=6'} + + expect@30.0.4: + resolution: {integrity: sha512-dDLGjnP2cKbEppxVICxI/Uf4YemmGMPNy0QytCbfafbpYk9AFQsxb8Uyrxii0RPK7FWgLGlSem+07WirwS3cFQ==} + 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==} + engines: {node: '>= 16'} + peerDependencies: + express: '>= 4.11' + + express@4.22.1: + resolution: {integrity: sha512-F2X8g9P1X7uCPZMA3MVf9wcTqlyNp7IhH5qPCI0izhaOIYXaW9L535tGA3qmjRzpH+bZczqq7hVKxTR4NWnu+g==} + engines: {node: '>= 0.10.0'} + + express@5.2.1: + resolution: {integrity: sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==} + engines: {node: '>= 18'} + + extendable-error@0.1.7: + resolution: {integrity: sha512-UOiS2in6/Q0FK0R0q6UY9vYpQ21mr/Qn1KOnte7vsACuNJf514WvCCUHSRCPcgjPT2bAhNIJdlE6bVap1GKmeg==} + + fast-deep-equal@3.1.3: + resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} + + fast-glob@3.2.7: + resolution: {integrity: sha512-rYGMRwip6lUMvYD3BTScMwT1HtAs2d71SMv66Vrxs0IekGZEjhM0pcMfjQPnknBt2zeCwQMEupiN02ZP4DiT1Q==} + engines: {node: '>=8'} + + fast-glob@3.3.3: + resolution: {integrity: sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==} + engines: {node: '>=8.6.0'} + + fast-json-stable-stringify@2.1.0: + resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} + + fast-levenshtein@2.0.6: + resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} + + fast-uri@3.1.2: + resolution: {integrity: sha512-rVjf7ArG3LTk+FS6Yw81V1DLuZl1bRbNrev6Tmd/9RaroeeRRJhAt7jg/6YFxbvAQXUCavSoZhPPj6oOx+5KjQ==} + + fastest-levenshtein@1.0.16: + resolution: {integrity: sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg==} + engines: {node: '>= 4.9.1'} + + fastq@1.19.1: + resolution: {integrity: sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==} + + faye-websocket@0.11.4: + resolution: {integrity: sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g==} + engines: {node: '>=0.8.0'} + + fdir@6.4.6: + resolution: {integrity: sha512-hiFoqpyZcfNm1yc4u8oWCf9A2c4D3QjCrks3zmoVKVxpQRzmPNar1hUJcBG2RQHvEVGDN+Jm81ZheVLAQMK6+w==} + peerDependencies: + picomatch: 4.0.4 + peerDependenciesMeta: + picomatch: + optional: true + + figures@3.2.0: + resolution: {integrity: sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==} + engines: {node: '>=8'} + + file-entry-cache@6.0.1: + resolution: {integrity: sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==} + engines: {node: ^10.12.0 || >=12.0.0} + + file-loader@6.2.0: + resolution: {integrity: sha512-qo3glqyTa61Ytg4u73GultjHGjdRyig3tG6lPtyX/jOEJvHif9uB0/OCI2Kif6ctF3caQTW2G5gym21oAsI4pw==} + engines: {node: '>= 10.13.0'} + peerDependencies: + webpack: 5.107.1 + + filelist@1.0.6: + resolution: {integrity: sha512-5giy2PkLYY1cP39p17Ech+2xlpTRL9HLspOfEgm0L6CwBXBTgsK5ou0JtzYuepxkaQ/tvhCFIJ5uXo0OrM2DxA==} + + fill-range@7.1.1: + resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} + engines: {node: '>=8'} + + finalhandler@1.3.2: + resolution: {integrity: sha512-aA4RyPcd3badbdABGDuTXCMTtOneUCAYH/gxoYRTZlIJdF0YPWuGqiAsIrhNnnqdXGswYk6dGujem4w80UJFhg==} + engines: {node: '>= 0.8'} + + finalhandler@2.1.1: + resolution: {integrity: sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA==} + engines: {node: '>= 18.0.0'} + + find-cache-dir@4.0.0: + resolution: {integrity: sha512-9ZonPT4ZAK4a+1pUPVPZJapbi7O5qbbJPdYw/NOQWZZbVLdDTYM3A4R9z/DpAM08IDaFGsvPgiGZ82WEwUDWjg==} + engines: {node: '>=14.16'} + + find-root@1.1.0: + resolution: {integrity: sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==} + + find-up@4.1.0: + resolution: {integrity: sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==} + engines: {node: '>=8'} + + find-up@5.0.0: + resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} + engines: {node: '>=10'} + + find-up@6.3.0: + resolution: {integrity: sha512-v2ZsoEuVHYy8ZIlYqwPe/39Cy+cFDzp4dXPaxNvkEuouymu+2Jbz0PxpKarJHYJTmv2HWT3O382qY8l4jMWthw==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + flat-cache@3.2.0: + resolution: {integrity: sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==} + engines: {node: ^10.12.0 || >=12.0.0} + + flat@5.0.2: + resolution: {integrity: sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==} + hasBin: true + + flatted@3.4.2: + resolution: {integrity: sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==} + + focus-lock@1.3.6: + resolution: {integrity: sha512-Ik/6OCk9RQQ0T5Xw+hKNLWrjSMtv51dD4GRmJjbD5a58TIEpI5a5iXagKVl3Z5UuyslMCA8Xwnu76jQob62Yhg==} + engines: {node: '>=10'} + + follow-redirects@1.16.0: + resolution: {integrity: sha512-y5rN/uOsadFT/JfYwhxRS5R7Qce+g3zG97+JrtFZlC9klX/W5hD7iiLzScI4nZqUS7DNUdhPgw4xI8W2LuXlUw==} + engines: {node: '>=4.0'} + peerDependencies: + debug: '*' + peerDependenciesMeta: + debug: + optional: true + + for-each@0.3.5: + resolution: {integrity: sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==} + engines: {node: '>= 0.4'} + + fork-ts-checker-webpack-plugin@7.2.13: + resolution: {integrity: sha512-fR3WRkOb4bQdWB/y7ssDUlVdrclvwtyCUIHCfivAoYxq9dF7XfrDKbMdZIfwJ7hxIAqkYSGeU7lLJE6xrxIBdg==} + engines: {node: '>=12.13.0', yarn: '>=1.0.0'} + peerDependencies: + typescript: '>3.6.0' + vue-template-compiler: '*' + webpack: 5.107.1 + peerDependenciesMeta: + vue-template-compiler: + optional: true + + form-data@4.0.5: + resolution: {integrity: sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==} + engines: {node: '>= 6'} + + forwarded@0.2.0: + resolution: {integrity: sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==} + engines: {node: '>= 0.6'} + + fraction.js@5.3.4: + resolution: {integrity: sha512-1X1NTtiJphryn/uLQz3whtY6jK3fTqoE3ohKs0tT+Ujr1W59oopxmoEh7Lu5p6vBaPbgoM0bzveAW4Qi5RyWDQ==} + + framer-motion@12.38.0: + resolution: {integrity: sha512-rFYkY/pigbcswl1XQSb7q424kSTQ8q6eAC+YUsSKooHQYuLdzdHjrt6uxUC+PRAO++q5IS7+TamgIw1AphxR+g==} + peerDependencies: + '@emotion/is-prop-valid': '*' + react: ^18.0.0 || ^19.0.0 + react-dom: ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + '@emotion/is-prop-valid': + optional: true + react: + optional: true + react-dom: + optional: true + + framesync@6.1.2: + resolution: {integrity: sha512-jBTqhX6KaQVDyus8muwZbBeGGP0XgujBRbQ7gM7BRdS3CadCZIHiawyzYLnafYcvZIh5j8WE7cxZKFn7dXhu9g==} + + fresh@0.5.2: + resolution: {integrity: sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==} + engines: {node: '>= 0.6'} + + fresh@2.0.0: + resolution: {integrity: sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==} + engines: {node: '>= 0.8'} + + fs-constants@1.0.0: + resolution: {integrity: sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==} + + fs-extra@10.1.0: + resolution: {integrity: sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==} + engines: {node: '>=12'} + + fs-extra@11.3.5: + resolution: {integrity: sha512-eKpRKAovdpZtR1WopLHxlBWvAgPny3c4gX1G5Jhwmmw4XJj0ifSD5qB5TOo8hmA0wlRKDAOAhEE1yVPgs6Fgcg==} + engines: {node: '>=14.14'} + + fs-extra@7.0.1: + resolution: {integrity: sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==} + engines: {node: '>=6 <7 || >=8'} + + fs-extra@8.1.0: + resolution: {integrity: sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==} + engines: {node: '>=6 <7 || >=8'} + + fs-monkey@1.1.0: + resolution: {integrity: sha512-QMUezzXWII9EV5aTFXW1UBVUO77wYPpjqIF8/AviUCThNeSYZykpoTixUeaNNBwmCev0AMDWMAni+f8Hxb1IFw==} + + fs.realpath@1.0.0: + resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} + + fsevents@2.3.2: + resolution: {integrity: sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + + fsevents@2.3.3: + resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + + function-bind@1.1.2: + resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} + + functional-red-black-tree@1.0.1: + resolution: {integrity: sha512-dsKNQNdj6xA3T+QlADDA7mOSlX0qiMINjn0cgr+eGHGsbSHzTabcIogz2+p/iqP1Xs6EP/sS2SbqH+brGTbq0g==} + + functions-have-names@1.2.3: + resolution: {integrity: sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==} + + gensync@1.0.0-beta.2: + resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} + engines: {node: '>=6.9.0'} + + get-caller-file@2.0.5: + resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} + engines: {node: 6.* || 8.* || >= 10.*} + + get-intrinsic@1.3.0: + resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==} + engines: {node: '>= 0.4'} + + get-nonce@1.0.1: + resolution: {integrity: sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q==} + engines: {node: '>=6'} + + get-proto@1.0.1: + resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==} + engines: {node: '>= 0.4'} + + get-stream@4.1.0: + resolution: {integrity: sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==} + engines: {node: '>=6'} + + glob-parent@5.1.2: + resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} + engines: {node: '>= 6'} + + glob-parent@6.0.2: + resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} + engines: {node: '>=10.13.0'} + + glob-to-regex.js@1.2.0: + resolution: {integrity: sha512-QMwlOQKU/IzqMUOAZWubUOT8Qft+Y0KQWnX9nK3ch0CJg0tTp4TvGZsTfudYKv2NzoQSyPcnA6TYeIQ3jGichQ==} + engines: {node: '>=10.0'} + peerDependencies: + tslib: '2' + + glob-to-regexp@0.4.1: + resolution: {integrity: sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==} + + glob@7.1.4: + resolution: {integrity: sha512-hkLPepehmnKk41pUGm3sYxoFs/umurYfYJCerbXEyFIWcAzvpipAgVkBqqT9RBKMGjnq6kMuyYwha6csxbiM1A==} + deprecated: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me + + glob@7.2.3: + resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} + deprecated: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me + + globals@13.24.0: + resolution: {integrity: sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==} + engines: {node: '>=8'} + + globby@11.1.0: + resolution: {integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==} + engines: {node: '>=10'} + + globby@12.2.0: + resolution: {integrity: sha512-wiSuFQLZ+urS9x2gGPl1H5drc5twabmm4m2gTR27XDFyjUHJUNsS8o/2aKyIF6IoBaR630atdher0XJ5g6OMmA==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + gopd@1.2.0: + resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==} + engines: {node: '>= 0.4'} + + graceful-fs@4.2.11: + resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} + + grapheme-splitter@1.0.4: + resolution: {integrity: sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==} + + handle-thing@2.0.1: + resolution: {integrity: sha512-9Qn4yBxelxoh2Ow62nP+Ka/kMnOXRi8BXnRaUwezLNhqelnN49xKz4F/dPP8OYLxLxq6JDtZb2i9XznUQbNPTg==} + + has-bigints@1.1.0: + resolution: {integrity: sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==} + engines: {node: '>= 0.4'} + + has-flag@4.0.0: + resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} + engines: {node: '>=8'} + + has-property-descriptors@1.0.2: + resolution: {integrity: sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==} + + has-symbols@1.1.0: + resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==} + engines: {node: '>= 0.4'} + + has-tostringtag@1.0.2: + resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==} + engines: {node: '>= 0.4'} + + hasown@2.0.2: + resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} + engines: {node: '>= 0.4'} + + he@1.2.0: + resolution: {integrity: sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==} + hasBin: true + + hoist-non-react-statics@3.3.2: + resolution: {integrity: sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==} + + hono@4.12.23: + resolution: {integrity: sha512-eIaZ9qDgu7XV0pxOCrg7/WhnQ6Ivm22UcxhXx/A3dcbqbbYgBEkc6e/J/s7j2tS96zoB0S9VBdLwQNCWwUo4LA==} + engines: {node: '>=16.9.0'} + + hpack.js@2.1.6: + resolution: {integrity: sha512-zJxVehUdMGIKsRaNt7apO2Gqp0BdqW5yaiGHXXmbpvxgBYVZnAql+BJb4RO5ad2MgpbZKn5G6nMnegrH1FcNYQ==} + + html-minifier-terser@6.1.0: + resolution: {integrity: sha512-YXxSlJBZTP7RS3tWnQw74ooKa6L9b9i9QYXY21eUEvhZ3u9XLfv6OnFsQq6RxkhHygsaUMvYsZRV5rU/OVNZxw==} + engines: {node: '>=12'} + hasBin: true + + html-webpack-plugin@5.6.3: + resolution: {integrity: sha512-QSf1yjtSAsmf7rYBV7XX86uua4W/vkhIt0xNXKbsi2foEeW7vjJQz4bhnpL3xH+l1ryl1680uNv968Z+X6jSYg==} + engines: {node: '>=10.13.0'} + peerDependencies: + '@rspack/core': 0.x || 1.x + webpack: 5.107.1 + peerDependenciesMeta: + '@rspack/core': + optional: true + webpack: + optional: true + + htmlparser2@6.1.0: + resolution: {integrity: sha512-gyyPk6rgonLFEDGoeRgQNaEUvdJ4ktTmmUh/h2t7s+M8oPpIPxgNACWa+6ESR57kXstwqPiCut0V8NRpcwgU7A==} + + http-deceiver@1.2.7: + resolution: {integrity: sha512-LmpOGxTfbpgtGVxJrj5k7asXHCgNZp5nLfp+hWc8QQRqtb7fUy6kRY3BO1h9ddF6yIPYUARgxGOwB42DnxIaNw==} + + http-errors@1.8.1: + resolution: {integrity: sha512-Kpk9Sm7NmI+RHhnj6OIWDI1d6fIoFAtFt9RLaTMRlg/8w49juAStsrBgp0Dp4OdxdVbRIeKhtCUvoi/RuAhO4g==} + engines: {node: '>= 0.6'} + + http-errors@2.0.1: + resolution: {integrity: sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==} + engines: {node: '>= 0.8'} + + http-parser-js@0.5.10: + resolution: {integrity: sha512-Pysuw9XpUq5dVc/2SMHpuTY01RFl8fttgcyunjL7eEMhGM3cI4eOmiCycJDVCo/7O7ClfQD3SaI6ftDzqOXYMA==} + + http-proxy-middleware@2.0.9: + resolution: {integrity: sha512-c1IyJYLYppU574+YI7R4QyX2ystMtVXZwIdzazUIPIJsHuWNd+mho2j+bKoHftndicGj9yh+xjd+l0yj7VeT1Q==} + engines: {node: '>=12.0.0'} + peerDependencies: + '@types/express': ^4.17.13 + peerDependenciesMeta: + '@types/express': + optional: true + + http-proxy@1.18.1: + resolution: {integrity: sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==} + engines: {node: '>=8.0.0'} + + human-id@4.2.0: + resolution: {integrity: sha512-K3GbkIWqyvvlpfhBPlbEvD97TtqBpAYA4kt+cn2lD2x2HuohzZCibcA2nOlnJT6exqvJLggoB5nv2dNf192nEA==} + hasBin: true + + hyperdyperid@1.2.0: + resolution: {integrity: sha512-Y93lCzHYgGWdrJ66yIktxiaGULYc6oGiABxhcO5AufBeOyoIdZF7bIfLaOrbM0iGIOXQQgxxRrFEnb+Y6w1n4A==} + engines: {node: '>=10.18'} + + iconv-lite@0.4.24: + resolution: {integrity: sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==} + engines: {node: '>=0.10.0'} + + iconv-lite@0.6.3: + resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==} + engines: {node: '>=0.10.0'} + + iconv-lite@0.7.2: + resolution: {integrity: sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw==} + engines: {node: '>=0.10.0'} + + icss-utils@5.1.0: + resolution: {integrity: sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==} + engines: {node: ^10 || ^12 || >= 14} + peerDependencies: + postcss: 8.5.15 + + ieee754@1.2.1: + resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} + + ignore@5.3.2: + resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} + engines: {node: '>= 4'} + + image-size@0.5.5: + resolution: {integrity: sha512-6TDAlDPZxUFCv+fuOkIoXT/V/f3Qbq8e37p+YOiYrUv3v9cc3/6x78VdfPgFVaB9dZYeLUfKgHRebpkm/oP2VQ==} + engines: {node: '>=0.10.0'} + hasBin: true + + immutable@5.1.5: + resolution: {integrity: sha512-t7xcm2siw+hlUM68I+UEOK+z84RzmN59as9DZ7P1l0994DKUWV7UXBMQZVxaoMSRQ+PBZbHCOoBt7a2wxOMt+A==} + + import-fresh@3.3.1: + resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==} + engines: {node: '>=6'} + + import-local@3.2.0: + resolution: {integrity: sha512-2SPlun1JUPWoM6t3F0dw0FkCF/jWY8kttcY4f599GLTSjh2OCuuhdTkJQsEcZzBqbXZGKMK2OqW1oZsjtf/gQA==} + engines: {node: '>=8'} + hasBin: true + + imurmurhash@0.1.4: + resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} + engines: {node: '>=0.8.19'} + + indent-string@4.0.0: + resolution: {integrity: sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==} + engines: {node: '>=8'} + + 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. + + inherits@2.0.4: + resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} + + internal-slot@1.1.0: + resolution: {integrity: sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==} + engines: {node: '>= 0.4'} + + interpret@1.4.0: + resolution: {integrity: sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA==} + engines: {node: '>= 0.10'} + + interpret@3.1.1: + resolution: {integrity: sha512-6xwYfHbajpoF0xLW+iwLkhwgvLoZDfjYfoFNu8ftMoXINzwuymNLd9u/KmwtdT2GbR+/Cz66otEGEVVUHX9QLQ==} + engines: {node: '>=10.13.0'} + + ip-address@10.2.0: + resolution: {integrity: sha512-/+S6j4E9AHvW9SWMSEY9Xfy66O5PWvVEJ08O0y5JGyEKQpojb0K0GKpz/v5HJ/G0vi3D2sjGK78119oXZeE0qA==} + engines: {node: '>= 12'} + + ipaddr.js@1.9.1: + resolution: {integrity: sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==} + engines: {node: '>= 0.10'} + + ipaddr.js@2.3.0: + resolution: {integrity: sha512-Zv/pA+ciVFbCSBBjGfaKUya/CcGmUHzTydLMaTwrUUEM2DIEO3iZvueGxmacvmN50fGpGVKeTXpb2LcYQxeVdg==} + engines: {node: '>= 10'} + + is-arguments@1.2.0: + resolution: {integrity: sha512-7bVbi0huj/wrIAOzb8U1aszg9kdi3KN/CyU19CTI7tAoZYEZoL9yCDXpbXN+uPsuWnP02cyug1gleqq+TU+YCA==} + engines: {node: '>= 0.4'} + + is-array-buffer@3.0.5: + resolution: {integrity: sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==} + engines: {node: '>= 0.4'} + + is-arrayish@0.2.1: + resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==} + + is-bigint@1.1.0: + resolution: {integrity: sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==} + engines: {node: '>= 0.4'} + + is-binary-path@2.1.0: + resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==} + engines: {node: '>=8'} + + is-boolean-object@1.2.2: + resolution: {integrity: sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A==} + engines: {node: '>= 0.4'} + + is-callable@1.2.7: + 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-date-object@1.1.0: + resolution: {integrity: sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==} + engines: {node: '>= 0.4'} + + is-docker@2.2.1: + resolution: {integrity: sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==} + engines: {node: '>=8'} + hasBin: true + + is-docker@3.0.0: + resolution: {integrity: sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + hasBin: true + + is-extglob@2.1.1: + resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} + engines: {node: '>=0.10.0'} + + is-fullwidth-code-point@3.0.0: + resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} + engines: {node: '>=8'} + + is-glob@4.0.3: + resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} + engines: {node: '>=0.10.0'} + + is-inside-container@1.0.0: + resolution: {integrity: sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==} + engines: {node: '>=14.16'} + hasBin: true + + is-map@2.0.3: + resolution: {integrity: sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==} + engines: {node: '>= 0.4'} + + is-mobile@5.0.0: + resolution: {integrity: sha512-Tz/yndySvLAEXh+Uk8liFCxOwVH6YutuR74utvOcu7I9Di+DwM0mtdPVZNaVvvBUM2OXxne/NhOs1zAO7riusQ==} + + is-network-error@1.3.1: + resolution: {integrity: sha512-6QCxa49rQbmUWLfk0nuGqzql9U8uaV2H6279bRErPBHe/109hCzsLUBUHfbEtvLIHBd6hyXbgedBSHevm43Edw==} + engines: {node: '>=16'} + + is-number-object@1.1.1: + resolution: {integrity: sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==} + engines: {node: '>= 0.4'} + + is-number@7.0.0: + resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} + engines: {node: '>=0.12.0'} + + is-plain-obj@3.0.0: + resolution: {integrity: sha512-gwsOE28k+23GP1B6vFl1oVh/WOzmawBrKwo5Ev6wMKzPkaXaCDIQKzLnvsA42DRlbVTWorkgTKIviAKCWkfUwA==} + engines: {node: '>=10'} + + is-plain-object@2.0.4: + resolution: {integrity: sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==} + engines: {node: '>=0.10.0'} + + is-promise@4.0.0: + resolution: {integrity: sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==} + + is-regex@1.2.1: + resolution: {integrity: sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==} + engines: {node: '>= 0.4'} + + is-set@2.0.3: + resolution: {integrity: sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==} + engines: {node: '>= 0.4'} + + is-shared-array-buffer@1.0.4: + resolution: {integrity: sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A==} + engines: {node: '>= 0.4'} + + is-stream@1.1.0: + resolution: {integrity: sha512-uQPm8kcs47jx38atAcWTVxyltQYoPT68y9aWYdV6yWXSyW8mzSat0TL6CiWdZeCdF3KrAvpVtnHbTv4RN+rqdQ==} + engines: {node: '>=0.10.0'} + + is-string@1.1.1: + resolution: {integrity: sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA==} + engines: {node: '>= 0.4'} + + is-subdir@1.2.0: + resolution: {integrity: sha512-2AT6j+gXe/1ueqbW6fLZJiIw3F8iXGJtt0yDrZaBhAZEG1raiTxKWU+IPqMCzQAXOUCKdA4UDMgacKH25XG2Cw==} + engines: {node: '>=4'} + + is-symbol@1.1.1: + resolution: {integrity: sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w==} + engines: {node: '>= 0.4'} + + is-weakmap@2.0.2: + resolution: {integrity: sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==} + engines: {node: '>= 0.4'} + + is-weakset@2.0.4: + resolution: {integrity: sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==} + engines: {node: '>= 0.4'} + + is-what@3.14.1: + resolution: {integrity: sha512-sNxgpk9793nzSs7bA6JQJGeIuRBQhAaNGG77kzYQgMkrID+lS6SlK07K5LaptscDlSaIgH+GPFzf+d75FVxozA==} + + is-windows@1.0.2: + resolution: {integrity: sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==} + engines: {node: '>=0.10.0'} + + is-wsl@2.2.0: + resolution: {integrity: sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==} + engines: {node: '>=8'} + + is-wsl@3.1.1: + resolution: {integrity: sha512-e6rvdUCiQCAuumZslxRJWR/Doq4VpPR82kqclvcS0efgt430SlGIk05vdCN58+VrzgtIcfNODjozVielycD4Sw==} + engines: {node: '>=16'} + + isarray@1.0.0: + resolution: {integrity: sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==} + + isarray@2.0.5: + resolution: {integrity: sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==} + + isexe@2.0.0: + resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + + isobject@3.0.1: + resolution: {integrity: sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==} + engines: {node: '>=0.10.0'} + + jake@10.9.4: + resolution: {integrity: sha512-wpHYzhxiVQL+IV05BLE2Xn34zW1S223hvjtqk0+gsPrwd/8JNLXJgZZM/iPFsYc1xyphF+6M6EvdE5E9MBGkDA==} + engines: {node: '>=10'} + hasBin: true + + jest-diff@30.0.4: + resolution: {integrity: sha512-TSjceIf6797jyd+R64NXqicttROD+Qf98fex7CowmlSn7f8+En0da1Dglwr1AXxDtVizoxXYZBlUQwNhoOXkNw==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + jest-matcher-utils@30.0.4: + resolution: {integrity: sha512-ubCewJ54YzeAZ2JeHHGVoU+eDIpQFsfPQs0xURPWoNiO42LGJ+QGgfSf+hFIRplkZDkhH5MOvuxHKXRTUU3dUQ==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + jest-message-util@30.0.2: + resolution: {integrity: sha512-vXywcxmr0SsKXF/bAD7t7nMamRvPuJkras00gqYeB1V0WllxZrbZ0paRr3XqpFU2sYYjD0qAaG2fRyn/CGZ0aw==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + jest-mock@30.0.2: + resolution: {integrity: sha512-PnZOHmqup/9cT/y+pXIVbbi8ID6U1XHRmbvR7MvUy4SLqhCbwpkmXhLbsWbGewHrV5x/1bF7YDjs+x24/QSvFA==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + jest-regex-util@30.0.1: + resolution: {integrity: sha512-jHEQgBXAgc+Gh4g0p3bCevgRCVRkB4VB70zhoAE48gxeSr1hfUOsM/C2WoJgVL7Eyg//hudYENbm3Ne+/dRVVA==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + jest-util@30.0.2: + resolution: {integrity: sha512-8IyqfKS4MqprBuUpZNlFB5l+WFehc8bfCe1HSZFHzft2mOuND8Cvi9r1musli+u6F3TqanCZ/Ik4H4pXUolZIg==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + jest-worker@27.5.1: + resolution: {integrity: sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==} + engines: {node: '>= 10.13.0'} + + jose@6.2.3: + resolution: {integrity: sha512-YYVDInQKFJfR/xa3ojUTl8c2KoTwiL1R5Wg9YCydwH0x0B9grbzlg5HC7mMjCtUJjbQ/YnGEZIhI5tCgfTb4Hw==} + + js-sdsl@4.4.2: + resolution: {integrity: sha512-dwXFwByc/ajSV6m5bcKAPwe4yDDF6D614pxmIi5odytzxRlwqF6nwoiCek80Ixc7Cvma5awClxrzFtxCQvcM8w==} + + js-tokens@4.0.0: + resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} + + js-yaml@3.14.1: + resolution: {integrity: sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==} + hasBin: true + + js-yaml@4.1.1: + resolution: {integrity: sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==} + hasBin: true + + jsesc@3.1.0: + resolution: {integrity: sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==} + engines: {node: '>=6'} + hasBin: true + + json-buffer@3.0.1: + resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} + + json-parse-even-better-errors@2.3.1: + resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==} + + json-schema-traverse@0.4.1: + resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} + + json-schema-traverse@1.0.0: + resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==} + + json-schema-typed@8.0.2: + resolution: {integrity: sha512-fQhoXdcvc3V28x7C7BMs4P5+kNlgUURe2jmUT1T//oBRMDrqy1QPelJimwZGo7Hg9VPV3EQV5Bnq4hbFy2vetA==} + + json-stable-stringify-without-jsonify@1.0.1: + resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} + + json2mq@0.2.0: + resolution: {integrity: sha512-SzoRg7ux5DWTII9J2qkrZrqV1gt+rTaoufMxEzXbS26Uid0NwaJd123HcoB80TgubEppxxIGdNxCx50fEoEWQA==} + + json5@2.2.3: + resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==} + engines: {node: '>=6'} + hasBin: true + + jsonc-parser@3.2.0: + resolution: {integrity: sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w==} + + jsonfile@4.0.0: + resolution: {integrity: sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==} + + jsonfile@6.2.1: + resolution: {integrity: sha512-zwOTdL3rFQ/lRdBnntKVOX6k5cKJwEc1HdilT71BWEu7J41gXIB2MRp+vxduPSwZJPWBxEzv4yH1wYLJGUHX4Q==} + + keyv@4.5.4: + resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} + + kind-of@6.0.3: + resolution: {integrity: sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==} + engines: {node: '>=0.10.0'} + + klona@2.0.6: + resolution: {integrity: sha512-dhG34DXATL5hSxJbIexCft8FChFXtmskoZYnoPWjXQuebWYCNkVeV3KkGegCK9CP1oswI/vQibS2GY7Em/sJJA==} + engines: {node: '>= 8'} + + launch-editor@2.13.2: + resolution: {integrity: sha512-4VVDnbOpLXy/s8rdRCSXb+zfMeFR0WlJWpET1iA9CQdlZDfwyLjUuGQzXU4VeOoey6AicSAluWan7Etga6Kcmg==} + + less-loader@11.1.0: + resolution: {integrity: sha512-C+uDBV7kS7W5fJlUjq5mPBeBVhYpTIm5gB09APT9o3n/ILeaXVsiSFTbZpTJCJwQ/Crczfn3DmfQFwxYusWFug==} + engines: {node: '>= 14.15.0'} + peerDependencies: + less: ^3.5.0 || ^4.0.0 + webpack: 5.107.1 + + less@4.1.3: + resolution: {integrity: sha512-w16Xk/Ta9Hhyei0Gpz9m7VS8F28nieJaL/VyShID7cYvP6IL5oHeL6p4TXSDJqZE/lNv0oJ2pGVjJsRkfwm5FA==} + engines: {node: '>=6'} + hasBin: true + + levn@0.4.1: + resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} + engines: {node: '>= 0.8.0'} + + license-webpack-plugin@4.0.2: + resolution: {integrity: sha512-771TFWFD70G1wLTC4oU2Cw4qvtmNrIw+wRvBtn+okgHl7slJVi7zfNcdmqDL72BojM30VNJ2UHylr1o77U37Jw==} + peerDependencies: + webpack: '*' + peerDependenciesMeta: + webpack: + optional: true + + lilconfig@2.1.0: + resolution: {integrity: sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==} + engines: {node: '>=10'} + + lines-and-columns@1.2.4: + resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} + + lines-and-columns@2.0.4: + resolution: {integrity: sha512-wM1+Z03eypVAVUCE7QdSqpVIvelbOakn1M0bPDoA4SGWPx3sNDVUiMo3L6To6WWGClB7VyXnhQ4Sn7gxiJbE6A==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + loader-runner@4.3.2: + resolution: {integrity: sha512-DFEqQ3ihfS9blba08cLfYf1NRAIEm+dDjic073DRDc3/JspI/8wYmtDsHwd3+4hwvdxSK7PGaElfTmm0awWJ4w==} + engines: {node: '>=6.11.5'} + + loader-utils@2.0.4: + resolution: {integrity: sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==} + engines: {node: '>=8.9.0'} + + locate-path@5.0.0: + resolution: {integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==} + engines: {node: '>=8'} + + locate-path@6.0.0: + 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} + + lodash.debounce@4.0.8: + resolution: {integrity: sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==} + + lodash.memoize@4.1.2: + resolution: {integrity: sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==} + + lodash.merge@4.6.2: + resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} + + lodash.mergewith@4.6.2: + resolution: {integrity: sha512-GK3g5RPZWTRSeLSpgP8Xhra+pnjBC56q9FZYe1d5RN3TJ35dbkGy3YqBSMbyCrlbi+CM9Z3Jk5yTL7RCsqboyQ==} + + lodash.startcase@4.4.0: + resolution: {integrity: sha512-+WKqsK294HMSc2jEbNgpHpd0JfIBhp7rEV4aqXWqFr6AlXov+SlcgB1Fv01y2kGe3Gc8nMW7VA0SrGuSkRfIEg==} + + lodash.uniq@4.5.0: + resolution: {integrity: sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==} + + lodash@4.18.1: + resolution: {integrity: sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==} + + loose-envify@1.4.0: + resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==} + hasBin: true + + lower-case@2.0.2: + resolution: {integrity: sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==} + + lru-cache@5.1.1: + resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} + + lru-cache@6.0.0: + resolution: {integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==} + engines: {node: '>=10'} + + lz-string@1.5.0: + resolution: {integrity: sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==} + hasBin: true + + make-dir@2.1.0: + resolution: {integrity: sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==} + engines: {node: '>=6'} + + make-error@1.3.6: + resolution: {integrity: sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==} + + math-intrinsics@1.1.0: + resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} + engines: {node: '>= 0.4'} + + mdn-data@2.0.14: + resolution: {integrity: sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==} + + media-typer@0.3.0: + resolution: {integrity: sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==} + engines: {node: '>= 0.6'} + + media-typer@1.1.0: + resolution: {integrity: sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==} + engines: {node: '>= 0.8'} + + memfs@3.5.3: + resolution: {integrity: sha512-UERzLsxzllchadvbPs5aolHh65ISpKpM+ccLbOJ8/vvpBKmAWf+la7dXFy7Mr0ySHbdHrFv5kGFCUHHe6GFEmw==} + engines: {node: '>= 4.0.0'} + + memfs@4.57.2: + resolution: {integrity: sha512-2nWzSsJzrukurSDna4Z0WywuScK4Id3tSKejgu74u8KCdW4uNrseKRSIDg75C6Yw5ZRqBe0F0EtMNlTbUq8bAQ==} + peerDependencies: + tslib: '2' + + merge-descriptors@1.0.3: + resolution: {integrity: sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==} + + merge-descriptors@2.0.0: + resolution: {integrity: sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==} + engines: {node: '>=18'} + + merge-stream@2.0.0: + resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==} + + merge2@1.4.1: + resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} + engines: {node: '>= 8'} + + methods@1.1.2: + resolution: {integrity: sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==} + engines: {node: '>= 0.6'} + + micromatch@4.0.8: + resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==} + engines: {node: '>=8.6'} + + mime-db@1.52.0: + resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} + engines: {node: '>= 0.6'} + + mime-db@1.54.0: + resolution: {integrity: sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==} + engines: {node: '>= 0.6'} + + mime-types@2.1.35: + resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} + engines: {node: '>= 0.6'} + + mime-types@3.0.1: + resolution: {integrity: sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA==} + engines: {node: '>= 0.6'} + + mime-types@3.0.2: + resolution: {integrity: sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==} + engines: {node: '>=18'} + + mime@1.6.0: + resolution: {integrity: sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==} + engines: {node: '>=4'} + hasBin: true + + mimic-fn@2.1.0: + resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==} + engines: {node: '>=6'} + + min-indent@1.0.1: + resolution: {integrity: sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==} + engines: {node: '>=4'} + + mini-css-extract-plugin@2.4.7: + resolution: {integrity: sha512-euWmddf0sk9Nv1O0gfeeUAvAkoSlWncNLF77C0TP2+WoPvy8mAHKOzMajcCz2dzvyt3CNgxb1obIEVFIRxaipg==} + engines: {node: '>= 12.13.0'} + peerDependencies: + webpack: 5.107.1 + + minimalistic-assert@1.0.1: + resolution: {integrity: sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==} + + minimatch@3.1.5: + resolution: {integrity: sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==} + + minimist@1.2.8: + resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} + + mkdirp@1.0.4: + resolution: {integrity: sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==} + engines: {node: '>=10'} + hasBin: true + + motion-dom@12.38.0: + resolution: {integrity: sha512-pdkHLD8QYRp8VfiNLb8xIBJis1byQ9gPT3Jnh2jqfFtAsWUA3dEepDlsWe/xMpO8McV+VdpKVcp+E+TGJEtOoA==} + + motion-utils@12.36.0: + resolution: {integrity: sha512-eHWisygbiwVvf6PZ1vhaHCLamvkSbPIeAYxWUuL3a2PD/TROgE7FvfHWTIH4vMl798QLfMw15nRqIaRDXTlYRg==} + + mri@1.2.0: + resolution: {integrity: sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==} + engines: {node: '>=4'} + + ms@2.0.0: + resolution: {integrity: sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==} + + ms@2.1.3: + resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + + multicast-dns@7.2.5: + resolution: {integrity: sha512-2eznPJP8z2BFLX50tf0LuODrpINqP1RVIm/CObbTcBRITQgmC/TjcREF1NeTBzIcR5XO/ukWo+YHOjBbFwIupg==} + hasBin: true + + nanoid@3.3.12: + resolution: {integrity: sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + + natural-compare@1.4.0: + resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} + + needle@3.5.0: + resolution: {integrity: sha512-jaQyPKKk2YokHrEg+vFDYxXIHTCBgiZwSHOoVx/8V3GIBS8/VN6NdVRmg8q1ERtPkMvmOvebsgga4sAj5hls/w==} + engines: {node: '>= 4.4.x'} + hasBin: true + + negotiator@0.6.3: + resolution: {integrity: sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==} + engines: {node: '>= 0.6'} + + negotiator@0.6.4: + resolution: {integrity: sha512-myRT3DiWPHqho5PrJaIRyaMv2kgYf0mUVgBNOYMuCH5Ki1yEiQaf/ZJuQ62nvpc44wL5WDbTX7yGJi1Neevw8w==} + engines: {node: '>= 0.6'} + + negotiator@1.0.0: + resolution: {integrity: sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==} + engines: {node: '>= 0.6'} + + neo-async@2.6.2: + resolution: {integrity: sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==} + + next@15.5.18: + resolution: {integrity: sha512-eKL8zUJkX9Y5lE+RX/2YJoItVdGlIscyVyboeD9wSpp0PaGqjoA4tTpT2qPqz9ax+5IzGESyLSeZ/RCwbSZ2uQ==} + engines: {node: ^18.18.0 || ^19.8.0 || >= 20.0.0} + hasBin: true + peerDependencies: + '@opentelemetry/api': ^1.1.0 + '@playwright/test': ^1.51.1 + babel-plugin-react-compiler: '*' + react: ^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0 + react-dom: ^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0 + sass: ^1.3.0 + peerDependenciesMeta: + '@opentelemetry/api': + optional: true + '@playwright/test': + optional: true + babel-plugin-react-compiler: + optional: true + sass: + optional: true + + nice-try@1.0.5: + resolution: {integrity: sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==} + + no-case@3.0.4: + resolution: {integrity: sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==} + + node-abort-controller@3.1.1: + resolution: {integrity: sha512-AGK2yQKIjRuqnc6VkX2Xj5d+QW8xZ87pa1UK6yA6ouUyuxfHuMP6umE5QK7UmTeOAymo+Zx1Fxiuw9rVx8taHQ==} + + node-addon-api@3.2.1: + resolution: {integrity: sha512-mmcei9JghVNDYydghQmeDX8KoAm0FAiYyIcUt/N4nhyAipB17pllZQDOJD2fotxABnt4Mdz+dKTO7eftLg4d0A==} + + node-addon-api@7.1.1: + resolution: {integrity: sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==} + + node-gyp-build@4.8.4: + resolution: {integrity: sha512-LA4ZjwlnUblHVgq0oBF3Jl/6h/Nvs5fzBLwdEF4nuxnFdsfajde4WfxtJr3CaiH+F6ewcIB/q4jQ4UzPyid+CQ==} + hasBin: true + + node-releases@2.0.19: + resolution: {integrity: sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==} + + node-releases@2.0.38: + resolution: {integrity: sha512-3qT/88Y3FbH/Kx4szpQQ4HzUbVrHPKTLVpVocKiLfoYvw9XSGOX2FmD2d6DrXbVYyAQTF2HeF6My8jmzx7/CRw==} + + normalize-path@3.0.0: + resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} + engines: {node: '>=0.10.0'} + + normalize-url@6.1.0: + resolution: {integrity: sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==} + engines: {node: '>=10'} + + npm-run-path@2.0.2: + resolution: {integrity: sha512-lJxZYlT4DW/bRUtFh1MQIWqmLwQfAxnqWG4HhEdjMlkrJYnJn0Jrr2u3mgxqaWsdiBc76TYkTG/mhrnYTuzfHw==} + engines: {node: '>=4'} + + npm-run-path@4.0.1: + resolution: {integrity: sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==} + engines: {node: '>=8'} + + nth-check@2.1.1: + resolution: {integrity: sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==} + + nx@15.9.7: + resolution: {integrity: sha512-1qlEeDjX9OKZEryC8i4bA+twNg+lB5RKrozlNwWx/lLJHqWPUfvUTvxh+uxlPYL9KzVReQjUuxMLFMsHNqWUrA==} + hasBin: true + peerDependencies: + '@swc-node/register': ^1.4.2 + '@swc/core': ^1.2.173 + peerDependenciesMeta: + '@swc-node/register': + optional: true + '@swc/core': + optional: true + + object-assign@4.1.1: + resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} + engines: {node: '>=0.10.0'} + + object-inspect@1.13.4: + resolution: {integrity: sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==} + engines: {node: '>= 0.4'} + + object-is@1.1.6: + resolution: {integrity: sha512-F8cZ+KfGlSGi09lJT7/Nd6KJZ9ygtvYC0/UYYLI9nmQKLMnydpB9yvbv9K1uSkEu7FU9vYPmVwLg328tX+ot3Q==} + engines: {node: '>= 0.4'} + + object-keys@1.1.1: + resolution: {integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==} + engines: {node: '>= 0.4'} + + object.assign@4.1.7: + resolution: {integrity: sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==} + engines: {node: '>= 0.4'} + + obuf@1.1.2: + resolution: {integrity: sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==} + + on-finished@2.4.1: + resolution: {integrity: sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==} + engines: {node: '>= 0.8'} + + on-headers@1.1.0: + resolution: {integrity: sha512-737ZY3yNnXy37FHkQxPzt4UZ2UWPWiCZWLvFZ4fu5cueciegX0zGPnrlY6bwRg4FdQOe9YU8MkmJwGhoMybl8A==} + engines: {node: '>= 0.8'} + + once@1.4.0: + resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} + + onetime@5.1.2: + resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==} + engines: {node: '>=6'} + + open@10.2.0: + resolution: {integrity: sha512-YgBpdJHPyQ2UE5x+hlSXcnejzAvD0b22U2OuAP+8OnlJT+PjWPxtgmGqKKc+RgTM63U9gN0YzrYc71R2WT/hTA==} + engines: {node: '>=18'} + + open@8.4.2: + resolution: {integrity: sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ==} + engines: {node: '>=12'} + + optionator@0.9.4: + resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} + engines: {node: '>= 0.8.0'} + + outdent@0.5.0: + resolution: {integrity: sha512-/jHxFIzoMXdqPzTaCpFzAAWhpkSjZPF4Vsn6jAfNpmbH/ymsmd7Qc6VE9BGn0L6YMj6uwpQLxCECpus4ukKS9Q==} + + p-filter@2.1.0: + resolution: {integrity: sha512-ZBxxZ5sL2HghephhpGAQdoskxplTwr7ICaehZwLIlfL6acuVgZPm8yBNuRAFBGEqtD/hmUeq9eqLg2ys9Xr/yw==} + engines: {node: '>=8'} + + p-finally@1.0.0: + resolution: {integrity: sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow==} + engines: {node: '>=4'} + + p-limit@2.3.0: + resolution: {integrity: sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==} + engines: {node: '>=6'} + + p-limit@3.1.0: + 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@4.1.0: + resolution: {integrity: sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==} + engines: {node: '>=8'} + + p-locate@5.0.0: + 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@2.1.0: + resolution: {integrity: sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw==} + engines: {node: '>=6'} + + p-retry@6.2.1: + resolution: {integrity: sha512-hEt02O4hUct5wtwg4H4KcWgDdm+l1bOaEy/hWzd8xtXB9BqxTWBBhb+2ImAtH4Cv4rPjV76xN3Zumqk3k3AhhQ==} + engines: {node: '>=16.17'} + + p-try@2.2.0: + resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==} + engines: {node: '>=6'} + + package-manager-detector@0.2.11: + resolution: {integrity: sha512-BEnLolu+yuz22S56CU1SUKq3XC3PkwD5wv4ikR4MfGvnRVcmzXR9DwSlW2fEamyTPyXHomBJRzgapeuBvRNzJQ==} + + param-case@3.0.4: + resolution: {integrity: sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A==} + + parent-module@1.0.1: + resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} + engines: {node: '>=6'} + + parse-json@5.2.0: + resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==} + engines: {node: '>=8'} + + parse-node-version@1.0.1: + resolution: {integrity: sha512-3YHlOa/JgH6Mnpr05jP9eDG254US9ek25LyIxZlDItp2iJtwyaXQb57lBYLdT3MowkUFYEV2XXNAYIPlESvJlA==} + engines: {node: '>= 0.10'} + + parse5@4.0.0: + resolution: {integrity: sha512-VrZ7eOd3T1Fk4XWNXMgiGBK/z0MG48BWG2uQNU4I72fkQuKUTZpl+u9k+CxEG0twMVzSmXEEz12z5Fnw1jIQFA==} + + parseurl@1.3.3: + resolution: {integrity: sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==} + engines: {node: '>= 0.8'} + + pascal-case@3.1.2: + resolution: {integrity: sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g==} + + path-exists@4.0.0: + 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'} + + path-key@2.0.1: + resolution: {integrity: sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw==} + engines: {node: '>=4'} + + path-key@3.1.1: + resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} + engines: {node: '>=8'} + + path-parse@1.0.7: + resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} + + path-to-regexp@0.1.13: + resolution: {integrity: sha512-A/AGNMFN3c8bOlvV9RreMdrv7jsmF9XIfDeCd87+I8RNg6s78BhJxMu69NEMHBSJFxKidViTEdruRwEk/WIKqA==} + + path-to-regexp@8.4.2: + resolution: {integrity: sha512-qRcuIdP69NPm4qbACK+aDogI5CBDMi1jKe0ry5rSQJz8JVLsC7jV8XpiJjGRLLol3N+R5ihGYcrPLTno6pAdBA==} + + path-type@4.0.0: + resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} + engines: {node: '>=8'} + + picocolors@1.1.1: + resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} + + picomatch@2.3.2: + resolution: {integrity: sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==} + engines: {node: '>=8.6'} + + picomatch@4.0.4: + resolution: {integrity: sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==} + engines: {node: '>=12'} + + pify@2.3.0: + resolution: {integrity: sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==} + engines: {node: '>=0.10.0'} + + pify@4.0.1: + resolution: {integrity: sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==} + engines: {node: '>=6'} + + pkce-challenge@5.0.1: + resolution: {integrity: sha512-wQ0b/W4Fr01qtpHlqSqspcj3EhBvimsdh0KlHhH8HRZnMsEa0ea2fTULOXOS9ccQr3om+GcGRk4e+isrZWV8qQ==} + engines: {node: '>=16.20.0'} + + pkg-dir@4.2.0: + resolution: {integrity: sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==} + engines: {node: '>=8'} + + pkg-dir@7.0.0: + resolution: {integrity: sha512-Ie9z/WINcxxLp27BKOCHGde4ITq9UklYKDzVo1nhk5sqGEXU3FpkwP5GM2voTGJkGd9B3Otl+Q4uwSOeSUtOBA==} + engines: {node: '>=14.16'} + + pkijs@3.4.0: + resolution: {integrity: sha512-emEcLuomt2j03vxD54giVB4SxTjnsqkU692xZOZXHDVoYyypEm+b3jpiTcc+Cf+myooc+/Ly0z01jqeNHVgJGw==} + engines: {node: '>=16.0.0'} + + playwright-core@1.59.1: + resolution: {integrity: sha512-HBV/RJg81z5BiiZ9yPzIiClYV/QMsDCKUyogwH9p3MCP6IYjUFu/MActgYAvK0oWyV9NlwM3GLBjADyWgydVyg==} + engines: {node: '>=18'} + hasBin: true + + playwright@1.59.1: + resolution: {integrity: sha512-C8oWjPR3F81yljW9o5OxcWzfh6avkVwDD2VYdwIGqTkl+OGFISgypqzfu7dOe4QNLL2aqcWBmI3PMtLIK233lw==} + engines: {node: '>=18'} + hasBin: true + + possible-typed-array-names@1.1.0: + resolution: {integrity: sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==} + engines: {node: '>= 0.4'} + + postcss-calc@8.2.4: + resolution: {integrity: sha512-SmWMSJmB8MRnnULldx0lQIyhSNvuDl9HfrZkaqqE/WHAhToYsAvDq+yAsA/kIyINDszOp3Rh0GFoNuH5Ypsm3Q==} + peerDependencies: + postcss: 8.5.15 + + postcss-colormin@5.3.1: + resolution: {integrity: sha512-UsWQG0AqTFQmpBegeLLc1+c3jIqBNB0zlDGRWR+dQ3pRKJL1oeMzyqmH3o2PIfn9MBdNrVPWhDbT769LxCTLJQ==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: 8.5.15 + + postcss-convert-values@5.1.3: + resolution: {integrity: sha512-82pC1xkJZtcJEfiLw6UXnXVXScgtBrjlO5CBmuDQc+dlb88ZYheFsjTn40+zBVi3DkfF7iezO0nJUPLcJK3pvA==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: 8.5.15 + + postcss-discard-comments@5.1.2: + resolution: {integrity: sha512-+L8208OVbHVF2UQf1iDmRcbdjJkuBF6IS29yBDSiWUIzpYaAhtNl6JYnYm12FnkeCwQqF5LeklOu6rAqgfBZqQ==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: 8.5.15 + + postcss-discard-duplicates@5.1.0: + resolution: {integrity: sha512-zmX3IoSI2aoenxHV6C7plngHWWhUOV3sP1T8y2ifzxzbtnuhk1EdPwm0S1bIUNaJ2eNbWeGLEwzw8huPD67aQw==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: 8.5.15 + + postcss-discard-empty@5.1.1: + resolution: {integrity: sha512-zPz4WljiSuLWsI0ir4Mcnr4qQQ5e1Ukc3i7UfE2XcrwKK2LIPIqE5jxMRxO6GbI3cv//ztXDsXwEWT3BHOGh3A==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: 8.5.15 + + postcss-discard-overridden@5.1.0: + resolution: {integrity: sha512-21nOL7RqWR1kasIVdKs8HNqQJhFxLsyRfAnUDm4Fe4t4mCWL9OJiHvlHPjcd8zc5Myu89b/7wZDnOSjFgeWRtw==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: 8.5.15 + + postcss-import@14.1.0: + resolution: {integrity: sha512-flwI+Vgm4SElObFVPpTIT7SU7R3qk2L7PyduMcokiaVKuWv9d/U+Gm/QAd8NDLuykTWTkcrjOeD2Pp1rMeBTGw==} + engines: {node: '>=10.0.0'} + peerDependencies: + postcss: 8.5.15 + + postcss-loader@6.2.1: + resolution: {integrity: sha512-WbbYpmAaKcux/P66bZ40bpWsBucjx/TTgVVzRZ9yUO8yQfVBlameJ0ZGVaPfH64hNSBh63a+ICP5nqOpBA0w+Q==} + engines: {node: '>= 12.13.0'} + peerDependencies: + postcss: 8.5.15 + webpack: 5.107.1 + + postcss-merge-longhand@5.1.7: + resolution: {integrity: sha512-YCI9gZB+PLNskrK0BB3/2OzPnGhPkBEwmwhfYk1ilBHYVAZB7/tkTHFBAnCrvBBOmeYyMYw3DMjT55SyxMBzjQ==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: 8.5.15 + + postcss-merge-rules@5.1.4: + resolution: {integrity: sha512-0R2IuYpgU93y9lhVbO/OylTtKMVcHb67zjWIfCiKR9rWL3GUk1677LAqD/BcHizukdZEjT8Ru3oHRoAYoJy44g==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: 8.5.15 + + postcss-minify-font-values@5.1.0: + resolution: {integrity: sha512-el3mYTgx13ZAPPirSVsHqFzl+BBBDrXvbySvPGFnQcTI4iNslrPaFq4muTkLZmKlGk4gyFAYUBMH30+HurREyA==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: 8.5.15 + + postcss-minify-gradients@5.1.1: + resolution: {integrity: sha512-VGvXMTpCEo4qHTNSa9A0a3D+dxGFZCYwR6Jokk+/3oB6flu2/PnPXAh2x7x52EkY5xlIHLm+Le8tJxe/7TNhzw==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: 8.5.15 + + postcss-minify-params@5.1.4: + resolution: {integrity: sha512-+mePA3MgdmVmv6g+30rn57USjOGSAyuxUmkfiWpzalZ8aiBkdPYjXWtHuwJGm1v5Ojy0Z0LaSYhHaLJQB0P8Jw==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: 8.5.15 + + postcss-minify-selectors@5.2.1: + resolution: {integrity: sha512-nPJu7OjZJTsVUmPdm2TcaiohIwxP+v8ha9NehQ2ye9szv4orirRU3SDdtUmKH+10nzn0bAyOXZ0UEr7OpvLehg==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: 8.5.15 + + postcss-modules-extract-imports@3.1.0: + resolution: {integrity: sha512-k3kNe0aNFQDAZGbin48pL2VNidTF0w4/eASDsxlyspobzU3wZQLOGj7L9gfRe0Jo9/4uud09DsjFNH7winGv8Q==} + engines: {node: ^10 || ^12 || >= 14} + peerDependencies: + postcss: 8.5.15 + + postcss-modules-local-by-default@4.2.0: + resolution: {integrity: sha512-5kcJm/zk+GJDSfw+V/42fJ5fhjL5YbFDl8nVdXkJPLLW+Vf9mTD5Xe0wqIaDnLuL2U6cDNpTr+UQ+v2HWIBhzw==} + engines: {node: ^10 || ^12 || >= 14} + peerDependencies: + postcss: 8.5.15 + + postcss-modules-scope@3.2.1: + resolution: {integrity: sha512-m9jZstCVaqGjTAuny8MdgE88scJnCiQSlSrOWcTQgM2t32UBe+MUmFSO5t7VMSfAf/FJKImAxBav8ooCHJXCJA==} + engines: {node: ^10 || ^12 || >= 14} + peerDependencies: + postcss: 8.5.15 + + postcss-modules-values@4.0.0: + resolution: {integrity: sha512-RDxHkAiEGI78gS2ofyvCsu7iycRv7oqw5xMWn9iMoR0N/7mf9D50ecQqUo5BZ9Zh2vH4bCUR/ktCqbB9m8vJjQ==} + engines: {node: ^10 || ^12 || >= 14} + peerDependencies: + postcss: 8.5.15 + + postcss-normalize-charset@5.1.0: + resolution: {integrity: sha512-mSgUJ+pd/ldRGVx26p2wz9dNZ7ji6Pn8VWBajMXFf8jk7vUoSrZ2lt/wZR7DtlZYKesmZI680qjr2CeFF2fbUg==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: 8.5.15 + + postcss-normalize-display-values@5.1.0: + resolution: {integrity: sha512-WP4KIM4o2dazQXWmFaqMmcvsKmhdINFblgSeRgn8BJ6vxaMyaJkwAzpPpuvSIoG/rmX3M+IrRZEz2H0glrQNEA==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: 8.5.15 + + postcss-normalize-positions@5.1.1: + resolution: {integrity: sha512-6UpCb0G4eofTCQLFVuI3EVNZzBNPiIKcA1AKVka+31fTVySphr3VUgAIULBhxZkKgwLImhzMR2Bw1ORK+37INg==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: 8.5.15 + + postcss-normalize-repeat-style@5.1.1: + resolution: {integrity: sha512-mFpLspGWkQtBcWIRFLmewo8aC3ImN2i/J3v8YCFUwDnPu3Xz4rLohDO26lGjwNsQxB3YF0KKRwspGzE2JEuS0g==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: 8.5.15 + + postcss-normalize-string@5.1.0: + resolution: {integrity: sha512-oYiIJOf4T9T1N4i+abeIc7Vgm/xPCGih4bZz5Nm0/ARVJ7K6xrDlLwvwqOydvyL3RHNf8qZk6vo3aatiw/go3w==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: 8.5.15 + + postcss-normalize-timing-functions@5.1.0: + resolution: {integrity: sha512-DOEkzJ4SAXv5xkHl0Wa9cZLF3WCBhF3o1SKVxKQAa+0pYKlueTpCgvkFAHfk+Y64ezX9+nITGrDZeVGgITJXjg==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: 8.5.15 + + postcss-normalize-unicode@5.1.1: + resolution: {integrity: sha512-qnCL5jzkNUmKVhZoENp1mJiGNPcsJCs1aaRmURmeJGES23Z/ajaln+EPTD+rBeNkSryI+2WTdW+lwcVdOikrpA==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: 8.5.15 + + postcss-normalize-url@5.1.0: + resolution: {integrity: sha512-5upGeDO+PVthOxSmds43ZeMeZfKH+/DKgGRD7TElkkyS46JXAUhMzIKiCa7BabPeIy3AQcTkXwVVN7DbqsiCew==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: 8.5.15 + + postcss-normalize-whitespace@5.1.1: + resolution: {integrity: sha512-83ZJ4t3NUDETIHTa3uEg6asWjSBYL5EdkVB0sDncx9ERzOKBVJIUeDO9RyA9Zwtig8El1d79HBp0JEi8wvGQnA==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: 8.5.15 + + postcss-ordered-values@5.1.3: + resolution: {integrity: sha512-9UO79VUhPwEkzbb3RNpqqghc6lcYej1aveQteWY+4POIwlqkYE21HKWaLDF6lWNuqCobEAyTovVhtI32Rbv2RQ==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: 8.5.15 + + postcss-reduce-initial@5.1.2: + resolution: {integrity: sha512-dE/y2XRaqAi6OvjzD22pjTUQ8eOfc6m/natGHgKFBK9DxFmIm69YmaRVQrGgFlEfc1HePIurY0TmDeROK05rIg==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: 8.5.15 + + postcss-reduce-transforms@5.1.0: + resolution: {integrity: sha512-2fbdbmgir5AvpW9RLtdONx1QoYG2/EtqpNQbFASDlixBbAYuTcJ0dECwlqNqH7VbaUnEnh8SrxOe2sRIn24XyQ==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: 8.5.15 + + postcss-selector-parser@6.1.2: + resolution: {integrity: sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==} + engines: {node: '>=4'} + + postcss-selector-parser@7.1.1: + resolution: {integrity: sha512-orRsuYpJVw8LdAwqqLykBj9ecS5/cRHlI5+nvTo8LcCKmzDmqVORXtOIYEEQuL9D4BxtA1lm5isAqzQZCoQ6Eg==} + engines: {node: '>=4'} + + postcss-svgo@5.1.0: + resolution: {integrity: sha512-D75KsH1zm5ZrHyxPakAxJWtkyXew5qwS70v56exwvw542d9CRtTo78K0WeFxZB4G7JXKKMbEZtZayTGdIky/eA==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: 8.5.15 + + postcss-unique-selectors@5.1.1: + resolution: {integrity: sha512-5JiODlELrz8L2HwxfPnhOWZYWDxVHWL83ufOv84NrcgipI7TaeRsatAhK4Tr2/ZiYldpK/wBvw5BD3qfaK96GA==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: 8.5.15 + + postcss-value-parser@4.2.0: + resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==} + + postcss@8.5.15: + resolution: {integrity: sha512-FfR8sjd4em2T6fb3I2MwAJU7HWVMr9zba+enmQeeWFfCbm+UOC/0X4DS8XtpUTMwWMGbjKYP7xjfNekzyGmB3A==} + engines: {node: ^10 || ^12 || >=14} + + prelude-ls@1.2.1: + resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} + engines: {node: '>= 0.8.0'} + + prettier@2.8.8: + resolution: {integrity: sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==} + engines: {node: '>=10.13.0'} + hasBin: true + + pretty-error@4.0.0: + resolution: {integrity: sha512-AoJ5YMAcXKYxKhuJGdcvse+Voc6v1RgnsR3nWcYU7q4t6z0Q6T86sv5Zq8VIRbOWWFpvdGE83LtdSMNd+6Y0xw==} + + 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@30.0.2: + resolution: {integrity: sha512-yC5/EBSOrTtqhCKfLHqoUIAXVRZnukHPwWBJWR7h84Q3Be1DRQZLncwcfLoPA5RPQ65qfiCMqgYwdUuQ//eVpg==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + process-nextick-args@2.0.1: + resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==} + + prop-types@15.8.1: + resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==} + + proxy-addr@2.0.7: + resolution: {integrity: sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==} + engines: {node: '>= 0.10'} + + proxy-from-env@2.1.0: + resolution: {integrity: sha512-cJ+oHTW1VAEa8cJslgmUZrc+sjRKgAKl3Zyse6+PV38hZe/V6Z14TbCuXcan9F9ghlz4QrFr2c92TNF82UkYHA==} + engines: {node: '>=10'} + + prr@1.0.1: + resolution: {integrity: sha512-yPw4Sng1gWghHQWj0B3ZggWUm4qVbPwPFcRG8KyxiU7J2OHFSoEHKS+EZ3fv5l1t9CyCiop6l/ZYeWbrgoQejw==} + + pump@3.0.3: + resolution: {integrity: sha512-todwxLMY7/heScKmntwQG8CXVkWUOdYxIvY2s0VWAAMh/nd8SoYiRaKjlr7+iCs984f2P8zvrfWcDDYVb73NfA==} + + punycode@2.3.1: + resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} + engines: {node: '>=6'} + + pvtsutils@1.3.6: + resolution: {integrity: sha512-PLgQXQ6H2FWCaeRak8vvk1GW462lMxB5s3Jm673N82zI4vqtVUPuZdffdZbPDFRoU8kAhItWFtPCWiPpp4/EDg==} + + pvutils@1.1.5: + resolution: {integrity: sha512-KTqnxsgGiQ6ZAzZCVlJH5eOjSnvlyEgx1m8bkRJfOhmGRqfo5KLvmAlACQkrjEtOQ4B7wF9TdSLIs9O90MX9xA==} + engines: {node: '>=16.0.0'} + + qs@6.15.2: + resolution: {integrity: sha512-Rzq0KEyX/w/tEybncDgdkZrJgVUsUMk3xjh3t5bv3S1HTAtg+uOYt72+ZfwiQwKdysThkTBdL/rTi6HDmX9Ddw==} + engines: {node: '>=0.6'} + + quansync@0.2.11: + resolution: {integrity: sha512-AifT7QEbW9Nri4tAwR5M/uzpBuqfZf+zwaEM/QkzEjj7NBuFD2rBuy0K3dE+8wltbezDV7JMA0WfnCPYRSYbXA==} + + queue-microtask@1.2.3: + resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} + + range-parser@1.2.1: + resolution: {integrity: sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==} + engines: {node: '>= 0.6'} + + raw-body@2.5.3: + resolution: {integrity: sha512-s4VSOf6yN0rvbRZGxs8Om5CWj6seneMwK3oDb4lWDH0UPhWcxwOWw5+qk24bxq87szX1ydrwylIOp2uG1ojUpA==} + engines: {node: '>= 0.8'} + + raw-body@3.0.2: + resolution: {integrity: sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA==} + engines: {node: '>= 0.10'} + + react-clientside-effect@1.2.8: + resolution: {integrity: sha512-ma2FePH0z3px2+WOu6h+YycZcEvFmmxIlAb62cF52bG86eMySciO/EQZeQMXd07kPCYB0a1dWDT5J+KE9mCDUw==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc + + react-dom@18.2.0: + resolution: {integrity: sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==} + peerDependencies: + react: ^18.2.0 + + react-dom@18.3.1: + resolution: {integrity: sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==} + peerDependencies: + react: ^18.3.1 + + react-dom@19.1.1: + resolution: {integrity: sha512-Dlq/5LAZgF0Gaz6yiqZCf6VCcZs1ghAJyrsu84Q/GT0gV+mCxbfmKNoGRKBYMJ8IEdGPqu49YWXD02GCknEDkw==} + peerDependencies: + react: ^19.1.1 + + react-fast-compare@3.2.2: + resolution: {integrity: sha512-nsO+KSNgo1SbJqJEYRE9ERzo7YtYbou/OqjSQKxV7jcKox7+usiUVZOAC+XnDOABXggQTno0Y1CpVnuWEc1boQ==} + + react-focus-lock@2.13.7: + resolution: {integrity: sha512-20lpZHEQrXPb+pp1tzd4ULL6DyO5D2KnR0G69tTDdydrmNhU7pdFmbQUYVyHUgp+xN29IuFR0PVuhOmvaZL9Og==} + peerDependencies: + '@types/react': '*' + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + react-is@16.13.1: + resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==} + + react-is@17.0.2: + resolution: {integrity: sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==} + + react-is@18.3.1: + resolution: {integrity: sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==} + + react-is@19.1.0: + resolution: {integrity: sha512-Oe56aUPnkHyyDxxkvqtd7KkdQP5uIUfHxd5XTb3wE9d/kRnZLmKbDB0GWk919tdQ+mxxPtG6EAs6RMT6i1qtHg==} + + react-number-format@5.4.5: + resolution: {integrity: sha512-y8O2yHHj3w0aE9XO8d2BCcUOOdQTRSVq+WIuMlLVucAm5XNjJAy+BoOJiuQMldVYVOKTMyvVNfnbl2Oqp+YxGw==} + peerDependencies: + react: ^0.14 || ^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + react-dom: ^0.14 || ^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + + react-refresh@0.17.0: + resolution: {integrity: sha512-z6F7K9bV85EfseRCp2bzrpyQ0Gkw1uLoCel9XBVWPg/TjRj94SkJzUTGfOa4bs7iJvBWtQG0Wq7wnI0syw3EBQ==} + engines: {node: '>=0.10.0'} + + react-remove-scroll-bar@2.3.8: + resolution: {integrity: sha512-9r+yi9+mgU33AKcj6IbT9oRCO78WriSj6t/cF8DWBZJ9aOGPOTEDvdUDz1FwKim7QXWwmHqtdHnRJfhAxEG46Q==} + engines: {node: '>=10'} + peerDependencies: + '@types/react': '*' + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + + react-remove-scroll@2.7.2: + resolution: {integrity: sha512-Iqb9NjCCTt6Hf+vOdNIZGdTiH1QSqr27H/Ek9sv/a97gfueI/5h1s3yRi1nngzMUaOOToin5dI1dXKdXiF+u0Q==} + engines: {node: '>=10'} + peerDependencies: + '@types/react': '*' + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + react-router-dom@6.30.3: + resolution: {integrity: sha512-pxPcv1AczD4vso7G4Z3TKcvlxK7g7TNt3/FNGMhfqyntocvYKj+GCatfigGDjbLozC4baguJ0ReCigoDJXb0ag==} + engines: {node: '>=14.0.0'} + peerDependencies: + react: '>=16.8' + react-dom: '>=16.8' + + react-router@6.30.3: + resolution: {integrity: sha512-XRnlbKMTmktBkjCLE8/XcZFlnHvr2Ltdr1eJX4idL55/9BbORzyZEaIkBFDhFGCEWBBItsVrDxwx3gnisMitdw==} + engines: {node: '>=14.0.0'} + peerDependencies: + react: '>=16.8' + + react-style-singleton@2.2.3: + resolution: {integrity: sha512-b6jSvxvVnyptAiLjbkWLE/lOnR4lfTtDAl+eUC7RZy+QQWc6wRzIV2CE6xBuMmDxc2qIihtDCZD5NPOFl7fRBQ==} + engines: {node: '>=10'} + peerDependencies: + '@types/react': '*' + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + react-textarea-autosize@8.5.9: + resolution: {integrity: sha512-U1DGlIQN5AwgjTyOEnI1oCcMuEr1pv1qOtklB2l4nyMGbHzWrI0eFsYK0zos2YWqAolJyG0IWJaqWmWj5ETh0A==} + engines: {node: '>=10'} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + + react-transition-group@4.4.5: + resolution: {integrity: sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==} + peerDependencies: + react: '>=16.6.0' + react-dom: '>=16.6.0' + + react@17.0.2: + resolution: {integrity: sha512-gnhPt75i/dq/z3/6q/0asP78D0u592D5L1pd7M8P+dck6Fu/jJeL6iVVK23fptSUZj8Vjf++7wXA8UNclGQcbA==} + engines: {node: '>=0.10.0'} + + react@18.2.0: + resolution: {integrity: sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==} + engines: {node: '>=0.10.0'} + + react@18.3.1: + resolution: {integrity: sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==} + engines: {node: '>=0.10.0'} + + react@19.1.1: + resolution: {integrity: sha512-w8nqGImo45dmMIfljjMwOGtbmC/mk4CMYhWIicdSflH91J9TyCyczcPFXJzrZ/ZXcgGRFeP6BU0BEJTw6tZdfQ==} + engines: {node: '>=0.10.0'} + + read-cache@1.0.0: + resolution: {integrity: sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==} + + read-yaml-file@1.1.0: + resolution: {integrity: sha512-VIMnQi/Z4HT2Fxuwg5KrY174U1VdUIASQVWXXyqtNRtxSr9IYkn1rsI6Tb6HsrHCmB7gVpNwX6JxPTHcH6IoTA==} + engines: {node: '>=6'} + + readable-stream@2.3.8: + resolution: {integrity: sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==} + + readable-stream@3.6.2: + resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==} + engines: {node: '>= 6'} + + readdirp@3.6.0: + resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} + engines: {node: '>=8.10.0'} + + readdirp@4.1.2: + resolution: {integrity: sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==} + engines: {node: '>= 14.18.0'} + + rechoir@0.6.2: + resolution: {integrity: sha512-HFM8rkZ+i3zrV+4LQjwQ0W+ez98pApMGM3HUrN04j3CqzPOzl9nmP15Y8YXNm8QHGv/eacOVEjqhmWpkRV0NAw==} + engines: {node: '>= 0.10'} + + rechoir@0.8.0: + resolution: {integrity: sha512-/vxpCXddiX8NGfGO/mTafwjq4aFa/71pvamip0++IQk3zG8cbCj0fifNPrjjF1XMXUne91jL9OoxmdykoEtifQ==} + engines: {node: '>= 10.13.0'} + + redent@3.0.0: + resolution: {integrity: sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==} + engines: {node: '>=8'} + + reflect-metadata@0.2.2: + resolution: {integrity: sha512-urBwgfrvVP/eAyXx4hluJivBKzuEbSQs9rKWCrCkbSxNv8mxPcUZKeuoF3Uy4mJl3Lwprp6yy5/39VWigZ4K6Q==} + + regenerate-unicode-properties@10.2.2: + resolution: {integrity: sha512-m03P+zhBeQd1RGnYxrGyDAPpWX/epKirLrp8e3qevZdVkKtnCrjjWczIbYc8+xd6vcTStVlqfycTx1KR4LOr0g==} + engines: {node: '>=4'} + + regenerate@1.4.2: + resolution: {integrity: sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==} + + regexp.prototype.flags@1.5.4: + resolution: {integrity: sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==} + engines: {node: '>= 0.4'} + + regexpp@3.2.0: + resolution: {integrity: sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==} + engines: {node: '>=8'} + + regexpu-core@6.4.0: + resolution: {integrity: sha512-0ghuzq67LI9bLXpOX/ISfve/Mq33a4aFRzoQYhnnok1JOFpmE/A2TBGkNVenOGEeSBCjIiWcc6MVOG5HEQv0sA==} + engines: {node: '>=4'} + + regjsgen@0.8.0: + resolution: {integrity: sha512-RvwtGe3d7LvWiDQXeQw8p5asZUmfU1G/l6WbUXeHta7Y2PEIvBTwH6E2EfmYUK8pxcxEdEmaomqyp0vZZ7C+3Q==} + + regjsparser@0.13.1: + resolution: {integrity: sha512-dLsljMd9sqwRkby8zhO1gSg3PnJIBFid8f4CQj/sXx+7cKx+E7u0PKhZ+U4wmhx7EfmtvnA318oVaIkAB1lRJw==} + hasBin: true + + relateurl@0.2.7: + resolution: {integrity: sha512-G08Dxvm4iDN3MLM0EsP62EDV9IuhXPR6blNz6Utcp7zyV3tr4HVNINt6MpaRWbxoOHT3Q7YN2P+jaHX8vUbgog==} + engines: {node: '>= 0.10'} + + renderkid@3.0.0: + resolution: {integrity: sha512-q/7VIQA8lmM1hF+jn+sFSPWGlMkSAeNYcPLmDQx2zzuiDfaLrOmumR8iaUKlenFgh0XRPIUeSPlH3A+AW3Z5pg==} + + require-directory@2.1.1: + resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} + engines: {node: '>=0.10.0'} + + require-from-string@2.0.2: + resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} + engines: {node: '>=0.10.0'} + + requires-port@1.0.0: + resolution: {integrity: sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==} + + resolve-cwd@3.0.0: + resolution: {integrity: sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==} + engines: {node: '>=8'} + + resolve-from@4.0.0: + resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} + engines: {node: '>=4'} + + resolve-from@5.0.0: + resolution: {integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==} + engines: {node: '>=8'} + + resolve@1.22.12: + resolution: {integrity: sha512-TyeJ1zif53BPfHootBGwPRYT1RUt6oGWsaQr8UyZW/eAm9bKoijtvruSDEmZHm92CwS9nj7/fWttqPCgzep8CA==} + engines: {node: '>= 0.4'} + hasBin: true + + restore-cursor@3.1.0: + resolution: {integrity: sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==} + engines: {node: '>=8'} + + retry@0.13.1: + resolution: {integrity: sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==} + engines: {node: '>= 4'} + + reusify@1.1.0: + resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==} + engines: {iojs: '>=1.0.0', node: '>=0.10.0'} + + rimraf@3.0.2: + resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==} + deprecated: Rimraf versions prior to v4 are no longer supported + hasBin: true + + rollup@4.60.4: + resolution: {integrity: sha512-WHeFSbZYsPu3+bLoNRUuAO+wavNlocOPf3wSHTP7hcFKVnJeWsYlCDbr3mTS14FCizf9ccIxXA8sGL8zKeQN3g==} + engines: {node: '>=18.0.0', npm: '>=8.0.0'} + hasBin: true + + router@2.2.0: + resolution: {integrity: sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==} + engines: {node: '>= 18'} + + run-applescript@7.1.0: + resolution: {integrity: sha512-DPe5pVFaAsinSaV6QjQ6gdiedWDcRCbUuiQfQa2wmWV7+xC9bGulGI8+TdRmoFkAPaBXk8CrAbnlY2ISniJ47Q==} + engines: {node: '>=18'} + + run-parallel@1.2.0: + resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} + + rxjs@6.6.7: + resolution: {integrity: sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==} + engines: {npm: '>=2.0.0'} + + safe-buffer@5.1.2: + resolution: {integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==} + + safe-buffer@5.2.1: + resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} + + safe-regex-test@1.1.0: + resolution: {integrity: sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==} + engines: {node: '>= 0.4'} + + safer-buffer@2.1.2: + resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} + + sass-loader@12.6.0: + resolution: {integrity: sha512-oLTaH0YCtX4cfnJZxKSLAyglED0naiYfNG1iXfU5w1LNZ+ukoA5DtyDIN5zmKVZwYNJP4KRc5Y3hkWga+7tYfA==} + engines: {node: '>= 12.13.0'} + peerDependencies: + fibers: '>= 3.1.0' + node-sass: ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 + sass: ^1.3.0 + sass-embedded: '*' + webpack: 5.107.1 + peerDependenciesMeta: + fibers: + optional: true + node-sass: + optional: true + sass: + optional: true + sass-embedded: + optional: true + + sass@1.99.0: + resolution: {integrity: sha512-kgW13M54DUB7IsIRM5LvJkNlpH+WhMpooUcaWGFARkF1Tc82v9mIWkCbCYf+MBvpIUBSeSOTilpZjEPr2VYE6Q==} + engines: {node: '>=14.0.0'} + hasBin: true + + sax@1.2.4: + resolution: {integrity: sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==} + + sax@1.6.0: + resolution: {integrity: sha512-6R3J5M4AcbtLUdZmRv2SygeVaM7IhrLXu9BmnOGmmACak8fiUtOsYNWUS4uK7upbmHIBbLBeFeI//477BKLBzA==} + engines: {node: '>=11.0.0'} + + scheduler@0.23.2: + resolution: {integrity: sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==} + + scheduler@0.26.0: + resolution: {integrity: sha512-NlHwttCI/l5gCPR3D1nNXtWABUmBwvZpEQiD4IXSbIDq8BzLIK/7Ir5gTFSGZDUu37K5cMNp0hFtzO38sC7gWA==} + + schema-utils@3.3.0: + resolution: {integrity: sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==} + engines: {node: '>= 10.13.0'} + + schema-utils@4.3.3: + resolution: {integrity: sha512-eflK8wEtyOE6+hsaRVPxvUKYCpRgzLqDTb8krvAsRIwOGlHoSgYLgBXoubGgLd2fT41/OUYdb48v4k4WWHQurA==} + engines: {node: '>= 10.13.0'} + + scroll-into-view-if-needed@3.1.0: + resolution: {integrity: sha512-49oNpRjWRvnU8NyGVmUaYG4jtTkNonFZI86MmGRDqBphEK2EXT9gdEUoQPZhuBM8yWHxCWbobltqYO5M4XrUvQ==} + + select-hose@2.0.0: + resolution: {integrity: sha512-mEugaLK+YfkijB4fx0e6kImuJdCIt2LxCRcbEYPqRGCs4F2ogyfZU5IAZRdjCP8JPq2AtdNoC/Dux63d9Kiryg==} + + selfsigned@5.5.0: + resolution: {integrity: sha512-ftnu3TW4+3eBfLRFnDEkzGxSF/10BJBkaLJuBHZX0kiPS7bRdlpZGu6YGt4KngMkdTwJE6MbjavFpqHvqVt+Ew==} + engines: {node: '>=18'} + + semver@5.7.2: + resolution: {integrity: sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==} + hasBin: true + + semver@6.3.1: + resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} + hasBin: true + + semver@7.5.4: + resolution: {integrity: sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==} + engines: {node: '>=10'} + hasBin: true + + semver@7.7.4: + resolution: {integrity: sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==} + engines: {node: '>=10'} + hasBin: true + + send@0.19.2: + resolution: {integrity: sha512-VMbMxbDeehAxpOtWJXlcUS5E8iXh6QmN+BkRX1GARS3wRaXEEgzCcB10gTQazO42tpNIya8xIyNx8fll1OFPrg==} + engines: {node: '>= 0.8.0'} + + send@1.2.1: + resolution: {integrity: sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ==} + engines: {node: '>= 18'} + + serialize-javascript@7.0.5: + resolution: {integrity: sha512-F4LcB0UqUl1zErq+1nYEEzSHJnIwb3AF2XWB94b+afhrekOUijwooAYqFyRbjYkm2PAKBabx6oYv/xDxNi8IBw==} + engines: {node: '>=20.0.0'} + + serve-index@1.9.2: + resolution: {integrity: sha512-KDj11HScOaLmrPxl70KYNW1PksP4Nb/CLL2yvC+Qd2kHMPEEpfc4Re2e4FOay+bC/+XQl/7zAcWON3JVo5v3KQ==} + engines: {node: '>= 0.8.0'} + + serve-static@1.16.3: + resolution: {integrity: sha512-x0RTqQel6g5SY7Lg6ZreMmsOzncHFU7nhnRWkKgWuMTu5NN0DR5oruckMqRvacAN9d5w6ARnRBXl9xhDCgfMeA==} + engines: {node: '>= 0.8.0'} + + serve-static@2.2.1: + resolution: {integrity: sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw==} + engines: {node: '>= 18'} + + set-function-length@1.2.2: + resolution: {integrity: sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==} + engines: {node: '>= 0.4'} + + set-function-name@2.0.2: + resolution: {integrity: sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==} + engines: {node: '>= 0.4'} + + setprototypeof@1.2.0: + resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==} + + shallow-clone@3.0.1: + resolution: {integrity: sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==} + engines: {node: '>=8'} + + sharp@0.34.5: + resolution: {integrity: sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + + shebang-command@1.2.0: + resolution: {integrity: sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==} + engines: {node: '>=0.10.0'} + + shebang-command@2.0.0: + resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} + engines: {node: '>=8'} + + shebang-regex@1.0.0: + resolution: {integrity: sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==} + engines: {node: '>=0.10.0'} + + shebang-regex@3.0.0: + resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} + engines: {node: '>=8'} + + shell-quote@1.8.3: + resolution: {integrity: sha512-ObmnIF4hXNg1BqhnHmgbDETF8dLPCggZWBjkQfhZpbszZnYur5DUljTcCHii5LC3J5E0yeO/1LIMyH+UvHQgyw==} + engines: {node: '>= 0.4'} + + shelljs@0.9.2: + resolution: {integrity: sha512-S3I64fEiKgTZzKCC46zT/Ib9meqofLrQVbpSswtjFfAVDW+AZ54WTnAM/3/yENoxz/V1Cy6u3kiiEbQ4DNphvw==} + engines: {node: '>=18'} + hasBin: true + + shx@0.4.0: + resolution: {integrity: sha512-Z0KixSIlGPpijKgcH6oCMCbltPImvaKy0sGH8AkLRXw1KyzpKtaCTizP2xen+hNDqVF4xxgvA0KXSb9o4Q6hnA==} + engines: {node: '>=18'} + hasBin: true + + side-channel-list@1.0.0: + resolution: {integrity: sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==} + engines: {node: '>= 0.4'} + + side-channel-map@1.0.1: + resolution: {integrity: sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==} + engines: {node: '>= 0.4'} + + side-channel-weakmap@1.0.2: + 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'} + + signal-exit@3.0.7: + resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==} + + signal-exit@4.1.0: + resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} + engines: {node: '>=14'} + + slash@3.0.0: + resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} + engines: {node: '>=8'} + + slash@4.0.0: + resolution: {integrity: sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew==} + engines: {node: '>=12'} + + sockjs@0.3.24: + resolution: {integrity: sha512-GJgLTZ7vYb/JtPSSZ10hsOYIvEYsjbNU+zPdIHcUaWVNUEPivzxku31865sSSud0Da0W4lEeOPlmw93zLQchuQ==} + + source-map-js@1.2.1: + resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} + engines: {node: '>=0.10.0'} + + source-map-loader@3.0.2: + resolution: {integrity: sha512-BokxPoLjyl3iOrgkWaakaxqnelAJSS+0V+De0kKIq6lyWrXuiPgYTGp6z3iHmqljKAaLXwZa+ctD8GccRJeVvg==} + engines: {node: '>= 12.13.0'} + peerDependencies: + webpack: 5.107.1 + + source-map-resolve@0.6.0: + resolution: {integrity: sha512-KXBr9d/fO/bWo97NXsPIAW1bFSBOuCnjbNTBMO7N59hsv5i9yzRDfcYwwt0l04+VqnKC+EwzvJZIP/qkuMgR/w==} + deprecated: See https://github.com/lydell/source-map-resolve#deprecated + + source-map-support@0.5.19: + resolution: {integrity: sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw==} + + source-map-support@0.5.21: + resolution: {integrity: sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==} + + source-map@0.5.7: + resolution: {integrity: sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==} + engines: {node: '>=0.10.0'} + + source-map@0.6.1: + resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} + engines: {node: '>=0.10.0'} + + source-map@0.7.6: + resolution: {integrity: sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ==} + engines: {node: '>= 12'} + + spawndamnit@3.0.1: + resolution: {integrity: sha512-MmnduQUuHCoFckZoWnXsTg7JaiLBJrKFj9UI2MbRPGaJeVpsLcVBu6P/IGZovziM/YBsellCmsprgNA+w0CzVg==} + + spdy-transport@3.0.0: + resolution: {integrity: sha512-hsLVFE5SjA6TCisWeJXFKniGGOpBgMLmerfO2aCyCU5s7nJ/rpAepqmFifv/GCbSbueEeAJJnmSQ2rKC/g8Fcw==} + + spdy@4.0.2: + resolution: {integrity: sha512-r46gZQZQV+Kl9oItvl1JZZqJKGr+oEkB08A6BzkiR7593/7IbtuncXHd2YoYeTsG4157ZssMu9KYvUHLcjcDoA==} + engines: {node: '>=6.0.0'} + + sprintf-js@1.0.3: + resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==} + + 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' + + stack-utils@2.0.6: + resolution: {integrity: sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==} + engines: {node: '>=10'} + + statuses@1.5.0: + resolution: {integrity: sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==} + engines: {node: '>= 0.6'} + + statuses@2.0.2: + resolution: {integrity: sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==} + engines: {node: '>= 0.8'} + + stop-iteration-iterator@1.1.0: + resolution: {integrity: sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==} + engines: {node: '>= 0.4'} + + string-convert@0.2.1: + resolution: {integrity: sha512-u/1tdPl4yQnPBjnVrmdLo9gtuLvELKsAoRapekWggdiQNvvvum+jYF329d84NAa660KQw7pB2n36KrIKVoXa3A==} + + string-width@4.2.3: + resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} + engines: {node: '>=8'} + + string_decoder@1.1.1: + resolution: {integrity: sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==} + + string_decoder@1.3.0: + resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==} + + strip-ansi@6.0.1: + resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} + engines: {node: '>=8'} + + strip-bom@3.0.0: + resolution: {integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==} + engines: {node: '>=4'} + + strip-eof@1.0.0: + resolution: {integrity: sha512-7FCwGGmx8mD5xQd3RPUvnSpUXHM3BWuzjtpD4TXsfcZ9EL4azvVVUscFYwD9nx8Kh+uCBC00XBtAykoMHwTh8Q==} + engines: {node: '>=0.10.0'} + + strip-indent@3.0.0: + resolution: {integrity: sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==} + engines: {node: '>=8'} + + strip-json-comments@3.1.1: + resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} + engines: {node: '>=8'} + + strong-log-transformer@2.1.0: + resolution: {integrity: sha512-B3Hgul+z0L9a236FAUC9iZsL+nVHgoCJnqCbN588DjYxvGXaXaaFbfmQ/JhvKjZwsOukuR72XbHv71Qkug0HxA==} + engines: {node: '>=4'} + hasBin: true + + style-loader@3.3.4: + resolution: {integrity: sha512-0WqXzrsMTyb8yjZJHDqwmnwRJvhALK9LfRtRc6B4UTWe8AijYLZYZ9thuJTZc2VfQWINADW/j+LiJnfy2RoC1w==} + engines: {node: '>= 12.13.0'} + peerDependencies: + webpack: 5.107.1 + + style-loader@4.0.0: + resolution: {integrity: sha512-1V4WqhhZZgjVAVJyt7TdDPZoPBPNHbekX4fWnCJL1yQukhCeZhJySUL+gL9y6sNdN95uEOS83Y55SqHcP7MzLA==} + engines: {node: '>= 18.12.0'} + peerDependencies: + webpack: 5.107.1 + + styled-jsx@5.1.6: + resolution: {integrity: sha512-qSVyDTeMotdvQYoHWLNGwRFJHC+i+ZvdBRYosOFgC+Wg1vx4frN2/RG/NA7SYqqvKNLf39P2LSRA2pu6n0XYZA==} + engines: {node: '>= 12.0.0'} + peerDependencies: + '@babel/core': '*' + babel-plugin-macros: '*' + react: '>= 16.8.0 || 17.x.x || ^18.0.0-0 || ^19.0.0-0' + peerDependenciesMeta: + '@babel/core': + optional: true + babel-plugin-macros: + optional: true + + stylehacks@5.1.1: + resolution: {integrity: sha512-sBpcd5Hx7G6seo7b1LkpttvTz7ikD0LlH5RmdcBNb6fFR0Fl7LQwHDFr300q4cwUqi+IYrFGmsIHieMBfnN/Bw==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: 8.5.15 + + stylis@4.2.0: + resolution: {integrity: sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw==} + + stylis@4.4.0: + resolution: {integrity: sha512-5Z9ZpRzfuH6l/UAvCPAPUo3665Nk2wLaZU3x+TLHKVzIz33+sbJqbtrYoC3KD4/uVOr2Zp+L0LySezP9OHV9yA==} + + stylus-loader@7.1.3: + resolution: {integrity: sha512-TY0SKwiY7D2kMd3UxaWKSf3xHF0FFN/FAfsSqfrhxRT/koXTwffq2cgEWDkLQz7VojMu7qEEHt5TlMjkPx9UDw==} + engines: {node: '>= 14.15.0'} + peerDependencies: + stylus: '>=0.52.4' + webpack: 5.107.1 + + stylus@0.55.0: + resolution: {integrity: sha512-MuzIIVRSbc8XxHH7FjkvWqkIcr1BvoMZoR/oFuAJDlh7VSaNJzrB4uJ38GRQa+mWjLXODAMzeDe0xi9GYbGwnw==} + hasBin: true + + supports-color@7.2.0: + resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} + engines: {node: '>=8'} + + supports-color@8.1.1: + resolution: {integrity: sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==} + engines: {node: '>=10'} + + supports-preserve-symlinks-flag@1.0.0: + resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} + engines: {node: '>= 0.4'} + + svg-parser@2.0.4: + resolution: {integrity: sha512-e4hG1hRwoOdRb37cIMSgzNsxyzKfayW6VOflrwvR+/bzrkyxY/31WkbgnQpgtrNp1SdpJvpUAGTa/ZoiPNDuRQ==} + + svgo@2.8.2: + resolution: {integrity: sha512-TyzE4NVGLUFy+H/Uy4N6c3G0HEeprsVfge6Lmq+0FdQQ/zqoVYB62IsBZORsiL+o96s6ff/V6/3UQo/C0cgCAA==} + engines: {node: '>=10.13.0'} + hasBin: true + + tabbable@6.4.0: + resolution: {integrity: sha512-05PUHKSNE8ou2dwIxTngl4EzcnsCDZGJ/iCLtDflR/SHB/ny14rXc+qU5P4mG9JkusiV7EivzY9Mhm55AzAvCg==} + + tapable@2.3.3: + resolution: {integrity: sha512-uxc/zpqFg6x7C8vOE7lh6Lbda8eEL9zmVm/PLeTPBRhh1xCgdWaQ+J1CUieGpIfm2HdtsUpRv+HshiasBMcc6A==} + engines: {node: '>=6'} + + tar-stream@2.2.0: + resolution: {integrity: sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==} + engines: {node: '>=6'} + + term-size@2.2.1: + resolution: {integrity: sha512-wK0Ri4fOGjv/XPy8SBHZChl8CM7uMc5VML7SqiQ0zG7+J5Vr+RMQDoHa2CNT6KHUnTGIXH34UDMkPzAUyapBZg==} + engines: {node: '>=8'} + + terser-webpack-plugin@5.5.0: + resolution: {integrity: sha512-UYhptBwhWvfIjKd/UuFo6D8uq9xpGLDK+z8EDsj/zWhrTaH34cKEbrkMKfV5YWqGBvAYA3tlzZbs2R+qYrbQJA==} + engines: {node: '>= 10.13.0'} + peerDependencies: + '@swc/core': '*' + esbuild: '*' + uglify-js: '*' + webpack: 5.107.1 + peerDependenciesMeta: + '@swc/core': + optional: true + esbuild: + optional: true + uglify-js: + optional: true + + terser@5.46.2: + resolution: {integrity: sha512-uxfo9fPcSgLDYob/w1FuL0c99MWiJDnv+5qXSQc5+Ki5NjVNsYi66INnMFBjf6uFz6OnX12piJQPF4IpjJTNTw==} + engines: {node: '>=10'} + hasBin: true + + text-table@0.2.0: + resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==} + + thingies@2.6.0: + resolution: {integrity: sha512-rMHRjmlFLM1R96UYPvpmnc3LYtdFrT33JIB7L9hetGue1qAPfn1N2LJeEjxUSidu1Iku+haLZXDuEXUHNGO/lg==} + engines: {node: '>=10.18'} + peerDependencies: + tslib: ^2 + + throttle-debounce@5.0.2: + resolution: {integrity: sha512-B71/4oyj61iNH0KeCamLuE2rmKuTO5byTOSVwECM5FA7TiAiAW+UqTKZ9ERueC4qvgSttUhdmq1mXC3kJqGX7A==} + engines: {node: '>=12.22'} + + through@2.3.8: + resolution: {integrity: sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==} + + thunky@1.1.0: + resolution: {integrity: sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==} + + tinyglobby@0.2.14: + resolution: {integrity: sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ==} + engines: {node: '>=12.0.0'} + + tmp@0.2.5: + resolution: {integrity: sha512-voyz6MApa1rQGUxT3E+BK7/ROe8itEx7vD8/HEvt4xwXucvQ5G5oeEiHkmHZJuBO21RpOf+YYm9MOivj709jow==} + engines: {node: '>=14.14'} + + to-regex-range@5.0.1: + resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} + engines: {node: '>=8.0'} + + toggle-selection@1.0.6: + resolution: {integrity: sha512-BiZS+C1OS8g/q2RRbJmy59xpyghNBqrr6k5L/uKBGRsTfxmu3ffiRnd8mlGPUVayg8pvfi5urfnu8TU7DVOkLQ==} + + toidentifier@1.0.1: + resolution: {integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==} + engines: {node: '>=0.6'} + + tree-dump@1.1.0: + resolution: {integrity: sha512-rMuvhU4MCDbcbnleZTFezWsaZXRFemSqAM+7jPnzUl1fo9w3YEKOxAeui0fz3OI4EU4hf23iyA7uQRVko+UaBA==} + engines: {node: '>=10.0'} + peerDependencies: + tslib: '2' + + tree-kill@1.2.2: + resolution: {integrity: sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==} + hasBin: true + + ts-loader@9.4.1: + resolution: {integrity: sha512-384TYAqGs70rn9F0VBnh6BPTfhga7yFNdC5gXbQpDrBj9/KsT4iRkGqKXhziofHOlE2j6YEaiTYVGKKvPhGWvw==} + engines: {node: '>=12.0.0'} + peerDependencies: + typescript: '*' + webpack: 5.107.1 + + ts-node@10.9.1: + resolution: {integrity: sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==} + hasBin: true + peerDependencies: + '@swc/core': '>=1.2.50' + '@swc/wasm': '>=1.2.50' + '@types/node': '*' + typescript: '>=2.7' + peerDependenciesMeta: + '@swc/core': + optional: true + '@swc/wasm': + optional: true + + tsconfig-paths-webpack-plugin@4.0.0: + resolution: {integrity: sha512-fw/7265mIWukrSHd0i+wSwx64kYUSAKPfxRDksjKIYTxSAp9W9/xcZVBF4Kl0eqQd5eBpAQ/oQrc5RyM/0c1GQ==} + engines: {node: '>=10.13.0'} + + tsconfig-paths@4.2.0: + resolution: {integrity: sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg==} + engines: {node: '>=6'} + + tslib@1.14.1: + resolution: {integrity: sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==} + + tslib@2.4.0: + resolution: {integrity: sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==} + + tslib@2.8.1: + resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} + + tsutils@3.21.0: + resolution: {integrity: sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==} + engines: {node: '>= 6'} + peerDependencies: + typescript: '>=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta' + + tsyringe@4.10.0: + resolution: {integrity: sha512-axr3IdNuVIxnaK5XGEUFTu3YmAQ6lllgrvqfEoR16g/HGnYY/6We4oWENtAnzK6/LpJ2ur9PAb80RBt7/U4ugw==} + engines: {node: '>= 6.0.0'} + + type-check@0.4.0: + resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} + engines: {node: '>= 0.8.0'} + + type-fest@0.20.2: + resolution: {integrity: sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==} + engines: {node: '>=10'} + + type-fest@4.41.0: + resolution: {integrity: sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==} + engines: {node: '>=16'} + + type-is@1.6.18: + resolution: {integrity: sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==} + engines: {node: '>= 0.6'} + + type-is@2.1.0: + resolution: {integrity: sha512-faYHw0anBbc/kWF3zFTEnxSFOAGUX9GFbOBthvDdLsIlEoWOFOtS0zgCiQYwIskL9iGXZL3kAXD8OoZ4GmMATA==} + engines: {node: '>= 18'} + + typed-assert@1.0.9: + resolution: {integrity: sha512-KNNZtayBCtmnNmbo5mG47p1XsCyrx6iVqomjcZnec/1Y5GGARaxPs6r49RnSPeUP3YjNYiU9sQHAtY4BBvnZwg==} + + typescript@4.8.4: + resolution: {integrity: sha512-QCh+85mCy+h0IGff8r5XWzOVSbBO+KfeYrMQh7NJ58QujwcE22u+NUSmUxqF+un70P9GXKxa2HCNiTTMJknyjQ==} + engines: {node: '>=4.2.0'} + hasBin: true + + typescript@5.8.3: + resolution: {integrity: sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==} + engines: {node: '>=14.17'} + hasBin: true + + undici-types@7.8.0: + resolution: {integrity: sha512-9UJ2xGDvQ43tYyVMpuHlsgApydB8ZKfVYTsLDhXkFL/6gfkp+U8xTGdh8pMJv1SpZna0zxG1DwsKZsreLbXBxw==} + + unicode-canonical-property-names-ecmascript@2.0.1: + resolution: {integrity: sha512-dA8WbNeb2a6oQzAQ55YlT5vQAWGV9WXOsi3SskE3bcCdM0P4SDd+24zS/OCacdRq5BkdsRj9q3Pg6YyQoxIGqg==} + engines: {node: '>=4'} + + unicode-match-property-ecmascript@2.0.0: + resolution: {integrity: sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==} + engines: {node: '>=4'} + + unicode-match-property-value-ecmascript@2.2.1: + resolution: {integrity: sha512-JQ84qTuMg4nVkx8ga4A16a1epI9H6uTXAknqxkGF/aFfRLw1xC/Bp24HNLaZhHSkWd3+84t8iXnp1J0kYcZHhg==} + engines: {node: '>=4'} + + unicode-property-aliases-ecmascript@2.2.0: + resolution: {integrity: sha512-hpbDzxUY9BFwX+UeBnxv3Sh1q7HFxj48DTmXchNgRa46lO8uj3/1iEn3MiNUYTg1g9ctIqXCCERn8gYZhHC5lQ==} + engines: {node: '>=4'} + + universalify@0.1.2: + resolution: {integrity: sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==} + engines: {node: '>= 4.0.0'} + + universalify@2.0.1: + resolution: {integrity: sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==} + engines: {node: '>= 10.0.0'} + + unpipe@1.0.0: + resolution: {integrity: sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==} + engines: {node: '>= 0.8'} + + update-browserslist-db@1.1.3: + resolution: {integrity: sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==} + hasBin: true + peerDependencies: + browserslist: '>= 4.21.0' + + update-browserslist-db@1.2.3: + resolution: {integrity: sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==} + hasBin: true + peerDependencies: + browserslist: '>= 4.21.0' + + uri-js@4.4.1: + resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} + + use-callback-ref@1.3.3: + resolution: {integrity: sha512-jQL3lRnocaFtu3V00JToYz/4QkNWswxijDaCVNZRiRTO3HQDLsdu1ZtmIUvV4yPp+rvWm5j0y0TG/S61cuijTg==} + engines: {node: '>=10'} + peerDependencies: + '@types/react': '*' + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + use-composed-ref@1.4.0: + resolution: {integrity: sha512-djviaxuOOh7wkj0paeO1Q/4wMZ8Zrnag5H6yBvzN7AKKe8beOaED9SF5/ByLqsku8NP4zQqsvM2u3ew/tJK8/w==} + peerDependencies: + '@types/react': '*' + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + + use-isomorphic-layout-effect@1.2.1: + resolution: {integrity: sha512-tpZZ+EX0gaghDAiFR37hj5MgY6ZN55kLiPkJsKxBMZ6GZdOSPJXiOzPM984oPYZ5AnehYx5WQp1+ME8I/P/pRA==} + peerDependencies: + '@types/react': '*' + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + + use-latest@1.3.0: + resolution: {integrity: sha512-mhg3xdm9NaM8q+gLT8KryJPnRFOz1/5XPBhmDEVZK1webPzDjrPk7f/mbpeLqTgB9msytYWANxgALOCJKnLvcQ==} + peerDependencies: + '@types/react': '*' + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + + use-sidecar@1.1.3: + resolution: {integrity: sha512-Fedw0aZvkhynoPYlA5WXrMCAMm+nSWdZt6lzJQ7Ok8S6Q+VsHmHpRWndVRJ8Be0ZbkfPc5LRYH+5XrzXcEeLRQ==} + engines: {node: '>=10'} + peerDependencies: + '@types/react': '*' + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + util-deprecate@1.0.2: + resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} + + utila@0.4.0: + resolution: {integrity: sha512-Z0DbgELS9/L/75wZbro8xAnT50pBVFQZ+hUEueGDU5FN51YSCYM+jdxsfCiHjwNP/4LCDD0i/graKpeBnOXKRA==} + + utils-merge@1.0.1: + resolution: {integrity: sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==} + engines: {node: '>= 0.4.0'} + + uuid@11.1.1: + resolution: {integrity: sha512-vIYxrBCC/N/K+Js3qSN88go7kIfNPssr/hHCesKCQNAjmgvYS2oqr69kIufEG+O4+PfezOH4EbIeHCfFov8ZgQ==} + hasBin: true + + v8-compile-cache-lib@3.0.1: + resolution: {integrity: sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==} + + v8-compile-cache@2.3.0: + resolution: {integrity: sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==} + + vary@1.1.2: + resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==} + engines: {node: '>= 0.8'} + + vite@6.4.2: + resolution: {integrity: sha512-2N/55r4JDJ4gdrCvGgINMy+HH3iRpNIz8K6SFwVsA+JbQScLiC+clmAxBgwiSPgcG9U15QmvqCGWzMbqda5zGQ==} + engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} + hasBin: true + peerDependencies: + '@types/node': ^18.0.0 || ^20.0.0 || >=22.0.0 + jiti: '>=1.21.0' + less: '*' + lightningcss: ^1.21.0 + sass: '*' + sass-embedded: '*' + stylus: '*' + sugarss: '*' + terser: ^5.16.0 + tsx: ^4.8.1 + yaml: 1.10.3 + peerDependenciesMeta: + '@types/node': + optional: true + jiti: + optional: true + less: + optional: true + lightningcss: + optional: true + sass: + optional: true + sass-embedded: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + tsx: + optional: true + yaml: + optional: true + + watchpack@2.5.1: + resolution: {integrity: sha512-Zn5uXdcFNIA1+1Ei5McRd+iRzfhENPCe7LeABkJtNulSxjma+l7ltNx55BWZkRlwRnpOgHqxnjyaDgJnNXnqzg==} + engines: {node: '>=10.13.0'} + + wbuf@1.7.3: + resolution: {integrity: sha512-O84QOnr0icsbFGLS0O3bI5FswxzRr8/gHwWkDlQFskhSPryQXvrTMxjxGP4+iWYoauLoBvfDpkrOauZ+0iZpDA==} + + webpack-cli@6.0.1: + resolution: {integrity: sha512-MfwFQ6SfwinsUVi0rNJm7rHZ31GyTcpVE5pgVA3hwFRb7COD4TzjUUwhGWKfO50+xdc2MQPuEBBJoqIMGt3JDw==} + engines: {node: '>=18.12.0'} + hasBin: true + peerDependencies: + webpack: 5.107.1 + webpack-bundle-analyzer: '*' + webpack-dev-server: '*' + peerDependenciesMeta: + webpack-bundle-analyzer: + optional: true + webpack-dev-server: + optional: true + + webpack-dev-middleware@7.4.5: + resolution: {integrity: sha512-uxQ6YqGdE4hgDKNf7hUiPXOdtkXvBJXrfEGYSx7P7LC8hnUYGK70X6xQXUvXeNyBDDcsiQXpG2m3G9vxowaEuA==} + engines: {node: '>= 18.12.0'} + peerDependencies: + webpack: 5.107.1 + peerDependenciesMeta: + webpack: + optional: true + + webpack-dev-server@5.2.4: + resolution: {integrity: sha512-GqDPGZN9bRqKBTkp4aWkobDDHMsrXKoGSdOH56smIri8qR0JG8gfL8/v/f/OZR3/OKXjG8uwJbFVhKm/FNU/UA==} + engines: {node: '>= 18.12.0'} + hasBin: true + peerDependencies: + webpack: 5.107.1 + webpack-cli: '*' + peerDependenciesMeta: + webpack: + optional: true + webpack-cli: + optional: true + + webpack-merge@5.8.0: + resolution: {integrity: sha512-/SaI7xY0831XwP6kzuwhKWVKDP9t1QY1h65lAFLbZqMPIuYcD9QAW4u9STIbU9kaJbPBB/geU/gLr1wDjOhQ+Q==} + engines: {node: '>=10.0.0'} + + webpack-merge@6.0.1: + resolution: {integrity: sha512-hXXvrjtx2PLYx4qruKl+kyRSLc52V+cCvMxRjmKwoA+CBbbF5GfIBtR6kCvl0fYGqTUPKB+1ktVmTHqMOzgCBg==} + engines: {node: '>=18.0.0'} + + webpack-node-externals@3.0.0: + resolution: {integrity: sha512-LnL6Z3GGDPht/AigwRh2dvL9PQPFQ8skEpVrWZXLWBYmqcaojHNN0onvHzie6rq7EWKrrBfPYqNEzTJgiwEQDQ==} + engines: {node: '>=6'} + + webpack-sources@3.4.1: + resolution: {integrity: sha512-eACpxRN02yaawnt+uUNIF7Qje6A9zArxBbcAJjK1PK3S9Ycg5jIuJ8pW4q8EMnwNZCEGltcjkRx1QzOxOkKD8A==} + engines: {node: '>=10.13.0'} + + webpack-subresource-integrity@5.1.0: + resolution: {integrity: sha512-sacXoX+xd8r4WKsy9MvH/q/vBtEHr86cpImXwyg74pFIpERKt6FmB8cXpeuh0ZLgclOlHI4Wcll7+R5L02xk9Q==} + engines: {node: '>= 12'} + peerDependencies: + html-webpack-plugin: '>= 5.0.0-beta.1 < 6' + webpack: 5.107.1 + peerDependenciesMeta: + html-webpack-plugin: + optional: true + + webpack@5.107.1: + resolution: {integrity: sha512-mvdIWxj/H6QsfgDdH9djne3a5dYcmEmtsXGESkypaGN5jXjF/b+9KDlmTDQ2TKlFUeA2fI9Y65kihD30JOdB+Q==} + engines: {node: '>=10.13.0'} + hasBin: true + peerDependencies: + webpack-cli: '*' + peerDependenciesMeta: + webpack-cli: + optional: true + + websocket-driver@0.7.4: + resolution: {integrity: sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg==} + engines: {node: '>=0.8.0'} + + websocket-extensions@0.1.4: + resolution: {integrity: sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==} + engines: {node: '>=0.8.0'} + + which-boxed-primitive@1.1.1: + resolution: {integrity: sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==} + engines: {node: '>= 0.4'} + + which-collection@1.0.2: + resolution: {integrity: sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==} + engines: {node: '>= 0.4'} + + which-typed-array@1.1.19: + resolution: {integrity: sha512-rEvr90Bck4WZt9HHFC4DJMsjvu7x+r6bImz0/BrbWb7A2djJ8hnZMrWnHo9F8ssv0OMErasDhftrfROTyqSDrw==} + engines: {node: '>= 0.4'} + + which@1.3.1: + resolution: {integrity: sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==} + hasBin: true + + which@2.0.2: + resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} + engines: {node: '>= 8'} + hasBin: true + + wildcard@2.0.1: + resolution: {integrity: sha512-CC1bOL87PIWSBhDcTrdeLo6eGT7mCFtrg0uIJtqJUFyK+eJnzl8A1niH56uu7KMa5XFrtiV+AQuHO3n7DsHnLQ==} + + word-wrap@1.2.5: + resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} + engines: {node: '>=0.10.0'} + + wrap-ansi@7.0.0: + resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} + engines: {node: '>=10'} + + wrappy@1.0.2: + resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} + + ws@8.21.0: + resolution: {integrity: sha512-Vsp28b7DRcimFQvrqu2Wek3z1iYxDCWqHYB8Qsnk/S4RfaCQzPGPyBNuVjJV3cd6UiKtUtp6sNM77gWvzcCH+g==} + engines: {node: '>=10.0.0'} + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: '>=5.0.2' + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + + wsl-utils@0.1.0: + resolution: {integrity: sha512-h3Fbisa2nKGPxCpm89Hk33lBLsnaGBvctQopaBSOW/uIs6FTe1ATyAnKFJrzVs9vpGdsTe73WF3V4lIsk4Gacw==} + engines: {node: '>=18'} + + y18n@5.0.8: + resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} + engines: {node: '>=10'} + + yallist@3.1.1: + resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} + + yallist@4.0.0: + resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} + + yaml@1.10.3: + resolution: {integrity: sha512-vIYeF1u3CjlhAFekPPAk2h/Kv4T3mAkMox5OymRiJQB0spDP10LHvt+K7G9Ny6NuuMAb25/6n1qyUjAcGNf/AA==} + engines: {node: '>= 6'} + + yargs-parser@21.1.1: + resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==} + engines: {node: '>=12'} + + yargs@17.7.2: + resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==} + engines: {node: '>=12'} + + yn@3.1.1: + resolution: {integrity: sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==} + engines: {node: '>=6'} + + yocto-queue@0.1.0: + 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'} + + zod-to-json-schema@3.25.2: + resolution: {integrity: sha512-O/PgfnpT1xKSDeQYSCfRI5Gy3hPf91mKVDuYLUHZJMiDFptvP41MSnWofm8dnCm0256ZNfZIM7DSzuSMAFnjHA==} + peerDependencies: + zod: ^3.25.28 || ^4 + + zod@3.25.76: + resolution: {integrity: sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==} + +snapshots: + + '@adobe/css-tools@4.4.3': {} + + '@ampproject/remapping@2.3.0': + dependencies: + '@jridgewell/gen-mapping': 0.3.12 + '@jridgewell/trace-mapping': 0.3.29 + + '@ant-design/colors@8.0.1': + dependencies: + '@ant-design/fast-color': 3.0.1 + + '@ant-design/cssinjs-utils@2.1.2(react-dom@19.1.1(react@19.1.1))(react@19.1.1)': + dependencies: + '@ant-design/cssinjs': 2.1.2(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@babel/runtime': 7.29.2 + '@rc-component/util': 1.10.1(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + react: 19.1.1 + react-dom: 19.1.1(react@19.1.1) + + '@ant-design/cssinjs@2.1.2(react-dom@19.1.1(react@19.1.1))(react@19.1.1)': + dependencies: + '@babel/runtime': 7.29.2 + '@emotion/hash': 0.8.0 + '@emotion/unitless': 0.7.5 + '@rc-component/util': 1.10.1(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + clsx: 2.1.1 + csstype: 3.2.3 + react: 19.1.1 + react-dom: 19.1.1(react@19.1.1) + stylis: 4.4.0 + + '@ant-design/fast-color@3.0.1': {} + + '@ant-design/icons-svg@4.4.2': {} + + '@ant-design/icons@6.2.2(react-dom@19.1.1(react@19.1.1))(react@19.1.1)': + dependencies: + '@ant-design/colors': 8.0.1 + '@ant-design/icons-svg': 4.4.2 + '@rc-component/util': 1.10.1(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + clsx: 2.1.1 + react: 19.1.1 + react-dom: 19.1.1(react@19.1.1) + + '@ant-design/react-slick@2.0.0(react-dom@19.1.1(react@19.1.1))(react@19.1.1)': + dependencies: + '@babel/runtime': 7.29.2 + clsx: 2.1.1 + json2mq: 0.2.0 + react: 19.1.1 + react-dom: 19.1.1(react@19.1.1) + throttle-debounce: 5.0.2 + + '@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.0': + dependencies: + '@babel/helper-validator-identifier': 7.28.5 + js-tokens: 4.0.0 + picocolors: 1.1.1 + + '@babel/compat-data@7.28.0': {} + + '@babel/compat-data@7.29.3': {} + + '@babel/core@7.20.2': + dependencies: + '@ampproject/remapping': 2.3.0 + '@babel/code-frame': 7.29.0 + '@babel/generator': 7.29.1 + '@babel/helper-compilation-targets': 7.28.6 + '@babel/helper-module-transforms': 7.28.6(@babel/core@7.20.2) + '@babel/helpers': 7.29.2 + '@babel/parser': 7.29.3 + '@babel/template': 7.28.6 + '@babel/traverse': 7.29.0 + '@babel/types': 7.29.0 + convert-source-map: 1.9.0 + debug: 4.4.3 + gensync: 1.0.0-beta.2 + json5: 2.2.3 + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + + '@babel/core@7.27.4': + dependencies: + '@ampproject/remapping': 2.3.0 + '@babel/code-frame': 7.27.1 + '@babel/generator': 7.28.0 + '@babel/helper-compilation-targets': 7.27.2 + '@babel/helper-module-transforms': 7.27.3(@babel/core@7.27.4) + '@babel/helpers': 7.27.6 + '@babel/parser': 7.28.0 + '@babel/template': 7.27.2 + '@babel/traverse': 7.28.0 + '@babel/types': 7.29.0 + convert-source-map: 2.0.0 + debug: 4.4.1 + gensync: 1.0.0-beta.2 + json5: 2.2.3 + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + + '@babel/generator@7.28.0': + dependencies: + '@babel/parser': 7.28.0 + '@babel/types': 7.29.0 + '@jridgewell/gen-mapping': 0.3.12 + '@jridgewell/trace-mapping': 0.3.29 + jsesc: 3.1.0 + + '@babel/generator@7.29.1': + dependencies: + '@babel/parser': 7.29.3 + '@babel/types': 7.29.0 + '@jridgewell/gen-mapping': 0.3.12 + '@jridgewell/trace-mapping': 0.3.29 + jsesc: 3.1.0 + + '@babel/helper-annotate-as-pure@7.27.3': + dependencies: + '@babel/types': 7.29.0 + + '@babel/helper-compilation-targets@7.27.2': + dependencies: + '@babel/compat-data': 7.28.0 + '@babel/helper-validator-option': 7.27.1 + browserslist: 4.25.1 + lru-cache: 5.1.1 + semver: 6.3.1 + + '@babel/helper-compilation-targets@7.28.6': + dependencies: + '@babel/compat-data': 7.29.3 + '@babel/helper-validator-option': 7.27.1 + browserslist: 4.28.2 + lru-cache: 5.1.1 + semver: 6.3.1 + + '@babel/helper-create-class-features-plugin@7.29.3(@babel/core@7.20.2)': + dependencies: + '@babel/core': 7.20.2 + '@babel/helper-annotate-as-pure': 7.27.3 + '@babel/helper-member-expression-to-functions': 7.28.5 + '@babel/helper-optimise-call-expression': 7.27.1 + '@babel/helper-replace-supers': 7.28.6(@babel/core@7.20.2) + '@babel/helper-skip-transparent-expression-wrappers': 7.27.1 + '@babel/traverse': 7.29.0 + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + + '@babel/helper-create-class-features-plugin@7.29.3(@babel/core@7.27.4)': + dependencies: + '@babel/core': 7.27.4 + '@babel/helper-annotate-as-pure': 7.27.3 + '@babel/helper-member-expression-to-functions': 7.28.5 + '@babel/helper-optimise-call-expression': 7.27.1 + '@babel/helper-replace-supers': 7.28.6(@babel/core@7.27.4) + '@babel/helper-skip-transparent-expression-wrappers': 7.27.1 + '@babel/traverse': 7.29.0 + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + + '@babel/helper-create-regexp-features-plugin@7.28.5(@babel/core@7.20.2)': + dependencies: + '@babel/core': 7.20.2 + '@babel/helper-annotate-as-pure': 7.27.3 + regexpu-core: 6.4.0 + semver: 6.3.1 + + '@babel/helper-create-regexp-features-plugin@7.28.5(@babel/core@7.27.4)': + dependencies: + '@babel/core': 7.27.4 + '@babel/helper-annotate-as-pure': 7.27.3 + regexpu-core: 6.4.0 + semver: 6.3.1 + + '@babel/helper-define-polyfill-provider@0.3.3(@babel/core@7.20.2)': + dependencies: + '@babel/core': 7.20.2 + '@babel/helper-compilation-targets': 7.28.6 + '@babel/helper-plugin-utils': 7.28.6 + debug: 4.4.3 + lodash.debounce: 4.0.8 + resolve: 1.22.12 + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + + '@babel/helper-define-polyfill-provider@0.3.3(@babel/core@7.27.4)': + dependencies: + '@babel/core': 7.27.4 + '@babel/helper-compilation-targets': 7.28.6 + '@babel/helper-plugin-utils': 7.28.6 + debug: 4.4.3 + lodash.debounce: 4.0.8 + resolve: 1.22.12 + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + + '@babel/helper-define-polyfill-provider@0.6.8(@babel/core@7.27.4)': + dependencies: + '@babel/core': 7.27.4 + '@babel/helper-compilation-targets': 7.28.6 + '@babel/helper-plugin-utils': 7.28.6 + debug: 4.4.3 + lodash.debounce: 4.0.8 + resolve: 1.22.12 + transitivePeerDependencies: + - supports-color + + '@babel/helper-environment-visitor@7.24.7': + dependencies: + '@babel/types': 7.29.0 + + '@babel/helper-globals@7.28.0': {} + + '@babel/helper-member-expression-to-functions@7.28.5': + dependencies: + '@babel/traverse': 7.29.0 + '@babel/types': 7.29.0 + transitivePeerDependencies: + - supports-color + + '@babel/helper-module-imports@7.27.1': + dependencies: + '@babel/traverse': 7.29.0 + '@babel/types': 7.29.0 + transitivePeerDependencies: + - supports-color + + '@babel/helper-module-imports@7.28.6': + dependencies: + '@babel/traverse': 7.29.0 + '@babel/types': 7.29.0 + transitivePeerDependencies: + - supports-color + + '@babel/helper-module-transforms@7.27.3(@babel/core@7.27.4)': + dependencies: + '@babel/core': 7.27.4 + '@babel/helper-module-imports': 7.27.1 + '@babel/helper-validator-identifier': 7.27.1 + '@babel/traverse': 7.28.0 + transitivePeerDependencies: + - supports-color + + '@babel/helper-module-transforms@7.28.6(@babel/core@7.20.2)': + dependencies: + '@babel/core': 7.20.2 + '@babel/helper-module-imports': 7.28.6 + '@babel/helper-validator-identifier': 7.28.5 + '@babel/traverse': 7.29.0 + transitivePeerDependencies: + - supports-color + + '@babel/helper-module-transforms@7.28.6(@babel/core@7.27.4)': + dependencies: + '@babel/core': 7.27.4 + '@babel/helper-module-imports': 7.28.6 + '@babel/helper-validator-identifier': 7.28.5 + '@babel/traverse': 7.29.0 + transitivePeerDependencies: + - supports-color + + '@babel/helper-optimise-call-expression@7.27.1': + dependencies: + '@babel/types': 7.29.0 + + '@babel/helper-plugin-utils@7.27.1': {} + + '@babel/helper-plugin-utils@7.28.6': {} + + '@babel/helper-remap-async-to-generator@7.27.1(@babel/core@7.20.2)': + dependencies: + '@babel/core': 7.20.2 + '@babel/helper-annotate-as-pure': 7.27.3 + '@babel/helper-wrap-function': 7.28.6 + '@babel/traverse': 7.29.0 + transitivePeerDependencies: + - supports-color + + '@babel/helper-remap-async-to-generator@7.27.1(@babel/core@7.27.4)': + dependencies: + '@babel/core': 7.27.4 + '@babel/helper-annotate-as-pure': 7.27.3 + '@babel/helper-wrap-function': 7.28.6 + '@babel/traverse': 7.29.0 + transitivePeerDependencies: + - supports-color + + '@babel/helper-replace-supers@7.28.6(@babel/core@7.20.2)': + dependencies: + '@babel/core': 7.20.2 + '@babel/helper-member-expression-to-functions': 7.28.5 + '@babel/helper-optimise-call-expression': 7.27.1 + '@babel/traverse': 7.29.0 + transitivePeerDependencies: + - supports-color + + '@babel/helper-replace-supers@7.28.6(@babel/core@7.27.4)': + dependencies: + '@babel/core': 7.27.4 + '@babel/helper-member-expression-to-functions': 7.28.5 + '@babel/helper-optimise-call-expression': 7.27.1 + '@babel/traverse': 7.29.0 + transitivePeerDependencies: + - supports-color + + '@babel/helper-skip-transparent-expression-wrappers@7.27.1': + dependencies: + '@babel/traverse': 7.29.0 + '@babel/types': 7.29.0 + transitivePeerDependencies: + - supports-color + + '@babel/helper-string-parser@7.27.1': {} + + '@babel/helper-validator-identifier@7.27.1': {} + + '@babel/helper-validator-identifier@7.28.5': {} + + '@babel/helper-validator-option@7.27.1': {} + + '@babel/helper-wrap-function@7.28.6': + dependencies: + '@babel/template': 7.28.6 + '@babel/traverse': 7.29.0 + '@babel/types': 7.29.0 + transitivePeerDependencies: + - supports-color + + '@babel/helpers@7.27.6': + dependencies: + '@babel/template': 7.27.2 + '@babel/types': 7.29.0 + + '@babel/helpers@7.29.2': + dependencies: + '@babel/template': 7.28.6 + '@babel/types': 7.29.0 + + '@babel/parser@7.28.0': + dependencies: + '@babel/types': 7.29.0 + + '@babel/parser@7.29.3': + dependencies: + '@babel/types': 7.29.0 + + '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@7.27.1(@babel/core@7.20.2)': + dependencies: + '@babel/core': 7.20.2 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@7.27.1(@babel/core@7.27.4)': + dependencies: + '@babel/core': 7.27.4 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@7.27.1(@babel/core@7.20.2)': + dependencies: + '@babel/core': 7.20.2 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/helper-skip-transparent-expression-wrappers': 7.27.1 + '@babel/plugin-transform-optional-chaining': 7.28.6(@babel/core@7.20.2) + transitivePeerDependencies: + - supports-color + + '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@7.27.1(@babel/core@7.27.4)': + dependencies: + '@babel/core': 7.27.4 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/helper-skip-transparent-expression-wrappers': 7.27.1 + '@babel/plugin-transform-optional-chaining': 7.28.6(@babel/core@7.27.4) + transitivePeerDependencies: + - supports-color + + '@babel/plugin-proposal-async-generator-functions@7.20.7(@babel/core@7.20.2)': + dependencies: + '@babel/core': 7.20.2 + '@babel/helper-environment-visitor': 7.24.7 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/helper-remap-async-to-generator': 7.27.1(@babel/core@7.20.2) + '@babel/plugin-syntax-async-generators': 7.8.4(@babel/core@7.20.2) + transitivePeerDependencies: + - supports-color + + '@babel/plugin-proposal-async-generator-functions@7.20.7(@babel/core@7.27.4)': + dependencies: + '@babel/core': 7.27.4 + '@babel/helper-environment-visitor': 7.24.7 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/helper-remap-async-to-generator': 7.27.1(@babel/core@7.27.4) + '@babel/plugin-syntax-async-generators': 7.8.4(@babel/core@7.27.4) + transitivePeerDependencies: + - supports-color + + '@babel/plugin-proposal-class-properties@7.18.6(@babel/core@7.20.2)': + dependencies: + '@babel/core': 7.20.2 + '@babel/helper-create-class-features-plugin': 7.29.3(@babel/core@7.20.2) + '@babel/helper-plugin-utils': 7.28.6 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-proposal-class-properties@7.18.6(@babel/core@7.27.4)': + dependencies: + '@babel/core': 7.27.4 + '@babel/helper-create-class-features-plugin': 7.29.3(@babel/core@7.27.4) + '@babel/helper-plugin-utils': 7.28.6 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-proposal-class-static-block@7.21.0(@babel/core@7.20.2)': + dependencies: + '@babel/core': 7.20.2 + '@babel/helper-create-class-features-plugin': 7.29.3(@babel/core@7.20.2) + '@babel/helper-plugin-utils': 7.28.6 + '@babel/plugin-syntax-class-static-block': 7.14.5(@babel/core@7.20.2) + transitivePeerDependencies: + - supports-color + + '@babel/plugin-proposal-class-static-block@7.21.0(@babel/core@7.27.4)': + dependencies: + '@babel/core': 7.27.4 + '@babel/helper-create-class-features-plugin': 7.29.3(@babel/core@7.27.4) + '@babel/helper-plugin-utils': 7.28.6 + '@babel/plugin-syntax-class-static-block': 7.14.5(@babel/core@7.27.4) + transitivePeerDependencies: + - supports-color + + '@babel/plugin-proposal-decorators@7.29.0(@babel/core@7.27.4)': + dependencies: + '@babel/core': 7.27.4 + '@babel/helper-create-class-features-plugin': 7.29.3(@babel/core@7.27.4) + '@babel/helper-plugin-utils': 7.28.6 + '@babel/plugin-syntax-decorators': 7.28.6(@babel/core@7.27.4) + transitivePeerDependencies: + - supports-color + + '@babel/plugin-proposal-dynamic-import@7.18.6(@babel/core@7.20.2)': + dependencies: + '@babel/core': 7.20.2 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/plugin-syntax-dynamic-import': 7.8.3(@babel/core@7.20.2) + + '@babel/plugin-proposal-dynamic-import@7.18.6(@babel/core@7.27.4)': + dependencies: + '@babel/core': 7.27.4 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/plugin-syntax-dynamic-import': 7.8.3(@babel/core@7.27.4) + + '@babel/plugin-proposal-export-namespace-from@7.18.9(@babel/core@7.20.2)': + dependencies: + '@babel/core': 7.20.2 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/plugin-syntax-export-namespace-from': 7.8.3(@babel/core@7.20.2) + + '@babel/plugin-proposal-export-namespace-from@7.18.9(@babel/core@7.27.4)': + dependencies: + '@babel/core': 7.27.4 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/plugin-syntax-export-namespace-from': 7.8.3(@babel/core@7.27.4) + + '@babel/plugin-proposal-json-strings@7.18.6(@babel/core@7.20.2)': + dependencies: + '@babel/core': 7.20.2 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/plugin-syntax-json-strings': 7.8.3(@babel/core@7.20.2) + + '@babel/plugin-proposal-json-strings@7.18.6(@babel/core@7.27.4)': + dependencies: + '@babel/core': 7.27.4 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/plugin-syntax-json-strings': 7.8.3(@babel/core@7.27.4) + + '@babel/plugin-proposal-logical-assignment-operators@7.20.7(@babel/core@7.20.2)': + dependencies: + '@babel/core': 7.20.2 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/plugin-syntax-logical-assignment-operators': 7.10.4(@babel/core@7.20.2) + + '@babel/plugin-proposal-logical-assignment-operators@7.20.7(@babel/core@7.27.4)': + dependencies: + '@babel/core': 7.27.4 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/plugin-syntax-logical-assignment-operators': 7.10.4(@babel/core@7.27.4) + + '@babel/plugin-proposal-nullish-coalescing-operator@7.18.6(@babel/core@7.20.2)': + dependencies: + '@babel/core': 7.20.2 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.20.2) + + '@babel/plugin-proposal-nullish-coalescing-operator@7.18.6(@babel/core@7.27.4)': + dependencies: + '@babel/core': 7.27.4 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.27.4) + + '@babel/plugin-proposal-numeric-separator@7.18.6(@babel/core@7.20.2)': + dependencies: + '@babel/core': 7.20.2 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/plugin-syntax-numeric-separator': 7.10.4(@babel/core@7.20.2) + + '@babel/plugin-proposal-numeric-separator@7.18.6(@babel/core@7.27.4)': + dependencies: + '@babel/core': 7.27.4 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/plugin-syntax-numeric-separator': 7.10.4(@babel/core@7.27.4) + + '@babel/plugin-proposal-object-rest-spread@7.20.7(@babel/core@7.20.2)': + dependencies: + '@babel/compat-data': 7.29.3 + '@babel/core': 7.20.2 + '@babel/helper-compilation-targets': 7.28.6 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.20.2) + '@babel/plugin-transform-parameters': 7.27.7(@babel/core@7.20.2) + + '@babel/plugin-proposal-object-rest-spread@7.20.7(@babel/core@7.27.4)': + dependencies: + '@babel/compat-data': 7.29.3 + '@babel/core': 7.27.4 + '@babel/helper-compilation-targets': 7.28.6 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.27.4) + '@babel/plugin-transform-parameters': 7.27.7(@babel/core@7.27.4) + + '@babel/plugin-proposal-optional-catch-binding@7.18.6(@babel/core@7.20.2)': + dependencies: + '@babel/core': 7.20.2 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@7.20.2) + + '@babel/plugin-proposal-optional-catch-binding@7.18.6(@babel/core@7.27.4)': + dependencies: + '@babel/core': 7.27.4 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@7.27.4) + + '@babel/plugin-proposal-optional-chaining@7.21.0(@babel/core@7.20.2)': + dependencies: + '@babel/core': 7.20.2 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/helper-skip-transparent-expression-wrappers': 7.27.1 + '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.20.2) + transitivePeerDependencies: + - supports-color + + '@babel/plugin-proposal-optional-chaining@7.21.0(@babel/core@7.27.4)': + dependencies: + '@babel/core': 7.27.4 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/helper-skip-transparent-expression-wrappers': 7.27.1 + '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.27.4) + transitivePeerDependencies: + - supports-color + + '@babel/plugin-proposal-private-methods@7.18.6(@babel/core@7.20.2)': + dependencies: + '@babel/core': 7.20.2 + '@babel/helper-create-class-features-plugin': 7.29.3(@babel/core@7.20.2) + '@babel/helper-plugin-utils': 7.28.6 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-proposal-private-methods@7.18.6(@babel/core@7.27.4)': + dependencies: + '@babel/core': 7.27.4 + '@babel/helper-create-class-features-plugin': 7.29.3(@babel/core@7.27.4) + '@babel/helper-plugin-utils': 7.28.6 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-proposal-private-property-in-object@7.21.11(@babel/core@7.20.2)': + dependencies: + '@babel/core': 7.20.2 + '@babel/helper-annotate-as-pure': 7.27.3 + '@babel/helper-create-class-features-plugin': 7.29.3(@babel/core@7.20.2) + '@babel/helper-plugin-utils': 7.28.6 + '@babel/plugin-syntax-private-property-in-object': 7.14.5(@babel/core@7.20.2) + transitivePeerDependencies: + - supports-color + + '@babel/plugin-proposal-private-property-in-object@7.21.11(@babel/core@7.27.4)': + dependencies: + '@babel/core': 7.27.4 + '@babel/helper-annotate-as-pure': 7.27.3 + '@babel/helper-create-class-features-plugin': 7.29.3(@babel/core@7.27.4) + '@babel/helper-plugin-utils': 7.28.6 + '@babel/plugin-syntax-private-property-in-object': 7.14.5(@babel/core@7.27.4) + transitivePeerDependencies: + - supports-color + + '@babel/plugin-proposal-unicode-property-regex@7.18.6(@babel/core@7.20.2)': + dependencies: + '@babel/core': 7.20.2 + '@babel/helper-create-regexp-features-plugin': 7.28.5(@babel/core@7.20.2) + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-proposal-unicode-property-regex@7.18.6(@babel/core@7.27.4)': + dependencies: + '@babel/core': 7.27.4 + '@babel/helper-create-regexp-features-plugin': 7.28.5(@babel/core@7.27.4) + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-syntax-async-generators@7.8.4(@babel/core@7.20.2)': + dependencies: + '@babel/core': 7.20.2 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-syntax-async-generators@7.8.4(@babel/core@7.27.4)': + dependencies: + '@babel/core': 7.27.4 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-syntax-class-properties@7.12.13(@babel/core@7.20.2)': + dependencies: + '@babel/core': 7.20.2 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-syntax-class-properties@7.12.13(@babel/core@7.27.4)': + dependencies: + '@babel/core': 7.27.4 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-syntax-class-static-block@7.14.5(@babel/core@7.20.2)': + dependencies: + '@babel/core': 7.20.2 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-syntax-class-static-block@7.14.5(@babel/core@7.27.4)': + dependencies: + '@babel/core': 7.27.4 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-syntax-decorators@7.28.6(@babel/core@7.27.4)': + dependencies: + '@babel/core': 7.27.4 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-syntax-dynamic-import@7.8.3(@babel/core@7.20.2)': + dependencies: + '@babel/core': 7.20.2 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-syntax-dynamic-import@7.8.3(@babel/core@7.27.4)': + dependencies: + '@babel/core': 7.27.4 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-syntax-export-namespace-from@7.8.3(@babel/core@7.20.2)': + dependencies: + '@babel/core': 7.20.2 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-syntax-export-namespace-from@7.8.3(@babel/core@7.27.4)': + dependencies: + '@babel/core': 7.27.4 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-syntax-import-assertions@7.28.6(@babel/core@7.20.2)': + dependencies: + '@babel/core': 7.20.2 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-syntax-import-assertions@7.28.6(@babel/core@7.27.4)': + dependencies: + '@babel/core': 7.27.4 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-syntax-json-strings@7.8.3(@babel/core@7.20.2)': + dependencies: + '@babel/core': 7.20.2 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-syntax-json-strings@7.8.3(@babel/core@7.27.4)': + dependencies: + '@babel/core': 7.27.4 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-syntax-jsx@7.27.1(@babel/core@7.20.2)': + dependencies: + '@babel/core': 7.20.2 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-syntax-jsx@7.27.1(@babel/core@7.27.4)': + dependencies: + '@babel/core': 7.27.4 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-syntax-logical-assignment-operators@7.10.4(@babel/core@7.20.2)': + dependencies: + '@babel/core': 7.20.2 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-syntax-logical-assignment-operators@7.10.4(@babel/core@7.27.4)': + dependencies: + '@babel/core': 7.27.4 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-syntax-nullish-coalescing-operator@7.8.3(@babel/core@7.20.2)': + dependencies: + '@babel/core': 7.20.2 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-syntax-nullish-coalescing-operator@7.8.3(@babel/core@7.27.4)': + dependencies: + '@babel/core': 7.27.4 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-syntax-numeric-separator@7.10.4(@babel/core@7.20.2)': + dependencies: + '@babel/core': 7.20.2 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-syntax-numeric-separator@7.10.4(@babel/core@7.27.4)': + dependencies: + '@babel/core': 7.27.4 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-syntax-object-rest-spread@7.8.3(@babel/core@7.20.2)': + dependencies: + '@babel/core': 7.20.2 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-syntax-object-rest-spread@7.8.3(@babel/core@7.27.4)': + dependencies: + '@babel/core': 7.27.4 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-syntax-optional-catch-binding@7.8.3(@babel/core@7.20.2)': + dependencies: + '@babel/core': 7.20.2 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-syntax-optional-catch-binding@7.8.3(@babel/core@7.27.4)': + dependencies: + '@babel/core': 7.27.4 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-syntax-optional-chaining@7.8.3(@babel/core@7.20.2)': + dependencies: + '@babel/core': 7.20.2 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-syntax-optional-chaining@7.8.3(@babel/core@7.27.4)': + dependencies: + '@babel/core': 7.27.4 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-syntax-private-property-in-object@7.14.5(@babel/core@7.20.2)': + dependencies: + '@babel/core': 7.20.2 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-syntax-private-property-in-object@7.14.5(@babel/core@7.27.4)': + dependencies: + '@babel/core': 7.27.4 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-syntax-top-level-await@7.14.5(@babel/core@7.20.2)': + dependencies: + '@babel/core': 7.20.2 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-syntax-top-level-await@7.14.5(@babel/core@7.27.4)': + dependencies: + '@babel/core': 7.27.4 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-syntax-typescript@7.28.6(@babel/core@7.20.2)': + dependencies: + '@babel/core': 7.20.2 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-syntax-typescript@7.28.6(@babel/core@7.27.4)': + dependencies: + '@babel/core': 7.27.4 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-arrow-functions@7.27.1(@babel/core@7.20.2)': + dependencies: + '@babel/core': 7.20.2 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-arrow-functions@7.27.1(@babel/core@7.27.4)': + dependencies: + '@babel/core': 7.27.4 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-async-to-generator@7.28.6(@babel/core@7.20.2)': + dependencies: + '@babel/core': 7.20.2 + '@babel/helper-module-imports': 7.28.6 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/helper-remap-async-to-generator': 7.27.1(@babel/core@7.20.2) + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-async-to-generator@7.28.6(@babel/core@7.27.4)': + dependencies: + '@babel/core': 7.27.4 + '@babel/helper-module-imports': 7.28.6 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/helper-remap-async-to-generator': 7.27.1(@babel/core@7.27.4) + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-block-scoped-functions@7.27.1(@babel/core@7.20.2)': + dependencies: + '@babel/core': 7.20.2 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-block-scoped-functions@7.27.1(@babel/core@7.27.4)': + dependencies: + '@babel/core': 7.27.4 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-block-scoping@7.28.6(@babel/core@7.20.2)': + dependencies: + '@babel/core': 7.20.2 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-block-scoping@7.28.6(@babel/core@7.27.4)': + dependencies: + '@babel/core': 7.27.4 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-classes@7.28.6(@babel/core@7.20.2)': + dependencies: + '@babel/core': 7.20.2 + '@babel/helper-annotate-as-pure': 7.27.3 + '@babel/helper-compilation-targets': 7.28.6 + '@babel/helper-globals': 7.28.0 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/helper-replace-supers': 7.28.6(@babel/core@7.20.2) + '@babel/traverse': 7.29.0 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-classes@7.28.6(@babel/core@7.27.4)': + dependencies: + '@babel/core': 7.27.4 + '@babel/helper-annotate-as-pure': 7.27.3 + '@babel/helper-compilation-targets': 7.28.6 + '@babel/helper-globals': 7.28.0 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/helper-replace-supers': 7.28.6(@babel/core@7.27.4) + '@babel/traverse': 7.29.0 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-computed-properties@7.28.6(@babel/core@7.20.2)': + dependencies: + '@babel/core': 7.20.2 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/template': 7.28.6 + + '@babel/plugin-transform-computed-properties@7.28.6(@babel/core@7.27.4)': + dependencies: + '@babel/core': 7.27.4 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/template': 7.28.6 + + '@babel/plugin-transform-destructuring@7.28.5(@babel/core@7.20.2)': + dependencies: + '@babel/core': 7.20.2 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/traverse': 7.29.0 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-destructuring@7.28.5(@babel/core@7.27.4)': + dependencies: + '@babel/core': 7.27.4 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/traverse': 7.29.0 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-dotall-regex@7.28.6(@babel/core@7.20.2)': + dependencies: + '@babel/core': 7.20.2 + '@babel/helper-create-regexp-features-plugin': 7.28.5(@babel/core@7.20.2) + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-dotall-regex@7.28.6(@babel/core@7.27.4)': + dependencies: + '@babel/core': 7.27.4 + '@babel/helper-create-regexp-features-plugin': 7.28.5(@babel/core@7.27.4) + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-duplicate-keys@7.27.1(@babel/core@7.20.2)': + dependencies: + '@babel/core': 7.20.2 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-duplicate-keys@7.27.1(@babel/core@7.27.4)': + dependencies: + '@babel/core': 7.27.4 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-exponentiation-operator@7.28.6(@babel/core@7.20.2)': + dependencies: + '@babel/core': 7.20.2 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-exponentiation-operator@7.28.6(@babel/core@7.27.4)': + dependencies: + '@babel/core': 7.27.4 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-for-of@7.27.1(@babel/core@7.20.2)': + dependencies: + '@babel/core': 7.20.2 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/helper-skip-transparent-expression-wrappers': 7.27.1 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-for-of@7.27.1(@babel/core@7.27.4)': + dependencies: + '@babel/core': 7.27.4 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/helper-skip-transparent-expression-wrappers': 7.27.1 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-function-name@7.27.1(@babel/core@7.20.2)': + dependencies: + '@babel/core': 7.20.2 + '@babel/helper-compilation-targets': 7.28.6 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/traverse': 7.29.0 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-function-name@7.27.1(@babel/core@7.27.4)': + dependencies: + '@babel/core': 7.27.4 + '@babel/helper-compilation-targets': 7.28.6 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/traverse': 7.29.0 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-literals@7.27.1(@babel/core@7.20.2)': + dependencies: + '@babel/core': 7.20.2 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-literals@7.27.1(@babel/core@7.27.4)': + dependencies: + '@babel/core': 7.27.4 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-member-expression-literals@7.27.1(@babel/core@7.20.2)': + dependencies: + '@babel/core': 7.20.2 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-member-expression-literals@7.27.1(@babel/core@7.27.4)': + dependencies: + '@babel/core': 7.27.4 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-modules-amd@7.27.1(@babel/core@7.20.2)': + dependencies: + '@babel/core': 7.20.2 + '@babel/helper-module-transforms': 7.28.6(@babel/core@7.20.2) + '@babel/helper-plugin-utils': 7.28.6 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-modules-amd@7.27.1(@babel/core@7.27.4)': + dependencies: + '@babel/core': 7.27.4 + '@babel/helper-module-transforms': 7.28.6(@babel/core@7.27.4) + '@babel/helper-plugin-utils': 7.28.6 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-modules-commonjs@7.28.6(@babel/core@7.20.2)': + dependencies: + '@babel/core': 7.20.2 + '@babel/helper-module-transforms': 7.28.6(@babel/core@7.20.2) + '@babel/helper-plugin-utils': 7.28.6 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-modules-commonjs@7.28.6(@babel/core@7.27.4)': + dependencies: + '@babel/core': 7.27.4 + '@babel/helper-module-transforms': 7.28.6(@babel/core@7.27.4) + '@babel/helper-plugin-utils': 7.28.6 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-modules-systemjs@7.29.4(@babel/core@7.20.2)': + dependencies: + '@babel/core': 7.20.2 + '@babel/helper-module-transforms': 7.28.6(@babel/core@7.20.2) + '@babel/helper-plugin-utils': 7.28.6 + '@babel/helper-validator-identifier': 7.28.5 + '@babel/traverse': 7.29.0 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-modules-systemjs@7.29.4(@babel/core@7.27.4)': + dependencies: + '@babel/core': 7.27.4 + '@babel/helper-module-transforms': 7.28.6(@babel/core@7.27.4) + '@babel/helper-plugin-utils': 7.28.6 + '@babel/helper-validator-identifier': 7.28.5 + '@babel/traverse': 7.29.0 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-modules-umd@7.27.1(@babel/core@7.20.2)': + dependencies: + '@babel/core': 7.20.2 + '@babel/helper-module-transforms': 7.28.6(@babel/core@7.20.2) + '@babel/helper-plugin-utils': 7.28.6 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-modules-umd@7.27.1(@babel/core@7.27.4)': + dependencies: + '@babel/core': 7.27.4 + '@babel/helper-module-transforms': 7.28.6(@babel/core@7.27.4) + '@babel/helper-plugin-utils': 7.28.6 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-named-capturing-groups-regex@7.29.0(@babel/core@7.20.2)': + dependencies: + '@babel/core': 7.20.2 + '@babel/helper-create-regexp-features-plugin': 7.28.5(@babel/core@7.20.2) + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-named-capturing-groups-regex@7.29.0(@babel/core@7.27.4)': + dependencies: + '@babel/core': 7.27.4 + '@babel/helper-create-regexp-features-plugin': 7.28.5(@babel/core@7.27.4) + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-new-target@7.27.1(@babel/core@7.20.2)': + dependencies: + '@babel/core': 7.20.2 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-new-target@7.27.1(@babel/core@7.27.4)': + dependencies: + '@babel/core': 7.27.4 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-object-super@7.27.1(@babel/core@7.20.2)': + dependencies: + '@babel/core': 7.20.2 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/helper-replace-supers': 7.28.6(@babel/core@7.20.2) + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-object-super@7.27.1(@babel/core@7.27.4)': + dependencies: + '@babel/core': 7.27.4 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/helper-replace-supers': 7.28.6(@babel/core@7.27.4) + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-optional-chaining@7.28.6(@babel/core@7.20.2)': + dependencies: + '@babel/core': 7.20.2 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/helper-skip-transparent-expression-wrappers': 7.27.1 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-optional-chaining@7.28.6(@babel/core@7.27.4)': + dependencies: + '@babel/core': 7.27.4 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/helper-skip-transparent-expression-wrappers': 7.27.1 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-parameters@7.27.7(@babel/core@7.20.2)': + dependencies: + '@babel/core': 7.20.2 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-parameters@7.27.7(@babel/core@7.27.4)': + dependencies: + '@babel/core': 7.27.4 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-property-literals@7.27.1(@babel/core@7.20.2)': + dependencies: + '@babel/core': 7.20.2 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-property-literals@7.27.1(@babel/core@7.27.4)': + dependencies: + '@babel/core': 7.27.4 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-react-constant-elements@7.27.1(@babel/core@7.27.4)': + dependencies: + '@babel/core': 7.27.4 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-react-display-name@7.28.0(@babel/core@7.20.2)': + dependencies: + '@babel/core': 7.20.2 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-react-display-name@7.28.0(@babel/core@7.27.4)': + dependencies: + '@babel/core': 7.27.4 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-react-jsx-development@7.27.1(@babel/core@7.20.2)': + dependencies: + '@babel/core': 7.20.2 + '@babel/plugin-transform-react-jsx': 7.27.1(@babel/core@7.20.2) + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-react-jsx-development@7.27.1(@babel/core@7.27.4)': + dependencies: + '@babel/core': 7.27.4 + '@babel/plugin-transform-react-jsx': 7.27.1(@babel/core@7.27.4) + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-react-jsx-self@7.27.1(@babel/core@7.27.4)': + dependencies: + '@babel/core': 7.27.4 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-react-jsx-source@7.27.1(@babel/core@7.27.4)': + dependencies: + '@babel/core': 7.27.4 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-react-jsx@7.27.1(@babel/core@7.20.2)': + dependencies: + '@babel/core': 7.20.2 + '@babel/helper-annotate-as-pure': 7.27.3 + '@babel/helper-module-imports': 7.27.1 + '@babel/helper-plugin-utils': 7.27.1 + '@babel/plugin-syntax-jsx': 7.27.1(@babel/core@7.20.2) + '@babel/types': 7.29.0 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-react-jsx@7.27.1(@babel/core@7.27.4)': + dependencies: + '@babel/core': 7.27.4 + '@babel/helper-annotate-as-pure': 7.27.3 + '@babel/helper-module-imports': 7.27.1 + '@babel/helper-plugin-utils': 7.27.1 + '@babel/plugin-syntax-jsx': 7.27.1(@babel/core@7.27.4) + '@babel/types': 7.29.0 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-react-pure-annotations@7.27.1(@babel/core@7.20.2)': + dependencies: + '@babel/core': 7.20.2 + '@babel/helper-annotate-as-pure': 7.27.3 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-react-pure-annotations@7.27.1(@babel/core@7.27.4)': + dependencies: + '@babel/core': 7.27.4 + '@babel/helper-annotate-as-pure': 7.27.3 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-regenerator@7.29.0(@babel/core@7.20.2)': + dependencies: + '@babel/core': 7.20.2 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-regenerator@7.29.0(@babel/core@7.27.4)': + dependencies: + '@babel/core': 7.27.4 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-reserved-words@7.27.1(@babel/core@7.20.2)': + dependencies: + '@babel/core': 7.20.2 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-reserved-words@7.27.1(@babel/core@7.27.4)': + dependencies: + '@babel/core': 7.27.4 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-runtime@7.29.0(@babel/core@7.27.4)': + dependencies: + '@babel/core': 7.27.4 + '@babel/helper-module-imports': 7.28.6 + '@babel/helper-plugin-utils': 7.28.6 + babel-plugin-polyfill-corejs2: 0.4.17(@babel/core@7.27.4) + babel-plugin-polyfill-corejs3: 0.13.0(@babel/core@7.27.4) + babel-plugin-polyfill-regenerator: 0.6.8(@babel/core@7.27.4) + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-shorthand-properties@7.27.1(@babel/core@7.20.2)': + dependencies: + '@babel/core': 7.20.2 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-shorthand-properties@7.27.1(@babel/core@7.27.4)': + dependencies: + '@babel/core': 7.27.4 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-spread@7.28.6(@babel/core@7.20.2)': + dependencies: + '@babel/core': 7.20.2 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/helper-skip-transparent-expression-wrappers': 7.27.1 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-spread@7.28.6(@babel/core@7.27.4)': + dependencies: + '@babel/core': 7.27.4 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/helper-skip-transparent-expression-wrappers': 7.27.1 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-sticky-regex@7.27.1(@babel/core@7.20.2)': + dependencies: + '@babel/core': 7.20.2 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-sticky-regex@7.27.1(@babel/core@7.27.4)': + dependencies: + '@babel/core': 7.27.4 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-template-literals@7.27.1(@babel/core@7.20.2)': + dependencies: + '@babel/core': 7.20.2 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-template-literals@7.27.1(@babel/core@7.27.4)': + dependencies: + '@babel/core': 7.27.4 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-typeof-symbol@7.27.1(@babel/core@7.20.2)': + dependencies: + '@babel/core': 7.20.2 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-typeof-symbol@7.27.1(@babel/core@7.27.4)': + dependencies: + '@babel/core': 7.27.4 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-typescript@7.28.6(@babel/core@7.20.2)': + dependencies: + '@babel/core': 7.20.2 + '@babel/helper-annotate-as-pure': 7.27.3 + '@babel/helper-create-class-features-plugin': 7.29.3(@babel/core@7.20.2) + '@babel/helper-plugin-utils': 7.28.6 + '@babel/helper-skip-transparent-expression-wrappers': 7.27.1 + '@babel/plugin-syntax-typescript': 7.28.6(@babel/core@7.20.2) + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-typescript@7.28.6(@babel/core@7.27.4)': + dependencies: + '@babel/core': 7.27.4 + '@babel/helper-annotate-as-pure': 7.27.3 + '@babel/helper-create-class-features-plugin': 7.29.3(@babel/core@7.27.4) + '@babel/helper-plugin-utils': 7.28.6 + '@babel/helper-skip-transparent-expression-wrappers': 7.27.1 + '@babel/plugin-syntax-typescript': 7.28.6(@babel/core@7.27.4) + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-unicode-escapes@7.27.1(@babel/core@7.20.2)': + dependencies: + '@babel/core': 7.20.2 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-unicode-escapes@7.27.1(@babel/core@7.27.4)': + dependencies: + '@babel/core': 7.27.4 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-unicode-regex@7.27.1(@babel/core@7.20.2)': + dependencies: + '@babel/core': 7.20.2 + '@babel/helper-create-regexp-features-plugin': 7.28.5(@babel/core@7.20.2) + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-unicode-regex@7.27.1(@babel/core@7.27.4)': + dependencies: + '@babel/core': 7.27.4 + '@babel/helper-create-regexp-features-plugin': 7.28.5(@babel/core@7.27.4) + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/preset-env@7.20.2(@babel/core@7.20.2)': + dependencies: + '@babel/compat-data': 7.29.3 + '@babel/core': 7.20.2 + '@babel/helper-compilation-targets': 7.28.6 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/helper-validator-option': 7.27.1 + '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression': 7.27.1(@babel/core@7.20.2) + '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining': 7.27.1(@babel/core@7.20.2) + '@babel/plugin-proposal-async-generator-functions': 7.20.7(@babel/core@7.20.2) + '@babel/plugin-proposal-class-properties': 7.18.6(@babel/core@7.20.2) + '@babel/plugin-proposal-class-static-block': 7.21.0(@babel/core@7.20.2) + '@babel/plugin-proposal-dynamic-import': 7.18.6(@babel/core@7.20.2) + '@babel/plugin-proposal-export-namespace-from': 7.18.9(@babel/core@7.20.2) + '@babel/plugin-proposal-json-strings': 7.18.6(@babel/core@7.20.2) + '@babel/plugin-proposal-logical-assignment-operators': 7.20.7(@babel/core@7.20.2) + '@babel/plugin-proposal-nullish-coalescing-operator': 7.18.6(@babel/core@7.20.2) + '@babel/plugin-proposal-numeric-separator': 7.18.6(@babel/core@7.20.2) + '@babel/plugin-proposal-object-rest-spread': 7.20.7(@babel/core@7.20.2) + '@babel/plugin-proposal-optional-catch-binding': 7.18.6(@babel/core@7.20.2) + '@babel/plugin-proposal-optional-chaining': 7.21.0(@babel/core@7.20.2) + '@babel/plugin-proposal-private-methods': 7.18.6(@babel/core@7.20.2) + '@babel/plugin-proposal-private-property-in-object': 7.21.11(@babel/core@7.20.2) + '@babel/plugin-proposal-unicode-property-regex': 7.18.6(@babel/core@7.20.2) + '@babel/plugin-syntax-async-generators': 7.8.4(@babel/core@7.20.2) + '@babel/plugin-syntax-class-properties': 7.12.13(@babel/core@7.20.2) + '@babel/plugin-syntax-class-static-block': 7.14.5(@babel/core@7.20.2) + '@babel/plugin-syntax-dynamic-import': 7.8.3(@babel/core@7.20.2) + '@babel/plugin-syntax-export-namespace-from': 7.8.3(@babel/core@7.20.2) + '@babel/plugin-syntax-import-assertions': 7.28.6(@babel/core@7.20.2) + '@babel/plugin-syntax-json-strings': 7.8.3(@babel/core@7.20.2) + '@babel/plugin-syntax-logical-assignment-operators': 7.10.4(@babel/core@7.20.2) + '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.20.2) + '@babel/plugin-syntax-numeric-separator': 7.10.4(@babel/core@7.20.2) + '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.20.2) + '@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@7.20.2) + '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.20.2) + '@babel/plugin-syntax-private-property-in-object': 7.14.5(@babel/core@7.20.2) + '@babel/plugin-syntax-top-level-await': 7.14.5(@babel/core@7.20.2) + '@babel/plugin-transform-arrow-functions': 7.27.1(@babel/core@7.20.2) + '@babel/plugin-transform-async-to-generator': 7.28.6(@babel/core@7.20.2) + '@babel/plugin-transform-block-scoped-functions': 7.27.1(@babel/core@7.20.2) + '@babel/plugin-transform-block-scoping': 7.28.6(@babel/core@7.20.2) + '@babel/plugin-transform-classes': 7.28.6(@babel/core@7.20.2) + '@babel/plugin-transform-computed-properties': 7.28.6(@babel/core@7.20.2) + '@babel/plugin-transform-destructuring': 7.28.5(@babel/core@7.20.2) + '@babel/plugin-transform-dotall-regex': 7.28.6(@babel/core@7.20.2) + '@babel/plugin-transform-duplicate-keys': 7.27.1(@babel/core@7.20.2) + '@babel/plugin-transform-exponentiation-operator': 7.28.6(@babel/core@7.20.2) + '@babel/plugin-transform-for-of': 7.27.1(@babel/core@7.20.2) + '@babel/plugin-transform-function-name': 7.27.1(@babel/core@7.20.2) + '@babel/plugin-transform-literals': 7.27.1(@babel/core@7.20.2) + '@babel/plugin-transform-member-expression-literals': 7.27.1(@babel/core@7.20.2) + '@babel/plugin-transform-modules-amd': 7.27.1(@babel/core@7.20.2) + '@babel/plugin-transform-modules-commonjs': 7.28.6(@babel/core@7.20.2) + '@babel/plugin-transform-modules-systemjs': 7.29.4(@babel/core@7.20.2) + '@babel/plugin-transform-modules-umd': 7.27.1(@babel/core@7.20.2) + '@babel/plugin-transform-named-capturing-groups-regex': 7.29.0(@babel/core@7.20.2) + '@babel/plugin-transform-new-target': 7.27.1(@babel/core@7.20.2) + '@babel/plugin-transform-object-super': 7.27.1(@babel/core@7.20.2) + '@babel/plugin-transform-parameters': 7.27.7(@babel/core@7.20.2) + '@babel/plugin-transform-property-literals': 7.27.1(@babel/core@7.20.2) + '@babel/plugin-transform-regenerator': 7.29.0(@babel/core@7.20.2) + '@babel/plugin-transform-reserved-words': 7.27.1(@babel/core@7.20.2) + '@babel/plugin-transform-shorthand-properties': 7.27.1(@babel/core@7.20.2) + '@babel/plugin-transform-spread': 7.28.6(@babel/core@7.20.2) + '@babel/plugin-transform-sticky-regex': 7.27.1(@babel/core@7.20.2) + '@babel/plugin-transform-template-literals': 7.27.1(@babel/core@7.20.2) + '@babel/plugin-transform-typeof-symbol': 7.27.1(@babel/core@7.20.2) + '@babel/plugin-transform-unicode-escapes': 7.27.1(@babel/core@7.20.2) + '@babel/plugin-transform-unicode-regex': 7.27.1(@babel/core@7.20.2) + '@babel/preset-modules': 0.1.6(@babel/core@7.20.2) + '@babel/types': 7.29.0 + babel-plugin-polyfill-corejs2: 0.3.3(@babel/core@7.20.2) + babel-plugin-polyfill-corejs3: 0.6.0(@babel/core@7.20.2) + babel-plugin-polyfill-regenerator: 0.4.1(@babel/core@7.20.2) + core-js-compat: 3.49.0 + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + + '@babel/preset-env@7.20.2(@babel/core@7.27.4)': + dependencies: + '@babel/compat-data': 7.29.3 + '@babel/core': 7.27.4 + '@babel/helper-compilation-targets': 7.28.6 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/helper-validator-option': 7.27.1 + '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression': 7.27.1(@babel/core@7.27.4) + '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining': 7.27.1(@babel/core@7.27.4) + '@babel/plugin-proposal-async-generator-functions': 7.20.7(@babel/core@7.27.4) + '@babel/plugin-proposal-class-properties': 7.18.6(@babel/core@7.27.4) + '@babel/plugin-proposal-class-static-block': 7.21.0(@babel/core@7.27.4) + '@babel/plugin-proposal-dynamic-import': 7.18.6(@babel/core@7.27.4) + '@babel/plugin-proposal-export-namespace-from': 7.18.9(@babel/core@7.27.4) + '@babel/plugin-proposal-json-strings': 7.18.6(@babel/core@7.27.4) + '@babel/plugin-proposal-logical-assignment-operators': 7.20.7(@babel/core@7.27.4) + '@babel/plugin-proposal-nullish-coalescing-operator': 7.18.6(@babel/core@7.27.4) + '@babel/plugin-proposal-numeric-separator': 7.18.6(@babel/core@7.27.4) + '@babel/plugin-proposal-object-rest-spread': 7.20.7(@babel/core@7.27.4) + '@babel/plugin-proposal-optional-catch-binding': 7.18.6(@babel/core@7.27.4) + '@babel/plugin-proposal-optional-chaining': 7.21.0(@babel/core@7.27.4) + '@babel/plugin-proposal-private-methods': 7.18.6(@babel/core@7.27.4) + '@babel/plugin-proposal-private-property-in-object': 7.21.11(@babel/core@7.27.4) + '@babel/plugin-proposal-unicode-property-regex': 7.18.6(@babel/core@7.27.4) + '@babel/plugin-syntax-async-generators': 7.8.4(@babel/core@7.27.4) + '@babel/plugin-syntax-class-properties': 7.12.13(@babel/core@7.27.4) + '@babel/plugin-syntax-class-static-block': 7.14.5(@babel/core@7.27.4) + '@babel/plugin-syntax-dynamic-import': 7.8.3(@babel/core@7.27.4) + '@babel/plugin-syntax-export-namespace-from': 7.8.3(@babel/core@7.27.4) + '@babel/plugin-syntax-import-assertions': 7.28.6(@babel/core@7.27.4) + '@babel/plugin-syntax-json-strings': 7.8.3(@babel/core@7.27.4) + '@babel/plugin-syntax-logical-assignment-operators': 7.10.4(@babel/core@7.27.4) + '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.27.4) + '@babel/plugin-syntax-numeric-separator': 7.10.4(@babel/core@7.27.4) + '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.27.4) + '@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@7.27.4) + '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.27.4) + '@babel/plugin-syntax-private-property-in-object': 7.14.5(@babel/core@7.27.4) + '@babel/plugin-syntax-top-level-await': 7.14.5(@babel/core@7.27.4) + '@babel/plugin-transform-arrow-functions': 7.27.1(@babel/core@7.27.4) + '@babel/plugin-transform-async-to-generator': 7.28.6(@babel/core@7.27.4) + '@babel/plugin-transform-block-scoped-functions': 7.27.1(@babel/core@7.27.4) + '@babel/plugin-transform-block-scoping': 7.28.6(@babel/core@7.27.4) + '@babel/plugin-transform-classes': 7.28.6(@babel/core@7.27.4) + '@babel/plugin-transform-computed-properties': 7.28.6(@babel/core@7.27.4) + '@babel/plugin-transform-destructuring': 7.28.5(@babel/core@7.27.4) + '@babel/plugin-transform-dotall-regex': 7.28.6(@babel/core@7.27.4) + '@babel/plugin-transform-duplicate-keys': 7.27.1(@babel/core@7.27.4) + '@babel/plugin-transform-exponentiation-operator': 7.28.6(@babel/core@7.27.4) + '@babel/plugin-transform-for-of': 7.27.1(@babel/core@7.27.4) + '@babel/plugin-transform-function-name': 7.27.1(@babel/core@7.27.4) + '@babel/plugin-transform-literals': 7.27.1(@babel/core@7.27.4) + '@babel/plugin-transform-member-expression-literals': 7.27.1(@babel/core@7.27.4) + '@babel/plugin-transform-modules-amd': 7.27.1(@babel/core@7.27.4) + '@babel/plugin-transform-modules-commonjs': 7.28.6(@babel/core@7.27.4) + '@babel/plugin-transform-modules-systemjs': 7.29.4(@babel/core@7.27.4) + '@babel/plugin-transform-modules-umd': 7.27.1(@babel/core@7.27.4) + '@babel/plugin-transform-named-capturing-groups-regex': 7.29.0(@babel/core@7.27.4) + '@babel/plugin-transform-new-target': 7.27.1(@babel/core@7.27.4) + '@babel/plugin-transform-object-super': 7.27.1(@babel/core@7.27.4) + '@babel/plugin-transform-parameters': 7.27.7(@babel/core@7.27.4) + '@babel/plugin-transform-property-literals': 7.27.1(@babel/core@7.27.4) + '@babel/plugin-transform-regenerator': 7.29.0(@babel/core@7.27.4) + '@babel/plugin-transform-reserved-words': 7.27.1(@babel/core@7.27.4) + '@babel/plugin-transform-shorthand-properties': 7.27.1(@babel/core@7.27.4) + '@babel/plugin-transform-spread': 7.28.6(@babel/core@7.27.4) + '@babel/plugin-transform-sticky-regex': 7.27.1(@babel/core@7.27.4) + '@babel/plugin-transform-template-literals': 7.27.1(@babel/core@7.27.4) + '@babel/plugin-transform-typeof-symbol': 7.27.1(@babel/core@7.27.4) + '@babel/plugin-transform-unicode-escapes': 7.27.1(@babel/core@7.27.4) + '@babel/plugin-transform-unicode-regex': 7.27.1(@babel/core@7.27.4) + '@babel/preset-modules': 0.1.6(@babel/core@7.27.4) + '@babel/types': 7.29.0 + babel-plugin-polyfill-corejs2: 0.3.3(@babel/core@7.27.4) + babel-plugin-polyfill-corejs3: 0.6.0(@babel/core@7.27.4) + babel-plugin-polyfill-regenerator: 0.4.1(@babel/core@7.27.4) + core-js-compat: 3.49.0 + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + + '@babel/preset-modules@0.1.6(@babel/core@7.20.2)': + dependencies: + '@babel/core': 7.20.2 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/plugin-proposal-unicode-property-regex': 7.18.6(@babel/core@7.20.2) + '@babel/plugin-transform-dotall-regex': 7.28.6(@babel/core@7.20.2) + '@babel/types': 7.29.0 + esutils: 2.0.3 + + '@babel/preset-modules@0.1.6(@babel/core@7.27.4)': + dependencies: + '@babel/core': 7.27.4 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/plugin-proposal-unicode-property-regex': 7.18.6(@babel/core@7.27.4) + '@babel/plugin-transform-dotall-regex': 7.28.6(@babel/core@7.27.4) + '@babel/types': 7.29.0 + esutils: 2.0.3 + + '@babel/preset-react@7.18.6(@babel/core@7.20.2)': + dependencies: + '@babel/core': 7.20.2 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/helper-validator-option': 7.27.1 + '@babel/plugin-transform-react-display-name': 7.28.0(@babel/core@7.20.2) + '@babel/plugin-transform-react-jsx': 7.27.1(@babel/core@7.20.2) + '@babel/plugin-transform-react-jsx-development': 7.27.1(@babel/core@7.20.2) + '@babel/plugin-transform-react-pure-annotations': 7.27.1(@babel/core@7.20.2) + transitivePeerDependencies: + - supports-color + + '@babel/preset-react@7.27.1(@babel/core@7.27.4)': + dependencies: + '@babel/core': 7.27.4 + '@babel/helper-plugin-utils': 7.27.1 + '@babel/helper-validator-option': 7.27.1 + '@babel/plugin-transform-react-display-name': 7.28.0(@babel/core@7.27.4) + '@babel/plugin-transform-react-jsx': 7.27.1(@babel/core@7.27.4) + '@babel/plugin-transform-react-jsx-development': 7.27.1(@babel/core@7.27.4) + '@babel/plugin-transform-react-pure-annotations': 7.27.1(@babel/core@7.27.4) + transitivePeerDependencies: + - supports-color + + '@babel/preset-typescript@7.18.6(@babel/core@7.20.2)': + dependencies: + '@babel/core': 7.20.2 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/helper-validator-option': 7.27.1 + '@babel/plugin-transform-typescript': 7.28.6(@babel/core@7.20.2) + transitivePeerDependencies: + - supports-color + + '@babel/preset-typescript@7.18.6(@babel/core@7.27.4)': + dependencies: + '@babel/core': 7.27.4 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/helper-validator-option': 7.27.1 + '@babel/plugin-transform-typescript': 7.28.6(@babel/core@7.27.4) + transitivePeerDependencies: + - supports-color + + '@babel/runtime@7.29.2': {} + + '@babel/template@7.27.2': + dependencies: + '@babel/code-frame': 7.27.1 + '@babel/parser': 7.28.0 + '@babel/types': 7.29.0 + + '@babel/template@7.28.6': + dependencies: + '@babel/code-frame': 7.29.0 + '@babel/parser': 7.29.3 + '@babel/types': 7.29.0 + + '@babel/traverse@7.28.0': + dependencies: + '@babel/code-frame': 7.27.1 + '@babel/generator': 7.28.0 + '@babel/helper-globals': 7.28.0 + '@babel/parser': 7.28.0 + '@babel/template': 7.27.2 + '@babel/types': 7.29.0 + debug: 4.4.1 + transitivePeerDependencies: + - supports-color + + '@babel/traverse@7.29.0': + dependencies: + '@babel/code-frame': 7.29.0 + '@babel/generator': 7.29.1 + '@babel/helper-globals': 7.28.0 + '@babel/parser': 7.29.3 + '@babel/template': 7.28.6 + '@babel/types': 7.29.0 + debug: 4.4.3 + transitivePeerDependencies: + - supports-color + + '@babel/types@7.29.0': + dependencies: + '@babel/helper-string-parser': 7.27.1 + '@babel/helper-validator-identifier': 7.28.5 + + '@biomejs/biome@1.9.4': + optionalDependencies: + '@biomejs/cli-darwin-arm64': 1.9.4 + '@biomejs/cli-darwin-x64': 1.9.4 + '@biomejs/cli-linux-arm64': 1.9.4 + '@biomejs/cli-linux-arm64-musl': 1.9.4 + '@biomejs/cli-linux-x64': 1.9.4 + '@biomejs/cli-linux-x64-musl': 1.9.4 + '@biomejs/cli-win32-arm64': 1.9.4 + '@biomejs/cli-win32-x64': 1.9.4 + + '@biomejs/cli-darwin-arm64@1.9.4': + optional: true + + '@biomejs/cli-darwin-x64@1.9.4': + optional: true + + '@biomejs/cli-linux-arm64-musl@1.9.4': + optional: true + + '@biomejs/cli-linux-arm64@1.9.4': + optional: true + + '@biomejs/cli-linux-x64-musl@1.9.4': + optional: true + + '@biomejs/cli-linux-x64@1.9.4': + optional: true + + '@biomejs/cli-win32-arm64@1.9.4': + optional: true + + '@biomejs/cli-win32-x64@1.9.4': + optional: true + + '@chakra-ui/anatomy@2.3.6': {} + + '@chakra-ui/hooks@2.4.5(react@18.2.0)': + dependencies: + '@chakra-ui/utils': 2.2.5(react@18.2.0) + '@zag-js/element-size': 0.31.1 + copy-to-clipboard: 3.3.3 + framesync: 6.1.2 + react: 18.2.0 + + '@chakra-ui/react@2.10.9(@emotion/react@11.14.0(@types/react@18.3.23)(react@18.2.0))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.23)(react@18.2.0))(@types/react@18.3.23)(react@18.2.0))(@types/react@18.3.23)(framer-motion@12.38.0(@emotion/is-prop-valid@1.3.1)(react-dom@18.2.0(react@18.2.0))(react@18.2.0))(react-dom@18.2.0(react@18.2.0))(react@18.2.0)': + dependencies: + '@chakra-ui/hooks': 2.4.5(react@18.2.0) + '@chakra-ui/styled-system': 2.12.4(react@18.2.0) + '@chakra-ui/theme': 3.4.9(@chakra-ui/styled-system@2.12.4(react@18.2.0))(react@18.2.0) + '@chakra-ui/utils': 2.2.5(react@18.2.0) + '@emotion/react': 11.14.0(@types/react@18.3.23)(react@18.2.0) + '@emotion/styled': 11.14.1(@emotion/react@11.14.0(@types/react@18.3.23)(react@18.2.0))(@types/react@18.3.23)(react@18.2.0) + '@popperjs/core': 2.11.8 + '@zag-js/focus-visible': 0.31.1 + aria-hidden: 1.2.6 + framer-motion: 12.38.0(@emotion/is-prop-valid@1.3.1)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + react-fast-compare: 3.2.2 + react-focus-lock: 2.13.7(@types/react@18.3.23)(react@18.2.0) + react-remove-scroll: 2.7.2(@types/react@18.3.23)(react@18.2.0) + transitivePeerDependencies: + - '@types/react' + + '@chakra-ui/styled-system@2.12.4(react@18.2.0)': + dependencies: + '@chakra-ui/utils': 2.2.5(react@18.2.0) + csstype: 3.2.3 + transitivePeerDependencies: + - react + + '@chakra-ui/theme-tools@2.2.9(@chakra-ui/styled-system@2.12.4(react@18.2.0))(react@18.2.0)': + dependencies: + '@chakra-ui/anatomy': 2.3.6 + '@chakra-ui/styled-system': 2.12.4(react@18.2.0) + '@chakra-ui/utils': 2.2.5(react@18.2.0) + color2k: 2.0.3 + transitivePeerDependencies: + - react + + '@chakra-ui/theme@3.4.9(@chakra-ui/styled-system@2.12.4(react@18.2.0))(react@18.2.0)': + dependencies: + '@chakra-ui/anatomy': 2.3.6 + '@chakra-ui/styled-system': 2.12.4(react@18.2.0) + '@chakra-ui/theme-tools': 2.2.9(@chakra-ui/styled-system@2.12.4(react@18.2.0))(react@18.2.0) + '@chakra-ui/utils': 2.2.5(react@18.2.0) + transitivePeerDependencies: + - react + + '@chakra-ui/utils@2.2.5(react@18.2.0)': + dependencies: + '@types/lodash.mergewith': 4.6.9 + lodash.mergewith: 4.6.2 + react: 18.2.0 + + '@changesets/apply-release-plan@7.1.1': + dependencies: + '@changesets/config': 3.1.4 + '@changesets/get-version-range-type': 0.4.0 + '@changesets/git': 3.0.4 + '@changesets/should-skip-package': 0.1.2 + '@changesets/types': 6.1.0 + '@manypkg/get-packages': 1.1.3 + detect-indent: 6.1.0 + fs-extra: 7.0.1 + lodash.startcase: 4.4.0 + outdent: 0.5.0 + prettier: 2.8.8 + resolve-from: 5.0.0 + semver: 7.7.4 + + '@changesets/assemble-release-plan@6.0.10': + dependencies: + '@changesets/errors': 0.2.0 + '@changesets/get-dependents-graph': 2.1.4 + '@changesets/should-skip-package': 0.1.2 + '@changesets/types': 6.1.0 + '@manypkg/get-packages': 1.1.3 + semver: 7.7.4 + + '@changesets/changelog-git@0.2.1': + dependencies: + '@changesets/types': 6.1.0 + + '@changesets/cli@2.31.0(@types/node@24.0.13)': + dependencies: + '@changesets/apply-release-plan': 7.1.1 + '@changesets/assemble-release-plan': 6.0.10 + '@changesets/changelog-git': 0.2.1 + '@changesets/config': 3.1.4 + '@changesets/errors': 0.2.0 + '@changesets/get-dependents-graph': 2.1.4 + '@changesets/get-release-plan': 4.0.16 + '@changesets/git': 3.0.4 + '@changesets/logger': 0.1.1 + '@changesets/pre': 2.0.2 + '@changesets/read': 0.6.7 + '@changesets/should-skip-package': 0.1.2 + '@changesets/types': 6.1.0 + '@changesets/write': 0.4.0 + '@inquirer/external-editor': 1.0.3(@types/node@24.0.13) + '@manypkg/get-packages': 1.1.3 + ansi-colors: 4.1.3 + enquirer: 2.4.1 + fs-extra: 7.0.1 + mri: 1.2.0 + package-manager-detector: 0.2.11 + picocolors: 1.1.1 + resolve-from: 5.0.0 + semver: 7.7.4 + spawndamnit: 3.0.1 + term-size: 2.2.1 + transitivePeerDependencies: + - '@types/node' + + '@changesets/config@3.1.4': + dependencies: + '@changesets/errors': 0.2.0 + '@changesets/get-dependents-graph': 2.1.4 + '@changesets/logger': 0.1.1 + '@changesets/should-skip-package': 0.1.2 + '@changesets/types': 6.1.0 + '@manypkg/get-packages': 1.1.3 + fs-extra: 7.0.1 + micromatch: 4.0.8 + + '@changesets/errors@0.2.0': + dependencies: + extendable-error: 0.1.7 + + '@changesets/get-dependents-graph@2.1.4': + dependencies: + '@changesets/types': 6.1.0 + '@manypkg/get-packages': 1.1.3 + picocolors: 1.1.1 + semver: 7.7.4 + + '@changesets/get-release-plan@4.0.16': + dependencies: + '@changesets/assemble-release-plan': 6.0.10 + '@changesets/config': 3.1.4 + '@changesets/pre': 2.0.2 + '@changesets/read': 0.6.7 + '@changesets/types': 6.1.0 + '@manypkg/get-packages': 1.1.3 + + '@changesets/get-version-range-type@0.4.0': {} + + '@changesets/git@3.0.4': + dependencies: + '@changesets/errors': 0.2.0 + '@manypkg/get-packages': 1.1.3 + is-subdir: 1.2.0 + micromatch: 4.0.8 + spawndamnit: 3.0.1 + + '@changesets/logger@0.1.1': + dependencies: + picocolors: 1.1.1 + + '@changesets/parse@0.4.3': + dependencies: + '@changesets/types': 6.1.0 + js-yaml: 4.1.1 + + '@changesets/pre@2.0.2': + dependencies: + '@changesets/errors': 0.2.0 + '@changesets/types': 6.1.0 + '@manypkg/get-packages': 1.1.3 + fs-extra: 7.0.1 + + '@changesets/read@0.6.7': + dependencies: + '@changesets/git': 3.0.4 + '@changesets/logger': 0.1.1 + '@changesets/parse': 0.4.3 + '@changesets/types': 6.1.0 + fs-extra: 7.0.1 + p-filter: 2.1.0 + picocolors: 1.1.1 + + '@changesets/should-skip-package@0.1.2': + dependencies: + '@changesets/types': 6.1.0 + '@manypkg/get-packages': 1.1.3 + + '@changesets/types@4.1.0': {} + + '@changesets/types@6.1.0': {} + + '@changesets/write@0.4.0': + dependencies: + '@changesets/types': 6.1.0 + fs-extra: 7.0.1 + human-id: 4.2.0 + prettier: 2.8.8 + + '@cspotcode/source-map-support@0.8.1': + dependencies: + '@jridgewell/trace-mapping': 0.3.9 + + '@discoveryjs/json-ext@0.6.3': {} + + '@emnapi/runtime@1.10.0': + dependencies: + tslib: 2.8.1 + optional: true + + '@emotion/babel-plugin@11.13.5': + dependencies: + '@babel/helper-module-imports': 7.28.6 + '@babel/runtime': 7.29.2 + '@emotion/hash': 0.9.2 + '@emotion/memoize': 0.9.0 + '@emotion/serialize': 1.3.3 + babel-plugin-macros: 3.1.0 + convert-source-map: 1.9.0 + escape-string-regexp: 4.0.0 + find-root: 1.1.0 + source-map: 0.5.7 + stylis: 4.2.0 + transitivePeerDependencies: + - supports-color + + '@emotion/cache@11.14.0': + dependencies: + '@emotion/memoize': 0.9.0 + '@emotion/sheet': 1.4.0 + '@emotion/utils': 1.4.2 + '@emotion/weak-memoize': 0.4.0 + stylis: 4.2.0 + + '@emotion/hash@0.8.0': {} + + '@emotion/hash@0.9.2': {} + + '@emotion/is-prop-valid@1.3.1': + dependencies: + '@emotion/memoize': 0.9.0 + + '@emotion/memoize@0.9.0': {} + + '@emotion/react@11.14.0(@types/react@18.3.23)(react@18.2.0)': + dependencies: + '@babel/runtime': 7.29.2 + '@emotion/babel-plugin': 11.13.5 + '@emotion/cache': 11.14.0 + '@emotion/serialize': 1.3.3 + '@emotion/use-insertion-effect-with-fallbacks': 1.2.0(react@18.2.0) + '@emotion/utils': 1.4.2 + '@emotion/weak-memoize': 0.4.0 + hoist-non-react-statics: 3.3.2 + react: 18.2.0 + optionalDependencies: + '@types/react': 18.3.23 + transitivePeerDependencies: + - supports-color + + '@emotion/react@11.14.0(@types/react@18.3.23)(react@18.3.1)': + dependencies: + '@babel/runtime': 7.29.2 + '@emotion/babel-plugin': 11.13.5 + '@emotion/cache': 11.14.0 + '@emotion/serialize': 1.3.3 + '@emotion/use-insertion-effect-with-fallbacks': 1.2.0(react@18.3.1) + '@emotion/utils': 1.4.2 + '@emotion/weak-memoize': 0.4.0 + hoist-non-react-statics: 3.3.2 + react: 18.3.1 + optionalDependencies: + '@types/react': 18.3.23 + transitivePeerDependencies: + - supports-color + + '@emotion/serialize@1.3.3': + dependencies: + '@emotion/hash': 0.9.2 + '@emotion/memoize': 0.9.0 + '@emotion/unitless': 0.10.0 + '@emotion/utils': 1.4.2 + csstype: 3.2.3 + + '@emotion/sheet@1.4.0': {} + + '@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.23)(react@18.2.0))(@types/react@18.3.23)(react@18.2.0)': + dependencies: + '@babel/runtime': 7.29.2 + '@emotion/babel-plugin': 11.13.5 + '@emotion/is-prop-valid': 1.3.1 + '@emotion/react': 11.14.0(@types/react@18.3.23)(react@18.2.0) + '@emotion/serialize': 1.3.3 + '@emotion/use-insertion-effect-with-fallbacks': 1.2.0(react@18.2.0) + '@emotion/utils': 1.4.2 + react: 18.2.0 + optionalDependencies: + '@types/react': 18.3.23 + transitivePeerDependencies: + - supports-color + + '@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.23)(react@18.3.1))(@types/react@18.3.23)(react@18.3.1)': + dependencies: + '@babel/runtime': 7.29.2 + '@emotion/babel-plugin': 11.13.5 + '@emotion/is-prop-valid': 1.3.1 + '@emotion/react': 11.14.0(@types/react@18.3.23)(react@18.3.1) + '@emotion/serialize': 1.3.3 + '@emotion/use-insertion-effect-with-fallbacks': 1.2.0(react@18.3.1) + '@emotion/utils': 1.4.2 + react: 18.3.1 + optionalDependencies: + '@types/react': 18.3.23 + transitivePeerDependencies: + - supports-color + + '@emotion/unitless@0.10.0': {} + + '@emotion/unitless@0.7.5': {} + + '@emotion/use-insertion-effect-with-fallbacks@1.2.0(react@18.2.0)': + dependencies: + react: 18.2.0 + + '@emotion/use-insertion-effect-with-fallbacks@1.2.0(react@18.3.1)': + dependencies: + react: 18.3.1 + + '@emotion/utils@1.4.2': {} + + '@emotion/weak-memoize@0.4.0': {} + + '@esbuild/aix-ppc64@0.25.6': + optional: true + + '@esbuild/android-arm64@0.25.6': + optional: true + + '@esbuild/android-arm@0.25.6': + optional: true + + '@esbuild/android-x64@0.25.6': + optional: true + + '@esbuild/darwin-arm64@0.25.6': + optional: true + + '@esbuild/darwin-x64@0.25.6': + optional: true + + '@esbuild/freebsd-arm64@0.25.6': + optional: true + + '@esbuild/freebsd-x64@0.25.6': + optional: true + + '@esbuild/linux-arm64@0.25.6': + optional: true + + '@esbuild/linux-arm@0.25.6': + optional: true + + '@esbuild/linux-ia32@0.25.6': + optional: true + + '@esbuild/linux-loong64@0.25.6': + optional: true + + '@esbuild/linux-mips64el@0.25.6': + optional: true + + '@esbuild/linux-ppc64@0.25.6': + optional: true + + '@esbuild/linux-riscv64@0.25.6': + optional: true + + '@esbuild/linux-s390x@0.25.6': + optional: true + + '@esbuild/linux-x64@0.25.6': + optional: true + + '@esbuild/netbsd-arm64@0.25.6': + optional: true + + '@esbuild/netbsd-x64@0.25.6': + optional: true + + '@esbuild/openbsd-arm64@0.25.6': + optional: true + + '@esbuild/openbsd-x64@0.25.6': + optional: true + + '@esbuild/openharmony-arm64@0.25.6': + optional: true + + '@esbuild/sunos-x64@0.25.6': + optional: true + + '@esbuild/win32-arm64@0.25.6': + optional: true + + '@esbuild/win32-ia32@0.25.6': + optional: true + + '@esbuild/win32-x64@0.25.6': + optional: true + + '@eslint-community/eslint-utils@4.9.1(eslint@8.23.1)': + dependencies: + eslint: 8.23.1 + eslint-visitor-keys: 3.4.3 + + '@eslint/eslintrc@1.4.1': + dependencies: + ajv: 6.15.0 + debug: 4.4.3 + espree: 9.6.1 + globals: 13.24.0 + ignore: 5.3.2 + import-fresh: 3.3.1 + js-yaml: 4.1.1 + minimatch: 3.1.5 + strip-json-comments: 3.1.1 + transitivePeerDependencies: + - supports-color + + '@floating-ui/core@1.7.5': + dependencies: + '@floating-ui/utils': 0.2.11 + + '@floating-ui/dom@1.7.6': + dependencies: + '@floating-ui/core': 1.7.5 + '@floating-ui/utils': 0.2.11 + + '@floating-ui/react-dom@2.1.8(react-dom@19.1.1(react@19.1.1))(react@19.1.1)': + dependencies: + '@floating-ui/dom': 1.7.6 + react: 19.1.1 + react-dom: 19.1.1(react@19.1.1) + + '@floating-ui/react@0.27.19(react-dom@19.1.1(react@19.1.1))(react@19.1.1)': + dependencies: + '@floating-ui/react-dom': 2.1.8(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@floating-ui/utils': 0.2.11 + react: 19.1.1 + react-dom: 19.1.1(react@19.1.1) + tabbable: 6.4.0 + + '@floating-ui/utils@0.2.11': {} + + '@heroicons/react@2.2.0(react@18.2.0)': + dependencies: + react: 18.2.0 + + '@hono/node-server@1.19.14(hono@4.12.23)': + dependencies: + hono: 4.12.23 + + '@humanwhocodes/config-array@0.10.7': + dependencies: + '@humanwhocodes/object-schema': 1.2.1 + debug: 4.4.3 + minimatch: 3.1.5 + transitivePeerDependencies: + - supports-color + + '@humanwhocodes/gitignore-to-minimatch@1.0.2': {} + + '@humanwhocodes/module-importer@1.0.1': {} + + '@humanwhocodes/object-schema@1.2.1': {} + + '@img/colour@1.1.0': + optional: true + + '@img/sharp-darwin-arm64@0.34.5': + optionalDependencies: + '@img/sharp-libvips-darwin-arm64': 1.2.4 + optional: true + + '@img/sharp-darwin-x64@0.34.5': + optionalDependencies: + '@img/sharp-libvips-darwin-x64': 1.2.4 + optional: true + + '@img/sharp-libvips-darwin-arm64@1.2.4': + optional: true + + '@img/sharp-libvips-darwin-x64@1.2.4': + optional: true + + '@img/sharp-libvips-linux-arm64@1.2.4': + optional: true + + '@img/sharp-libvips-linux-arm@1.2.4': + optional: true + + '@img/sharp-libvips-linux-ppc64@1.2.4': + optional: true + + '@img/sharp-libvips-linux-riscv64@1.2.4': + optional: true + + '@img/sharp-libvips-linux-s390x@1.2.4': + optional: true + + '@img/sharp-libvips-linux-x64@1.2.4': + optional: true + + '@img/sharp-libvips-linuxmusl-arm64@1.2.4': + optional: true + + '@img/sharp-libvips-linuxmusl-x64@1.2.4': + optional: true + + '@img/sharp-linux-arm64@0.34.5': + optionalDependencies: + '@img/sharp-libvips-linux-arm64': 1.2.4 + optional: true + + '@img/sharp-linux-arm@0.34.5': + optionalDependencies: + '@img/sharp-libvips-linux-arm': 1.2.4 + optional: true + + '@img/sharp-linux-ppc64@0.34.5': + optionalDependencies: + '@img/sharp-libvips-linux-ppc64': 1.2.4 + optional: true + + '@img/sharp-linux-riscv64@0.34.5': + optionalDependencies: + '@img/sharp-libvips-linux-riscv64': 1.2.4 + optional: true + + '@img/sharp-linux-s390x@0.34.5': + optionalDependencies: + '@img/sharp-libvips-linux-s390x': 1.2.4 + optional: true + + '@img/sharp-linux-x64@0.34.5': + optionalDependencies: + '@img/sharp-libvips-linux-x64': 1.2.4 + optional: true + + '@img/sharp-linuxmusl-arm64@0.34.5': + optionalDependencies: + '@img/sharp-libvips-linuxmusl-arm64': 1.2.4 + optional: true + + '@img/sharp-linuxmusl-x64@0.34.5': + optionalDependencies: + '@img/sharp-libvips-linuxmusl-x64': 1.2.4 + optional: true + + '@img/sharp-wasm32@0.34.5': + dependencies: + '@emnapi/runtime': 1.10.0 + optional: true + + '@img/sharp-win32-arm64@0.34.5': + optional: true + + '@img/sharp-win32-ia32@0.34.5': + optional: true + + '@img/sharp-win32-x64@0.34.5': + optional: true + + '@inquirer/external-editor@1.0.3(@types/node@24.0.13)': + dependencies: + chardet: 2.1.1 + iconv-lite: 0.7.2 + optionalDependencies: + '@types/node': 24.0.13 + + '@jest/diff-sequences@30.0.1': {} + + '@jest/expect-utils@30.0.4': + dependencies: + '@jest/get-type': 30.0.1 + + '@jest/get-type@30.0.1': {} + + '@jest/pattern@30.0.1': + dependencies: + '@types/node': 24.0.13 + jest-regex-util: 30.0.1 + + '@jest/schemas@30.0.1': + dependencies: + '@sinclair/typebox': 0.34.37 + + '@jest/types@30.0.1': + dependencies: + '@jest/pattern': 30.0.1 + '@jest/schemas': 30.0.1 + '@types/istanbul-lib-coverage': 2.0.6 + '@types/istanbul-reports': 3.0.4 + '@types/node': 24.0.13 + '@types/yargs': 17.0.33 + chalk: 4.1.2 + + '@jridgewell/gen-mapping@0.3.12': + dependencies: + '@jridgewell/sourcemap-codec': 1.5.4 + '@jridgewell/trace-mapping': 0.3.29 + + '@jridgewell/resolve-uri@3.1.2': {} + + '@jridgewell/source-map@0.3.11': + dependencies: + '@jridgewell/gen-mapping': 0.3.12 + '@jridgewell/trace-mapping': 0.3.29 + + '@jridgewell/sourcemap-codec@1.5.4': {} + + '@jridgewell/trace-mapping@0.3.29': + dependencies: + '@jridgewell/resolve-uri': 3.1.2 + '@jridgewell/sourcemap-codec': 1.5.4 + + '@jridgewell/trace-mapping@0.3.9': + dependencies: + '@jridgewell/resolve-uri': 3.1.2 + '@jridgewell/sourcemap-codec': 1.5.4 + + '@jsonjoy.com/base64@1.1.2(tslib@2.8.1)': + dependencies: + tslib: 2.8.1 + + '@jsonjoy.com/base64@17.67.0(tslib@2.8.1)': + dependencies: + tslib: 2.8.1 + + '@jsonjoy.com/buffers@1.2.1(tslib@2.8.1)': + dependencies: + tslib: 2.8.1 + + '@jsonjoy.com/buffers@17.67.0(tslib@2.8.1)': + dependencies: + tslib: 2.8.1 + + '@jsonjoy.com/codegen@1.0.0(tslib@2.8.1)': + dependencies: + tslib: 2.8.1 + + '@jsonjoy.com/codegen@17.67.0(tslib@2.8.1)': + dependencies: + tslib: 2.8.1 + + '@jsonjoy.com/fs-core@4.57.2(tslib@2.8.1)': + dependencies: + '@jsonjoy.com/fs-node-builtins': 4.57.2(tslib@2.8.1) + '@jsonjoy.com/fs-node-utils': 4.57.2(tslib@2.8.1) + thingies: 2.6.0(tslib@2.8.1) + tslib: 2.8.1 + + '@jsonjoy.com/fs-fsa@4.57.2(tslib@2.8.1)': + dependencies: + '@jsonjoy.com/fs-core': 4.57.2(tslib@2.8.1) + '@jsonjoy.com/fs-node-builtins': 4.57.2(tslib@2.8.1) + '@jsonjoy.com/fs-node-utils': 4.57.2(tslib@2.8.1) + thingies: 2.6.0(tslib@2.8.1) + tslib: 2.8.1 + + '@jsonjoy.com/fs-node-builtins@4.57.2(tslib@2.8.1)': + dependencies: + tslib: 2.8.1 + + '@jsonjoy.com/fs-node-to-fsa@4.57.2(tslib@2.8.1)': + dependencies: + '@jsonjoy.com/fs-fsa': 4.57.2(tslib@2.8.1) + '@jsonjoy.com/fs-node-builtins': 4.57.2(tslib@2.8.1) + '@jsonjoy.com/fs-node-utils': 4.57.2(tslib@2.8.1) + tslib: 2.8.1 + + '@jsonjoy.com/fs-node-utils@4.57.2(tslib@2.8.1)': + dependencies: + '@jsonjoy.com/fs-node-builtins': 4.57.2(tslib@2.8.1) + tslib: 2.8.1 + + '@jsonjoy.com/fs-node@4.57.2(tslib@2.8.1)': + dependencies: + '@jsonjoy.com/fs-core': 4.57.2(tslib@2.8.1) + '@jsonjoy.com/fs-node-builtins': 4.57.2(tslib@2.8.1) + '@jsonjoy.com/fs-node-utils': 4.57.2(tslib@2.8.1) + '@jsonjoy.com/fs-print': 4.57.2(tslib@2.8.1) + '@jsonjoy.com/fs-snapshot': 4.57.2(tslib@2.8.1) + glob-to-regex.js: 1.2.0(tslib@2.8.1) + thingies: 2.6.0(tslib@2.8.1) + tslib: 2.8.1 + + '@jsonjoy.com/fs-print@4.57.2(tslib@2.8.1)': + dependencies: + '@jsonjoy.com/fs-node-utils': 4.57.2(tslib@2.8.1) + tree-dump: 1.1.0(tslib@2.8.1) + tslib: 2.8.1 + + '@jsonjoy.com/fs-snapshot@4.57.2(tslib@2.8.1)': + dependencies: + '@jsonjoy.com/buffers': 17.67.0(tslib@2.8.1) + '@jsonjoy.com/fs-node-utils': 4.57.2(tslib@2.8.1) + '@jsonjoy.com/json-pack': 17.67.0(tslib@2.8.1) + '@jsonjoy.com/util': 17.67.0(tslib@2.8.1) + tslib: 2.8.1 + + '@jsonjoy.com/json-pack@1.21.0(tslib@2.8.1)': + dependencies: + '@jsonjoy.com/base64': 1.1.2(tslib@2.8.1) + '@jsonjoy.com/buffers': 1.2.1(tslib@2.8.1) + '@jsonjoy.com/codegen': 1.0.0(tslib@2.8.1) + '@jsonjoy.com/json-pointer': 1.0.2(tslib@2.8.1) + '@jsonjoy.com/util': 1.9.0(tslib@2.8.1) + hyperdyperid: 1.2.0 + thingies: 2.6.0(tslib@2.8.1) + tree-dump: 1.1.0(tslib@2.8.1) + tslib: 2.8.1 + + '@jsonjoy.com/json-pack@17.67.0(tslib@2.8.1)': + dependencies: + '@jsonjoy.com/base64': 17.67.0(tslib@2.8.1) + '@jsonjoy.com/buffers': 17.67.0(tslib@2.8.1) + '@jsonjoy.com/codegen': 17.67.0(tslib@2.8.1) + '@jsonjoy.com/json-pointer': 17.67.0(tslib@2.8.1) + '@jsonjoy.com/util': 17.67.0(tslib@2.8.1) + hyperdyperid: 1.2.0 + thingies: 2.6.0(tslib@2.8.1) + tree-dump: 1.1.0(tslib@2.8.1) + tslib: 2.8.1 + + '@jsonjoy.com/json-pointer@1.0.2(tslib@2.8.1)': + dependencies: + '@jsonjoy.com/codegen': 1.0.0(tslib@2.8.1) + '@jsonjoy.com/util': 1.9.0(tslib@2.8.1) + tslib: 2.8.1 + + '@jsonjoy.com/json-pointer@17.67.0(tslib@2.8.1)': + dependencies: + '@jsonjoy.com/util': 17.67.0(tslib@2.8.1) + tslib: 2.8.1 + + '@jsonjoy.com/util@1.9.0(tslib@2.8.1)': + dependencies: + '@jsonjoy.com/buffers': 1.2.1(tslib@2.8.1) + '@jsonjoy.com/codegen': 1.0.0(tslib@2.8.1) + tslib: 2.8.1 + + '@jsonjoy.com/util@17.67.0(tslib@2.8.1)': + dependencies: + '@jsonjoy.com/buffers': 17.67.0(tslib@2.8.1) + '@jsonjoy.com/codegen': 17.67.0(tslib@2.8.1) + tslib: 2.8.1 + + '@leichtgewicht/ip-codec@2.0.5': {} + + '@mantine/core@8.3.9(@mantine/hooks@8.3.9(react@19.1.1))(@types/react@19.1.8)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)': + dependencies: + '@floating-ui/react': 0.27.19(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@mantine/hooks': 8.3.9(react@19.1.1) + clsx: 2.1.1 + react: 19.1.1 + react-dom: 19.1.1(react@19.1.1) + react-number-format: 5.4.5(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + react-remove-scroll: 2.7.2(@types/react@19.1.8)(react@19.1.1) + react-textarea-autosize: 8.5.9(@types/react@19.1.8)(react@19.1.1) + type-fest: 4.41.0 + transitivePeerDependencies: + - '@types/react' + + '@mantine/hooks@8.3.9(react@19.1.1)': + dependencies: + react: 19.1.1 + + '@manypkg/find-root@1.1.0': + dependencies: + '@babel/runtime': 7.29.2 + '@types/node': 12.20.55 + find-up: 4.1.0 + fs-extra: 8.1.0 + + '@manypkg/get-packages@1.1.3': + dependencies: + '@babel/runtime': 7.29.2 + '@changesets/types': 4.1.0 + '@manypkg/find-root': 1.1.0 + fs-extra: 8.1.0 + globby: 11.1.0 + read-yaml-file: 1.1.0 + + '@modelcontextprotocol/sdk@1.29.0(zod@3.25.76)': + dependencies: + '@hono/node-server': 1.19.14(hono@4.12.23) + ajv: 8.20.0 + ajv-formats: 3.0.1(ajv@8.20.0) + 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.23 + jose: 6.2.3 + json-schema-typed: 8.0.2 + pkce-challenge: 5.0.1 + raw-body: 3.0.2 + zod: 3.25.76 + zod-to-json-schema: 3.25.2(zod@3.25.76) + transitivePeerDependencies: + - supports-color + + '@mui/core-downloads-tracker@5.18.0': {} + + '@mui/icons-material@5.18.0(@mui/material@5.18.0(@emotion/react@11.14.0(@types/react@18.3.23)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.23)(react@18.3.1))(@types/react@18.3.23)(react@18.3.1))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@types/react@18.3.23)(react@18.3.1)': + dependencies: + '@babel/runtime': 7.29.2 + '@mui/material': 5.18.0(@emotion/react@11.14.0(@types/react@18.3.23)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.23)(react@18.3.1))(@types/react@18.3.23)(react@18.3.1))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react: 18.3.1 + optionalDependencies: + '@types/react': 18.3.23 + + '@mui/material@5.18.0(@emotion/react@11.14.0(@types/react@18.3.23)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.23)(react@18.3.1))(@types/react@18.3.23)(react@18.3.1))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@babel/runtime': 7.29.2 + '@mui/core-downloads-tracker': 5.18.0 + '@mui/system': 5.18.0(@emotion/react@11.14.0(@types/react@18.3.23)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.23)(react@18.3.1))(@types/react@18.3.23)(react@18.3.1))(@types/react@18.3.23)(react@18.3.1) + '@mui/types': 7.2.24(@types/react@18.3.23) + '@mui/utils': 5.17.1(@types/react@18.3.23)(react@18.3.1) + '@popperjs/core': 2.11.8 + '@types/react-transition-group': 4.4.12(@types/react@18.3.23) + clsx: 2.1.1 + csstype: 3.2.3 + prop-types: 15.8.1 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + react-is: 19.1.0 + react-transition-group: 4.4.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + optionalDependencies: + '@emotion/react': 11.14.0(@types/react@18.3.23)(react@18.3.1) + '@emotion/styled': 11.14.1(@emotion/react@11.14.0(@types/react@18.3.23)(react@18.3.1))(@types/react@18.3.23)(react@18.3.1) + '@types/react': 18.3.23 + + '@mui/private-theming@5.17.1(@types/react@18.3.23)(react@18.3.1)': + dependencies: + '@babel/runtime': 7.29.2 + '@mui/utils': 5.17.1(@types/react@18.3.23)(react@18.3.1) + prop-types: 15.8.1 + react: 18.3.1 + optionalDependencies: + '@types/react': 18.3.23 + + '@mui/styled-engine@5.18.0(@emotion/react@11.14.0(@types/react@18.3.23)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.23)(react@18.3.1))(@types/react@18.3.23)(react@18.3.1))(react@18.3.1)': + dependencies: + '@babel/runtime': 7.29.2 + '@emotion/cache': 11.14.0 + '@emotion/serialize': 1.3.3 + csstype: 3.2.3 + prop-types: 15.8.1 + react: 18.3.1 + optionalDependencies: + '@emotion/react': 11.14.0(@types/react@18.3.23)(react@18.3.1) + '@emotion/styled': 11.14.1(@emotion/react@11.14.0(@types/react@18.3.23)(react@18.3.1))(@types/react@18.3.23)(react@18.3.1) + + '@mui/system@5.18.0(@emotion/react@11.14.0(@types/react@18.3.23)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.23)(react@18.3.1))(@types/react@18.3.23)(react@18.3.1))(@types/react@18.3.23)(react@18.3.1)': + dependencies: + '@babel/runtime': 7.29.2 + '@mui/private-theming': 5.17.1(@types/react@18.3.23)(react@18.3.1) + '@mui/styled-engine': 5.18.0(@emotion/react@11.14.0(@types/react@18.3.23)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.23)(react@18.3.1))(@types/react@18.3.23)(react@18.3.1))(react@18.3.1) + '@mui/types': 7.2.24(@types/react@18.3.23) + '@mui/utils': 5.17.1(@types/react@18.3.23)(react@18.3.1) + clsx: 2.1.1 + csstype: 3.2.3 + prop-types: 15.8.1 + react: 18.3.1 + optionalDependencies: + '@emotion/react': 11.14.0(@types/react@18.3.23)(react@18.3.1) + '@emotion/styled': 11.14.1(@emotion/react@11.14.0(@types/react@18.3.23)(react@18.3.1))(@types/react@18.3.23)(react@18.3.1) + '@types/react': 18.3.23 + + '@mui/types@7.2.24(@types/react@18.3.23)': + optionalDependencies: + '@types/react': 18.3.23 + + '@mui/utils@5.17.1(@types/react@18.3.23)(react@18.3.1)': + dependencies: + '@babel/runtime': 7.29.2 + '@mui/types': 7.2.24(@types/react@18.3.23) + '@types/prop-types': 15.7.15 + clsx: 2.1.1 + prop-types: 15.8.1 + react: 18.3.1 + react-is: 19.1.0 + optionalDependencies: + '@types/react': 18.3.23 + + '@next/env@15.5.18': {} + + '@next/swc-darwin-arm64@15.5.18': + optional: true + + '@next/swc-darwin-x64@15.5.18': + optional: true + + '@next/swc-linux-arm64-gnu@15.5.18': + optional: true + + '@next/swc-linux-arm64-musl@15.5.18': + optional: true + + '@next/swc-linux-x64-gnu@15.5.18': + optional: true + + '@next/swc-linux-x64-musl@15.5.18': + optional: true + + '@next/swc-win32-arm64-msvc@15.5.18': + optional: true + + '@next/swc-win32-x64-msvc@15.5.18': + optional: true + + '@noble/hashes@1.4.0': {} + + '@nodelib/fs.scandir@2.1.5': + dependencies: + '@nodelib/fs.stat': 2.0.5 + run-parallel: 1.2.0 + + '@nodelib/fs.stat@2.0.5': {} + + '@nodelib/fs.walk@1.2.8': + dependencies: + '@nodelib/fs.scandir': 2.1.5 + fastq: 1.19.1 + + '@nrwl/cli@15.9.7': + dependencies: + nx: 15.9.7 + transitivePeerDependencies: + - '@swc-node/register' + - '@swc/core' + - debug + + '@nrwl/devkit@15.9.7(nx@15.9.7)': + dependencies: + ejs: 3.1.10 + ignore: 5.3.2 + nx: 15.9.7 + semver: 7.5.4 + tmp: 0.2.5 + tslib: 2.8.1 + + '@nrwl/eslint-plugin-nx@15.9.7(@typescript-eslint/parser@5.36.1(eslint@8.23.1)(typescript@4.8.4))(eslint@8.23.1)(nx@15.9.7)(typescript@4.8.4)': + dependencies: + '@nrwl/devkit': 15.9.7(nx@15.9.7) + '@typescript-eslint/parser': 5.36.1(eslint@8.23.1)(typescript@4.8.4) + '@typescript-eslint/utils': 5.62.0(eslint@8.23.1)(typescript@4.8.4) + chalk: 4.1.2 + confusing-browser-globals: 1.0.11 + semver: 7.5.4 + transitivePeerDependencies: + - eslint + - nx + - supports-color + - typescript + + '@nrwl/js@15.9.7(@babel/traverse@7.29.0)(nx@15.9.7)(typescript@4.8.4)': + dependencies: + '@babel/core': 7.27.4 + '@babel/plugin-proposal-class-properties': 7.18.6(@babel/core@7.27.4) + '@babel/plugin-proposal-decorators': 7.29.0(@babel/core@7.27.4) + '@babel/plugin-transform-runtime': 7.29.0(@babel/core@7.27.4) + '@babel/preset-env': 7.20.2(@babel/core@7.27.4) + '@babel/preset-typescript': 7.18.6(@babel/core@7.27.4) + '@babel/runtime': 7.29.2 + '@nrwl/devkit': 15.9.7(nx@15.9.7) + '@nrwl/workspace': 15.9.7 + '@phenomnomnominal/tsquery': 4.1.1(typescript@4.8.4) + babel-plugin-const-enum: 1.2.0(@babel/core@7.27.4) + babel-plugin-macros: 2.8.0 + babel-plugin-transform-typescript-metadata: 0.3.2(@babel/core@7.27.4)(@babel/traverse@7.29.0) + chalk: 4.1.2 + fast-glob: 3.2.7 + fs-extra: 11.3.5 + ignore: 5.3.2 + js-tokens: 4.0.0 + minimatch: 3.1.5 + source-map-support: 0.5.19 + tree-kill: 1.2.2 + tslib: 2.8.1 + transitivePeerDependencies: + - '@babel/traverse' + - '@swc-node/register' + - '@swc/core' + - debug + - nx + - supports-color + - typescript + + '@nrwl/linter@15.9.7(@babel/traverse@7.29.0)(eslint@8.23.1)(nx@15.9.7)(typescript@4.8.4)': + dependencies: + '@nrwl/devkit': 15.9.7(nx@15.9.7) + '@nrwl/js': 15.9.7(@babel/traverse@7.29.0)(nx@15.9.7)(typescript@4.8.4) + '@phenomnomnominal/tsquery': 4.1.1(typescript@4.8.4) + tmp: 0.2.5 + tslib: 2.8.1 + optionalDependencies: + eslint: 8.23.1 + transitivePeerDependencies: + - '@babel/traverse' + - '@swc-node/register' + - '@swc/core' + - debug + - nx + - supports-color + - typescript + + '@nrwl/nx-darwin-arm64@15.9.7': + optional: true + + '@nrwl/nx-darwin-x64@15.9.7': + optional: true + + '@nrwl/nx-linux-arm-gnueabihf@15.9.7': + optional: true + + '@nrwl/nx-linux-arm64-gnu@15.9.7': + optional: true + + '@nrwl/nx-linux-arm64-musl@15.9.7': + optional: true + + '@nrwl/nx-linux-x64-gnu@15.9.7': + optional: true + + '@nrwl/nx-linux-x64-musl@15.9.7': + optional: true + + '@nrwl/nx-win32-arm64-msvc@15.9.7': + optional: true + + '@nrwl/nx-win32-x64-msvc@15.9.7': + optional: true + + '@nrwl/react@15.9.7(@babel/traverse@7.29.0)(eslint@8.23.1)(nx@15.9.7)(typescript@4.8.4)(webpack@5.107.1)': + dependencies: + '@nrwl/devkit': 15.9.7(nx@15.9.7) + '@nrwl/js': 15.9.7(@babel/traverse@7.29.0)(nx@15.9.7)(typescript@4.8.4) + '@nrwl/linter': 15.9.7(@babel/traverse@7.29.0)(eslint@8.23.1)(nx@15.9.7)(typescript@4.8.4) + '@nrwl/workspace': 15.9.7 + '@phenomnomnominal/tsquery': 4.1.1(typescript@4.8.4) + '@svgr/webpack': 6.5.1 + chalk: 4.1.2 + file-loader: 6.2.0(webpack@5.107.1) + minimatch: 3.1.5 + transitivePeerDependencies: + - '@babel/traverse' + - '@swc-node/register' + - '@swc/core' + - debug + - eslint + - nx + - supports-color + - typescript + - webpack + + '@nrwl/tao@15.9.7': + dependencies: + nx: 15.9.7 + transitivePeerDependencies: + - '@swc-node/register' + - '@swc/core' + - debug + + '@nrwl/webpack@15.9.7(@babel/traverse@7.29.0)(@types/node@24.0.13)(html-webpack-plugin@5.6.3(webpack@5.107.1))(nx@15.9.7)(typescript@4.8.4)(webpack-cli@6.0.1)': + dependencies: + '@babel/core': 7.27.4 + '@nrwl/devkit': 15.9.7(nx@15.9.7) + '@nrwl/js': 15.9.7(@babel/traverse@7.29.0)(nx@15.9.7)(typescript@4.8.4) + '@nrwl/workspace': 15.9.7 + autoprefixer: 10.5.0(postcss@8.5.15) + babel-loader: 9.2.1(@babel/core@7.27.4)(webpack@5.107.1) + chalk: 4.1.2 + copy-webpack-plugin: 10.2.4(webpack@5.107.1) + css-loader: 6.11.0(webpack@5.107.1) + css-minimizer-webpack-plugin: 3.4.1(webpack@5.107.1) + dotenv: 10.0.0 + file-loader: 6.2.0(webpack@5.107.1) + fork-ts-checker-webpack-plugin: 7.2.13(typescript@4.8.4)(webpack@5.107.1) + less: 4.1.3 + less-loader: 11.1.0(less@4.1.3)(webpack@5.107.1) + license-webpack-plugin: 4.0.2(webpack@5.107.1) + loader-utils: 2.0.4 + mini-css-extract-plugin: 2.4.7(webpack@5.107.1) + parse5: 4.0.0 + postcss: 8.5.15 + postcss-import: 14.1.0(postcss@8.5.15) + postcss-loader: 6.2.1(postcss@8.5.15)(webpack@5.107.1) + rxjs: 6.6.7 + sass: 1.99.0 + sass-loader: 12.6.0(sass@1.99.0)(webpack@5.107.1) + source-map-loader: 3.0.2(webpack@5.107.1) + style-loader: 3.3.4(webpack@5.107.1) + stylus: 0.55.0 + stylus-loader: 7.1.3(stylus@0.55.0)(webpack@5.107.1) + terser-webpack-plugin: 5.5.0(webpack@5.107.1) + ts-loader: 9.4.1(typescript@4.8.4)(webpack@5.107.1) + ts-node: 10.9.1(@types/node@24.0.13)(typescript@4.8.4) + tsconfig-paths: 4.2.0 + tsconfig-paths-webpack-plugin: 4.0.0 + tslib: 2.8.1 + webpack: 5.107.1(webpack-cli@6.0.1) + webpack-dev-server: 5.2.4(tslib@2.8.1)(webpack-cli@6.0.1)(webpack@5.107.1) + webpack-node-externals: 3.0.0 + webpack-subresource-integrity: 5.1.0(html-webpack-plugin@5.6.3(webpack@5.107.1))(webpack@5.107.1) + transitivePeerDependencies: + - '@babel/traverse' + - '@parcel/css' + - '@rspack/core' + - '@swc-node/register' + - '@swc/core' + - '@swc/wasm' + - '@types/node' + - bufferutil + - clean-css + - csso + - debug + - esbuild + - fibers + - html-webpack-plugin + - node-sass + - nx + - sass-embedded + - supports-color + - typescript + - uglify-js + - utf-8-validate + - vue-template-compiler + - webpack-cli + + '@nrwl/workspace@15.9.7': + dependencies: + '@nrwl/devkit': 15.9.7(nx@15.9.7) + '@parcel/watcher': 2.0.4 + chalk: 4.1.2 + chokidar: 3.6.0 + cli-cursor: 3.1.0 + cli-spinners: 2.6.1 + dotenv: 10.0.0 + figures: 3.2.0 + flat: 5.0.2 + glob: 7.1.4 + ignore: 5.3.2 + minimatch: 3.1.5 + npm-run-path: 4.0.1 + nx: 15.9.7 + open: 8.4.2 + rxjs: 6.6.7 + tmp: 0.2.5 + tslib: 2.8.1 + yargs: 17.7.2 + yargs-parser: 21.1.1 + transitivePeerDependencies: + - '@swc-node/register' + - '@swc/core' + - debug + + '@parcel/watcher-android-arm64@2.5.6': + optional: true + + '@parcel/watcher-darwin-arm64@2.5.6': + optional: true + + '@parcel/watcher-darwin-x64@2.5.6': + optional: true + + '@parcel/watcher-freebsd-x64@2.5.6': + optional: true + + '@parcel/watcher-linux-arm-glibc@2.5.6': + optional: true + + '@parcel/watcher-linux-arm-musl@2.5.6': + optional: true + + '@parcel/watcher-linux-arm64-glibc@2.5.6': + optional: true + + '@parcel/watcher-linux-arm64-musl@2.5.6': + optional: true + + '@parcel/watcher-linux-x64-glibc@2.5.6': + optional: true + + '@parcel/watcher-linux-x64-musl@2.5.6': + optional: true + + '@parcel/watcher-win32-arm64@2.5.6': + optional: true + + '@parcel/watcher-win32-ia32@2.5.6': + optional: true + + '@parcel/watcher-win32-x64@2.5.6': + optional: true + + '@parcel/watcher@2.0.4': + dependencies: + node-addon-api: 3.2.1 + node-gyp-build: 4.8.4 + + '@parcel/watcher@2.5.6': + dependencies: + detect-libc: 2.1.2 + is-glob: 4.0.3 + 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 + optional: true + + '@peculiar/asn1-cms@2.7.0': + dependencies: + '@peculiar/asn1-schema': 2.7.0 + '@peculiar/asn1-x509': 2.7.0 + '@peculiar/asn1-x509-attr': 2.7.0 + asn1js: 3.0.10 + tslib: 2.8.1 + + '@peculiar/asn1-csr@2.7.0': + dependencies: + '@peculiar/asn1-schema': 2.7.0 + '@peculiar/asn1-x509': 2.7.0 + asn1js: 3.0.10 + tslib: 2.8.1 + + '@peculiar/asn1-ecc@2.7.0': + dependencies: + '@peculiar/asn1-schema': 2.7.0 + '@peculiar/asn1-x509': 2.7.0 + asn1js: 3.0.10 + tslib: 2.8.1 + + '@peculiar/asn1-pfx@2.7.0': + dependencies: + '@peculiar/asn1-cms': 2.7.0 + '@peculiar/asn1-pkcs8': 2.7.0 + '@peculiar/asn1-rsa': 2.7.0 + '@peculiar/asn1-schema': 2.7.0 + asn1js: 3.0.10 + tslib: 2.8.1 + + '@peculiar/asn1-pkcs8@2.7.0': + dependencies: + '@peculiar/asn1-schema': 2.7.0 + '@peculiar/asn1-x509': 2.7.0 + asn1js: 3.0.10 + tslib: 2.8.1 + + '@peculiar/asn1-pkcs9@2.7.0': + dependencies: + '@peculiar/asn1-cms': 2.7.0 + '@peculiar/asn1-pfx': 2.7.0 + '@peculiar/asn1-pkcs8': 2.7.0 + '@peculiar/asn1-schema': 2.7.0 + '@peculiar/asn1-x509': 2.7.0 + '@peculiar/asn1-x509-attr': 2.7.0 + asn1js: 3.0.10 + tslib: 2.8.1 + + '@peculiar/asn1-rsa@2.7.0': + dependencies: + '@peculiar/asn1-schema': 2.7.0 + '@peculiar/asn1-x509': 2.7.0 + asn1js: 3.0.10 + tslib: 2.8.1 + + '@peculiar/asn1-schema@2.7.0': + dependencies: + '@peculiar/utils': 2.0.3 + asn1js: 3.0.10 + tslib: 2.8.1 + + '@peculiar/asn1-x509-attr@2.7.0': + dependencies: + '@peculiar/asn1-schema': 2.7.0 + '@peculiar/asn1-x509': 2.7.0 + asn1js: 3.0.10 + tslib: 2.8.1 + + '@peculiar/asn1-x509@2.7.0': + dependencies: + '@peculiar/asn1-schema': 2.7.0 + '@peculiar/utils': 2.0.3 + asn1js: 3.0.10 + tslib: 2.8.1 + + '@peculiar/utils@2.0.3': + dependencies: + tslib: 2.8.1 + + '@peculiar/x509@1.14.3': + dependencies: + '@peculiar/asn1-cms': 2.7.0 + '@peculiar/asn1-csr': 2.7.0 + '@peculiar/asn1-ecc': 2.7.0 + '@peculiar/asn1-pkcs9': 2.7.0 + '@peculiar/asn1-rsa': 2.7.0 + '@peculiar/asn1-schema': 2.7.0 + '@peculiar/asn1-x509': 2.7.0 + pvtsutils: 1.3.6 + reflect-metadata: 0.2.2 + tslib: 2.8.1 + tsyringe: 4.10.0 + + '@phenomnomnominal/tsquery@4.1.1(typescript@4.8.4)': + dependencies: + esquery: 1.7.0 + typescript: 4.8.4 + + '@phosphor-icons/react@2.1.10(react-dom@19.1.1(react@19.1.1))(react@19.1.1)': + dependencies: + react: 19.1.1 + react-dom: 19.1.1(react@19.1.1) + + '@playwright/test@1.59.1': + dependencies: + playwright: 1.59.1 + + '@popperjs/core@2.11.8': {} + + '@rc-component/async-validator@5.1.0': + dependencies: + '@babel/runtime': 7.29.2 + + '@rc-component/cascader@1.14.0(react-dom@19.1.1(react@19.1.1))(react@19.1.1)': + dependencies: + '@rc-component/select': 1.6.15(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@rc-component/tree': 1.2.4(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@rc-component/util': 1.10.1(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + clsx: 2.1.1 + react: 19.1.1 + react-dom: 19.1.1(react@19.1.1) + + '@rc-component/checkbox@2.0.0(react-dom@19.1.1(react@19.1.1))(react@19.1.1)': + dependencies: + '@rc-component/util': 1.10.1(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + clsx: 2.1.1 + react: 19.1.1 + react-dom: 19.1.1(react@19.1.1) + + '@rc-component/collapse@1.2.0(react-dom@19.1.1(react@19.1.1))(react@19.1.1)': + dependencies: + '@babel/runtime': 7.29.2 + '@rc-component/motion': 1.3.2(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@rc-component/util': 1.10.1(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + clsx: 2.1.1 + react: 19.1.1 + react-dom: 19.1.1(react@19.1.1) + + '@rc-component/color-picker@3.1.1(react-dom@19.1.1(react@19.1.1))(react@19.1.1)': + dependencies: + '@ant-design/fast-color': 3.0.1 + '@rc-component/util': 1.10.1(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + clsx: 2.1.1 + react: 19.1.1 + react-dom: 19.1.1(react@19.1.1) + + '@rc-component/context@2.0.1(react-dom@19.1.1(react@19.1.1))(react@19.1.1)': + dependencies: + '@rc-component/util': 1.10.1(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + react: 19.1.1 + react-dom: 19.1.1(react@19.1.1) + + '@rc-component/dialog@1.8.4(react-dom@19.1.1(react@19.1.1))(react@19.1.1)': + dependencies: + '@rc-component/motion': 1.3.2(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@rc-component/portal': 2.2.0(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@rc-component/util': 1.10.1(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + clsx: 2.1.1 + react: 19.1.1 + react-dom: 19.1.1(react@19.1.1) + + '@rc-component/drawer@1.4.2(react-dom@19.1.1(react@19.1.1))(react@19.1.1)': + dependencies: + '@rc-component/motion': 1.3.2(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@rc-component/portal': 2.2.0(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@rc-component/util': 1.10.1(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + clsx: 2.1.1 + react: 19.1.1 + react-dom: 19.1.1(react@19.1.1) + + '@rc-component/dropdown@1.0.2(react-dom@19.1.1(react@19.1.1))(react@19.1.1)': + dependencies: + '@rc-component/trigger': 3.9.0(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@rc-component/util': 1.10.1(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + clsx: 2.1.1 + react: 19.1.1 + react-dom: 19.1.1(react@19.1.1) + + '@rc-component/form@1.8.1(react-dom@19.1.1(react@19.1.1))(react@19.1.1)': + dependencies: + '@rc-component/async-validator': 5.1.0 + '@rc-component/util': 1.10.1(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + clsx: 2.1.1 + react: 19.1.1 + react-dom: 19.1.1(react@19.1.1) + + '@rc-component/image@1.9.0(react-dom@19.1.1(react@19.1.1))(react@19.1.1)': + dependencies: + '@rc-component/motion': 1.3.2(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@rc-component/portal': 2.2.0(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@rc-component/util': 1.10.1(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + clsx: 2.1.1 + react: 19.1.1 + react-dom: 19.1.1(react@19.1.1) + + '@rc-component/input-number@1.6.2(react-dom@19.1.1(react@19.1.1))(react@19.1.1)': + dependencies: + '@rc-component/mini-decimal': 1.1.3 + '@rc-component/util': 1.10.1(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + clsx: 2.1.1 + react: 19.1.1 + react-dom: 19.1.1(react@19.1.1) + + '@rc-component/input@1.1.2(react-dom@19.1.1(react@19.1.1))(react@19.1.1)': + dependencies: + '@rc-component/util': 1.10.1(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + clsx: 2.1.1 + react: 19.1.1 + react-dom: 19.1.1(react@19.1.1) + + '@rc-component/mentions@1.6.0(react-dom@19.1.1(react@19.1.1))(react@19.1.1)': + dependencies: + '@rc-component/input': 1.1.2(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@rc-component/menu': 1.2.0(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@rc-component/textarea': 1.1.2(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@rc-component/trigger': 3.9.0(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@rc-component/util': 1.10.1(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + clsx: 2.1.1 + react: 19.1.1 + react-dom: 19.1.1(react@19.1.1) + + '@rc-component/menu@1.2.0(react-dom@19.1.1(react@19.1.1))(react@19.1.1)': + dependencies: + '@rc-component/motion': 1.3.2(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@rc-component/overflow': 1.0.1(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@rc-component/trigger': 3.9.0(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@rc-component/util': 1.10.1(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + clsx: 2.1.1 + react: 19.1.1 + react-dom: 19.1.1(react@19.1.1) + + '@rc-component/mini-decimal@1.1.3': + dependencies: + '@babel/runtime': 7.29.2 + + '@rc-component/motion@1.3.2(react-dom@19.1.1(react@19.1.1))(react@19.1.1)': + dependencies: + '@rc-component/util': 1.10.1(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + clsx: 2.1.1 + react: 19.1.1 + react-dom: 19.1.1(react@19.1.1) + + '@rc-component/mutate-observer@2.0.1(react-dom@19.1.1(react@19.1.1))(react@19.1.1)': + dependencies: + '@rc-component/util': 1.10.1(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + react: 19.1.1 + react-dom: 19.1.1(react@19.1.1) + + '@rc-component/notification@1.2.0(react-dom@19.1.1(react@19.1.1))(react@19.1.1)': + dependencies: + '@rc-component/motion': 1.3.2(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@rc-component/util': 1.10.1(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + clsx: 2.1.1 + react: 19.1.1 + react-dom: 19.1.1(react@19.1.1) + + '@rc-component/overflow@1.0.1(react-dom@19.1.1(react@19.1.1))(react@19.1.1)': + dependencies: + '@babel/runtime': 7.29.2 + '@rc-component/resize-observer': 1.1.2(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@rc-component/util': 1.10.1(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + clsx: 2.1.1 + react: 19.1.1 + react-dom: 19.1.1(react@19.1.1) + + '@rc-component/pagination@1.2.0(react-dom@19.1.1(react@19.1.1))(react@19.1.1)': + dependencies: + '@rc-component/util': 1.10.1(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + clsx: 2.1.1 + react: 19.1.1 + react-dom: 19.1.1(react@19.1.1) + + '@rc-component/picker@1.9.1(dayjs@1.11.20)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)': + dependencies: + '@rc-component/overflow': 1.0.1(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@rc-component/resize-observer': 1.1.2(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@rc-component/trigger': 3.9.0(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@rc-component/util': 1.10.1(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + clsx: 2.1.1 + react: 19.1.1 + react-dom: 19.1.1(react@19.1.1) + optionalDependencies: + dayjs: 1.11.20 + + '@rc-component/portal@2.2.0(react-dom@19.1.1(react@19.1.1))(react@19.1.1)': + dependencies: + '@rc-component/util': 1.10.1(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + clsx: 2.1.1 + react: 19.1.1 + react-dom: 19.1.1(react@19.1.1) + + '@rc-component/progress@1.0.2(react-dom@19.1.1(react@19.1.1))(react@19.1.1)': + dependencies: + '@rc-component/util': 1.10.1(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + clsx: 2.1.1 + react: 19.1.1 + react-dom: 19.1.1(react@19.1.1) + + '@rc-component/qrcode@1.1.1(react-dom@19.1.1(react@19.1.1))(react@19.1.1)': + dependencies: + '@babel/runtime': 7.29.2 + react: 19.1.1 + react-dom: 19.1.1(react@19.1.1) + + '@rc-component/rate@1.0.1(react-dom@19.1.1(react@19.1.1))(react@19.1.1)': + dependencies: + '@rc-component/util': 1.10.1(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + clsx: 2.1.1 + react: 19.1.1 + react-dom: 19.1.1(react@19.1.1) + + '@rc-component/resize-observer@1.1.2(react-dom@19.1.1(react@19.1.1))(react@19.1.1)': + dependencies: + '@rc-component/util': 1.10.1(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + react: 19.1.1 + react-dom: 19.1.1(react@19.1.1) + + '@rc-component/segmented@1.3.0(react-dom@19.1.1(react@19.1.1))(react@19.1.1)': + dependencies: + '@babel/runtime': 7.29.2 + '@rc-component/motion': 1.3.2(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@rc-component/util': 1.10.1(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + clsx: 2.1.1 + react: 19.1.1 + react-dom: 19.1.1(react@19.1.1) + + '@rc-component/select@1.6.15(react-dom@19.1.1(react@19.1.1))(react@19.1.1)': + dependencies: + '@rc-component/overflow': 1.0.1(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@rc-component/trigger': 3.9.0(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@rc-component/util': 1.10.1(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@rc-component/virtual-list': 1.0.2(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + clsx: 2.1.1 + react: 19.1.1 + react-dom: 19.1.1(react@19.1.1) + + '@rc-component/slider@1.0.1(react-dom@19.1.1(react@19.1.1))(react@19.1.1)': + dependencies: + '@rc-component/util': 1.10.1(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + clsx: 2.1.1 + react: 19.1.1 + react-dom: 19.1.1(react@19.1.1) + + '@rc-component/steps@1.2.2(react-dom@19.1.1(react@19.1.1))(react@19.1.1)': + dependencies: + '@rc-component/util': 1.10.1(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + clsx: 2.1.1 + react: 19.1.1 + react-dom: 19.1.1(react@19.1.1) + + '@rc-component/switch@1.0.3(react-dom@19.1.1(react@19.1.1))(react@19.1.1)': + dependencies: + '@rc-component/util': 1.10.1(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + clsx: 2.1.1 + react: 19.1.1 + react-dom: 19.1.1(react@19.1.1) + + '@rc-component/table@1.9.1(react-dom@19.1.1(react@19.1.1))(react@19.1.1)': + dependencies: + '@rc-component/context': 2.0.1(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@rc-component/resize-observer': 1.1.2(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@rc-component/util': 1.10.1(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@rc-component/virtual-list': 1.0.2(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + clsx: 2.1.1 + react: 19.1.1 + react-dom: 19.1.1(react@19.1.1) + + '@rc-component/tabs@1.7.0(react-dom@19.1.1(react@19.1.1))(react@19.1.1)': + dependencies: + '@rc-component/dropdown': 1.0.2(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@rc-component/menu': 1.2.0(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@rc-component/motion': 1.3.2(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@rc-component/resize-observer': 1.1.2(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@rc-component/util': 1.10.1(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + clsx: 2.1.1 + react: 19.1.1 + react-dom: 19.1.1(react@19.1.1) + + '@rc-component/textarea@1.1.2(react-dom@19.1.1(react@19.1.1))(react@19.1.1)': + dependencies: + '@rc-component/input': 1.1.2(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@rc-component/resize-observer': 1.1.2(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@rc-component/util': 1.10.1(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + clsx: 2.1.1 + react: 19.1.1 + react-dom: 19.1.1(react@19.1.1) + + '@rc-component/tooltip@1.4.0(react-dom@19.1.1(react@19.1.1))(react@19.1.1)': + dependencies: + '@rc-component/trigger': 3.9.0(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@rc-component/util': 1.10.1(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + clsx: 2.1.1 + react: 19.1.1 + react-dom: 19.1.1(react@19.1.1) + + '@rc-component/tour@2.3.0(react-dom@19.1.1(react@19.1.1))(react@19.1.1)': + dependencies: + '@rc-component/portal': 2.2.0(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@rc-component/trigger': 3.9.0(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@rc-component/util': 1.10.1(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + clsx: 2.1.1 + react: 19.1.1 + react-dom: 19.1.1(react@19.1.1) + + '@rc-component/tree-select@1.8.0(react-dom@19.1.1(react@19.1.1))(react@19.1.1)': + dependencies: + '@rc-component/select': 1.6.15(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@rc-component/tree': 1.2.4(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@rc-component/util': 1.10.1(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + clsx: 2.1.1 + react: 19.1.1 + react-dom: 19.1.1(react@19.1.1) + + '@rc-component/tree@1.2.4(react-dom@19.1.1(react@19.1.1))(react@19.1.1)': + dependencies: + '@rc-component/motion': 1.3.2(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@rc-component/util': 1.10.1(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@rc-component/virtual-list': 1.0.2(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + clsx: 2.1.1 + react: 19.1.1 + react-dom: 19.1.1(react@19.1.1) + + '@rc-component/trigger@3.9.0(react-dom@19.1.1(react@19.1.1))(react@19.1.1)': + dependencies: + '@rc-component/motion': 1.3.2(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@rc-component/portal': 2.2.0(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@rc-component/resize-observer': 1.1.2(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@rc-component/util': 1.10.1(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + clsx: 2.1.1 + react: 19.1.1 + react-dom: 19.1.1(react@19.1.1) + + '@rc-component/upload@1.1.0(react-dom@19.1.1(react@19.1.1))(react@19.1.1)': + dependencies: + '@rc-component/util': 1.10.1(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + clsx: 2.1.1 + react: 19.1.1 + react-dom: 19.1.1(react@19.1.1) + + '@rc-component/util@1.10.1(react-dom@19.1.1(react@19.1.1))(react@19.1.1)': + dependencies: + is-mobile: 5.0.0 + react: 19.1.1 + react-dom: 19.1.1(react@19.1.1) + react-is: 18.3.1 + + '@rc-component/virtual-list@1.0.2(react-dom@19.1.1(react@19.1.1))(react@19.1.1)': + dependencies: + '@babel/runtime': 7.29.2 + '@rc-component/resize-observer': 1.1.2(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@rc-component/util': 1.10.1(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + clsx: 2.1.1 + react: 19.1.1 + react-dom: 19.1.1(react@19.1.1) + + '@remix-run/router@1.23.2': {} + + '@rolldown/pluginutils@1.0.0-beta.19': {} + + '@rollup/rollup-android-arm-eabi@4.60.4': + optional: true + + '@rollup/rollup-android-arm64@4.60.4': + optional: true + + '@rollup/rollup-darwin-arm64@4.60.4': + optional: true + + '@rollup/rollup-darwin-x64@4.60.4': + optional: true + + '@rollup/rollup-freebsd-arm64@4.60.4': + optional: true + + '@rollup/rollup-freebsd-x64@4.60.4': + optional: true + + '@rollup/rollup-linux-arm-gnueabihf@4.60.4': + optional: true + + '@rollup/rollup-linux-arm-musleabihf@4.60.4': + optional: true + + '@rollup/rollup-linux-arm64-gnu@4.60.4': + optional: true + + '@rollup/rollup-linux-arm64-musl@4.60.4': + optional: true + + '@rollup/rollup-linux-loong64-gnu@4.60.4': + optional: true + + '@rollup/rollup-linux-loong64-musl@4.60.4': + optional: true + + '@rollup/rollup-linux-ppc64-gnu@4.60.4': + optional: true + + '@rollup/rollup-linux-ppc64-musl@4.60.4': + optional: true + + '@rollup/rollup-linux-riscv64-gnu@4.60.4': + optional: true + + '@rollup/rollup-linux-riscv64-musl@4.60.4': + optional: true + + '@rollup/rollup-linux-s390x-gnu@4.60.4': + optional: true + + '@rollup/rollup-linux-x64-gnu@4.60.4': + optional: true + + '@rollup/rollup-linux-x64-musl@4.60.4': + optional: true + + '@rollup/rollup-openbsd-x64@4.60.4': + optional: true + + '@rollup/rollup-openharmony-arm64@4.60.4': + optional: true + + '@rollup/rollup-win32-arm64-msvc@4.60.4': + optional: true + + '@rollup/rollup-win32-ia32-msvc@4.60.4': + optional: true + + '@rollup/rollup-win32-x64-gnu@4.60.4': + optional: true + + '@rollup/rollup-win32-x64-msvc@4.60.4': + optional: true + + '@sinclair/typebox@0.34.37': {} + + '@svgr/babel-plugin-add-jsx-attribute@6.5.1(@babel/core@7.27.4)': + dependencies: + '@babel/core': 7.27.4 + + '@svgr/babel-plugin-remove-jsx-attribute@8.0.0(@babel/core@7.27.4)': + dependencies: + '@babel/core': 7.27.4 + + '@svgr/babel-plugin-remove-jsx-empty-expression@8.0.0(@babel/core@7.27.4)': + dependencies: + '@babel/core': 7.27.4 + + '@svgr/babel-plugin-replace-jsx-attribute-value@6.5.1(@babel/core@7.27.4)': + dependencies: + '@babel/core': 7.27.4 + + '@svgr/babel-plugin-svg-dynamic-title@6.5.1(@babel/core@7.27.4)': + dependencies: + '@babel/core': 7.27.4 + + '@svgr/babel-plugin-svg-em-dimensions@6.5.1(@babel/core@7.27.4)': + dependencies: + '@babel/core': 7.27.4 + + '@svgr/babel-plugin-transform-react-native-svg@6.5.1(@babel/core@7.27.4)': + dependencies: + '@babel/core': 7.27.4 + + '@svgr/babel-plugin-transform-svg-component@6.5.1(@babel/core@7.27.4)': + dependencies: + '@babel/core': 7.27.4 + + '@svgr/babel-preset@6.5.1(@babel/core@7.27.4)': + dependencies: + '@babel/core': 7.27.4 + '@svgr/babel-plugin-add-jsx-attribute': 6.5.1(@babel/core@7.27.4) + '@svgr/babel-plugin-remove-jsx-attribute': 8.0.0(@babel/core@7.27.4) + '@svgr/babel-plugin-remove-jsx-empty-expression': 8.0.0(@babel/core@7.27.4) + '@svgr/babel-plugin-replace-jsx-attribute-value': 6.5.1(@babel/core@7.27.4) + '@svgr/babel-plugin-svg-dynamic-title': 6.5.1(@babel/core@7.27.4) + '@svgr/babel-plugin-svg-em-dimensions': 6.5.1(@babel/core@7.27.4) + '@svgr/babel-plugin-transform-react-native-svg': 6.5.1(@babel/core@7.27.4) + '@svgr/babel-plugin-transform-svg-component': 6.5.1(@babel/core@7.27.4) + + '@svgr/core@6.5.1': + dependencies: + '@babel/core': 7.27.4 + '@svgr/babel-preset': 6.5.1(@babel/core@7.27.4) + '@svgr/plugin-jsx': 6.5.1(@svgr/core@6.5.1) + camelcase: 6.3.0 + cosmiconfig: 7.1.0 + transitivePeerDependencies: + - supports-color + + '@svgr/hast-util-to-babel-ast@6.5.1': + dependencies: + '@babel/types': 7.29.0 + entities: 4.5.0 + + '@svgr/plugin-jsx@6.5.1(@svgr/core@6.5.1)': + dependencies: + '@babel/core': 7.27.4 + '@svgr/babel-preset': 6.5.1(@babel/core@7.27.4) + '@svgr/core': 6.5.1 + '@svgr/hast-util-to-babel-ast': 6.5.1 + svg-parser: 2.0.4 + transitivePeerDependencies: + - supports-color + + '@svgr/plugin-svgo@6.5.1(@svgr/core@6.5.1)': + dependencies: + '@svgr/core': 6.5.1 + cosmiconfig: 7.1.0 + deepmerge: 4.3.1 + svgo: 2.8.2 + + '@svgr/webpack@6.5.1': + dependencies: + '@babel/core': 7.27.4 + '@babel/plugin-transform-react-constant-elements': 7.27.1(@babel/core@7.27.4) + '@babel/preset-env': 7.20.2(@babel/core@7.27.4) + '@babel/preset-react': 7.27.1(@babel/core@7.27.4) + '@babel/preset-typescript': 7.18.6(@babel/core@7.27.4) + '@svgr/core': 6.5.1 + '@svgr/plugin-jsx': 6.5.1(@svgr/core@6.5.1) + '@svgr/plugin-svgo': 6.5.1(@svgr/core@6.5.1) + transitivePeerDependencies: + - supports-color + + '@swc/helpers@0.5.15': + dependencies: + tslib: 2.8.1 + + '@tabler/icons-react@3.43.0(react@19.1.1)': + dependencies: + '@tabler/icons': 3.43.0 + react: 19.1.1 + + '@tabler/icons@3.43.0': {} + + '@testing-library/dom@8.20.1': + dependencies: + '@babel/code-frame': 7.29.0 + '@babel/runtime': 7.29.2 + '@types/aria-query': 5.0.4 + aria-query: 5.1.3 + chalk: 4.1.2 + dom-accessibility-api: 0.5.16 + lz-string: 1.5.0 + pretty-format: 27.5.1 + + '@testing-library/jest-dom@5.17.0': + dependencies: + '@adobe/css-tools': 4.4.3 + '@babel/runtime': 7.29.2 + '@types/testing-library__jest-dom': 5.14.9 + aria-query: 5.3.2 + chalk: 3.0.0 + css.escape: 1.5.1 + dom-accessibility-api: 0.5.16 + lodash: 4.18.1 + redent: 3.0.0 + + '@testing-library/react@13.4.0(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@babel/runtime': 7.29.2 + '@testing-library/dom': 8.20.1 + '@types/react-dom': 18.3.7(@types/react@18.3.23) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + transitivePeerDependencies: + - '@types/react' + + '@testing-library/user-event@13.5.0(@testing-library/dom@8.20.1)': + dependencies: + '@babel/runtime': 7.29.2 + '@testing-library/dom': 8.20.1 + + '@tsconfig/node10@1.0.12': {} + + '@tsconfig/node12@1.0.11': {} + + '@tsconfig/node14@1.0.3': {} + + '@tsconfig/node16@1.0.4': {} + + '@types/aria-query@5.0.4': {} + + '@types/babel__core@7.20.5': + dependencies: + '@babel/parser': 7.29.3 + '@babel/types': 7.29.0 + '@types/babel__generator': 7.27.0 + '@types/babel__template': 7.4.4 + '@types/babel__traverse': 7.20.7 + + '@types/babel__generator@7.27.0': + dependencies: + '@babel/types': 7.29.0 + + '@types/babel__template@7.4.4': + dependencies: + '@babel/parser': 7.29.3 + '@babel/types': 7.29.0 + + '@types/babel__traverse@7.20.7': + dependencies: + '@babel/types': 7.29.0 + + '@types/body-parser@1.19.6': + dependencies: + '@types/connect': 3.4.38 + '@types/node': 24.0.13 + + '@types/bonjour@3.5.13': + dependencies: + '@types/node': 24.0.13 + + '@types/connect-history-api-fallback@1.5.4': + dependencies: + '@types/express-serve-static-core': 4.19.8 + '@types/node': 24.0.13 + + '@types/connect@3.4.38': + dependencies: + '@types/node': 24.0.13 + + '@types/estree@1.0.8': {} + + '@types/express-serve-static-core@4.19.8': + dependencies: + '@types/node': 24.0.13 + '@types/qs': 6.15.0 + '@types/range-parser': 1.2.7 + '@types/send': 1.2.1 + + '@types/express@4.17.25': + dependencies: + '@types/body-parser': 1.19.6 + '@types/express-serve-static-core': 4.19.8 + '@types/qs': 6.15.0 + '@types/serve-static': 1.15.10 + + '@types/html-minifier-terser@6.1.0': {} + + '@types/http-errors@2.0.5': {} + + '@types/http-proxy@1.17.17': + dependencies: + '@types/node': 24.0.13 + + '@types/istanbul-lib-coverage@2.0.6': {} + + '@types/istanbul-lib-report@3.0.3': + dependencies: + '@types/istanbul-lib-coverage': 2.0.6 + + '@types/istanbul-reports@3.0.4': + dependencies: + '@types/istanbul-lib-report': 3.0.3 + + '@types/jest@30.0.0': + dependencies: + expect: 30.0.4 + pretty-format: 30.0.2 + + '@types/json-schema@7.0.15': {} + + '@types/lodash.mergewith@4.6.9': + dependencies: + '@types/lodash': 4.17.24 + + '@types/lodash@4.17.24': {} + + '@types/mime@1.3.5': {} + + '@types/node@12.20.55': {} + + '@types/node@24.0.13': + dependencies: + undici-types: 7.8.0 + + '@types/parse-json@4.0.2': {} + + '@types/prop-types@15.7.15': {} + + '@types/qs@6.15.0': {} + + '@types/range-parser@1.2.7': {} + + '@types/react-dom@18.3.7(@types/react@18.3.23)': + dependencies: + '@types/react': 18.3.23 + + '@types/react-reconciler@0.26.7': + dependencies: + '@types/react': 18.3.23 + + '@types/react-transition-group@4.4.12(@types/react@18.3.23)': + dependencies: + '@types/react': 18.3.23 + + '@types/react@18.3.23': + dependencies: + '@types/prop-types': 15.7.15 + csstype: 3.2.3 + + '@types/react@19.1.8': + dependencies: + csstype: 3.2.3 + optional: true + + '@types/retry@0.12.2': {} + + '@types/semver@7.7.1': {} + + '@types/send@0.17.6': + dependencies: + '@types/mime': 1.3.5 + '@types/node': 24.0.13 + + '@types/send@1.2.1': + dependencies: + '@types/node': 24.0.13 + + '@types/serve-index@1.9.4': + dependencies: + '@types/express': 4.17.25 + + '@types/serve-static@1.15.10': + dependencies: + '@types/http-errors': 2.0.5 + '@types/node': 24.0.13 + '@types/send': 0.17.6 + + '@types/sockjs@0.3.36': + dependencies: + '@types/node': 24.0.13 + + '@types/stack-utils@2.0.3': {} + + '@types/testing-library__jest-dom@5.14.9': + dependencies: + '@types/jest': 30.0.0 + + '@types/ws@8.18.1': + dependencies: + '@types/node': 24.0.13 + + '@types/yargs-parser@21.0.3': {} + + '@types/yargs@17.0.33': + dependencies: + '@types/yargs-parser': 21.0.3 + + '@typescript-eslint/eslint-plugin@5.36.1(@typescript-eslint/parser@5.36.1(eslint@8.23.1)(typescript@4.8.4))(eslint@8.23.1)(typescript@4.8.4)': + dependencies: + '@typescript-eslint/parser': 5.36.1(eslint@8.23.1)(typescript@4.8.4) + '@typescript-eslint/scope-manager': 5.36.1 + '@typescript-eslint/type-utils': 5.36.1(eslint@8.23.1)(typescript@4.8.4) + '@typescript-eslint/utils': 5.36.1(eslint@8.23.1)(typescript@4.8.4) + debug: 4.4.3 + eslint: 8.23.1 + functional-red-black-tree: 1.0.1 + ignore: 5.3.2 + regexpp: 3.2.0 + semver: 7.7.4 + tsutils: 3.21.0(typescript@4.8.4) + optionalDependencies: + typescript: 4.8.4 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/parser@5.36.1(eslint@8.23.1)(typescript@4.8.4)': + dependencies: + '@typescript-eslint/scope-manager': 5.36.1 + '@typescript-eslint/types': 5.36.1 + '@typescript-eslint/typescript-estree': 5.36.1(typescript@4.8.4) + debug: 4.4.3 + eslint: 8.23.1 + optionalDependencies: + typescript: 4.8.4 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/scope-manager@5.36.1': + dependencies: + '@typescript-eslint/types': 5.36.1 + '@typescript-eslint/visitor-keys': 5.36.1 + + '@typescript-eslint/scope-manager@5.62.0': + dependencies: + '@typescript-eslint/types': 5.62.0 + '@typescript-eslint/visitor-keys': 5.62.0 + + '@typescript-eslint/type-utils@5.36.1(eslint@8.23.1)(typescript@4.8.4)': + dependencies: + '@typescript-eslint/typescript-estree': 5.36.1(typescript@4.8.4) + '@typescript-eslint/utils': 5.36.1(eslint@8.23.1)(typescript@4.8.4) + debug: 4.4.3 + eslint: 8.23.1 + tsutils: 3.21.0(typescript@4.8.4) + optionalDependencies: + typescript: 4.8.4 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/types@5.36.1': {} + + '@typescript-eslint/types@5.62.0': {} + + '@typescript-eslint/typescript-estree@5.36.1(typescript@4.8.4)': + dependencies: + '@typescript-eslint/types': 5.36.1 + '@typescript-eslint/visitor-keys': 5.36.1 + debug: 4.4.3 + globby: 11.1.0 + is-glob: 4.0.3 + semver: 7.7.4 + tsutils: 3.21.0(typescript@4.8.4) + optionalDependencies: + typescript: 4.8.4 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/typescript-estree@5.62.0(typescript@4.8.4)': + dependencies: + '@typescript-eslint/types': 5.62.0 + '@typescript-eslint/visitor-keys': 5.62.0 + debug: 4.4.3 + globby: 11.1.0 + is-glob: 4.0.3 + semver: 7.7.4 + tsutils: 3.21.0(typescript@4.8.4) + optionalDependencies: + typescript: 4.8.4 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/utils@5.36.1(eslint@8.23.1)(typescript@4.8.4)': + dependencies: + '@types/json-schema': 7.0.15 + '@typescript-eslint/scope-manager': 5.36.1 + '@typescript-eslint/types': 5.36.1 + '@typescript-eslint/typescript-estree': 5.36.1(typescript@4.8.4) + eslint: 8.23.1 + eslint-scope: 5.1.1 + eslint-utils: 3.0.0(eslint@8.23.1) + transitivePeerDependencies: + - supports-color + - typescript + + '@typescript-eslint/utils@5.62.0(eslint@8.23.1)(typescript@4.8.4)': + dependencies: + '@eslint-community/eslint-utils': 4.9.1(eslint@8.23.1) + '@types/json-schema': 7.0.15 + '@types/semver': 7.7.1 + '@typescript-eslint/scope-manager': 5.62.0 + '@typescript-eslint/types': 5.62.0 + '@typescript-eslint/typescript-estree': 5.62.0(typescript@4.8.4) + eslint: 8.23.1 + eslint-scope: 5.1.1 + semver: 7.7.4 + transitivePeerDependencies: + - supports-color + - typescript + + '@typescript-eslint/visitor-keys@5.36.1': + dependencies: + '@typescript-eslint/types': 5.36.1 + eslint-visitor-keys: 3.4.3 + + '@typescript-eslint/visitor-keys@5.62.0': + dependencies: + '@typescript-eslint/types': 5.62.0 + eslint-visitor-keys: 3.4.3 + + '@vitejs/plugin-react@4.6.0(vite@6.4.2(@types/node@24.0.13)(less@4.1.3)(sass@1.99.0)(stylus@0.55.0)(terser@5.46.2)(yaml@1.10.3))': + dependencies: + '@babel/core': 7.27.4 + '@babel/plugin-transform-react-jsx-self': 7.27.1(@babel/core@7.27.4) + '@babel/plugin-transform-react-jsx-source': 7.27.1(@babel/core@7.27.4) + '@rolldown/pluginutils': 1.0.0-beta.19 + '@types/babel__core': 7.20.5 + react-refresh: 0.17.0 + vite: 6.4.2(@types/node@24.0.13)(less@4.1.3)(sass@1.99.0)(stylus@0.55.0)(terser@5.46.2)(yaml@1.10.3) + transitivePeerDependencies: + - supports-color + + '@webassemblyjs/ast@1.14.1': + dependencies: + '@webassemblyjs/helper-numbers': 1.13.2 + '@webassemblyjs/helper-wasm-bytecode': 1.13.2 + + '@webassemblyjs/floating-point-hex-parser@1.13.2': {} + + '@webassemblyjs/helper-api-error@1.13.2': {} + + '@webassemblyjs/helper-buffer@1.14.1': {} + + '@webassemblyjs/helper-numbers@1.13.2': + dependencies: + '@webassemblyjs/floating-point-hex-parser': 1.13.2 + '@webassemblyjs/helper-api-error': 1.13.2 + '@xtuc/long': 4.2.2 + + '@webassemblyjs/helper-wasm-bytecode@1.13.2': {} + + '@webassemblyjs/helper-wasm-section@1.14.1': + dependencies: + '@webassemblyjs/ast': 1.14.1 + '@webassemblyjs/helper-buffer': 1.14.1 + '@webassemblyjs/helper-wasm-bytecode': 1.13.2 + '@webassemblyjs/wasm-gen': 1.14.1 + + '@webassemblyjs/ieee754@1.13.2': + dependencies: + '@xtuc/ieee754': 1.2.0 + + '@webassemblyjs/leb128@1.13.2': + dependencies: + '@xtuc/long': 4.2.2 + + '@webassemblyjs/utf8@1.13.2': {} + + '@webassemblyjs/wasm-edit@1.14.1': + dependencies: + '@webassemblyjs/ast': 1.14.1 + '@webassemblyjs/helper-buffer': 1.14.1 + '@webassemblyjs/helper-wasm-bytecode': 1.13.2 + '@webassemblyjs/helper-wasm-section': 1.14.1 + '@webassemblyjs/wasm-gen': 1.14.1 + '@webassemblyjs/wasm-opt': 1.14.1 + '@webassemblyjs/wasm-parser': 1.14.1 + '@webassemblyjs/wast-printer': 1.14.1 + + '@webassemblyjs/wasm-gen@1.14.1': + dependencies: + '@webassemblyjs/ast': 1.14.1 + '@webassemblyjs/helper-wasm-bytecode': 1.13.2 + '@webassemblyjs/ieee754': 1.13.2 + '@webassemblyjs/leb128': 1.13.2 + '@webassemblyjs/utf8': 1.13.2 + + '@webassemblyjs/wasm-opt@1.14.1': + dependencies: + '@webassemblyjs/ast': 1.14.1 + '@webassemblyjs/helper-buffer': 1.14.1 + '@webassemblyjs/wasm-gen': 1.14.1 + '@webassemblyjs/wasm-parser': 1.14.1 + + '@webassemblyjs/wasm-parser@1.14.1': + dependencies: + '@webassemblyjs/ast': 1.14.1 + '@webassemblyjs/helper-api-error': 1.13.2 + '@webassemblyjs/helper-wasm-bytecode': 1.13.2 + '@webassemblyjs/ieee754': 1.13.2 + '@webassemblyjs/leb128': 1.13.2 + '@webassemblyjs/utf8': 1.13.2 + + '@webassemblyjs/wast-printer@1.14.1': + dependencies: + '@webassemblyjs/ast': 1.14.1 + '@xtuc/long': 4.2.2 + + '@webpack-cli/configtest@3.0.1(webpack-cli@6.0.1)(webpack@5.107.1)': + dependencies: + webpack: 5.107.1(webpack-cli@6.0.1) + webpack-cli: 6.0.1(webpack-dev-server@5.2.4)(webpack@5.107.1) + + '@webpack-cli/info@3.0.1(webpack-cli@6.0.1)(webpack@5.107.1)': + dependencies: + webpack: 5.107.1(webpack-cli@6.0.1) + webpack-cli: 6.0.1(webpack-dev-server@5.2.4)(webpack@5.107.1) + + '@webpack-cli/serve@3.0.1(webpack-cli@6.0.1)(webpack-dev-server@5.2.4)(webpack@5.107.1)': + dependencies: + webpack: 5.107.1(webpack-cli@6.0.1) + webpack-cli: 6.0.1(webpack-dev-server@5.2.4)(webpack@5.107.1) + optionalDependencies: + webpack-dev-server: 5.2.4(tslib@2.8.1)(webpack-cli@6.0.1)(webpack@5.107.1) + + '@xtuc/ieee754@1.2.0': {} + + '@xtuc/long@4.2.2': {} + + '@yarnpkg/lockfile@1.1.0': {} + + '@yarnpkg/parsers@3.0.0-rc.46': + dependencies: + js-yaml: 4.1.1 + tslib: 2.8.1 + + '@zag-js/dom-query@0.31.1': {} + + '@zag-js/element-size@0.31.1': {} + + '@zag-js/focus-visible@0.31.1': + dependencies: + '@zag-js/dom-query': 0.31.1 + + '@zkochan/js-yaml@0.0.6': + dependencies: + argparse: 2.0.1 + + abab@2.0.6: {} + + accepts@1.3.8: + dependencies: + mime-types: 2.1.35 + negotiator: 0.6.3 + + accepts@2.0.0: + dependencies: + mime-types: 3.0.2 + negotiator: 1.0.0 + + acorn-import-phases@1.0.4(acorn@8.16.0): + dependencies: + acorn: 8.16.0 + + acorn-jsx@5.3.2(acorn@8.16.0): + dependencies: + acorn: 8.16.0 + + acorn-walk@8.3.5: + dependencies: + acorn: 8.16.0 + + acorn@8.16.0: {} + + ajv-formats@2.1.1(ajv@8.20.0): + optionalDependencies: + ajv: 8.20.0 + + ajv-formats@3.0.1(ajv@8.20.0): + optionalDependencies: + ajv: 8.20.0 + + ajv-keywords@3.5.2(ajv@6.15.0): + dependencies: + ajv: 6.15.0 + + ajv-keywords@5.1.0(ajv@8.20.0): + dependencies: + ajv: 8.20.0 + fast-deep-equal: 3.1.3 + + ajv@6.15.0: + 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: + dependencies: + fast-deep-equal: 3.1.3 + fast-uri: 3.1.2 + json-schema-traverse: 1.0.0 + require-from-string: 2.0.2 + + ansi-colors@4.1.3: {} + + ansi-html-community@0.0.8: {} + + ansi-regex@5.0.1: {} + + ansi-styles@4.3.0: + dependencies: + color-convert: 2.0.1 + + ansi-styles@5.2.0: {} + + antd@6.3.7(react-dom@19.1.1(react@19.1.1))(react@19.1.1): + dependencies: + '@ant-design/colors': 8.0.1 + '@ant-design/cssinjs': 2.1.2(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@ant-design/cssinjs-utils': 2.1.2(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@ant-design/fast-color': 3.0.1 + '@ant-design/icons': 6.2.2(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@ant-design/react-slick': 2.0.0(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@babel/runtime': 7.29.2 + '@rc-component/cascader': 1.14.0(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@rc-component/checkbox': 2.0.0(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@rc-component/collapse': 1.2.0(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@rc-component/color-picker': 3.1.1(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@rc-component/dialog': 1.8.4(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@rc-component/drawer': 1.4.2(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@rc-component/dropdown': 1.0.2(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@rc-component/form': 1.8.1(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@rc-component/image': 1.9.0(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@rc-component/input': 1.1.2(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@rc-component/input-number': 1.6.2(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@rc-component/mentions': 1.6.0(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@rc-component/menu': 1.2.0(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@rc-component/motion': 1.3.2(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@rc-component/mutate-observer': 2.0.1(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@rc-component/notification': 1.2.0(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@rc-component/pagination': 1.2.0(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@rc-component/picker': 1.9.1(dayjs@1.11.20)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@rc-component/progress': 1.0.2(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@rc-component/qrcode': 1.1.1(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@rc-component/rate': 1.0.1(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@rc-component/resize-observer': 1.1.2(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@rc-component/segmented': 1.3.0(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@rc-component/select': 1.6.15(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@rc-component/slider': 1.0.1(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@rc-component/steps': 1.2.2(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@rc-component/switch': 1.0.3(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@rc-component/table': 1.9.1(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@rc-component/tabs': 1.7.0(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@rc-component/textarea': 1.1.2(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@rc-component/tooltip': 1.4.0(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@rc-component/tour': 2.3.0(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@rc-component/tree': 1.2.4(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@rc-component/tree-select': 1.8.0(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@rc-component/trigger': 3.9.0(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@rc-component/upload': 1.1.0(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@rc-component/util': 1.10.1(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + clsx: 2.1.1 + dayjs: 1.11.20 + react: 19.1.1 + react-dom: 19.1.1(react@19.1.1) + scroll-into-view-if-needed: 3.1.0 + throttle-debounce: 5.0.2 + transitivePeerDependencies: + - date-fns + - luxon + - moment + + anymatch@3.1.3: + dependencies: + normalize-path: 3.0.0 + picomatch: 2.3.2 + + arg@4.1.3: {} + + argparse@1.0.10: + dependencies: + sprintf-js: 1.0.3 + + argparse@2.0.1: {} + + aria-hidden@1.2.6: + dependencies: + tslib: 2.8.1 + + aria-query@5.1.3: + dependencies: + deep-equal: 2.2.3 + + aria-query@5.3.2: {} + + array-buffer-byte-length@1.0.2: + dependencies: + call-bound: 1.0.4 + is-array-buffer: 3.0.5 + + array-flatten@1.1.1: {} + + array-union@2.1.0: {} + + array-union@3.0.1: {} + + asn1js@3.0.10: + dependencies: + pvtsutils: 1.3.6 + pvutils: 1.1.5 + tslib: 2.8.1 + + async@3.2.6: {} + + asynckit@0.4.0: {} + + atob@2.1.2: {} + + autoprefixer@10.5.0(postcss@8.5.15): + dependencies: + browserslist: 4.28.2 + caniuse-lite: 1.0.30001793 + fraction.js: 5.3.4 + picocolors: 1.1.1 + postcss: 8.5.15 + postcss-value-parser: 4.2.0 + + available-typed-arrays@1.0.7: + dependencies: + possible-typed-array-names: 1.1.0 + + axios@1.16.0: + dependencies: + follow-redirects: 1.16.0 + form-data: 4.0.5 + proxy-from-env: 2.1.0 + transitivePeerDependencies: + - debug + + babel-loader@10.0.0(@babel/core@7.20.2)(webpack@5.107.1): + dependencies: + '@babel/core': 7.20.2 + find-up: 5.0.0 + webpack: 5.107.1(webpack-cli@6.0.1) + + babel-loader@10.0.0(@babel/core@7.27.4)(webpack@5.107.1): + dependencies: + '@babel/core': 7.27.4 + find-up: 5.0.0 + webpack: 5.107.1(webpack-cli@6.0.1) + + babel-loader@9.2.1(@babel/core@7.27.4)(webpack@5.107.1): + dependencies: + '@babel/core': 7.27.4 + find-cache-dir: 4.0.0 + schema-utils: 4.3.3 + webpack: 5.107.1(webpack-cli@6.0.1) + + babel-plugin-const-enum@1.2.0(@babel/core@7.27.4): + dependencies: + '@babel/core': 7.27.4 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/plugin-syntax-typescript': 7.28.6(@babel/core@7.27.4) + '@babel/traverse': 7.29.0 + transitivePeerDependencies: + - supports-color + + babel-plugin-macros@2.8.0: + dependencies: + '@babel/runtime': 7.29.2 + cosmiconfig: 6.0.0 + resolve: 1.22.12 + + babel-plugin-macros@3.1.0: + dependencies: + '@babel/runtime': 7.29.2 + cosmiconfig: 7.1.0 + resolve: 1.22.12 + + babel-plugin-polyfill-corejs2@0.3.3(@babel/core@7.20.2): + dependencies: + '@babel/compat-data': 7.29.3 + '@babel/core': 7.20.2 + '@babel/helper-define-polyfill-provider': 0.3.3(@babel/core@7.20.2) + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + + babel-plugin-polyfill-corejs2@0.3.3(@babel/core@7.27.4): + dependencies: + '@babel/compat-data': 7.29.3 + '@babel/core': 7.27.4 + '@babel/helper-define-polyfill-provider': 0.3.3(@babel/core@7.27.4) + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + + babel-plugin-polyfill-corejs2@0.4.17(@babel/core@7.27.4): + dependencies: + '@babel/compat-data': 7.29.3 + '@babel/core': 7.27.4 + '@babel/helper-define-polyfill-provider': 0.6.8(@babel/core@7.27.4) + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + + babel-plugin-polyfill-corejs3@0.13.0(@babel/core@7.27.4): + dependencies: + '@babel/core': 7.27.4 + '@babel/helper-define-polyfill-provider': 0.6.8(@babel/core@7.27.4) + core-js-compat: 3.49.0 + transitivePeerDependencies: + - supports-color + + babel-plugin-polyfill-corejs3@0.6.0(@babel/core@7.20.2): + dependencies: + '@babel/core': 7.20.2 + '@babel/helper-define-polyfill-provider': 0.3.3(@babel/core@7.20.2) + core-js-compat: 3.49.0 + transitivePeerDependencies: + - supports-color + + babel-plugin-polyfill-corejs3@0.6.0(@babel/core@7.27.4): + dependencies: + '@babel/core': 7.27.4 + '@babel/helper-define-polyfill-provider': 0.3.3(@babel/core@7.27.4) + core-js-compat: 3.49.0 + transitivePeerDependencies: + - supports-color + + babel-plugin-polyfill-regenerator@0.4.1(@babel/core@7.20.2): + dependencies: + '@babel/core': 7.20.2 + '@babel/helper-define-polyfill-provider': 0.3.3(@babel/core@7.20.2) + transitivePeerDependencies: + - supports-color + + babel-plugin-polyfill-regenerator@0.4.1(@babel/core@7.27.4): + dependencies: + '@babel/core': 7.27.4 + '@babel/helper-define-polyfill-provider': 0.3.3(@babel/core@7.27.4) + transitivePeerDependencies: + - supports-color + + babel-plugin-polyfill-regenerator@0.6.8(@babel/core@7.27.4): + dependencies: + '@babel/core': 7.27.4 + '@babel/helper-define-polyfill-provider': 0.6.8(@babel/core@7.27.4) + transitivePeerDependencies: + - supports-color + + babel-plugin-transform-typescript-metadata@0.3.2(@babel/core@7.27.4)(@babel/traverse@7.29.0): + dependencies: + '@babel/core': 7.27.4 + '@babel/helper-plugin-utils': 7.28.6 + optionalDependencies: + '@babel/traverse': 7.29.0 + + balanced-match@1.0.2: {} + + base64-js@1.5.1: {} + + baseline-browser-mapping@2.10.27: {} + + batch@0.6.1: {} + + better-path-resolve@1.0.0: + dependencies: + is-windows: 1.0.2 + + big.js@5.2.2: {} + + binary-extensions@2.3.0: {} + + bippy@0.5.39(react@19.1.1): + dependencies: + react: 19.1.1 + + bl@4.1.0: + dependencies: + buffer: 5.7.1 + inherits: 2.0.4 + readable-stream: 3.6.2 + + body-parser@1.20.5: + dependencies: + bytes: 3.1.2 + content-type: 1.0.5 + debug: 2.6.9 + depd: 2.0.0 + destroy: 1.2.0 + http-errors: 2.0.1 + iconv-lite: 0.4.24 + on-finished: 2.4.1 + qs: 6.15.2 + raw-body: 2.5.3 + type-is: 1.6.18 + unpipe: 1.0.0 + transitivePeerDependencies: + - supports-color + + body-parser@2.2.2: + dependencies: + bytes: 3.1.2 + content-type: 1.0.5 + debug: 4.4.3 + http-errors: 2.0.1 + iconv-lite: 0.7.2 + on-finished: 2.4.1 + qs: 6.15.2 + raw-body: 3.0.2 + type-is: 2.1.0 + transitivePeerDependencies: + - supports-color + + bonjour-service@1.3.0: + dependencies: + fast-deep-equal: 3.1.3 + multicast-dns: 7.2.5 + + boolbase@1.0.0: {} + + brace-expansion@1.1.14: + dependencies: + balanced-match: 1.0.2 + concat-map: 0.0.1 + + braces@3.0.3: + dependencies: + fill-range: 7.1.1 + + browserslist@4.25.1: + dependencies: + caniuse-lite: 1.0.30001793 + electron-to-chromium: 1.5.182 + node-releases: 2.0.19 + update-browserslist-db: 1.1.3(browserslist@4.25.1) + + browserslist@4.28.2: + dependencies: + baseline-browser-mapping: 2.10.27 + caniuse-lite: 1.0.30001793 + electron-to-chromium: 1.5.352 + node-releases: 2.0.38 + update-browserslist-db: 1.2.3(browserslist@4.28.2) + + buffer-from@1.1.2: {} + + buffer@5.7.1: + dependencies: + base64-js: 1.5.1 + ieee754: 1.2.1 + + bundle-name@4.1.0: + dependencies: + run-applescript: 7.1.0 + + bytes@3.1.2: {} + + bytestreamjs@2.0.1: {} + + call-bind-apply-helpers@1.0.2: + dependencies: + es-errors: 1.3.0 + function-bind: 1.1.2 + + call-bind@1.0.8: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-define-property: 1.0.1 + get-intrinsic: 1.3.0 + set-function-length: 1.2.2 + + call-bound@1.0.4: + dependencies: + call-bind-apply-helpers: 1.0.2 + get-intrinsic: 1.3.0 + + callsites@3.1.0: {} + + camel-case@4.1.2: + dependencies: + pascal-case: 3.1.2 + tslib: 2.8.1 + + camelcase@6.3.0: {} + + caniuse-api@3.0.0: + dependencies: + browserslist: 4.28.2 + caniuse-lite: 1.0.30001793 + lodash.memoize: 4.1.2 + lodash.uniq: 4.5.0 + + caniuse-lite@1.0.30001793: {} + + chalk@3.0.0: + dependencies: + ansi-styles: 4.3.0 + supports-color: 7.2.0 + + chalk@4.1.2: + dependencies: + ansi-styles: 4.3.0 + supports-color: 7.2.0 + + chardet@2.1.1: {} + + chokidar@3.6.0: + dependencies: + anymatch: 3.1.3 + braces: 3.0.3 + glob-parent: 5.1.2 + is-binary-path: 2.1.0 + is-glob: 4.0.3 + normalize-path: 3.0.0 + readdirp: 3.6.0 + optionalDependencies: + fsevents: 2.3.3 + + chokidar@4.0.3: + dependencies: + readdirp: 4.1.2 + + chrome-trace-event@1.0.4: {} + + ci-info@4.3.0: {} + + clean-css@5.3.3: + dependencies: + source-map: 0.6.1 + + cli-cursor@3.1.0: + dependencies: + restore-cursor: 3.1.0 + + cli-spinners@2.6.1: {} + + client-only@0.0.1: {} + + cliui@7.0.4: + dependencies: + string-width: 4.2.3 + strip-ansi: 6.0.1 + wrap-ansi: 7.0.0 + + cliui@8.0.1: + dependencies: + string-width: 4.2.3 + strip-ansi: 6.0.1 + wrap-ansi: 7.0.0 + + clone-deep@4.0.1: + dependencies: + is-plain-object: 2.0.4 + kind-of: 6.0.3 + shallow-clone: 3.0.1 + + clsx@2.1.1: {} + + color-convert@2.0.1: + dependencies: + color-name: 1.1.4 + + color-name@1.1.4: {} + + color2k@2.0.3: {} + + colord@2.9.3: {} + + colorette@2.0.20: {} + + combined-stream@1.0.8: + dependencies: + delayed-stream: 1.0.0 + + commander@12.1.0: {} + + commander@2.20.3: {} + + commander@7.2.0: {} + + commander@8.3.0: {} + + common-path-prefix@3.0.0: {} + + compressible@2.0.18: + dependencies: + mime-db: 1.54.0 + + compression@1.8.1: + dependencies: + bytes: 3.1.2 + compressible: 2.0.18 + debug: 2.6.9 + negotiator: 0.6.4 + on-headers: 1.1.0 + safe-buffer: 5.2.1 + vary: 1.1.2 + transitivePeerDependencies: + - supports-color + + compute-scroll-into-view@3.1.1: {} + + concat-map@0.0.1: {} + + confusing-browser-globals@1.0.11: {} + + connect-history-api-fallback@2.0.0: {} + + content-disposition@0.5.4: + dependencies: + safe-buffer: 5.2.1 + + content-disposition@1.1.0: {} + + content-type@1.0.5: {} + + content-type@2.0.0: {} + + convert-source-map@1.9.0: {} + + convert-source-map@2.0.0: {} + + cookie-signature@1.0.7: {} + + cookie-signature@1.2.2: {} + + cookie@0.7.2: {} + + copy-anything@2.0.6: + dependencies: + is-what: 3.14.1 + + copy-to-clipboard@3.3.3: + dependencies: + toggle-selection: 1.0.6 + + copy-webpack-plugin@10.2.4(webpack@5.107.1): + dependencies: + fast-glob: 3.3.3 + glob-parent: 6.0.2 + globby: 12.2.0 + normalize-path: 3.0.0 + schema-utils: 4.3.3 + serialize-javascript: 7.0.5 + webpack: 5.107.1(webpack-cli@6.0.1) + + core-js-compat@3.49.0: + dependencies: + browserslist: 4.28.2 + + core-util-is@1.0.3: {} + + cors@2.8.6: + dependencies: + object-assign: 4.1.1 + vary: 1.1.2 + + cosmiconfig@6.0.0: + dependencies: + '@types/parse-json': 4.0.2 + import-fresh: 3.3.1 + parse-json: 5.2.0 + path-type: 4.0.0 + yaml: 1.10.3 + + cosmiconfig@7.1.0: + dependencies: + '@types/parse-json': 4.0.2 + import-fresh: 3.3.1 + parse-json: 5.2.0 + path-type: 4.0.0 + yaml: 1.10.3 + + create-require@1.1.1: {} + + cross-spawn@6.0.6: + dependencies: + nice-try: 1.0.5 + path-key: 2.0.1 + semver: 5.7.2 + shebang-command: 1.2.0 + which: 1.3.1 + + cross-spawn@7.0.6: + dependencies: + path-key: 3.1.1 + shebang-command: 2.0.0 + which: 2.0.2 + + css-declaration-sorter@6.4.1(postcss@8.5.15): + dependencies: + postcss: 8.5.15 + + css-loader@6.11.0(webpack@5.107.1): + dependencies: + icss-utils: 5.1.0(postcss@8.5.15) + 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-value-parser: 4.2.0 + semver: 7.7.4 + optionalDependencies: + webpack: 5.107.1(webpack-cli@6.0.1) + + css-loader@7.1.4(webpack@5.107.1): + dependencies: + icss-utils: 5.1.0(postcss@8.5.15) + 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-value-parser: 4.2.0 + semver: 7.7.4 + optionalDependencies: + webpack: 5.107.1(webpack-cli@6.0.1) + + css-minimizer-webpack-plugin@3.4.1(webpack@5.107.1): + dependencies: + cssnano: 5.1.15(postcss@8.5.15) + jest-worker: 27.5.1 + postcss: 8.5.15 + schema-utils: 4.3.3 + serialize-javascript: 7.0.5 + source-map: 0.6.1 + webpack: 5.107.1(webpack-cli@6.0.1) + + css-select@4.3.0: + dependencies: + boolbase: 1.0.0 + css-what: 6.2.2 + domhandler: 4.3.1 + domutils: 2.8.0 + nth-check: 2.1.1 + + css-tree@1.1.3: + dependencies: + mdn-data: 2.0.14 + source-map: 0.6.1 + + css-what@6.2.2: {} + + css.escape@1.5.1: {} + + css@3.0.0: + dependencies: + inherits: 2.0.4 + source-map: 0.6.1 + source-map-resolve: 0.6.0 + + cssesc@3.0.0: {} + + 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) + lilconfig: 2.1.0 + postcss: 8.5.15 + yaml: 1.10.3 + + csso@4.2.0: + dependencies: + css-tree: 1.1.3 + + csstype@3.2.3: {} + + dayjs@1.11.20: {} + + debug@2.6.9: + dependencies: + ms: 2.0.0 + + debug@3.1.0: + dependencies: + ms: 2.0.0 + + debug@4.4.1: + dependencies: + ms: 2.1.3 + + debug@4.4.3: + dependencies: + ms: 2.1.3 + + decode-uri-component@0.2.2: {} + + deep-equal@2.2.3: + dependencies: + array-buffer-byte-length: 1.0.2 + call-bind: 1.0.8 + es-get-iterator: 1.1.3 + get-intrinsic: 1.3.0 + is-arguments: 1.2.0 + is-array-buffer: 3.0.5 + is-date-object: 1.1.0 + is-regex: 1.2.1 + is-shared-array-buffer: 1.0.4 + isarray: 2.0.5 + object-is: 1.1.6 + object-keys: 1.1.1 + object.assign: 4.1.7 + regexp.prototype.flags: 1.5.4 + side-channel: 1.1.0 + which-boxed-primitive: 1.1.1 + which-collection: 1.0.2 + which-typed-array: 1.1.19 + + deep-is@0.1.4: {} + + deepmerge@4.3.1: {} + + default-browser-id@5.0.1: {} + + default-browser@5.5.0: + dependencies: + bundle-name: 4.1.0 + default-browser-id: 5.0.1 + + define-data-property@1.1.4: + dependencies: + es-define-property: 1.0.1 + es-errors: 1.3.0 + gopd: 1.2.0 + + define-lazy-prop@2.0.0: {} + + define-lazy-prop@3.0.0: {} + + define-properties@1.2.1: + dependencies: + define-data-property: 1.1.4 + has-property-descriptors: 1.0.2 + object-keys: 1.1.1 + + delayed-stream@1.0.0: {} + + depd@1.1.2: {} + + depd@2.0.0: {} + + destroy@1.2.0: {} + + detect-indent@6.1.0: {} + + detect-libc@2.1.2: + optional: true + + detect-node-es@1.1.0: {} + + detect-node@2.1.0: {} + + diff@4.0.4: {} + + dir-glob@3.0.1: + dependencies: + path-type: 4.0.0 + + dns-packet@5.6.1: + dependencies: + '@leichtgewicht/ip-codec': 2.0.5 + + doctrine@3.0.0: + dependencies: + esutils: 2.0.3 + + dom-accessibility-api@0.5.16: {} + + dom-converter@0.2.0: + dependencies: + utila: 0.4.0 + + dom-helpers@5.2.1: + dependencies: + '@babel/runtime': 7.29.2 + csstype: 3.2.3 + + dom-serializer@1.4.1: + dependencies: + domelementtype: 2.3.0 + domhandler: 4.3.1 + entities: 2.2.0 + + domelementtype@2.3.0: {} + + domhandler@4.3.1: + dependencies: + domelementtype: 2.3.0 + + domutils@2.8.0: + dependencies: + dom-serializer: 1.4.1 + domelementtype: 2.3.0 + domhandler: 4.3.1 + + dot-case@3.0.4: + dependencies: + no-case: 3.0.4 + tslib: 2.8.1 + + dotenv@10.0.0: {} + + dunder-proto@1.0.1: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-errors: 1.3.0 + gopd: 1.2.0 + + duplexer@0.1.2: {} + + ee-first@1.1.1: {} + + ejs@3.1.10: + dependencies: + jake: 10.9.4 + + electron-to-chromium@1.5.182: {} + + electron-to-chromium@1.5.352: {} + + emoji-regex@8.0.0: {} + + emojis-list@3.0.0: {} + + encodeurl@2.0.0: {} + + end-of-stream@1.4.5: + dependencies: + once: 1.4.0 + + enhanced-resolve@5.22.0: + dependencies: + graceful-fs: 4.2.11 + tapable: 2.3.3 + + enquirer@2.3.6: + dependencies: + ansi-colors: 4.1.3 + + enquirer@2.4.1: + dependencies: + ansi-colors: 4.1.3 + strip-ansi: 6.0.1 + + entities@2.2.0: {} + + entities@4.5.0: {} + + envinfo@7.21.0: {} + + errno@0.1.8: + dependencies: + prr: 1.0.1 + optional: true + + error-ex@1.3.2: + dependencies: + is-arrayish: 0.2.1 + + es-define-property@1.0.1: {} + + es-errors@1.3.0: {} + + es-get-iterator@1.1.3: + dependencies: + call-bind: 1.0.8 + get-intrinsic: 1.3.0 + has-symbols: 1.1.0 + is-arguments: 1.2.0 + is-map: 2.0.3 + is-set: 2.0.3 + is-string: 1.1.1 + isarray: 2.0.5 + stop-iteration-iterator: 1.1.0 + + es-module-lexer@2.1.0: {} + + es-object-atoms@1.1.1: + dependencies: + es-errors: 1.3.0 + + es-set-tostringtag@2.1.0: + dependencies: + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + has-tostringtag: 1.0.2 + hasown: 2.0.2 + + esbuild@0.25.6: + optionalDependencies: + '@esbuild/aix-ppc64': 0.25.6 + '@esbuild/android-arm': 0.25.6 + '@esbuild/android-arm64': 0.25.6 + '@esbuild/android-x64': 0.25.6 + '@esbuild/darwin-arm64': 0.25.6 + '@esbuild/darwin-x64': 0.25.6 + '@esbuild/freebsd-arm64': 0.25.6 + '@esbuild/freebsd-x64': 0.25.6 + '@esbuild/linux-arm': 0.25.6 + '@esbuild/linux-arm64': 0.25.6 + '@esbuild/linux-ia32': 0.25.6 + '@esbuild/linux-loong64': 0.25.6 + '@esbuild/linux-mips64el': 0.25.6 + '@esbuild/linux-ppc64': 0.25.6 + '@esbuild/linux-riscv64': 0.25.6 + '@esbuild/linux-s390x': 0.25.6 + '@esbuild/linux-x64': 0.25.6 + '@esbuild/netbsd-arm64': 0.25.6 + '@esbuild/netbsd-x64': 0.25.6 + '@esbuild/openbsd-arm64': 0.25.6 + '@esbuild/openbsd-x64': 0.25.6 + '@esbuild/openharmony-arm64': 0.25.6 + '@esbuild/sunos-x64': 0.25.6 + '@esbuild/win32-arm64': 0.25.6 + '@esbuild/win32-ia32': 0.25.6 + '@esbuild/win32-x64': 0.25.6 + + escalade@3.2.0: {} + + escape-html@1.0.3: {} + + escape-string-regexp@1.0.5: {} + + escape-string-regexp@2.0.0: {} + + escape-string-regexp@4.0.0: {} + + eslint-scope@5.1.1: + dependencies: + esrecurse: 4.3.0 + estraverse: 4.3.0 + + eslint-scope@7.2.2: + dependencies: + esrecurse: 4.3.0 + estraverse: 5.3.0 + + eslint-utils@3.0.0(eslint@8.23.1): + dependencies: + eslint: 8.23.1 + eslint-visitor-keys: 2.1.0 + + eslint-visitor-keys@2.1.0: {} + + eslint-visitor-keys@3.4.3: {} + + eslint@8.23.1: + dependencies: + '@eslint/eslintrc': 1.4.1 + '@humanwhocodes/config-array': 0.10.7 + '@humanwhocodes/gitignore-to-minimatch': 1.0.2 + '@humanwhocodes/module-importer': 1.0.1 + ajv: 6.15.0 + chalk: 4.1.2 + cross-spawn: 7.0.6 + debug: 4.4.3 + doctrine: 3.0.0 + escape-string-regexp: 4.0.0 + eslint-scope: 7.2.2 + eslint-utils: 3.0.0(eslint@8.23.1) + eslint-visitor-keys: 3.4.3 + espree: 9.6.1 + esquery: 1.7.0 + esutils: 2.0.3 + fast-deep-equal: 3.1.3 + file-entry-cache: 6.0.1 + find-up: 5.0.0 + glob-parent: 6.0.2 + globals: 13.24.0 + globby: 11.1.0 + grapheme-splitter: 1.0.4 + ignore: 5.3.2 + import-fresh: 3.3.1 + imurmurhash: 0.1.4 + is-glob: 4.0.3 + js-sdsl: 4.4.2 + js-yaml: 4.1.1 + json-stable-stringify-without-jsonify: 1.0.1 + levn: 0.4.1 + lodash.merge: 4.6.2 + minimatch: 3.1.5 + natural-compare: 1.4.0 + optionator: 0.9.4 + regexpp: 3.2.0 + strip-ansi: 6.0.1 + strip-json-comments: 3.1.1 + text-table: 0.2.0 + transitivePeerDependencies: + - supports-color + + espree@9.6.1: + dependencies: + acorn: 8.16.0 + acorn-jsx: 5.3.2(acorn@8.16.0) + eslint-visitor-keys: 3.4.3 + + esprima@4.0.1: {} + + esquery@1.7.0: + dependencies: + estraverse: 5.3.0 + + esrecurse@4.3.0: + dependencies: + estraverse: 5.3.0 + + estraverse@4.3.0: {} + + estraverse@5.3.0: {} + + esutils@2.0.3: {} + + etag@1.8.1: {} + + eventemitter3@4.0.7: {} + + events@3.3.0: {} + + eventsource-parser@3.1.0: {} + + eventsource@3.0.7: + dependencies: + eventsource-parser: 3.1.0 + + execa@1.0.0: + dependencies: + cross-spawn: 6.0.6 + get-stream: 4.1.0 + is-stream: 1.1.0 + npm-run-path: 2.0.2 + p-finally: 1.0.0 + signal-exit: 3.0.7 + strip-eof: 1.0.0 + + expect@30.0.4: + dependencies: + '@jest/expect-utils': 30.0.4 + '@jest/get-type': 30.0.1 + jest-matcher-utils: 30.0.4 + jest-message-util: 30.0.2 + jest-mock: 30.0.2 + jest-util: 30.0.2 + + express-rate-limit@8.5.2(express@5.2.1): + dependencies: + express: 5.2.1 + ip-address: 10.2.0 + + express@4.22.1: + dependencies: + accepts: 1.3.8 + array-flatten: 1.1.1 + body-parser: 1.20.5 + content-disposition: 0.5.4 + content-type: 1.0.5 + cookie: 0.7.2 + cookie-signature: 1.0.7 + debug: 2.6.9 + depd: 2.0.0 + encodeurl: 2.0.0 + escape-html: 1.0.3 + etag: 1.8.1 + finalhandler: 1.3.2 + fresh: 0.5.2 + http-errors: 2.0.1 + merge-descriptors: 1.0.3 + methods: 1.1.2 + on-finished: 2.4.1 + parseurl: 1.3.3 + path-to-regexp: 0.1.13 + proxy-addr: 2.0.7 + qs: 6.15.2 + range-parser: 1.2.1 + safe-buffer: 5.2.1 + send: 0.19.2 + serve-static: 1.16.3 + setprototypeof: 1.2.0 + statuses: 2.0.2 + type-is: 1.6.18 + utils-merge: 1.0.1 + vary: 1.1.2 + transitivePeerDependencies: + - supports-color + + express@5.2.1: + dependencies: + accepts: 2.0.0 + body-parser: 2.2.2 + content-disposition: 1.1.0 + content-type: 1.0.5 + cookie: 0.7.2 + cookie-signature: 1.2.2 + debug: 4.4.3 + depd: 2.0.0 + encodeurl: 2.0.0 + escape-html: 1.0.3 + etag: 1.8.1 + finalhandler: 2.1.1 + fresh: 2.0.0 + http-errors: 2.0.1 + 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 + range-parser: 1.2.1 + router: 2.2.0 + send: 1.2.1 + serve-static: 2.2.1 + statuses: 2.0.2 + type-is: 2.1.0 + vary: 1.1.2 + transitivePeerDependencies: + - supports-color + + extendable-error@0.1.7: {} + + fast-deep-equal@3.1.3: {} + + fast-glob@3.2.7: + dependencies: + '@nodelib/fs.stat': 2.0.5 + '@nodelib/fs.walk': 1.2.8 + glob-parent: 5.1.2 + merge2: 1.4.1 + micromatch: 4.0.8 + + fast-glob@3.3.3: + dependencies: + '@nodelib/fs.stat': 2.0.5 + '@nodelib/fs.walk': 1.2.8 + glob-parent: 5.1.2 + merge2: 1.4.1 + micromatch: 4.0.8 + + fast-json-stable-stringify@2.1.0: {} + + fast-levenshtein@2.0.6: {} + + fast-uri@3.1.2: {} + + fastest-levenshtein@1.0.16: {} + + fastq@1.19.1: + dependencies: + reusify: 1.1.0 + + faye-websocket@0.11.4: + dependencies: + websocket-driver: 0.7.4 + + fdir@6.4.6(picomatch@4.0.4): + optionalDependencies: + picomatch: 4.0.4 + + figures@3.2.0: + dependencies: + escape-string-regexp: 1.0.5 + + file-entry-cache@6.0.1: + dependencies: + flat-cache: 3.2.0 + + file-loader@6.2.0(webpack@5.107.1): + dependencies: + loader-utils: 2.0.4 + schema-utils: 3.3.0 + webpack: 5.107.1(webpack-cli@6.0.1) + + filelist@1.0.6: + dependencies: + minimatch: 3.1.5 + + fill-range@7.1.1: + dependencies: + to-regex-range: 5.0.1 + + finalhandler@1.3.2: + dependencies: + debug: 2.6.9 + encodeurl: 2.0.0 + escape-html: 1.0.3 + on-finished: 2.4.1 + parseurl: 1.3.3 + statuses: 2.0.2 + unpipe: 1.0.0 + transitivePeerDependencies: + - supports-color + + finalhandler@2.1.1: + dependencies: + debug: 4.4.3 + encodeurl: 2.0.0 + escape-html: 1.0.3 + on-finished: 2.4.1 + parseurl: 1.3.3 + statuses: 2.0.2 + transitivePeerDependencies: + - supports-color + + find-cache-dir@4.0.0: + dependencies: + common-path-prefix: 3.0.0 + pkg-dir: 7.0.0 + + find-root@1.1.0: {} + + find-up@4.1.0: + dependencies: + locate-path: 5.0.0 + path-exists: 4.0.0 + + find-up@5.0.0: + dependencies: + locate-path: 6.0.0 + path-exists: 4.0.0 + + find-up@6.3.0: + dependencies: + locate-path: 7.2.0 + path-exists: 5.0.0 + + flat-cache@3.2.0: + dependencies: + flatted: 3.4.2 + keyv: 4.5.4 + rimraf: 3.0.2 + + flat@5.0.2: {} + + flatted@3.4.2: {} + + focus-lock@1.3.6: + dependencies: + tslib: 2.8.1 + + follow-redirects@1.16.0: {} + + for-each@0.3.5: + dependencies: + is-callable: 1.2.7 + + fork-ts-checker-webpack-plugin@7.2.13(typescript@4.8.4)(webpack@5.107.1): + dependencies: + '@babel/code-frame': 7.29.0 + chalk: 4.1.2 + chokidar: 3.6.0 + cosmiconfig: 7.1.0 + deepmerge: 4.3.1 + fs-extra: 10.1.0 + memfs: 3.5.3 + minimatch: 3.1.5 + node-abort-controller: 3.1.1 + schema-utils: 3.3.0 + semver: 7.7.4 + tapable: 2.3.3 + typescript: 4.8.4 + webpack: 5.107.1(webpack-cli@6.0.1) + + form-data@4.0.5: + dependencies: + asynckit: 0.4.0 + combined-stream: 1.0.8 + es-set-tostringtag: 2.1.0 + hasown: 2.0.2 + mime-types: 2.1.35 + + forwarded@0.2.0: {} + + fraction.js@5.3.4: {} + + framer-motion@12.38.0(@emotion/is-prop-valid@1.3.1)(react-dom@18.2.0(react@18.2.0))(react@18.2.0): + dependencies: + motion-dom: 12.38.0 + motion-utils: 12.36.0 + tslib: 2.8.1 + optionalDependencies: + '@emotion/is-prop-valid': 1.3.1 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + + framesync@6.1.2: + dependencies: + tslib: 2.4.0 + + fresh@0.5.2: {} + + fresh@2.0.0: {} + + fs-constants@1.0.0: {} + + fs-extra@10.1.0: + dependencies: + graceful-fs: 4.2.11 + jsonfile: 6.2.1 + universalify: 2.0.1 + + fs-extra@11.3.5: + dependencies: + graceful-fs: 4.2.11 + jsonfile: 6.2.1 + universalify: 2.0.1 + + fs-extra@7.0.1: + dependencies: + graceful-fs: 4.2.11 + jsonfile: 4.0.0 + universalify: 0.1.2 + + fs-extra@8.1.0: + dependencies: + graceful-fs: 4.2.11 + jsonfile: 4.0.0 + universalify: 0.1.2 + + fs-monkey@1.1.0: {} + + fs.realpath@1.0.0: {} + + fsevents@2.3.2: + optional: true + + fsevents@2.3.3: + optional: true + + function-bind@1.1.2: {} + + functional-red-black-tree@1.0.1: {} + + functions-have-names@1.2.3: {} + + gensync@1.0.0-beta.2: {} + + get-caller-file@2.0.5: {} + + 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.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 + + get-nonce@1.0.1: {} + + get-proto@1.0.1: + dependencies: + dunder-proto: 1.0.1 + es-object-atoms: 1.1.1 + + get-stream@4.1.0: + dependencies: + pump: 3.0.3 + + glob-parent@5.1.2: + dependencies: + is-glob: 4.0.3 + + glob-parent@6.0.2: + dependencies: + is-glob: 4.0.3 + + glob-to-regex.js@1.2.0(tslib@2.8.1): + dependencies: + tslib: 2.8.1 + + glob-to-regexp@0.4.1: {} + + glob@7.1.4: + dependencies: + fs.realpath: 1.0.0 + inflight: 1.0.6 + inherits: 2.0.4 + minimatch: 3.1.5 + once: 1.4.0 + path-is-absolute: 1.0.1 + + glob@7.2.3: + dependencies: + fs.realpath: 1.0.0 + inflight: 1.0.6 + inherits: 2.0.4 + minimatch: 3.1.5 + once: 1.4.0 + path-is-absolute: 1.0.1 + + globals@13.24.0: + dependencies: + type-fest: 0.20.2 + + 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@12.2.0: + dependencies: + array-union: 3.0.1 + dir-glob: 3.0.1 + fast-glob: 3.3.3 + ignore: 5.3.2 + merge2: 1.4.1 + slash: 4.0.0 + + gopd@1.2.0: {} + + graceful-fs@4.2.11: {} + + grapheme-splitter@1.0.4: {} + + handle-thing@2.0.1: {} + + has-bigints@1.1.0: {} + + has-flag@4.0.0: {} + + has-property-descriptors@1.0.2: + dependencies: + es-define-property: 1.0.1 + + has-symbols@1.1.0: {} + + has-tostringtag@1.0.2: + dependencies: + has-symbols: 1.1.0 + + hasown@2.0.2: + dependencies: + function-bind: 1.1.2 + + he@1.2.0: {} + + hoist-non-react-statics@3.3.2: + dependencies: + react-is: 16.13.1 + + hono@4.12.23: {} + + hpack.js@2.1.6: + dependencies: + inherits: 2.0.4 + obuf: 1.1.2 + readable-stream: 2.3.8 + wbuf: 1.7.3 + + html-minifier-terser@6.1.0: + dependencies: + camel-case: 4.1.2 + clean-css: 5.3.3 + commander: 8.3.0 + he: 1.2.0 + param-case: 3.0.4 + relateurl: 0.2.7 + terser: 5.46.2 + + html-webpack-plugin@5.6.3(webpack@5.107.1): + dependencies: + '@types/html-minifier-terser': 6.1.0 + html-minifier-terser: 6.1.0 + lodash: 4.18.1 + pretty-error: 4.0.0 + tapable: 2.3.3 + optionalDependencies: + webpack: 5.107.1(webpack-cli@6.0.1) + + htmlparser2@6.1.0: + dependencies: + domelementtype: 2.3.0 + domhandler: 4.3.1 + domutils: 2.8.0 + entities: 2.2.0 + + http-deceiver@1.2.7: {} + + http-errors@1.8.1: + dependencies: + depd: 1.1.2 + inherits: 2.0.4 + setprototypeof: 1.2.0 + statuses: 1.5.0 + toidentifier: 1.0.1 + + http-errors@2.0.1: + dependencies: + depd: 2.0.0 + inherits: 2.0.4 + setprototypeof: 1.2.0 + statuses: 2.0.2 + toidentifier: 1.0.1 + + http-parser-js@0.5.10: {} + + http-proxy-middleware@2.0.9(@types/express@4.17.25): + dependencies: + '@types/http-proxy': 1.17.17 + http-proxy: 1.18.1 + is-glob: 4.0.3 + is-plain-obj: 3.0.0 + micromatch: 4.0.8 + optionalDependencies: + '@types/express': 4.17.25 + transitivePeerDependencies: + - debug + + http-proxy@1.18.1: + dependencies: + eventemitter3: 4.0.7 + follow-redirects: 1.16.0 + requires-port: 1.0.0 + transitivePeerDependencies: + - debug + + human-id@4.2.0: {} + + hyperdyperid@1.2.0: {} + + iconv-lite@0.4.24: + dependencies: + safer-buffer: 2.1.2 + + iconv-lite@0.6.3: + dependencies: + safer-buffer: 2.1.2 + + iconv-lite@0.7.2: + dependencies: + safer-buffer: 2.1.2 + + icss-utils@5.1.0(postcss@8.5.15): + dependencies: + postcss: 8.5.15 + + ieee754@1.2.1: {} + + ignore@5.3.2: {} + + image-size@0.5.5: + optional: true + + immutable@5.1.5: {} + + import-fresh@3.3.1: + dependencies: + parent-module: 1.0.1 + resolve-from: 4.0.0 + + import-local@3.2.0: + dependencies: + pkg-dir: 4.2.0 + resolve-cwd: 3.0.0 + + imurmurhash@0.1.4: {} + + indent-string@4.0.0: {} + + inflight@1.0.6: + dependencies: + once: 1.4.0 + wrappy: 1.0.2 + + inherits@2.0.4: {} + + internal-slot@1.1.0: + dependencies: + es-errors: 1.3.0 + hasown: 2.0.2 + side-channel: 1.1.0 + + interpret@1.4.0: {} + + interpret@3.1.1: {} + + ip-address@10.2.0: {} + + ipaddr.js@1.9.1: {} + + ipaddr.js@2.3.0: {} + + is-arguments@1.2.0: + dependencies: + call-bound: 1.0.4 + has-tostringtag: 1.0.2 + + is-array-buffer@3.0.5: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + get-intrinsic: 1.3.0 + + is-arrayish@0.2.1: {} + + is-bigint@1.1.0: + dependencies: + has-bigints: 1.1.0 + + is-binary-path@2.1.0: + dependencies: + binary-extensions: 2.3.0 + + is-boolean-object@1.2.2: + dependencies: + call-bound: 1.0.4 + has-tostringtag: 1.0.2 + + is-callable@1.2.7: {} + + is-core-module@2.16.1: + dependencies: + hasown: 2.0.2 + + is-date-object@1.1.0: + dependencies: + call-bound: 1.0.4 + has-tostringtag: 1.0.2 + + is-docker@2.2.1: {} + + is-docker@3.0.0: {} + + is-extglob@2.1.1: {} + + is-fullwidth-code-point@3.0.0: {} + + is-glob@4.0.3: + dependencies: + is-extglob: 2.1.1 + + is-inside-container@1.0.0: + dependencies: + is-docker: 3.0.0 + + is-map@2.0.3: {} + + is-mobile@5.0.0: {} + + is-network-error@1.3.1: {} + + is-number-object@1.1.1: + dependencies: + call-bound: 1.0.4 + has-tostringtag: 1.0.2 + + is-number@7.0.0: {} + + is-plain-obj@3.0.0: {} + + is-plain-object@2.0.4: + dependencies: + isobject: 3.0.1 + + is-promise@4.0.0: {} + + is-regex@1.2.1: + dependencies: + call-bound: 1.0.4 + gopd: 1.2.0 + has-tostringtag: 1.0.2 + hasown: 2.0.2 + + is-set@2.0.3: {} + + is-shared-array-buffer@1.0.4: + dependencies: + call-bound: 1.0.4 + + is-stream@1.1.0: {} + + is-string@1.1.1: + dependencies: + call-bound: 1.0.4 + has-tostringtag: 1.0.2 + + is-subdir@1.2.0: + dependencies: + better-path-resolve: 1.0.0 + + is-symbol@1.1.1: + dependencies: + call-bound: 1.0.4 + has-symbols: 1.1.0 + safe-regex-test: 1.1.0 + + is-weakmap@2.0.2: {} + + is-weakset@2.0.4: + dependencies: + call-bound: 1.0.4 + get-intrinsic: 1.3.0 + + is-what@3.14.1: {} + + is-windows@1.0.2: {} + + is-wsl@2.2.0: + dependencies: + is-docker: 2.2.1 + + is-wsl@3.1.1: + dependencies: + is-inside-container: 1.0.0 + + isarray@1.0.0: {} + + isarray@2.0.5: {} + + isexe@2.0.0: {} + + isobject@3.0.1: {} + + jake@10.9.4: + dependencies: + async: 3.2.6 + filelist: 1.0.6 + picocolors: 1.1.1 + + jest-diff@30.0.4: + dependencies: + '@jest/diff-sequences': 30.0.1 + '@jest/get-type': 30.0.1 + chalk: 4.1.2 + pretty-format: 30.0.2 + + jest-matcher-utils@30.0.4: + dependencies: + '@jest/get-type': 30.0.1 + chalk: 4.1.2 + jest-diff: 30.0.4 + pretty-format: 30.0.2 + + jest-message-util@30.0.2: + dependencies: + '@babel/code-frame': 7.29.0 + '@jest/types': 30.0.1 + '@types/stack-utils': 2.0.3 + chalk: 4.1.2 + graceful-fs: 4.2.11 + micromatch: 4.0.8 + pretty-format: 30.0.2 + slash: 3.0.0 + stack-utils: 2.0.6 + + jest-mock@30.0.2: + dependencies: + '@jest/types': 30.0.1 + '@types/node': 24.0.13 + jest-util: 30.0.2 + + jest-regex-util@30.0.1: {} + + jest-util@30.0.2: + dependencies: + '@jest/types': 30.0.1 + '@types/node': 24.0.13 + chalk: 4.1.2 + ci-info: 4.3.0 + graceful-fs: 4.2.11 + picomatch: 4.0.4 + + jest-worker@27.5.1: + dependencies: + '@types/node': 24.0.13 + merge-stream: 2.0.0 + supports-color: 8.1.1 + + jose@6.2.3: {} + + js-sdsl@4.4.2: {} + + js-tokens@4.0.0: {} + + js-yaml@3.14.1: + dependencies: + argparse: 1.0.10 + esprima: 4.0.1 + + js-yaml@4.1.1: + dependencies: + argparse: 2.0.1 + + jsesc@3.1.0: {} + + json-buffer@3.0.1: {} + + json-parse-even-better-errors@2.3.1: {} + + json-schema-traverse@0.4.1: {} + + json-schema-traverse@1.0.0: {} + + json-schema-typed@8.0.2: {} + + json-stable-stringify-without-jsonify@1.0.1: {} + + json2mq@0.2.0: + dependencies: + string-convert: 0.2.1 + + json5@2.2.3: {} + + jsonc-parser@3.2.0: {} + + jsonfile@4.0.0: + optionalDependencies: + graceful-fs: 4.2.11 + + jsonfile@6.2.1: + dependencies: + universalify: 2.0.1 + optionalDependencies: + graceful-fs: 4.2.11 + + keyv@4.5.4: + dependencies: + json-buffer: 3.0.1 + + kind-of@6.0.3: {} + + klona@2.0.6: {} + + launch-editor@2.13.2: + dependencies: + picocolors: 1.1.1 + shell-quote: 1.8.3 + + less-loader@11.1.0(less@4.1.3)(webpack@5.107.1): + dependencies: + klona: 2.0.6 + less: 4.1.3 + webpack: 5.107.1(webpack-cli@6.0.1) + + less@4.1.3: + dependencies: + copy-anything: 2.0.6 + parse-node-version: 1.0.1 + tslib: 2.8.1 + optionalDependencies: + errno: 0.1.8 + graceful-fs: 4.2.11 + image-size: 0.5.5 + make-dir: 2.1.0 + mime: 1.6.0 + needle: 3.5.0 + source-map: 0.6.1 + + levn@0.4.1: + dependencies: + prelude-ls: 1.2.1 + type-check: 0.4.0 + + license-webpack-plugin@4.0.2(webpack@5.107.1): + dependencies: + webpack-sources: 3.4.1 + optionalDependencies: + webpack: 5.107.1(webpack-cli@6.0.1) + + lilconfig@2.1.0: {} + + lines-and-columns@1.2.4: {} + + lines-and-columns@2.0.4: {} + + loader-runner@4.3.2: {} + + loader-utils@2.0.4: + dependencies: + big.js: 5.2.2 + emojis-list: 3.0.0 + json5: 2.2.3 + + locate-path@5.0.0: + dependencies: + p-locate: 4.1.0 + + locate-path@6.0.0: + dependencies: + p-locate: 5.0.0 + + locate-path@7.2.0: + dependencies: + p-locate: 6.0.0 + + lodash.debounce@4.0.8: {} + + lodash.memoize@4.1.2: {} + + lodash.merge@4.6.2: {} + + lodash.mergewith@4.6.2: {} + + lodash.startcase@4.4.0: {} + + lodash.uniq@4.5.0: {} + + lodash@4.18.1: {} + + loose-envify@1.4.0: + dependencies: + js-tokens: 4.0.0 + + lower-case@2.0.2: + dependencies: + tslib: 2.8.1 + + lru-cache@5.1.1: + dependencies: + yallist: 3.1.1 + + lru-cache@6.0.0: + dependencies: + yallist: 4.0.0 + + lz-string@1.5.0: {} + + make-dir@2.1.0: + dependencies: + pify: 4.0.1 + semver: 5.7.2 + optional: true + + make-error@1.3.6: {} + + math-intrinsics@1.1.0: {} + + mdn-data@2.0.14: {} + + media-typer@0.3.0: {} + + media-typer@1.1.0: {} + + memfs@3.5.3: + dependencies: + fs-monkey: 1.1.0 + + memfs@4.57.2(tslib@2.8.1): + dependencies: + '@jsonjoy.com/fs-core': 4.57.2(tslib@2.8.1) + '@jsonjoy.com/fs-fsa': 4.57.2(tslib@2.8.1) + '@jsonjoy.com/fs-node': 4.57.2(tslib@2.8.1) + '@jsonjoy.com/fs-node-builtins': 4.57.2(tslib@2.8.1) + '@jsonjoy.com/fs-node-to-fsa': 4.57.2(tslib@2.8.1) + '@jsonjoy.com/fs-node-utils': 4.57.2(tslib@2.8.1) + '@jsonjoy.com/fs-print': 4.57.2(tslib@2.8.1) + '@jsonjoy.com/fs-snapshot': 4.57.2(tslib@2.8.1) + '@jsonjoy.com/json-pack': 1.21.0(tslib@2.8.1) + '@jsonjoy.com/util': 1.9.0(tslib@2.8.1) + glob-to-regex.js: 1.2.0(tslib@2.8.1) + thingies: 2.6.0(tslib@2.8.1) + tree-dump: 1.1.0(tslib@2.8.1) + tslib: 2.8.1 + + merge-descriptors@1.0.3: {} + + merge-descriptors@2.0.0: {} + + merge-stream@2.0.0: {} + + merge2@1.4.1: {} + + methods@1.1.2: {} + + micromatch@4.0.8: + dependencies: + braces: 3.0.3 + picomatch: 2.3.2 + + mime-db@1.52.0: {} + + mime-db@1.54.0: {} + + mime-types@2.1.35: + dependencies: + mime-db: 1.52.0 + + mime-types@3.0.1: + dependencies: + mime-db: 1.54.0 + + mime-types@3.0.2: + dependencies: + mime-db: 1.54.0 + + mime@1.6.0: {} + + mimic-fn@2.1.0: {} + + min-indent@1.0.1: {} + + mini-css-extract-plugin@2.4.7(webpack@5.107.1): + dependencies: + schema-utils: 4.3.3 + webpack: 5.107.1(webpack-cli@6.0.1) + + minimalistic-assert@1.0.1: {} + + minimatch@3.1.5: + dependencies: + brace-expansion: 1.1.14 + + minimist@1.2.8: {} + + mkdirp@1.0.4: {} + + motion-dom@12.38.0: + dependencies: + motion-utils: 12.36.0 + + motion-utils@12.36.0: {} + + mri@1.2.0: {} + + ms@2.0.0: {} + + ms@2.1.3: {} + + multicast-dns@7.2.5: + dependencies: + dns-packet: 5.6.1 + thunky: 1.1.0 + + nanoid@3.3.12: {} + + natural-compare@1.4.0: {} + + needle@3.5.0: + dependencies: + iconv-lite: 0.6.3 + sax: 1.6.0 + optional: true + + negotiator@0.6.3: {} + + negotiator@0.6.4: {} + + negotiator@1.0.0: {} + + neo-async@2.6.2: {} + + next@15.5.18(@playwright/test@1.59.1)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(sass@1.99.0): + dependencies: + '@next/env': 15.5.18 + '@swc/helpers': 0.5.15 + caniuse-lite: 1.0.30001793 + postcss: 8.5.15 + react: 19.1.1 + react-dom: 19.1.1(react@19.1.1) + styled-jsx: 5.1.6(react@19.1.1) + optionalDependencies: + '@next/swc-darwin-arm64': 15.5.18 + '@next/swc-darwin-x64': 15.5.18 + '@next/swc-linux-arm64-gnu': 15.5.18 + '@next/swc-linux-arm64-musl': 15.5.18 + '@next/swc-linux-x64-gnu': 15.5.18 + '@next/swc-linux-x64-musl': 15.5.18 + '@next/swc-win32-arm64-msvc': 15.5.18 + '@next/swc-win32-x64-msvc': 15.5.18 + '@playwright/test': 1.59.1 + sass: 1.99.0 + sharp: 0.34.5 + transitivePeerDependencies: + - '@babel/core' + - babel-plugin-macros + + nice-try@1.0.5: {} + + no-case@3.0.4: + dependencies: + lower-case: 2.0.2 + tslib: 2.8.1 + + node-abort-controller@3.1.1: {} + + node-addon-api@3.2.1: {} + + node-addon-api@7.1.1: + optional: true + + node-gyp-build@4.8.4: {} + + node-releases@2.0.19: {} + + node-releases@2.0.38: {} + + normalize-path@3.0.0: {} + + normalize-url@6.1.0: {} + + npm-run-path@2.0.2: + dependencies: + path-key: 2.0.1 + + npm-run-path@4.0.1: + dependencies: + path-key: 3.1.1 + + nth-check@2.1.1: + dependencies: + boolbase: 1.0.0 + + nx@15.9.7: + dependencies: + '@nrwl/cli': 15.9.7 + '@nrwl/tao': 15.9.7 + '@parcel/watcher': 2.0.4 + '@yarnpkg/lockfile': 1.1.0 + '@yarnpkg/parsers': 3.0.0-rc.46 + '@zkochan/js-yaml': 0.0.6 + axios: 1.16.0 + chalk: 4.1.2 + cli-cursor: 3.1.0 + cli-spinners: 2.6.1 + cliui: 7.0.4 + dotenv: 10.0.0 + enquirer: 2.3.6 + fast-glob: 3.2.7 + figures: 3.2.0 + flat: 5.0.2 + fs-extra: 11.3.5 + glob: 7.1.4 + ignore: 5.3.2 + js-yaml: 4.1.1 + jsonc-parser: 3.2.0 + lines-and-columns: 2.0.4 + minimatch: 3.1.5 + npm-run-path: 4.0.1 + open: 8.4.2 + semver: 7.5.4 + string-width: 4.2.3 + strong-log-transformer: 2.1.0 + tar-stream: 2.2.0 + tmp: 0.2.5 + tsconfig-paths: 4.2.0 + tslib: 2.8.1 + v8-compile-cache: 2.3.0 + yargs: 17.7.2 + yargs-parser: 21.1.1 + optionalDependencies: + '@nrwl/nx-darwin-arm64': 15.9.7 + '@nrwl/nx-darwin-x64': 15.9.7 + '@nrwl/nx-linux-arm-gnueabihf': 15.9.7 + '@nrwl/nx-linux-arm64-gnu': 15.9.7 + '@nrwl/nx-linux-arm64-musl': 15.9.7 + '@nrwl/nx-linux-x64-gnu': 15.9.7 + '@nrwl/nx-linux-x64-musl': 15.9.7 + '@nrwl/nx-win32-arm64-msvc': 15.9.7 + '@nrwl/nx-win32-x64-msvc': 15.9.7 + transitivePeerDependencies: + - debug + + object-assign@4.1.1: {} + + object-inspect@1.13.4: {} + + object-is@1.1.6: + dependencies: + 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.8 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-object-atoms: 1.1.1 + has-symbols: 1.1.0 + object-keys: 1.1.1 + + obuf@1.1.2: {} + + on-finished@2.4.1: + dependencies: + ee-first: 1.1.1 + + on-headers@1.1.0: {} + + once@1.4.0: + dependencies: + wrappy: 1.0.2 + + onetime@5.1.2: + dependencies: + mimic-fn: 2.1.0 + + open@10.2.0: + dependencies: + default-browser: 5.5.0 + define-lazy-prop: 3.0.0 + is-inside-container: 1.0.0 + wsl-utils: 0.1.0 + + open@8.4.2: + dependencies: + define-lazy-prop: 2.0.0 + is-docker: 2.2.1 + is-wsl: 2.2.0 + + optionator@0.9.4: + dependencies: + deep-is: 0.1.4 + fast-levenshtein: 2.0.6 + levn: 0.4.1 + prelude-ls: 1.2.1 + type-check: 0.4.0 + word-wrap: 1.2.5 + + outdent@0.5.0: {} + + p-filter@2.1.0: + dependencies: + p-map: 2.1.0 + + p-finally@1.0.0: {} + + p-limit@2.3.0: + dependencies: + p-try: 2.2.0 + + p-limit@3.1.0: + dependencies: + yocto-queue: 0.1.0 + + p-limit@4.0.0: + dependencies: + yocto-queue: 1.2.2 + + p-locate@4.1.0: + dependencies: + p-limit: 2.3.0 + + p-locate@5.0.0: + dependencies: + p-limit: 3.1.0 + + p-locate@6.0.0: + dependencies: + p-limit: 4.0.0 + + p-map@2.1.0: {} + + p-retry@6.2.1: + dependencies: + '@types/retry': 0.12.2 + is-network-error: 1.3.1 + retry: 0.13.1 + + p-try@2.2.0: {} + + package-manager-detector@0.2.11: + dependencies: + quansync: 0.2.11 + + param-case@3.0.4: + dependencies: + dot-case: 3.0.4 + tslib: 2.8.1 + + parent-module@1.0.1: + dependencies: + callsites: 3.1.0 + + parse-json@5.2.0: + dependencies: + '@babel/code-frame': 7.29.0 + error-ex: 1.3.2 + json-parse-even-better-errors: 2.3.1 + lines-and-columns: 1.2.4 + + parse-node-version@1.0.1: {} + + parse5@4.0.0: {} + + parseurl@1.3.3: {} + + pascal-case@3.1.2: + dependencies: + no-case: 3.0.4 + tslib: 2.8.1 + + 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-parse@1.0.7: {} + + path-to-regexp@0.1.13: {} + + path-to-regexp@8.4.2: {} + + path-type@4.0.0: {} + + picocolors@1.1.1: {} + + picomatch@2.3.2: {} + + picomatch@4.0.4: {} + + pify@2.3.0: {} + + pify@4.0.1: {} + + pkce-challenge@5.0.1: {} + + pkg-dir@4.2.0: + dependencies: + find-up: 4.1.0 + + pkg-dir@7.0.0: + dependencies: + find-up: 6.3.0 + + pkijs@3.4.0: + dependencies: + '@noble/hashes': 1.4.0 + asn1js: 3.0.10 + bytestreamjs: 2.0.1 + pvtsutils: 1.3.6 + pvutils: 1.1.5 + tslib: 2.8.1 + + playwright-core@1.59.1: {} + + playwright@1.59.1: + dependencies: + playwright-core: 1.59.1 + optionalDependencies: + fsevents: 2.3.2 + + possible-typed-array-names@1.1.0: {} + + postcss-calc@8.2.4(postcss@8.5.15): + dependencies: + postcss: 8.5.15 + postcss-selector-parser: 6.1.2 + postcss-value-parser: 4.2.0 + + postcss-colormin@5.3.1(postcss@8.5.15): + dependencies: + browserslist: 4.28.2 + caniuse-api: 3.0.0 + colord: 2.9.3 + postcss: 8.5.15 + postcss-value-parser: 4.2.0 + + postcss-convert-values@5.1.3(postcss@8.5.15): + dependencies: + browserslist: 4.28.2 + postcss: 8.5.15 + postcss-value-parser: 4.2.0 + + postcss-discard-comments@5.1.2(postcss@8.5.15): + dependencies: + postcss: 8.5.15 + + postcss-discard-duplicates@5.1.0(postcss@8.5.15): + dependencies: + postcss: 8.5.15 + + postcss-discard-empty@5.1.1(postcss@8.5.15): + dependencies: + postcss: 8.5.15 + + postcss-discard-overridden@5.1.0(postcss@8.5.15): + dependencies: + postcss: 8.5.15 + + postcss-import@14.1.0(postcss@8.5.15): + dependencies: + postcss: 8.5.15 + postcss-value-parser: 4.2.0 + read-cache: 1.0.0 + resolve: 1.22.12 + + postcss-loader@6.2.1(postcss@8.5.15)(webpack@5.107.1): + dependencies: + cosmiconfig: 7.1.0 + klona: 2.0.6 + postcss: 8.5.15 + semver: 7.7.4 + webpack: 5.107.1(webpack-cli@6.0.1) + + postcss-merge-longhand@5.1.7(postcss@8.5.15): + dependencies: + postcss: 8.5.15 + postcss-value-parser: 4.2.0 + stylehacks: 5.1.1(postcss@8.5.15) + + postcss-merge-rules@5.1.4(postcss@8.5.15): + dependencies: + browserslist: 4.28.2 + caniuse-api: 3.0.0 + cssnano-utils: 3.1.0(postcss@8.5.15) + postcss: 8.5.15 + postcss-selector-parser: 6.1.2 + + postcss-minify-font-values@5.1.0(postcss@8.5.15): + dependencies: + postcss: 8.5.15 + postcss-value-parser: 4.2.0 + + postcss-minify-gradients@5.1.1(postcss@8.5.15): + dependencies: + colord: 2.9.3 + cssnano-utils: 3.1.0(postcss@8.5.15) + postcss: 8.5.15 + postcss-value-parser: 4.2.0 + + postcss-minify-params@5.1.4(postcss@8.5.15): + dependencies: + browserslist: 4.28.2 + cssnano-utils: 3.1.0(postcss@8.5.15) + postcss: 8.5.15 + postcss-value-parser: 4.2.0 + + postcss-minify-selectors@5.2.1(postcss@8.5.15): + dependencies: + postcss: 8.5.15 + postcss-selector-parser: 6.1.2 + + postcss-modules-extract-imports@3.1.0(postcss@8.5.15): + dependencies: + postcss: 8.5.15 + + postcss-modules-local-by-default@4.2.0(postcss@8.5.15): + dependencies: + icss-utils: 5.1.0(postcss@8.5.15) + postcss: 8.5.15 + postcss-selector-parser: 7.1.1 + postcss-value-parser: 4.2.0 + + postcss-modules-scope@3.2.1(postcss@8.5.15): + dependencies: + postcss: 8.5.15 + postcss-selector-parser: 7.1.1 + + postcss-modules-values@4.0.0(postcss@8.5.15): + dependencies: + icss-utils: 5.1.0(postcss@8.5.15) + postcss: 8.5.15 + + postcss-normalize-charset@5.1.0(postcss@8.5.15): + dependencies: + postcss: 8.5.15 + + postcss-normalize-display-values@5.1.0(postcss@8.5.15): + dependencies: + postcss: 8.5.15 + postcss-value-parser: 4.2.0 + + postcss-normalize-positions@5.1.1(postcss@8.5.15): + dependencies: + postcss: 8.5.15 + postcss-value-parser: 4.2.0 + + postcss-normalize-repeat-style@5.1.1(postcss@8.5.15): + dependencies: + postcss: 8.5.15 + postcss-value-parser: 4.2.0 + + postcss-normalize-string@5.1.0(postcss@8.5.15): + dependencies: + postcss: 8.5.15 + postcss-value-parser: 4.2.0 + + postcss-normalize-timing-functions@5.1.0(postcss@8.5.15): + dependencies: + postcss: 8.5.15 + postcss-value-parser: 4.2.0 + + postcss-normalize-unicode@5.1.1(postcss@8.5.15): + dependencies: + browserslist: 4.28.2 + postcss: 8.5.15 + postcss-value-parser: 4.2.0 + + postcss-normalize-url@5.1.0(postcss@8.5.15): + dependencies: + normalize-url: 6.1.0 + postcss: 8.5.15 + postcss-value-parser: 4.2.0 + + postcss-normalize-whitespace@5.1.1(postcss@8.5.15): + dependencies: + postcss: 8.5.15 + postcss-value-parser: 4.2.0 + + postcss-ordered-values@5.1.3(postcss@8.5.15): + dependencies: + cssnano-utils: 3.1.0(postcss@8.5.15) + postcss: 8.5.15 + postcss-value-parser: 4.2.0 + + postcss-reduce-initial@5.1.2(postcss@8.5.15): + dependencies: + browserslist: 4.28.2 + caniuse-api: 3.0.0 + postcss: 8.5.15 + + postcss-reduce-transforms@5.1.0(postcss@8.5.15): + dependencies: + postcss: 8.5.15 + 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.1: + dependencies: + cssesc: 3.0.0 + util-deprecate: 1.0.2 + + postcss-svgo@5.1.0(postcss@8.5.15): + dependencies: + postcss: 8.5.15 + postcss-value-parser: 4.2.0 + svgo: 2.8.2 + + postcss-unique-selectors@5.1.1(postcss@8.5.15): + dependencies: + postcss: 8.5.15 + postcss-selector-parser: 6.1.2 + + postcss-value-parser@4.2.0: {} + + postcss@8.5.15: + dependencies: + nanoid: 3.3.12 + picocolors: 1.1.1 + source-map-js: 1.2.1 + + prelude-ls@1.2.1: {} + + prettier@2.8.8: {} + + pretty-error@4.0.0: + dependencies: + lodash: 4.18.1 + renderkid: 3.0.0 + + pretty-format@27.5.1: + dependencies: + ansi-regex: 5.0.1 + ansi-styles: 5.2.0 + react-is: 17.0.2 + + pretty-format@30.0.2: + dependencies: + '@jest/schemas': 30.0.1 + ansi-styles: 5.2.0 + react-is: 18.3.1 + + process-nextick-args@2.0.1: {} + + prop-types@15.8.1: + dependencies: + loose-envify: 1.4.0 + object-assign: 4.1.1 + react-is: 16.13.1 + + proxy-addr@2.0.7: + dependencies: + forwarded: 0.2.0 + ipaddr.js: 1.9.1 + + proxy-from-env@2.1.0: {} + + prr@1.0.1: + optional: true + + pump@3.0.3: + dependencies: + end-of-stream: 1.4.5 + once: 1.4.0 + + punycode@2.3.1: {} + + pvtsutils@1.3.6: + dependencies: + tslib: 2.8.1 + + pvutils@1.1.5: {} + + qs@6.15.2: + dependencies: + side-channel: 1.1.0 + + quansync@0.2.11: {} + + queue-microtask@1.2.3: {} + + range-parser@1.2.1: {} + + raw-body@2.5.3: + dependencies: + bytes: 3.1.2 + http-errors: 2.0.1 + iconv-lite: 0.4.24 + unpipe: 1.0.0 + + raw-body@3.0.2: + dependencies: + bytes: 3.1.2 + http-errors: 2.0.1 + iconv-lite: 0.7.2 + unpipe: 1.0.0 + + react-clientside-effect@1.2.8(react@18.2.0): + dependencies: + '@babel/runtime': 7.29.2 + react: 18.2.0 + + react-dom@18.2.0(react@18.2.0): + dependencies: + loose-envify: 1.4.0 + react: 18.2.0 + scheduler: 0.23.2 + + react-dom@18.3.1(react@18.3.1): + dependencies: + loose-envify: 1.4.0 + react: 18.3.1 + scheduler: 0.23.2 + + react-dom@19.1.1(react@19.1.1): + dependencies: + react: 19.1.1 + scheduler: 0.26.0 + + react-fast-compare@3.2.2: {} + + react-focus-lock@2.13.7(@types/react@18.3.23)(react@18.2.0): + dependencies: + '@babel/runtime': 7.29.2 + focus-lock: 1.3.6 + prop-types: 15.8.1 + react: 18.2.0 + react-clientside-effect: 1.2.8(react@18.2.0) + use-callback-ref: 1.3.3(@types/react@18.3.23)(react@18.2.0) + use-sidecar: 1.1.3(@types/react@18.3.23)(react@18.2.0) + optionalDependencies: + '@types/react': 18.3.23 + + react-is@16.13.1: {} + + react-is@17.0.2: {} + + react-is@18.3.1: {} + + react-is@19.1.0: {} + + react-number-format@5.4.5(react-dom@19.1.1(react@19.1.1))(react@19.1.1): + dependencies: + react: 19.1.1 + react-dom: 19.1.1(react@19.1.1) + + react-refresh@0.17.0: {} + + react-remove-scroll-bar@2.3.8(@types/react@18.3.23)(react@18.2.0): + dependencies: + react: 18.2.0 + react-style-singleton: 2.2.3(@types/react@18.3.23)(react@18.2.0) + tslib: 2.8.1 + optionalDependencies: + '@types/react': 18.3.23 + + react-remove-scroll-bar@2.3.8(@types/react@19.1.8)(react@19.1.1): + dependencies: + react: 19.1.1 + react-style-singleton: 2.2.3(@types/react@19.1.8)(react@19.1.1) + tslib: 2.8.1 + optionalDependencies: + '@types/react': 19.1.8 + + react-remove-scroll@2.7.2(@types/react@18.3.23)(react@18.2.0): + dependencies: + react: 18.2.0 + react-remove-scroll-bar: 2.3.8(@types/react@18.3.23)(react@18.2.0) + react-style-singleton: 2.2.3(@types/react@18.3.23)(react@18.2.0) + tslib: 2.8.1 + use-callback-ref: 1.3.3(@types/react@18.3.23)(react@18.2.0) + use-sidecar: 1.1.3(@types/react@18.3.23)(react@18.2.0) + optionalDependencies: + '@types/react': 18.3.23 + + react-remove-scroll@2.7.2(@types/react@19.1.8)(react@19.1.1): + dependencies: + react: 19.1.1 + react-remove-scroll-bar: 2.3.8(@types/react@19.1.8)(react@19.1.1) + react-style-singleton: 2.2.3(@types/react@19.1.8)(react@19.1.1) + tslib: 2.8.1 + use-callback-ref: 1.3.3(@types/react@19.1.8)(react@19.1.1) + use-sidecar: 1.1.3(@types/react@19.1.8)(react@19.1.1) + optionalDependencies: + '@types/react': 19.1.8 + + react-router-dom@6.30.3(react-dom@18.2.0(react@18.2.0))(react@18.2.0): + dependencies: + '@remix-run/router': 1.23.2 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + react-router: 6.30.3(react@18.2.0) + + react-router-dom@6.30.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + '@remix-run/router': 1.23.2 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + react-router: 6.30.3(react@18.3.1) + + react-router@6.30.3(react@18.2.0): + dependencies: + '@remix-run/router': 1.23.2 + react: 18.2.0 + + react-router@6.30.3(react@18.3.1): + dependencies: + '@remix-run/router': 1.23.2 + react: 18.3.1 + + react-style-singleton@2.2.3(@types/react@18.3.23)(react@18.2.0): + dependencies: + get-nonce: 1.0.1 + react: 18.2.0 + tslib: 2.8.1 + optionalDependencies: + '@types/react': 18.3.23 + + react-style-singleton@2.2.3(@types/react@19.1.8)(react@19.1.1): + dependencies: + get-nonce: 1.0.1 + react: 19.1.1 + tslib: 2.8.1 + optionalDependencies: + '@types/react': 19.1.8 + + react-textarea-autosize@8.5.9(@types/react@19.1.8)(react@19.1.1): + dependencies: + '@babel/runtime': 7.29.2 + react: 19.1.1 + use-composed-ref: 1.4.0(@types/react@19.1.8)(react@19.1.1) + use-latest: 1.3.0(@types/react@19.1.8)(react@19.1.1) + transitivePeerDependencies: + - '@types/react' + + react-transition-group@4.4.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + '@babel/runtime': 7.29.2 + dom-helpers: 5.2.1 + loose-envify: 1.4.0 + prop-types: 15.8.1 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + + react@17.0.2: + dependencies: + loose-envify: 1.4.0 + object-assign: 4.1.1 + + react@18.2.0: + dependencies: + loose-envify: 1.4.0 + + react@18.3.1: + dependencies: + loose-envify: 1.4.0 + + react@19.1.1: {} + + read-cache@1.0.0: + dependencies: + pify: 2.3.0 + + read-yaml-file@1.1.0: + dependencies: + graceful-fs: 4.2.11 + js-yaml: 3.14.1 + pify: 4.0.1 + strip-bom: 3.0.0 + + readable-stream@2.3.8: + dependencies: + core-util-is: 1.0.3 + inherits: 2.0.4 + isarray: 1.0.0 + process-nextick-args: 2.0.1 + safe-buffer: 5.1.2 + string_decoder: 1.1.1 + util-deprecate: 1.0.2 + + readable-stream@3.6.2: + dependencies: + inherits: 2.0.4 + string_decoder: 1.3.0 + util-deprecate: 1.0.2 + + readdirp@3.6.0: + dependencies: + picomatch: 2.3.2 + + readdirp@4.1.2: {} + + rechoir@0.6.2: + dependencies: + resolve: 1.22.12 + + rechoir@0.8.0: + dependencies: + resolve: 1.22.12 + + redent@3.0.0: + dependencies: + indent-string: 4.0.0 + strip-indent: 3.0.0 + + reflect-metadata@0.2.2: {} + + regenerate-unicode-properties@10.2.2: + dependencies: + regenerate: 1.4.2 + + regenerate@1.4.2: {} + + regexp.prototype.flags@1.5.4: + dependencies: + call-bind: 1.0.8 + define-properties: 1.2.1 + es-errors: 1.3.0 + get-proto: 1.0.1 + gopd: 1.2.0 + set-function-name: 2.0.2 + + regexpp@3.2.0: {} + + regexpu-core@6.4.0: + dependencies: + regenerate: 1.4.2 + regenerate-unicode-properties: 10.2.2 + regjsgen: 0.8.0 + regjsparser: 0.13.1 + unicode-match-property-ecmascript: 2.0.0 + unicode-match-property-value-ecmascript: 2.2.1 + + regjsgen@0.8.0: {} + + regjsparser@0.13.1: + dependencies: + jsesc: 3.1.0 + + relateurl@0.2.7: {} + + renderkid@3.0.0: + dependencies: + css-select: 4.3.0 + dom-converter: 0.2.0 + htmlparser2: 6.1.0 + lodash: 4.18.1 + strip-ansi: 6.0.1 + + require-directory@2.1.1: {} + + require-from-string@2.0.2: {} + + requires-port@1.0.0: {} + + resolve-cwd@3.0.0: + dependencies: + resolve-from: 5.0.0 + + resolve-from@4.0.0: {} + + resolve-from@5.0.0: {} + + resolve@1.22.12: + dependencies: + es-errors: 1.3.0 + is-core-module: 2.16.1 + path-parse: 1.0.7 + supports-preserve-symlinks-flag: 1.0.0 + + restore-cursor@3.1.0: + dependencies: + onetime: 5.1.2 + signal-exit: 3.0.7 + + retry@0.13.1: {} + + reusify@1.1.0: {} + + rimraf@3.0.2: + dependencies: + glob: 7.2.3 + + rollup@4.60.4: + dependencies: + '@types/estree': 1.0.8 + optionalDependencies: + '@rollup/rollup-android-arm-eabi': 4.60.4 + '@rollup/rollup-android-arm64': 4.60.4 + '@rollup/rollup-darwin-arm64': 4.60.4 + '@rollup/rollup-darwin-x64': 4.60.4 + '@rollup/rollup-freebsd-arm64': 4.60.4 + '@rollup/rollup-freebsd-x64': 4.60.4 + '@rollup/rollup-linux-arm-gnueabihf': 4.60.4 + '@rollup/rollup-linux-arm-musleabihf': 4.60.4 + '@rollup/rollup-linux-arm64-gnu': 4.60.4 + '@rollup/rollup-linux-arm64-musl': 4.60.4 + '@rollup/rollup-linux-loong64-gnu': 4.60.4 + '@rollup/rollup-linux-loong64-musl': 4.60.4 + '@rollup/rollup-linux-ppc64-gnu': 4.60.4 + '@rollup/rollup-linux-ppc64-musl': 4.60.4 + '@rollup/rollup-linux-riscv64-gnu': 4.60.4 + '@rollup/rollup-linux-riscv64-musl': 4.60.4 + '@rollup/rollup-linux-s390x-gnu': 4.60.4 + '@rollup/rollup-linux-x64-gnu': 4.60.4 + '@rollup/rollup-linux-x64-musl': 4.60.4 + '@rollup/rollup-openbsd-x64': 4.60.4 + '@rollup/rollup-openharmony-arm64': 4.60.4 + '@rollup/rollup-win32-arm64-msvc': 4.60.4 + '@rollup/rollup-win32-ia32-msvc': 4.60.4 + '@rollup/rollup-win32-x64-gnu': 4.60.4 + '@rollup/rollup-win32-x64-msvc': 4.60.4 + fsevents: 2.3.3 + + router@2.2.0: + dependencies: + debug: 4.4.3 + depd: 2.0.0 + is-promise: 4.0.0 + parseurl: 1.3.3 + path-to-regexp: 8.4.2 + transitivePeerDependencies: + - supports-color + + run-applescript@7.1.0: {} + + run-parallel@1.2.0: + dependencies: + queue-microtask: 1.2.3 + + rxjs@6.6.7: + dependencies: + tslib: 1.14.1 + + safe-buffer@5.1.2: {} + + safe-buffer@5.2.1: {} + + safe-regex-test@1.1.0: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + is-regex: 1.2.1 + + safer-buffer@2.1.2: {} + + sass-loader@12.6.0(sass@1.99.0)(webpack@5.107.1): + dependencies: + klona: 2.0.6 + neo-async: 2.6.2 + webpack: 5.107.1(webpack-cli@6.0.1) + optionalDependencies: + sass: 1.99.0 + + sass@1.99.0: + dependencies: + chokidar: 4.0.3 + immutable: 5.1.5 + source-map-js: 1.2.1 + optionalDependencies: + '@parcel/watcher': 2.5.6 + + sax@1.2.4: {} + + sax@1.6.0: {} + + scheduler@0.23.2: + dependencies: + loose-envify: 1.4.0 + + scheduler@0.26.0: {} + + schema-utils@3.3.0: + dependencies: + '@types/json-schema': 7.0.15 + ajv: 6.15.0 + ajv-keywords: 3.5.2(ajv@6.15.0) + + schema-utils@4.3.3: + dependencies: + '@types/json-schema': 7.0.15 + ajv: 8.20.0 + ajv-formats: 2.1.1(ajv@8.20.0) + ajv-keywords: 5.1.0(ajv@8.20.0) + + scroll-into-view-if-needed@3.1.0: + dependencies: + compute-scroll-into-view: 3.1.1 + + select-hose@2.0.0: {} + + selfsigned@5.5.0: + dependencies: + '@peculiar/x509': 1.14.3 + pkijs: 3.4.0 + + semver@5.7.2: {} + + semver@6.3.1: {} + + semver@7.5.4: + dependencies: + lru-cache: 6.0.0 + + semver@7.7.4: {} + + send@0.19.2: + dependencies: + debug: 2.6.9 + depd: 2.0.0 + destroy: 1.2.0 + encodeurl: 2.0.0 + escape-html: 1.0.3 + etag: 1.8.1 + fresh: 0.5.2 + http-errors: 2.0.1 + mime: 1.6.0 + ms: 2.1.3 + on-finished: 2.4.1 + range-parser: 1.2.1 + statuses: 2.0.2 + transitivePeerDependencies: + - supports-color + + send@1.2.1: + dependencies: + debug: 4.4.3 + encodeurl: 2.0.0 + escape-html: 1.0.3 + etag: 1.8.1 + fresh: 2.0.0 + http-errors: 2.0.1 + mime-types: 3.0.2 + ms: 2.1.3 + on-finished: 2.4.1 + range-parser: 1.2.1 + statuses: 2.0.2 + transitivePeerDependencies: + - supports-color + + serialize-javascript@7.0.5: {} + + serve-index@1.9.2: + dependencies: + accepts: 1.3.8 + batch: 0.6.1 + debug: 2.6.9 + escape-html: 1.0.3 + http-errors: 1.8.1 + mime-types: 2.1.35 + parseurl: 1.3.3 + transitivePeerDependencies: + - supports-color + + serve-static@1.16.3: + dependencies: + encodeurl: 2.0.0 + escape-html: 1.0.3 + parseurl: 1.3.3 + send: 0.19.2 + transitivePeerDependencies: + - supports-color + + serve-static@2.2.1: + dependencies: + encodeurl: 2.0.0 + escape-html: 1.0.3 + parseurl: 1.3.3 + send: 1.2.1 + transitivePeerDependencies: + - supports-color + + set-function-length@1.2.2: + dependencies: + define-data-property: 1.1.4 + es-errors: 1.3.0 + function-bind: 1.1.2 + get-intrinsic: 1.3.0 + gopd: 1.2.0 + has-property-descriptors: 1.0.2 + + set-function-name@2.0.2: + dependencies: + define-data-property: 1.1.4 + es-errors: 1.3.0 + functions-have-names: 1.2.3 + has-property-descriptors: 1.0.2 + + setprototypeof@1.2.0: {} + + shallow-clone@3.0.1: + dependencies: + kind-of: 6.0.3 + + sharp@0.34.5: + dependencies: + '@img/colour': 1.1.0 + detect-libc: 2.1.2 + semver: 7.7.4 + optionalDependencies: + '@img/sharp-darwin-arm64': 0.34.5 + '@img/sharp-darwin-x64': 0.34.5 + '@img/sharp-libvips-darwin-arm64': 1.2.4 + '@img/sharp-libvips-darwin-x64': 1.2.4 + '@img/sharp-libvips-linux-arm': 1.2.4 + '@img/sharp-libvips-linux-arm64': 1.2.4 + '@img/sharp-libvips-linux-ppc64': 1.2.4 + '@img/sharp-libvips-linux-riscv64': 1.2.4 + '@img/sharp-libvips-linux-s390x': 1.2.4 + '@img/sharp-libvips-linux-x64': 1.2.4 + '@img/sharp-libvips-linuxmusl-arm64': 1.2.4 + '@img/sharp-libvips-linuxmusl-x64': 1.2.4 + '@img/sharp-linux-arm': 0.34.5 + '@img/sharp-linux-arm64': 0.34.5 + '@img/sharp-linux-ppc64': 0.34.5 + '@img/sharp-linux-riscv64': 0.34.5 + '@img/sharp-linux-s390x': 0.34.5 + '@img/sharp-linux-x64': 0.34.5 + '@img/sharp-linuxmusl-arm64': 0.34.5 + '@img/sharp-linuxmusl-x64': 0.34.5 + '@img/sharp-wasm32': 0.34.5 + '@img/sharp-win32-arm64': 0.34.5 + '@img/sharp-win32-ia32': 0.34.5 + '@img/sharp-win32-x64': 0.34.5 + optional: true + + shebang-command@1.2.0: + dependencies: + shebang-regex: 1.0.0 + + shebang-command@2.0.0: + dependencies: + shebang-regex: 3.0.0 + + shebang-regex@1.0.0: {} + + shebang-regex@3.0.0: {} + + shell-quote@1.8.3: {} + + shelljs@0.9.2: + dependencies: + execa: 1.0.0 + fast-glob: 3.3.3 + interpret: 1.4.0 + rechoir: 0.6.2 + + shx@0.4.0: + dependencies: + 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-map@1.0.1: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + object-inspect: 1.13.4 + + side-channel-weakmap@1.0.2: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + 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 + + signal-exit@3.0.7: {} + + signal-exit@4.1.0: {} + + slash@3.0.0: {} + + slash@4.0.0: {} + + sockjs@0.3.24: + dependencies: + faye-websocket: 0.11.4 + uuid: 11.1.1 + websocket-driver: 0.7.4 + + source-map-js@1.2.1: {} + + source-map-loader@3.0.2(webpack@5.107.1): + dependencies: + abab: 2.0.6 + iconv-lite: 0.6.3 + source-map-js: 1.2.1 + webpack: 5.107.1(webpack-cli@6.0.1) + + source-map-resolve@0.6.0: + dependencies: + atob: 2.1.2 + decode-uri-component: 0.2.2 + + source-map-support@0.5.19: + dependencies: + buffer-from: 1.1.2 + source-map: 0.6.1 + + source-map-support@0.5.21: + dependencies: + buffer-from: 1.1.2 + source-map: 0.6.1 + + source-map@0.5.7: {} + + source-map@0.6.1: {} + + source-map@0.7.6: {} + + spawndamnit@3.0.1: + dependencies: + cross-spawn: 7.0.6 + signal-exit: 4.1.0 + + spdy-transport@3.0.0: + dependencies: + debug: 4.4.3 + detect-node: 2.1.0 + hpack.js: 2.1.6 + obuf: 1.1.2 + readable-stream: 3.6.2 + wbuf: 1.7.3 + transitivePeerDependencies: + - supports-color + + spdy@4.0.2: + dependencies: + debug: 4.4.3 + handle-thing: 2.0.1 + http-deceiver: 1.2.7 + select-hose: 2.0.0 + spdy-transport: 3.0.0 + transitivePeerDependencies: + - supports-color + + sprintf-js@1.0.3: {} + + stable@0.1.8: {} + + stack-utils@2.0.6: + dependencies: + escape-string-regexp: 2.0.0 + + statuses@1.5.0: {} + + statuses@2.0.2: {} + + stop-iteration-iterator@1.1.0: + dependencies: + es-errors: 1.3.0 + internal-slot: 1.1.0 + + string-convert@0.2.1: {} + + string-width@4.2.3: + dependencies: + emoji-regex: 8.0.0 + is-fullwidth-code-point: 3.0.0 + strip-ansi: 6.0.1 + + string_decoder@1.1.1: + dependencies: + safe-buffer: 5.1.2 + + string_decoder@1.3.0: + dependencies: + safe-buffer: 5.2.1 + + strip-ansi@6.0.1: + dependencies: + ansi-regex: 5.0.1 + + strip-bom@3.0.0: {} + + strip-eof@1.0.0: {} + + strip-indent@3.0.0: + dependencies: + min-indent: 1.0.1 + + strip-json-comments@3.1.1: {} + + strong-log-transformer@2.1.0: + dependencies: + duplexer: 0.1.2 + minimist: 1.2.8 + through: 2.3.8 + + style-loader@3.3.4(webpack@5.107.1): + dependencies: + webpack: 5.107.1(webpack-cli@6.0.1) + + style-loader@4.0.0(webpack@5.107.1): + dependencies: + webpack: 5.107.1(webpack-cli@6.0.1) + + styled-jsx@5.1.6(react@19.1.1): + dependencies: + client-only: 0.0.1 + react: 19.1.1 + + stylehacks@5.1.1(postcss@8.5.15): + dependencies: + browserslist: 4.28.2 + postcss: 8.5.15 + postcss-selector-parser: 6.1.2 + + stylis@4.2.0: {} + + stylis@4.4.0: {} + + stylus-loader@7.1.3(stylus@0.55.0)(webpack@5.107.1): + dependencies: + fast-glob: 3.3.3 + normalize-path: 3.0.0 + stylus: 0.55.0 + webpack: 5.107.1(webpack-cli@6.0.1) + + stylus@0.55.0: + dependencies: + css: 3.0.0 + debug: 3.1.0 + glob: 7.2.3 + mkdirp: 1.0.4 + safer-buffer: 2.1.2 + sax: 1.2.4 + semver: 6.3.1 + source-map: 0.7.6 + transitivePeerDependencies: + - supports-color + + supports-color@7.2.0: + dependencies: + has-flag: 4.0.0 + + supports-color@8.1.1: + dependencies: + has-flag: 4.0.0 + + supports-preserve-symlinks-flag@1.0.0: {} + + svg-parser@2.0.4: {} + + svgo@2.8.2: + dependencies: + 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 + + tabbable@6.4.0: {} + + tapable@2.3.3: {} + + tar-stream@2.2.0: + dependencies: + bl: 4.1.0 + end-of-stream: 1.4.5 + fs-constants: 1.0.0 + inherits: 2.0.4 + readable-stream: 3.6.2 + + term-size@2.2.1: {} + + terser-webpack-plugin@5.5.0(webpack@5.107.1): + dependencies: + '@jridgewell/trace-mapping': 0.3.29 + jest-worker: 27.5.1 + schema-utils: 4.3.3 + terser: 5.46.2 + webpack: 5.107.1(webpack-cli@6.0.1) + + terser@5.46.2: + dependencies: + '@jridgewell/source-map': 0.3.11 + acorn: 8.16.0 + commander: 2.20.3 + source-map-support: 0.5.21 + + text-table@0.2.0: {} + + thingies@2.6.0(tslib@2.8.1): + dependencies: + tslib: 2.8.1 + + throttle-debounce@5.0.2: {} + + through@2.3.8: {} + + thunky@1.1.0: {} + + tinyglobby@0.2.14: + dependencies: + fdir: 6.4.6(picomatch@4.0.4) + picomatch: 4.0.4 + + tmp@0.2.5: {} + + to-regex-range@5.0.1: + dependencies: + is-number: 7.0.0 + + toggle-selection@1.0.6: {} + + toidentifier@1.0.1: {} + + tree-dump@1.1.0(tslib@2.8.1): + dependencies: + tslib: 2.8.1 + + tree-kill@1.2.2: {} + + ts-loader@9.4.1(typescript@4.8.4)(webpack@5.107.1): + dependencies: + chalk: 4.1.2 + enhanced-resolve: 5.22.0 + micromatch: 4.0.8 + semver: 7.7.4 + typescript: 4.8.4 + webpack: 5.107.1(webpack-cli@6.0.1) + + ts-node@10.9.1(@types/node@24.0.13)(typescript@4.8.4): + 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.0.13 + acorn: 8.16.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: 4.8.4 + v8-compile-cache-lib: 3.0.1 + yn: 3.1.1 + + tsconfig-paths-webpack-plugin@4.0.0: + dependencies: + chalk: 4.1.2 + enhanced-resolve: 5.22.0 + tsconfig-paths: 4.2.0 + + tsconfig-paths@4.2.0: + dependencies: + json5: 2.2.3 + minimist: 1.2.8 + strip-bom: 3.0.0 + + tslib@1.14.1: {} + + tslib@2.4.0: {} + + tslib@2.8.1: {} + + tsutils@3.21.0(typescript@4.8.4): + dependencies: + tslib: 1.14.1 + typescript: 4.8.4 + + tsyringe@4.10.0: + dependencies: + tslib: 1.14.1 + + type-check@0.4.0: + dependencies: + prelude-ls: 1.2.1 + + type-fest@0.20.2: {} + + type-fest@4.41.0: {} + + type-is@1.6.18: + dependencies: + media-typer: 0.3.0 + mime-types: 2.1.35 + + type-is@2.1.0: + dependencies: + content-type: 2.0.0 + media-typer: 1.1.0 + mime-types: 3.0.2 + + typed-assert@1.0.9: {} + + typescript@4.8.4: {} + + typescript@5.8.3: {} + + undici-types@7.8.0: {} + + unicode-canonical-property-names-ecmascript@2.0.1: {} + + unicode-match-property-ecmascript@2.0.0: + dependencies: + unicode-canonical-property-names-ecmascript: 2.0.1 + unicode-property-aliases-ecmascript: 2.2.0 + + unicode-match-property-value-ecmascript@2.2.1: {} + + unicode-property-aliases-ecmascript@2.2.0: {} + + universalify@0.1.2: {} + + universalify@2.0.1: {} + + unpipe@1.0.0: {} + + update-browserslist-db@1.1.3(browserslist@4.25.1): + dependencies: + browserslist: 4.25.1 + escalade: 3.2.0 + picocolors: 1.1.1 + + update-browserslist-db@1.2.3(browserslist@4.28.2): + dependencies: + browserslist: 4.28.2 + escalade: 3.2.0 + picocolors: 1.1.1 + + uri-js@4.4.1: + dependencies: + punycode: 2.3.1 + + use-callback-ref@1.3.3(@types/react@18.3.23)(react@18.2.0): + dependencies: + react: 18.2.0 + tslib: 2.8.1 + optionalDependencies: + '@types/react': 18.3.23 + + use-callback-ref@1.3.3(@types/react@19.1.8)(react@19.1.1): + dependencies: + react: 19.1.1 + tslib: 2.8.1 + optionalDependencies: + '@types/react': 19.1.8 + + use-composed-ref@1.4.0(@types/react@19.1.8)(react@19.1.1): + dependencies: + react: 19.1.1 + optionalDependencies: + '@types/react': 19.1.8 + + use-isomorphic-layout-effect@1.2.1(@types/react@19.1.8)(react@19.1.1): + dependencies: + react: 19.1.1 + optionalDependencies: + '@types/react': 19.1.8 + + use-latest@1.3.0(@types/react@19.1.8)(react@19.1.1): + dependencies: + react: 19.1.1 + use-isomorphic-layout-effect: 1.2.1(@types/react@19.1.8)(react@19.1.1) + optionalDependencies: + '@types/react': 19.1.8 + + use-sidecar@1.1.3(@types/react@18.3.23)(react@18.2.0): + dependencies: + detect-node-es: 1.1.0 + react: 18.2.0 + tslib: 2.8.1 + optionalDependencies: + '@types/react': 18.3.23 + + use-sidecar@1.1.3(@types/react@19.1.8)(react@19.1.1): + dependencies: + detect-node-es: 1.1.0 + react: 19.1.1 + tslib: 2.8.1 + optionalDependencies: + '@types/react': 19.1.8 + + util-deprecate@1.0.2: {} + + utila@0.4.0: {} + + utils-merge@1.0.1: {} + + uuid@11.1.1: {} + + v8-compile-cache-lib@3.0.1: {} + + v8-compile-cache@2.3.0: {} + + vary@1.1.2: {} + + vite@6.4.2(@types/node@24.0.13)(less@4.1.3)(sass@1.99.0)(stylus@0.55.0)(terser@5.46.2)(yaml@1.10.3): + dependencies: + esbuild: 0.25.6 + fdir: 6.4.6(picomatch@4.0.4) + picomatch: 4.0.4 + postcss: 8.5.15 + rollup: 4.60.4 + tinyglobby: 0.2.14 + optionalDependencies: + '@types/node': 24.0.13 + fsevents: 2.3.3 + less: 4.1.3 + sass: 1.99.0 + stylus: 0.55.0 + terser: 5.46.2 + yaml: 1.10.3 + + watchpack@2.5.1: + dependencies: + glob-to-regexp: 0.4.1 + graceful-fs: 4.2.11 + + wbuf@1.7.3: + dependencies: + minimalistic-assert: 1.0.1 + + webpack-cli@6.0.1(webpack-dev-server@5.2.4)(webpack@5.107.1): + dependencies: + '@discoveryjs/json-ext': 0.6.3 + '@webpack-cli/configtest': 3.0.1(webpack-cli@6.0.1)(webpack@5.107.1) + '@webpack-cli/info': 3.0.1(webpack-cli@6.0.1)(webpack@5.107.1) + '@webpack-cli/serve': 3.0.1(webpack-cli@6.0.1)(webpack-dev-server@5.2.4)(webpack@5.107.1) + colorette: 2.0.20 + commander: 12.1.0 + cross-spawn: 7.0.6 + envinfo: 7.21.0 + fastest-levenshtein: 1.0.16 + import-local: 3.2.0 + interpret: 3.1.1 + rechoir: 0.8.0 + webpack: 5.107.1(webpack-cli@6.0.1) + webpack-merge: 6.0.1 + optionalDependencies: + webpack-dev-server: 5.2.4(tslib@2.8.1)(webpack-cli@6.0.1)(webpack@5.107.1) + + webpack-dev-middleware@7.4.5(tslib@2.8.1)(webpack@5.107.1): + dependencies: + colorette: 2.0.20 + memfs: 4.57.2(tslib@2.8.1) + mime-types: 3.0.1 + on-finished: 2.4.1 + range-parser: 1.2.1 + schema-utils: 4.3.3 + optionalDependencies: + webpack: 5.107.1(webpack-cli@6.0.1) + transitivePeerDependencies: + - tslib + + webpack-dev-server@5.2.4(tslib@2.8.1)(webpack-cli@6.0.1)(webpack@5.107.1): + dependencies: + '@types/bonjour': 3.5.13 + '@types/connect-history-api-fallback': 1.5.4 + '@types/express': 4.17.25 + '@types/express-serve-static-core': 4.19.8 + '@types/serve-index': 1.9.4 + '@types/serve-static': 1.15.10 + '@types/sockjs': 0.3.36 + '@types/ws': 8.18.1 + ansi-html-community: 0.0.8 + bonjour-service: 1.3.0 + chokidar: 3.6.0 + colorette: 2.0.20 + compression: 1.8.1 + connect-history-api-fallback: 2.0.0 + express: 4.22.1 + graceful-fs: 4.2.11 + http-proxy-middleware: 2.0.9(@types/express@4.17.25) + ipaddr.js: 2.3.0 + launch-editor: 2.13.2 + open: 10.2.0 + p-retry: 6.2.1 + schema-utils: 4.3.3 + selfsigned: 5.5.0 + serve-index: 1.9.2 + sockjs: 0.3.24 + spdy: 4.0.2 + webpack-dev-middleware: 7.4.5(tslib@2.8.1)(webpack@5.107.1) + ws: 8.21.0 + optionalDependencies: + webpack: 5.107.1(webpack-cli@6.0.1) + webpack-cli: 6.0.1(webpack-dev-server@5.2.4)(webpack@5.107.1) + transitivePeerDependencies: + - bufferutil + - debug + - supports-color + - tslib + - utf-8-validate + + webpack-merge@5.8.0: + dependencies: + clone-deep: 4.0.1 + wildcard: 2.0.1 + + webpack-merge@6.0.1: + dependencies: + clone-deep: 4.0.1 + flat: 5.0.2 + wildcard: 2.0.1 + + webpack-node-externals@3.0.0: {} + + webpack-sources@3.4.1: {} + + webpack-subresource-integrity@5.1.0(html-webpack-plugin@5.6.3(webpack@5.107.1))(webpack@5.107.1): + dependencies: + typed-assert: 1.0.9 + webpack: 5.107.1(webpack-cli@6.0.1) + optionalDependencies: + html-webpack-plugin: 5.6.3(webpack@5.107.1) + + webpack@5.107.1(webpack-cli@6.0.1): + dependencies: + '@types/estree': 1.0.8 + '@types/json-schema': 7.0.15 + '@webassemblyjs/ast': 1.14.1 + '@webassemblyjs/wasm-edit': 1.14.1 + '@webassemblyjs/wasm-parser': 1.14.1 + acorn: 8.16.0 + acorn-import-phases: 1.0.4(acorn@8.16.0) + browserslist: 4.28.2 + chrome-trace-event: 1.0.4 + enhanced-resolve: 5.22.0 + es-module-lexer: 2.1.0 + eslint-scope: 5.1.1 + events: 3.3.0 + glob-to-regexp: 0.4.1 + graceful-fs: 4.2.11 + loader-runner: 4.3.2 + mime-db: 1.54.0 + neo-async: 2.6.2 + schema-utils: 4.3.3 + tapable: 2.3.3 + terser-webpack-plugin: 5.5.0(webpack@5.107.1) + watchpack: 2.5.1 + webpack-sources: 3.4.1 + optionalDependencies: + webpack-cli: 6.0.1(webpack-dev-server@5.2.4)(webpack@5.107.1) + transitivePeerDependencies: + - '@swc/core' + - esbuild + - uglify-js + + websocket-driver@0.7.4: + dependencies: + http-parser-js: 0.5.10 + safe-buffer: 5.2.1 + websocket-extensions: 0.1.4 + + websocket-extensions@0.1.4: {} + + which-boxed-primitive@1.1.1: + dependencies: + is-bigint: 1.1.0 + is-boolean-object: 1.2.2 + is-number-object: 1.1.1 + is-string: 1.1.1 + is-symbol: 1.1.1 + + which-collection@1.0.2: + dependencies: + is-map: 2.0.3 + is-set: 2.0.3 + is-weakmap: 2.0.2 + is-weakset: 2.0.4 + + which-typed-array@1.1.19: + dependencies: + available-typed-arrays: 1.0.7 + call-bind: 1.0.8 + call-bound: 1.0.4 + for-each: 0.3.5 + get-proto: 1.0.1 + gopd: 1.2.0 + has-tostringtag: 1.0.2 + + which@1.3.1: + dependencies: + isexe: 2.0.0 + + which@2.0.2: + dependencies: + isexe: 2.0.0 + + wildcard@2.0.1: {} + + word-wrap@1.2.5: {} + + wrap-ansi@7.0.0: + dependencies: + ansi-styles: 4.3.0 + string-width: 4.2.3 + strip-ansi: 6.0.1 + + wrappy@1.0.2: {} + + ws@8.21.0: {} + + wsl-utils@0.1.0: + dependencies: + is-wsl: 3.1.1 + + y18n@5.0.8: {} + + yallist@3.1.1: {} + + yallist@4.0.0: {} + + yaml@1.10.3: {} + + yargs-parser@21.1.1: {} + + yargs@17.7.2: + dependencies: + cliui: 8.0.1 + escalade: 3.2.0 + get-caller-file: 2.0.5 + require-directory: 2.1.1 + string-width: 4.2.3 + y18n: 5.0.8 + yargs-parser: 21.1.1 + + yn@3.1.1: {} + + yocto-queue@0.1.0: {} + + yocto-queue@1.2.2: {} + + zod-to-json-schema@3.25.2(zod@3.25.76): + dependencies: + zod: 3.25.76 + + zod@3.25.76: {} diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 8ee391c..47123ec 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -1,16 +1,14 @@ packages: - # Include the root package - - '.' - # Include all packages in playground directory + # Workspace packages + - 'packages/*' + # Local examples and integration test apps - 'playground/*' - # Include all packages in src directory - - 'src/*' catalog: # React ecosystem react: ^18.3.0 react-dom: ^18.3.0 - react-router-dom: ^6.18.0 + react-router-dom: ^6.30.3 '@types/react': ^18.3.0 '@types/react-dom': ^18.3.0 '@modelcontextprotocol/inspector': ^0.6.0 @@ -26,8 +24,8 @@ catalog: # Build tools typescript: ^5.8.2 react-scripts: ^5.0.1 - vite: ^6.2.1 + vite: ^6.4.2 vite-dev-rpc: ^1.0.7 vite-hot-client: ^2.0.4 - vite-react-mcp: ^0.1.1 - zod: ^3.24.2 \ No newline at end of file + '@agentic-react/core': ^0.1.0 + zod: ^3.24.2 diff --git a/scripts/run-e2e.mjs b/scripts/run-e2e.mjs new file mode 100644 index 0000000..711c1e6 --- /dev/null +++ b/scripts/run-e2e.mjs @@ -0,0 +1,72 @@ +import { spawn } from 'node:child_process'; +import path from 'node:path'; +import { fileURLToPath } from 'node:url'; + +const __filename = fileURLToPath(import.meta.url); +const rootDir = path.resolve(path.dirname(__filename), '..'); +const playwrightArgs = process.argv.slice(2).filter((arg) => arg !== '--'); + +const run = (command, args, cwd) => + new Promise((resolve, reject) => { + const child = spawn(command, args, { + cwd, + stdio: 'inherit', + shell: process.platform === 'win32', + }); + + child.on('error', reject); + child.on('close', (exitCode) => { + if (exitCode === 0) { + resolve(); + return; + } + reject( + new Error( + `${command} ${args.join(' ')} failed with exit code ${exitCode}`, + ), + ); + }); + }); + +const suites = [ + { + name: 'agentic-react-vite-playground', + cwd: path.join(rootDir, 'playground/agentic-react-vite-playground'), + command: ['pnpm', ['run', 'e2e', ...playwrightArgs]], + }, + { + name: 'agentic-react-webpack-playground', + cwd: path.join(rootDir, 'playground/agentic-react-webpack-playground'), + command: [ + 'pnpm', + ['exec', 'env', '-u', 'NO_COLOR', 'playwright', 'test', ...playwrightArgs], + ], + }, + { + name: 'agentic-react-next-playground', + cwd: path.join(rootDir, 'playground/agentic-react-next-playground'), + command: [ + 'pnpm', + ['exec', 'env', '-u', 'NO_COLOR', 'playwright', 'test', ...playwrightArgs], + ], + }, + { + name: 'agentic-react-nx-module-federation-playground', + cwd: path.join( + rootDir, + 'playground/agentic-react-nx-module-federation-playground', + ), + command: [ + 'pnpm', + ['exec', 'env', '-u', 'NO_COLOR', 'playwright', 'test', ...playwrightArgs], + ], + }, +]; + +await run('pnpm', ['run', 'build'], rootDir); + +for (const suite of suites) { + const [command, args] = suite.command; + console.info(`\n[agentic-react] running ${suite.name}`); + await run(command, args, suite.cwd); +} diff --git a/scripts/with-build-lock.mjs b/scripts/with-build-lock.mjs new file mode 100644 index 0000000..10e1479 --- /dev/null +++ b/scripts/with-build-lock.mjs @@ -0,0 +1,99 @@ +import fs from 'node:fs'; +import os from 'node:os'; +import path from 'node:path'; +import { spawn } from 'node:child_process'; + +const STALE_LOCK_MS = 10 * 60 * 1000; +const HEARTBEAT_MS = 30 * 1000; +const WAIT_MS = 100; + +const command = process.argv[2]; +const commandArgs = process.argv.slice(3); + +if (!command) { + console.error('Usage: node scripts/with-build-lock.mjs [...args]'); + process.exit(1); +} + +const workspaceKey = Buffer.from(process.cwd()).toString('base64url'); +const lockDir = path.join( + os.tmpdir(), + `agentic-react-workspace-build-${workspaceKey}.lock`, +); + +const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms)); + +const acquireLock = async () => { + while (true) { + try { + fs.mkdirSync(lockDir); + fs.writeFileSync( + path.join(lockDir, 'owner.json'), + JSON.stringify( + { pid: process.pid, cwd: process.cwd(), createdAt: Date.now() }, + null, + 2, + ), + ); + return; + } catch (error) { + if (error?.code !== 'EEXIST') { + throw error; + } + + let isStale = false; + try { + const stats = fs.statSync(lockDir); + isStale = Date.now() - stats.mtimeMs > STALE_LOCK_MS; + } catch (_error) { + isStale = true; + } + + if (isStale) { + fs.rmSync(lockDir, { recursive: true, force: true }); + continue; + } + + await sleep(WAIT_MS); + } + } +}; + +const releaseLock = () => { + fs.rmSync(lockDir, { recursive: true, force: true }); +}; + +await acquireLock(); + +let child; +const heartbeat = setInterval(() => { + try { + fs.utimesSync(lockDir, new Date(), new Date()); + } catch (_error) { + // best effort only + } +}, HEARTBEAT_MS); + +try { + const forwardSignal = (signal) => { + if (child && !child.killed) { + child.kill(signal); + } + }; + process.once('SIGINT', () => forwardSignal('SIGINT')); + process.once('SIGTERM', () => forwardSignal('SIGTERM')); + + child = spawn(command, commandArgs, { + stdio: 'inherit', + shell: process.platform === 'win32', + }); + + const exitCode = await new Promise((resolve) => { + child.on('close', resolve); + }); + + process.exitCode = exitCode ?? 1; +} finally { + clearInterval(heartbeat); + releaseLock(); +} diff --git a/src/babel_collect_name.ts b/src/babel_collect_name.ts deleted file mode 100644 index 4f7f4d0..0000000 --- a/src/babel_collect_name.ts +++ /dev/null @@ -1,377 +0,0 @@ -import type * as babel from '@babel/core'; -import type { PluginItem } from '@babel/core'; -import type { PluginObj } from '@babel/core'; -import { store } from './shared/store.js'; - -export function findDisplayNamePropertyInClassExpression( - path: babel.NodePath, - t: typeof babel.types, - componentName: string, -): string | null { - if (!t.isClassDeclaration(path.node) && !t.isClassExpression(path.node)) - return null; - - path.traverse({ - ClassProperty(propertyPath: babel.NodePath) { - // e.g. class App extends React.Component { - // static displayName = 'App_mcp' - // } - if ( - t.isIdentifier(propertyPath.node.key, { name: 'displayName' }) && - propertyPath.node.static - ) { - if (t.isStringLiteral(propertyPath.node.value)) { - return propertyPath.node.value.value; - } - if (t.isIdentifier(propertyPath.node.value)) { - const value = findConstantIdentifierValueInScope( - propertyPath, - t, - propertyPath.node.value, - ); - if (value) return value; - return componentName; - } - } - }, - }); - - return null; -} - -/** - * Handles the cases of App.displayName = 'App' or App.displayName = NAME_CONSTANT - */ -export function findDisplayNameAssignmentInScope( - path: babel.NodePath, - t: typeof babel.types, - componentName: string, -): string | null { - // Get the binding for the component - const binding = path.scope.getBinding(componentName); - - // If we can't find the binding, assume no displayName exists - if (!binding) return null; - - // Check all references to this component - for (const refPath of binding.referencePaths) { - // Check if it's componentName.displayName = ? - const parent = refPath.parent; - if (!t.isMemberExpression(parent)) continue; - if (!t.isIdentifier(parent.property, { name: 'displayName' })) continue; - - // Check if it's used in an assignment - const memberPath = refPath.parentPath; - const grandParent = memberPath.parent; - - if (t.isAssignmentExpression(grandParent) && grandParent.operator === '=') { - // Found an assignment, now extract the value - const valueNode = grandParent.right; - const grandParentPath = memberPath.parentPath; - // simple case like App.displayName = 'App' - if (t.isStringLiteral(valueNode)) return valueNode.value; - - if (t.isIdentifier(valueNode)) { - // it also can be App.displayName = NAME_CONSTANT - const value = findConstantIdentifierValueInScope( - grandParentPath, - t, - valueNode, - ); - if (value) return value; - return componentName; - } - } - } - return null; -} - -/** - * Find the value of the constant identifier in the scope - */ -function findConstantIdentifierValueInScope( - path: babel.NodePath, - t: typeof babel.types, - valueNode: babel.types.Node, -): string | null { - if (!t.isIdentifier(valueNode)) return null; - // it also can be App.displayName = NAME_CONSTANT - // so we try to find the NAME_CONSTANT in the scope - // if not found just return null; - const valueBind = path.scope.getBinding(valueNode.name); - if (valueBind?.constant && valueBind.path.isVariableDeclarator()) { - const init = valueBind.path.node.init; - if (t.isStringLiteral(init)) { - return init.value; - } - } - return null; -} - -/** - * Check if a path represents a React class component - */ -function isReactClassComponent( - node: babel.types.ClassExpression | babel.types.ClassDeclaration, - t: typeof babel.types, -) { - // Check if class extends React.Component, Component, PureComponent, or React.PureComponent - const superClass = node.superClass; - if (!superClass) return false; - - return ( - (t.isMemberExpression(superClass) && - t.isIdentifier(superClass.object, { name: 'React' }) && - (t.isIdentifier(superClass.property, { name: 'Component' }) || - t.isIdentifier(superClass.property, { name: 'PureComponent' }))) || - t.isIdentifier(superClass, { name: 'Component' }) || - t.isIdentifier(superClass, { name: 'PureComponent' }) - ); -} - -/** - * Creates a Babel plugin that collects displayName property - */ -export function createBabelDisplayNamePlugin(): PluginItem { - return ({ types: t }: { types: typeof babel.types }): PluginObj => ({ - /** - * Visit AST and collect displayName or componentName - */ - visitor: { - // function A () {} - FunctionDeclaration( - path: babel.NodePath, - ) { - const node = path.node; - if (isReactFunctionComponent(node, t)) { - const componentName = node.id.name; - const existingDisplayName = - findDisplayNameAssignmentInScope(path, t, componentName) || - componentName; - store.SELF_REACT_COMPONENTS.add(existingDisplayName); - } - }, - - // const App = ... - VariableDeclarator(path: babel.NodePath) { - const node = path.node; - // we only care about the case we have identifier on the LHS - if (!t.isIdentifier(node.id)) return; - if ( - // const App = () => {} or const App = function () {} - (t.isArrowFunctionExpression(node.init) || - t.isFunctionExpression(node.init)) && - node.id && - isReactFunctionComponent(node.init, t) - ) { - const componentName = node.id.name; - const existingDisplayName = - findDisplayNameAssignmentInScope(path, t, componentName) || - componentName; - store.SELF_REACT_COMPONENTS.add(existingDisplayName); - } else if ( - // const App = class extends React.Component {} - t.isClassExpression(node.init) && - isReactClassComponent(node.init, t) - ) { - const componentName = node.id.name; - const existingDisplayName = - findDisplayNameAssignmentInScope(path, t, componentName) || - findDisplayNamePropertyInClassExpression(path, t, componentName) || - componentName; - store.SELF_REACT_COMPONENTS.add(existingDisplayName); - } else if (t.isCallExpression(node.init)) { - const pathRight = path.get('init'); - const componentName = node.id.name; - pathRight.traverse({ - FunctionExpression( - innerPath: babel.NodePath, - ) { - const node = innerPath.node; - if (isReactFunctionComponent(node, t)) { - const existingDisplayName = - findDisplayNameAssignmentInScope( - innerPath, - t, - componentName, - ) || componentName; - store.SELF_REACT_COMPONENTS.add(existingDisplayName); - } - }, - ArrowFunctionExpression( - innerPath: babel.NodePath, - ) { - const node = innerPath.node; - if (isReactFunctionComponent(node, t)) { - const existingDisplayName = - findDisplayNameAssignmentInScope( - innerPath, - t, - componentName, - ) || componentName; - store.SELF_REACT_COMPONENTS.add(existingDisplayName); - } - }, - ClassExpression( - innerPath: babel.NodePath, - ) { - const node = innerPath.node; - if (isReactClassComponent(node, t)) { - const existingDisplayName = - findDisplayNameAssignmentInScope( - innerPath, - t, - componentName, - ) || - findDisplayNamePropertyInClassExpression( - innerPath, - t, - componentName, - ) || - componentName; - store.SELF_REACT_COMPONENTS.add(existingDisplayName); - } - }, - }); - } - }, - - // class App extends React.Component {} - ClassDeclaration(path: babel.NodePath) { - const node = path.node; - if (isReactClassComponent(node, t)) { - const componentName = node.id.name; - const existingDisplayName = - findDisplayNameAssignmentInScope(path, t, componentName) || - findDisplayNamePropertyInClassExpression(path, t, componentName) || - componentName; - store.SELF_REACT_COMPONENTS.add(existingDisplayName); - } - }, - }, - }); -} - -// TODO: use traverse -/** - * Check if a path represents a React component (returns JSX) - */ -function isReactFunctionComponent( - node: - | babel.types.ArrowFunctionExpression - | babel.types.FunctionExpression - | babel.types.FunctionDeclaration, - t: typeof babel.types, -) { - // For arrow functions with expression body (implicit return) - if (!t.isBlockStatement(node.body)) { - return ( - isJSXNode(node.body, t) || - (t.isParenthesizedExpression(node.body) && - isJSXNode(node.body.expression, t)) - ); - } - - return searchForJSXReturn(node.body, t); -} - -// Helper to check if a node is a JSX element or fragment -function isJSXNode(node: babel.types.Expression, t: typeof babel.types) { - return t.isJSXElement(node) || t.isJSXFragment(node); -} - -function searchForJSXReturn( - node: babel.types.Statement | babel.types.Expression, - t: typeof babel.types, -) { - if (!node) return false; - - // Handle return statements - if (t.isReturnStatement(node) && node.argument) { - return isJSXNode(node.argument, t); - } - - // Arrow function expressions in arguments or expressions - if ( - t.isArrowFunctionExpression(node) || - t.isFunctionExpression(node) || - t.isFunctionDeclaration(node) - ) { - return isReactFunctionComponent(node, t); - } - - // Array of statements (function body, block statement, etc.) - if (t.isBlockStatement(node)) { - return node.body.some((statement) => searchForJSXReturn(statement, t)); - } - - // If statement - if (t.isIfStatement(node)) { - return ( - searchForJSXReturn(node.consequent, t) || - (node.alternate && searchForJSXReturn(node.alternate, t)) - ); - } - - // Switch statement - if (t.isSwitchStatement(node)) { - return node.cases.some((switchCase) => - switchCase.consequent.some((statement) => - searchForJSXReturn(statement, t), - ), - ); - } - - // Try statement - if (t.isTryStatement(node)) { - return ( - searchForJSXReturn(node.block, t) || - (node.handler && searchForJSXReturn(node.handler.body, t)) || - (node.finalizer && searchForJSXReturn(node.finalizer, t)) - ); - } - - // For loop - if ( - t.isForStatement(node) || - t.isForInStatement(node) || - t.isForOfStatement(node) - ) { - return searchForJSXReturn(node.body, t); - } - - // While and do-while loops - if (t.isWhileStatement(node) || t.isDoWhileStatement(node)) { - return searchForJSXReturn(node.body, t); - } - - // Labeled statement - if (t.isLabeledStatement(node)) { - return searchForJSXReturn(node.body, t); - } - - // Ternary expression - if (t.isConditionalExpression(node)) { - return ( - searchForJSXReturn(node.consequent, t) || - searchForJSXReturn(node.alternate, t) - ); - } - - // Logical expressions (&&, ||) - if (t.isLogicalExpression(node)) { - return ( - searchForJSXReturn(node.left, t) || searchForJSXReturn(node.right, t) - ); - } - - if (t.isArrayExpression(node)) { - return node.elements.some((element) => { - if (t.isExpression(element)) { - return searchForJSXReturn(element, t); - } - }); - } - - return false; -} diff --git a/src/global.d.ts b/src/global.d.ts deleted file mode 100644 index d5491ee..0000000 --- a/src/global.d.ts +++ /dev/null @@ -1,21 +0,0 @@ -import type { WastedRenderFiberInfo } from './types/internal'; - -declare global { - interface Window { - __REACT_COMPONENTS__: string[]; - __VITE_REACT_MCP_TOOLS__: { - highlightReactComponent: ( - componentName: string, - options: { debugMode?: boolean }, - ) => void; - getComponentTree: (options: { - selfOnly: boolean; - debugMode?: boolean; - }) => any; - getComponentStates: (options: { debugMode?: boolean }) => any; - getUnnecessaryRenderedComponents: (options: { - debugMode?: boolean; - }) => WastedRenderFiberInfo[]; - }; - } -} diff --git a/src/index.ts b/src/index.ts deleted file mode 100644 index 7a8a103..0000000 --- a/src/index.ts +++ /dev/null @@ -1,318 +0,0 @@ -import fs from 'node:fs'; -import path from 'node:path'; -import { fileURLToPath } from 'node:url'; -import * as babel from '@babel/core'; -import { createFilter } from '@rollup/pluginutils'; -import { type ViteDevServer, normalizePath } from 'vite'; -import type { Plugin, ResolvedConfig } from 'vite'; -import { createBabelDisplayNamePlugin } from './babel_collect_name.js'; -import { initMcpServer, instrumentViteDevServer } from './mcp/index.js'; -import { store } from './shared/store.js'; -import type { CustomTool } from './shared/types.js'; - -function getViteReactMcpPath() { - const pluginPath = normalizePath( - path.dirname(fileURLToPath(import.meta.url)), - ); - return pluginPath; -} - -const viteReactMcpPath = getViteReactMcpPath(); -const viteReactMcpResourceSymbol = '?__vite-react-mcp-resource'; -const viteReactMcpImportee = 'virtual:vite-react-mcp'; -const resolvedViteReactMcp = `\0${viteReactMcpImportee}`; - -/** - * Read include/exclude patterns from tsconfig.json or other config files - * @param {string} configPath Path to config file - * @returns {Object} Object with include and exclude arrays - */ -function readProjectConfig(configPath: string): { - include: string[]; - exclude: string[]; -} { - try { - if (fs.existsSync(configPath)) { - const configContent = fs.readFileSync(configPath, 'utf8'); - const config = JSON.parse(configContent); - - // Extract include/exclude patterns - let include = config.include || []; - const exclude = config.exclude || []; - - // Convert to glob patterns if needed - include = include.map((pattern) => { - // Convert directory patterns to include all supported file types - if (!pattern.includes('*')) { - return pattern.endsWith('/') - ? `${pattern}**/*.{js,jsx,ts,tsx}` - : `${pattern}/**/*.{js,jsx,ts,tsx}`; - } - return pattern; - }); - - // Add node_modules to exclude if not already present - if (!exclude.some((pattern) => pattern.includes('node_modules'))) { - exclude.push('**/node_modules/**'); - } - - return { include, exclude }; - } - } catch (error: unknown) { - if (error instanceof Error) { - console.warn(`Failed to read config from ${configPath}:`, error.message); - } else { - console.warn(`Failed to read config from ${configPath}:`, error); - } - } - - // Default fallback patterns - return { - include: ['src/**/*.{js,jsx,ts,tsx}'], - exclude: ['**/node_modules/**'], - }; -} - -interface ReactMCPOptions { - customTools?: CustomTool[]; -} - -function ReactMCP(options: ReactMCPOptions = {}): Plugin { - const configPatterns = readProjectConfig('tsconfig.json'); - const filter = createFilter(configPatterns.include, configPatterns.exclude); - function transformBabelCollectName(code: string, id: string) { - if (!filter(id) || !/\.[jt]sx?$/.test(id)) { - return null; - } - - try { - let presetReact: boolean; - try { - // In ESM we can't use require, so we can check if the package is installed - // by importing it dynamically or checking if it exists in node_modules - const presetReactPath = path.resolve( - 'node_modules/@babel/preset-react', - ); - if (fs.existsSync(presetReactPath)) { - // If it exists, we'll let Babel use it through its name - presetReact = true; - } - } catch (_e) { - // Log warning but continue - the developer's project might have its own JSX handling - console.warn( - '\n[vite-plugin-display-name-suffix] Warning: @babel/preset-react is not installed.', - ); - console.warn( - 'If you encounter JSX parsing errors, install it with: npm install @babel/preset-react --save-dev\n', - ); - } - - // Configure Babel transformation - const babelOptions: babel.TransformOptions = { - babelrc: false, - configFile: false, - filename: id, - presets: [], - plugins: [ - // Plugin to add displayName to React components - [createBabelDisplayNamePlugin()], - ], - ast: true, - sourceType: 'module', - }; - - // Add preset-react if available - if (presetReact) { - babelOptions.presets.push([ - '@babel/preset-react', - { runtime: 'automatic' }, - ]); - } - - // Transform the code using Babel - const result = babel.transformSync(code, babelOptions); - - return { - code: result.code, - map: result.map, - }; - } catch (error) { - console.error(`Error transforming ${id}:`, error); - return null; - } - } - - let config: ResolvedConfig; - const customTools = options.customTools || []; - - return { - name: 'vite-react-mcp', - enforce: 'pre', - apply: 'serve', - - configureServer(viteDevServer: ViteDevServer) { - const mcpServer = initMcpServer(viteDevServer, customTools); - instrumentViteDevServer(viteDevServer, mcpServer); - setTimeout(() => { - console.info( - 'Vite React MCP server is running on port', - viteDevServer.config.server.port, - ); - }, 1000); - }, - - async resolveId(importee) { - if (importee === viteReactMcpImportee) { - return resolvedViteReactMcp; - } - if (importee.startsWith(`${viteReactMcpImportee}:`)) { - const resolved = importee.replace( - `${viteReactMcpImportee}:`, - `${viteReactMcpPath}/`, - ); - return `${resolved}${viteReactMcpResourceSymbol}`; - } - }, - - configResolved(resolvedConfig) { - config = resolvedConfig; - }, - - transform(code, id) { - transformBabelCollectName(code, id); - }, - - transformIndexHtml() { - // Convert the Set to an Array for serialization - const componentsArray = Array.from(store.SELF_REACT_COMPONENTS); - - // Create the script to register components to window - const registerComponentsScript = ` - window.__REACT_COMPONENTS__ = ${JSON.stringify(componentsArray)}; - `; - - // Generate custom tools registration script - const customToolsScript = generateCustomToolsScript(customTools, config); - - return [ - { - tag: 'script', - injectTo: 'head-prepend', - attrs: { type: 'text/javascript' }, - children: registerComponentsScript, - }, - { - tag: 'script', - injectTo: 'head-prepend', - attrs: { - type: 'module', - src: `${config.base || "/"}@id/${viteReactMcpImportee}:overlay.js`, - }, - }, - { - tag: 'script', - injectTo: 'head-prepend', - attrs: { type: 'module' }, - children: customToolsScript, - }, - ]; - }, - }; -} - -function toClientImportSpecifier( - filePathOrSpecifier: string, - resolvedConfig: ResolvedConfig, -): string { - const normalized = normalizePath(filePathOrSpecifier); - - if ( - normalized.startsWith('/@fs/') || - normalized.startsWith('http://') || - normalized.startsWith('https://') - ) { - return normalized; - } - - if (path.isAbsolute(normalized)) { - return `/@fs/${normalized}`; - } - - if (normalized.startsWith('./') || normalized.startsWith('../')) { - const absolutePath = normalizePath(path.resolve(resolvedConfig.root, normalized)); - return `/@fs/${absolutePath}`; - } - - if (normalized.startsWith('/')) { - return normalized; - } - - return `/@id/${normalized}`; -} - -function generateCustomToolsScript( - customTools: CustomTool[], - resolvedConfig: ResolvedConfig, -): string { - const toolRegistrations = customTools - .map((tool) => { - const safeToolName = JSON.stringify(tool.name); - if (typeof tool.clientFunction === 'function') { - const functionSource = tool.clientFunction.toString(); - if (functionSource.includes('[native code]')) { - return ` - console.error('[vite-react-mcp] Unable to register custom tool ${tool.name}: native functions are not supported as clientFunction'); - `; - } - return ` - registerTool(${safeToolName}, (${functionSource})); - `; - } - - const importSpecifier = toClientImportSpecifier( - tool.clientFunction, - resolvedConfig, - ); - const safeSpecifier = JSON.stringify(importSpecifier); - return ` - import(${safeSpecifier}) - .then((module) => { - const handler = module.default ?? module; - registerTool(${safeToolName}, handler); - }) - .catch((error) => { - console.error('[vite-react-mcp] Failed to load custom tool ${tool.name}:', error); - }); - `; - }) - .join('\n'); - - return ` - const registerTool = (name, handler) => { - const tryRegister = (attempt = 0) => { - const registry = window.__VITE_REACT_MCP_TOOLS__; - if (registry?.registerCustomTool) { - registry.registerCustomTool(name, handler); - return; - } - if (attempt >= 40) { - console.warn('[vite-react-mcp] Custom tool registration timed out for', name); - return; - } - setTimeout(() => tryRegister(attempt + 1), 50); - }; - - tryRegister(); - }; - - ${toolRegistrations} - `; -} - -export default ReactMCP; -export type { - CustomClientFunction, - CustomTool, - JsonValue, - ToolResultValue, -} from './shared/types.js'; diff --git a/src/mcp/index.ts b/src/mcp/index.ts deleted file mode 100644 index 48618b0..0000000 --- a/src/mcp/index.ts +++ /dev/null @@ -1,247 +0,0 @@ -import { Server } from '@modelcontextprotocol/sdk/server/index.js'; -import { SSEServerTransport } from '@modelcontextprotocol/sdk/server/sse.js'; -import { - CallToolRequestSchema, - type CallToolResult, - ListToolsRequestSchema, -} from '@modelcontextprotocol/sdk/types.js'; -import type { ViteDevServer } from 'vite'; -import { z } from 'zod'; -import zodToJsonSchema from 'zod-to-json-schema'; -import { getVersionString, waitForEvent } from '../shared/node_util.js'; -import { - GetComponentStatesSchema, - GetComponentTreeSchema, - GetUnnecessaryRerendersSchema, - HighlightComponentSchema, -} from './schema.js'; -import type { CustomTool, ToolResultValue } from '../shared/types.js'; - -const builtInTools = [ - { - name: 'highlight-component', - description: 'Highlight React component based on the component name.', - inputSchema: zodToJsonSchema(HighlightComponentSchema), - }, - { - name: 'get-component-tree', - description: - 'Get the React component tree of the current page in ASCII format.', - inputSchema: zodToJsonSchema(GetComponentTreeSchema), - }, - { - name: 'get-component-states', - description: - 'Get the React component props, states, and contexts in JSON structure format.', - inputSchema: zodToJsonSchema(GetComponentStatesSchema), - }, - { - name: 'get-unnecessary-rerenders', - description: - 'Get the wasted re-rendered components of the current page', - inputSchema: zodToJsonSchema(GetUnnecessaryRerendersSchema), - }, -] as const; - -type BuiltInToolName = (typeof builtInTools)[number]['name']; - -const builtInToolNames = new Set(builtInTools.map((tool) => tool.name)); - -function isBuiltInToolName(name: string): name is BuiltInToolName { - return builtInToolNames.has(name as BuiltInToolName); -} - -function formatToolResult(result: ToolResultValue): string { - if (typeof result === 'string') { - return result; - } - if (result === undefined) { - return 'Custom tool executed successfully.'; - } - try { - return JSON.stringify(result); - } catch (_error) { - return String(result); - } -} - -export function initMcpServer(viteDevServer: ViteDevServer, customTools: CustomTool[] = []): Server { - const server = new Server( - { - name: 'vite-react-mcp', - version: getVersionString(), - }, - { - capabilities: { - tools: {}, - }, - }, - ); - - server.setRequestHandler(ListToolsRequestSchema, async () => { - const dynamicTools = customTools.map(tool => ({ - name: tool.name, - description: tool.description, - inputSchema: zodToJsonSchema(tool.schema), - })); - - return { - tools: [...builtInTools, ...dynamicTools], - }; - }); - - server.setRequestHandler( - CallToolRequestSchema, - async (request): Promise => { - try { - if (isBuiltInToolName(request.params.name)) { - switch (request.params.name) { - case 'highlight-component': { - const args = HighlightComponentSchema.parse( - request.params.arguments, - ); - viteDevServer.ws.send({ - type: 'custom', - event: 'highlight-component', - data: JSON.stringify(args), - }); - - const response = await waitForEvent( - viteDevServer, - 'highlight-component-response', - ); - return { - content: [{ type: 'text', text: response.data }], - }; - } - - case 'get-component-tree': { - const args = GetComponentTreeSchema.parse(request.params.arguments); - viteDevServer.ws.send({ - type: 'custom', - event: 'get-component-tree', - data: JSON.stringify(args), - }); - - const response = await waitForEvent( - viteDevServer, - 'get-component-tree-response', - ); - return { - content: [{ type: 'text', text: response.data }], - }; - } - - case 'get-component-states': { - const args = GetComponentStatesSchema.parse( - request.params.arguments, - ); - viteDevServer.ws.send({ - type: 'custom', - event: 'get-component-states', - data: JSON.stringify(args), - }); - - const response = await waitForEvent( - viteDevServer, - 'get-component-states-response', - ); - return { - content: [{ type: 'text', text: response.data }], - }; - } - - case 'get-unnecessary-rerenders': { - const args = GetUnnecessaryRerendersSchema.parse( - request.params.arguments, - ); - - viteDevServer.ws.send({ - type: 'custom', - event: 'get-unnecessary-rerenders', - data: JSON.stringify(args), - }); - - const response = await waitForEvent( - viteDevServer, - 'get-unnecessary-rerenders-response', - ); - return { - content: [{ type: 'text', text: response.data }], - }; - } - default: - throw new Error(`Unknown tool: ${request.params.name}`); - } - } - - const customTool = customTools.find((tool) => tool.name === request.params.name); - if (customTool) { - const parsedArgs = customTool.schema.parse(request.params.arguments); - - if (typeof customTool.clientFunction !== 'function') { - throw new Error( - `Custom tool "${customTool.name}" must provide a function clientFunction for MCP execution.`, - ); - } - - const result = await customTool.clientFunction(parsedArgs); - return { - content: [{ type: 'text', text: formatToolResult(result) }], - }; - } - - throw new Error(`Unknown tool: ${request.params.name}`); - } catch (error) { - if (error instanceof z.ZodError) { - throw new Error(`Invalid input: ${JSON.stringify(error.errors)}`); - } - throw error; - } - }, - ); - - return server; -} - -export function instrumentViteDevServer( - viteDevServer: ViteDevServer, - mcpServer: Server, -) { - const transports = new Map(); - - viteDevServer.middlewares.use('/sse', async (_req, res) => { - const transport = new SSEServerTransport('/messages', res); - transports.set(transport.sessionId, transport); - res.on('close', () => { - transports.delete(transport.sessionId); - }); - await mcpServer.connect(transport); - }); - - viteDevServer.middlewares.use('/messages', async (req, res) => { - if (req.method !== 'POST') { - res.statusCode = 405; - res.end('Method Not Allowed'); - return; - } - - const query = new URLSearchParams(req.url?.split('?').pop() || ''); - const clientId = query.get('sessionId'); - - if (!clientId || typeof clientId !== 'string') { - res.statusCode = 400; - res.end('Bad Request'); - return; - } - - const transport = transports.get(clientId); - if (!transport) { - res.statusCode = 404; - res.end('Not Found'); - return; - } - - await transport.handlePostMessage(req, res); - }); -} diff --git a/src/mcp/schema.ts b/src/mcp/schema.ts deleted file mode 100644 index 6335a4f..0000000 --- a/src/mcp/schema.ts +++ /dev/null @@ -1,35 +0,0 @@ -import { z } from 'zod'; - -export const HighlightComponentSchema = z.object({ - componentName: z.string().describe('The name of the component to highlight'), -}); - -export const GetComponentTreeSchema = z.object({ - allComponents: z - .boolean() - .default(false) - .describe( - 'Whether to get tree for all components, instead of just your self-defined ones', - ), -}); - -export const GetComponentStatesSchema = z.object({ - componentName: z - .string() - .describe('The name of the component to get the states of'), -}); - -export const GetUnnecessaryRerendersSchema = z.object({ - timeframe: z - .number() - .optional() - .describe( - 'The timeframe to query wasted re-renders, in unit of seconds, e.g. 10 means last 10 seconds', - ), - allComponents: z - .boolean() - .default(false) - .describe( - 'Whether to query wasted re-renders for all components. Must be explicitly stated to set this to true', - ), -}); diff --git a/src/overlay.js b/src/overlay.js deleted file mode 100644 index e6f3716..0000000 --- a/src/overlay.js +++ /dev/null @@ -1,189 +0,0 @@ -import * as bippy from 'bippy'; -import { __VITE_REACT_MCP_TOOLS__, target } from './shared/const.js'; -import { fiberRoots, store } from './shared/store.js'; -import { highlightComponent } from './core/tools/component_highlighter.js'; -import { getComponentStates } from './core/tools/component_state_viewer.js'; -import { getComponentTree } from './core/tools/component_viewer.js'; -import { - collectUnnecessaryRender, - queryWastedRender, -} from './core/tools/track_wasted_render.js'; - -const init = () => { - if (Object.hasOwn(target, __VITE_REACT_MCP_TOOLS__)) { - return; - } - - Object.defineProperty(target, __VITE_REACT_MCP_TOOLS__, { - value: { - highlightComponent: highlightComponent, - getComponentTree: getComponentTree, - getComponentStates: getComponentStates, - getUnnecessaryRenderedComponents: queryWastedRender, - }, - writable: false, - configurable: true, - }); -}; - -init(); - -bippy.instrument({ - name: 'vite-react-mcp', - // TODO: frameify onCommitFiberRoot - // every onCommit should record an auto increment id - // and time. We then can query, between [start, end] - // what happened, in junction with states changes - // ALL of these are to answer the question: - // hey, this component was waste rendered, so what happened? - onCommitFiberRoot: (_renderId, root) => { - if (fiberRoots.has(_renderId)) { - fiberRoots.get(_renderId).add(root); - } else { - fiberRoots.set(_renderId, new Set([root])); - } - - bippy.traverseRenderedFibers(root.current || root, (fiber, phase) => { - if (phase === 'update') { - collectUnnecessaryRender(fiber); - } - }); - - store.currentCommitFrameId += 1; - }, -}); - -// Add dynamic tool registration capability -const customToolHandlers = new Map(); - -const registerCustomTool = (name, handler) => { - customToolHandlers.set(name, handler); - - // Set up WebSocket handler for this custom tool - if (import.meta.hot) { - import.meta.hot.on(`custom-tool-${name}`, async (data) => { - try { - const deserializedData = JSON.parse(data); - const result = await handler(deserializedData); - import.meta.hot.send(`custom-tool-${name}-response`, JSON.stringify(result)); - } catch (error) { - import.meta.hot.send(`custom-tool-${name}-response`, JSON.stringify({ error: error.message })); - } - }); - } -}; - -// Expose registration API -Object.defineProperty(target, __VITE_REACT_MCP_TOOLS__, { - value: { - highlightComponent: highlightComponent, - getComponentTree: getComponentTree, - getComponentStates: getComponentStates, - getUnnecessaryRenderedComponents: queryWastedRender, - registerCustomTool, - }, - writable: false, - configurable: true, -}); - -const setupMcpToolsHandler = () => { - if (import.meta.hot) { - import.meta.hot.on('highlight-component', (data) => { - let deserializedData; - try { - deserializedData = JSON.parse(data); - } catch (_error) { - throw new Error(`Data is not deserializable: ${data}`); - } - if (typeof deserializedData?.componentName !== 'string') { - throw new Error('Invalid args sent from ViteDevServer'); - } - - let response = 'Action failed'; - try { - const components = target.__VITE_REACT_MCP_TOOLS__.highlightComponent( - deserializedData.componentName, - ); - if (components.length > 0) { - response = `Found and highlighted ${components.length} components`; - } - } catch (_error) { - response = `Error: ${_error.message}`; - } - - import.meta.hot.send('highlight-component-response', response); - }); - - import.meta.hot.on('get-component-tree', (data) => { - let deserializedData; - try { - deserializedData = JSON.parse(data); - } catch (_error) { - throw new Error(`Args is not deserializable: ${data}`); - } - - const componentTreeRoot = - target.__VITE_REACT_MCP_TOOLS__.getComponentTree(deserializedData); - console.log('get-component-tree-response', componentTreeRoot); - import.meta.hot.send( - 'get-component-tree-response', - JSON.stringify(componentTreeRoot), - ); - }); - - import.meta.hot.on('get-component-states', (data) => { - let deserializedData; - try { - deserializedData = JSON.parse(data); - } catch (_error) { - throw new Error(`Data is not deserializable: ${data}`); - } - if (typeof deserializedData?.componentName !== 'string') { - console.debug('get-component-states ws handler', deserializedData); - throw new Error( - 'Invalid data sent from ViteDevServer: missing componentName', - ); - } - - const componentStatesResult = - target.__VITE_REACT_MCP_TOOLS__.getComponentStates( - deserializedData.componentName, - ); - import.meta.hot.send( - 'get-component-states-response', - JSON.stringify(componentStatesResult), - ); - }); - - import.meta.hot.on('get-unnecessary-rerenders', (data) => { - let deserializedData; - try { - deserializedData = JSON.parse(data); - } catch (_error) { - throw new Error(`Data is not deserializable: ${data}`); - } - - const wastedRenders = - target.__VITE_REACT_MCP_TOOLS__.getUnnecessaryRenderedComponents( - deserializedData.timeframe, - { - allComponents: !!deserializedData.allComponents, - debugMode: !!deserializedData.debugMode, - }, - ); - - let response; - - try { - response = JSON.stringify(wastedRenders); - } catch (_error) { - console.error('Error serializing wasted renders', _error); - response = JSON.stringify({ error: _error.message }); - } - - import.meta.hot.send('get-unnecessary-rerenders-response', response); - }); - } -}; - -setupMcpToolsHandler(); diff --git a/src/shared/node_util.ts b/src/shared/node_util.ts deleted file mode 100644 index 4fe3913..0000000 --- a/src/shared/node_util.ts +++ /dev/null @@ -1,58 +0,0 @@ -// This files must be run in node runtime - -import { readFileSync } from 'node:fs'; -import { dirname, resolve } from 'node:path'; -import { fileURLToPath } from 'node:url'; -import type { ViteDevServer } from 'vite'; - -const __dirname = dirname(fileURLToPath(import.meta.url)); - -let packageVersion: string | undefined; - -/** - * Read package.json and get the version string - * @param packageVersion - * @returns - */ -export function getVersionString() { - try { - packageVersion ??= JSON.parse( - readFileSync(resolve(__dirname, '..', '..', 'package.json')).toString(), - ).version; - } catch (error) { - console.error('Error reading package.json', error); - packageVersion = 'unknown'; - } - - return packageVersion; -} - -/** - * A Promise-based wrapper for Vite's WebSocket event system - * @param server The Vite dev server instance - * @param eventName The event name to listen for - * @param timeout The timeout for the promise - * @returns A Promise that resolves with the next event data - */ -export function waitForEvent( - server: ViteDevServer, - eventName: string, - timeout = 10000, -): Promise<{ data: T }> { - return new Promise<{ data: T }>((resolve, reject) => { - // Create a timeout to reject the promise if no response is received - const timeoutId = setTimeout(() => { - server.ws.off(eventName, handler); - reject(new Error(`Timeout waiting for event: ${eventName}`)); - }, timeout); - - const handler = (data: T) => { - clearTimeout(timeoutId); - server.ws.off(eventName, handler); - resolve({ data }); - }; - - // Register the event handler - server.ws.on(eventName, handler); - }); -} diff --git a/src/shared/types.ts b/src/shared/types.ts deleted file mode 100644 index b686549..0000000 --- a/src/shared/types.ts +++ /dev/null @@ -1,22 +0,0 @@ -import type { ZodTypeAny } from 'zod'; - -export type JsonValue = - | string - | number - | boolean - | null - | { [key: string]: JsonValue } - | JsonValue[]; - -export type ToolResultValue = string | JsonValue | undefined; - -export type CustomClientFunction = - | string - | ((args: unknown) => ToolResultValue | Promise); - -export interface CustomTool { - name: string; - description: string; - schema: ZodTypeAny; - clientFunction: CustomClientFunction; -} \ No newline at end of file
+ {value} +
Issues
+ The workspace behaves like a dense production app: list filtering, + issue selection, ownership metadata, source context, and activity all + stay visible without leaving the page. +
{body}
+ "We replaced scattered planning docs with one issue workspace. The + team can see the next decision, the owner, and the source context + without asking where anything lives." +
+ Use the Agentic React floating selector to capture this issue + management UI. The webpack runtime exposes component state, html + selectors, and source snippets from the same app surface. +
+ Plan work, manage ownership, and resolve project issues without + slowing down the team. +
{value}