diff --git a/.gitignore b/.gitignore index a23b52332962..9a1c952d10da 100644 --- a/.gitignore +++ b/.gitignore @@ -150,6 +150,9 @@ dist-docs/ #env /core-web/**/.env +.env +.env.* +!.env.example /**/**/*.css.map @@ -218,6 +221,9 @@ dist/ *.egg .venv/ +# Local scratch space +/core-web/scratch/ + # Spec-Kit working artifacts — process-only, kept local (see .specify/CUSTOMIZATIONS.md). # spec.md (and data-model.md / contracts/ when they carry verified contracts) stay tracked. specs/*/plan.md diff --git a/core-web/.gitignore b/core-web/.gitignore index 880fea3b227e..f97405822e91 100644 --- a/core-web/.gitignore +++ b/core-web/.gitignore @@ -1,5 +1,6 @@ .env -/libs/agentic-tools/src/generated vite.config.*.timestamp* -vitest.config.*.timestamp* \ No newline at end of file +vitest.config.*.timestamp* + +.angular diff --git a/core-web/apps/ai-evals/src/tools.ts b/core-web/apps/ai-evals/src/tools.ts index 915ee1a4e22e..3db6a12bf8b8 100644 --- a/core-web/apps/ai-evals/src/tools.ts +++ b/core-web/apps/ai-evals/src/tools.ts @@ -2,20 +2,13 @@ import { tool } from 'ai'; import { z } from 'zod/v4'; import { createApiAdapter } from '@dotcms/ai/adapter'; -import { createExecutor } from '@dotcms/ai/sandbox'; +import { createExecutor, formatSandboxResult } from '@dotcms/ai/sandbox'; import { getSpec } from '@dotcms/ai/spec'; -function sandboxResult( - result: Awaited['execute']>> -): string { - if (!result.success) return `Error: ${result.error?.name}: ${result.error?.message}`; - return typeof result.value === 'string' ? result.value : JSON.stringify(result.value, null, 2); -} - export function makeTools(dotcmsUrl: string, authToken: string) { // nosemgrep: detect-vercelai -- internal LLM eval harness (ai-evals), not shipped runtime code; Vercel AI SDK usage is intentional const searchTool = tool({ - description: `Explore the dotCMS REST API spec. Write JavaScript with the \`spec\` global (spec.paths keyed by path string). Return the data you need.`, + description: `Explore the dotCMS REST API spec. Write JavaScript with the \`spec\` global (\`spec.paths\` + \`spec.components.schemas\`, \`$ref\`-based). Schemas in requestBody/responses are usually \`$ref\`s — call \`resolveRef(schemaOrName, depth)\` to expand them. Return the data you need.`, inputSchema: z.object({ code: z .string() @@ -31,7 +24,7 @@ export function makeTools(dotcmsUrl: string, authToken: string) { variables: { spec }, sandbox: { timeout: 10000 } }); - return sandboxResult(result); + return formatSandboxResult(result); } }); @@ -54,7 +47,7 @@ export function makeTools(dotcmsUrl: string, authToken: string) { sandbox: { timeout: 15000 }, adapters: ['api'] }); - return sandboxResult(result); + return formatSandboxResult(result); } }); diff --git a/core-web/apps/dotcms-ui/proxy-dev.conf.mjs b/core-web/apps/dotcms-ui/proxy-dev.conf.mjs index 561e7eaf0e7b..0395f6c3d697 100644 --- a/core-web/apps/dotcms-ui/proxy-dev.conf.mjs +++ b/core-web/apps/dotcms-ui/proxy-dev.conf.mjs @@ -27,6 +27,51 @@ export default [ changeOrigin: true, logLevel: 'debug' }, + // 2. Embedded dotCMS page proxy (a11y portlet iframe). + // + // Lets the portlet iframe load live/edit-mode pages same-origin in dev. + // Use src="/dot-page/index?mode=EDIT_MODE" — the prefix is stripped so it + // hits the dotCMS page renderer (e.g. /index) on the BE. The sentinel prefix + // avoids colliding with the dev server's own Angular routes. + // + // DEV-ONLY WORKAROUND FOR A MISSING BACKEND CAPABILITY — do not delete this + // rule on its own; it is load-bearing (see below). + // -------------------------------------------------------------------------- + // Why it exists: the Accessibility Studio's side-by-side frames are not + // passive previews. The run screen reaches INTO each iframe's contentWindow to + // inject the axe violation-marker overlay and to sync scroll between the two + // frames (see A11yMarkerService + DotA11yRunComponent.frameWindow). That is + // same-origin-only by the browser's security model — cross-origin frames throw + // on contentWindow access, so the markers silently never render. + // + // In prod there is no problem: the portlet is served FROM the dotCMS origin, so + // the page is already same-origin and the iframe needs no prefix at all. This + // rule exists purely because `nx serve` puts the app on a different origin than + // the backend, and it papers over that split in the dev server instead of in + // the platform. + // + // What the real fix is (BACKEND): dotCMS should expose a first-class, + // same-origin endpoint for rendering a page for inspection — i.e. a supported + // resource under /api that returns the page render, so the Studio (and any + // future agent that needs to inspect a rendered page) can frame it directly + // with no origin games and no dev-server rewrite. Today no such endpoint + // exists, which is the actual gap. + // + // Until that lands this rule must stay, and it must stay in sync with + // DotA11yRunComponent.previewPathPrefix, which emits the `/dot-page` sentinel + // under isDevMode(). Removing one without the other 404s the preview frames in + // local dev. Both should be deleted together once the backend endpoint exists. + { + context: ['/dot-page'], + target, + secure: false, + changeOrigin: true, + logLevel: 'debug', + followRedirects: false, + pathRewrite: { + '^/dot-page': '' + } + }, // 2. Main API Proxy { context: [ diff --git a/core-web/apps/dotcms-ui/src/app/app.routes.ts b/core-web/apps/dotcms-ui/src/app/app.routes.ts index 0bbd483813ad..bd1ca719595b 100644 --- a/core-web/apps/dotcms-ui/src/app/app.routes.ts +++ b/core-web/apps/dotcms-ui/src/app/app.routes.ts @@ -185,6 +185,12 @@ const PORTLETS_ANGULAR: Route[] = [ (m) => m.dotPublishingQueueRoutes ) }, + { + path: 'agents', + data: { reuseRoute: false }, + loadChildren: () => + import('@dotcms/portlets/dot-agents/portlet').then((m) => m.dotAgentsRoutes) + }, { path: 'users', canActivate: [MenuGuardService], diff --git a/core-web/apps/mcp-server/.env.example b/core-web/apps/mcp-server/.env.example index 4c7e1a83038b..35a9621886e3 100644 --- a/core-web/apps/mcp-server/.env.example +++ b/core-web/apps/mcp-server/.env.example @@ -4,5 +4,5 @@ DOTCMS_URL= # API token for authentication AUTH_TOKEN= -# Sandbox execution timeout in milliseconds (default: 15000) -SANDBOX_TIMEOUT=15000 +# Sandbox execution timeout in milliseconds (default: 45000) +SANDBOX_TIMEOUT=45000 diff --git a/core-web/apps/mcp-server/CLAUDE.md b/core-web/apps/mcp-server/CLAUDE.md index f16056236cc7..b927216ac1e4 100644 --- a/core-web/apps/mcp-server/CLAUDE.md +++ b/core-web/apps/mcp-server/CLAUDE.md @@ -47,7 +47,7 @@ Configure the MCP server via the `env` block in your MCP client config: |---|---|---| | `DOTCMS_URL` | Yes | Base URL of the dotCMS instance | | `AUTH_TOKEN` | Yes | JWT Bearer token (generate in dotCMS → User Tools → API Tokens) | -| `SANDBOX_TIMEOUT` | No | Sandbox execution timeout in ms (default: `15000`) | +| `SANDBOX_TIMEOUT` | No | Sandbox execution timeout in ms (default: `45000`) | ## Architecture Overview @@ -60,7 +60,7 @@ This is a **Model Context Protocol (MCP) server** for dotCMS, built with [xmcp]( **Entry Point**: xmcp generates the entry point at build time (`dist/stdio.js`). **Build Pipeline**: -1. `generate-spec` — fetches the OpenAPI spec from a dotCMS instance, processes it into `src/generated/spec.json` (dereferences $refs, filters to relevant endpoints) +1. `generate-spec` — fetches the OpenAPI spec from a dotCMS instance, processes it into `src/generated/spec.json` (filters to relevant endpoints, keeps request/response `$ref`s, prunes `components.schemas` to just the schemas those endpoints reference) 2. `xmcp build` — bundles everything with rspack into `dist/` **Tool Layer** (`src/tools/`): @@ -86,7 +86,7 @@ This is a **Model Context Protocol (MCP) server** for dotCMS, built with [xmcp]( - Main thread executes the actual HTTP call with injected auth - Result is posted back to the sandbox -**Build-time Spec Processing**: `scripts/generate-spec.ts` fetches the OpenAPI spec from a URL (or reads a local file), dereferences it, filters to allowed endpoint prefixes, strips response schemas, and handles circular references. The developer must provide the spec URL or file path when running `generate-spec`. +**Build-time Spec Processing**: `scripts/generate-spec.ts` (thin CLI) + `scripts/spec-transform.ts` (pure, testable logic) fetch the OpenAPI spec from a URL (or read a local file), filter to allowed endpoint prefixes, replace only Jersey-autogenerated multipart bodies with a placeholder (curated multipart schemas are kept), keep request/response `$ref`s as-is, and prune `components.schemas` to just the schemas transitively referenced by the kept paths. Keeping `$ref`s (rather than dereferencing) dedupes shared schemas and is naturally acyclic. The developer must provide the spec URL or file path when running `generate-spec`. ### Type System @@ -99,7 +99,7 @@ All interfaces are in `src/lib/types.ts`: ### Search Tool **Purpose**: Explore the dotCMS REST API specification -**Sandbox globals**: `spec` (the dereferenced OpenAPI spec object) +**Sandbox globals**: `spec` (the filtered OpenAPI spec: `$ref`-based `paths` + `components.schemas`), plus helpers `resolveRef`, `pick`, `table`, `count`, `sum`, `first`. Use `resolveRef(schemaOrName, depth)` to expand `$ref`s at a bounded depth. **Read-only**: Yes — no side effects ### Execute Tool diff --git a/core-web/apps/mcp-server/README.md b/core-web/apps/mcp-server/README.md index be786282e8d7..0d95148c4102 100644 --- a/core-web/apps/mcp-server/README.md +++ b/core-web/apps/mcp-server/README.md @@ -89,7 +89,7 @@ Before setting up the MCP server, you need these environment variables to connec | ------------------ | -------- | ---------------------------------- | ------- | | `DOTCMS_URL` | ✅ | Your dotCMS instance URL | `https://demo.dotcms.com` | | `AUTH_TOKEN` | ✅ | API authentication token (created in [setup step](#create-a-dotcms-api-token)) | `your-api-token-here` | -| `SANDBOX_TIMEOUT` | ❌ | Sandbox execution timeout in ms (default: 15000) | `15000` | +| `SANDBOX_TIMEOUT` | ❌ | Sandbox execution timeout in ms (default: 45000) | `45000` | | `DEBUG` | ❌ | When set to any truthy value, emits diagnostic logs to stderr (e.g. context cache load events) | `1` | @@ -208,7 +208,7 @@ The dotCMS MCP Server provides tools that enable comprehensive content managemen **Purpose**: Explore the dotCMS REST API specification using JavaScript code that runs in an isolated sandbox. -The `spec` global contains the full dereferenced OpenAPI spec with `paths` object. +The `spec` global contains the filtered OpenAPI spec — `$ref`-based, with a `paths` object and a `components.schemas` map. Request/response `.schema` values are usually `$ref`s (e.g. `{ $ref: '#/components/schemas/PageView' }`); call `resolveRef(schemaOrName, depth)` to expand them at a bounded depth. Output is hard-capped (~25k chars), so return only what you need. ```javascript // List all available endpoint paths @@ -218,6 +218,9 @@ return Object.keys(spec.paths) return Object.entries(spec.paths) .filter(([path]) => path.includes('contenttype')) .map(([path, methods]) => ({ path, methods: Object.keys(methods) })) + +// Resolve a request-body schema one level deep +return resolveRef(spec.paths['/api/v1/contenttype'].post.requestBody.content['application/json'].schema, 1) ``` ### Execute @@ -459,7 +462,7 @@ libs/sdk/ai/ # Portable runtime primitives │ │ ├── bun-worker.ts # Bun Web Worker sandbox │ │ └── node-worker.ts # Node.js worker_threads sandbox │ └── generated/ -│ └── spec.json # Committed processed OpenAPI spec +│ └── spec.json # Build-generated, git-ignored (lives in libs/sdk/ai) └── project.json # Nx project configuration ``` @@ -479,7 +482,7 @@ libs/sdk/ai/ # Portable runtime primitives - Adapter pattern bridges sandbox ↔ main thread for API calls **Build-time Spec Processing**: The OpenAPI spec is pre-processed at build time: -- `generate-spec` target dereferences `$ref` pointers and filters to relevant endpoints +- `generate-spec` target filters to relevant endpoints, keeps request/response `$ref`s, and prunes `components.schemas` to just the referenced schemas - Output is a compact JSON embedded in the bundle - Reduces runtime overhead and MCP response size @@ -501,7 +504,7 @@ pnpm nx test mcp-server # Run tests in watch mode pnpm nx test mcp-server --watch -# Refresh the OpenAPI spec (run when dotCMS API changes, then commit spec.json) +# Refresh the OpenAPI spec (git-ignored; regenerated by build/serve/test via dependsOn) # Defaults to https://demo.dotcms.com/api/openapi.json pnpm nx run sdk-ai:generate-spec ``` diff --git a/core-web/apps/mcp-server/src/lib/assets-transfer-io.spec.ts b/core-web/apps/mcp-server/src/lib/assets-transfer-io.spec.ts new file mode 100644 index 000000000000..8b26d4cf7db5 --- /dev/null +++ b/core-web/apps/mcp-server/src/lib/assets-transfer-io.spec.ts @@ -0,0 +1,462 @@ +import { mkdtemp, readFile, rm, writeFile } from 'node:fs/promises'; +import { tmpdir } from 'node:os'; +import { join } from 'node:path'; + +import type { DotCMSRuntime, RequestOptions } from '@dotcms/ai/runtime'; + +import { downloadAssets, uploadAssets } from './assets-transfer'; + +/** + * Exercises `uploadAssets` / `downloadAssets` end to end against a fake runtime and a real + * temp directory. + * + * The sibling spec covers only `splitIncludePatterns` and `includeMatcher`, which left the + * transfer behaviour itself — the manifest, the per-file failure isolation, and the whole + * publish-then-verify path — with no coverage at all, in the file carrying the most error + * handling in the server. + * + * A real temp dir rather than a mocked `fs`: these functions walk directories, read bytes and + * write files, and mocking that surface would mostly test the mock. + */ + +const SITE = '//demo.dotcms.com/application/themes/travel'; + +interface FakeOptions { + /** Per-path handler. Return a value, or throw to simulate a failure. */ + onRequest?: (options: RequestOptions) => unknown; +} + +function fakeRuntime(options?: FakeOptions) { + const calls: RequestOptions[] = []; + let uploadCount = 0; + + const request = jest.fn(async (opts: RequestOptions) => { + calls.push(opts); + + const custom = options?.onRequest?.(opts); + if (custom !== undefined) { + return custom; + } + + // Default happy path: every upload gets an identifier, everything reads back live. + if (opts.path === '/api/v2/assets/publish' || opts.path === '/api/v2/assets/save') { + uploadCount += 1; + + return { entity: { identifier: `id-${uploadCount}` } }; + } + if (opts.path?.startsWith('/api/v1/content/')) { + return { entity: { live: true } }; + } + if (opts.path === '/api/v1/workflow/actions/default/fire/PUBLISH') { + return { entity: {} }; + } + + return {}; + }); + + return { runtime: { request } as unknown as DotCMSRuntime, calls }; +} + +/** The binary envelope `downloadAssetBytes` expects back from an asset read. */ +function binary(text: string) { + const base64 = Buffer.from(text, 'utf8').toString('base64'); + + return { + __dotcmsBinary: true as const, + contentType: 'text/css', + base64, + byteLength: Buffer.byteLength(text) + }; +} + +/** Count how many requests hit a given path. */ +function callsTo(calls: RequestOptions[], path: string): number { + return calls.filter((call) => call.path === path).length; +} + +describe('uploadAssets', () => { + let src: string; + + beforeEach(async () => { + src = await mkdtemp(join(tmpdir(), 'dot-upload-')); + await writeFile(join(src, 'style.css'), '.a{color:red}'); + await writeFile(join(src, 'main.vtl'), '#set($x = 1)'); + }); + + afterEach(async () => { + await rm(src, { recursive: true, force: true }); + }); + + it('uploads every file and reports them in the manifest', async () => { + const { runtime, calls } = fakeRuntime(); + + const manifest = await uploadAssets({ + dotcms: runtime, + src, + dest: SITE, + publish: true, + verify: false + }); + + expect(manifest.count).toBe(2); + expect(manifest.failures).toEqual([]); + expect(manifest.files.map((file) => file.path).sort()).toEqual(['main.vtl', 'style.css']); + expect(callsTo(calls, '/api/v2/assets/publish')).toBe(2); + }); + + it('records a per-file failure without abandoning the rest of the batch', async () => { + let seen = 0; + const { runtime } = fakeRuntime({ + onRequest: (opts) => { + if (opts.path === '/api/v2/assets/publish') { + seen += 1; + if (seen === 1) { + throw new Error('HTTP 400 Bad Request'); + } + } + + return undefined; + } + }); + + const manifest = await uploadAssets({ + dotcms: runtime, + src, + dest: SITE, + publish: true, + verify: false + }); + + expect(manifest.failures).toHaveLength(1); + expect(manifest.count).toBe(1); + }); + + it('rejects a destination that is not host-qualified', async () => { + const { runtime } = fakeRuntime(); + + await expect( + uploadAssets({ + dotcms: runtime, + src, + dest: '/application/themes/travel', + publish: true, + verify: false + }) + ).rejects.toThrow(/host-qualified/i); + }); + + it('distinguishes a bad include pattern from an empty source dir', async () => { + const { runtime } = fakeRuntime(); + + const manifest = await uploadAssets({ + dotcms: runtime, + src, + dest: SITE, + include: '*.png', + publish: true, + verify: false + }); + + expect(manifest.count).toBe(0); + // "matched 0 of 2" rather than "no files found" — a mistyped glob must not read as + // silent success in an unattended run. + expect(manifest.warnings.join(' ')).toMatch(/matched 0 of 2/); + }); + + describe('publish + verify', () => { + it('does not let a failed liveness read destroy the report of completed writes', async () => { + // The headline case. Every file uploaded and published; one liveness GET then + // fails. Before this was guarded the throw escaped uploadAssets entirely and the + // caller was told the operation failed — so its next move was to re-upload + // everything that had in fact already landed. + const { runtime } = fakeRuntime({ + onRequest: (opts) => { + if (opts.path?.startsWith('/api/v1/content/')) { + throw new Error('HTTP 500 Server Error'); + } + + return undefined; + } + }); + + const manifest = await uploadAssets({ + dotcms: runtime, + src, + dest: SITE, + publish: true, + verify: true + }); + + expect(manifest.count).toBe(2); + expect(manifest.files).toHaveLength(2); + expect(manifest.warnings.join(' ')).toMatch(/[Cc]ould not check/); + // An unreadable status is NOT a confirmed failure, so it must not be reported as + // not-live. + expect(manifest.notLive).toEqual([]); + }); + + it('reports files whose identifier never parsed instead of silently skipping them', async () => { + // With an unexpected publish envelope every identifier is undefined. Filtering + // them out silently produced "2 files, 0 failures, 0 notLive" — indistinguishable + // from a fully verified publish when nothing at all was verified. + const { runtime, calls } = fakeRuntime({ + onRequest: (opts) => + opts.path === '/api/v2/assets/publish' ? { entity: {} } : undefined + }); + + const manifest = await uploadAssets({ + dotcms: runtime, + src, + dest: SITE, + publish: true, + verify: true + }); + + expect(manifest.count).toBe(2); + expect(manifest.warnings.join(' ')).toMatch(/could NOT be verified/i); + // Nothing was checkable, so no liveness read should have been attempted. + expect(calls.filter((call) => call.path?.startsWith('/api/v1/content/'))).toHaveLength( + 0 + ); + }); + + it('re-fires PUBLISH for a file that is not live, then re-checks it', async () => { + let liveChecks = 0; + const { runtime, calls } = fakeRuntime({ + onRequest: (opts) => { + if (opts.path?.startsWith('/api/v1/content/')) { + liveChecks += 1; + + // Not live on the first pass, live once re-published. + return { entity: { live: liveChecks > 2 } }; + } + + return undefined; + } + }); + + const manifest = await uploadAssets({ + dotcms: runtime, + src, + dest: SITE, + publish: true, + verify: true + }); + + expect(callsTo(calls, '/api/v1/workflow/actions/default/fire/PUBLISH')).toBe(2); + expect(manifest.notLive).toEqual([]); + }); + + it('keeps going when one re-publish fails, and says which one', async () => { + let fires = 0; + const { runtime } = fakeRuntime({ + onRequest: (opts) => { + if (opts.path?.startsWith('/api/v1/content/')) { + return { entity: { live: false } }; + } + if (opts.path === '/api/v1/workflow/actions/default/fire/PUBLISH') { + fires += 1; + if (fires === 1) { + throw new Error('HTTP 400 locked by another workflow'); + } + } + + return undefined; + } + }); + + const manifest = await uploadAssets({ + dotcms: runtime, + src, + dest: SITE, + publish: true, + verify: true + }); + + // The first fire failed; the rest were still attempted rather than abandoned. + expect(fires).toBeGreaterThan(1); + expect(manifest.warnings.join(' ')).toMatch(/Re-publish failed/); + expect(manifest.notLive).toHaveLength(2); + }); + + it('skips verification entirely when publish is off', async () => { + const { runtime, calls } = fakeRuntime(); + + await uploadAssets({ + dotcms: runtime, + src, + dest: SITE, + publish: false, + verify: true + }); + + expect(callsTo(calls, '/api/v2/assets/save')).toBe(2); + expect(calls.filter((call) => call.path?.startsWith('/api/v1/content/'))).toHaveLength( + 0 + ); + }); + }); + + it('warns when a 0-byte file had to be uploaded as a newline', async () => { + // The remote asset then DIFFERS from the source, which is invisible to the caller + // unless it is said out loud. + await writeFile(join(src, 'empty.vtl'), ''); + const { runtime } = fakeRuntime({ + onRequest: (opts) => { + if (opts.path === '/api/v2/assets/publish') { + const data = (opts.formData as { file?: { data?: string } })?.file?.data; + if (data === '') { + throw new Error('HTTP 400 empty body rejected'); + } + } + + return undefined; + } + }); + + const manifest = await uploadAssets({ + dotcms: runtime, + src, + dest: SITE, + publish: true, + verify: false + }); + + const empty = manifest.files.find((file) => file.path === 'empty.vtl'); + expect(manifest.warnings.join(' ')).toMatch(/0 bytes[\s\S]*single newline/); + expect(empty?.bytes).toBe(1); + }); +}); + +describe('downloadAssets', () => { + let dest: string; + + beforeEach(async () => { + dest = await mkdtemp(join(tmpdir(), 'dot-download-')); + }); + + afterEach(async () => { + await rm(dest, { recursive: true, force: true }); + }); + + /** A `_search` page followed by the per-asset byte reads. */ + function searchRuntime(assets: Array<{ identifier: string; path: string }>, bytes = 'body') { + return fakeRuntime({ + onRequest: (opts) => { + if (opts.path === '/api/content/_search') { + return { entity: { jsonObjectView: { contentlets: assets } } }; + } + if (opts.path?.startsWith('/api/v2/assets/')) { + return binary(bytes); + } + + return undefined; + } + }); + } + + it('writes each enumerated asset to disk and reports it', async () => { + const { runtime } = searchRuntime([ + { identifier: 'a1', path: '//demo.dotcms.com/application/themes/travel/style.css' } + ]); + + const manifest = await downloadAssets({ + dotcms: runtime, + path: '//demo.dotcms.com/application/themes/travel', + dest, + recursive: true, + overwrite: 'overwrite' + }); + + expect(manifest.count).toBe(1); + expect(await readFile(join(dest, 'style.css'), 'utf8')).toBe('body'); + }); + + it('explains a zero-match instead of reporting an empty success', async () => { + const { runtime } = searchRuntime([]); + + const manifest = await downloadAssets({ + dotcms: runtime, + path: '//demo.dotcms.com/application/themes/nope', + dest, + recursive: true, + overwrite: 'overwrite' + }); + + expect(manifest.count).toBe(0); + expect(manifest.warnings.length).toBeGreaterThan(0); + }); + + it('records a per-asset failure rather than aborting the batch', async () => { + const { runtime } = fakeRuntime({ + onRequest: (opts) => { + if (opts.path === '/api/content/_search') { + return { + entity: { + jsonObjectView: { + contentlets: [ + { identifier: 'a1', path: '//demo.dotcms.com/a/one.css' }, + { identifier: 'a2', path: '//demo.dotcms.com/a/two.css' } + ] + } + } + }; + } + if (opts.path === '/api/v2/assets/a1') { + throw new Error('HTTP 404 Not Found'); + } + if (opts.path?.startsWith('/api/v2/assets/')) { + return binary('ok'); + } + + return undefined; + } + }); + + const manifest = await downloadAssets({ + dotcms: runtime, + path: '//demo.dotcms.com/a', + dest, + recursive: true, + overwrite: 'overwrite' + }); + + expect(manifest.failures).toHaveLength(1); + expect(manifest.count).toBe(1); + }); + + it('stops paginating when a page adds nothing new', async () => { + // The termination guard. If the backend ignores `offset` every page comes back full + // of the same identifiers: the short-page exit never fires, `seen` de-dupes so the + // result stops growing, and the loop would spin forever issuing identical POSTs. + const page = Array.from({ length: 500 }, (_, i) => ({ + identifier: `id-${i}`, + path: `//demo.dotcms.com/a/file-${i}.css` + })); + const { runtime, calls } = fakeRuntime({ + onRequest: (opts) => { + if (opts.path === '/api/content/_search') { + // Always the SAME page, regardless of offset. + return { entity: { jsonObjectView: { contentlets: page } } }; + } + if (opts.path?.startsWith('/api/v2/assets/')) { + return binary('x'); + } + + return undefined; + } + }); + + const manifest = await downloadAssets({ + dotcms: runtime, + path: '//demo.dotcms.com/a', + dest, + recursive: true, + overwrite: 'overwrite' + }); + + // Two searches: the first yields 500 new ids, the second adds none and breaks. + expect(callsTo(calls, '/api/content/_search')).toBe(2); + expect(manifest.count).toBe(500); + }); +}); diff --git a/core-web/apps/mcp-server/src/lib/assets-transfer.spec.ts b/core-web/apps/mcp-server/src/lib/assets-transfer.spec.ts new file mode 100644 index 000000000000..aa66b47831fd --- /dev/null +++ b/core-web/apps/mcp-server/src/lib/assets-transfer.spec.ts @@ -0,0 +1,143 @@ +import { includeMatcher, splitIncludePatterns } from './assets-transfer'; + +describe('splitIncludePatterns', () => { + it('splits comma-separated patterns and trims them', () => { + expect(splitIncludePatterns('*.vtl, *.scss')).toEqual(['*.vtl', '*.scss']); + }); + + it('does NOT split on a comma inside a brace group', () => { + expect(splitIncludePatterns('*.{png,webp,jpg}')).toEqual(['*.{png,webp,jpg}']); + }); + + it('splits around a brace group but keeps the group intact', () => { + expect(splitIncludePatterns('*.{png,jpg},*.vtl')).toEqual(['*.{png,jpg}', '*.vtl']); + }); + + it('drops empty entries and returns [] for undefined', () => { + expect(splitIncludePatterns('*.png,,')).toEqual(['*.png']); + expect(splitIncludePatterns(undefined)).toEqual([]); + }); +}); + +describe('includeMatcher', () => { + it('matches everything when no include is given', () => { + const m = includeMatcher(); + expect(m('a.png')).toBe(true); + expect(m('deep/nested/a.vtl')).toBe(true); + }); + + // The three repro cases from the bug report — files live directly in the source dir. + describe('bug report repro (top-level files)', () => { + it('brace expansion matches top-level files (was: 0 matched)', () => { + const m = includeMatcher('*.{png,webp,jpg}'); + expect(m('amazon-logo.png')).toBe(true); + expect(m('book1.webp')).toBe(true); + expect(m('cover.jpg')).toBe(true); + expect(m('notes.txt')).toBe(false); + }); + + it('** globstar matches a top-level file too (was: 0 matched)', () => { + const m = includeMatcher('**/*.png'); + expect(m('amazon-logo.png')).toBe(true); // no subdirectory — must still match + expect(m('img/hero.png')).toBe(true); + expect(m('a/b/c/deep.png')).toBe(true); + expect(m('a/b/c/deep.webp')).toBe(false); + }); + + it('plain top-level glob still works', () => { + const m = includeMatcher('*.png'); + expect(m('amazon-logo.png')).toBe(true); + expect(m('book1.webp')).toBe(false); + }); + }); + + describe('single-star does not cross directories', () => { + it('"*.png" (no slash) matches a basename anywhere in the tree', () => { + const m = includeMatcher('*.png'); + expect(m('a.png')).toBe(true); + expect(m('deep/dir/a.png')).toBe(true); // basename match, unanchored + }); + + it('an anchored "img/*.png" only matches that one directory level', () => { + const m = includeMatcher('img/*.png'); + expect(m('img/a.png')).toBe(true); + expect(m('img/sub/a.png')).toBe(false); // * does not cross / + expect(m('other/a.png')).toBe(false); + }); + }); + + describe('** globstar depth', () => { + it('"img/**/*.png" matches zero or more intermediate dirs', () => { + const m = includeMatcher('img/**/*.png'); + expect(m('img/a.png')).toBe(true); // zero intermediate dirs + expect(m('img/sub/a.png')).toBe(true); + expect(m('img/a/b/c.png')).toBe(true); + expect(m('other/a.png')).toBe(false); + }); + }); + + describe('? single char', () => { + it('matches exactly one non-slash char', () => { + const m = includeMatcher('file?.txt'); + expect(m('file1.txt')).toBe(true); + expect(m('fileA.txt')).toBe(true); + expect(m('file.txt')).toBe(false); + expect(m('file12.txt')).toBe(false); + }); + }); + + describe('literals are escaped', () => { + it('a dot in the pattern is literal, not "any char"', () => { + const m = includeMatcher('*.png'); + expect(m('axpng')).toBe(false); // the "." must be a real dot + expect(m('a.png')).toBe(true); + }); + + it('multiple patterns OR together', () => { + const m = includeMatcher('*.vtl,*.scss'); + expect(m('theme.vtl')).toBe(true); + expect(m('styles.scss')).toBe(true); + expect(m('image.png')).toBe(false); + }); + }); + + it('is case-insensitive', () => { + const m = includeMatcher('*.PNG'); + expect(m('photo.png')).toBe(true); + }); +}); + +describe('includeMatcher — trailing globstar', () => { + // `dir/**` is the common glob idiom, but it used to compile to `^dir(?:.*/)?$`, which + // matches only the bare string `dir` and no file path under it. Anyone writing it hit the + // "include pattern matched 0 of N files — check the glob syntax" warning while their + // syntax was perfectly reasonable. + it('matches files directly under the directory', () => { + const m = includeMatcher('themes/**'); + expect(m('themes/style.css')).toBe(true); + }); + + it('matches files nested deeper under the directory', () => { + const m = includeMatcher('themes/**'); + expect(m('themes/travel/css/style.css')).toBe(true); + }); + + it('does not match a sibling directory that shares the prefix', () => { + const m = includeMatcher('themes/**'); + expect(m('themes-backup/style.css')).toBe(false); + expect(m('other/style.css')).toBe(false); + }); + + it('still supports a leading globstar with a pattern after it', () => { + const m = includeMatcher('**/*.png'); + expect(m('logo.png')).toBe(true); + expect(m('themes/img/logo.png')).toBe(true); + expect(m('themes/style.css')).toBe(false); + }); + + it('treats a bare globstar as match-everything', () => { + const m = includeMatcher('**'); + expect(m('a.css')).toBe(true); + expect(m('a/b/c.vtl')).toBe(true); + }); +}); diff --git a/core-web/apps/mcp-server/src/lib/assets-transfer.ts b/core-web/apps/mcp-server/src/lib/assets-transfer.ts index 18e993eba74a..76b72c56e299 100644 --- a/core-web/apps/mcp-server/src/lib/assets-transfer.ts +++ b/core-web/apps/mcp-server/src/lib/assets-transfer.ts @@ -2,11 +2,10 @@ import { constants } from 'node:fs'; import { access, mkdir, readdir, readFile, stat, writeFile } from 'node:fs/promises'; import { basename, extname, isAbsolute, join, posix, relative, resolve, sep } from 'node:path'; -import { createRuntime, isBinaryResponseEnvelope } from '@dotcms/ai/runtime'; +import { type DotCMSRuntime, isBinaryResponseEnvelope } from '@dotcms/ai/runtime'; +import { isContentLive } from './page-common'; import { errorMessage } from './runtime'; - -type DotCMSRuntime = ReturnType; type OverwriteMode = 'skip' | 'overwrite' | 'error'; export interface AssetManifestFile { @@ -37,6 +36,16 @@ interface LocalFile { } const SEARCH_LIMIT = 500; + +/** + * Bounds on a single enumeration. `/application` on a large site can hold tens of + * thousands of assets, and every one enumerated is then downloaded one at a time with an + * open file handle — so an unbounded walk is both an unbounded MCP call and unbounded load + * on the instance. A realistic theme is 100–500 files, so these are far above any genuine + * use while still being a ceiling. Hitting either is reported, never silent. + */ +const MAX_ENUMERATED_ASSETS = 5_000; +const MAX_SEARCH_PAGES = Math.ceil(MAX_ENUMERATED_ASSETS / SEARCH_LIMIT); const MIME_BY_EXT: Record = { '.css': 'text/css', '.eot': 'application/vnd.ms-fontobject', @@ -100,13 +109,21 @@ export async function downloadAssets(options: { }) ); } else { - const assets = await enumerateAssets( + const { assets, truncated } = await enumerateAssets( options.dotcms, input.path, options.recursive, options.include ); + if (truncated) { + warnings.push( + `Enumeration stopped at the ${MAX_ENUMERATED_ASSETS}-asset cap — this folder ` + + `holds more than that, so the download is INCOMPLETE. Narrow it with a ` + + `subfolder path or an \`include\` pattern and run again.` + ); + } + if (assets.length === 0) { warnings.push(zeroMatchWarning(options.path, input)); } @@ -184,41 +201,69 @@ export async function uploadAssets(options: { ); } - const localFiles = await collectLocalFiles(src, options.include); + const { files: localFiles, totalSeen } = await collectLocalFiles(src, options.include); const files: AssetManifestFile[] = []; const failures: AssetManifestFailure[] = []; const skipped: AssetManifestSkipped[] = []; const warnings: string[] = []; if (localFiles.length === 0) { - warnings.push( - options.include - ? `No files under "${src}" matched the include filter "${options.include}".` - : `No files found under "${src}".` - ); + if (options.include && totalSeen > 0) { + // The source dir is NOT empty — the include pattern is the problem. Say so distinctly so + // this never reads as "nothing to upload" in an unattended run. The matcher supports + // *, ? , ** globstar, and {a,b,c} brace expansion, all relative to `src`. + warnings.push( + `Include pattern "${options.include}" matched 0 of ${totalSeen} file(s) under ` + + `"${src}" — check the glob syntax. Patterns are relative to the source dir and ` + + `support *, ?, ** (globstar), and {png,webp,jpg} brace expansion (e.g. ` + + `"*.{png,webp,jpg}" or "**/*.png"). Nothing was uploaded.` + ); + } else { + warnings.push(`No files found under "${src}".`); + } } for (const file of localFiles) { try { - if (file.bytes === 0) { - skipped.push({ path: file.rel, reason: 'empty file' }); - continue; - } - + // Every file in src lands in dotCMS as-is, 0-byte content included. We do not + // skip on empty content: an empty file that exists locally must exist remotely, + // otherwise the container can't assemble CONTENT bodies (the empty-skip was the + // root cause of a missing postloop.vtl). `skipped[]` is reserved for real skips + // (e.g. a glob matching nothing), never for empty content. const uploaded = await uploadOneAsset( options.dotcms, file, `${dest.siteQualified}/${file.rel}`, options.publish ); - files.push(uploaded); + files.push(uploaded.file); + if (uploaded.warning) { + warnings.push(uploaded.warning); + } } catch (error) { failures.push({ path: file.rel, error: errorMessage(error) }); } } - const notLive = - options.publish && options.verify ? await verifyLive(options.dotcms, files) : []; + // Belt AND braces: `verifyLive` guards every await internally, but this call is the last + // thing standing between a completed set of writes and the manifest that reports them. + // If verification ever fails in a way it did not anticipate, the uploads still happened + // and the model still needs to be told exactly what landed — so the worst case here is a + // manifest with a warning, never a thrown error that erases the whole report. + let notLive: AssetManifestFile[] = []; + if (options.publish && options.verify) { + try { + const verified = await verifyLive(options.dotcms, files); + notLive = verified.notLive; + warnings.push(...verified.warnings); + } catch (error) { + warnings.push( + `Upload succeeded but live-verification could not complete: ` + + `${errorMessage(error)}. The ${files.length} file(s) listed below WERE ` + + `uploaded — do not re-upload them; check their published state directly.` + ); + } + } return sortManifest({ src, @@ -233,17 +278,24 @@ export async function uploadAssets(options: { }); } +/** Enumerated assets plus whether a cap stopped the walk early (see MAX_ENUMERATED_ASSETS). */ +interface EnumerateResult { + assets: AssetContentlet[]; + truncated: boolean; +} + async function enumerateAssets( dotcms: DotCMSRuntime, folder: string, recursive: boolean, include?: string -): Promise { +): Promise { const matches = includeMatcher(include); const assets: AssetContentlet[] = []; const seen = new Set(); - for (let offset = 0; ; offset += SEARCH_LIMIT) { + for (let page_ = 0; page_ < MAX_SEARCH_PAGES; page_++) { + const offset = page_ * SEARCH_LIMIT; const response = await dotcms.request({ method: 'POST', path: '/api/content/_search', @@ -255,6 +307,7 @@ async function enumerateAssets( } }); const page = extractContentlets(response); + const seenBefore = seen.size; for (const asset of page) { if (!asset.identifier || !asset.path || seen.has(asset.identifier)) { @@ -273,9 +326,23 @@ async function enumerateAssets( if (page.length < SEARCH_LIMIT) { break; } + + // Termination guard, NOT an optimisation. If the backend ignores or clamps `offset`, + // every page comes back full of the same identifiers: `page.length < SEARCH_LIMIT` + // never fires, `seen` de-dupes so `assets` stops growing, and the loop spins forever + // issuing identical POSTs — an MCP call that never returns while the instance takes + // sustained load. A page that adds nothing new means we are not advancing, whatever + // the backend thinks it is doing. + if (seen.size === seenBefore) { + break; + } + + if (assets.length >= MAX_ENUMERATED_ASSETS) { + return { assets, truncated: true }; + } } - return assets; + return { assets, truncated: false }; } /** Fetch an asset's raw bytes — by identifier (`/api/v2/assets/{id}`) or by path query. */ @@ -297,80 +364,201 @@ async function downloadAssetBytes( return bytes; } +/** An uploaded file, plus any caveat the caller must surface (see the 0-byte fallback). */ +interface UploadOneResult { + file: AssetManifestFile; + warning?: string; +} + async function uploadOneAsset( dotcms: DotCMSRuntime, file: LocalFile, destPath: string, publish: boolean -): Promise { +): Promise { const bytes = await readFile(file.abs); - const response = (await dotcms.request({ - method: 'PUT', - path: publish ? '/api/v2/assets/publish' : '/api/v2/assets/save', - formData: { - path: destPath, - file: { - name: basename(file.rel), - type: mimeFor(file.rel), - data: bytes.toString('base64') + + const put = (data: Buffer) => + dotcms.request({ + method: 'PUT', + path: publish ? '/api/v2/assets/publish' : '/api/v2/assets/save', + formData: { + path: destPath, + file: { + name: basename(file.rel), + type: mimeFor(file.rel), + data: data.toString('base64') + } } + }) as Promise<{ entity?: { identifier?: string } }>; + + let response: { entity?: { identifier?: string } }; + let warning: string | undefined; + try { + // Upload the real content, 0-byte included. + response = await put(bytes); + } catch (error) { + // Fallback: if (and only if) dotCMS rejects a 0-byte body, retry with a single + // newline so the file still lands instead of being dropped. The demo postloop.vtl + // indicates 0-byte is accepted, so this path is expected to be unused. + if (bytes.byteLength === 0) { + response = await put(Buffer.from('\n')); + // The remote asset now DIFFERS from the source: 1 byte where the source has 0. + // Reporting a clean success would leave the caller unable to see that, and for + // an empty VTL or CSS partial the difference is invisible until something + // downstream behaves oddly. Say it plainly and report the bytes actually sent. + warning = + `"${file.rel}" is 0 bytes and dotCMS rejected an empty body, so it was ` + + `uploaded as a single newline (1 byte) instead. The remote file does NOT ` + + `match the source exactly.`; + } else { + throw error; } - })) as { entity?: { identifier?: string } }; + } return { - path: file.rel, - bytes: file.bytes, - identifier: response.entity?.identifier + file: { + path: file.rel, + bytes: warning ? 1 : file.bytes, + identifier: response.entity?.identifier + }, + warning }; } +/** What a verification pass learned. It can only ever ADD to a manifest, never replace it. */ +interface VerifyLiveResult { + notLive: AssetManifestFile[]; + warnings: string[]; +} + +/** + * Re-check that every uploaded asset is actually live, re-firing PUBLISH for any that + * aren't (up to 3 rounds), then confirming the last round's fires. + * + * Every await in here is individually guarded, for one reason: this is a READ-ONLY + * verification of writes that have ALREADY COMMITTED. A throw escaping this function would + * propagate out of `uploadAssets` and discard `files[]`, `failures[]` and `warnings[]` — so + * a 120-file theme that uploaded and published perfectly, then hit one flaky liveness GET, + * would be reported to the model as a failure. Its next move is to re-upload all 120. + * + * Verification can therefore only ever downgrade the manifest (add to `notLive`/`warnings`), + * never replace it with an exception. + */ async function verifyLive( dotcms: DotCMSRuntime, files: AssetManifestFile[] -): Promise { +): Promise { + const warnings: string[] = []; + + // A file with no identifier CANNOT be checked, which is not the same as it being fine. + // Silently filtering these out meant that if the publish envelope ever stopped matching + // the expected shape, every identifier would be undefined, every file would drop out + // here, the round loop would never run, and the manifest would report + // `count: 120, notLive: [], warnings: []` — indistinguishable from a fully verified + // publish when in fact nothing at all was verified. + const unverifiable = files.filter((file) => !file.identifier); + if (unverifiable.length > 0) { + warnings.push( + `${unverifiable.length} of ${files.length} uploaded file(s) returned no identifier, ` + + `so their live status could NOT be verified: ` + + `${unverifiable.map((file) => file.path).join(', ')}. ` + + `They may or may not be published — check them directly.` + ); + } + let pending = files.filter((file) => file.identifier); for (let round = 0; round < 3 && pending.length > 0; round++) { - const notLive: AssetManifestFile[] = []; - - for (const file of pending) { - if (!(await isLive(dotcms, file.identifier as string))) { - notLive.push(file); - } - } + const notLive = await collectNotLive(dotcms, pending, warnings); if (notLive.length === 0) { - return []; + return { notLive: [], warnings }; } + // Sequential, not concurrent: these fire workflow actions against content dotCMS is + // concurrently versioning and indexing. The per-item catch is the fix that matters — + // previously the first bad fire (locked by another workflow, or a token without + // PUBLISH on that folder) threw, so every remaining asset was never even attempted + // and nothing recorded which ones those were. for (const file of notLive) { - await dotcms.request({ - method: 'PUT', - path: '/api/v1/workflow/actions/default/fire/PUBLISH', - body: { contentlet: { identifier: file.identifier } } - }); + try { + await dotcms.request({ + method: 'PUT', + path: '/api/v1/workflow/actions/default/fire/PUBLISH', + body: { contentlet: { identifier: file.identifier } } + }); + } catch (error) { + warnings.push( + `Re-publish failed for "${file.path}" (${file.identifier}): ` + + `${errorMessage(error)}. Remaining files were still attempted.` + ); + } } pending = notLive; } - return pending; + // The PUBLISH fired in the final round has not been verified yet — without this pass an + // asset that only goes live on its last re-fire would be reported as notLive despite + // having published successfully (a false negative in the transfer manifest). + return { notLive: await collectNotLive(dotcms, pending, warnings), warnings }; } -async function isLive(dotcms: DotCMSRuntime, identifier: string): Promise { - const response = (await dotcms.request({ - path: `/api/v1/content/${encodeURIComponent(identifier)}`, - query: { depth: 0 } - })) as { entity?: { live?: boolean; contentlets?: Array<{ live?: boolean }> } }; - const entity = response.entity; - const contentlet = entity?.contentlets?.[0] || entity; +/** + * Which of `files` are not live yet. + * + * Pure GETs on distinct identifiers with no interdependence, so they run concurrently via + * `allSettled` — a 120-file theme was previously up to 3 rounds of 120 sequential round + * trips plus a final 120, and only the last round's results mattered. + * + * `allSettled` (not `all`) for the same reason the whole function is guarded: `all` fails + * fast and discards its settled siblings, and here those siblings ARE the answer. A single + * rejected read must not decide the fate of the other 119. A file whose check failed is + * treated as NOT-not-live — it is left out of `notLive` and reported as a warning, so an + * unreadable status never masquerades as a confirmed failure. + */ +async function collectNotLive( + dotcms: DotCMSRuntime, + files: AssetManifestFile[], + warnings: string[] +): Promise { + const results = await Promise.allSettled( + files.map((file) => isContentLive(dotcms, file.identifier as string)) + ); + + const notLive: AssetManifestFile[] = []; + results.forEach((result, index) => { + const file = files[index]; + if (result.status === 'rejected') { + warnings.push( + `Could not check whether "${file.path}" (${file.identifier}) is live: ` + + `${errorMessage(result.reason)}.` + ); + + return; + } + if (!result.value) { + notLive.push(file); + } + }); - return contentlet?.live === true; + return notLive; } -async function collectLocalFiles(src: string, include?: string): Promise { +/** + * Walk `src` and return the files matching `include` (all files when no `include`), plus + * `totalSeen` — the count of files present regardless of the filter. `totalSeen` lets the caller + * distinguish "the source dir is empty" from "your include pattern matched none of N real files", + * so a mistyped glob is reported as a syntax problem instead of silent success. + */ +async function collectLocalFiles( + src: string, + include?: string +): Promise<{ files: LocalFile[]; totalSeen: number }> { const matches = includeMatcher(include); const files: LocalFile[] = []; + let totalSeen = 0; async function walk(dir: string) { for (const entry of await readdir(dir, { withFileTypes: true })) { @@ -385,6 +573,8 @@ async function collectLocalFiles(src: string, include?: string): Promise a.rel.localeCompare(b.rel)); + return { files: files.sort((a, b) => a.rel.localeCompare(b.rel)), totalSeen }; } function normalizeDotCMSPath(input: string): { siteQualified?: string; path: string } { @@ -506,13 +696,51 @@ function safeJoin(root: string, rel: string): string { return output; } -function includeMatcher(include?: string): (rel: string) => boolean { - const patterns = include - ?.split(',') - .map((pattern) => pattern.trim()) - .filter(Boolean); +/** + * Split an `include` string into its comma-separated patterns — but NOT on commas inside a brace + * group, so `*.{png,webp,jpg}` stays one pattern while `*.vtl,*.scss` is two. Exported for tests. + */ +export function splitIncludePatterns(include?: string): string[] { + if (!include) { + return []; + } + const patterns: string[] = []; + let current = ''; + let braceDepth = 0; + for (const ch of include) { + if (ch === '{') { + braceDepth++; + current += ch; + } else if (ch === '}') { + braceDepth = Math.max(0, braceDepth - 1); + current += ch; + } else if (ch === ',' && braceDepth === 0) { + patterns.push(current); + current = ''; + } else { + current += ch; + } + } + patterns.push(current); + return patterns.map((p) => p.trim()).filter(Boolean); +} + +/** + * Build a matcher over relative POSIX paths from a comma-separated `include` string. A file matches + * if it matches ANY pattern. No `include` → matches everything. Exported for tests. + * + * Supports the glob features callers reasonably assume from a standard glob: + * - `*` matches any run of chars WITHIN a path segment (does not cross `/`) + * - `**` matches across segments, including zero (so a leading globstar also matches a top-level file) + * - `?` matches a single non-`/` char + * - `{png,webp,jpg}` brace expansion (alternation) + * A pattern with no `/` matches the file's basename anywhere in the tree; a pattern with a `/` is + * anchored at the root of `src`. + */ +export function includeMatcher(include?: string): (rel: string) => boolean { + const patterns = splitIncludePatterns(include); - if (!patterns?.length) { + if (!patterns.length) { return () => true; } @@ -522,11 +750,120 @@ function includeMatcher(include?: string): (rel: string) => boolean { return (rel: string) => regexes.some((re) => re.test(rel)); } +/** + * Compile a single glob pattern to a RegExp with a single left-to-right character scan. + * + * A scanner (rather than chained `.replace()` passes) is used deliberately: it has no ordering + * hazard between `**` and `*`, needs no placeholder sentinels, and each glob token emits its regex + * exactly once. The old chained-replace version turned every `*` into `[^/]*`, so a `**` + `/*.png` + * pattern compiled to "exactly one subdirectory" and silently matched nothing for top-level files. + * + * Tokens: + * - `**` (with an optional adjacent `/`) crosses directory boundaries, matching zero or more + * segments, so a leading `**` also matches a top-level file. + * - `*` matches any run of chars within one segment (never crosses `/`). + * - `?` matches a single non-`/` char. + * - `{png,webp,jpg}` expands to alternation `(?:png|webp|jpg)` (nested wildcards are honored). + * A pattern containing `/` is anchored at the root of `src`; otherwise it matches a basename + * anywhere in the tree. + */ function globToRegExp(pattern: string): RegExp { const normalized = pattern.split(sep).join(posix.sep); - const source = normalized.replace(/[|\\{}()[\]^$+?.]/g, '\\$&').replace(/\*/g, '[^/]*'); + const anchored = normalized.includes('/'); + const source = compileGlob(normalized, 0, normalized.length); + return new RegExp(`${anchored ? '^' : '(^|/)'}${source}$`, 'i'); +} - return new RegExp(`${normalized.includes('/') ? '^' : '(^|/)'}${source}$`, 'i'); +/** Regex-escape a single literal character. */ +function escapeRegexChar(ch: string): string { + return /[|\\{}()[\]^$+.*?]/.test(ch) ? `\\${ch}` : ch; +} + +/** + * Translate the glob in `input[start..end)` to a regex source string. Recurses into brace groups so + * `{a*,b}` honors the wildcard inside each alternative. + */ +function compileGlob(input: string, start: number, end: number): string { + let out = ''; + let i = start; + + while (i < end) { + const ch = input[i]; + + if (ch === '*') { + if (input[i + 1] === '*') { + // `**` crosses directory boundaries. Consume it plus one adjacent `/` (leading or + // trailing) and emit an optional "any number of full segments" fragment. + i += 2; + const trailing = i >= end; + if (input[i] === '/') { + i++; + } else if (!trailing && out.endsWith('/')) { + out = out.slice(0, -1); + } + + if (trailing) { + // A globstar with nothing after it — `themes/**` — means "everything + // below here", so it has to be able to match a final FILENAME segment. + // The general fragment below cannot: it only ever ends at a `/`, so + // `themes/**` compiled to `^themes(?:.*/)?$` and matched nothing but the + // bare string `themes`. Since `dir/**` is the common idiom, users writing + // it hit the "matched 0 of N files, check the glob syntax" warning while + // their syntax was perfectly reasonable. + out += '.*'; + } else { + out += '(?:.*/)?'; + } + } else { + out += '[^/]*'; + i++; + } + } else if (ch === '?') { + out += '[^/]'; + i++; + } else if (ch === '{') { + const close = input.indexOf('}', i); + if (close === -1 || close >= end) { + // Unbalanced brace: treat the `{` literally rather than throwing. + out += '\\{'; + i++; + } else { + const alternatives = splitTopLevelCommas(input.slice(i + 1, close)).map((alt) => + compileGlob(alt, 0, alt.length) + ); + out += `(?:${alternatives.join('|')})`; + i = close + 1; + } + } else { + out += escapeRegexChar(ch); + i++; + } + } + + return out; +} + +/** Split on commas that are NOT inside a nested brace group (for brace-group alternatives). */ +function splitTopLevelCommas(group: string): string[] { + const parts: string[] = []; + let current = ''; + let depth = 0; + for (const ch of group) { + if (ch === '{') { + depth++; + current += ch; + } else if (ch === '}') { + depth = Math.max(0, depth - 1); + current += ch; + } else if (ch === ',' && depth === 0) { + parts.push(current); + current = ''; + } else { + current += ch; + } + } + parts.push(current); + return parts; } function extractContentlets(response: unknown): AssetContentlet[] { diff --git a/core-web/apps/mcp-server/src/lib/page-common.ts b/core-web/apps/mcp-server/src/lib/page-common.ts new file mode 100644 index 000000000000..de9ba21f706e --- /dev/null +++ b/core-web/apps/mcp-server/src/lib/page-common.ts @@ -0,0 +1,62 @@ +import type { DotCMSRuntime } from '@dotcms/ai/runtime'; +import type { DotCMSColumnContainer } from '@dotcms/types'; + +/** + * Response shapes and probes shared by more than one page tool. + * + * Small on purpose: these are the pieces that were previously written out twice and had + * already started to drift, not a general dumping ground for page types. + */ + +/** + * One row of a page layout, as `/api/v1/page/json` and `/api/v1/page/render` return it. + * + * Hoisted here because `page-verify` and `page-place-content` declared byte-identical + * copies, which meant two tools parsing the SAME response through two shapes nobody kept in + * step. + * + * The innermost element is DERIVED from `DotCMSColumnContainer` — the SDK's own page-asset + * contract — so a change to the container shape upstream fails here at compile time instead + * of drifting silently. `import type` erases at build, so this costs nothing at runtime. + * + * The surrounding optionality stays hand-written, and that is deliberate: the canonical + * `DotPageAssetLayoutRow` declares `columns` (and every field below it) as REQUIRED, which + * would be a lie about a response this code has not validated. Everything here is optional + * because the payload is unproven at compile time. `historyUUIDs` is picked off for the same + * reason — these tools never read it, and requiring it would assert a field that may be + * absent. `@dotcms/dotcms-models` also ships a `DotLayoutRow`, but it is the editor-side + * model (fully required, and its barrel pulls Angular into the transitive graph), so it is + * the wrong contract for an MCP server parsing a REST response. + */ +export interface LayoutRow { + columns?: Array<{ containers?: Array> }>; +} + +/** The `/api/v1/content/{id}` envelope, which nests the contentlet one of two ways. */ +interface ContentLiveResponse { + entity?: { live?: boolean; contentlets?: Array<{ live?: boolean }> }; +} + +/** + * Whether a contentlet is published. + * + * THROWS on a failed read, and that is the contract callers depend on rather than an + * oversight. The two previous copies disagreed here: one swallowed every error into `false`, + * which conflates "this is not live" with "we could not find out" — and for the transfer + * manifest those are opposite conclusions, since a read failure reported as not-live sends + * the caller off to re-publish assets that were already fine. `assets-transfer` needs the + * distinction, so the shared primitive is the honest one and the caller that wants a + * best-effort answer catches for itself. + */ +export async function isContentLive(dotcms: DotCMSRuntime, identifier: string): Promise { + const response = (await dotcms.request({ + path: `/api/v1/content/${encodeURIComponent(identifier)}`, + query: { depth: 0 } + })) as ContentLiveResponse; + + const entity = response.entity; + // A fire response wraps the contentlet under `contentlets[0]`; a plain read does not. + const contentlet = entity?.contentlets?.[0] || entity; + + return contentlet?.live === true; +} diff --git a/core-web/apps/mcp-server/src/lib/page-create.spec.ts b/core-web/apps/mcp-server/src/lib/page-create.spec.ts new file mode 100644 index 000000000000..f3420b91aad5 --- /dev/null +++ b/core-web/apps/mcp-server/src/lib/page-create.spec.ts @@ -0,0 +1,571 @@ +import type { DotCMSRuntime, RequestOptions } from '@dotcms/ai/runtime'; + +import { createPage } from './page-create'; +import { splitUrlPath } from './page-path'; + +describe('splitUrlPath', () => { + it('splits a path with an explicit leaf into folder + leaf', () => { + expect(splitUrlPath('/books/index')).toEqual({ + folder: '/books', + url: 'index', + fullPath: '/books/index' + }); + }); + + it('treats a non-index leaf as the page url', () => { + expect(splitUrlPath('/store/checkout')).toEqual({ + folder: '/store', + url: 'checkout', + fullPath: '/store/checkout' + }); + }); + + it('treats a trailing slash as a folder whose page is index', () => { + expect(splitUrlPath('/about-us/')).toEqual({ + folder: '/about-us', + url: 'index', + fullPath: '/about-us/index' + }); + }); + + it('treats a single bare segment as a leaf under root', () => { + expect(splitUrlPath('/contact')).toEqual({ + folder: '/', + url: 'contact', + fullPath: '/contact' + }); + }); + + it('maps the site root to the root index page', () => { + expect(splitUrlPath('/')).toEqual({ folder: '/', url: 'index', fullPath: '/index' }); + }); + + it('handles a deep nested folder path', () => { + expect(splitUrlPath('/store/books/scifi/index')).toEqual({ + folder: '/store/books/scifi', + url: 'index', + fullPath: '/store/books/scifi/index' + }); + }); + + it('rejects a path that does not start with /', () => { + expect(() => splitUrlPath('books/index')).toThrow('must start with'); + }); + + it('percent-decodes segments so the folder is named literally', () => { + expect(splitUrlPath('/my%20books/index')).toEqual({ + folder: '/my books', + url: 'index', + fullPath: '/my books/index' + }); + }); + + it('collapses ./.. segments', () => { + expect(splitUrlPath('/store/../books/index')).toEqual({ + folder: '/books', + url: 'index', + fullPath: '/books/index' + }); + }); + + it('strips a query string and fragment', () => { + expect(splitUrlPath('/books/checkout?draft=1#top')).toEqual({ + folder: '/books', + url: 'checkout', + fullPath: '/books/checkout' + }); + }); +}); + +describe('createPage', () => { + interface FakeField { + variable: string; + required?: boolean; + fixed?: boolean; + defaultValue?: unknown; + } + interface FakeContentType { + id: string; + variable: string; + baseType: string; + fields?: FakeField[]; + } + + // A standard page type with no user-added required fields — the default for tests that + // don't care about content-type resolution. + const HTMLPAGE_ASSET: FakeContentType = { + id: 'ct-htmlpageasset', + variable: 'htmlpageasset', + baseType: 'HTMLPAGE', + fields: [ + { variable: 'title', required: true }, + { variable: 'url', required: true }, + { variable: 'template', required: true } + ] + }; + + // A request recorder standing in for the runtime, so we can assert ordering and payloads. + // `contentTypes` seeds both loadContext() and the /contenttype/id/{} lookup. + // The default site every test's `site: 'demo.dotcms.com'` resolves against. `contentHost` on the + // fire body is expected to be this identifier UUID, never the hostname. + const DEMO_SITE = { + identifier: 'site-uuid-1', + hostname: 'demo.dotcms.com', + isDefault: true, + archived: false + }; + + function fakeRuntime(handlers: { + onCreateFolders?: (body: unknown) => unknown; + onFire?: (body: unknown, query: unknown) => unknown; + onLive?: () => unknown; + contentTypes?: FakeContentType[]; + sites?: Array<{ + identifier: string; + hostname: string; + isDefault: boolean; + archived: boolean; + }>; + }) { + const contentTypes = handlers.contentTypes ?? [HTMLPAGE_ASSET]; + const sites = handlers.sites ?? [DEMO_SITE]; + const calls: Array<{ method?: string; path: string; body?: unknown; query?: unknown }> = []; + const request = jest.fn(async (options: RequestOptions) => { + calls.push({ + method: options.method, + path: options.path, + body: options.body, + query: options.query + }); + if (options.path.startsWith('/api/v1/contenttype/id/')) { + const idOrVar = decodeURIComponent( + options.path.replace('/api/v1/contenttype/id/', '') + ); + const ct = contentTypes.find((c) => c.id === idOrVar || c.variable === idOrVar); + return { entity: ct ?? {} }; + } + if (options.path.startsWith('/api/v1/folder/createfolders/')) { + return handlers.onCreateFolders?.(options.body) ?? { entity: [] }; + } + if (options.path.includes('/fire/')) { + return handlers.onFire?.(options.body, options.query) ?? { entity: {} }; + } + if (options.path.startsWith('/api/v1/content/')) { + return handlers.onLive?.() ?? { entity: { live: true } }; + } + return {}; + }); + const loadContext = jest.fn(async () => ({ + contentTypes: contentTypes.map((c) => ({ + id: c.id, + name: c.variable, + variable: c.variable, + baseType: c.baseType + })), + sites, + languages: [], + currentUser: null + })); + return { runtime: { request, loadContext } as unknown as DotCMSRuntime, calls }; + } + + it('creates the parent folder BEFORE firing the page (trap #1)', async () => { + const { runtime, calls } = fakeRuntime({ + onCreateFolders: () => ({ entity: [{ path: '/books', identifier: 'folder-123' }] }), + onFire: () => ({ entity: { identifier: 'page-1', inode: 'inode-1', live: true } }), + onLive: () => ({ entity: { live: true } }) + }); + + await createPage({ + dotcms: runtime, + site: 'demo.dotcms.com', + urlPath: '/books/index', + title: 'Books', + template: 'tmpl-1' + }); + + const folderCall = calls.findIndex((c) => c.path.includes('createfolders')); + const fireCall = calls.findIndex((c) => c.path.includes('/fire/')); + expect(folderCall).toBeGreaterThanOrEqual(0); + expect(fireCall).toBeGreaterThan(folderCall); + }); + + it('fires the page with the leaf url and the created folder id, not the full path', async () => { + let firedBody: { contentlet: Record } | undefined; + const { runtime } = fakeRuntime({ + onCreateFolders: () => ({ entity: [{ path: '/books', identifier: 'folder-123' }] }), + onFire: (body) => { + firedBody = body as { contentlet: Record }; + return { entity: { identifier: 'page-1', live: true } }; + } + }); + + await createPage({ + dotcms: runtime, + site: 'demo.dotcms.com', + urlPath: '/books/index', + title: 'Books', + template: 'tmpl-1' + }); + + // The url must be the bare leaf — never "/books/index", which dotCMS would collapse. + const contentlet = firedBody?.contentlet ?? {}; + expect(contentlet.url).toBe('index'); + expect(contentlet.hostFolder).toBe('folder-123'); + expect(contentlet.contentType).toBe('htmlpageasset'); + expect(contentlet.template).toBe('tmpl-1'); + }); + + it('fires with indexPolicy=WAIT_FOR', async () => { + let firedQuery: Record | undefined; + const { runtime } = fakeRuntime({ + onCreateFolders: () => ({ entity: [{ path: '/x', identifier: 'f' }] }), + onFire: (_body, query) => { + firedQuery = query as Record; + return { entity: { identifier: 'p', live: true } }; + } + }); + + await createPage({ + dotcms: runtime, + site: 'demo.dotcms.com', + urlPath: '/x/index', + title: 'X', + template: 't' + }); + + expect(firedQuery?.indexPolicy).toBe('WAIT_FOR'); + }); + + it('skips folder creation for a root page', async () => { + const { runtime, calls } = fakeRuntime({ + onFire: () => ({ entity: { identifier: 'home', live: true } }) + }); + + const manifest = await createPage({ + dotcms: runtime, + site: 'demo.dotcms.com', + urlPath: '/index', + title: 'Home', + template: 't' + }); + + expect(calls.some((c) => c.path.includes('createfolders'))).toBe(false); + expect(manifest.folder).toBe('/'); + expect(manifest.url).toBe('index'); + }); + + it('fires a ROOT page with contentHost = site UUID, not the hostname (null-host trap)', async () => { + let firedBody: { contentlet: Record } | undefined; + const { runtime, calls } = fakeRuntime({ + onFire: (body) => { + firedBody = body as { contentlet: Record }; + return { entity: { identifier: 'home', live: true } }; + } + }); + + await createPage({ + dotcms: runtime, + site: 'demo.dotcms.com', // a hostname — must be resolved to the UUID + urlPath: '/', + title: 'Home', + template: 't' + }); + + const contentlet = firedBody?.contentlet ?? {}; + // The regression: contentHost must be the resolved identifier, never the raw hostname — + // a hostname here NPEs the fire for a root page (no folder to anchor the host). + expect(contentlet.contentHost).toBe('site-uuid-1'); + expect(contentlet.contentHost).not.toBe('demo.dotcms.com'); + // Trap #3: a root page has no folder, so hostFolder must fall back to the SITE id (a + // concrete HOST_OR_FOLDER value), never undefined — otherwise the fire 500s with + // "Host.getIdentifier() ... host is null". + expect(contentlet.hostFolder).toBe('site-uuid-1'); + // Root page still creates no folder. + expect(calls.some((c) => c.path.includes('createfolders'))).toBe(false); + }); + + it('fires a ROOT page on a NON-default site with hostFolder = site id (Host.getIdentifier NPE fix)', async () => { + let firedBody: { contentlet: Record } | undefined; + const NON_DEFAULT_SITE = { + identifier: 'site-uuid-2', + hostname: 'other.example.com', + isDefault: false, + archived: false + }; + const { runtime, calls } = fakeRuntime({ + sites: [DEMO_SITE, NON_DEFAULT_SITE], + onFire: (body) => { + firedBody = body as { contentlet: Record }; + return { entity: { identifier: 'home', live: true } }; + } + }); + + await createPage({ + dotcms: runtime, + site: 'other.example.com', + urlPath: '/index', + title: 'Home', + template: 't' + }); + + const contentlet = firedBody?.contentlet ?? {}; + // Both location keys point at the non-default site id — this is exactly the manual recovery + // that worked ("explicit host + hostFolder = site id"). + expect(contentlet.contentHost).toBe('site-uuid-2'); + expect(contentlet.hostFolder).toBe('site-uuid-2'); + expect(contentlet.hostFolder).not.toBeUndefined(); + expect(calls.some((c) => c.path.includes('createfolders'))).toBe(false); + }); + + it('resolves a hostname to its identifier UUID for a nested page too', async () => { + let firedBody: { contentlet: Record } | undefined; + const { runtime, calls } = fakeRuntime({ + onCreateFolders: () => ({ entity: [{ path: '/books', identifier: 'folder-123' }] }), + onFire: (body) => { + firedBody = body as { contentlet: Record }; + return { entity: { identifier: 'page-1', live: true } }; + } + }); + + await createPage({ + dotcms: runtime, + site: 'demo.dotcms.com', + urlPath: '/books/index', + title: 'Books', + template: 't' + }); + + const contentlet = firedBody?.contentlet ?? {}; + expect(contentlet.contentHost).toBe('site-uuid-1'); + // A nested page anchors on its created folder id (which carries the host) — NOT the + // site-id fallback that only a root page uses. + expect(contentlet.hostFolder).toBe('folder-123'); + // The createfolders path is called with the resolved UUID, not the hostname. + const folderCall = calls.find((c) => c.path.includes('createfolders')); + expect(folderCall?.path).toContain('site-uuid-1'); + }); + + it('accepts a site passed as its identifier UUID directly', async () => { + let firedBody: { contentlet: Record } | undefined; + const { runtime } = fakeRuntime({ + onFire: (body) => { + firedBody = body as { contentlet: Record }; + return { entity: { identifier: 'home', live: true } }; + } + }); + + await createPage({ + dotcms: runtime, + site: 'site-uuid-1', // already a UUID + urlPath: '/', + title: 'Home', + template: 't' + }); + + expect((firedBody?.contentlet ?? {}).contentHost).toBe('site-uuid-1'); + }); + + it('throws a clear error when the site is neither a known hostname nor identifier', async () => { + const { runtime, calls } = fakeRuntime({ + onFire: () => ({ entity: { identifier: 'home', live: true } }) + }); + + await expect( + createPage({ + dotcms: runtime, + site: 'unknown.example.com', + urlPath: '/', + title: 'Home', + template: 't' + }) + ).rejects.toThrow(/not found.*hostname.*identifier/i); + + // Fails before any side effect. + expect(calls.some((c) => c.path.includes('/fire/'))).toBe(false); + }); + + it('warns when the created page is not confirmed live (the blank-page trap #2)', async () => { + const { runtime } = fakeRuntime({ + onCreateFolders: () => ({ entity: [{ path: '/p', identifier: 'f' }] }), + onFire: () => ({ entity: { identifier: 'page-1', live: false } }), + onLive: () => ({ entity: { live: false } }) + }); + + const manifest = await createPage({ + dotcms: runtime, + site: 'demo.dotcms.com', + urlPath: '/p/index', + title: 'P', + template: 't' + }); + + expect(manifest.live).toBe(false); + expect(manifest.warnings.length).toBeGreaterThan(0); + expect(manifest.warnings[0]).toMatch(/blank|content/i); + }); + + describe('content type resolution', () => { + const CUSTOM_PAGE: FakeContentType = { + id: 'ct-landing', + variable: 'landingPage', + baseType: 'HTMLPAGE', + fields: [ + { variable: 'title', required: true }, + { variable: 'url', required: true }, + // user-added required field with no default — must be supplied via extraFields + { variable: 'campaign', required: true }, + // user-added required field WITH a default — does not need to be supplied + { variable: 'region', required: true, defaultValue: 'global' }, + // optional user field — never required + { variable: 'subtitle', required: false } + ] + }; + + it('defaults to htmlpageasset and stamps it on the manifest', async () => { + const { runtime } = fakeRuntime({ + onCreateFolders: () => ({ entity: [{ path: '/p', identifier: 'f' }] }), + onFire: () => ({ entity: { identifier: 'page-1', live: true } }) + }); + + const manifest = await createPage({ + dotcms: runtime, + site: 'demo.dotcms.com', + urlPath: '/p/index', + title: 'P', + template: 't' + }); + + expect(manifest.contentType).toBe('htmlpageasset'); + }); + + it('fires a custom page type and merges extraFields', async () => { + let firedBody: { contentlet: Record } | undefined; + const { runtime } = fakeRuntime({ + contentTypes: [CUSTOM_PAGE], + onCreateFolders: () => ({ entity: [{ path: '/lp', identifier: 'f' }] }), + onFire: (body) => { + firedBody = body as { contentlet: Record }; + return { entity: { identifier: 'page-1', live: true } }; + } + }); + + const manifest = await createPage({ + dotcms: runtime, + site: 'demo.dotcms.com', + urlPath: '/lp/index', + title: 'Launch', + template: 't', + contentType: 'landingPage', + extraFields: { campaign: 'summer', subtitle: 'Hot deals' } + }); + + expect(manifest.contentType).toBe('landingPage'); + const contentlet = firedBody?.contentlet ?? {}; + expect(contentlet.contentType).toBe('landingPage'); + expect(contentlet.campaign).toBe('summer'); + expect(contentlet.subtitle).toBe('Hot deals'); + }); + + it('throws listing missing user-required fields before firing', async () => { + const { runtime, calls } = fakeRuntime({ + contentTypes: [CUSTOM_PAGE], + onFire: () => ({ entity: { identifier: 'page-1', live: true } }) + }); + + await expect( + createPage({ + dotcms: runtime, + site: 'demo.dotcms.com', + urlPath: '/lp/index', + title: 'Launch', + template: 't', + contentType: 'landingPage' + // campaign missing + }) + ).rejects.toThrow(/campaign/); + + // It must fail BEFORE any folder/fire side effect. + expect(calls.some((c) => c.path.includes('createfolders'))).toBe(false); + expect(calls.some((c) => c.path.includes('/fire/'))).toBe(false); + }); + + it('does not require a user field that has a default value', async () => { + const { runtime } = fakeRuntime({ + contentTypes: [CUSTOM_PAGE], + onCreateFolders: () => ({ entity: [{ path: '/lp', identifier: 'f' }] }), + onFire: () => ({ entity: { identifier: 'page-1', live: true } }) + }); + + // `region` is required but has a default — supplying only `campaign` is enough. + await expect( + createPage({ + dotcms: runtime, + site: 'demo.dotcms.com', + urlPath: '/lp/index', + title: 'Launch', + template: 't', + contentType: 'landingPage', + extraFields: { campaign: 'summer' } + }) + ).resolves.toMatchObject({ contentType: 'landingPage' }); + }); + + it('rejects a content type that is not a page (wrong base type)', async () => { + const { runtime } = fakeRuntime({ + contentTypes: [{ id: 'ct-blog', variable: 'Blog', baseType: 'CONTENT', fields: [] }] + }); + + await expect( + createPage({ + dotcms: runtime, + site: 'demo.dotcms.com', + urlPath: '/b/index', + title: 'B', + template: 't', + contentType: 'Blog' + }) + ).rejects.toThrow(/not a page|HTMLPAGE/i); + }); + + it('rejects an unknown content type', async () => { + const { runtime } = fakeRuntime({ contentTypes: [HTMLPAGE_ASSET] }); + + await expect( + createPage({ + dotcms: runtime, + site: 'demo.dotcms.com', + urlPath: '/x/index', + title: 'X', + template: 't', + contentType: 'doesNotExist' + }) + ).rejects.toThrow(/not found/i); + }); + + it('cannot be overridden by extraFields on a typed page field', async () => { + let firedBody: { contentlet: Record } | undefined; + const { runtime } = fakeRuntime({ + onCreateFolders: () => ({ entity: [{ path: '/p', identifier: 'f' }] }), + onFire: (body) => { + firedBody = body as { contentlet: Record }; + return { entity: { identifier: 'page-1', live: true } }; + } + }); + + await createPage({ + dotcms: runtime, + site: 'demo.dotcms.com', + urlPath: '/p/index', + title: 'Real Title', + template: 'real-tmpl', + extraFields: { title: 'HIJACK', template: 'evil', url: 'evil' } + }); + + const contentlet = firedBody?.contentlet ?? {}; + expect(contentlet.title).toBe('Real Title'); + expect(contentlet.template).toBe('real-tmpl'); + expect(contentlet.url).toBe('index'); + }); + }); +}); diff --git a/core-web/apps/mcp-server/src/lib/page-create.ts b/core-web/apps/mcp-server/src/lib/page-create.ts new file mode 100644 index 000000000000..17f29b4e0be3 --- /dev/null +++ b/core-web/apps/mcp-server/src/lib/page-create.ts @@ -0,0 +1,636 @@ +import { + HttpError, + type ContentTypeSummary, + type DotCMSRuntime, + type RequestOptions +} from '@dotcms/ai/runtime'; + +import { isContentLive } from './page-common'; +import { splitUrlPath } from './page-path'; +import { resolveLanguageId, resolveSite } from './resolve'; +import { errorMessage } from './runtime'; + +/** The default page content type when the caller does not name one. */ +export const DEFAULT_PAGE_CONTENT_TYPE = 'htmlpageasset'; + +/** Cap on an interpolated response entity inside an error message (see describeEntity). */ +const MAX_ENTITY_CHARS = 1_000; + +/** Base type string the REST API reports for any page content type. */ +const PAGE_BASE_TYPE = 'HTMLPAGE'; + +/** + * System field variables every page content type carries — mirrored by hand from + * dotCMS/.../contenttype/model/type/PageContentType.java (requiredFields() + the field-var + * constants). These are either set explicitly from typed args below or filled by the platform, so + * they are NOT treated as "unsatisfied user-required fields" during validation. Re-sync this set + * if those field variables change backend-side. (The fetched field's `fixed` flag also excludes + * platform fields at line ~`assertRequiredFieldsSatisfied`; this list is the belt-and-suspenders + * fallback for instances/versions where `fixed` isn't reliably serialized.) + */ +const SYSTEM_PAGE_FIELD_VARS = new Set([ + 'title', + 'url', + 'hostFolder', + 'template', + 'showOnMenu', + 'sortOrder', + 'cachettl', + 'friendlyName', + 'pageTitle', + 'redirecturl', + 'httpsreq', + 'seodescription', + 'seokeywords', + 'pagemetadata' +]); + +export interface CreatePageOptions { + dotcms: DotCMSRuntime; + /** Site identifier (UUID) or hostname the page lives on. */ + site: string; + /** Page-relative URL path, e.g. "/books/index" or "/books". The leaf becomes the page url. */ + urlPath: string; + /** Page title. */ + title: string; + /** Template identifier the page renders with. */ + template: string; + /** + * Content type for the page — variable or id. Defaults to `htmlpageasset`. Any content type + * works as long as its base type is HTMLPAGE (a custom page type may add its own fields). + */ + contentType?: string; + /** + * Values for content-type fields beyond the common page fields — keyed by field variable. + * Required for any user-added required field on a custom page type that has no default value. + */ + extraFields?: Record; + /** Optional friendly name; defaults to `title`. */ + friendlyName?: string; + /** Optional page title (browser ); defaults to `title`. */ + pageTitle?: string; + /** Language id. Default 1. */ + languageId?: number; + /** Cache TTL seconds (as dotCMS expects: a string). Default "0". */ + cacheTtl?: string; + /** Sort order. Default 0. */ + sortOrder?: number; +} + +export interface CreatePageManifest { + /** Identifier of the created page contentlet. */ + identifier?: string; + /** Inode of the created version. */ + inode?: string; + /** The resolved content type variable the page was created as. */ + contentType: string; + /** The folder path the page was placed under (e.g. "/books"). */ + folder: string; + /** The leaf url stored on the page (e.g. "index"). */ + url: string; + /** Full live URL on the site (e.g. "/books/index"). */ + fullPath: string; + /** Site the page lives on. */ + site: string; + /** Whether the page is live after the publish fire. */ + live: boolean; + /** + * Set when the page was created but NOT verified live. The page exists; it just may render + * blank with no content placed (the two-step trap). Not a hard failure. + */ + warnings: string[]; +} + +/** + * Create and publish a dotCMS page in one safe call. + * + * A "page" is a contentlet whose content type's base type is HTMLPAGE, fired through the generic + * workflow endpoint — there is no dedicated create-page endpoint. The content type defaults to + * `htmlpageasset` but can be any page type; custom page types may add their own (possibly required) + * fields, so we resolve the type and validate against its actual field set before firing. + * + * This wrapper absorbs the URL-collapse trap: dotCMS silently collapses a page `url` whose parent + * folder does not exist down to `/index`, which then 400s against the home page. We split `urlPath` + * into folder + leaf, create the folder first, then fire the page with `url: "<leaf>"` and + * `hostFolder: <created folder>` so the URL lands where the caller meant. + * + * This is the THIN tier: it does NOT place content. The page comes up live but blank — content + * placement and the re-publish that follows are a separate, explicit step for the caller. The + * manifest flags this so a successful create is never mistaken for a fully-populated page. + */ +export async function createPage(options: CreatePageOptions): Promise<CreatePageManifest> { + const { folder, url, fullPath } = splitUrlPath(options.urlPath); + const warnings: string[] = []; + const extraFields = options.extraFields ?? {}; + + // Resolve the site to its identifier UUID up front. `contentHost` on the fire body MUST be a + // site UUID — a bare hostname makes the fire NPE ("Host.getIdentifier() because host is null"), + // which is exactly the root-page (`/`) trap where there is no folder to anchor the page on. + const siteId = await resolveSiteId(options.dotcms, options.site); + + // Resolve the page content type and validate it BEFORE creating anything. A wrong type (not a + // page) or a missing user-required field would otherwise 400 the fire opaquely — and only after + // we'd already created the folder. Fail early with a precise message instead. + const contentType = await resolvePageContentType(options.dotcms, options.contentType); + assertRequiredFieldsSatisfied(contentType, extraFields); + + // Validate the remaining caller inputs BEFORE anything is written. `site` and + // `contentType` are already resolved above against cached context; `template` and + // `languageId` were the two that were not, which made them the only inputs whose + // rejection arrived AFTER the folder had been created (see ensureFolder below). + const languageId = await resolveLanguageId(options.dotcms, options.languageId); + await assertTemplateExists(options.dotcms, options.template); + + // Trap #1: the parent folder must exist before the page is fired, or dotCMS collapses the + // page url to /index. createfolders is idempotent — re-creating an existing folder is a no-op. + const folderId = await ensureFolder(options.dotcms, siteId, folder); + + // Trap #3 (root/leaf page on a NON-default site): the page's HOST_OR_FOLDER field must carry a + // concrete location id, or the fire cannot resolve the host and 500s with + // "Host.getIdentifier() ... host is null". A nested page passes the folder id (which carries its + // host); a root page has no folder, so `folderId` is undefined and, left alone, `hostFolder` + // would be dropped from the JSON body — leaving only `contentHost`, which is not enough to + // anchor a root page. Fall the location back to the SITE id: HOST_OR_FOLDER accepts a host id + // and resolves it to that host's system folder. (This mirrors the working manual recovery: + // fire with hostFolder = site id.) + const hostFolder = folderId ?? siteId; + + const title = options.title; + // Guarded: `ensureFolder` above has ALREADY COMMITTED a folder by this point, so a bare + // rethrow here leaves a folder nothing mentions. `page_create({urlPath:"/books/index"})` + // with a bad input would create `/books`, fail, and report only the HTTP error — and a + // corrected retry then operates on folder state the caller does not know exists. + const fired = await fireCreate(options.dotcms, folder, folderId, { + method: 'PUT', + path: '/api/v1/workflow/actions/default/fire/PUBLISH', + query: { indexPolicy: 'WAIT_FOR' }, + body: { + contentlet: { + // User-added fields first, so the typed page fields below always win on the keys + // they own (a caller can't accidentally override `url`/`template` via extraFields). + ...extraFields, + contentType: contentType.variable, + contentHost: siteId, + hostFolder, + languageId, + title, + url, + template: options.template, + cachettl: options.cacheTtl ?? '0', + sortOrder: options.sortOrder ?? 0, + friendlyName: options.friendlyName ?? title, + pageTitle: options.pageTitle ?? title + } + } + }); + + const entity = extractPageEntity(fired); + const identifier = entity?.identifier; + const inode = entity?.inode; + + const live = await isLive(options.dotcms, identifier); + if (!live) { + warnings.push( + `Page created but not confirmed live. It may render blank until content is placed and the page is re-published.` + ); + } + + return { + identifier, + inode, + contentType: contentType.variable, + folder, + url, + fullPath, + site: options.site, + live, + warnings + }; +} + +interface PageEntity { + identifier?: string; + inode?: string; + live?: boolean; + contentlets?: PageEntity[]; +} + +interface ContentTypeField { + variable?: string; + required?: boolean; + fixed?: boolean; + defaultValue?: unknown; +} + +/** + * A page content type as this file needs it: the three identity fields plus the field list. + * + * Derived from `ContentTypeSummary` rather than re-declared, so an upstream change to the + * response contract fails at COMPILE time here instead of silently drifting — a hand-written + * copy of a response shape cannot drift-check against anything. + * + * `Pick`, not `extends`, on purpose: `fetchContentTypeDefinition` never populates `name`, and + * inheriting the full summary would assert a field this value does not carry. + */ +type ContentTypeDefinition = Pick<ContentTypeSummary, 'id' | 'variable' | 'baseType'> & { + fields: ContentTypeField[]; +}; + +/** + * Resolve a site (given as a hostname OR an identifier UUID) to its identifier UUID. + * + * The fire body's `contentHost` must be a site UUID. Passing a bare hostname works for pages under + * a folder (the folder anchors the host) but NPEs for a root page, where there is no folder — dotCMS + * then can't resolve the host and throws "Host.getIdentifier() because host is null". Resolving to + * the UUID here makes every page (root included) fire cleanly. Uses the runtime's cached site + * context (already loaded), mirroring how resolvePageContentType uses cached content types. + */ +async function resolveSiteId(dotcms: DotCMSRuntime, site: string): Promise<string> { + return (await resolveSite(dotcms, site)).identifier; +} + +/** + * Resolve the page content type by variable or id and confirm it is actually a page type. + * + * Defaults to `htmlpageasset`. We first match against the runtime's cached content-type summaries + * (cheap, already loaded) to give a precise "not found / not a page type" error, then fetch the + * full definition (with fields) so required-field validation can run. Firing a non-page type as a + * page produces a broken contentlet, so a wrong base type is a hard error, not a warning. + */ +async function resolvePageContentType( + dotcms: DotCMSRuntime, + requested?: string +): Promise<ContentTypeDefinition> { + const wanted = (requested ?? DEFAULT_PAGE_CONTENT_TYPE).trim(); + + // The cache is consulted for the id and for a candidate list, but it does NOT decide + // whether the type exists: the definition fetch below is a live call that answers the + // same question authoritatively. Gating on the cache meant that when the one-time + // context load failed — leaving `contentTypes` empty for the whole session — even the + // default `htmlpageasset` was rejected as "not found". + const context = await safeLoadContext(dotcms); + const summary = context.contentTypes.find((ct) => ct.variable === wanted || ct.id === wanted); + + // Computed lazily — the happy path never needs the list of page types for an error message. + const availablePageTypes = () => + context.contentTypes + .filter((ct) => ct.baseType === PAGE_BASE_TYPE) + .map((ct) => ct.variable) + .join(', ') || '(unknown — the session content-type list did not load)'; + + let definition: ContentTypeDefinition | undefined; + try { + definition = await fetchContentTypeDefinition(dotcms, summary?.id || wanted); + } catch (error) { + if (!(error instanceof HttpError) || error.status !== 404) { + throw error; + } + // 404 → not found, handled below alongside the empty-entity case. + } + + if (!definition) { + throw new Error( + `Content type "${wanted}" was not found on this instance. ` + + `Available page content types: ${availablePageTypes()}.` + ); + } + + // Checked against the FETCHED definition, not the cached summary — the fetch is the + // authority and is present on every path. + if (definition.baseType !== PAGE_BASE_TYPE) { + throw new Error( + `Content type "${definition.variable}" is base type ${definition.baseType}, not a page ` + + `(HTMLPAGE). page_create only creates pages. Available page content types: ` + + `${availablePageTypes()}.` + ); + } + + return definition; +} + +/** + * `loadContext()` with its failure absorbed — the context is an accelerator here, and every + * caller below has a live path that does not need it. See lib/resolve.ts for the full + * rationale on why an empty cache must never be read as "this does not exist". + */ +async function safeLoadContext(dotcms: DotCMSRuntime) { + try { + return await dotcms.loadContext(); + } catch (error) { + console.error( + `[context] load failed during content-type resolution: ${errorMessage(error)}` + ); + + return { contentTypes: [], sites: [], languages: [], currentUser: null }; + } +} + +/** Fetch the full content-type definition (including fields) by id or variable. */ +async function fetchContentTypeDefinition( + dotcms: DotCMSRuntime, + idOrVar: string +): Promise<ContentTypeDefinition | undefined> { + const response = await dotcms.request({ + path: `/api/v1/contenttype/id/${encodeURIComponent(idOrVar)}` + }); + + const entity = asRecord(responseEntity(response)); + const rawFields = entity?.['fields']; + const fields: ContentTypeField[] = Array.isArray(rawFields) + ? rawFields + .map(asRecord) + .filter((f): f is Record<string, unknown> => f !== undefined) + .map((f) => ({ + variable: optionalString(f, 'variable'), + required: f['required'] === true, + fixed: f['fixed'] === true, + defaultValue: f['defaultValue'] + })) + : []; + + const variable = optionalString(entity, 'variable'); + const baseType = optionalString(entity, 'baseType'); + + // An empty/unrecognised entity means the type was not resolved. Returning a shape that + // defaults `baseType` to HTMLPAGE would assert the very thing the caller is being + // checked for, so the base-type guard would pass on a type that does not exist. + if (!variable || !baseType) { + return undefined; + } + + return { + id: optionalString(entity, 'id') ?? idOrVar, + variable, + baseType, + fields + }; +} + +/** + * Fail before firing if the type has a user-added required field we have no value for. dotCMS + * would reject the fire with a 400 anyway — but only after we've created the folder, and with a + * less actionable message. We skip system page fields (filled from typed args / the platform), + * fixed fields, and fields that carry a default value. + */ +function assertRequiredFieldsSatisfied( + contentType: ContentTypeDefinition, + extraFields: Record<string, unknown> +): void { + // flatMap (not filter + map) so `variable` stays narrowed to string without a cast. + const missing = contentType.fields.flatMap((field) => { + const variable = field.variable; + if (!variable || !field.required || field.fixed) return []; + if (SYSTEM_PAGE_FIELD_VARS.has(variable)) return []; + if (hasDefault(field.defaultValue)) return []; + + return hasValue(extraFields[variable]) ? [] : [variable]; + }); + + if (missing.length > 0) { + throw new Error( + `Content type "${contentType.variable}" has required field(s) with no value: ` + + `${missing.join(', ')}. Pass them via extraFields, e.g. ` + + `{ "${missing[0]}": <value> }.` + ); + } +} + +/** A server-side default counts if non-null and (for strings) non-empty. */ +function hasDefault(defaultValue: unknown): boolean { + return typeof defaultValue === 'string' ? defaultValue.length > 0 : defaultValue != null; +} + +/** A caller-supplied value counts if non-null and (for strings) non-blank after trimming. */ +function hasValue(value: unknown): boolean { + if (value == null) return false; + if (typeof value === 'string') return value.trim().length > 0; + return true; +} + +/** + * Ensure the folder path exists on the site and return the id of the deepest (target) folder. + * createfolders creates the full path and is idempotent on existing folders. + */ +async function ensureFolder( + dotcms: DotCMSRuntime, + site: string, + folder: string +): Promise<string | undefined> { + if (folder === '/' || folder === '') { + // Root page: no folder to create. The caller falls hostFolder back to the site id (a root + // page fired with an undefined hostFolder 500s on a non-default site — see Trap #3). + return undefined; + } + + const response = await dotcms.request({ + method: 'POST', + path: `/api/v1/folder/createfolders/${encodeURIComponent(site)}`, + body: [folder] + }); + + const folderId = extractFolderId(response, folder); + + // A nested page MUST be anchored on its real folder id. Falling back to the site id here + // (the root-page path below) would silently anchor the page at the site root and + // reintroduce the very /index URL-collapse trap this function exists to prevent, while the + // manifest still reported the intended folder/fullPath. Fail loudly instead. + if (!folderId) { + throw new Error( + `Folder "${folder}" was requested on site "${site}" but createfolders returned no ` + + `resolvable folder id, so the page cannot be anchored to it. Refusing to fall ` + + `back to the site root (that would collapse the page url to /index). ` + + `Response entity: ${describeEntity(response)}` + ); + } + + return folderId; +} + +/** + * `dotcms.request()` is typed `unknown` — it speaks to a live instance whose payload we cannot + * prove at compile time. Rather than assert a shape with `as` (which type-checks a lie and then + * lets `undefined` surface deep in the caller), narrow the ONE thing every dotCMS REST envelope + * guarantees: an object with an optional `entity`. Callers keep their own per-endpoint guards for + * what lives inside it, so a shape change becomes a handled `undefined` rather than a crash. + */ +function responseEntity(response: unknown): unknown { + if (typeof response !== 'object' || response === null) { + return undefined; + } + + return (response as { entity?: unknown }).entity; +} + +/** Narrow a value to an indexable record, or undefined when it isn't one. */ +function asRecord(value: unknown): Record<string, unknown> | undefined { + return typeof value === 'object' && value !== null && !Array.isArray(value) + ? (value as Record<string, unknown>) + : undefined; +} + +/** Read an optional string property, ignoring non-string values. */ +function optionalString( + record: Record<string, unknown> | undefined, + key: string +): string | undefined { + const value = record?.[key]; + + return typeof value === 'string' ? value : undefined; +} + +/** + * createfolders returns the created/found folders. We want the id of the folder matching our + * target path. Shapes vary across versions (array vs object, identifier vs inode), so probe + * defensively and fall back to the deepest entry. + */ +function extractFolderId(response: unknown, folder: string): string | undefined { + const entity = responseEntity(response); + const raw: unknown[] = Array.isArray(entity) ? entity : entity ? [entity] : []; + const list = raw.map(asRecord).filter((f): f is Record<string, unknown> => f !== undefined); + if (list.length === 0) { + return undefined; + } + + const normalized = folder.replace(/\/+$/, ''); + const match = list.find((f) => optionalString(f, 'path')?.replace(/\/+$/, '') === normalized); + const chosen = match ?? list[list.length - 1]; + + return optionalString(chosen, 'identifier') ?? optionalString(chosen, 'inode'); +} + +/** + * Read the page fields out of a fire/read response. + * + * Every field is read through a checking accessor rather than asserted with `as`. The two + * casts that used to live here claimed `identifier`, `inode` and `live` existed with the + * right types off a `Record<string, unknown>` that nothing had checked — precisely the + * "type-check a lie and let `undefined` surface deep in the caller" failure that + * `responseEntity`/`asRecord`/`optionalString` were introduced to close. + */ +function extractPageEntity(response: unknown): PageEntity | undefined { + const entity = asRecord(responseEntity(response)); + if (!entity) { + return undefined; + } + // Fire responses sometimes wrap the contentlet under `contentlets[0]`. + const contentlets = entity['contentlets']; + if (Array.isArray(contentlets) && contentlets.length) { + const first = asRecord(contentlets[0]); + + return first && toPageEntity(first); + } + + return toPageEntity(entity); +} + +/** Project a checked record onto {@link PageEntity} — unknown/mistyped fields stay undefined. */ +function toPageEntity(record: Record<string, unknown>): PageEntity { + return { + identifier: optionalString(record, 'identifier'), + inode: optionalString(record, 'inode'), + live: optionalBoolean(record, 'live') + }; +} + +/** Read an optional boolean property, ignoring non-boolean values. */ +function optionalBoolean( + record: Record<string, unknown> | undefined, + key: string +): boolean | undefined { + const value = record?.[key]; + + return typeof value === 'boolean' ? value : undefined; +} + +async function isLive(dotcms: DotCMSRuntime, identifier?: string): Promise<boolean> { + if (!identifier) { + return false; + } + + try { + return await isContentLive(dotcms, identifier); + } catch { + // A failed liveness check is not a failed create — the page exists either way, so + // report it as not-verified-live rather than failing the whole operation. The + // shared probe deliberately throws; swallowing is this caller's choice, not the + // primitive's (see page-common.ts). + return false; + } +} + +/** + * Confirm the template exists before anything is written. + * + * `template` is the one input a caller most often gets wrong — the schema says "the template + * UUID, not its name", which is exactly the mistake worth catching — and it was the only one + * whose rejection arrived from the fire, i.e. after the folder had already been created. + * + * A non-404 failure here is deliberately NOT fatal: the check is a courtesy, and refusing to + * create a page because the template lookup was briefly unavailable would be worse than + * letting the fire decide. + */ +async function assertTemplateExists(dotcms: DotCMSRuntime, template: string): Promise<void> { + try { + await dotcms.request({ + path: `/api/v1/templates/${encodeURIComponent(template)}/working` + }); + } catch (error) { + if (error instanceof HttpError && error.status === 404) { + throw new Error( + `Template "${template}" was not found. This must be the template's IDENTIFIER ` + + `(a UUID), not its name — passing a human-readable title here is the most ` + + `common cause. Nothing has been created; fix the template and re-run.` + ); + } + // Anything else (403, 5xx, timeout): fall through and let the fire be the judge. + } +} + +/** + * Fire the create, and on failure say what has ALREADY been committed. + * + * The folder write happens before this point and cannot be rolled back, so the recoverable + * outcome depends on the caller knowing three things: which inputs were used, that folder + * `<x>` now exists, and that re-running is safe because `createfolders` is idempotent. That + * last sentence is what turns an orphaned folder into a retryable operation. + */ +async function fireCreate( + dotcms: DotCMSRuntime, + folder: string, + folderId: string | undefined, + request: RequestOptions +): Promise<unknown> { + try { + return await dotcms.request(request); + } catch (error) { + const created = folderId + ? `Folder "${folder}" (${folderId}) WAS created before this failure and still exists. ` + : ''; + throw new Error( + `${errorMessage(error)}\n\n${created}Re-running this call after fixing the input is ` + + `SAFE: folder creation is idempotent, so no duplicate folder is made.` + ); + } +} + +/** + * Render a response's entity for an error message. + * + * `JSON.stringify(undefined)` returns `undefined` (the value, not a string), so interpolating + * it printed the literal text "Response entity: undefined" — and an ABSENT entity is exactly + * the condition that triggers the branch using this, so that was the common case rather than + * the edge one. When an entity IS present it is capped: the raw blob can be arbitrarily large + * and this is going straight into an error the model has to read. + */ +function describeEntity(response: unknown): string { + const entity = responseEntity(response); + if (entity === undefined || entity === null) { + return '(no entity in the response — the endpoint returned a body this tool could not read)'; + } + + const json = JSON.stringify(entity); + + return json.length <= MAX_ENTITY_CHARS + ? json + : `${json.slice(0, MAX_ENTITY_CHARS)}… [truncated]`; +} diff --git a/core-web/apps/mcp-server/src/lib/page-path.spec.ts b/core-web/apps/mcp-server/src/lib/page-path.spec.ts new file mode 100644 index 000000000000..ee04987727a0 --- /dev/null +++ b/core-web/apps/mcp-server/src/lib/page-path.spec.ts @@ -0,0 +1,72 @@ +import { normalizePagePath } from './page-path'; + +describe('normalizePagePath', () => { + it('leaves an already-clean path alone', () => { + expect(normalizePagePath('/about-us')).toBe('/about-us'); + expect(normalizePagePath('/blog/post/hello')).toBe('/blog/post/hello'); + }); + + it('adds the leading slash', () => { + expect(normalizePagePath('about-us')).toBe('/about-us'); + }); + + it('collapses . and .. segments', () => { + // Live bug before this: `page_verify({ path: '/a/../b' })` RENDERED /b while the + // manifest reported '/a/../b' — the tool reporting on a page it did not read. + expect(normalizePagePath('/a/../b')).toBe('/b'); + expect(normalizePagePath('/a/./b')).toBe('/a/b'); + expect(normalizePagePath('/a/b/../../c')).toBe('/c'); + }); + + it('cannot escape above the root', () => { + // Latent until an `allow` policy exists: requestCore policy-checks the RAW string and + // normalizes afterwards, so this would pass an `/api/v1/page/` prefix allowlist and + // then resolve somewhere else entirely. + expect(normalizePagePath('/../../../../api/v1/users/current')).toBe( + '/api/v1/users/current' + ); + }); + + it('drops a query string and a fragment', () => { + // A `#` used to silently truncate the path with no mention in the manifest. + expect(normalizePagePath('/about-us?foo=1')).toBe('/about-us'); + expect(normalizePagePath('/about-us#section')).toBe('/about-us'); + }); + + it('normalizes the root to /', () => { + expect(normalizePagePath('/')).toBe('/'); + expect(normalizePagePath('///')).toBe('/'); + }); + + it('collapses repeated slashes', () => { + expect(normalizePagePath('/a//b')).toBe('/a/b'); + }); + + it('does not read a leading // as a host', () => { + // To the URL API a `//` prefix is scheme-relative, so `//books/index` parses `books` + // as a HOST and yields `/index` — a different page, with nothing to indicate it. + // Callers reach this legitimately: `//host/path` is how dotCMS writes a + // host-qualified path elsewhere. + expect(normalizePagePath('//books/index')).toBe('/books/index'); + }); + + it('keeps a space encoded so the result is a usable URL path', () => { + expect(normalizePagePath('/my%20books')).toBe('/my%20books'); + expect(normalizePagePath('/my books')).toBe('/my%20books'); + }); + + it('refuses an encoded path separator', () => { + // Splitting first does not make it inert — callers rebuild by joining segments, which + // turns it straight back into a path boundary. + expect(() => normalizePagePath('/a/my%2Fbooks')).toThrow(/encoded path separator/i); + }); + + it('rejects an empty path', () => { + expect(() => normalizePagePath('')).toThrow(/must not be empty/i); + expect(() => normalizePagePath(' ')).toThrow(/must not be empty/i); + }); + + it('uses the caller-supplied label in its errors', () => { + expect(() => normalizePagePath('', 'urlPath')).toThrow(/urlPath must not be empty/i); + }); +}); diff --git a/core-web/apps/mcp-server/src/lib/page-path.ts b/core-web/apps/mcp-server/src/lib/page-path.ts new file mode 100644 index 000000000000..ba7093ecb570 --- /dev/null +++ b/core-web/apps/mcp-server/src/lib/page-path.ts @@ -0,0 +1,135 @@ +/** + * Normalization for the page paths callers hand to the page tools. + * + * Shared because all three tools build a request URL by interpolating a caller-supplied path + * into an endpoint template — `/api/v1/page/render${uri}`, `/api/v1/page/json${uri}` — and + * only `page_create` was normalizing first. The two that were not had a live correctness bug + * and a latent security one: + * + * - Live today: `page_verify({ path: '/a/../b' })` renders `/b` while the manifest reports + * `/a/../b`, and a `#` silently truncates the path. The tool's whole job is to report on + * the page it actually checked, so reporting a different path than it read is a + * correctness failure, not a cosmetic one. + * - Latent: `requestCore` policy-checks the RAW request string and normalizes afterwards, + * so a path like `/../../../../api/v1/users/current` would pass an `/api/v1/page/` prefix + * allowlist and then resolve somewhere else entirely. No `allow` policy is configured + * today, which is the only reason this is not already exploitable — and adding one is the + * natural next hardening step for this tool surface. + */ + +/** The percent-decoded segments of a page path, with encoded separators refused. */ +function decodeSegments(pathname: string, original: string, label: string): string[] { + const segments = pathname + .split('/') + .filter(Boolean) + .map((segment) => decodeURIComponent(segment)); + + // Splitting first does not make an encoded slash inert: every caller rebuilds a path by + // joining these segments, which turns it straight back into a path boundary. dotCMS + // folder names cannot contain a separator anyway, so nothing legitimate is refused. + const smuggled = segments.find((segment) => segment.includes('/')); + if (smuggled !== undefined) { + throw new Error( + `${label} segment "${smuggled}" contains an encoded path separator (%2F), which ` + + `would silently resolve to a DIFFERENT path than the one named: "${original}" ` + + `would behave as if the slash had been written literally. dotCMS folder names ` + + `cannot contain "/", so write the path out plainly instead.` + ); + } + + return segments; +} + +/** Run a path through the URL API, collapsing `.`/`..` and dropping any query or fragment. */ +function toPathname(trimmed: string, original: string, label: string): string { + // Collapse a leading `//` FIRST. To the URL API a `//` prefix is scheme-relative, so it + // reads the next segment as a HOST: `new URL('//books/index', 'http://_').pathname` is + // `/index`, silently discarding `books` — a page path resolving to a different page with + // nothing to indicate it. (`///` does not even parse.) Callers reach this legitimately, + // since `//host/path` is how dotCMS writes a host-qualified path elsewhere. + const withoutSchemeRelative = trimmed.replace(/^\/+/, '/'); + + try { + // The base is a throwaway — only `pathname` is read back out, so the host never leaks + // into the result. + return new URL(withoutSchemeRelative, 'http://_').pathname; + } catch { + throw new Error(`${label} is not a valid path: "${original}"`); + } +} + +/** + * The canonical form of a page path: leading slash, `.`/`..` collapsed, query and fragment + * dropped, percent-encoding decoded, encoded separators refused. + * + * Returned so the CALLER can report what it actually requested. A tool that normalizes for + * the request but echoes the raw input in its manifest is telling the model about a page it + * did not look at. + */ +export function normalizePagePath(path: string, label = 'path'): string { + const trimmed = path.trim(); + if (!trimmed) { + throw new Error(`${label} must not be empty.`); + } + + const withSlash = trimmed.startsWith('/') ? trimmed : `/${trimmed}`; + const segments = decodeSegments(toPathname(withSlash, path, label), path, label); + + if (segments.length === 0) { + return '/'; + } + + // Re-encode each segment so the result is a valid URL path again: decoding happened only + // to inspect the segments, and a space or `#` left raw here would break the request URL + // this feeds. `encodeURIComponent` escapes `/` too, which is exactly right — a slash + // inside a single segment is the case refused above. + return `/${segments.map((segment) => encodeURIComponent(segment)).join('/')}`; +} + +/** + * Split a page-relative URL into the parent folder and the leaf url stored on the page. + * + * "/books/index" → { folder: "/books", url: "index", fullPath: "/books/index" } + * "/books" → { folder: "/books", url: "index", fullPath: "/books/index" } + * "/about-us/" → { folder: "/about-us", url: "index", fullPath: "/about-us/index" } + * "/" → { folder: "/", url: "index", fullPath: "/index" } + * + * Shares its normalization with {@link normalizePagePath} but NOT its output: the folder-vs-leaf + * decision is page-create's alone. The URL API preserves a trailing slash but does not know that + * "/about-us/" means a folder index while "/about-us" means a leaf url, and dotCMS pages always + * have a leaf url (commonly "index"), so a path with no explicit leaf gets one — matching how the + * admin UI and the rest of the platform address a folder's default page. + * + * Segments are returned DECODED here, because they become folder and page names rather than + * parts of a URL. + */ +export function splitUrlPath(urlPath: string): { folder: string; url: string; fullPath: string } { + const trimmed = urlPath.trim(); + if (!trimmed.startsWith('/')) { + throw new Error(`urlPath must start with "/": "${urlPath}"`); + } + + const pathname = toPathname(trimmed, urlPath, 'urlPath'); + const segments = decodeSegments(pathname, urlPath, 'urlPath'); + + // No segments → the site root; the page is the root index. + if (segments.length === 0) { + return { folder: '/', url: 'index', fullPath: '/index' }; + } + + // A trailing slash means the whole path IS the folder and the page is its index. Otherwise the + // last segment is the leaf url and everything before it is the folder. (segments is non-empty + // here — the length===0 case returned above.) + if (!pathname.endsWith('/')) { + const url = segments[segments.length - 1]; + const folderSegments = segments.slice(0, -1); + const folder = folderSegments.length ? `/${folderSegments.join('/')}` : '/'; + const fullPath = `${folder === '/' ? '' : folder}/${url}`; + + return { folder, url, fullPath }; + } + + const folder = `/${segments.join('/')}`; + + return { folder, url: 'index', fullPath: `${folder}/index` }; +} diff --git a/core-web/apps/mcp-server/src/lib/page-place-content.spec.ts b/core-web/apps/mcp-server/src/lib/page-place-content.spec.ts new file mode 100644 index 000000000000..6c1d5fe073fb --- /dev/null +++ b/core-web/apps/mcp-server/src/lib/page-place-content.spec.ts @@ -0,0 +1,450 @@ +import { HttpError, type DotCMSRuntime, type RequestOptions } from '@dotcms/ai/runtime'; + +import { + placeContent as placeContentImpl, + type PagePlaceContentOptions +} from './page-place-content'; + +/** + * A page with two slots on the default file container (uuids "1" and "2") and one system-container + * slot (uuid "1"). Slot #1 already has one contentlet, slot #2 is empty, the system slot has one. + * This mirrors the /api/v1/page/json shape: `containers` keyed by container key, each with a + * `contentlets` map keyed by uuid; `layout.body.rows[].columns[].containers[]` for slot order. + */ +const DEFAULT_CONTAINER = '//demo.dotcms.com/application/containers/default/'; +const SYSTEM_CONTAINER = 'SYSTEM_CONTAINER'; + +/** Existing behavior tests target the demo site explicitly; targeting-specific tests call impl. */ +function placeContent(options: PagePlaceContentOptions) { + return placeContentImpl({ site: 'demo.dotcms.com', ...options }); +} + +function pageJson() { + return { + entity: { + page: { identifier: 'page-1', pageURI: '/about-us' }, + containers: { + [DEFAULT_CONTAINER]: { + contentlets: { + 'uuid-1': [{ identifier: 'existing-a' }], + 'uuid-2': [] + } + }, + [SYSTEM_CONTAINER]: { + contentlets: { + 'uuid-1': [{ identifier: 'sys-x' }] + } + } + }, + layout: { + body: { + rows: [ + { + columns: [ + { + containers: [ + { identifier: DEFAULT_CONTAINER, uuid: '1' }, + { identifier: DEFAULT_CONTAINER, uuid: '2' } + ] + } + ] + }, + { + columns: [{ containers: [{ identifier: SYSTEM_CONTAINER, uuid: '1' }] }] + } + ] + } + } + } + }; +} + +interface PostedEntry { + identifier: string; + uuid: string; + contentletsId: string[]; +} + +function fakeRuntime(overrides?: { + onPost?: (body: unknown, query: unknown) => unknown; + page?: unknown; +}) { + const calls: Array<{ method?: string; path: string; body?: unknown; query?: unknown }> = []; + const request = jest.fn(async (options: RequestOptions) => { + calls.push({ + method: options.method, + path: options.path, + body: options.body, + query: options.query + }); + if (options.path.startsWith('/api/v1/page/json')) { + return overrides?.page ?? pageJson(); + } + if (/\/api\/v1\/page\/.+\/content$/.test(options.path)) { + return overrides?.onPost?.(options.body, options.query) ?? { entity: [] }; + } + return {}; + }); + const loadContext = jest.fn(async () => ({ + contentTypes: [], + sites: [ + { + identifier: 'site-demo', + hostname: 'demo.dotcms.com', + isDefault: true, + archived: false, + live: true + }, + { + identifier: 'site-awazon', + hostname: 'awazon.dotcms.site', + isDefault: false, + archived: false, + live: true + } + ], + languages: [], + currentUser: null + })); + return { runtime: { request, loadContext } as unknown as DotCMSRuntime, calls }; +} + +/** Pull the POSTed body (the full container array) out of the recorded calls. */ +function postedBody(calls: Array<{ path: string; body?: unknown }>): PostedEntry[] { + const post = calls.find((c) => /\/api\/v1\/page\/.+\/content$/.test(c.path)); + return (post?.body as PostedEntry[]) ?? []; +} + +function slot(body: PostedEntry[], identifier: string, uuid: string): string[] { + return body.find((e) => e.identifier === identifier && e.uuid === uuid)?.contentletsId ?? []; +} + +describe('placeContent', () => { + it('requires site for a bare path before making any page request', async () => { + const { runtime, calls } = fakeRuntime(); + + await expect( + placeContentImpl({ + dotcms: runtime, + path: '/about-us', + slots: [{ slot: 1, contentlets: ['x'] }] + }) + ).rejects.toThrow(/`site` is required.*wrong page/i); + + expect(calls).toHaveLength(0); + }); + + it('normalizes a host-qualified path and sends that site as host_id', async () => { + const { runtime, calls } = fakeRuntime(); + + const manifest = await placeContentImpl({ + dotcms: runtime, + path: '//awazon.dotcms.site/about-us', + slots: [{ slot: 1, contentlets: ['x'] }] + }); + + const read = calls.find((call) => call.path.startsWith('/api/v1/page/json')); + expect(read?.path).toBe('/api/v1/page/json/about-us'); + expect(read?.query).toMatchObject({ host_id: 'site-awazon' }); + expect(manifest.site).toBe('awazon.dotcms.site'); + }); + + it('rejects conflicting explicit and host-qualified sites before reading the page', async () => { + const { runtime, calls } = fakeRuntime(); + + await expect( + placeContentImpl({ + dotcms: runtime, + path: '//awazon.dotcms.site/about-us', + site: 'demo.dotcms.com', + slots: [{ slot: 1, contentlets: ['x'] }] + }) + ).rejects.toThrow(/Conflicting sites/i); + + expect(calls).toHaveLength(0); + }); + + it('posts the COMPLETE container map, not just the touched slot', async () => { + const { runtime, calls } = fakeRuntime(); + + await placeContent({ + dotcms: runtime, + path: '/about-us', + slots: [{ slot: 1, contentlets: ['new-b'], op: 'append' }] // default container, uuid "1" + }); + + const body = postedBody(calls); + // Every one of the page's three slots is present in the body. + expect(body).toHaveLength(3); + // Touched slot got the append (existing kept, new added, in order). + expect(slot(body, DEFAULT_CONTAINER, '1')).toEqual(['existing-a', 'new-b']); + // Untouched slots preserved verbatim — this is the anti-wipe guarantee. + expect(slot(body, DEFAULT_CONTAINER, '2')).toEqual([]); + expect(slot(body, SYSTEM_CONTAINER, '1')).toEqual(['sys-x']); + }); + + it('op "set" replaces the slot content exactly', async () => { + const { runtime, calls } = fakeRuntime(); + + await placeContent({ + dotcms: runtime, + path: '/about-us', + slots: [{ slot: 1, contentlets: ['only-this'], op: 'set' }] + }); + + expect(slot(postedBody(calls), DEFAULT_CONTAINER, '1')).toEqual(['only-this']); + }); + + it('op "set" with [] clears a slot but leaves others intact', async () => { + const { runtime, calls } = fakeRuntime(); + + await placeContent({ + dotcms: runtime, + path: '/about-us', + slots: [{ slot: 1, contentlets: [], op: 'set' }] + }); + + const body = postedBody(calls); + expect(slot(body, DEFAULT_CONTAINER, '1')).toEqual([]); + expect(slot(body, SYSTEM_CONTAINER, '1')).toEqual(['sys-x']); + }); + + it('op "remove" removes only the named ids', async () => { + const { runtime, calls } = fakeRuntime(); + + await placeContent({ + dotcms: runtime, + path: '/about-us', + slots: [{ slot: 1, contentlets: ['existing-a'], op: 'remove' }] + }); + + expect(slot(postedBody(calls), DEFAULT_CONTAINER, '1')).toEqual([]); + }); + + it('append de-duplicates ids already in the slot', async () => { + const { runtime, calls } = fakeRuntime(); + + await placeContent({ + dotcms: runtime, + path: '/about-us', + slots: [{ slot: 1, contentlets: ['existing-a', 'new-b'], op: 'append' }] + }); + + expect(slot(postedBody(calls), DEFAULT_CONTAINER, '1')).toEqual(['existing-a', 'new-b']); + }); + + it('defaults op to append when omitted', async () => { + const { runtime, calls } = fakeRuntime(); + + await placeContent({ + dotcms: runtime, + path: '/about-us', + slots: [{ slot: 1, contentlets: ['new-b'] }] + }); + + expect(slot(postedBody(calls), DEFAULT_CONTAINER, '1')).toEqual(['existing-a', 'new-b']); + }); + + it('addresses a slot by container + instance uuid', async () => { + const { runtime, calls } = fakeRuntime(); + + await placeContent({ + dotcms: runtime, + path: '/about-us', + slots: [{ slot: { container: 'default', instance: '2' }, contentlets: ['new-c'] }] + }); + + expect(slot(postedBody(calls), DEFAULT_CONTAINER, '2')).toEqual(['new-c']); + // Sibling instance "1" of the same container is untouched. + expect(slot(postedBody(calls), DEFAULT_CONTAINER, '1')).toEqual(['existing-a']); + }); + + it('errors (before any write) when a container instance is ambiguous', async () => { + const { runtime, calls } = fakeRuntime(); + + await expect( + placeContent({ + dotcms: runtime, + path: '/about-us', + // 'default' appears in uuid "1" and "2" — ambiguous without an instance. + slots: [{ slot: { container: 'default' }, contentlets: ['x'] }] + }) + ).rejects.toThrow(/appears in 2 slots.*instances.*Pass slot\.instance/i); + + expect(calls.some((c) => /\/content$/.test(c.path))).toBe(false); + }); + + it('errors (before any write) for an out-of-range slot index, listing valid slots', async () => { + const { runtime, calls } = fakeRuntime(); + + await expect( + placeContent({ + dotcms: runtime, + path: '/about-us', + slots: [{ slot: 9, contentlets: ['x'] }] + }) + ).rejects.toThrow(/out of range.*3 slot/i); + + expect(calls.some((c) => /\/content$/.test(c.path))).toBe(false); + }); + + it('errors (before any write) for an unknown container', async () => { + const { runtime, calls } = fakeRuntime(); + + await expect( + placeContent({ + dotcms: runtime, + path: '/about-us', + slots: [{ slot: { container: 'nope-not-here' }, contentlets: ['x'] }] + }) + ).rejects.toThrow(/No slot.*uses container/i); + + expect(calls.some((c) => /\/content$/.test(c.path))).toBe(false); + }); + + it('applies multiple slot assignments in one write', async () => { + const { runtime, calls } = fakeRuntime(); + + await placeContent({ + dotcms: runtime, + path: '/about-us', + slots: [ + { slot: 1, contentlets: ['a2'], op: 'append' }, + { slot: { container: 'default', instance: '2' }, contentlets: ['b1'], op: 'set' } + ] + }); + + const body = postedBody(calls); + expect(slot(body, DEFAULT_CONTAINER, '1')).toEqual(['existing-a', 'a2']); + expect(slot(body, DEFAULT_CONTAINER, '2')).toEqual(['b1']); + // Untouched system slot preserved. + expect(slot(body, SYSTEM_CONTAINER, '1')).toEqual(['sys-x']); + }); + + it('mode "replace" clears every slot the caller does not set', async () => { + const { runtime, calls } = fakeRuntime(); + + await placeContent({ + dotcms: runtime, + path: '/about-us', + slots: [{ slot: 1, contentlets: ['only-a'], op: 'set' }], + mode: 'replace' + }); + + const body = postedBody(calls); + expect(slot(body, DEFAULT_CONTAINER, '1')).toEqual(['only-a']); + // Replace wipes the rest. + expect(slot(body, DEFAULT_CONTAINER, '2')).toEqual([]); + expect(slot(body, SYSTEM_CONTAINER, '1')).toEqual([]); + }); + + it('reports a before/after diff and flags a slot that lost content', async () => { + const { runtime } = fakeRuntime(); + + const manifest = await placeContent({ + dotcms: runtime, + path: '/about-us', + slots: [{ slot: 1, contentlets: [], op: 'set' }] // clears slot #1 + }); + + const slot1 = manifest.slots.find( + (s) => s.uuid === '1' && s.identifier === DEFAULT_CONTAINER + ); + expect(slot1).toMatchObject({ before: ['existing-a'], after: [], changed: true }); + expect(manifest.warnings.some((w) => /lost 1 contentlet.*existing-a/i.test(w))).toBe(true); + }); + + it('rejects an empty slots array', async () => { + const { runtime, calls } = fakeRuntime(); + + await expect( + placeContent({ dotcms: runtime, path: '/about-us', slots: [] }) + ).rejects.toThrow(/`slots` is required.*at least one/i); + + expect(calls.some((c) => /\/content$/.test(c.path))).toBe(false); + }); + + it('throws a clear error when the page is not found', async () => { + const { runtime } = fakeRuntime({ page: { entity: {} } }); + + await expect( + placeContent({ + dotcms: runtime, + path: '/nope', + slots: [{ slot: 1, contentlets: ['a'] }] + }) + ).rejects.toThrow(/not found/i); + }); + + it('translates a net-loss 409 into an actionable message', async () => { + const { runtime } = fakeRuntime({ + onPost: () => { + throw new HttpError(409, 'Conflict', 'net content loss exceeds threshold'); + } + }); + + await expect( + placeContent({ + dotcms: runtime, + path: '/about-us', + slots: [{ slot: 1, contentlets: ['a'] }] + }) + ).rejects.toThrow(/net-loss conflict.*Re-read the page and retry/i); + }); + + it('names the likely cause on a 400 instead of relaying the raw error', async () => { + // The most common failure of this tool in a placement loop. Without naming it, the + // model cannot tell that the fix is a different container or a different content + // type, so it retries the identical call and fails identically. + const { runtime } = fakeRuntime({ + onPost: () => { + throw new HttpError(400, 'Bad Request', 'invalid contentlet for container'); + } + }); + + await expect( + placeContent({ + dotcms: runtime, + path: '/about-us', + slots: [{ slot: 1, contentlets: ['a'] }] + }) + ).rejects.toThrow(/CONTENT TYPE is not permitted in the container/i); + }); + + it('does not report a non-409 as a conflict just because its body says "conflict"', async () => { + // The old test was a regex over the message, so any body containing "conflict" (or the + // digits 409 anywhere) was reported as a net-loss conflict — telling the model to + // re-read and retry a page that in fact hit a server error. + const { runtime } = fakeRuntime({ + onPost: () => { + throw new HttpError(500, 'Server Error', 'unexpected conflict in module 409x'); + } + }); + + await expect( + placeContent({ + dotcms: runtime, + path: '/about-us', + slots: [{ slot: 1, contentlets: ['a'] }] + }) + ).rejects.toThrow(/Failed to save page content/i); + }); + + it('passes variantName and languageId through to both the read and the write', async () => { + const { runtime, calls } = fakeRuntime(); + + await placeContent({ + dotcms: runtime, + path: '/about-us', + slots: [{ slot: 1, contentlets: ['a'] }], + variantName: 'my-variant', + languageId: 2 + }); + + const read = calls.find((c) => c.path.startsWith('/api/v1/page/json')); + const write = calls.find((c) => /\/content$/.test(c.path)); + expect((read?.query as Record<string, unknown>)?.language_id).toBe(2); + expect((write?.query as Record<string, unknown>)?.variantName).toBe('my-variant'); + expect((write?.query as Record<string, unknown>)?.language_id).toBe(2); + }); +}); diff --git a/core-web/apps/mcp-server/src/lib/page-place-content.ts b/core-web/apps/mcp-server/src/lib/page-place-content.ts new file mode 100644 index 000000000000..094d84643be4 --- /dev/null +++ b/core-web/apps/mcp-server/src/lib/page-place-content.ts @@ -0,0 +1,633 @@ +import { HttpError, type DotCMSRuntime } from '@dotcms/ai/runtime'; + +import { LayoutRow } from './page-common'; +import { normalizePagePath } from './page-path'; +import { resolveSite } from './resolve'; +import { errorMessage } from './runtime'; + +/** The default variant when the caller does not name one. */ +export const DEFAULT_VARIANT = 'DEFAULT'; +/** The default language id when the caller does not name one. */ +export const DEFAULT_LANGUAGE_ID = 1; + +/** How a slot's contentlet list is combined with the ids the caller supplies. */ +export type PlaceOp = 'append' | 'set' | 'remove'; + +/** Whether the tool merges into the page's current content or replaces it wholesale. */ +export type PlaceMode = 'merge' | 'replace'; + +/** + * How the caller points at a slot. Either the 1-based index into the page's real slots (in layout + * order), or a container reference. `instance` (the slot uuid) is optional ONLY when the named + * container occupies exactly one slot on the page; otherwise it is required to disambiguate. + */ +export type SlotAddress = number | { container: string; instance?: string }; + +/** One slot assignment in the multi-slot (`slots[]`) form. */ +export interface SlotAssignment { + slot: SlotAddress; + contentlets: string[]; + op?: PlaceOp; +} + +export interface PagePlaceContentOptions { + dotcms: DotCMSRuntime; + /** Page URL path (e.g. "/about-us"), host-qualified path, or a page identifier (UUID). */ + path: string; + /** + * Site hostname or identifier. Required unless `path` is host-qualified as + * `//hostname/path`; the qualified form supplies this value implicitly. + */ + site?: string; + /** + * One or more slot assignments applied in a single atomic write. Placing content in one slot is + * just an array of one: `[{ slot, contentlets }]`. + */ + slots: SlotAssignment[]; + /** Variant name. Default "DEFAULT". */ + variantName?: string; + /** Language id. Default 1. */ + languageId?: number; + /** + * "merge" (default) reads the page's current content and applies the ops on top, preserving every + * untouched slot. "replace" treats the supplied slots as the complete desired map — every other + * slot on the page is cleared. Both still validate slot existence and return a before/after diff. + */ + mode?: PlaceMode; +} + +/** The before/after picture of one slot the write touched or preserved. */ +export interface SlotResult { + /** Container key as the layout addresses it (path for file containers, id for db containers). */ + identifier: string; + /** Slot instance uuid. */ + uuid: string; + /** Contentlet ids in the slot before the write. */ + before: string[]; + /** Contentlet ids in the slot after the write. */ + after: string[]; + /** True when before/after differ. */ + changed: boolean; +} + +export interface PagePlaceContentManifest { + /** Identifier of the page whose content was written. */ + pageId: string; + /** Hostname of the site whose page was updated. */ + site: string; + /** The page's url path. */ + url: string; + /** Variant the write targeted. */ + variantName: string; + /** Language id the write targeted. */ + languageId: number; + /** The effective mode ("merge" or "replace"). */ + mode: PlaceMode; + /** Per-slot before/after for every slot on the page (touched and untouched). */ + slots: SlotResult[]; + /** Actionable notices: content loss, net-loss conflict, etc. Empty on a clean happy path. */ + warnings: string[]; +} + +/** A slot discovered on the page: its layout addressing + the content currently in it. */ +interface PageSlot { + /** Container key as the layout addresses it — this is what the POST body's `identifier` must be. */ + identifier: string; + /** Slot instance uuid, as the layout addresses it — the POST body's `uuid`. */ + uuid: string; + /** Contentlet identifiers currently in this slot, in order. */ + contentlets: string[]; +} + +/** + * Place content into a page's container slots without wiping the rest of the page. + * + * `POST /api/v1/page/{pageId}/content` is a FULL replacement of the page's container-to-contentlet + * map: any slot omitted from the body is emptied. A caller that means "add one contentlet to one + * slot" but sends only that slot silently clears every other slot. This wrapper absorbs that trap: + * it reads the page's current content first, applies the requested op(s) to the addressed slot(s) + * only, then POSTs the COMPLETE map back so untouched slots survive. + * + * It also removes the discovery friction: the caller addresses a slot by index or container name + * and we resolve it to the exact `identifier`+`uuid` the endpoint needs — sourced from the page's + * real layout, so a typo fails clearly instead of silently doing nothing. The returned manifest + * gives a before/after diff per slot and flags any slot that lost content. + */ +export async function placeContent( + options: PagePlaceContentOptions +): Promise<PagePlaceContentManifest> { + const mode: PlaceMode = options.mode ?? 'merge'; + const variantName = options.variantName ?? DEFAULT_VARIANT; + const languageId = options.languageId ?? DEFAULT_LANGUAGE_ID; + const assignments = validateAssignments(options.slots); + const target = await resolvePageTarget(options.dotcms, options.path, options.site); + + // Read the page's current content. This is the whole point of merge mode, but replace mode needs + // it too — to validate the addressed slots exist and to build the before/after diff. + const { pageId, url, slots } = await loadPageSlots( + options.dotcms, + target.path, + target.siteId, + languageId, + variantName + ); + + // Index the real slots two ways so a caller can address either by 1-based layout order or by + // container (+optional instance uuid). Resolution validates existence and disambiguates. + const resolved = assignments.map((assignment) => ({ + assignment, + target: resolveSlot(assignment.slot, slots) + })); + + // Start from the page's current map. In merge mode we mutate the addressed slots in place; in + // replace mode we start empty (every slot cleared) and set only what the caller specifies. + const desired = new Map<string, string[]>(); + for (const slot of slots) { + desired.set( + slotKey(slot.identifier, slot.uuid), + mode === 'replace' ? [] : [...slot.contentlets] + ); + } + + for (const { assignment, target } of resolved) { + const key = slotKey(target.identifier, target.uuid); + const current = desired.get(key) ?? []; + desired.set(key, applyOp(current, assignment.contentlets, assignment.op ?? 'append')); + } + + // The POST body is the FULL array — every slot on the page, with its desired contents. + const body = slots.map((slot) => ({ + identifier: slot.identifier, + uuid: slot.uuid, + contentletsId: desired.get(slotKey(slot.identifier, slot.uuid)) ?? [] + })); + + await postContent(options.dotcms, pageId, body, variantName, languageId); + + // Build the diff and warnings from before (page's current) vs after (what we sent). + const warnings: string[] = []; + const slotResults: SlotResult[] = slots.map((slot) => { + const after = desired.get(slotKey(slot.identifier, slot.uuid)) ?? []; + const before = slot.contentlets; + const lost = before.filter((id) => !after.includes(id)); + if (lost.length > 0) { + warnings.push( + `Slot ${slot.identifier} [uuid ${slot.uuid}] lost ${lost.length} contentlet(s): ` + + `${lost.join(', ')}.` + ); + } + return { + identifier: slot.identifier, + uuid: slot.uuid, + before, + after, + changed: !sameOrder(before, after) + }; + }); + + return { + pageId, + site: target.hostname, + url, + variantName, + languageId, + mode, + slots: slotResults, + warnings + }; +} + +/** Resolve explicit or host-qualified page targeting without ever falling back to a default site. */ +async function resolvePageTarget( + dotcms: DotCMSRuntime, + rawPath: string, + explicitSite?: string +): Promise<{ path: string; siteId: string; hostname: string }> { + const trimmed = rawPath.trim(); + const qualified = /^\/\/([^/]+)(\/.*)?$/.exec(trimmed); + const qualifiedHost = qualified?.[1]; + const pagePath = qualified ? qualified[2] || '/' : trimmed; + const requestedSite = explicitSite?.trim(); + + if (!requestedSite && !qualifiedHost) { + throw new Error( + '`site` is required for page placement when `path` is not host-qualified. ' + + 'Pass a site hostname/identifier or use `//hostname/path`; refusing to choose the ' + + 'default site prevents writes to the wrong page on multi-site instances.' + ); + } + + // Resolved through the shared resolver, which falls back to a direct lookup rather than + // treating an empty session cache as proof a site does not exist. Each resolver throws + // its own explanatory error, so an unknown site still fails here — just for the right + // reason, and before any request is made. + const explicit = requestedSite ? await resolveSite(dotcms, requestedSite) : undefined; + const embedded = qualifiedHost ? await resolveSite(dotcms, qualifiedHost) : undefined; + + if (explicit && embedded && explicit.identifier !== embedded.identifier) { + throw new Error( + `Conflicting sites: path targets "${qualifiedHost}" but site targets ` + + `"${requestedSite}". Pass one site consistently; no request was made.` + ); + } + + const site = explicit ?? embedded; + // The branches above make this unreachable, but keep the guard for type safety and future edits. + if (!site) { + throw new Error('Page placement site could not be resolved.'); + } + + // The manifest must echo the path actually operated on, not the raw input. + const normalizedPath = normalizePagePath(pagePath); + + return { path: normalizedPath, siteId: site.identifier, hostname: site.hostname }; +} + +/** + * `slots` is required and must be non-empty. The tool schema also guards this, but the lib is + * called directly from tests, so it validates too. + */ +function validateAssignments(slots: SlotAssignment[] | undefined): SlotAssignment[] { + if (!slots || slots.length === 0) { + throw new Error('`slots` is required and must contain at least one slot assignment.'); + } + return slots; +} + +/** Apply an op to a slot's current contentlet list, preserving order and de-duplicating. */ +function applyOp(current: string[], incoming: string[], op: PlaceOp): string[] { + switch (op) { + case 'set': + return dedupe(incoming); + case 'remove': { + const remove = new Set(incoming); + return current.filter((id) => !remove.has(id)); + } + case 'append': + default: + return dedupe([...current, ...incoming]); + } +} + +function dedupe(ids: string[]): string[] { + const seen = new Set<string>(); + const out: string[] = []; + for (const id of ids) { + if (id && !seen.has(id)) { + seen.add(id); + out.push(id); + } + } + return out; +} + +function sameOrder(a: string[], b: string[]): boolean { + return a.length === b.length && a.every((v, i) => v === b[i]); +} + +/** A stable key for a slot: a container can appear multiple times, so the uuid is part of it. */ +function slotKey(identifier: string, uuid: string): string { + return `${identifier} ${uuid}`; +} + +/** + * Resolve a caller-supplied slot address to a concrete slot on the page. Fails with the list of + * valid slots when the address does not match, and (for a container address with no instance) with + * the list of instances when the container occupies more than one slot. + */ +function resolveSlot(address: SlotAddress, slots: PageSlot[]): PageSlot { + if (typeof address === 'number') { + const index = address - 1; // 1-based, in layout order. + if (!Number.isInteger(address) || index < 0 || index >= slots.length) { + throw new Error( + `Slot index ${address} is out of range. The page has ${slots.length} slot(s): ` + + `${describeSlots(slots)}.` + ); + } + return slots[index]; + } + + const wanted = address.container.trim(); + // Rank-filtered, like `findContainer`: keep only the slots matching at the BEST rank, so + // an exact/path-boundary hit on `.../default/` is never diluted by a loose substring hit on + // `.../default-banner/`. Without this, naming one container could report "appears in 2 + // slots, pass slot.instance" about two DIFFERENT containers. + const bestRank = slots.reduce( + (rank, slot) => Math.max(rank, containerMatchRank(slot.identifier, wanted)), + MATCH_NONE + ); + const matches = + bestRank === MATCH_NONE + ? [] + : slots.filter((slot) => containerMatchRank(slot.identifier, wanted) === bestRank); + + if (matches.length === 0) { + throw new Error( + `No slot on the page uses container "${address.container}". Available slots: ` + + `${describeSlots(slots)}.` + ); + } + + if (address.instance !== undefined) { + const exact = matches.find((slot) => slot.uuid === String(address.instance)); + if (!exact) { + const instances = matches.map((slot) => `'${slot.uuid}'`).join(', '); + throw new Error( + `Container "${address.container}" has no slot with instance '${address.instance}'. ` + + `Available instances: ${instances}.` + ); + } + return exact; + } + + if (matches.length > 1) { + const instances = matches.map((slot) => `'${slot.uuid}'`).join(', '); + throw new Error( + `Container "${address.container}" appears in ${matches.length} slots ` + + `(instances: ${instances}). Pass slot.instance to disambiguate.` + ); + } + + return matches[0]; +} + +/** + * How well a layout container key matches what the caller asked for. Higher is better; + * `NONE` means no match at all. + * + * Ranked rather than boolean, because the previous "either string contains the other" test + * made resolution ORDER-DEPENDENT and therefore non-deterministic from the caller's side: a + * layout holding both `.../containers/default/` and `.../containers/default-banner/` would + * resolve a request for `.../default/` to whichever appeared FIRST in the object. Object keys + * iterate in insertion order, so which container won depended on layout authoring order — and + * in `merge` mode the tool would read the banner's contentlet list and write the merged result + * back under it, putting content in the wrong container and potentially replacing the banner's + * own contents. + */ +const MATCH_NONE = 0; +/** One string contains the other anywhere — the loosest, most ambiguous signal. */ +const MATCH_SUBSTRING = 1; +/** The key ends with the wanted value on a `/` boundary, e.g. `.../containers/default/`. */ +const MATCH_PATH_SUFFIX = 2; +/** Byte-identical (case-insensitively). */ +const MATCH_EXACT = 3; + +function containerMatchRank(identifier: string, wanted: string): number { + const key = identifier.toLowerCase(); + const want = wanted.toLowerCase(); + + if (key === want) { + return MATCH_EXACT; + } + + // Compare on `/`-delimited boundaries so `default` cannot match `default-banner`. Both + // sides are normalised for a trailing slash first, since container paths carry one and + // callers routinely omit it. + const keyTrimmed = key.replace(/\/+$/, ''); + const wantTrimmed = want.replace(/\/+$/, ''); + if (keyTrimmed === wantTrimmed || keyTrimmed.endsWith(`/${wantTrimmed}`)) { + return MATCH_PATH_SUFFIX; + } + + if (key.includes(want) || want.includes(key)) { + return MATCH_SUBSTRING; + } + + return MATCH_NONE; +} + +/** + * The single best-matching key, or a hard error when the choice is genuinely ambiguous. + * + * Failing loudly with the candidates beats silently picking one: writing to the wrong + * container is not recoverable by the caller, whereas an error naming both candidates tells + * them exactly what to disambiguate with. + */ +function bestContainerKey(keys: string[], wanted: string): string | undefined { + let bestRank = MATCH_NONE; + let best: string[] = []; + + for (const key of keys) { + const rank = containerMatchRank(key, wanted); + if (rank === MATCH_NONE || rank < bestRank) { + continue; + } + if (rank > bestRank) { + bestRank = rank; + best = [key]; + } else { + best.push(key); + } + } + + if (best.length === 0) { + return undefined; + } + + if (best.length > 1) { + throw new Error( + `Container "${wanted}" is ambiguous — it matches ${best.length} containers on this ` + + `page equally well: ${best.join(', ')}. Pass the full container path or id to ` + + `disambiguate; guessing here could write content into the wrong container.` + ); + } + + return best[0]; +} + +function describeSlots(slots: PageSlot[]): string { + if (slots.length === 0) return '(none)'; + return slots.map((slot, i) => `#${i + 1} ${slot.identifier} [uuid ${slot.uuid}]`).join('; '); +} + +interface PageJsonResponse { + entity?: { + page?: { identifier?: string; pageURI?: string; pageUrl?: string; url?: string }; + containers?: Record<string, RawContainer>; + layout?: { body?: { rows?: LayoutRow[] } }; + }; +} + +interface RawContainer { + contentlets?: Record<string, Array<{ identifier?: string; inode?: string }>>; +} + +/** + * Fetch the page's current content and flatten it into the ordered list of slots we operate on. + * + * The slot order and the authoritative identifier+uuid come from `layout.body.rows[].columns[] + * .containers[]` — this is exactly what the endpoint expects back. The current contentlet ids come + * from `containers[identifier].contentlets[uuid]`, matched tolerantly because the layout key and the + * containers-map key can differ (shorty vs full id, host-relative vs host-qualified path) and the + * contentlets map is historically keyed as either "1" or "uuid-1". + */ +async function loadPageSlots( + dotcms: DotCMSRuntime, + path: string, + siteId: string, + languageId: number, + variantName: string +): Promise<{ pageId: string; url: string; slots: PageSlot[] }> { + // Normalized before interpolation — see lib/page-path.ts for why the raw form is unsafe. + const uri = normalizePagePath(path); + // Read the SAME variant the write targets. Omitting `variantName` here reads DEFAULT, + // so in `merge` mode on a non-DEFAULT variant the "before" slot map and the + // untouched-slot preservation would be computed from DEFAULT and then written into the + // target variant — clobbering its real contents and reporting a bogus before/after diff. + const response = (await dotcms.request({ + path: `/api/v1/page/json${uri}`, + query: { variantName, language_id: languageId, host_id: siteId } + })) as PageJsonResponse; + + const entity = response.entity; + if (!entity || !entity.page) { + throw new Error( + `Page "${path}" was not found (no page at this url for language ${languageId}).` + ); + } + + const pageId = entity.page.identifier; + if (!pageId) { + throw new Error(`Page "${path}" resolved but has no identifier.`); + } + const url = entity.page.pageURI ?? entity.page.pageUrl ?? entity.page.url ?? uri; + + const containers = entity.containers ?? {}; + const rows = entity.layout?.body?.rows ?? []; + + const slots: PageSlot[] = []; + for (const row of rows) { + for (const column of row.columns ?? []) { + for (const container of column.containers ?? []) { + const identifier = container.identifier; + const uuid = container.uuid; + if (!identifier || !uuid) { + continue; + } + slots.push({ + identifier, + uuid, + contentlets: currentContentlets(containers, identifier, uuid) + }); + } + } + } + + return { pageId, url, slots }; +} + +/** + * Read the contentlet ids currently in a slot from the containers map. The layout `identifier` may + * not be byte-identical to the containers-map key, and the per-slot key may be "uuid" or "uuid-N", + * so both lookups are tolerant. + */ +function currentContentlets( + containers: Record<string, RawContainer>, + identifier: string, + uuid: string +): string[] { + const raw = findContainer(containers, identifier); + if (!raw?.contentlets) { + return []; + } + + const map = raw.contentlets; + const list = + map[uuid] ?? + map[`uuid-${uuid}`] ?? + map[stripUuidPrefix(uuid)] ?? + // Last resort: a key that ends with the uuid (covers other prefixings). + map[Object.keys(map).find((k) => stripUuidPrefix(k) === stripUuidPrefix(uuid)) ?? '']; + + if (!Array.isArray(list)) { + return []; + } + return list.map((c) => c.identifier).filter((id): id is string => Boolean(id)); +} + +function stripUuidPrefix(uuid: string): string { + return uuid.startsWith('uuid-') ? uuid.slice('uuid-'.length) : uuid; +} + +function findContainer( + containers: Record<string, RawContainer>, + identifier: string +): RawContainer | undefined { + if (containers[identifier]) { + return containers[identifier]; + } + const key = bestContainerKey(Object.keys(containers), identifier); + + return key ? containers[key] : undefined; +} + +/** + * POST the full container map, translating the two documented non-200 outcomes into actionable + * messages: 409 is the net-loss conflict (someone else changed the page, or the write would + * remove too much), and a 400 usually means a contentlet's type is not allowed in its container. + * + * Both branches are keyed on `HttpError.status`, not on the text of the message. The 409 used to + * be sniffed with `/\b409\b|net content loss|conflict/i`, which both over-matched (any response + * body happening to contain "409" or "conflict") and under-matched (a real 409 whose body says + * neither). `cause` is threaded through so the original typed error — and with it `code` and + * `status` — still reaches the tool boundary instead of being flattened away here. + */ +async function postContent( + dotcms: DotCMSRuntime, + pageId: string, + body: Array<{ identifier: string; uuid: string; contentletsId: string[] }>, + variantName: string, + languageId: number +): Promise<void> { + try { + await dotcms.request({ + method: 'POST', + path: `/api/v1/page/${encodeURIComponent(pageId)}/content`, + query: { variantName, language_id: languageId }, + body + }); + } catch (error) { + const message = errorMessage(error); + + if (error instanceof HttpError && error.status === 409) { + throw withCause( + 'Page content save was rejected as a net-loss conflict (the change would remove more ' + + 'content than allowed, or the page changed underneath this write). Re-read the page ' + + `and retry. Original error: ${message}`, + error + ); + } + + // The 400 branch the docblock has always promised but never had. This is the single + // most common failure of this tool in a placement loop, and without naming the cause + // the model cannot tell that the fix is a different container or a different content + // type — so it retries the identical call and fails identically. + if (error instanceof HttpError && error.status === 400) { + throw withCause( + 'Page content save was rejected (HTTP 400). The usual cause is a contentlet whose ' + + 'CONTENT TYPE is not permitted in the container it was placed in — check the ' + + "container's allowed content types and either place a permitted type or choose " + + 'a different container. Retrying this same call unchanged will fail the same ' + + `way. Original error: ${message}`, + error + ); + } + + throw withCause(`Failed to save page content: ${message}`, error); + } +} + +/** + * An `Error` carrying the original as `cause`. + * + * Written by assignment rather than `new Error(msg, { cause })` because that constructor + * overload needs the ES2022 lib and this project targets lower. Threading the cause matters: + * without it the typed `HttpError` — and with it `code` and `status` — is flattened to a + * message here and can never reach the tool boundary that reports `retryable`. + */ +function withCause(message: string, cause: unknown): Error { + const error = new Error(message); + (error as Error & { cause?: unknown }).cause = cause; + + return error; +} diff --git a/core-web/apps/mcp-server/src/lib/page-verify.spec.ts b/core-web/apps/mcp-server/src/lib/page-verify.spec.ts new file mode 100644 index 000000000000..c7829f2bd55f --- /dev/null +++ b/core-web/apps/mcp-server/src/lib/page-verify.spec.ts @@ -0,0 +1,355 @@ +import { HttpError, type DotCMSRuntime, type RequestOptions } from '@dotcms/ai/runtime'; + +import { buildManifest, MAX_INCLUDED_HTML_CHARS, verifyPage } from './page-verify'; + +const DEFAULT_CONTAINER = '//demo.dotcms.com/application/containers/default/'; + +/** + * A render response with two slots on the default container (uuids "1" and "2"). Callers override + * the per-slot rendered HTML / contentlets and the page.rendered to exercise each verdict. + */ +function renderResponse(opts: { + pageRendered?: string; + slot1Html?: string; + slot1Content?: number; + slot2Html?: string; + slot2Content?: number; + urlContentMap?: { identifier?: string } | null; +}) { + const contentlets = (n: number) => + Array.from({ length: n }, (_, i) => ({ identifier: `c-${i}` })); + + return { + entity: { + page: { rendered: opts.pageRendered ?? '<html>ok</html>', pageURI: '/about-us' }, + containers: { + [DEFAULT_CONTAINER]: { + rendered: { + '1': opts.slot1Html ?? '', + '2': opts.slot2Html ?? '' + }, + contentlets: { + '1': contentlets(opts.slot1Content ?? 0), + '2': contentlets(opts.slot2Content ?? 0) + } + } + }, + layout: { + body: { + rows: [ + { + columns: [ + { + containers: [ + { identifier: DEFAULT_CONTAINER, uuid: '1' }, + { identifier: DEFAULT_CONTAINER, uuid: '2' } + ] + } + ] + } + ] + } + }, + ...(opts.urlContentMap !== undefined ? { urlContentMap: opts.urlContentMap } : {}) + } + }; +} + +function manifestFrom( + body: ReturnType<typeof renderResponse>, + over?: { status?: number; mode?: 'LIVE' | 'WORKING'; includeHtml?: boolean } +) { + return buildManifest({ + path: '/about-us', + uri: '/about-us', + siteLabel: 'demo.dotcms.com', + mode: over?.mode ?? 'LIVE', + languageId: 1, + status: over?.status ?? 200, + body, + includeHtml: over?.includeHtml + }); +} + +function slot(m: ReturnType<typeof manifestFrom>, uuid: string) { + return m.slots.find((s) => s.uuid === uuid); +} + +describe('buildManifest verdicts', () => { + it('verdict "ok" when the slot and the page both render', () => { + // Both slots must render for the "all slots ok" diagnosis; an empty slot #2 would (correctly) + // steer the diagnosis to the empty-no-content branch. + const m = manifestFrom( + renderResponse({ + slot1Html: '<div>hello</div>', + slot1Content: 1, + slot2Html: '<div>world</div>', + slot2Content: 1 + }) + ); + expect(slot(m, '1')).toMatchObject({ rendered: true, verdict: 'ok', contentCount: 1 }); + expect(slot(m, '2')).toMatchObject({ rendered: true, verdict: 'ok' }); + expect(m.pageRendered).toBe(true); + expect(m.diagnosis).toMatch(/rendered successfully/i); + }); + + it('verdict "empty-vtl-error" when content is placed but the slot rendered empty', () => { + const m = manifestFrom(renderResponse({ slot1Html: ' ', slot1Content: 2 })); + expect(slot(m, '1')).toMatchObject({ + rendered: false, + verdict: 'empty-vtl-error', + contentCount: 2 + }); + expect(m.warnings.some((w) => /vtl.*failed.*\/api\/vtl\/dynamic/i.test(w))).toBe(true); + expect(m.diagnosis).toMatch(/VTL error.*\/api\/vtl\/dynamic/i); + }); + + it('verdict "empty-no-content" when the slot resolved but nothing is placed', () => { + const m = manifestFrom(renderResponse({ slot1Html: '', slot1Content: 0 })); + expect(slot(m, '1')).toMatchObject({ + rendered: false, + verdict: 'empty-no-content', + contentCount: 0 + }); + expect(m.warnings.some((w) => /no content.*page_place_content/i.test(w))).toBe(true); + }); + + it('verdict "cache-stale" when the slot renders but page.rendered is empty', () => { + const m = manifestFrom( + renderResponse({ pageRendered: '', slot1Html: '<div>hi</div>', slot1Content: 1 }) + ); + expect(slot(m, '1')).toMatchObject({ rendered: true, verdict: 'cache-stale' }); + expect(m.pageRendered).toBe(false); + expect(m.diagnosis).toMatch(/cache is stale.*cachettl.*re-publish/i); + }); + + it('verdict "not-assembled" when distinctive slot HTML is absent from a non-empty page', () => { + const slotHtml = '<section class="awazon-book-grid"><h2>Featured books</h2></section>'; + const shell = `<html><head><title>Awazon
${'x'.repeat(3000)}`; + const m = manifestFrom( + renderResponse({ pageRendered: shell, slot1Html: slotHtml, slot1Content: 4 }) + ); + + expect(m.pageBytes).toBeGreaterThan(slot(m, '1')?.bytes ?? 0); + expect(slot(m, '1')?.verdict).toBe('not-assembled'); + expect(m.warnings.some((warning) => /layout loop.*column\.draw/i.test(warning))).toBe(true); + expect(m.diagnosis).toMatch(/absent from the assembled page/i); + }); + + it('recognizes assembled content despite whitespace and entity encoding changes', () => { + const m = manifestFrom( + renderResponse({ + slot1Html: + '

Books & stories for everyone

', + slot1Content: 1, + pageRendered: + '
\n

Books & stories for everyone

' + }) + ); + + expect(slot(m, '1')?.verdict).toBe('ok'); + }); + + it('does not invent not-assembled when the slot has no reliable distinctive evidence', () => { + const m = manifestFrom( + renderResponse({ + slot1Html: '
hi
', + slot1Content: 1, + pageRendered: 'shell' + }) + ); + + expect(slot(m, '1')?.verdict).toBe('ok'); + }); + + it('flags 200-but-empty as a swallowed #dotParse error (200 != rendered)', () => { + const m = manifestFrom( + renderResponse({ pageRendered: ' \n ', slot1Html: '', slot1Content: 0 }) + ); + expect(m.httpStatus).toBe(200); + expect(m.pageRendered).toBe(false); + expect(m.warnings.some((w) => /200.*swallowed|#dotParse/i.test(w))).toBe(true); + }); + + it('reports byte length per slot and for the page', () => { + const m = manifestFrom(renderResponse({ slot1Html: 'abcde', slot1Content: 1 })); + expect(slot(m, '1')?.bytes).toBe(5); + expect(m.pageBytes).toBeGreaterThan(0); + }); + + it('includes bounded assembled HTML only when requested', () => { + const longHtml = `${'x'.repeat(MAX_INCLUDED_HTML_CHARS + 50)}`; + const omitted = manifestFrom(renderResponse({ pageRendered: longHtml })); + const included = manifestFrom(renderResponse({ pageRendered: longHtml }), { + includeHtml: true + }); + + expect(omitted.html).toBeUndefined(); + expect(included.html).toMatchObject({ + totalChars: longHtml.length, + truncated: true, + limit: MAX_INCLUDED_HTML_CHARS + }); + expect(included.html?.content).toHaveLength(MAX_INCLUDED_HTML_CHARS); + }); + + it('enumerates slots in layout order', () => { + const m = manifestFrom(renderResponse({ slot1Html: 'x', slot1Content: 1 })); + expect(m.slots.map((s) => s.uuid)).toEqual(['1', '2']); + }); + + it('a non-200 render is a verdict, not a crash', () => { + const m = manifestFrom(renderResponse({}), { status: 404 }); + expect(m.httpStatus).toBe(404); + expect(m.diagnosis).toMatch(/HTTP 404.*did not render/i); + }); + + it('WORKING mode that renders warns the result reflects unpublished edits', () => { + const m = manifestFrom(renderResponse({ slot1Html: '
draft
', slot1Content: 1 }), { + mode: 'WORKING' + }); + expect(m.warnings.some((w) => /WORKING.*unpublished.*LIVE/i.test(w))).toBe(true); + }); + + describe('urlMap', () => { + it('is null for a plain page (no urlContentMap field)', () => { + const m = manifestFrom(renderResponse({ slot1Html: 'x', slot1Content: 1 })); + expect(m.urlMap).toBeNull(); + }); + + it('reports resolved + contentletId for a URL-mapped detail page', () => { + const m = manifestFrom( + renderResponse({ + slot1Html: '
post
', + slot1Content: 1, + urlContentMap: { identifier: 'detail-123' } + }) + ); + expect(m.urlMap).toEqual({ resolved: true, contentletId: 'detail-123' }); + }); + }); +}); + +describe('verifyPage', () => { + const DEMO_SITE = { + identifier: 'site-uuid-1', + hostname: 'demo.dotcms.com', + isDefault: true, + archived: false, + live: true + }; + const OTHER_SITE = { + identifier: 'site-uuid-2', + hostname: 'other.example.com', + isDefault: false, + archived: false, + live: true + }; + + function fakeRuntime(over?: { render?: unknown; sites?: unknown[]; renderThrows?: unknown }) { + const calls: Array<{ path: string; query?: unknown }> = []; + const request = jest.fn(async (options: RequestOptions) => { + calls.push({ path: options.path, query: options.query }); + if (options.path.startsWith('/api/v1/page/render')) { + if (over?.renderThrows) { + throw over.renderThrows; + } + return ( + over?.render ?? renderResponse({ slot1Html: '
x
', slot1Content: 1 }) + ); + } + return {}; + }); + const loadContext = jest.fn(async () => ({ + contentTypes: [], + sites: over?.sites ?? [DEMO_SITE, OTHER_SITE], + languages: [], + currentUser: null + })); + return { runtime: { request, loadContext } as unknown as DotCMSRuntime, calls }; + } + + it('renders the default site with NO host_id when site is omitted', async () => { + const { runtime, calls } = fakeRuntime(); + + const m = await verifyPage({ dotcms: runtime, path: '/about-us' }); + + const render = calls.find((c) => c.path.startsWith('/api/v1/page/render')); + expect((render?.query as Record)?.host_id).toBeUndefined(); + expect((render?.query as Record)?.mode).toBe('LIVE'); + expect(m.site).toBe('(default)'); + }); + + it('resolves a hostname to host_id for a non-default site', async () => { + const { runtime, calls } = fakeRuntime(); + + const m = await verifyPage({ + dotcms: runtime, + path: '/about-us', + site: 'other.example.com' + }); + + const render = calls.find((c) => c.path.startsWith('/api/v1/page/render')); + expect((render?.query as Record)?.host_id).toBe('site-uuid-2'); + expect(m.site).toBe('other.example.com'); + }); + + it('accepts a site passed as its identifier directly', async () => { + const { runtime, calls } = fakeRuntime(); + + await verifyPage({ dotcms: runtime, path: '/x', site: 'site-uuid-2' }); + + const render = calls.find((c) => c.path.startsWith('/api/v1/page/render')); + expect((render?.query as Record)?.host_id).toBe('site-uuid-2'); + }); + + it('throws a clear error for an unknown site', async () => { + const { runtime } = fakeRuntime(); + + await expect( + verifyPage({ dotcms: runtime, path: '/x', site: 'nope.example.com' }) + ).rejects.toThrow(/not found.*hostname.*identifier/i); + }); + + it('passes languageId and mode through to the render call', async () => { + const { runtime, calls } = fakeRuntime(); + + await verifyPage({ dotcms: runtime, path: '/x', languageId: 2, mode: 'WORKING' }); + + const render = calls.find((c) => c.path.startsWith('/api/v1/page/render')); + expect((render?.query as Record)?.language_id).toBe(2); + expect((render?.query as Record)?.mode).toBe('WORKING'); + }); + + it('surfaces a 404 render as a manifest verdict, not a throw', async () => { + const { runtime } = fakeRuntime({ + renderThrows: new HttpError(404, 'Not Found', 'not found') + }); + + const m = await verifyPage({ dotcms: runtime, path: '/missing' }); + expect(m.httpStatus).toBe(404); + expect(m.diagnosis).toMatch(/HTTP 404/); + }); + + it('rethrows a transport failure instead of inventing a status from its text', async () => { + // The status used to be scraped as the first three-digit run in the message, so + // `connect ETIMEDOUT 10.0.0.5:443` became status 443 — and the manifest then stated + // flatly that the page did not render and to check the path, site and existence. + // The model's next move is to "fix" a page that is almost certainly fine. + const { runtime } = fakeRuntime({ + renderThrows: new Error('connect ETIMEDOUT 10.0.0.5:443') + }); + + await expect(verifyPage({ dotcms: runtime, path: '/about-us' })).rejects.toThrow( + /ETIMEDOUT/ + ); + }); + + it('produces an end-to-end ok verdict on a healthy page', async () => { + const { runtime } = fakeRuntime(); + + const m = await verifyPage({ dotcms: runtime, path: '/about-us' }); + expect(m.pageRendered).toBe(true); + expect(slot(m, '1')?.verdict).toBe('ok'); + }); +}); diff --git a/core-web/apps/mcp-server/src/lib/page-verify.ts b/core-web/apps/mcp-server/src/lib/page-verify.ts new file mode 100644 index 000000000000..16ce61625057 --- /dev/null +++ b/core-web/apps/mcp-server/src/lib/page-verify.ts @@ -0,0 +1,574 @@ +import { HttpError, type DotCMSRuntime } from '@dotcms/ai/runtime'; + +import { LayoutRow } from './page-common'; +import { normalizePagePath } from './page-path'; +import { resolveSite } from './resolve'; + +/** Render modes the verify tool supports. LIVE = published; WORKING = latest saved (pre-publish). */ +export type VerifyMode = 'LIVE' | 'WORKING'; + +/** The default render mode when the caller does not name one. */ +export const DEFAULT_MODE: VerifyMode = 'LIVE'; +/** The default language id when the caller does not name one. */ +export const DEFAULT_LANGUAGE_ID = 1; + +/** + * Per-slot verdict: + * - ok — the slot produced rendered HTML. + * - empty-vtl-error — content IS placed in the slot, but it rendered empty → the container/widget + * VTL failed (a #dotParse error swallowed to nothing). Fix lives in Layer 1: + * re-run that VTL through POST /api/vtl/dynamic to get the parse/eval error. + * - empty-no-content — the slot resolved but has no contentlets placed. A placement gap, not a code + * bug — use page_place_content to add content. + * - cache-stale — the slot rendered HTML, but the full page.rendered came back empty → a + * page-level cache problem. Set cachettl "0" and re-publish. + * - not-assembled — the slot rendered HTML in isolation, but distinctive evidence from that + * HTML is absent from the assembled page → the template/theme omitted it. + */ +export type SlotVerdict = + | 'ok' + | 'empty-vtl-error' + | 'empty-no-content' + | 'cache-stale' + | 'not-assembled'; + +export interface VerifySlotResult { + /** Container key as the layout addresses it (path for file containers, id for db containers). */ + container: string; + /** Slot instance uuid. */ + uuid: string; + /** Whether this slot produced non-empty rendered HTML. */ + rendered: boolean; + /** Byte length of this slot's rendered HTML (0 when empty). */ + bytes: number; + /** How many contentlets are placed in the slot (from the render response's contentlets map). */ + contentCount: number; + /** Classification of the slot's render outcome. */ + verdict: SlotVerdict; +} + +/** The $URLMapContent resolution outcome — only present for URL-mapped (detail) pages. */ +export interface UrlMapResult { + /** True when the path resolved to a concrete detail contentlet (vs. the no-match branch). */ + resolved: boolean; + /** The resolved contentlet identifier, when resolved. */ + contentletId?: string; +} + +export interface VerifyPageOptions { + dotcms: DotCMSRuntime; + /** Page URL path (e.g. "/about-us" or a URL-mapped detail slug like "/blog/my-post"). */ + path: string; + /** + * Site to render against — hostname or identifier (UUID). Optional: when omitted the instance's + * current/default site is used (no host_id sent). Only NON-default sites need this. + */ + site?: string; + /** Language id. Default 1. */ + languageId?: number; + /** Render mode. Default "LIVE" (published). "WORKING" checks the latest saved, pre-publish. */ + mode?: VerifyMode; + /** Include a bounded prefix of the assembled page HTML in the manifest. Default false. */ + includeHtml?: boolean; +} + +export interface VerifyHtmlResult { + /** Bounded prefix of the assembled page HTML. */ + content: string; + /** Total JavaScript character count before truncation. */ + totalChars: number; + /** Total UTF-8 byte count before truncation. */ + totalBytes: number; + /** True when content is only a prefix of the assembled HTML. */ + truncated: boolean; + /** Maximum number of characters returned in content. */ + limit: number; +} + +export interface VerifyPageManifest { + /** The path that was verified. */ + path: string; + /** The page's resolved url. */ + url: string; + /** The site the page rendered against (hostname when resolved, else "(default)"). */ + site: string; + /** The render mode used. */ + mode: VerifyMode; + /** Language id used. */ + languageId: number; + /** HTTP status of the render call (200 even when the body is empty — see pageRendered). */ + httpStatus: number; + /** True when page.rendered came back non-empty. A 200 with pageRendered=false is a swallowed error. */ + pageRendered: boolean; + /** Byte length of the full page.rendered HTML. */ + pageBytes: number; + /** Per-slot verdicts, in layout order. */ + slots: VerifySlotResult[]; + /** URL-map resolution for detail pages; null for a regular page. */ + urlMap: UrlMapResult | null; + /** Actionable notices derived from the verdicts. */ + warnings: string[]; + /** One-line summary plus the next action to take. */ + diagnosis: string; + /** Bounded assembled HTML, present only when includeHtml=true. */ + html?: VerifyHtmlResult; +} + +export const MAX_INCLUDED_HTML_CHARS = 20_000; + +/** + * Verify that a dotCMS page actually renders — the layer that catches a blank slot, a swallowed + * #dotParse error, a cache-stale page, or an unpublished edit, none of which a VTL-only check + * (/api/vtl/dynamic) can see because that runs in a request context, not a render context. + * + * Wraps GET /api/v1/page/render/{uri} and absorbs its sharp edges: + * - Host: the caller passes a hostname (or nothing); the tool resolves it to a host_id. host_id is + * required only for a NON-default site — omit `site` and the default site is used. + * - 200 != rendered: #dotParse swallows a VTL error into an empty HTTP 200, so a 200 with an empty + * body is a failure, surfaced as pageRendered=false, not success. + * - Two rendered layers that can disagree: each slot's rendered HTML (containers[].rendered[uuid]) + * vs. the assembled page.rendered. The disagreement IS the diagnosis (cache-stale) as opposed to + * an empty slot (VTL error or no content). + * + * Returns a structured verdict — per-slot classification plus a one-line diagnosis with the next + * action — instead of two JSON blobs the caller has to compare by hand. + */ +export async function verifyPage(options: VerifyPageOptions): Promise { + const mode: VerifyMode = options.mode ?? DEFAULT_MODE; + const languageId = options.languageId ?? DEFAULT_LANGUAGE_ID; + + // Resolve the site to a host_id ONLY when one was given. Absent → the backend uses the default + // site, and we send no host_id (the render endpoint's documented default behavior). + const resolvedSite = options.site ? await resolveSite(options.dotcms, options.site) : undefined; + + // Normalized BEFORE it reaches the request URL: `/a/../b` used to render `/b` while the + // manifest reported `/a/../b`, and a `#` silently truncated the path. + const uri = normalizePagePath(options.path); + + const query: Record = { + language_id: languageId, + mode + }; + if (resolvedSite) { + query.host_id = resolvedSite.identifier; + } + + const { status, body } = await renderPage(options.dotcms, uri, query); + + return buildManifest({ + path: options.path, + uri, + siteLabel: resolvedSite?.hostname ?? '(default)', + mode, + languageId, + status, + body, + includeHtml: options.includeHtml ?? false + }); +} + +interface RenderResponse { + entity?: { + page?: { rendered?: string; pageURI?: string; pageUrl?: string; url?: string }; + containers?: Record; + layout?: { body?: { rows?: LayoutRow[] } }; + urlContentMap?: { identifier?: string; inode?: string } | null; + }; +} + +interface RenderedContainer { + /** Per-slot rendered HTML, keyed by uuid (ContainerRendered.getRendered()). */ + rendered?: Record; + /** Per-slot placed contentlets, keyed by uuid (ContainerRaw.getContentletsMap()). */ + contentlets?: Record>; +} + +/** Assemble the verdict manifest from a render response. Pure — no I/O, so it is unit-testable. */ +export function buildManifest(input: { + path: string; + uri: string; + siteLabel: string; + mode: VerifyMode; + languageId: number; + status: number; + body: RenderResponse; + includeHtml?: boolean; +}): VerifyPageManifest { + const entity = input.body.entity ?? {}; + const page = entity.page ?? {}; + const containers = entity.containers ?? {}; + const rows = entity.layout?.body?.rows ?? []; + + const pageHtml = page.rendered ?? ''; + const pageBytes = byteLength(pageHtml); + const pageRendered = !isBlank(pageHtml); + const url = page.pageURI ?? page.pageUrl ?? page.url ?? input.uri; + + const slots: VerifySlotResult[] = []; + for (const row of rows) { + for (const column of row.columns ?? []) { + for (const layoutContainer of column.containers ?? []) { + const container = layoutContainer.identifier; + const uuid = layoutContainer.uuid; + if (!container || !uuid) { + continue; + } + slots.push(classifySlot(containers, container, uuid, pageHtml)); + } + } + } + + const urlMap = resolveUrlMap(entity.urlContentMap); + const warnings = collectWarnings(slots, pageRendered, input.status, urlMap, input.mode); + const diagnosis = diagnose(slots, pageRendered, input.status, urlMap, input.mode); + + const manifest: VerifyPageManifest = { + path: input.path, + url, + site: input.siteLabel, + mode: input.mode, + languageId: input.languageId, + httpStatus: input.status, + pageRendered, + pageBytes, + slots, + urlMap, + warnings, + diagnosis + }; + + if (input.includeHtml) { + manifest.html = { + content: pageHtml.slice(0, MAX_INCLUDED_HTML_CHARS), + totalChars: pageHtml.length, + totalBytes: pageBytes, + truncated: pageHtml.length > MAX_INCLUDED_HTML_CHARS, + limit: MAX_INCLUDED_HTML_CHARS + }; + } + + return manifest; +} + +/** Classify one slot from the render response. */ +function classifySlot( + containers: Record, + container: string, + uuid: string, + pageHtml: string +): VerifySlotResult { + const raw = findContainer(containers, container); + const html = lookupByUuid(raw?.rendered, uuid) ?? ''; + const contentCount = (lookupByUuid(raw?.contentlets, uuid) ?? []).length; + const rendered = !isBlank(html); + const pageRendered = !isBlank(pageHtml); + const bytes = byteLength(html); + + let verdict: SlotVerdict; + if (rendered) { + // The slot produced HTML. If the assembled page did NOT, that is a page-level cache problem. + if (!pageRendered) { + verdict = 'cache-stale'; + } else if (!slotEvidenceAppearsInPage(html, pageHtml)) { + verdict = 'not-assembled'; + } else { + verdict = 'ok'; + } + } else if (contentCount > 0) { + // Content is placed but rendered to nothing → the container/widget VTL failed. + verdict = 'empty-vtl-error'; + } else { + // Nothing placed → a placement gap, not a code bug. + verdict = 'empty-no-content'; + } + + return { container, uuid, rendered, bytes, contentCount, verdict }; +} + +function resolveUrlMap( + urlContentMap: { identifier?: string; inode?: string } | null | undefined +): UrlMapResult | null { + // The field is present (as null/absent) on every page; a non-null value with an identifier means + // the path resolved to a concrete detail contentlet via $URLMapContent. + if (urlContentMap === undefined) { + return null; + } + if (urlContentMap === null) { + // Present-but-null happens on URL-map-capable responses that did not resolve. We can't tell + // that apart from a plain page here, so treat absence of a match as "no url map". + return null; + } + const contentletId = urlContentMap.identifier; + return contentletId ? { resolved: true, contentletId } : { resolved: false }; +} + +function collectWarnings( + slots: VerifySlotResult[], + pageRendered: boolean, + status: number, + urlMap: UrlMapResult | null, + mode: VerifyMode +): string[] { + const warnings: string[] = []; + + if (status === 200 && !pageRendered) { + warnings.push( + 'HTTP 200 but page.rendered is empty — a #dotParse VTL error was likely swallowed into ' + + 'an empty body. 200 does NOT mean the page rendered.' + ); + } + + for (const slot of slots) { + if (slot.verdict === 'empty-vtl-error') { + warnings.push( + `Slot ${slot.container} [uuid ${slot.uuid}] has ${slot.contentCount} contentlet(s) ` + + 'placed but rendered empty — the container/widget VTL failed. Re-run that VTL ' + + 'through POST /api/vtl/dynamic to get the parse/eval error with line/column.' + ); + } else if (slot.verdict === 'empty-no-content') { + warnings.push( + `Slot ${slot.container} [uuid ${slot.uuid}] is empty — no content placed. Use ` + + 'page_place_content to add contentlets.' + ); + } else if (slot.verdict === 'cache-stale') { + warnings.push( + `Slot ${slot.container} [uuid ${slot.uuid}] rendered content but the page did not — ` + + 'page-level cache is stale. Set cachettl "0" and re-publish the page.' + ); + } else if (slot.verdict === 'not-assembled') { + warnings.push( + `Slot ${slot.container} [uuid ${slot.uuid}] rendered content, but its HTML is ` + + 'absent from the assembled page — the template/theme layout loop is not ' + + "emitting this container. Inspect the theme's row/column loop and use " + + '$render.eval($column.draw()).' + ); + } + } + + if (urlMap && !urlMap.resolved) { + warnings.push( + 'This looks like a URL-mapped page but the path did not resolve to a detail contentlet ' + + '($URLMapContent hit the no-match branch). Check the detail-page slug.' + ); + } + + if (mode === 'WORKING' && pageRendered) { + warnings.push( + 'Rendered in WORKING mode — this reflects unpublished edits. Re-verify with mode "LIVE" ' + + 'to confirm what the public actually sees.' + ); + } + + return warnings; +} + +/** Derive the one-line diagnosis + next action from the worst thing found. */ +function diagnose( + slots: VerifySlotResult[], + pageRendered: boolean, + status: number, + urlMap: UrlMapResult | null, + mode: VerifyMode +): string { + if (status !== 200) { + return `Render returned HTTP ${status}. The page did not render — check the path, site, and that the page exists in ${mode} mode.`; + } + + if (urlMap && !urlMap.resolved) { + return 'URL-mapped page did not resolve to a detail contentlet — the slug hit the no-match branch. Verify the detail-page slug exists and is published.'; + } + + const vtlError = slots.find((s) => s.verdict === 'empty-vtl-error'); + if (vtlError) { + return `Slot ${vtlError.container} [uuid ${vtlError.uuid}] has content but rendered empty (VTL error). Next: re-run that container's VTL through POST /api/vtl/dynamic to get the parse/eval error.`; + } + + const stale = slots.find((s) => s.verdict === 'cache-stale'); + if (stale || (!pageRendered && slots.some((s) => s.rendered))) { + return 'Slots rendered but the assembled page.rendered is empty — page-level cache is stale. Next: set cachettl "0" and re-publish the page.'; + } + + const notAssembled = slots.find((s) => s.verdict === 'not-assembled'); + if (notAssembled) { + return `Slot ${notAssembled.container} [uuid ${notAssembled.uuid}] rendered in isolation but is absent from the assembled page. Next: inspect the theme's row/column loop and ensure it emits $render.eval($column.draw()).`; + } + + if (!pageRendered) { + return `HTTP 200 but the page rendered empty (a swallowed #dotParse error, or nothing is placed). Next: check per-slot verdicts${mode === 'LIVE' ? ' and confirm the page is published' : ''}.`; + } + + const noContent = slots.filter((s) => s.verdict === 'empty-no-content'); + if (noContent.length > 0) { + return `Page rendered, but ${noContent.length} slot(s) are empty (no content placed). Next: use page_place_content to fill them if intended.`; + } + + // Zero slots is NOT a clean bill of health. `collectWarnings` iterates `slots`, so an + // empty list yields no warnings, and the sentence below would interpolate to "all 0 + // slot(s) produced content" — the tool built to catch blank slots declaring success over + // a page whose layout it could not read. Legacy/advanced templates, and any response with + // `entity.containers` populated but no `entity.layout.body.rows`, land exactly here. + if (slots.length === 0) { + return ( + `The page rendered in ${mode} mode, but NO slots could be parsed from its layout, ` + + `so per-slot verification did not run and this is not a clean result. The template ` + + `may be legacy/advanced (no layout.body.rows), or the response shape may be ` + + `unexpected. Next: inspect the page's template directly — do not read this as "the ` + + `page is fine".` + ); + } + + return `Page rendered successfully in ${mode} mode — all ${slots.length} slot(s) produced content.`; +} + +/** GET the render endpoint, capturing the HTTP status even on a non-2xx so verdicts can use it. */ +async function renderPage( + dotcms: DotCMSRuntime, + uri: string, + query: Record +): Promise<{ status: number; body: RenderResponse }> { + try { + const body = (await dotcms.request({ + path: `/api/v1/page/render${uri}`, + query + })) as RenderResponse; + return { status: 200, body }; + } catch (error) { + // A 404/403/etc. is a legitimate verify outcome, not a tool failure — surface it in the + // manifest with an empty body so the diagnosis explains the HTTP result. + // + // ONLY a real HttpError qualifies. The previous version fell back to scraping the + // first three-digit run out of the message, which turned any error containing a + // number into a fabricated verdict: `connect ETIMEDOUT 10.0.0.5:443` became status + // 443, and `buildManifest` then stated flatly that the page did not render and to + // check the path, site and existence — about a page that is very likely fine. The + // model's next move is to "fix" something that was never broken. A transport failure + // must surface AS a transport failure. + if (error instanceof HttpError) { + return { status: error.status, body: {} }; + } + throw error; + } +} + +/** + * The layout `identifier` may not be byte-identical to the containers-map key (shorty vs full id, + * host-relative vs host-qualified path). Match tolerantly, same as page_place_content. + */ +function findContainer( + containers: Record, + identifier: string +): RenderedContainer | undefined { + if (containers[identifier]) { + return containers[identifier]; + } + const key = Object.keys(containers).find((k) => containerMatches(k, identifier)); + return key ? containers[key] : undefined; +} + +function containerMatches(a: string, b: string): boolean { + if (a === b) return true; + const la = a.toLowerCase(); + const lb = b.toLowerCase(); + return la === lb || la.includes(lb) || lb.includes(la); +} + +/** The per-slot maps are historically keyed as "1" or "uuid-1"; look up tolerantly. */ +function lookupByUuid(map: Record | undefined, uuid: string): T | undefined { + if (!map) { + return undefined; + } + const direct = map[uuid] ?? map[`uuid-${uuid}`] ?? map[stripUuidPrefix(uuid)]; + if (direct !== undefined) { + return direct; + } + const key = Object.keys(map).find((k) => stripUuidPrefix(k) === stripUuidPrefix(uuid)); + return key ? map[key] : undefined; +} + +function stripUuidPrefix(uuid: string): string { + return uuid.startsWith('uuid-') ? uuid.slice('uuid-'.length) : uuid; +} + +/** + * Confirm assembly using several short, normalized pieces of evidence rather than an exact HTML + * substring. Theme assembly may rewrite whitespace and entity encoding, so exact matching would + * label healthy pages as broken. If no reliable evidence can be extracted, return true (unknown) + * instead of issuing a false failure. + */ +function slotEvidenceAppearsInPage(slotHtml: string, pageHtml: string): boolean { + const candidates = assemblyEvidence(slotHtml); + if (candidates.length === 0) { + return true; + } + const normalizedPage = normalizeEvidence(pageHtml); + return candidates.some((candidate) => normalizedPage.includes(candidate)); +} + +function assemblyEvidence(html: string): string[] { + const candidates: string[] = []; + const attributePattern = /\b(?:id|data-[\w:-]+)\s*=\s*["']([^"']+)["']/gi; + for (const match of html.matchAll(attributePattern)) { + addEvidence(candidates, match[1], 6); + } + + const classPattern = /\bclass\s*=\s*["']([^"']+)["']/gi; + for (const match of html.matchAll(classPattern)) { + for (const className of match[1].split(/\s+/)) { + addEvidence(candidates, className, 8); + } + } + + const visibleText = decodeEntities( + html + .replace(/]*>[\s\S]*?<\/script>/gi, ' ') + .replace(/]*>[\s\S]*?<\/style>/gi, ' ') + .replace(/<[^>]+>/g, ' ') + ); + for (const textRun of visibleText.split(/\s{2,}|[.!?]\s+/)) { + addEvidence(candidates, textRun, 12); + } + + return [...new Set(candidates)].slice(0, 12); +} + +function addEvidence(target: string[], raw: string, minimumLength: number): void { + const normalized = normalizeEvidence(raw); + if (normalized.length >= minimumLength) { + target.push(normalized); + } +} + +function normalizeEvidence(value: string): string { + return decodeEntities(value).replace(/\s+/g, ' ').trim().toLowerCase(); +} + +function decodeEntities(value: string): string { + const named: Record = { + amp: '&', + apos: "'", + gt: '>', + lt: '<', + nbsp: ' ', + quot: '"' + }; + return value.replace(/&(#x[0-9a-f]+|#\d+|[a-z]+);/gi, (entity, token: string) => { + if (token[0] !== '#') { + return named[token.toLowerCase()] ?? entity; + } + const hex = token[1]?.toLowerCase() === 'x'; + const codePoint = Number.parseInt(token.slice(hex ? 2 : 1), hex ? 16 : 10); + return Number.isFinite(codePoint) ? String.fromCodePoint(codePoint) : entity; + }); +} + +/** Empty or whitespace-only (comments/blank lines count as "not rendered" for verdict purposes). */ +function isBlank(html: string): boolean { + return html.trim().length === 0; +} + +function byteLength(html: string): number { + // Byte length, not char length — multibyte content should report its real size. + return typeof TextEncoder !== 'undefined' + ? new TextEncoder().encode(html).length + : Buffer.byteLength(html, 'utf8'); +} diff --git a/core-web/apps/mcp-server/src/lib/resolve.spec.ts b/core-web/apps/mcp-server/src/lib/resolve.spec.ts new file mode 100644 index 000000000000..40512abd1845 --- /dev/null +++ b/core-web/apps/mcp-server/src/lib/resolve.spec.ts @@ -0,0 +1,180 @@ +import { HttpError, type DotCMSRuntime, type RequestOptions } from '@dotcms/ai/runtime'; + +import { resolveLanguageId, resolveSite } from './resolve'; + +const DEMO_SITE = { + identifier: '48190c8c-42c4-46af-8d1a-0cd5db894797', + hostname: 'demo.dotcms.com', + isDefault: true, + archived: false, + live: true +}; + +/** + * A runtime whose context cache can be empty — the reported failure mode. Every loader inside + * `loadContext()` swallows its own error and returns `[]`, so one bad response during the + * single per-session load leaves the cache empty for the whole session. + */ +function fakeRuntime(options?: { + cachedSites?: unknown[]; + cachedLanguages?: unknown[]; + onRequest?: (options: RequestOptions) => unknown; + contextThrows?: boolean; +}) { + const calls: RequestOptions[] = []; + const request = jest.fn(async (opts: RequestOptions) => { + calls.push(opts); + + return options?.onRequest ? options.onRequest(opts) : {}; + }); + const loadContext = jest.fn(async () => { + if (options?.contextThrows) { + throw new Error('context boom'); + } + + return { + contentTypes: [], + sites: options?.cachedSites ?? [], + languages: options?.cachedLanguages ?? [], + currentUser: null + }; + }); + + return { runtime: { request, loadContext } as unknown as DotCMSRuntime, calls }; +} + +describe('resolveSite', () => { + it('uses the cache when it has the site, without any extra request', async () => { + const { runtime, calls } = fakeRuntime({ cachedSites: [DEMO_SITE] }); + + const site = await resolveSite(runtime, 'demo.dotcms.com'); + + expect(site).toEqual({ identifier: DEMO_SITE.identifier, hostname: 'demo.dotcms.com' }); + expect(calls).toHaveLength(0); + }); + + describe('when the session cache is empty (the reported failure)', () => { + // Reported from a real session: page_create / page_place_content / page_verify all + // failed with "Available sites: (none found)" — for the default site, and even when + // passed a correct site identifier. An empty cache is not evidence a site is missing. + it('resolves an identifier directly instead of rejecting it', async () => { + const { runtime, calls } = fakeRuntime({ + cachedSites: [], + onRequest: (opts) => + opts.path === `/api/v1/site/${DEMO_SITE.identifier}` + ? { entity: DEMO_SITE } + : {} + }); + + const site = await resolveSite(runtime, DEMO_SITE.identifier); + + expect(site.identifier).toBe(DEMO_SITE.identifier); + expect(site.hostname).toBe('demo.dotcms.com'); + expect(calls[0].path).toBe(`/api/v1/site/${DEMO_SITE.identifier}`); + }); + + it('resolves a hostname via the filtered site list', async () => { + const { runtime } = fakeRuntime({ + cachedSites: [], + onRequest: (opts) => (opts.path === '/api/v1/site' ? { entity: [DEMO_SITE] } : {}) + }); + + const site = await resolveSite(runtime, 'demo.dotcms.com'); + + expect(site.identifier).toBe(DEMO_SITE.identifier); + }); + + it('still resolves when loadContext itself throws', async () => { + const { runtime } = fakeRuntime({ + contextThrows: true, + onRequest: (opts) => (opts.path === '/api/v1/site' ? { entity: [DEMO_SITE] } : {}) + }); + + await expect(resolveSite(runtime, 'demo.dotcms.com')).resolves.toEqual({ + identifier: DEMO_SITE.identifier, + hostname: 'demo.dotcms.com' + }); + }); + + it('explains that the context did not load rather than claiming no sites exist', async () => { + const { runtime } = fakeRuntime({ cachedSites: [], onRequest: () => ({}) }); + + await expect(resolveSite(runtime, 'ghost.example.com')).rejects.toThrow( + /site list is empty[\s\S]*context load failed/i + ); + }); + }); + + it('does not accept a substring near-miss from the filtered list', async () => { + // The list endpoint filters by substring, so `demo` also returns `demo-backup`. + // Accepting a near-miss would write content to the wrong site. + const { runtime } = fakeRuntime({ + cachedSites: [], + onRequest: (opts) => + opts.path === '/api/v1/site' + ? { entity: [{ identifier: 'other-id', hostname: 'demo-backup.dotcms.com' }] } + : {} + }); + + await expect(resolveSite(runtime, 'demo.dotcms.com')).rejects.toThrow(/not found|empty/i); + }); + + it('reports a genuine miss with the sites it does know about', async () => { + const { runtime } = fakeRuntime({ cachedSites: [DEMO_SITE], onRequest: () => ({}) }); + + await expect(resolveSite(runtime, 'ghost.example.com')).rejects.toThrow( + /Available sites: demo\.dotcms\.com/ + ); + }); + + it('rethrows a non-404 lookup failure instead of reporting "not found"', async () => { + // A 500 while looking up a site says nothing about whether that site exists. + const { runtime } = fakeRuntime({ + cachedSites: [], + onRequest: () => { + throw new HttpError(500, 'Server Error', 'boom'); + } + }); + + await expect(resolveSite(runtime, DEMO_SITE.identifier)).rejects.toThrow(/500|boom/i); + }); +}); + +describe('resolveLanguageId', () => { + const EN = { id: 1, isoCode: 'en-us' }; + const ES = { id: 2, isoCode: 'es-es' }; + + it('accepts an id present in the cache', async () => { + const { runtime } = fakeRuntime({ cachedLanguages: [EN, ES] }); + await expect(resolveLanguageId(runtime, 2)).resolves.toBe(2); + }); + + it('rejects an id the instance does not have', async () => { + // dotCMS silently falls back to the default language rather than rejecting, so an + // unknown id would write to the WRONG language while the manifest echoed the id asked for. + const { runtime } = fakeRuntime({ cachedLanguages: [EN, ES] }); + await expect(resolveLanguageId(runtime, 12)).rejects.toThrow(/does not exist/i); + }); + + it('trusts the caller when the language list could not be loaded', async () => { + // Same rule as sites: an empty list is a failed load, not proof of absence. Refusing + // here would block every call for the rest of the session. + const { runtime } = fakeRuntime({ cachedLanguages: [], onRequest: () => ({}) }); + await expect(resolveLanguageId(runtime, 12)).resolves.toBe(12); + }); + + it('falls back to a live read when the cache is empty', async () => { + const { runtime } = fakeRuntime({ + cachedLanguages: [], + onRequest: (opts) => (opts.path === '/api/v2/languages' ? { entity: [EN, ES] } : {}) + }); + + await expect(resolveLanguageId(runtime, 12)).rejects.toThrow(/does not exist/i); + await expect(resolveLanguageId(runtime, 2)).resolves.toBe(2); + }); + + it("defaults to the instance's first language rather than a hardcoded 1", async () => { + const { runtime } = fakeRuntime({ cachedLanguages: [{ id: 7, isoCode: 'fr-fr' }] }); + await expect(resolveLanguageId(runtime, undefined)).resolves.toBe(7); + }); +}); diff --git a/core-web/apps/mcp-server/src/lib/resolve.ts b/core-web/apps/mcp-server/src/lib/resolve.ts new file mode 100644 index 000000000000..3a444adffeab --- /dev/null +++ b/core-web/apps/mcp-server/src/lib/resolve.ts @@ -0,0 +1,268 @@ +import { HttpError, type DotCMSRuntime } from '@dotcms/ai/runtime'; + +import { errorMessage } from './runtime'; + +/** + * Resolution of the instance references a caller can name: sites and languages. + * + * ONE RULE runs through this file: **the session context cache is a fast path, never a + * gate.** `loadContext()` loads sites/languages/content-types once per session, and every + * loader inside it catches its own failure and returns an empty array (see + * `sdk/ai/src/adapter/context.ts`). So a transient 500, an expired token, or a permission + * quirk during that one load leaves the cache permanently empty for the session — and the + * tools that gated on it then rejected every call with "Available sites: (none found)", + * including for the default site and for a caller passing a correct site IDENTIFIER. + * + * That failure mode is worse than the problem the cache was solving. A cached list is a + * useful accelerator and a good source of candidate names for an error message; it is not + * evidence that something does not exist. Every resolver here therefore tries the cache + * first, falls back to asking dotCMS directly, and only fails when the instance itself says + * no — at which point the message distinguishes "this does not exist" from "the session + * context never loaded". + */ + +/** A dotCMS identifier is a 36-char UUID; anything else a caller passes is a name. */ +const UUID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i; + +/** A resolved site: the identifier the API needs, plus the hostname for manifests. */ +export interface ResolvedSite { + identifier: string; + hostname: string; +} + +function looksLikeIdentifier(value: string): boolean { + return UUID_PATTERN.test(value.trim()); +} + +function asRecord(value: unknown): Record | undefined { + return value && typeof value === 'object' ? (value as Record) : undefined; +} + +function entityOf(response: unknown): unknown { + const record = asRecord(response); + + return record && 'entity' in record ? record['entity'] : response; +} + +function readString(record: Record | undefined, key: string): string | undefined { + const value = record?.[key]; + + return typeof value === 'string' && value ? value : undefined; +} + +/** Project a raw site payload onto {@link ResolvedSite}, or undefined if it has no identifier. */ +function toResolvedSite(raw: unknown): ResolvedSite | undefined { + const record = asRecord(raw); + const identifier = readString(record, 'identifier'); + if (!identifier) { + return undefined; + } + + return { + identifier, + hostname: + readString(record, 'hostname') ?? + readString(record, 'hostName') ?? + readString(record, 'siteName') ?? + identifier + }; +} + +/** + * Resolve a site (hostname OR identifier) to its identifier + hostname. + * + * Order is deliberate — cheapest and least fallible first: + * 1. the session cache, when it actually has sites; + * 2. a direct `GET /api/v1/site/{id}` when the caller gave something UUID-shaped; + * 3. a filtered `GET /api/v1/site` lookup by hostname. + * + * Step 2 matters most for the reported failure: a caller who already knows the identifier + * should never be blocked by a cache, because the identifier is precisely what the + * downstream call needs and no lookup is required to use it. + */ +export async function resolveSite(dotcms: DotCMSRuntime, site: string): Promise { + const wanted = site.trim(); + if (!wanted) { + throw new Error('Site must be a non-empty hostname or identifier.'); + } + + const cached = await siteFromCache(dotcms, wanted); + if (cached) { + return cached; + } + + const live = await siteFromApi(dotcms, wanted); + if (live) { + return live; + } + + throw new Error(await siteNotFoundMessage(dotcms, wanted)); +} + +async function siteFromCache( + dotcms: DotCMSRuntime, + wanted: string +): Promise { + const { sites } = await safeContext(dotcms); + const match = sites.find( + (entry) => + entry.identifier === wanted || entry.hostname?.toLowerCase() === wanted.toLowerCase() + ); + + return match ? { identifier: match.identifier, hostname: match.hostname } : undefined; +} + +async function siteFromApi( + dotcms: DotCMSRuntime, + wanted: string +): Promise { + if (looksLikeIdentifier(wanted)) { + try { + const resolved = toResolvedSite( + entityOf( + await dotcms.request({ path: `/api/v1/site/${encodeURIComponent(wanted)}` }) + ) + ); + if (resolved) { + return resolved; + } + } catch (error) { + // A 404 means this identifier genuinely does not exist — fall through to the + // hostname search, which will also miss, and report not-found properly. Any other + // failure is an instance problem and must not be reported as "site not found". + if (!(error instanceof HttpError) || error.status !== 404) { + throw error; + } + } + } + + try { + const raw = entityOf( + await dotcms.request({ + path: '/api/v1/site', + query: { filter: wanted, per_page: 50, page: 1 } + }) + ); + const candidates = Array.isArray(raw) ? raw : []; + + // The endpoint filters by substring, so `demo` also returns `demo-backup`. Only an + // exact hostname (or identifier) match may be accepted — silently picking a + // near-miss would write content to the wrong site. + for (const candidate of candidates) { + const resolved = toResolvedSite(candidate); + if ( + resolved && + (resolved.identifier === wanted || + resolved.hostname.toLowerCase() === wanted.toLowerCase()) + ) { + return resolved; + } + } + } catch { + // Fall through to the not-found message, which explains what was tried. + } + + return undefined; +} + +/** Explain a miss WITHOUT implying the instance has no sites when the cache simply failed. */ +async function siteNotFoundMessage(dotcms: DotCMSRuntime, wanted: string): Promise { + const { sites } = await safeContext(dotcms); + + if (sites.length === 0) { + return ( + `Site "${wanted}" could not be resolved, and this session's site list is empty — ` + + `which usually means the one-time context load failed (a transient error or a ` + + `permissions problem), NOT that the instance has no sites. A direct lookup was ` + + `also tried and did not find it. Verify the site exists and that the configured ` + + `token can read it; if the site is correct, reconnecting the MCP server reloads ` + + `the context.` + ); + } + + const available = sites.map((entry) => entry.hostname).join(', '); + + return ( + `Site "${wanted}" was not found (neither a known hostname nor a site identifier), ` + + `and a direct lookup did not find it either. Available sites: ${available}.` + ); +} + +/** + * Resolve a language id against the instance. + * + * dotCMS silently falls back to its default language for an unknown id rather than + * rejecting it, so an unrecognised id does not fail — it quietly writes to a DIFFERENT + * language than the caller named while the manifest echoes the id they asked for. That is + * worth catching, but ONLY when the language list actually loaded: with an empty list the + * caller's explicit id is better evidence than our missing cache, so it is passed through. + */ +export async function resolveLanguageId( + dotcms: DotCMSRuntime, + languageId?: number +): Promise { + const languages = await loadLanguages(dotcms); + + if (languageId === undefined) { + // The instance's own default, not a hardcoded 1 — on some instances it is not 1. + return languages[0]?.id ?? 1; + } + + if (languages.length === 0 || languages.some((language) => language.id === languageId)) { + return languageId; + } + + const available = languages + .map((language) => `${language.id} (${language.isoCode})`) + .join(', '); + throw new Error( + `languageId ${languageId} does not exist on this instance. dotCMS would silently fall ` + + `back to the default language and write to the WRONG language rather than reject ` + + `it, so this is refused up front. Available languages: ${available}.` + ); +} + +/** Languages from the cache, falling back to a direct read when the cache is empty. */ +async function loadLanguages( + dotcms: DotCMSRuntime +): Promise> { + const { languages } = await safeContext(dotcms); + if (languages.length > 0) { + return languages.map((language) => ({ id: language.id, isoCode: language.isoCode })); + } + + try { + const raw = entityOf(await dotcms.request({ path: '/api/v2/languages' })); + + return (Array.isArray(raw) ? raw : []) + .map((item) => { + const record = asRecord(item); + const id = record?.['id']; + + return { + id: typeof id === 'number' ? id : Number(id) || 0, + isoCode: readString(record, 'isoCode') ?? '' + }; + }) + .filter((language) => language.id > 0); + } catch { + // Unknown rather than empty — the caller's id is then taken at face value. + return []; + } +} + +/** + * `loadContext()` with its own failure absorbed. + * + * A context load failing must never be the reason a tool call dies: the context is an + * accelerator, and every resolver here has a direct-lookup path that does not need it. + */ +async function safeContext(dotcms: DotCMSRuntime) { + try { + return await dotcms.loadContext(); + } catch (error) { + console.error(`[context] load failed during resolution: ${errorMessage(error)}`); + + return { contentTypes: [], sites: [], languages: [], currentUser: null }; + } +} diff --git a/core-web/apps/mcp-server/src/lib/runtime.spec.ts b/core-web/apps/mcp-server/src/lib/runtime.spec.ts new file mode 100644 index 000000000000..357ab2db8c1a --- /dev/null +++ b/core-web/apps/mcp-server/src/lib/runtime.spec.ts @@ -0,0 +1,111 @@ +import { AbortError, HttpError, PolicyError, TimeoutError } from '@dotcms/ai/runtime'; + +import { errorMessage, MAX_ERROR_CHARS, toolFailure, type ToolFailure } from './runtime'; + +/** Parse what a tool handler actually returns — a JSON string, not an object. */ +function parse(result: string): ToolFailure { + return JSON.parse(result) as ToolFailure; +} + +describe('errorMessage', () => { + it('returns the message of an Error', () => { + expect(errorMessage(new Error('boom'))).toBe('boom'); + }); + + it('stringifies a non-Error throw', () => { + expect(errorMessage('plain string')).toBe('plain string'); + expect(errorMessage(42)).toBe('42'); + }); + + it('caps a huge message and says it was truncated', () => { + // A dotCMS 5xx returns its full HTML stack-trace page and `HttpError.message` embeds + // the body verbatim. A transfer manifest keeps one message per FAILED FILE, so 200 + // files against a broken instance would carry 200 copies of that page. + const huge = 'x'.repeat(MAX_ERROR_CHARS * 3); + const capped = errorMessage(new Error(huge)); + + expect(capped.length).toBeLessThan(huge.length); + expect(capped).toContain('truncated'); + expect(capped).toContain(String(huge.length)); + }); + + it('leaves a message at exactly the cap alone', () => { + const exact = 'y'.repeat(MAX_ERROR_CHARS); + expect(errorMessage(new Error(exact))).toBe(exact); + }); +}); + +describe('toolFailure', () => { + it('returns JSON carrying the operation, code and prefix', () => { + const failure = parse(toolFailure('page_verify', new Error('nope'))); + + expect(failure.ok).toBe(false); + expect(failure.operation).toBe('page_verify'); + expect(failure.error).toContain('[MCP Server - page_verify]'); + expect(failure.error).toContain('nope'); + expect(failure.code).toBe('UNKNOWN'); + }); + + describe('retryable', () => { + // `retryable` has to be a FIELD: MCP hands the model a string, so `instanceof` is + // unavailable on the far side and anything it must branch on has to survive JSON. + it('is true for a timeout', () => { + const failure = parse(toolFailure('op', new TimeoutError('too slow', 30_000))); + expect(failure.retryable).toBe(true); + expect(failure.code).toBe('TIMEOUT'); + }); + + it.each([ + [408, 'Request Timeout'], + [429, 'Too Many Requests'], + [500, 'Server Error'], + [503, 'Service Unavailable'] + ])('is true for a transient HTTP %d', (status, statusText) => { + const failure = parse(toolFailure('op', new HttpError(status, statusText, 'body'))); + expect(failure.retryable).toBe(true); + expect(failure.status).toBe(status); + }); + + it.each([ + [400, 'Bad Request'], + [403, 'Forbidden'], + [404, 'Not Found'], + [409, 'Conflict'] + ])('is false for a client-side HTTP %d', (status, statusText) => { + // A 429 on file 3 of 200 and a permanent 403 read identically once flattened to a + // message, so the model either abandons a transfer that would have succeeded or + // retries one that never can. + const failure = parse(toolFailure('op', new HttpError(status, statusText, 'body'))); + expect(failure.retryable).toBe(false); + expect(failure.status).toBe(status); + }); + + it('is false for a caller-initiated abort', () => { + const failure = parse(toolFailure('op', new AbortError('cancelled'))); + expect(failure.retryable).toBe(false); + expect(failure.code).toBe('ABORT'); + }); + + it('is false for a policy rejection', () => { + const failure = parse(toolFailure('op', new PolicyError('blocked', 'GET', '/x'))); + expect(failure.retryable).toBe(false); + expect(failure.code).toBe('POLICY'); + }); + }); + + it('caps the embedded error body', () => { + const huge = new HttpError(500, 'Server Error', 'z'.repeat(MAX_ERROR_CHARS * 4)); + const failure = parse(toolFailure('upload_assets', huge)); + + expect(failure.error).toContain('truncated'); + expect(failure.error.length).toBeLessThan(MAX_ERROR_CHARS * 2); + }); + + it('merges caller-supplied context without losing the standard fields', () => { + const failure = parse(toolFailure('op', new Error('x'), { path: '/about-us' })); + + expect(failure['path']).toBe('/about-us'); + expect(failure.ok).toBe(false); + expect(failure.operation).toBe('op'); + }); +}); diff --git a/core-web/apps/mcp-server/src/lib/runtime.ts b/core-web/apps/mcp-server/src/lib/runtime.ts index a1d43efab67f..ab6e133db6dc 100644 --- a/core-web/apps/mcp-server/src/lib/runtime.ts +++ b/core-web/apps/mcp-server/src/lib/runtime.ts @@ -1,30 +1,212 @@ -import { createRuntime } from '@dotcms/ai/runtime'; +import { + AbortError, + createRuntime, + HttpError, + isDotCMSError, + TimeoutError, + type DotCMSRuntime, + type RequestOptions +} from '@dotcms/ai/runtime'; -type DotCMSRuntime = ReturnType; +/** + * Wall-clock deadline applied to every direct `dotcms.request()` a lib tool makes. + * + * `createRuntime`'s `timeout` bounds `run()` only; `request()` is documented as having no + * surrounding timeout of its own. Without a deadline a wedged instance hangs the MCP call + * forever — the model gets no error, no result, and no way to tell the difference from slow + * work — and `TIMEOUT`, the one unambiguously retryable code, could never be produced. + */ +const DEFAULT_REQUEST_TIMEOUT_MS = 30_000; + +/** + * Cap on any single error string handed back to the model. + * + * A dotCMS 5xx returns its full HTML stack-trace page, tens to hundreds of KB, and + * `HttpError.message` embeds the body verbatim. A transfer manifest keeps one message PER + * FAILED FILE, so 200 files against a broken instance would otherwise carry 200 copies of + * that page in a single tool result. + */ +export const MAX_ERROR_CHARS = 2_000; + +/** + * The server is misconfigured — a deployment problem, not a bad tool call. + * + * Carries its own `code` so it lands in {@link ToolFailure} as `CONFIGURATION` rather than + * `UNKNOWN`, which is what lets the model tell "I called this wrong" from "this server + * cannot serve anyone right now". + */ +export class ConfigurationError extends Error { + readonly code = 'CONFIGURATION' as const; + + constructor(message: string) { + super(message); + this.name = 'ConfigurationError'; + } +} /** * Build a runtime from the MCP server's environment. One place owns the `DOTCMS_URL` / - * `AUTH_TOKEN` reading, the default session id, and the standard context-error logging — so - * every tool (`execute`, `search`, `download_assets`, `upload_assets`) constructs the runtime - * the same way instead of re-deriving it (and silently drifting on which options they set). + * `AUTH_TOKEN` reading, the default session id, the standard context-error logging, and the + * per-request deadline — so every tool (`execute`, `search`, `download_assets`, + * `upload_assets`) constructs the runtime the same way instead of re-deriving it (and + * silently drifting on which options they set). */ export function runtimeFromEnv( sessionId?: string, - opts?: { timeout?: number; includeSpec?: boolean } + opts?: { timeout?: number; includeSpec?: boolean; requestTimeout?: number } ): DotCMSRuntime { - return createRuntime({ - url: process.env.DOTCMS_URL ?? '', - token: process.env.AUTH_TOKEN ?? '', - sessionId: sessionId ?? '__default__', - timeout: opts?.timeout, - includeSpec: opts?.includeSpec, - onContextError: (label, error) => { - console.error(`[context] failed to load ${label}: ${errorMessage(error)}`); + let runtime: DotCMSRuntime; + try { + runtime = createRuntime({ + url: process.env.DOTCMS_URL ?? '', + token: process.env.AUTH_TOKEN ?? '', + sessionId: sessionId ?? '__default__', + timeout: opts?.timeout, + includeSpec: opts?.includeSpec, + onContextError: (label, error) => { + console.error(`[context] failed to load ${label}: ${errorMessage(error)}`); + } + }); + } catch (error) { + // `createRuntime` throws e.g. "token is required" when DOTCMS_URL / AUTH_TOKEN are + // unset. Raw, that reads to a model as a problem with ITS call — and the transfer + // tools' own descriptions tell it "you do NOT need a dotCMS token, never go looking + // for them", so a server misconfiguration would push it toward exactly the + // credential-hunting those descriptions forbid. Say plainly whose problem it is. + throw new ConfigurationError( + `The MCP server is not configured: ${errorMessage(error)}. DOTCMS_URL and ` + + `AUTH_TOKEN are set in the MCP client's server config, by the operator. This ` + + `is not a problem with the tool call and no argument can fix it — report it ` + + `and stop; do not look for credentials.` + ); + } + + const requestTimeout = opts?.requestTimeout ?? DEFAULT_REQUEST_TIMEOUT_MS; + + return { + ...runtime, + /** + * `request` with a default deadline. A caller that passes its own signal keeps full + * control and is left alone; otherwise the call is bounded and a deadline hit is + * reported as `TIMEOUT` rather than `ABORT`, because the two mean opposite things to + * the model — a timeout is worth retrying, a caller-initiated abort is not. + */ + request: (options: RequestOptions, reqOpts?: { signal?: AbortSignal }) => { + if (reqOpts?.signal) { + return runtime.request(options, reqOpts); + } + + const controller = new AbortController(); + const timer = setTimeout(() => controller.abort(), requestTimeout); + + return runtime + .request(options, { signal: controller.signal }) + .catch((error: unknown) => { + if (controller.signal.aborted && error instanceof AbortError) { + throw new TimeoutError( + `Request ${options.method ?? 'GET'} ${options.path} exceeded the ` + + `${requestTimeout}ms deadline and was aborted. The instance may be ` + + `overloaded or wedged; this is worth retrying.`, + requestTimeout + ); + } + throw error; + }) + .finally(() => clearTimeout(timer)); } - }); + }; } -/** Normalize any thrown value to a message string. */ +/** + * Normalize any thrown value to a message string, capped at {@link MAX_ERROR_CHARS}. + * + * The cap is the point: see MAX_ERROR_CHARS for why an uncapped `HttpError.message` is a + * real problem rather than a cosmetic one. + */ export function errorMessage(error: unknown): string { - return error instanceof Error ? error.message : String(error); + const raw = error instanceof Error ? error.message : String(error); + + return raw.length <= MAX_ERROR_CHARS + ? raw + : `${raw.slice(0, MAX_ERROR_CHARS)}… [truncated, ${raw.length} chars total]`; +} + +/** + * Whether re-issuing the same call could plausibly succeed. + * + * This is the single most useful thing a tool can tell the calling model, and the one it + * cannot work out for itself: a 429 on file 3 of 200 and a permanent 403 read identically + * once flattened to a message, so the model either abandons a transfer that would have + * succeeded or retries one that never can. + */ +function isRetryable(error: unknown): boolean { + if (error instanceof TimeoutError) { + return true; + } + if (error instanceof HttpError) { + // 408 Request Timeout and 429 Too Many Requests are explicitly transient; 5xx is the + // instance failing rather than the request being wrong. Every other 4xx is the call + // itself being wrong, and retrying it unchanged cannot help. + return error.status === 408 || error.status === 429 || error.status >= 500; + } + + // ABORT is caller-initiated, VALIDATION/POLICY are the call being wrong, and + // SANDBOX/RUNTIME are bugs. None of them improve on a retry. + return false; +} + +/** Stable machine-readable code for any thrown value. */ +function errorCode(error: unknown): string { + if (isDotCMSError(error)) { + return error.code; + } + + return error instanceof ConfigurationError ? error.code : 'UNKNOWN'; +} + +/** The structured failure a tool returns instead of a bare message. */ +export interface ToolFailure { + ok: false; + operation: string; + error: string; + /** Stable machine-readable code (`HTTP`, `TIMEOUT`, `POLICY`, …), or `UNKNOWN`. */ + code: string; + /** + * Whether retrying could help. A FIELD rather than only a type, because MCP hands the + * model a STRING — `instanceof` is unavailable on the far side, so anything the model + * needs to branch on has to survive JSON. + */ + retryable: boolean; + /** HTTP status when the failure was an HTTP error. */ + status?: number; + [key: string]: unknown; +} + +/** + * Build the failure payload a tool handler returns. + * + * Owns the `[MCP Server - ]` prefix convention in one place rather than as a + * template string at each throw site, and preserves the typed detail (`code`, `status`, + * `retryable`) that flattening to `.message` used to discard. + * + * Deliberately NOT a parallel error hierarchy: `formatSandboxResult` remains the layer for + * sandbox results (`execute`/`search` already use it). This is only for the direct-request + * tools, which have no sandbox result to format. + */ +export function toolFailure( + operation: string, + error: unknown, + extra?: Record +): string { + const failure: ToolFailure = { + ok: false, + operation, + error: `[MCP Server - ${operation}]: ${errorMessage(error)}`, + code: errorCode(error), + retryable: isRetryable(error), + ...(error instanceof HttpError ? { status: error.status } : {}), + ...extra + }; + + return JSON.stringify(failure, null, 2); } diff --git a/core-web/apps/mcp-server/src/tools/download_assets.ts b/core-web/apps/mcp-server/src/tools/download_assets.ts index c69312885e00..d905ea673d2c 100644 --- a/core-web/apps/mcp-server/src/tools/download_assets.ts +++ b/core-web/apps/mcp-server/src/tools/download_assets.ts @@ -2,7 +2,7 @@ import { type InferSchema, type ToolExtraArguments, type ToolMetadata } from 'xm import { z } from 'zod'; import { downloadAssets } from '../lib/assets-transfer'; -import { errorMessage, runtimeFromEnv } from '../lib/runtime'; +import { runtimeFromEnv, toolFailure } from '../lib/runtime'; export const schema = { path: z @@ -70,6 +70,6 @@ export default async function handler( return JSON.stringify(manifest, null, 2); } catch (error) { - return `Error: ${errorMessage(error)}`; + return toolFailure('download_assets', error); } } diff --git a/core-web/apps/mcp-server/src/tools/execute.ts b/core-web/apps/mcp-server/src/tools/execute.ts index fe5294dbb4d6..d3d6a8e1abaa 100644 --- a/core-web/apps/mcp-server/src/tools/execute.ts +++ b/core-web/apps/mcp-server/src/tools/execute.ts @@ -1,7 +1,9 @@ import { type InferSchema, type ToolExtraArguments, type ToolMetadata } from 'xmcp'; import { z } from 'zod'; -import { createRuntime } from '@dotcms/ai/runtime'; +import { formatSandboxResult } from '@dotcms/ai/runtime'; + +import { runtimeFromEnv, toolFailure } from '../lib/runtime'; export const schema = { code: z @@ -27,9 +29,15 @@ Use api.request(options) where options is: Auth is handled automatically — tokens are never exposed to your code. +This is a **JavaScript sandbox, NOT Velocity/VTL**: +- Velocity variables like \`$dotcontent\`, \`$dotcontent.pull(...)\`, \`$date\`, \`#foreach\` do NOT exist here — referencing \`$dotcontent\` throws \`ReferenceError\`. To query content, call \`api.request({ method: 'POST', path: '/api/content/_search', body: { query, ... } })\`. Run VTL only via \`POST /api/vtl/dynamic\`. +- **\`await\` every \`api.request\`** and return only JSON-serializable values (objects, arrays, strings, numbers). Returning an un-awaited Promise (or a function/class instance) throws \`DataCloneError\` — the result is structured-cloned out of the worker. +- Watch string literals: a raw apostrophe inside a single-quoted JS string (e.g. \`'grandchild's'\`) is a \`SyntaxError\`. Use double quotes or escape it. + Pre-loaded instance context (available as globals — no API calls needed to read these): - contentTypes: Array<{ id, name, variable, baseType, host?, folder? }> - - sites: Array<{ identifier, hostname, isDefault, archived }> + - sites: Array<{ identifier, hostname, isDefault, archived, live }> — all accessible non-system + sites, including stopped and archived states - languages: Array<{ id, languageCode, countryCode, language, country, isoCode }> - currentUser: { userId, email, givenName?, surname?, admin, roles? } | null Examples: @@ -37,6 +45,15 @@ Pre-loaded instance context (available as globals — no API calls needed to rea const defaultSite = sites.find(s => s.isDefault); const en = languages.find(l => l.languageCode === 'en'); +The pre-loaded globals are a snapshot taken at the start of one tool invocation and do not mutate +halfway through the current script. Each MCP invocation constructs fresh runtime context, so a +subsequent call sees successful changes to sites, content types, or languages without maintaining +resource-specific invalidation rules. + +Do not use \`PUT /api/v1/site/switch/{id}\` as a targeting mechanism. MCP API requests are +independent and session-scoped site selection is not guaranteed to carry to the next request. +Pass explicit site/host identifiers (for example \`host_id\` or \`contentHost\`) instead. + Always use the \`search\` tool first to discover the correct endpoint path and request/response schema before calling \`execute\`. Transferring file assets? Do NOT use this tool. Use the dedicated \`upload_assets\` / @@ -47,7 +64,7 @@ context. The \`formData\`/base64 path below exists only for small, programmatic for transferring real files, themes, or directories. Tips: -- Use \`pick(arr, fields)\` to return only the fields you need — responses can be very large +- Output is hard-capped (~25k chars). Use \`pick(arr, fields)\` / \`first(arr, n)\` to return only the fields you need — responses can be very large and are truncated past the cap. - For a small programmatic upload (NOT real files — use \`upload_assets\` for those) use \`formData\` with \`{ name, type, data }\` (base64) or \`{ name, type, url }\` (remote URL) Binary responses (small/programmatic reads only — for real files use \`download_assets\`): @@ -55,6 +72,11 @@ Binary responses (small/programmatic reads only — for real files use \`downloa - The \`base64\` field IS the raw file bytes — base64-decode it to recover the exact file. Do NOT treat it as text; the bytes are intact (not UTF-8-mangled). - JSON and textual responses (\`text/*\`, xml, js, \`+json\`/\`+xml\`) are returned as parsed objects / strings as before — only binary bodies use the envelope. +Content field variables (the \`contentlet\` fire body): +- The fire body's \`contentlet\` is keyed by each field's exact **field variable** (from the content type's \`fields[].variable\`) — casing is significant and a wrong-case key is silently ignored (its value is dropped, and a required field then 400s as "required"). +- Page (htmlpageasset) system fields are lowercase/camel exactly: \`contentHost\` (the SITE — NOT \`host\`), \`hostFolder\` (the folder id), \`cachettl\` (all lowercase — NOT \`cacheTTL\`/\`cacheTtl\`), \`template\`, \`url\`, \`title\`, \`friendlyName\`, \`pageTitle\`. \`contentHost\` must be a site **identifier UUID**, not a hostname. Prefer the \`page_create\` tool, which sets all of these correctly. +- For any content type, read the real field variables first: \`GET /api/v1/contenttype/id/{idOrVar}\` → \`entity.fields[].variable\`. Don't guess. + Block Editor (Story Block) fields: - A Story Block field stores a string. When creating or updating content via a fire endpoint, send the field value as an **HTML or Markdown string** — do NOT hand-author the ProseMirror/JSON document. The server converts it to the Block Editor structure **on save**, so the field immediately reads back as structured content. - Example: \`{ "contentType": "Blog", "title": "My Post", "body": "

Intro

Hello world.

" }\` — where \`body\` is the Story Block field. @@ -85,6 +107,12 @@ Workflow fires and Elasticsearch (indexPolicy): - Use \`DEFER\` for isolated, one-off fires where nothing depends on immediate index visibility. - Reserve \`FORCE\` for debugging and testing only — it is heavy on the cluster. +Velocity \`$dotcontent.pull\` sorting: +- Pass content field variables in canonical unsuffixed form, e.g. \`Book.title asc\`. The search + layer selects the keyword mapping by appending \`_dotraw\` internally. +- Already-suffixed input such as \`Book.title_dotraw asc\` is accepted for compatibility and is + normalized without producing \`_dotraw_dotraw\`. + Workflow action discovery (when you need a workflow action ID): - The 'fire' endpoints that take \`{actionId}\` in the path (e.g. PUT /api/v1/workflow/actions/{actionId}/fire and bulk fire) require a workflow action **UUID**, not the system action enum (NEW, EDIT, PUBLISH, …). - To find a UUID, call GET /api/v1/workflow/contentlet/{inode}/actions — returns actions firable on that contentlet right now. @@ -106,35 +134,23 @@ export default async function handler( { code }: InferSchema, extra?: ToolExtraArguments ) { - const timeout = Number(process.env.SANDBOX_TIMEOUT) || 15000; - - // The front door absorbs the executor + adapter + context-cache wiring and injects - // dotCMS instance context automatically. Auth tokens never enter the sandbox. - const dotcms = createRuntime({ - url: process.env.DOTCMS_URL ?? '', - token: process.env.AUTH_TOKEN ?? '', - sessionId: extra?.sessionId ?? '__default__', - timeout, - onContextError: (label, error) => { - const msg = error instanceof Error ? error.message : String(error); - console.error(`[context] failed to load ${label}: ${msg}`); - } - }); - - const result = await dotcms.run(code); // code === the model's output - - if (!result.success) { - const errorMsg = result.error - ? `${result.error.name}: ${result.error.message}` - : 'Unknown error'; - const logs = result.logs.length > 0 ? `\nLogs:\n${result.logs.join('\n')}` : ''; - return `Error: ${errorMsg}${logs}`; + const timeout = Number(process.env.SANDBOX_TIMEOUT) || 45000; + + // Guarded: `runtimeFromEnv` throws on a misconfigured server, and an unguarded throw + // here escapes as an MCP PROTOCOL error rather than a tool result — the model sees a + // transport failure with none of the explanation the error itself carries. + try { + // The front door absorbs the executor + adapter + context-cache wiring and injects + // dotCMS instance context automatically. Auth tokens never enter the sandbox. + const dotcms = runtimeFromEnv(extra?.sessionId, { timeout }); + + const result = await dotcms.run(code); // code === the model's output + + return formatSandboxResult(result, { + truncationHint: + 'Return only the fields you need — use pick(arr, fields) and first(arr, n).' + }); + } catch (error) { + return toolFailure('execute', error); } - - const output = - typeof result.value === 'string' ? result.value : JSON.stringify(result.value, null, 2); - - const logs = result.logs.length > 0 ? `\n\n--- Logs ---\n${result.logs.join('\n')}` : ''; - - return `${output}${logs}`; } diff --git a/core-web/apps/mcp-server/src/tools/page_create.ts b/core-web/apps/mcp-server/src/tools/page_create.ts new file mode 100644 index 000000000000..88c1d73db3e3 --- /dev/null +++ b/core-web/apps/mcp-server/src/tools/page_create.ts @@ -0,0 +1,103 @@ +import { type InferSchema, type ToolExtraArguments, type ToolMetadata } from 'xmcp'; +import { z } from 'zod'; + +import { createPage } from '../lib/page-create'; +import { runtimeFromEnv, toolFailure } from '../lib/runtime'; + +export const schema = { + site: z + .string() + .min(1) + .describe('Site the page lives on — identifier (UUID) or hostname, e.g. "demo.dotcms.com"'), + urlPath: z + .string() + .min(1) + .describe( + 'Page URL path on the site, e.g. "/books/index" or "/books". The parent folder is created automatically; the leaf segment becomes the page url (a bare folder or trailing slash defaults the leaf to "index").' + ), + title: z.string().min(1).describe('Page title'), + template: z + .string() + .min(1) + .describe('Template identifier the page renders with (the template UUID, not its name)'), + contentType: z + .string() + .optional() + .describe( + 'Page content type — variable or id. Defaults to "htmlpageasset". Must be a content type whose base type is HTMLPAGE; a custom page type may add its own fields (pass values via extraFields).' + ), + extraFields: z + .record(z.string(), z.unknown()) + .optional() + .describe( + 'Values for content-type fields beyond the common page fields, keyed by field variable. Required for any user-added required field on a custom page type that has no default value (the tool validates this before firing and tells you which fields are missing).' + ), + friendlyName: z.string().optional().describe('Friendly name; defaults to the title'), + pageTitle: z.string().optional().describe('Browser ; defaults to the title'), + languageId: z.number().int().default(1).describe('Language id. Default 1'), + cacheTtl: z.string().default('0').describe('Cache TTL in seconds, as a string. Default "0"'), + sortOrder: z.number().int().default(0).describe('Sort order within the folder. Default 0') +}; + +export const metadata: ToolMetadata = { + name: 'page_create', + description: `Create and publish a dotCMS page in one safe call. + +A dotCMS "page" is a contentlet whose content type's base type is HTMLPAGE, fired through the +generic workflow endpoint — there is NO dedicated create-page endpoint. The content type defaults +to \`htmlpageasset\`, but pass \`contentType\` to use a custom page type. The tool resolves the type, +confirms it really is a page type, and validates its required fields BEFORE creating anything — so +a custom page type's user-added required fields surface as a clear "pass extraFields: { … }" error +instead of an opaque 400 after the folder was already created. Hand-rolling that fire with the +\`execute\` tool is the path that hits two sharp edges; this tool exists to absorb the first one: + + The URL-collapse trap. If you fire a page with \`url: "/books/index"\` but the \`/books\` folder + does not exist yet, dotCMS silently collapses the url down to \`/index\` — which then 400s with + "Page URL [/index] already exists" because the home page already owns it. This tool splits + \`urlPath\` into folder + leaf, creates the parent folder first (idempotent), then fires the + page with the leaf url under that folder — so the url lands exactly where you meant. + +WHAT THIS TOOL DOES NOT DO — read this. It creates the page; it does NOT place any content on it. +The page comes up live but BLANK. Placing content (and the re-publish that makes it render) is a +separate, explicit step you perform afterward with the \`execute\` tool against the page's layout. +The returned manifest flags a \`live: false\` / warning case so a successful create is never +mistaken for a fully-populated page. + +Returns a JSON manifest: { identifier, inode, folder, url, fullPath, site, live, warnings }. + +Use the \`execute\` tool (not this one) when you need a page variant, want to set custom page +fields beyond the common set, or need to fire a non-PUBLISH workflow action.`, + annotations: { + title: 'Create dotCMS Page', + readOnlyHint: false, + destructiveHint: false, + idempotentHint: false, + openWorldHint: true + } +}; + +export default async function handler( + args: InferSchema<typeof schema>, + extra?: ToolExtraArguments +) { + try { + const manifest = await createPage({ + dotcms: runtimeFromEnv(extra?.sessionId), + site: args.site, + urlPath: args.urlPath, + title: args.title, + template: args.template, + contentType: args.contentType, + extraFields: args.extraFields, + friendlyName: args.friendlyName, + pageTitle: args.pageTitle, + languageId: args.languageId, + cacheTtl: args.cacheTtl, + sortOrder: args.sortOrder + }); + + return JSON.stringify(manifest, null, 2); + } catch (error) { + return toolFailure('page_create', error); + } +} diff --git a/core-web/apps/mcp-server/src/tools/page_place_content.ts b/core-web/apps/mcp-server/src/tools/page_place_content.ts new file mode 100644 index 000000000000..a06a8bfd1e71 --- /dev/null +++ b/core-web/apps/mcp-server/src/tools/page_place_content.ts @@ -0,0 +1,164 @@ +import { type InferSchema, type ToolExtraArguments, type ToolMetadata } from 'xmcp'; +import { z } from 'zod'; + +import { placeContent, type PagePlaceContentOptions } from '../lib/page-place-content'; +import { runtimeFromEnv, toolFailure } from '../lib/runtime'; + +// A slot address: a 1-based index into the page's real slots, OR a container reference. `instance` +// (the slot uuid) is optional only when the container occupies exactly one slot on the page. +const slotAddress = z.union([ + z + .number() + .int() + .positive() + .describe('1-based index into the page’s slots, in layout order (the first slot is 1).'), + z + .object({ + container: z + .string() + .min(1) + .describe( + 'Container key as it appears on the page: a container id/shorty, a container ' + + 'file path (e.g. "//demo.dotcms.com/application/containers/default/"), or a ' + + 'recognizable fragment of either. "SYSTEM_CONTAINER" for the system container.' + ), + instance: z + .string() + .optional() + .describe( + 'Slot instance uuid (e.g. "1", "10"). Required only when the container appears ' + + 'in more than one slot; the tool errors and lists the instances if omitted.' + ) + }) + .strict() +]); + +export const schema = { + path: z + .string() + .min(1) + .describe( + 'Page URL path, e.g. "/about-us", host-qualified path such as "//demo.dotcms.com/about-us", or a page identifier. Bare paths/identifiers require site.' + ), + // Conditionally required: a host-qualified path already supplies the site. Marking this field + // required in JSON Schema would incorrectly reject the supported `//hostname/path` form; the + // resolver enforces that exactly one source of site identity is present before any request. + site: z + .string() + .min(1) + .optional() + .describe( + 'Site hostname or identifier (UUID). Required for a bare path or page identifier. Omit only when path is host-qualified as "//hostname/path".' + ), + slots: z + .array( + z + .object({ + slot: slotAddress, + contentlets: z + .array(z.string().min(1)) + .describe( + 'Contentlet identifiers to place, in order. For op "remove" these are ' + + 'the ids to remove; to clear a slot use op "set" with [].' + ), + op: z + .enum(['append', 'set', 'remove']) + .optional() + .describe( + 'How to combine with the slot’s current content. "append" (default) ' + + 'adds after existing (de-duped), "set" replaces, "remove" removes.' + ) + }) + .strict() + ) + .min(1) + .describe( + 'One or more slot assignments applied in a single atomic write. Placing content in one ' + + 'slot is just an array of one. Each entry: { slot, contentlets, op? }.' + ), + // ── scope ── + variantName: z.string().optional().describe('Variant to write to. Default "DEFAULT".'), + languageId: z.number().int().positive().optional().describe('Language id. Default 1.'), + mode: z + .enum(['merge', 'replace']) + .optional() + .describe( + '"merge" (default) preserves every slot you don’t touch. "replace" treats the slots ' + + 'you pass as the complete page — every other slot is cleared.' + ) +}; + +export const metadata: ToolMetadata = { + name: 'page_place_content', + description: `Place content into a dotCMS page's container slots — safely, without wiping the rest of the page. + +The underlying endpoint (POST /api/v1/page/{pageId}/content) is a FULL replacement: it rewrites the +page's entire container-to-contentlet map, and any slot omitted from the body is emptied. Adding one +contentlet "the raw way" therefore silently clears every other slot. This tool removes that footgun: +it reads the page's current content, applies your change to the addressed slot(s) only, and writes +the COMPLETE map back — so untouched slots survive. + +You do NOT need to call GET /api/v1/page/json first. Give the page \`path\` and address a slot by: + - a 1-based \`slot\` index (in layout order), or + - \`slot: { container, instance? }\` — a container id/path/fragment, plus the slot uuid when that + container appears in more than one slot (the tool lists the instances if you omit it). +A slot that doesn't resolve fails with the list of valid slots, so a typo can't silently no-op. + +Ops (per entry in \`slots[]\`): + - append (default) — add the ids after what's already in the slot (de-duplicated) + - set — replace the slot's content with exactly these ids ([] clears the slot) + - remove — remove these ids from the slot + +Targeting: pass \`site\` with every bare path/identifier, or use a host-qualified path such as +\`//awazon.dotcms.site/index\`. The tool resolves the hostname to \`host_id\` before reading the page +and never silently falls back to the default site. If explicit and embedded sites conflict, it +fails before making a request. + +Shape: { path, site?, slots: [{ slot, contentlets, op? }, ...] }. One atomic write across all listed slots +— placing content in a single slot is just an array of one: slots: [{ slot, contentlets }]. + +Modes: "merge" (default) keeps every slot you don't address. "replace" treats the slots you pass as +the whole page and clears all others — use it only for deliberate whole-page authoring. + +Scope: \`variantName\` (default DEFAULT) and \`languageId\` (default 1) target a specific A/B variant +and language. + +Returns a manifest: { pageId, site, url, variantName, languageId, mode, slots: [{ identifier, uuid, +before[], after[], changed }], warnings[] }. \`warnings\` flags any slot that lost content and +explains a net-loss 409 (refresh and retry). Contentlets whose type isn't allowed in a container are +rejected by the backend; archived/missing ids are skipped and show up as a slot that didn't gain them. + +Typical flow: create the (blank) page with \`page_create\`, then populate it with this tool. The +contentlets themselves are created separately (e.g. via the \`execute\` tool) — this tool only places +existing contentlets into slots.`, + annotations: { + title: 'Place Content on a dotCMS Page', + readOnlyHint: false, + destructiveHint: true, + idempotentHint: false, + openWorldHint: true + } +}; + +export default async function handler( + args: InferSchema<typeof schema>, + extra?: ToolExtraArguments +) { + try { + const options: PagePlaceContentOptions = { + dotcms: runtimeFromEnv(extra?.sessionId), + path: args.path, + site: args.site, + slots: args.slots, + variantName: args.variantName, + languageId: args.languageId, + mode: args.mode + }; + + const manifest = await placeContent(options); + + return JSON.stringify(manifest, null, 2); + } catch (error) { + return toolFailure('page_place_content', error); + } +} diff --git a/core-web/apps/mcp-server/src/tools/page_verify.ts b/core-web/apps/mcp-server/src/tools/page_verify.ts new file mode 100644 index 000000000000..96a720629967 --- /dev/null +++ b/core-web/apps/mcp-server/src/tools/page_verify.ts @@ -0,0 +1,114 @@ +import { type InferSchema, type ToolExtraArguments, type ToolMetadata } from 'xmcp'; +import { z } from 'zod'; + +import { verifyPage, type VerifyPageOptions } from '../lib/page-verify'; +import { runtimeFromEnv, toolFailure } from '../lib/runtime'; + +export const schema = { + path: z + .string() + .min(1) + .describe( + 'Page URL path, e.g. "/about-us". For a URL-mapped detail page, pass a concrete slug ' + + '(e.g. "/blog/my-post") — the tool reports whether $URLMapContent resolved.' + ), + site: z + .string() + .optional() + .describe( + 'Site to render against — hostname or identifier (UUID). Omit for the default site. ' + + 'Only a NON-default site needs this; the tool resolves it to the host_id the ' + + 'endpoint requires.' + ), + languageId: z.number().int().positive().optional().describe('Language id. Default 1.'), + mode: z + .enum(['LIVE', 'WORKING']) + .optional() + .describe( + 'Render mode. "LIVE" (default) = what the public sees (published). "WORKING" = latest ' + + 'saved, for a pre-publish check. An unpublished edit renders stale in LIVE.' + ), + includeHtml: z + .boolean() + .optional() + .describe( + 'Include a bounded prefix of the assembled page HTML for diagnosis. Default false; returns at most 20,000 characters with truncation metadata.' + ) +}; + +export const metadata: ToolMetadata = { + name: 'page_verify', + description: `Verify that a dotCMS page actually RENDERS — catch a blank slot, a swallowed VTL error, a cache-stale page, or an unpublished edit. + +This is the render-verification layer, distinct from VTL validation (POST /api/vtl/dynamic). VTL +validation runs in a REQUEST context and is structurally blind to $URLMapContent, $CONTENTLETS, +$dotContentMap, $dotTheme, and per-container vars. Only a real render exercises the full +page-assembly pipeline — so this is the only layer that catches a slot that came out empty, a +#dotParse error that got swallowed, or a page serving stale from cache. + +It wraps GET /api/v1/page/render/{uri} and absorbs its sharp edges: + - Host: pass a hostname in \`site\` (or nothing). The tool resolves it to the host_id the endpoint + needs. host_id is required only for a NON-default site; omit \`site\` for the default site. + - 200 != rendered: #dotParse swallows a VTL error into an empty HTTP 200. A 200 with an empty body + is a FAILURE here (pageRendered=false), not success. + - Two rendered layers that can disagree: each slot's HTML (containers[].rendered[uuid]) vs. the + assembled page.rendered. Their disagreement IS the diagnosis. + +Per-slot \`verdict\`: + - ok — the slot produced rendered HTML. + - empty-vtl-error — content is placed but the slot rendered empty → the container/widget VTL + failed. The fix lives in the OTHER layer: re-run that container's VTL through + POST /api/vtl/dynamic to get the parse/eval error with line/column. + - empty-no-content — the slot resolved but has no content placed (a placement gap, not a code bug). + Use page_place_content to fill it. + - cache-stale — the slot rendered HTML but page.rendered is empty → page-level cache. Set + cachettl "0" and re-publish. + - not-assembled — the slot rendered in isolation but distinctive HTML evidence is absent from + page.rendered → inspect the theme row/column loop and emit + $render.eval($column.draw()). + +LIVE vs WORKING: an unpublished edit renders stale in LIVE. Use mode "WORKING" for a pre-publish +check; the result flags that it reflects unpublished edits. + +URL-mapped pages: for a detail slug, \`urlMap\` reports whether $URLMapContent resolved to a concrete +contentlet (vs. the no-match/404 branch). + +Returns a manifest: { path, url, site, mode, languageId, httpStatus, pageRendered, pageBytes, +slots: [{ container, uuid, rendered, bytes, contentCount, verdict }], urlMap, warnings, diagnosis }. +The \`diagnosis\` is a one-line summary plus the next action to take — read it first. + +Set \`includeHtml: true\` to add \`html: { content, totalChars, totalBytes, truncated, limit }\`. +The content is capped at 20,000 characters so a diagnostic call cannot flood model context. + +Out of scope: visual/screenshot checks, accessibility, performance, multi-page crawl (one page per +call). This verifies DEFAULT-variant rendering — the render endpoint does not take a variant.`, + annotations: { + title: 'Verify a dotCMS Page Renders', + readOnlyHint: true, + destructiveHint: false, + idempotentHint: true, + openWorldHint: true + } +}; + +export default async function handler( + args: InferSchema<typeof schema>, + extra?: ToolExtraArguments +) { + try { + const options: VerifyPageOptions = { + dotcms: runtimeFromEnv(extra?.sessionId), + path: args.path, + site: args.site, + languageId: args.languageId, + mode: args.mode, + includeHtml: args.includeHtml + }; + + const manifest = await verifyPage(options); + + return JSON.stringify(manifest, null, 2); + } catch (error) { + return toolFailure('page_verify', error); + } +} diff --git a/core-web/apps/mcp-server/src/tools/search.ts b/core-web/apps/mcp-server/src/tools/search.ts index 440c164ed927..3f5e8572834d 100644 --- a/core-web/apps/mcp-server/src/tools/search.ts +++ b/core-web/apps/mcp-server/src/tools/search.ts @@ -1,15 +1,17 @@ import { type InferSchema, type ToolExtraArguments, type ToolMetadata } from 'xmcp'; import { z } from 'zod'; -import { createRuntime } from '@dotcms/ai/runtime'; +import { formatSandboxResult } from '@dotcms/ai/runtime'; import { getSpec } from '@dotcms/ai/spec'; +import { runtimeFromEnv, toolFailure } from '../lib/runtime'; + export const schema = { code: z .string() .max(100_000) .describe( - 'JavaScript async function body. The `spec` global contains the dereferenced OpenAPI spec. Return the data you need.' + 'JavaScript async function body. The `spec` global contains the filtered dotCMS OpenAPI spec (`$ref`-based: `spec.paths` + `spec.components.schemas`). Return the data you need.' ) }; @@ -21,19 +23,38 @@ Spec structure: - \`spec.paths\` — object keyed by path string (e.g. "/api/v1/contenttype") - Method keys are lowercase: get, post, put, delete - Each operation has: summary, parameters, requestBody, responses -- \`requestBody.content\` is keyed by MIME type (e.g. "application/json"), then \`.schema\` for the body shape +- \`requestBody.content\` / \`responses[status].content\` are keyed by MIME type (e.g. "application/json"), then \`.schema\` for the body shape — \`.schema\` is usually a \`$ref\` like \`{ $ref: '#/components/schemas/PageView' }\`, NOT an inline object - \`parameters\` is an array of { name, in, required, schema } — "in" is "query", "path", or "header" -- \`responses\` keys are HTTP status codes; schemas are stripped — only description and content MIME type keys remain (e.g. \`responses['200'].content['application/json']\` is \`{}\`) +- \`responses\` keys are HTTP status codes +- \`spec.components.schemas\` — every schema referenced by the kept endpoints, keyed by name + +Resolving \`$ref\`s: call \`resolveRef(schemaOrName, depth = 2)\` — it resolves a \`$ref\` (or a schema name) against \`spec.components.schemas\`, expanding nested refs \`depth\` levels and leaving deeper ones as \`$ref\` strings for a follow-up query. Use it instead of hand-walking refs. + +Helpers available here: \`resolveRef(schemaOrName, depth)\`, \`pick(arr, fields)\`, \`table(arr)\`, \`count(arr, field)\`, \`sum(arr, field)\`, \`first(arr, n)\`. + +Output is hard-capped (~25k chars). Return only what you need — resolve one schema at a bounded depth, not the whole spec. + +This spec is a CURATED allow-list of the supported authoring endpoints — not every dotCMS endpoint is present, and that is deliberate. Treat it as the set of endpoints you should use. So: +- **Guard path access:** \`spec.paths['/x']\` may be \`undefined\`, and \`spec.paths['/x'].get\` on a missing path throws \`TypeError: Cannot read properties of undefined\`. Always use optional chaining: \`spec.paths['/x']?.get\`, or check \`if (!spec.paths['/x']) return 'not in spec'\` first. +- **Discover before assuming:** to find a path, filter the keys — \`Object.keys(spec.paths).filter(p => p.includes('template'))\` — rather than guessing an exact string. +- **Absent usually means "not the intended path."** If an endpoint isn't here, first look for a supported one that does the job (search the keys; a different path or verb often covers it). Reaching for an off-list endpoint should be a last resort for a genuine gap, not a reflex — the curated set is what these tools are designed and tested around. Pre-loaded instance context (also available as globals here): - contentTypes, sites, languages, currentUser Use these to cross-reference spec endpoints with what the connected instance actually has. -Example: +Examples: + // endpoint summary + param names const op = spec.paths['/api/v1/contenttype'].get return { summary: op.summary, params: op.parameters?.map(p => p.name) } -When inspecting workflow \`fire\` operations, always check the \`indexPolicy\` query parameterand its allowed values. When the \`execute\` tool needs to chain multiple fire calls, or fireand then immediately read content, use \`indexPolicy=WAIT_FOR\` to ensure the index isupdated before the next operation runs. + // the request-body schema of an endpoint, one level deep + return resolveRef(spec.paths['/api/v1/contenttype'].post.requestBody.content['application/json'].schema, 1) + + // a named schema, two levels deep + return resolveRef('ContentType', 2) + +When inspecting workflow \`fire\` operations, always check the \`indexPolicy\` query parameter and its allowed values. When the \`execute\` tool needs to chain multiple fire calls, or fire and then immediately read content, use \`indexPolicy=WAIT_FOR\` to ensure the index is updated before the next operation runs. Common recipes: - **Find a workflow action ID for a contentlet:** call GET /api/v1/workflow/contentlet/{inode}/actions — direct lookup of actions firable on this contentlet right now. The \`workflowActionId\` returned here is the UUID required by PUT /api/v1/workflow/actions/{actionId}/fire and PUT /api/v1/workflow/contentlet/actions/bulk/fire. **Do not** pass system-action enum values (NEW, EDIT, PUBLISH, …) as the action ID; those are only valid for the /default/fire/{systemAction} endpoints. @@ -59,33 +80,19 @@ export default async function handler( return 'Error: OpenAPI spec is not available. The server may not have been built with a generated spec (run the generate-spec step), so the search tool cannot run.'; } - // The front door injects the instance context AND the `spec` global (includeSpec). - const dotcms = createRuntime({ - url: process.env.DOTCMS_URL ?? '', - token: process.env.AUTH_TOKEN ?? '', - sessionId: extra?.sessionId ?? '__default__', - timeout: 10000, - includeSpec: true, - onContextError: (label, error) => { - const msg = error instanceof Error ? error.message : String(error); - console.error(`[context] failed to load ${label}: ${msg}`); - } - }); - - const result = await dotcms.run(code); - - if (!result.success) { - const errorMsg = result.error - ? `${result.error.name}: ${result.error.message}` - : 'Unknown error'; - const logs = result.logs.length > 0 ? `\nLogs:\n${result.logs.join('\n')}` : ''; - return `Error: ${errorMsg}${logs}`; - } + // Guarded for the same reason as `execute`: a config failure must reach the model as a + // tool result it can read, not as an MCP protocol error. + try { + // The front door injects the instance context AND the `spec` global (includeSpec). + const dotcms = runtimeFromEnv(extra?.sessionId, { timeout: 10000, includeSpec: true }); - const output = - typeof result.value === 'string' ? result.value : JSON.stringify(result.value, null, 2); + const result = await dotcms.run(code); - const logs = result.logs.length > 0 ? `\n\n--- Logs ---\n${result.logs.join('\n')}` : ''; - - return `${output}${logs}`; + return formatSandboxResult(result, { + truncationHint: + 'Use resolveRef(schemaOrName, depth) to expand one schema at a bounded depth.' + }); + } catch (error) { + return toolFailure('search', error); + } } diff --git a/core-web/apps/mcp-server/src/tools/upload_assets.spec.ts b/core-web/apps/mcp-server/src/tools/upload_assets.spec.ts new file mode 100644 index 000000000000..81374ce203a1 --- /dev/null +++ b/core-web/apps/mcp-server/src/tools/upload_assets.spec.ts @@ -0,0 +1,44 @@ +import { lenientBoolean } from './upload_assets'; + +describe('lenientBoolean', () => { + const publish = lenientBoolean(true); + const verify = lenientBoolean(false); + + it('passes real booleans through', () => { + expect(publish.parse(true)).toBe(true); + expect(publish.parse(false)).toBe(false); + }); + + it.each([ + ['false', false], + ['FALSE', false], + ['0', false], + ['no', false], + ['true', true], + ['TRUE', true], + ['1', true], + ['yes', true], + [' false ', false] + ])('maps the string %p to %p', (input, expected) => { + // Plain z.coerce.boolean() is wrong here: it uses JS truthiness, so "false" → true. + expect(publish.parse(input)).toBe(expected); + }); + + it('applies the default when the value is absent', () => { + expect(publish.parse(undefined)).toBe(true); + expect(verify.parse(undefined)).toBe(false); + }); + + it('applies the default for an empty string rather than failing validation', () => { + // `.default()` only substitutes on `undefined`, so returning the original '' from the + // preprocessor skipped the default entirely and failed with "Expected boolean, + // received string" — for an argument the caller never set. + expect(publish.parse('')).toBe(true); + expect(verify.parse('')).toBe(false); + expect(publish.parse(' ')).toBe(true); + }); + + it('still rejects a string that means nothing', () => { + expect(() => publish.parse('maybe')).toThrow(); + }); +}); diff --git a/core-web/apps/mcp-server/src/tools/upload_assets.ts b/core-web/apps/mcp-server/src/tools/upload_assets.ts index 6e96be1c0898..44fcb86a460f 100644 --- a/core-web/apps/mcp-server/src/tools/upload_assets.ts +++ b/core-web/apps/mcp-server/src/tools/upload_assets.ts @@ -2,7 +2,27 @@ import { type InferSchema, type ToolExtraArguments, type ToolMetadata } from 'xm import { z } from 'zod'; import { uploadAssets } from '../lib/assets-transfer'; -import { errorMessage, runtimeFromEnv } from '../lib/runtime'; +import { runtimeFromEnv, toolFailure } from '../lib/runtime'; + +/** + * A boolean that also accepts the string forms MCP clients often send ("true"/"false"/"1"/"0"). + * Plain `z.coerce.boolean()` is wrong here: it uses JS truthiness, so the string "false" becomes + * `true`. This maps the string forms to their intended value and leaves real booleans untouched. + */ +export const lenientBoolean = (defaultValue: boolean) => + z.preprocess((value) => { + if (typeof value === 'string') { + const v = value.trim().toLowerCase(); + // An empty (or whitespace-only) string means "I did not set this", so it has to + // become `undefined` — `.default()` only substitutes on `undefined`, so returning + // the original `''` skipped the default entirely and failed validation with + // "Expected boolean, received string" for an argument the caller never set. + if (v === '') return undefined; + if (v === 'false' || v === '0' || v === 'no') return false; + if (v === 'true' || v === '1' || v === 'yes') return true; + } + return value; + }, z.boolean().default(defaultValue)); export const schema = { src: z.string().min(1).describe('Absolute local directory the MCP server reads files from'), @@ -15,15 +35,19 @@ export const schema = { include: z .string() .optional() - .describe('Optional comma-separated glob filter, e.g. *.vtl,*.scss'), - publish: z - .boolean() - .default(true) - .describe('Use /api/v2/assets/publish when true, otherwise /api/v2/assets/save'), - verify: z - .boolean() - .default(true) - .describe('After publishing, verify live status through /api/v1/content/{identifier}') + .describe( + 'Optional comma-separated glob filter, matched against each file path RELATIVE to `src`. ' + + 'Supports *, ? (single non-slash char), ** (globstar, crosses directories), and ' + + '{a,b,c} brace expansion. A pattern with no "/" matches the basename anywhere in the ' + + 'tree; a pattern with a "/" is anchored at the top of `src`. Examples: "*.vtl,*.scss", ' + + '"*.{png,webp,jpg}", "**/*.png". A comma inside {…} does not split patterns.' + ), + publish: lenientBoolean(true).describe( + 'Use /api/v2/assets/publish when true, otherwise /api/v2/assets/save' + ), + verify: lenientBoolean(true).describe( + 'After publishing, verify live status through /api/v1/content/{identifier}' + ) }; export const metadata: ToolMetadata = { @@ -49,6 +73,11 @@ Provide an absolute source directory (\`src\`) and a host-qualified destination \`//demo.dotcms.com/application/themes/travel\`). Optional \`include\` globs limit which files go. The tool preserves relative paths and returns only a JSON manifest — never the file bytes. +Reserved-folder trap: \`assets\` is a RESERVED top-level folder name — a \`dest\` like +\`//host/assets/...\` fails with "reserved folder name: assets". Put files under \`/application\` +(the conventional home for themes, VTL, containers, e.g. \`//host/application/themes/<name>\`) or +another non-reserved path. \`dest\` must be host-qualified (start with \`//<hostname>/\`). + Tip: to avoid inlining large templates, write them to files on disk and upload them with this tool, then reference them from a container/template via \`#dotParse\`.`, annotations: { @@ -76,6 +105,6 @@ export default async function handler( return JSON.stringify(manifest, null, 2); } catch (error) { - return `Error: ${errorMessage(error)}`; + return toolFailure('upload_assets', error); } } diff --git a/core-web/libs/ai-ui/.eslintrc.json b/core-web/libs/ai-ui/.eslintrc.json new file mode 100644 index 000000000000..9aef3251991f --- /dev/null +++ b/core-web/libs/ai-ui/.eslintrc.json @@ -0,0 +1,40 @@ +{ + "extends": ["../../.eslintrc.base.json"], + "ignorePatterns": ["!**/*", "src/test.ts"], + "overrides": [ + { + "files": ["*.ts"], + "extends": [ + "plugin:@nx/angular", + "plugin:@angular-eslint/template/process-inline-templates" + ], + "rules": { + "@angular-eslint/directive-selector": [ + "error", + { + "type": "attribute", + "prefix": "dot", + "style": "camelCase" + } + ], + "@angular-eslint/component-selector": [ + "error", + { + "type": "element", + "prefix": "dot", + "style": "kebab-case" + } + ], + "@angular-eslint/prefer-standalone": "off", + "@angular-eslint/no-input-rename": "off", + "@angular-eslint/no-output-on-prefix": "off", + "@angular-eslint/no-output-native": "off" + } + }, + { + "files": ["*.html"], + "extends": ["plugin:@nx/angular-template"], + "rules": {} + } + ] +} diff --git a/core-web/libs/ai-ui/jest.config.ts b/core-web/libs/ai-ui/jest.config.ts new file mode 100644 index 000000000000..736fe2361935 --- /dev/null +++ b/core-web/libs/ai-ui/jest.config.ts @@ -0,0 +1,28 @@ +/* eslint-disable */ +export default { + displayName: 'ai-ui', + preset: '../../jest.preset.js', + setupFilesAfterEnv: ['<rootDir>/src/test-setup.ts'], + globals: {}, + transform: { + '^.+\\.(ts|mjs|js|html)$': [ + 'jest-preset-angular', + { + tsconfig: '<rootDir>/tsconfig.spec.json', + stringifyContentPathRegex: '\\.(html|svg)$' + } + ] + }, + transformIgnorePatterns: ['node_modules/(?!.*\\.mjs$)'], + snapshotSerializers: [ + 'jest-preset-angular/build/serializers/no-ng-attributes', + 'jest-preset-angular/build/serializers/ng-snapshot', + 'jest-preset-angular/build/serializers/html-comment' + ], + testEnvironment: '@happy-dom/jest-environment', + testEnvironmentOptions: { + errorOnUnknownElements: true, + errorOnUnknownProperties: true + }, + coverageDirectory: '../../coverage/libs/ai-ui' +}; diff --git a/core-web/libs/ai-ui/project.json b/core-web/libs/ai-ui/project.json new file mode 100644 index 000000000000..44448b4348b2 --- /dev/null +++ b/core-web/libs/ai-ui/project.json @@ -0,0 +1,29 @@ +{ + "name": "ai-ui", + "$schema": "../../node_modules/nx/schemas/project-schema.json", + "projectType": "library", + "sourceRoot": "libs/ai-ui/src", + "prefix": "dot", + "tags": [], + "targets": { + "lint": { + "executor": "@nx/eslint:lint", + "outputs": ["{options.outputFile}"] + }, + "test": { + "executor": "@nx/jest:jest", + "outputs": ["{workspaceRoot}/coverage/{projectRoot}"], + "options": { + "jestConfig": "libs/ai-ui/jest.config.ts", + "passWithNoTests": true, + "tsConfig": "libs/ai-ui/tsconfig.spec.json" + }, + "configurations": { + "ci": { + "ci": true, + "codeCoverage": true + } + } + } + } +} diff --git a/core-web/libs/ai-ui/src/index.ts b/core-web/libs/ai-ui/src/index.ts new file mode 100644 index 000000000000..7b5151328481 --- /dev/null +++ b/core-web/libs/ai-ui/src/index.ts @@ -0,0 +1,8 @@ +// Components +export * from './lib/components/dot-agent-activity-log/dot-agent-activity-log.component'; +export * from './lib/components/dot-agent-message/dot-agent-message.component'; +export * from './lib/components/dot-agent-thinking/dot-agent-thinking.component'; + +// Models +export * from './lib/models/agent-message'; +export * from './lib/models/agent-message-presenter'; diff --git a/core-web/libs/ai-ui/src/lib/components/dot-agent-activity-log/dot-agent-activity-log.component.html b/core-web/libs/ai-ui/src/lib/components/dot-agent-activity-log/dot-agent-activity-log.component.html new file mode 100644 index 000000000000..c7b7345d4325 --- /dev/null +++ b/core-web/libs/ai-ui/src/lib/components/dot-agent-activity-log/dot-agent-activity-log.component.html @@ -0,0 +1,10 @@ +<!-- Settled steps (finished) — one bubble each. --> +@for (message of messages(); track message.id; let last = $last) { + <dot-agent-message [message]="message" [last]="last" /> +} + +<!-- Live thinking / working indicator — its own distinct "loading pill", + shown below the steps while the agent is running. --> +@if (working()) { + <dot-agent-thinking [text]="workingText()" [sub]="workingSub()" /> +} diff --git a/core-web/libs/ai-ui/src/lib/components/dot-agent-activity-log/dot-agent-activity-log.component.spec.ts b/core-web/libs/ai-ui/src/lib/components/dot-agent-activity-log/dot-agent-activity-log.component.spec.ts new file mode 100644 index 000000000000..123655e4bf0b --- /dev/null +++ b/core-web/libs/ai-ui/src/lib/components/dot-agent-activity-log/dot-agent-activity-log.component.spec.ts @@ -0,0 +1,169 @@ +import { byTestId, createComponentFactory, mockProvider, Spectator } from '@openng/spectator/jest'; + +import { ApplicationRef } from '@angular/core'; +import { TestBed } from '@angular/core/testing'; + +import { DotMessageService } from '@dotcms/data-access'; + +import { DotAgentActivityLogComponent } from './dot-agent-activity-log.component'; + +import { AgentMessage } from '../../models/agent-message'; + +const MESSAGES: AgentMessage[] = [ + { id: 1, icon: 'search', text: 'Scanning page', tone: 'info' }, + { + id: 2, + icon: 'check', + text: 'Fixed alt text', + sub: 'image-alt · hero.vtl', + tone: 'success' + }, + { id: 3, icon: 'flag', text: 'Reported contrast', tone: 'warning' } +]; + +describe('DotAgentActivityLogComponent', () => { + let spectator: Spectator<DotAgentActivityLogComponent>; + + const createComponent = createComponentFactory({ + component: DotAgentActivityLogComponent, + providers: [mockProvider(DotMessageService, { get: (key: string) => key })] + }); + + beforeEach(() => { + spectator = createComponent(); + }); + + describe('auto-scroll', () => { + /** + * Make the host itself the scroller with the given geometry, so the component's + * `scrollParent` walk stops here. `scrollTop` is a real property on the element, + * so the assertions read back whatever the component wrote (or didn't). + */ + /** Run the component's `afterRenderEffect` — detectChanges alone does not. */ + function flushRender() { + TestBed.inject(ApplicationRef).tick(); + } + + function makeScrollable({ scrollTop }: { scrollTop: number }) { + const host = spectator.element as HTMLElement; + // jsdom does no layout: it reports `overflowY` as undefined and both scroll + // dimensions as 0, so the component's `scrollParent` walk would never find a + // scroller. Stub the three things that walk reads. + jest.spyOn(window, 'getComputedStyle').mockImplementation( + (el) => + (el === host + ? { overflowY: 'auto' } + : { overflowY: 'visible' }) as CSSStyleDeclaration + ); + Object.defineProperty(host, 'scrollHeight', { value: 1000, configurable: true }); + Object.defineProperty(host, 'clientHeight', { value: 400, configurable: true }); + host.scrollTop = scrollTop; + + return host; + } + + afterEach(() => { + jest.restoreAllMocks(); + }); + + it('pins to the bottom while the user is following along', () => { + const host = makeScrollable({ scrollTop: 600 }); // 1000 - 600 - 400 = 0 from bottom + spectator.setInput('messages', MESSAGES); + flushRender(); + + expect(host.scrollTop).toBe(1000); + }); + + it('leaves the scroll alone once the user has scrolled up', () => { + // Scrolling up mid-run is an explicit "leave me here". The scroller is the + // whole surrounding pane in real consumers, so yanking it back made the score + // ring unreadable for the entire run. + const host = makeScrollable({ scrollTop: 100 }); + spectator.setInput('messages', MESSAGES); + flushRender(); + + expect(host.scrollTop).toBe(100); + }); + + it('still pins when only a few px from the bottom (sub-pixel tolerance)', () => { + const host = makeScrollable({ scrollTop: 580 }); // 20px from the bottom + spectator.setInput('messages', MESSAGES); + flushRender(); + + expect(host.scrollTop).toBe(1000); + }); + + it('does not re-pin for a working-text change alone', () => { + // `workingText` ticks every few seconds on a heartbeat with no new content; + // tracking it was what dragged the pane back roughly every 5s. + const host = makeScrollable({ scrollTop: 100 }); + spectator.setInput({ messages: MESSAGES, working: true }); + flushRender(); + // Re-pin so the only thing changing afterwards is the working text. + host.scrollTop = 100; + + spectator.setInput('workingMessage', { id: 9, icon: '', text: 'tick 1' }); + flushRender(); + spectator.setInput('workingMessage', { id: 9, icon: '', text: 'tick 2' }); + flushRender(); + + expect(host.scrollTop).toBe(100); + }); + }); + + it('renders one message bubble per message', () => { + spectator.setInput('messages', MESSAGES); + expect(spectator.queryAll(byTestId('agent-message')).length).toBe(3); + }); + + it('passes each message through so its text renders', () => { + spectator.setInput('messages', MESSAGES); + const steps = spectator.queryAll(byTestId('agent-message')); + expect(steps[1]).toHaveText('Fixed alt text'); + }); + + it('renders no thinking indicator when not working', () => { + spectator.setInput({ messages: MESSAGES, working: false }); + expect(spectator.queryAll(byTestId('agent-message')).length).toBe(3); + expect(spectator.query(byTestId('agent-thinking'))).toBeNull(); + }); + + it('shows the thinking indicator (separate from the settled steps) while working', () => { + spectator.setInput({ messages: MESSAGES, working: true }); + // Settled steps stay as message bubbles; the thinking item is its own node. + expect(spectator.queryAll(byTestId('agent-message')).length).toBe(3); + const thinking = spectator.query(byTestId('agent-thinking')); + expect(thinking).not.toBeNull(); + expect(thinking?.querySelector('[data-testid="agent-thinking-text"]')).not.toBeNull(); + }); + + it('renders the supplied workingMessage text + sub in the thinking indicator', () => { + spectator.setInput({ + messages: MESSAGES, + working: true, + workingMessage: { + id: 'agent-working', + icon: '', + text: 'Still working…', + sub: '8s', + tone: 'info' + } as AgentMessage + }); + const thinking = spectator.query(byTestId('agent-thinking')); + expect(thinking).toHaveText('Still working…'); + expect(thinking).toHaveText('8s'); + }); + + it('falls back to the working key in the thinking indicator when no workingMessage', () => { + spectator.setInput({ + messages: [], + working: true, + workingMessage: null, + workingFallbackKey: 'my.working.key' + }); + expect(spectator.queryAll(byTestId('agent-message')).length).toBe(0); + const thinking = spectator.query(byTestId('agent-thinking')); + expect(thinking).toHaveText('my.working.key'); + expect(thinking?.querySelector('[data-testid="agent-thinking-text"]')).not.toBeNull(); + }); +}); diff --git a/core-web/libs/ai-ui/src/lib/components/dot-agent-activity-log/dot-agent-activity-log.component.ts b/core-web/libs/ai-ui/src/lib/components/dot-agent-activity-log/dot-agent-activity-log.component.ts new file mode 100644 index 000000000000..ec6fa487e90b --- /dev/null +++ b/core-web/libs/ai-ui/src/lib/components/dot-agent-activity-log/dot-agent-activity-log.component.ts @@ -0,0 +1,151 @@ +import { + afterRenderEffect, + ChangeDetectionStrategy, + Component, + computed, + ElementRef, + inject, + input +} from '@angular/core'; + +import { DotMessageService } from '@dotcms/data-access'; + +import { AgentMessage } from '../../models/agent-message'; +import { DotAgentMessageComponent } from '../dot-agent-message/dot-agent-message.component'; +import { DotAgentThinkingComponent } from '../dot-agent-thinking/dot-agent-thinking.component'; + +/** + * How close to the bottom still counts as "following along", in px. Absorbs fractional + * layout heights and a partially-rendered in-flight row, either of which would otherwise + * read as the user having scrolled away. + */ +const PINNED_TO_BOTTOM_TOLERANCE_PX = 32; + +/** + * The shared "watch the agent work" surface — a thin composer. + * + * It renders one settled bubble per message ({@link DotAgentMessageComponent}) and, + * while the agent is running, appends ONE live thinking indicator at the end + * ({@link DotAgentThinkingComponent}). The thinking item is a distinct component — + * not a settled bubble with a spinner bolted on — driven by {@link workingMessage} + * (which a consumer updates from the agent's current step + keep-alive heartbeat), + * so a long, quiet step shows reassuring, ticking copy instead of looking hung. + * The log auto-scrolls to the latest entry as it grows. + * + * Layout is the consumer's: the component imposes NO sizing on its own box (no + * height, no flex, no overflow, no margins) — it just grows with its content. + * Where it scrolls is the consumer's call: + * - give the host a bounded height + `overflow-y-auto` and it scrolls itself; + * - or place it inside a taller scroll container (among other content) and + * that container scrolls. + * Either way, auto-scroll-to-latest follows the nearest scrollable ancestor + * (including the host), so the newest entry stays in view without the consumer + * wiring anything. + */ +@Component({ + selector: 'dot-agent-activity-log', + imports: [DotAgentMessageComponent, DotAgentThinkingComponent], + templateUrl: './dot-agent-activity-log.component.html', + changeDetection: ChangeDetectionStrategy.OnPush, + host: { class: 'flex flex-col', 'data-testid': 'agent-activity-steps' } +}) +export class DotAgentActivityLogComponent { + /** The settled bubbles — completed steps and/or the expanded terminal result. */ + readonly messages = input<AgentMessage[]>([]); + + /** + * Drives the live thinking indicator shown while {@link working} is true. The + * consumer updates it from the agent's current step + heartbeat, so it reads as + * "still working…" and ticks even when no new step has landed. When null the + * indicator falls back to {@link workingFallbackKey}. Only `text`/`sub` are + * used — the thinking component owns its own spinner + styling. + */ + readonly workingMessage = input<AgentMessage | null>(null); + + /** Whether the agent is actively running (shows the live thinking indicator). */ + readonly working = input<boolean>(false); + + /** + * i18n key for the fallback thinking text when the agent is working but has + * nothing specific to show yet. Consumers override it with their own key. + */ + readonly workingFallbackKey = input<string>('agent.activity.working'); + + readonly #dm = inject(DotMessageService); + readonly #host = inject<ElementRef<HTMLElement>>(ElementRef); + + /** Primary line for the thinking indicator (working message, or the fallback). */ + readonly workingText = computed<string>( + () => this.workingMessage()?.text ?? this.#dm.get(this.workingFallbackKey()) + ); + + /** Optional secondary line for the thinking indicator (e.g. elapsed seconds). */ + readonly workingSub = computed<string | undefined>(() => this.workingMessage()?.sub); + + constructor() { + // Keep the latest entry — the live thinking indicator — in view as the + // agent streams its activity. Uses afterRenderEffect (not a plain effect) + // so it runs AFTER the newly-appended bubble is laid out; a plain effect + // reads scrollHeight before the new DOM exists and stops one row short. + // It pins whichever element actually scrolls — the host if the consumer + // made it a scroll box, otherwise its nearest scrollable ancestor. + // + // Auto-scroll ONLY while the user is already at the bottom. The scroller is the + // nearest scrollable ancestor, which in a consumer like the Accessibility Studio + // is the whole side pane — score ring, legend and issue list included — so an + // unconditional pin dragged the user back down every time it ran. Scrolling up to + // read something mid-run is an explicit "leave me here", and this now honours it + // until they scroll back to the bottom themselves. + // + // NOTE: `workingText` is deliberately NOT tracked. It changes on every heartbeat + // (a few seconds apart) with no new content, so it was the reason a run yanked the + // pane back roughly every 5 seconds for its entire duration. + afterRenderEffect(() => { + const count = this.messages().length; + const working = this.working(); + if (!count && !working) { + return; + } + const scroller = this.#scrollParent(this.#host.nativeElement); + // Measured BEFORE the write, or the comparison is against the value we are + // about to set and every check trivially passes. + if (scroller && this.#isPinnedToBottom(scroller)) { + scroller.scrollTop = scroller.scrollHeight; + } + }); + } + + /** + * Whether the scroller is at (or within a hair of) the bottom. + * + * The tolerance absorbs fractional layout heights and the partially-rendered row that + * is normally in flight while content streams in — without it, sub-pixel rounding + * alone would read as "the user scrolled away" and auto-scroll would stop for good. + */ + #isPinnedToBottom(scroller: HTMLElement): boolean { + const distanceFromBottom = + scroller.scrollHeight - scroller.scrollTop - scroller.clientHeight; + + return distanceFromBottom <= PINNED_TO_BOTTOM_TOLERANCE_PX; + } + + /** + * Walk up from the host to the nearest ancestor that scrolls vertically + * (overflow auto/scroll and actually overflowing), or the host itself if it + * scrolls. Returns null when nothing scrolls (the log grows freely). + */ + #scrollParent(from: HTMLElement): HTMLElement | null { + let el: HTMLElement | null = from; + while (el) { + const overflowY = getComputedStyle(el).overflowY; + const scrolls = + (overflowY === 'auto' || overflowY === 'scroll') && + el.scrollHeight > el.clientHeight; + if (scrolls) { + return el; + } + el = el.parentElement; + } + return null; + } +} diff --git a/core-web/libs/ai-ui/src/lib/components/dot-agent-message/dot-agent-message.component.html b/core-web/libs/ai-ui/src/lib/components/dot-agent-message/dot-agent-message.component.html new file mode 100644 index 000000000000..0eda9a08a375 --- /dev/null +++ b/core-web/libs/ai-ui/src/lib/components/dot-agent-message/dot-agent-message.component.html @@ -0,0 +1,30 @@ +<div class="flex flex-none flex-col items-center"> + <!-- Timeline dot. dot-color-icon's own sizes (48/56px) are far too big for a + log row, so the w/h + radius are overridden here — it's kept for the + tone-driven bg/fg derivation, which is the part worth sharing. --> + <dot-color-icon + class="size-7.5! rounded-lg!" + size="sm" + aria-hidden="true" + data-testid="agent-message-chip" + [color]="toneColor()"> + <span class="material-symbols-outlined text-base!" data-testid="agent-message-icon"> + {{ message().icon }} + </span> + </dot-color-icon> + @if (!last()) { + <div + class="mt-0.5 min-h-1.5 w-0.5 flex-1 bg-surface-100" + data-testid="agent-message-connector"></div> + } +</div> +<div class="min-w-0 pt-1"> + <div class="text-color" data-testid="agent-message-text"> + {{ message().text }} + </div> + @if (message().sub) { + <div class="mt-0.5 text-muted-color" data-testid="agent-message-sub"> + {{ message().sub }} + </div> + } +</div> diff --git a/core-web/libs/ai-ui/src/lib/components/dot-agent-message/dot-agent-message.component.spec.ts b/core-web/libs/ai-ui/src/lib/components/dot-agent-message/dot-agent-message.component.spec.ts new file mode 100644 index 000000000000..7f02e7a6ae92 --- /dev/null +++ b/core-web/libs/ai-ui/src/lib/components/dot-agent-message/dot-agent-message.component.spec.ts @@ -0,0 +1,83 @@ +import { byTestId, createComponentFactory, Spectator } from '@openng/spectator/jest'; + +import { DotAgentMessageComponent } from './dot-agent-message.component'; + +import { AgentMessage } from '../../models/agent-message'; + +const MESSAGE: AgentMessage = { + id: 1, + icon: 'check', + text: 'Fixed alt text', + sub: 'image-alt · hero.vtl', + tone: 'success' +}; + +describe('DotAgentMessageComponent', () => { + let spectator: Spectator<DotAgentMessageComponent>; + + const createComponent = createComponentFactory(DotAgentMessageComponent); + + /** The tone accent dot-color-icon resolved onto its host custom property. */ + const chipColor = () => + spectator + .query(byTestId('agent-message-chip')) + ?.getAttribute('style') + ?.match(/--dot-color-icon-color:\s*([^;]+)/)?.[1] + ?.trim(); + + beforeEach(() => { + spectator = createComponent({ props: { message: MESSAGE } }); + }); + + it('renders the message text and sub', () => { + expect(spectator.query(byTestId('agent-message-text'))).toHaveText('Fixed alt text'); + expect(spectator.query(byTestId('agent-message-sub'))).toHaveText('image-alt · hero.vtl'); + }); + + it('renders the icon as a material symbol ligature', () => { + const icon = spectator.query(byTestId('agent-message-icon')); + expect(icon).toBeTruthy(); + expect(icon).toHaveText('check'); + }); + + it('keeps the timeline-dot size override on the chip host', () => { + const chip = spectator.query(byTestId('agent-message-chip')); + expect(chip).toHaveClass('size-7.5!'); + expect(chip).toHaveClass('rounded-lg!'); + }); + + it('tints the icon chip by tone', () => { + expect(chipColor()).toBe('var(--p-green-500)'); + }); + + it('maps each tone to its accent color', () => { + spectator.setInput('message', { ...MESSAGE, tone: 'warning' }); + expect(chipColor()).toBe('var(--p-orange-500)'); + spectator.setInput('message', { ...MESSAGE, tone: 'info' }); + expect(chipColor()).toBe('var(--p-primary-500)'); + spectator.setInput('message', { ...MESSAGE, tone: 'danger' }); + expect(chipColor()).toBe('var(--p-red-500)'); + }); + + it('omits the sub-line when absent', () => { + spectator.setInput('message', { + id: 2, + icon: 'search', + text: 'Scanning', + tone: 'info' + }); + expect(spectator.query(byTestId('agent-message-sub'))).toBeNull(); + }); + + it('hides the connector on the last bubble and shows it otherwise', () => { + // Default: standalone/last → no connector. + expect(spectator.query(byTestId('agent-message-connector'))).toBeNull(); + spectator.setInput('last', false); + expect(spectator.query(byTestId('agent-message-connector'))).not.toBeNull(); + }); + + it('renders the settled message icon (never a spinner)', () => { + expect(spectator.query(byTestId('agent-message-icon'))).toHaveText('check'); + expect(spectator.query(byTestId('agent-thinking-spinner'))).toBeNull(); + }); +}); diff --git a/core-web/libs/ai-ui/src/lib/components/dot-agent-message/dot-agent-message.component.ts b/core-web/libs/ai-ui/src/lib/components/dot-agent-message/dot-agent-message.component.ts new file mode 100644 index 000000000000..ad6054c79d3a --- /dev/null +++ b/core-web/libs/ai-ui/src/lib/components/dot-agent-message/dot-agent-message.component.ts @@ -0,0 +1,72 @@ +import { ChangeDetectionStrategy, Component, computed, input } from '@angular/core'; + +import { DotColorIconComponent } from '@dotcms/ui'; + +import { AgentMessage, AgentMessageTone } from '../../models/agent-message'; + +/** + * `dot-color-icon` accent per message tone. The component derives the chip's + * background + foreground from this single color, so tones stay one token each. + */ +const TONE_COLOR: Record<AgentMessageTone, string> = { + info: 'primary', + success: 'green', + warning: 'orange', + danger: 'red' +}; + +/** + * One SETTLED agent activity bubble: a tone-tinted icon chip, an optional + * connector line down to the next bubble, and the message text + optional + * sub-line. Pure presentation — the {@link AgentMessage} view-model carries + * everything it needs. This renders finished steps only; the live "in-progress" + * state is a separate primitive ({@link DotAgentThinkingComponent}). Render it + * standalone or inside a list (see {@link DotAgentActivityLogComponent}). + */ +@Component({ + selector: 'dot-agent-message', + imports: [DotColorIconComponent], + templateUrl: './dot-agent-message.component.html', + // The entrance animation lives here, not in the app's Tailwind theme: it is + // this library's own presentation detail, and a consuming app shouldn't have to + // register a keyframe for the component to look right. Same treatment as the + // shimmer in DotAgentThinkingComponent. + styles: [ + ` + @keyframes agent-enter { + from { + opacity: 0; + transform: translateY(6px); + } + to { + opacity: 1; + transform: translateY(0); + } + } + + @media (prefers-reduced-motion: no-preference) { + :host { + animation: agent-enter 0.28s ease-out both; + } + } + ` + ], + changeDetection: ChangeDetectionStrategy.OnPush, + host: { + class: 'relative flex gap-3 py-1.5', + 'data-testid': 'agent-message' + } +}) +export class DotAgentMessageComponent { + /** The bubble to render. */ + readonly message = input.required<AgentMessage>(); + + /** + * Whether this is the last bubble in a sequence — hides the trailing + * connector line. Defaults to true (standalone bubbles have no connector). + */ + readonly last = input<boolean>(true); + + /** `dot-color-icon` accent for the message's tone. */ + readonly toneColor = computed<string>(() => TONE_COLOR[this.message().tone]); +} diff --git a/core-web/libs/ai-ui/src/lib/components/dot-agent-thinking/dot-agent-thinking.component.html b/core-web/libs/ai-ui/src/lib/components/dot-agent-thinking/dot-agent-thinking.component.html new file mode 100644 index 000000000000..d3589a014732 --- /dev/null +++ b/core-web/libs/ai-ui/src/lib/components/dot-agent-thinking/dot-agent-thinking.component.html @@ -0,0 +1,15 @@ +<!-- Spinner + gradient-text shimmer (AI-chatbot style): the spinner is the clear + motion cue; the shimmer sweeps a band through the label as it works. The + shimmer is plain CSS (see the component styles) so it never clips the text. --> +<i + class="pi pi-spin pi-spinner flex-none text-primary" + aria-hidden="true" + data-testid="agent-thinking-spinner"></i> +<span class="agent-shimmer min-w-0 font-medium" data-testid="agent-thinking-text"> + {{ text() }} +</span> +@if (sub()) { + <span class="flex-none text-muted-color tabular-nums" data-testid="agent-thinking-sub"> + {{ sub() }} + </span> +} diff --git a/core-web/libs/ai-ui/src/lib/components/dot-agent-thinking/dot-agent-thinking.component.spec.ts b/core-web/libs/ai-ui/src/lib/components/dot-agent-thinking/dot-agent-thinking.component.spec.ts new file mode 100644 index 000000000000..7e685b216572 --- /dev/null +++ b/core-web/libs/ai-ui/src/lib/components/dot-agent-thinking/dot-agent-thinking.component.spec.ts @@ -0,0 +1,30 @@ +import { byTestId, createComponentFactory, Spectator } from '@openng/spectator/jest'; + +import { DotAgentThinkingComponent } from './dot-agent-thinking.component'; + +describe('DotAgentThinkingComponent', () => { + let spectator: Spectator<DotAgentThinkingComponent>; + + const createComponent = createComponentFactory(DotAgentThinkingComponent); + + beforeEach(() => { + spectator = createComponent({ props: { text: 'Thinking…' } }); + }); + + it('renders a spinner + gradient-shimmer label', () => { + // Spinner is the clear motion cue… + expect(spectator.query(byTestId('agent-thinking-spinner'))).toBeTruthy(); + // …and the label carries the shimmer class (styling is component CSS). + expect(spectator.query(byTestId('agent-thinking-text'))).toBeTruthy(); + }); + + it('renders the primary text in the shimmer label', () => { + expect(spectator.query(byTestId('agent-thinking-text'))).toHaveText('Thinking…'); + }); + + it('renders the sub-line when provided, omits it otherwise', () => { + expect(spectator.element).not.toHaveText('12s'); + spectator.setInput('sub', '12s'); + expect(spectator.element).toHaveText('12s'); + }); +}); diff --git a/core-web/libs/ai-ui/src/lib/components/dot-agent-thinking/dot-agent-thinking.component.ts b/core-web/libs/ai-ui/src/lib/components/dot-agent-thinking/dot-agent-thinking.component.ts new file mode 100644 index 000000000000..70f6e400bcc9 --- /dev/null +++ b/core-web/libs/ai-ui/src/lib/components/dot-agent-thinking/dot-agent-thinking.component.ts @@ -0,0 +1,95 @@ +import { ChangeDetectionStrategy, Component, input } from '@angular/core'; + +/** + * The live "thinking / working" indicator for the agent activity log. + * + * A distinct primitive from {@link DotAgentMessageComponent}: a settled message is + * a finished step, whereas this is the agent's *current, in-progress* state — a + * spinner beside generic "working" copy with an AI-chatbot-style gradient-text + * shimmer sweeping through it (plus an optional elapsed sub-line). It deliberately + * does NOT look like a settled step bubble, and its text is always generic loading + * copy — never a step message. + * + * Render it once, at the bottom of the log, while a run is in flight (see + * {@link DotAgentActivityLogComponent}). Styling is Tailwind-only. + */ +@Component({ + selector: 'dot-agent-thinking', + templateUrl: './dot-agent-thinking.component.html', + // Plain CSS for the gradient-text shimmer: background-clip:text is fiddly and + // Tailwind utilities can't express the moving, REPEATING gradient cleanly. A + // repeating gradient is what keeps the sweep from clipping the leading glyphs — + // there's always paint under every character regardless of the sweep offset. + styles: [ + ` + .agent-shimmer { + background: repeating-linear-gradient( + 100deg, + var(--p-gray-500, #6b7280) 0%, + var(--p-gray-500, #6b7280) 40%, + var(--p-gray-900, #111827) 50%, + var(--p-gray-500, #6b7280) 60%, + var(--p-gray-500, #6b7280) 100% + ); + background-size: 200% 100%; + background-clip: text; + -webkit-background-clip: text; + color: transparent; + -webkit-text-fill-color: transparent; + animation: agent-shimmer-sweep 2s linear infinite; + } + + @keyframes agent-shimmer-sweep { + from { + background-position: 200% 0; + } + to { + background-position: 0 0; + } + } + + @media (prefers-reduced-motion: reduce) { + .agent-shimmer { + background: none; + color: var(--p-gray-500, #6b7280); + -webkit-text-fill-color: currentColor; + animation: none; + } + } + + /* Entrance animation, owned here rather than in the app's Tailwind theme + so this library carries its own presentation (see DotAgentMessageComponent). */ + @keyframes agent-enter { + from { + opacity: 0; + transform: translateY(6px); + } + to { + opacity: 1; + transform: translateY(0); + } + } + + @media (prefers-reduced-motion: no-preference) { + :host { + animation: agent-enter 0.28s ease-out both; + } + } + ` + ], + changeDetection: ChangeDetectionStrategy.OnPush, + host: { + class: 'flex items-center gap-2.5 py-2', + 'data-testid': 'agent-thinking', + // Announce the busy state to assistive tech (the spinner is decorative). + role: 'status', + 'aria-live': 'polite' + } +}) +export class DotAgentThinkingComponent { + /** The primary line — generic loading/working/thinking copy (never a step). */ + readonly text = input.required<string>(); + + /** Optional secondary line, e.g. elapsed seconds on the current action. */ + readonly sub = input<string | undefined>(undefined); +} diff --git a/core-web/libs/ai-ui/src/lib/models/agent-message-presenter.ts b/core-web/libs/ai-ui/src/lib/models/agent-message-presenter.ts new file mode 100644 index 000000000000..d1bdf0f85c8d --- /dev/null +++ b/core-web/libs/ai-ui/src/lib/models/agent-message-presenter.ts @@ -0,0 +1,26 @@ +import { AgentRunStep } from '@dotcms/dotcms-models'; + +import { AgentMessage } from './agent-message'; + +/** + * Per-agent strategy that turns an agent's stream into renderable + * {@link AgentMessage} bubbles for the shared activity log. This is the seam + * that keeps the shared UI agent-agnostic: the presenter is the only place that + * knows the agent's domain shapes (its step phases, its result payload). + * + * @typeParam TResult The agent's terminal result payload + * (the `done`/`aborted` event data). + */ +export interface AgentMessagePresenter<TResult> { + /** + * Map one live SSE step to a bubble (icon + tone chosen from the step's + * `meta`). `index` is the step's position in the run, useful for a stable id. + */ + liveStep(step: AgentRunStep, index: number): AgentMessage; + + /** + * Expand the terminal result into the bubbles that summarize it + * ("result-as-more-messages"). Called once the run completes. + */ + resultMessages(result: TResult): AgentMessage[]; +} diff --git a/core-web/libs/ai-ui/src/lib/models/agent-message.ts b/core-web/libs/ai-ui/src/lib/models/agent-message.ts new file mode 100644 index 000000000000..6c42f066c568 --- /dev/null +++ b/core-web/libs/ai-ui/src/lib/models/agent-message.ts @@ -0,0 +1,39 @@ +/** + * The render view-model for the shared agent activity log. This is a UI concern + * — not part of the agent wire contract (see `@dotcms/dotcms-models` for that). + * An agent's presenter turns its live steps and terminal result into a list of + * these; the shared components ({@link DotAgentMessageComponent} et al.) draw them. + */ + +/** + * Visual tone of a rendered agent message — drives the bubble/icon color. + * Agent-neutral: a presenter maps its own outcomes (e.g. "fixed" → `success`, + * "reported" → `warning`) onto these. + */ +export const AGENT_MESSAGE_TONE = { + INFO: 'info', + SUCCESS: 'success', + WARNING: 'warning', + DANGER: 'danger' +} as const; + +export type AgentMessageTone = (typeof AGENT_MESSAGE_TONE)[keyof typeof AGENT_MESSAGE_TONE]; + +/** + * A single renderable line in the shared agent activity log. Produced by a + * presenter ("result-as-more-messages"), consumed by the message component. + * + * @property id Stable id for `@for` tracking + entry animation. + * @property icon Material Symbols ligature name (e.g. `check`) — a render detail + * the presenter chooses; the agent response carries no icon. + * @property text Primary line. + * @property sub Optional secondary line (e.g. a rule id + file). + * @property tone Bubble/icon color — see {@link AgentMessageTone}. + */ +export interface AgentMessage { + id: string | number; + icon: string; + text: string; + sub?: string; + tone: AgentMessageTone; +} diff --git a/core-web/libs/ai-ui/src/test-setup.ts b/core-web/libs/ai-ui/src/test-setup.ts new file mode 100644 index 000000000000..010932d7a07e --- /dev/null +++ b/core-web/libs/ai-ui/src/test-setup.ts @@ -0,0 +1,90 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ + +import '@testing-library/jest-dom'; +import { setupZoneTestEnv } from 'jest-preset-angular/setup-env/zone'; +setupZoneTestEnv(); + +// Mock PointerEvent +class MockPointerEvent implements Partial<PointerEvent> { + public clientX?: number; + public clientY?: number; + public pointerType?: string; + public pressure?: number; + public relatedTarget?: EventTarget | null; + + constructor(type: string, props: PointerEventInit = {}) { + Object.assign(this, props); + } +} +(globalThis as any).PointerEvent = MockPointerEvent; + +/* global mocks for jsdom */ +const mock = () => { + let storage: { [key: string]: string } = {}; + + return { + getItem: (key: string) => (key in storage ? storage[key] : null), + setItem: (key: string, value: string) => (storage[key] = value || ''), + removeItem: (key: string) => delete storage[key], + clear: () => (storage = {}) + }; +}; + +Object.defineProperty(window, 'localStorage', { value: mock() }); +Object.defineProperty(window, 'sessionStorage', { value: mock() }); +Object.defineProperty(window, 'getComputedStyle', { + value: () => ({ + getPropertyValue: (prop: string) => '', + setProperty: (propertyName: string, value: string) => {} + }) +}); + +Object.defineProperty(document.body.style, 'transform', { + value: () => ({ + enumerable: true, + configurable: true + }) +}); + +// structuredClone is not exposed by the happy-dom sandbox — polyfill for tests +if (typeof globalThis.structuredClone === 'undefined') { + globalThis.structuredClone = <T>(obj: T): T => JSON.parse(JSON.stringify(obj)); +} + +// PrimeNG mocks +(globalThis as any).ResizeObserver = class ResizeObserver { + observe() {} + unobserve() {} + disconnect() {} +}; + +// Mock window.matchMedia +Object.defineProperty(window, 'matchMedia', { + writable: true, + value: jest.fn().mockImplementation((query) => ({ + matches: false, + media: query, + onchange: null, + addListener: jest.fn(), + removeListener: jest.fn(), + addEventListener: jest.fn(), + removeEventListener: jest.fn(), + dispatchEvent: jest.fn() + })) +}); + +// Mock IntersectionObserver +(globalThis as any).IntersectionObserver = class IntersectionObserver { + constructor() {} + observe() { + return null; + } + unobserve() { + return null; + } + disconnect() { + return null; + } +}; diff --git a/core-web/libs/ai-ui/tsconfig.json b/core-web/libs/ai-ui/tsconfig.json new file mode 100644 index 000000000000..3011ac3588a2 --- /dev/null +++ b/core-web/libs/ai-ui/tsconfig.json @@ -0,0 +1,19 @@ +{ + "extends": "../../tsconfig.base.json", + "files": [], + "include": [], + "references": [ + { + "path": "./tsconfig.lib.json" + }, + { + "path": "./tsconfig.spec.json" + } + ], + "compilerOptions": { + "target": "ES2022", + "module": "preserve", + "moduleResolution": "bundler", + "lib": ["dom", "dom.iterable", "es2022"] + } +} diff --git a/core-web/libs/ai-ui/tsconfig.lib.json b/core-web/libs/ai-ui/tsconfig.lib.json new file mode 100644 index 000000000000..b9f3dc159814 --- /dev/null +++ b/core-web/libs/ai-ui/tsconfig.lib.json @@ -0,0 +1,19 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "../../dist/out-tsc", + "target": "es2015", + "declaration": true, + "declarationMap": true, + "inlineSources": true, + "types": [], + "lib": ["dom", "es2022"] + }, + "angularCompilerOptions": { + "skipTemplateCodegen": true, + "strictMetadataEmit": true, + "enableResourceInlining": true + }, + "exclude": ["src/test.ts", "**/*.spec.ts"], + "include": ["**/*.ts"] +} diff --git a/core-web/libs/ai-ui/tsconfig.spec.json b/core-web/libs/ai-ui/tsconfig.spec.json new file mode 100644 index 000000000000..599f58c08f5e --- /dev/null +++ b/core-web/libs/ai-ui/tsconfig.spec.json @@ -0,0 +1,13 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "../../dist/out-tsc", + "module": "preserve", + "target": "es2016", + "types": ["jest", "node"], + "moduleResolution": "bundler", + "isolatedModules": true + }, + "files": ["src/test-setup.ts"], + "include": ["jest.config.ts", "src/**/*.spec.ts", "src/**/*.test.ts", "src/**/*.d.ts"] +} diff --git a/core-web/libs/data-access/src/index.ts b/core-web/libs/data-access/src/index.ts index 578d42570b24..75c3d5ae76d0 100644 --- a/core-web/libs/data-access/src/index.ts +++ b/core-web/libs/data-access/src/index.ts @@ -1,5 +1,6 @@ export * from './lib/add-to-bundle/add-to-bundle.service'; export * from './lib/can-deactivate/can-deactivate-guard.service'; +export * from './lib/dot-agent-run/dot-agent-run.service'; export * from './lib/dot-ai/dot-ai.service'; export * from './lib/dot-alert-confirm/dot-alert-confirm.service'; export * from './lib/dot-analytics-search/dot-analytics-search.service'; diff --git a/core-web/libs/data-access/src/lib/dot-agent-run/dot-agent-run.service.spec.ts b/core-web/libs/data-access/src/lib/dot-agent-run/dot-agent-run.service.spec.ts new file mode 100644 index 000000000000..216f6363cd9d --- /dev/null +++ b/core-web/libs/data-access/src/lib/dot-agent-run/dot-agent-run.service.spec.ts @@ -0,0 +1,428 @@ +import { createHttpFactory, HttpMethod, SpectatorHttp } from '@openng/spectator/jest'; +import { firstValueFrom, toArray } from 'rxjs'; + +import { AgentStreamEvent } from '@dotcms/dotcms-models'; + +import { DotAgentRunService } from './dot-agent-run.service'; + +/** Build a mock fetch Response whose body streams the given SSE text in chunks. */ +function mockSseResponse(chunks: string[], { ok = true, status = 200 } = {}): Response { + const encoder = new TextEncoder(); + let i = 0; + const body = { + getReader() { + return { + read() { + if (i < chunks.length) { + return Promise.resolve({ value: encoder.encode(chunks[i++]), done: false }); + } + + return Promise.resolve({ value: undefined, done: true }); + } + }; + } + } as unknown as ReadableStream<Uint8Array>; + + return { ok, status, statusText: 'OK', body } as unknown as Response; +} + +/** + * A response body the test drives read-by-read. Each entry is either a chunk to emit or a + * rejection, so a failure can be placed AFTER events have already been delivered — the case + * that distinguishes "the connection never opened" from "it died mid-run". + */ +function controllableSseResponse(steps: Array<string | Error>): { + response: Response; + reads: () => number; +} { + const encoder = new TextEncoder(); + let i = 0; + const body = { + getReader() { + return { + read() { + if (i >= steps.length) { + return Promise.resolve({ value: undefined, done: true }); + } + const step = steps[i++]; + + return step instanceof Error + ? Promise.reject(step) + : Promise.resolve({ value: encoder.encode(step), done: false }); + } + }; + } + } as unknown as ReadableStream<Uint8Array>; + + return { + response: { ok: true, status: 200, statusText: 'OK', body } as unknown as Response, + reads: () => i + }; +} + +/** A body that never resolves a read, so the stream stays open until it is aborted. */ +function neverEndingSseResponse(): Response { + const body = { + getReader() { + return { read: () => new Promise<never>(() => undefined) }; + } + } as unknown as ReadableStream<Uint8Array>; + + return { ok: true, status: 200, statusText: 'OK', body } as unknown as Response; +} + +interface DemoResult { + total: number; +} + +describe('DotAgentRunService', () => { + let spectator: SpectatorHttp<DotAgentRunService>; + let service: DotAgentRunService; + const fetchMock = jest.fn(); + const originalFetch = global.fetch; + + const createHttp = createHttpFactory(DotAgentRunService); + + beforeEach(() => { + spectator = createHttp(); + service = spectator.service; + global.fetch = fetchMock as unknown as typeof fetch; + fetchMock.mockReset(); + }); + + afterEach(() => { + global.fetch = originalFetch; + }); + + describe('run() SSE parsing', () => { + it('emits step events with message split from meta, then done with the result', async () => { + fetchMock.mockResolvedValue( + mockSseResponse([ + 'event: step\ndata: {"phase":"scan","message":"Scanning page"}\n\n', + 'event: step\ndata: {"phase":"fix","message":"Fixed alt text"}\n\n', + 'event: done\ndata: {"total":3}\n\n' + ]) + ); + + const events = await firstValueFrom( + service.run<DemoResult>('/api/v1/agent/demo/stream', { id: 'x' }).pipe(toArray()) + ); + + expect(fetchMock).toHaveBeenCalledWith( + '/api/v1/agent/demo/stream', + expect.objectContaining({ method: 'POST' }) + ); + expect(events).toEqual<AgentStreamEvent<DemoResult>[]>([ + { type: 'step', step: { message: 'Scanning page', meta: { phase: 'scan' } } }, + { type: 'step', step: { message: 'Fixed alt text', meta: { phase: 'fix' } } }, + { type: 'done', result: { total: 3 } } + ]); + }); + + it('maps a phase event to a phase-typed step (message split from meta)', async () => { + fetchMock.mockResolvedValue( + mockSseResponse([ + 'event: phase\ndata: {"phase":"scan","message":"Scanning live + working (preview) baseline"}\n\n', + 'event: phase\ndata: {"phase":"read","message":"Agent: reading template.vtl"}\n\n' + ]) + ); + + const events = await firstValueFrom( + service.run<DemoResult>('/url', {}).pipe(toArray()) + ); + + expect(events).toEqual<AgentStreamEvent<DemoResult>[]>([ + { + type: 'phase', + step: { + message: 'Scanning live + working (preview) baseline', + meta: { phase: 'scan' } + } + }, + { + type: 'phase', + step: { message: 'Agent: reading template.vtl', meta: { phase: 'read' } } + } + ]); + }); + + it('maps a progress event to a typed running count', async () => { + fetchMock.mockResolvedValue( + mockSseResponse([ + 'event: progress\ndata: {"baseline":29,"current":3,"cleared":26}\n\n' + ]) + ); + + const events = await firstValueFrom( + service.run<DemoResult>('/url', {}).pipe(toArray()) + ); + + expect(events).toEqual<AgentStreamEvent<DemoResult>[]>([ + { type: 'progress', progress: { baseline: 29, current: 3, cleared: 26 } } + ]); + }); + + it('maps a workingChanged event to the typed changed-file list (dropping malformed entries)', async () => { + fetchMock.mockResolvedValue( + mockSseResponse([ + 'event: workingChanged\ndata: {"changedFiles":[' + + '{"path":"//site/a.css","identifier":"id-a"},' + + '{"path":"//site/b.vtl"},' + // no identifier → dropped + '{"path":"//site/c.vtl","identifier":"id-c"}]}\n\n' + ]) + ); + + const events = await firstValueFrom( + service.run<DemoResult>('/url', {}).pipe(toArray()) + ); + + expect(events).toEqual<AgentStreamEvent<DemoResult>[]>([ + { + type: 'workingChanged', + changedFiles: [ + { path: '//site/a.css', identifier: 'id-a' }, + { path: '//site/c.vtl', identifier: 'id-c' } + ] + } + ]); + }); + + it('maps a heartbeat event to the typed keep-alive timings', async () => { + fetchMock.mockResolvedValue( + mockSseResponse([ + 'event: heartbeat\ndata: {"elapsedMs":45000,"sinceLastEventMs":8000}\n\n' + ]) + ); + + const events = await firstValueFrom( + service.run<DemoResult>('/url', {}).pipe(toArray()) + ); + + expect(events).toEqual<AgentStreamEvent<DemoResult>[]>([ + { type: 'heartbeat', heartbeat: { elapsedMs: 45000, sinceLastEventMs: 8000 } } + ]); + }); + + it('emits a run event for the first run-id frame (no event name, no message)', async () => { + fetchMock.mockResolvedValue( + mockSseResponse([ + 'data: {"runId":"r_abc123"}\n\n', + 'event: step\ndata: {"message":"working"}\n\n', + 'event: done\ndata: {"total":1}\n\n' + ]) + ); + + const events = await firstValueFrom( + service.run<DemoResult>('/url', {}).pipe(toArray()) + ); + + expect(events).toEqual<AgentStreamEvent<DemoResult>[]>([ + { type: 'run', runId: 'r_abc123' }, + { type: 'step', step: { message: 'working' } }, + { type: 'done', result: { total: 1 } } + ]); + }); + + it('keeps a step a step even if its payload carries a runId', async () => { + fetchMock.mockResolvedValue( + mockSseResponse(['event: step\ndata: {"runId":"r_x","message":"working"}\n\n']) + ); + + const events = await firstValueFrom( + service.run<DemoResult>('/url', {}).pipe(toArray()) + ); + + // runId rides along as meta; the frame stays a step, not a run event. + expect(events).toEqual([ + { type: 'step', step: { message: 'working', meta: { runId: 'r_x' } } } + ]); + }); + + it('handles frames that straddle chunk boundaries', async () => { + fetchMock.mockResolvedValue( + mockSseResponse([ + 'event: step\ndata: {"mess', + 'age":"partial"}\n\nevent: done\ndata: {"total":0}\n\n' + ]) + ); + + const events = await firstValueFrom( + service.run<DemoResult>('/url', {}).pipe(toArray()) + ); + + expect(events).toEqual([ + { type: 'step', step: { message: 'partial' } }, + { type: 'done', result: { total: 0 } } + ]); + }); + + it('emits a step with no meta when only a message is present', async () => { + fetchMock.mockResolvedValue( + mockSseResponse(['event: step\ndata: {"message":"just text"}\n\n']) + ); + + const events = await firstValueFrom( + service.run<DemoResult>('/url', {}).pipe(toArray()) + ); + + expect(events).toEqual([{ type: 'step', step: { message: 'just text' } }]); + }); + + it('maps aborted to a terminal event carrying the partial result', async () => { + fetchMock.mockResolvedValue(mockSseResponse(['event: aborted\ndata: {"total":1}\n\n'])); + + const events = await firstValueFrom( + service.run<DemoResult>('/url', {}).pipe(toArray()) + ); + + expect(events).toEqual([{ type: 'aborted', result: { total: 1 } }]); + }); + + it('maps an error event to a typed error with a fallback message', async () => { + fetchMock.mockResolvedValue(mockSseResponse(['event: error\ndata: {}\n\n'])); + + const events = await firstValueFrom( + service.run<DemoResult>('/url', {}).pipe(toArray()) + ); + + expect(events).toEqual([{ type: 'error', message: 'Agent run failed.' }]); + }); + + it('emits a trailing frame that never got its blank-line terminator', async () => { + // A server that ends the body right after writing the terminal event leaves the + // frame in the buffer with no `\n\n`. Dropping it turned a finished run into + // one that looked hung — the consumer waited forever for a `done` already sent. + fetchMock.mockResolvedValue( + mockSseResponse([ + 'event: step\ndata: {"message":"Fixing"}\n\n', + 'event: done\ndata: {"total":3}\n' + ]) + ); + + const events = await firstValueFrom( + service.run<DemoResult>('/url', {}).pipe(toArray()) + ); + + expect(events[events.length - 1]).toEqual({ type: 'done', result: { total: 3 } }); + }); + + it('does not invent an event from trailing whitespace', async () => { + fetchMock.mockResolvedValue( + mockSseResponse(['event: done\ndata: {"total":1}\n\n', '\n\n \n']) + ); + + const events = await firstValueFrom( + service.run<DemoResult>('/url', {}).pipe(toArray()) + ); + + expect(events).toEqual([{ type: 'done', result: { total: 1 } }]); + }); + + it('drops an unparseable frame but logs it, and keeps the stream alive', async () => { + const warn = jest.spyOn(console, 'warn').mockImplementation(() => undefined); + fetchMock.mockResolvedValue( + mockSseResponse([ + 'event: step\ndata: {not json}\n\n', + 'event: done\ndata: {"total":7}\n\n' + ]) + ); + + const events = await firstValueFrom( + service.run<DemoResult>('/url', {}).pipe(toArray()) + ); + + // One bad frame from a flaky backend must not take the whole stream down... + expect(events).toEqual([{ type: 'done', result: { total: 7 } }]); + // ...but it must not vanish silently either. + expect(warn).toHaveBeenCalledWith( + expect.stringContaining('dropped an unparseable "step" frame'), + expect.stringContaining('{not json}') + ); + warn.mockRestore(); + }); + + it('errors the observable when the response is not ok', async () => { + fetchMock.mockResolvedValue(mockSseResponse([], { ok: false, status: 500 })); + + await expect( + firstValueFrom(service.run<DemoResult>('/url', {}).pipe(toArray())) + ).rejects.toThrow(/Agent request failed \(500/); + }); + + it('aborts the in-flight request when the caller unsubscribes', async () => { + // The teardown `() => controller.abort()` is the ONLY thing stopping a running + // agent stream when the user navigates away or restarts a run. Nothing else in + // this suite touches it, so a refactor that dropped `signal` from the fetch call + // — or moved the abort — would leave every other test green while leaking a + // request per abandoned run. + fetchMock.mockResolvedValue(neverEndingSseResponse()); + + const subscription = service.run<DemoResult>('/url', {}).subscribe(); + // Let the async fetch start so the signal is actually attached. + await Promise.resolve(); + + const signal = (fetchMock.mock.calls[0][1] as RequestInit).signal as AbortSignal; + expect(signal.aborted).toBe(false); + + subscription.unsubscribe(); + + expect(signal.aborted).toBe(true); + }); + + it('errors the observable when fetch itself rejects', async () => { + // A network drop before any response — the connection never opened. + fetchMock.mockRejectedValue(new Error('network down')); + + await expect( + firstValueFrom(service.run<DemoResult>('/url', {}).pipe(toArray())) + ).rejects.toThrow(/network down/); + }); + + it('errors the observable when the read fails mid-stream', async () => { + // The dangerous one: events were already delivered, so a swallowed failure here + // reads as a run that simply stopped talking rather than one that broke. + const { response } = controllableSseResponse([ + 'event: step\ndata: {"message":"working"}\n\n', + new Error('connection reset') + ]); + fetchMock.mockResolvedValue(response); + + const events: AgentStreamEvent<DemoResult>[] = []; + await expect( + new Promise((resolve, reject) => { + service.run<DemoResult>('/url', {}).subscribe({ + next: (event) => events.push(event), + error: reject, + complete: resolve + }); + }) + ).rejects.toThrow(/connection reset/); + + // The frames that DID arrive before the failure are still delivered. + expect(events).toEqual([{ type: 'step', step: { message: 'working' } }]); + }); + + it('does not error the observable when the read fails because we aborted', async () => { + // Unsubscribing rejects the pending read. That is our own doing, so it must not + // surface as a stream error to a consumer that has already walked away. + fetchMock.mockResolvedValue(neverEndingSseResponse()); + + const onError = jest.fn(); + const subscription = service.run<DemoResult>('/url', {}).subscribe({ error: onError }); + await Promise.resolve(); + + subscription.unsubscribe(); + await Promise.resolve(); + + expect(onError).not.toHaveBeenCalled(); + }); + }); + + describe('stop()', () => { + it('POSTs to the given url', () => { + service.stop('/api/v1/agent/demo/stop').subscribe(); + const req = spectator.expectOne('/api/v1/agent/demo/stop', HttpMethod.POST); + expect(req.request.body).toEqual({}); + }); + }); +}); diff --git a/core-web/libs/data-access/src/lib/dot-agent-run/dot-agent-run.service.ts b/core-web/libs/data-access/src/lib/dot-agent-run/dot-agent-run.service.ts new file mode 100644 index 000000000000..167e1e01d84a --- /dev/null +++ b/core-web/libs/data-access/src/lib/dot-agent-run/dot-agent-run.service.ts @@ -0,0 +1,280 @@ +import { Observable } from 'rxjs'; + +import { HttpClient } from '@angular/common/http'; +import { Injectable, inject } from '@angular/core'; + +import { AgentRunStep, AgentStreamEvent } from '@dotcms/dotcms-models'; + +/** + * Generic transport for a streaming "AI agent run". + * + * dotCMS agents (Accessibility, SEO, broken-links, …) run a loop server-side and + * stream their progress over Server-Sent Events: + * event: run → { runId } (first frame) + * event: phase → { phase, message } (live, many) + * event: progress → { baseline, current, cleared } (live, many) + * event: workingChanged → { changedFiles:[{path,identifier}] } (live, many) + * event: heartbeat → { elapsedMs, sinceLastEventMs } (live keep-alive, many) + * event: done → { ...result } (terminal — the agent's result) + * event: aborted → { ...result } (terminal — partial result after stop) + * event: error → { message } (terminal) + * event: step → legacy alias of `phase` (still parsed) + * + * Angular's HttpClient can't read a streaming response incrementally, so this + * uses the fetch() ReadableStream and hand-parses SSE frames, surfacing each + * event through an Observable of the generic {@link AgentStreamEvent} union. + * + * This service is agent-agnostic: the caller supplies the endpoint URL and the + * result type parameter, and interprets the terminal `result` payload. Calls go + * same-origin to a dotCMS proxy resource that authenticates the session and + * streams the agent response back — the browser never holds a token. + * + * NOT provided at the root: add it to the `providers` of the agent route/component + * that runs a stream, so it lives and dies with that screen instead of being + * retained app-wide by every consumer of `@dotcms/data-access`. + */ +@Injectable() +export class DotAgentRunService { + readonly #http = inject(HttpClient); + + /** + * POST `body` to `url` and stop the caller's in-flight run (cooperative). The + * agent stops at the next safe checkpoint and the open stream emits a terminal + * `aborted` event with the partial result. Errors are the caller's to handle. + */ + stop(url: string, body: unknown = {}): Observable<unknown> { + return this.#http.post(url, body); + } + + /** + * Run the agent loop, streaming each event. The observable emits one + * {@link AgentStreamEvent} per SSE event and completes after + * `done`/`aborted`/`error` (or when the caller unsubscribes, which aborts the + * in-flight request). `TResult` is the shape of the terminal `done`/`aborted` + * payload — opaque to this service. + */ + run<TResult>(url: string, body: unknown): Observable<AgentStreamEvent<TResult>> { + return new Observable<AgentStreamEvent<TResult>>((subscriber) => { + const controller = new AbortController(); + + (async () => { + let response: Response; + try { + response = await fetch(url, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + Accept: 'text/event-stream' + }, + body: JSON.stringify(body), + signal: controller.signal + }); + } catch (e) { + if (!controller.signal.aborted) { + subscriber.error(e); + } + + return; + } + + if (!response.ok || !response.body) { + subscriber.error( + new Error( + `Agent request failed (${response.status} ${response.statusText})` + ) + ); + + return; + } + + const reader = response.body.getReader(); + const decoder = new TextDecoder(); + let buffer = ''; + + try { + // Read the byte stream, splitting on the SSE frame delimiter + // (a blank line). A frame may straddle chunk boundaries, so we + // accumulate in `buffer` and only consume complete frames. + let done = false; + while (!done) { + const chunk = await reader.read(); + done = chunk.done; + if (chunk.value) { + buffer += decoder.decode(chunk.value, { stream: true }); + } + + let delimiter = buffer.indexOf('\n\n'); + while (delimiter !== -1) { + const frame = buffer.slice(0, delimiter); + buffer = buffer.slice(delimiter + 2); + const parsed = this.#parseFrame<TResult>(frame); + if (parsed) { + subscriber.next(parsed); + } + delimiter = buffer.indexOf('\n\n'); + } + } + + // Flush a trailing frame that never got its blank line. A server that + // ends the body right after writing the terminal event leaves it here, + // and dropping it turns a finished run into one that looks like it + // hung — the consumer waits forever for a `done` that was sent. + const tail = buffer.trim(); + if (tail) { + const parsed = this.#parseFrame<TResult>(tail); + if (parsed) { + subscriber.next(parsed); + } + } + + subscriber.complete(); + } catch (e) { + if (!controller.signal.aborted) { + subscriber.error(e); + } + } + })(); + + return () => controller.abort(); + }); + } + + /** Parse one SSE frame (`event:` + `data:` lines) into a typed event, or null. */ + #parseFrame<TResult>(frame: string): AgentStreamEvent<TResult> | null { + let event = 'message'; + const dataLines: string[] = []; + for (const line of frame.split('\n')) { + if (line.startsWith('event:')) { + event = line.slice('event:'.length).trim(); + } else if (line.startsWith('data:')) { + dataLines.push(line.slice('data:'.length).trim()); + } + } + if (!dataLines.length) { + return null; + } + + const data = dataLines.join('\n'); + let payload: unknown; + try { + payload = JSON.parse(data); + } catch { + // Dropping the frame is right — one bad frame from a flaky backend must not + // take down the whole stream — but do it loudly. Silently discarding a frame + // that happened to be the terminal `done` leaves the run looking hung with + // nothing anywhere to explain why. + console.warn( + `[DotAgentRunService] dropped an unparseable "${event}" frame:`, + data.length > 500 ? `${data.slice(0, 500)}…` : data + ); + + return null; + } + + return this.#toEvent<TResult>(event, payload); + } + + /** + * Map a raw SSE (event, payload) to the generic {@link AgentStreamEvent}. + * `step` splits off `message` and keeps the rest as `meta` (so an agent's + * presenter can read domain fields like a phase tag). `done`/`aborted` pass + * the whole payload through as the opaque `TResult`. + */ + #toEvent<TResult>(event: string, payload: unknown): AgentStreamEvent<TResult> | null { + const data = (payload ?? {}) as Record<string, unknown>; + + // The agent's first frame announces the run id (`{ "runId": "..." }`), + // often with no `event:` name and no `message`. Surface it as a `run` + // event so the caller can target a later stop at this specific run. Checked + // ahead of the switch so it works regardless of the (possibly absent) + // event name — but only for non-terminal, non-step frames, so a step (whose + // meta may carry a runId) or a bare-report done/aborted isn't misread. + const KNOWN = + event === 'phase' || + event === 'progress' || + event === 'workingChanged' || + event === 'heartbeat' || + event === 'step' || + event === 'done' || + event === 'aborted' || + event === 'error'; + if (!KNOWN && typeof data['runId'] === 'string' && !('message' in data)) { + return { type: 'run', runId: data['runId'] as string }; + } + + switch (event) { + // `phase` (and its legacy alias `step`) → a live progress entry. Split + // off `message`; keep the rest (e.g. the `phase` tag) as `meta` so a + // presenter can read domain fields. + case 'phase': + case 'step': { + const message = typeof data['message'] === 'string' ? data['message'] : ''; + const meta: Record<string, unknown> = {}; + for (const key of Object.keys(data)) { + if (key !== 'message') { + meta[key] = data[key]; + } + } + const step: AgentRunStep = { message }; + if (Object.keys(meta).length) { + step.meta = meta; + } + + // Emit under the frame's own name so callers can distinguish the + // modern `phase` stream from the legacy `step` stream if needed. + return event === 'phase' ? { type: 'phase', step } : { type: 'step', step }; + } + case 'progress': { + const num = (key: string): number => + typeof data[key] === 'number' ? (data[key] as number) : 0; + + return { + type: 'progress', + progress: { + baseline: num('baseline'), + current: num('current'), + cleared: num('cleared') + } + }; + } + case 'workingChanged': { + const raw = Array.isArray(data['changedFiles']) ? data['changedFiles'] : []; + const changedFiles = raw + .map((f) => f as Record<string, unknown>) + .filter( + (f) => typeof f['path'] === 'string' && typeof f['identifier'] === 'string' + ) + .map((f) => ({ + path: f['path'] as string, + identifier: f['identifier'] as string + })); + + return { type: 'workingChanged', changedFiles }; + } + case 'heartbeat': { + const num = (key: string): number => + typeof data[key] === 'number' ? (data[key] as number) : 0; + + return { + type: 'heartbeat', + heartbeat: { + elapsedMs: num('elapsedMs'), + sinceLastEventMs: num('sinceLastEventMs') + } + }; + } + case 'done': + return { type: 'done', result: payload as TResult }; + case 'aborted': + return { type: 'aborted', result: payload as TResult }; + case 'error': { + const message = + typeof data['message'] === 'string' ? data['message'] : 'Agent run failed.'; + + return { type: 'error', message }; + } + default: + return null; + } + } +} diff --git a/core-web/libs/dotcms-models/src/index.ts b/core-web/libs/dotcms-models/src/index.ts index 04990e76be8d..8e8abd4079d0 100644 --- a/core-web/libs/dotcms-models/src/index.ts +++ b/core-web/libs/dotcms-models/src/index.ts @@ -2,6 +2,7 @@ export * from './lib/content-type-view.model'; export * from './lib/dot-action-bulk-request-options.model'; export * from './lib/dot-action-bulk-result.model'; export * from './lib/dot-action-menu-item.model'; +export * from './lib/dot-agent-run.model'; export * from './lib/dot-ai.model'; export * from './lib/dot-ajax-action-response'; export * from './lib/dot-alert-confirm.model'; diff --git a/core-web/libs/dotcms-models/src/lib/dot-agent-run.model.ts b/core-web/libs/dotcms-models/src/lib/dot-agent-run.model.ts new file mode 100644 index 000000000000..4782cce80c23 --- /dev/null +++ b/core-web/libs/dotcms-models/src/lib/dot-agent-run.model.ts @@ -0,0 +1,99 @@ +/** + * Framework-agnostic contract for a streaming "AI agent run". + * + * A dotCMS agent studio (Accessibility, SEO, broken-links, …) drives an agent + * that streams its progress over Server-Sent Events and finishes with a + * domain-specific result. These types describe the *generic wire envelope* every + * agent shares — the live steps, the terminal result, the run status. Each agent + * parameterizes {@link AgentStreamEvent} over its own result payload. + * + * The *render* view-model (how a message looks in the UI) is a separate concern + * and lives in `@dotcms/ai-ui` (`AgentMessage`), not here. + */ + +/** + * One live progress entry streamed by an agent (an SSE `phase` event, or the + * legacy `step`). `message` is the human-readable line; `meta` carries any + * agent-specific fields (e.g. a `phase` tag) that a presenter reads to pick an + * icon/tone. + */ +export interface AgentRunStep { + message: string; + meta?: Record<string, unknown>; +} + +/** + * A running violation/issue count streamed while an agent works (SSE `progress`). + * `baseline` is the count at the start, `current` the live count, `cleared` how + * many have been resolved so far — the authoritative source for a live score + * (an agent no longer has to be inferred from step text). + */ +export interface AgentProgress { + baseline: number; + current: number; + cleared: number; +} + +/** + * A file the agent has changed in the working version but not yet published + * (SSE `workingChanged` and the terminal report's `changedFiles`). + */ +export interface AgentChangedFile { + /** Host-qualified asset path, e.g. `//site/application/themes/x/style.css`. */ + path: string; + /** dotCMS content identifier of the changed asset. */ + identifier: string; +} + +/** + * A keep-alive tick streamed while the agent is thinking between actions (SSE + * `heartbeat`). Some steps (a model call, a long read) take many seconds with no + * `phase` change; the heartbeat lets the UI show elapsed time and reassure the + * user the run hasn't hung. + * + * `elapsedMs` is the total run time so far; `sinceLastEventMs` is how long since + * the last non-heartbeat event (i.e. how long the current action has been running). + */ +export interface AgentHeartbeat { + elapsedMs: number; + sinceLastEventMs: number; +} + +/** + * The parsed stream of events an agent emits, generic over the terminal + * result payload `TResult`. Discriminated by `type`: + * - `run` — the run's id, emitted on the first frame; needed to + * target a subsequent stop request at this specific run + * - `phase` — a live progress entry (many, non-terminal); the primary + * activity signal. Carries the phase tag in `step.meta.phase`. + * - `progress` — a live violation/issue count (many, non-terminal) + * - `workingChanged` — the set of files changed so far (many, non-terminal) + * - `heartbeat` — a keep-alive tick while the agent is thinking (many, + * non-terminal); carries elapsed timings, no new activity + * - `step` — legacy alias of `phase`, kept for back-compat + * - `done` — the run completed; carries the full result + * - `aborted` — the user stopped the run early; carries the PARTIAL result + * - `error` — the run failed; carries a message + */ +export type AgentStreamEvent<TResult> = + | { type: 'run'; runId: string } + | { type: 'phase'; step: AgentRunStep } + | { type: 'progress'; progress: AgentProgress } + | { type: 'workingChanged'; changedFiles: AgentChangedFile[] } + | { type: 'heartbeat'; heartbeat: AgentHeartbeat } + | { type: 'step'; step: AgentRunStep } + | { type: 'done'; result: TResult } + | { type: 'aborted'; result: TResult } + | { type: 'error'; message: string }; + +/** + * Coarse lifecycle of an agent run, independent of any agent's own workflow + * (which each agent models separately). + */ +export const AGENT_RUN_STATUS = { + RUNNING: 'running', + DONE: 'done', + ERROR: 'error' +} as const; + +export type AgentRunStatus = (typeof AGENT_RUN_STATUS)[keyof typeof AGENT_RUN_STATUS]; diff --git a/core-web/libs/portlets/dot-agents/.eslintrc.json b/core-web/libs/portlets/dot-agents/.eslintrc.json new file mode 100644 index 000000000000..ef536cdfaf37 --- /dev/null +++ b/core-web/libs/portlets/dot-agents/.eslintrc.json @@ -0,0 +1,18 @@ +{ + "extends": ["../../../.eslintrc.base.json"], + "ignorePatterns": ["!**/*"], + "overrides": [ + { + "files": ["*.ts", "*.tsx", "*.js", "*.jsx"], + "rules": {} + }, + { + "files": ["*.ts", "*.tsx"], + "rules": {} + }, + { + "files": ["*.js", "*.jsx"], + "rules": {} + } + ] +} diff --git a/core-web/libs/portlets/dot-agents/jest.config.ts b/core-web/libs/portlets/dot-agents/jest.config.ts new file mode 100644 index 000000000000..1fdba74e3941 --- /dev/null +++ b/core-web/libs/portlets/dot-agents/jest.config.ts @@ -0,0 +1,26 @@ +export default { + displayName: 'portlets-dot-agents-portlet', + preset: '../../../jest.preset.js', + setupFilesAfterEnv: ['<rootDir>/src/test-setup.ts'], + coverageDirectory: '../../../coverage/libs/portlets/dot-agents', + transform: { + '^.+\\.(ts|mjs|js|html)$': [ + 'jest-preset-angular', + { + tsconfig: '<rootDir>/tsconfig.spec.json', + stringifyContentPathRegex: '\\.(html|svg)$' + } + ] + }, + // The studio imports the page scanner from @dotcms/portlets/dot-ema/ui, whose + // barrel transitively pulls ESM-only deps (lib0/y-protocols/@tiptap/etc.) that + // must be transformed. Mirrors dot-content-drive/portlet's jest config. + transformIgnorePatterns: [ + 'node_modules/(?!.*\\.mjs$|.*(y-protocols|lib0|y-prosemirror|@tiptap|marked|lowlight|devlop))' + ], + snapshotSerializers: [ + 'jest-preset-angular/build/serializers/no-ng-attributes', + 'jest-preset-angular/build/serializers/ng-snapshot', + 'jest-preset-angular/build/serializers/html-comment' + ] +}; diff --git a/core-web/libs/portlets/dot-agents/project.json b/core-web/libs/portlets/dot-agents/project.json new file mode 100644 index 000000000000..f913c0474f77 --- /dev/null +++ b/core-web/libs/portlets/dot-agents/project.json @@ -0,0 +1,21 @@ +{ + "name": "portlets-dot-agents-portlet", + "$schema": "../../../node_modules/nx/schemas/project-schema.json", + "sourceRoot": "libs/portlets/dot-agents/src", + "prefix": "dot", + "projectType": "library", + "tags": ["type:feature", "scope:dotcms-ui", "portlet:agents"], + "targets": { + "test": { + "executor": "@nx/jest:jest", + "outputs": ["{workspaceRoot}/coverage/{projectRoot}"], + "options": { + "jestConfig": "libs/portlets/dot-agents/jest.config.ts", + "tsConfig": "libs/portlets/dot-agents/tsconfig.spec.json" + } + }, + "lint": { + "executor": "@nx/eslint:lint" + } + } +} diff --git a/core-web/libs/portlets/dot-agents/src/index.ts b/core-web/libs/portlets/dot-agents/src/index.ts new file mode 100644 index 000000000000..3b935479ba1d --- /dev/null +++ b/core-web/libs/portlets/dot-agents/src/index.ts @@ -0,0 +1,2 @@ +export * from './lib/lib.routes'; +export * from './lib/agent-registry'; diff --git a/core-web/libs/portlets/dot-agents/src/lib/agent-registry.ts b/core-web/libs/portlets/dot-agents/src/lib/agent-registry.ts new file mode 100644 index 000000000000..1ec88f39f771 --- /dev/null +++ b/core-web/libs/portlets/dot-agents/src/lib/agent-registry.ts @@ -0,0 +1,78 @@ +import { Route } from '@angular/router'; + +/** + * Whether an agent is ready to use or still on the roadmap. `coming-soon` + * agents render a disabled card in the gallery and register no route. + */ +export type AgentStatus = 'available' | 'coming-soon'; + +/** + * One AI agent that plugs into the agents shell. Adding an agent = one entry + * in {@link DOT_AGENTS}: give it an id/label/icon, point `loadChildren` at the + * agent's own routes, and it appears in the gallery and gets a child route + * under `agents/{id}` automatically. No edits to the shell, landing, or the + * shared streaming kernel (`@dotcms/ai-ui`, `DotAgentRunService`). + */ +export interface AgentDefinition { + /** URL segment + i18n key stem, e.g. `a11y` → route `agents/a11y`. */ + readonly id: string; + /** i18n key for the gallery card title. */ + readonly labelKey: string; + /** i18n key for the gallery card description. */ + readonly descriptionKey: string; + /** + * Material Symbols ligature name for the card icon, e.g. `accessibility_new`. + * Rendered inside a `dot-color-icon` — see the gallery template. + */ + readonly icon: string; + /** + * Icon accent, passed straight to `dot-color-icon`'s `color`. A PrimeNG palette + * token (e.g. `blue`) or a hex value. + */ + readonly iconColor: string; + /** Availability. `coming-soon` disables the card and skips routing. */ + readonly status: AgentStatus; + /** + * Lazy loader for the agent's own routes. Required for `available` agents, + * omitted for `coming-soon`. The returned routes render full-screen inside + * the shell's router outlet. + */ + readonly loadChildren?: () => Promise<Route[]>; +} + +/** + * The catalog of agents. This is the single extension point of the agents + * shell — everything else (gallery cards, child routes) is derived from it. + * + * To add an agent: + * 1. Build its UI under `src/lib/agents/{id}/` with its own `{id}.routes.ts`. + * 2. Add an entry here with `status: 'available'` and a `loadChildren`. + */ +export const DOT_AGENTS: readonly AgentDefinition[] = [ + { + id: 'a11y', + labelKey: 'agents.a11y.label', + descriptionKey: 'agents.a11y.description', + icon: 'accessibility_new', + iconColor: 'blue', + status: 'available', + loadChildren: () => + import('./agents/a11y/a11y.routes').then((m) => m.dotAccessibilityStudioRoutes) + }, + { + id: 'geo-fixer', + labelKey: 'agents.geo-fixer.label', + descriptionKey: 'agents.geo-fixer.description', + icon: 'location_on', + iconColor: 'green', + status: 'coming-soon' + }, + { + id: 'page-builder', + labelKey: 'agents.page-builder.label', + descriptionKey: 'agents.page-builder.description', + icon: 'dashboard_customize', + iconColor: 'purple', + status: 'coming-soon' + } +]; diff --git a/core-web/libs/portlets/dot-agents/src/lib/agents-landing/dot-agents-landing.component.html b/core-web/libs/portlets/dot-agents/src/lib/agents-landing/dot-agents-landing.component.html new file mode 100644 index 000000000000..2dedf5c6d587 --- /dev/null +++ b/core-web/libs/portlets/dot-agents/src/lib/agents-landing/dot-agents-landing.component.html @@ -0,0 +1,65 @@ +<div class="mx-auto w-full max-w-5xl px-6 py-10"> + <header class="mb-8"> + <h1 class="m-0 text-2xl font-bold text-color">{{ 'agents.landing.title' | dm }}</h1> + <p class="m-0 mt-2 text-muted-color">{{ 'agents.landing.subtitle' | dm }}</p> + </header> + + <ul + class="m-0 grid list-none grid-cols-1 gap-4 p-0 sm:grid-cols-2 lg:grid-cols-3" + data-testid="agents-grid"> + @for (agent of agents; track agent.id) { + @let available = agent.status === 'available'; + + <li class="contents"> + @if (available) { + <!-- `no-underline` on the anchor alone isn't enough: the global + `a` rule sets `underline` + `text-primary-500`, and both are + inherited by the card's heading/description. Reset them for + the whole subtree so only the card's own colors apply. --> + <a + class="text-color! no-underline **:no-underline" + [routerLink]="[agent.id]" + [attr.data-testid]="'agent-card-' + agent.id" + [attr.aria-label]="agent.labelKey | dm"> + <ng-container + [ngTemplateOutlet]="card" + [ngTemplateOutletContext]="{ $implicit: agent, available: true }" /> + </a> + } @else { + <div [attr.data-testid]="'agent-card-' + agent.id" [attr.aria-disabled]="true"> + <ng-container + [ngTemplateOutlet]="card" + [ngTemplateOutletContext]="{ $implicit: agent, available: false }" /> + </div> + } + </li> + } + </ul> +</div> + +<ng-template #card let-agent let-available="available"> + <p-card + styleClass="h-full transition-all duration-150" + [class.cursor-pointer]="available" + [class.hover:shadow-lg]="available" + [class.opacity-60]="!available"> + <div class="flex items-start gap-4"> + <dot-color-icon size="sm" [color]="agent.iconColor"> + <i class="material-symbols-outlined" aria-hidden="true">{{ agent.icon }}</i> + </dot-color-icon> + <div class="min-w-0"> + <div class="flex flex-wrap items-center gap-2"> + <h2 class="m-0 text-base font-semibold text-color"> + {{ agent.labelKey | dm }} + </h2> + @if (!available) { + <p-tag severity="secondary" [value]="'agents.status.coming-soon' | dm" /> + } + </div> + <p class="m-0 mt-1 text-sm text-muted-color"> + {{ agent.descriptionKey | dm }} + </p> + </div> + </div> + </p-card> +</ng-template> diff --git a/core-web/libs/portlets/dot-agents/src/lib/agents-landing/dot-agents-landing.component.spec.ts b/core-web/libs/portlets/dot-agents/src/lib/agents-landing/dot-agents-landing.component.spec.ts new file mode 100644 index 000000000000..b202ab6def82 --- /dev/null +++ b/core-web/libs/portlets/dot-agents/src/lib/agents-landing/dot-agents-landing.component.spec.ts @@ -0,0 +1,61 @@ +import { byTestId, createComponentFactory, Spectator } from '@openng/spectator/jest'; + +import { provideRouter } from '@angular/router'; + +import { DotMessageService } from '@dotcms/data-access'; +import { MockDotMessageService } from '@dotcms/utils-testing'; + +import { DotAgentsLandingComponent } from './dot-agents-landing.component'; + +import { DOT_AGENTS } from '../agent-registry'; + +describe('DotAgentsLandingComponent', () => { + let spectator: Spectator<DotAgentsLandingComponent>; + + const createComponent = createComponentFactory({ + component: DotAgentsLandingComponent, + providers: [ + provideRouter([]), + { + provide: DotMessageService, + useValue: new MockDotMessageService({ + 'agents.landing.title': 'AI Agents', + 'agents.status.coming-soon': 'Coming soon', + 'agents.a11y.label': 'Accessibility Studio' + }) + } + ] + }); + + beforeEach(() => { + spectator = createComponent(); + spectator.detectChanges(); + }); + + it('renders a card per registered agent', () => { + const cards = DOT_AGENTS.map((agent) => + spectator.query(byTestId(`agent-card-${agent.id}`)) + ); + expect(cards.every(Boolean)).toBe(true); + expect(cards.length).toBe(DOT_AGENTS.length); + }); + + it('links available agents to their id and skips the link on coming-soon', () => { + for (const agent of DOT_AGENTS) { + const card = spectator.query(byTestId(`agent-card-${agent.id}`)); + + if (agent.status === 'available') { + expect(card?.tagName.toLowerCase()).toBe('a'); + expect(card?.getAttribute('href')).toContain(agent.id); + } else { + expect(card?.getAttribute('aria-disabled')).toBe('true'); + } + } + }); + + it('shows a coming-soon tag only on unavailable agents', () => { + const comingSoon = DOT_AGENTS.filter((a) => a.status === 'coming-soon'); + const tags = spectator.queryAll('p-tag'); + expect(tags.length).toBe(comingSoon.length); + }); +}); diff --git a/core-web/libs/portlets/dot-agents/src/lib/agents-landing/dot-agents-landing.component.ts b/core-web/libs/portlets/dot-agents/src/lib/agents-landing/dot-agents-landing.component.ts new file mode 100644 index 000000000000..10974a063a95 --- /dev/null +++ b/core-web/libs/portlets/dot-agents/src/lib/agents-landing/dot-agents-landing.component.ts @@ -0,0 +1,34 @@ +import { NgTemplateOutlet } from '@angular/common'; +import { ChangeDetectionStrategy, Component } from '@angular/core'; +import { RouterLink } from '@angular/router'; + +import { CardModule } from 'primeng/card'; +import { TagModule } from 'primeng/tag'; + +import { DotColorIconComponent, DotMessagePipe } from '@dotcms/ui'; + +import { DOT_AGENTS } from '../agent-registry'; + +/** + * The agents gallery: a card grid of every registered agent. Available agents + * link to `agents/{id}`; `coming-soon` agents render disabled with a tag. The + * grid is derived entirely from {@link DOT_AGENTS}, so adding an agent needs no + * change here. + */ +@Component({ + selector: 'dot-agents-landing', + imports: [ + NgTemplateOutlet, + RouterLink, + CardModule, + TagModule, + DotMessagePipe, + DotColorIconComponent + ], + templateUrl: './dot-agents-landing.component.html', + changeDetection: ChangeDetectionStrategy.OnPush, + host: { class: 'block h-full min-h-0 overflow-y-auto' } +}) +export class DotAgentsLandingComponent { + protected readonly agents = DOT_AGENTS; +} diff --git a/core-web/libs/portlets/dot-agents/src/lib/agents-shell/dot-agents-shell.component.ts b/core-web/libs/portlets/dot-agents/src/lib/agents-shell/dot-agents-shell.component.ts new file mode 100644 index 000000000000..1a38027d72ac --- /dev/null +++ b/core-web/libs/portlets/dot-agents/src/lib/agents-shell/dot-agents-shell.component.ts @@ -0,0 +1,18 @@ +import { ChangeDetectionStrategy, Component } from '@angular/core'; +import { RouterOutlet } from '@angular/router'; + +/** + * Host for the AI agents area. Thin router-outlet wrapper: the gallery landing + * renders at the base path and each agent renders full-screen at `agents/{id}`. + * Owns only the full-height layout box so agents (and the landing) can fill it. + */ +@Component({ + selector: 'dot-agents-shell', + imports: [RouterOutlet], + template: ` + <router-outlet /> + `, + changeDetection: ChangeDetectionStrategy.OnPush, + host: { class: 'flex flex-col h-full min-h-0 block bg-surface-100' } +}) +export class DotAgentsShellComponent {} diff --git a/core-web/libs/portlets/dot-agents/src/lib/agents/a11y/a11y-diff/a11y-diff-viewer.component.html b/core-web/libs/portlets/dot-agents/src/lib/agents/a11y/a11y-diff/a11y-diff-viewer.component.html new file mode 100644 index 000000000000..4861aa9e9e58 --- /dev/null +++ b/core-web/libs/portlets/dot-agents/src/lib/agents/a11y/a11y-diff/a11y-diff-viewer.component.html @@ -0,0 +1,37 @@ +<!-- Diff header: file name + path, live ⇄ working labels, close. --> +<div + class="flex h-12 flex-none items-center justify-between gap-3 border-b border-surface-200 bg-white px-4"> + <div class="flex min-w-0 items-center gap-2.5"> + <p-button + icon="pi pi-arrow-left" + [text]="true" + severity="secondary" + [rounded]="true" + (onClick)="closed.emit()" + [attr.aria-label]="'accessibility.studio.diff.backtopreview' | dm" + data-testid="diff-viewer-close-btn" /> + <div class="min-w-0"> + <div class="truncate text-sm font-bold text-color"> + {{ file()?.name }} + </div> + <div class="truncate text-xs text-muted-color">{{ file()?.path }}</div> + </div> + </div> + <div class="flex flex-none items-center gap-2 text-[11px] font-semibold"> + <span + class="inline-flex items-center gap-1 rounded-full bg-surface-100 px-2.5 py-1 text-muted-color"> + <i class="pi pi-globe text-[11px]!" aria-hidden="true"></i> + {{ 'accessibility.studio.diff.live' | dm }} + </span> + <i class="pi pi-arrow-right text-surface-300" aria-hidden="true"></i> + <span + class="inline-flex items-center gap-1 rounded-full bg-primary-50 px-2.5 py-1 text-primary"> + <i class="pi pi-sparkles text-[11px]!" aria-hidden="true"></i> + {{ 'accessibility.studio.diff.working' | dm }} + </span> + </div> +</div> + +<!-- Monaco host. Always mounted so the editor keeps its instance while the + user moves between files; only the models are swapped. --> +<div #diffHost class="min-h-0 flex-1" data-testid="diff-editor-host"></div> diff --git a/core-web/libs/portlets/dot-agents/src/lib/agents/a11y/a11y-diff/a11y-diff-viewer.component.spec.ts b/core-web/libs/portlets/dot-agents/src/lib/agents/a11y/a11y-diff/a11y-diff-viewer.component.spec.ts new file mode 100644 index 000000000000..b5ffb0e3ea69 --- /dev/null +++ b/core-web/libs/portlets/dot-agents/src/lib/agents/a11y/a11y-diff/a11y-diff-viewer.component.spec.ts @@ -0,0 +1,136 @@ +import { MonacoEditorLoaderService } from '@materia-ui/ngx-monaco-editor'; +import { byTestId, createComponentFactory, mockProvider, Spectator } from '@openng/spectator/jest'; +import { of } from 'rxjs'; + +import { DotMessageService } from '@dotcms/data-access'; +import { MockDotMessageService } from '@dotcms/utils-testing'; + +import { DotA11yDiffViewerComponent } from './a11y-diff-viewer.component'; + +import { PageDiffFile } from '../models/page-render-sources.models'; + +const VTL_FILE: PageDiffFile = { + identifier: 'vtl-1', + path: '//demo/application/containers/awazon/a.vtl', + name: 'a.vtl', + extension: 'vtl', + origin: 'container', + working: 'new\ncode', + live: 'old\ncode', + added: 1, + removed: 1 +}; + +const CSS_FILE: PageDiffFile = { + identifier: 'css-1', + path: '//demo/application/themes/x/style.css', + name: 'style.css', + extension: 'css', + origin: 'theme', + working: '.a{color:red}', + live: '', + added: 1, + removed: 0 +}; + +/** A minimal monaco diff-editor mock installed on the window global. */ +function installMonacoMock() { + const setModel = jest.fn(); + const dispose = jest.fn(); + const editor = { + getModel: jest.fn().mockReturnValue(null), + setModel, + dispose + }; + const createDiffEditor = jest.fn().mockReturnValue(editor); + const createModel = jest.fn((value: string) => ({ value, dispose: jest.fn() })); + (window as unknown as { monaco: unknown }).monaco = { + editor: { createDiffEditor, createModel } + }; + + return { createDiffEditor, createModel, setModel }; +} + +describe('DotA11yDiffViewerComponent', () => { + let spectator: Spectator<DotA11yDiffViewerComponent>; + let monacoMock: ReturnType<typeof installMonacoMock>; + + const createComponent = createComponentFactory({ + component: DotA11yDiffViewerComponent, + providers: [ + { + provide: DotMessageService, + useValue: new MockDotMessageService({ + 'accessibility.studio.diff.live': 'Live', + 'accessibility.studio.diff.working': 'Working', + 'accessibility.studio.diff.backtopreview': 'Back to preview' + }) + }, + mockProvider(MonacoEditorLoaderService, { isMonacoLoaded$: of(true) }) + ] + }); + + beforeEach(() => { + jest.clearAllMocks(); + monacoMock = installMonacoMock(); + }); + + it('builds the diff editor for the file it is given', () => { + // Regression: the viewer mounts inside an @if in the run screen, so the + // render effect first runs before the host element exists. It must re-run + // once the host appears rather than silently bailing out — otherwise the + // pane renders empty and the preview appears to stay up. + spectator = createComponent({ props: { file: VTL_FILE } }); + spectator.detectChanges(); + + expect(monacoMock.createDiffEditor).toHaveBeenCalledTimes(1); + // live is the original (left), working the modified (right). + expect(monacoMock.createModel).toHaveBeenCalledWith('old\ncode', 'html'); + expect(monacoMock.createModel).toHaveBeenCalledWith('new\ncode', 'html'); + expect(monacoMock.setModel).toHaveBeenCalled(); + }); + + it('shows the file name and path in the header', () => { + spectator = createComponent({ props: { file: VTL_FILE } }); + spectator.detectChanges(); + + const header = spectator.query(byTestId('diff-viewer-close-btn'))?.parentElement; + expect(header?.textContent).toContain('a.vtl'); + expect(header?.textContent).toContain('//demo/application/containers/awazon/a.vtl'); + }); + + it('swaps the models when a different file comes in, reusing the editor', () => { + spectator = createComponent({ props: { file: VTL_FILE } }); + spectator.detectChanges(); + monacoMock.createModel.mockClear(); + + spectator.setInput('file', CSS_FILE); + spectator.detectChanges(); + + // Same editor instance, new models — css language, empty live side. + expect(monacoMock.createDiffEditor).toHaveBeenCalledTimes(1); + expect(monacoMock.createModel).toHaveBeenCalledWith('', 'css'); + expect(monacoMock.createModel).toHaveBeenCalledWith('.a{color:red}', 'css'); + }); + + it('emits closed when the back control is used', () => { + spectator = createComponent({ props: { file: VTL_FILE } }); + spectator.detectChanges(); + + let closed = false; + spectator.component.closed.subscribe(() => (closed = true)); + + spectator.click( + spectator + .query(byTestId('diff-viewer-close-btn')) + ?.querySelector('button') as HTMLElement + ); + expect(closed).toBe(true); + }); + + it('builds nothing until a file is set', () => { + spectator = createComponent({ props: { file: null } }); + spectator.detectChanges(); + expect(monacoMock.createDiffEditor).not.toHaveBeenCalled(); + }); +}); diff --git a/core-web/libs/portlets/dot-agents/src/lib/agents/a11y/a11y-diff/a11y-diff-viewer.component.ts b/core-web/libs/portlets/dot-agents/src/lib/agents/a11y/a11y-diff/a11y-diff-viewer.component.ts new file mode 100644 index 000000000000..b5bac5576f10 --- /dev/null +++ b/core-web/libs/portlets/dot-agents/src/lib/agents/a11y/a11y-diff/a11y-diff-viewer.component.ts @@ -0,0 +1,183 @@ +import { MonacoEditorLoaderService } from '@materia-ui/ngx-monaco-editor'; + +import { + ChangeDetectionStrategy, + Component, + DestroyRef, + effect, + ElementRef, + inject, + input, + output, + signal, + untracked, + viewChild +} from '@angular/core'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; + +import { ButtonModule } from 'primeng/button'; + +import { filter, take } from 'rxjs/operators'; + +import { DotMessagePipe } from '@dotcms/ui'; + +import { PageDiffFile } from '../models/page-render-sources.models'; + +/** Monaco language id per source extension — everything else falls back to plaintext. */ +const LANGUAGE_BY_EXTENSION: Record<string, string> = { + vtl: 'html', + html: 'html', + css: 'css', + scss: 'scss', + sass: 'scss', + js: 'javascript', + ts: 'typescript', + json: 'json' +}; + +/** + * Read-only side-by-side diff (live → working) for one source file, filling the run + * screen's right pane in place of the preview. + * + * Purely presentational: the file comes in via {@link file} and the close action goes + * back out via {@link closed} — the run screen owns which of preview/diff is showing, + * and the changed-files accordion in the left panel owns the selection. + * + * The Monaco diff editor is created imperatively against the `monaco` global (the app + * registers `MonacoEditorModule` in app.config, so the AMD loader is configured); + * `@materia-ui/ngx-monaco-editor` only exposes the plain editor, so the diff editor + * has no Angular wrapper. + */ +@Component({ + selector: 'dot-a11y-diff-viewer', + imports: [ButtonModule, DotMessagePipe], + changeDetection: ChangeDetectionStrategy.OnPush, + host: { class: 'flex min-h-0 flex-col bg-surface-100' }, + templateUrl: './a11y-diff-viewer.component.html' +}) +export class DotA11yDiffViewerComponent { + readonly #monacoLoader = inject(MonacoEditorLoaderService); + readonly #destroyRef = inject(DestroyRef); + + /** The file to diff. */ + readonly file = input<PageDiffFile | null>(null); + + /** The user asked to go back to the preview. */ + readonly closed = output<void>(); + + // NOTE: `private`, not `#`. Angular rejects an ES-private member for a signal query + // outright — "Cannot use 'viewChild' on a class member that is declared as ES private" + // — because the compiler has to write to the field from generated code. + private readonly $diffHost = viewChild<ElementRef<HTMLDivElement>>('diffHost'); + + /** True once the monaco global has loaded. */ + readonly #monacoReady = signal(false); + + #editor: MonacoDiffEditor | null = null; + + constructor() { + // Wait for the AMD-loaded monaco global before creating the editor. + this.#monacoLoader.isMonacoLoaded$ + .pipe( + filter((loaded) => loaded), + take(1), + takeUntilDestroyed() + ) + .subscribe(() => this.#monacoReady.set(true)); + + // (Re)build the models whenever monaco is ready, a file is set, and the host + // element exists. Depending on the `diffHost` viewChild signal matters: this + // component is mounted inside an @if, so on the first pass the effect runs + // before the view renders and the host is still undefined — reading it here + // makes the effect re-run once it appears, instead of silently bailing out. + effect(() => { + const ready = this.#monacoReady(); + const file = this.file(); + const host = this.$diffHost()?.nativeElement; + untracked(() => { + if (ready && file && host) { + this.#renderDiff(host, file); + } + }); + }); + + this.#destroyRef.onDestroy(() => this.#disposeEditor()); + } + + /** Monaco language id for a file, from its extension. */ + #languageFor(extension: string): string { + return LANGUAGE_BY_EXTENSION[extension.toLowerCase()] ?? 'plaintext'; + } + + /** + * Create (once) and populate the diff editor with the file's live (original) vs + * working (modified) text. Read-only side-by-side. + */ + #renderDiff(host: HTMLElement, file: PageDiffFile): void { + const monaco = getMonaco(); + if (!monaco) { + return; + } + + if (!this.#editor) { + this.#editor = monaco.editor.createDiffEditor(host, { + theme: 'vs', + readOnly: true, + originalEditable: false, + renderSideBySide: true, + automaticLayout: true, + minimap: { enabled: false }, + scrollBeyondLastLine: false, + fontSize: 13, + fontFamily: 'JetBrains Mono, Fira Code, Consolas, monospace' + }); + } + + const language = this.#languageFor(file.extension); + // Dispose the previous models before swapping so we don't leak them. + const previous = this.#editor.getModel(); + const original = monaco.editor.createModel(file.live, language); + const modified = monaco.editor.createModel(file.working, language); + this.#editor.setModel({ original, modified }); + previous?.original?.dispose(); + previous?.modified?.dispose(); + } + + #disposeEditor(): void { + const model = this.#editor?.getModel(); + model?.original?.dispose(); + model?.modified?.dispose(); + this.#editor?.dispose(); + this.#editor = null; + } +} + +// ── Minimal structural types for the imperative Monaco diff editor ────────── +// `@materia-ui/ngx-monaco-editor` exposes only the plain editor, so the diff +// editor is untyped through it. The `monaco` global carries full types via the +// ambient `monaco-editor` declarations; we narrow to just what we call to avoid a +// hard `monaco-editor` import (which would pull the full editor into this chunk). + +interface MonacoTextModel { + dispose(): void; +} +interface MonacoDiffModel { + original: MonacoTextModel; + modified: MonacoTextModel; +} +interface MonacoDiffEditor { + getModel(): MonacoDiffModel | null; + setModel(model: MonacoDiffModel): void; + dispose(): void; +} +interface MonacoGlobal { + editor: { + createDiffEditor(host: HTMLElement, options: Record<string, unknown>): MonacoDiffEditor; + createModel(value: string, language: string): MonacoTextModel; + }; +} + +/** The AMD-loaded monaco global; null before the loader finishes. */ +function getMonaco(): MonacoGlobal | null { + return (window as unknown as { monaco?: MonacoGlobal }).monaco ?? null; +} diff --git a/core-web/libs/portlets/dot-agents/src/lib/agents/a11y/a11y-diff/a11y-diff.component.html b/core-web/libs/portlets/dot-agents/src/lib/agents/a11y/a11y-diff/a11y-diff.component.html new file mode 100644 index 000000000000..950f88695ec6 --- /dev/null +++ b/core-web/libs/portlets/dot-agents/src/lib/agents/a11y/a11y-diff/a11y-diff.component.html @@ -0,0 +1,100 @@ +<!-- ============ CHANGED FILES (list) ============ --> +<!-- The body of the side panel's "Files" accordion panel. The run screen owns the + panel header, its count badge, and the Publish action; this is the list only. + Clicking a file opens its diff in the RIGHT pane (where the preview lives), so + the narrow panel stays a list and the diff gets the full width. --> + +@switch ($status()) { + @case ('loading') { + <!-- Skeleton rows, same footprint as the real list. --> + <div class="flex animate-pulse flex-col gap-1" aria-hidden="true"> + @for (i of [1, 2, 3]; track i) { + <div class="flex items-center gap-2.5 rounded-lg p-2"> + <span class="size-4 flex-none rounded-sm bg-surface-200"></span> + <span class="h-3.5 w-32 rounded-sm bg-surface-200"></span> + </div> + } + </div> + } + @case ('error') { + <div + class="flex items-start gap-2.5 rounded-xl border border-red-100 bg-red-50 p-3 text-red-700" + role="alert" + data-testid="diff-error"> + <i class="pi pi-exclamation-triangle mt-0.5" aria-hidden="true"></i> + <div class="min-w-0"> + <div class="font-semibold"> + {{ 'accessibility.studio.diff.error.title' | dm }} + </div> + <div class="mt-0.5 text-red-600"> + {{ 'accessibility.studio.diff.error.sub' | dm }} + </div> + </div> + </div> + } + @case ('loaded') { + @if ($empty()) { + <!-- No working-vs-live delta for this page. --> + <div class="flex items-center gap-2.5 p-2" data-testid="diff-empty"> + <i class="pi pi-check-circle flex-none text-green-500" aria-hidden="true"></i> + <div class="min-w-0"> + <div class="font-bold text-color"> + {{ 'accessibility.studio.diff.empty.title' | dm }} + </div> + <p class="m-0 mt-0.5 leading-snug text-muted-color"> + {{ 'accessibility.studio.diff.empty.sub' | dm }} + </p> + </div> + </div> + } @else { + <ul class="m-0 flex list-none flex-col gap-0.5 p-0" data-testid="diff-file-list"> + @for (file of $files(); track file.identifier) { + <li> + <button + type="button" + class="flex w-full items-center gap-2.5 rounded-lg p-2 text-left transition-colors" + [class.bg-primary-50]="file.identifier === $selectedId()" + [class.hover:bg-surface-50]="file.identifier !== $selectedId()" + (click)="selectFile(file.identifier)" + [attr.aria-current]="file.identifier === $selectedId()" + data-testid="diff-file-row"> + <i + class="pi pi-file flex-none" + [class.text-primary]="file.identifier === $selectedId()" + [class.text-muted-color]="file.identifier !== $selectedId()" + aria-hidden="true"></i> + <div class="min-w-0 flex-1"> + <div class="truncate font-semibold text-color"> + {{ file.name }} + </div> + </div> + <span + class="flex flex-none items-center gap-1.5 font-mono font-bold tabular-nums"> + @if (file.added > 0) { + <span class="text-green-600">+{{ file.added }}</span> + } + @if (file.removed > 0) { + <span class="text-red-600">−{{ file.removed }}</span> + } + </span> + </button> + </li> + } + </ul> + + <!-- Back to preview — the way out of the diff view from the side panel, + without hunting for a control in the right pane. Only shown while a + file's diff is open. --> + @if ($selectedId()) { + <button + type="button" + class="mt-1.5 flex w-full items-center justify-center gap-1.5 rounded-lg border border-surface-200 bg-white p-2 font-semibold text-muted-color transition-colors hover:bg-surface-50" + (click)="clearSelection()" + data-testid="diff-back-to-preview-btn"> + <i class="pi pi-arrow-left" aria-hidden="true"></i> + {{ 'accessibility.studio.diff.backtopreview' | dm }} + </button> + } + } + } +} diff --git a/core-web/libs/portlets/dot-agents/src/lib/agents/a11y/a11y-diff/a11y-diff.component.spec.ts b/core-web/libs/portlets/dot-agents/src/lib/agents/a11y/a11y-diff/a11y-diff.component.spec.ts new file mode 100644 index 000000000000..cab65325144d --- /dev/null +++ b/core-web/libs/portlets/dot-agents/src/lib/agents/a11y/a11y-diff/a11y-diff.component.spec.ts @@ -0,0 +1,246 @@ +import { byTestId, createComponentFactory, mockProvider, Spectator } from '@openng/spectator/jest'; +import { of, throwError } from 'rxjs'; + +import { signal } from '@angular/core'; + +import { DotMessageService } from '@dotcms/data-access'; +import { MockDotMessageService } from '@dotcms/utils-testing'; + +import { DIFF_RELOAD_DEBOUNCE_MS, DotA11yDiffComponent } from './a11y-diff.component'; + +import { PageDiffFile, PageSourceFile } from '../models/page-render-sources.models'; +import { DotPageSourcesService } from '../services/dot-page-sources.service'; +import { A11yRunStore } from '../store/a11y-run.store'; + +const MOCK_PAGE = { + identifier: 'id-1', + title: 'About Us', + path: '/about-us', + type: 'htmlpageasset', + languageId: 1, + hostId: 'host-1', + hostName: 'demo.dotcms.com', + modDate: '', + modUserName: '', + live: true +}; + +const DIFF_FILES: PageDiffFile[] = [ + { + identifier: 'vtl-1', + path: '//demo/application/containers/awazon/a.vtl', + name: 'a.vtl', + extension: 'vtl', + origin: 'container', + working: 'new\ncode', + live: 'old\ncode', + added: 1, + removed: 1 + }, + { + identifier: 'css-1', + path: '//demo/application/themes/x/style.css', + name: 'style.css', + extension: 'css', + origin: 'theme', + working: '.a{color:red}', + live: '', + added: 1, + removed: 0 + } +]; + +describe('DotA11yDiffComponent', () => { + let spectator: Spectator<DotA11yDiffComponent>; + + let selectedPage: typeof MOCK_PAGE | null = MOCK_PAGE; + /** Signal-backed so bumping it re-runs the component's reload effect. */ + const previewRevision = signal(0); + + const storeMock = { + selected: () => selectedPage, + previewRevision: () => previewRevision() + }; + + const createComponent = createComponentFactory({ + component: DotA11yDiffComponent, + componentProviders: [{ provide: A11yRunStore, useValue: storeMock }], + providers: [ + { + provide: DotMessageService, + useValue: new MockDotMessageService({ + 'accessibility.studio.diff.fileschanged': 'Files changed', + 'accessibility.studio.diff.empty.title': 'No files changed', + 'accessibility.studio.diff.loading': 'Loading…', + 'accessibility.studio.diff.working': 'Working', + 'accessibility.studio.diff.live': 'Live' + }) + } + ] + }); + + beforeEach(() => { + jest.clearAllMocks(); + jest.useFakeTimers(); + selectedPage = MOCK_PAGE; + previewRevision.set(0); + }); + + afterEach(() => { + jest.useRealTimers(); + }); + + /** + * Run the reload pipeline's debounce and re-render. The component collapses a burst + * of `previewRevision` bumps into one load, so nothing is fetched until the timer + * fires — every assertion about a load has to come after this. + */ + function flushReload() { + jest.advanceTimersByTime(DIFF_RELOAD_DEBOUNCE_MS); + spectator.detectChanges(); + } + + /** Render the accordion with the given diff files. */ + function render(diffFiles: PageDiffFile[] = DIFF_FILES) { + spectator = createComponent({ + providers: [ + mockProvider(DotPageSourcesService, { + getPageSources: jest + .fn() + .mockReturnValue(of(diffFiles.map((f) => f as PageSourceFile))), + getDiffFiles: jest.fn().mockReturnValue(of(diffFiles)) + }) + ] + }); + spectator.detectChanges(); + flushReload(); + } + + it('loads the diff for the selected page on init — no scan required', () => { + render(); + expect(spectator.inject(DotPageSourcesService).getPageSources).toHaveBeenCalledWith( + '/about-us', + 'host-1', + 1 + ); + }); + + it('lists only the changed files with add/remove counts', () => { + render(); + const rows = spectator.queryAll(byTestId('diff-file-row')); + expect(rows.length).toBe(2); + // The count badge itself lives on the run screen's panel header now. + expect(rows[0].textContent).toContain('+1'); + }); + + it('shows each file name and its +/- line counts, but not the folder path', () => { + render(); + const rows = spectator.queryAll(byTestId('diff-file-row')); + expect(rows[0].textContent).toContain('a.vtl'); + expect(rows[0].textContent).toContain('+1'); + expect(rows[0].textContent).not.toContain('//demo/application/containers/'); + expect(rows[1].textContent).toContain('style.css'); + expect(rows[1].textContent).not.toContain('//demo/application/themes/'); + }); + + it('emits the picked file so the run screen can diff it in the right pane', () => { + render(); + const emitted: (PageDiffFile | null)[] = []; + spectator.component.fileSelected.subscribe((f) => emitted.push(f)); + + spectator.click(spectator.queryAll(byTestId('diff-file-row'))[0]); + expect(emitted).toEqual([DIFF_FILES[0]]); + }); + + it('offers a way back to the preview once a file is open, and emits null', () => { + render(); + const emitted: (PageDiffFile | null)[] = []; + spectator.component.fileSelected.subscribe((f) => emitted.push(f)); + + // No back control until a file is actually being diffed. + expect(spectator.query(byTestId('diff-back-to-preview-btn'))).toBeFalsy(); + + // The run screen owns the selection and feeds it back in. + spectator.setInput('activeFileId', DIFF_FILES[0].identifier); + spectator.detectChanges(); + + spectator.click(spectator.query(byTestId('diff-back-to-preview-btn')) as HTMLElement); + expect(emitted).toEqual([null]); + }); + + it('closes the right pane when a reload drops the file it was showing', () => { + render(); + spectator.setInput('activeFileId', DIFF_FILES[0].identifier); + spectator.detectChanges(); + + const emitted: (PageDiffFile | null)[] = []; + spectator.component.fileSelected.subscribe((f) => emitted.push(f)); + + // A publish makes working == live, so the file leaves the list. + spectator.inject(DotPageSourcesService).getDiffFiles.mockReturnValue(of([DIFF_FILES[1]])); + previewRevision.set(1); + spectator.detectChanges(); + flushReload(); + + expect(emitted).toEqual([null]); + }); + + it('reports the changed-file count so the panel header can badge it', () => { + const counts: number[] = []; + spectator = createComponent({ + providers: [ + mockProvider(DotPageSourcesService, { + getPageSources: jest.fn().mockReturnValue(of(DIFF_FILES as PageSourceFile[])), + getDiffFiles: jest.fn().mockReturnValue(of(DIFF_FILES)) + }) + ] + }); + spectator.component.changedCount.subscribe((n) => counts.push(n)); + spectator.detectChanges(); + flushReload(); + + // Re-resolve on a revision bump so the count is re-reported. + previewRevision.set(1); + spectator.detectChanges(); + flushReload(); + expect(counts).toContain(2); + }); + + it('shows the empty state when nothing changed', () => { + render([]); + expect(spectator.query(byTestId('diff-empty'))).toBeTruthy(); + expect(spectator.query(byTestId('diff-file-list'))).toBeFalsy(); + }); + + it('reports zero changed files when the page has no working-vs-live delta', () => { + const counts: number[] = []; + spectator = createComponent({ + providers: [ + mockProvider(DotPageSourcesService, { + getPageSources: jest.fn().mockReturnValue(of([])), + getDiffFiles: jest.fn().mockReturnValue(of([])) + }) + ] + }); + spectator.component.changedCount.subscribe((n) => counts.push(n)); + previewRevision.set(1); + spectator.detectChanges(); + flushReload(); + + expect(counts).toContain(0); + }); + + it('shows the error state when the diff load fails', () => { + spectator = createComponent({ + providers: [ + mockProvider(DotPageSourcesService, { + getPageSources: jest.fn().mockReturnValue(of([])), + getDiffFiles: jest.fn().mockReturnValue(throwError(() => new Error('boom'))) + }) + ] + }); + spectator.detectChanges(); + flushReload(); + expect(spectator.query(byTestId('diff-error'))).toBeTruthy(); + }); +}); diff --git a/core-web/libs/portlets/dot-agents/src/lib/agents/a11y/a11y-diff/a11y-diff.component.ts b/core-web/libs/portlets/dot-agents/src/lib/agents/a11y/a11y-diff/a11y-diff.component.ts new file mode 100644 index 000000000000..9a2b9a4060ff --- /dev/null +++ b/core-web/libs/portlets/dot-agents/src/lib/agents/a11y/a11y-diff/a11y-diff.component.ts @@ -0,0 +1,193 @@ +import { of } from 'rxjs'; + +import { + ChangeDetectionStrategy, + Component, + computed, + DestroyRef, + inject, + input, + output, + signal +} from '@angular/core'; +import { takeUntilDestroyed, toObservable } from '@angular/core/rxjs-interop'; + +import { + catchError, + debounceTime, + distinctUntilChanged, + filter, + map, + switchMap +} from 'rxjs/operators'; + +import { DotMessagePipe } from '@dotcms/ui'; + +import { PageDiffFile } from '../models/page-render-sources.models'; +import { DotPageSourcesService } from '../services/dot-page-sources.service'; +import { A11yRunStore } from '../store/a11y-run.store'; + +/** Load status of the file list. */ +type DiffStatus = 'loading' | 'loaded' | 'error'; + +/** + * How long to wait for `previewRevision` to settle before reloading the file list. + * Long enough to collapse a burst of SSE progress frames into one load, short enough + * that a terminal frame still updates the panel promptly. + */ +export const DIFF_RELOAD_DEBOUNCE_MS = 400; + +/** + * The "working vs live" changed-file list — the body of the side panel's "Files" + * accordion panel. The run screen owns the panel chrome (header, count badge, + * Publish action); this is just the list. + * + * One row per source file that DIFFERS between the working (unpublished) and live + * (published) versions. Selecting a file emits it upward: the run screen swaps its + * RIGHT pane from the preview to that file's diff, so the narrow side panel stays a + * list and the diff gets the full width. A "Back to preview" control here clears the + * selection, so the user can leave the diff view without reaching into the right + * pane. When nothing differs it says so rather than hiding. + * + * It's a presentational child of {@link DotA11yRunComponent}: the page context + * comes from the run screen's {@link A11yRunStore} (which this injects up the DI + * tree), so there's no routing/rehydration here. The list loads as soon as + * the page is known — before any scan — so pre-existing working edits (an earlier + * run, a manual change) are visible immediately, and reloads whenever the working + * render changes (each run, re-scan, publish). + * + * Data path (see {@link DotPageSourcesService}): + * `_render-sources` → flatten to file assets → per file, fetch working + live + * text via each version's `/dA/<inode>/…` URL → keep only the ones that differ. + */ +@Component({ + selector: 'dot-a11y-diff', + imports: [DotMessagePipe], + templateUrl: './a11y-diff.component.html', + providers: [DotPageSourcesService], + changeDetection: ChangeDetectionStrategy.OnPush, + host: { class: 'block' } +}) +export class DotA11yDiffComponent { + readonly store = inject(A11yRunStore); + + readonly #sourcesService = inject(DotPageSourcesService); + readonly #destroyRef = inject(DestroyRef); + + /** + * The file whose diff the right pane should show, or null for the preview. + * The run screen owns the right pane, so the selection is published upward + * rather than rendered here. + */ + readonly fileSelected = output<PageDiffFile | null>(); + + /** + * Which file the right pane is currently diffing, owned by the run screen. Fed + * back in so the highlighted row follows the pane — including when the pane is + * closed from its own control rather than from this list. + */ + readonly activeFileId = input<string | null>(null); + + /** + * How many files differ, emitted on every (re)load. The run screen owns the + * panel header's count badge and the Publish action, so it needs this rather + * than reaching into the child. + */ + readonly changedCount = output<number>(); + + /** Changed files (working ≠ live); empty until the first load resolves. */ + readonly $files = signal<PageDiffFile[]>([]); + readonly $status = signal<DiffStatus>('loading'); + + /** Identifier of the file being diffed in the right pane; null → preview. */ + readonly $selectedId = computed(() => this.activeFileId()); + + /** The currently selected diff file. */ + readonly $selected = computed<PageDiffFile | null>(() => { + const id = this.$selectedId(); + + return this.$files().find((f) => f.identifier === id) ?? null; + }); + + /** True once loaded and there are no changed files to show. */ + readonly $empty = computed(() => this.$status() === 'loaded' && this.$files().length === 0); + + constructor() { + // Load the file list as soon as the page is known — before any scan — and reload + // whenever the working render changes (each run, re-scan, publish). + // + // Built as a stream rather than an effect for two reasons, both about + // `previewRevision` being a HOT key: it bumps on every SSE progress frame, and one + // load is a `_render-sources` call plus two fetches per source file. + // - `debounceTime` collapses a burst of frames into a single load, instead of + // dozens of overlapping request sets per run. + // - `switchMap` makes the newest load the only one that can write. Previously + // nothing superseded an in-flight load, so a slow early response could land + // after a fast later one and set a stale `files`/`changedCount` — and since + // the Publish bar is gated on `changedFileCount`, that could flip + // `hasChangedFiles()` back to FALSE after the agent had written files, + // blocking publish until request ordering happened to favour it. + toObservable( + computed(() => { + const page = this.store.selected(); + + return page + ? { + key: `${page.identifier}#${this.store.previewRevision()}`, + path: page.path, + hostId: page.hostId, + languageId: page.languageId + } + : null; + }) + ) + .pipe( + filter((request) => request !== null), + distinctUntilChanged((a, b) => a.key === b.key), + debounceTime(DIFF_RELOAD_DEBOUNCE_MS), + switchMap((request) => { + this.$status.set('loading'); + + return this.#sourcesService + .getPageSources(request.path, request.hostId, request.languageId) + .pipe( + switchMap((sources) => + this.#sourcesService.getDiffFiles(sources, request.languageId) + ), + map((files) => ({ files, failed: false })), + catchError(() => of({ files: [] as PageDiffFile[], failed: true })) + ); + }), + takeUntilDestroyed(this.#destroyRef) + ) + .subscribe(({ files, failed }) => { + if (failed) { + this.$status.set('error'); + this.changedCount.emit(0); + + return; + } + + this.$files.set(files); + // A reload can drop the file the right pane was showing (e.g. a + // publish makes working == live). Close the pane in that case so + // it isn't left diffing something no longer in the list. + const openId = this.$selectedId(); + if (openId && !files.some((f) => f.identifier === openId)) { + this.fileSelected.emit(null); + } + this.$status.set('loaded'); + this.changedCount.emit(files.length); + }); + } + + /** Open a file's diff in the right pane. */ + selectFile(identifier: string): void { + this.fileSelected.emit(this.$files().find((f) => f.identifier === identifier) ?? null); + } + + /** Leave the diff view — the right pane goes back to the preview. */ + clearSelection(): void { + this.fileSelected.emit(null); + } +} diff --git a/core-web/libs/portlets/dot-agents/src/lib/agents/a11y/a11y-page-list/a11y-page-list.component.html b/core-web/libs/portlets/dot-agents/src/lib/agents/a11y/a11y-page-list/a11y-page-list.component.html new file mode 100644 index 000000000000..2ae8c3d0fc1b --- /dev/null +++ b/core-web/libs/portlets/dot-agents/src/lib/agents/a11y/a11y-page-list/a11y-page-list.component.html @@ -0,0 +1,139 @@ +<div class="mx-auto w-full max-w-[1120px] px-8 py-12"> + <!-- Header --> + <header class="mb-6 flex items-center gap-3"> + <span + class="flex size-11 items-center justify-center rounded-xl bg-primary text-primary-contrast"> + <i class="pi pi-eye text-2xl!" aria-hidden="true"></i> + </span> + <div> + <h1 class="m-0 text-xl font-extrabold tracking-tight text-color"> + {{ 'accessibility.studio.title' | dm }} + </h1> + <p class="m-0 mt-0.5 text-sm text-muted-color"> + {{ 'accessibility.studio.pagelist.subtitle' | dm }} + </p> + </div> + </header> + + <!-- Search --> + <p-iconField iconPosition="left" class="block"> + <p-inputIcon styleClass="pi pi-search" /> + <input + pInputText + type="text" + [ngModel]="store.filter()" + (ngModelChange)="onSearch($event)" + [placeholder]="'accessibility.studio.pagelist.search.placeholder' | dm" + [attr.aria-label]="'accessibility.studio.pagelist.search.placeholder' | dm" + data-testid="studio-search-input" + class="h-13 w-full" /> + </p-iconField> + + <!-- Result count --> + <div class="mt-4 mb-2.5 flex items-center gap-2 px-0.5"> + <span class="text-xs font-semibold text-muted-color" data-testid="studio-result-count"> + {{ + 'accessibility.studio.pagelist.count' + | dm: [store.pages().length.toString(), store.totalRecords().toString()] + }} + </span> + <span class="ml-auto inline-flex items-center gap-1.5 text-xs text-muted-color"> + <i class="pi pi-sort-amount-down text-sm!" aria-hidden="true"></i> + {{ 'accessibility.studio.pagelist.sorted' | dm }} + </span> + </div> + + <!-- Pages table --> + <div + class="overflow-hidden rounded-2xl border border-surface-200" + data-testid="studio-pages-wrapper"> + <p-table + [value]="store.pages()" + [lazy]="true" + (onLazyLoad)="onLazyLoad($event)" + [paginator]="store.totalRecords() > store.rows()" + [rows]="store.rows()" + [totalRecords]="store.totalRecords()" + [first]="(store.page() - 1) * store.rows()" + [rowsPerPageOptions]="[10, 25, 50]" + [rowHover]="true" + [pt]="$ptConfig" + dataKey="identifier" + data-testid="studio-pages-table"> + <ng-template pTemplate="header"> + <tr> + <th class="w-[26%]">{{ 'accessibility.studio.pagelist.col.title' | dm }}</th> + <th class="w-[28%]">{{ 'accessibility.studio.pagelist.col.url' | dm }}</th> + <th class="w-[16%]">{{ 'accessibility.studio.pagelist.col.type' | dm }}</th> + <th class="w-[12%]">{{ 'accessibility.studio.pagelist.col.status' | dm }}</th> + <th class="w-[18%]"> + {{ 'accessibility.studio.pagelist.col.edited' | dm }} + </th> + </tr> + </ng-template> + + <ng-template pTemplate="body" let-row> + @if (store.pageListStatus() === 'loading') { + <tr data-testid="studio-loading-row" class="h-14 hover:bg-transparent!"> + <td><p-skeleton height="1.25rem" width="80%" /></td> + <td><p-skeleton height="1.25rem" width="90%" /></td> + <td><p-skeleton height="1.25rem" width="60%" /></td> + <td><p-skeleton height="1.25rem" width="70%" /></td> + <td><p-skeleton height="1.25rem" width="60%" /></td> + </tr> + } @else { + <tr + data-testid="studio-page-row" + class="cursor-pointer" + (click)="openPage(row)"> + <td + class="truncate font-semibold text-color" + data-testid="studio-page-title"> + {{ row.title }} + </td> + <td class="truncate text-muted-color">{{ row.path }}</td> + <td class="truncate text-muted-color">{{ row.type }}</td> + <td> + @if (row.live) { + <p-tag + [value]="'accessibility.studio.pagelist.status.published' | dm" + severity="success" + [rounded]="true" /> + } @else { + <p-tag + [value]="'accessibility.studio.pagelist.status.draft' | dm" + severity="warn" + [rounded]="true" /> + } + </td> + <td class="text-muted-color">{{ row.modDate }}</td> + </tr> + } + </ng-template> + + <ng-template pTemplate="emptymessage"> + <tr class="hover:bg-transparent!"> + <td colspan="5" class="border-none p-12 text-center"> + <div + class="mx-auto flex max-w-100 flex-col items-center gap-3" + data-testid="studio-empty-state"> + <i + class="pi pi-search text-5xl! text-surface-400" + aria-hidden="true"></i> + <h3 class="m-0 text-lg font-medium text-color"> + {{ 'accessibility.studio.pagelist.empty.title' | dm }} + </h3> + <p class="m-0 text-base leading-6 text-muted-color"> + {{ 'accessibility.studio.pagelist.empty.description' | dm }} + </p> + </div> + </td> + </tr> + </ng-template> + </p-table> + </div> + + <p class="mt-4 text-center text-xs text-muted-color"> + {{ 'accessibility.studio.pagelist.hint' | dm }} + </p> +</div> diff --git a/core-web/libs/portlets/dot-agents/src/lib/agents/a11y/a11y-page-list/a11y-page-list.component.spec.ts b/core-web/libs/portlets/dot-agents/src/lib/agents/a11y/a11y-page-list/a11y-page-list.component.spec.ts new file mode 100644 index 000000000000..1e3e2c1bf4e6 --- /dev/null +++ b/core-web/libs/portlets/dot-agents/src/lib/agents/a11y/a11y-page-list/a11y-page-list.component.spec.ts @@ -0,0 +1,136 @@ +import { byTestId, createComponentFactory, Spectator } from '@openng/spectator/jest'; + +import { ActivatedRoute, Router } from '@angular/router'; + +import { DotMessageService } from '@dotcms/data-access'; +import { MockDotMessageService } from '@dotcms/utils-testing'; + +import { DotA11yPageListComponent } from './a11y-page-list.component'; + +import { StudioPageRow } from '../models/accessibility-studio.models'; +import { A11yPageListStore } from '../store/a11y-page-list.store'; + +const MOCK_ROWS: StudioPageRow[] = [ + { + identifier: 'id-1', + title: 'About Us', + path: '/about-us', + type: 'htmlpageasset', + languageId: 1, + hostId: 'host-id-1', + hostName: 'demo.dotcms.com', + modDate: '04/09/2026', + modUserName: 'Admin User', + live: true + }, + { + identifier: 'id-2', + title: 'Draft Page', + path: '/draft', + type: 'Blog', + languageId: 1, + hostId: 'host-id-1', + hostName: 'demo.dotcms.com', + modDate: '03/10/2026', + modUserName: 'Admin User', + live: false + } +]; + +describe('DotA11yPageListComponent', () => { + let spectator: Spectator<DotA11yPageListComponent>; + + const setFilter = jest.fn(); + const setPagination = jest.fn(); + const navigate = jest.fn(); + + const storeMock = { + pages: () => MOCK_ROWS, + totalRecords: () => 2, + page: () => 1, + rows: () => 25, + filter: () => '', + pageListStatus: () => 'loaded', + setFilter, + setPagination + }; + + const createComponent = createComponentFactory({ + component: DotA11yPageListComponent, + componentProviders: [{ provide: A11yPageListStore, useValue: storeMock }], + providers: [ + { provide: Router, useValue: { navigate } }, + { provide: ActivatedRoute, useValue: {} }, + { + provide: DotMessageService, + useValue: new MockDotMessageService({ + 'accessibility.studio.title': 'Accessibility Studio', + 'accessibility.studio.pagelist.col.title': 'Title', + 'accessibility.studio.pagelist.status.published': 'Published', + 'accessibility.studio.pagelist.status.draft': 'Draft' + }) + } + ] + }); + + beforeEach(() => { + jest.clearAllMocks(); + spectator = createComponent(); + spectator.detectChanges(); + }); + + it('renders a row per page', () => { + expect(spectator.queryAll(byTestId('studio-page-row')).length).toBe(2); + }); + + it('renders the page title', () => { + const titles = spectator.queryAll(byTestId('studio-page-title')); + expect(titles[0]).toHaveText('About Us'); + }); + + it('navigates to the page run route (deep link) when a row is clicked', () => { + spectator.click(spectator.queryAll(byTestId('studio-page-row'))[0]); + // Navigates to the page path as route segments relative to the page list — + // the run screen then drives the store from the URL. Selection not set here. + // MOCK_ROWS[0].path === '/about-us' → ['about-us']. + expect(navigate).toHaveBeenCalledWith( + ['about-us'], + expect.objectContaining({ relativeTo: expect.anything() }) + ); + }); + + it('debounces search input before calling setFilter', () => { + jest.useFakeTimers(); + spectator.component.onSearch('contact'); + expect(setFilter).not.toHaveBeenCalled(); + jest.advanceTimersByTime(300); + expect(setFilter).toHaveBeenCalledWith('contact'); + jest.useRealTimers(); + }); + + describe('onLazyLoad', () => { + // PrimeNG's table reports a zero-based ROW OFFSET; the store wants a 1-based PAGE + // number. Off-by-one page math regresses quietly — the table still renders, just the + // wrong slice — so pin the conversion rather than trusting it by eye. + it('converts the first-row offset into a 1-based page number', () => { + spectator.component.onLazyLoad({ first: 25, rows: 25 }); + expect(setPagination).toHaveBeenCalledWith(2, 25); + }); + + it('treats the first-row boundary as page 1', () => { + spectator.component.onLazyLoad({ first: 0, rows: 25 }); + expect(setPagination).toHaveBeenCalledWith(1, 25); + }); + + it('handles a later page and a different page size', () => { + spectator.component.onLazyLoad({ first: 90, rows: 30 }); + expect(setPagination).toHaveBeenCalledWith(4, 30); + }); + + it('falls back to the store values when the event omits them', () => { + // PrimeNG can emit a lazy-load event with neither field set. + spectator.component.onLazyLoad({}); + expect(setPagination).toHaveBeenCalledWith(1, 25); + }); + }); +}); diff --git a/core-web/libs/portlets/dot-agents/src/lib/agents/a11y/a11y-page-list/a11y-page-list.component.ts b/core-web/libs/portlets/dot-agents/src/lib/agents/a11y/a11y-page-list/a11y-page-list.component.ts new file mode 100644 index 000000000000..469c0f02a2ce --- /dev/null +++ b/core-web/libs/portlets/dot-agents/src/lib/agents/a11y/a11y-page-list/a11y-page-list.component.ts @@ -0,0 +1,89 @@ +import { Subject } from 'rxjs'; + +import { ChangeDetectionStrategy, Component, DestroyRef, inject } from '@angular/core'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; +import { FormsModule } from '@angular/forms'; +import { ActivatedRoute, Router } from '@angular/router'; + +import { IconFieldModule } from 'primeng/iconfield'; +import { InputIconModule } from 'primeng/inputicon'; +import { InputTextModule } from 'primeng/inputtext'; +import { SkeletonModule } from 'primeng/skeleton'; +import { TableLazyLoadEvent, TableModule } from 'primeng/table'; +import { TagModule } from 'primeng/tag'; + +import { debounceTime, distinctUntilChanged } from 'rxjs/operators'; + +import { DotMessagePipe } from '@dotcms/ui'; + +import { StudioPageRow } from '../models/accessibility-studio.models'; +import { A11yPageListStore } from '../store/a11y-page-list.store'; + +/** + * The Studio entry screen: lists/searches the site's pages and selects one + * to scan. Pages come from a real `_search`; selecting a row opens the studio. + */ +@Component({ + selector: 'dot-a11y-page-list', + imports: [ + FormsModule, + TableModule, + InputTextModule, + IconFieldModule, + InputIconModule, + SkeletonModule, + TagModule, + DotMessagePipe + ], + templateUrl: './a11y-page-list.component.html', + providers: [A11yPageListStore], + changeDetection: ChangeDetectionStrategy.OnPush, + host: { class: 'block h-full min-h-0 overflow-y-auto' } +}) +export class DotA11yPageListComponent { + readonly store = inject(A11yPageListStore); + + /** Skeleton rows to render while a page of results loads. */ + readonly skeletonRows = Array.from({ length: 8 }); + + /** Pass-through config: fixed table layout so column widths hold on empty state. */ + readonly $ptConfig = { table: { style: { 'table-layout': 'fixed' as const } } }; + + readonly #destroyRef = inject(DestroyRef); + readonly #router = inject(Router); + readonly #route = inject(ActivatedRoute); + readonly #searchSubject = new Subject<string>(); + + constructor() { + this.#searchSubject + .pipe(debounceTime(300), distinctUntilChanged(), takeUntilDestroyed(this.#destroyRef)) + .subscribe((value) => this.store.setFilter(value)); + } + + onSearch(value: string): void { + this.#searchSubject.next(value); + } + + /** + * Open a page by navigating to its run route, so the run URL carries a readable + * path (e.g. `/agents/a11y/blog/post/hello`). + * + * The selected row rides along in the navigation's `state`: the run screen needs + * the whole {@link StudioPageRow} (identifier, host, language) and the path alone + * can't supply it. Handing it over here is what lets the run store skip a lookup + * entirely — the trade-off is that the run route is only reachable THROUGH this + * list, so a cold load / refresh of a run URL has no row and bounces back here. + */ + openPage(row: StudioPageRow): void { + // "/blog/post/hello" → ['blog','post','hello'] (drop empty leading/trailing). + const segments = row.path.split('/').filter(Boolean); + this.#router.navigate(segments, { relativeTo: this.#route, state: { row } }); + } + + onLazyLoad(event: TableLazyLoadEvent): void { + const rows = (event.rows as number) ?? this.store.rows(); + const first = (event.first as number) ?? 0; + const page = Math.floor(first / rows) + 1; + this.store.setPagination(page, rows); + } +} diff --git a/core-web/libs/portlets/dot-agents/src/lib/agents/a11y/a11y-root/a11y-root.component.ts b/core-web/libs/portlets/dot-agents/src/lib/agents/a11y/a11y-root/a11y-root.component.ts new file mode 100644 index 000000000000..9265871d7dde --- /dev/null +++ b/core-web/libs/portlets/dot-agents/src/lib/agents/a11y/a11y-root/a11y-root.component.ts @@ -0,0 +1,23 @@ +import { ChangeDetectionStrategy, Component } from '@angular/core'; +import { RouterOutlet } from '@angular/router'; + +/** + * Root of the Accessibility Studio agent — a thin layout wrapper that owns the + * full-height host box and hosts the child routes via `<router-outlet>`: + * - `''` → the page list (provides its own {@link A11yPageListStore}) + * - `**` → the run screen (provides its own {@link A11yRunStore}) + * + * It holds NO store: the page-list and run screens are independent routes, each + * providing its own store at its component (see {@link dotAccessibilityStudioRoutes}), + * so run state resets per page and page-list state never leaks into a run. + */ +@Component({ + selector: 'dot-a11y', + imports: [RouterOutlet], + template: ` + <router-outlet /> + `, + changeDetection: ChangeDetectionStrategy.OnPush, + host: { class: 'flex flex-col h-full min-h-0 block bg-surface-100' } +}) +export class DotA11yRootComponent {} diff --git a/core-web/libs/portlets/dot-agents/src/lib/agents/a11y/a11y-run/a11y-run.component.html b/core-web/libs/portlets/dot-agents/src/lib/agents/a11y/a11y-run/a11y-run.component.html new file mode 100644 index 000000000000..83890af83b42 --- /dev/null +++ b/core-web/libs/portlets/dot-agents/src/lib/agents/a11y/a11y-run/a11y-run.component.html @@ -0,0 +1,661 @@ +<!-- ============ ERROR BANNER ============ --> +<!-- The single error surface for this screen: a failed scan, a failed or dropped fix + run, or a stop that didn't take. Spans both columns at the top of the portlet so + it's visible whichever pane the user is looking at, and takes no space when there + is no error (the host's first grid row is `auto`, and nothing else is placed in it). + Every child below is pinned to row 2 explicitly — without that, auto-placement puts + them in row 1 whenever this banner is absent, collapsing both panes to their content + height and leaving row 2 empty. --> +@if (store.runError(); as error) { + <div + class="col-span-2 row-start-1 flex items-start gap-2.5 border-b border-red-100 bg-red-50 px-4 py-2.5 text-red-700" + data-testid="studio-run-error" + role="alert"> + <i class="pi pi-exclamation-triangle mt-0.5 flex-none" aria-hidden="true"></i> + <div class="min-w-0"> + <span class="font-semibold">{{ 'accessibility.studio.error.title' | dm }}</span> + <span class="ml-1.5 wrap-break-word text-red-600">{{ error }}</span> + </div> + </div> +} + +<!-- ============ AGENT COLUMN ============ --> +<aside class="row-start-2 flex min-h-0 flex-col border-r border-surface-200 bg-white"> + <!-- Page context bar --> + <div class="flex h-13 flex-none items-center gap-2.5 border-b border-surface-100 pr-4 pl-3"> + <p-button + icon="pi pi-arrow-left" + [text]="true" + severity="secondary" + [rounded]="true" + (onClick)="backToPageList()" + [attr.aria-label]="'accessibility.studio.back' | dm" + [pTooltip]="'accessibility.studio.back' | dm" + data-testid="studio-back-btn" /> + <i class="pi pi-file text-primary" aria-hidden="true"></i> + <div class="min-w-0"> + <div class="truncate font-bold text-color"> + {{ store.selected()?.title }} + </div> + <div class="truncate text-muted-color">{{ store.selected()?.path }}</div> + </div> + </div> + + <!-- ============ SIDE PANEL ACCORDION ============ --> + <!-- Two panels: the scanner (score, issues, activity log + scan/fix actions) and + the changed files (list + discard/publish). `multiple` so they open and close + independently — a run can be watched while reviewing the files it touched. + The scanner starts open (it's the entry point); files start collapsed. --> + <!-- The accordion is a flex column that fills the aside; the panel HEADERS stay + put and each open panel's CONTENT scrolls on its own (see below), rather + than the whole accordion scrolling as one block. --> + <p-accordion + [multiple]="true" + [(value)]="$openPanels" + styleClass="flex min-h-0 flex-1 flex-col" + data-testid="studio-panels"> + <!-- ---------- PANEL 1: SCANNER ---------- --> + <!-- The scanner is the tall panel: while open it grows to take the remaining + height and its own content scrolls; while collapsed it's just a header. --> + <p-accordion-panel + value="scanner" + [class.flex]="isPanelOpen('scanner')" + [class.min-h-0]="isPanelOpen('scanner')" + [class.flex-col]="isPanelOpen('scanner')" + [class.flex-1]="isPanelOpen('scanner')" + data-testid="studio-panel-scanner"> + <p-accordion-header> + <span data-testid="studio-panel-scanner-title"> + {{ 'accessibility.studio.panel.scanner' | dm }} + </span> + </p-accordion-header> + <!-- p-0!: PrimeNG's accordion content ships its own padding, and the score + widget + issue list draw their own full-bleed section dividers, which the + default padding would inset. min-h-0/h-full let this panel be bounded + shorter than its content so it scrolls internally (see :host styles). --> + <p-accordion-content + [pt]="{ + root: { class: 'min-h-0 flex-1' }, + content: { class: 'p-0! min-h-0 h-full' } + }"> + <div class="flex flex-col" data-testid="studio-panel-scanner-body"> + <!-- Score widget SKELETON — shown while scanning, with the SAME footprint as + the real widget so the swap to results is a crossfade with no layout shift. --> + @if (store.phase() === 'scanning') { + <div + class="studio-fade-in flex flex-none animate-pulse items-center justify-center gap-8 border-b border-surface-100 px-6 py-5" + data-testid="studio-score-skeleton" + aria-hidden="true"> + <!-- Ring placeholder: same 124px square, drawn as a thick gray donut. --> + <div + class="h-[124px] w-[124px] flex-none rounded-full border-13 border-surface-200"></div> + <!-- Legend placeholder: a header bar + three rows (dot + label). --> + <div class="flex min-w-0 flex-col gap-2.5"> + <div class="h-3 w-24 rounded-sm bg-surface-200"></div> + @for (i of [1, 2, 3]; track i) { + <div class="flex items-center gap-2"> + <span + class="size-2.5 flex-none rounded-full bg-surface-200"></span> + <span class="h-3 w-28 rounded-sm bg-surface-200"></span> + </div> + } + </div> + </div> + } + + <!-- Score widget — shown once a scan has produced results (scanned/fixing/done). + Hidden while ready + scanning (scanning shows the skeleton above). --> + @if (store.hasResults()) { + <div + class="studio-fade-in flex flex-none items-center justify-center gap-8 border-b border-surface-100 px-6 py-5"> + <!-- Donut: severity-segmented ring with the live open count in the center. + The wrapper and the p-chart host MUST be the same size or the canvas + overflows and the centered overlay drifts. The app's root font is 14px, + so a rem-based `size-*` wouldn't equal a px chart — both are pinned to + the same explicit 124px so ring center == overlay center. --> + <div + class="relative h-[124px] w-[124px] flex-none" + data-testid="studio-score-ring"> + <p-chart + type="doughnut" + [data]="$donutData()" + [options]="donutOptions" + width="124px" + height="124px" /> + <div + class="pointer-events-none absolute inset-0 flex flex-col items-center justify-center leading-none"> + <span + class="text-4xl font-extrabold text-color tabular-nums" + data-testid="studio-score-count"> + {{ $displayCount() }} + </span> + <span + class="mt-1 font-bold tracking-wider text-muted-color uppercase"> + {{ 'accessibility.studio.score.open' | dm }} + </span> + </div> + </div> + + <div class="min-w-0"> + <!-- Headline: "Issues found" (scanned) / "Fixing issues…" / "Issues remaining". + Uses the same section-label style as the other column headers. --> + <div + class="mb-2 font-bold tracking-wider text-muted-color uppercase"> + {{ $scoreHeadlineKey() | dm }} + </div> + + <!-- before → open delta (only once a run has started). --> + @if (store.runStarted()) { + <div class="mb-2.5 flex items-center gap-2.5"> + <div class="flex flex-col"> + <span + class="text-2xl leading-none font-extrabold text-muted-color"> + {{ store.beforeCount() }} + </span> + <span class="mt-0.5 text-muted-color"> + {{ 'accessibility.studio.score.before' | dm }} + </span> + </div> + <i + class="pi pi-arrow-right text-surface-300" + aria-hidden="true"></i> + <div class="flex flex-col"> + <span + class="text-2xl leading-none font-extrabold text-color"> + {{ store.openCount() }} + </span> + <span class="mt-0.5 text-muted-color"> + {{ + store.phase() === 'fixing' + ? ('accessibility.studio.score.open' | dm) + : ('accessibility.studio.score.now' | dm) + }} + </span> + </div> + </div> + } + + <!-- Severity legend: N Critical / Serious / Moderate / Minor. --> + <div + class="flex flex-col gap-1.5" + data-testid="studio-severity-legend"> + @for (row of $severityRows(); track row.severity) { + <div class="flex items-center gap-2"> + <span + class="size-2.5 flex-none rounded-full" + [style.background-color]="row.color" + aria-hidden="true"></span> + <span class="font-bold text-color"> + {{ row.count }} + </span> + <span class="text-muted-color"> + {{ row.label }} + </span> + </div> + } + </div> + + <!-- Needs-review: axe flagged these but couldn't confirm them (e.g. + contrast over an image). The agent doesn't auto-fix them, so they + stay out of the count above — but we surface them so the user + knows they exist and need a human eye. --> + @if (store.warningCount() > 0) { + <div + class="mt-2 flex items-center gap-1.5 text-muted-color" + data-testid="studio-needsreview-note"> + <i class="pi pi-eye" aria-hidden="true"></i> + <span> + {{ + 'accessibility.studio.score.needsreview' + | dm: [store.warningCount().toString()] + }} + </span> + </div> + } + </div> + </div> + } + + <!-- Section header — label + working badge, both phase-aware. Hidden when ready. --> + @if (store.phase() !== 'ready') { + <div class="flex flex-none items-center gap-2 px-6 pt-4 pb-2"> + <span class="font-bold tracking-wider text-muted-color uppercase"> + {{ $logHeaderKey() | dm }} + </span> + @if ($logBadgeKey(); as badge) { + <span + class="inline-flex h-5 items-center gap-1.5 rounded-full bg-primary-50 px-2 font-bold text-primary" + data-testid="studio-working-badge"> + <span + class="size-1.5 animate-pulse rounded-full bg-primary"></span> + {{ badge | dm }} + </span> + } + </div> + } + + <!-- Recipe log: sizes to content. The accordion column owns the scrolling, so + this must NOT scroll on its own — a nested scroll area would trap the + wheel and make the second panel hard to reach with both open. --> + <div data-testid="studio-recipe-log" class="mx-4 mb-8"> + @if (store.phase() === 'ready') { + <!-- Ready: what to expect --> + <div + class="rounded-2xl border border-surface-200 bg-surface-50 p-5" + data-testid="studio-ready-card"> + <div class="mb-1.5 font-bold text-color"> + {{ 'accessibility.studio.ready.title' | dm }} + </div> + <p class="m-0 mb-4 leading-relaxed text-muted-color"> + {{ 'accessibility.studio.ready.description' | dm }} + </p> + <div class="flex flex-col gap-2.5"> + <div class="flex items-center gap-3"> + <span + class="flex size-7 flex-none items-center justify-center rounded-lg bg-primary-50 text-primary"> + <i class="pi pi-search" aria-hidden="true"></i> + </span> + <span class="text-muted-color"> + {{ 'accessibility.studio.ready.step1' | dm }} + </span> + </div> + <div class="flex items-center gap-3"> + <span + class="flex size-7 flex-none items-center justify-center rounded-lg bg-primary-50 text-primary"> + <i class="pi pi-sitemap" aria-hidden="true"></i> + </span> + <span class="text-muted-color"> + {{ 'accessibility.studio.ready.step2' | dm }} + </span> + </div> + <div class="flex items-center gap-3"> + <span + class="flex size-7 flex-none items-center justify-center rounded-lg bg-primary-50 text-primary"> + <i class="pi pi-pencil" aria-hidden="true"></i> + </span> + <span class="text-muted-color"> + {{ 'accessibility.studio.ready.step3' | dm }} + </span> + </div> + </div> + </div> + } + + <!-- Issue-type list SKELETON: same row shape/footprint as the results, so + the swap to real rows is a crossfade with no layout shift. --> + @if (store.phase() === 'scanning') { + <div + class="studio-fade-in flex animate-pulse flex-col gap-2" + data-testid="studio-issue-type-skeleton" + aria-hidden="true"> + @for (i of [1, 2, 3, 4]; track i) { + <div + class="flex items-start gap-3 rounded-xl border border-surface-200 px-4 py-3"> + <span + class="mt-1.5 size-2.5 flex-none rounded-full bg-surface-200"></span> + <div class="min-w-0 flex-1"> + <div class="h-3.5 w-28 rounded-sm bg-surface-200"></div> + <div + class="mt-1.5 h-3 w-full max-w-64 rounded-sm bg-surface-100"></div> + </div> + <span + class="size-7 flex-none rounded-lg bg-surface-100"></span> + </div> + } + </div> + } + + <!-- BY ISSUE TYPE list: one row per axe rule (scanned state). Crossfades in + over the skeleton — same footprint, so no jump. --> + @if (store.phase() === 'scanned') { + <div + class="studio-fade-in flex flex-col gap-2" + data-testid="studio-issue-type-list"> + @for (group of $issueTypeRows(); track group.code) { + <div + class="flex items-start gap-3 rounded-xl border border-surface-200 px-4 py-3" + data-testid="studio-issue-type-row"> + <span + class="mt-1.5 size-2.5 flex-none rounded-full" + [style.background-color]="group.color" + aria-hidden="true"></span> + <div class="min-w-0 flex-1"> + <div class="font-mono font-bold text-color"> + {{ group.code }} + </div> + <div class="mt-0.5 leading-snug text-muted-color"> + {{ group.message }} + </div> + </div> + <span + class="flex size-7 flex-none items-center justify-center rounded-lg bg-surface-100 font-bold text-muted-color"> + {{ group.count }} + </span> + </div> + } + </div> + } + + <!-- Live agent activity + final report timeline (shared, agent-agnostic). + The log appends the live "working" bubble (workingMessage) below the + settled steps while fixing. --> + <dot-agent-activity-log + [messages]="$activityMessages()" + [workingMessage]="$workingMessage()" + [working]="store.phase() === 'fixing'" + workingFallbackKey="accessibility.studio.fixing" + data-testid="studio-activity-log" /> + + <!-- Needs your review: axe `incomplete` items. axe flagged these but + couldn't be sure, so the agent doesn't touch them — a human decides. + Shown after a scan (scanned) and in the final report (done/published). --> + @if ( + (store.phase() === 'scanned' || store.finished()) && + store.reviewGroups().length > 0 + ) { + <div class="mt-4" data-testid="studio-review-section"> + <div class="mb-1 flex items-center gap-1.5"> + <i class="pi pi-eye text-muted-color" aria-hidden="true"></i> + <span + class="font-bold tracking-wider text-muted-color uppercase"> + {{ 'accessibility.studio.review.title' | dm }} + </span> + </div> + <p class="m-0 mb-2.5 leading-relaxed text-muted-color"> + {{ 'accessibility.studio.review.intro' | dm }} + </p> + <div class="flex flex-col gap-2"> + @for (group of $reviewRows(); track group.code) { + <div + class="rounded-xl border border-surface-200 px-4 py-3" + data-testid="studio-review-row"> + <div class="flex items-center gap-3"> + <div class="min-w-0 flex-1"> + <div + class="font-bold wrap-break-word text-color"> + {{ group.message }} + </div> + <div + class="font-mono wrap-break-word text-muted-color"> + {{ group.code }} + </div> + </div> + <span + class="flex size-7 flex-none items-center justify-center rounded-lg bg-surface-100 font-bold text-muted-color"> + {{ group.count }} + </span> + </div> + <div class="mt-1.5 leading-snug text-muted-color"> + {{ group.reasonKey | dm }} + </div> + </div> + } + </div> + </div> + } + </div> + + <!-- Scanner actions — phase-driven, inside the scanner panel so the controls + sit with the results they act on. --> + <div class="flex-none px-4"> + <!-- Footer copy — hidden in the ready + scanning states. --> + @if (store.phase() !== 'ready' && store.phase() !== 'scanning') { + <div class="mb-3.5 flex min-w-0 items-center gap-2.5"> + @if ($footerIcon(); as fi) { + <span + class="flex size-9 flex-none items-center justify-center rounded-lg" + [class]="fi.cls"> + <!-- text-base!: the icon font sets its own size + on the class in `fi.icon`, so a plain + utility loses to it. --> + <i + [class]="fi.icon" + class="text-base!" + aria-hidden="true"></i> + </span> + } + <div class="min-w-0"> + <div class="leading-snug font-bold text-color"> + {{ $footerKeys().titleKey | dm: $footerArgs() }} + </div> + <div class="mt-px leading-snug text-muted-color"> + {{ + $footerKeys().subKey + | dm: [store.selected()?.path ?? ''] + }} + </div> + </div> + </div> + } + + @switch (store.phase()) { + @case ('ready') { + <p-button + [label]="'accessibility.studio.action.scan' | dm" + styleClass="w-full" + (onClick)="runScan()" + data-testid="studio-scan-btn" /> + } + @case ('scanning') { + <p-button + [label]="'accessibility.studio.action.stopscan' | dm" + severity="secondary" + [outlined]="true" + styleClass="w-full" + (onClick)="stopScan()" + data-testid="studio-stopscan-btn" /> + } + @case ('scanned') { + <!-- Skip-CSS is a fix option (the agent reports CSS contrast instead + of fixing it), so it sits with the Fix action, not the scan. --> + <div class="mb-4 flex items-center justify-between"> + <label for="studio-skip-css" class="text-muted-color"> + {{ 'accessibility.studio.skipcss.label' | dm }} + </label> + <p-toggleswitch + inputId="studio-skip-css" + [ngModel]="store.skipCss()" + (ngModelChange)="onSkipCssChange($event)" + data-testid="studio-skipcss-toggle" /> + </div> + <div class="flex gap-2.5"> + <p-button + [label]="'accessibility.studio.action.rescan' | dm" + severity="secondary" + [outlined]="true" + (onClick)="runScan()" + data-testid="studio-rescan-btn" /> + <p-button + [label]="'accessibility.studio.action.fix' | dm" + styleClass="flex-1" + (onClick)="startFix()" + data-testid="studio-fix-btn" /> + </div> + } + @case ('fixing') { + <p-button + [label]="'accessibility.studio.action.stopagent' | dm" + severity="danger" + [outlined]="true" + styleClass="w-full" + (onClick)="stopAgent()" + data-testid="studio-stopagent-btn" /> + } + @case ('done') { + <!-- Discard + Publish live in the Files panel, with the files they + act on. Here we just send the user there. --> + <p-button + [label]="'accessibility.studio.action.reviewfiles' | dm" + styleClass="w-full" + (onClick)="openPanel('files')" + data-testid="studio-reviewfiles-btn" /> + } + @case ('published') { + <div class="flex gap-2.5"> + <p-button + [label]="'accessibility.studio.action.allpages' | dm" + severity="secondary" + [outlined]="true" + styleClass="flex-1" + (onClick)="backToPageList()" + data-testid="studio-allpages-btn" /> + </div> + } + } + </div> + </div> + </p-accordion-content> + </p-accordion-panel> + + <!-- ---------- PANEL 2: CHANGED FILES ---------- --> + <!-- Typically short: takes its content height, but its OWN content scrolls + once it passes 400px so a long file list never pushes the panel past + that or hijacks the scanner's space. flex-none = never grows. --> + <!-- border-t!/border-surface-200!: PrimeNG draws no divider between accordion + panels; this separates the files panel from the scanner above it. --> + <p-accordion-panel + value="files" + class="flex-none border-t! border-surface-200!" + data-testid="studio-panel-files"> + <p-accordion-header> + <span data-testid="studio-panel-files-title"> + {{ 'accessibility.studio.panel.files' | dm }} + </span> + </p-accordion-header> + <!-- p-0!: same reason as the scanner panel — the file list owns its own + padding so its rows can span the full width. --> + <p-accordion-content [pt]="{ content: { class: 'p-0! max-h-[400px]' } }"> + <div class="px-4 pb-4" data-testid="studio-panel-files-body"> + <dot-a11y-diff + [activeFileId]="$diffFile()?.identifier ?? null" + (fileSelected)="onDiffFileSelected($event)" + (changedCount)="onChangedFilesCount($event)" + data-testid="studio-diff-list" /> + + <!-- Discard + Publish sit together with the files they act on. + Publishing the page publishes its changed source files as a + unit; there is no per-file publishing. Shown whenever there + are changed files, regardless of phase — working changes can + predate this run (an earlier run, a manual edit), so they + must be publishable without scanning first. --> + @if ($hasChangedFiles()) { + <div + class="mt-3 flex gap-2.5 border-t border-surface-100 pt-3.5" + data-testid="studio-publish-bar"> + <p-button + [label]="'accessibility.studio.action.discard' | dm" + severity="secondary" + [outlined]="true" + (onClick)="discardChanges()" + data-testid="studio-discard-btn" /> + <p-button + [label]="'accessibility.studio.action.apply' | dm" + severity="success" + styleClass="flex-1" + (onClick)="applyChanges()" + data-testid="studio-apply-btn" /> + </div> + } + </div> + </p-accordion-content> + </p-accordion-panel> + </p-accordion> +</aside> + +<!-- ============ RIGHT COLUMN — Preview / file diff ============ --> +<!-- Shows the visual before/after, or the selected file's source diff when the user + picks one from the left panel's changed-files list. The preview stays mounted + (just hidden) so its iframes don't reload when returning from a diff. --> +<div class="row-start-2 flex min-h-0 flex-col bg-surface-100"> + <!-- The iframes stay mounted while a diff is open so returning to the preview + doesn't reload them. `display` is set inline rather than via a `hidden` + class: Tailwind's `hidden` and the `grid` class here have equal specificity, + so the class toggle wouldn't reliably win. --> + <main + class="grid min-h-0 flex-1 grid-cols-2 gap-4 p-5" + [style.display]="$diffFile() ? 'none' : null"> + <!-- AFTER — the working render carrying the agent's fixes --> + <section + class="flex min-h-0 flex-col overflow-hidden rounded-2xl border border-surface-300 bg-white shadow-lg"> + <div + class="flex h-10 flex-none items-center border-b border-surface-100 bg-surface-50 px-3.5"> + <!-- Single address bar: lock + URL, with the mode badge as a segment + on the right inside the same pill. --> + <div + class="mx-auto flex h-6.5 w-full max-w-[460px] items-center gap-1.5 overflow-hidden rounded-full border border-surface-200 bg-white pl-3"> + <!-- text-[11px]!: PrimeIcons sets font-size on `.pi`, which a plain + utility can't override at equal specificity. --> + <i class="pi pi-lock text-[11px]! text-muted-color" aria-hidden="true"></i> + <span class="min-w-0 flex-1 truncate text-xs text-muted-color"> + {{ store.selected()?.hostName }}{{ store.selected()?.path }} + </span> + <span + class="inline-flex flex-none items-center gap-1 self-stretch border-l border-surface-200 bg-surface-200 px-2.5 text-[11px] font-semibold text-muted-color" + data-testid="studio-preview-label"> + <i class="pi pi-sparkles text-[11px]!" aria-hidden="true"></i> + {{ 'accessibility.studio.preview.mode.preview' | dm }} + </span> + </div> + </div> + + <div class="min-h-0 flex-1 bg-white"> + @if ($previewUrl()) { + <iframe + #previewFrame + [src]="$previewUrl() | safeUrl" + (load)="onPreviewLoad()" + class="size-full border-0" + [title]="'accessibility.studio.preview.mode.preview' | dm" + data-testid="studio-preview-iframe"></iframe> + } + </div> + </section> + + <!-- BEFORE — the published page + markers --> + <section + class="flex min-h-0 flex-col overflow-hidden rounded-2xl border border-surface-300 bg-white shadow-lg"> + <div + class="flex h-10 flex-none items-center border-b border-surface-100 bg-surface-50 px-3.5"> + <!-- Single address bar: lock + URL, with the mode badge as a segment + on the right inside the same pill. --> + <div + class="mx-auto flex h-6.5 w-full max-w-[460px] items-center gap-1.5 overflow-hidden rounded-full border border-surface-200 bg-white pl-3"> + <!-- text-[11px]!: PrimeIcons sets font-size on `.pi`, which a plain + utility can't override at equal specificity. --> + <i class="pi pi-lock text-[11px]! text-muted-color" aria-hidden="true"></i> + <span class="min-w-0 flex-1 truncate text-xs text-muted-color"> + {{ store.selected()?.hostName }}{{ store.selected()?.path }} + </span> + <span + class="inline-flex flex-none items-center gap-1 self-stretch border-l border-surface-200 bg-surface-200 px-2.5 text-[11px] font-semibold text-muted-color" + data-testid="studio-live-label"> + <i class="pi pi-globe text-[11px]!" aria-hidden="true"></i> + {{ 'accessibility.studio.preview.mode.live' | dm }} + </span> + </div> + </div> + + <div class="min-h-0 flex-1 bg-white"> + @if ($liveUrl()) { + <iframe + #liveFrame + [src]="$liveUrl() | safeUrl" + (load)="onLiveLoad()" + class="size-full border-0" + [title]="'accessibility.studio.preview.mode.live' | dm" + data-testid="studio-live-iframe"></iframe> + } + </div> + </section> + </main> + + <!-- File diff — replaces the preview while a file is selected. Mounted only + then, so Monaco isn't built until the user asks for a diff. --> + @if ($diffFile(); as file) { + <dot-a11y-diff-viewer + class="min-h-0 flex-1" + [file]="file" + (closed)="closeDiff()" + data-testid="studio-diff-viewer" /> + } +</div> diff --git a/core-web/libs/portlets/dot-agents/src/lib/agents/a11y/a11y-run/a11y-run.component.spec.ts b/core-web/libs/portlets/dot-agents/src/lib/agents/a11y/a11y-run/a11y-run.component.spec.ts new file mode 100644 index 000000000000..2457b08f7982 --- /dev/null +++ b/core-web/libs/portlets/dot-agents/src/lib/agents/a11y/a11y-run/a11y-run.component.spec.ts @@ -0,0 +1,894 @@ +import { byTestId, createComponentFactory, mockProvider, Spectator } from '@openng/spectator/jest'; + +import { Location } from '@angular/common'; +import { Component, input, output } from '@angular/core'; +import { Router } from '@angular/router'; + +import { DotMessageService } from '@dotcms/data-access'; +import { AgentHeartbeat, AgentRunStep } from '@dotcms/dotcms-models'; +import { A11yGroup } from '@dotcms/portlets/dot-ema/ui'; +import { MockDotMessageService } from '@dotcms/utils-testing'; + +import { DotA11yRunComponent } from './a11y-run.component'; + +import { DotA11yDiffViewerComponent } from '../a11y-diff/a11y-diff-viewer.component'; +import { DotA11yDiffComponent } from '../a11y-diff/a11y-diff.component'; +import { A11Y_PAGE_LIST_ROUTE } from '../a11y.constants'; +import { + FixReport, + NEEDS_ATTENTION_STATUSES, + RESEARCH_RULE_ID, + StudioPageRow, + StudioPhase +} from '../models/accessibility-studio.models'; +import { MOCK_FIX_REPORT } from '../models/mock-fix-report'; +import { PageDiffFile } from '../models/page-render-sources.models'; +import { A11yMarkerService } from '../services/a11y-marker.service'; +import { A11yRunStore } from '../store/a11y-run.store'; + +/** + * Stubs for the diff pieces so the run spec doesn't pull in Monaco / HTTP. The list + * emits the picked file; the viewer just records what it was handed. + */ +@Component({ selector: 'dot-a11y-diff', standalone: true, template: '' }) +class DotA11yDiffStubComponent { + readonly activeFileId = input<string | null>(null); + readonly fileSelected = output<PageDiffFile | null>(); + readonly changedCount = output<number>(); +} + +@Component({ selector: 'dot-a11y-diff-viewer', standalone: true, template: '' }) +class DotA11yDiffViewerStubComponent { + readonly file = input<PageDiffFile | null>(null); + readonly closed = output<void>(); +} + +const DIFF_FILE: PageDiffFile = { + identifier: 'vtl-1', + path: '//demo/application/containers/awazon/a.vtl', + name: 'a.vtl', + extension: 'vtl', + origin: 'container', + working: 'new', + live: 'old', + added: 1, + removed: 1 +}; + +const MOCK_PAGE: StudioPageRow = { + identifier: 'id-1', + title: 'About Us', + path: '/about-us', + type: 'htmlpageasset', + languageId: 1, + hostId: 'host-id-1', + hostName: 'demo.dotcms.com', + modDate: '04/09/2026', + modUserName: 'Admin User', + live: true +}; + +describe('DotA11yRunComponent', () => { + let spectator: Spectator<DotA11yRunComponent>; + + const runScan = jest.fn(); + const stopScan = jest.fn(); + const startFix = jest.fn(); + const stopAgent = jest.fn(); + const publish = jest.fn(); + const discard = jest.fn(); + const setSkipCss = jest.fn(); + const openSelectedPage = jest.fn(); + const navigate = jest.fn().mockResolvedValue(true); + + // Mutable per-test state read by the store mock's reactive getters. + let phase: StudioPhase = 'ready'; + let report: FixReport | null = null; + let steps: AgentRunStep[] = []; + let runError: string | null = null; + let heartbeat: AgentHeartbeat | null = null; + // Bumped when the working render changes → preview iframe cache-buster. + let previewRevision = 0; + // Whether a scan result is present (drives report vs. iframe in the pane). + let hasScan = false; + // The page path (as URL segments) the run component reads on init. + /** The row the page list "handed over" via router state for the current test. */ + let handoverRow: StudioPageRow | null = null; + // The current site — the run rehydrate effect waits for it before fetching. + + // Two error groups (5 elements) + one warning group (2 elements). + /** + * The LIVE frame's findings, deliberately DIFFERENT from the preview's. The two frames + * run separate scans, so a test that fed both the same array could not tell a correct + * pairing from an inverted one. + */ + const MOCK_LIVE_GROUPS: A11yGroup[] = [ + { + code: 'link-name', + type: 'error', + message: 'Links must have discernible text', + impact: 'serious', + helpUrl: 'https://example.com/link-name', + items: [{ context: '<a>', selector: 'a.live-only' }], + count: 1 + } + ]; + + const MOCK_GROUPS: A11yGroup[] = [ + { + code: 'image-alt', + type: 'error', + message: 'Images must have alternate text', + impact: 'critical', + helpUrl: 'https://example.com/image-alt', + items: [ + { context: '<img>', selector: 'img.a' }, + { context: '<img>', selector: 'img.b' }, + { context: '<img>', selector: 'img.c' } + ], + count: 3 + }, + { + code: 'button-name', + type: 'error', + message: 'Buttons must have discernible text', + impact: 'serious', + helpUrl: 'https://example.com/button-name', + items: [ + { context: '<button>', selector: 'button.x' }, + { context: '<button>', selector: 'button.y' } + ], + count: 2 + }, + { + code: 'color-contrast', + type: 'warning', + message: 'Elements must have sufficient color contrast', + impact: 'moderate', + helpUrl: 'https://example.com/color-contrast', + items: [{ context: '<a>', selector: 'a.l1' }], + count: 1 + } + ]; + + const storeMock = { + phase: () => phase, + report: () => report, + steps: () => steps, + runError: () => runError, + latestStep: () => (steps.length ? steps[steps.length - 1] : null), + heartbeat: () => heartbeat, + selected: () => MOCK_PAGE, + skipCss: () => false, + scanResult: () => (hasScan ? ({ standard: 'WCAG2AA' } as unknown) : null), + liveScanResult: () => (hasScan ? ({ standard: 'WCAG2AA' } as unknown) : null), + a11yGroups: () => (hasScan ? MOCK_GROUPS : []), + liveA11yGroups: () => (hasScan ? MOCK_LIVE_GROUPS : []), + errorCount: () => (hasScan ? 5 : 0), + warningCount: () => (hasScan ? 2 : 0), + isWorking: () => phase === 'scanning' || phase === 'fixing', + finished: () => ['done', 'published'].includes(phase), + runStarted: () => ['fixing', 'done', 'published'].includes(phase), + hasResults: () => ['scanned', 'fixing', 'done', 'published'].includes(phase), + beforeCount: () => (hasScan ? 5 : 0), + afterCount: () => report?.scan.after.violations ?? 0, + openCount: () => report?.scan.after.violations ?? (hasScan ? 5 : 0), + // 3 critical (image-alt) + 2 serious (button-name) + 0 moderate/minor errors. + severityCounts: () => ({ + critical: hasScan ? 3 : 0, + serious: hasScan ? 2 : 0, + moderate: 0, + minor: 0 + }), + issueTypeRows: () => (hasScan ? MOCK_GROUPS.filter((g) => g.type === 'error') : []), + reviewGroups: () => (hasScan ? MOCK_GROUPS.filter((g) => g.type === 'warning') : []), + fixedResults: () => + report?.results.filter( + (r) => r.status === 'fixed-to-working' && r.ruleId !== RESEARCH_RULE_ID + ) ?? [], + reportedResults: () => + report?.results.filter((r) => NEEDS_ATTENTION_STATUSES.includes(r.status)) ?? [], + // Both counts come from the before/after rescan, not from row statuses — the + // rows only log the deterministic pass. See the real store's computeds. + fixedCount: () => + report ? Math.max(0, report.scan.before.violations - report.scan.after.violations) : 0, + reportedCount: () => report?.scan.after.violations ?? 0, + previewRevision: () => previewRevision, + runScan, + stopScan, + startFix, + stopAgent, + publish, + discard, + setSkipCss, + openSelectedPage + }; + + const createComponent = createComponentFactory({ + component: DotA11yRunComponent, + overrideComponents: [ + [ + DotA11yRunComponent, + { + remove: { + imports: [DotA11yDiffComponent, DotA11yDiffViewerComponent] + }, + add: { + imports: [DotA11yDiffStubComponent, DotA11yDiffViewerStubComponent] + } + } + ] + ], + componentProviders: [ + { provide: A11yRunStore, useValue: storeMock }, + mockProvider(A11yMarkerService) + ], + providers: [ + { + provide: DotMessageService, + useValue: new MockDotMessageService({ + 'accessibility.studio.working.thinking': 'Thinking…', + 'accessibility.studio.working.analyzing': 'Analyzing the page…', + 'accessibility.studio.working.reasoning': 'Working through the fix…', + 'accessibility.studio.working.stillworking': 'Still working on it…', + 'accessibility.studio.working.elapsed': '{0}s' + }) + }, + { provide: Router, useValue: { navigate } }, + { + provide: Location, + // useFactory so each test's `handoverRow` is read at injection time. + useFactory: () => ({ getState: () => (handoverRow ? { row: handoverRow } : null) }) + } + ] + }); + + function render( + nextPhase: StudioPhase, + nextReport: FixReport | null = null, + nextSteps: AgentRunStep[] = [], + nextRunError: string | null = null + ) { + phase = nextPhase; + report = nextReport; + steps = nextSteps; + runError = nextRunError; + // A scan result exists once the page has been scanned. + hasScan = ['scanned', 'fixing', 'done', 'published'].includes(nextPhase); + spectator = createComponent(); + spectator.detectChanges(); + } + + beforeEach(() => { + jest.clearAllMocks(); + phase = 'ready'; + report = null; + steps = []; + runError = null; + heartbeat = null; + previewRevision = 0; + hasScan = false; + handoverRow = MOCK_PAGE; + // Report reduced-motion so the score count-up snaps to its final value + // synchronously (no requestAnimationFrame timing in the DOM assertions). + window.matchMedia = jest + .fn() + .mockReturnValue({ matches: true }) as unknown as typeof matchMedia; + }); + + describe('side panel accordion', () => { + beforeEach(() => render('ready')); + + /** Click a panel's PrimeNG accordion header. */ + const clickHeader = (panel: 'scanner' | 'files') => { + const header = spectator + .query(byTestId(`studio-panel-${panel}`)) + ?.querySelector('p-accordion-header') as HTMLElement; + spectator.click(header); + spectator.detectChanges(); + }; + + it('opens with the scanner panel expanded and files collapsed', () => { + expect(spectator.component.isPanelOpen('scanner')).toBe(true); + expect(spectator.component.isPanelOpen('files')).toBe(false); + // p-accordion-content keeps content mounted (hideStrategy="visibility"), + // so the files list keeps resolving the delta while collapsed. + expect(spectator.query(byTestId('studio-panel-files-body'))).toBeTruthy(); + }); + + it('both panels can be open at once', () => { + clickHeader('files'); + + expect(spectator.component.isPanelOpen('files')).toBe(true); + // Opening files must NOT collapse the scanner. + expect(spectator.component.isPanelOpen('scanner')).toBe(true); + expect(spectator.component.$openPanels()).toEqual(['scanner', 'files']); + }); + + it('each panel collapses independently', () => { + clickHeader('files'); + clickHeader('scanner'); + + expect(spectator.component.isPanelOpen('scanner')).toBe(false); + expect(spectator.component.isPanelOpen('files')).toBe(true); + }); + + it('both panels can be closed at once', () => { + clickHeader('scanner'); + + expect(spectator.component.isPanelOpen('scanner')).toBe(false); + expect(spectator.component.isPanelOpen('files')).toBe(false); + expect(spectator.component.$openPanels()).toEqual([]); + }); + + it('tracks the changed-file count reported by the diff list', () => { + expect(spectator.component.$changedFileCount()).toBe(0); + expect(spectator.component.$hasChangedFiles()).toBe(false); + + const list = spectator.query(DotA11yDiffStubComponent) as DotA11yDiffStubComponent; + list.changedCount.emit(2); + spectator.detectChanges(); + + expect(spectator.component.$changedFileCount()).toBe(2); + expect(spectator.component.$hasChangedFiles()).toBe(true); + }); + }); + + describe('files panel actions', () => { + beforeEach(() => render('done', MOCK_FIX_REPORT)); + + /** Report N changed files from the stubbed list. */ + const reportFiles = (n: number) => { + const list = spectator.query(DotA11yDiffStubComponent) as DotA11yDiffStubComponent; + list.changedCount.emit(n); + spectator.detectChanges(); + }; + + it('shows no action bar until there are files to publish', () => { + expect(spectator.query(byTestId('studio-publish-bar'))).toBeFalsy(); + }); + + it('shows Discard next to Publish once files changed', () => { + reportFiles(2); + + expect(spectator.query(byTestId('studio-publish-bar'))).toBeTruthy(); + expect(spectator.query(byTestId('studio-discard-btn'))).toBeTruthy(); + expect(spectator.query(byTestId('studio-apply-btn'))).toBeTruthy(); + }); + + it('Publish publishes the page', () => { + reportFiles(1); + spectator.click( + spectator + .query(byTestId('studio-apply-btn')) + ?.querySelector('button') as HTMLElement + ); + expect(publish).toHaveBeenCalled(); + }); + + it('Discard drops the working fixes', () => { + reportFiles(1); + spectator.click( + spectator + .query(byTestId('studio-discard-btn')) + ?.querySelector('button') as HTMLElement + ); + expect(discard).toHaveBeenCalled(); + }); + + // The changed files may predate this run (an earlier run, a manual edit), so + // the actions are gated on the files existing — not on the run's phase. + it.each(['ready', 'scanned', 'published'] as StudioPhase[])( + 'shows both actions in the %s phase when files changed', + (studioPhase) => { + render(studioPhase, studioPhase === 'ready' ? null : MOCK_FIX_REPORT); + reportFiles(2); + + expect(spectator.query(byTestId('studio-discard-btn'))).toBeTruthy(); + expect(spectator.query(byTestId('studio-apply-btn'))).toBeTruthy(); + } + ); + }); + + describe('ready phase', () => { + beforeEach(() => render('ready')); + + it('shows the scan button', () => { + expect(spectator.query(byTestId('studio-scan-btn'))).toBeTruthy(); + }); + + it('does not show the skip-css toggle yet (it is a fix option)', () => { + expect(spectator.query(byTestId('studio-skipcss-toggle'))).toBeFalsy(); + }); + + it('hides the score widget in the ready state (before scanning)', () => { + expect(spectator.query(byTestId('studio-score-ring'))).toBeFalsy(); + expect(spectator.query(byTestId('studio-score-count'))).toBeFalsy(); + }); + + it('triggers runScan on click', () => { + const btn = spectator.query(byTestId('studio-scan-btn'))?.querySelector('button'); + spectator.click(btn as HTMLElement); + expect(runScan).toHaveBeenCalled(); + }); + + it('shows the changed-files list before a run completes', () => { + // The list resolves the working-vs-live delta itself, so it's present + // (and loading) from the moment the page opens — no scan required. + expect(spectator.query(DotA11yDiffStubComponent)).toBeTruthy(); + }); + + it('shows the preview, not a diff, until a file is picked', () => { + expect(spectator.component.$diffFile()).toBeNull(); + expect(spectator.query(DotA11yDiffViewerStubComponent)).toBeFalsy(); + }); + }); + + describe('scanned phase', () => { + beforeEach(() => render('scanned', MOCK_FIX_REPORT)); + + it('shows the fix button', () => { + expect(spectator.query(byTestId('studio-fix-btn'))).toBeTruthy(); + }); + + it('shows the skip-css toggle (a fix option, offered before Fix)', () => { + expect(spectator.query(byTestId('studio-skipcss-toggle'))).toBeTruthy(); + }); + + it('shows the real open-count in the ring', () => { + expect(spectator.query(byTestId('studio-score-count'))).toHaveText('5'); + }); + + it('animates the score count up to the open-count (snaps under reduced motion)', () => { + // reduced-motion is mocked on, so displayCount snaps to the target. + expect(spectator.component.$displayCount()).toBe(5); + }); + + it('crossfades the real issue-type list in (over the skeleton)', () => { + expect(spectator.query(byTestId('studio-issue-type-list'))).toHaveClass( + 'studio-fade-in' + ); + // The skeleton is only shown while scanning, not in the scanned state. + expect(spectator.query(byTestId('studio-issue-type-skeleton'))).toBeFalsy(); + }); + + it('keeps the preview iframe visible after scanning', () => { + expect(spectator.query(byTestId('studio-preview-iframe'))).toBeTruthy(); + }); + + it('triggers startFix on click', () => { + const btn = spectator.query(byTestId('studio-fix-btn'))?.querySelector('button'); + spectator.click(btn as HTMLElement); + expect(startFix).toHaveBeenCalled(); + }); + + it('renders the BY ISSUE TYPE list — one row per error rule', () => { + // MOCK_GROUPS has 2 error groups (image-alt, button-name) + 1 warning. + expect(spectator.queryAll(byTestId('studio-issue-type-row')).length).toBe(2); + }); + + it('renders the severity legend (non-empty buckets)', () => { + const legend = spectator.query(byTestId('studio-severity-legend')); + expect(legend).toBeTruthy(); + // critical + serious have counts; moderate/minor are 0 → hidden when scanned. + expect(legend).toHaveText('Critical'); + expect(legend).toHaveText('Serious'); + }); + + it('shows the re-scan icon button', () => { + expect(spectator.query(byTestId('studio-rescan-btn'))).toBeTruthy(); + }); + + it('surfaces needs-review items separately (not in the fix count)', () => { + // mock warningCount = 2 → the note renders (the mock message service returns + // the key verbatim); the donut count stays 5 (confirmed errors only). + expect(spectator.query(byTestId('studio-needsreview-note'))).toBeTruthy(); + expect(spectator.query(byTestId('studio-score-count'))).toHaveText('5'); + }); + + it('renders the needs-review section with a row per incomplete rule', () => { + // MOCK_GROUPS has 1 warning group (color-contrast). + expect(spectator.query(byTestId('studio-review-section'))).toBeTruthy(); + expect(spectator.queryAll(byTestId('studio-review-row')).length).toBe(1); + }); + }); + + describe('marker visibility (showMarkers)', () => { + // Markers only ever go on the LIVE frame, which always still carries the + // original scan's violations. The only gate is: a scan has produced findings. + it('is off before a scan', () => { + render('ready'); + expect(spectator.component.$showMarkers()).toBe(false); + }); + + it('is on once scanned (pre-fix)', () => { + render('scanned', MOCK_FIX_REPORT); + expect(spectator.component.$showMarkers()).toBe(true); + }); + + it('stays on after fixes exist (done) — the LIVE frame is still unfixed', () => { + render('done', MOCK_FIX_REPORT); + expect(spectator.component.$showMarkers()).toBe(true); + }); + + describe('what actually reaches the marker service', () => { + // The computed above is only the input. The behaviour is the effect calling + // render() with `show ? groups : []` for EACH frame, and the service was mocked + // and never asserted — so an inverted ternary, or the preview groups sent to the + // live frame, passed silently. + function renderCalls() { + const marker = spectator.inject(A11yMarkerService, true); + + return (marker.render as jest.Mock).mock.calls; + } + + it('draws each frame with its OWN scan findings', () => { + render('scanned', MOCK_FIX_REPORT); + + const groupsByFrame = new Map( + renderCalls().map(([frame, groups]) => [frame, groups]) + ); + const preview = spectator.query('[data-testid="studio-preview-iframe"]'); + const live = spectator.query('[data-testid="studio-live-iframe"]'); + + expect(groupsByFrame.get(preview)).toEqual(MOCK_GROUPS); + expect(groupsByFrame.get(live)).toEqual(MOCK_LIVE_GROUPS); + }); + + it('clears BOTH frames rather than skipping the call when markers are off', () => { + // Passing [] is what erases a previously drawn layer; not calling at all + // would leave stale markers on screen. + render('ready'); + + const calls = renderCalls(); + expect(calls.length).toBeGreaterThanOrEqual(2); + for (const [, groups] of calls) { + expect(groups).toEqual([]); + } + }); + }); + }); + + describe('scanning phase', () => { + beforeEach(() => render('scanning')); + + it('renders the results skeleton (score + issue list) with the results footprint', () => { + expect(spectator.query(byTestId('studio-score-skeleton'))).toBeTruthy(); + expect(spectator.query(byTestId('studio-issue-type-skeleton'))).toBeTruthy(); + }); + + it('shows the Stop scan button and triggers stopScan', () => { + const btn = spectator.query(byTestId('studio-stopscan-btn'))?.querySelector('button'); + expect(btn).toBeTruthy(); + spectator.click(btn as HTMLElement); + expect(stopScan).toHaveBeenCalled(); + }); + + it('shows the skeleton, not the real widget or issue list, while scanning', () => { + expect(spectator.query(byTestId('studio-issue-type-list'))).toBeFalsy(); + expect(spectator.query(byTestId('studio-score-ring'))).toBeFalsy(); + }); + + // Guards the `phase() !== 'ready'` negations: written as `!phase() === 'ready'` + // they'd parse as `(!phase()) === 'ready'` — always false — silently hiding the + // section header and showing the ready-state explainer mid-scan. + it('shows the section header and hides the ready explainer once scanning', () => { + expect(spectator.query(byTestId('studio-working-badge'))).toBeTruthy(); + expect(spectator.query(byTestId('studio-ready-card'))).toBeFalsy(); + }); + }); + + describe('fixing phase (live stream)', () => { + const LIVE_STEPS: AgentRunStep[] = [ + { message: 'Scanning live + working baseline', meta: { phase: 'scan' } }, + { message: 'Fixing color-contrast → .btn', meta: { phase: 'fix' } }, + // Leading "Agent:" role label the model sometimes prepends — the + // presenter strips it so the log/banner show just the action. + { message: 'Agent: reading activity.vtl', meta: { phase: 'read' } } + ]; + + beforeEach(() => render('fixing', null, LIVE_STEPS)); + + it('shows the Stop agent button', () => { + expect(spectator.query(byTestId('studio-stopagent-btn'))).toBeTruthy(); + }); + + it('triggers stopAgent on click', () => { + const btn = spectator.query(byTestId('studio-stopagent-btn'))?.querySelector('button'); + spectator.click(btn as HTMLElement); + expect(stopAgent).toHaveBeenCalled(); + }); + + it('renders one settled bubble per streamed step, plus a separate thinking item', () => { + // 3 streamed steps as settled message bubbles… + expect(spectator.queryAll(byTestId('agent-message')).length).toBe(3); + // …and the live state is its own thinking component, not a 4th message. + expect(spectator.query(byTestId('agent-thinking'))).not.toBeNull(); + }); + + it('shows generic thinking copy — never the last step text', () => { + const thinking = spectator.query(byTestId('agent-thinking')); + expect(thinking).not.toBeNull(); + // Always generic loading copy; must NOT echo the latest step. + expect(thinking).not.toHaveText('reading activity.vtl'); + // No heartbeat yet → first cycling phrase. + expect(thinking).toHaveText('Thinking…'); + }); + + it('shows the elapsed seconds sub-line from the heartbeat', () => { + heartbeat = { elapsedMs: 20000, sinceLastEventMs: 12000 }; + render('fixing', null, LIVE_STEPS); + const thinking = spectator.query(byTestId('agent-thinking')); + // Still generic copy, never the step text. + expect(thinking).not.toHaveText('reading activity.vtl'); + // Elapsed seconds on the current action ride along as the sub-line. + expect(thinking).toHaveText('12s'); + }); + + it('keeps cycling reassurance copy on a very long step (loops, never freezes)', () => { + // 5-minute step: index wraps (300000/5000 % 4 = 0 → "Thinking…"), so the + // copy keeps moving rather than sticking on a "nearly done" phrase. + heartbeat = { elapsedMs: 305000, sinceLastEventMs: 300000 }; + render('fixing', null, LIVE_STEPS); + const thinking = spectator.query(byTestId('agent-thinking')); + expect(thinking).toHaveText('Thinking…'); + expect(thinking).toHaveText('300s'); + }); + }); + + describe('run error state', () => { + beforeEach(() => render('scanned', MOCK_FIX_REPORT, [], 'render unreliable')); + + it('surfaces the error in the banner at the top of the portlet', () => { + const error = spectator.query(byTestId('studio-run-error')); + expect(error).toHaveText('render unreliable'); + }); + + it('renders no banner when there is no error', () => { + render('scanned', MOCK_FIX_REPORT); + expect(spectator.query(byTestId('studio-run-error'))).toBeFalsy(); + }); + }); + + describe('done phase', () => { + beforeEach(() => render('done', MOCK_FIX_REPORT)); + + it('offers a jump to the files panel — discard/publish live there', () => { + expect(spectator.query(byTestId('studio-reviewfiles-btn'))).toBeTruthy(); + // Both actions belong to the files panel, and it has no files yet. + expect(spectator.query(byTestId('studio-discard-btn'))).toBeFalsy(); + expect(spectator.query(byTestId('studio-apply-btn'))).toBeFalsy(); + }); + + it('Review files opens the files panel, leaving the scanner open', () => { + const btn = spectator + .query(byTestId('studio-reviewfiles-btn')) + ?.querySelector('button'); + spectator.click(btn as HTMLElement); + spectator.detectChanges(); + + expect(spectator.component.isPanelOpen('files')).toBe(true); + expect(spectator.component.isPanelOpen('scanner')).toBe(true); + }); + + it('Review files is idempotent — it opens rather than toggles', () => { + const btn = () => + spectator.query(byTestId('studio-reviewfiles-btn'))?.querySelector('button'); + spectator.click(btn() as HTMLElement); + spectator.detectChanges(); + spectator.click(btn() as HTMLElement); + spectator.detectChanges(); + + // A second press must not close the panel it just opened. + expect(spectator.component.isPanelOpen('files')).toBe(true); + }); + + it('renders an activity step per result plus scan/locate/rescan framing', () => { + // 7 fixed + 2 skipped + 3 framing steps (scan, locate, rescan). The mock's 3 + // `reported` rows are deferrals to the agentic pass, not unresolved work, so + // they get no bubble. + expect(spectator.queryAll(byTestId('agent-message')).length).toBe(12); + }); + + it('shows the after-count in the ring', () => { + expect(spectator.query(byTestId('studio-score-count'))).toHaveText('5'); + }); + + it('still shows the needs-review section in the report', () => { + expect(spectator.query(byTestId('studio-review-section'))).toBeTruthy(); + }); + + it('picking a file in the list opens its diff in the right pane', () => { + const list = spectator.query(DotA11yDiffStubComponent) as DotA11yDiffStubComponent; + list.fileSelected.emit(DIFF_FILE); + spectator.detectChanges(); + + expect(spectator.component.$diffFile()).toEqual(DIFF_FILE); + expect(spectator.query(DotA11yDiffViewerStubComponent)?.file()).toEqual(DIFF_FILE); + // Opening a diff is a view swap, not a navigation — run state is kept. + expect(navigate).not.toHaveBeenCalled(); + }); + + it("the viewer's close action returns to the preview and clears the list", () => { + const list = spectator.query(DotA11yDiffStubComponent) as DotA11yDiffStubComponent; + list.fileSelected.emit(DIFF_FILE); + spectator.detectChanges(); + // The list's highlighted row tracks the pane via activeFileId. + expect(list.activeFileId()).toBe(DIFF_FILE.identifier); + + const viewer = spectator.query( + DotA11yDiffViewerStubComponent + ) as DotA11yDiffViewerStubComponent; + viewer.closed.emit(); + spectator.detectChanges(); + + expect(spectator.component.$diffFile()).toBeNull(); + expect(spectator.query(DotA11yDiffViewerStubComponent)).toBeFalsy(); + expect(list.activeFileId()).toBeNull(); + }); + + it('the list can also clear the selection itself (back to preview)', () => { + const list = spectator.query(DotA11yDiffStubComponent) as DotA11yDiffStubComponent; + list.fileSelected.emit(DIFF_FILE); + spectator.detectChanges(); + expect(spectator.component.$diffFile()).toEqual(DIFF_FILE); + + list.fileSelected.emit(null); + spectator.detectChanges(); + expect(spectator.component.$diffFile()).toBeNull(); + }); + }); + + describe('published phase', () => { + beforeEach(() => render('published', MOCK_FIX_REPORT)); + + it('shows the all-pages button', () => { + expect(spectator.query(byTestId('studio-allpages-btn'))).toBeTruthy(); + }); + }); + + describe('preview pane (side-by-side diff)', () => { + beforeEach(() => render('ready')); + + it('renders the PREVIEW (with-fixes) iframe on a /dot-page PREVIEW_MODE URL', () => { + const iframe = spectator.query(byTestId('studio-preview-iframe')); + expect(iframe).toBeTruthy(); + expect(iframe?.getAttribute('src')).toContain('/dot-page/about-us'); + expect(iframe?.getAttribute('src')).toContain('host_id=host-id-1'); + expect(iframe?.getAttribute('src')).toContain('mode=PREVIEW_MODE'); + }); + + it('renders the LIVE (published) iframe on a /dot-page LIVE URL', () => { + const iframe = spectator.query(byTestId('studio-live-iframe')); + expect(iframe).toBeTruthy(); + expect(iframe?.getAttribute('src')).toContain('/dot-page/about-us'); + expect(iframe?.getAttribute('src')).toContain('host_id=host-id-1'); + expect(iframe?.getAttribute('src')).toContain('mode=LIVE'); + expect(iframe?.getAttribute('src')).not.toContain('PREVIEW_MODE'); + }); + + it('shows both frames at once with their before/after labels', () => { + expect(spectator.query(byTestId('studio-live-label'))).toBeTruthy(); + expect(spectator.query(byTestId('studio-preview-label'))).toBeTruthy(); + // No dropdown anymore — the two versions are shown simultaneously. + expect(spectator.query(byTestId('studio-preview-mode-select'))).toBeFalsy(); + }); + + it('reloads the PREVIEW iframe when previewRevision advances (cache-buster)', () => { + // Revision 0 → no cache-buster. + expect( + spectator.query(byTestId('studio-preview-iframe'))?.getAttribute('src') + ).not.toContain('rev='); + + // A fix landing bumps the revision → src carries rev → iframe reloads. + previewRevision = 3; + render('fixing', null, [{ message: 'working', meta: { phase: 'fix' } }]); + expect( + spectator.query(byTestId('studio-preview-iframe'))?.getAttribute('src') + ).toContain('rev=3'); + // LIVE never reloads mid-run (published render is fixed). + expect( + spectator.query(byTestId('studio-live-iframe'))?.getAttribute('src') + ).not.toContain('rev='); + }); + }); + + describe('scroll sync', () => { + // jsdom iframes don't lay out or scroll, so drive the same-origin + // contentWindows directly and assert the mirror direction + guard. + beforeEach(() => render('ready')); + + function fakeFrame(scrollX: number, scrollY: number) { + const listeners: Array<() => void> = []; + const win = { + scrollX, + scrollY, + addEventListener: (_evt: string, cb: () => void) => listeners.push(cb), + scrollTo: jest.fn((x: number, y: number) => { + win.scrollX = x; + win.scrollY = y; + }) + }; + return { + emitScroll: () => listeners.forEach((cb) => cb()), + nativeElement: { contentWindow: win }, + win + }; + } + + it('mirrors the live frame scroll onto the preview frame', () => { + const live = fakeFrame(0, 0); + const preview = fakeFrame(0, 0); + jest.spyOn(spectator.component as never, '$liveFrame').mockReturnValue(live); + jest.spyOn(spectator.component as never, '$previewFrame').mockReturnValue(preview); + + spectator.component.onLiveLoad(); + live.win.scrollX = 40; + live.win.scrollY = 120; + live.emitScroll(); + + expect(preview.win.scrollTo).toHaveBeenCalledWith(40, 120); + }); + + it('does not bounce back (re-entrancy guard)', () => { + const live = fakeFrame(0, 0); + const preview = fakeFrame(0, 0); + jest.spyOn(spectator.component as never, '$liveFrame').mockReturnValue(live); + jest.spyOn(spectator.component as never, '$previewFrame').mockReturnValue(preview); + + // Wire BOTH directions, then scroll live once. + spectator.component.onLiveLoad(); + spectator.component.onPreviewLoad(); + live.win.scrollY = 200; + live.emitScroll(); + + // preview mirrored live once; the echoed preview-scroll must NOT scroll + // live back while the guard is set. + expect(preview.win.scrollTo).toHaveBeenCalledTimes(1); + preview.emitScroll(); + expect(live.win.scrollTo).not.toHaveBeenCalled(); + }); + }); + + it('navigates up to the page list from the back button', () => { + render('ready'); + const btn = spectator.query(byTestId('studio-back-btn'))?.querySelector('button'); + spectator.click(btn as HTMLElement); + // No store reset — the per-route run store is destroyed on navigation. + // Absolute, not relative: a `..` from the multi-segment `**` run route lands on + // `**` again and leaves the user on the same screen. + expect(navigate).toHaveBeenCalledWith([A11Y_PAGE_LIST_ROUTE]); + }); + + describe('page handover from the page list', () => { + it('adopts the row handed over in the navigation state on init', () => { + handoverRow = MOCK_PAGE; + render('ready'); + expect(openSelectedPage).toHaveBeenCalledWith(MOCK_PAGE); + }); + + it('bounces back to the page list when no row was handed over (cold load)', () => { + handoverRow = null; + render('ready'); + expect(openSelectedPage).not.toHaveBeenCalled(); + expect(navigate).toHaveBeenCalledWith([A11Y_PAGE_LIST_ROUTE]); + }); + + it.each([ + ['a row missing its identifier', { hostId: 'h', languageId: 1 }], + ['a row missing its hostId', { identifier: 'id-1', languageId: 1 }], + ['a row missing its languageId', { identifier: 'id-1', hostId: 'h' }], + ['a row with an empty identifier', { identifier: '', hostId: 'h', languageId: 1 }], + ['a non-object row', 'not-a-row'], + ['a languageId of the wrong type', { identifier: 'id-1', hostId: 'h', languageId: '1' }] + ])('bounces rather than adopting %s', (_label, row) => { + // `history.state` survives a reload and anything can write to it, so the three + // fields the run screen cannot work without are validated. A partial row would + // scan the wrong page, or none, with no error to explain it. + handoverRow = row as never; + render('ready'); + expect(openSelectedPage).not.toHaveBeenCalled(); + expect(navigate).toHaveBeenCalledWith([A11Y_PAGE_LIST_ROUTE]); + }); + }); +}); diff --git a/core-web/libs/portlets/dot-agents/src/lib/agents/a11y/a11y-run/a11y-run.component.ts b/core-web/libs/portlets/dot-agents/src/lib/agents/a11y/a11y-run/a11y-run.component.ts new file mode 100644 index 000000000000..ed0f7ac2ff7a --- /dev/null +++ b/core-web/libs/portlets/dot-agents/src/lib/agents/a11y/a11y-run/a11y-run.component.ts @@ -0,0 +1,808 @@ +import { Location } from '@angular/common'; +import { + ChangeDetectionStrategy, + Component, + computed, + DestroyRef, + effect, + ElementRef, + inject, + isDevMode, + signal, + untracked, + viewChild +} from '@angular/core'; +import { FormsModule } from '@angular/forms'; +import { Router } from '@angular/router'; + +import { AccordionModule } from 'primeng/accordion'; +import { ButtonModule } from 'primeng/button'; +import { ChartModule } from 'primeng/chart'; +import { ToggleSwitchModule } from 'primeng/toggleswitch'; +import { TooltipModule } from 'primeng/tooltip'; + +import { AgentMessage, DotAgentActivityLogComponent } from '@dotcms/ai-ui'; +import { DotAgentRunService, DotMessageService } from '@dotcms/data-access'; +import { DotPageScannerService } from '@dotcms/portlets/dot-ema/ui'; +import { DotMessagePipe, SafeUrlPipe } from '@dotcms/ui'; + +import { DotA11yDiffViewerComponent } from '../a11y-diff/a11y-diff-viewer.component'; +import { DotA11yDiffComponent } from '../a11y-diff/a11y-diff.component'; +import { A11Y_PAGE_LIST_ROUTE } from '../a11y.constants'; +import { A11yAgentPresenter } from '../models/a11y-agent.presenter'; +import { + impactToSeverity, + SEVERITY_COLOR, + SEVERITY_LABEL, + SEVERITY_ORDER, + type Severity +} from '../models/a11y-severity'; +import { StudioPageRow } from '../models/accessibility-studio.models'; +import { PageDiffFile } from '../models/page-render-sources.models'; +import { A11yMarkerService } from '../services/a11y-marker.service'; +import { DotA11yAgentService } from '../services/dot-a11y-agent.service'; +import { A11yRunStore } from '../store/a11y-run.store'; + +/** The side panel's two accordion panels. */ +type StudioPanel = 'scanner' | 'files'; + +/** A severity legend / breakdown row beside the donut. */ +interface SeverityRow { + severity: Severity; + label: string; + color: string; + count: number; +} + +/** + * The Studio run screen: the agent column (score widget + recipe log + + * state-driven action footer) beside a live preview pane. + */ +@Component({ + selector: 'dot-a11y-run', + imports: [ + FormsModule, + AccordionModule, + ButtonModule, + ChartModule, + ToggleSwitchModule, + TooltipModule, + DotMessagePipe, + SafeUrlPipe, + DotAgentActivityLogComponent, + DotA11yDiffComponent, + DotA11yDiffViewerComponent + ], + templateUrl: './a11y-run.component.html', + styles: [ + ` + /* Scanning shows a skeleton with the SAME footprint as the results, so + swapping to the real data is a pure crossfade — no layout shift. Both + the skeleton and the results just fade in. */ + @keyframes studio-fade-in { + from { + opacity: 0; + } + to { + opacity: 1; + } + } + + .studio-fade-in { + animation: studio-fade-in 0.25s ease-out both; + } + + @media (prefers-reduced-motion: reduce) { + .studio-fade-in { + animation: none; + } + } + + /* Make the OPEN scanner panel's content scroll in place (headers stay put) + instead of the whole accordion scrolling. PrimeNG's collapse animation + wraps the content in a grid + <p-motion> + wrapper that its [pt] API + can't reach; those need min-height:0 so the innermost content — which + carries overflow-y:auto via [pt] — can be bounded and scroll. + The wrapper also gets overflow:hidden: the inner content's own + overflow-y:auto would otherwise escape the collapsed (0-height) panel + and make the whole page scroll. Scoped to this component's accordion. */ + :host ::ng-deep [data-testid='studio-panels'] .p-accordioncontent { + min-height: 0; + /* Clip the content to its grid row. When collapsed PrimeNG animates + the row to 0; without this the content overflows that 0-height cell + and scrolls the whole page. The open panel scrolls via its inner + content's overflow-y (below), not this box. */ + overflow: hidden; + } + + :host ::ng-deep [data-testid='studio-panels'] .p-accordioncontent p-motion { + min-height: 0; + } + + :host ::ng-deep [data-testid='studio-panels'] .p-accordioncontent-wrapper { + min-height: 0; + } + + /* Both the shrinkable grid row (lets content be bounded shorter than its + natural height) and the scroll live ONLY on the ACTIVE panel. On a + collapsed panel PrimeNG animates the grid row to 0; leaving the row + shrinkable or the content scrollable there would let the content escape + the 0-height cell and scroll the whole page. */ + :host + ::ng-deep + [data-testid='studio-panels'] + .p-accordionpanel-active + .p-accordioncontent { + grid-template-rows: minmax(0, 1fr); + } + + :host + ::ng-deep + [data-testid='studio-panels'] + .p-accordionpanel-active + .p-accordioncontent-content { + min-height: 0; + overflow-y: auto; + } + ` + ], + // The run store + the services it drives are provided HERE (not at the root), + // so each run route gets a fresh instance — navigating to a different page + // recreates the store with clean scan/fix state. + providers: [ + A11yRunStore, + A11yMarkerService, + DotPageScannerService, + DotA11yAgentService, + DotAgentRunService + ], + changeDetection: ChangeDetectionStrategy.OnPush, + // Two rows: an `auto` row for the error banner (collapses to 0 when there is no + // error, since the banner is the only thing in it) over the main content row, which + // takes the rest. `minmax(0,1fr)` rather than `1fr` so the panes can be bounded + // shorter than their content and scroll internally. + host: { class: 'grid h-full min-h-0 grid-cols-[412px_1fr] grid-rows-[auto_minmax(0,1fr)]' } +}) +export class DotA11yRunComponent { + readonly store = inject(A11yRunStore); + + readonly #markerService = inject(A11yMarkerService); + readonly #router = inject(Router); + readonly #location = inject(Location); + readonly #destroyRef = inject(DestroyRef); + + /** + * The number shown in the ring center. Eased from its previous value up to + * the store's `openCount()` whenever a scan resolves (or the count changes + * while fixing), so the score "rolls" in sync with the donut sweep instead of + * snapping. See {@link animateCountTo}. + */ + readonly $displayCount = signal(0); + + /** + * The source file whose diff the right pane is showing, or null for the preview. + * Set from the changed-files accordion in the left panel; the preview stays + * mounted underneath so returning to it doesn't reload the iframes. + */ + readonly $diffFile = signal<PageDiffFile | null>(null); + + /** + * Which of the side panel's accordion panels are open — the `scanner` (score, + * issues, activity log + scan/fix actions) and `files` (changed files + publish). + * `p-accordion` is in `multiple` mode, so this is the array it two-way binds: + * the panels open and close independently and the user can watch a run while + * reviewing the files it touched. The scanner starts open, files collapsed. + */ + readonly $openPanels = signal<StudioPanel[]>(['scanner']); + + /** How many source files differ between working and live, for the count badge. */ + readonly $changedFileCount = signal(0); + + /** True when there's something to publish — drives the Publish bar. */ + readonly $hasChangedFiles = computed(() => this.$changedFileCount() > 0); + + /** rAF handle for the in-flight count-up, so a new scan can cancel it. */ + #countRaf: number | null = null; + + readonly #dm = inject(DotMessageService); + + /** Maps the agent stream + FixReport into shared activity-log bubbles. */ + readonly #presenter = new A11yAgentPresenter(this.#dm); + + /** + * The two side-by-side preview iframes. Markers are injected only into the + * LIVE frame's (same-origin) document — it always still carries the original + * scan's violations (see {@link showMarkers}). + */ + // NOTE: `private`, not `#`. Angular rejects an ES-private member for a signal query + // outright — "Cannot use 'viewChild' on a class member that is declared as ES private" + // — because the compiler has to write to the field from generated code. + private readonly $liveFrame = viewChild<ElementRef<HTMLIFrameElement>>('liveFrame'); + private readonly $previewFrame = viewChild<ElementRef<HTMLIFrameElement>>('previewFrame'); + + constructor() { + // The page list hands the selected row over in the navigation's `state` (the + // URL carries only its readable path, which can't supply identifier/host/ + // language). Adopt it, or bounce back to the list when there is none — which + // is what a cold load, refresh, or pasted run URL looks like, since the run + // route is reachable only THROUGH the list. + const row = readHandoverRow(this.#location.getState()); + if (row) { + this.store.openSelectedPage(row); + } else { + this.#toPageList(); + } + + // Redraw both frames' marker layers whenever their scans (or the phase) + // change. Each frame gets its OWN scan's findings: the preview frame from + // the primary/working scan (a11yGroups), the live frame from the + // comparison scan (liveA11yGroups). We run separate scans, so a fix that + // isn't published yet clears the preview markers while the live markers + // (still-published violations) remain. + effect(() => { + const show = this.$showMarkers(); + const previewGroups = this.store.a11yGroups(); + const liveGroups = this.store.liveA11yGroups(); + this.#markerService.render( + this.$previewFrame()?.nativeElement, + show ? previewGroups : [] + ); + this.#markerService.render(this.$liveFrame()?.nativeElement, show ? liveGroups : []); + }); + + // Roll the ring count up to the live open-count whenever it changes and a + // scan has produced results — the score animates in sync with the donut + // sweep. Before any results (ready/scanning) it stays parked at 0, so each + // scan / rescan rolls up fresh. + effect(() => { + const target = this.store.openCount(); + const scanned = this.store.hasResults(); + untracked(() => this.#animateCountTo(scanned ? target : 0)); + }); + + // Cancel any in-flight count-up when the component is torn down. + this.#destroyRef.onDestroy(() => this.#cancelCount()); + } + + /** + * Ease {@link displayCount} from its current value to `target` over ~600ms + * (easeOutCubic), synced with the donut's sweep. Snaps immediately when the + * user prefers reduced motion or the delta is trivial. + */ + #animateCountTo(target: number): void { + this.#cancelCount(); + + const from = this.$displayCount(); + if (from === target) { + return; + } + const reduceMotion = + typeof matchMedia === 'function' && + matchMedia('(prefers-reduced-motion: reduce)').matches; + if (reduceMotion) { + this.$displayCount.set(target); + + return; + } + + const duration = 600; + let start: number | null = null; + const step = (now: number) => { + start ??= now; + const t = Math.min(1, (now - start) / duration); + const eased = 1 - Math.pow(1 - t, 3); // easeOutCubic + this.$displayCount.set(Math.round(from + (target - from) * eased)); + if (t < 1) { + this.#countRaf = requestAnimationFrame(step); + } else { + this.#countRaf = null; + } + }; + this.#countRaf = requestAnimationFrame(step); + } + + #cancelCount(): void { + if (this.#countRaf !== null) { + cancelAnimationFrame(this.#countRaf); + this.#countRaf = null; + } + } + + /** + * Navigate to the page-list route. + * + * Absolute, NOT relative: the run screen is the `**` route (the page path is + * multi-segment, so it can't be a single param), and Angular's `..` drops one URL + * SEGMENT rather than one route level. From `/agents/a11y/about-us/index` a `..` + * yields `/agents/a11y/about-us`, which matches `**` again — the same component is + * reused and the screen never changes. + */ + #toPageList(): void { + this.#router.navigate([A11Y_PAGE_LIST_ROUTE]).catch(() => { + // Navigation cancelled (guard or a newer navigation superseded this + // one). Nothing to recover — the router owns where we ended up. + }); + } + + /** + * Re-entrancy guard for scroll mirroring: setting frame B's scroll fires B's + * own `scroll` event, which would mirror straight back to A — an infinite + * bounce. While we're programmatically scrolling the target, ignore its echo. + */ + #syncingScroll = false; + + /** + * LIVE iframe finished (re)loading — (re)draw its markers from the LIVE + * (comparison) scan + (re)wire scroll sync. A load replaces the document, so + * the effect-drawn layer is gone and must be redrawn here. + */ + onLiveLoad(): void { + this.#markerService.render( + this.$liveFrame()?.nativeElement, + this.$showMarkers() ? this.store.liveA11yGroups() : [] + ); + this.#wireScrollSync(this.$liveFrame(), this.$previewFrame()); + } + + /** + * PREVIEW iframe finished (re)loading — (re)draw its markers from the primary + * (working) scan + (re)wire scroll sync. + */ + onPreviewLoad(): void { + this.#markerService.render( + this.$previewFrame()?.nativeElement, + this.$showMarkers() ? this.store.a11yGroups() : [] + ); + this.#wireScrollSync(this.$previewFrame(), this.$liveFrame()); + } + + /** + * Mirror `source`'s scroll onto `target` so the two side-by-side renders stay + * aligned — makes the before/after diff scannable without scrolling each pane + * separately. Both frames are same-origin (the `/dot-page` proxy / BE origin), + * so we can read/write `contentWindow.scroll*` directly; cross-origin access + * throws and we no-op. + * + * Wired on every `load`: a reload/navigation replaces the frame's window, which + * drops the old listener for free, so we just attach a fresh one each time. + */ + #wireScrollSync( + source: ElementRef<HTMLIFrameElement> | undefined, + target: ElementRef<HTMLIFrameElement> | undefined + ): void { + const srcWin = this.#frameWindow(source); + if (!srcWin) { + return; + } + srcWin.addEventListener( + 'scroll', + () => { + if (this.#syncingScroll) { + return; + } + const tgtWin = this.#frameWindow(target); + if (!tgtWin) { + return; + } + this.#syncingScroll = true; + tgtWin.scrollTo(srcWin.scrollX, srcWin.scrollY); + // Release after the target's echoed scroll event has fired. + requestAnimationFrame(() => (this.#syncingScroll = false)); + }, + { passive: true } + ); + } + + /** The iframe's window; null when cross-origin or not yet loaded. */ + #frameWindow(frame: ElementRef<HTMLIFrameElement> | undefined): Window | null { + try { + return frame?.nativeElement.contentWindow ?? null; + } catch { + return null; + } + } + + /** + * Whether the violation overlays should be drawn. Each frame draws its OWN + * scan's findings (preview ← primary scan, live ← comparison scan), so the + * only shared gate is: a scan pass has run. Empty groups (e.g. the live scan + * hasn't landed yet, or a frame came back clean) simply draw no markers. + */ + readonly $showMarkers = computed<boolean>(() => this.store.hasResults()); + + /** + * The section-header label above the scrollable body, by phase: + * scanning → "SCAN", scanned → "BY ISSUE TYPE", fixing/done → "AGENT ACTIVITY". + */ + readonly $logHeaderKey = computed<string>(() => { + if (this.store.phase() === 'scanning') { + return 'accessibility.studio.loghdr.scan'; + } + if (this.store.phase() === 'scanned') { + return 'accessibility.studio.loghdr.issues'; + } + return 'accessibility.studio.loghdr.activity'; + }); + + /** The working badge label beside the header ("SCANNING" / "WORKING"), or null. */ + readonly $logBadgeKey = computed<string | null>(() => { + if (this.store.phase() === 'scanning') { + return 'accessibility.studio.badge.scanning'; + } + if (this.store.phase() === 'fixing') { + return 'accessibility.studio.badge.working'; + } + return null; + }); + + /** Headline above the severity legend, by phase. */ + readonly $scoreHeadlineKey = computed<string>(() => { + if (this.store.phase() === 'fixing') { + return 'accessibility.studio.score.fixing'; + } + if (this.store.finished()) { + return 'accessibility.studio.score.remaining'; + } + return 'accessibility.studio.score.found'; + }); + + /** + * Severity legend rows beside the donut (Critical/Serious/Moderate/Minor with + * their element counts). Drives both the legend and the donut segments. While + * scanned we hide empty buckets (matches the mockup); once fixing/done we keep + * them so the user sees a bucket reach 0. + */ + readonly $severityRows = computed<SeverityRow[]>(() => { + const counts = this.store.severityCounts(); + const keepZeros = this.store.runStarted(); + return SEVERITY_ORDER.map((severity) => ({ + severity, + label: SEVERITY_LABEL[severity], + color: SEVERITY_COLOR[severity], + count: counts[severity] + })).filter((row) => keepZeros || row.count > 0); + }); + + /** + * BY ISSUE TYPE rows with their dot color resolved. Projected here rather than + * calling a method from the template: this component's change detection is driven + * by a live SSE stream plus a rAF count-up, so a template method would re-run for + * every row many times a second. + */ + readonly $issueTypeRows = computed(() => + this.store.issueTypeRows().map((group) => ({ + ...group, + color: SEVERITY_COLOR[impactToSeverity(group.impact)] + })) + ); + + /** Needs-review rows with their "why a human is needed" i18n key resolved. */ + readonly $reviewRows = computed(() => + this.store.reviewGroups().map((group) => ({ + ...group, + reasonKey: + REVIEW_REASON_KEYS[group.code] ?? 'accessibility.studio.review.reason.default' + })) + ); + + /** PrimeNG doughnut data — one arc per severity, colored by SEVERITY_COLOR. */ + readonly $donutData = computed(() => { + const counts = this.store.severityCounts(); + const open = this.store.openCount(); + const total = SEVERITY_ORDER.reduce((sum, s) => sum + counts[s], 0); + // No open issues → render a single full "clear" ring (green) so the donut + // still reads as a complete circle rather than collapsing. + if (total === 0 || open === 0) { + return { + labels: ['Clear'], + datasets: [{ data: [1], backgroundColor: ['#22c55e'], borderWidth: 0 }] + }; + } + return { + labels: SEVERITY_ORDER.map((s) => SEVERITY_LABEL[s]), + datasets: [ + { + data: SEVERITY_ORDER.map((s) => counts[s]), + backgroundColor: SEVERITY_ORDER.map((s) => SEVERITY_COLOR[s]), + borderWidth: 0 + } + ] + }; + }); + + /** + * Doughnut options — thin ring, no legend/tooltip (the center text is overlaid). + * p-chart is sized via its `width`/`height` inputs (124px square); PrimeNG then + * sets `maintainAspectRatio: false` itself so the ring fills that square. We + * don't set responsive/aspect here — letting PrimeNG own the sizing keeps the + * ring centered in the box, aligned with the absolutely-centered count. + */ + readonly donutOptions = { + cutout: '74%', + plugins: { legend: { display: false }, tooltip: { enabled: false } }, + animation: { duration: 500 } + }; + + /** + * The SETTLED bubbles for the shared activity log, via the a11y presenter: + * - while fixing → one bubble per streamed SSE `phase` step (the completed + * actions); the live "now working" item is {@link workingMessage}, appended + * by the log itself + * - after done → the final report expanded into bubbles (scan/fixed/reported/rescan) + */ + readonly $activityMessages = computed<AgentMessage[]>(() => { + if (this.store.phase() === 'fixing') { + return this.store.steps().map((step, i) => this.#presenter.liveStep(step, i)); + } + if (this.store.finished()) { + const report = this.store.report(); + return report ? this.#presenter.resultMessages(report) : []; + } + return []; + }); + + /** + * The live "thinking" copy shown while fixing. It is ALWAYS generic + * loading/working/thinking text — never the last step's message — so the + * indicator reads clearly as "the agent is busy" and doesn't get mistaken for a + * finished step. The phrases cycle (and loop) as the run progresses so the line + * keeps visibly changing; elapsed seconds on the current action ride along as + * the sub-line. + */ + readonly $workingMessage = computed<AgentMessage | null>(() => { + if (this.store.phase() !== 'fixing') { + return null; + } + const sinceLastEventMs = this.store.heartbeat()?.sinceLastEventMs ?? 0; + + // Elapsed on the current action, once it's been running a beat. + const sinceSec = Math.floor(sinceLastEventMs / 1000); + const sub = + sinceSec >= 3 + ? this.#dm.get('accessibility.studio.working.elapsed', String(sinceSec)) + : undefined; + + return { + id: 'agent-working', + // Unused: dot-agent-thinking renders its own spinner and reads only + // text/sub. Kept non-empty only to satisfy AgentMessage. + icon: '', + text: this.#dm.get(this.#workingReassuranceKey(sinceLastEventMs)), + sub, + tone: 'info' + }; + }); + + /** + * Pick a generic reassurance line. Cycles through the phrases as the current + * action runs so the copy keeps visibly changing — and LOOPS, since a step can + * run for minutes and no phrase should imply it's nearly done or freeze on one + * message. + */ + #workingReassuranceKey(sinceLastEventMs: number): string { + const KEYS = [ + 'accessibility.studio.working.thinking', + 'accessibility.studio.working.analyzing', + 'accessibility.studio.working.reasoning', + 'accessibility.studio.working.stillworking' + ]; + // Advance one phrase roughly every 5s, wrapping around forever. + const index = Math.floor(sinceLastEventMs / 5000) % KEYS.length; + + return KEYS[index]; + } + + /** Footer title + sub keys derived from the current phase — single switch. */ + readonly $footerKeys = computed(() => { + const p = this.store.phase(); + const base = `accessibility.studio.footer.${p}`; + return { titleKey: `${base}.title`, subKey: `${base}.sub` }; + }); + + /** Interpolation args for the footer title, by phase. */ + readonly $footerArgs = computed<string[]>(() => { + switch (this.store.phase()) { + case 'scanned': + return [this.store.openCount().toString()]; + case 'fixing': + case 'done': + case 'published': + return [this.store.fixedCount().toString(), this.store.reportedCount().toString()]; + default: + return []; + } + }); + + /** Small leading icon + bubble color for the footer copy, by phase. */ + readonly $footerIcon = computed<{ icon: string; cls: string } | null>(() => { + switch (this.store.phase()) { + case 'scanned': + return { icon: 'pi pi-sparkles', cls: 'bg-primary-50 text-primary' }; + case 'fixing': + return { icon: 'pi pi-bolt', cls: 'bg-orange-50 text-orange-600' }; + default: + return null; + } + }); + + /** + * Same-origin prefix for the preview iframe URLs. + * + * In DEV the Angular dev server can't render dotCMS pages, so the iframes must + * hit the backend. The dev proxy maps the `/dot-page` sentinel → the BE page + * renderer (see apps/dotcms-ui/proxy-dev.conf.mjs). In PROD the portlet is + * served from the dotCMS origin, so the page lives at its own path with NO + * prefix — `/dot-page` would 404 there. `isDevMode()` is build-time accurate + * (true under `ng serve`, false in a production build) and needs no app-env + * import, so the dev-only prefix never leaks to production. + * + * NOTE: this pairs with the `/dot-page` rule in proxy-dev.conf.mjs — the two + * must change together, or the preview frames 404 in local dev. + * + * Same-origin is a hard requirement, not a convenience: the marker overlay and + * the scroll sync both reach into each frame's `contentWindow` + * ({@link frameWindow}), which the browser forbids cross-origin. A cross-origin + * frame would still render the page but silently draw no violation markers. + * + * NEEDS A BACKEND FIX: the proper solution is a first-class, same-origin dotCMS + * endpoint that renders a page for inspection (a supported resource under + * `/api`), so this component — and any future agent that has to inspect a + * rendered page — can frame it directly with no origin games and no dev-server + * rewrite. No such endpoint exists today; that gap is why the sentinel + proxy + * pair exists. Delete both once it lands. + */ + readonly #previewPathPrefix = isDevMode() ? '/dot-page' : ''; + + /** + * The page rendered in the given mode. `host_id` disambiguates which site's + * copy renders. Shared by the two side-by-side frames. An optional + * cache-busting `rev` forces the iframe to reload when the working render + * changes (see {@link previewUrl}). + */ + #urlFor(mode: 'PREVIEW_MODE' | 'LIVE', rev = 0): string { + const page = this.store.selected(); + if (!page) { + return ''; + } + const path = page.path.startsWith('/') ? page.path : `/${page.path}`; + const bust = rev > 0 ? `&rev=${rev}` : ''; + return `${this.#previewPathPrefix}${path}?host_id=${page.hostId}&language_id=${page.languageId}&mode=${mode}${bust}`; + } + + /** + * The two frames shown side by side so the diff reads at a glance: + * LIVE — the published render (what visitors see today, pre-fix) + markers + * PREVIEW — the working render (carries the agent's fixes, the "after") + * + * The PREVIEW url carries the store's `previewRevision` as a cache-buster, so + * the iframe reloads whenever the agent applies fixes (each mid-fix re-scan + + * the final report) and the page updates visually. LIVE never changes mid-run + * (it's the published render), so it takes no revision. + */ + readonly $liveUrl = computed(() => this.#urlFor('LIVE')); + readonly $previewUrl = computed(() => + this.#urlFor('PREVIEW_MODE', this.store.previewRevision()) + ); + + /** + * Back button / "All pages" — return to the page list. No store reset needed: the + * run store is provided at this component, so navigating away destroys it and + * the next run starts fresh. + */ + backToPageList(): void { + this.#toPageList(); + } + + /** + * Whether a given side panel is expanded. `p-accordion` renders the panel bodies + * itself off the same two-way-bound `openPanels`, so nothing in the template needs + * this — it's the readable way to assert open state from tests. + */ + isPanelOpen(panel: StudioPanel): boolean { + return this.$openPanels().includes(panel); + } + + /** + * Ensure a panel is open — used to jump the user to the files list. Not a toggle: + * pressing "Review files" twice must not close the panel it just opened. + */ + openPanel(panel: StudioPanel): void { + this.$openPanels.update((current) => + current.includes(panel) ? current : [...current, panel] + ); + } + + /** A file was picked in (or cleared from) the changed-files list. */ + onDiffFileSelected(file: PageDiffFile | null): void { + this.$diffFile.set(file); + } + + /** The changed-files list reports how many files differ, for the Publish gate. */ + onChangedFilesCount(count: number): void { + this.$changedFileCount.set(count); + } + + /** + * Leave the diff view from the right pane's own control. The accordion's + * highlighted row follows via its `activeFileId` input, so the two stay in sync. + */ + closeDiff(): void { + this.$diffFile.set(null); + } + + /** + * "Apply these changes" (done phase): publish the page. That promotes the whole + * working version — the page and its changed source files together — since there + * is no per-file publishing. The changed-files accordion above lists exactly + * what goes live. + */ + applyChanges(): void { + this.store.publish(); + } + + /** Drop this run's working fixes → back to the scanned state. */ + discardChanges(): void { + this.store.discard(); + } + + runScan(): void { + this.store.runScan(); + } + + stopScan(): void { + this.store.stopScan(); + } + + startFix(): void { + this.store.startFix(); + } + + stopAgent(): void { + this.store.stopAgent(); + } + + onSkipCssChange(value: boolean): void { + this.store.setSkipCss(value); + } +} + +/** Per-rule "why it needs review" i18n keys for the common axe incomplete rules. */ +const REVIEW_REASON_KEYS: Record<string, string> = { + 'color-contrast': 'accessibility.studio.review.reason.colorcontrast', + 'color-contrast-enhanced': 'accessibility.studio.review.reason.colorcontrast', + 'link-in-text-block': 'accessibility.studio.review.reason.linkintext', + 'scrollable-region-focusable': 'accessibility.studio.review.reason.scrollable', + 'aria-allowed-attr': 'accessibility.studio.review.reason.aria', + 'nested-interactive': 'accessibility.studio.review.reason.nested' +}; + +/** + * The page row the list hands over in the navigation's `state`, or null. + * + * Validated rather than cast: `history.state` is external input — it survives a reload, + * and anything can put anything there via `history.pushState`. The three fields checked + * are the ones the run screen cannot work without: `identifier` targets the fix, + * `hostId` scopes the scan URL, and `languageId` selects the version to read. A partial + * object reaching `openSelectedPage` would scan the wrong page, or a nonexistent one, + * with no error to explain it — bouncing to the list is the recoverable outcome. + */ +function readHandoverRow(state: unknown): StudioPageRow | null { + if (!state || typeof state !== 'object') { + return null; + } + + const row = (state as { row?: unknown }).row; + if (!row || typeof row !== 'object') { + return null; + } + + const { identifier, hostId, languageId } = row as Partial<StudioPageRow>; + + return typeof identifier === 'string' && + identifier.length > 0 && + typeof hostId === 'string' && + hostId.length > 0 && + typeof languageId === 'number' + ? (row as StudioPageRow) + : null; +} diff --git a/core-web/libs/portlets/dot-agents/src/lib/agents/a11y/a11y.constants.ts b/core-web/libs/portlets/dot-agents/src/lib/agents/a11y/a11y.constants.ts new file mode 100644 index 000000000000..58b1c35dfb12 --- /dev/null +++ b/core-web/libs/portlets/dot-agents/src/lib/agents/a11y/a11y.constants.ts @@ -0,0 +1,15 @@ +/** + * Absolute URL of the Accessibility Studio page list — the screen the run screen + * returns to. Kept in its own module (rather than beside the route table) because the + * run component imports it and `a11y.routes.ts` imports the run component. + * + * It has to be ABSOLUTE. The run screen is the `**` route, since a page path is + * multi-segment and can't be a single route param, and Angular's `..` drops one URL + * segment rather than one route level — so a relative hop from + * `/agents/a11y/about-us/index` lands on `/agents/a11y/about-us`, which matches `**` + * again and leaves the user on the same screen. + * + * Mirrors the mount path: `agents` in `app.routes.ts` + the `a11y` agent id in + * `agent-registry.ts`. + */ +export const A11Y_PAGE_LIST_ROUTE = '/agents/a11y'; diff --git a/core-web/libs/portlets/dot-agents/src/lib/agents/a11y/a11y.routes.ts b/core-web/libs/portlets/dot-agents/src/lib/agents/a11y/a11y.routes.ts new file mode 100644 index 000000000000..43db13baf098 --- /dev/null +++ b/core-web/libs/portlets/dot-agents/src/lib/agents/a11y/a11y.routes.ts @@ -0,0 +1,32 @@ +import { Route } from '@angular/router'; + +import { DotA11yPageListComponent } from './a11y-page-list/a11y-page-list.component'; +import { DotA11yRootComponent } from './a11y-root/a11y-root.component'; +import { DotA11yRunComponent } from './a11y-run/a11y-run.component'; + +/** + * Accessibility Studio routes. The root is a thin outlet host; each child route + * provides its OWN store (page list vs run) so the two screens are fully independent: + * - `''` → the page list + * - `**` → the run screen for one page + */ +export const dotAccessibilityStudioRoutes: Route[] = [ + { + path: '', + component: DotA11yRootComponent, + children: [ + { path: '', component: DotA11yPageListComponent }, + // Wildcard, not `:id`: the run URL carries the page's human-readable path + // (e.g. `blog/post/hello`), which is multi-segment and so can't be a single + // Angular route param. `**` captures the whole path. Must come after `''`. + // + // The URL is for DISPLAY and history only — it does NOT make the run screen + // deep-linkable. The page itself is handed over in the navigation's `state` + // by the list, because the path alone can't supply the identifier, host and + // language the run needs. Opening a run URL cold (new tab, refresh, pasted + // link) therefore has no state and bounces straight back to the list; see + // DotA11yRunComponent's constructor. + { path: '**', component: DotA11yRunComponent } + ] + } +]; diff --git a/core-web/libs/portlets/dot-agents/src/lib/agents/a11y/models/a11y-agent.presenter.spec.ts b/core-web/libs/portlets/dot-agents/src/lib/agents/a11y/models/a11y-agent.presenter.spec.ts new file mode 100644 index 000000000000..5193818da408 --- /dev/null +++ b/core-web/libs/portlets/dot-agents/src/lib/agents/a11y/models/a11y-agent.presenter.spec.ts @@ -0,0 +1,97 @@ +import { DotMessageService } from '@dotcms/data-access'; + +import { A11yAgentPresenter } from './a11y-agent.presenter'; +import { + FixReport, + NEEDS_ATTENTION_STATUSES, + RESEARCH_RULE_ID +} from './accessibility-studio.models'; +import { MOCK_FIX_REPORT } from './mock-fix-report'; + +describe('A11yAgentPresenter', () => { + let presenter: A11yAgentPresenter; + + beforeEach(() => { + // Echo the key + args so assertions can check what was requested. + const dm = { + get: (key: string, ...args: string[]) => + args.length ? `${key}(${args.join(',')})` : key + } as unknown as DotMessageService; + presenter = new A11yAgentPresenter(dm); + }); + + describe('liveStep', () => { + it('picks the icon from the step phase meta and uses info tone', () => { + const msg = presenter.liveStep({ message: 'Scanning', meta: { phase: 'scan' } }, 0); + expect(msg).toEqual({ id: 0, icon: 'search', text: 'Scanning', tone: 'info' }); + }); + + it('falls back to the wrench icon when phase meta is missing', () => { + const msg = presenter.liveStep({ message: 'Working' }, 3); + expect(msg).toEqual({ id: 3, icon: 'build', text: 'Working', tone: 'info' }); + }); + }); + + describe('resultMessages', () => { + it('bookends the fixed/reported rows with scan and rescan headers', () => { + const messages = presenter.resultMessages(MOCK_FIX_REPORT); + expect(messages[0].id).toBe('scan'); + expect(messages[1].id).toBe('locate'); + expect(messages[messages.length - 1].id).toBe('rescan'); + }); + + it('renders fixed results as success bubbles and reported as warning', () => { + const messages = presenter.resultMessages(MOCK_FIX_REPORT); + const fixed = messages.filter((m) => String(m.id).startsWith('fixed-')); + const reported = messages.filter((m) => String(m.id).startsWith('reported-')); + + const expectedFixed = MOCK_FIX_REPORT.results.filter( + (r) => r.status === 'fixed-to-working' + ).length; + // `reported` is excluded on purpose: it marks a violation the deterministic + // pass handed to the agentic pass, not one left unresolved. + const expectedReported = MOCK_FIX_REPORT.results.filter((r) => + NEEDS_ATTENTION_STATUSES.includes(r.status) + ).length; + + expect(fixed.length).toBe(expectedFixed); + expect(reported.length).toBe(expectedReported); + expect(fixed.every((m) => m.tone === 'success')).toBe(true); + expect(reported.every((m) => m.tone === 'warning')).toBe(true); + }); + + it('does not surface `reported` rows as needing attention', () => { + const messages = presenter.resultMessages(MOCK_FIX_REPORT); + const reported = messages.filter((m) => String(m.id).startsWith('reported-')); + + // The mock carries 3 `reported` + 2 `skipped`; only the skipped ones are + // unresolved, so a `reported` row must never produce a warning bubble. + expect(reported.length).toBe(2); + }); + + it('keeps the research pass out of the fixed rows', () => { + const report: FixReport = { + ...MOCK_FIX_REPORT, + results: [ + { ruleId: RESEARCH_RULE_ID, status: 'fixed-to-working', file: '/a.vtl' }, + { ruleId: 'color-contrast', status: 'fixed-to-working', file: '/b.css' } + ] + }; + const fixed = presenter + .resultMessages(report) + .filter((m) => String(m.id).startsWith('fixed-')); + + expect(fixed.length).toBe(1); + expect(fixed[0].sub).toContain('color-contrast'); + }); + + it('builds the rule · file sub-line', () => { + const messages = presenter.resultMessages(MOCK_FIX_REPORT); + const firstFixed = messages.find((m) => String(m.id).startsWith('fixed-')); + const firstFixedResult = MOCK_FIX_REPORT.results.find( + (r) => r.status === 'fixed-to-working' + ); + expect(firstFixed?.sub).toContain(firstFixedResult?.ruleId ?? ''); + }); + }); +}); diff --git a/core-web/libs/portlets/dot-agents/src/lib/agents/a11y/models/a11y-agent.presenter.ts b/core-web/libs/portlets/dot-agents/src/lib/agents/a11y/models/a11y-agent.presenter.ts new file mode 100644 index 000000000000..15bdde39847b --- /dev/null +++ b/core-web/libs/portlets/dot-agents/src/lib/agents/a11y/models/a11y-agent.presenter.ts @@ -0,0 +1,123 @@ +import { AgentMessage, AgentMessagePresenter } from '@dotcms/ai-ui'; +import { DotMessageService } from '@dotcms/data-access'; +import { AgentRunStep } from '@dotcms/dotcms-models'; + +import { + FixReport, + FixResult, + NEEDS_ATTENTION_STATUSES, + RESEARCH_RULE_ID, + StudioStepPhase +} from './accessibility-studio.models'; + +/** Material Symbols icon for each live agent step phase (SSE `step` events). */ +const STEP_PHASE_ICON: Record<StudioStepPhase, string> = { + scan: 'search', + locate: 'account_tree', + read: 'description', + fix: 'build', + rescan: 'verified' +}; + +/** + * Strips a leading role label (e.g. `Agent:`, `Assistant:`) that the model + * sometimes prepends to a step message, so the log shows the action itself + * ("reading activity.vtl") rather than a chat-style "Agent: reading …". Only a + * single leading `Word:` token is removed; the rest of the message is untouched. + */ +const ROLE_PREFIX = /^\s*(?:agent|assistant|system|ai)\s*:\s*/i; + +function cleanStepText(message: string): string { + return message.replace(ROLE_PREFIX, '').trim(); +} + +/** + * Maps the accessibility agent's stream + {@link FixReport} into activity-log + * bubbles. This is the a11y agent's implementation of the shared + * {@link AgentMessagePresenter} seam — the only place that knows about axe rules, + * fix statuses, and the scan→fix→rescan recipe. Depends only on + * {@link DotMessageService} for i18n, so it can be constructed anywhere. + */ +export class A11yAgentPresenter implements AgentMessagePresenter<FixReport> { + readonly #dm: DotMessageService; + + constructor(dm: DotMessageService) { + this.#dm = dm; + } + + /** A live step → an info bubble, icon chosen from the step's `phase` meta. */ + liveStep(step: AgentRunStep, index: number): AgentMessage { + const phase = step.meta?.['phase'] as StudioStepPhase | undefined; + return { + id: index, + icon: phase ? STEP_PHASE_ICON[phase] : 'build', + text: cleanStepText(step.message), + tone: 'info' + }; + } + + /** + * The final report as bubbles: a scan header, a locate row, one row per fixed + * rule (success) and per reported rule (warning), then a rescan footer with + * the before/after counts. + */ + resultMessages(report: FixReport): AgentMessage[] { + const fixed = report.results.filter( + (r) => r.status === 'fixed-to-working' && r.ruleId !== RESEARCH_RULE_ID + ); + const reported = report.results.filter((r) => NEEDS_ATTENTION_STATUSES.includes(r.status)); + + return [ + { + id: 'scan', + icon: 'search', + text: this.#dm.get('accessibility.studio.recipe.scan'), + sub: this.#dm.get( + 'accessibility.studio.recipe.scan.sub', + String(report.scan.before.violations) + ), + tone: 'info' + }, + { + id: 'locate', + icon: 'account_tree', + text: this.#dm.get('accessibility.studio.recipe.locate'), + tone: 'info' + }, + ...fixed.map((r, i) => ({ + id: `fixed-${i}`, + icon: 'check', + text: r.review ?? this.#dm.get('accessibility.studio.recipe.fixed', r.ruleId), + sub: this.#ruleAndFile(r), + tone: 'success' as const + })), + ...reported.map((r, i) => ({ + id: `reported-${i}`, + // Distinct icon: reverted/regressed → undo, everything else → flag. + icon: r.reverted || r.status === 'regressed' ? 'undo' : 'flag', + text: + r.review ?? + r.reason ?? + this.#dm.get('accessibility.studio.recipe.flagged', r.ruleId), + sub: this.#ruleAndFile(r), + tone: 'warning' as const + })), + { + id: 'rescan', + icon: 'verified', + text: this.#dm.get('accessibility.studio.recipe.rescan'), + sub: this.#dm.get( + 'accessibility.studio.recipe.rescan.sub', + String(report.scan.before.violations), + String(report.scan.after.violations) + ), + tone: 'info' + } + ]; + } + + #ruleAndFile(r: FixResult): string { + const file = r.file ? r.file.split('/').pop() : undefined; + return file ? `${r.ruleId} · ${file}` : r.ruleId; + } +} diff --git a/core-web/libs/portlets/dot-agents/src/lib/agents/a11y/models/a11y-severity.spec.ts b/core-web/libs/portlets/dot-agents/src/lib/agents/a11y/models/a11y-severity.spec.ts new file mode 100644 index 000000000000..7211fde86532 --- /dev/null +++ b/core-web/libs/portlets/dot-agents/src/lib/agents/a11y/models/a11y-severity.spec.ts @@ -0,0 +1,54 @@ +import { A11yGroup } from '@dotcms/portlets/dot-ema/ui'; + +import { impactToSeverity, severityBreakdown, SEVERITY_ORDER } from './a11y-severity'; + +describe('a11y-severity', () => { + describe('impactToSeverity', () => { + it('maps each axe impact to its bucket', () => { + expect(impactToSeverity('critical')).toBe('critical'); + expect(impactToSeverity('serious')).toBe('serious'); + expect(impactToSeverity('moderate')).toBe('moderate'); + expect(impactToSeverity('minor')).toBe('minor'); + }); + + it('buckets a null/absent impact as minor', () => { + expect(impactToSeverity(null)).toBe('minor'); + }); + }); + + describe('severityBreakdown', () => { + const group = (impact: A11yGroup['impact'], count: number): A11yGroup => ({ + code: 'x', + type: 'error', + message: 'm', + impact, + helpUrl: '', + items: [], + count + }); + + it('sums element counts per severity bucket', () => { + const counts = severityBreakdown([ + group('critical', 3), + group('critical', 1), + group('serious', 2), + group('moderate', 1), + group(null, 4) // → minor + ]); + expect(counts).toEqual({ critical: 4, serious: 2, moderate: 1, minor: 4 }); + }); + + it('returns all-zero for no groups', () => { + expect(severityBreakdown([])).toEqual({ + critical: 0, + serious: 0, + moderate: 0, + minor: 0 + }); + }); + }); + + it('orders severities high → low', () => { + expect(SEVERITY_ORDER).toEqual(['critical', 'serious', 'moderate', 'minor']); + }); +}); diff --git a/core-web/libs/portlets/dot-agents/src/lib/agents/a11y/models/a11y-severity.ts b/core-web/libs/portlets/dot-agents/src/lib/agents/a11y/models/a11y-severity.ts new file mode 100644 index 000000000000..fe9aa0d7f25b --- /dev/null +++ b/core-web/libs/portlets/dot-agents/src/lib/agents/a11y/models/a11y-severity.ts @@ -0,0 +1,69 @@ +import { A11yGroup, AxeImpact } from '@dotcms/portlets/dot-ema/ui'; + +/** + * Severity model for the Studio score widget + issue-type list. Mirrors axe-core's + * four impact levels; the donut ring + legend are colored by these. A null impact + * (axe occasionally omits it) buckets to the lowest, `minor`, so every issue is + * always counted somewhere. + */ +export type Severity = 'critical' | 'serious' | 'moderate' | 'minor'; + +/** Highest → lowest. Drives legend order, ring segment order, and list sort. */ +export const SEVERITY_ORDER: readonly Severity[] = ['critical', 'serious', 'moderate', 'minor']; + +/** + * `SEVERITY_ORDER` as a lookup, so sort comparators rank by O(1) index instead of a + * linear `indexOf` per comparison. + */ +export const SEVERITY_RANK: Record<Severity, number> = SEVERITY_ORDER.reduce( + (acc, severity, index) => ({ ...acc, [severity]: index }), + {} as Record<Severity, number> +); + +export const SEVERITY_LABEL: Record<Severity, string> = { + critical: 'Critical', + serious: 'Serious', + moderate: 'Moderate', + minor: 'Minor' +}; + +/** + * Hex colors for each severity — used for the donut segments and the legend/list + * dots. Taken from the Accessibility Studio design (.dc.html): red / orange / + * amber / slate-gray. + */ +export const SEVERITY_COLOR: Record<Severity, string> = { + critical: '#e0314f', + serious: '#f06a1e', + moderate: '#e8b838', + minor: '#94a3b8' +}; + +/** Map an axe impact (or null) to a Severity bucket — null → minor. */ +export function impactToSeverity(impact: AxeImpact): Severity { + switch (impact) { + case 'critical': + return 'critical'; + case 'serious': + return 'serious'; + case 'moderate': + return 'moderate'; + default: + return 'minor'; + } +} + +export type SeverityCounts = Record<Severity, number>; + +const EMPTY_COUNTS = (): SeverityCounts => ({ critical: 0, serious: 0, moderate: 0, minor: 0 }); + +/** + * Sum the flagged ELEMENT counts per severity across the given groups (use the + * `error` groups — confirmed violations — for the "open issues" breakdown). + */ +export function severityBreakdown(groups: A11yGroup[]): SeverityCounts { + return groups.reduce<SeverityCounts>((acc, g) => { + acc[impactToSeverity(g.impact)] += g.count; + return acc; + }, EMPTY_COUNTS()); +} diff --git a/core-web/libs/portlets/dot-agents/src/lib/agents/a11y/models/accessibility-studio.models.ts b/core-web/libs/portlets/dot-agents/src/lib/agents/a11y/models/accessibility-studio.models.ts new file mode 100644 index 000000000000..b084323789a3 --- /dev/null +++ b/core-web/libs/portlets/dot-agents/src/lib/agents/a11y/models/accessibility-studio.models.ts @@ -0,0 +1,120 @@ +import { AgentChangedFile, AgentStreamEvent } from '@dotcms/dotcms-models'; + +// ── Agent wire contract ───────────────────────────────────────────────────── + +export type FixStatus = 'fixed-to-working' | 'reported' | 'skipped' | 'regressed' | 'failed'; + +/** + * Statuses that mean a violation was genuinely left unresolved. + * + * `reported` is NOT one of them: a run makes two passes (deterministic, then agentic), + * and `reported` marks a violation the first pass handed to the second — an + * intermediate handoff, not an outcome. The agentic pass usually goes on to fix it. + * Counting `reported` as unresolved reported work that was actually done. + */ +export const NEEDS_ATTENTION_STATUSES: ReadonlyArray<FixStatus> = [ + 'skipped', + 'regressed', + 'failed' +]; + +/** + * Rule id the agent uses for its research pass. Not a fix — it's the step where the + * agent worked out how to fix something (or why it couldn't), so it carries a `reason` + * narrative and no `diff`. Kept out of the fixed list; the files it touched appear in + * the diff view via the report's `changedFiles`. + */ +export const RESEARCH_RULE_ID = 'agentic-research'; + +export type BlastRadius = 'element-scoped' | 'shared-rule' | 'token'; + +export interface ScanCount { + violations: number; +} + +export interface FixResult { + ruleId: string; + status: FixStatus; + file?: string; + identifier?: string; + diff?: string; + blastRadius?: BlastRadius; + review?: string; + reverted?: boolean; + reason?: string; +} + +export interface FixReport { + runId: string; + page: { uri: string; host: string; languageId: number }; + scan: { before: ScanCount; after: ScanCount }; + results: FixResult[]; + /** Files the agent changed in the working version (path + identifier). */ + changedFiles: AgentChangedFile[]; + /** True when the working fixes still need publishing to reach the live site. */ + publishRequired: boolean; + /** Terminal status the agent reports on the `done` frame. */ + status?: string; +} + +/** + * The Studio → proxy request body (POST /api/v1/agents/a11y/fix[/stream]). + * Simpler than the full FixRequest — the Java proxy resolves the page and builds + * the complete agent payload (FixRequest) before forwarding. + */ +export interface AgentFixRequest { + /** dotCMS content identifier of the page to fix. */ + identifier: string; + /** Language id (default: 1). */ + languageId: number; + /** When true the agent fixes only VTL and reports CSS contrast. */ + skipCss: boolean; +} + +/** + * The run screen's state machine. Drives which action block renders. + * ready → scanning → scanned → fixing → done → published + * + * (The page list is a separate route with its own store — see A11yPageListStore — so + * there is no `page-list` phase here.) + */ +export type StudioPhase = 'ready' | 'scanning' | 'scanned' | 'fixing' | 'done' | 'published'; + +/** A page row in the page list, projected from a DotCMSContentlet. */ +export interface StudioPageRow { + identifier: string; + title: string; + /** Page URI / path, e.g. "/about-us". */ + path: string; + /** Content type / base type label, e.g. "htmlpageasset", "Blog". */ + type: string; + languageId: number; + /** Host identifier — used as `host_id` to disambiguate the page render. */ + hostId: string; + hostName: string; + /** Human-formatted last-edited date. */ + modDate: string; + modUserName: string; + live: boolean; +} + +// ── Agent streaming (SSE) ─────────────────────────────────────────────────── + +/** + * Coarse phase tag on each streamed `step` event, emitted by the agent loop. + * Mirrors the agent's onStep phases (run-fix/tools): carried in the generic + * step's `meta.phase`, the presenter maps it to an icon for the activity log. + */ +export type StudioStepPhase = 'scan' | 'locate' | 'read' | 'fix' | 'rescan'; + +/** + * The a11y agent's stream: the generic {@link AgentStreamEvent} specialized to + * the a11y terminal payload. `done`/`aborted` carry a {@link FixReport} (the + * `aborted` one is partial — fixes already applied are kept). + * + * Nullable because a terminal frame does not guarantee a report: `aborted` can carry a + * status-only payload, and `FixReport.status` exists precisely for that. The service + * validates the shape and hands back null rather than a `FixReport`-shaped lie, so + * consumers reading `report.scan` have to acknowledge the case. + */ +export type A11yAgentStreamEvent = AgentStreamEvent<FixReport | null>; diff --git a/core-web/libs/portlets/dot-agents/src/lib/agents/a11y/models/mock-fix-report.ts b/core-web/libs/portlets/dot-agents/src/lib/agents/a11y/models/mock-fix-report.ts new file mode 100644 index 000000000000..c23f2ad3fc6c --- /dev/null +++ b/core-web/libs/portlets/dot-agents/src/lib/agents/a11y/models/mock-fix-report.ts @@ -0,0 +1,140 @@ +import { FixReport } from './accessibility-studio.models'; + +/** + * Sample run report used as a test fixture across the store, run-component, + * and presenter specs: 7 fixed to working, 5 reported/skipped — 12 → 5 + * violations. + */ +export const MOCK_FIX_REPORT: FixReport = { + runId: 'r_mock_01J', + page: { + uri: '/travel/about-us', + host: 'demo.dotcms.com', + languageId: 1 + }, + scan: { + before: { violations: 12 }, + after: { violations: 5 } + }, + results: [ + { + ruleId: 'image-alt', + status: 'fixed-to-working', + file: '//demo.dotcms.com/application/themes/travel/templates/travel-header.vtl', + identifier: 'a56e1f00', + diff: '+ alt="Aerial view of a turquoise coastline at sunset"', + review: 'Added alt text to the hero image' + }, + { + ruleId: 'heading-order', + status: 'fixed-to-working', + file: '//demo.dotcms.com/application/themes/travel/templates/travel-header.vtl', + identifier: 'a56e1f00', + diff: '- <h3 class="hero-title">\n+ <h2 class="hero-title">', + review: 'Fixed heading order (h3 → h2)' + }, + { + ruleId: 'button-name', + status: 'fixed-to-working', + file: '//demo.dotcms.com/application/themes/travel/templates/travel-header.vtl', + identifier: 'a56e1f00', + diff: '+ aria-label="Open navigation menu"', + review: 'Named the menu toggle button' + }, + { + ruleId: 'html-has-lang', + status: 'fixed-to-working', + file: '//demo.dotcms.com/application/themes/travel/html-head.vtl', + identifier: 'b1c2d3e4', + diff: '- <html>\n+ <html lang="en">', + review: 'Set lang="en" on <html>' + }, + { + ruleId: 'region', + status: 'fixed-to-working', + file: '//demo.dotcms.com/application/themes/travel/default-template.vtl', + identifier: 'c3d4e5f6', + diff: '+ <main id="main-content">\n...\n+ </main>', + review: 'Wrapped content in a <main> landmark' + }, + { + ruleId: 'label', + status: 'fixed-to-working', + file: '//demo.dotcms.com/application/containers/newsletter/newsletter-container.vtl', + identifier: 'd4e5f6a7', + diff: '+ <label for="newsletter-email">Email address</label>', + review: 'Added a label to the email input' + }, + { + ruleId: 'link-name', + status: 'fixed-to-working', + file: '//demo.dotcms.com/application/containers/cards/destination-card.vtl', + identifier: 'e5f6a7b8', + diff: '+ aria-label="Read more about $destination.title"', + review: 'Gave "Read more" links accessible names' + }, + { + ruleId: 'color-contrast', + status: 'reported', + file: '//demo.dotcms.com/application/themes/travel/css/_variables-custom.scss', + identifier: 'f6a7b8c9', + blastRadius: 'token', + review: 'Hero CTA contrast 3.1:1 — driven by the --brand-fg token', + reason: 'Fixing the token changes the brand color site-wide; needs a design decision' + }, + { + ruleId: 'color-contrast', + status: 'reported', + file: '//demo.dotcms.com/application/themes/travel/css/styles.scss', + identifier: 'a7b8c9d0', + blastRadius: 'shared-rule', + review: 'Footer link contrast 4.1:1 — adjust .footer a in styles.scss', + reason: 'Borderline contrast; a small nudge affects all footer links' + }, + { + ruleId: 'color-contrast', + status: 'reported', + file: '//demo.dotcms.com/application/themes/travel/css/_nav.scss', + identifier: 'b8c9d0e1', + blastRadius: 'shared-rule', + review: 'Nav links contrast borderline (4.4:1) — theme CSS variable', + reason: 'Just under the 4.5:1 AA threshold; needs a deliberate color choice' + }, + { + ruleId: 'image-alt', + status: 'skipped', + reason: 'Alt text lives in a content field; out of v1 scope' + }, + { + ruleId: 'link-name', + status: 'skipped', + reason: 'Link text comes from a contentlet; out of v1 scope' + } + ], + // Distinct files left changed (the fixed-to-working ones, deduped). The + // reported .scss files were not modified, so they're excluded. Each entry is + // the changed asset's host-qualified path + its content identifier. + changedFiles: [ + { + path: '//demo.dotcms.com/application/themes/travel/templates/travel-header.vtl', + identifier: 'a1b2c3d4-travel-header' + }, + { + path: '//demo.dotcms.com/application/themes/travel/html-head.vtl', + identifier: 'a1b2c3d4-html-head' + }, + { + path: '//demo.dotcms.com/application/themes/travel/default-template.vtl', + identifier: 'a1b2c3d4-default-template' + }, + { + path: '//demo.dotcms.com/application/containers/newsletter/newsletter-container.vtl', + identifier: 'a1b2c3d4-newsletter' + }, + { + path: '//demo.dotcms.com/application/containers/cards/destination-card.vtl', + identifier: 'a1b2c3d4-destination-card' + } + ], + publishRequired: true +}; diff --git a/core-web/libs/portlets/dot-agents/src/lib/agents/a11y/models/page-render-sources.models.ts b/core-web/libs/portlets/dot-agents/src/lib/agents/a11y/models/page-render-sources.models.ts new file mode 100644 index 000000000000..0208f1c897a2 --- /dev/null +++ b/core-web/libs/portlets/dot-agents/src/lib/agents/a11y/models/page-render-sources.models.ts @@ -0,0 +1,123 @@ +/** + * Types for the "working vs live" file diff view. + * + * The diff view answers: "which source files that compose this page differ + * between the working and the published (live) version?" — i.e. what the agent + * actually changed but hasn't published yet. It is driven by two backend calls: + * + * 1. GET /api/v1/page/_render-sources/{uri} → the source files of a page + * (theme VTLs/CSS, container VTLs, widget VTLs). References only, no content. + * 2. GET /api/v1/content/versions?identifier=&groupByLang=1 → every version of + * a file asset, with `working`/`live` flags and a version-specific + * `fileAssetVersion` path (`/dA/<inode>/fileAsset/<name>`) whose bytes are + * that exact version's content. + * + * The `_render-sources` shapes mirror the Java views in + * `com.dotcms.rest.api.v1.page` (PageRenderSourcesView & friends). + */ + +// ── /api/v1/page/_render-sources/{uri} response ───────────────────────────── + +/** Where a container / widget / content-type's Velocity code lives. */ +export type RenderSourceType = 'DB' | 'FILE' | 'CODE'; + +/** A file asset reference (theme file) — path, identifier, extension only. */ +export interface FileRefView { + /** Host-qualified path, e.g. `//demo.dotcms.com/application/themes/travel/header.vtl`. */ + path: string; + /** File asset identifier. */ + identifier: string; + /** Lowercased extension without the dot, e.g. `vtl`, `css`, `scss`. */ + extension: string; +} + +/** One content type placed in a container. FILE containers also carry path + identifier. */ +export interface ContentTypeEntryView { + contentTypeVar: string; + /** Host-qualified path to the VTL file (FILE containers only). */ + path?: string; + /** File asset identifier of the VTL file (FILE containers only). */ + identifier?: string; +} + +/** A container referenced by the page template (keyed by container ref in the map). */ +export interface ContainerSourceView { + /** `DB` or `FILE`. */ + source: RenderSourceType; + contentTypes: ContentTypeEntryView[]; +} + +/** The page's theme: folder ref + every file under it (recursive). */ +export interface ThemeSourceView { + id: string; + name: string; + folderPath: string; + files: FileRefView[]; +} + +/** A widget contentlet placed on the page. FILE widgets carry the VTL file ref. */ +export interface WidgetSourceView { + contentTypeVar: string; + title: string; + contentletId: string; + contentletInode: string; + /** `FILE` (path/identifier populated) or `CODE` (inline Velocity, no file). */ + source: 'FILE' | 'CODE'; + path?: string; + identifier?: string; +} + +/** Lightweight page reference. */ +export interface PageSourceRefView { + identifier: string; + /** Host-qualified page URI, e.g. `//demo.dotcms.com/index`. */ + uri: string; + languageId: number; +} + +/** Top-level `_render-sources` response entity. */ +export interface PageRenderSourcesView { + page: PageSourceRefView; + theme: ThemeSourceView; + /** Keyed by container reference (UUID for DB, host-qualified path for FILE). */ + containers: Record<string, ContainerSourceView>; + widgets: WidgetSourceView[]; +} + +// ── Flattened source file + diff models ───────────────────────────────────── + +/** Which part of the page a source file belongs to — for grouping / labeling. */ +export type PageSourceOrigin = 'theme' | 'container' | 'widget'; + +/** + * A single source file of the page, flattened from the `_render-sources` tree + * and de-duplicated by identifier. Only files backed by a real file asset + * (with an `identifier`) appear — inline CODE widgets and DB containers have no + * file to diff and are dropped upstream. + */ +export interface PageSourceFile { + identifier: string; + /** Host-qualified path from `_render-sources`. */ + path: string; + /** Basename for display, e.g. `header.vtl`. */ + name: string; + /** Lowercased extension without the dot, e.g. `vtl`, `css`. */ + extension: string; + /** Where in the page this file comes from. */ + origin: PageSourceOrigin; +} + +/** + * A source file whose working and live versions have been resolved to text and + * found to DIFFER. The diff view lists only these. + */ +export interface PageDiffFile extends PageSourceFile { + /** Working (unpublished) version text — the "after". */ + working: string; + /** Live (published) version text — the "before". Empty when the file has no live version yet. */ + live: string; + /** Added line count (working vs live). */ + added: number; + /** Removed line count (working vs live). */ + removed: number; +} diff --git a/core-web/libs/portlets/dot-agents/src/lib/agents/a11y/services/a11y-marker.service.spec.ts b/core-web/libs/portlets/dot-agents/src/lib/agents/a11y/services/a11y-marker.service.spec.ts new file mode 100644 index 000000000000..6cf86884600d --- /dev/null +++ b/core-web/libs/portlets/dot-agents/src/lib/agents/a11y/services/a11y-marker.service.spec.ts @@ -0,0 +1,143 @@ +import { A11yGroup } from '@dotcms/portlets/dot-ema/ui'; + +import { A11yMarkerService } from './a11y-marker.service'; + +const GROUPS: A11yGroup[] = [ + { + code: 'image-alt', + type: 'error', + message: 'Images must have alternate text', + impact: 'critical', + helpUrl: '', + items: [ + { context: '<img>', selector: 'img.hero' }, + { context: '<img>', selector: 'img.logo' } + ], + count: 2 + }, + { + code: 'color-contrast', + type: 'warning', + message: 'Low contrast', + impact: 'moderate', + helpUrl: '', + items: [{ context: '<a>', selector: 'a.cta' }], + count: 1 + } +]; + +const MARKER_SELECTOR = '[data-a11y-marker]'; + +describe('A11yMarkerService', () => { + let service: A11yMarkerService; + let iframe: HTMLIFrameElement; + + beforeEach(() => { + service = new A11yMarkerService(); + iframe = document.createElement('iframe'); + document.body.appendChild(iframe); + const doc = iframe.contentDocument as Document; + doc.body.innerHTML = ` + <img class="hero" /> + <img class="logo" /> + <a class="cta">x</a> + <section class="landmarkless">big region</section> + <p class="not-flagged">y</p> + `; + // jsdom does no layout — every getBoundingClientRect() is 0×0, which the + // service skips. Give flagged elements a non-zero box so markers render. + doc.querySelectorAll('img, a, section').forEach((el, i) => { + (el as HTMLElement).getBoundingClientRect = () => + ({ + top: 100 + i * 50, + left: 20, + width: 200, + height: 40, + right: 220, + bottom: 140 + i * 50, + x: 20, + y: 100 + i * 50, + toJSON: () => ({}) + }) as DOMRect; + }); + }); + + afterEach(() => iframe.remove()); + + it('injects one marker per flagged element that resolves', () => { + service.render(iframe, GROUPS); + const markers = iframe.contentDocument?.querySelectorAll(MARKER_SELECTOR); + expect(markers?.length).toBe(3); + }); + + it('tags markers with the rule code', () => { + service.render(iframe, GROUPS); + const codes = Array.from( + iframe.contentDocument?.querySelectorAll(MARKER_SELECTOR) ?? [] + ).map((el) => el.getAttribute('data-a11y-marker')); + expect(codes).toEqual(['image-alt', 'image-alt', 'color-contrast']); + }); + + it('colors markers by finding type', () => { + service.render(iframe, GROUPS); + const markers = Array.from( + iframe.contentDocument?.querySelectorAll<HTMLElement>(MARKER_SELECTOR) ?? [] + ); + // error → orange, warning → red + expect(markers[0].style.border).toContain('#f59e0b'); + expect(markers[2].style.border).toContain('#dc2626'); + }); + + it('does not draw overlay boxes for landmark/structural rules (region)', () => { + const regionGroup: A11yGroup = { + code: 'region', + type: 'warning', + message: 'All page content should be contained by landmarks', + impact: 'moderate', + helpUrl: '', + items: [{ context: '<section>', selector: 'section.landmarkless' }], + count: 1 + }; + service.render(iframe, [...GROUPS, regionGroup]); + + const codes = Array.from( + iframe.contentDocument?.querySelectorAll(MARKER_SELECTOR) ?? [] + ).map((el) => el.getAttribute('data-a11y-marker')); + // Element-scoped rules still draw; `region` is suppressed. + expect(codes).toEqual(['image-alt', 'image-alt', 'color-contrast']); + expect(codes).not.toContain('region'); + }); + + it('skips selectors that do not match any element', () => { + service.render(iframe, [ + { ...GROUPS[0], items: [{ context: '', selector: '.does-not-exist' }], count: 1 } + ]); + expect(iframe.contentDocument?.querySelectorAll(MARKER_SELECTOR).length).toBe(0); + }); + + it('ignores invalid selectors without throwing', () => { + expect(() => + service.render(iframe, [ + { ...GROUPS[0], items: [{ context: '', selector: ')))bad' }], count: 1 } + ]) + ).not.toThrow(); + expect(iframe.contentDocument?.querySelectorAll(MARKER_SELECTOR).length).toBe(0); + }); + + it('clears prior markers before re-rendering (no duplicates)', () => { + service.render(iframe, GROUPS); + service.render(iframe, GROUPS); + expect(iframe.contentDocument?.querySelectorAll(MARKER_SELECTOR).length).toBe(3); + }); + + it('clear() removes all markers', () => { + service.render(iframe, GROUPS); + service.clear(iframe); + expect(iframe.contentDocument?.querySelectorAll(MARKER_SELECTOR).length).toBe(0); + }); + + it('no-ops when the iframe is null', () => { + expect(() => service.render(null, GROUPS)).not.toThrow(); + expect(() => service.clear(null)).not.toThrow(); + }); +}); diff --git a/core-web/libs/portlets/dot-agents/src/lib/agents/a11y/services/a11y-marker.service.ts b/core-web/libs/portlets/dot-agents/src/lib/agents/a11y/services/a11y-marker.service.ts new file mode 100644 index 000000000000..6617236d7db6 --- /dev/null +++ b/core-web/libs/portlets/dot-agents/src/lib/agents/a11y/services/a11y-marker.service.ts @@ -0,0 +1,183 @@ +import { Injectable } from '@angular/core'; + +import { A11yGroup } from '@dotcms/portlets/dot-ema/ui'; + +/** Attribute tagging marker elements so we can find + clear them. */ +const MARKER_ATTR = 'data-a11y-marker'; +/** Id of the single overlay layer the markers live in. */ +const LAYER_ID = 'dot-a11y-marker-layer'; + +/** + * axe rules we do NOT draw overlay boxes for. These are structural / landmark + * rules: they fire on large page sections (or the absence of a landmark), so an + * outline spans whole regions and reads as noise rather than pointing at a + * discrete problem — and the fix ("wrap in a <main>") isn't something a viewer + * locates by looking at a highlighted rectangle. They still surface as text in + * the activity log + needs-review list; only the *marker* is suppressed. Keep in + * sync with the landmark family so a sibling rule can't reintroduce the noise. + */ +const NON_VISUAL_RULES: ReadonlySet<string> = new Set([ + 'region', + 'landmark-one-main', + 'landmark-unique', + 'landmark-complementary-is-top-level', + 'landmark-no-duplicate-banner', + 'landmark-no-duplicate-contentinfo', + 'landmark-main-is-top-level', + 'landmark-banner-is-top-level', + 'landmark-contentinfo-is-top-level', + 'page-has-heading-one' +]); + +/** Outline colors matching the preview-pane legend. */ +const COLOR = { + error: { outline: '#f59e0b', fill: 'rgba(245,158,11,.12)' }, // detected + warning: { outline: '#dc2626', fill: 'rgba(220,38,38,.10)' } // needs attention +}; + +/** + * Draws accessibility violation markers *inside* a same-origin preview iframe. + * + * For each axe finding we resolve its CSS `selector` against the iframe document + * and append an absolutely-positioned highlight box into the iframe's own body — + * so markers scroll with the page content for free (no parent-side reposition). + * + * The iframe must be same-origin (it is: the preview loads through the + * `/dot-page` dev proxy / the BE origin in prod). If `contentDocument` is null + * (cross-origin / not yet loaded) the methods no-op safely. + */ +@Injectable() +export class A11yMarkerService { + /** Remove any markers previously injected, then draw one per flagged element. */ + render(iframe: HTMLIFrameElement | null | undefined, groups: A11yGroup[]): void { + const doc = this.#getDocument(iframe); + if (!doc?.body) { + return; + } + + const layer = this.#ensureLayer(doc); + layer.replaceChildren(); + + for (const group of groups) { + // Landmark/structural rules span whole sections — outlining them is + // noise. They stay in the log/needs-review list; just no overlay box. + if (NON_VISUAL_RULES.has(group.code)) { + continue; + } + const palette = COLOR[group.type]; + for (const item of group.items) { + const el = this.#safeQuery(doc, item.selector); + if (!el || this.#isRootLevel(el)) { + // Skip <html>/<body> and page-spanning roots — outlining the + // whole page is noise, not a useful marker. + continue; + } + const marker = this.#buildMarker(doc, el, group, palette); + if (marker) { + layer.appendChild(marker); + } + } + } + } + + /** Remove all injected markers from the iframe (e.g. before a re-scan). */ + clear(iframe: HTMLIFrameElement | null | undefined): void { + const doc = this.#getDocument(iframe); + if (doc) { + this.#clearIn(doc); + } + } + + #buildMarker( + doc: Document, + el: Element, + group: A11yGroup, + palette: { outline: string; fill: string } + ): HTMLElement | null { + const rect = el.getBoundingClientRect(); + // Skip elements that aren't laid out yet (0×0) — re-render on next load. + if (rect.width === 0 && rect.height === 0) { + return null; + } + + const win = doc.defaultView; + // getBoundingClientRect() is viewport-relative; add the page scroll to get + // document-space coordinates. The layer's containing block is the initial + // containing block (it's on <html>), so these align regardless of body margin. + const scrollX = win?.scrollX ?? doc.documentElement.scrollLeft; + const scrollY = win?.scrollY ?? doc.documentElement.scrollTop; + + const marker = doc.createElement('div'); + marker.setAttribute(MARKER_ATTR, group.code); + marker.title = `${group.code} (${group.impact ?? group.type})`; + Object.assign(marker.style, { + position: 'absolute', + top: `${rect.top + scrollY}px`, + left: `${rect.left + scrollX}px`, + width: `${rect.width}px`, + height: `${rect.height}px`, + boxSizing: 'border-box', + border: `2px solid ${palette.outline}`, + borderRadius: '3px', + background: palette.fill, + pointerEvents: 'none' + }); + return marker; + } + + /** + * The single full-document overlay layer the markers live in. Positioned on + * <html> so its containing block is the initial containing block — document + * coordinates map 1:1, unaffected by body margins/padding. + */ + #ensureLayer(doc: Document): HTMLElement { + let layer = doc.getElementById(LAYER_ID); + if (!layer) { + layer = doc.createElement('div'); + layer.id = LAYER_ID; + Object.assign(layer.style, { + position: 'absolute', + top: '0', + left: '0', + width: '0', + height: '0', + pointerEvents: 'none', + zIndex: '2147483646' + }); + doc.documentElement.appendChild(layer); + } + return layer; + } + + /** True for <html>/<body> — page-spanning roots we don't want to outline. */ + #isRootLevel(el: Element): boolean { + const tag = el.tagName?.toLowerCase(); + return tag === 'html' || tag === 'body'; + } + + #clearIn(doc: Document): void { + doc.getElementById(LAYER_ID)?.remove(); + } + + /** Access the iframe document; null when cross-origin or not yet loaded. */ + #getDocument(iframe: HTMLIFrameElement | null | undefined): Document | null { + try { + return iframe?.contentDocument ?? null; + } catch { + // Cross-origin access throws — markers are unavailable in that case. + return null; + } + } + + /** axe selectors can be exotic; guard against invalid querySelector input. */ + #safeQuery(doc: Document, selector: string): Element | null { + if (!selector) { + return null; + } + try { + return doc.querySelector(selector); + } catch { + return null; + } + } +} diff --git a/core-web/libs/portlets/dot-agents/src/lib/agents/a11y/services/dot-a11y-agent.service.spec.ts b/core-web/libs/portlets/dot-agents/src/lib/agents/a11y/services/dot-a11y-agent.service.spec.ts new file mode 100644 index 000000000000..ad5d8477e557 --- /dev/null +++ b/core-web/libs/portlets/dot-agents/src/lib/agents/a11y/services/dot-a11y-agent.service.spec.ts @@ -0,0 +1,168 @@ +import { createServiceFactory, mockProvider, SpectatorService } from '@openng/spectator/jest'; +import { of } from 'rxjs'; + +import { DotAgentRunService } from '@dotcms/data-access'; +import { AgentStreamEvent } from '@dotcms/dotcms-models'; + +import { DotA11yAgentService } from './dot-a11y-agent.service'; + +import { AgentFixRequest, FixReport } from '../models/accessibility-studio.models'; + +const REQUEST: AgentFixRequest = { + identifier: 'id-1', + languageId: 1, + skipCss: false +}; + +const FIX_REPORT: FixReport = { + runId: 'r_1', + page: { uri: '/index', host: 'demo.dotcms.com', languageId: 1 }, + scan: { before: { violations: 28 }, after: { violations: 20 } }, + results: [{ ruleId: 'image-alt', status: 'reported' }], + changedFiles: [], + publishRequired: true +}; + +describe('DotA11yAgentService', () => { + let spectator: SpectatorService<DotA11yAgentService>; + let service: DotA11yAgentService; + let runService: jest.Mocked<DotAgentRunService>; + + const createService = createServiceFactory({ + service: DotA11yAgentService, + providers: [ + mockProvider(DotAgentRunService, { + run: jest.fn().mockReturnValue(of()), + stop: jest.fn().mockReturnValue(of()) + }) + ] + }); + + beforeEach(() => { + spectator = createService(); + service = spectator.service; + runService = spectator.inject(DotAgentRunService) as jest.Mocked<DotAgentRunService>; + }); + + it('fixStream delegates to the generic run service with the a11y stream endpoint', () => { + service.fixStream(REQUEST).subscribe(); + expect(runService.run).toHaveBeenCalledWith('/api/v1/agents/a11y/fix/stream', REQUEST); + }); + + it('stop delegates to the run service with the endpoint and the run id in the body', () => { + service.stop('r_123').subscribe(); + expect(runService.stop).toHaveBeenCalledWith('/api/v1/agents/a11y/stop', { + runId: 'r_123' + }); + }); + + it('unwraps the { report } wrapper on the done event → bare FixReport as result', () => { + runService.run.mockReturnValue( + of({ type: 'done', result: { report: FIX_REPORT } } as AgentStreamEvent<unknown>) + ); + let received: unknown; + service.fixStream(REQUEST).subscribe((e) => (received = e)); + expect(received).toEqual({ type: 'done', result: FIX_REPORT }); + }); + + it('unwraps the aborted event the same way (partial report)', () => { + runService.run.mockReturnValue( + of({ type: 'aborted', result: { report: FIX_REPORT } } as AgentStreamEvent<unknown>) + ); + let received: unknown; + service.fixStream(REQUEST).subscribe((e) => (received = e)); + expect(received).toEqual({ type: 'aborted', result: FIX_REPORT }); + }); + + it('accepts a bare report payload (no wrapper) unchanged', () => { + runService.run.mockReturnValue( + of({ type: 'done', result: FIX_REPORT } as AgentStreamEvent<unknown>) + ); + let received: unknown; + service.fixStream(REQUEST).subscribe((e) => (received = e)); + expect(received).toEqual({ type: 'done', result: FIX_REPORT }); + }); + + it('yields null for a status-only terminal payload rather than a report-shaped lie', () => { + // `aborted` may carry only a status. The old double cast asserted FixReport onto + // any truthy payload, so consumers read `report.scan` on an object without one and + // threw during change detection, blanking the whole run pane. + runService.run.mockReturnValue( + of({ type: 'aborted', result: { status: 'cancelled' } } as AgentStreamEvent<unknown>) + ); + let received: unknown; + service.fixStream(REQUEST).subscribe((e) => (received = e)); + expect(received).toEqual({ type: 'aborted', result: null }); + }); + + it('yields null for a wrapped payload whose report has no scan', () => { + runService.run.mockReturnValue( + of({ + type: 'done', + result: { report: { runId: 'r_1', results: [] } } + } as AgentStreamEvent<unknown>) + ); + let received: unknown; + service.fixStream(REQUEST).subscribe((e) => (received = e)); + expect(received).toEqual({ type: 'done', result: null }); + }); + + it('yields null for a null or non-object terminal payload', () => { + for (const payload of [null, 'done', 42]) { + runService.run.mockReturnValue( + of({ type: 'done', result: payload } as AgentStreamEvent<unknown>) + ); + let received: unknown; + service.fixStream(REQUEST).subscribe((e) => (received = e)); + expect(received).toEqual({ type: 'done', result: null }); + } + }); + + it('passes non-terminal events (phase / progress / workingChanged) through untouched', () => { + const events = [ + { type: 'phase', step: { message: 'scanning', meta: { phase: 'scan' } } }, + { type: 'progress', progress: { baseline: 29, current: 3, cleared: 26 } }, + { + type: 'workingChanged', + changedFiles: [{ path: '//site/a.css', identifier: 'id-a' }] + } + ] as AgentStreamEvent<unknown>[]; + + for (const event of events) { + runService.run.mockReturnValue(of(event)); + let received: unknown; + service.fixStream(REQUEST).subscribe((e) => (received = e)); + expect(received).toEqual(event); + } + }); + + it('unwraps a real backend done payload so the report fields are readable', () => { + // The exact wrapper shape the agent sends: { report: { scan, results, … } }. + const backendPayload = { + report: { + runId: 'r_abc', + page: { uri: '/index', host: 'awazon.local', languageId: 1 }, + scan: { before: { violations: 28 }, after: { violations: 28 } }, + results: [ + { ruleId: 'color-contrast', status: 'reported', reason: 'no match' }, + { ruleId: 'agentic-research', status: 'fixed-to-working', file: '/a.vtl' } + ], + changedFiles: [{ path: '/a.vtl', identifier: 'id-a' }], + publishRequired: true + } + }; + runService.run.mockReturnValue( + of({ type: 'done', result: backendPayload } as AgentStreamEvent<unknown>) + ); + let report: FixReport | undefined; + service.fixStream(REQUEST).subscribe((e) => { + if (e.type === 'done') { + report = e.result; + } + }); + // The store + presenter read these directly — they must resolve. + expect(report?.scan.after.violations).toBe(28); + expect(report?.results).toHaveLength(2); + expect(report?.results.filter((r) => r.status === 'fixed-to-working')).toHaveLength(1); + }); +}); diff --git a/core-web/libs/portlets/dot-agents/src/lib/agents/a11y/services/dot-a11y-agent.service.ts b/core-web/libs/portlets/dot-agents/src/lib/agents/a11y/services/dot-a11y-agent.service.ts new file mode 100644 index 000000000000..c16a47b779c2 --- /dev/null +++ b/core-web/libs/portlets/dot-agents/src/lib/agents/a11y/services/dot-a11y-agent.service.ts @@ -0,0 +1,114 @@ +import { Observable } from 'rxjs'; + +import { Injectable, inject } from '@angular/core'; + +import { map } from 'rxjs/operators'; + +import { DotAgentRunService } from '@dotcms/data-access'; + +import { + A11yAgentStreamEvent, + AgentFixRequest, + FixReport +} from '../models/accessibility-studio.models'; + +/** + * Talks to the ai-agents a11y-fix agent. + * + * A thin, a11y-specific wrapper over the generic {@link DotAgentRunService}: it + * owns only the a11y proxy endpoints and the `FixReport` result type. The agent + * runs the fix loop and streams its progress over Server-Sent Events (parsed by + * the generic run service): + * event: step → { phase, message } (live, many) + * event: done → { ...FixReport } (terminal, the run report) + * event: aborted → { ...FixReport } (terminal, partial report after stop) + * event: error → { message } (terminal) + * + * Calls go same-origin to the dotCMS proxy resource at `/api/v1/agents/a11y/*`; + * the Java proxy authenticates the session, mints a short-lived JWT, resolves the + * page, and streams the agent response back. The browser never holds a token — + * the proxy is the auth boundary. + */ + +/** dotCMS proxy resource — the browser's same-origin entry point. */ +const AGENT_BASE = '/api/v1/agents/a11y'; + +@Injectable() +export class DotA11yAgentService { + readonly #runService = inject(DotAgentRunService); + + /** + * Ask the agent to stop a specific in-flight run (cooperative). The agent + * identifies the run by `runId` — the id it announced on the stream's first + * frame (the `run` event). It stops at the next safe checkpoint and the open + * SSE stream emits a terminal `aborted` event with the partial report. 202 if + * a run was signalled, 404 if none — both are fine here, so errors are + * swallowed by the caller. + */ + stop(runId: string): Observable<unknown> { + return this.#runService.stop(`${AGENT_BASE}/stop`, { runId }); + } + + /** + * Run the fix loop, streaming each agent step. The observable emits one + * {@link A11yAgentStreamEvent} per SSE event and completes after + * `done`/`aborted`/`error` (or when the caller unsubscribes, which aborts the + * in-flight request). + * + * The agent's terminal `done`/`aborted` payload wraps the report as + * `{ report: FixReport }`; we unwrap it here so downstream consumers receive + * the bare {@link FixReport} as `result` (the generic transport is agent- + * agnostic and passes the whole payload through untouched). + */ + fixStream(request: AgentFixRequest): Observable<A11yAgentStreamEvent> { + return this.#runService + .run<FixReport | { report: FixReport } | null>(`${AGENT_BASE}/fix/stream`, request) + .pipe( + map((event): A11yAgentStreamEvent => { + if (event.type === 'done' || event.type === 'aborted') { + return { ...event, result: unwrapReport(event.result) }; + } + + return event; + }) + ); + } +} + +/** + * Unwrap the agent's terminal payload to a bare {@link FixReport}. The agent + * sends `{ report: FixReport }`; older/other shapes may send the report directly, + * so accept both. + * + * Narrowed with an `in` check rather than a double cast. The old form asserted a + * `FixReport` onto ANY truthy payload, so a status-only terminal frame (`aborted` carries + * one) became a `report` with no `scan`, and every count computed off `report.scan` threw + * during change detection — taking the score widget, footer and donut down together and + * leaving a blank pane. Returning null lets the store keep its pre-run figures instead. + */ +function unwrapReport(payload: unknown): FixReport | null { + if (!payload || typeof payload !== 'object') { + return null; + } + + const candidate = 'report' in payload ? (payload as { report?: unknown }).report : payload; + + // `scan` is what every derived count reads; a payload without it is a status frame, + // not a report, however much of the rest it happens to carry. + return isFixReport(candidate) ? candidate : null; +} + +/** Whether a terminal payload actually carries the report shape the widgets read. */ +function isFixReport(value: unknown): value is FixReport { + if (!value || typeof value !== 'object') { + return false; + } + const scan = (value as { scan?: unknown }).scan; + + return ( + !!scan && + typeof scan === 'object' && + 'before' in (scan as object) && + 'after' in (scan as object) + ); +} diff --git a/core-web/libs/portlets/dot-agents/src/lib/agents/a11y/services/dot-page-sources.service.spec.ts b/core-web/libs/portlets/dot-agents/src/lib/agents/a11y/services/dot-page-sources.service.spec.ts new file mode 100644 index 000000000000..5028013df843 --- /dev/null +++ b/core-web/libs/portlets/dot-agents/src/lib/agents/a11y/services/dot-page-sources.service.spec.ts @@ -0,0 +1,356 @@ +import { createHttpFactory, HttpMethod, SpectatorHttp } from '@openng/spectator/jest'; + +import { DotPageSourcesService } from './dot-page-sources.service'; + +import { PageRenderSourcesView, PageSourceFile } from '../models/page-render-sources.models'; + +/** A `_render-sources` response with a theme CSS, a container VTL, and a widget VTL. */ +const RENDER_SOURCES: PageRenderSourcesView = { + page: { identifier: 'page-1', uri: '//demo/index', languageId: 1 }, + theme: { + id: 'theme-1', + name: 'awazon', + folderPath: '//demo/application/themes/awazon/', + files: [ + { + path: '//demo/application/themes/awazon/css/awazon.css', + identifier: 'css-1', + extension: 'css' + }, + { + path: '//demo/application/themes/awazon/header.vtl', + identifier: 'vtl-theme-1', + extension: 'vtl' + } + ] + }, + containers: { + '//demo/application/containers/awazon-content/': { + source: 'FILE', + contentTypes: [ + { + contentTypeVar: 'AwazonBookList', + path: '//demo/application/containers/awazon-content/AwazonBookList.vtl', + identifier: 'vtl-container-1' + }, + // A DB content type — no file, must be dropped. + { contentTypeVar: 'AwazonNoFile' } + ] + } + }, + widgets: [ + { + contentTypeVar: 'AwazonNewsletter', + title: 'Newsletter', + contentletId: 'w-1', + contentletInode: 'w-inode-1', + source: 'FILE', + path: '//demo/application/containers/awazon-content/AwazonNewsletter.vtl', + identifier: 'vtl-widget-1' + }, + // A CODE widget — no file, must be dropped. + { + contentTypeVar: 'AwazonInline', + title: 'Inline', + contentletId: 'w-2', + contentletInode: 'w-inode-2', + source: 'CODE' + } + ] +}; + +/** Build a versions response grouped by ISO code (as the backend returns it). */ +function versionsResponse( + rows: Array<{ inode: string; working?: boolean; live?: boolean; languageId?: number }> +) { + return { + entity: { + versions: { + 'en-us': rows.map((r) => ({ + inode: r.inode, + identifier: 'ignored', + working: !!r.working, + live: !!r.live, + languageId: r.languageId ?? 1, + fileAssetVersion: `/dA/${r.inode}/fileAsset/file.vtl` + })) + } + } + }; +} + +describe('DotPageSourcesService', () => { + let spectator: SpectatorHttp<DotPageSourcesService>; + + const createHttp = createHttpFactory(DotPageSourcesService); + + beforeEach(() => { + spectator = createHttp(); + }); + + describe('getPageSources', () => { + it('flattens theme, FILE container, and FILE widget files (dropping DB/CODE)', () => { + let result: PageSourceFile[] = []; + spectator.service.getPageSources('/index', 'host-1', 1).subscribe((r) => (result = r)); + + const req = spectator.expectOne( + '/api/v1/page/_render-sources/index?host_id=host-1&language_id=1', + HttpMethod.GET + ); + req.flush({ entity: RENDER_SOURCES }); + + // css-1 + vtl-theme-1 (theme) + vtl-container-1 (FILE container) + vtl-widget-1 (FILE widget) + expect(result.map((f) => f.identifier).sort()).toEqual([ + 'css-1', + 'vtl-container-1', + 'vtl-theme-1', + 'vtl-widget-1' + ]); + // The DB content type (AwazonNoFile) and CODE widget (AwazonInline) are dropped. + expect(result.length).toBe(4); + }); + + it('derives name, extension, and origin per file', () => { + let result: PageSourceFile[] = []; + spectator.service.getPageSources('/index', 'host-1', 1).subscribe((r) => (result = r)); + spectator + .expectOne( + '/api/v1/page/_render-sources/index?host_id=host-1&language_id=1', + HttpMethod.GET + ) + .flush({ entity: RENDER_SOURCES }); + + const css = result.find((f) => f.identifier === 'css-1'); + expect(css).toMatchObject({ name: 'awazon.css', extension: 'css', origin: 'theme' }); + + const widget = result.find((f) => f.identifier === 'vtl-widget-1'); + expect(widget).toMatchObject({ + name: 'AwazonNewsletter.vtl', + extension: 'vtl', + origin: 'widget' + }); + }); + + it('strips a leading slash from the uri before the render-sources path', () => { + spectator.service.getPageSources('/about/team', 'host-1', 2).subscribe(); + spectator.expectOne( + '/api/v1/page/_render-sources/about/team?host_id=host-1&language_id=2', + HttpMethod.GET + ); + }); + }); + + describe('getDiffFiles', () => { + const files: PageSourceFile[] = [ + { + identifier: 'vtl-1', + path: '//demo/a.vtl', + name: 'a.vtl', + extension: 'vtl', + origin: 'container' + } + ]; + + it('returns only files whose working and live text differ, with line counts', () => { + let result = null as unknown; + spectator.service.getDiffFiles(files, 1).subscribe((r) => (result = r)); + + // versions lookup for the file + spectator + .expectOne( + '/api/v1/content/versions?identifier=vtl-1&groupByLang=1', + HttpMethod.GET + ) + .flush( + versionsResponse([ + { inode: 'working-inode', working: true }, + { inode: 'live-inode', live: true } + ]) + ); + + // working + live text fetches run concurrently (forkJoin) — grab both + // via the backend matcher so neither auto-verify races the other. + spectator.controller + .match('/dA/working-inode/fileAsset/file.vtl')[0] + .flush('line1\nline2\nnewline'); + spectator.controller + .match('/dA/live-inode/fileAsset/file.vtl')[0] + .flush('line1\nline2'); + + expect(result).toEqual([ + expect.objectContaining({ + identifier: 'vtl-1', + working: 'line1\nline2\nnewline', + live: 'line1\nline2', + added: 1, + removed: 0 + }) + ]); + }); + + it('drops files whose working and live text are identical', () => { + let result = null as unknown; + spectator.service.getDiffFiles(files, 1).subscribe((r) => (result = r)); + + spectator + .expectOne( + '/api/v1/content/versions?identifier=vtl-1&groupByLang=1', + HttpMethod.GET + ) + .flush( + versionsResponse([ + { inode: 'working-inode', working: true }, + { inode: 'live-inode', live: true } + ]) + ); + spectator.controller.match('/dA/working-inode/fileAsset/file.vtl')[0].flush('same'); + spectator.controller.match('/dA/live-inode/fileAsset/file.vtl')[0].flush('same'); + + expect(result).toEqual([]); + }); + + it('treats a working-only file (no live version) as an all-added diff', () => { + let result = null as unknown; + spectator.service.getDiffFiles(files, 1).subscribe((r) => (result = r)); + + spectator + .expectOne( + '/api/v1/content/versions?identifier=vtl-1&groupByLang=1', + HttpMethod.GET + ) + .flush(versionsResponse([{ inode: 'working-inode', working: true }])); + + // Only the working text is fetched; live falls back to empty. + spectator + .expectOne('/dA/working-inode/fileAsset/file.vtl', HttpMethod.GET) + .flush('a\nb'); + + expect(result).toEqual([ + expect.objectContaining({ working: 'a\nb', live: '', added: 2, removed: 0 }) + ]); + }); + + it('filters versions to the requested languageId', () => { + let result = null as unknown; + spectator.service.getDiffFiles(files, 2).subscribe((r) => (result = r)); + + // Only a lang-1 working version exists; none for lang 2 → no working → dropped. + spectator + .expectOne( + '/api/v1/content/versions?identifier=vtl-1&groupByLang=1', + HttpMethod.GET + ) + .flush( + versionsResponse([{ inode: 'working-inode', working: true, languageId: 1 }]) + ); + + expect(result).toEqual([]); + }); + + it('skips a file whose versions request errors, without aborting', () => { + let result = null as unknown; + spectator.service.getDiffFiles(files, 1).subscribe((r) => (result = r)); + + spectator + .expectOne( + '/api/v1/content/versions?identifier=vtl-1&groupByLang=1', + HttpMethod.GET + ) + .flush('boom', { status: 500, statusText: 'Server Error' }); + + expect(result).toEqual([]); + }); + + it('drops a file whose working text fails to load, rather than showing a deletion', () => { + // A 502 on the working version used to map to `''`, which diffs as every line + // removed — a whole-file deletion the agent never made, in the one panel whose + // job is to be the trustworthy account of what changed before publish. + let result = null as unknown; + spectator.service.getDiffFiles(files, 1).subscribe((r) => (result = r)); + + spectator + .expectOne( + '/api/v1/content/versions?identifier=vtl-1&groupByLang=1', + HttpMethod.GET + ) + .flush( + versionsResponse([ + { inode: 'working-inode', working: true }, + { inode: 'live-inode', live: true } + ]) + ); + spectator.controller + .match('/dA/working-inode/fileAsset/file.vtl')[0] + .flush('nope', { status: 502, statusText: 'Bad Gateway' }); + spectator.controller + .match('/dA/live-inode/fileAsset/file.vtl')[0] + .flush('line one\nline two'); + + expect(result).toEqual([]); + }); + + it('drops a file whose live text fails to load', () => { + let result = null as unknown; + spectator.service.getDiffFiles(files, 1).subscribe((r) => (result = r)); + + spectator + .expectOne( + '/api/v1/content/versions?identifier=vtl-1&groupByLang=1', + HttpMethod.GET + ) + .flush( + versionsResponse([ + { inode: 'working-inode', working: true }, + { inode: 'live-inode', live: true } + ]) + ); + spectator.controller.match('/dA/working-inode/fileAsset/file.vtl')[0].flush('new'); + spectator.controller + .match('/dA/live-inode/fileAsset/file.vtl')[0] + .flush('nope', { status: 500, statusText: 'Server Error' }); + + expect(result).toEqual([]); + }); + + it('prefers assetVersion over fileAssetVersion, matching getFileVersion', () => { + // The local copy had these the other way round while its docblock claimed to + // mirror the util, so a contentlet carrying both made the diff viewer show a + // different version of the file than every other admin surface. + let result = null as unknown; + spectator.service.getDiffFiles(files, 1).subscribe((r) => (result = r)); + + spectator + .expectOne( + '/api/v1/content/versions?identifier=vtl-1&groupByLang=1', + HttpMethod.GET + ) + .flush({ + entity: { + versions: { + 'en-us': [ + { + inode: 'working-inode', + working: true, + live: false, + languageId: 1, + assetVersion: '/dA/canonical/fileAsset/file.vtl', + fileAssetVersion: '/dA/other/fileAsset/file.vtl' + } + ] + } + } + }); + + spectator.expectOne('/dA/canonical/fileAsset/file.vtl', HttpMethod.GET).flush('a'); + spectator.controller.verify(); + expect(result).toEqual([expect.objectContaining({ working: 'a', live: '' })]); + }); + + it('returns an empty array for an empty file list without any HTTP', () => { + let result = null as unknown; + spectator.service.getDiffFiles([], 1).subscribe((r) => (result = r)); + expect(result).toEqual([]); + spectator.controller.verify(); + }); + }); +}); diff --git a/core-web/libs/portlets/dot-agents/src/lib/agents/a11y/services/dot-page-sources.service.ts b/core-web/libs/portlets/dot-agents/src/lib/agents/a11y/services/dot-page-sources.service.ts new file mode 100644 index 000000000000..f68459e266fc --- /dev/null +++ b/core-web/libs/portlets/dot-agents/src/lib/agents/a11y/services/dot-page-sources.service.ts @@ -0,0 +1,294 @@ +import { forkJoin, Observable, of } from 'rxjs'; + +import { HttpClient } from '@angular/common/http'; +import { Injectable, inject } from '@angular/core'; + +import { catchError, map, switchMap } from 'rxjs/operators'; + +import { DotCMSAPIResponse, DotCMSContentlet } from '@dotcms/dotcms-models'; +import { getFileVersion } from '@dotcms/utils'; + +import { + ContainerSourceView, + PageDiffFile, + PageRenderSourcesView, + PageSourceFile, + ThemeSourceView, + WidgetSourceView +} from '../models/page-render-sources.models'; + +/** + * Resolves the "working vs live" file diff for a page. + * + * The flow is two-staged and entirely read-only: + * 1. {@link getPageSources} → `_render-sources` for the page, flattened + de-duped + * into the file assets that compose it (theme VTL/CSS, container VTLs, widget VTLs). + * 2. {@link getDiffFiles} → for each file, look up its versions, fetch the working + * and live text, and keep only the files whose two versions actually differ. + * + * All calls go same-origin to the dotCMS backend (the dev server proxies `/api` + * and `/dA` to :8080), so no dev-only path prefix is needed here — unlike the + * run screen's iframes, which render pages via the `/dot-page` proxy sentinel. + */ +@Injectable() +export class DotPageSourcesService { + readonly #http = inject(HttpClient); + + /** + * Fetch the page's source files via `_render-sources` and flatten them into + * a de-duplicated list of file assets (by identifier). Only files backed by a + * real file asset (with an identifier) survive — inline CODE widgets and DB + * containers have no file to diff and are dropped. + * + * @param uri Plain page path, e.g. `/index` or `/about/team` (no host). + * @param hostId Host identifier to disambiguate the site (`host_id` query param). + * @param languageId Language id for the render-sources lookup. + */ + getPageSources(uri: string, hostId: string, languageId: number): Observable<PageSourceFile[]> { + // JAX-RS binds `{uri: .*}` with the leading slash stripped; the backend + // re-adds it. Send the path without a leading slash so it slots cleanly + // after `_render-sources/`. + const path = uri.startsWith('/') ? uri.slice(1) : uri; + const params = new URLSearchParams({ + host_id: hostId, + language_id: String(languageId) + }); + + return this.#http + .get< + DotCMSAPIResponse<PageRenderSourcesView> + >(`/api/v1/page/_render-sources/${path}?${params.toString()}`) + .pipe(map((response) => flattenSources(response?.entity))); + } + + /** + * Resolve the working-vs-live diff for every page source file: fetch each + * file's versions, pull the working + live text, and return only the files + * whose two versions differ (with per-file added/removed line counts). + * + * Files that fail to resolve (deleted, no versions, fetch error) are skipped + * rather than aborting the whole diff — a single bad asset shouldn't blank the + * screen. The result is ordered theme → container → widget, then by name. + * + * @param files The flattened source files from {@link getPageSources}. + * @param languageId Numeric language id — versions are filtered to this language + * by each version's own `languageId`, so no id→iso mapping is needed. + */ + getDiffFiles(files: PageSourceFile[], languageId: number): Observable<PageDiffFile[]> { + if (!files.length) { + return of([]); + } + + return forkJoin(files.map((file) => this.#resolveDiffFile(file, languageId))).pipe( + map((results) => + results.filter((f): f is PageDiffFile => f !== null).sort(byOriginThenName) + ) + ); + } + + /** + * Resolve a single file's working-vs-live diff, or `null` when it can't be + * diffed (no versions, missing working/live, fetch error, or identical text). + */ + #resolveDiffFile(file: PageSourceFile, languageId: number): Observable<PageDiffFile | null> { + return this.#getVersions(file.identifier, languageId).pipe( + switchMap((versions) => { + const working = versions.find((v) => v.working); + const live = versions.find((v) => v.live); + + // No working version → nothing the agent could have changed. No live + // version → a brand-new working-only file; still worth showing as an + // all-added diff against an empty "before". + if (!working) { + return of(null); + } + + const workingUrl = versionUrl(working); + const liveUrl = live ? versionUrl(live) : null; + if (!workingUrl) { + return of(null); + } + + return forkJoin({ + working: this.#fetchText(workingUrl), + // No live version is a fact (`''`, a brand-new working-only file); + // a failed fetch is not (`null`, handled below). + live: liveUrl ? this.#fetchText(liveUrl) : of<string | null>('') + }).pipe( + map(({ working: workingText, live: liveText }) => { + // Either side unknown → drop the file rather than diff against a + // guess. Showing it as a whole-file deletion would report an edit + // the agent never made, and the user's next move is Discard. + if (workingText === null || liveText === null) { + return null; + } + // Identical → not a change; drop it (the whole point is to + // surface only what the agent touched). + if (workingText === liveText) { + return null; + } + const { added, removed } = countLineChanges(liveText, workingText); + + return { ...file, working: workingText, live: liveText, added, removed }; + }) + ); + }), + // A single asset failing (deleted, 404, etc.) must not blank the diff. + catchError(() => of(null)) + ); + } + + /** + * All versions of a file asset for the given language, via `/api/v1/content/versions`. + * + * The response groups versions by language ISO code (e.g. `en-us`), which we + * don't have from the numeric page `languageId`. Rather than resolve id→iso, + * flatten every group and filter by each version's own `languageId` — the + * numeric id we already hold. + */ + #getVersions(identifier: string, languageId: number): Observable<DotCMSContentlet[]> { + return this.#http + .get< + DotCMSAPIResponse<{ versions: Record<string, DotCMSContentlet[]> }> + >(`/api/v1/content/versions?identifier=${identifier}&groupByLang=1`) + .pipe( + map((response) => { + const groups = response?.entity?.versions ?? {}; + + return Object.values(groups) + .flat() + .filter((version) => version.languageId === languageId); + }) + ); + } + + /** + * Fetch the raw text of a specific file version. The `fileAssetVersion` path + * (`/dA/<inode>/fileAsset/<name>`) is version-specific (the inode identifies + * the version), so GETting it returns that exact version's bytes. + * + * Returns `null` — NOT `''` — on failure. The two are opposite facts: `''` is a + * genuinely empty file, while `null` means we don't know what the file holds. Mapping + * a failure to `''` made a 502 on the working version render as a real diff deleting + * every line, which is the single most misleading thing this panel could show, since + * its entire job is to be the trustworthy account of what the agent changed. + */ + #fetchText(url: string): Observable<string | null> { + return this.#http.get(url, { responseType: 'text' }).pipe(catchError(() => of(null))); + } +} + +/** + * The versioned asset URL of a file-asset contentlet. + * + * Delegates to `getFileVersion` so the version-key precedence is identical to every other + * admin surface — a local copy that merely *said* it mirrored the util had the two keys + * the other way round, so whenever a contentlet carried both, the diff viewer fetched a + * different version of the file than the editor showed. `fileAsset` (the identifier-level + * path) stays as a last resort here; the util doesn't consider it. + */ +function versionUrl(contentlet: DotCMSContentlet): string | null { + return (getFileVersion(contentlet) as string) || (contentlet['fileAsset'] as string) || null; +} + +/** + * Flatten the `_render-sources` tree into a de-duplicated (by identifier) list of + * file-asset source files. Theme files, FILE-container content-type VTLs, and + * FILE widgets each contribute; DB containers and CODE widgets have no file. + */ +function flattenSources(view: PageRenderSourcesView | undefined): PageSourceFile[] { + if (!view) { + return []; + } + + const byId = new Map<string, PageSourceFile>(); + const add = ( + identifier: string | undefined, + path: string | undefined, + origin: PageSourceFile['origin'], + extension?: string + ) => { + if (!identifier || !path || byId.has(identifier)) { + return; + } + byId.set(identifier, { + identifier, + path, + name: basename(path), + extension: (extension ?? extensionOf(path)).toLowerCase(), + origin + }); + }; + + // Theme files (VTL, CSS, SCSS, …) — each carries its own extension. + (view.theme as ThemeSourceView | undefined)?.files?.forEach((f) => + add(f.identifier, f.path, 'theme', f.extension) + ); + + // Container VTLs — only FILE containers reference a file per content type. + Object.values(view.containers ?? {}).forEach((container: ContainerSourceView) => + container.contentTypes?.forEach((ct) => add(ct.identifier, ct.path, 'container')) + ); + + // Widget VTLs — only FILE widgets reference a file. + (view.widgets ?? []).forEach((w: WidgetSourceView) => add(w.identifier, w.path, 'widget')); + + return [...byId.values()]; +} + +/** Last path segment, e.g. `//host/a/b/header.vtl` → `header.vtl`. */ +function basename(path: string): string { + const parts = path.split('/').filter(Boolean); + + return parts.length ? parts[parts.length - 1] : path; +} + +/** Lowercased extension without the dot, e.g. `header.vtl` → `vtl`; `''` when none. */ +function extensionOf(path: string): string { + const name = basename(path); + const dot = name.lastIndexOf('.'); + + return dot > -1 ? name.slice(dot + 1) : ''; +} + +/** + * Count added / removed lines between two texts. A line present in `next` but not + * `prev` counts as added and vice-versa. This is a coarse multiset delta — enough + * for the +N / −M badges beside each file; Monaco owns the precise line-by-line + * rendering. + */ +function countLineChanges(prev: string, next: string): { added: number; removed: number } { + const prevLines = prev.length ? prev.split('\n') : []; + const nextLines = next.length ? next.split('\n') : []; + + const counts = new Map<string, number>(); + for (const line of prevLines) { + counts.set(line, (counts.get(line) ?? 0) + 1); + } + let added = 0; + for (const line of nextLines) { + const remaining = counts.get(line) ?? 0; + if (remaining > 0) { + counts.set(line, remaining - 1); + } else { + added++; + } + } + // Whatever prev lines were never matched by a next line were removed. + let removed = 0; + for (const remaining of counts.values()) { + removed += remaining; + } + + return { added, removed }; +} + +/** Sort diff files theme → container → widget, then alphabetically by name. */ +const ORIGIN_RANK: Record<PageSourceFile['origin'], number> = { + theme: 0, + container: 1, + widget: 2 +}; +function byOriginThenName(a: PageSourceFile, b: PageSourceFile): number { + return ORIGIN_RANK[a.origin] - ORIGIN_RANK[b.origin] || a.name.localeCompare(b.name); +} diff --git a/core-web/libs/portlets/dot-agents/src/lib/agents/a11y/store/a11y-page-list.store.spec.ts b/core-web/libs/portlets/dot-agents/src/lib/agents/a11y/store/a11y-page-list.store.spec.ts new file mode 100644 index 000000000000..407baa7e7401 --- /dev/null +++ b/core-web/libs/portlets/dot-agents/src/lib/agents/a11y/store/a11y-page-list.store.spec.ts @@ -0,0 +1,263 @@ +import { createServiceFactory, mockProvider, SpectatorService } from '@openng/spectator/jest'; +import { Observable, of, Subject, throwError } from 'rxjs'; + +import { signal } from '@angular/core'; + +import { + DotContentSearchService, + DotHttpErrorManagerService, + DotLanguagesService +} from '@dotcms/data-access'; +import { DotCMSContentlet, DotLanguage } from '@dotcms/dotcms-models'; +import { GlobalStore } from '@dotcms/store'; + +import { A11yPageListStore, pickDefaultLanguageId } from './a11y-page-list.store'; + +import { StudioPageRow } from '../models/accessibility-studio.models'; + +const MOCK_CONTENTLETS = [ + { + identifier: 'id-1', + title: 'About Us', + url: '/about-us', + contentType: 'htmlpageasset', + languageId: 1, + host: 'host-id-1', + hostName: 'demo.dotcms.com', + modDate: '04/09/2026', + modUserName: 'Admin User', + live: true + }, + { + identifier: 'id-2', + title: 'Blog Post', + url: '/blog/post/hello', + contentType: 'Blog', + languageId: 1, + host: 'host-id-1', + hostName: 'demo.dotcms.com', + modDate: '03/10/2026', + modUserName: 'Admin User', + live: false + } +] as unknown as DotCMSContentlet[]; + +const MOCK_SEARCH_ENTITY = { + jsonObjectView: { contentlets: MOCK_CONTENTLETS }, + resultsSize: 42 +}; + +const MOCK_ROW: StudioPageRow = { + identifier: 'id-1', + title: 'About Us', + path: '/about-us', + type: 'htmlpageasset', + languageId: 1, + hostId: 'host-id-1', + hostName: 'demo.dotcms.com', + modDate: '04/09/2026', + modUserName: 'Admin User', + live: true +}; + +describe('A11yPageListStore', () => { + let spectator: SpectatorService<InstanceType<typeof A11yPageListStore>>; + let store: InstanceType<typeof A11yPageListStore>; + let searchService: jest.Mocked<DotContentSearchService>; + let currentSiteIdSignal: ReturnType<typeof signal<string | null>>; + /** What `DotLanguagesService.get()` returns for the next store instance. */ + let languagesResponse: () => Observable<DotLanguage[]>; + + const createService = createServiceFactory({ + service: A11yPageListStore, + providers: [ + mockProvider(DotContentSearchService, { + get: jest.fn().mockReturnValue(of(MOCK_SEARCH_ENTITY)) + }), + mockProvider(DotHttpErrorManagerService, { + handle: jest.fn().mockReturnValue(of(null)) + }), + mockProvider(DotLanguagesService, { + get: jest.fn(() => languagesResponse()) + }), + mockProvider(GlobalStore, { + get currentSiteId() { + return currentSiteIdSignal; + } + }) + ] + }); + + beforeEach(() => { + jest.clearAllMocks(); + currentSiteIdSignal = signal<string | null>('site-1'); + languagesResponse = () => + of([ + { id: 1, defaultLanguage: true }, + { id: 2, defaultLanguage: false } + ] as DotLanguage[]); + spectator = createService(); + store = spectator.service; + searchService = spectator.inject( + DotContentSearchService + ) as jest.Mocked<DotContentSearchService>; + // The onInit effect loads the page list — this store is page-list-only, no gate. + spectator.flushEffects(); + }); + + it('loads + projects pages into rows on init', () => { + expect(searchService.get).toHaveBeenCalled(); + expect(store.pages().length).toBe(2); + expect(store.pages()[0]).toEqual(MOCK_ROW); + expect(store.totalRecords()).toBe(42); + expect(store.pageListStatus()).toBe('loaded'); + }); + + it('prefers the urlMap over url for the row path (URL-mapped content)', () => { + searchService.get.mockClear(); + searchService.get.mockReturnValueOnce( + of({ + jsonObjectView: { + contentlets: [ + { + ...MOCK_CONTENTLETS[1], + url: '/blog-detail-template', // detail template URL + urlMap: '/blog/post/hello' // the real navigable path + } + ] + }, + resultsSize: 1 + }) + ); + // Re-trigger a load with the urlMapped contentlet. + store.setFilter('hello'); + spectator.flushEffects(); + + expect(store.pages()[0].path).toBe('/blog/post/hello'); + }); + + it('builds a host- and language-scoped pages query', () => { + const query = (searchService.get.mock.calls[0][0] as { query: string }).query; + expect(query).toContain('+working:true'); + expect(query).toContain('+(urlmap:* OR basetype:5)'); + expect(query).toContain('+deleted:false'); + expect(query).toContain('+conhost:site-1'); + // Without this a multilingual site returns the same page once per language, + // identical in a table that renders no language column. + expect(query).toContain('+languageId:1'); + expect(query).not.toContain('title:'); + }); + + describe('pickDefaultLanguageId', () => { + it('picks the flagged default, not merely the first returned', () => { + // The endpoint's ordering is not a contract, and on a screen with no language + // column a silently non-default list would be invisible. + expect( + pickDefaultLanguageId([ + { id: 3, defaultLanguage: false }, + { id: 7, defaultLanguage: true } + ] as DotLanguage[]) + ).toBe(7); + }); + + it('falls back to the first entry when nothing is flagged', () => { + expect(pickDefaultLanguageId([{ id: 5 }, { id: 9 }] as DotLanguage[])).toBe(5); + }); + + it('falls back to language 1 for an empty or missing list', () => { + expect(pickDefaultLanguageId([])).toBe(1); + expect(pickDefaultLanguageId(null)).toBe(1); + }); + }); + + it('does not fetch until the current site is known, then fetches scoped', () => { + // Simulate the real boot order: site resolves AFTER init. + searchService.get.mockClear(); + currentSiteIdSignal.set(null); + spectator.flushEffects(); + expect(searchService.get).not.toHaveBeenCalled(); // no unscoped all-sites query + + currentSiteIdSignal.set('site-2'); + spectator.flushEffects(); + expect(searchService.get).toHaveBeenCalledTimes(1); + const query = (searchService.get.mock.calls[0][0] as { query: string }).query; + expect(query).toContain('+conhost:site-2'); + }); + + it('adds a title/path/urlmap clause when filtering', () => { + searchService.get.mockClear(); + store.setFilter('contact'); + spectator.flushEffects(); + + const query = (searchService.get.mock.calls[0][0] as { query: string }).query; + expect(query).toContain('+(title:contact* OR path:*contact* OR urlmap:*contact*)'); + expect(store.page()).toBe(1); + }); + + it('escapes Lucene special characters in the filter', () => { + searchService.get.mockClear(); + store.setFilter('a:b(c)'); + spectator.flushEffects(); + + const query = (searchService.get.mock.calls[0][0] as { query: string }).query; + expect(query).toContain('a\\:b\\(c\\)'); + }); + + it('keeps a multi-word filter inside one term instead of leaking a bare token', () => { + searchService.get.mockClear(); + store.setFilter('about us'); + spectator.flushEffects(); + + const query = (searchService.get.mock.calls[0][0] as { query: string }).query; + // A raw space would end the field-qualified term, leaving `us*` to run against + // the DEFAULT field and match users/usage instead of the intended path. + expect(query).not.toContain('path:*about us*'); + expect(query).toContain('+(title:about?us* OR path:*about?us* OR urlmap:*about?us*)'); + }); + + it('translates pagination into limit/offset', () => { + searchService.get.mockClear(); + store.setPagination(3, 10); + spectator.flushEffects(); + + const params = searchService.get.mock.calls[0][0] as { + limit: number; + offset: number; + }; + expect(params.limit).toBe(10); + expect(params.offset).toBe(20); + }); + + it('handles a search error without throwing', () => { + const errorManager = spectator.inject(DotHttpErrorManagerService); + searchService.get.mockReturnValueOnce(throwError(() => new Error('boom'))); + store.setFilter('err'); + spectator.flushEffects(); + + expect(errorManager.handle).toHaveBeenCalled(); + expect(store.pageListStatus()).toBe('error'); + }); + + it('supersedes an in-flight search so a slow earlier response cannot win', () => { + // Type "blog", then page before it lands. If the FIRST response were allowed to + // write, the table would show page 1's rows while the paginator showed page 2. + const slowFirst = new Subject<unknown>(); + searchService.get.mockClear(); + searchService.get.mockReturnValueOnce(slowFirst as never); + store.setFilter('blog'); + spectator.flushEffects(); + + searchService.get.mockReturnValueOnce( + of({ jsonObjectView: { contentlets: [MOCK_CONTENTLETS[1]] }, resultsSize: 1 }) as never + ); + store.setPagination(2, 25); + spectator.flushEffects(); + + // The stale first response resolves last and must be ignored. + slowFirst.next(MOCK_SEARCH_ENTITY); + slowFirst.complete(); + + expect(store.totalRecords()).toBe(1); + expect(store.pages().length).toBe(1); + }); +}); diff --git a/core-web/libs/portlets/dot-agents/src/lib/agents/a11y/store/a11y-page-list.store.ts b/core-web/libs/portlets/dot-agents/src/lib/agents/a11y/store/a11y-page-list.store.ts new file mode 100644 index 000000000000..65f461841091 --- /dev/null +++ b/core-web/libs/portlets/dot-agents/src/lib/agents/a11y/store/a11y-page-list.store.ts @@ -0,0 +1,294 @@ +import { patchState, signalStore, withHooks, withMethods, withState } from '@ngrx/signals'; +import { EMPTY } from 'rxjs'; + +import { effect, inject, untracked } from '@angular/core'; + +import { catchError, take } from 'rxjs/operators'; + +import { + DotContentSearchService, + DotHttpErrorManagerService, + DotLanguagesService +} from '@dotcms/data-access'; +import { DotCMSContentlet, DotLanguage, ESContent } from '@dotcms/dotcms-models'; +import { GlobalStore } from '@dotcms/store'; + +import { SubscriptionSlot } from './subscription-slot'; + +import { StudioPageRow } from '../models/accessibility-studio.models'; + +type PageListStatus = 'init' | 'loading' | 'loaded' | 'error'; + +/** dotCMS's own default language id — the fallback when the lookup fails. */ +const DEFAULT_LANGUAGE_ID = 1; + +/** + * The language the page list scopes to: the instance default. + * + * Explicitly the one flagged `defaultLanguage`, NOT simply the first returned — the + * endpoint's order is not a contract, and silently listing a non-default language would + * be invisible on a screen that renders no language column. Falls back to the first + * entry, then to dotCMS's own default id, so a missing flag still yields a usable list. + */ +export function pickDefaultLanguageId(languages: DotLanguage[] | null | undefined): number { + if (!languages?.length) { + return DEFAULT_LANGUAGE_ID; + } + + return languages.find((language) => language.defaultLanguage)?.id ?? languages[0].id; +} + +interface A11yPageListState { + /** The page rows for the current query + page. */ + pages: StudioPageRow[]; + /** Total matches for the current query (drives the paginator). */ + totalRecords: number; + /** 1-based page number. */ + page: number; + /** Rows per page. */ + rows: number; + /** Free-text search term (title / path / urlmap prefix). */ + filter: string; + /** + * The language the list is scoped to — the instance default, resolved once on init. + * Null until it lands, which gates the first load the same way `currentSiteId` does. + * + * Scoping to ONE language is what stops a multilingual site returning the same page + * once per language. Default rather than user-selectable because the table renders no + * language column; surfacing a language picker (and a column) is the follow-up that + * would let a user reach a non-default translation from this screen. + */ + languageId: number | null; + pageListStatus: PageListStatus; +} + +const initialState: A11yPageListState = { + pages: [], + totalRecords: 0, + page: 1, + rows: 25, + filter: '', + languageId: null, + pageListStatus: 'init' +}; + +/** + * Escape a user-typed term for use inside a Lucene clause. + * + * Two separate jobs. The character class escapes the metacharacters that would + * otherwise change the query's structure. The whitespace collapse then matters just as + * much: a field-qualified term ends at the first space, so an unquoted `about us` in + * `path:*about us*` parses as `path:*about` followed by a bare `us*` against the DEFAULT + * field — which quietly returns any content starting with "us" (users, usage) and never + * applies the intended path wildcard to the full phrase. Spaces become `?`, Lucene's + * single-character wildcard, keeping the whole phrase inside one term. + */ +function escapeLuceneTerm(term: string): string { + return term.replace(/[+\-&|!(){}[\]^"~*?:\\/]/g, '\\$&').replace(/\s+/g, '?'); +} + +/** + * Builds the Lucene query for the page list — pages (`basetype:5`) plus URL-mapped + * content, working + not deleted, scoped to the current host and language. Search adds + * a title / path / urlmap prefix clause. + * + * `languageId` is not optional in practice: without it a multilingual site returns one + * row PER LANGUAGE for the same page, identical in the table (which renders no language + * column), so the "N of M" count doubles and which row the user clicks is arbitrary — + * they can scan and fix the Spanish page believing it's the English one. + */ +function buildPagesQuery(filter: string, siteId: string | null, languageId: number): string { + const clauses = [ + '+working:true', + '+(urlmap:* OR basetype:5)', + '+deleted:false', + `+languageId:${languageId}` + ]; + + if (siteId) { + clauses.push(`+conhost:${siteId}`); + } + + const q = filter.trim(); + if (q) { + const safe = escapeLuceneTerm(q); + clauses.push(`+(title:${safe}* OR path:*${safe}* OR urlmap:*${safe}*)`); + } + + return clauses.join(' '); +} + +/** Projects a search contentlet into the page-list row shape. */ +function toPageRow(content: DotCMSContentlet): StudioPageRow { + return { + identifier: content.identifier, + title: content.title || content.url || content.identifier, + // Prefer the urlMap for URL-mapped content (e.g. Blog): it's the real + // navigable path visitors use, whereas `url` may point at the detail + // template. Fall back to `url` for plain pages (no urlMap). + path: content['urlMap'] || content.url || '', + type: content.contentType, + languageId: content.languageId, + hostId: content.host, + hostName: content.hostName, + modDate: content.modDate, + modUserName: content.modUserName, + live: !!content.live + }; +} + +/** + * The Accessibility Studio **page list** store — owns only the page-list screen + * (`agents/a11y`): the searchable, paginated list of pages to run against. It's + * provided at {@link DotA11yPageListComponent}, so it lives and dies with that + * route and never shares state with a run. + * + * Selecting a page navigates to the run route; the run screen reads the page from + * the URL and drives its own {@link A11yRunStore}. This store never holds run + * state (no selected page, no scan/fix/report) — that split is the whole point. + */ +export const A11yPageListStore = signalStore( + withState<A11yPageListState>(initialState), + withMethods((store) => { + const contentSearchService = inject(DotContentSearchService); + const languagesService = inject(DotLanguagesService); + const httpErrorManager = inject(DotHttpErrorManagerService); + const globalStore = inject(GlobalStore); + + // The in-flight search. Held so a newer query supersedes an older one: typing + // "blog" then paging is two overlapping POSTs, and if the FIRST resolves last it + // overwrites `pages` and `totalRecords` while the paginator shows the newer page, + // so the table and paginator disagree and a row click opens a page the user did + // not select. Cancelling on destroy also stops `patchState` running against a + // destroyed store when the user navigates away mid-search. + const activeSearch = new SubscriptionSlot(); + /** The one-shot default-language lookup; cancelled on destroy like the search. */ + const languageLoad = new SubscriptionSlot(); + + function loadPages() { + const siteId = globalStore.currentSiteId(); + const languageId = store.languageId(); + // Both load asynchronously (GlobalStore → auth → HTTP; languages → HTTP). + // Until they're known a fetch would be unscoped — every site, every language — + // so skip. The reload effect tracks both and re-runs once they resolve, so + // this fires exactly once, fully scoped. + if (!siteId || !languageId) { + return; + } + patchState(store, { pageListStatus: 'loading' }); + + const query = buildPagesQuery(store.filter(), siteId, languageId); + const offset = (store.page() - 1) * store.rows(); + + activeSearch.set( + contentSearchService + // `ESContent` rather than an inline restatement of the envelope: the + // inline copy omitted `contentTook`/`queryTook`, so wanting query timing + // later would mean a second partial copy instead of one shared type. + .get<ESContent>({ + query, + limit: store.rows(), + offset, + sort: 'modDate desc' + }) + .pipe( + take(1), + catchError((error) => { + httpErrorManager.handle(error); + patchState(store, { pageListStatus: 'error' }); + + return EMPTY; + }) + ) + .subscribe((entity) => { + const contentlets = entity?.jsonObjectView?.contentlets ?? []; + patchState(store, { + pages: contentlets.map(toPageRow), + totalRecords: entity?.resultsSize ?? 0, + pageListStatus: 'loaded' + }); + }) + ); + } + + return { + loadPages, + + /** + * Resolve the instance's default language, which scopes every query. On + * failure fall back to id 1 (dotCMS's default) rather than blocking the + * screen: a wrong-but-plausible language still lists pages, where a null + * would leave the list permanently empty with no explanation. + */ + loadDefaultLanguage() { + languageLoad.set( + languagesService + .get() + .pipe( + take(1), + catchError(() => { + patchState(store, { languageId: DEFAULT_LANGUAGE_ID }); + + return EMPTY; + }) + ) + .subscribe((languages) => { + patchState(store, { languageId: pickDefaultLanguageId(languages) }); + }) + ); + }, + + setFilter(filter: string) { + patchState(store, { filter, page: 1 }); + }, + + setPagination(page: number, rows: number) { + patchState(store, { page, rows }); + }, + + /** Cancel the in-flight search + language lookup (see the `onDestroy` hook). */ + teardown() { + activeSearch.cancel(); + languageLoad.cancel(); + } + }; + }), + withHooks((store) => { + return { + onInit() { + const globalStore = inject(GlobalStore); + + // Every query is language-scoped, so resolve the default language once + // up front; the reload effect below tracks it and fires the first load. + store.loadDefaultLanguage(); + + // Reset pagination when the site changes; pages are per-site. + effect(() => { + globalStore.currentSiteId(); + untracked(() => patchState(store, { page: 1 })); + }); + + // Reload the list on query / pagination / site / language changes. This + // store only exists on the page-list route, so it always loads (no phase + // gate). + effect(() => { + store.filter(); + store.page(); + store.rows(); + store.languageId(); + globalStore.currentSiteId(); + + untracked(() => store.loadPages()); + }); + }, + + /** + * Cancel in-flight requests when the store is destroyed (route navigation), + * so a late response can't `patchState` a store that no longer exists. + */ + onDestroy() { + store.teardown(); + } + }; + }) +); diff --git a/core-web/libs/portlets/dot-agents/src/lib/agents/a11y/store/a11y-run.store.spec.ts b/core-web/libs/portlets/dot-agents/src/lib/agents/a11y/store/a11y-run.store.spec.ts new file mode 100644 index 000000000000..e711e02b9333 --- /dev/null +++ b/core-web/libs/portlets/dot-agents/src/lib/agents/a11y/store/a11y-run.store.spec.ts @@ -0,0 +1,733 @@ +import { patchState } from '@ngrx/signals'; +import { createServiceFactory, mockProvider, SpectatorService } from '@openng/spectator/jest'; +import { concat, NEVER, Observable, of, throwError } from 'rxjs'; + +import { createEnvironmentInjector, EnvironmentInjector } from '@angular/core'; + +import { DotPageScannerService, PageScannerA11yResponse } from '@dotcms/portlets/dot-ema/ui'; + +import { A11yRunStore } from './a11y-run.store'; + +import { + A11yAgentStreamEvent, + FixResult, + NEEDS_ATTENTION_STATUSES, + RESEARCH_RULE_ID, + StudioPageRow +} from '../models/accessibility-studio.models'; +import { MOCK_FIX_REPORT } from '../models/mock-fix-report'; +import { DotA11yAgentService } from '../services/dot-a11y-agent.service'; + +/** + * A canned SSE run in the current contract: the run-id frame, two `phase` steps, + * a `progress` count, a `workingChanged` file set, then `done` with the report. + */ +const MOCK_FIX_STREAM: A11yAgentStreamEvent[] = [ + { type: 'run', runId: 'r_test_123' }, + { + type: 'phase', + step: { message: 'Scanning live + working baseline', meta: { phase: 'scan' } } + }, + { type: 'phase', step: { message: 'Fixing color-contrast → .btn', meta: { phase: 'fix' } } }, + { type: 'progress', progress: { baseline: 5, current: 2, cleared: 3 } }, + { + type: 'workingChanged', + changedFiles: [{ path: '//site/application/themes/x/style.css', identifier: 'css-id' }] + }, + { type: 'done', result: MOCK_FIX_REPORT } +]; + +/** + * A fix stream that emits `events` and then STAYS OPEN, as a real SSE connection does + * mid-run. + * + * Use this — not a bare `of(...)` — for any run that has not reached a terminal frame. + * `of()` completes as soon as it has emitted, which the store reads as the connection + * dropping mid-run (its documented abnormal-close path), so a mid-run assertion written + * against `of()` would be asserting against a closed stream. + */ +const openStream = (...events: A11yAgentStreamEvent[]): Observable<A11yAgentStreamEvent> => + concat(of<A11yAgentStreamEvent>(...events), NEVER); + +// Two violation rules (3 + 2 = 5 error elements) + one incomplete rule (2 warnings). +const MOCK_SCAN_RESPONSE = { + ok: true, + standard: 'WCAG2AA', + axe: { + violations: [ + { + id: 'image-alt', + impact: 'critical', + description: 'Images must have alternate text', + help: '', + helpUrl: 'https://example.com/image-alt', + tags: [], + nodes: [ + { html: '<img>', target: ['img.a'], impact: 'critical', failureSummary: '' }, + { html: '<img>', target: ['img.b'], impact: 'critical', failureSummary: '' }, + { html: '<img>', target: ['img.c'], impact: 'critical', failureSummary: '' } + ] + }, + { + id: 'button-name', + impact: 'serious', + description: 'Buttons must have discernible text', + help: '', + helpUrl: 'https://example.com/button-name', + tags: [], + nodes: [ + { + html: '<button>', + target: ['button.x'], + impact: 'serious', + failureSummary: '' + }, + { + html: '<button>', + target: ['button.y'], + impact: 'serious', + failureSummary: '' + } + ] + } + ], + incomplete: [ + { + id: 'color-contrast', + impact: 'moderate', + description: 'Elements must have sufficient color contrast', + help: '', + helpUrl: 'https://example.com/color-contrast', + tags: [], + nodes: [ + { html: '<a>', target: ['a.l1'], impact: 'moderate', failureSummary: '' }, + { html: '<a>', target: ['a.l2'], impact: 'moderate', failureSummary: '' } + ] + } + ] + } +} as unknown as PageScannerA11yResponse; + +const MOCK_ROW: StudioPageRow = { + identifier: 'id-1', + title: 'About Us', + path: '/about-us', + type: 'htmlpageasset', + languageId: 1, + hostId: 'host-id-1', + hostName: 'demo.dotcms.com', + modDate: '04/09/2026', + modUserName: 'Admin User', + live: true +}; + +/** A second page, for switch-away assertions. */ +const OTHER_ROW: StudioPageRow = { + identifier: 'id-2', + title: 'Blog Post', + path: '/blog/post/hello', + type: 'Blog', + languageId: 1, + hostId: 'host-id-1', + hostName: 'demo.dotcms.com', + modDate: '03/10/2026', + modUserName: 'Admin User', + live: false +}; + +describe('A11yRunStore', () => { + let spectator: SpectatorService<InstanceType<typeof A11yRunStore>>; + let store: InstanceType<typeof A11yRunStore>; + let scannerService: jest.Mocked<DotPageScannerService>; + let agentService: jest.Mocked<DotA11yAgentService>; + + const createService = createServiceFactory({ + service: A11yRunStore, + providers: [ + mockProvider(DotPageScannerService, { + checkA11y: jest.fn().mockReturnValue(of(MOCK_SCAN_RESPONSE)) + }), + mockProvider(DotA11yAgentService, { + fixStream: jest.fn().mockReturnValue(of(...MOCK_FIX_STREAM)), + stop: jest.fn().mockReturnValue(of(null)) + }) + ] + }); + + beforeEach(() => { + jest.clearAllMocks(); + spectator = createService(); + store = spectator.service; + scannerService = spectator.inject( + DotPageScannerService + ) as jest.Mocked<DotPageScannerService>; + agentService = spectator.inject(DotA11yAgentService) as jest.Mocked<DotA11yAgentService>; + }); + + /** Open the default page (id-1, /about-us) the way the page list hands it over. */ + function openDefaultPage() { + store.openSelectedPage(MOCK_ROW); + } + + it('starts in the ready phase with no page', () => { + expect(store.phase()).toBe('ready'); + expect(store.selected()).toBeNull(); + }); + + describe('openSelectedPage', () => { + it('adopts the row handed over by the page list and opens it to ready', () => { + store.openSelectedPage(MOCK_ROW); + expect(store.selected()).toEqual(MOCK_ROW); + expect(store.phase()).toBe('ready'); + }); + + it('is a no-op when the same page is already selected (keeps an in-progress run)', () => { + store.openSelectedPage(MOCK_ROW); + store.runScan(); + const scanResult = store.scanResult(); + + store.openSelectedPage({ ...MOCK_ROW }); + + // Same identifier → the run is left alone rather than reset. + expect(store.scanResult()).toBe(scanResult); + expect(store.phase()).toBe('scanned'); + }); + + it('switches to a different page and resets the prior run', () => { + store.openSelectedPage(MOCK_ROW); + store.runScan(); + expect(store.scanResult()).not.toBeNull(); + + store.openSelectedPage(OTHER_ROW); + + expect(store.selected()).toEqual(OTHER_ROW); + expect(store.phase()).toBe('ready'); + expect(store.scanResult()).toBeNull(); + }); + }); + + describe('scan + fix state machine', () => { + beforeEach(() => { + openDefaultPage(); + }); + + it('opens the page to ready with the selection', () => { + expect(store.phase()).toBe('ready'); + expect(store.selected()).toEqual(MOCK_ROW); + expect(store.scanResult()).toBeNull(); + expect(store.report()).toBeNull(); + }); + + it('runScan fires two scans: the primary EDIT_MODE (working) scan and the comparison LIVE scan', () => { + store.runScan(); + expect(scannerService.checkA11y).toHaveBeenCalledTimes(2); + + const previewUrl = scannerService.checkA11y.mock.calls[0][0]; + expect(previewUrl).toContain(`${window.location.origin}/about-us`); + expect(previewUrl).toContain('host_id=host-id-1'); + expect(previewUrl).toContain('language_id=1'); + expect(previewUrl).toContain('mode=EDIT_MODE'); + + const liveUrl = scannerService.checkA11y.mock.calls[1][0]; + expect(liveUrl).toContain(`${window.location.origin}/about-us`); + expect(liveUrl).toContain('host_id=host-id-1'); + expect(liveUrl).toContain('mode=LIVE'); + }); + + it('runScan populates liveScanResult (comparison-only) alongside scanResult', () => { + store.runScan(); + expect(store.scanResult()).toBe(MOCK_SCAN_RESPONSE); + expect(store.liveScanResult()).toBe(MOCK_SCAN_RESPONSE); + expect(store.liveA11yGroups().length).toBe(3); + }); + + it('a failing LIVE scan does not derail the UI (comparison-only, error swallowed)', () => { + scannerService.checkA11y + .mockReturnValueOnce(of(MOCK_SCAN_RESPONSE)) + .mockReturnValueOnce(throwError(() => new Error('live boom'))); + store.runScan(); + expect(store.phase()).toBe('scanned'); + expect(store.scanResult()).toBe(MOCK_SCAN_RESPONSE); + expect(store.liveScanResult()).toBeNull(); + // Comparison-only: it must not reach the banner either. + expect(store.runError()).toBeNull(); + }); + + it('runScan stores the scan result and the real error/warning counts', () => { + store.runScan(); + expect(store.phase()).toBe('scanned'); + expect(store.hasResults()).toBe(true); + expect(store.scanResult()).toBe(MOCK_SCAN_RESPONSE); + expect(store.errorCount()).toBe(5); // 3 + 2 violation elements + expect(store.warningCount()).toBe(2); // 2 incomplete elements + expect(store.beforeCount()).toBe(5); + expect(store.a11yGroups().length).toBe(3); + }); + + it('runScan re-scans from the scanned phase (the re-scan button)', () => { + store.runScan(); // ready → scanned (preview + live scan) + store.runScan(); // scanned → scanning → scanned again (re-scan) + expect(scannerService.checkA11y).toHaveBeenCalledTimes(4); + expect(store.phase()).toBe('scanned'); + }); + + it('re-scanning drops the prior scan result before the new one lands', () => { + store.runScan(); // ready → scanned, result populated + scannerService.checkA11y.mockReturnValueOnce(NEVER); + store.runScan(); + expect(store.phase()).toBe('scanning'); + expect(store.scanResult()).toBeNull(); + }); + + it('returns to ready and reports the error in the banner if the scan fails', () => { + scannerService.checkA11y.mockReturnValueOnce(throwError(() => new Error('boom'))); + store.runScan(); + expect(store.phase()).toBe('ready'); + expect(store.scanResult()).toBeNull(); + expect(store.runError()).toBe('boom'); + }); + + it('startFix streams phase steps then moves scanned → done with the full report', () => { + store.runScan(); + store.startFix(); + expect(agentService.fixStream).toHaveBeenCalledTimes(1); + expect(store.steps()).toHaveLength(2); + expect(store.steps()[0]).toEqual({ + message: 'Scanning live + working baseline', + meta: { phase: 'scan' } + }); + expect(store.phase()).toBe('done'); + expect(store.fixedCount()).toBe(7); + expect(store.reportedCount()).toBe(5); + expect(store.afterCount()).toBe(MOCK_FIX_REPORT.scan.after.violations); + }); + + it('progress events drive the live openCount down while fixing', () => { + agentService.fixStream.mockReturnValueOnce( + openStream( + { type: 'run', runId: 'r_test_123' }, + { type: 'progress', progress: { baseline: 5, current: 2, cleared: 3 } } + ) + ); + store.runScan(); + store.startFix(); + expect(store.phase()).toBe('fixing'); // stream still open, no terminal event + expect(store.openCount()).toBe(2); + }); + + it('re-scans the preview on each progress frame so the legend/ring track fixes', () => { + agentService.fixStream.mockReturnValueOnce( + openStream( + { type: 'run', runId: 'r_test_123' }, + { type: 'progress', progress: { baseline: 5, current: 3, cleared: 2 } }, + { type: 'progress', progress: { baseline: 5, current: 1, cleared: 4 } } + ) + ); + store.runScan(); // (preview + live comparison scans) + scannerService.checkA11y.mockClear(); + store.startFix(); + expect(scannerService.checkA11y).toHaveBeenCalledTimes(2); + expect(scannerService.checkA11y.mock.calls[0][0]).toContain('mode=EDIT_MODE'); + expect(store.previewRevision()).toBe(2); + }); + + it('survives a mid-fix rescan failure without derailing the run', () => { + // `rescanPreviewDuringFix` fires on every progress frame and swallows scan + // failures — a named failure mode with no coverage. The run must continue: the + // agent is still working, and the next progress frame retries. A scanner blip + // must not end a fix pass. + scannerService.checkA11y + .mockReturnValueOnce(of(MOCK_SCAN_RESPONSE)) // preview scan + .mockReturnValueOnce(of(MOCK_SCAN_RESPONSE)) // live comparison scan + .mockReturnValueOnce(throwError(() => new Error('scanner blip'))); // mid-fix + agentService.fixStream.mockReturnValueOnce( + openStream( + { type: 'run', runId: 'r_test_123' }, + { type: 'progress', progress: { baseline: 5, current: 3, cleared: 2 } } + ) + ); + + store.runScan(); + const revisionBeforeFix = store.previewRevision(); + store.startFix(); + + expect(store.phase()).toBe('fixing'); + // The failed rescan writes nothing: no stale scanResult, and no reload of a + // preview that has not been re-read. + expect(store.previewRevision()).toBe(revisionBeforeFix); + expect(store.scanResult()).toBe(MOCK_SCAN_RESPONSE); + // And it is not surfaced as a run error — the run is still going. + expect(store.runError()).toBeNull(); + }); + + it('fixedCount reflects the live progress.cleared while fixing', () => { + agentService.fixStream.mockReturnValueOnce( + openStream( + { type: 'run', runId: 'r_test_123' }, + { type: 'progress', progress: { baseline: 5, current: 2, cleared: 3 } } + ) + ); + store.runScan(); + store.startFix(); + expect(store.phase()).toBe('fixing'); + expect(store.fixedCount()).toBe(3); + }); + + it('keeps beforeCount pinned to the baseline while the preview re-scan shrinks', () => { + const REDUCED_SCAN = { + ok: true, + standard: 'WCAG2AA', + axe: { + violations: [ + { + id: 'image-alt', + impact: 'critical', + description: 'Images must have alternate text', + help: '', + helpUrl: '', + tags: [], + nodes: [ + { + html: '<img>', + target: ['img.a'], + impact: 'critical', + failureSummary: '' + } + ] + } + ], + incomplete: [] + } + } as unknown as PageScannerA11yResponse; + + scannerService.checkA11y + .mockReturnValueOnce(of(MOCK_SCAN_RESPONSE)) // preview scan + .mockReturnValueOnce(of(MOCK_SCAN_RESPONSE)) // live comparison scan + .mockReturnValueOnce(of(REDUCED_SCAN)); // mid-fix re-scan + agentService.fixStream.mockReturnValueOnce( + openStream( + { type: 'run', runId: 'r_test_123' }, + { type: 'progress', progress: { baseline: 5, current: 1, cleared: 4 } } + ) + ); + store.runScan(); + store.startFix(); + + expect(store.errorCount()).toBe(1); + expect(store.beforeCount()).toBe(5); + }); + + it('captures the run id from the stream and targets stop at it', () => { + agentService.fixStream.mockReturnValueOnce( + openStream( + { type: 'run', runId: 'r_test_123' }, + { type: 'step', step: { message: 'working' } } + ) + ); + store.runScan(); + store.startFix(); + expect(store.phase()).toBe('fixing'); // stream still open, no terminal event + expect(store.runId()).toBe('r_test_123'); + + store.stopAgent(); + expect(agentService.stop).toHaveBeenCalledWith('r_test_123'); + }); + + it('stopAgent is a no-op when no run id has arrived yet', () => { + agentService.fixStream.mockReturnValueOnce( + openStream({ type: 'step', step: { message: 'working' } }) + ); + store.runScan(); + store.startFix(); + expect(store.runId()).toBeNull(); + + store.stopAgent(); + expect(agentService.stop).not.toHaveBeenCalled(); + }); + + it('startFix sends the selected page + skipCss in the agent request', () => { + store.setSkipCss(true); + store.runScan(); + store.startFix(); + const request = agentService.fixStream.mock.calls[0][0]; + expect(request.identifier).toBe('id-1'); + expect(request.languageId).toBe(1); + expect(request.skipCss).toBe(true); + }); + + it('startFix returns to scanned and records the error on a terminal error event', () => { + agentService.fixStream.mockReturnValueOnce( + of<A11yAgentStreamEvent>({ type: 'error', message: 'render unreliable' }) + ); + store.runScan(); + store.startFix(); + expect(store.phase()).toBe('scanned'); + expect(store.runError()).toBe('render unreliable'); + expect(store.report()).toBeNull(); + }); + + it('startFix returns to scanned and records the error if the stream throws', () => { + agentService.fixStream.mockReturnValueOnce(throwError(() => new Error('network down'))); + store.runScan(); + store.startFix(); + expect(store.phase()).toBe('scanned'); + expect(store.runError()).toBe('network down'); + }); + + it('a stream that closes with no terminal frame leaves fixing instead of wedging', () => { + // The transport completes the observable whenever the response body ends — + // an agent pod restart, an ingress idle timeout, or the relay closing on an + // upstream socket drop all look like this: frames, then a clean close and no + // `done`/`aborted`/`error`. Staying in `fixing` would spin the "still + // working…" indicator forever with no way back to the results. + agentService.fixStream.mockReturnValueOnce( + of<A11yAgentStreamEvent>( + { type: 'run', runId: 'r_test_123' }, + { type: 'progress', progress: { baseline: 5, current: 3, cleared: 2 } } + ) + ); + store.runScan(); + store.startFix(); + + expect(store.phase()).toBe('scanned'); + expect(store.runError()).toContain('ended before it reported a result'); + expect(store.report()).toBeNull(); + }); + + it('a normal close after a terminal frame does not overwrite the outcome', () => { + // The default mock ends with `done` and then completes, which is the ordinary + // end of a run — the abnormal-close fallback must not fire here. + store.runScan(); + store.startFix(); + + expect(store.phase()).toBe('done'); + expect(store.runError()).toBeNull(); + expect(store.report()).toEqual(MOCK_FIX_REPORT); + }); + + it('survives a terminal frame that carries no report', () => { + // `aborted` can arrive with a status-only payload — `FixReport.status` exists + // for exactly that — and the service hands that through as null. The counts + // must fall back to the pre-run scan rather than reading `report.scan` on + // something that has none, which threw inside change detection and blanked the + // score widget, footer and donut together. + agentService.fixStream.mockReturnValueOnce( + of<A11yAgentStreamEvent>({ type: 'aborted', result: null }) + ); + store.runScan(); + store.startFix(); + + expect(store.report()).toBeNull(); + // The pre-run scan figures survive rather than the pane dying. + expect(() => store.beforeCount()).not.toThrow(); + expect(store.beforeCount()).toBe(5); + expect(store.afterCount()).toBe(0); + expect(store.fixedCount()).toBe(0); + }); + + it('refreshes the preview on a terminal error, so fixes already written show up', () => { + // A run can fail AFTER writing fixes to the working version; without a bump the + // preview and changed-files panel keep their pre-run state and hide them. + agentService.fixStream.mockReturnValueOnce( + of<A11yAgentStreamEvent>({ type: 'error', message: 'render unreliable' }) + ); + store.runScan(); + const before = store.previewRevision(); + store.startFix(); + + expect(store.previewRevision()).toBeGreaterThan(before); + }); + + it('surfaces a failed stop instead of swallowing it, and stays in fixing', () => { + // The service already treats 202 and 404 as equivalent, so anything reaching + // this path means the agent is likely still running and still writing to the + // working version. Silence here would claim a stop that never took. + agentService.fixStream.mockReturnValueOnce( + openStream({ type: 'run', runId: 'r_test_123' }) + ); + agentService.stop.mockReturnValueOnce(throwError(() => new Error('502 Bad Gateway'))); + + store.runScan(); + store.startFix(); + store.stopAgent(); + + expect(store.runError()).toContain('Could not stop the agent'); + expect(store.phase()).toBe('fixing'); + }); + + it('publish moves done → published', () => { + store.runScan(); + store.startFix(); + store.publish(); + expect(store.phase()).toBe('published'); + }); + + it('finished + runStarted track their phase sets', () => { + expect(store.runStarted()).toBe(false); + expect(store.finished()).toBe(false); + + store.runScan(); + expect(store.runStarted()).toBe(false); + expect(store.finished()).toBe(false); + + store.startFix(); + expect(store.phase()).toBe('done'); + expect(store.runStarted()).toBe(true); + expect(store.finished()).toBe(true); + + store.publish(); + expect(store.runStarted()).toBe(true); + expect(store.finished()).toBe(true); + }); + + it('publish works from scanned — working changes can predate a fix run', () => { + store.runScan(); + store.publish(); + expect(store.phase()).toBe('published'); + }); + + it('publish is a no-op while a scan is in flight', () => { + scannerService.checkA11y.mockReturnValueOnce(NEVER); + store.runScan(); + expect(store.phase()).toBe('scanning'); + + store.publish(); + expect(store.phase()).toBe('scanning'); + }); + + it('discard returns from done to scanned', () => { + store.runScan(); + store.startFix(); + store.discard(); + expect(store.phase()).toBe('scanned'); + }); + + it('splits results into fixed vs reported buckets', () => { + store.runScan(); + store.startFix(); + expect(store.fixedResults().every((r) => r.status === 'fixed-to-working')).toBe(true); + expect( + store.reportedResults().every((r) => NEEDS_ATTENTION_STATUSES.includes(r.status)) + ).toBe(true); + }); + + it('keeps `reported` deferrals out of the needs-attention bucket', () => { + store.runScan(); + store.startFix(); + // A `reported` row means the deterministic pass handed the violation to the + // agentic pass — an intermediate marker, so it must never be counted as + // unresolved work. + expect(store.reportedResults().some((r) => r.status === 'reported')).toBe(false); + }); + + it('derives both counts from the before/after rescan, not row counts', () => { + store.runScan(); + store.startFix(); + const report = store.report(); + const before = report?.scan.before.violations ?? 0; + const after = report?.scan.after.violations ?? 0; + + expect(store.fixedCount()).toBe(before - after); + expect(store.reportedCount()).toBe(after); + }); + + it('counts violations cleared by the agentic pass, which emits no fix rows', () => { + // 20 → 2 means 18 cleared, but only one row logs a deterministic fix. Counting + // rows would report "1 fixed"; the rescan is what knows the real number. + patchState(store, { + report: { + ...MOCK_FIX_REPORT, + scan: { before: { violations: 20 }, after: { violations: 2 } }, + results: [{ ruleId: 'color-contrast', status: 'fixed-to-working' }] + } + }); + + expect(store.fixedCount()).toBe(18); + expect(store.reportedCount()).toBe(2); + }); + + it('dedupes fixed rows emitted once per violating element', () => { + const duplicated: FixResult[] = [ + { + ruleId: 'color-contrast', + status: 'fixed-to-working', + file: '/style.css', + diff: '- a\n+ b' + }, + { + ruleId: 'color-contrast', + status: 'fixed-to-working', + file: '/style.css', + diff: '- a\n+ b' + }, + { ruleId: RESEARCH_RULE_ID, status: 'fixed-to-working', file: '/t.vtl' } + ]; + patchState(store, { + report: { + ...MOCK_FIX_REPORT, + results: duplicated + } + }); + + // One distinct edit survives; the research row is not a fix. + expect(store.fixedResults().length).toBe(1); + }); + }); + + describe('skip CSS toggle', () => { + it('defaults to false and can be toggled', () => { + expect(store.skipCss()).toBe(false); + store.setSkipCss(true); + expect(store.skipCss()).toBe(true); + }); + }); + + /** + * The store is provided per-route, so navigation destroys its injector. Instantiate it in a + * child environment injector here so `destroy()` fires the real `onDestroy` hook — Spectator's + * own injector outlives the individual test. + */ + describe('teardown on destroy', () => { + let injector: EnvironmentInjector; + let scopedStore: InstanceType<typeof A11yRunStore>; + + beforeEach(() => { + injector = createEnvironmentInjector( + [A11yRunStore], + spectator.inject(EnvironmentInjector) + ); + scopedStore = injector.get(A11yRunStore); + }); + + it('aborts an in-flight fix stream when the store is destroyed', () => { + const fixTeardown = jest.fn(); + agentService.fixStream.mockReturnValue(new Observable(() => fixTeardown)); + + scopedStore.openSelectedPage(MOCK_ROW); + scopedStore.runScan(); + scopedStore.startFix(); + expect(fixTeardown).not.toHaveBeenCalled(); + + injector.destroy(); + + expect(fixTeardown).toHaveBeenCalledTimes(1); + }); + + it('aborts both in-flight scans when the store is destroyed', () => { + const previewTeardown = jest.fn(); + const liveTeardown = jest.fn(); + scannerService.checkA11y + .mockReturnValueOnce(new Observable(() => previewTeardown)) + .mockReturnValueOnce(new Observable(() => liveTeardown)); + + scopedStore.openSelectedPage(MOCK_ROW); + scopedStore.runScan(); + expect(previewTeardown).not.toHaveBeenCalled(); + + injector.destroy(); + + expect(previewTeardown).toHaveBeenCalledTimes(1); + expect(liveTeardown).toHaveBeenCalledTimes(1); + }); + }); +}); diff --git a/core-web/libs/portlets/dot-agents/src/lib/agents/a11y/store/a11y-run.store.ts b/core-web/libs/portlets/dot-agents/src/lib/agents/a11y/store/a11y-run.store.ts new file mode 100644 index 000000000000..ddf9a1b7ead3 --- /dev/null +++ b/core-web/libs/portlets/dot-agents/src/lib/agents/a11y/store/a11y-run.store.ts @@ -0,0 +1,799 @@ +import { + patchState, + signalStore, + withComputed, + withHooks, + withMethods, + withState +} from '@ngrx/signals'; +import { EMPTY } from 'rxjs'; + +import { computed, inject, isDevMode } from '@angular/core'; + +import { catchError, take } from 'rxjs/operators'; + +import { AgentHeartbeat, AgentProgress, AgentRunStep } from '@dotcms/dotcms-models'; +import { + A11yGroup, + DotPageScannerService, + PageScannerA11yResponse, + buildA11yGroups +} from '@dotcms/portlets/dot-ema/ui'; + +import { SubscriptionSlot } from './subscription-slot'; + +import { + impactToSeverity, + SEVERITY_RANK, + severityBreakdown, + type SeverityCounts +} from '../models/a11y-severity'; +import { + AgentFixRequest, + FixReport, + FixResult, + NEEDS_ATTENTION_STATUSES, + RESEARCH_RULE_ID, + StudioPageRow, + StudioPhase +} from '../models/accessibility-studio.models'; +import { DotA11yAgentService } from '../services/dot-a11y-agent.service'; + +/** + * Dev-only port swap for {@link backendOrigin}. Under `nx serve` the app is served + * on DEV_SERVER_PORT while dotCMS itself answers on DEV_BACKEND_PORT; the agent + * renders server-side and must be handed the latter. Both are inert in prod, where + * the portlet is served from the dotCMS origin and no swap happens. + */ +const DEV_SERVER_PORT = '4200'; +const DEV_BACKEND_PORT = '8080'; + +interface A11yRunState { + /** Studio state machine. Starts at `ready` (a page is being opened). */ + phase: StudioPhase; + /** The page this run is against. */ + selected: StudioPageRow | null; + /** Per-run opt-out: when true, the agent reports CSS contrast instead of fixing it. */ + skipCss: boolean; + /** + * The real axe scan result of the PREVIEW (working) render — populated by + * runScan() via DotPageScannerService. This is the scan that owns the whole + * UI: score donut, issue list, fix flow, and the preview-frame markers. + */ + scanResult: PageScannerA11yResponse | null; + /** + * The axe scan result of the LIVE (published) render. Used ONLY for the + * side-by-side comparison — it draws the live-frame marker layer so the user + * sees the violations that still exist on the published page (which may lag + * the preview when fixes aren't published yet). Feeds no other widget. + */ + liveScanResult: PageScannerA11yResponse | null; + /** Live agent activity log — appended from SSE `phase` events during a fix run. */ + steps: AgentRunStep[]; + /** + * Live violation count from SSE `progress` events — the authoritative running + * score while fixing (baseline → current, cleared so far). Null until the + * first progress frame arrives. + */ + progress: AgentProgress | null; + /** + * The current run's id — captured from the stream's first `run` event, used to + * target the /stop request at this specific run. Null when no run is active. + */ + runId: string | null; + /** + * Latest keep-alive tick from SSE `heartbeat` events — how long the run and the + * current action have been going. Drives the "still working…" indicator so a + * long, quiet step (a model call) doesn't look hung. Null between runs. + */ + heartbeat: AgentHeartbeat | null; + /** + * The one error channel for this screen — a failed scan, a failed fix run, a stream + * that dropped, or a stop that didn't take. Rendered as a banner at the top of the + * portlet and cleared whenever a new scan or fix starts. + * + * Deliberately NOT routed through `DotHttpErrorManagerService`: a modal error dialog + * over a long-running agent screen interrupts a run the user is watching, and these + * failures are all recoverable in place (re-scan, re-run, retry the stop). + */ + runError: string | null; + /** + * The run report — populated when the fix pass completes (SSE `done`). + * + * Non-null implies `scan.before` and `scan.after` are present: `DotA11yAgentService` + * validates the terminal payload and yields null for a status-only frame rather than + * a partial report. That invariant is what lets every derived count below read + * `report.scan` behind a plain truthiness check — a status-only `aborted` frame used + * to satisfy that check and then throw inside the computeds, blanking the run pane. + */ + report: FixReport | null; + /** + * Monotonic counter bumped whenever the working (preview) render changes — each + * mid-fix re-scan and the terminal report. The preview iframe keys its URL off + * this so it reloads to show the agent's applied fixes visually. Not otherwise + * meaningful; only its changes matter. + */ + previewRevision: number; +} + +const initialState: A11yRunState = { + phase: 'ready', + selected: null, + skipCss: false, + scanResult: null, + liveScanResult: null, + steps: [], + progress: null, + runId: null, + heartbeat: null, + runError: null, + report: null, + previewRevision: 0 +}; + +/** + * A thrown value as banner text, falling back to `fallback` for anything that isn't an + * `Error` (an HTTP failure arrives as an `HttpErrorResponse`, whose `message` is the + * useful part; a non-Error throw has nothing worth showing the user). + */ +const errorText = (error: unknown, fallback: string): string => + error instanceof Error && error.message ? error.message : fallback; + +/** The run-state reset shared by opening a page and starting a fresh scan. */ +const runReset = (): Partial<A11yRunState> => ({ + scanResult: null, + liveScanResult: null, + steps: [], + progress: null, + runId: null, + heartbeat: null, + runError: null, + report: null, + previewRevision: 0 +}); + +/** + * The Accessibility Studio **run** store — owns a single page's scan / fix / + * review / publish lifecycle for the run route (`agents/a11y/<page-path>`). It's + * provided at {@link DotA11yRunComponent}, so navigating to a different page + * destroys and recreates it → fresh state per page, no manual reset needed. + * + * The page it runs against is handed over by the page list through router state + * ({@link openSelectedPage}); the URL carries its readable path for display and + * sharing, but the run route is only reachable through the list. + */ +export const A11yRunStore = signalStore( + withState<A11yRunState>(initialState), + withComputed((store) => { + // Hoisted so every derived count reads ONE memoized traversal of the axe + // payload. Sibling computeds in the returned literal can't reference each + // other, so the shared derivations live here. + const a11yGroups = computed<A11yGroup[]>(() => buildA11yGroups(store.scanResult())); + const errorGroups = computed<A11yGroup[]>(() => + a11yGroups().filter((g) => g.type === 'error') + ); + const warningGroups = computed<A11yGroup[]>(() => + a11yGroups().filter((g) => g.type === 'warning') + ); + /** Element count across groups (a group's `count` is its flagged-node total). */ + const elementCount = (groups: A11yGroup[]) => + groups.reduce((total, g) => total + g.count, 0); + + return { + // `phase` IS the interface for single-state questions — consumers compare + // it directly (`phase() === 'scanning'`) rather than going through a + // per-phase boolean. What lives here is only the phase SETS: groups that + // carry domain meaning an enum comparison can't express, and that would + // otherwise be spelled out inline in every consumer. + /** A scan or fix run is in flight (the working copy may still be changing). */ + isWorking: computed(() => ['scanning', 'fixing'].includes(store.phase())), + /** A scan has produced (or is producing) results, so the score is meaningful. */ + hasResults: computed(() => + ['scanned', 'fixing', 'done', 'published'].includes(store.phase()) + ), + /** A fix run has started, so before→after figures are meaningful. */ + runStarted: computed(() => ['fixing', 'done', 'published'].includes(store.phase())), + /** The run reached a terminal state — its report is final. */ + finished: computed(() => ['done', 'published'].includes(store.phase())), + /** Real axe findings grouped per rule (violations → error, incomplete → warning). */ + a11yGroups, + /** + * The LIVE (published) render's findings, grouped per rule — drives ONLY the + * live-frame marker layer for the side-by-side comparison. Empty until the + * live scan lands (it runs alongside the preview scan on Scan / Re-scan). + */ + liveA11yGroups: computed<A11yGroup[]>(() => buildA11yGroups(store.liveScanResult())), + /** Real axe error-element count (confirmed violations). */ + errorCount: computed(() => elementCount(errorGroups())), + /** Real axe warning-element count (incomplete / needs review). */ + warningCount: computed(() => elementCount(warningGroups())), + /** + * Axe `incomplete` groups (needs manual review) — one per rule, sorted by + * occurrence count. The agent doesn't fix these (axe couldn't confirm them), + * so the panel lists them separately with an explanation. + */ + reviewGroups: computed<A11yGroup[]>(() => + [...warningGroups()].sort((a, b) => b.count - a.count) + ), + /** + * The BASELINE violation count — the "before" side of the before→after + * comparison. It must stay pinned to the ORIGINAL scan even as the preview + * is re-scanned mid-fix (which mutates `scanResult` and would otherwise drag + * this down). Source order: the report's frozen `scan.before` once the run + * completes; the agent's `progress.baseline` while fixing; otherwise the + * pre-run scan's error count. + */ + beforeCount: computed(() => { + const report = store.report(); + if (report) { + return report.scan.before.violations; + } + const progress = store.progress(); + if (store.phase() === 'fixing' && progress) { + return progress.baseline; + } + return elementCount(errorGroups()); + }), + /** Violations remaining after the fix pass. */ + afterCount: computed(() => store.report()?.scan.after.violations ?? 0), + /** + * The applied fixes, one row per distinct edit. The agent emits a + * `fixed-to-working` row per violating ELEMENT, so a single CSS edit matching 5 + * elements arrives 5 times — deduped here on rule + file + diff (the diff text + * already carries its own "[5 instances]" note, so nothing is lost). Research + * rows are excluded: they're a step, not a fix. + */ + fixedResults: computed<FixResult[]>(() => { + const rows = + store + .report() + ?.results.filter( + (r) => r.status === 'fixed-to-working' && r.ruleId !== RESEARCH_RULE_ID + ) ?? []; + + return [ + ...new Map( + rows.map((r) => [`${r.ruleId}|${r.file ?? ''}|${r.diff ?? ''}`, r]) + ).values() + ]; + }), + reportedResults: computed<FixResult[]>( + () => + store + .report() + ?.results.filter((r) => NEEDS_ATTENTION_STATUSES.includes(r.status)) ?? [] + ), + latestStep: computed<AgentRunStep | null>(() => { + const steps = store.steps(); + return steps.length ? steps[steps.length - 1] : null; + }), + /** + * Confirmed-violation groups (axe `error`s), one per rule, sorted for the + * "BY ISSUE TYPE" list: highest severity first, then most occurrences. + */ + issueTypeRows: computed<A11yGroup[]>(() => { + const rank = (g: A11yGroup) => SEVERITY_RANK[impactToSeverity(g.impact)]; + return [...errorGroups()].sort((a, b) => rank(a) - rank(b) || b.count - a.count); + }), + /** Open issues broken down by severity (element counts) — drives the donut + legend. */ + severityCounts: computed<SeverityCounts>(() => severityBreakdown(errorGroups())), + /** + * Live "open" count for the score widget. After the run finishes it's the + * report's authoritative after-count; while fixing it's the live count from + * the agent's `progress` stream (`current`) so the donut animates down as + * fixes land; before any run it's the scan's before-count. + */ + openCount: computed<number>(() => { + const report = store.report(); + if (report) { + return report.scan.after.violations; + } + if (store.phase() === 'fixing') { + // Authoritative live count straight from the agent's `progress` + // events. Before the first progress frame lands, fall back to the + // baseline (the scan's before-count). + const progress = store.progress(); + if (progress) { + return Math.max(0, progress.current); + } + } + return elementCount(errorGroups()); + }), + /** + * How many violations have been cleared. While fixing it's the agent's live + * `progress.cleared`; once the run completes it's derived from the report's + * before/after rescan (`before - after`). + * + * Deliberately NOT a count of `fixed-to-working` rows: a run makes two passes + * (deterministic, then agentic), and the rows only log the deterministic pass — + * one row per violating element, so a single CSS edit fanning out to 5 elements + * emits 5 rows. Fixes the agentic pass lands have no row at all. The rescan is + * the only number that reflects both passes. + */ + fixedCount: computed<number>(() => { + const report = store.report(); + if (report) { + return Math.max( + 0, + report.scan.before.violations - report.scan.after.violations + ); + } + if (store.phase() === 'fixing') { + return Math.max(0, store.progress()?.cleared ?? 0); + } + return 0; + }), + /** + * How many violations still need attention — the rescan's after-count, not a + * row count. `reported` rows are a pass-1 handoff marker ("the agentic pass will + * take this"), not an outcome, so counting them reported work the agent then went + * on to fix. Only the post-both-passes rescan knows what actually survived. + */ + reportedCount: computed<number>(() => store.report()?.scan.after.violations ?? 0) + }; + }), + withMethods((store) => { + const scannerService = inject(DotPageScannerService); + const agentService = inject(DotA11yAgentService); + + // The in-flight scan / fix-stream subscription, held so Stop can cancel it + // (unsubscribing aborts the underlying fetch). Not reactive UI state. + const activeScan = new SubscriptionSlot(); + // The comparison-only LIVE scan, tracked separately so Stop cancels it too + // without coupling it to the UI-driving preview scan's lifecycle. + const liveScan = new SubscriptionSlot(); + // The mid-fix re-scan of the PREVIEW render, triggered by `progress` frames + // so the ring + per-severity legend track the agent's live fixes. Held so a + // newer progress frame supersedes an in-flight one (no stampede / stale writes). + const fixRescan = new SubscriptionSlot(); + // The in-flight POST /stop. Held so teardown can cancel it, and so a user + // clicking Stop repeatedly supersedes the previous request instead of piling + // up one un-cancellable POST per click. + const stopRequest = new SubscriptionSlot(); + + /** + * The dotCMS backend origin the agent must render + call against. + * + * In prod the portlet is served FROM the dotCMS origin, so the browser's own + * origin is already correct and is returned untouched. + * + * Under `nx serve` the app is on the dev-server port but the agent (and the + * dotCMS scanner it drives) render server-side and can only reach the backend + * directly, so the port is swapped for the backend's. Only the PORT is + * rewritten — protocol and hostname are preserved — and only in dev mode, so a + * hostname that merely contains the dev port's digits (`dev4200.example.com`) + * can't be corrupted the way a blind string replace would. + */ + function backendOrigin(): string { + const origin = window.location.origin; + if (!isDevMode()) { + return origin; + } + + try { + const url = new URL(origin); + if (url.port === DEV_SERVER_PORT) { + url.port = DEV_BACKEND_PORT; + } + + return url.origin; + } catch { + return origin; + } + } + + /** + * Build the absolute URL the scanner renders + checks. It must be on the + * backend origin (never the content-site hostname, which may not be publicly + * reachable) with `host_id` to disambiguate the site. + * + * `mode` selects which render to scan: + * - `EDIT_MODE` (default) — the working version. NOTE: DotPageScannerService + * rewrites EDIT_MODE → PREVIEW_MODE at its chokepoint (editor chrome would + * produce phantom violations), so this scans the PREVIEW render — the same + * one the left "with fixes" frame shows. This is the scan that owns the UI. + * - `LIVE` — the published render, for the comparison-only live scan. `LIVE` + * passes through the scanner untouched. + * Mirrors DotEmaShellComponent.handleScannerToolClick. + */ + function buildScanUrl( + page: StudioPageRow, + mode: 'EDIT_MODE' | 'LIVE' = 'EDIT_MODE' + ): string { + const path = page.path.startsWith('/') ? page.path : `/${page.path}`; + const url = new URL(path, backendOrigin()); + url.searchParams.set('host_id', page.hostId); + url.searchParams.set('language_id', String(page.languageId)); + url.searchParams.set('mode', mode); + return url.toString(); + } + + /** + * Re-scan the PREVIEW render mid-fix and fold the fresh axe result into + * `scanResult`, so the score ring, per-severity legend, and issue list track + * the agent's live fixes as they land. Triggered by each `progress` frame. + * + * A newer call supersedes an in-flight one (unsubscribe aborts it), so bursts + * of progress frames don't stampede the scanner or write stale results. Stays + * in the `fixing` phase throughout; failures are swallowed (the run continues + * and the next progress frame retries). Guarded to the fixing phase so a late + * response after done/abort can't overwrite the report-driven widgets. + */ + function rescanPreviewDuringFix() { + const page = store.selected(); + if (store.phase() !== 'fixing' || !page) { + return; + } + fixRescan.set( + scannerService + .checkA11y(buildScanUrl(page, 'EDIT_MODE')) + .pipe( + take(1), + catchError(() => EMPTY) + ) + .subscribe((scanResult) => { + // Only apply if we're still fixing (a done/abort may have landed). + if (store.phase() === 'fixing') { + // Bump previewRevision so the preview iframe reloads and shows + // the fixes that this re-scan just picked up. + patchState(store, { + scanResult, + previewRevision: store.previewRevision() + 1 + }); + } + }) + ); + } + + /** Open a page → studio "ready" (waits for the user to scan). */ + function openPage(selected: StudioPageRow) { + patchState(store, { + selected, + phase: 'ready', + ...runReset() + }); + } + + return { + setSkipCss(skipCss: boolean) { + patchState(store, { skipCss }); + }, + + /** + * Adopt the page the page list handed over via router state → run screen + * "ready". The row carries everything the run needs (identifier, host, + * language), so there is no lookup: the run route is entered only from the + * list, and a run URL opened cold has no row to adopt (the run screen + * bounces back to the list instead). + * + * A no-op when the SAME page under the SAME site is already selected, so a + * re-navigation into the route can't reset an in-progress run. + */ + openSelectedPage(row: StudioPageRow) { + if (store.selected()?.identifier === row.identifier) { + return; + } + openPage(row); + }, + + /** + * Run the REAL axe scan via DotPageScannerService against the page's + * EDIT_MODE render, then store the result and move to "scanned". + */ + runScan() { + const page = store.selected(); + // Allow scanning from `ready` (first scan) and `scanned` (the re-scan + // button) — both transition into `scanning`. + if ((store.phase() !== 'ready' && store.phase() !== 'scanned') || !page) { + return; + } + // Drop any prior scan/report so the widgets reflect the fresh scan. + patchState(store, { + phase: 'scanning', + scanResult: null, + liveScanResult: null, + report: null, + runError: null + }); + + // Primary scan — the PREVIEW (working) render. Owns the phase + all + // widgets. Failure returns to `ready` so the user can retry. + activeScan.set( + scannerService + .checkA11y(buildScanUrl(page, 'EDIT_MODE')) + .pipe( + take(1), + catchError((error: unknown) => { + // Return to ready so the user can retry the scan, and + // report it in the portlet's own banner. + patchState(store, { + phase: 'ready', + runError: errorText(error, 'The scan failed.') + }); + + return EMPTY; + }) + ) + .subscribe((scanResult) => { + patchState(store, { scanResult, phase: 'scanned' }); + }) + ); + + // Comparison-only scan — the LIVE (published) render. Draws the + // live-frame markers and nothing else, so its failure must NOT touch + // the phase or surface an error dialog: swallow it and leave the live + // markers empty. Runs in parallel with the primary scan. + liveScan.set( + scannerService + .checkA11y(buildScanUrl(page, 'LIVE')) + .pipe( + take(1), + catchError(() => EMPTY) + ) + .subscribe((liveScanResult) => { + patchState(store, { liveScanResult }); + }) + ); + }, + + /** + * Tear down every in-flight subscription without touching state. + * + * The in-band transitions (stopScan / done / error) each clean up the + * subscriptions they own, but component/route teardown does not: navigating + * away mid-run would otherwise leave the SSE `fetch` ReadableStream open, + * with its `subscriber.next` closures keeping this store alive and the + * abandoned run still streaming. Called from the `onDestroy` hook below. + */ + teardown() { + activeScan.cancel(); + liveScan.cancel(); + fixRescan.cancel(); + stopRequest.cancel(); + }, + + /** Cancel the in-flight scan (unsubscribe aborts the request) → back to ready. */ + stopScan() { + if (store.phase() !== 'scanning') { + return; + } + activeScan.cancel(); + // Cancel the parallel comparison scan too. + liveScan.cancel(); + patchState(store, { phase: 'ready' }); + }, + + /** + * Run the real fix pass: POST the page to the agent and stream its + * progress over SSE. Each `phase` event appends to the live activity + * log; `progress` updates the live violation count; `done` sets the + * report and moves to "done"; `error` returns to "scanned" so the user + * can retry. The browser holds no token — the dev/prod proxy injects the + * bearer (see DotA11yAgentService). + */ + startFix() { + const page = store.selected(); + if (store.phase() !== 'scanned' || !page) { + return; + } + patchState(store, { + phase: 'fixing', + steps: [], + progress: null, + runId: null, + heartbeat: null, + runError: null, + report: null, + previewRevision: 0 + }); + + // The Java proxy resolves page details and builds the full + // FixRequest; the Studio sends only the identifier, languageId, and skipCss. + const request: AgentFixRequest = { + identifier: page.identifier, + languageId: page.languageId, + skipCss: store.skipCss() + }; + + // Whether this run's outcome has already been recorded — by a terminal + // frame (`done` / `aborted` / `error`) or by the stream erroring. Read by + // the `complete` handler below, which fires in BOTH cases (the error path + // completes too, via the EMPTY that `catchError` returns) and must not + // overwrite an outcome that is already correct. + let settled = false; + + activeScan.set( + agentService + .fixStream(request) + .pipe( + catchError((error: unknown) => { + const message = + error instanceof Error + ? error.message + : 'The agent run failed.'; + settled = true; + patchState(store, { + phase: 'scanned', + runError: message, + // The run may have written fixes before the stream + // died; refresh so they aren't hidden (see `error`). + previewRevision: store.previewRevision() + 1 + }); + + return EMPTY; + }) + ) + .subscribe({ + next: (event) => { + switch (event.type) { + case 'run': + // First frame: capture the run id so Stop can target it. + patchState(store, { runId: event.runId }); + break; + // `step` is the legacy alias of `phase` — treat identically. + case 'phase': + case 'step': + patchState(store, { + steps: [...store.steps(), event.step] + }); + break; + case 'progress': + // Live violation count → drives the score donut down. + patchState(store, { progress: event.progress }); + // Re-scan the preview so the ring segments, legend, and + // issue list reflect the fixes that just landed (the + // progress totals alone carry no per-severity split). + rescanPreviewDuringFix(); + break; + case 'heartbeat': + // Keep-alive while the agent is thinking between + // actions — drives the "still working…" indicator so a + // long, quiet step doesn't look hung. + patchState(store, { heartbeat: event.heartbeat }); + break; + case 'done': + case 'aborted': + // done = full run; aborted = stopped early with a partial + // report (fixes already applied are kept). Both land on + // the done screen with the report the agent returned. + // Cancel any pending mid-fix rescan first so it can't + // overwrite the report-driven widgets afterwards. + settled = true; + fixRescan.cancel(); + patchState(store, { + phase: 'done', + report: event.result, + // Final reload so the preview reflects the finished + // working render. + previewRevision: store.previewRevision() + 1 + }); + break; + case 'error': + // Terminal error event from the agent. + settled = true; + fixRescan.cancel(); + patchState(store, { + phase: 'scanned', + runError: event.message, + // Bump like `done`/`aborted` do. A run can fail + // AFTER writing fixes to the working version, and + // without this the preview and changed-files panel + // keep their pre-run state — hiding real unpublished + // edits and, with them, the Publish bar. + previewRevision: store.previewRevision() + 1 + }); + break; + default: + // Exhaustive: any unhandled event type is ignored. + break; + } + }, + complete: () => { + // The stream ended. If a terminal frame already landed this + // is the normal close and the phase is already correct. + if (settled) { + return; + } + + // Otherwise the connection dropped mid-run. Fall back to + // `scanned` so the user keeps their scan results and can + // retry — staying in `fixing` would spin the "still + // working…" indicator forever with no way out, since Stop + // targets a run the agent may no longer have. + fixRescan.cancel(); + patchState(store, { + phase: 'scanned', + runError: + 'The connection to the agent ended before it reported a result. ' + + 'Any fixes it already wrote to the working version are kept — ' + + 're-scan to see the current state.', + // Those kept fixes are exactly what the preview and + // changed-files panel must now show (see `error`). + previewRevision: store.previewRevision() + 1 + }); + } + }) + ); + }, + + /** + * Stop the in-flight agent run. Tells the agent (by run id) to stop; it + * returns a partial report via the stream's `aborted` event, keeping + * fixes already applied. We keep the stream subscribed so that terminal + * event still lands and moves us to the done screen. No-op if the run id + * hasn't arrived yet (the agent hasn't announced the run). + * + * A FAILED stop must be visible. The service already treats 202 and 404 as + * equivalent (the run is gone either way), so anything still reaching the + * error path — a 5xx, a network drop — means the agent very likely kept + * running and kept writing to the working version. Swallowing that would + * leave the UI claiming a stop that never took, on the one control the user + * reaches for when they want the agent to stop touching their page. + */ + stopAgent() { + const runId = store.runId(); + if (store.phase() !== 'fixing' || !runId) { + return; + } + stopRequest.set( + agentService + .stop(runId) + .pipe( + take(1), + catchError(() => { + // Stay in `fixing`: the run is, as far as we know, still + // going. The stream's own terminal frame (or its close + // handler) still owns the phase transition. + patchState(store, { + runError: + 'Could not stop the agent — it may still be running and ' + + 'writing to the working version. Try again in a moment.' + }); + + return EMPTY; + }) + ) + .subscribe() + ); + }, + + /** + * Promote the working version to live (the only publish; human-triggered). + * + * Not gated on the `done` phase: the changed files a user publishes may + * predate this run (an earlier run, a manual edit), so the files panel + * offers Publish whenever a working-vs-live delta exists — including + * before any scan. Blocked only while a run is in flight, where the + * working copy is still being written. + */ + publish() { + if (store.isWorking()) { + return; + } + patchState(store, { phase: 'published' }); + }, + + /** + * Discard the working fixes. Returns to `scanned` when a scan's results + * are still on screen, otherwise to `ready` — going to `scanned` from + * `ready` would show a results view for a scan that never ran. + */ + discard() { + if (store.isWorking()) { + return; + } + patchState(store, { phase: store.scanResult() ? 'scanned' : 'ready' }); + } + }; + }), + withHooks({ + /** + * Abort any in-flight scan / SSE fix stream when the store is destroyed + * (route navigation, component teardown), so an abandoned run cannot keep + * streaming in the background. + */ + onDestroy(store) { + store.teardown(); + } + }) +); diff --git a/core-web/libs/portlets/dot-agents/src/lib/agents/a11y/store/subscription-slot.ts b/core-web/libs/portlets/dot-agents/src/lib/agents/a11y/store/subscription-slot.ts new file mode 100644 index 000000000000..9139fec66e67 --- /dev/null +++ b/core-web/libs/portlets/dot-agents/src/lib/agents/a11y/store/subscription-slot.ts @@ -0,0 +1,34 @@ +import { Subscription } from 'rxjs'; + +/** + * A single-occupancy slot for an in-flight subscription. + * + * The run store holds three long-lived streams (the primary scan / fix SSE, the + * comparison LIVE scan, and the mid-fix rescan) that all need the same discipline: + * starting a new one cancels whatever was already there, and every teardown path + * has to both unsubscribe AND drop the reference. Written inline that was seven + * scattered `sub?.unsubscribe(); sub = null;` pairs — each one a place to forget + * half of it. Unsubscribing is what aborts the underlying `fetch`, so a missed + * cancel leaks an open stream rather than merely a dead object. + * + * Not reactive state: nothing here is read by the UI. + */ +export class SubscriptionSlot { + #sub: Subscription | null = null; + + /** + * Take ownership of `next`, cancelling any subscription already in the slot — + * so a burst of triggers can't stampede or let an older, slower response write + * stale results after a newer one. + */ + set(next: Subscription): void { + this.cancel(); + this.#sub = next; + } + + /** Cancel and clear whatever occupies the slot. Safe to call when empty. */ + cancel(): void { + this.#sub?.unsubscribe(); + this.#sub = null; + } +} diff --git a/core-web/libs/portlets/dot-agents/src/lib/lib.routes.ts b/core-web/libs/portlets/dot-agents/src/lib/lib.routes.ts new file mode 100644 index 000000000000..1ac3050fbc91 --- /dev/null +++ b/core-web/libs/portlets/dot-agents/src/lib/lib.routes.ts @@ -0,0 +1,36 @@ +import { Route } from '@angular/router'; + +import { DOT_AGENTS } from './agent-registry'; +import { DotAgentsShellComponent } from './agents-shell/dot-agents-shell.component'; + +/** + * Lazy child route for every agent that has a route loader, e.g. + * `agents/a11y`. `coming-soon` agents (no `loadChildren`) produce no route. + */ +const agentRoutes: Route[] = DOT_AGENTS.flatMap((agent) => + agent.loadChildren + ? [{ path: agent.id, data: { reuseRoute: false }, loadChildren: agent.loadChildren }] + : [] +); + +/** + * Routes for the agents shell. The gallery landing renders at the base path; + * each available agent is lazy-loaded full-screen at `agents/{id}`. Registered + * in `app.routes.ts` under the `agents` path. + */ +export const dotAgentsRoutes: Route[] = [ + { + path: '', + component: DotAgentsShellComponent, + children: [ + { + path: '', + loadComponent: () => + import('./agents-landing/dot-agents-landing.component').then( + (m) => m.DotAgentsLandingComponent + ) + }, + ...agentRoutes + ] + } +]; diff --git a/core-web/libs/portlets/dot-agents/src/test-setup.ts b/core-web/libs/portlets/dot-agents/src/test-setup.ts new file mode 100644 index 000000000000..b13563bb93c0 --- /dev/null +++ b/core-web/libs/portlets/dot-agents/src/test-setup.ts @@ -0,0 +1,6 @@ +import { setupZoneTestEnv } from 'jest-preset-angular/setup-env/zone'; + +setupZoneTestEnv({ + errorOnUnknownElements: true, + errorOnUnknownProperties: true +}); diff --git a/core-web/libs/portlets/dot-agents/tsconfig.json b/core-web/libs/portlets/dot-agents/tsconfig.json new file mode 100644 index 000000000000..2ac0a4fd922f --- /dev/null +++ b/core-web/libs/portlets/dot-agents/tsconfig.json @@ -0,0 +1,29 @@ +{ + "extends": "../../../tsconfig.base.json", + "compilerOptions": { + "isolatedModules": true, + "target": "es2022", + "strict": true, + "noImplicitOverride": true, + "noPropertyAccessFromIndexSignature": true, + "noImplicitReturns": true, + "noFallthroughCasesInSwitch": true, + "module": "preserve" + }, + "angularCompilerOptions": { + "enableI18nLegacyMessageIdFormat": false, + "strictInjectionParameters": true, + "strictInputAccessModifiers": true, + "strictTemplates": true + }, + "files": [], + "include": [], + "references": [ + { + "path": "./tsconfig.lib.json" + }, + { + "path": "./tsconfig.spec.json" + } + ] +} diff --git a/core-web/libs/portlets/dot-agents/tsconfig.lib.json b/core-web/libs/portlets/dot-agents/tsconfig.lib.json new file mode 100644 index 000000000000..816bd7adebd7 --- /dev/null +++ b/core-web/libs/portlets/dot-agents/tsconfig.lib.json @@ -0,0 +1,18 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "../../../dist/out-tsc", + "declaration": true, + "declarationMap": true, + "inlineSources": true, + "types": [] + }, + "include": ["src/**/*.ts"], + "exclude": [ + "src/**/*.spec.ts", + "src/**/*.test.ts", + "jest.config.ts", + "jest.config.cts", + "src/test-setup.ts" + ] +} diff --git a/core-web/libs/portlets/dot-agents/tsconfig.spec.json b/core-web/libs/portlets/dot-agents/tsconfig.spec.json new file mode 100644 index 000000000000..ae96844742c9 --- /dev/null +++ b/core-web/libs/portlets/dot-agents/tsconfig.spec.json @@ -0,0 +1,13 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "../../../dist/out-tsc", + "module": "commonjs", + "target": "es2016", + "types": ["jest", "node"], + "moduleResolution": "node10", + "isolatedModules": true + }, + "files": ["src/test-setup.ts"], + "include": ["jest.config.ts", "src/**/*.test.ts", "src/**/*.spec.ts", "src/**/*.d.ts"] +} diff --git a/core-web/libs/portlets/dot-velocity-playground/src/lib/dot-velocity-playground-page/dot-velocity-playground-page.component.html b/core-web/libs/portlets/dot-velocity-playground/src/lib/dot-velocity-playground-page/dot-velocity-playground-page.component.html index 421ca031b75c..16cf0dd37959 100644 --- a/core-web/libs/portlets/dot-velocity-playground/src/lib/dot-velocity-playground-page/dot-velocity-playground-page.component.html +++ b/core-web/libs/portlets/dot-velocity-playground/src/lib/dot-velocity-playground-page/dot-velocity-playground-page.component.html @@ -149,19 +149,63 @@ <!-- Right: Output --> <ng-template pTemplate> <div class="flex h-full flex-col overflow-hidden"> - @if (store.hasError()) { + @if (store.error(); as error) { <p-message severity="error" [pt]="{ - root: { class: '!rounded-none border-x-0! border-t-0! shrink-0' } + root: { class: '!rounded-none border-x-0! border-t-0! shrink-0 block' }, + text: { class: 'w-full' } }" data-testid="velocity-playground-error-banner"> <ng-template #icon> <span class="material-symbols-rounded" aria-hidden="true">warning</span> </ng-template> - {{ store.errorMessage() | dm }} + <div class="flex w-full flex-col gap-1"> + <span + class="font-medium wrap-break-word" + data-testid="velocity-playground-error-message"> + {{ $errorSummary() }} + </span> + @if (error.structured; as detail) { + <div + class="flex flex-wrap items-center gap-x-3 gap-y-1 text-xs" + data-testid="velocity-playground-error-detail"> + @if (detail.errorType) { + <!-- Read-only status label: severity comes from the + theme preset so it tracks light/dark, rather than + a literal red palette pairing. --> + <p-tag + severity="danger" + [value]="detail.errorType" + styleClass="font-mono" + data-testid="velocity-playground-error-type" /> + } + @if (detail.line !== undefined && detail.line !== null) { + <span + class="inline-flex items-center gap-1" + data-testid="velocity-playground-error-location"> + <i class="pi pi-map-marker text-[0.7rem]"></i> + {{ + 'velocityPlayground.error.location' + | dm + : [ + '' + detail.line, + detail.column !== undefined && + detail.column !== null + ? '' + detail.column + : '—' + ] + }} + </span> + } + </div> + } + </div> </p-message> } + @if (store.hasWarnings() && !store.hasError()) { + <dot-velocity-playground-warnings [warnings]="store.warnings()" /> + } @if (store.status() === ComponentStatus.INIT && !store.hasError()) { <dot-empty-container class="flex-1" @@ -173,14 +217,20 @@ class="flex flex-1 flex-col items-center justify-center gap-3" data-testid="velocity-playground-loading"> <dot-spinner size="3rem" borderSize="3px" /> - <span class="text-color-secondary text-sm"> + <span class="text-sm text-muted-color"> {{ 'velocityPlayground.output.running' | dm }} </span> </div> + } @else if (store.hasError()) { + <ngx-monaco-editor + class="min-h-0 flex-1" + [ngModel]="$errorTrace()" + [options]="$errorEditorOptions()" + data-testid="velocity-playground-error-editor" /> } @else { <!-- Stats bar --> <div - class="text-color-secondary flex min-h-14 shrink-0 items-center gap-2 border-b border-surface-200 bg-surface-50 px-4 py-2 text-sm" + class="flex min-h-14 shrink-0 items-center gap-2 border-b border-surface-200 bg-surface-50 px-4 py-2 text-sm text-muted-color" data-testid="velocity-playground-stats-bar"> <div class="flex items-center gap-1.5"> @if (store.hasOutput()) { @@ -190,7 +240,7 @@ {{ 'velocityPlayground.output.viewing' | dm }} {{ store.outputContentType() }} </span> - <span class="text-color-secondary mx-1">·</span> + <span class="mx-1 text-muted-color">·</span> } <span class="material-symbols-rounded text-xl! text-green-600" @@ -200,7 +250,7 @@ </span> {{ 'velocityPlayground.output.ready' | dm }} @if (store.elapsedMs() !== null) { - <span class="text-color-secondary mx-1">·</span> + <span class="mx-1 text-muted-color">·</span> <span class="inline-flex items-center gap-1"> <span class="font-bold text-color">{{ store.elapsedMs() }}</span> {{ 'velocityPlayground.output.timing' | dm }} @@ -290,7 +340,7 @@ </div> @if (example.description) { <p - class="text-color-secondary m-0 border-b border-surface-200 bg-surface-0 px-3 py-2 text-xs" + class="m-0 border-b border-surface-200 bg-surface-0 px-3 py-2 text-xs text-muted-color" data-testid="velocity-playground-help-description"> {{ example.description | dm }} </p> diff --git a/core-web/libs/portlets/dot-velocity-playground/src/lib/dot-velocity-playground-page/dot-velocity-playground-page.component.spec.ts b/core-web/libs/portlets/dot-velocity-playground/src/lib/dot-velocity-playground-page/dot-velocity-playground-page.component.spec.ts index 491fe35ffc82..f55d7c1424eb 100644 --- a/core-web/libs/portlets/dot-velocity-playground/src/lib/dot-velocity-playground-page/dot-velocity-playground-page.component.spec.ts +++ b/core-web/libs/portlets/dot-velocity-playground/src/lib/dot-velocity-playground-page/dot-velocity-playground-page.component.spec.ts @@ -26,10 +26,12 @@ const buildStoreMock = (overrides: StoreOverrides = {}) => ({ output: jest.fn().mockReturnValue(''), outputContentType: jest.fn().mockReturnValue('plaintext'), elapsedMs: jest.fn().mockReturnValue(null), - errorMessage: jest.fn().mockReturnValue(null), + error: jest.fn().mockReturnValue(null), + warnings: jest.fn().mockReturnValue([]), isLoading: jest.fn().mockReturnValue(false), hasOutput: jest.fn().mockReturnValue(false), hasError: jest.fn().mockReturnValue(false), + hasWarnings: jest.fn().mockReturnValue(false), canRun: jest.fn().mockReturnValue(false), hasHistory: jest.fn().mockReturnValue(false), setCode: jest.fn(), @@ -71,9 +73,18 @@ describe('DotVelocityPlaygroundPageComponent', () => { ] }); - const setup = (storeOverrides: StoreOverrides = {}) => { + const setup = ( + storeOverrides: StoreOverrides = {}, + messageGetter: (key: string) => string = () => '' + ) => { pendingStoreOverrides = storeOverrides; - spectator = createComponent(); + spectator = createComponent({ + providers: [ + mockProvider(DotMessageService, { + get: jest.fn().mockImplementation(messageGetter) + }) + ] + }); return spectator.inject(DotVelocityPlaygroundStore, true); }; @@ -164,19 +175,163 @@ describe('DotVelocityPlaygroundPageComponent', () => { }); describe('error banner', () => { - it('renders when hasError is true', () => { + it('renders the message when an error is present', () => { + // Echo the message back so $errorSummary's DotMessageService.get is a pass-through, + // matching the real behavior for unknown keys (raw backend messages are not i18n keys). + setup( + { + hasError: jest.fn().mockReturnValue(true), + error: jest.fn().mockReturnValue({ + message: 'Velocity failed', + structured: null, + warnings: [] + }), + status: jest.fn().mockReturnValue(ComponentStatus.LOADED) + }, + (key: string) => key + ); + expect(spectator.query(byTestId('velocity-playground-error-banner'))).toBeTruthy(); + expect( + spectator.query(byTestId('velocity-playground-error-message'))?.textContent?.trim() + ).toBe('Velocity failed'); + // Unstructured error → no detail row. + expect(spectator.query(byTestId('velocity-playground-error-detail'))).toBeFalsy(); + }); + + it('collapses a multi-line message to a single line in the banner', () => { + const multi = + 'Encountered "<EOF>" at line 5, column 39\nWas expecting one of:\n "[" ...'; + setup( + { + hasError: jest.fn().mockReturnValue(true), + error: jest + .fn() + .mockReturnValue({ message: multi, structured: null, warnings: [] }), + status: jest.fn().mockReturnValue(ComponentStatus.LOADED) + }, + (key: string) => key + ); + + // Banner shows only the first line… + expect( + spectator.query(byTestId('velocity-playground-error-message'))?.textContent?.trim() + ).toBe('Encountered "<EOF>" at line 5, column 39'); + // …while the trace pane keeps the full multi-line detail. + expect(spectator.component.$errorTrace()).toBe(multi); + }); + + it('renders the errorType chip and line/column locator for a structured error', () => { setup({ hasError: jest.fn().mockReturnValue(true), - errorMessage: jest.fn().mockReturnValue('Velocity failed'), + error: jest.fn().mockReturnValue({ + message: 'Encountered "#end"', + warnings: [], + structured: { + message: 'Encountered "#end"', + errorType: 'ParseErrorException', + line: 12, + column: 3 + } + }), status: jest.fn().mockReturnValue(ComponentStatus.LOADED) }); - expect(spectator.query(byTestId('velocity-playground-error-banner'))).toBeTruthy(); + + expect(spectator.query(byTestId('velocity-playground-error-detail'))).toBeTruthy(); + expect( + spectator.query(byTestId('velocity-playground-error-type'))?.textContent?.trim() + ).toBe('ParseErrorException'); + expect(spectator.query(byTestId('velocity-playground-error-location'))).toBeTruthy(); }); - it('is hidden when hasError is false', () => { - setup({ hasError: jest.fn().mockReturnValue(false) }); + it('is hidden when there is no error', () => { + setup({ error: jest.fn().mockReturnValue(null) }); expect(spectator.query(byTestId('velocity-playground-error-banner'))).toBeFalsy(); }); + + it('renders the error trace pane instead of the output editor on error', () => { + setup({ + hasError: jest.fn().mockReturnValue(true), + error: jest.fn().mockReturnValue({ + message: 'Velocity failed', + structured: null, + warnings: [] + }), + status: jest.fn().mockReturnValue(ComponentStatus.LOADED) + }); + + expect(spectator.query(byTestId('velocity-playground-error-editor'))).toBeTruthy(); + // The normal output editor + "Ready" stats bar must not show alongside an error. + expect(spectator.query(byTestId('velocity-playground-output-editor'))).toBeFalsy(); + expect(spectator.query(byTestId('velocity-playground-stats-bar'))).toBeFalsy(); + }); + + it('$errorTrace composes the header and location lines from the structured error', () => { + setup( + { + hasError: jest.fn().mockReturnValue(true), + error: jest.fn().mockReturnValue({ + message: 'Encountered "#end"', + warnings: [], + structured: { + message: 'Encountered "#end"', + errorType: 'ParseErrorException', + line: 12, + column: 3 + } + }), + status: jest.fn().mockReturnValue(ComponentStatus.LOADED) + }, + (key: string) => key + ); + + expect(spectator.component.$errorTrace()).toBe( + ['ParseErrorException: Encountered "#end"', ' at line 12, column 3'].join('\n') + ); + }); + }); + + describe('warnings banner', () => { + const warning = { + type: 'UNDEFINED_REFERENCE', + message: "Undefined reference '$x'", + reference: '$x', + line: 2, + column: 1 + }; + + it('renders on a successful run that has warnings', () => { + setup( + { + hasWarnings: jest.fn().mockReturnValue(true), + warnings: jest.fn().mockReturnValue([warning]), + hasError: jest.fn().mockReturnValue(false), + status: jest.fn().mockReturnValue(ComponentStatus.LOADED) + }, + (key: string) => key + ); + + expect(spectator.query(byTestId('velocity-playground-warnings-banner'))).toBeTruthy(); + expect(spectator.queryAll(byTestId('velocity-playground-warning-item')).length).toBe(1); + }); + + it('is suppressed when there is also an error (the trace pane carries warnings)', () => { + setup({ + hasWarnings: jest.fn().mockReturnValue(true), + warnings: jest.fn().mockReturnValue([warning]), + hasError: jest.fn().mockReturnValue(true), + error: jest + .fn() + .mockReturnValue({ message: 'boom', structured: null, warnings: [warning] }), + status: jest.fn().mockReturnValue(ComponentStatus.LOADED) + }); + + expect(spectator.query(byTestId('velocity-playground-warnings-banner'))).toBeFalsy(); + }); + + it('is hidden when there are no warnings', () => { + setup({ hasWarnings: jest.fn().mockReturnValue(false) }); + expect(spectator.query(byTestId('velocity-playground-warnings-banner'))).toBeFalsy(); + }); }); describe('content-type label', () => { diff --git a/core-web/libs/portlets/dot-velocity-playground/src/lib/dot-velocity-playground-page/dot-velocity-playground-page.component.ts b/core-web/libs/portlets/dot-velocity-playground/src/lib/dot-velocity-playground-page/dot-velocity-playground-page.component.ts index 5c9500547c11..c8be4ca7cb76 100644 --- a/core-web/libs/portlets/dot-velocity-playground/src/lib/dot-velocity-playground-page/dot-velocity-playground-page.component.ts +++ b/core-web/libs/portlets/dot-velocity-playground/src/lib/dot-velocity-playground-page/dot-velocity-playground-page.component.ts @@ -20,6 +20,7 @@ import { PanelModule } from 'primeng/panel'; import { Popover, PopoverModule } from 'primeng/popover'; import { SelectModule } from 'primeng/select'; import { SplitterModule } from 'primeng/splitter'; +import { TagModule } from 'primeng/tag'; import { TooltipModule } from 'primeng/tooltip'; import { filter, take } from 'rxjs/operators'; @@ -37,9 +38,12 @@ import { } from '@dotcms/ui'; import { buildCurlSnippet, buildFetchSnippet, getDownloadLink } from '@dotcms/utils'; +import { DotVelocityPlaygroundWarningsComponent } from './dot-velocity-playground-warnings/dot-velocity-playground-warnings.component'; import { DotVelocityPlaygroundStore } from './store/dot-velocity-playground.store'; import { + firstLine, + formatErrorTrace, formatHistoryLabel, getDownloadParams, VELOCITY_HELP_EXAMPLES @@ -63,9 +67,11 @@ import { MenuModule, PanelModule, PopoverModule, + TagModule, DotEmptyContainerComponent, DotSpinnerComponent, - DotMessagePipe + DotMessagePipe, + DotVelocityPlaygroundWarningsComponent ], providers: [DotVelocityPlaygroundStore, DotClipboardUtil], templateUrl: './dot-velocity-playground-page.component.html', @@ -103,6 +109,31 @@ export class DotVelocityPlaygroundPageComponent { readOnly: true })); + // Read-only, plaintext options for the error "stack trace" pane. + readonly $errorEditorOptions = computed(() => ({ + ...DOT_MONACO_RAW_OPTIONS, + language: 'plaintext', + wordWrap: this.store.wrapCode() ? 'on' : 'off', + readOnly: true, + lineNumbers: 'off', + folding: false + })); + + // Full error rendered as a copyable, stack-trace-style block for the output pane. + readonly $errorTrace = computed(() => { + const error = this.store.error(); + if (!error) return ''; + const resolvedMessage = this.#messageService.get(error.message); + return formatErrorTrace(error, resolvedMessage); + }); + + // Single-line summary for the banner — the full multi-line detail lives in the trace pane. + readonly $errorSummary = computed(() => { + const error = this.store.error(); + if (!error) return ''; + return firstLine(this.#messageService.get(error.message)); + }); + readonly $historyOptions = computed(() => this.store.history().map((entry) => ({ label: formatHistoryLabel(entry, this.#emptyHistoryLabel), diff --git a/core-web/libs/portlets/dot-velocity-playground/src/lib/dot-velocity-playground-page/dot-velocity-playground-warnings/dot-velocity-playground-warnings.component.html b/core-web/libs/portlets/dot-velocity-playground/src/lib/dot-velocity-playground-page/dot-velocity-playground-warnings/dot-velocity-playground-warnings.component.html new file mode 100644 index 000000000000..9f1652da88d2 --- /dev/null +++ b/core-web/libs/portlets/dot-velocity-playground/src/lib/dot-velocity-playground-page/dot-velocity-playground-warnings/dot-velocity-playground-warnings.component.html @@ -0,0 +1,31 @@ +<p-message + severity="warn" + [pt]="{ + root: { class: '!rounded-none border-x-0! border-t-0! shrink-0 block' }, + text: { class: 'w-full' } + }" + data-testid="velocity-playground-warnings-banner"> + <ng-template #icon> + <span class="material-symbols-rounded" aria-hidden="true">warning</span> + </ng-template> + <div class="flex w-full flex-col gap-1"> + <span class="font-medium" data-testid="velocity-playground-warnings-summary"> + {{ 'velocityPlayground.warnings.summary' | dm: ['' + warnings().length] }} + </span> + <ul class="m-0 flex flex-col gap-0.5 pl-0 text-xs"> + @for (warning of warnings(); track $index) { + <li + class="flex flex-wrap items-baseline gap-x-2" + data-testid="velocity-playground-warning-item"> + <!-- Read-only status label: severity comes from the theme preset so + it tracks light/dark, rather than a literal yellow pairing. --> + <p-tag severity="warn" [value]="warning.type" styleClass="font-mono" /> + <span class="wrap-break-word">{{ warning.message }}</span> + @if (location(warning); as text) { + <span class="text-muted-color">{{ text }}</span> + } + </li> + } + </ul> + </div> +</p-message> diff --git a/core-web/libs/portlets/dot-velocity-playground/src/lib/dot-velocity-playground-page/dot-velocity-playground-warnings/dot-velocity-playground-warnings.component.spec.ts b/core-web/libs/portlets/dot-velocity-playground/src/lib/dot-velocity-playground-page/dot-velocity-playground-warnings/dot-velocity-playground-warnings.component.spec.ts new file mode 100644 index 000000000000..852dc7386cc2 --- /dev/null +++ b/core-web/libs/portlets/dot-velocity-playground/src/lib/dot-velocity-playground-page/dot-velocity-playground-warnings/dot-velocity-playground-warnings.component.spec.ts @@ -0,0 +1,80 @@ +import { byTestId, createComponentFactory, Spectator } from '@openng/spectator/jest'; + +import { DotMessageService } from '@dotcms/data-access'; +import { MockDotMessageService } from '@dotcms/utils-testing'; + +import { DotVelocityPlaygroundWarningsComponent } from './dot-velocity-playground-warnings.component'; + +import { VelocityWarning } from '../../models/dot-velocity-playground.models'; + +const WARNING: VelocityWarning = { + type: 'UNDEFINED_REFERENCE', + message: "Undefined reference '$x'", + reference: '$x', + line: 2, + column: 1 +}; + +describe('DotVelocityPlaygroundWarningsComponent', () => { + let spectator: Spectator<DotVelocityPlaygroundWarningsComponent>; + + const createComponent = createComponentFactory({ + component: DotVelocityPlaygroundWarningsComponent, + providers: [ + { + provide: DotMessageService, + useValue: new MockDotMessageService({ + 'velocityPlayground.warnings.summary': '{0} warnings', + 'velocityPlayground.error.location': 'line {0}, column {1}' + }) + } + ] + }); + + const render = (warnings: VelocityWarning[]) => { + spectator = createComponent({ props: { warnings } }); + spectator.detectChanges(); + }; + + it('renders one row per warning with its type and message', () => { + render([WARNING, { ...WARNING, type: 'NULL_SET', message: 'Null set' }]); + + const items = spectator.queryAll(byTestId('velocity-playground-warning-item')); + expect(items.length).toBe(2); + expect(items[0]).toHaveText('UNDEFINED_REFERENCE'); + expect(items[0]).toHaveText("Undefined reference '$x'"); + expect(items[1]).toHaveText('Null set'); + }); + + it('summarizes the warning count', () => { + render([WARNING, WARNING, WARNING]); + + expect(spectator.query(byTestId('velocity-playground-warnings-summary'))).toHaveText( + '3 warnings' + ); + }); + + it('renders the line and column when both are present', () => { + render([WARNING]); + + expect(spectator.query(byTestId('velocity-playground-warning-item'))).toHaveText( + 'line 2, column 1' + ); + }); + + it('falls back to an em dash when the column is missing', () => { + render([{ ...WARNING, column: undefined }]); + + expect(spectator.query(byTestId('velocity-playground-warning-item'))).toHaveText( + 'line 2, column —' + ); + }); + + it('omits the location entirely when the warning carries no line', () => { + render([{ ...WARNING, line: undefined, column: undefined }]); + + const item = spectator.query(byTestId('velocity-playground-warning-item')); + expect(item).toHaveText("Undefined reference '$x'"); + expect(item?.textContent).not.toContain('line'); + }); +}); diff --git a/core-web/libs/portlets/dot-velocity-playground/src/lib/dot-velocity-playground-page/dot-velocity-playground-warnings/dot-velocity-playground-warnings.component.ts b/core-web/libs/portlets/dot-velocity-playground/src/lib/dot-velocity-playground-page/dot-velocity-playground-warnings/dot-velocity-playground-warnings.component.ts new file mode 100644 index 000000000000..fc420c000ca5 --- /dev/null +++ b/core-web/libs/portlets/dot-velocity-playground/src/lib/dot-velocity-playground-page/dot-velocity-playground-warnings/dot-velocity-playground-warnings.component.ts @@ -0,0 +1,50 @@ +import { ChangeDetectionStrategy, Component, inject, input } from '@angular/core'; + +import { MessageModule } from 'primeng/message'; +import { TagModule } from 'primeng/tag'; + +import { DotMessageService } from '@dotcms/data-access'; +import { DotMessagePipe } from '@dotcms/ui'; + +import { VelocityWarning } from '../../models/dot-velocity-playground.models'; + +/** + * The non-fatal warnings banner for a Velocity run — one row per + * {@link VelocityWarning} (type chip, message, and an optional line/column). + * + * Split out of the output pane's template, which had grown large enough that the + * warnings list buried the pane's actual structure. Presentational only: the + * caller decides when to show it (`store.hasWarnings() && !store.hasError()`). + */ +@Component({ + selector: 'dot-velocity-playground-warnings', + imports: [MessageModule, TagModule, DotMessagePipe], + templateUrl: './dot-velocity-playground-warnings.component.html', + changeDetection: ChangeDetectionStrategy.OnPush, + host: { class: 'contents' } +}) +export class DotVelocityPlaygroundWarningsComponent { + /** The warnings to list. Rendering an empty array shows an empty banner. */ + readonly warnings = input.required<VelocityWarning[]>(); + + private readonly dm = inject(DotMessageService); + + /** + * The "line N, column M" suffix for a warning, or null when it carries no + * line (the template then omits the element entirely). A missing column + * renders as an em dash — the backend reports line without column for some + * warning types. Built here rather than in the template: the interpolation + * needs two null checks plus number→string coercion per argument, which as a + * nested pipe expression was effectively unreadable. + */ + protected location(warning: VelocityWarning): string | null { + if (warning.line === undefined || warning.line === null) { + return null; + } + + const column = + warning.column === undefined || warning.column === null ? '—' : String(warning.column); + + return this.dm.get('velocityPlayground.error.location', String(warning.line), column); + } +} diff --git a/core-web/libs/portlets/dot-velocity-playground/src/lib/dot-velocity-playground-page/store/dot-velocity-playground.store.spec.ts b/core-web/libs/portlets/dot-velocity-playground/src/lib/dot-velocity-playground-page/store/dot-velocity-playground.store.spec.ts index 7d23bd3944d6..f969ca129028 100644 --- a/core-web/libs/portlets/dot-velocity-playground/src/lib/dot-velocity-playground-page/store/dot-velocity-playground.store.spec.ts +++ b/core-web/libs/portlets/dot-velocity-playground/src/lib/dot-velocity-playground-page/store/dot-velocity-playground.store.spec.ts @@ -21,7 +21,8 @@ import { DotVelocityPlaygroundService } from '../../services/dot-velocity-playgr const MOCK_RESPONSE: DotVelocityPlaygroundResponse = { body: 'hello', contentType: 'plaintext', - elapsedMs: 42 + elapsedMs: 42, + warnings: [] }; describe('DotVelocityPlaygroundStore', () => { @@ -49,6 +50,7 @@ describe('DotVelocityPlaygroundStore', () => { errorHandler = spectator.inject(DotHttpErrorManagerService) as unknown as { handle: jest.Mock; }; + errorHandler.handle.mockClear(); }); afterEach(() => { @@ -59,7 +61,7 @@ describe('DotVelocityPlaygroundStore', () => { it('starts in INIT status with empty output', () => { expect(spectator.service.status()).toBe(ComponentStatus.INIT); expect(spectator.service.output()).toBe(''); - expect(spectator.service.errorMessage()).toBeNull(); + expect(spectator.service.error()).toBeNull(); expect(spectator.service.history()).toEqual([]); expect(spectator.service.splitterRatio()).toEqual([...DEFAULT_SPLITTER_RATIO]); expect(spectator.service.wrapCode()).toBe(true); @@ -153,7 +155,8 @@ describe('DotVelocityPlaygroundStore', () => { of({ body: '{"ok":true}', contentType: 'json', - elapsedMs: 17 + elapsedMs: 17, + warnings: [] } satisfies DotVelocityPlaygroundResponse) ); @@ -167,6 +170,32 @@ describe('DotVelocityPlaygroundStore', () => { expect(spectator.service.elapsedMs()).toBe(17); }); + it('surfaces warnings from a successful run', () => { + const warnings = [ + { + type: 'UNDEFINED_REFERENCE' as const, + message: "Undefined reference '$x'", + reference: '$x', + line: 1 + } + ]; + runScriptSpy.mockReturnValue( + of({ + body: 'output', + contentType: 'plaintext', + elapsedMs: 5, + warnings + } satisfies DotVelocityPlaygroundResponse) + ); + + spectator.service.setCode('$x'); + spectator.service.runScript(); + + expect(spectator.service.warnings()).toEqual(warnings); + expect(spectator.service.hasWarnings()).toBe(true); + expect(spectator.service.hasError()).toBe(false); + }); + it('pushes the un-wrapped code into history on success', () => { spectator.service.setCode('$hello'); spectator.service.runScript(); @@ -177,7 +206,7 @@ describe('DotVelocityPlaygroundStore', () => { ]); }); - it('on error sets errorMessage, returns to LOADED, and delegates to the http error manager', () => { + it('on infra error sets error, returns to LOADED, and delegates to the http error manager', () => { const httpError = new HttpErrorResponse({ error: { message: 'broken' }, status: 500, @@ -189,11 +218,15 @@ describe('DotVelocityPlaygroundStore', () => { spectator.service.runScript(); expect(spectator.service.status()).toBe(ComponentStatus.LOADED); - expect(spectator.service.errorMessage()).toBe('broken'); + expect(spectator.service.error()).toEqual({ + message: 'broken', + structured: null, + warnings: [] + }); expect(errorHandler.handle).toHaveBeenCalledWith(httpError); }); - it('uses the raw text response body as errorMessage when responseType is text', () => { + it('uses the raw text response body as the error message when responseType is text', () => { // responseType: 'text' → error.error is the raw VTL body, not error.message. const httpError = new HttpErrorResponse({ error: 'Velocity parse error at line 4', @@ -205,10 +238,45 @@ describe('DotVelocityPlaygroundStore', () => { spectator.service.setCode('$broken'); spectator.service.runScript(); - expect(spectator.service.errorMessage()).toBe('Velocity parse error at line 4'); + expect(spectator.service.error()).toEqual({ + message: 'Velocity parse error at line 4', + structured: null, + warnings: [] + }); expect(errorHandler.handle).toHaveBeenCalledWith(httpError); }); + it('parses the structured 400 body and keeps the error inline (no global handler)', () => { + // Backend contract: 400 with { errors: [...] }. Service uses responseType:'text', + // so error.error arrives as a JSON string that the store must parse. + const structured = { + message: 'Encountered "#end" — expected #if', + errorType: 'ParseErrorException', + templateName: 'dynamic velocity', + line: 12, + column: 3 + }; + const httpError = new HttpErrorResponse({ + error: JSON.stringify({ errors: [structured] }), + status: 400, + statusText: 'Bad Request' + }); + runScriptSpy.mockReturnValue(throwError(() => httpError)); + + spectator.service.setCode('#if(true)'); + spectator.service.runScript(); + + expect(spectator.service.status()).toBe(ComponentStatus.LOADED); + expect(spectator.service.error()).toEqual({ + message: structured.message, + structured, + warnings: [] + }); + expect(spectator.service.hasError()).toBe(true); + // Velocity errors stay inline — the global (modal) handler must not fire. + expect(errorHandler.handle).not.toHaveBeenCalled(); + }); + it('does not add to history when the call errors', () => { runScriptSpy.mockReturnValue( throwError(() => new HttpErrorResponse({ status: 500, statusText: 'boom' })) diff --git a/core-web/libs/portlets/dot-velocity-playground/src/lib/dot-velocity-playground-page/store/dot-velocity-playground.store.ts b/core-web/libs/portlets/dot-velocity-playground/src/lib/dot-velocity-playground-page/store/dot-velocity-playground.store.ts index dbafa93a4f92..57fc064be00e 100644 --- a/core-web/libs/portlets/dot-velocity-playground/src/lib/dot-velocity-playground-page/store/dot-velocity-playground.store.ts +++ b/core-web/libs/portlets/dot-velocity-playground/src/lib/dot-velocity-playground-page/store/dot-velocity-playground.store.ts @@ -31,12 +31,15 @@ import { HISTORY_STORAGE_KEY, isValidHistory, isValidRatio, + parseVelocityError, SPLITTER_STORAGE_KEY, WRAP_STORAGE_KEY } from '../../dot-velocity-playground.utils'; import { + DotVelocityPlaygroundError, DotVelocityPlaygroundResponse, - DotVelocityResponseContentType + DotVelocityResponseContentType, + VelocityWarning } from '../../models/dot-velocity-playground.models'; import { DotVelocityPlaygroundService } from '../../services/dot-velocity-playground.service'; @@ -52,7 +55,8 @@ export interface VelocityPlaygroundState { output: string; outputContentType: DotVelocityResponseContentType; elapsedMs: number | null; - errorMessage: string | null; + error: DotVelocityPlaygroundError | null; + warnings: VelocityWarning[]; } const initialState: VelocityPlaygroundState = { @@ -64,7 +68,8 @@ const initialState: VelocityPlaygroundState = { output: '', outputContentType: 'plaintext', elapsedMs: null, - errorMessage: null + error: null, + warnings: [] }; export const DotVelocityPlaygroundStore = signalStore( @@ -75,7 +80,8 @@ export const DotVelocityPlaygroundStore = signalStore( hasOutput: computed( () => store.status() === ComponentStatus.LOADED && store.output().length > 0 ), - hasError: computed(() => store.errorMessage() !== null), + hasError: computed(() => store.error() !== null), + hasWarnings: computed(() => store.warnings().length > 0), canRun: computed( () => store.code().trim().length > 0 && store.status() !== ComponentStatus.LOADING ), @@ -118,7 +124,8 @@ export const DotVelocityPlaygroundStore = signalStore( patchState(store, { status: ComponentStatus.LOADING, output: '', - errorMessage: null, + error: null, + warnings: [], elapsedMs: null }) ), @@ -136,24 +143,26 @@ export const DotVelocityPlaygroundStore = signalStore( output: formatBody(response.body, response.contentType), outputContentType: response.contentType, elapsedMs: response.elapsedMs, + warnings: response.warnings, history: nextHistory }); }, error: (error: HttpErrorResponse) => { - // responseType: 'text' → error.error holds the raw VTL error body. - const serverBody = - typeof error?.error === 'string' && error.error.trim() - ? error.error - : null; + // The backend returns a structured 400 + // ({ errors: [...], warnings: [...] }) for Velocity + // parse/runtime errors. Keep those inline; only fall back to + // the global (modal) handler for infrastructure failures + // (network, 403 license, 500…). + const { error: parsed, isVelocityError } = + parseVelocityError(error); patchState(store, { status: ComponentStatus.LOADED, - errorMessage: - serverBody ?? - error?.error?.message ?? - error?.message ?? - 'velocityPlayground.error.unknown' + error: parsed, + warnings: parsed.warnings }); - httpErrorManager.handle(error); + if (!isVelocityError) { + httpErrorManager.handle(error); + } } }) ); diff --git a/core-web/libs/portlets/dot-velocity-playground/src/lib/dot-velocity-playground.utils.spec.ts b/core-web/libs/portlets/dot-velocity-playground/src/lib/dot-velocity-playground.utils.spec.ts index 13de54f749cf..a5305140f01f 100644 --- a/core-web/libs/portlets/dot-velocity-playground/src/lib/dot-velocity-playground.utils.spec.ts +++ b/core-web/libs/portlets/dot-velocity-playground/src/lib/dot-velocity-playground.utils.spec.ts @@ -1,15 +1,23 @@ +import { HttpErrorResponse } from '@angular/common/http'; + import { DEFAULT_SPLITTER_RATIO, dedupeAndCap, + firstLine, formatBody, + formatErrorTrace, formatHistoryLabel, + formatWarnings, getDownloadParams, HISTORY_MAX_ENTRIES, HISTORY_STORAGE_KEY, isValidHistory, isValidRatio, JSON_PRETTY_PRINT_MAX_BYTES, + parseVelocityError, + parseWarningsHeader, SPLITTER_STORAGE_KEY, + UNKNOWN_ERROR_KEY, VELOCITY_HELP_EXAMPLES } from './dot-velocity-playground.utils'; @@ -147,4 +155,288 @@ describe('dot-velocity-playground.utils', () => { expect(DEFAULT_SPLITTER_RATIO).toEqual([50, 50]); }); }); + + describe('parseVelocityError', () => { + const makeError = (body: unknown, status = 400): HttpErrorResponse => + new HttpErrorResponse({ error: body, status, statusText: 'error' }); + + it('parses the structured 400 body from a JSON string (responseType: text)', () => { + const detail = { + message: 'Encountered "#end" — expected #if', + errorType: 'ParseErrorException', + templateName: 'dynamic velocity', + line: 12, + column: 3 + }; + + const result = parseVelocityError(makeError(JSON.stringify({ errors: [detail] }))); + + expect(result.isVelocityError).toBe(true); + expect(result.error).toEqual({ + message: detail.message, + structured: detail, + warnings: [] + }); + }); + + it('includes warnings from the structured 400 body', () => { + const detail = { message: 'boom', errorType: 'ParseErrorException' }; + const warning = { + type: 'UNDEFINED_REFERENCE', + message: "Undefined reference '$x'", + reference: '$x', + line: 2 + }; + + const result = parseVelocityError( + makeError(JSON.stringify({ errors: [detail], warnings: [warning] })) + ); + + expect(result.error.warnings).toEqual([warning]); + }); + + it('parses the structured body when it arrives already as an object', () => { + const detail = { message: 'boom', errorType: 'MethodInvocationException' }; + + const result = parseVelocityError(makeError({ errors: [detail] })); + + expect(result.isVelocityError).toBe(true); + expect(result.error.structured).toEqual(detail); + }); + + it('takes only the first error when several are returned', () => { + const first = { message: 'first', line: 1 }; + const result = parseVelocityError( + makeError(JSON.stringify({ errors: [first, { message: 'second' }] })) + ); + + expect(result.error.structured).toEqual(first); + }); + + it('falls back to the raw text body for unstructured errors (not a velocity error)', () => { + const result = parseVelocityError(makeError('Something went wrong', 500)); + + expect(result.isVelocityError).toBe(false); + expect(result.error).toEqual({ + message: 'Something went wrong', + structured: null, + warnings: [] + }); + }); + + it('falls back to a nested error.message when there is no errors array', () => { + const result = parseVelocityError(makeError({ message: 'nested detail' }, 500)); + + expect(result.isVelocityError).toBe(false); + expect(result.error).toEqual({ + message: 'nested detail', + structured: null, + warnings: [] + }); + }); + + it('falls back to the unknown i18n key when nothing usable is present', () => { + const result = parseVelocityError(makeError(null, 0)); + + expect(result.isVelocityError).toBe(false); + expect(result.error.structured).toBeNull(); + // HttpErrorResponse synthesizes a generic message for status 0; when the body is + // empty we still guarantee a non-empty message for the banner. + expect(result.error.message.length).toBeGreaterThan(0); + }); + + it('does not treat an empty errors array as a velocity error', () => { + const result = parseVelocityError(makeError(JSON.stringify({ errors: [] }))); + + expect(result.isVelocityError).toBe(false); + }); + + it('ignores a malformed JSON string body and does not throw', () => { + const result = parseVelocityError(makeError('{ not valid json', 500)); + + expect(result.isVelocityError).toBe(false); + expect(result.error.message).toBe('{ not valid json'); + }); + + it('uses the unknown key constant for a null error', () => { + const result = parseVelocityError(null); + + expect(result.error.message).toBe(UNKNOWN_ERROR_KEY); + expect(result.error.structured).toBeNull(); + }); + }); + + describe('firstLine', () => { + it('returns the message unchanged when it is a single line', () => { + expect(firstLine('Something went wrong')).toBe('Something went wrong'); + }); + + it('returns only the first non-empty line of a multi-line message', () => { + const multi = + 'Encountered "<EOF>" at line 5, column 39\nWas expecting one of:\n "[" ...\n "(" ...'; + expect(firstLine(multi)).toBe('Encountered "<EOF>" at line 5, column 39'); + }); + + it('skips leading blank lines', () => { + expect(firstLine('\n\n real message\nmore')).toBe('real message'); + }); + + it('trims surrounding whitespace', () => { + expect(firstLine(' padded ')).toBe('padded'); + }); + }); + + describe('formatErrorTrace', () => { + it('returns just the resolved message for an unstructured error', () => { + const trace = formatErrorTrace( + { message: 'raw', structured: null, warnings: [] }, + 'Something went wrong' + ); + + expect(trace).toBe('Something went wrong'); + }); + + it('prefixes the header with the error type and appends template + location lines', () => { + const trace = formatErrorTrace( + { + message: 'ignored — resolvedMessage wins', + warnings: [], + structured: { + message: 'Encountered "#end"', + errorType: 'ParseErrorException', + templateName: 'dynamic velocity', + line: 12, + column: 3 + } + }, + 'Encountered "#end"' + ); + + expect(trace).toBe( + [ + 'ParseErrorException: Encountered "#end"', + ' at template "dynamic velocity"', + ' at line 12, column 3' + ].join('\n') + ); + }); + + it('uses the full detail (not the summary) as the header body when present', () => { + const trace = formatErrorTrace( + { + message: 'Encountered "<EOF>" at line 6, column 39', + warnings: [], + structured: { + message: 'Encountered "<EOF>" at line 6, column 39', + errorType: 'ParseErrorException', + detail: 'Encountered "<EOF>" at line 6, column 39\nWas expecting one of:\n "[" ...' + } + }, + 'Encountered "<EOF>" at line 6, column 39' + ); + + expect(trace).toBe( + 'ParseErrorException: Encountered "<EOF>" at line 6, column 39\nWas expecting one of:\n "[" ...' + ); + }); + + it('omits the column segment when only a line is reported', () => { + const trace = formatErrorTrace( + { + message: 'boom', + warnings: [], + structured: { message: 'boom', errorType: 'MethodInvocationException', line: 5 } + }, + 'boom' + ); + + expect(trace).toBe(['MethodInvocationException: boom', ' at line 5'].join('\n')); + }); + + it('renders the message alone when structured detail has no type or location', () => { + const trace = formatErrorTrace( + { message: 'boom', structured: { message: 'boom' }, warnings: [] }, + 'boom' + ); + + expect(trace).toBe('boom'); + }); + + it('appends collected warnings after the error', () => { + const trace = formatErrorTrace( + { + message: 'boom', + structured: { message: 'boom', errorType: 'ParseErrorException' }, + warnings: [ + { + type: 'UNDEFINED_REFERENCE', + message: "Undefined reference '$x'", + reference: '$x', + line: 2, + column: 1 + } + ] + }, + 'boom' + ); + + expect(trace).toBe( + [ + 'ParseErrorException: boom', + '', + '1 warning:', + " - [UNDEFINED_REFERENCE] Undefined reference '$x' (line 2, column 1)" + ].join('\n') + ); + }); + }); + + describe('formatWarnings', () => { + it('returns an empty string for no warnings', () => { + expect(formatWarnings([])).toBe(''); + }); + + it('formats a single warning with a singular header and location', () => { + expect( + formatWarnings([ + { + type: 'INVALID_METHOD', + message: 'nope() missing', + reference: '$o', + line: 4, + column: 2 + } + ]) + ).toBe( + ['1 warning:', ' - [INVALID_METHOD] nope() missing (line 4, column 2)'].join('\n') + ); + }); + + it('uses a plural header and omits location when unavailable', () => { + expect( + formatWarnings([ + { type: 'UNDEFINED_REFERENCE', message: 'a' }, + { type: 'NULL_SET', message: 'b' } + ]) + ).toBe(['2 warnings:', ' - [UNDEFINED_REFERENCE] a', ' - [NULL_SET] b'].join('\n')); + }); + }); + + describe('parseWarningsHeader', () => { + it('returns [] for null/empty', () => { + expect(parseWarningsHeader(null)).toEqual([]); + expect(parseWarningsHeader('')).toEqual([]); + expect(parseWarningsHeader(' ')).toEqual([]); + }); + + it('parses a JSON array of warnings', () => { + const warnings = [{ type: 'UNDEFINED_REFERENCE', message: 'a', reference: '$a' }]; + expect(parseWarningsHeader(JSON.stringify(warnings))).toEqual(warnings); + }); + + it('returns [] for malformed JSON or a non-array', () => { + expect(parseWarningsHeader('{ not json')).toEqual([]); + expect(parseWarningsHeader('{"not":"array"}')).toEqual([]); + }); + }); }); diff --git a/core-web/libs/portlets/dot-velocity-playground/src/lib/dot-velocity-playground.utils.ts b/core-web/libs/portlets/dot-velocity-playground/src/lib/dot-velocity-playground.utils.ts index 00bad6e980ea..654b1cd857d7 100644 --- a/core-web/libs/portlets/dot-velocity-playground/src/lib/dot-velocity-playground.utils.ts +++ b/core-web/libs/portlets/dot-velocity-playground/src/lib/dot-velocity-playground.utils.ts @@ -1,4 +1,12 @@ -import { DotVelocityResponseContentType } from './models/dot-velocity-playground.models'; +import { HttpErrorResponse } from '@angular/common/http'; + +import { + DotVelocityPlaygroundError, + DotVelocityResponseContentType, + VelocityError, + VelocityErrorResponse, + VelocityWarning +} from './models/dot-velocity-playground.models'; export const HISTORY_STORAGE_KEY = 'velocityPlayground'; export const SPLITTER_STORAGE_KEY = 'velocityPlayground.splitterRatio'; @@ -84,6 +92,169 @@ export const getDownloadParams = ( return { ext: 'txt', mime: 'text/plain' }; }; +/** i18n key used when we can't extract any usable message from a failed run. */ +export const UNKNOWN_ERROR_KEY = 'velocityPlayground.error.unknown'; + +/** Type guard: a single Velocity error object carrying at least a string `message`. */ +const isVelocityErrorObject = (value: unknown): value is VelocityError => + typeof value === 'object' && + value !== null && + typeof (value as VelocityError).message === 'string'; + +/** Type guard: the structured `{ errors: VelocityError[] }` body from a `400`. */ +const isVelocityErrorResponse = (value: unknown): value is VelocityErrorResponse => + typeof value === 'object' && + value !== null && + Array.isArray((value as VelocityErrorResponse).errors) && + (value as VelocityErrorResponse).errors.length > 0 && + isVelocityErrorObject((value as VelocityErrorResponse).errors[0]); + +/** + * Coerce an `HttpErrorResponse.error` into a plain object. The service uses + * `responseType: 'text'`, so a structured `400` arrives as a JSON *string* that + * must be parsed; a defensive object branch covers interceptors that may have + * already parsed it. Returns `null` for anything that isn't structured JSON. + */ +const coerceErrorBody = (raw: unknown): unknown => { + if (typeof raw === 'object' && raw !== null) return raw; + if (typeof raw === 'string') { + const trimmed = raw.trim(); + if (!trimmed.startsWith('{')) return null; + try { + return JSON.parse(trimmed); + } catch { + return null; + } + } + return null; +}; + +/** + * Normalize a failed `POST /api/vtl/dynamic` run into a `DotVelocityPlaygroundError`. + * + * Recognizes the structured `400` contract (`{ errors: [{ message, errorType, + * templateName, line, column }] }`) and returns its first error as `structured`. + * Otherwise falls back to the raw text body, then the nested `error.message`, + * then the top-level message, then an i18n key — always yielding a non-empty + * `message` for the banner. + * + * `isVelocityError` is `true` only for the structured Velocity contract; callers + * use it to keep VTL syntax/runtime errors inline and skip the global (modal) + * error handler, which should stay reserved for infrastructure failures. + */ +export const parseVelocityError = ( + error: HttpErrorResponse | null | undefined +): { error: DotVelocityPlaygroundError; isVelocityError: boolean } => { + const body = coerceErrorBody(error?.error); + + if (isVelocityErrorResponse(body)) { + const first = body.errors[0]; + const warnings = Array.isArray(body.warnings) ? body.warnings : []; + return { + error: { message: first.message, structured: first, warnings }, + isVelocityError: true + }; + } + + const rawText = + typeof error?.error === 'string' && error.error.trim() ? error.error.trim() : null; + const nestedMessage = + isVelocityErrorObject(body) && body.message.trim() ? body.message.trim() : null; + + const message = rawText ?? nestedMessage ?? error?.message?.trim() ?? UNKNOWN_ERROR_KEY; + + return { + error: { message: message || UNKNOWN_ERROR_KEY, structured: null, warnings: [] }, + isVelocityError: false + }; +}; + +/** + * Parse the `X-Dot-Velocity-Warnings` response header (a JSON array of + * `VelocityWarning`) sent on a successful run. Returns an empty array when the + * header is absent, empty, or malformed — warnings are best-effort context and + * must never break the success path. + */ +export const parseWarningsHeader = (raw: string | null | undefined): VelocityWarning[] => { + if (!raw || !raw.trim()) return []; + try { + const parsed: unknown = JSON.parse(raw); + return Array.isArray(parsed) ? (parsed as VelocityWarning[]) : []; + } catch { + return []; + } +}; + +/** + * Reduce a (possibly multi-line) error message to a single-line summary for the + * banner. Velocity parse errors carry a long "Was expecting one of: …" dump on + * subsequent lines — that belongs in the Monaco trace, not the banner. Returns + * the first non-empty line, trimmed. + */ +export const firstLine = (message: string): string => { + const match = message.split('\n').find((line) => line.trim().length > 0); + return (match ?? message).trim(); +}; + +/** + * Render a normalized Velocity error as a plain-text, stack-trace-style block for + * the read-only Monaco output pane. Shows the full engine output (the backend's + * `detail` when present, otherwise the summary), the location, and any collected + * warnings — everything the caller needs to fix the code, in one copyable block. + * + * `message` may be an i18n key for the unknown-error fallback; the caller passes + * an already-resolved string via `resolvedMessage` so this stays pure/DOM-free. + */ +export const formatErrorTrace = ( + error: DotVelocityPlaygroundError, + resolvedMessage: string +): string => { + const lines: string[] = []; + const detail = error.structured; + + // Prefer the full engine output (detail) over the one-line summary for the body. + const body = detail?.detail?.trim() ? detail.detail.trim() : resolvedMessage; + const header = detail?.errorType ? `${detail.errorType}: ${body}` : body; + lines.push(header); + + if (detail) { + if (detail.templateName) { + lines.push(` at template "${detail.templateName}"`); + } + if (detail.line !== undefined && detail.line !== null) { + const col = + detail.column !== undefined && detail.column !== null + ? `, column ${detail.column}` + : ''; + lines.push(` at line ${detail.line}${col}`); + } + } + + const warningLines = formatWarnings(error.warnings); + if (warningLines) { + lines.push('', warningLines); + } + + return lines.join('\n'); +}; + +/** + * Format a list of Velocity warnings as a plain-text block for the trace pane. + * Returns an empty string when there are none. + */ +export const formatWarnings = (warnings: VelocityWarning[]): string => { + if (!warnings.length) return ''; + const header = warnings.length === 1 ? '1 warning:' : `${warnings.length} warnings:`; + const lines = warnings.map((w) => { + const loc = + w.line !== undefined && w.line !== null + ? ` (line ${w.line}${w.column !== undefined && w.column !== null ? `, column ${w.column}` : ''})` + : ''; + return ` - [${w.type}] ${w.message}${loc}`; + }); + return [header, ...lines].join('\n'); +}; + /** * Static catalog of example snippets shown in the help popover. Titles and * descriptions are i18n keys resolved with DotMessagePipe at render time. diff --git a/core-web/libs/portlets/dot-velocity-playground/src/lib/models/dot-velocity-playground.models.ts b/core-web/libs/portlets/dot-velocity-playground/src/lib/models/dot-velocity-playground.models.ts index 0a66a751dad2..aa4768c5222a 100644 --- a/core-web/libs/portlets/dot-velocity-playground/src/lib/models/dot-velocity-playground.models.ts +++ b/core-web/libs/portlets/dot-velocity-playground/src/lib/models/dot-velocity-playground.models.ts @@ -8,4 +8,56 @@ export interface DotVelocityPlaygroundResponse { body: string; contentType: DotVelocityResponseContentType; elapsedMs: number; + /** Non-fatal warnings parsed from the `X-Dot-Velocity-Warnings` response header. */ + warnings: VelocityWarning[]; +} + +/** + * A single Velocity error as returned by `POST /api/vtl/dynamic` with a `400`. + * Mirrors the backend `VelocityErrorView`. `message` is a concise one-liner; + * `detail` carries the full engine output (for parse errors, the exhaustive + * "was expecting one of …" token list). `line`/`column`/`templateName` are only + * present when Velocity reports a position. + */ +export interface VelocityError { + message: string; + errorType?: string; + templateName?: string; + line?: number; + column?: number; + detail?: string; +} + +/** + * A non-fatal Velocity warning (undefined reference, null method result). + * Mirrors the backend `VelocityWarningView`. + */ +export interface VelocityWarning { + type: 'UNDEFINED_REFERENCE' | 'NULL_METHOD_RESULT' | 'INVALID_METHOD' | 'NULL_SET'; + message: string; + reference?: string; + line?: number; + column?: number; +} + +/** + * The `{ "errors": [...], "warnings": [...] }` body the backend returns with a + * `400` when the submitted Velocity fails to parse or evaluate. + */ +export interface VelocityErrorResponse { + errors: VelocityError[]; + warnings?: VelocityWarning[]; +} + +/** + * Normalized error the store exposes to the view. `structured` carries the + * parsed Velocity error detail when the backend returned the structured `400` + * contract; `message` is always populated (an i18n key or a raw string) so the + * banner has something to show even for unstructured/infra failures. `warnings` + * carries any non-fatal issues reported alongside the error. + */ +export interface DotVelocityPlaygroundError { + message: string; + structured: VelocityError | null; + warnings: VelocityWarning[]; } diff --git a/core-web/libs/portlets/dot-velocity-playground/src/lib/services/dot-velocity-playground.service.spec.ts b/core-web/libs/portlets/dot-velocity-playground/src/lib/services/dot-velocity-playground.service.spec.ts index 63c78d3b1803..389ef5ad3514 100644 --- a/core-web/libs/portlets/dot-velocity-playground/src/lib/services/dot-velocity-playground.service.spec.ts +++ b/core-web/libs/portlets/dot-velocity-playground/src/lib/services/dot-velocity-playground.service.spec.ts @@ -78,4 +78,38 @@ describe('DotVelocityPlaygroundService', () => { expect(received?.contentType).toBe('plaintext'); expect(received?.body).toBe('raw output'); }); + + it('parses warnings from the X-Dot-Velocity-Warnings header', () => { + const warnings = [ + { type: 'UNDEFINED_REFERENCE', message: "Undefined reference '$x'", reference: '$x' } + ]; + let received: DotVelocityPlaygroundResponse | undefined; + spectator.service.runScript({ velocity: '$x' }).subscribe((res) => { + received = res; + }); + + const req = spectator.expectOne('/api/vtl/dynamic/', HttpMethod.POST); + req.flush('output', { + headers: { + 'Content-Type': 'text/plain', + 'X-Dot-Velocity-Warnings': JSON.stringify(warnings) + }, + status: 200, + statusText: 'OK' + }); + + expect(received?.warnings).toEqual(warnings); + }); + + it('returns an empty warnings array when the header is absent', () => { + let received: DotVelocityPlaygroundResponse | undefined; + spectator.service.runScript({ velocity: 'x' }).subscribe((res) => { + received = res; + }); + + const req = spectator.expectOne('/api/vtl/dynamic/', HttpMethod.POST); + req.flush('output', { status: 200, statusText: 'OK' }); + + expect(received?.warnings).toEqual([]); + }); }); diff --git a/core-web/libs/portlets/dot-velocity-playground/src/lib/services/dot-velocity-playground.service.ts b/core-web/libs/portlets/dot-velocity-playground/src/lib/services/dot-velocity-playground.service.ts index ab8e134973ca..3174568aaae9 100644 --- a/core-web/libs/portlets/dot-velocity-playground/src/lib/services/dot-velocity-playground.service.ts +++ b/core-web/libs/portlets/dot-velocity-playground/src/lib/services/dot-velocity-playground.service.ts @@ -5,6 +5,7 @@ import { Injectable, inject } from '@angular/core'; import { map } from 'rxjs/operators'; +import { parseWarningsHeader } from '../dot-velocity-playground.utils'; import { DotVelocityPlaygroundForm, DotVelocityPlaygroundResponse, @@ -27,7 +28,8 @@ export class DotVelocityPlaygroundService { map((response) => ({ body: response.body ?? '', contentType: this.#mapContentType(response.headers.get('content-type')), - elapsedMs: Date.now() - started + elapsedMs: Date.now() - started, + warnings: parseWarningsHeader(response.headers.get('X-Dot-Velocity-Warnings')) })) ); } diff --git a/core-web/libs/portlets/edit-ema/ui/src/index.ts b/core-web/libs/portlets/edit-ema/ui/src/index.ts index 8505b36592ad..2c2ab7418e12 100644 --- a/core-web/libs/portlets/edit-ema/ui/src/index.ts +++ b/core-web/libs/portlets/edit-ema/ui/src/index.ts @@ -8,6 +8,9 @@ export * from './lib/dot-content-compare/dot-content-compare.component'; export * from './lib/dot-content-compare/components/dot-content-compare-dialog/dot-content-compare-dialog.component'; export * from './lib/dot-page-scanner-report/dot-page-scanner-report.component'; export * from './lib/dot-page-scanner-report/dot-page-scanner.service'; +// Scan-result display models + grouping, shared with the Accessibility Studio. +export * from './lib/dot-page-scanner-report/models'; +export * from './lib/dot-page-scanner-report/a11y-groups'; // Reusable content-type palette list (shared with Content Drive) export * from './lib/palette/components/dot-uve-palette-list/dot-uve-palette-list.component'; diff --git a/core-web/libs/portlets/edit-ema/ui/src/lib/dot-page-scanner-report/a11y-groups.spec.ts b/core-web/libs/portlets/edit-ema/ui/src/lib/dot-page-scanner-report/a11y-groups.spec.ts new file mode 100644 index 000000000000..69e5f9ffdd6f --- /dev/null +++ b/core-web/libs/portlets/edit-ema/ui/src/lib/dot-page-scanner-report/a11y-groups.spec.ts @@ -0,0 +1,59 @@ +import { buildA11yGroups } from './a11y-groups'; +import { PageScannerA11yResponse } from './dot-page-scanner.service'; + +/** Wrap axe nodes in the scanner response shape `buildA11yGroups` consumes. */ +function scanWith(target: string[] | undefined): PageScannerA11yResponse { + return { + ok: true, + standard: 'WCAG2AA', + axe: { + violations: [ + { + id: 'button-name', + impact: 'serious', + description: 'Buttons must have discernible text', + help: '', + helpUrl: 'https://example.com/button-name', + tags: [], + nodes: [{ html: '<button>', target, impact: 'serious', failureSummary: '' }] + } + ], + incomplete: [] + } + } as unknown as PageScannerA11yResponse; +} + +describe('buildA11yGroups', () => { + describe('selector', () => { + it('takes the LAST entry of the target chain, not a joined list', () => { + // axe's `target` is an ancestor chain — one entry per frame or shadow-root + // boundary crossed — so the element's own selector is the last one. Joining + // them made a selector LIST, and `querySelector` returns whichever matches + // FIRST, so the marker overlay outlined the iframe instead of the button. + const [group] = buildA11yGroups(scanWith(['iframe#promo', 'button.cta'])); + + expect(group.items[0].selector).toBe('button.cta'); + expect(group.items[0].selector).not.toContain('iframe'); + }); + + it('uses the only entry when the element is not nested', () => { + const [group] = buildA11yGroups(scanWith(['button.cta'])); + expect(group.items[0].selector).toBe('button.cta'); + }); + + it('handles a deeper chain (frame inside a frame)', () => { + const [group] = buildA11yGroups(scanWith(['iframe#outer', 'iframe#inner', 'a.link'])); + expect(group.items[0].selector).toBe('a.link'); + }); + + it('falls back to an empty selector when axe reported no target', () => { + expect(buildA11yGroups(scanWith(undefined))[0].items[0].selector).toBe(''); + expect(buildA11yGroups(scanWith([]))[0].items[0].selector).toBe(''); + }); + }); + + it('returns nothing for a null or axe-less payload', () => { + expect(buildA11yGroups(null)).toEqual([]); + expect(buildA11yGroups({ ok: true } as unknown as PageScannerA11yResponse)).toEqual([]); + }); +}); diff --git a/core-web/libs/portlets/edit-ema/ui/src/lib/dot-page-scanner-report/a11y-groups.ts b/core-web/libs/portlets/edit-ema/ui/src/lib/dot-page-scanner-report/a11y-groups.ts new file mode 100644 index 000000000000..158f933cf38b --- /dev/null +++ b/core-web/libs/portlets/edit-ema/ui/src/lib/dot-page-scanner-report/a11y-groups.ts @@ -0,0 +1,46 @@ +import { AxeRule, PageScannerA11yResponse } from './dot-page-scanner.service'; +import { A11yFindingType, A11yGroup } from './models'; + +/** + * Flatten an axe scan result into the display groups both scanner surfaces render: + * `violations` become errors, `incomplete` (needs manual review) become warnings. One axe + * rule maps to one group, and its `nodes` are the flagged elements. + * + * Lives here — beside the models and the service whose response it consumes — because two + * features need it: UVE's scanner report panel and the Accessibility Studio. It was + * previously implemented twice, and the copies had already drifted: the same `target.join` + * bug had to be fixed in both, and one copy declared `impact` as `AxeImpact | null` when + * `AxeImpact` already includes `null`. + */ +export function buildA11yGroups(data: PageScannerA11yResponse | null): A11yGroup[] { + const axe = data?.axe; + if (!axe) { + return []; + } + + return [ + ...mapRules(axe.violations ?? [], 'error'), + ...mapRules(axe.incomplete ?? [], 'warning') + ]; +} + +function mapRules(rules: AxeRule[], type: A11yFindingType): A11yGroup[] { + return rules.map((rule) => ({ + code: rule.id, + type, + message: rule.description ?? rule.help ?? '', + impact: rule.impact ?? null, + helpUrl: rule.helpUrl ?? '', + items: (rule.nodes ?? []).map((node) => ({ + context: node.html, + // LAST entry, not a join. axe's `target` is an ancestor CHAIN — one entry per + // frame or shadow-root boundary crossed on the way to the element — so the + // element's own selector is the last one. Joining them produced a selector LIST, + // which `querySelector` resolves to whichever matches FIRST: for + // `['iframe#promo', 'button.cta']` that is the iframe, so an overlay drawn from + // this selector outlined the whole embed instead of the button inside it. + selector: node.target?.at(-1) ?? '' + })), + count: rule.nodes?.length ?? 0 + })); +} diff --git a/core-web/libs/portlets/edit-ema/ui/src/lib/dot-page-scanner-report/dot-page-scan-loading/dot-page-scan-loading.component.ts b/core-web/libs/portlets/edit-ema/ui/src/lib/dot-page-scanner-report/dot-page-scan-loading/dot-page-scan-loading.component.ts index 614704f648f2..d5a4a5598c8b 100644 --- a/core-web/libs/portlets/edit-ema/ui/src/lib/dot-page-scanner-report/dot-page-scan-loading/dot-page-scan-loading.component.ts +++ b/core-web/libs/portlets/edit-ema/ui/src/lib/dot-page-scanner-report/dot-page-scan-loading/dot-page-scan-loading.component.ts @@ -4,7 +4,6 @@ import { DotMessagePipe } from '@dotcms/ui'; @Component({ selector: 'dot-page-scan-loading', - standalone: true, imports: [DotMessagePipe], templateUrl: './dot-page-scan-loading.component.html', styles: [ diff --git a/core-web/libs/portlets/edit-ema/ui/src/lib/dot-page-scanner-report/dot-page-scanner-a11y-report/dot-page-scanner-a11y-report.component.html b/core-web/libs/portlets/edit-ema/ui/src/lib/dot-page-scanner-report/dot-page-scanner-a11y-report/dot-page-scanner-a11y-report.component.html index 35c29d1dc768..9f13967d30d3 100644 --- a/core-web/libs/portlets/edit-ema/ui/src/lib/dot-page-scanner-report/dot-page-scanner-a11y-report/dot-page-scanner-a11y-report.component.html +++ b/core-web/libs/portlets/edit-ema/ui/src/lib/dot-page-scanner-report/dot-page-scanner-a11y-report/dot-page-scanner-a11y-report.component.html @@ -9,7 +9,7 @@ <p-card class="flex-1"> <div class="mb-1 flex items-center justify-between"> <p class="m-0 text-2xl font-bold text-red-700"> - {{ a11yData().findings?.byType?.errors ?? 0 }} + {{ errorCount() }} </p> <span class="material-symbols-outlined text-red-500">cancel</span> </div> @@ -18,21 +18,12 @@ <p-card class="flex-1"> <div class="mb-1 flex items-center justify-between"> <p class="m-0 text-2xl font-bold text-yellow-600"> - {{ a11yData().findings?.byType?.warnings ?? 0 }} + {{ warningCount() }} </p> <span class="material-symbols-outlined text-yellow-500">warning</span> </div> <p class="m-0 text-gray-600">{{ 'page.scanner.a11y.warnings' | dm }}</p> </p-card> - <p-card class="flex-1"> - <div class="mb-1 flex items-center justify-between"> - <p class="m-0 text-2xl font-bold text-blue-700"> - {{ a11yData().findings?.byType?.notices ?? 0 }} - </p> - <span class="material-symbols-outlined text-blue-500">info</span> - </div> - <p class="m-0 text-gray-600">{{ 'page.scanner.a11y.notices' | dm }}</p> - </p-card> </div> <h3 class="m-0 mb-4 text-xl font-bold">{{ 'page.scanner.a11y.findings' | dm }}</h3> @@ -49,22 +40,9 @@ <h3 class="m-0 mb-4 text-xl font-bold">{{ 'page.scanner.a11y.findings' | dm }}</ <p-accordion-header> <div class="flex w-full items-center justify-between gap-2 pr-4"> <div class="flex items-center gap-4"> - <dot-color-icon - [color]=" - group.type === 'error' - ? 'red' - : group.type === 'warning' - ? 'yellow' - : 'blue' - "> + <dot-color-icon [color]="group.type === 'error' ? 'red' : 'yellow'"> <span class="material-symbols-outlined"> - {{ - group.type === 'error' - ? 'cancel' - : group.type === 'warning' - ? 'warning' - : 'info' - }} + {{ group.type === 'error' ? 'cancel' : 'warning' }} </span> </dot-color-icon> @@ -72,35 +50,38 @@ <h3 class="m-0 mb-4 text-xl font-bold">{{ 'page.scanner.a11y.findings' | dm }}</ <span class="text-lg font-semibold text-gray-900"> {{ group.code }} </span> - <p-chip - [pt]="{ - label: 'text-sm font-normal text-gray-700' - }" - [dt]="{ - root: { - paddingX: '0.5rem', - paddingY: '0.1rem' - } - }" - [style]=" - group.impact === 'critical' || - group.impact === 'serious' - ? { - '--p-chip-background': 'var(--p-red-100)', - '--p-chip-color': 'var(--p-red-700)' - } - : group.impact === 'moderate' - ? { - '--p-chip-background': - 'var(--p-yellow-100)', - '--p-chip-color': 'var(--p-yellow-700)' - } - : { - '--p-chip-background': 'var(--p-blue-100)', - '--p-chip-color': 'var(--p-blue-700)' - } - " - [label]="group.impact" /> + @if (group.impact) { + <p-chip + [pt]="{ + label: 'text-sm font-normal text-gray-700' + }" + [dt]="{ + root: { + paddingX: '0.5rem', + paddingY: '0.1rem' + } + }" + [style]=" + group.impact === 'critical' || + group.impact === 'serious' + ? { + '--p-chip-background': 'var(--p-red-100)', + '--p-chip-color': 'var(--p-red-700)' + } + : group.impact === 'moderate' + ? { + '--p-chip-background': + 'var(--p-yellow-100)', + '--p-chip-color': 'var(--p-yellow-700)' + } + : { + '--p-chip-background': + 'var(--p-blue-100)', + '--p-chip-color': 'var(--p-blue-700)' + } + " + [label]="group.impact" /> + } </div> </div> <span class="text-sm font-normal text-gray-700"> diff --git a/core-web/libs/portlets/edit-ema/ui/src/lib/dot-page-scanner-report/dot-page-scanner-a11y-report/dot-page-scanner-a11y-report.component.ts b/core-web/libs/portlets/edit-ema/ui/src/lib/dot-page-scanner-report/dot-page-scanner-a11y-report/dot-page-scanner-a11y-report.component.ts index a5e8179a3b86..09928b1c8674 100644 --- a/core-web/libs/portlets/edit-ema/ui/src/lib/dot-page-scanner-report/dot-page-scanner-a11y-report/dot-page-scanner-a11y-report.component.ts +++ b/core-web/libs/portlets/edit-ema/ui/src/lib/dot-page-scanner-report/dot-page-scanner-a11y-report/dot-page-scanner-a11y-report.component.ts @@ -6,12 +6,11 @@ import { ChipModule } from 'primeng/chip'; import { DotColorIconComponent, DotMessagePipe } from '@dotcms/ui'; -import { PageScannerA11yItem, PageScannerA11yResponse } from '../dot-page-scanner.service'; -import { A11yGroup } from '../models'; +import { buildA11yGroups } from '../a11y-groups'; +import { PageScannerA11yResponse } from '../dot-page-scanner.service'; @Component({ selector: 'dot-page-scanner-a11y-report', - standalone: true, imports: [AccordionModule, CardModule, ChipModule, DotColorIconComponent, DotMessagePipe], templateUrl: './dot-page-scanner-a11y-report.component.html', changeDetection: ChangeDetectionStrategy.OnPush @@ -19,7 +18,22 @@ import { A11yGroup } from '../models'; export class DotPageScannerA11yReportComponent { a11yData = input.required<PageScannerA11yResponse>(); - protected a11yGroups = computed(() => this.buildA11yGroups(this.a11yData())); + protected a11yGroups = computed(() => buildA11yGroups(this.a11yData())); + + /** One accordion group per axe rule that flagged at least one element. */ + protected errorCount = computed(() => + this.a11yGroups() + .filter((group) => group.type === 'error') + .reduce((total, group) => total + group.count, 0) + ); + + /** Elements axe could not conclusively check (its `incomplete` results). */ + protected warningCount = computed(() => + this.a11yGroups() + .filter((group) => group.type === 'warning') + .reduce((total, group) => total + group.count, 0) + ); + protected readonly accordionPt = { motion: { root: { @@ -29,36 +43,4 @@ export class DotPageScannerA11yReportComponent { } } }; - - private buildA11yGroups(data: PageScannerA11yResponse): A11yGroup[] { - const items: PageScannerA11yItem[] = data.findings?.items ?? data.issues ?? []; - const map = new Map<string, A11yGroup>(); - - for (const item of items) { - if (map.has(item.code)) { - const existingGroup = map.get(item.code); - - if (!existingGroup) { - continue; - } - - existingGroup.items.push(item); - existingGroup.count++; - } else { - const impact = item.runnerExtras?.impact ?? ''; - const type = item.type; - map.set(item.code, { - message: item.runnerExtras?.description ?? '', - code: item.code, - type, - impact, - helpUrl: item.runnerExtras?.helpUrl ?? '', - items: [item], - count: 1 - }); - } - } - - return Array.from(map.values()); - } } diff --git a/core-web/libs/portlets/edit-ema/ui/src/lib/dot-page-scanner-report/dot-page-scanner-geo-report/dot-page-scanner-geo-report.component.ts b/core-web/libs/portlets/edit-ema/ui/src/lib/dot-page-scanner-report/dot-page-scanner-geo-report/dot-page-scanner-geo-report.component.ts index 4220416d6d8e..1b7e31ff9df5 100644 --- a/core-web/libs/portlets/edit-ema/ui/src/lib/dot-page-scanner-report/dot-page-scanner-geo-report/dot-page-scanner-geo-report.component.ts +++ b/core-web/libs/portlets/edit-ema/ui/src/lib/dot-page-scanner-report/dot-page-scanner-geo-report/dot-page-scanner-geo-report.component.ts @@ -14,7 +14,6 @@ import { GeoCategory } from '../models'; @Component({ selector: 'dot-page-scanner-geo-report', - standalone: true, imports: [AccordionModule, CardModule, ChartModule, ChipModule, DecimalPipe, DotMessagePipe], templateUrl: './dot-page-scanner-geo-report.component.html', changeDetection: ChangeDetectionStrategy.OnPush diff --git a/core-web/libs/portlets/edit-ema/ui/src/lib/dot-page-scanner-report/dot-page-scanner-message/dot-page-scanner-message.component.ts b/core-web/libs/portlets/edit-ema/ui/src/lib/dot-page-scanner-report/dot-page-scanner-message/dot-page-scanner-message.component.ts index 4cb991a88a69..c60e683001a5 100644 --- a/core-web/libs/portlets/edit-ema/ui/src/lib/dot-page-scanner-report/dot-page-scanner-message/dot-page-scanner-message.component.ts +++ b/core-web/libs/portlets/edit-ema/ui/src/lib/dot-page-scanner-report/dot-page-scanner-message/dot-page-scanner-message.component.ts @@ -2,7 +2,6 @@ import { ChangeDetectionStrategy, Component, input } from '@angular/core'; @Component({ selector: 'dot-page-scanner-message', - standalone: true, templateUrl: './dot-page-scanner-message.component.html', changeDetection: ChangeDetectionStrategy.OnPush }) diff --git a/core-web/libs/portlets/edit-ema/ui/src/lib/dot-page-scanner-report/dot-page-scanner-report.component.ts b/core-web/libs/portlets/edit-ema/ui/src/lib/dot-page-scanner-report/dot-page-scanner-report.component.ts index f212f3b57abf..3152e7a3783c 100644 --- a/core-web/libs/portlets/edit-ema/ui/src/lib/dot-page-scanner-report/dot-page-scanner-report.component.ts +++ b/core-web/libs/portlets/edit-ema/ui/src/lib/dot-page-scanner-report/dot-page-scanner-report.component.ts @@ -40,7 +40,6 @@ interface DotPageScannerState { @Component({ selector: 'dot-page-scanner-report', - standalone: true, providers: [DotPageScannerService], imports: [ DialogModule, diff --git a/core-web/libs/portlets/edit-ema/ui/src/lib/dot-page-scanner-report/dot-page-scanner.service.ts b/core-web/libs/portlets/edit-ema/ui/src/lib/dot-page-scanner-report/dot-page-scanner.service.ts index feb4a365d985..70a86e784c3f 100644 --- a/core-web/libs/portlets/edit-ema/ui/src/lib/dot-page-scanner-report/dot-page-scanner.service.ts +++ b/core-web/libs/portlets/edit-ema/ui/src/lib/dot-page-scanner-report/dot-page-scanner.service.ts @@ -3,20 +3,45 @@ import { Observable } from 'rxjs'; import { HttpClient } from '@angular/common/http'; import { Injectable, inject } from '@angular/core'; -export interface PageScannerA11yItem { - code: string; - type: 'error' | 'warning' | 'notice'; - typeCode: number; - message: string; - context: string; - selector: string; - runner: string; - runnerExtras: { - description: string; - impact: string; - help: string; - helpUrl: string; - }; +/** + * Severity reported by axe-core for a rule/node. + */ +export type AxeImpact = 'critical' | 'serious' | 'moderate' | 'minor' | null; + +/** + * A single DOM element flagged by an axe rule. + */ +export interface AxeNode { + html: string; + target: string[]; + impact: AxeImpact; + failureSummary: string; +} + +/** + * A raw axe-core rule result. The same shape is used for both `violations` + * (confirmed failures) and `incomplete` (needs manual review). + */ +export interface AxeRule { + id: string; + impact: AxeImpact; + tags: string[]; + description: string; + help: string; + helpUrl: string; + nodes: AxeNode[]; +} + +/** + * Raw axe-core run payload as returned by the external scanner. + */ +export interface AxeResult { + testEngine: { name: string; version: string }; + testRunner: { name: string }; + timestamp: string; + url: string; + violations: AxeRule[]; + incomplete: AxeRule[]; } export interface PageScannerA11yResponse { @@ -27,29 +52,15 @@ export interface PageScannerA11yResponse { documentTitle: string; standard: string; runners: string[]; + stylesheets: string[]; authenticatedRequest: boolean; authHeaderMode: string; - counts: { - errors: number; - warnings: number; - notices: number; - }; - totalIssues: number; - findings: { - total: number; - byType: { - errors: number; - warnings: number; - notices: number; - }; - items: PageScannerA11yItem[]; - }; - issues: PageScannerA11yItem[]; + axe: AxeResult; screenshot: { captured: boolean; - fileName: string; - endpoint: string; - mimeType: string; + fileName?: string; + endpoint?: string; + mimeType?: string; }; } @@ -88,7 +99,30 @@ export class DotPageScannerService { private http = inject(HttpClient); checkA11y(url: string): Observable<PageScannerA11yResponse> { - return this.http.post<PageScannerA11yResponse>('/api/v1/page-scanner/a11y/check', { url }); + // Never scan in EDIT_MODE: dotCMS injects editor-only chrome (drag handles, + // add-content buttons, etc.) into the EDIT_MODE render, which axe flags as + // accessibility violations that don't exist on the real page. Force + // PREVIEW_MODE so the scan sees the page as visitors do. Enforced here at the + // single chokepoint so no caller can accidentally scan EDIT_MODE. + const scanUrl = this.forcePreviewMode(url); + + return this.http.post<PageScannerA11yResponse>('/api/v1/page-scanner/a11y/check', { + url: scanUrl + }); + } + + /** Rewrite any `mode=EDIT_MODE` on the URL to `PREVIEW_MODE` (see checkA11y). */ + private forcePreviewMode(url: string): string { + try { + const parsed = new URL(url, window.location.origin); + if (parsed.searchParams.get('mode') === 'EDIT_MODE') { + parsed.searchParams.set('mode', 'PREVIEW_MODE'); + } + return parsed.toString(); + } catch { + // Fall back to a plain string replace for non-absolute / unparseable URLs. + return url.replace('mode=EDIT_MODE', 'mode=PREVIEW_MODE'); + } } checkGeo(url: string): Observable<PageScannerGeoResponse> { diff --git a/core-web/libs/portlets/edit-ema/ui/src/lib/dot-page-scanner-report/models.ts b/core-web/libs/portlets/edit-ema/ui/src/lib/dot-page-scanner-report/models.ts index 4f1086a8808d..b2247ccdd72e 100644 --- a/core-web/libs/portlets/edit-ema/ui/src/lib/dot-page-scanner-report/models.ts +++ b/core-web/libs/portlets/edit-ema/ui/src/lib/dot-page-scanner-report/models.ts @@ -1,17 +1,42 @@ -import { PageScannerA11yItem } from './dot-page-scanner.service'; +import { AxeImpact, AxeNode } from './dot-page-scanner.service'; export type ReportType = 'a11y' | 'geo'; +/** + * UI classification derived from the axe section a rule came from: + * confirmed `violations` map to `error`, `incomplete` (needs review) to `warning`. + */ +export type A11yFindingType = 'error' | 'warning'; + +/** + * A flattened element flagged by an axe rule, ready for display. + */ +export interface A11yGroupItem { + /** Outer HTML of the offending element. */ + context: string; + /** + * CSS selector for the element itself — the LAST entry of axe's `target` chain, which + * is scoped to the innermost frame or shadow root containing it (see `mapRules`). + */ + selector: string; +} + +/** + * One axe rule grouped with every element it flagged. + */ export interface A11yGroup { code: string; - type: 'error' | 'warning' | 'notice'; + type: A11yFindingType; message: string; - impact: string; + impact: AxeImpact; helpUrl: string; - items: PageScannerA11yItem[]; + items: A11yGroupItem[]; count: number; } +/** Re-exported for convenience where node-level data is needed. */ +export type { AxeNode }; + export interface GeoCategorySignal { key: string; score: number; diff --git a/core-web/libs/sdk/ai/README.md b/core-web/libs/sdk/ai/README.md index a7f5157a6f24..266acf011c4f 100644 --- a/core-web/libs/sdk/ai/README.md +++ b/core-web/libs/sdk/ai/README.md @@ -47,7 +47,7 @@ await dotcms.run(code); // SANDBOXED — a model wrote `code`. | `@dotcms/ai/runtime` | Most callers — the front door | `createRuntime`, `defineAdapter`, errors | dotCMS-wired | | `@dotcms/ai/sandbox` | Power users / custom adapters | `createSandbox`, `defineAdapter`, `Executor`, types, errors | **fully generic, lint-enforced** | | `@dotcms/ai/adapter` | Power users | `dotcmsAdapter`, `requestCore`, context loading + cache | dotCMS-specific | -| `@dotcms/ai/spec` | The search use case | the OpenAPI spec (opt-in; keeps the ~550KB off the default path) | dotCMS-specific | +| `@dotcms/ai/spec` | The search use case | the OpenAPI spec (opt-in; keeps the ~400KB off the default path) | dotCMS-specific | `@dotcms/ai` is a pure namespace — there is no bare import; everything is reached through a subpath. It is an **umbrella** for growth: future AI surfaces (RAG, embeddings, custom agents, harness) land as new subpaths under the same package. @@ -123,9 +123,11 @@ pnpm nx run sdk-ai:generate-spec pnpm nx run sdk-ai:generate-spec -- http://localhost:8080/api/openapi.json ``` -The script filters the spec to the endpoints in `ALLOWED_PREFIXES` (see `scripts/generate-spec.ts`), -dereferences `$ref`s, and strips response schemas to keep the file small. Because the spec is -regenerated at build time, there is nothing to commit. +The script filters the spec to the endpoints in `ALLOWED_PREFIXES` (see `scripts/spec-transform.ts`), +keeps request/response `$ref`s, and prunes `components.schemas` to just the schemas those endpoints +reference. Keeping `$ref`s (rather than dereferencing) dedupes shared schemas and keeps the file +small (~400KB). The output is compact JSON (machine-read only) — use `jq` to inspect it. Because the +spec is regenerated at build time, there is nothing to commit. ## Commands diff --git a/core-web/libs/sdk/ai/package.json b/core-web/libs/sdk/ai/package.json index e21c3530f10c..06ca2c20ecaa 100644 --- a/core-web/libs/sdk/ai/package.json +++ b/core-web/libs/sdk/ai/package.json @@ -1,6 +1,6 @@ { "name": "@dotcms/ai", - "version": "0.1.0", + "version": "1.5.6", "description": "The dotCMS agentic runtime — run model-written or human-written code safely against a dotCMS instance, with auth and policy owned in one place.", "repository": { "type": "git", @@ -16,14 +16,34 @@ }, "exports": { "./package.json": "./package.json", - "./runtime": "./src/runtime.ts", - "./sandbox": "./src/sandbox/index.ts", - "./adapter": "./src/adapter/index.ts", - "./spec": "./src/spec/index.ts" + "./runtime": { + "module": "./runtime.esm.js", + "types": "./runtime.d.ts", + "import": "./runtime.cjs.mjs", + "default": "./runtime.cjs.js" + }, + "./sandbox": { + "module": "./runtime.esm.js", + "types": "./src/sandbox/index.d.ts", + "import": "./runtime.cjs.mjs", + "default": "./runtime.cjs.js" + }, + "./adapter": { + "module": "./runtime.esm.js", + "types": "./src/adapter/index.d.ts", + "import": "./runtime.cjs.mjs", + "default": "./runtime.cjs.js" + }, + "./spec": { + "module": "./spec.esm.js", + "types": "./src/spec/index.d.ts", + "import": "./spec.cjs.js", + "default": "./spec.cjs.js" + } }, "typesVersions": { "*": { - "runtime": ["./src/runtime.d.ts"], + "runtime": ["./runtime.d.ts"], "sandbox": ["./src/sandbox/index.d.ts"], "adapter": ["./src/adapter/index.d.ts"], "spec": ["./src/spec/index.d.ts"] diff --git a/core-web/libs/sdk/ai/scripts/generate-spec.ts b/core-web/libs/sdk/ai/scripts/generate-spec.ts index 4f71c212cf6f..4864f56b0c37 100644 --- a/core-web/libs/sdk/ai/scripts/generate-spec.ts +++ b/core-web/libs/sdk/ai/scripts/generate-spec.ts @@ -1,289 +1,113 @@ /* eslint-disable no-console */ -import SwaggerParser from '@apidevtools/swagger-parser'; +import { parse as parseYaml } from 'yaml'; import fs from 'node:fs'; import path from 'node:path'; import { fileURLToPath } from 'node:url'; -const __dirname = path.dirname(fileURLToPath(import.meta.url)); - -const ALLOWED_PREFIXES = [ - '/api/v1/contenttype', - '/api/v1/page', - '/api/v1/page-scanner/a11y/check', - '/api/v1/page-scanner/geo/check', - '/api/v1/nav', - '/api/v1/workflow', - '/api/v1/categories', - '/api/v2/tags', - '/api/v1/folder', - '/api/v1/site', - '/api/v2/languages', - '/api/v1/roles', - '/api/v1/user', - '/api/v1/containers', - '/api/v1/themes', - '/api/v1/templates', - '/api/v1/content/_search', - '/api/v2/assets' -]; - -const EXCLUDED_PATTERNS = [ - '/api/v1/workflow/tasks/**', - '/api/v1/contenttype/page', - '/api/v1/contenttype/render/id/**', - '/api/v1/categories/_export', - '/api/v1/categories/_sort', - '/api/v1/folder/{id}/file-browser-selected', - '/api/v1/folder/siteId/{siteId}/path/{path}', - '/api/v1/site/{siteId}/setup_progress', - '/api/v1/site/thumbnails', - '/api/v1/site/variable/{siteId}', - '/api/v1/site/switch', - '/api/v1/languages/i18n', - '/api/v1/roles/{roleId}/layouts', - '/api/v1/roles/{roleid}/rolehierarchyanduserroles', - '/api/v1/roles/layouts', - '/api/v1/containers/{containerId}/content/{contentletId}', - '/api/v1/containers/{containerId}/form/{formId}', - '/api/v1/containers/form/{formId}', - '/api/v1/containers/live', - '/api/v1/containers/working', - '/api/v1/templates/_savepublish', - '/api/v1/templates/{templateId}/live', - '/api/v1/templates/{templateId}/working', - '/api/v1/templates/image', - '/api/v1/workflow/actions/separator', - '/api/v1/sites/{siteId}/ruleengine/' -]; +import { transformSpec } from './spec-transform'; -const DEFAULT_SPEC_PATH = '/api/openapi.json'; -const DEFAULT_SPEC_URL = `https://dotcms-corp-headless-prod.dotcms.dev${DEFAULT_SPEC_PATH}`; +const __dirname = path.dirname(fileURLToPath(import.meta.url)); /** - * Matches a path against a pattern. Pattern syntax: - * - `{name}` or `*` — matches a single path segment (anything except `/`) - * - `**` — matches any number of segments - * - everything else is matched literally - * Match is exact (anchored at both ends). + * The committed, auto-generated spec that ships with the backend. `swagger-maven-plugin` + * writes it at compile phase and CI verifies the working copy matches — so it's always + * present and offline, no running dotCMS instance required. This is the only source: + * spec generation reads this local YAML file and nothing else. + * Resolved relative to this script (the `generate-spec` task runs with cwd `libs/sdk/ai`). */ -function matchesPattern(pathKey: string, pattern: string): boolean { - const regex = new RegExp( - '^' + - pattern - .replace(/[.+?^$()|[\]\\]/g, '\\$&') - .replace(/\{[^}]+\}/g, '[^/]+') - .replace(/\*\*/g, '.*') - .replace(/(?<!\.)\*/g, '[^/]+') + - '$' - ); - return regex.test(pathKey); -} +const LOCAL_SPEC_FILE = path.resolve( + __dirname, + '../../../../../dotCMS/src/main/webapp/WEB-INF/openapi/openapi.yaml' +); /** - * Resolve the OpenAPI spec source (a URL or local file path), in priority order: - * 1. an explicit CLI arg (`... generate-spec -- <url-or-path>`) - * 2. `DOTCMS_SPEC_URL` — env vars are inherited by the `generate-spec` task that `build` - * runs via `dependsOn` (CLI args are NOT), so this is what lets - * `DOTCMS_SPEC_URL=… nx build mcp-server` regenerate from a local instance in one command. - * 3. `${DOTCMS_URL}/api/openapi.json` — convenience: reuse the same instance the runtime targets. - * 4. the demo instance (so CI builds with no env set produce the committed spec). + * Resolve the OpenAPI spec file to read. Defaults to the committed local `openapi.yaml`; + * an explicit CLI arg (`... generate-spec -- <path>`) can point at an alternate local YAML. */ -function resolveSpecSource(): string { - if (process.argv[2]) { - return process.argv[2]; - } - if (process.env.DOTCMS_SPEC_URL) { - return process.env.DOTCMS_SPEC_URL; - } - if (process.env.DOTCMS_URL) { - return `${process.env.DOTCMS_URL.replace(/\/+$/, '')}${DEFAULT_SPEC_PATH}`; - } - return DEFAULT_SPEC_URL; +function resolveSpecFile(): string { + return process.argv[2] ? path.resolve(process.argv[2]) : LOCAL_SPEC_FILE; } -async function fetchSpec(source: string): Promise<string> { - const isUrl = source.startsWith('http://') || source.startsWith('https://'); - - if (isUrl) { - console.log(`[generate-spec] Fetching spec from ${source}`); - const response = await fetch(source); - - if (!response.ok) { - throw new Error( - `Failed to fetch OpenAPI spec from ${source}\n` + - `Status: ${response.status} ${response.statusText}\n\n` + - `Make sure the URL is correct and the dotCMS instance is running.` - ); - } - - const tempPath = path.resolve('.openapi-temp.json'); - const body = await response.text(); - - // Validate it's actually JSON - try { - JSON.parse(body); - } catch { - throw new Error( - `Response from ${source} is not valid JSON.\n` + - `Make sure the URL points to a valid OpenAPI spec endpoint.` - ); +/** Parse an OpenAPI YAML document into memory. */ +function parseSpec(body: string, filePath: string): Record<string, unknown> { + try { + const parsed = parseYaml(body); + if (!parsed || typeof parsed !== 'object') { + throw new Error('parsed value is not an object'); } + return parsed as Record<string, unknown>; + } catch (err) { + const detail = err instanceof Error ? err.message : String(err); + throw new Error(`${filePath} is not a valid OpenAPI YAML spec: ${detail}`); + } +} - fs.writeFileSync(tempPath, body, 'utf-8'); - return tempPath; +/** + * Normalize path keys to the full `/api/...` form. + * + * The committed `openapi.yaml` declares `servers: [{ url: '/' }]` and lists routes WITHOUT the + * `/api` prefix (e.g. `/v1/page/...`), while the routes are actually served under `/api` at + * runtime. `ALLOWED_PREFIXES`/`EXCLUDED_PATTERNS` are written against the full `/api/...` form, + * so prepend `/api` to any path key that lacks it. Idempotent: paths already under `/api` are + * left untouched. + */ +function normalizeApiPrefix(spec: Record<string, unknown>): Record<string, unknown> { + const paths = spec.paths as Record<string, unknown> | undefined; + if (!paths) return spec; + + const normalized: Record<string, unknown> = {}; + for (const [key, value] of Object.entries(paths)) { + const newKey = key.startsWith('/api/') || key === '/api' ? key : `/api${key}`; + normalized[newKey] = value; } + spec.paths = normalized; + return spec; +} - // Local file path - const filePath = path.resolve(source); +/** Read and parse the raw OpenAPI document from the local YAML file. */ +function loadSpec(filePath: string): Record<string, unknown> { if (!fs.existsSync(filePath)) { throw new Error(`OpenAPI spec file not found: ${filePath}`); } - console.log(`[generate-spec] Reading spec from ${filePath}`); - return filePath; -} - -async function generateSpec() { - const source = resolveSpecSource(); - const specPath = await fetchSpec(source); - - try { - // Dereference all $ref pointers - const api = (await SwaggerParser.dereference(specPath)) as Record<string, unknown>; - - // Filter paths to allowed prefixes - const allPaths = (api.paths || {}) as Record<string, unknown>; - const filteredPaths: Record<string, unknown> = {}; - - for (const [pathKey, pathValue] of Object.entries(allPaths)) { - const isAllowed = ALLOWED_PREFIXES.some((prefix) => pathKey.startsWith(prefix)); - const isExcluded = EXCLUDED_PATTERNS.some((pattern) => - matchesPattern(pathKey, pattern) - ); - if (isAllowed && !isExcluded) { - // Strip response schemas but keep description and content types - const methods = pathValue as Record<string, unknown>; - const strippedMethods: Record<string, unknown> = {}; - - for (const [method, methodValue] of Object.entries(methods)) { - if (typeof methodValue !== 'object' || methodValue === null) { - strippedMethods[method] = methodValue; - continue; - } - - if ((methodValue as Record<string, unknown>).deprecated === true) { - continue; - } - - const op = { ...(methodValue as Record<string, unknown>) }; + const body = fs.readFileSync(filePath, 'utf-8'); - // Replace Jersey-autogenerated multipart schemas with a simple placeholder. - // Jersey emits noisy internal types (bodyParts, contentDisposition, - // messageBodyWorkers, etc.) that aren't part of the user-facing contract. - const requestBody = op.requestBody as Record<string, unknown> | undefined; - const requestContent = requestBody?.content as - | Record<string, unknown> - | undefined; - if (requestContent && requestContent['multipart/form-data']) { - requestContent['multipart/form-data'] = { - schema: { - type: 'object', - description: 'Multipart form. See endpoint description for fields.', - properties: { - file: { type: 'string', format: 'binary' } - } - } - }; - } + return normalizeApiPrefix(parseSpec(body, filePath)); +} - const responses = op.responses as Record<string, unknown> | undefined; +function generateSpec() { + const filePath = resolveSpecFile(); + const raw = loadSpec(filePath); - if (responses) { - const strippedResponses: Record<string, unknown> = {}; - for (const [status, responseValue] of Object.entries(responses)) { - if (typeof responseValue !== 'object' || responseValue === null) { - strippedResponses[status] = responseValue; - continue; - } - const resp = responseValue as Record<string, unknown>; - const stripped: Record<string, unknown> = {}; - if (resp.description) stripped.description = resp.description; - if (resp.content) { - // Keep standard `content` key but strip schemas — only MIME type keys remain - const strippedContent: Record<string, unknown> = {}; - for (const mimeType of Object.keys( - resp.content as Record<string, unknown> - )) { - strippedContent[mimeType] = {}; - } - stripped.content = strippedContent; - } - strippedResponses[status] = stripped; - } - op.responses = strippedResponses; - } + const { spec, stats } = transformSpec(raw); - strippedMethods[method] = op; - } + // Compact JSON: this file is machine-read only (query results are re-stringified by the tool + // handlers). Pretty-printing would add ~270KB for zero model benefit — use `jq` to inspect. + const json = JSON.stringify(spec); - if (Object.keys(strippedMethods).length > 0) { - filteredPaths[pathKey] = strippedMethods; - } - } - } + const outDir = path.resolve(__dirname, '../src/generated'); + const outPath = path.join(outDir, 'spec.json'); - // Build minimal spec (no components/schemas) - const result: Record<string, unknown> = { - openapi: api.openapi, - info: api.info, - paths: filteredPaths - }; + fs.mkdirSync(outDir, { recursive: true }); + fs.writeFileSync(outPath, json, 'utf-8'); - // Include servers if present - if (api.servers) { - result.servers = api.servers; - } - - // Dereferenced specs can have circular refs (e.g., Category.parent -> Category). - // Use an ancestor-stack approach so shared references (same object appearing in - // multiple endpoints) are duplicated in the output, while only true ancestor - // cycles are replaced with "[Circular]". - const ancestors: object[] = []; - const json = JSON.stringify( - result, - function (_key, value) { - if (typeof value === 'object' && value !== null) { - // Pop the stack back to the current parent object - while (ancestors.length > 0 && ancestors[ancestors.length - 1] !== this) { - ancestors.pop(); - } - if (ancestors.includes(value)) return '[Circular]'; - ancestors.push(value); - } - return value; - }, - 2 + const sizeKB = (Buffer.byteLength(json, 'utf-8') / 1024).toFixed(1); + console.log( + `[generate-spec] Wrote ${stats.pathCount} paths + ${stats.schemaCount} schemas ` + + `(${sizeKB}KB) to ${outPath}` + ); + if (stats.danglingRefs.length > 0) { + console.warn( + `[generate-spec] ${stats.danglingRefs.length} dangling $ref(s) left in place ` + + `(not found in components.schemas): ${stats.danglingRefs.join(', ')}` ); - const outDir = path.resolve(__dirname, '../src/generated'); - const outPath = path.join(outDir, 'spec.json'); - - fs.mkdirSync(outDir, { recursive: true }); - fs.writeFileSync(outPath, json, 'utf-8'); - - const pathCount = Object.keys(filteredPaths).length; - const sizeKB = (Buffer.byteLength(json, 'utf-8') / 1024).toFixed(1); - console.log(`[generate-spec] Wrote ${pathCount} paths (${sizeKB}KB) to ${outPath}`); - } finally { - // Clean up temp file if we fetched from URL - const tempPath = path.resolve('.openapi-temp.json'); - if (fs.existsSync(tempPath)) { - fs.unlinkSync(tempPath); - } } } -generateSpec().catch((err) => { - console.error(`[generate-spec] Failed: ${err.message}`); +try { + generateSpec(); +} catch (err) { + console.error(`[generate-spec] Failed: ${err instanceof Error ? err.message : String(err)}`); process.exit(1); -}); +} diff --git a/core-web/libs/sdk/ai/scripts/spec-transform.spec.ts b/core-web/libs/sdk/ai/scripts/spec-transform.spec.ts new file mode 100644 index 000000000000..bf3a8d8e1078 --- /dev/null +++ b/core-web/libs/sdk/ai/scripts/spec-transform.spec.ts @@ -0,0 +1,239 @@ +import { matchesPattern, transformSpec } from './spec-transform'; + +/** + * A compact raw OpenAPI doc exercising the transform's branches: prefix filtering, exclusion + * patterns, deprecated-op dropping, targeted multipart replacement (Jersey vs curated), the + * transitive `$ref` walk (incl. an `allOf` hop and a cycle), and dangling-ref handling. + */ +function makeRawSpec(): Record<string, unknown> { + return { + openapi: '3.0.1', + info: { title: 'test', version: '1' }, + servers: [{ url: 'https://demo.dotcms.com' }], + paths: { + // allowed; references ContentType (which references Field via allOf) + '/api/v1/contenttype': { + get: { + summary: 'list', + responses: { + '200': { + content: { + 'application/json': { + schema: { $ref: '#/components/schemas/ContentType' } + } + } + } + } + }, + // deprecated op — should be dropped + post: { + deprecated: true, + responses: { + '200': { + content: { + 'application/json': { + schema: { $ref: '#/components/schemas/Dropped' } + } + } + } + } + } + }, + // allowed; Jersey multipart (must be replaced) + a dangling ref in responses + '/api/v2/tags/import': { + post: { + requestBody: { + content: { + 'multipart/form-data': { + schema: { $ref: '#/components/schemas/FormDataMultiPart' } + } + } + }, + responses: { + '200': { + content: { + 'application/json': { + schema: { $ref: '#/components/schemas/Missing' } + } + } + } + } + } + }, + // allowed; curated multipart (must be kept) + '/api/v1/workflow/actions/firemultipart': { + put: { + requestBody: { + content: { + 'multipart/form-data': { + schema: { + $ref: '#/components/schemas/WorkflowActionMultipartSchema' + } + } + } + } + } + }, + // allowed prefix but excluded pattern — should be dropped entirely + '/api/v1/containers/live': { + get: { summary: 'excluded' } + }, + // not in ALLOWED_PREFIXES — should be dropped entirely + '/api/v1/secretstuff': { + get: { + responses: { + '200': { + content: { + 'application/json': { + schema: { $ref: '#/components/schemas/NeverReferenced' } + } + } + } + } + } + } + }, + components: { + schemas: { + ContentType: { type: 'object', allOf: [{ $ref: '#/components/schemas/Field' }] }, + Field: { + type: 'object', + properties: { + // self-referential cycle — the walk must terminate + child: { $ref: '#/components/schemas/Field' } + } + }, + WorkflowActionMultipartSchema: { + type: 'object', + properties: { comments: { type: 'string' } } + }, + FormDataMultiPart: { + type: 'object', + properties: { + bodyParts: { type: 'array' }, + messageBodyWorkers: { type: 'object' } + } + }, + Dropped: { type: 'object' }, + NeverReferenced: { type: 'object' } + } + } + }; +} + +describe('matchesPattern', () => { + it('matches {param} and * against a single segment', () => { + expect(matchesPattern('/api/v1/site/abc', '/api/v1/site/{id}')).toBe(true); + expect(matchesPattern('/api/v1/site/abc/def', '/api/v1/site/{id}')).toBe(false); + }); + + it('matches ** across multiple segments', () => { + expect(matchesPattern('/api/v1/workflow/tasks/a/b', '/api/v1/workflow/tasks/**')).toBe( + true + ); + }); + + it('is anchored at both ends', () => { + expect(matchesPattern('/api/v1/site/switch/extra', '/api/v1/site/switch')).toBe(false); + }); +}); + +describe('transformSpec', () => { + it('keeps allowed paths and drops excluded / non-allowed ones', () => { + const { spec, stats } = transformSpec(makeRawSpec()); + const paths = spec.paths as Record<string, unknown>; + expect(Object.keys(paths).sort()).toEqual([ + '/api/v1/contenttype', + '/api/v1/workflow/actions/firemultipart', + '/api/v2/tags/import' + ]); + expect(stats.pathCount).toBe(3); + }); + + it('keeps templates/{id}/working but still excludes templates/{id}/live', () => { + // Authoring needs the working-layout endpoint; the live variant stays out (edit working, + // publish separately). Guards the C1 inclusion decision against a regression. + const raw = { + openapi: '3.0.1', + info: { title: 't', version: '1' }, + paths: { + '/api/v1/templates/{templateId}/working': { get: { summary: 'working' } }, + '/api/v1/templates/{templateId}/live': { get: { summary: 'live' } } + }, + components: { schemas: {} } + }; + const { spec } = transformSpec(raw); + const paths = spec.paths as Record<string, unknown>; + expect(paths['/api/v1/templates/{templateId}/working']).toBeDefined(); + expect(paths['/api/v1/templates/{templateId}/live']).toBeUndefined(); + }); + + it('drops deprecated operations', () => { + const { spec } = transformSpec(makeRawSpec()); + const contentType = (spec.paths as Record<string, Record<string, unknown>>)[ + '/api/v1/contenttype' + ]; + expect(contentType.get).toBeDefined(); + expect(contentType.post).toBeUndefined(); + }); + + it('replaces Jersey multipart with the placeholder but keeps curated multipart', () => { + const { spec } = transformSpec(makeRawSpec()); + const paths = spec.paths as Record<string, Record<string, Record<string, unknown>>>; + + const jersey = ( + (paths['/api/v2/tags/import'].post.requestBody as Record<string, unknown>) + .content as Record<string, Record<string, unknown>> + )['multipart/form-data'].schema as Record<string, unknown>; + expect(jersey.$ref).toBeUndefined(); + expect((jersey.properties as Record<string, unknown>).file).toBeDefined(); + + const curated = ( + ( + paths['/api/v1/workflow/actions/firemultipart'].put.requestBody as Record< + string, + unknown + > + ).content as Record<string, Record<string, unknown>> + )['multipart/form-data'].schema as Record<string, unknown>; + expect(curated.$ref).toBe('#/components/schemas/WorkflowActionMultipartSchema'); + }); + + it('prunes components to transitively-referenced schemas only, sorted', () => { + const { spec, stats } = transformSpec(makeRawSpec()); + const schemas = (spec.components as Record<string, Record<string, unknown>>).schemas; + const names = Object.keys(schemas); + + // ContentType → Field (allOf hop, self-cycle) and the curated multipart schema. + expect(names).toEqual(['ContentType', 'Field', 'WorkflowActionMultipartSchema']); + // Jersey noise, the deprecated op's schema, and unreferenced schemas are excluded. + expect(schemas.FormDataMultiPart).toBeUndefined(); + expect(schemas.Dropped).toBeUndefined(); + expect(schemas.NeverReferenced).toBeUndefined(); + // sorted for deterministic output + expect(names).toEqual([...names].sort()); + expect(stats.schemaCount).toBe(3); + }); + + it('reports dangling refs and leaves them in place', () => { + const { spec, stats } = transformSpec(makeRawSpec()); + expect(stats.danglingRefs).toEqual(['Missing']); + const paths = spec.paths as Record<string, Record<string, Record<string, unknown>>>; + const respSchema = ( + ( + paths['/api/v2/tags/import'].post.responses as Record< + string, + Record<string, unknown> + > + )['200'].content as Record<string, Record<string, unknown>> + )['application/json'].schema as Record<string, unknown>; + expect(respSchema.$ref).toBe('#/components/schemas/Missing'); + }); + + it('preserves top-level metadata (openapi, info, servers)', () => { + const { spec } = transformSpec(makeRawSpec()); + expect(spec.openapi).toBe('3.0.1'); + expect(spec.info).toEqual({ title: 'test', version: '1' }); + expect(spec.servers).toEqual([{ url: 'https://demo.dotcms.com' }]); + }); +}); diff --git a/core-web/libs/sdk/ai/scripts/spec-transform.ts b/core-web/libs/sdk/ai/scripts/spec-transform.ts new file mode 100644 index 000000000000..d0b02a6e0eb0 --- /dev/null +++ b/core-web/libs/sdk/ai/scripts/spec-transform.ts @@ -0,0 +1,251 @@ +/** + * Pure spec-transform logic (no I/O), so it can be unit-tested without a network fetch. + * + * Given a raw dotCMS OpenAPI document, produce a smaller, model-facing spec: + * - keep only allowed endpoints (ALLOWED_PREFIXES minus EXCLUDED_PATTERNS), drop deprecated ops + * - replace ONLY Jersey-autogenerated multipart schemas with a simple placeholder (curated, + * hand-annotated multipart schemas are kept) + * - keep request/response `$ref`s as-is (NOT dereferenced) — this dedupes shared schemas and + * is naturally acyclic, so the model can read a small ref-labelled endpoint and then resolve + * only the schemas it actually needs (progressive disclosure via the `resolveRef` sandbox helper) + * - emit `components.schemas` pruned to just the schemas transitively referenced by kept paths + */ + +export const ALLOWED_PREFIXES = [ + '/api/v1/contenttype', + // Active v3 field API (move/add, update, list, delete). The v1 `.../fields` + // CRUD endpoints are deprecated and dropped by the transform, so this is the + // only field-mutation surface agents get. `.../fields/move` is what the admin + // UI uses to add a field to a content type (drag-and-drop). + '/api/v3/contenttype', + '/api/v1/page', + '/api/v1/page-scanner/a11y/check', + '/api/v1/page-scanner/geo/check', + '/api/v1/nav', + '/api/v1/workflow', + '/api/v1/categories', + '/api/v2/tags', + '/api/v1/folder', + '/api/v1/site', + '/api/v2/languages', + '/api/v1/roles', + '/api/v1/user', + '/api/v1/containers', + '/api/v1/themes', + '/api/v1/templates', + '/api/v1/content/_search', + '/api/v2/assets' +]; + +export const EXCLUDED_PATTERNS = [ + '/api/v1/workflow/tasks/**', + '/api/v1/contenttype/page', + '/api/v1/contenttype/render/id/**', + // Deprecated v1 field CRUD — superseded by the active v3 field API + // (/api/v3/contenttype/.../fields[/move|/{id}|/allfields]). Excluded explicitly so + // agents never see them even if the `deprecated` flag is ever removed upstream. The + // v1 field-VARIABLE subpaths (.../fields/**/variables) are NOT matched here and stay. + '/api/v1/contenttype/{typeId}/fields', + '/api/v1/contenttype/{typeId}/fields/id/{fieldId}', + '/api/v1/contenttype/{typeId}/fields/var/{fieldVar}', + '/api/v1/categories/_export', + '/api/v1/categories/_sort', + '/api/v1/folder/{id}/file-browser-selected', + '/api/v1/folder/siteId/{siteId}/path/{path}', + '/api/v1/site/{siteId}/setup_progress', + '/api/v1/site/thumbnails', + '/api/v1/site/variable/{siteId}', + '/api/v1/site/switch', + '/api/v1/languages/i18n', + '/api/v1/roles/{roleId}/layouts', + '/api/v1/roles/{roleid}/rolehierarchyanduserroles', + '/api/v1/roles/layouts', + '/api/v1/containers/{containerId}/content/{contentletId}', + '/api/v1/containers/{containerId}/form/{formId}', + '/api/v1/containers/form/{formId}', + '/api/v1/containers/live', + '/api/v1/containers/working', + '/api/v1/templates/_savepublish', + '/api/v1/templates/{templateId}/live', + '/api/v1/templates/image', + '/api/v1/workflow/actions/separator', + '/api/v1/sites/{siteId}/ruleengine/' +]; + +const SCHEMA_REF_PREFIX = '#/components/schemas/'; + +/** + * Matches a path against a pattern. Pattern syntax: + * - `{name}` or `*` — matches a single path segment (anything except `/`) + * - `**` — matches any number of segments + * - everything else is matched literally + * Match is exact (anchored at both ends). + */ +export function matchesPattern(pathKey: string, pattern: string): boolean { + const regex = new RegExp( + '^' + + pattern + .replace(/[.+?^$()|[\]\\]/g, '\\$&') + .replace(/\{[^}]+\}/g, '[^/]+') + .replace(/\*\*/g, '.*') + .replace(/(?<!\.)\*/g, '[^/]+') + + '$' + ); + return regex.test(pathKey); +} + +/** A Jersey-autogenerated multipart body, either as the `FormDataMultiPart` $ref or its inline shape. */ +function isJerseyMultipart(schema: Record<string, unknown> | undefined): boolean { + if (!schema) return true; // no schema at all — nothing worth keeping + const ref = schema.$ref; + if (typeof ref === 'string' && ref.endsWith('/FormDataMultiPart')) return true; + const props = schema.properties as Record<string, unknown> | undefined; + return !!props && ('bodyParts' in props || 'messageBodyWorkers' in props); +} + +const MULTIPART_PLACEHOLDER = { + schema: { + type: 'object', + description: 'Multipart form. See endpoint description for fields.', + properties: { + file: { type: 'string', format: 'binary' } + } + } +}; + +export interface TransformStats { + pathCount: number; + schemaCount: number; + /** Schema names referenced by kept paths but missing from `components.schemas`. */ + danglingRefs: string[]; +} + +export interface TransformResult { + spec: Record<string, unknown>; + stats: TransformStats; +} + +/** + * Collect every schema name transitively reachable from `node` via `$ref`. Follows + * schema→schema references (an `allOf`/`items`/`properties` ref is just a nested object, + * so the generic walk covers it). The `needed` guard makes ref cycles terminate. + */ +function collectSchemaRefs( + node: unknown, + allSchemas: Record<string, unknown>, + needed: Set<string>, + dangling: Set<string> +): void { + if (Array.isArray(node)) { + for (const item of node) collectSchemaRefs(item, allSchemas, needed, dangling); + return; + } + if (!node || typeof node !== 'object') return; + + const obj = node as Record<string, unknown>; + const ref = obj.$ref; + if (typeof ref === 'string') { + if (!ref.startsWith(SCHEMA_REF_PREFIX)) { + // Future-proofing: today the dotCMS spec only uses #/components/schemas refs. + console.warn(`[generate-spec] Skipping non-schema $ref: ${ref}`); + return; + } + const name = ref.slice(SCHEMA_REF_PREFIX.length); + if (!needed.has(name)) { + if (allSchemas[name] === undefined) { + dangling.add(name); + return; + } + needed.add(name); + collectSchemaRefs(allSchemas[name], allSchemas, needed, dangling); + } + return; + } + + for (const value of Object.values(obj)) { + collectSchemaRefs(value, allSchemas, needed, dangling); + } +} + +/** Transform a raw OpenAPI document into the pruned, ref-preserving model-facing spec. */ +export function transformSpec(raw: Record<string, unknown>): TransformResult { + const allPaths = (raw.paths || {}) as Record<string, unknown>; + const allSchemas = ((raw.components as Record<string, unknown> | undefined)?.schemas || + {}) as Record<string, unknown>; + + const filteredPaths: Record<string, unknown> = {}; + + for (const [pathKey, pathValue] of Object.entries(allPaths)) { + const isAllowed = ALLOWED_PREFIXES.some((prefix) => pathKey.startsWith(prefix)); + const isExcluded = EXCLUDED_PATTERNS.some((pattern) => matchesPattern(pathKey, pattern)); + if (!isAllowed || isExcluded) continue; + + const methods = pathValue as Record<string, unknown>; + const keptMethods: Record<string, unknown> = {}; + + for (const [method, methodValue] of Object.entries(methods)) { + if (typeof methodValue !== 'object' || methodValue === null) { + keptMethods[method] = methodValue; + continue; + } + if ((methodValue as Record<string, unknown>).deprecated === true) { + continue; + } + + const op = { ...(methodValue as Record<string, unknown>) }; + + // Replace ONLY Jersey-autogenerated multipart schemas (noisy internal types like + // bodyParts/messageBodyWorkers) with a placeholder. Curated, hand-annotated multipart + // schemas (WorkflowActionMultipartSchema, CategoryImportFormSchema, inline asset forms) + // are kept — they carry the field descriptions this change exists to preserve. Runs + // BEFORE the ref walk so Jersey subtrees never enter components.schemas. + const requestBody = op.requestBody as Record<string, unknown> | undefined; + const requestContent = requestBody?.content as Record<string, unknown> | undefined; + const multipart = requestContent?.['multipart/form-data'] as + | Record<string, unknown> + | undefined; + if ( + requestContent && + multipart && + isJerseyMultipart(multipart.schema as Record<string, unknown> | undefined) + ) { + requestContent['multipart/form-data'] = MULTIPART_PLACEHOLDER; + } + + keptMethods[method] = op; + } + + if (Object.keys(keptMethods).length > 0) { + filteredPaths[pathKey] = keptMethods; + } + } + + // Walk the kept paths to find every transitively-referenced schema, then emit only those. + const needed = new Set<string>(); + const danglingSet = new Set<string>(); + collectSchemaRefs(filteredPaths, allSchemas, needed, danglingSet); + + const schemas: Record<string, unknown> = {}; + for (const name of [...needed].sort()) { + schemas[name] = allSchemas[name]; + } + + const spec: Record<string, unknown> = { + openapi: raw.openapi, + info: raw.info, + paths: filteredPaths, + components: { schemas } + }; + if (raw.servers) { + spec.servers = raw.servers; + } + + return { + spec, + stats: { + pathCount: Object.keys(filteredPaths).length, + schemaCount: Object.keys(schemas).length, + danglingRefs: [...danglingSet].sort() + } + }; +} diff --git a/core-web/libs/sdk/ai/src/adapter/context.ts b/core-web/libs/sdk/ai/src/adapter/context.ts index 89a9ba3629e4..3795a172621a 100644 --- a/core-web/libs/sdk/ai/src/adapter/context.ts +++ b/core-web/libs/sdk/ai/src/adapter/context.ts @@ -14,6 +14,7 @@ export interface SiteSummary { hostname: string; isDefault: boolean; archived: boolean; + live: boolean; } export interface LanguageSummary { @@ -95,21 +96,35 @@ async function loadContentTypes(request: RequestFn): Promise<ContentTypeSummary[ } async function loadSites(request: RequestFn): Promise<SiteSummary[]> { - const raw = await request({ - method: 'GET', - path: '/api/v1/site', - query: { per_page: 200 } - }); - const list = asArray(unwrapEntity(raw)); - return list.map((item) => { + const pageSize = 200; + const all: unknown[] = []; + + // `archive=true` means "include archived" and also asks the backend for stopped sites; + // without it the endpoint returns only the active selector list. Keep paging until the + // server returns a short page so the injected global is a catalog, not the first 200 sites. + for (let page = 0; page < 100; page += 1) { + const raw = await request({ + method: 'GET', + path: '/api/v1/site', + query: { per_page: pageSize, page, archive: true } + }); + const batch = asArray(unwrapEntity(raw)); + all.push(...batch); + if (batch.length < pageSize) break; + } + + const sites = all.map((item) => { const s = item as Record<string, unknown>; return { identifier: asString(s.identifier), - hostname: asString(s.hostname ?? s.hostName), + hostname: asString(s.hostname ?? s.hostName ?? s.siteName), isDefault: asBool(s.default ?? s.isDefault), - archived: asBool(s.archived) + archived: asBool(s.archived ?? s.isArchived), + live: asBool(s.live ?? s.isLive) }; }); + + return [...new Map(sites.map((site) => [site.identifier, site])).values()]; } async function loadLanguages(request: RequestFn): Promise<LanguageSummary[]> { @@ -165,19 +180,19 @@ export async function loadDotCMSContext( const request = getRequestFn(apiAdapter); const [contentTypes, sites, languages, currentUser] = await Promise.all([ - loadContentTypes(request).catch((err) => { + loadContentTypes(request).catch((err): ContentTypeSummary[] => { onError?.('contentTypes', err); - return [] as ContentTypeSummary[]; + return []; }), - loadSites(request).catch((err) => { + loadSites(request).catch((err): SiteSummary[] => { onError?.('sites', err); - return [] as SiteSummary[]; + return []; }), - loadLanguages(request).catch((err) => { + loadLanguages(request).catch((err): LanguageSummary[] => { onError?.('languages', err); - return [] as LanguageSummary[]; + return []; }), - loadCurrentUser(request).catch((err) => { + loadCurrentUser(request).catch((err): CurrentUserSummary | null => { onError?.('currentUser', err); return null; }) diff --git a/core-web/libs/sdk/ai/src/runtime.spec.ts b/core-web/libs/sdk/ai/src/runtime.spec.ts index 865287355ac3..cec2aef5175a 100644 --- a/core-web/libs/sdk/ai/src/runtime.spec.ts +++ b/core-web/libs/sdk/ai/src/runtime.spec.ts @@ -148,3 +148,78 @@ describe('createRuntime.run — context-load timeout', () => { expect(result.value).toBe(1); }, 5000); }); + +describe('createRuntime context freshness', () => { + const fetchMock = jest.fn(); + + beforeEach(() => { + fetchMock.mockReset(); + global.fetch = fetchMock as unknown as typeof fetch; + }); + + it('loads all site states and reuses the snapshot within one runtime', async () => { + let siteLoads = 0; + fetchMock.mockImplementation(async (url: string) => { + const parsed = new URL(url); + if (parsed.pathname === '/api/v1/site') { + siteLoads += 1; + expect(parsed.searchParams.get('archive')).toBe('true'); + return jsonResponse({ + entity: [ + { + identifier: `site-${siteLoads}`, + siteName: 'demo.dotcms.com', + isDefault: true, + isArchived: siteLoads === 1, + isLive: siteLoads > 1 + } + ] + }); + } + return jsonResponse({ entity: [] }); + }); + + const dotcms = createRuntime({ url: 'https://demo.dotcms.com', token: 't' }); + const first = await dotcms.loadContext(); + const cached = await dotcms.loadContext(); + expect(first.sites[0]).toMatchObject({ archived: true, live: false }); + expect(cached.sites[0].identifier).toBe('site-1'); + expect(siteLoads).toBe(1); + }); + + it('paginates and de-duplicates the complete site catalog', async () => { + fetchMock.mockImplementation(async (url: string) => { + const parsed = new URL(url); + if (parsed.pathname !== '/api/v1/site') { + return jsonResponse({ entity: [] }); + } + const page = Number(parsed.searchParams.get('page')); + if (page === 0) { + return jsonResponse({ + entity: Array.from({ length: 200 }, (_, index) => ({ + identifier: `site-${index}`, + siteName: `site-${index}.example.com`, + isLive: true + })) + }); + } + return jsonResponse({ + entity: [ + { identifier: 'site-199', siteName: 'duplicate.example.com', isLive: false }, + { identifier: 'site-200', siteName: 'last.example.com', isLive: false } + ] + }); + }); + + const context = await createRuntime({ + url: 'https://demo.dotcms.com', + token: 't' + }).loadContext(); + + expect(context.sites).toHaveLength(201); + expect(context.sites.find((site) => site.identifier === 'site-199')?.hostname).toBe( + 'duplicate.example.com' + ); + expect(context.sites.find((site) => site.identifier === 'site-200')?.live).toBe(false); + }); +}); diff --git a/core-web/libs/sdk/ai/src/runtime.ts b/core-web/libs/sdk/ai/src/runtime.ts index 0c704d33811a..3af670b1a82f 100644 --- a/core-web/libs/sdk/ai/src/runtime.ts +++ b/core-web/libs/sdk/ai/src/runtime.ts @@ -151,7 +151,7 @@ export function createRuntime(config: DotCMSRuntimeConfig): DotCMSRuntime { currentUser: context.currentUser }; if (config.includeSpec) { - // Dynamic import so the ~550KB generated spec is only pulled in by consumers that + // Dynamic import so the ~400KB generated spec is only pulled in by consumers that // actually opt into it — a bare `@dotcms/ai/runtime` import never drags in the spec. const { getSpec } = await import('./spec/spec'); variables.spec = getSpec(); @@ -219,6 +219,10 @@ export type { DotCMSErrorCode, SerializedDotCMSError } from './sandbox/errors'; // Result shape returned by `run()`, and the binary-response helpers callers need to decode it. export type { SandboxResult, SandboxResultError } from './sandbox/types'; + +// The context-cap helper that turns a `run()` result into the capped string a tool returns. +export { formatSandboxResult } from './sandbox/format-result'; +export type { FormatSandboxResultOptions } from './sandbox/format-result'; export { isBinaryResponseEnvelope } from './adapter/request-core'; export type { BinaryResponseEnvelope, diff --git a/core-web/libs/sdk/ai/src/sandbox/format-result.spec.ts b/core-web/libs/sdk/ai/src/sandbox/format-result.spec.ts new file mode 100644 index 000000000000..28ace7b29d17 --- /dev/null +++ b/core-web/libs/sdk/ai/src/sandbox/format-result.spec.ts @@ -0,0 +1,102 @@ +import { formatSandboxResult } from './format-result'; + +import type { SandboxResult } from './types'; + +function ok(value: unknown, logs: string[] = []): SandboxResult { + return { success: true, value, logs, executionTime: 1 }; +} + +function err(error: { name: string; message: string }, logs: string[] = []): SandboxResult { + return { success: false, error, logs, executionTime: 1 }; +} + +describe('formatSandboxResult', () => { + it('pretty-prints a non-string value', () => { + expect(formatSandboxResult(ok({ a: 1 }))).toBe('{\n "a": 1\n}'); + }); + + it('passes a string value through untouched', () => { + expect(formatSandboxResult(ok('hello'))).toBe('hello'); + }); + + describe('values structured clone allows but JSON does not', () => { + // postMessage uses structured clone, which handles cycles and BigInt; JSON handles + // neither. So the worker reports success, the value transfers intact, and formatting + // it used to throw — "the tool breaks only when my code succeeds". + it('renders a circular structure instead of throwing', () => { + // Building a tree from a flat list with parent back-pointers is the ordinary way + // to do it, so this is not an exotic input. + const parent: Record<string, unknown> = { id: 'root' }; + const child: Record<string, unknown> = { id: 'leaf', parent }; + parent['children'] = [child]; + + const out = formatSandboxResult(ok(parent)); + + expect(out).toContain('[Circular]'); + expect(out).toContain('root'); + }); + + it('renders BigInt instead of throwing', () => { + const out = formatSandboxResult(ok({ total: BigInt(9007199254740993n) })); + expect(out).toContain('9007199254740993n'); + }); + + it('keeps the logs when the value cannot be serialized at all', () => { + // The logs used to be attached AFTER the stringify, so a serialization failure + // discarded every console.log the model had written to debug its code — at the + // exact moment they became most useful. + const hostile = { + get boom() { + throw new Error('nope'); + } + }; + + const out = formatSandboxResult(ok(hostile, ['step 1', 'step 2'])); + + expect(out).toContain('could not be serialized'); + expect(out).toContain('step 1'); + expect(out).toContain('step 2'); + }); + }); + + it('appends logs on success', () => { + expect(formatSandboxResult(ok('v', ['line1', 'line2']))).toBe( + 'v\n\n--- Logs ---\nline1\nline2' + ); + }); + + it('formats an error branch with logs', () => { + expect(formatSandboxResult(err({ name: 'HttpError', message: 'boom' }, ['ctx']))).toBe( + 'Error: HttpError: boom\nLogs:\nctx' + ); + }); + + it('leaves output under the cap untouched', () => { + const out = formatSandboxResult(ok('x'.repeat(100)), { maxChars: 200 }); + expect(out).toBe('x'.repeat(100)); + expect(out).not.toContain('truncated'); + }); + + it('truncates over-cap success output and appends a notice', () => { + const out = formatSandboxResult(ok('x'.repeat(500)), { maxChars: 100 }); + expect(out.startsWith('x'.repeat(100))).toBe(true); + expect(out).toContain('[output truncated at 100 of'); + expect(out).toContain('refine the query'); + }); + + it('appends a custom truncation hint', () => { + const out = formatSandboxResult(ok('x'.repeat(500)), { + maxChars: 100, + truncationHint: 'Use resolveRef().' + }); + expect(out).toContain('Use resolveRef().'); + }); + + it('also caps a huge error branch', () => { + const out = formatSandboxResult(err({ name: 'HttpError', message: 'x'.repeat(500) }), { + maxChars: 100 + }); + expect(out.length).toBeLessThan(300); + expect(out).toContain('[output truncated at 100 of'); + }); +}); diff --git a/core-web/libs/sdk/ai/src/sandbox/format-result.ts b/core-web/libs/sdk/ai/src/sandbox/format-result.ts new file mode 100644 index 000000000000..2c837f8536db --- /dev/null +++ b/core-web/libs/sdk/ai/src/sandbox/format-result.ts @@ -0,0 +1,108 @@ +import type { SandboxResult } from './types'; + +/** + * Default hard cap on the string handed back to the model (~6k tokens). A depth-1/2 + * `resolveRef` of even the largest schemas fits comfortably; a whole-`spec` dump does not. + */ +const DEFAULT_MAX_CHARS = 25_000; + +export interface FormatSandboxResultOptions { + /** Hard cap on the returned string (chars). Default {@link DEFAULT_MAX_CHARS}. */ + maxChars?: number; + /** Tool-specific guidance appended inside the truncation notice. */ + truncationHint?: string; +} + +/** + * Render a {@link SandboxResult} into the single string a tool hands back to the model, and + * hard-cap its length so one query can't flood the context window. + * + * There is NO truncation anywhere else on the result path — whatever the model's code returns is + * stringified whole. This is the one place that bounds it. The cap is applied to the final + * combined string in BOTH the success and error branches (an `HttpError` body embedded in an + * error message can itself be huge). On truncation, a clear notice explains the cut and tells the + * model how to narrow the query rather than silently dropping data. + */ +export function formatSandboxResult( + result: SandboxResult, + options?: FormatSandboxResultOptions +): string { + const maxChars = options?.maxChars ?? DEFAULT_MAX_CHARS; + + let out: string; + if (!result.success) { + const errorMsg = result.error + ? `${result.error.name}: ${result.error.message}` + : 'Unknown error'; + const logs = result.logs.length > 0 ? `\nLogs:\n${result.logs.join('\n')}` : ''; + out = `Error: ${errorMsg}${logs}`; + } else { + // Logs are built BEFORE the value is stringified. Serialization can fail (see + // `stringifyValue`), and when it did, the throw escaped this function with the logs + // still unattached — so every `console.log` the model had written to debug its code + // was discarded at the exact moment it became most useful. + const logs = result.logs.length > 0 ? `\n\n--- Logs ---\n${result.logs.join('\n')}` : ''; + const value = + typeof result.value === 'string' ? result.value : stringifyValue(result.value); + out = `${value}${logs}`; + } + + if (out.length <= maxChars) return out; + + const hint = options?.truncationHint ? ` ${options.truncationHint}` : ''; + return ( + out.slice(0, maxChars) + + `\n\n[output truncated at ${maxChars} of ${out.length} chars — refine the query: ` + + `select specific paths/fields, use pick()/first(), or resolve one schema at a time.${hint}]` + ); +} + +/** + * `JSON.stringify` for a value that arrived over `postMessage`. + * + * The two serializers do NOT agree on what is representable. `postMessage` uses structured + * clone, which handles circular references and `BigInt`; JSON handles neither. So a worker + * could report `success: true`, the value could transfer intact, and stringifying it here + * would then throw `TypeError: Converting circular structure to JSON` — out of a function + * whose job is to REPORT the result. It presented as "the tool breaks only when my code + * succeeds", which is about the worst shape a failure can take. + * + * Circular references are not exotic here: building a tree from a flat folder or page list + * with parent back-pointers is the ordinary way to do it. They are replaced with a marker + * rather than dropped, so the model can see the shape it produced and why it could not be + * returned whole. + */ +function stringifyValue(value: unknown): string { + try { + return JSON.stringify(value, circularSafeReplacer(), 2) ?? String(value); + } catch (error) { + const reason = error instanceof Error ? error.message : String(error); + + return ( + `[value could not be serialized: ${reason}]\n` + + `The worker returned this value successfully, but it cannot be represented as JSON. ` + + `Return a plain-data projection instead — e.g. pick(items, ['id','title']) — rather ` + + `than the object graph itself.` + ); + } +} + +/** A replacer that survives cycles and BigInt, the two structured-clone/JSON mismatches. */ +function circularSafeReplacer(): (key: string, value: unknown) => unknown { + const seen = new WeakSet<object>(); + + return function replacer(this: unknown, _key: string, value: unknown) { + if (typeof value === 'bigint') { + // BigInt has no JSON representation at all — stringify throws on it outright. + return `${value.toString()}n`; + } + if (typeof value === 'object' && value !== null) { + if (seen.has(value)) { + return '[Circular]'; + } + seen.add(value); + } + + return value; + }; +} diff --git a/core-web/libs/sdk/ai/src/sandbox/index.ts b/core-web/libs/sdk/ai/src/sandbox/index.ts index 7ee60509e66e..69f9a58da98c 100644 --- a/core-web/libs/sdk/ai/src/sandbox/index.ts +++ b/core-web/libs/sdk/ai/src/sandbox/index.ts @@ -16,6 +16,9 @@ export type { ExecutorOptions } from './executor'; export { createWorkerSandbox } from './factory'; export type { ISandbox, SandboxFactory } from './interface'; +export { formatSandboxResult } from './format-result'; +export type { FormatSandboxResultOptions } from './format-result'; + export { defineAdapter, isDefinedAdapter, describeAdapterForLLM } from './define-adapter'; export type { AdapterContext, @@ -75,7 +78,7 @@ export interface Sandbox { } /** - * Create a sandbox over a set of adapters — the generic entry point shown in §6.2. + * Create a sandbox over a set of adapters — the generic entry point. * * `defineAdapter` results are bound to the injected host `request` (and a per-run abort * signal) via `withContext`; plain hand-built adapters are passed through untouched. Each diff --git a/core-web/libs/sdk/ai/src/sandbox/sandbox.spec.ts b/core-web/libs/sdk/ai/src/sandbox/sandbox.spec.ts index 0b21f64dfcfc..1fbe75f4f230 100644 --- a/core-web/libs/sdk/ai/src/sandbox/sandbox.spec.ts +++ b/core-web/libs/sdk/ai/src/sandbox/sandbox.spec.ts @@ -200,3 +200,111 @@ describe('sandbox adapter routing', () => { expect(aborted).toBe(true); }, 10000); }); + +describe('resolveRef sandbox helper', () => { + // A tiny fake spec injected as the `spec` global (as the search runtime does via includeSpec). + const fakeSpec = { + components: { + schemas: { + Page: { + type: 'object', + properties: { + title: { type: 'string' }, + template: { $ref: '#/components/schemas/Template' } + } + }, + Template: { + type: 'object', + properties: { theme: { $ref: '#/components/schemas/Theme' } } + }, + Theme: { type: 'object', properties: { name: { type: 'string' } } }, + // self-referential — bounded depth must terminate + Node: { + type: 'object', + properties: { child: { $ref: '#/components/schemas/Node' } } + }, + // Mutually recursive, as the committed spec genuinely is + // (BodyPart -> MultiPart -> BodyPart). + BodyPart: { + type: 'object', + properties: { part: { $ref: '#/components/schemas/MultiPart' } } + }, + MultiPart: { + type: 'object', + properties: { body: { $ref: '#/components/schemas/BodyPart' } } + } + } + } + }; + + function runWithSpec(code: string) { + const executor = new Executor({ config: { adapters: [], sandbox: { timeout: 5000 } } }); + return executor.execute(code, { variables: { spec: fakeSpec } }); + } + + it('expands nested $refs up to the given depth', async () => { + const result = await runWithSpec(`return resolveRef('Page', 2);`); + expect(result.success).toBe(true); + const value = result.value as { + properties: { + template: { properties: { theme: { $ref?: string; properties?: unknown } } }; + }; + }; + // depth 2: Page → Template → Theme all expanded (Theme has no further refs) + expect(value.properties.template.properties.theme.properties).toBeDefined(); + expect(value.properties.template.properties.theme.$ref).toBeUndefined(); + }); + + it('leaves $ref strings in place beyond the depth bound', async () => { + const result = await runWithSpec(`return resolveRef('Page', 1);`); + expect(result.success).toBe(true); + const value = result.value as { + properties: { template: { properties: { theme: { $ref?: string } } } }; + }; + // depth 1: Page → Template expanded, but Template's theme ref is left unresolved + expect(value.properties.template.properties.theme.$ref).toBe('#/components/schemas/Theme'); + }); + + it('terminates on a self-referential schema', async () => { + const result = await runWithSpec(`return resolveRef('Node', 5);`); + expect(result.success).toBe(true); + // Should complete without infinite recursion; the deepest child stays a $ref. + expect(result.value).toBeDefined(); + }); + + it('terminates on a MUTUALLY recursive pair', async () => { + // depth alone cannot save this one: each hop copies the whole target subtree, so a + // large requested depth over a cycle grows multiplicatively until the worker is + // killed for exhausting its heap. + const result = await runWithSpec(`return resolveRef('BodyPart', 5);`); + expect(result.success).toBe(true); + expect(result.value).toBeDefined(); + }); + + it('clamps a large caller-chosen depth instead of exhausting the worker', async () => { + // Asking for a bigger number is exactly what a model does when depth 2 looks + // truncated, and `depth` is entirely model-chosen. + const result = await runWithSpec(`return resolveRef('Node', 500);`); + expect(result.success).toBe(true); + expect(JSON.stringify(result.value)).toContain('$ref'); + }); + + it('survives a non-numeric depth', async () => { + const result = await runWithSpec(`return resolveRef('Page', 'deep');`); + expect(result.success).toBe(true); + expect(result.value).toBeDefined(); + }); + + it('throws a friendly error for an unknown schema name', async () => { + const result = await runWithSpec(`return resolveRef('Nope');`); + expect(result.success).toBe(false); + expect(result.error?.message).toMatch(/Unknown schema "Nope"/); + }); + + it('throws a friendly error when the spec global is absent', async () => { + const executor = new Executor({ config: { adapters: [], sandbox: { timeout: 5000 } } }); + const result = await executor.execute(`return resolveRef('Page');`); + expect(result.success).toBe(false); + expect(result.error?.message).toMatch(/only available in the search sandbox/i); + }); +}); diff --git a/core-web/libs/sdk/ai/src/sandbox/worker-harness.ts b/core-web/libs/sdk/ai/src/sandbox/worker-harness.ts index 5f77560b1bf5..f1b7421ca003 100644 --- a/core-web/libs/sdk/ai/src/sandbox/worker-harness.ts +++ b/core-web/libs/sdk/ai/src/sandbox/worker-harness.ts @@ -88,6 +88,66 @@ const HARNESS_BODY = ` return arr.slice(0, n); }; + // Resolve OpenAPI $refs against spec.components.schemas, expanding nested refs up to + // 'depth' levels. Beyond depth, { $ref } objects are left verbatim so the model can + // resolve the next hop in a follow-up query (progressive disclosure). Only available in + // the search sandbox, where the 'spec' global is injected. + // + // THREE bounds, not one. 'depth' alone was never sufficient: it is chosen by the model, + // and asking for a bigger number is exactly what a model does when depth 2 looks + // truncated. Expansion copies the whole target subtree per $ref hop with no memo, so + // cost grows multiplicatively with fan-out — and the committed spec is NOT acyclic + // (self-refs on FolderView/MultiPart/Permissionable, the BodyPart -> MultiPart -> + // BodyPart cycle, and fan-out around 10 on PageView). resolveRef('PageView', 12) would + // exhaust the worker's heap and get it killed, which the caller sees as an opaque + // sandbox death with the accumulated logs lost. The formatSandboxResult cap does not + // help: that applies to the result string, long after the graph is built in memory. + globalThis.resolveRef = (schemaOrName, depth = 2) => { + const MAX_DEPTH = 5; + const MAX_NODES = 50000; + const spec = globalThis.spec; + const schemas = spec && spec.components && spec.components.schemas; + if (!schemas) { + throw new Error('resolveRef() needs the spec global (spec.components.schemas). It is only available in the search sandbox.'); + } + // Clamped rather than rejected, and the clamp is self-evident in the output: an + // unexpanded { $ref } is the same signal the model already follows for a deeper hop. + const requested = Number(depth); + const maxDepth = Math.max(0, Math.min(Number.isFinite(requested) ? requested : 2, MAX_DEPTH)); + let budget = MAX_NODES; + const nameOf = (ref) => String(ref).split('/').pop(); + const expand = (node, d, path) => { + if (--budget < 0) { + throw new Error('resolveRef() expanded more than ' + MAX_NODES + ' nodes and was stopped before exhausting worker memory. Resolve a smaller schema, or use a lower depth and follow the remaining $refs in a second call.'); + } + if (Array.isArray(node)) return node.map((item) => expand(item, d, path)); + if (!node || typeof node !== 'object') return node; + if (typeof node.$ref === 'string') { + if (d <= 0) return node; + const name = nameOf(node.$ref); + // Already on this branch's ancestry: expanding again would recurse forever on a + // self-referential or mutually-referential schema. Leaving the $ref verbatim is + // the same progressive-disclosure contract as running out of depth. + if (path.indexOf(name) !== -1) return node; + const target = schemas[name]; + if (!target) return node; + return expand(target, d - 1, path.concat(name)); + } + const out = {}; + for (const key of Object.keys(node)) out[key] = expand(node[key], d, path); + return out; + }; + if (typeof schemaOrName === 'string') { + const name = nameOf(schemaOrName); + const target = schemas[name]; + if (!target) { + throw new Error('Unknown schema "' + schemaOrName + '". List names with Object.keys(spec.components.schemas).'); + } + return expand(target, maxDepth, [name]); + } + return expand(schemaOrName, maxDepth, []); + }; + __onMessage(async (msg) => { const { type, data } = msg; diff --git a/core-web/libs/sdk/ai/tsconfig.spec.json b/core-web/libs/sdk/ai/tsconfig.spec.json index cd5ae549b3bf..f6107b81231f 100644 --- a/core-web/libs/sdk/ai/tsconfig.spec.json +++ b/core-web/libs/sdk/ai/tsconfig.spec.json @@ -6,5 +6,12 @@ "resolveJsonModule": true, "types": ["jest", "node"] }, - "include": ["jest.config.ts", "src/**/*.test.ts", "src/**/*.spec.ts", "src/**/*.d.ts"] + "include": [ + "jest.config.ts", + "src/**/*.test.ts", + "src/**/*.spec.ts", + "src/**/*.d.ts", + "scripts/spec-transform.ts", + "scripts/spec-transform.spec.ts" + ] } diff --git a/core-web/package.json b/core-web/package.json index 74443a495664..949277e10132 100644 --- a/core-web/package.json +++ b/core-web/package.json @@ -64,6 +64,7 @@ "@date-fns/tz": "1.4.1", "@emoji-mart/data": "1.2.1", "@floating-ui/dom": "1.6.13", + "@hono/node-server": "^2.0.5", "@jitsu/sdk-js": "3.1.5", "@material/base": "14.0.0", "@material/dom": "14.0.0", @@ -85,6 +86,7 @@ "@ngrx/signals": "21.1.1", "@nx/angular": "23.1.1", "@nx/playwright": "23.1.1", + "@openrouter/ai-sdk-provider": "^2.9.1", "@primeuix/themes": "2.0.3", "@primeuix/utils": "0.6.4", "@tailwindcss/postcss": "4.2.1", @@ -126,6 +128,7 @@ "@tiptap/suggestion": "3.22.2", "@tiptap/y-tiptap": "3.0.3", "ai": "^6.0.168", + "ai-sdk-provider-opencode-sdk": "^3.0.6", "axios": "1.15.0", "cfonts": "3.3.1", "chalk": "5.6.2", @@ -146,6 +149,7 @@ "font-awesome": "4.7.0", "fs-extra": "11.3.2", "gridstack": "8.4.0", + "hono": "^4.12.26", "htmldiff-js": "1.0.5", "inquirer": "13.0.1", "jstat": "1.9.6", @@ -199,7 +203,6 @@ "@angular/cli": "22.1.2", "@angular/compiler-cli": "22.1.0", "@angular/language-service": "22.1.0", - "@apidevtools/swagger-parser": "10.1.1", "@babel/core": "7.29.0", "@babel/plugin-proposal-class-properties": "7.18.6", "@babel/plugin-proposal-private-methods": "7.18.6", @@ -261,7 +264,10 @@ "angular-eslint": "22.1.0", "babel-jest": "30.2.0", "babel-loader": "10.0.0", + "css-select": "^7.0.0", "daisyui": "5.5.19", + "domhandler": "^6.0.1", + "domutils": "^4.0.2", "dotenv": "16.5.0", "esbuild": "0.19.2", "eslint": "9.39.4", @@ -279,13 +285,16 @@ "flatpickr": "4.5.7", "gh-pages": "6.1.1", "happy-dom": "15.7.4", + "htmlparser2": "^12.0.0", "http-proxy-middleware": "3.0.5", "husky": "9.1.7", "jest": "30.2.0", "jest-environment-jsdom": "29.7.0", + "jest-environment-node": "^30.0.2", "jest-html-reporters": "3.1.5", "jest-junit": "16.0.0", "jest-preset-angular": "17.0.0", + "jest-util": "^30.0.2", "jiti": "2.4.2", "jsdom": "28.1.0", "jsonc-eslint-parser": "2.4.0", @@ -303,6 +312,7 @@ "rollup-plugin-postcss": "4.0.2", "rollup-plugin-preserve-directives": "^0.4.0", "sass": "1.56.2", + "source-map": "^0.7.6", "ts-jest": "29.4.6", "ts-node": "10.9.2", "tsx": "4.19.0", diff --git a/core-web/pnpm-lock.yaml b/core-web/pnpm-lock.yaml index 4b9ca7a56aca..a4975568e27f 100644 --- a/core-web/pnpm-lock.yaml +++ b/core-web/pnpm-lock.yaml @@ -61,6 +61,9 @@ importers: '@floating-ui/dom': specifier: 1.6.13 version: 1.6.13 + '@hono/node-server': + specifier: ^2.0.5 + version: 2.0.12(hono@4.12.30) '@jitsu/sdk-js': specifier: 3.1.5 version: 3.1.5 @@ -124,6 +127,9 @@ importers: '@nx/playwright': specifier: 23.1.1 version: 23.1.1(@babel/traverse@7.29.7)(@nx/jest@23.1.1(8be0172cf244ce853aad5d3fbafc9ccb))(@playwright/test@1.36.0)(@swc-node/register@1.11.1(@swc/core@1.15.8(@swc/helpers@0.5.23))(@swc/types@0.1.27)(typescript@6.0.3))(@swc/core@1.15.8(@swc/helpers@0.5.23))(@zkochan/js-yaml@0.0.7)(eslint@9.39.4(jiti@2.4.2))(nx@23.1.1(@swc-node/register@1.11.1(@swc/core@1.15.8(@swc/helpers@0.5.23))(@swc/types@0.1.27)(typescript@6.0.3))(@swc/core@1.15.8(@swc/helpers@0.5.23))) + '@openrouter/ai-sdk-provider': + specifier: ^2.9.1 + version: 2.10.0(ai@6.0.225(zod@4.1.9))(zod@4.1.9) '@primeuix/themes': specifier: 2.0.3 version: 2.0.3 @@ -247,6 +253,9 @@ importers: ai: specifier: ^6.0.168 version: 6.0.225(zod@4.1.9) + ai-sdk-provider-opencode-sdk: + specifier: ^3.0.6 + version: 3.0.6(zod@4.1.9) axios: specifier: 1.15.0 version: 1.15.0 @@ -307,6 +316,9 @@ importers: gridstack: specifier: 8.4.0 version: 8.4.0 + hono: + specifier: ^4.12.26 + version: 4.12.30 htmldiff-js: specifier: 1.0.5 version: 1.0.5 @@ -461,9 +473,6 @@ importers: '@angular/language-service': specifier: 22.1.0 version: 22.1.0 - '@apidevtools/swagger-parser': - specifier: 10.1.1 - version: 10.1.1(openapi-types@12.1.3) '@babel/core': specifier: 7.29.0 version: 7.29.0 @@ -647,9 +656,18 @@ importers: babel-loader: specifier: 10.0.0 version: 10.0.0(@babel/core@7.29.0)(webpack@5.64.0(@swc/core@1.15.8(@swc/helpers@0.5.23))(esbuild@0.19.2)(lightningcss@1.33.0)(postcss@8.5.6)) + css-select: + specifier: ^7.0.0 + version: 7.0.0 daisyui: specifier: 5.5.19 version: 5.5.19 + domhandler: + specifier: ^6.0.1 + version: 6.0.1 + domutils: + specifier: ^4.0.2 + version: 4.0.2 dotenv: specifier: 16.5.0 version: 16.5.0 @@ -701,6 +719,9 @@ importers: happy-dom: specifier: 15.7.4 version: 15.7.4 + htmlparser2: + specifier: ^12.0.0 + version: 12.0.0 http-proxy-middleware: specifier: 3.0.5 version: 3.0.5 @@ -713,6 +734,9 @@ importers: jest-environment-jsdom: specifier: 29.7.0 version: 29.7.0 + jest-environment-node: + specifier: ^30.0.2 + version: 30.4.1 jest-html-reporters: specifier: 3.1.5 version: 3.1.5 @@ -722,6 +746,9 @@ importers: jest-preset-angular: specifier: 17.0.0 version: 17.0.0(@angular/compiler-cli@22.1.0(@angular/compiler@22.1.0)(typescript@6.0.3))(@angular/core@22.1.0(@angular/compiler@22.1.0)(rxjs@7.8.2)(zone.js@0.15.1))(@angular/platform-browser@22.1.0(@angular/animations@22.1.0(@angular/core@22.1.0(@angular/compiler@22.1.0)(rxjs@7.8.2)(zone.js@0.15.1)))(@angular/common@22.1.0(@angular/core@22.1.0(@angular/compiler@22.1.0)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@22.1.0(@angular/compiler@22.1.0)(rxjs@7.8.2)(zone.js@0.15.1)))(@babel/core@7.29.0)(@jest/transform@30.4.1)(@jest/types@30.4.1)(babel-jest@30.2.0(@babel/core@7.29.0))(jest@30.2.0(@types/node@20.19.9)(babel-plugin-macros@3.1.0)(esbuild-register@3.6.0(esbuild@0.19.2))(ts-node@10.9.2(@swc/core@1.15.8(@swc/helpers@0.5.23))(@types/node@20.19.9)(typescript@6.0.3)))(jsdom@28.1.0)(typescript@6.0.3) + jest-util: + specifier: ^30.0.2 + version: 30.4.1 jiti: specifier: 2.4.2 version: 2.4.2 @@ -773,6 +800,9 @@ importers: sass: specifier: 1.56.2 version: 1.56.2 + source-map: + specifier: ^0.7.6 + version: 0.7.6 ts-jest: specifier: 29.4.6 version: 29.4.6(@babel/core@7.29.0)(@jest/transform@30.4.1)(@jest/types@30.4.1)(babel-jest@30.2.0(@babel/core@7.29.0))(esbuild@0.19.2)(jest-util@30.4.1)(jest@30.2.0(@types/node@20.19.9)(babel-plugin-macros@3.1.0)(esbuild-register@3.6.0(esbuild@0.19.2))(ts-node@10.9.2(@swc/core@1.15.8(@swc/helpers@0.5.23))(@types/node@20.19.9)(typescript@6.0.3)))(typescript@6.0.3) @@ -828,75 +858,75 @@ importers: packages: '@acemir/cssom@0.9.31': - resolution: {integrity: sha512-ZnR3GSaH+/vJ0YlHau21FjfLYjMpYVIzTD8M8vIEQvIGxeOXyXdzCI140rrCY862p/C/BbzWsjc1dgnM9mkoTA==} + resolution: {integrity: sha512-ZnR3GSaH+/vJ0YlHau21FjfLYjMpYVIzTD8M8vIEQvIGxeOXyXdzCI140rrCY862p/C/BbzWsjc1dgnM9mkoTA==, tarball: https://registry.npmjs.org/@acemir/cssom/-/cssom-0.9.31.tgz} '@adobe/css-tools@4.5.0': - resolution: {integrity: sha512-6OzddxPio9UiWTCemp4N8cYLV2ZN1ncRnV1cVGtve7dhPOtRkleRyx32GQCYSwDYgaHU3USMm84tNsvKzRCa1Q==} + resolution: {integrity: sha512-6OzddxPio9UiWTCemp4N8cYLV2ZN1ncRnV1cVGtve7dhPOtRkleRyx32GQCYSwDYgaHU3USMm84tNsvKzRCa1Q==, tarball: https://registry.npmjs.org/@adobe/css-tools/-/css-tools-4.5.0.tgz} '@ai-sdk/anthropic@3.0.96': - resolution: {integrity: sha512-6VQzaXQdm5FkX6NWOyKzV5GB11C8IqkgsKZE91lg/bdwyvnQJLDwal2qkE0+fC8CCGeW5d+VV8Mw/+H+OcDC1A==} + resolution: {integrity: sha512-6VQzaXQdm5FkX6NWOyKzV5GB11C8IqkgsKZE91lg/bdwyvnQJLDwal2qkE0+fC8CCGeW5d+VV8Mw/+H+OcDC1A==, tarball: https://registry.npmjs.org/@ai-sdk/anthropic/-/anthropic-3.0.96.tgz} engines: {node: '>=18'} peerDependencies: zod: ^3.25.76 || ^4.1.8 '@ai-sdk/gateway@3.0.149': - resolution: {integrity: sha512-+EVPEHqdJVJn0FZHBd6NyH4rvlTK7X79B6xFuW5bfZIP1G/7Y5OTEgxpL0hjOCAxDBG4ZFM6SZWnVBXxeR1x8w==} + resolution: {integrity: sha512-+EVPEHqdJVJn0FZHBd6NyH4rvlTK7X79B6xFuW5bfZIP1G/7Y5OTEgxpL0hjOCAxDBG4ZFM6SZWnVBXxeR1x8w==, tarball: https://registry.npmjs.org/@ai-sdk/gateway/-/gateway-3.0.149.tgz} engines: {node: '>=18'} peerDependencies: zod: ^3.25.76 || ^4.1.8 '@ai-sdk/provider-utils@4.0.38': - resolution: {integrity: sha512-/HHGmtKllqjg1OLc023v9w9kK3laW7Z6TzfZukYQWCsGBbzB9p60zTvvpXFVcs44NZBVXL3viOa1HRKUbeee8g==} + resolution: {integrity: sha512-/HHGmtKllqjg1OLc023v9w9kK3laW7Z6TzfZukYQWCsGBbzB9p60zTvvpXFVcs44NZBVXL3viOa1HRKUbeee8g==, tarball: https://registry.npmjs.org/@ai-sdk/provider-utils/-/provider-utils-4.0.38.tgz} engines: {node: '>=18'} peerDependencies: zod: ^3.25.76 || ^4.1.8 '@ai-sdk/provider@3.0.14': - resolution: {integrity: sha512-5X1k57JBJ4H7H1QjX7CnJYAB1I19r/trVZTMcSms7/kLNZ8RaU4Nt2agcwZzv82Hfx6Q7/TOLU7agAKeFfc8cA==} + resolution: {integrity: sha512-5X1k57JBJ4H7H1QjX7CnJYAB1I19r/trVZTMcSms7/kLNZ8RaU4Nt2agcwZzv82Hfx6Q7/TOLU7agAKeFfc8cA==, tarball: https://registry.npmjs.org/@ai-sdk/provider/-/provider-3.0.14.tgz} engines: {node: '>=18'} '@alloc/quick-lru@5.2.0': - resolution: {integrity: sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==} + resolution: {integrity: sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==, tarball: https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz} engines: {node: '>=10'} '@ampproject/remapping@2.3.0': - resolution: {integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==} + resolution: {integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==, tarball: https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz} engines: {node: '>=6.0.0'} '@analytics/cookie-utils@0.2.14': - resolution: {integrity: sha512-x51x2cLqvP5Fb1ydgNvTCX+SVv0ALK/yTNwp/53++yk4kLhxb850krWtQ4aASN0612oXrIGotwfmdJIttnLiPQ==} + resolution: {integrity: sha512-x51x2cLqvP5Fb1ydgNvTCX+SVv0ALK/yTNwp/53++yk4kLhxb850krWtQ4aASN0612oXrIGotwfmdJIttnLiPQ==, tarball: https://registry.npmjs.org/@analytics/cookie-utils/-/cookie-utils-0.2.14.tgz} '@analytics/core@0.12.17': - resolution: {integrity: sha512-GMxRm5Dp3Wam/w5NNvqNKMO6zWecozbVv21Kn4WhftCx6OjJI7zMlVtiLpjGjxa0RRZfVG80YhupF0Qh9XL2gw==} + resolution: {integrity: sha512-GMxRm5Dp3Wam/w5NNvqNKMO6zWecozbVv21Kn4WhftCx6OjJI7zMlVtiLpjGjxa0RRZfVG80YhupF0Qh9XL2gw==, tarball: https://registry.npmjs.org/@analytics/core/-/core-0.12.17.tgz} '@analytics/global-storage-utils@0.1.9': - resolution: {integrity: sha512-+xm6CDnWsVOQIKkqbPRPRdYDXKk3PNgr/bCZWSI+7tEDT5PCDgI0QSBZe+FqCVkCRtTkgOrjFOY7wOM8Gq+ndA==} + resolution: {integrity: sha512-+xm6CDnWsVOQIKkqbPRPRdYDXKk3PNgr/bCZWSI+7tEDT5PCDgI0QSBZe+FqCVkCRtTkgOrjFOY7wOM8Gq+ndA==, tarball: https://registry.npmjs.org/@analytics/global-storage-utils/-/global-storage-utils-0.1.9.tgz} '@analytics/localstorage-utils@0.1.12': - resolution: {integrity: sha512-BL3vuZUwWgMqdkQsE0GKsED5SPLC6daI4K4LE0a/BkKv+4Cae5JLLqpO5gju2HUGOjJxIvw8U/G5EcglNY5+1w==} + resolution: {integrity: sha512-BL3vuZUwWgMqdkQsE0GKsED5SPLC6daI4K4LE0a/BkKv+4Cae5JLLqpO5gju2HUGOjJxIvw8U/G5EcglNY5+1w==, tarball: https://registry.npmjs.org/@analytics/localstorage-utils/-/localstorage-utils-0.1.12.tgz} '@analytics/queue-utils@0.1.3': - resolution: {integrity: sha512-W3nrt7vZDsR0Dzpte2o44myMfNAdHJVc8xVLwEdFFMTUB7/tKauM7GAWv07GjG6v3YEfNMab7l1UAEIFA/0FqA==} + resolution: {integrity: sha512-W3nrt7vZDsR0Dzpte2o44myMfNAdHJVc8xVLwEdFFMTUB7/tKauM7GAWv07GjG6v3YEfNMab7l1UAEIFA/0FqA==, tarball: https://registry.npmjs.org/@analytics/queue-utils/-/queue-utils-0.1.3.tgz} '@analytics/router-utils@0.1.1': - resolution: {integrity: sha512-IsSwkTp854IdYrzS4Mcj0rDh5R7xmXexL5mpcr/CO9PzH7FHqwc//zjmAk4wLZh7I+7gSylh+xnkFnNMoCsepw==} + resolution: {integrity: sha512-IsSwkTp854IdYrzS4Mcj0rDh5R7xmXexL5mpcr/CO9PzH7FHqwc//zjmAk4wLZh7I+7gSylh+xnkFnNMoCsepw==, tarball: https://registry.npmjs.org/@analytics/router-utils/-/router-utils-0.1.1.tgz} '@analytics/session-storage-utils@0.0.9': - resolution: {integrity: sha512-fhP9QCpyq45rZKsXaAxyz+VTmOUWljIW08CWSkFzpwOHkDM4Xy5tymc1YcWqSBBaLjHldo3HlY4qfqEIS4Aj1A==} + resolution: {integrity: sha512-fhP9QCpyq45rZKsXaAxyz+VTmOUWljIW08CWSkFzpwOHkDM4Xy5tymc1YcWqSBBaLjHldo3HlY4qfqEIS4Aj1A==, tarball: https://registry.npmjs.org/@analytics/session-storage-utils/-/session-storage-utils-0.0.9.tgz} '@analytics/storage-utils@0.4.4': - resolution: {integrity: sha512-873P4wDIunbOnBqADc2AhTVsLbluUv1dP6k9UrK8FIeV8WXv5+fG12HdwwaniUIxq6QLgZJfKEaCwtWSKrrV0g==} + resolution: {integrity: sha512-873P4wDIunbOnBqADc2AhTVsLbluUv1dP6k9UrK8FIeV8WXv5+fG12HdwwaniUIxq6QLgZJfKEaCwtWSKrrV0g==, tarball: https://registry.npmjs.org/@analytics/storage-utils/-/storage-utils-0.4.4.tgz} '@analytics/type-utils@0.6.4': - resolution: {integrity: sha512-Ou1gQxFakOWLcPnbFVsrPb8g1wLLUZYYJXDPjHkG07+5mustGs5yqACx42UAu4A6NszNN6Z5gGxhyH45zPWRxw==} + resolution: {integrity: sha512-Ou1gQxFakOWLcPnbFVsrPb8g1wLLUZYYJXDPjHkG07+5mustGs5yqACx42UAu4A6NszNN6Z5gGxhyH45zPWRxw==, tarball: https://registry.npmjs.org/@analytics/type-utils/-/type-utils-0.6.4.tgz} '@angular-devkit/architect@0.2201.2': - resolution: {integrity: sha512-RRG3JA3hPH0ypbDIyquZt9DDTP5pOMPgqQ/iLSkok1MZdKiOgpk6FGfXCa1ei72SwlX7lnJdq94d6WWdqpbyKg==} + resolution: {integrity: sha512-RRG3JA3hPH0ypbDIyquZt9DDTP5pOMPgqQ/iLSkok1MZdKiOgpk6FGfXCa1ei72SwlX7lnJdq94d6WWdqpbyKg==, tarball: https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.2201.2.tgz} engines: {node: ^22.22.3 || ^24.15.0 || >=26.0.0, npm: ^6.11.0 || ^7.5.6 || >=8.0.0, yarn: '>= 1.13.0'} hasBin: true '@angular-devkit/build-angular@22.1.2': - resolution: {integrity: sha512-+jngKxBnagJcfrFcNXHfb0bmKWMmxTPEba0T9KPXmtgv+jk6AUlVhWfcU3fpBKrRZs9ChxyVWMDnI56FNItpvA==} + resolution: {integrity: sha512-+jngKxBnagJcfrFcNXHfb0bmKWMmxTPEba0T9KPXmtgv+jk6AUlVhWfcU3fpBKrRZs9ChxyVWMDnI56FNItpvA==, tarball: https://registry.npmjs.org/@angular-devkit/build-angular/-/build-angular-22.1.2.tgz} engines: {node: ^22.22.3 || ^24.15.0 || >=26.0.0, npm: ^6.11.0 || ^7.5.6 || >=8.0.0, yarn: '>= 1.13.0'} deprecated: Angular's Webpack support is deprecated. Use the esbuild and Vite-based "@angular/build" package instead. peerDependencies: @@ -935,14 +965,14 @@ packages: optional: true '@angular-devkit/build-webpack@0.2201.2': - resolution: {integrity: sha512-mRt2JsrQVBI/CKoD6yxFJRsTb9Xv20kmJDjzuAiuxAbwXLjXDgZWqqibPjt3bd78IHDntE5+n5ZNjsj0vR9lJA==} + resolution: {integrity: sha512-mRt2JsrQVBI/CKoD6yxFJRsTb9Xv20kmJDjzuAiuxAbwXLjXDgZWqqibPjt3bd78IHDntE5+n5ZNjsj0vR9lJA==, tarball: https://registry.npmjs.org/@angular-devkit/build-webpack/-/build-webpack-0.2201.2.tgz} engines: {node: ^22.22.3 || ^24.15.0 || >=26.0.0, npm: ^6.11.0 || ^7.5.6 || >=8.0.0, yarn: '>= 1.13.0'} peerDependencies: webpack: ^5.30.0 webpack-dev-server: ^5.0.2 '@angular-devkit/core@22.0.7': - resolution: {integrity: sha512-r8XiflsVYcsvWp+zVvaNv5GsDoihaZ2OwWfn++N6YqTUZLcqDzqhsxeNk60sJ5V+Jn4ck1aKF4A9flmvSY+tpQ==} + resolution: {integrity: sha512-r8XiflsVYcsvWp+zVvaNv5GsDoihaZ2OwWfn++N6YqTUZLcqDzqhsxeNk60sJ5V+Jn4ck1aKF4A9flmvSY+tpQ==, tarball: https://registry.npmjs.org/@angular-devkit/core/-/core-22.0.7.tgz} engines: {node: ^22.22.3 || ^24.15.0 || >=26.0.0, npm: ^6.11.0 || ^7.5.6 || >=8.0.0, yarn: '>= 1.13.0'} peerDependencies: chokidar: ^5.0.0 @@ -951,7 +981,7 @@ packages: optional: true '@angular-devkit/core@22.1.2': - resolution: {integrity: sha512-tF1oEE7KPs8I08HJQmH5e4GkLUB3+MXXy8t6gMJULaLFxZYP9K1oXRFLappMpdm9OIbEXOChk23hrho0By9aYg==} + resolution: {integrity: sha512-tF1oEE7KPs8I08HJQmH5e4GkLUB3+MXXy8t6gMJULaLFxZYP9K1oXRFLappMpdm9OIbEXOChk23hrho0By9aYg==, tarball: https://registry.npmjs.org/@angular-devkit/core/-/core-22.1.2.tgz} engines: {node: ^22.22.3 || ^24.15.0 || >=26.0.0, npm: ^6.11.0 || ^7.5.6 || >=8.0.0, yarn: '>= 1.13.0'} peerDependencies: chokidar: ^5.0.0 @@ -960,25 +990,25 @@ packages: optional: true '@angular-devkit/schematics@22.0.7': - resolution: {integrity: sha512-bKgnBB0LPAj44uVXfW0UO1rQBb3HGXDZxa1bLtESr/KCK4j5iiaXlHqvJjc/z0em1Ds9WNQOfNXjV3IdJo9sSw==} + resolution: {integrity: sha512-bKgnBB0LPAj44uVXfW0UO1rQBb3HGXDZxa1bLtESr/KCK4j5iiaXlHqvJjc/z0em1Ds9WNQOfNXjV3IdJo9sSw==, tarball: https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-22.0.7.tgz} engines: {node: ^22.22.3 || ^24.15.0 || >=26.0.0, npm: ^6.11.0 || ^7.5.6 || >=8.0.0, yarn: '>= 1.13.0'} '@angular-devkit/schematics@22.1.2': - resolution: {integrity: sha512-Lw6NvW5rfMUl/2dsuWY8l6wlfWCuYBzCYSSqqliLPDco0doGzBliHwY9uxuzuUKZgOl5TvuVyvEo0t3o4Jj4GA==} + resolution: {integrity: sha512-Lw6NvW5rfMUl/2dsuWY8l6wlfWCuYBzCYSSqqliLPDco0doGzBliHwY9uxuzuUKZgOl5TvuVyvEo0t3o4Jj4GA==, tarball: https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-22.1.2.tgz} engines: {node: ^22.22.3 || ^24.15.0 || >=26.0.0, npm: ^6.11.0 || ^7.5.6 || >=8.0.0, yarn: '>= 1.13.0'} '@angular-eslint/builder@22.1.0': - resolution: {integrity: sha512-gmdk06PK0whNJlapIQjRnhud2/n+oIIDBIoLUHWFFxhHRs+ZstVc6+81gf+sYDOm1Ehmb0V3nZAg3YpT1Mk0Eg==} + resolution: {integrity: sha512-gmdk06PK0whNJlapIQjRnhud2/n+oIIDBIoLUHWFFxhHRs+ZstVc6+81gf+sYDOm1Ehmb0V3nZAg3YpT1Mk0Eg==, tarball: https://registry.npmjs.org/@angular-eslint/builder/-/builder-22.1.0.tgz} peerDependencies: '@angular/cli': '>= 22.0.0 < 23.0.0' eslint: ^9.0.0 || ^10.0.0 typescript: '*' '@angular-eslint/bundled-angular-compiler@22.1.0': - resolution: {integrity: sha512-iOtOQ2jtrtko1rIQo6i+g3ezxGL0lyYv80j4GccFTK1JGh4K+AqYkmaBvfUfNtqoE/7VcKsOoyxaFt6iIpKhaQ==} + resolution: {integrity: sha512-iOtOQ2jtrtko1rIQo6i+g3ezxGL0lyYv80j4GccFTK1JGh4K+AqYkmaBvfUfNtqoE/7VcKsOoyxaFt6iIpKhaQ==, tarball: https://registry.npmjs.org/@angular-eslint/bundled-angular-compiler/-/bundled-angular-compiler-22.1.0.tgz} '@angular-eslint/eslint-plugin-template@22.1.0': - resolution: {integrity: sha512-GK/Mwhwvj+MX6DefD29/IfuYjcai5CsCbYJ6Qb9P6IAzZaQqw+EN+CiXxIwzMnzm2edLcJzAsX8iBk4X7sVi2Q==} + resolution: {integrity: sha512-GK/Mwhwvj+MX6DefD29/IfuYjcai5CsCbYJ6Qb9P6IAzZaQqw+EN+CiXxIwzMnzm2edLcJzAsX8iBk4X7sVi2Q==, tarball: https://registry.npmjs.org/@angular-eslint/eslint-plugin-template/-/eslint-plugin-template-22.1.0.tgz} peerDependencies: '@angular-eslint/template-parser': 22.1.0 '@typescript-eslint/types': ^8.0.0 @@ -987,39 +1017,39 @@ packages: typescript: '*' '@angular-eslint/eslint-plugin@22.1.0': - resolution: {integrity: sha512-nRwdbUiW7vF0gbwtxw2hRGPZYZCNWOTLZKw4HM+I37o5YFIA0CFLtbyRKgZWs6JZCIqzTYSwCyKNsW41Qw0FwQ==} + resolution: {integrity: sha512-nRwdbUiW7vF0gbwtxw2hRGPZYZCNWOTLZKw4HM+I37o5YFIA0CFLtbyRKgZWs6JZCIqzTYSwCyKNsW41Qw0FwQ==, tarball: https://registry.npmjs.org/@angular-eslint/eslint-plugin/-/eslint-plugin-22.1.0.tgz} peerDependencies: '@typescript-eslint/utils': ^8.0.0 eslint: ^9.0.0 || ^10.0.0 typescript: '*' '@angular-eslint/schematics@22.1.0': - resolution: {integrity: sha512-/Z7MEO9ys9P5w5itM2mpNW4R/QNAVxtDdcU5LVFulSx/lo7zCDx1rZ/tLDuzbN5k5RGQy5ZkdRIs0MoscsGXQQ==} + resolution: {integrity: sha512-/Z7MEO9ys9P5w5itM2mpNW4R/QNAVxtDdcU5LVFulSx/lo7zCDx1rZ/tLDuzbN5k5RGQy5ZkdRIs0MoscsGXQQ==, tarball: https://registry.npmjs.org/@angular-eslint/schematics/-/schematics-22.1.0.tgz} peerDependencies: '@angular/cli': '>= 22.0.0 < 23.0.0' '@angular-eslint/template-parser@22.1.0': - resolution: {integrity: sha512-gcufZLI/Rl2fOWtBk2MgMRkH1t+OrbJGxIsfT0w3pVjKm0zwi7njM/dtPbVjCHzsGhx7szQMvY0i0UwTBVYkSw==} + resolution: {integrity: sha512-gcufZLI/Rl2fOWtBk2MgMRkH1t+OrbJGxIsfT0w3pVjKm0zwi7njM/dtPbVjCHzsGhx7szQMvY0i0UwTBVYkSw==, tarball: https://registry.npmjs.org/@angular-eslint/template-parser/-/template-parser-22.1.0.tgz} peerDependencies: eslint: ^9.0.0 || ^10.0.0 typescript: '*' '@angular-eslint/utils@22.1.0': - resolution: {integrity: sha512-zDPJqgOxlkG20UJWsVDSvnEtC2MfIP0+yKMaQUBL+2nrEknV/fVLrz7ApnerWiDrGTcAms5si49KV10MarMpRA==} + resolution: {integrity: sha512-zDPJqgOxlkG20UJWsVDSvnEtC2MfIP0+yKMaQUBL+2nrEknV/fVLrz7ApnerWiDrGTcAms5si49KV10MarMpRA==, tarball: https://registry.npmjs.org/@angular-eslint/utils/-/utils-22.1.0.tgz} peerDependencies: '@typescript-eslint/utils': ^8.0.0 eslint: ^9.0.0 || ^10.0.0 typescript: '*' '@angular/animations@22.1.0': - resolution: {integrity: sha512-MHXOXmn9zmkiq234J+pr2Ir4A+z1iiVmQ0WjQ4skvK8GHTjnjeHKb4HrpAo5f7S3W0oenszmkNCddTQ3pvoJPw==} + resolution: {integrity: sha512-MHXOXmn9zmkiq234J+pr2Ir4A+z1iiVmQ0WjQ4skvK8GHTjnjeHKb4HrpAo5f7S3W0oenszmkNCddTQ3pvoJPw==, tarball: https://registry.npmjs.org/@angular/animations/-/animations-22.1.0.tgz} engines: {node: ^22.22.3 || ^24.15.0 || >=26.0.0} deprecated: '@angular/animations is deprecated. Use `animate.enter` and `animate.leave` instead. For more information see: https://v22.angular.dev/guide/animations.' peerDependencies: '@angular/core': 22.1.0 '@angular/build@22.1.2': - resolution: {integrity: sha512-DE/3o17JTel4EBt2BA4DqJYeBBuz5Ef/kf1jL9YZTyJu4SrLr/HI79K14jFr0VRIxzcqG92FdIzfLDxbOesQsg==} + resolution: {integrity: sha512-DE/3o17JTel4EBt2BA4DqJYeBBuz5Ef/kf1jL9YZTyJu4SrLr/HI79K14jFr0VRIxzcqG92FdIzfLDxbOesQsg==, tarball: https://registry.npmjs.org/@angular/build/-/build-22.1.2.tgz} engines: {node: ^22.22.3 || ^24.15.0 || >=26.0.0, npm: ^6.11.0 || ^7.5.6 || >=8.0.0, yarn: '>= 1.13.0'} peerDependencies: '@angular/compiler': ^22.0.0 @@ -1071,7 +1101,7 @@ packages: optional: true '@angular/cdk@22.1.0': - resolution: {integrity: sha512-yfQug47CZ+51mHy0ZLWzi6F/YHfsAF2Z7Jxo3JLM7Aj3Er47hHB8VnrNERwK5tBLs0bE5DoEIm59ga/MI3fVGg==} + resolution: {integrity: sha512-yfQug47CZ+51mHy0ZLWzi6F/YHfsAF2Z7Jxo3JLM7Aj3Er47hHB8VnrNERwK5tBLs0bE5DoEIm59ga/MI3fVGg==, tarball: https://registry.npmjs.org/@angular/cdk/-/cdk-22.1.0.tgz} peerDependencies: '@angular/common': ^22.0.0 || ^23.0.0 '@angular/core': ^22.0.0 || ^23.0.0 @@ -1079,19 +1109,19 @@ packages: rxjs: ^6.5.3 || ^7.4.0 '@angular/cli@22.1.2': - resolution: {integrity: sha512-gzB+iuZzB507DAkZb9s5+Jw8QRzOBolUhHEuAKH74xF6oWlEP5JdexfTgti45SjXaKKqeYpODJFnUmSQQJRhxA==} + resolution: {integrity: sha512-gzB+iuZzB507DAkZb9s5+Jw8QRzOBolUhHEuAKH74xF6oWlEP5JdexfTgti45SjXaKKqeYpODJFnUmSQQJRhxA==, tarball: https://registry.npmjs.org/@angular/cli/-/cli-22.1.2.tgz} engines: {node: ^22.22.3 || ^24.15.0 || >=26.0.0, npm: ^6.11.0 || ^7.5.6 || >=8.0.0, yarn: '>= 1.13.0'} hasBin: true '@angular/common@22.1.0': - resolution: {integrity: sha512-67L8AS00egxwEKnoMhNDxy+TY+eKOwvwa+os0Odq8nLm7+Qh7JnMVeub8hfncpenOFqlC/RUjO2W9H7Gd2veNA==} + resolution: {integrity: sha512-67L8AS00egxwEKnoMhNDxy+TY+eKOwvwa+os0Odq8nLm7+Qh7JnMVeub8hfncpenOFqlC/RUjO2W9H7Gd2veNA==, tarball: https://registry.npmjs.org/@angular/common/-/common-22.1.0.tgz} engines: {node: ^22.22.3 || ^24.15.0 || >=26.0.0} peerDependencies: '@angular/core': 22.1.0 rxjs: ^6.5.3 || ^7.4.0 '@angular/compiler-cli@22.1.0': - resolution: {integrity: sha512-jL89dbzkrV8AeLaxedBgT7ErMnbfi2dvwDJuCrUgm3eCNfbcOpGbNxzH+wDgvbRg2Lhj11/u3JPbW50xA6rvvg==} + resolution: {integrity: sha512-jL89dbzkrV8AeLaxedBgT7ErMnbfi2dvwDJuCrUgm3eCNfbcOpGbNxzH+wDgvbRg2Lhj11/u3JPbW50xA6rvvg==, tarball: https://registry.npmjs.org/@angular/compiler-cli/-/compiler-cli-22.1.0.tgz} engines: {node: ^22.22.3 || ^24.15.0 || >=26.0.0} hasBin: true peerDependencies: @@ -1102,11 +1132,11 @@ packages: optional: true '@angular/compiler@22.1.0': - resolution: {integrity: sha512-WCmuPnuXgqnqrkbrwqQRyldi1k3rlzNLVDl8ntINF7XWuJh0KfQLEkRK0FCCmBztWJkbGug4RBVnKTWlKhRzCQ==} + resolution: {integrity: sha512-WCmuPnuXgqnqrkbrwqQRyldi1k3rlzNLVDl8ntINF7XWuJh0KfQLEkRK0FCCmBztWJkbGug4RBVnKTWlKhRzCQ==, tarball: https://registry.npmjs.org/@angular/compiler/-/compiler-22.1.0.tgz} engines: {node: ^22.22.3 || ^24.15.0 || >=26.0.0} '@angular/core@22.1.0': - resolution: {integrity: sha512-X5UaMuOCI4HAvSQIs3QtM+5e0Cni16DRaHUIL3BIBd4ZQNnSH3pZ25TsKQ8Jlu/3hAQ9rzV278kNQcecooGJ7g==} + resolution: {integrity: sha512-X5UaMuOCI4HAvSQIs3QtM+5e0Cni16DRaHUIL3BIBd4ZQNnSH3pZ25TsKQ8Jlu/3hAQ9rzV278kNQcecooGJ7g==, tarball: https://registry.npmjs.org/@angular/core/-/core-22.1.0.tgz} engines: {node: ^22.22.3 || ^24.15.0 || >=26.0.0} peerDependencies: '@angular/compiler': 22.1.0 @@ -1119,14 +1149,14 @@ packages: optional: true '@angular/elements@22.1.0': - resolution: {integrity: sha512-ZxKAxRPiptxeuli6cKL0pU15wnIgg02Aear0AuXofNmgUNm3+YVA8HsBhu2JZfQ7tYa/+LfwluRerpxK0tawCA==} + resolution: {integrity: sha512-ZxKAxRPiptxeuli6cKL0pU15wnIgg02Aear0AuXofNmgUNm3+YVA8HsBhu2JZfQ7tYa/+LfwluRerpxK0tawCA==, tarball: https://registry.npmjs.org/@angular/elements/-/elements-22.1.0.tgz} engines: {node: ^22.22.3 || ^24.15.0 || >=26.0.0} peerDependencies: '@angular/core': 22.1.0 rxjs: ^6.5.3 || ^7.4.0 '@angular/forms@22.1.0': - resolution: {integrity: sha512-nWlSM/pPp78Sx/fBM/tFEgZxdfZe50LkCE2/hkO22Fi1UM2maGc43LDsu/s6l0q9hFep4Wj+xa30KXDBS7Cn8A==} + resolution: {integrity: sha512-nWlSM/pPp78Sx/fBM/tFEgZxdfZe50LkCE2/hkO22Fi1UM2maGc43LDsu/s6l0q9hFep4Wj+xa30KXDBS7Cn8A==, tarball: https://registry.npmjs.org/@angular/forms/-/forms-22.1.0.tgz} engines: {node: ^22.22.3 || ^24.15.0 || >=26.0.0} peerDependencies: '@angular/common': 22.1.0 @@ -1135,11 +1165,11 @@ packages: rxjs: ^6.5.3 || ^7.4.0 '@angular/language-service@22.1.0': - resolution: {integrity: sha512-5J+j17o9rvJEiTVotsQfHprPCgKrHhYxz+SpiV25p5mG2qOJ9vX465o/ODbFpRapK8eyHqd/HkPRaGafu3nZkg==} + resolution: {integrity: sha512-5J+j17o9rvJEiTVotsQfHprPCgKrHhYxz+SpiV25p5mG2qOJ9vX465o/ODbFpRapK8eyHqd/HkPRaGafu3nZkg==, tarball: https://registry.npmjs.org/@angular/language-service/-/language-service-22.1.0.tgz} engines: {node: ^22.22.3 || ^24.15.0 || >=26.0.0} '@angular/platform-browser-dynamic@22.1.0': - resolution: {integrity: sha512-iLn9vCk6HhxQZhFJIEBjHH1CbyOLEABw0Do3VAzly2YimemitgQEmTLJ5a96qd70rYmpJH6zudhnYqUjyaI6Ig==} + resolution: {integrity: sha512-iLn9vCk6HhxQZhFJIEBjHH1CbyOLEABw0Do3VAzly2YimemitgQEmTLJ5a96qd70rYmpJH6zudhnYqUjyaI6Ig==, tarball: https://registry.npmjs.org/@angular/platform-browser-dynamic/-/platform-browser-dynamic-22.1.0.tgz} engines: {node: ^22.22.3 || ^24.15.0 || >=26.0.0} deprecated: '@angular/platform-browser-dynamic is deprecated. Use `@angular/platform-browser` instead.' peerDependencies: @@ -1149,7 +1179,7 @@ packages: '@angular/platform-browser': 22.1.0 '@angular/platform-browser@22.1.0': - resolution: {integrity: sha512-gqUYDUiPfwbaLYdH8WLnOLl3feo3OcNpnMO08HBHaUdi4TLNkC28xwa9fC6ANyYD22QZ5A3abSg8fmR6upWMwg==} + resolution: {integrity: sha512-gqUYDUiPfwbaLYdH8WLnOLl3feo3OcNpnMO08HBHaUdi4TLNkC28xwa9fC6ANyYD22QZ5A3abSg8fmR6upWMwg==, tarball: https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-22.1.0.tgz} engines: {node: ^22.22.3 || ^24.15.0 || >=26.0.0} peerDependencies: '@angular/animations': 22.1.0 @@ -1160,7 +1190,7 @@ packages: optional: true '@angular/router@22.1.0': - resolution: {integrity: sha512-42Bs0g+tV2gE70Lqnt+VD/+DWbvWwQcg8QgXkTIu3A504tYknrZG/wmvki2AJGyZhmyQ46B4pfXLG4WDP8MFSA==} + resolution: {integrity: sha512-42Bs0g+tV2gE70Lqnt+VD/+DWbvWwQcg8QgXkTIu3A504tYknrZG/wmvki2AJGyZhmyQ46B4pfXLG4WDP8MFSA==, tarball: https://registry.npmjs.org/@angular/router/-/router-22.1.0.tgz} engines: {node: ^22.22.3 || ^24.15.0 || >=26.0.0} peerDependencies: '@angular/common': 22.1.0 @@ -1169,1259 +1199,1243 @@ packages: rxjs: ^6.5.3 || ^7.4.0 '@antfu/install-pkg@1.1.0': - resolution: {integrity: sha512-MGQsmw10ZyI+EJo45CdSER4zEb+p31LpDAFp2Z3gkSd1yqVZGi0Ebx++YTEMonJy4oChEMLsxZ64j8FH6sSqtQ==} - - '@apidevtools/json-schema-ref-parser@11.7.2': - resolution: {integrity: sha512-4gY54eEGEstClvEkGnwVkTkrx0sqwemEFG5OSRRn3tD91XH0+Q8XIkYIfo7IwEWPpJZwILb9GUXeShtplRc/eA==} - engines: {node: '>= 16'} - - '@apidevtools/openapi-schemas@2.1.0': - resolution: {integrity: sha512-Zc1AlqrJlX3SlpupFGpiLi2EbteyP7fXmUOGup6/DnkRgjP9bgMM/ag+n91rsv0U1Gpz0H3VILA/o3bW7Ua6BQ==} - engines: {node: '>=10'} - - '@apidevtools/swagger-methods@3.0.2': - resolution: {integrity: sha512-QAkD5kK2b1WfjDS/UQn/qQkbwF31uqRjPTrsCs5ZG9BQGAkjwvqGFjjPqAuzac/IYzpPtRzjCP1WrTuAIjMrXg==} - - '@apidevtools/swagger-parser@10.1.1': - resolution: {integrity: sha512-u/kozRnsPO/x8QtKYJOqoGtC4kH6yg1lfYkB9Au0WhYB0FNLpyFusttQtvhlwjtG3rOwiRz4D8DnnXa8iEpIKA==} - peerDependencies: - openapi-types: '>=7' + resolution: {integrity: sha512-MGQsmw10ZyI+EJo45CdSER4zEb+p31LpDAFp2Z3gkSd1yqVZGi0Ebx++YTEMonJy4oChEMLsxZ64j8FH6sSqtQ==, tarball: https://registry.npmjs.org/@antfu/install-pkg/-/install-pkg-1.1.0.tgz} '@asamuzakjp/css-color@5.1.11': - resolution: {integrity: sha512-KVw6qIiCTUQhByfTd78h2yD1/00waTmm9uy/R7Ck/ctUyAPj+AEDLkQIdJW0T8+qGgj3j5bpNKK7Q3G+LedJWg==} + resolution: {integrity: sha512-KVw6qIiCTUQhByfTd78h2yD1/00waTmm9uy/R7Ck/ctUyAPj+AEDLkQIdJW0T8+qGgj3j5bpNKK7Q3G+LedJWg==, tarball: https://registry.npmjs.org/@asamuzakjp/css-color/-/css-color-5.1.11.tgz} engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} '@asamuzakjp/dom-selector@6.8.1': - resolution: {integrity: sha512-MvRz1nCqW0fsy8Qz4dnLIvhOlMzqDVBabZx6lH+YywFDdjXhMY37SmpV1XFX3JzG5GWHn63j6HX6QPr3lZXHvQ==} + resolution: {integrity: sha512-MvRz1nCqW0fsy8Qz4dnLIvhOlMzqDVBabZx6lH+YywFDdjXhMY37SmpV1XFX3JzG5GWHn63j6HX6QPr3lZXHvQ==, tarball: https://registry.npmjs.org/@asamuzakjp/dom-selector/-/dom-selector-6.8.1.tgz} '@asamuzakjp/generational-cache@1.0.1': - resolution: {integrity: sha512-wajfB8KqzMCN2KGNFdLkReeHncd0AslUSrvHVvvYWuU8ghncRJoA50kT3zP9MVL0+9g4/67H+cdvBskj9THPzg==} + resolution: {integrity: sha512-wajfB8KqzMCN2KGNFdLkReeHncd0AslUSrvHVvvYWuU8ghncRJoA50kT3zP9MVL0+9g4/67H+cdvBskj9THPzg==, tarball: https://registry.npmjs.org/@asamuzakjp/generational-cache/-/generational-cache-1.0.1.tgz} engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} '@asamuzakjp/nwsapi@2.3.9': - resolution: {integrity: sha512-n8GuYSrI9bF7FFZ/SjhwevlHc8xaVlb/7HmHelnc/PZXBD2ZR49NnN9sMMuDdEGPeeRQ5d0hqlSlEpgCX3Wl0Q==} + resolution: {integrity: sha512-n8GuYSrI9bF7FFZ/SjhwevlHc8xaVlb/7HmHelnc/PZXBD2ZR49NnN9sMMuDdEGPeeRQ5d0hqlSlEpgCX3Wl0Q==, tarball: https://registry.npmjs.org/@asamuzakjp/nwsapi/-/nwsapi-2.3.9.tgz} '@babel/code-frame@7.29.7': - resolution: {integrity: sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==} + resolution: {integrity: sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==, tarball: https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.7.tgz} engines: {node: '>=6.9.0'} '@babel/code-frame@8.0.0': - resolution: {integrity: sha512-dYYg153EyN2Ekbqw2zAsbd6/JR+9N2SEoC7YV2GyyqMM7x9bLDTjBD6XBhSMLH0wtIVyJj03jWNriQhaN+eoCw==} + resolution: {integrity: sha512-dYYg153EyN2Ekbqw2zAsbd6/JR+9N2SEoC7YV2GyyqMM7x9bLDTjBD6XBhSMLH0wtIVyJj03jWNriQhaN+eoCw==, tarball: https://registry.npmjs.org/@babel/code-frame/-/code-frame-8.0.0.tgz} engines: {node: ^22.18.0 || >=24.11.0} '@babel/compat-data@7.29.7': - resolution: {integrity: sha512-locTkQyKvwIEgBzVrn8693ebc97F2U8ZHjbXwDXJ5Fn2TCpNwTlKcaKLkdHop5c/icOFE7qt7Q9JC5hnKNa6Gg==} + resolution: {integrity: sha512-locTkQyKvwIEgBzVrn8693ebc97F2U8ZHjbXwDXJ5Fn2TCpNwTlKcaKLkdHop5c/icOFE7qt7Q9JC5hnKNa6Gg==, tarball: https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.7.tgz} engines: {node: '>=6.9.0'} '@babel/compat-data@8.0.0': - resolution: {integrity: sha512-DOjnob/cXOUgDOozCDeq/aK2p5y8dUIVdf6tNhEV1HQRd6I8aQ4f4fbtHRVEvb6lP3BGomrKHiS8ICAASSVQSw==} + resolution: {integrity: sha512-DOjnob/cXOUgDOozCDeq/aK2p5y8dUIVdf6tNhEV1HQRd6I8aQ4f4fbtHRVEvb6lP3BGomrKHiS8ICAASSVQSw==, tarball: https://registry.npmjs.org/@babel/compat-data/-/compat-data-8.0.0.tgz} engines: {node: ^22.18.0 || >=24.11.0} '@babel/core@7.29.0': - resolution: {integrity: sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==} + resolution: {integrity: sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==, tarball: https://registry.npmjs.org/@babel/core/-/core-7.29.0.tgz} engines: {node: '>=6.9.0'} '@babel/core@8.0.1': - resolution: {integrity: sha512-5FgxM4dLQpMJHSiVATk8foW263dVHQHBVpXYiimNECVWG01f4nFyEbQixeT6Mwvg7TayREJ2gpKl3o2RoMdnqw==} + resolution: {integrity: sha512-5FgxM4dLQpMJHSiVATk8foW263dVHQHBVpXYiimNECVWG01f4nFyEbQixeT6Mwvg7TayREJ2gpKl3o2RoMdnqw==, tarball: https://registry.npmjs.org/@babel/core/-/core-8.0.1.tgz} engines: {node: ^22.18.0 || >=24.11.0} '@babel/generator@7.29.7': - resolution: {integrity: sha512-DkXD5OJQaAQIdZ1bt3UZdEnHAn9Imd3IVBdX03UFe+ony9Ojw5pzr9YVKGDY1jt+Gcn/FnGkNf8r+Vj5NOJWtQ==} + resolution: {integrity: sha512-DkXD5OJQaAQIdZ1bt3UZdEnHAn9Imd3IVBdX03UFe+ony9Ojw5pzr9YVKGDY1jt+Gcn/FnGkNf8r+Vj5NOJWtQ==, tarball: https://registry.npmjs.org/@babel/generator/-/generator-7.29.7.tgz} engines: {node: '>=6.9.0'} '@babel/generator@8.0.0': - resolution: {integrity: sha512-NT9NrVwJsbSV6Y2FSstWa71EETOnzrjkL5/wX3D2mYHtKM+qvqB1DvR4D0Setb/gDBsHzRICifwEWMO8CnTF6g==} + resolution: {integrity: sha512-NT9NrVwJsbSV6Y2FSstWa71EETOnzrjkL5/wX3D2mYHtKM+qvqB1DvR4D0Setb/gDBsHzRICifwEWMO8CnTF6g==, tarball: https://registry.npmjs.org/@babel/generator/-/generator-8.0.0.tgz} engines: {node: ^22.18.0 || >=24.11.0} '@babel/helper-annotate-as-pure@7.29.7': - resolution: {integrity: sha512-OoK6239jHPuSQOoS0kfTVKn0b/rVTk0seKq4Gd2UMLtmOVLjDC0ki3e+c90Trqv2gMfvJFqkiljrr568+qddiw==} + resolution: {integrity: sha512-OoK6239jHPuSQOoS0kfTVKn0b/rVTk0seKq4Gd2UMLtmOVLjDC0ki3e+c90Trqv2gMfvJFqkiljrr568+qddiw==, tarball: https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.29.7.tgz} engines: {node: '>=6.9.0'} '@babel/helper-annotate-as-pure@8.0.0': - resolution: {integrity: sha512-NSpMkMsvvZqzThJ0p1B02cbtA2ObEyfBvq950bmNkyxsxvcxwhvvCB036rKhlEnuBBo30bOrk13u3FzlKSoRrw==} + resolution: {integrity: sha512-NSpMkMsvvZqzThJ0p1B02cbtA2ObEyfBvq950bmNkyxsxvcxwhvvCB036rKhlEnuBBo30bOrk13u3FzlKSoRrw==, tarball: https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-8.0.0.tgz} engines: {node: ^22.18.0 || >=24.11.0} '@babel/helper-compilation-targets@7.29.7': - resolution: {integrity: sha512-wem6WaBj4NaVYVdNhLPPVacES6ZJ+KBBfSkTMD3YZxbP3rm3Di85tJU5ljaUNhaOynt+Aj0xruhYuzQBt8n71g==} + resolution: {integrity: sha512-wem6WaBj4NaVYVdNhLPPVacES6ZJ+KBBfSkTMD3YZxbP3rm3Di85tJU5ljaUNhaOynt+Aj0xruhYuzQBt8n71g==, tarball: https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.29.7.tgz} engines: {node: '>=6.9.0'} '@babel/helper-compilation-targets@8.0.0': - resolution: {integrity: sha512-JwculLABZvyPvyLBpwU/E/IbH2uM3mnxNtIJpxnIfb24y1PrdVxK5Dqjle4DpgqpGRnwgC7G8IkzPdSXZrO1Ew==} + resolution: {integrity: sha512-JwculLABZvyPvyLBpwU/E/IbH2uM3mnxNtIJpxnIfb24y1PrdVxK5Dqjle4DpgqpGRnwgC7G8IkzPdSXZrO1Ew==, tarball: https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-8.0.0.tgz} engines: {node: ^22.18.0 || >=24.11.0} '@babel/helper-create-class-features-plugin@7.29.7': - resolution: {integrity: sha512-IY3ZD9Tmooqr3TUhc3DUWxiuo8xx1DWLhd5M7hQ+ZWJamqM2BbalrBJb2MisSLoYorOj75U03qULCxQTY9r3hg==} + resolution: {integrity: sha512-IY3ZD9Tmooqr3TUhc3DUWxiuo8xx1DWLhd5M7hQ+ZWJamqM2BbalrBJb2MisSLoYorOj75U03qULCxQTY9r3hg==, tarball: https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.29.7.tgz} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 '@babel/helper-create-class-features-plugin@8.0.1': - resolution: {integrity: sha512-++t3ZktzlLmASAxIlxeXQK9Z2YwUafYGYcvGBFevqOqt16HozVHStUoQvWD09fzAZOb/uJGpUTBuGK41AJAuOA==} + resolution: {integrity: sha512-++t3ZktzlLmASAxIlxeXQK9Z2YwUafYGYcvGBFevqOqt16HozVHStUoQvWD09fzAZOb/uJGpUTBuGK41AJAuOA==, tarball: https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-8.0.1.tgz} engines: {node: ^22.18.0 || >=24.11.0} peerDependencies: '@babel/core': ^8.0.0 '@babel/helper-create-regexp-features-plugin@7.29.7': - resolution: {integrity: sha512-907Uymvqgg1dwUA+7IGwFAOSYzQOuzPXKNJ1yxzwPffzkYFg2q2eHi1fIOs6sXkG9NbIUMunnUlkYsfRFNvomg==} + resolution: {integrity: sha512-907Uymvqgg1dwUA+7IGwFAOSYzQOuzPXKNJ1yxzwPffzkYFg2q2eHi1fIOs6sXkG9NbIUMunnUlkYsfRFNvomg==, tarball: https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.29.7.tgz} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 '@babel/helper-create-regexp-features-plugin@8.0.1': - resolution: {integrity: sha512-PydTbcVTiIfVweHMeY1u3MslaD/ZzvnaTNhJp+7ghofelLWshF66Ckc/ZsjStfvRQIKQ4uVG0yEJucyDtyrWgw==} + resolution: {integrity: sha512-PydTbcVTiIfVweHMeY1u3MslaD/ZzvnaTNhJp+7ghofelLWshF66Ckc/ZsjStfvRQIKQ4uVG0yEJucyDtyrWgw==, tarball: https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-8.0.1.tgz} engines: {node: ^22.18.0 || >=24.11.0} peerDependencies: '@babel/core': ^8.0.0 '@babel/helper-define-polyfill-provider@0.6.6': - resolution: {integrity: sha512-mOAsxeeKkUKayvZR3HeTYD/fICpCPLJrU5ZjelT/PA6WHtNDBOE436YiaEUvHN454bRM3CebhDsIpieCc4texA==} + resolution: {integrity: sha512-mOAsxeeKkUKayvZR3HeTYD/fICpCPLJrU5ZjelT/PA6WHtNDBOE436YiaEUvHN454bRM3CebhDsIpieCc4texA==, tarball: https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.6.6.tgz} peerDependencies: '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0 '@babel/helper-globals@7.29.7': - resolution: {integrity: sha512-3nQVUAtvkKH9zahfWgw96Jc/uFOmjACE1kQz82E2lqWmHBgjzbNlsC22nuQTfahmWeQtTq5nQ/4Nnd2A1wj4zA==} + resolution: {integrity: sha512-3nQVUAtvkKH9zahfWgw96Jc/uFOmjACE1kQz82E2lqWmHBgjzbNlsC22nuQTfahmWeQtTq5nQ/4Nnd2A1wj4zA==, tarball: https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.29.7.tgz} engines: {node: '>=6.9.0'} '@babel/helper-globals@8.0.0': - resolution: {integrity: sha512-lLozHOM6sWWlxNo8CYqHy4MBZeTvHXNgVPBfPOGsjPKUzHC2Az9QwB6gxdQmpwHl6GlQtbGgS+lj5887guDiLw==} + resolution: {integrity: sha512-lLozHOM6sWWlxNo8CYqHy4MBZeTvHXNgVPBfPOGsjPKUzHC2Az9QwB6gxdQmpwHl6GlQtbGgS+lj5887guDiLw==, tarball: https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-8.0.0.tgz} engines: {node: ^22.18.0 || >=24.11.0} '@babel/helper-member-expression-to-functions@7.29.7': - resolution: {integrity: sha512-j+7JYmk1JYDtACIGj0QJqqWZjoUpMoEikQGADMaHgCMCSDqd2+P32rfcibUNrGOMWrlzK1WJBdxrB3JJQZwWtg==} + resolution: {integrity: sha512-j+7JYmk1JYDtACIGj0QJqqWZjoUpMoEikQGADMaHgCMCSDqd2+P32rfcibUNrGOMWrlzK1WJBdxrB3JJQZwWtg==, tarball: https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.29.7.tgz} engines: {node: '>=6.9.0'} '@babel/helper-member-expression-to-functions@8.0.0': - resolution: {integrity: sha512-xkXrMbtk87Gk7+oKBVmBc6EORg/Qwx++AHESldmHkpvG8wgccdhJJFwrzqlF382Fk8wfXhJHWE/g/43QvEGNPQ==} + resolution: {integrity: sha512-xkXrMbtk87Gk7+oKBVmBc6EORg/Qwx++AHESldmHkpvG8wgccdhJJFwrzqlF382Fk8wfXhJHWE/g/43QvEGNPQ==, tarball: https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-8.0.0.tgz} engines: {node: ^22.18.0 || >=24.11.0} '@babel/helper-module-imports@7.29.7': - resolution: {integrity: sha512-ejHwrQQYcm9xnTivShn2IDOlIzInN34AXskvq9QicvCtEzq1Vzclu/tKF8Jq1Cg8JG2GL6/EmjgsCT7lXepE3g==} + resolution: {integrity: sha512-ejHwrQQYcm9xnTivShn2IDOlIzInN34AXskvq9QicvCtEzq1Vzclu/tKF8Jq1Cg8JG2GL6/EmjgsCT7lXepE3g==, tarball: https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.29.7.tgz} engines: {node: '>=6.9.0'} '@babel/helper-module-imports@8.0.0': - resolution: {integrity: sha512-NZ7mSS93o4ndX4KrbD7W8Sf3QT8Qe24PrnFyUcuOPDzK6faqDFKjY9RG7he7+I7FdiQ4llpnosFqzrXa+Vy3Ew==} + resolution: {integrity: sha512-NZ7mSS93o4ndX4KrbD7W8Sf3QT8Qe24PrnFyUcuOPDzK6faqDFKjY9RG7he7+I7FdiQ4llpnosFqzrXa+Vy3Ew==, tarball: https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-8.0.0.tgz} engines: {node: ^22.18.0 || >=24.11.0} '@babel/helper-module-transforms@7.29.7': - resolution: {integrity: sha512-UPUVSyXbOh627KiCIGQSgwWzGeBKLkaJ9PJEdrngIwMSzxLR4jS4+f1f1jb7VzBbg8nFLaYotvVPFCTqdrmTAg==} + resolution: {integrity: sha512-UPUVSyXbOh627KiCIGQSgwWzGeBKLkaJ9PJEdrngIwMSzxLR4jS4+f1f1jb7VzBbg8nFLaYotvVPFCTqdrmTAg==, tarball: https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.29.7.tgz} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 '@babel/helper-module-transforms@8.0.1': - resolution: {integrity: sha512-UgAhl1kqiW5ciE0yCXqqvnb4H2n3IELJ7lIIQRezwDPilPEZX5i+Rvbja9MFTkwUn2biEiSMeV31aUzR4Lwakw==} + resolution: {integrity: sha512-UgAhl1kqiW5ciE0yCXqqvnb4H2n3IELJ7lIIQRezwDPilPEZX5i+Rvbja9MFTkwUn2biEiSMeV31aUzR4Lwakw==, tarball: https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-8.0.1.tgz} engines: {node: ^22.18.0 || >=24.11.0} peerDependencies: '@babel/core': ^8.0.0 '@babel/helper-optimise-call-expression@7.29.7': - resolution: {integrity: sha512-+kmGVjcT9RGYzoDwdwEqEvGgKe3BYq+O1iGzjFubaNgZHwYHP6lsF2Yghf4kEuv9BV7tYDZ913aBW9am6YKong==} + resolution: {integrity: sha512-+kmGVjcT9RGYzoDwdwEqEvGgKe3BYq+O1iGzjFubaNgZHwYHP6lsF2Yghf4kEuv9BV7tYDZ913aBW9am6YKong==, tarball: https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.29.7.tgz} engines: {node: '>=6.9.0'} '@babel/helper-optimise-call-expression@8.0.0': - resolution: {integrity: sha512-3W6satvtPuCUkUx63S2jMoW9EQNYkADgs1HTfufmL7gCmAulHMKupA/12WNz4A0GMMFn/YnWWwqOT9IZrJHQjg==} + resolution: {integrity: sha512-3W6satvtPuCUkUx63S2jMoW9EQNYkADgs1HTfufmL7gCmAulHMKupA/12WNz4A0GMMFn/YnWWwqOT9IZrJHQjg==, tarball: https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-8.0.0.tgz} engines: {node: ^22.18.0 || >=24.11.0} '@babel/helper-plugin-utils@7.29.7': - resolution: {integrity: sha512-G7sHYigPY17oO5SYWnfD/0MTBwVR781S/JI643e/JhUYgVgWE/61SoW3NH9KWUKyKq5LVh3npif99Wkt6j86Jw==} + resolution: {integrity: sha512-G7sHYigPY17oO5SYWnfD/0MTBwVR781S/JI643e/JhUYgVgWE/61SoW3NH9KWUKyKq5LVh3npif99Wkt6j86Jw==, tarball: https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.29.7.tgz} engines: {node: '>=6.9.0'} '@babel/helper-plugin-utils@8.0.1': - resolution: {integrity: sha512-3PKFgjTyPlhFhorfP+SjKQxLViIL++zWjFOO4hGriYU+Bsm983DxEM1JmDRJVWXV0O9npu+xXRqz7Pbd3mh70g==} + resolution: {integrity: sha512-3PKFgjTyPlhFhorfP+SjKQxLViIL++zWjFOO4hGriYU+Bsm983DxEM1JmDRJVWXV0O9npu+xXRqz7Pbd3mh70g==, tarball: https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-8.0.1.tgz} engines: {node: ^22.18.0 || >=24.11.0} peerDependencies: '@babel/core': ^8.0.0 '@babel/helper-remap-async-to-generator@7.29.7': - resolution: {integrity: sha512-16AMiW26DbXWBbr3B8wNozKM0ydMLB892vaOaJW/fPJdnT8vJk5sdkQcU/isqUxyCE0cEoa8wZOcbgDuC4b6Og==} + resolution: {integrity: sha512-16AMiW26DbXWBbr3B8wNozKM0ydMLB892vaOaJW/fPJdnT8vJk5sdkQcU/isqUxyCE0cEoa8wZOcbgDuC4b6Og==, tarball: https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.29.7.tgz} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 '@babel/helper-remap-async-to-generator@8.0.1': - resolution: {integrity: sha512-baAKuLEMmu6BCSY3tuiU7qglM1qOZt6F1SrFScA241oNqksxkxfEZEKztlGRmoVns9AQ5UgArH7RsUEjxWnzgQ==} + resolution: {integrity: sha512-baAKuLEMmu6BCSY3tuiU7qglM1qOZt6F1SrFScA241oNqksxkxfEZEKztlGRmoVns9AQ5UgArH7RsUEjxWnzgQ==, tarball: https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-8.0.1.tgz} engines: {node: ^22.18.0 || >=24.11.0} peerDependencies: '@babel/core': ^8.0.0 '@babel/helper-replace-supers@7.29.7': - resolution: {integrity: sha512-atfGXWSeCiF4DnKZIfmJfQRkSw9b9gNNXR1kqKjbhG4pGYCOnkp8OcTB8E3NXjBu8NpheSnOeNKz8KT7UNFTmQ==} + resolution: {integrity: sha512-atfGXWSeCiF4DnKZIfmJfQRkSw9b9gNNXR1kqKjbhG4pGYCOnkp8OcTB8E3NXjBu8NpheSnOeNKz8KT7UNFTmQ==, tarball: https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.29.7.tgz} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 '@babel/helper-replace-supers@8.0.1': - resolution: {integrity: sha512-B1SZADIcy3tmH8CmWvj4SHi/oAPom4UL3uknTc2QRNsPVLFk/sPnZvQL/8kj7Y5omvjMqie0vklvs6XM4OLW5Q==} + resolution: {integrity: sha512-B1SZADIcy3tmH8CmWvj4SHi/oAPom4UL3uknTc2QRNsPVLFk/sPnZvQL/8kj7Y5omvjMqie0vklvs6XM4OLW5Q==, tarball: https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-8.0.1.tgz} engines: {node: ^22.18.0 || >=24.11.0} peerDependencies: '@babel/core': ^8.0.0 '@babel/helper-skip-transparent-expression-wrappers@7.29.7': - resolution: {integrity: sha512-brcMGQaVzIeUb+6/bs1Av0f8YuNNjKY2JyvfRCsFuFsdKccEQ5Ges2y74D74NZ1Rz8lKJ9ksJkfqwQFJ/iNEyQ==} + resolution: {integrity: sha512-brcMGQaVzIeUb+6/bs1Av0f8YuNNjKY2JyvfRCsFuFsdKccEQ5Ges2y74D74NZ1Rz8lKJ9ksJkfqwQFJ/iNEyQ==, tarball: https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.29.7.tgz} engines: {node: '>=6.9.0'} '@babel/helper-skip-transparent-expression-wrappers@8.0.0': - resolution: {integrity: sha512-xmCA9kP3IhySsqhzwIdWGlDN/1A4cCKNBO/uwZx/3YzmDoMePwno2Q5/Bq0q+tYaKbeF940YiKV/kaW8Mzvpjw==} + resolution: {integrity: sha512-xmCA9kP3IhySsqhzwIdWGlDN/1A4cCKNBO/uwZx/3YzmDoMePwno2Q5/Bq0q+tYaKbeF940YiKV/kaW8Mzvpjw==, tarball: https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-8.0.0.tgz} engines: {node: ^22.18.0 || >=24.11.0} '@babel/helper-split-export-declaration@7.24.7': - resolution: {integrity: sha512-oy5V7pD+UvfkEATUKvIjvIAH/xCzfsFVw7ygW2SI6NClZzquT+mwdTfgfdbUiceh6iQO0CHtCPsyze/MZ2YbAA==} + resolution: {integrity: sha512-oy5V7pD+UvfkEATUKvIjvIAH/xCzfsFVw7ygW2SI6NClZzquT+mwdTfgfdbUiceh6iQO0CHtCPsyze/MZ2YbAA==, tarball: https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.24.7.tgz} engines: {node: '>=6.9.0'} '@babel/helper-string-parser@7.29.7': - resolution: {integrity: sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==} + resolution: {integrity: sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==, tarball: https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.29.7.tgz} engines: {node: '>=6.9.0'} '@babel/helper-string-parser@8.0.0': - resolution: {integrity: sha512-6mJgmFFFIIO82vvoLt9XtRC7/TkzXfts1t/SpRX4IHSzMgqoPYCWesVu1udUPUWioAE/2fcG6WuI8zrkE1gwrg==} + resolution: {integrity: sha512-6mJgmFFFIIO82vvoLt9XtRC7/TkzXfts1t/SpRX4IHSzMgqoPYCWesVu1udUPUWioAE/2fcG6WuI8zrkE1gwrg==, tarball: https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-8.0.0.tgz} engines: {node: ^22.18.0 || >=24.11.0} '@babel/helper-validator-identifier@7.29.7': - resolution: {integrity: sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==} + resolution: {integrity: sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==, tarball: https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.29.7.tgz} engines: {node: '>=6.9.0'} '@babel/helper-validator-identifier@8.0.4': - resolution: {integrity: sha512-4wFaiLd0bVo4cIoTXI3zKI038NIWE/cr3jvBjejOVYVxV/m8Ltav1USiGzG1fmS5J2RhgEOgXNNK46cRPnRsrg==} + resolution: {integrity: sha512-4wFaiLd0bVo4cIoTXI3zKI038NIWE/cr3jvBjejOVYVxV/m8Ltav1USiGzG1fmS5J2RhgEOgXNNK46cRPnRsrg==, tarball: https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-8.0.4.tgz} engines: {node: ^22.18.0 || >=24.11.0} '@babel/helper-validator-option@7.29.7': - resolution: {integrity: sha512-N9ZErrD+yW5geCDtBqnOoxmR8+tNKiGuxKlDpuJxfsqpa2dFcexaziGAE/qoHLiDDreVNMupxGmSoNlyvsA3gw==} + resolution: {integrity: sha512-N9ZErrD+yW5geCDtBqnOoxmR8+tNKiGuxKlDpuJxfsqpa2dFcexaziGAE/qoHLiDDreVNMupxGmSoNlyvsA3gw==, tarball: https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.29.7.tgz} engines: {node: '>=6.9.0'} '@babel/helper-validator-option@8.0.0': - resolution: {integrity: sha512-U4Dybxh4WESWHt5XhBeExi4DrY0/DNK1aHpQbsrQXCUbFHuMweT0TpLEWKvaraV2Y6fS+ZXunsZ8zIuZIgvF2Q==} + resolution: {integrity: sha512-U4Dybxh4WESWHt5XhBeExi4DrY0/DNK1aHpQbsrQXCUbFHuMweT0TpLEWKvaraV2Y6fS+ZXunsZ8zIuZIgvF2Q==, tarball: https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-8.0.0.tgz} engines: {node: ^22.18.0 || >=24.11.0} '@babel/helper-wrap-function@7.29.7': - resolution: {integrity: sha512-iES0Skag9ERIF68aXadpO6dbXa03mNWK3sEqJaMnLNs/eC3l0lkImdfoy6Y09/SfkpawdAB4RjQ7PVA7TcVGdw==} + resolution: {integrity: sha512-iES0Skag9ERIF68aXadpO6dbXa03mNWK3sEqJaMnLNs/eC3l0lkImdfoy6Y09/SfkpawdAB4RjQ7PVA7TcVGdw==, tarball: https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.29.7.tgz} engines: {node: '>=6.9.0'} '@babel/helper-wrap-function@8.0.0': - resolution: {integrity: sha512-Qpm8+wi5xfDkBfollanwriCcKniFfBmMmaKB01GVM6VGzKXo1fdxosZp04qEr5HM+LKhwr3hG1yRy8+ORsficA==} + resolution: {integrity: sha512-Qpm8+wi5xfDkBfollanwriCcKniFfBmMmaKB01GVM6VGzKXo1fdxosZp04qEr5HM+LKhwr3hG1yRy8+ORsficA==, tarball: https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-8.0.0.tgz} engines: {node: ^22.18.0 || >=24.11.0} '@babel/helpers@7.29.7': - resolution: {integrity: sha512-1k2lAGRMfHTcwuNYcCNUmaUffmQv8KWMfh2iJUUeRlwlwH4FdNG7mfPI10NPfLHJFThE4Tyr4mv7kTNZOiPuBg==} + resolution: {integrity: sha512-1k2lAGRMfHTcwuNYcCNUmaUffmQv8KWMfh2iJUUeRlwlwH4FdNG7mfPI10NPfLHJFThE4Tyr4mv7kTNZOiPuBg==, tarball: https://registry.npmjs.org/@babel/helpers/-/helpers-7.29.7.tgz} engines: {node: '>=6.9.0'} '@babel/helpers@8.0.0': - resolution: {integrity: sha512-wfbi91pM3py96oIiJEz7qIpyXDytgr9zQC1HEWwlGNVRAEmItuU/0a41ZUKu1sJGyhhOIpc4t5vk4PYzt8wpsg==} + resolution: {integrity: sha512-wfbi91pM3py96oIiJEz7qIpyXDytgr9zQC1HEWwlGNVRAEmItuU/0a41ZUKu1sJGyhhOIpc4t5vk4PYzt8wpsg==, tarball: https://registry.npmjs.org/@babel/helpers/-/helpers-8.0.0.tgz} engines: {node: ^22.18.0 || >=24.11.0} '@babel/parser@7.29.7': - resolution: {integrity: sha512-hnORnjP/1P/zFEndoeX+n+t1RwWRJiJpM/jO7FW32Kn9r5+sJB2JWOdYo4L6k78j15eCwY3Gm/7364B1EMwtNg==} + resolution: {integrity: sha512-hnORnjP/1P/zFEndoeX+n+t1RwWRJiJpM/jO7FW32Kn9r5+sJB2JWOdYo4L6k78j15eCwY3Gm/7364B1EMwtNg==, tarball: https://registry.npmjs.org/@babel/parser/-/parser-7.29.7.tgz} engines: {node: '>=6.0.0'} hasBin: true '@babel/parser@8.0.4': - resolution: {integrity: sha512-srpptsAkEbbNIC/q8nT7o+m6CQe8CJUTV/t7MYc9NnWlgYVtHOb7JH6SorxMhN0kuRJjVqXbKClG6xSbPtzz+g==} + resolution: {integrity: sha512-srpptsAkEbbNIC/q8nT7o+m6CQe8CJUTV/t7MYc9NnWlgYVtHOb7JH6SorxMhN0kuRJjVqXbKClG6xSbPtzz+g==, tarball: https://registry.npmjs.org/@babel/parser/-/parser-8.0.4.tgz} engines: {node: ^22.18.0 || >=24.11.0} hasBin: true '@babel/plugin-bugfix-firefox-class-in-computed-class-key@7.29.7': - resolution: {integrity: sha512-j8SrR0zLZrRsC09DlszEx8FpMiwukKffYXMK0d5LmOglO7vGG6sz/BR/20yHqWH+Lnn31JTt2PE3hIWNgM2J6w==} + resolution: {integrity: sha512-j8SrR0zLZrRsC09DlszEx8FpMiwukKffYXMK0d5LmOglO7vGG6sz/BR/20yHqWH+Lnn31JTt2PE3hIWNgM2J6w==, tarball: https://registry.npmjs.org/@babel/plugin-bugfix-firefox-class-in-computed-class-key/-/plugin-bugfix-firefox-class-in-computed-class-key-7.29.7.tgz} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 '@babel/plugin-bugfix-firefox-class-in-computed-class-key@8.0.1': - resolution: {integrity: sha512-Ytgjjne4RnG3Oig7ik+NfY4ebRY30BPptVkkyu1f72eINJXRM3/bkU++tIc5aPvyLmo4KH20avq0xJ2o+9aEnw==} + resolution: {integrity: sha512-Ytgjjne4RnG3Oig7ik+NfY4ebRY30BPptVkkyu1f72eINJXRM3/bkU++tIc5aPvyLmo4KH20avq0xJ2o+9aEnw==, tarball: https://registry.npmjs.org/@babel/plugin-bugfix-firefox-class-in-computed-class-key/-/plugin-bugfix-firefox-class-in-computed-class-key-8.0.1.tgz} engines: {node: ^22.18.0 || >=24.11.0} peerDependencies: '@babel/core': ^8.0.0 '@babel/plugin-bugfix-safari-class-field-initializer-scope@7.29.7': - resolution: {integrity: sha512-r8j8escF+U2FUHo0KOhPUdMzUO+jp9fInva6+ACVAF3Y97Ev+5iNZwiqTghmzNeWwDkOPlYuTcfb1vDaoZKmAQ==} + resolution: {integrity: sha512-r8j8escF+U2FUHo0KOhPUdMzUO+jp9fInva6+ACVAF3Y97Ev+5iNZwiqTghmzNeWwDkOPlYuTcfb1vDaoZKmAQ==, tarball: https://registry.npmjs.org/@babel/plugin-bugfix-safari-class-field-initializer-scope/-/plugin-bugfix-safari-class-field-initializer-scope-7.29.7.tgz} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 '@babel/plugin-bugfix-safari-class-field-initializer-scope@8.0.1': - resolution: {integrity: sha512-X7pAMBhuKluA7UfwZNvKN0XVVu/AGeo84Z75eJl85rcb8J2aBzLK92btahM1X5h0oi0QIrbe0qIMA/0+4Buk7w==} + resolution: {integrity: sha512-X7pAMBhuKluA7UfwZNvKN0XVVu/AGeo84Z75eJl85rcb8J2aBzLK92btahM1X5h0oi0QIrbe0qIMA/0+4Buk7w==, tarball: https://registry.npmjs.org/@babel/plugin-bugfix-safari-class-field-initializer-scope/-/plugin-bugfix-safari-class-field-initializer-scope-8.0.1.tgz} engines: {node: ^22.18.0 || >=24.11.0} peerDependencies: '@babel/core': ^8.0.0 '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@7.29.7': - resolution: {integrity: sha512-GE1TFSiuFeGsCxmYXZl8HwoPrVlwe4rHPFE8weieGKZqnDORK+Ar3vgWMgW+AOxQ6/2TgLSKx9p6W7O4rC6qgQ==} + resolution: {integrity: sha512-GE1TFSiuFeGsCxmYXZl8HwoPrVlwe4rHPFE8weieGKZqnDORK+Ar3vgWMgW+AOxQ6/2TgLSKx9p6W7O4rC6qgQ==, tarball: https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.29.7.tgz} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@8.0.1': - resolution: {integrity: sha512-DJviKTxYfH0hFwnMiW4dnPyMGzS3Hrr4zUfXl1zwQ0QiGlGlNYklLoPSYEQr8S7nau0/K7NdQjTh0qbYuyFjCA==} + resolution: {integrity: sha512-DJviKTxYfH0hFwnMiW4dnPyMGzS3Hrr4zUfXl1zwQ0QiGlGlNYklLoPSYEQr8S7nau0/K7NdQjTh0qbYuyFjCA==, tarball: https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-8.0.1.tgz} engines: {node: ^22.18.0 || >=24.11.0} peerDependencies: '@babel/core': ^8.0.0 '@babel/plugin-bugfix-safari-rest-destructuring-rhs-array@7.29.7': - resolution: {integrity: sha512-oBNVCvnO5tND+xSopWvV8WNGfpTfgP4Zr/YXXSj8zfmcPktp5Ku/aZlsIowgSD4fjmgHn6sGmB9APVsU5zOdhA==} + resolution: {integrity: sha512-oBNVCvnO5tND+xSopWvV8WNGfpTfgP4Zr/YXXSj8zfmcPktp5Ku/aZlsIowgSD4fjmgHn6sGmB9APVsU5zOdhA==, tarball: https://registry.npmjs.org/@babel/plugin-bugfix-safari-rest-destructuring-rhs-array/-/plugin-bugfix-safari-rest-destructuring-rhs-array-7.29.7.tgz} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 '@babel/plugin-bugfix-safari-rest-destructuring-rhs-array@8.0.1': - resolution: {integrity: sha512-DmR/N+B9+4PbURFj4+zdnWj49/PFAnK2bn8+E4ZAmwn3J5QCxnbG7Ep6aRfz9M8Aw+rBro0kIJQycvzFpl4buQ==} + resolution: {integrity: sha512-DmR/N+B9+4PbURFj4+zdnWj49/PFAnK2bn8+E4ZAmwn3J5QCxnbG7Ep6aRfz9M8Aw+rBro0kIJQycvzFpl4buQ==, tarball: https://registry.npmjs.org/@babel/plugin-bugfix-safari-rest-destructuring-rhs-array/-/plugin-bugfix-safari-rest-destructuring-rhs-array-8.0.1.tgz} engines: {node: ^22.18.0 || >=24.11.0} peerDependencies: '@babel/core': ^8.0.0 '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@7.29.7': - resolution: {integrity: sha512-QQt9qKHZ2sg/kivaLr7lnQr8HVrQDdBNSfCsTjiDxRuX/K5ORyKq+Bu8Xr0cDE3Dfkv0cw28Ve0EKyKMvulkOw==} + resolution: {integrity: sha512-QQt9qKHZ2sg/kivaLr7lnQr8HVrQDdBNSfCsTjiDxRuX/K5ORyKq+Bu8Xr0cDE3Dfkv0cw28Ve0EKyKMvulkOw==, tarball: https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.29.7.tgz} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.13.0 '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@8.0.1': - resolution: {integrity: sha512-x8bi0LFVD2xkULjfNn+hCMg16yAFHAM9fS/ThSFeYBi+0MP9K6qcY2BZb4urUwC7PYtEy5wPe6TKjOEjXrCGFA==} + resolution: {integrity: sha512-x8bi0LFVD2xkULjfNn+hCMg16yAFHAM9fS/ThSFeYBi+0MP9K6qcY2BZb4urUwC7PYtEy5wPe6TKjOEjXrCGFA==, tarball: https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-8.0.1.tgz} engines: {node: ^22.18.0 || >=24.11.0} peerDependencies: '@babel/core': ^8.0.0 '@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly@7.29.7': - resolution: {integrity: sha512-pn6QacGLgvCcwc+syUhKE/qSjV2D1IHDB84RNxWYSt1mW3K/SCtjinZ2p0cETJxAWBjPy3K/1lHwG5BjjPxNlw==} + resolution: {integrity: sha512-pn6QacGLgvCcwc+syUhKE/qSjV2D1IHDB84RNxWYSt1mW3K/SCtjinZ2p0cETJxAWBjPy3K/1lHwG5BjjPxNlw==, tarball: https://registry.npmjs.org/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/-/plugin-bugfix-v8-static-class-fields-redefine-readonly-7.29.7.tgz} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 '@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly@8.0.1': - resolution: {integrity: sha512-P8+RN2n7ts2s1vnE+lXdHYf+dhnmcGSen/kWzBsVluT9Sey5AqmcRXYWlHqgQxaNlKTD5YMa1tf5z4d1v8W88w==} + resolution: {integrity: sha512-P8+RN2n7ts2s1vnE+lXdHYf+dhnmcGSen/kWzBsVluT9Sey5AqmcRXYWlHqgQxaNlKTD5YMa1tf5z4d1v8W88w==, tarball: https://registry.npmjs.org/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/-/plugin-bugfix-v8-static-class-fields-redefine-readonly-8.0.1.tgz} engines: {node: ^22.18.0 || >=24.11.0} peerDependencies: '@babel/core': ^8.0.0 '@babel/plugin-proposal-class-properties@7.18.6': - resolution: {integrity: sha512-cumfXOF0+nzZrrN8Rf0t7M+tF6sZc7vhQwYQck9q1/5w2OExlD+b4v4RpMJFaV1Z7WcDRgO6FqvxqxGlwo+RHQ==} + resolution: {integrity: sha512-cumfXOF0+nzZrrN8Rf0t7M+tF6sZc7vhQwYQck9q1/5w2OExlD+b4v4RpMJFaV1Z7WcDRgO6FqvxqxGlwo+RHQ==, tarball: https://registry.npmjs.org/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.18.6.tgz} 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-decorators@7.29.7': - resolution: {integrity: sha512-EtU0Hi3GvrTqD56xKmZvV/uCXK2ZbwVNPNLAquVItcAZpUhkXwWlo3Fmj0c2LxgSf2I8IDULeAepwNP1OefLXg==} + resolution: {integrity: sha512-EtU0Hi3GvrTqD56xKmZvV/uCXK2ZbwVNPNLAquVItcAZpUhkXwWlo3Fmj0c2LxgSf2I8IDULeAepwNP1OefLXg==, tarball: https://registry.npmjs.org/@babel/plugin-proposal-decorators/-/plugin-proposal-decorators-7.29.7.tgz} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 '@babel/plugin-proposal-private-methods@7.18.6': - resolution: {integrity: sha512-nutsvktDItsNn4rpGItSNV2sz1XwS+nfU0Rg8aCx3W3NOKVzdMjJRu0O5OkgDp3ZGICSTbgRpxZoWsxoKRvbeA==} + resolution: {integrity: sha512-nutsvktDItsNn4rpGItSNV2sz1XwS+nfU0Rg8aCx3W3NOKVzdMjJRu0O5OkgDp3ZGICSTbgRpxZoWsxoKRvbeA==, tarball: https://registry.npmjs.org/@babel/plugin-proposal-private-methods/-/plugin-proposal-private-methods-7.18.6.tgz} 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.0-placeholder-for-preset-env.2': - resolution: {integrity: sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w==} + resolution: {integrity: sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w==, tarball: https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0-placeholder-for-preset-env.2.tgz} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 '@babel/plugin-proposal-private-property-in-object@7.21.11': - resolution: {integrity: sha512-0QZ8qP/3RLDVBwBFoWAwCtgcDZJVwA5LUJRZU8x2YFfKNuFq161wK3cuGrALu5yiPu+vzwTAg/sMWVNeWeNyaw==} + resolution: {integrity: sha512-0QZ8qP/3RLDVBwBFoWAwCtgcDZJVwA5LUJRZU8x2YFfKNuFq161wK3cuGrALu5yiPu+vzwTAg/sMWVNeWeNyaw==, tarball: https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.11.tgz} 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-syntax-async-generators@7.8.4': - resolution: {integrity: sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==} + resolution: {integrity: sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==, tarball: https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz} peerDependencies: '@babel/core': ^7.0.0-0 '@babel/plugin-syntax-bigint@7.8.3': - resolution: {integrity: sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==} + resolution: {integrity: sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==, tarball: https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz} peerDependencies: '@babel/core': ^7.0.0-0 '@babel/plugin-syntax-class-properties@7.12.13': - resolution: {integrity: sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==} + resolution: {integrity: sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==, tarball: https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz} peerDependencies: '@babel/core': ^7.0.0-0 '@babel/plugin-syntax-class-static-block@7.14.5': - resolution: {integrity: sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==} + resolution: {integrity: sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==, tarball: https://registry.npmjs.org/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 '@babel/plugin-syntax-decorators@7.29.7': - resolution: {integrity: sha512-9MTTLbF39X6sqM92JPEsoI7++26hjZvzkxKZy64aMhWLH2mPkJ/Q3AV4QLmls3R14FpSpkOwQQfUh962JGQxxg==} + resolution: {integrity: sha512-9MTTLbF39X6sqM92JPEsoI7++26hjZvzkxKZy64aMhWLH2mPkJ/Q3AV4QLmls3R14FpSpkOwQQfUh962JGQxxg==, tarball: https://registry.npmjs.org/@babel/plugin-syntax-decorators/-/plugin-syntax-decorators-7.29.7.tgz} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 '@babel/plugin-syntax-import-assertions@7.29.7': - resolution: {integrity: sha512-/An1OCBN93thpBAGyfsK2pcf0jvju1SAtKkL2Ny++B5Sy6sqgzXDQH1cZxWbF96Wuk+bn41MDA9bLd4VVAw6rw==} + resolution: {integrity: sha512-/An1OCBN93thpBAGyfsK2pcf0jvju1SAtKkL2Ny++B5Sy6sqgzXDQH1cZxWbF96Wuk+bn41MDA9bLd4VVAw6rw==, tarball: https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.29.7.tgz} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 '@babel/plugin-syntax-import-attributes@7.29.7': - resolution: {integrity: sha512-zGYcYfq/WmZ4V+kBIXQon9dSSc8ircGZqw9ZaNhhGj9nZkeBu1jHLBDQqYYi5WA9uawvA2sIMbry2nCFhf5Djg==} + resolution: {integrity: sha512-zGYcYfq/WmZ4V+kBIXQon9dSSc8ircGZqw9ZaNhhGj9nZkeBu1jHLBDQqYYi5WA9uawvA2sIMbry2nCFhf5Djg==, tarball: https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.29.7.tgz} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 '@babel/plugin-syntax-import-meta@7.10.4': - resolution: {integrity: sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==} + resolution: {integrity: sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==, tarball: https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz} peerDependencies: '@babel/core': ^7.0.0-0 '@babel/plugin-syntax-json-strings@7.8.3': - resolution: {integrity: sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==} + resolution: {integrity: sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==, tarball: https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz} peerDependencies: '@babel/core': ^7.0.0-0 '@babel/plugin-syntax-jsx@7.29.7': - resolution: {integrity: sha512-TSu8+mHCoEaaCDEZ0I3+6mvTBYR4PCxQwf2z9/r5Tbztv6NaLR3B9thGTTxX2WGuGHJqRiAbKPeGTJ5XWXVg6A==} + resolution: {integrity: sha512-TSu8+mHCoEaaCDEZ0I3+6mvTBYR4PCxQwf2z9/r5Tbztv6NaLR3B9thGTTxX2WGuGHJqRiAbKPeGTJ5XWXVg6A==, tarball: https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.29.7.tgz} 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==} + resolution: {integrity: sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==, tarball: https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz} peerDependencies: '@babel/core': ^7.0.0-0 '@babel/plugin-syntax-nullish-coalescing-operator@7.8.3': - resolution: {integrity: sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==} + resolution: {integrity: sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==, tarball: https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz} peerDependencies: '@babel/core': ^7.0.0-0 '@babel/plugin-syntax-numeric-separator@7.10.4': - resolution: {integrity: sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==} + resolution: {integrity: sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==, tarball: https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz} peerDependencies: '@babel/core': ^7.0.0-0 '@babel/plugin-syntax-object-rest-spread@7.8.3': - resolution: {integrity: sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==} + resolution: {integrity: sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==, tarball: https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz} peerDependencies: '@babel/core': ^7.0.0-0 '@babel/plugin-syntax-optional-catch-binding@7.8.3': - resolution: {integrity: sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==} + resolution: {integrity: sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==, tarball: https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz} peerDependencies: '@babel/core': ^7.0.0-0 '@babel/plugin-syntax-optional-chaining@7.8.3': - resolution: {integrity: sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==} + resolution: {integrity: sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==, tarball: https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz} peerDependencies: '@babel/core': ^7.0.0-0 '@babel/plugin-syntax-private-property-in-object@7.14.5': - resolution: {integrity: sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==} + resolution: {integrity: sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==, tarball: https://registry.npmjs.org/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz} 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==} + resolution: {integrity: sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==, tarball: https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 '@babel/plugin-syntax-typescript@7.29.7': - resolution: {integrity: sha512-ngr+82Sh0xMz25TPCZi+nC2iTzjfCdWS2ONXTp/PtSCHCgaCNBpdMqgvJ2ccdLlClVZ7sisIgB914j/JFe+RZA==} + resolution: {integrity: sha512-ngr+82Sh0xMz25TPCZi+nC2iTzjfCdWS2ONXTp/PtSCHCgaCNBpdMqgvJ2ccdLlClVZ7sisIgB914j/JFe+RZA==, tarball: https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.29.7.tgz} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 '@babel/plugin-syntax-unicode-sets-regex@7.18.6': - resolution: {integrity: sha512-727YkEAPwSIQTv5im8QHz3upqp92JTWhidIC81Tdx4VJYIte/VndKf1qKrfnnhPLiPghStWfvC/iFaMCQu7Nqg==} + resolution: {integrity: sha512-727YkEAPwSIQTv5im8QHz3upqp92JTWhidIC81Tdx4VJYIte/VndKf1qKrfnnhPLiPghStWfvC/iFaMCQu7Nqg==, tarball: https://registry.npmjs.org/@babel/plugin-syntax-unicode-sets-regex/-/plugin-syntax-unicode-sets-regex-7.18.6.tgz} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 '@babel/plugin-transform-arrow-functions@7.29.7': - resolution: {integrity: sha512-N7zArUXWzAMzm+/N0uPBeVB3Fam5lMxtUwMmDK5f/IBBS7a7p1qeUoxd/6CckXoxUdgsntq1Dh8xNW06maZbDQ==} + resolution: {integrity: sha512-N7zArUXWzAMzm+/N0uPBeVB3Fam5lMxtUwMmDK5f/IBBS7a7p1qeUoxd/6CckXoxUdgsntq1Dh8xNW06maZbDQ==, tarball: https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.29.7.tgz} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 '@babel/plugin-transform-arrow-functions@8.0.1': - resolution: {integrity: sha512-o/gr7kRlq3PKLLuYth4udOsrC7geBerti+QtwPeyxMOsEQO1d8kDHqk9r2PtMx2y9i8FG7tzyTerfv1yMLSMsQ==} + resolution: {integrity: sha512-o/gr7kRlq3PKLLuYth4udOsrC7geBerti+QtwPeyxMOsEQO1d8kDHqk9r2PtMx2y9i8FG7tzyTerfv1yMLSMsQ==, tarball: https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-8.0.1.tgz} engines: {node: ^22.18.0 || >=24.11.0} peerDependencies: '@babel/core': ^8.0.0 '@babel/plugin-transform-async-generator-functions@7.29.7': - resolution: {integrity: sha512-d98gXZkgswvkyohMBABkhm3GeXhYj8psWfwQ2C7gtfrKGTykQa/iOIi+JJhwMjPlZ6Vm2XN+DCf3Es1EoG4ZLA==} + resolution: {integrity: sha512-d98gXZkgswvkyohMBABkhm3GeXhYj8psWfwQ2C7gtfrKGTykQa/iOIi+JJhwMjPlZ6Vm2XN+DCf3Es1EoG4ZLA==, tarball: https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.29.7.tgz} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 '@babel/plugin-transform-async-generator-functions@8.0.1': - resolution: {integrity: sha512-kqnSMF1YHBzuiQrl68675i5Ma1oljvo+SJsNEZFZVBu5BUrVIZm9KId3ui2PdtLK2sv2zM8sJnjPDfgLxQlEqQ==} + resolution: {integrity: sha512-kqnSMF1YHBzuiQrl68675i5Ma1oljvo+SJsNEZFZVBu5BUrVIZm9KId3ui2PdtLK2sv2zM8sJnjPDfgLxQlEqQ==, tarball: https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-8.0.1.tgz} engines: {node: ^22.18.0 || >=24.11.0} peerDependencies: '@babel/core': ^8.0.0 '@babel/plugin-transform-async-to-generator@7.29.7': - resolution: {integrity: sha512-pcUb2SS+RMo9TWVBwKGI5ShtoG7R+zBsFmCKDa6fe8c+hPr3XJlZgoE5j6i8W7gDjhyvy+85vmYexanvXh3d1w==} + resolution: {integrity: sha512-pcUb2SS+RMo9TWVBwKGI5ShtoG7R+zBsFmCKDa6fe8c+hPr3XJlZgoE5j6i8W7gDjhyvy+85vmYexanvXh3d1w==, tarball: https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.29.7.tgz} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 '@babel/plugin-transform-async-to-generator@8.0.1': - resolution: {integrity: sha512-e1jmmEU4p2Lx64sA1+EF8e8/RxPuegzbXcEbmFp5alDyLE+f2ViUpZ77bRWMXzihTwgVVmn/TOpqDbAuS5g1Ew==} + resolution: {integrity: sha512-e1jmmEU4p2Lx64sA1+EF8e8/RxPuegzbXcEbmFp5alDyLE+f2ViUpZ77bRWMXzihTwgVVmn/TOpqDbAuS5g1Ew==, tarball: https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-8.0.1.tgz} engines: {node: ^22.18.0 || >=24.11.0} peerDependencies: '@babel/core': ^8.0.0 '@babel/plugin-transform-block-scoped-functions@7.29.7': - resolution: {integrity: sha512-cUSmjh72N+rN4PrkFlN1dJwNCwjVp5d38/CQrEsFggkD10UiFlBFgdH3tv5dNsLuHY+3S8db2xCHjhZcv5WgvA==} + resolution: {integrity: sha512-cUSmjh72N+rN4PrkFlN1dJwNCwjVp5d38/CQrEsFggkD10UiFlBFgdH3tv5dNsLuHY+3S8db2xCHjhZcv5WgvA==, tarball: https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.29.7.tgz} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 '@babel/plugin-transform-block-scoped-functions@8.0.1': - resolution: {integrity: sha512-0V97/gcf7LIgPieEiK1YT0eXa18XJFSLOTZjzEZhA9SJIqZhD/IwGUrCitBzXSmnGCP7hchwC6svHtJ/Eidcpg==} + resolution: {integrity: sha512-0V97/gcf7LIgPieEiK1YT0eXa18XJFSLOTZjzEZhA9SJIqZhD/IwGUrCitBzXSmnGCP7hchwC6svHtJ/Eidcpg==, tarball: https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-8.0.1.tgz} engines: {node: ^22.18.0 || >=24.11.0} peerDependencies: '@babel/core': ^8.0.0 '@babel/plugin-transform-block-scoping@7.29.7': - resolution: {integrity: sha512-ONyr4+AZhKh8yKWInVxU9AXA9EbsyeLcL6V0dJy6M2/62vuvpGm29zzuymbTpdc451GEpDIdAyPLP3r+P61yKQ==} + resolution: {integrity: sha512-ONyr4+AZhKh8yKWInVxU9AXA9EbsyeLcL6V0dJy6M2/62vuvpGm29zzuymbTpdc451GEpDIdAyPLP3r+P61yKQ==, tarball: https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.29.7.tgz} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 '@babel/plugin-transform-block-scoping@8.0.1': - resolution: {integrity: sha512-HxiQvKsSCs2jOmMhjDrooHaZYOy6W8bqwXp/zjdgPjsNrda6tK9/CH3a/cVIeg6ge3hSS02ALqvqgIo4rTsuSg==} + resolution: {integrity: sha512-HxiQvKsSCs2jOmMhjDrooHaZYOy6W8bqwXp/zjdgPjsNrda6tK9/CH3a/cVIeg6ge3hSS02ALqvqgIo4rTsuSg==, tarball: https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-8.0.1.tgz} engines: {node: ^22.18.0 || >=24.11.0} peerDependencies: '@babel/core': ^8.0.0 '@babel/plugin-transform-class-properties@7.29.7': - resolution: {integrity: sha512-GtcpjFvanPfzNQi3eTitsCqtRRmmqzpy/A+yhTR1HaZo1Ly3EA8ZXxlPyHdR8/IuRMYc3E4wdGBewB2QKQjAaA==} + resolution: {integrity: sha512-GtcpjFvanPfzNQi3eTitsCqtRRmmqzpy/A+yhTR1HaZo1Ly3EA8ZXxlPyHdR8/IuRMYc3E4wdGBewB2QKQjAaA==, tarball: https://registry.npmjs.org/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.29.7.tgz} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 '@babel/plugin-transform-class-properties@8.0.1': - resolution: {integrity: sha512-tORnYiVhIHnKj90TgbSZXrO24f9oEpA6MgFxpIDSKKlHv7AzBIRhkMlYevanueLNYaQXqZWarfCgXM4bWTfNiw==} + resolution: {integrity: sha512-tORnYiVhIHnKj90TgbSZXrO24f9oEpA6MgFxpIDSKKlHv7AzBIRhkMlYevanueLNYaQXqZWarfCgXM4bWTfNiw==, tarball: https://registry.npmjs.org/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-8.0.1.tgz} engines: {node: ^22.18.0 || >=24.11.0} peerDependencies: '@babel/core': ^8.0.0 '@babel/plugin-transform-class-static-block@7.29.7': - resolution: {integrity: sha512-kibJgmEdX2iMwsHY2tSZNDgj8PwIlCQz7FK9KuGKO8zsuoUwSEhoNnNVp/emKWrbY4HeO6kkXfdMqRKKKXBm2A==} + resolution: {integrity: sha512-kibJgmEdX2iMwsHY2tSZNDgj8PwIlCQz7FK9KuGKO8zsuoUwSEhoNnNVp/emKWrbY4HeO6kkXfdMqRKKKXBm2A==, tarball: https://registry.npmjs.org/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.29.7.tgz} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.12.0 '@babel/plugin-transform-class-static-block@8.0.1': - resolution: {integrity: sha512-NEVK+L0Le8h8tJ+IK0CGS5y9Yi1ZHxLj6M5PeanhMFuq9aSo0XI+Wtmbuyop6fTNukOm7ORNntf/kwid891vqQ==} + resolution: {integrity: sha512-NEVK+L0Le8h8tJ+IK0CGS5y9Yi1ZHxLj6M5PeanhMFuq9aSo0XI+Wtmbuyop6fTNukOm7ORNntf/kwid891vqQ==, tarball: https://registry.npmjs.org/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-8.0.1.tgz} engines: {node: ^22.18.0 || >=24.11.0} peerDependencies: '@babel/core': ^8.0.0 '@babel/plugin-transform-classes@7.29.7': - resolution: {integrity: sha512-qV0OGGBVacduzQHE649JyCneOFI/maT+YKsO+K4Yi3xv2wTPNjM/W2o2gdzMwEAZz7fXNTHAe0NcSg30bIN69g==} + resolution: {integrity: sha512-qV0OGGBVacduzQHE649JyCneOFI/maT+YKsO+K4Yi3xv2wTPNjM/W2o2gdzMwEAZz7fXNTHAe0NcSg30bIN69g==, tarball: https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.29.7.tgz} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 '@babel/plugin-transform-classes@8.0.1': - resolution: {integrity: sha512-phwyCES8kIMAdVOFw25ztmgAvkl2G+TvUv7azUYyrlR1Qoo3eLJC/MU3MGUKFZ4BWtsJ1NTJM1lKRLzKbswg7w==} + resolution: {integrity: sha512-phwyCES8kIMAdVOFw25ztmgAvkl2G+TvUv7azUYyrlR1Qoo3eLJC/MU3MGUKFZ4BWtsJ1NTJM1lKRLzKbswg7w==, tarball: https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-8.0.1.tgz} engines: {node: ^22.18.0 || >=24.11.0} peerDependencies: '@babel/core': ^8.0.0 '@babel/plugin-transform-computed-properties@7.29.7': - resolution: {integrity: sha512-RK7/IyU5phpuCdBAuig5VkzG/EnbDaui5SQGdU9BFrHdV+mV4cUjLMQ9lJDjLNtWHsqtiefpGZUXQP2BiTYMsA==} + resolution: {integrity: sha512-RK7/IyU5phpuCdBAuig5VkzG/EnbDaui5SQGdU9BFrHdV+mV4cUjLMQ9lJDjLNtWHsqtiefpGZUXQP2BiTYMsA==, tarball: https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.29.7.tgz} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 '@babel/plugin-transform-computed-properties@8.0.1': - resolution: {integrity: sha512-i4l3OGLO8DUDcwdnyraOvILbhqdUf4QgfzhVxSOSzRy49XKXrY7pwaSg9gDSKmhZfNPrEMciBSJSciQh/CjB1A==} + resolution: {integrity: sha512-i4l3OGLO8DUDcwdnyraOvILbhqdUf4QgfzhVxSOSzRy49XKXrY7pwaSg9gDSKmhZfNPrEMciBSJSciQh/CjB1A==, tarball: https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-8.0.1.tgz} engines: {node: ^22.18.0 || >=24.11.0} peerDependencies: '@babel/core': ^8.0.0 '@babel/plugin-transform-destructuring@7.29.7': - resolution: {integrity: sha512-iPX8aD6H9zV5s7ZsqTdNocPN/MGQ5sSMnElKrktxjJRMnB2jN/1p2+R7GkfD6CAYoVFqy5A4XnSIUeGgJzIWpg==} + resolution: {integrity: sha512-iPX8aD6H9zV5s7ZsqTdNocPN/MGQ5sSMnElKrktxjJRMnB2jN/1p2+R7GkfD6CAYoVFqy5A4XnSIUeGgJzIWpg==, tarball: https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.29.7.tgz} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 '@babel/plugin-transform-destructuring@8.0.1': - resolution: {integrity: sha512-RtR8uLDl0QcCmqMNIkM8gmDeYZ3rS0ZH+sa+I6sfc09yFoqfp9AEPgBstq9KyfVb0lFCVSRFfJXCI70FIl5ccw==} + resolution: {integrity: sha512-RtR8uLDl0QcCmqMNIkM8gmDeYZ3rS0ZH+sa+I6sfc09yFoqfp9AEPgBstq9KyfVb0lFCVSRFfJXCI70FIl5ccw==, tarball: https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-8.0.1.tgz} engines: {node: ^22.18.0 || >=24.11.0} peerDependencies: '@babel/core': ^8.0.0 '@babel/plugin-transform-dotall-regex@7.29.7': - resolution: {integrity: sha512-3qc18hsD2RdZiyJNDNc7HQpv6xbncwh8FYtxNFFzclSyh/trPD9KkVR9BDECUjDLvb7yJVF15GfYUuC+LMkkiQ==} + resolution: {integrity: sha512-3qc18hsD2RdZiyJNDNc7HQpv6xbncwh8FYtxNFFzclSyh/trPD9KkVR9BDECUjDLvb7yJVF15GfYUuC+LMkkiQ==, tarball: https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.29.7.tgz} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 '@babel/plugin-transform-dotall-regex@8.0.1': - resolution: {integrity: sha512-czOUoSaZljJ92yu+bYlXqb/UBN8K9daNCob/B6/7nthSvfGP6YhCnfqD64XWfyb2dN4ypxALNplApoJrsMd4fw==} + resolution: {integrity: sha512-czOUoSaZljJ92yu+bYlXqb/UBN8K9daNCob/B6/7nthSvfGP6YhCnfqD64XWfyb2dN4ypxALNplApoJrsMd4fw==, tarball: https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-8.0.1.tgz} engines: {node: ^22.18.0 || >=24.11.0} peerDependencies: '@babel/core': ^8.0.0 '@babel/plugin-transform-duplicate-keys@7.29.7': - resolution: {integrity: sha512-6IvRRriEMqnBwD6chtxdLpMYCHWEzN+oL5cyQtjykya19UgzbmKhxmhZgKC/LHxS2nYr9Q/qYPZ5Lr6jOL9+yQ==} + resolution: {integrity: sha512-6IvRRriEMqnBwD6chtxdLpMYCHWEzN+oL5cyQtjykya19UgzbmKhxmhZgKC/LHxS2nYr9Q/qYPZ5Lr6jOL9+yQ==, tarball: https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.29.7.tgz} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 '@babel/plugin-transform-duplicate-keys@8.0.1': - resolution: {integrity: sha512-kNnVLkxFUEcTtCyB5PFVQ5Xoy88Bk1lU/ZgDu97CW8eNhRH2Wsiy8Sq5l5dFnwtIUYjzsXHU77jUy1W5AtGSIw==} + resolution: {integrity: sha512-kNnVLkxFUEcTtCyB5PFVQ5Xoy88Bk1lU/ZgDu97CW8eNhRH2Wsiy8Sq5l5dFnwtIUYjzsXHU77jUy1W5AtGSIw==, tarball: https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-8.0.1.tgz} engines: {node: ^22.18.0 || >=24.11.0} peerDependencies: '@babel/core': ^8.0.0 '@babel/plugin-transform-duplicate-named-capturing-groups-regex@7.29.7': - resolution: {integrity: sha512-2wiIyo2BjtgU7HufSeDnL9L2O7zr8jmhFKuSr65VpRkUiRKRNpb0mdlk56+XPPKoIrfHqzbMuglDvZun0RISsA==} + resolution: {integrity: sha512-2wiIyo2BjtgU7HufSeDnL9L2O7zr8jmhFKuSr65VpRkUiRKRNpb0mdlk56+XPPKoIrfHqzbMuglDvZun0RISsA==, tarball: https://registry.npmjs.org/@babel/plugin-transform-duplicate-named-capturing-groups-regex/-/plugin-transform-duplicate-named-capturing-groups-regex-7.29.7.tgz} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 '@babel/plugin-transform-duplicate-named-capturing-groups-regex@8.0.1': - resolution: {integrity: sha512-Tv43P47o6fuHgBL7HLHQg3WKXohW9CEUGjLtnCDW27yJLK0zKUdTTqREbZbycNHA83hewMjde5tF6ekrHu9bAA==} + resolution: {integrity: sha512-Tv43P47o6fuHgBL7HLHQg3WKXohW9CEUGjLtnCDW27yJLK0zKUdTTqREbZbycNHA83hewMjde5tF6ekrHu9bAA==, tarball: https://registry.npmjs.org/@babel/plugin-transform-duplicate-named-capturing-groups-regex/-/plugin-transform-duplicate-named-capturing-groups-regex-8.0.1.tgz} engines: {node: ^22.18.0 || >=24.11.0} peerDependencies: '@babel/core': ^8.0.0 '@babel/plugin-transform-dynamic-import@7.29.7': - resolution: {integrity: sha512-giOlEm/EFjfjr+te9NsdjkUo2v4f8rS/SXPumRVHAtbNcyNlvtREkU1dZzaIDclNpnaVhlCqRdFKhJBjBikzLg==} + resolution: {integrity: sha512-giOlEm/EFjfjr+te9NsdjkUo2v4f8rS/SXPumRVHAtbNcyNlvtREkU1dZzaIDclNpnaVhlCqRdFKhJBjBikzLg==, tarball: https://registry.npmjs.org/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.29.7.tgz} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 '@babel/plugin-transform-dynamic-import@8.0.1': - resolution: {integrity: sha512-AS9GlgKc43tJNRu7yOvLaTko4qmdOb+8M69uNS8i421WLO20eVez7LdG5khKdi8E0LIQpYzzzdGIrdXWnO753g==} + resolution: {integrity: sha512-AS9GlgKc43tJNRu7yOvLaTko4qmdOb+8M69uNS8i421WLO20eVez7LdG5khKdi8E0LIQpYzzzdGIrdXWnO753g==, tarball: https://registry.npmjs.org/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-8.0.1.tgz} engines: {node: ^22.18.0 || >=24.11.0} peerDependencies: '@babel/core': ^8.0.0 '@babel/plugin-transform-explicit-resource-management@7.29.7': - resolution: {integrity: sha512-Rstj7coNz8sE+7Ju7ihpHLI564lsK5pUpNNlvptCIC/16E/S5hbl6n3kESPKdNRmqEWlpn5xpS5Q2dvXBsySLw==} + resolution: {integrity: sha512-Rstj7coNz8sE+7Ju7ihpHLI564lsK5pUpNNlvptCIC/16E/S5hbl6n3kESPKdNRmqEWlpn5xpS5Q2dvXBsySLw==, tarball: https://registry.npmjs.org/@babel/plugin-transform-explicit-resource-management/-/plugin-transform-explicit-resource-management-7.29.7.tgz} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 '@babel/plugin-transform-explicit-resource-management@8.0.1': - resolution: {integrity: sha512-VzDIYwBlLCpV6mJfloRdJm8HmYnMqs7O+bGha8yfg2kP7jAdxeCw6yZBVBeaKKQUThtSU52iy+3lB7DhYsbOBA==} + resolution: {integrity: sha512-VzDIYwBlLCpV6mJfloRdJm8HmYnMqs7O+bGha8yfg2kP7jAdxeCw6yZBVBeaKKQUThtSU52iy+3lB7DhYsbOBA==, tarball: https://registry.npmjs.org/@babel/plugin-transform-explicit-resource-management/-/plugin-transform-explicit-resource-management-8.0.1.tgz} engines: {node: ^22.18.0 || >=24.11.0} peerDependencies: '@babel/core': ^8.0.0 '@babel/plugin-transform-exponentiation-operator@7.29.7': - resolution: {integrity: sha512-zFpMOTLZBdW5LfObqcSbL6kefg4R4eLdmvS0wbN9M6D5Mym/sKm9toOoWyVOa+xDjvCnuWcHls2YonXwHvH3CQ==} + resolution: {integrity: sha512-zFpMOTLZBdW5LfObqcSbL6kefg4R4eLdmvS0wbN9M6D5Mym/sKm9toOoWyVOa+xDjvCnuWcHls2YonXwHvH3CQ==, tarball: https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.29.7.tgz} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 '@babel/plugin-transform-exponentiation-operator@8.0.1': - resolution: {integrity: sha512-DsZvUUklUmDQ7d2vp+VjqgUWD51mGxhZZ1FPdPP9Hcj0vsgGUKX+zEBGp/vzB1O5PZUxWT/Euq5fu39M9dm9wg==} + resolution: {integrity: sha512-DsZvUUklUmDQ7d2vp+VjqgUWD51mGxhZZ1FPdPP9Hcj0vsgGUKX+zEBGp/vzB1O5PZUxWT/Euq5fu39M9dm9wg==, tarball: https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-8.0.1.tgz} engines: {node: ^22.18.0 || >=24.11.0} peerDependencies: '@babel/core': ^8.0.0 '@babel/plugin-transform-export-namespace-from@7.29.7': - resolution: {integrity: sha512-24B2nOy2TeJSMheqwPD4DDQOV/elLSIlKxjZt4i05H5AgdPdWR3n18HnNrcJ+j76WJd9gbwb9jPjNYUy6RautA==} + resolution: {integrity: sha512-24B2nOy2TeJSMheqwPD4DDQOV/elLSIlKxjZt4i05H5AgdPdWR3n18HnNrcJ+j76WJd9gbwb9jPjNYUy6RautA==, tarball: https://registry.npmjs.org/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.29.7.tgz} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 '@babel/plugin-transform-export-namespace-from@8.0.1': - resolution: {integrity: sha512-bFzznm46bvWGaTYKle3iolbBJ+oPBfUjwCPesxlFE3SQ7DaY9EHf/8Y5ZzrodKJi8JDdcAyaVWaDUSVyhULh0g==} + resolution: {integrity: sha512-bFzznm46bvWGaTYKle3iolbBJ+oPBfUjwCPesxlFE3SQ7DaY9EHf/8Y5ZzrodKJi8JDdcAyaVWaDUSVyhULh0g==, tarball: https://registry.npmjs.org/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-8.0.1.tgz} engines: {node: ^22.18.0 || >=24.11.0} peerDependencies: '@babel/core': ^8.0.0 '@babel/plugin-transform-for-of@7.29.7': - resolution: {integrity: sha512-zeSIHh0+E1Um1WJRXCFlHQYu2ieJNdivLLjlBEp+dIBu3S51n+SZZmIXjxnItw6pz56Cn+KvK68BIBVsxq2JiQ==} + resolution: {integrity: sha512-zeSIHh0+E1Um1WJRXCFlHQYu2ieJNdivLLjlBEp+dIBu3S51n+SZZmIXjxnItw6pz56Cn+KvK68BIBVsxq2JiQ==, tarball: https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.29.7.tgz} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 '@babel/plugin-transform-for-of@8.0.1': - resolution: {integrity: sha512-rpeXtgELjpIBQH/+YmyFlD9timPEVCyqY+TNednzoeoTYvXSBEeUvYnYE+BK8rB8m6hHiNK7aL9QWKhGifEJCw==} + resolution: {integrity: sha512-rpeXtgELjpIBQH/+YmyFlD9timPEVCyqY+TNednzoeoTYvXSBEeUvYnYE+BK8rB8m6hHiNK7aL9QWKhGifEJCw==, tarball: https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-8.0.1.tgz} engines: {node: ^22.18.0 || >=24.11.0} peerDependencies: '@babel/core': ^8.0.0 '@babel/plugin-transform-function-name@7.29.7': - resolution: {integrity: sha512-otRWaHXE6fbAGkePvaj/kvs3HsqXfPhlnzwSOlnFgbqCPMd975dW+4wZ00WFBt+/YlBGcJwNrARQTOJOb4ZrIg==} + resolution: {integrity: sha512-otRWaHXE6fbAGkePvaj/kvs3HsqXfPhlnzwSOlnFgbqCPMd975dW+4wZ00WFBt+/YlBGcJwNrARQTOJOb4ZrIg==, tarball: https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.29.7.tgz} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 '@babel/plugin-transform-function-name@8.0.1': - resolution: {integrity: sha512-H1L/JfPf3CqmubuaiZaquXKQ8MRs4YWSsgRllkTviM8TafcCNnlvc4/fJZ3rXP8HmFM+/Bg+TlsPehUI9BtDFA==} + resolution: {integrity: sha512-H1L/JfPf3CqmubuaiZaquXKQ8MRs4YWSsgRllkTviM8TafcCNnlvc4/fJZ3rXP8HmFM+/Bg+TlsPehUI9BtDFA==, tarball: https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-8.0.1.tgz} engines: {node: ^22.18.0 || >=24.11.0} peerDependencies: '@babel/core': ^8.0.0 '@babel/plugin-transform-json-strings@7.29.7': - resolution: {integrity: sha512-RRnE2+eon1rJAq8MnoF1b5kTpY1vU88twHcvcKMrsqP/jxIRqDVs9iJB5fqPuqyeFAW0wJo4MlUIPpQCq/aRsg==} + resolution: {integrity: sha512-RRnE2+eon1rJAq8MnoF1b5kTpY1vU88twHcvcKMrsqP/jxIRqDVs9iJB5fqPuqyeFAW0wJo4MlUIPpQCq/aRsg==, tarball: https://registry.npmjs.org/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.29.7.tgz} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 '@babel/plugin-transform-json-strings@8.0.1': - resolution: {integrity: sha512-Mowp8X0J6p7ZehLU82B5e65te2uuSeDHyxrEROwEAS2VKXNXssfw5ZMqhY7k9iXTsOv1Xs/49G3lDCj9Vvw8qQ==} + resolution: {integrity: sha512-Mowp8X0J6p7ZehLU82B5e65te2uuSeDHyxrEROwEAS2VKXNXssfw5ZMqhY7k9iXTsOv1Xs/49G3lDCj9Vvw8qQ==, tarball: https://registry.npmjs.org/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-8.0.1.tgz} engines: {node: ^22.18.0 || >=24.11.0} peerDependencies: '@babel/core': ^8.0.0 '@babel/plugin-transform-literals@7.29.7': - resolution: {integrity: sha512-DZ/oLP21ZuWx1vKqnoNv6/tvEK48AQOBRai40CX9dTjGluvT/YZCyY3rryDtyUqCEoyNroy5KKPwX2iQCiRvyw==} + resolution: {integrity: sha512-DZ/oLP21ZuWx1vKqnoNv6/tvEK48AQOBRai40CX9dTjGluvT/YZCyY3rryDtyUqCEoyNroy5KKPwX2iQCiRvyw==, tarball: https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.29.7.tgz} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 '@babel/plugin-transform-literals@8.0.1': - resolution: {integrity: sha512-ai7kfPRcfyUV1EszXoF1PvL3IuJoCuH08WSEPoRcJTWfZZ55VL/rcfvbVY16QLA3jjbzzSneQSoCtD3L6OyUjw==} + resolution: {integrity: sha512-ai7kfPRcfyUV1EszXoF1PvL3IuJoCuH08WSEPoRcJTWfZZ55VL/rcfvbVY16QLA3jjbzzSneQSoCtD3L6OyUjw==, tarball: https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-8.0.1.tgz} engines: {node: ^22.18.0 || >=24.11.0} peerDependencies: '@babel/core': ^8.0.0 '@babel/plugin-transform-logical-assignment-operators@7.29.7': - resolution: {integrity: sha512-A0H91hh6W8MFRkp5TqJmMr39jzGD1A1E1Ysiv2O06Sfbhkapm+XyIzxWCEh5kqwOZ1/8QZ0dY3SeQ7XBqfJd5Q==} + resolution: {integrity: sha512-A0H91hh6W8MFRkp5TqJmMr39jzGD1A1E1Ysiv2O06Sfbhkapm+XyIzxWCEh5kqwOZ1/8QZ0dY3SeQ7XBqfJd5Q==, tarball: https://registry.npmjs.org/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.29.7.tgz} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 '@babel/plugin-transform-logical-assignment-operators@8.0.1': - resolution: {integrity: sha512-Emvtr5zkEGyCNAmt+qKD5EUh8G0RbxV9EZWrDdX0LuVy5tBq1B3fOIslvVF9aCJmpnwS/AvAT53b9LxAZyXlng==} + resolution: {integrity: sha512-Emvtr5zkEGyCNAmt+qKD5EUh8G0RbxV9EZWrDdX0LuVy5tBq1B3fOIslvVF9aCJmpnwS/AvAT53b9LxAZyXlng==, tarball: https://registry.npmjs.org/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-8.0.1.tgz} engines: {node: ^22.18.0 || >=24.11.0} peerDependencies: '@babel/core': ^8.0.0 '@babel/plugin-transform-member-expression-literals@7.29.7': - resolution: {integrity: sha512-hl1kwFZCCiDyfH25Xmco9jTrkPgnS9pmOzSG7W5I4SaGbLeqKv417hcU2RKmaxoPEgsoJh7ZPOrnPGq99bHoUg==} + resolution: {integrity: sha512-hl1kwFZCCiDyfH25Xmco9jTrkPgnS9pmOzSG7W5I4SaGbLeqKv417hcU2RKmaxoPEgsoJh7ZPOrnPGq99bHoUg==, tarball: https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.29.7.tgz} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 '@babel/plugin-transform-member-expression-literals@8.0.1': - resolution: {integrity: sha512-3Axi9abnyGsm/hh6DsKPZ1Cr9fTtKqS7w0Ig5g12mU269YclpH8pV3xMln2vPLexXgUp6S6L+I06d9/YOLfRKA==} + resolution: {integrity: sha512-3Axi9abnyGsm/hh6DsKPZ1Cr9fTtKqS7w0Ig5g12mU269YclpH8pV3xMln2vPLexXgUp6S6L+I06d9/YOLfRKA==, tarball: https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-8.0.1.tgz} engines: {node: ^22.18.0 || >=24.11.0} peerDependencies: '@babel/core': ^8.0.0 '@babel/plugin-transform-modules-amd@7.29.7': - resolution: {integrity: sha512-fxtQoH3m5ywUSIfaH0FGCzWu4McsYon5bD3K4XnskC7f+OyQMj7rsOMi4NvvmJ83WwBAg4UCe+ov4VZlqEvyew==} + resolution: {integrity: sha512-fxtQoH3m5ywUSIfaH0FGCzWu4McsYon5bD3K4XnskC7f+OyQMj7rsOMi4NvvmJ83WwBAg4UCe+ov4VZlqEvyew==, tarball: https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.29.7.tgz} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 '@babel/plugin-transform-modules-amd@8.0.1': - resolution: {integrity: sha512-FDdhET8y1YFDNRuoynqSf23WTzbBBpbIB2oRrlFX7YYm9uWtFvJDSD1r/epBSjfPkOjeaaLgRW9xNnt3JGx46A==} + resolution: {integrity: sha512-FDdhET8y1YFDNRuoynqSf23WTzbBBpbIB2oRrlFX7YYm9uWtFvJDSD1r/epBSjfPkOjeaaLgRW9xNnt3JGx46A==, tarball: https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-8.0.1.tgz} engines: {node: ^22.18.0 || >=24.11.0} peerDependencies: '@babel/core': ^8.0.0 '@babel/plugin-transform-modules-commonjs@7.29.7': - resolution: {integrity: sha512-j0vCldybPC5b5dwCQOJ21uKtHzt7hxLygJTg9eF1ScfaikEDNfzn94XoW5Fi+seBR0nCyL23xaBFFkq7dTM8XQ==} + resolution: {integrity: sha512-j0vCldybPC5b5dwCQOJ21uKtHzt7hxLygJTg9eF1ScfaikEDNfzn94XoW5Fi+seBR0nCyL23xaBFFkq7dTM8XQ==, tarball: https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.29.7.tgz} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 '@babel/plugin-transform-modules-commonjs@8.0.1': - resolution: {integrity: sha512-PMuzulWrrzFNmY3lXSk/tV9NRb7y0eZZLJY4UEo2TKszroxvUZHAPPi+T9FDyrQhod+TQA+t+8/QYaaMpiEuhA==} + resolution: {integrity: sha512-PMuzulWrrzFNmY3lXSk/tV9NRb7y0eZZLJY4UEo2TKszroxvUZHAPPi+T9FDyrQhod+TQA+t+8/QYaaMpiEuhA==, tarball: https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-8.0.1.tgz} engines: {node: ^22.18.0 || >=24.11.0} peerDependencies: '@babel/core': ^8.0.0 '@babel/plugin-transform-modules-systemjs@7.29.7': - resolution: {integrity: sha512-TM2ZcQLoG2/y4HODiStCo10DibYhWhGWAwVv+EQKmG/7GFl0N+AAmUiXOMKM+aiJ9XBJ9AHVZBvTzMnJ2sM3cQ==} + resolution: {integrity: sha512-TM2ZcQLoG2/y4HODiStCo10DibYhWhGWAwVv+EQKmG/7GFl0N+AAmUiXOMKM+aiJ9XBJ9AHVZBvTzMnJ2sM3cQ==, tarball: https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.29.7.tgz} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 '@babel/plugin-transform-modules-systemjs@8.0.1': - resolution: {integrity: sha512-0NEHanXmnFEnfT2dLKTXnu7m8GXFsnxRgteBC2aH21hYMBwAgxu5dcTdi/Eg+ToI1HbZe0CHwz4XRLgRNQhYoQ==} + resolution: {integrity: sha512-0NEHanXmnFEnfT2dLKTXnu7m8GXFsnxRgteBC2aH21hYMBwAgxu5dcTdi/Eg+ToI1HbZe0CHwz4XRLgRNQhYoQ==, tarball: https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-8.0.1.tgz} engines: {node: ^22.18.0 || >=24.11.0} peerDependencies: '@babel/core': ^8.0.0 '@babel/plugin-transform-modules-umd@7.29.7': - resolution: {integrity: sha512-B4UkaTK3QpgCwJnrxKfMPKdo92CN7OKXAlpAAnM3UPu0Q0lCCk57ylA9AJbRy2v8dDKOPAAWcoR6CMyeoHwRCA==} + resolution: {integrity: sha512-B4UkaTK3QpgCwJnrxKfMPKdo92CN7OKXAlpAAnM3UPu0Q0lCCk57ylA9AJbRy2v8dDKOPAAWcoR6CMyeoHwRCA==, tarball: https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.29.7.tgz} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 '@babel/plugin-transform-modules-umd@8.0.1': - resolution: {integrity: sha512-XKTa2J2MdkmbVEeChq9f7Or0VYcsF0NyVBgytRyeN9F+J+ETAB2SHhfkG4toz/ssuU0i+h/QgJ6ddo5YakSQcA==} + resolution: {integrity: sha512-XKTa2J2MdkmbVEeChq9f7Or0VYcsF0NyVBgytRyeN9F+J+ETAB2SHhfkG4toz/ssuU0i+h/QgJ6ddo5YakSQcA==, tarball: https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-8.0.1.tgz} engines: {node: ^22.18.0 || >=24.11.0} peerDependencies: '@babel/core': ^8.0.0 '@babel/plugin-transform-named-capturing-groups-regex@7.29.7': - resolution: {integrity: sha512-vuFoLwr4qnv2xbZ16SQd6uPcH5FNrLHhk/Jzo++0XJFcaDsr4gjJVg6j398oMHiC+83k/GiBzviwF5KBJkPUtQ==} + resolution: {integrity: sha512-vuFoLwr4qnv2xbZ16SQd6uPcH5FNrLHhk/Jzo++0XJFcaDsr4gjJVg6j398oMHiC+83k/GiBzviwF5KBJkPUtQ==, tarball: https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.29.7.tgz} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 '@babel/plugin-transform-named-capturing-groups-regex@8.0.1': - resolution: {integrity: sha512-zCHu+Jr2gTdJE48lN9SV/kXueCW2M79mKtKJc/ttfzzr/jvgdQdCd17RADMqFRQc/25MLxdtjTmlD0HSAMOlIQ==} + resolution: {integrity: sha512-zCHu+Jr2gTdJE48lN9SV/kXueCW2M79mKtKJc/ttfzzr/jvgdQdCd17RADMqFRQc/25MLxdtjTmlD0HSAMOlIQ==, tarball: https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-8.0.1.tgz} engines: {node: ^22.18.0 || >=24.11.0} peerDependencies: '@babel/core': ^8.0.0 '@babel/plugin-transform-new-target@7.29.7': - resolution: {integrity: sha512-fEo41GmsOUhOBlw8ioo6zvjX5Xc2Lqkzlyfqbpsk3eB6TReV18uhxZ0esfEokVbY2+PVJAQHNKxER6lGrzNd3A==} + resolution: {integrity: sha512-fEo41GmsOUhOBlw8ioo6zvjX5Xc2Lqkzlyfqbpsk3eB6TReV18uhxZ0esfEokVbY2+PVJAQHNKxER6lGrzNd3A==, tarball: https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.29.7.tgz} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 '@babel/plugin-transform-new-target@8.0.1': - resolution: {integrity: sha512-QSQxVg1x4PuOuhWUs4Y9u+x9Y+ER8z6G3tC+bDLBzvoOrNLJrEBQLRnwrTP8e5klihAw6Z+e9X5RjdAKcAGapA==} + resolution: {integrity: sha512-QSQxVg1x4PuOuhWUs4Y9u+x9Y+ER8z6G3tC+bDLBzvoOrNLJrEBQLRnwrTP8e5klihAw6Z+e9X5RjdAKcAGapA==, tarball: https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-8.0.1.tgz} engines: {node: ^22.18.0 || >=24.11.0} peerDependencies: '@babel/core': ^8.0.0 '@babel/plugin-transform-nullish-coalescing-operator@7.29.7': - resolution: {integrity: sha512-idmp1dFaekP9GbcMvG24Kvw2BfhFZjHnNJCkV4WuIY4PskJzwI3f1N5OdgYke38T7rftO6ERulFRn2cFeZwRkg==} + resolution: {integrity: sha512-idmp1dFaekP9GbcMvG24Kvw2BfhFZjHnNJCkV4WuIY4PskJzwI3f1N5OdgYke38T7rftO6ERulFRn2cFeZwRkg==, tarball: https://registry.npmjs.org/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.29.7.tgz} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 '@babel/plugin-transform-nullish-coalescing-operator@8.0.1': - resolution: {integrity: sha512-AgCJAmQLF7+PtsK79wJqr4xJ2StHCXlz7JL5CVFP4HejJx25Tk6yl1ZrXvi0cKh3VGDVnfVxefxnrpsBirgpyQ==} + resolution: {integrity: sha512-AgCJAmQLF7+PtsK79wJqr4xJ2StHCXlz7JL5CVFP4HejJx25Tk6yl1ZrXvi0cKh3VGDVnfVxefxnrpsBirgpyQ==, tarball: https://registry.npmjs.org/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-8.0.1.tgz} engines: {node: ^22.18.0 || >=24.11.0} peerDependencies: '@babel/core': ^8.0.0 '@babel/plugin-transform-numeric-separator@7.29.7': - resolution: {integrity: sha512-zR7fv/z14OjgHl4AgRtkDBvBMhIzCxqV/qN/2BCRC7LjFwvuzjYe7gDWxC4Wl/SNsLM6SE1IWvRPYMgSJaUvNw==} + resolution: {integrity: sha512-zR7fv/z14OjgHl4AgRtkDBvBMhIzCxqV/qN/2BCRC7LjFwvuzjYe7gDWxC4Wl/SNsLM6SE1IWvRPYMgSJaUvNw==, tarball: https://registry.npmjs.org/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.29.7.tgz} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 '@babel/plugin-transform-numeric-separator@8.0.1': - resolution: {integrity: sha512-it2DmUyLIA1GQUXlFDEnI+/G89mTgxndnAiZYpW8xYR6LboblfirMqiWJeTna5uypQJg7viTT4D1iEURRtFcfw==} + resolution: {integrity: sha512-it2DmUyLIA1GQUXlFDEnI+/G89mTgxndnAiZYpW8xYR6LboblfirMqiWJeTna5uypQJg7viTT4D1iEURRtFcfw==, tarball: https://registry.npmjs.org/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-8.0.1.tgz} engines: {node: ^22.18.0 || >=24.11.0} peerDependencies: '@babel/core': ^8.0.0 '@babel/plugin-transform-object-rest-spread@7.29.7': - resolution: {integrity: sha512-Ld98jn4c0smUywL57m7SgsHq3OpThOa6LqZJif3G6jYOovPleoFhVrBJ1WegRApSFB2wu4+RelAj9AC9G08Z4A==} + resolution: {integrity: sha512-Ld98jn4c0smUywL57m7SgsHq3OpThOa6LqZJif3G6jYOovPleoFhVrBJ1WegRApSFB2wu4+RelAj9AC9G08Z4A==, tarball: https://registry.npmjs.org/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.29.7.tgz} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 '@babel/plugin-transform-object-rest-spread@8.0.1': - resolution: {integrity: sha512-VmxkDu6bBdbxRzqn6E93hYucug4OVa6svSO19W//vVzNUGAmQzk3QRyHyyEtfcjSLR3NWfRsWwVM9zExLmd+2w==} + resolution: {integrity: sha512-VmxkDu6bBdbxRzqn6E93hYucug4OVa6svSO19W//vVzNUGAmQzk3QRyHyyEtfcjSLR3NWfRsWwVM9zExLmd+2w==, tarball: https://registry.npmjs.org/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-8.0.1.tgz} engines: {node: ^22.18.0 || >=24.11.0} peerDependencies: '@babel/core': ^8.0.0 '@babel/plugin-transform-object-super@7.29.7': - resolution: {integrity: sha512-Ea/diGcw0twB5IlZPO5sgET6fJsLJqPABqTuFWIR+iMPGPZJkATEIWx0wa+aEQ5UY1CBQyP/gkAiLEqn1vBiQA==} + resolution: {integrity: sha512-Ea/diGcw0twB5IlZPO5sgET6fJsLJqPABqTuFWIR+iMPGPZJkATEIWx0wa+aEQ5UY1CBQyP/gkAiLEqn1vBiQA==, tarball: https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.29.7.tgz} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 '@babel/plugin-transform-object-super@8.0.1': - resolution: {integrity: sha512-fDkPXRTRKGm25bAq01q82UM4ypPqdVXCwphUUm4t1dL01fGIG0v8KRvT+4BjhMAtRxtPuI34t5Vs7yjRgs3ZgQ==} + resolution: {integrity: sha512-fDkPXRTRKGm25bAq01q82UM4ypPqdVXCwphUUm4t1dL01fGIG0v8KRvT+4BjhMAtRxtPuI34t5Vs7yjRgs3ZgQ==, tarball: https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-8.0.1.tgz} engines: {node: ^22.18.0 || >=24.11.0} peerDependencies: '@babel/core': ^8.0.0 '@babel/plugin-transform-optional-catch-binding@7.29.7': - resolution: {integrity: sha512-sLsyndxK2VwX6yNUOakMb7Sh553ZTe/vVM1XJ+9Z5aW1ytsc8xOIwmyk05NNjN60vkc5/KqoTH6hB4V41LJhng==} + resolution: {integrity: sha512-sLsyndxK2VwX6yNUOakMb7Sh553ZTe/vVM1XJ+9Z5aW1ytsc8xOIwmyk05NNjN60vkc5/KqoTH6hB4V41LJhng==, tarball: https://registry.npmjs.org/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.29.7.tgz} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 '@babel/plugin-transform-optional-catch-binding@8.0.1': - resolution: {integrity: sha512-b2OQ74uGliyATcasTjxGy2O/86UI/n+EN4juB4EMfEwTi9j9uq70PuP0L8fW77vfRY66gO/YoTo/WbIdQ/Si1g==} + resolution: {integrity: sha512-b2OQ74uGliyATcasTjxGy2O/86UI/n+EN4juB4EMfEwTi9j9uq70PuP0L8fW77vfRY66gO/YoTo/WbIdQ/Si1g==, tarball: https://registry.npmjs.org/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-8.0.1.tgz} engines: {node: ^22.18.0 || >=24.11.0} peerDependencies: '@babel/core': ^8.0.0 '@babel/plugin-transform-optional-chaining@7.29.7': - resolution: {integrity: sha512-6GM1dhvK3gNODkXcEcMCOLEDCLSoZ/sBbro2Ax8HURyasQ4NshagQixkRFdh5niI6E4gmA/jYI/4aT7rRos3ZQ==} + resolution: {integrity: sha512-6GM1dhvK3gNODkXcEcMCOLEDCLSoZ/sBbro2Ax8HURyasQ4NshagQixkRFdh5niI6E4gmA/jYI/4aT7rRos3ZQ==, tarball: https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.29.7.tgz} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 '@babel/plugin-transform-optional-chaining@8.0.1': - resolution: {integrity: sha512-WtRS1c94lZGpGHxYLXMEWeoMVcuv8nkiyr8BTs6OYZv7N3Y9xVE8nbdFIl4lDJH6aH8/pLhqAQOL69d/WI9WdA==} + resolution: {integrity: sha512-WtRS1c94lZGpGHxYLXMEWeoMVcuv8nkiyr8BTs6OYZv7N3Y9xVE8nbdFIl4lDJH6aH8/pLhqAQOL69d/WI9WdA==, tarball: https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-8.0.1.tgz} engines: {node: ^22.18.0 || >=24.11.0} peerDependencies: '@babel/core': ^8.0.0 '@babel/plugin-transform-parameters@7.29.7': - resolution: {integrity: sha512-ZDOBqV/qLYJI0YElr8DcENEyARsFQeESqWXH6gZlghYXuPPjvweuDhP4VyEi4BlUBlLRFZVjxoZDMjxhLW766g==} + resolution: {integrity: sha512-ZDOBqV/qLYJI0YElr8DcENEyARsFQeESqWXH6gZlghYXuPPjvweuDhP4VyEi4BlUBlLRFZVjxoZDMjxhLW766g==, tarball: https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.29.7.tgz} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 '@babel/plugin-transform-parameters@8.0.1': - resolution: {integrity: sha512-IIwRqroW0CYQwR6+3pnmu27z+H98poScWdnov8z6osumMeEsFxAFBBsDS2CFk2jFpPlGqVr89jK/HXO6i5DzxQ==} + resolution: {integrity: sha512-IIwRqroW0CYQwR6+3pnmu27z+H98poScWdnov8z6osumMeEsFxAFBBsDS2CFk2jFpPlGqVr89jK/HXO6i5DzxQ==, tarball: https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-8.0.1.tgz} engines: {node: ^22.18.0 || >=24.11.0} peerDependencies: '@babel/core': ^8.0.0 '@babel/plugin-transform-private-methods@7.29.7': - resolution: {integrity: sha512-/6Rz4DK1ETDEM/bWHsPHcaEe7ZaT1EqSXjtSP/L0DijOYuaUhiRiOKcwpZ8P7zR4xXEHc2ITdiCgBm9Tpyv9ug==} + resolution: {integrity: sha512-/6Rz4DK1ETDEM/bWHsPHcaEe7ZaT1EqSXjtSP/L0DijOYuaUhiRiOKcwpZ8P7zR4xXEHc2ITdiCgBm9Tpyv9ug==, tarball: https://registry.npmjs.org/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.29.7.tgz} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 '@babel/plugin-transform-private-methods@8.0.1': - resolution: {integrity: sha512-TrFCGcXaVDh6S5IRhmLSRTY9H80VTCMQWnZtzBRg4RWg3KCLmdmsmj4M15kZAPZfoPkWL/SJb4em3Py/vOiX8g==} + resolution: {integrity: sha512-TrFCGcXaVDh6S5IRhmLSRTY9H80VTCMQWnZtzBRg4RWg3KCLmdmsmj4M15kZAPZfoPkWL/SJb4em3Py/vOiX8g==, tarball: https://registry.npmjs.org/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-8.0.1.tgz} engines: {node: ^22.18.0 || >=24.11.0} peerDependencies: '@babel/core': ^8.0.0 '@babel/plugin-transform-private-property-in-object@7.29.7': - resolution: {integrity: sha512-+BNo06dnrzdNNqCm1X6YUaVv0DKk8Q+JYcoZfOkLhYWNCXzlwTSRq8zGWayT1csjcpNXV9CQTBRRbmTLZac5cA==} + resolution: {integrity: sha512-+BNo06dnrzdNNqCm1X6YUaVv0DKk8Q+JYcoZfOkLhYWNCXzlwTSRq8zGWayT1csjcpNXV9CQTBRRbmTLZac5cA==, tarball: https://registry.npmjs.org/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.29.7.tgz} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 '@babel/plugin-transform-private-property-in-object@8.0.1': - resolution: {integrity: sha512-e+yfOqSYBZaf3PARpiQkjZrpWYgmcFLhK+1tevh2CpHR1O9/36IdyPnAZusESX5nzVV/XZTDAtQBRLa8HPT5Dw==} + resolution: {integrity: sha512-e+yfOqSYBZaf3PARpiQkjZrpWYgmcFLhK+1tevh2CpHR1O9/36IdyPnAZusESX5nzVV/XZTDAtQBRLa8HPT5Dw==, tarball: https://registry.npmjs.org/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-8.0.1.tgz} engines: {node: ^22.18.0 || >=24.11.0} peerDependencies: '@babel/core': ^8.0.0 '@babel/plugin-transform-property-literals@7.29.7': - resolution: {integrity: sha512-bOMRLQuI0A5ZqHq3OWJ89/rXpJ/NJrbVhXiP4zwPGMs6kpcVsuTUNjwoE30K0Qm3mf48a/TnRYYD6vPNqcg6jA==} + resolution: {integrity: sha512-bOMRLQuI0A5ZqHq3OWJ89/rXpJ/NJrbVhXiP4zwPGMs6kpcVsuTUNjwoE30K0Qm3mf48a/TnRYYD6vPNqcg6jA==, tarball: https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.29.7.tgz} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 '@babel/plugin-transform-property-literals@8.0.1': - resolution: {integrity: sha512-Z/qx4cxUtYR1nt7XWRutObPxDks98fEYsjWbVeKEqZH6y3AGknmgzCqmHf2FHWZCl1DfoPeuJY+3hZ+35D+2tg==} + resolution: {integrity: sha512-Z/qx4cxUtYR1nt7XWRutObPxDks98fEYsjWbVeKEqZH6y3AGknmgzCqmHf2FHWZCl1DfoPeuJY+3hZ+35D+2tg==, tarball: https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-8.0.1.tgz} engines: {node: ^22.18.0 || >=24.11.0} peerDependencies: '@babel/core': ^8.0.0 '@babel/plugin-transform-react-constant-elements@7.29.7': - resolution: {integrity: sha512-J0wGhKan+rIiE2OhfhRptySLrJ6SjQYM6b6N1FMlhyhCcw1Mig8vQjWchyB+bgHGDvaWo6Diu6CLRMra2uMtmg==} + resolution: {integrity: sha512-J0wGhKan+rIiE2OhfhRptySLrJ6SjQYM6b6N1FMlhyhCcw1Mig8vQjWchyB+bgHGDvaWo6Diu6CLRMra2uMtmg==, tarball: https://registry.npmjs.org/@babel/plugin-transform-react-constant-elements/-/plugin-transform-react-constant-elements-7.29.7.tgz} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 '@babel/plugin-transform-react-display-name@7.29.7': - resolution: {integrity: sha512-+1wdDMGNb4UPeY3Q4L5yLiYe6TXPXubs4NjrgRFw13hPRLJfEMw2Q5OXkee6/IfdqePIeW4Jjwe3aBh7SdKz4Q==} + resolution: {integrity: sha512-+1wdDMGNb4UPeY3Q4L5yLiYe6TXPXubs4NjrgRFw13hPRLJfEMw2Q5OXkee6/IfdqePIeW4Jjwe3aBh7SdKz4Q==, tarball: https://registry.npmjs.org/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.29.7.tgz} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 '@babel/plugin-transform-react-jsx-development@7.29.7': - resolution: {integrity: sha512-Xfy3UVMF04+ypnFbkhvfqtmvwfe92qwQdbGZVonhE+6v35GzlofmOnA1szaZqzb9xYWr0nl1e5EMmzi0DNON1g==} + resolution: {integrity: sha512-Xfy3UVMF04+ypnFbkhvfqtmvwfe92qwQdbGZVonhE+6v35GzlofmOnA1szaZqzb9xYWr0nl1e5EMmzi0DNON1g==, tarball: https://registry.npmjs.org/@babel/plugin-transform-react-jsx-development/-/plugin-transform-react-jsx-development-7.29.7.tgz} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 '@babel/plugin-transform-react-jsx-self@7.29.7': - resolution: {integrity: sha512-TL0hMc9xzy86VD31nUiwzd5otRAcyEPcsegCxolO0PvcXuH1v0kECe/UIznYFihpkvU5wg/jk4v0TTEFfm53fw==} + resolution: {integrity: sha512-TL0hMc9xzy86VD31nUiwzd5otRAcyEPcsegCxolO0PvcXuH1v0kECe/UIznYFihpkvU5wg/jk4v0TTEFfm53fw==, tarball: https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.29.7.tgz} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 '@babel/plugin-transform-react-jsx-source@7.29.7': - resolution: {integrity: sha512-06IyK09H3wi4cGbhDBwp5gUGo0IKtnYa8tyTiephirPCK6fbobVGiXMMI5zLQ4aKEYP3wZ3ArU44o+8KMrSG/Q==} + resolution: {integrity: sha512-06IyK09H3wi4cGbhDBwp5gUGo0IKtnYa8tyTiephirPCK6fbobVGiXMMI5zLQ4aKEYP3wZ3ArU44o+8KMrSG/Q==, tarball: https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.29.7.tgz} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 '@babel/plugin-transform-react-jsx@7.29.7': - resolution: {integrity: sha512-WsZulLVBUHXVj2cUcPVx6UE21TpalB6bHbSFErKT0Ib++ax24jjXe73FqlWvdylFOjiuPHYi6VCcgRad1ItN+A==} + resolution: {integrity: sha512-WsZulLVBUHXVj2cUcPVx6UE21TpalB6bHbSFErKT0Ib++ax24jjXe73FqlWvdylFOjiuPHYi6VCcgRad1ItN+A==, tarball: https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.29.7.tgz} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 '@babel/plugin-transform-react-pure-annotations@7.29.7': - resolution: {integrity: sha512-H5E+HBgDpr6Q5t+Aj11tL7XkIui1jhbIoArVQnqjgXo5/3YxkN7ZEBcWF4RQlB0T4rrxJQbXS6kiFV6B7XTqUA==} + resolution: {integrity: sha512-H5E+HBgDpr6Q5t+Aj11tL7XkIui1jhbIoArVQnqjgXo5/3YxkN7ZEBcWF4RQlB0T4rrxJQbXS6kiFV6B7XTqUA==, tarball: https://registry.npmjs.org/@babel/plugin-transform-react-pure-annotations/-/plugin-transform-react-pure-annotations-7.29.7.tgz} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 '@babel/plugin-transform-regenerator@7.29.7': - resolution: {integrity: sha512-rNNFV0DBAJp988xW2DOntfDoYn1eR8GGF5AT5vYc+rjyfaQkM242c9tZUHHPe7KYaiJizXPWhQTzzdbXySyhBw==} + resolution: {integrity: sha512-rNNFV0DBAJp988xW2DOntfDoYn1eR8GGF5AT5vYc+rjyfaQkM242c9tZUHHPe7KYaiJizXPWhQTzzdbXySyhBw==, tarball: https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.29.7.tgz} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 '@babel/plugin-transform-regenerator@8.0.2': - resolution: {integrity: sha512-aFfsjCRYducRV4dPnpsBbdRkLjboca9FVDg6HZCgy0Ahvk2ZQ/2exmCRC5qS9P6rsWwrmIheNaIM6A1j2F8KMA==} + resolution: {integrity: sha512-aFfsjCRYducRV4dPnpsBbdRkLjboca9FVDg6HZCgy0Ahvk2ZQ/2exmCRC5qS9P6rsWwrmIheNaIM6A1j2F8KMA==, tarball: https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-8.0.2.tgz} engines: {node: ^22.18.0 || >=24.11.0} peerDependencies: '@babel/core': ^8.0.0 '@babel/plugin-transform-regexp-modifiers@7.29.7': - resolution: {integrity: sha512-mB5Fs0VWrJ42ZCmc8114v60qetdaUVNkj9PmSZRmanCZM3S9hm0CFRLjRmYIsuXav14l2jvZ+4T8iiCGnhj3nQ==} + resolution: {integrity: sha512-mB5Fs0VWrJ42ZCmc8114v60qetdaUVNkj9PmSZRmanCZM3S9hm0CFRLjRmYIsuXav14l2jvZ+4T8iiCGnhj3nQ==, tarball: https://registry.npmjs.org/@babel/plugin-transform-regexp-modifiers/-/plugin-transform-regexp-modifiers-7.29.7.tgz} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 '@babel/plugin-transform-regexp-modifiers@8.0.1': - resolution: {integrity: sha512-02ITRDBesPdTYU0oShAzERwEPzozOUQSXlz3qrt8JGuhalBJQv9z5NjgHJPC9sS3Fsam8gDtfAEpBnqZwUIdjQ==} + resolution: {integrity: sha512-02ITRDBesPdTYU0oShAzERwEPzozOUQSXlz3qrt8JGuhalBJQv9z5NjgHJPC9sS3Fsam8gDtfAEpBnqZwUIdjQ==, tarball: https://registry.npmjs.org/@babel/plugin-transform-regexp-modifiers/-/plugin-transform-regexp-modifiers-8.0.1.tgz} engines: {node: ^22.18.0 || >=24.11.0} peerDependencies: '@babel/core': ^8.0.0 '@babel/plugin-transform-reserved-words@7.29.7': - resolution: {integrity: sha512-5+YhdpVgmfSmwZyLMftfaiffLRMHjzIRHFHHLdibcSyJm2pasMrKHrO3Ptrt2DRshjvpgjEJJ1zVW14WPq/6QA==} + resolution: {integrity: sha512-5+YhdpVgmfSmwZyLMftfaiffLRMHjzIRHFHHLdibcSyJm2pasMrKHrO3Ptrt2DRshjvpgjEJJ1zVW14WPq/6QA==, tarball: https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.29.7.tgz} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 '@babel/plugin-transform-reserved-words@8.0.1': - resolution: {integrity: sha512-+aykZi7ZP3U84veqfJXm3HhPZGddWFi64g7jr0ni6tb1zel+1ey+SL+IRKPoZXFyFqvYEsoqrmx4PyEJRlHl/Q==} + resolution: {integrity: sha512-+aykZi7ZP3U84veqfJXm3HhPZGddWFi64g7jr0ni6tb1zel+1ey+SL+IRKPoZXFyFqvYEsoqrmx4PyEJRlHl/Q==, tarball: https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-8.0.1.tgz} engines: {node: ^22.18.0 || >=24.11.0} peerDependencies: '@babel/core': ^8.0.0 '@babel/plugin-transform-runtime@7.29.7': - resolution: {integrity: sha512-xmAscdE/AsqRW7vutbPNoUmu/nF5SrLKPs7aoJgEjo35lLKA/Bc0i2rMv/hr1+Y0o1bQCiVtith3u2vdgRL39Q==} + resolution: {integrity: sha512-xmAscdE/AsqRW7vutbPNoUmu/nF5SrLKPs7aoJgEjo35lLKA/Bc0i2rMv/hr1+Y0o1bQCiVtith3u2vdgRL39Q==, tarball: https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.29.7.tgz} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 '@babel/plugin-transform-runtime@8.0.1': - resolution: {integrity: sha512-MPDpKBrxn+thQay3eJmUiSeHswiT7MkINb48hHkX6OzodB149PKq1kred+lpMebrDzHA+G1ekCQnlYSkyEqAOw==} + resolution: {integrity: sha512-MPDpKBrxn+thQay3eJmUiSeHswiT7MkINb48hHkX6OzodB149PKq1kred+lpMebrDzHA+G1ekCQnlYSkyEqAOw==, tarball: https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-8.0.1.tgz} engines: {node: ^22.18.0 || >=24.11.0} peerDependencies: '@babel/core': ^8.0.0 '@babel/plugin-transform-shorthand-properties@7.29.7': - resolution: {integrity: sha512-I+WYbGBAiCn7nA6xBrlgPH+MB7HWb4u8pv5S0Pv7OtwNvIFvCCb24YlttKEeUFVurfBCEaOTnuhlqsb7f0Z5Dg==} + resolution: {integrity: sha512-I+WYbGBAiCn7nA6xBrlgPH+MB7HWb4u8pv5S0Pv7OtwNvIFvCCb24YlttKEeUFVurfBCEaOTnuhlqsb7f0Z5Dg==, tarball: https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.29.7.tgz} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 '@babel/plugin-transform-shorthand-properties@8.0.1': - resolution: {integrity: sha512-JddANd9yPVH8dYgVoNkqAH5BftnsDxFpG51Zas7sc6F3poz5QWcejHNGO8a/57IX5ByjGSzEmYk9Z7ZMa5MWaw==} + resolution: {integrity: sha512-JddANd9yPVH8dYgVoNkqAH5BftnsDxFpG51Zas7sc6F3poz5QWcejHNGO8a/57IX5ByjGSzEmYk9Z7ZMa5MWaw==, tarball: https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-8.0.1.tgz} engines: {node: ^22.18.0 || >=24.11.0} peerDependencies: '@babel/core': ^8.0.0 '@babel/plugin-transform-spread@7.29.7': - resolution: {integrity: sha512-/u5K1QWada7tbYNqTjMh96718g9NTwh9tfPJMsSmVsQwGT447FskV+KcfeXkXq2GWki4EM/MuTdmBec+hOuVTQ==} + resolution: {integrity: sha512-/u5K1QWada7tbYNqTjMh96718g9NTwh9tfPJMsSmVsQwGT447FskV+KcfeXkXq2GWki4EM/MuTdmBec+hOuVTQ==, tarball: https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.29.7.tgz} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 '@babel/plugin-transform-spread@8.0.1': - resolution: {integrity: sha512-O9Bw9FyxlSw1SlMg3S82/GKNZ0x77RPbHezotEy1JTlIM/vk6WO8jW1iF+iTiKLOXNvi+b+LZ9t77Gi+Q0FhGg==} + resolution: {integrity: sha512-O9Bw9FyxlSw1SlMg3S82/GKNZ0x77RPbHezotEy1JTlIM/vk6WO8jW1iF+iTiKLOXNvi+b+LZ9t77Gi+Q0FhGg==, tarball: https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-8.0.1.tgz} engines: {node: ^22.18.0 || >=24.11.0} peerDependencies: '@babel/core': ^8.0.0 '@babel/plugin-transform-sticky-regex@7.29.7': - resolution: {integrity: sha512-BCHzNYJGe9l7EpwwDBN/ztlL2NYFFq8hp9ddjtUEM9f2O7S7kKV/lL6Fwo7IF7NSkYhPK2vO+86nIGltA90MsA==} + resolution: {integrity: sha512-BCHzNYJGe9l7EpwwDBN/ztlL2NYFFq8hp9ddjtUEM9f2O7S7kKV/lL6Fwo7IF7NSkYhPK2vO+86nIGltA90MsA==, tarball: https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.29.7.tgz} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 '@babel/plugin-transform-sticky-regex@8.0.1': - resolution: {integrity: sha512-IsVP6WrZZQdaG2zLmeKwWiI+ua2NB5L1+f77C2/8z2NCDz7uxlIA/lnwocYOJk9PXcOC2sZgRls3LN4XpNduzQ==} + resolution: {integrity: sha512-IsVP6WrZZQdaG2zLmeKwWiI+ua2NB5L1+f77C2/8z2NCDz7uxlIA/lnwocYOJk9PXcOC2sZgRls3LN4XpNduzQ==, tarball: https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-8.0.1.tgz} engines: {node: ^22.18.0 || >=24.11.0} peerDependencies: '@babel/core': ^8.0.0 '@babel/plugin-transform-template-literals@7.29.7': - resolution: {integrity: sha512-NCSEJ4sLFU2gqAub45HYh4fus2yQ36rr6ei6vpU7NdoJqCpxvEG8E6eJpscGyXP3VHD2Ny+fSXr04k1hoUrFqA==} + resolution: {integrity: sha512-NCSEJ4sLFU2gqAub45HYh4fus2yQ36rr6ei6vpU7NdoJqCpxvEG8E6eJpscGyXP3VHD2Ny+fSXr04k1hoUrFqA==, tarball: https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.29.7.tgz} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 '@babel/plugin-transform-template-literals@8.0.1': - resolution: {integrity: sha512-JXvtj5+BJA9Qv3prDzW2z2DkGTJNmG0BObTdUD03STiu1Jr4fNQkQy3hYZgPL46a2RjcuhwBMYf49BOuJ98gnA==} + resolution: {integrity: sha512-JXvtj5+BJA9Qv3prDzW2z2DkGTJNmG0BObTdUD03STiu1Jr4fNQkQy3hYZgPL46a2RjcuhwBMYf49BOuJ98gnA==, tarball: https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-8.0.1.tgz} engines: {node: ^22.18.0 || >=24.11.0} peerDependencies: '@babel/core': ^8.0.0 '@babel/plugin-transform-typeof-symbol@7.29.7': - resolution: {integrity: sha512-223mNGoTkBiTEWFoK+Q6Go3tueMRclO8vxxxxquNCYuNI4jWOofFKJRRDu6SDrB8Sgo1UEGW9T4GAQ8ZyRso1A==} + resolution: {integrity: sha512-223mNGoTkBiTEWFoK+Q6Go3tueMRclO8vxxxxquNCYuNI4jWOofFKJRRDu6SDrB8Sgo1UEGW9T4GAQ8ZyRso1A==, tarball: https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.29.7.tgz} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 '@babel/plugin-transform-typeof-symbol@8.0.1': - resolution: {integrity: sha512-+wJoxgxP2gtey0UMUOMhzMMji2XHO/Uu6MXUh/r5Yhc2jngKzK/wFxY2WNe4UCaRcMvCb4gcnB8wIgFXJsocXg==} + resolution: {integrity: sha512-+wJoxgxP2gtey0UMUOMhzMMji2XHO/Uu6MXUh/r5Yhc2jngKzK/wFxY2WNe4UCaRcMvCb4gcnB8wIgFXJsocXg==, tarball: https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-8.0.1.tgz} engines: {node: ^22.18.0 || >=24.11.0} peerDependencies: '@babel/core': ^8.0.0 '@babel/plugin-transform-typescript@7.29.7': - resolution: {integrity: sha512-jK52h8LaLc7JarhQV2ofeFMts4H7vnOXnqZNA6fYglBTZewRBE51KWt3BUltW1P+KoPsYkHoJeXePuz4zo2LMw==} + resolution: {integrity: sha512-jK52h8LaLc7JarhQV2ofeFMts4H7vnOXnqZNA6fYglBTZewRBE51KWt3BUltW1P+KoPsYkHoJeXePuz4zo2LMw==, tarball: https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.29.7.tgz} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 '@babel/plugin-transform-unicode-escapes@7.29.7': - resolution: {integrity: sha512-jCfXxSjf94lf4E0hKE0AByxF6F3/pVFqRdUUNkDJhsY0m1ZKjnN6ZYyMeHNpzflxb/0q5b7t3p+BE+SLF1WOtA==} + resolution: {integrity: sha512-jCfXxSjf94lf4E0hKE0AByxF6F3/pVFqRdUUNkDJhsY0m1ZKjnN6ZYyMeHNpzflxb/0q5b7t3p+BE+SLF1WOtA==, tarball: https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.29.7.tgz} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 '@babel/plugin-transform-unicode-escapes@8.0.1': - resolution: {integrity: sha512-TAXJepIJ6vZphytTwcf+LuXi2M2ZWI43VCqNw+1ZZLPP/38Z1A8j4Mahvg8kqDgMOSM/cakk+hedTJCiw3jQuQ==} + resolution: {integrity: sha512-TAXJepIJ6vZphytTwcf+LuXi2M2ZWI43VCqNw+1ZZLPP/38Z1A8j4Mahvg8kqDgMOSM/cakk+hedTJCiw3jQuQ==, tarball: https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-8.0.1.tgz} engines: {node: ^22.18.0 || >=24.11.0} peerDependencies: '@babel/core': ^8.0.0 '@babel/plugin-transform-unicode-property-regex@7.29.7': - resolution: {integrity: sha512-OgZ+zoAJgZLUCunsTRQ5LAjOywDv5zzZ2/hQ5aMw1pGXyY2rtE8/chXYUmu3AlVHKpm10KEdG9aMwbI/K76ZGw==} + resolution: {integrity: sha512-OgZ+zoAJgZLUCunsTRQ5LAjOywDv5zzZ2/hQ5aMw1pGXyY2rtE8/chXYUmu3AlVHKpm10KEdG9aMwbI/K76ZGw==, tarball: https://registry.npmjs.org/@babel/plugin-transform-unicode-property-regex/-/plugin-transform-unicode-property-regex-7.29.7.tgz} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 '@babel/plugin-transform-unicode-property-regex@8.0.1': - resolution: {integrity: sha512-zjBN9tSMSuomNDfurL69Gf7+v4D2t5uI1mSZaYJDo88SKpbduhCXqtxH7Tx66iCF6caWYwnBzSM0tnCozmQq5Q==} + resolution: {integrity: sha512-zjBN9tSMSuomNDfurL69Gf7+v4D2t5uI1mSZaYJDo88SKpbduhCXqtxH7Tx66iCF6caWYwnBzSM0tnCozmQq5Q==, tarball: https://registry.npmjs.org/@babel/plugin-transform-unicode-property-regex/-/plugin-transform-unicode-property-regex-8.0.1.tgz} engines: {node: ^22.18.0 || >=24.11.0} peerDependencies: '@babel/core': ^8.0.0 '@babel/plugin-transform-unicode-regex@7.29.7': - resolution: {integrity: sha512-7D/x/23/d/3VqZ0QA+LGbZMlGwZjztBygSWWWsfTPoQ1oQ6Q1P6Mr3d0kk42XabyUVw+fha3LqdRsFqeKqvCyA==} + resolution: {integrity: sha512-7D/x/23/d/3VqZ0QA+LGbZMlGwZjztBygSWWWsfTPoQ1oQ6Q1P6Mr3d0kk42XabyUVw+fha3LqdRsFqeKqvCyA==, tarball: https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.29.7.tgz} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 '@babel/plugin-transform-unicode-regex@8.0.1': - resolution: {integrity: sha512-v0oO83cvT5lwbcIVRShpx4vaHD8AvM9IBowsQuTeP+kGmhh3recJQs33Bl6dlo3/2g9amlznLbFGn4VJbPCJqA==} + resolution: {integrity: sha512-v0oO83cvT5lwbcIVRShpx4vaHD8AvM9IBowsQuTeP+kGmhh3recJQs33Bl6dlo3/2g9amlznLbFGn4VJbPCJqA==, tarball: https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-8.0.1.tgz} engines: {node: ^22.18.0 || >=24.11.0} peerDependencies: '@babel/core': ^8.0.0 '@babel/plugin-transform-unicode-sets-regex@7.29.7': - resolution: {integrity: sha512-BLOhLht9DOJwIxlmp91wHvkXv1lguuHS3/FwUO8HL1H0u8s4hR1gASVFyilu9iGtcTRYqjTZmlsFFeQletntEg==} + resolution: {integrity: sha512-BLOhLht9DOJwIxlmp91wHvkXv1lguuHS3/FwUO8HL1H0u8s4hR1gASVFyilu9iGtcTRYqjTZmlsFFeQletntEg==, tarball: https://registry.npmjs.org/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.29.7.tgz} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 '@babel/plugin-transform-unicode-sets-regex@8.0.1': - resolution: {integrity: sha512-MlQeyS0K7gh0XNeLBMS/3Z07HjDOKhA7xm2L18GyxOXyiFHI9E+ZuQ4mFYmcLjluXsE/Wf6dABIqZvKpKw0Z3w==} + resolution: {integrity: sha512-MlQeyS0K7gh0XNeLBMS/3Z07HjDOKhA7xm2L18GyxOXyiFHI9E+ZuQ4mFYmcLjluXsE/Wf6dABIqZvKpKw0Z3w==, tarball: https://registry.npmjs.org/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-8.0.1.tgz} engines: {node: ^22.18.0 || >=24.11.0} peerDependencies: '@babel/core': ^8.0.0 '@babel/preset-env@7.29.7': - resolution: {integrity: sha512-GYzX36n1nsciIb0uyH0GHwxwtNwPQIcpxSeiVLDtG/B7jB5xXgchnmL1f/jCX5o+pwnaDBtO60ONSJhEBJfxYA==} + resolution: {integrity: sha512-GYzX36n1nsciIb0uyH0GHwxwtNwPQIcpxSeiVLDtG/B7jB5xXgchnmL1f/jCX5o+pwnaDBtO60ONSJhEBJfxYA==, tarball: https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.29.7.tgz} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 '@babel/preset-env@8.0.2': - resolution: {integrity: sha512-CUGLn9hNBCF/eXnwdFAWERbniCcXCRvnKwLV9fegeUEIqv7YlU2MepsWMMM54GcILx5XYMnRh+JAL+K5G+mK6g==} + resolution: {integrity: sha512-CUGLn9hNBCF/eXnwdFAWERbniCcXCRvnKwLV9fegeUEIqv7YlU2MepsWMMM54GcILx5XYMnRh+JAL+K5G+mK6g==, tarball: https://registry.npmjs.org/@babel/preset-env/-/preset-env-8.0.2.tgz} engines: {node: ^22.18.0 || >=24.11.0} peerDependencies: '@babel/core': ^8.0.0 '@babel/preset-modules@0.1.6-no-external-plugins': - resolution: {integrity: sha512-HrcgcIESLm9aIR842yhJ5RWan/gebQUJ6E/E5+rf0y9o6oj7w0Br+sWuL6kEQ/o/AdfvR1Je9jG18/gnpwjEyA==} + resolution: {integrity: sha512-HrcgcIESLm9aIR842yhJ5RWan/gebQUJ6E/E5+rf0y9o6oj7w0Br+sWuL6kEQ/o/AdfvR1Je9jG18/gnpwjEyA==, tarball: https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.6-no-external-plugins.tgz} peerDependencies: '@babel/core': ^7.0.0-0 || ^8.0.0-0 <8.0.0 '@babel/preset-modules@0.2.0': - resolution: {integrity: sha512-yz0RBN2fx4fjCeFcTWsWgL7PxSRltvTa0Qg14HkWCU3qS8MO7ZSJlBVbGceynd5C9NsJwwUHNQD3dc6tYO+jqQ==} + resolution: {integrity: sha512-yz0RBN2fx4fjCeFcTWsWgL7PxSRltvTa0Qg14HkWCU3qS8MO7ZSJlBVbGceynd5C9NsJwwUHNQD3dc6tYO+jqQ==, tarball: https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.2.0.tgz} peerDependencies: '@babel/core': ^8.0.0 '@babel/preset-react@7.28.5': - resolution: {integrity: sha512-Z3J8vhRq7CeLjdC58jLv4lnZ5RKFUJWqH5emvxmv9Hv3BD1T9R/Im713R4MTKwvFaV74ejZ3sM01LyEKk4ugNQ==} + resolution: {integrity: sha512-Z3J8vhRq7CeLjdC58jLv4lnZ5RKFUJWqH5emvxmv9Hv3BD1T9R/Im713R4MTKwvFaV74ejZ3sM01LyEKk4ugNQ==, tarball: https://registry.npmjs.org/@babel/preset-react/-/preset-react-7.28.5.tgz} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 '@babel/preset-typescript@7.29.7': - resolution: {integrity: sha512-/Foi8vKY2EVbed/1eZx0gJEEwHAIxogrySI7rULcRIvhZzbvoE/b5qG5Ghc0WKAFKOHA9SD1x7RsFlOYdutIiQ==} + resolution: {integrity: sha512-/Foi8vKY2EVbed/1eZx0gJEEwHAIxogrySI7rULcRIvhZzbvoE/b5qG5Ghc0WKAFKOHA9SD1x7RsFlOYdutIiQ==, tarball: https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.29.7.tgz} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 '@babel/runtime@7.29.7': - resolution: {integrity: sha512-Nq8OhGWiZIZGV6hLHoyAKLLcJihP/xFeBMGJoUrxTX2psI8dCifzLhZISFb+VWS3wFMRDmCGw5R+dOySCqPLhw==} + resolution: {integrity: sha512-Nq8OhGWiZIZGV6hLHoyAKLLcJihP/xFeBMGJoUrxTX2psI8dCifzLhZISFb+VWS3wFMRDmCGw5R+dOySCqPLhw==, tarball: https://registry.npmjs.org/@babel/runtime/-/runtime-7.29.7.tgz} engines: {node: '>=6.9.0'} '@babel/runtime@8.0.0': - resolution: {integrity: sha512-sL6cvO2IfkSu/iU+zs2S/w01B7A8V7suXSIKEN4hPFFdZoiPGxrj5pAG0lCaqLWiEIrjKzdznIWuaLcxPR53qw==} + resolution: {integrity: sha512-sL6cvO2IfkSu/iU+zs2S/w01B7A8V7suXSIKEN4hPFFdZoiPGxrj5pAG0lCaqLWiEIrjKzdznIWuaLcxPR53qw==, tarball: https://registry.npmjs.org/@babel/runtime/-/runtime-8.0.0.tgz} '@babel/template@7.29.7': - resolution: {integrity: sha512-puq+Gf35oI24FeN11LkoUQFqv9uwNeWpxXZi/Ji3rRIoKAzKnxRaZ+Gkj0vKS9ZCiTESfng1N9LyOyXvo+m+Gg==} + resolution: {integrity: sha512-puq+Gf35oI24FeN11LkoUQFqv9uwNeWpxXZi/Ji3rRIoKAzKnxRaZ+Gkj0vKS9ZCiTESfng1N9LyOyXvo+m+Gg==, tarball: https://registry.npmjs.org/@babel/template/-/template-7.29.7.tgz} engines: {node: '>=6.9.0'} '@babel/template@8.0.0': - resolution: {integrity: sha512-eAD0QW/AlbamBbw0FeGiwasbCVPq5ncW0HNVyLP3B9czqLyh4gvw+5JTSNt6le9+ziAU7mqDZsKTHf3jTb4chQ==} + resolution: {integrity: sha512-eAD0QW/AlbamBbw0FeGiwasbCVPq5ncW0HNVyLP3B9czqLyh4gvw+5JTSNt6le9+ziAU7mqDZsKTHf3jTb4chQ==, tarball: https://registry.npmjs.org/@babel/template/-/template-8.0.0.tgz} engines: {node: ^22.18.0 || >=24.11.0} '@babel/traverse@7.29.7': - resolution: {integrity: sha512-EhlfNQtZ+NK22w5BM61ciuiq1m58ed33Wr1Xan//ZRTy6hgjnwyCffRYwzsGXdASJSUJ1guZILsErh1eQcl+zw==} + resolution: {integrity: sha512-EhlfNQtZ+NK22w5BM61ciuiq1m58ed33Wr1Xan//ZRTy6hgjnwyCffRYwzsGXdASJSUJ1guZILsErh1eQcl+zw==, tarball: https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.7.tgz} engines: {node: '>=6.9.0'} '@babel/traverse@8.0.4': - resolution: {integrity: sha512-bZnmqzGG8UZneG1lLxBoWIH0G6Gr1D846Yu4/3XnY6FhCndMR49u26nTY08u/dAxWmLWF9vGQOuC+84FfIUoeg==} + resolution: {integrity: sha512-bZnmqzGG8UZneG1lLxBoWIH0G6Gr1D846Yu4/3XnY6FhCndMR49u26nTY08u/dAxWmLWF9vGQOuC+84FfIUoeg==, tarball: https://registry.npmjs.org/@babel/traverse/-/traverse-8.0.4.tgz} engines: {node: ^22.18.0 || >=24.11.0} '@babel/types@7.29.7': - resolution: {integrity: sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA==} + resolution: {integrity: sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA==, tarball: https://registry.npmjs.org/@babel/types/-/types-7.29.7.tgz} engines: {node: '>=6.9.0'} '@babel/types@8.0.4': - resolution: {integrity: sha512-eY+Yn3dCqTGmyiq2QRU66lA5FL8lqqqvecHt0fF3uHONIa7ToYsaCiWV8lOKqAs0Rb2SjixiKFROngnulPtt2g==} + resolution: {integrity: sha512-eY+Yn3dCqTGmyiq2QRU66lA5FL8lqqqvecHt0fF3uHONIa7ToYsaCiWV8lOKqAs0Rb2SjixiKFROngnulPtt2g==, tarball: https://registry.npmjs.org/@babel/types/-/types-8.0.4.tgz} engines: {node: ^22.18.0 || >=24.11.0} '@bcoe/v8-coverage@0.2.3': - resolution: {integrity: sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==} + resolution: {integrity: sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==, tarball: https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz} '@bcoe/v8-coverage@1.0.2': - resolution: {integrity: sha512-6zABk/ECA/QYSCQ1NGiVwwbQerUCZ+TQbp64Q3AgmfNvurHH0j8TtXa1qbShXA6qqkpAj4V5W8pP6mLe1mcMqA==} + resolution: {integrity: sha512-6zABk/ECA/QYSCQ1NGiVwwbQerUCZ+TQbp64Q3AgmfNvurHH0j8TtXa1qbShXA6qqkpAj4V5W8pP6mLe1mcMqA==, tarball: https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-1.0.2.tgz} engines: {node: '>=18'} '@braintree/sanitize-url@7.1.2': - resolution: {integrity: sha512-jigsZK+sMF/cuiB7sERuo9V7N9jx+dhmHHnQyDSVdpZwVutaBu7WvNYqMDLSgFgfB30n452TP3vjDAvFC973mA==} + resolution: {integrity: sha512-jigsZK+sMF/cuiB7sERuo9V7N9jx+dhmHHnQyDSVdpZwVutaBu7WvNYqMDLSgFgfB30n452TP3vjDAvFC973mA==, tarball: https://registry.npmjs.org/@braintree/sanitize-url/-/sanitize-url-7.1.2.tgz} '@bramus/specificity@2.4.2': - resolution: {integrity: sha512-ctxtJ/eA+t+6q2++vj5j7FYX3nRu311q1wfYH3xjlLOsczhlhxAg2FWNUXhpGvAw3BWo1xBcvOV6/YLc2r5FJw==} + resolution: {integrity: sha512-ctxtJ/eA+t+6q2++vj5j7FYX3nRu311q1wfYH3xjlLOsczhlhxAg2FWNUXhpGvAw3BWo1xBcvOV6/YLc2r5FJw==, tarball: https://registry.npmjs.org/@bramus/specificity/-/specificity-2.4.2.tgz} hasBin: true '@bufbuild/protobuf@2.12.1': - resolution: {integrity: sha512-BvAMfS6LrgZiryOAZ4pBYucu4wG/Ei/9o9DZ9akbREnMLbPJiom2i8b9C8IsKErQoiKqVhrerzt3kOT/RrzLHg==} + resolution: {integrity: sha512-BvAMfS6LrgZiryOAZ4pBYucu4wG/Ei/9o9DZ9akbREnMLbPJiom2i8b9C8IsKErQoiKqVhrerzt3kOT/RrzLHg==, tarball: https://registry.npmjs.org/@bufbuild/protobuf/-/protobuf-2.12.1.tgz} '@chevrotain/types@11.1.2': - resolution: {integrity: sha512-U+HFai5+zmJCkK86QsaJtoITlboZHBqrVketcO2ROv865xfCMSFpELQoz1GkX5GzME8pTa+3kbKrZHQtI0gdbw==} + resolution: {integrity: sha512-U+HFai5+zmJCkK86QsaJtoITlboZHBqrVketcO2ROv865xfCMSFpELQoz1GkX5GzME8pTa+3kbKrZHQtI0gdbw==, tarball: https://registry.npmjs.org/@chevrotain/types/-/types-11.1.2.tgz} '@colordx/core@5.5.0': - resolution: {integrity: sha512-3PxTH8itZzltK0U9jTwVVnjLXvnDYuq3m+QXsHkENxWiPRh4WaoLcs1SQjqgZ55kS+QyirpH5BVwzP2gMVG6EQ==} + resolution: {integrity: sha512-3PxTH8itZzltK0U9jTwVVnjLXvnDYuq3m+QXsHkENxWiPRh4WaoLcs1SQjqgZ55kS+QyirpH5BVwzP2gMVG6EQ==, tarball: https://registry.npmjs.org/@colordx/core/-/core-5.5.0.tgz} '@cspotcode/source-map-support@0.8.1': - resolution: {integrity: sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==} + resolution: {integrity: sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==, tarball: https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz} engines: {node: '>=12'} '@csstools/color-helpers@6.1.0': - resolution: {integrity: sha512-064IFJdjTfUqnjpCVpMOdbr8FLQBhinbZj6yRv2An2E41O/pLEXqfFRWqGq/SxlE5PEUYTlvWsG2r8MswAVvkg==} + resolution: {integrity: sha512-064IFJdjTfUqnjpCVpMOdbr8FLQBhinbZj6yRv2An2E41O/pLEXqfFRWqGq/SxlE5PEUYTlvWsG2r8MswAVvkg==, tarball: https://registry.npmjs.org/@csstools/color-helpers/-/color-helpers-6.1.0.tgz} engines: {node: '>=20.19.0'} '@csstools/css-calc@3.2.1': - resolution: {integrity: sha512-DtdHlgXh5ZkA43cwBcAm+huzgJiwx3ZTWVjBs94kwz2xKqSimDA3lBgCjphYgwgVUMWatSM0pDd8TILB1yrVVg==} + resolution: {integrity: sha512-DtdHlgXh5ZkA43cwBcAm+huzgJiwx3ZTWVjBs94kwz2xKqSimDA3lBgCjphYgwgVUMWatSM0pDd8TILB1yrVVg==, tarball: https://registry.npmjs.org/@csstools/css-calc/-/css-calc-3.2.1.tgz} engines: {node: '>=20.19.0'} peerDependencies: '@csstools/css-parser-algorithms': ^4.0.0 '@csstools/css-tokenizer': ^4.0.0 '@csstools/css-color-parser@4.1.9': - resolution: {integrity: sha512-paQcIaOO53Rk5+YrBaBjm/SgrV4INImjo2BT1DtQRYr+XeTRbeAYlS+jxXp9drqvKmtFnWRJKIalDLhZZDu42A==} + resolution: {integrity: sha512-paQcIaOO53Rk5+YrBaBjm/SgrV4INImjo2BT1DtQRYr+XeTRbeAYlS+jxXp9drqvKmtFnWRJKIalDLhZZDu42A==, tarball: https://registry.npmjs.org/@csstools/css-color-parser/-/css-color-parser-4.1.9.tgz} engines: {node: '>=20.19.0'} peerDependencies: '@csstools/css-parser-algorithms': ^4.0.0 '@csstools/css-tokenizer': ^4.0.0 '@csstools/css-parser-algorithms@4.0.0': - resolution: {integrity: sha512-+B87qS7fIG3L5h3qwJ/IFbjoVoOe/bpOdh9hAjXbvx0o8ImEmUsGXN0inFOnk2ChCFgqkkGFQ+TpM5rbhkKe4w==} + resolution: {integrity: sha512-+B87qS7fIG3L5h3qwJ/IFbjoVoOe/bpOdh9hAjXbvx0o8ImEmUsGXN0inFOnk2ChCFgqkkGFQ+TpM5rbhkKe4w==, tarball: https://registry.npmjs.org/@csstools/css-parser-algorithms/-/css-parser-algorithms-4.0.0.tgz} engines: {node: '>=20.19.0'} peerDependencies: '@csstools/css-tokenizer': ^4.0.0 '@csstools/css-syntax-patches-for-csstree@1.1.6': - resolution: {integrity: sha512-TcJCWFbXLPpJYq6z7bfOyjWYJDiDg2/I4gyUC9pqPNqHFRIey0EB0q0L5cSnQDfWJg8Jd6VadakxdIez/3zkqQ==} + resolution: {integrity: sha512-TcJCWFbXLPpJYq6z7bfOyjWYJDiDg2/I4gyUC9pqPNqHFRIey0EB0q0L5cSnQDfWJg8Jd6VadakxdIez/3zkqQ==, tarball: https://registry.npmjs.org/@csstools/css-syntax-patches-for-csstree/-/css-syntax-patches-for-csstree-1.1.6.tgz} peerDependencies: css-tree: ^3.2.1 peerDependenciesMeta: @@ -2429,838 +2443,838 @@ packages: optional: true '@csstools/css-tokenizer@4.0.0': - resolution: {integrity: sha512-QxULHAm7cNu72w97JUNCBFODFaXpbDg+dP8b/oWFAZ2MTRppA3U00Y2L1HqaS4J6yBqxwa/Y3nMBaxVKbB/NsA==} + resolution: {integrity: sha512-QxULHAm7cNu72w97JUNCBFODFaXpbDg+dP8b/oWFAZ2MTRppA3U00Y2L1HqaS4J6yBqxwa/Y3nMBaxVKbB/NsA==, tarball: https://registry.npmjs.org/@csstools/css-tokenizer/-/css-tokenizer-4.0.0.tgz} engines: {node: '>=20.19.0'} '@ctrl/tinycolor@3.1.7': - resolution: {integrity: sha512-/0C6fjXbCwu22k8mMsKRSAo9zgu61d2p75Or9IuIC0Vu5CWN88t2QHK93LhNnxnqHWf5SFwFU28w9cKfTmnfvg==} + resolution: {integrity: sha512-/0C6fjXbCwu22k8mMsKRSAo9zgu61d2p75Or9IuIC0Vu5CWN88t2QHK93LhNnxnqHWf5SFwFU28w9cKfTmnfvg==, tarball: https://registry.npmjs.org/@ctrl/tinycolor/-/tinycolor-3.1.7.tgz} engines: {node: '>=10'} '@date-fns/tz@1.4.1': - resolution: {integrity: sha512-P5LUNhtbj6YfI3iJjw5EL9eUAG6OitD0W3fWQcpQjDRc/QIsL0tRNuO1PcDvPccWL1fSTXXdE1ds+l95DV/OFA==} + resolution: {integrity: sha512-P5LUNhtbj6YfI3iJjw5EL9eUAG6OitD0W3fWQcpQjDRc/QIsL0tRNuO1PcDvPccWL1fSTXXdE1ds+l95DV/OFA==, tarball: https://registry.npmjs.org/@date-fns/tz/-/tz-1.4.1.tgz} '@discoveryjs/json-ext@1.1.0': - resolution: {integrity: sha512-Xc3VhU02wqZ1HvHRJUwL09HkZSTvidqY5Ya0NXBSYOxAp+Ln9dcJr9fySI+CkONzP3PekQo9WdzCv0PGER/mOA==} + resolution: {integrity: sha512-Xc3VhU02wqZ1HvHRJUwL09HkZSTvidqY5Ya0NXBSYOxAp+Ln9dcJr9fySI+CkONzP3PekQo9WdzCv0PGER/mOA==, tarball: https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-1.1.0.tgz} engines: {node: '>=14.17.0'} '@emnapi/core@1.10.0': - resolution: {integrity: sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw==} + resolution: {integrity: sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw==, tarball: https://registry.npmjs.org/@emnapi/core/-/core-1.10.0.tgz} '@emnapi/core@1.11.1': - resolution: {integrity: sha512-RSvbQmHzdKzNsLYa/wHrbc3KN4sYLKAdPZxqiM2HATqv/SBk2/ENSHpvXGaLOMcsAyz0poEGqkmmKYG3OWiJEQ==} + resolution: {integrity: sha512-RSvbQmHzdKzNsLYa/wHrbc3KN4sYLKAdPZxqiM2HATqv/SBk2/ENSHpvXGaLOMcsAyz0poEGqkmmKYG3OWiJEQ==, tarball: https://registry.npmjs.org/@emnapi/core/-/core-1.11.1.tgz} '@emnapi/core@1.11.2': - resolution: {integrity: sha512-TC8MkTuZUtcTSiFeuC0ksCh9QIJ5+F21MvZ4Wn4ORfYaFJ/0dsiudv5tVkejgwZlwQ39jL9WWDe2lz8x0WglOA==} + resolution: {integrity: sha512-TC8MkTuZUtcTSiFeuC0ksCh9QIJ5+F21MvZ4Wn4ORfYaFJ/0dsiudv5tVkejgwZlwQ39jL9WWDe2lz8x0WglOA==, tarball: https://registry.npmjs.org/@emnapi/core/-/core-1.11.2.tgz} '@emnapi/core@1.4.5': - resolution: {integrity: sha512-XsLw1dEOpkSX/WucdqUhPWP7hDxSvZiY+fsUC14h+FtQ2Ifni4znbBt8punRX+Uj2JG/uDb8nEHVKvrVlvdZ5Q==} + resolution: {integrity: sha512-XsLw1dEOpkSX/WucdqUhPWP7hDxSvZiY+fsUC14h+FtQ2Ifni4znbBt8punRX+Uj2JG/uDb8nEHVKvrVlvdZ5Q==, tarball: https://registry.npmjs.org/@emnapi/core/-/core-1.4.5.tgz} '@emnapi/runtime@1.10.0': - resolution: {integrity: sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA==} + resolution: {integrity: sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA==, tarball: https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.10.0.tgz} '@emnapi/runtime@1.11.1': - resolution: {integrity: sha512-vgj7R3y3Wgx24IQaGPA/R6YFXLHVMOZ0uVEyIQPaWs+rd1AzfEMXlAC22FYwO1XkKR6NPsq7mUandH8oIRdZFw==} + resolution: {integrity: sha512-vgj7R3y3Wgx24IQaGPA/R6YFXLHVMOZ0uVEyIQPaWs+rd1AzfEMXlAC22FYwO1XkKR6NPsq7mUandH8oIRdZFw==, tarball: https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.11.1.tgz} '@emnapi/runtime@1.11.2': - resolution: {integrity: sha512-kyOl3X0DuTiT1h2ft8r2fYO8JYtU9a9Xis/zBSiGArNaagCOWx90N1k2wxp18czFDH+OgcWGb5ZP/XMt3dcyPA==} + resolution: {integrity: sha512-kyOl3X0DuTiT1h2ft8r2fYO8JYtU9a9Xis/zBSiGArNaagCOWx90N1k2wxp18czFDH+OgcWGb5ZP/XMt3dcyPA==, tarball: https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.11.2.tgz} '@emnapi/runtime@1.4.5': - resolution: {integrity: sha512-++LApOtY0pEEz1zrd9vy1/zXVaVJJ/EbAF3u0fXIzPJEDtnITsBGbbK0EkM72amhl/R5b+5xx0Y/QhcVOpuulg==} + resolution: {integrity: sha512-++LApOtY0pEEz1zrd9vy1/zXVaVJJ/EbAF3u0fXIzPJEDtnITsBGbbK0EkM72amhl/R5b+5xx0Y/QhcVOpuulg==, tarball: https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.4.5.tgz} '@emnapi/wasi-threads@1.0.4': - resolution: {integrity: sha512-PJR+bOmMOPH8AtcTGAyYNiuJ3/Fcoj2XN/gBEWzDIKh254XO+mM9XoXHk5GNEhodxeMznbg7BlRojVbKN+gC6g==} + resolution: {integrity: sha512-PJR+bOmMOPH8AtcTGAyYNiuJ3/Fcoj2XN/gBEWzDIKh254XO+mM9XoXHk5GNEhodxeMznbg7BlRojVbKN+gC6g==, tarball: https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.0.4.tgz} '@emnapi/wasi-threads@1.2.1': - resolution: {integrity: sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==} + resolution: {integrity: sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==, tarball: https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.1.tgz} '@emnapi/wasi-threads@1.2.2': - resolution: {integrity: sha512-c95qOXkHdydNKhscBTebqEC1CVAZpyqOfVfBzQ1qgzyl3gfeldUjIggDbIZgDKsHLgnsM+igH7TJ/eAasaVuMA==} + resolution: {integrity: sha512-c95qOXkHdydNKhscBTebqEC1CVAZpyqOfVfBzQ1qgzyl3gfeldUjIggDbIZgDKsHLgnsM+igH7TJ/eAasaVuMA==, tarball: https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.2.tgz} '@emoji-mart/data@1.2.1': - resolution: {integrity: sha512-no2pQMWiBy6gpBEiqGeU77/bFejDqUTRY7KX+0+iur13op3bqUsXdnwoZs6Xb1zbv0gAj5VvS1PWoUUckSr5Dw==} + resolution: {integrity: sha512-no2pQMWiBy6gpBEiqGeU77/bFejDqUTRY7KX+0+iur13op3bqUsXdnwoZs6Xb1zbv0gAj5VvS1PWoUUckSr5Dw==, tarball: https://registry.npmjs.org/@emoji-mart/data/-/data-1.2.1.tgz} '@esbuild/aix-ppc64@0.23.1': - resolution: {integrity: sha512-6VhYk1diRqrhBAqpJEdjASR/+WVRtfjpqKuNw11cLiaWpAT/Uu+nokB+UJnevzy/P9C/ty6AOe0dwueMrGh/iQ==} + resolution: {integrity: sha512-6VhYk1diRqrhBAqpJEdjASR/+WVRtfjpqKuNw11cLiaWpAT/Uu+nokB+UJnevzy/P9C/ty6AOe0dwueMrGh/iQ==, tarball: https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.23.1.tgz} engines: {node: '>=18'} cpu: [ppc64] os: [aix] '@esbuild/aix-ppc64@0.25.5': - resolution: {integrity: sha512-9o3TMmpmftaCMepOdA5k/yDw8SfInyzWWTjYTFCX3kPSDJMROQTb8jg+h9Cnwnmm1vOzvxN7gIfB5V2ewpjtGA==} + resolution: {integrity: sha512-9o3TMmpmftaCMepOdA5k/yDw8SfInyzWWTjYTFCX3kPSDJMROQTb8jg+h9Cnwnmm1vOzvxN7gIfB5V2ewpjtGA==, tarball: https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.5.tgz} engines: {node: '>=18'} cpu: [ppc64] os: [aix] '@esbuild/aix-ppc64@0.27.7': - resolution: {integrity: sha512-EKX3Qwmhz1eMdEJokhALr0YiD0lhQNwDqkPYyPhiSwKrh7/4KRjQc04sZ8db+5DVVnZ1LmbNDI1uAMPEUBnQPg==} + resolution: {integrity: sha512-EKX3Qwmhz1eMdEJokhALr0YiD0lhQNwDqkPYyPhiSwKrh7/4KRjQc04sZ8db+5DVVnZ1LmbNDI1uAMPEUBnQPg==, tarball: https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.7.tgz} engines: {node: '>=18'} cpu: [ppc64] os: [aix] '@esbuild/aix-ppc64@0.28.1': - resolution: {integrity: sha512-Svl7tq8k/08+p6CXPpRjQ1fKX+1odH/BQbb48fV6fj3CWHhsoIOoY87w1oHXm0qEpkIK3ZfVgp0hed3XBXzXMQ==} + resolution: {integrity: sha512-Svl7tq8k/08+p6CXPpRjQ1fKX+1odH/BQbb48fV6fj3CWHhsoIOoY87w1oHXm0qEpkIK3ZfVgp0hed3XBXzXMQ==, tarball: https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.28.1.tgz} engines: {node: '>=18'} cpu: [ppc64] os: [aix] '@esbuild/android-arm64@0.19.2': - resolution: {integrity: sha512-lsB65vAbe90I/Qe10OjkmrdxSX4UJDjosDgb8sZUKcg3oefEuW2OT2Vozz8ef7wrJbMcmhvCC+hciF8jY/uAkw==} + resolution: {integrity: sha512-lsB65vAbe90I/Qe10OjkmrdxSX4UJDjosDgb8sZUKcg3oefEuW2OT2Vozz8ef7wrJbMcmhvCC+hciF8jY/uAkw==, tarball: https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.19.2.tgz} engines: {node: '>=12'} cpu: [arm64] os: [android] '@esbuild/android-arm64@0.23.1': - resolution: {integrity: sha512-xw50ipykXcLstLeWH7WRdQuysJqejuAGPd30vd1i5zSyKK3WE+ijzHmLKxdiCMtH1pHz78rOg0BKSYOSB/2Khw==} + resolution: {integrity: sha512-xw50ipykXcLstLeWH7WRdQuysJqejuAGPd30vd1i5zSyKK3WE+ijzHmLKxdiCMtH1pHz78rOg0BKSYOSB/2Khw==, tarball: https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.23.1.tgz} engines: {node: '>=18'} cpu: [arm64] os: [android] '@esbuild/android-arm64@0.25.5': - resolution: {integrity: sha512-VGzGhj4lJO+TVGV1v8ntCZWJktV7SGCs3Pn1GRWI1SBFtRALoomm8k5E9Pmwg3HOAal2VDc2F9+PM/rEY6oIDg==} + resolution: {integrity: sha512-VGzGhj4lJO+TVGV1v8ntCZWJktV7SGCs3Pn1GRWI1SBFtRALoomm8k5E9Pmwg3HOAal2VDc2F9+PM/rEY6oIDg==, tarball: https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.5.tgz} engines: {node: '>=18'} cpu: [arm64] os: [android] '@esbuild/android-arm64@0.27.7': - resolution: {integrity: sha512-62dPZHpIXzvChfvfLJow3q5dDtiNMkwiRzPylSCfriLvZeq0a1bWChrGx/BbUbPwOrsWKMn8idSllklzBy+dgQ==} + resolution: {integrity: sha512-62dPZHpIXzvChfvfLJow3q5dDtiNMkwiRzPylSCfriLvZeq0a1bWChrGx/BbUbPwOrsWKMn8idSllklzBy+dgQ==, tarball: https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.7.tgz} engines: {node: '>=18'} cpu: [arm64] os: [android] '@esbuild/android-arm64@0.28.1': - resolution: {integrity: sha512-34EGEbCIAgosYz6goLcopX6Mo7NyGv9tfwEM2/7Ce2VcVRk568iSvniGWcUXIy7wEDR1wzolcxcriFVrWYcwBg==} + resolution: {integrity: sha512-34EGEbCIAgosYz6goLcopX6Mo7NyGv9tfwEM2/7Ce2VcVRk568iSvniGWcUXIy7wEDR1wzolcxcriFVrWYcwBg==, tarball: https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.28.1.tgz} engines: {node: '>=18'} cpu: [arm64] os: [android] '@esbuild/android-arm@0.19.2': - resolution: {integrity: sha512-tM8yLeYVe7pRyAu9VMi/Q7aunpLwD139EY1S99xbQkT4/q2qa6eA4ige/WJQYdJ8GBL1K33pPFhPfPdJ/WzT8Q==} + resolution: {integrity: sha512-tM8yLeYVe7pRyAu9VMi/Q7aunpLwD139EY1S99xbQkT4/q2qa6eA4ige/WJQYdJ8GBL1K33pPFhPfPdJ/WzT8Q==, tarball: https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.19.2.tgz} engines: {node: '>=12'} cpu: [arm] os: [android] '@esbuild/android-arm@0.23.1': - resolution: {integrity: sha512-uz6/tEy2IFm9RYOyvKl88zdzZfwEfKZmnX9Cj1BHjeSGNuGLuMD1kR8y5bteYmwqKm1tj8m4cb/aKEorr6fHWQ==} + resolution: {integrity: sha512-uz6/tEy2IFm9RYOyvKl88zdzZfwEfKZmnX9Cj1BHjeSGNuGLuMD1kR8y5bteYmwqKm1tj8m4cb/aKEorr6fHWQ==, tarball: https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.23.1.tgz} engines: {node: '>=18'} cpu: [arm] os: [android] '@esbuild/android-arm@0.25.5': - resolution: {integrity: sha512-AdJKSPeEHgi7/ZhuIPtcQKr5RQdo6OO2IL87JkianiMYMPbCtot9fxPbrMiBADOWWm3T2si9stAiVsGbTQFkbA==} + resolution: {integrity: sha512-AdJKSPeEHgi7/ZhuIPtcQKr5RQdo6OO2IL87JkianiMYMPbCtot9fxPbrMiBADOWWm3T2si9stAiVsGbTQFkbA==, tarball: https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.5.tgz} engines: {node: '>=18'} cpu: [arm] os: [android] '@esbuild/android-arm@0.27.7': - resolution: {integrity: sha512-jbPXvB4Yj2yBV7HUfE2KHe4GJX51QplCN1pGbYjvsyCZbQmies29EoJbkEc+vYuU5o45AfQn37vZlyXy4YJ8RQ==} + resolution: {integrity: sha512-jbPXvB4Yj2yBV7HUfE2KHe4GJX51QplCN1pGbYjvsyCZbQmies29EoJbkEc+vYuU5o45AfQn37vZlyXy4YJ8RQ==, tarball: https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.7.tgz} engines: {node: '>=18'} cpu: [arm] os: [android] '@esbuild/android-arm@0.28.1': - resolution: {integrity: sha512-0k2F129Xdio1TdJfzJ8sy1Q47vUD2NnwdhiAf7drUN1EBTfPf4hsFCtmMgu/6m8JSzsBrlmVjudMBQqOfG8usQ==} + resolution: {integrity: sha512-0k2F129Xdio1TdJfzJ8sy1Q47vUD2NnwdhiAf7drUN1EBTfPf4hsFCtmMgu/6m8JSzsBrlmVjudMBQqOfG8usQ==, tarball: https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.28.1.tgz} engines: {node: '>=18'} cpu: [arm] os: [android] '@esbuild/android-x64@0.19.2': - resolution: {integrity: sha512-qK/TpmHt2M/Hg82WXHRc/W/2SGo/l1thtDHZWqFq7oi24AjZ4O/CpPSu6ZuYKFkEgmZlFoa7CooAyYmuvnaG8w==} + resolution: {integrity: sha512-qK/TpmHt2M/Hg82WXHRc/W/2SGo/l1thtDHZWqFq7oi24AjZ4O/CpPSu6ZuYKFkEgmZlFoa7CooAyYmuvnaG8w==, tarball: https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.19.2.tgz} engines: {node: '>=12'} cpu: [x64] os: [android] '@esbuild/android-x64@0.23.1': - resolution: {integrity: sha512-nlN9B69St9BwUoB+jkyU090bru8L0NA3yFvAd7k8dNsVH8bi9a8cUAUSEcEEgTp2z3dbEDGJGfP6VUnkQnlReg==} + resolution: {integrity: sha512-nlN9B69St9BwUoB+jkyU090bru8L0NA3yFvAd7k8dNsVH8bi9a8cUAUSEcEEgTp2z3dbEDGJGfP6VUnkQnlReg==, tarball: https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.23.1.tgz} engines: {node: '>=18'} cpu: [x64] os: [android] '@esbuild/android-x64@0.25.5': - resolution: {integrity: sha512-D2GyJT1kjvO//drbRT3Hib9XPwQeWd9vZoBJn+bu/lVsOZ13cqNdDeqIF/xQ5/VmWvMduP6AmXvylO/PIc2isw==} + resolution: {integrity: sha512-D2GyJT1kjvO//drbRT3Hib9XPwQeWd9vZoBJn+bu/lVsOZ13cqNdDeqIF/xQ5/VmWvMduP6AmXvylO/PIc2isw==, tarball: https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.5.tgz} engines: {node: '>=18'} cpu: [x64] os: [android] '@esbuild/android-x64@0.27.7': - resolution: {integrity: sha512-x5VpMODneVDb70PYV2VQOmIUUiBtY3D3mPBG8NxVk5CogneYhkR7MmM3yR/uMdITLrC1ml/NV1rj4bMJuy9MCg==} + resolution: {integrity: sha512-x5VpMODneVDb70PYV2VQOmIUUiBtY3D3mPBG8NxVk5CogneYhkR7MmM3yR/uMdITLrC1ml/NV1rj4bMJuy9MCg==, tarball: https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.7.tgz} engines: {node: '>=18'} cpu: [x64] os: [android] '@esbuild/android-x64@0.28.1': - resolution: {integrity: sha512-dbwY7ltSMDWsRatcRpCnES4F+im88OCUgGZjy52shC7GqHRE/cYlxNbB4Z4UpJswpcc4Qxd2oE/ufM0p61IKng==} + resolution: {integrity: sha512-dbwY7ltSMDWsRatcRpCnES4F+im88OCUgGZjy52shC7GqHRE/cYlxNbB4Z4UpJswpcc4Qxd2oE/ufM0p61IKng==, tarball: https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.28.1.tgz} engines: {node: '>=18'} cpu: [x64] os: [android] '@esbuild/darwin-arm64@0.19.2': - resolution: {integrity: sha512-Ora8JokrvrzEPEpZO18ZYXkH4asCdc1DLdcVy8TGf5eWtPO1Ie4WroEJzwI52ZGtpODy3+m0a2yEX9l+KUn0tA==} + resolution: {integrity: sha512-Ora8JokrvrzEPEpZO18ZYXkH4asCdc1DLdcVy8TGf5eWtPO1Ie4WroEJzwI52ZGtpODy3+m0a2yEX9l+KUn0tA==, tarball: https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.19.2.tgz} engines: {node: '>=12'} cpu: [arm64] os: [darwin] '@esbuild/darwin-arm64@0.23.1': - resolution: {integrity: sha512-YsS2e3Wtgnw7Wq53XXBLcV6JhRsEq8hkfg91ESVadIrzr9wO6jJDMZnCQbHm1Guc5t/CdDiFSSfWP58FNuvT3Q==} + resolution: {integrity: sha512-YsS2e3Wtgnw7Wq53XXBLcV6JhRsEq8hkfg91ESVadIrzr9wO6jJDMZnCQbHm1Guc5t/CdDiFSSfWP58FNuvT3Q==, tarball: https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.23.1.tgz} engines: {node: '>=18'} cpu: [arm64] os: [darwin] '@esbuild/darwin-arm64@0.25.5': - resolution: {integrity: sha512-GtaBgammVvdF7aPIgH2jxMDdivezgFu6iKpmT+48+F8Hhg5J/sfnDieg0aeG/jfSvkYQU2/pceFPDKlqZzwnfQ==} + resolution: {integrity: sha512-GtaBgammVvdF7aPIgH2jxMDdivezgFu6iKpmT+48+F8Hhg5J/sfnDieg0aeG/jfSvkYQU2/pceFPDKlqZzwnfQ==, tarball: https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.5.tgz} engines: {node: '>=18'} cpu: [arm64] os: [darwin] '@esbuild/darwin-arm64@0.27.7': - resolution: {integrity: sha512-5lckdqeuBPlKUwvoCXIgI2D9/ABmPq3Rdp7IfL70393YgaASt7tbju3Ac+ePVi3KDH6N2RqePfHnXkaDtY9fkw==} + resolution: {integrity: sha512-5lckdqeuBPlKUwvoCXIgI2D9/ABmPq3Rdp7IfL70393YgaASt7tbju3Ac+ePVi3KDH6N2RqePfHnXkaDtY9fkw==, tarball: https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.7.tgz} engines: {node: '>=18'} cpu: [arm64] os: [darwin] '@esbuild/darwin-arm64@0.28.1': - resolution: {integrity: sha512-TZbWkQY7kvTAXbXUT7uVACR5cMHsDiSz9z7ZKAX/RTq/WJEk3QyRr0wZpNhBDX+/0CtdqUIJlOiodQcta6tY3Q==} + resolution: {integrity: sha512-TZbWkQY7kvTAXbXUT7uVACR5cMHsDiSz9z7ZKAX/RTq/WJEk3QyRr0wZpNhBDX+/0CtdqUIJlOiodQcta6tY3Q==, tarball: https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.28.1.tgz} engines: {node: '>=18'} cpu: [arm64] os: [darwin] '@esbuild/darwin-x64@0.19.2': - resolution: {integrity: sha512-tP+B5UuIbbFMj2hQaUr6EALlHOIOmlLM2FK7jeFBobPy2ERdohI4Ka6ZFjZ1ZYsrHE/hZimGuU90jusRE0pwDw==} + resolution: {integrity: sha512-tP+B5UuIbbFMj2hQaUr6EALlHOIOmlLM2FK7jeFBobPy2ERdohI4Ka6ZFjZ1ZYsrHE/hZimGuU90jusRE0pwDw==, tarball: https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.19.2.tgz} engines: {node: '>=12'} cpu: [x64] os: [darwin] '@esbuild/darwin-x64@0.23.1': - resolution: {integrity: sha512-aClqdgTDVPSEGgoCS8QDG37Gu8yc9lTHNAQlsztQ6ENetKEO//b8y31MMu2ZaPbn4kVsIABzVLXYLhCGekGDqw==} + resolution: {integrity: sha512-aClqdgTDVPSEGgoCS8QDG37Gu8yc9lTHNAQlsztQ6ENetKEO//b8y31MMu2ZaPbn4kVsIABzVLXYLhCGekGDqw==, tarball: https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.23.1.tgz} engines: {node: '>=18'} cpu: [x64] os: [darwin] '@esbuild/darwin-x64@0.25.5': - resolution: {integrity: sha512-1iT4FVL0dJ76/q1wd7XDsXrSW+oLoquptvh4CLR4kITDtqi2e/xwXwdCVH8hVHU43wgJdsq7Gxuzcs6Iq/7bxQ==} + resolution: {integrity: sha512-1iT4FVL0dJ76/q1wd7XDsXrSW+oLoquptvh4CLR4kITDtqi2e/xwXwdCVH8hVHU43wgJdsq7Gxuzcs6Iq/7bxQ==, tarball: https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.5.tgz} engines: {node: '>=18'} cpu: [x64] os: [darwin] '@esbuild/darwin-x64@0.27.7': - resolution: {integrity: sha512-rYnXrKcXuT7Z+WL5K980jVFdvVKhCHhUwid+dDYQpH+qu+TefcomiMAJpIiC2EM3Rjtq0sO3StMV/+3w3MyyqQ==} + resolution: {integrity: sha512-rYnXrKcXuT7Z+WL5K980jVFdvVKhCHhUwid+dDYQpH+qu+TefcomiMAJpIiC2EM3Rjtq0sO3StMV/+3w3MyyqQ==, tarball: https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.7.tgz} engines: {node: '>=18'} cpu: [x64] os: [darwin] '@esbuild/darwin-x64@0.28.1': - resolution: {integrity: sha512-zfdzgK9ACBNZLI/CyHTOx81SyNbM6YXn7rxSgX97VjyiPl9W1i4Ka4fgKECEoFCKGpvBj5qArWIGgQjOwkgskQ==} + resolution: {integrity: sha512-zfdzgK9ACBNZLI/CyHTOx81SyNbM6YXn7rxSgX97VjyiPl9W1i4Ka4fgKECEoFCKGpvBj5qArWIGgQjOwkgskQ==, tarball: https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.28.1.tgz} engines: {node: '>=18'} cpu: [x64] os: [darwin] '@esbuild/freebsd-arm64@0.19.2': - resolution: {integrity: sha512-YbPY2kc0acfzL1VPVK6EnAlig4f+l8xmq36OZkU0jzBVHcOTyQDhnKQaLzZudNJQyymd9OqQezeaBgkTGdTGeQ==} + resolution: {integrity: sha512-YbPY2kc0acfzL1VPVK6EnAlig4f+l8xmq36OZkU0jzBVHcOTyQDhnKQaLzZudNJQyymd9OqQezeaBgkTGdTGeQ==, tarball: https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.19.2.tgz} engines: {node: '>=12'} cpu: [arm64] os: [freebsd] '@esbuild/freebsd-arm64@0.23.1': - resolution: {integrity: sha512-h1k6yS8/pN/NHlMl5+v4XPfikhJulk4G+tKGFIOwURBSFzE8bixw1ebjluLOjfwtLqY0kewfjLSrO6tN2MgIhA==} + resolution: {integrity: sha512-h1k6yS8/pN/NHlMl5+v4XPfikhJulk4G+tKGFIOwURBSFzE8bixw1ebjluLOjfwtLqY0kewfjLSrO6tN2MgIhA==, tarball: https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.23.1.tgz} engines: {node: '>=18'} cpu: [arm64] os: [freebsd] '@esbuild/freebsd-arm64@0.25.5': - resolution: {integrity: sha512-nk4tGP3JThz4La38Uy/gzyXtpkPW8zSAmoUhK9xKKXdBCzKODMc2adkB2+8om9BDYugz+uGV7sLmpTYzvmz6Sw==} + resolution: {integrity: sha512-nk4tGP3JThz4La38Uy/gzyXtpkPW8zSAmoUhK9xKKXdBCzKODMc2adkB2+8om9BDYugz+uGV7sLmpTYzvmz6Sw==, tarball: https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.5.tgz} engines: {node: '>=18'} cpu: [arm64] os: [freebsd] '@esbuild/freebsd-arm64@0.27.7': - resolution: {integrity: sha512-B48PqeCsEgOtzME2GbNM2roU29AMTuOIN91dsMO30t+Ydis3z/3Ngoj5hhnsOSSwNzS+6JppqWsuhTp6E82l2w==} + resolution: {integrity: sha512-B48PqeCsEgOtzME2GbNM2roU29AMTuOIN91dsMO30t+Ydis3z/3Ngoj5hhnsOSSwNzS+6JppqWsuhTp6E82l2w==, tarball: https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.7.tgz} engines: {node: '>=18'} cpu: [arm64] os: [freebsd] '@esbuild/freebsd-arm64@0.28.1': - resolution: {integrity: sha512-wG2EA8ENdEI0qhkSZMjfqrdY+ziCYCPMmtZjjIwOmXFjmyzEHn+UUxk5of+SYsjtfs3VpnlC7QLzSI5hY/rOAw==} + resolution: {integrity: sha512-wG2EA8ENdEI0qhkSZMjfqrdY+ziCYCPMmtZjjIwOmXFjmyzEHn+UUxk5of+SYsjtfs3VpnlC7QLzSI5hY/rOAw==, tarball: https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.28.1.tgz} engines: {node: '>=18'} cpu: [arm64] os: [freebsd] '@esbuild/freebsd-x64@0.19.2': - resolution: {integrity: sha512-nSO5uZT2clM6hosjWHAsS15hLrwCvIWx+b2e3lZ3MwbYSaXwvfO528OF+dLjas1g3bZonciivI8qKR/Hm7IWGw==} + resolution: {integrity: sha512-nSO5uZT2clM6hosjWHAsS15hLrwCvIWx+b2e3lZ3MwbYSaXwvfO528OF+dLjas1g3bZonciivI8qKR/Hm7IWGw==, tarball: https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.19.2.tgz} engines: {node: '>=12'} cpu: [x64] os: [freebsd] '@esbuild/freebsd-x64@0.23.1': - resolution: {integrity: sha512-lK1eJeyk1ZX8UklqFd/3A60UuZ/6UVfGT2LuGo3Wp4/z7eRTRYY+0xOu2kpClP+vMTi9wKOfXi2vjUpO1Ro76g==} + resolution: {integrity: sha512-lK1eJeyk1ZX8UklqFd/3A60UuZ/6UVfGT2LuGo3Wp4/z7eRTRYY+0xOu2kpClP+vMTi9wKOfXi2vjUpO1Ro76g==, tarball: https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.23.1.tgz} engines: {node: '>=18'} cpu: [x64] os: [freebsd] '@esbuild/freebsd-x64@0.25.5': - resolution: {integrity: sha512-PrikaNjiXdR2laW6OIjlbeuCPrPaAl0IwPIaRv+SMV8CiM8i2LqVUHFC1+8eORgWyY7yhQY+2U2fA55mBzReaw==} + resolution: {integrity: sha512-PrikaNjiXdR2laW6OIjlbeuCPrPaAl0IwPIaRv+SMV8CiM8i2LqVUHFC1+8eORgWyY7yhQY+2U2fA55mBzReaw==, tarball: https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.5.tgz} engines: {node: '>=18'} cpu: [x64] os: [freebsd] '@esbuild/freebsd-x64@0.27.7': - resolution: {integrity: sha512-jOBDK5XEjA4m5IJK3bpAQF9/Lelu/Z9ZcdhTRLf4cajlB+8VEhFFRjWgfy3M1O4rO2GQ/b2dLwCUGpiF/eATNQ==} + resolution: {integrity: sha512-jOBDK5XEjA4m5IJK3bpAQF9/Lelu/Z9ZcdhTRLf4cajlB+8VEhFFRjWgfy3M1O4rO2GQ/b2dLwCUGpiF/eATNQ==, tarball: https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.7.tgz} engines: {node: '>=18'} cpu: [x64] os: [freebsd] '@esbuild/freebsd-x64@0.28.1': - resolution: {integrity: sha512-i7dZ9vQgnvSCzi/rYCXNgtF/U+eKZNJBzu3eTQbRgHnM7tNSizLOkRFAl3qzVc/Op/u5YkHHa4pf/3DOYHthLQ==} + resolution: {integrity: sha512-i7dZ9vQgnvSCzi/rYCXNgtF/U+eKZNJBzu3eTQbRgHnM7tNSizLOkRFAl3qzVc/Op/u5YkHHa4pf/3DOYHthLQ==, tarball: https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.28.1.tgz} engines: {node: '>=18'} cpu: [x64] os: [freebsd] '@esbuild/linux-arm64@0.19.2': - resolution: {integrity: sha512-ig2P7GeG//zWlU0AggA3pV1h5gdix0MA3wgB+NsnBXViwiGgY77fuN9Wr5uoCrs2YzaYfogXgsWZbm+HGr09xg==} + resolution: {integrity: sha512-ig2P7GeG//zWlU0AggA3pV1h5gdix0MA3wgB+NsnBXViwiGgY77fuN9Wr5uoCrs2YzaYfogXgsWZbm+HGr09xg==, tarball: https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.19.2.tgz} engines: {node: '>=12'} cpu: [arm64] os: [linux] '@esbuild/linux-arm64@0.23.1': - resolution: {integrity: sha512-/93bf2yxencYDnItMYV/v116zff6UyTjo4EtEQjUBeGiVpMmffDNUyD9UN2zV+V3LRV3/on4xdZ26NKzn6754g==} + resolution: {integrity: sha512-/93bf2yxencYDnItMYV/v116zff6UyTjo4EtEQjUBeGiVpMmffDNUyD9UN2zV+V3LRV3/on4xdZ26NKzn6754g==, tarball: https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.23.1.tgz} engines: {node: '>=18'} cpu: [arm64] os: [linux] '@esbuild/linux-arm64@0.25.5': - resolution: {integrity: sha512-Z9kfb1v6ZlGbWj8EJk9T6czVEjjq2ntSYLY2cw6pAZl4oKtfgQuS4HOq41M/BcoLPzrUbNd+R4BXFyH//nHxVg==} + resolution: {integrity: sha512-Z9kfb1v6ZlGbWj8EJk9T6czVEjjq2ntSYLY2cw6pAZl4oKtfgQuS4HOq41M/BcoLPzrUbNd+R4BXFyH//nHxVg==, tarball: https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.5.tgz} engines: {node: '>=18'} cpu: [arm64] os: [linux] '@esbuild/linux-arm64@0.27.7': - resolution: {integrity: sha512-RZPHBoxXuNnPQO9rvjh5jdkRmVizktkT7TCDkDmQ0W2SwHInKCAV95GRuvdSvA7w4VMwfCjUiPwDi0ZO6Nfe9A==} + resolution: {integrity: sha512-RZPHBoxXuNnPQO9rvjh5jdkRmVizktkT7TCDkDmQ0W2SwHInKCAV95GRuvdSvA7w4VMwfCjUiPwDi0ZO6Nfe9A==, tarball: https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.7.tgz} engines: {node: '>=18'} cpu: [arm64] os: [linux] '@esbuild/linux-arm64@0.28.1': - resolution: {integrity: sha512-yHs+0uc8+nvEAfAfxrWQKK5peSNzBc4PegcMO0EJ2hT71uA7vB8Ihg2e77R2P7SG5uYjPbHlLLmve4LLLRCf0g==} + resolution: {integrity: sha512-yHs+0uc8+nvEAfAfxrWQKK5peSNzBc4PegcMO0EJ2hT71uA7vB8Ihg2e77R2P7SG5uYjPbHlLLmve4LLLRCf0g==, tarball: https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.28.1.tgz} engines: {node: '>=18'} cpu: [arm64] os: [linux] '@esbuild/linux-arm@0.19.2': - resolution: {integrity: sha512-Odalh8hICg7SOD7XCj0YLpYCEc+6mkoq63UnExDCiRA2wXEmGlK5JVrW50vZR9Qz4qkvqnHcpH+OFEggO3PgTg==} + resolution: {integrity: sha512-Odalh8hICg7SOD7XCj0YLpYCEc+6mkoq63UnExDCiRA2wXEmGlK5JVrW50vZR9Qz4qkvqnHcpH+OFEggO3PgTg==, tarball: https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.19.2.tgz} engines: {node: '>=12'} cpu: [arm] os: [linux] '@esbuild/linux-arm@0.23.1': - resolution: {integrity: sha512-CXXkzgn+dXAPs3WBwE+Kvnrf4WECwBdfjfeYHpMeVxWE0EceB6vhWGShs6wi0IYEqMSIzdOF1XjQ/Mkm5d7ZdQ==} + resolution: {integrity: sha512-CXXkzgn+dXAPs3WBwE+Kvnrf4WECwBdfjfeYHpMeVxWE0EceB6vhWGShs6wi0IYEqMSIzdOF1XjQ/Mkm5d7ZdQ==, tarball: https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.23.1.tgz} engines: {node: '>=18'} cpu: [arm] os: [linux] '@esbuild/linux-arm@0.25.5': - resolution: {integrity: sha512-cPzojwW2okgh7ZlRpcBEtsX7WBuqbLrNXqLU89GxWbNt6uIg78ET82qifUy3W6OVww6ZWobWub5oqZOVtwolfw==} + resolution: {integrity: sha512-cPzojwW2okgh7ZlRpcBEtsX7WBuqbLrNXqLU89GxWbNt6uIg78ET82qifUy3W6OVww6ZWobWub5oqZOVtwolfw==, tarball: https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.5.tgz} engines: {node: '>=18'} cpu: [arm] os: [linux] '@esbuild/linux-arm@0.27.7': - resolution: {integrity: sha512-RkT/YXYBTSULo3+af8Ib0ykH8u2MBh57o7q/DAs3lTJlyVQkgQvlrPTnjIzzRPQyavxtPtfg0EopvDyIt0j1rA==} + resolution: {integrity: sha512-RkT/YXYBTSULo3+af8Ib0ykH8u2MBh57o7q/DAs3lTJlyVQkgQvlrPTnjIzzRPQyavxtPtfg0EopvDyIt0j1rA==, tarball: https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.7.tgz} engines: {node: '>=18'} cpu: [arm] os: [linux] '@esbuild/linux-arm@0.28.1': - resolution: {integrity: sha512-qVXBOHQS+d5Y722GwJzJUtOLlX7km3CraOaGormF1pDtPd2C/l1SHRPgjLunLGe51Sh5YYWKMFDyV4SxgMQYTQ==} + resolution: {integrity: sha512-qVXBOHQS+d5Y722GwJzJUtOLlX7km3CraOaGormF1pDtPd2C/l1SHRPgjLunLGe51Sh5YYWKMFDyV4SxgMQYTQ==, tarball: https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.28.1.tgz} engines: {node: '>=18'} cpu: [arm] os: [linux] '@esbuild/linux-ia32@0.19.2': - resolution: {integrity: sha512-mLfp0ziRPOLSTek0Gd9T5B8AtzKAkoZE70fneiiyPlSnUKKI4lp+mGEnQXcQEHLJAcIYDPSyBvsUbKUG2ri/XQ==} + resolution: {integrity: sha512-mLfp0ziRPOLSTek0Gd9T5B8AtzKAkoZE70fneiiyPlSnUKKI4lp+mGEnQXcQEHLJAcIYDPSyBvsUbKUG2ri/XQ==, tarball: https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.19.2.tgz} engines: {node: '>=12'} cpu: [ia32] os: [linux] '@esbuild/linux-ia32@0.23.1': - resolution: {integrity: sha512-VTN4EuOHwXEkXzX5nTvVY4s7E/Krz7COC8xkftbbKRYAl96vPiUssGkeMELQMOnLOJ8k3BY1+ZY52tttZnHcXQ==} + resolution: {integrity: sha512-VTN4EuOHwXEkXzX5nTvVY4s7E/Krz7COC8xkftbbKRYAl96vPiUssGkeMELQMOnLOJ8k3BY1+ZY52tttZnHcXQ==, tarball: https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.23.1.tgz} engines: {node: '>=18'} cpu: [ia32] os: [linux] '@esbuild/linux-ia32@0.25.5': - resolution: {integrity: sha512-sQ7l00M8bSv36GLV95BVAdhJ2QsIbCuCjh/uYrWiMQSUuV+LpXwIqhgJDcvMTj+VsQmqAHL2yYaasENvJ7CDKA==} + resolution: {integrity: sha512-sQ7l00M8bSv36GLV95BVAdhJ2QsIbCuCjh/uYrWiMQSUuV+LpXwIqhgJDcvMTj+VsQmqAHL2yYaasENvJ7CDKA==, tarball: https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.5.tgz} engines: {node: '>=18'} cpu: [ia32] os: [linux] '@esbuild/linux-ia32@0.27.7': - resolution: {integrity: sha512-GA48aKNkyQDbd3KtkplYWT102C5sn/EZTY4XROkxONgruHPU72l+gW+FfF8tf2cFjeHaRbWpOYa/uRBz/Xq1Pg==} + resolution: {integrity: sha512-GA48aKNkyQDbd3KtkplYWT102C5sn/EZTY4XROkxONgruHPU72l+gW+FfF8tf2cFjeHaRbWpOYa/uRBz/Xq1Pg==, tarball: https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.7.tgz} engines: {node: '>=18'} cpu: [ia32] os: [linux] '@esbuild/linux-ia32@0.28.1': - resolution: {integrity: sha512-d1z4ZuP0ajrfz/FhGT4vv278rX8KnPPJx8i5+AtK7TYbx9Le9F1hyzurZpkEyjkGa9dUGhQow4C1NmeGvqxN2w==} + resolution: {integrity: sha512-d1z4ZuP0ajrfz/FhGT4vv278rX8KnPPJx8i5+AtK7TYbx9Le9F1hyzurZpkEyjkGa9dUGhQow4C1NmeGvqxN2w==, tarball: https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.28.1.tgz} engines: {node: '>=18'} cpu: [ia32] os: [linux] '@esbuild/linux-loong64@0.19.2': - resolution: {integrity: sha512-hn28+JNDTxxCpnYjdDYVMNTR3SKavyLlCHHkufHV91fkewpIyQchS1d8wSbmXhs1fiYDpNww8KTFlJ1dHsxeSw==} + resolution: {integrity: sha512-hn28+JNDTxxCpnYjdDYVMNTR3SKavyLlCHHkufHV91fkewpIyQchS1d8wSbmXhs1fiYDpNww8KTFlJ1dHsxeSw==, tarball: https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.19.2.tgz} engines: {node: '>=12'} cpu: [loong64] os: [linux] '@esbuild/linux-loong64@0.23.1': - resolution: {integrity: sha512-Vx09LzEoBa5zDnieH8LSMRToj7ir/Jeq0Gu6qJ/1GcBq9GkfoEAoXvLiW1U9J1qE/Y/Oyaq33w5p2ZWrNNHNEw==} + resolution: {integrity: sha512-Vx09LzEoBa5zDnieH8LSMRToj7ir/Jeq0Gu6qJ/1GcBq9GkfoEAoXvLiW1U9J1qE/Y/Oyaq33w5p2ZWrNNHNEw==, tarball: https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.23.1.tgz} engines: {node: '>=18'} cpu: [loong64] os: [linux] '@esbuild/linux-loong64@0.25.5': - resolution: {integrity: sha512-0ur7ae16hDUC4OL5iEnDb0tZHDxYmuQyhKhsPBV8f99f6Z9KQM02g33f93rNH5A30agMS46u2HP6qTdEt6Q1kg==} + resolution: {integrity: sha512-0ur7ae16hDUC4OL5iEnDb0tZHDxYmuQyhKhsPBV8f99f6Z9KQM02g33f93rNH5A30agMS46u2HP6qTdEt6Q1kg==, tarball: https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.5.tgz} engines: {node: '>=18'} cpu: [loong64] os: [linux] '@esbuild/linux-loong64@0.27.7': - resolution: {integrity: sha512-a4POruNM2oWsD4WKvBSEKGIiWQF8fZOAsycHOt6JBpZ+JN2n2JH9WAv56SOyu9X5IqAjqSIPTaJkqN8F7XOQ5Q==} + resolution: {integrity: sha512-a4POruNM2oWsD4WKvBSEKGIiWQF8fZOAsycHOt6JBpZ+JN2n2JH9WAv56SOyu9X5IqAjqSIPTaJkqN8F7XOQ5Q==, tarball: https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.7.tgz} engines: {node: '>=18'} cpu: [loong64] os: [linux] '@esbuild/linux-loong64@0.28.1': - resolution: {integrity: sha512-M5sRjUVZrkm1OAPR3dlOYzNmN+loZKGVi1VUQGrwuqLcbR6qeAz+famMhjASeH3YVKvZz+zT1jlh/keC3Rj/lg==} + resolution: {integrity: sha512-M5sRjUVZrkm1OAPR3dlOYzNmN+loZKGVi1VUQGrwuqLcbR6qeAz+famMhjASeH3YVKvZz+zT1jlh/keC3Rj/lg==, tarball: https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.28.1.tgz} engines: {node: '>=18'} cpu: [loong64] os: [linux] '@esbuild/linux-mips64el@0.19.2': - resolution: {integrity: sha512-KbXaC0Sejt7vD2fEgPoIKb6nxkfYW9OmFUK9XQE4//PvGIxNIfPk1NmlHmMg6f25x57rpmEFrn1OotASYIAaTg==} + resolution: {integrity: sha512-KbXaC0Sejt7vD2fEgPoIKb6nxkfYW9OmFUK9XQE4//PvGIxNIfPk1NmlHmMg6f25x57rpmEFrn1OotASYIAaTg==, tarball: https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.19.2.tgz} engines: {node: '>=12'} cpu: [mips64el] os: [linux] '@esbuild/linux-mips64el@0.23.1': - resolution: {integrity: sha512-nrFzzMQ7W4WRLNUOU5dlWAqa6yVeI0P78WKGUo7lg2HShq/yx+UYkeNSE0SSfSure0SqgnsxPvmAUu/vu0E+3Q==} + resolution: {integrity: sha512-nrFzzMQ7W4WRLNUOU5dlWAqa6yVeI0P78WKGUo7lg2HShq/yx+UYkeNSE0SSfSure0SqgnsxPvmAUu/vu0E+3Q==, tarball: https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.23.1.tgz} engines: {node: '>=18'} cpu: [mips64el] os: [linux] '@esbuild/linux-mips64el@0.25.5': - resolution: {integrity: sha512-kB/66P1OsHO5zLz0i6X0RxlQ+3cu0mkxS3TKFvkb5lin6uwZ/ttOkP3Z8lfR9mJOBk14ZwZ9182SIIWFGNmqmg==} + resolution: {integrity: sha512-kB/66P1OsHO5zLz0i6X0RxlQ+3cu0mkxS3TKFvkb5lin6uwZ/ttOkP3Z8lfR9mJOBk14ZwZ9182SIIWFGNmqmg==, tarball: https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.5.tgz} engines: {node: '>=18'} cpu: [mips64el] os: [linux] '@esbuild/linux-mips64el@0.27.7': - resolution: {integrity: sha512-KabT5I6StirGfIz0FMgl1I+R1H73Gp0ofL9A3nG3i/cYFJzKHhouBV5VWK1CSgKvVaG4q1RNpCTR2LuTVB3fIw==} + resolution: {integrity: sha512-KabT5I6StirGfIz0FMgl1I+R1H73Gp0ofL9A3nG3i/cYFJzKHhouBV5VWK1CSgKvVaG4q1RNpCTR2LuTVB3fIw==, tarball: https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.7.tgz} engines: {node: '>=18'} cpu: [mips64el] os: [linux] '@esbuild/linux-mips64el@0.28.1': - resolution: {integrity: sha512-mRObBZeHh2OxcBFPWE/FjylkRgZdYuiTR3vaTozquCGOH14iP9oN4x4Ge81CoIDYQrXmIxpFumJBu5MtZpnQJQ==} + resolution: {integrity: sha512-mRObBZeHh2OxcBFPWE/FjylkRgZdYuiTR3vaTozquCGOH14iP9oN4x4Ge81CoIDYQrXmIxpFumJBu5MtZpnQJQ==, tarball: https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.28.1.tgz} engines: {node: '>=18'} cpu: [mips64el] os: [linux] '@esbuild/linux-ppc64@0.19.2': - resolution: {integrity: sha512-dJ0kE8KTqbiHtA3Fc/zn7lCd7pqVr4JcT0JqOnbj4LLzYnp+7h8Qi4yjfq42ZlHfhOCM42rBh0EwHYLL6LEzcw==} + resolution: {integrity: sha512-dJ0kE8KTqbiHtA3Fc/zn7lCd7pqVr4JcT0JqOnbj4LLzYnp+7h8Qi4yjfq42ZlHfhOCM42rBh0EwHYLL6LEzcw==, tarball: https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.19.2.tgz} engines: {node: '>=12'} cpu: [ppc64] os: [linux] '@esbuild/linux-ppc64@0.23.1': - resolution: {integrity: sha512-dKN8fgVqd0vUIjxuJI6P/9SSSe/mB9rvA98CSH2sJnlZ/OCZWO1DJvxj8jvKTfYUdGfcq2dDxoKaC6bHuTlgcw==} + resolution: {integrity: sha512-dKN8fgVqd0vUIjxuJI6P/9SSSe/mB9rvA98CSH2sJnlZ/OCZWO1DJvxj8jvKTfYUdGfcq2dDxoKaC6bHuTlgcw==, tarball: https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.23.1.tgz} engines: {node: '>=18'} cpu: [ppc64] os: [linux] '@esbuild/linux-ppc64@0.25.5': - resolution: {integrity: sha512-UZCmJ7r9X2fe2D6jBmkLBMQetXPXIsZjQJCjgwpVDz+YMcS6oFR27alkgGv3Oqkv07bxdvw7fyB71/olceJhkQ==} + resolution: {integrity: sha512-UZCmJ7r9X2fe2D6jBmkLBMQetXPXIsZjQJCjgwpVDz+YMcS6oFR27alkgGv3Oqkv07bxdvw7fyB71/olceJhkQ==, tarball: https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.5.tgz} engines: {node: '>=18'} cpu: [ppc64] os: [linux] '@esbuild/linux-ppc64@0.27.7': - resolution: {integrity: sha512-gRsL4x6wsGHGRqhtI+ifpN/vpOFTQtnbsupUF5R5YTAg+y/lKelYR1hXbnBdzDjGbMYjVJLJTd2OFmMewAgwlQ==} + resolution: {integrity: sha512-gRsL4x6wsGHGRqhtI+ifpN/vpOFTQtnbsupUF5R5YTAg+y/lKelYR1hXbnBdzDjGbMYjVJLJTd2OFmMewAgwlQ==, tarball: https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.7.tgz} engines: {node: '>=18'} cpu: [ppc64] os: [linux] '@esbuild/linux-ppc64@0.28.1': - resolution: {integrity: sha512-slScBsMAb3GFDcdrCgLwZtPYRoH2H/youv10QiZyRjmsP48fznoveWytSgCI/R0ZcUgpc0ZhIUEx6LHts8yrfQ==} + resolution: {integrity: sha512-slScBsMAb3GFDcdrCgLwZtPYRoH2H/youv10QiZyRjmsP48fznoveWytSgCI/R0ZcUgpc0ZhIUEx6LHts8yrfQ==, tarball: https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.28.1.tgz} engines: {node: '>=18'} cpu: [ppc64] os: [linux] '@esbuild/linux-riscv64@0.19.2': - resolution: {integrity: sha512-7Z/jKNFufZ/bbu4INqqCN6DDlrmOTmdw6D0gH+6Y7auok2r02Ur661qPuXidPOJ+FSgbEeQnnAGgsVynfLuOEw==} + resolution: {integrity: sha512-7Z/jKNFufZ/bbu4INqqCN6DDlrmOTmdw6D0gH+6Y7auok2r02Ur661qPuXidPOJ+FSgbEeQnnAGgsVynfLuOEw==, tarball: https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.19.2.tgz} engines: {node: '>=12'} cpu: [riscv64] os: [linux] '@esbuild/linux-riscv64@0.23.1': - resolution: {integrity: sha512-5AV4Pzp80fhHL83JM6LoA6pTQVWgB1HovMBsLQ9OZWLDqVY8MVobBXNSmAJi//Csh6tcY7e7Lny2Hg1tElMjIA==} + resolution: {integrity: sha512-5AV4Pzp80fhHL83JM6LoA6pTQVWgB1HovMBsLQ9OZWLDqVY8MVobBXNSmAJi//Csh6tcY7e7Lny2Hg1tElMjIA==, tarball: https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.23.1.tgz} engines: {node: '>=18'} cpu: [riscv64] os: [linux] '@esbuild/linux-riscv64@0.25.5': - resolution: {integrity: sha512-kTxwu4mLyeOlsVIFPfQo+fQJAV9mh24xL+y+Bm6ej067sYANjyEw1dNHmvoqxJUCMnkBdKpvOn0Ahql6+4VyeA==} + resolution: {integrity: sha512-kTxwu4mLyeOlsVIFPfQo+fQJAV9mh24xL+y+Bm6ej067sYANjyEw1dNHmvoqxJUCMnkBdKpvOn0Ahql6+4VyeA==, tarball: https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.5.tgz} engines: {node: '>=18'} cpu: [riscv64] os: [linux] '@esbuild/linux-riscv64@0.27.7': - resolution: {integrity: sha512-hL25LbxO1QOngGzu2U5xeXtxXcW+/GvMN3ejANqXkxZ/opySAZMrc+9LY/WyjAan41unrR3YrmtTsUpwT66InQ==} + resolution: {integrity: sha512-hL25LbxO1QOngGzu2U5xeXtxXcW+/GvMN3ejANqXkxZ/opySAZMrc+9LY/WyjAan41unrR3YrmtTsUpwT66InQ==, tarball: https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.7.tgz} engines: {node: '>=18'} cpu: [riscv64] os: [linux] '@esbuild/linux-riscv64@0.28.1': - resolution: {integrity: sha512-kw0owk1o0GFETUJyW0jc0G4Yzs0BHZn0JDZ8JRT088vjJYX777BAs1fDGxAC+q831qOs2DTC96mNsG2opdfyyQ==} + resolution: {integrity: sha512-kw0owk1o0GFETUJyW0jc0G4Yzs0BHZn0JDZ8JRT088vjJYX777BAs1fDGxAC+q831qOs2DTC96mNsG2opdfyyQ==, tarball: https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.28.1.tgz} engines: {node: '>=18'} cpu: [riscv64] os: [linux] '@esbuild/linux-s390x@0.19.2': - resolution: {integrity: sha512-U+RinR6aXXABFCcAY4gSlv4CL1oOVvSSCdseQmGO66H+XyuQGZIUdhG56SZaDJQcLmrSfRmx5XZOWyCJPRqS7g==} + resolution: {integrity: sha512-U+RinR6aXXABFCcAY4gSlv4CL1oOVvSSCdseQmGO66H+XyuQGZIUdhG56SZaDJQcLmrSfRmx5XZOWyCJPRqS7g==, tarball: https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.19.2.tgz} engines: {node: '>=12'} cpu: [s390x] os: [linux] '@esbuild/linux-s390x@0.23.1': - resolution: {integrity: sha512-9ygs73tuFCe6f6m/Tb+9LtYxWR4c9yg7zjt2cYkjDbDpV/xVn+68cQxMXCjUpYwEkze2RcU/rMnfIXNRFmSoDw==} + resolution: {integrity: sha512-9ygs73tuFCe6f6m/Tb+9LtYxWR4c9yg7zjt2cYkjDbDpV/xVn+68cQxMXCjUpYwEkze2RcU/rMnfIXNRFmSoDw==, tarball: https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.23.1.tgz} engines: {node: '>=18'} cpu: [s390x] os: [linux] '@esbuild/linux-s390x@0.25.5': - resolution: {integrity: sha512-K2dSKTKfmdh78uJ3NcWFiqyRrimfdinS5ErLSn3vluHNeHVnBAFWC8a4X5N+7FgVE1EjXS1QDZbpqZBjfrqMTQ==} + resolution: {integrity: sha512-K2dSKTKfmdh78uJ3NcWFiqyRrimfdinS5ErLSn3vluHNeHVnBAFWC8a4X5N+7FgVE1EjXS1QDZbpqZBjfrqMTQ==, tarball: https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.5.tgz} engines: {node: '>=18'} cpu: [s390x] os: [linux] '@esbuild/linux-s390x@0.27.7': - resolution: {integrity: sha512-2k8go8Ycu1Kb46vEelhu1vqEP+UeRVj2zY1pSuPdgvbd5ykAw82Lrro28vXUrRmzEsUV0NzCf54yARIK8r0fdw==} + resolution: {integrity: sha512-2k8go8Ycu1Kb46vEelhu1vqEP+UeRVj2zY1pSuPdgvbd5ykAw82Lrro28vXUrRmzEsUV0NzCf54yARIK8r0fdw==, tarball: https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.7.tgz} engines: {node: '>=18'} cpu: [s390x] os: [linux] '@esbuild/linux-s390x@0.28.1': - resolution: {integrity: sha512-/lAIjX8aYFRByhh6L5rYtPEDRqa9de/4V/juOXcta5frjvzXO4/sqEtyytse0g3zZFuWu5cDN0MkLz2qRDD2Ag==} + resolution: {integrity: sha512-/lAIjX8aYFRByhh6L5rYtPEDRqa9de/4V/juOXcta5frjvzXO4/sqEtyytse0g3zZFuWu5cDN0MkLz2qRDD2Ag==, tarball: https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.28.1.tgz} engines: {node: '>=18'} cpu: [s390x] os: [linux] '@esbuild/linux-x64@0.19.2': - resolution: {integrity: sha512-oxzHTEv6VPm3XXNaHPyUTTte+3wGv7qVQtqaZCrgstI16gCuhNOtBXLEBkBREP57YTd68P0VgDgG73jSD8bwXQ==} + resolution: {integrity: sha512-oxzHTEv6VPm3XXNaHPyUTTte+3wGv7qVQtqaZCrgstI16gCuhNOtBXLEBkBREP57YTd68P0VgDgG73jSD8bwXQ==, tarball: https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.19.2.tgz} engines: {node: '>=12'} cpu: [x64] os: [linux] '@esbuild/linux-x64@0.23.1': - resolution: {integrity: sha512-EV6+ovTsEXCPAp58g2dD68LxoP/wK5pRvgy0J/HxPGB009omFPv3Yet0HiaqvrIrgPTBuC6wCH1LTOY91EO5hQ==} + resolution: {integrity: sha512-EV6+ovTsEXCPAp58g2dD68LxoP/wK5pRvgy0J/HxPGB009omFPv3Yet0HiaqvrIrgPTBuC6wCH1LTOY91EO5hQ==, tarball: https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.23.1.tgz} engines: {node: '>=18'} cpu: [x64] os: [linux] '@esbuild/linux-x64@0.25.5': - resolution: {integrity: sha512-uhj8N2obKTE6pSZ+aMUbqq+1nXxNjZIIjCjGLfsWvVpy7gKCOL6rsY1MhRh9zLtUtAI7vpgLMK6DxjO8Qm9lJw==} + resolution: {integrity: sha512-uhj8N2obKTE6pSZ+aMUbqq+1nXxNjZIIjCjGLfsWvVpy7gKCOL6rsY1MhRh9zLtUtAI7vpgLMK6DxjO8Qm9lJw==, tarball: https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.5.tgz} engines: {node: '>=18'} cpu: [x64] os: [linux] '@esbuild/linux-x64@0.27.7': - resolution: {integrity: sha512-hzznmADPt+OmsYzw1EE33ccA+HPdIqiCRq7cQeL1Jlq2gb1+OyWBkMCrYGBJ+sxVzve2ZJEVeePbLM2iEIZSxA==} + resolution: {integrity: sha512-hzznmADPt+OmsYzw1EE33ccA+HPdIqiCRq7cQeL1Jlq2gb1+OyWBkMCrYGBJ+sxVzve2ZJEVeePbLM2iEIZSxA==, tarball: https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.7.tgz} engines: {node: '>=18'} cpu: [x64] os: [linux] '@esbuild/linux-x64@0.28.1': - resolution: {integrity: sha512-u/anNYF2mmVOEDwLtnQ1wOr3EZ9sTNGLWrsYGYwHWzGA3Si84IOkHXlbWTD1NB+9/1lcnweYKO54uhxZydNzfA==} + resolution: {integrity: sha512-u/anNYF2mmVOEDwLtnQ1wOr3EZ9sTNGLWrsYGYwHWzGA3Si84IOkHXlbWTD1NB+9/1lcnweYKO54uhxZydNzfA==, tarball: https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.28.1.tgz} engines: {node: '>=18'} cpu: [x64] os: [linux] '@esbuild/netbsd-arm64@0.25.5': - resolution: {integrity: sha512-pwHtMP9viAy1oHPvgxtOv+OkduK5ugofNTVDilIzBLpoWAM16r7b/mxBvfpuQDpRQFMfuVr5aLcn4yveGvBZvw==} + resolution: {integrity: sha512-pwHtMP9viAy1oHPvgxtOv+OkduK5ugofNTVDilIzBLpoWAM16r7b/mxBvfpuQDpRQFMfuVr5aLcn4yveGvBZvw==, tarball: https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.5.tgz} engines: {node: '>=18'} cpu: [arm64] os: [netbsd] '@esbuild/netbsd-arm64@0.27.7': - resolution: {integrity: sha512-b6pqtrQdigZBwZxAn1UpazEisvwaIDvdbMbmrly7cDTMFnw/+3lVxxCTGOrkPVnsYIosJJXAsILG9XcQS+Yu6w==} + resolution: {integrity: sha512-b6pqtrQdigZBwZxAn1UpazEisvwaIDvdbMbmrly7cDTMFnw/+3lVxxCTGOrkPVnsYIosJJXAsILG9XcQS+Yu6w==, tarball: https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.7.tgz} engines: {node: '>=18'} cpu: [arm64] os: [netbsd] '@esbuild/netbsd-arm64@0.28.1': - resolution: {integrity: sha512-oks0DYbLwWMmaakTsCb+zL4E+aHRVLom9IJZOAthMQEPiQmydXHkziYEsGYRx0uNV/IjEKGAV941JzH02pflqw==} + resolution: {integrity: sha512-oks0DYbLwWMmaakTsCb+zL4E+aHRVLom9IJZOAthMQEPiQmydXHkziYEsGYRx0uNV/IjEKGAV941JzH02pflqw==, tarball: https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.28.1.tgz} engines: {node: '>=18'} cpu: [arm64] os: [netbsd] '@esbuild/netbsd-x64@0.19.2': - resolution: {integrity: sha512-WNa5zZk1XpTTwMDompZmvQLHszDDDN7lYjEHCUmAGB83Bgs20EMs7ICD+oKeT6xt4phV4NDdSi/8OfjPbSbZfQ==} + resolution: {integrity: sha512-WNa5zZk1XpTTwMDompZmvQLHszDDDN7lYjEHCUmAGB83Bgs20EMs7ICD+oKeT6xt4phV4NDdSi/8OfjPbSbZfQ==, tarball: https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.19.2.tgz} engines: {node: '>=12'} cpu: [x64] os: [netbsd] '@esbuild/netbsd-x64@0.23.1': - resolution: {integrity: sha512-aevEkCNu7KlPRpYLjwmdcuNz6bDFiE7Z8XC4CPqExjTvrHugh28QzUXVOZtiYghciKUacNktqxdpymplil1beA==} + resolution: {integrity: sha512-aevEkCNu7KlPRpYLjwmdcuNz6bDFiE7Z8XC4CPqExjTvrHugh28QzUXVOZtiYghciKUacNktqxdpymplil1beA==, tarball: https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.23.1.tgz} engines: {node: '>=18'} cpu: [x64] os: [netbsd] '@esbuild/netbsd-x64@0.25.5': - resolution: {integrity: sha512-WOb5fKrvVTRMfWFNCroYWWklbnXH0Q5rZppjq0vQIdlsQKuw6mdSihwSo4RV/YdQ5UCKKvBy7/0ZZYLBZKIbwQ==} + resolution: {integrity: sha512-WOb5fKrvVTRMfWFNCroYWWklbnXH0Q5rZppjq0vQIdlsQKuw6mdSihwSo4RV/YdQ5UCKKvBy7/0ZZYLBZKIbwQ==, tarball: https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.5.tgz} engines: {node: '>=18'} cpu: [x64] os: [netbsd] '@esbuild/netbsd-x64@0.27.7': - resolution: {integrity: sha512-OfatkLojr6U+WN5EDYuoQhtM+1xco+/6FSzJJnuWiUw5eVcicbyK3dq5EeV/QHT1uy6GoDhGbFpprUiHUYggrw==} + resolution: {integrity: sha512-OfatkLojr6U+WN5EDYuoQhtM+1xco+/6FSzJJnuWiUw5eVcicbyK3dq5EeV/QHT1uy6GoDhGbFpprUiHUYggrw==, tarball: https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.7.tgz} engines: {node: '>=18'} cpu: [x64] os: [netbsd] '@esbuild/netbsd-x64@0.28.1': - resolution: {integrity: sha512-aeL6lAnN89Hz43Mlh1G8ARasbuoYvSITDEx0tHh5b7jJnHcssqgjy9Yx430GDpmCa6OyrKoS0aNRjKundRizGg==} + resolution: {integrity: sha512-aeL6lAnN89Hz43Mlh1G8ARasbuoYvSITDEx0tHh5b7jJnHcssqgjy9Yx430GDpmCa6OyrKoS0aNRjKundRizGg==, tarball: https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.28.1.tgz} engines: {node: '>=18'} cpu: [x64] os: [netbsd] '@esbuild/openbsd-arm64@0.23.1': - resolution: {integrity: sha512-3x37szhLexNA4bXhLrCC/LImN/YtWis6WXr1VESlfVtVeoFJBRINPJ3f0a/6LV8zpikqoUg4hyXw0sFBt5Cr+Q==} + resolution: {integrity: sha512-3x37szhLexNA4bXhLrCC/LImN/YtWis6WXr1VESlfVtVeoFJBRINPJ3f0a/6LV8zpikqoUg4hyXw0sFBt5Cr+Q==, tarball: https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.23.1.tgz} engines: {node: '>=18'} cpu: [arm64] os: [openbsd] '@esbuild/openbsd-arm64@0.25.5': - resolution: {integrity: sha512-7A208+uQKgTxHd0G0uqZO8UjK2R0DDb4fDmERtARjSHWxqMTye4Erz4zZafx7Di9Cv+lNHYuncAkiGFySoD+Mw==} + resolution: {integrity: sha512-7A208+uQKgTxHd0G0uqZO8UjK2R0DDb4fDmERtARjSHWxqMTye4Erz4zZafx7Di9Cv+lNHYuncAkiGFySoD+Mw==, tarball: https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.5.tgz} engines: {node: '>=18'} cpu: [arm64] os: [openbsd] '@esbuild/openbsd-arm64@0.27.7': - resolution: {integrity: sha512-AFuojMQTxAz75Fo8idVcqoQWEHIXFRbOc1TrVcFSgCZtQfSdc1RXgB3tjOn/krRHENUB4j00bfGjyl2mJrU37A==} + resolution: {integrity: sha512-AFuojMQTxAz75Fo8idVcqoQWEHIXFRbOc1TrVcFSgCZtQfSdc1RXgB3tjOn/krRHENUB4j00bfGjyl2mJrU37A==, tarball: https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.7.tgz} engines: {node: '>=18'} cpu: [arm64] os: [openbsd] '@esbuild/openbsd-arm64@0.28.1': - resolution: {integrity: sha512-MEFJe5C3R8pwXdZ5Y21oo6m7ePiS0d9pWucn99O/wvyJZChoIQKrQDxKrGeW8F5+T0okTHesAmDeiHDTIq0V/Q==} + resolution: {integrity: sha512-MEFJe5C3R8pwXdZ5Y21oo6m7ePiS0d9pWucn99O/wvyJZChoIQKrQDxKrGeW8F5+T0okTHesAmDeiHDTIq0V/Q==, tarball: https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.28.1.tgz} engines: {node: '>=18'} cpu: [arm64] os: [openbsd] '@esbuild/openbsd-x64@0.19.2': - resolution: {integrity: sha512-S6kI1aT3S++Dedb7vxIuUOb3oAxqxk2Rh5rOXOTYnzN8JzW1VzBd+IqPiSpgitu45042SYD3HCoEyhLKQcDFDw==} + resolution: {integrity: sha512-S6kI1aT3S++Dedb7vxIuUOb3oAxqxk2Rh5rOXOTYnzN8JzW1VzBd+IqPiSpgitu45042SYD3HCoEyhLKQcDFDw==, tarball: https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.19.2.tgz} engines: {node: '>=12'} cpu: [x64] os: [openbsd] '@esbuild/openbsd-x64@0.23.1': - resolution: {integrity: sha512-aY2gMmKmPhxfU+0EdnN+XNtGbjfQgwZj43k8G3fyrDM/UdZww6xrWxmDkuz2eCZchqVeABjV5BpildOrUbBTqA==} + resolution: {integrity: sha512-aY2gMmKmPhxfU+0EdnN+XNtGbjfQgwZj43k8G3fyrDM/UdZww6xrWxmDkuz2eCZchqVeABjV5BpildOrUbBTqA==, tarball: https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.23.1.tgz} engines: {node: '>=18'} cpu: [x64] os: [openbsd] '@esbuild/openbsd-x64@0.25.5': - resolution: {integrity: sha512-G4hE405ErTWraiZ8UiSoesH8DaCsMm0Cay4fsFWOOUcz8b8rC6uCvnagr+gnioEjWn0wC+o1/TAHt+It+MpIMg==} + resolution: {integrity: sha512-G4hE405ErTWraiZ8UiSoesH8DaCsMm0Cay4fsFWOOUcz8b8rC6uCvnagr+gnioEjWn0wC+o1/TAHt+It+MpIMg==, tarball: https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.5.tgz} engines: {node: '>=18'} cpu: [x64] os: [openbsd] '@esbuild/openbsd-x64@0.27.7': - resolution: {integrity: sha512-+A1NJmfM8WNDv5CLVQYJ5PshuRm/4cI6WMZRg1by1GwPIQPCTs1GLEUHwiiQGT5zDdyLiRM/l1G0Pv54gvtKIg==} + resolution: {integrity: sha512-+A1NJmfM8WNDv5CLVQYJ5PshuRm/4cI6WMZRg1by1GwPIQPCTs1GLEUHwiiQGT5zDdyLiRM/l1G0Pv54gvtKIg==, tarball: https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.7.tgz} engines: {node: '>=18'} cpu: [x64] os: [openbsd] '@esbuild/openbsd-x64@0.28.1': - resolution: {integrity: sha512-i/ZLIOafE0Z8cI/XANJAixoJL/uRAoS2xOA3rb0xN+KK0K177cMAsQYkzHtBrtMXAKuAc7HGgcWiZ/sRC1Nxgw==} + resolution: {integrity: sha512-i/ZLIOafE0Z8cI/XANJAixoJL/uRAoS2xOA3rb0xN+KK0K177cMAsQYkzHtBrtMXAKuAc7HGgcWiZ/sRC1Nxgw==, tarball: https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.28.1.tgz} engines: {node: '>=18'} cpu: [x64] os: [openbsd] '@esbuild/openharmony-arm64@0.27.7': - resolution: {integrity: sha512-+KrvYb/C8zA9CU/g0sR6w2RBw7IGc5J2BPnc3dYc5VJxHCSF1yNMxTV5LQ7GuKteQXZtspjFbiuW5/dOj7H4Yw==} + resolution: {integrity: sha512-+KrvYb/C8zA9CU/g0sR6w2RBw7IGc5J2BPnc3dYc5VJxHCSF1yNMxTV5LQ7GuKteQXZtspjFbiuW5/dOj7H4Yw==, tarball: https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.7.tgz} engines: {node: '>=18'} cpu: [arm64] os: [openharmony] '@esbuild/openharmony-arm64@0.28.1': - resolution: {integrity: sha512-ge+Z7EXFNt2BO1oAMsVpiQ8EwndV9i1xXerAeTIK7AtPs3bKFXQM7nlRxDSIUIMeueR1CNXxqztLzdNeReKBJg==} + resolution: {integrity: sha512-ge+Z7EXFNt2BO1oAMsVpiQ8EwndV9i1xXerAeTIK7AtPs3bKFXQM7nlRxDSIUIMeueR1CNXxqztLzdNeReKBJg==, tarball: https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.28.1.tgz} engines: {node: '>=18'} cpu: [arm64] os: [openharmony] '@esbuild/sunos-x64@0.19.2': - resolution: {integrity: sha512-VXSSMsmb+Z8LbsQGcBMiM+fYObDNRm8p7tkUDMPG/g4fhFX5DEFmjxIEa3N8Zr96SjsJ1woAhF0DUnS3MF3ARw==} + resolution: {integrity: sha512-VXSSMsmb+Z8LbsQGcBMiM+fYObDNRm8p7tkUDMPG/g4fhFX5DEFmjxIEa3N8Zr96SjsJ1woAhF0DUnS3MF3ARw==, tarball: https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.19.2.tgz} engines: {node: '>=12'} cpu: [x64] os: [sunos] '@esbuild/sunos-x64@0.23.1': - resolution: {integrity: sha512-RBRT2gqEl0IKQABT4XTj78tpk9v7ehp+mazn2HbUeZl1YMdaGAQqhapjGTCe7uw7y0frDi4gS0uHzhvpFuI1sA==} + resolution: {integrity: sha512-RBRT2gqEl0IKQABT4XTj78tpk9v7ehp+mazn2HbUeZl1YMdaGAQqhapjGTCe7uw7y0frDi4gS0uHzhvpFuI1sA==, tarball: https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.23.1.tgz} engines: {node: '>=18'} cpu: [x64] os: [sunos] '@esbuild/sunos-x64@0.25.5': - resolution: {integrity: sha512-l+azKShMy7FxzY0Rj4RCt5VD/q8mG/e+mDivgspo+yL8zW7qEwctQ6YqKX34DTEleFAvCIUviCFX1SDZRSyMQA==} + resolution: {integrity: sha512-l+azKShMy7FxzY0Rj4RCt5VD/q8mG/e+mDivgspo+yL8zW7qEwctQ6YqKX34DTEleFAvCIUviCFX1SDZRSyMQA==, tarball: https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.5.tgz} engines: {node: '>=18'} cpu: [x64] os: [sunos] '@esbuild/sunos-x64@0.27.7': - resolution: {integrity: sha512-ikktIhFBzQNt/QDyOL580ti9+5mL/YZeUPKU2ivGtGjdTYoqz6jObj6nOMfhASpS4GU4Q/Clh1QtxWAvcYKamA==} + resolution: {integrity: sha512-ikktIhFBzQNt/QDyOL580ti9+5mL/YZeUPKU2ivGtGjdTYoqz6jObj6nOMfhASpS4GU4Q/Clh1QtxWAvcYKamA==, tarball: https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.7.tgz} engines: {node: '>=18'} cpu: [x64] os: [sunos] '@esbuild/sunos-x64@0.28.1': - resolution: {integrity: sha512-BEjgtECkL3vY+SaSQ6nzVfiALUeFxpawyp8Jmf5PtYhf1Ug40N1h/hxlhts+f1FvSvarEigdxS3BlSMI2PJLcQ==} + resolution: {integrity: sha512-BEjgtECkL3vY+SaSQ6nzVfiALUeFxpawyp8Jmf5PtYhf1Ug40N1h/hxlhts+f1FvSvarEigdxS3BlSMI2PJLcQ==, tarball: https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.28.1.tgz} engines: {node: '>=18'} cpu: [x64] os: [sunos] '@esbuild/win32-arm64@0.19.2': - resolution: {integrity: sha512-5NayUlSAyb5PQYFAU9x3bHdsqB88RC3aM9lKDAz4X1mo/EchMIT1Q+pSeBXNgkfNmRecLXA0O8xP+x8V+g/LKg==} + resolution: {integrity: sha512-5NayUlSAyb5PQYFAU9x3bHdsqB88RC3aM9lKDAz4X1mo/EchMIT1Q+pSeBXNgkfNmRecLXA0O8xP+x8V+g/LKg==, tarball: https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.19.2.tgz} engines: {node: '>=12'} cpu: [arm64] os: [win32] '@esbuild/win32-arm64@0.23.1': - resolution: {integrity: sha512-4O+gPR5rEBe2FpKOVyiJ7wNDPA8nGzDuJ6gN4okSA1gEOYZ67N8JPk58tkWtdtPeLz7lBnY6I5L3jdsr3S+A6A==} + resolution: {integrity: sha512-4O+gPR5rEBe2FpKOVyiJ7wNDPA8nGzDuJ6gN4okSA1gEOYZ67N8JPk58tkWtdtPeLz7lBnY6I5L3jdsr3S+A6A==, tarball: https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.23.1.tgz} engines: {node: '>=18'} cpu: [arm64] os: [win32] '@esbuild/win32-arm64@0.25.5': - resolution: {integrity: sha512-O2S7SNZzdcFG7eFKgvwUEZ2VG9D/sn/eIiz8XRZ1Q/DO5a3s76Xv0mdBzVM5j5R639lXQmPmSo0iRpHqUUrsxw==} + resolution: {integrity: sha512-O2S7SNZzdcFG7eFKgvwUEZ2VG9D/sn/eIiz8XRZ1Q/DO5a3s76Xv0mdBzVM5j5R639lXQmPmSo0iRpHqUUrsxw==, tarball: https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.5.tgz} engines: {node: '>=18'} cpu: [arm64] os: [win32] '@esbuild/win32-arm64@0.27.7': - resolution: {integrity: sha512-7yRhbHvPqSpRUV7Q20VuDwbjW5kIMwTHpptuUzV+AA46kiPze5Z7qgt6CLCK3pWFrHeNfDd1VKgyP4O+ng17CA==} + resolution: {integrity: sha512-7yRhbHvPqSpRUV7Q20VuDwbjW5kIMwTHpptuUzV+AA46kiPze5Z7qgt6CLCK3pWFrHeNfDd1VKgyP4O+ng17CA==, tarball: https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.7.tgz} engines: {node: '>=18'} cpu: [arm64] os: [win32] '@esbuild/win32-arm64@0.28.1': - resolution: {integrity: sha512-lCv9eK/H6ZJWbE7bh2nw54CZ9M2nupBxJcTsdk/QQnWkdSjKGuxmmH8/GWrlT1eMmZfn4dGcCjRte397WqfQXA==} + resolution: {integrity: sha512-lCv9eK/H6ZJWbE7bh2nw54CZ9M2nupBxJcTsdk/QQnWkdSjKGuxmmH8/GWrlT1eMmZfn4dGcCjRte397WqfQXA==, tarball: https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.28.1.tgz} engines: {node: '>=18'} cpu: [arm64] os: [win32] '@esbuild/win32-ia32@0.19.2': - resolution: {integrity: sha512-47gL/ek1v36iN0wL9L4Q2MFdujR0poLZMJwhO2/N3gA89jgHp4MR8DKCmwYtGNksbfJb9JoTtbkoe6sDhg2QTA==} + resolution: {integrity: sha512-47gL/ek1v36iN0wL9L4Q2MFdujR0poLZMJwhO2/N3gA89jgHp4MR8DKCmwYtGNksbfJb9JoTtbkoe6sDhg2QTA==, tarball: https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.19.2.tgz} engines: {node: '>=12'} cpu: [ia32] os: [win32] '@esbuild/win32-ia32@0.23.1': - resolution: {integrity: sha512-BcaL0Vn6QwCwre3Y717nVHZbAa4UBEigzFm6VdsVdT/MbZ38xoj1X9HPkZhbmaBGUD1W8vxAfffbDe8bA6AKnQ==} + resolution: {integrity: sha512-BcaL0Vn6QwCwre3Y717nVHZbAa4UBEigzFm6VdsVdT/MbZ38xoj1X9HPkZhbmaBGUD1W8vxAfffbDe8bA6AKnQ==, tarball: https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.23.1.tgz} engines: {node: '>=18'} cpu: [ia32] os: [win32] '@esbuild/win32-ia32@0.25.5': - resolution: {integrity: sha512-onOJ02pqs9h1iMJ1PQphR+VZv8qBMQ77Klcsqv9CNW2w6yLqoURLcgERAIurY6QE63bbLuqgP9ATqajFLK5AMQ==} + resolution: {integrity: sha512-onOJ02pqs9h1iMJ1PQphR+VZv8qBMQ77Klcsqv9CNW2w6yLqoURLcgERAIurY6QE63bbLuqgP9ATqajFLK5AMQ==, tarball: https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.5.tgz} engines: {node: '>=18'} cpu: [ia32] os: [win32] '@esbuild/win32-ia32@0.27.7': - resolution: {integrity: sha512-SmwKXe6VHIyZYbBLJrhOoCJRB/Z1tckzmgTLfFYOfpMAx63BJEaL9ExI8x7v0oAO3Zh6D/Oi1gVxEYr5oUCFhw==} + resolution: {integrity: sha512-SmwKXe6VHIyZYbBLJrhOoCJRB/Z1tckzmgTLfFYOfpMAx63BJEaL9ExI8x7v0oAO3Zh6D/Oi1gVxEYr5oUCFhw==, tarball: https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.7.tgz} engines: {node: '>=18'} cpu: [ia32] os: [win32] '@esbuild/win32-ia32@0.28.1': - resolution: {integrity: sha512-zvb/mB2bSCoJOpoCBgYKKpX6YM6mJBlBUVUtVj41DlZJVEB6/0CKlRYxP5wWl1C1ILiCoAU5wZZ4q1P3qeS6Eg==} + resolution: {integrity: sha512-zvb/mB2bSCoJOpoCBgYKKpX6YM6mJBlBUVUtVj41DlZJVEB6/0CKlRYxP5wWl1C1ILiCoAU5wZZ4q1P3qeS6Eg==, tarball: https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.28.1.tgz} engines: {node: '>=18'} cpu: [ia32] os: [win32] '@esbuild/win32-x64@0.19.2': - resolution: {integrity: sha512-tcuhV7ncXBqbt/Ybf0IyrMcwVOAPDckMK9rXNHtF17UTK18OKLpg08glminN06pt2WCoALhXdLfSPbVvK/6fxw==} + resolution: {integrity: sha512-tcuhV7ncXBqbt/Ybf0IyrMcwVOAPDckMK9rXNHtF17UTK18OKLpg08glminN06pt2WCoALhXdLfSPbVvK/6fxw==, tarball: https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.19.2.tgz} engines: {node: '>=12'} cpu: [x64] os: [win32] '@esbuild/win32-x64@0.23.1': - resolution: {integrity: sha512-BHpFFeslkWrXWyUPnbKm+xYYVYruCinGcftSBaa8zoF9hZO4BcSCFUvHVTtzpIY6YzUnYtuEhZ+C9iEXjxnasg==} + resolution: {integrity: sha512-BHpFFeslkWrXWyUPnbKm+xYYVYruCinGcftSBaa8zoF9hZO4BcSCFUvHVTtzpIY6YzUnYtuEhZ+C9iEXjxnasg==, tarball: https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.23.1.tgz} engines: {node: '>=18'} cpu: [x64] os: [win32] '@esbuild/win32-x64@0.25.5': - resolution: {integrity: sha512-TXv6YnJ8ZMVdX+SXWVBo/0p8LTcrUYngpWjvm91TMjjBQii7Oz11Lw5lbDV5Y0TzuhSJHwiH4hEtC1I42mMS0g==} + resolution: {integrity: sha512-TXv6YnJ8ZMVdX+SXWVBo/0p8LTcrUYngpWjvm91TMjjBQii7Oz11Lw5lbDV5Y0TzuhSJHwiH4hEtC1I42mMS0g==, tarball: https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.5.tgz} engines: {node: '>=18'} cpu: [x64] os: [win32] '@esbuild/win32-x64@0.27.7': - resolution: {integrity: sha512-56hiAJPhwQ1R4i+21FVF7V8kSD5zZTdHcVuRFMW0hn753vVfQN8xlx4uOPT4xoGH0Z/oVATuR82AiqSTDIpaHg==} + resolution: {integrity: sha512-56hiAJPhwQ1R4i+21FVF7V8kSD5zZTdHcVuRFMW0hn753vVfQN8xlx4uOPT4xoGH0Z/oVATuR82AiqSTDIpaHg==, tarball: https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.7.tgz} engines: {node: '>=18'} cpu: [x64] os: [win32] '@esbuild/win32-x64@0.28.1': - resolution: {integrity: sha512-bm4Mowrv+GXMlpWX++EcXw/iLyd1o3+bJkC2DkWXYVvgZCqD/bSj9ctZeAMC3cIxgjRVR2Dufaiu4YPxr5gW1A==} + resolution: {integrity: sha512-bm4Mowrv+GXMlpWX++EcXw/iLyd1o3+bJkC2DkWXYVvgZCqD/bSj9ctZeAMC3cIxgjRVR2Dufaiu4YPxr5gW1A==, tarball: https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.28.1.tgz} engines: {node: '>=18'} cpu: [x64] os: [win32] '@eslint-community/eslint-utils@4.9.1': - resolution: {integrity: sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==} + resolution: {integrity: sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==, tarball: https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 '@eslint-community/regexpp@4.12.2': - resolution: {integrity: sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==} + resolution: {integrity: sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==, tarball: https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz} engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} '@eslint/config-array@0.21.2': - resolution: {integrity: sha512-nJl2KGTlrf9GjLimgIru+V/mzgSK0ABCDQRvxw5BjURL7WfH5uoWmizbH7QB6MmnMBd8cIC9uceWnezL1VZWWw==} + resolution: {integrity: sha512-nJl2KGTlrf9GjLimgIru+V/mzgSK0ABCDQRvxw5BjURL7WfH5uoWmizbH7QB6MmnMBd8cIC9uceWnezL1VZWWw==, tarball: https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.2.tgz} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@eslint/config-helpers@0.4.2': - resolution: {integrity: sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==} + resolution: {integrity: sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==, tarball: https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.4.2.tgz} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@eslint/core@0.17.0': - resolution: {integrity: sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==} + resolution: {integrity: sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==, tarball: https://registry.npmjs.org/@eslint/core/-/core-0.17.0.tgz} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@eslint/css-tree@4.0.4': - resolution: {integrity: sha512-nxMparyhqVWQvadx9x8dIfubfIPOE+X2b2waua8fzdnM9vdp9rgVtwEZlG0TmCwEUz/d/f40fzvO/eqBwdxz0A==} + resolution: {integrity: sha512-nxMparyhqVWQvadx9x8dIfubfIPOE+X2b2waua8fzdnM9vdp9rgVtwEZlG0TmCwEUz/d/f40fzvO/eqBwdxz0A==, tarball: https://registry.npmjs.org/@eslint/css-tree/-/css-tree-4.0.4.tgz} engines: {node: ^20.19.0 || ^22.13.0 || >=24} '@eslint/eslintrc@3.3.5': - resolution: {integrity: sha512-4IlJx0X0qftVsN5E+/vGujTRIFtwuLbNsVUe7TO6zYPDR1O6nFwvwhIKEKSrl6dZchmYBITazxKoUYOjdtjlRg==} + resolution: {integrity: sha512-4IlJx0X0qftVsN5E+/vGujTRIFtwuLbNsVUe7TO6zYPDR1O6nFwvwhIKEKSrl6dZchmYBITazxKoUYOjdtjlRg==, tarball: https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.5.tgz} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@eslint/js@9.39.4': - resolution: {integrity: sha512-nE7DEIchvtiFTwBw4Lfbu59PG+kCofhjsKaCWzxTpt4lfRjRMqG6uMBzKXuEcyXhOHoUp9riAm7/aWYGhXZ9cw==} + resolution: {integrity: sha512-nE7DEIchvtiFTwBw4Lfbu59PG+kCofhjsKaCWzxTpt4lfRjRMqG6uMBzKXuEcyXhOHoUp9riAm7/aWYGhXZ9cw==, tarball: https://registry.npmjs.org/@eslint/js/-/js-9.39.4.tgz} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@eslint/object-schema@2.1.7': - resolution: {integrity: sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==} + resolution: {integrity: sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==, tarball: https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.7.tgz} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@eslint/plugin-kit@0.4.1': - resolution: {integrity: sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==} + resolution: {integrity: sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==, tarball: https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.4.1.tgz} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@exodus/bytes@1.15.1': - resolution: {integrity: sha512-S6mL0yNB/Abt9Ei4tq8gDhcczc4S3+vQ4ra7vxnAf+YHC02srtqxKKZghx2Dq6p0e66THKwR6r8N6P95wEty7Q==} + resolution: {integrity: sha512-S6mL0yNB/Abt9Ei4tq8gDhcczc4S3+vQ4ra7vxnAf+YHC02srtqxKKZghx2Dq6p0e66THKwR6r8N6P95wEty7Q==, tarball: https://registry.npmjs.org/@exodus/bytes/-/bytes-1.15.1.tgz} engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} peerDependencies: '@noble/hashes': ^1.8.0 || ^2.0.0 @@ -3269,20 +3283,20 @@ packages: optional: true '@faker-js/faker@8.4.1': - resolution: {integrity: sha512-XQ3cU+Q8Uqmrbf2e0cIC/QN43sTBSC8KF12u29Mb47tWrt2hAgBXSgpZMj4Ao8Uk0iJcU99QsOCaIL8934obCg==} + resolution: {integrity: sha512-XQ3cU+Q8Uqmrbf2e0cIC/QN43sTBSC8KF12u29Mb47tWrt2hAgBXSgpZMj4Ao8Uk0iJcU99QsOCaIL8934obCg==, tarball: https://registry.npmjs.org/@faker-js/faker/-/faker-8.4.1.tgz} engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0, npm: '>=6.14.13'} '@floating-ui/core@1.8.0': - resolution: {integrity: sha512-0CIZ5itps/8x7BG8dEIhs53BvCUH2PCoogtakwRTut+Arm58sJooJ0AuZhLw2HJYIR5cMLNPBSS728sPho2khQ==} + resolution: {integrity: sha512-0CIZ5itps/8x7BG8dEIhs53BvCUH2PCoogtakwRTut+Arm58sJooJ0AuZhLw2HJYIR5cMLNPBSS728sPho2khQ==, tarball: https://registry.npmjs.org/@floating-ui/core/-/core-1.8.0.tgz} '@floating-ui/dom@1.6.13': - resolution: {integrity: sha512-umqzocjDgNRGTuO7Q8CU32dkHkECqI8ZdMZ5Swb6QAM0t5rnlrN3lGo1hdpscRd3WS8T6DKYK4ephgIH9iRh3w==} + resolution: {integrity: sha512-umqzocjDgNRGTuO7Q8CU32dkHkECqI8ZdMZ5Swb6QAM0t5rnlrN3lGo1hdpscRd3WS8T6DKYK4ephgIH9iRh3w==, tarball: https://registry.npmjs.org/@floating-ui/dom/-/dom-1.6.13.tgz} '@floating-ui/utils@0.2.12': - resolution: {integrity: sha512-HpCo8tmWzLVad5s2d19EhAz5zqrrQ6s69qd6moPMQvkOuSwDT1YgRfWSVuc4ennqrgv3OHppiOGMQ7oC13yIww==} + resolution: {integrity: sha512-HpCo8tmWzLVad5s2d19EhAz5zqrrQ6s69qd6moPMQvkOuSwDT1YgRfWSVuc4ennqrgv3OHppiOGMQ7oC13yIww==, tarball: https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.12.tgz} '@happy-dom/jest-environment@20.8.3': - resolution: {integrity: sha512-VMOfNvF7UPPHIc7SUrFqGXqJrkONYX6Vd0ZXblmjgb1JA2RFnrc1KiVodzG0c7IT5Q0jfA0CQjvlqWjQ/BYtkQ==} + resolution: {integrity: sha512-VMOfNvF7UPPHIc7SUrFqGXqJrkONYX6Vd0ZXblmjgb1JA2RFnrc1KiVodzG0c7IT5Q0jfA0CQjvlqWjQ/BYtkQ==, tarball: https://registry.npmjs.org/@happy-dom/jest-environment/-/jest-environment-20.8.3.tgz} engines: {node: '>=20.0.0'} peerDependencies: '@jest/environment': '>=25.0.0' @@ -3292,46 +3306,52 @@ packages: jest-util: '>=25.0.0' '@harperfast/extended-iterable@1.0.3': - resolution: {integrity: sha512-sSAYhQca3rDWtQUHSAPeO7axFIUJOI6hn1gjRC5APVE1a90tuyT8f5WIgRsFhhWA7htNkju2veB9eWL6YHi/Lw==} + resolution: {integrity: sha512-sSAYhQca3rDWtQUHSAPeO7axFIUJOI6hn1gjRC5APVE1a90tuyT8f5WIgRsFhhWA7htNkju2veB9eWL6YHi/Lw==, tarball: https://registry.npmjs.org/@harperfast/extended-iterable/-/extended-iterable-1.0.3.tgz} '@hono/node-server@1.19.14': - resolution: {integrity: sha512-GwtvgtXxnWsucXvbQXkRgqksiH2Qed37H9xHZocE5sA3N8O8O8/8FA3uclQXxXVzc9XBZuEOMK7+r02FmSpHtw==} + resolution: {integrity: sha512-GwtvgtXxnWsucXvbQXkRgqksiH2Qed37H9xHZocE5sA3N8O8O8/8FA3uclQXxXVzc9XBZuEOMK7+r02FmSpHtw==, tarball: https://registry.npmjs.org/@hono/node-server/-/node-server-1.19.14.tgz} engines: {node: '>=18.14.1'} peerDependencies: hono: ^4 + '@hono/node-server@2.0.12': + resolution: {integrity: sha512-eWpQYr67tqJLeaSUl0Q+TquuYfUdTibpOJlUMV2FfUP7+KqCC5TufnwnlXL6mobZBJbGAYRd7ZvEBDCbLInjhg==, tarball: https://registry.npmjs.org/@hono/node-server/-/node-server-2.0.12.tgz} + engines: {node: '>=20'} + peerDependencies: + hono: ^4 + '@humanfs/core@0.19.2': - resolution: {integrity: sha512-UhXNm+CFMWcbChXywFwkmhqjs3PRCmcSa/hfBgLIb7oQ5HNb1wS0icWsGtSAUNgefHeI+eBrA8I1fxmbHsGdvA==} + resolution: {integrity: sha512-UhXNm+CFMWcbChXywFwkmhqjs3PRCmcSa/hfBgLIb7oQ5HNb1wS0icWsGtSAUNgefHeI+eBrA8I1fxmbHsGdvA==, tarball: https://registry.npmjs.org/@humanfs/core/-/core-0.19.2.tgz} engines: {node: '>=18.18.0'} '@humanfs/node@0.16.8': - resolution: {integrity: sha512-gE1eQNZ3R++kTzFUpdGlpmy8kDZD/MLyHqDwqjkVQI0JMdI1D51sy1H958PNXYkM2rAac7e5/CnIKZrHtPh3BQ==} + resolution: {integrity: sha512-gE1eQNZ3R++kTzFUpdGlpmy8kDZD/MLyHqDwqjkVQI0JMdI1D51sy1H958PNXYkM2rAac7e5/CnIKZrHtPh3BQ==, tarball: https://registry.npmjs.org/@humanfs/node/-/node-0.16.8.tgz} engines: {node: '>=18.18.0'} '@humanfs/types@0.15.0': - resolution: {integrity: sha512-ZZ1w0aoQkwuUuC7Yf+7sdeaNfqQiiLcSRbfI08oAxqLtpXQr9AIVX7Ay7HLDuiLYAaFPu8oBYNq/QIi9URHJ3Q==} + resolution: {integrity: sha512-ZZ1w0aoQkwuUuC7Yf+7sdeaNfqQiiLcSRbfI08oAxqLtpXQr9AIVX7Ay7HLDuiLYAaFPu8oBYNq/QIi9URHJ3Q==, tarball: https://registry.npmjs.org/@humanfs/types/-/types-0.15.0.tgz} engines: {node: '>=18.18.0'} '@humanwhocodes/module-importer@1.0.1': - resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==} + resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==, tarball: https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz} engines: {node: '>=12.22'} '@humanwhocodes/retry@0.4.3': - resolution: {integrity: sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==} + resolution: {integrity: sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==, tarball: https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz} engines: {node: '>=18.18'} '@iconify/types@2.0.0': - resolution: {integrity: sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg==} + resolution: {integrity: sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg==, tarball: https://registry.npmjs.org/@iconify/types/-/types-2.0.0.tgz} '@iconify/utils@3.1.4': - resolution: {integrity: sha512-b1S7B1k9ohZ+iNTi2ATxbRYG9fTrJmUT0rc46bvVnNxqNRGW7dyo/vRREwyniI5IRN2RSJHDcm+s3BjWrSAjHw==} + resolution: {integrity: sha512-b1S7B1k9ohZ+iNTi2ATxbRYG9fTrJmUT0rc46bvVnNxqNRGW7dyo/vRREwyniI5IRN2RSJHDcm+s3BjWrSAjHw==, tarball: https://registry.npmjs.org/@iconify/utils/-/utils-3.1.4.tgz} '@inquirer/ansi@2.0.7': - resolution: {integrity: sha512-3eTuUO1vH2cZm2ZKHeQxnOqlTi9EfZDGgIe3BL3I4u+rJHocr9Fz86M4fjYABPvFnQG/gGK551HqDiIcETwU6Q==} + resolution: {integrity: sha512-3eTuUO1vH2cZm2ZKHeQxnOqlTi9EfZDGgIe3BL3I4u+rJHocr9Fz86M4fjYABPvFnQG/gGK551HqDiIcETwU6Q==, tarball: https://registry.npmjs.org/@inquirer/ansi/-/ansi-2.0.7.tgz} engines: {node: '>=23.5.0 || ^22.13.0 || ^20.17.0'} '@inquirer/checkbox@5.2.1': - resolution: {integrity: sha512-b6xmA/VlTe0ZgDQHDui+Nav470u7u49nRd8/iuhOcQPO9Ch7lGuogydhi2VOmNlZ+zXcM8IcPuNSwQcdJaF/kw==} + resolution: {integrity: sha512-b6xmA/VlTe0ZgDQHDui+Nav470u7u49nRd8/iuhOcQPO9Ch7lGuogydhi2VOmNlZ+zXcM8IcPuNSwQcdJaF/kw==, tarball: https://registry.npmjs.org/@inquirer/checkbox/-/checkbox-5.2.1.tgz} engines: {node: '>=23.5.0 || ^22.13.0 || ^20.17.0'} peerDependencies: '@types/node': '>=18' @@ -3340,7 +3360,7 @@ packages: optional: true '@inquirer/confirm@6.1.1': - resolution: {integrity: sha512-eb8DBZcz/2qHWQda4rk2JiQk5h9QV/cVHi1yjt0f69WFZMRFn0sJTye3EAP8icut8UDMjQPsaH5KbcOogefrFQ==} + resolution: {integrity: sha512-eb8DBZcz/2qHWQda4rk2JiQk5h9QV/cVHi1yjt0f69WFZMRFn0sJTye3EAP8icut8UDMjQPsaH5KbcOogefrFQ==, tarball: https://registry.npmjs.org/@inquirer/confirm/-/confirm-6.1.1.tgz} engines: {node: '>=23.5.0 || ^22.13.0 || ^20.17.0'} peerDependencies: '@types/node': '>=18' @@ -3349,7 +3369,7 @@ packages: optional: true '@inquirer/core@11.2.1': - resolution: {integrity: sha512-Qd6GJT1yVyrZZCfN8W2qKF5ApmqryXRhRKCuip8h01x2w/esJQ2XIYc6f9abMIHgKQdBfFTSOdbHRLAhuM09UA==} + resolution: {integrity: sha512-Qd6GJT1yVyrZZCfN8W2qKF5ApmqryXRhRKCuip8h01x2w/esJQ2XIYc6f9abMIHgKQdBfFTSOdbHRLAhuM09UA==, tarball: https://registry.npmjs.org/@inquirer/core/-/core-11.2.1.tgz} engines: {node: '>=23.5.0 || ^22.13.0 || ^20.17.0'} peerDependencies: '@types/node': '>=18' @@ -3358,7 +3378,7 @@ packages: optional: true '@inquirer/editor@5.2.2': - resolution: {integrity: sha512-ZRVd/oD+sYsUd5zVm0NflqEzlqfYCyHNsqkHl2oWXEUHs12tCbcSFi+wVFEvD8+LGRaMUsVrE7qeo6lSG/S1Vg==} + resolution: {integrity: sha512-ZRVd/oD+sYsUd5zVm0NflqEzlqfYCyHNsqkHl2oWXEUHs12tCbcSFi+wVFEvD8+LGRaMUsVrE7qeo6lSG/S1Vg==, tarball: https://registry.npmjs.org/@inquirer/editor/-/editor-5.2.2.tgz} engines: {node: '>=23.5.0 || ^22.13.0 || ^20.17.0'} peerDependencies: '@types/node': '>=18' @@ -3367,7 +3387,7 @@ packages: optional: true '@inquirer/expand@5.1.1': - resolution: {integrity: sha512-YmQpenjbFSHAK3sOd44puHh3V1KXXr+JiNpUztoSQ4drLh2rTVzTap/YtlAVu/5xavifIlBfNEzJ/neZJ1a/1g==} + resolution: {integrity: sha512-YmQpenjbFSHAK3sOd44puHh3V1KXXr+JiNpUztoSQ4drLh2rTVzTap/YtlAVu/5xavifIlBfNEzJ/neZJ1a/1g==, tarball: https://registry.npmjs.org/@inquirer/expand/-/expand-5.1.1.tgz} engines: {node: '>=23.5.0 || ^22.13.0 || ^20.17.0'} peerDependencies: '@types/node': '>=18' @@ -3376,7 +3396,7 @@ packages: optional: true '@inquirer/external-editor@3.0.3': - resolution: {integrity: sha512-6thf5I8q7lZwzGLAxPaaGEREEkZ3nyePPDQ1oyobblxmEE8mqTLguScP7pDjUTAibiyb4hfXl+qjUEJ+di/aNA==} + resolution: {integrity: sha512-6thf5I8q7lZwzGLAxPaaGEREEkZ3nyePPDQ1oyobblxmEE8mqTLguScP7pDjUTAibiyb4hfXl+qjUEJ+di/aNA==, tarball: https://registry.npmjs.org/@inquirer/external-editor/-/external-editor-3.0.3.tgz} engines: {node: '>=23.5.0 || ^22.13.0 || ^20.17.0'} peerDependencies: '@types/node': '>=18' @@ -3385,11 +3405,11 @@ packages: optional: true '@inquirer/figures@2.0.7': - resolution: {integrity: sha512-aJ8TBPOGB6f/2qziPfElISTCEd5XOYTFckA2SGjhNmiKzfK/u4ot3v0DUzGVdUnKjN10EqnnEPck36BkyfLnJw==} + resolution: {integrity: sha512-aJ8TBPOGB6f/2qziPfElISTCEd5XOYTFckA2SGjhNmiKzfK/u4ot3v0DUzGVdUnKjN10EqnnEPck36BkyfLnJw==, tarball: https://registry.npmjs.org/@inquirer/figures/-/figures-2.0.7.tgz} engines: {node: '>=23.5.0 || ^22.13.0 || ^20.17.0'} '@inquirer/input@5.1.2': - resolution: {integrity: sha512-9K/DDBSQpOyZSkt6sOVP9Vo0TR7atX2kuILsUu0x3wVcVbe97lJwIJKMLdMw25tDYuXl/qp6erT0Xs1rfmcfZg==} + resolution: {integrity: sha512-9K/DDBSQpOyZSkt6sOVP9Vo0TR7atX2kuILsUu0x3wVcVbe97lJwIJKMLdMw25tDYuXl/qp6erT0Xs1rfmcfZg==, tarball: https://registry.npmjs.org/@inquirer/input/-/input-5.1.2.tgz} engines: {node: '>=23.5.0 || ^22.13.0 || ^20.17.0'} peerDependencies: '@types/node': '>=18' @@ -3398,7 +3418,7 @@ packages: optional: true '@inquirer/number@4.1.1': - resolution: {integrity: sha512-XF4IXAbPnGPgw0wsbC/i2tPcyfdZgDpUlhsqU0SfT4IRIGWha6Xm9VRgN5yYxJq+jnyXlfXI/nQ3ulfk0iEICA==} + resolution: {integrity: sha512-XF4IXAbPnGPgw0wsbC/i2tPcyfdZgDpUlhsqU0SfT4IRIGWha6Xm9VRgN5yYxJq+jnyXlfXI/nQ3ulfk0iEICA==, tarball: https://registry.npmjs.org/@inquirer/number/-/number-4.1.1.tgz} engines: {node: '>=23.5.0 || ^22.13.0 || ^20.17.0'} peerDependencies: '@types/node': '>=18' @@ -3407,7 +3427,7 @@ packages: optional: true '@inquirer/password@5.1.1': - resolution: {integrity: sha512-3XBfF7DAsp5qeDsvN5Rd1HmbNokVvEQoUM0QLrRcybC9nX96w3Pbmu7qUsb3IT3J3jBvs2+mTXaKHOUsgHMLzg==} + resolution: {integrity: sha512-3XBfF7DAsp5qeDsvN5Rd1HmbNokVvEQoUM0QLrRcybC9nX96w3Pbmu7qUsb3IT3J3jBvs2+mTXaKHOUsgHMLzg==, tarball: https://registry.npmjs.org/@inquirer/password/-/password-5.1.1.tgz} engines: {node: '>=23.5.0 || ^22.13.0 || ^20.17.0'} peerDependencies: '@types/node': '>=18' @@ -3416,7 +3436,7 @@ packages: optional: true '@inquirer/prompts@8.5.2': - resolution: {integrity: sha512-IYR/3C/paEVVQYQvdDlFZVjRCJVYHHON0XXMH91KO9GSxs0TdKYWlUdvfQl2EfAHDxUaN3IBffkE/BDTh5nJ6g==} + resolution: {integrity: sha512-IYR/3C/paEVVQYQvdDlFZVjRCJVYHHON0XXMH91KO9GSxs0TdKYWlUdvfQl2EfAHDxUaN3IBffkE/BDTh5nJ6g==, tarball: https://registry.npmjs.org/@inquirer/prompts/-/prompts-8.5.2.tgz} engines: {node: '>=23.5.0 || ^22.13.0 || ^20.17.0'} peerDependencies: '@types/node': '>=18' @@ -3425,7 +3445,7 @@ packages: optional: true '@inquirer/rawlist@5.3.1': - resolution: {integrity: sha512-QqdTqQddL3qPX/PPrjobpsO25NZ4dWXgTLenrR445L2ptLEYE6Z+PD5c5CNDJNx4ugRgELAIpSIJxZaO2jJ2Og==} + resolution: {integrity: sha512-QqdTqQddL3qPX/PPrjobpsO25NZ4dWXgTLenrR445L2ptLEYE6Z+PD5c5CNDJNx4ugRgELAIpSIJxZaO2jJ2Og==, tarball: https://registry.npmjs.org/@inquirer/rawlist/-/rawlist-5.3.1.tgz} engines: {node: '>=23.5.0 || ^22.13.0 || ^20.17.0'} peerDependencies: '@types/node': '>=18' @@ -3434,7 +3454,7 @@ packages: optional: true '@inquirer/search@4.2.1': - resolution: {integrity: sha512-xJj8QWKRSrfKoBIITLZK61dD3zwo0Rz11fgDImku30/Oe81zMdIdGgrLY2h6RkJ+KZ/GhNYIRMKnH/62qBTA5g==} + resolution: {integrity: sha512-xJj8QWKRSrfKoBIITLZK61dD3zwo0Rz11fgDImku30/Oe81zMdIdGgrLY2h6RkJ+KZ/GhNYIRMKnH/62qBTA5g==, tarball: https://registry.npmjs.org/@inquirer/search/-/search-4.2.1.tgz} engines: {node: '>=23.5.0 || ^22.13.0 || ^20.17.0'} peerDependencies: '@types/node': '>=18' @@ -3443,7 +3463,7 @@ packages: optional: true '@inquirer/select@5.2.1': - resolution: {integrity: sha512-FlDndEUww8m7BfukO2nJa25vhD+H5jxxCv4oGioKqzyWz3nPHhhw4LKdYRSlXuAx7DsdWia7iyaBPKKS95Evfw==} + resolution: {integrity: sha512-FlDndEUww8m7BfukO2nJa25vhD+H5jxxCv4oGioKqzyWz3nPHhhw4LKdYRSlXuAx7DsdWia7iyaBPKKS95Evfw==, tarball: https://registry.npmjs.org/@inquirer/select/-/select-5.2.1.tgz} engines: {node: '>=23.5.0 || ^22.13.0 || ^20.17.0'} peerDependencies: '@types/node': '>=18' @@ -3452,7 +3472,7 @@ packages: optional: true '@inquirer/type@4.0.7': - resolution: {integrity: sha512-t28inv14nMQ1PhKpsJPY+kEs/c00qzeCOS2gTNRyTjG5d6qsVA2fItxW4hkvGZ5lvanGLdtCzVIx5dwdRpN1+g==} + resolution: {integrity: sha512-t28inv14nMQ1PhKpsJPY+kEs/c00qzeCOS2gTNRyTjG5d6qsVA2fItxW4hkvGZ5lvanGLdtCzVIx5dwdRpN1+g==, tarball: https://registry.npmjs.org/@inquirer/type/-/type-4.0.7.tgz} engines: {node: '>=23.5.0 || ^22.13.0 || ^20.17.0'} peerDependencies: '@types/node': '>=18' @@ -3461,27 +3481,27 @@ packages: optional: true '@isaacs/cliui@8.0.2': - resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} + resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==, tarball: https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz} engines: {node: '>=12'} '@istanbuljs/load-nyc-config@1.1.0': - resolution: {integrity: sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==} + resolution: {integrity: sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==, tarball: https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz} engines: {node: '>=8'} '@istanbuljs/schema@0.1.6': - resolution: {integrity: sha512-+Sg6GCR/wy1oSmQDFq4LQDAhm3ETKnorxN+y5nbLULOR3P0c14f2Wurzj3/xqPXtasLFfHd5iRFQ7AJt4KH2cw==} + resolution: {integrity: sha512-+Sg6GCR/wy1oSmQDFq4LQDAhm3ETKnorxN+y5nbLULOR3P0c14f2Wurzj3/xqPXtasLFfHd5iRFQ7AJt4KH2cw==, tarball: https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.6.tgz} engines: {node: '>=8'} '@jest/console@30.2.0': - resolution: {integrity: sha512-+O1ifRjkvYIkBqASKWgLxrpEhQAAE7hY77ALLUufSk5717KfOShg6IbqLmdsLMPdUiFvA2kTs0R7YZy+l0IzZQ==} + resolution: {integrity: sha512-+O1ifRjkvYIkBqASKWgLxrpEhQAAE7hY77ALLUufSk5717KfOShg6IbqLmdsLMPdUiFvA2kTs0R7YZy+l0IzZQ==, tarball: https://registry.npmjs.org/@jest/console/-/console-30.2.0.tgz} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} '@jest/console@30.4.1': - resolution: {integrity: sha512-v3bhyxUh9Hgmo5p6hAOXe14/R3ZxZDOsvHleh4B07z3m/x4/ngPUXEm9XwK4sF4u+f+P2ORb0Ge+MgpaqRMVDA==} + resolution: {integrity: sha512-v3bhyxUh9Hgmo5p6hAOXe14/R3ZxZDOsvHleh4B07z3m/x4/ngPUXEm9XwK4sF4u+f+P2ORb0Ge+MgpaqRMVDA==, tarball: https://registry.npmjs.org/@jest/console/-/console-30.4.1.tgz} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} '@jest/core@30.2.0': - resolution: {integrity: sha512-03W6IhuhjqTlpzh/ojut/pDB2LPRygyWX8ExpgHtQA8H/3K7+1vKmcINx5UzeOX1se6YEsBsOHQ1CRzf3fOwTQ==} + resolution: {integrity: sha512-03W6IhuhjqTlpzh/ojut/pDB2LPRygyWX8ExpgHtQA8H/3K7+1vKmcINx5UzeOX1se6YEsBsOHQ1CRzf3fOwTQ==, tarball: https://registry.npmjs.org/@jest/core/-/core-30.2.0.tgz} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} peerDependencies: node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 @@ -3490,15 +3510,15 @@ packages: optional: true '@jest/diff-sequences@30.0.1': - resolution: {integrity: sha512-n5H8QLDJ47QqbCNn5SuFjCRDrOLEZ0h8vAHCK5RL9Ls7Xa8AQLa/YxAc9UjFqoEDM48muwtBGjtMY5cr0PLDCw==} + resolution: {integrity: sha512-n5H8QLDJ47QqbCNn5SuFjCRDrOLEZ0h8vAHCK5RL9Ls7Xa8AQLa/YxAc9UjFqoEDM48muwtBGjtMY5cr0PLDCw==, tarball: https://registry.npmjs.org/@jest/diff-sequences/-/diff-sequences-30.0.1.tgz} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} '@jest/diff-sequences@30.4.0': - resolution: {integrity: sha512-zOpzlfUs45l6u7jm39qr87JCHUDsaeCtvL+kQe/Vn9jSnRB4/5IPXISm0h9I1vZW/o00Kn4UTJ2MOlhnUGwv3g==} + resolution: {integrity: sha512-zOpzlfUs45l6u7jm39qr87JCHUDsaeCtvL+kQe/Vn9jSnRB4/5IPXISm0h9I1vZW/o00Kn4UTJ2MOlhnUGwv3g==, tarball: https://registry.npmjs.org/@jest/diff-sequences/-/diff-sequences-30.4.0.tgz} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} '@jest/environment-jsdom-abstract@30.4.1': - resolution: {integrity: sha512-dSlKrqug3siYNHVnjwIldShY12wAH3spwRltO/+8VOjg0X+xEq7vOs3DbBs4LRKsu7OH+NUb9kuZUNBF9Ho3TA==} + resolution: {integrity: sha512-dSlKrqug3siYNHVnjwIldShY12wAH3spwRltO/+8VOjg0X+xEq7vOs3DbBs4LRKsu7OH+NUb9kuZUNBF9Ho3TA==, tarball: https://registry.npmjs.org/@jest/environment-jsdom-abstract/-/environment-jsdom-abstract-30.4.1.tgz} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} peerDependencies: canvas: ^3.0.0 @@ -3508,67 +3528,67 @@ packages: optional: true '@jest/environment@29.7.0': - resolution: {integrity: sha512-aQIfHDq33ExsN4jP1NWGXhxgQ/wixs60gDiKO+XVMd8Mn0NWPWgc34ZQDTb2jKaUWQ7MuwoitXAsN2XVXNMpAw==} + resolution: {integrity: sha512-aQIfHDq33ExsN4jP1NWGXhxgQ/wixs60gDiKO+XVMd8Mn0NWPWgc34ZQDTb2jKaUWQ7MuwoitXAsN2XVXNMpAw==, tarball: https://registry.npmjs.org/@jest/environment/-/environment-29.7.0.tgz} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} '@jest/environment@30.2.0': - resolution: {integrity: sha512-/QPTL7OBJQ5ac09UDRa3EQes4gt1FTEG/8jZ/4v5IVzx+Cv7dLxlVIvfvSVRiiX2drWyXeBjkMSR8hvOWSog5g==} + resolution: {integrity: sha512-/QPTL7OBJQ5ac09UDRa3EQes4gt1FTEG/8jZ/4v5IVzx+Cv7dLxlVIvfvSVRiiX2drWyXeBjkMSR8hvOWSog5g==, tarball: https://registry.npmjs.org/@jest/environment/-/environment-30.2.0.tgz} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} '@jest/environment@30.4.1': - resolution: {integrity: sha512-AK9yNRqgKxiabqMoe4oW+3/TSSeV8vkdC7BGaxZdU0AFXfOpofTLqdru2GXKZghP3sdgwE9XXpnVwfZ8JnFV4w==} + resolution: {integrity: sha512-AK9yNRqgKxiabqMoe4oW+3/TSSeV8vkdC7BGaxZdU0AFXfOpofTLqdru2GXKZghP3sdgwE9XXpnVwfZ8JnFV4w==, tarball: https://registry.npmjs.org/@jest/environment/-/environment-30.4.1.tgz} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} '@jest/expect-utils@30.2.0': - resolution: {integrity: sha512-1JnRfhqpD8HGpOmQp180Fo9Zt69zNtC+9lR+kT7NVL05tNXIi+QC8Csz7lfidMoVLPD3FnOtcmp0CEFnxExGEA==} + resolution: {integrity: sha512-1JnRfhqpD8HGpOmQp180Fo9Zt69zNtC+9lR+kT7NVL05tNXIi+QC8Csz7lfidMoVLPD3FnOtcmp0CEFnxExGEA==, tarball: https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-30.2.0.tgz} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} '@jest/expect-utils@30.4.1': - resolution: {integrity: sha512-ZBn5CglH8fBsQsvs4VWNzD4aWfUYks+IdOOQU3MEK71ol/BcVm+P+rtb1KpiFBpSWSCE27uOahyyf1vfqOVbcQ==} + resolution: {integrity: sha512-ZBn5CglH8fBsQsvs4VWNzD4aWfUYks+IdOOQU3MEK71ol/BcVm+P+rtb1KpiFBpSWSCE27uOahyyf1vfqOVbcQ==, tarball: https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-30.4.1.tgz} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} '@jest/expect@30.2.0': - resolution: {integrity: sha512-V9yxQK5erfzx99Sf+7LbhBwNWEZ9eZay8qQ9+JSC0TrMR1pMDHLMY+BnVPacWU6Jamrh252/IKo4F1Xn/zfiqA==} + resolution: {integrity: sha512-V9yxQK5erfzx99Sf+7LbhBwNWEZ9eZay8qQ9+JSC0TrMR1pMDHLMY+BnVPacWU6Jamrh252/IKo4F1Xn/zfiqA==, tarball: https://registry.npmjs.org/@jest/expect/-/expect-30.2.0.tgz} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} '@jest/expect@30.4.1': - resolution: {integrity: sha512-ginrj6TMgh2GshLUGCjO94Ptx9HhdZA/I6A9iUfyeLKFtdAjnKzHDgzgP9HYQgbxM1lbXScQ2eUBz2lGeVDPWA==} + resolution: {integrity: sha512-ginrj6TMgh2GshLUGCjO94Ptx9HhdZA/I6A9iUfyeLKFtdAjnKzHDgzgP9HYQgbxM1lbXScQ2eUBz2lGeVDPWA==, tarball: https://registry.npmjs.org/@jest/expect/-/expect-30.4.1.tgz} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} '@jest/fake-timers@29.7.0': - resolution: {integrity: sha512-q4DH1Ha4TTFPdxLsqDXK1d3+ioSL7yL5oCMJZgDYm6i+6CygW5E5xVr/D1HdsGxjt1ZWSfUAs9OxSB/BNelWrQ==} + resolution: {integrity: sha512-q4DH1Ha4TTFPdxLsqDXK1d3+ioSL7yL5oCMJZgDYm6i+6CygW5E5xVr/D1HdsGxjt1ZWSfUAs9OxSB/BNelWrQ==, tarball: https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-29.7.0.tgz} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} '@jest/fake-timers@30.2.0': - resolution: {integrity: sha512-HI3tRLjRxAbBy0VO8dqqm7Hb2mIa8d5bg/NJkyQcOk7V118ObQML8RC5luTF/Zsg4474a+gDvhce7eTnP4GhYw==} + resolution: {integrity: sha512-HI3tRLjRxAbBy0VO8dqqm7Hb2mIa8d5bg/NJkyQcOk7V118ObQML8RC5luTF/Zsg4474a+gDvhce7eTnP4GhYw==, tarball: https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-30.2.0.tgz} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} '@jest/fake-timers@30.4.1': - resolution: {integrity: sha512-iW5umdmfPeWzehrVhugFQZqCchSCud5S1l2YT0O9ZhjRR0ExclANDZkiSBwzqtnlOn0J1JXvO+HZ6rkuyOVOgQ==} + resolution: {integrity: sha512-iW5umdmfPeWzehrVhugFQZqCchSCud5S1l2YT0O9ZhjRR0ExclANDZkiSBwzqtnlOn0J1JXvO+HZ6rkuyOVOgQ==, tarball: https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-30.4.1.tgz} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} '@jest/get-type@30.1.0': - resolution: {integrity: sha512-eMbZE2hUnx1WV0pmURZY9XoXPkUYjpc55mb0CrhtdWLtzMQPFvu/rZkTLZFTsdaVQa+Tr4eWAteqcUzoawq/uA==} + resolution: {integrity: sha512-eMbZE2hUnx1WV0pmURZY9XoXPkUYjpc55mb0CrhtdWLtzMQPFvu/rZkTLZFTsdaVQa+Tr4eWAteqcUzoawq/uA==, tarball: https://registry.npmjs.org/@jest/get-type/-/get-type-30.1.0.tgz} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} '@jest/globals@30.2.0': - resolution: {integrity: sha512-b63wmnKPaK+6ZZfpYhz9K61oybvbI1aMcIs80++JI1O1rR1vaxHUCNqo3ITu6NU0d4V34yZFoHMn/uoKr/Rwfw==} + resolution: {integrity: sha512-b63wmnKPaK+6ZZfpYhz9K61oybvbI1aMcIs80++JI1O1rR1vaxHUCNqo3ITu6NU0d4V34yZFoHMn/uoKr/Rwfw==, tarball: https://registry.npmjs.org/@jest/globals/-/globals-30.2.0.tgz} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} '@jest/globals@30.4.1': - resolution: {integrity: sha512-ZbuY4cmXC8DkxYjfvT2DbcHWL2T6vmsMhXCDcmTB2T0y0gaezBI77ufq5ZAIdcRkYZ7NEQEDg1xFeKbxUJ5v5Q==} + resolution: {integrity: sha512-ZbuY4cmXC8DkxYjfvT2DbcHWL2T6vmsMhXCDcmTB2T0y0gaezBI77ufq5ZAIdcRkYZ7NEQEDg1xFeKbxUJ5v5Q==, tarball: https://registry.npmjs.org/@jest/globals/-/globals-30.4.1.tgz} 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==} + resolution: {integrity: sha512-gWp7NfQW27LaBQz3TITS8L7ZCQ0TLvtmI//4OwlQRx4rnWxcPNIYjxZpDcN4+UlGxgm3jS5QPz8IPTCkb59wZA==, tarball: https://registry.npmjs.org/@jest/pattern/-/pattern-30.0.1.tgz} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} '@jest/pattern@30.4.0': - resolution: {integrity: sha512-RAWn3+f9u8BsHijKJ71uHcFp6vmyEt6VvoWXkl6hKF3qVIuWNmudVjg12DlBPGup/frIl5UcUlH5HfEuvHpEXg==} + resolution: {integrity: sha512-RAWn3+f9u8BsHijKJ71uHcFp6vmyEt6VvoWXkl6hKF3qVIuWNmudVjg12DlBPGup/frIl5UcUlH5HfEuvHpEXg==, tarball: https://registry.npmjs.org/@jest/pattern/-/pattern-30.4.0.tgz} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} '@jest/reporters@30.2.0': - resolution: {integrity: sha512-DRyW6baWPqKMa9CzeiBjHwjd8XeAyco2Vt8XbcLFjiwCOEKOvy82GJ8QQnJE9ofsxCMPjH4MfH8fCWIHHDKpAQ==} + resolution: {integrity: sha512-DRyW6baWPqKMa9CzeiBjHwjd8XeAyco2Vt8XbcLFjiwCOEKOvy82GJ8QQnJE9ofsxCMPjH4MfH8fCWIHHDKpAQ==, tarball: https://registry.npmjs.org/@jest/reporters/-/reporters-30.2.0.tgz} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} peerDependencies: node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 @@ -3577,7 +3597,7 @@ packages: optional: true '@jest/reporters@30.4.1': - resolution: {integrity: sha512-/SnkPCzEQpUaBH81kjdEdDdo2WZl5hxw+BmLDGWjRkm8o7XlhjwsU36cqwe5PGBE5WYpBvDzRSdXx9rbGuJtNA==} + resolution: {integrity: sha512-/SnkPCzEQpUaBH81kjdEdDdo2WZl5hxw+BmLDGWjRkm8o7XlhjwsU36cqwe5PGBE5WYpBvDzRSdXx9rbGuJtNA==, tarball: https://registry.npmjs.org/@jest/reporters/-/reporters-30.4.1.tgz} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} peerDependencies: node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 @@ -3586,436 +3606,433 @@ packages: optional: true '@jest/schemas@29.6.3': - resolution: {integrity: sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==} + resolution: {integrity: sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==, tarball: https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} '@jest/schemas@30.0.5': - resolution: {integrity: sha512-DmdYgtezMkh3cpU8/1uyXakv3tJRcmcXxBOcO0tbaozPwpmh4YMsnWrQm9ZmZMfa5ocbxzbFk6O4bDPEc/iAnA==} + resolution: {integrity: sha512-DmdYgtezMkh3cpU8/1uyXakv3tJRcmcXxBOcO0tbaozPwpmh4YMsnWrQm9ZmZMfa5ocbxzbFk6O4bDPEc/iAnA==, tarball: https://registry.npmjs.org/@jest/schemas/-/schemas-30.0.5.tgz} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} '@jest/schemas@30.4.1': - resolution: {integrity: sha512-i6b4qw5qnP8c5FEeBJg/uZQ4ddrkN6Ca8qISJh0pr7a5hfn3h3v5x60BEbOC7OYAGZNMs1LfFLwnW2CuK8F57Q==} + resolution: {integrity: sha512-i6b4qw5qnP8c5FEeBJg/uZQ4ddrkN6Ca8qISJh0pr7a5hfn3h3v5x60BEbOC7OYAGZNMs1LfFLwnW2CuK8F57Q==, tarball: https://registry.npmjs.org/@jest/schemas/-/schemas-30.4.1.tgz} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} '@jest/snapshot-utils@30.2.0': - resolution: {integrity: sha512-0aVxM3RH6DaiLcjj/b0KrIBZhSX1373Xci4l3cW5xiUWPctZ59zQ7jj4rqcJQ/Z8JuN/4wX3FpJSa3RssVvCug==} + resolution: {integrity: sha512-0aVxM3RH6DaiLcjj/b0KrIBZhSX1373Xci4l3cW5xiUWPctZ59zQ7jj4rqcJQ/Z8JuN/4wX3FpJSa3RssVvCug==, tarball: https://registry.npmjs.org/@jest/snapshot-utils/-/snapshot-utils-30.2.0.tgz} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} '@jest/snapshot-utils@30.4.1': - resolution: {integrity: sha512-ObY4ljvQ95mt6iwKtVLetR/4yXiAgl3H4nJxhztr0MTjrN97TwDYrnCp/kF60Ec9HdhkWTHSu+Hg05aXfngpOA==} + resolution: {integrity: sha512-ObY4ljvQ95mt6iwKtVLetR/4yXiAgl3H4nJxhztr0MTjrN97TwDYrnCp/kF60Ec9HdhkWTHSu+Hg05aXfngpOA==, tarball: https://registry.npmjs.org/@jest/snapshot-utils/-/snapshot-utils-30.4.1.tgz} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} '@jest/source-map@30.0.1': - resolution: {integrity: sha512-MIRWMUUR3sdbP36oyNyhbThLHyJ2eEDClPCiHVbrYAe5g3CHRArIVpBw7cdSB5fr+ofSfIb2Tnsw8iEHL0PYQg==} + resolution: {integrity: sha512-MIRWMUUR3sdbP36oyNyhbThLHyJ2eEDClPCiHVbrYAe5g3CHRArIVpBw7cdSB5fr+ofSfIb2Tnsw8iEHL0PYQg==, tarball: https://registry.npmjs.org/@jest/source-map/-/source-map-30.0.1.tgz} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} '@jest/test-result@30.2.0': - resolution: {integrity: sha512-RF+Z+0CCHkARz5HT9mcQCBulb1wgCP3FBvl9VFokMX27acKphwyQsNuWH3c+ojd1LeWBLoTYoxF0zm6S/66mjg==} + resolution: {integrity: sha512-RF+Z+0CCHkARz5HT9mcQCBulb1wgCP3FBvl9VFokMX27acKphwyQsNuWH3c+ojd1LeWBLoTYoxF0zm6S/66mjg==, tarball: https://registry.npmjs.org/@jest/test-result/-/test-result-30.2.0.tgz} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} '@jest/test-result@30.4.1': - resolution: {integrity: sha512-/ZG7pgEiOmmWkN9TplKbOu4id2N5lh7FHwRwlkgBVAzGdRH+OkkQ8wX/kIxg4zmd3ZQvAL1RwL2yWsvNYYECTw==} + resolution: {integrity: sha512-/ZG7pgEiOmmWkN9TplKbOu4id2N5lh7FHwRwlkgBVAzGdRH+OkkQ8wX/kIxg4zmd3ZQvAL1RwL2yWsvNYYECTw==, tarball: https://registry.npmjs.org/@jest/test-result/-/test-result-30.4.1.tgz} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} '@jest/test-sequencer@30.2.0': - resolution: {integrity: sha512-wXKgU/lk8fKXMu/l5Hog1R61bL4q5GCdT6OJvdAFz1P+QrpoFuLU68eoKuVc4RbrTtNnTL5FByhWdLgOPSph+Q==} + resolution: {integrity: sha512-wXKgU/lk8fKXMu/l5Hog1R61bL4q5GCdT6OJvdAFz1P+QrpoFuLU68eoKuVc4RbrTtNnTL5FByhWdLgOPSph+Q==, tarball: https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-30.2.0.tgz} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} '@jest/test-sequencer@30.4.1': - resolution: {integrity: sha512-PeYE+4td5rKjoRPxztObrXU+H8hsjZfxKMXOcmrr34JerSyB/ROOxbbicz8B7A5j9R9VayDnVPvBmedqCsFCdw==} + resolution: {integrity: sha512-PeYE+4td5rKjoRPxztObrXU+H8hsjZfxKMXOcmrr34JerSyB/ROOxbbicz8B7A5j9R9VayDnVPvBmedqCsFCdw==, tarball: https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-30.4.1.tgz} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} '@jest/transform@30.2.0': - resolution: {integrity: sha512-XsauDV82o5qXbhalKxD7p4TZYYdwcaEXC77PPD2HixEFF+6YGppjrAAQurTl2ECWcEomHBMMNS9AH3kcCFx8jA==} + resolution: {integrity: sha512-XsauDV82o5qXbhalKxD7p4TZYYdwcaEXC77PPD2HixEFF+6YGppjrAAQurTl2ECWcEomHBMMNS9AH3kcCFx8jA==, tarball: https://registry.npmjs.org/@jest/transform/-/transform-30.2.0.tgz} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} '@jest/transform@30.4.1': - resolution: {integrity: sha512-Wz0LyktlTvRefoymh+n64hQ84KNXsRGcwdoZ8CSa0Ea+fgYcHZlnk+hDP7v2MS7il2bQ5uTEIxf4/NNfhMN4KQ==} + resolution: {integrity: sha512-Wz0LyktlTvRefoymh+n64hQ84KNXsRGcwdoZ8CSa0Ea+fgYcHZlnk+hDP7v2MS7il2bQ5uTEIxf4/NNfhMN4KQ==, tarball: https://registry.npmjs.org/@jest/transform/-/transform-30.4.1.tgz} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} '@jest/types@29.6.3': - resolution: {integrity: sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==} + resolution: {integrity: sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==, tarball: https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} '@jest/types@30.2.0': - resolution: {integrity: sha512-H9xg1/sfVvyfU7o3zMfBEjQ1gcsdeTMgqHoYdN79tuLqfTtuu7WckRA1R5whDwOzxaZAeMKTYWqP+WCAi0CHsg==} + resolution: {integrity: sha512-H9xg1/sfVvyfU7o3zMfBEjQ1gcsdeTMgqHoYdN79tuLqfTtuu7WckRA1R5whDwOzxaZAeMKTYWqP+WCAi0CHsg==, tarball: https://registry.npmjs.org/@jest/types/-/types-30.2.0.tgz} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} '@jest/types@30.4.1': - resolution: {integrity: sha512-f1x/vJXIfjOlEmejYpbkbgw1gOqpPECwMvMEtBqe47j7H2Hg8h8w3o3ikhSXq3MI15kg+oQ0exWO0uCtTNJLoQ==} + resolution: {integrity: sha512-f1x/vJXIfjOlEmejYpbkbgw1gOqpPECwMvMEtBqe47j7H2Hg8h8w3o3ikhSXq3MI15kg+oQ0exWO0uCtTNJLoQ==, tarball: https://registry.npmjs.org/@jest/types/-/types-30.4.1.tgz} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} '@jitsu/sdk-js@3.1.5': - resolution: {integrity: sha512-e7P8uvGBNwWCC864MItcYSv0IT2+nwYiX8QXZ0lfyG8hutEXXX9Yt2/+MynXuGxMIoIrJ+j0bWVu20k9rrhxSg==} + resolution: {integrity: sha512-e7P8uvGBNwWCC864MItcYSv0IT2+nwYiX8QXZ0lfyG8hutEXXX9Yt2/+MynXuGxMIoIrJ+j0bWVu20k9rrhxSg==, tarball: https://registry.npmjs.org/@jitsu/sdk-js/-/sdk-js-3.1.5.tgz} deprecated: This package is for legacy Jitsu Classic version. For latest version of Jitsu please use @jitsu/js '@jridgewell/gen-mapping@0.3.13': - resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==} + resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==, tarball: https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz} '@jridgewell/remapping@2.3.5': - resolution: {integrity: sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==} + resolution: {integrity: sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==, tarball: https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz} '@jridgewell/resolve-uri@3.1.2': - resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} + resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==, tarball: https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz} engines: {node: '>=6.0.0'} '@jridgewell/source-map@0.3.11': - resolution: {integrity: sha512-ZMp1V8ZFcPG5dIWnQLr3NSI1MiCU7UETdS/A0G8V/XWHvJv3ZsFqutJn1Y5RPmAPX6F3BiE397OqveU/9NCuIA==} + resolution: {integrity: sha512-ZMp1V8ZFcPG5dIWnQLr3NSI1MiCU7UETdS/A0G8V/XWHvJv3ZsFqutJn1Y5RPmAPX6F3BiE397OqveU/9NCuIA==, tarball: https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.11.tgz} '@jridgewell/sourcemap-codec@1.5.5': - resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==} + resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==, tarball: https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz} '@jridgewell/trace-mapping@0.3.31': - resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==} + resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==, tarball: https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz} '@jridgewell/trace-mapping@0.3.9': - resolution: {integrity: sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==} - - '@jsdevtools/ono@7.1.3': - resolution: {integrity: sha512-4JQNk+3mVzK3xh2rqd6RB4J46qUR19azEHBneZyTZM+c456qOrbbM/5xcR8huNCCcbVt7+UmizG6GuUvPvKUYg==} + resolution: {integrity: sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==, tarball: https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz} '@jsonjoy.com/base64@1.1.2': - resolution: {integrity: sha512-q6XAnWQDIMA3+FTiOYajoYqySkO+JSat0ytXGSuRdq9uXE7o92gzuQwQM14xaCRlBLGq3v5miDGC4vkVTn54xA==} + resolution: {integrity: sha512-q6XAnWQDIMA3+FTiOYajoYqySkO+JSat0ytXGSuRdq9uXE7o92gzuQwQM14xaCRlBLGq3v5miDGC4vkVTn54xA==, tarball: https://registry.npmjs.org/@jsonjoy.com/base64/-/base64-1.1.2.tgz} engines: {node: '>=10.0'} peerDependencies: tslib: '2' '@jsonjoy.com/base64@17.67.0': - resolution: {integrity: sha512-5SEsJGsm15aP8TQGkDfJvz9axgPwAEm98S5DxOuYe8e1EbfajcDmgeXXzccEjh+mLnjqEKrkBdjHWS5vFNwDdw==} + resolution: {integrity: sha512-5SEsJGsm15aP8TQGkDfJvz9axgPwAEm98S5DxOuYe8e1EbfajcDmgeXXzccEjh+mLnjqEKrkBdjHWS5vFNwDdw==, tarball: https://registry.npmjs.org/@jsonjoy.com/base64/-/base64-17.67.0.tgz} engines: {node: '>=10.0'} peerDependencies: tslib: '2' '@jsonjoy.com/buffers@1.2.1': - resolution: {integrity: sha512-12cdlDwX4RUM3QxmUbVJWqZ/mrK6dFQH4Zxq6+r1YXKXYBNgZXndx2qbCJwh3+WWkCSn67IjnlG3XYTvmvYtgA==} + resolution: {integrity: sha512-12cdlDwX4RUM3QxmUbVJWqZ/mrK6dFQH4Zxq6+r1YXKXYBNgZXndx2qbCJwh3+WWkCSn67IjnlG3XYTvmvYtgA==, tarball: https://registry.npmjs.org/@jsonjoy.com/buffers/-/buffers-1.2.1.tgz} engines: {node: '>=10.0'} peerDependencies: tslib: '2' '@jsonjoy.com/buffers@17.67.0': - resolution: {integrity: sha512-tfExRpYxBvi32vPs9ZHaTjSP4fHAfzSmcahOfNxtvGHcyJel+aibkPlGeBB+7AoC6hL7lXIE++8okecBxx7lcw==} + resolution: {integrity: sha512-tfExRpYxBvi32vPs9ZHaTjSP4fHAfzSmcahOfNxtvGHcyJel+aibkPlGeBB+7AoC6hL7lXIE++8okecBxx7lcw==, tarball: https://registry.npmjs.org/@jsonjoy.com/buffers/-/buffers-17.67.0.tgz} engines: {node: '>=10.0'} peerDependencies: tslib: '2' '@jsonjoy.com/codegen@1.0.0': - resolution: {integrity: sha512-E8Oy+08cmCf0EK/NMxpaJZmOxPqM+6iSe2S4nlSBrPZOORoDJILxtbSUEDKQyTamm/BVAhIGllOBNU79/dwf0g==} + resolution: {integrity: sha512-E8Oy+08cmCf0EK/NMxpaJZmOxPqM+6iSe2S4nlSBrPZOORoDJILxtbSUEDKQyTamm/BVAhIGllOBNU79/dwf0g==, tarball: https://registry.npmjs.org/@jsonjoy.com/codegen/-/codegen-1.0.0.tgz} engines: {node: '>=10.0'} peerDependencies: tslib: '2' '@jsonjoy.com/codegen@17.67.0': - resolution: {integrity: sha512-idnkUplROpdBOV0HMcwhsCUS5TRUi9poagdGs70A6S4ux9+/aPuKbh8+UYRTLYQHtXvAdNfQWXDqZEx5k4Dj2Q==} + resolution: {integrity: sha512-idnkUplROpdBOV0HMcwhsCUS5TRUi9poagdGs70A6S4ux9+/aPuKbh8+UYRTLYQHtXvAdNfQWXDqZEx5k4Dj2Q==, tarball: https://registry.npmjs.org/@jsonjoy.com/codegen/-/codegen-17.67.0.tgz} engines: {node: '>=10.0'} peerDependencies: tslib: '2' '@jsonjoy.com/fs-core@4.64.0': - resolution: {integrity: sha512-zs2TAq7Six5jgMuoMNjpspAvOP3mhtgq/k1UyQodEzCtQi/N83y2/y+zcvnZSGp/Rxq96DBN+bValOBQAyn/ew==} + resolution: {integrity: sha512-zs2TAq7Six5jgMuoMNjpspAvOP3mhtgq/k1UyQodEzCtQi/N83y2/y+zcvnZSGp/Rxq96DBN+bValOBQAyn/ew==, tarball: https://registry.npmjs.org/@jsonjoy.com/fs-core/-/fs-core-4.64.0.tgz} engines: {node: '>=10.0'} peerDependencies: tslib: '2' '@jsonjoy.com/fs-fsa@4.64.0': - resolution: {integrity: sha512-nMWOVbkLFyEgmXZih3wyvxA9XpgyyqyfrINMHvEFqhi7uqfRl7c9ERJt6yX7vgMPrB9Uo+OJO+Spa0cFzPD01w==} + resolution: {integrity: sha512-nMWOVbkLFyEgmXZih3wyvxA9XpgyyqyfrINMHvEFqhi7uqfRl7c9ERJt6yX7vgMPrB9Uo+OJO+Spa0cFzPD01w==, tarball: https://registry.npmjs.org/@jsonjoy.com/fs-fsa/-/fs-fsa-4.64.0.tgz} engines: {node: '>=10.0'} peerDependencies: tslib: '2' '@jsonjoy.com/fs-node-builtins@4.64.0': - resolution: {integrity: sha512-/o7WRFhUWaM/fOrslwLZGnzn4RmRILykn+lAL+mNObqqRNw+CQSiij6hpCeZ+C7buhdoVo7go/OYqzaSUfDYmA==} + resolution: {integrity: sha512-/o7WRFhUWaM/fOrslwLZGnzn4RmRILykn+lAL+mNObqqRNw+CQSiij6hpCeZ+C7buhdoVo7go/OYqzaSUfDYmA==, tarball: https://registry.npmjs.org/@jsonjoy.com/fs-node-builtins/-/fs-node-builtins-4.64.0.tgz} engines: {node: '>=10.0'} peerDependencies: tslib: '2' '@jsonjoy.com/fs-node-to-fsa@4.64.0': - resolution: {integrity: sha512-WDD9WVs0hb7UAEKTgZW2f66WDrbj7gIIWwpP3spbLyXa0rghtUaFTB8L4gdR3ZCWwiKIsj38/CNijpVmpnuPUw==} + resolution: {integrity: sha512-WDD9WVs0hb7UAEKTgZW2f66WDrbj7gIIWwpP3spbLyXa0rghtUaFTB8L4gdR3ZCWwiKIsj38/CNijpVmpnuPUw==, tarball: https://registry.npmjs.org/@jsonjoy.com/fs-node-to-fsa/-/fs-node-to-fsa-4.64.0.tgz} engines: {node: '>=10.0'} peerDependencies: tslib: '2' '@jsonjoy.com/fs-node-utils@4.64.0': - resolution: {integrity: sha512-k5Indsx9hWW9xSF7Y6oSKKwtCUNhzZxadub3owhIlitc+iMRVlPPdX2duTKQWBL3qNWpXya8jykgaaWpheeS4w==} + resolution: {integrity: sha512-k5Indsx9hWW9xSF7Y6oSKKwtCUNhzZxadub3owhIlitc+iMRVlPPdX2duTKQWBL3qNWpXya8jykgaaWpheeS4w==, tarball: https://registry.npmjs.org/@jsonjoy.com/fs-node-utils/-/fs-node-utils-4.64.0.tgz} engines: {node: '>=10.0'} peerDependencies: tslib: '2' '@jsonjoy.com/fs-node@4.64.0': - resolution: {integrity: sha512-dO+NNkODbUli4uV42bcNrrLvq5rE7SNpdZ5TNd0dtbLsAaNK3MDiIC9lUi+brboGoIjW6vd2fB1qao60nrk5xA==} + resolution: {integrity: sha512-dO+NNkODbUli4uV42bcNrrLvq5rE7SNpdZ5TNd0dtbLsAaNK3MDiIC9lUi+brboGoIjW6vd2fB1qao60nrk5xA==, tarball: https://registry.npmjs.org/@jsonjoy.com/fs-node/-/fs-node-4.64.0.tgz} engines: {node: '>=10.0'} peerDependencies: tslib: '2' '@jsonjoy.com/fs-print@4.64.0': - resolution: {integrity: sha512-PHZFccchvkhWrwPWHjmVAhbC3vSHCtyZvlZfJJ3ho2bnzl450hXri6/8e6pbkWdH+SkmLXNml0sV8e5HDAfxKw==} + resolution: {integrity: sha512-PHZFccchvkhWrwPWHjmVAhbC3vSHCtyZvlZfJJ3ho2bnzl450hXri6/8e6pbkWdH+SkmLXNml0sV8e5HDAfxKw==, tarball: https://registry.npmjs.org/@jsonjoy.com/fs-print/-/fs-print-4.64.0.tgz} engines: {node: '>=10.0'} peerDependencies: tslib: '2' '@jsonjoy.com/fs-snapshot@4.64.0': - resolution: {integrity: sha512-oM7UDeL83q6NBzzsfKAsYKXKVXlykKFqqOLh4xZZKAzzROTlInkPbc6LTDGThEOnPiFiUzA7tYziHG9xavd76Q==} + resolution: {integrity: sha512-oM7UDeL83q6NBzzsfKAsYKXKVXlykKFqqOLh4xZZKAzzROTlInkPbc6LTDGThEOnPiFiUzA7tYziHG9xavd76Q==, tarball: https://registry.npmjs.org/@jsonjoy.com/fs-snapshot/-/fs-snapshot-4.64.0.tgz} engines: {node: '>=10.0'} peerDependencies: tslib: '2' '@jsonjoy.com/json-pack@1.21.0': - resolution: {integrity: sha512-+AKG+R2cfZMShzrF2uQw34v3zbeDYUqnQ+jg7ORic3BGtfw9p/+N6RJbq/kkV8JmYZaINknaEQ2m0/f693ZPpg==} + resolution: {integrity: sha512-+AKG+R2cfZMShzrF2uQw34v3zbeDYUqnQ+jg7ORic3BGtfw9p/+N6RJbq/kkV8JmYZaINknaEQ2m0/f693ZPpg==, tarball: https://registry.npmjs.org/@jsonjoy.com/json-pack/-/json-pack-1.21.0.tgz} engines: {node: '>=10.0'} peerDependencies: tslib: '2' '@jsonjoy.com/json-pack@17.67.0': - resolution: {integrity: sha512-t0ejURcGaZsn1ClbJ/3kFqSOjlryd92eQY465IYrezsXmPcfHPE/av4twRSxf6WE+TkZgLY+71vCZbiIiFKA/w==} + resolution: {integrity: sha512-t0ejURcGaZsn1ClbJ/3kFqSOjlryd92eQY465IYrezsXmPcfHPE/av4twRSxf6WE+TkZgLY+71vCZbiIiFKA/w==, tarball: https://registry.npmjs.org/@jsonjoy.com/json-pack/-/json-pack-17.67.0.tgz} engines: {node: '>=10.0'} peerDependencies: tslib: '2' '@jsonjoy.com/json-pointer@1.0.2': - resolution: {integrity: sha512-Fsn6wM2zlDzY1U+v4Nc8bo3bVqgfNTGcn6dMgs6FjrEnt4ZCe60o6ByKRjOGlI2gow0aE/Q41QOigdTqkyK5fg==} + resolution: {integrity: sha512-Fsn6wM2zlDzY1U+v4Nc8bo3bVqgfNTGcn6dMgs6FjrEnt4ZCe60o6ByKRjOGlI2gow0aE/Q41QOigdTqkyK5fg==, tarball: https://registry.npmjs.org/@jsonjoy.com/json-pointer/-/json-pointer-1.0.2.tgz} engines: {node: '>=10.0'} peerDependencies: tslib: '2' '@jsonjoy.com/json-pointer@17.67.0': - resolution: {integrity: sha512-+iqOFInH+QZGmSuaybBUNdh7yvNrXvqR+h3wjXm0N/3JK1EyyFAeGJvqnmQL61d1ARLlk/wJdFKSL+LHJ1eaUA==} + resolution: {integrity: sha512-+iqOFInH+QZGmSuaybBUNdh7yvNrXvqR+h3wjXm0N/3JK1EyyFAeGJvqnmQL61d1ARLlk/wJdFKSL+LHJ1eaUA==, tarball: https://registry.npmjs.org/@jsonjoy.com/json-pointer/-/json-pointer-17.67.0.tgz} engines: {node: '>=10.0'} peerDependencies: tslib: '2' '@jsonjoy.com/util@1.9.0': - resolution: {integrity: sha512-pLuQo+VPRnN8hfPqUTLTHk126wuYdXVxE6aDmjSeV4NCAgyxWbiOIeNJVtID3h1Vzpoi9m4jXezf73I6LgabgQ==} + resolution: {integrity: sha512-pLuQo+VPRnN8hfPqUTLTHk126wuYdXVxE6aDmjSeV4NCAgyxWbiOIeNJVtID3h1Vzpoi9m4jXezf73I6LgabgQ==, tarball: https://registry.npmjs.org/@jsonjoy.com/util/-/util-1.9.0.tgz} engines: {node: '>=10.0'} peerDependencies: tslib: '2' '@jsonjoy.com/util@17.67.0': - resolution: {integrity: sha512-6+8xBaz1rLSohlGh68D1pdw3AwDi9xydm8QNlAFkvnavCJYSze+pxoW2VKP8p308jtlMRLs5NTHfPlZLd4w7ew==} + resolution: {integrity: sha512-6+8xBaz1rLSohlGh68D1pdw3AwDi9xydm8QNlAFkvnavCJYSze+pxoW2VKP8p308jtlMRLs5NTHfPlZLd4w7ew==, tarball: https://registry.npmjs.org/@jsonjoy.com/util/-/util-17.67.0.tgz} engines: {node: '>=10.0'} peerDependencies: tslib: '2' '@kurkle/color@0.3.4': - resolution: {integrity: sha512-M5UknZPHRu3DEDWoipU6sE8PdkZ6Z/S+v4dD+Ke8IaNlpdSQah50lz1KtcFBa2vsdOnwbbnxJwVM4wty6udA5w==} + resolution: {integrity: sha512-M5UknZPHRu3DEDWoipU6sE8PdkZ6Z/S+v4dD+Ke8IaNlpdSQah50lz1KtcFBa2vsdOnwbbnxJwVM4wty6udA5w==, tarball: https://registry.npmjs.org/@kurkle/color/-/color-0.3.4.tgz} '@leichtgewicht/ip-codec@2.0.5': - resolution: {integrity: sha512-Vo+PSpZG2/fmgmiNzYK9qWRh8h/CHrwD0mo1h1DzL4yzHNSfWYujGTYsWGreD000gcgmZ7K4Ys6Tx9TxtsKdDw==} + resolution: {integrity: sha512-Vo+PSpZG2/fmgmiNzYK9qWRh8h/CHrwD0mo1h1DzL4yzHNSfWYujGTYsWGreD000gcgmZ7K4Ys6Tx9TxtsKdDw==, tarball: https://registry.npmjs.org/@leichtgewicht/ip-codec/-/ip-codec-2.0.5.tgz} '@listr2/prompt-adapter-inquirer@4.2.4': - resolution: {integrity: sha512-/KRI2DMD7JGSYaREF0Ygl7AefJ/2ase4Gc5cBiKqT5l4tFjsSJfhFGcc5nSkgl0Sp9LkCQNzl/cqbVJYP2L3dw==} + resolution: {integrity: sha512-/KRI2DMD7JGSYaREF0Ygl7AefJ/2ase4Gc5cBiKqT5l4tFjsSJfhFGcc5nSkgl0Sp9LkCQNzl/cqbVJYP2L3dw==, tarball: https://registry.npmjs.org/@listr2/prompt-adapter-inquirer/-/prompt-adapter-inquirer-4.2.4.tgz} engines: {node: '>=22.13.0'} peerDependencies: '@inquirer/prompts': '>= 3 < 9' listr2: 10.2.1 '@lit-labs/ssr-dom-shim@1.6.0': - resolution: {integrity: sha512-VHb0ALPMTlgKjM6yIxxoQNnpKyUKLD04VzeQdsiXkMqkvYlAHxq9glGLmgbb889/1GsohSOAjvQYoiBppXFqrQ==} + resolution: {integrity: sha512-VHb0ALPMTlgKjM6yIxxoQNnpKyUKLD04VzeQdsiXkMqkvYlAHxq9glGLmgbb889/1GsohSOAjvQYoiBppXFqrQ==, tarball: https://registry.npmjs.org/@lit-labs/ssr-dom-shim/-/ssr-dom-shim-1.6.0.tgz} '@lit/reactive-element@1.6.3': - resolution: {integrity: sha512-QuTgnG52Poic7uM1AN5yJ09QMe0O28e10XzSvWDz02TJiiKee4stsiownEIadWm8nYzyDAyT+gKzUoZmiWQtsQ==} + resolution: {integrity: sha512-QuTgnG52Poic7uM1AN5yJ09QMe0O28e10XzSvWDz02TJiiKee4stsiownEIadWm8nYzyDAyT+gKzUoZmiWQtsQ==, tarball: https://registry.npmjs.org/@lit/reactive-element/-/reactive-element-1.6.3.tgz} '@lmdb/lmdb-darwin-arm64@3.5.6': - resolution: {integrity: sha512-mY5FG4TjPAkY4P0w+OhHaUka5mDh2TX2WKYIwuKzJ1zeW3VvRgxdam/lGJTquI+bthTx5CSHDW+BAQCnNAzkEA==} + resolution: {integrity: sha512-mY5FG4TjPAkY4P0w+OhHaUka5mDh2TX2WKYIwuKzJ1zeW3VvRgxdam/lGJTquI+bthTx5CSHDW+BAQCnNAzkEA==, tarball: https://registry.npmjs.org/@lmdb/lmdb-darwin-arm64/-/lmdb-darwin-arm64-3.5.6.tgz} cpu: [arm64] os: [darwin] '@lmdb/lmdb-darwin-x64@3.5.6': - resolution: {integrity: sha512-foa+pwitysO8k+xhs7psBFfTKnVgR69NlZRRTHaFVDqphh7AdGpLeyRzKw/ofatr/sN6TiHRRW6mmop0ZrrppQ==} + resolution: {integrity: sha512-foa+pwitysO8k+xhs7psBFfTKnVgR69NlZRRTHaFVDqphh7AdGpLeyRzKw/ofatr/sN6TiHRRW6mmop0ZrrppQ==, tarball: https://registry.npmjs.org/@lmdb/lmdb-darwin-x64/-/lmdb-darwin-x64-3.5.6.tgz} cpu: [x64] os: [darwin] '@lmdb/lmdb-linux-arm64@3.5.6': - resolution: {integrity: sha512-HmiyFFdJa38s1heCMSooSPaBSFTHJ3C+ERPp28xAPlDX1YiALJVOgbry065nXd8Y7KISWjnw05zpG1RX8IfftA==} + resolution: {integrity: sha512-HmiyFFdJa38s1heCMSooSPaBSFTHJ3C+ERPp28xAPlDX1YiALJVOgbry065nXd8Y7KISWjnw05zpG1RX8IfftA==, tarball: https://registry.npmjs.org/@lmdb/lmdb-linux-arm64/-/lmdb-linux-arm64-3.5.6.tgz} cpu: [arm64] os: [linux] '@lmdb/lmdb-linux-arm@3.5.6': - resolution: {integrity: sha512-QR4YRyR5h5Z8eGXrNQjiyo2NNDfqi3tCc9dQG5Is1blCt+qWw1ZoBWhlWAr5d+jshkifMIJjVHzHGKbkKzF8Tw==} + resolution: {integrity: sha512-QR4YRyR5h5Z8eGXrNQjiyo2NNDfqi3tCc9dQG5Is1blCt+qWw1ZoBWhlWAr5d+jshkifMIJjVHzHGKbkKzF8Tw==, tarball: https://registry.npmjs.org/@lmdb/lmdb-linux-arm/-/lmdb-linux-arm-3.5.6.tgz} cpu: [arm] os: [linux] '@lmdb/lmdb-linux-x64@3.5.6': - resolution: {integrity: sha512-ADzCuCF2cTNiX9kDScqcz1fjnAkxPpQNneV3KFTdV3wWtVlI2sTGzySoMTgDpinkMMFj1NTJlxA6XR8fwc4hlA==} + resolution: {integrity: sha512-ADzCuCF2cTNiX9kDScqcz1fjnAkxPpQNneV3KFTdV3wWtVlI2sTGzySoMTgDpinkMMFj1NTJlxA6XR8fwc4hlA==, tarball: https://registry.npmjs.org/@lmdb/lmdb-linux-x64/-/lmdb-linux-x64-3.5.6.tgz} cpu: [x64] os: [linux] '@lmdb/lmdb-win32-arm64@3.5.6': - resolution: {integrity: sha512-J7A9aEQsQiv0TYtBGL7NDIPp2lOS8nnl+zm4sWZm1xlsTTaQ4PgD096Adzdrk27rw3UxCkDXdCUa4ax41oztBQ==} + resolution: {integrity: sha512-J7A9aEQsQiv0TYtBGL7NDIPp2lOS8nnl+zm4sWZm1xlsTTaQ4PgD096Adzdrk27rw3UxCkDXdCUa4ax41oztBQ==, tarball: https://registry.npmjs.org/@lmdb/lmdb-win32-arm64/-/lmdb-win32-arm64-3.5.6.tgz} cpu: [arm64] os: [win32] '@lmdb/lmdb-win32-x64@3.5.6': - resolution: {integrity: sha512-1g7G0knRX2iV/voDu54yxrGqw5Dk0w2oIYb7dgJq8IkOi+m7wbD8Q3QpPFjh0C01G58S88dqGn03len6UPCXsg==} + resolution: {integrity: sha512-1g7G0knRX2iV/voDu54yxrGqw5Dk0w2oIYb7dgJq8IkOi+m7wbD8Q3QpPFjh0C01G58S88dqGn03len6UPCXsg==, tarball: https://registry.npmjs.org/@lmdb/lmdb-win32-x64/-/lmdb-win32-x64-3.5.6.tgz} cpu: [x64] os: [win32] '@materia-ui/ngx-monaco-editor@6.0.0': - resolution: {integrity: sha512-gTqNQjOGznZxOC0NlmKdKSGCJuTts8YmK4dsTQAGc5IgIV7cZdQWiW6AL742h0ruED6q0cAunEYjXT6jzHBoIQ==} + resolution: {integrity: sha512-gTqNQjOGznZxOC0NlmKdKSGCJuTts8YmK4dsTQAGc5IgIV7cZdQWiW6AL742h0ruED6q0cAunEYjXT6jzHBoIQ==, tarball: https://registry.npmjs.org/@materia-ui/ngx-monaco-editor/-/ngx-monaco-editor-6.0.0.tgz} peerDependencies: '@angular/core': '>=13.0.0' rxjs: '>=6.0.0' '@material/animation@14.0.0': - resolution: {integrity: sha512-VlYSfUaIj/BBVtRZI8Gv0VvzikFf+XgK0Zdgsok5c1v5DDnNz5tpB8mnGrveWz0rHbp1X4+CWLKrTwNmjrw3Xw==} + resolution: {integrity: sha512-VlYSfUaIj/BBVtRZI8Gv0VvzikFf+XgK0Zdgsok5c1v5DDnNz5tpB8mnGrveWz0rHbp1X4+CWLKrTwNmjrw3Xw==, tarball: https://registry.npmjs.org/@material/animation/-/animation-14.0.0.tgz} '@material/animation@14.0.0-canary.53b3cad2f.0': - resolution: {integrity: sha512-GBuR4VmcTQW1D0lPXEosf5Giho72LLbyGIydWGtaEUtLJoive/D9kFkwTN4Fsyt9Kkl7hbhs35vrNe6QkAH4/Q==} + resolution: {integrity: sha512-GBuR4VmcTQW1D0lPXEosf5Giho72LLbyGIydWGtaEUtLJoive/D9kFkwTN4Fsyt9Kkl7hbhs35vrNe6QkAH4/Q==, tarball: https://registry.npmjs.org/@material/animation/-/animation-14.0.0-canary.53b3cad2f.0.tgz} '@material/base@14.0.0': - resolution: {integrity: sha512-Ou7vS7n1H4Y10MUZyYAbt6H0t67c6urxoCgeVT7M38aQlaNUwFMODp7KT/myjYz2YULfhu3PtfSV3Sltgac9mA==} + resolution: {integrity: sha512-Ou7vS7n1H4Y10MUZyYAbt6H0t67c6urxoCgeVT7M38aQlaNUwFMODp7KT/myjYz2YULfhu3PtfSV3Sltgac9mA==, tarball: https://registry.npmjs.org/@material/base/-/base-14.0.0.tgz} '@material/base@14.0.0-canary.53b3cad2f.0': - resolution: {integrity: sha512-UJKbXwZtkrA3sfQDmj8Zbw1Q3Tqtl6KdfVFws95Yf7TCUgTFzbZI/FSx1w7dVugQPOEnIBuZnzqZam/MtHkx4w==} + resolution: {integrity: sha512-UJKbXwZtkrA3sfQDmj8Zbw1Q3Tqtl6KdfVFws95Yf7TCUgTFzbZI/FSx1w7dVugQPOEnIBuZnzqZam/MtHkx4w==, tarball: https://registry.npmjs.org/@material/base/-/base-14.0.0-canary.53b3cad2f.0.tgz} '@material/button@14.0.0-canary.53b3cad2f.0': - resolution: {integrity: sha512-IPBAByKpQjrWNVmAWx5VCTCLnOw4ymbLsbHmBkLiDgcLPs1EtwYnKKIwQ+/t3bV02OShUdMiyboL8V/C0gMS1A==} + resolution: {integrity: sha512-IPBAByKpQjrWNVmAWx5VCTCLnOw4ymbLsbHmBkLiDgcLPs1EtwYnKKIwQ+/t3bV02OShUdMiyboL8V/C0gMS1A==, tarball: https://registry.npmjs.org/@material/button/-/button-14.0.0-canary.53b3cad2f.0.tgz} '@material/circular-progress@14.0.0-canary.53b3cad2f.0': - resolution: {integrity: sha512-4A+HMgp66b45Fvbcbh9qb1j0vRFjKESbE2fHFkSMMNDPqFiKcvq4tJFBxKG2szYzpAnXdWLYaB+DeQ2+wSu9hg==} + resolution: {integrity: sha512-4A+HMgp66b45Fvbcbh9qb1j0vRFjKESbE2fHFkSMMNDPqFiKcvq4tJFBxKG2szYzpAnXdWLYaB+DeQ2+wSu9hg==, tarball: https://registry.npmjs.org/@material/circular-progress/-/circular-progress-14.0.0-canary.53b3cad2f.0.tgz} '@material/density@14.0.0-canary.53b3cad2f.0': - resolution: {integrity: sha512-Eh/vZ3vVyqtpylg5Ci33qlgtToS4H1/ppd450Ib3tcdISIoodgijYY0w4XsRvrnZgbI/h/1STFdLxdzS0UNuFw==} + resolution: {integrity: sha512-Eh/vZ3vVyqtpylg5Ci33qlgtToS4H1/ppd450Ib3tcdISIoodgijYY0w4XsRvrnZgbI/h/1STFdLxdzS0UNuFw==, tarball: https://registry.npmjs.org/@material/density/-/density-14.0.0-canary.53b3cad2f.0.tgz} '@material/dialog@14.0.0-canary.53b3cad2f.0': - resolution: {integrity: sha512-yiG2nlVKTW0Ro3CF8Z/MVpTwSyG/8Kio3AaTUbeQdbjt5r692s4x5Yhd8m1IjEQKUeulY4CndvIbCUwZ8/G2PA==} + resolution: {integrity: sha512-yiG2nlVKTW0Ro3CF8Z/MVpTwSyG/8Kio3AaTUbeQdbjt5r692s4x5Yhd8m1IjEQKUeulY4CndvIbCUwZ8/G2PA==, tarball: https://registry.npmjs.org/@material/dialog/-/dialog-14.0.0-canary.53b3cad2f.0.tgz} '@material/dom@14.0.0': - resolution: {integrity: sha512-8t88XyacclTj8qsIw9q0vEj4PI2KVncLoIsIMzwuMx49P2FZg6TsLjor262MI3Qs00UWAifuLMrhnOnfyrbe7Q==} + resolution: {integrity: sha512-8t88XyacclTj8qsIw9q0vEj4PI2KVncLoIsIMzwuMx49P2FZg6TsLjor262MI3Qs00UWAifuLMrhnOnfyrbe7Q==, tarball: https://registry.npmjs.org/@material/dom/-/dom-14.0.0.tgz} '@material/dom@14.0.0-canary.53b3cad2f.0': - resolution: {integrity: sha512-aR+rfncF6oi2ivdOlKSJI4UXwNzWV5rXM88MLDoSJF1D7lXxhAKhge+tMUBodWGV/q0+FnXLuVAa0WYTrKjo+A==} + resolution: {integrity: sha512-aR+rfncF6oi2ivdOlKSJI4UXwNzWV5rXM88MLDoSJF1D7lXxhAKhge+tMUBodWGV/q0+FnXLuVAa0WYTrKjo+A==, tarball: https://registry.npmjs.org/@material/dom/-/dom-14.0.0-canary.53b3cad2f.0.tgz} '@material/elevation@14.0.0-canary.53b3cad2f.0': - resolution: {integrity: sha512-3h+EkR588RMZ5TSNQ4UeXD1FOBnL3ABQix0DQIGwtNJCqSMoPndT/oJEFvwQbTkZNDbFIKN9p1Q7/KuFPVY8Pw==} + resolution: {integrity: sha512-3h+EkR588RMZ5TSNQ4UeXD1FOBnL3ABQix0DQIGwtNJCqSMoPndT/oJEFvwQbTkZNDbFIKN9p1Q7/KuFPVY8Pw==, tarball: https://registry.npmjs.org/@material/elevation/-/elevation-14.0.0-canary.53b3cad2f.0.tgz} '@material/feature-targeting@14.0.0': - resolution: {integrity: sha512-a5WGgHEq5lJeeNL5yevtgoZjBjXWy6+klfVWQEh8oyix/rMJygGgO7gEc52uv8fB8uAIoYEB3iBMOv8jRq8FeA==} + resolution: {integrity: sha512-a5WGgHEq5lJeeNL5yevtgoZjBjXWy6+klfVWQEh8oyix/rMJygGgO7gEc52uv8fB8uAIoYEB3iBMOv8jRq8FeA==, tarball: https://registry.npmjs.org/@material/feature-targeting/-/feature-targeting-14.0.0.tgz} '@material/feature-targeting@14.0.0-canary.53b3cad2f.0': - resolution: {integrity: sha512-fn7Af3PRyARtNeYqtjxXmE3Y/dCpnpQVWWys57MqiGR/nvc6qpgOfJ6rOdcu/MrOysOE/oebTUDmDnTmwpe9Hw==} + resolution: {integrity: sha512-fn7Af3PRyARtNeYqtjxXmE3Y/dCpnpQVWWys57MqiGR/nvc6qpgOfJ6rOdcu/MrOysOE/oebTUDmDnTmwpe9Hw==, tarball: https://registry.npmjs.org/@material/feature-targeting/-/feature-targeting-14.0.0-canary.53b3cad2f.0.tgz} '@material/focus-ring@14.0.0-canary.53b3cad2f.0': - resolution: {integrity: sha512-exPX5VrjQimipBwgcFDGRiEE783sOBgpkFui59A6i6iGvS2UrLHlYY2E65fyyyQnD1f/rv4Po1OOnCesE1kulg==} + resolution: {integrity: sha512-exPX5VrjQimipBwgcFDGRiEE783sOBgpkFui59A6i6iGvS2UrLHlYY2E65fyyyQnD1f/rv4Po1OOnCesE1kulg==, tarball: https://registry.npmjs.org/@material/focus-ring/-/focus-ring-14.0.0-canary.53b3cad2f.0.tgz} '@material/form-field@14.0.0-canary.53b3cad2f.0': - resolution: {integrity: sha512-h9jFm9f5WeMHJWGpQsZ9sPrERLGcDQdW8uvbHAHZ/zN35Mqj43s8+alXROiibx+m1oHLvf2Z01pPWtFSXLYzxA==} + resolution: {integrity: sha512-h9jFm9f5WeMHJWGpQsZ9sPrERLGcDQdW8uvbHAHZ/zN35Mqj43s8+alXROiibx+m1oHLvf2Z01pPWtFSXLYzxA==, tarball: https://registry.npmjs.org/@material/form-field/-/form-field-14.0.0-canary.53b3cad2f.0.tgz} '@material/icon-button@14.0.0-canary.53b3cad2f.0': - resolution: {integrity: sha512-BFdj3CP0JXHC/F2bDmpmzWhum4fkzIDgCCavvnpE/KcCbr0AaoSULRde+LtqvbdLIYW20cXhvjinIOlRhSOshA==} + resolution: {integrity: sha512-BFdj3CP0JXHC/F2bDmpmzWhum4fkzIDgCCavvnpE/KcCbr0AaoSULRde+LtqvbdLIYW20cXhvjinIOlRhSOshA==, tarball: https://registry.npmjs.org/@material/icon-button/-/icon-button-14.0.0-canary.53b3cad2f.0.tgz} '@material/list@14.0.0-canary.53b3cad2f.0': - resolution: {integrity: sha512-mkMpltSKAYLBtFnTTCk/mQIDzwxF/VLh1gh59ehOtmRXt7FvTz83RoAa4tqe53hpVrbX4HoLDBu+vILhq/wkjw==} + resolution: {integrity: sha512-mkMpltSKAYLBtFnTTCk/mQIDzwxF/VLh1gh59ehOtmRXt7FvTz83RoAa4tqe53hpVrbX4HoLDBu+vILhq/wkjw==, tarball: https://registry.npmjs.org/@material/list/-/list-14.0.0-canary.53b3cad2f.0.tgz} '@material/menu-surface@14.0.0-canary.53b3cad2f.0': - resolution: {integrity: sha512-IQWb/n15FpLnn+kHp0EqzLE+UoWSPumq3eze2QifiowvGb37bNFR9oSe7CaOzPMrHdkrZ5SBWnDU41wPZN5kOg==} + resolution: {integrity: sha512-IQWb/n15FpLnn+kHp0EqzLE+UoWSPumq3eze2QifiowvGb37bNFR9oSe7CaOzPMrHdkrZ5SBWnDU41wPZN5kOg==, tarball: https://registry.npmjs.org/@material/menu-surface/-/menu-surface-14.0.0-canary.53b3cad2f.0.tgz} '@material/menu@14.0.0-canary.53b3cad2f.0': - resolution: {integrity: sha512-MmYKVrMIqOtP3TN4vdrrnQrS8P81+tMaA6bKiT9V79R1U6+mKsBYTzaLtLbzyem5vF8O0q7bSwyPwhWPtJr75Q==} + resolution: {integrity: sha512-MmYKVrMIqOtP3TN4vdrrnQrS8P81+tMaA6bKiT9V79R1U6+mKsBYTzaLtLbzyem5vF8O0q7bSwyPwhWPtJr75Q==, tarball: https://registry.npmjs.org/@material/menu/-/menu-14.0.0-canary.53b3cad2f.0.tgz} '@material/mwc-base@0.27.0': - resolution: {integrity: sha512-oCWWtjbyQ52AaUbzINLGBKScIPyqhps2Y7c8t6Gu6fcFeDxhKXMV1Cqvtj/OMhtAt53XjHfD2XruWwYv3cYYUA==} + resolution: {integrity: sha512-oCWWtjbyQ52AaUbzINLGBKScIPyqhps2Y7c8t6Gu6fcFeDxhKXMV1Cqvtj/OMhtAt53XjHfD2XruWwYv3cYYUA==, tarball: https://registry.npmjs.org/@material/mwc-base/-/mwc-base-0.27.0.tgz} deprecated: MWC beta is longer supported. Please upgrade to @material/web '@material/mwc-button@0.27.0': - resolution: {integrity: sha512-t5m2zfE93RNKHMjdsU67X6csFzuSG08VJKKvXVQ+BriGE3xBgzY5nZdmZXomFpaWjDENPAlyS4ppCFm6o+DILw==} + resolution: {integrity: sha512-t5m2zfE93RNKHMjdsU67X6csFzuSG08VJKKvXVQ+BriGE3xBgzY5nZdmZXomFpaWjDENPAlyS4ppCFm6o+DILw==, tarball: https://registry.npmjs.org/@material/mwc-button/-/mwc-button-0.27.0.tgz} deprecated: MWC beta is longer supported. Please upgrade to @material/web '@material/mwc-checkbox@0.27.0': - resolution: {integrity: sha512-EY0iYZLwo8qaqMwR5da4fdn0xI0BZNAvKTcwoubYWpDDHlGxDcqwvjp/40ChGo3Q/zv8/4/A0Qp7cwapI82EkA==} + resolution: {integrity: sha512-EY0iYZLwo8qaqMwR5da4fdn0xI0BZNAvKTcwoubYWpDDHlGxDcqwvjp/40ChGo3Q/zv8/4/A0Qp7cwapI82EkA==, tarball: https://registry.npmjs.org/@material/mwc-checkbox/-/mwc-checkbox-0.27.0.tgz} deprecated: MWC beta is longer supported. Please upgrade to @material/web '@material/mwc-circular-progress@0.27.0': - resolution: {integrity: sha512-7DkqMb5pUrHfLus5EZ7IT2HUMoE+uPpjGzu3MnDpIzDlfYo6I8p+ifWtEytI0NtXdY5UmITNuCv0RC77mhJrgQ==} + resolution: {integrity: sha512-7DkqMb5pUrHfLus5EZ7IT2HUMoE+uPpjGzu3MnDpIzDlfYo6I8p+ifWtEytI0NtXdY5UmITNuCv0RC77mhJrgQ==, tarball: https://registry.npmjs.org/@material/mwc-circular-progress/-/mwc-circular-progress-0.27.0.tgz} deprecated: MWC beta is longer supported. Please upgrade to @material/web '@material/mwc-dialog@0.27.0': - resolution: {integrity: sha512-rkOEmCroVs0wBQbj87vH79SvSHHZ61QRCTUYsU2rHGZCvdzlmvHjWdoyKjJER6WwwM3rrT8xthfecmjICI28CA==} + resolution: {integrity: sha512-rkOEmCroVs0wBQbj87vH79SvSHHZ61QRCTUYsU2rHGZCvdzlmvHjWdoyKjJER6WwwM3rrT8xthfecmjICI28CA==, tarball: https://registry.npmjs.org/@material/mwc-dialog/-/mwc-dialog-0.27.0.tgz} deprecated: MWC beta is longer supported. Please upgrade to @material/web '@material/mwc-formfield@0.27.0': - resolution: {integrity: sha512-XGZtC1MTyGQ8b2osnaygGzS3qe2QvlWfXZepcFs9i6MW+b6VimQQ4c/KsKIF7dHmeY6N0o4k9pAZ086EGesXOQ==} + resolution: {integrity: sha512-XGZtC1MTyGQ8b2osnaygGzS3qe2QvlWfXZepcFs9i6MW+b6VimQQ4c/KsKIF7dHmeY6N0o4k9pAZ086EGesXOQ==, tarball: https://registry.npmjs.org/@material/mwc-formfield/-/mwc-formfield-0.27.0.tgz} deprecated: MWC beta is longer supported. Please upgrade to @material/web '@material/mwc-icon-button@0.27.0': - resolution: {integrity: sha512-wReiPa1UkLaCSPtpkAs1OGKEBtvqPnz9kzuY+RvN5ZQnpo3Uh7n3plHV4y/stsUBfrWtBCcOgYnCdNRaR/r2nQ==} + resolution: {integrity: sha512-wReiPa1UkLaCSPtpkAs1OGKEBtvqPnz9kzuY+RvN5ZQnpo3Uh7n3plHV4y/stsUBfrWtBCcOgYnCdNRaR/r2nQ==, tarball: https://registry.npmjs.org/@material/mwc-icon-button/-/mwc-icon-button-0.27.0.tgz} deprecated: MWC beta is longer supported. Please upgrade to @material/web '@material/mwc-icon@0.27.0': - resolution: {integrity: sha512-Sul44I37M9Ewynn0A9DjkEBrmll2VtNbth6Pxj7I1A/EAwEfaCrPvryyGqfIu1T2hTsRcaojzQx6QjF+B5QW9A==} + resolution: {integrity: sha512-Sul44I37M9Ewynn0A9DjkEBrmll2VtNbth6Pxj7I1A/EAwEfaCrPvryyGqfIu1T2hTsRcaojzQx6QjF+B5QW9A==, tarball: https://registry.npmjs.org/@material/mwc-icon/-/mwc-icon-0.27.0.tgz} deprecated: MWC beta is longer supported. Please upgrade to @material/web '@material/mwc-list@0.27.0': - resolution: {integrity: sha512-oAhNQsBuAOgF3ENOIY8PeWjXsl35HoYaUkl0ixBQk8jJP2HIEf+MdbS5688y/UXxFbSjr0m//LfwR5gauEashg==} + resolution: {integrity: sha512-oAhNQsBuAOgF3ENOIY8PeWjXsl35HoYaUkl0ixBQk8jJP2HIEf+MdbS5688y/UXxFbSjr0m//LfwR5gauEashg==, tarball: https://registry.npmjs.org/@material/mwc-list/-/mwc-list-0.27.0.tgz} deprecated: MWC beta is longer supported. Please upgrade to @material/web '@material/mwc-menu@0.27.0': - resolution: {integrity: sha512-K+L/t267ZGrlhjK/iSKUVZQKRMkWELArKVglfS5of93ALP4in0RGnj1sOG2u3IFI2F/mEZxRi+wr7HgNxpe0wA==} + resolution: {integrity: sha512-K+L/t267ZGrlhjK/iSKUVZQKRMkWELArKVglfS5of93ALP4in0RGnj1sOG2u3IFI2F/mEZxRi+wr7HgNxpe0wA==, tarball: https://registry.npmjs.org/@material/mwc-menu/-/mwc-menu-0.27.0.tgz} deprecated: MWC beta is longer supported. Please upgrade to @material/web '@material/mwc-radio@0.27.0': - resolution: {integrity: sha512-+rSO9a373BgyMgQOM0Z8vVkuieobBylPJ8qpltytM+yGPj8+n+MtwRZyg+ry3WwEjYYDMP6GxZPHwLgWs6lMpQ==} + resolution: {integrity: sha512-+rSO9a373BgyMgQOM0Z8vVkuieobBylPJ8qpltytM+yGPj8+n+MtwRZyg+ry3WwEjYYDMP6GxZPHwLgWs6lMpQ==, tarball: https://registry.npmjs.org/@material/mwc-radio/-/mwc-radio-0.27.0.tgz} deprecated: MWC beta is longer supported. Please upgrade to @material/web '@material/mwc-ripple@0.27.0': - resolution: {integrity: sha512-by0O8d8g3Rd96/sUB8hxy6MrDx1QTstqOsA64vqypWd526hMTBGRik08jTNap5sVIyrN9Vq17jb4NJLWQLnNHQ==} + resolution: {integrity: sha512-by0O8d8g3Rd96/sUB8hxy6MrDx1QTstqOsA64vqypWd526hMTBGRik08jTNap5sVIyrN9Vq17jb4NJLWQLnNHQ==, tarball: https://registry.npmjs.org/@material/mwc-ripple/-/mwc-ripple-0.27.0.tgz} deprecated: MWC beta is longer supported. Please upgrade to @material/web '@material/progress-indicator@14.0.0-canary.53b3cad2f.0': - resolution: {integrity: sha512-vW0oZK70QOpAarip95ueCQ/I3kBClcWjxsc0F0QjkqT76DOVXpjnZ4XoRRyq9eMpwLqlKLTecrsSNpmqwwF1Dg==} + resolution: {integrity: sha512-vW0oZK70QOpAarip95ueCQ/I3kBClcWjxsc0F0QjkqT76DOVXpjnZ4XoRRyq9eMpwLqlKLTecrsSNpmqwwF1Dg==, tarball: https://registry.npmjs.org/@material/progress-indicator/-/progress-indicator-14.0.0-canary.53b3cad2f.0.tgz} '@material/radio@14.0.0-canary.53b3cad2f.0': - resolution: {integrity: sha512-V/AgWEOuHFoh9d4Gq1rqBZnKSGtMLQNh23Bwrv0c1FhPqFvUpwt9jR3SVwhJk5gvQQWGy9p3iiGc9QCJ+0+P8Q==} + resolution: {integrity: sha512-V/AgWEOuHFoh9d4Gq1rqBZnKSGtMLQNh23Bwrv0c1FhPqFvUpwt9jR3SVwhJk5gvQQWGy9p3iiGc9QCJ+0+P8Q==, tarball: https://registry.npmjs.org/@material/radio/-/radio-14.0.0-canary.53b3cad2f.0.tgz} '@material/ripple@14.0.0': - resolution: {integrity: sha512-9XoGBFd5JhFgELgW7pqtiLy+CnCIcV2s9cQ2BWbOQeA8faX9UZIDUx/g76nHLZ7UzKFtsULJxZTwORmsEt2zvw==} + resolution: {integrity: sha512-9XoGBFd5JhFgELgW7pqtiLy+CnCIcV2s9cQ2BWbOQeA8faX9UZIDUx/g76nHLZ7UzKFtsULJxZTwORmsEt2zvw==, tarball: https://registry.npmjs.org/@material/ripple/-/ripple-14.0.0.tgz} '@material/ripple@14.0.0-canary.53b3cad2f.0': - resolution: {integrity: sha512-6g2G62vd8DsMuIUSXlRrzb98qkZ4o8ZREknNwNP2zaLQEOkJ//4j9HaqDt98/3LIjUTY9UIVFTQENiMmlwKHYQ==} + resolution: {integrity: sha512-6g2G62vd8DsMuIUSXlRrzb98qkZ4o8ZREknNwNP2zaLQEOkJ//4j9HaqDt98/3LIjUTY9UIVFTQENiMmlwKHYQ==, tarball: https://registry.npmjs.org/@material/ripple/-/ripple-14.0.0-canary.53b3cad2f.0.tgz} '@material/rtl@14.0.0': - resolution: {integrity: sha512-xl6OZYyRjuiW2hmbjV2omMV8sQtfmKAjeWnD1RMiAPLCTyOW9Lh/PYYnXjxUrNa0cRwIIbOn5J7OYXokja8puA==} + resolution: {integrity: sha512-xl6OZYyRjuiW2hmbjV2omMV8sQtfmKAjeWnD1RMiAPLCTyOW9Lh/PYYnXjxUrNa0cRwIIbOn5J7OYXokja8puA==, tarball: https://registry.npmjs.org/@material/rtl/-/rtl-14.0.0.tgz} '@material/rtl@14.0.0-canary.53b3cad2f.0': - resolution: {integrity: sha512-f08LT0HSa0WYU+4Jz/tbm1TQ9Fcf2k+H6dPPYv0J1sZmX6hMgCEmNiUdUFLQFvszoXx2XrRi1/hIFjbz2e69Yg==} + resolution: {integrity: sha512-f08LT0HSa0WYU+4Jz/tbm1TQ9Fcf2k+H6dPPYv0J1sZmX6hMgCEmNiUdUFLQFvszoXx2XrRi1/hIFjbz2e69Yg==, tarball: https://registry.npmjs.org/@material/rtl/-/rtl-14.0.0-canary.53b3cad2f.0.tgz} '@material/shape@14.0.0-canary.53b3cad2f.0': - resolution: {integrity: sha512-RyjInLCNe+nI/ulKea0ZLHphXQDiDqYazS25SRn18g8Hoa5qGNaY5oOBncDXUYn3jm5oI5kFc9oif//kulkbjg==} + resolution: {integrity: sha512-RyjInLCNe+nI/ulKea0ZLHphXQDiDqYazS25SRn18g8Hoa5qGNaY5oOBncDXUYn3jm5oI5kFc9oif//kulkbjg==, tarball: https://registry.npmjs.org/@material/shape/-/shape-14.0.0-canary.53b3cad2f.0.tgz} '@material/theme@14.0.0': - resolution: {integrity: sha512-6/SENWNIFuXzeHMPHrYwbsXKgkvCtWuzzQ3cUu4UEt3KcQ5YpViazIM6h8ByYKZP8A9d8QpkJ0WGX5btGDcVoA==} + resolution: {integrity: sha512-6/SENWNIFuXzeHMPHrYwbsXKgkvCtWuzzQ3cUu4UEt3KcQ5YpViazIM6h8ByYKZP8A9d8QpkJ0WGX5btGDcVoA==, tarball: https://registry.npmjs.org/@material/theme/-/theme-14.0.0.tgz} '@material/theme@14.0.0-canary.53b3cad2f.0': - resolution: {integrity: sha512-S06XAevDCDWMe+GgsEpITMS07imUidzadNaTbJsqssFajBLr53QWVZsG84BpjXKXoYvyEJvb0hX5U0lq6ip9UQ==} + resolution: {integrity: sha512-S06XAevDCDWMe+GgsEpITMS07imUidzadNaTbJsqssFajBLr53QWVZsG84BpjXKXoYvyEJvb0hX5U0lq6ip9UQ==, tarball: https://registry.npmjs.org/@material/theme/-/theme-14.0.0-canary.53b3cad2f.0.tgz} '@material/tokens@14.0.0-canary.53b3cad2f.0': - resolution: {integrity: sha512-myHFB7vac8zErA3qgkqmV+kpE+i9JEwc/6Yf0MOumDSpylJGw28QikpNC6eAVBK2EmPQTaFn20mqUxyud8dGqw==} + resolution: {integrity: sha512-myHFB7vac8zErA3qgkqmV+kpE+i9JEwc/6Yf0MOumDSpylJGw28QikpNC6eAVBK2EmPQTaFn20mqUxyud8dGqw==, tarball: https://registry.npmjs.org/@material/tokens/-/tokens-14.0.0-canary.53b3cad2f.0.tgz} '@material/touch-target@14.0.0-canary.53b3cad2f.0': - resolution: {integrity: sha512-d83e5vbqoLyL542yOTTp4TLVltddWiqbI/j1w/D9ipE30YKfe2EDN+CNJc32Zufh5IUfK41DsZdrN8fI9cL99A==} + resolution: {integrity: sha512-d83e5vbqoLyL542yOTTp4TLVltddWiqbI/j1w/D9ipE30YKfe2EDN+CNJc32Zufh5IUfK41DsZdrN8fI9cL99A==, tarball: https://registry.npmjs.org/@material/touch-target/-/touch-target-14.0.0-canary.53b3cad2f.0.tgz} '@material/typography@14.0.0-canary.53b3cad2f.0': - resolution: {integrity: sha512-9J0k2fq7uyHsRzRqJDJLGmg3YzRpfRPtFDVeUH/xBcYoqpZE7wYw5Mb7s/l8eP626EtR7HhXhSPjvRTLA6NIJg==} + resolution: {integrity: sha512-9J0k2fq7uyHsRzRqJDJLGmg3YzRpfRPtFDVeUH/xBcYoqpZE7wYw5Mb7s/l8eP626EtR7HhXhSPjvRTLA6NIJg==, tarball: https://registry.npmjs.org/@material/typography/-/typography-14.0.0-canary.53b3cad2f.0.tgz} '@mermaid-js/parser@1.2.0': - resolution: {integrity: sha512-oYPyv8A4As1yH5Bx+04iQEQxXuIQDe0GKCNSRgao6z8AM9jixXIfP0vsppRLvGf+nKIOb9/LdpWA4YuJiVvESA==} + resolution: {integrity: sha512-oYPyv8A4As1yH5Bx+04iQEQxXuIQDe0GKCNSRgao6z8AM9jixXIfP0vsppRLvGf+nKIOb9/LdpWA4YuJiVvESA==, tarball: https://registry.npmjs.org/@mermaid-js/parser/-/parser-1.2.0.tgz} '@microsoft/api-extractor-model@7.33.8': - resolution: {integrity: sha512-aIcoQggPyer3B6Ze3usz0YWC/oBwUHfRH5ETUsr+oT2BRA6SfTJl7IKPcPZkX4UR+PohowzW4uMxsvjrn8vm+w==} + resolution: {integrity: sha512-aIcoQggPyer3B6Ze3usz0YWC/oBwUHfRH5ETUsr+oT2BRA6SfTJl7IKPcPZkX4UR+PohowzW4uMxsvjrn8vm+w==, tarball: https://registry.npmjs.org/@microsoft/api-extractor-model/-/api-extractor-model-7.33.8.tgz} '@microsoft/api-extractor@7.58.9': - resolution: {integrity: sha512-S2UF4yza5GoxCmf7hJQNxJNZN9ltOVuOQv8Dy+Z21aol5ERoBNMdWcQHm4MJMPPItW4H/4rZD906iaf4mUojJA==} + resolution: {integrity: sha512-S2UF4yza5GoxCmf7hJQNxJNZN9ltOVuOQv8Dy+Z21aol5ERoBNMdWcQHm4MJMPPItW4H/4rZD906iaf4mUojJA==, tarball: https://registry.npmjs.org/@microsoft/api-extractor/-/api-extractor-7.58.9.tgz} hasBin: true '@microsoft/tsdoc-config@0.18.1': - resolution: {integrity: sha512-9brPoVdfN9k9g0dcWkFeA7IH9bbcttzDJlXvkf8b2OBzd5MueR1V2wkKBL0abn0otvmkHJC6aapBOTJDDeMCZg==} + resolution: {integrity: sha512-9brPoVdfN9k9g0dcWkFeA7IH9bbcttzDJlXvkf8b2OBzd5MueR1V2wkKBL0abn0otvmkHJC6aapBOTJDDeMCZg==, tarball: https://registry.npmjs.org/@microsoft/tsdoc-config/-/tsdoc-config-0.18.1.tgz} '@microsoft/tsdoc@0.16.0': - resolution: {integrity: sha512-xgAyonlVVS+q7Vc7qLW0UrJU7rSFcETRWsqdXZtjzRU8dF+6CkozTK4V4y1LwOX7j8r/vHphjDeMeGI4tNGeGA==} + resolution: {integrity: sha512-xgAyonlVVS+q7Vc7qLW0UrJU7rSFcETRWsqdXZtjzRU8dF+6CkozTK4V4y1LwOX7j8r/vHphjDeMeGI4tNGeGA==, tarball: https://registry.npmjs.org/@microsoft/tsdoc/-/tsdoc-0.16.0.tgz} '@mixmark-io/domino@2.2.0': - resolution: {integrity: sha512-Y28PR25bHXUg88kCV7nivXrP2Nj2RueZ3/l/jdx6J9f8J4nsEGcgX0Qe6lt7Pa+J79+kPiJU3LguR6O/6zrLOw==} + resolution: {integrity: sha512-Y28PR25bHXUg88kCV7nivXrP2Nj2RueZ3/l/jdx6J9f8J4nsEGcgX0Qe6lt7Pa+J79+kPiJU3LguR6O/6zrLOw==, tarball: https://registry.npmjs.org/@mixmark-io/domino/-/domino-2.2.0.tgz} '@modelcontextprotocol/sdk@1.27.1': - resolution: {integrity: sha512-sr6GbP+4edBwFndLbM60gf07z0FQ79gaExpnsjMGePXqFcSSb7t6iscpjk9DhFhwd+mTEQrzNafGP8/iGGFYaA==} + resolution: {integrity: sha512-sr6GbP+4edBwFndLbM60gf07z0FQ79gaExpnsjMGePXqFcSSb7t6iscpjk9DhFhwd+mTEQrzNafGP8/iGGFYaA==, tarball: https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.27.1.tgz} engines: {node: '>=18'} peerDependencies: '@cfworker/json-schema': ^4.1.1 @@ -4025,7 +4042,7 @@ packages: optional: true '@modelcontextprotocol/sdk@1.29.0': - resolution: {integrity: sha512-zo37mZA9hJWpULgkRpowewez1y6ML5GsXJPY8FI0tBBCd77HEvza4jDqRKOXgHNn867PVGCyTdzqpz0izu5ZjQ==} + resolution: {integrity: sha512-zo37mZA9hJWpULgkRpowewez1y6ML5GsXJPY8FI0tBBCd77HEvza4jDqRKOXgHNn867PVGCyTdzqpz0izu5ZjQ==, tarball: https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.29.0.tgz} engines: {node: '>=18'} peerDependencies: '@cfworker/json-schema': ^4.1.1 @@ -4035,35 +4052,35 @@ packages: optional: true '@modern-js/node-bundle-require@2.68.2': - resolution: {integrity: sha512-MWk/pYx7KOsp+A/rN0as2ji/Ba8x0m129aqZ3Lj6T6CCTWdz0E/IsamPdTmF9Jnb6whQoBKtWSaLTCQlmCoY0Q==} + resolution: {integrity: sha512-MWk/pYx7KOsp+A/rN0as2ji/Ba8x0m129aqZ3Lj6T6CCTWdz0E/IsamPdTmF9Jnb6whQoBKtWSaLTCQlmCoY0Q==, tarball: https://registry.npmjs.org/@modern-js/node-bundle-require/-/node-bundle-require-2.68.2.tgz} '@modern-js/utils@2.68.2': - resolution: {integrity: sha512-revom/i/EhKfI0STNLo/AUbv7gY0JY0Ni2gO6P/Z4cTyZZRgd5j90678YB2DGn+LtmSrEWtUphyDH5Jn1RKjgg==} + resolution: {integrity: sha512-revom/i/EhKfI0STNLo/AUbv7gY0JY0Ni2gO6P/Z4cTyZZRgd5j90678YB2DGn+LtmSrEWtUphyDH5Jn1RKjgg==, tarball: https://registry.npmjs.org/@modern-js/utils/-/utils-2.68.2.tgz} '@module-federation/bridge-react-webpack-plugin@0.18.4': - resolution: {integrity: sha512-tYgso9izSinWzzVlsOUsBjW5lPMsvsVp95Jrw5W4Ajg9Un/yTkjOqEqmsMYpiL7drEN2+gPPVYyQ/hUK4QWz8Q==} + resolution: {integrity: sha512-tYgso9izSinWzzVlsOUsBjW5lPMsvsVp95Jrw5W4Ajg9Un/yTkjOqEqmsMYpiL7drEN2+gPPVYyQ/hUK4QWz8Q==, tarball: https://registry.npmjs.org/@module-federation/bridge-react-webpack-plugin/-/bridge-react-webpack-plugin-0.18.4.tgz} '@module-federation/bridge-react-webpack-plugin@2.7.0': - resolution: {integrity: sha512-+7eYeJnIaofQHha8CK+FxPAXMIigd2xwONHi9rlYpdGqpCBIggRKMqL0b1owyDDNiAPF2lWUbxeNm0oUfF7GbA==} + resolution: {integrity: sha512-+7eYeJnIaofQHha8CK+FxPAXMIigd2xwONHi9rlYpdGqpCBIggRKMqL0b1owyDDNiAPF2lWUbxeNm0oUfF7GbA==, tarball: https://registry.npmjs.org/@module-federation/bridge-react-webpack-plugin/-/bridge-react-webpack-plugin-2.7.0.tgz} '@module-federation/cli@0.18.4': - resolution: {integrity: sha512-31c+2OjtRdsYq7oV+rCoTO9AXizT3D9CNzofZ9EVRGsaS9+H+nJKTkK+pw+IhK0Y8I0HsP+uxgLrazqF0tLbgg==} + resolution: {integrity: sha512-31c+2OjtRdsYq7oV+rCoTO9AXizT3D9CNzofZ9EVRGsaS9+H+nJKTkK+pw+IhK0Y8I0HsP+uxgLrazqF0tLbgg==, tarball: https://registry.npmjs.org/@module-federation/cli/-/cli-0.18.4.tgz} engines: {node: '>=16.0.0'} hasBin: true '@module-federation/cli@2.7.0': - resolution: {integrity: sha512-Nx5PQFmYqiiiIaU8uyzFcm9j8bNGb0SEo1GvbjI4ehfRJ5a92sUjNE++Q3o6zmVDI4wDXIhpGxioYdXi5QF40w==} + resolution: {integrity: sha512-Nx5PQFmYqiiiIaU8uyzFcm9j8bNGb0SEo1GvbjI4ehfRJ5a92sUjNE++Q3o6zmVDI4wDXIhpGxioYdXi5QF40w==, tarball: https://registry.npmjs.org/@module-federation/cli/-/cli-2.7.0.tgz} engines: {node: '>=16.0.0'} hasBin: true '@module-federation/data-prefetch@0.18.4': - resolution: {integrity: sha512-XOHFFO1wrVbjjfP2JRMbht+ILim5Is6Mfb5f2H4I9w0CSaZNRltG0fTnebECB1jgosrd8xaYnrwzXsCI/S53qQ==} + resolution: {integrity: sha512-XOHFFO1wrVbjjfP2JRMbht+ILim5Is6Mfb5f2H4I9w0CSaZNRltG0fTnebECB1jgosrd8xaYnrwzXsCI/S53qQ==, tarball: https://registry.npmjs.org/@module-federation/data-prefetch/-/data-prefetch-0.18.4.tgz} peerDependencies: react: '>=16.9.0' react-dom: '>=16.9.0' '@module-federation/dts-plugin@0.18.4': - resolution: {integrity: sha512-5FlrajLCypQ8+vEsncgEGpDmxUDG+Ub6ogKOE00e2gMxcYlgcCZNUSn5VbEGdCMcHQmIK2xt3WGQT30/7j2KiQ==} + resolution: {integrity: sha512-5FlrajLCypQ8+vEsncgEGpDmxUDG+Ub6ogKOE00e2gMxcYlgcCZNUSn5VbEGdCMcHQmIK2xt3WGQT30/7j2KiQ==, tarball: https://registry.npmjs.org/@module-federation/dts-plugin/-/dts-plugin-0.18.4.tgz} peerDependencies: typescript: ^4.9.0 || ^5.0.0 vue-tsc: '>=1.0.24' @@ -4072,7 +4089,7 @@ packages: optional: true '@module-federation/dts-plugin@2.7.0': - resolution: {integrity: sha512-mVKeGUf/7iqRMAFfikhsr2zXtA70WuzJdS1bPqqoeLQNRGXBLDjedcDG26RpIlsvuZcmIOqgN5Z6qw7Bh/HZUg==} + resolution: {integrity: sha512-mVKeGUf/7iqRMAFfikhsr2zXtA70WuzJdS1bPqqoeLQNRGXBLDjedcDG26RpIlsvuZcmIOqgN5Z6qw7Bh/HZUg==, tarball: https://registry.npmjs.org/@module-federation/dts-plugin/-/dts-plugin-2.7.0.tgz} peerDependencies: typescript: ^4.9.0 || ^5.0.0 || ^6.0.0 vue-tsc: '>=1.0.24' @@ -4081,7 +4098,7 @@ packages: optional: true '@module-federation/enhanced@0.18.4': - resolution: {integrity: sha512-KiBw7e+aIBFoO2cmN5hJlKrYv3nUuXsB8yOSVnV9JBAkYNyRZQ9xoSbRCDt8rDRz/ydgEURUIwnGyL2ZU5jZYw==} + resolution: {integrity: sha512-KiBw7e+aIBFoO2cmN5hJlKrYv3nUuXsB8yOSVnV9JBAkYNyRZQ9xoSbRCDt8rDRz/ydgEURUIwnGyL2ZU5jZYw==, tarball: https://registry.npmjs.org/@module-federation/enhanced/-/enhanced-0.18.4.tgz} hasBin: true peerDependencies: typescript: ^4.9.0 || ^5.0.0 @@ -4096,7 +4113,7 @@ packages: optional: true '@module-federation/enhanced@2.7.0': - resolution: {integrity: sha512-1ZaiFIsFdH68MLoU7jrYxwhDt4WbDqmuTcnVsRqp9QZhZsex9h2zkeNpZdctL2Q1BtGsuNJ4ngJCOmO84O+6CQ==} + resolution: {integrity: sha512-1ZaiFIsFdH68MLoU7jrYxwhDt4WbDqmuTcnVsRqp9QZhZsex9h2zkeNpZdctL2Q1BtGsuNJ4ngJCOmO84O+6CQ==, tarball: https://registry.npmjs.org/@module-federation/enhanced/-/enhanced-2.7.0.tgz} hasBin: true peerDependencies: typescript: ^4.9.0 || ^5.0.0 || ^6.0.0 @@ -4111,38 +4128,38 @@ packages: optional: true '@module-federation/error-codes@0.18.4': - resolution: {integrity: sha512-cpLsqL8du9CfTTCKvXbRg93ALF+lklqHnuPryhbwVEQg2eYo6CMoMQ6Eb7kJhLigUABIDujbHD01SvBbASGkeQ==} + resolution: {integrity: sha512-cpLsqL8du9CfTTCKvXbRg93ALF+lklqHnuPryhbwVEQg2eYo6CMoMQ6Eb7kJhLigUABIDujbHD01SvBbASGkeQ==, tarball: https://registry.npmjs.org/@module-federation/error-codes/-/error-codes-0.18.4.tgz} '@module-federation/error-codes@0.22.0': - resolution: {integrity: sha512-xF9SjnEy7vTdx+xekjPCV5cIHOGCkdn3pIxo9vU7gEZMIw0SvAEdsy6Uh17xaCpm8V0FWvR0SZoK9Ik6jGOaug==} + resolution: {integrity: sha512-xF9SjnEy7vTdx+xekjPCV5cIHOGCkdn3pIxo9vU7gEZMIw0SvAEdsy6Uh17xaCpm8V0FWvR0SZoK9Ik6jGOaug==, tarball: https://registry.npmjs.org/@module-federation/error-codes/-/error-codes-0.22.0.tgz} '@module-federation/error-codes@2.7.0': - resolution: {integrity: sha512-syToF3H77IbBhJ7auGMCIb5ZDmJ5tvaqSeEvncJrOCq7JBT96F4UDlQDyNh6kCVznvYqqHsPeEMrfI9b5/Omlg==} + resolution: {integrity: sha512-syToF3H77IbBhJ7auGMCIb5ZDmJ5tvaqSeEvncJrOCq7JBT96F4UDlQDyNh6kCVznvYqqHsPeEMrfI9b5/Omlg==, tarball: https://registry.npmjs.org/@module-federation/error-codes/-/error-codes-2.7.0.tgz} '@module-federation/inject-external-runtime-core-plugin@0.18.4': - resolution: {integrity: sha512-x+IakEXu+ammna2SMKkb1NRDXKxhKckOJIYanNHh1FtG2bvhu8xJplShvStmfO+BUv1n0KODSq89qGVYxFMbGQ==} + resolution: {integrity: sha512-x+IakEXu+ammna2SMKkb1NRDXKxhKckOJIYanNHh1FtG2bvhu8xJplShvStmfO+BUv1n0KODSq89qGVYxFMbGQ==, tarball: https://registry.npmjs.org/@module-federation/inject-external-runtime-core-plugin/-/inject-external-runtime-core-plugin-0.18.4.tgz} peerDependencies: '@module-federation/runtime-tools': 0.18.4 '@module-federation/inject-external-runtime-core-plugin@2.7.0': - resolution: {integrity: sha512-8xHVUWsnlYd1vQPUjVEO+OPBhBjbdur+jp33QwqwkJkSUW/WOgnybCevVyfiA05aaiSyPo1PluQPT6uhImVb7A==} + resolution: {integrity: sha512-8xHVUWsnlYd1vQPUjVEO+OPBhBjbdur+jp33QwqwkJkSUW/WOgnybCevVyfiA05aaiSyPo1PluQPT6uhImVb7A==, tarball: https://registry.npmjs.org/@module-federation/inject-external-runtime-core-plugin/-/inject-external-runtime-core-plugin-2.7.0.tgz} peerDependencies: '@module-federation/runtime-tools': 2.7.0 '@module-federation/managers@0.18.4': - resolution: {integrity: sha512-wJ8wheGNq4vnaLHx17F8Y0L+T9nzO5ijqMxQ7q9Yohm7MGeC5DoSjjurv/afxL6Dg5rGky+kHsYGM4qRTMFXaA==} + resolution: {integrity: sha512-wJ8wheGNq4vnaLHx17F8Y0L+T9nzO5ijqMxQ7q9Yohm7MGeC5DoSjjurv/afxL6Dg5rGky+kHsYGM4qRTMFXaA==, tarball: https://registry.npmjs.org/@module-federation/managers/-/managers-0.18.4.tgz} '@module-federation/managers@2.7.0': - resolution: {integrity: sha512-cWohiUvSrY6dIfhwsRABmEWfvJiAD5u/gxU7XRk7bx5nYPj7qn5gvYWJtvLNWzenluHZR6sib+03QMlyo+MYKQ==} + resolution: {integrity: sha512-cWohiUvSrY6dIfhwsRABmEWfvJiAD5u/gxU7XRk7bx5nYPj7qn5gvYWJtvLNWzenluHZR6sib+03QMlyo+MYKQ==, tarball: https://registry.npmjs.org/@module-federation/managers/-/managers-2.7.0.tgz} '@module-federation/manifest@0.18.4': - resolution: {integrity: sha512-1+sfldRpYmJX/SDqG3gWeeBbPb0H0eKyQcedf77TQGwFypVAOJwI39qV0yp3FdjutD7GdJ2TGPBHnGt7AbEvKA==} + resolution: {integrity: sha512-1+sfldRpYmJX/SDqG3gWeeBbPb0H0eKyQcedf77TQGwFypVAOJwI39qV0yp3FdjutD7GdJ2TGPBHnGt7AbEvKA==, tarball: https://registry.npmjs.org/@module-federation/manifest/-/manifest-0.18.4.tgz} '@module-federation/manifest@2.7.0': - resolution: {integrity: sha512-phK5/pK/e0JyjMh7a2tvRXUFE0WCG9lxu4BtBQllZXNO0zjrroOHl2s/0sSUCdOq6NIL1HF9wTAudYiCKmNFjA==} + resolution: {integrity: sha512-phK5/pK/e0JyjMh7a2tvRXUFE0WCG9lxu4BtBQllZXNO0zjrroOHl2s/0sSUCdOq6NIL1HF9wTAudYiCKmNFjA==, tarball: https://registry.npmjs.org/@module-federation/manifest/-/manifest-2.7.0.tgz} '@module-federation/node@2.7.46': - resolution: {integrity: sha512-LgrV5NU8SHKznzxl1gAtAYiWT0lFe9K8+mYNZ1atGkhpQiSeQFVsQbObZq5USs0dgjmpZtLtkwfFOQ66fKyNRA==} + resolution: {integrity: sha512-LgrV5NU8SHKznzxl1gAtAYiWT0lFe9K8+mYNZ1atGkhpQiSeQFVsQbObZq5USs0dgjmpZtLtkwfFOQ66fKyNRA==, tarball: https://registry.npmjs.org/@module-federation/node/-/node-2.7.46.tgz} peerDependencies: webpack: ^5.40.0 peerDependenciesMeta: @@ -4150,7 +4167,7 @@ packages: optional: true '@module-federation/rspack@0.18.4': - resolution: {integrity: sha512-gnvXKtk/w0ML15JHueWej5/8Lkoho7EoYUxvO77nBCnGOlXNqVYqLZ3REy2SS/8SQ4vQK156eSiyUkth2OYQqw==} + resolution: {integrity: sha512-gnvXKtk/w0ML15JHueWej5/8Lkoho7EoYUxvO77nBCnGOlXNqVYqLZ3REy2SS/8SQ4vQK156eSiyUkth2OYQqw==, tarball: https://registry.npmjs.org/@module-federation/rspack/-/rspack-0.18.4.tgz} peerDependencies: '@rspack/core': '>=0.7' typescript: ^4.9.0 || ^5.0.0 @@ -4162,7 +4179,7 @@ packages: optional: true '@module-federation/rspack@2.7.0': - resolution: {integrity: sha512-VbYc/5cpIze16ysBZJvmeXU7NN8tAs6Q/MdDsllIwO+Ir7JIEXgGrs5Bs+K7BuAu3HpxyRC8KanJTD+JB91+WA==} + resolution: {integrity: sha512-VbYc/5cpIze16ysBZJvmeXU7NN8tAs6Q/MdDsllIwO+Ir7JIEXgGrs5Bs+K7BuAu3HpxyRC8KanJTD+JB91+WA==, tarball: https://registry.npmjs.org/@module-federation/rspack/-/rspack-2.7.0.tgz} peerDependencies: '@rspack/core': ^0.7.0 || ^1.0.0 || ^2.0.0-0 typescript: ^4.9.0 || ^5.0.0 || ^6.0.0 @@ -4174,274 +4191,274 @@ packages: optional: true '@module-federation/runtime-core@0.18.4': - resolution: {integrity: sha512-LGGlFXlNeTbIGBFDiOvg0zz4jBWCGPqQatXdKx7mylXhDij7YmwbuW19oenX+P1fGhmoBUBM5WndmR87U66qWA==} + resolution: {integrity: sha512-LGGlFXlNeTbIGBFDiOvg0zz4jBWCGPqQatXdKx7mylXhDij7YmwbuW19oenX+P1fGhmoBUBM5WndmR87U66qWA==, tarball: https://registry.npmjs.org/@module-federation/runtime-core/-/runtime-core-0.18.4.tgz} '@module-federation/runtime-core@0.22.0': - resolution: {integrity: sha512-GR1TcD6/s7zqItfhC87zAp30PqzvceoeDGYTgF3Vx2TXvsfDrhP6Qw9T4vudDQL3uJRne6t7CzdT29YyVxlgIA==} + resolution: {integrity: sha512-GR1TcD6/s7zqItfhC87zAp30PqzvceoeDGYTgF3Vx2TXvsfDrhP6Qw9T4vudDQL3uJRne6t7CzdT29YyVxlgIA==, tarball: https://registry.npmjs.org/@module-federation/runtime-core/-/runtime-core-0.22.0.tgz} '@module-federation/runtime-core@2.7.0': - resolution: {integrity: sha512-5ROZLVIeV9YnWO2RwCwSHcy6sh48yclErO/2GZ2Xe8lFubPrFirgU8pbwBjZw+All0ZzN44BGS4ECRMVFzVcpg==} + resolution: {integrity: sha512-5ROZLVIeV9YnWO2RwCwSHcy6sh48yclErO/2GZ2Xe8lFubPrFirgU8pbwBjZw+All0ZzN44BGS4ECRMVFzVcpg==, tarball: https://registry.npmjs.org/@module-federation/runtime-core/-/runtime-core-2.7.0.tgz} '@module-federation/runtime-tools@0.18.4': - resolution: {integrity: sha512-wSGTdx77R8BQX+q6nAcUuHPydYYm0F97gAEP9RTW1UlzXnM/0AFysDHujvtRQf5vyXkhj//HdcH6LIJJCImy2g==} + resolution: {integrity: sha512-wSGTdx77R8BQX+q6nAcUuHPydYYm0F97gAEP9RTW1UlzXnM/0AFysDHujvtRQf5vyXkhj//HdcH6LIJJCImy2g==, tarball: https://registry.npmjs.org/@module-federation/runtime-tools/-/runtime-tools-0.18.4.tgz} '@module-federation/runtime-tools@0.22.0': - resolution: {integrity: sha512-4ScUJ/aUfEernb+4PbLdhM/c60VHl698Gn1gY21m9vyC1Ucn69fPCA1y2EwcCB7IItseRMoNhdcWQnzt/OPCNA==} + resolution: {integrity: sha512-4ScUJ/aUfEernb+4PbLdhM/c60VHl698Gn1gY21m9vyC1Ucn69fPCA1y2EwcCB7IItseRMoNhdcWQnzt/OPCNA==, tarball: https://registry.npmjs.org/@module-federation/runtime-tools/-/runtime-tools-0.22.0.tgz} '@module-federation/runtime-tools@2.7.0': - resolution: {integrity: sha512-AY61QeZ0jV0GywgR9j3Yd37KiXoY6gaSYABhjDF3q7XL9PNtb2Ezm1F/985wqaKJYX+qJvYv+PMH6hTvyHdPYQ==} + resolution: {integrity: sha512-AY61QeZ0jV0GywgR9j3Yd37KiXoY6gaSYABhjDF3q7XL9PNtb2Ezm1F/985wqaKJYX+qJvYv+PMH6hTvyHdPYQ==, tarball: https://registry.npmjs.org/@module-federation/runtime-tools/-/runtime-tools-2.7.0.tgz} '@module-federation/runtime@0.18.4': - resolution: {integrity: sha512-2et6p7pjGRHzpmrW425jt/BiAU7QHgkZtbQB7pj01eQ8qx6SloFEBk9ODnV8/ztSm9H2T3d8GxXA6/9xVOslmQ==} + resolution: {integrity: sha512-2et6p7pjGRHzpmrW425jt/BiAU7QHgkZtbQB7pj01eQ8qx6SloFEBk9ODnV8/ztSm9H2T3d8GxXA6/9xVOslmQ==, tarball: https://registry.npmjs.org/@module-federation/runtime/-/runtime-0.18.4.tgz} '@module-federation/runtime@0.22.0': - resolution: {integrity: sha512-38g5iPju2tPC3KHMPxRKmy4k4onNp6ypFPS1eKGsNLUkXgHsPMBFqAjDw96iEcjri91BrahG4XcdyKi97xZzlA==} + resolution: {integrity: sha512-38g5iPju2tPC3KHMPxRKmy4k4onNp6ypFPS1eKGsNLUkXgHsPMBFqAjDw96iEcjri91BrahG4XcdyKi97xZzlA==, tarball: https://registry.npmjs.org/@module-federation/runtime/-/runtime-0.22.0.tgz} '@module-federation/runtime@2.7.0': - resolution: {integrity: sha512-UtLozKKNvhT0D1+F0MEWsAmddJ39ItKW15E22LVMAwXmYZRSnzIvJQ2Y6kQ4LwhWABsw/GRSpUDex5OfhpSQPw==} + resolution: {integrity: sha512-UtLozKKNvhT0D1+F0MEWsAmddJ39ItKW15E22LVMAwXmYZRSnzIvJQ2Y6kQ4LwhWABsw/GRSpUDex5OfhpSQPw==, tarball: https://registry.npmjs.org/@module-federation/runtime/-/runtime-2.7.0.tgz} '@module-federation/sdk@0.18.4': - resolution: {integrity: sha512-dErzOlX+E3HS2Sg1m12Hi9nCnfvQPuIvlq9N47KxrbT2TIU3KKYc9q/Ua+QWqxfTyMVFpbNDwFMJ1R/w/gYf4A==} + resolution: {integrity: sha512-dErzOlX+E3HS2Sg1m12Hi9nCnfvQPuIvlq9N47KxrbT2TIU3KKYc9q/Ua+QWqxfTyMVFpbNDwFMJ1R/w/gYf4A==, tarball: https://registry.npmjs.org/@module-federation/sdk/-/sdk-0.18.4.tgz} '@module-federation/sdk@0.22.0': - resolution: {integrity: sha512-x4aFNBKn2KVQRuNVC5A7SnrSCSqyfIWmm1DvubjbO9iKFe7ith5niw8dqSFBekYBg2Fwy+eMg4sEFNVvCAdo6g==} + resolution: {integrity: sha512-x4aFNBKn2KVQRuNVC5A7SnrSCSqyfIWmm1DvubjbO9iKFe7ith5niw8dqSFBekYBg2Fwy+eMg4sEFNVvCAdo6g==, tarball: https://registry.npmjs.org/@module-federation/sdk/-/sdk-0.22.0.tgz} '@module-federation/sdk@2.7.0': - resolution: {integrity: sha512-piiLEjaIdjbNq8E11Di6vsfryhcdN/+sBCH6NjG7gSU2VHHoHEayZQWWL7VVdS6rVjexF9McLhPTSrl0adUU+A==} + resolution: {integrity: sha512-piiLEjaIdjbNq8E11Di6vsfryhcdN/+sBCH6NjG7gSU2VHHoHEayZQWWL7VVdS6rVjexF9McLhPTSrl0adUU+A==, tarball: https://registry.npmjs.org/@module-federation/sdk/-/sdk-2.7.0.tgz} '@module-federation/third-party-dts-extractor@0.18.4': - resolution: {integrity: sha512-PpiC0jxOegNR/xjhNOkjSYnUqMNJAy1kWsRd10to3Y64ZvGRf7/HF+x3aLIX8MbN7Ioy9F7Gd5oax6rtm+XmNQ==} + resolution: {integrity: sha512-PpiC0jxOegNR/xjhNOkjSYnUqMNJAy1kWsRd10to3Y64ZvGRf7/HF+x3aLIX8MbN7Ioy9F7Gd5oax6rtm+XmNQ==, tarball: https://registry.npmjs.org/@module-federation/third-party-dts-extractor/-/third-party-dts-extractor-0.18.4.tgz} '@module-federation/third-party-dts-extractor@2.7.0': - resolution: {integrity: sha512-hZJKkngQ4hwe0U+vrT3KOsU11qoHCQK0wB4x1bXoTgpTfV9j5NSuddOyRmwbvXpir9bDWh3xy7ghqsx3mFf3kA==} + resolution: {integrity: sha512-hZJKkngQ4hwe0U+vrT3KOsU11qoHCQK0wB4x1bXoTgpTfV9j5NSuddOyRmwbvXpir9bDWh3xy7ghqsx3mFf3kA==, tarball: https://registry.npmjs.org/@module-federation/third-party-dts-extractor/-/third-party-dts-extractor-2.7.0.tgz} '@module-federation/webpack-bundler-runtime@0.18.4': - resolution: {integrity: sha512-nPHp2wRS4/yfrGRQchZ0cyvdUZk+XgUmD0qWQl95xmeIeXUb90s3JrWFHSmS6Dt1gwMgJOeNpzzZDcBSy2P1VQ==} + resolution: {integrity: sha512-nPHp2wRS4/yfrGRQchZ0cyvdUZk+XgUmD0qWQl95xmeIeXUb90s3JrWFHSmS6Dt1gwMgJOeNpzzZDcBSy2P1VQ==, tarball: https://registry.npmjs.org/@module-federation/webpack-bundler-runtime/-/webpack-bundler-runtime-0.18.4.tgz} '@module-federation/webpack-bundler-runtime@0.22.0': - resolution: {integrity: sha512-aM8gCqXu+/4wBmJtVeMeeMN5guw3chf+2i6HajKtQv7SJfxV/f4IyNQJUeUQu9HfiAZHjqtMV5Lvq/Lvh8LdyA==} + resolution: {integrity: sha512-aM8gCqXu+/4wBmJtVeMeeMN5guw3chf+2i6HajKtQv7SJfxV/f4IyNQJUeUQu9HfiAZHjqtMV5Lvq/Lvh8LdyA==, tarball: https://registry.npmjs.org/@module-federation/webpack-bundler-runtime/-/webpack-bundler-runtime-0.22.0.tgz} '@module-federation/webpack-bundler-runtime@2.7.0': - resolution: {integrity: sha512-3qLRIqcZVBNgJrZpiEzcTP8a6+mCdUV1QGk/XljawsQHyNieMVdLQtdLvkoF5Z5kRXNMovq+wv3vchKOMWyA8w==} + resolution: {integrity: sha512-3qLRIqcZVBNgJrZpiEzcTP8a6+mCdUV1QGk/XljawsQHyNieMVdLQtdLvkoF5Z5kRXNMovq+wv3vchKOMWyA8w==, tarball: https://registry.npmjs.org/@module-federation/webpack-bundler-runtime/-/webpack-bundler-runtime-2.7.0.tgz} '@msgpackr-extract/msgpackr-extract-darwin-arm64@3.0.4': - resolution: {integrity: sha512-LCkGo6JDfaBhgST7UpPWgNgLINpcpabaHfyz5OBx75nUYxBsaEPxjnyNjWpeb/xBup/682QnBfRBy2/LvPutZQ==} + resolution: {integrity: sha512-LCkGo6JDfaBhgST7UpPWgNgLINpcpabaHfyz5OBx75nUYxBsaEPxjnyNjWpeb/xBup/682QnBfRBy2/LvPutZQ==, tarball: https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-darwin-arm64/-/msgpackr-extract-darwin-arm64-3.0.4.tgz} cpu: [arm64] os: [darwin] '@msgpackr-extract/msgpackr-extract-darwin-x64@3.0.4': - resolution: {integrity: sha512-zExlW9zUJKZH/tOtVMttwjKa4Xm/3KcNjnE3dPN92uCktwavMxpgCA3MoJK/DOnTWsQgo224OaST27/mPNAf+w==} + resolution: {integrity: sha512-zExlW9zUJKZH/tOtVMttwjKa4Xm/3KcNjnE3dPN92uCktwavMxpgCA3MoJK/DOnTWsQgo224OaST27/mPNAf+w==, tarball: https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-darwin-x64/-/msgpackr-extract-darwin-x64-3.0.4.tgz} cpu: [x64] os: [darwin] '@msgpackr-extract/msgpackr-extract-linux-arm64@3.0.4': - resolution: {integrity: sha512-dgX0P/9wGPJeHFBG+ZmhgE6bmtMt7NP5CRBGyyktpopdk/mW4POnrpQsSLtKI1dwpc+pPLuXHDh6vvskyQE/sw==} + resolution: {integrity: sha512-dgX0P/9wGPJeHFBG+ZmhgE6bmtMt7NP5CRBGyyktpopdk/mW4POnrpQsSLtKI1dwpc+pPLuXHDh6vvskyQE/sw==, tarball: https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-linux-arm64/-/msgpackr-extract-linux-arm64-3.0.4.tgz} cpu: [arm64] os: [linux] '@msgpackr-extract/msgpackr-extract-linux-arm@3.0.4': - resolution: {integrity: sha512-Tg3yX65f5GbtXLkrYEHE5oibZG9epyYWas7FogTTEJeDEF9JlXJzKgXaNhT3UXlTOeA+AfZpYZYZ0uPj7Cfquw==} + resolution: {integrity: sha512-Tg3yX65f5GbtXLkrYEHE5oibZG9epyYWas7FogTTEJeDEF9JlXJzKgXaNhT3UXlTOeA+AfZpYZYZ0uPj7Cfquw==, tarball: https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-linux-arm/-/msgpackr-extract-linux-arm-3.0.4.tgz} cpu: [arm] os: [linux] '@msgpackr-extract/msgpackr-extract-linux-x64@3.0.4': - resolution: {integrity: sha512-8TNXMEjJc3QEy7R/x1INhgiU+XakDAFUzBhaz7+Rbrs8NH5UQeHQxxmzsSBJGyV6I1jW79undiQm8tOI+D+8FQ==} + resolution: {integrity: sha512-8TNXMEjJc3QEy7R/x1INhgiU+XakDAFUzBhaz7+Rbrs8NH5UQeHQxxmzsSBJGyV6I1jW79undiQm8tOI+D+8FQ==, tarball: https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-linux-x64/-/msgpackr-extract-linux-x64-3.0.4.tgz} cpu: [x64] os: [linux] '@msgpackr-extract/msgpackr-extract-win32-x64@3.0.4': - resolution: {integrity: sha512-CmCXPQrkbwExx3j946/PtHWHbYJiCRBRDl4BlkRQcJB/YOwQxJRTpoo7aTsortjgoJ1x7opzTSxn7C+ASSLVjQ==} + resolution: {integrity: sha512-CmCXPQrkbwExx3j946/PtHWHbYJiCRBRDl4BlkRQcJB/YOwQxJRTpoo7aTsortjgoJ1x7opzTSxn7C+ASSLVjQ==, tarball: https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-win32-x64/-/msgpackr-extract-win32-x64-3.0.4.tgz} cpu: [x64] os: [win32] '@napi-rs/nice-android-arm-eabi@1.1.1': - resolution: {integrity: sha512-kjirL3N6TnRPv5iuHw36wnucNqXAO46dzK9oPb0wj076R5Xm8PfUVA9nAFB5ZNMmfJQJVKACAPd/Z2KYMppthw==} + resolution: {integrity: sha512-kjirL3N6TnRPv5iuHw36wnucNqXAO46dzK9oPb0wj076R5Xm8PfUVA9nAFB5ZNMmfJQJVKACAPd/Z2KYMppthw==, tarball: https://registry.npmjs.org/@napi-rs/nice-android-arm-eabi/-/nice-android-arm-eabi-1.1.1.tgz} engines: {node: '>= 10'} cpu: [arm] os: [android] '@napi-rs/nice-android-arm64@1.1.1': - resolution: {integrity: sha512-blG0i7dXgbInN5urONoUCNf+DUEAavRffrO7fZSeoRMJc5qD+BJeNcpr54msPF6qfDD6kzs9AQJogZvT2KD5nw==} + resolution: {integrity: sha512-blG0i7dXgbInN5urONoUCNf+DUEAavRffrO7fZSeoRMJc5qD+BJeNcpr54msPF6qfDD6kzs9AQJogZvT2KD5nw==, tarball: https://registry.npmjs.org/@napi-rs/nice-android-arm64/-/nice-android-arm64-1.1.1.tgz} engines: {node: '>= 10'} cpu: [arm64] os: [android] '@napi-rs/nice-darwin-arm64@1.1.1': - resolution: {integrity: sha512-s/E7w45NaLqTGuOjC2p96pct4jRfo61xb9bU1unM/MJ/RFkKlJyJDx7OJI/O0ll/hrfpqKopuAFDV8yo0hfT7A==} + resolution: {integrity: sha512-s/E7w45NaLqTGuOjC2p96pct4jRfo61xb9bU1unM/MJ/RFkKlJyJDx7OJI/O0ll/hrfpqKopuAFDV8yo0hfT7A==, tarball: https://registry.npmjs.org/@napi-rs/nice-darwin-arm64/-/nice-darwin-arm64-1.1.1.tgz} engines: {node: '>= 10'} cpu: [arm64] os: [darwin] '@napi-rs/nice-darwin-x64@1.1.1': - resolution: {integrity: sha512-dGoEBnVpsdcC+oHHmW1LRK5eiyzLwdgNQq3BmZIav+9/5WTZwBYX7r5ZkQC07Nxd3KHOCkgbHSh4wPkH1N1LiQ==} + resolution: {integrity: sha512-dGoEBnVpsdcC+oHHmW1LRK5eiyzLwdgNQq3BmZIav+9/5WTZwBYX7r5ZkQC07Nxd3KHOCkgbHSh4wPkH1N1LiQ==, tarball: https://registry.npmjs.org/@napi-rs/nice-darwin-x64/-/nice-darwin-x64-1.1.1.tgz} engines: {node: '>= 10'} cpu: [x64] os: [darwin] '@napi-rs/nice-freebsd-x64@1.1.1': - resolution: {integrity: sha512-kHv4kEHAylMYmlNwcQcDtXjklYp4FCf0b05E+0h6nDHsZ+F0bDe04U/tXNOqrx5CmIAth4vwfkjjUmp4c4JktQ==} + resolution: {integrity: sha512-kHv4kEHAylMYmlNwcQcDtXjklYp4FCf0b05E+0h6nDHsZ+F0bDe04U/tXNOqrx5CmIAth4vwfkjjUmp4c4JktQ==, tarball: https://registry.npmjs.org/@napi-rs/nice-freebsd-x64/-/nice-freebsd-x64-1.1.1.tgz} engines: {node: '>= 10'} cpu: [x64] os: [freebsd] '@napi-rs/nice-linux-arm-gnueabihf@1.1.1': - resolution: {integrity: sha512-E1t7K0efyKXZDoZg1LzCOLxgolxV58HCkaEkEvIYQx12ht2pa8hoBo+4OB3qh7e+QiBlp1SRf+voWUZFxyhyqg==} + resolution: {integrity: sha512-E1t7K0efyKXZDoZg1LzCOLxgolxV58HCkaEkEvIYQx12ht2pa8hoBo+4OB3qh7e+QiBlp1SRf+voWUZFxyhyqg==, tarball: https://registry.npmjs.org/@napi-rs/nice-linux-arm-gnueabihf/-/nice-linux-arm-gnueabihf-1.1.1.tgz} engines: {node: '>= 10'} cpu: [arm] os: [linux] '@napi-rs/nice-linux-arm64-gnu@1.1.1': - resolution: {integrity: sha512-CIKLA12DTIZlmTaaKhQP88R3Xao+gyJxNWEn04wZwC2wmRapNnxCUZkVwggInMJvtVElA+D4ZzOU5sX4jV+SmQ==} + resolution: {integrity: sha512-CIKLA12DTIZlmTaaKhQP88R3Xao+gyJxNWEn04wZwC2wmRapNnxCUZkVwggInMJvtVElA+D4ZzOU5sX4jV+SmQ==, tarball: https://registry.npmjs.org/@napi-rs/nice-linux-arm64-gnu/-/nice-linux-arm64-gnu-1.1.1.tgz} engines: {node: '>= 10'} cpu: [arm64] os: [linux] '@napi-rs/nice-linux-arm64-musl@1.1.1': - resolution: {integrity: sha512-+2Rzdb3nTIYZ0YJF43qf2twhqOCkiSrHx2Pg6DJaCPYhhaxbLcdlV8hCRMHghQ+EtZQWGNcS2xF4KxBhSGeutg==} + resolution: {integrity: sha512-+2Rzdb3nTIYZ0YJF43qf2twhqOCkiSrHx2Pg6DJaCPYhhaxbLcdlV8hCRMHghQ+EtZQWGNcS2xF4KxBhSGeutg==, tarball: https://registry.npmjs.org/@napi-rs/nice-linux-arm64-musl/-/nice-linux-arm64-musl-1.1.1.tgz} engines: {node: '>= 10'} cpu: [arm64] os: [linux] '@napi-rs/nice-linux-ppc64-gnu@1.1.1': - resolution: {integrity: sha512-4FS8oc0GeHpwvv4tKciKkw3Y4jKsL7FRhaOeiPei0X9T4Jd619wHNe4xCLmN2EMgZoeGg+Q7GY7BsvwKpL22Tg==} + resolution: {integrity: sha512-4FS8oc0GeHpwvv4tKciKkw3Y4jKsL7FRhaOeiPei0X9T4Jd619wHNe4xCLmN2EMgZoeGg+Q7GY7BsvwKpL22Tg==, tarball: https://registry.npmjs.org/@napi-rs/nice-linux-ppc64-gnu/-/nice-linux-ppc64-gnu-1.1.1.tgz} engines: {node: '>= 10'} cpu: [ppc64] os: [linux] '@napi-rs/nice-linux-riscv64-gnu@1.1.1': - resolution: {integrity: sha512-HU0nw9uD4FO/oGCCk409tCi5IzIZpH2agE6nN4fqpwVlCn5BOq0MS1dXGjXaG17JaAvrlpV5ZeyZwSon10XOXw==} + resolution: {integrity: sha512-HU0nw9uD4FO/oGCCk409tCi5IzIZpH2agE6nN4fqpwVlCn5BOq0MS1dXGjXaG17JaAvrlpV5ZeyZwSon10XOXw==, tarball: https://registry.npmjs.org/@napi-rs/nice-linux-riscv64-gnu/-/nice-linux-riscv64-gnu-1.1.1.tgz} engines: {node: '>= 10'} cpu: [riscv64] os: [linux] '@napi-rs/nice-linux-s390x-gnu@1.1.1': - resolution: {integrity: sha512-2YqKJWWl24EwrX0DzCQgPLKQBxYDdBxOHot1KWEq7aY2uYeX+Uvtv4I8xFVVygJDgf6/92h9N3Y43WPx8+PAgQ==} + resolution: {integrity: sha512-2YqKJWWl24EwrX0DzCQgPLKQBxYDdBxOHot1KWEq7aY2uYeX+Uvtv4I8xFVVygJDgf6/92h9N3Y43WPx8+PAgQ==, tarball: https://registry.npmjs.org/@napi-rs/nice-linux-s390x-gnu/-/nice-linux-s390x-gnu-1.1.1.tgz} engines: {node: '>= 10'} cpu: [s390x] os: [linux] '@napi-rs/nice-linux-x64-gnu@1.1.1': - resolution: {integrity: sha512-/gaNz3R92t+dcrfCw/96pDopcmec7oCcAQ3l/M+Zxr82KT4DljD37CpgrnXV+pJC263JkW572pdbP3hP+KjcIg==} + resolution: {integrity: sha512-/gaNz3R92t+dcrfCw/96pDopcmec7oCcAQ3l/M+Zxr82KT4DljD37CpgrnXV+pJC263JkW572pdbP3hP+KjcIg==, tarball: https://registry.npmjs.org/@napi-rs/nice-linux-x64-gnu/-/nice-linux-x64-gnu-1.1.1.tgz} engines: {node: '>= 10'} cpu: [x64] os: [linux] '@napi-rs/nice-linux-x64-musl@1.1.1': - resolution: {integrity: sha512-xScCGnyj/oppsNPMnevsBe3pvNaoK7FGvMjT35riz9YdhB2WtTG47ZlbxtOLpjeO9SqqQ2J2igCmz6IJOD5JYw==} + resolution: {integrity: sha512-xScCGnyj/oppsNPMnevsBe3pvNaoK7FGvMjT35riz9YdhB2WtTG47ZlbxtOLpjeO9SqqQ2J2igCmz6IJOD5JYw==, tarball: https://registry.npmjs.org/@napi-rs/nice-linux-x64-musl/-/nice-linux-x64-musl-1.1.1.tgz} engines: {node: '>= 10'} cpu: [x64] os: [linux] '@napi-rs/nice-openharmony-arm64@1.1.1': - resolution: {integrity: sha512-6uJPRVwVCLDeoOaNyeiW0gp2kFIM4r7PL2MczdZQHkFi9gVlgm+Vn+V6nTWRcu856mJ2WjYJiumEajfSm7arPQ==} + resolution: {integrity: sha512-6uJPRVwVCLDeoOaNyeiW0gp2kFIM4r7PL2MczdZQHkFi9gVlgm+Vn+V6nTWRcu856mJ2WjYJiumEajfSm7arPQ==, tarball: https://registry.npmjs.org/@napi-rs/nice-openharmony-arm64/-/nice-openharmony-arm64-1.1.1.tgz} engines: {node: '>= 10'} cpu: [arm64] os: [openharmony] '@napi-rs/nice-win32-arm64-msvc@1.1.1': - resolution: {integrity: sha512-uoTb4eAvM5B2aj/z8j+Nv8OttPf2m+HVx3UjA5jcFxASvNhQriyCQF1OB1lHL43ZhW+VwZlgvjmP5qF3+59atA==} + resolution: {integrity: sha512-uoTb4eAvM5B2aj/z8j+Nv8OttPf2m+HVx3UjA5jcFxASvNhQriyCQF1OB1lHL43ZhW+VwZlgvjmP5qF3+59atA==, tarball: https://registry.npmjs.org/@napi-rs/nice-win32-arm64-msvc/-/nice-win32-arm64-msvc-1.1.1.tgz} engines: {node: '>= 10'} cpu: [arm64] os: [win32] '@napi-rs/nice-win32-ia32-msvc@1.1.1': - resolution: {integrity: sha512-CNQqlQT9MwuCsg1Vd/oKXiuH+TcsSPJmlAFc5frFyX/KkOh0UpBLEj7aoY656d5UKZQMQFP7vJNa1DNUNORvug==} + resolution: {integrity: sha512-CNQqlQT9MwuCsg1Vd/oKXiuH+TcsSPJmlAFc5frFyX/KkOh0UpBLEj7aoY656d5UKZQMQFP7vJNa1DNUNORvug==, tarball: https://registry.npmjs.org/@napi-rs/nice-win32-ia32-msvc/-/nice-win32-ia32-msvc-1.1.1.tgz} engines: {node: '>= 10'} cpu: [ia32] os: [win32] '@napi-rs/nice-win32-x64-msvc@1.1.1': - resolution: {integrity: sha512-vB+4G/jBQCAh0jelMTY3+kgFy00Hlx2f2/1zjMoH821IbplbWZOkLiTYXQkygNTzQJTq5cvwBDgn2ppHD+bglQ==} + resolution: {integrity: sha512-vB+4G/jBQCAh0jelMTY3+kgFy00Hlx2f2/1zjMoH821IbplbWZOkLiTYXQkygNTzQJTq5cvwBDgn2ppHD+bglQ==, tarball: https://registry.npmjs.org/@napi-rs/nice-win32-x64-msvc/-/nice-win32-x64-msvc-1.1.1.tgz} engines: {node: '>= 10'} cpu: [x64] os: [win32] '@napi-rs/nice@1.1.1': - resolution: {integrity: sha512-xJIPs+bYuc9ASBl+cvGsKbGrJmS6fAKaSZCnT0lhahT5rhA2VVy9/EcIgd2JhtEuFOJNx7UHNn/qiTPTY4nrQw==} + resolution: {integrity: sha512-xJIPs+bYuc9ASBl+cvGsKbGrJmS6fAKaSZCnT0lhahT5rhA2VVy9/EcIgd2JhtEuFOJNx7UHNn/qiTPTY4nrQw==, tarball: https://registry.npmjs.org/@napi-rs/nice/-/nice-1.1.1.tgz} engines: {node: '>= 10'} '@napi-rs/wasm-runtime@0.2.4': - resolution: {integrity: sha512-9zESzOO5aDByvhIAsOy9TbpZ0Ur2AJbUI7UT73kcUTS2mxAMHOBaa1st/jAymNoCtvrit99kkzT1FZuXVcgfIQ==} + resolution: {integrity: sha512-9zESzOO5aDByvhIAsOy9TbpZ0Ur2AJbUI7UT73kcUTS2mxAMHOBaa1st/jAymNoCtvrit99kkzT1FZuXVcgfIQ==, tarball: https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-0.2.4.tgz} '@napi-rs/wasm-runtime@1.0.7': - resolution: {integrity: sha512-SeDnOO0Tk7Okiq6DbXmmBODgOAb9dp9gjlphokTUxmt8U3liIP1ZsozBahH69j/RJv+Rfs6IwUKHTgQYJ/HBAw==} + resolution: {integrity: sha512-SeDnOO0Tk7Okiq6DbXmmBODgOAb9dp9gjlphokTUxmt8U3liIP1ZsozBahH69j/RJv+Rfs6IwUKHTgQYJ/HBAw==, tarball: https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.0.7.tgz} '@napi-rs/wasm-runtime@1.1.6': - resolution: {integrity: sha512-ZLv/JdUfkvOy9eCnnBaGfiO+XimbjebAeO+MRQqD/B+FR1tnRN0tpKSJHRbE8sFfS6aqsXZ67TQjfwfsxULVbg==} + resolution: {integrity: sha512-ZLv/JdUfkvOy9eCnnBaGfiO+XimbjebAeO+MRQqD/B+FR1tnRN0tpKSJHRbE8sFfS6aqsXZ67TQjfwfsxULVbg==, tarball: https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.6.tgz} peerDependencies: '@emnapi/core': ^1.7.1 '@emnapi/runtime': ^1.7.1 '@next/env@14.0.4': - resolution: {integrity: sha512-irQnbMLbUNQpP1wcE5NstJtbuA/69kRfzBrpAD7Gsn8zm/CY6YQYc3HQBz8QPxwISG26tIm5afvvVbu508oBeQ==} + resolution: {integrity: sha512-irQnbMLbUNQpP1wcE5NstJtbuA/69kRfzBrpAD7Gsn8zm/CY6YQYc3HQBz8QPxwISG26tIm5afvvVbu508oBeQ==, tarball: https://registry.npmjs.org/@next/env/-/env-14.0.4.tgz} '@next/swc-darwin-arm64@14.0.4': - resolution: {integrity: sha512-mF05E/5uPthWzyYDyptcwHptucf/jj09i2SXBPwNzbgBNc+XnwzrL0U6BmPjQeOL+FiB+iG1gwBeq7mlDjSRPg==} + resolution: {integrity: sha512-mF05E/5uPthWzyYDyptcwHptucf/jj09i2SXBPwNzbgBNc+XnwzrL0U6BmPjQeOL+FiB+iG1gwBeq7mlDjSRPg==, tarball: https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-14.0.4.tgz} engines: {node: '>= 10'} cpu: [arm64] os: [darwin] '@next/swc-darwin-x64@14.0.4': - resolution: {integrity: sha512-IZQ3C7Bx0k2rYtrZZxKKiusMTM9WWcK5ajyhOZkYYTCc8xytmwSzR1skU7qLgVT/EY9xtXDG0WhY6fyujnI3rw==} + resolution: {integrity: sha512-IZQ3C7Bx0k2rYtrZZxKKiusMTM9WWcK5ajyhOZkYYTCc8xytmwSzR1skU7qLgVT/EY9xtXDG0WhY6fyujnI3rw==, tarball: https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-14.0.4.tgz} engines: {node: '>= 10'} cpu: [x64] os: [darwin] '@next/swc-linux-arm64-gnu@14.0.4': - resolution: {integrity: sha512-VwwZKrBQo/MGb1VOrxJ6LrKvbpo7UbROuyMRvQKTFKhNaXjUmKTu7wxVkIuCARAfiI8JpaWAnKR+D6tzpCcM4w==} + resolution: {integrity: sha512-VwwZKrBQo/MGb1VOrxJ6LrKvbpo7UbROuyMRvQKTFKhNaXjUmKTu7wxVkIuCARAfiI8JpaWAnKR+D6tzpCcM4w==, tarball: https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-14.0.4.tgz} engines: {node: '>= 10'} cpu: [arm64] os: [linux] '@next/swc-linux-arm64-musl@14.0.4': - resolution: {integrity: sha512-8QftwPEW37XxXoAwsn+nXlodKWHfpMaSvt81W43Wh8dv0gkheD+30ezWMcFGHLI71KiWmHK5PSQbTQGUiidvLQ==} + resolution: {integrity: sha512-8QftwPEW37XxXoAwsn+nXlodKWHfpMaSvt81W43Wh8dv0gkheD+30ezWMcFGHLI71KiWmHK5PSQbTQGUiidvLQ==, tarball: https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-14.0.4.tgz} engines: {node: '>= 10'} cpu: [arm64] os: [linux] '@next/swc-linux-x64-gnu@14.0.4': - resolution: {integrity: sha512-/s/Pme3VKfZAfISlYVq2hzFS8AcAIOTnoKupc/j4WlvF6GQ0VouS2Q2KEgPuO1eMBwakWPB1aYFIA4VNVh667A==} + resolution: {integrity: sha512-/s/Pme3VKfZAfISlYVq2hzFS8AcAIOTnoKupc/j4WlvF6GQ0VouS2Q2KEgPuO1eMBwakWPB1aYFIA4VNVh667A==, tarball: https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-14.0.4.tgz} engines: {node: '>= 10'} cpu: [x64] os: [linux] '@next/swc-linux-x64-musl@14.0.4': - resolution: {integrity: sha512-m8z/6Fyal4L9Bnlxde5g2Mfa1Z7dasMQyhEhskDATpqr+Y0mjOBZcXQ7G5U+vgL22cI4T7MfvgtrM2jdopqWaw==} + resolution: {integrity: sha512-m8z/6Fyal4L9Bnlxde5g2Mfa1Z7dasMQyhEhskDATpqr+Y0mjOBZcXQ7G5U+vgL22cI4T7MfvgtrM2jdopqWaw==, tarball: https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-14.0.4.tgz} engines: {node: '>= 10'} cpu: [x64] os: [linux] '@next/swc-win32-arm64-msvc@14.0.4': - resolution: {integrity: sha512-7Wv4PRiWIAWbm5XrGz3D8HUkCVDMMz9igffZG4NB1p4u1KoItwx9qjATHz88kwCEal/HXmbShucaslXCQXUM5w==} + resolution: {integrity: sha512-7Wv4PRiWIAWbm5XrGz3D8HUkCVDMMz9igffZG4NB1p4u1KoItwx9qjATHz88kwCEal/HXmbShucaslXCQXUM5w==, tarball: https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-14.0.4.tgz} engines: {node: '>= 10'} cpu: [arm64] os: [win32] '@next/swc-win32-ia32-msvc@14.0.4': - resolution: {integrity: sha512-zLeNEAPULsl0phfGb4kdzF/cAVIfaC7hY+kt0/d+y9mzcZHsMS3hAS829WbJ31DkSlVKQeHEjZHIdhN+Pg7Gyg==} + resolution: {integrity: sha512-zLeNEAPULsl0phfGb4kdzF/cAVIfaC7hY+kt0/d+y9mzcZHsMS3hAS829WbJ31DkSlVKQeHEjZHIdhN+Pg7Gyg==, tarball: https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-14.0.4.tgz} engines: {node: '>= 10'} cpu: [ia32] os: [win32] '@next/swc-win32-x64-msvc@14.0.4': - resolution: {integrity: sha512-yEh2+R8qDlDCjxVpzOTEpBLQTEFAcP2A8fUFLaWNap9GitYKkKv1//y2S6XY6zsR4rCOPRpU7plYDR+az2n30A==} + resolution: {integrity: sha512-yEh2+R8qDlDCjxVpzOTEpBLQTEFAcP2A8fUFLaWNap9GitYKkKv1//y2S6XY6zsR4rCOPRpU7plYDR+az2n30A==, tarball: https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-14.0.4.tgz} engines: {node: '>= 10'} cpu: [x64] os: [win32] '@ngrx/component-store@21.1.1': - resolution: {integrity: sha512-P0FgzOtCv8ULa4VuNnmYr9y5JfZT4T2fxb7Ozp306J0yjIv9xamMjehVmra5iA2U22TMS5L+WXmLWQ5O2I4Piw==} + resolution: {integrity: sha512-P0FgzOtCv8ULa4VuNnmYr9y5JfZT4T2fxb7Ozp306J0yjIv9xamMjehVmra5iA2U22TMS5L+WXmLWQ5O2I4Piw==, tarball: https://registry.npmjs.org/@ngrx/component-store/-/component-store-21.1.1.tgz} peerDependencies: '@angular/core': ^21.0.0 rxjs: ^6.5.3 || ^7.5.0 '@ngrx/operators@21.1.1': - resolution: {integrity: sha512-2ChT1rVi6w8xHrmIAqRFuvY7YLAJiH7idfmbimjOYHzJ7pgaz0+4WVqyCp9pv2UiDEXuvpUOvs3afgS5m1LDNQ==} + resolution: {integrity: sha512-2ChT1rVi6w8xHrmIAqRFuvY7YLAJiH7idfmbimjOYHzJ7pgaz0+4WVqyCp9pv2UiDEXuvpUOvs3afgS5m1LDNQ==, tarball: https://registry.npmjs.org/@ngrx/operators/-/operators-21.1.1.tgz} peerDependencies: rxjs: ^6.5.3 || ^7.4.0 '@ngrx/signals@21.1.1': - resolution: {integrity: sha512-AXxsvO39cJ4gu2GMvLyUHwx+3qHi35Bn7OUqyIfJW0fffi/Af7ML6h0MPMWDHVROo55FBgxzJrYRswwzdew9VA==} + resolution: {integrity: sha512-AXxsvO39cJ4gu2GMvLyUHwx+3qHi35Bn7OUqyIfJW0fffi/Af7ML6h0MPMWDHVROo55FBgxzJrYRswwzdew9VA==, tarball: https://registry.npmjs.org/@ngrx/signals/-/signals-21.1.1.tgz} peerDependencies: '@angular/core': ^21.0.0 rxjs: ^6.5.3 || ^7.4.0 @@ -4450,7 +4467,7 @@ packages: optional: true '@ngtools/webpack@22.1.2': - resolution: {integrity: sha512-5o3zbOdEHv5+wjRCMN+t6RBou28dbPkio5/rxEJBsQ9iEq11K+atsSKEaZXBMUhYr9nLYR8Pckm5Cqx2FP9R2Q==} + resolution: {integrity: sha512-5o3zbOdEHv5+wjRCMN+t6RBou28dbPkio5/rxEJBsQ9iEq11K+atsSKEaZXBMUhYr9nLYR8Pckm5Cqx2FP9R2Q==, tarball: https://registry.npmjs.org/@ngtools/webpack/-/webpack-22.1.2.tgz} engines: {node: ^22.22.3 || ^24.15.0 || >=26.0.0, npm: ^6.11.0 || ^7.5.6 || >=8.0.0, yarn: '>= 1.13.0'} peerDependencies: '@angular/compiler-cli': ^22.0.0 @@ -4458,23 +4475,23 @@ packages: webpack: ^5.54.0 '@noble/hashes@1.4.0': - resolution: {integrity: sha512-V1JJ1WTRUqHHrOSh597hURcMqVKVGL/ea3kv0gSnEdsEZ0/+VyPghM1lMNGc00z7CIQorSvbKpuJkxvuHbvdbg==} + resolution: {integrity: sha512-V1JJ1WTRUqHHrOSh597hURcMqVKVGL/ea3kv0gSnEdsEZ0/+VyPghM1lMNGc00z7CIQorSvbKpuJkxvuHbvdbg==, tarball: https://registry.npmjs.org/@noble/hashes/-/hashes-1.4.0.tgz} engines: {node: '>= 16'} '@nodelib/fs.scandir@2.1.5': - resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} + resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==, tarball: https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz} engines: {node: '>= 8'} '@nodelib/fs.stat@2.0.5': - resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==} + resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==, tarball: https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz} engines: {node: '>= 8'} '@nodelib/fs.walk@1.2.8': - resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} + resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==, tarball: https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz} engines: {node: '>= 8'} '@nx/angular@21.6.11': - resolution: {integrity: sha512-+yKVaftz90aNmH2+L0upOGHpRNpQUD3hoOUhmPit+GB59Ow6cwxUqF5DGRcUpEKBVkFY8d6GrBiha9Jep9brQA==} + resolution: {integrity: sha512-+yKVaftz90aNmH2+L0upOGHpRNpQUD3hoOUhmPit+GB59Ow6cwxUqF5DGRcUpEKBVkFY8d6GrBiha9Jep9brQA==, tarball: https://registry.npmjs.org/@nx/angular/-/angular-21.6.11.tgz} peerDependencies: '@angular-devkit/build-angular': '>= 18.0.0 < 21.0.0' '@angular-devkit/core': '>= 18.0.0 < 21.0.0' @@ -4492,7 +4509,7 @@ packages: optional: true '@nx/angular@23.1.1': - resolution: {integrity: sha512-E1n6Rn2C9hnkYEmgSzqqO1NrZW+bI0Ti43O34j1eDbw2l66by1RxUAtRvGPMcJz19sJDWGH4I4UUIRz2CSJ7Cw==} + resolution: {integrity: sha512-E1n6Rn2C9hnkYEmgSzqqO1NrZW+bI0Ti43O34j1eDbw2l66by1RxUAtRvGPMcJz19sJDWGH4I4UUIRz2CSJ7Cw==, tarball: https://registry.npmjs.org/@nx/angular/-/angular-23.1.1.tgz} peerDependencies: '@angular-devkit/build-angular': '>= 20.0.0 < 23.0.0' '@angular-devkit/core': '>= 20.0.0 < 23.0.0' @@ -4516,7 +4533,7 @@ packages: optional: true '@nx/cypress@21.6.11': - resolution: {integrity: sha512-ntFQwnOMgdgvbimjirLM+6YjqSook1C3Pw+OihZQAiZ5Ivry4h6JMWdCH4e16BO20uossiep/oEiv+F/uxBPqA==} + resolution: {integrity: sha512-ntFQwnOMgdgvbimjirLM+6YjqSook1C3Pw+OihZQAiZ5Ivry4h6JMWdCH4e16BO20uossiep/oEiv+F/uxBPqA==, tarball: https://registry.npmjs.org/@nx/cypress/-/cypress-21.6.11.tgz} peerDependencies: cypress: '>= 3 < 15' peerDependenciesMeta: @@ -4524,20 +4541,20 @@ packages: optional: true '@nx/devkit@21.6.11': - resolution: {integrity: sha512-tjx0GMuJQSXBhmz4XZD3D5jVtXO9/bIc7fLe0tXJ5w6ohQMKhBdkpyyjFm+1nRdAnWRQARqM2HqV+WZLYr3axQ==} + resolution: {integrity: sha512-tjx0GMuJQSXBhmz4XZD3D5jVtXO9/bIc7fLe0tXJ5w6ohQMKhBdkpyyjFm+1nRdAnWRQARqM2HqV+WZLYr3axQ==, tarball: https://registry.npmjs.org/@nx/devkit/-/devkit-21.6.11.tgz} peerDependencies: nx: '>= 20 <= 22' '@nx/devkit@23.1.1': - resolution: {integrity: sha512-FmBfS1xUkWYvDYH/ysO7gAqGlaRzugLac8SIC+X/p76WBmhM6tJhfRW/BQ8mxOFZoVLmwhIiR8X0dRPKdasFxw==} + resolution: {integrity: sha512-FmBfS1xUkWYvDYH/ysO7gAqGlaRzugLac8SIC+X/p76WBmhM6tJhfRW/BQ8mxOFZoVLmwhIiR8X0dRPKdasFxw==, tarball: https://registry.npmjs.org/@nx/devkit/-/devkit-23.1.1.tgz} peerDependencies: nx: '>= 22 <= 24 || ^23.0.0-0' '@nx/docker@23.1.1': - resolution: {integrity: sha512-0zjVbO1e0xlC8BvB2O9S5Ll23khTBYMI6Vwe3jPGs5ZhCbw9sTaqmHm9CwKA++x+lDJ9nCWI7C0lKpk9otZiAw==} + resolution: {integrity: sha512-0zjVbO1e0xlC8BvB2O9S5Ll23khTBYMI6Vwe3jPGs5ZhCbw9sTaqmHm9CwKA++x+lDJ9nCWI7C0lKpk9otZiAw==, tarball: https://registry.npmjs.org/@nx/docker/-/docker-23.1.1.tgz} '@nx/esbuild@23.1.1': - resolution: {integrity: sha512-jdNdMmBp2snspaYLDqoKkEYE+n5ZFCppQJcUSRxon0MzK0AZ4/mLVzhCJmpE2mycgrHyaNlF34UF6eWMNSwQtw==} + resolution: {integrity: sha512-jdNdMmBp2snspaYLDqoKkEYE+n5ZFCppQJcUSRxon0MzK0AZ4/mLVzhCJmpE2mycgrHyaNlF34UF6eWMNSwQtw==, tarball: https://registry.npmjs.org/@nx/esbuild/-/esbuild-23.1.1.tgz} peerDependencies: esbuild: '>=0.19.2 <1.0.0' peerDependenciesMeta: @@ -4545,7 +4562,7 @@ packages: optional: true '@nx/eslint-plugin@23.1.1': - resolution: {integrity: sha512-XJwYAwroCPCk6m60mB6MMt92DFtRzQwUgZnKoszhgvWIX5RUx4RqQqGOmTpvxAkY0f5csUm9BWwfOeQLPewSMQ==} + resolution: {integrity: sha512-XJwYAwroCPCk6m60mB6MMt92DFtRzQwUgZnKoszhgvWIX5RUx4RqQqGOmTpvxAkY0f5csUm9BWwfOeQLPewSMQ==, tarball: https://registry.npmjs.org/@nx/eslint-plugin/-/eslint-plugin-23.1.1.tgz} peerDependencies: '@typescript-eslint/parser': ^8.0.0 eslint-config-prettier: ^10.0.0 @@ -4556,7 +4573,7 @@ packages: optional: true '@nx/eslint@21.6.11': - resolution: {integrity: sha512-/44yu7ulGZeFt0XNeJDUIms2K1izQbEW83Z1NK/m9jwTdiHU2BwSsKkm5iXzU8GIiKFVnCg/IA519/5WiCjXfw==} + resolution: {integrity: sha512-/44yu7ulGZeFt0XNeJDUIms2K1izQbEW83Z1NK/m9jwTdiHU2BwSsKkm5iXzU8GIiKFVnCg/IA519/5WiCjXfw==, tarball: https://registry.npmjs.org/@nx/eslint/-/eslint-21.6.11.tgz} peerDependencies: '@zkochan/js-yaml': 0.0.7 eslint: ^8.0.0 || ^9.0.0 @@ -4565,7 +4582,7 @@ packages: optional: true '@nx/eslint@23.1.1': - resolution: {integrity: sha512-tG8/OxsGj8DVnxIu/838Jad1kv5jUgf/OpyYNFkHEDdx2KfE/fpaKh6uwOptlz6IZU47473xzAocx4UBemsaaQ==} + resolution: {integrity: sha512-tG8/OxsGj8DVnxIu/838Jad1kv5jUgf/OpyYNFkHEDdx2KfE/fpaKh6uwOptlz6IZU47473xzAocx4UBemsaaQ==, tarball: https://registry.npmjs.org/@nx/eslint/-/eslint-23.1.1.tgz} peerDependencies: '@nx/jest': 23.1.1 '@zkochan/js-yaml': 0.0.7 @@ -4577,10 +4594,10 @@ packages: optional: true '@nx/jest@21.6.11': - resolution: {integrity: sha512-d0ON6plXuA4Xn6Pnd5DHQaW9meuPygVF9QDZKPUthYBffBS5TdLnu2GUFjnSxWa4gryQtxeICQ6tuCWma1loyA==} + resolution: {integrity: sha512-d0ON6plXuA4Xn6Pnd5DHQaW9meuPygVF9QDZKPUthYBffBS5TdLnu2GUFjnSxWa4gryQtxeICQ6tuCWma1loyA==, tarball: https://registry.npmjs.org/@nx/jest/-/jest-21.6.11.tgz} '@nx/jest@23.1.1': - resolution: {integrity: sha512-kigS+KVWe1/JuQteKApWOJvWCMvKCDgdIoZRNmxfaM5c8Ou/KoNNLZEQBPaK5tpbR4SIUCOb51JDo75WVuDolA==} + resolution: {integrity: sha512-kigS+KVWe1/JuQteKApWOJvWCMvKCDgdIoZRNmxfaM5c8Ou/KoNNLZEQBPaK5tpbR4SIUCOb51JDo75WVuDolA==, tarball: https://registry.npmjs.org/@nx/jest/-/jest-23.1.1.tgz} peerDependencies: jest: ^29.0.0 || ^30.0.0 ts-jest: ^29.0.0 @@ -4591,7 +4608,7 @@ packages: optional: true '@nx/js@21.6.11': - resolution: {integrity: sha512-4o6+zcxa82FgUMYfC8a4UujvYIINqwEaBPV0wq64Kk7h6YVeTioCNCDqUVMHQdcv4NRiWsUpGhc19PMnGGHTBQ==} + resolution: {integrity: sha512-4o6+zcxa82FgUMYfC8a4UujvYIINqwEaBPV0wq64Kk7h6YVeTioCNCDqUVMHQdcv4NRiWsUpGhc19PMnGGHTBQ==, tarball: https://registry.npmjs.org/@nx/js/-/js-21.6.11.tgz} peerDependencies: verdaccio: ^6.0.5 peerDependenciesMeta: @@ -4599,7 +4616,7 @@ packages: optional: true '@nx/js@23.1.1': - resolution: {integrity: sha512-8YnhKAnSE7lTiiUEoUyO5LBmZiRIqsc/v2qFMAUfbJKr2bAKAB4h+6Lz9ZfbMvRzJH3fcDZVVWVcvU5GXmQhzg==} + resolution: {integrity: sha512-8YnhKAnSE7lTiiUEoUyO5LBmZiRIqsc/v2qFMAUfbJKr2bAKAB4h+6Lz9ZfbMvRzJH3fcDZVVWVcvU5GXmQhzg==, tarball: https://registry.npmjs.org/@nx/js/-/js-23.1.1.tgz} peerDependencies: '@swc/cli': '>=0.6.0 <0.9.0' verdaccio: ^6.0.5 @@ -4610,10 +4627,10 @@ packages: optional: true '@nx/module-federation@21.6.11': - resolution: {integrity: sha512-iqp2QNqXtm/z90Tcaw0WurRmXsOLgKYZ/ptTAAWc9zxNrN+3uPDYEoLFp1ghTI+GVDugr2cBsu+bxW29G9OEVw==} + resolution: {integrity: sha512-iqp2QNqXtm/z90Tcaw0WurRmXsOLgKYZ/ptTAAWc9zxNrN+3uPDYEoLFp1ghTI+GVDugr2cBsu+bxW29G9OEVw==, tarball: https://registry.npmjs.org/@nx/module-federation/-/module-federation-21.6.11.tgz} '@nx/module-federation@23.1.1': - resolution: {integrity: sha512-FHfSDIskZIV35kzb+kqEJfRRDJhev6b0ncg5SCEp9PVyA7npd7qOWxHcFs4F5bJJRZp5VMWGDHnIdwRWasCJCg==} + resolution: {integrity: sha512-FHfSDIskZIV35kzb+kqEJfRRDJhev6b0ncg5SCEp9PVyA7npd7qOWxHcFs4F5bJJRZp5VMWGDHnIdwRWasCJCg==, tarball: https://registry.npmjs.org/@nx/module-federation/-/module-federation-23.1.1.tgz} peerDependencies: '@module-federation/enhanced': ^2.0.0 '@module-federation/node': ^2.0.0 @@ -4624,7 +4641,7 @@ packages: optional: true '@nx/node@23.1.1': - resolution: {integrity: sha512-4L0LZmE162emyzldLgS3tAi3EQhevzfg5yNOsVXOM9jvWUX+hRK7HY1WKXlqtZ32h/JdVyRoL22SMzZVSJ4foA==} + resolution: {integrity: sha512-4L0LZmE162emyzldLgS3tAi3EQhevzfg5yNOsVXOM9jvWUX+hRK7HY1WKXlqtZ32h/JdVyRoL22SMzZVSJ4foA==, tarball: https://registry.npmjs.org/@nx/node/-/node-23.1.1.tgz} peerDependencies: express: '>=4.0.0 <6.0.0' fastify: '>=4.0.0 <6.0.0' @@ -4638,107 +4655,107 @@ packages: optional: true '@nx/nx-darwin-arm64@21.6.11': - resolution: {integrity: sha512-4hXhV7ShXIlfPEjjm7dJY383xM2vTcnkKr5FUncAU08GKkkL67ib5CMlQADtdi32ewfCZntqiT8gUfFFSNvKtA==} + resolution: {integrity: sha512-4hXhV7ShXIlfPEjjm7dJY383xM2vTcnkKr5FUncAU08GKkkL67ib5CMlQADtdi32ewfCZntqiT8gUfFFSNvKtA==, tarball: https://registry.npmjs.org/@nx/nx-darwin-arm64/-/nx-darwin-arm64-21.6.11.tgz} cpu: [arm64] os: [darwin] '@nx/nx-darwin-arm64@23.1.1': - resolution: {integrity: sha512-Rq/RXLX5uIvJQfb6kuUgEirquT5ARaAgQyNaMZVnOPAL5wuxaDvQag8WX/WUCIgbUKZuGkclJwM7Vlnvtn3bdg==} + resolution: {integrity: sha512-Rq/RXLX5uIvJQfb6kuUgEirquT5ARaAgQyNaMZVnOPAL5wuxaDvQag8WX/WUCIgbUKZuGkclJwM7Vlnvtn3bdg==, tarball: https://registry.npmjs.org/@nx/nx-darwin-arm64/-/nx-darwin-arm64-23.1.1.tgz} cpu: [arm64] os: [darwin] '@nx/nx-darwin-x64@21.6.11': - resolution: {integrity: sha512-VxjKkzyhdO47X7d4JPx/f6HslERKetFmouDUBIoqbKDPVWpRegGMsRKcMYh4l61usxI1Qa2U4Ec6MgO5Fnm41g==} + resolution: {integrity: sha512-VxjKkzyhdO47X7d4JPx/f6HslERKetFmouDUBIoqbKDPVWpRegGMsRKcMYh4l61usxI1Qa2U4Ec6MgO5Fnm41g==, tarball: https://registry.npmjs.org/@nx/nx-darwin-x64/-/nx-darwin-x64-21.6.11.tgz} cpu: [x64] os: [darwin] '@nx/nx-darwin-x64@23.1.1': - resolution: {integrity: sha512-doWaPLPd6yUas3FhQJqMAScupCsToeTedK4RRWm700VhHoVdBTN4ejIBRBfoiT/SPAwY6EOHb8uFDJhGo7geMg==} + resolution: {integrity: sha512-doWaPLPd6yUas3FhQJqMAScupCsToeTedK4RRWm700VhHoVdBTN4ejIBRBfoiT/SPAwY6EOHb8uFDJhGo7geMg==, tarball: https://registry.npmjs.org/@nx/nx-darwin-x64/-/nx-darwin-x64-23.1.1.tgz} cpu: [x64] os: [darwin] '@nx/nx-freebsd-x64@21.6.11': - resolution: {integrity: sha512-3jDn7Tb3FMFfeFTM/XKAcI+J92kDLNmxUS/N/n/+kF/XYLPgMUZQqSeDpVifk4fgy0BCoH8DSMQIqTQau6dV/g==} + resolution: {integrity: sha512-3jDn7Tb3FMFfeFTM/XKAcI+J92kDLNmxUS/N/n/+kF/XYLPgMUZQqSeDpVifk4fgy0BCoH8DSMQIqTQau6dV/g==, tarball: https://registry.npmjs.org/@nx/nx-freebsd-x64/-/nx-freebsd-x64-21.6.11.tgz} cpu: [x64] os: [freebsd] '@nx/nx-freebsd-x64@23.1.1': - resolution: {integrity: sha512-9rDZKBPGuX8mid11RimJ2ENqDYZpPZhrqTlI9q/VnqcPLq0Bw/8AhqCKhBVlfNqJjbi4OsRQYDK7UkqIlcfThg==} + resolution: {integrity: sha512-9rDZKBPGuX8mid11RimJ2ENqDYZpPZhrqTlI9q/VnqcPLq0Bw/8AhqCKhBVlfNqJjbi4OsRQYDK7UkqIlcfThg==, tarball: https://registry.npmjs.org/@nx/nx-freebsd-x64/-/nx-freebsd-x64-23.1.1.tgz} cpu: [x64] os: [freebsd] '@nx/nx-linux-arm-gnueabihf@21.6.11': - resolution: {integrity: sha512-37tpiVod5FN/EAuCGh+uad/6nsfDFze02OjYReUPKeYPs8Q7Ac/V9j4kn/y5uZhKSle+9+sa2QR/K79B0+lNxw==} + resolution: {integrity: sha512-37tpiVod5FN/EAuCGh+uad/6nsfDFze02OjYReUPKeYPs8Q7Ac/V9j4kn/y5uZhKSle+9+sa2QR/K79B0+lNxw==, tarball: https://registry.npmjs.org/@nx/nx-linux-arm-gnueabihf/-/nx-linux-arm-gnueabihf-21.6.11.tgz} cpu: [arm] os: [linux] '@nx/nx-linux-arm-gnueabihf@23.1.1': - resolution: {integrity: sha512-NDR5X2HiD6WU3JEaDJmOLteIGIFqjrjkzoFWrQke2Y1oCRYu+UyFdPeaMVUyAs5OyUx4U+SD+eBQPTCNbWmazA==} + resolution: {integrity: sha512-NDR5X2HiD6WU3JEaDJmOLteIGIFqjrjkzoFWrQke2Y1oCRYu+UyFdPeaMVUyAs5OyUx4U+SD+eBQPTCNbWmazA==, tarball: https://registry.npmjs.org/@nx/nx-linux-arm-gnueabihf/-/nx-linux-arm-gnueabihf-23.1.1.tgz} cpu: [arm] os: [linux] '@nx/nx-linux-arm64-gnu@21.6.11': - resolution: {integrity: sha512-r+czH0OtldQqFm2B6BBBUPW8aO+sBMvpZCgN855vko30WaCeXo8Fkuk71rQMxByz0jnoCxSnzLtEWVZm1rmJYA==} + resolution: {integrity: sha512-r+czH0OtldQqFm2B6BBBUPW8aO+sBMvpZCgN855vko30WaCeXo8Fkuk71rQMxByz0jnoCxSnzLtEWVZm1rmJYA==, tarball: https://registry.npmjs.org/@nx/nx-linux-arm64-gnu/-/nx-linux-arm64-gnu-21.6.11.tgz} cpu: [arm64] os: [linux] '@nx/nx-linux-arm64-gnu@23.1.1': - resolution: {integrity: sha512-tWDHJII8+aHweTzHelf5dGM6qGNmHbAPhCc3jrtrM0uE+UD/wt2Dpq7H3086Iyia9M9jGM9sYpuD/6W6WAFAMA==} + resolution: {integrity: sha512-tWDHJII8+aHweTzHelf5dGM6qGNmHbAPhCc3jrtrM0uE+UD/wt2Dpq7H3086Iyia9M9jGM9sYpuD/6W6WAFAMA==, tarball: https://registry.npmjs.org/@nx/nx-linux-arm64-gnu/-/nx-linux-arm64-gnu-23.1.1.tgz} cpu: [arm64] os: [linux] '@nx/nx-linux-arm64-musl@21.6.11': - resolution: {integrity: sha512-0FAPyWEGPCukXxR1qowvFx6Q/cU906vwPlAQtcbrBU1e2H2aMUslk9EmL8iHb5MsHdmGcFyFemwr9oN8gxmz4g==} + resolution: {integrity: sha512-0FAPyWEGPCukXxR1qowvFx6Q/cU906vwPlAQtcbrBU1e2H2aMUslk9EmL8iHb5MsHdmGcFyFemwr9oN8gxmz4g==, tarball: https://registry.npmjs.org/@nx/nx-linux-arm64-musl/-/nx-linux-arm64-musl-21.6.11.tgz} cpu: [arm64] os: [linux] '@nx/nx-linux-arm64-musl@23.1.1': - resolution: {integrity: sha512-t7iVMZ7Cj3LmPcfaYt9KOkojpuC5XRmtZ/0G+NBMA2GuRhCVbzpOgUCob0nQMV2TrTwn5oAWJeDc4xLni+oteg==} + resolution: {integrity: sha512-t7iVMZ7Cj3LmPcfaYt9KOkojpuC5XRmtZ/0G+NBMA2GuRhCVbzpOgUCob0nQMV2TrTwn5oAWJeDc4xLni+oteg==, tarball: https://registry.npmjs.org/@nx/nx-linux-arm64-musl/-/nx-linux-arm64-musl-23.1.1.tgz} cpu: [arm64] os: [linux] '@nx/nx-linux-x64-gnu@21.6.11': - resolution: {integrity: sha512-+bWJWXJ8tdddl1L3bTKE0VmvTmdsk4zzUr6P9ts9hXQbwoWqMcuA6LNqOhUfzVOL3VioirRMtKMfFuUAUhi3Yg==} + resolution: {integrity: sha512-+bWJWXJ8tdddl1L3bTKE0VmvTmdsk4zzUr6P9ts9hXQbwoWqMcuA6LNqOhUfzVOL3VioirRMtKMfFuUAUhi3Yg==, tarball: https://registry.npmjs.org/@nx/nx-linux-x64-gnu/-/nx-linux-x64-gnu-21.6.11.tgz} cpu: [x64] os: [linux] '@nx/nx-linux-x64-gnu@23.1.1': - resolution: {integrity: sha512-stuCayctOt/4AFvxKYgGTPluUc0HW7DcyTz9yeTMl/zj0+FENEr8RCTjInD0Q5qVGzYphma6SssVSh432w5agw==} + resolution: {integrity: sha512-stuCayctOt/4AFvxKYgGTPluUc0HW7DcyTz9yeTMl/zj0+FENEr8RCTjInD0Q5qVGzYphma6SssVSh432w5agw==, tarball: https://registry.npmjs.org/@nx/nx-linux-x64-gnu/-/nx-linux-x64-gnu-23.1.1.tgz} cpu: [x64] os: [linux] '@nx/nx-linux-x64-musl@21.6.11': - resolution: {integrity: sha512-Mh09mLc+yeJk7DKfx7x6XfB+bm2dP1/7gyUuRQj4WjkT+Il2ZponyTuH8LmX06Jpr7efAAFk+8lBXeleV7XvMw==} + resolution: {integrity: sha512-Mh09mLc+yeJk7DKfx7x6XfB+bm2dP1/7gyUuRQj4WjkT+Il2ZponyTuH8LmX06Jpr7efAAFk+8lBXeleV7XvMw==, tarball: https://registry.npmjs.org/@nx/nx-linux-x64-musl/-/nx-linux-x64-musl-21.6.11.tgz} cpu: [x64] os: [linux] '@nx/nx-linux-x64-musl@23.1.1': - resolution: {integrity: sha512-cZSUqGV+iHda39W91BNKSysSu6OFfR6M4ViQBcMtbwq3ce19gDr2f23MqGZEQZtaD/eSQ9UNEhx09nhrdYxWDg==} + resolution: {integrity: sha512-cZSUqGV+iHda39W91BNKSysSu6OFfR6M4ViQBcMtbwq3ce19gDr2f23MqGZEQZtaD/eSQ9UNEhx09nhrdYxWDg==, tarball: https://registry.npmjs.org/@nx/nx-linux-x64-musl/-/nx-linux-x64-musl-23.1.1.tgz} cpu: [x64] os: [linux] '@nx/nx-win32-arm64-msvc@21.6.11': - resolution: {integrity: sha512-d7ZeCCDwaeyKiWD2JLSSxMSuSHHwIdpbnMtZtzRE6tDdAgs6e9F36/OBNQB3IRXH8V6QKOy2OGDyWeOKr01X9A==} + resolution: {integrity: sha512-d7ZeCCDwaeyKiWD2JLSSxMSuSHHwIdpbnMtZtzRE6tDdAgs6e9F36/OBNQB3IRXH8V6QKOy2OGDyWeOKr01X9A==, tarball: https://registry.npmjs.org/@nx/nx-win32-arm64-msvc/-/nx-win32-arm64-msvc-21.6.11.tgz} cpu: [arm64] os: [win32] '@nx/nx-win32-arm64-msvc@23.1.1': - resolution: {integrity: sha512-iDlYbFHgTYV5lg1ypEt+LAj86o/uyy6vR0ha5pcUs4FqXzQgy14lOeyRod/EZs9Er3DIyJlEi/rpEvaP99/Hag==} + resolution: {integrity: sha512-iDlYbFHgTYV5lg1ypEt+LAj86o/uyy6vR0ha5pcUs4FqXzQgy14lOeyRod/EZs9Er3DIyJlEi/rpEvaP99/Hag==, tarball: https://registry.npmjs.org/@nx/nx-win32-arm64-msvc/-/nx-win32-arm64-msvc-23.1.1.tgz} cpu: [arm64] os: [win32] '@nx/nx-win32-x64-msvc@21.6.11': - resolution: {integrity: sha512-otHSkhyoGilttV4RRkVmLLGb2W+Ia4b90lWxLnEm9jAAKmA9O2FUG2vAvKDHNcqTyDwwCcHfHxslgnR1u/3OIg==} + resolution: {integrity: sha512-otHSkhyoGilttV4RRkVmLLGb2W+Ia4b90lWxLnEm9jAAKmA9O2FUG2vAvKDHNcqTyDwwCcHfHxslgnR1u/3OIg==, tarball: https://registry.npmjs.org/@nx/nx-win32-x64-msvc/-/nx-win32-x64-msvc-21.6.11.tgz} cpu: [x64] os: [win32] '@nx/nx-win32-x64-msvc@23.1.1': - resolution: {integrity: sha512-UD21AHWJ2PEA+PuANPCt+lj3kYpAzYNq+bm4VCbrt3KZd7zDPas0ns85UIhfrhsNiSmYzjWz2/JDk2S3cA6lGw==} + resolution: {integrity: sha512-UD21AHWJ2PEA+PuANPCt+lj3kYpAzYNq+bm4VCbrt3KZd7zDPas0ns85UIhfrhsNiSmYzjWz2/JDk2S3cA6lGw==, tarball: https://registry.npmjs.org/@nx/nx-win32-x64-msvc/-/nx-win32-x64-msvc-23.1.1.tgz} cpu: [x64] os: [win32] '@nx/playwright@23.1.1': - resolution: {integrity: sha512-gLVExIBJlpqEm5ssPGxQNqvrgb3TSM8yP05cO/xgI5C2vOXvFFsAYSy1uBO+hPg/MWPZtVtcOi50QH+IMbJ4Aw==} + resolution: {integrity: sha512-gLVExIBJlpqEm5ssPGxQNqvrgb3TSM8yP05cO/xgI5C2vOXvFFsAYSy1uBO+hPg/MWPZtVtcOi50QH+IMbJ4Aw==, tarball: https://registry.npmjs.org/@nx/playwright/-/playwright-23.1.1.tgz} peerDependencies: '@playwright/test': ^1.36.0 peerDependenciesMeta: @@ -4746,10 +4763,10 @@ packages: optional: true '@nx/react@21.6.11': - resolution: {integrity: sha512-8W+YpG88bco9APjKptHHlHxlBv+vbCz8KhHpFoYZc+MMX9T1MElWNObyJWyF+jf9t1csjdfaWBCevzdq/tFJQQ==} + resolution: {integrity: sha512-8W+YpG88bco9APjKptHHlHxlBv+vbCz8KhHpFoYZc+MMX9T1MElWNObyJWyF+jf9t1csjdfaWBCevzdq/tFJQQ==, tarball: https://registry.npmjs.org/@nx/react/-/react-21.6.11.tgz} '@nx/react@23.1.1': - resolution: {integrity: sha512-/DMricayXZ+pE9gsGvwn1P+XxrUwzDulvMEz21wc0wxMuw5FYOGsTdKTrg59Q9l0NwSAyKAUloLCNOcrltMkPQ==} + resolution: {integrity: sha512-/DMricayXZ+pE9gsGvwn1P+XxrUwzDulvMEz21wc0wxMuw5FYOGsTdKTrg59Q9l0NwSAyKAUloLCNOcrltMkPQ==, tarball: https://registry.npmjs.org/@nx/react/-/react-23.1.1.tgz} peerDependencies: babel-plugin-react-compiler: ^1.0.0 react: '>=18.0.0 <20.0.0' @@ -4759,10 +4776,10 @@ packages: optional: true '@nx/rollup@21.6.11': - resolution: {integrity: sha512-c1K5zkhUKxnsCgair+rC68avrF967hw17dq7e5qkbWgKtwJw3W3twHYlsEO0cgPlMofbYnX0RHOvYJPegKJmLQ==} + resolution: {integrity: sha512-c1K5zkhUKxnsCgair+rC68avrF967hw17dq7e5qkbWgKtwJw3W3twHYlsEO0cgPlMofbYnX0RHOvYJPegKJmLQ==, tarball: https://registry.npmjs.org/@nx/rollup/-/rollup-21.6.11.tgz} '@nx/rollup@23.1.1': - resolution: {integrity: sha512-0597XSjzU1aG+HaZmzTqrKPqEQ4aRMKjgg9bwQD3PS/uDaXlJ7ye1uGzHtC8PGZtOWwdr5y4MeBJcXXOlARaUw==} + resolution: {integrity: sha512-0597XSjzU1aG+HaZmzTqrKPqEQ4aRMKjgg9bwQD3PS/uDaXlJ7ye1uGzHtC8PGZtOWwdr5y4MeBJcXXOlARaUw==, tarball: https://registry.npmjs.org/@nx/rollup/-/rollup-23.1.1.tgz} peerDependencies: rollup: ^3.0.0 || ^4.0.0 peerDependenciesMeta: @@ -4770,13 +4787,13 @@ packages: optional: true '@nx/rspack@21.6.11': - resolution: {integrity: sha512-ryJ6xG1AVC7orDkwehOf0+E38zb8kWGLROW/6Ny8cB/uaU0ea2dMFlNHUAJuwdJ183yjbi2rhWW/u8PWqFHGsw==} + resolution: {integrity: sha512-ryJ6xG1AVC7orDkwehOf0+E38zb8kWGLROW/6Ny8cB/uaU0ea2dMFlNHUAJuwdJ183yjbi2rhWW/u8PWqFHGsw==, tarball: https://registry.npmjs.org/@nx/rspack/-/rspack-21.6.11.tgz} peerDependencies: '@module-federation/enhanced': ^0.18.0 '@module-federation/node': ^2.7.11 '@nx/rspack@23.1.1': - resolution: {integrity: sha512-PAa6ytDhK3P0d85MNo0AqZhxGkCSca/KOWix99OB5baxtESLruyL1odK4mooOOoAlyDbDsZTPO0wXUyfe6YE6A==} + resolution: {integrity: sha512-PAa6ytDhK3P0d85MNo0AqZhxGkCSca/KOWix99OB5baxtESLruyL1odK4mooOOoAlyDbDsZTPO0wXUyfe6YE6A==, tarball: https://registry.npmjs.org/@nx/rspack/-/rspack-23.1.1.tgz} peerDependencies: '@rspack/cli': ^1.0.0 || ^2.0.0 '@rspack/core': ^1.0.0 || ^2.0.0 @@ -4793,23 +4810,23 @@ packages: optional: true '@nx/storybook@21.6.11': - resolution: {integrity: sha512-iVycGMvLZ7yiJnS8WYlRzZpdSlRybeaYuuAm1hT2k8LZK9kN67UwZUI/CQhIIcaXizRbh2WONiQ0dG9CFdwBSw==} + resolution: {integrity: sha512-iVycGMvLZ7yiJnS8WYlRzZpdSlRybeaYuuAm1hT2k8LZK9kN67UwZUI/CQhIIcaXizRbh2WONiQ0dG9CFdwBSw==, tarball: https://registry.npmjs.org/@nx/storybook/-/storybook-21.6.11.tgz} peerDependencies: storybook: '>=7.0.0 <10.0.0' '@nx/vite@21.6.11': - resolution: {integrity: sha512-T0yx+8R2N/srGOE1b2QJhaETS4UH690YmaM2hHUwWHWyXi83BzNBM8TPPj007wsoEAcbd6PYKp/vHaIVEker6g==} + resolution: {integrity: sha512-T0yx+8R2N/srGOE1b2QJhaETS4UH690YmaM2hHUwWHWyXi83BzNBM8TPPj007wsoEAcbd6PYKp/vHaIVEker6g==, tarball: https://registry.npmjs.org/@nx/vite/-/vite-21.6.11.tgz} peerDependencies: vite: ^5.0.0 || ^6.0.0 || ^7.0.0 vitest: ^1.3.1 || ^2.0.0 || ^3.0.0 '@nx/vite@23.1.1': - resolution: {integrity: sha512-mYjlCnp0iKBas8TT/4xLL7PnZwx6YiL1gYngj3dbmYRAhJwd+D6dkG6AcDdiodoO0KRPxdKQv+pffKQB8EnELw==} + resolution: {integrity: sha512-mYjlCnp0iKBas8TT/4xLL7PnZwx6YiL1gYngj3dbmYRAhJwd+D6dkG6AcDdiodoO0KRPxdKQv+pffKQB8EnELw==, tarball: https://registry.npmjs.org/@nx/vite/-/vite-23.1.1.tgz} peerDependencies: vite: ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0 '@nx/vitest@23.1.1': - resolution: {integrity: sha512-gKZ/dkJcA6Ex2rSDbJuyEUsU8HaApA8T7O3wV7RD1qLIefRDCr8+beaOCClOB+3e+8peGddrCp3wMXSysrTRnQ==} + resolution: {integrity: sha512-gKZ/dkJcA6Ex2rSDbJuyEUsU8HaApA8T7O3wV7RD1qLIefRDCr8+beaOCClOB+3e+8peGddrCp3wMXSysrTRnQ==, tarball: https://registry.npmjs.org/@nx/vitest/-/vitest-23.1.1.tgz} peerDependencies: '@nx/eslint': 23.1.1 vite: ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0 @@ -4823,10 +4840,10 @@ packages: optional: true '@nx/vue@21.6.11': - resolution: {integrity: sha512-FZLYQBtSgzGghtzwllV8oNBJD82tzk9+EOIE4OBIyUVPEPv6fT8MlNezi4A8Az1+aebiPpHoikjLf/vGbWfw9Q==} + resolution: {integrity: sha512-FZLYQBtSgzGghtzwllV8oNBJD82tzk9+EOIE4OBIyUVPEPv6fT8MlNezi4A8Az1+aebiPpHoikjLf/vGbWfw9Q==, tarball: https://registry.npmjs.org/@nx/vue/-/vue-21.6.11.tgz} '@nx/vue@23.1.1': - resolution: {integrity: sha512-8K7fNYIEAwXgvElNt4hdlDoS/pMVY93P/WKV1FC4LCbWALDIJ7lDCV6wzRlaf3qJl3aFih+Zv73cbW28VR3nvQ==} + resolution: {integrity: sha512-8K7fNYIEAwXgvElNt4hdlDoS/pMVY93P/WKV1FC4LCbWALDIJ7lDCV6wzRlaf3qJl3aFih+Zv73cbW28VR3nvQ==, tarball: https://registry.npmjs.org/@nx/vue/-/vue-23.1.1.tgz} peerDependencies: '@nx/cypress': 23.1.1 '@nx/playwright': 23.1.1 @@ -4855,10 +4872,10 @@ packages: optional: true '@nx/web@21.6.11': - resolution: {integrity: sha512-Vsv2HE1hrECajujvL0ss28ZhOF6cUErHoZ11B3OYRNgZMjv28qx725cyTesxc9z9UrFLNUVlkCr57XZPnAOHnw==} + resolution: {integrity: sha512-Vsv2HE1hrECajujvL0ss28ZhOF6cUErHoZ11B3OYRNgZMjv28qx725cyTesxc9z9UrFLNUVlkCr57XZPnAOHnw==, tarball: https://registry.npmjs.org/@nx/web/-/web-21.6.11.tgz} '@nx/web@23.1.1': - resolution: {integrity: sha512-f1yQcnsC/HUzQJ8WHLzIdxqgemCfORjdC+zYFOpXURhLoJTqFt3J0Xifoez3oK/qUrC7eiDjSsjvartJthgCeQ==} + resolution: {integrity: sha512-f1yQcnsC/HUzQJ8WHLzIdxqgemCfORjdC+zYFOpXURhLoJTqFt3J0Xifoez3oK/qUrC7eiDjSsjvartJthgCeQ==, tarball: https://registry.npmjs.org/@nx/web/-/web-23.1.1.tgz} peerDependencies: '@nx/cypress': 23.1.1 '@nx/eslint': 23.1.1 @@ -4881,10 +4898,10 @@ packages: optional: true '@nx/webpack@21.6.11': - resolution: {integrity: sha512-2pN4WvEhRXi+AGTor1G6tk2kR4wzSAEcAAwEdx9EifDdoWf8voQNU1Z3+Uia0NGahdt/oer24EiTCHb4J2q5VQ==} + resolution: {integrity: sha512-2pN4WvEhRXi+AGTor1G6tk2kR4wzSAEcAAwEdx9EifDdoWf8voQNU1Z3+Uia0NGahdt/oer24EiTCHb4J2q5VQ==, tarball: https://registry.npmjs.org/@nx/webpack/-/webpack-21.6.11.tgz} '@nx/webpack@23.1.1': - resolution: {integrity: sha512-vO7N0cpzpWI7yWUtORJKMVtmvPr218xZjhbHXB7BGEzmqV7ZE+Wkg36pRAy5gMjGqBbjsGHicxUo4yUcYsWGBw==} + resolution: {integrity: sha512-vO7N0cpzpWI7yWUtORJKMVtmvPr218xZjhbHXB7BGEzmqV7ZE+Wkg36pRAy5gMjGqBbjsGHicxUo4yUcYsWGBw==, tarball: https://registry.npmjs.org/@nx/webpack/-/webpack-23.1.1.tgz} peerDependencies: webpack: ^5.0.0 webpack-cli: ^5.0.0 || ^6.0.0 || ^7.0.0 @@ -4898,16 +4915,16 @@ packages: optional: true '@nx/workspace@21.6.11': - resolution: {integrity: sha512-EMY3erQiP/VNqzsPyfxLhCrBzbTb1ug9+LWr7McDd30f4qM8VSgc2f/L00HcmNhRInkwBeEO+PqL11rIqz/lFw==} + resolution: {integrity: sha512-EMY3erQiP/VNqzsPyfxLhCrBzbTb1ug9+LWr7McDd30f4qM8VSgc2f/L00HcmNhRInkwBeEO+PqL11rIqz/lFw==, tarball: https://registry.npmjs.org/@nx/workspace/-/workspace-21.6.11.tgz} '@nx/workspace@23.1.1': - resolution: {integrity: sha512-woBDOW9bNcp+I2UEKhV62zc+1/gPCKPkTHZgwCdgoXSlXC3N9soGpnLG5QFy8NLodFC1f+5TSQJqN1tBW5rfIA==} + resolution: {integrity: sha512-woBDOW9bNcp+I2UEKhV62zc+1/gPCKPkTHZgwCdgoXSlXC3N9soGpnLG5QFy8NLodFC1f+5TSQJqN1tBW5rfIA==, tarball: https://registry.npmjs.org/@nx/workspace/-/workspace-23.1.1.tgz} '@nxext/common@21.0.0': - resolution: {integrity: sha512-LJZTB8wWVNLkkGnBZr90f7ciXCNPQT/qlL8t7zpfRqE6f5b3rEdcajwoqq+UogJsgjdSO4pfeiQzO2fevTu+qQ==} + resolution: {integrity: sha512-LJZTB8wWVNLkkGnBZr90f7ciXCNPQT/qlL8t7zpfRqE6f5b3rEdcajwoqq+UogJsgjdSO4pfeiQzO2fevTu+qQ==, tarball: https://registry.npmjs.org/@nxext/common/-/common-21.0.0.tgz} '@nxext/stencil@21.0.0': - resolution: {integrity: sha512-G3fWPVmRsCxMeFPAAqopzkiAqofa7u3x6RwNrUmGVj8x2lYYqDF+YuEkJ3wsI4kkHjwveeBxGZbyhiX3QUVe8Q==} + resolution: {integrity: sha512-G3fWPVmRsCxMeFPAAqopzkiAqofa7u3x6RwNrUmGVj8x2lYYqDF+YuEkJ3wsI4kkHjwveeBxGZbyhiX3QUVe8Q==, tarball: https://registry.npmjs.org/@nxext/stencil/-/stencil-21.0.0.tgz} peerDependencies: '@nxext/svelte': '*' peerDependenciesMeta: @@ -4915,598 +4932,608 @@ packages: optional: true '@one-ini/wasm@0.1.1': - resolution: {integrity: sha512-XuySG1E38YScSJoMlqovLru4KTUNSjgVTIjyh7qMX6aNN5HY5Ct5LhRJdxO79JtTzKfzV/bnWpz+zquYrISsvw==} + resolution: {integrity: sha512-XuySG1E38YScSJoMlqovLru4KTUNSjgVTIjyh7qMX6aNN5HY5Ct5LhRJdxO79JtTzKfzV/bnWpz+zquYrISsvw==, tarball: https://registry.npmjs.org/@one-ini/wasm/-/wasm-0.1.1.tgz} + + '@opencode-ai/sdk@1.18.13': + resolution: {integrity: sha512-JY9etiVcu1G/pZjaH2vjK/b8z54ujxaWCD1GziO4ADUhRM6m6zm2332bPGcxEfA6TwweiJfNlK6wVZQ0f/X4KQ==, tarball: https://registry.npmjs.org/@opencode-ai/sdk/-/sdk-1.18.13.tgz} '@openng/spectator@1.0.1': - resolution: {integrity: sha512-YvQofW4P6+rcIK8L7R24G6TG3J635HaU6uSWZyK+zw6JeVHV5eZEGU2yUUWQfn8bPnmR+hCgosrlLnNiKtC6ew==} + resolution: {integrity: sha512-YvQofW4P6+rcIK8L7R24G6TG3J635HaU6uSWZyK+zw6JeVHV5eZEGU2yUUWQfn8bPnmR+hCgosrlLnNiKtC6ew==, tarball: https://registry.npmjs.org/@openng/spectator/-/spectator-1.0.1.tgz} peerDependencies: '@angular/animations': '>= 22.0.0' '@angular/common': '>= 22.0.0' '@angular/router': '>= 22.0.0' + '@openrouter/ai-sdk-provider@2.10.0': + resolution: {integrity: sha512-FMsAEjLUt5pWuRE2LDC/LCvVrFjLlrEzUITH5+5SZtfq7KZ2wrOHjQVxzz92sju8S9ltpzW87CLW8/b0oBXVCw==, tarball: https://registry.npmjs.org/@openrouter/ai-sdk-provider/-/ai-sdk-provider-2.10.0.tgz} + engines: {node: '>=18'} + peerDependencies: + ai: ^6.0.0 + zod: ^3.25.0 || ^4.0.0 + '@opentelemetry/api@1.9.1': - resolution: {integrity: sha512-gLyJlPHPZYdAk1JENA9LeHejZe1Ti77/pTeFm/nMXmQH/HFZlcS/O2XJB+L8fkbrNSqhdtlvjBVjxwUYanNH5Q==} + resolution: {integrity: sha512-gLyJlPHPZYdAk1JENA9LeHejZe1Ti77/pTeFm/nMXmQH/HFZlcS/O2XJB+L8fkbrNSqhdtlvjBVjxwUYanNH5Q==, tarball: https://registry.npmjs.org/@opentelemetry/api/-/api-1.9.1.tgz} engines: {node: '>=8.0.0'} '@oxc-parser/binding-android-arm-eabi@0.142.0': - resolution: {integrity: sha512-ZiRGDutGsv1G6bL/ozy/koC0Sv39T1DqyoC4KD1DOy9ZoACm1O5UWhEK2c02Qdk+4lfLVkvFa/mQ0fm/4h1BtQ==} + resolution: {integrity: sha512-ZiRGDutGsv1G6bL/ozy/koC0Sv39T1DqyoC4KD1DOy9ZoACm1O5UWhEK2c02Qdk+4lfLVkvFa/mQ0fm/4h1BtQ==, tarball: https://registry.npmjs.org/@oxc-parser/binding-android-arm-eabi/-/binding-android-arm-eabi-0.142.0.tgz} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [android] '@oxc-parser/binding-android-arm64@0.142.0': - resolution: {integrity: sha512-WZkvGRLNQTz8lR9zP5nLjUdlroRCopBu3g9zF1p/laE6DzT1UbQo8Rdz5MWhaJUPYg/6gp+jo7HUgsyKaN1FtQ==} + resolution: {integrity: sha512-WZkvGRLNQTz8lR9zP5nLjUdlroRCopBu3g9zF1p/laE6DzT1UbQo8Rdz5MWhaJUPYg/6gp+jo7HUgsyKaN1FtQ==, tarball: https://registry.npmjs.org/@oxc-parser/binding-android-arm64/-/binding-android-arm64-0.142.0.tgz} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [android] '@oxc-parser/binding-darwin-arm64@0.142.0': - resolution: {integrity: sha512-l4khS8LQOOVYsGRVARo1gSaCT/aBSceUVXgtovWc2+drnxVuDr082WA3OCHVdVzIz5JIrP/y9CWsSKxBDNmYGg==} + resolution: {integrity: sha512-l4khS8LQOOVYsGRVARo1gSaCT/aBSceUVXgtovWc2+drnxVuDr082WA3OCHVdVzIz5JIrP/y9CWsSKxBDNmYGg==, tarball: https://registry.npmjs.org/@oxc-parser/binding-darwin-arm64/-/binding-darwin-arm64-0.142.0.tgz} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [darwin] '@oxc-parser/binding-darwin-x64@0.142.0': - resolution: {integrity: sha512-QBsNF3nqlXmcH2B1YOPqQYmCJoy4HuIjUxGbBO/k5JAJUl68ghU2psRY2zPk+RyBaWqKP/qfL4oaFgEMCdwskA==} + resolution: {integrity: sha512-QBsNF3nqlXmcH2B1YOPqQYmCJoy4HuIjUxGbBO/k5JAJUl68ghU2psRY2zPk+RyBaWqKP/qfL4oaFgEMCdwskA==, tarball: https://registry.npmjs.org/@oxc-parser/binding-darwin-x64/-/binding-darwin-x64-0.142.0.tgz} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [darwin] '@oxc-parser/binding-freebsd-x64@0.142.0': - resolution: {integrity: sha512-b7Q7m4Cqc6XqNhri3R+QhU+GVy646Pn+bkdhrDdWym/Fdi0ZUa+d73H9dm5H91JtbtAQ/z1d8XKMW3oOV8a4tQ==} + resolution: {integrity: sha512-b7Q7m4Cqc6XqNhri3R+QhU+GVy646Pn+bkdhrDdWym/Fdi0ZUa+d73H9dm5H91JtbtAQ/z1d8XKMW3oOV8a4tQ==, tarball: https://registry.npmjs.org/@oxc-parser/binding-freebsd-x64/-/binding-freebsd-x64-0.142.0.tgz} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [freebsd] '@oxc-parser/binding-linux-arm-gnueabihf@0.142.0': - resolution: {integrity: sha512-3riVS5IhdH3uCZj1Y9ftDQlR0dvLsIlw/edrRqk8JhgNd5K0XSs+UBtgh50N13CAlW9/TXj6sVGXaKNBocd0Yg==} + resolution: {integrity: sha512-3riVS5IhdH3uCZj1Y9ftDQlR0dvLsIlw/edrRqk8JhgNd5K0XSs+UBtgh50N13CAlW9/TXj6sVGXaKNBocd0Yg==, tarball: https://registry.npmjs.org/@oxc-parser/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-0.142.0.tgz} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [linux] '@oxc-parser/binding-linux-arm-musleabihf@0.142.0': - resolution: {integrity: sha512-NmXUOpgpTSkhl795TiXmWppTwmSJ92RC1qvD6e4XOF+slgmo3e6Ah+kEu+6AN8s7NAOEwqGmir58MgSQSWmBSA==} + resolution: {integrity: sha512-NmXUOpgpTSkhl795TiXmWppTwmSJ92RC1qvD6e4XOF+slgmo3e6Ah+kEu+6AN8s7NAOEwqGmir58MgSQSWmBSA==, tarball: https://registry.npmjs.org/@oxc-parser/binding-linux-arm-musleabihf/-/binding-linux-arm-musleabihf-0.142.0.tgz} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [linux] '@oxc-parser/binding-linux-arm64-gnu@0.142.0': - resolution: {integrity: sha512-gc0EXsKtXgerujmU2Bql3u1L1HsSQ2774R83idq/FoNMPVV/RY/1ErFsvnit7KoiP/sLvzQixeUo4Ut0ic0wmw==} + resolution: {integrity: sha512-gc0EXsKtXgerujmU2Bql3u1L1HsSQ2774R83idq/FoNMPVV/RY/1ErFsvnit7KoiP/sLvzQixeUo4Ut0ic0wmw==, tarball: https://registry.npmjs.org/@oxc-parser/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-0.142.0.tgz} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] '@oxc-parser/binding-linux-arm64-musl@0.142.0': - resolution: {integrity: sha512-F2XvmWSE0uWpie+jHKKIFgdVOe9ypGhkEZxKx5DuW215K6cbAC274yYaPkcM7EqY4Df3Weyhpcz3lsURyH2LVg==} + resolution: {integrity: sha512-F2XvmWSE0uWpie+jHKKIFgdVOe9ypGhkEZxKx5DuW215K6cbAC274yYaPkcM7EqY4Df3Weyhpcz3lsURyH2LVg==, tarball: https://registry.npmjs.org/@oxc-parser/binding-linux-arm64-musl/-/binding-linux-arm64-musl-0.142.0.tgz} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] '@oxc-parser/binding-linux-ppc64-gnu@0.142.0': - resolution: {integrity: sha512-wLMbT21U/QxknQsk+VvNF0b9D2/aGWhcaQQQ+VYlE8FwD5+GoWZIPPXNzyHmkYyhm0KB3itL+TBavjMatqNnYA==} + resolution: {integrity: sha512-wLMbT21U/QxknQsk+VvNF0b9D2/aGWhcaQQQ+VYlE8FwD5+GoWZIPPXNzyHmkYyhm0KB3itL+TBavjMatqNnYA==, tarball: https://registry.npmjs.org/@oxc-parser/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-0.142.0.tgz} engines: {node: ^20.19.0 || >=22.12.0} cpu: [ppc64] os: [linux] '@oxc-parser/binding-linux-riscv64-gnu@0.142.0': - resolution: {integrity: sha512-+G8F/4ckwT7FCJV4H2bt09xEzJbjNCfuL4Sp1AYNaFtFMVtgIGMuJlteT82U+K0UIZ/DzAR/LDlMFnEuajG7Kw==} + resolution: {integrity: sha512-+G8F/4ckwT7FCJV4H2bt09xEzJbjNCfuL4Sp1AYNaFtFMVtgIGMuJlteT82U+K0UIZ/DzAR/LDlMFnEuajG7Kw==, tarball: https://registry.npmjs.org/@oxc-parser/binding-linux-riscv64-gnu/-/binding-linux-riscv64-gnu-0.142.0.tgz} engines: {node: ^20.19.0 || >=22.12.0} cpu: [riscv64] os: [linux] '@oxc-parser/binding-linux-riscv64-musl@0.142.0': - resolution: {integrity: sha512-hTsHtTLxMAfCo+rpF5K3qZJKW2NpPN/CHd4mYB3y7XlSdspHkd2gehDIofP64AacA9nWQw2tY3O7wR6UY8IVOA==} + resolution: {integrity: sha512-hTsHtTLxMAfCo+rpF5K3qZJKW2NpPN/CHd4mYB3y7XlSdspHkd2gehDIofP64AacA9nWQw2tY3O7wR6UY8IVOA==, tarball: https://registry.npmjs.org/@oxc-parser/binding-linux-riscv64-musl/-/binding-linux-riscv64-musl-0.142.0.tgz} engines: {node: ^20.19.0 || >=22.12.0} cpu: [riscv64] os: [linux] '@oxc-parser/binding-linux-s390x-gnu@0.142.0': - resolution: {integrity: sha512-6y7qYY3TCUDYjqswImdTGl92y+KA/80twALegQPN27kfY+bG7Ib1+L3jbmrCZQx6wrVnai9IPsEZp07I0hx7JQ==} + resolution: {integrity: sha512-6y7qYY3TCUDYjqswImdTGl92y+KA/80twALegQPN27kfY+bG7Ib1+L3jbmrCZQx6wrVnai9IPsEZp07I0hx7JQ==, tarball: https://registry.npmjs.org/@oxc-parser/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-0.142.0.tgz} engines: {node: ^20.19.0 || >=22.12.0} cpu: [s390x] os: [linux] '@oxc-parser/binding-linux-x64-gnu@0.142.0': - resolution: {integrity: sha512-i69kAWU+2LgoH5bR+zWiiu+UzAw7Oxkwv7COeJTeY19pn4e70nKQcr9Pm6cL2Z0Z54d+gl9qADlK/0yyuCPiBA==} + resolution: {integrity: sha512-i69kAWU+2LgoH5bR+zWiiu+UzAw7Oxkwv7COeJTeY19pn4e70nKQcr9Pm6cL2Z0Z54d+gl9qADlK/0yyuCPiBA==, tarball: https://registry.npmjs.org/@oxc-parser/binding-linux-x64-gnu/-/binding-linux-x64-gnu-0.142.0.tgz} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] '@oxc-parser/binding-linux-x64-musl@0.142.0': - resolution: {integrity: sha512-4SQs678MmjYVrmhAgCWD4o0vpaFszXw9xLX5p2Z9MMFcltxiLkA88wQjh80YHjPrXtpyZ2CWI5m+1yNKM0m2Pw==} + resolution: {integrity: sha512-4SQs678MmjYVrmhAgCWD4o0vpaFszXw9xLX5p2Z9MMFcltxiLkA88wQjh80YHjPrXtpyZ2CWI5m+1yNKM0m2Pw==, tarball: https://registry.npmjs.org/@oxc-parser/binding-linux-x64-musl/-/binding-linux-x64-musl-0.142.0.tgz} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] '@oxc-parser/binding-openharmony-arm64@0.142.0': - resolution: {integrity: sha512-YHpx9N7Ln3a++Tc8rv+H7mrK1zyJQOAwCFg8LZ3lTs1T5afGWeZrLPhPT9HLnIwSjCyJqPWVMIrMxbjcmBr2oQ==} + resolution: {integrity: sha512-YHpx9N7Ln3a++Tc8rv+H7mrK1zyJQOAwCFg8LZ3lTs1T5afGWeZrLPhPT9HLnIwSjCyJqPWVMIrMxbjcmBr2oQ==, tarball: https://registry.npmjs.org/@oxc-parser/binding-openharmony-arm64/-/binding-openharmony-arm64-0.142.0.tgz} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [openharmony] '@oxc-parser/binding-wasm32-wasi@0.142.0': - resolution: {integrity: sha512-3pLDyY3+oogW73RM5uehNgAiR/Xfb7fvO2Q1Z1gIqZ2+50XDVQmBVlRkHXZTU4gKnQHpwETNsYQVsJ3joVB2iA==} + resolution: {integrity: sha512-3pLDyY3+oogW73RM5uehNgAiR/Xfb7fvO2Q1Z1gIqZ2+50XDVQmBVlRkHXZTU4gKnQHpwETNsYQVsJ3joVB2iA==, tarball: https://registry.npmjs.org/@oxc-parser/binding-wasm32-wasi/-/binding-wasm32-wasi-0.142.0.tgz} engines: {node: ^20.19.0 || >=22.12.0} cpu: [wasm32] '@oxc-parser/binding-win32-arm64-msvc@0.142.0': - resolution: {integrity: sha512-Had/VeVY28Oyb0K+Q4FV8KCzoBycIh93oDK6pCbya9lkzdq+ikMHMgBubsdqqlybjJmQRawCQRrnBRHyQwYvcQ==} + resolution: {integrity: sha512-Had/VeVY28Oyb0K+Q4FV8KCzoBycIh93oDK6pCbya9lkzdq+ikMHMgBubsdqqlybjJmQRawCQRrnBRHyQwYvcQ==, tarball: https://registry.npmjs.org/@oxc-parser/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-0.142.0.tgz} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [win32] '@oxc-parser/binding-win32-ia32-msvc@0.142.0': - resolution: {integrity: sha512-GGi3+YphVHavvgs6gum2UXoNCqzHAmPt/nXkn8ZQZstV2Q1qZD1Mn8fz/nWrDkefHQtrG/+1/XrbMxsBTo6Svw==} + resolution: {integrity: sha512-GGi3+YphVHavvgs6gum2UXoNCqzHAmPt/nXkn8ZQZstV2Q1qZD1Mn8fz/nWrDkefHQtrG/+1/XrbMxsBTo6Svw==, tarball: https://registry.npmjs.org/@oxc-parser/binding-win32-ia32-msvc/-/binding-win32-ia32-msvc-0.142.0.tgz} engines: {node: ^20.19.0 || >=22.12.0} cpu: [ia32] os: [win32] '@oxc-parser/binding-win32-x64-msvc@0.142.0': - resolution: {integrity: sha512-Ny/Wv4Us1LGC/ljwNTp+Hx3r/pH15EFfeDF0p+n898gt+TtRd6C9SccHcuUhDiNTb8s5tt7jdeAMDRQZ4Vq6hg==} + resolution: {integrity: sha512-Ny/Wv4Us1LGC/ljwNTp+Hx3r/pH15EFfeDF0p+n898gt+TtRd6C9SccHcuUhDiNTb8s5tt7jdeAMDRQZ4Vq6hg==, tarball: https://registry.npmjs.org/@oxc-parser/binding-win32-x64-msvc/-/binding-win32-x64-msvc-0.142.0.tgz} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [win32] '@oxc-project/types@0.139.0': - resolution: {integrity: sha512-r9gHphtCs+1M7J0pw6Sn/hh/Wpa/iQrOOkrNAlVLF/gHq+/CJmHIWKKUUhdWjcD6CIa8idarspCsASiXCXvFUw==} + resolution: {integrity: sha512-r9gHphtCs+1M7J0pw6Sn/hh/Wpa/iQrOOkrNAlVLF/gHq+/CJmHIWKKUUhdWjcD6CIa8idarspCsASiXCXvFUw==, tarball: https://registry.npmjs.org/@oxc-project/types/-/types-0.139.0.tgz} '@oxc-project/types@0.140.0': - resolution: {integrity: sha512-h5LUOzGArYemnW1NMz/DuuQhBi96J6JL2Bk8zE4kvqxB5Sg3jxmCiH4uyOWHDkiKSt5vWlG4FIwCR/DbstcNRQ==} + resolution: {integrity: sha512-h5LUOzGArYemnW1NMz/DuuQhBi96J6JL2Bk8zE4kvqxB5Sg3jxmCiH4uyOWHDkiKSt5vWlG4FIwCR/DbstcNRQ==, tarball: https://registry.npmjs.org/@oxc-project/types/-/types-0.140.0.tgz} '@oxc-project/types@0.142.0': - resolution: {integrity: sha512-7W+2q5AKQVU36fkaryontrHn3YDt1RyUYXatw9i5H8ocYe2sPKSFB6eS8WNPeRKiN1qAWWZUPm7gwFzJGrccqQ==} + resolution: {integrity: sha512-7W+2q5AKQVU36fkaryontrHn3YDt1RyUYXatw9i5H8ocYe2sPKSFB6eS8WNPeRKiN1qAWWZUPm7gwFzJGrccqQ==, tarball: https://registry.npmjs.org/@oxc-project/types/-/types-0.142.0.tgz} '@oxc-resolver/binding-android-arm-eabi@11.24.2': - resolution: {integrity: sha512-y09e0L0SRI2OA2tUIrjBgoV3eH5hvUKXNkJqXmNo5V2WxIjyC7I7aJfRLMEVpA8yi95f90gFDvO0VMgrDw+vwA==} + resolution: {integrity: sha512-y09e0L0SRI2OA2tUIrjBgoV3eH5hvUKXNkJqXmNo5V2WxIjyC7I7aJfRLMEVpA8yi95f90gFDvO0VMgrDw+vwA==, tarball: https://registry.npmjs.org/@oxc-resolver/binding-android-arm-eabi/-/binding-android-arm-eabi-11.24.2.tgz} cpu: [arm] os: [android] '@oxc-resolver/binding-android-arm64@11.24.2': - resolution: {integrity: sha512-cl4icWaZFnLdg8m6qtnh5rBMuGbxc/ptStFHLeCNwr+2cZjkjNwQu/jYRS0CHlnPecOJMpuS5M6/BH+0J/YkEg==} + resolution: {integrity: sha512-cl4icWaZFnLdg8m6qtnh5rBMuGbxc/ptStFHLeCNwr+2cZjkjNwQu/jYRS0CHlnPecOJMpuS5M6/BH+0J/YkEg==, tarball: https://registry.npmjs.org/@oxc-resolver/binding-android-arm64/-/binding-android-arm64-11.24.2.tgz} cpu: [arm64] os: [android] '@oxc-resolver/binding-darwin-arm64@11.24.2': - resolution: {integrity: sha512-At29QEMF6HajbQvgY8K6OXnHD1x9rad74xBEfmCB6ZqCGsdq75aK7tOYcTbOanMy8qdIBrfL3SMr3p/lfSlb9w==} + resolution: {integrity: sha512-At29QEMF6HajbQvgY8K6OXnHD1x9rad74xBEfmCB6ZqCGsdq75aK7tOYcTbOanMy8qdIBrfL3SMr3p/lfSlb9w==, tarball: https://registry.npmjs.org/@oxc-resolver/binding-darwin-arm64/-/binding-darwin-arm64-11.24.2.tgz} cpu: [arm64] os: [darwin] '@oxc-resolver/binding-darwin-x64@11.24.2': - resolution: {integrity: sha512-A5Kqr1EUj4oIL5CF4WRssq/o5P0Y11cwoFouMRmQ7YnC/A8V93nv1nb7aSU8HwcgmXropjLNkVTl4MN87cu28Q==} + resolution: {integrity: sha512-A5Kqr1EUj4oIL5CF4WRssq/o5P0Y11cwoFouMRmQ7YnC/A8V93nv1nb7aSU8HwcgmXropjLNkVTl4MN87cu28Q==, tarball: https://registry.npmjs.org/@oxc-resolver/binding-darwin-x64/-/binding-darwin-x64-11.24.2.tgz} cpu: [x64] os: [darwin] '@oxc-resolver/binding-freebsd-x64@11.24.2': - resolution: {integrity: sha512-R5xkRBRRz7ceH/P5Jrc6G7FmdUdgpLYyESFAUDVTNQ9K0sGPxcp4ljiwEwEqsvNcQ4sYbMRrWcHHBCu7ksAJVw==} + resolution: {integrity: sha512-R5xkRBRRz7ceH/P5Jrc6G7FmdUdgpLYyESFAUDVTNQ9K0sGPxcp4ljiwEwEqsvNcQ4sYbMRrWcHHBCu7ksAJVw==, tarball: https://registry.npmjs.org/@oxc-resolver/binding-freebsd-x64/-/binding-freebsd-x64-11.24.2.tgz} cpu: [x64] os: [freebsd] '@oxc-resolver/binding-linux-arm-gnueabihf@11.24.2': - resolution: {integrity: sha512-k/RuYL4L/R58IBn3wT5ma3Wh4k62bp1eYCFRWCmMsasUOqL+H6sW0VGFadEzKWXFFlz+2uIMoeMk9ySSZJHgbg==} + resolution: {integrity: sha512-k/RuYL4L/R58IBn3wT5ma3Wh4k62bp1eYCFRWCmMsasUOqL+H6sW0VGFadEzKWXFFlz+2uIMoeMk9ySSZJHgbg==, tarball: https://registry.npmjs.org/@oxc-resolver/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-11.24.2.tgz} cpu: [arm] os: [linux] '@oxc-resolver/binding-linux-arm-musleabihf@11.24.2': - resolution: {integrity: sha512-bnHAak3ujYfH5pKk4NieFNbvYvernfoQDgwLddbZ3OtMYrem87/qjlA+u+aKG0oZcqSLGCful/6/CEA+aeAgaA==} + resolution: {integrity: sha512-bnHAak3ujYfH5pKk4NieFNbvYvernfoQDgwLddbZ3OtMYrem87/qjlA+u+aKG0oZcqSLGCful/6/CEA+aeAgaA==, tarball: https://registry.npmjs.org/@oxc-resolver/binding-linux-arm-musleabihf/-/binding-linux-arm-musleabihf-11.24.2.tgz} cpu: [arm] os: [linux] '@oxc-resolver/binding-linux-arm64-gnu@11.24.2': - resolution: {integrity: sha512-vDT3KHgzYp47gmtNOqL2VNhCyl5Zv643eyxm//A68J8DeUGXrvD1pZFiaT4jSfe+RInfnn1R2yVHye4enx6RnA==} + resolution: {integrity: sha512-vDT3KHgzYp47gmtNOqL2VNhCyl5Zv643eyxm//A68J8DeUGXrvD1pZFiaT4jSfe+RInfnn1R2yVHye4enx6RnA==, tarball: https://registry.npmjs.org/@oxc-resolver/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-11.24.2.tgz} cpu: [arm64] os: [linux] '@oxc-resolver/binding-linux-arm64-musl@11.24.2': - resolution: {integrity: sha512-+kMlQvbzfyEYtu5FcjE4p+ttBLpKW4d/AsAsuE69BxV6V4twZJeIQZFfD8gh/wqglY0MkPSezWXQH0jBV13MUw==} + resolution: {integrity: sha512-+kMlQvbzfyEYtu5FcjE4p+ttBLpKW4d/AsAsuE69BxV6V4twZJeIQZFfD8gh/wqglY0MkPSezWXQH0jBV13MUw==, tarball: https://registry.npmjs.org/@oxc-resolver/binding-linux-arm64-musl/-/binding-linux-arm64-musl-11.24.2.tgz} cpu: [arm64] os: [linux] '@oxc-resolver/binding-linux-ppc64-gnu@11.24.2': - resolution: {integrity: sha512-shjfMhmZ3gq9fv/w7bi3PnZlgOPG+2QAOFf0BJF0EgBSIGZ6PMLN2zbGEblTUYB/NKVDRyYhE2ff3dJ1QqNPkA==} + resolution: {integrity: sha512-shjfMhmZ3gq9fv/w7bi3PnZlgOPG+2QAOFf0BJF0EgBSIGZ6PMLN2zbGEblTUYB/NKVDRyYhE2ff3dJ1QqNPkA==, tarball: https://registry.npmjs.org/@oxc-resolver/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-11.24.2.tgz} cpu: [ppc64] os: [linux] '@oxc-resolver/binding-linux-riscv64-gnu@11.24.2': - resolution: {integrity: sha512-zGelwFR5oRo+b69k8Lrzun86DyUHzfKN6cnjbR9l7Z7NIRznOE/2ZvPa1IUKqAL2PzAXOdwkfVqNvO1H2RlpAw==} + resolution: {integrity: sha512-zGelwFR5oRo+b69k8Lrzun86DyUHzfKN6cnjbR9l7Z7NIRznOE/2ZvPa1IUKqAL2PzAXOdwkfVqNvO1H2RlpAw==, tarball: https://registry.npmjs.org/@oxc-resolver/binding-linux-riscv64-gnu/-/binding-linux-riscv64-gnu-11.24.2.tgz} cpu: [riscv64] os: [linux] '@oxc-resolver/binding-linux-riscv64-musl@11.24.2': - resolution: {integrity: sha512-qxZ1SWCXJY0eyhAlP6Lmo9F2Nrtx7EkYj9oCgL8apDPCwXwCEDA2U697bbT81JIc2IrVjxO4KX6WU2N+oN9Z4w==} + resolution: {integrity: sha512-qxZ1SWCXJY0eyhAlP6Lmo9F2Nrtx7EkYj9oCgL8apDPCwXwCEDA2U697bbT81JIc2IrVjxO4KX6WU2N+oN9Z4w==, tarball: https://registry.npmjs.org/@oxc-resolver/binding-linux-riscv64-musl/-/binding-linux-riscv64-musl-11.24.2.tgz} cpu: [riscv64] os: [linux] '@oxc-resolver/binding-linux-s390x-gnu@11.24.2': - resolution: {integrity: sha512-sGCecF3cx2DFlH4t/z7ApnOnXqN48p5p5mlHDEnHTAukQa2P+qMVE4CwyWE9W+q/m3QJ7kKfGrIjax31f44oFQ==} + resolution: {integrity: sha512-sGCecF3cx2DFlH4t/z7ApnOnXqN48p5p5mlHDEnHTAukQa2P+qMVE4CwyWE9W+q/m3QJ7kKfGrIjax31f44oFQ==, tarball: https://registry.npmjs.org/@oxc-resolver/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-11.24.2.tgz} cpu: [s390x] os: [linux] '@oxc-resolver/binding-linux-x64-gnu@11.24.2': - resolution: {integrity: sha512-k/VlMMcSzMlahb3/fENM4rTlsJ0s3fFROA0KXPBmKggqmTSaE383sl8F3KCOXPLmVsYfW6hCitMhXCEtNeZxxg==} + resolution: {integrity: sha512-k/VlMMcSzMlahb3/fENM4rTlsJ0s3fFROA0KXPBmKggqmTSaE383sl8F3KCOXPLmVsYfW6hCitMhXCEtNeZxxg==, tarball: https://registry.npmjs.org/@oxc-resolver/binding-linux-x64-gnu/-/binding-linux-x64-gnu-11.24.2.tgz} cpu: [x64] os: [linux] '@oxc-resolver/binding-linux-x64-musl@11.24.2': - resolution: {integrity: sha512-8hbnZyNi97b/8wapYaIF9+t9GmZKBW2vunaOc3h9HGJptH7b7XpvZqOTBSm/MpTjr7H497BlgOaSfLUdhmy2bw==} + resolution: {integrity: sha512-8hbnZyNi97b/8wapYaIF9+t9GmZKBW2vunaOc3h9HGJptH7b7XpvZqOTBSm/MpTjr7H497BlgOaSfLUdhmy2bw==, tarball: https://registry.npmjs.org/@oxc-resolver/binding-linux-x64-musl/-/binding-linux-x64-musl-11.24.2.tgz} cpu: [x64] os: [linux] '@oxc-resolver/binding-openharmony-arm64@11.24.2': - resolution: {integrity: sha512-MvyGik3a6pVgZ0t/kWlbmFxFLmXQJwgLsY2eYFHLpy0wGwRbfzeIGgDwQ3kXqE30z+kSXennRkCrT7TUvkptNg==} + resolution: {integrity: sha512-MvyGik3a6pVgZ0t/kWlbmFxFLmXQJwgLsY2eYFHLpy0wGwRbfzeIGgDwQ3kXqE30z+kSXennRkCrT7TUvkptNg==, tarball: https://registry.npmjs.org/@oxc-resolver/binding-openharmony-arm64/-/binding-openharmony-arm64-11.24.2.tgz} cpu: [arm64] os: [openharmony] '@oxc-resolver/binding-wasm32-wasi@11.24.2': - resolution: {integrity: sha512-vHcssMPwO08RTvj/c0iOBz90attxyG3wQJ0dTcyEQK43LRpcdLWZlV5feBhv6Isn6ahbQIzHbCgfa81+RiML0Q==} + resolution: {integrity: sha512-vHcssMPwO08RTvj/c0iOBz90attxyG3wQJ0dTcyEQK43LRpcdLWZlV5feBhv6Isn6ahbQIzHbCgfa81+RiML0Q==, tarball: https://registry.npmjs.org/@oxc-resolver/binding-wasm32-wasi/-/binding-wasm32-wasi-11.24.2.tgz} engines: {node: '>=14.0.0'} cpu: [wasm32] '@oxc-resolver/binding-win32-arm64-msvc@11.24.2': - resolution: {integrity: sha512-uokJqro2iBqkFvJdKQLP7d8/BUmFwESQFVmIJUQKj1Xn1a/LysJoe1vmeECLF5b3jsV8CAL5sEMJXX6SdK9Nhg==} + resolution: {integrity: sha512-uokJqro2iBqkFvJdKQLP7d8/BUmFwESQFVmIJUQKj1Xn1a/LysJoe1vmeECLF5b3jsV8CAL5sEMJXX6SdK9Nhg==, tarball: https://registry.npmjs.org/@oxc-resolver/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-11.24.2.tgz} cpu: [arm64] os: [win32] '@oxc-resolver/binding-win32-x64-msvc@11.24.2': - resolution: {integrity: sha512-UqGPmo56KDfLlfXFAFIrNflHT8tFxWGEivWg3Zeyp4Uy2NlKN1FGPr6/BxcLGG3+kZ6Wp14g5Uj+n71boqZfiw==} + resolution: {integrity: sha512-UqGPmo56KDfLlfXFAFIrNflHT8tFxWGEivWg3Zeyp4Uy2NlKN1FGPr6/BxcLGG3+kZ6Wp14g5Uj+n71boqZfiw==, tarball: https://registry.npmjs.org/@oxc-resolver/binding-win32-x64-msvc/-/binding-win32-x64-msvc-11.24.2.tgz} cpu: [x64] os: [win32] '@parcel/watcher-android-arm64@2.5.6': - resolution: {integrity: sha512-YQxSS34tPF/6ZG7r/Ih9xy+kP/WwediEUsqmtf0cuCV5TPPKw/PQHRhueUo6JdeFJaqV3pyjm0GdYjZotbRt/A==} + resolution: {integrity: sha512-YQxSS34tPF/6ZG7r/Ih9xy+kP/WwediEUsqmtf0cuCV5TPPKw/PQHRhueUo6JdeFJaqV3pyjm0GdYjZotbRt/A==, tarball: https://registry.npmjs.org/@parcel/watcher-android-arm64/-/watcher-android-arm64-2.5.6.tgz} engines: {node: '>= 10.0.0'} cpu: [arm64] os: [android] '@parcel/watcher-darwin-arm64@2.5.6': - resolution: {integrity: sha512-Z2ZdrnwyXvvvdtRHLmM4knydIdU9adO3D4n/0cVipF3rRiwP+3/sfzpAwA/qKFL6i1ModaabkU7IbpeMBgiVEA==} + resolution: {integrity: sha512-Z2ZdrnwyXvvvdtRHLmM4knydIdU9adO3D4n/0cVipF3rRiwP+3/sfzpAwA/qKFL6i1ModaabkU7IbpeMBgiVEA==, tarball: https://registry.npmjs.org/@parcel/watcher-darwin-arm64/-/watcher-darwin-arm64-2.5.6.tgz} engines: {node: '>= 10.0.0'} cpu: [arm64] os: [darwin] '@parcel/watcher-darwin-x64@2.5.6': - resolution: {integrity: sha512-HgvOf3W9dhithcwOWX9uDZyn1lW9R+7tPZ4sug+NGrGIo4Rk1hAXLEbcH1TQSqxts0NYXXlOWqVpvS1SFS4fRg==} + resolution: {integrity: sha512-HgvOf3W9dhithcwOWX9uDZyn1lW9R+7tPZ4sug+NGrGIo4Rk1hAXLEbcH1TQSqxts0NYXXlOWqVpvS1SFS4fRg==, tarball: https://registry.npmjs.org/@parcel/watcher-darwin-x64/-/watcher-darwin-x64-2.5.6.tgz} engines: {node: '>= 10.0.0'} cpu: [x64] os: [darwin] '@parcel/watcher-freebsd-x64@2.5.6': - resolution: {integrity: sha512-vJVi8yd/qzJxEKHkeemh7w3YAn6RJCtYlE4HPMoVnCpIXEzSrxErBW5SJBgKLbXU3WdIpkjBTeUNtyBVn8TRng==} + resolution: {integrity: sha512-vJVi8yd/qzJxEKHkeemh7w3YAn6RJCtYlE4HPMoVnCpIXEzSrxErBW5SJBgKLbXU3WdIpkjBTeUNtyBVn8TRng==, tarball: https://registry.npmjs.org/@parcel/watcher-freebsd-x64/-/watcher-freebsd-x64-2.5.6.tgz} engines: {node: '>= 10.0.0'} cpu: [x64] os: [freebsd] '@parcel/watcher-linux-arm-glibc@2.5.6': - resolution: {integrity: sha512-9JiYfB6h6BgV50CCfasfLf/uvOcJskMSwcdH1PHH9rvS1IrNy8zad6IUVPVUfmXr+u+Km9IxcfMLzgdOudz9EQ==} + resolution: {integrity: sha512-9JiYfB6h6BgV50CCfasfLf/uvOcJskMSwcdH1PHH9rvS1IrNy8zad6IUVPVUfmXr+u+Km9IxcfMLzgdOudz9EQ==, tarball: https://registry.npmjs.org/@parcel/watcher-linux-arm-glibc/-/watcher-linux-arm-glibc-2.5.6.tgz} engines: {node: '>= 10.0.0'} cpu: [arm] os: [linux] '@parcel/watcher-linux-arm-musl@2.5.6': - resolution: {integrity: sha512-Ve3gUCG57nuUUSyjBq/MAM0CzArtuIOxsBdQ+ftz6ho8n7s1i9E1Nmk/xmP323r2YL0SONs1EuwqBp2u1k5fxg==} + resolution: {integrity: sha512-Ve3gUCG57nuUUSyjBq/MAM0CzArtuIOxsBdQ+ftz6ho8n7s1i9E1Nmk/xmP323r2YL0SONs1EuwqBp2u1k5fxg==, tarball: https://registry.npmjs.org/@parcel/watcher-linux-arm-musl/-/watcher-linux-arm-musl-2.5.6.tgz} engines: {node: '>= 10.0.0'} cpu: [arm] os: [linux] '@parcel/watcher-linux-arm64-glibc@2.5.6': - resolution: {integrity: sha512-f2g/DT3NhGPdBmMWYoxixqYr3v/UXcmLOYy16Bx0TM20Tchduwr4EaCbmxh1321TABqPGDpS8D/ggOTaljijOA==} + resolution: {integrity: sha512-f2g/DT3NhGPdBmMWYoxixqYr3v/UXcmLOYy16Bx0TM20Tchduwr4EaCbmxh1321TABqPGDpS8D/ggOTaljijOA==, tarball: https://registry.npmjs.org/@parcel/watcher-linux-arm64-glibc/-/watcher-linux-arm64-glibc-2.5.6.tgz} engines: {node: '>= 10.0.0'} cpu: [arm64] os: [linux] '@parcel/watcher-linux-arm64-musl@2.5.6': - resolution: {integrity: sha512-qb6naMDGlbCwdhLj6hgoVKJl2odL34z2sqkC7Z6kzir8b5W65WYDpLB6R06KabvZdgoHI/zxke4b3zR0wAbDTA==} + resolution: {integrity: sha512-qb6naMDGlbCwdhLj6hgoVKJl2odL34z2sqkC7Z6kzir8b5W65WYDpLB6R06KabvZdgoHI/zxke4b3zR0wAbDTA==, tarball: https://registry.npmjs.org/@parcel/watcher-linux-arm64-musl/-/watcher-linux-arm64-musl-2.5.6.tgz} engines: {node: '>= 10.0.0'} cpu: [arm64] os: [linux] '@parcel/watcher-linux-x64-glibc@2.5.6': - resolution: {integrity: sha512-kbT5wvNQlx7NaGjzPFu8nVIW1rWqV780O7ZtkjuWaPUgpv2NMFpjYERVi0UYj1msZNyCzGlaCWEtzc+exjMGbQ==} + resolution: {integrity: sha512-kbT5wvNQlx7NaGjzPFu8nVIW1rWqV780O7ZtkjuWaPUgpv2NMFpjYERVi0UYj1msZNyCzGlaCWEtzc+exjMGbQ==, tarball: https://registry.npmjs.org/@parcel/watcher-linux-x64-glibc/-/watcher-linux-x64-glibc-2.5.6.tgz} 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==} + resolution: {integrity: sha512-1JRFeC+h7RdXwldHzTsmdtYR/Ku8SylLgTU/reMuqdVD7CtLwf0VR1FqeprZ0eHQkO0vqsbvFLXUmYm/uNKJBg==, tarball: https://registry.npmjs.org/@parcel/watcher-linux-x64-musl/-/watcher-linux-x64-musl-2.5.6.tgz} engines: {node: '>= 10.0.0'} cpu: [x64] os: [linux] '@parcel/watcher-win32-arm64@2.5.6': - resolution: {integrity: sha512-3ukyebjc6eGlw9yRt678DxVF7rjXatWiHvTXqphZLvo7aC5NdEgFufVwjFfY51ijYEWpXbqF5jtrK275z52D4Q==} + resolution: {integrity: sha512-3ukyebjc6eGlw9yRt678DxVF7rjXatWiHvTXqphZLvo7aC5NdEgFufVwjFfY51ijYEWpXbqF5jtrK275z52D4Q==, tarball: https://registry.npmjs.org/@parcel/watcher-win32-arm64/-/watcher-win32-arm64-2.5.6.tgz} engines: {node: '>= 10.0.0'} cpu: [arm64] os: [win32] '@parcel/watcher-win32-ia32@2.5.6': - resolution: {integrity: sha512-k35yLp1ZMwwee3Ez/pxBi5cf4AoBKYXj00CZ80jUz5h8prpiaQsiRPKQMxoLstNuqe2vR4RNPEAEcjEFzhEz/g==} + resolution: {integrity: sha512-k35yLp1ZMwwee3Ez/pxBi5cf4AoBKYXj00CZ80jUz5h8prpiaQsiRPKQMxoLstNuqe2vR4RNPEAEcjEFzhEz/g==, tarball: https://registry.npmjs.org/@parcel/watcher-win32-ia32/-/watcher-win32-ia32-2.5.6.tgz} engines: {node: '>= 10.0.0'} cpu: [ia32] os: [win32] '@parcel/watcher-win32-x64@2.5.6': - resolution: {integrity: sha512-hbQlYcCq5dlAX9Qx+kFb0FHue6vbjlf0FrNzSKdYK2APUf7tGfGxQCk2ihEREmbR6ZMc0MVAD5RIX/41gpUzTw==} + resolution: {integrity: sha512-hbQlYcCq5dlAX9Qx+kFb0FHue6vbjlf0FrNzSKdYK2APUf7tGfGxQCk2ihEREmbR6ZMc0MVAD5RIX/41gpUzTw==, tarball: https://registry.npmjs.org/@parcel/watcher-win32-x64/-/watcher-win32-x64-2.5.6.tgz} engines: {node: '>= 10.0.0'} cpu: [x64] os: [win32] '@parcel/watcher@2.5.6': - resolution: {integrity: sha512-tmmZ3lQxAe/k/+rNnXQRawJ4NjxO2hqiOLTHvWchtGZULp4RyFeh6aU4XdOYBFe2KE1oShQTv4AblOs2iOrNnQ==} + resolution: {integrity: sha512-tmmZ3lQxAe/k/+rNnXQRawJ4NjxO2hqiOLTHvWchtGZULp4RyFeh6aU4XdOYBFe2KE1oShQTv4AblOs2iOrNnQ==, tarball: https://registry.npmjs.org/@parcel/watcher/-/watcher-2.5.6.tgz} engines: {node: '>= 10.0.0'} '@peculiar/asn1-cms@2.8.0': - resolution: {integrity: sha512-NgekZOrSJFSBFLFoLfwePguAWAx7z1+f2TEsWFUMyiqqfntZ4+S/S5hzqME3q4pCA0iOsFKdwiQ35dwY24eVqA==} + resolution: {integrity: sha512-NgekZOrSJFSBFLFoLfwePguAWAx7z1+f2TEsWFUMyiqqfntZ4+S/S5hzqME3q4pCA0iOsFKdwiQ35dwY24eVqA==, tarball: https://registry.npmjs.org/@peculiar/asn1-cms/-/asn1-cms-2.8.0.tgz} '@peculiar/asn1-csr@2.8.0': - resolution: {integrity: sha512-akbF8+uvleHs8sejNPQxwmVFuInAg6FMNHOwMILXfP518YfFJwdR3jr6oNUPOaEJfuEhn/vkNOCIT6ASUd4mbg==} + resolution: {integrity: sha512-akbF8+uvleHs8sejNPQxwmVFuInAg6FMNHOwMILXfP518YfFJwdR3jr6oNUPOaEJfuEhn/vkNOCIT6ASUd4mbg==, tarball: https://registry.npmjs.org/@peculiar/asn1-csr/-/asn1-csr-2.8.0.tgz} '@peculiar/asn1-ecc@2.8.0': - resolution: {integrity: sha512-ohwlk+u9Rv2NOAY1c6MfHj45ATVF8R1DUN/WCgABiRtLi2ZftlZWZX7KvpAbU8v9xPcmoILfELeEABj/rn18AQ==} + resolution: {integrity: sha512-ohwlk+u9Rv2NOAY1c6MfHj45ATVF8R1DUN/WCgABiRtLi2ZftlZWZX7KvpAbU8v9xPcmoILfELeEABj/rn18AQ==, tarball: https://registry.npmjs.org/@peculiar/asn1-ecc/-/asn1-ecc-2.8.0.tgz} '@peculiar/asn1-pfx@2.8.0': - resolution: {integrity: sha512-5yof1ytoB++RQtaFbqSUJ8pxDJtZT6vbVqZ8XoJ61ph7UjNVvfFwAilnCodqkNsAodpy13gDhoxZXw00pghnyg==} + resolution: {integrity: sha512-5yof1ytoB++RQtaFbqSUJ8pxDJtZT6vbVqZ8XoJ61ph7UjNVvfFwAilnCodqkNsAodpy13gDhoxZXw00pghnyg==, tarball: https://registry.npmjs.org/@peculiar/asn1-pfx/-/asn1-pfx-2.8.0.tgz} '@peculiar/asn1-pkcs8@2.8.0': - resolution: {integrity: sha512-qAKXtLpBEw9LqhKpjw3ajZSXlBur+ipW+y2ivVBQAG6F6qRx94yO+1ZR4mvw+YaCfKSaOzLeYEzsPaBp4SJELA==} + resolution: {integrity: sha512-qAKXtLpBEw9LqhKpjw3ajZSXlBur+ipW+y2ivVBQAG6F6qRx94yO+1ZR4mvw+YaCfKSaOzLeYEzsPaBp4SJELA==, tarball: https://registry.npmjs.org/@peculiar/asn1-pkcs8/-/asn1-pkcs8-2.8.0.tgz} '@peculiar/asn1-pkcs9@2.8.0': - resolution: {integrity: sha512-b5nDWCnkV60+cQ141D6sVVwK9nz64R5n3zSVnklGd+ECdkW2Ol3U1a6yYFlalpSOaD557yuJB64A+q42jG7lUQ==} + resolution: {integrity: sha512-b5nDWCnkV60+cQ141D6sVVwK9nz64R5n3zSVnklGd+ECdkW2Ol3U1a6yYFlalpSOaD557yuJB64A+q42jG7lUQ==, tarball: https://registry.npmjs.org/@peculiar/asn1-pkcs9/-/asn1-pkcs9-2.8.0.tgz} '@peculiar/asn1-rsa@2.8.0': - resolution: {integrity: sha512-zHEUlCqB2mk7x2lxDwHHJy7hWZOPdGHVlsmITWKB5/PbQo61atbu9PJ/0r9dQNMwFzbKPXZ8uK8/91eUhRznSg==} + resolution: {integrity: sha512-zHEUlCqB2mk7x2lxDwHHJy7hWZOPdGHVlsmITWKB5/PbQo61atbu9PJ/0r9dQNMwFzbKPXZ8uK8/91eUhRznSg==, tarball: https://registry.npmjs.org/@peculiar/asn1-rsa/-/asn1-rsa-2.8.0.tgz} '@peculiar/asn1-schema@2.8.0': - resolution: {integrity: sha512-7YT0U/ze0tF2QOBbE15gKZwy5tvgGyLRiRHLzhlbOpf7BT032oBSd0haZqXn5W6l26WLlu3dyxzjM+2638/z2Q==} + resolution: {integrity: sha512-7YT0U/ze0tF2QOBbE15gKZwy5tvgGyLRiRHLzhlbOpf7BT032oBSd0haZqXn5W6l26WLlu3dyxzjM+2638/z2Q==, tarball: https://registry.npmjs.org/@peculiar/asn1-schema/-/asn1-schema-2.8.0.tgz} '@peculiar/asn1-x509-attr@2.8.0': - resolution: {integrity: sha512-tHjkfS/qhMnmrlB2J9NhflQlQ7In3khO3CfmVrriOlpTeErY9ZIKOso1hQ5JQiyrJ7ShvqVPk7E5fQmbclkSKA==} + resolution: {integrity: sha512-tHjkfS/qhMnmrlB2J9NhflQlQ7In3khO3CfmVrriOlpTeErY9ZIKOso1hQ5JQiyrJ7ShvqVPk7E5fQmbclkSKA==, tarball: https://registry.npmjs.org/@peculiar/asn1-x509-attr/-/asn1-x509-attr-2.8.0.tgz} '@peculiar/asn1-x509@2.8.0': - resolution: {integrity: sha512-N0CMuhWUzsWEVq6F1q9X6+VKUnWzSW+cSVg+aPaGGwDdbFoFWTYgin5MHwXgpWd6y9COMBxnfy/Qc+Xc7F0Zwg==} + resolution: {integrity: sha512-N0CMuhWUzsWEVq6F1q9X6+VKUnWzSW+cSVg+aPaGGwDdbFoFWTYgin5MHwXgpWd6y9COMBxnfy/Qc+Xc7F0Zwg==, tarball: https://registry.npmjs.org/@peculiar/asn1-x509/-/asn1-x509-2.8.0.tgz} '@peculiar/utils@2.0.3': - resolution: {integrity: sha512-+oL3HPFRIZ1St2K50lWCXiioIgSoxzz7R1J3uF6neO2yl1sgmpgY6XXJH4BdpoDkMWznQTeYF6oWNDZLCdQ4eQ==} + resolution: {integrity: sha512-+oL3HPFRIZ1St2K50lWCXiioIgSoxzz7R1J3uF6neO2yl1sgmpgY6XXJH4BdpoDkMWznQTeYF6oWNDZLCdQ4eQ==, tarball: https://registry.npmjs.org/@peculiar/utils/-/utils-2.0.3.tgz} '@peculiar/x509@1.14.3': - resolution: {integrity: sha512-C2Xj8FZ0uHWeCXXqX5B4/gVFQmtSkiuOolzAgutjTfseNOHT3pUjljDZsTSxXFGgio54bCzVFqmEOUrIVk8RDA==} + resolution: {integrity: sha512-C2Xj8FZ0uHWeCXXqX5B4/gVFQmtSkiuOolzAgutjTfseNOHT3pUjljDZsTSxXFGgio54bCzVFqmEOUrIVk8RDA==, tarball: https://registry.npmjs.org/@peculiar/x509/-/x509-1.14.3.tgz} engines: {node: '>=20.0.0'} '@phenomnomnominal/tsquery@5.0.1': - resolution: {integrity: sha512-3nVv+e2FQwsW8Aw6qTU6f+1rfcJ3hrcnvH/mu9i8YhxO+9sqbOfpL8m6PbET5+xKOlz/VSbp0RoYWYCtIsnmuA==} + resolution: {integrity: sha512-3nVv+e2FQwsW8Aw6qTU6f+1rfcJ3hrcnvH/mu9i8YhxO+9sqbOfpL8m6PbET5+xKOlz/VSbp0RoYWYCtIsnmuA==, tarball: https://registry.npmjs.org/@phenomnomnominal/tsquery/-/tsquery-5.0.1.tgz} peerDependencies: typescript: ^3 || ^4 || ^5 '@phenomnomnominal/tsquery@6.2.0': - resolution: {integrity: sha512-Vo9nkhfZxDB/sBiqIY3pjDC4mOSyure+AFlEW5hcy/tRE82MqCXjRN4InnVNMldinRt0dLYqg4HAU2XPq5e1LA==} + resolution: {integrity: sha512-Vo9nkhfZxDB/sBiqIY3pjDC4mOSyure+AFlEW5hcy/tRE82MqCXjRN4InnVNMldinRt0dLYqg4HAU2XPq5e1LA==, tarball: https://registry.npmjs.org/@phenomnomnominal/tsquery/-/tsquery-6.2.0.tgz} peerDependencies: typescript: '>3.0.0' '@pkgjs/parseargs@0.11.0': - resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} + resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==, tarball: https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz} engines: {node: '>=14'} '@pkgr/core@0.3.6': - resolution: {integrity: sha512-SEeaJLb3qBNF/OaXnaR1NmmBbFYk1zC0ZH/52fATcRPLFg/p791YrcyFFy44Bo9sLaGuSuLp5Q6axbb/O+v/RA==} + resolution: {integrity: sha512-SEeaJLb3qBNF/OaXnaR1NmmBbFYk1zC0ZH/52fATcRPLFg/p791YrcyFFy44Bo9sLaGuSuLp5Q6axbb/O+v/RA==, tarball: https://registry.npmjs.org/@pkgr/core/-/core-0.3.6.tgz} engines: {node: ^14.18.0 || >=16.0.0} '@playwright/test@1.36.0': - resolution: {integrity: sha512-yN+fvMYtiyLFDCQos+lWzoX4XW3DNuaxjBu68G0lkgLgC6BP+m/iTxJQoSicz/x2G5EsrqlZTqTIP9sTgLQerg==} + resolution: {integrity: sha512-yN+fvMYtiyLFDCQos+lWzoX4XW3DNuaxjBu68G0lkgLgC6BP+m/iTxJQoSicz/x2G5EsrqlZTqTIP9sTgLQerg==, tarball: https://registry.npmjs.org/@playwright/test/-/test-1.36.0.tgz} engines: {node: '>=16'} deprecated: Please update to the latest version of Playwright to test up-to-date browsers. hasBin: true '@polka/url@1.0.0-next.29': - resolution: {integrity: sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww==} + resolution: {integrity: sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww==, tarball: https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.29.tgz} '@popperjs/core@2.11.8': - resolution: {integrity: sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==} + resolution: {integrity: sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==, tarball: https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz} '@primeuix/motion@0.0.10': - resolution: {integrity: sha512-PsZwOPq79Scp7/ionshRcQ5xKVf9+zuLcyY5mf6onK8chHT5C9JGphmcIZ4CzcqxuGEpsm8AIbTGy+zS3RtzLA==} + resolution: {integrity: sha512-PsZwOPq79Scp7/ionshRcQ5xKVf9+zuLcyY5mf6onK8chHT5C9JGphmcIZ4CzcqxuGEpsm8AIbTGy+zS3RtzLA==, tarball: https://registry.npmjs.org/@primeuix/motion/-/motion-0.0.10.tgz} engines: {node: '>=12.11.0'} '@primeuix/styled@0.7.4': - resolution: {integrity: sha512-QSO/NpOQg8e9BONWRBx9y8VGMCMYz0J/uKfNJEya/RGEu7ARx0oYW0ugI1N3/KB1AAvyGxzKBzGImbwg0KUiOQ==} + resolution: {integrity: sha512-QSO/NpOQg8e9BONWRBx9y8VGMCMYz0J/uKfNJEya/RGEu7ARx0oYW0ugI1N3/KB1AAvyGxzKBzGImbwg0KUiOQ==, tarball: https://registry.npmjs.org/@primeuix/styled/-/styled-0.7.4.tgz} engines: {node: '>=12.11.0'} '@primeuix/styles@2.0.3': - resolution: {integrity: sha512-2ykAB6BaHzR/6TwF8ShpJTsZrid6cVIEBVlookSdvOdmlWuevGu5vWOScgIwqWwlZcvkFYAGR/SUV3OHCTBMdw==} + resolution: {integrity: sha512-2ykAB6BaHzR/6TwF8ShpJTsZrid6cVIEBVlookSdvOdmlWuevGu5vWOScgIwqWwlZcvkFYAGR/SUV3OHCTBMdw==, tarball: https://registry.npmjs.org/@primeuix/styles/-/styles-2.0.3.tgz} '@primeuix/themes@2.0.3': - resolution: {integrity: sha512-3fS1883mtCWhgUgNf/feiaaDSOND4EBIOu9tZnzJlJ8QtYyL6eFLcA6V3ymCWqLVXQ1+lTVEZv1gl47FIdXReg==} + resolution: {integrity: sha512-3fS1883mtCWhgUgNf/feiaaDSOND4EBIOu9tZnzJlJ8QtYyL6eFLcA6V3ymCWqLVXQ1+lTVEZv1gl47FIdXReg==, tarball: https://registry.npmjs.org/@primeuix/themes/-/themes-2.0.3.tgz} '@primeuix/utils@0.6.4': - resolution: {integrity: sha512-pZ5f+vj7wSzRhC7KoEQRU5fvYAe+RP9+m39CTscZ3UywCD1Y2o6Fe1rRgklMPSkzUcty2jzkA0zMYkiJBD1hgg==} + resolution: {integrity: sha512-pZ5f+vj7wSzRhC7KoEQRU5fvYAe+RP9+m39CTscZ3UywCD1Y2o6Fe1rRgklMPSkzUcty2jzkA0zMYkiJBD1hgg==, tarball: https://registry.npmjs.org/@primeuix/utils/-/utils-0.6.4.tgz} engines: {node: '>=12.11.0'} '@remirror/core-constants@3.0.0': - resolution: {integrity: sha512-42aWfPrimMfDKDi4YegyS7x+/0tlzaqwPQCULLanv3DMIlu96KTJR0fM5isWX2UViOqlGnX6YFgqWepcX+XMNg==} + resolution: {integrity: sha512-42aWfPrimMfDKDi4YegyS7x+/0tlzaqwPQCULLanv3DMIlu96KTJR0fM5isWX2UViOqlGnX6YFgqWepcX+XMNg==, tarball: https://registry.npmjs.org/@remirror/core-constants/-/core-constants-3.0.0.tgz} '@rolldown/binding-android-arm64@1.1.5': - resolution: {integrity: sha512-lZg8fqIv2v7FF237bwMgzGZEJvGL79/s5knJ/i6FmsGF4XXlzccZ4jb+TrFIxtSSxFtIpdsgrPZeMk1I9AFcyQ==} + resolution: {integrity: sha512-lZg8fqIv2v7FF237bwMgzGZEJvGL79/s5knJ/i6FmsGF4XXlzccZ4jb+TrFIxtSSxFtIpdsgrPZeMk1I9AFcyQ==, tarball: https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.1.5.tgz} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [android] '@rolldown/binding-android-arm64@1.2.0': - resolution: {integrity: sha512-9yB1l95IrJuNGDFdOYe79vdApdz6WWBCObE+rQ2LUliYUlcyFwSYIb2xb5/Ifw7dAtMy2ZqNyd8QTSOc7duAKw==} + resolution: {integrity: sha512-9yB1l95IrJuNGDFdOYe79vdApdz6WWBCObE+rQ2LUliYUlcyFwSYIb2xb5/Ifw7dAtMy2ZqNyd8QTSOc7duAKw==, tarball: https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.2.0.tgz} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [android] '@rolldown/binding-darwin-arm64@1.1.5': - resolution: {integrity: sha512-51Bnx9pNiMRKSUNtBfySkNJ9vMU9Hh3I1ozDd6gyPPYzaXCfnptUcEZxXGYFn+ul2dtcMUiqGR1Yai2K10uoTw==} + resolution: {integrity: sha512-51Bnx9pNiMRKSUNtBfySkNJ9vMU9Hh3I1ozDd6gyPPYzaXCfnptUcEZxXGYFn+ul2dtcMUiqGR1Yai2K10uoTw==, tarball: https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.1.5.tgz} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [darwin] '@rolldown/binding-darwin-arm64@1.2.0': - resolution: {integrity: sha512-pexNaW9ACLUOaBITOpU6qVu4VrsOFIjTv6bzgu0YUATo4eUJx0V605PxwZfndpPOn0ilqGqvGQ0M8UW0IE24jg==} + resolution: {integrity: sha512-pexNaW9ACLUOaBITOpU6qVu4VrsOFIjTv6bzgu0YUATo4eUJx0V605PxwZfndpPOn0ilqGqvGQ0M8UW0IE24jg==, tarball: https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.2.0.tgz} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [darwin] '@rolldown/binding-darwin-x64@1.1.5': - resolution: {integrity: sha512-Tm+gbfC0aHu1tBA/JvKQh32S0K6YgCHkiAF4/W6xX0K0RmNuc94VeK419dJoE65R5aRxmo+noZQSWrAMF6yb6g==} + resolution: {integrity: sha512-Tm+gbfC0aHu1tBA/JvKQh32S0K6YgCHkiAF4/W6xX0K0RmNuc94VeK419dJoE65R5aRxmo+noZQSWrAMF6yb6g==, tarball: https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.1.5.tgz} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [darwin] '@rolldown/binding-darwin-x64@1.2.0': - resolution: {integrity: sha512-NqKYaq0355ZmNMG4QGpxtEDxsc7tGDhjhCm4PpE0cwnBW+5Il95LJyq414niEiaKLVjnVHBEjSo1wngKxJNiFw==} + resolution: {integrity: sha512-NqKYaq0355ZmNMG4QGpxtEDxsc7tGDhjhCm4PpE0cwnBW+5Il95LJyq414niEiaKLVjnVHBEjSo1wngKxJNiFw==, tarball: https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.2.0.tgz} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [darwin] '@rolldown/binding-freebsd-x64@1.1.5': - resolution: {integrity: sha512-JMzDKCCXq93YccG5gz3hvOs1oXRKAf0XYpfOS88e+wZrC8Iugj6j68867vrYZkvpDDpKn/KoKORThmchMpF6TA==} + resolution: {integrity: sha512-JMzDKCCXq93YccG5gz3hvOs1oXRKAf0XYpfOS88e+wZrC8Iugj6j68867vrYZkvpDDpKn/KoKORThmchMpF6TA==, tarball: https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.1.5.tgz} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [freebsd] '@rolldown/binding-freebsd-x64@1.2.0': - resolution: {integrity: sha512-3vPoHzh6eBTz9IbB0/qZdSr0Qeks2echn+I4cHu2joV74VriPDdldswksEDzrl1mBB+oPRi+67+3Ib59paxIPQ==} + resolution: {integrity: sha512-3vPoHzh6eBTz9IbB0/qZdSr0Qeks2echn+I4cHu2joV74VriPDdldswksEDzrl1mBB+oPRi+67+3Ib59paxIPQ==, tarball: https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.2.0.tgz} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [freebsd] '@rolldown/binding-linux-arm-gnueabihf@1.1.5': - resolution: {integrity: sha512-uML21j2K5TfPGutKxub+M+nLjZIrWjXQ5Grx4lCe/nimTj9B4L63zHpjXLl4y0L3mcm2htEQIb06oCG/szerNw==} + resolution: {integrity: sha512-uML21j2K5TfPGutKxub+M+nLjZIrWjXQ5Grx4lCe/nimTj9B4L63zHpjXLl4y0L3mcm2htEQIb06oCG/szerNw==, tarball: https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.1.5.tgz} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [linux] '@rolldown/binding-linux-arm-gnueabihf@1.2.0': - resolution: {integrity: sha512-E6NNefZ1bUVmKJq2tJkf45J4Zyczj7qm9rUT7NY+Xo2474Y13qWAwc2tvBt0BAVbmtXR1llkxXg0Ou1jbDf2SQ==} + resolution: {integrity: sha512-E6NNefZ1bUVmKJq2tJkf45J4Zyczj7qm9rUT7NY+Xo2474Y13qWAwc2tvBt0BAVbmtXR1llkxXg0Ou1jbDf2SQ==, tarball: https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.2.0.tgz} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [linux] '@rolldown/binding-linux-arm64-gnu@1.1.5': - resolution: {integrity: sha512-navSiuTMogvnQoZoM/v+l3ZWo50/NTwSHSzheABx/RCnmUPaKwq9qSo4Br2OYRs21+Fz8uFqITZM3H4opOB0/Q==} + resolution: {integrity: sha512-navSiuTMogvnQoZoM/v+l3ZWo50/NTwSHSzheABx/RCnmUPaKwq9qSo4Br2OYRs21+Fz8uFqITZM3H4opOB0/Q==, tarball: https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.1.5.tgz} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] '@rolldown/binding-linux-arm64-gnu@1.2.0': - resolution: {integrity: sha512-D+TgkdgM1vu+7/Fpf8+v0ARW+RXEP9Ccazgm8zQ4JFFd9Q7SrYQ2TakU5S5ihazQDgpKyAgZDOcIFsvoHmTZ8w==} + resolution: {integrity: sha512-D+TgkdgM1vu+7/Fpf8+v0ARW+RXEP9Ccazgm8zQ4JFFd9Q7SrYQ2TakU5S5ihazQDgpKyAgZDOcIFsvoHmTZ8w==, tarball: https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.2.0.tgz} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] '@rolldown/binding-linux-arm64-musl@1.1.5': - resolution: {integrity: sha512-lAryqH7IteztmCXQXk0etKj4wBQ7Gx5S6LjKhsgp9zb8I5bsuvU/2llH1hDQcjsFeqIsovMVN339/8pUDDBXxA==} + resolution: {integrity: sha512-lAryqH7IteztmCXQXk0etKj4wBQ7Gx5S6LjKhsgp9zb8I5bsuvU/2llH1hDQcjsFeqIsovMVN339/8pUDDBXxA==, tarball: https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.1.5.tgz} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] '@rolldown/binding-linux-arm64-musl@1.2.0': - resolution: {integrity: sha512-wUqdwJBbAv0APN87GecstdMUtLjjNTs0hBALpxETD73mccFxdmt/XeizXDtN5RAlBwNKmI+Tg+blect2G+8IeQ==} + resolution: {integrity: sha512-wUqdwJBbAv0APN87GecstdMUtLjjNTs0hBALpxETD73mccFxdmt/XeizXDtN5RAlBwNKmI+Tg+blect2G+8IeQ==, tarball: https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.2.0.tgz} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] '@rolldown/binding-linux-ppc64-gnu@1.1.5': - resolution: {integrity: sha512-fsK/sNBnxzBlL4O1JNrZakVQxPspqpED5dLtNsZS9oOKmtSpdNIzxH2kkol5HYTWJN47sE20ztMJPxfZ89qGOg==} + resolution: {integrity: sha512-fsK/sNBnxzBlL4O1JNrZakVQxPspqpED5dLtNsZS9oOKmtSpdNIzxH2kkol5HYTWJN47sE20ztMJPxfZ89qGOg==, tarball: https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.1.5.tgz} engines: {node: ^20.19.0 || >=22.12.0} cpu: [ppc64] os: [linux] '@rolldown/binding-linux-ppc64-gnu@1.2.0': - resolution: {integrity: sha512-9DtF35qR9/NrfhM4oxLplCzVVjE+KKm8Pjemi0i/sdhAWkUasjmSo8WTTubNJClhSHCfyk2yeyoXDQEDPtDAAw==} + resolution: {integrity: sha512-9DtF35qR9/NrfhM4oxLplCzVVjE+KKm8Pjemi0i/sdhAWkUasjmSo8WTTubNJClhSHCfyk2yeyoXDQEDPtDAAw==, tarball: https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.2.0.tgz} engines: {node: ^20.19.0 || >=22.12.0} cpu: [ppc64] os: [linux] '@rolldown/binding-linux-s390x-gnu@1.1.5': - resolution: {integrity: sha512-gLYb4BIadlfTOYT5gO503n8zQjXflgzpD0FcyKh0Mzx3rqCZKnHoJWV9xe1KXUJ5lx2JfcSHr/mhzS0PC/McAA==} + resolution: {integrity: sha512-gLYb4BIadlfTOYT5gO503n8zQjXflgzpD0FcyKh0Mzx3rqCZKnHoJWV9xe1KXUJ5lx2JfcSHr/mhzS0PC/McAA==, tarball: https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.1.5.tgz} engines: {node: ^20.19.0 || >=22.12.0} cpu: [s390x] os: [linux] '@rolldown/binding-linux-s390x-gnu@1.2.0': - resolution: {integrity: sha512-RzuHrBh8X8Hntd2N4VR02QGEciq/9JhcZoTpR/Cee6otRrlILGCf3cg2ygHuih+ZebUnWmMrDX6ITI85btO6rQ==} + resolution: {integrity: sha512-RzuHrBh8X8Hntd2N4VR02QGEciq/9JhcZoTpR/Cee6otRrlILGCf3cg2ygHuih+ZebUnWmMrDX6ITI85btO6rQ==, tarball: https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.2.0.tgz} engines: {node: ^20.19.0 || >=22.12.0} cpu: [s390x] os: [linux] '@rolldown/binding-linux-x64-gnu@1.1.5': - resolution: {integrity: sha512-FjcpEKUyJygHgs1o50VYNvkt5+7Le/VEdYt0AkRpkL33MnyQfwr8l5mXwMmfmTbyMPr5vJLC+8/Gd9gXnwU1QQ==} + resolution: {integrity: sha512-FjcpEKUyJygHgs1o50VYNvkt5+7Le/VEdYt0AkRpkL33MnyQfwr8l5mXwMmfmTbyMPr5vJLC+8/Gd9gXnwU1QQ==, tarball: https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.1.5.tgz} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] '@rolldown/binding-linux-x64-gnu@1.2.0': - resolution: {integrity: sha512-MK7L0018jjh1jR3mh21G2j1zAVcpscJBlPo2z19pRjv2XOYGRhaV4LyiD8HO6nCDdZln9IFgCMIV5yt4E3klGQ==} + resolution: {integrity: sha512-MK7L0018jjh1jR3mh21G2j1zAVcpscJBlPo2z19pRjv2XOYGRhaV4LyiD8HO6nCDdZln9IFgCMIV5yt4E3klGQ==, tarball: https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.2.0.tgz} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] '@rolldown/binding-linux-x64-musl@1.1.5': - resolution: {integrity: sha512-Me+PfPI2TMeOQk0gYWfLQZtTktrmzbr8cDboqX83XKc7UrgAi55gF+2dUkWdxd19n55Essp2yeca+O9N5rBxHg==} + resolution: {integrity: sha512-Me+PfPI2TMeOQk0gYWfLQZtTktrmzbr8cDboqX83XKc7UrgAi55gF+2dUkWdxd19n55Essp2yeca+O9N5rBxHg==, tarball: https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.1.5.tgz} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] '@rolldown/binding-linux-x64-musl@1.2.0': - resolution: {integrity: sha512-gyrxLQ9NfGb/9LoVnC4kb9miUghw1mghnkfYvNHSnVIXriabnfgGPUP4RLcJm87q3KgYz4FYUG8IDiWUT+CpSw==} + resolution: {integrity: sha512-gyrxLQ9NfGb/9LoVnC4kb9miUghw1mghnkfYvNHSnVIXriabnfgGPUP4RLcJm87q3KgYz4FYUG8IDiWUT+CpSw==, tarball: https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.2.0.tgz} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] '@rolldown/binding-openharmony-arm64@1.1.5': - resolution: {integrity: sha512-yc5WrLzXks6zCQfn9Oxr8pORKyl/pF+QjHmW/Qx3qu0oyrrNC+y2JLTU1E2rcWYAmzlnqngWXHQjy51VzW70Vw==} + resolution: {integrity: sha512-yc5WrLzXks6zCQfn9Oxr8pORKyl/pF+QjHmW/Qx3qu0oyrrNC+y2JLTU1E2rcWYAmzlnqngWXHQjy51VzW70Vw==, tarball: https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.1.5.tgz} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [openharmony] '@rolldown/binding-openharmony-arm64@1.2.0': - resolution: {integrity: sha512-/6VFMQGRmrhP77KXDC+StIxGzcNp5JOIyYtw0CQ8gPlzhpiIRucYfoM5FaFamHd5BJYIdH86yfP46l1p3WdrFA==} + resolution: {integrity: sha512-/6VFMQGRmrhP77KXDC+StIxGzcNp5JOIyYtw0CQ8gPlzhpiIRucYfoM5FaFamHd5BJYIdH86yfP46l1p3WdrFA==, tarball: https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.2.0.tgz} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [openharmony] '@rolldown/binding-wasm32-wasi@1.1.5': - resolution: {integrity: sha512-VbQGPX2b4r48TAMIM2cjgluIM1HYutm4pcTEJsle7iEP7sB1dFqtPLBVbdLAZCxy1txCcPxf4QFf4v8uvltPqA==} + resolution: {integrity: sha512-VbQGPX2b4r48TAMIM2cjgluIM1HYutm4pcTEJsle7iEP7sB1dFqtPLBVbdLAZCxy1txCcPxf4QFf4v8uvltPqA==, tarball: https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.1.5.tgz} engines: {node: ^20.19.0 || >=22.12.0} cpu: [wasm32] '@rolldown/binding-wasm32-wasi@1.2.0': - resolution: {integrity: sha512-rwdbUL465kisF24WEJLvP3JrEG6E5GRuIHt5wpMwHGERtHe4Wm2CIvtf5gTBgr2tGOHKh5NdKEAFS2VkOPE91g==} + resolution: {integrity: sha512-rwdbUL465kisF24WEJLvP3JrEG6E5GRuIHt5wpMwHGERtHe4Wm2CIvtf5gTBgr2tGOHKh5NdKEAFS2VkOPE91g==, tarball: https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.2.0.tgz} engines: {node: ^20.19.0 || >=22.12.0} cpu: [wasm32] '@rolldown/binding-win32-arm64-msvc@1.1.5': - resolution: {integrity: sha512-gHv82k63z4qpV5+Q1y/12KrK0ltWBukVDI8nZcbT7Tt/ZlOIVwppazneq0F93oDxTo3IgAMEDIoQh3E2n6mVsw==} + resolution: {integrity: sha512-gHv82k63z4qpV5+Q1y/12KrK0ltWBukVDI8nZcbT7Tt/ZlOIVwppazneq0F93oDxTo3IgAMEDIoQh3E2n6mVsw==, tarball: https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.1.5.tgz} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [win32] '@rolldown/binding-win32-arm64-msvc@1.2.0': - resolution: {integrity: sha512-+5suHwRiKGmhwyUaNT8a5QbrBvLFh2DbO910TEmGRH1aSxwrCezodvGQnulv4uiWEIv1Kq4ypRsJ5+O+ry1DiA==} + resolution: {integrity: sha512-+5suHwRiKGmhwyUaNT8a5QbrBvLFh2DbO910TEmGRH1aSxwrCezodvGQnulv4uiWEIv1Kq4ypRsJ5+O+ry1DiA==, tarball: https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.2.0.tgz} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [win32] '@rolldown/binding-win32-x64-msvc@1.1.5': - resolution: {integrity: sha512-tTZuDBPw85tEN5PQi1pnEBzDy0Z49HtScLAbD5t6hyeU92A95pRWaSMw1GZZi/RwgSgUIl0xrSlXIT/9QzvYSA==} + resolution: {integrity: sha512-tTZuDBPw85tEN5PQi1pnEBzDy0Z49HtScLAbD5t6hyeU92A95pRWaSMw1GZZi/RwgSgUIl0xrSlXIT/9QzvYSA==, tarball: https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.1.5.tgz} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [win32] '@rolldown/binding-win32-x64-msvc@1.2.0': - resolution: {integrity: sha512-WfFv6/qGufotqBSBzBYwgpCkJBk8Nj7697LL9vTz/XWc67e0r3oewu8iMRwQj3AUL45GVD7wVsPjCsAAtW66Wg==} + resolution: {integrity: sha512-WfFv6/qGufotqBSBzBYwgpCkJBk8Nj7697LL9vTz/XWc67e0r3oewu8iMRwQj3AUL45GVD7wVsPjCsAAtW66Wg==, tarball: https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.2.0.tgz} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [win32] '@rolldown/pluginutils@1.0.0-rc.3': - resolution: {integrity: sha512-eybk3TjzzzV97Dlj5c+XrBFW57eTNhzod66y9HrBlzJ6NsCrWCp/2kaPS3K9wJmurBC0Tdw4yPjXKZqlznim3Q==} + resolution: {integrity: sha512-eybk3TjzzzV97Dlj5c+XrBFW57eTNhzod66y9HrBlzJ6NsCrWCp/2kaPS3K9wJmurBC0Tdw4yPjXKZqlznim3Q==, tarball: https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-rc.3.tgz} '@rolldown/pluginutils@1.0.1': - resolution: {integrity: sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==} + resolution: {integrity: sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==, tarball: https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.1.tgz} '@rollup/plugin-babel@6.1.0': - resolution: {integrity: sha512-dFZNuFD2YRcoomP4oYf+DvQNSUA9ih+A3vUqopQx5EdtPGo3WBnQcI/S8pwpz91UsGfL0HsMSOlaMld8HrbubA==} + resolution: {integrity: sha512-dFZNuFD2YRcoomP4oYf+DvQNSUA9ih+A3vUqopQx5EdtPGo3WBnQcI/S8pwpz91UsGfL0HsMSOlaMld8HrbubA==, tarball: https://registry.npmjs.org/@rollup/plugin-babel/-/plugin-babel-6.1.0.tgz} engines: {node: '>=14.0.0'} peerDependencies: '@babel/core': ^7.0.0 @@ -5519,7 +5546,7 @@ packages: optional: true '@rollup/plugin-commonjs@25.0.8': - resolution: {integrity: sha512-ZEZWTK5n6Qde0to4vS9Mr5x/0UZoqCxPVR9KRUjU4kA2sO7GEUn1fop0DAwpO6z0Nw/kJON9bDmSxdWxO/TT1A==} + resolution: {integrity: sha512-ZEZWTK5n6Qde0to4vS9Mr5x/0UZoqCxPVR9KRUjU4kA2sO7GEUn1fop0DAwpO6z0Nw/kJON9bDmSxdWxO/TT1A==, tarball: https://registry.npmjs.org/@rollup/plugin-commonjs/-/plugin-commonjs-25.0.8.tgz} engines: {node: '>=14.0.0'} peerDependencies: rollup: ^2.68.0||^3.0.0||^4.0.0 @@ -5528,7 +5555,7 @@ packages: optional: true '@rollup/plugin-image@3.0.3': - resolution: {integrity: sha512-qXWQwsXpvD4trSb8PeFPFajp8JLpRtqqOeNYRUKnEQNHm7e5UP7fuSRcbjQAJ7wDZBbnJvSdY5ujNBQd9B1iFg==} + resolution: {integrity: sha512-qXWQwsXpvD4trSb8PeFPFajp8JLpRtqqOeNYRUKnEQNHm7e5UP7fuSRcbjQAJ7wDZBbnJvSdY5ujNBQd9B1iFg==, tarball: https://registry.npmjs.org/@rollup/plugin-image/-/plugin-image-3.0.3.tgz} engines: {node: '>=14.0.0'} peerDependencies: rollup: ^1.20.0||^2.0.0||^3.0.0||^4.0.0 @@ -5537,7 +5564,7 @@ packages: optional: true '@rollup/plugin-json@6.1.0': - resolution: {integrity: sha512-EGI2te5ENk1coGeADSIwZ7G2Q8CJS2sF120T7jLw4xFw9n7wIOXHo+kIYRAoVpJAN+kmqZSoO3Fp4JtoNF4ReA==} + resolution: {integrity: sha512-EGI2te5ENk1coGeADSIwZ7G2Q8CJS2sF120T7jLw4xFw9n7wIOXHo+kIYRAoVpJAN+kmqZSoO3Fp4JtoNF4ReA==, tarball: https://registry.npmjs.org/@rollup/plugin-json/-/plugin-json-6.1.0.tgz} engines: {node: '>=14.0.0'} peerDependencies: rollup: ^1.20.0||^2.0.0||^3.0.0||^4.0.0 @@ -5546,7 +5573,7 @@ packages: optional: true '@rollup/plugin-node-resolve@15.3.1': - resolution: {integrity: sha512-tgg6b91pAybXHJQMAAwW9VuWBO6Thi+q7BCNARLwSqlmsHz0XYURtGvh/AuwSADXSI4h/2uHbs7s4FzlZDGSGA==} + resolution: {integrity: sha512-tgg6b91pAybXHJQMAAwW9VuWBO6Thi+q7BCNARLwSqlmsHz0XYURtGvh/AuwSADXSI4h/2uHbs7s4FzlZDGSGA==, tarball: https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-15.3.1.tgz} engines: {node: '>=14.0.0'} peerDependencies: rollup: ^2.78.0||^3.0.0||^4.0.0 @@ -5555,7 +5582,7 @@ packages: optional: true '@rollup/plugin-typescript@12.3.0': - resolution: {integrity: sha512-7DP0/p7y3t67+NabT9f8oTBFE6gGkto4SA6Np2oudYmZE/m1dt8RB0SjL1msMxFpLo631qjRCcBlAbq1ml/Big==} + resolution: {integrity: sha512-7DP0/p7y3t67+NabT9f8oTBFE6gGkto4SA6Np2oudYmZE/m1dt8RB0SjL1msMxFpLo631qjRCcBlAbq1ml/Big==, tarball: https://registry.npmjs.org/@rollup/plugin-typescript/-/plugin-typescript-12.3.0.tgz} engines: {node: '>=14.0.0'} peerDependencies: rollup: ^2.14.0||^3.0.0||^4.0.0 @@ -5568,11 +5595,11 @@ packages: optional: true '@rollup/pluginutils@4.2.1': - resolution: {integrity: sha512-iKnFXr7NkdZAIHiIWE+BX5ULi/ucVFYWD6TbAV+rZctiRTY2PL6tsIKhoIOaoskiWAkgu+VsbXgUVDNLHf+InQ==} + resolution: {integrity: sha512-iKnFXr7NkdZAIHiIWE+BX5ULi/ucVFYWD6TbAV+rZctiRTY2PL6tsIKhoIOaoskiWAkgu+VsbXgUVDNLHf+InQ==, tarball: https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-4.2.1.tgz} engines: {node: '>= 8.0.0'} '@rollup/pluginutils@5.4.0': - resolution: {integrity: sha512-MfPp06CjRLfXQ3wY0R8vJDYBy/MvVcc9OulEfR0B8Iv9ko+GCNaRZ+EpJYFl27LhKsZK0o420sYCRHCjfCgeUg==} + resolution: {integrity: sha512-MfPp06CjRLfXQ3wY0R8vJDYBy/MvVcc9OulEfR0B8Iv9ko+GCNaRZ+EpJYFl27LhKsZK0o420sYCRHCjfCgeUg==, tarball: https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.4.0.tgz} engines: {node: '>=14.0.0'} peerDependencies: rollup: ^1.20.0||^2.0.0||^3.0.0||^4.0.0 @@ -5581,366 +5608,366 @@ packages: optional: true '@rollup/rollup-android-arm-eabi@4.14.0': - resolution: {integrity: sha512-jwXtxYbRt1V+CdQSy6Z+uZti7JF5irRKF8hlKfEnF/xJpcNGuuiZMBvuoYM+x9sr9iWGnzrlM0+9hvQ1kgkf1w==} + resolution: {integrity: sha512-jwXtxYbRt1V+CdQSy6Z+uZti7JF5irRKF8hlKfEnF/xJpcNGuuiZMBvuoYM+x9sr9iWGnzrlM0+9hvQ1kgkf1w==, tarball: https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.14.0.tgz} cpu: [arm] os: [android] '@rollup/rollup-android-arm-eabi@4.62.2': - resolution: {integrity: sha512-6o7ZLZK+BeenkZCFNDXqpbjw9bD6nuWonvS/lwQJp7NoVVxm6p3qE7qQ5jGuBjiFsgvqjD8mZAU5oWxTmbOeOg==} + resolution: {integrity: sha512-6o7ZLZK+BeenkZCFNDXqpbjw9bD6nuWonvS/lwQJp7NoVVxm6p3qE7qQ5jGuBjiFsgvqjD8mZAU5oWxTmbOeOg==, tarball: https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.62.2.tgz} cpu: [arm] os: [android] '@rollup/rollup-android-arm64@4.14.0': - resolution: {integrity: sha512-fI9nduZhCccjzlsA/OuAwtFGWocxA4gqXGTLvOyiF8d+8o0fZUeSztixkYjcGq1fGZY3Tkq4yRvHPFxU+jdZ9Q==} + resolution: {integrity: sha512-fI9nduZhCccjzlsA/OuAwtFGWocxA4gqXGTLvOyiF8d+8o0fZUeSztixkYjcGq1fGZY3Tkq4yRvHPFxU+jdZ9Q==, tarball: https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.14.0.tgz} cpu: [arm64] os: [android] '@rollup/rollup-android-arm64@4.62.2': - resolution: {integrity: sha512-BaH7BllCACHoH1LguOU56UItGfUWjujlO65kS9LAodViaN4bwIKd7oeW/ZHJ/4ljr/7MIiENnNy3HJ0zXv8Zkw==} + resolution: {integrity: sha512-BaH7BllCACHoH1LguOU56UItGfUWjujlO65kS9LAodViaN4bwIKd7oeW/ZHJ/4ljr/7MIiENnNy3HJ0zXv8Zkw==, tarball: https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.62.2.tgz} cpu: [arm64] os: [android] '@rollup/rollup-darwin-arm64@4.14.0': - resolution: {integrity: sha512-BcnSPRM76/cD2gQC+rQNGBN6GStBs2pl/FpweW8JYuz5J/IEa0Fr4AtrPv766DB/6b2MZ/AfSIOSGw3nEIP8SA==} + resolution: {integrity: sha512-BcnSPRM76/cD2gQC+rQNGBN6GStBs2pl/FpweW8JYuz5J/IEa0Fr4AtrPv766DB/6b2MZ/AfSIOSGw3nEIP8SA==, tarball: https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.14.0.tgz} cpu: [arm64] os: [darwin] '@rollup/rollup-darwin-arm64@4.34.9': - resolution: {integrity: sha512-0CY3/K54slrzLDjOA7TOjN1NuLKERBgk9nY5V34mhmuu673YNb+7ghaDUs6N0ujXR7fz5XaS5Aa6d2TNxZd0OQ==} + resolution: {integrity: sha512-0CY3/K54slrzLDjOA7TOjN1NuLKERBgk9nY5V34mhmuu673YNb+7ghaDUs6N0ujXR7fz5XaS5Aa6d2TNxZd0OQ==, tarball: https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.34.9.tgz} cpu: [arm64] os: [darwin] '@rollup/rollup-darwin-arm64@4.62.2': - resolution: {integrity: sha512-v39RCCvj4He82I9sFmk+M1VZ0PLM9sfsLVikjfx2hYBNALhrrOR2D3JjQA6AhlaSOgcR+RzrKY7e1+bT6SUO/A==} + resolution: {integrity: sha512-v39RCCvj4He82I9sFmk+M1VZ0PLM9sfsLVikjfx2hYBNALhrrOR2D3JjQA6AhlaSOgcR+RzrKY7e1+bT6SUO/A==, tarball: https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.62.2.tgz} cpu: [arm64] os: [darwin] '@rollup/rollup-darwin-x64@4.14.0': - resolution: {integrity: sha512-LDyFB9GRolGN7XI6955aFeI3wCdCUszFWumWU0deHA8VpR3nWRrjG6GtGjBrQxQKFevnUTHKCfPR4IvrW3kCgQ==} + resolution: {integrity: sha512-LDyFB9GRolGN7XI6955aFeI3wCdCUszFWumWU0deHA8VpR3nWRrjG6GtGjBrQxQKFevnUTHKCfPR4IvrW3kCgQ==, tarball: https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.14.0.tgz} cpu: [x64] os: [darwin] '@rollup/rollup-darwin-x64@4.34.9': - resolution: {integrity: sha512-eOojSEAi/acnsJVYRxnMkPFqcxSMFfrw7r2iD9Q32SGkb/Q9FpUY1UlAu1DH9T7j++gZ0lHjnm4OyH2vCI7l7Q==} + resolution: {integrity: sha512-eOojSEAi/acnsJVYRxnMkPFqcxSMFfrw7r2iD9Q32SGkb/Q9FpUY1UlAu1DH9T7j++gZ0lHjnm4OyH2vCI7l7Q==, tarball: https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.34.9.tgz} cpu: [x64] os: [darwin] '@rollup/rollup-darwin-x64@4.62.2': - resolution: {integrity: sha512-yl0y2vq3S3lHeuXhEdss6TWfKW8vkujImO12tn4ZkG/4oghr09LvdYm2RElVjokTQiUvDUGXLGsYeLqUMCKpGA==} + resolution: {integrity: sha512-yl0y2vq3S3lHeuXhEdss6TWfKW8vkujImO12tn4ZkG/4oghr09LvdYm2RElVjokTQiUvDUGXLGsYeLqUMCKpGA==, tarball: https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.62.2.tgz} cpu: [x64] os: [darwin] '@rollup/rollup-freebsd-arm64@4.62.2': - resolution: {integrity: sha512-tT4pvt4qXD+vEoezupCWi+a1F0vvDiksiHc+PxRlYTOH1I6/X4id9jPxTP+Fg+545euaFT1jJVs4CEdHZAU1vw==} + resolution: {integrity: sha512-tT4pvt4qXD+vEoezupCWi+a1F0vvDiksiHc+PxRlYTOH1I6/X4id9jPxTP+Fg+545euaFT1jJVs4CEdHZAU1vw==, tarball: https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.62.2.tgz} cpu: [arm64] os: [freebsd] '@rollup/rollup-freebsd-x64@4.62.2': - resolution: {integrity: sha512-6nU5F2wCW+qvCBhTn1pdIU3bzsIoF7EUwsCDRxilWGprQR6yd508YnH9+OKFCwpfS8pjZqDUmnCAr7exax0XCg==} + resolution: {integrity: sha512-6nU5F2wCW+qvCBhTn1pdIU3bzsIoF7EUwsCDRxilWGprQR6yd508YnH9+OKFCwpfS8pjZqDUmnCAr7exax0XCg==, tarball: https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.62.2.tgz} cpu: [x64] os: [freebsd] '@rollup/rollup-linux-arm-gnueabihf@4.14.0': - resolution: {integrity: sha512-ygrGVhQP47mRh0AAD0zl6QqCbNsf0eTo+vgwkY6LunBcg0f2Jv365GXlDUECIyoXp1kKwL5WW6rsO429DBY/bA==} + resolution: {integrity: sha512-ygrGVhQP47mRh0AAD0zl6QqCbNsf0eTo+vgwkY6LunBcg0f2Jv365GXlDUECIyoXp1kKwL5WW6rsO429DBY/bA==, tarball: https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.14.0.tgz} cpu: [arm] os: [linux] '@rollup/rollup-linux-arm-gnueabihf@4.62.2': - resolution: {integrity: sha512-n1GJHPOvpIfhi3TmrCeh6S6URt9BFCt0KQE3qvexyGCTAKpR4Lg+eWvNZEqu7epxwus/8ElT3hacYEucm49SZg==} + resolution: {integrity: sha512-n1GJHPOvpIfhi3TmrCeh6S6URt9BFCt0KQE3qvexyGCTAKpR4Lg+eWvNZEqu7epxwus/8ElT3hacYEucm49SZg==, tarball: https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.62.2.tgz} cpu: [arm] os: [linux] '@rollup/rollup-linux-arm-musleabihf@4.62.2': - resolution: {integrity: sha512-JqgflS8wEB+UXV/vS1RpRbifGBeN4D5lz8D8oOFbFZw4vedvdOgCFAjfBmIMdW3yL10XpQQ0Ambepw6MXrhOnA==} + resolution: {integrity: sha512-JqgflS8wEB+UXV/vS1RpRbifGBeN4D5lz8D8oOFbFZw4vedvdOgCFAjfBmIMdW3yL10XpQQ0Ambepw6MXrhOnA==, tarball: https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.62.2.tgz} cpu: [arm] os: [linux] '@rollup/rollup-linux-arm64-gnu@4.14.0': - resolution: {integrity: sha512-x+uJ6MAYRlHGe9wi4HQjxpaKHPM3d3JjqqCkeC5gpnnI6OWovLdXTpfa8trjxPLnWKyBsSi5kne+146GAxFt4A==} + resolution: {integrity: sha512-x+uJ6MAYRlHGe9wi4HQjxpaKHPM3d3JjqqCkeC5gpnnI6OWovLdXTpfa8trjxPLnWKyBsSi5kne+146GAxFt4A==, tarball: https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.14.0.tgz} cpu: [arm64] os: [linux] '@rollup/rollup-linux-arm64-gnu@4.34.9': - resolution: {integrity: sha512-6TZjPHjKZUQKmVKMUowF3ewHxctrRR09eYyvT5eFv8w/fXarEra83A2mHTVJLA5xU91aCNOUnM+DWFMSbQ0Nxw==} + resolution: {integrity: sha512-6TZjPHjKZUQKmVKMUowF3ewHxctrRR09eYyvT5eFv8w/fXarEra83A2mHTVJLA5xU91aCNOUnM+DWFMSbQ0Nxw==, tarball: https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.34.9.tgz} cpu: [arm64] os: [linux] '@rollup/rollup-linux-arm64-gnu@4.62.2': - resolution: {integrity: sha512-wnFJkogWvN4jm/hQRF2UBaeUmk20j5+DmHvoyWii2b8HJDyvz1MF2OU/6ynXt2KR63rbZLWkFpoytpdc/yBuSA==} + resolution: {integrity: sha512-wnFJkogWvN4jm/hQRF2UBaeUmk20j5+DmHvoyWii2b8HJDyvz1MF2OU/6ynXt2KR63rbZLWkFpoytpdc/yBuSA==, tarball: https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.62.2.tgz} cpu: [arm64] os: [linux] '@rollup/rollup-linux-arm64-musl@4.14.0': - resolution: {integrity: sha512-nrRw8ZTQKg6+Lttwqo6a2VxR9tOroa2m91XbdQ2sUUzHoedXlsyvY1fN4xWdqz8PKmf4orDwejxXHjh7YBGUCA==} + resolution: {integrity: sha512-nrRw8ZTQKg6+Lttwqo6a2VxR9tOroa2m91XbdQ2sUUzHoedXlsyvY1fN4xWdqz8PKmf4orDwejxXHjh7YBGUCA==, tarball: https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.14.0.tgz} cpu: [arm64] os: [linux] '@rollup/rollup-linux-arm64-musl@4.34.9': - resolution: {integrity: sha512-LD2fytxZJZ6xzOKnMbIpgzFOuIKlxVOpiMAXawsAZ2mHBPEYOnLRK5TTEsID6z4eM23DuO88X0Tq1mErHMVq0A==} + resolution: {integrity: sha512-LD2fytxZJZ6xzOKnMbIpgzFOuIKlxVOpiMAXawsAZ2mHBPEYOnLRK5TTEsID6z4eM23DuO88X0Tq1mErHMVq0A==, tarball: https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.34.9.tgz} cpu: [arm64] os: [linux] '@rollup/rollup-linux-arm64-musl@4.62.2': - resolution: {integrity: sha512-HVu2bp0zhvJ8xHEV9+UUs7S90VadmBSY3LcIMvozbPo4AuMGDWlz3ymHLHZPX4hR67TKTt8Qp5PJ5RBg/i+RMQ==} + resolution: {integrity: sha512-HVu2bp0zhvJ8xHEV9+UUs7S90VadmBSY3LcIMvozbPo4AuMGDWlz3ymHLHZPX4hR67TKTt8Qp5PJ5RBg/i+RMQ==, tarball: https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.62.2.tgz} cpu: [arm64] os: [linux] '@rollup/rollup-linux-loong64-gnu@4.62.2': - resolution: {integrity: sha512-mQqqAV8QaoSgr9I2fKDLY2BAVvmKjWoGiu/cSYQonsLvtqwEn1E4QYfnCOcp5zoEqNhsDYin1s6jx/VJmrxlZg==} + resolution: {integrity: sha512-mQqqAV8QaoSgr9I2fKDLY2BAVvmKjWoGiu/cSYQonsLvtqwEn1E4QYfnCOcp5zoEqNhsDYin1s6jx/VJmrxlZg==, tarball: https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.62.2.tgz} cpu: [loong64] os: [linux] '@rollup/rollup-linux-loong64-musl@4.62.2': - resolution: {integrity: sha512-IxKLoxCQ2IWi6bT2akyDUBGsOImDKB+sPp4EsTmwFQ/fMwpCKm8uLSSgP/Kx/QYUgKis6SEZ5/Nlhup0DIA0PQ==} + resolution: {integrity: sha512-IxKLoxCQ2IWi6bT2akyDUBGsOImDKB+sPp4EsTmwFQ/fMwpCKm8uLSSgP/Kx/QYUgKis6SEZ5/Nlhup0DIA0PQ==, tarball: https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.62.2.tgz} cpu: [loong64] os: [linux] '@rollup/rollup-linux-powerpc64le-gnu@4.14.0': - resolution: {integrity: sha512-xV0d5jDb4aFu84XKr+lcUJ9y3qpIWhttO3Qev97z8DKLXR62LC3cXT/bMZXrjLF9X+P5oSmJTzAhqwUbY96PnA==} + resolution: {integrity: sha512-xV0d5jDb4aFu84XKr+lcUJ9y3qpIWhttO3Qev97z8DKLXR62LC3cXT/bMZXrjLF9X+P5oSmJTzAhqwUbY96PnA==, tarball: https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.14.0.tgz} cpu: [ppc64le] os: [linux] '@rollup/rollup-linux-ppc64-gnu@4.62.2': - resolution: {integrity: sha512-Mk5ha2RQSgyFfmYYLkBpPnUk8D8FriBxesO1u9O75X0mHgXL1UQcH5Itl2lurWL2tj0RxV9b9tJgipac0hRY9A==} + resolution: {integrity: sha512-Mk5ha2RQSgyFfmYYLkBpPnUk8D8FriBxesO1u9O75X0mHgXL1UQcH5Itl2lurWL2tj0RxV9b9tJgipac0hRY9A==, tarball: https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.62.2.tgz} cpu: [ppc64] os: [linux] '@rollup/rollup-linux-ppc64-musl@4.62.2': - resolution: {integrity: sha512-CjvEnqJL/0/TQ3TXX3OPIJ/kmBellrWd4heXUmHeJlTnmwjKpSJzoehLaL6Xk0ZnMHBu9dZuFADNOrtjF4v+2w==} + resolution: {integrity: sha512-CjvEnqJL/0/TQ3TXX3OPIJ/kmBellrWd4heXUmHeJlTnmwjKpSJzoehLaL6Xk0ZnMHBu9dZuFADNOrtjF4v+2w==, tarball: https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.62.2.tgz} cpu: [ppc64] os: [linux] '@rollup/rollup-linux-riscv64-gnu@4.14.0': - resolution: {integrity: sha512-SDDhBQwZX6LPRoPYjAZWyL27LbcBo7WdBFWJi5PI9RPCzU8ijzkQn7tt8NXiXRiFMJCVpkuMkBf4OxSxVMizAw==} + resolution: {integrity: sha512-SDDhBQwZX6LPRoPYjAZWyL27LbcBo7WdBFWJi5PI9RPCzU8ijzkQn7tt8NXiXRiFMJCVpkuMkBf4OxSxVMizAw==, tarball: https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.14.0.tgz} cpu: [riscv64] os: [linux] '@rollup/rollup-linux-riscv64-gnu@4.62.2': - resolution: {integrity: sha512-1SiZbzwdkaDURsew/tSOrooKiYy7EQGT6m8ufavAi9NEyQb/6VuIxFXAL1fqa4iZe3g4NbNk4P7J32z2tw5Mgg==} + resolution: {integrity: sha512-1SiZbzwdkaDURsew/tSOrooKiYy7EQGT6m8ufavAi9NEyQb/6VuIxFXAL1fqa4iZe3g4NbNk4P7J32z2tw5Mgg==, tarball: https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.62.2.tgz} cpu: [riscv64] os: [linux] '@rollup/rollup-linux-riscv64-musl@4.62.2': - resolution: {integrity: sha512-nQts12zJ3NQRoE6uYljOH89v7szzLDvG2JD/vsX+vGXU8w/At1GowTZ5/7qeFQ8m7L55rpR8Okugnuo5bgjy2Q==} + resolution: {integrity: sha512-nQts12zJ3NQRoE6uYljOH89v7szzLDvG2JD/vsX+vGXU8w/At1GowTZ5/7qeFQ8m7L55rpR8Okugnuo5bgjy2Q==, tarball: https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.62.2.tgz} cpu: [riscv64] os: [linux] '@rollup/rollup-linux-s390x-gnu@4.14.0': - resolution: {integrity: sha512-RxB/qez8zIDshNJDufYlTT0ZTVut5eCpAZ3bdXDU9yTxBzui3KhbGjROK2OYTTor7alM7XBhssgoO3CZ0XD3qA==} + resolution: {integrity: sha512-RxB/qez8zIDshNJDufYlTT0ZTVut5eCpAZ3bdXDU9yTxBzui3KhbGjROK2OYTTor7alM7XBhssgoO3CZ0XD3qA==, tarball: https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.14.0.tgz} cpu: [s390x] os: [linux] '@rollup/rollup-linux-s390x-gnu@4.62.2': - resolution: {integrity: sha512-E9/ll019jhPIJgpzfZoIkBGhcz+kKNgVWYRY0zr9srBdPPFVpvOKW8VaJKUbeK+eZXyQF9ltME+Kk6affeaPgg==} + resolution: {integrity: sha512-E9/ll019jhPIJgpzfZoIkBGhcz+kKNgVWYRY0zr9srBdPPFVpvOKW8VaJKUbeK+eZXyQF9ltME+Kk6affeaPgg==, tarball: https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.62.2.tgz} cpu: [s390x] os: [linux] '@rollup/rollup-linux-x64-gnu@4.14.0': - resolution: {integrity: sha512-C6y6z2eCNCfhZxT9u+jAM2Fup89ZjiG5pIzZIDycs1IwESviLxwkQcFRGLjnDrP+PT+v5i4YFvlcfAs+LnreXg==} + resolution: {integrity: sha512-C6y6z2eCNCfhZxT9u+jAM2Fup89ZjiG5pIzZIDycs1IwESviLxwkQcFRGLjnDrP+PT+v5i4YFvlcfAs+LnreXg==, tarball: https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.14.0.tgz} cpu: [x64] os: [linux] '@rollup/rollup-linux-x64-gnu@4.34.9': - resolution: {integrity: sha512-FwBHNSOjUTQLP4MG7y6rR6qbGw4MFeQnIBrMe161QGaQoBQLqSUEKlHIiVgF3g/mb3lxlxzJOpIBhaP+C+KP2A==} + resolution: {integrity: sha512-FwBHNSOjUTQLP4MG7y6rR6qbGw4MFeQnIBrMe161QGaQoBQLqSUEKlHIiVgF3g/mb3lxlxzJOpIBhaP+C+KP2A==, tarball: https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.34.9.tgz} cpu: [x64] os: [linux] '@rollup/rollup-linux-x64-gnu@4.62.2': - resolution: {integrity: sha512-5BqxR/pshjey51iliyzTD5Xi3EN0aLmQ2lZ3lvefVV9c82BvrLo2/6OT55iifpWBufs6kdwWbuOKS841DrmK9A==} + resolution: {integrity: sha512-5BqxR/pshjey51iliyzTD5Xi3EN0aLmQ2lZ3lvefVV9c82BvrLo2/6OT55iifpWBufs6kdwWbuOKS841DrmK9A==, tarball: https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.62.2.tgz} cpu: [x64] os: [linux] '@rollup/rollup-linux-x64-musl@4.14.0': - resolution: {integrity: sha512-i0QwbHYfnOMYsBEyjxcwGu5SMIi9sImDVjDg087hpzXqhBSosxkE7gyIYFHgfFl4mr7RrXksIBZ4DoLoP4FhJg==} + resolution: {integrity: sha512-i0QwbHYfnOMYsBEyjxcwGu5SMIi9sImDVjDg087hpzXqhBSosxkE7gyIYFHgfFl4mr7RrXksIBZ4DoLoP4FhJg==, tarball: https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.14.0.tgz} cpu: [x64] os: [linux] '@rollup/rollup-linux-x64-musl@4.34.9': - resolution: {integrity: sha512-cYRpV4650z2I3/s6+5/LONkjIz8MBeqrk+vPXV10ORBnshpn8S32bPqQ2Utv39jCiDcO2eJTuSlPXpnvmaIgRA==} + resolution: {integrity: sha512-cYRpV4650z2I3/s6+5/LONkjIz8MBeqrk+vPXV10ORBnshpn8S32bPqQ2Utv39jCiDcO2eJTuSlPXpnvmaIgRA==, tarball: https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.34.9.tgz} cpu: [x64] os: [linux] '@rollup/rollup-linux-x64-musl@4.62.2': - resolution: {integrity: sha512-uNN83XxQrRAh/w0/pmAfibcwyb6YWt4gP+dpnQKPVJshAloQ785ii8CT8ZCIxkGg9opVsvAlGhFitSm6D1Jjpg==} + resolution: {integrity: sha512-uNN83XxQrRAh/w0/pmAfibcwyb6YWt4gP+dpnQKPVJshAloQ785ii8CT8ZCIxkGg9opVsvAlGhFitSm6D1Jjpg==, tarball: https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.62.2.tgz} cpu: [x64] os: [linux] '@rollup/rollup-openbsd-x64@4.62.2': - resolution: {integrity: sha512-srjEIxSH3LRnJN6THczDHWQplqEMFiAJrTab0msUryh9kwNpkICf3Ea6q6MN/2cZwRFUNx5w+h6Hpi4QuHS6Zg==} + resolution: {integrity: sha512-srjEIxSH3LRnJN6THczDHWQplqEMFiAJrTab0msUryh9kwNpkICf3Ea6q6MN/2cZwRFUNx5w+h6Hpi4QuHS6Zg==, tarball: https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.62.2.tgz} cpu: [x64] os: [openbsd] '@rollup/rollup-openharmony-arm64@4.62.2': - resolution: {integrity: sha512-8hOJnxgbyObnCm5AlRA3A931xX19xq80RjVTKgJOvEKWqJruP/Uf12IbAOaDjjEXYRewwHLfmF0YRIdK3OwKWA==} + resolution: {integrity: sha512-8hOJnxgbyObnCm5AlRA3A931xX19xq80RjVTKgJOvEKWqJruP/Uf12IbAOaDjjEXYRewwHLfmF0YRIdK3OwKWA==, tarball: https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.62.2.tgz} cpu: [arm64] os: [openharmony] '@rollup/rollup-win32-arm64-msvc@4.14.0': - resolution: {integrity: sha512-Fq52EYb0riNHLBTAcL0cun+rRwyZ10S9vKzhGKKgeD+XbwunszSY0rVMco5KbOsTlwovP2rTOkiII/fQ4ih/zQ==} + resolution: {integrity: sha512-Fq52EYb0riNHLBTAcL0cun+rRwyZ10S9vKzhGKKgeD+XbwunszSY0rVMco5KbOsTlwovP2rTOkiII/fQ4ih/zQ==, tarball: https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.14.0.tgz} cpu: [arm64] os: [win32] '@rollup/rollup-win32-arm64-msvc@4.34.9': - resolution: {integrity: sha512-z4mQK9dAN6byRA/vsSgQiPeuO63wdiDxZ9yg9iyX2QTzKuQM7T4xlBoeUP/J8uiFkqxkcWndWi+W7bXdPbt27Q==} + resolution: {integrity: sha512-z4mQK9dAN6byRA/vsSgQiPeuO63wdiDxZ9yg9iyX2QTzKuQM7T4xlBoeUP/J8uiFkqxkcWndWi+W7bXdPbt27Q==, tarball: https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.34.9.tgz} cpu: [arm64] os: [win32] '@rollup/rollup-win32-arm64-msvc@4.62.2': - resolution: {integrity: sha512-mmF4AY1i0hG/bLWUctUq59gtmgaSIRa3cu/A3JFRp/sCNEme2bgDEiDS22P9FbnJB8NJNF4jPJiSP5RHQpUTDg==} + resolution: {integrity: sha512-mmF4AY1i0hG/bLWUctUq59gtmgaSIRa3cu/A3JFRp/sCNEme2bgDEiDS22P9FbnJB8NJNF4jPJiSP5RHQpUTDg==, tarball: https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.62.2.tgz} cpu: [arm64] os: [win32] '@rollup/rollup-win32-ia32-msvc@4.14.0': - resolution: {integrity: sha512-e/PBHxPdJ00O9p5Ui43+vixSgVf4NlLsmV6QneGERJ3lnjIua/kim6PRFe3iDueT1rQcgSkYP8ZBBXa/h4iPvw==} + resolution: {integrity: sha512-e/PBHxPdJ00O9p5Ui43+vixSgVf4NlLsmV6QneGERJ3lnjIua/kim6PRFe3iDueT1rQcgSkYP8ZBBXa/h4iPvw==, tarball: https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.14.0.tgz} cpu: [ia32] os: [win32] '@rollup/rollup-win32-ia32-msvc@4.62.2': - resolution: {integrity: sha512-DZgkknc6jhHrk46V25vbAM0zZkyP0nSDkJB8/dRkLTxv470dOmWDqGoEJl/9A0dFfS7yE3REOwNDxpHwSLSt0Q==} + resolution: {integrity: sha512-DZgkknc6jhHrk46V25vbAM0zZkyP0nSDkJB8/dRkLTxv470dOmWDqGoEJl/9A0dFfS7yE3REOwNDxpHwSLSt0Q==, tarball: https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.62.2.tgz} cpu: [ia32] os: [win32] '@rollup/rollup-win32-x64-gnu@4.62.2': - resolution: {integrity: sha512-T6xr6ucWSFto+VGajA8YH26LdpHRuP4YLHEKAtCWvJDOlnmWcDZVCI2Jmjr+IFHDlt2zRaTAKE4tfjTaWLgJBg==} + resolution: {integrity: sha512-T6xr6ucWSFto+VGajA8YH26LdpHRuP4YLHEKAtCWvJDOlnmWcDZVCI2Jmjr+IFHDlt2zRaTAKE4tfjTaWLgJBg==, tarball: https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.62.2.tgz} cpu: [x64] os: [win32] '@rollup/rollup-win32-x64-msvc@4.14.0': - resolution: {integrity: sha512-aGg7iToJjdklmxlUlJh/PaPNa4PmqHfyRMLunbL3eaMO0gp656+q1zOKkpJ/CVe9CryJv6tAN1HDoR8cNGzkag==} + resolution: {integrity: sha512-aGg7iToJjdklmxlUlJh/PaPNa4PmqHfyRMLunbL3eaMO0gp656+q1zOKkpJ/CVe9CryJv6tAN1HDoR8cNGzkag==, tarball: https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.14.0.tgz} cpu: [x64] os: [win32] '@rollup/rollup-win32-x64-msvc@4.34.9': - resolution: {integrity: sha512-AyleYRPU7+rgkMWbEh71fQlrzRfeP6SyMnRf9XX4fCdDPAJumdSBqYEcWPMzVQ4ScAl7E4oFfK0GUVn77xSwbw==} + resolution: {integrity: sha512-AyleYRPU7+rgkMWbEh71fQlrzRfeP6SyMnRf9XX4fCdDPAJumdSBqYEcWPMzVQ4ScAl7E4oFfK0GUVn77xSwbw==, tarball: https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.34.9.tgz} cpu: [x64] os: [win32] '@rollup/rollup-win32-x64-msvc@4.62.2': - resolution: {integrity: sha512-BfzEnDJOt9T8M989/lA37EcJgat01wLRnoi5dQf3QzOH7jzpqTAzdDbVfRljVr5r+jzKqpbHeyOfAaXxAd0PAA==} + resolution: {integrity: sha512-BfzEnDJOt9T8M989/lA37EcJgat01wLRnoi5dQf3QzOH7jzpqTAzdDbVfRljVr5r+jzKqpbHeyOfAaXxAd0PAA==, tarball: https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.62.2.tgz} cpu: [x64] os: [win32] '@rollup/wasm-node@4.62.2': - resolution: {integrity: sha512-LseVv64SSO6S7eyc+LFGUnH36NMMFbtKN28vTUHFinRVzFKH4cVQ/BB22JfXM9Ei5l7x46AIQp+n2QzzJ9kxHg==} + resolution: {integrity: sha512-LseVv64SSO6S7eyc+LFGUnH36NMMFbtKN28vTUHFinRVzFKH4cVQ/BB22JfXM9Ei5l7x46AIQp+n2QzzJ9kxHg==, tarball: https://registry.npmjs.org/@rollup/wasm-node/-/wasm-node-4.62.2.tgz} engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true '@rspack/binding-darwin-arm64@1.7.12': - resolution: {integrity: sha512-rbFprJaJiqrmfy8SHth8EsoRS0wg4bXcucwj9NiMzpGFq14Opw8c04iQ6H9BECYzgmN0PKZ9rh41LdVvhdZe4A==} + resolution: {integrity: sha512-rbFprJaJiqrmfy8SHth8EsoRS0wg4bXcucwj9NiMzpGFq14Opw8c04iQ6H9BECYzgmN0PKZ9rh41LdVvhdZe4A==, tarball: https://registry.npmjs.org/@rspack/binding-darwin-arm64/-/binding-darwin-arm64-1.7.12.tgz} cpu: [arm64] os: [darwin] '@rspack/binding-darwin-arm64@2.1.4': - resolution: {integrity: sha512-3Xcs01iw48F4WeE4SHga6bCNb/UEFvtQX4P4eMIaJfGPjTQuxfabGE8yCPm9e3tpLZ5uo+IBnJ6nh5r6tIDOXQ==} + resolution: {integrity: sha512-3Xcs01iw48F4WeE4SHga6bCNb/UEFvtQX4P4eMIaJfGPjTQuxfabGE8yCPm9e3tpLZ5uo+IBnJ6nh5r6tIDOXQ==, tarball: https://registry.npmjs.org/@rspack/binding-darwin-arm64/-/binding-darwin-arm64-2.1.4.tgz} cpu: [arm64] os: [darwin] '@rspack/binding-darwin-x64@1.7.12': - resolution: {integrity: sha512-jnOp+/UXOJa9xqUb8KXH03sysoO2e4Ij6tw6MqDdmdj8n/A8PQENRPUbW9AwXpPtVDJPus9r4fi7b3+6e4B8Hg==} + resolution: {integrity: sha512-jnOp+/UXOJa9xqUb8KXH03sysoO2e4Ij6tw6MqDdmdj8n/A8PQENRPUbW9AwXpPtVDJPus9r4fi7b3+6e4B8Hg==, tarball: https://registry.npmjs.org/@rspack/binding-darwin-x64/-/binding-darwin-x64-1.7.12.tgz} cpu: [x64] os: [darwin] '@rspack/binding-darwin-x64@2.1.4': - resolution: {integrity: sha512-bz/AsCplLs+3fXULPQU9d4r8H4PdeljgHFyItvVIrA/NKZqzQ8sX0topf/zJVZAOtPH7GNnrKjq0/F0U2DHikQ==} + resolution: {integrity: sha512-bz/AsCplLs+3fXULPQU9d4r8H4PdeljgHFyItvVIrA/NKZqzQ8sX0topf/zJVZAOtPH7GNnrKjq0/F0U2DHikQ==, tarball: https://registry.npmjs.org/@rspack/binding-darwin-x64/-/binding-darwin-x64-2.1.4.tgz} cpu: [x64] os: [darwin] '@rspack/binding-linux-arm64-gnu@1.7.12': - resolution: {integrity: sha512-C8owWG+yvo7X0oVLIXetkoJhIFBP1LYNcAQqtgLmJnQLQDklGuP83dKC+zISGQWpjawHfZ1ER96vLgoTrxKZdw==} + resolution: {integrity: sha512-C8owWG+yvo7X0oVLIXetkoJhIFBP1LYNcAQqtgLmJnQLQDklGuP83dKC+zISGQWpjawHfZ1ER96vLgoTrxKZdw==, tarball: https://registry.npmjs.org/@rspack/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.7.12.tgz} cpu: [arm64] os: [linux] '@rspack/binding-linux-arm64-gnu@2.1.4': - resolution: {integrity: sha512-x0HQTLU1MusCtNamuXxf3ayEPkvh9uuaq4wVyBqveRkn4FznSOoHUsxTAKMnjGARX+vdLV/y/SwWJRDp2RI4zw==} + resolution: {integrity: sha512-x0HQTLU1MusCtNamuXxf3ayEPkvh9uuaq4wVyBqveRkn4FznSOoHUsxTAKMnjGARX+vdLV/y/SwWJRDp2RI4zw==, tarball: https://registry.npmjs.org/@rspack/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-2.1.4.tgz} cpu: [arm64] os: [linux] '@rspack/binding-linux-arm64-musl@1.7.12': - resolution: {integrity: sha512-i51WWI64aRpsfSki6rN0aepPqXkVfS+vZM7+4bWDcmnhUmdMvhIPcYg0QRk3DtyJnu33jqNLM0WHY78k00NyfA==} + resolution: {integrity: sha512-i51WWI64aRpsfSki6rN0aepPqXkVfS+vZM7+4bWDcmnhUmdMvhIPcYg0QRk3DtyJnu33jqNLM0WHY78k00NyfA==, tarball: https://registry.npmjs.org/@rspack/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.7.12.tgz} cpu: [arm64] os: [linux] '@rspack/binding-linux-arm64-musl@2.1.4': - resolution: {integrity: sha512-SEYCQD9UflKJMkYGnG5nt2gcsqdkgJsQmryBC/jxo+bOuY9gUSReU99FqCt7WRQrsHLbGeAFeTWs1QzItWG/Bw==} + resolution: {integrity: sha512-SEYCQD9UflKJMkYGnG5nt2gcsqdkgJsQmryBC/jxo+bOuY9gUSReU99FqCt7WRQrsHLbGeAFeTWs1QzItWG/Bw==, tarball: https://registry.npmjs.org/@rspack/binding-linux-arm64-musl/-/binding-linux-arm64-musl-2.1.4.tgz} cpu: [arm64] os: [linux] '@rspack/binding-linux-riscv64-gnu@2.1.4': - resolution: {integrity: sha512-jYtQKtnDRaVfyasvTGY04Z7m+xDWZYVwAIEOB4hP7czM7FVLOMgHlMlvw/EgF0DNHrBthqbPfBIS2tP50CzpEA==} + resolution: {integrity: sha512-jYtQKtnDRaVfyasvTGY04Z7m+xDWZYVwAIEOB4hP7czM7FVLOMgHlMlvw/EgF0DNHrBthqbPfBIS2tP50CzpEA==, tarball: https://registry.npmjs.org/@rspack/binding-linux-riscv64-gnu/-/binding-linux-riscv64-gnu-2.1.4.tgz} cpu: [riscv64] os: [linux] '@rspack/binding-linux-riscv64-musl@2.1.4': - resolution: {integrity: sha512-ZgxKjQAm9pidq2kChQO2PqKI9OQpLuGD7iPBuyT0gQg3m1+7vvbbkArLBPzJ12CQHVZvqua7n/QGx8pQjJI2Zw==} + resolution: {integrity: sha512-ZgxKjQAm9pidq2kChQO2PqKI9OQpLuGD7iPBuyT0gQg3m1+7vvbbkArLBPzJ12CQHVZvqua7n/QGx8pQjJI2Zw==, tarball: https://registry.npmjs.org/@rspack/binding-linux-riscv64-musl/-/binding-linux-riscv64-musl-2.1.4.tgz} cpu: [riscv64] os: [linux] '@rspack/binding-linux-x64-gnu@1.7.12': - resolution: {integrity: sha512-MSos0FuPEefqo9V92ULd5hggKG29EkSNg1zDcypy0OkpsKh5pfjVxTLYFXgTcVyFoUQQbdG8zFBzYbwmJ8V4ew==} + resolution: {integrity: sha512-MSos0FuPEefqo9V92ULd5hggKG29EkSNg1zDcypy0OkpsKh5pfjVxTLYFXgTcVyFoUQQbdG8zFBzYbwmJ8V4ew==, tarball: https://registry.npmjs.org/@rspack/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.7.12.tgz} cpu: [x64] os: [linux] '@rspack/binding-linux-x64-gnu@2.1.4': - resolution: {integrity: sha512-C53B3e6M4yzlYn4hDxR9cHZV+HqkfFGR0zuhH8QCfdBfN5KyGsmXmujiFU85ANEvBgf9CF8VreBQeJ20lyto/g==} + resolution: {integrity: sha512-C53B3e6M4yzlYn4hDxR9cHZV+HqkfFGR0zuhH8QCfdBfN5KyGsmXmujiFU85ANEvBgf9CF8VreBQeJ20lyto/g==, tarball: https://registry.npmjs.org/@rspack/binding-linux-x64-gnu/-/binding-linux-x64-gnu-2.1.4.tgz} cpu: [x64] os: [linux] '@rspack/binding-linux-x64-musl@1.7.12': - resolution: {integrity: sha512-JcAMVKXOnjfpC3coWjCFPWD3Yl8RBw6a+IXQQ8mfRlHaHMIiOv8IfZqx15XRxMUn49CtP7Z0Na8iiAg2aKrcfw==} + resolution: {integrity: sha512-JcAMVKXOnjfpC3coWjCFPWD3Yl8RBw6a+IXQQ8mfRlHaHMIiOv8IfZqx15XRxMUn49CtP7Z0Na8iiAg2aKrcfw==, tarball: https://registry.npmjs.org/@rspack/binding-linux-x64-musl/-/binding-linux-x64-musl-1.7.12.tgz} cpu: [x64] os: [linux] '@rspack/binding-linux-x64-musl@2.1.4': - resolution: {integrity: sha512-UaeG3FRo7e5RameQvWRNQ6KeXF29LEk67U/ohb9tF4U38mKH1OGqhmwCf5yLkqiOSgOi7Ff3ireOZD83Fso1iw==} + resolution: {integrity: sha512-UaeG3FRo7e5RameQvWRNQ6KeXF29LEk67U/ohb9tF4U38mKH1OGqhmwCf5yLkqiOSgOi7Ff3ireOZD83Fso1iw==, tarball: https://registry.npmjs.org/@rspack/binding-linux-x64-musl/-/binding-linux-x64-musl-2.1.4.tgz} cpu: [x64] os: [linux] '@rspack/binding-wasm32-wasi@1.7.12': - resolution: {integrity: sha512-n+ZqP6ZMc0nhOgvadg5VhEs9ojtbES80AcWeFnmGkbzIszvGSO63GKNiRkXtjJ9KFuRzytbbmsCqkUVH+Tywxg==} + resolution: {integrity: sha512-n+ZqP6ZMc0nhOgvadg5VhEs9ojtbES80AcWeFnmGkbzIszvGSO63GKNiRkXtjJ9KFuRzytbbmsCqkUVH+Tywxg==, tarball: https://registry.npmjs.org/@rspack/binding-wasm32-wasi/-/binding-wasm32-wasi-1.7.12.tgz} cpu: [wasm32] '@rspack/binding-wasm32-wasi@2.1.4': - resolution: {integrity: sha512-0P1WZEfu7JOPzD/jQfk9U/6gnRFc7RpvYCQaYZVVYZNJ2gU6O0/yLegRGKNK/2L0zjYwiD0ynhOIBVUUERf5Pw==} + resolution: {integrity: sha512-0P1WZEfu7JOPzD/jQfk9U/6gnRFc7RpvYCQaYZVVYZNJ2gU6O0/yLegRGKNK/2L0zjYwiD0ynhOIBVUUERf5Pw==, tarball: https://registry.npmjs.org/@rspack/binding-wasm32-wasi/-/binding-wasm32-wasi-2.1.4.tgz} cpu: [wasm32] '@rspack/binding-win32-arm64-msvc@1.7.12': - resolution: {integrity: sha512-8+h5fYDXYdmugbdfZ+D1y8IQ3rv2EhSfyGP7vBe+bjNyaMa4jWrpucmZbtxojUL1AzaeuHbvMdj9UO/gelk/+g==} + resolution: {integrity: sha512-8+h5fYDXYdmugbdfZ+D1y8IQ3rv2EhSfyGP7vBe+bjNyaMa4jWrpucmZbtxojUL1AzaeuHbvMdj9UO/gelk/+g==, tarball: https://registry.npmjs.org/@rspack/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.7.12.tgz} cpu: [arm64] os: [win32] '@rspack/binding-win32-arm64-msvc@2.1.4': - resolution: {integrity: sha512-+aStQipk1EakLRPfD+/aQbtmTXfxqSWetcRRDWV3cAsD4ebv1tF8FLKYY1PCaFpQbX1FxzCBGF0KHxkAsi9cxA==} + resolution: {integrity: sha512-+aStQipk1EakLRPfD+/aQbtmTXfxqSWetcRRDWV3cAsD4ebv1tF8FLKYY1PCaFpQbX1FxzCBGF0KHxkAsi9cxA==, tarball: https://registry.npmjs.org/@rspack/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-2.1.4.tgz} cpu: [arm64] os: [win32] '@rspack/binding-win32-ia32-msvc@1.7.12': - resolution: {integrity: sha512-cDMGwTRSa2p9fNBVe1wTRkF2AEXZ9ARWW36QeC5CkLaI0Ezz8lvhF2+CSOPnhaQ1O1qtn0L0SF+lFnrY+I7xGQ==} + resolution: {integrity: sha512-cDMGwTRSa2p9fNBVe1wTRkF2AEXZ9ARWW36QeC5CkLaI0Ezz8lvhF2+CSOPnhaQ1O1qtn0L0SF+lFnrY+I7xGQ==, tarball: https://registry.npmjs.org/@rspack/binding-win32-ia32-msvc/-/binding-win32-ia32-msvc-1.7.12.tgz} cpu: [ia32] os: [win32] '@rspack/binding-win32-ia32-msvc@2.1.4': - resolution: {integrity: sha512-7nyrLRQ07j6i6omZuwiwwTI6rpjdy/Su3niLDAG7WsLL21u3/UQQrw6Fvm8SH3XYteghoHLSM3dhgaisuUhdJg==} + resolution: {integrity: sha512-7nyrLRQ07j6i6omZuwiwwTI6rpjdy/Su3niLDAG7WsLL21u3/UQQrw6Fvm8SH3XYteghoHLSM3dhgaisuUhdJg==, tarball: https://registry.npmjs.org/@rspack/binding-win32-ia32-msvc/-/binding-win32-ia32-msvc-2.1.4.tgz} cpu: [ia32] os: [win32] '@rspack/binding-win32-x64-msvc@1.7.12': - resolution: {integrity: sha512-wIqFvlgFqrgUyj/6S/FJcvShnkZOmIeXTfqvheLY67MGq8qd8jb1YimQVKAIrmWB3yuJKUFACI3Ag1UBtEedEA==} + resolution: {integrity: sha512-wIqFvlgFqrgUyj/6S/FJcvShnkZOmIeXTfqvheLY67MGq8qd8jb1YimQVKAIrmWB3yuJKUFACI3Ag1UBtEedEA==, tarball: https://registry.npmjs.org/@rspack/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.7.12.tgz} cpu: [x64] os: [win32] '@rspack/binding-win32-x64-msvc@2.1.4': - resolution: {integrity: sha512-Z4je7JBaDpO9vvNMgCxlycycRJq+GQwwuXSXagW2xvvGM10Ij63YVtIehSMG9xaW8PED41oc9nWndoEsiD60pA==} + resolution: {integrity: sha512-Z4je7JBaDpO9vvNMgCxlycycRJq+GQwwuXSXagW2xvvGM10Ij63YVtIehSMG9xaW8PED41oc9nWndoEsiD60pA==, tarball: https://registry.npmjs.org/@rspack/binding-win32-x64-msvc/-/binding-win32-x64-msvc-2.1.4.tgz} cpu: [x64] os: [win32] '@rspack/binding@1.7.12': - resolution: {integrity: sha512-f4HHuLbvuld8Ba4iB/4ibse5XrKxFrgmM3S4P2AOKnPlekAFlBjmltCuaTL/W2ggYvILaVY+YcFXrEH1rrKeQA==} + resolution: {integrity: sha512-f4HHuLbvuld8Ba4iB/4ibse5XrKxFrgmM3S4P2AOKnPlekAFlBjmltCuaTL/W2ggYvILaVY+YcFXrEH1rrKeQA==, tarball: https://registry.npmjs.org/@rspack/binding/-/binding-1.7.12.tgz} '@rspack/binding@2.1.4': - resolution: {integrity: sha512-iye4BaTYtTt0qa39avWwEsUobVxFNhQHr6B8reFeKU7sdaZsC9LUoDR8JAt5gOnbnzFMPdKgrdU/VzkC6rXbIg==} + resolution: {integrity: sha512-iye4BaTYtTt0qa39avWwEsUobVxFNhQHr6B8reFeKU7sdaZsC9LUoDR8JAt5gOnbnzFMPdKgrdU/VzkC6rXbIg==, tarball: https://registry.npmjs.org/@rspack/binding/-/binding-2.1.4.tgz} '@rspack/core@1.7.12': - resolution: {integrity: sha512-6CwFIHlhRmXfZoMj3v9MZ1SMTPBn+cHVXeMIeaGp5sufqinKsISbsqHu6ZMJu2wDSmZLdmQJX6zLxkhcAUlhkQ==} + resolution: {integrity: sha512-6CwFIHlhRmXfZoMj3v9MZ1SMTPBn+cHVXeMIeaGp5sufqinKsISbsqHu6ZMJu2wDSmZLdmQJX6zLxkhcAUlhkQ==, tarball: https://registry.npmjs.org/@rspack/core/-/core-1.7.12.tgz} engines: {node: '>=18.12.0'} peerDependencies: '@swc/helpers': '>=0.5.1' @@ -5949,7 +5976,7 @@ packages: optional: true '@rspack/core@2.1.4': - resolution: {integrity: sha512-lpJgtr+JAXuDAMBJfRJ1LHyWVuYJyhZu6L6aj9t4lipUU03qwakHnzn3vwSCr68PsVvVPzR6NbJE1gJienSV0g==} + resolution: {integrity: sha512-lpJgtr+JAXuDAMBJfRJ1LHyWVuYJyhZu6L6aj9t4lipUU03qwakHnzn3vwSCr68PsVvVPzR6NbJE1gJienSV0g==, tarball: https://registry.npmjs.org/@rspack/core/-/core-2.1.4.tgz} engines: {node: ^20.19.0 || >=22.12.0} peerDependencies: '@module-federation/runtime-tools': ^0.24.1 || ^2.0.0 @@ -5961,19 +5988,19 @@ packages: optional: true '@rspack/dev-server@1.2.1': - resolution: {integrity: sha512-e/ARvskYn2Qdd02qLvc0i6H9BnOmzP0xGHS2XCr7GZ3t2k5uC5ZlLkeN1iEebU0FkAW+6ot89NahFo3nupKuww==} + resolution: {integrity: sha512-e/ARvskYn2Qdd02qLvc0i6H9BnOmzP0xGHS2XCr7GZ3t2k5uC5ZlLkeN1iEebU0FkAW+6ot89NahFo3nupKuww==, tarball: https://registry.npmjs.org/@rspack/dev-server/-/dev-server-1.2.1.tgz} engines: {node: '>= 18.12.0'} peerDependencies: '@rspack/core': '*' '@rspack/lite-tapable@1.1.0': - resolution: {integrity: sha512-E2B0JhYFmVAwdDiG14+DW0Di4Ze4Jg10Pc4/lILUrd5DRCaklduz2OvJ5HYQ6G+hd+WTzqQb3QnDNfK4yvAFYw==} + resolution: {integrity: sha512-E2B0JhYFmVAwdDiG14+DW0Di4Ze4Jg10Pc4/lILUrd5DRCaklduz2OvJ5HYQ6G+hd+WTzqQb3QnDNfK4yvAFYw==, tarball: https://registry.npmjs.org/@rspack/lite-tapable/-/lite-tapable-1.1.0.tgz} '@rspack/lite-tapable@1.1.2': - resolution: {integrity: sha512-1OnyWChLGE46YzWyjlmYJssOu/Y0STAnnr2ueKPqDCYTf63GJMs0mxNnCul4dNiVqHYPKv3/fxrTY3IpqoVwZQ==} + resolution: {integrity: sha512-1OnyWChLGE46YzWyjlmYJssOu/Y0STAnnr2ueKPqDCYTf63GJMs0mxNnCul4dNiVqHYPKv3/fxrTY3IpqoVwZQ==, tarball: https://registry.npmjs.org/@rspack/lite-tapable/-/lite-tapable-1.1.2.tgz} '@rspack/plugin-react-refresh@1.6.2': - resolution: {integrity: sha512-k+/VrfTNgo+KirjI6V+8CWRj6y+DH9jOUWv8JorYY4vKf/9xfnZ8xHzuB4iqCwTtoZl9YnxOaOuoyjJipc2tiQ==} + resolution: {integrity: sha512-k+/VrfTNgo+KirjI6V+8CWRj6y+DH9jOUWv8JorYY4vKf/9xfnZ8xHzuB4iqCwTtoZl9YnxOaOuoyjJipc2tiQ==, tarball: https://registry.npmjs.org/@rspack/plugin-react-refresh/-/plugin-react-refresh-1.6.2.tgz} peerDependencies: react-refresh: '>=0.10.0 <1.0.0' webpack-hot-middleware: 2.x @@ -5982,10 +6009,10 @@ packages: optional: true '@rtsao/scc@1.1.0': - resolution: {integrity: sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==} + resolution: {integrity: sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==, tarball: https://registry.npmjs.org/@rtsao/scc/-/scc-1.1.0.tgz} '@rushstack/node-core-library@5.23.1': - resolution: {integrity: sha512-wlKmIKIYCKuCASbITvOxLZXepPbwXvrv7S6ig6XNWFchSyhL/E2txmVXspHY49Wu2dzf7nI27a2k/yV5BA3EiA==} + resolution: {integrity: sha512-wlKmIKIYCKuCASbITvOxLZXepPbwXvrv7S6ig6XNWFchSyhL/E2txmVXspHY49Wu2dzf7nI27a2k/yV5BA3EiA==, tarball: https://registry.npmjs.org/@rushstack/node-core-library/-/node-core-library-5.23.1.tgz} peerDependencies: '@types/node': '*' peerDependenciesMeta: @@ -5993,7 +6020,7 @@ packages: optional: true '@rushstack/problem-matcher@0.2.1': - resolution: {integrity: sha512-gulfhBs6n+I5b7DvjKRfhMGyUejtSgOHTclF/eONr8hcgF1APEDjhxIsfdUYYMzC3rvLwGluqLjbwCFZ8nxrog==} + resolution: {integrity: sha512-gulfhBs6n+I5b7DvjKRfhMGyUejtSgOHTclF/eONr8hcgF1APEDjhxIsfdUYYMzC3rvLwGluqLjbwCFZ8nxrog==, tarball: https://registry.npmjs.org/@rushstack/problem-matcher/-/problem-matcher-0.2.1.tgz} peerDependencies: '@types/node': '*' peerDependenciesMeta: @@ -6001,10 +6028,10 @@ packages: optional: true '@rushstack/rig-package@0.7.3': - resolution: {integrity: sha512-aAA518n6wxxjCfnTAOjQnm7ngNE0FVHxHAw2pxKlIhxrMn0XQjGcXKF0oKWpjBgJOmsaJpVob/v+zr3zxgPWuA==} + resolution: {integrity: sha512-aAA518n6wxxjCfnTAOjQnm7ngNE0FVHxHAw2pxKlIhxrMn0XQjGcXKF0oKWpjBgJOmsaJpVob/v+zr3zxgPWuA==, tarball: https://registry.npmjs.org/@rushstack/rig-package/-/rig-package-0.7.3.tgz} '@rushstack/terminal@0.24.0': - resolution: {integrity: sha512-8ZQS4MMaGsv27EXCBiH7WMPkRZrffeDoIevs6z9TM5dzqiY6+Hn4evfK/G+gvgBTjfvfkHIZPQQmalmI2sM4TQ==} + resolution: {integrity: sha512-8ZQS4MMaGsv27EXCBiH7WMPkRZrffeDoIevs6z9TM5dzqiY6+Hn4evfK/G+gvgBTjfvfkHIZPQQmalmI2sM4TQ==, tarball: https://registry.npmjs.org/@rushstack/terminal/-/terminal-0.24.0.tgz} peerDependencies: '@types/node': '*' peerDependenciesMeta: @@ -6012,216 +6039,216 @@ packages: optional: true '@rushstack/ts-command-line@5.3.10': - resolution: {integrity: sha512-fwI076HYknC0IrMXdY6UmjDv+PH7NHhNJX3/pY2UblSE5XrXgndXZPiOe/6ZtuFpn6DvVDVNhtkIzQ+Qu/MhVQ==} + resolution: {integrity: sha512-fwI076HYknC0IrMXdY6UmjDv+PH7NHhNJX3/pY2UblSE5XrXgndXZPiOe/6ZtuFpn6DvVDVNhtkIzQ+Qu/MhVQ==, tarball: https://registry.npmjs.org/@rushstack/ts-command-line/-/ts-command-line-5.3.10.tgz} '@schematics/angular@22.1.2': - resolution: {integrity: sha512-52udja/QGSNH5geSnL4JWFOEfx8M7tqf7LNXz8byjki4VshVOkKHTawQcL4YbJfo3MfwwXm4AsoadMOk8EST2w==} + resolution: {integrity: sha512-52udja/QGSNH5geSnL4JWFOEfx8M7tqf7LNXz8byjki4VshVOkKHTawQcL4YbJfo3MfwwXm4AsoadMOk8EST2w==, tarball: https://registry.npmjs.org/@schematics/angular/-/angular-22.1.2.tgz} engines: {node: ^22.22.3 || ^24.15.0 || >=26.0.0, npm: ^6.11.0 || ^7.5.6 || >=8.0.0, yarn: '>= 1.13.0'} '@sec-ant/readable-stream@0.4.1': - resolution: {integrity: sha512-831qok9r2t8AlxLko40y2ebgSDhenenCatLVeW/uBtnHPyhHOvG0C7TvfgecV+wHzIm5KUICgzmVpWS+IMEAeg==} + resolution: {integrity: sha512-831qok9r2t8AlxLko40y2ebgSDhenenCatLVeW/uBtnHPyhHOvG0C7TvfgecV+wHzIm5KUICgzmVpWS+IMEAeg==, tarball: https://registry.npmjs.org/@sec-ant/readable-stream/-/readable-stream-0.4.1.tgz} '@sinclair/typebox@0.27.10': - resolution: {integrity: sha512-MTBk/3jGLNB2tVxv6uLlFh1iu64iYOQ2PbdOSK3NW8JZsmlaOh2q6sdtKowBhfw8QFLmYNzTW4/oK4uATIi6ZA==} + resolution: {integrity: sha512-MTBk/3jGLNB2tVxv6uLlFh1iu64iYOQ2PbdOSK3NW8JZsmlaOh2q6sdtKowBhfw8QFLmYNzTW4/oK4uATIi6ZA==, tarball: https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.10.tgz} '@sinclair/typebox@0.34.52': - resolution: {integrity: sha512-XiMQh7qqVlxZzcVD+kkGMNGMzcTrDMLWI7S4x7z1MkCkbDPrekpZXEUK0eZqZFMuHQg2a2DZOcDIh9o5v3Gonw==} + resolution: {integrity: sha512-XiMQh7qqVlxZzcVD+kkGMNGMzcTrDMLWI7S4x7z1MkCkbDPrekpZXEUK0eZqZFMuHQg2a2DZOcDIh9o5v3Gonw==, tarball: https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.34.52.tgz} '@sindresorhus/merge-streams@4.0.0': - resolution: {integrity: sha512-tlqY9xq5ukxTUZBmoOp+m61cqwQD5pHJtFY3Mn8CA8ps6yghLH/Hw8UPdqg4OLmFW3IFlcXnQNmo/dh8HzXYIQ==} + resolution: {integrity: sha512-tlqY9xq5ukxTUZBmoOp+m61cqwQD5pHJtFY3Mn8CA8ps6yghLH/Hw8UPdqg4OLmFW3IFlcXnQNmo/dh8HzXYIQ==, tarball: https://registry.npmjs.org/@sindresorhus/merge-streams/-/merge-streams-4.0.0.tgz} engines: {node: '>=18'} '@sinonjs/commons@3.0.1': - resolution: {integrity: sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==} + resolution: {integrity: sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==, tarball: https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.1.tgz} '@sinonjs/fake-timers@10.3.0': - resolution: {integrity: sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==} + resolution: {integrity: sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==, tarball: https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.3.0.tgz} '@sinonjs/fake-timers@13.0.5': - resolution: {integrity: sha512-36/hTbH2uaWuGVERyC6da9YwGWnzUZXuPro/F2LfsdOsLnCojz/iSH8MxUt/FD2S5XBSVPhmArFUXcpCQ2Hkiw==} + resolution: {integrity: sha512-36/hTbH2uaWuGVERyC6da9YwGWnzUZXuPro/F2LfsdOsLnCojz/iSH8MxUt/FD2S5XBSVPhmArFUXcpCQ2Hkiw==, tarball: https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-13.0.5.tgz} '@sinonjs/fake-timers@15.4.0': - resolution: {integrity: sha512-DsG+8/LscQIQg68J6Ef3dv10u6nVyetYn923s3/sus5eaGfTo1of5WMZSLf0UJc9KDuKPilPH0UDJCjvNbDNCA==} + resolution: {integrity: sha512-DsG+8/LscQIQg68J6Ef3dv10u6nVyetYn923s3/sus5eaGfTo1of5WMZSLf0UJc9KDuKPilPH0UDJCjvNbDNCA==, tarball: https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-15.4.0.tgz} '@standard-schema/spec@1.1.0': - resolution: {integrity: sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==} + resolution: {integrity: sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==, tarball: https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz} '@stencil/core@4.39.0': - resolution: {integrity: sha512-wLASFh5wecnbxY+9pEPd6bl7AZJksLmuiBd0ShvkJ0v/N1nL4HNSw/jq2+TzgFE1+XqCUhKPDeVXFpZf1uuRDw==} + resolution: {integrity: sha512-wLASFh5wecnbxY+9pEPd6bl7AZJksLmuiBd0ShvkJ0v/N1nL4HNSw/jq2+TzgFE1+XqCUhKPDeVXFpZf1uuRDw==, tarball: https://registry.npmjs.org/@stencil/core/-/core-4.39.0.tgz} engines: {node: '>=16.0.0', npm: '>=7.10.0'} hasBin: true '@stencil/sass@3.2.3': - resolution: {integrity: sha512-Wru76NJqa6D79/fDjSuiXoe2U0Ky1j7LLycqn7DV0jCmVO3tiWqXHBUPg0gMXJtxEiIbIJeiH/VpKhjNrBIUkQ==} + resolution: {integrity: sha512-Wru76NJqa6D79/fDjSuiXoe2U0Ky1j7LLycqn7DV0jCmVO3tiWqXHBUPg0gMXJtxEiIbIJeiH/VpKhjNrBIUkQ==, tarball: https://registry.npmjs.org/@stencil/sass/-/sass-3.2.3.tgz} engines: {node: '>=12.0.0', npm: '>=6.0.0'} peerDependencies: '@stencil/core': '>=2.0.0 || >=3.0.0-beta.0 || >= 4.0.0-beta.0 || >= 4.0.0' '@storybook/global@5.0.0': - resolution: {integrity: sha512-FcOqPAXACP0I3oJ/ws6/rrPT9WGhu915Cg8D02a9YxLo0DE9zI+a9A5gRGvmQ09fiWPukqI8ZAEoQEdWUKMQdQ==} + resolution: {integrity: sha512-FcOqPAXACP0I3oJ/ws6/rrPT9WGhu915Cg8D02a9YxLo0DE9zI+a9A5gRGvmQ09fiWPukqI8ZAEoQEdWUKMQdQ==, tarball: https://registry.npmjs.org/@storybook/global/-/global-5.0.0.tgz} '@stylistic/eslint-plugin@5.2.2': - resolution: {integrity: sha512-bE2DUjruqXlHYP3Q2Gpqiuj2bHq7/88FnuaS0FjeGGLCy+X6a07bGVuwtiOYnPSLHR6jmx5Bwdv+j7l8H+G97A==} + resolution: {integrity: sha512-bE2DUjruqXlHYP3Q2Gpqiuj2bHq7/88FnuaS0FjeGGLCy+X6a07bGVuwtiOYnPSLHR6jmx5Bwdv+j7l8H+G97A==, tarball: https://registry.npmjs.org/@stylistic/eslint-plugin/-/eslint-plugin-5.2.2.tgz} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: '>=9.0.0' '@svgr/babel-plugin-add-jsx-attribute@8.0.0': - resolution: {integrity: sha512-b9MIk7yhdS1pMCZM8VeNfUlSKVRhsHZNMl5O9SfaX0l0t5wjdgu4IDzGB8bpnGBBOjGST3rRFVsaaEtI4W6f7g==} + resolution: {integrity: sha512-b9MIk7yhdS1pMCZM8VeNfUlSKVRhsHZNMl5O9SfaX0l0t5wjdgu4IDzGB8bpnGBBOjGST3rRFVsaaEtI4W6f7g==, tarball: https://registry.npmjs.org/@svgr/babel-plugin-add-jsx-attribute/-/babel-plugin-add-jsx-attribute-8.0.0.tgz} engines: {node: '>=14'} peerDependencies: '@babel/core': ^7.0.0-0 '@svgr/babel-plugin-remove-jsx-attribute@8.0.0': - resolution: {integrity: sha512-BcCkm/STipKvbCl6b7QFrMh/vx00vIP63k2eM66MfHJzPr6O2U0jYEViXkHJWqXqQYjdeA9cuCl5KWmlwjDvbA==} + resolution: {integrity: sha512-BcCkm/STipKvbCl6b7QFrMh/vx00vIP63k2eM66MfHJzPr6O2U0jYEViXkHJWqXqQYjdeA9cuCl5KWmlwjDvbA==, tarball: https://registry.npmjs.org/@svgr/babel-plugin-remove-jsx-attribute/-/babel-plugin-remove-jsx-attribute-8.0.0.tgz} 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==} + resolution: {integrity: sha512-5BcGCBfBxB5+XSDSWnhTThfI9jcO5f0Ai2V24gZpG+wXF14BzwxxdDb4g6trdOux0rhibGs385BeFMSmxtS3uA==, tarball: https://registry.npmjs.org/@svgr/babel-plugin-remove-jsx-empty-expression/-/babel-plugin-remove-jsx-empty-expression-8.0.0.tgz} engines: {node: '>=14'} peerDependencies: '@babel/core': ^7.0.0-0 '@svgr/babel-plugin-replace-jsx-attribute-value@8.0.0': - resolution: {integrity: sha512-KVQ+PtIjb1BuYT3ht8M5KbzWBhdAjjUPdlMtpuw/VjT8coTrItWX6Qafl9+ji831JaJcu6PJNKCV0bp01lBNzQ==} + resolution: {integrity: sha512-KVQ+PtIjb1BuYT3ht8M5KbzWBhdAjjUPdlMtpuw/VjT8coTrItWX6Qafl9+ji831JaJcu6PJNKCV0bp01lBNzQ==, tarball: https://registry.npmjs.org/@svgr/babel-plugin-replace-jsx-attribute-value/-/babel-plugin-replace-jsx-attribute-value-8.0.0.tgz} engines: {node: '>=14'} peerDependencies: '@babel/core': ^7.0.0-0 '@svgr/babel-plugin-svg-dynamic-title@8.0.0': - resolution: {integrity: sha512-omNiKqwjNmOQJ2v6ge4SErBbkooV2aAWwaPFs2vUY7p7GhVkzRkJ00kILXQvRhA6miHnNpXv7MRnnSjdRjK8og==} + resolution: {integrity: sha512-omNiKqwjNmOQJ2v6ge4SErBbkooV2aAWwaPFs2vUY7p7GhVkzRkJ00kILXQvRhA6miHnNpXv7MRnnSjdRjK8og==, tarball: https://registry.npmjs.org/@svgr/babel-plugin-svg-dynamic-title/-/babel-plugin-svg-dynamic-title-8.0.0.tgz} engines: {node: '>=14'} peerDependencies: '@babel/core': ^7.0.0-0 '@svgr/babel-plugin-svg-em-dimensions@8.0.0': - resolution: {integrity: sha512-mURHYnu6Iw3UBTbhGwE/vsngtCIbHE43xCRK7kCw4t01xyGqb2Pd+WXekRRoFOBIY29ZoOhUCTEweDMdrjfi9g==} + resolution: {integrity: sha512-mURHYnu6Iw3UBTbhGwE/vsngtCIbHE43xCRK7kCw4t01xyGqb2Pd+WXekRRoFOBIY29ZoOhUCTEweDMdrjfi9g==, tarball: https://registry.npmjs.org/@svgr/babel-plugin-svg-em-dimensions/-/babel-plugin-svg-em-dimensions-8.0.0.tgz} engines: {node: '>=14'} peerDependencies: '@babel/core': ^7.0.0-0 '@svgr/babel-plugin-transform-react-native-svg@8.1.0': - resolution: {integrity: sha512-Tx8T58CHo+7nwJ+EhUwx3LfdNSG9R2OKfaIXXs5soiy5HtgoAEkDay9LIimLOcG8dJQH1wPZp/cnAv6S9CrR1Q==} + resolution: {integrity: sha512-Tx8T58CHo+7nwJ+EhUwx3LfdNSG9R2OKfaIXXs5soiy5HtgoAEkDay9LIimLOcG8dJQH1wPZp/cnAv6S9CrR1Q==, tarball: https://registry.npmjs.org/@svgr/babel-plugin-transform-react-native-svg/-/babel-plugin-transform-react-native-svg-8.1.0.tgz} engines: {node: '>=14'} peerDependencies: '@babel/core': ^7.0.0-0 '@svgr/babel-plugin-transform-svg-component@8.0.0': - resolution: {integrity: sha512-DFx8xa3cZXTdb/k3kfPeaixecQLgKh5NVBMwD0AQxOzcZawK4oo1Jh9LbrcACUivsCA7TLG8eeWgrDXjTMhRmw==} + resolution: {integrity: sha512-DFx8xa3cZXTdb/k3kfPeaixecQLgKh5NVBMwD0AQxOzcZawK4oo1Jh9LbrcACUivsCA7TLG8eeWgrDXjTMhRmw==, tarball: https://registry.npmjs.org/@svgr/babel-plugin-transform-svg-component/-/babel-plugin-transform-svg-component-8.0.0.tgz} engines: {node: '>=12'} peerDependencies: '@babel/core': ^7.0.0-0 '@svgr/babel-preset@8.1.0': - resolution: {integrity: sha512-7EYDbHE7MxHpv4sxvnVPngw5fuR6pw79SkcrILHJ/iMpuKySNCl5W1qcwPEpU+LgyRXOaAFgH0KhwD18wwg6ug==} + resolution: {integrity: sha512-7EYDbHE7MxHpv4sxvnVPngw5fuR6pw79SkcrILHJ/iMpuKySNCl5W1qcwPEpU+LgyRXOaAFgH0KhwD18wwg6ug==, tarball: https://registry.npmjs.org/@svgr/babel-preset/-/babel-preset-8.1.0.tgz} engines: {node: '>=14'} peerDependencies: '@babel/core': ^7.0.0-0 '@svgr/core@8.1.0': - resolution: {integrity: sha512-8QqtOQT5ACVlmsvKOJNEaWmRPmcojMOzCz4Hs2BGG/toAp/K38LcsMRyLp349glq5AzJbCEeimEoxaX6v/fLrA==} + resolution: {integrity: sha512-8QqtOQT5ACVlmsvKOJNEaWmRPmcojMOzCz4Hs2BGG/toAp/K38LcsMRyLp349glq5AzJbCEeimEoxaX6v/fLrA==, tarball: https://registry.npmjs.org/@svgr/core/-/core-8.1.0.tgz} engines: {node: '>=14'} '@svgr/hast-util-to-babel-ast@8.0.0': - resolution: {integrity: sha512-EbDKwO9GpfWP4jN9sGdYwPBU0kdomaPIL2Eu4YwmgP+sJeXT+L7bMwJUBnhzfH8Q2qMBqZ4fJwpCyYsAN3mt2Q==} + resolution: {integrity: sha512-EbDKwO9GpfWP4jN9sGdYwPBU0kdomaPIL2Eu4YwmgP+sJeXT+L7bMwJUBnhzfH8Q2qMBqZ4fJwpCyYsAN3mt2Q==, tarball: https://registry.npmjs.org/@svgr/hast-util-to-babel-ast/-/hast-util-to-babel-ast-8.0.0.tgz} engines: {node: '>=14'} '@svgr/plugin-jsx@8.1.0': - resolution: {integrity: sha512-0xiIyBsLlr8quN+WyuxooNW9RJ0Dpr8uOnH/xrCVO8GLUcwHISwj1AG0k+LFzteTkAA0GbX0kj9q6Dk70PTiPA==} + resolution: {integrity: sha512-0xiIyBsLlr8quN+WyuxooNW9RJ0Dpr8uOnH/xrCVO8GLUcwHISwj1AG0k+LFzteTkAA0GbX0kj9q6Dk70PTiPA==, tarball: https://registry.npmjs.org/@svgr/plugin-jsx/-/plugin-jsx-8.1.0.tgz} engines: {node: '>=14'} peerDependencies: '@svgr/core': '*' '@svgr/plugin-svgo@8.1.0': - resolution: {integrity: sha512-Ywtl837OGO9pTLIN/onoWLmDQ4zFUycI1g76vuKGEz6evR/ZTJlJuz3G/fIkb6OVBJ2g0o6CGJzaEjfmEo3AHA==} + resolution: {integrity: sha512-Ywtl837OGO9pTLIN/onoWLmDQ4zFUycI1g76vuKGEz6evR/ZTJlJuz3G/fIkb6OVBJ2g0o6CGJzaEjfmEo3AHA==, tarball: https://registry.npmjs.org/@svgr/plugin-svgo/-/plugin-svgo-8.1.0.tgz} engines: {node: '>=14'} peerDependencies: '@svgr/core': '*' '@svgr/webpack@8.1.0': - resolution: {integrity: sha512-LnhVjMWyMQV9ZmeEy26maJk+8HTIbd59cH4F2MJ439k9DqejRisfFNGAPvRYlKETuh9LrImlS8aKsBgKjMA8WA==} + resolution: {integrity: sha512-LnhVjMWyMQV9ZmeEy26maJk+8HTIbd59cH4F2MJ439k9DqejRisfFNGAPvRYlKETuh9LrImlS8aKsBgKjMA8WA==, tarball: https://registry.npmjs.org/@svgr/webpack/-/webpack-8.1.0.tgz} engines: {node: '>=14'} '@swc-node/core@1.14.1': - resolution: {integrity: sha512-jrt5GUaZUU6cmMS+WTJEvGvaB6j1YNKPHPzC2PUi2BjaFbtxURHj6641Az6xN7b665hNniAIdvjxWcRml5yCnw==} + resolution: {integrity: sha512-jrt5GUaZUU6cmMS+WTJEvGvaB6j1YNKPHPzC2PUi2BjaFbtxURHj6641Az6xN7b665hNniAIdvjxWcRml5yCnw==, tarball: https://registry.npmjs.org/@swc-node/core/-/core-1.14.1.tgz} engines: {node: '>= 10'} peerDependencies: '@swc/core': '>= 1.13.3' '@swc/types': '>= 0.1' '@swc-node/register@1.11.1': - resolution: {integrity: sha512-VQ0hJ5jX31TVv/fhZx4xJRzd8pwn6VvzYd2tGOHHr2TfXGCBixZoqdPDXTiEoJLCTS2MmvBf6zyQZZ0M8aGQCQ==} + resolution: {integrity: sha512-VQ0hJ5jX31TVv/fhZx4xJRzd8pwn6VvzYd2tGOHHr2TfXGCBixZoqdPDXTiEoJLCTS2MmvBf6zyQZZ0M8aGQCQ==, tarball: https://registry.npmjs.org/@swc-node/register/-/register-1.11.1.tgz} peerDependencies: '@swc/core': '>= 1.4.13' typescript: '>= 4.3' '@swc-node/sourcemap-support@0.6.1': - resolution: {integrity: sha512-ovltDVH5QpdHXZkW138vG4+dgcNsxfwxHVoV6BtmTbz2KKl1A8ZSlbdtxzzfNjCjbpayda8Us9eMtcHobm38dA==} + resolution: {integrity: sha512-ovltDVH5QpdHXZkW138vG4+dgcNsxfwxHVoV6BtmTbz2KKl1A8ZSlbdtxzzfNjCjbpayda8Us9eMtcHobm38dA==, tarball: https://registry.npmjs.org/@swc-node/sourcemap-support/-/sourcemap-support-0.6.1.tgz} '@swc/core-darwin-arm64@1.15.8': - resolution: {integrity: sha512-M9cK5GwyWWRkRGwwCbREuj6r8jKdES/haCZ3Xckgkl8MUQJZA3XB7IXXK1IXRNeLjg6m7cnoMICpXv1v1hlJOg==} + resolution: {integrity: sha512-M9cK5GwyWWRkRGwwCbREuj6r8jKdES/haCZ3Xckgkl8MUQJZA3XB7IXXK1IXRNeLjg6m7cnoMICpXv1v1hlJOg==, tarball: https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.15.8.tgz} engines: {node: '>=10'} cpu: [arm64] os: [darwin] '@swc/core-darwin-x64@1.15.8': - resolution: {integrity: sha512-j47DasuOvXl80sKJHSi2X25l44CMc3VDhlJwA7oewC1nV1VsSzwX+KOwE5tLnfORvVJJyeiXgJORNYg4jeIjYQ==} + resolution: {integrity: sha512-j47DasuOvXl80sKJHSi2X25l44CMc3VDhlJwA7oewC1nV1VsSzwX+KOwE5tLnfORvVJJyeiXgJORNYg4jeIjYQ==, tarball: https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.15.8.tgz} engines: {node: '>=10'} cpu: [x64] os: [darwin] '@swc/core-linux-arm-gnueabihf@1.15.8': - resolution: {integrity: sha512-siAzDENu2rUbwr9+fayWa26r5A9fol1iORG53HWxQL1J8ym4k7xt9eME0dMPXlYZDytK5r9sW8zEA10F2U3Xwg==} + resolution: {integrity: sha512-siAzDENu2rUbwr9+fayWa26r5A9fol1iORG53HWxQL1J8ym4k7xt9eME0dMPXlYZDytK5r9sW8zEA10F2U3Xwg==, tarball: https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.15.8.tgz} engines: {node: '>=10'} cpu: [arm] os: [linux] '@swc/core-linux-arm64-gnu@1.15.8': - resolution: {integrity: sha512-o+1y5u6k2FfPYbTRUPvurwzNt5qd0NTumCTFscCNuBksycloXY16J8L+SMW5QRX59n4Hp9EmFa3vpvNHRVv1+Q==} + resolution: {integrity: sha512-o+1y5u6k2FfPYbTRUPvurwzNt5qd0NTumCTFscCNuBksycloXY16J8L+SMW5QRX59n4Hp9EmFa3vpvNHRVv1+Q==, tarball: https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.15.8.tgz} engines: {node: '>=10'} cpu: [arm64] os: [linux] '@swc/core-linux-arm64-musl@1.15.8': - resolution: {integrity: sha512-koiCqL09EwOP1S2RShCI7NbsQuG6r2brTqUYE7pV7kZm9O17wZ0LSz22m6gVibpwEnw8jI3IE1yYsQTVpluALw==} + resolution: {integrity: sha512-koiCqL09EwOP1S2RShCI7NbsQuG6r2brTqUYE7pV7kZm9O17wZ0LSz22m6gVibpwEnw8jI3IE1yYsQTVpluALw==, tarball: https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.15.8.tgz} engines: {node: '>=10'} cpu: [arm64] os: [linux] '@swc/core-linux-x64-gnu@1.15.8': - resolution: {integrity: sha512-4p6lOMU3bC+Vd5ARtKJ/FxpIC5G8v3XLoPEZ5s7mLR8h7411HWC/LmTXDHcrSXRC55zvAVia1eldy6zDLz8iFQ==} + resolution: {integrity: sha512-4p6lOMU3bC+Vd5ARtKJ/FxpIC5G8v3XLoPEZ5s7mLR8h7411HWC/LmTXDHcrSXRC55zvAVia1eldy6zDLz8iFQ==, tarball: https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.15.8.tgz} engines: {node: '>=10'} cpu: [x64] os: [linux] '@swc/core-linux-x64-musl@1.15.8': - resolution: {integrity: sha512-z3XBnbrZAL+6xDGAhJoN4lOueIxC/8rGrJ9tg+fEaeqLEuAtHSW2QHDHxDwkxZMjuF/pZ6MUTjHjbp8wLbuRLA==} + resolution: {integrity: sha512-z3XBnbrZAL+6xDGAhJoN4lOueIxC/8rGrJ9tg+fEaeqLEuAtHSW2QHDHxDwkxZMjuF/pZ6MUTjHjbp8wLbuRLA==, tarball: https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.15.8.tgz} engines: {node: '>=10'} cpu: [x64] os: [linux] '@swc/core-win32-arm64-msvc@1.15.8': - resolution: {integrity: sha512-djQPJ9Rh9vP8GTS/Df3hcc6XP6xnG5c8qsngWId/BLA9oX6C7UzCPAn74BG/wGb9a6j4w3RINuoaieJB3t+7iQ==} + resolution: {integrity: sha512-djQPJ9Rh9vP8GTS/Df3hcc6XP6xnG5c8qsngWId/BLA9oX6C7UzCPAn74BG/wGb9a6j4w3RINuoaieJB3t+7iQ==, tarball: https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.15.8.tgz} engines: {node: '>=10'} cpu: [arm64] os: [win32] '@swc/core-win32-ia32-msvc@1.15.8': - resolution: {integrity: sha512-/wfAgxORg2VBaUoFdytcVBVCgf1isWZIEXB9MZEUty4wwK93M/PxAkjifOho9RN3WrM3inPLabICRCEgdHpKKQ==} + resolution: {integrity: sha512-/wfAgxORg2VBaUoFdytcVBVCgf1isWZIEXB9MZEUty4wwK93M/PxAkjifOho9RN3WrM3inPLabICRCEgdHpKKQ==, tarball: https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.15.8.tgz} engines: {node: '>=10'} cpu: [ia32] os: [win32] '@swc/core-win32-x64-msvc@1.15.8': - resolution: {integrity: sha512-GpMePrh9Sl4d61o4KAHOOv5is5+zt6BEXCOCgs/H0FLGeii7j9bWDE8ExvKFy2GRRZVNR1ugsnzaGWHKM6kuzA==} + resolution: {integrity: sha512-GpMePrh9Sl4d61o4KAHOOv5is5+zt6BEXCOCgs/H0FLGeii7j9bWDE8ExvKFy2GRRZVNR1ugsnzaGWHKM6kuzA==, tarball: https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.15.8.tgz} engines: {node: '>=10'} cpu: [x64] os: [win32] '@swc/core@1.15.8': - resolution: {integrity: sha512-T8keoJjXaSUoVBCIjgL6wAnhADIb09GOELzKg10CjNg+vLX48P93SME6jTfte9MZIm5m+Il57H3rTSk/0kzDUw==} + resolution: {integrity: sha512-T8keoJjXaSUoVBCIjgL6wAnhADIb09GOELzKg10CjNg+vLX48P93SME6jTfte9MZIm5m+Il57H3rTSk/0kzDUw==, tarball: https://registry.npmjs.org/@swc/core/-/core-1.15.8.tgz} engines: {node: '>=10'} peerDependencies: '@swc/helpers': '>=0.5.17' @@ -6230,76 +6257,76 @@ packages: optional: true '@swc/counter@0.1.3': - resolution: {integrity: sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==} + resolution: {integrity: sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==, tarball: https://registry.npmjs.org/@swc/counter/-/counter-0.1.3.tgz} '@swc/helpers@0.5.2': - resolution: {integrity: sha512-E4KcWTpoLHqwPHLxidpOqQbcrZVgi0rsmmZXUle1jXmJfuIf/UWpczUJ7MZZ5tlxytgJXyp0w4PGkkeLiuIdZw==} + resolution: {integrity: sha512-E4KcWTpoLHqwPHLxidpOqQbcrZVgi0rsmmZXUle1jXmJfuIf/UWpczUJ7MZZ5tlxytgJXyp0w4PGkkeLiuIdZw==, tarball: https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.2.tgz} '@swc/helpers@0.5.23': - resolution: {integrity: sha512-5lSsMOTXURePglDfvuAQUqkGek9Hg2kksOYay2m0+XR++b2NWYL/4sWyuvVBIs8oKnJaxkdi9whaL/sqN13afw==} + resolution: {integrity: sha512-5lSsMOTXURePglDfvuAQUqkGek9Hg2kksOYay2m0+XR++b2NWYL/4sWyuvVBIs8oKnJaxkdi9whaL/sqN13afw==, tarball: https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.23.tgz} '@swc/types@0.1.27': - resolution: {integrity: sha512-K6h3iUlqeM946U4sXFYeahefR1YBbXJvko+hv8WS8/0BNJ4OHiHRywMnQUJCqkR7Y9+hqQ1TvEpiKqUhz7NEFg==} + resolution: {integrity: sha512-K6h3iUlqeM946U4sXFYeahefR1YBbXJvko+hv8WS8/0BNJ4OHiHRywMnQUJCqkR7Y9+hqQ1TvEpiKqUhz7NEFg==, tarball: https://registry.npmjs.org/@swc/types/-/types-0.1.27.tgz} '@tailwindcss/node@4.2.1': - resolution: {integrity: sha512-jlx6sLk4EOwO6hHe1oCGm1Q4AN/s0rSrTTPBGPM0/RQ6Uylwq17FuU8IeJJKEjtc6K6O07zsvP+gDO6MMWo7pg==} + resolution: {integrity: sha512-jlx6sLk4EOwO6hHe1oCGm1Q4AN/s0rSrTTPBGPM0/RQ6Uylwq17FuU8IeJJKEjtc6K6O07zsvP+gDO6MMWo7pg==, tarball: https://registry.npmjs.org/@tailwindcss/node/-/node-4.2.1.tgz} '@tailwindcss/oxide-android-arm64@4.2.1': - resolution: {integrity: sha512-eZ7G1Zm5EC8OOKaesIKuw77jw++QJ2lL9N+dDpdQiAB/c/B2wDh0QPFHbkBVrXnwNugvrbJFk1gK2SsVjwWReg==} + resolution: {integrity: sha512-eZ7G1Zm5EC8OOKaesIKuw77jw++QJ2lL9N+dDpdQiAB/c/B2wDh0QPFHbkBVrXnwNugvrbJFk1gK2SsVjwWReg==, tarball: https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.2.1.tgz} engines: {node: '>= 20'} cpu: [arm64] os: [android] '@tailwindcss/oxide-darwin-arm64@4.2.1': - resolution: {integrity: sha512-q/LHkOstoJ7pI1J0q6djesLzRvQSIfEto148ppAd+BVQK0JYjQIFSK3JgYZJa+Yzi0DDa52ZsQx2rqytBnf8Hw==} + resolution: {integrity: sha512-q/LHkOstoJ7pI1J0q6djesLzRvQSIfEto148ppAd+BVQK0JYjQIFSK3JgYZJa+Yzi0DDa52ZsQx2rqytBnf8Hw==, tarball: https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.2.1.tgz} engines: {node: '>= 20'} cpu: [arm64] os: [darwin] '@tailwindcss/oxide-darwin-x64@4.2.1': - resolution: {integrity: sha512-/f/ozlaXGY6QLbpvd/kFTro2l18f7dHKpB+ieXz+Cijl4Mt9AI2rTrpq7V+t04nK+j9XBQHnSMdeQRhbGyt6fw==} + resolution: {integrity: sha512-/f/ozlaXGY6QLbpvd/kFTro2l18f7dHKpB+ieXz+Cijl4Mt9AI2rTrpq7V+t04nK+j9XBQHnSMdeQRhbGyt6fw==, tarball: https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.2.1.tgz} engines: {node: '>= 20'} cpu: [x64] os: [darwin] '@tailwindcss/oxide-freebsd-x64@4.2.1': - resolution: {integrity: sha512-5e/AkgYJT/cpbkys/OU2Ei2jdETCLlifwm7ogMC7/hksI2fC3iiq6OcXwjibcIjPung0kRtR3TxEITkqgn0TcA==} + resolution: {integrity: sha512-5e/AkgYJT/cpbkys/OU2Ei2jdETCLlifwm7ogMC7/hksI2fC3iiq6OcXwjibcIjPung0kRtR3TxEITkqgn0TcA==, tarball: https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.2.1.tgz} engines: {node: '>= 20'} cpu: [x64] os: [freebsd] '@tailwindcss/oxide-linux-arm-gnueabihf@4.2.1': - resolution: {integrity: sha512-Uny1EcVTTmerCKt/1ZuKTkb0x8ZaiuYucg2/kImO5A5Y/kBz41/+j0gxUZl+hTF3xkWpDmHX+TaWhOtba2Fyuw==} + resolution: {integrity: sha512-Uny1EcVTTmerCKt/1ZuKTkb0x8ZaiuYucg2/kImO5A5Y/kBz41/+j0gxUZl+hTF3xkWpDmHX+TaWhOtba2Fyuw==, tarball: https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.2.1.tgz} engines: {node: '>= 20'} cpu: [arm] os: [linux] '@tailwindcss/oxide-linux-arm64-gnu@4.2.1': - resolution: {integrity: sha512-CTrwomI+c7n6aSSQlsPL0roRiNMDQ/YzMD9EjcR+H4f0I1SQ8QqIuPnsVp7QgMkC1Qi8rtkekLkOFjo7OlEFRQ==} + resolution: {integrity: sha512-CTrwomI+c7n6aSSQlsPL0roRiNMDQ/YzMD9EjcR+H4f0I1SQ8QqIuPnsVp7QgMkC1Qi8rtkekLkOFjo7OlEFRQ==, tarball: https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.2.1.tgz} engines: {node: '>= 20'} cpu: [arm64] os: [linux] '@tailwindcss/oxide-linux-arm64-musl@4.2.1': - resolution: {integrity: sha512-WZA0CHRL/SP1TRbA5mp9htsppSEkWuQ4KsSUumYQnyl8ZdT39ntwqmz4IUHGN6p4XdSlYfJwM4rRzZLShHsGAQ==} + resolution: {integrity: sha512-WZA0CHRL/SP1TRbA5mp9htsppSEkWuQ4KsSUumYQnyl8ZdT39ntwqmz4IUHGN6p4XdSlYfJwM4rRzZLShHsGAQ==, tarball: https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.2.1.tgz} engines: {node: '>= 20'} cpu: [arm64] os: [linux] '@tailwindcss/oxide-linux-x64-gnu@4.2.1': - resolution: {integrity: sha512-qMFzxI2YlBOLW5PhblzuSWlWfwLHaneBE0xHzLrBgNtqN6mWfs+qYbhryGSXQjFYB1Dzf5w+LN5qbUTPhW7Y5g==} + resolution: {integrity: sha512-qMFzxI2YlBOLW5PhblzuSWlWfwLHaneBE0xHzLrBgNtqN6mWfs+qYbhryGSXQjFYB1Dzf5w+LN5qbUTPhW7Y5g==, tarball: https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.2.1.tgz} engines: {node: '>= 20'} cpu: [x64] os: [linux] '@tailwindcss/oxide-linux-x64-musl@4.2.1': - resolution: {integrity: sha512-5r1X2FKnCMUPlXTWRYpHdPYUY6a1Ar/t7P24OuiEdEOmms5lyqjDRvVY1yy9Rmioh+AunQ0rWiOTPE8F9A3v5g==} + resolution: {integrity: sha512-5r1X2FKnCMUPlXTWRYpHdPYUY6a1Ar/t7P24OuiEdEOmms5lyqjDRvVY1yy9Rmioh+AunQ0rWiOTPE8F9A3v5g==, tarball: https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.2.1.tgz} engines: {node: '>= 20'} cpu: [x64] os: [linux] '@tailwindcss/oxide-wasm32-wasi@4.2.1': - resolution: {integrity: sha512-MGFB5cVPvshR85MTJkEvqDUnuNoysrsRxd6vnk1Lf2tbiqNlXpHYZqkqOQalydienEWOHHFyyuTSYRsLfxFJ2Q==} + resolution: {integrity: sha512-MGFB5cVPvshR85MTJkEvqDUnuNoysrsRxd6vnk1Lf2tbiqNlXpHYZqkqOQalydienEWOHHFyyuTSYRsLfxFJ2Q==, tarball: https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.2.1.tgz} engines: {node: '>=14.0.0'} cpu: [wasm32] bundledDependencies: @@ -6311,42 +6338,42 @@ packages: - tslib '@tailwindcss/oxide-win32-arm64-msvc@4.2.1': - resolution: {integrity: sha512-YlUEHRHBGnCMh4Nj4GnqQyBtsshUPdiNroZj8VPkvTZSoHsilRCwXcVKnG9kyi0ZFAS/3u+qKHBdDc81SADTRA==} + resolution: {integrity: sha512-YlUEHRHBGnCMh4Nj4GnqQyBtsshUPdiNroZj8VPkvTZSoHsilRCwXcVKnG9kyi0ZFAS/3u+qKHBdDc81SADTRA==, tarball: https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.2.1.tgz} engines: {node: '>= 20'} cpu: [arm64] os: [win32] '@tailwindcss/oxide-win32-x64-msvc@4.2.1': - resolution: {integrity: sha512-rbO34G5sMWWyrN/idLeVxAZgAKWrn5LiR3/I90Q9MkA67s6T1oB0xtTe+0heoBvHSpbU9Mk7i6uwJnpo4u21XQ==} + resolution: {integrity: sha512-rbO34G5sMWWyrN/idLeVxAZgAKWrn5LiR3/I90Q9MkA67s6T1oB0xtTe+0heoBvHSpbU9Mk7i6uwJnpo4u21XQ==, tarball: https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.2.1.tgz} engines: {node: '>= 20'} cpu: [x64] os: [win32] '@tailwindcss/oxide@4.2.1': - resolution: {integrity: sha512-yv9jeEFWnjKCI6/T3Oq50yQEOqmpmpfzG1hcZsAOaXFQPfzWprWrlHSdGPEF3WQTi8zu8ohC9Mh9J470nT5pUw==} + resolution: {integrity: sha512-yv9jeEFWnjKCI6/T3Oq50yQEOqmpmpfzG1hcZsAOaXFQPfzWprWrlHSdGPEF3WQTi8zu8ohC9Mh9J470nT5pUw==, tarball: https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.2.1.tgz} engines: {node: '>= 20'} '@tailwindcss/postcss@4.2.1': - resolution: {integrity: sha512-OEwGIBnXnj7zJeonOh6ZG9woofIjGrd2BORfvE5p9USYKDCZoQmfqLcfNiRWoJlRWLdNPn2IgVZuWAOM4iTYMw==} + resolution: {integrity: sha512-OEwGIBnXnj7zJeonOh6ZG9woofIjGrd2BORfvE5p9USYKDCZoQmfqLcfNiRWoJlRWLdNPn2IgVZuWAOM4iTYMw==, tarball: https://registry.npmjs.org/@tailwindcss/postcss/-/postcss-4.2.1.tgz} '@tailwindcss/typography@0.5.20': - resolution: {integrity: sha512-hwbzQuNUfcPvbegQFatVPl/MY/tcM9KLl963hQ5laJKPh81TEZ1+dNG9PirGvcaDBkp+BCshExAyKVPW91dozw==} + resolution: {integrity: sha512-hwbzQuNUfcPvbegQFatVPl/MY/tcM9KLl963hQ5laJKPh81TEZ1+dNG9PirGvcaDBkp+BCshExAyKVPW91dozw==, tarball: https://registry.npmjs.org/@tailwindcss/typography/-/typography-0.5.20.tgz} peerDependencies: tailwindcss: '>=3.0.0 || >=4.0.0 || insiders' '@tarekraafat/autocomplete.js@10.2.9': - resolution: {integrity: sha512-A7OP3iJDTWeO85M3Vxu391acu9SmDguormHpMZ13khuyM180dKl9O1gAXSDA322XwkYuUU1Ad7WchW1TQNNuDw==} + resolution: {integrity: sha512-A7OP3iJDTWeO85M3Vxu391acu9SmDguormHpMZ13khuyM180dKl9O1gAXSDA322XwkYuUU1Ad7WchW1TQNNuDw==, tarball: https://registry.npmjs.org/@tarekraafat/autocomplete.js/-/autocomplete.js-10.2.9.tgz} '@testing-library/dom@10.4.1': - resolution: {integrity: sha512-o4PXJQidqJl82ckFaXUeoAW+XysPLauYI43Abki5hABd853iMhitooc6znOnczgbTYmEP6U6/y1ZyKAIsvMKGg==} + resolution: {integrity: sha512-o4PXJQidqJl82ckFaXUeoAW+XysPLauYI43Abki5hABd853iMhitooc6znOnczgbTYmEP6U6/y1ZyKAIsvMKGg==, tarball: https://registry.npmjs.org/@testing-library/dom/-/dom-10.4.1.tgz} engines: {node: '>=18'} '@testing-library/jest-dom@6.6.3': - resolution: {integrity: sha512-IteBhl4XqYNkM54f4ejhLRJiZNqcSCoXUOG2CPK7qbD322KjQozM4kHQOfkG2oln9b9HTYqs+Sae8vBATubxxA==} + resolution: {integrity: sha512-IteBhl4XqYNkM54f4ejhLRJiZNqcSCoXUOG2CPK7qbD322KjQozM4kHQOfkG2oln9b9HTYqs+Sae8vBATubxxA==, tarball: https://registry.npmjs.org/@testing-library/jest-dom/-/jest-dom-6.6.3.tgz} engines: {node: '>=14', npm: '>=6', yarn: '>=1'} '@testing-library/react-hooks@8.0.1': - resolution: {integrity: sha512-Aqhl2IVmLt8IovEVarNDFuJDVWVvhnr9/GCU6UUnrYXwgDFF9h2L2o2P9KBni1AST5sT6riAyoukFLyjQUgD/g==} + resolution: {integrity: sha512-Aqhl2IVmLt8IovEVarNDFuJDVWVvhnr9/GCU6UUnrYXwgDFF9h2L2o2P9KBni1AST5sT6riAyoukFLyjQUgD/g==, tarball: https://registry.npmjs.org/@testing-library/react-hooks/-/react-hooks-8.0.1.tgz} engines: {node: '>=12'} peerDependencies: '@types/react': ^16.9.0 || ^17.0.0 @@ -6362,7 +6389,7 @@ packages: optional: true '@testing-library/react@16.1.0': - resolution: {integrity: sha512-Q2ToPvg0KsVL0ohND9A3zLJWcOXXcO8IDu3fj11KhNt0UlCWyFyvnCIBkd12tidB2lkiVRG8VFqdhcqhqnAQtg==} + resolution: {integrity: sha512-Q2ToPvg0KsVL0ohND9A3zLJWcOXXcO8IDu3fj11KhNt0UlCWyFyvnCIBkd12tidB2lkiVRG8VFqdhcqhqnAQtg==, tarball: https://registry.npmjs.org/@testing-library/react/-/react-16.1.0.tgz} engines: {node: '>=18'} peerDependencies: '@testing-library/dom': ^10.0.0 @@ -6377,26 +6404,26 @@ packages: optional: true '@testing-library/user-event@14.6.1': - resolution: {integrity: sha512-vq7fv0rnt+QTXgPxr5Hjc210p6YKq2kmdziLgnsZGgLJ9e6VAShx1pACLuRjd/AS/sr7phAR58OIIpf0LlmQNw==} + resolution: {integrity: sha512-vq7fv0rnt+QTXgPxr5Hjc210p6YKq2kmdziLgnsZGgLJ9e6VAShx1pACLuRjd/AS/sr7phAR58OIIpf0LlmQNw==, tarball: https://registry.npmjs.org/@testing-library/user-event/-/user-event-14.6.1.tgz} engines: {node: '>=12', npm: '>=6'} peerDependencies: '@testing-library/dom': '>=7.21.4' '@tinymce/tinymce-angular@7.0.0': - resolution: {integrity: sha512-IKNaG/ihlxE1XCfq6lzULbnsqZO9KNJtlpu5jo6JDJDL9zcFzj/N2A16Kk7rTj1yfmDoB1IXAk/BpMOvgDY8cg==} + resolution: {integrity: sha512-IKNaG/ihlxE1XCfq6lzULbnsqZO9KNJtlpu5jo6JDJDL9zcFzj/N2A16Kk7rTj1yfmDoB1IXAk/BpMOvgDY8cg==, tarball: https://registry.npmjs.org/@tinymce/tinymce-angular/-/tinymce-angular-7.0.0.tgz} peerDependencies: '@angular/common': '>=14.0.0' '@angular/core': '>=14.0.0' '@angular/forms': '>=14.0.0' '@tinymce/tinymce-react@5.1.1': - resolution: {integrity: sha512-DQ0wpvnf/9z8RsOEAmrWZ1DN1PKqcQHfU+DpM3llLze7FHmxVtzuN8O+FYh0oAAF4stzAXwiCIVacfqjMwRieQ==} + resolution: {integrity: sha512-DQ0wpvnf/9z8RsOEAmrWZ1DN1PKqcQHfU+DpM3llLze7FHmxVtzuN8O+FYh0oAAF4stzAXwiCIVacfqjMwRieQ==, tarball: https://registry.npmjs.org/@tinymce/tinymce-react/-/tinymce-react-5.1.1.tgz} peerDependencies: react: ^18.0.0 || ^17.0.1 || ^16.7.0 react-dom: ^18.0.0 || ^17.0.1 || ^16.7.0 '@tinymce/tinymce-vue@6.3.0': - resolution: {integrity: sha512-DSP8Jhd3XqCCliTnusfbmz3D8GqQ4iRzkc4aadYHDcJPVjkaqopJ61McOdH82CSy599vGLkPjGzqJYWJkRMiUA==} + resolution: {integrity: sha512-DSP8Jhd3XqCCliTnusfbmz3D8GqQ4iRzkc4aadYHDcJPVjkaqopJ61McOdH82CSy599vGLkPjGzqJYWJkRMiUA==, tarball: https://registry.npmjs.org/@tinymce/tinymce-vue/-/tinymce-vue-6.3.0.tgz} peerDependencies: tinymce: ^8.0.0 || ^7.0.0 || ^6.0.0 || ^5.5.1 vue: ^3.0.0 @@ -6405,39 +6432,39 @@ packages: optional: true '@tiptap/core@3.22.2': - resolution: {integrity: sha512-atq35NkpeEphH6vNYJ0pTLLBA73FAbvTV9Ovd3AaTC5s99/KF5Q86zVJXvml8xPRcMGM6dLp+eSSd06oTscMSA==} + resolution: {integrity: sha512-atq35NkpeEphH6vNYJ0pTLLBA73FAbvTV9Ovd3AaTC5s99/KF5Q86zVJXvml8xPRcMGM6dLp+eSSd06oTscMSA==, tarball: https://registry.npmjs.org/@tiptap/core/-/core-3.22.2.tgz} peerDependencies: '@tiptap/pm': ^3.22.2 '@tiptap/extension-blockquote@3.27.4': - resolution: {integrity: sha512-d1tOHgP3R5cOE+Ot8qL/dkLXRByajgn+j6cCXHqDtmJO2wsK9knmbKQ0SEjbKrU6OgHrTnY/EotNxBEBW9HGoA==} + resolution: {integrity: sha512-d1tOHgP3R5cOE+Ot8qL/dkLXRByajgn+j6cCXHqDtmJO2wsK9knmbKQ0SEjbKrU6OgHrTnY/EotNxBEBW9HGoA==, tarball: https://registry.npmjs.org/@tiptap/extension-blockquote/-/extension-blockquote-3.27.4.tgz} peerDependencies: '@tiptap/core': 3.22.2 '@tiptap/pm': 3.27.4 '@tiptap/extension-bold@3.27.4': - resolution: {integrity: sha512-wTtJUUAxCAZ01ICH2DNlOBzzHKRQ1ZST8aRYtIhBPzqEUhnJaKGcjnDB4X49fqPi48iXaPxzhsInDl+rVUujWg==} + resolution: {integrity: sha512-wTtJUUAxCAZ01ICH2DNlOBzzHKRQ1ZST8aRYtIhBPzqEUhnJaKGcjnDB4X49fqPi48iXaPxzhsInDl+rVUujWg==, tarball: https://registry.npmjs.org/@tiptap/extension-bold/-/extension-bold-3.27.4.tgz} peerDependencies: '@tiptap/core': 3.22.2 '@tiptap/extension-bubble-menu@3.22.2': - resolution: {integrity: sha512-5hbyDOSkJwA2uh0v9Mm0Dd9bb9inx6tHBEDSH2tCB9Rm23poz3yOreB7SNX8xDMe5L0/PQesfWC14RitcmhKPg==} + resolution: {integrity: sha512-5hbyDOSkJwA2uh0v9Mm0Dd9bb9inx6tHBEDSH2tCB9Rm23poz3yOreB7SNX8xDMe5L0/PQesfWC14RitcmhKPg==, tarball: https://registry.npmjs.org/@tiptap/extension-bubble-menu/-/extension-bubble-menu-3.22.2.tgz} peerDependencies: '@tiptap/core': 3.22.2 '@tiptap/pm': ^3.22.2 '@tiptap/extension-bullet-list@3.27.4': - resolution: {integrity: sha512-rvja0N1RnwGJAVwDdbUfDIJ4NoT+KjPFaZudKiPuEMfMHfbqe4xcbbC2hsfs61JNcl2xmx+ohV6lzD9YxxJl1w==} + resolution: {integrity: sha512-rvja0N1RnwGJAVwDdbUfDIJ4NoT+KjPFaZudKiPuEMfMHfbqe4xcbbC2hsfs61JNcl2xmx+ohV6lzD9YxxJl1w==, tarball: https://registry.npmjs.org/@tiptap/extension-bullet-list/-/extension-bullet-list-3.27.4.tgz} peerDependencies: '@tiptap/extension-list': 3.27.4 '@tiptap/extension-character-count@3.22.2': - resolution: {integrity: sha512-EBTVHbRkv5IhoO/TAij4ivZ2RD2u6aiZHtWuhHVbsVsHfoxSi7YGjVNFv/DnT/BrEwpNy3u/SI/Xo57120HTOA==} + resolution: {integrity: sha512-EBTVHbRkv5IhoO/TAij4ivZ2RD2u6aiZHtWuhHVbsVsHfoxSi7YGjVNFv/DnT/BrEwpNy3u/SI/Xo57120HTOA==, tarball: https://registry.npmjs.org/@tiptap/extension-character-count/-/extension-character-count-3.22.2.tgz} peerDependencies: '@tiptap/extensions': ^3.22.2 '@tiptap/extension-code-block-lowlight@3.22.2': - resolution: {integrity: sha512-z3OUuNulh2ehHPnMw4PLEt4JvR8Xy9GEqaDLDADIU+hfk6ztrbhweGm1evZ6fzUI00274NZQCNNtcUwZSa3IHw==} + resolution: {integrity: sha512-z3OUuNulh2ehHPnMw4PLEt4JvR8Xy9GEqaDLDADIU+hfk6ztrbhweGm1evZ6fzUI00274NZQCNNtcUwZSa3IHw==, tarball: https://registry.npmjs.org/@tiptap/extension-code-block-lowlight/-/extension-code-block-lowlight-3.22.2.tgz} peerDependencies: '@tiptap/core': 3.22.2 '@tiptap/extension-code-block': ^3.22.2 @@ -6446,18 +6473,18 @@ packages: lowlight: ^2 || ^3 '@tiptap/extension-code-block@3.22.2': - resolution: {integrity: sha512-PEwFlDyvtKF19WCrOFg77qJV9WqhvjCY4ZoXlHP9Hx0KTcOA8W39mtw8d4NWU5pLRK94yHKF1DVVL8UUkEOnww==} + resolution: {integrity: sha512-PEwFlDyvtKF19WCrOFg77qJV9WqhvjCY4ZoXlHP9Hx0KTcOA8W39mtw8d4NWU5pLRK94yHKF1DVVL8UUkEOnww==, tarball: https://registry.npmjs.org/@tiptap/extension-code-block/-/extension-code-block-3.22.2.tgz} peerDependencies: '@tiptap/core': 3.22.2 '@tiptap/pm': ^3.22.2 '@tiptap/extension-code@3.27.4': - resolution: {integrity: sha512-aPc7opCR1ylK4m4c2lsjLsGpEBD1fLQQKWd5PbZiJvrTF8gkdGZlYLt9A6VukpxeJyHhb22Jaj4fxgKmGMeTtw==} + resolution: {integrity: sha512-aPc7opCR1ylK4m4c2lsjLsGpEBD1fLQQKWd5PbZiJvrTF8gkdGZlYLt9A6VukpxeJyHhb22Jaj4fxgKmGMeTtw==, tarball: https://registry.npmjs.org/@tiptap/extension-code/-/extension-code-3.27.4.tgz} peerDependencies: '@tiptap/core': 3.22.2 '@tiptap/extension-collaboration@3.22.2': - resolution: {integrity: sha512-+viAk2EVoYgJEmJpvnT1NBCK+intvwHEMp7T7luYffkQz8irGKF/7YcgauXp5NBLPTsnIzDWQuY571mo8XMcKg==} + resolution: {integrity: sha512-+viAk2EVoYgJEmJpvnT1NBCK+intvwHEMp7T7luYffkQz8irGKF/7YcgauXp5NBLPTsnIzDWQuY571mo8XMcKg==, tarball: https://registry.npmjs.org/@tiptap/extension-collaboration/-/extension-collaboration-3.22.2.tgz} peerDependencies: '@tiptap/core': 3.22.2 '@tiptap/pm': ^3.22.2 @@ -6465,12 +6492,12 @@ packages: yjs: ^13 '@tiptap/extension-document@3.22.2': - resolution: {integrity: sha512-yPw9pQeVC4QDh86TuyKCZxxM4g0NAw7mEtGnAo6EpxaBQr1wyBr9yFpys+QTsQpRTmyTf1VHp4iTTLuWHMljIw==} + resolution: {integrity: sha512-yPw9pQeVC4QDh86TuyKCZxxM4g0NAw7mEtGnAo6EpxaBQr1wyBr9yFpys+QTsQpRTmyTf1VHp4iTTLuWHMljIw==, tarball: https://registry.npmjs.org/@tiptap/extension-document/-/extension-document-3.22.2.tgz} peerDependencies: '@tiptap/core': 3.22.2 '@tiptap/extension-drag-handle@3.22.2': - resolution: {integrity: sha512-9L2krYNe+ZxI7hULAuxE0i9wKMxL8eIoiH866hrOenb2C8PySQLWy/BjWwu3Z6fBFwCG+29wiMeRL7WE128oxg==} + resolution: {integrity: sha512-9L2krYNe+ZxI7hULAuxE0i9wKMxL8eIoiH866hrOenb2C8PySQLWy/BjWwu3Z6fBFwCG+29wiMeRL7WE128oxg==, tarball: https://registry.npmjs.org/@tiptap/extension-drag-handle/-/extension-drag-handle-3.22.2.tgz} peerDependencies: '@tiptap/core': 3.22.2 '@tiptap/extension-collaboration': ^3.22.2 @@ -6479,181 +6506,181 @@ packages: '@tiptap/y-tiptap': ^3.0.2 '@tiptap/extension-dropcursor@3.27.4': - resolution: {integrity: sha512-RiZasQJuUTUO3aME16Bn8eJH7cYnvhT5JCFDFq0ya/1iFI9wUQA2NJC5tb5TrZ74+sQwkYU9VzexnchM481Y9w==} + resolution: {integrity: sha512-RiZasQJuUTUO3aME16Bn8eJH7cYnvhT5JCFDFq0ya/1iFI9wUQA2NJC5tb5TrZ74+sQwkYU9VzexnchM481Y9w==, tarball: https://registry.npmjs.org/@tiptap/extension-dropcursor/-/extension-dropcursor-3.27.4.tgz} peerDependencies: '@tiptap/extensions': 3.27.4 '@tiptap/extension-emoji@3.22.2': - resolution: {integrity: sha512-XvuJdV8XMu9of5LfvpFmZZtSbHEbFxAkxNd07vAjxD6AXJiuSMH6stDnfjsSAd5tSoKjDwPoilmvh83Y+8kIcQ==} + resolution: {integrity: sha512-XvuJdV8XMu9of5LfvpFmZZtSbHEbFxAkxNd07vAjxD6AXJiuSMH6stDnfjsSAd5tSoKjDwPoilmvh83Y+8kIcQ==, tarball: https://registry.npmjs.org/@tiptap/extension-emoji/-/extension-emoji-3.22.2.tgz} peerDependencies: '@tiptap/core': 3.22.2 '@tiptap/pm': ^3.22.2 '@tiptap/suggestion': ^3.22.2 '@tiptap/extension-floating-menu@3.22.2': - resolution: {integrity: sha512-r0ZTeh9rNtj9Api+G0YyaB+tAKPDn7aYWg+qSrmAC5EyUPee6Zjn3zlw0q4renCeQflvNRK20xHM8zokC41jOA==} + resolution: {integrity: sha512-r0ZTeh9rNtj9Api+G0YyaB+tAKPDn7aYWg+qSrmAC5EyUPee6Zjn3zlw0q4renCeQflvNRK20xHM8zokC41jOA==, tarball: https://registry.npmjs.org/@tiptap/extension-floating-menu/-/extension-floating-menu-3.22.2.tgz} peerDependencies: '@floating-ui/dom': ^1.0.0 '@tiptap/core': 3.22.2 '@tiptap/pm': ^3.22.2 '@tiptap/extension-gapcursor@3.27.4': - resolution: {integrity: sha512-svLwSKcFhzpcJeXvxxKkRFuQpykmXrQefVhEsaXq0L95yJIIAGKMRmQC3mxKdzL2j0P9cY7V41bNVSyOAyvclw==} + resolution: {integrity: sha512-svLwSKcFhzpcJeXvxxKkRFuQpykmXrQefVhEsaXq0L95yJIIAGKMRmQC3mxKdzL2j0P9cY7V41bNVSyOAyvclw==, tarball: https://registry.npmjs.org/@tiptap/extension-gapcursor/-/extension-gapcursor-3.27.4.tgz} peerDependencies: '@tiptap/extensions': 3.27.4 '@tiptap/extension-hard-break@3.27.4': - resolution: {integrity: sha512-W+Z9pmDgqjbdu3NeZOQrzA15iM4w60Yd8l2CYzxcdApPVIfYzb2S3a7+u1RqW9wnTYb6xyZjASmFNfxXS4P4cg==} + resolution: {integrity: sha512-W+Z9pmDgqjbdu3NeZOQrzA15iM4w60Yd8l2CYzxcdApPVIfYzb2S3a7+u1RqW9wnTYb6xyZjASmFNfxXS4P4cg==, tarball: https://registry.npmjs.org/@tiptap/extension-hard-break/-/extension-hard-break-3.27.4.tgz} peerDependencies: '@tiptap/core': 3.22.2 '@tiptap/extension-heading@3.22.2': - resolution: {integrity: sha512-QPHLef+ikAyf7RVc4EdGeKxH4OEGb3ueCEwJ41RcYPtZ1BX9ueei7FC936guTdL1U7w3vQ65qfy86HznzkYgvw==} + resolution: {integrity: sha512-QPHLef+ikAyf7RVc4EdGeKxH4OEGb3ueCEwJ41RcYPtZ1BX9ueei7FC936guTdL1U7w3vQ65qfy86HznzkYgvw==, tarball: https://registry.npmjs.org/@tiptap/extension-heading/-/extension-heading-3.22.2.tgz} peerDependencies: '@tiptap/core': 3.22.2 '@tiptap/extension-highlight@3.22.2': - resolution: {integrity: sha512-ecJ5HnCSlUW65xZlqkqz0nN8yhGzp+91HIPKjafPurV4jseUy1O77FthQ6KiZBQFipeqN04tkqEiFt918ydWUQ==} + resolution: {integrity: sha512-ecJ5HnCSlUW65xZlqkqz0nN8yhGzp+91HIPKjafPurV4jseUy1O77FthQ6KiZBQFipeqN04tkqEiFt918ydWUQ==, tarball: https://registry.npmjs.org/@tiptap/extension-highlight/-/extension-highlight-3.22.2.tgz} peerDependencies: '@tiptap/core': 3.22.2 '@tiptap/extension-horizontal-rule@3.27.4': - resolution: {integrity: sha512-2eQU/55nE5mhMJHALtLMuBL3dcVJUDVVT7n+uZYMaYE63BtCvC4VS08YLFSR7JZSVJIlgVAmdt5nAw0B+rEPNA==} + resolution: {integrity: sha512-2eQU/55nE5mhMJHALtLMuBL3dcVJUDVVT7n+uZYMaYE63BtCvC4VS08YLFSR7JZSVJIlgVAmdt5nAw0B+rEPNA==, tarball: https://registry.npmjs.org/@tiptap/extension-horizontal-rule/-/extension-horizontal-rule-3.27.4.tgz} peerDependencies: '@tiptap/core': 3.22.2 '@tiptap/pm': 3.27.4 '@tiptap/extension-image@3.22.2': - resolution: {integrity: sha512-xFCgwreF6sn5mQ/hFDQKn41NIbbfks/Ou9j763Djf3pWsastgzdgwifQOpXVI3aSsqlKUO3o8/8R/yQczvZcwg==} + resolution: {integrity: sha512-xFCgwreF6sn5mQ/hFDQKn41NIbbfks/Ou9j763Djf3pWsastgzdgwifQOpXVI3aSsqlKUO3o8/8R/yQczvZcwg==, tarball: https://registry.npmjs.org/@tiptap/extension-image/-/extension-image-3.22.2.tgz} peerDependencies: '@tiptap/core': 3.22.2 '@tiptap/extension-italic@3.27.4': - resolution: {integrity: sha512-PeZT4XbyxAp7Lqo/hfA1k5LI27g1RlgS+YgXp2CeHXIrUfSpO5HlZXh02Bvb0pOdl3RFw2tEKtlHzjt8Y1+Nwg==} + resolution: {integrity: sha512-PeZT4XbyxAp7Lqo/hfA1k5LI27g1RlgS+YgXp2CeHXIrUfSpO5HlZXh02Bvb0pOdl3RFw2tEKtlHzjt8Y1+Nwg==, tarball: https://registry.npmjs.org/@tiptap/extension-italic/-/extension-italic-3.27.4.tgz} peerDependencies: '@tiptap/core': 3.22.2 '@tiptap/extension-link@3.22.2': - resolution: {integrity: sha512-TXfSoKmng5pecvQUZqdsx6ICeob5V5hhYOj2vCEtjfcjWsyCndqFIl1w+Nt/yI5ehrFNOVPyj3ZvcELuuAW6pw==} + resolution: {integrity: sha512-TXfSoKmng5pecvQUZqdsx6ICeob5V5hhYOj2vCEtjfcjWsyCndqFIl1w+Nt/yI5ehrFNOVPyj3ZvcELuuAW6pw==, tarball: https://registry.npmjs.org/@tiptap/extension-link/-/extension-link-3.22.2.tgz} peerDependencies: '@tiptap/core': 3.22.2 '@tiptap/pm': ^3.22.2 '@tiptap/extension-list-item@3.27.4': - resolution: {integrity: sha512-z5TVuPw2mkK0B/x+gFg3uUV7tBdaElDFg0zVgnXZCqlSVTLfIyInOOnG5LTWoAd9BdzBjGrzE3PohDcLVDDGBQ==} + resolution: {integrity: sha512-z5TVuPw2mkK0B/x+gFg3uUV7tBdaElDFg0zVgnXZCqlSVTLfIyInOOnG5LTWoAd9BdzBjGrzE3PohDcLVDDGBQ==, tarball: https://registry.npmjs.org/@tiptap/extension-list-item/-/extension-list-item-3.27.4.tgz} peerDependencies: '@tiptap/extension-list': 3.27.4 '@tiptap/extension-list-keymap@3.27.4': - resolution: {integrity: sha512-on7JNDi7Eqz7UdZeZdiO83bQHo0flVDHzjmtR+v/nrCGW9H15D3CHs5+4ozLDiCvTK8tbkBuut/l9AWNxcCE/Q==} + resolution: {integrity: sha512-on7JNDi7Eqz7UdZeZdiO83bQHo0flVDHzjmtR+v/nrCGW9H15D3CHs5+4ozLDiCvTK8tbkBuut/l9AWNxcCE/Q==, tarball: https://registry.npmjs.org/@tiptap/extension-list-keymap/-/extension-list-keymap-3.27.4.tgz} peerDependencies: '@tiptap/extension-list': 3.27.4 '@tiptap/extension-list@3.27.4': - resolution: {integrity: sha512-A0BgmRO1RE0yLCx9w7GQITtKfS9wLE5cdngSYDiSpwulcXJhJjKm5mZ4OUZmks2VN4HO5jMl2BWCGt2NSDhA+w==} + resolution: {integrity: sha512-A0BgmRO1RE0yLCx9w7GQITtKfS9wLE5cdngSYDiSpwulcXJhJjKm5mZ4OUZmks2VN4HO5jMl2BWCGt2NSDhA+w==, tarball: https://registry.npmjs.org/@tiptap/extension-list/-/extension-list-3.27.4.tgz} peerDependencies: '@tiptap/core': 3.22.2 '@tiptap/pm': 3.27.4 '@tiptap/extension-node-range@3.22.2': - resolution: {integrity: sha512-hipsIUXrU9RUcc32BLJ/mtfiCtgV35oMTMxEJTJWxJhebEw0iWd7L6cLwHbKui6HgH4W82Zo1s1Ia0Owq3Nu8w==} + resolution: {integrity: sha512-hipsIUXrU9RUcc32BLJ/mtfiCtgV35oMTMxEJTJWxJhebEw0iWd7L6cLwHbKui6HgH4W82Zo1s1Ia0Owq3Nu8w==, tarball: https://registry.npmjs.org/@tiptap/extension-node-range/-/extension-node-range-3.22.2.tgz} peerDependencies: '@tiptap/core': 3.22.2 '@tiptap/pm': ^3.22.2 '@tiptap/extension-ordered-list@3.27.4': - resolution: {integrity: sha512-bHwLiof0FqJfWzB0act7oEKMTZatEKQ4IYCvmyF5EktjMs4kxEatkPp4Yx/1LSYSjLy1MMT7oLELyaz2FFYyXA==} + resolution: {integrity: sha512-bHwLiof0FqJfWzB0act7oEKMTZatEKQ4IYCvmyF5EktjMs4kxEatkPp4Yx/1LSYSjLy1MMT7oLELyaz2FFYyXA==, tarball: https://registry.npmjs.org/@tiptap/extension-ordered-list/-/extension-ordered-list-3.27.4.tgz} peerDependencies: '@tiptap/extension-list': 3.27.4 '@tiptap/extension-paragraph@3.22.2': - resolution: {integrity: sha512-EHZZzxVhvzEPDPWtRBF1YKhB+WCUjd1C2NhjHfL3Dl71PBqM3ZWA6qN7NDGPyNyGGWauui/NR/4X+5AfPqlHyA==} + resolution: {integrity: sha512-EHZZzxVhvzEPDPWtRBF1YKhB+WCUjd1C2NhjHfL3Dl71PBqM3ZWA6qN7NDGPyNyGGWauui/NR/4X+5AfPqlHyA==, tarball: https://registry.npmjs.org/@tiptap/extension-paragraph/-/extension-paragraph-3.22.2.tgz} peerDependencies: '@tiptap/core': 3.22.2 '@tiptap/extension-placeholder@3.22.2': - resolution: {integrity: sha512-xYw733CmSeG7MyYBDdV5NFiwlBdXXzw4Mvjb2t4QRXagkDbHeNY/LtKTcrtcMNfO4Jx0mwivGQZUIEC8oAfvxg==} + resolution: {integrity: sha512-xYw733CmSeG7MyYBDdV5NFiwlBdXXzw4Mvjb2t4QRXagkDbHeNY/LtKTcrtcMNfO4Jx0mwivGQZUIEC8oAfvxg==, tarball: https://registry.npmjs.org/@tiptap/extension-placeholder/-/extension-placeholder-3.22.2.tgz} peerDependencies: '@tiptap/extensions': ^3.22.2 '@tiptap/extension-strike@3.27.4': - resolution: {integrity: sha512-8OXwcPKuV3ToBBgyvDxH1jQdObK5FIKCGiyIim6qNWiOpi9BhM3XYD+aO1khjv8qIjtoI/DYbizF4ewj09fX2g==} + resolution: {integrity: sha512-8OXwcPKuV3ToBBgyvDxH1jQdObK5FIKCGiyIim6qNWiOpi9BhM3XYD+aO1khjv8qIjtoI/DYbizF4ewj09fX2g==, tarball: https://registry.npmjs.org/@tiptap/extension-strike/-/extension-strike-3.27.4.tgz} peerDependencies: '@tiptap/core': 3.22.2 '@tiptap/extension-subscript@3.22.2': - resolution: {integrity: sha512-J1wkSlbk7LTE9QRRFDtrIARST2TR9PFl7SIjXxxJwtBdBAJBqRYmioG4m44cFbbmwHDBLOoSs3JTb95Sx+OiAQ==} + resolution: {integrity: sha512-J1wkSlbk7LTE9QRRFDtrIARST2TR9PFl7SIjXxxJwtBdBAJBqRYmioG4m44cFbbmwHDBLOoSs3JTb95Sx+OiAQ==, tarball: https://registry.npmjs.org/@tiptap/extension-subscript/-/extension-subscript-3.22.2.tgz} peerDependencies: '@tiptap/core': 3.22.2 '@tiptap/pm': ^3.22.2 '@tiptap/extension-superscript@3.22.2': - resolution: {integrity: sha512-TNMqn/0EGjRKPooCRq7uBBwk0Khj+AmSfJ/7+GC/QlvHOgL8/tpgisLOqPih9dMdp5YNTLlpdeI6SkA1VikBEw==} + resolution: {integrity: sha512-TNMqn/0EGjRKPooCRq7uBBwk0Khj+AmSfJ/7+GC/QlvHOgL8/tpgisLOqPih9dMdp5YNTLlpdeI6SkA1VikBEw==, tarball: https://registry.npmjs.org/@tiptap/extension-superscript/-/extension-superscript-3.22.2.tgz} peerDependencies: '@tiptap/core': 3.22.2 '@tiptap/pm': ^3.22.2 '@tiptap/extension-table-cell@3.22.2': - resolution: {integrity: sha512-3+YUNtZRHrl6jqQ/RyoGq9iSdXVKwUw3awgu/ogdUvaanXLyESrncbWsEiRzo98PDa4m6hFvjFZ5yhw3cXEhGQ==} + resolution: {integrity: sha512-3+YUNtZRHrl6jqQ/RyoGq9iSdXVKwUw3awgu/ogdUvaanXLyESrncbWsEiRzo98PDa4m6hFvjFZ5yhw3cXEhGQ==, tarball: https://registry.npmjs.org/@tiptap/extension-table-cell/-/extension-table-cell-3.22.2.tgz} peerDependencies: '@tiptap/extension-table': ^3.22.2 '@tiptap/extension-table-header@3.22.2': - resolution: {integrity: sha512-tVqbgl+it314/zzziKuOyRk2O1qptqiclYOfZKl0+ir5pgsVrUczujxzkDAPe4DPEZm/mSjWlsaYpF5OBQU0ng==} + resolution: {integrity: sha512-tVqbgl+it314/zzziKuOyRk2O1qptqiclYOfZKl0+ir5pgsVrUczujxzkDAPe4DPEZm/mSjWlsaYpF5OBQU0ng==, tarball: https://registry.npmjs.org/@tiptap/extension-table-header/-/extension-table-header-3.22.2.tgz} peerDependencies: '@tiptap/extension-table': ^3.22.2 '@tiptap/extension-table-row@3.22.2': - resolution: {integrity: sha512-n2IDQhThOwRU+vxYj3aGYp66P45r3lgBkWBCGFPLFSL8bx/7p7ZifEtzsk6FOmzNa/GzgKT0lq2RvWVILq/rLA==} + resolution: {integrity: sha512-n2IDQhThOwRU+vxYj3aGYp66P45r3lgBkWBCGFPLFSL8bx/7p7ZifEtzsk6FOmzNa/GzgKT0lq2RvWVILq/rLA==, tarball: https://registry.npmjs.org/@tiptap/extension-table-row/-/extension-table-row-3.22.2.tgz} peerDependencies: '@tiptap/extension-table': ^3.22.2 '@tiptap/extension-table@3.22.2': - resolution: {integrity: sha512-J9fVsboNRgmdbCVxWl+zlm5FKHmx6TnUHAb+7yt6Fum9lqy1/TwEfP3N7DAF3v7qpkIniVlU3X9ERmiiTAWxSA==} + resolution: {integrity: sha512-J9fVsboNRgmdbCVxWl+zlm5FKHmx6TnUHAb+7yt6Fum9lqy1/TwEfP3N7DAF3v7qpkIniVlU3X9ERmiiTAWxSA==, tarball: https://registry.npmjs.org/@tiptap/extension-table/-/extension-table-3.22.2.tgz} peerDependencies: '@tiptap/core': 3.22.2 '@tiptap/pm': ^3.22.2 '@tiptap/extension-text-align@3.22.2': - resolution: {integrity: sha512-pgqyXzVHo4WmDhK26rDwhK2lxQwnjl/9DP816C2k3To/fZRK1eW7q0pSAYteHWmKkaYAxwj/0UvCU0nXKlPujw==} + resolution: {integrity: sha512-pgqyXzVHo4WmDhK26rDwhK2lxQwnjl/9DP816C2k3To/fZRK1eW7q0pSAYteHWmKkaYAxwj/0UvCU0nXKlPujw==, tarball: https://registry.npmjs.org/@tiptap/extension-text-align/-/extension-text-align-3.22.2.tgz} peerDependencies: '@tiptap/core': 3.22.2 '@tiptap/extension-text@3.22.2': - resolution: {integrity: sha512-J1w7JwijfSD7ah0WfiwZ/DVWCIGT9x369RM4RJc57i44mIBElj7tl1dh+N5KPGOXKUup4gr7sSJAE38lgeaDMg==} + resolution: {integrity: sha512-J1w7JwijfSD7ah0WfiwZ/DVWCIGT9x369RM4RJc57i44mIBElj7tl1dh+N5KPGOXKUup4gr7sSJAE38lgeaDMg==, tarball: https://registry.npmjs.org/@tiptap/extension-text/-/extension-text-3.22.2.tgz} peerDependencies: '@tiptap/core': 3.22.2 '@tiptap/extension-underline@3.22.2': - resolution: {integrity: sha512-BaV6WOowxdkGTLWiU7DdZ3Twh633O4RGqwUM5dDas5LvaqL8AMWGTO8Wg9yAaaKXzd9MtKI1ZCqS/+MtzusgkQ==} + resolution: {integrity: sha512-BaV6WOowxdkGTLWiU7DdZ3Twh633O4RGqwUM5dDas5LvaqL8AMWGTO8Wg9yAaaKXzd9MtKI1ZCqS/+MtzusgkQ==, tarball: https://registry.npmjs.org/@tiptap/extension-underline/-/extension-underline-3.22.2.tgz} peerDependencies: '@tiptap/core': 3.22.2 '@tiptap/extension-youtube@3.22.2': - resolution: {integrity: sha512-wSLswwaLW+LWxe1/PtKzALeeAUS+LGLJfwFJHYTyc+EkqqpQSi2PhDwFx8m9+ADmb8UvjF2Hsg3cha1KrFAJEg==} + resolution: {integrity: sha512-wSLswwaLW+LWxe1/PtKzALeeAUS+LGLJfwFJHYTyc+EkqqpQSi2PhDwFx8m9+ADmb8UvjF2Hsg3cha1KrFAJEg==, tarball: https://registry.npmjs.org/@tiptap/extension-youtube/-/extension-youtube-3.22.2.tgz} peerDependencies: '@tiptap/core': 3.22.2 '@tiptap/extensions@3.22.2': - resolution: {integrity: sha512-s7MZmm2Xdq+8feIXgY3v7gVpQ5ClqBZi20KheouS7KSbBlrY4fu2irYR1EGc6r1UUVaHMxEa+cx5knhx+mIPUw==} + resolution: {integrity: sha512-s7MZmm2Xdq+8feIXgY3v7gVpQ5ClqBZi20KheouS7KSbBlrY4fu2irYR1EGc6r1UUVaHMxEa+cx5knhx+mIPUw==, tarball: https://registry.npmjs.org/@tiptap/extensions/-/extensions-3.22.2.tgz} peerDependencies: '@tiptap/core': 3.22.2 '@tiptap/pm': ^3.22.2 '@tiptap/pm@3.22.2': - resolution: {integrity: sha512-G2ENwIazoSKkAnN5MN5yN91TIZNFm6TxB74kPf3Empr2k9W51Hkcier70jHGpArhgcEaL4BVreuU1PRDRwCeGw==} + resolution: {integrity: sha512-G2ENwIazoSKkAnN5MN5yN91TIZNFm6TxB74kPf3Empr2k9W51Hkcier70jHGpArhgcEaL4BVreuU1PRDRwCeGw==, tarball: https://registry.npmjs.org/@tiptap/pm/-/pm-3.22.2.tgz} '@tiptap/starter-kit@3.22.2': - resolution: {integrity: sha512-+CCKX8tOQ/ZPb2k/z6em4AQCFYAcdd8+0TOzPWiuLxRyCHRPBBVhnPsXOKgKwE4OO3E8BsezquuYRYRwsyzCqg==} + resolution: {integrity: sha512-+CCKX8tOQ/ZPb2k/z6em4AQCFYAcdd8+0TOzPWiuLxRyCHRPBBVhnPsXOKgKwE4OO3E8BsezquuYRYRwsyzCqg==, tarball: https://registry.npmjs.org/@tiptap/starter-kit/-/starter-kit-3.22.2.tgz} '@tiptap/suggestion@3.22.2': - resolution: {integrity: sha512-t2GQSrF4eQyPb+KqXVfcC2cokYIDNfpLLq7B0ELlnWBJURnLOVJ2ssJ6ASI247scu9ZKPG1g5bFP4IXdBhyPgg==} + resolution: {integrity: sha512-t2GQSrF4eQyPb+KqXVfcC2cokYIDNfpLLq7B0ELlnWBJURnLOVJ2ssJ6ASI247scu9ZKPG1g5bFP4IXdBhyPgg==, tarball: https://registry.npmjs.org/@tiptap/suggestion/-/suggestion-3.22.2.tgz} peerDependencies: '@tiptap/core': 3.22.2 '@tiptap/pm': ^3.22.2 '@tiptap/y-tiptap@3.0.3': - resolution: {integrity: sha512-8UvuV4lTisCE9cMTc/X8kRyTn9edUO7Kball0I6wb17VwZSjNDfh/YKtP4O5vcPawEzFHQIvZGq/k1h37kAf0w==} + resolution: {integrity: sha512-8UvuV4lTisCE9cMTc/X8kRyTn9edUO7Kball0I6wb17VwZSjNDfh/YKtP4O5vcPawEzFHQIvZGq/k1h37kAf0w==, tarball: https://registry.npmjs.org/@tiptap/y-tiptap/-/y-tiptap-3.0.3.tgz} engines: {node: '>=16.0.0', npm: '>=8.0.0'} peerDependencies: prosemirror-model: ^1.7.1 @@ -6663,336 +6690,336 @@ packages: yjs: ^13.5.38 '@tootallnate/once@2.0.1': - resolution: {integrity: sha512-HqmEUIGRJ5fSXchkVgR5F7qn48bDBzv0kWj/Kfu5e6uci4UlEeng4331LnBkWffb++Ei3FOVLxo8JJWMFBDMeQ==} + resolution: {integrity: sha512-HqmEUIGRJ5fSXchkVgR5F7qn48bDBzv0kWj/Kfu5e6uci4UlEeng4331LnBkWffb++Ei3FOVLxo8JJWMFBDMeQ==, tarball: https://registry.npmjs.org/@tootallnate/once/-/once-2.0.1.tgz} engines: {node: '>= 10'} '@tsconfig/node10@1.0.12': - resolution: {integrity: sha512-UCYBaeFvM11aU2y3YPZ//O5Rhj+xKyzy7mvcIoAjASbigy8mHMryP5cK7dgjlz2hWxh1g5pLw084E0a/wlUSFQ==} + resolution: {integrity: sha512-UCYBaeFvM11aU2y3YPZ//O5Rhj+xKyzy7mvcIoAjASbigy8mHMryP5cK7dgjlz2hWxh1g5pLw084E0a/wlUSFQ==, tarball: https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.12.tgz} '@tsconfig/node12@1.0.11': - resolution: {integrity: sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==} + resolution: {integrity: sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==, tarball: https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz} '@tsconfig/node14@1.0.3': - resolution: {integrity: sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==} + resolution: {integrity: sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==, tarball: https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz} '@tsconfig/node16@1.0.4': - resolution: {integrity: sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==} + resolution: {integrity: sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==, tarball: https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz} '@tybys/wasm-util@0.10.3': - resolution: {integrity: sha512-F3fo1MYrRJYL3zER0OUOmkutjr1Vp23m7OsSgp7nq4SP6OqX6C/56XFIPAl5bt3zaBRjmW7SGz3u/6LwFpYcOg==} + resolution: {integrity: sha512-F3fo1MYrRJYL3zER0OUOmkutjr1Vp23m7OsSgp7nq4SP6OqX6C/56XFIPAl5bt3zaBRjmW7SGz3u/6LwFpYcOg==, tarball: https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.3.tgz} '@tybys/wasm-util@0.9.0': - resolution: {integrity: sha512-6+7nlbMVX/PVDCwaIQ8nTOPveOcFLSt8GcXdx8hD0bt39uWxYT88uXzqTd4fTvqta7oeUJqudepapKNt2DYJFw==} + resolution: {integrity: sha512-6+7nlbMVX/PVDCwaIQ8nTOPveOcFLSt8GcXdx8hD0bt39uWxYT88uXzqTd4fTvqta7oeUJqudepapKNt2DYJFw==, tarball: https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.9.0.tgz} '@types/argparse@1.0.38': - resolution: {integrity: sha512-ebDJ9b0e702Yr7pWgB0jzm+CX4Srzz8RcXtLJDJB+BSccqMa36uyH/zUsSYao5+BD1ytv3k3rPYCq4mAE1hsXA==} + resolution: {integrity: sha512-ebDJ9b0e702Yr7pWgB0jzm+CX4Srzz8RcXtLJDJB+BSccqMa36uyH/zUsSYao5+BD1ytv3k3rPYCq4mAE1hsXA==, tarball: https://registry.npmjs.org/@types/argparse/-/argparse-1.0.38.tgz} '@types/aria-query@5.0.4': - resolution: {integrity: sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==} + resolution: {integrity: sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==, tarball: https://registry.npmjs.org/@types/aria-query/-/aria-query-5.0.4.tgz} '@types/babel__core@7.20.5': - resolution: {integrity: sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==} + resolution: {integrity: sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==, tarball: https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz} '@types/babel__generator@7.27.0': - resolution: {integrity: sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==} + resolution: {integrity: sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==, tarball: https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.27.0.tgz} '@types/babel__template@7.4.4': - resolution: {integrity: sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==} + resolution: {integrity: sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==, tarball: https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz} '@types/babel__traverse@7.28.0': - resolution: {integrity: sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==} + resolution: {integrity: sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==, tarball: https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.28.0.tgz} '@types/body-parser@1.19.6': - resolution: {integrity: sha512-HLFeCYgz89uk22N5Qg3dvGvsv46B8GLvKKo1zKG4NybA8U2DiEO3w9lqGg29t/tfLRJpJ6iQxnVw4OnB7MoM9g==} + resolution: {integrity: sha512-HLFeCYgz89uk22N5Qg3dvGvsv46B8GLvKKo1zKG4NybA8U2DiEO3w9lqGg29t/tfLRJpJ6iQxnVw4OnB7MoM9g==, tarball: https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.6.tgz} '@types/bonjour@3.5.13': - resolution: {integrity: sha512-z9fJ5Im06zvUL548KvYNecEVlA7cVDkGUi6kZusb04mpyEFKCIZJvloCcmpmLaIahDpOQGHaHmG6imtPMmPXGQ==} + resolution: {integrity: sha512-z9fJ5Im06zvUL548KvYNecEVlA7cVDkGUi6kZusb04mpyEFKCIZJvloCcmpmLaIahDpOQGHaHmG6imtPMmPXGQ==, tarball: https://registry.npmjs.org/@types/bonjour/-/bonjour-3.5.13.tgz} '@types/chai@5.2.3': - resolution: {integrity: sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==} + resolution: {integrity: sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==, tarball: https://registry.npmjs.org/@types/chai/-/chai-5.2.3.tgz} '@types/connect-history-api-fallback@1.5.4': - resolution: {integrity: sha512-n6Cr2xS1h4uAulPRdlw6Jl6s1oG8KrVilPN2yUITEs+K48EzMJJ3W1xy8K5eWuFvjp3R74AOIGSmp2UfBJ8HFw==} + resolution: {integrity: sha512-n6Cr2xS1h4uAulPRdlw6Jl6s1oG8KrVilPN2yUITEs+K48EzMJJ3W1xy8K5eWuFvjp3R74AOIGSmp2UfBJ8HFw==, tarball: https://registry.npmjs.org/@types/connect-history-api-fallback/-/connect-history-api-fallback-1.5.4.tgz} '@types/connect@3.4.38': - resolution: {integrity: sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==} + resolution: {integrity: sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==, tarball: https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz} '@types/d3-array@3.2.2': - resolution: {integrity: sha512-hOLWVbm7uRza0BYXpIIW5pxfrKe0W+D5lrFiAEYR+pb6w3N2SwSMaJbXdUfSEv+dT4MfHBLtn5js0LAWaO6otw==} + resolution: {integrity: sha512-hOLWVbm7uRza0BYXpIIW5pxfrKe0W+D5lrFiAEYR+pb6w3N2SwSMaJbXdUfSEv+dT4MfHBLtn5js0LAWaO6otw==, tarball: https://registry.npmjs.org/@types/d3-array/-/d3-array-3.2.2.tgz} '@types/d3-axis@3.0.6': - resolution: {integrity: sha512-pYeijfZuBd87T0hGn0FO1vQ/cgLk6E1ALJjfkC0oJ8cbwkZl3TpgS8bVBLZN+2jjGgg38epgxb2zmoGtSfvgMw==} + resolution: {integrity: sha512-pYeijfZuBd87T0hGn0FO1vQ/cgLk6E1ALJjfkC0oJ8cbwkZl3TpgS8bVBLZN+2jjGgg38epgxb2zmoGtSfvgMw==, tarball: https://registry.npmjs.org/@types/d3-axis/-/d3-axis-3.0.6.tgz} '@types/d3-brush@3.0.6': - resolution: {integrity: sha512-nH60IZNNxEcrh6L1ZSMNA28rj27ut/2ZmI3r96Zd+1jrZD++zD3LsMIjWlvg4AYrHn/Pqz4CF3veCxGjtbqt7A==} + resolution: {integrity: sha512-nH60IZNNxEcrh6L1ZSMNA28rj27ut/2ZmI3r96Zd+1jrZD++zD3LsMIjWlvg4AYrHn/Pqz4CF3veCxGjtbqt7A==, tarball: https://registry.npmjs.org/@types/d3-brush/-/d3-brush-3.0.6.tgz} '@types/d3-chord@3.0.6': - resolution: {integrity: sha512-LFYWWd8nwfwEmTZG9PfQxd17HbNPksHBiJHaKuY1XeqscXacsS2tyoo6OdRsjf+NQYeB6XrNL3a25E3gH69lcg==} + resolution: {integrity: sha512-LFYWWd8nwfwEmTZG9PfQxd17HbNPksHBiJHaKuY1XeqscXacsS2tyoo6OdRsjf+NQYeB6XrNL3a25E3gH69lcg==, tarball: https://registry.npmjs.org/@types/d3-chord/-/d3-chord-3.0.6.tgz} '@types/d3-color@3.1.3': - resolution: {integrity: sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A==} + resolution: {integrity: sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A==, tarball: https://registry.npmjs.org/@types/d3-color/-/d3-color-3.1.3.tgz} '@types/d3-contour@3.0.6': - resolution: {integrity: sha512-BjzLgXGnCWjUSYGfH1cpdo41/hgdWETu4YxpezoztawmqsvCeep+8QGfiY6YbDvfgHz/DkjeIkkZVJavB4a3rg==} + resolution: {integrity: sha512-BjzLgXGnCWjUSYGfH1cpdo41/hgdWETu4YxpezoztawmqsvCeep+8QGfiY6YbDvfgHz/DkjeIkkZVJavB4a3rg==, tarball: https://registry.npmjs.org/@types/d3-contour/-/d3-contour-3.0.6.tgz} '@types/d3-delaunay@6.0.4': - resolution: {integrity: sha512-ZMaSKu4THYCU6sV64Lhg6qjf1orxBthaC161plr5KuPHo3CNm8DTHiLw/5Eq2b6TsNP0W0iJrUOFscY6Q450Hw==} + resolution: {integrity: sha512-ZMaSKu4THYCU6sV64Lhg6qjf1orxBthaC161plr5KuPHo3CNm8DTHiLw/5Eq2b6TsNP0W0iJrUOFscY6Q450Hw==, tarball: https://registry.npmjs.org/@types/d3-delaunay/-/d3-delaunay-6.0.4.tgz} '@types/d3-dispatch@3.0.7': - resolution: {integrity: sha512-5o9OIAdKkhN1QItV2oqaE5KMIiXAvDWBDPrD85e58Qlz1c1kI/J0NcqbEG88CoTwJrYe7ntUCVfeUl2UJKbWgA==} + resolution: {integrity: sha512-5o9OIAdKkhN1QItV2oqaE5KMIiXAvDWBDPrD85e58Qlz1c1kI/J0NcqbEG88CoTwJrYe7ntUCVfeUl2UJKbWgA==, tarball: https://registry.npmjs.org/@types/d3-dispatch/-/d3-dispatch-3.0.7.tgz} '@types/d3-drag@3.0.7': - resolution: {integrity: sha512-HE3jVKlzU9AaMazNufooRJ5ZpWmLIoc90A37WU2JMmeq28w1FQqCZswHZ3xR+SuxYftzHq6WU6KJHvqxKzTxxQ==} + resolution: {integrity: sha512-HE3jVKlzU9AaMazNufooRJ5ZpWmLIoc90A37WU2JMmeq28w1FQqCZswHZ3xR+SuxYftzHq6WU6KJHvqxKzTxxQ==, tarball: https://registry.npmjs.org/@types/d3-drag/-/d3-drag-3.0.7.tgz} '@types/d3-dsv@3.0.7': - resolution: {integrity: sha512-n6QBF9/+XASqcKK6waudgL0pf/S5XHPPI8APyMLLUHd8NqouBGLsU8MgtO7NINGtPBtk9Kko/W4ea0oAspwh9g==} + resolution: {integrity: sha512-n6QBF9/+XASqcKK6waudgL0pf/S5XHPPI8APyMLLUHd8NqouBGLsU8MgtO7NINGtPBtk9Kko/W4ea0oAspwh9g==, tarball: https://registry.npmjs.org/@types/d3-dsv/-/d3-dsv-3.0.7.tgz} '@types/d3-ease@3.0.2': - resolution: {integrity: sha512-NcV1JjO5oDzoK26oMzbILE6HW7uVXOHLQvHshBUW4UMdZGfiY6v5BeQwh9a9tCzv+CeefZQHJt5SRgK154RtiA==} + resolution: {integrity: sha512-NcV1JjO5oDzoK26oMzbILE6HW7uVXOHLQvHshBUW4UMdZGfiY6v5BeQwh9a9tCzv+CeefZQHJt5SRgK154RtiA==, tarball: https://registry.npmjs.org/@types/d3-ease/-/d3-ease-3.0.2.tgz} '@types/d3-fetch@3.0.7': - resolution: {integrity: sha512-fTAfNmxSb9SOWNB9IoG5c8Hg6R+AzUHDRlsXsDZsNp6sxAEOP0tkP3gKkNSO/qmHPoBFTxNrjDprVHDQDvo5aA==} + resolution: {integrity: sha512-fTAfNmxSb9SOWNB9IoG5c8Hg6R+AzUHDRlsXsDZsNp6sxAEOP0tkP3gKkNSO/qmHPoBFTxNrjDprVHDQDvo5aA==, tarball: https://registry.npmjs.org/@types/d3-fetch/-/d3-fetch-3.0.7.tgz} '@types/d3-force@3.0.10': - resolution: {integrity: sha512-ZYeSaCF3p73RdOKcjj+swRlZfnYpK1EbaDiYICEEp5Q6sUiqFaFQ9qgoshp5CzIyyb/yD09kD9o2zEltCexlgw==} + resolution: {integrity: sha512-ZYeSaCF3p73RdOKcjj+swRlZfnYpK1EbaDiYICEEp5Q6sUiqFaFQ9qgoshp5CzIyyb/yD09kD9o2zEltCexlgw==, tarball: https://registry.npmjs.org/@types/d3-force/-/d3-force-3.0.10.tgz} '@types/d3-format@3.0.4': - resolution: {integrity: sha512-fALi2aI6shfg7vM5KiR1wNJnZ7r6UuggVqtDA+xiEdPZQwy/trcQaHnwShLuLdta2rTymCNpxYTiMZX/e09F4g==} + resolution: {integrity: sha512-fALi2aI6shfg7vM5KiR1wNJnZ7r6UuggVqtDA+xiEdPZQwy/trcQaHnwShLuLdta2rTymCNpxYTiMZX/e09F4g==, tarball: https://registry.npmjs.org/@types/d3-format/-/d3-format-3.0.4.tgz} '@types/d3-geo@3.1.0': - resolution: {integrity: sha512-856sckF0oP/diXtS4jNsiQw/UuK5fQG8l/a9VVLeSouf1/PPbBE1i1W852zVwKwYCBkFJJB7nCFTbk6UMEXBOQ==} + resolution: {integrity: sha512-856sckF0oP/diXtS4jNsiQw/UuK5fQG8l/a9VVLeSouf1/PPbBE1i1W852zVwKwYCBkFJJB7nCFTbk6UMEXBOQ==, tarball: https://registry.npmjs.org/@types/d3-geo/-/d3-geo-3.1.0.tgz} '@types/d3-hierarchy@3.1.7': - resolution: {integrity: sha512-tJFtNoYBtRtkNysX1Xq4sxtjK8YgoWUNpIiUee0/jHGRwqvzYxkq0hGVbbOGSz+JgFxxRu4K8nb3YpG3CMARtg==} + resolution: {integrity: sha512-tJFtNoYBtRtkNysX1Xq4sxtjK8YgoWUNpIiUee0/jHGRwqvzYxkq0hGVbbOGSz+JgFxxRu4K8nb3YpG3CMARtg==, tarball: https://registry.npmjs.org/@types/d3-hierarchy/-/d3-hierarchy-3.1.7.tgz} '@types/d3-interpolate@3.0.4': - resolution: {integrity: sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA==} + resolution: {integrity: sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA==, tarball: https://registry.npmjs.org/@types/d3-interpolate/-/d3-interpolate-3.0.4.tgz} '@types/d3-path@3.1.1': - resolution: {integrity: sha512-VMZBYyQvbGmWyWVea0EHs/BwLgxc+MKi1zLDCONksozI4YJMcTt8ZEuIR4Sb1MMTE8MMW49v0IwI5+b7RmfWlg==} + resolution: {integrity: sha512-VMZBYyQvbGmWyWVea0EHs/BwLgxc+MKi1zLDCONksozI4YJMcTt8ZEuIR4Sb1MMTE8MMW49v0IwI5+b7RmfWlg==, tarball: https://registry.npmjs.org/@types/d3-path/-/d3-path-3.1.1.tgz} '@types/d3-polygon@3.0.2': - resolution: {integrity: sha512-ZuWOtMaHCkN9xoeEMr1ubW2nGWsp4nIql+OPQRstu4ypeZ+zk3YKqQT0CXVe/PYqrKpZAi+J9mTs05TKwjXSRA==} + resolution: {integrity: sha512-ZuWOtMaHCkN9xoeEMr1ubW2nGWsp4nIql+OPQRstu4ypeZ+zk3YKqQT0CXVe/PYqrKpZAi+J9mTs05TKwjXSRA==, tarball: https://registry.npmjs.org/@types/d3-polygon/-/d3-polygon-3.0.2.tgz} '@types/d3-quadtree@3.0.6': - resolution: {integrity: sha512-oUzyO1/Zm6rsxKRHA1vH0NEDG58HrT5icx/azi9MF1TWdtttWl0UIUsjEQBBh+SIkrpd21ZjEv7ptxWys1ncsg==} + resolution: {integrity: sha512-oUzyO1/Zm6rsxKRHA1vH0NEDG58HrT5icx/azi9MF1TWdtttWl0UIUsjEQBBh+SIkrpd21ZjEv7ptxWys1ncsg==, tarball: https://registry.npmjs.org/@types/d3-quadtree/-/d3-quadtree-3.0.6.tgz} '@types/d3-random@3.0.4': - resolution: {integrity: sha512-UHYId5WTCx4L4YNel7NU00XUXXgvgpgZOvp10PuvsQENjMDXhh2RyFc0KBjO7B45ne4Ha1yVH7ii0vnzKkuzWA==} + resolution: {integrity: sha512-UHYId5WTCx4L4YNel7NU00XUXXgvgpgZOvp10PuvsQENjMDXhh2RyFc0KBjO7B45ne4Ha1yVH7ii0vnzKkuzWA==, tarball: https://registry.npmjs.org/@types/d3-random/-/d3-random-3.0.4.tgz} '@types/d3-scale-chromatic@3.1.0': - resolution: {integrity: sha512-iWMJgwkK7yTRmWqRB5plb1kadXyQ5Sj8V/zYlFGMUBbIPKQScw+Dku9cAAMgJG+z5GYDoMjWGLVOvjghDEFnKQ==} + resolution: {integrity: sha512-iWMJgwkK7yTRmWqRB5plb1kadXyQ5Sj8V/zYlFGMUBbIPKQScw+Dku9cAAMgJG+z5GYDoMjWGLVOvjghDEFnKQ==, tarball: https://registry.npmjs.org/@types/d3-scale-chromatic/-/d3-scale-chromatic-3.1.0.tgz} '@types/d3-scale@4.0.9': - resolution: {integrity: sha512-dLmtwB8zkAeO/juAMfnV+sItKjlsw2lKdZVVy6LRr0cBmegxSABiLEpGVmSJJ8O08i4+sGR6qQtb6WtuwJdvVw==} + resolution: {integrity: sha512-dLmtwB8zkAeO/juAMfnV+sItKjlsw2lKdZVVy6LRr0cBmegxSABiLEpGVmSJJ8O08i4+sGR6qQtb6WtuwJdvVw==, tarball: https://registry.npmjs.org/@types/d3-scale/-/d3-scale-4.0.9.tgz} '@types/d3-selection@3.0.11': - resolution: {integrity: sha512-bhAXu23DJWsrI45xafYpkQ4NtcKMwWnAC/vKrd2l+nxMFuvOT3XMYTIj2opv8vq8AO5Yh7Qac/nSeP/3zjTK0w==} + resolution: {integrity: sha512-bhAXu23DJWsrI45xafYpkQ4NtcKMwWnAC/vKrd2l+nxMFuvOT3XMYTIj2opv8vq8AO5Yh7Qac/nSeP/3zjTK0w==, tarball: https://registry.npmjs.org/@types/d3-selection/-/d3-selection-3.0.11.tgz} '@types/d3-shape@3.1.8': - resolution: {integrity: sha512-lae0iWfcDeR7qt7rA88BNiqdvPS5pFVPpo5OfjElwNaT2yyekbM0C9vK+yqBqEmHr6lDkRnYNoTBYlAgJa7a4w==} + resolution: {integrity: sha512-lae0iWfcDeR7qt7rA88BNiqdvPS5pFVPpo5OfjElwNaT2yyekbM0C9vK+yqBqEmHr6lDkRnYNoTBYlAgJa7a4w==, tarball: https://registry.npmjs.org/@types/d3-shape/-/d3-shape-3.1.8.tgz} '@types/d3-time-format@4.0.3': - resolution: {integrity: sha512-5xg9rC+wWL8kdDj153qZcsJ0FWiFt0J5RB6LYUNZjwSnesfblqrI/bJ1wBdJ8OQfncgbJG5+2F+qfqnqyzYxyg==} + resolution: {integrity: sha512-5xg9rC+wWL8kdDj153qZcsJ0FWiFt0J5RB6LYUNZjwSnesfblqrI/bJ1wBdJ8OQfncgbJG5+2F+qfqnqyzYxyg==, tarball: https://registry.npmjs.org/@types/d3-time-format/-/d3-time-format-4.0.3.tgz} '@types/d3-time@3.0.4': - resolution: {integrity: sha512-yuzZug1nkAAaBlBBikKZTgzCeA+k1uy4ZFwWANOfKw5z5LRhV0gNA7gNkKm7HoK+HRN0wX3EkxGk0fpbWhmB7g==} + resolution: {integrity: sha512-yuzZug1nkAAaBlBBikKZTgzCeA+k1uy4ZFwWANOfKw5z5LRhV0gNA7gNkKm7HoK+HRN0wX3EkxGk0fpbWhmB7g==, tarball: https://registry.npmjs.org/@types/d3-time/-/d3-time-3.0.4.tgz} '@types/d3-timer@3.0.2': - resolution: {integrity: sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw==} + resolution: {integrity: sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw==, tarball: https://registry.npmjs.org/@types/d3-timer/-/d3-timer-3.0.2.tgz} '@types/d3-transition@3.0.9': - resolution: {integrity: sha512-uZS5shfxzO3rGlu0cC3bjmMFKsXv+SmZZcgp0KD22ts4uGXp5EVYGzu/0YdwZeKmddhcAccYtREJKkPfXkZuCg==} + resolution: {integrity: sha512-uZS5shfxzO3rGlu0cC3bjmMFKsXv+SmZZcgp0KD22ts4uGXp5EVYGzu/0YdwZeKmddhcAccYtREJKkPfXkZuCg==, tarball: https://registry.npmjs.org/@types/d3-transition/-/d3-transition-3.0.9.tgz} '@types/d3-zoom@3.0.8': - resolution: {integrity: sha512-iqMC4/YlFCSlO8+2Ii1GGGliCAY4XdeG748w5vQUbevlbDu0zSjH/+jojorQVBK/se0j6DUFNPBGSqD3YWYnDw==} + resolution: {integrity: sha512-iqMC4/YlFCSlO8+2Ii1GGGliCAY4XdeG748w5vQUbevlbDu0zSjH/+jojorQVBK/se0j6DUFNPBGSqD3YWYnDw==, tarball: https://registry.npmjs.org/@types/d3-zoom/-/d3-zoom-3.0.8.tgz} '@types/d3@7.4.3': - resolution: {integrity: sha512-lZXZ9ckh5R8uiFVt8ogUNf+pIrK4EsWrx2Np75WvF/eTpJ0FMHNhjXk8CKEx/+gpHbNQyJWehbFaTvqmHWB3ww==} + resolution: {integrity: sha512-lZXZ9ckh5R8uiFVt8ogUNf+pIrK4EsWrx2Np75WvF/eTpJ0FMHNhjXk8CKEx/+gpHbNQyJWehbFaTvqmHWB3ww==, tarball: https://registry.npmjs.org/@types/d3/-/d3-7.4.3.tgz} '@types/deep-eql@4.0.2': - resolution: {integrity: sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==} + resolution: {integrity: sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==, tarball: https://registry.npmjs.org/@types/deep-eql/-/deep-eql-4.0.2.tgz} '@types/dlv@1.1.5': - resolution: {integrity: sha512-JHOWNfiWepAhfwlSw17kiWrWrk6od2dEQgHltJw9AS0JPFoLZJBge5+Dnil2NfdjAvJ/+vGSX60/BRW20PpUXw==} + resolution: {integrity: sha512-JHOWNfiWepAhfwlSw17kiWrWrk6od2dEQgHltJw9AS0JPFoLZJBge5+Dnil2NfdjAvJ/+vGSX60/BRW20PpUXw==, tarball: https://registry.npmjs.org/@types/dlv/-/dlv-1.1.5.tgz} '@types/dragula@3.7.4': - resolution: {integrity: sha512-cjg5MNq8CnXyJTScWM2t9fc0FC77rrBxYBs6/R3FiwW9NOTdHPB2nJq52I0tfxiBnhFb451S2j5ok4wW7sKmvA==} + resolution: {integrity: sha512-cjg5MNq8CnXyJTScWM2t9fc0FC77rrBxYBs6/R3FiwW9NOTdHPB2nJq52I0tfxiBnhFb451S2j5ok4wW7sKmvA==, tarball: https://registry.npmjs.org/@types/dragula/-/dragula-3.7.4.tgz} '@types/eslint-scope@3.7.7': - resolution: {integrity: sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==} + resolution: {integrity: sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==, tarball: https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.7.tgz} '@types/eslint@9.6.1': - resolution: {integrity: sha512-FXx2pKgId/WyYo2jXw63kk7/+TY7u7AziEJxJAnSFzHlqTAS3Ync6SvgYAN/k4/PQpnnVuzoMuVnByKK2qp0ag==} + resolution: {integrity: sha512-FXx2pKgId/WyYo2jXw63kk7/+TY7u7AziEJxJAnSFzHlqTAS3Ync6SvgYAN/k4/PQpnnVuzoMuVnByKK2qp0ag==, tarball: https://registry.npmjs.org/@types/eslint/-/eslint-9.6.1.tgz} '@types/esquery@1.5.4': - resolution: {integrity: sha512-yYO4Q8H+KJHKW1rEeSzHxcZi90durqYgWVfnh5K6ZADVBjBv2e1NEveYX5yT2bffgN7RqzH3k9930m+i2yBoMA==} + resolution: {integrity: sha512-yYO4Q8H+KJHKW1rEeSzHxcZi90durqYgWVfnh5K6ZADVBjBv2e1NEveYX5yT2bffgN7RqzH3k9930m+i2yBoMA==, tarball: https://registry.npmjs.org/@types/esquery/-/esquery-1.5.4.tgz} '@types/esrecurse@4.3.1': - resolution: {integrity: sha512-xJBAbDifo5hpffDBuHl0Y8ywswbiAp/Wi7Y/GtAgSlZyIABppyurxVueOPE8LUQOxdlgi6Zqce7uoEpqNTeiUw==} + resolution: {integrity: sha512-xJBAbDifo5hpffDBuHl0Y8ywswbiAp/Wi7Y/GtAgSlZyIABppyurxVueOPE8LUQOxdlgi6Zqce7uoEpqNTeiUw==, tarball: https://registry.npmjs.org/@types/esrecurse/-/esrecurse-4.3.1.tgz} '@types/estree@0.0.50': - resolution: {integrity: sha512-C6N5s2ZFtuZRj54k2/zyRhNDjJwwcViAM3Nbm8zjBpbqAdZ00mr0CFxvSKeO8Y/e03WVFLpQMdHYVfUd6SB+Hw==} + resolution: {integrity: sha512-C6N5s2ZFtuZRj54k2/zyRhNDjJwwcViAM3Nbm8zjBpbqAdZ00mr0CFxvSKeO8Y/e03WVFLpQMdHYVfUd6SB+Hw==, tarball: https://registry.npmjs.org/@types/estree/-/estree-0.0.50.tgz} '@types/estree@1.0.5': - resolution: {integrity: sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==} + resolution: {integrity: sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==, tarball: https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz} '@types/estree@1.0.9': - resolution: {integrity: sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==} + resolution: {integrity: sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==, tarball: https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz} '@types/express-serve-static-core@4.19.9': - resolution: {integrity: sha512-QP2ESEe/ImWY0HDwNAnK9PvEffUyhLTnWkk7KXzHfyeWAnlrDe1fN77bXl6ia8KT3wPlmA7t9/VPRpnf4Ex9sg==} + resolution: {integrity: sha512-QP2ESEe/ImWY0HDwNAnK9PvEffUyhLTnWkk7KXzHfyeWAnlrDe1fN77bXl6ia8KT3wPlmA7t9/VPRpnf4Ex9sg==, tarball: https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.19.9.tgz} '@types/express@4.17.25': - resolution: {integrity: sha512-dVd04UKsfpINUnK0yBoYHDF3xu7xVH4BuDotC/xGuycx4CgbP48X/KF/586bcObxT0HENHXEU8Nqtu6NR+eKhw==} + resolution: {integrity: sha512-dVd04UKsfpINUnK0yBoYHDF3xu7xVH4BuDotC/xGuycx4CgbP48X/KF/586bcObxT0HENHXEU8Nqtu6NR+eKhw==, tarball: https://registry.npmjs.org/@types/express/-/express-4.17.25.tgz} '@types/fs-extra@11.0.4': - resolution: {integrity: sha512-yTbItCNreRooED33qjunPthRcSjERP1r4MqCZc7wv0u2sUkzTFp45tgUfS5+r7FrZPdmCCNflLhVSP/o+SemsQ==} + resolution: {integrity: sha512-yTbItCNreRooED33qjunPthRcSjERP1r4MqCZc7wv0u2sUkzTFp45tgUfS5+r7FrZPdmCCNflLhVSP/o+SemsQ==, tarball: https://registry.npmjs.org/@types/fs-extra/-/fs-extra-11.0.4.tgz} '@types/gensync@1.0.5': - resolution: {integrity: sha512-MbsRCT7mTikHwKZ0X+LVUTLRrZZRLipTuXEO9qOYO+zmjMVk81axyClMROf6uoPD9MRVu46bx8zoR0Ad9q3NAg==} + resolution: {integrity: sha512-MbsRCT7mTikHwKZ0X+LVUTLRrZZRLipTuXEO9qOYO+zmjMVk81axyClMROf6uoPD9MRVu46bx8zoR0Ad9q3NAg==, tarball: https://registry.npmjs.org/@types/gensync/-/gensync-1.0.5.tgz} '@types/geojson@7946.0.16': - resolution: {integrity: sha512-6C8nqWur3j98U6+lXDfTUWIfgvZU+EumvpHKcYjujKH7woYyLj2sUmff0tRhrqM7BohUw7Pz3ZB1jj2gW9Fvmg==} + resolution: {integrity: sha512-6C8nqWur3j98U6+lXDfTUWIfgvZU+EumvpHKcYjujKH7woYyLj2sUmff0tRhrqM7BohUw7Pz3ZB1jj2gW9Fvmg==, tarball: https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.16.tgz} '@types/googlemaps@3.40.3': - resolution: {integrity: sha512-ivlG5S0LlnQgpgPnQCUNAs7kjBtO367ZwDmuK+ggsQfW+w4N0RyWbxWZ6vPwegDe50Du3Xbb5+QVwJuB/U1XpA==} + resolution: {integrity: sha512-ivlG5S0LlnQgpgPnQCUNAs7kjBtO367ZwDmuK+ggsQfW+w4N0RyWbxWZ6vPwegDe50Du3Xbb5+QVwJuB/U1XpA==, tarball: https://registry.npmjs.org/@types/googlemaps/-/googlemaps-3.40.3.tgz} deprecated: 'Types for the Google Maps browser API have moved to @types/google.maps. Note: these types are not for the googlemaps npm package, which is a Node API.' '@types/hast@3.0.5': - resolution: {integrity: sha512-rp/ezSWaD1m44dPKICGhiskI13nVr7qTloFwDa/IYkhhf5nzwP+zIQcIJh3WIFSBOy/H1PzB40jPjMDksN4F+g==} + resolution: {integrity: sha512-rp/ezSWaD1m44dPKICGhiskI13nVr7qTloFwDa/IYkhhf5nzwP+zIQcIJh3WIFSBOy/H1PzB40jPjMDksN4F+g==, tarball: https://registry.npmjs.org/@types/hast/-/hast-3.0.5.tgz} '@types/http-errors@2.0.5': - resolution: {integrity: sha512-r8Tayk8HJnX0FztbZN7oVqGccWgw98T/0neJphO91KkmOzug1KkofZURD4UaD5uH8AqcFLfdPErnBod0u71/qg==} + resolution: {integrity: sha512-r8Tayk8HJnX0FztbZN7oVqGccWgw98T/0neJphO91KkmOzug1KkofZURD4UaD5uH8AqcFLfdPErnBod0u71/qg==, tarball: https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.5.tgz} '@types/http-proxy@1.17.17': - resolution: {integrity: sha512-ED6LB+Z1AVylNTu7hdzuBqOgMnvG/ld6wGCG8wFnAzKX5uyW2K3WD52v0gnLCTK/VLpXtKckgWuyScYK6cSPaw==} + resolution: {integrity: sha512-ED6LB+Z1AVylNTu7hdzuBqOgMnvG/ld6wGCG8wFnAzKX5uyW2K3WD52v0gnLCTK/VLpXtKckgWuyScYK6cSPaw==, tarball: https://registry.npmjs.org/@types/http-proxy/-/http-proxy-1.17.17.tgz} '@types/istanbul-lib-coverage@2.0.6': - resolution: {integrity: sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==} + resolution: {integrity: sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==, tarball: https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz} '@types/istanbul-lib-report@3.0.3': - resolution: {integrity: sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==} + resolution: {integrity: sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==, tarball: https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.3.tgz} '@types/istanbul-reports@3.0.4': - resolution: {integrity: sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==} + resolution: {integrity: sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==, tarball: https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz} '@types/jest@30.0.0': - resolution: {integrity: sha512-XTYugzhuwqWjws0CVz8QpM36+T+Dz5mTEBKhNs/esGLnCIlGdRy+Dq78NRjd7ls7r8BC8ZRMOrKlkO1hU0JOwA==} + resolution: {integrity: sha512-XTYugzhuwqWjws0CVz8QpM36+T+Dz5mTEBKhNs/esGLnCIlGdRy+Dq78NRjd7ls7r8BC8ZRMOrKlkO1hU0JOwA==, tarball: https://registry.npmjs.org/@types/jest/-/jest-30.0.0.tgz} '@types/jsdom@20.0.1': - resolution: {integrity: sha512-d0r18sZPmMQr1eG35u12FZfhIXNrnsPU/g5wvRKCUf/tOGilKKwYMYGqh33BNR6ba+2gkHw1EUiHoN3mn7E5IQ==} + resolution: {integrity: sha512-d0r18sZPmMQr1eG35u12FZfhIXNrnsPU/g5wvRKCUf/tOGilKKwYMYGqh33BNR6ba+2gkHw1EUiHoN3mn7E5IQ==, tarball: https://registry.npmjs.org/@types/jsdom/-/jsdom-20.0.1.tgz} '@types/jsdom@21.1.7': - resolution: {integrity: sha512-yOriVnggzrnQ3a9OKOCxaVuSug3w3/SbOj5i7VwXWZEyUNl3bLF9V3MfxGbZKuwqJOQyRfqXyROBB1CoZLFWzA==} + resolution: {integrity: sha512-yOriVnggzrnQ3a9OKOCxaVuSug3w3/SbOj5i7VwXWZEyUNl3bLF9V3MfxGbZKuwqJOQyRfqXyROBB1CoZLFWzA==, tarball: https://registry.npmjs.org/@types/jsdom/-/jsdom-21.1.7.tgz} '@types/jsesc@2.5.1': - resolution: {integrity: sha512-9VN+6yxLOPLOav+7PwjZbxiID2bVaeq0ED4qSQmdQTdjnXJSaCVKTR58t15oqH1H5t8Ng2ZX1SabJVoN9Q34bw==} + resolution: {integrity: sha512-9VN+6yxLOPLOav+7PwjZbxiID2bVaeq0ED4qSQmdQTdjnXJSaCVKTR58t15oqH1H5t8Ng2ZX1SabJVoN9Q34bw==, tarball: https://registry.npmjs.org/@types/jsesc/-/jsesc-2.5.1.tgz} '@types/json-schema@7.0.15': - resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} + resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==, tarball: https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz} '@types/json5@0.0.29': - resolution: {integrity: sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==} + resolution: {integrity: sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==, tarball: https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz} '@types/jsonfile@6.1.4': - resolution: {integrity: sha512-D5qGUYwjvnNNextdU59/+fI+spnwtTFmyQP0h+PfIOSkNfpU6AOICUOkm4i0OnSk+NyjdPJrxCDro0sJsWlRpQ==} + resolution: {integrity: sha512-D5qGUYwjvnNNextdU59/+fI+spnwtTFmyQP0h+PfIOSkNfpU6AOICUOkm4i0OnSk+NyjdPJrxCDro0sJsWlRpQ==, tarball: https://registry.npmjs.org/@types/jsonfile/-/jsonfile-6.1.4.tgz} '@types/less@3.0.8': - resolution: {integrity: sha512-Gjm4+H9noDJgu5EdT3rUw5MhPBag46fiOy27BefvWkNL8mlZnKnCaVVVTLKj6RYXed9b62CPKnPav9govyQDzA==} + resolution: {integrity: sha512-Gjm4+H9noDJgu5EdT3rUw5MhPBag46fiOy27BefvWkNL8mlZnKnCaVVVTLKj6RYXed9b62CPKnPav9govyQDzA==, tarball: https://registry.npmjs.org/@types/less/-/less-3.0.8.tgz} '@types/linkify-it@5.0.0': - resolution: {integrity: sha512-sVDA58zAw4eWAffKOaQH5/5j3XeayukzDk+ewSsnv3p4yJEZHCCzMDiZM8e0OUrRvmpGZ85jf4yDHkHsgBNr9Q==} + resolution: {integrity: sha512-sVDA58zAw4eWAffKOaQH5/5j3XeayukzDk+ewSsnv3p4yJEZHCCzMDiZM8e0OUrRvmpGZ85jf4yDHkHsgBNr9Q==, tarball: https://registry.npmjs.org/@types/linkify-it/-/linkify-it-5.0.0.tgz} '@types/markdown-it@14.1.2': - resolution: {integrity: sha512-promo4eFwuiW+TfGxhi+0x3czqTYJkG8qB17ZUJiVF10Xm7NLVRSLUsfRTU/6h1e24VvRnXCx+hG7li58lkzog==} + resolution: {integrity: sha512-promo4eFwuiW+TfGxhi+0x3czqTYJkG8qB17ZUJiVF10Xm7NLVRSLUsfRTU/6h1e24VvRnXCx+hG7li58lkzog==, tarball: https://registry.npmjs.org/@types/markdown-it/-/markdown-it-14.1.2.tgz} '@types/md5@2.3.5': - resolution: {integrity: sha512-/i42wjYNgE6wf0j2bcTX6kuowmdL/6PE4IVitMpm2eYKBUuYCprdcWVK+xEF0gcV6ufMCRhtxmReGfc6hIK7Jw==} + resolution: {integrity: sha512-/i42wjYNgE6wf0j2bcTX6kuowmdL/6PE4IVitMpm2eYKBUuYCprdcWVK+xEF0gcV6ufMCRhtxmReGfc6hIK7Jw==, tarball: https://registry.npmjs.org/@types/md5/-/md5-2.3.5.tgz} '@types/mdurl@2.0.0': - resolution: {integrity: sha512-RGdgjQUZba5p6QEFAVx2OGb8rQDL/cPRG7GiedRzMcJ1tYnUANBncjbSB1NRGwbvjcPeikRABz2nshyPk1bhWg==} + resolution: {integrity: sha512-RGdgjQUZba5p6QEFAVx2OGb8rQDL/cPRG7GiedRzMcJ1tYnUANBncjbSB1NRGwbvjcPeikRABz2nshyPk1bhWg==, tarball: https://registry.npmjs.org/@types/mdurl/-/mdurl-2.0.0.tgz} '@types/mime@1.3.5': - resolution: {integrity: sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==} + resolution: {integrity: sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==, tarball: https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz} '@types/node-forge@1.3.14': - resolution: {integrity: sha512-mhVF2BnD4BO+jtOp7z1CdzaK4mbuK0LLQYAvdOLqHTavxFNq4zA1EmYkpnFjP8HOUzedfQkRnp0E2ulSAYSzAw==} + resolution: {integrity: sha512-mhVF2BnD4BO+jtOp7z1CdzaK4mbuK0LLQYAvdOLqHTavxFNq4zA1EmYkpnFjP8HOUzedfQkRnp0E2ulSAYSzAw==, tarball: https://registry.npmjs.org/@types/node-forge/-/node-forge-1.3.14.tgz} '@types/node@20.19.9': - resolution: {integrity: sha512-cuVNgarYWZqxRJDQHEB58GEONhOK79QVR/qYx4S7kcUObQvUwvFnYxJuuHUKm2aieN9X3yZB4LZsuYNU1Qphsw==} + resolution: {integrity: sha512-cuVNgarYWZqxRJDQHEB58GEONhOK79QVR/qYx4S7kcUObQvUwvFnYxJuuHUKm2aieN9X3yZB4LZsuYNU1Qphsw==, tarball: https://registry.npmjs.org/@types/node/-/node-20.19.9.tgz} '@types/parse-json@4.0.2': - resolution: {integrity: sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==} + resolution: {integrity: sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==, tarball: https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.2.tgz} '@types/prop-types@15.7.15': - resolution: {integrity: sha512-F6bEyamV9jKGAFBEmlQnesRPGOQqS2+Uwi0Em15xenOxHaf2hv6L8YCVn3rPdPJOiJfPiCnLIRyvwVaqMY3MIw==} + resolution: {integrity: sha512-F6bEyamV9jKGAFBEmlQnesRPGOQqS2+Uwi0Em15xenOxHaf2hv6L8YCVn3rPdPJOiJfPiCnLIRyvwVaqMY3MIw==, tarball: https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.15.tgz} '@types/qs@6.15.1': - resolution: {integrity: sha512-GZHUBZR9hckSUhrxmp1nG6NwdpM9fCunJwyThLW1X3AyHgd9IlHb6VANpQQqDr2o/qQp6McZ3y/IA2rVzKzSbw==} + resolution: {integrity: sha512-GZHUBZR9hckSUhrxmp1nG6NwdpM9fCunJwyThLW1X3AyHgd9IlHb6VANpQQqDr2o/qQp6McZ3y/IA2rVzKzSbw==, tarball: https://registry.npmjs.org/@types/qs/-/qs-6.15.1.tgz} '@types/range-parser@1.2.7': - resolution: {integrity: sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==} + resolution: {integrity: sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==, tarball: https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz} '@types/react-dom@18.3.0': - resolution: {integrity: sha512-EhwApuTmMBmXuFOikhQLIBUn6uFg81SwLMOAUgodJF14SOBOCMdU04gDoYi0WOJJHD144TL32z4yDqCW3dnkQg==} + resolution: {integrity: sha512-EhwApuTmMBmXuFOikhQLIBUn6uFg81SwLMOAUgodJF14SOBOCMdU04gDoYi0WOJJHD144TL32z4yDqCW3dnkQg==, tarball: https://registry.npmjs.org/@types/react-dom/-/react-dom-18.3.0.tgz} '@types/react@18.3.1': - resolution: {integrity: sha512-V0kuGBX3+prX+DQ/7r2qsv1NsdfnCLnTgnRJ1pYnxykBhGMz+qj+box5lq7XsO5mtZsBqpjwwTu/7wszPfMBcw==} + resolution: {integrity: sha512-V0kuGBX3+prX+DQ/7r2qsv1NsdfnCLnTgnRJ1pYnxykBhGMz+qj+box5lq7XsO5mtZsBqpjwwTu/7wszPfMBcw==, tarball: https://registry.npmjs.org/@types/react/-/react-18.3.1.tgz} '@types/resolve@1.20.2': - resolution: {integrity: sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q==} + resolution: {integrity: sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q==, tarball: https://registry.npmjs.org/@types/resolve/-/resolve-1.20.2.tgz} '@types/retry@0.12.2': - resolution: {integrity: sha512-XISRgDJ2Tc5q4TRqvgJtzsRkFYNJzZrhTdtMoGVBttwzzQJkPnS3WWTFc7kuDRoPtPakl+T+OfdEUjYJj7Jbow==} + resolution: {integrity: sha512-XISRgDJ2Tc5q4TRqvgJtzsRkFYNJzZrhTdtMoGVBttwzzQJkPnS3WWTFc7kuDRoPtPakl+T+OfdEUjYJj7Jbow==, tarball: https://registry.npmjs.org/@types/retry/-/retry-0.12.2.tgz} '@types/semver@7.5.8': - resolution: {integrity: sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==} + resolution: {integrity: sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==, tarball: https://registry.npmjs.org/@types/semver/-/semver-7.5.8.tgz} '@types/send@0.17.6': - resolution: {integrity: sha512-Uqt8rPBE8SY0RK8JB1EzVOIZ32uqy8HwdxCnoCOsYrvnswqmFZ/k+9Ikidlk/ImhsdvBsloHbAlewb2IEBV/Og==} + resolution: {integrity: sha512-Uqt8rPBE8SY0RK8JB1EzVOIZ32uqy8HwdxCnoCOsYrvnswqmFZ/k+9Ikidlk/ImhsdvBsloHbAlewb2IEBV/Og==, tarball: https://registry.npmjs.org/@types/send/-/send-0.17.6.tgz} '@types/send@1.2.1': - resolution: {integrity: sha512-arsCikDvlU99zl1g69TcAB3mzZPpxgw0UQnaHeC1Nwb015xp8bknZv5rIfri9xTOcMuaVgvabfIRA7PSZVuZIQ==} + resolution: {integrity: sha512-arsCikDvlU99zl1g69TcAB3mzZPpxgw0UQnaHeC1Nwb015xp8bknZv5rIfri9xTOcMuaVgvabfIRA7PSZVuZIQ==, tarball: https://registry.npmjs.org/@types/send/-/send-1.2.1.tgz} '@types/serve-index@1.9.4': - resolution: {integrity: sha512-qLpGZ/c2fhSs5gnYsQxtDEq3Oy8SXPClIXkW5ghvAvsNuVSA8k+gCONcUCS/UjLEYvYps+e8uBtfgXgvhwfNug==} + resolution: {integrity: sha512-qLpGZ/c2fhSs5gnYsQxtDEq3Oy8SXPClIXkW5ghvAvsNuVSA8k+gCONcUCS/UjLEYvYps+e8uBtfgXgvhwfNug==, tarball: https://registry.npmjs.org/@types/serve-index/-/serve-index-1.9.4.tgz} '@types/serve-static@1.15.10': - resolution: {integrity: sha512-tRs1dB+g8Itk72rlSI2ZrW6vZg0YrLI81iQSTkMmOqnqCaNr/8Ek4VwWcN5vZgCYWbg/JJSGBlUaYGAOP73qBw==} + resolution: {integrity: sha512-tRs1dB+g8Itk72rlSI2ZrW6vZg0YrLI81iQSTkMmOqnqCaNr/8Ek4VwWcN5vZgCYWbg/JJSGBlUaYGAOP73qBw==, tarball: https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.10.tgz} '@types/sockjs@0.3.36': - resolution: {integrity: sha512-MK9V6NzAS1+Ud7JV9lJLFqW85VbC9dq3LmwZCuBe4wBDgKC0Kj/jd8Xl+nSviU+Qc3+m7umHHyHg//2KSa0a0Q==} + resolution: {integrity: sha512-MK9V6NzAS1+Ud7JV9lJLFqW85VbC9dq3LmwZCuBe4wBDgKC0Kj/jd8Xl+nSviU+Qc3+m7umHHyHg//2KSa0a0Q==, tarball: https://registry.npmjs.org/@types/sockjs/-/sockjs-0.3.36.tgz} '@types/stack-utils@2.0.3': - resolution: {integrity: sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==} + resolution: {integrity: sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==, tarball: https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.3.tgz} '@types/tough-cookie@4.0.5': - resolution: {integrity: sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA==} + resolution: {integrity: sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA==, tarball: https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-4.0.5.tgz} '@types/trusted-types@2.0.7': - resolution: {integrity: sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==} + resolution: {integrity: sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==, tarball: https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz} '@types/unist@3.0.3': - resolution: {integrity: sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==} + resolution: {integrity: sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==, tarball: https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz} '@types/uuid@9.0.1': - resolution: {integrity: sha512-rFT3ak0/2trgvp4yYZo5iKFEPsET7vKydKF+VRCxlQ9bpheehyAJH89dAkaLEq/j/RZXJIqcgsmPJKUP1Z28HA==} + resolution: {integrity: sha512-rFT3ak0/2trgvp4yYZo5iKFEPsET7vKydKF+VRCxlQ9bpheehyAJH89dAkaLEq/j/RZXJIqcgsmPJKUP1Z28HA==, tarball: https://registry.npmjs.org/@types/uuid/-/uuid-9.0.1.tgz} '@types/whatwg-mimetype@3.0.2': - resolution: {integrity: sha512-c2AKvDT8ToxLIOUlN51gTiHXflsfIFisS4pO7pDPoKouJCESkhZnEy623gwP9laCy5lnLDAw1vAzu2vM2YLOrA==} + resolution: {integrity: sha512-c2AKvDT8ToxLIOUlN51gTiHXflsfIFisS4pO7pDPoKouJCESkhZnEy623gwP9laCy5lnLDAw1vAzu2vM2YLOrA==, tarball: https://registry.npmjs.org/@types/whatwg-mimetype/-/whatwg-mimetype-3.0.2.tgz} '@types/ws@8.18.1': - resolution: {integrity: sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==} + resolution: {integrity: sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==, tarball: https://registry.npmjs.org/@types/ws/-/ws-8.18.1.tgz} '@types/yargs-parser@21.0.3': - resolution: {integrity: sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==} + resolution: {integrity: sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==, tarball: https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.3.tgz} '@types/yargs@17.0.35': - resolution: {integrity: sha512-qUHkeCyQFxMXg79wQfTtfndEC+N9ZZg76HJftDJp+qH2tV7Gj4OJi7l+PiWwJ+pWtW8GwSmqsDj/oymhrTWXjg==} + resolution: {integrity: sha512-qUHkeCyQFxMXg79wQfTtfndEC+N9ZZg76HJftDJp+qH2tV7Gj4OJi7l+PiWwJ+pWtW8GwSmqsDj/oymhrTWXjg==, tarball: https://registry.npmjs.org/@types/yargs/-/yargs-17.0.35.tgz} '@typescript-eslint/eslint-plugin@8.62.0': - resolution: {integrity: sha512-o+mpz7EYiMzXoySXiKmzlabIvTVqUuK5yLrAedRPRDA0IpPFMUV1IXt6OqljIxX/kumN6EjUYp41Hqelh6p/Dw==} + resolution: {integrity: sha512-o+mpz7EYiMzXoySXiKmzlabIvTVqUuK5yLrAedRPRDA0IpPFMUV1IXt6OqljIxX/kumN6EjUYp41Hqelh6p/Dw==, tarball: https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.62.0.tgz} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: '@typescript-eslint/parser': ^8.62.0 @@ -7000,246 +7027,246 @@ packages: typescript: '>=4.8.4 <6.1.0' '@typescript-eslint/parser@8.62.0': - resolution: {integrity: sha512-dzHeT2gySzZtLDsuqxU9AkYgIsQoHAHtRBpOqM+Ofzx1Bwrd2RcCjQJ+6iQbsHOIR6NS33bF2W1k3blN1zLDrA==} + resolution: {integrity: sha512-dzHeT2gySzZtLDsuqxU9AkYgIsQoHAHtRBpOqM+Ofzx1Bwrd2RcCjQJ+6iQbsHOIR6NS33bF2W1k3blN1zLDrA==, tarball: https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.62.0.tgz} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 typescript: '>=4.8.4 <6.1.0' '@typescript-eslint/project-service@8.62.0': - resolution: {integrity: sha512-wexnCqiTg7BOGtbLDftYpRWlmLq4xfoMd7BKFR6Y75sZS3QmRKLdN3yWLhmIYgqMmP/OXWpj3H8odkb5nGURCQ==} + resolution: {integrity: sha512-wexnCqiTg7BOGtbLDftYpRWlmLq4xfoMd7BKFR6Y75sZS3QmRKLdN3yWLhmIYgqMmP/OXWpj3H8odkb5nGURCQ==, tarball: https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.62.0.tgz} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <6.1.0' '@typescript-eslint/project-service@8.64.0': - resolution: {integrity: sha512-tk4WpOJ6IEbGrVHaNmM0YRrwAD3exZlIK3iadQNAxh4YKk6jvUQ4ecq18n+v7+meh+cJ3j+D8nbk8sRKhlwLQg==} + resolution: {integrity: sha512-tk4WpOJ6IEbGrVHaNmM0YRrwAD3exZlIK3iadQNAxh4YKk6jvUQ4ecq18n+v7+meh+cJ3j+D8nbk8sRKhlwLQg==, tarball: https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.64.0.tgz} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <6.1.0' '@typescript-eslint/scope-manager@8.62.0': - resolution: {integrity: sha512-1lX38kNxXIRb8mEc3lbq5mdHq1Pf2+U0nFU65KfT18mtPxxl0fvjuEE92mHuXPuCtElJhOrddOpyMlM3Z0umEA==} + resolution: {integrity: sha512-1lX38kNxXIRb8mEc3lbq5mdHq1Pf2+U0nFU65KfT18mtPxxl0fvjuEE92mHuXPuCtElJhOrddOpyMlM3Z0umEA==, tarball: https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.62.0.tgz} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@typescript-eslint/scope-manager@8.64.0': - resolution: {integrity: sha512-CXEaFdYXjSTgKhisNkwCcJwTP8Pl+fmRrEQrri4nm3vU743bALrxzLmq7fHG/7e6a5xO0lDYeURpZmBuhHk54w==} + resolution: {integrity: sha512-CXEaFdYXjSTgKhisNkwCcJwTP8Pl+fmRrEQrri4nm3vU743bALrxzLmq7fHG/7e6a5xO0lDYeURpZmBuhHk54w==, tarball: https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.64.0.tgz} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@typescript-eslint/tsconfig-utils@8.62.0': - resolution: {integrity: sha512-y2GAdB6ykaXUvuspbYnizQc4oDDz0Tz/Yc7iWrXf9mx8vm/L/0vLHCe0tS2boG96Zy+DivnVDQ9ZUEWoHqqx1g==} + resolution: {integrity: sha512-y2GAdB6ykaXUvuspbYnizQc4oDDz0Tz/Yc7iWrXf9mx8vm/L/0vLHCe0tS2boG96Zy+DivnVDQ9ZUEWoHqqx1g==, tarball: https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.62.0.tgz} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <6.1.0' '@typescript-eslint/tsconfig-utils@8.64.0': - resolution: {integrity: sha512-2yo8rRNKuzbVWQp5kslhANqZ2uDAeROQHBRZNPu8JDsHmeFNj/XJJhX/FhNUWmkHHvoNsKa6+tHJiig87EzsQw==} + resolution: {integrity: sha512-2yo8rRNKuzbVWQp5kslhANqZ2uDAeROQHBRZNPu8JDsHmeFNj/XJJhX/FhNUWmkHHvoNsKa6+tHJiig87EzsQw==, tarball: https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.64.0.tgz} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <6.1.0' '@typescript-eslint/type-utils@8.62.0': - resolution: {integrity: sha512-+g5O3j0w2ldzC86Pv6fvbO/xhAonbJFIdf/MKQ1d30gndlsVzUOE83ldfSE15Qrl9fhFjK6AovHs5Wpp6vx86w==} + resolution: {integrity: sha512-+g5O3j0w2ldzC86Pv6fvbO/xhAonbJFIdf/MKQ1d30gndlsVzUOE83ldfSE15Qrl9fhFjK6AovHs5Wpp6vx86w==, tarball: https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.62.0.tgz} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 typescript: '>=4.8.4 <6.1.0' '@typescript-eslint/type-utils@8.64.0': - resolution: {integrity: sha512-XWG4Fmmv/6SvyS9nH8jWrKs6terwJvE8cyRt1CzYYqzp9OrPhCT4cMc/f7C6RZCwG+qMmiffJS1/qJP8G1URtg==} + resolution: {integrity: sha512-XWG4Fmmv/6SvyS9nH8jWrKs6terwJvE8cyRt1CzYYqzp9OrPhCT4cMc/f7C6RZCwG+qMmiffJS1/qJP8G1URtg==, tarball: https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.64.0.tgz} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 typescript: '>=4.8.4 <6.1.0' '@typescript-eslint/types@8.62.0': - resolution: {integrity: sha512-KvAclkktORPvM54TgLgA4z9HIV1M8zOgw9ZVNXl9f/8dLYfXYX1wkMXP7qmabpijQRV5bHJLOmoyGQbLMaUYeg==} + resolution: {integrity: sha512-KvAclkktORPvM54TgLgA4z9HIV1M8zOgw9ZVNXl9f/8dLYfXYX1wkMXP7qmabpijQRV5bHJLOmoyGQbLMaUYeg==, tarball: https://registry.npmjs.org/@typescript-eslint/types/-/types-8.62.0.tgz} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@typescript-eslint/types@8.64.0': - resolution: {integrity: sha512-qjhfuTfLXjA4IOzXvz0rTjT01BqEiIgPoUeMwiEjnaHKJMTNo8rH5pYW1a2L/0Dnux2fPC85AeyJoWaGa8WxTA==} + resolution: {integrity: sha512-qjhfuTfLXjA4IOzXvz0rTjT01BqEiIgPoUeMwiEjnaHKJMTNo8rH5pYW1a2L/0Dnux2fPC85AeyJoWaGa8WxTA==, tarball: https://registry.npmjs.org/@typescript-eslint/types/-/types-8.64.0.tgz} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@typescript-eslint/typescript-estree@8.62.0': - resolution: {integrity: sha512-+hVbNxtW64pIcZWDPGbyaKF7vp2IBTVY5ma1blwwksrjdsbdqqEKvJWMGbBofei4F6Dovx1M0RJgoFeNu2279A==} + resolution: {integrity: sha512-+hVbNxtW64pIcZWDPGbyaKF7vp2IBTVY5ma1blwwksrjdsbdqqEKvJWMGbBofei4F6Dovx1M0RJgoFeNu2279A==, tarball: https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.62.0.tgz} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <6.1.0' '@typescript-eslint/typescript-estree@8.64.0': - resolution: {integrity: sha512-Pztpsn1aCE1oWDvDEfUk31nngvvF7vUB5SwHFEaZIFpvw7WJtqUHHL4plBZDA9HfWJJjL13BdG0YrJInTUvoVA==} + resolution: {integrity: sha512-Pztpsn1aCE1oWDvDEfUk31nngvvF7vUB5SwHFEaZIFpvw7WJtqUHHL4plBZDA9HfWJJjL13BdG0YrJInTUvoVA==, tarball: https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.64.0.tgz} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <6.1.0' '@typescript-eslint/utils@8.62.0': - resolution: {integrity: sha512-82r66fi9zYwZ+mTq3vKgwjbZ1PVk/DJzrXFLpG6RnBbdvH8TEGVHIs9H4d2drhkOzf0syZuD/OZvvlu6GDbP4g==} + resolution: {integrity: sha512-82r66fi9zYwZ+mTq3vKgwjbZ1PVk/DJzrXFLpG6RnBbdvH8TEGVHIs9H4d2drhkOzf0syZuD/OZvvlu6GDbP4g==, tarball: https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.62.0.tgz} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 typescript: '>=4.8.4 <6.1.0' '@typescript-eslint/utils@8.64.0': - resolution: {integrity: sha512-aJUGVB3+U0htrrCjoA8qukw8cm8fNCGAxK/tVoS70k8aeb7DETKeFozRiVFIwEeN9WJLsjaP3ph8I60tY2XZoQ==} + resolution: {integrity: sha512-aJUGVB3+U0htrrCjoA8qukw8cm8fNCGAxK/tVoS70k8aeb7DETKeFozRiVFIwEeN9WJLsjaP3ph8I60tY2XZoQ==, tarball: https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.64.0.tgz} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 typescript: '>=4.8.4 <6.1.0' '@typescript-eslint/visitor-keys@8.62.0': - resolution: {integrity: sha512-CY3uyFSRbcQv3nnSv8S0+lDftMVz6P963PoRlxrV7ew/Md564g9ut60PYzdLM5qW4jFn93GBF+Soi90ISAN+GQ==} + resolution: {integrity: sha512-CY3uyFSRbcQv3nnSv8S0+lDftMVz6P963PoRlxrV7ew/Md564g9ut60PYzdLM5qW4jFn93GBF+Soi90ISAN+GQ==, tarball: https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.62.0.tgz} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@typescript-eslint/visitor-keys@8.64.0': - resolution: {integrity: sha512-mrtuL8Nsn6gi2H4mo5KMTp823M+3Q19Ew/i+Zlikq20tIMm99C3Ez0dCmkWWnxut20esQvTg8aUSEhMcAOXhEw==} + resolution: {integrity: sha512-mrtuL8Nsn6gi2H4mo5KMTp823M+3Q19Ew/i+Zlikq20tIMm99C3Ez0dCmkWWnxut20esQvTg8aUSEhMcAOXhEw==, tarball: https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.64.0.tgz} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@ungap/structured-clone@1.3.3': - resolution: {integrity: sha512-60YRaenCQcVjYEKOcG824+DRGGIQ3VKErcBoAEDJZz5bKIs2ZG+X/H9Nk+Q6EVkwJk5QNApxbrc5QtBSwtrXAg==} + resolution: {integrity: sha512-60YRaenCQcVjYEKOcG824+DRGGIQ3VKErcBoAEDJZz5bKIs2ZG+X/H9Nk+Q6EVkwJk5QNApxbrc5QtBSwtrXAg==, tarball: https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.3.tgz} '@unrs/resolver-binding-android-arm-eabi@1.12.2': - resolution: {integrity: sha512-g5T90pqg1bo/7mytQx6F4iBNC0Wsh9cu+z9veDbFjc7HjpesJFWD7QMS0NGStXM075+7dJPPVvBbpZlnrdpi/w==} + resolution: {integrity: sha512-g5T90pqg1bo/7mytQx6F4iBNC0Wsh9cu+z9veDbFjc7HjpesJFWD7QMS0NGStXM075+7dJPPVvBbpZlnrdpi/w==, tarball: https://registry.npmjs.org/@unrs/resolver-binding-android-arm-eabi/-/resolver-binding-android-arm-eabi-1.12.2.tgz} cpu: [arm] os: [android] '@unrs/resolver-binding-android-arm64@1.12.2': - resolution: {integrity: sha512-YGCRZv/9GLhwmz6mYDeTsm/92BAyR28l6c2ReweVW5pWgfsitWLY8upvfRlGdoyD8HjeTHSYJWyZGD4KJA/nFQ==} + resolution: {integrity: sha512-YGCRZv/9GLhwmz6mYDeTsm/92BAyR28l6c2ReweVW5pWgfsitWLY8upvfRlGdoyD8HjeTHSYJWyZGD4KJA/nFQ==, tarball: https://registry.npmjs.org/@unrs/resolver-binding-android-arm64/-/resolver-binding-android-arm64-1.12.2.tgz} cpu: [arm64] os: [android] '@unrs/resolver-binding-darwin-arm64@1.12.2': - resolution: {integrity: sha512-u9DiNT1auQMO20A9SyTuG3wUgQWB9Z7KjAg0uFuCDR1FsAY8A0CG2S6JpHS1xwm/w1G08bjXZDcyOCjv1WAm2w==} + resolution: {integrity: sha512-u9DiNT1auQMO20A9SyTuG3wUgQWB9Z7KjAg0uFuCDR1FsAY8A0CG2S6JpHS1xwm/w1G08bjXZDcyOCjv1WAm2w==, tarball: https://registry.npmjs.org/@unrs/resolver-binding-darwin-arm64/-/resolver-binding-darwin-arm64-1.12.2.tgz} cpu: [arm64] os: [darwin] '@unrs/resolver-binding-darwin-x64@1.12.2': - resolution: {integrity: sha512-f7rPLi/T1HVKZu/u6t87lroib16n8vrSzcyxI7lg4BGO9UF26KhQL44sd9eOUgrTYhvRXtWOIZT5PejdPyJfUA==} + resolution: {integrity: sha512-f7rPLi/T1HVKZu/u6t87lroib16n8vrSzcyxI7lg4BGO9UF26KhQL44sd9eOUgrTYhvRXtWOIZT5PejdPyJfUA==, tarball: https://registry.npmjs.org/@unrs/resolver-binding-darwin-x64/-/resolver-binding-darwin-x64-1.12.2.tgz} cpu: [x64] os: [darwin] '@unrs/resolver-binding-freebsd-x64@1.12.2': - resolution: {integrity: sha512-BpcOjWCJub6nRZUS2zA20pmLvjtqAtGejETaIyRLiZiQf++cbrjltLA5NN/xaXfqeOBOSlMFbemIl5/S5tljmg==} + resolution: {integrity: sha512-BpcOjWCJub6nRZUS2zA20pmLvjtqAtGejETaIyRLiZiQf++cbrjltLA5NN/xaXfqeOBOSlMFbemIl5/S5tljmg==, tarball: https://registry.npmjs.org/@unrs/resolver-binding-freebsd-x64/-/resolver-binding-freebsd-x64-1.12.2.tgz} cpu: [x64] os: [freebsd] '@unrs/resolver-binding-linux-arm-gnueabihf@1.12.2': - resolution: {integrity: sha512-vZTDvdSISZjJx66OzJqtsOhzifbqRjbmI1Mnu49fQDwog5GtDI4QidRiEAYbZCRj9C8YZEW+3ZjqsyS9GR4k2A==} + resolution: {integrity: sha512-vZTDvdSISZjJx66OzJqtsOhzifbqRjbmI1Mnu49fQDwog5GtDI4QidRiEAYbZCRj9C8YZEW+3ZjqsyS9GR4k2A==, tarball: https://registry.npmjs.org/@unrs/resolver-binding-linux-arm-gnueabihf/-/resolver-binding-linux-arm-gnueabihf-1.12.2.tgz} cpu: [arm] os: [linux] '@unrs/resolver-binding-linux-arm-musleabihf@1.12.2': - resolution: {integrity: sha512-BiPI+IrIlwcW4nLLMM21+B1dFPzd55yAVgVGrdgDjNef+ch03GdxrcyaIz8X9SsQirh/kCQ7mviyWlMxdh2D7g==} + resolution: {integrity: sha512-BiPI+IrIlwcW4nLLMM21+B1dFPzd55yAVgVGrdgDjNef+ch03GdxrcyaIz8X9SsQirh/kCQ7mviyWlMxdh2D7g==, tarball: https://registry.npmjs.org/@unrs/resolver-binding-linux-arm-musleabihf/-/resolver-binding-linux-arm-musleabihf-1.12.2.tgz} cpu: [arm] os: [linux] '@unrs/resolver-binding-linux-arm64-gnu@1.12.2': - resolution: {integrity: sha512-zJc0H99FEPoFfSrNpa91HYfxzfAJCr502oxNK1cfdC9hlaFI43RT+JFCann9JUgZmLzzntChHyn13Sgn9ljHNg==} + resolution: {integrity: sha512-zJc0H99FEPoFfSrNpa91HYfxzfAJCr502oxNK1cfdC9hlaFI43RT+JFCann9JUgZmLzzntChHyn13Sgn9ljHNg==, tarball: https://registry.npmjs.org/@unrs/resolver-binding-linux-arm64-gnu/-/resolver-binding-linux-arm64-gnu-1.12.2.tgz} cpu: [arm64] os: [linux] '@unrs/resolver-binding-linux-arm64-musl@1.12.2': - resolution: {integrity: sha512-KQ3Lki6l+Pz1k/eBipN41ES+YUK30beLGb9YqcB1O542cyLCNE6GaxrfcY3T6EezmGGk84wb5XyO9loTM9tkcA==} + resolution: {integrity: sha512-KQ3Lki6l+Pz1k/eBipN41ES+YUK30beLGb9YqcB1O542cyLCNE6GaxrfcY3T6EezmGGk84wb5XyO9loTM9tkcA==, tarball: https://registry.npmjs.org/@unrs/resolver-binding-linux-arm64-musl/-/resolver-binding-linux-arm64-musl-1.12.2.tgz} cpu: [arm64] os: [linux] '@unrs/resolver-binding-linux-loong64-gnu@1.12.2': - resolution: {integrity: sha512-3SJGEh1DborhG6pyxvhPzCT4bbSIVihsvgJc13P1bHG7KLdNDaF9T3gsTwFc7Jw/5Y5/iWOjkEx7Zy0NvCGX3Q==} + resolution: {integrity: sha512-3SJGEh1DborhG6pyxvhPzCT4bbSIVihsvgJc13P1bHG7KLdNDaF9T3gsTwFc7Jw/5Y5/iWOjkEx7Zy0NvCGX3Q==, tarball: https://registry.npmjs.org/@unrs/resolver-binding-linux-loong64-gnu/-/resolver-binding-linux-loong64-gnu-1.12.2.tgz} cpu: [loong64] os: [linux] '@unrs/resolver-binding-linux-loong64-musl@1.12.2': - resolution: {integrity: sha512-jiuG/Obbel7uw1PwHNFfrkiKhLAF6mnyZ6aWlOAVN9WqKm8v0OFGnciJIHu8+CMvXLQ8AD51LPzAoUfT21D5Ew==} + resolution: {integrity: sha512-jiuG/Obbel7uw1PwHNFfrkiKhLAF6mnyZ6aWlOAVN9WqKm8v0OFGnciJIHu8+CMvXLQ8AD51LPzAoUfT21D5Ew==, tarball: https://registry.npmjs.org/@unrs/resolver-binding-linux-loong64-musl/-/resolver-binding-linux-loong64-musl-1.12.2.tgz} cpu: [loong64] os: [linux] '@unrs/resolver-binding-linux-ppc64-gnu@1.12.2': - resolution: {integrity: sha512-q7xRvVpmcfeL+LlZg8Pbbo6QaTZwDU5BaGZbwfhkEsXJn3Was8xYfE0RBH266xZt0rM6B7i8xAYIvjthuUIWHg==} + resolution: {integrity: sha512-q7xRvVpmcfeL+LlZg8Pbbo6QaTZwDU5BaGZbwfhkEsXJn3Was8xYfE0RBH266xZt0rM6B7i8xAYIvjthuUIWHg==, tarball: https://registry.npmjs.org/@unrs/resolver-binding-linux-ppc64-gnu/-/resolver-binding-linux-ppc64-gnu-1.12.2.tgz} cpu: [ppc64] os: [linux] '@unrs/resolver-binding-linux-riscv64-gnu@1.12.2': - resolution: {integrity: sha512-0CVdx6lcnT3Q9inOH8tsMIOJ6ImndllMjqJHg8RLVdB7Vq4SfkEXl9mCSsVNuNA4MCYycRicCUxPCabVHJRr6A==} + resolution: {integrity: sha512-0CVdx6lcnT3Q9inOH8tsMIOJ6ImndllMjqJHg8RLVdB7Vq4SfkEXl9mCSsVNuNA4MCYycRicCUxPCabVHJRr6A==, tarball: https://registry.npmjs.org/@unrs/resolver-binding-linux-riscv64-gnu/-/resolver-binding-linux-riscv64-gnu-1.12.2.tgz} cpu: [riscv64] os: [linux] '@unrs/resolver-binding-linux-riscv64-musl@1.12.2': - resolution: {integrity: sha512-iOwlRo9vnp6R6ohHQS11n0NnfdXx/omhkocmIfaPRpQhKZ+3BDMkkdRVh53qjkFkpPddf+FETA28NwGN7l5l+w==} + resolution: {integrity: sha512-iOwlRo9vnp6R6ohHQS11n0NnfdXx/omhkocmIfaPRpQhKZ+3BDMkkdRVh53qjkFkpPddf+FETA28NwGN7l5l+w==, tarball: https://registry.npmjs.org/@unrs/resolver-binding-linux-riscv64-musl/-/resolver-binding-linux-riscv64-musl-1.12.2.tgz} cpu: [riscv64] os: [linux] '@unrs/resolver-binding-linux-s390x-gnu@1.12.2': - resolution: {integrity: sha512-HYJtLfXq94q8iZNFT1lknx258wlkkWhZeUXJRqzKBBUJ00CvZ+N33zgbCqimLjsyw5Va6uUxhVa12mI+kaveEw==} + resolution: {integrity: sha512-HYJtLfXq94q8iZNFT1lknx258wlkkWhZeUXJRqzKBBUJ00CvZ+N33zgbCqimLjsyw5Va6uUxhVa12mI+kaveEw==, tarball: https://registry.npmjs.org/@unrs/resolver-binding-linux-s390x-gnu/-/resolver-binding-linux-s390x-gnu-1.12.2.tgz} cpu: [s390x] os: [linux] '@unrs/resolver-binding-linux-x64-gnu@1.12.2': - resolution: {integrity: sha512-mPsUhunKKDih5O96Y6enDQyHc1SqBPlY1E/SfMWDM3EdJ95Z9CArPeCVwCCqbP45ljvivdEk8Fxn+SIb1rDAJQ==} + resolution: {integrity: sha512-mPsUhunKKDih5O96Y6enDQyHc1SqBPlY1E/SfMWDM3EdJ95Z9CArPeCVwCCqbP45ljvivdEk8Fxn+SIb1rDAJQ==, tarball: https://registry.npmjs.org/@unrs/resolver-binding-linux-x64-gnu/-/resolver-binding-linux-x64-gnu-1.12.2.tgz} cpu: [x64] os: [linux] '@unrs/resolver-binding-linux-x64-musl@1.12.2': - resolution: {integrity: sha512-azrt6+5ydLd8Vt210AAFis/lZevSfPw93EJRIJG+xPu4WCJ8K0kppCTpMyLPcKT7H15M4Jnt2tMp5bOvCkRC6A==} + resolution: {integrity: sha512-azrt6+5ydLd8Vt210AAFis/lZevSfPw93EJRIJG+xPu4WCJ8K0kppCTpMyLPcKT7H15M4Jnt2tMp5bOvCkRC6A==, tarball: https://registry.npmjs.org/@unrs/resolver-binding-linux-x64-musl/-/resolver-binding-linux-x64-musl-1.12.2.tgz} cpu: [x64] os: [linux] '@unrs/resolver-binding-openharmony-arm64@1.12.2': - resolution: {integrity: sha512-YZ9hP4O0X9PQb8eO980qmLNGH4zT3I9+SZTdt0Pr0YyuGQhYKoOZkV02VzrzyOZJ5xIJ3UFIenKkUkGg8GjgWQ==} + resolution: {integrity: sha512-YZ9hP4O0X9PQb8eO980qmLNGH4zT3I9+SZTdt0Pr0YyuGQhYKoOZkV02VzrzyOZJ5xIJ3UFIenKkUkGg8GjgWQ==, tarball: https://registry.npmjs.org/@unrs/resolver-binding-openharmony-arm64/-/resolver-binding-openharmony-arm64-1.12.2.tgz} cpu: [arm64] os: [openharmony] '@unrs/resolver-binding-wasm32-wasi@1.12.2': - resolution: {integrity: sha512-tYFDIkMxSflfEc/h92ZWNsZlHSwgimbNHSO3PL2JWQHfCuC2q316jMyYU9TIWZsFK2bQwyK5VAdYgn8ygPj69A==} + resolution: {integrity: sha512-tYFDIkMxSflfEc/h92ZWNsZlHSwgimbNHSO3PL2JWQHfCuC2q316jMyYU9TIWZsFK2bQwyK5VAdYgn8ygPj69A==, tarball: https://registry.npmjs.org/@unrs/resolver-binding-wasm32-wasi/-/resolver-binding-wasm32-wasi-1.12.2.tgz} engines: {node: '>=14.0.0'} cpu: [wasm32] '@unrs/resolver-binding-win32-arm64-msvc@1.12.2': - resolution: {integrity: sha512-qzNyg3xL0VPQmCaUh+N5jSitce6k+uCBfMDesWRnlULOZaqUkaJ0ybdT+UqlAWJoQjuqfIU/0Ptx9bteN4D82g==} + resolution: {integrity: sha512-qzNyg3xL0VPQmCaUh+N5jSitce6k+uCBfMDesWRnlULOZaqUkaJ0ybdT+UqlAWJoQjuqfIU/0Ptx9bteN4D82g==, tarball: https://registry.npmjs.org/@unrs/resolver-binding-win32-arm64-msvc/-/resolver-binding-win32-arm64-msvc-1.12.2.tgz} cpu: [arm64] os: [win32] '@unrs/resolver-binding-win32-ia32-msvc@1.12.2': - resolution: {integrity: sha512-WD9sY00OfpHVGfsnHZoA8jVT+esS/Bg8z8jzxp5BnDCjjwsuKsPQrzswwpFy4J1AUJbXPRfkpcX0mXrzeXW79g==} + resolution: {integrity: sha512-WD9sY00OfpHVGfsnHZoA8jVT+esS/Bg8z8jzxp5BnDCjjwsuKsPQrzswwpFy4J1AUJbXPRfkpcX0mXrzeXW79g==, tarball: https://registry.npmjs.org/@unrs/resolver-binding-win32-ia32-msvc/-/resolver-binding-win32-ia32-msvc-1.12.2.tgz} cpu: [ia32] os: [win32] '@unrs/resolver-binding-win32-x64-msvc@1.12.2': - resolution: {integrity: sha512-nAB74NfSNKknqQ1RrYj6uz8FcXEomu/MATJZxh/x+BArzN2U3JbOYC0APYzUIGhVY3m5hRxA8VPNdPBoG8txlA==} + resolution: {integrity: sha512-nAB74NfSNKknqQ1RrYj6uz8FcXEomu/MATJZxh/x+BArzN2U3JbOYC0APYzUIGhVY3m5hRxA8VPNdPBoG8txlA==, tarball: https://registry.npmjs.org/@unrs/resolver-binding-win32-x64-msvc/-/resolver-binding-win32-x64-msvc-1.12.2.tgz} cpu: [x64] os: [win32] '@upsetjs/venn.js@2.0.0': - resolution: {integrity: sha512-WbBhLrooyePuQ1VZxrJjtLvTc4NVfpOyKx0sKqioq9bX1C1m7Jgykkn8gLrtwumBioXIqam8DLxp88Adbue6Hw==} + resolution: {integrity: sha512-WbBhLrooyePuQ1VZxrJjtLvTc4NVfpOyKx0sKqioq9bX1C1m7Jgykkn8gLrtwumBioXIqam8DLxp88Adbue6Hw==, tarball: https://registry.npmjs.org/@upsetjs/venn.js/-/venn.js-2.0.0.tgz} '@valibot/to-json-schema@1.7.1': - resolution: {integrity: sha512-3qkmU6KXWh8GIThEAW3kuRHPQBMjWkKy+Ppz3WkUucx53DTpOa6siMn4xDGSOhlVyMrDaJTCTMLYPZVAIk1P0A==} + resolution: {integrity: sha512-3qkmU6KXWh8GIThEAW3kuRHPQBMjWkKy+Ppz3WkUucx53DTpOa6siMn4xDGSOhlVyMrDaJTCTMLYPZVAIk1P0A==, tarball: https://registry.npmjs.org/@valibot/to-json-schema/-/to-json-schema-1.7.1.tgz} peerDependencies: valibot: ^1.4.0 '@vercel/oidc@3.2.0': - resolution: {integrity: sha512-UycprH3T6n3jH0k44NHMa7pnFHGu/N05MjojYr+Mc6I7obkoLIJujSWwin1pCvdy/eOxrI/l3uDLQsmcrOb4ug==} + resolution: {integrity: sha512-UycprH3T6n3jH0k44NHMa7pnFHGu/N05MjojYr+Mc6I7obkoLIJujSWwin1pCvdy/eOxrI/l3uDLQsmcrOb4ug==, tarball: https://registry.npmjs.org/@vercel/oidc/-/oidc-3.2.0.tgz} engines: {node: '>= 20'} '@vitejs/plugin-basic-ssl@2.3.0': - resolution: {integrity: sha512-bdyo8rB3NnQbikdMpHaML9Z1OZPBu6fFOBo+OtxsBlvMJtysWskmBcnbIDhUqgC8tcxNv/a+BcV5U+2nQMm1OQ==} + resolution: {integrity: sha512-bdyo8rB3NnQbikdMpHaML9Z1OZPBu6fFOBo+OtxsBlvMJtysWskmBcnbIDhUqgC8tcxNv/a+BcV5U+2nQMm1OQ==, tarball: https://registry.npmjs.org/@vitejs/plugin-basic-ssl/-/plugin-basic-ssl-2.3.0.tgz} engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} peerDependencies: vite: ^6.0.0 || ^7.0.0 || ^8.0.0 '@vitejs/plugin-react@5.1.4': - resolution: {integrity: sha512-VIcFLdRi/VYRU8OL/puL7QXMYafHmqOnwTZY50U1JPlCNj30PxCMx65c494b1K9be9hX83KVt0+gTEwTWLqToA==} + resolution: {integrity: sha512-VIcFLdRi/VYRU8OL/puL7QXMYafHmqOnwTZY50U1JPlCNj30PxCMx65c494b1K9be9hX83KVt0+gTEwTWLqToA==, tarball: https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-5.1.4.tgz} engines: {node: ^20.19.0 || >=22.12.0} peerDependencies: vite: ^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 '@vitejs/plugin-vue@6.0.8': - resolution: {integrity: sha512-0ZjgOg7oO6farnNGup7yvoM/YXZV84OZxHAwtflItNa/6zzQyVb5LNxyea3FEKEX2XlagIKzrlH7wwxkKgtiew==} + resolution: {integrity: sha512-0ZjgOg7oO6farnNGup7yvoM/YXZV84OZxHAwtflItNa/6zzQyVb5LNxyea3FEKEX2XlagIKzrlH7wwxkKgtiew==, tarball: https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-6.0.8.tgz} engines: {node: ^20.19.0 || >=22.12.0} peerDependencies: vite: ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0 vue: ^3.2.25 '@vitest/coverage-v8@4.1.10': - resolution: {integrity: sha512-IM49HmthevbgAO4anp1hwtoT9wYe59w0LR00gr+eagHE+ZJ5lK4sLPeO0ubgoJcwLk6dehU3R24N+FbEEKDc8g==} + resolution: {integrity: sha512-IM49HmthevbgAO4anp1hwtoT9wYe59w0LR00gr+eagHE+ZJ5lK4sLPeO0ubgoJcwLk6dehU3R24N+FbEEKDc8g==, tarball: https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-4.1.10.tgz} peerDependencies: '@vitest/browser': 4.1.10 vitest: 4.1.10 @@ -7248,13 +7275,13 @@ packages: optional: true '@vitest/expect@3.2.4': - resolution: {integrity: sha512-Io0yyORnB6sikFlt8QW5K7slY4OjqNX9jmJQ02QDda8lyM6B5oNgVWoSoKPac8/kgnCUzuHQKrSLtu/uOqqrig==} + resolution: {integrity: sha512-Io0yyORnB6sikFlt8QW5K7slY4OjqNX9jmJQ02QDda8lyM6B5oNgVWoSoKPac8/kgnCUzuHQKrSLtu/uOqqrig==, tarball: https://registry.npmjs.org/@vitest/expect/-/expect-3.2.4.tgz} '@vitest/expect@4.0.18': - resolution: {integrity: sha512-8sCWUyckXXYvx4opfzVY03EOiYVxyNrHS5QxX3DAIi5dpJAAkyJezHCP77VMX4HKA2LDT/Jpfo8i2r5BE3GnQQ==} + resolution: {integrity: sha512-8sCWUyckXXYvx4opfzVY03EOiYVxyNrHS5QxX3DAIi5dpJAAkyJezHCP77VMX4HKA2LDT/Jpfo8i2r5BE3GnQQ==, tarball: https://registry.npmjs.org/@vitest/expect/-/expect-4.0.18.tgz} '@vitest/mocker@3.2.4': - resolution: {integrity: sha512-46ryTE9RZO/rfDd7pEqFl7etuyzekzEhUbTW3BvmeO/BcCMEgq59BKhek3dXDWgAj4oMK6OZi+vRr1wPW6qjEQ==} + resolution: {integrity: sha512-46ryTE9RZO/rfDd7pEqFl7etuyzekzEhUbTW3BvmeO/BcCMEgq59BKhek3dXDWgAj4oMK6OZi+vRr1wPW6qjEQ==, tarball: https://registry.npmjs.org/@vitest/mocker/-/mocker-3.2.4.tgz} peerDependencies: msw: ^2.4.9 vite: ^5.0.0 || ^6.0.0 || ^7.0.0-0 @@ -7265,7 +7292,7 @@ packages: optional: true '@vitest/mocker@4.0.18': - resolution: {integrity: sha512-HhVd0MDnzzsgevnOWCBj5Otnzobjy5wLBe4EdeeFGv8luMsGcYqDuFRMcttKWZA5vVO8RFjexVovXvAM4JoJDQ==} + resolution: {integrity: sha512-HhVd0MDnzzsgevnOWCBj5Otnzobjy5wLBe4EdeeFGv8luMsGcYqDuFRMcttKWZA5vVO8RFjexVovXvAM4JoJDQ==, tarball: https://registry.npmjs.org/@vitest/mocker/-/mocker-4.0.18.tgz} peerDependencies: msw: ^2.4.9 vite: ^6.0.0 || ^7.0.0-0 @@ -7276,81 +7303,81 @@ packages: optional: true '@vitest/pretty-format@3.2.4': - resolution: {integrity: sha512-IVNZik8IVRJRTr9fxlitMKeJeXFFFN0JaB9PHPGQ8NKQbGpfjlTx9zO4RefN8gp7eqjNy8nyK3NZmBzOPeIxtA==} + resolution: {integrity: sha512-IVNZik8IVRJRTr9fxlitMKeJeXFFFN0JaB9PHPGQ8NKQbGpfjlTx9zO4RefN8gp7eqjNy8nyK3NZmBzOPeIxtA==, tarball: https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-3.2.4.tgz} '@vitest/pretty-format@4.0.18': - resolution: {integrity: sha512-P24GK3GulZWC5tz87ux0m8OADrQIUVDPIjjj65vBXYG17ZeU3qD7r+MNZ1RNv4l8CGU2vtTRqixrOi9fYk/yKw==} + resolution: {integrity: sha512-P24GK3GulZWC5tz87ux0m8OADrQIUVDPIjjj65vBXYG17ZeU3qD7r+MNZ1RNv4l8CGU2vtTRqixrOi9fYk/yKw==, tarball: https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.0.18.tgz} '@vitest/pretty-format@4.1.10': - resolution: {integrity: sha512-W1HsjSH4MXQ9YfmmhLAoIYf1HRfekQCGngeIgcei6MP5QQGWUe0gkopdZQaVCFO+JDJMrAJGwa5pRpNpvy4P8Q==} + resolution: {integrity: sha512-W1HsjSH4MXQ9YfmmhLAoIYf1HRfekQCGngeIgcei6MP5QQGWUe0gkopdZQaVCFO+JDJMrAJGwa5pRpNpvy4P8Q==, tarball: https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.1.10.tgz} '@vitest/runner@4.0.18': - resolution: {integrity: sha512-rpk9y12PGa22Jg6g5M3UVVnTS7+zycIGk9ZNGN+m6tZHKQb7jrP7/77WfZy13Y/EUDd52NDsLRQhYKtv7XfPQw==} + resolution: {integrity: sha512-rpk9y12PGa22Jg6g5M3UVVnTS7+zycIGk9ZNGN+m6tZHKQb7jrP7/77WfZy13Y/EUDd52NDsLRQhYKtv7XfPQw==, tarball: https://registry.npmjs.org/@vitest/runner/-/runner-4.0.18.tgz} '@vitest/snapshot@4.0.18': - resolution: {integrity: sha512-PCiV0rcl7jKQjbgYqjtakly6T1uwv/5BQ9SwBLekVg/EaYeQFPiXcgrC2Y7vDMA8dM1SUEAEV82kgSQIlXNMvA==} + resolution: {integrity: sha512-PCiV0rcl7jKQjbgYqjtakly6T1uwv/5BQ9SwBLekVg/EaYeQFPiXcgrC2Y7vDMA8dM1SUEAEV82kgSQIlXNMvA==, tarball: https://registry.npmjs.org/@vitest/snapshot/-/snapshot-4.0.18.tgz} '@vitest/spy@3.2.4': - resolution: {integrity: sha512-vAfasCOe6AIK70iP5UD11Ac4siNUNJ9i/9PZ3NKx07sG6sUxeag1LWdNrMWeKKYBLlzuK+Gn65Yd5nyL6ds+nw==} + resolution: {integrity: sha512-vAfasCOe6AIK70iP5UD11Ac4siNUNJ9i/9PZ3NKx07sG6sUxeag1LWdNrMWeKKYBLlzuK+Gn65Yd5nyL6ds+nw==, tarball: https://registry.npmjs.org/@vitest/spy/-/spy-3.2.4.tgz} '@vitest/spy@4.0.18': - resolution: {integrity: sha512-cbQt3PTSD7P2OARdVW3qWER5EGq7PHlvE+QfzSC0lbwO+xnt7+XH06ZzFjFRgzUX//JmpxrCu92VdwvEPlWSNw==} + resolution: {integrity: sha512-cbQt3PTSD7P2OARdVW3qWER5EGq7PHlvE+QfzSC0lbwO+xnt7+XH06ZzFjFRgzUX//JmpxrCu92VdwvEPlWSNw==, tarball: https://registry.npmjs.org/@vitest/spy/-/spy-4.0.18.tgz} '@vitest/ui@4.0.18': - resolution: {integrity: sha512-CGJ25bc8fRi8Lod/3GHSvXRKi7nBo3kxh0ApW4yCjmrWmRmlT53B5E08XRSZRliygG0aVNxLrBEqPYdz/KcCtQ==} + resolution: {integrity: sha512-CGJ25bc8fRi8Lod/3GHSvXRKi7nBo3kxh0ApW4yCjmrWmRmlT53B5E08XRSZRliygG0aVNxLrBEqPYdz/KcCtQ==, tarball: https://registry.npmjs.org/@vitest/ui/-/ui-4.0.18.tgz} peerDependencies: vitest: 4.0.18 '@vitest/utils@3.2.4': - resolution: {integrity: sha512-fB2V0JFrQSMsCo9HiSq3Ezpdv4iYaXRG1Sx8edX3MwxfyNn83mKiGzOcH+Fkxt4MHxr3y42fQi1oeAInqgX2QA==} + resolution: {integrity: sha512-fB2V0JFrQSMsCo9HiSq3Ezpdv4iYaXRG1Sx8edX3MwxfyNn83mKiGzOcH+Fkxt4MHxr3y42fQi1oeAInqgX2QA==, tarball: https://registry.npmjs.org/@vitest/utils/-/utils-3.2.4.tgz} '@vitest/utils@4.0.18': - resolution: {integrity: sha512-msMRKLMVLWygpK3u2Hybgi4MNjcYJvwTb0Ru09+fOyCXIgT5raYP041DRRdiJiI3k/2U6SEbAETB3YtBrUkCFA==} + resolution: {integrity: sha512-msMRKLMVLWygpK3u2Hybgi4MNjcYJvwTb0Ru09+fOyCXIgT5raYP041DRRdiJiI3k/2U6SEbAETB3YtBrUkCFA==, tarball: https://registry.npmjs.org/@vitest/utils/-/utils-4.0.18.tgz} '@vitest/utils@4.1.10': - resolution: {integrity: sha512-fy9am/HWxbaGt/Sawrp90vt6Y6jQwf1RX77cz3uwoJwJVMli/e1IEwRPnMNJ7vKfPTwo0diXifkpPvwH9v7nGA==} + resolution: {integrity: sha512-fy9am/HWxbaGt/Sawrp90vt6Y6jQwf1RX77cz3uwoJwJVMli/e1IEwRPnMNJ7vKfPTwo0diXifkpPvwH9v7nGA==, tarball: https://registry.npmjs.org/@vitest/utils/-/utils-4.1.10.tgz} '@volar/language-core@2.4.15': - resolution: {integrity: sha512-3VHw+QZU0ZG9IuQmzT68IyN4hZNd9GchGPhbD9+pa8CVv7rnoOZwo7T8weIbrRmihqy3ATpdfXFnqRrfPVK6CA==} + resolution: {integrity: sha512-3VHw+QZU0ZG9IuQmzT68IyN4hZNd9GchGPhbD9+pa8CVv7rnoOZwo7T8weIbrRmihqy3ATpdfXFnqRrfPVK6CA==, tarball: https://registry.npmjs.org/@volar/language-core/-/language-core-2.4.15.tgz} '@volar/language-core@2.4.28': - resolution: {integrity: sha512-w4qhIJ8ZSitgLAkVay6AbcnC7gP3glYM3fYwKV3srj8m494E3xtrCv6E+bWviiK/8hs6e6t1ij1s2Endql7vzQ==} + resolution: {integrity: sha512-w4qhIJ8ZSitgLAkVay6AbcnC7gP3glYM3fYwKV3srj8m494E3xtrCv6E+bWviiK/8hs6e6t1ij1s2Endql7vzQ==, tarball: https://registry.npmjs.org/@volar/language-core/-/language-core-2.4.28.tgz} '@volar/source-map@2.4.15': - resolution: {integrity: sha512-CPbMWlUN6hVZJYGcU/GSoHu4EnCHiLaXI9n8c9la6RaI9W5JHX+NqG+GSQcB0JdC2FIBLdZJwGsfKyBB71VlTg==} + resolution: {integrity: sha512-CPbMWlUN6hVZJYGcU/GSoHu4EnCHiLaXI9n8c9la6RaI9W5JHX+NqG+GSQcB0JdC2FIBLdZJwGsfKyBB71VlTg==, tarball: https://registry.npmjs.org/@volar/source-map/-/source-map-2.4.15.tgz} '@volar/source-map@2.4.28': - resolution: {integrity: sha512-yX2BDBqJkRXfKw8my8VarTyjv48QwxdJtvRgUpNE5erCsgEUdI2DsLbpa+rOQVAJYshY99szEcRDmyHbF10ggQ==} + resolution: {integrity: sha512-yX2BDBqJkRXfKw8my8VarTyjv48QwxdJtvRgUpNE5erCsgEUdI2DsLbpa+rOQVAJYshY99szEcRDmyHbF10ggQ==, tarball: https://registry.npmjs.org/@volar/source-map/-/source-map-2.4.28.tgz} '@volar/typescript@2.4.15': - resolution: {integrity: sha512-2aZ8i0cqPGjXb4BhkMsPYDkkuc2ZQ6yOpqwAuNwUoncELqoy5fRgOQtLR9gB0g902iS0NAkvpIzs27geVyVdPg==} + resolution: {integrity: sha512-2aZ8i0cqPGjXb4BhkMsPYDkkuc2ZQ6yOpqwAuNwUoncELqoy5fRgOQtLR9gB0g902iS0NAkvpIzs27geVyVdPg==, tarball: https://registry.npmjs.org/@volar/typescript/-/typescript-2.4.15.tgz} '@volar/typescript@2.4.28': - resolution: {integrity: sha512-Ja6yvWrbis2QtN4ClAKreeUZPVYMARDYZl9LMEv1iQ1QdepB6wn0jTRxA9MftYmYa4DQ4k/DaSZpFPUfxl8giw==} + resolution: {integrity: sha512-Ja6yvWrbis2QtN4ClAKreeUZPVYMARDYZl9LMEv1iQ1QdepB6wn0jTRxA9MftYmYa4DQ4k/DaSZpFPUfxl8giw==, tarball: https://registry.npmjs.org/@volar/typescript/-/typescript-2.4.28.tgz} '@vue/compiler-core@3.5.39': - resolution: {integrity: sha512-16KBTEXAJCpDr0mwlw+AZyhu8iyC7R3S2vBwsI7QnWJU6X3WKc9VKeNEZpiMdZ569qWhz9574L3vV55qRL0Vtw==} + resolution: {integrity: sha512-16KBTEXAJCpDr0mwlw+AZyhu8iyC7R3S2vBwsI7QnWJU6X3WKc9VKeNEZpiMdZ569qWhz9574L3vV55qRL0Vtw==, tarball: https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.39.tgz} '@vue/compiler-dom@3.5.39': - resolution: {integrity: sha512-oQPigALqYbNxTNPvNgSOe+czwVExfbVF02lz8jP0S3AXJiu3jxYDygNUiqSep4ezzW8XgnubqH63My2A7JR/vg==} + resolution: {integrity: sha512-oQPigALqYbNxTNPvNgSOe+czwVExfbVF02lz8jP0S3AXJiu3jxYDygNUiqSep4ezzW8XgnubqH63My2A7JR/vg==, tarball: https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.39.tgz} '@vue/compiler-sfc@3.5.39': - resolution: {integrity: sha512-d0ki86iOyN8LoZPBmk5SJWNwHP19CnDDCfuo//+2WJa2g5Ke0Jay983PIBIcSSzldC68I8DrD5GrHV3OSDfodg==} + resolution: {integrity: sha512-d0ki86iOyN8LoZPBmk5SJWNwHP19CnDDCfuo//+2WJa2g5Ke0Jay983PIBIcSSzldC68I8DrD5GrHV3OSDfodg==, tarball: https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.39.tgz} '@vue/compiler-ssr@3.5.39': - resolution: {integrity: sha512-Ce7/wvwMHai74bdszfXExdazFigYnlF9zgCmEQUcM1j0fOymlouZ7XilTYNo8oUjhlnjYOZbGrcYKuqjz89Ucw==} + resolution: {integrity: sha512-Ce7/wvwMHai74bdszfXExdazFigYnlF9zgCmEQUcM1j0fOymlouZ7XilTYNo8oUjhlnjYOZbGrcYKuqjz89Ucw==, tarball: https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.39.tgz} '@vue/compiler-vue2@2.7.16': - resolution: {integrity: sha512-qYC3Psj9S/mfu9uVi5WvNZIzq+xnXMhOwbTFKKDD7b1lhpnn71jXSFdTQ+WsIEk0ONCd7VV2IMm7ONl6tbQ86A==} + resolution: {integrity: sha512-qYC3Psj9S/mfu9uVi5WvNZIzq+xnXMhOwbTFKKDD7b1lhpnn71jXSFdTQ+WsIEk0ONCd7VV2IMm7ONl6tbQ86A==, tarball: https://registry.npmjs.org/@vue/compiler-vue2/-/compiler-vue2-2.7.16.tgz} '@vue/eslint-config-prettier@10.2.0': - resolution: {integrity: sha512-GL3YBLwv/+b86yHcNNfPJxOTtVFJ4Mbc9UU3zR+KVoG7SwGTjPT+32fXamscNumElhcpXW3mT0DgzS9w32S7Bw==} + resolution: {integrity: sha512-GL3YBLwv/+b86yHcNNfPJxOTtVFJ4Mbc9UU3zR+KVoG7SwGTjPT+32fXamscNumElhcpXW3mT0DgzS9w32S7Bw==, tarball: https://registry.npmjs.org/@vue/eslint-config-prettier/-/eslint-config-prettier-10.2.0.tgz} peerDependencies: eslint: '>= 8.21.0' prettier: '>= 3.0.0' '@vue/eslint-config-typescript@14.9.0': - resolution: {integrity: sha512-E3j9hDlfVf10F30MRcLTPY2IIhWIx1nsvkVukk14kTcuA+oBVot9zsP1hzsO+PAMDxV3Fd9FimBJtUBNBL5KFA==} + resolution: {integrity: sha512-E3j9hDlfVf10F30MRcLTPY2IIhWIx1nsvkVukk14kTcuA+oBVot9zsP1hzsO+PAMDxV3Fd9FimBJtUBNBL5KFA==, tarball: https://registry.npmjs.org/@vue/eslint-config-typescript/-/eslint-config-typescript-14.9.0.tgz} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} hasBin: true peerDependencies: @@ -7362,7 +7389,7 @@ packages: optional: true '@vue/language-core@2.2.0': - resolution: {integrity: sha512-O1ZZFaaBGkKbsRfnVH1ifOK1/1BUkyK+3SQsfnh6PmMmD4qJcTU8godCeA96jjDRTL6zgnK7YzCHfaUlH2r0Mw==} + resolution: {integrity: sha512-O1ZZFaaBGkKbsRfnVH1ifOK1/1BUkyK+3SQsfnh6PmMmD4qJcTU8godCeA96jjDRTL6zgnK7YzCHfaUlH2r0Mw==, tarball: https://registry.npmjs.org/@vue/language-core/-/language-core-2.2.0.tgz} peerDependencies: typescript: '*' peerDependenciesMeta: @@ -7370,7 +7397,7 @@ packages: optional: true '@vue/language-core@2.2.12': - resolution: {integrity: sha512-IsGljWbKGU1MZpBPN+BvPAdr55YPkj2nB/TBNGNC32Vy2qLG25DYu/NBN2vNtZqdRbTRjaoYrahLrToim2NanA==} + resolution: {integrity: sha512-IsGljWbKGU1MZpBPN+BvPAdr55YPkj2nB/TBNGNC32Vy2qLG25DYu/NBN2vNtZqdRbTRjaoYrahLrToim2NanA==, tarball: https://registry.npmjs.org/@vue/language-core/-/language-core-2.2.12.tgz} peerDependencies: typescript: '*' peerDependenciesMeta: @@ -7378,24 +7405,24 @@ packages: optional: true '@vue/reactivity@3.5.39': - resolution: {integrity: sha512-TpsuBJ9gGlZa5d23XcM2y8EXanz9dZeVDQBXRwzy46ItgvM+rWpzs+UVM0wcRLxGvcav0HE5jz2gNL53xlRAog==} + resolution: {integrity: sha512-TpsuBJ9gGlZa5d23XcM2y8EXanz9dZeVDQBXRwzy46ItgvM+rWpzs+UVM0wcRLxGvcav0HE5jz2gNL53xlRAog==, tarball: https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.5.39.tgz} '@vue/runtime-core@3.5.39': - resolution: {integrity: sha512-9GLtNyRvPAUMbX+7ono0RC2j0guo2LXVi8LvcmAooImACUKm0oFf0jjwbX8/H0AE/t1nxhAkn8RSl9PMCzzxZw==} + resolution: {integrity: sha512-9GLtNyRvPAUMbX+7ono0RC2j0guo2LXVi8LvcmAooImACUKm0oFf0jjwbX8/H0AE/t1nxhAkn8RSl9PMCzzxZw==, tarball: https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.5.39.tgz} '@vue/runtime-dom@3.5.39': - resolution: {integrity: sha512-7Y6aAGboKcXAZ3ECuUy7RrS5yy2r47dhTp2SKaJmYxjopImaVFaNa5Ne66NwGovsrxVAl5S5rwc7m22UG7Lmww==} + resolution: {integrity: sha512-7Y6aAGboKcXAZ3ECuUy7RrS5yy2r47dhTp2SKaJmYxjopImaVFaNa5Ne66NwGovsrxVAl5S5rwc7m22UG7Lmww==, tarball: https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.5.39.tgz} '@vue/server-renderer@3.5.39': - resolution: {integrity: sha512-yZSakiAGw85rZfG7UM8akMnIF+FmeiNk47uvHf2nVBBSe+dIKUhZuZq9+XgJhbV3nS5Z4ALH23/MpXofW+mbcw==} + resolution: {integrity: sha512-yZSakiAGw85rZfG7UM8akMnIF+FmeiNk47uvHf2nVBBSe+dIKUhZuZq9+XgJhbV3nS5Z4ALH23/MpXofW+mbcw==, tarball: https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.5.39.tgz} peerDependencies: vue: 3.5.39 '@vue/shared@3.5.29': - resolution: {integrity: sha512-w7SR0A5zyRByL9XUkCfdLs7t9XOHUyJ67qPGQjOou3p6GvBeBW+AVjUUmlxtZ4PIYaRvE+1LmK44O4uajlZwcg==} + resolution: {integrity: sha512-w7SR0A5zyRByL9XUkCfdLs7t9XOHUyJ67qPGQjOou3p6GvBeBW+AVjUUmlxtZ4PIYaRvE+1LmK44O4uajlZwcg==, tarball: https://registry.npmjs.org/@vue/shared/-/shared-3.5.29.tgz} '@vue/test-utils@2.4.11': - resolution: {integrity: sha512-GDqaqZsA6m2E5vNzej0aYiIb6BX8xV9pNSbbbXKOfEYwg7ZNblVX8suyqmUBThq8VIrgAJNxn+z72hVtUeiWHA==} + resolution: {integrity: sha512-GDqaqZsA6m2E5vNzej0aYiIb6BX8xV9pNSbbbXKOfEYwg7ZNblVX8suyqmUBThq8VIrgAJNxn+z72hVtUeiWHA==, tarball: https://registry.npmjs.org/@vue/test-utils/-/test-utils-2.4.11.tgz} peerDependencies: '@vue/compiler-dom': 3.x '@vue/server-renderer': 3.x @@ -7405,197 +7432,203 @@ packages: optional: true '@webassemblyjs/ast@1.11.1': - resolution: {integrity: sha512-ukBh14qFLjxTQNTXocdyksN5QdM28S1CxHt2rdskFyL+xFV7VremuBLVbmCePj+URalXBENx/9Lm7lnhihtCSw==} + resolution: {integrity: sha512-ukBh14qFLjxTQNTXocdyksN5QdM28S1CxHt2rdskFyL+xFV7VremuBLVbmCePj+URalXBENx/9Lm7lnhihtCSw==, tarball: https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.1.tgz} '@webassemblyjs/ast@1.14.1': - resolution: {integrity: sha512-nuBEDgQfm1ccRp/8bCQrx1frohyufl4JlbMMZ4P1wpeOfDhF6FQkxZJ1b/e+PLwr6X1Nhw6OLme5usuBWYBvuQ==} + resolution: {integrity: sha512-nuBEDgQfm1ccRp/8bCQrx1frohyufl4JlbMMZ4P1wpeOfDhF6FQkxZJ1b/e+PLwr6X1Nhw6OLme5usuBWYBvuQ==, tarball: https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.14.1.tgz} '@webassemblyjs/floating-point-hex-parser@1.11.1': - resolution: {integrity: sha512-iGRfyc5Bq+NnNuX8b5hwBrRjzf0ocrJPI6GWFodBFzmFnyvrQ83SHKhmilCU/8Jv67i4GJZBMhEzltxzcNagtQ==} + resolution: {integrity: sha512-iGRfyc5Bq+NnNuX8b5hwBrRjzf0ocrJPI6GWFodBFzmFnyvrQ83SHKhmilCU/8Jv67i4GJZBMhEzltxzcNagtQ==, tarball: https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.1.tgz} '@webassemblyjs/floating-point-hex-parser@1.13.2': - resolution: {integrity: sha512-6oXyTOzbKxGH4steLbLNOu71Oj+C8Lg34n6CqRvqfS2O71BxY6ByfMDRhBytzknj9yGUPVJ1qIKhRlAwO1AovA==} + resolution: {integrity: sha512-6oXyTOzbKxGH4steLbLNOu71Oj+C8Lg34n6CqRvqfS2O71BxY6ByfMDRhBytzknj9yGUPVJ1qIKhRlAwO1AovA==, tarball: https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.13.2.tgz} '@webassemblyjs/helper-api-error@1.11.1': - resolution: {integrity: sha512-RlhS8CBCXfRUR/cwo2ho9bkheSXG0+NwooXcc3PAILALf2QLdFyj7KGsKRbVc95hZnhnERon4kW/D3SZpp6Tcg==} + resolution: {integrity: sha512-RlhS8CBCXfRUR/cwo2ho9bkheSXG0+NwooXcc3PAILALf2QLdFyj7KGsKRbVc95hZnhnERon4kW/D3SZpp6Tcg==, tarball: https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.1.tgz} '@webassemblyjs/helper-api-error@1.13.2': - resolution: {integrity: sha512-U56GMYxy4ZQCbDZd6JuvvNV/WFildOjsaWD3Tzzvmw/mas3cXzRJPMjP83JqEsgSbyrmaGjBfDtV7KDXV9UzFQ==} + resolution: {integrity: sha512-U56GMYxy4ZQCbDZd6JuvvNV/WFildOjsaWD3Tzzvmw/mas3cXzRJPMjP83JqEsgSbyrmaGjBfDtV7KDXV9UzFQ==, tarball: https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.13.2.tgz} '@webassemblyjs/helper-buffer@1.11.1': - resolution: {integrity: sha512-gwikF65aDNeeXa8JxXa2BAk+REjSyhrNC9ZwdT0f8jc4dQQeDQ7G4m0f2QCLPJiMTTO6wfDmRmj/pW0PsUvIcA==} + resolution: {integrity: sha512-gwikF65aDNeeXa8JxXa2BAk+REjSyhrNC9ZwdT0f8jc4dQQeDQ7G4m0f2QCLPJiMTTO6wfDmRmj/pW0PsUvIcA==, tarball: https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.1.tgz} '@webassemblyjs/helper-buffer@1.14.1': - resolution: {integrity: sha512-jyH7wtcHiKssDtFPRB+iQdxlDf96m0E39yb0k5uJVhFGleZFoNw1c4aeIcVUPPbXUVJ94wwnMOAqUHyzoEPVMA==} + resolution: {integrity: sha512-jyH7wtcHiKssDtFPRB+iQdxlDf96m0E39yb0k5uJVhFGleZFoNw1c4aeIcVUPPbXUVJ94wwnMOAqUHyzoEPVMA==, tarball: https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.14.1.tgz} '@webassemblyjs/helper-numbers@1.11.1': - resolution: {integrity: sha512-vDkbxiB8zfnPdNK9Rajcey5C0w+QJugEglN0of+kmO8l7lDb77AnlKYQF7aarZuCrv+l0UvqL+68gSDr3k9LPQ==} + resolution: {integrity: sha512-vDkbxiB8zfnPdNK9Rajcey5C0w+QJugEglN0of+kmO8l7lDb77AnlKYQF7aarZuCrv+l0UvqL+68gSDr3k9LPQ==, tarball: https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.1.tgz} '@webassemblyjs/helper-numbers@1.13.2': - resolution: {integrity: sha512-FE8aCmS5Q6eQYcV3gI35O4J789wlQA+7JrqTTpJqn5emA4U2hvwJmvFRC0HODS+3Ye6WioDklgd6scJ3+PLnEA==} + resolution: {integrity: sha512-FE8aCmS5Q6eQYcV3gI35O4J789wlQA+7JrqTTpJqn5emA4U2hvwJmvFRC0HODS+3Ye6WioDklgd6scJ3+PLnEA==, tarball: https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.13.2.tgz} '@webassemblyjs/helper-wasm-bytecode@1.11.1': - resolution: {integrity: sha512-PvpoOGiJwXeTrSf/qfudJhwlvDQxFgelbMqtq52WWiXC6Xgg1IREdngmPN3bs4RoO83PnL/nFrxucXj1+BX62Q==} + resolution: {integrity: sha512-PvpoOGiJwXeTrSf/qfudJhwlvDQxFgelbMqtq52WWiXC6Xgg1IREdngmPN3bs4RoO83PnL/nFrxucXj1+BX62Q==, tarball: https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.1.tgz} '@webassemblyjs/helper-wasm-bytecode@1.13.2': - resolution: {integrity: sha512-3QbLKy93F0EAIXLh0ogEVR6rOubA9AoZ+WRYhNbFyuB70j3dRdwH9g+qXhLAO0kiYGlg3TxDV+I4rQTr/YNXkA==} + resolution: {integrity: sha512-3QbLKy93F0EAIXLh0ogEVR6rOubA9AoZ+WRYhNbFyuB70j3dRdwH9g+qXhLAO0kiYGlg3TxDV+I4rQTr/YNXkA==, tarball: https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.13.2.tgz} '@webassemblyjs/helper-wasm-section@1.11.1': - resolution: {integrity: sha512-10P9No29rYX1j7F3EVPX3JvGPQPae+AomuSTPiF9eBQeChHI6iqjMIwR9JmOJXwpnn/oVGDk7I5IlskuMwU/pg==} + resolution: {integrity: sha512-10P9No29rYX1j7F3EVPX3JvGPQPae+AomuSTPiF9eBQeChHI6iqjMIwR9JmOJXwpnn/oVGDk7I5IlskuMwU/pg==, tarball: https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.1.tgz} '@webassemblyjs/helper-wasm-section@1.14.1': - resolution: {integrity: sha512-ds5mXEqTJ6oxRoqjhWDU83OgzAYjwsCV8Lo/N+oRsNDmx/ZDpqalmrtgOMkHwxsG0iI//3BwWAErYRHtgn0dZw==} + resolution: {integrity: sha512-ds5mXEqTJ6oxRoqjhWDU83OgzAYjwsCV8Lo/N+oRsNDmx/ZDpqalmrtgOMkHwxsG0iI//3BwWAErYRHtgn0dZw==, tarball: https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.14.1.tgz} '@webassemblyjs/ieee754@1.11.1': - resolution: {integrity: sha512-hJ87QIPtAMKbFq6CGTkZYJivEwZDbQUgYd3qKSadTNOhVY7p+gfP6Sr0lLRVTaG1JjFj+r3YchoqRYxNH3M0GQ==} + resolution: {integrity: sha512-hJ87QIPtAMKbFq6CGTkZYJivEwZDbQUgYd3qKSadTNOhVY7p+gfP6Sr0lLRVTaG1JjFj+r3YchoqRYxNH3M0GQ==, tarball: https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.1.tgz} '@webassemblyjs/ieee754@1.13.2': - resolution: {integrity: sha512-4LtOzh58S/5lX4ITKxnAK2USuNEvpdVV9AlgGQb8rJDHaLeHciwG4zlGr0j/SNWlr7x3vO1lDEsuePvtcDNCkw==} + resolution: {integrity: sha512-4LtOzh58S/5lX4ITKxnAK2USuNEvpdVV9AlgGQb8rJDHaLeHciwG4zlGr0j/SNWlr7x3vO1lDEsuePvtcDNCkw==, tarball: https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.13.2.tgz} '@webassemblyjs/leb128@1.11.1': - resolution: {integrity: sha512-BJ2P0hNZ0u+Th1YZXJpzW6miwqQUGcIHT1G/sf72gLVD9DZ5AdYTqPNbHZh6K1M5VmKvFXwGSWZADz+qBWxeRw==} + resolution: {integrity: sha512-BJ2P0hNZ0u+Th1YZXJpzW6miwqQUGcIHT1G/sf72gLVD9DZ5AdYTqPNbHZh6K1M5VmKvFXwGSWZADz+qBWxeRw==, tarball: https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.1.tgz} '@webassemblyjs/leb128@1.13.2': - resolution: {integrity: sha512-Lde1oNoIdzVzdkNEAWZ1dZ5orIbff80YPdHx20mrHwHrVNNTjNr8E3xz9BdpcGqRQbAEa+fkrCb+fRFTl/6sQw==} + resolution: {integrity: sha512-Lde1oNoIdzVzdkNEAWZ1dZ5orIbff80YPdHx20mrHwHrVNNTjNr8E3xz9BdpcGqRQbAEa+fkrCb+fRFTl/6sQw==, tarball: https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.13.2.tgz} '@webassemblyjs/utf8@1.11.1': - resolution: {integrity: sha512-9kqcxAEdMhiwQkHpkNiorZzqpGrodQQ2IGrHHxCy+Ozng0ofyMA0lTqiLkVs1uzTRejX+/O0EOT7KxqVPuXosQ==} + resolution: {integrity: sha512-9kqcxAEdMhiwQkHpkNiorZzqpGrodQQ2IGrHHxCy+Ozng0ofyMA0lTqiLkVs1uzTRejX+/O0EOT7KxqVPuXosQ==, tarball: https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.1.tgz} '@webassemblyjs/utf8@1.13.2': - resolution: {integrity: sha512-3NQWGjKTASY1xV5m7Hr0iPeXD9+RDobLll3T9d2AO+g3my8xy5peVyjSag4I50mR1bBSN/Ct12lo+R9tJk0NZQ==} + resolution: {integrity: sha512-3NQWGjKTASY1xV5m7Hr0iPeXD9+RDobLll3T9d2AO+g3my8xy5peVyjSag4I50mR1bBSN/Ct12lo+R9tJk0NZQ==, tarball: https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.13.2.tgz} '@webassemblyjs/wasm-edit@1.11.1': - resolution: {integrity: sha512-g+RsupUC1aTHfR8CDgnsVRVZFJqdkFHpsHMfJuWQzWU3tvnLC07UqHICfP+4XyL2tnr1amvl1Sdp06TnYCmVkA==} + resolution: {integrity: sha512-g+RsupUC1aTHfR8CDgnsVRVZFJqdkFHpsHMfJuWQzWU3tvnLC07UqHICfP+4XyL2tnr1amvl1Sdp06TnYCmVkA==, tarball: https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.1.tgz} '@webassemblyjs/wasm-edit@1.14.1': - resolution: {integrity: sha512-RNJUIQH/J8iA/1NzlE4N7KtyZNHi3w7at7hDjvRNm5rcUXa00z1vRz3glZoULfJ5mpvYhLybmVcwcjGrC1pRrQ==} + resolution: {integrity: sha512-RNJUIQH/J8iA/1NzlE4N7KtyZNHi3w7at7hDjvRNm5rcUXa00z1vRz3glZoULfJ5mpvYhLybmVcwcjGrC1pRrQ==, tarball: https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.14.1.tgz} '@webassemblyjs/wasm-gen@1.11.1': - resolution: {integrity: sha512-F7QqKXwwNlMmsulj6+O7r4mmtAlCWfO/0HdgOxSklZfQcDu0TpLiD1mRt/zF25Bk59FIjEuGAIyn5ei4yMfLhA==} + resolution: {integrity: sha512-F7QqKXwwNlMmsulj6+O7r4mmtAlCWfO/0HdgOxSklZfQcDu0TpLiD1mRt/zF25Bk59FIjEuGAIyn5ei4yMfLhA==, tarball: https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.1.tgz} '@webassemblyjs/wasm-gen@1.14.1': - resolution: {integrity: sha512-AmomSIjP8ZbfGQhumkNvgC33AY7qtMCXnN6bL2u2Js4gVCg8fp735aEiMSBbDR7UQIj90n4wKAFUSEd0QN2Ukg==} + resolution: {integrity: sha512-AmomSIjP8ZbfGQhumkNvgC33AY7qtMCXnN6bL2u2Js4gVCg8fp735aEiMSBbDR7UQIj90n4wKAFUSEd0QN2Ukg==, tarball: https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.14.1.tgz} '@webassemblyjs/wasm-opt@1.11.1': - resolution: {integrity: sha512-VqnkNqnZlU5EB64pp1l7hdm3hmQw7Vgqa0KF/KCNO9sIpI6Fk6brDEiX+iCOYrvMuBWDws0NkTOxYEb85XQHHw==} + resolution: {integrity: sha512-VqnkNqnZlU5EB64pp1l7hdm3hmQw7Vgqa0KF/KCNO9sIpI6Fk6brDEiX+iCOYrvMuBWDws0NkTOxYEb85XQHHw==, tarball: https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.1.tgz} '@webassemblyjs/wasm-opt@1.14.1': - resolution: {integrity: sha512-PTcKLUNvBqnY2U6E5bdOQcSM+oVP/PmrDY9NzowJjislEjwP/C4an2303MCVS2Mg9d3AJpIGdUFIQQWbPds0Sw==} + resolution: {integrity: sha512-PTcKLUNvBqnY2U6E5bdOQcSM+oVP/PmrDY9NzowJjislEjwP/C4an2303MCVS2Mg9d3AJpIGdUFIQQWbPds0Sw==, tarball: https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.14.1.tgz} '@webassemblyjs/wasm-parser@1.11.1': - resolution: {integrity: sha512-rrBujw+dJu32gYB7/Lup6UhdkPx9S9SnobZzRVL7VcBH9Bt9bCBLEuX/YXOOtBsOZ4NQrRykKhffRWHvigQvOA==} + resolution: {integrity: sha512-rrBujw+dJu32gYB7/Lup6UhdkPx9S9SnobZzRVL7VcBH9Bt9bCBLEuX/YXOOtBsOZ4NQrRykKhffRWHvigQvOA==, tarball: https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.1.tgz} '@webassemblyjs/wasm-parser@1.14.1': - resolution: {integrity: sha512-JLBl+KZ0R5qB7mCnud/yyX08jWFw5MsoalJ1pQ4EdFlgj9VdXKGuENGsiCIjegI1W7p91rUlcB/LB5yRJKNTcQ==} + resolution: {integrity: sha512-JLBl+KZ0R5qB7mCnud/yyX08jWFw5MsoalJ1pQ4EdFlgj9VdXKGuENGsiCIjegI1W7p91rUlcB/LB5yRJKNTcQ==, tarball: https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.14.1.tgz} '@webassemblyjs/wast-printer@1.11.1': - resolution: {integrity: sha512-IQboUWM4eKzWW+N/jij2sRatKMh99QEelo3Eb2q0qXkvPRISAj8Qxtmw5itwqK+TTkBuUIE45AxYPToqPtL5gg==} + resolution: {integrity: sha512-IQboUWM4eKzWW+N/jij2sRatKMh99QEelo3Eb2q0qXkvPRISAj8Qxtmw5itwqK+TTkBuUIE45AxYPToqPtL5gg==, tarball: https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.11.1.tgz} '@webassemblyjs/wast-printer@1.14.1': - resolution: {integrity: sha512-kPSSXE6De1XOR820C90RIo2ogvZG+c3KiHzqUoO/F34Y2shGzesfqv7o57xrxovZJH/MetF5UjroJ/R/3isoiw==} + resolution: {integrity: sha512-kPSSXE6De1XOR820C90RIo2ogvZG+c3KiHzqUoO/F34Y2shGzesfqv7o57xrxovZJH/MetF5UjroJ/R/3isoiw==, tarball: https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.14.1.tgz} '@xtuc/ieee754@1.2.0': - resolution: {integrity: sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==} + resolution: {integrity: sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==, tarball: https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz} '@xtuc/long@4.2.2': - resolution: {integrity: sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==} + resolution: {integrity: sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==, tarball: https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz} '@yarnpkg/lockfile@1.1.0': - resolution: {integrity: sha512-GpSwvyXOcOOlV70vbnzjj4fW5xW/FdUF6nQEt1ENy7m4ZCczi1+/buVUPAqmGfqznsORNFzUMjctTIp8a9tuCQ==} + resolution: {integrity: sha512-GpSwvyXOcOOlV70vbnzjj4fW5xW/FdUF6nQEt1ENy7m4ZCczi1+/buVUPAqmGfqznsORNFzUMjctTIp8a9tuCQ==, tarball: https://registry.npmjs.org/@yarnpkg/lockfile/-/lockfile-1.1.0.tgz} '@yarnpkg/parsers@3.0.2': - resolution: {integrity: sha512-/HcYgtUSiJiot/XWGLOlGxPYUG65+/31V8oqk17vZLW1xlCoR4PampyePljOxY2n8/3jz9+tIFzICsyGujJZoA==} + resolution: {integrity: sha512-/HcYgtUSiJiot/XWGLOlGxPYUG65+/31V8oqk17vZLW1xlCoR4PampyePljOxY2n8/3jz9+tIFzICsyGujJZoA==, tarball: https://registry.npmjs.org/@yarnpkg/parsers/-/parsers-3.0.2.tgz} engines: {node: '>=18.12.0'} '@zkochan/js-yaml@0.0.7': - resolution: {integrity: sha512-nrUSn7hzt7J6JWgWGz78ZYI8wj+gdIJdk0Ynjpp8l+trkn58Uqsf6RYrYkEK+3X18EX+TNdtJI0WxAtc+L84SQ==} + resolution: {integrity: sha512-nrUSn7hzt7J6JWgWGz78ZYI8wj+gdIJdk0Ynjpp8l+trkn58Uqsf6RYrYkEK+3X18EX+TNdtJI0WxAtc+L84SQ==, tarball: https://registry.npmjs.org/@zkochan/js-yaml/-/js-yaml-0.0.7.tgz} hasBin: true abab@2.0.6: - resolution: {integrity: sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==} + resolution: {integrity: sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==, tarball: https://registry.npmjs.org/abab/-/abab-2.0.6.tgz} deprecated: Use your platform's native atob() and btoa() methods instead abbrev@2.0.0: - resolution: {integrity: sha512-6/mh1E2u2YgEsCHdY0Yx5oW+61gZU+1vXaoiHHrpKeuRNNgFvS+/jrwHiQhB5apAf5oB7UB7E19ol2R2LKH8hQ==} + resolution: {integrity: sha512-6/mh1E2u2YgEsCHdY0Yx5oW+61gZU+1vXaoiHHrpKeuRNNgFvS+/jrwHiQhB5apAf5oB7UB7E19ol2R2LKH8hQ==, tarball: https://registry.npmjs.org/abbrev/-/abbrev-2.0.0.tgz} engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} accepts@1.3.8: - resolution: {integrity: sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==} + resolution: {integrity: sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==, tarball: https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz} engines: {node: '>= 0.6'} accepts@2.0.0: - resolution: {integrity: sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==} + resolution: {integrity: sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==, tarball: https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz} engines: {node: '>= 0.6'} acorn-globals@7.0.1: - resolution: {integrity: sha512-umOSDSDrfHbTNPuNpC2NSnnA3LUrqpevPb4T9jRx4MagXNS0rs+gwiTcAvqCRmsD6utzsrzNt+ebm00SNWiC3Q==} + resolution: {integrity: sha512-umOSDSDrfHbTNPuNpC2NSnnA3LUrqpevPb4T9jRx4MagXNS0rs+gwiTcAvqCRmsD6utzsrzNt+ebm00SNWiC3Q==, tarball: https://registry.npmjs.org/acorn-globals/-/acorn-globals-7.0.1.tgz} acorn-import-assertions@1.9.0: - resolution: {integrity: sha512-cmMwop9x+8KFhxvKrKfPYmN6/pKTYYHBqLa0DfvVZcKMJWNyWLnaqND7dx/qn66R7ewM1UX5XMaDVP5wlVTaVA==} + resolution: {integrity: sha512-cmMwop9x+8KFhxvKrKfPYmN6/pKTYYHBqLa0DfvVZcKMJWNyWLnaqND7dx/qn66R7ewM1UX5XMaDVP5wlVTaVA==, tarball: https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.9.0.tgz} deprecated: package has been renamed to acorn-import-attributes peerDependencies: acorn: ^8 acorn-import-phases@1.0.4: - resolution: {integrity: sha512-wKmbr/DDiIXzEOiWrTTUcDm24kQ2vGfZQvM2fwg2vXqR5uW6aapr7ObPtj1th32b9u90/Pf4AItvdTh42fBmVQ==} + resolution: {integrity: sha512-wKmbr/DDiIXzEOiWrTTUcDm24kQ2vGfZQvM2fwg2vXqR5uW6aapr7ObPtj1th32b9u90/Pf4AItvdTh42fBmVQ==, tarball: https://registry.npmjs.org/acorn-import-phases/-/acorn-import-phases-1.0.4.tgz} engines: {node: '>=10.13.0'} peerDependencies: acorn: ^8.14.0 acorn-jsx@5.3.2: - resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} + resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==, tarball: https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz} peerDependencies: acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 acorn-walk@8.3.5: - resolution: {integrity: sha512-HEHNfbars9v4pgpW6SO1KSPkfoS0xVOM/9UzkJltjlsHZmJasxg8aXkuZa7SMf8vKGIBhpUsPluQSqhJFCqebw==} + resolution: {integrity: sha512-HEHNfbars9v4pgpW6SO1KSPkfoS0xVOM/9UzkJltjlsHZmJasxg8aXkuZa7SMf8vKGIBhpUsPluQSqhJFCqebw==, tarball: https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.5.tgz} engines: {node: '>=0.4.0'} acorn@8.17.0: - resolution: {integrity: sha512-xRQbDb9BnwDafYNn6Vwl839DYVjqXYb1XVGtWAZ1kcDc6iwAL4hg3B1dZlRiuENFeO2H53gFG3in621AdERVAg==} + resolution: {integrity: sha512-xRQbDb9BnwDafYNn6Vwl839DYVjqXYb1XVGtWAZ1kcDc6iwAL4hg3B1dZlRiuENFeO2H53gFG3in621AdERVAg==, tarball: https://registry.npmjs.org/acorn/-/acorn-8.17.0.tgz} engines: {node: '>=0.4.0'} hasBin: true address@1.2.2: - resolution: {integrity: sha512-4B/qKCfeE/ODUaAUpSwfzazo5x29WD4r3vXiWsB7I2mSDAihwEqKO+g8GELZUQSSAo5e1XTYh3ZVfLyxBc12nA==} + resolution: {integrity: sha512-4B/qKCfeE/ODUaAUpSwfzazo5x29WD4r3vXiWsB7I2mSDAihwEqKO+g8GELZUQSSAo5e1XTYh3ZVfLyxBc12nA==, tarball: https://registry.npmjs.org/address/-/address-1.2.2.tgz} engines: {node: '>= 10.0.0'} address@2.0.3: - resolution: {integrity: sha512-XNAb/a6TCqou+TufU8/u11HCu9x1gYvOoxLwtlXgIqmkrYQADVv6ljyW2zwiPhHz9R1gItAWpuDrdJMmrOBFEA==} + resolution: {integrity: sha512-XNAb/a6TCqou+TufU8/u11HCu9x1gYvOoxLwtlXgIqmkrYQADVv6ljyW2zwiPhHz9R1gItAWpuDrdJMmrOBFEA==, tarball: https://registry.npmjs.org/address/-/address-2.0.3.tgz} engines: {node: '>= 16.0.0'} adjust-sourcemap-loader@4.0.0: - resolution: {integrity: sha512-OXwN5b9pCUXNQHJpwwD2qP40byEmSgzj8B4ydSN0uMNYWiFmJ6x6KwUllMmfk8Rwu/HJDFR7U8ubsWBoN0Xp0A==} + resolution: {integrity: sha512-OXwN5b9pCUXNQHJpwwD2qP40byEmSgzj8B4ydSN0uMNYWiFmJ6x6KwUllMmfk8Rwu/HJDFR7U8ubsWBoN0Xp0A==, tarball: https://registry.npmjs.org/adjust-sourcemap-loader/-/adjust-sourcemap-loader-4.0.0.tgz} engines: {node: '>=8.9'} adm-zip@0.5.10: - resolution: {integrity: sha512-x0HvcHqVJNTPk/Bw8JbLWlWoo6Wwnsug0fnYYro1HBrjxZ3G7/AZk7Ahv8JwDe1uIcz8eBqvu86FuF1POiG7vQ==} + resolution: {integrity: sha512-x0HvcHqVJNTPk/Bw8JbLWlWoo6Wwnsug0fnYYro1HBrjxZ3G7/AZk7Ahv8JwDe1uIcz8eBqvu86FuF1POiG7vQ==, tarball: https://registry.npmjs.org/adm-zip/-/adm-zip-0.5.10.tgz} engines: {node: '>=6.0'} adm-zip@0.5.18: - resolution: {integrity: sha512-ufJnssQGbxzLNS1Ho9bCtX4rQKCCvoVuDLHoJyc3F9dOGDB4BkWs2Ci0kv53lqocAEQ/Cbi+I2XCsNYGqVYqng==} + resolution: {integrity: sha512-ufJnssQGbxzLNS1Ho9bCtX4rQKCCvoVuDLHoJyc3F9dOGDB4BkWs2Ci0kv53lqocAEQ/Cbi+I2XCsNYGqVYqng==, tarball: https://registry.npmjs.org/adm-zip/-/adm-zip-0.5.18.tgz} engines: {node: '>=12.0'} agent-base@6.0.2: - resolution: {integrity: sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==} + resolution: {integrity: sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==, tarball: https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz} engines: {node: '>= 6.0.0'} agent-base@7.1.4: - resolution: {integrity: sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==} + resolution: {integrity: sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==, tarball: https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz} engines: {node: '>= 14'} agent-base@9.0.0: - resolution: {integrity: sha512-TQf59BsZnytt8GdJKLPfUZ54g/iaUL2OWDSFCCvMOhsHduDQxO8xC4PNeyIkVcA5KwL2phPSv0douC0fgWzmnA==} + resolution: {integrity: sha512-TQf59BsZnytt8GdJKLPfUZ54g/iaUL2OWDSFCCvMOhsHduDQxO8xC4PNeyIkVcA5KwL2phPSv0douC0fgWzmnA==, tarball: https://registry.npmjs.org/agent-base/-/agent-base-9.0.0.tgz} engines: {node: '>= 20'} + ai-sdk-provider-opencode-sdk@3.0.6: + resolution: {integrity: sha512-CZ2I5z96HUdEKAT1ExhdxLE1bjD8efzT7I5GjBevWgwNGgYZjMPE7ZfQCJFVAzWqODwdTdxp7KSZjnmcd4h1Bw==, tarball: https://registry.npmjs.org/ai-sdk-provider-opencode-sdk/-/ai-sdk-provider-opencode-sdk-3.0.6.tgz} + engines: {node: '>=18'} + peerDependencies: + zod: ^3.25.76 || ^4.1.8 + ai@6.0.225: - resolution: {integrity: sha512-TCkt/NxyZFyXyHeGO/4FMsdTIoCdwV/e78jG9iEHIjnwrAHYlqOwVj7WDFsNRqXUeoUefgbiOkNuE5k8jdpFRg==} + resolution: {integrity: sha512-TCkt/NxyZFyXyHeGO/4FMsdTIoCdwV/e78jG9iEHIjnwrAHYlqOwVj7WDFsNRqXUeoUefgbiOkNuE5k8jdpFRg==, tarball: https://registry.npmjs.org/ai/-/ai-6.0.225.tgz} engines: {node: '>=18'} peerDependencies: zod: ^3.25.76 || ^4.1.8 ajv-draft-04@1.0.0: - resolution: {integrity: sha512-mv00Te6nmYbRp5DCwclxtt7yV/joXJPGS7nM+97GdxvuttCOfgI3K4U25zboyeX0O+myI8ERluxQe5wljMmVIw==} + resolution: {integrity: sha512-mv00Te6nmYbRp5DCwclxtt7yV/joXJPGS7nM+97GdxvuttCOfgI3K4U25zboyeX0O+myI8ERluxQe5wljMmVIw==, tarball: https://registry.npmjs.org/ajv-draft-04/-/ajv-draft-04-1.0.0.tgz} peerDependencies: ajv: ^8.5.0 peerDependenciesMeta: @@ -7603,7 +7636,7 @@ packages: optional: true ajv-formats@2.1.1: - resolution: {integrity: sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==} + resolution: {integrity: sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==, tarball: https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz} peerDependencies: ajv: ^8.0.0 peerDependenciesMeta: @@ -7611,7 +7644,7 @@ packages: optional: true ajv-formats@3.0.1: - resolution: {integrity: sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==} + resolution: {integrity: sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==, tarball: https://registry.npmjs.org/ajv-formats/-/ajv-formats-3.0.1.tgz} peerDependencies: ajv: ^8.0.0 peerDependenciesMeta: @@ -7619,40 +7652,40 @@ packages: optional: true ajv-keywords@3.5.2: - resolution: {integrity: sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==} + resolution: {integrity: sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==, tarball: https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz} peerDependencies: ajv: ^6.9.1 ajv-keywords@5.1.0: - resolution: {integrity: sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==} + resolution: {integrity: sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==, tarball: https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz} peerDependencies: ajv: ^8.8.2 ajv@6.15.0: - resolution: {integrity: sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==} + resolution: {integrity: sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==, tarball: https://registry.npmjs.org/ajv/-/ajv-6.15.0.tgz} ajv@8.18.0: - resolution: {integrity: sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==} + resolution: {integrity: sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==, tarball: https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz} ajv@8.20.0: - resolution: {integrity: sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==} + resolution: {integrity: sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==, tarball: https://registry.npmjs.org/ajv/-/ajv-8.20.0.tgz} alien-signals@0.4.14: - resolution: {integrity: sha512-itUAVzhczTmP2U5yX67xVpsbbOiquusbWVyA9N+sy6+r6YVbFkahXvNCeEPWEOMhwDYwbVbGHFkVL03N9I5g+Q==} + resolution: {integrity: sha512-itUAVzhczTmP2U5yX67xVpsbbOiquusbWVyA9N+sy6+r6YVbFkahXvNCeEPWEOMhwDYwbVbGHFkVL03N9I5g+Q==, tarball: https://registry.npmjs.org/alien-signals/-/alien-signals-0.4.14.tgz} alien-signals@1.0.13: - resolution: {integrity: sha512-OGj9yyTnJEttvzhTUWuscOvtqxq5vrhF7vL9oS0xJ2mK0ItPYP1/y+vCFebfxoEyAz0++1AIwJ5CMr+Fk3nDmg==} + resolution: {integrity: sha512-OGj9yyTnJEttvzhTUWuscOvtqxq5vrhF7vL9oS0xJ2mK0ItPYP1/y+vCFebfxoEyAz0++1AIwJ5CMr+Fk3nDmg==, tarball: https://registry.npmjs.org/alien-signals/-/alien-signals-1.0.13.tgz} analytics-utils@1.1.1: - resolution: {integrity: sha512-nRybjTpRAcHVhWb1cvYaOLJaI3R79r8XjMbu5c0wd2jKmANNqSrYwybiU0X3mp+CQQdm4YiAggTXb2cIA8XhUg==} + resolution: {integrity: sha512-nRybjTpRAcHVhWb1cvYaOLJaI3R79r8XjMbu5c0wd2jKmANNqSrYwybiU0X3mp+CQQdm4YiAggTXb2cIA8XhUg==, tarball: https://registry.npmjs.org/analytics-utils/-/analytics-utils-1.1.1.tgz} peerDependencies: '@types/dlv': ^1.0.0 analytics@0.8.14: - resolution: {integrity: sha512-ZKpqWHEHBrN0lvIsrUKmt0fcXNyQuKa0JUWDRAz7LgJ+Sf4ZX+a66/ai28W4H8kJJlLeItCrhIi/xvdbV08RlA==} + resolution: {integrity: sha512-ZKpqWHEHBrN0lvIsrUKmt0fcXNyQuKa0JUWDRAz7LgJ+Sf4ZX+a66/ai28W4H8kJJlLeItCrhIi/xvdbV08RlA==, tarball: https://registry.npmjs.org/analytics/-/analytics-0.8.14.tgz} angular-eslint@22.1.0: - resolution: {integrity: sha512-LU5a6MOSeQTETnc4xaHi10p9S1cstNczr93sChi3rx8IzgH4+risIsjjQHVLSnQDrSGH2O8jAGCaChker7f7TQ==} + resolution: {integrity: sha512-LU5a6MOSeQTETnc4xaHi10p9S1cstNczr93sChi3rx8IzgH4+risIsjjQHVLSnQDrSGH2O8jAGCaChker7f7TQ==, tarball: https://registry.npmjs.org/angular-eslint/-/angular-eslint-22.1.0.tgz} peerDependencies: '@angular/cli': '>= 22.0.0 < 23.0.0' eslint: ^9.0.0 || ^10.0.0 @@ -7660,206 +7693,206 @@ packages: typescript-eslint: ^8.0.0 animation-frame-polyfill@1.1.0: - resolution: {integrity: sha512-ix9fY7tjhq+MLO/sBltxWzJHET+KWBgir2IOcEkFTcsoHH5a64c8gqe90+PS+qMSgfXd9PG5BAjrANvX12/Ckw==} + resolution: {integrity: sha512-ix9fY7tjhq+MLO/sBltxWzJHET+KWBgir2IOcEkFTcsoHH5a64c8gqe90+PS+qMSgfXd9PG5BAjrANvX12/Ckw==, tarball: https://registry.npmjs.org/animation-frame-polyfill/-/animation-frame-polyfill-1.1.0.tgz} ansi-colors@4.1.3: - resolution: {integrity: sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==} + resolution: {integrity: sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==, tarball: https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz} engines: {node: '>=6'} ansi-escapes@4.3.2: - resolution: {integrity: sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==} + resolution: {integrity: sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==, tarball: https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz} engines: {node: '>=8'} ansi-escapes@7.3.0: - resolution: {integrity: sha512-BvU8nYgGQBxcmMuEeUEmNTvrMVjJNSH7RgW24vXexN4Ven6qCvy4TntnvlnwnMLTVlcRQQdbRY8NKnaIoeWDNg==} + resolution: {integrity: sha512-BvU8nYgGQBxcmMuEeUEmNTvrMVjJNSH7RgW24vXexN4Ven6qCvy4TntnvlnwnMLTVlcRQQdbRY8NKnaIoeWDNg==, tarball: https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-7.3.0.tgz} engines: {node: '>=18'} ansi-html-community@0.0.8: - resolution: {integrity: sha512-1APHAyr3+PCamwNw3bXCPp4HFLONZt/yIH0sZp0/469KWNTEy+qN5jQ3GVX6DMZ1UXAi34yVwtTeaG/HpBuuzw==} + resolution: {integrity: sha512-1APHAyr3+PCamwNw3bXCPp4HFLONZt/yIH0sZp0/469KWNTEy+qN5jQ3GVX6DMZ1UXAi34yVwtTeaG/HpBuuzw==, tarball: https://registry.npmjs.org/ansi-html-community/-/ansi-html-community-0.0.8.tgz} engines: {'0': node >= 0.8.0} hasBin: true ansi-regex@5.0.1: - resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} + resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==, tarball: https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz} engines: {node: '>=8'} ansi-regex@6.2.2: - resolution: {integrity: sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==} + resolution: {integrity: sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==, tarball: https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz} engines: {node: '>=12'} ansi-sequence-parser@1.1.3: - resolution: {integrity: sha512-+fksAx9eG3Ab6LDnLs3ZqZa8KVJ/jYnX+D4Qe1azX+LFGFAXqynCQLOdLpNYN/l9e7l6hMWwZbrnctqr6eSQSw==} + resolution: {integrity: sha512-+fksAx9eG3Ab6LDnLs3ZqZa8KVJ/jYnX+D4Qe1azX+LFGFAXqynCQLOdLpNYN/l9e7l6hMWwZbrnctqr6eSQSw==, tarball: https://registry.npmjs.org/ansi-sequence-parser/-/ansi-sequence-parser-1.1.3.tgz} ansi-styles@4.3.0: - resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} + resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==, tarball: https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz} engines: {node: '>=8'} ansi-styles@5.2.0: - resolution: {integrity: sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==} + resolution: {integrity: sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==, tarball: https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz} engines: {node: '>=10'} ansi-styles@6.2.3: - resolution: {integrity: sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==} + resolution: {integrity: sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==, tarball: https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz} engines: {node: '>=12'} anymatch@3.1.3: - resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} + resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==, tarball: https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz} engines: {node: '>= 8'} arg@4.1.3: - resolution: {integrity: sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==} + resolution: {integrity: sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==, tarball: https://registry.npmjs.org/arg/-/arg-4.1.3.tgz} argparse@1.0.10: - resolution: {integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==} + resolution: {integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==, tarball: https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz} argparse@2.0.1: - resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} + resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==, tarball: https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz} aria-query@5.3.0: - resolution: {integrity: sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==} + resolution: {integrity: sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==, tarball: https://registry.npmjs.org/aria-query/-/aria-query-5.3.0.tgz} aria-query@5.3.2: - resolution: {integrity: sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==} + resolution: {integrity: sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==, tarball: https://registry.npmjs.org/aria-query/-/aria-query-5.3.2.tgz} engines: {node: '>= 0.4'} array-buffer-byte-length@1.0.2: - resolution: {integrity: sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==} + resolution: {integrity: sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==, tarball: https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.2.tgz} engines: {node: '>= 0.4'} array-flatten@1.1.1: - resolution: {integrity: sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==} + resolution: {integrity: sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==, tarball: https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz} array-from@2.1.1: - resolution: {integrity: sha512-GQTc6Uupx1FCavi5mPzBvVT7nEOeWMmUA9P95wpfpW1XwMSKs+KaymD5C2Up7KAUKg/mYwbsUYzdZWcoajlNZg==} + resolution: {integrity: sha512-GQTc6Uupx1FCavi5mPzBvVT7nEOeWMmUA9P95wpfpW1XwMSKs+KaymD5C2Up7KAUKg/mYwbsUYzdZWcoajlNZg==, tarball: https://registry.npmjs.org/array-from/-/array-from-2.1.1.tgz} array-includes@3.1.9: - resolution: {integrity: sha512-FmeCCAenzH0KH381SPT5FZmiA/TmpndpcaShhfgEN9eCVjnFBqq3l1xrI42y8+PPLI6hypzou4GXw00WHmPBLQ==} + resolution: {integrity: sha512-FmeCCAenzH0KH381SPT5FZmiA/TmpndpcaShhfgEN9eCVjnFBqq3l1xrI42y8+PPLI6hypzou4GXw00WHmPBLQ==, tarball: https://registry.npmjs.org/array-includes/-/array-includes-3.1.9.tgz} engines: {node: '>= 0.4'} array-union@1.0.2: - resolution: {integrity: sha512-Dxr6QJj/RdU/hCaBjOfxW+q6lyuVE6JFWIrAUpuOOhoJJoQ99cUn3igRaHVB5P9WrgFVN0FfArM3x0cueOU8ng==} + resolution: {integrity: sha512-Dxr6QJj/RdU/hCaBjOfxW+q6lyuVE6JFWIrAUpuOOhoJJoQ99cUn3igRaHVB5P9WrgFVN0FfArM3x0cueOU8ng==, tarball: https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz} engines: {node: '>=0.10.0'} array-union@3.0.1: - resolution: {integrity: sha512-1OvF9IbWwaeiM9VhzYXVQacMibxpXOMYVNIvMtKRyX9SImBXpKcFr8XvFDeEslCyuH/t6KRt7HEO94AlP8Iatw==} + resolution: {integrity: sha512-1OvF9IbWwaeiM9VhzYXVQacMibxpXOMYVNIvMtKRyX9SImBXpKcFr8XvFDeEslCyuH/t6KRt7HEO94AlP8Iatw==, tarball: https://registry.npmjs.org/array-union/-/array-union-3.0.1.tgz} engines: {node: '>=12'} array-uniq@1.0.3: - resolution: {integrity: sha512-MNha4BWQ6JbwhFhj03YK552f7cb3AzoE8SzeljgChvL1dl3IcvggXVz1DilzySZkCja+CXuZbdW7yATchWn8/Q==} + resolution: {integrity: sha512-MNha4BWQ6JbwhFhj03YK552f7cb3AzoE8SzeljgChvL1dl3IcvggXVz1DilzySZkCja+CXuZbdW7yATchWn8/Q==, tarball: https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz} engines: {node: '>=0.10.0'} array.prototype.findlast@1.2.5: - resolution: {integrity: sha512-CVvd6FHg1Z3POpBLxO6E6zr+rSKEQ9L6rZHAaY7lLfhKsWYUBBOuMs0e9o24oopj6H+geRCX0YJ+TJLBK2eHyQ==} + resolution: {integrity: sha512-CVvd6FHg1Z3POpBLxO6E6zr+rSKEQ9L6rZHAaY7lLfhKsWYUBBOuMs0e9o24oopj6H+geRCX0YJ+TJLBK2eHyQ==, tarball: https://registry.npmjs.org/array.prototype.findlast/-/array.prototype.findlast-1.2.5.tgz} engines: {node: '>= 0.4'} array.prototype.findlastindex@1.2.6: - resolution: {integrity: sha512-F/TKATkzseUExPlfvmwQKGITM3DGTK+vkAsCZoDc5daVygbJBnjEUCbgkAvVFsgfXfX4YIqZ/27G3k3tdXrTxQ==} + resolution: {integrity: sha512-F/TKATkzseUExPlfvmwQKGITM3DGTK+vkAsCZoDc5daVygbJBnjEUCbgkAvVFsgfXfX4YIqZ/27G3k3tdXrTxQ==, tarball: https://registry.npmjs.org/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.6.tgz} engines: {node: '>= 0.4'} array.prototype.flat@1.3.3: - resolution: {integrity: sha512-rwG/ja1neyLqCuGZ5YYrznA62D4mZXg0i1cIskIUKSiqF3Cje9/wXAls9B9s1Wa2fomMsIv8czB8jZcPmxCXFg==} + resolution: {integrity: sha512-rwG/ja1neyLqCuGZ5YYrznA62D4mZXg0i1cIskIUKSiqF3Cje9/wXAls9B9s1Wa2fomMsIv8czB8jZcPmxCXFg==, tarball: https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.3.tgz} engines: {node: '>= 0.4'} array.prototype.flatmap@1.3.3: - resolution: {integrity: sha512-Y7Wt51eKJSyi80hFrJCePGGNo5ktJCslFuboqJsbf57CCPcm5zztluPlc4/aD8sWsKvlwatezpV4U1efk8kpjg==} + resolution: {integrity: sha512-Y7Wt51eKJSyi80hFrJCePGGNo5ktJCslFuboqJsbf57CCPcm5zztluPlc4/aD8sWsKvlwatezpV4U1efk8kpjg==, tarball: https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.3.tgz} engines: {node: '>= 0.4'} array.prototype.tosorted@1.1.4: - resolution: {integrity: sha512-p6Fx8B7b7ZhL/gmUsAy0D15WhvDccw3mnGNbZpi3pmeJdxtWsj2jEaI4Y6oo3XiHfzuSgPwKc04MYt6KgvC/wA==} + resolution: {integrity: sha512-p6Fx8B7b7ZhL/gmUsAy0D15WhvDccw3mnGNbZpi3pmeJdxtWsj2jEaI4Y6oo3XiHfzuSgPwKc04MYt6KgvC/wA==, tarball: https://registry.npmjs.org/array.prototype.tosorted/-/array.prototype.tosorted-1.1.4.tgz} engines: {node: '>= 0.4'} arraybuffer.prototype.slice@1.0.4: - resolution: {integrity: sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==} + resolution: {integrity: sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==, tarball: https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.4.tgz} engines: {node: '>= 0.4'} asn1js@3.0.10: - resolution: {integrity: sha512-S2s3aOytiKdFRdulw2qPE51MzjzVOisppcVv7jVFR+Kw0kxwvFrDcYA0h7Ndqbmj0HkMIXYWaoj7fli8kgx1eg==} + resolution: {integrity: sha512-S2s3aOytiKdFRdulw2qPE51MzjzVOisppcVv7jVFR+Kw0kxwvFrDcYA0h7Ndqbmj0HkMIXYWaoj7fli8kgx1eg==, tarball: https://registry.npmjs.org/asn1js/-/asn1js-3.0.10.tgz} engines: {node: '>=12.0.0'} assertion-error@2.0.1: - resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==} + resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==, tarball: https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz} engines: {node: '>=12'} ast-types-flow@0.0.8: - resolution: {integrity: sha512-OH/2E5Fg20h2aPrbe+QL8JZQFko0YZaF+j4mnQ7BGhfavO7OpSLa8a0y9sBwomHdSbkhTS8TQNayBfnW5DwbvQ==} + resolution: {integrity: sha512-OH/2E5Fg20h2aPrbe+QL8JZQFko0YZaF+j4mnQ7BGhfavO7OpSLa8a0y9sBwomHdSbkhTS8TQNayBfnW5DwbvQ==, tarball: https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.8.tgz} ast-types@0.16.1: - resolution: {integrity: sha512-6t10qk83GOG8p0vKmaCr8eiilZwO171AvbROMtvvNiwrTly62t+7XkA8RdIIVbpMhCASAsxgAzdRSwh6nw/5Dg==} + resolution: {integrity: sha512-6t10qk83GOG8p0vKmaCr8eiilZwO171AvbROMtvvNiwrTly62t+7XkA8RdIIVbpMhCASAsxgAzdRSwh6nw/5Dg==, tarball: https://registry.npmjs.org/ast-types/-/ast-types-0.16.1.tgz} engines: {node: '>=4'} ast-v8-to-istanbul@1.0.5: - resolution: {integrity: sha512-UPAgKJFSEGMWSDr3LX4tqnAb4f7KGT8O40Tyx8wbYmmZ/yn58lNCm8h3svs3eXgiGd5AXxz8NDOvXWvicq+rJA==} + resolution: {integrity: sha512-UPAgKJFSEGMWSDr3LX4tqnAb4f7KGT8O40Tyx8wbYmmZ/yn58lNCm8h3svs3eXgiGd5AXxz8NDOvXWvicq+rJA==, tarball: https://registry.npmjs.org/ast-v8-to-istanbul/-/ast-v8-to-istanbul-1.0.5.tgz} async-function@1.0.0: - resolution: {integrity: sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==} + resolution: {integrity: sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==, tarball: https://registry.npmjs.org/async-function/-/async-function-1.0.0.tgz} engines: {node: '>= 0.4'} async@3.2.6: - resolution: {integrity: sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==} + resolution: {integrity: sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==, tarball: https://registry.npmjs.org/async/-/async-3.2.6.tgz} asynckit@0.4.0: - resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} + resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==, tarball: https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz} at-least-node@1.0.0: - resolution: {integrity: sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==} + resolution: {integrity: sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==, tarball: https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz} engines: {node: '>= 4.0.0'} atoa@1.0.0: - resolution: {integrity: sha512-VVE1H6cc4ai+ZXo/CRWoJiHXrA1qfA31DPnx6D20+kSI547hQN5Greh51LQ1baMRMfxO5K5M4ImMtZbZt2DODQ==} + resolution: {integrity: sha512-VVE1H6cc4ai+ZXo/CRWoJiHXrA1qfA31DPnx6D20+kSI547hQN5Greh51LQ1baMRMfxO5K5M4ImMtZbZt2DODQ==, tarball: https://registry.npmjs.org/atoa/-/atoa-1.0.0.tgz} autoprefixer@10.5.2: - resolution: {integrity: sha512-rD5t5DwOjJdmSORcTq64j8MawTC+tbQ+HHqjR4NDumamy/ambn1UJrlKL+KdwujWxMkFjPM3pPHOEA9tl4767Q==} + resolution: {integrity: sha512-rD5t5DwOjJdmSORcTq64j8MawTC+tbQ+HHqjR4NDumamy/ambn1UJrlKL+KdwujWxMkFjPM3pPHOEA9tl4767Q==, tarball: https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.5.2.tgz} engines: {node: ^10 || ^12 || >=14} hasBin: true peerDependencies: postcss: ^8.1.0 autoprefixer@10.5.4: - resolution: {integrity: sha512-MaU0U/za7N3r6brxD4YB/l4NSrFzLPlANv6wEuQVaIPlD3L4W9rFcQPbL/EilY9BHhHvhfcz3gInDLrEtWT4EA==} + resolution: {integrity: sha512-MaU0U/za7N3r6brxD4YB/l4NSrFzLPlANv6wEuQVaIPlD3L4W9rFcQPbL/EilY9BHhHvhfcz3gInDLrEtWT4EA==, tarball: https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.5.4.tgz} engines: {node: ^10 || ^12 || >=14} hasBin: true peerDependencies: postcss: ^8.1.0 available-typed-arrays@1.0.7: - resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==} + resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==, tarball: https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz} engines: {node: '>= 0.4'} axe-core@4.12.1: - resolution: {integrity: sha512-s7iGf5GaVMxEG0ENN9x+xTr7GFZCb1ZP/1uATUpCEK2X78nDB3RwbtFCo9pGAf9ru+VwoQ464DkaLEeRM08wJA==} + resolution: {integrity: sha512-s7iGf5GaVMxEG0ENN9x+xTr7GFZCb1ZP/1uATUpCEK2X78nDB3RwbtFCo9pGAf9ru+VwoQ464DkaLEeRM08wJA==, tarball: https://registry.npmjs.org/axe-core/-/axe-core-4.12.1.tgz} engines: {node: '>=4'} axios@1.15.0: - resolution: {integrity: sha512-wWyJDlAatxk30ZJer+GeCWS209sA42X+N5jU2jy6oHTp7ufw8uzUTVFBX9+wTfAlhiJXGS0Bq7X6efruWjuK9Q==} + resolution: {integrity: sha512-wWyJDlAatxk30ZJer+GeCWS209sA42X+N5jU2jy6oHTp7ufw8uzUTVFBX9+wTfAlhiJXGS0Bq7X6efruWjuK9Q==, tarball: https://registry.npmjs.org/axios/-/axios-1.15.0.tgz} axios@1.18.1: - resolution: {integrity: sha512-3nTvFlvpn9Zu/RkHUqtc7/+al4UpRW5az71ap5zccp6e8RAYEzhMTecX8Dz1wWDYrPpUoB1HAQEGEAEvUr7S9g==} + resolution: {integrity: sha512-3nTvFlvpn9Zu/RkHUqtc7/+al4UpRW5az71ap5zccp6e8RAYEzhMTecX8Dz1wWDYrPpUoB1HAQEGEAEvUr7S9g==, tarball: https://registry.npmjs.org/axios/-/axios-1.18.1.tgz} axobject-query@4.1.0: - resolution: {integrity: sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==} + resolution: {integrity: sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==, tarball: https://registry.npmjs.org/axobject-query/-/axobject-query-4.1.0.tgz} engines: {node: '>= 0.4'} babel-jest@30.2.0: - resolution: {integrity: sha512-0YiBEOxWqKkSQWL9nNGGEgndoeL0ZpWrbLMNL5u/Kaxrli3Eaxlt3ZtIDktEvXt4L/R9r3ODr2zKwGM/2BjxVw==} + resolution: {integrity: sha512-0YiBEOxWqKkSQWL9nNGGEgndoeL0ZpWrbLMNL5u/Kaxrli3Eaxlt3ZtIDktEvXt4L/R9r3ODr2zKwGM/2BjxVw==, tarball: https://registry.npmjs.org/babel-jest/-/babel-jest-30.2.0.tgz} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} peerDependencies: '@babel/core': ^7.11.0 || ^8.0.0-0 babel-jest@30.4.1: - resolution: {integrity: sha512-fATAbM8piYxkiXQp3RBXmZHxZVNJZAVXXfyeyCN2Tida3+qJ8ea9UxhiJ2y4fLO90ZImKt6k9FlcH2+rLkJGhw==} + resolution: {integrity: sha512-fATAbM8piYxkiXQp3RBXmZHxZVNJZAVXXfyeyCN2Tida3+qJ8ea9UxhiJ2y4fLO90ZImKt6k9FlcH2+rLkJGhw==, tarball: https://registry.npmjs.org/babel-jest/-/babel-jest-30.4.1.tgz} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} peerDependencies: '@babel/core': ^7.11.0 || ^8.0.0-0 babel-loader@10.0.0: - resolution: {integrity: sha512-z8jt+EdS61AMw22nSfoNJAZ0vrtmhPRVi6ghL3rCeRZI8cdNYFiV5xeV3HbE7rlZZNmGH8BVccwWt8/ED0QOHA==} + resolution: {integrity: sha512-z8jt+EdS61AMw22nSfoNJAZ0vrtmhPRVi6ghL3rCeRZI8cdNYFiV5xeV3HbE7rlZZNmGH8BVccwWt8/ED0QOHA==, tarball: https://registry.npmjs.org/babel-loader/-/babel-loader-10.0.0.tgz} engines: {node: ^18.20.0 || ^20.10.0 || >=22.0.0} peerDependencies: '@babel/core': ^7.12.0 webpack: '>=5.61.0' babel-loader@10.1.1: - resolution: {integrity: sha512-JwKSzk2kjIe7mgPK+/lyZ2QAaJcpahNAdM+hgR2HI8D0OJVkdj8Rl6J3kaLYki9pwF7P2iWnD8qVv80Lq1ABtg==} + resolution: {integrity: sha512-JwKSzk2kjIe7mgPK+/lyZ2QAaJcpahNAdM+hgR2HI8D0OJVkdj8Rl6J3kaLYki9pwF7P2iWnD8qVv80Lq1ABtg==, tarball: https://registry.npmjs.org/babel-loader/-/babel-loader-10.1.1.tgz} engines: {node: ^18.20.0 || ^20.10.0 || >=22.0.0} peerDependencies: '@babel/core': ^7.12.0 || ^8.0.0-beta.1 @@ -7872,61 +7905,61 @@ packages: optional: true babel-loader@9.2.1: - resolution: {integrity: sha512-fqe8naHt46e0yIdkjUZYqddSXfej3AHajX+CSO5X7oy0EmPc6o5Xh+RClNoHjnieWz9AW4kZxW9yyFMhVB1QLA==} + resolution: {integrity: sha512-fqe8naHt46e0yIdkjUZYqddSXfej3AHajX+CSO5X7oy0EmPc6o5Xh+RClNoHjnieWz9AW4kZxW9yyFMhVB1QLA==, tarball: https://registry.npmjs.org/babel-loader/-/babel-loader-9.2.1.tgz} engines: {node: '>= 14.15.0'} peerDependencies: '@babel/core': ^7.12.0 webpack: '>=5' babel-plugin-const-enum@1.2.0: - resolution: {integrity: sha512-o1m/6iyyFnp9MRsK1dHF3bneqyf3AlM2q3A/YbgQr2pCat6B6XJVDv2TXqzfY2RYUi4mak6WAksSBPlyYGx9dg==} + resolution: {integrity: sha512-o1m/6iyyFnp9MRsK1dHF3bneqyf3AlM2q3A/YbgQr2pCat6B6XJVDv2TXqzfY2RYUi4mak6WAksSBPlyYGx9dg==, tarball: https://registry.npmjs.org/babel-plugin-const-enum/-/babel-plugin-const-enum-1.2.0.tgz} peerDependencies: '@babel/core': ^7.0.0-0 babel-plugin-istanbul@7.0.1: - resolution: {integrity: sha512-D8Z6Qm8jCvVXtIRkBnqNHX0zJ37rQcFJ9u8WOS6tkYOsRdHBzypCstaxWiu5ZIlqQtviRYbgnRLSoCEvjqcqbA==} + resolution: {integrity: sha512-D8Z6Qm8jCvVXtIRkBnqNHX0zJ37rQcFJ9u8WOS6tkYOsRdHBzypCstaxWiu5ZIlqQtviRYbgnRLSoCEvjqcqbA==, tarball: https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-7.0.1.tgz} engines: {node: '>=12'} babel-plugin-jest-hoist@30.2.0: - resolution: {integrity: sha512-ftzhzSGMUnOzcCXd6WHdBGMyuwy15Wnn0iyyWGKgBDLxf9/s5ABuraCSpBX2uG0jUg4rqJnxsLc5+oYBqoxVaA==} + resolution: {integrity: sha512-ftzhzSGMUnOzcCXd6WHdBGMyuwy15Wnn0iyyWGKgBDLxf9/s5ABuraCSpBX2uG0jUg4rqJnxsLc5+oYBqoxVaA==, tarball: https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-30.2.0.tgz} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} babel-plugin-jest-hoist@30.4.0: - resolution: {integrity: sha512-9EdtWM/sSfXLOGLwSn+GS6pIXyBnL07/8gyJlwFXjWy4DxMOyItqyUT29d4lQiS380EZwYlX7/At4PgBS+m2aA==} + resolution: {integrity: sha512-9EdtWM/sSfXLOGLwSn+GS6pIXyBnL07/8gyJlwFXjWy4DxMOyItqyUT29d4lQiS380EZwYlX7/At4PgBS+m2aA==, tarball: https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-30.4.0.tgz} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} babel-plugin-macros@3.1.0: - resolution: {integrity: sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg==} + resolution: {integrity: sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg==, tarball: https://registry.npmjs.org/babel-plugin-macros/-/babel-plugin-macros-3.1.0.tgz} engines: {node: '>=10', npm: '>=6'} babel-plugin-polyfill-corejs2@0.4.17: - resolution: {integrity: sha512-aTyf30K/rqAsNwN76zYrdtx8obu0E4KoUME29B1xj+B3WxgvWkp943vYQ+z8Mv3lw9xHXMHpvSPOBxzAkIa94w==} + resolution: {integrity: sha512-aTyf30K/rqAsNwN76zYrdtx8obu0E4KoUME29B1xj+B3WxgvWkp943vYQ+z8Mv3lw9xHXMHpvSPOBxzAkIa94w==, tarball: https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.17.tgz} 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==} + resolution: {integrity: sha512-U+GNwMdSFgzVmfhNm8GJUX88AadB3uo9KpJqS3FaqNIPKgySuvMb+bHPsOmmuWyIcuqZj/pzt1RUIUZns4y2+A==, tarball: https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.13.0.tgz} peerDependencies: '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0 babel-plugin-polyfill-corejs3@0.14.2: - resolution: {integrity: sha512-coWpDLJ410R781Npmn/SIBZEsAetR4xVi0SxLMXPaMO4lSf1MwnkGYMtkFxew0Dn8B3/CpbpYxN0JCgg8mn67g==} + resolution: {integrity: sha512-coWpDLJ410R781Npmn/SIBZEsAetR4xVi0SxLMXPaMO4lSf1MwnkGYMtkFxew0Dn8B3/CpbpYxN0JCgg8mn67g==, tarball: https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.14.2.tgz} peerDependencies: '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0 babel-plugin-polyfill-corejs3@1.0.0: - resolution: {integrity: sha512-yIkslVjbmml2Xjb6XhFW7lISXHsqk6cesxTdDsXoMom4Lnb99DbD3OQbSOoM5Z+ASh8YXYaLAsRQrU2Jeh3Qig==} + resolution: {integrity: sha512-yIkslVjbmml2Xjb6XhFW7lISXHsqk6cesxTdDsXoMom4Lnb99DbD3OQbSOoM5Z+ASh8YXYaLAsRQrU2Jeh3Qig==, tarball: https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-1.0.0.tgz} engines: {node: ^20.19.0 || >=22.12.0} peerDependencies: '@babel/core': ^7.4.0 || ^8.0.0 babel-plugin-polyfill-regenerator@0.6.8: - resolution: {integrity: sha512-M762rNHfSF1EV3SLtnCJXFoQbbIIz0OyRwnCmV0KPC7qosSfCO0QLTSuJX3ayAebubhE6oYBAYPrBA5ljowaZg==} + resolution: {integrity: sha512-M762rNHfSF1EV3SLtnCJXFoQbbIIz0OyRwnCmV0KPC7qosSfCO0QLTSuJX3ayAebubhE6oYBAYPrBA5ljowaZg==, tarball: https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.6.8.tgz} 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==} + resolution: {integrity: sha512-mWEvCQTgXQf48yDqgN7CH50waTyYBeP2Lpqx4nNWab9sxEpdXVeKgfj1qYI2/TgUPQtNFZ85i3PemRtnXVYYJg==, tarball: https://registry.npmjs.org/babel-plugin-transform-typescript-metadata/-/babel-plugin-transform-typescript-metadata-0.3.2.tgz} peerDependencies: '@babel/core': ^7 '@babel/traverse': ^7 @@ -7935,504 +7968,505 @@ packages: optional: true babel-preset-current-node-syntax@1.2.0: - resolution: {integrity: sha512-E/VlAEzRrsLEb2+dv8yp3bo4scof3l9nR4lrld+Iy5NyVqgVYUJnDAmunkhPMisRI32Qc4iRiz425d8vM++2fg==} + resolution: {integrity: sha512-E/VlAEzRrsLEb2+dv8yp3bo4scof3l9nR4lrld+Iy5NyVqgVYUJnDAmunkhPMisRI32Qc4iRiz425d8vM++2fg==, tarball: https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.2.0.tgz} peerDependencies: '@babel/core': ^7.0.0 || ^8.0.0-0 babel-preset-jest@30.2.0: - resolution: {integrity: sha512-US4Z3NOieAQumwFnYdUWKvUKh8+YSnS/gB3t6YBiz0bskpu7Pine8pPCheNxlPEW4wnUkma2a94YuW2q3guvCQ==} + resolution: {integrity: sha512-US4Z3NOieAQumwFnYdUWKvUKh8+YSnS/gB3t6YBiz0bskpu7Pine8pPCheNxlPEW4wnUkma2a94YuW2q3guvCQ==, tarball: https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-30.2.0.tgz} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} peerDependencies: '@babel/core': ^7.11.0 || ^8.0.0-beta.1 babel-preset-jest@30.4.0: - resolution: {integrity: sha512-lBY4jxsNmCnSiu7kquw8ZC9F4+XLMOKypT3RnNHPvU2Kpd4W0xaPuLr5ZkRyOsvLYAY4yaW1ZwTW4xB7NIiZzg==} + resolution: {integrity: sha512-lBY4jxsNmCnSiu7kquw8ZC9F4+XLMOKypT3RnNHPvU2Kpd4W0xaPuLr5ZkRyOsvLYAY4yaW1ZwTW4xB7NIiZzg==, tarball: https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-30.4.0.tgz} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} peerDependencies: '@babel/core': ^7.11.0 || ^8.0.0-beta.1 balanced-match@1.0.2: - resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==, tarball: https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz} balanced-match@4.0.3: - resolution: {integrity: sha512-1pHv8LX9CpKut1Zp4EXey7Z8OfH11ONNH6Dhi2WDUt31VVZFXZzKwXcysBgqSumFCmR+0dqjMK5v5JiFHzi0+g==} + resolution: {integrity: sha512-1pHv8LX9CpKut1Zp4EXey7Z8OfH11ONNH6Dhi2WDUt31VVZFXZzKwXcysBgqSumFCmR+0dqjMK5v5JiFHzi0+g==, tarball: https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.3.tgz} engines: {node: 20 || >=22} balanced-match@4.0.4: - resolution: {integrity: sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==} + resolution: {integrity: sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==, tarball: https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz} engines: {node: 18 || 20 || >=22} base64-js@1.5.1: - resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} + resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==, tarball: https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz} baseline-browser-mapping@2.10.43: - resolution: {integrity: sha512-AjYpR78kDWAY3Efj+cDTFH9t9SCoL7OoTp1BOb0mQV7S+6CiLwnWM3FyxhJtdPufDFKzmCSFoUncKjWgJEZTCQ==} + resolution: {integrity: sha512-AjYpR78kDWAY3Efj+cDTFH9t9SCoL7OoTp1BOb0mQV7S+6CiLwnWM3FyxhJtdPufDFKzmCSFoUncKjWgJEZTCQ==, tarball: https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.43.tgz} engines: {node: '>=6.0.0'} hasBin: true basic-auth@2.0.1: - resolution: {integrity: sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg==} + resolution: {integrity: sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg==, tarball: https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.1.tgz} engines: {node: '>= 0.8'} batch@0.6.1: - resolution: {integrity: sha512-x+VAiMRL6UPkx+kudNvxTl6hB2XNNCG2r+7wixVfIYwu/2HKRXimwQyaumLjMveWvT2Hkd/cAJw+QBMfJ/EKVw==} + resolution: {integrity: sha512-x+VAiMRL6UPkx+kudNvxTl6hB2XNNCG2r+7wixVfIYwu/2HKRXimwQyaumLjMveWvT2Hkd/cAJw+QBMfJ/EKVw==, tarball: https://registry.npmjs.org/batch/-/batch-0.6.1.tgz} beasties@0.4.3: - resolution: {integrity: sha512-fIIeLOcbAB/K1kb1HBVJoiq1alHL4RCYBSo5e7HzrNkkgMggXR1Vqt/Z9JWnkfe/qdCo66Ux3QRwZioAIBdWRA==} + resolution: {integrity: sha512-fIIeLOcbAB/K1kb1HBVJoiq1alHL4RCYBSo5e7HzrNkkgMggXR1Vqt/Z9JWnkfe/qdCo66Ux3QRwZioAIBdWRA==, tarball: https://registry.npmjs.org/beasties/-/beasties-0.4.3.tgz} engines: {node: '>=18.0.0'} better-opn@3.0.2: - resolution: {integrity: sha512-aVNobHnJqLiUelTaHat9DZ1qM2w0C0Eym4LPI/3JxOnSokGVdsl1T1kN7TFvsEAD8G47A6VKQ0TVHqbBnYMJlQ==} + resolution: {integrity: sha512-aVNobHnJqLiUelTaHat9DZ1qM2w0C0Eym4LPI/3JxOnSokGVdsl1T1kN7TFvsEAD8G47A6VKQ0TVHqbBnYMJlQ==, tarball: https://registry.npmjs.org/better-opn/-/better-opn-3.0.2.tgz} engines: {node: '>=12.0.0'} bidi-js@1.0.3: - resolution: {integrity: sha512-RKshQI1R3YQ+n9YJz2QQ147P66ELpa1FQEg20Dk8oW9t2KgLbpDLLp9aGZ7y8WHSshDknG0bknqGw5/tyCs5tw==} + resolution: {integrity: sha512-RKshQI1R3YQ+n9YJz2QQ147P66ELpa1FQEg20Dk8oW9t2KgLbpDLLp9aGZ7y8WHSshDknG0bknqGw5/tyCs5tw==, tarball: https://registry.npmjs.org/bidi-js/-/bidi-js-1.0.3.tgz} big.js@5.2.2: - resolution: {integrity: sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==} + resolution: {integrity: sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==, tarball: https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz} binary-extensions@2.3.0: - resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==} + resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==, tarball: https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz} engines: {node: '>=8'} bl@4.1.0: - resolution: {integrity: sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==} + resolution: {integrity: sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==, tarball: https://registry.npmjs.org/bl/-/bl-4.1.0.tgz} blocking-elements@0.1.1: - resolution: {integrity: sha512-/SLWbEzMoVIMZACCyhD/4Ya2M1PWP1qMKuiymowPcI+PdWDARqeARBjhj73kbUBCxEmTZCUu5TAqxtwUO9C1Ig==} + resolution: {integrity: sha512-/SLWbEzMoVIMZACCyhD/4Ya2M1PWP1qMKuiymowPcI+PdWDARqeARBjhj73kbUBCxEmTZCUu5TAqxtwUO9C1Ig==, tarball: https://registry.npmjs.org/blocking-elements/-/blocking-elements-0.1.1.tgz} body-parser@1.20.6: - resolution: {integrity: sha512-p5tAzS57i5MV9fZFDj9LeIiTZEufbSe2eDozP+ElheSUq1m74CRq1jI4mYNDdVs9vQztXFLuk/Gd6BWTdwRJ5g==} + resolution: {integrity: sha512-p5tAzS57i5MV9fZFDj9LeIiTZEufbSe2eDozP+ElheSUq1m74CRq1jI4mYNDdVs9vQztXFLuk/Gd6BWTdwRJ5g==, tarball: https://registry.npmjs.org/body-parser/-/body-parser-1.20.6.tgz} engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16} body-parser@2.3.0: - resolution: {integrity: sha512-2cGmJupaNgg+QUwVLAucDuWuoMZ6EX9iHDRswZ5lsNYEmwPaRknMPCLZz07yTzVq/83p4o/wzbDZbBrTvGGTIw==} + resolution: {integrity: sha512-2cGmJupaNgg+QUwVLAucDuWuoMZ6EX9iHDRswZ5lsNYEmwPaRknMPCLZz07yTzVq/83p4o/wzbDZbBrTvGGTIw==, tarball: https://registry.npmjs.org/body-parser/-/body-parser-2.3.0.tgz} engines: {node: '>=18'} bonjour-service@1.4.3: - resolution: {integrity: sha512-2Kd5UYlFUVgAKMTyuBLl6w49wqfOnbxHqmuH0oCl/n7TfAikR0zoowNOP5BU4dfXmm+Vr9JyEN370auSMx+CNg==} + resolution: {integrity: sha512-2Kd5UYlFUVgAKMTyuBLl6w49wqfOnbxHqmuH0oCl/n7TfAikR0zoowNOP5BU4dfXmm+Vr9JyEN370auSMx+CNg==, tarball: https://registry.npmjs.org/bonjour-service/-/bonjour-service-1.4.3.tgz} boolbase@1.0.0: - resolution: {integrity: sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==} + resolution: {integrity: sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==, tarball: https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz} + + boolbase@2.0.0: + resolution: {integrity: sha512-DkVaaQHymRhpYEYo9x1oo7Q7B0Y6KJUsjm3c9eTyFDby4MHLBTwZ6ZDWBel5zrYxj1WsZgC5oLpiz+93MluXeA==, tarball: https://registry.npmjs.org/boolbase/-/boolbase-2.0.0.tgz} + engines: {node: '>=20.19.0'} brace-expansion@1.1.16: - resolution: {integrity: sha512-IDw48K2/2kRkg9LdJxurvq3lV3aBgq0REY89duEqFRthjlPdXHKMj7EnQOXVckxzgisinf3nHfrcE2FufFLXMw==} + resolution: {integrity: sha512-IDw48K2/2kRkg9LdJxurvq3lV3aBgq0REY89duEqFRthjlPdXHKMj7EnQOXVckxzgisinf3nHfrcE2FufFLXMw==, tarball: https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.16.tgz} brace-expansion@2.1.2: - resolution: {integrity: sha512-w5JZcKgdhDOgOwm8H+KgbosopHMuGcl6qbulwjtz3SM7I7P3yW1eAjzMPLrIE+NQ9vjgANKHWeMHnrT0OXW1oA==} + resolution: {integrity: sha512-w5JZcKgdhDOgOwm8H+KgbosopHMuGcl6qbulwjtz3SM7I7P3yW1eAjzMPLrIE+NQ9vjgANKHWeMHnrT0OXW1oA==, tarball: https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.2.tgz} brace-expansion@5.0.7: - resolution: {integrity: sha512-7oFy703dxfY3/NLxC1fh2SUCQ0H9rmAY+5EpDVfXjUTTs+HEwR2nYaqLv+GWcTsumwxPfiz6CzCNkwXwBUwqCA==} + resolution: {integrity: sha512-7oFy703dxfY3/NLxC1fh2SUCQ0H9rmAY+5EpDVfXjUTTs+HEwR2nYaqLv+GWcTsumwxPfiz6CzCNkwXwBUwqCA==, tarball: https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.7.tgz} engines: {node: 18 || 20 || >=22} brace-expansion@5.0.8: - resolution: {integrity: sha512-JZyDyq3D4AUifKTPOB7DELf6XsB3WdPuNxCtob1vFXPsSXhdAiHBWJ/tJ8HAc9aH84BK+5JFZLNkJKx3G9kzQg==} + resolution: {integrity: sha512-JZyDyq3D4AUifKTPOB7DELf6XsB3WdPuNxCtob1vFXPsSXhdAiHBWJ/tJ8HAc9aH84BK+5JFZLNkJKx3G9kzQg==, tarball: https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.8.tgz} engines: {node: 20 || >=22} braces@3.0.3: - resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} + resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==, tarball: https://registry.npmjs.org/braces/-/braces-3.0.3.tgz} engines: {node: '>=8'} browserslist@4.28.6: - resolution: {integrity: sha512-FQBYNK15VMslhLHpA7+n+n1GOlF1kId2xcCg7/j95f24AOF6VDYMNH4mFxF7KuaTdv627faazpOAjFzMrfJOUw==} + resolution: {integrity: sha512-FQBYNK15VMslhLHpA7+n+n1GOlF1kId2xcCg7/j95f24AOF6VDYMNH4mFxF7KuaTdv627faazpOAjFzMrfJOUw==, tarball: https://registry.npmjs.org/browserslist/-/browserslist-4.28.6.tgz} engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} hasBin: true bs-logger@0.2.6: - resolution: {integrity: sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog==} + resolution: {integrity: sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog==, tarball: https://registry.npmjs.org/bs-logger/-/bs-logger-0.2.6.tgz} engines: {node: '>= 6'} bser@2.1.1: - resolution: {integrity: sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==} + resolution: {integrity: sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==, tarball: https://registry.npmjs.org/bser/-/bser-2.1.1.tgz} btoa@1.2.1: - resolution: {integrity: sha512-SB4/MIGlsiVkMcHmT+pSmIPoNDoHg+7cMzmt3Uxt628MTz2487DKSqK/fuhFBrkuqrYv5UCEnACpF4dTFNKc/g==} + resolution: {integrity: sha512-SB4/MIGlsiVkMcHmT+pSmIPoNDoHg+7cMzmt3Uxt628MTz2487DKSqK/fuhFBrkuqrYv5UCEnACpF4dTFNKc/g==, tarball: https://registry.npmjs.org/btoa/-/btoa-1.2.1.tgz} engines: {node: '>= 0.4.0'} hasBin: true buffer-from@1.1.2: - resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} + resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==, tarball: https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz} buffer-image-size@0.6.4: - resolution: {integrity: sha512-nEh+kZOPY1w+gcCMobZ6ETUp9WfibndnosbpwB1iJk/8Gt5ZF2bhS6+B6bPYz424KtwsR6Rflc3tCz1/ghX2dQ==} + resolution: {integrity: sha512-nEh+kZOPY1w+gcCMobZ6ETUp9WfibndnosbpwB1iJk/8Gt5ZF2bhS6+B6bPYz424KtwsR6Rflc3tCz1/ghX2dQ==, tarball: https://registry.npmjs.org/buffer-image-size/-/buffer-image-size-0.6.4.tgz} engines: {node: '>=4.0'} buffer@5.7.1: - resolution: {integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==} + resolution: {integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==, tarball: https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz} bundle-name@4.1.0: - resolution: {integrity: sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q==} + resolution: {integrity: sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q==, tarball: https://registry.npmjs.org/bundle-name/-/bundle-name-4.1.0.tgz} engines: {node: '>=18'} busboy@1.6.0: - resolution: {integrity: sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==} + resolution: {integrity: sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==, tarball: https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz} engines: {node: '>=10.16.0'} bytes@3.1.2: - resolution: {integrity: sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==} + resolution: {integrity: sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==, tarball: https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz} engines: {node: '>= 0.8'} bytestreamjs@2.0.1: - resolution: {integrity: sha512-U1Z/ob71V/bXfVABvNr/Kumf5VyeQRBEm6Txb0PQ6S7V5GpBM3w4Cbqz/xPDicR5tN0uvDifng8C+5qECeGwyQ==} + resolution: {integrity: sha512-U1Z/ob71V/bXfVABvNr/Kumf5VyeQRBEm6Txb0PQ6S7V5GpBM3w4Cbqz/xPDicR5tN0uvDifng8C+5qECeGwyQ==, tarball: https://registry.npmjs.org/bytestreamjs/-/bytestreamjs-2.0.1.tgz} engines: {node: '>=6.0.0'} call-bind-apply-helpers@1.0.2: - resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==} + resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==, tarball: https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz} engines: {node: '>= 0.4'} call-bind@1.0.9: - resolution: {integrity: sha512-a/hy+pNsFUTR+Iz8TCJvXudKVLAnz/DyeSUo10I5yvFDQJBFU2s9uqQpoSrJlroHUKoKqzg+epxyP9lqFdzfBQ==} + resolution: {integrity: sha512-a/hy+pNsFUTR+Iz8TCJvXudKVLAnz/DyeSUo10I5yvFDQJBFU2s9uqQpoSrJlroHUKoKqzg+epxyP9lqFdzfBQ==, tarball: https://registry.npmjs.org/call-bind/-/call-bind-1.0.9.tgz} engines: {node: '>= 0.4'} call-bound@1.0.4: - resolution: {integrity: sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==} + resolution: {integrity: sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==, tarball: https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz} engines: {node: '>= 0.4'} - call-me-maybe@1.0.2: - resolution: {integrity: sha512-HpX65o1Hnr9HH25ojC1YGs7HCQLq0GCOibSaWER0eNpgJ/Z1MZv2mTc7+xh6WOPxbRVcmgbv4hGU+uSQ/2xFZQ==} - callsites@3.1.0: - resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} + resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==, tarball: https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz} engines: {node: '>=6'} camelcase@5.3.1: - resolution: {integrity: sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==} + resolution: {integrity: sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==, tarball: https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz} engines: {node: '>=6'} camelcase@6.3.0: - resolution: {integrity: sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==} + resolution: {integrity: sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==, tarball: https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz} engines: {node: '>=10'} caniuse-api@3.0.0: - resolution: {integrity: sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw==} + resolution: {integrity: sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw==, tarball: https://registry.npmjs.org/caniuse-api/-/caniuse-api-3.0.0.tgz} caniuse-lite@1.0.30001805: - resolution: {integrity: sha512-52noaS3DubycKSXaU30TwPGIp+POyQSUVa5jBEq3vkRkY0kjyb3LQgvhU6WGyCcyXqVLWO0Cw0Q6BSdD0kUfVA==} + resolution: {integrity: sha512-52noaS3DubycKSXaU30TwPGIp+POyQSUVa5jBEq3vkRkY0kjyb3LQgvhU6WGyCcyXqVLWO0Cw0Q6BSdD0kUfVA==, tarball: https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001805.tgz} caniuse-lite@1.0.30001806: - resolution: {integrity: sha512-72Cuvd95zbSYPKq6Fhg8eDJRlzgWDf7/mtoZv6Qe/DYNCEBdNxoA3+rZAU2ZhGCpZlns3EssFavaZomckT5Uuw==} + resolution: {integrity: sha512-72Cuvd95zbSYPKq6Fhg8eDJRlzgWDf7/mtoZv6Qe/DYNCEBdNxoA3+rZAU2ZhGCpZlns3EssFavaZomckT5Uuw==, tarball: https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001806.tgz} cfonts@3.3.1: - resolution: {integrity: sha512-ZGEmN3W9mViWEDjsuPo4nK4h39sfh6YtoneFYp9WLPI/rw8BaSSrfQC6jkrGW3JMvV3ZnExJB/AEqXc/nHYxkw==} + resolution: {integrity: sha512-ZGEmN3W9mViWEDjsuPo4nK4h39sfh6YtoneFYp9WLPI/rw8BaSSrfQC6jkrGW3JMvV3ZnExJB/AEqXc/nHYxkw==, tarball: https://registry.npmjs.org/cfonts/-/cfonts-3.3.1.tgz} engines: {node: '>=10'} hasBin: true chai@5.3.3: - resolution: {integrity: sha512-4zNhdJD/iOjSH0A05ea+Ke6MU5mmpQcbQsSOkgdaUMJ9zTlDTD/GYlwohmIE2u0gaxHYiVHEn1Fw9mZ/ktJWgw==} + resolution: {integrity: sha512-4zNhdJD/iOjSH0A05ea+Ke6MU5mmpQcbQsSOkgdaUMJ9zTlDTD/GYlwohmIE2u0gaxHYiVHEn1Fw9mZ/ktJWgw==, tarball: https://registry.npmjs.org/chai/-/chai-5.3.3.tgz} engines: {node: '>=18'} chai@6.2.2: - resolution: {integrity: sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==} + resolution: {integrity: sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==, tarball: https://registry.npmjs.org/chai/-/chai-6.2.2.tgz} engines: {node: '>=18'} chalk@3.0.0: - resolution: {integrity: sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==} + resolution: {integrity: sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==, tarball: https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz} engines: {node: '>=8'} chalk@4.1.2: - resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} + resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==, tarball: https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz} engines: {node: '>=10'} chalk@5.3.0: - resolution: {integrity: sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==} + resolution: {integrity: sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==, tarball: https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz} engines: {node: ^12.17.0 || ^14.13 || >=16.0.0} chalk@5.6.2: - resolution: {integrity: sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==} + resolution: {integrity: sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==, tarball: https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz} engines: {node: ^12.17.0 || ^14.13 || >=16.0.0} char-regex@1.0.2: - resolution: {integrity: sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==} + resolution: {integrity: sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==, tarball: https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz} engines: {node: '>=10'} chardet@2.2.0: - resolution: {integrity: sha512-rddelWYNPRrXq6PtNEN2S3f6t9ILzvqaN5pVgi4kqt9jHQaXIial9PznB5iSPVlQSLNaaH22ItWz3EJtQ10+OA==} + resolution: {integrity: sha512-rddelWYNPRrXq6PtNEN2S3f6t9ILzvqaN5pVgi4kqt9jHQaXIial9PznB5iSPVlQSLNaaH22ItWz3EJtQ10+OA==, tarball: https://registry.npmjs.org/chardet/-/chardet-2.2.0.tgz} charenc@0.0.2: - resolution: {integrity: sha512-yrLQ/yVUFXkzg7EDQsPieE/53+0RlaWTs+wBrvW36cyilJ2SaDWfl4Yj7MtLTXleV9uEKefbAGUPv2/iWSooRA==} + resolution: {integrity: sha512-yrLQ/yVUFXkzg7EDQsPieE/53+0RlaWTs+wBrvW36cyilJ2SaDWfl4Yj7MtLTXleV9uEKefbAGUPv2/iWSooRA==, tarball: https://registry.npmjs.org/charenc/-/charenc-0.0.2.tgz} chart.js@4.5.1: - resolution: {integrity: sha512-GIjfiT9dbmHRiYi6Nl2yFCq7kkwdkp1W/lp2J99rX0yo9tgJGn3lKQATztIjb5tVtevcBtIdICNWqlq5+E8/Pw==} + resolution: {integrity: sha512-GIjfiT9dbmHRiYi6Nl2yFCq7kkwdkp1W/lp2J99rX0yo9tgJGn3lKQATztIjb5tVtevcBtIdICNWqlq5+E8/Pw==, tarball: https://registry.npmjs.org/chart.js/-/chart.js-4.5.1.tgz} engines: {pnpm: '>=8'} check-error@2.1.3: - resolution: {integrity: sha512-PAJdDJusoxnwm1VwW07VWwUN1sl7smmC3OKggvndJFadxxDRyFJBX/ggnu/KE4kQAB7a3Dp8f/YXC1FlUprWmA==} + resolution: {integrity: sha512-PAJdDJusoxnwm1VwW07VWwUN1sl7smmC3OKggvndJFadxxDRyFJBX/ggnu/KE4kQAB7a3Dp8f/YXC1FlUprWmA==, tarball: https://registry.npmjs.org/check-error/-/check-error-2.1.3.tgz} engines: {node: '>= 16'} chokidar@3.6.0: - resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==} + resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==, tarball: https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz} engines: {node: '>= 8.10.0'} chokidar@4.0.3: - resolution: {integrity: sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==} + resolution: {integrity: sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==, tarball: https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz} engines: {node: '>= 14.16.0'} chokidar@5.0.0: - resolution: {integrity: sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw==} + resolution: {integrity: sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw==, tarball: https://registry.npmjs.org/chokidar/-/chokidar-5.0.0.tgz} engines: {node: '>= 20.19.0'} chrome-trace-event@1.0.4: - resolution: {integrity: sha512-rNjApaLzuwaOTjCiT8lSDdGN1APCiqkChLMJxJPWLunPAt5fy8xgU9/jNOchV84wfIxrA0lRQB7oCT8jrn/wrQ==} + resolution: {integrity: sha512-rNjApaLzuwaOTjCiT8lSDdGN1APCiqkChLMJxJPWLunPAt5fy8xgU9/jNOchV84wfIxrA0lRQB7oCT8jrn/wrQ==, tarball: https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.4.tgz} engines: {node: '>=6.0'} ci-info@3.9.0: - resolution: {integrity: sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==} + resolution: {integrity: sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==, tarball: https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz} engines: {node: '>=8'} ci-info@4.4.0: - resolution: {integrity: sha512-77PSwercCZU2Fc4sX94eF8k8Pxte6JAwL4/ICZLFjJLqegs7kCuAsqqj/70NQF6TvDpgFjkubQB2FW2ZZddvQg==} + resolution: {integrity: sha512-77PSwercCZU2Fc4sX94eF8k8Pxte6JAwL4/ICZLFjJLqegs7kCuAsqqj/70NQF6TvDpgFjkubQB2FW2ZZddvQg==, tarball: https://registry.npmjs.org/ci-info/-/ci-info-4.4.0.tgz} engines: {node: '>=8'} cjs-module-lexer@2.2.0: - resolution: {integrity: sha512-4bHTS2YuzUvtoLjdy+98ykbNB5jS0+07EvFNXerqZQJ89F7DI6ET7OQo/HJuW6K0aVsKA9hj9/RVb2kQVOrPDQ==} + resolution: {integrity: sha512-4bHTS2YuzUvtoLjdy+98ykbNB5jS0+07EvFNXerqZQJ89F7DI6ET7OQo/HJuW6K0aVsKA9hj9/RVb2kQVOrPDQ==, tarball: https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-2.2.0.tgz} cli-cursor@3.1.0: - resolution: {integrity: sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==} + resolution: {integrity: sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==, tarball: https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz} engines: {node: '>=8'} cli-cursor@5.0.0: - resolution: {integrity: sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==} + resolution: {integrity: sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==, tarball: https://registry.npmjs.org/cli-cursor/-/cli-cursor-5.0.0.tgz} engines: {node: '>=18'} cli-spinners@2.6.1: - resolution: {integrity: sha512-x/5fWmGMnbKQAaNwN+UZlV79qBLM9JFnJuJ03gIi5whrob0xV0ofNVHy9DhwGdsMJQc2OKv0oGmLzvaqvAVv+g==} + resolution: {integrity: sha512-x/5fWmGMnbKQAaNwN+UZlV79qBLM9JFnJuJ03gIi5whrob0xV0ofNVHy9DhwGdsMJQc2OKv0oGmLzvaqvAVv+g==, tarball: https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.6.1.tgz} engines: {node: '>=6'} cli-spinners@2.9.2: - resolution: {integrity: sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==} + resolution: {integrity: sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==, tarball: https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.2.tgz} engines: {node: '>=6'} cli-spinners@3.4.0: - resolution: {integrity: sha512-bXfOC4QcT1tKXGorxL3wbJm6XJPDqEnij2gQ2m7ESQuE+/z9YFIWnl/5RpTiKWbMq3EVKR4fRLJGn6DVfu0mpw==} + resolution: {integrity: sha512-bXfOC4QcT1tKXGorxL3wbJm6XJPDqEnij2gQ2m7ESQuE+/z9YFIWnl/5RpTiKWbMq3EVKR4fRLJGn6DVfu0mpw==, tarball: https://registry.npmjs.org/cli-spinners/-/cli-spinners-3.4.0.tgz} engines: {node: '>=18.20'} cli-truncate@4.0.0: - resolution: {integrity: sha512-nPdaFdQ0h/GEigbPClz11D0v/ZJEwxmeVZGeMo3Z5StPtUTkA9o1lD6QwoirYiSDzbcwn2XcjwmCp68W1IS4TA==} + resolution: {integrity: sha512-nPdaFdQ0h/GEigbPClz11D0v/ZJEwxmeVZGeMo3Z5StPtUTkA9o1lD6QwoirYiSDzbcwn2XcjwmCp68W1IS4TA==, tarball: https://registry.npmjs.org/cli-truncate/-/cli-truncate-4.0.0.tgz} engines: {node: '>=18'} cli-truncate@5.2.0: - resolution: {integrity: sha512-xRwvIOMGrfOAnM1JYtqQImuaNtDEv9v6oIYAs4LIHwTiKee8uwvIi363igssOC0O5U04i4AlENs79LQLu9tEMw==} + resolution: {integrity: sha512-xRwvIOMGrfOAnM1JYtqQImuaNtDEv9v6oIYAs4LIHwTiKee8uwvIi363igssOC0O5U04i4AlENs79LQLu9tEMw==, tarball: https://registry.npmjs.org/cli-truncate/-/cli-truncate-5.2.0.tgz} engines: {node: '>=20'} cli-width@4.1.0: - resolution: {integrity: sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ==} + resolution: {integrity: sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ==, tarball: https://registry.npmjs.org/cli-width/-/cli-width-4.1.0.tgz} engines: {node: '>= 12'} client-only@0.0.1: - resolution: {integrity: sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==} + resolution: {integrity: sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==, tarball: https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz} clipboard@2.0.11: - resolution: {integrity: sha512-C+0bbOqkezLIsmWSvlsXS0Q0bmkugu7jcfMIACB+RDEntIzQIkdr148we28AfSloQLRdZlYL/QYyrq05j/3Faw==} + resolution: {integrity: sha512-C+0bbOqkezLIsmWSvlsXS0Q0bmkugu7jcfMIACB+RDEntIzQIkdr148we28AfSloQLRdZlYL/QYyrq05j/3Faw==, tarball: https://registry.npmjs.org/clipboard/-/clipboard-2.0.11.tgz} cliui@8.0.1: - resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==} + resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==, tarball: https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz} engines: {node: '>=12'} cliui@9.0.1: - resolution: {integrity: sha512-k7ndgKhwoQveBL+/1tqGJYNz097I7WOvwbmmU2AR5+magtbjPWQTS1C5vzGkBC8Ym8UWRzfKUzUUqFLypY4Q+w==} + resolution: {integrity: sha512-k7ndgKhwoQveBL+/1tqGJYNz097I7WOvwbmmU2AR5+magtbjPWQTS1C5vzGkBC8Ym8UWRzfKUzUUqFLypY4Q+w==, tarball: https://registry.npmjs.org/cliui/-/cliui-9.0.1.tgz} engines: {node: '>=20'} clone-deep@4.0.1: - resolution: {integrity: sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==} + resolution: {integrity: sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==, tarball: https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz} engines: {node: '>=6'} clone@1.0.4: - resolution: {integrity: sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==} + resolution: {integrity: sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==, tarball: https://registry.npmjs.org/clone/-/clone-1.0.4.tgz} engines: {node: '>=0.8'} co@4.6.0: - resolution: {integrity: sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==} + resolution: {integrity: sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==, tarball: https://registry.npmjs.org/co/-/co-4.6.0.tgz} engines: {iojs: '>= 1.0.0', node: '>= 0.12.0'} collect-v8-coverage@1.0.3: - resolution: {integrity: sha512-1L5aqIkwPfiodaMgQunkF1zRhNqifHBmtbbbxcr6yVxxBnliw4TDOW6NxpO8DJLgJ16OT+Y4ztZqP6p/FtXnAw==} + resolution: {integrity: sha512-1L5aqIkwPfiodaMgQunkF1zRhNqifHBmtbbbxcr6yVxxBnliw4TDOW6NxpO8DJLgJ16OT+Y4ztZqP6p/FtXnAw==, tarball: https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.3.tgz} color-convert@2.0.1: - resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} + resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==, tarball: https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz} engines: {node: '>=7.0.0'} color-name@1.1.4: - resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==, tarball: https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz} colord@2.9.3: - resolution: {integrity: sha512-jeC1axXpnb0/2nn/Y1LPuLdgXBLH7aDcHu4KEKfqw3CUhX7ZpfBSlPKyqXE6btIgEzfWtrX3/tyBCaCvXvMkOw==} + resolution: {integrity: sha512-jeC1axXpnb0/2nn/Y1LPuLdgXBLH7aDcHu4KEKfqw3CUhX7ZpfBSlPKyqXE6btIgEzfWtrX3/tyBCaCvXvMkOw==, tarball: https://registry.npmjs.org/colord/-/colord-2.9.3.tgz} colorette@2.0.20: - resolution: {integrity: sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==} + resolution: {integrity: sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==, tarball: https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz} colorjs.io@0.5.2: - resolution: {integrity: sha512-twmVoizEW7ylZSN32OgKdXRmo1qg+wT5/6C3xu5b9QsWzSFAhHLn2xd8ro0diCsKfCj1RdaTP/nrcW+vAoQPIw==} + resolution: {integrity: sha512-twmVoizEW7ylZSN32OgKdXRmo1qg+wT5/6C3xu5b9QsWzSFAhHLn2xd8ro0diCsKfCj1RdaTP/nrcW+vAoQPIw==, tarball: https://registry.npmjs.org/colorjs.io/-/colorjs.io-0.5.2.tgz} columnify@1.6.0: - resolution: {integrity: sha512-lomjuFZKfM6MSAnV9aCZC9sc0qGbmZdfygNv+nCpqVkSKdCxCklLtd16O0EILGkImHw9ZpHkAnHaB+8Zxq5W6Q==} + resolution: {integrity: sha512-lomjuFZKfM6MSAnV9aCZC9sc0qGbmZdfygNv+nCpqVkSKdCxCklLtd16O0EILGkImHw9ZpHkAnHaB+8Zxq5W6Q==, tarball: https://registry.npmjs.org/columnify/-/columnify-1.6.0.tgz} engines: {node: '>=8.0.0'} combined-stream@1.0.8: - resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} + resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==, tarball: https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz} engines: {node: '>= 0.8'} commander@10.0.1: - resolution: {integrity: sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==} + resolution: {integrity: sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==, tarball: https://registry.npmjs.org/commander/-/commander-10.0.1.tgz} engines: {node: '>=14'} commander@11.1.0: - resolution: {integrity: sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ==} + resolution: {integrity: sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ==, tarball: https://registry.npmjs.org/commander/-/commander-11.1.0.tgz} engines: {node: '>=16'} commander@12.1.0: - resolution: {integrity: sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==} + resolution: {integrity: sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==, tarball: https://registry.npmjs.org/commander/-/commander-12.1.0.tgz} engines: {node: '>=18'} commander@14.0.3: - resolution: {integrity: sha512-H+y0Jo/T1RZ9qPP4Eh1pkcQcLRglraJaSLoyOtHxu6AapkjWVCy2Sit1QQ4x3Dng8qDlSsZEet7g5Pq06MvTgw==} + resolution: {integrity: sha512-H+y0Jo/T1RZ9qPP4Eh1pkcQcLRglraJaSLoyOtHxu6AapkjWVCy2Sit1QQ4x3Dng8qDlSsZEet7g5Pq06MvTgw==, tarball: https://registry.npmjs.org/commander/-/commander-14.0.3.tgz} engines: {node: '>=20'} commander@15.0.0: - resolution: {integrity: sha512-z67u4ZhzCL/Tydu1lJARtEZYWbWaN7oYLHbsuzocr6y4N6WZAagG3RQ4FW61V1/0+jImpj293XfrcYnd1qxtPg==} + resolution: {integrity: sha512-z67u4ZhzCL/Tydu1lJARtEZYWbWaN7oYLHbsuzocr6y4N6WZAagG3RQ4FW61V1/0+jImpj293XfrcYnd1qxtPg==, tarball: https://registry.npmjs.org/commander/-/commander-15.0.0.tgz} engines: {node: '>=22.12.0'} commander@2.20.3: - resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==} + resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==, tarball: https://registry.npmjs.org/commander/-/commander-2.20.3.tgz} commander@7.2.0: - resolution: {integrity: sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==} + resolution: {integrity: sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==, tarball: https://registry.npmjs.org/commander/-/commander-7.2.0.tgz} engines: {node: '>= 10'} commander@8.3.0: - resolution: {integrity: sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==} + resolution: {integrity: sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==, tarball: https://registry.npmjs.org/commander/-/commander-8.3.0.tgz} engines: {node: '>= 12'} common-path-prefix@3.0.0: - resolution: {integrity: sha512-QE33hToZseCH3jS0qN96O/bSh3kaw/h+Tq7ngyY9eWDUnTlTNUyqfqvCXioLe5Na5jFsL78ra/wuBU4iuEgd4w==} + resolution: {integrity: sha512-QE33hToZseCH3jS0qN96O/bSh3kaw/h+Tq7ngyY9eWDUnTlTNUyqfqvCXioLe5Na5jFsL78ra/wuBU4iuEgd4w==, tarball: https://registry.npmjs.org/common-path-prefix/-/common-path-prefix-3.0.0.tgz} commondir@1.0.1: - resolution: {integrity: sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==} + resolution: {integrity: sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==, tarball: https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz} compare-versions@6.1.1: - resolution: {integrity: sha512-4hm4VPpIecmlg59CHXnRDnqGplJFrbLG4aFEl5vl6cK1u76ws3LLvX7ikFnTDl5vo39sjWD6AaDPYodJp/NNHg==} + resolution: {integrity: sha512-4hm4VPpIecmlg59CHXnRDnqGplJFrbLG4aFEl5vl6cK1u76ws3LLvX7ikFnTDl5vo39sjWD6AaDPYodJp/NNHg==, tarball: https://registry.npmjs.org/compare-versions/-/compare-versions-6.1.1.tgz} compressible@2.0.18: - resolution: {integrity: sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==} + resolution: {integrity: sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==, tarball: https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz} engines: {node: '>= 0.6'} compression@1.8.1: - resolution: {integrity: sha512-9mAqGPHLakhCLeNyxPkK4xVo746zQ/czLH1Ky+vkitMnWfWZps8r0qXuwhwizagCRttsL4lfG4pIOvaWLpAP0w==} + resolution: {integrity: sha512-9mAqGPHLakhCLeNyxPkK4xVo746zQ/czLH1Ky+vkitMnWfWZps8r0qXuwhwizagCRttsL4lfG4pIOvaWLpAP0w==, tarball: https://registry.npmjs.org/compression/-/compression-1.8.1.tgz} engines: {node: '>= 0.8.0'} concat-map@0.0.1: - resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} + resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==, tarball: https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz} concat-with-sourcemaps@1.1.0: - resolution: {integrity: sha512-4gEjHJFT9e+2W/77h/DS5SGUgwDaOwprX8L/gl5+3ixnzkVJJsZWDSelmN3Oilw3LNDZjZV0yqH1hLG3k6nghg==} + resolution: {integrity: sha512-4gEjHJFT9e+2W/77h/DS5SGUgwDaOwprX8L/gl5+3ixnzkVJJsZWDSelmN3Oilw3LNDZjZV0yqH1hLG3k6nghg==, tarball: https://registry.npmjs.org/concat-with-sourcemaps/-/concat-with-sourcemaps-1.1.0.tgz} confbox@0.1.8: - resolution: {integrity: sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==} + resolution: {integrity: sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==, tarball: https://registry.npmjs.org/confbox/-/confbox-0.1.8.tgz} confbox@0.2.4: - resolution: {integrity: sha512-ysOGlgTFbN2/Y6Cg3Iye8YKulHw+R2fNXHrgSmXISQdMnomY6eNDprVdW9R5xBguEqI954+S6709UyiO7B+6OQ==} + resolution: {integrity: sha512-ysOGlgTFbN2/Y6Cg3Iye8YKulHw+R2fNXHrgSmXISQdMnomY6eNDprVdW9R5xBguEqI954+S6709UyiO7B+6OQ==, tarball: https://registry.npmjs.org/confbox/-/confbox-0.2.4.tgz} config-chain@1.1.13: - resolution: {integrity: sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ==} + resolution: {integrity: sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ==, tarball: https://registry.npmjs.org/config-chain/-/config-chain-1.1.13.tgz} confusing-browser-globals@1.0.11: - resolution: {integrity: sha512-JsPKdmh8ZkmnHxDk55FZ1TqVLvEQTvoByJZRN9jzI0UjxK/QgAmsphz7PGtqgPieQZ/CQcHWXCR7ATDNhGe+YA==} + resolution: {integrity: sha512-JsPKdmh8ZkmnHxDk55FZ1TqVLvEQTvoByJZRN9jzI0UjxK/QgAmsphz7PGtqgPieQZ/CQcHWXCR7ATDNhGe+YA==, tarball: https://registry.npmjs.org/confusing-browser-globals/-/confusing-browser-globals-1.0.11.tgz} connect-history-api-fallback@2.0.0: - resolution: {integrity: sha512-U73+6lQFmfiNPrYbXqr6kZ1i1wiRqXnp2nhMsINseWXO8lDau0LGEffJ8kQi4EjLZympVgRdvqjAgiZ1tgzDDA==} + resolution: {integrity: sha512-U73+6lQFmfiNPrYbXqr6kZ1i1wiRqXnp2nhMsINseWXO8lDau0LGEffJ8kQi4EjLZympVgRdvqjAgiZ1tgzDDA==, tarball: https://registry.npmjs.org/connect-history-api-fallback/-/connect-history-api-fallback-2.0.0.tgz} engines: {node: '>=0.8'} consola@3.4.2: - resolution: {integrity: sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA==} + resolution: {integrity: sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA==, tarball: https://registry.npmjs.org/consola/-/consola-3.4.2.tgz} engines: {node: ^14.18.0 || >=16.10.0} content-disposition@0.5.4: - resolution: {integrity: sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==} + resolution: {integrity: sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==, tarball: https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz} engines: {node: '>= 0.6'} content-disposition@1.1.0: - resolution: {integrity: sha512-5jRCH9Z/+DRP7rkvY83B+yGIGX96OYdJmzngqnw2SBSxqCFPd0w2km3s5iawpGX8krnwSGmF0FW5Nhr0Hfai3g==} + resolution: {integrity: sha512-5jRCH9Z/+DRP7rkvY83B+yGIGX96OYdJmzngqnw2SBSxqCFPd0w2km3s5iawpGX8krnwSGmF0FW5Nhr0Hfai3g==, tarball: https://registry.npmjs.org/content-disposition/-/content-disposition-1.1.0.tgz} engines: {node: '>=18'} content-type@1.0.5: - resolution: {integrity: sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==} + resolution: {integrity: sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==, tarball: https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz} engines: {node: '>= 0.6'} content-type@2.0.0: - resolution: {integrity: sha512-j/O/d7GcZCyNl7/hwZAb606rzqkyvaDctLmckbxLzHvFBzTJHuGEdodATcP3yIRoDrLHkIATJuvzbFlp/ki2cQ==} + resolution: {integrity: sha512-j/O/d7GcZCyNl7/hwZAb606rzqkyvaDctLmckbxLzHvFBzTJHuGEdodATcP3yIRoDrLHkIATJuvzbFlp/ki2cQ==, tarball: https://registry.npmjs.org/content-type/-/content-type-2.0.0.tgz} engines: {node: '>=18'} contra@1.9.4: - resolution: {integrity: sha512-N9ArHAqwR/lhPq4OdIAwH4e1btn6EIZMAz4TazjnzCiVECcWUPTma+dRAM38ERImEJBh8NiCCpjoQruSZ+agYg==} + resolution: {integrity: sha512-N9ArHAqwR/lhPq4OdIAwH4e1btn6EIZMAz4TazjnzCiVECcWUPTma+dRAM38ERImEJBh8NiCCpjoQruSZ+agYg==, tarball: https://registry.npmjs.org/contra/-/contra-1.9.4.tgz} convert-source-map@1.9.0: - resolution: {integrity: sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==} + resolution: {integrity: sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==, tarball: https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz} convert-source-map@2.0.0: - resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} + resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==, tarball: https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz} cookie-signature@1.0.7: - resolution: {integrity: sha512-NXdYc3dLr47pBkpUCHtKSwIOQXLVn8dZEuywboCOJY/osA0wFSLlSawr3KN8qXJEyX66FcONTH8EIlVuK0yyFA==} + resolution: {integrity: sha512-NXdYc3dLr47pBkpUCHtKSwIOQXLVn8dZEuywboCOJY/osA0wFSLlSawr3KN8qXJEyX66FcONTH8EIlVuK0yyFA==, tarball: https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.7.tgz} cookie-signature@1.2.2: - resolution: {integrity: sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==} + resolution: {integrity: sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==, tarball: https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz} engines: {node: '>=6.6.0'} cookie@0.7.2: - resolution: {integrity: sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==} + resolution: {integrity: sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==, tarball: https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz} engines: {node: '>= 0.6'} cookies@0.9.1: - resolution: {integrity: sha512-TG2hpqe4ELx54QER/S3HQ9SRVnQnGBtKUz5bLQWtYAQ+o6GpgMs6sYUvaiJjVxb+UXwhRhAEP3m7LbsIZ77Hmw==} + resolution: {integrity: sha512-TG2hpqe4ELx54QER/S3HQ9SRVnQnGBtKUz5bLQWtYAQ+o6GpgMs6sYUvaiJjVxb+UXwhRhAEP3m7LbsIZ77Hmw==, tarball: https://registry.npmjs.org/cookies/-/cookies-0.9.1.tgz} engines: {node: '>= 0.8'} copy-anything@2.0.6: - resolution: {integrity: sha512-1j20GZTsvKNkc4BY3NpMOM8tt///wY3FpIzozTOFO2ffuZcV61nojHXVKIy3WM+7ADCy5FVhdZYHYDdgTU0yJw==} + resolution: {integrity: sha512-1j20GZTsvKNkc4BY3NpMOM8tt///wY3FpIzozTOFO2ffuZcV61nojHXVKIy3WM+7ADCy5FVhdZYHYDdgTU0yJw==, tarball: https://registry.npmjs.org/copy-anything/-/copy-anything-2.0.6.tgz} copy-anything@3.0.5: - resolution: {integrity: sha512-yCEafptTtb4bk7GLEQoM8KVJpxAfdBJYaXyzQEgQQQgYrZiDp8SJmGKlYza6CYjEDNstAdNdKA3UuoULlEbS6w==} + resolution: {integrity: sha512-yCEafptTtb4bk7GLEQoM8KVJpxAfdBJYaXyzQEgQQQgYrZiDp8SJmGKlYza6CYjEDNstAdNdKA3UuoULlEbS6w==, tarball: https://registry.npmjs.org/copy-anything/-/copy-anything-3.0.5.tgz} engines: {node: '>=12.13'} copy-webpack-plugin@10.2.4: - resolution: {integrity: sha512-xFVltahqlsRcyyJqQbDY6EYTtyQZF9rf+JPjwHObLdPFMEISqkFkr7mFoVOC6BfYS/dNThyoQKvziugm+OnwBg==} + resolution: {integrity: sha512-xFVltahqlsRcyyJqQbDY6EYTtyQZF9rf+JPjwHObLdPFMEISqkFkr7mFoVOC6BfYS/dNThyoQKvziugm+OnwBg==, tarball: https://registry.npmjs.org/copy-webpack-plugin/-/copy-webpack-plugin-10.2.4.tgz} engines: {node: '>= 12.20.0'} peerDependencies: webpack: ^5.1.0 copy-webpack-plugin@14.0.0: - resolution: {integrity: sha512-3JLW90aBGeaTLpM7mYQKpnVdgsUZRExY55giiZgLuX/xTQRUs1dOCwbBnWnvY6Q6rfZoXMNwzOQJCSZPppfqXA==} + resolution: {integrity: sha512-3JLW90aBGeaTLpM7mYQKpnVdgsUZRExY55giiZgLuX/xTQRUs1dOCwbBnWnvY6Q6rfZoXMNwzOQJCSZPppfqXA==, tarball: https://registry.npmjs.org/copy-webpack-plugin/-/copy-webpack-plugin-14.0.0.tgz} engines: {node: '>= 20.9.0'} peerDependencies: webpack: ^5.1.0 core-js-compat@3.49.0: - resolution: {integrity: sha512-VQXt1jr9cBz03b331DFDCCP90b3fanciLkgiOoy8SBHy06gNf+vQ1A3WFLqG7I8TipYIKeYK9wxd0tUrvHcOZA==} + resolution: {integrity: sha512-VQXt1jr9cBz03b331DFDCCP90b3fanciLkgiOoy8SBHy06gNf+vQ1A3WFLqG7I8TipYIKeYK9wxd0tUrvHcOZA==, tarball: https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.49.0.tgz} core-js@3.36.1: - resolution: {integrity: sha512-BTvUrwxVBezj5SZ3f10ImnX2oRByMxql3EimVqMysepbC9EeMUOpLwdy6Eoili2x6E4kf+ZUB5k/+Jv55alPfA==} + resolution: {integrity: sha512-BTvUrwxVBezj5SZ3f10ImnX2oRByMxql3EimVqMysepbC9EeMUOpLwdy6Eoili2x6E4kf+ZUB5k/+Jv55alPfA==, tarball: https://registry.npmjs.org/core-js/-/core-js-3.36.1.tgz} core-util-is@1.0.3: - resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==} + resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==, tarball: https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz} cors@2.8.6: - resolution: {integrity: sha512-tJtZBBHA6vjIAaF6EnIaq6laBBP9aq/Y3ouVJjEfoHbRBcHBAHYcMh/w8LDrk2PvIMMq8gmopa5D4V8RmbrxGw==} + resolution: {integrity: sha512-tJtZBBHA6vjIAaF6EnIaq6laBBP9aq/Y3ouVJjEfoHbRBcHBAHYcMh/w8LDrk2PvIMMq8gmopa5D4V8RmbrxGw==, tarball: https://registry.npmjs.org/cors/-/cors-2.8.6.tgz} engines: {node: '>= 0.10'} corser@2.0.1: - resolution: {integrity: sha512-utCYNzRSQIZNPIcGZdQc92UVJYAhtGAteCFg0yRaFm8f0P+CPtyGyHXJcGXnffjCybUCEx3FQ2G7U3/o9eIkVQ==} + resolution: {integrity: sha512-utCYNzRSQIZNPIcGZdQc92UVJYAhtGAteCFg0yRaFm8f0P+CPtyGyHXJcGXnffjCybUCEx3FQ2G7U3/o9eIkVQ==, tarball: https://registry.npmjs.org/corser/-/corser-2.0.1.tgz} engines: {node: '>= 0.4.0'} cose-base@1.0.3: - resolution: {integrity: sha512-s9whTXInMSgAp/NVXVNuVxVKzGH2qck3aQlVHxDCdAEPgtMKwc4Wq6/QKhgdEdgbLSi9rBTAcPoRa6JpiG4ksg==} + resolution: {integrity: sha512-s9whTXInMSgAp/NVXVNuVxVKzGH2qck3aQlVHxDCdAEPgtMKwc4Wq6/QKhgdEdgbLSi9rBTAcPoRa6JpiG4ksg==, tarball: https://registry.npmjs.org/cose-base/-/cose-base-1.0.3.tgz} cose-base@2.2.0: - resolution: {integrity: sha512-AzlgcsCbUMymkADOJtQm3wO9S3ltPfYOFD5033keQn9NJzIbtnZj+UdBJe7DYml/8TdbtHJW3j58SOnKhWY/5g==} + resolution: {integrity: sha512-AzlgcsCbUMymkADOJtQm3wO9S3ltPfYOFD5033keQn9NJzIbtnZj+UdBJe7DYml/8TdbtHJW3j58SOnKhWY/5g==, tarball: https://registry.npmjs.org/cose-base/-/cose-base-2.2.0.tgz} cosmiconfig@7.1.0: - resolution: {integrity: sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==} + resolution: {integrity: sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==, tarball: https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.1.0.tgz} engines: {node: '>=10'} cosmiconfig@8.3.6: - resolution: {integrity: sha512-kcZ6+W5QzcJ3P1Mt+83OUv/oHFqZHIx8DuxG6eZ5RGMERoLqp4BuGjhHLYGK+Kf5XVkQvqBSmAy/nGWN3qDgEA==} + resolution: {integrity: sha512-kcZ6+W5QzcJ3P1Mt+83OUv/oHFqZHIx8DuxG6eZ5RGMERoLqp4BuGjhHLYGK+Kf5XVkQvqBSmAy/nGWN3qDgEA==, tarball: https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-8.3.6.tgz} engines: {node: '>=14'} peerDependencies: typescript: '>=4.9.5' @@ -8441,7 +8475,7 @@ packages: optional: true cosmiconfig@9.0.2: - resolution: {integrity: sha512-gtTZxTDau1wL7Y7zifc2dd8jHSK/k6BTx/2Xp/BpdlAdnlYWFVt7qhJqgwi7637yRwRQ3qL4ZidbB4I8tA5VOg==} + resolution: {integrity: sha512-gtTZxTDau1wL7Y7zifc2dd8jHSK/k6BTx/2Xp/BpdlAdnlYWFVt7qhJqgwi7637yRwRQ3qL4ZidbB4I8tA5VOg==, tarball: https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-9.0.2.tgz} engines: {node: '>=14'} peerDependencies: typescript: '>=4.9.5' @@ -8450,45 +8484,45 @@ packages: optional: true create-point-cb@1.2.0: - resolution: {integrity: sha512-r4l6IO/YGI7hIZRMLggOzwM6XO80+Fdcv4hx1fXCEdU+hKd7zZki6i+cbYfK9OliMwMYx1wPfQLU/snvS+Dygw==} + resolution: {integrity: sha512-r4l6IO/YGI7hIZRMLggOzwM6XO80+Fdcv4hx1fXCEdU+hKd7zZki6i+cbYfK9OliMwMYx1wPfQLU/snvS+Dygw==, tarball: https://registry.npmjs.org/create-point-cb/-/create-point-cb-1.2.0.tgz} create-require@1.1.1: - resolution: {integrity: sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==} + resolution: {integrity: sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==, tarball: https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz} crelt@1.0.7: - resolution: {integrity: sha512-aK6BbWfhf4U/wCcLHKPJl/xa6VkVstRaPywWtMKGwuOLc/wZTyQYuoxgvZnNsBvv7Kg3YTBQYYBCggcviQczuA==} + resolution: {integrity: sha512-aK6BbWfhf4U/wCcLHKPJl/xa6VkVstRaPywWtMKGwuOLc/wZTyQYuoxgvZnNsBvv7Kg3YTBQYYBCggcviQczuA==, tarball: https://registry.npmjs.org/crelt/-/crelt-1.0.7.tgz} cron-parser@4.9.0: - resolution: {integrity: sha512-p0SaNjrHOnQeR8/VnfGbmg9te2kfyYSQ7Sc/j/6DtPL3JQvKxmjO9TSjNFpujqV3vEYYBvNNvXSxzyksBWAx1Q==} + resolution: {integrity: sha512-p0SaNjrHOnQeR8/VnfGbmg9te2kfyYSQ7Sc/j/6DtPL3JQvKxmjO9TSjNFpujqV3vEYYBvNNvXSxzyksBWAx1Q==, tarball: https://registry.npmjs.org/cron-parser/-/cron-parser-4.9.0.tgz} engines: {node: '>=12.0.0'} cross-fetch@3.1.4: - resolution: {integrity: sha512-1eAtFWdIubi6T4XPy6ei9iUFoKpUkIF971QLN8lIvvvwueI65+Nw5haMNKUwfJxabqlIIDODJKGrQ66gxC0PbQ==} + resolution: {integrity: sha512-1eAtFWdIubi6T4XPy6ei9iUFoKpUkIF971QLN8lIvvvwueI65+Nw5haMNKUwfJxabqlIIDODJKGrQ66gxC0PbQ==, tarball: https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.1.4.tgz} cross-spawn@7.0.6: - resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} + resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==, tarball: https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz} engines: {node: '>= 8'} crossvent@1.5.5: - resolution: {integrity: sha512-MY4xhBYEnVi+pmTpHCOCsCLYczc0PVtGdPBz6NXNXxikLaUZo4HdAeUb1UqAo3t3yXAloSelTmfxJ+/oUqkW5w==} + resolution: {integrity: sha512-MY4xhBYEnVi+pmTpHCOCsCLYczc0PVtGdPBz6NXNXxikLaUZo4HdAeUb1UqAo3t3yXAloSelTmfxJ+/oUqkW5w==, tarball: https://registry.npmjs.org/crossvent/-/crossvent-1.5.5.tgz} crypt@0.0.2: - resolution: {integrity: sha512-mCxBlsHFYh9C+HVpiEacem8FEBnMXgU9gy4zmNC+SXAZNB/1idgp/aulFJ4FgCi7GPEVbfyng092GqL2k2rmow==} + resolution: {integrity: sha512-mCxBlsHFYh9C+HVpiEacem8FEBnMXgU9gy4zmNC+SXAZNB/1idgp/aulFJ4FgCi7GPEVbfyng092GqL2k2rmow==, tarball: https://registry.npmjs.org/crypt/-/crypt-0.0.2.tgz} css-declaration-sorter@6.4.1: - resolution: {integrity: sha512-rtdthzxKuyq6IzqX6jEcIzQF/YqccluefyCYheovBOLhFT/drQA9zj/UbRAa9J7C0o6EG6u3E6g+vKkay7/k3g==} + resolution: {integrity: sha512-rtdthzxKuyq6IzqX6jEcIzQF/YqccluefyCYheovBOLhFT/drQA9zj/UbRAa9J7C0o6EG6u3E6g+vKkay7/k3g==, tarball: https://registry.npmjs.org/css-declaration-sorter/-/css-declaration-sorter-6.4.1.tgz} engines: {node: ^10 || ^12 || >=14} peerDependencies: postcss: ^8.0.9 css-declaration-sorter@7.4.0: - resolution: {integrity: sha512-LTuzjPoyA2vMGKKcaOqKSp7Ub2eGrNfKiZH4LpezxpNrsICGCSFvsQOI29psISxNZtaXibkC2CXzrQ5enMeGGw==} + resolution: {integrity: sha512-LTuzjPoyA2vMGKKcaOqKSp7Ub2eGrNfKiZH4LpezxpNrsICGCSFvsQOI29psISxNZtaXibkC2CXzrQ5enMeGGw==, tarball: https://registry.npmjs.org/css-declaration-sorter/-/css-declaration-sorter-7.4.0.tgz} engines: {node: ^14 || ^16 || >=18} peerDependencies: postcss: ^8.0.9 css-loader@6.11.0: - resolution: {integrity: sha512-CTJ+AEQJjq5NzLga5pE39qdiSV56F8ywCIsqNIRF0r7BDgWsN25aazToqAFg7ZrtA/U016xudB3ffgweORxX7g==} + resolution: {integrity: sha512-CTJ+AEQJjq5NzLga5pE39qdiSV56F8ywCIsqNIRF0r7BDgWsN25aazToqAFg7ZrtA/U016xudB3ffgweORxX7g==, tarball: https://registry.npmjs.org/css-loader/-/css-loader-6.11.0.tgz} engines: {node: '>= 12.13.0'} peerDependencies: '@rspack/core': 0.x || 1.x @@ -8500,7 +8534,7 @@ packages: optional: true css-loader@7.1.4: - resolution: {integrity: sha512-vv3J9tlOl04WjiMvHQI/9tmIrCxVrj6PFbHemBB1iihpeRbi/I4h033eoFIhwxBBqLhI0KYFS7yvynBFhIZfTw==} + resolution: {integrity: sha512-vv3J9tlOl04WjiMvHQI/9tmIrCxVrj6PFbHemBB1iihpeRbi/I4h033eoFIhwxBBqLhI0KYFS7yvynBFhIZfTw==, tarball: https://registry.npmjs.org/css-loader/-/css-loader-7.1.4.tgz} engines: {node: '>= 18.12.0'} peerDependencies: '@rspack/core': 0.x || ^1.0.0 || ^2.0.0-0 @@ -8512,7 +8546,7 @@ packages: optional: true css-minimizer-webpack-plugin@5.0.1: - resolution: {integrity: sha512-3caImjKFQkS+ws1TGcFn0V1HyDJFq1Euy589JlD6/3rV2kj+w7r5G9WDMgSHvpvXHNZ2calVypZWuEDQd9wfLg==} + resolution: {integrity: sha512-3caImjKFQkS+ws1TGcFn0V1HyDJFq1Euy589JlD6/3rV2kj+w7r5G9WDMgSHvpvXHNZ2calVypZWuEDQd9wfLg==, tarball: https://registry.npmjs.org/css-minimizer-webpack-plugin/-/css-minimizer-webpack-plugin-5.0.1.tgz} engines: {node: '>= 14.15.0'} peerDependencies: '@parcel/css': '*' @@ -8537,7 +8571,7 @@ packages: optional: true css-minimizer-webpack-plugin@8.0.0: - resolution: {integrity: sha512-9bEpzHs8gEq6/cbEj418jXL/YWjBUD2YTLLk905Npt2JODqnRITin0+So5Vx4Dp5vyi2Lpt9pp2QHzQ7fdxNrw==} + resolution: {integrity: sha512-9bEpzHs8gEq6/cbEj418jXL/YWjBUD2YTLLk905Npt2JODqnRITin0+So5Vx4Dp5vyi2Lpt9pp2QHzQ7fdxNrw==, tarball: https://registry.npmjs.org/css-minimizer-webpack-plugin/-/css-minimizer-webpack-plugin-8.0.0.tgz} engines: {node: '>= 20.9.0'} peerDependencies: '@parcel/css': '*' @@ -8562,325 +8596,333 @@ packages: optional: true css-select@4.3.0: - resolution: {integrity: sha512-wPpOYtnsVontu2mODhA19JrqWxNsfdatRKd64kmpRbQgh1KtItko5sTnEpPdpSaJszTOhEMlF/RPz28qj4HqhQ==} + resolution: {integrity: sha512-wPpOYtnsVontu2mODhA19JrqWxNsfdatRKd64kmpRbQgh1KtItko5sTnEpPdpSaJszTOhEMlF/RPz28qj4HqhQ==, tarball: https://registry.npmjs.org/css-select/-/css-select-4.3.0.tgz} css-select@5.2.2: - resolution: {integrity: sha512-TizTzUddG/xYLA3NXodFM0fSbNizXjOKhqiQQwvhlspadZokn1KDy0NZFS0wuEubIYAV5/c1/lAr0TaaFXEXzw==} + resolution: {integrity: sha512-TizTzUddG/xYLA3NXodFM0fSbNizXjOKhqiQQwvhlspadZokn1KDy0NZFS0wuEubIYAV5/c1/lAr0TaaFXEXzw==, tarball: https://registry.npmjs.org/css-select/-/css-select-5.2.2.tgz} css-select@6.0.0: - resolution: {integrity: sha512-rZZVSLle8v0+EY8QAkDWrKhpgt6SA5OtHsgBnsj6ZaLb5dmDVOWUDtQitd9ydxxvEjhewNudS6eTVU7uOyzvXw==} + resolution: {integrity: sha512-rZZVSLle8v0+EY8QAkDWrKhpgt6SA5OtHsgBnsj6ZaLb5dmDVOWUDtQitd9ydxxvEjhewNudS6eTVU7uOyzvXw==, tarball: https://registry.npmjs.org/css-select/-/css-select-6.0.0.tgz} + + css-select@7.0.0: + resolution: {integrity: sha512-snmjEVXy+1LnwXdxhYvTMj1d9tOh4HxkA1YmoayVBeeyR2C14Pum7fcxJIm4SswYspVy866eYNwlH6xC3/VH5g==, tarball: https://registry.npmjs.org/css-select/-/css-select-7.0.0.tgz} + engines: {node: '>=20.19.0'} css-tree@1.1.3: - resolution: {integrity: sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q==} + resolution: {integrity: sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q==, tarball: https://registry.npmjs.org/css-tree/-/css-tree-1.1.3.tgz} engines: {node: '>=8.0.0'} css-tree@2.2.1: - resolution: {integrity: sha512-OA0mILzGc1kCOCSJerOeqDxDQ4HOh+G8NbOJFOTgOCzpw7fCBubk0fEyxp8AgOL/jvLgYA/uV0cMbe43ElF1JA==} + resolution: {integrity: sha512-OA0mILzGc1kCOCSJerOeqDxDQ4HOh+G8NbOJFOTgOCzpw7fCBubk0fEyxp8AgOL/jvLgYA/uV0cMbe43ElF1JA==, tarball: https://registry.npmjs.org/css-tree/-/css-tree-2.2.1.tgz} engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0, npm: '>=7.0.0'} css-tree@2.3.1: - resolution: {integrity: sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw==} + resolution: {integrity: sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw==, tarball: https://registry.npmjs.org/css-tree/-/css-tree-2.3.1.tgz} engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0} css-tree@3.2.1: - resolution: {integrity: sha512-X7sjQzceUhu1u7Y/ylrRZFU2FS6LRiFVp6rKLPg23y3x3c3DOKAwuXGDp+PAGjh6CSnCjYeAul8pcT8bAl+lSA==} + resolution: {integrity: sha512-X7sjQzceUhu1u7Y/ylrRZFU2FS6LRiFVp6rKLPg23y3x3c3DOKAwuXGDp+PAGjh6CSnCjYeAul8pcT8bAl+lSA==, tarball: https://registry.npmjs.org/css-tree/-/css-tree-3.2.1.tgz} engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0} css-what@6.2.2: - resolution: {integrity: sha512-u/O3vwbptzhMs3L1fQE82ZSLHQQfto5gyZzwteVIEyeaY5Fc7R4dapF/BvRoSYFeqfBk4m0V1Vafq5Pjv25wvA==} + resolution: {integrity: sha512-u/O3vwbptzhMs3L1fQE82ZSLHQQfto5gyZzwteVIEyeaY5Fc7R4dapF/BvRoSYFeqfBk4m0V1Vafq5Pjv25wvA==, tarball: https://registry.npmjs.org/css-what/-/css-what-6.2.2.tgz} engines: {node: '>= 6'} css-what@7.0.0: - resolution: {integrity: sha512-wD5oz5xibMOPHzy13CyGmogB3phdvcDaB5t0W/Nr5Z2O/agcB8YwOz6e2Lsp10pNDzBoDO9nVa3RGs/2BttpHQ==} + resolution: {integrity: sha512-wD5oz5xibMOPHzy13CyGmogB3phdvcDaB5t0W/Nr5Z2O/agcB8YwOz6e2Lsp10pNDzBoDO9nVa3RGs/2BttpHQ==, tarball: https://registry.npmjs.org/css-what/-/css-what-7.0.0.tgz} engines: {node: '>= 6'} + css-what@8.0.0: + resolution: {integrity: sha512-DH0Bqq3DNp5tdOReuNyAA+Ev4Y2GS5FMbZpeTLP6C4CDi0h5nL0BmUPChXw3o/qbHLDWHl49sbNqQVY7bMSDdw==, tarball: https://registry.npmjs.org/css-what/-/css-what-8.0.0.tgz} + engines: {node: '>=20.19.0'} + css.escape@1.5.1: - resolution: {integrity: sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg==} + resolution: {integrity: sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg==, tarball: https://registry.npmjs.org/css.escape/-/css.escape-1.5.1.tgz} cssesc@3.0.0: - resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==} + resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==, tarball: https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz} engines: {node: '>=4'} hasBin: true cssnano-preset-default@5.2.14: - resolution: {integrity: sha512-t0SFesj/ZV2OTylqQVOrFgEh5uanxbO6ZAdeCrNsUQ6fVuXwYTxJPNAGvGTxHbD68ldIJNec7PyYZDBrfDQ+6A==} + resolution: {integrity: sha512-t0SFesj/ZV2OTylqQVOrFgEh5uanxbO6ZAdeCrNsUQ6fVuXwYTxJPNAGvGTxHbD68ldIJNec7PyYZDBrfDQ+6A==, tarball: https://registry.npmjs.org/cssnano-preset-default/-/cssnano-preset-default-5.2.14.tgz} engines: {node: ^10 || ^12 || >=14.0} peerDependencies: postcss: ^8.2.15 cssnano-preset-default@6.1.2: - resolution: {integrity: sha512-1C0C+eNaeN8OcHQa193aRgYexyJtU8XwbdieEjClw+J9d94E41LwT6ivKH0WT+fYwYWB0Zp3I3IZ7tI/BbUbrg==} + resolution: {integrity: sha512-1C0C+eNaeN8OcHQa193aRgYexyJtU8XwbdieEjClw+J9d94E41LwT6ivKH0WT+fYwYWB0Zp3I3IZ7tI/BbUbrg==, tarball: https://registry.npmjs.org/cssnano-preset-default/-/cssnano-preset-default-6.1.2.tgz} engines: {node: ^14 || ^16 || >=18.0} peerDependencies: postcss: ^8.4.31 cssnano-preset-default@7.0.17: - resolution: {integrity: sha512-11qO63A+czwguQFJCaTdICvbaxn0pJzz/XghLlv+OT7WyToDxAMR0Xb3/26/l0y0hQJywwNbj/SLSQlGBHE1OA==} + resolution: {integrity: sha512-11qO63A+czwguQFJCaTdICvbaxn0pJzz/XghLlv+OT7WyToDxAMR0Xb3/26/l0y0hQJywwNbj/SLSQlGBHE1OA==, tarball: https://registry.npmjs.org/cssnano-preset-default/-/cssnano-preset-default-7.0.17.tgz} engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} peerDependencies: postcss: ^8.5.13 cssnano-utils@3.1.0: - resolution: {integrity: sha512-JQNR19/YZhz4psLX/rQ9M83e3z2Wf/HdJbryzte4a3NSuafyp9w/I4U+hx5C2S9g41qlstH7DEWnZaaj83OuEA==} + resolution: {integrity: sha512-JQNR19/YZhz4psLX/rQ9M83e3z2Wf/HdJbryzte4a3NSuafyp9w/I4U+hx5C2S9g41qlstH7DEWnZaaj83OuEA==, tarball: https://registry.npmjs.org/cssnano-utils/-/cssnano-utils-3.1.0.tgz} engines: {node: ^10 || ^12 || >=14.0} peerDependencies: postcss: ^8.2.15 cssnano-utils@4.0.2: - resolution: {integrity: sha512-ZR1jHg+wZ8o4c3zqf1SIUSTIvm/9mU343FMR6Obe/unskbvpGhZOo1J6d/r8D1pzkRQYuwbcH3hToOuoA2G7oQ==} + resolution: {integrity: sha512-ZR1jHg+wZ8o4c3zqf1SIUSTIvm/9mU343FMR6Obe/unskbvpGhZOo1J6d/r8D1pzkRQYuwbcH3hToOuoA2G7oQ==, tarball: https://registry.npmjs.org/cssnano-utils/-/cssnano-utils-4.0.2.tgz} engines: {node: ^14 || ^16 || >=18.0} peerDependencies: postcss: ^8.4.31 cssnano-utils@5.0.3: - resolution: {integrity: sha512-ynIREMICLxkxm7e9bCR9sh75s4Q5drICi0ua1yxo5jH2XPBqSKkl4dOh4EbFqtUmnTMhRffHgYL0EKKkMjtJTg==} + resolution: {integrity: sha512-ynIREMICLxkxm7e9bCR9sh75s4Q5drICi0ua1yxo5jH2XPBqSKkl4dOh4EbFqtUmnTMhRffHgYL0EKKkMjtJTg==, tarball: https://registry.npmjs.org/cssnano-utils/-/cssnano-utils-5.0.3.tgz} engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} peerDependencies: postcss: ^8.5.13 cssnano@5.1.15: - resolution: {integrity: sha512-j+BKgDcLDQA+eDifLx0EO4XSA56b7uut3BQFH+wbSaSTuGLuiyTa/wbRYthUXX8LC9mLg+WWKe8h+qJuwTAbHw==} + resolution: {integrity: sha512-j+BKgDcLDQA+eDifLx0EO4XSA56b7uut3BQFH+wbSaSTuGLuiyTa/wbRYthUXX8LC9mLg+WWKe8h+qJuwTAbHw==, tarball: https://registry.npmjs.org/cssnano/-/cssnano-5.1.15.tgz} engines: {node: ^10 || ^12 || >=14.0} peerDependencies: postcss: ^8.2.15 cssnano@6.1.2: - resolution: {integrity: sha512-rYk5UeX7VAM/u0lNqewCdasdtPK81CgX8wJFLEIXHbV2oldWRgJAsZrdhRXkV1NJzA2g850KiFm9mMU2HxNxMA==} + resolution: {integrity: sha512-rYk5UeX7VAM/u0lNqewCdasdtPK81CgX8wJFLEIXHbV2oldWRgJAsZrdhRXkV1NJzA2g850KiFm9mMU2HxNxMA==, tarball: https://registry.npmjs.org/cssnano/-/cssnano-6.1.2.tgz} engines: {node: ^14 || ^16 || >=18.0} peerDependencies: postcss: ^8.4.31 cssnano@7.1.9: - resolution: {integrity: sha512-uPR75+5Dk/WJ/YSPR1/YDHdwMM9c5FsaARljfKWgeCKLKOtJ0we21xy/RcCjn53fZnD/f6yYEIZ8pu18+GnbNQ==} + resolution: {integrity: sha512-uPR75+5Dk/WJ/YSPR1/YDHdwMM9c5FsaARljfKWgeCKLKOtJ0we21xy/RcCjn53fZnD/f6yYEIZ8pu18+GnbNQ==, tarball: https://registry.npmjs.org/cssnano/-/cssnano-7.1.9.tgz} engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} peerDependencies: postcss: ^8.5.13 csso@4.2.0: - resolution: {integrity: sha512-wvlcdIbf6pwKEk7vHj8/Bkc0B4ylXZruLvOgs9doS5eOsOpuodOV2zJChSpkp+pRpYQLQMeF04nr3Z68Sta9jA==} + resolution: {integrity: sha512-wvlcdIbf6pwKEk7vHj8/Bkc0B4ylXZruLvOgs9doS5eOsOpuodOV2zJChSpkp+pRpYQLQMeF04nr3Z68Sta9jA==, tarball: https://registry.npmjs.org/csso/-/csso-4.2.0.tgz} engines: {node: '>=8.0.0'} csso@5.0.5: - resolution: {integrity: sha512-0LrrStPOdJj+SPCCrGhzryycLjwcgUSHBtxNA8aIDxf0GLsRh1cKYhB00Gd1lDOS4yGH69+SNn13+TWbVHETFQ==} + resolution: {integrity: sha512-0LrrStPOdJj+SPCCrGhzryycLjwcgUSHBtxNA8aIDxf0GLsRh1cKYhB00Gd1lDOS4yGH69+SNn13+TWbVHETFQ==, tarball: https://registry.npmjs.org/csso/-/csso-5.0.5.tgz} engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0, npm: '>=7.0.0'} cssom@0.3.8: - resolution: {integrity: sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==} + resolution: {integrity: sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==, tarball: https://registry.npmjs.org/cssom/-/cssom-0.3.8.tgz} cssom@0.5.0: - resolution: {integrity: sha512-iKuQcq+NdHqlAcwUY0o/HL69XQrUaQdMjmStJ8JFmUaiiQErlhrmuigkg/CU4E2J0IyUKUrMAgl36TvN67MqTw==} + resolution: {integrity: sha512-iKuQcq+NdHqlAcwUY0o/HL69XQrUaQdMjmStJ8JFmUaiiQErlhrmuigkg/CU4E2J0IyUKUrMAgl36TvN67MqTw==, tarball: https://registry.npmjs.org/cssom/-/cssom-0.5.0.tgz} cssstyle@2.3.0: - resolution: {integrity: sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A==} + resolution: {integrity: sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A==, tarball: https://registry.npmjs.org/cssstyle/-/cssstyle-2.3.0.tgz} engines: {node: '>=8'} cssstyle@6.2.0: - resolution: {integrity: sha512-Fm5NvhYathRnXNVndkUsCCuR63DCLVVwGOOwQw782coXFi5HhkXdu289l59HlXZBawsyNccXfWRYvLzcDCdDig==} + resolution: {integrity: sha512-Fm5NvhYathRnXNVndkUsCCuR63DCLVVwGOOwQw782coXFi5HhkXdu289l59HlXZBawsyNccXfWRYvLzcDCdDig==, tarball: https://registry.npmjs.org/cssstyle/-/cssstyle-6.2.0.tgz} engines: {node: '>=20'} csstype@3.2.3: - resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==} + resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==, tarball: https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz} custom-event@1.0.1: - resolution: {integrity: sha512-GAj5FOq0Hd+RsCGVJxZuKaIDXDf3h6GQoNEjFgbLLI/trgtavwUbSnZ5pVfg27DVCaWjIohryS0JFwIJyT2cMg==} + resolution: {integrity: sha512-GAj5FOq0Hd+RsCGVJxZuKaIDXDf3h6GQoNEjFgbLLI/trgtavwUbSnZ5pVfg27DVCaWjIohryS0JFwIJyT2cMg==, tarball: https://registry.npmjs.org/custom-event/-/custom-event-1.0.1.tgz} cytoscape-cose-bilkent@4.1.0: - resolution: {integrity: sha512-wgQlVIUJF13Quxiv5e1gstZ08rnZj2XaLHGoFMYXz7SkNfCDOOteKBE6SYRfA9WxxI/iBc3ajfDoc6hb/MRAHQ==} + resolution: {integrity: sha512-wgQlVIUJF13Quxiv5e1gstZ08rnZj2XaLHGoFMYXz7SkNfCDOOteKBE6SYRfA9WxxI/iBc3ajfDoc6hb/MRAHQ==, tarball: https://registry.npmjs.org/cytoscape-cose-bilkent/-/cytoscape-cose-bilkent-4.1.0.tgz} peerDependencies: cytoscape: ^3.2.0 cytoscape-fcose@2.2.0: - resolution: {integrity: sha512-ki1/VuRIHFCzxWNrsshHYPs6L7TvLu3DL+TyIGEsRcvVERmxokbf5Gdk7mFxZnTdiGtnA4cfSmjZJMviqSuZrQ==} + resolution: {integrity: sha512-ki1/VuRIHFCzxWNrsshHYPs6L7TvLu3DL+TyIGEsRcvVERmxokbf5Gdk7mFxZnTdiGtnA4cfSmjZJMviqSuZrQ==, tarball: https://registry.npmjs.org/cytoscape-fcose/-/cytoscape-fcose-2.2.0.tgz} peerDependencies: cytoscape: ^3.2.0 cytoscape@3.34.0: - resolution: {integrity: sha512-62rNSrioXw93uliKFBwjukeQyeWwH2PqDrTac31r2P6464u3AUvTk0xS4LVvT251g7IgkFunrI48ZEZGjywSOg==} + resolution: {integrity: sha512-62rNSrioXw93uliKFBwjukeQyeWwH2PqDrTac31r2P6464u3AUvTk0xS4LVvT251g7IgkFunrI48ZEZGjywSOg==, tarball: https://registry.npmjs.org/cytoscape/-/cytoscape-3.34.0.tgz} engines: {node: '>=0.10'} d3-array@2.12.1: - resolution: {integrity: sha512-B0ErZK/66mHtEsR1TkPEEkwdy+WDesimkM5gpZr5Dsg54BiTA5RXtYW5qTLIAcekaS9xfZrzBLF/OAkB3Qn1YQ==} + resolution: {integrity: sha512-B0ErZK/66mHtEsR1TkPEEkwdy+WDesimkM5gpZr5Dsg54BiTA5RXtYW5qTLIAcekaS9xfZrzBLF/OAkB3Qn1YQ==, tarball: https://registry.npmjs.org/d3-array/-/d3-array-2.12.1.tgz} d3-array@3.2.4: - resolution: {integrity: sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==} + resolution: {integrity: sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==, tarball: https://registry.npmjs.org/d3-array/-/d3-array-3.2.4.tgz} engines: {node: '>=12'} d3-axis@3.0.0: - resolution: {integrity: sha512-IH5tgjV4jE/GhHkRV0HiVYPDtvfjHQlQfJHs0usq7M30XcSBvOotpmH1IgkcXsO/5gEQZD43B//fc7SRT5S+xw==} + resolution: {integrity: sha512-IH5tgjV4jE/GhHkRV0HiVYPDtvfjHQlQfJHs0usq7M30XcSBvOotpmH1IgkcXsO/5gEQZD43B//fc7SRT5S+xw==, tarball: https://registry.npmjs.org/d3-axis/-/d3-axis-3.0.0.tgz} engines: {node: '>=12'} d3-brush@3.0.0: - resolution: {integrity: sha512-ALnjWlVYkXsVIGlOsuWH1+3udkYFI48Ljihfnh8FZPF2QS9o+PzGLBslO0PjzVoHLZ2KCVgAM8NVkXPJB2aNnQ==} + resolution: {integrity: sha512-ALnjWlVYkXsVIGlOsuWH1+3udkYFI48Ljihfnh8FZPF2QS9o+PzGLBslO0PjzVoHLZ2KCVgAM8NVkXPJB2aNnQ==, tarball: https://registry.npmjs.org/d3-brush/-/d3-brush-3.0.0.tgz} engines: {node: '>=12'} d3-chord@3.0.1: - resolution: {integrity: sha512-VE5S6TNa+j8msksl7HwjxMHDM2yNK3XCkusIlpX5kwauBfXuyLAtNg9jCp/iHH61tgI4sb6R/EIMWCqEIdjT/g==} + resolution: {integrity: sha512-VE5S6TNa+j8msksl7HwjxMHDM2yNK3XCkusIlpX5kwauBfXuyLAtNg9jCp/iHH61tgI4sb6R/EIMWCqEIdjT/g==, tarball: https://registry.npmjs.org/d3-chord/-/d3-chord-3.0.1.tgz} engines: {node: '>=12'} d3-color@3.1.0: - resolution: {integrity: sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==} + resolution: {integrity: sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==, tarball: https://registry.npmjs.org/d3-color/-/d3-color-3.1.0.tgz} engines: {node: '>=12'} d3-contour@4.0.2: - resolution: {integrity: sha512-4EzFTRIikzs47RGmdxbeUvLWtGedDUNkTcmzoeyg4sP/dvCexO47AaQL7VKy/gul85TOxw+IBgA8US2xwbToNA==} + resolution: {integrity: sha512-4EzFTRIikzs47RGmdxbeUvLWtGedDUNkTcmzoeyg4sP/dvCexO47AaQL7VKy/gul85TOxw+IBgA8US2xwbToNA==, tarball: https://registry.npmjs.org/d3-contour/-/d3-contour-4.0.2.tgz} engines: {node: '>=12'} d3-delaunay@6.0.4: - resolution: {integrity: sha512-mdjtIZ1XLAM8bm/hx3WwjfHt6Sggek7qH043O8KEjDXN40xi3vx/6pYSVTwLjEgiXQTbvaouWKynLBiUZ6SK6A==} + resolution: {integrity: sha512-mdjtIZ1XLAM8bm/hx3WwjfHt6Sggek7qH043O8KEjDXN40xi3vx/6pYSVTwLjEgiXQTbvaouWKynLBiUZ6SK6A==, tarball: https://registry.npmjs.org/d3-delaunay/-/d3-delaunay-6.0.4.tgz} engines: {node: '>=12'} d3-dispatch@3.0.1: - resolution: {integrity: sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg==} + resolution: {integrity: sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg==, tarball: https://registry.npmjs.org/d3-dispatch/-/d3-dispatch-3.0.1.tgz} engines: {node: '>=12'} d3-drag@3.0.0: - resolution: {integrity: sha512-pWbUJLdETVA8lQNJecMxoXfH6x+mO2UQo8rSmZ+QqxcbyA3hfeprFgIT//HW2nlHChWeIIMwS2Fq+gEARkhTkg==} + resolution: {integrity: sha512-pWbUJLdETVA8lQNJecMxoXfH6x+mO2UQo8rSmZ+QqxcbyA3hfeprFgIT//HW2nlHChWeIIMwS2Fq+gEARkhTkg==, tarball: https://registry.npmjs.org/d3-drag/-/d3-drag-3.0.0.tgz} engines: {node: '>=12'} d3-dsv@3.0.1: - resolution: {integrity: sha512-UG6OvdI5afDIFP9w4G0mNq50dSOsXHJaRE8arAS5o9ApWnIElp8GZw1Dun8vP8OyHOZ/QJUKUJwxiiCCnUwm+Q==} + resolution: {integrity: sha512-UG6OvdI5afDIFP9w4G0mNq50dSOsXHJaRE8arAS5o9ApWnIElp8GZw1Dun8vP8OyHOZ/QJUKUJwxiiCCnUwm+Q==, tarball: https://registry.npmjs.org/d3-dsv/-/d3-dsv-3.0.1.tgz} engines: {node: '>=12'} hasBin: true d3-ease@3.0.1: - resolution: {integrity: sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==} + resolution: {integrity: sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==, tarball: https://registry.npmjs.org/d3-ease/-/d3-ease-3.0.1.tgz} engines: {node: '>=12'} d3-fetch@3.0.1: - resolution: {integrity: sha512-kpkQIM20n3oLVBKGg6oHrUchHM3xODkTzjMoj7aWQFq5QEM+R6E4WkzT5+tojDY7yjez8KgCBRoj4aEr99Fdqw==} + resolution: {integrity: sha512-kpkQIM20n3oLVBKGg6oHrUchHM3xODkTzjMoj7aWQFq5QEM+R6E4WkzT5+tojDY7yjez8KgCBRoj4aEr99Fdqw==, tarball: https://registry.npmjs.org/d3-fetch/-/d3-fetch-3.0.1.tgz} engines: {node: '>=12'} d3-force@3.0.0: - resolution: {integrity: sha512-zxV/SsA+U4yte8051P4ECydjD/S+qeYtnaIyAs9tgHCqfguma/aAQDjo85A9Z6EKhBirHRJHXIgJUlffT4wdLg==} + resolution: {integrity: sha512-zxV/SsA+U4yte8051P4ECydjD/S+qeYtnaIyAs9tgHCqfguma/aAQDjo85A9Z6EKhBirHRJHXIgJUlffT4wdLg==, tarball: https://registry.npmjs.org/d3-force/-/d3-force-3.0.0.tgz} engines: {node: '>=12'} d3-format@3.1.2: - resolution: {integrity: sha512-AJDdYOdnyRDV5b6ArilzCPPwc1ejkHcoyFarqlPqT7zRYjhavcT3uSrqcMvsgh2CgoPbK3RCwyHaVyxYcP2Arg==} + resolution: {integrity: sha512-AJDdYOdnyRDV5b6ArilzCPPwc1ejkHcoyFarqlPqT7zRYjhavcT3uSrqcMvsgh2CgoPbK3RCwyHaVyxYcP2Arg==, tarball: https://registry.npmjs.org/d3-format/-/d3-format-3.1.2.tgz} engines: {node: '>=12'} d3-geo@3.1.1: - resolution: {integrity: sha512-637ln3gXKXOwhalDzinUgY83KzNWZRKbYubaG+fGVuc/dxO64RRljtCTnf5ecMyE1RIdtqpkVcq0IbtU2S8j2Q==} + resolution: {integrity: sha512-637ln3gXKXOwhalDzinUgY83KzNWZRKbYubaG+fGVuc/dxO64RRljtCTnf5ecMyE1RIdtqpkVcq0IbtU2S8j2Q==, tarball: https://registry.npmjs.org/d3-geo/-/d3-geo-3.1.1.tgz} engines: {node: '>=12'} d3-hierarchy@3.1.2: - resolution: {integrity: sha512-FX/9frcub54beBdugHjDCdikxThEqjnR93Qt7PvQTOHxyiNCAlvMrHhclk3cD5VeAaq9fxmfRp+CnWw9rEMBuA==} + resolution: {integrity: sha512-FX/9frcub54beBdugHjDCdikxThEqjnR93Qt7PvQTOHxyiNCAlvMrHhclk3cD5VeAaq9fxmfRp+CnWw9rEMBuA==, tarball: https://registry.npmjs.org/d3-hierarchy/-/d3-hierarchy-3.1.2.tgz} engines: {node: '>=12'} d3-interpolate@3.0.1: - resolution: {integrity: sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==} + resolution: {integrity: sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==, tarball: https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-3.0.1.tgz} engines: {node: '>=12'} d3-path@1.0.9: - resolution: {integrity: sha512-VLaYcn81dtHVTjEHd8B+pbe9yHWpXKZUC87PzoFmsFrJqgFwDe/qxfp5MlfsfM1V5E/iVt0MmEbWQ7FVIXh/bg==} + resolution: {integrity: sha512-VLaYcn81dtHVTjEHd8B+pbe9yHWpXKZUC87PzoFmsFrJqgFwDe/qxfp5MlfsfM1V5E/iVt0MmEbWQ7FVIXh/bg==, tarball: https://registry.npmjs.org/d3-path/-/d3-path-1.0.9.tgz} d3-path@3.1.0: - resolution: {integrity: sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==} + resolution: {integrity: sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==, tarball: https://registry.npmjs.org/d3-path/-/d3-path-3.1.0.tgz} engines: {node: '>=12'} d3-polygon@3.0.1: - resolution: {integrity: sha512-3vbA7vXYwfe1SYhED++fPUQlWSYTTGmFmQiany/gdbiWgU/iEyQzyymwL9SkJjFFuCS4902BSzewVGsHHmHtXg==} + resolution: {integrity: sha512-3vbA7vXYwfe1SYhED++fPUQlWSYTTGmFmQiany/gdbiWgU/iEyQzyymwL9SkJjFFuCS4902BSzewVGsHHmHtXg==, tarball: https://registry.npmjs.org/d3-polygon/-/d3-polygon-3.0.1.tgz} engines: {node: '>=12'} d3-quadtree@3.0.1: - resolution: {integrity: sha512-04xDrxQTDTCFwP5H6hRhsRcb9xxv2RzkcsygFzmkSIOJy3PeRJP7sNk3VRIbKXcog561P9oU0/rVH6vDROAgUw==} + resolution: {integrity: sha512-04xDrxQTDTCFwP5H6hRhsRcb9xxv2RzkcsygFzmkSIOJy3PeRJP7sNk3VRIbKXcog561P9oU0/rVH6vDROAgUw==, tarball: https://registry.npmjs.org/d3-quadtree/-/d3-quadtree-3.0.1.tgz} engines: {node: '>=12'} d3-random@3.0.1: - resolution: {integrity: sha512-FXMe9GfxTxqd5D6jFsQ+DJ8BJS4E/fT5mqqdjovykEB2oFbTMDVdg1MGFxfQW+FBOGoB++k8swBrgwSHT1cUXQ==} + resolution: {integrity: sha512-FXMe9GfxTxqd5D6jFsQ+DJ8BJS4E/fT5mqqdjovykEB2oFbTMDVdg1MGFxfQW+FBOGoB++k8swBrgwSHT1cUXQ==, tarball: https://registry.npmjs.org/d3-random/-/d3-random-3.0.1.tgz} engines: {node: '>=12'} d3-sankey@0.12.3: - resolution: {integrity: sha512-nQhsBRmM19Ax5xEIPLMY9ZmJ/cDvd1BG3UVvt5h3WRxKg5zGRbvnteTyWAbzeSvlh3tW7ZEmq4VwR5mB3tutmQ==} + resolution: {integrity: sha512-nQhsBRmM19Ax5xEIPLMY9ZmJ/cDvd1BG3UVvt5h3WRxKg5zGRbvnteTyWAbzeSvlh3tW7ZEmq4VwR5mB3tutmQ==, tarball: https://registry.npmjs.org/d3-sankey/-/d3-sankey-0.12.3.tgz} d3-scale-chromatic@3.1.0: - resolution: {integrity: sha512-A3s5PWiZ9YCXFye1o246KoscMWqf8BsD9eRiJ3He7C9OBaxKhAd5TFCdEx/7VbKtxxTsu//1mMJFrEt572cEyQ==} + resolution: {integrity: sha512-A3s5PWiZ9YCXFye1o246KoscMWqf8BsD9eRiJ3He7C9OBaxKhAd5TFCdEx/7VbKtxxTsu//1mMJFrEt572cEyQ==, tarball: https://registry.npmjs.org/d3-scale-chromatic/-/d3-scale-chromatic-3.1.0.tgz} engines: {node: '>=12'} d3-scale@4.0.2: - resolution: {integrity: sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==} + resolution: {integrity: sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==, tarball: https://registry.npmjs.org/d3-scale/-/d3-scale-4.0.2.tgz} engines: {node: '>=12'} d3-selection@3.0.0: - resolution: {integrity: sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==} + resolution: {integrity: sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==, tarball: https://registry.npmjs.org/d3-selection/-/d3-selection-3.0.0.tgz} engines: {node: '>=12'} d3-shape@1.3.7: - resolution: {integrity: sha512-EUkvKjqPFUAZyOlhY5gzCxCeI0Aep04LwIRpsZ/mLFelJiUfnK56jo5JMDSE7yyP2kLSb6LtF+S5chMk7uqPqw==} + resolution: {integrity: sha512-EUkvKjqPFUAZyOlhY5gzCxCeI0Aep04LwIRpsZ/mLFelJiUfnK56jo5JMDSE7yyP2kLSb6LtF+S5chMk7uqPqw==, tarball: https://registry.npmjs.org/d3-shape/-/d3-shape-1.3.7.tgz} d3-shape@3.2.0: - resolution: {integrity: sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==} + resolution: {integrity: sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==, tarball: https://registry.npmjs.org/d3-shape/-/d3-shape-3.2.0.tgz} engines: {node: '>=12'} d3-time-format@4.1.0: - resolution: {integrity: sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==} + resolution: {integrity: sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==, tarball: https://registry.npmjs.org/d3-time-format/-/d3-time-format-4.1.0.tgz} engines: {node: '>=12'} d3-time@3.1.0: - resolution: {integrity: sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==} + resolution: {integrity: sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==, tarball: https://registry.npmjs.org/d3-time/-/d3-time-3.1.0.tgz} engines: {node: '>=12'} d3-timer@3.0.1: - resolution: {integrity: sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==} + resolution: {integrity: sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==, tarball: https://registry.npmjs.org/d3-timer/-/d3-timer-3.0.1.tgz} engines: {node: '>=12'} d3-transition@3.0.1: - resolution: {integrity: sha512-ApKvfjsSR6tg06xrL434C0WydLr7JewBB3V+/39RMHsaXTOG0zmt/OAXeng5M5LBm0ojmxJrpomQVZ1aPvBL4w==} + resolution: {integrity: sha512-ApKvfjsSR6tg06xrL434C0WydLr7JewBB3V+/39RMHsaXTOG0zmt/OAXeng5M5LBm0ojmxJrpomQVZ1aPvBL4w==, tarball: https://registry.npmjs.org/d3-transition/-/d3-transition-3.0.1.tgz} engines: {node: '>=12'} peerDependencies: d3-selection: 2 - 3 d3-zoom@3.0.0: - resolution: {integrity: sha512-b8AmV3kfQaqWAuacbPuNbL6vahnOJflOhexLzMMNLga62+/nh0JzvJ0aO/5a5MVgUFGS7Hu1P9P03o3fJkDCyw==} + resolution: {integrity: sha512-b8AmV3kfQaqWAuacbPuNbL6vahnOJflOhexLzMMNLga62+/nh0JzvJ0aO/5a5MVgUFGS7Hu1P9P03o3fJkDCyw==, tarball: https://registry.npmjs.org/d3-zoom/-/d3-zoom-3.0.0.tgz} engines: {node: '>=12'} d3@7.9.0: - resolution: {integrity: sha512-e1U46jVP+w7Iut8Jt8ri1YsPOvFpg46k+K8TpCb0P+zjCkjkPnV7WzfDJzMHy1LnA+wj5pLT1wjO901gLXeEhA==} + resolution: {integrity: sha512-e1U46jVP+w7Iut8Jt8ri1YsPOvFpg46k+K8TpCb0P+zjCkjkPnV7WzfDJzMHy1LnA+wj5pLT1wjO901gLXeEhA==, tarball: https://registry.npmjs.org/d3/-/d3-7.9.0.tgz} engines: {node: '>=12'} dagre-d3-es@7.0.14: - resolution: {integrity: sha512-P4rFMVq9ESWqmOgK+dlXvOtLwYg0i7u0HBGJER0LZDJT2VHIPAMZ/riPxqJceWMStH5+E61QxFra9kIS3AqdMg==} + resolution: {integrity: sha512-P4rFMVq9ESWqmOgK+dlXvOtLwYg0i7u0HBGJER0LZDJT2VHIPAMZ/riPxqJceWMStH5+E61QxFra9kIS3AqdMg==, tarball: https://registry.npmjs.org/dagre-d3-es/-/dagre-d3-es-7.0.14.tgz} daisyui@5.5.19: - resolution: {integrity: sha512-pbFAkl1VCEh/MPCeclKL61I/MqRIFFhNU7yiXoDDRapXN4/qNCoMxeCCswyxEEhqL5eiTTfwHvucFtOE71C9sA==} + resolution: {integrity: sha512-pbFAkl1VCEh/MPCeclKL61I/MqRIFFhNU7yiXoDDRapXN4/qNCoMxeCCswyxEEhqL5eiTTfwHvucFtOE71C9sA==, tarball: https://registry.npmjs.org/daisyui/-/daisyui-5.5.19.tgz} damerau-levenshtein@1.0.8: - resolution: {integrity: sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==} + resolution: {integrity: sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==, tarball: https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz} data-urls@3.0.2: - resolution: {integrity: sha512-Jy/tj3ldjZJo63sVAvg6LHt2mHvl4V6AgRAmNDtLdm7faqtsx+aJG42rsyCo9JCoRVKwPFzKlIPx3DIibwSIaQ==} + resolution: {integrity: sha512-Jy/tj3ldjZJo63sVAvg6LHt2mHvl4V6AgRAmNDtLdm7faqtsx+aJG42rsyCo9JCoRVKwPFzKlIPx3DIibwSIaQ==, tarball: https://registry.npmjs.org/data-urls/-/data-urls-3.0.2.tgz} engines: {node: '>=12'} data-urls@7.0.0: - resolution: {integrity: sha512-23XHcCF+coGYevirZceTVD7NdJOqVn+49IHyxgszm+JIiHLoB2TkmPtsYkNWT1pvRSGkc35L6NHs0yHkN2SumA==} + resolution: {integrity: sha512-23XHcCF+coGYevirZceTVD7NdJOqVn+49IHyxgszm+JIiHLoB2TkmPtsYkNWT1pvRSGkc35L6NHs0yHkN2SumA==, tarball: https://registry.npmjs.org/data-urls/-/data-urls-7.0.0.tgz} engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} data-view-buffer@1.0.2: - resolution: {integrity: sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ==} + resolution: {integrity: sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ==, tarball: https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.2.tgz} engines: {node: '>= 0.4'} data-view-byte-length@1.0.2: - resolution: {integrity: sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ==} + resolution: {integrity: sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ==, tarball: https://registry.npmjs.org/data-view-byte-length/-/data-view-byte-length-1.0.2.tgz} engines: {node: '>= 0.4'} data-view-byte-offset@1.0.1: - resolution: {integrity: sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ==} + resolution: {integrity: sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ==, tarball: https://registry.npmjs.org/data-view-byte-offset/-/data-view-byte-offset-1.0.1.tgz} engines: {node: '>= 0.4'} date-fns@4.1.0: - resolution: {integrity: sha512-Ukq0owbQXxa/U3EGtsdVBkR1w7KOQ5gIBqdH2hkvknzZPYvBxb/aa6E8L7tmjFtkwZBu3UXBbjIgPo/Ez4xaNg==} + resolution: {integrity: sha512-Ukq0owbQXxa/U3EGtsdVBkR1w7KOQ5gIBqdH2hkvknzZPYvBxb/aa6E8L7tmjFtkwZBu3UXBbjIgPo/Ez4xaNg==, tarball: https://registry.npmjs.org/date-fns/-/date-fns-4.1.0.tgz} date-format@4.0.14: - resolution: {integrity: sha512-39BOQLs9ZjKh0/patS9nrT8wc3ioX3/eA/zgbKNopnF2wCqJEoxywwwElATYvRsXdnOxA/OQeQoFZ3rFjVajhg==} + resolution: {integrity: sha512-39BOQLs9ZjKh0/patS9nrT8wc3ioX3/eA/zgbKNopnF2wCqJEoxywwwElATYvRsXdnOxA/OQeQoFZ3rFjVajhg==, tarball: https://registry.npmjs.org/date-format/-/date-format-4.0.14.tgz} engines: {node: '>=4.0'} dayjs@1.11.21: - resolution: {integrity: sha512-98IT+HOahAisibz/yjKbzuOBwYcjJ7BCLPzARyHiyEBmRz4fatF+KPJszEHXsGYjUG234aH/cOjW1wwTbKUZlA==} + resolution: {integrity: sha512-98IT+HOahAisibz/yjKbzuOBwYcjJ7BCLPzARyHiyEBmRz4fatF+KPJszEHXsGYjUG234aH/cOjW1wwTbKUZlA==, tarball: https://registry.npmjs.org/dayjs/-/dayjs-1.11.21.tgz} de-indent@1.0.2: - resolution: {integrity: sha512-e/1zu3xH5MQryN2zdVaF0OrdNLUbvWxzMbi+iNA6Bky7l1RoP8a2fIbRocyHclXt/arDrrR6lL3TqFD9pMQTsg==} + resolution: {integrity: sha512-e/1zu3xH5MQryN2zdVaF0OrdNLUbvWxzMbi+iNA6Bky7l1RoP8a2fIbRocyHclXt/arDrrR6lL3TqFD9pMQTsg==, tarball: https://registry.npmjs.org/de-indent/-/de-indent-1.0.2.tgz} debug@2.6.9: - resolution: {integrity: sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==} + resolution: {integrity: sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==, tarball: https://registry.npmjs.org/debug/-/debug-2.6.9.tgz} peerDependencies: supports-color: '*' peerDependenciesMeta: @@ -8888,7 +8930,7 @@ packages: optional: true debug@3.2.7: - resolution: {integrity: sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==} + resolution: {integrity: sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==, tarball: https://registry.npmjs.org/debug/-/debug-3.2.7.tgz} peerDependencies: supports-color: '*' peerDependenciesMeta: @@ -8896,7 +8938,7 @@ packages: optional: true debug@4.3.1: - resolution: {integrity: sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==} + resolution: {integrity: sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==, tarball: https://registry.npmjs.org/debug/-/debug-4.3.1.tgz} engines: {node: '>=6.0'} peerDependencies: supports-color: '*' @@ -8905,7 +8947,7 @@ packages: optional: true debug@4.3.7: - resolution: {integrity: sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==} + resolution: {integrity: sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==, tarball: https://registry.npmjs.org/debug/-/debug-4.3.7.tgz} engines: {node: '>=6.0'} peerDependencies: supports-color: '*' @@ -8914,7 +8956,7 @@ packages: optional: true debug@4.4.3: - resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==} + resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==, tarball: https://registry.npmjs.org/debug/-/debug-4.4.3.tgz} engines: {node: '>=6.0'} peerDependencies: supports-color: '*' @@ -8923,10 +8965,10 @@ packages: optional: true decimal.js@10.6.0: - resolution: {integrity: sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==} + resolution: {integrity: sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==, tarball: https://registry.npmjs.org/decimal.js/-/decimal.js-10.6.0.tgz} dedent@1.7.2: - resolution: {integrity: sha512-WzMx3mW98SN+zn3hgemf4OzdmyNhhhKz5Ay0pUfQiMQ3e1g+xmTJWp/pKdwKVXhdSkAEGIIzqeuWrL3mV/AXbA==} + resolution: {integrity: sha512-WzMx3mW98SN+zn3hgemf4OzdmyNhhhKz5Ay0pUfQiMQ3e1g+xmTJWp/pKdwKVXhdSkAEGIIzqeuWrL3mV/AXbA==, tarball: https://registry.npmjs.org/dedent/-/dedent-1.7.2.tgz} peerDependencies: babel-plugin-macros: ^3.1.0 peerDependenciesMeta: @@ -8934,468 +8976,484 @@ packages: optional: true deep-eql@5.0.2: - resolution: {integrity: sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==} + resolution: {integrity: sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==, tarball: https://registry.npmjs.org/deep-eql/-/deep-eql-5.0.2.tgz} engines: {node: '>=6'} deep-equal@1.0.1: - resolution: {integrity: sha512-bHtC0iYvWhyaTzvV3CZgPeZQqCOBGyGsVV7v4eevpdkLHfiSrXUdBG+qAuSz4RI70sszvjQ1QSZ98An1yNwpSw==} + resolution: {integrity: sha512-bHtC0iYvWhyaTzvV3CZgPeZQqCOBGyGsVV7v4eevpdkLHfiSrXUdBG+qAuSz4RI70sszvjQ1QSZ98An1yNwpSw==, tarball: https://registry.npmjs.org/deep-equal/-/deep-equal-1.0.1.tgz} deep-is@0.1.4: - resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} + resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==, tarball: https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz} deepmerge@4.3.1: - resolution: {integrity: sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==} + resolution: {integrity: sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==, tarball: https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz} engines: {node: '>=0.10.0'} default-browser-id@5.0.0: - resolution: {integrity: sha512-A6p/pu/6fyBcA1TRz/GqWYPViplrftcW2gZC9q79ngNCKAeR/X3gcEdXQHl4KNXV+3wgIJ1CPkJQ3IHM6lcsyA==} + resolution: {integrity: sha512-A6p/pu/6fyBcA1TRz/GqWYPViplrftcW2gZC9q79ngNCKAeR/X3gcEdXQHl4KNXV+3wgIJ1CPkJQ3IHM6lcsyA==, tarball: https://registry.npmjs.org/default-browser-id/-/default-browser-id-5.0.0.tgz} engines: {node: '>=18'} default-browser-id@5.0.1: - resolution: {integrity: sha512-x1VCxdX4t+8wVfd1so/9w+vQ4vx7lKd2Qp5tDRutErwmR85OgmfX7RlLRMWafRMY7hbEiXIbudNrjOAPa/hL8Q==} + resolution: {integrity: sha512-x1VCxdX4t+8wVfd1so/9w+vQ4vx7lKd2Qp5tDRutErwmR85OgmfX7RlLRMWafRMY7hbEiXIbudNrjOAPa/hL8Q==, tarball: https://registry.npmjs.org/default-browser-id/-/default-browser-id-5.0.1.tgz} engines: {node: '>=18'} default-browser@5.2.1: - resolution: {integrity: sha512-WY/3TUME0x3KPYdRRxEJJvXRHV4PyPoUsxtZa78lwItwRQRHhd2U9xOscaT/YTf8uCXIAjeJOFBVEh/7FtD8Xg==} + resolution: {integrity: sha512-WY/3TUME0x3KPYdRRxEJJvXRHV4PyPoUsxtZa78lwItwRQRHhd2U9xOscaT/YTf8uCXIAjeJOFBVEh/7FtD8Xg==, tarball: https://registry.npmjs.org/default-browser/-/default-browser-5.2.1.tgz} engines: {node: '>=18'} default-browser@5.5.0: - resolution: {integrity: sha512-H9LMLr5zwIbSxrmvikGuI/5KGhZ8E2zH3stkMgM5LpOWDutGM2JZaj460Udnf1a+946zc7YBgrqEWwbk7zHvGw==} + resolution: {integrity: sha512-H9LMLr5zwIbSxrmvikGuI/5KGhZ8E2zH3stkMgM5LpOWDutGM2JZaj460Udnf1a+946zc7YBgrqEWwbk7zHvGw==, tarball: https://registry.npmjs.org/default-browser/-/default-browser-5.5.0.tgz} engines: {node: '>=18'} defaults@1.0.4: - resolution: {integrity: sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==} + resolution: {integrity: sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==, tarball: https://registry.npmjs.org/defaults/-/defaults-1.0.4.tgz} define-data-property@1.1.4: - resolution: {integrity: sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==} + resolution: {integrity: sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==, tarball: https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz} engines: {node: '>= 0.4'} define-lazy-prop@2.0.0: - resolution: {integrity: sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==} + resolution: {integrity: sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==, tarball: https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz} engines: {node: '>=8'} define-lazy-prop@3.0.0: - resolution: {integrity: sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==} + resolution: {integrity: sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==, tarball: https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-3.0.0.tgz} engines: {node: '>=12'} define-properties@1.2.1: - resolution: {integrity: sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==} + resolution: {integrity: sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==, tarball: https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz} engines: {node: '>= 0.4'} define-property@1.0.0: - resolution: {integrity: sha512-cZTYKFWspt9jZsMscWo8sc/5lbPC9Q0N5nBLgb+Yd915iL3udB1uFgS3B8YCx66UVHq018DAVFoee7x+gxggeA==} + resolution: {integrity: sha512-cZTYKFWspt9jZsMscWo8sc/5lbPC9Q0N5nBLgb+Yd915iL3udB1uFgS3B8YCx66UVHq018DAVFoee7x+gxggeA==, tarball: https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz} engines: {node: '>=0.10.0'} delaunator@5.1.0: - resolution: {integrity: sha512-AGrQ4QSgssa1NGmWmLPqN5NY2KajF5MqxetNEO+o0n3ZwZZeTmt7bBnvzHWrmkZFxGgr4HdyFgelzgi06otLuQ==} + resolution: {integrity: sha512-AGrQ4QSgssa1NGmWmLPqN5NY2KajF5MqxetNEO+o0n3ZwZZeTmt7bBnvzHWrmkZFxGgr4HdyFgelzgi06otLuQ==, tarball: https://registry.npmjs.org/delaunator/-/delaunator-5.1.0.tgz} delayed-stream@1.0.0: - resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} + resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==, tarball: https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz} engines: {node: '>=0.4.0'} delegate@3.2.0: - resolution: {integrity: sha512-IofjkYBZaZivn0V8nnsMJGBr4jVLxHDheKSW88PyxS5QC4Vo9ZbZVvhzlSxY87fVq3STR6r+4cGepyHkcWOQSw==} + resolution: {integrity: sha512-IofjkYBZaZivn0V8nnsMJGBr4jVLxHDheKSW88PyxS5QC4Vo9ZbZVvhzlSxY87fVq3STR6r+4cGepyHkcWOQSw==, tarball: https://registry.npmjs.org/delegate/-/delegate-3.2.0.tgz} delegates@1.0.0: - resolution: {integrity: sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==} + resolution: {integrity: sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==, tarball: https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz} depd@1.1.2: - resolution: {integrity: sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==} + resolution: {integrity: sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==, tarball: https://registry.npmjs.org/depd/-/depd-1.1.2.tgz} engines: {node: '>= 0.6'} depd@2.0.0: - resolution: {integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==} + resolution: {integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==, tarball: https://registry.npmjs.org/depd/-/depd-2.0.0.tgz} engines: {node: '>= 0.8'} dependency-graph@1.0.0: - resolution: {integrity: sha512-cW3gggJ28HZ/LExwxP2B++aiKxhJXMSIt9K48FOXQkm+vuG5gyatXnLsONRJdzO/7VfjDIiaOOa/bs4l464Lwg==} + resolution: {integrity: sha512-cW3gggJ28HZ/LExwxP2B++aiKxhJXMSIt9K48FOXQkm+vuG5gyatXnLsONRJdzO/7VfjDIiaOOa/bs4l464Lwg==, tarball: https://registry.npmjs.org/dependency-graph/-/dependency-graph-1.0.0.tgz} engines: {node: '>=4'} dequal@2.0.3: - resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==} + resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==, tarball: https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz} engines: {node: '>=6'} destroy@1.2.0: - resolution: {integrity: sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==} + resolution: {integrity: sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==, tarball: https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz} engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16} detect-libc@2.1.2: - resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==} + resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==, tarball: https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz} engines: {node: '>=8'} detect-newline@3.1.0: - resolution: {integrity: sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==} + resolution: {integrity: sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==, tarball: https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz} engines: {node: '>=8'} detect-node@2.1.0: - resolution: {integrity: sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==} + resolution: {integrity: sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==, tarball: https://registry.npmjs.org/detect-node/-/detect-node-2.1.0.tgz} detect-port@1.6.1: - resolution: {integrity: sha512-CmnVc+Hek2egPx1PeTFVta2W78xy2K/9Rkf6cC4T59S50tVnzKj+tnx5mmx5lwvCkujZ4uRrpRSuV+IVs3f90Q==} + resolution: {integrity: sha512-CmnVc+Hek2egPx1PeTFVta2W78xy2K/9Rkf6cC4T59S50tVnzKj+tnx5mmx5lwvCkujZ4uRrpRSuV+IVs3f90Q==, tarball: https://registry.npmjs.org/detect-port/-/detect-port-1.6.1.tgz} engines: {node: '>= 4.0.0'} hasBin: true detect-port@2.1.0: - resolution: {integrity: sha512-epZuWb/6Q62L+nDHJc/hQAqf8pylsqgk3BpZXVBx1CDnr3nkrVNn73Uu1rXcFzkNcc+hkP3whuOg7JZYaQB65Q==} + resolution: {integrity: sha512-epZuWb/6Q62L+nDHJc/hQAqf8pylsqgk3BpZXVBx1CDnr3nkrVNn73Uu1rXcFzkNcc+hkP3whuOg7JZYaQB65Q==, tarball: https://registry.npmjs.org/detect-port/-/detect-port-2.1.0.tgz} engines: {node: '>= 16.0.0'} hasBin: true devlop@1.1.0: - resolution: {integrity: sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==} + resolution: {integrity: sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==, tarball: https://registry.npmjs.org/devlop/-/devlop-1.1.0.tgz} diff@4.0.4: - resolution: {integrity: sha512-X07nttJQkwkfKfvTPG/KSnE2OMdcUCao6+eXF3wmnIQRn2aPAHH3VxDbDOdegkd6JbPsXqShpvEOHfAT+nCNwQ==} + resolution: {integrity: sha512-X07nttJQkwkfKfvTPG/KSnE2OMdcUCao6+eXF3wmnIQRn2aPAHH3VxDbDOdegkd6JbPsXqShpvEOHfAT+nCNwQ==, tarball: https://registry.npmjs.org/diff/-/diff-4.0.4.tgz} engines: {node: '>=0.3.1'} diff@8.0.4: - resolution: {integrity: sha512-DPi0FmjiSU5EvQV0++GFDOJ9ASQUVFh5kD+OzOnYdi7n3Wpm9hWWGfB/O2blfHcMVTL5WkQXSnRiK9makhrcnw==} + resolution: {integrity: sha512-DPi0FmjiSU5EvQV0++GFDOJ9ASQUVFh5kD+OzOnYdi7n3Wpm9hWWGfB/O2blfHcMVTL5WkQXSnRiK9makhrcnw==, tarball: https://registry.npmjs.org/diff/-/diff-8.0.4.tgz} engines: {node: '>=0.3.1'} dir-glob@3.0.1: - resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==} + resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==, tarball: https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz} engines: {node: '>=8'} dlv@1.1.3: - resolution: {integrity: sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==} + resolution: {integrity: sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==, tarball: https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz} dns-packet@5.6.1: - resolution: {integrity: sha512-l4gcSouhcgIKRvyy99RNVOgxXiicE+2jZoNmaNmZ6JXiGajBOJAesk1OBlJuM5k2c+eudGdLxDqXuPCKIj6kpw==} + resolution: {integrity: sha512-l4gcSouhcgIKRvyy99RNVOgxXiicE+2jZoNmaNmZ6JXiGajBOJAesk1OBlJuM5k2c+eudGdLxDqXuPCKIj6kpw==, tarball: https://registry.npmjs.org/dns-packet/-/dns-packet-5.6.1.tgz} engines: {node: '>=6'} doctrine@2.1.0: - resolution: {integrity: sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==} + resolution: {integrity: sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==, tarball: https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz} engines: {node: '>=0.10.0'} document-register-element@1.7.2: - resolution: {integrity: sha512-E+y3qJgckbXK/Ev+jPIF+ussxJRaFRIxYCAkxeYcTdccuQoPw5emVWXkox6JnMejFryjlCs/sw2cokMO2lHxjQ==} + resolution: {integrity: sha512-E+y3qJgckbXK/Ev+jPIF+ussxJRaFRIxYCAkxeYcTdccuQoPw5emVWXkox6JnMejFryjlCs/sw2cokMO2lHxjQ==, tarball: https://registry.npmjs.org/document-register-element/-/document-register-element-1.7.2.tgz} deprecated: V0 is gone and the best V1 polyfill is now @ungap/custom-elements dom-accessibility-api@0.5.16: - resolution: {integrity: sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==} + resolution: {integrity: sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==, tarball: https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.5.16.tgz} dom-accessibility-api@0.6.3: - resolution: {integrity: sha512-7ZgogeTnjuHbo+ct10G9Ffp0mif17idi0IyWNVA/wcwcm7NPOD/WEHVP3n7n3MhXqxoIYm8d6MuZohYWIZ4T3w==} + resolution: {integrity: sha512-7ZgogeTnjuHbo+ct10G9Ffp0mif17idi0IyWNVA/wcwcm7NPOD/WEHVP3n7n3MhXqxoIYm8d6MuZohYWIZ4T3w==, tarball: https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.6.3.tgz} dom-autoscroller@2.3.4: - resolution: {integrity: sha512-HcAdt/2Dq9x4CG6LWXc2x9Iq0MJPAu8fuzHncclq7byufqYEYVtx9sZ/dyzR+gdj4qwEC9p27Lw1G2HRRYX6jQ==} + resolution: {integrity: sha512-HcAdt/2Dq9x4CG6LWXc2x9Iq0MJPAu8fuzHncclq7byufqYEYVtx9sZ/dyzR+gdj4qwEC9p27Lw1G2HRRYX6jQ==, tarball: https://registry.npmjs.org/dom-autoscroller/-/dom-autoscroller-2.3.4.tgz} dom-mousemove-dispatcher@1.0.1: - resolution: {integrity: sha512-NMdqqMbgW8kqOdmod2hkS+9hD/v7h4XoSvwU9qqe+wAA/O+ba0jhpbfW0Kb/fCyR0RX9jf4dwfQrl04LQX4FzQ==} + resolution: {integrity: sha512-NMdqqMbgW8kqOdmod2hkS+9hD/v7h4XoSvwU9qqe+wAA/O+ba0jhpbfW0Kb/fCyR0RX9jf4dwfQrl04LQX4FzQ==, tarball: https://registry.npmjs.org/dom-mousemove-dispatcher/-/dom-mousemove-dispatcher-1.0.1.tgz} dom-plane@1.0.2: - resolution: {integrity: sha512-/tR67G6ZGSciXoZLsD706yLxEXvX3mG/OWE8YNYj3A1yU/RAimtPXzklVTu5Y5xoeMoloA/Y+MaNjQm9apgAww==} + resolution: {integrity: sha512-/tR67G6ZGSciXoZLsD706yLxEXvX3mG/OWE8YNYj3A1yU/RAimtPXzklVTu5Y5xoeMoloA/Y+MaNjQm9apgAww==, tarball: https://registry.npmjs.org/dom-plane/-/dom-plane-1.0.2.tgz} dom-serializer@1.4.1: - resolution: {integrity: sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==} + resolution: {integrity: sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==, tarball: https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz} dom-serializer@2.0.0: - resolution: {integrity: sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==} + resolution: {integrity: sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==, tarball: https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz} + + dom-serializer@3.1.1: + resolution: {integrity: sha512-4MEa38/QexBob6gFNwu+EGdWvhJ1OKuNwdYY3Y3NyeWDQfnGeDYQUDfIRzWu5B5gsv03so2Uxd28YC6zrsx3Lw==, tarball: https://registry.npmjs.org/dom-serializer/-/dom-serializer-3.1.1.tgz} + engines: {node: '>=20.19.0'} dom-set@1.1.1: - resolution: {integrity: sha512-sUi2aSvRsK3Ixx++gwX9cnaWk9ZxGVFry8+HnTRVmDimybU5PaiI4wX0o00mVtjFKlQNZLmtGoPTLorYbN0+Rw==} + resolution: {integrity: sha512-sUi2aSvRsK3Ixx++gwX9cnaWk9ZxGVFry8+HnTRVmDimybU5PaiI4wX0o00mVtjFKlQNZLmtGoPTLorYbN0+Rw==, tarball: https://registry.npmjs.org/dom-set/-/dom-set-1.1.1.tgz} domelementtype@2.3.0: - resolution: {integrity: sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==} + resolution: {integrity: sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==, tarball: https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz} + + domelementtype@3.0.0: + resolution: {integrity: sha512-umCQid3jKbDmVjx8jGaW7uUykm4DEUeyV21hPxNMo2nV955DhUThwqyOIDtreepP31hl84X7G5U9ZfsWvIB3Pg==, tarball: https://registry.npmjs.org/domelementtype/-/domelementtype-3.0.0.tgz} + engines: {node: '>=20.19.0'} domexception@4.0.0: - resolution: {integrity: sha512-A2is4PLG+eeSfoTMA95/s4pvAoSo2mKtiM5jlHkAVewmiO8ISFTFKZjH7UAM1Atli/OT/7JHOrJRJiMKUZKYBw==} + resolution: {integrity: sha512-A2is4PLG+eeSfoTMA95/s4pvAoSo2mKtiM5jlHkAVewmiO8ISFTFKZjH7UAM1Atli/OT/7JHOrJRJiMKUZKYBw==, tarball: https://registry.npmjs.org/domexception/-/domexception-4.0.0.tgz} engines: {node: '>=12'} deprecated: Use your platform's native DOMException instead domhandler@4.3.1: - resolution: {integrity: sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==} + resolution: {integrity: sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==, tarball: https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz} engines: {node: '>= 4'} domhandler@5.0.3: - resolution: {integrity: sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==} + resolution: {integrity: sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==, tarball: https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz} engines: {node: '>= 4'} + domhandler@6.0.1: + resolution: {integrity: sha512-gYzvtM72ZtxQO0T048kd6HWSbbGCNOUwcnfQ01cqIJ4X2IYKFFHZ5mKvrQETcFXxsRObZulDaKmy//R7TPtsBg==, tarball: https://registry.npmjs.org/domhandler/-/domhandler-6.0.1.tgz} + engines: {node: '>=20.19.0'} + dompurify@3.4.12: - resolution: {integrity: sha512-zQvGet8Z2sWbQhCmfFz/T5QWH2oBmjnqK3qvOjaqaNLrLEF912WamU+ohnTp0TCep/MFVHpdJuCZEdFOdTnEFg==} + resolution: {integrity: sha512-zQvGet8Z2sWbQhCmfFz/T5QWH2oBmjnqK3qvOjaqaNLrLEF912WamU+ohnTp0TCep/MFVHpdJuCZEdFOdTnEFg==, tarball: https://registry.npmjs.org/dompurify/-/dompurify-3.4.12.tgz} domutils@2.8.0: - resolution: {integrity: sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==} + resolution: {integrity: sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==, tarball: https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz} domutils@3.2.2: - resolution: {integrity: sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==} + resolution: {integrity: sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==, tarball: https://registry.npmjs.org/domutils/-/domutils-3.2.2.tgz} + + domutils@4.0.2: + resolution: {integrity: sha512-qI4JLRKnSzqFqr7hAlS5xQDusBCjKSEG4t4+7aNrIQMHBcsC2TGEhuyABJdYkgSewL57PNLYEiibY2iPKhKpaA==, tarball: https://registry.npmjs.org/domutils/-/domutils-4.0.2.tgz} + engines: {node: '>=20.19.0'} dot-case@3.0.4: - resolution: {integrity: sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==} + resolution: {integrity: sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==, tarball: https://registry.npmjs.org/dot-case/-/dot-case-3.0.4.tgz} dotenv-expand@11.0.7: - resolution: {integrity: sha512-zIHwmZPRshsCdpMDyVsqGmgyP0yT8GAgXUnkdAoJisxvf33k7yO6OuoKmcTGuXPWSsm8Oh88nZicRLA9Y0rUeA==} + resolution: {integrity: sha512-zIHwmZPRshsCdpMDyVsqGmgyP0yT8GAgXUnkdAoJisxvf33k7yO6OuoKmcTGuXPWSsm8Oh88nZicRLA9Y0rUeA==, tarball: https://registry.npmjs.org/dotenv-expand/-/dotenv-expand-11.0.7.tgz} engines: {node: '>=12'} dotenv-expand@12.0.3: - resolution: {integrity: sha512-uc47g4b+4k/M/SeaW1y4OApx+mtLWl92l5LMPP0GNXctZqELk+YGgOPIIC5elYmUH4OuoK3JLhuRUYegeySiFA==} + resolution: {integrity: sha512-uc47g4b+4k/M/SeaW1y4OApx+mtLWl92l5LMPP0GNXctZqELk+YGgOPIIC5elYmUH4OuoK3JLhuRUYegeySiFA==, tarball: https://registry.npmjs.org/dotenv-expand/-/dotenv-expand-12.0.3.tgz} engines: {node: '>=12'} dotenv@16.4.7: - resolution: {integrity: sha512-47qPchRCykZC03FhkYAhrvwU4xDBFIj1QPqaarj6mdM/hgUzfPHcpkHJOn3mJAufFeeAxAzeGsr5X0M4k6fLZQ==} + resolution: {integrity: sha512-47qPchRCykZC03FhkYAhrvwU4xDBFIj1QPqaarj6mdM/hgUzfPHcpkHJOn3mJAufFeeAxAzeGsr5X0M4k6fLZQ==, tarball: https://registry.npmjs.org/dotenv/-/dotenv-16.4.7.tgz} engines: {node: '>=12'} dotenv@16.5.0: - resolution: {integrity: sha512-m/C+AwOAr9/W1UOIZUo232ejMNnJAJtYQjUbHoNTBNTJSvqzzDh7vnrei3o3r3m9blf6ZoDkvcw0VmozNRFJxg==} + resolution: {integrity: sha512-m/C+AwOAr9/W1UOIZUo232ejMNnJAJtYQjUbHoNTBNTJSvqzzDh7vnrei3o3r3m9blf6ZoDkvcw0VmozNRFJxg==, tarball: https://registry.npmjs.org/dotenv/-/dotenv-16.5.0.tgz} engines: {node: '>=12'} dragula@3.7.3: - resolution: {integrity: sha512-/rRg4zRhcpf81TyDhaHLtXt6sEywdfpv1cRUMeFFy7DuypH2U0WUL0GTdyAQvXegviT4PJK4KuMmOaIDpICseQ==} + resolution: {integrity: sha512-/rRg4zRhcpf81TyDhaHLtXt6sEywdfpv1cRUMeFFy7DuypH2U0WUL0GTdyAQvXegviT4PJK4KuMmOaIDpICseQ==, tarball: https://registry.npmjs.org/dragula/-/dragula-3.7.3.tgz} dunder-proto@1.0.1: - resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} + resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==, tarball: https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz} engines: {node: '>= 0.4'} duplexer@0.1.2: - resolution: {integrity: sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==} + resolution: {integrity: sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==, tarball: https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz} eastasianwidth@0.2.0: - resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} + resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==, tarball: https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz} editorconfig@1.0.7: - resolution: {integrity: sha512-e0GOtq/aTQhVdNyDU9e02+wz9oDDM+SIOQxWME2QRjzRX5yyLAuHDE+0aE8vHb9XRC8XD37eO2u57+F09JqFhw==} + resolution: {integrity: sha512-e0GOtq/aTQhVdNyDU9e02+wz9oDDM+SIOQxWME2QRjzRX5yyLAuHDE+0aE8vHb9XRC8XD37eO2u57+F09JqFhw==, tarball: https://registry.npmjs.org/editorconfig/-/editorconfig-1.0.7.tgz} engines: {node: '>=14'} hasBin: true ee-first@1.1.1: - resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==} + resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==, tarball: https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz} ejs@3.1.10: - resolution: {integrity: sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==} + resolution: {integrity: sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==, tarball: https://registry.npmjs.org/ejs/-/ejs-3.1.10.tgz} engines: {node: '>=0.10.0'} hasBin: true ejs@5.0.1: - resolution: {integrity: sha512-COqBPFMxuPTPspXl2DkVYaDS3HtrD1GpzOGkNTJ1IYkifq/r9h8SVEFrjA3D9/VJGOEoMQcrlhpntcSUrM8k6A==} + resolution: {integrity: sha512-COqBPFMxuPTPspXl2DkVYaDS3HtrD1GpzOGkNTJ1IYkifq/r9h8SVEFrjA3D9/VJGOEoMQcrlhpntcSUrM8k6A==, tarball: https://registry.npmjs.org/ejs/-/ejs-5.0.1.tgz} engines: {node: '>=0.12.18'} hasBin: true electron-to-chromium@1.5.389: - resolution: {integrity: sha512-cEto7aeOqBfU1D+c5py5pE+ooscKE75JifxLBdFUZsqAxRS6y7kebtxAZvICszSl05gPjYHDTjY+lXpyGvpJbg==} + resolution: {integrity: sha512-cEto7aeOqBfU1D+c5py5pE+ooscKE75JifxLBdFUZsqAxRS6y7kebtxAZvICszSl05gPjYHDTjY+lXpyGvpJbg==, tarball: https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.389.tgz} email-addresses@5.0.0: - resolution: {integrity: sha512-4OIPYlA6JXqtVn8zpHpGiI7vE6EQOAg16aGnDMIAlZVinnoZ8208tW1hAbjWydgN/4PLTT9q+O1K6AH/vALJGw==} + resolution: {integrity: sha512-4OIPYlA6JXqtVn8zpHpGiI7vE6EQOAg16aGnDMIAlZVinnoZ8208tW1hAbjWydgN/4PLTT9q+O1K6AH/vALJGw==, tarball: https://registry.npmjs.org/email-addresses/-/email-addresses-5.0.0.tgz} emittery@0.13.1: - resolution: {integrity: sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==} + resolution: {integrity: sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==, tarball: https://registry.npmjs.org/emittery/-/emittery-0.13.1.tgz} engines: {node: '>=12'} emoji-mart@5.6.0: - resolution: {integrity: sha512-eJp3QRe79pjwa+duv+n7+5YsNhRcMl812EcFVwrnRvYKoNPoQb5qxU8DG6Bgwji0akHdp6D4Ln6tYLG58MFSow==} + resolution: {integrity: sha512-eJp3QRe79pjwa+duv+n7+5YsNhRcMl812EcFVwrnRvYKoNPoQb5qxU8DG6Bgwji0akHdp6D4Ln6tYLG58MFSow==, tarball: https://registry.npmjs.org/emoji-mart/-/emoji-mart-5.6.0.tgz} emoji-regex@10.6.0: - resolution: {integrity: sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==} + resolution: {integrity: sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==, tarball: https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.6.0.tgz} emoji-regex@8.0.0: - resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} + resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==, tarball: https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz} emoji-regex@9.2.2: - resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} + resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==, tarball: https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz} emoji-toolkit@9.0.1: - resolution: {integrity: sha512-sMMNqKNLVHXJfIKoPbrRJwtYuysVNC9GlKetr72zE3SSVbHqoeDLWVrxP0uM0AE0qvdl3hbUk+tJhhwXZrDHaw==} + resolution: {integrity: sha512-sMMNqKNLVHXJfIKoPbrRJwtYuysVNC9GlKetr72zE3SSVbHqoeDLWVrxP0uM0AE0qvdl3hbUk+tJhhwXZrDHaw==, tarball: https://registry.npmjs.org/emoji-toolkit/-/emoji-toolkit-9.0.1.tgz} emojibase-data@17.0.0: - resolution: {integrity: sha512-Yvgb5AWoHViHV/gq1qr5ZAarcBip+B27/ZLRsUJkbgAEaLlZ/fof9g882LTpmEpyhBNEC0m2SEmItljHsTygjA==} + resolution: {integrity: sha512-Yvgb5AWoHViHV/gq1qr5ZAarcBip+B27/ZLRsUJkbgAEaLlZ/fof9g882LTpmEpyhBNEC0m2SEmItljHsTygjA==, tarball: https://registry.npmjs.org/emojibase-data/-/emojibase-data-17.0.0.tgz} peerDependencies: emojibase: '*' emojibase@17.0.0: - resolution: {integrity: sha512-bXdpf4HPY3p41zK5swVKZdC/VynsMZ4LoLxdYDE+GucqkFwzcM1GVc4ODfYAlwoKaf2U2oNNUoOO78N96ovpBA==} + resolution: {integrity: sha512-bXdpf4HPY3p41zK5swVKZdC/VynsMZ4LoLxdYDE+GucqkFwzcM1GVc4ODfYAlwoKaf2U2oNNUoOO78N96ovpBA==, tarball: https://registry.npmjs.org/emojibase/-/emojibase-17.0.0.tgz} engines: {node: '>=18.12.0'} emojis-list@3.0.0: - resolution: {integrity: sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==} + resolution: {integrity: sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==, tarball: https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz} engines: {node: '>= 4'} empathic@2.0.0: - resolution: {integrity: sha512-i6UzDscO/XfAcNYD75CfICkmfLedpyPDdozrLMmQc5ORaQcdMoc21OnlEylMIqI7U8eniKrPMxxtj8k0vhmJhA==} + resolution: {integrity: sha512-i6UzDscO/XfAcNYD75CfICkmfLedpyPDdozrLMmQc5ORaQcdMoc21OnlEylMIqI7U8eniKrPMxxtj8k0vhmJhA==, tarball: https://registry.npmjs.org/empathic/-/empathic-2.0.0.tgz} engines: {node: '>=14'} empathic@2.0.1: - resolution: {integrity: sha512-YGRs8knHhKHVShLkFET/rWAU8kmHbOV5LwN938RHI0pljAJ1Gf6SzXsSmRaEzcXTtOOmVqJ5+WtQPL5uigY50Q==} + resolution: {integrity: sha512-YGRs8knHhKHVShLkFET/rWAU8kmHbOV5LwN938RHI0pljAJ1Gf6SzXsSmRaEzcXTtOOmVqJ5+WtQPL5uigY50Q==, tarball: https://registry.npmjs.org/empathic/-/empathic-2.0.1.tgz} engines: {node: '>=14'} encodeurl@2.0.0: - resolution: {integrity: sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==} + resolution: {integrity: sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==, tarball: https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz} engines: {node: '>= 0.8'} encoding@0.1.13: - resolution: {integrity: sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==} + resolution: {integrity: sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==, tarball: https://registry.npmjs.org/encoding/-/encoding-0.1.13.tgz} end-of-stream@1.4.5: - resolution: {integrity: sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==} + resolution: {integrity: sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==, tarball: https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.5.tgz} enhanced-resolve@5.24.2: - resolution: {integrity: sha512-rpsZEGT1jFuve6QlpyRp9ckQ+kN61hvF9BzCPyMdaKTm8UJce96KBn3sorXOFXlzjPrs3Vc4T1NsSroZ3PxlFw==} + resolution: {integrity: sha512-rpsZEGT1jFuve6QlpyRp9ckQ+kN61hvF9BzCPyMdaKTm8UJce96KBn3sorXOFXlzjPrs3Vc4T1NsSroZ3PxlFw==, tarball: https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.24.2.tgz} engines: {node: '>=10.13.0'} enhanced-resolve@5.24.5: - resolution: {integrity: sha512-L1l8TNvomm6UVW5B253AGxQagSQr+vGwhMlrrfRS2qmhx46AMpMVJKQYLvWYbysTMY8VoicOvzHzoHMbyzB+4A==} + resolution: {integrity: sha512-L1l8TNvomm6UVW5B253AGxQagSQr+vGwhMlrrfRS2qmhx46AMpMVJKQYLvWYbysTMY8VoicOvzHzoHMbyzB+4A==, tarball: https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.24.5.tgz} engines: {node: '>=10.13.0'} enquirer@2.3.6: - resolution: {integrity: sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==} + resolution: {integrity: sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==, tarball: https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz} engines: {node: '>=8.6'} entities@2.2.0: - resolution: {integrity: sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==} + resolution: {integrity: sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==, tarball: https://registry.npmjs.org/entities/-/entities-2.2.0.tgz} entities@4.5.0: - resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==} + resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==, tarball: https://registry.npmjs.org/entities/-/entities-4.5.0.tgz} engines: {node: '>=0.12'} entities@6.0.1: - resolution: {integrity: sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==} + resolution: {integrity: sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==, tarball: https://registry.npmjs.org/entities/-/entities-6.0.1.tgz} engines: {node: '>=0.12'} entities@7.0.1: - resolution: {integrity: sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA==} + resolution: {integrity: sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA==, tarball: https://registry.npmjs.org/entities/-/entities-7.0.1.tgz} engines: {node: '>=0.12'} entities@8.0.0: - resolution: {integrity: sha512-zwfzJecQ/Uej6tusMqwAqU/6KL2XaB2VZ2Jg54Je6ahNBGNH6Ek6g3jjNCF0fG9EWQKGZNddNjU5F1ZQn/sBnA==} + resolution: {integrity: sha512-zwfzJecQ/Uej6tusMqwAqU/6KL2XaB2VZ2Jg54Je6ahNBGNH6Ek6g3jjNCF0fG9EWQKGZNddNjU5F1ZQn/sBnA==, tarball: https://registry.npmjs.org/entities/-/entities-8.0.0.tgz} engines: {node: '>=20.19.0'} env-paths@2.2.1: - resolution: {integrity: sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==} + resolution: {integrity: sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==, tarball: https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz} engines: {node: '>=6'} environment@1.1.0: - resolution: {integrity: sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q==} + resolution: {integrity: sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q==, tarball: https://registry.npmjs.org/environment/-/environment-1.1.0.tgz} engines: {node: '>=18'} errno@0.1.8: - resolution: {integrity: sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A==} + resolution: {integrity: sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A==, tarball: https://registry.npmjs.org/errno/-/errno-0.1.8.tgz} hasBin: true error-ex@1.3.4: - resolution: {integrity: sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==} + resolution: {integrity: sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==, tarball: https://registry.npmjs.org/error-ex/-/error-ex-1.3.4.tgz} error-stack-parser@2.1.4: - resolution: {integrity: sha512-Sk5V6wVazPhq5MhpO+AUxJn5x7XSXGl1R93Vn7i+zS15KDVxQijejNCrz8340/2bgLBjR9GtEG8ZVKONDjcqGQ==} + resolution: {integrity: sha512-Sk5V6wVazPhq5MhpO+AUxJn5x7XSXGl1R93Vn7i+zS15KDVxQijejNCrz8340/2bgLBjR9GtEG8ZVKONDjcqGQ==, tarball: https://registry.npmjs.org/error-stack-parser/-/error-stack-parser-2.1.4.tgz} es-abstract-get@1.0.0: - resolution: {integrity: sha512-6PMWXpdhshVvFp+FoWYs1EvG1Nj0tvk0dZM+XcK0xMEM1czRVcP6ohqPWHy6qPagSpC8j4+p89WXlT+xXJs/fg==} + resolution: {integrity: sha512-6PMWXpdhshVvFp+FoWYs1EvG1Nj0tvk0dZM+XcK0xMEM1czRVcP6ohqPWHy6qPagSpC8j4+p89WXlT+xXJs/fg==, tarball: https://registry.npmjs.org/es-abstract-get/-/es-abstract-get-1.0.0.tgz} engines: {node: '>= 0.4'} es-abstract@1.24.2: - resolution: {integrity: sha512-2FpH9Q5i2RRwyEP1AylXe6nYLR5OhaJTZwmlcP0dL/+JCbgg7yyEo/sEK6HeGZRf3dFpWwThaRHVApXSkW3xeg==} + resolution: {integrity: sha512-2FpH9Q5i2RRwyEP1AylXe6nYLR5OhaJTZwmlcP0dL/+JCbgg7yyEo/sEK6HeGZRf3dFpWwThaRHVApXSkW3xeg==, tarball: https://registry.npmjs.org/es-abstract/-/es-abstract-1.24.2.tgz} engines: {node: '>= 0.4'} es-define-property@1.0.1: - resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==} + resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==, tarball: https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz} engines: {node: '>= 0.4'} es-errors@1.3.0: - resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} + resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==, tarball: https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz} engines: {node: '>= 0.4'} es-iterator-helpers@1.4.0: - resolution: {integrity: sha512-c/A0P0oxkACDc+cKWw8evLXK83oBKgn0qPOqCYT4x9uolpCIJAcYvJC9QYKNDRPsTeGyCrQ326jrvgZWdCdK5Q==} + resolution: {integrity: sha512-c/A0P0oxkACDc+cKWw8evLXK83oBKgn0qPOqCYT4x9uolpCIJAcYvJC9QYKNDRPsTeGyCrQ326jrvgZWdCdK5Q==, tarball: https://registry.npmjs.org/es-iterator-helpers/-/es-iterator-helpers-1.4.0.tgz} engines: {node: '>= 0.4'} es-module-lexer@0.9.3: - resolution: {integrity: sha512-1HQ2M2sPtxwnvOvT1ZClHyQDiggdNjURWpY2we6aMKCQiUVxTmVs2UYPLIrD84sS+kMdUwfBSylbJPwNnBrnHQ==} + resolution: {integrity: sha512-1HQ2M2sPtxwnvOvT1ZClHyQDiggdNjURWpY2we6aMKCQiUVxTmVs2UYPLIrD84sS+kMdUwfBSylbJPwNnBrnHQ==, tarball: https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-0.9.3.tgz} es-module-lexer@1.7.0: - resolution: {integrity: sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==} + resolution: {integrity: sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==, tarball: https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.7.0.tgz} es-module-lexer@2.3.1: - resolution: {integrity: sha512-shc1dbU90Yl/xq1QrC7QRtfcwURZuVRfPhZbDoldJ1cn1gzDvBaBWlv0eFolj5+0znnPJz5TXLxsN77X/12KTA==} + resolution: {integrity: sha512-shc1dbU90Yl/xq1QrC7QRtfcwURZuVRfPhZbDoldJ1cn1gzDvBaBWlv0eFolj5+0znnPJz5TXLxsN77X/12KTA==, tarball: https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-2.3.1.tgz} es-object-atoms@1.1.1: - resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==} + resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==, tarball: https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz} engines: {node: '>= 0.4'} es-object-atoms@1.1.2: - resolution: {integrity: sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw==} + resolution: {integrity: sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw==, tarball: https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.2.tgz} engines: {node: '>= 0.4'} es-set-tostringtag@2.1.0: - resolution: {integrity: sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==} + resolution: {integrity: sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==, tarball: https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz} engines: {node: '>= 0.4'} es-shim-unscopables@1.1.0: - resolution: {integrity: sha512-d9T8ucsEhh8Bi1woXCf+TIKDIROLG5WCkxg8geBCbvk22kzwC5G2OnXVMO6FUsvQlgUUXQ2itephWDLqDzbeCw==} + resolution: {integrity: sha512-d9T8ucsEhh8Bi1woXCf+TIKDIROLG5WCkxg8geBCbvk22kzwC5G2OnXVMO6FUsvQlgUUXQ2itephWDLqDzbeCw==, tarball: https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.1.0.tgz} engines: {node: '>= 0.4'} es-to-primitive@1.3.4: - resolution: {integrity: sha512-yPDz7wqpg1/mmHLmS3tcfTfbw5f1eryXvyghYBffGdERwe+mV7ZcWzTR8LR17Kvqt3qfPurjlonmnq3MKXIOXw==} + resolution: {integrity: sha512-yPDz7wqpg1/mmHLmS3tcfTfbw5f1eryXvyghYBffGdERwe+mV7ZcWzTR8LR17Kvqt3qfPurjlonmnq3MKXIOXw==, tarball: https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.3.4.tgz} engines: {node: '>= 0.4'} es-toolkit@1.49.0: - resolution: {integrity: sha512-G5iZ6Pc/FNRY/soKZHC+TxGDD83rHUDXxzaWhGCX44vAv/tMs56WMusnm/KMNK+luUPsgA9U28cGr4RDlSzL2g==} + resolution: {integrity: sha512-G5iZ6Pc/FNRY/soKZHC+TxGDD83rHUDXxzaWhGCX44vAv/tMs56WMusnm/KMNK+luUPsgA9U28cGr4RDlSzL2g==, tarball: https://registry.npmjs.org/es-toolkit/-/es-toolkit-1.49.0.tgz} esbuild-register@3.6.0: - resolution: {integrity: sha512-H2/S7Pm8a9CL1uhp9OvjwrBh5Pvx0H8qVOxNu8Wed9Y7qv56MPtq+GGM8RJpq6glYJn9Wspr8uw7l55uyinNeg==} + resolution: {integrity: sha512-H2/S7Pm8a9CL1uhp9OvjwrBh5Pvx0H8qVOxNu8Wed9Y7qv56MPtq+GGM8RJpq6glYJn9Wspr8uw7l55uyinNeg==, tarball: https://registry.npmjs.org/esbuild-register/-/esbuild-register-3.6.0.tgz} peerDependencies: esbuild: '>=0.12 <1' esbuild-wasm@0.28.1: - resolution: {integrity: sha512-p/GD4E8oYRjg3kjdKrnMb0s4PzXgJF42e0MF4H0+ACyK/kIlFRp3e0fzOleIG+wBBm6MM3XQrbpe7soEA+vJIA==} + resolution: {integrity: sha512-p/GD4E8oYRjg3kjdKrnMb0s4PzXgJF42e0MF4H0+ACyK/kIlFRp3e0fzOleIG+wBBm6MM3XQrbpe7soEA+vJIA==, tarball: https://registry.npmjs.org/esbuild-wasm/-/esbuild-wasm-0.28.1.tgz} engines: {node: '>=18'} hasBin: true esbuild@0.19.2: - resolution: {integrity: sha512-G6hPax8UbFakEj3hWO0Vs52LQ8k3lnBhxZWomUJDxfz3rZTLqF5k/FCzuNdLx2RbpBiQQF9H9onlDDH1lZsnjg==} + resolution: {integrity: sha512-G6hPax8UbFakEj3hWO0Vs52LQ8k3lnBhxZWomUJDxfz3rZTLqF5k/FCzuNdLx2RbpBiQQF9H9onlDDH1lZsnjg==, tarball: https://registry.npmjs.org/esbuild/-/esbuild-0.19.2.tgz} engines: {node: '>=12'} hasBin: true esbuild@0.23.1: - resolution: {integrity: sha512-VVNz/9Sa0bs5SELtn3f7qhJCDPCF5oMEl5cO9/SSinpE9hbPVvxbd572HH5AKiP7WD8INO53GgfDDhRjkylHEg==} + resolution: {integrity: sha512-VVNz/9Sa0bs5SELtn3f7qhJCDPCF5oMEl5cO9/SSinpE9hbPVvxbd572HH5AKiP7WD8INO53GgfDDhRjkylHEg==, tarball: https://registry.npmjs.org/esbuild/-/esbuild-0.23.1.tgz} engines: {node: '>=18'} hasBin: true esbuild@0.25.5: - resolution: {integrity: sha512-P8OtKZRv/5J5hhz0cUAdu/cLuPIKXpQl1R9pZtvmHWQvrAUVd0UNIPT4IB4W3rNOqVO0rlqHmCIbSwxh/c9yUQ==} + resolution: {integrity: sha512-P8OtKZRv/5J5hhz0cUAdu/cLuPIKXpQl1R9pZtvmHWQvrAUVd0UNIPT4IB4W3rNOqVO0rlqHmCIbSwxh/c9yUQ==, tarball: https://registry.npmjs.org/esbuild/-/esbuild-0.25.5.tgz} engines: {node: '>=18'} hasBin: true esbuild@0.27.7: - resolution: {integrity: sha512-IxpibTjyVnmrIQo5aqNpCgoACA/dTKLTlhMHihVHhdkxKyPO1uBBthumT0rdHmcsk9uMonIWS0m4FljWzILh3w==} + resolution: {integrity: sha512-IxpibTjyVnmrIQo5aqNpCgoACA/dTKLTlhMHihVHhdkxKyPO1uBBthumT0rdHmcsk9uMonIWS0m4FljWzILh3w==, tarball: https://registry.npmjs.org/esbuild/-/esbuild-0.27.7.tgz} engines: {node: '>=18'} hasBin: true esbuild@0.28.1: - resolution: {integrity: sha512-HrJrvZv5ayxBzPfwphOoNzkzOIIlifzk0KJrGK2c8R4+LKpMtpYLQeUdjnwjWv/LZlkH2laZk+4w78pi99D4Vw==} + resolution: {integrity: sha512-HrJrvZv5ayxBzPfwphOoNzkzOIIlifzk0KJrGK2c8R4+LKpMtpYLQeUdjnwjWv/LZlkH2laZk+4w78pi99D4Vw==, tarball: https://registry.npmjs.org/esbuild/-/esbuild-0.28.1.tgz} engines: {node: '>=18'} hasBin: true escalade@3.2.0: - resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} + resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==, tarball: https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz} engines: {node: '>=6'} escape-html@1.0.3: - resolution: {integrity: sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==} + resolution: {integrity: sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==, tarball: https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz} escape-string-regexp@1.0.5: - resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==} + resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==, tarball: https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz} engines: {node: '>=0.8.0'} escape-string-regexp@2.0.0: - resolution: {integrity: sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==} + resolution: {integrity: sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==, tarball: https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz} engines: {node: '>=8'} escape-string-regexp@4.0.0: - resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} + resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==, tarball: https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz} engines: {node: '>=10'} escodegen@2.1.0: - resolution: {integrity: sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==} + resolution: {integrity: sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==, tarball: https://registry.npmjs.org/escodegen/-/escodegen-2.1.0.tgz} engines: {node: '>=6.0'} hasBin: true eslint-config-prettier@10.1.8: - resolution: {integrity: sha512-82GZUjRS0p/jganf6q1rEO25VSoHH0hKPCTrgillPjdI/3bgBhAE1QzHrHTizjpRvy6pGAvKjDJtk2pF9NDq8w==} + resolution: {integrity: sha512-82GZUjRS0p/jganf6q1rEO25VSoHH0hKPCTrgillPjdI/3bgBhAE1QzHrHTizjpRvy6pGAvKjDJtk2pF9NDq8w==, tarball: https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-10.1.8.tgz} hasBin: true peerDependencies: eslint: '>=7.0.0' eslint-import-resolver-node@0.3.10: - resolution: {integrity: sha512-tRrKqFyCaKict5hOd244sL6EQFNycnMQnBe+j8uqGNXYzsImGbGUU4ibtoaBmv5FLwJwcFJNeg1GeVjQfbMrDQ==} + resolution: {integrity: sha512-tRrKqFyCaKict5hOd244sL6EQFNycnMQnBe+j8uqGNXYzsImGbGUU4ibtoaBmv5FLwJwcFJNeg1GeVjQfbMrDQ==, tarball: https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.10.tgz} eslint-module-utils@2.14.0: - resolution: {integrity: sha512-W2WCRZ9Dqntd+2u8jJcVMV2PKulc6RdLgUUoh/yQr3uB6lo/ZOeGx11sv60/8S4QFFKNslAlWhr9u0Ef7ZW6Ig==} + resolution: {integrity: sha512-W2WCRZ9Dqntd+2u8jJcVMV2PKulc6RdLgUUoh/yQr3uB6lo/ZOeGx11sv60/8S4QFFKNslAlWhr9u0Ef7ZW6Ig==, tarball: https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.14.0.tgz} engines: {node: '>=4'} peerDependencies: '@typescript-eslint/parser': '*' @@ -9416,11 +9474,11 @@ packages: optional: true eslint-plugin-ban@1.6.0: - resolution: {integrity: sha512-gZptoV+SFHOHO57/5lmPvizMvSXrjFatP9qlVQf3meL/WHo9TxSoERygrMlESl19CPh95U86asTxohT8OprwDw==} + resolution: {integrity: sha512-gZptoV+SFHOHO57/5lmPvizMvSXrjFatP9qlVQf3meL/WHo9TxSoERygrMlESl19CPh95U86asTxohT8OprwDw==, tarball: https://registry.npmjs.org/eslint-plugin-ban/-/eslint-plugin-ban-1.6.0.tgz} engines: {node: '>=0.10.0'} eslint-plugin-better-tailwindcss@4.6.1: - resolution: {integrity: sha512-Lr8mPyuaZ+dS6ATuJaPwcOFOpOUsRBs5TXyOPqiTzLy0SvK4+0G6usbklCuQn4QabwFtNKdSXwl2WxOIPvu0lQ==} + resolution: {integrity: sha512-Lr8mPyuaZ+dS6ATuJaPwcOFOpOUsRBs5TXyOPqiTzLy0SvK4+0G6usbklCuQn4QabwFtNKdSXwl2WxOIPvu0lQ==, tarball: https://registry.npmjs.org/eslint-plugin-better-tailwindcss/-/eslint-plugin-better-tailwindcss-4.6.1.tgz} engines: {node: ^20.19.0 || ^22.12.0 || >=23.0.0} peerDependencies: eslint: ^7.0.0 || ^8.0.0 || ^9.0.0 || ^10.0.0 @@ -9433,7 +9491,7 @@ packages: optional: true eslint-plugin-import@2.31.0: - resolution: {integrity: sha512-ixmkI62Rbc2/w8Vfxyh1jQRTdRTF52VxwRVHl/ykPAmqG+Nb7/kNn+byLP0LxPgI7zWA16Jt82SybJInmMia3A==} + resolution: {integrity: sha512-ixmkI62Rbc2/w8Vfxyh1jQRTdRTF52VxwRVHl/ykPAmqG+Nb7/kNn+byLP0LxPgI7zWA16Jt82SybJInmMia3A==, tarball: https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.31.0.tgz} engines: {node: '>=4'} peerDependencies: '@typescript-eslint/parser': '*' @@ -9443,19 +9501,19 @@ packages: optional: true eslint-plugin-jsx-a11y@6.10.2: - resolution: {integrity: sha512-scB3nz4WmG75pV8+3eRUQOHZlNSUhFNq37xnpgRkCCELU3XMvXAxLk1eqWWyE22Ki4Q01Fnsw9BA3cJHDPgn2Q==} + resolution: {integrity: sha512-scB3nz4WmG75pV8+3eRUQOHZlNSUhFNq37xnpgRkCCELU3XMvXAxLk1eqWWyE22Ki4Q01Fnsw9BA3cJHDPgn2Q==, tarball: https://registry.npmjs.org/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.10.2.tgz} engines: {node: '>=4.0'} peerDependencies: eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9 eslint-plugin-playwright@2.9.0: - resolution: {integrity: sha512-k3xrG6YzrallWNFMoGUjMNeu3SFFKXN79KJQBD2PkM4PasJegqV2Up+mPY5od2UmPKQGT+MeIhCmWH8r5eYuQQ==} + resolution: {integrity: sha512-k3xrG6YzrallWNFMoGUjMNeu3SFFKXN79KJQBD2PkM4PasJegqV2Up+mPY5od2UmPKQGT+MeIhCmWH8r5eYuQQ==, tarball: https://registry.npmjs.org/eslint-plugin-playwright/-/eslint-plugin-playwright-2.9.0.tgz} engines: {node: '>=16.9.0'} peerDependencies: eslint: '>=8.40.0' eslint-plugin-prettier@5.5.6: - resolution: {integrity: sha512-ifetmTcxWfz+4qRW3pH/ujdTq2jQIj59AxJMIN26K5avYgU8dxycUETQonWiW+wPrYXA0j3Try0l1CnwVQtDqQ==} + resolution: {integrity: sha512-ifetmTcxWfz+4qRW3pH/ujdTq2jQIj59AxJMIN26K5avYgU8dxycUETQonWiW+wPrYXA0j3Try0l1CnwVQtDqQ==, tarball: https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.5.6.tgz} engines: {node: ^14.18.0 || >=16.0.0} peerDependencies: '@types/eslint': '>=8.0.0' @@ -9469,53 +9527,53 @@ packages: optional: true eslint-plugin-react-hooks@5.0.0: - resolution: {integrity: sha512-hIOwI+5hYGpJEc4uPRmz2ulCjAGD/N13Lukkh8cLV0i2IRk/bdZDYjgLVHj+U9Z704kLIdIO6iueGvxNur0sgw==} + resolution: {integrity: sha512-hIOwI+5hYGpJEc4uPRmz2ulCjAGD/N13Lukkh8cLV0i2IRk/bdZDYjgLVHj+U9Z704kLIdIO6iueGvxNur0sgw==, tarball: https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-5.0.0.tgz} engines: {node: '>=10'} peerDependencies: eslint: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0 eslint-plugin-react@7.37.5: - resolution: {integrity: sha512-Qteup0SqU15kdocexFNAJMvCJEfa2xUKNV4CC1xsVMrIIqEy3SQ/rqyxCWNzfrd3/ldy6HMlD2e0JDVpDg2qIA==} + resolution: {integrity: sha512-Qteup0SqU15kdocexFNAJMvCJEfa2xUKNV4CC1xsVMrIIqEy3SQ/rqyxCWNzfrd3/ldy6HMlD2e0JDVpDg2qIA==, tarball: https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.37.5.tgz} engines: {node: '>=4'} peerDependencies: eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9.7 eslint-plugin-vue@9.33.0: - resolution: {integrity: sha512-174lJKuNsuDIlLpjeXc5E2Tss8P44uIimAfGD0b90k0NoirJqpG7stLuU9Vp/9ioTOrQdWVREc4mRd1BD+CvGw==} + resolution: {integrity: sha512-174lJKuNsuDIlLpjeXc5E2Tss8P44uIimAfGD0b90k0NoirJqpG7stLuU9Vp/9ioTOrQdWVREc4mRd1BD+CvGw==, tarball: https://registry.npmjs.org/eslint-plugin-vue/-/eslint-plugin-vue-9.33.0.tgz} engines: {node: ^14.17.0 || >=16.0.0} peerDependencies: eslint: ^6.2.0 || ^7.0.0 || ^8.0.0 || ^9.0.0 eslint-scope@5.1.1: - resolution: {integrity: sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==} + resolution: {integrity: sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==, tarball: https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz} engines: {node: '>=8.0.0'} eslint-scope@7.2.2: - resolution: {integrity: sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==} + resolution: {integrity: sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==, tarball: https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} eslint-scope@8.4.0: - resolution: {integrity: sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==} + resolution: {integrity: sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==, tarball: https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} eslint-scope@9.1.2: - resolution: {integrity: sha512-xS90H51cKw0jltxmvmHy2Iai1LIqrfbw57b79w/J7MfvDfkIkFZ+kj6zC3BjtUwh150HsSSdxXZcsuv72miDFQ==} + resolution: {integrity: sha512-xS90H51cKw0jltxmvmHy2Iai1LIqrfbw57b79w/J7MfvDfkIkFZ+kj6zC3BjtUwh150HsSSdxXZcsuv72miDFQ==, tarball: https://registry.npmjs.org/eslint-scope/-/eslint-scope-9.1.2.tgz} engines: {node: ^20.19.0 || ^22.13.0 || >=24} eslint-visitor-keys@3.4.3: - resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==} + resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==, tarball: https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} eslint-visitor-keys@4.2.1: - resolution: {integrity: sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==} + resolution: {integrity: sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==, tarball: https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} eslint-visitor-keys@5.0.1: - resolution: {integrity: sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==} + resolution: {integrity: sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==, tarball: https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz} engines: {node: ^20.19.0 || ^22.13.0 || >=24} eslint@9.39.4: - resolution: {integrity: sha512-XoMjdBOwe/esVgEvLmNsD3IRHkm7fbKIUGvrleloJXUZgDHig2IPWNniv+GwjyJXzuNqVjlr5+4yVUZjycJwfQ==} + resolution: {integrity: sha512-XoMjdBOwe/esVgEvLmNsD3IRHkm7fbKIUGvrleloJXUZgDHig2IPWNniv+GwjyJXzuNqVjlr5+4yVUZjycJwfQ==, tarball: https://registry.npmjs.org/eslint/-/eslint-9.39.4.tgz} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} hasBin: true peerDependencies: @@ -9525,169 +9583,169 @@ packages: optional: true espree@10.4.0: - resolution: {integrity: sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==} + resolution: {integrity: sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==, tarball: https://registry.npmjs.org/espree/-/espree-10.4.0.tgz} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} espree@11.2.0: - resolution: {integrity: sha512-7p3DrVEIopW1B1avAGLuCSh1jubc01H2JHc8B4qqGblmg5gI9yumBgACjWo4JlIc04ufug4xJ3SQI8HkS/Rgzw==} + resolution: {integrity: sha512-7p3DrVEIopW1B1avAGLuCSh1jubc01H2JHc8B4qqGblmg5gI9yumBgACjWo4JlIc04ufug4xJ3SQI8HkS/Rgzw==, tarball: https://registry.npmjs.org/espree/-/espree-11.2.0.tgz} engines: {node: ^20.19.0 || ^22.13.0 || >=24} espree@9.6.1: - resolution: {integrity: sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==} + resolution: {integrity: sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==, tarball: https://registry.npmjs.org/espree/-/espree-9.6.1.tgz} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} esprima@4.0.1: - resolution: {integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==} + resolution: {integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==, tarball: https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz} engines: {node: '>=4'} hasBin: true esquery@1.7.0: - resolution: {integrity: sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==} + resolution: {integrity: sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==, tarball: https://registry.npmjs.org/esquery/-/esquery-1.7.0.tgz} engines: {node: '>=0.10'} esrecurse@4.3.0: - resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==} + resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==, tarball: https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz} engines: {node: '>=4.0'} estraverse@4.3.0: - resolution: {integrity: sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==} + resolution: {integrity: sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==, tarball: https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz} engines: {node: '>=4.0'} estraverse@5.3.0: - resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} + resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==, tarball: https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz} engines: {node: '>=4.0'} estree-walker@0.6.1: - resolution: {integrity: sha512-SqmZANLWS0mnatqbSfRP5g8OXZC12Fgg1IwNtLsyHDzJizORW4khDfjPqJZsemPWBB2uqykUah5YpQ6epsqC/w==} + resolution: {integrity: sha512-SqmZANLWS0mnatqbSfRP5g8OXZC12Fgg1IwNtLsyHDzJizORW4khDfjPqJZsemPWBB2uqykUah5YpQ6epsqC/w==, tarball: https://registry.npmjs.org/estree-walker/-/estree-walker-0.6.1.tgz} estree-walker@2.0.2: - resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==} + resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==, tarball: https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz} estree-walker@3.0.3: - resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==} + resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==, tarball: https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz} esutils@2.0.3: - resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} + resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==, tarball: https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz} engines: {node: '>=0.10.0'} etag@1.8.1: - resolution: {integrity: sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==} + resolution: {integrity: sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==, tarball: https://registry.npmjs.org/etag/-/etag-1.8.1.tgz} engines: {node: '>= 0.6'} eventemitter3@4.0.7: - resolution: {integrity: sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==} + resolution: {integrity: sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==, tarball: https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz} eventemitter3@5.0.4: - resolution: {integrity: sha512-mlsTRyGaPBjPedk6Bvw+aqbsXDtoAyAzm5MO7JgU+yVRyMQ5O8bD4Kcci7BS85f93veegeCPkL8R4GLClnjLFw==} + resolution: {integrity: sha512-mlsTRyGaPBjPedk6Bvw+aqbsXDtoAyAzm5MO7JgU+yVRyMQ5O8bD4Kcci7BS85f93veegeCPkL8R4GLClnjLFw==, tarball: https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.4.tgz} events@3.3.0: - resolution: {integrity: sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==} + resolution: {integrity: sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==, tarball: https://registry.npmjs.org/events/-/events-3.3.0.tgz} engines: {node: '>=0.8.x'} eventsource-parser@3.1.0: - resolution: {integrity: sha512-kJezFj9YFAMLeORyi7aCLxLbD5/qWMQnoMVlVPyHIll7lgRJCc3JVln9Vgl9nwQi0YkMnhdGTMNn7CkRRAptMg==} + resolution: {integrity: sha512-kJezFj9YFAMLeORyi7aCLxLbD5/qWMQnoMVlVPyHIll7lgRJCc3JVln9Vgl9nwQi0YkMnhdGTMNn7CkRRAptMg==, tarball: https://registry.npmjs.org/eventsource-parser/-/eventsource-parser-3.1.0.tgz} engines: {node: '>=18.0.0'} eventsource@3.0.7: - resolution: {integrity: sha512-CRT1WTyuQoD771GW56XEZFQ/ZoSfWid1alKGDYMmkt2yl8UXrVR4pspqWNEcqKvVIzg6PAltWjxcSSPrboA4iA==} + resolution: {integrity: sha512-CRT1WTyuQoD771GW56XEZFQ/ZoSfWid1alKGDYMmkt2yl8UXrVR4pspqWNEcqKvVIzg6PAltWjxcSSPrboA4iA==, tarball: https://registry.npmjs.org/eventsource/-/eventsource-3.0.7.tgz} engines: {node: '>=18.0.0'} execa@5.1.1: - resolution: {integrity: sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==} + resolution: {integrity: sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==, tarball: https://registry.npmjs.org/execa/-/execa-5.1.1.tgz} engines: {node: '>=10'} execa@8.0.1: - resolution: {integrity: sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==} + resolution: {integrity: sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==, tarball: https://registry.npmjs.org/execa/-/execa-8.0.1.tgz} engines: {node: '>=16.17'} execa@9.6.0: - resolution: {integrity: sha512-jpWzZ1ZhwUmeWRhS7Qv3mhpOhLfwI+uAX4e5fOcXqwMR7EcJ0pj2kV1CVzHVMX/LphnKWD3LObjZCoJ71lKpHw==} + resolution: {integrity: sha512-jpWzZ1ZhwUmeWRhS7Qv3mhpOhLfwI+uAX4e5fOcXqwMR7EcJ0pj2kV1CVzHVMX/LphnKWD3LObjZCoJ71lKpHw==, tarball: https://registry.npmjs.org/execa/-/execa-9.6.0.tgz} engines: {node: ^18.19.0 || >=20.5.0} exit-x@0.2.2: - resolution: {integrity: sha512-+I6B/IkJc1o/2tiURyz/ivu/O0nKNEArIUB5O7zBrlDVJr22SCLH3xTeEry428LvFhRzIA1g8izguxJ/gbNcVQ==} + resolution: {integrity: sha512-+I6B/IkJc1o/2tiURyz/ivu/O0nKNEArIUB5O7zBrlDVJr22SCLH3xTeEry428LvFhRzIA1g8izguxJ/gbNcVQ==, tarball: https://registry.npmjs.org/exit-x/-/exit-x-0.2.2.tgz} engines: {node: '>= 0.8.0'} expand-tilde@2.0.2: - resolution: {integrity: sha512-A5EmesHW6rfnZ9ysHQjPdJRni0SRar0tjtG5MNtm9n5TUvsYU8oozprtRD4AqHxcZWWlVuAmQo2nWKfN9oyjTw==} + resolution: {integrity: sha512-A5EmesHW6rfnZ9ysHQjPdJRni0SRar0tjtG5MNtm9n5TUvsYU8oozprtRD4AqHxcZWWlVuAmQo2nWKfN9oyjTw==, tarball: https://registry.npmjs.org/expand-tilde/-/expand-tilde-2.0.2.tgz} engines: {node: '>=0.10.0'} expect-type@1.4.0: - resolution: {integrity: sha512-KfYbmpRm0VbLjEvVa9yGwCi9GI34xvi7A/HXYWQO65CSD2u3MczUJSuwXKFIxlGsgBQizV9q5J9NHj4VG0n+pA==} + resolution: {integrity: sha512-KfYbmpRm0VbLjEvVa9yGwCi9GI34xvi7A/HXYWQO65CSD2u3MczUJSuwXKFIxlGsgBQizV9q5J9NHj4VG0n+pA==, tarball: https://registry.npmjs.org/expect-type/-/expect-type-1.4.0.tgz} engines: {node: '>=12.0.0'} expect@30.2.0: - resolution: {integrity: sha512-u/feCi0GPsI+988gU2FLcsHyAHTU0MX1Wg68NhAnN7z/+C5wqG+CY8J53N9ioe8RXgaoz0nBR/TYMf3AycUuPw==} + resolution: {integrity: sha512-u/feCi0GPsI+988gU2FLcsHyAHTU0MX1Wg68NhAnN7z/+C5wqG+CY8J53N9ioe8RXgaoz0nBR/TYMf3AycUuPw==, tarball: https://registry.npmjs.org/expect/-/expect-30.2.0.tgz} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} expect@30.4.1: - resolution: {integrity: sha512-PMARsyh/JtqC20HoGqlFcIlQAyqUtW4PlI1rup1uhYJtKuwAjbvWi3GQMAn+STdHum/dk8xrKfUM1+5SAwpolA==} + resolution: {integrity: sha512-PMARsyh/JtqC20HoGqlFcIlQAyqUtW4PlI1rup1uhYJtKuwAjbvWi3GQMAn+STdHum/dk8xrKfUM1+5SAwpolA==, tarball: https://registry.npmjs.org/expect/-/expect-30.4.1.tgz} 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==} + resolution: {integrity: sha512-5Kb34ipNX694DH48vN9irak1Qx30nb0PLYHXfJgw4YEjiC3ZEmZJhwOp+VfiCYwFzvFTdB9QkArYS5kXa2cx2A==, tarball: https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-8.5.2.tgz} engines: {node: '>= 16'} peerDependencies: express: '>= 4.11' express@4.22.2: - resolution: {integrity: sha512-IuL+Elrou2ZvCFHs18/CIzy2Nzvo25nZ1/D2eIZlz7c+QUayAcYoiM2BthCjs+EBHVpjYjcuLDAiCWgeIX3X1Q==} + resolution: {integrity: sha512-IuL+Elrou2ZvCFHs18/CIzy2Nzvo25nZ1/D2eIZlz7c+QUayAcYoiM2BthCjs+EBHVpjYjcuLDAiCWgeIX3X1Q==, tarball: https://registry.npmjs.org/express/-/express-4.22.2.tgz} engines: {node: '>= 0.10.0'} express@5.2.1: - resolution: {integrity: sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==} + resolution: {integrity: sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==, tarball: https://registry.npmjs.org/express/-/express-5.2.1.tgz} engines: {node: '>= 18'} exsolve@1.0.8: - resolution: {integrity: sha512-LmDxfWXwcTArk8fUEnOfSZpHOJ6zOMUJKOtFLFqJLoKJetuQG874Uc7/Kki7zFLzYybmZhp1M7+98pfMqeX8yA==} + resolution: {integrity: sha512-LmDxfWXwcTArk8fUEnOfSZpHOJ6zOMUJKOtFLFqJLoKJetuQG874Uc7/Kki7zFLzYybmZhp1M7+98pfMqeX8yA==, tarball: https://registry.npmjs.org/exsolve/-/exsolve-1.0.8.tgz} exsolve@1.1.0: - resolution: {integrity: sha512-D+42+T12DdIlJM3uepa55qGiL3sYdLBOxIl2ifQCzCHz4c7eiolaHsi3BIqEr7JxBzxv2pYZQX9kw16ziMcEmw==} + resolution: {integrity: sha512-D+42+T12DdIlJM3uepa55qGiL3sYdLBOxIl2ifQCzCHz4c7eiolaHsi3BIqEr7JxBzxv2pYZQX9kw16ziMcEmw==, tarball: https://registry.npmjs.org/exsolve/-/exsolve-1.1.0.tgz} fake-indexeddb@5.0.2: - resolution: {integrity: sha512-cB507r5T3D55DfclY01GLkninZLfU7HXV/mhVRTnTRm5k2u+fY7Fof2dBkr80p5t7G7dlA/G5dI87QiMdPpMCQ==} + resolution: {integrity: sha512-cB507r5T3D55DfclY01GLkninZLfU7HXV/mhVRTnTRm5k2u+fY7Fof2dBkr80p5t7G7dlA/G5dI87QiMdPpMCQ==, tarball: https://registry.npmjs.org/fake-indexeddb/-/fake-indexeddb-5.0.2.tgz} engines: {node: '>=18'} fast-deep-equal@3.1.3: - resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} + resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==, tarball: https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz} fast-diff@1.3.0: - resolution: {integrity: sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==} + resolution: {integrity: sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==, tarball: https://registry.npmjs.org/fast-diff/-/fast-diff-1.3.0.tgz} fast-glob@3.3.3: - resolution: {integrity: sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==} + resolution: {integrity: sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==, tarball: https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz} engines: {node: '>=8.6.0'} fast-json-stable-stringify@2.1.0: - resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} + resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==, tarball: https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz} fast-levenshtein@2.0.6: - resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} + resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==, tarball: https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz} fast-string-truncated-width@3.0.3: - resolution: {integrity: sha512-0jjjIEL6+0jag3l2XWWizO64/aZVtpiGE3t0Zgqxv0DPuxiMjvB3M24fCyhZUO4KomJQPj3LTSUnDP3GpdwC0g==} + resolution: {integrity: sha512-0jjjIEL6+0jag3l2XWWizO64/aZVtpiGE3t0Zgqxv0DPuxiMjvB3M24fCyhZUO4KomJQPj3LTSUnDP3GpdwC0g==, tarball: https://registry.npmjs.org/fast-string-truncated-width/-/fast-string-truncated-width-3.0.3.tgz} fast-string-width@3.0.2: - resolution: {integrity: sha512-gX8LrtNEI5hq8DVUfRQMbr5lpaS4nMIWV+7XEbXk2b8kiQIizgnlr12B4dA3ZEx3308ze0O4Q1R+cHts8kyUJg==} + resolution: {integrity: sha512-gX8LrtNEI5hq8DVUfRQMbr5lpaS4nMIWV+7XEbXk2b8kiQIizgnlr12B4dA3ZEx3308ze0O4Q1R+cHts8kyUJg==, tarball: https://registry.npmjs.org/fast-string-width/-/fast-string-width-3.0.2.tgz} fast-uri@3.1.3: - resolution: {integrity: sha512-i70LwGWUduXqzicKXWshooq+sWL1K3WUU5rKZNG/0i3a1OSoX3HqhH5WbWwTmqWfor4urUakGPiRQcleRZTwOg==} + resolution: {integrity: sha512-i70LwGWUduXqzicKXWshooq+sWL1K3WUU5rKZNG/0i3a1OSoX3HqhH5WbWwTmqWfor4urUakGPiRQcleRZTwOg==, tarball: https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.3.tgz} fast-wrap-ansi@0.2.2: - resolution: {integrity: sha512-7F2Fl+TjRSenLqlU3UjSH0iyqopqoZIu7eZVpEirP2g1GtWa2G/ecEmBdgz31+Mxr+ELclgg6sokpSFIQiZ02Q==} + resolution: {integrity: sha512-7F2Fl+TjRSenLqlU3UjSH0iyqopqoZIu7eZVpEirP2g1GtWa2G/ecEmBdgz31+Mxr+ELclgg6sokpSFIQiZ02Q==, tarball: https://registry.npmjs.org/fast-wrap-ansi/-/fast-wrap-ansi-0.2.2.tgz} fastq@1.20.1: - resolution: {integrity: sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==} + resolution: {integrity: sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==, tarball: https://registry.npmjs.org/fastq/-/fastq-1.20.1.tgz} faye-websocket@0.11.4: - resolution: {integrity: sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g==} + resolution: {integrity: sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g==, tarball: https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.4.tgz} engines: {node: '>=0.8.0'} fb-watchman@2.0.2: - resolution: {integrity: sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==} + resolution: {integrity: sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==, tarball: https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz} fdir@6.5.0: - resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} + resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==, tarball: https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz} engines: {node: '>=12.0.0'} peerDependencies: picomatch: ^3 || ^4 @@ -9696,7 +9754,7 @@ packages: optional: true fetch-mock@9.11.0: - resolution: {integrity: sha512-PG1XUv+x7iag5p/iNHD4/jdpxL9FtVSqRMUQhPab4hVDt80T1MH5ehzVrL2IdXO9Q2iBggArFvPqjUbHFuI58Q==} + resolution: {integrity: sha512-PG1XUv+x7iag5p/iNHD4/jdpxL9FtVSqRMUQhPab4hVDt80T1MH5ehzVrL2IdXO9Q2iBggArFvPqjUbHFuI58Q==, tarball: https://registry.npmjs.org/fetch-mock/-/fetch-mock-9.11.0.tgz} engines: {node: '>=4.0.0'} peerDependencies: node-fetch: '*' @@ -9705,101 +9763,101 @@ packages: optional: true fflate@0.8.3: - resolution: {integrity: sha512-tbZNuJrLwGUp3zshBtdy4W+ORxZuIh8a5ilyIEQDC5rY1f3U20JMry0Ll3WBzU58EZKsEuJFXhb5gwv8CsPvgA==} + resolution: {integrity: sha512-tbZNuJrLwGUp3zshBtdy4W+ORxZuIh8a5ilyIEQDC5rY1f3U20JMry0Ll3WBzU58EZKsEuJFXhb5gwv8CsPvgA==, tarball: https://registry.npmjs.org/fflate/-/fflate-0.8.3.tgz} figures@3.2.0: - resolution: {integrity: sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==} + resolution: {integrity: sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==, tarball: https://registry.npmjs.org/figures/-/figures-3.2.0.tgz} engines: {node: '>=8'} figures@6.1.0: - resolution: {integrity: sha512-d+l3qxjSesT4V7v2fh+QnmFnUWv9lSpjarhShNTgBOfA0ttejbQUAlHLitbjkoRiDulW0OPoQPYIGhIC8ohejg==} + resolution: {integrity: sha512-d+l3qxjSesT4V7v2fh+QnmFnUWv9lSpjarhShNTgBOfA0ttejbQUAlHLitbjkoRiDulW0OPoQPYIGhIC8ohejg==, tarball: https://registry.npmjs.org/figures/-/figures-6.1.0.tgz} engines: {node: '>=18'} file-entry-cache@8.0.0: - resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==} + resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==, tarball: https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz} engines: {node: '>=16.0.0'} file-loader@6.2.0: - resolution: {integrity: sha512-qo3glqyTa61Ytg4u73GultjHGjdRyig3tG6lPtyX/jOEJvHif9uB0/OCI2Kif6ctF3caQTW2G5gym21oAsI4pw==} + resolution: {integrity: sha512-qo3glqyTa61Ytg4u73GultjHGjdRyig3tG6lPtyX/jOEJvHif9uB0/OCI2Kif6ctF3caQTW2G5gym21oAsI4pw==, tarball: https://registry.npmjs.org/file-loader/-/file-loader-6.2.0.tgz} engines: {node: '>= 10.13.0'} peerDependencies: webpack: ^4.0.0 || ^5.0.0 filelist@1.0.6: - resolution: {integrity: sha512-5giy2PkLYY1cP39p17Ech+2xlpTRL9HLspOfEgm0L6CwBXBTgsK5ou0JtzYuepxkaQ/tvhCFIJ5uXo0OrM2DxA==} + resolution: {integrity: sha512-5giy2PkLYY1cP39p17Ech+2xlpTRL9HLspOfEgm0L6CwBXBTgsK5ou0JtzYuepxkaQ/tvhCFIJ5uXo0OrM2DxA==, tarball: https://registry.npmjs.org/filelist/-/filelist-1.0.6.tgz} filename-reserved-regex@2.0.0: - resolution: {integrity: sha512-lc1bnsSr4L4Bdif8Xb/qrtokGbq5zlsms/CYH8PP+WtCkGNF65DPiQY8vG3SakEdRn8Dlnm+gW/qWKKjS5sZzQ==} + resolution: {integrity: sha512-lc1bnsSr4L4Bdif8Xb/qrtokGbq5zlsms/CYH8PP+WtCkGNF65DPiQY8vG3SakEdRn8Dlnm+gW/qWKKjS5sZzQ==, tarball: https://registry.npmjs.org/filename-reserved-regex/-/filename-reserved-regex-2.0.0.tgz} engines: {node: '>=4'} filenamify@4.3.0: - resolution: {integrity: sha512-hcFKyUG57yWGAzu1CMt/dPzYZuv+jAJUT85bL8mrXvNe6hWj6yEHEc4EdcgiA6Z3oi1/9wXJdZPXF2dZNgwgOg==} + resolution: {integrity: sha512-hcFKyUG57yWGAzu1CMt/dPzYZuv+jAJUT85bL8mrXvNe6hWj6yEHEc4EdcgiA6Z3oi1/9wXJdZPXF2dZNgwgOg==, tarball: https://registry.npmjs.org/filenamify/-/filenamify-4.3.0.tgz} engines: {node: '>=8'} fill-range@7.1.1: - resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} + resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==, tarball: https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz} engines: {node: '>=8'} finalhandler@1.3.2: - resolution: {integrity: sha512-aA4RyPcd3badbdABGDuTXCMTtOneUCAYH/gxoYRTZlIJdF0YPWuGqiAsIrhNnnqdXGswYk6dGujem4w80UJFhg==} + resolution: {integrity: sha512-aA4RyPcd3badbdABGDuTXCMTtOneUCAYH/gxoYRTZlIJdF0YPWuGqiAsIrhNnnqdXGswYk6dGujem4w80UJFhg==, tarball: https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.2.tgz} engines: {node: '>= 0.8'} finalhandler@2.1.1: - resolution: {integrity: sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA==} + resolution: {integrity: sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA==, tarball: https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.1.tgz} engines: {node: '>= 18.0.0'} find-cache-dir@3.3.2: - resolution: {integrity: sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig==} + resolution: {integrity: sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig==, tarball: https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.2.tgz} engines: {node: '>=8'} find-cache-dir@4.0.0: - resolution: {integrity: sha512-9ZonPT4ZAK4a+1pUPVPZJapbi7O5qbbJPdYw/NOQWZZbVLdDTYM3A4R9z/DpAM08IDaFGsvPgiGZ82WEwUDWjg==} + resolution: {integrity: sha512-9ZonPT4ZAK4a+1pUPVPZJapbi7O5qbbJPdYw/NOQWZZbVLdDTYM3A4R9z/DpAM08IDaFGsvPgiGZ82WEwUDWjg==, tarball: https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-4.0.0.tgz} engines: {node: '>=14.16'} find-cache-directory@6.0.0: - resolution: {integrity: sha512-CvFd5ivA6HcSHbD+59P7CyzINHXzwhuQK8RY7CxJZtgDSAtRlHiCaQpZQ2lMR/WRyUIEmzUvL6G2AGurMfegZA==} + resolution: {integrity: sha512-CvFd5ivA6HcSHbD+59P7CyzINHXzwhuQK8RY7CxJZtgDSAtRlHiCaQpZQ2lMR/WRyUIEmzUvL6G2AGurMfegZA==, tarball: https://registry.npmjs.org/find-cache-directory/-/find-cache-directory-6.0.0.tgz} engines: {node: '>=20'} find-file-up@2.0.1: - resolution: {integrity: sha512-qVdaUhYO39zmh28/JLQM5CoYN9byEOKEH4qfa8K1eNV17W0UUMJ9WgbR/hHFH+t5rcl+6RTb5UC7ck/I+uRkpQ==} + resolution: {integrity: sha512-qVdaUhYO39zmh28/JLQM5CoYN9byEOKEH4qfa8K1eNV17W0UUMJ9WgbR/hHFH+t5rcl+6RTb5UC7ck/I+uRkpQ==, tarball: https://registry.npmjs.org/find-file-up/-/find-file-up-2.0.1.tgz} engines: {node: '>=8'} find-pkg@2.0.0: - resolution: {integrity: sha512-WgZ+nKbELDa6N3i/9nrHeNznm+lY3z4YfhDDWgW+5P0pdmMj26bxaxU11ookgY3NyP9GC7HvZ9etp0jRFqGEeQ==} + resolution: {integrity: sha512-WgZ+nKbELDa6N3i/9nrHeNznm+lY3z4YfhDDWgW+5P0pdmMj26bxaxU11ookgY3NyP9GC7HvZ9etp0jRFqGEeQ==, tarball: https://registry.npmjs.org/find-pkg/-/find-pkg-2.0.0.tgz} engines: {node: '>=8'} find-up-simple@1.0.1: - resolution: {integrity: sha512-afd4O7zpqHeRyg4PfDQsXmlDe2PfdHtJt6Akt8jOWaApLOZk5JXs6VMR29lz03pRe9mpykrRCYIYxaJYcfpncQ==} + resolution: {integrity: sha512-afd4O7zpqHeRyg4PfDQsXmlDe2PfdHtJt6Akt8jOWaApLOZk5JXs6VMR29lz03pRe9mpykrRCYIYxaJYcfpncQ==, tarball: https://registry.npmjs.org/find-up-simple/-/find-up-simple-1.0.1.tgz} engines: {node: '>=18'} find-up@4.1.0: - resolution: {integrity: sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==} + resolution: {integrity: sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==, tarball: https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz} engines: {node: '>=8'} find-up@5.0.0: - resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} + resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==, tarball: https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz} engines: {node: '>=10'} find-up@6.3.0: - resolution: {integrity: sha512-v2ZsoEuVHYy8ZIlYqwPe/39Cy+cFDzp4dXPaxNvkEuouymu+2Jbz0PxpKarJHYJTmv2HWT3O382qY8l4jMWthw==} + resolution: {integrity: sha512-v2ZsoEuVHYy8ZIlYqwPe/39Cy+cFDzp4dXPaxNvkEuouymu+2Jbz0PxpKarJHYJTmv2HWT3O382qY8l4jMWthw==, tarball: https://registry.npmjs.org/find-up/-/find-up-6.3.0.tgz} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} flat-cache@4.0.1: - resolution: {integrity: sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==} + resolution: {integrity: sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==, tarball: https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz} engines: {node: '>=16'} flat@5.0.2: - resolution: {integrity: sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==} + resolution: {integrity: sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==, tarball: https://registry.npmjs.org/flat/-/flat-5.0.2.tgz} hasBin: true flatpickr@4.5.7: - resolution: {integrity: sha512-JqPfihUc9A/j9QAsh6otoARmMyUauPE17vRBEG+ThJwbl8zAq4ssGpxlPK3wWM/i8EFxkHg9UuVo0ds7XluKxw==} + resolution: {integrity: sha512-JqPfihUc9A/j9QAsh6otoARmMyUauPE17vRBEG+ThJwbl8zAq4ssGpxlPK3wWM/i8EFxkHg9UuVo0ds7XluKxw==, tarball: https://registry.npmjs.org/flatpickr/-/flatpickr-4.5.7.tgz} flatted@3.4.2: - resolution: {integrity: sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==} + resolution: {integrity: sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==, tarball: https://registry.npmjs.org/flatted/-/flatted-3.4.2.tgz} follow-redirects@1.16.0: - resolution: {integrity: sha512-y5rN/uOsadFT/JfYwhxRS5R7Qce+g3zG97+JrtFZlC9klX/W5hD7iiLzScI4nZqUS7DNUdhPgw4xI8W2LuXlUw==} + resolution: {integrity: sha512-y5rN/uOsadFT/JfYwhxRS5R7Qce+g3zG97+JrtFZlC9klX/W5hD7iiLzScI4nZqUS7DNUdhPgw4xI8W2LuXlUw==, tarball: https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.16.0.tgz} engines: {node: '>=4.0'} peerDependencies: debug: '*' @@ -9808,19 +9866,19 @@ packages: optional: true font-awesome@4.7.0: - resolution: {integrity: sha512-U6kGnykA/6bFmg1M/oT9EkFeIYv7JlX3bozwQJWiiLz6L0w3F5vBVPxHlwyX/vtNq1ckcpRKOB9f2Qal/VtFpg==} + resolution: {integrity: sha512-U6kGnykA/6bFmg1M/oT9EkFeIYv7JlX3bozwQJWiiLz6L0w3F5vBVPxHlwyX/vtNq1ckcpRKOB9f2Qal/VtFpg==, tarball: https://registry.npmjs.org/font-awesome/-/font-awesome-4.7.0.tgz} engines: {node: '>=0.10.3'} for-each@0.3.5: - resolution: {integrity: sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==} + resolution: {integrity: sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==, tarball: https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz} engines: {node: '>= 0.4'} foreground-child@3.3.1: - resolution: {integrity: sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==} + resolution: {integrity: sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==, tarball: https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz} engines: {node: '>=14'} fork-ts-checker-webpack-plugin@7.2.13: - resolution: {integrity: sha512-fR3WRkOb4bQdWB/y7ssDUlVdrclvwtyCUIHCfivAoYxq9dF7XfrDKbMdZIfwJ7hxIAqkYSGeU7lLJE6xrxIBdg==} + resolution: {integrity: sha512-fR3WRkOb4bQdWB/y7ssDUlVdrclvwtyCUIHCfivAoYxq9dF7XfrDKbMdZIfwJ7hxIAqkYSGeU7lLJE6xrxIBdg==, tarball: https://registry.npmjs.org/fork-ts-checker-webpack-plugin/-/fork-ts-checker-webpack-plugin-7.2.13.tgz} engines: {node: '>=12.13.0', yarn: '>=1.0.0'} peerDependencies: typescript: '>3.6.0' @@ -9831,341 +9889,345 @@ packages: optional: true fork-ts-checker-webpack-plugin@9.1.0: - resolution: {integrity: sha512-mpafl89VFPJmhnJ1ssH+8wmM2b50n+Rew5x42NeI2U78aRWgtkEtGmctp7iT16UjquJTjorEmIfESj3DxdW84Q==} + resolution: {integrity: sha512-mpafl89VFPJmhnJ1ssH+8wmM2b50n+Rew5x42NeI2U78aRWgtkEtGmctp7iT16UjquJTjorEmIfESj3DxdW84Q==, tarball: https://registry.npmjs.org/fork-ts-checker-webpack-plugin/-/fork-ts-checker-webpack-plugin-9.1.0.tgz} engines: {node: '>=14.21.3'} peerDependencies: typescript: '>3.6.0' webpack: ^5.11.0 form-data@4.0.6: - resolution: {integrity: sha512-vKatAh4SlVfgbv+YtmhiRjhEMJsYpsG1Y2rMQtR+SVSbytsSD1YGzDIcrAJmdFec88u/+VoGmxnl+80gL1tRCQ==} + resolution: {integrity: sha512-vKatAh4SlVfgbv+YtmhiRjhEMJsYpsG1Y2rMQtR+SVSbytsSD1YGzDIcrAJmdFec88u/+VoGmxnl+80gL1tRCQ==, tarball: https://registry.npmjs.org/form-data/-/form-data-4.0.6.tgz} engines: {node: '>= 6'} forwarded@0.2.0: - resolution: {integrity: sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==} + resolution: {integrity: sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==, tarball: https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz} engines: {node: '>= 0.6'} fraction.js@5.3.4: - resolution: {integrity: sha512-1X1NTtiJphryn/uLQz3whtY6jK3fTqoE3ohKs0tT+Ujr1W59oopxmoEh7Lu5p6vBaPbgoM0bzveAW4Qi5RyWDQ==} + resolution: {integrity: sha512-1X1NTtiJphryn/uLQz3whtY6jK3fTqoE3ohKs0tT+Ujr1W59oopxmoEh7Lu5p6vBaPbgoM0bzveAW4Qi5RyWDQ==, tarball: https://registry.npmjs.org/fraction.js/-/fraction.js-5.3.4.tgz} fresh@0.5.2: - resolution: {integrity: sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==} + resolution: {integrity: sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==, tarball: https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz} engines: {node: '>= 0.6'} fresh@2.0.0: - resolution: {integrity: sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==} + resolution: {integrity: sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==, tarball: https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz} engines: {node: '>= 0.8'} front-matter@4.0.2: - resolution: {integrity: sha512-I8ZuJ/qG92NWX8i5x1Y8qyj3vizhXS31OxjKDu3LKP+7/qBgfIKValiZIEwoVoJKUHlhWtYrktkxV1XsX+pPlg==} + resolution: {integrity: sha512-I8ZuJ/qG92NWX8i5x1Y8qyj3vizhXS31OxjKDu3LKP+7/qBgfIKValiZIEwoVoJKUHlhWtYrktkxV1XsX+pPlg==, tarball: https://registry.npmjs.org/front-matter/-/front-matter-4.0.2.tgz} fs-constants@1.0.0: - resolution: {integrity: sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==} + resolution: {integrity: sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==, tarball: https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz} fs-extra@10.1.0: - resolution: {integrity: sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==} + resolution: {integrity: sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==, tarball: https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz} engines: {node: '>=12'} fs-extra@11.3.2: - resolution: {integrity: sha512-Xr9F6z6up6Ws+NjzMCZc6WXg2YFRlrLP9NQDO3VQrWrfiojdhS56TzueT88ze0uBdCTwEIhQ3ptnmKeWGFAe0A==} + resolution: {integrity: sha512-Xr9F6z6up6Ws+NjzMCZc6WXg2YFRlrLP9NQDO3VQrWrfiojdhS56TzueT88ze0uBdCTwEIhQ3ptnmKeWGFAe0A==, tarball: https://registry.npmjs.org/fs-extra/-/fs-extra-11.3.2.tgz} engines: {node: '>=14.14'} fs-extra@8.1.0: - resolution: {integrity: sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==} + resolution: {integrity: sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==, tarball: https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz} engines: {node: '>=6 <7 || >=8'} fs-extra@9.1.0: - resolution: {integrity: sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==} + resolution: {integrity: sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==, tarball: https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz} engines: {node: '>=10'} fs-monkey@1.1.0: - resolution: {integrity: sha512-QMUezzXWII9EV5aTFXW1UBVUO77wYPpjqIF8/AviUCThNeSYZykpoTixUeaNNBwmCev0AMDWMAni+f8Hxb1IFw==} + resolution: {integrity: sha512-QMUezzXWII9EV5aTFXW1UBVUO77wYPpjqIF8/AviUCThNeSYZykpoTixUeaNNBwmCev0AMDWMAni+f8Hxb1IFw==, tarball: https://registry.npmjs.org/fs-monkey/-/fs-monkey-1.1.0.tgz} fs.realpath@1.0.0: - resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} + resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==, tarball: https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz} fsevents@2.3.2: - resolution: {integrity: sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==} + resolution: {integrity: sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==, tarball: https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz} engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} os: [darwin] fsevents@2.3.3: - resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} + resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==, tarball: https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz} engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} os: [darwin] function-bind@1.1.2: - resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} + resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==, tarball: https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz} function.prototype.name@1.2.0: - resolution: {integrity: sha512-jObKIik1P2QjPHP5nz5BaOtUlfgS0fWo8IUByNXkM+o+02sJOi94em77GwJKQSJ3gfPHdgzLNrHc1uokV4P/ew==} + resolution: {integrity: sha512-jObKIik1P2QjPHP5nz5BaOtUlfgS0fWo8IUByNXkM+o+02sJOi94em77GwJKQSJ3gfPHdgzLNrHc1uokV4P/ew==, tarball: https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.2.0.tgz} engines: {node: '>= 0.4'} functions-have-names@1.2.3: - resolution: {integrity: sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==} + resolution: {integrity: sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==, tarball: https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz} generator-function@2.0.1: - resolution: {integrity: sha512-SFdFmIJi+ybC0vjlHN0ZGVGHc3lgE0DxPAT0djjVg+kjOnSqclqmj0KQ7ykTOLP6YxoqOvuAODGdcHJn+43q3g==} + resolution: {integrity: sha512-SFdFmIJi+ybC0vjlHN0ZGVGHc3lgE0DxPAT0djjVg+kjOnSqclqmj0KQ7ykTOLP6YxoqOvuAODGdcHJn+43q3g==, tarball: https://registry.npmjs.org/generator-function/-/generator-function-2.0.1.tgz} engines: {node: '>= 0.4'} generic-names@4.0.0: - resolution: {integrity: sha512-ySFolZQfw9FoDb3ed9d80Cm9f0+r7qj+HJkWjeD9RBfpxEVTlVhol+gvaQB/78WbwYfbnNh8nWHHBSlg072y6A==} + resolution: {integrity: sha512-ySFolZQfw9FoDb3ed9d80Cm9f0+r7qj+HJkWjeD9RBfpxEVTlVhol+gvaQB/78WbwYfbnNh8nWHHBSlg072y6A==, tarball: https://registry.npmjs.org/generic-names/-/generic-names-4.0.0.tgz} gensync@1.0.0-beta.2: - resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} + resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==, tarball: https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz} engines: {node: '>=6.9.0'} get-caller-file@2.0.5: - resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} + resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==, tarball: https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz} engines: {node: 6.* || 8.* || >= 10.*} get-east-asian-width@1.6.0: - resolution: {integrity: sha512-QRbvDIbx6YklUe6RxeTeleMR0yv3cYH6PsPZHcnVn7xv7zO1BHN8r0XETu8n6Ye3Q+ahtSarc3WgtNWmehIBfA==} + resolution: {integrity: sha512-QRbvDIbx6YklUe6RxeTeleMR0yv3cYH6PsPZHcnVn7xv7zO1BHN8r0XETu8n6Ye3Q+ahtSarc3WgtNWmehIBfA==, tarball: https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.6.0.tgz} engines: {node: '>=18'} get-intrinsic@1.3.0: - resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==} + resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==, tarball: https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz} engines: {node: '>= 0.4'} get-package-type@0.1.0: - resolution: {integrity: sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==} + resolution: {integrity: sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==, tarball: https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz} engines: {node: '>=8.0.0'} get-proto@1.0.1: - resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==} + resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==, tarball: https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz} engines: {node: '>= 0.4'} get-stream@6.0.1: - resolution: {integrity: sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==} + resolution: {integrity: sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==, tarball: https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz} engines: {node: '>=10'} get-stream@8.0.1: - resolution: {integrity: sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==} + resolution: {integrity: sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==, tarball: https://registry.npmjs.org/get-stream/-/get-stream-8.0.1.tgz} engines: {node: '>=16'} get-stream@9.0.1: - resolution: {integrity: sha512-kVCxPF3vQM/N0B1PmoqVUqgHP+EeVjmZSQn+1oCRPxd2P21P2F19lIgbR3HBosbB1PUhOAoctJnfEn2GbN2eZA==} + resolution: {integrity: sha512-kVCxPF3vQM/N0B1PmoqVUqgHP+EeVjmZSQn+1oCRPxd2P21P2F19lIgbR3HBosbB1PUhOAoctJnfEn2GbN2eZA==, tarball: https://registry.npmjs.org/get-stream/-/get-stream-9.0.1.tgz} engines: {node: '>=18'} get-symbol-description@1.1.0: - resolution: {integrity: sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==} + resolution: {integrity: sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==, tarball: https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.1.0.tgz} engines: {node: '>= 0.4'} get-them-args@1.3.2: - resolution: {integrity: sha512-LRn8Jlk+DwZE4GTlDbT3Hikd1wSHgLMme/+7ddlqKd7ldwR6LjJgTVWzBnR01wnYGe4KgrXjg287RaI22UHmAw==} + resolution: {integrity: sha512-LRn8Jlk+DwZE4GTlDbT3Hikd1wSHgLMme/+7ddlqKd7ldwR6LjJgTVWzBnR01wnYGe4KgrXjg287RaI22UHmAw==, tarball: https://registry.npmjs.org/get-them-args/-/get-them-args-1.3.2.tgz} get-tsconfig@4.14.0: - resolution: {integrity: sha512-yTb+8DXzDREzgvYmh6s9vHsSVCHeC0G3PI5bEXNBHtmshPnO+S5O7qgLEOn0I5QvMy6kpZN8K1NKGyilLb93wA==} + resolution: {integrity: sha512-yTb+8DXzDREzgvYmh6s9vHsSVCHeC0G3PI5bEXNBHtmshPnO+S5O7qgLEOn0I5QvMy6kpZN8K1NKGyilLb93wA==, tarball: https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.14.0.tgz} gh-pages@6.1.1: - resolution: {integrity: sha512-upnohfjBwN5hBP9w2dPE7HO5JJTHzSGMV1JrLrHvNuqmjoYHg6TBrCcnEoorjG/e0ejbuvnwyKMdTyM40PEByw==} + resolution: {integrity: sha512-upnohfjBwN5hBP9w2dPE7HO5JJTHzSGMV1JrLrHvNuqmjoYHg6TBrCcnEoorjG/e0ejbuvnwyKMdTyM40PEByw==, tarball: https://registry.npmjs.org/gh-pages/-/gh-pages-6.1.1.tgz} engines: {node: '>=10'} hasBin: true glob-parent@5.1.2: - resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} + resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==, tarball: https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz} engines: {node: '>= 6'} glob-parent@6.0.2: - resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} + resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==, tarball: https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz} engines: {node: '>=10.13.0'} glob-to-regex.js@1.2.0: - resolution: {integrity: sha512-QMwlOQKU/IzqMUOAZWubUOT8Qft+Y0KQWnX9nK3ch0CJg0tTp4TvGZsTfudYKv2NzoQSyPcnA6TYeIQ3jGichQ==} + resolution: {integrity: sha512-QMwlOQKU/IzqMUOAZWubUOT8Qft+Y0KQWnX9nK3ch0CJg0tTp4TvGZsTfudYKv2NzoQSyPcnA6TYeIQ3jGichQ==, tarball: https://registry.npmjs.org/glob-to-regex.js/-/glob-to-regex.js-1.2.0.tgz} engines: {node: '>=10.0'} peerDependencies: tslib: '2' glob-to-regexp@0.4.1: - resolution: {integrity: sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==} + resolution: {integrity: sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==, tarball: https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz} glob@10.5.0: - resolution: {integrity: sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==} + resolution: {integrity: sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==, tarball: https://registry.npmjs.org/glob/-/glob-10.5.0.tgz} 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 hasBin: true glob@7.2.3: - resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} + resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==, tarball: https://registry.npmjs.org/glob/-/glob-7.2.3.tgz} 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@8.1.0: - resolution: {integrity: sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==} + resolution: {integrity: sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==, tarball: https://registry.npmjs.org/glob/-/glob-8.1.0.tgz} engines: {node: '>=12'} 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 global-modules@1.0.0: - resolution: {integrity: sha512-sKzpEkf11GpOFuw0Zzjzmt4B4UZwjOcG757PPvrfhxcLFbq0wpsgpOqxpxtxFiCG4DtG93M6XRVbF2oGdev7bg==} + resolution: {integrity: sha512-sKzpEkf11GpOFuw0Zzjzmt4B4UZwjOcG757PPvrfhxcLFbq0wpsgpOqxpxtxFiCG4DtG93M6XRVbF2oGdev7bg==, tarball: https://registry.npmjs.org/global-modules/-/global-modules-1.0.0.tgz} engines: {node: '>=0.10.0'} global-prefix@1.0.2: - resolution: {integrity: sha512-5lsx1NUDHtSjfg0eHlmYvZKv8/nVqX4ckFbM+FrGcQ+04KWcWFo9P5MxPZYSzUvyzmdTbI7Eix8Q4IbELDqzKg==} + resolution: {integrity: sha512-5lsx1NUDHtSjfg0eHlmYvZKv8/nVqX4ckFbM+FrGcQ+04KWcWFo9P5MxPZYSzUvyzmdTbI7Eix8Q4IbELDqzKg==, tarball: https://registry.npmjs.org/global-prefix/-/global-prefix-1.0.2.tgz} engines: {node: '>=0.10.0'} globals@13.24.0: - resolution: {integrity: sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==} + resolution: {integrity: sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==, tarball: https://registry.npmjs.org/globals/-/globals-13.24.0.tgz} engines: {node: '>=8'} globals@14.0.0: - resolution: {integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==} + resolution: {integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==, tarball: https://registry.npmjs.org/globals/-/globals-14.0.0.tgz} engines: {node: '>=18'} globals@17.7.0: - resolution: {integrity: sha512-Czmyns5dUsq4seFBR/Kdydhmo8y9kC79hiSkPn0YcGtNnYWnrgt0vjrSjx9tspoDGWm2CMarffRuLjM4xUz8xg==} + resolution: {integrity: sha512-Czmyns5dUsq4seFBR/Kdydhmo8y9kC79hiSkPn0YcGtNnYWnrgt0vjrSjx9tspoDGWm2CMarffRuLjM4xUz8xg==, tarball: https://registry.npmjs.org/globals/-/globals-17.7.0.tgz} engines: {node: '>=18'} globalthis@1.0.4: - resolution: {integrity: sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==} + resolution: {integrity: sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==, tarball: https://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz} engines: {node: '>= 0.4'} globby@12.2.0: - resolution: {integrity: sha512-wiSuFQLZ+urS9x2gGPl1H5drc5twabmm4m2gTR27XDFyjUHJUNsS8o/2aKyIF6IoBaR630atdher0XJ5g6OMmA==} + resolution: {integrity: sha512-wiSuFQLZ+urS9x2gGPl1H5drc5twabmm4m2gTR27XDFyjUHJUNsS8o/2aKyIF6IoBaR630atdher0XJ5g6OMmA==, tarball: https://registry.npmjs.org/globby/-/globby-12.2.0.tgz} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} globby@6.1.0: - resolution: {integrity: sha512-KVbFv2TQtbzCoxAnfD6JcHZTYCzyliEaaeM/gH8qQdkKr5s0OP9scEgvdcngyk7AVdY6YVW/TJHd+lQ/Df3Daw==} + resolution: {integrity: sha512-KVbFv2TQtbzCoxAnfD6JcHZTYCzyliEaaeM/gH8qQdkKr5s0OP9scEgvdcngyk7AVdY6YVW/TJHd+lQ/Df3Daw==, tarball: https://registry.npmjs.org/globby/-/globby-6.1.0.tgz} engines: {node: '>=0.10.0'} globrex@0.1.2: - resolution: {integrity: sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg==} + resolution: {integrity: sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg==, tarball: https://registry.npmjs.org/globrex/-/globrex-0.1.2.tgz} good-listener@1.2.2: - resolution: {integrity: sha512-goW1b+d9q/HIwbVYZzZ6SsTr4IgE+WA44A0GmPIQstuOrgsFcT7VEJ48nmr9GaRtNu0XTKacFLGnBPAM6Afouw==} + resolution: {integrity: sha512-goW1b+d9q/HIwbVYZzZ6SsTr4IgE+WA44A0GmPIQstuOrgsFcT7VEJ48nmr9GaRtNu0XTKacFLGnBPAM6Afouw==, tarball: https://registry.npmjs.org/good-listener/-/good-listener-1.2.2.tgz} gopd@1.2.0: - resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==} + resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==, tarball: https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz} engines: {node: '>= 0.4'} graceful-fs@4.2.11: - resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} + resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==, tarball: https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz} gridstack@8.4.0: - resolution: {integrity: sha512-qLJuJrBy9bbG3hI+h2cEhiuZ51J3MyEMmv5AXg7MCFiBeG8A4HyIUytueqtD/oZcA3Pccq2Xoj7GrwpmKOS3ig==} + resolution: {integrity: sha512-qLJuJrBy9bbG3hI+h2cEhiuZ51J3MyEMmv5AXg7MCFiBeG8A4HyIUytueqtD/oZcA3Pccq2Xoj7GrwpmKOS3ig==, tarball: https://registry.npmjs.org/gridstack/-/gridstack-8.4.0.tgz} gzip-size@6.0.0: - resolution: {integrity: sha512-ax7ZYomf6jqPTQ4+XCpUGyXKHk5WweS+e05MBO4/y3WJ5RkmPXNKvX+bx1behVILVwr6JSQvZAku021CHPXG3Q==} + resolution: {integrity: sha512-ax7ZYomf6jqPTQ4+XCpUGyXKHk5WweS+e05MBO4/y3WJ5RkmPXNKvX+bx1behVILVwr6JSQvZAku021CHPXG3Q==, tarball: https://registry.npmjs.org/gzip-size/-/gzip-size-6.0.0.tgz} engines: {node: '>=10'} hachure-fill@0.5.2: - resolution: {integrity: sha512-3GKBOn+m2LX9iq+JC1064cSFprJY4jL1jCXTcpnfER5HYE2l/4EfWSGzkPa/ZDBmYI0ZOEj5VHV/eKnPGkHuOg==} + resolution: {integrity: sha512-3GKBOn+m2LX9iq+JC1064cSFprJY4jL1jCXTcpnfER5HYE2l/4EfWSGzkPa/ZDBmYI0ZOEj5VHV/eKnPGkHuOg==, tarball: https://registry.npmjs.org/hachure-fill/-/hachure-fill-0.5.2.tgz} handle-thing@2.0.1: - resolution: {integrity: sha512-9Qn4yBxelxoh2Ow62nP+Ka/kMnOXRi8BXnRaUwezLNhqelnN49xKz4F/dPP8OYLxLxq6JDtZb2i9XznUQbNPTg==} + resolution: {integrity: sha512-9Qn4yBxelxoh2Ow62nP+Ka/kMnOXRi8BXnRaUwezLNhqelnN49xKz4F/dPP8OYLxLxq6JDtZb2i9XznUQbNPTg==, tarball: https://registry.npmjs.org/handle-thing/-/handle-thing-2.0.1.tgz} handlebars@4.7.9: - resolution: {integrity: sha512-4E71E0rpOaQuJR2A3xDZ+GM1HyWYv1clR58tC8emQNeQe3RH7MAzSbat+V0wG78LQBo6m6bzSG/L4pBuCsgnUQ==} + resolution: {integrity: sha512-4E71E0rpOaQuJR2A3xDZ+GM1HyWYv1clR58tC8emQNeQe3RH7MAzSbat+V0wG78LQBo6m6bzSG/L4pBuCsgnUQ==, tarball: https://registry.npmjs.org/handlebars/-/handlebars-4.7.9.tgz} engines: {node: '>=0.4.7'} hasBin: true happy-dom@15.7.4: - resolution: {integrity: sha512-r1vadDYGMtsHAAsqhDuk4IpPvr6N8MGKy5ntBo7tSdim+pWDxus2PNqOcOt8LuDZ4t3KJHE+gCuzupcx/GKnyQ==} + resolution: {integrity: sha512-r1vadDYGMtsHAAsqhDuk4IpPvr6N8MGKy5ntBo7tSdim+pWDxus2PNqOcOt8LuDZ4t3KJHE+gCuzupcx/GKnyQ==, tarball: https://registry.npmjs.org/happy-dom/-/happy-dom-15.7.4.tgz} engines: {node: '>=18.0.0'} happy-dom@20.10.6: - resolution: {integrity: sha512-6QD0ilzDDt93tX44y8tbmZdAcdTRYDhUP+Asgi6pC8Pp5IA3cvaZGyoVN/EGtlq9ziT65iPuBBn3ASLr6hCgVw==} + resolution: {integrity: sha512-6QD0ilzDDt93tX44y8tbmZdAcdTRYDhUP+Asgi6pC8Pp5IA3cvaZGyoVN/EGtlq9ziT65iPuBBn3ASLr6hCgVw==, tarball: https://registry.npmjs.org/happy-dom/-/happy-dom-20.10.6.tgz} engines: {node: '>=20.0.0'} harmony-reflect@1.6.2: - resolution: {integrity: sha512-HIp/n38R9kQjDEziXyDTuW3vvoxxyxjxFzXLrBr18uB47GnSt+G9D29fqrpM5ZkspMcPICud3XsBJQ4Y2URg8g==} + resolution: {integrity: sha512-HIp/n38R9kQjDEziXyDTuW3vvoxxyxjxFzXLrBr18uB47GnSt+G9D29fqrpM5ZkspMcPICud3XsBJQ4Y2URg8g==, tarball: https://registry.npmjs.org/harmony-reflect/-/harmony-reflect-1.6.2.tgz} has-bigints@1.1.0: - resolution: {integrity: sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==} + resolution: {integrity: sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==, tarball: https://registry.npmjs.org/has-bigints/-/has-bigints-1.1.0.tgz} engines: {node: '>= 0.4'} has-flag@4.0.0: - resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} + resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==, tarball: https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz} engines: {node: '>=8'} has-property-descriptors@1.0.2: - resolution: {integrity: sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==} + resolution: {integrity: sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==, tarball: https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz} has-proto@1.2.0: - resolution: {integrity: sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ==} + resolution: {integrity: sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ==, tarball: https://registry.npmjs.org/has-proto/-/has-proto-1.2.0.tgz} engines: {node: '>= 0.4'} has-symbols@1.1.0: - resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==} + resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==, tarball: https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz} engines: {node: '>= 0.4'} has-tostringtag@1.0.2: - resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==} + resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==, tarball: https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz} engines: {node: '>= 0.4'} hasown@2.0.4: - resolution: {integrity: sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==} + resolution: {integrity: sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==, tarball: https://registry.npmjs.org/hasown/-/hasown-2.0.4.tgz} engines: {node: '>= 0.4'} he@1.2.0: - resolution: {integrity: sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==} + resolution: {integrity: sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==, tarball: https://registry.npmjs.org/he/-/he-1.2.0.tgz} hasBin: true highlight.js@11.11.1: - resolution: {integrity: sha512-Xwwo44whKBVCYoliBQwaPvtd/2tYFkRQtXDWj1nackaV2JPXx3L0+Jvd8/qCJ2p+ML0/XVkJ2q+Mr+UVdpJK5w==} + resolution: {integrity: sha512-Xwwo44whKBVCYoliBQwaPvtd/2tYFkRQtXDWj1nackaV2JPXx3L0+Jvd8/qCJ2p+ML0/XVkJ2q+Mr+UVdpJK5w==, tarball: https://registry.npmjs.org/highlight.js/-/highlight.js-11.11.1.tgz} engines: {node: '>=12.0.0'} homedir-polyfill@1.0.3: - resolution: {integrity: sha512-eSmmWE5bZTK2Nou4g0AI3zZ9rswp7GRKoKXS1BLUkvPviOqs4YTN1djQIqrXy9k5gEtdLPy86JjRwsNM9tnDcA==} + resolution: {integrity: sha512-eSmmWE5bZTK2Nou4g0AI3zZ9rswp7GRKoKXS1BLUkvPviOqs4YTN1djQIqrXy9k5gEtdLPy86JjRwsNM9tnDcA==, tarball: https://registry.npmjs.org/homedir-polyfill/-/homedir-polyfill-1.0.3.tgz} engines: {node: '>=0.10.0'} hono@4.12.30: - resolution: {integrity: sha512-emn+JoJjrN9YTpRDS5it/UI2SO9BAE37T6I3d963RxcZ81G9A4pr2SZTEiiaiKbzx+NKRg5BZ89fCL7gCJCUog==} + resolution: {integrity: sha512-emn+JoJjrN9YTpRDS5it/UI2SO9BAE37T6I3d963RxcZ81G9A4pr2SZTEiiaiKbzx+NKRg5BZ89fCL7gCJCUog==, tarball: https://registry.npmjs.org/hono/-/hono-4.12.30.tgz} engines: {node: '>=16.9.0'} hosted-git-info@10.1.1: - resolution: {integrity: sha512-DeOnSPAvOndYKfw075gt8yZzQ7S2hNztw34zBTfhIzLhmBTswIBg5/y+pqu/VD5cYWm5goAFTusDmUEmKZ0PEQ==} + resolution: {integrity: sha512-DeOnSPAvOndYKfw075gt8yZzQ7S2hNztw34zBTfhIzLhmBTswIBg5/y+pqu/VD5cYWm5goAFTusDmUEmKZ0PEQ==, tarball: https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-10.1.1.tgz} engines: {node: ^22.22.2 || ^24.15.0 || >=26.0.0} hosted-git-info@7.0.2: - resolution: {integrity: sha512-puUZAUKT5m8Zzvs72XWy3HtvVbTWljRE66cP60bxJzAqf2DgICo7lYTY2IHUmLnNpjYvw5bvmoHvPc0QO2a62w==} + resolution: {integrity: sha512-puUZAUKT5m8Zzvs72XWy3HtvVbTWljRE66cP60bxJzAqf2DgICo7lYTY2IHUmLnNpjYvw5bvmoHvPc0QO2a62w==, tarball: https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-7.0.2.tgz} engines: {node: ^16.14.0 || >=18.0.0} hpack.js@2.1.6: - resolution: {integrity: sha512-zJxVehUdMGIKsRaNt7apO2Gqp0BdqW5yaiGHXXmbpvxgBYVZnAql+BJb4RO5ad2MgpbZKn5G6nMnegrH1FcNYQ==} + resolution: {integrity: sha512-zJxVehUdMGIKsRaNt7apO2Gqp0BdqW5yaiGHXXmbpvxgBYVZnAql+BJb4RO5ad2MgpbZKn5G6nMnegrH1FcNYQ==, tarball: https://registry.npmjs.org/hpack.js/-/hpack.js-2.1.6.tgz} html-encoding-sniffer@3.0.0: - resolution: {integrity: sha512-oWv4T4yJ52iKrufjnyZPkrN0CH3QnrUqdB6In1g5Fe1mia8GmF36gnfNySxoZtxD5+NmYw1EElVXiBk93UeskA==} + resolution: {integrity: sha512-oWv4T4yJ52iKrufjnyZPkrN0CH3QnrUqdB6In1g5Fe1mia8GmF36gnfNySxoZtxD5+NmYw1EElVXiBk93UeskA==, tarball: https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-3.0.0.tgz} engines: {node: '>=12'} html-encoding-sniffer@6.0.0: - resolution: {integrity: sha512-CV9TW3Y3f8/wT0BRFc1/KAVQ3TUHiXmaAb6VW9vtiMFf7SLoMd1PdAc4W3KFOFETBJUb90KatHqlsZMWV+R9Gg==} + resolution: {integrity: sha512-CV9TW3Y3f8/wT0BRFc1/KAVQ3TUHiXmaAb6VW9vtiMFf7SLoMd1PdAc4W3KFOFETBJUb90KatHqlsZMWV+R9Gg==, tarball: https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-6.0.0.tgz} engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} html-escaper@2.0.2: - resolution: {integrity: sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==} + resolution: {integrity: sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==, tarball: https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz} htmldiff-js@1.0.5: - resolution: {integrity: sha512-rmow9353OK0elkub15Sbze8Nj7BYfduqoJJw4yEvHHjOcHeCazNPk0PoUbjE8SvxKgjymeRIFU/OnS8jtitRtA==} + resolution: {integrity: sha512-rmow9353OK0elkub15Sbze8Nj7BYfduqoJJw4yEvHHjOcHeCazNPk0PoUbjE8SvxKgjymeRIFU/OnS8jtitRtA==, tarball: https://registry.npmjs.org/htmldiff-js/-/htmldiff-js-1.0.5.tgz} htmlparser2@10.1.0: - resolution: {integrity: sha512-VTZkM9GWRAtEpveh7MSF6SjjrpNVNNVJfFup7xTY3UpFtm67foy9HDVXneLtFVt4pMz5kZtgNcvCniNFb1hlEQ==} + resolution: {integrity: sha512-VTZkM9GWRAtEpveh7MSF6SjjrpNVNNVJfFup7xTY3UpFtm67foy9HDVXneLtFVt4pMz5kZtgNcvCniNFb1hlEQ==, tarball: https://registry.npmjs.org/htmlparser2/-/htmlparser2-10.1.0.tgz} + + htmlparser2@12.0.0: + resolution: {integrity: sha512-Tz7u1i95/g2x2jz81+x0FBVhBhY5aRTvD3tXXdFaljuNdzDLJ8UGNRrTcj2cgQvAg3iW/h77Fz15nLW0L0CrZw==, tarball: https://registry.npmjs.org/htmlparser2/-/htmlparser2-12.0.0.tgz} + engines: {node: '>=20.19.0'} http-assert@1.5.0: - resolution: {integrity: sha512-uPpH7OKX4H25hBmU6G1jWNaqJGpTXxey+YOUizJUAgu0AjLUeC8D73hTrhvDS5D+GJN1DN1+hhc/eF/wpxtp0w==} + resolution: {integrity: sha512-uPpH7OKX4H25hBmU6G1jWNaqJGpTXxey+YOUizJUAgu0AjLUeC8D73hTrhvDS5D+GJN1DN1+hhc/eF/wpxtp0w==, tarball: https://registry.npmjs.org/http-assert/-/http-assert-1.5.0.tgz} engines: {node: '>= 0.8'} http-deceiver@1.2.7: - resolution: {integrity: sha512-LmpOGxTfbpgtGVxJrj5k7asXHCgNZp5nLfp+hWc8QQRqtb7fUy6kRY3BO1h9ddF6yIPYUARgxGOwB42DnxIaNw==} + resolution: {integrity: sha512-LmpOGxTfbpgtGVxJrj5k7asXHCgNZp5nLfp+hWc8QQRqtb7fUy6kRY3BO1h9ddF6yIPYUARgxGOwB42DnxIaNw==, tarball: https://registry.npmjs.org/http-deceiver/-/http-deceiver-1.2.7.tgz} http-errors@1.8.1: - resolution: {integrity: sha512-Kpk9Sm7NmI+RHhnj6OIWDI1d6fIoFAtFt9RLaTMRlg/8w49juAStsrBgp0Dp4OdxdVbRIeKhtCUvoi/RuAhO4g==} + resolution: {integrity: sha512-Kpk9Sm7NmI+RHhnj6OIWDI1d6fIoFAtFt9RLaTMRlg/8w49juAStsrBgp0Dp4OdxdVbRIeKhtCUvoi/RuAhO4g==, tarball: https://registry.npmjs.org/http-errors/-/http-errors-1.8.1.tgz} engines: {node: '>= 0.6'} http-errors@2.0.1: - resolution: {integrity: sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==} + resolution: {integrity: sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==, tarball: https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz} engines: {node: '>= 0.8'} http-parser-js@0.5.10: - resolution: {integrity: sha512-Pysuw9XpUq5dVc/2SMHpuTY01RFl8fttgcyunjL7eEMhGM3cI4eOmiCycJDVCo/7O7ClfQD3SaI6ftDzqOXYMA==} + resolution: {integrity: sha512-Pysuw9XpUq5dVc/2SMHpuTY01RFl8fttgcyunjL7eEMhGM3cI4eOmiCycJDVCo/7O7ClfQD3SaI6ftDzqOXYMA==, tarball: https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.10.tgz} http-proxy-agent@5.0.0: - resolution: {integrity: sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==} + resolution: {integrity: sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==, tarball: https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz} engines: {node: '>= 6'} http-proxy-agent@7.0.2: - resolution: {integrity: sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==} + resolution: {integrity: sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==, tarball: https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz} engines: {node: '>= 14'} http-proxy-middleware@2.0.10: - resolution: {integrity: sha512-RKzRWNPxUZqbuk3BC5mGVJbBnWgr+diEnjJexIOytFbBzDy88Fbh/YvBr3DsNrl1jYAfjWfpATEv0NO35FDuPQ==} + resolution: {integrity: sha512-RKzRWNPxUZqbuk3BC5mGVJbBnWgr+diEnjJexIOytFbBzDy88Fbh/YvBr3DsNrl1jYAfjWfpATEv0NO35FDuPQ==, tarball: https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-2.0.10.tgz} engines: {node: '>=12.0.0'} peerDependencies: '@types/express': ^4.17.13 @@ -10174,156 +10236,156 @@ packages: optional: true http-proxy-middleware@3.0.5: - resolution: {integrity: sha512-GLZZm1X38BPY4lkXA01jhwxvDoOkkXqjgVyUzVxiEK4iuRu03PZoYHhHRwxnfhQMDuaxi3vVri0YgSro/1oWqg==} + resolution: {integrity: sha512-GLZZm1X38BPY4lkXA01jhwxvDoOkkXqjgVyUzVxiEK4iuRu03PZoYHhHRwxnfhQMDuaxi3vVri0YgSro/1oWqg==, tarball: https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-3.0.5.tgz} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} http-proxy-middleware@4.2.0: - resolution: {integrity: sha512-ZA+oNOoM+GLoFTIzhkJptVQov73Srep2LBqhF8hG8CIPKO3nam1jonXVQ/QUH8RbwsmaaVz2SOJdzBNBHNtKbw==} + resolution: {integrity: sha512-ZA+oNOoM+GLoFTIzhkJptVQov73Srep2LBqhF8hG8CIPKO3nam1jonXVQ/QUH8RbwsmaaVz2SOJdzBNBHNtKbw==, tarball: https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-4.2.0.tgz} engines: {node: ^22.15.0 || ^24.0.0 || >=26.0.0} http-proxy@1.18.1: - resolution: {integrity: sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==} + resolution: {integrity: sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==, tarball: https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.1.tgz} engines: {node: '>=8.0.0'} http-server@14.1.1: - resolution: {integrity: sha512-+cbxadF40UXd9T01zUHgA+rlo2Bg1Srer4+B4NwIHdaGxAGGv59nYRnGGDJ9LBk7alpS0US+J+bLLdQOOkJq4A==} + resolution: {integrity: sha512-+cbxadF40UXd9T01zUHgA+rlo2Bg1Srer4+B4NwIHdaGxAGGv59nYRnGGDJ9LBk7alpS0US+J+bLLdQOOkJq4A==, tarball: https://registry.npmjs.org/http-server/-/http-server-14.1.1.tgz} engines: {node: '>=12'} hasBin: true https-proxy-agent@5.0.1: - resolution: {integrity: sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==} + resolution: {integrity: sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==, tarball: https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz} engines: {node: '>= 6'} https-proxy-agent@7.0.6: - resolution: {integrity: sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==} + resolution: {integrity: sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==, tarball: https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz} engines: {node: '>= 14'} https-proxy-agent@9.1.0: - resolution: {integrity: sha512-ag87y7cJJ9/3+GxFr8Oy4O5faDsGRGnBGsJj/YjOSsSx/5eadKLYTMPlzuR6obgoCDDm0abAAZitXXQkMOPSpA==} + resolution: {integrity: sha512-ag87y7cJJ9/3+GxFr8Oy4O5faDsGRGnBGsJj/YjOSsSx/5eadKLYTMPlzuR6obgoCDDm0abAAZitXXQkMOPSpA==, tarball: https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-9.1.0.tgz} engines: {node: '>= 20'} httpxy@0.5.5: - resolution: {integrity: sha512-uDjmnPyp1q4Sgzf3w+J/Fc6UqcCEj0x4Wjp7OqK5dGhNeDgpyrAmnS6ey8QWrX3SWDon2DMKf9sBa5X9+CVyMA==} + resolution: {integrity: sha512-uDjmnPyp1q4Sgzf3w+J/Fc6UqcCEj0x4Wjp7OqK5dGhNeDgpyrAmnS6ey8QWrX3SWDon2DMKf9sBa5X9+CVyMA==, tarball: https://registry.npmjs.org/httpxy/-/httpxy-0.5.5.tgz} human-signals@2.1.0: - resolution: {integrity: sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==} + resolution: {integrity: sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==, tarball: https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz} engines: {node: '>=10.17.0'} human-signals@5.0.0: - resolution: {integrity: sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==} + resolution: {integrity: sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==, tarball: https://registry.npmjs.org/human-signals/-/human-signals-5.0.0.tgz} engines: {node: '>=16.17.0'} human-signals@8.0.1: - resolution: {integrity: sha512-eKCa6bwnJhvxj14kZk5NCPc6Hb6BdsU9DZcOnmQKSnO1VKrfV0zCvtttPZUsBvjmNDn8rpcJfpwSYnHBjc95MQ==} + resolution: {integrity: sha512-eKCa6bwnJhvxj14kZk5NCPc6Hb6BdsU9DZcOnmQKSnO1VKrfV0zCvtttPZUsBvjmNDn8rpcJfpwSYnHBjc95MQ==, tarball: https://registry.npmjs.org/human-signals/-/human-signals-8.0.1.tgz} engines: {node: '>=18.18.0'} husky@9.1.7: - resolution: {integrity: sha512-5gs5ytaNjBrh5Ow3zrvdUUY+0VxIuWVL4i9irt6friV+BqdCfmV11CQTWMiBYWHbXhco+J1kHfTOUkePhCDvMA==} + resolution: {integrity: sha512-5gs5ytaNjBrh5Ow3zrvdUUY+0VxIuWVL4i9irt6friV+BqdCfmV11CQTWMiBYWHbXhco+J1kHfTOUkePhCDvMA==, tarball: https://registry.npmjs.org/husky/-/husky-9.1.7.tgz} engines: {node: '>=18'} hasBin: true hyperdyperid@1.2.0: - resolution: {integrity: sha512-Y93lCzHYgGWdrJ66yIktxiaGULYc6oGiABxhcO5AufBeOyoIdZF7bIfLaOrbM0iGIOXQQgxxRrFEnb+Y6w1n4A==} + resolution: {integrity: sha512-Y93lCzHYgGWdrJ66yIktxiaGULYc6oGiABxhcO5AufBeOyoIdZF7bIfLaOrbM0iGIOXQQgxxRrFEnb+Y6w1n4A==, tarball: https://registry.npmjs.org/hyperdyperid/-/hyperdyperid-1.2.0.tgz} engines: {node: '>=10.18'} iconv-lite@0.4.24: - resolution: {integrity: sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==} + resolution: {integrity: sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==, tarball: https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz} engines: {node: '>=0.10.0'} iconv-lite@0.6.3: - resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==} + resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==, tarball: https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz} engines: {node: '>=0.10.0'} iconv-lite@0.7.3: - resolution: {integrity: sha512-IKXpvIzjnC9XTAUbVBcMfGS0EPaIXtW6v+zr+RRp+hqULEpo0owZax6wyRwPOJbWbzjYspQwusTsfVr0ifh4uQ==} + resolution: {integrity: sha512-IKXpvIzjnC9XTAUbVBcMfGS0EPaIXtW6v+zr+RRp+hqULEpo0owZax6wyRwPOJbWbzjYspQwusTsfVr0ifh4uQ==, tarball: https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.3.tgz} engines: {node: '>=0.10.0'} icss-replace-symbols@1.1.0: - resolution: {integrity: sha512-chIaY3Vh2mh2Q3RGXttaDIzeiPvaVXJ+C4DAh/w3c37SKZ/U6PGMmuicR2EQQp9bKG8zLMCl7I+PtIoOOPp8Gg==} + resolution: {integrity: sha512-chIaY3Vh2mh2Q3RGXttaDIzeiPvaVXJ+C4DAh/w3c37SKZ/U6PGMmuicR2EQQp9bKG8zLMCl7I+PtIoOOPp8Gg==, tarball: https://registry.npmjs.org/icss-replace-symbols/-/icss-replace-symbols-1.1.0.tgz} icss-utils@5.1.0: - resolution: {integrity: sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==} + resolution: {integrity: sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==, tarball: https://registry.npmjs.org/icss-utils/-/icss-utils-5.1.0.tgz} engines: {node: ^10 || ^12 || >= 14} peerDependencies: postcss: ^8.1.0 identity-obj-proxy@3.0.0: - resolution: {integrity: sha512-00n6YnVHKrinT9t0d9+5yZC6UBNJANpYEQvL2LlX6Ab9lnmxzIRcEmTPuyGScvl1+jKuCICX1Z0Ab1pPKKdikA==} + resolution: {integrity: sha512-00n6YnVHKrinT9t0d9+5yZC6UBNJANpYEQvL2LlX6Ab9lnmxzIRcEmTPuyGScvl1+jKuCICX1Z0Ab1pPKKdikA==, tarball: https://registry.npmjs.org/identity-obj-proxy/-/identity-obj-proxy-3.0.0.tgz} engines: {node: '>=4'} ieee754@1.2.1: - resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} + resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==, tarball: https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz} ignore@5.3.2: - resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} + resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==, tarball: https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz} engines: {node: '>= 4'} ignore@7.0.5: - resolution: {integrity: sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==} + resolution: {integrity: sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==, tarball: https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz} engines: {node: '>= 4'} ignore@7.0.6: - resolution: {integrity: sha512-BAg6QkE8W+TuQLrrw0Ugr7HegXduRuuj8/ti2kSOc+jz1dmx8/WNcjr6XGnq5YpDWxFwwaavqD0+jIUOKelTsw==} + resolution: {integrity: sha512-BAg6QkE8W+TuQLrrw0Ugr7HegXduRuuj8/ti2kSOc+jz1dmx8/WNcjr6XGnq5YpDWxFwwaavqD0+jIUOKelTsw==, tarball: https://registry.npmjs.org/ignore/-/ignore-7.0.6.tgz} engines: {node: '>= 4'} image-size@0.5.5: - resolution: {integrity: sha512-6TDAlDPZxUFCv+fuOkIoXT/V/f3Qbq8e37p+YOiYrUv3v9cc3/6x78VdfPgFVaB9dZYeLUfKgHRebpkm/oP2VQ==} + resolution: {integrity: sha512-6TDAlDPZxUFCv+fuOkIoXT/V/f3Qbq8e37p+YOiYrUv3v9cc3/6x78VdfPgFVaB9dZYeLUfKgHRebpkm/oP2VQ==, tarball: https://registry.npmjs.org/image-size/-/image-size-0.5.5.tgz} engines: {node: '>=0.10.0'} hasBin: true immutable@4.3.9: - resolution: {integrity: sha512-ObHy4YN7ycwZOUCLI1/6svfyAFu7vL8RhAvVu/bh/RZW9EPlOyDaQ9jDQWCtdqzaXUjgXZCW1migtHE7YI7UGQ==} + resolution: {integrity: sha512-ObHy4YN7ycwZOUCLI1/6svfyAFu7vL8RhAvVu/bh/RZW9EPlOyDaQ9jDQWCtdqzaXUjgXZCW1migtHE7YI7UGQ==, tarball: https://registry.npmjs.org/immutable/-/immutable-4.3.9.tgz} immutable@5.1.9: - resolution: {integrity: sha512-m8nVez3rwrgmWxtLMt1ZYXB2Lv7OKYn/disyxAlSDYAlKSlFoPPfIAmAM/M5xqL4m4C/wAPw7S2/CNaUii1Hxg==} + resolution: {integrity: sha512-m8nVez3rwrgmWxtLMt1ZYXB2Lv7OKYn/disyxAlSDYAlKSlFoPPfIAmAM/M5xqL4m4C/wAPw7S2/CNaUii1Hxg==, tarball: https://registry.npmjs.org/immutable/-/immutable-5.1.9.tgz} import-cwd@3.0.0: - resolution: {integrity: sha512-4pnzH16plW+hgvRECbDWpQl3cqtvSofHWh44met7ESfZ8UZOWWddm8hEyDTqREJ9RbYHY8gi8DqmaelApoOGMg==} + resolution: {integrity: sha512-4pnzH16plW+hgvRECbDWpQl3cqtvSofHWh44met7ESfZ8UZOWWddm8hEyDTqREJ9RbYHY8gi8DqmaelApoOGMg==, tarball: https://registry.npmjs.org/import-cwd/-/import-cwd-3.0.0.tgz} engines: {node: '>=8'} import-fresh@3.3.1: - resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==} + resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==, tarball: https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz} engines: {node: '>=6'} import-from@3.0.0: - resolution: {integrity: sha512-CiuXOFFSzkU5x/CR0+z7T91Iht4CXgfCxVOFRhh2Zyhg5wOpWvvDLQUsWl+gcN+QscYBjez8hDCt85O7RLDttQ==} + resolution: {integrity: sha512-CiuXOFFSzkU5x/CR0+z7T91Iht4CXgfCxVOFRhh2Zyhg5wOpWvvDLQUsWl+gcN+QscYBjez8hDCt85O7RLDttQ==, tarball: https://registry.npmjs.org/import-from/-/import-from-3.0.0.tgz} engines: {node: '>=8'} import-lazy@4.0.0: - resolution: {integrity: sha512-rKtvo6a868b5Hu3heneU+L4yEQ4jYKLtjpnPeUdK7h0yzXGmyBTypknlkCvHFBqfX9YlorEiMM6Dnq/5atfHkw==} + resolution: {integrity: sha512-rKtvo6a868b5Hu3heneU+L4yEQ4jYKLtjpnPeUdK7h0yzXGmyBTypknlkCvHFBqfX9YlorEiMM6Dnq/5atfHkw==, tarball: https://registry.npmjs.org/import-lazy/-/import-lazy-4.0.0.tgz} engines: {node: '>=8'} import-local@3.2.0: - resolution: {integrity: sha512-2SPlun1JUPWoM6t3F0dw0FkCF/jWY8kttcY4f599GLTSjh2OCuuhdTkJQsEcZzBqbXZGKMK2OqW1oZsjtf/gQA==} + resolution: {integrity: sha512-2SPlun1JUPWoM6t3F0dw0FkCF/jWY8kttcY4f599GLTSjh2OCuuhdTkJQsEcZzBqbXZGKMK2OqW1oZsjtf/gQA==, tarball: https://registry.npmjs.org/import-local/-/import-local-3.2.0.tgz} engines: {node: '>=8'} hasBin: true import-meta-resolve@4.2.0: - resolution: {integrity: sha512-Iqv2fzaTQN28s/FwZAoFq0ZSs/7hMAHJVX+w8PZl3cY19Pxk6jFFalxQoIfW2826i/fDLXv8IiEZRIT0lDuWcg==} + resolution: {integrity: sha512-Iqv2fzaTQN28s/FwZAoFq0ZSs/7hMAHJVX+w8PZl3cY19Pxk6jFFalxQoIfW2826i/fDLXv8IiEZRIT0lDuWcg==, tarball: https://registry.npmjs.org/import-meta-resolve/-/import-meta-resolve-4.2.0.tgz} imurmurhash@0.1.4: - resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} + resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==, tarball: https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz} engines: {node: '>=0.8.19'} indent-string@4.0.0: - resolution: {integrity: sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==} + resolution: {integrity: sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==, tarball: https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz} engines: {node: '>=8'} inflight@1.0.6: - resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==} + resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==, tarball: https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz} 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==} + resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==, tarball: https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz} ini@1.3.8: - resolution: {integrity: sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==} + resolution: {integrity: sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==, tarball: https://registry.npmjs.org/ini/-/ini-1.3.8.tgz} injection-js@2.6.1: - resolution: {integrity: sha512-dbR5bdhi7TWDoCye9cByZqeg/gAfamm8Vu3G1KZOTYkOif8WkuM8CD0oeDPtZYMzT5YH76JAFB7bkmyY9OJi2A==} + resolution: {integrity: sha512-dbR5bdhi7TWDoCye9cByZqeg/gAfamm8Vu3G1KZOTYkOif8WkuM8CD0oeDPtZYMzT5YH76JAFB7bkmyY9OJi2A==, tarball: https://registry.npmjs.org/injection-js/-/injection-js-2.6.1.tgz} inquirer@13.0.1: - resolution: {integrity: sha512-+Qob/OSCmHIgyFKa4S+bDk36Nudwt+zpUBGZaSttGMnvsrzbIqtNFS9RutEPc2QAzpQxBP0cV3wmY/c5Vy73qg==} + resolution: {integrity: sha512-+Qob/OSCmHIgyFKa4S+bDk36Nudwt+zpUBGZaSttGMnvsrzbIqtNFS9RutEPc2QAzpQxBP0cV3wmY/c5Vy73qg==, tarball: https://registry.npmjs.org/inquirer/-/inquirer-13.0.1.tgz} engines: {node: '>=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0'} peerDependencies: '@types/node': '>=18' @@ -10332,366 +10394,366 @@ packages: optional: true internal-slot@1.1.0: - resolution: {integrity: sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==} + resolution: {integrity: sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==, tarball: https://registry.npmjs.org/internal-slot/-/internal-slot-1.1.0.tgz} engines: {node: '>= 0.4'} internmap@1.0.1: - resolution: {integrity: sha512-lDB5YccMydFBtasVtxnZ3MRBHuaoE8GKsppq+EchKL2U4nK/DmEpPHNH8MZe5HkMtpSiTSOZwfN0tzYjO/lJEw==} + resolution: {integrity: sha512-lDB5YccMydFBtasVtxnZ3MRBHuaoE8GKsppq+EchKL2U4nK/DmEpPHNH8MZe5HkMtpSiTSOZwfN0tzYjO/lJEw==, tarball: https://registry.npmjs.org/internmap/-/internmap-1.0.1.tgz} internmap@2.0.3: - resolution: {integrity: sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==} + resolution: {integrity: sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==, tarball: https://registry.npmjs.org/internmap/-/internmap-2.0.3.tgz} engines: {node: '>=12'} ip-address@10.2.0: - resolution: {integrity: sha512-/+S6j4E9AHvW9SWMSEY9Xfy66O5PWvVEJ08O0y5JGyEKQpojb0K0GKpz/v5HJ/G0vi3D2sjGK78119oXZeE0qA==} + resolution: {integrity: sha512-/+S6j4E9AHvW9SWMSEY9Xfy66O5PWvVEJ08O0y5JGyEKQpojb0K0GKpz/v5HJ/G0vi3D2sjGK78119oXZeE0qA==, tarball: https://registry.npmjs.org/ip-address/-/ip-address-10.2.0.tgz} engines: {node: '>= 12'} ip-regex@2.1.0: - resolution: {integrity: sha512-58yWmlHpp7VYfcdTwMTvwMmqx/Elfxjd9RXTDyMsbL7lLWmhMylLEqiYVLKuLzOZqVgiWXD9MfR62Vv89VRxkw==} + resolution: {integrity: sha512-58yWmlHpp7VYfcdTwMTvwMmqx/Elfxjd9RXTDyMsbL7lLWmhMylLEqiYVLKuLzOZqVgiWXD9MfR62Vv89VRxkw==, tarball: https://registry.npmjs.org/ip-regex/-/ip-regex-2.1.0.tgz} engines: {node: '>=4'} ipaddr.js@1.9.1: - resolution: {integrity: sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==} + resolution: {integrity: sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==, tarball: https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz} engines: {node: '>= 0.10'} ipaddr.js@2.4.0: - resolution: {integrity: sha512-9VGk3HGanVE6JoZXHiCpnGy5X0jYDnN4EA4lntFPj+1vIWlFhIylq2CrrCOJH9EAhc5CYhq18F2Av2tgoAPsYQ==} + resolution: {integrity: sha512-9VGk3HGanVE6JoZXHiCpnGy5X0jYDnN4EA4lntFPj+1vIWlFhIylq2CrrCOJH9EAhc5CYhq18F2Av2tgoAPsYQ==, tarball: https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.4.0.tgz} engines: {node: '>= 10'} is-accessor-descriptor@1.0.2: - resolution: {integrity: sha512-AIbwAcazqP3R65dGvqk1V+a+vE5Fg1yu/ZKMOiBWSUIXXiwQkYmXQcVa2O0nh0tSDKDFKxG2mY7dB1Sr4hEP1g==} + resolution: {integrity: sha512-AIbwAcazqP3R65dGvqk1V+a+vE5Fg1yu/ZKMOiBWSUIXXiwQkYmXQcVa2O0nh0tSDKDFKxG2mY7dB1Sr4hEP1g==, tarball: https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.2.tgz} engines: {node: '>= 0.4'} is-array-buffer@3.0.5: - resolution: {integrity: sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==} + resolution: {integrity: sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==, tarball: https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.5.tgz} engines: {node: '>= 0.4'} is-array@1.0.1: - resolution: {integrity: sha512-gxiZ+y/u67AzpeFmAmo4CbtME/bs7J2C++su5zQzvQyaxUqVzkh69DI+jN+KZuSO6JaH6TIIU6M6LhqxMjxEpw==} + resolution: {integrity: sha512-gxiZ+y/u67AzpeFmAmo4CbtME/bs7J2C++su5zQzvQyaxUqVzkh69DI+jN+KZuSO6JaH6TIIU6M6LhqxMjxEpw==, tarball: https://registry.npmjs.org/is-array/-/is-array-1.0.1.tgz} is-arrayish@0.2.1: - resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==} + resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==, tarball: https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz} is-async-function@2.1.1: - resolution: {integrity: sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ==} + resolution: {integrity: sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ==, tarball: https://registry.npmjs.org/is-async-function/-/is-async-function-2.1.1.tgz} engines: {node: '>= 0.4'} is-bigint@1.1.0: - resolution: {integrity: sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==} + resolution: {integrity: sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==, tarball: https://registry.npmjs.org/is-bigint/-/is-bigint-1.1.0.tgz} engines: {node: '>= 0.4'} is-binary-path@2.1.0: - resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==} + resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==, tarball: https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz} engines: {node: '>=8'} is-boolean-object@1.2.2: - resolution: {integrity: sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A==} + resolution: {integrity: sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A==, tarball: https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.2.2.tgz} engines: {node: '>= 0.4'} is-buffer@1.1.6: - resolution: {integrity: sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==} + resolution: {integrity: sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==, tarball: https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz} is-callable@1.2.7: - resolution: {integrity: sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==} + resolution: {integrity: sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==, tarball: https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz} engines: {node: '>= 0.4'} is-core-module@2.16.2: - resolution: {integrity: sha512-evOr8xfXKxE6qSR0hSXL2r3sd7ALj8+7jQEUvPYcm5sgZFdJ+AYzT6yNmJenvIYQBgIGwfwz08sL8zoL7yq2BA==} + resolution: {integrity: sha512-evOr8xfXKxE6qSR0hSXL2r3sd7ALj8+7jQEUvPYcm5sgZFdJ+AYzT6yNmJenvIYQBgIGwfwz08sL8zoL7yq2BA==, tarball: https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.2.tgz} engines: {node: '>= 0.4'} is-data-descriptor@1.0.1: - resolution: {integrity: sha512-bc4NlCDiCr28U4aEsQ3Qs2491gVq4V8G7MQyws968ImqjKuYtTJXrl7Vq7jsN7Ly/C3xj5KWFrY7sHNeDkAzXw==} + resolution: {integrity: sha512-bc4NlCDiCr28U4aEsQ3Qs2491gVq4V8G7MQyws968ImqjKuYtTJXrl7Vq7jsN7Ly/C3xj5KWFrY7sHNeDkAzXw==, tarball: https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.1.tgz} engines: {node: '>= 0.4'} is-data-view@1.0.2: - resolution: {integrity: sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw==} + resolution: {integrity: sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw==, tarball: https://registry.npmjs.org/is-data-view/-/is-data-view-1.0.2.tgz} engines: {node: '>= 0.4'} is-date-object@1.1.0: - resolution: {integrity: sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==} + resolution: {integrity: sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==, tarball: https://registry.npmjs.org/is-date-object/-/is-date-object-1.1.0.tgz} engines: {node: '>= 0.4'} is-descriptor@1.0.4: - resolution: {integrity: sha512-bv5z95W0dDtLfKwDfkTNxaRxmISBD3eQBKJeVxv2AQ7MjuUnDNG7cIQqvFtMOUYhsILWHhMayWdoGqNqYYYjww==} + resolution: {integrity: sha512-bv5z95W0dDtLfKwDfkTNxaRxmISBD3eQBKJeVxv2AQ7MjuUnDNG7cIQqvFtMOUYhsILWHhMayWdoGqNqYYYjww==, tarball: https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.4.tgz} engines: {node: '>= 0.4'} is-docker@2.2.1: - resolution: {integrity: sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==} + resolution: {integrity: sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==, tarball: https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz} engines: {node: '>=8'} hasBin: true is-docker@3.0.0: - resolution: {integrity: sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==} + resolution: {integrity: sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==, tarball: https://registry.npmjs.org/is-docker/-/is-docker-3.0.0.tgz} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} hasBin: true is-document.all@1.0.0: - resolution: {integrity: sha512-+XSoyS05OdBbhFuELhgTCpFNHkpBOJqtsZfUFFpe5QTw+9Sjbh8zitxhQkYAo6wV7e1Vb8cAPvpCk9jGam/82g==} + resolution: {integrity: sha512-+XSoyS05OdBbhFuELhgTCpFNHkpBOJqtsZfUFFpe5QTw+9Sjbh8zitxhQkYAo6wV7e1Vb8cAPvpCk9jGam/82g==, tarball: https://registry.npmjs.org/is-document.all/-/is-document.all-1.0.0.tgz} engines: {node: '>= 0.4'} is-emoji-supported@0.0.5: - resolution: {integrity: sha512-WOlXUhDDHxYqcSmFZis+xWhhqXiK2SU0iYiqmth5Ip0FHLZQAt9rKL5ahnilE8/86WH8tZ3bmNNNC+bTzamqlw==} + resolution: {integrity: sha512-WOlXUhDDHxYqcSmFZis+xWhhqXiK2SU0iYiqmth5Ip0FHLZQAt9rKL5ahnilE8/86WH8tZ3bmNNNC+bTzamqlw==, tarball: https://registry.npmjs.org/is-emoji-supported/-/is-emoji-supported-0.0.5.tgz} is-extglob@2.1.1: - resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} + resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==, tarball: https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz} engines: {node: '>=0.10.0'} is-finalizationregistry@1.1.1: - resolution: {integrity: sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg==} + resolution: {integrity: sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg==, tarball: https://registry.npmjs.org/is-finalizationregistry/-/is-finalizationregistry-1.1.1.tgz} engines: {node: '>= 0.4'} is-fullwidth-code-point@3.0.0: - resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} + resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==, tarball: https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz} engines: {node: '>=8'} is-fullwidth-code-point@4.0.0: - resolution: {integrity: sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ==} + resolution: {integrity: sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ==, tarball: https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-4.0.0.tgz} engines: {node: '>=12'} is-fullwidth-code-point@5.1.0: - resolution: {integrity: sha512-5XHYaSyiqADb4RnZ1Bdad6cPp8Toise4TzEjcOYDHZkTCbKgiUl7WTUCpNWHuxmDt91wnsZBc9xinNzopv3JMQ==} + resolution: {integrity: sha512-5XHYaSyiqADb4RnZ1Bdad6cPp8Toise4TzEjcOYDHZkTCbKgiUl7WTUCpNWHuxmDt91wnsZBc9xinNzopv3JMQ==, tarball: https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-5.1.0.tgz} engines: {node: '>=18'} is-generator-fn@2.1.0: - resolution: {integrity: sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==} + resolution: {integrity: sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==, tarball: https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz} engines: {node: '>=6'} is-generator-function@1.1.2: - resolution: {integrity: sha512-upqt1SkGkODW9tsGNG5mtXTXtECizwtS2kA161M+gJPc1xdb/Ax629af6YrTwcOeQHbewrPNlE5Dx7kzvXTizA==} + resolution: {integrity: sha512-upqt1SkGkODW9tsGNG5mtXTXtECizwtS2kA161M+gJPc1xdb/Ax629af6YrTwcOeQHbewrPNlE5Dx7kzvXTizA==, tarball: https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.1.2.tgz} engines: {node: '>= 0.4'} is-glob@4.0.3: - resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} + resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==, tarball: https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz} engines: {node: '>=0.10.0'} is-in-ssh@1.0.0: - resolution: {integrity: sha512-jYa6Q9rH90kR1vKB6NM7qqd1mge3Fx4Dhw5TVlK1MUBqhEOuCagrEHMevNuCcbECmXZ0ThXkRm+Ymr51HwEPAw==} + resolution: {integrity: sha512-jYa6Q9rH90kR1vKB6NM7qqd1mge3Fx4Dhw5TVlK1MUBqhEOuCagrEHMevNuCcbECmXZ0ThXkRm+Ymr51HwEPAw==, tarball: https://registry.npmjs.org/is-in-ssh/-/is-in-ssh-1.0.0.tgz} engines: {node: '>=20'} is-inside-container@1.0.0: - resolution: {integrity: sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==} + resolution: {integrity: sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==, tarball: https://registry.npmjs.org/is-inside-container/-/is-inside-container-1.0.0.tgz} engines: {node: '>=14.16'} hasBin: true is-interactive@1.0.0: - resolution: {integrity: sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==} + resolution: {integrity: sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==, tarball: https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz} engines: {node: '>=8'} is-interactive@2.0.0: - resolution: {integrity: sha512-qP1vozQRI+BMOPcjFzrjXuQvdak2pHNUMZoeG2eRbiSqyvbEf/wQtEOTOX1guk6E3t36RkaqiSt8A/6YElNxLQ==} + resolution: {integrity: sha512-qP1vozQRI+BMOPcjFzrjXuQvdak2pHNUMZoeG2eRbiSqyvbEf/wQtEOTOX1guk6E3t36RkaqiSt8A/6YElNxLQ==, tarball: https://registry.npmjs.org/is-interactive/-/is-interactive-2.0.0.tgz} engines: {node: '>=12'} is-map@2.0.3: - resolution: {integrity: sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==} + resolution: {integrity: sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==, tarball: https://registry.npmjs.org/is-map/-/is-map-2.0.3.tgz} engines: {node: '>= 0.4'} is-module@1.0.0: - resolution: {integrity: sha512-51ypPSPCoTEIN9dy5Oy+h4pShgJmPCygKfyRCISBI+JoWT/2oJvK8QPxmwv7b/p239jXrm9M1mlQbyKJ5A152g==} + resolution: {integrity: sha512-51ypPSPCoTEIN9dy5Oy+h4pShgJmPCygKfyRCISBI+JoWT/2oJvK8QPxmwv7b/p239jXrm9M1mlQbyKJ5A152g==, tarball: https://registry.npmjs.org/is-module/-/is-module-1.0.0.tgz} is-negative-zero@2.0.3: - resolution: {integrity: sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==} + resolution: {integrity: sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==, tarball: https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.3.tgz} engines: {node: '>= 0.4'} is-network-error@1.3.2: - resolution: {integrity: sha512-PhBY86zaxNZUuWP6h13Vu5oFe0XY6/UlKzQnYFELzGVHygP3MxmvTfYSG7GN3aIab/iWudSMgjSnG9Dq+nHrgA==} + resolution: {integrity: sha512-PhBY86zaxNZUuWP6h13Vu5oFe0XY6/UlKzQnYFELzGVHygP3MxmvTfYSG7GN3aIab/iWudSMgjSnG9Dq+nHrgA==, tarball: https://registry.npmjs.org/is-network-error/-/is-network-error-1.3.2.tgz} engines: {node: '>=16'} is-number-object@1.1.1: - resolution: {integrity: sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==} + resolution: {integrity: sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==, tarball: https://registry.npmjs.org/is-number-object/-/is-number-object-1.1.1.tgz} engines: {node: '>= 0.4'} is-number@3.0.0: - resolution: {integrity: sha512-4cboCqIpliH+mAvFNegjZQ4kgKc3ZUhQVr3HvWbSh5q3WH2v82ct+T2Y1hdU5Gdtorx/cLifQjqCbL7bpznLTg==} + resolution: {integrity: sha512-4cboCqIpliH+mAvFNegjZQ4kgKc3ZUhQVr3HvWbSh5q3WH2v82ct+T2Y1hdU5Gdtorx/cLifQjqCbL7bpznLTg==, tarball: https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz} engines: {node: '>=0.10.0'} is-number@7.0.0: - resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} + resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==, tarball: https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz} engines: {node: '>=0.12.0'} is-plain-obj@3.0.0: - resolution: {integrity: sha512-gwsOE28k+23GP1B6vFl1oVh/WOzmawBrKwo5Ev6wMKzPkaXaCDIQKzLnvsA42DRlbVTWorkgTKIviAKCWkfUwA==} + resolution: {integrity: sha512-gwsOE28k+23GP1B6vFl1oVh/WOzmawBrKwo5Ev6wMKzPkaXaCDIQKzLnvsA42DRlbVTWorkgTKIviAKCWkfUwA==, tarball: https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-3.0.0.tgz} engines: {node: '>=10'} is-plain-obj@4.1.0: - resolution: {integrity: sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==} + resolution: {integrity: sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==, tarball: https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-4.1.0.tgz} engines: {node: '>=12'} is-plain-object@2.0.4: - resolution: {integrity: sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==} + resolution: {integrity: sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==, tarball: https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz} engines: {node: '>=0.10.0'} is-plain-object@5.0.0: - resolution: {integrity: sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==} + resolution: {integrity: sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==, tarball: https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz} engines: {node: '>=0.10.0'} is-potential-custom-element-name@1.0.1: - resolution: {integrity: sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==} + resolution: {integrity: sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==, tarball: https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz} is-promise@4.0.0: - resolution: {integrity: sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==} + resolution: {integrity: sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==, tarball: https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz} is-reference@1.2.1: - resolution: {integrity: sha512-U82MsXXiFIrjCK4otLT+o2NA2Cd2g5MLoOVXUZjIOhLurrRxpEXzI8O0KZHr3IjLvlAH1kTPYSuqer5T9ZVBKQ==} + resolution: {integrity: sha512-U82MsXXiFIrjCK4otLT+o2NA2Cd2g5MLoOVXUZjIOhLurrRxpEXzI8O0KZHr3IjLvlAH1kTPYSuqer5T9ZVBKQ==, tarball: https://registry.npmjs.org/is-reference/-/is-reference-1.2.1.tgz} is-regex@1.2.1: - resolution: {integrity: sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==} + resolution: {integrity: sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==, tarball: https://registry.npmjs.org/is-regex/-/is-regex-1.2.1.tgz} engines: {node: '>= 0.4'} is-set@2.0.3: - resolution: {integrity: sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==} + resolution: {integrity: sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==, tarball: https://registry.npmjs.org/is-set/-/is-set-2.0.3.tgz} engines: {node: '>= 0.4'} is-shared-array-buffer@1.0.4: - resolution: {integrity: sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A==} + resolution: {integrity: sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A==, tarball: https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.4.tgz} engines: {node: '>= 0.4'} is-stream@2.0.1: - resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==} + resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==, tarball: https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz} engines: {node: '>=8'} is-stream@3.0.0: - resolution: {integrity: sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==} + resolution: {integrity: sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==, tarball: https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} is-stream@4.0.1: - resolution: {integrity: sha512-Dnz92NInDqYckGEUJv689RbRiTSEHCQ7wOVeALbkOz999YpqT46yMRIGtSNl2iCL1waAZSx40+h59NV/EwzV/A==} + resolution: {integrity: sha512-Dnz92NInDqYckGEUJv689RbRiTSEHCQ7wOVeALbkOz999YpqT46yMRIGtSNl2iCL1waAZSx40+h59NV/EwzV/A==, tarball: https://registry.npmjs.org/is-stream/-/is-stream-4.0.1.tgz} engines: {node: '>=18'} is-string@1.1.1: - resolution: {integrity: sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA==} + resolution: {integrity: sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA==, tarball: https://registry.npmjs.org/is-string/-/is-string-1.1.1.tgz} engines: {node: '>= 0.4'} is-subset@0.1.1: - resolution: {integrity: sha512-6Ybun0IkarhmEqxXCNw/C0bna6Zb/TkfUX9UbwJtK6ObwAVCxmAP308WWTHviM/zAqXk05cdhYsUsZeGQh99iw==} + resolution: {integrity: sha512-6Ybun0IkarhmEqxXCNw/C0bna6Zb/TkfUX9UbwJtK6ObwAVCxmAP308WWTHviM/zAqXk05cdhYsUsZeGQh99iw==, tarball: https://registry.npmjs.org/is-subset/-/is-subset-0.1.1.tgz} is-symbol@1.1.1: - resolution: {integrity: sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w==} + resolution: {integrity: sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w==, tarball: https://registry.npmjs.org/is-symbol/-/is-symbol-1.1.1.tgz} engines: {node: '>= 0.4'} is-typed-array@1.1.15: - resolution: {integrity: sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==} + resolution: {integrity: sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==, tarball: https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.15.tgz} engines: {node: '>= 0.4'} is-unicode-supported@0.1.0: - resolution: {integrity: sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==} + resolution: {integrity: sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==, tarball: https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz} engines: {node: '>=10'} is-unicode-supported@2.1.0: - resolution: {integrity: sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ==} + resolution: {integrity: sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ==, tarball: https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-2.1.0.tgz} engines: {node: '>=18'} is-url@1.2.4: - resolution: {integrity: sha512-ITvGim8FhRiYe4IQ5uHSkj7pVaPDrCTkNd3yq3cV7iZAcJdHTUMPMEHcqSOy9xZ9qFenQCvi+2wjH9a1nXqHww==} + resolution: {integrity: sha512-ITvGim8FhRiYe4IQ5uHSkj7pVaPDrCTkNd3yq3cV7iZAcJdHTUMPMEHcqSOy9xZ9qFenQCvi+2wjH9a1nXqHww==, tarball: https://registry.npmjs.org/is-url/-/is-url-1.2.4.tgz} is-weakmap@2.0.2: - resolution: {integrity: sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==} + resolution: {integrity: sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==, tarball: https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.2.tgz} engines: {node: '>= 0.4'} is-weakref@1.1.1: - resolution: {integrity: sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew==} + resolution: {integrity: sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew==, tarball: https://registry.npmjs.org/is-weakref/-/is-weakref-1.1.1.tgz} engines: {node: '>= 0.4'} is-weakset@2.0.4: - resolution: {integrity: sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==} + resolution: {integrity: sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==, tarball: https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.4.tgz} engines: {node: '>= 0.4'} is-what@3.14.1: - resolution: {integrity: sha512-sNxgpk9793nzSs7bA6JQJGeIuRBQhAaNGG77kzYQgMkrID+lS6SlK07K5LaptscDlSaIgH+GPFzf+d75FVxozA==} + resolution: {integrity: sha512-sNxgpk9793nzSs7bA6JQJGeIuRBQhAaNGG77kzYQgMkrID+lS6SlK07K5LaptscDlSaIgH+GPFzf+d75FVxozA==, tarball: https://registry.npmjs.org/is-what/-/is-what-3.14.1.tgz} is-what@4.1.16: - resolution: {integrity: sha512-ZhMwEosbFJkA0YhFnNDgTM4ZxDRsS6HqTo7qsZM08fehyRYIYa0yHu5R6mgo1n/8MgaPBXiPimPD77baVFYg+A==} + resolution: {integrity: sha512-ZhMwEosbFJkA0YhFnNDgTM4ZxDRsS6HqTo7qsZM08fehyRYIYa0yHu5R6mgo1n/8MgaPBXiPimPD77baVFYg+A==, tarball: https://registry.npmjs.org/is-what/-/is-what-4.1.16.tgz} engines: {node: '>=12.13'} is-windows@1.0.2: - resolution: {integrity: sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==} + resolution: {integrity: sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==, tarball: https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz} engines: {node: '>=0.10.0'} is-wsl@2.2.0: - resolution: {integrity: sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==} + resolution: {integrity: sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==, tarball: https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz} engines: {node: '>=8'} is-wsl@3.1.0: - resolution: {integrity: sha512-UcVfVfaK4Sc4m7X3dUSoHoozQGBEFeDC+zVo06t98xe8CzHSZZBekNXH+tu0NalHolcJ/QAGqS46Hef7QXBIMw==} + resolution: {integrity: sha512-UcVfVfaK4Sc4m7X3dUSoHoozQGBEFeDC+zVo06t98xe8CzHSZZBekNXH+tu0NalHolcJ/QAGqS46Hef7QXBIMw==, tarball: https://registry.npmjs.org/is-wsl/-/is-wsl-3.1.0.tgz} engines: {node: '>=16'} is-wsl@3.1.1: - resolution: {integrity: sha512-e6rvdUCiQCAuumZslxRJWR/Doq4VpPR82kqclvcS0efgt430SlGIk05vdCN58+VrzgtIcfNODjozVielycD4Sw==} + resolution: {integrity: sha512-e6rvdUCiQCAuumZslxRJWR/Doq4VpPR82kqclvcS0efgt430SlGIk05vdCN58+VrzgtIcfNODjozVielycD4Sw==, tarball: https://registry.npmjs.org/is-wsl/-/is-wsl-3.1.1.tgz} engines: {node: '>=16'} is2@2.0.1: - resolution: {integrity: sha512-+WaJvnaA7aJySz2q/8sLjMb2Mw14KTplHmSwcSpZ/fWJPkUmqw3YTzSWbPJ7OAwRvdYTWF2Wg+yYJ1AdP5Z8CA==} + resolution: {integrity: sha512-+WaJvnaA7aJySz2q/8sLjMb2Mw14KTplHmSwcSpZ/fWJPkUmqw3YTzSWbPJ7OAwRvdYTWF2Wg+yYJ1AdP5Z8CA==, tarball: https://registry.npmjs.org/is2/-/is2-2.0.1.tgz} engines: {node: '>=v0.10.0'} isarray@1.0.0: - resolution: {integrity: sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==} + resolution: {integrity: sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==, tarball: https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz} isarray@2.0.5: - resolution: {integrity: sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==} + resolution: {integrity: sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==, tarball: https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz} iselement@1.1.4: - resolution: {integrity: sha512-4Q519eWmbHO1pbimiz7H1iJRUHVmAmfh0viSsUD+oAwVO4ntZt7gpf8i8AShVBTyOvRTZNYNBpUxOIvwZR+ffw==} + resolution: {integrity: sha512-4Q519eWmbHO1pbimiz7H1iJRUHVmAmfh0viSsUD+oAwVO4ntZt7gpf8i8AShVBTyOvRTZNYNBpUxOIvwZR+ffw==, tarball: https://registry.npmjs.org/iselement/-/iselement-1.1.4.tgz} isexe@2.0.0: - resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==, tarball: https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz} isobject@3.0.1: - resolution: {integrity: sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==} + resolution: {integrity: sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==, tarball: https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz} engines: {node: '>=0.10.0'} isomorphic-ws@5.0.0: - resolution: {integrity: sha512-muId7Zzn9ywDsyXgTIafTry2sV3nySZeUDe6YedVd1Hvuuep5AsIlqK+XefWpYTyJG5e503F2xIuT2lcU6rCSw==} + resolution: {integrity: sha512-muId7Zzn9ywDsyXgTIafTry2sV3nySZeUDe6YedVd1Hvuuep5AsIlqK+XefWpYTyJG5e503F2xIuT2lcU6rCSw==, tarball: https://registry.npmjs.org/isomorphic-ws/-/isomorphic-ws-5.0.0.tgz} peerDependencies: ws: '*' isomorphic.js@0.2.5: - resolution: {integrity: sha512-PIeMbHqMt4DnUP3MA/Flc0HElYjMXArsw1qwJZcm9sqR8mq3l8NYizFMty0pWwE/tzIGH3EKK5+jes5mAr85yw==} + resolution: {integrity: sha512-PIeMbHqMt4DnUP3MA/Flc0HElYjMXArsw1qwJZcm9sqR8mq3l8NYizFMty0pWwE/tzIGH3EKK5+jes5mAr85yw==, tarball: https://registry.npmjs.org/isomorphic.js/-/isomorphic.js-0.2.5.tgz} istanbul-lib-coverage@3.2.2: - resolution: {integrity: sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==} + resolution: {integrity: sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==, tarball: https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz} engines: {node: '>=8'} istanbul-lib-instrument@6.0.3: - resolution: {integrity: sha512-Vtgk7L/R2JHyyGW07spoFlB8/lpjiOLTjMdms6AFMraYt3BaJauod/NGrfnVG/y4Ix1JEuMRPDPEj2ua+zz1/Q==} + resolution: {integrity: sha512-Vtgk7L/R2JHyyGW07spoFlB8/lpjiOLTjMdms6AFMraYt3BaJauod/NGrfnVG/y4Ix1JEuMRPDPEj2ua+zz1/Q==, tarball: https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.3.tgz} engines: {node: '>=10'} istanbul-lib-report@3.0.1: - resolution: {integrity: sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==} + resolution: {integrity: sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==, tarball: https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz} engines: {node: '>=10'} istanbul-lib-source-maps@5.0.6: - resolution: {integrity: sha512-yg2d+Em4KizZC5niWhQaIomgf5WlL4vOOjZ5xGCmF8SnPE/mDWWXgvRExdcpCgh9lLRRa1/fSYp2ymmbJ1pI+A==} + resolution: {integrity: sha512-yg2d+Em4KizZC5niWhQaIomgf5WlL4vOOjZ5xGCmF8SnPE/mDWWXgvRExdcpCgh9lLRRa1/fSYp2ymmbJ1pI+A==, tarball: https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-5.0.6.tgz} engines: {node: '>=10'} istanbul-reports@3.2.0: - resolution: {integrity: sha512-HGYWWS/ehqTV3xN10i23tkPkpH46MLCIMFNCaaKNavAXTF1RkqxawEPtnjnGZ6XKSInBKkiOA5BKS+aZiY3AvA==} + resolution: {integrity: sha512-HGYWWS/ehqTV3xN10i23tkPkpH46MLCIMFNCaaKNavAXTF1RkqxawEPtnjnGZ6XKSInBKkiOA5BKS+aZiY3AvA==, tarball: https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.2.0.tgz} engines: {node: '>=8'} iterator.prototype@1.1.5: - resolution: {integrity: sha512-H0dkQoCa3b2VEeKQBOxFph+JAbcrQdE7KC0UkqwpLmv2EC4P41QXP+rqo9wYodACiG5/WM5s9oDApTU8utwj9g==} + resolution: {integrity: sha512-H0dkQoCa3b2VEeKQBOxFph+JAbcrQdE7KC0UkqwpLmv2EC4P41QXP+rqo9wYodACiG5/WM5s9oDApTU8utwj9g==, tarball: https://registry.npmjs.org/iterator.prototype/-/iterator.prototype-1.1.5.tgz} engines: {node: '>= 0.4'} jackspeak@3.4.3: - resolution: {integrity: sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==} + resolution: {integrity: sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==, tarball: https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz} jake@10.9.4: - resolution: {integrity: sha512-wpHYzhxiVQL+IV05BLE2Xn34zW1S223hvjtqk0+gsPrwd/8JNLXJgZZM/iPFsYc1xyphF+6M6EvdE5E9MBGkDA==} + resolution: {integrity: sha512-wpHYzhxiVQL+IV05BLE2Xn34zW1S223hvjtqk0+gsPrwd/8JNLXJgZZM/iPFsYc1xyphF+6M6EvdE5E9MBGkDA==, tarball: https://registry.npmjs.org/jake/-/jake-10.9.4.tgz} engines: {node: '>=10'} hasBin: true jest-changed-files@30.2.0: - resolution: {integrity: sha512-L8lR1ChrRnSdfeOvTrwZMlnWV8G/LLjQ0nG9MBclwWZidA2N5FviRki0Bvh20WRMOX31/JYvzdqTJrk5oBdydQ==} + resolution: {integrity: sha512-L8lR1ChrRnSdfeOvTrwZMlnWV8G/LLjQ0nG9MBclwWZidA2N5FviRki0Bvh20WRMOX31/JYvzdqTJrk5oBdydQ==, tarball: https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-30.2.0.tgz} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} jest-circus@30.2.0: - resolution: {integrity: sha512-Fh0096NC3ZkFx05EP2OXCxJAREVxj1BcW/i6EWqqymcgYKWjyyDpral3fMxVcHXg6oZM7iULer9wGRFvfpl+Tg==} + resolution: {integrity: sha512-Fh0096NC3ZkFx05EP2OXCxJAREVxj1BcW/i6EWqqymcgYKWjyyDpral3fMxVcHXg6oZM7iULer9wGRFvfpl+Tg==, tarball: https://registry.npmjs.org/jest-circus/-/jest-circus-30.2.0.tgz} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} jest-circus@30.4.2: - resolution: {integrity: sha512-rvHH7VlY6LgbJXJTQ87GW62g1FntOtbhh0zT+v04kC+pgL6aBKyYINXxWukCpj3dcIBMw5/XUbtDS9dU9JTXeQ==} + resolution: {integrity: sha512-rvHH7VlY6LgbJXJTQ87GW62g1FntOtbhh0zT+v04kC+pgL6aBKyYINXxWukCpj3dcIBMw5/XUbtDS9dU9JTXeQ==, tarball: https://registry.npmjs.org/jest-circus/-/jest-circus-30.4.2.tgz} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} jest-cli@30.2.0: - resolution: {integrity: sha512-Os9ukIvADX/A9sLt6Zse3+nmHtHaE6hqOsjQtNiugFTbKRHYIYtZXNGNK9NChseXy7djFPjndX1tL0sCTlfpAA==} + resolution: {integrity: sha512-Os9ukIvADX/A9sLt6Zse3+nmHtHaE6hqOsjQtNiugFTbKRHYIYtZXNGNK9NChseXy7djFPjndX1tL0sCTlfpAA==, tarball: https://registry.npmjs.org/jest-cli/-/jest-cli-30.2.0.tgz} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} hasBin: true peerDependencies: @@ -10701,7 +10763,7 @@ packages: optional: true jest-config@30.2.0: - resolution: {integrity: sha512-g4WkyzFQVWHtu6uqGmQR4CQxz/CH3yDSlhzXMWzNjDx843gYjReZnMRanjRCq5XZFuQrGDxgUaiYWE8BRfVckA==} + resolution: {integrity: sha512-g4WkyzFQVWHtu6uqGmQR4CQxz/CH3yDSlhzXMWzNjDx843gYjReZnMRanjRCq5XZFuQrGDxgUaiYWE8BRfVckA==, tarball: https://registry.npmjs.org/jest-config/-/jest-config-30.2.0.tgz} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} peerDependencies: '@types/node': '*' @@ -10716,7 +10778,7 @@ packages: optional: true jest-config@30.4.2: - resolution: {integrity: sha512-rNHAShJQqQwFNoL0hbf3BphSBOWnpOUAKvidLS/AjNVLPfoj5mSf4jQMfW3cYOs6hXeZC7nF7mDHaBnbxELOzg==} + resolution: {integrity: sha512-rNHAShJQqQwFNoL0hbf3BphSBOWnpOUAKvidLS/AjNVLPfoj5mSf4jQMfW3cYOs6hXeZC7nF7mDHaBnbxELOzg==, tarball: https://registry.npmjs.org/jest-config/-/jest-config-30.4.2.tgz} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} peerDependencies: '@types/node': '*' @@ -10731,31 +10793,31 @@ packages: optional: true jest-diff@30.2.0: - resolution: {integrity: sha512-dQHFo3Pt4/NLlG5z4PxZ/3yZTZ1C7s9hveiOj+GCN+uT109NC2QgsoVZsVOAvbJ3RgKkvyLGXZV9+piDpWbm6A==} + resolution: {integrity: sha512-dQHFo3Pt4/NLlG5z4PxZ/3yZTZ1C7s9hveiOj+GCN+uT109NC2QgsoVZsVOAvbJ3RgKkvyLGXZV9+piDpWbm6A==, tarball: https://registry.npmjs.org/jest-diff/-/jest-diff-30.2.0.tgz} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} jest-diff@30.4.1: - resolution: {integrity: sha512-CRpFK0RtLriVDGcPPAnR6HMVI8bSR2jnUIgralhauzYQZIb4RH9AtEInTuQr65LmmGggGcRT6HIASxwqsVsmlA==} + resolution: {integrity: sha512-CRpFK0RtLriVDGcPPAnR6HMVI8bSR2jnUIgralhauzYQZIb4RH9AtEInTuQr65LmmGggGcRT6HIASxwqsVsmlA==, tarball: https://registry.npmjs.org/jest-diff/-/jest-diff-30.4.1.tgz} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} jest-docblock@30.2.0: - resolution: {integrity: sha512-tR/FFgZKS1CXluOQzZvNH3+0z9jXr3ldGSD8bhyuxvlVUwbeLOGynkunvlTMxchC5urrKndYiwCFC0DLVjpOCA==} + resolution: {integrity: sha512-tR/FFgZKS1CXluOQzZvNH3+0z9jXr3ldGSD8bhyuxvlVUwbeLOGynkunvlTMxchC5urrKndYiwCFC0DLVjpOCA==, tarball: https://registry.npmjs.org/jest-docblock/-/jest-docblock-30.2.0.tgz} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} jest-docblock@30.4.0: - resolution: {integrity: sha512-ZPMabUZCx5MpbZ2eBYSvZ0J8fvo3dR9oM+eeUpb3aKNQFuS2tu3Duw1TNlMoP8k3WQgKGJuhcMFvwcVuq6T7oA==} + resolution: {integrity: sha512-ZPMabUZCx5MpbZ2eBYSvZ0J8fvo3dR9oM+eeUpb3aKNQFuS2tu3Duw1TNlMoP8k3WQgKGJuhcMFvwcVuq6T7oA==, tarball: https://registry.npmjs.org/jest-docblock/-/jest-docblock-30.4.0.tgz} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} jest-each@30.2.0: - resolution: {integrity: sha512-lpWlJlM7bCUf1mfmuqTA8+j2lNURW9eNafOy99knBM01i5CQeY5UH1vZjgT9071nDJac1M4XsbyI44oNOdhlDQ==} + resolution: {integrity: sha512-lpWlJlM7bCUf1mfmuqTA8+j2lNURW9eNafOy99knBM01i5CQeY5UH1vZjgT9071nDJac1M4XsbyI44oNOdhlDQ==, tarball: https://registry.npmjs.org/jest-each/-/jest-each-30.2.0.tgz} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} jest-each@30.4.1: - resolution: {integrity: sha512-/8MJbH6fuj48TstjrMf+u/pd06Qezz5xOXvZA6442heNOWr8bdeoGZX2d9fCn028CoMgYmroH9//zky5GfyYmA==} + resolution: {integrity: sha512-/8MJbH6fuj48TstjrMf+u/pd06Qezz5xOXvZA6442heNOWr8bdeoGZX2d9fCn028CoMgYmroH9//zky5GfyYmA==, tarball: https://registry.npmjs.org/jest-each/-/jest-each-30.4.1.tgz} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} jest-environment-jsdom@29.7.0: - resolution: {integrity: sha512-k9iQbsf9OyOfdzWH8HDmrRT0gSIcX+FLNW7IQq94tFX0gynPwqDTW0Ho6iMVNjGz/nb+l/vW3dWM2bbLLpkbXA==} + resolution: {integrity: sha512-k9iQbsf9OyOfdzWH8HDmrRT0gSIcX+FLNW7IQq94tFX0gynPwqDTW0Ho6iMVNjGz/nb+l/vW3dWM2bbLLpkbXA==, tarball: https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-29.7.0.tgz} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} peerDependencies: canvas: ^2.5.0 @@ -10764,70 +10826,70 @@ packages: optional: true jest-environment-node@30.2.0: - resolution: {integrity: sha512-ElU8v92QJ9UrYsKrxDIKCxu6PfNj4Hdcktcn0JX12zqNdqWHB0N+hwOnnBBXvjLd2vApZtuLUGs1QSY+MsXoNA==} + resolution: {integrity: sha512-ElU8v92QJ9UrYsKrxDIKCxu6PfNj4Hdcktcn0JX12zqNdqWHB0N+hwOnnBBXvjLd2vApZtuLUGs1QSY+MsXoNA==, tarball: https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-30.2.0.tgz} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} jest-environment-node@30.4.1: - resolution: {integrity: sha512-4FZYVOk85hz2AyT6BbarKy9u37g6DbrDyCdFhsnDdXqyrueYQvB+0zO4f/kqLCRD0BsPRXPMNJeQwihKZV8naw==} + resolution: {integrity: sha512-4FZYVOk85hz2AyT6BbarKy9u37g6DbrDyCdFhsnDdXqyrueYQvB+0zO4f/kqLCRD0BsPRXPMNJeQwihKZV8naw==, tarball: https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-30.4.1.tgz} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} jest-haste-map@30.2.0: - resolution: {integrity: sha512-sQA/jCb9kNt+neM0anSj6eZhLZUIhQgwDt7cPGjumgLM4rXsfb9kpnlacmvZz3Q5tb80nS+oG/if+NBKrHC+Xw==} + resolution: {integrity: sha512-sQA/jCb9kNt+neM0anSj6eZhLZUIhQgwDt7cPGjumgLM4rXsfb9kpnlacmvZz3Q5tb80nS+oG/if+NBKrHC+Xw==, tarball: https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-30.2.0.tgz} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} jest-haste-map@30.4.1: - resolution: {integrity: sha512-rFrcONd8jeFsyw+Z9CrScJgglRf2+NFmNam8dKu7n+SoHqNYT47mn0DdEcVUZJpvh7Iz6/si7f7yUH7GJHVgnw==} + resolution: {integrity: sha512-rFrcONd8jeFsyw+Z9CrScJgglRf2+NFmNam8dKu7n+SoHqNYT47mn0DdEcVUZJpvh7Iz6/si7f7yUH7GJHVgnw==, tarball: https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-30.4.1.tgz} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} jest-html-reporters@3.1.5: - resolution: {integrity: sha512-uw5w9j8nqxrki3VfXkHwz1bU0coyHuNNOmSSzJK8hLvImYNvIy4SHhTs9pPcI8E0nEnbqIXKxdRUb6qsHZUNIw==} + resolution: {integrity: sha512-uw5w9j8nqxrki3VfXkHwz1bU0coyHuNNOmSSzJK8hLvImYNvIy4SHhTs9pPcI8E0nEnbqIXKxdRUb6qsHZUNIw==, tarball: https://registry.npmjs.org/jest-html-reporters/-/jest-html-reporters-3.1.5.tgz} jest-junit@16.0.0: - resolution: {integrity: sha512-A94mmw6NfJab4Fg/BlvVOUXzXgF0XIH6EmTgJ5NDPp4xoKq0Kr7sErb+4Xs9nZvu58pJojz5RFGpqnZYJTrRfQ==} + resolution: {integrity: sha512-A94mmw6NfJab4Fg/BlvVOUXzXgF0XIH6EmTgJ5NDPp4xoKq0Kr7sErb+4Xs9nZvu58pJojz5RFGpqnZYJTrRfQ==, tarball: https://registry.npmjs.org/jest-junit/-/jest-junit-16.0.0.tgz} engines: {node: '>=10.12.0'} jest-leak-detector@30.2.0: - resolution: {integrity: sha512-M6jKAjyzjHG0SrQgwhgZGy9hFazcudwCNovY/9HPIicmNSBuockPSedAP9vlPK6ONFJ1zfyH/M2/YYJxOz5cdQ==} + resolution: {integrity: sha512-M6jKAjyzjHG0SrQgwhgZGy9hFazcudwCNovY/9HPIicmNSBuockPSedAP9vlPK6ONFJ1zfyH/M2/YYJxOz5cdQ==, tarball: https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-30.2.0.tgz} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} jest-leak-detector@30.4.1: - resolution: {integrity: sha512-IpmyiioeHxiWDhesHnUFmOxcTzwCwKpgACgWajtAP+nYQXiY7DakTxB6Bx9JFiRMljr0AX1PvnQdaU1KFoz6NQ==} + resolution: {integrity: sha512-IpmyiioeHxiWDhesHnUFmOxcTzwCwKpgACgWajtAP+nYQXiY7DakTxB6Bx9JFiRMljr0AX1PvnQdaU1KFoz6NQ==, tarball: https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-30.4.1.tgz} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} jest-matcher-utils@30.2.0: - resolution: {integrity: sha512-dQ94Nq4dbzmUWkQ0ANAWS9tBRfqCrn0bV9AMYdOi/MHW726xn7eQmMeRTpX2ViC00bpNaWXq+7o4lIQ3AX13Hg==} + resolution: {integrity: sha512-dQ94Nq4dbzmUWkQ0ANAWS9tBRfqCrn0bV9AMYdOi/MHW726xn7eQmMeRTpX2ViC00bpNaWXq+7o4lIQ3AX13Hg==, tarball: https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-30.2.0.tgz} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} jest-matcher-utils@30.4.1: - resolution: {integrity: sha512-zvYfX5CaeEkFrrLS9suWe9rvJrm9J1Iv3ua8kIBv9GEPzcnsfBf0bob37la7s67fs0nlBC3EuvkOLnXQKxtx4A==} + resolution: {integrity: sha512-zvYfX5CaeEkFrrLS9suWe9rvJrm9J1Iv3ua8kIBv9GEPzcnsfBf0bob37la7s67fs0nlBC3EuvkOLnXQKxtx4A==, tarball: https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-30.4.1.tgz} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} jest-message-util@29.7.0: - resolution: {integrity: sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w==} + resolution: {integrity: sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w==, tarball: https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.7.0.tgz} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} jest-message-util@30.2.0: - resolution: {integrity: sha512-y4DKFLZ2y6DxTWD4cDe07RglV88ZiNEdlRfGtqahfbIjfsw1nMCPx49Uev4IA/hWn3sDKyAnSPwoYSsAEdcimw==} + resolution: {integrity: sha512-y4DKFLZ2y6DxTWD4cDe07RglV88ZiNEdlRfGtqahfbIjfsw1nMCPx49Uev4IA/hWn3sDKyAnSPwoYSsAEdcimw==, tarball: https://registry.npmjs.org/jest-message-util/-/jest-message-util-30.2.0.tgz} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} jest-message-util@30.4.1: - resolution: {integrity: sha512-kwCKIvq0MCW1HzLoGola9Te6JUdzgV0loyKJ3Qghrkz9i5/RRIHsL95BMQc2HBBhlBKC4j22K9p11TGHH8RBpQ==} + resolution: {integrity: sha512-kwCKIvq0MCW1HzLoGola9Te6JUdzgV0loyKJ3Qghrkz9i5/RRIHsL95BMQc2HBBhlBKC4j22K9p11TGHH8RBpQ==, tarball: https://registry.npmjs.org/jest-message-util/-/jest-message-util-30.4.1.tgz} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} jest-mock@29.7.0: - resolution: {integrity: sha512-ITOMZn+UkYS4ZFh83xYAOzWStloNzJFO2s8DWrE4lhtGD+AorgnbkiKERe4wQVBydIGPx059g6riW5Btp6Llnw==} + resolution: {integrity: sha512-ITOMZn+UkYS4ZFh83xYAOzWStloNzJFO2s8DWrE4lhtGD+AorgnbkiKERe4wQVBydIGPx059g6riW5Btp6Llnw==, tarball: https://registry.npmjs.org/jest-mock/-/jest-mock-29.7.0.tgz} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} jest-mock@30.2.0: - resolution: {integrity: sha512-JNNNl2rj4b5ICpmAcq+WbLH83XswjPbjH4T7yvGzfAGCPh1rw+xVNbtk+FnRslvt9lkCcdn9i1oAoKUuFsOxRw==} + resolution: {integrity: sha512-JNNNl2rj4b5ICpmAcq+WbLH83XswjPbjH4T7yvGzfAGCPh1rw+xVNbtk+FnRslvt9lkCcdn9i1oAoKUuFsOxRw==, tarball: https://registry.npmjs.org/jest-mock/-/jest-mock-30.2.0.tgz} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} jest-mock@30.4.1: - resolution: {integrity: sha512-/i8SVb8/NSB7RfNi8gfqu8gxLV23KaL5EpAttyb9iz8qWRIqXRLflycz/32wXsYkOnaUlx8NAKnJYtpsmXUmfw==} + resolution: {integrity: sha512-/i8SVb8/NSB7RfNi8gfqu8gxLV23KaL5EpAttyb9iz8qWRIqXRLflycz/32wXsYkOnaUlx8NAKnJYtpsmXUmfw==, tarball: https://registry.npmjs.org/jest-mock/-/jest-mock-30.4.1.tgz} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} jest-pnp-resolver@1.2.3: - resolution: {integrity: sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==} + resolution: {integrity: sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==, tarball: https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz} engines: {node: '>=6'} peerDependencies: jest-resolve: '*' @@ -10836,7 +10898,7 @@ packages: optional: true jest-preset-angular@17.0.0: - resolution: {integrity: sha512-2yAHkA1c5rSICGJVtLYYqPC5RDsvo4+i4CwWFHVXwv41cHNX7gCWeh074IuZ5mFw7Vwsr+i25EowCnGtKkxNWw==} + resolution: {integrity: sha512-2yAHkA1c5rSICGJVtLYYqPC5RDsvo4+i4CwWFHVXwv41cHNX7gCWeh074IuZ5mFw7Vwsr+i25EowCnGtKkxNWw==, tarball: https://registry.npmjs.org/jest-preset-angular/-/jest-preset-angular-17.0.0.tgz} engines: {node: ^20.11.1 || >=22.0.0} peerDependencies: '@angular/compiler-cli': '>=20.0.0 <23.0.0' @@ -10847,95 +10909,95 @@ packages: typescript: '>=5.8' jest-regex-util@30.0.1: - resolution: {integrity: sha512-jHEQgBXAgc+Gh4g0p3bCevgRCVRkB4VB70zhoAE48gxeSr1hfUOsM/C2WoJgVL7Eyg//hudYENbm3Ne+/dRVVA==} + resolution: {integrity: sha512-jHEQgBXAgc+Gh4g0p3bCevgRCVRkB4VB70zhoAE48gxeSr1hfUOsM/C2WoJgVL7Eyg//hudYENbm3Ne+/dRVVA==, tarball: https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-30.0.1.tgz} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} jest-regex-util@30.4.0: - resolution: {integrity: sha512-mWlvLviKIgIQ8VCuM1xRdD0TWp3zlzionlmDBjuXVBs+VkmXq6FgW9T4Emr7oGz/Rk6feDCGyiugolcQEyp3mg==} + resolution: {integrity: sha512-mWlvLviKIgIQ8VCuM1xRdD0TWp3zlzionlmDBjuXVBs+VkmXq6FgW9T4Emr7oGz/Rk6feDCGyiugolcQEyp3mg==, tarball: https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-30.4.0.tgz} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} jest-resolve-dependencies@30.2.0: - resolution: {integrity: sha512-xTOIGug/0RmIe3mmCqCT95yO0vj6JURrn1TKWlNbhiAefJRWINNPgwVkrVgt/YaerPzY3iItufd80v3lOrFJ2w==} + resolution: {integrity: sha512-xTOIGug/0RmIe3mmCqCT95yO0vj6JURrn1TKWlNbhiAefJRWINNPgwVkrVgt/YaerPzY3iItufd80v3lOrFJ2w==, tarball: https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-30.2.0.tgz} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} jest-resolve@30.2.0: - resolution: {integrity: sha512-TCrHSxPlx3tBY3hWNtRQKbtgLhsXa1WmbJEqBlTBrGafd5fiQFByy2GNCEoGR+Tns8d15GaL9cxEzKOO3GEb2A==} + resolution: {integrity: sha512-TCrHSxPlx3tBY3hWNtRQKbtgLhsXa1WmbJEqBlTBrGafd5fiQFByy2GNCEoGR+Tns8d15GaL9cxEzKOO3GEb2A==, tarball: https://registry.npmjs.org/jest-resolve/-/jest-resolve-30.2.0.tgz} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} jest-resolve@30.4.1: - resolution: {integrity: sha512-Zry8Yq/yJcNAZ7dJ5F2heic8AheXvbFZ7XI5V+h28nrYZ7Qoyy4dItq8OodjnYD270mvX+ZudmrNV9cysqhW5Q==} + resolution: {integrity: sha512-Zry8Yq/yJcNAZ7dJ5F2heic8AheXvbFZ7XI5V+h28nrYZ7Qoyy4dItq8OodjnYD270mvX+ZudmrNV9cysqhW5Q==, tarball: https://registry.npmjs.org/jest-resolve/-/jest-resolve-30.4.1.tgz} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} jest-runner@30.2.0: - resolution: {integrity: sha512-PqvZ2B2XEyPEbclp+gV6KO/F1FIFSbIwewRgmROCMBo/aZ6J1w8Qypoj2pEOcg3G2HzLlaP6VUtvwCI8dM3oqQ==} + resolution: {integrity: sha512-PqvZ2B2XEyPEbclp+gV6KO/F1FIFSbIwewRgmROCMBo/aZ6J1w8Qypoj2pEOcg3G2HzLlaP6VUtvwCI8dM3oqQ==, tarball: https://registry.npmjs.org/jest-runner/-/jest-runner-30.2.0.tgz} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} jest-runner@30.4.2: - resolution: {integrity: sha512-2dw0PslVYXxffXGpLo+Ejad+KcI1Qkjn7f4X4619gf21oCUmL+SPfjqIa/losUem3yEOvfNZe/F1HWUcNpODcg==} + resolution: {integrity: sha512-2dw0PslVYXxffXGpLo+Ejad+KcI1Qkjn7f4X4619gf21oCUmL+SPfjqIa/losUem3yEOvfNZe/F1HWUcNpODcg==, tarball: https://registry.npmjs.org/jest-runner/-/jest-runner-30.4.2.tgz} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} jest-runtime@30.2.0: - resolution: {integrity: sha512-p1+GVX/PJqTucvsmERPMgCPvQJpFt4hFbM+VN3n8TMo47decMUcJbt+rgzwrEme0MQUA/R+1de2axftTHkKckg==} + resolution: {integrity: sha512-p1+GVX/PJqTucvsmERPMgCPvQJpFt4hFbM+VN3n8TMo47decMUcJbt+rgzwrEme0MQUA/R+1de2axftTHkKckg==, tarball: https://registry.npmjs.org/jest-runtime/-/jest-runtime-30.2.0.tgz} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} jest-runtime@30.4.2: - resolution: {integrity: sha512-3/5e8iPz2k/VLqlr8DgTftYyLUv8Su3FkCAO2/Od81UsUTpSxOrS6O5x5KkoQwyUjmpYyDJKeyAvg2T2nvpNkQ==} + resolution: {integrity: sha512-3/5e8iPz2k/VLqlr8DgTftYyLUv8Su3FkCAO2/Od81UsUTpSxOrS6O5x5KkoQwyUjmpYyDJKeyAvg2T2nvpNkQ==, tarball: https://registry.npmjs.org/jest-runtime/-/jest-runtime-30.4.2.tgz} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} jest-snapshot@30.2.0: - resolution: {integrity: sha512-5WEtTy2jXPFypadKNpbNkZ72puZCa6UjSr/7djeecHWOu7iYhSXSnHScT8wBz3Rn8Ena5d5RYRcsyKIeqG1IyA==} + resolution: {integrity: sha512-5WEtTy2jXPFypadKNpbNkZ72puZCa6UjSr/7djeecHWOu7iYhSXSnHScT8wBz3Rn8Ena5d5RYRcsyKIeqG1IyA==, tarball: https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-30.2.0.tgz} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} jest-snapshot@30.4.1: - resolution: {integrity: sha512-tEOkkfOMppUyeiHwjZswOQ3lcnoTnws/q5FnGIaeIh/jmoU0ZlgMYRR8sTlTj+nNGCoJ0RDq6SfxGxCsyMTPmw==} + resolution: {integrity: sha512-tEOkkfOMppUyeiHwjZswOQ3lcnoTnws/q5FnGIaeIh/jmoU0ZlgMYRR8sTlTj+nNGCoJ0RDq6SfxGxCsyMTPmw==, tarball: https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-30.4.1.tgz} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} jest-util@29.7.0: - resolution: {integrity: sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==} + resolution: {integrity: sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==, tarball: https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} jest-util@30.2.0: - resolution: {integrity: sha512-QKNsM0o3Xe6ISQU869e+DhG+4CK/48aHYdJZGlFQVTjnbvgpcKyxpzk29fGiO7i/J8VENZ+d2iGnSsvmuHywlA==} + resolution: {integrity: sha512-QKNsM0o3Xe6ISQU869e+DhG+4CK/48aHYdJZGlFQVTjnbvgpcKyxpzk29fGiO7i/J8VENZ+d2iGnSsvmuHywlA==, tarball: https://registry.npmjs.org/jest-util/-/jest-util-30.2.0.tgz} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} jest-util@30.4.1: - resolution: {integrity: sha512-vjQb1sACEiv13DKJMDToJpzVW0joCsIQrmbg0fi7CyOOt+g9jTuQl2A216pWRBYhOVt53XbL/2LbMKg1BECWOw==} + resolution: {integrity: sha512-vjQb1sACEiv13DKJMDToJpzVW0joCsIQrmbg0fi7CyOOt+g9jTuQl2A216pWRBYhOVt53XbL/2LbMKg1BECWOw==, tarball: https://registry.npmjs.org/jest-util/-/jest-util-30.4.1.tgz} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} jest-validate@30.2.0: - resolution: {integrity: sha512-FBGWi7dP2hpdi8nBoWxSsLvBFewKAg0+uSQwBaof4Y4DPgBabXgpSYC5/lR7VmnIlSpASmCi/ntRWPbv7089Pw==} + resolution: {integrity: sha512-FBGWi7dP2hpdi8nBoWxSsLvBFewKAg0+uSQwBaof4Y4DPgBabXgpSYC5/lR7VmnIlSpASmCi/ntRWPbv7089Pw==, tarball: https://registry.npmjs.org/jest-validate/-/jest-validate-30.2.0.tgz} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} jest-validate@30.4.1: - resolution: {integrity: sha512-PDWi4SOwLnwqNDfHZjOcsEFyZ4fc/2W2gVL3DEoyqnB6jCQMLRtfBong8s6omIw3lI0HWOus12xfnFmQtjW3fw==} + resolution: {integrity: sha512-PDWi4SOwLnwqNDfHZjOcsEFyZ4fc/2W2gVL3DEoyqnB6jCQMLRtfBong8s6omIw3lI0HWOus12xfnFmQtjW3fw==, tarball: https://registry.npmjs.org/jest-validate/-/jest-validate-30.4.1.tgz} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} jest-watcher@30.2.0: - resolution: {integrity: sha512-PYxa28dxJ9g777pGm/7PrbnMeA0Jr7osHP9bS7eJy9DuAjMgdGtxgf0uKMyoIsTWAkIbUW5hSDdJ3urmgXBqxg==} + resolution: {integrity: sha512-PYxa28dxJ9g777pGm/7PrbnMeA0Jr7osHP9bS7eJy9DuAjMgdGtxgf0uKMyoIsTWAkIbUW5hSDdJ3urmgXBqxg==, tarball: https://registry.npmjs.org/jest-watcher/-/jest-watcher-30.2.0.tgz} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} jest-watcher@30.4.1: - resolution: {integrity: sha512-/l9UonmvCwjHH7d2h3iAwIloLc1H0S8mJZ/LNK3i86hqwPAz8otUJjP9MfYtz9Tt77Su5FD2xGjZn8d31IZHlw==} + resolution: {integrity: sha512-/l9UonmvCwjHH7d2h3iAwIloLc1H0S8mJZ/LNK3i86hqwPAz8otUJjP9MfYtz9Tt77Su5FD2xGjZn8d31IZHlw==, tarball: https://registry.npmjs.org/jest-watcher/-/jest-watcher-30.4.1.tgz} 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==} + resolution: {integrity: sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==, tarball: https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz} engines: {node: '>= 10.13.0'} jest-worker@29.7.0: - resolution: {integrity: sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==} + resolution: {integrity: sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==, tarball: https://registry.npmjs.org/jest-worker/-/jest-worker-29.7.0.tgz} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} jest-worker@30.2.0: - resolution: {integrity: sha512-0Q4Uk8WF7BUwqXHuAjc23vmopWJw5WH7w2tqBoUOZpOjW/ZnR44GXXd1r82RvnmI2GZge3ivrYXk/BE2+VtW2g==} + resolution: {integrity: sha512-0Q4Uk8WF7BUwqXHuAjc23vmopWJw5WH7w2tqBoUOZpOjW/ZnR44GXXd1r82RvnmI2GZge3ivrYXk/BE2+VtW2g==, tarball: https://registry.npmjs.org/jest-worker/-/jest-worker-30.2.0.tgz} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} jest-worker@30.4.1: - resolution: {integrity: sha512-SHynN/q/QD++iNyvMdy+WMmbCGk8jIsNcRxycXbWubSOhvo6T+j2afcfUSl+3hYsiBebOTo0cT7c2H7CXugu1g==} + resolution: {integrity: sha512-SHynN/q/QD++iNyvMdy+WMmbCGk8jIsNcRxycXbWubSOhvo6T+j2afcfUSl+3hYsiBebOTo0cT7c2H7CXugu1g==, tarball: https://registry.npmjs.org/jest-worker/-/jest-worker-30.4.1.tgz} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} jest@30.2.0: - resolution: {integrity: sha512-F26gjC0yWN8uAA5m5Ss8ZQf5nDHWGlN/xWZIh8S5SRbsEKBovwZhxGd6LJlbZYxBgCYOtreSUyb8hpXyGC5O4A==} + resolution: {integrity: sha512-F26gjC0yWN8uAA5m5Ss8ZQf5nDHWGlN/xWZIh8S5SRbsEKBovwZhxGd6LJlbZYxBgCYOtreSUyb8hpXyGC5O4A==, tarball: https://registry.npmjs.org/jest/-/jest-30.2.0.tgz} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} hasBin: true peerDependencies: @@ -10945,46 +11007,46 @@ packages: optional: true jiti@2.4.2: - resolution: {integrity: sha512-rg9zJN+G4n2nfJl5MW3BMygZX56zKPNVEYYqq7adpmMh4Jn2QNEwhvQlFy6jPVdcod7txZtKHWnyZiA3a0zP7A==} + resolution: {integrity: sha512-rg9zJN+G4n2nfJl5MW3BMygZX56zKPNVEYYqq7adpmMh4Jn2QNEwhvQlFy6jPVdcod7txZtKHWnyZiA3a0zP7A==, tarball: https://registry.npmjs.org/jiti/-/jiti-2.4.2.tgz} hasBin: true jiti@2.7.0: - resolution: {integrity: sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ==} + resolution: {integrity: sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ==, tarball: https://registry.npmjs.org/jiti/-/jiti-2.7.0.tgz} hasBin: true jju@1.4.0: - resolution: {integrity: sha512-8wb9Yw966OSxApiCt0K3yNJL8pnNeIv+OEq2YMidz4FKP6nonSRoOXc80iXY4JaN2FC11B9qsNmDsm+ZOfMROA==} + resolution: {integrity: sha512-8wb9Yw966OSxApiCt0K3yNJL8pnNeIv+OEq2YMidz4FKP6nonSRoOXc80iXY4JaN2FC11B9qsNmDsm+ZOfMROA==, tarball: https://registry.npmjs.org/jju/-/jju-1.4.0.tgz} jose@6.2.3: - resolution: {integrity: sha512-YYVDInQKFJfR/xa3ojUTl8c2KoTwiL1R5Wg9YCydwH0x0B9grbzlg5HC7mMjCtUJjbQ/YnGEZIhI5tCgfTb4Hw==} + resolution: {integrity: sha512-YYVDInQKFJfR/xa3ojUTl8c2KoTwiL1R5Wg9YCydwH0x0B9grbzlg5HC7mMjCtUJjbQ/YnGEZIhI5tCgfTb4Hw==, tarball: https://registry.npmjs.org/jose/-/jose-6.2.3.tgz} jquery@3.7.1: - resolution: {integrity: sha512-m4avr8yL8kmFN8psrbFFFmB/If14iN5o9nw/NgnnM+kybDJpRsAynV2BsfpTYrTRysYUdADVD7CkUUizgkpLfg==} + resolution: {integrity: sha512-m4avr8yL8kmFN8psrbFFFmB/If14iN5o9nw/NgnnM+kybDJpRsAynV2BsfpTYrTRysYUdADVD7CkUUizgkpLfg==, tarball: https://registry.npmjs.org/jquery/-/jquery-3.7.1.tgz} js-beautify@1.15.4: - resolution: {integrity: sha512-9/KXeZUKKJwqCXUdBxFJ3vPh467OCckSBmYDwSK/EtV090K+iMJ7zx2S3HLVDIWFQdqMIsZWbnaGiba18aWhaA==} + resolution: {integrity: sha512-9/KXeZUKKJwqCXUdBxFJ3vPh467OCckSBmYDwSK/EtV090K+iMJ7zx2S3HLVDIWFQdqMIsZWbnaGiba18aWhaA==, tarball: https://registry.npmjs.org/js-beautify/-/js-beautify-1.15.4.tgz} engines: {node: '>=14'} hasBin: true js-cookie@3.0.8: - resolution: {integrity: sha512-yeJd4aNAdYZQjaon2bpD/Gb0B/omw7HQOsynXXcOiWVCacbBcPlgn8S/d1X6blFSaHao7ozqtW7NZW19xpCtIw==} + resolution: {integrity: sha512-yeJd4aNAdYZQjaon2bpD/Gb0B/omw7HQOsynXXcOiWVCacbBcPlgn8S/d1X6blFSaHao7ozqtW7NZW19xpCtIw==, tarball: https://registry.npmjs.org/js-cookie/-/js-cookie-3.0.8.tgz} js-tokens@10.0.0: - resolution: {integrity: sha512-lM/UBzQmfJRo9ABXbPWemivdCW8V2G8FHaHdypQaIy523snUjog0W71ayWXTjiR+ixeMyVHN2XcpnTd/liPg/Q==} + resolution: {integrity: sha512-lM/UBzQmfJRo9ABXbPWemivdCW8V2G8FHaHdypQaIy523snUjog0W71ayWXTjiR+ixeMyVHN2XcpnTd/liPg/Q==, tarball: https://registry.npmjs.org/js-tokens/-/js-tokens-10.0.0.tgz} js-tokens@4.0.0: - resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} + resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==, tarball: https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz} js-yaml@3.15.0: - resolution: {integrity: sha512-ttBQIIQPDeLjpPOohtUdXuXUVoA2uIB6fEH9HyJ7234s5mBJ5wTx20njxplLZQgLaOfpmPQA7X2t5AX6tIPbog==} + resolution: {integrity: sha512-ttBQIIQPDeLjpPOohtUdXuXUVoA2uIB6fEH9HyJ7234s5mBJ5wTx20njxplLZQgLaOfpmPQA7X2t5AX6tIPbog==, tarball: https://registry.npmjs.org/js-yaml/-/js-yaml-3.15.0.tgz} hasBin: true js-yaml@4.3.0: - resolution: {integrity: sha512-1td788aAnnZ5qs7V2QIRl1owjtYpbKt749Y3xauqQgwIIGF/xXWz1wMTEBx5O3LK3lXLVuqXPdPxj2BoFHaW9Q==} + resolution: {integrity: sha512-1td788aAnnZ5qs7V2QIRl1owjtYpbKt749Y3xauqQgwIIGF/xXWz1wMTEBx5O3LK3lXLVuqXPdPxj2BoFHaW9Q==, tarball: https://registry.npmjs.org/js-yaml/-/js-yaml-4.3.0.tgz} hasBin: true jsdom@20.0.3: - resolution: {integrity: sha512-SYhBvTh89tTfCD/CRdSOm13mOBa42iTaTyfyEWBdKcGdPxPtLFBXuHR8XHb33YNYaP+lLbmSvBTsnoesCNJEsQ==} + resolution: {integrity: sha512-SYhBvTh89tTfCD/CRdSOm13mOBa42iTaTyfyEWBdKcGdPxPtLFBXuHR8XHb33YNYaP+lLbmSvBTsnoesCNJEsQ==, tarball: https://registry.npmjs.org/jsdom/-/jsdom-20.0.3.tgz} engines: {node: '>=14'} peerDependencies: canvas: ^2.5.0 @@ -10993,7 +11055,7 @@ packages: optional: true jsdom@28.1.0: - resolution: {integrity: sha512-0+MoQNYyr2rBHqO1xilltfDjV9G7ymYGlAUazgcDLQaUf8JDHbuGwsxN6U9qWaElZ4w1B2r7yEGIL3GdeW3Rug==} + resolution: {integrity: sha512-0+MoQNYyr2rBHqO1xilltfDjV9G7ymYGlAUazgcDLQaUf8JDHbuGwsxN6U9qWaElZ4w1B2r7yEGIL3GdeW3Rug==, tarball: https://registry.npmjs.org/jsdom/-/jsdom-28.1.0.tgz} engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} peerDependencies: canvas: ^3.0.0 @@ -11002,134 +11064,134 @@ packages: optional: true jsesc@3.1.0: - resolution: {integrity: sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==} + resolution: {integrity: sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==, tarball: https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz} engines: {node: '>=6'} hasBin: true json-buffer@3.0.1: - resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} + resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==, tarball: https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz} json-parse-better-errors@1.0.2: - resolution: {integrity: sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==} + resolution: {integrity: sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==, tarball: https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz} json-parse-even-better-errors@2.3.1: - resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==} + resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==, tarball: https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz} json-schema-traverse@0.4.1: - resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} + resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==, tarball: https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz} json-schema-traverse@1.0.0: - resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==} + resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==, tarball: https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz} json-schema-typed@8.0.2: - resolution: {integrity: sha512-fQhoXdcvc3V28x7C7BMs4P5+kNlgUURe2jmUT1T//oBRMDrqy1QPelJimwZGo7Hg9VPV3EQV5Bnq4hbFy2vetA==} + resolution: {integrity: sha512-fQhoXdcvc3V28x7C7BMs4P5+kNlgUURe2jmUT1T//oBRMDrqy1QPelJimwZGo7Hg9VPV3EQV5Bnq4hbFy2vetA==, tarball: https://registry.npmjs.org/json-schema-typed/-/json-schema-typed-8.0.2.tgz} json-schema@0.4.0: - resolution: {integrity: sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==} + resolution: {integrity: sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==, tarball: https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz} json-stable-stringify-without-jsonify@1.0.1: - resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} + resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==, tarball: https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz} json5@1.0.2: - resolution: {integrity: sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==} + resolution: {integrity: sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==, tarball: https://registry.npmjs.org/json5/-/json5-1.0.2.tgz} hasBin: true json5@2.2.3: - resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==} + resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==, tarball: https://registry.npmjs.org/json5/-/json5-2.2.3.tgz} engines: {node: '>=6'} hasBin: true jsonc-eslint-parser@2.4.0: - resolution: {integrity: sha512-WYDyuc/uFcGp6YtM2H0uKmUwieOuzeE/5YocFJLnLfclZ4inf3mRn8ZVy1s7Hxji7Jxm6Ss8gqpexD/GlKoGgg==} + resolution: {integrity: sha512-WYDyuc/uFcGp6YtM2H0uKmUwieOuzeE/5YocFJLnLfclZ4inf3mRn8ZVy1s7Hxji7Jxm6Ss8gqpexD/GlKoGgg==, tarball: https://registry.npmjs.org/jsonc-eslint-parser/-/jsonc-eslint-parser-2.4.0.tgz} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} jsonc-parser@3.2.0: - resolution: {integrity: sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w==} + resolution: {integrity: sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w==, tarball: https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.2.0.tgz} jsonc-parser@3.3.1: - resolution: {integrity: sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ==} + resolution: {integrity: sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ==, tarball: https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.3.1.tgz} jsonfile@4.0.0: - resolution: {integrity: sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==} + resolution: {integrity: sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==, tarball: https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz} jsonfile@6.2.1: - resolution: {integrity: sha512-zwOTdL3rFQ/lRdBnntKVOX6k5cKJwEc1HdilT71BWEu7J41gXIB2MRp+vxduPSwZJPWBxEzv4yH1wYLJGUHX4Q==} + resolution: {integrity: sha512-zwOTdL3rFQ/lRdBnntKVOX6k5cKJwEc1HdilT71BWEu7J41gXIB2MRp+vxduPSwZJPWBxEzv4yH1wYLJGUHX4Q==, tarball: https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.1.tgz} jstat@1.9.6: - resolution: {integrity: sha512-rPBkJbK2TnA8pzs93QcDDPlKcrtZWuuCo2dVR0TFLOJSxhqfWOVCSp8aV3/oSbn+4uY4yw1URtLpHQedtmXfug==} + resolution: {integrity: sha512-rPBkJbK2TnA8pzs93QcDDPlKcrtZWuuCo2dVR0TFLOJSxhqfWOVCSp8aV3/oSbn+4uY4yw1URtLpHQedtmXfug==, tarball: https://registry.npmjs.org/jstat/-/jstat-1.9.6.tgz} jsx-ast-utils@3.3.5: - resolution: {integrity: sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==} + resolution: {integrity: sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==, tarball: https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz} engines: {node: '>=4.0'} karma-source-map-support@1.4.0: - resolution: {integrity: sha512-RsBECncGO17KAoJCYXjv+ckIz+Ii9NCi+9enk+rq6XC81ezYkb4/RHE6CTXdA7IOJqoF3wcaLfVG0CPmE5ca6A==} + resolution: {integrity: sha512-RsBECncGO17KAoJCYXjv+ckIz+Ii9NCi+9enk+rq6XC81ezYkb4/RHE6CTXdA7IOJqoF3wcaLfVG0CPmE5ca6A==, tarball: https://registry.npmjs.org/karma-source-map-support/-/karma-source-map-support-1.4.0.tgz} katex@0.16.47: - resolution: {integrity: sha512-Eeo8Ys1doU1z+x8AZsPpQu+p/QcZBI5PeOo7QGQdy2x2m0MU/hYagBbGOmXwr5KVbEfVuWv9LpnQWeehogurjg==} + resolution: {integrity: sha512-Eeo8Ys1doU1z+x8AZsPpQu+p/QcZBI5PeOo7QGQdy2x2m0MU/hYagBbGOmXwr5KVbEfVuWv9LpnQWeehogurjg==, tarball: https://registry.npmjs.org/katex/-/katex-0.16.47.tgz} hasBin: true keygrip@1.1.0: - resolution: {integrity: sha512-iYSchDJ+liQ8iwbSI2QqsQOvqv58eJCEanyJPJi+Khyu8smkcKSFUCbPwzFcL7YVtZ6eONjqRX/38caJ7QjRAQ==} + resolution: {integrity: sha512-iYSchDJ+liQ8iwbSI2QqsQOvqv58eJCEanyJPJi+Khyu8smkcKSFUCbPwzFcL7YVtZ6eONjqRX/38caJ7QjRAQ==, tarball: https://registry.npmjs.org/keygrip/-/keygrip-1.1.0.tgz} engines: {node: '>= 0.6'} keyv@4.5.4: - resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} + resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==, tarball: https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz} khroma@2.1.0: - resolution: {integrity: sha512-Ls993zuzfayK269Svk9hzpeGUKob/sIgZzyHYdjQoAdQetRKpOLj+k/QQQ/6Qi0Yz65mlROrfd+Ev+1+7dz9Kw==} + resolution: {integrity: sha512-Ls993zuzfayK269Svk9hzpeGUKob/sIgZzyHYdjQoAdQetRKpOLj+k/QQQ/6Qi0Yz65mlROrfd+Ev+1+7dz9Kw==, tarball: https://registry.npmjs.org/khroma/-/khroma-2.1.0.tgz} kill-port@1.6.1: - resolution: {integrity: sha512-un0Y55cOM7JKGaLnGja28T38tDDop0AQ8N0KlAdyh+B1nmMoX8AnNmqPNZbS3mUMgiST51DCVqmbFT1gNJpVNw==} + resolution: {integrity: sha512-un0Y55cOM7JKGaLnGja28T38tDDop0AQ8N0KlAdyh+B1nmMoX8AnNmqPNZbS3mUMgiST51DCVqmbFT1gNJpVNw==, tarball: https://registry.npmjs.org/kill-port/-/kill-port-1.6.1.tgz} hasBin: true kind-of@3.2.2: - resolution: {integrity: sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==} + resolution: {integrity: sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==, tarball: https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz} engines: {node: '>=0.10.0'} kind-of@6.0.3: - resolution: {integrity: sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==} + resolution: {integrity: sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==, tarball: https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz} engines: {node: '>=0.10.0'} klona@2.0.6: - resolution: {integrity: sha512-dhG34DXATL5hSxJbIexCft8FChFXtmskoZYnoPWjXQuebWYCNkVeV3KkGegCK9CP1oswI/vQibS2GY7Em/sJJA==} + resolution: {integrity: sha512-dhG34DXATL5hSxJbIexCft8FChFXtmskoZYnoPWjXQuebWYCNkVeV3KkGegCK9CP1oswI/vQibS2GY7Em/sJJA==, tarball: https://registry.npmjs.org/klona/-/klona-2.0.6.tgz} engines: {node: '>= 8'} koa-compose@4.1.0: - resolution: {integrity: sha512-8ODW8TrDuMYvXRwra/Kh7/rJo9BtOfPc6qO8eAfC80CnCvSjSl0bkRM24X6/XBBEyj0v1nRUQ1LyOy3dbqOWXw==} + resolution: {integrity: sha512-8ODW8TrDuMYvXRwra/Kh7/rJo9BtOfPc6qO8eAfC80CnCvSjSl0bkRM24X6/XBBEyj0v1nRUQ1LyOy3dbqOWXw==, tarball: https://registry.npmjs.org/koa-compose/-/koa-compose-4.1.0.tgz} koa@3.0.1: - resolution: {integrity: sha512-oDxVkRwPOHhGlxKIDiDB2h+/l05QPtefD7nSqRgDfZt8P+QVYFWjfeK8jANf5O2YXjk8egd7KntvXKYx82wOag==} + resolution: {integrity: sha512-oDxVkRwPOHhGlxKIDiDB2h+/l05QPtefD7nSqRgDfZt8P+QVYFWjfeK8jANf5O2YXjk8egd7KntvXKYx82wOag==, tarball: https://registry.npmjs.org/koa/-/koa-3.0.1.tgz} engines: {node: '>= 18'} kolorist@1.8.0: - resolution: {integrity: sha512-Y+60/zizpJ3HRH8DCss+q95yr6145JXZo46OTpFvDZWLfRCE4qChOyk1b26nMaNpfHHgxagk9dXT5OP0Tfe+dQ==} + resolution: {integrity: sha512-Y+60/zizpJ3HRH8DCss+q95yr6145JXZo46OTpFvDZWLfRCE4qChOyk1b26nMaNpfHHgxagk9dXT5OP0Tfe+dQ==, tarball: https://registry.npmjs.org/kolorist/-/kolorist-1.8.0.tgz} language-subtag-registry@0.3.23: - resolution: {integrity: sha512-0K65Lea881pHotoGEa5gDlMxt3pctLi2RplBb7Ezh4rRdLEOtgi7n4EwK9lamnUCkKBqaeKRVebTq6BAxSkpXQ==} + resolution: {integrity: sha512-0K65Lea881pHotoGEa5gDlMxt3pctLi2RplBb7Ezh4rRdLEOtgi7n4EwK9lamnUCkKBqaeKRVebTq6BAxSkpXQ==, tarball: https://registry.npmjs.org/language-subtag-registry/-/language-subtag-registry-0.3.23.tgz} language-tags@1.0.9: - resolution: {integrity: sha512-MbjN408fEndfiQXbFQ1vnd+1NoLDsnQW41410oQBXiyXDMYH5z505juWa4KUE1LqxRC7DgOgZDbKLxHIwm27hA==} + resolution: {integrity: sha512-MbjN408fEndfiQXbFQ1vnd+1NoLDsnQW41410oQBXiyXDMYH5z505juWa4KUE1LqxRC7DgOgZDbKLxHIwm27hA==, tarball: https://registry.npmjs.org/language-tags/-/language-tags-1.0.9.tgz} engines: {node: '>=0.10'} launch-editor@2.14.1: - resolution: {integrity: sha512-QWBrQsMpH7gPr965dsKD/3cKWiNoTjpATQf++Xq63N6sKRGMwlVXz41O1IZTMfZQgBctD/K5Zt06+/I6pP6+HA==} + resolution: {integrity: sha512-QWBrQsMpH7gPr965dsKD/3cKWiNoTjpATQf++Xq63N6sKRGMwlVXz41O1IZTMfZQgBctD/K5Zt06+/I6pP6+HA==, tarball: https://registry.npmjs.org/launch-editor/-/launch-editor-2.14.1.tgz} layout-base@1.0.2: - resolution: {integrity: sha512-8h2oVEZNktL4BH2JCOI90iD1yXwL6iNW7KcCKT2QZgQJR2vbqDsldCTPRU9NifTCqHZci57XvQQ15YTu+sTYPg==} + resolution: {integrity: sha512-8h2oVEZNktL4BH2JCOI90iD1yXwL6iNW7KcCKT2QZgQJR2vbqDsldCTPRU9NifTCqHZci57XvQQ15YTu+sTYPg==, tarball: https://registry.npmjs.org/layout-base/-/layout-base-1.0.2.tgz} layout-base@2.0.1: - resolution: {integrity: sha512-dp3s92+uNI1hWIpPGH3jK2kxE2lMjdXdr+DH8ynZHpd6PUlH6x6cbuXnoMmiNumznqaNO31xu9e79F0uuZ0JFg==} + resolution: {integrity: sha512-dp3s92+uNI1hWIpPGH3jK2kxE2lMjdXdr+DH8ynZHpd6PUlH6x6cbuXnoMmiNumznqaNO31xu9e79F0uuZ0JFg==, tarball: https://registry.npmjs.org/layout-base/-/layout-base-2.0.1.tgz} less-loader@11.1.4: - resolution: {integrity: sha512-6/GrYaB6QcW6Vj+/9ZPgKKs6G10YZai/l/eJ4SLwbzqNTBsAqt5hSLVF47TgsiBxV1P6eAU0GYRH3YRuQU9V3A==} + resolution: {integrity: sha512-6/GrYaB6QcW6Vj+/9ZPgKKs6G10YZai/l/eJ4SLwbzqNTBsAqt5hSLVF47TgsiBxV1P6eAU0GYRH3YRuQU9V3A==, tarball: https://registry.npmjs.org/less-loader/-/less-loader-11.1.4.tgz} engines: {node: '>= 14.15.0'} peerDependencies: less: ^3.5.0 || ^4.0.0 webpack: ^5.0.0 less-loader@12.3.3: - resolution: {integrity: sha512-F0+ErFFDj3Pt+nVrCN6VlEGEzocv9s7x/aR9v2riI+WM83UAfTYBDBjGPJnT55lBLR6JSI4fmWblt+aA2JN6/w==} + resolution: {integrity: sha512-F0+ErFFDj3Pt+nVrCN6VlEGEzocv9s7x/aR9v2riI+WM83UAfTYBDBjGPJnT55lBLR6JSI4fmWblt+aA2JN6/w==, tarball: https://registry.npmjs.org/less-loader/-/less-loader-12.3.3.tgz} engines: {node: '>= 18.12.0'} peerDependencies: '@rspack/core': 0.x || ^1.0.0 || ^2.0.0-0 @@ -11142,7 +11204,7 @@ packages: optional: true less-loader@13.0.0: - resolution: {integrity: sha512-TIa8d6znKH634Mg+7OU3jevZT6KeOhh0amW+YeMPD0GM9buUn5Y7HvtyCR5pUDdLaFfqLA8AX5PTSIHMNSexEA==} + resolution: {integrity: sha512-TIa8d6znKH634Mg+7OU3jevZT6KeOhh0amW+YeMPD0GM9buUn5Y7HvtyCR5pUDdLaFfqLA8AX5PTSIHMNSexEA==, tarball: https://registry.npmjs.org/less-loader/-/less-loader-13.0.0.tgz} engines: {node: '>= 22.11.0'} peerDependencies: '@rspack/core': 0.x || ^1.0.0 || ^2.0.0-0 @@ -11155,30 +11217,30 @@ packages: optional: true less@4.5.1: - resolution: {integrity: sha512-UKgI3/KON4u6ngSsnDADsUERqhZknsVZbnuzlRZXLQCmfC/MDld42fTydUE9B+Mla1AL6SJ/Pp6SlEFi/AVGfw==} + resolution: {integrity: sha512-UKgI3/KON4u6ngSsnDADsUERqhZknsVZbnuzlRZXLQCmfC/MDld42fTydUE9B+Mla1AL6SJ/Pp6SlEFi/AVGfw==, tarball: https://registry.npmjs.org/less/-/less-4.5.1.tgz} engines: {node: '>=14'} hasBin: true less@4.6.7: - resolution: {integrity: sha512-o3UxHBPPVY1HtCXx15/z1NlknQiWyafRNbtLEv+6xFaDRI2g2xPKIH43do9dSwt8bGLTsjNSaifa48N3d6odsQ==} + resolution: {integrity: sha512-o3UxHBPPVY1HtCXx15/z1NlknQiWyafRNbtLEv+6xFaDRI2g2xPKIH43do9dSwt8bGLTsjNSaifa48N3d6odsQ==, tarball: https://registry.npmjs.org/less/-/less-4.6.7.tgz} engines: {node: '>=18'} hasBin: true leven@3.1.0: - resolution: {integrity: sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==} + resolution: {integrity: sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==, tarball: https://registry.npmjs.org/leven/-/leven-3.1.0.tgz} engines: {node: '>=6'} levn@0.4.1: - resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} + resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==, tarball: https://registry.npmjs.org/levn/-/levn-0.4.1.tgz} engines: {node: '>= 0.8.0'} lib0@0.2.117: - resolution: {integrity: sha512-DeXj9X5xDCjgKLU/7RR+/HQEVzuuEUiwldwOGsHK/sfAfELGWEyTcf0x+uOvCvK3O2zPmZePXWL85vtia6GyZw==} + resolution: {integrity: sha512-DeXj9X5xDCjgKLU/7RR+/HQEVzuuEUiwldwOGsHK/sfAfELGWEyTcf0x+uOvCvK3O2zPmZePXWL85vtia6GyZw==, tarball: https://registry.npmjs.org/lib0/-/lib0-0.2.117.tgz} engines: {node: '>=16'} hasBin: true license-webpack-plugin@4.0.2: - resolution: {integrity: sha512-771TFWFD70G1wLTC4oU2Cw4qvtmNrIw+wRvBtn+okgHl7slJVi7zfNcdmqDL72BojM30VNJ2UHylr1o77U37Jw==} + resolution: {integrity: sha512-771TFWFD70G1wLTC4oU2Cw4qvtmNrIw+wRvBtn+okgHl7slJVi7zfNcdmqDL72BojM30VNJ2UHylr1o77U37Jw==, tarball: https://registry.npmjs.org/license-webpack-plugin/-/license-webpack-plugin-4.0.2.tgz} peerDependencies: webpack: '*' peerDependenciesMeta: @@ -11186,509 +11248,509 @@ packages: optional: true lightercollective@0.0.0: - resolution: {integrity: sha512-4SdGgHgQjlqhk90QxAYsltGEI6HHt30xy9fOIPKi+c3vcGRcpPDtzm6+kxr1N9ixkrx/ngMhTCe/VpBc/HmNBQ==} + resolution: {integrity: sha512-4SdGgHgQjlqhk90QxAYsltGEI6HHt30xy9fOIPKi+c3vcGRcpPDtzm6+kxr1N9ixkrx/ngMhTCe/VpBc/HmNBQ==, tarball: https://registry.npmjs.org/lightercollective/-/lightercollective-0.0.0.tgz} hasBin: true lightningcss-android-arm64@1.31.1: - resolution: {integrity: sha512-HXJF3x8w9nQ4jbXRiNppBCqeZPIAfUo8zE/kOEGbW5NZvGc/K7nMxbhIr+YlFlHW5mpbg/YFPdbnCh1wAXCKFg==} + resolution: {integrity: sha512-HXJF3x8w9nQ4jbXRiNppBCqeZPIAfUo8zE/kOEGbW5NZvGc/K7nMxbhIr+YlFlHW5mpbg/YFPdbnCh1wAXCKFg==, tarball: https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.31.1.tgz} engines: {node: '>= 12.0.0'} cpu: [arm64] os: [android] lightningcss-android-arm64@1.33.0: - resolution: {integrity: sha512-gEpRTalKdosp4Bb8qWtc2iOgE5SeIHlpS1up9bFq2wAyYhl1UdTObYiHe98zEM9SQvSoqQZ1IQD0JNpg3Ml5pg==} + resolution: {integrity: sha512-gEpRTalKdosp4Bb8qWtc2iOgE5SeIHlpS1up9bFq2wAyYhl1UdTObYiHe98zEM9SQvSoqQZ1IQD0JNpg3Ml5pg==, tarball: https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.33.0.tgz} engines: {node: '>= 12.0.0'} cpu: [arm64] os: [android] lightningcss-darwin-arm64@1.31.1: - resolution: {integrity: sha512-02uTEqf3vIfNMq3h/z2cJfcOXnQ0GRwQrkmPafhueLb2h7mqEidiCzkE4gBMEH65abHRiQvhdcQ+aP0D0g67sg==} + resolution: {integrity: sha512-02uTEqf3vIfNMq3h/z2cJfcOXnQ0GRwQrkmPafhueLb2h7mqEidiCzkE4gBMEH65abHRiQvhdcQ+aP0D0g67sg==, tarball: https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.31.1.tgz} engines: {node: '>= 12.0.0'} cpu: [arm64] os: [darwin] lightningcss-darwin-arm64@1.33.0: - resolution: {integrity: sha512-Sciaz8eenNTKn9b3t7+xr0ipTp9YxKQY4npwQ3mrRuL0BAVHBLyZxofhaKBAVtzmtRZ/zTyo0/to4B1uWG/Djg==} + resolution: {integrity: sha512-Sciaz8eenNTKn9b3t7+xr0ipTp9YxKQY4npwQ3mrRuL0BAVHBLyZxofhaKBAVtzmtRZ/zTyo0/to4B1uWG/Djg==, tarball: https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.33.0.tgz} engines: {node: '>= 12.0.0'} cpu: [arm64] os: [darwin] lightningcss-darwin-x64@1.31.1: - resolution: {integrity: sha512-1ObhyoCY+tGxtsz1lSx5NXCj3nirk0Y0kB/g8B8DT+sSx4G9djitg9ejFnjb3gJNWo7qXH4DIy2SUHvpoFwfTA==} + resolution: {integrity: sha512-1ObhyoCY+tGxtsz1lSx5NXCj3nirk0Y0kB/g8B8DT+sSx4G9djitg9ejFnjb3gJNWo7qXH4DIy2SUHvpoFwfTA==, tarball: https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.31.1.tgz} engines: {node: '>= 12.0.0'} cpu: [x64] os: [darwin] lightningcss-darwin-x64@1.33.0: - resolution: {integrity: sha512-Z5UPAxzrjlWNNyGy6i65cJzzvgJ5D3T6wMvs+gWpY9d7qRhANrxqAp6LhxIgZhWEw18RfJTGcRxjuLIBr+m8XQ==} + resolution: {integrity: sha512-Z5UPAxzrjlWNNyGy6i65cJzzvgJ5D3T6wMvs+gWpY9d7qRhANrxqAp6LhxIgZhWEw18RfJTGcRxjuLIBr+m8XQ==, tarball: https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.33.0.tgz} engines: {node: '>= 12.0.0'} cpu: [x64] os: [darwin] lightningcss-freebsd-x64@1.31.1: - resolution: {integrity: sha512-1RINmQKAItO6ISxYgPwszQE1BrsVU5aB45ho6O42mu96UiZBxEXsuQ7cJW4zs4CEodPUioj/QrXW1r9pLUM74A==} + resolution: {integrity: sha512-1RINmQKAItO6ISxYgPwszQE1BrsVU5aB45ho6O42mu96UiZBxEXsuQ7cJW4zs4CEodPUioj/QrXW1r9pLUM74A==, tarball: https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.31.1.tgz} engines: {node: '>= 12.0.0'} cpu: [x64] os: [freebsd] lightningcss-freebsd-x64@1.33.0: - resolution: {integrity: sha512-QQM/Ti/hQajJwCY+RiWuCZ9sdtI/XQk7nDK5vC8kkdwixezOlDgvDx7+RT+QjK6FcFT4MpsuoBnHIo/O3StRRg==} + resolution: {integrity: sha512-QQM/Ti/hQajJwCY+RiWuCZ9sdtI/XQk7nDK5vC8kkdwixezOlDgvDx7+RT+QjK6FcFT4MpsuoBnHIo/O3StRRg==, tarball: https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.33.0.tgz} engines: {node: '>= 12.0.0'} cpu: [x64] os: [freebsd] lightningcss-linux-arm-gnueabihf@1.31.1: - resolution: {integrity: sha512-OOCm2//MZJ87CdDK62rZIu+aw9gBv4azMJuA8/KB74wmfS3lnC4yoPHm0uXZ/dvNNHmnZnB8XLAZzObeG0nS1g==} + resolution: {integrity: sha512-OOCm2//MZJ87CdDK62rZIu+aw9gBv4azMJuA8/KB74wmfS3lnC4yoPHm0uXZ/dvNNHmnZnB8XLAZzObeG0nS1g==, tarball: https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.31.1.tgz} engines: {node: '>= 12.0.0'} cpu: [arm] os: [linux] lightningcss-linux-arm-gnueabihf@1.33.0: - resolution: {integrity: sha512-N7FVBe6iS24MlM6R/4RBTxGhQheZGs7tiQ9U32UtF75NzP5Q7xWPRqLBCKxlRQRk3rY1jCIPLzx7WzOhuUIRLQ==} + resolution: {integrity: sha512-N7FVBe6iS24MlM6R/4RBTxGhQheZGs7tiQ9U32UtF75NzP5Q7xWPRqLBCKxlRQRk3rY1jCIPLzx7WzOhuUIRLQ==, tarball: https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.33.0.tgz} engines: {node: '>= 12.0.0'} cpu: [arm] os: [linux] lightningcss-linux-arm64-gnu@1.31.1: - resolution: {integrity: sha512-WKyLWztD71rTnou4xAD5kQT+982wvca7E6QoLpoawZ1gP9JM0GJj4Tp5jMUh9B3AitHbRZ2/H3W5xQmdEOUlLg==} + resolution: {integrity: sha512-WKyLWztD71rTnou4xAD5kQT+982wvca7E6QoLpoawZ1gP9JM0GJj4Tp5jMUh9B3AitHbRZ2/H3W5xQmdEOUlLg==, tarball: https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.31.1.tgz} engines: {node: '>= 12.0.0'} cpu: [arm64] os: [linux] lightningcss-linux-arm64-gnu@1.33.0: - resolution: {integrity: sha512-j2v/itmy4HlNxlc6voKXYgBqNi0Ng2LShg4z7GufpEgs05P+2suBVyi9I6YHq5uoVFx9ETin3eCEhLVyXGQnKg==} + resolution: {integrity: sha512-j2v/itmy4HlNxlc6voKXYgBqNi0Ng2LShg4z7GufpEgs05P+2suBVyi9I6YHq5uoVFx9ETin3eCEhLVyXGQnKg==, tarball: https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.33.0.tgz} engines: {node: '>= 12.0.0'} cpu: [arm64] os: [linux] lightningcss-linux-arm64-musl@1.31.1: - resolution: {integrity: sha512-mVZ7Pg2zIbe3XlNbZJdjs86YViQFoJSpc41CbVmKBPiGmC4YrfeOyz65ms2qpAobVd7WQsbW4PdsSJEMymyIMg==} + resolution: {integrity: sha512-mVZ7Pg2zIbe3XlNbZJdjs86YViQFoJSpc41CbVmKBPiGmC4YrfeOyz65ms2qpAobVd7WQsbW4PdsSJEMymyIMg==, tarball: https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.31.1.tgz} engines: {node: '>= 12.0.0'} cpu: [arm64] os: [linux] lightningcss-linux-arm64-musl@1.33.0: - resolution: {integrity: sha512-yiO5ROMuYQgXbC60yjZU5CYSFZGKXL0HFATXt9mHJn1+zW55oCtMI9NfcVhYLMFDL7gV7oBPon/EmMMGg2OvtQ==} + resolution: {integrity: sha512-yiO5ROMuYQgXbC60yjZU5CYSFZGKXL0HFATXt9mHJn1+zW55oCtMI9NfcVhYLMFDL7gV7oBPon/EmMMGg2OvtQ==, tarball: https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.33.0.tgz} engines: {node: '>= 12.0.0'} cpu: [arm64] os: [linux] lightningcss-linux-x64-gnu@1.31.1: - resolution: {integrity: sha512-xGlFWRMl+0KvUhgySdIaReQdB4FNudfUTARn7q0hh/V67PVGCs3ADFjw+6++kG1RNd0zdGRlEKa+T13/tQjPMA==} + resolution: {integrity: sha512-xGlFWRMl+0KvUhgySdIaReQdB4FNudfUTARn7q0hh/V67PVGCs3ADFjw+6++kG1RNd0zdGRlEKa+T13/tQjPMA==, tarball: https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.31.1.tgz} engines: {node: '>= 12.0.0'} cpu: [x64] os: [linux] lightningcss-linux-x64-gnu@1.33.0: - resolution: {integrity: sha512-ar+Ju7LmcN0Jo4FpL4hpFybwNG9/3A/Br5KW2n2jyODg3MEZXaDYADdemoNS+BDNfMgKvylJLj4S5tyRActuAg==} + resolution: {integrity: sha512-ar+Ju7LmcN0Jo4FpL4hpFybwNG9/3A/Br5KW2n2jyODg3MEZXaDYADdemoNS+BDNfMgKvylJLj4S5tyRActuAg==, tarball: https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.33.0.tgz} engines: {node: '>= 12.0.0'} cpu: [x64] os: [linux] lightningcss-linux-x64-musl@1.31.1: - resolution: {integrity: sha512-eowF8PrKHw9LpoZii5tdZwnBcYDxRw2rRCyvAXLi34iyeYfqCQNA9rmUM0ce62NlPhCvof1+9ivRaTY6pSKDaA==} + resolution: {integrity: sha512-eowF8PrKHw9LpoZii5tdZwnBcYDxRw2rRCyvAXLi34iyeYfqCQNA9rmUM0ce62NlPhCvof1+9ivRaTY6pSKDaA==, tarball: https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.31.1.tgz} engines: {node: '>= 12.0.0'} cpu: [x64] os: [linux] lightningcss-linux-x64-musl@1.33.0: - resolution: {integrity: sha512-RYiYbkokw0trfKqqzfF55lginwEPrD3OJDfTuJzFs1MK6iFnDenaz1fqLLtX4ITG3OktJQXOeTaw1awrBAlZPw==} + resolution: {integrity: sha512-RYiYbkokw0trfKqqzfF55lginwEPrD3OJDfTuJzFs1MK6iFnDenaz1fqLLtX4ITG3OktJQXOeTaw1awrBAlZPw==, tarball: https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.33.0.tgz} engines: {node: '>= 12.0.0'} cpu: [x64] os: [linux] lightningcss-win32-arm64-msvc@1.31.1: - resolution: {integrity: sha512-aJReEbSEQzx1uBlQizAOBSjcmr9dCdL3XuC/6HLXAxmtErsj2ICo5yYggg1qOODQMtnjNQv2UHb9NpOuFtYe4w==} + resolution: {integrity: sha512-aJReEbSEQzx1uBlQizAOBSjcmr9dCdL3XuC/6HLXAxmtErsj2ICo5yYggg1qOODQMtnjNQv2UHb9NpOuFtYe4w==, tarball: https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.31.1.tgz} engines: {node: '>= 12.0.0'} cpu: [arm64] os: [win32] lightningcss-win32-arm64-msvc@1.33.0: - resolution: {integrity: sha512-1K+MPfLSFVpphzpdbfkhlWk6wBrTObBzS2T6db10PNOZgR9GoVsAWzwNyuhUYYbTp23j+4RrncfujZ4uAzXvwA==} + resolution: {integrity: sha512-1K+MPfLSFVpphzpdbfkhlWk6wBrTObBzS2T6db10PNOZgR9GoVsAWzwNyuhUYYbTp23j+4RrncfujZ4uAzXvwA==, tarball: https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.33.0.tgz} engines: {node: '>= 12.0.0'} cpu: [arm64] os: [win32] lightningcss-win32-x64-msvc@1.31.1: - resolution: {integrity: sha512-I9aiFrbd7oYHwlnQDqr1Roz+fTz61oDDJX7n9tYF9FJymH1cIN1DtKw3iYt6b8WZgEjoNwVSncwF4wx/ZedMhw==} + resolution: {integrity: sha512-I9aiFrbd7oYHwlnQDqr1Roz+fTz61oDDJX7n9tYF9FJymH1cIN1DtKw3iYt6b8WZgEjoNwVSncwF4wx/ZedMhw==, tarball: https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.31.1.tgz} engines: {node: '>= 12.0.0'} cpu: [x64] os: [win32] lightningcss-win32-x64-msvc@1.33.0: - resolution: {integrity: sha512-OlEICDx/Xl0FqSp4bry8zFnCvGpig3Gl4gCquvYwHuqJKEC1+n9NgDniFvqHGmMv1ZkqDJrDqKKSykTDX+ehuA==} + resolution: {integrity: sha512-OlEICDx/Xl0FqSp4bry8zFnCvGpig3Gl4gCquvYwHuqJKEC1+n9NgDniFvqHGmMv1ZkqDJrDqKKSykTDX+ehuA==, tarball: https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.33.0.tgz} engines: {node: '>= 12.0.0'} cpu: [x64] os: [win32] lightningcss@1.31.1: - resolution: {integrity: sha512-l51N2r93WmGUye3WuFoN5k10zyvrVs0qfKBhyC5ogUQ6Ew6JUSswh78mbSO+IU3nTWsyOArqPCcShdQSadghBQ==} + resolution: {integrity: sha512-l51N2r93WmGUye3WuFoN5k10zyvrVs0qfKBhyC5ogUQ6Ew6JUSswh78mbSO+IU3nTWsyOArqPCcShdQSadghBQ==, tarball: https://registry.npmjs.org/lightningcss/-/lightningcss-1.31.1.tgz} engines: {node: '>= 12.0.0'} lightningcss@1.33.0: - resolution: {integrity: sha512-WkUDrojuJs0xkgGf2udWxa3yGBRxPtxUkB79i6aCZLRgc7PM8fZe9TosfPDcvEpQZbuFASnHYmRLBLUbmLOIIA==} + resolution: {integrity: sha512-WkUDrojuJs0xkgGf2udWxa3yGBRxPtxUkB79i6aCZLRgc7PM8fZe9TosfPDcvEpQZbuFASnHYmRLBLUbmLOIIA==, tarball: https://registry.npmjs.org/lightningcss/-/lightningcss-1.33.0.tgz} engines: {node: '>= 12.0.0'} lilconfig@2.1.0: - resolution: {integrity: sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==} + resolution: {integrity: sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==, tarball: https://registry.npmjs.org/lilconfig/-/lilconfig-2.1.0.tgz} engines: {node: '>=10'} lilconfig@3.1.3: - resolution: {integrity: sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==} + resolution: {integrity: sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==, tarball: https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz} engines: {node: '>=14'} lines-and-columns@1.2.4: - resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} + resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==, tarball: https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz} lines-and-columns@2.0.3: - resolution: {integrity: sha512-cNOjgCnLB+FnvWWtyRTzmB3POJ+cXxTA81LoW7u8JdmhfXzriropYwpjShnz1QLLWsQwY7nIxoDmcPTwphDK9w==} + resolution: {integrity: sha512-cNOjgCnLB+FnvWWtyRTzmB3POJ+cXxTA81LoW7u8JdmhfXzriropYwpjShnz1QLLWsQwY7nIxoDmcPTwphDK9w==, tarball: https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-2.0.3.tgz} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} linkify-it@5.0.2: - resolution: {integrity: sha512-ONTm2jCMAVZjgQa/Fy1kScXsuOoF5NPTsoFBdE1KVIZ2vAh/r9+Bqo+0jINCBYnavTPQZz38QzFTme79ENoN3Q==} + resolution: {integrity: sha512-ONTm2jCMAVZjgQa/Fy1kScXsuOoF5NPTsoFBdE1KVIZ2vAh/r9+Bqo+0jINCBYnavTPQZz38QzFTme79ENoN3Q==, tarball: https://registry.npmjs.org/linkify-it/-/linkify-it-5.0.2.tgz} linkifyjs@4.3.3: - resolution: {integrity: sha512-P8aEP5U/D1/IlTY2OeYsErdwh9bGuLE30NcXtKEjgdHcahveQoQwM2yZNsioQHsWFz0P7KKudisbrzCgR0sDHg==} + resolution: {integrity: sha512-P8aEP5U/D1/IlTY2OeYsErdwh9bGuLE30NcXtKEjgdHcahveQoQwM2yZNsioQHsWFz0P7KKudisbrzCgR0sDHg==, tarball: https://registry.npmjs.org/linkifyjs/-/linkifyjs-4.3.3.tgz} lint-staged@15.2.10: - resolution: {integrity: sha512-5dY5t743e1byO19P9I4b3x8HJwalIznL5E1FWYnU6OWw33KxNBSLAc6Cy7F2PsFEO8FKnLwjwm5hx7aMF0jzZg==} + resolution: {integrity: sha512-5dY5t743e1byO19P9I4b3x8HJwalIznL5E1FWYnU6OWw33KxNBSLAc6Cy7F2PsFEO8FKnLwjwm5hx7aMF0jzZg==, tarball: https://registry.npmjs.org/lint-staged/-/lint-staged-15.2.10.tgz} engines: {node: '>=18.12.0'} hasBin: true listr2@10.2.2: - resolution: {integrity: sha512-JtNtbZj8q5BnDMR7trpwvwk3RIrANtIVzEUm8w7amp6xelLgyuq+4WZoTH913XaQAoH/cNdYhaNzBPA2U3xbDw==} + resolution: {integrity: sha512-JtNtbZj8q5BnDMR7trpwvwk3RIrANtIVzEUm8w7amp6xelLgyuq+4WZoTH913XaQAoH/cNdYhaNzBPA2U3xbDw==, tarball: https://registry.npmjs.org/listr2/-/listr2-10.2.2.tgz} engines: {node: '>=22.13.0'} listr2@8.2.5: - resolution: {integrity: sha512-iyAZCeyD+c1gPyE9qpFu8af0Y+MRtmKOncdGoA2S5EY8iFq99dmmvkNnHiWo+pj0s7yH7l3KPIgee77tKpXPWQ==} + resolution: {integrity: sha512-iyAZCeyD+c1gPyE9qpFu8af0Y+MRtmKOncdGoA2S5EY8iFq99dmmvkNnHiWo+pj0s7yH7l3KPIgee77tKpXPWQ==, tarball: https://registry.npmjs.org/listr2/-/listr2-8.2.5.tgz} engines: {node: '>=18.0.0'} lit-element@3.3.3: - resolution: {integrity: sha512-XbeRxmTHubXENkV4h8RIPyr8lXc+Ff28rkcQzw3G6up2xg5E8Zu1IgOWIwBLEQsu3cOVFqdYwiVi0hv0SlpqUA==} + resolution: {integrity: sha512-XbeRxmTHubXENkV4h8RIPyr8lXc+Ff28rkcQzw3G6up2xg5E8Zu1IgOWIwBLEQsu3cOVFqdYwiVi0hv0SlpqUA==, tarball: https://registry.npmjs.org/lit-element/-/lit-element-3.3.3.tgz} lit-html@2.8.0: - resolution: {integrity: sha512-o9t+MQM3P4y7M7yNzqAyjp7z+mQGa4NS4CxiyLqFPyFWyc4O+nodLrkrxSaCTrla6M5YOLaT3RpbbqjszB5g3Q==} + resolution: {integrity: sha512-o9t+MQM3P4y7M7yNzqAyjp7z+mQGa4NS4CxiyLqFPyFWyc4O+nodLrkrxSaCTrla6M5YOLaT3RpbbqjszB5g3Q==, tarball: https://registry.npmjs.org/lit-html/-/lit-html-2.8.0.tgz} lit@2.8.0: - resolution: {integrity: sha512-4Sc3OFX9QHOJaHbmTMk28SYgVxLN3ePDjg7hofEft2zWlehFL3LiAuapWc4U/kYwMYJSh2hTCPZ6/LIC7ii0MA==} + resolution: {integrity: sha512-4Sc3OFX9QHOJaHbmTMk28SYgVxLN3ePDjg7hofEft2zWlehFL3LiAuapWc4U/kYwMYJSh2hTCPZ6/LIC7ii0MA==, tarball: https://registry.npmjs.org/lit/-/lit-2.8.0.tgz} lmdb@3.5.6: - resolution: {integrity: sha512-j3uE8ReKNyUWDjhfEFSJqE/1DLtfTR5Z8yFzVHvBjAk37wNg7HdScjcv8ttPHRvrdgPQMPWxFFI0SsdBzI5lBw==} + resolution: {integrity: sha512-j3uE8ReKNyUWDjhfEFSJqE/1DLtfTR5Z8yFzVHvBjAk37wNg7HdScjcv8ttPHRvrdgPQMPWxFFI0SsdBzI5lBw==, tarball: https://registry.npmjs.org/lmdb/-/lmdb-3.5.6.tgz} hasBin: true loader-runner@4.3.2: - resolution: {integrity: sha512-DFEqQ3ihfS9blba08cLfYf1NRAIEm+dDjic073DRDc3/JspI/8wYmtDsHwd3+4hwvdxSK7PGaElfTmm0awWJ4w==} + resolution: {integrity: sha512-DFEqQ3ihfS9blba08cLfYf1NRAIEm+dDjic073DRDc3/JspI/8wYmtDsHwd3+4hwvdxSK7PGaElfTmm0awWJ4w==, tarball: https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.2.tgz} engines: {node: '>=6.11.5'} loader-utils@2.0.4: - resolution: {integrity: sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==} + resolution: {integrity: sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==, tarball: https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.4.tgz} engines: {node: '>=8.9.0'} loader-utils@3.3.1: - resolution: {integrity: sha512-FMJTLMXfCLMLfJxcX9PFqX5qD88Z5MRGaZCVzfuqeZSPsyiBzs+pahDQjbIWz2QIzPZz0NX9Zy4FX3lmK6YHIg==} + resolution: {integrity: sha512-FMJTLMXfCLMLfJxcX9PFqX5qD88Z5MRGaZCVzfuqeZSPsyiBzs+pahDQjbIWz2QIzPZz0NX9Zy4FX3lmK6YHIg==, tarball: https://registry.npmjs.org/loader-utils/-/loader-utils-3.3.1.tgz} engines: {node: '>= 12.13.0'} local-pkg@1.2.1: - resolution: {integrity: sha512-++gUqRDEvcnN6Zhqrr+y/CkVEHhlrR96vZn3nZZPYzMcBUyBtTKzB9NadClFIsIVSsu+3i9tfk/erqy9kAmt7Q==} + resolution: {integrity: sha512-++gUqRDEvcnN6Zhqrr+y/CkVEHhlrR96vZn3nZZPYzMcBUyBtTKzB9NadClFIsIVSsu+3i9tfk/erqy9kAmt7Q==, tarball: https://registry.npmjs.org/local-pkg/-/local-pkg-1.2.1.tgz} engines: {node: '>=14'} locate-path@5.0.0: - resolution: {integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==} + resolution: {integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==, tarball: https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz} engines: {node: '>=8'} locate-path@6.0.0: - resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} + resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==, tarball: https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz} engines: {node: '>=10'} locate-path@7.2.0: - resolution: {integrity: sha512-gvVijfZvn7R+2qyPX8mAuKcFGDf6Nc61GdvGafQsHL0sBIxfKzA+usWn4GFC/bk+QdwPUD4kWFJLhElipq+0VA==} + resolution: {integrity: sha512-gvVijfZvn7R+2qyPX8mAuKcFGDf6Nc61GdvGafQsHL0sBIxfKzA+usWn4GFC/bk+QdwPUD4kWFJLhElipq+0VA==, tarball: https://registry.npmjs.org/locate-path/-/locate-path-7.2.0.tgz} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} lodash-es@4.18.1: - resolution: {integrity: sha512-J8xewKD/Gk22OZbhpOVSwcs60zhd95ESDwezOFuA3/099925PdHJ7OFHNTGtajL3AlZkykD32HykiMo+BIBI8A==} + resolution: {integrity: sha512-J8xewKD/Gk22OZbhpOVSwcs60zhd95ESDwezOFuA3/099925PdHJ7OFHNTGtajL3AlZkykD32HykiMo+BIBI8A==, tarball: https://registry.npmjs.org/lodash-es/-/lodash-es-4.18.1.tgz} lodash.camelcase@4.3.0: - resolution: {integrity: sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==} + resolution: {integrity: sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==, tarball: https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz} lodash.clonedeepwith@4.5.0: - resolution: {integrity: sha512-QRBRSxhbtsX1nc0baxSkkK5WlVTTm/s48DSukcGcWZwIyI8Zz+lB+kFiELJXtzfH4Aj6kMWQ1VWW4U5uUDgZMA==} + resolution: {integrity: sha512-QRBRSxhbtsX1nc0baxSkkK5WlVTTm/s48DSukcGcWZwIyI8Zz+lB+kFiELJXtzfH4Aj6kMWQ1VWW4U5uUDgZMA==, tarball: https://registry.npmjs.org/lodash.clonedeepwith/-/lodash.clonedeepwith-4.5.0.tgz} lodash.debounce@4.0.8: - resolution: {integrity: sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==} + resolution: {integrity: sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==, tarball: https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz} lodash.isequal@4.5.0: - resolution: {integrity: sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==} + resolution: {integrity: sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==, tarball: https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz} deprecated: This package is deprecated. Use require('node:util').isDeepStrictEqual instead. lodash.memoize@4.1.2: - resolution: {integrity: sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==} + resolution: {integrity: sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==, tarball: https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz} lodash.merge@4.6.2: - resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} + resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==, tarball: https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz} lodash.sortby@4.7.0: - resolution: {integrity: sha512-HDWXG8isMntAyRF5vZ7xKuEvOhT4AhlRt/3czTSjvGUxjYCBVRQY48ViDHyfYz9VIoBkW4TMGQNapx+l3RUwdA==} + resolution: {integrity: sha512-HDWXG8isMntAyRF5vZ7xKuEvOhT4AhlRt/3czTSjvGUxjYCBVRQY48ViDHyfYz9VIoBkW4TMGQNapx+l3RUwdA==, tarball: https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz} lodash.uniq@4.5.0: - resolution: {integrity: sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==} + resolution: {integrity: sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==, tarball: https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz} lodash@4.18.1: - resolution: {integrity: sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==} + resolution: {integrity: sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==, tarball: https://registry.npmjs.org/lodash/-/lodash-4.18.1.tgz} log-symbols@4.1.0: - resolution: {integrity: sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==} + resolution: {integrity: sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==, tarball: https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz} engines: {node: '>=10'} log-symbols@7.0.1: - resolution: {integrity: sha512-ja1E3yCr9i/0hmBVaM0bfwDjnGy8I/s6PP4DFp+yP+a+mrHO4Rm7DtmnqROTUkHIkqffC84YY7AeqX6oFk0WFg==} + resolution: {integrity: sha512-ja1E3yCr9i/0hmBVaM0bfwDjnGy8I/s6PP4DFp+yP+a+mrHO4Rm7DtmnqROTUkHIkqffC84YY7AeqX6oFk0WFg==, tarball: https://registry.npmjs.org/log-symbols/-/log-symbols-7.0.1.tgz} engines: {node: '>=18'} log-update@6.1.0: - resolution: {integrity: sha512-9ie8ItPR6tjY5uYJh8K/Zrv/RMZ5VOlOWvtZdEHYSTFKZfIBPQa9tOAEeAWhd+AnIneLJ22w5fjOYtoutpWq5w==} + resolution: {integrity: sha512-9ie8ItPR6tjY5uYJh8K/Zrv/RMZ5VOlOWvtZdEHYSTFKZfIBPQa9tOAEeAWhd+AnIneLJ22w5fjOYtoutpWq5w==, tarball: https://registry.npmjs.org/log-update/-/log-update-6.1.0.tgz} engines: {node: '>=18'} log4js@6.9.1: - resolution: {integrity: sha512-1somDdy9sChrr9/f4UlzhdaGfDR2c/SaD2a4T7qEkG4jTS57/B3qmnjLYePwQ8cqWnUHZI0iAKxMBpCZICiZ2g==} + resolution: {integrity: sha512-1somDdy9sChrr9/f4UlzhdaGfDR2c/SaD2a4T7qEkG4jTS57/B3qmnjLYePwQ8cqWnUHZI0iAKxMBpCZICiZ2g==, tarball: https://registry.npmjs.org/log4js/-/log4js-6.9.1.tgz} engines: {node: '>=8.0'} long-timeout@0.1.1: - resolution: {integrity: sha512-BFRuQUqc7x2NWxfJBCyUrN8iYUYznzL9JROmRz1gZ6KlOIgmoD+njPVbb+VNn2nGMKggMsK79iUNErillsrx7w==} + resolution: {integrity: sha512-BFRuQUqc7x2NWxfJBCyUrN8iYUYznzL9JROmRz1gZ6KlOIgmoD+njPVbb+VNn2nGMKggMsK79iUNErillsrx7w==, tarball: https://registry.npmjs.org/long-timeout/-/long-timeout-0.1.1.tgz} loose-envify@1.4.0: - resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==} + resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==, tarball: https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz} hasBin: true loupe@3.2.1: - resolution: {integrity: sha512-CdzqowRJCeLU72bHvWqwRBBlLcMEtIvGrlvef74kMnV2AolS9Y8xUv1I0U/MNAWMhBlKIoyuEgoJ0t/bbwHbLQ==} + resolution: {integrity: sha512-CdzqowRJCeLU72bHvWqwRBBlLcMEtIvGrlvef74kMnV2AolS9Y8xUv1I0U/MNAWMhBlKIoyuEgoJ0t/bbwHbLQ==, tarball: https://registry.npmjs.org/loupe/-/loupe-3.2.1.tgz} lower-case@2.0.2: - resolution: {integrity: sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==} + resolution: {integrity: sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==, tarball: https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz} lowlight@3.3.0: - resolution: {integrity: sha512-0JNhgFoPvP6U6lE/UdVsSq99tn6DhjjpAj5MxG49ewd2mOBVtwWYIT8ClyABhq198aXXODMU6Ox8DrGy/CpTZQ==} + resolution: {integrity: sha512-0JNhgFoPvP6U6lE/UdVsSq99tn6DhjjpAj5MxG49ewd2mOBVtwWYIT8ClyABhq198aXXODMU6Ox8DrGy/CpTZQ==, tarball: https://registry.npmjs.org/lowlight/-/lowlight-3.3.0.tgz} lru-cache@10.4.3: - resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==} + resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==, tarball: https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz} lru-cache@11.5.2: - resolution: {integrity: sha512-4pfM1Ff0x50o0tQwb5ucw/RzNyD0/YJME6IVcStalZuMWxdt3sR3huStTtxz4PUmvZfRguvDejasvQ2kifR11g==} + resolution: {integrity: sha512-4pfM1Ff0x50o0tQwb5ucw/RzNyD0/YJME6IVcStalZuMWxdt3sR3huStTtxz4PUmvZfRguvDejasvQ2kifR11g==, tarball: https://registry.npmjs.org/lru-cache/-/lru-cache-11.5.2.tgz} engines: {node: 20 || >=22} lru-cache@5.1.1: - resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} + resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==, tarball: https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz} lunr@2.3.9: - resolution: {integrity: sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow==} + resolution: {integrity: sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow==, tarball: https://registry.npmjs.org/lunr/-/lunr-2.3.9.tgz} luxon@3.7.2: - resolution: {integrity: sha512-vtEhXh/gNjI9Yg1u4jX/0YVPMvxzHuGgCm6tC5kZyb08yjGWGnqAjGJvcXbqQR2P3MyMEFnRbpcdFS6PBcLqew==} + resolution: {integrity: sha512-vtEhXh/gNjI9Yg1u4jX/0YVPMvxzHuGgCm6tC5kZyb08yjGWGnqAjGJvcXbqQR2P3MyMEFnRbpcdFS6PBcLqew==, tarball: https://registry.npmjs.org/luxon/-/luxon-3.7.2.tgz} engines: {node: '>=12'} lz-string@1.5.0: - resolution: {integrity: sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==} + resolution: {integrity: sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==, tarball: https://registry.npmjs.org/lz-string/-/lz-string-1.5.0.tgz} hasBin: true magic-string@0.30.21: - resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==} + resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==, tarball: https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz} magic-string@1.0.0: - resolution: {integrity: sha512-CGvjzMN08iv6w1mm4/x3Gh1hLb4VnyRUA15FFpl6CsCIGGoe36k7kY5KNz9QDbSBN5I/fWHM6ZlIkUTa5xdUEA==} + resolution: {integrity: sha512-CGvjzMN08iv6w1mm4/x3Gh1hLb4VnyRUA15FFpl6CsCIGGoe36k7kY5KNz9QDbSBN5I/fWHM6ZlIkUTa5xdUEA==, tarball: https://registry.npmjs.org/magic-string/-/magic-string-1.0.0.tgz} magicast@0.5.3: - resolution: {integrity: sha512-pVKE4UdSQ7DvHzivsCIFx2BJn1mHG6KsyrFcaxFx6tONdneEuThrDx0Cj3AMg58KyN4pzYT+LHOotxDQDjNvkw==} + resolution: {integrity: sha512-pVKE4UdSQ7DvHzivsCIFx2BJn1mHG6KsyrFcaxFx6tONdneEuThrDx0Cj3AMg58KyN4pzYT+LHOotxDQDjNvkw==, tarball: https://registry.npmjs.org/magicast/-/magicast-0.5.3.tgz} make-dir@2.1.0: - resolution: {integrity: sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==} + resolution: {integrity: sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==, tarball: https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz} engines: {node: '>=6'} make-dir@3.1.0: - resolution: {integrity: sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==} + resolution: {integrity: sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==, tarball: https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz} engines: {node: '>=8'} make-dir@4.0.0: - resolution: {integrity: sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==} + resolution: {integrity: sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==, tarball: https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz} engines: {node: '>=10'} make-dir@5.1.0: - resolution: {integrity: sha512-IfpFq6UM39dUNiphpA6uDezNx/AvWyhwfICWPR3t1VspkgkMZrL+Rk1RbN1bx+aeNYwOrqGJgEgV3yotk+ZUVw==} + resolution: {integrity: sha512-IfpFq6UM39dUNiphpA6uDezNx/AvWyhwfICWPR3t1VspkgkMZrL+Rk1RbN1bx+aeNYwOrqGJgEgV3yotk+ZUVw==, tarball: https://registry.npmjs.org/make-dir/-/make-dir-5.1.0.tgz} engines: {node: '>=18'} make-error@1.3.6: - resolution: {integrity: sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==} + resolution: {integrity: sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==, tarball: https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz} makeerror@1.0.12: - resolution: {integrity: sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==} + resolution: {integrity: sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==, tarball: https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz} markdown-it@14.3.0: - resolution: {integrity: sha512-RCEsPjR+sr0x+AuYp601tKTkgFG4YEPLCzHST3cQ/fhlJkqAkz1L2/Qbp1j9qw5SBwQHFBoW8+hoN5xssOF0Tw==} + resolution: {integrity: sha512-RCEsPjR+sr0x+AuYp601tKTkgFG4YEPLCzHST3cQ/fhlJkqAkz1L2/Qbp1j9qw5SBwQHFBoW8+hoN5xssOF0Tw==, tarball: https://registry.npmjs.org/markdown-it/-/markdown-it-14.3.0.tgz} hasBin: true marked@12.0.2: - resolution: {integrity: sha512-qXUm7e/YKFoqFPYPa3Ukg9xlI5cyAtGmyEIzMfW//m6kXwCy2Ps9DYf5ioijFKQ8qyuscrHoY04iJGctu2Kg0Q==} + resolution: {integrity: sha512-qXUm7e/YKFoqFPYPa3Ukg9xlI5cyAtGmyEIzMfW//m6kXwCy2Ps9DYf5ioijFKQ8qyuscrHoY04iJGctu2Kg0Q==, tarball: https://registry.npmjs.org/marked/-/marked-12.0.2.tgz} engines: {node: '>= 18'} hasBin: true marked@16.4.2: - resolution: {integrity: sha512-TI3V8YYWvkVf3KJe1dRkpnjs68JUPyEa5vjKrp1XEEJUAOaQc+Qj+L1qWbPd0SJuAdQkFU0h73sXXqwDYxsiDA==} + resolution: {integrity: sha512-TI3V8YYWvkVf3KJe1dRkpnjs68JUPyEa5vjKrp1XEEJUAOaQc+Qj+L1qWbPd0SJuAdQkFU0h73sXXqwDYxsiDA==, tarball: https://registry.npmjs.org/marked/-/marked-16.4.2.tgz} engines: {node: '>= 20'} hasBin: true marked@4.3.0: - resolution: {integrity: sha512-PRsaiG84bK+AMvxziE/lCFss8juXjNaWzVbN5tXAm4XjeaS9NAHhop+PjQxz2A9h8Q4M/xGmzP8vqNwy6JeK0A==} + resolution: {integrity: sha512-PRsaiG84bK+AMvxziE/lCFss8juXjNaWzVbN5tXAm4XjeaS9NAHhop+PjQxz2A9h8Q4M/xGmzP8vqNwy6JeK0A==, tarball: https://registry.npmjs.org/marked/-/marked-4.3.0.tgz} engines: {node: '>= 12'} hasBin: true math-intrinsics@1.1.0: - resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} + resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==, tarball: https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz} engines: {node: '>= 0.4'} md5@2.3.0: - resolution: {integrity: sha512-T1GITYmFaKuO91vxyoQMFETst+O71VUPEU3ze5GNzDm0OWdP8v1ziTaAEPUr/3kLsY3Sftgz242A1SetQiDL7g==} + resolution: {integrity: sha512-T1GITYmFaKuO91vxyoQMFETst+O71VUPEU3ze5GNzDm0OWdP8v1ziTaAEPUr/3kLsY3Sftgz242A1SetQiDL7g==, tarball: https://registry.npmjs.org/md5/-/md5-2.3.0.tgz} mdn-data@2.0.14: - resolution: {integrity: sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==} + resolution: {integrity: sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==, tarball: https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.14.tgz} mdn-data@2.0.28: - resolution: {integrity: sha512-aylIc7Z9y4yzHYAJNuESG3hfhC+0Ibp/MAMiaOZgNv4pmEdFyfZhhhny4MNiAfWdBQ1RQ2mfDWmM1x8SvGyp8g==} + resolution: {integrity: sha512-aylIc7Z9y4yzHYAJNuESG3hfhC+0Ibp/MAMiaOZgNv4pmEdFyfZhhhny4MNiAfWdBQ1RQ2mfDWmM1x8SvGyp8g==, tarball: https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.28.tgz} mdn-data@2.0.30: - resolution: {integrity: sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==} + resolution: {integrity: sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==, tarball: https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.30.tgz} mdn-data@2.27.1: - resolution: {integrity: sha512-9Yubnt3e8A0OKwxYSXyhLymGW4sCufcLG6VdiDdUGVkPhpqLxlvP5vl1983gQjJl3tqbrM731mjaZaP68AgosQ==} + resolution: {integrity: sha512-9Yubnt3e8A0OKwxYSXyhLymGW4sCufcLG6VdiDdUGVkPhpqLxlvP5vl1983gQjJl3tqbrM731mjaZaP68AgosQ==, tarball: https://registry.npmjs.org/mdn-data/-/mdn-data-2.27.1.tgz} mdn-data@2.28.1: - resolution: {integrity: sha512-U9w+PzSZ00Z5m9rZ5ARVFL5xOfuCHdKYi/1RRwDCJsboFgJDNT3zT6PIPD7mZQYaQLhsZM3GfDRgSMRHhSmVng==} + resolution: {integrity: sha512-U9w+PzSZ00Z5m9rZ5ARVFL5xOfuCHdKYi/1RRwDCJsboFgJDNT3zT6PIPD7mZQYaQLhsZM3GfDRgSMRHhSmVng==, tarball: https://registry.npmjs.org/mdn-data/-/mdn-data-2.28.1.tgz} mdurl@2.0.0: - resolution: {integrity: sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==} + resolution: {integrity: sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==, tarball: https://registry.npmjs.org/mdurl/-/mdurl-2.0.0.tgz} media-typer@0.3.0: - resolution: {integrity: sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==} + resolution: {integrity: sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==, tarball: https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz} engines: {node: '>= 0.6'} media-typer@1.1.0: - resolution: {integrity: sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==} + resolution: {integrity: sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==, tarball: https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz} engines: {node: '>= 0.8'} memfs@3.5.3: - resolution: {integrity: sha512-UERzLsxzllchadvbPs5aolHh65ISpKpM+ccLbOJ8/vvpBKmAWf+la7dXFy7Mr0ySHbdHrFv5kGFCUHHe6GFEmw==} + resolution: {integrity: sha512-UERzLsxzllchadvbPs5aolHh65ISpKpM+ccLbOJ8/vvpBKmAWf+la7dXFy7Mr0ySHbdHrFv5kGFCUHHe6GFEmw==, tarball: https://registry.npmjs.org/memfs/-/memfs-3.5.3.tgz} engines: {node: '>= 4.0.0'} memfs@4.64.0: - resolution: {integrity: sha512-Kw72fgY7Wn+sD8KmtNWSafl1dz0UvAsE/PHs3YVfLiaZuA3HxNm9sRLqAu0ATiBGJvME1PxZXbBZPv5GycDeAw==} + resolution: {integrity: sha512-Kw72fgY7Wn+sD8KmtNWSafl1dz0UvAsE/PHs3YVfLiaZuA3HxNm9sRLqAu0ATiBGJvME1PxZXbBZPv5GycDeAw==, tarball: https://registry.npmjs.org/memfs/-/memfs-4.64.0.tgz} peerDependencies: tslib: '2' merge-descriptors@1.0.3: - resolution: {integrity: sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==} + resolution: {integrity: sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==, tarball: https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz} merge-descriptors@2.0.0: - resolution: {integrity: sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==} + resolution: {integrity: sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==, tarball: https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-2.0.0.tgz} engines: {node: '>=18'} merge-stream@2.0.0: - resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==} + resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==, tarball: https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz} merge2@1.4.1: - resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} + resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==, tarball: https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz} engines: {node: '>= 8'} mermaid@11.16.0: - resolution: {integrity: sha512-Zvm3kbstgdpvIJPPItlL7fppIZ3kibvc1oZIGxdvk9t6UFz6flv+Jw7FtRGKwfcI8OckmH04LqG6LlS6X4B1pA==} + resolution: {integrity: sha512-Zvm3kbstgdpvIJPPItlL7fppIZ3kibvc1oZIGxdvk9t6UFz6flv+Jw7FtRGKwfcI8OckmH04LqG6LlS6X4B1pA==, tarball: https://registry.npmjs.org/mermaid/-/mermaid-11.16.0.tgz} methods@1.1.2: - resolution: {integrity: sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==} + resolution: {integrity: sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==, tarball: https://registry.npmjs.org/methods/-/methods-1.1.2.tgz} engines: {node: '>= 0.6'} micromatch@4.0.8: - resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==} + resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==, tarball: https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz} engines: {node: '>=8.6'} mime-db@1.52.0: - resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} + resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==, tarball: https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz} engines: {node: '>= 0.6'} mime-db@1.54.0: - resolution: {integrity: sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==} + resolution: {integrity: sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==, tarball: https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz} engines: {node: '>= 0.6'} mime-types@2.1.35: - resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} + resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==, tarball: https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz} engines: {node: '>= 0.6'} mime-types@3.0.2: - resolution: {integrity: sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==} + resolution: {integrity: sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==, tarball: https://registry.npmjs.org/mime-types/-/mime-types-3.0.2.tgz} engines: {node: '>=18'} mime@1.6.0: - resolution: {integrity: sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==} + resolution: {integrity: sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==, tarball: https://registry.npmjs.org/mime/-/mime-1.6.0.tgz} engines: {node: '>=4'} hasBin: true mimic-fn@2.1.0: - resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==} + resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==, tarball: https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz} engines: {node: '>=6'} mimic-fn@4.0.0: - resolution: {integrity: sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==} + resolution: {integrity: sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==, tarball: https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz} engines: {node: '>=12'} mimic-function@5.0.1: - resolution: {integrity: sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==} + resolution: {integrity: sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==, tarball: https://registry.npmjs.org/mimic-function/-/mimic-function-5.0.1.tgz} engines: {node: '>=18'} min-indent@1.0.1: - resolution: {integrity: sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==} + resolution: {integrity: sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==, tarball: https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz} engines: {node: '>=4'} mini-css-extract-plugin@2.10.2: - resolution: {integrity: sha512-AOSS0IdEB95ayVkxn5oGzNQwqAi2J0Jb/kKm43t7H73s8+f5873g0yuj0PNvK4dO75mu5DHg4nlgp4k6Kga8eg==} + resolution: {integrity: sha512-AOSS0IdEB95ayVkxn5oGzNQwqAi2J0Jb/kKm43t7H73s8+f5873g0yuj0PNvK4dO75mu5DHg4nlgp4k6Kga8eg==, tarball: https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-2.10.2.tgz} engines: {node: '>= 12.13.0'} peerDependencies: webpack: ^5.0.0 mini-css-extract-plugin@2.4.7: - resolution: {integrity: sha512-euWmddf0sk9Nv1O0gfeeUAvAkoSlWncNLF77C0TP2+WoPvy8mAHKOzMajcCz2dzvyt3CNgxb1obIEVFIRxaipg==} + resolution: {integrity: sha512-euWmddf0sk9Nv1O0gfeeUAvAkoSlWncNLF77C0TP2+WoPvy8mAHKOzMajcCz2dzvyt3CNgxb1obIEVFIRxaipg==, tarball: https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-2.4.7.tgz} engines: {node: '>= 12.13.0'} peerDependencies: webpack: ^5.0.0 mini-svg-data-uri@1.4.4: - resolution: {integrity: sha512-r9deDe9p5FJUPZAk3A59wGH7Ii9YrjjWw0jmw/liSbHl2CHiyXj6FcDXDu2K3TjVAXqiJdaw3xxwlZZr9E6nHg==} + resolution: {integrity: sha512-r9deDe9p5FJUPZAk3A59wGH7Ii9YrjjWw0jmw/liSbHl2CHiyXj6FcDXDu2K3TjVAXqiJdaw3xxwlZZr9E6nHg==, tarball: https://registry.npmjs.org/mini-svg-data-uri/-/mini-svg-data-uri-1.4.4.tgz} hasBin: true minimalistic-assert@1.0.1: - resolution: {integrity: sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==} + resolution: {integrity: sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==, tarball: https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz} minimatch@10.2.3: - resolution: {integrity: sha512-Rwi3pnapEqirPSbWbrZaa6N3nmqq4Xer/2XooiOKyV3q12ML06f7MOuc5DVH8ONZIFhwIYQ3yzPH4nt7iWHaTg==} + resolution: {integrity: sha512-Rwi3pnapEqirPSbWbrZaa6N3nmqq4Xer/2XooiOKyV3q12ML06f7MOuc5DVH8ONZIFhwIYQ3yzPH4nt7iWHaTg==, tarball: https://registry.npmjs.org/minimatch/-/minimatch-10.2.3.tgz} engines: {node: 18 || 20 || >=22} minimatch@10.2.5: - resolution: {integrity: sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==} + resolution: {integrity: sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==, tarball: https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz} engines: {node: 18 || 20 || >=22} minimatch@3.1.5: - resolution: {integrity: sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==} + resolution: {integrity: sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==, tarball: https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz} minimatch@5.1.9: - resolution: {integrity: sha512-7o1wEA2RyMP7Iu7GNba9vc0RWWGACJOCZBJX2GJWip0ikV+wcOsgVuY9uE8CPiyQhkGFSlhuSkZPavN7u1c2Fw==} + resolution: {integrity: sha512-7o1wEA2RyMP7Iu7GNba9vc0RWWGACJOCZBJX2GJWip0ikV+wcOsgVuY9uE8CPiyQhkGFSlhuSkZPavN7u1c2Fw==, tarball: https://registry.npmjs.org/minimatch/-/minimatch-5.1.9.tgz} engines: {node: '>=10'} minimatch@9.0.3: - resolution: {integrity: sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==} + resolution: {integrity: sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==, tarball: https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz} engines: {node: '>=16 || 14 >=14.17'} minimatch@9.0.9: - resolution: {integrity: sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==} + resolution: {integrity: sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==, tarball: https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz} engines: {node: '>=16 || 14 >=14.17'} minimist@1.2.8: - resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} + resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==, tarball: https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz} minimizer-webpack-plugin@5.6.1: - resolution: {integrity: sha512-DoeAZz8Q1C1znwsUzej1fdoi4jCf7/+Em27ouLqfK/+3m8G+D7yDhUwrc3CNhjSzGUN1kn7Iv4sWmjflQHenpw==} + resolution: {integrity: sha512-DoeAZz8Q1C1znwsUzej1fdoi4jCf7/+Em27ouLqfK/+3m8G+D7yDhUwrc3CNhjSzGUN1kn7Iv4sWmjflQHenpw==, tarball: https://registry.npmjs.org/minimizer-webpack-plugin/-/minimizer-webpack-plugin-5.6.1.tgz} engines: {node: '>= 10.13.0'} peerDependencies: '@minify-html/node': '*' @@ -11731,94 +11793,94 @@ packages: optional: true minipass@7.1.3: - resolution: {integrity: sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==} + resolution: {integrity: sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==, tarball: https://registry.npmjs.org/minipass/-/minipass-7.1.3.tgz} engines: {node: '>=16 || 14 >=14.17'} mkdirp@1.0.4: - resolution: {integrity: sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==} + resolution: {integrity: sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==, tarball: https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz} engines: {node: '>=10'} hasBin: true mlly@1.8.2: - resolution: {integrity: sha512-d+ObxMQFmbt10sretNDytwt85VrbkhhUA/JBGm1MPaWJ65Cl4wOgLaB1NYvJSZ0Ef03MMEU/0xpPMXUIQ29UfA==} + resolution: {integrity: sha512-d+ObxMQFmbt10sretNDytwt85VrbkhhUA/JBGm1MPaWJ65Cl4wOgLaB1NYvJSZ0Ef03MMEU/0xpPMXUIQ29UfA==, tarball: https://registry.npmjs.org/mlly/-/mlly-1.8.2.tgz} mock-socket@9.0.3: - resolution: {integrity: sha512-SxIiD2yE/By79p3cNAAXyLQWTvEFNEzcAO7PH+DzRqKSFaplAPFjiQLmw8ofmpCsZf+Rhfn2/xCJagpdGmYdTw==} + resolution: {integrity: sha512-SxIiD2yE/By79p3cNAAXyLQWTvEFNEzcAO7PH+DzRqKSFaplAPFjiQLmw8ofmpCsZf+Rhfn2/xCJagpdGmYdTw==, tarball: https://registry.npmjs.org/mock-socket/-/mock-socket-9.0.3.tgz} engines: {node: '>= 8'} monaco-editor@0.33.0: - resolution: {integrity: sha512-VcRWPSLIUEgQJQIE0pVT8FcGBIgFoxz7jtqctE+IiCxWugD0DwgyQBcZBhdSrdMC84eumoqMZsGl2GTreOzwqw==} + resolution: {integrity: sha512-VcRWPSLIUEgQJQIE0pVT8FcGBIgFoxz7jtqctE+IiCxWugD0DwgyQBcZBhdSrdMC84eumoqMZsGl2GTreOzwqw==, tarball: https://registry.npmjs.org/monaco-editor/-/monaco-editor-0.33.0.tgz} mrmime@1.0.1: - resolution: {integrity: sha512-hzzEagAgDyoU1Q6yg5uI+AorQgdvMCur3FcKf7NhMKWsaYg+RnbTyHRa/9IlLF9rf455MOCtcqqrQQ83pPP7Uw==} + resolution: {integrity: sha512-hzzEagAgDyoU1Q6yg5uI+AorQgdvMCur3FcKf7NhMKWsaYg+RnbTyHRa/9IlLF9rf455MOCtcqqrQQ83pPP7Uw==, tarball: https://registry.npmjs.org/mrmime/-/mrmime-1.0.1.tgz} engines: {node: '>=10'} mrmime@2.0.1: - resolution: {integrity: sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ==} + resolution: {integrity: sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ==, tarball: https://registry.npmjs.org/mrmime/-/mrmime-2.0.1.tgz} engines: {node: '>=10'} ms@2.0.0: - resolution: {integrity: sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==} + resolution: {integrity: sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==, tarball: https://registry.npmjs.org/ms/-/ms-2.0.0.tgz} ms@2.1.2: - resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==} + resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==, tarball: https://registry.npmjs.org/ms/-/ms-2.1.2.tgz} ms@2.1.3: - resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==, tarball: https://registry.npmjs.org/ms/-/ms-2.1.3.tgz} msgpackr-extract@3.0.4: - resolution: {integrity: sha512-4kmO/MdyUIkLIvTPr8VHLil4AtoKIoniWPIEk5+CDy0xnWC84azhSFmuJ7PxZdsYtiP5kEeQsORAVIeMgxT+Hw==} + resolution: {integrity: sha512-4kmO/MdyUIkLIvTPr8VHLil4AtoKIoniWPIEk5+CDy0xnWC84azhSFmuJ7PxZdsYtiP5kEeQsORAVIeMgxT+Hw==, tarball: https://registry.npmjs.org/msgpackr-extract/-/msgpackr-extract-3.0.4.tgz} hasBin: true msgpackr@1.12.1: - resolution: {integrity: sha512-4EUH9tQHnMmEgzW/MdAP0KIfa1T9AF+htl0ffe2n5vb2EKn9y2co8ccpgWko6S52Jy1PQZKwRnx5/KkYjtd9MQ==} + resolution: {integrity: sha512-4EUH9tQHnMmEgzW/MdAP0KIfa1T9AF+htl0ffe2n5vb2EKn9y2co8ccpgWko6S52Jy1PQZKwRnx5/KkYjtd9MQ==, tarball: https://registry.npmjs.org/msgpackr/-/msgpackr-1.12.1.tgz} muggle-string@0.4.1: - resolution: {integrity: sha512-VNTrAak/KhO2i8dqqnqnAHOa3cYBwXEZe9h+D5h/1ZqFSTEFHdM65lR7RoIqq3tBBYavsOXV84NoHXZ0AkPyqQ==} + resolution: {integrity: sha512-VNTrAak/KhO2i8dqqnqnAHOa3cYBwXEZe9h+D5h/1ZqFSTEFHdM65lR7RoIqq3tBBYavsOXV84NoHXZ0AkPyqQ==, tarball: https://registry.npmjs.org/muggle-string/-/muggle-string-0.4.1.tgz} multicast-dns@7.2.5: - resolution: {integrity: sha512-2eznPJP8z2BFLX50tf0LuODrpINqP1RVIm/CObbTcBRITQgmC/TjcREF1NeTBzIcR5XO/ukWo+YHOjBbFwIupg==} + resolution: {integrity: sha512-2eznPJP8z2BFLX50tf0LuODrpINqP1RVIm/CObbTcBRITQgmC/TjcREF1NeTBzIcR5XO/ukWo+YHOjBbFwIupg==, tarball: https://registry.npmjs.org/multicast-dns/-/multicast-dns-7.2.5.tgz} hasBin: true mute-stream@3.0.0: - resolution: {integrity: sha512-dkEJPVvun4FryqBmZ5KhDo0K9iDXAwn08tMLDinNdRBNPcYEDiWYysLcc6k3mjTMlbP9KyylvRpd4wFtwrT9rw==} + resolution: {integrity: sha512-dkEJPVvun4FryqBmZ5KhDo0K9iDXAwn08tMLDinNdRBNPcYEDiWYysLcc6k3mjTMlbP9KyylvRpd4wFtwrT9rw==, tarball: https://registry.npmjs.org/mute-stream/-/mute-stream-3.0.0.tgz} engines: {node: ^20.17.0 || >=22.9.0} nanoid@3.3.16: - resolution: {integrity: sha512-bzlKTyNJ7+LdGIIwy8ijFpIqEQIvafahV7eYykJ8Cvh42EdJeODoJ6gUJXpQJvej1BddH8OqTXZNE/KfbWAu8Q==} + resolution: {integrity: sha512-bzlKTyNJ7+LdGIIwy8ijFpIqEQIvafahV7eYykJ8Cvh42EdJeODoJ6gUJXpQJvej1BddH8OqTXZNE/KfbWAu8Q==, tarball: https://registry.npmjs.org/nanoid/-/nanoid-3.3.16.tgz} engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} hasBin: true napi-postinstall@0.3.4: - resolution: {integrity: sha512-PHI5f1O0EP5xJ9gQmFGMS6IZcrVvTjpXjz7Na41gTE7eE2hK11lg04CECCYEEjdc17EV4DO+fkGEtt7TpTaTiQ==} + resolution: {integrity: sha512-PHI5f1O0EP5xJ9gQmFGMS6IZcrVvTjpXjz7Na41gTE7eE2hK11lg04CECCYEEjdc17EV4DO+fkGEtt7TpTaTiQ==, tarball: https://registry.npmjs.org/napi-postinstall/-/napi-postinstall-0.3.4.tgz} engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0} hasBin: true natural-compare@1.4.0: - resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} + resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==, tarball: https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz} needle@3.5.0: - resolution: {integrity: sha512-jaQyPKKk2YokHrEg+vFDYxXIHTCBgiZwSHOoVx/8V3GIBS8/VN6NdVRmg8q1ERtPkMvmOvebsgga4sAj5hls/w==} + resolution: {integrity: sha512-jaQyPKKk2YokHrEg+vFDYxXIHTCBgiZwSHOoVx/8V3GIBS8/VN6NdVRmg8q1ERtPkMvmOvebsgga4sAj5hls/w==, tarball: https://registry.npmjs.org/needle/-/needle-3.5.0.tgz} engines: {node: '>= 4.4.x'} hasBin: true negotiator@0.6.3: - resolution: {integrity: sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==} + resolution: {integrity: sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==, tarball: https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz} engines: {node: '>= 0.6'} negotiator@0.6.4: - resolution: {integrity: sha512-myRT3DiWPHqho5PrJaIRyaMv2kgYf0mUVgBNOYMuCH5Ki1yEiQaf/ZJuQ62nvpc44wL5WDbTX7yGJi1Neevw8w==} + resolution: {integrity: sha512-myRT3DiWPHqho5PrJaIRyaMv2kgYf0mUVgBNOYMuCH5Ki1yEiQaf/ZJuQ62nvpc44wL5WDbTX7yGJi1Neevw8w==, tarball: https://registry.npmjs.org/negotiator/-/negotiator-0.6.4.tgz} engines: {node: '>= 0.6'} negotiator@1.0.0: - resolution: {integrity: sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==} + resolution: {integrity: sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==, tarball: https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz} engines: {node: '>= 0.6'} neo-async@2.6.2: - resolution: {integrity: sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==} + resolution: {integrity: sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==, tarball: https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz} next@14.0.4: - resolution: {integrity: sha512-qbwypnM7327SadwFtxXnQdGiKpkuhaRLE2uq62/nRul9cj9KhQ5LhHmlziTNqUidZotw/Q1I9OjirBROdUJNgA==} + resolution: {integrity: sha512-qbwypnM7327SadwFtxXnQdGiKpkuhaRLE2uq62/nRul9cj9KhQ5LhHmlziTNqUidZotw/Q1I9OjirBROdUJNgA==, tarball: https://registry.npmjs.org/next/-/next-14.0.4.tgz} engines: {node: '>=18.17.0'} deprecated: This version has a security vulnerability. Please upgrade to a patched version. See https://nextjs.org/blog/security-update-2025-12-11 for more details. hasBin: true @@ -11834,7 +11896,7 @@ packages: optional: true ng-mocks@14.15.3: - resolution: {integrity: sha512-J0UafeI2K5kc5oDhIinGu+VqT990b0sP2dlKV3JxXYTlaQxQ7Mm0LLrxGz10hsGGJVtn9JEKkhjnpb6oijLT3w==} + resolution: {integrity: sha512-J0UafeI2K5kc5oDhIinGu+VqT990b0sP2dlKV3JxXYTlaQxQ7Mm0LLrxGz10hsGGJVtn9JEKkhjnpb6oijLT3w==, tarball: https://registry.npmjs.org/ng-mocks/-/ng-mocks-14.15.3.tgz} peerDependencies: '@angular/common': 5.0.0-alpha - 5 || 6.0.0-alpha - 6 || 7.0.0-alpha - 7 || 8.0.0-alpha - 8 || 9.0.0-alpha - 9 || 10.0.0-alpha - 10 || 11.0.0-alpha - 11 || 12.0.0-alpha - 12 || 13.0.0-alpha - 13 || 14.0.0-alpha - 14 || 15.0.0-alpha - 15 || 16.0.0-alpha - 16 || 17.0.0-alpha - 17 || 18.0.0-alpha - 18 || 19.0.0-alpha - 19 || 20.0.0-alpha - 20 || 21.0.0-alpha - 21 || 22.0.0-alpha - 22 '@angular/core': 5.0.0-alpha - 5 || 6.0.0-alpha - 6 || 7.0.0-alpha - 7 || 8.0.0-alpha - 8 || 9.0.0-alpha - 9 || 10.0.0-alpha - 10 || 11.0.0-alpha - 11 || 12.0.0-alpha - 12 || 13.0.0-alpha - 13 || 14.0.0-alpha - 14 || 15.0.0-alpha - 15 || 16.0.0-alpha - 16 || 17.0.0-alpha - 17 || 18.0.0-alpha - 18 || 19.0.0-alpha - 19 || 20.0.0-alpha - 20 || 21.0.0-alpha - 21 || 22.0.0-alpha - 22 @@ -11842,7 +11904,7 @@ packages: '@angular/platform-browser': 5.0.0-alpha - 5 || 6.0.0-alpha - 6 || 7.0.0-alpha - 7 || 8.0.0-alpha - 8 || 9.0.0-alpha - 9 || 10.0.0-alpha - 10 || 11.0.0-alpha - 11 || 12.0.0-alpha - 12 || 13.0.0-alpha - 13 || 14.0.0-alpha - 14 || 15.0.0-alpha - 15 || 16.0.0-alpha - 16 || 17.0.0-alpha - 17 || 18.0.0-alpha - 18 || 19.0.0-alpha - 19 || 20.0.0-alpha - 20 || 21.0.0-alpha - 21 || 22.0.0-alpha - 22 ng-packagr@22.1.0: - resolution: {integrity: sha512-Vd4M/N0dDMYk3QTcKx8DAEiE7qOmM7WE04GICW7Kcr25XQYq0rQJGVSCsgpI/jmPV5V/UOANWMJARUiMlktyYg==} + resolution: {integrity: sha512-Vd4M/N0dDMYk3QTcKx8DAEiE7qOmM7WE04GICW7Kcr25XQYq0rQJGVSCsgpI/jmPV5V/UOANWMJARUiMlktyYg==, tarball: https://registry.npmjs.org/ng-packagr/-/ng-packagr-22.1.0.tgz} engines: {node: ^22.22.3 || ^24.15.0 || >=26.0.0} hasBin: true peerDependencies: @@ -11855,7 +11917,7 @@ packages: optional: true ng2-dragula@5.0.1: - resolution: {integrity: sha512-dg9h8d04SRvvaGOsZ2n2fXidfzfOr9jzwLvwVNSaVdMHjryjDpK6I/S7kf3R8/oqcppu3GR2kIHQvysZKaQM4A==} + resolution: {integrity: sha512-dg9h8d04SRvvaGOsZ2n2fXidfzfOr9jzwLvwVNSaVdMHjryjDpK6I/S7kf3R8/oqcppu3GR2kIHQvysZKaQM4A==, tarball: https://registry.npmjs.org/ng2-dragula/-/ng2-dragula-5.0.1.tgz} peerDependencies: '@angular/animations': ^16.0.0 '@angular/common': ^16.0.0 @@ -11865,7 +11927,7 @@ packages: rxjs: '>=6.0.0' ngx-markdown@20.1.0: - resolution: {integrity: sha512-BLn6CTMO27cU0zeaJYoC1g5c1hAkrpE5oqVSQFGW0J5gq+gEuvTt4vrtNLc8Z+HYXtuuWmuhUWiXL/bYoiDJ+A==} + resolution: {integrity: sha512-BLn6CTMO27cU0zeaJYoC1g5c1hAkrpE5oqVSQFGW0J5gq+gEuvTt4vrtNLc8Z+HYXtuuWmuhUWiXL/bYoiDJ+A==, tarball: https://registry.npmjs.org/ngx-markdown/-/ngx-markdown-20.1.0.tgz} peerDependencies: '@angular/common': ^20.0.0 '@angular/core': ^20.0.0 @@ -11875,7 +11937,7 @@ packages: zone.js: ~0.15.0 ngx-tiptap@14.0.1: - resolution: {integrity: sha512-LOd8y+8H09Oi6jAE/UWy2sg68Mk5ZGTnPhnq7qeiDhU/QSZWD+SJ5vWkF4sguOCsHtPfde010xBf/zhy/b1P5A==} + resolution: {integrity: sha512-LOd8y+8H09Oi6jAE/UWy2sg68Mk5ZGTnPhnq7qeiDhU/QSZWD+SJ5vWkF4sguOCsHtPfde010xBf/zhy/b1P5A==, tarball: https://registry.npmjs.org/ngx-tiptap/-/ngx-tiptap-14.0.1.tgz} peerDependencies: '@angular/common': '>=20.0.0' '@angular/core': '>=20.0.0' @@ -11887,27 +11949,27 @@ packages: '@tiptap/pm': ^3.0.1 no-case@3.0.4: - resolution: {integrity: sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==} + resolution: {integrity: sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==, tarball: https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz} node-abort-controller@3.1.1: - resolution: {integrity: sha512-AGK2yQKIjRuqnc6VkX2Xj5d+QW8xZ87pa1UK6yA6ouUyuxfHuMP6umE5QK7UmTeOAymo+Zx1Fxiuw9rVx8taHQ==} + resolution: {integrity: sha512-AGK2yQKIjRuqnc6VkX2Xj5d+QW8xZ87pa1UK6yA6ouUyuxfHuMP6umE5QK7UmTeOAymo+Zx1Fxiuw9rVx8taHQ==, tarball: https://registry.npmjs.org/node-abort-controller/-/node-abort-controller-3.1.1.tgz} node-addon-api@6.1.0: - resolution: {integrity: sha512-+eawOlIgy680F0kBzPUNFhMZGtJ1YmqM6l4+Crf4IkImjYrO/mqPwRMh352g23uIaQKFItcQ64I7KMaJxHgAVA==} + resolution: {integrity: sha512-+eawOlIgy680F0kBzPUNFhMZGtJ1YmqM6l4+Crf4IkImjYrO/mqPwRMh352g23uIaQKFItcQ64I7KMaJxHgAVA==, tarball: https://registry.npmjs.org/node-addon-api/-/node-addon-api-6.1.0.tgz} node-addon-api@7.1.1: - resolution: {integrity: sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==} + resolution: {integrity: sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==, tarball: https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.1.tgz} node-exports-info@1.6.2: - resolution: {integrity: sha512-kXs9Go0cah0qHVV2v389IXQLdLCeE1xfFtjOAF+iobu0OIoG1pje8At2vMHyaPMiPMnG/LWP50twML21eMcAag==} + resolution: {integrity: sha512-kXs9Go0cah0qHVV2v389IXQLdLCeE1xfFtjOAF+iobu0OIoG1pje8At2vMHyaPMiPMnG/LWP50twML21eMcAag==, tarball: https://registry.npmjs.org/node-exports-info/-/node-exports-info-1.6.2.tgz} engines: {node: '>= 0.4'} node-fetch@2.6.1: - resolution: {integrity: sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==} + resolution: {integrity: sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==, tarball: https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz} engines: {node: 4.x || >=6.0.0} node-fetch@2.7.0: - resolution: {integrity: sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==} + resolution: {integrity: sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==, tarball: https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz} engines: {node: 4.x || >=6.0.0} peerDependencies: encoding: ^0.1.0 @@ -11916,68 +11978,72 @@ packages: optional: true node-forge@1.4.0: - resolution: {integrity: sha512-LarFH0+6VfriEhqMMcLX2F7SwSXeWwnEAJEsYm5QKWchiVYVvJyV9v7UDvUv+w5HO23ZpQTXDv/GxdDdMyOuoQ==} + resolution: {integrity: sha512-LarFH0+6VfriEhqMMcLX2F7SwSXeWwnEAJEsYm5QKWchiVYVvJyV9v7UDvUv+w5HO23ZpQTXDv/GxdDdMyOuoQ==, tarball: https://registry.npmjs.org/node-forge/-/node-forge-1.4.0.tgz} engines: {node: '>= 6.13.0'} node-gyp-build-optional-packages@5.2.2: - resolution: {integrity: sha512-s+w+rBWnpTMwSFbaE0UXsRlg7hU4FjekKU4eyAih5T8nJuNZT1nNsskXpxmeqSK9UzkBl6UgRlnKc8hz8IEqOw==} + resolution: {integrity: sha512-s+w+rBWnpTMwSFbaE0UXsRlg7hU4FjekKU4eyAih5T8nJuNZT1nNsskXpxmeqSK9UzkBl6UgRlnKc8hz8IEqOw==, tarball: https://registry.npmjs.org/node-gyp-build-optional-packages/-/node-gyp-build-optional-packages-5.2.2.tgz} hasBin: true node-int64@0.4.0: - resolution: {integrity: sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==} + resolution: {integrity: sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==, tarball: https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz} node-machine-id@1.1.12: - resolution: {integrity: sha512-QNABxbrPa3qEIfrE6GOJ7BYIuignnJw7iQ2YPbc3Nla1HzRJjXzZOiikfF8m7eAMfichLt3M4VgLOetqgDmgGQ==} + resolution: {integrity: sha512-QNABxbrPa3qEIfrE6GOJ7BYIuignnJw7iQ2YPbc3Nla1HzRJjXzZOiikfF8m7eAMfichLt3M4VgLOetqgDmgGQ==, tarball: https://registry.npmjs.org/node-machine-id/-/node-machine-id-1.1.12.tgz} node-releases@2.0.51: - resolution: {integrity: sha512-wRNIrw4DmVLKQlbgOMdkMx27Wrpzes2hh5Jtbi2bjPd+4wJstWIqP5A+lscnqbm0xxmT5Bpg8Lec5ItEBwx6BQ==} + resolution: {integrity: sha512-wRNIrw4DmVLKQlbgOMdkMx27Wrpzes2hh5Jtbi2bjPd+4wJstWIqP5A+lscnqbm0xxmT5Bpg8Lec5ItEBwx6BQ==, tarball: https://registry.npmjs.org/node-releases/-/node-releases-2.0.51.tgz} engines: {node: '>=18'} node-schedule@2.1.1: - resolution: {integrity: sha512-OXdegQq03OmXEjt2hZP33W2YPs/E5BcFQks46+G2gAxs4gHOIVD1u7EqlYLYSKsaIpyKCK9Gbk0ta1/gjRSMRQ==} + resolution: {integrity: sha512-OXdegQq03OmXEjt2hZP33W2YPs/E5BcFQks46+G2gAxs4gHOIVD1u7EqlYLYSKsaIpyKCK9Gbk0ta1/gjRSMRQ==, tarball: https://registry.npmjs.org/node-schedule/-/node-schedule-2.1.1.tgz} engines: {node: '>=6'} nopt@7.2.1: - resolution: {integrity: sha512-taM24ViiimT/XntxbPyJQzCG+p4EKOpgD3mxFwW38mGjVUrfERQOeY4EDHjdnptttfHuHQXFx+lTP08Q+mLa/w==} + resolution: {integrity: sha512-taM24ViiimT/XntxbPyJQzCG+p4EKOpgD3mxFwW38mGjVUrfERQOeY4EDHjdnptttfHuHQXFx+lTP08Q+mLa/w==, tarball: https://registry.npmjs.org/nopt/-/nopt-7.2.1.tgz} engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} hasBin: true normalize-path@3.0.0: - resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} + resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==, tarball: https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz} engines: {node: '>=0.10.0'} normalize-url@6.1.0: - resolution: {integrity: sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==} + resolution: {integrity: sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==, tarball: https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz} engines: {node: '>=10'} npm-package-arg@11.0.1: - resolution: {integrity: sha512-M7s1BD4NxdAvBKUPqqRW957Xwcl/4Zvo8Aj+ANrzvIPzGJZElrH7Z//rSaec2ORcND6FHHLnZeY8qgTpXDMFQQ==} + resolution: {integrity: sha512-M7s1BD4NxdAvBKUPqqRW957Xwcl/4Zvo8Aj+ANrzvIPzGJZElrH7Z//rSaec2ORcND6FHHLnZeY8qgTpXDMFQQ==, tarball: https://registry.npmjs.org/npm-package-arg/-/npm-package-arg-11.0.1.tgz} engines: {node: ^16.14.0 || >=18.0.0} npm-package-arg@14.0.0: - resolution: {integrity: sha512-69XQh3k+dtGa1p+7RaR57IuG3rCko96xr/nUfN4yDYBXbTYICiWcOpsFKLN2GtGE9cyIljE+f1exnaYt9MvM+Q==} + resolution: {integrity: sha512-69XQh3k+dtGa1p+7RaR57IuG3rCko96xr/nUfN4yDYBXbTYICiWcOpsFKLN2GtGE9cyIljE+f1exnaYt9MvM+Q==, tarball: https://registry.npmjs.org/npm-package-arg/-/npm-package-arg-14.0.0.tgz} engines: {node: ^22.22.2 || ^24.15.0 || >=26.0.0} npm-run-path@4.0.1: - resolution: {integrity: sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==} + resolution: {integrity: sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==, tarball: https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz} engines: {node: '>=8'} npm-run-path@5.3.0: - resolution: {integrity: sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==} + resolution: {integrity: sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==, tarball: https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.3.0.tgz} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} npm-run-path@6.0.0: - resolution: {integrity: sha512-9qny7Z9DsQU8Ou39ERsPU4OZQlSTP47ShQzuKZ6PRXpYLtIFgl/DEBYEXKlvcEa+9tHVcK8CF81Y2V72qaZhWA==} + resolution: {integrity: sha512-9qny7Z9DsQU8Ou39ERsPU4OZQlSTP47ShQzuKZ6PRXpYLtIFgl/DEBYEXKlvcEa+9tHVcK8CF81Y2V72qaZhWA==, tarball: https://registry.npmjs.org/npm-run-path/-/npm-run-path-6.0.0.tgz} engines: {node: '>=18'} nth-check@2.1.1: - resolution: {integrity: sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==} + resolution: {integrity: sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==, tarball: https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz} + + nth-check@3.0.1: + resolution: {integrity: sha512-GX0gsdbGVCgnRgbeGaubfjpBXyYRWOOCVeYh08bSQvDZqxz5ndXs1OTfAt/h36G1xvI94YIspsI0sVFqAV9+RQ==, tarball: https://registry.npmjs.org/nth-check/-/nth-check-3.0.1.tgz} + engines: {node: '>=20.19.0'} nwsapi@2.2.24: - resolution: {integrity: sha512-7YRhZ3jS45LwmSCT4b2sVFHt/WuovaktDU07QrtOBY2PXskss5a9jfmR9jptyumwXST+rFjrmppMY1KT/yn35A==} + resolution: {integrity: sha512-7YRhZ3jS45LwmSCT4b2sVFHt/WuovaktDU07QrtOBY2PXskss5a9jfmR9jptyumwXST+rFjrmppMY1KT/yn35A==, tarball: https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.24.tgz} nx@21.6.11: - resolution: {integrity: sha512-AAgJGhS+7xlsmZF6ArKX1vgONxf7IymUYZ1BxGXHVa5927rGfgKoMaPOgwwtvN0OL3o/QYaNGwlDfIzCvlpOLQ==} + resolution: {integrity: sha512-AAgJGhS+7xlsmZF6ArKX1vgONxf7IymUYZ1BxGXHVa5927rGfgKoMaPOgwwtvN0OL3o/QYaNGwlDfIzCvlpOLQ==, tarball: https://registry.npmjs.org/nx/-/nx-21.6.11.tgz} hasBin: true peerDependencies: '@swc-node/register': ^1.8.0 @@ -11989,7 +12055,7 @@ packages: optional: true nx@23.1.1: - resolution: {integrity: sha512-oDdW2JgVllgfyyN6OqlRzeABw0QrlXdxyl9rtOUMMXQzlkpYA1RTs8jinJCe6QSo7aEn0dZ+Ar7dd09hMudBsg==} + resolution: {integrity: sha512-oDdW2JgVllgfyyN6OqlRzeABw0QrlXdxyl9rtOUMMXQzlkpYA1RTs8jinJCe6QSo7aEn0dZ+Ar7dd09hMudBsg==, tarball: https://registry.npmjs.org/nx/-/nx-23.1.1.tgz} hasBin: true peerDependencies: '@swc-node/register': ^1.11.1 @@ -12001,507 +12067,504 @@ packages: optional: true object-assign@4.1.1: - resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} + resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==, tarball: https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz} engines: {node: '>=0.10.0'} object-inspect@1.13.4: - resolution: {integrity: sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==} + resolution: {integrity: sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==, tarball: https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz} engines: {node: '>= 0.4'} object-keys@1.1.1: - resolution: {integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==} + resolution: {integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==, tarball: https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz} engines: {node: '>= 0.4'} object.assign@4.1.7: - resolution: {integrity: sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==} + resolution: {integrity: sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==, tarball: https://registry.npmjs.org/object.assign/-/object.assign-4.1.7.tgz} engines: {node: '>= 0.4'} object.entries@1.1.9: - resolution: {integrity: sha512-8u/hfXFRBD1O0hPUjioLhoWFHRmt6tKA4/vZPyckBr18l1KE9uHrFaFaUi8MDRTpi4uak2goyPTSNJLXX2k2Hw==} + resolution: {integrity: sha512-8u/hfXFRBD1O0hPUjioLhoWFHRmt6tKA4/vZPyckBr18l1KE9uHrFaFaUi8MDRTpi4uak2goyPTSNJLXX2k2Hw==, tarball: https://registry.npmjs.org/object.entries/-/object.entries-1.1.9.tgz} engines: {node: '>= 0.4'} object.fromentries@2.0.8: - resolution: {integrity: sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==} + resolution: {integrity: sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==, tarball: https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.8.tgz} engines: {node: '>= 0.4'} object.groupby@1.0.3: - resolution: {integrity: sha512-+Lhy3TQTuzXI5hevh8sBGqbmurHbbIjAi0Z4S63nthVLmLxfbj4T54a4CfZrXIrt9iP4mVAPYMo/v99taj3wjQ==} + resolution: {integrity: sha512-+Lhy3TQTuzXI5hevh8sBGqbmurHbbIjAi0Z4S63nthVLmLxfbj4T54a4CfZrXIrt9iP4mVAPYMo/v99taj3wjQ==, tarball: https://registry.npmjs.org/object.groupby/-/object.groupby-1.0.3.tgz} engines: {node: '>= 0.4'} object.values@1.2.1: - resolution: {integrity: sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA==} + resolution: {integrity: sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA==, tarball: https://registry.npmjs.org/object.values/-/object.values-1.2.1.tgz} engines: {node: '>= 0.4'} obuf@1.1.2: - resolution: {integrity: sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==} + resolution: {integrity: sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==, tarball: https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz} obug@2.1.3: - resolution: {integrity: sha512-9miFgM2OFba7hB+pRgvtV84pYTBaoTHohvmIgiRt6dRIzbwEOIaNaP+dIlGs2fNFoB0SeISs0Jz5WFVRid6Xyg==} + resolution: {integrity: sha512-9miFgM2OFba7hB+pRgvtV84pYTBaoTHohvmIgiRt6dRIzbwEOIaNaP+dIlGs2fNFoB0SeISs0Jz5WFVRid6Xyg==, tarball: https://registry.npmjs.org/obug/-/obug-2.1.3.tgz} engines: {node: '>=12.20.0'} on-finished@2.4.1: - resolution: {integrity: sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==} + resolution: {integrity: sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==, tarball: https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz} engines: {node: '>= 0.8'} on-headers@1.1.0: - resolution: {integrity: sha512-737ZY3yNnXy37FHkQxPzt4UZ2UWPWiCZWLvFZ4fu5cueciegX0zGPnrlY6bwRg4FdQOe9YU8MkmJwGhoMybl8A==} + resolution: {integrity: sha512-737ZY3yNnXy37FHkQxPzt4UZ2UWPWiCZWLvFZ4fu5cueciegX0zGPnrlY6bwRg4FdQOe9YU8MkmJwGhoMybl8A==, tarball: https://registry.npmjs.org/on-headers/-/on-headers-1.1.0.tgz} engines: {node: '>= 0.8'} once@1.4.0: - resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} + resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==, tarball: https://registry.npmjs.org/once/-/once-1.4.0.tgz} onetime@5.1.2: - resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==} + resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==, tarball: https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz} engines: {node: '>=6'} onetime@6.0.0: - resolution: {integrity: sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==} + resolution: {integrity: sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==, tarball: https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz} engines: {node: '>=12'} onetime@7.0.0: - resolution: {integrity: sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==} + resolution: {integrity: sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==, tarball: https://registry.npmjs.org/onetime/-/onetime-7.0.0.tgz} engines: {node: '>=18'} open@10.1.0: - resolution: {integrity: sha512-mnkeQ1qP5Ue2wd+aivTD3NHd/lZ96Lu0jgf0pwktLPtx6cTZiH7tyeGRRHs0zX0rbrahXPnXlUnbeXyaBBuIaw==} + resolution: {integrity: sha512-mnkeQ1qP5Ue2wd+aivTD3NHd/lZ96Lu0jgf0pwktLPtx6cTZiH7tyeGRRHs0zX0rbrahXPnXlUnbeXyaBBuIaw==, tarball: https://registry.npmjs.org/open/-/open-10.1.0.tgz} engines: {node: '>=18'} open@10.2.0: - resolution: {integrity: sha512-YgBpdJHPyQ2UE5x+hlSXcnejzAvD0b22U2OuAP+8OnlJT+PjWPxtgmGqKKc+RgTM63U9gN0YzrYc71R2WT/hTA==} + resolution: {integrity: sha512-YgBpdJHPyQ2UE5x+hlSXcnejzAvD0b22U2OuAP+8OnlJT+PjWPxtgmGqKKc+RgTM63U9gN0YzrYc71R2WT/hTA==, tarball: https://registry.npmjs.org/open/-/open-10.2.0.tgz} engines: {node: '>=18'} open@11.0.0: - resolution: {integrity: sha512-smsWv2LzFjP03xmvFoJ331ss6h+jixfA4UUV/Bsiyuu4YJPfN+FIQGOIiv4w9/+MoHkfkJ22UIaQWRVFRfH6Vw==} + resolution: {integrity: sha512-smsWv2LzFjP03xmvFoJ331ss6h+jixfA4UUV/Bsiyuu4YJPfN+FIQGOIiv4w9/+MoHkfkJ22UIaQWRVFRfH6Vw==, tarball: https://registry.npmjs.org/open/-/open-11.0.0.tgz} engines: {node: '>=20'} open@8.4.2: - resolution: {integrity: sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ==} + resolution: {integrity: sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ==, tarball: https://registry.npmjs.org/open/-/open-8.4.2.tgz} engines: {node: '>=12'} - openapi-types@12.1.3: - resolution: {integrity: sha512-N4YtSYJqghVu4iek2ZUvcN/0aqH1kRDuNqzcycDxhOUpg7GdvLa2F3DgS6yBNhInhv2r/6I0Flkn7CqL8+nIcw==} - opener@1.5.2: - resolution: {integrity: sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A==} + resolution: {integrity: sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A==, tarball: https://registry.npmjs.org/opener/-/opener-1.5.2.tgz} hasBin: true optionator@0.9.4: - resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} + resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==, tarball: https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz} engines: {node: '>= 0.8.0'} ora@5.3.0: - resolution: {integrity: sha512-zAKMgGXUim0Jyd6CXK9lraBnD3H5yPGBPPOkC23a2BG6hsm4Zu6OQSjQuEtV0BHDf4aKHcUFvJiGRrFuW3MG8g==} + resolution: {integrity: sha512-zAKMgGXUim0Jyd6CXK9lraBnD3H5yPGBPPOkC23a2BG6hsm4Zu6OQSjQuEtV0BHDf4aKHcUFvJiGRrFuW3MG8g==, tarball: https://registry.npmjs.org/ora/-/ora-5.3.0.tgz} engines: {node: '>=10'} ora@5.4.1: - resolution: {integrity: sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==} + resolution: {integrity: sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==, tarball: https://registry.npmjs.org/ora/-/ora-5.4.1.tgz} engines: {node: '>=10'} ora@9.0.0: - resolution: {integrity: sha512-m0pg2zscbYgWbqRR6ABga5c3sZdEon7bSgjnlXC64kxtxLOyjRcbbUkLj7HFyy/FTD+P2xdBWu8snGhYI0jc4A==} + resolution: {integrity: sha512-m0pg2zscbYgWbqRR6ABga5c3sZdEon7bSgjnlXC64kxtxLOyjRcbbUkLj7HFyy/FTD+P2xdBWu8snGhYI0jc4A==, tarball: https://registry.npmjs.org/ora/-/ora-9.0.0.tgz} engines: {node: '>=20'} ora@9.4.0: - resolution: {integrity: sha512-84cglkRILFxdtA8hAvLNdMrtBpPNBTrQ9/ulg0FA7xLMnD6mifv+enAIeRmvtv+WgdCE+LPGOfQmtJRrVaIVhQ==} + resolution: {integrity: sha512-84cglkRILFxdtA8hAvLNdMrtBpPNBTrQ9/ulg0FA7xLMnD6mifv+enAIeRmvtv+WgdCE+LPGOfQmtJRrVaIVhQ==, tarball: https://registry.npmjs.org/ora/-/ora-9.4.0.tgz} engines: {node: '>=20'} ora@9.4.1: - resolution: {integrity: sha512-6VlU9MLXbjVQD04AZCMX28hVtA5bUoadvUqO76MUCVA0ilwJbMiHsITRPfyVm6p/BC0Av/BXMujx39WCe1LEqw==} + resolution: {integrity: sha512-6VlU9MLXbjVQD04AZCMX28hVtA5bUoadvUqO76MUCVA0ilwJbMiHsITRPfyVm6p/BC0Av/BXMujx39WCe1LEqw==, tarball: https://registry.npmjs.org/ora/-/ora-9.4.1.tgz} engines: {node: '>=20'} ordered-binary@1.6.1: - resolution: {integrity: sha512-QkCdPooczexPLiXIrbVOPYkR3VO3T6v2OyKRkR1Xbhpy7/LAVXwahnRCgRp78Oe/Ehf0C/HATAxfSr6eA1oX+w==} + resolution: {integrity: sha512-QkCdPooczexPLiXIrbVOPYkR3VO3T6v2OyKRkR1Xbhpy7/LAVXwahnRCgRp78Oe/Ehf0C/HATAxfSr6eA1oX+w==, tarball: https://registry.npmjs.org/ordered-binary/-/ordered-binary-1.6.1.tgz} orderedmap@2.1.1: - resolution: {integrity: sha512-TvAWxi0nDe1j/rtMcWcIj94+Ffe6n7zhow33h40SKxmsmozs6dz/e+EajymfoFcHd7sxNn8yHM8839uixMOV6g==} + resolution: {integrity: sha512-TvAWxi0nDe1j/rtMcWcIj94+Ffe6n7zhow33h40SKxmsmozs6dz/e+EajymfoFcHd7sxNn8yHM8839uixMOV6g==, tarball: https://registry.npmjs.org/orderedmap/-/orderedmap-2.1.1.tgz} own-keys@1.0.1: - resolution: {integrity: sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==} + resolution: {integrity: sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==, tarball: https://registry.npmjs.org/own-keys/-/own-keys-1.0.1.tgz} engines: {node: '>= 0.4'} oxc-parser@0.142.0: - resolution: {integrity: sha512-kKR+jPiRJYJDexVoziIg/FVGvr1fT1FZSSJOk6tVoMKKSlsf1Cso+cgGCJkOEDWOP174vRntCPFKg+AS7InWvw==} + resolution: {integrity: sha512-kKR+jPiRJYJDexVoziIg/FVGvr1fT1FZSSJOk6tVoMKKSlsf1Cso+cgGCJkOEDWOP174vRntCPFKg+AS7InWvw==, tarball: https://registry.npmjs.org/oxc-parser/-/oxc-parser-0.142.0.tgz} engines: {node: ^20.19.0 || >=22.12.0} oxc-resolver@11.24.2: - resolution: {integrity: sha512-FY91FiDBj7ls5MsFS9jN3tjz2o0/zsdSsymlakySaBwVJZorHhkWyICLZMKxlu1R9vYo+sd3z1jwb4J8x7bNDw==} + resolution: {integrity: sha512-FY91FiDBj7ls5MsFS9jN3tjz2o0/zsdSsymlakySaBwVJZorHhkWyICLZMKxlu1R9vYo+sd3z1jwb4J8x7bNDw==, tarball: https://registry.npmjs.org/oxc-resolver/-/oxc-resolver-11.24.2.tgz} p-finally@1.0.0: - resolution: {integrity: sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow==} + resolution: {integrity: sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow==, tarball: https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz} engines: {node: '>=4'} p-limit@2.3.0: - resolution: {integrity: sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==} + resolution: {integrity: sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==, tarball: https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz} engines: {node: '>=6'} p-limit@3.1.0: - resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} + resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==, tarball: https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz} engines: {node: '>=10'} p-limit@4.0.0: - resolution: {integrity: sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ==} + resolution: {integrity: sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ==, tarball: https://registry.npmjs.org/p-limit/-/p-limit-4.0.0.tgz} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} p-locate@4.1.0: - resolution: {integrity: sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==} + resolution: {integrity: sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==, tarball: https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz} engines: {node: '>=8'} p-locate@5.0.0: - resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} + resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==, tarball: https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz} engines: {node: '>=10'} p-locate@6.0.0: - resolution: {integrity: sha512-wPrq66Llhl7/4AGC6I+cqxT07LhXvWL08LNXz1fENOw0Ap4sRZZ/gZpTTJ5jpurzzzfS2W/Ge9BY3LgLjCShcw==} + resolution: {integrity: sha512-wPrq66Llhl7/4AGC6I+cqxT07LhXvWL08LNXz1fENOw0Ap4sRZZ/gZpTTJ5jpurzzzfS2W/Ge9BY3LgLjCShcw==, tarball: https://registry.npmjs.org/p-locate/-/p-locate-6.0.0.tgz} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} p-map@7.0.5: - resolution: {integrity: sha512-e8vJF4XdVkzqqSHguEMz41mQO1wKwxKm5ENrUJQUu9kLDCtn83cxbyHZcszr4QC5zEA7WffRRC4gsTecC7J9oA==} + resolution: {integrity: sha512-e8vJF4XdVkzqqSHguEMz41mQO1wKwxKm5ENrUJQUu9kLDCtn83cxbyHZcszr4QC5zEA7WffRRC4gsTecC7J9oA==, tarball: https://registry.npmjs.org/p-map/-/p-map-7.0.5.tgz} engines: {node: '>=18'} p-queue@6.6.2: - resolution: {integrity: sha512-RwFpb72c/BhQLEXIZ5K2e+AhgNVmIejGlTgiB9MzZ0e93GRvqZ7uSi0dvRF7/XIXDeNkra2fNHBxTyPDGySpjQ==} + resolution: {integrity: sha512-RwFpb72c/BhQLEXIZ5K2e+AhgNVmIejGlTgiB9MzZ0e93GRvqZ7uSi0dvRF7/XIXDeNkra2fNHBxTyPDGySpjQ==, tarball: https://registry.npmjs.org/p-queue/-/p-queue-6.6.2.tgz} engines: {node: '>=8'} p-retry@6.2.1: - resolution: {integrity: sha512-hEt02O4hUct5wtwg4H4KcWgDdm+l1bOaEy/hWzd8xtXB9BqxTWBBhb+2ImAtH4Cv4rPjV76xN3Zumqk3k3AhhQ==} + resolution: {integrity: sha512-hEt02O4hUct5wtwg4H4KcWgDdm+l1bOaEy/hWzd8xtXB9BqxTWBBhb+2ImAtH4Cv4rPjV76xN3Zumqk3k3AhhQ==, tarball: https://registry.npmjs.org/p-retry/-/p-retry-6.2.1.tgz} engines: {node: '>=16.17'} p-timeout@3.2.0: - resolution: {integrity: sha512-rhIwUycgwwKcP9yTOOFK/AKsAopjjCakVqLHePO3CC6Mir1Z99xT+R63jZxAT5lFZLa2inS5h+ZS2GvR99/FBg==} + resolution: {integrity: sha512-rhIwUycgwwKcP9yTOOFK/AKsAopjjCakVqLHePO3CC6Mir1Z99xT+R63jZxAT5lFZLa2inS5h+ZS2GvR99/FBg==, tarball: https://registry.npmjs.org/p-timeout/-/p-timeout-3.2.0.tgz} engines: {node: '>=8'} p-try@2.2.0: - resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==} + resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==, tarball: https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz} engines: {node: '>=6'} package-json-from-dist@1.0.1: - resolution: {integrity: sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==} + resolution: {integrity: sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==, tarball: https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz} package-manager-detector@1.7.0: - resolution: {integrity: sha512-xg1eHpwYL/D/HEdWw2goFZP6vV0FH7W+PZ5rFkGjdIDLtxq7EkzBUeT3m+lndYCt8wKbmofUu1MUdMCXkCk9ZQ==} + resolution: {integrity: sha512-xg1eHpwYL/D/HEdWw2goFZP6vV0FH7W+PZ5rFkGjdIDLtxq7EkzBUeT3m+lndYCt8wKbmofUu1MUdMCXkCk9ZQ==, tarball: https://registry.npmjs.org/package-manager-detector/-/package-manager-detector-1.7.0.tgz} parent-module@1.0.1: - resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} + resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==, tarball: https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz} engines: {node: '>=6'} parse-json@5.2.0: - resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==} + resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==, tarball: https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz} engines: {node: '>=8'} parse-ms@4.0.0: - resolution: {integrity: sha512-TXfryirbmq34y8QBwgqCVLi+8oA3oWx2eAnSn62ITyEhEYaWRlVZ2DvMM9eZbMs/RfxPu/PK/aBLyGj4IrqMHw==} + resolution: {integrity: sha512-TXfryirbmq34y8QBwgqCVLi+8oA3oWx2eAnSn62ITyEhEYaWRlVZ2DvMM9eZbMs/RfxPu/PK/aBLyGj4IrqMHw==, tarball: https://registry.npmjs.org/parse-ms/-/parse-ms-4.0.0.tgz} engines: {node: '>=18'} parse-node-version@1.0.1: - resolution: {integrity: sha512-3YHlOa/JgH6Mnpr05jP9eDG254US9ek25LyIxZlDItp2iJtwyaXQb57lBYLdT3MowkUFYEV2XXNAYIPlESvJlA==} + resolution: {integrity: sha512-3YHlOa/JgH6Mnpr05jP9eDG254US9ek25LyIxZlDItp2iJtwyaXQb57lBYLdT3MowkUFYEV2XXNAYIPlESvJlA==, tarball: https://registry.npmjs.org/parse-node-version/-/parse-node-version-1.0.1.tgz} engines: {node: '>= 0.10'} parse-passwd@1.0.0: - resolution: {integrity: sha512-1Y1A//QUXEZK7YKz+rD9WydcE1+EuPr6ZBgKecAB8tmoW6UFv0NREVJe1p+jRxtThkcbbKkfwIbWJe/IeE6m2Q==} + resolution: {integrity: sha512-1Y1A//QUXEZK7YKz+rD9WydcE1+EuPr6ZBgKecAB8tmoW6UFv0NREVJe1p+jRxtThkcbbKkfwIbWJe/IeE6m2Q==, tarball: https://registry.npmjs.org/parse-passwd/-/parse-passwd-1.0.0.tgz} engines: {node: '>=0.10.0'} parse5-html-rewriting-stream@8.0.1: - resolution: {integrity: sha512-NaRku2aMpUN1Sh1Gyk1KWUh2A7EJx2c6qYzvwsPtqhoHoaURshdrceYK3LunVCm3WHhm6FS7Vcczbvdh3/UIVw==} + resolution: {integrity: sha512-NaRku2aMpUN1Sh1Gyk1KWUh2A7EJx2c6qYzvwsPtqhoHoaURshdrceYK3LunVCm3WHhm6FS7Vcczbvdh3/UIVw==, tarball: https://registry.npmjs.org/parse5-html-rewriting-stream/-/parse5-html-rewriting-stream-8.0.1.tgz} parse5-sax-parser@8.0.0: - resolution: {integrity: sha512-/dQ8UzHZwnrzs3EvDj6IkKrD/jIZyTlB+8XrHJvcjNgRdmWruNdN9i9RK/JtxakmlUdPwKubKPTCqvbTgzGhrw==} + resolution: {integrity: sha512-/dQ8UzHZwnrzs3EvDj6IkKrD/jIZyTlB+8XrHJvcjNgRdmWruNdN9i9RK/JtxakmlUdPwKubKPTCqvbTgzGhrw==, tarball: https://registry.npmjs.org/parse5-sax-parser/-/parse5-sax-parser-8.0.0.tgz} parse5@4.0.0: - resolution: {integrity: sha512-VrZ7eOd3T1Fk4XWNXMgiGBK/z0MG48BWG2uQNU4I72fkQuKUTZpl+u9k+CxEG0twMVzSmXEEz12z5Fnw1jIQFA==} + resolution: {integrity: sha512-VrZ7eOd3T1Fk4XWNXMgiGBK/z0MG48BWG2uQNU4I72fkQuKUTZpl+u9k+CxEG0twMVzSmXEEz12z5Fnw1jIQFA==, tarball: https://registry.npmjs.org/parse5/-/parse5-4.0.0.tgz} parse5@7.3.0: - resolution: {integrity: sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==} + resolution: {integrity: sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==, tarball: https://registry.npmjs.org/parse5/-/parse5-7.3.0.tgz} parse5@8.0.1: - resolution: {integrity: sha512-z1e/HMG90obSGeidlli3hj7cbocou0/wa5HacvI3ASx34PecNjNQeaHNo5WIZpWofN9kgkqV1q5YvXe3F0FoPw==} + resolution: {integrity: sha512-z1e/HMG90obSGeidlli3hj7cbocou0/wa5HacvI3ASx34PecNjNQeaHNo5WIZpWofN9kgkqV1q5YvXe3F0FoPw==, tarball: https://registry.npmjs.org/parse5/-/parse5-8.0.1.tgz} parseurl@1.3.3: - resolution: {integrity: sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==} + resolution: {integrity: sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==, tarball: https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz} engines: {node: '>= 0.8'} path-browserify@1.0.1: - resolution: {integrity: sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==} + resolution: {integrity: sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==, tarball: https://registry.npmjs.org/path-browserify/-/path-browserify-1.0.1.tgz} path-data-parser@0.1.0: - resolution: {integrity: sha512-NOnmBpt5Y2RWbuv0LMzsayp3lVylAHLPUTut412ZA3l+C4uw4ZVkQbjShYCQ8TCpUMdPapr4YjUqLYD6v68j+w==} + resolution: {integrity: sha512-NOnmBpt5Y2RWbuv0LMzsayp3lVylAHLPUTut412ZA3l+C4uw4ZVkQbjShYCQ8TCpUMdPapr4YjUqLYD6v68j+w==, tarball: https://registry.npmjs.org/path-data-parser/-/path-data-parser-0.1.0.tgz} path-exists@4.0.0: - resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} + resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==, tarball: https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz} engines: {node: '>=8'} path-exists@5.0.0: - resolution: {integrity: sha512-RjhtfwJOxzcFmNOi6ltcbcu4Iu+FL3zEj83dk4kAS+fVpTxXLO1b38RvJgT/0QwvV/L3aY9TAnyv0EOqW4GoMQ==} + resolution: {integrity: sha512-RjhtfwJOxzcFmNOi6ltcbcu4Iu+FL3zEj83dk4kAS+fVpTxXLO1b38RvJgT/0QwvV/L3aY9TAnyv0EOqW4GoMQ==, tarball: https://registry.npmjs.org/path-exists/-/path-exists-5.0.0.tgz} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} path-is-absolute@1.0.1: - resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==} + resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==, tarball: https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz} engines: {node: '>=0.10.0'} path-key@3.1.1: - resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} + resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==, tarball: https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz} engines: {node: '>=8'} path-key@4.0.0: - resolution: {integrity: sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==} + resolution: {integrity: sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==, tarball: https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz} engines: {node: '>=12'} path-parse@1.0.7: - resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} + resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==, tarball: https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz} path-scurry@1.11.1: - resolution: {integrity: sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==} + resolution: {integrity: sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==, tarball: https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz} engines: {node: '>=16 || 14 >=14.18'} path-to-regexp@0.1.13: - resolution: {integrity: sha512-A/AGNMFN3c8bOlvV9RreMdrv7jsmF9XIfDeCd87+I8RNg6s78BhJxMu69NEMHBSJFxKidViTEdruRwEk/WIKqA==} + resolution: {integrity: sha512-A/AGNMFN3c8bOlvV9RreMdrv7jsmF9XIfDeCd87+I8RNg6s78BhJxMu69NEMHBSJFxKidViTEdruRwEk/WIKqA==, tarball: https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.13.tgz} path-to-regexp@2.4.0: - resolution: {integrity: sha512-G6zHoVqC6GGTQkZwF4lkuEyMbVOjoBKAEybQUypI1WTkqinCOrq2x6U2+phkJ1XsEMTy4LjtwPI7HW+NVrRR2w==} + resolution: {integrity: sha512-G6zHoVqC6GGTQkZwF4lkuEyMbVOjoBKAEybQUypI1WTkqinCOrq2x6U2+phkJ1XsEMTy4LjtwPI7HW+NVrRR2w==, tarball: https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-2.4.0.tgz} path-to-regexp@8.4.2: - resolution: {integrity: sha512-qRcuIdP69NPm4qbACK+aDogI5CBDMi1jKe0ry5rSQJz8JVLsC7jV8XpiJjGRLLol3N+R5ihGYcrPLTno6pAdBA==} + resolution: {integrity: sha512-qRcuIdP69NPm4qbACK+aDogI5CBDMi1jKe0ry5rSQJz8JVLsC7jV8XpiJjGRLLol3N+R5ihGYcrPLTno6pAdBA==, tarball: https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.4.2.tgz} path-type@4.0.0: - resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} + resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==, tarball: https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz} engines: {node: '>=8'} pathe@2.0.3: - resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==} + resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==, tarball: https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz} pathval@2.0.1: - resolution: {integrity: sha512-//nshmD55c46FuFw26xV/xFAaB5HF9Xdap7HJBBnrKdAd6/GxDBaNA1870O79+9ueg61cZLSVc+OaFlfmObYVQ==} + resolution: {integrity: sha512-//nshmD55c46FuFw26xV/xFAaB5HF9Xdap7HJBBnrKdAd6/GxDBaNA1870O79+9ueg61cZLSVc+OaFlfmObYVQ==, tarball: https://registry.npmjs.org/pathval/-/pathval-2.0.1.tgz} engines: {node: '>= 14.16'} picocolors@1.1.1: - resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} + resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==, tarball: https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz} picomatch@2.3.2: - resolution: {integrity: sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==} + resolution: {integrity: sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==, tarball: https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz} engines: {node: '>=8.6'} picomatch@4.0.2: - resolution: {integrity: sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==} + resolution: {integrity: sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==, tarball: https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz} engines: {node: '>=12'} picomatch@4.0.4: - resolution: {integrity: sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==} + resolution: {integrity: sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==, tarball: https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz} engines: {node: '>=12'} picomatch@4.0.5: - resolution: {integrity: sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A==} + resolution: {integrity: sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A==, tarball: https://registry.npmjs.org/picomatch/-/picomatch-4.0.5.tgz} engines: {node: '>=12'} pidtree@0.6.1: - resolution: {integrity: sha512-e0F9AOF1JMrCfBsyJOwU9lNvQ0WtXTq0j/4jk0BQ5JSI9VAybPXmDpPRw/2FQ3e5d3ZFN1mLh7jW99m/jjaptw==} + resolution: {integrity: sha512-e0F9AOF1JMrCfBsyJOwU9lNvQ0WtXTq0j/4jk0BQ5JSI9VAybPXmDpPRw/2FQ3e5d3ZFN1mLh7jW99m/jjaptw==, tarball: https://registry.npmjs.org/pidtree/-/pidtree-0.6.1.tgz} engines: {node: '>=0.10'} hasBin: true pify@2.3.0: - resolution: {integrity: sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==} + resolution: {integrity: sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==, tarball: https://registry.npmjs.org/pify/-/pify-2.3.0.tgz} engines: {node: '>=0.10.0'} pify@4.0.1: - resolution: {integrity: sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==} + resolution: {integrity: sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==, tarball: https://registry.npmjs.org/pify/-/pify-4.0.1.tgz} engines: {node: '>=6'} pify@5.0.0: - resolution: {integrity: sha512-eW/gHNMlxdSP6dmG6uJip6FXN0EQBwm2clYYd8Wul42Cwu/DK8HEftzsapcNdYe2MfLiIwZqsDk2RDEsTE79hA==} + resolution: {integrity: sha512-eW/gHNMlxdSP6dmG6uJip6FXN0EQBwm2clYYd8Wul42Cwu/DK8HEftzsapcNdYe2MfLiIwZqsDk2RDEsTE79hA==, tarball: https://registry.npmjs.org/pify/-/pify-5.0.0.tgz} engines: {node: '>=10'} pinkie-promise@2.0.1: - resolution: {integrity: sha512-0Gni6D4UcLTbv9c57DfxDGdr41XfgUjqWZu492f0cIGr16zDU06BWP/RAEvOuo7CQ0CNjHaLlM59YJJFm3NWlw==} + resolution: {integrity: sha512-0Gni6D4UcLTbv9c57DfxDGdr41XfgUjqWZu492f0cIGr16zDU06BWP/RAEvOuo7CQ0CNjHaLlM59YJJFm3NWlw==, tarball: https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz} engines: {node: '>=0.10.0'} pinkie@2.0.4: - resolution: {integrity: sha512-MnUuEycAemtSaeFSjXKW/aroV7akBbY+Sv+RkyqFjgAe73F+MR0TBWKBRDkmfWq/HiFmdavfZ1G7h4SPZXaCSg==} + resolution: {integrity: sha512-MnUuEycAemtSaeFSjXKW/aroV7akBbY+Sv+RkyqFjgAe73F+MR0TBWKBRDkmfWq/HiFmdavfZ1G7h4SPZXaCSg==, tarball: https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz} engines: {node: '>=0.10.0'} pirates@4.0.7: - resolution: {integrity: sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==} + resolution: {integrity: sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==, tarball: https://registry.npmjs.org/pirates/-/pirates-4.0.7.tgz} engines: {node: '>= 6'} piscina@5.2.0: - resolution: {integrity: sha512-DszUCKeVN/5G5QKo6jAVHL8fmKnkJvQ0ACiVgY7YGCq3TUB2oznAOayvZPIAdEThvhczkXR+qm3IHsNXpFCYfA==} + resolution: {integrity: sha512-DszUCKeVN/5G5QKo6jAVHL8fmKnkJvQ0ACiVgY7YGCq3TUB2oznAOayvZPIAdEThvhczkXR+qm3IHsNXpFCYfA==, tarball: https://registry.npmjs.org/piscina/-/piscina-5.2.0.tgz} engines: {node: '>=20.x'} pkce-challenge@5.0.1: - resolution: {integrity: sha512-wQ0b/W4Fr01qtpHlqSqspcj3EhBvimsdh0KlHhH8HRZnMsEa0ea2fTULOXOS9ccQr3om+GcGRk4e+isrZWV8qQ==} + resolution: {integrity: sha512-wQ0b/W4Fr01qtpHlqSqspcj3EhBvimsdh0KlHhH8HRZnMsEa0ea2fTULOXOS9ccQr3om+GcGRk4e+isrZWV8qQ==, tarball: https://registry.npmjs.org/pkce-challenge/-/pkce-challenge-5.0.1.tgz} engines: {node: '>=16.20.0'} pkg-dir@4.2.0: - resolution: {integrity: sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==} + resolution: {integrity: sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==, tarball: https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz} engines: {node: '>=8'} pkg-dir@7.0.0: - resolution: {integrity: sha512-Ie9z/WINcxxLp27BKOCHGde4ITq9UklYKDzVo1nhk5sqGEXU3FpkwP5GM2voTGJkGd9B3Otl+Q4uwSOeSUtOBA==} + resolution: {integrity: sha512-Ie9z/WINcxxLp27BKOCHGde4ITq9UklYKDzVo1nhk5sqGEXU3FpkwP5GM2voTGJkGd9B3Otl+Q4uwSOeSUtOBA==, tarball: https://registry.npmjs.org/pkg-dir/-/pkg-dir-7.0.0.tgz} engines: {node: '>=14.16'} pkg-dir@8.0.0: - resolution: {integrity: sha512-4peoBq4Wks0riS0z8741NVv+/8IiTvqnZAr8QGgtdifrtpdXbNw/FxRS1l6NFqm4EMzuS0EDqNNx4XGaz8cuyQ==} + resolution: {integrity: sha512-4peoBq4Wks0riS0z8741NVv+/8IiTvqnZAr8QGgtdifrtpdXbNw/FxRS1l6NFqm4EMzuS0EDqNNx4XGaz8cuyQ==, tarball: https://registry.npmjs.org/pkg-dir/-/pkg-dir-8.0.0.tgz} engines: {node: '>=18'} pkg-types@1.3.1: - resolution: {integrity: sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==} + resolution: {integrity: sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==, tarball: https://registry.npmjs.org/pkg-types/-/pkg-types-1.3.1.tgz} pkg-types@2.3.1: - resolution: {integrity: sha512-y+ichcgc2LrADuhLNAx8DFjVfgz91pRxfZdI3UDhxHvcVEZsenLO+7XaU5vOp0u/7V/wZ+plyuQxtrDlZJ+yeg==} + resolution: {integrity: sha512-y+ichcgc2LrADuhLNAx8DFjVfgz91pRxfZdI3UDhxHvcVEZsenLO+7XaU5vOp0u/7V/wZ+plyuQxtrDlZJ+yeg==, tarball: https://registry.npmjs.org/pkg-types/-/pkg-types-2.3.1.tgz} pkijs@3.4.0: - resolution: {integrity: sha512-emEcLuomt2j03vxD54giVB4SxTjnsqkU692xZOZXHDVoYyypEm+b3jpiTcc+Cf+myooc+/Ly0z01jqeNHVgJGw==} + resolution: {integrity: sha512-emEcLuomt2j03vxD54giVB4SxTjnsqkU692xZOZXHDVoYyypEm+b3jpiTcc+Cf+myooc+/Ly0z01jqeNHVgJGw==, tarball: https://registry.npmjs.org/pkijs/-/pkijs-3.4.0.tgz} engines: {node: '>=16.0.0'} playwright-core@1.36.0: - resolution: {integrity: sha512-7RTr8P6YJPAqB+8j5ATGHqD6LvLLM39sYVNsslh78g8QeLcBs5750c6+msjrHUwwGt+kEbczBj1XB22WMwn+WA==} + resolution: {integrity: sha512-7RTr8P6YJPAqB+8j5ATGHqD6LvLLM39sYVNsslh78g8QeLcBs5750c6+msjrHUwwGt+kEbczBj1XB22WMwn+WA==, tarball: https://registry.npmjs.org/playwright-core/-/playwright-core-1.36.0.tgz} engines: {node: '>=16'} hasBin: true points-on-curve@0.2.0: - resolution: {integrity: sha512-0mYKnYYe9ZcqMCWhUjItv/oHjvgEsfKvnUTg8sAtnHr3GVy7rGkXCb6d5cSyqrWqL4k81b9CPg3urd+T7aop3A==} + resolution: {integrity: sha512-0mYKnYYe9ZcqMCWhUjItv/oHjvgEsfKvnUTg8sAtnHr3GVy7rGkXCb6d5cSyqrWqL4k81b9CPg3urd+T7aop3A==, tarball: https://registry.npmjs.org/points-on-curve/-/points-on-curve-0.2.0.tgz} points-on-path@0.2.1: - resolution: {integrity: sha512-25ClnWWuw7JbWZcgqY/gJ4FQWadKxGWk+3kR/7kD0tCaDtPPMj7oHu2ToLaVhfpnHrZzYby2w6tUA0eOIuUg8g==} + resolution: {integrity: sha512-25ClnWWuw7JbWZcgqY/gJ4FQWadKxGWk+3kR/7kD0tCaDtPPMj7oHu2ToLaVhfpnHrZzYby2w6tUA0eOIuUg8g==, tarball: https://registry.npmjs.org/points-on-path/-/points-on-path-0.2.1.tgz} portfinder@1.0.38: - resolution: {integrity: sha512-rEwq/ZHlJIKw++XtLAO8PPuOQA/zaPJOZJ37BVuN97nLpMJeuDVLVGRwbFoBgLudgdTMP2hdRJP++H+8QOA3vg==} + resolution: {integrity: sha512-rEwq/ZHlJIKw++XtLAO8PPuOQA/zaPJOZJ37BVuN97nLpMJeuDVLVGRwbFoBgLudgdTMP2hdRJP++H+8QOA3vg==, tarball: https://registry.npmjs.org/portfinder/-/portfinder-1.0.38.tgz} engines: {node: '>= 10.12'} possible-typed-array-names@1.1.0: - resolution: {integrity: sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==} + resolution: {integrity: sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==, tarball: https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz} engines: {node: '>= 0.4'} postcss-calc@10.1.1: - resolution: {integrity: sha512-NYEsLHh8DgG/PRH2+G9BTuUdtf9ViS+vdoQ0YA5OQdGsfN4ztiwtDWNtBl9EKeqNMFnIu8IKZ0cLxEQ5r5KVMw==} + resolution: {integrity: sha512-NYEsLHh8DgG/PRH2+G9BTuUdtf9ViS+vdoQ0YA5OQdGsfN4ztiwtDWNtBl9EKeqNMFnIu8IKZ0cLxEQ5r5KVMw==, tarball: https://registry.npmjs.org/postcss-calc/-/postcss-calc-10.1.1.tgz} engines: {node: ^18.12 || ^20.9 || >=22.0} peerDependencies: postcss: ^8.4.38 postcss-calc@8.2.4: - resolution: {integrity: sha512-SmWMSJmB8MRnnULldx0lQIyhSNvuDl9HfrZkaqqE/WHAhToYsAvDq+yAsA/kIyINDszOp3Rh0GFoNuH5Ypsm3Q==} + resolution: {integrity: sha512-SmWMSJmB8MRnnULldx0lQIyhSNvuDl9HfrZkaqqE/WHAhToYsAvDq+yAsA/kIyINDszOp3Rh0GFoNuH5Ypsm3Q==, tarball: https://registry.npmjs.org/postcss-calc/-/postcss-calc-8.2.4.tgz} peerDependencies: postcss: ^8.2.2 postcss-calc@9.0.1: - resolution: {integrity: sha512-TipgjGyzP5QzEhsOZUaIkeO5mKeMFpebWzRogWG/ysonUlnHcq5aJe0jOjpfzUU8PeSaBQnrE8ehR0QA5vs8PQ==} + resolution: {integrity: sha512-TipgjGyzP5QzEhsOZUaIkeO5mKeMFpebWzRogWG/ysonUlnHcq5aJe0jOjpfzUU8PeSaBQnrE8ehR0QA5vs8PQ==, tarball: https://registry.npmjs.org/postcss-calc/-/postcss-calc-9.0.1.tgz} engines: {node: ^14 || ^16 || >=18.0} peerDependencies: postcss: ^8.2.2 postcss-colormin@5.3.1: - resolution: {integrity: sha512-UsWQG0AqTFQmpBegeLLc1+c3jIqBNB0zlDGRWR+dQ3pRKJL1oeMzyqmH3o2PIfn9MBdNrVPWhDbT769LxCTLJQ==} + resolution: {integrity: sha512-UsWQG0AqTFQmpBegeLLc1+c3jIqBNB0zlDGRWR+dQ3pRKJL1oeMzyqmH3o2PIfn9MBdNrVPWhDbT769LxCTLJQ==, tarball: https://registry.npmjs.org/postcss-colormin/-/postcss-colormin-5.3.1.tgz} engines: {node: ^10 || ^12 || >=14.0} peerDependencies: postcss: ^8.2.15 postcss-colormin@6.1.0: - resolution: {integrity: sha512-x9yX7DOxeMAR+BgGVnNSAxmAj98NX/YxEMNFP+SDCEeNLb2r3i6Hh1ksMsnW8Ub5SLCpbescQqn9YEbE9554Sw==} + resolution: {integrity: sha512-x9yX7DOxeMAR+BgGVnNSAxmAj98NX/YxEMNFP+SDCEeNLb2r3i6Hh1ksMsnW8Ub5SLCpbescQqn9YEbE9554Sw==, tarball: https://registry.npmjs.org/postcss-colormin/-/postcss-colormin-6.1.0.tgz} engines: {node: ^14 || ^16 || >=18.0} peerDependencies: postcss: ^8.4.31 postcss-colormin@7.0.10: - resolution: {integrity: sha512-yFr6JezOolHLta/buLE71VKPh2mXursp4saVe98/ol8ZnEWhL+racShqPKlvd/DKWLre/39B6HhcMXf7RZ3hxg==} + resolution: {integrity: sha512-yFr6JezOolHLta/buLE71VKPh2mXursp4saVe98/ol8ZnEWhL+racShqPKlvd/DKWLre/39B6HhcMXf7RZ3hxg==, tarball: https://registry.npmjs.org/postcss-colormin/-/postcss-colormin-7.0.10.tgz} engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} peerDependencies: postcss: ^8.5.13 postcss-convert-values@5.1.3: - resolution: {integrity: sha512-82pC1xkJZtcJEfiLw6UXnXVXScgtBrjlO5CBmuDQc+dlb88ZYheFsjTn40+zBVi3DkfF7iezO0nJUPLcJK3pvA==} + resolution: {integrity: sha512-82pC1xkJZtcJEfiLw6UXnXVXScgtBrjlO5CBmuDQc+dlb88ZYheFsjTn40+zBVi3DkfF7iezO0nJUPLcJK3pvA==, tarball: https://registry.npmjs.org/postcss-convert-values/-/postcss-convert-values-5.1.3.tgz} engines: {node: ^10 || ^12 || >=14.0} peerDependencies: postcss: ^8.2.15 postcss-convert-values@6.1.0: - resolution: {integrity: sha512-zx8IwP/ts9WvUM6NkVSkiU902QZL1bwPhaVaLynPtCsOTqp+ZKbNi+s6XJg3rfqpKGA/oc7Oxk5t8pOQJcwl/w==} + resolution: {integrity: sha512-zx8IwP/ts9WvUM6NkVSkiU902QZL1bwPhaVaLynPtCsOTqp+ZKbNi+s6XJg3rfqpKGA/oc7Oxk5t8pOQJcwl/w==, tarball: https://registry.npmjs.org/postcss-convert-values/-/postcss-convert-values-6.1.0.tgz} engines: {node: ^14 || ^16 || >=18.0} peerDependencies: postcss: ^8.4.31 postcss-convert-values@7.0.12: - resolution: {integrity: sha512-xurKu5qqk4viR3Cp3p4xBR4KfnZm4w4ys6+UBwBmeuBSNkH7+DtLnYOYnOffgtE4yx8sH9S1VZ6RAAvROXzP2Q==} + resolution: {integrity: sha512-xurKu5qqk4viR3Cp3p4xBR4KfnZm4w4ys6+UBwBmeuBSNkH7+DtLnYOYnOffgtE4yx8sH9S1VZ6RAAvROXzP2Q==, tarball: https://registry.npmjs.org/postcss-convert-values/-/postcss-convert-values-7.0.12.tgz} engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} peerDependencies: postcss: ^8.5.13 postcss-discard-comments@5.1.2: - resolution: {integrity: sha512-+L8208OVbHVF2UQf1iDmRcbdjJkuBF6IS29yBDSiWUIzpYaAhtNl6JYnYm12FnkeCwQqF5LeklOu6rAqgfBZqQ==} + resolution: {integrity: sha512-+L8208OVbHVF2UQf1iDmRcbdjJkuBF6IS29yBDSiWUIzpYaAhtNl6JYnYm12FnkeCwQqF5LeklOu6rAqgfBZqQ==, tarball: https://registry.npmjs.org/postcss-discard-comments/-/postcss-discard-comments-5.1.2.tgz} engines: {node: ^10 || ^12 || >=14.0} peerDependencies: postcss: ^8.2.15 postcss-discard-comments@6.0.2: - resolution: {integrity: sha512-65w/uIqhSBBfQmYnG92FO1mWZjJ4GL5b8atm5Yw2UgrwD7HiNiSSNwJor1eCFGzUgYnN/iIknhNRVqjrrpuglw==} + resolution: {integrity: sha512-65w/uIqhSBBfQmYnG92FO1mWZjJ4GL5b8atm5Yw2UgrwD7HiNiSSNwJor1eCFGzUgYnN/iIknhNRVqjrrpuglw==, tarball: https://registry.npmjs.org/postcss-discard-comments/-/postcss-discard-comments-6.0.2.tgz} engines: {node: ^14 || ^16 || >=18.0} peerDependencies: postcss: ^8.4.31 postcss-discard-comments@7.0.8: - resolution: {integrity: sha512-CvvS5S9WrXblFXCEJ9nVo+4z+eA7zSC7Z88V1HEJuwlQhlFnYTIjg1xJY+BCUiG2bvICap2tXii4mP22BD108Q==} + resolution: {integrity: sha512-CvvS5S9WrXblFXCEJ9nVo+4z+eA7zSC7Z88V1HEJuwlQhlFnYTIjg1xJY+BCUiG2bvICap2tXii4mP22BD108Q==, tarball: https://registry.npmjs.org/postcss-discard-comments/-/postcss-discard-comments-7.0.8.tgz} engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} peerDependencies: postcss: ^8.5.13 postcss-discard-duplicates@5.1.0: - resolution: {integrity: sha512-zmX3IoSI2aoenxHV6C7plngHWWhUOV3sP1T8y2ifzxzbtnuhk1EdPwm0S1bIUNaJ2eNbWeGLEwzw8huPD67aQw==} + resolution: {integrity: sha512-zmX3IoSI2aoenxHV6C7plngHWWhUOV3sP1T8y2ifzxzbtnuhk1EdPwm0S1bIUNaJ2eNbWeGLEwzw8huPD67aQw==, tarball: https://registry.npmjs.org/postcss-discard-duplicates/-/postcss-discard-duplicates-5.1.0.tgz} engines: {node: ^10 || ^12 || >=14.0} peerDependencies: postcss: ^8.2.15 postcss-discard-duplicates@6.0.3: - resolution: {integrity: sha512-+JA0DCvc5XvFAxwx6f/e68gQu/7Z9ud584VLmcgto28eB8FqSFZwtrLwB5Kcp70eIoWP/HXqz4wpo8rD8gpsTw==} + resolution: {integrity: sha512-+JA0DCvc5XvFAxwx6f/e68gQu/7Z9ud584VLmcgto28eB8FqSFZwtrLwB5Kcp70eIoWP/HXqz4wpo8rD8gpsTw==, tarball: https://registry.npmjs.org/postcss-discard-duplicates/-/postcss-discard-duplicates-6.0.3.tgz} engines: {node: ^14 || ^16 || >=18.0} peerDependencies: postcss: ^8.4.31 postcss-discard-duplicates@7.0.4: - resolution: {integrity: sha512-VBNn1+EuMZkeGVVtz0gRfbNGtx9IFgAsAV+E2pHtXPrp4qfGBkhTIiAuE/wrb+Y6Pakg9NewAlfTpYIFAWODtw==} + resolution: {integrity: sha512-VBNn1+EuMZkeGVVtz0gRfbNGtx9IFgAsAV+E2pHtXPrp4qfGBkhTIiAuE/wrb+Y6Pakg9NewAlfTpYIFAWODtw==, tarball: https://registry.npmjs.org/postcss-discard-duplicates/-/postcss-discard-duplicates-7.0.4.tgz} engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} peerDependencies: postcss: ^8.5.13 postcss-discard-empty@5.1.1: - resolution: {integrity: sha512-zPz4WljiSuLWsI0ir4Mcnr4qQQ5e1Ukc3i7UfE2XcrwKK2LIPIqE5jxMRxO6GbI3cv//ztXDsXwEWT3BHOGh3A==} + resolution: {integrity: sha512-zPz4WljiSuLWsI0ir4Mcnr4qQQ5e1Ukc3i7UfE2XcrwKK2LIPIqE5jxMRxO6GbI3cv//ztXDsXwEWT3BHOGh3A==, tarball: https://registry.npmjs.org/postcss-discard-empty/-/postcss-discard-empty-5.1.1.tgz} engines: {node: ^10 || ^12 || >=14.0} peerDependencies: postcss: ^8.2.15 postcss-discard-empty@6.0.3: - resolution: {integrity: sha512-znyno9cHKQsK6PtxL5D19Fj9uwSzC2mB74cpT66fhgOadEUPyXFkbgwm5tvc3bt3NAy8ltE5MrghxovZRVnOjQ==} + resolution: {integrity: sha512-znyno9cHKQsK6PtxL5D19Fj9uwSzC2mB74cpT66fhgOadEUPyXFkbgwm5tvc3bt3NAy8ltE5MrghxovZRVnOjQ==, tarball: https://registry.npmjs.org/postcss-discard-empty/-/postcss-discard-empty-6.0.3.tgz} engines: {node: ^14 || ^16 || >=18.0} peerDependencies: postcss: ^8.4.31 postcss-discard-empty@7.0.3: - resolution: {integrity: sha512-M2pyjQCU+/7cMHVtL6bKTHjv0lZnPLMpicgr67Dlth7AbuV9gjVTtUqaRwn6Pp6BwSDspUzhz8SaUrRykJU5Dw==} + resolution: {integrity: sha512-M2pyjQCU+/7cMHVtL6bKTHjv0lZnPLMpicgr67Dlth7AbuV9gjVTtUqaRwn6Pp6BwSDspUzhz8SaUrRykJU5Dw==, tarball: https://registry.npmjs.org/postcss-discard-empty/-/postcss-discard-empty-7.0.3.tgz} engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} peerDependencies: postcss: ^8.5.13 postcss-discard-overridden@5.1.0: - resolution: {integrity: sha512-21nOL7RqWR1kasIVdKs8HNqQJhFxLsyRfAnUDm4Fe4t4mCWL9OJiHvlHPjcd8zc5Myu89b/7wZDnOSjFgeWRtw==} + resolution: {integrity: sha512-21nOL7RqWR1kasIVdKs8HNqQJhFxLsyRfAnUDm4Fe4t4mCWL9OJiHvlHPjcd8zc5Myu89b/7wZDnOSjFgeWRtw==, tarball: https://registry.npmjs.org/postcss-discard-overridden/-/postcss-discard-overridden-5.1.0.tgz} engines: {node: ^10 || ^12 || >=14.0} peerDependencies: postcss: ^8.2.15 postcss-discard-overridden@6.0.2: - resolution: {integrity: sha512-j87xzI4LUggC5zND7KdjsI25APtyMuynXZSujByMaav2roV6OZX+8AaCUcZSWqckZpjAjRyFDdpqybgjFO0HJQ==} + resolution: {integrity: sha512-j87xzI4LUggC5zND7KdjsI25APtyMuynXZSujByMaav2roV6OZX+8AaCUcZSWqckZpjAjRyFDdpqybgjFO0HJQ==, tarball: https://registry.npmjs.org/postcss-discard-overridden/-/postcss-discard-overridden-6.0.2.tgz} engines: {node: ^14 || ^16 || >=18.0} peerDependencies: postcss: ^8.4.31 postcss-discard-overridden@7.0.3: - resolution: {integrity: sha512-aNovXo9UsZuRNLzHJtp13lHIvinDPfiXBPePpXkSjCbgp++iU2FqE+YxvjIsg6EdyPZsASFbfu+JcBFVsErXIQ==} + resolution: {integrity: sha512-aNovXo9UsZuRNLzHJtp13lHIvinDPfiXBPePpXkSjCbgp++iU2FqE+YxvjIsg6EdyPZsASFbfu+JcBFVsErXIQ==, tarball: https://registry.npmjs.org/postcss-discard-overridden/-/postcss-discard-overridden-7.0.3.tgz} engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} peerDependencies: postcss: ^8.5.13 postcss-import@14.1.0: - resolution: {integrity: sha512-flwI+Vgm4SElObFVPpTIT7SU7R3qk2L7PyduMcokiaVKuWv9d/U+Gm/QAd8NDLuykTWTkcrjOeD2Pp1rMeBTGw==} + resolution: {integrity: sha512-flwI+Vgm4SElObFVPpTIT7SU7R3qk2L7PyduMcokiaVKuWv9d/U+Gm/QAd8NDLuykTWTkcrjOeD2Pp1rMeBTGw==, tarball: https://registry.npmjs.org/postcss-import/-/postcss-import-14.1.0.tgz} engines: {node: '>=10.0.0'} peerDependencies: postcss: ^8.0.0 postcss-load-config@3.1.4: - resolution: {integrity: sha512-6DiM4E7v4coTE4uzA8U//WhtPwyhiim3eyjEMFCnUpzbrkK9wJHgKDT2mR+HbtSrd/NubVaYTOpSpjUl8NQeRg==} + resolution: {integrity: sha512-6DiM4E7v4coTE4uzA8U//WhtPwyhiim3eyjEMFCnUpzbrkK9wJHgKDT2mR+HbtSrd/NubVaYTOpSpjUl8NQeRg==, tarball: https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-3.1.4.tgz} engines: {node: '>= 10'} peerDependencies: postcss: '>=8.0.9' @@ -12513,14 +12576,14 @@ packages: optional: true postcss-loader@6.2.1: - resolution: {integrity: sha512-WbbYpmAaKcux/P66bZ40bpWsBucjx/TTgVVzRZ9yUO8yQfVBlameJ0ZGVaPfH64hNSBh63a+ICP5nqOpBA0w+Q==} + resolution: {integrity: sha512-WbbYpmAaKcux/P66bZ40bpWsBucjx/TTgVVzRZ9yUO8yQfVBlameJ0ZGVaPfH64hNSBh63a+ICP5nqOpBA0w+Q==, tarball: https://registry.npmjs.org/postcss-loader/-/postcss-loader-6.2.1.tgz} engines: {node: '>= 12.13.0'} peerDependencies: postcss: ^7.0.0 || ^8.0.1 webpack: ^5.0.0 postcss-loader@8.2.1: - resolution: {integrity: sha512-k98jtRzthjj3f76MYTs9JTpRqV1RaaMhEU0Lpw9OTmQZQdppg4B30VZ74BojuBHt3F4KyubHJoXCMUeM8Bqeow==} + resolution: {integrity: sha512-k98jtRzthjj3f76MYTs9JTpRqV1RaaMhEU0Lpw9OTmQZQdppg4B30VZ74BojuBHt3F4KyubHJoXCMUeM8Bqeow==, tarball: https://registry.npmjs.org/postcss-loader/-/postcss-loader-8.2.1.tgz} engines: {node: '>= 18.12.0'} peerDependencies: '@rspack/core': 0.x || ^1.0.0 || ^2.0.0-0 @@ -12533,449 +12596,449 @@ packages: optional: true postcss-media-query-parser@0.2.3: - resolution: {integrity: sha512-3sOlxmbKcSHMjlUXQZKQ06jOswE7oVkXPxmZdoB1r5l0q6gTFTQSHxNxOrCccElbW7dxNytifNEo8qidX2Vsig==} + resolution: {integrity: sha512-3sOlxmbKcSHMjlUXQZKQ06jOswE7oVkXPxmZdoB1r5l0q6gTFTQSHxNxOrCccElbW7dxNytifNEo8qidX2Vsig==, tarball: https://registry.npmjs.org/postcss-media-query-parser/-/postcss-media-query-parser-0.2.3.tgz} postcss-merge-longhand@5.1.7: - resolution: {integrity: sha512-YCI9gZB+PLNskrK0BB3/2OzPnGhPkBEwmwhfYk1ilBHYVAZB7/tkTHFBAnCrvBBOmeYyMYw3DMjT55SyxMBzjQ==} + resolution: {integrity: sha512-YCI9gZB+PLNskrK0BB3/2OzPnGhPkBEwmwhfYk1ilBHYVAZB7/tkTHFBAnCrvBBOmeYyMYw3DMjT55SyxMBzjQ==, tarball: https://registry.npmjs.org/postcss-merge-longhand/-/postcss-merge-longhand-5.1.7.tgz} engines: {node: ^10 || ^12 || >=14.0} peerDependencies: postcss: ^8.2.15 postcss-merge-longhand@6.0.5: - resolution: {integrity: sha512-5LOiordeTfi64QhICp07nzzuTDjNSO8g5Ksdibt44d+uvIIAE1oZdRn8y/W5ZtYgRH/lnLDlvi9F8btZcVzu3w==} + resolution: {integrity: sha512-5LOiordeTfi64QhICp07nzzuTDjNSO8g5Ksdibt44d+uvIIAE1oZdRn8y/W5ZtYgRH/lnLDlvi9F8btZcVzu3w==, tarball: https://registry.npmjs.org/postcss-merge-longhand/-/postcss-merge-longhand-6.0.5.tgz} engines: {node: ^14 || ^16 || >=18.0} peerDependencies: postcss: ^8.4.31 postcss-merge-longhand@7.0.7: - resolution: {integrity: sha512-b3mfYUxR388u5Pt0HPcVIUtUDn/k15UfTY9M+ORW+meCR6JLNxoZffiYvXyOYQoRYQNZyX/UFkMCM/mNHxe1qA==} + resolution: {integrity: sha512-b3mfYUxR388u5Pt0HPcVIUtUDn/k15UfTY9M+ORW+meCR6JLNxoZffiYvXyOYQoRYQNZyX/UFkMCM/mNHxe1qA==, tarball: https://registry.npmjs.org/postcss-merge-longhand/-/postcss-merge-longhand-7.0.7.tgz} engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} peerDependencies: postcss: ^8.5.13 postcss-merge-rules@5.1.4: - resolution: {integrity: sha512-0R2IuYpgU93y9lhVbO/OylTtKMVcHb67zjWIfCiKR9rWL3GUk1677LAqD/BcHizukdZEjT8Ru3oHRoAYoJy44g==} + resolution: {integrity: sha512-0R2IuYpgU93y9lhVbO/OylTtKMVcHb67zjWIfCiKR9rWL3GUk1677LAqD/BcHizukdZEjT8Ru3oHRoAYoJy44g==, tarball: https://registry.npmjs.org/postcss-merge-rules/-/postcss-merge-rules-5.1.4.tgz} engines: {node: ^10 || ^12 || >=14.0} peerDependencies: postcss: ^8.2.15 postcss-merge-rules@6.1.1: - resolution: {integrity: sha512-KOdWF0gju31AQPZiD+2Ar9Qjowz1LTChSjFFbS+e2sFgc4uHOp3ZvVX4sNeTlk0w2O31ecFGgrFzhO0RSWbWwQ==} + resolution: {integrity: sha512-KOdWF0gju31AQPZiD+2Ar9Qjowz1LTChSjFFbS+e2sFgc4uHOp3ZvVX4sNeTlk0w2O31ecFGgrFzhO0RSWbWwQ==, tarball: https://registry.npmjs.org/postcss-merge-rules/-/postcss-merge-rules-6.1.1.tgz} engines: {node: ^14 || ^16 || >=18.0} peerDependencies: postcss: ^8.4.31 postcss-merge-rules@7.0.11: - resolution: {integrity: sha512-SJUPM18g2BmPhf8BVlbwqWz4aK3pLu6u6xjfwEzra7xL6IBR10sUaiB++EzqcVfadPHrKBSMlNdP+XieykhI+Q==} + resolution: {integrity: sha512-SJUPM18g2BmPhf8BVlbwqWz4aK3pLu6u6xjfwEzra7xL6IBR10sUaiB++EzqcVfadPHrKBSMlNdP+XieykhI+Q==, tarball: https://registry.npmjs.org/postcss-merge-rules/-/postcss-merge-rules-7.0.11.tgz} engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} peerDependencies: postcss: ^8.5.13 postcss-minify-font-values@5.1.0: - resolution: {integrity: sha512-el3mYTgx13ZAPPirSVsHqFzl+BBBDrXvbySvPGFnQcTI4iNslrPaFq4muTkLZmKlGk4gyFAYUBMH30+HurREyA==} + resolution: {integrity: sha512-el3mYTgx13ZAPPirSVsHqFzl+BBBDrXvbySvPGFnQcTI4iNslrPaFq4muTkLZmKlGk4gyFAYUBMH30+HurREyA==, tarball: https://registry.npmjs.org/postcss-minify-font-values/-/postcss-minify-font-values-5.1.0.tgz} engines: {node: ^10 || ^12 || >=14.0} peerDependencies: postcss: ^8.2.15 postcss-minify-font-values@6.1.0: - resolution: {integrity: sha512-gklfI/n+9rTh8nYaSJXlCo3nOKqMNkxuGpTn/Qm0gstL3ywTr9/WRKznE+oy6fvfolH6dF+QM4nCo8yPLdvGJg==} + resolution: {integrity: sha512-gklfI/n+9rTh8nYaSJXlCo3nOKqMNkxuGpTn/Qm0gstL3ywTr9/WRKznE+oy6fvfolH6dF+QM4nCo8yPLdvGJg==, tarball: https://registry.npmjs.org/postcss-minify-font-values/-/postcss-minify-font-values-6.1.0.tgz} engines: {node: ^14 || ^16 || >=18.0} peerDependencies: postcss: ^8.4.31 postcss-minify-font-values@7.0.3: - resolution: {integrity: sha512-yilG/VOaNI74IylQvAQQxm3/wZVBkXyYUqNUAdxqwtbWUXPsbK1q8Ms0mL83v+f8YicgcyfYCRZtWACUdYajpA==} + resolution: {integrity: sha512-yilG/VOaNI74IylQvAQQxm3/wZVBkXyYUqNUAdxqwtbWUXPsbK1q8Ms0mL83v+f8YicgcyfYCRZtWACUdYajpA==, tarball: https://registry.npmjs.org/postcss-minify-font-values/-/postcss-minify-font-values-7.0.3.tgz} engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} peerDependencies: postcss: ^8.5.13 postcss-minify-gradients@5.1.1: - resolution: {integrity: sha512-VGvXMTpCEo4qHTNSa9A0a3D+dxGFZCYwR6Jokk+/3oB6flu2/PnPXAh2x7x52EkY5xlIHLm+Le8tJxe/7TNhzw==} + resolution: {integrity: sha512-VGvXMTpCEo4qHTNSa9A0a3D+dxGFZCYwR6Jokk+/3oB6flu2/PnPXAh2x7x52EkY5xlIHLm+Le8tJxe/7TNhzw==, tarball: https://registry.npmjs.org/postcss-minify-gradients/-/postcss-minify-gradients-5.1.1.tgz} engines: {node: ^10 || ^12 || >=14.0} peerDependencies: postcss: ^8.2.15 postcss-minify-gradients@6.0.3: - resolution: {integrity: sha512-4KXAHrYlzF0Rr7uc4VrfwDJ2ajrtNEpNEuLxFgwkhFZ56/7gaE4Nr49nLsQDZyUe+ds+kEhf+YAUolJiYXF8+Q==} + resolution: {integrity: sha512-4KXAHrYlzF0Rr7uc4VrfwDJ2ajrtNEpNEuLxFgwkhFZ56/7gaE4Nr49nLsQDZyUe+ds+kEhf+YAUolJiYXF8+Q==, tarball: https://registry.npmjs.org/postcss-minify-gradients/-/postcss-minify-gradients-6.0.3.tgz} engines: {node: ^14 || ^16 || >=18.0} peerDependencies: postcss: ^8.4.31 postcss-minify-gradients@7.0.5: - resolution: {integrity: sha512-YraROyQRg3BI1+Hg8E05B/JPdnTm8EDSVu4P2BxdM+CRiOyfmou809+chGIqo6fQqwjPGQ947nbGncSjmTU1WQ==} + resolution: {integrity: sha512-YraROyQRg3BI1+Hg8E05B/JPdnTm8EDSVu4P2BxdM+CRiOyfmou809+chGIqo6fQqwjPGQ947nbGncSjmTU1WQ==, tarball: https://registry.npmjs.org/postcss-minify-gradients/-/postcss-minify-gradients-7.0.5.tgz} engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} peerDependencies: postcss: ^8.5.13 postcss-minify-params@5.1.4: - resolution: {integrity: sha512-+mePA3MgdmVmv6g+30rn57USjOGSAyuxUmkfiWpzalZ8aiBkdPYjXWtHuwJGm1v5Ojy0Z0LaSYhHaLJQB0P8Jw==} + resolution: {integrity: sha512-+mePA3MgdmVmv6g+30rn57USjOGSAyuxUmkfiWpzalZ8aiBkdPYjXWtHuwJGm1v5Ojy0Z0LaSYhHaLJQB0P8Jw==, tarball: https://registry.npmjs.org/postcss-minify-params/-/postcss-minify-params-5.1.4.tgz} engines: {node: ^10 || ^12 || >=14.0} peerDependencies: postcss: ^8.2.15 postcss-minify-params@6.1.0: - resolution: {integrity: sha512-bmSKnDtyyE8ujHQK0RQJDIKhQ20Jq1LYiez54WiaOoBtcSuflfK3Nm596LvbtlFcpipMjgClQGyGr7GAs+H1uA==} + resolution: {integrity: sha512-bmSKnDtyyE8ujHQK0RQJDIKhQ20Jq1LYiez54WiaOoBtcSuflfK3Nm596LvbtlFcpipMjgClQGyGr7GAs+H1uA==, tarball: https://registry.npmjs.org/postcss-minify-params/-/postcss-minify-params-6.1.0.tgz} engines: {node: ^14 || ^16 || >=18.0} peerDependencies: postcss: ^8.4.31 postcss-minify-params@7.0.9: - resolution: {integrity: sha512-R8itbB8BhlpoYyBm1ou0dD+vJnQ3F6adQipR4UnkCHUwlo+S9WXJaDRg1RHjC8YVAtIdrQzSWvJl40HnGDTKjA==} + resolution: {integrity: sha512-R8itbB8BhlpoYyBm1ou0dD+vJnQ3F6adQipR4UnkCHUwlo+S9WXJaDRg1RHjC8YVAtIdrQzSWvJl40HnGDTKjA==, tarball: https://registry.npmjs.org/postcss-minify-params/-/postcss-minify-params-7.0.9.tgz} engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} peerDependencies: postcss: ^8.5.13 postcss-minify-selectors@5.2.1: - resolution: {integrity: sha512-nPJu7OjZJTsVUmPdm2TcaiohIwxP+v8ha9NehQ2ye9szv4orirRU3SDdtUmKH+10nzn0bAyOXZ0UEr7OpvLehg==} + resolution: {integrity: sha512-nPJu7OjZJTsVUmPdm2TcaiohIwxP+v8ha9NehQ2ye9szv4orirRU3SDdtUmKH+10nzn0bAyOXZ0UEr7OpvLehg==, tarball: https://registry.npmjs.org/postcss-minify-selectors/-/postcss-minify-selectors-5.2.1.tgz} engines: {node: ^10 || ^12 || >=14.0} peerDependencies: postcss: ^8.2.15 postcss-minify-selectors@6.0.4: - resolution: {integrity: sha512-L8dZSwNLgK7pjTto9PzWRoMbnLq5vsZSTu8+j1P/2GB8qdtGQfn+K1uSvFgYvgh83cbyxT5m43ZZhUMTJDSClQ==} + resolution: {integrity: sha512-L8dZSwNLgK7pjTto9PzWRoMbnLq5vsZSTu8+j1P/2GB8qdtGQfn+K1uSvFgYvgh83cbyxT5m43ZZhUMTJDSClQ==, tarball: https://registry.npmjs.org/postcss-minify-selectors/-/postcss-minify-selectors-6.0.4.tgz} engines: {node: ^14 || ^16 || >=18.0} peerDependencies: postcss: ^8.4.31 postcss-minify-selectors@7.1.2: - resolution: {integrity: sha512-aQtrEWKwqafNlExcKHQvPGsXR2+vlUqqJtf5XsCQcgsSb5PL4wlujWBYDJuWsP4UnQX1YHDHU8qRlD+1PzTQ+Q==} + resolution: {integrity: sha512-aQtrEWKwqafNlExcKHQvPGsXR2+vlUqqJtf5XsCQcgsSb5PL4wlujWBYDJuWsP4UnQX1YHDHU8qRlD+1PzTQ+Q==, tarball: https://registry.npmjs.org/postcss-minify-selectors/-/postcss-minify-selectors-7.1.2.tgz} engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} peerDependencies: postcss: ^8.5.13 postcss-modules-extract-imports@3.1.0: - resolution: {integrity: sha512-k3kNe0aNFQDAZGbin48pL2VNidTF0w4/eASDsxlyspobzU3wZQLOGj7L9gfRe0Jo9/4uud09DsjFNH7winGv8Q==} + resolution: {integrity: sha512-k3kNe0aNFQDAZGbin48pL2VNidTF0w4/eASDsxlyspobzU3wZQLOGj7L9gfRe0Jo9/4uud09DsjFNH7winGv8Q==, tarball: https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.1.0.tgz} engines: {node: ^10 || ^12 || >= 14} peerDependencies: postcss: ^8.1.0 postcss-modules-local-by-default@4.2.0: - resolution: {integrity: sha512-5kcJm/zk+GJDSfw+V/42fJ5fhjL5YbFDl8nVdXkJPLLW+Vf9mTD5Xe0wqIaDnLuL2U6cDNpTr+UQ+v2HWIBhzw==} + resolution: {integrity: sha512-5kcJm/zk+GJDSfw+V/42fJ5fhjL5YbFDl8nVdXkJPLLW+Vf9mTD5Xe0wqIaDnLuL2U6cDNpTr+UQ+v2HWIBhzw==, tarball: https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.2.0.tgz} engines: {node: ^10 || ^12 || >= 14} peerDependencies: postcss: ^8.1.0 postcss-modules-scope@3.2.1: - resolution: {integrity: sha512-m9jZstCVaqGjTAuny8MdgE88scJnCiQSlSrOWcTQgM2t32UBe+MUmFSO5t7VMSfAf/FJKImAxBav8ooCHJXCJA==} + resolution: {integrity: sha512-m9jZstCVaqGjTAuny8MdgE88scJnCiQSlSrOWcTQgM2t32UBe+MUmFSO5t7VMSfAf/FJKImAxBav8ooCHJXCJA==, tarball: https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-3.2.1.tgz} engines: {node: ^10 || ^12 || >= 14} peerDependencies: postcss: ^8.1.0 postcss-modules-values@4.0.0: - resolution: {integrity: sha512-RDxHkAiEGI78gS2ofyvCsu7iycRv7oqw5xMWn9iMoR0N/7mf9D50ecQqUo5BZ9Zh2vH4bCUR/ktCqbB9m8vJjQ==} + resolution: {integrity: sha512-RDxHkAiEGI78gS2ofyvCsu7iycRv7oqw5xMWn9iMoR0N/7mf9D50ecQqUo5BZ9Zh2vH4bCUR/ktCqbB9m8vJjQ==, tarball: https://registry.npmjs.org/postcss-modules-values/-/postcss-modules-values-4.0.0.tgz} engines: {node: ^10 || ^12 || >= 14} peerDependencies: postcss: ^8.1.0 postcss-modules@4.3.1: - resolution: {integrity: sha512-ItUhSUxBBdNamkT3KzIZwYNNRFKmkJrofvC2nWab3CPKhYBQ1f27XXh1PAPE27Psx58jeelPsxWB/+og+KEH0Q==} + resolution: {integrity: sha512-ItUhSUxBBdNamkT3KzIZwYNNRFKmkJrofvC2nWab3CPKhYBQ1f27XXh1PAPE27Psx58jeelPsxWB/+og+KEH0Q==, tarball: https://registry.npmjs.org/postcss-modules/-/postcss-modules-4.3.1.tgz} peerDependencies: postcss: ^8.0.0 postcss-modules@6.0.1: - resolution: {integrity: sha512-zyo2sAkVvuZFFy0gc2+4O+xar5dYlaVy/ebO24KT0ftk/iJevSNyPyQellsBLlnccwh7f6V6Y4GvuKRYToNgpQ==} + resolution: {integrity: sha512-zyo2sAkVvuZFFy0gc2+4O+xar5dYlaVy/ebO24KT0ftk/iJevSNyPyQellsBLlnccwh7f6V6Y4GvuKRYToNgpQ==, tarball: https://registry.npmjs.org/postcss-modules/-/postcss-modules-6.0.1.tgz} peerDependencies: postcss: ^8.0.0 postcss-normalize-charset@5.1.0: - resolution: {integrity: sha512-mSgUJ+pd/ldRGVx26p2wz9dNZ7ji6Pn8VWBajMXFf8jk7vUoSrZ2lt/wZR7DtlZYKesmZI680qjr2CeFF2fbUg==} + resolution: {integrity: sha512-mSgUJ+pd/ldRGVx26p2wz9dNZ7ji6Pn8VWBajMXFf8jk7vUoSrZ2lt/wZR7DtlZYKesmZI680qjr2CeFF2fbUg==, tarball: https://registry.npmjs.org/postcss-normalize-charset/-/postcss-normalize-charset-5.1.0.tgz} engines: {node: ^10 || ^12 || >=14.0} peerDependencies: postcss: ^8.2.15 postcss-normalize-charset@6.0.2: - resolution: {integrity: sha512-a8N9czmdnrjPHa3DeFlwqst5eaL5W8jYu3EBbTTkI5FHkfMhFZh1EGbku6jhHhIzTA6tquI2P42NtZ59M/H/kQ==} + resolution: {integrity: sha512-a8N9czmdnrjPHa3DeFlwqst5eaL5W8jYu3EBbTTkI5FHkfMhFZh1EGbku6jhHhIzTA6tquI2P42NtZ59M/H/kQ==, tarball: https://registry.npmjs.org/postcss-normalize-charset/-/postcss-normalize-charset-6.0.2.tgz} engines: {node: ^14 || ^16 || >=18.0} peerDependencies: postcss: ^8.4.31 postcss-normalize-charset@7.0.3: - resolution: {integrity: sha512-NoBfZu8PR4c2NlmjvrqQTzCzLY79hwcSRgNQ3ZiNK0ABzf9kYKloE/jNj+/8GQY1wsm8pRRgANk6ydLH8cwo0Q==} + resolution: {integrity: sha512-NoBfZu8PR4c2NlmjvrqQTzCzLY79hwcSRgNQ3ZiNK0ABzf9kYKloE/jNj+/8GQY1wsm8pRRgANk6ydLH8cwo0Q==, tarball: https://registry.npmjs.org/postcss-normalize-charset/-/postcss-normalize-charset-7.0.3.tgz} engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} peerDependencies: postcss: ^8.5.13 postcss-normalize-display-values@5.1.0: - resolution: {integrity: sha512-WP4KIM4o2dazQXWmFaqMmcvsKmhdINFblgSeRgn8BJ6vxaMyaJkwAzpPpuvSIoG/rmX3M+IrRZEz2H0glrQNEA==} + resolution: {integrity: sha512-WP4KIM4o2dazQXWmFaqMmcvsKmhdINFblgSeRgn8BJ6vxaMyaJkwAzpPpuvSIoG/rmX3M+IrRZEz2H0glrQNEA==, tarball: https://registry.npmjs.org/postcss-normalize-display-values/-/postcss-normalize-display-values-5.1.0.tgz} engines: {node: ^10 || ^12 || >=14.0} peerDependencies: postcss: ^8.2.15 postcss-normalize-display-values@6.0.2: - resolution: {integrity: sha512-8H04Mxsb82ON/aAkPeq8kcBbAtI5Q2a64X/mnRRfPXBq7XeogoQvReqxEfc0B4WPq1KimjezNC8flUtC3Qz6jg==} + resolution: {integrity: sha512-8H04Mxsb82ON/aAkPeq8kcBbAtI5Q2a64X/mnRRfPXBq7XeogoQvReqxEfc0B4WPq1KimjezNC8flUtC3Qz6jg==, tarball: https://registry.npmjs.org/postcss-normalize-display-values/-/postcss-normalize-display-values-6.0.2.tgz} engines: {node: ^14 || ^16 || >=18.0} peerDependencies: postcss: ^8.4.31 postcss-normalize-display-values@7.0.3: - resolution: {integrity: sha512-ldsCX0QIt05pKIOobZtVQ48wXJecr+czw4+e1/YjVhLMqslShgpVxgPtI2CefURR8oyVoYaU/l829MMwExDMLw==} + resolution: {integrity: sha512-ldsCX0QIt05pKIOobZtVQ48wXJecr+czw4+e1/YjVhLMqslShgpVxgPtI2CefURR8oyVoYaU/l829MMwExDMLw==, tarball: https://registry.npmjs.org/postcss-normalize-display-values/-/postcss-normalize-display-values-7.0.3.tgz} engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} peerDependencies: postcss: ^8.5.13 postcss-normalize-positions@5.1.1: - resolution: {integrity: sha512-6UpCb0G4eofTCQLFVuI3EVNZzBNPiIKcA1AKVka+31fTVySphr3VUgAIULBhxZkKgwLImhzMR2Bw1ORK+37INg==} + resolution: {integrity: sha512-6UpCb0G4eofTCQLFVuI3EVNZzBNPiIKcA1AKVka+31fTVySphr3VUgAIULBhxZkKgwLImhzMR2Bw1ORK+37INg==, tarball: https://registry.npmjs.org/postcss-normalize-positions/-/postcss-normalize-positions-5.1.1.tgz} engines: {node: ^10 || ^12 || >=14.0} peerDependencies: postcss: ^8.2.15 postcss-normalize-positions@6.0.2: - resolution: {integrity: sha512-/JFzI441OAB9O7VnLA+RtSNZvQ0NCFZDOtp6QPFo1iIyawyXg0YI3CYM9HBy1WvwCRHnPep/BvI1+dGPKoXx/Q==} + resolution: {integrity: sha512-/JFzI441OAB9O7VnLA+RtSNZvQ0NCFZDOtp6QPFo1iIyawyXg0YI3CYM9HBy1WvwCRHnPep/BvI1+dGPKoXx/Q==, tarball: https://registry.npmjs.org/postcss-normalize-positions/-/postcss-normalize-positions-6.0.2.tgz} engines: {node: ^14 || ^16 || >=18.0} peerDependencies: postcss: ^8.4.31 postcss-normalize-positions@7.0.4: - resolution: {integrity: sha512-VEvlpeGd3Ju1Hqa/oN4jaP3+ms4laYwkEL9N9u+B6k54PZjXbW1n6wI+aVprf1BQXlCYpS5+1pl/7/vHiKgARg==} + resolution: {integrity: sha512-VEvlpeGd3Ju1Hqa/oN4jaP3+ms4laYwkEL9N9u+B6k54PZjXbW1n6wI+aVprf1BQXlCYpS5+1pl/7/vHiKgARg==, tarball: https://registry.npmjs.org/postcss-normalize-positions/-/postcss-normalize-positions-7.0.4.tgz} engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} peerDependencies: postcss: ^8.5.13 postcss-normalize-repeat-style@5.1.1: - resolution: {integrity: sha512-mFpLspGWkQtBcWIRFLmewo8aC3ImN2i/J3v8YCFUwDnPu3Xz4rLohDO26lGjwNsQxB3YF0KKRwspGzE2JEuS0g==} + resolution: {integrity: sha512-mFpLspGWkQtBcWIRFLmewo8aC3ImN2i/J3v8YCFUwDnPu3Xz4rLohDO26lGjwNsQxB3YF0KKRwspGzE2JEuS0g==, tarball: https://registry.npmjs.org/postcss-normalize-repeat-style/-/postcss-normalize-repeat-style-5.1.1.tgz} engines: {node: ^10 || ^12 || >=14.0} peerDependencies: postcss: ^8.2.15 postcss-normalize-repeat-style@6.0.2: - resolution: {integrity: sha512-YdCgsfHkJ2jEXwR4RR3Tm/iOxSfdRt7jplS6XRh9Js9PyCR/aka/FCb6TuHT2U8gQubbm/mPmF6L7FY9d79VwQ==} + resolution: {integrity: sha512-YdCgsfHkJ2jEXwR4RR3Tm/iOxSfdRt7jplS6XRh9Js9PyCR/aka/FCb6TuHT2U8gQubbm/mPmF6L7FY9d79VwQ==, tarball: https://registry.npmjs.org/postcss-normalize-repeat-style/-/postcss-normalize-repeat-style-6.0.2.tgz} engines: {node: ^14 || ^16 || >=18.0} peerDependencies: postcss: ^8.4.31 postcss-normalize-repeat-style@7.0.4: - resolution: {integrity: sha512-6mPKlY/8cSaDHxX502wERADarJsccwlky6yIrOapHH2ZgfoKAV94SbiTKfKEs4EEpdazuc3J72WsqeYk7hp9+Q==} + resolution: {integrity: sha512-6mPKlY/8cSaDHxX502wERADarJsccwlky6yIrOapHH2ZgfoKAV94SbiTKfKEs4EEpdazuc3J72WsqeYk7hp9+Q==, tarball: https://registry.npmjs.org/postcss-normalize-repeat-style/-/postcss-normalize-repeat-style-7.0.4.tgz} engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} peerDependencies: postcss: ^8.5.13 postcss-normalize-string@5.1.0: - resolution: {integrity: sha512-oYiIJOf4T9T1N4i+abeIc7Vgm/xPCGih4bZz5Nm0/ARVJ7K6xrDlLwvwqOydvyL3RHNf8qZk6vo3aatiw/go3w==} + resolution: {integrity: sha512-oYiIJOf4T9T1N4i+abeIc7Vgm/xPCGih4bZz5Nm0/ARVJ7K6xrDlLwvwqOydvyL3RHNf8qZk6vo3aatiw/go3w==, tarball: https://registry.npmjs.org/postcss-normalize-string/-/postcss-normalize-string-5.1.0.tgz} engines: {node: ^10 || ^12 || >=14.0} peerDependencies: postcss: ^8.2.15 postcss-normalize-string@6.0.2: - resolution: {integrity: sha512-vQZIivlxlfqqMp4L9PZsFE4YUkWniziKjQWUtsxUiVsSSPelQydwS8Wwcuw0+83ZjPWNTl02oxlIvXsmmG+CiQ==} + resolution: {integrity: sha512-vQZIivlxlfqqMp4L9PZsFE4YUkWniziKjQWUtsxUiVsSSPelQydwS8Wwcuw0+83ZjPWNTl02oxlIvXsmmG+CiQ==, tarball: https://registry.npmjs.org/postcss-normalize-string/-/postcss-normalize-string-6.0.2.tgz} engines: {node: ^14 || ^16 || >=18.0} peerDependencies: postcss: ^8.4.31 postcss-normalize-string@7.0.3: - resolution: {integrity: sha512-HnEQPUchi1eznmDKEYrKUTqrprEq97SrpUYClgUkv7V2zRODD9DFoUsYU+m9ZOetmD5ku7fEMZB/lwy8IT6xVQ==} + resolution: {integrity: sha512-HnEQPUchi1eznmDKEYrKUTqrprEq97SrpUYClgUkv7V2zRODD9DFoUsYU+m9ZOetmD5ku7fEMZB/lwy8IT6xVQ==, tarball: https://registry.npmjs.org/postcss-normalize-string/-/postcss-normalize-string-7.0.3.tgz} engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} peerDependencies: postcss: ^8.5.13 postcss-normalize-timing-functions@5.1.0: - resolution: {integrity: sha512-DOEkzJ4SAXv5xkHl0Wa9cZLF3WCBhF3o1SKVxKQAa+0pYKlueTpCgvkFAHfk+Y64ezX9+nITGrDZeVGgITJXjg==} + resolution: {integrity: sha512-DOEkzJ4SAXv5xkHl0Wa9cZLF3WCBhF3o1SKVxKQAa+0pYKlueTpCgvkFAHfk+Y64ezX9+nITGrDZeVGgITJXjg==, tarball: https://registry.npmjs.org/postcss-normalize-timing-functions/-/postcss-normalize-timing-functions-5.1.0.tgz} engines: {node: ^10 || ^12 || >=14.0} peerDependencies: postcss: ^8.2.15 postcss-normalize-timing-functions@6.0.2: - resolution: {integrity: sha512-a+YrtMox4TBtId/AEwbA03VcJgtyW4dGBizPl7e88cTFULYsprgHWTbfyjSLyHeBcK/Q9JhXkt2ZXiwaVHoMzA==} + resolution: {integrity: sha512-a+YrtMox4TBtId/AEwbA03VcJgtyW4dGBizPl7e88cTFULYsprgHWTbfyjSLyHeBcK/Q9JhXkt2ZXiwaVHoMzA==, tarball: https://registry.npmjs.org/postcss-normalize-timing-functions/-/postcss-normalize-timing-functions-6.0.2.tgz} engines: {node: ^14 || ^16 || >=18.0} peerDependencies: postcss: ^8.4.31 postcss-normalize-timing-functions@7.0.3: - resolution: {integrity: sha512-zmEzHdvpZBZu0OKlbJSfgASQvaayyAoVuWtvyr34IJ/LyS+DaOKvvR3EvFJ9RWWtNIx+CMvO125OVophaxNYew==} + resolution: {integrity: sha512-zmEzHdvpZBZu0OKlbJSfgASQvaayyAoVuWtvyr34IJ/LyS+DaOKvvR3EvFJ9RWWtNIx+CMvO125OVophaxNYew==, tarball: https://registry.npmjs.org/postcss-normalize-timing-functions/-/postcss-normalize-timing-functions-7.0.3.tgz} engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} peerDependencies: postcss: ^8.5.13 postcss-normalize-unicode@5.1.1: - resolution: {integrity: sha512-qnCL5jzkNUmKVhZoENp1mJiGNPcsJCs1aaRmURmeJGES23Z/ajaln+EPTD+rBeNkSryI+2WTdW+lwcVdOikrpA==} + resolution: {integrity: sha512-qnCL5jzkNUmKVhZoENp1mJiGNPcsJCs1aaRmURmeJGES23Z/ajaln+EPTD+rBeNkSryI+2WTdW+lwcVdOikrpA==, tarball: https://registry.npmjs.org/postcss-normalize-unicode/-/postcss-normalize-unicode-5.1.1.tgz} engines: {node: ^10 || ^12 || >=14.0} peerDependencies: postcss: ^8.2.15 postcss-normalize-unicode@6.1.0: - resolution: {integrity: sha512-QVC5TQHsVj33otj8/JD869Ndr5Xcc/+fwRh4HAsFsAeygQQXm+0PySrKbr/8tkDKzW+EVT3QkqZMfFrGiossDg==} + resolution: {integrity: sha512-QVC5TQHsVj33otj8/JD869Ndr5Xcc/+fwRh4HAsFsAeygQQXm+0PySrKbr/8tkDKzW+EVT3QkqZMfFrGiossDg==, tarball: https://registry.npmjs.org/postcss-normalize-unicode/-/postcss-normalize-unicode-6.1.0.tgz} engines: {node: ^14 || ^16 || >=18.0} peerDependencies: postcss: ^8.4.31 postcss-normalize-unicode@7.0.9: - resolution: {integrity: sha512-DRAdWfeh/TjmhLJsw91vdiWCnUod9iwvM7xyS02/nF/sLsCR3A8l3pztrSUrWG8DSBqfX7yEk9FM0USaVJ2mSg==} + resolution: {integrity: sha512-DRAdWfeh/TjmhLJsw91vdiWCnUod9iwvM7xyS02/nF/sLsCR3A8l3pztrSUrWG8DSBqfX7yEk9FM0USaVJ2mSg==, tarball: https://registry.npmjs.org/postcss-normalize-unicode/-/postcss-normalize-unicode-7.0.9.tgz} engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} peerDependencies: postcss: ^8.5.13 postcss-normalize-url@5.1.0: - resolution: {integrity: sha512-5upGeDO+PVthOxSmds43ZeMeZfKH+/DKgGRD7TElkkyS46JXAUhMzIKiCa7BabPeIy3AQcTkXwVVN7DbqsiCew==} + resolution: {integrity: sha512-5upGeDO+PVthOxSmds43ZeMeZfKH+/DKgGRD7TElkkyS46JXAUhMzIKiCa7BabPeIy3AQcTkXwVVN7DbqsiCew==, tarball: https://registry.npmjs.org/postcss-normalize-url/-/postcss-normalize-url-5.1.0.tgz} engines: {node: ^10 || ^12 || >=14.0} peerDependencies: postcss: ^8.2.15 postcss-normalize-url@6.0.2: - resolution: {integrity: sha512-kVNcWhCeKAzZ8B4pv/DnrU1wNh458zBNp8dh4y5hhxih5RZQ12QWMuQrDgPRw3LRl8mN9vOVfHl7uhvHYMoXsQ==} + resolution: {integrity: sha512-kVNcWhCeKAzZ8B4pv/DnrU1wNh458zBNp8dh4y5hhxih5RZQ12QWMuQrDgPRw3LRl8mN9vOVfHl7uhvHYMoXsQ==, tarball: https://registry.npmjs.org/postcss-normalize-url/-/postcss-normalize-url-6.0.2.tgz} engines: {node: ^14 || ^16 || >=18.0} peerDependencies: postcss: ^8.4.31 postcss-normalize-url@7.0.3: - resolution: {integrity: sha512-CL93wmloq5qsffmFv+bw24MIRbmhHrp53qoh1LDAb/5TtjWEXI/np4xcP/Gw9oWCb2XyWnqHYLDUwiKRoJBA1Q==} + resolution: {integrity: sha512-CL93wmloq5qsffmFv+bw24MIRbmhHrp53qoh1LDAb/5TtjWEXI/np4xcP/Gw9oWCb2XyWnqHYLDUwiKRoJBA1Q==, tarball: https://registry.npmjs.org/postcss-normalize-url/-/postcss-normalize-url-7.0.3.tgz} engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} peerDependencies: postcss: ^8.5.13 postcss-normalize-whitespace@5.1.1: - resolution: {integrity: sha512-83ZJ4t3NUDETIHTa3uEg6asWjSBYL5EdkVB0sDncx9ERzOKBVJIUeDO9RyA9Zwtig8El1d79HBp0JEi8wvGQnA==} + resolution: {integrity: sha512-83ZJ4t3NUDETIHTa3uEg6asWjSBYL5EdkVB0sDncx9ERzOKBVJIUeDO9RyA9Zwtig8El1d79HBp0JEi8wvGQnA==, tarball: https://registry.npmjs.org/postcss-normalize-whitespace/-/postcss-normalize-whitespace-5.1.1.tgz} engines: {node: ^10 || ^12 || >=14.0} peerDependencies: postcss: ^8.2.15 postcss-normalize-whitespace@6.0.2: - resolution: {integrity: sha512-sXZ2Nj1icbJOKmdjXVT9pnyHQKiSAyuNQHSgRCUgThn2388Y9cGVDR+E9J9iAYbSbLHI+UUwLVl1Wzco/zgv0Q==} + resolution: {integrity: sha512-sXZ2Nj1icbJOKmdjXVT9pnyHQKiSAyuNQHSgRCUgThn2388Y9cGVDR+E9J9iAYbSbLHI+UUwLVl1Wzco/zgv0Q==, tarball: https://registry.npmjs.org/postcss-normalize-whitespace/-/postcss-normalize-whitespace-6.0.2.tgz} engines: {node: ^14 || ^16 || >=18.0} peerDependencies: postcss: ^8.4.31 postcss-normalize-whitespace@7.0.3: - resolution: {integrity: sha512-FdHjjn+Ht5Z2ZRjNOmeCbNq6lq09sUYKpmlF/Aq0XjVNSLTL6fmHlA/3swN2wP2caY9GV/tjSDcIIyS7aN7W0A==} + resolution: {integrity: sha512-FdHjjn+Ht5Z2ZRjNOmeCbNq6lq09sUYKpmlF/Aq0XjVNSLTL6fmHlA/3swN2wP2caY9GV/tjSDcIIyS7aN7W0A==, tarball: https://registry.npmjs.org/postcss-normalize-whitespace/-/postcss-normalize-whitespace-7.0.3.tgz} engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} peerDependencies: postcss: ^8.5.13 postcss-ordered-values@5.1.3: - resolution: {integrity: sha512-9UO79VUhPwEkzbb3RNpqqghc6lcYej1aveQteWY+4POIwlqkYE21HKWaLDF6lWNuqCobEAyTovVhtI32Rbv2RQ==} + resolution: {integrity: sha512-9UO79VUhPwEkzbb3RNpqqghc6lcYej1aveQteWY+4POIwlqkYE21HKWaLDF6lWNuqCobEAyTovVhtI32Rbv2RQ==, tarball: https://registry.npmjs.org/postcss-ordered-values/-/postcss-ordered-values-5.1.3.tgz} engines: {node: ^10 || ^12 || >=14.0} peerDependencies: postcss: ^8.2.15 postcss-ordered-values@6.0.2: - resolution: {integrity: sha512-VRZSOB+JU32RsEAQrO94QPkClGPKJEL/Z9PCBImXMhIeK5KAYo6slP/hBYlLgrCjFxyqvn5VC81tycFEDBLG1Q==} + resolution: {integrity: sha512-VRZSOB+JU32RsEAQrO94QPkClGPKJEL/Z9PCBImXMhIeK5KAYo6slP/hBYlLgrCjFxyqvn5VC81tycFEDBLG1Q==, tarball: https://registry.npmjs.org/postcss-ordered-values/-/postcss-ordered-values-6.0.2.tgz} engines: {node: ^14 || ^16 || >=18.0} peerDependencies: postcss: ^8.4.31 postcss-ordered-values@7.0.4: - resolution: {integrity: sha512-nubSi49hDHQk4E8KIj+IbLY8Bg+8OcSUEhgyolgM+atnOvXjV7EjaR6bac4YGZoFyPa9mWoAF3EaYbWdFkKqVg==} + resolution: {integrity: sha512-nubSi49hDHQk4E8KIj+IbLY8Bg+8OcSUEhgyolgM+atnOvXjV7EjaR6bac4YGZoFyPa9mWoAF3EaYbWdFkKqVg==, tarball: https://registry.npmjs.org/postcss-ordered-values/-/postcss-ordered-values-7.0.4.tgz} engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} peerDependencies: postcss: ^8.5.13 postcss-reduce-initial@5.1.2: - resolution: {integrity: sha512-dE/y2XRaqAi6OvjzD22pjTUQ8eOfc6m/natGHgKFBK9DxFmIm69YmaRVQrGgFlEfc1HePIurY0TmDeROK05rIg==} + resolution: {integrity: sha512-dE/y2XRaqAi6OvjzD22pjTUQ8eOfc6m/natGHgKFBK9DxFmIm69YmaRVQrGgFlEfc1HePIurY0TmDeROK05rIg==, tarball: https://registry.npmjs.org/postcss-reduce-initial/-/postcss-reduce-initial-5.1.2.tgz} engines: {node: ^10 || ^12 || >=14.0} peerDependencies: postcss: ^8.2.15 postcss-reduce-initial@6.1.0: - resolution: {integrity: sha512-RarLgBK/CrL1qZags04oKbVbrrVK2wcxhvta3GCxrZO4zveibqbRPmm2VI8sSgCXwoUHEliRSbOfpR0b/VIoiw==} + resolution: {integrity: sha512-RarLgBK/CrL1qZags04oKbVbrrVK2wcxhvta3GCxrZO4zveibqbRPmm2VI8sSgCXwoUHEliRSbOfpR0b/VIoiw==, tarball: https://registry.npmjs.org/postcss-reduce-initial/-/postcss-reduce-initial-6.1.0.tgz} engines: {node: ^14 || ^16 || >=18.0} peerDependencies: postcss: ^8.4.31 postcss-reduce-initial@7.0.9: - resolution: {integrity: sha512-ztTNPdIxXTxtBcG03E9u8v44M4ElXbMIRT7pf2onlquGula0Y83nKKxqM22FA/hMgkfCjN7ohevkVlaNwI8iOQ==} + resolution: {integrity: sha512-ztTNPdIxXTxtBcG03E9u8v44M4ElXbMIRT7pf2onlquGula0Y83nKKxqM22FA/hMgkfCjN7ohevkVlaNwI8iOQ==, tarball: https://registry.npmjs.org/postcss-reduce-initial/-/postcss-reduce-initial-7.0.9.tgz} engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} peerDependencies: postcss: ^8.5.13 postcss-reduce-transforms@5.1.0: - resolution: {integrity: sha512-2fbdbmgir5AvpW9RLtdONx1QoYG2/EtqpNQbFASDlixBbAYuTcJ0dECwlqNqH7VbaUnEnh8SrxOe2sRIn24XyQ==} + resolution: {integrity: sha512-2fbdbmgir5AvpW9RLtdONx1QoYG2/EtqpNQbFASDlixBbAYuTcJ0dECwlqNqH7VbaUnEnh8SrxOe2sRIn24XyQ==, tarball: https://registry.npmjs.org/postcss-reduce-transforms/-/postcss-reduce-transforms-5.1.0.tgz} engines: {node: ^10 || ^12 || >=14.0} peerDependencies: postcss: ^8.2.15 postcss-reduce-transforms@6.0.2: - resolution: {integrity: sha512-sB+Ya++3Xj1WaT9+5LOOdirAxP7dJZms3GRcYheSPi1PiTMigsxHAdkrbItHxwYHr4kt1zL7mmcHstgMYT+aiA==} + resolution: {integrity: sha512-sB+Ya++3Xj1WaT9+5LOOdirAxP7dJZms3GRcYheSPi1PiTMigsxHAdkrbItHxwYHr4kt1zL7mmcHstgMYT+aiA==, tarball: https://registry.npmjs.org/postcss-reduce-transforms/-/postcss-reduce-transforms-6.0.2.tgz} engines: {node: ^14 || ^16 || >=18.0} peerDependencies: postcss: ^8.4.31 postcss-reduce-transforms@7.0.3: - resolution: {integrity: sha512-FXsnN9ZwcZTT8Yf8cAHA8qIGUXcX6WfLd9JoYhrdDfmvsVhhfqkkv7m4AC3rwFOfz+GzkUa87OCKF9dUcicd+g==} + resolution: {integrity: sha512-FXsnN9ZwcZTT8Yf8cAHA8qIGUXcX6WfLd9JoYhrdDfmvsVhhfqkkv7m4AC3rwFOfz+GzkUa87OCKF9dUcicd+g==, tarball: https://registry.npmjs.org/postcss-reduce-transforms/-/postcss-reduce-transforms-7.0.3.tgz} engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} peerDependencies: postcss: ^8.5.13 postcss-safe-parser@7.0.1: - resolution: {integrity: sha512-0AioNCJZ2DPYz5ABT6bddIqlhgwhpHZ/l65YAYo0BCIn0xiDpsnTHz0gnoTGk0OXZW0JRs+cDwL8u/teRdz+8A==} + resolution: {integrity: sha512-0AioNCJZ2DPYz5ABT6bddIqlhgwhpHZ/l65YAYo0BCIn0xiDpsnTHz0gnoTGk0OXZW0JRs+cDwL8u/teRdz+8A==, tarball: https://registry.npmjs.org/postcss-safe-parser/-/postcss-safe-parser-7.0.1.tgz} engines: {node: '>=18.0'} peerDependencies: postcss: ^8.4.31 postcss-selector-parser@6.0.10: - resolution: {integrity: sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w==} + resolution: {integrity: sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w==, tarball: https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.10.tgz} engines: {node: '>=4'} postcss-selector-parser@6.1.4: - resolution: {integrity: sha512-bIoJLOmjCO1S9XdY/DcnR5hJxvrDir1PbGChrzXG3vw0/FOliy/fA3dmdhQ441kah4gKv+TwckGzex6wNS5cnQ==} + resolution: {integrity: sha512-bIoJLOmjCO1S9XdY/DcnR5hJxvrDir1PbGChrzXG3vw0/FOliy/fA3dmdhQ441kah4gKv+TwckGzex6wNS5cnQ==, tarball: https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.4.tgz} engines: {node: '>=4'} postcss-selector-parser@7.1.4: - resolution: {integrity: sha512-HeP7D2wyhkR+XaK6v4W8oRF62Dsz4flyuczALJp61GckGm42u1saSSJ/0auvcBqxs3jMRFEcPK34At/0JBKdOg==} + resolution: {integrity: sha512-HeP7D2wyhkR+XaK6v4W8oRF62Dsz4flyuczALJp61GckGm42u1saSSJ/0auvcBqxs3jMRFEcPK34At/0JBKdOg==, tarball: https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.4.tgz} engines: {node: '>=4'} postcss-svgo@5.1.0: - resolution: {integrity: sha512-D75KsH1zm5ZrHyxPakAxJWtkyXew5qwS70v56exwvw542d9CRtTo78K0WeFxZB4G7JXKKMbEZtZayTGdIky/eA==} + resolution: {integrity: sha512-D75KsH1zm5ZrHyxPakAxJWtkyXew5qwS70v56exwvw542d9CRtTo78K0WeFxZB4G7JXKKMbEZtZayTGdIky/eA==, tarball: https://registry.npmjs.org/postcss-svgo/-/postcss-svgo-5.1.0.tgz} engines: {node: ^10 || ^12 || >=14.0} peerDependencies: postcss: ^8.2.15 postcss-svgo@6.0.3: - resolution: {integrity: sha512-dlrahRmxP22bX6iKEjOM+c8/1p+81asjKT+V5lrgOH944ryx/OHpclnIbGsKVd3uWOXFLYJwCVf0eEkJGvO96g==} + resolution: {integrity: sha512-dlrahRmxP22bX6iKEjOM+c8/1p+81asjKT+V5lrgOH944ryx/OHpclnIbGsKVd3uWOXFLYJwCVf0eEkJGvO96g==, tarball: https://registry.npmjs.org/postcss-svgo/-/postcss-svgo-6.0.3.tgz} engines: {node: ^14 || ^16 || >= 18} peerDependencies: postcss: ^8.4.31 postcss-svgo@7.1.3: - resolution: {integrity: sha512-2QfoFOYMcj8lwcVEf9WeTlkVIAm7u2QvOEhMzkQU3KUhhGX/l8hVV9EtjMv4iq3E9iI3OeeMN0YoMLbGusuigw==} + resolution: {integrity: sha512-2QfoFOYMcj8lwcVEf9WeTlkVIAm7u2QvOEhMzkQU3KUhhGX/l8hVV9EtjMv4iq3E9iI3OeeMN0YoMLbGusuigw==, tarball: https://registry.npmjs.org/postcss-svgo/-/postcss-svgo-7.1.3.tgz} engines: {node: ^18.12.0 || ^20.9.0 || >= 18} peerDependencies: postcss: ^8.5.13 postcss-unique-selectors@5.1.1: - resolution: {integrity: sha512-5JiODlELrz8L2HwxfPnhOWZYWDxVHWL83ufOv84NrcgipI7TaeRsatAhK4Tr2/ZiYldpK/wBvw5BD3qfaK96GA==} + resolution: {integrity: sha512-5JiODlELrz8L2HwxfPnhOWZYWDxVHWL83ufOv84NrcgipI7TaeRsatAhK4Tr2/ZiYldpK/wBvw5BD3qfaK96GA==, tarball: https://registry.npmjs.org/postcss-unique-selectors/-/postcss-unique-selectors-5.1.1.tgz} engines: {node: ^10 || ^12 || >=14.0} peerDependencies: postcss: ^8.2.15 postcss-unique-selectors@6.0.4: - resolution: {integrity: sha512-K38OCaIrO8+PzpArzkLKB42dSARtC2tmG6PvD4b1o1Q2E9Os8jzfWFfSy/rixsHwohtsDdFtAWGjFVFUdwYaMg==} + resolution: {integrity: sha512-K38OCaIrO8+PzpArzkLKB42dSARtC2tmG6PvD4b1o1Q2E9Os8jzfWFfSy/rixsHwohtsDdFtAWGjFVFUdwYaMg==, tarball: https://registry.npmjs.org/postcss-unique-selectors/-/postcss-unique-selectors-6.0.4.tgz} engines: {node: ^14 || ^16 || >=18.0} peerDependencies: postcss: ^8.4.31 postcss-unique-selectors@7.0.7: - resolution: {integrity: sha512-d+sCkaRnSefghOUdH8CMJZV9yUQhj2ojpe8Nw/lA+LV1UOfeleGkLTl6XdCFFSai9UJ+DJPb69FFuqthXYsY8w==} + resolution: {integrity: sha512-d+sCkaRnSefghOUdH8CMJZV9yUQhj2ojpe8Nw/lA+LV1UOfeleGkLTl6XdCFFSai9UJ+DJPb69FFuqthXYsY8w==, tarball: https://registry.npmjs.org/postcss-unique-selectors/-/postcss-unique-selectors-7.0.7.tgz} engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} peerDependencies: postcss: ^8.5.13 postcss-value-parser@4.2.0: - resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==} + resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==, tarball: https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz} postcss@8.4.31: - resolution: {integrity: sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==} + resolution: {integrity: sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==, tarball: https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz} engines: {node: ^10 || ^12 || >=14} postcss@8.5.19: - resolution: {integrity: sha512-Mz8SaolMd8nB+G13WkORcxQKHZ/NE4xXevtkJHVuG+guo9/wYKlIMTKAqGdEmYOXR2ijPjTYNHssizdaVSUNdQ==} + resolution: {integrity: sha512-Mz8SaolMd8nB+G13WkORcxQKHZ/NE4xXevtkJHVuG+guo9/wYKlIMTKAqGdEmYOXR2ijPjTYNHssizdaVSUNdQ==, tarball: https://registry.npmjs.org/postcss/-/postcss-8.5.19.tgz} engines: {node: ^10 || ^12 || >=14} postcss@8.5.6: - resolution: {integrity: sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==} + resolution: {integrity: sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==, tarball: https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz} engines: {node: ^10 || ^12 || >=14} powershell-utils@0.1.0: - resolution: {integrity: sha512-dM0jVuXJPsDN6DvRpea484tCUaMiXWjuCn++HGTqUWzGDjv5tZkEZldAJ/UMlqRYGFrD/etByo4/xOuC/snX2A==} + resolution: {integrity: sha512-dM0jVuXJPsDN6DvRpea484tCUaMiXWjuCn++HGTqUWzGDjv5tZkEZldAJ/UMlqRYGFrD/etByo4/xOuC/snX2A==, tarball: https://registry.npmjs.org/powershell-utils/-/powershell-utils-0.1.0.tgz} engines: {node: '>=20'} prelude-ls@1.2.1: - resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} + resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==, tarball: https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz} engines: {node: '>= 0.8.0'} prettier-linter-helpers@1.0.1: - resolution: {integrity: sha512-SxToR7P8Y2lWmv/kTzVLC1t/GDI2WGjMwNhLLE9qtH8Q13C+aEmuRlzDst4Up4s0Wc8sF2M+J57iB3cMLqftfg==} + resolution: {integrity: sha512-SxToR7P8Y2lWmv/kTzVLC1t/GDI2WGjMwNhLLE9qtH8Q13C+aEmuRlzDst4Up4s0Wc8sF2M+J57iB3cMLqftfg==, tarball: https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.1.tgz} engines: {node: '>=6.0.0'} prettier-plugin-tailwindcss@0.8.0: - resolution: {integrity: sha512-V8ITGH87yuBDF6JpEZTOVlUz/saAwqb8f3HRgUj8Lh+tGCcrmorhsLpYqzygwFwK0PE2Ib6Mv3M7T/uE2tZV1g==} + resolution: {integrity: sha512-V8ITGH87yuBDF6JpEZTOVlUz/saAwqb8f3HRgUj8Lh+tGCcrmorhsLpYqzygwFwK0PE2Ib6Mv3M7T/uE2tZV1g==, tarball: https://registry.npmjs.org/prettier-plugin-tailwindcss/-/prettier-plugin-tailwindcss-0.8.0.tgz} engines: {node: '>=20.19'} peerDependencies: '@ianvs/prettier-plugin-sort-imports': '*' @@ -13030,35 +13093,35 @@ packages: optional: true prettier@3.8.1: - resolution: {integrity: sha512-UOnG6LftzbdaHZcKoPFtOcCKztrQ57WkHDeRD9t/PTQtmT0NHSeWWepj6pS0z/N7+08BHFDQVUrfmfMRcZwbMg==} + resolution: {integrity: sha512-UOnG6LftzbdaHZcKoPFtOcCKztrQ57WkHDeRD9t/PTQtmT0NHSeWWepj6pS0z/N7+08BHFDQVUrfmfMRcZwbMg==, tarball: https://registry.npmjs.org/prettier/-/prettier-3.8.1.tgz} engines: {node: '>=14'} hasBin: true pretty-format@27.5.1: - resolution: {integrity: sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==} + resolution: {integrity: sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==, tarball: https://registry.npmjs.org/pretty-format/-/pretty-format-27.5.1.tgz} engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} pretty-format@29.7.0: - resolution: {integrity: sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==} + resolution: {integrity: sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==, tarball: https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} pretty-format@30.2.0: - resolution: {integrity: sha512-9uBdv/B4EefsuAL+pWqueZyZS2Ba+LxfFeQ9DN14HU4bN8bhaxKdkpjpB6fs9+pSjIBu+FXQHImEg8j/Lw0+vA==} + resolution: {integrity: sha512-9uBdv/B4EefsuAL+pWqueZyZS2Ba+LxfFeQ9DN14HU4bN8bhaxKdkpjpB6fs9+pSjIBu+FXQHImEg8j/Lw0+vA==, tarball: https://registry.npmjs.org/pretty-format/-/pretty-format-30.2.0.tgz} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} pretty-format@30.4.1: - resolution: {integrity: sha512-K6KiKMHTL4jjX4u3Kir2EW07nRfcqVTXIImx50wbjHQTcZPgg+gjVeNTIT3l3L1Rd4UefxfogquC9J37SoFyyw==} + resolution: {integrity: sha512-K6KiKMHTL4jjX4u3Kir2EW07nRfcqVTXIImx50wbjHQTcZPgg+gjVeNTIT3l3L1Rd4UefxfogquC9J37SoFyyw==, tarball: https://registry.npmjs.org/pretty-format/-/pretty-format-30.4.1.tgz} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} pretty-ms@9.3.0: - resolution: {integrity: sha512-gjVS5hOP+M3wMm5nmNOucbIrqudzs9v/57bWRHQWLYklXqoXKrVfYW2W9+glfGsqtPgpiz5WwyEEB+ksXIx3gQ==} + resolution: {integrity: sha512-gjVS5hOP+M3wMm5nmNOucbIrqudzs9v/57bWRHQWLYklXqoXKrVfYW2W9+glfGsqtPgpiz5WwyEEB+ksXIx3gQ==, tarball: https://registry.npmjs.org/pretty-ms/-/pretty-ms-9.3.0.tgz} engines: {node: '>=18'} primeicons@7.0.0: - resolution: {integrity: sha512-jK3Et9UzwzTsd6tzl2RmwrVY/b8raJ3QZLzoDACj+oTJ0oX7L9Hy+XnVwgo4QVKlKpnP/Ur13SXV/pVh4LzaDw==} + resolution: {integrity: sha512-jK3Et9UzwzTsd6tzl2RmwrVY/b8raJ3QZLzoDACj+oTJ0oX7L9Hy+XnVwgo4QVKlKpnP/Ur13SXV/pVh4LzaDw==, tarball: https://registry.npmjs.org/primeicons/-/primeicons-7.0.0.tgz} primeng@21.1.3: - resolution: {integrity: sha512-PDL76kiHXH3CS5YuEIb5kv2OXgXDA5mVBCxy6QlJVTGa518rKe/dsHVLJYvhTjhwLtC2EUnBJxOZMxKlzc/fDg==} + resolution: {integrity: sha512-PDL76kiHXH3CS5YuEIb5kv2OXgXDA5mVBCxy6QlJVTGa518rKe/dsHVLJYvhTjhwLtC2EUnBJxOZMxKlzc/fDg==, tarball: https://registry.npmjs.org/primeng/-/primeng-21.1.3.tgz} peerDependencies: '@angular/cdk': ^21.0.0 '@angular/common': ^21.0.0 @@ -13069,94 +13132,94 @@ packages: rxjs: ^6.0.0 || ^7.8.1 prismjs@1.30.0: - resolution: {integrity: sha512-DEvV2ZF2r2/63V+tK8hQvrR2ZGn10srHbXviTlcv7Kpzw8jWiNTqbVgjO3IY8RxrrOUF8VPMQQFysYYYv0YZxw==} + resolution: {integrity: sha512-DEvV2ZF2r2/63V+tK8hQvrR2ZGn10srHbXviTlcv7Kpzw8jWiNTqbVgjO3IY8RxrrOUF8VPMQQFysYYYv0YZxw==, tarball: https://registry.npmjs.org/prismjs/-/prismjs-1.30.0.tgz} engines: {node: '>=6'} proc-log@3.0.0: - resolution: {integrity: sha512-++Vn7NS4Xf9NacaU9Xq3URUuqZETPsf8L4j5/ckhaRYsfPeRyzGw+iDjFhV/Jr3uNmTvvddEJFWh5R1gRgUH8A==} + resolution: {integrity: sha512-++Vn7NS4Xf9NacaU9Xq3URUuqZETPsf8L4j5/ckhaRYsfPeRyzGw+iDjFhV/Jr3uNmTvvddEJFWh5R1gRgUH8A==, tarball: https://registry.npmjs.org/proc-log/-/proc-log-3.0.0.tgz} engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} proc-log@7.0.0: - resolution: {integrity: sha512-FYgfaA69XZ93zaXLoMNQ+ViDXGGBgR8aLh03txzcFhV+9xOXx7+8DLCULrKKpR9+GsH9ZfHm82aSUPpozX0Ztg==} + resolution: {integrity: sha512-FYgfaA69XZ93zaXLoMNQ+ViDXGGBgR8aLh03txzcFhV+9xOXx7+8DLCULrKKpR9+GsH9ZfHm82aSUPpozX0Ztg==, tarball: https://registry.npmjs.org/proc-log/-/proc-log-7.0.0.tgz} engines: {node: ^22.22.2 || ^24.15.0 || >=26.0.0} process-nextick-args@2.0.1: - resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==} + resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==, tarball: https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz} promise.series@0.2.0: - resolution: {integrity: sha512-VWQJyU2bcDTgZw8kpfBpB/ejZASlCrzwz5f2hjb/zlujOEB4oeiAhHygAWq8ubsX2GVkD4kCU5V2dwOTaCY5EQ==} + resolution: {integrity: sha512-VWQJyU2bcDTgZw8kpfBpB/ejZASlCrzwz5f2hjb/zlujOEB4oeiAhHygAWq8ubsX2GVkD4kCU5V2dwOTaCY5EQ==, tarball: https://registry.npmjs.org/promise.series/-/promise.series-0.2.0.tgz} engines: {node: '>=0.12'} prop-types@15.8.1: - resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==} + resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==, tarball: https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz} prosemirror-changeset@2.4.1: - resolution: {integrity: sha512-96WBLhOaYhJ+kPhLg3uW359Tz6I/MfcrQfL4EGv4SrcqKEMC1gmoGrXHecPE8eOwTVCJ4IwgfzM8fFad25wNfw==} + resolution: {integrity: sha512-96WBLhOaYhJ+kPhLg3uW359Tz6I/MfcrQfL4EGv4SrcqKEMC1gmoGrXHecPE8eOwTVCJ4IwgfzM8fFad25wNfw==, tarball: https://registry.npmjs.org/prosemirror-changeset/-/prosemirror-changeset-2.4.1.tgz} prosemirror-collab@1.3.1: - resolution: {integrity: sha512-4SnynYR9TTYaQVXd/ieUvsVV4PDMBzrq2xPUWutHivDuOshZXqQ5rGbZM84HEaXKbLdItse7weMGOUdDVcLKEQ==} + resolution: {integrity: sha512-4SnynYR9TTYaQVXd/ieUvsVV4PDMBzrq2xPUWutHivDuOshZXqQ5rGbZM84HEaXKbLdItse7weMGOUdDVcLKEQ==, tarball: https://registry.npmjs.org/prosemirror-collab/-/prosemirror-collab-1.3.1.tgz} prosemirror-commands@1.7.1: - resolution: {integrity: sha512-rT7qZnQtx5c0/y/KlYaGvtG411S97UaL6gdp6RIZ23DLHanMYLyfGBV5DtSnZdthQql7W+lEVbpSfwtO8T+L2w==} + resolution: {integrity: sha512-rT7qZnQtx5c0/y/KlYaGvtG411S97UaL6gdp6RIZ23DLHanMYLyfGBV5DtSnZdthQql7W+lEVbpSfwtO8T+L2w==, tarball: https://registry.npmjs.org/prosemirror-commands/-/prosemirror-commands-1.7.1.tgz} prosemirror-dropcursor@1.8.3: - resolution: {integrity: sha512-FoYbsJR8gK+DGlqhNoE29Loa38eIZPzQRIb1VMaDNBoo4OLP6vVof/jR8qFY/6XvUd6Dhug8MDCHl2a/h8RTfQ==} + resolution: {integrity: sha512-FoYbsJR8gK+DGlqhNoE29Loa38eIZPzQRIb1VMaDNBoo4OLP6vVof/jR8qFY/6XvUd6Dhug8MDCHl2a/h8RTfQ==, tarball: https://registry.npmjs.org/prosemirror-dropcursor/-/prosemirror-dropcursor-1.8.3.tgz} prosemirror-gapcursor@1.4.1: - resolution: {integrity: sha512-pMdYaEnjNMSwl11yjEGtgTmLkR08m/Vl+Jj443167p9eB3HVQKhYCc4gmHVDsLPODfZfjr/MmirsdyZziXbQKw==} + resolution: {integrity: sha512-pMdYaEnjNMSwl11yjEGtgTmLkR08m/Vl+Jj443167p9eB3HVQKhYCc4gmHVDsLPODfZfjr/MmirsdyZziXbQKw==, tarball: https://registry.npmjs.org/prosemirror-gapcursor/-/prosemirror-gapcursor-1.4.1.tgz} prosemirror-history@1.5.0: - resolution: {integrity: sha512-zlzTiH01eKA55UAf1MEjtssJeHnGxO0j4K4Dpx+gnmX9n+SHNlDqI2oO1Kv1iPN5B1dm5fsljCfqKF9nFL6HRg==} + resolution: {integrity: sha512-zlzTiH01eKA55UAf1MEjtssJeHnGxO0j4K4Dpx+gnmX9n+SHNlDqI2oO1Kv1iPN5B1dm5fsljCfqKF9nFL6HRg==, tarball: https://registry.npmjs.org/prosemirror-history/-/prosemirror-history-1.5.0.tgz} prosemirror-inputrules@1.5.1: - resolution: {integrity: sha512-7wj4uMjKaXWAQ1CDgxNzNtR9AlsuwzHfdFH1ygEHA2KHF2DOEaXl1CJfNPAKCg9qNEh4rum975QLaCiQPyY6Fw==} + resolution: {integrity: sha512-7wj4uMjKaXWAQ1CDgxNzNtR9AlsuwzHfdFH1ygEHA2KHF2DOEaXl1CJfNPAKCg9qNEh4rum975QLaCiQPyY6Fw==, tarball: https://registry.npmjs.org/prosemirror-inputrules/-/prosemirror-inputrules-1.5.1.tgz} prosemirror-keymap@1.2.3: - resolution: {integrity: sha512-4HucRlpiLd1IPQQXNqeo81BGtkY8Ai5smHhKW9jjPKRc2wQIxksg7Hl1tTI2IfT2B/LgX6bfYvXxEpJl7aKYKw==} + resolution: {integrity: sha512-4HucRlpiLd1IPQQXNqeo81BGtkY8Ai5smHhKW9jjPKRc2wQIxksg7Hl1tTI2IfT2B/LgX6bfYvXxEpJl7aKYKw==, tarball: https://registry.npmjs.org/prosemirror-keymap/-/prosemirror-keymap-1.2.3.tgz} prosemirror-markdown@1.13.5: - resolution: {integrity: sha512-ac8trNQ01ybKDRTcfUc56LZufG3oYyU4N25qSXgp8dS0U4JtzzCj7oQlKu5v09VSmS5IseYoQ2yDkTbo7f7D8Q==} + resolution: {integrity: sha512-ac8trNQ01ybKDRTcfUc56LZufG3oYyU4N25qSXgp8dS0U4JtzzCj7oQlKu5v09VSmS5IseYoQ2yDkTbo7f7D8Q==, tarball: https://registry.npmjs.org/prosemirror-markdown/-/prosemirror-markdown-1.13.5.tgz} prosemirror-menu@1.3.2: - resolution: {integrity: sha512-6VgUJTYod0nMBlCaYJGhXGLu7Gt4AvcwcOq0YfJCY/6Uh+3S7UsWhpy6rJFCBFOmonq1hD8KyWOtZhkppd4YPg==} + resolution: {integrity: sha512-6VgUJTYod0nMBlCaYJGhXGLu7Gt4AvcwcOq0YfJCY/6Uh+3S7UsWhpy6rJFCBFOmonq1hD8KyWOtZhkppd4YPg==, tarball: https://registry.npmjs.org/prosemirror-menu/-/prosemirror-menu-1.3.2.tgz} prosemirror-model@1.25.4: - resolution: {integrity: sha512-PIM7E43PBxKce8OQeezAs9j4TP+5yDpZVbuurd1h5phUxEKIu+G2a+EUZzIC5nS1mJktDJWzbqS23n1tsAf5QA==} + resolution: {integrity: sha512-PIM7E43PBxKce8OQeezAs9j4TP+5yDpZVbuurd1h5phUxEKIu+G2a+EUZzIC5nS1mJktDJWzbqS23n1tsAf5QA==, tarball: https://registry.npmjs.org/prosemirror-model/-/prosemirror-model-1.25.4.tgz} prosemirror-schema-basic@1.2.4: - resolution: {integrity: sha512-ELxP4TlX3yr2v5rM7Sb70SqStq5NvI15c0j9j/gjsrO5vaw+fnnpovCLEGIcpeGfifkuqJwl4fon6b+KdrODYQ==} + resolution: {integrity: sha512-ELxP4TlX3yr2v5rM7Sb70SqStq5NvI15c0j9j/gjsrO5vaw+fnnpovCLEGIcpeGfifkuqJwl4fon6b+KdrODYQ==, tarball: https://registry.npmjs.org/prosemirror-schema-basic/-/prosemirror-schema-basic-1.2.4.tgz} prosemirror-schema-list@1.5.1: - resolution: {integrity: sha512-927lFx/uwyQaGwJxLWCZRkjXG0p48KpMj6ueoYiu4JX05GGuGcgzAy62dfiV8eFZftgyBUvLx76RsMe20fJl+Q==} + resolution: {integrity: sha512-927lFx/uwyQaGwJxLWCZRkjXG0p48KpMj6ueoYiu4JX05GGuGcgzAy62dfiV8eFZftgyBUvLx76RsMe20fJl+Q==, tarball: https://registry.npmjs.org/prosemirror-schema-list/-/prosemirror-schema-list-1.5.1.tgz} prosemirror-state@1.4.4: - resolution: {integrity: sha512-6jiYHH2CIGbCfnxdHbXZ12gySFY/fz/ulZE333G6bPqIZ4F+TXo9ifiR86nAHpWnfoNjOb3o5ESi7J8Uz1jXHw==} + resolution: {integrity: sha512-6jiYHH2CIGbCfnxdHbXZ12gySFY/fz/ulZE333G6bPqIZ4F+TXo9ifiR86nAHpWnfoNjOb3o5ESi7J8Uz1jXHw==, tarball: https://registry.npmjs.org/prosemirror-state/-/prosemirror-state-1.4.4.tgz} prosemirror-tables@1.8.5: - resolution: {integrity: sha512-V/0cDCsHKHe/tfWkeCmthNUcEp1IVO3p6vwN8XtwE9PZQLAZJigbw3QoraAdfJPir4NKJtNvOB8oYGKRl+t0Dw==} + resolution: {integrity: sha512-V/0cDCsHKHe/tfWkeCmthNUcEp1IVO3p6vwN8XtwE9PZQLAZJigbw3QoraAdfJPir4NKJtNvOB8oYGKRl+t0Dw==, tarball: https://registry.npmjs.org/prosemirror-tables/-/prosemirror-tables-1.8.5.tgz} prosemirror-trailing-node@3.0.0: - resolution: {integrity: sha512-xiun5/3q0w5eRnGYfNlW1uU9W6x5MoFKWwq/0TIRgt09lv7Hcser2QYV8t4muXbEr+Fwo0geYn79Xs4GKywrRQ==} + resolution: {integrity: sha512-xiun5/3q0w5eRnGYfNlW1uU9W6x5MoFKWwq/0TIRgt09lv7Hcser2QYV8t4muXbEr+Fwo0geYn79Xs4GKywrRQ==, tarball: https://registry.npmjs.org/prosemirror-trailing-node/-/prosemirror-trailing-node-3.0.0.tgz} peerDependencies: prosemirror-model: ^1.22.1 prosemirror-state: ^1.4.2 prosemirror-view: ^1.33.8 prosemirror-transform@1.12.0: - resolution: {integrity: sha512-GxboyN4AMIsoHNtz5uf2r2Ru551i5hWeCMD6E2Ib4Eogqoub0NflniaBPVQ4MrGE5yZ8JV9tUHg9qcZTTrcN4w==} + resolution: {integrity: sha512-GxboyN4AMIsoHNtz5uf2r2Ru551i5hWeCMD6E2Ib4Eogqoub0NflniaBPVQ4MrGE5yZ8JV9tUHg9qcZTTrcN4w==, tarball: https://registry.npmjs.org/prosemirror-transform/-/prosemirror-transform-1.12.0.tgz} prosemirror-view@1.41.8: - resolution: {integrity: sha512-TnKDdohEatgyZNGCDWIdccOHXhYloJwbwU+phw/a23KBvJIR9lWQWW7WHHK3vBdOLDNuF7TaX98GObUZOWkOnA==} + resolution: {integrity: sha512-TnKDdohEatgyZNGCDWIdccOHXhYloJwbwU+phw/a23KBvJIR9lWQWW7WHHK3vBdOLDNuF7TaX98GObUZOWkOnA==, tarball: https://registry.npmjs.org/prosemirror-view/-/prosemirror-view-1.41.8.tgz} proto-list@1.2.4: - resolution: {integrity: sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==} + resolution: {integrity: sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==, tarball: https://registry.npmjs.org/proto-list/-/proto-list-1.2.4.tgz} proxy-addr@2.0.7: - resolution: {integrity: sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==} + resolution: {integrity: sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==, tarball: https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz} engines: {node: '>= 0.10'} proxy-agent-negotiate@1.1.0: - resolution: {integrity: sha512-N8IBcM3UgCVzz2L2Lqv8DVntDnnC8/hiV4nEDUPkqq72TPUgYWjQc+bdZlBPZK9LzPAvOY//gAt0S0DApoOXWQ==} + resolution: {integrity: sha512-N8IBcM3UgCVzz2L2Lqv8DVntDnnC8/hiV4nEDUPkqq72TPUgYWjQc+bdZlBPZK9LzPAvOY//gAt0S0DApoOXWQ==, tarball: https://registry.npmjs.org/proxy-agent-negotiate/-/proxy-agent-negotiate-1.1.0.tgz} engines: {node: '>= 20'} peerDependencies: kerberos: ^2.0.0 @@ -13165,473 +13228,473 @@ packages: optional: true proxy-from-env@2.1.0: - resolution: {integrity: sha512-cJ+oHTW1VAEa8cJslgmUZrc+sjRKgAKl3Zyse6+PV38hZe/V6Z14TbCuXcan9F9ghlz4QrFr2c92TNF82UkYHA==} + resolution: {integrity: sha512-cJ+oHTW1VAEa8cJslgmUZrc+sjRKgAKl3Zyse6+PV38hZe/V6Z14TbCuXcan9F9ghlz4QrFr2c92TNF82UkYHA==, tarball: https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-2.1.0.tgz} engines: {node: '>=10'} prr@1.0.1: - resolution: {integrity: sha512-yPw4Sng1gWghHQWj0B3ZggWUm4qVbPwPFcRG8KyxiU7J2OHFSoEHKS+EZ3fv5l1t9CyCiop6l/ZYeWbrgoQejw==} + resolution: {integrity: sha512-yPw4Sng1gWghHQWj0B3ZggWUm4qVbPwPFcRG8KyxiU7J2OHFSoEHKS+EZ3fv5l1t9CyCiop6l/ZYeWbrgoQejw==, tarball: https://registry.npmjs.org/prr/-/prr-1.0.1.tgz} psl@1.15.0: - resolution: {integrity: sha512-JZd3gMVBAVQkSs6HdNZo9Sdo0LNcQeMNP3CozBJb3JYC/QUYZTnKxP+f8oWRX4rHP5EurWxqAHTSwUCjlNKa1w==} + resolution: {integrity: sha512-JZd3gMVBAVQkSs6HdNZo9Sdo0LNcQeMNP3CozBJb3JYC/QUYZTnKxP+f8oWRX4rHP5EurWxqAHTSwUCjlNKa1w==, tarball: https://registry.npmjs.org/psl/-/psl-1.15.0.tgz} punycode.js@2.3.1: - resolution: {integrity: sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA==} + resolution: {integrity: sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA==, tarball: https://registry.npmjs.org/punycode.js/-/punycode.js-2.3.1.tgz} engines: {node: '>=6'} punycode@2.3.1: - resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} + resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==, tarball: https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz} engines: {node: '>=6'} pure-rand@7.0.1: - resolution: {integrity: sha512-oTUZM/NAZS8p7ANR3SHh30kXB+zK2r2BPcEn/awJIbOvq82WoMN4p62AWWp3Hhw50G0xMsw1mhIBLqHw64EcNQ==} + resolution: {integrity: sha512-oTUZM/NAZS8p7ANR3SHh30kXB+zK2r2BPcEn/awJIbOvq82WoMN4p62AWWp3Hhw50G0xMsw1mhIBLqHw64EcNQ==, tarball: https://registry.npmjs.org/pure-rand/-/pure-rand-7.0.1.tgz} pvtsutils@1.3.6: - resolution: {integrity: sha512-PLgQXQ6H2FWCaeRak8vvk1GW462lMxB5s3Jm673N82zI4vqtVUPuZdffdZbPDFRoU8kAhItWFtPCWiPpp4/EDg==} + resolution: {integrity: sha512-PLgQXQ6H2FWCaeRak8vvk1GW462lMxB5s3Jm673N82zI4vqtVUPuZdffdZbPDFRoU8kAhItWFtPCWiPpp4/EDg==, tarball: https://registry.npmjs.org/pvtsutils/-/pvtsutils-1.3.6.tgz} pvutils@1.1.5: - resolution: {integrity: sha512-KTqnxsgGiQ6ZAzZCVlJH5eOjSnvlyEgx1m8bkRJfOhmGRqfo5KLvmAlACQkrjEtOQ4B7wF9TdSLIs9O90MX9xA==} + resolution: {integrity: sha512-KTqnxsgGiQ6ZAzZCVlJH5eOjSnvlyEgx1m8bkRJfOhmGRqfo5KLvmAlACQkrjEtOQ4B7wF9TdSLIs9O90MX9xA==, tarball: https://registry.npmjs.org/pvutils/-/pvutils-1.1.5.tgz} engines: {node: '>=16.0.0'} qs@6.15.3: - resolution: {integrity: sha512-O9gl3zCl5h5blw1KGUzQKhA5oUXSl8rwUIM5o0S3nCXMliSvy5Dzx7/DJcI+SwgICv+IneSZwhBh1oSyEHA71A==} + resolution: {integrity: sha512-O9gl3zCl5h5blw1KGUzQKhA5oUXSl8rwUIM5o0S3nCXMliSvy5Dzx7/DJcI+SwgICv+IneSZwhBh1oSyEHA71A==, tarball: https://registry.npmjs.org/qs/-/qs-6.15.3.tgz} engines: {node: '>=0.6'} quansync@0.2.11: - resolution: {integrity: sha512-AifT7QEbW9Nri4tAwR5M/uzpBuqfZf+zwaEM/QkzEjj7NBuFD2rBuy0K3dE+8wltbezDV7JMA0WfnCPYRSYbXA==} + resolution: {integrity: sha512-AifT7QEbW9Nri4tAwR5M/uzpBuqfZf+zwaEM/QkzEjj7NBuFD2rBuy0K3dE+8wltbezDV7JMA0WfnCPYRSYbXA==, tarball: https://registry.npmjs.org/quansync/-/quansync-0.2.11.tgz} querystring@0.2.1: - resolution: {integrity: sha512-wkvS7mL/JMugcup3/rMitHmd9ecIGd2lhFhK9N3UUQ450h66d1r3Y9nvXzQAW1Lq+wyx61k/1pfKS5KuKiyEbg==} + resolution: {integrity: sha512-wkvS7mL/JMugcup3/rMitHmd9ecIGd2lhFhK9N3UUQ450h66d1r3Y9nvXzQAW1Lq+wyx61k/1pfKS5KuKiyEbg==, tarball: https://registry.npmjs.org/querystring/-/querystring-0.2.1.tgz} engines: {node: '>=0.4.x'} deprecated: The querystring API is considered Legacy. new code should use the URLSearchParams API instead. querystringify@2.2.0: - resolution: {integrity: sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==} + resolution: {integrity: sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==, tarball: https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz} queue-microtask@1.2.3: - resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} + resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==, tarball: https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz} rambda@9.4.2: - resolution: {integrity: sha512-++euMfxnl7OgaEKwXh9QqThOjMeta2HH001N1v4mYQzBjJBnmXBh2BCK6dZAbICFVXOFUVD3xFG0R3ZPU0mxXw==} + resolution: {integrity: sha512-++euMfxnl7OgaEKwXh9QqThOjMeta2HH001N1v4mYQzBjJBnmXBh2BCK6dZAbICFVXOFUVD3xFG0R3ZPU0mxXw==, tarball: https://registry.npmjs.org/rambda/-/rambda-9.4.2.tgz} randombytes@2.1.0: - resolution: {integrity: sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==} + resolution: {integrity: sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==, tarball: https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz} range-parser@1.2.1: - resolution: {integrity: sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==} + resolution: {integrity: sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==, tarball: https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz} engines: {node: '>= 0.6'} range-parser@1.3.0: - resolution: {integrity: sha512-hek2mFQpPuI4E1BBKrSto+BU3e3x4xuarsbiwr3+lf7p44juvFMV0XFWQAP3xUyqXA4RrXLIoaSUGbSt056ZMw==} + resolution: {integrity: sha512-hek2mFQpPuI4E1BBKrSto+BU3e3x4xuarsbiwr3+lf7p44juvFMV0XFWQAP3xUyqXA4RrXLIoaSUGbSt056ZMw==, tarball: https://registry.npmjs.org/range-parser/-/range-parser-1.3.0.tgz} engines: {node: '>= 0.6'} raw-body@2.5.3: - resolution: {integrity: sha512-s4VSOf6yN0rvbRZGxs8Om5CWj6seneMwK3oDb4lWDH0UPhWcxwOWw5+qk24bxq87szX1ydrwylIOp2uG1ojUpA==} + resolution: {integrity: sha512-s4VSOf6yN0rvbRZGxs8Om5CWj6seneMwK3oDb4lWDH0UPhWcxwOWw5+qk24bxq87szX1ydrwylIOp2uG1ojUpA==, tarball: https://registry.npmjs.org/raw-body/-/raw-body-2.5.3.tgz} engines: {node: '>= 0.8'} raw-body@3.0.2: - resolution: {integrity: sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA==} + resolution: {integrity: sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA==, tarball: https://registry.npmjs.org/raw-body/-/raw-body-3.0.2.tgz} engines: {node: '>= 0.10'} react-dom@18.3.1: - resolution: {integrity: sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==} + resolution: {integrity: sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==, tarball: https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz} peerDependencies: react: ^18.3.1 react-error-boundary@3.1.4: - resolution: {integrity: sha512-uM9uPzZJTF6wRQORmSrvOIgt4lJ9MC1sNgEOj2XGsDTRE4kmpWxg7ENK9EWNKJRMAOY9z0MuF4yIfl6gp4sotA==} + resolution: {integrity: sha512-uM9uPzZJTF6wRQORmSrvOIgt4lJ9MC1sNgEOj2XGsDTRE4kmpWxg7ENK9EWNKJRMAOY9z0MuF4yIfl6gp4sotA==, tarball: https://registry.npmjs.org/react-error-boundary/-/react-error-boundary-3.1.4.tgz} engines: {node: '>=10', npm: '>=6'} peerDependencies: react: '>=16.13.1' react-is@16.13.1: - resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==} + resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==, tarball: https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz} react-is@17.0.2: - resolution: {integrity: sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==} + resolution: {integrity: sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==, tarball: https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz} react-is@18.3.1: - resolution: {integrity: sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==} + resolution: {integrity: sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==, tarball: https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz} react-is@19.2.7: - resolution: {integrity: sha512-kZFnouyVv7eP/Phmrlo9FK+zcAdriZJvzxXHF1Sl1P377WSGe2G/JxVolhTrB/jeV47lKImhNUsijjHAAbcl/A==} + resolution: {integrity: sha512-kZFnouyVv7eP/Phmrlo9FK+zcAdriZJvzxXHF1Sl1P377WSGe2G/JxVolhTrB/jeV47lKImhNUsijjHAAbcl/A==, tarball: https://registry.npmjs.org/react-is/-/react-is-19.2.7.tgz} react-refresh@0.18.0: - resolution: {integrity: sha512-QgT5//D3jfjJb6Gsjxv0Slpj23ip+HtOpnNgnb2S5zU3CB26G/IDPGoy4RJB42wzFE46DRsstbW6tKHoKbhAxw==} + resolution: {integrity: sha512-QgT5//D3jfjJb6Gsjxv0Slpj23ip+HtOpnNgnb2S5zU3CB26G/IDPGoy4RJB42wzFE46DRsstbW6tKHoKbhAxw==, tarball: https://registry.npmjs.org/react-refresh/-/react-refresh-0.18.0.tgz} engines: {node: '>=0.10.0'} react-shallow-renderer@16.15.0: - resolution: {integrity: sha512-oScf2FqQ9LFVQgA73vr86xl2NaOIX73rh+YFqcOp68CWj56tSfgtGKrEbyhCj0rSijyG9M1CYprTh39fBi5hzA==} + resolution: {integrity: sha512-oScf2FqQ9LFVQgA73vr86xl2NaOIX73rh+YFqcOp68CWj56tSfgtGKrEbyhCj0rSijyG9M1CYprTh39fBi5hzA==, tarball: https://registry.npmjs.org/react-shallow-renderer/-/react-shallow-renderer-16.15.0.tgz} peerDependencies: react: ^16.0.0 || ^17.0.0 || ^18.0.0 react-test-renderer@18.2.0: - resolution: {integrity: sha512-JWD+aQ0lh2gvh4NM3bBM42Kx+XybOxCpgYK7F8ugAlpaTSnWsX+39Z4XkOykGZAHrjwwTZT3x3KxswVWxHPUqA==} + resolution: {integrity: sha512-JWD+aQ0lh2gvh4NM3bBM42Kx+XybOxCpgYK7F8ugAlpaTSnWsX+39Z4XkOykGZAHrjwwTZT3x3KxswVWxHPUqA==, tarball: https://registry.npmjs.org/react-test-renderer/-/react-test-renderer-18.2.0.tgz} peerDependencies: react: ^18.2.0 react@18.3.1: - resolution: {integrity: sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==} + resolution: {integrity: sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==, tarball: https://registry.npmjs.org/react/-/react-18.3.1.tgz} engines: {node: '>=0.10.0'} read-cache@1.0.0: - resolution: {integrity: sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==} + resolution: {integrity: sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==, tarball: https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz} readable-stream@2.3.8: - resolution: {integrity: sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==} + resolution: {integrity: sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==, tarball: https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz} readable-stream@3.6.2: - resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==} + resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==, tarball: https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz} engines: {node: '>= 6'} readdirp@3.6.0: - resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} + resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==, tarball: https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz} engines: {node: '>=8.10.0'} readdirp@4.1.2: - resolution: {integrity: sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==} + resolution: {integrity: sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==, tarball: https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz} engines: {node: '>= 14.18.0'} readdirp@5.0.0: - resolution: {integrity: sha512-9u/XQ1pvrQtYyMpZe7DXKv2p5CNvyVwzUB6uhLAnQwHMSgKMBR62lc7AHljaeteeHXn11XTAaLLUVZYVZyuRBQ==} + resolution: {integrity: sha512-9u/XQ1pvrQtYyMpZe7DXKv2p5CNvyVwzUB6uhLAnQwHMSgKMBR62lc7AHljaeteeHXn11XTAaLLUVZYVZyuRBQ==, tarball: https://registry.npmjs.org/readdirp/-/readdirp-5.0.0.tgz} engines: {node: '>= 20.19.0'} recast@0.23.12: - resolution: {integrity: sha512-dEWRjcINDu/F4l2dYx57ugBtD7HV9KXESyxhzw/MqWLeglJrsjJKqACPyUPg+6AF8mIgm+Zi0dZ3ACoIg+QtpA==} + resolution: {integrity: sha512-dEWRjcINDu/F4l2dYx57ugBtD7HV9KXESyxhzw/MqWLeglJrsjJKqACPyUPg+6AF8mIgm+Zi0dZ3ACoIg+QtpA==, tarball: https://registry.npmjs.org/recast/-/recast-0.23.12.tgz} engines: {node: '>= 4'} redent@3.0.0: - resolution: {integrity: sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==} + resolution: {integrity: sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==, tarball: https://registry.npmjs.org/redent/-/redent-3.0.0.tgz} engines: {node: '>=8'} reflect-metadata@0.2.2: - resolution: {integrity: sha512-urBwgfrvVP/eAyXx4hluJivBKzuEbSQs9rKWCrCkbSxNv8mxPcUZKeuoF3Uy4mJl3Lwprp6yy5/39VWigZ4K6Q==} + resolution: {integrity: sha512-urBwgfrvVP/eAyXx4hluJivBKzuEbSQs9rKWCrCkbSxNv8mxPcUZKeuoF3Uy4mJl3Lwprp6yy5/39VWigZ4K6Q==, tarball: https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.2.2.tgz} reflect.getprototypeof@1.0.10: - resolution: {integrity: sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==} + resolution: {integrity: sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==, tarball: https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz} engines: {node: '>= 0.4'} regenerate-unicode-properties@10.2.2: - resolution: {integrity: sha512-m03P+zhBeQd1RGnYxrGyDAPpWX/epKirLrp8e3qevZdVkKtnCrjjWczIbYc8+xd6vcTStVlqfycTx1KR4LOr0g==} + resolution: {integrity: sha512-m03P+zhBeQd1RGnYxrGyDAPpWX/epKirLrp8e3qevZdVkKtnCrjjWczIbYc8+xd6vcTStVlqfycTx1KR4LOr0g==, tarball: https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.2.2.tgz} engines: {node: '>=4'} regenerate@1.4.2: - resolution: {integrity: sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==} + resolution: {integrity: sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==, tarball: https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz} regenerator-runtime@0.13.9: - resolution: {integrity: sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA==} + resolution: {integrity: sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA==, tarball: https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz} regex-parser@2.3.1: - resolution: {integrity: sha512-yXLRqatcCuKtVHsWrNg0JL3l1zGfdXeEvDa0bdu4tCDQw0RpMDZsqbkyRTUnKMR0tXF627V2oEWjBEaEdqTwtQ==} + resolution: {integrity: sha512-yXLRqatcCuKtVHsWrNg0JL3l1zGfdXeEvDa0bdu4tCDQw0RpMDZsqbkyRTUnKMR0tXF627V2oEWjBEaEdqTwtQ==, tarball: https://registry.npmjs.org/regex-parser/-/regex-parser-2.3.1.tgz} regexp.prototype.flags@1.5.4: - resolution: {integrity: sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==} + resolution: {integrity: sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==, tarball: https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.4.tgz} engines: {node: '>= 0.4'} regexpu-core@6.4.0: - resolution: {integrity: sha512-0ghuzq67LI9bLXpOX/ISfve/Mq33a4aFRzoQYhnnok1JOFpmE/A2TBGkNVenOGEeSBCjIiWcc6MVOG5HEQv0sA==} + resolution: {integrity: sha512-0ghuzq67LI9bLXpOX/ISfve/Mq33a4aFRzoQYhnnok1JOFpmE/A2TBGkNVenOGEeSBCjIiWcc6MVOG5HEQv0sA==, tarball: https://registry.npmjs.org/regexpu-core/-/regexpu-core-6.4.0.tgz} engines: {node: '>=4'} regjsgen@0.8.0: - resolution: {integrity: sha512-RvwtGe3d7LvWiDQXeQw8p5asZUmfU1G/l6WbUXeHta7Y2PEIvBTwH6E2EfmYUK8pxcxEdEmaomqyp0vZZ7C+3Q==} + resolution: {integrity: sha512-RvwtGe3d7LvWiDQXeQw8p5asZUmfU1G/l6WbUXeHta7Y2PEIvBTwH6E2EfmYUK8pxcxEdEmaomqyp0vZZ7C+3Q==, tarball: https://registry.npmjs.org/regjsgen/-/regjsgen-0.8.0.tgz} regjsparser@0.13.2: - resolution: {integrity: sha512-NgRBy2Nx/bE+9F27nVHnqcN5HjyLmecqsqx2PJHu3/IEtADD4WuxuXIVExD5PoSDFVrl78dOonfcOe5O+5nbzQ==} + resolution: {integrity: sha512-NgRBy2Nx/bE+9F27nVHnqcN5HjyLmecqsqx2PJHu3/IEtADD4WuxuXIVExD5PoSDFVrl78dOonfcOe5O+5nbzQ==, tarball: https://registry.npmjs.org/regjsparser/-/regjsparser-0.13.2.tgz} hasBin: true require-directory@2.1.1: - resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} + resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==, tarball: https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz} engines: {node: '>=0.10.0'} require-from-string@2.0.2: - resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} + resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==, tarball: https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz} engines: {node: '>=0.10.0'} requireindex@1.2.0: - resolution: {integrity: sha512-L9jEkOi3ASd9PYit2cwRfyppc9NoABujTP8/5gFcbERmo5jUoAKovIC3fsF17pkTnGsrByysqX+Kxd2OTNI1ww==} + resolution: {integrity: sha512-L9jEkOi3ASd9PYit2cwRfyppc9NoABujTP8/5gFcbERmo5jUoAKovIC3fsF17pkTnGsrByysqX+Kxd2OTNI1ww==, tarball: https://registry.npmjs.org/requireindex/-/requireindex-1.2.0.tgz} engines: {node: '>=0.10.5'} requires-port@1.0.0: - resolution: {integrity: sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==} + resolution: {integrity: sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==, tarball: https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz} resolve-cwd@3.0.0: - resolution: {integrity: sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==} + resolution: {integrity: sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==, tarball: https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz} engines: {node: '>=8'} resolve-dir@1.0.1: - resolution: {integrity: sha512-R7uiTjECzvOsWSfdM0QKFNBVFcK27aHOUwdvK53BcW8zqnGdYp0Fbj82cy54+2A4P2tFM22J5kRfe1R+lM/1yg==} + resolution: {integrity: sha512-R7uiTjECzvOsWSfdM0QKFNBVFcK27aHOUwdvK53BcW8zqnGdYp0Fbj82cy54+2A4P2tFM22J5kRfe1R+lM/1yg==, tarball: https://registry.npmjs.org/resolve-dir/-/resolve-dir-1.0.1.tgz} engines: {node: '>=0.10.0'} resolve-from@4.0.0: - resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} + resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==, tarball: https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz} engines: {node: '>=4'} resolve-from@5.0.0: - resolution: {integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==} + resolution: {integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==, tarball: https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz} engines: {node: '>=8'} resolve-pkg-maps@1.0.0: - resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==} + resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==, tarball: https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz} resolve-url-loader@5.0.0: - resolution: {integrity: sha512-uZtduh8/8srhBoMx//5bwqjQ+rfYOUq8zC9NrMUGtjBiGTtFJM42s58/36+hTqeqINcnYe08Nj3LkK9lW4N8Xg==} + resolution: {integrity: sha512-uZtduh8/8srhBoMx//5bwqjQ+rfYOUq8zC9NrMUGtjBiGTtFJM42s58/36+hTqeqINcnYe08Nj3LkK9lW4N8Xg==, tarball: https://registry.npmjs.org/resolve-url-loader/-/resolve-url-loader-5.0.0.tgz} engines: {node: '>=12'} resolve.exports@2.0.3: - resolution: {integrity: sha512-OcXjMsGdhL4XnbShKpAcSqPMzQoYkYyhbEaeSko47MjRP9NfEQMhZkXL1DoFlt9LWQn4YttrdnV6X2OiyzBi+A==} + resolution: {integrity: sha512-OcXjMsGdhL4XnbShKpAcSqPMzQoYkYyhbEaeSko47MjRP9NfEQMhZkXL1DoFlt9LWQn4YttrdnV6X2OiyzBi+A==, tarball: https://registry.npmjs.org/resolve.exports/-/resolve.exports-2.0.3.tgz} engines: {node: '>=10'} resolve@1.22.12: - resolution: {integrity: sha512-TyeJ1zif53BPfHootBGwPRYT1RUt6oGWsaQr8UyZW/eAm9bKoijtvruSDEmZHm92CwS9nj7/fWttqPCgzep8CA==} + resolution: {integrity: sha512-TyeJ1zif53BPfHootBGwPRYT1RUt6oGWsaQr8UyZW/eAm9bKoijtvruSDEmZHm92CwS9nj7/fWttqPCgzep8CA==, tarball: https://registry.npmjs.org/resolve/-/resolve-1.22.12.tgz} engines: {node: '>= 0.4'} hasBin: true resolve@1.22.8: - resolution: {integrity: sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==} + resolution: {integrity: sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==, tarball: https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz} hasBin: true resolve@2.0.0-next.7: - resolution: {integrity: sha512-tqt+NBWwyaMgw3zDsnygx4CByWjQEJHOPMdslYhppaQSJUtL/D4JO9CcBBlhPoI8lz9oJIDXkwXfhF4aWqP8xQ==} + resolution: {integrity: sha512-tqt+NBWwyaMgw3zDsnygx4CByWjQEJHOPMdslYhppaQSJUtL/D4JO9CcBBlhPoI8lz9oJIDXkwXfhF4aWqP8xQ==, tarball: https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.7.tgz} engines: {node: '>= 0.4'} hasBin: true restore-cursor@3.1.0: - resolution: {integrity: sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==} + resolution: {integrity: sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==, tarball: https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz} engines: {node: '>=8'} restore-cursor@5.1.0: - resolution: {integrity: sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==} + resolution: {integrity: sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==, tarball: https://registry.npmjs.org/restore-cursor/-/restore-cursor-5.1.0.tgz} engines: {node: '>=18'} retry@0.13.1: - resolution: {integrity: sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==} + resolution: {integrity: sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==, tarball: https://registry.npmjs.org/retry/-/retry-0.13.1.tgz} engines: {node: '>= 4'} reusify@1.1.0: - resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==} + resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==, tarball: https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz} engines: {iojs: '>=1.0.0', node: '>=0.10.0'} rfdc@1.4.1: - resolution: {integrity: sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==} + resolution: {integrity: sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==, tarball: https://registry.npmjs.org/rfdc/-/rfdc-1.4.1.tgz} robust-predicates@3.0.3: - resolution: {integrity: sha512-NS3levdsRIUOmiJ8FZWCP7LG3QpJyrs/TE0Zpf1yvZu8cAJJ6QMW92H1c7kWpdIHo8RvmLxN/o2JXTKHp74lUA==} + resolution: {integrity: sha512-NS3levdsRIUOmiJ8FZWCP7LG3QpJyrs/TE0Zpf1yvZu8cAJJ6QMW92H1c7kWpdIHo8RvmLxN/o2JXTKHp74lUA==, tarball: https://registry.npmjs.org/robust-predicates/-/robust-predicates-3.0.3.tgz} rolldown@1.1.5: - resolution: {integrity: sha512-t9z29cJjXf/vxQ8dyhCSpt6H6aSwHTk8cT5I3iy6SMXuFpk5mB6PL6XfC8PCwrPTx93udwKUm9HRteAlTGBLiA==} + resolution: {integrity: sha512-t9z29cJjXf/vxQ8dyhCSpt6H6aSwHTk8cT5I3iy6SMXuFpk5mB6PL6XfC8PCwrPTx93udwKUm9HRteAlTGBLiA==, tarball: https://registry.npmjs.org/rolldown/-/rolldown-1.1.5.tgz} engines: {node: ^20.19.0 || >=22.12.0} hasBin: true rolldown@1.2.0: - resolution: {integrity: sha512-u7tgm5l4Yw1iTqUL4EcYOAt7fFvCgQMLeidrnD4GALlC6aOznCjezYajgxeyKw27u0Q5N7fwgCzjVyPIWzwuBA==} + resolution: {integrity: sha512-u7tgm5l4Yw1iTqUL4EcYOAt7fFvCgQMLeidrnD4GALlC6aOznCjezYajgxeyKw27u0Q5N7fwgCzjVyPIWzwuBA==, tarball: https://registry.npmjs.org/rolldown/-/rolldown-1.2.0.tgz} engines: {node: ^20.19.0 || >=22.12.0} hasBin: true rollup-plugin-dts@6.4.1: - resolution: {integrity: sha512-l//F3Zf7ID5GoOfLfD8kroBjQKEKpy1qfhtAdnpibFZMffPaylrg1CoDC2vGkPeTeyxUe4bVFCln2EFuL7IGGg==} + resolution: {integrity: sha512-l//F3Zf7ID5GoOfLfD8kroBjQKEKpy1qfhtAdnpibFZMffPaylrg1CoDC2vGkPeTeyxUe4bVFCln2EFuL7IGGg==, tarball: https://registry.npmjs.org/rollup-plugin-dts/-/rollup-plugin-dts-6.4.1.tgz} engines: {node: '>=20'} peerDependencies: rollup: ^3.29.4 || ^4 typescript: ^4.5 || ^5.0 || ^6.0 rollup-plugin-postcss@4.0.2: - resolution: {integrity: sha512-05EaY6zvZdmvPUDi3uCcAQoESDcYnv8ogJJQRp6V5kZ6J6P7uAVJlrTZcaaA20wTH527YTnKfkAoPxWI/jPp4w==} + resolution: {integrity: sha512-05EaY6zvZdmvPUDi3uCcAQoESDcYnv8ogJJQRp6V5kZ6J6P7uAVJlrTZcaaA20wTH527YTnKfkAoPxWI/jPp4w==, tarball: https://registry.npmjs.org/rollup-plugin-postcss/-/rollup-plugin-postcss-4.0.2.tgz} engines: {node: '>=10'} peerDependencies: postcss: 8.x rollup-plugin-preserve-directives@0.4.0: - resolution: {integrity: sha512-gx4nBxYm5BysmEQS+e2tAMrtFxrGvk+Pe5ppafRibQi0zlW7VYAbEGk6IKDw9sJGPdFWgVTE0o4BU4cdG0Fylg==} + resolution: {integrity: sha512-gx4nBxYm5BysmEQS+e2tAMrtFxrGvk+Pe5ppafRibQi0zlW7VYAbEGk6IKDw9sJGPdFWgVTE0o4BU4cdG0Fylg==, tarball: https://registry.npmjs.org/rollup-plugin-preserve-directives/-/rollup-plugin-preserve-directives-0.4.0.tgz} peerDependencies: rollup: 2.x || 3.x || 4.x rollup-plugin-typescript2@0.36.0: - resolution: {integrity: sha512-NB2CSQDxSe9+Oe2ahZbf+B4bh7pHwjV5L+RSYpCu7Q5ROuN94F9b6ioWwKfz3ueL3KTtmX4o2MUH2cgHDIEUsw==} + resolution: {integrity: sha512-NB2CSQDxSe9+Oe2ahZbf+B4bh7pHwjV5L+RSYpCu7Q5ROuN94F9b6ioWwKfz3ueL3KTtmX4o2MUH2cgHDIEUsw==, tarball: https://registry.npmjs.org/rollup-plugin-typescript2/-/rollup-plugin-typescript2-0.36.0.tgz} peerDependencies: rollup: '>=1.26.3' typescript: '>=2.4.0' rollup-pluginutils@2.8.2: - resolution: {integrity: sha512-EEp9NhnUkwY8aif6bxgovPHMoMoNr2FulJziTndpt5H9RdwC47GSGuII9XxpSdzVGM0GWrNPHV6ie1LTNJPaLQ==} + resolution: {integrity: sha512-EEp9NhnUkwY8aif6bxgovPHMoMoNr2FulJziTndpt5H9RdwC47GSGuII9XxpSdzVGM0GWrNPHV6ie1LTNJPaLQ==, tarball: https://registry.npmjs.org/rollup-pluginutils/-/rollup-pluginutils-2.8.2.tgz} rollup@4.14.0: - resolution: {integrity: sha512-Qe7w62TyawbDzB4yt32R0+AbIo6m1/sqO7UPzFS8Z/ksL5mrfhA0v4CavfdmFav3D+ub4QeAgsGEe84DoWe/nQ==} + resolution: {integrity: sha512-Qe7w62TyawbDzB4yt32R0+AbIo6m1/sqO7UPzFS8Z/ksL5mrfhA0v4CavfdmFav3D+ub4QeAgsGEe84DoWe/nQ==, tarball: https://registry.npmjs.org/rollup/-/rollup-4.14.0.tgz} engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true rollup@4.62.2: - resolution: {integrity: sha512-RFnrW4lhXA3s3eqHDZvN654g8OTjzRfqpIRJYczCGB6HzphckVAi/Qh4tbPUbRuDi7s1Llv8g/NspLkttY3gTA==} + resolution: {integrity: sha512-RFnrW4lhXA3s3eqHDZvN654g8OTjzRfqpIRJYczCGB6HzphckVAi/Qh4tbPUbRuDi7s1Llv8g/NspLkttY3gTA==, tarball: https://registry.npmjs.org/rollup/-/rollup-4.62.2.tgz} engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true rope-sequence@1.3.4: - resolution: {integrity: sha512-UT5EDe2cu2E/6O4igUr5PSFs23nvvukicWHx6GnOPlHAiiYbzNuCRQCuiUdHJQcqKalLKlrYJnjY0ySGsXNQXQ==} + resolution: {integrity: sha512-UT5EDe2cu2E/6O4igUr5PSFs23nvvukicWHx6GnOPlHAiiYbzNuCRQCuiUdHJQcqKalLKlrYJnjY0ySGsXNQXQ==, tarball: https://registry.npmjs.org/rope-sequence/-/rope-sequence-1.3.4.tgz} roughjs@4.6.6: - resolution: {integrity: sha512-ZUz/69+SYpFN/g/lUlo2FXcIjRkSu3nDarreVdGGndHEBJ6cXPdKguS8JGxwj5HA5xIbVKSmLgr5b3AWxtRfvQ==} + resolution: {integrity: sha512-ZUz/69+SYpFN/g/lUlo2FXcIjRkSu3nDarreVdGGndHEBJ6cXPdKguS8JGxwj5HA5xIbVKSmLgr5b3AWxtRfvQ==, tarball: https://registry.npmjs.org/roughjs/-/roughjs-4.6.6.tgz} router@2.2.0: - resolution: {integrity: sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==} + resolution: {integrity: sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==, tarball: https://registry.npmjs.org/router/-/router-2.2.0.tgz} engines: {node: '>= 18'} rslog@1.3.2: - resolution: {integrity: sha512-1YyYXBvN0a2b1MSIDLwDTqqgjDzRKxUg/S/+KO6EAgbtZW1B3fdLHAMhEEtvk1patJYMqcRvlp3HQwnxj7AdGQ==} + resolution: {integrity: sha512-1YyYXBvN0a2b1MSIDLwDTqqgjDzRKxUg/S/+KO6EAgbtZW1B3fdLHAMhEEtvk1patJYMqcRvlp3HQwnxj7AdGQ==, tarball: https://registry.npmjs.org/rslog/-/rslog-1.3.2.tgz} run-applescript@7.0.0: - resolution: {integrity: sha512-9by4Ij99JUr/MCFBUkDKLWK3G9HVXmabKz9U5MlIAIuvuzkiOicRYs8XJLxX+xahD+mLiiCYDqF9dKAgtzKP1A==} + resolution: {integrity: sha512-9by4Ij99JUr/MCFBUkDKLWK3G9HVXmabKz9U5MlIAIuvuzkiOicRYs8XJLxX+xahD+mLiiCYDqF9dKAgtzKP1A==, tarball: https://registry.npmjs.org/run-applescript/-/run-applescript-7.0.0.tgz} engines: {node: '>=18'} run-applescript@7.1.0: - resolution: {integrity: sha512-DPe5pVFaAsinSaV6QjQ6gdiedWDcRCbUuiQfQa2wmWV7+xC9bGulGI8+TdRmoFkAPaBXk8CrAbnlY2ISniJ47Q==} + resolution: {integrity: sha512-DPe5pVFaAsinSaV6QjQ6gdiedWDcRCbUuiQfQa2wmWV7+xC9bGulGI8+TdRmoFkAPaBXk8CrAbnlY2ISniJ47Q==, tarball: https://registry.npmjs.org/run-applescript/-/run-applescript-7.1.0.tgz} engines: {node: '>=18'} run-async@4.0.6: - resolution: {integrity: sha512-IoDlSLTs3Yq593mb3ZoKWKXMNu3UpObxhgA/Xuid5p4bbfi2jdY1Hj0m1K+0/tEuQTxIGMhQDqGjKb7RuxGpAQ==} + resolution: {integrity: sha512-IoDlSLTs3Yq593mb3ZoKWKXMNu3UpObxhgA/Xuid5p4bbfi2jdY1Hj0m1K+0/tEuQTxIGMhQDqGjKb7RuxGpAQ==, tarball: https://registry.npmjs.org/run-async/-/run-async-4.0.6.tgz} engines: {node: '>=0.12.0'} run-parallel@1.2.0: - resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} + resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==, tarball: https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz} rw@1.3.3: - resolution: {integrity: sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ==} + resolution: {integrity: sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ==, tarball: https://registry.npmjs.org/rw/-/rw-1.3.3.tgz} rxjs@7.8.2: - resolution: {integrity: sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==} + resolution: {integrity: sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==, tarball: https://registry.npmjs.org/rxjs/-/rxjs-7.8.2.tgz} safe-array-concat@1.1.4: - resolution: {integrity: sha512-wtZlHyOje6OZTGqAoaDKxFkgRtkF9CnHAVnCHKfuj200wAgL+bSJhdsCD2l0Qx/2ekEXjPWcyKkfGb5CPboslg==} + resolution: {integrity: sha512-wtZlHyOje6OZTGqAoaDKxFkgRtkF9CnHAVnCHKfuj200wAgL+bSJhdsCD2l0Qx/2ekEXjPWcyKkfGb5CPboslg==, tarball: https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.4.tgz} engines: {node: '>=0.4'} safe-buffer@5.1.2: - resolution: {integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==} + resolution: {integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==, tarball: https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz} safe-buffer@5.2.1: - resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} + resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==, tarball: https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz} safe-identifier@0.4.2: - resolution: {integrity: sha512-6pNbSMW6OhAi9j+N8V+U715yBQsaWJ7eyEUaOrawX+isg5ZxhUlV1NipNtgaKHmFGiABwt+ZF04Ii+3Xjkg+8w==} + resolution: {integrity: sha512-6pNbSMW6OhAi9j+N8V+U715yBQsaWJ7eyEUaOrawX+isg5ZxhUlV1NipNtgaKHmFGiABwt+ZF04Ii+3Xjkg+8w==, tarball: https://registry.npmjs.org/safe-identifier/-/safe-identifier-0.4.2.tgz} safe-push-apply@1.0.0: - resolution: {integrity: sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA==} + resolution: {integrity: sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA==, tarball: https://registry.npmjs.org/safe-push-apply/-/safe-push-apply-1.0.0.tgz} engines: {node: '>= 0.4'} safe-regex-test@1.1.0: - resolution: {integrity: sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==} + resolution: {integrity: sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==, tarball: https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.1.0.tgz} engines: {node: '>= 0.4'} safer-buffer@2.1.2: - resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} + resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==, tarball: https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz} sass-embedded-all-unknown@1.100.0: - resolution: {integrity: sha512-auFtXY/kwYILmSVjtBDwyj0axcLbYYiffOKWoaXHnI5bsYwiRbBh3EneR1rpbX2ZIZCrwX93i5pxKLTZF/662Q==} + resolution: {integrity: sha512-auFtXY/kwYILmSVjtBDwyj0axcLbYYiffOKWoaXHnI5bsYwiRbBh3EneR1rpbX2ZIZCrwX93i5pxKLTZF/662Q==, tarball: https://registry.npmjs.org/sass-embedded-all-unknown/-/sass-embedded-all-unknown-1.100.0.tgz} cpu: ['!arm', '!arm64', '!riscv64', '!x64'] sass-embedded-android-arm64@1.100.0: - resolution: {integrity: sha512-W+Ru9JwTnfU0UX3jSZcbqFdtKFMcYdfFwytc57h2DgnqCOIiAqI2E06mABZBZC+r3LwXCBuS5GbXAGeVgvVDkA==} + resolution: {integrity: sha512-W+Ru9JwTnfU0UX3jSZcbqFdtKFMcYdfFwytc57h2DgnqCOIiAqI2E06mABZBZC+r3LwXCBuS5GbXAGeVgvVDkA==, tarball: https://registry.npmjs.org/sass-embedded-android-arm64/-/sass-embedded-android-arm64-1.100.0.tgz} engines: {node: '>=14.0.0'} cpu: [arm64] os: [android] sass-embedded-android-arm@1.100.0: - resolution: {integrity: sha512-70f3HgX2pFNmzpGQ86n5e6QfWn2fP4QUQGfFQK0P1XH73ZLIzLo2YqygrGKGKeeqtc5eU2Wl1/xQzhzuKnO4kw==} + resolution: {integrity: sha512-70f3HgX2pFNmzpGQ86n5e6QfWn2fP4QUQGfFQK0P1XH73ZLIzLo2YqygrGKGKeeqtc5eU2Wl1/xQzhzuKnO4kw==, tarball: https://registry.npmjs.org/sass-embedded-android-arm/-/sass-embedded-android-arm-1.100.0.tgz} engines: {node: '>=14.0.0'} cpu: [arm] os: [android] sass-embedded-android-riscv64@1.100.0: - resolution: {integrity: sha512-icU3o0V/uCSytSpf+tX5Lf51BvyQEbLzDUJfUi9etSauYBGHpPKkdtdZH0si4v98phq11Kl8rSV1SggksxF1Hg==} + resolution: {integrity: sha512-icU3o0V/uCSytSpf+tX5Lf51BvyQEbLzDUJfUi9etSauYBGHpPKkdtdZH0si4v98phq11Kl8rSV1SggksxF1Hg==, tarball: https://registry.npmjs.org/sass-embedded-android-riscv64/-/sass-embedded-android-riscv64-1.100.0.tgz} engines: {node: '>=14.0.0'} cpu: [riscv64] os: [android] sass-embedded-android-x64@1.100.0: - resolution: {integrity: sha512-mevF9VQk6gEYByy8+jusaHGmd7Usb2ytX/DsEOd0JtOGCtcf1kh575xJ6OUBDIcJ15uLnbau/0iy1eP6WVBvWA==} + resolution: {integrity: sha512-mevF9VQk6gEYByy8+jusaHGmd7Usb2ytX/DsEOd0JtOGCtcf1kh575xJ6OUBDIcJ15uLnbau/0iy1eP6WVBvWA==, tarball: https://registry.npmjs.org/sass-embedded-android-x64/-/sass-embedded-android-x64-1.100.0.tgz} engines: {node: '>=14.0.0'} cpu: [x64] os: [android] sass-embedded-darwin-arm64@1.100.0: - resolution: {integrity: sha512-1PVlYi61POo93IT/FfrG1mc1tAHxeSTyUALF2aOFmXGWjVXr3bQzEQiBGCOvQbj/ix+5hNyXFXcEMEyKvtUJJA==} + resolution: {integrity: sha512-1PVlYi61POo93IT/FfrG1mc1tAHxeSTyUALF2aOFmXGWjVXr3bQzEQiBGCOvQbj/ix+5hNyXFXcEMEyKvtUJJA==, tarball: https://registry.npmjs.org/sass-embedded-darwin-arm64/-/sass-embedded-darwin-arm64-1.100.0.tgz} engines: {node: '>=14.0.0'} cpu: [arm64] os: [darwin] sass-embedded-darwin-x64@1.100.0: - resolution: {integrity: sha512-x97o3JnGyImZNCIVs9wQHJUE5QCvmVIKaH1cwrz/5dK7OT1FpeNiW+u9TUomP9hG6Ekjd8EL8NBHpxTfIhdjmg==} + resolution: {integrity: sha512-x97o3JnGyImZNCIVs9wQHJUE5QCvmVIKaH1cwrz/5dK7OT1FpeNiW+u9TUomP9hG6Ekjd8EL8NBHpxTfIhdjmg==, tarball: https://registry.npmjs.org/sass-embedded-darwin-x64/-/sass-embedded-darwin-x64-1.100.0.tgz} engines: {node: '>=14.0.0'} cpu: [x64] os: [darwin] sass-embedded-linux-arm64@1.100.0: - resolution: {integrity: sha512-Dwjmj8Z6VRy7rAi53JAdEwIyUjpfl7PhpSc2/LpQPQx+aO5Dp7Spaipkax0ufJl1SoDUdchCsM4y/88YaluorQ==} + resolution: {integrity: sha512-Dwjmj8Z6VRy7rAi53JAdEwIyUjpfl7PhpSc2/LpQPQx+aO5Dp7Spaipkax0ufJl1SoDUdchCsM4y/88YaluorQ==, tarball: https://registry.npmjs.org/sass-embedded-linux-arm64/-/sass-embedded-linux-arm64-1.100.0.tgz} engines: {node: '>=14.0.0'} cpu: [arm64] os: [linux] sass-embedded-linux-arm@1.100.0: - resolution: {integrity: sha512-9Ul7O1eKrc5YlhwWjkp8tZPSe3UEwSZ1uwUZOQom1HL0pRlBA6F/IlGZYFTLwnHMIP1fc77MMNaBRfc05mKMpw==} + resolution: {integrity: sha512-9Ul7O1eKrc5YlhwWjkp8tZPSe3UEwSZ1uwUZOQom1HL0pRlBA6F/IlGZYFTLwnHMIP1fc77MMNaBRfc05mKMpw==, tarball: https://registry.npmjs.org/sass-embedded-linux-arm/-/sass-embedded-linux-arm-1.100.0.tgz} engines: {node: '>=14.0.0'} cpu: [arm] os: [linux] sass-embedded-linux-musl-arm64@1.100.0: - resolution: {integrity: sha512-XpACJB2KjSLjf2e9uuvGVdOURsoNrFqgRiihhXyUHK9W0t3LIHb7z5MA/7XGPIT9bWSOO2zyw+rH/FHtDV/Yrg==} + resolution: {integrity: sha512-XpACJB2KjSLjf2e9uuvGVdOURsoNrFqgRiihhXyUHK9W0t3LIHb7z5MA/7XGPIT9bWSOO2zyw+rH/FHtDV/Yrg==, tarball: https://registry.npmjs.org/sass-embedded-linux-musl-arm64/-/sass-embedded-linux-musl-arm64-1.100.0.tgz} engines: {node: '>=14.0.0'} cpu: [arm64] os: [linux] sass-embedded-linux-musl-arm@1.100.0: - resolution: {integrity: sha512-sl0JgbGloPyJg66XXx5UDSDScZ0oU85DpMQU4JU/sCUCFj1Z8zZ69SJWKTCNE4/jwnce7WI2zPCV5AG+RHOZJw==} + resolution: {integrity: sha512-sl0JgbGloPyJg66XXx5UDSDScZ0oU85DpMQU4JU/sCUCFj1Z8zZ69SJWKTCNE4/jwnce7WI2zPCV5AG+RHOZJw==, tarball: https://registry.npmjs.org/sass-embedded-linux-musl-arm/-/sass-embedded-linux-musl-arm-1.100.0.tgz} engines: {node: '>=14.0.0'} cpu: [arm] os: [linux] sass-embedded-linux-musl-riscv64@1.100.0: - resolution: {integrity: sha512-ShvI0Kx04mwoCARwZ0UjiT97isQvzO80tAt91zmFyHLN9kelc/IrQi940farSm2xQVPCKdeVyeG0ekBsokSpYQ==} + resolution: {integrity: sha512-ShvI0Kx04mwoCARwZ0UjiT97isQvzO80tAt91zmFyHLN9kelc/IrQi940farSm2xQVPCKdeVyeG0ekBsokSpYQ==, tarball: https://registry.npmjs.org/sass-embedded-linux-musl-riscv64/-/sass-embedded-linux-musl-riscv64-1.100.0.tgz} engines: {node: '>=14.0.0'} cpu: [riscv64] os: [linux] sass-embedded-linux-musl-x64@1.100.0: - resolution: {integrity: sha512-TDBCRWNuS4RDLQXvRc1gjZlWiWTWaWGp0Bwu/IKwJxov81lsvrCs3TihTyNXtW7V5aoN4Ky3r0QOkNb3mwmBnA==} + resolution: {integrity: sha512-TDBCRWNuS4RDLQXvRc1gjZlWiWTWaWGp0Bwu/IKwJxov81lsvrCs3TihTyNXtW7V5aoN4Ky3r0QOkNb3mwmBnA==, tarball: https://registry.npmjs.org/sass-embedded-linux-musl-x64/-/sass-embedded-linux-musl-x64-1.100.0.tgz} engines: {node: '>=14.0.0'} cpu: [x64] os: [linux] sass-embedded-linux-riscv64@1.100.0: - resolution: {integrity: sha512-j4ENJGOheO+fm3j/yorLxCjBP6/XskrZx7dTLlT+lXYwN/qqCqoA/gsNLI0McS3DFM6GBwPiffzWsdWS8t6sEQ==} + resolution: {integrity: sha512-j4ENJGOheO+fm3j/yorLxCjBP6/XskrZx7dTLlT+lXYwN/qqCqoA/gsNLI0McS3DFM6GBwPiffzWsdWS8t6sEQ==, tarball: https://registry.npmjs.org/sass-embedded-linux-riscv64/-/sass-embedded-linux-riscv64-1.100.0.tgz} engines: {node: '>=14.0.0'} cpu: [riscv64] os: [linux] sass-embedded-linux-x64@1.100.0: - resolution: {integrity: sha512-0vUSN8j0WGtCJIOPh//EmUvYGHW0QOe5iul8qyhPk50MAcw49MA0r34AhftjDdx94ILPF6vApFs0gwHPQRlpVA==} + resolution: {integrity: sha512-0vUSN8j0WGtCJIOPh//EmUvYGHW0QOe5iul8qyhPk50MAcw49MA0r34AhftjDdx94ILPF6vApFs0gwHPQRlpVA==, tarball: https://registry.npmjs.org/sass-embedded-linux-x64/-/sass-embedded-linux-x64-1.100.0.tgz} engines: {node: '>=14.0.0'} cpu: [x64] os: [linux] sass-embedded-unknown-all@1.100.0: - resolution: {integrity: sha512-c+naBgWId4MIpToXcI0DgqetjdAkwTTAxFAuOaBz7HUXLdyG1oZRrEvSsbe41nEdQOKH0vgofVFCeSQgoXOG9A==} + resolution: {integrity: sha512-c+naBgWId4MIpToXcI0DgqetjdAkwTTAxFAuOaBz7HUXLdyG1oZRrEvSsbe41nEdQOKH0vgofVFCeSQgoXOG9A==, tarball: https://registry.npmjs.org/sass-embedded-unknown-all/-/sass-embedded-unknown-all-1.100.0.tgz} os: ['!android', '!darwin', '!linux', '!win32'] sass-embedded-win32-arm64@1.100.0: - resolution: {integrity: sha512-iE+yxj+hUXwwbqpHkXxgAWTzeRfcWxJ7SSTQEPMk48lwq3oCrWLlz5sQuWHbuTK/i0GKQfROdP+hOmPi89yjUg==} + resolution: {integrity: sha512-iE+yxj+hUXwwbqpHkXxgAWTzeRfcWxJ7SSTQEPMk48lwq3oCrWLlz5sQuWHbuTK/i0GKQfROdP+hOmPi89yjUg==, tarball: https://registry.npmjs.org/sass-embedded-win32-arm64/-/sass-embedded-win32-arm64-1.100.0.tgz} engines: {node: '>=14.0.0'} cpu: [arm64] os: [win32] sass-embedded-win32-x64@1.100.0: - resolution: {integrity: sha512-qI4F8MI7/KYoy9NdjJfhSspG42WPkADSNDvwEV7qWvCSFC83koJssRsKO2/PfY+niZz6BG65Ic/D+A11h959hw==} + resolution: {integrity: sha512-qI4F8MI7/KYoy9NdjJfhSspG42WPkADSNDvwEV7qWvCSFC83koJssRsKO2/PfY+niZz6BG65Ic/D+A11h959hw==, tarball: https://registry.npmjs.org/sass-embedded-win32-x64/-/sass-embedded-win32-x64-1.100.0.tgz} engines: {node: '>=14.0.0'} cpu: [x64] os: [win32] sass-embedded@1.100.0: - resolution: {integrity: sha512-Ut8wlQSk19tm7jMK6mz6cF1+e+E7tUnW2tM02zQDPnOTcVbV8qCQG8UWxZkkNlY50+hV3hqP24OOkUlMz8xBpw==} + resolution: {integrity: sha512-Ut8wlQSk19tm7jMK6mz6cF1+e+E7tUnW2tM02zQDPnOTcVbV8qCQG8UWxZkkNlY50+hV3hqP24OOkUlMz8xBpw==, tarball: https://registry.npmjs.org/sass-embedded/-/sass-embedded-1.100.0.tgz} engines: {node: '>=16.0.0'} hasBin: true sass-loader@16.0.8: - resolution: {integrity: sha512-hcov4ZwZJIGbEuyNr9EmiTmZueyrxSToE6GOzoZnq5JM7ecRO7ttyvilPn+VmRsqiP16+VYZzVnGZj/hzZgKBA==} + resolution: {integrity: sha512-hcov4ZwZJIGbEuyNr9EmiTmZueyrxSToE6GOzoZnq5JM7ecRO7ttyvilPn+VmRsqiP16+VYZzVnGZj/hzZgKBA==, tarball: https://registry.npmjs.org/sass-loader/-/sass-loader-16.0.8.tgz} engines: {node: '>= 18.12.0'} peerDependencies: '@rspack/core': 0.x || ^1.0.0 || ^2.0.0-0 @@ -13652,7 +13715,7 @@ packages: optional: true sass-loader@17.0.0: - resolution: {integrity: sha512-0Ybm8ohBQ9LcrycVrFQp/KQBNX5a3Wda9/smS0mE/xLffzEnwvV8nykOzrbiSWNzTE3IB/jiXx8O4QmDPG2+Gw==} + resolution: {integrity: sha512-0Ybm8ohBQ9LcrycVrFQp/KQBNX5a3Wda9/smS0mE/xLffzEnwvV8nykOzrbiSWNzTE3IB/jiXx8O4QmDPG2+Gw==, tarball: https://registry.npmjs.org/sass-loader/-/sass-loader-17.0.0.tgz} engines: {node: '>= 22.11.0'} peerDependencies: '@rspack/core': 0.x || ^1.0.0 || ^2.0.0-0 @@ -13670,298 +13733,298 @@ packages: optional: true sass@1.100.0: - resolution: {integrity: sha512-B5j0rYMlinhhOo9tjQebMVVn0TfyXAF+wB3b2ggZUuJ/is/Y+7+JGjirAMxHZ9Z3hIP98NPfamlAkBHa1lAaXQ==} + resolution: {integrity: sha512-B5j0rYMlinhhOo9tjQebMVVn0TfyXAF+wB3b2ggZUuJ/is/Y+7+JGjirAMxHZ9Z3hIP98NPfamlAkBHa1lAaXQ==, tarball: https://registry.npmjs.org/sass/-/sass-1.100.0.tgz} engines: {node: '>=20.19.0'} hasBin: true sass@1.101.0: - resolution: {integrity: sha512-OL3GoQyoUdDt843DpVmDO6y2k1sc5IhUDSpu8XucEI+35neq5QivZ1iuegnpraEVTJXlQGK1gl27zKcTLEPbQw==} + resolution: {integrity: sha512-OL3GoQyoUdDt843DpVmDO6y2k1sc5IhUDSpu8XucEI+35neq5QivZ1iuegnpraEVTJXlQGK1gl27zKcTLEPbQw==, tarball: https://registry.npmjs.org/sass/-/sass-1.101.0.tgz} engines: {node: '>=20.19.0'} hasBin: true sass@1.56.2: - resolution: {integrity: sha512-ciEJhnyCRwzlBCB+h5cCPM6ie/6f8HrhZMQOf5vlU60Y1bI1rx5Zb0vlDZvaycHsg/MqFfF1Eq2eokAa32iw8w==} + resolution: {integrity: sha512-ciEJhnyCRwzlBCB+h5cCPM6ie/6f8HrhZMQOf5vlU60Y1bI1rx5Zb0vlDZvaycHsg/MqFfF1Eq2eokAa32iw8w==, tarball: https://registry.npmjs.org/sass/-/sass-1.56.2.tgz} engines: {node: '>=12.0.0'} hasBin: true sax@1.6.0: - resolution: {integrity: sha512-6R3J5M4AcbtLUdZmRv2SygeVaM7IhrLXu9BmnOGmmACak8fiUtOsYNWUS4uK7upbmHIBbLBeFeI//477BKLBzA==} + resolution: {integrity: sha512-6R3J5M4AcbtLUdZmRv2SygeVaM7IhrLXu9BmnOGmmACak8fiUtOsYNWUS4uK7upbmHIBbLBeFeI//477BKLBzA==, tarball: https://registry.npmjs.org/sax/-/sax-1.6.0.tgz} engines: {node: '>=11.0.0'} saxes@6.0.0: - resolution: {integrity: sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==} + resolution: {integrity: sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==, tarball: https://registry.npmjs.org/saxes/-/saxes-6.0.0.tgz} engines: {node: '>=v12.22.7'} scheduler@0.23.2: - resolution: {integrity: sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==} + resolution: {integrity: sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==, tarball: https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz} schema-utils@3.3.0: - resolution: {integrity: sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==} + resolution: {integrity: sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==, tarball: https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz} engines: {node: '>= 10.13.0'} schema-utils@4.3.0: - resolution: {integrity: sha512-Gf9qqc58SpCA/xdziiHz35F4GNIWYWZrEshUc/G/r5BnLph6xpKuLeoJoQuj5WfBIx/eQLf+hmVPYHaxJu7V2g==} + resolution: {integrity: sha512-Gf9qqc58SpCA/xdziiHz35F4GNIWYWZrEshUc/G/r5BnLph6xpKuLeoJoQuj5WfBIx/eQLf+hmVPYHaxJu7V2g==, tarball: https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.0.tgz} engines: {node: '>= 10.13.0'} schema-utils@4.3.3: - resolution: {integrity: sha512-eflK8wEtyOE6+hsaRVPxvUKYCpRgzLqDTb8krvAsRIwOGlHoSgYLgBXoubGgLd2fT41/OUYdb48v4k4WWHQurA==} + resolution: {integrity: sha512-eflK8wEtyOE6+hsaRVPxvUKYCpRgzLqDTb8krvAsRIwOGlHoSgYLgBXoubGgLd2fT41/OUYdb48v4k4WWHQurA==, tarball: https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.3.tgz} engines: {node: '>= 10.13.0'} secure-compare@3.0.1: - resolution: {integrity: sha512-AckIIV90rPDcBcglUwXPF3kg0P0qmPsPXAj6BBEENQE1p5yA1xfmDJzfi1Tappj37Pv2mVbKpL3Z1T+Nn7k1Qw==} + resolution: {integrity: sha512-AckIIV90rPDcBcglUwXPF3kg0P0qmPsPXAj6BBEENQE1p5yA1xfmDJzfi1Tappj37Pv2mVbKpL3Z1T+Nn7k1Qw==, tarball: https://registry.npmjs.org/secure-compare/-/secure-compare-3.0.1.tgz} select-hose@2.0.0: - resolution: {integrity: sha512-mEugaLK+YfkijB4fx0e6kImuJdCIt2LxCRcbEYPqRGCs4F2ogyfZU5IAZRdjCP8JPq2AtdNoC/Dux63d9Kiryg==} + resolution: {integrity: sha512-mEugaLK+YfkijB4fx0e6kImuJdCIt2LxCRcbEYPqRGCs4F2ogyfZU5IAZRdjCP8JPq2AtdNoC/Dux63d9Kiryg==, tarball: https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz} select@1.1.2: - resolution: {integrity: sha512-OwpTSOfy6xSs1+pwcNrv0RBMOzI39Lp3qQKUTPVVPRjCdNa5JH/oPRiqsesIskK8TVgmRiHwO4KXlV2Li9dANA==} + resolution: {integrity: sha512-OwpTSOfy6xSs1+pwcNrv0RBMOzI39Lp3qQKUTPVVPRjCdNa5JH/oPRiqsesIskK8TVgmRiHwO4KXlV2Li9dANA==, tarball: https://registry.npmjs.org/select/-/select-1.1.2.tgz} selfsigned@2.4.1: - resolution: {integrity: sha512-th5B4L2U+eGLq1TVh7zNRGBapioSORUeymIydxgFpwww9d2qyKvtuPU2jJuHvYAwwqi2Y596QBL3eEqcPEYL8Q==} + resolution: {integrity: sha512-th5B4L2U+eGLq1TVh7zNRGBapioSORUeymIydxgFpwww9d2qyKvtuPU2jJuHvYAwwqi2Y596QBL3eEqcPEYL8Q==, tarball: https://registry.npmjs.org/selfsigned/-/selfsigned-2.4.1.tgz} engines: {node: '>=10'} selfsigned@5.5.0: - resolution: {integrity: sha512-ftnu3TW4+3eBfLRFnDEkzGxSF/10BJBkaLJuBHZX0kiPS7bRdlpZGu6YGt4KngMkdTwJE6MbjavFpqHvqVt+Ew==} + resolution: {integrity: sha512-ftnu3TW4+3eBfLRFnDEkzGxSF/10BJBkaLJuBHZX0kiPS7bRdlpZGu6YGt4KngMkdTwJE6MbjavFpqHvqVt+Ew==, tarball: https://registry.npmjs.org/selfsigned/-/selfsigned-5.5.0.tgz} engines: {node: '>=18'} semver@5.7.2: - resolution: {integrity: sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==} + resolution: {integrity: sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==, tarball: https://registry.npmjs.org/semver/-/semver-5.7.2.tgz} hasBin: true semver@6.3.1: - resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} + resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==, tarball: https://registry.npmjs.org/semver/-/semver-6.3.1.tgz} hasBin: true semver@7.6.3: - resolution: {integrity: sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==} + resolution: {integrity: sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==, tarball: https://registry.npmjs.org/semver/-/semver-7.6.3.tgz} engines: {node: '>=10'} hasBin: true semver@7.7.4: - resolution: {integrity: sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==} + resolution: {integrity: sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==, tarball: https://registry.npmjs.org/semver/-/semver-7.7.4.tgz} engines: {node: '>=10'} hasBin: true semver@7.8.4: - resolution: {integrity: sha512-rUCObTnP32Q08R2uuIrt7r9PlEonuTmtuXYcW6s5kjdlj3xbnwe+21yXptAUYcMAABLkYYTtnmzb3w3EDZfueA==} + resolution: {integrity: sha512-rUCObTnP32Q08R2uuIrt7r9PlEonuTmtuXYcW6s5kjdlj3xbnwe+21yXptAUYcMAABLkYYTtnmzb3w3EDZfueA==, tarball: https://registry.npmjs.org/semver/-/semver-7.8.4.tgz} engines: {node: '>=10'} hasBin: true semver@7.8.5: - resolution: {integrity: sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==} + resolution: {integrity: sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==, tarball: https://registry.npmjs.org/semver/-/semver-7.8.5.tgz} engines: {node: '>=10'} hasBin: true send@0.19.2: - resolution: {integrity: sha512-VMbMxbDeehAxpOtWJXlcUS5E8iXh6QmN+BkRX1GARS3wRaXEEgzCcB10gTQazO42tpNIya8xIyNx8fll1OFPrg==} + resolution: {integrity: sha512-VMbMxbDeehAxpOtWJXlcUS5E8iXh6QmN+BkRX1GARS3wRaXEEgzCcB10gTQazO42tpNIya8xIyNx8fll1OFPrg==, tarball: https://registry.npmjs.org/send/-/send-0.19.2.tgz} engines: {node: '>= 0.8.0'} send@1.2.1: - resolution: {integrity: sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ==} + resolution: {integrity: sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ==, tarball: https://registry.npmjs.org/send/-/send-1.2.1.tgz} engines: {node: '>= 18'} serialize-javascript@6.0.2: - resolution: {integrity: sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==} + resolution: {integrity: sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==, tarball: https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz} serialize-javascript@7.0.7: - resolution: {integrity: sha512-YAy8Od6KV+uuwUuU50np8fGB/Aues6Y0nAhA9y/hId74PlKUcme4pXcBD46NWKr1Q4osN/iseZ17YqO1XfmI8g==} + resolution: {integrity: sha512-YAy8Od6KV+uuwUuU50np8fGB/Aues6Y0nAhA9y/hId74PlKUcme4pXcBD46NWKr1Q4osN/iseZ17YqO1XfmI8g==, tarball: https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-7.0.7.tgz} engines: {node: '>=20.0.0'} serve-index@1.9.2: - resolution: {integrity: sha512-KDj11HScOaLmrPxl70KYNW1PksP4Nb/CLL2yvC+Qd2kHMPEEpfc4Re2e4FOay+bC/+XQl/7zAcWON3JVo5v3KQ==} + resolution: {integrity: sha512-KDj11HScOaLmrPxl70KYNW1PksP4Nb/CLL2yvC+Qd2kHMPEEpfc4Re2e4FOay+bC/+XQl/7zAcWON3JVo5v3KQ==, tarball: https://registry.npmjs.org/serve-index/-/serve-index-1.9.2.tgz} engines: {node: '>= 0.8.0'} serve-static@1.16.3: - resolution: {integrity: sha512-x0RTqQel6g5SY7Lg6ZreMmsOzncHFU7nhnRWkKgWuMTu5NN0DR5oruckMqRvacAN9d5w6ARnRBXl9xhDCgfMeA==} + resolution: {integrity: sha512-x0RTqQel6g5SY7Lg6ZreMmsOzncHFU7nhnRWkKgWuMTu5NN0DR5oruckMqRvacAN9d5w6ARnRBXl9xhDCgfMeA==, tarball: https://registry.npmjs.org/serve-static/-/serve-static-1.16.3.tgz} engines: {node: '>= 0.8.0'} serve-static@2.2.1: - resolution: {integrity: sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw==} + resolution: {integrity: sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw==, tarball: https://registry.npmjs.org/serve-static/-/serve-static-2.2.1.tgz} engines: {node: '>= 18'} set-function-length@1.2.2: - resolution: {integrity: sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==} + resolution: {integrity: sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==, tarball: https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz} engines: {node: '>= 0.4'} set-function-name@2.0.2: - resolution: {integrity: sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==} + resolution: {integrity: sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==, tarball: https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.2.tgz} engines: {node: '>= 0.4'} set-proto@1.0.0: - resolution: {integrity: sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw==} + resolution: {integrity: sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw==, tarball: https://registry.npmjs.org/set-proto/-/set-proto-1.0.0.tgz} engines: {node: '>= 0.4'} setprototypeof@1.2.0: - resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==} + resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==, tarball: https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz} shallow-clone@3.0.1: - resolution: {integrity: sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==} + resolution: {integrity: sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==, tarball: https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz} engines: {node: '>=8'} shebang-command@2.0.0: - resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} + resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==, tarball: https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz} engines: {node: '>=8'} shebang-regex@3.0.0: - resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} + resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==, tarball: https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz} engines: {node: '>=8'} shell-exec@1.0.2: - resolution: {integrity: sha512-jyVd+kU2X+mWKMmGhx4fpWbPsjvD53k9ivqetutVW/BQ+WIZoDoP4d8vUMGezV6saZsiNoW2f9GIhg9Dondohg==} + resolution: {integrity: sha512-jyVd+kU2X+mWKMmGhx4fpWbPsjvD53k9ivqetutVW/BQ+WIZoDoP4d8vUMGezV6saZsiNoW2f9GIhg9Dondohg==, tarball: https://registry.npmjs.org/shell-exec/-/shell-exec-1.0.2.tgz} shell-quote@1.10.0: - resolution: {integrity: sha512-w1aiOKwKuRgtwAReIIj89puqg+I7GvX4IbLrvmhXbzQsj1+Zwi4VO3+fa6ZF91TWSjIxoEkKnMeHcLEODK5ZXA==} + resolution: {integrity: sha512-w1aiOKwKuRgtwAReIIj89puqg+I7GvX4IbLrvmhXbzQsj1+Zwi4VO3+fa6ZF91TWSjIxoEkKnMeHcLEODK5ZXA==, tarball: https://registry.npmjs.org/shell-quote/-/shell-quote-1.10.0.tgz} engines: {node: '>= 0.4'} shiki@0.14.7: - resolution: {integrity: sha512-dNPAPrxSc87ua2sKJ3H5dQ/6ZaY8RNnaAqK+t0eG7p0Soi2ydiqbGOTaZCqaYvA/uZYfS1LJnemt3Q+mSfcPCg==} + resolution: {integrity: sha512-dNPAPrxSc87ua2sKJ3H5dQ/6ZaY8RNnaAqK+t0eG7p0Soi2ydiqbGOTaZCqaYvA/uZYfS1LJnemt3Q+mSfcPCg==, tarball: https://registry.npmjs.org/shiki/-/shiki-0.14.7.tgz} side-channel-list@1.0.1: - resolution: {integrity: sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w==} + resolution: {integrity: sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w==, tarball: https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.1.tgz} engines: {node: '>= 0.4'} side-channel-map@1.0.1: - resolution: {integrity: sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==} + resolution: {integrity: sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==, tarball: https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz} engines: {node: '>= 0.4'} side-channel-weakmap@1.0.2: - resolution: {integrity: sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==} + resolution: {integrity: sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==, tarball: https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz} engines: {node: '>= 0.4'} side-channel@1.1.1: - resolution: {integrity: sha512-6x6dK6zJdpTzF4sQeNYxwtvBzf6Eg4GtlesS94HOvTudUeyK2WXAaIfmDgsyslYrRBeFIlsi54AYsFGUuhmvrQ==} + resolution: {integrity: sha512-6x6dK6zJdpTzF4sQeNYxwtvBzf6Eg4GtlesS94HOvTudUeyK2WXAaIfmDgsyslYrRBeFIlsi54AYsFGUuhmvrQ==, tarball: https://registry.npmjs.org/side-channel/-/side-channel-1.1.1.tgz} engines: {node: '>= 0.4'} siginfo@2.0.0: - resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==} + resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==, tarball: https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz} signal-exit@3.0.7: - resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==} + resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==, tarball: https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz} signal-exit@4.1.0: - resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} + resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==, tarball: https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz} engines: {node: '>=14'} sirv@1.0.19: - resolution: {integrity: sha512-JuLThK3TnZG1TAKDwNIqNq6QA2afLOCcm+iE8D1Kj3GA40pSPsxQjjJl0J8X3tsR7T+CP1GavpzLwYkgVLWrZQ==} + resolution: {integrity: sha512-JuLThK3TnZG1TAKDwNIqNq6QA2afLOCcm+iE8D1Kj3GA40pSPsxQjjJl0J8X3tsR7T+CP1GavpzLwYkgVLWrZQ==, tarball: https://registry.npmjs.org/sirv/-/sirv-1.0.19.tgz} engines: {node: '>= 10'} sirv@3.0.2: - resolution: {integrity: sha512-2wcC/oGxHis/BoHkkPwldgiPSYcpZK3JU28WoMVv55yHJgcZ8rlXvuG9iZggz+sU1d4bRgIGASwyWqjxu3FM0g==} + resolution: {integrity: sha512-2wcC/oGxHis/BoHkkPwldgiPSYcpZK3JU28WoMVv55yHJgcZ8rlXvuG9iZggz+sU1d4bRgIGASwyWqjxu3FM0g==, tarball: https://registry.npmjs.org/sirv/-/sirv-3.0.2.tgz} engines: {node: '>=18'} slash@3.0.0: - resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} + resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==, tarball: https://registry.npmjs.org/slash/-/slash-3.0.0.tgz} engines: {node: '>=8'} slash@4.0.0: - resolution: {integrity: sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew==} + resolution: {integrity: sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew==, tarball: https://registry.npmjs.org/slash/-/slash-4.0.0.tgz} engines: {node: '>=12'} slice-ansi@5.0.0: - resolution: {integrity: sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ==} + resolution: {integrity: sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ==, tarball: https://registry.npmjs.org/slice-ansi/-/slice-ansi-5.0.0.tgz} engines: {node: '>=12'} slice-ansi@7.1.2: - resolution: {integrity: sha512-iOBWFgUX7caIZiuutICxVgX1SdxwAVFFKwt1EvMYYec/NWO5meOJ6K5uQxhrYBdQJne4KxiqZc+KptFOWFSI9w==} + resolution: {integrity: sha512-iOBWFgUX7caIZiuutICxVgX1SdxwAVFFKwt1EvMYYec/NWO5meOJ6K5uQxhrYBdQJne4KxiqZc+KptFOWFSI9w==, tarball: https://registry.npmjs.org/slice-ansi/-/slice-ansi-7.1.2.tgz} engines: {node: '>=18'} slice-ansi@8.0.0: - resolution: {integrity: sha512-stxByr12oeeOyY2BlviTNQlYV5xOj47GirPr4yA1hE9JCtxfQN0+tVbkxwCtYDQWhEKWFHsEK48ORg5jrouCAg==} + resolution: {integrity: sha512-stxByr12oeeOyY2BlviTNQlYV5xOj47GirPr4yA1hE9JCtxfQN0+tVbkxwCtYDQWhEKWFHsEK48ORg5jrouCAg==, tarball: https://registry.npmjs.org/slice-ansi/-/slice-ansi-8.0.0.tgz} engines: {node: '>=20'} smol-toml@1.6.1: - resolution: {integrity: sha512-dWUG8F5sIIARXih1DTaQAX4SsiTXhInKf1buxdY9DIg4ZYPZK5nGM1VRIYmEbDbsHt7USo99xSLFu5Q1IqTmsg==} + resolution: {integrity: sha512-dWUG8F5sIIARXih1DTaQAX4SsiTXhInKf1buxdY9DIg4ZYPZK5nGM1VRIYmEbDbsHt7USo99xSLFu5Q1IqTmsg==, tarball: https://registry.npmjs.org/smol-toml/-/smol-toml-1.6.1.tgz} engines: {node: '>= 18'} snake-case@3.0.4: - resolution: {integrity: sha512-LAOh4z89bGQvl9pFfNF8V146i7o7/CqFPbqzYgP+yYzDIDeS9HaNFtXABamRW+AQzEVODcvE79ljJ+8a9YSdMg==} + resolution: {integrity: sha512-LAOh4z89bGQvl9pFfNF8V146i7o7/CqFPbqzYgP+yYzDIDeS9HaNFtXABamRW+AQzEVODcvE79ljJ+8a9YSdMg==, tarball: https://registry.npmjs.org/snake-case/-/snake-case-3.0.4.tgz} sockjs@0.3.24: - resolution: {integrity: sha512-GJgLTZ7vYb/JtPSSZ10hsOYIvEYsjbNU+zPdIHcUaWVNUEPivzxku31865sSSud0Da0W4lEeOPlmw93zLQchuQ==} + resolution: {integrity: sha512-GJgLTZ7vYb/JtPSSZ10hsOYIvEYsjbNU+zPdIHcUaWVNUEPivzxku31865sSSud0Da0W4lEeOPlmw93zLQchuQ==, tarball: https://registry.npmjs.org/sockjs/-/sockjs-0.3.24.tgz} sorted-array-functions@1.3.0: - resolution: {integrity: sha512-2sqgzeFlid6N4Z2fUQ1cvFmTOLRi/sEDzSQ0OKYchqgoPmQBVyM3959qYx3fpS6Esef80KjmpgPeEr028dP3OA==} + resolution: {integrity: sha512-2sqgzeFlid6N4Z2fUQ1cvFmTOLRi/sEDzSQ0OKYchqgoPmQBVyM3959qYx3fpS6Esef80KjmpgPeEr028dP3OA==, tarball: https://registry.npmjs.org/sorted-array-functions/-/sorted-array-functions-1.3.0.tgz} source-map-js@1.2.1: - resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} + resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==, tarball: https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz} engines: {node: '>=0.10.0'} source-map-loader@5.0.0: - resolution: {integrity: sha512-k2Dur7CbSLcAH73sBcIkV5xjPV4SzqO1NJ7+XaQl8if3VODDUj3FNchNGpqgJSKbvUfJuhVdv8K2Eu8/TNl2eA==} + resolution: {integrity: sha512-k2Dur7CbSLcAH73sBcIkV5xjPV4SzqO1NJ7+XaQl8if3VODDUj3FNchNGpqgJSKbvUfJuhVdv8K2Eu8/TNl2eA==, tarball: https://registry.npmjs.org/source-map-loader/-/source-map-loader-5.0.0.tgz} engines: {node: '>= 18.12.0'} peerDependencies: webpack: ^5.72.1 source-map-support@0.5.13: - resolution: {integrity: sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==} + resolution: {integrity: sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==, tarball: https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz} source-map-support@0.5.19: - resolution: {integrity: sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw==} + resolution: {integrity: sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw==, tarball: https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.19.tgz} source-map-support@0.5.21: - resolution: {integrity: sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==} + resolution: {integrity: sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==, tarball: https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz} source-map@0.6.1: - resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} + resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==, tarball: https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz} engines: {node: '>=0.10.0'} source-map@0.7.6: - resolution: {integrity: sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ==} + resolution: {integrity: sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ==, tarball: https://registry.npmjs.org/source-map/-/source-map-0.7.6.tgz} engines: {node: '>= 12'} spdy-transport@3.0.0: - resolution: {integrity: sha512-hsLVFE5SjA6TCisWeJXFKniGGOpBgMLmerfO2aCyCU5s7nJ/rpAepqmFifv/GCbSbueEeAJJnmSQ2rKC/g8Fcw==} + resolution: {integrity: sha512-hsLVFE5SjA6TCisWeJXFKniGGOpBgMLmerfO2aCyCU5s7nJ/rpAepqmFifv/GCbSbueEeAJJnmSQ2rKC/g8Fcw==, tarball: https://registry.npmjs.org/spdy-transport/-/spdy-transport-3.0.0.tgz} spdy@4.0.2: - resolution: {integrity: sha512-r46gZQZQV+Kl9oItvl1JZZqJKGr+oEkB08A6BzkiR7593/7IbtuncXHd2YoYeTsG4157ZssMu9KYvUHLcjcDoA==} + resolution: {integrity: sha512-r46gZQZQV+Kl9oItvl1JZZqJKGr+oEkB08A6BzkiR7593/7IbtuncXHd2YoYeTsG4157ZssMu9KYvUHLcjcDoA==, tarball: https://registry.npmjs.org/spdy/-/spdy-4.0.2.tgz} engines: {node: '>=6.0.0'} sprintf-js@1.0.3: - resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==} + resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==, tarball: https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz} stable@0.1.8: - resolution: {integrity: sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w==} + resolution: {integrity: sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w==, tarball: https://registry.npmjs.org/stable/-/stable-0.1.8.tgz} 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==} + resolution: {integrity: sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==, tarball: https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz} engines: {node: '>=10'} stackback@0.0.2: - resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} + resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==, tarball: https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz} stackframe@1.3.4: - resolution: {integrity: sha512-oeVtt7eWQS+Na6F//S4kJ2K2VbRlS9D43mAlMyVpVWovy9o+jfgH8O9agzANzaiLjclA0oYzUXEM4PurhSUChw==} + resolution: {integrity: sha512-oeVtt7eWQS+Na6F//S4kJ2K2VbRlS9D43mAlMyVpVWovy9o+jfgH8O9agzANzaiLjclA0oYzUXEM4PurhSUChw==, tarball: https://registry.npmjs.org/stackframe/-/stackframe-1.3.4.tgz} statuses@1.5.0: - resolution: {integrity: sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==} + resolution: {integrity: sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==, tarball: https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz} engines: {node: '>= 0.6'} statuses@2.0.2: - resolution: {integrity: sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==} + resolution: {integrity: sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==, tarball: https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz} engines: {node: '>= 0.8'} std-env@3.10.0: - resolution: {integrity: sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==} + resolution: {integrity: sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==, tarball: https://registry.npmjs.org/std-env/-/std-env-3.10.0.tgz} std-env@4.2.0: - resolution: {integrity: sha512-oCUKSupKTHX53EyjDtuZQ64pjLJ6yYCtpmEw0goYxtjG9KpbRe8KAsl2tBUGU9DyMcJ0RwJ8GqJAFzMXcXW1Rw==} + resolution: {integrity: sha512-oCUKSupKTHX53EyjDtuZQ64pjLJ6yYCtpmEw0goYxtjG9KpbRe8KAsl2tBUGU9DyMcJ0RwJ8GqJAFzMXcXW1Rw==, tarball: https://registry.npmjs.org/std-env/-/std-env-4.2.0.tgz} stdin-discarder@0.2.2: - resolution: {integrity: sha512-UhDfHmA92YAlNnCfhmq0VeNL5bDbiZGg7sZ2IvPsXubGkiNa9EC+tUTsjBRsYUAz87btI6/1wf4XoVvQ3uRnmQ==} + resolution: {integrity: sha512-UhDfHmA92YAlNnCfhmq0VeNL5bDbiZGg7sZ2IvPsXubGkiNa9EC+tUTsjBRsYUAz87btI6/1wf4XoVvQ3uRnmQ==, tarball: https://registry.npmjs.org/stdin-discarder/-/stdin-discarder-0.2.2.tgz} engines: {node: '>=18'} stdin-discarder@0.3.2: - resolution: {integrity: sha512-eCPu1qRxPVkl5605OTWF8Wz40b4Mf45NY5LQmVPQ599knfs5QhASUm9GbJ5BDMDOXgrnh0wyEdvzmL//YMlw0A==} + resolution: {integrity: sha512-eCPu1qRxPVkl5605OTWF8Wz40b4Mf45NY5LQmVPQ599knfs5QhASUm9GbJ5BDMDOXgrnh0wyEdvzmL//YMlw0A==, tarball: https://registry.npmjs.org/stdin-discarder/-/stdin-discarder-0.3.2.tgz} engines: {node: '>=18'} stop-iteration-iterator@1.1.0: - resolution: {integrity: sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==} + resolution: {integrity: sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==, tarball: https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.1.0.tgz} engines: {node: '>= 0.4'} storybook@9.1.20: - resolution: {integrity: sha512-6rME2tww6PFhm96iG2Xx44yzwLDWBiDWy+kJ2ub6x90werSTOiuo+tZJ94BgCfFutR0tEfLRIq59s+Zg6YyChA==} + resolution: {integrity: sha512-6rME2tww6PFhm96iG2Xx44yzwLDWBiDWy+kJ2ub6x90werSTOiuo+tZJ94BgCfFutR0tEfLRIq59s+Zg6YyChA==, tarball: https://registry.npmjs.org/storybook/-/storybook-9.1.20.tgz} hasBin: true peerDependencies: prettier: ^2 || ^3 @@ -13970,120 +14033,120 @@ packages: optional: true streamroller@3.1.5: - resolution: {integrity: sha512-KFxaM7XT+irxvdqSP1LGLgNWbYN7ay5owZ3r/8t77p+EtSUAfUgtl7be3xtqtOmGUl9K9YPO2ca8133RlTjvKw==} + resolution: {integrity: sha512-KFxaM7XT+irxvdqSP1LGLgNWbYN7ay5owZ3r/8t77p+EtSUAfUgtl7be3xtqtOmGUl9K9YPO2ca8133RlTjvKw==, tarball: https://registry.npmjs.org/streamroller/-/streamroller-3.1.5.tgz} engines: {node: '>=8.0'} streamsearch@1.1.0: - resolution: {integrity: sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==} + resolution: {integrity: sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==, tarball: https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz} engines: {node: '>=10.0.0'} string-argv@0.3.2: - resolution: {integrity: sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==} + resolution: {integrity: sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==, tarball: https://registry.npmjs.org/string-argv/-/string-argv-0.3.2.tgz} engines: {node: '>=0.6.19'} string-hash@1.1.3: - resolution: {integrity: sha512-kJUvRUFK49aub+a7T1nNE66EJbZBMnBgoC1UbCZ5n6bsZKBRga4KgBRTMn/pFkeCZSYtNeSyMxPDM0AXWELk2A==} + resolution: {integrity: sha512-kJUvRUFK49aub+a7T1nNE66EJbZBMnBgoC1UbCZ5n6bsZKBRga4KgBRTMn/pFkeCZSYtNeSyMxPDM0AXWELk2A==, tarball: https://registry.npmjs.org/string-hash/-/string-hash-1.1.3.tgz} string-length@4.0.2: - resolution: {integrity: sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==} + resolution: {integrity: sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==, tarball: https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz} engines: {node: '>=10'} string-width@4.2.3: - resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} + resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==, tarball: https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz} engines: {node: '>=8'} string-width@5.1.2: - resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==} + resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==, tarball: https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz} engines: {node: '>=12'} string-width@7.2.0: - resolution: {integrity: sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==} + resolution: {integrity: sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==, tarball: https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz} engines: {node: '>=18'} string-width@8.2.2: - resolution: {integrity: sha512-GaPUh5gfdrYzqeVNZvUfT23vYYxXzKYidUcnMtJg/3rxRV63EFZy3k6xfKlmfeJD0176lnUV/Usr3XcwSvFzpg==} + resolution: {integrity: sha512-GaPUh5gfdrYzqeVNZvUfT23vYYxXzKYidUcnMtJg/3rxRV63EFZy3k6xfKlmfeJD0176lnUV/Usr3XcwSvFzpg==, tarball: https://registry.npmjs.org/string-width/-/string-width-8.2.2.tgz} engines: {node: '>=20'} string.prototype.includes@2.0.1: - resolution: {integrity: sha512-o7+c9bW6zpAdJHTtujeePODAhkuicdAryFsfVKwA+wGw89wJ4GTY484WTucM9hLtDEOpOvI+aHnzqnC5lHp4Rg==} + resolution: {integrity: sha512-o7+c9bW6zpAdJHTtujeePODAhkuicdAryFsfVKwA+wGw89wJ4GTY484WTucM9hLtDEOpOvI+aHnzqnC5lHp4Rg==, tarball: https://registry.npmjs.org/string.prototype.includes/-/string.prototype.includes-2.0.1.tgz} engines: {node: '>= 0.4'} string.prototype.matchall@4.0.12: - resolution: {integrity: sha512-6CC9uyBL+/48dYizRf7H7VAYCMCNTBeM78x/VTUe9bFEaxBepPJDa1Ow99LqI/1yF7kuy7Q3cQsYMrcjGUcskA==} + resolution: {integrity: sha512-6CC9uyBL+/48dYizRf7H7VAYCMCNTBeM78x/VTUe9bFEaxBepPJDa1Ow99LqI/1yF7kuy7Q3cQsYMrcjGUcskA==, tarball: https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.12.tgz} engines: {node: '>= 0.4'} string.prototype.repeat@1.0.0: - resolution: {integrity: sha512-0u/TldDbKD8bFCQ/4f5+mNRrXwZ8hg2w7ZR8wa16e8z9XpePWl3eGEcUD0OXpEH/VJH/2G3gjUtR3ZOiBe2S/w==} + resolution: {integrity: sha512-0u/TldDbKD8bFCQ/4f5+mNRrXwZ8hg2w7ZR8wa16e8z9XpePWl3eGEcUD0OXpEH/VJH/2G3gjUtR3ZOiBe2S/w==, tarball: https://registry.npmjs.org/string.prototype.repeat/-/string.prototype.repeat-1.0.0.tgz} string.prototype.trim@1.2.11: - resolution: {integrity: sha512-PwvK7BU+CMTJGYQCTZb5RWXIML92lftJLhQz1tBzgKiqGxJaMlBAa48POXaNAC2s4y8jr3EFqrkF9+44neS46w==} + resolution: {integrity: sha512-PwvK7BU+CMTJGYQCTZb5RWXIML92lftJLhQz1tBzgKiqGxJaMlBAa48POXaNAC2s4y8jr3EFqrkF9+44neS46w==, tarball: https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.11.tgz} engines: {node: '>= 0.4'} string.prototype.trimend@1.0.10: - resolution: {integrity: sha512-2+3aDAOmPTmuFwjDnmJG2ctEkQKVki7vOSqaxkv42Mowj1V6PnvuwFCRrR5lChUux1TBskPjfkeTOhqczDMxTw==} + resolution: {integrity: sha512-2+3aDAOmPTmuFwjDnmJG2ctEkQKVki7vOSqaxkv42Mowj1V6PnvuwFCRrR5lChUux1TBskPjfkeTOhqczDMxTw==, tarball: https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.10.tgz} engines: {node: '>= 0.4'} string.prototype.trimstart@1.0.8: - resolution: {integrity: sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==} + resolution: {integrity: sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==, tarball: https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz} engines: {node: '>= 0.4'} string_decoder@1.1.1: - resolution: {integrity: sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==} + resolution: {integrity: sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==, tarball: https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz} string_decoder@1.3.0: - resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==} + resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==, tarball: https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz} strip-ansi@6.0.1: - resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} + resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==, tarball: https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz} engines: {node: '>=8'} strip-ansi@7.2.0: - resolution: {integrity: sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==} + resolution: {integrity: sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==, tarball: https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz} engines: {node: '>=12'} strip-bom@3.0.0: - resolution: {integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==} + resolution: {integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==, tarball: https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz} engines: {node: '>=4'} strip-bom@4.0.0: - resolution: {integrity: sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==} + resolution: {integrity: sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==, tarball: https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz} engines: {node: '>=8'} strip-final-newline@2.0.0: - resolution: {integrity: sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==} + resolution: {integrity: sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==, tarball: https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz} engines: {node: '>=6'} strip-final-newline@3.0.0: - resolution: {integrity: sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==} + resolution: {integrity: sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==, tarball: https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz} engines: {node: '>=12'} strip-final-newline@4.0.0: - resolution: {integrity: sha512-aulFJcD6YK8V1G7iRB5tigAP4TsHBZZrOV8pjV++zdUwmeV8uzbY7yn6h9MswN62adStNZFuCIx4haBnRuMDaw==} + resolution: {integrity: sha512-aulFJcD6YK8V1G7iRB5tigAP4TsHBZZrOV8pjV++zdUwmeV8uzbY7yn6h9MswN62adStNZFuCIx4haBnRuMDaw==, tarball: https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-4.0.0.tgz} engines: {node: '>=18'} strip-indent@3.0.0: - resolution: {integrity: sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==} + resolution: {integrity: sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==, tarball: https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz} engines: {node: '>=8'} strip-json-comments@3.1.1: - resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} + resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==, tarball: https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz} engines: {node: '>=8'} strip-outer@1.0.1: - resolution: {integrity: sha512-k55yxKHwaXnpYGsOzg4Vl8+tDrWylxDEpknGjhTiZB8dFRU5rTo9CAzeycivxV3s+zlTKwrs6WxMxR95n26kwg==} + resolution: {integrity: sha512-k55yxKHwaXnpYGsOzg4Vl8+tDrWylxDEpknGjhTiZB8dFRU5rTo9CAzeycivxV3s+zlTKwrs6WxMxR95n26kwg==, tarball: https://registry.npmjs.org/strip-outer/-/strip-outer-1.0.1.tgz} engines: {node: '>=0.10.0'} style-inject@0.3.0: - resolution: {integrity: sha512-IezA2qp+vcdlhJaVm5SOdPPTUu0FCEqfNSli2vRuSIBbu5Nq5UvygTk/VzeCqfLz2Atj3dVII5QBKGZRZ0edzw==} + resolution: {integrity: sha512-IezA2qp+vcdlhJaVm5SOdPPTUu0FCEqfNSli2vRuSIBbu5Nq5UvygTk/VzeCqfLz2Atj3dVII5QBKGZRZ0edzw==, tarball: https://registry.npmjs.org/style-inject/-/style-inject-0.3.0.tgz} style-loader@3.3.4: - resolution: {integrity: sha512-0WqXzrsMTyb8yjZJHDqwmnwRJvhALK9LfRtRc6B4UTWe8AijYLZYZ9thuJTZc2VfQWINADW/j+LiJnfy2RoC1w==} + resolution: {integrity: sha512-0WqXzrsMTyb8yjZJHDqwmnwRJvhALK9LfRtRc6B4UTWe8AijYLZYZ9thuJTZc2VfQWINADW/j+LiJnfy2RoC1w==, tarball: https://registry.npmjs.org/style-loader/-/style-loader-3.3.4.tgz} engines: {node: '>= 12.13.0'} peerDependencies: webpack: ^5.0.0 styled-jsx@5.1.1: - resolution: {integrity: sha512-pW7uC1l4mBZ8ugbiZrcIsiIvVx1UmTfw7UkC3Um2tmfUq9Bhk8IiyEIPl6F8agHgjzku6j0xQEZbfA5uSgSaCw==} + resolution: {integrity: sha512-pW7uC1l4mBZ8ugbiZrcIsiIvVx1UmTfw7UkC3Um2tmfUq9Bhk8IiyEIPl6F8agHgjzku6j0xQEZbfA5uSgSaCw==, tarball: https://registry.npmjs.org/styled-jsx/-/styled-jsx-5.1.1.tgz} engines: {node: '>= 12.0.0'} peerDependencies: '@babel/core': '*' @@ -14096,77 +14159,77 @@ packages: optional: true stylehacks@5.1.1: - resolution: {integrity: sha512-sBpcd5Hx7G6seo7b1LkpttvTz7ikD0LlH5RmdcBNb6fFR0Fl7LQwHDFr300q4cwUqi+IYrFGmsIHieMBfnN/Bw==} + resolution: {integrity: sha512-sBpcd5Hx7G6seo7b1LkpttvTz7ikD0LlH5RmdcBNb6fFR0Fl7LQwHDFr300q4cwUqi+IYrFGmsIHieMBfnN/Bw==, tarball: https://registry.npmjs.org/stylehacks/-/stylehacks-5.1.1.tgz} engines: {node: ^10 || ^12 || >=14.0} peerDependencies: postcss: ^8.2.15 stylehacks@6.1.1: - resolution: {integrity: sha512-gSTTEQ670cJNoaeIp9KX6lZmm8LJ3jPB5yJmX8Zq/wQxOsAFXV3qjWzHas3YYk1qesuVIyYWWUpZ0vSE/dTSGg==} + resolution: {integrity: sha512-gSTTEQ670cJNoaeIp9KX6lZmm8LJ3jPB5yJmX8Zq/wQxOsAFXV3qjWzHas3YYk1qesuVIyYWWUpZ0vSE/dTSGg==, tarball: https://registry.npmjs.org/stylehacks/-/stylehacks-6.1.1.tgz} engines: {node: ^14 || ^16 || >=18.0} peerDependencies: postcss: ^8.4.31 stylehacks@7.0.11: - resolution: {integrity: sha512-iODNfhXVLqc5LADs+Y6Oh5wJuK5ZcHbVng8aiK3y9pjMQdc5hLrBW0eFU6FtnpNrE6PoEg/MmFTU4waotj5WNg==} + resolution: {integrity: sha512-iODNfhXVLqc5LADs+Y6Oh5wJuK5ZcHbVng8aiK3y9pjMQdc5hLrBW0eFU6FtnpNrE6PoEg/MmFTU4waotj5WNg==, tarball: https://registry.npmjs.org/stylehacks/-/stylehacks-7.0.11.tgz} engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} peerDependencies: postcss: ^8.5.13 stylis@4.4.0: - resolution: {integrity: sha512-5Z9ZpRzfuH6l/UAvCPAPUo3665Nk2wLaZU3x+TLHKVzIz33+sbJqbtrYoC3KD4/uVOr2Zp+L0LySezP9OHV9yA==} + resolution: {integrity: sha512-5Z9ZpRzfuH6l/UAvCPAPUo3665Nk2wLaZU3x+TLHKVzIz33+sbJqbtrYoC3KD4/uVOr2Zp+L0LySezP9OHV9yA==, tarball: https://registry.npmjs.org/stylis/-/stylis-4.4.0.tgz} superstruct@1.0.3: - resolution: {integrity: sha512-8iTn3oSS8nRGn+C2pgXSKPI3jmpm6FExNazNpjvqS6ZUJQCej3PUXEKM8NjHBOs54ExM+LPW/FBRhymrdcCiSg==} + resolution: {integrity: sha512-8iTn3oSS8nRGn+C2pgXSKPI3jmpm6FExNazNpjvqS6ZUJQCej3PUXEKM8NjHBOs54ExM+LPW/FBRhymrdcCiSg==, tarball: https://registry.npmjs.org/superstruct/-/superstruct-1.0.3.tgz} engines: {node: '>=14.0.0'} supports-color@7.2.0: - resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} + resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==, tarball: https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz} engines: {node: '>=8'} supports-color@8.1.1: - resolution: {integrity: sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==} + resolution: {integrity: sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==, tarball: https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz} engines: {node: '>=10'} supports-preserve-symlinks-flag@1.0.0: - resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} + resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==, tarball: https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz} engines: {node: '>= 0.4'} svg-parser@2.0.4: - resolution: {integrity: sha512-e4hG1hRwoOdRb37cIMSgzNsxyzKfayW6VOflrwvR+/bzrkyxY/31WkbgnQpgtrNp1SdpJvpUAGTa/ZoiPNDuRQ==} + resolution: {integrity: sha512-e4hG1hRwoOdRb37cIMSgzNsxyzKfayW6VOflrwvR+/bzrkyxY/31WkbgnQpgtrNp1SdpJvpUAGTa/ZoiPNDuRQ==, tarball: https://registry.npmjs.org/svg-parser/-/svg-parser-2.0.4.tgz} svgo@2.8.3: - resolution: {integrity: sha512-5EZD0pafXX6PphdwOGCiVLDSaV1xyuQao2blHajHLsPxr07q4mmEjdtXEWgG07ae2mIz8Ex2CDXNCTiXhy3Khw==} + resolution: {integrity: sha512-5EZD0pafXX6PphdwOGCiVLDSaV1xyuQao2blHajHLsPxr07q4mmEjdtXEWgG07ae2mIz8Ex2CDXNCTiXhy3Khw==, tarball: https://registry.npmjs.org/svgo/-/svgo-2.8.3.tgz} engines: {node: '>=10.13.0'} hasBin: true svgo@3.3.4: - resolution: {integrity: sha512-GsNRis4e8jxn2Y9ENz/8lbJ93CstG8svtMnuRaHbiF2LTJ5tK0/q3t/URPq9Zc7zVWBJnNnJMIp6bevK7bSmNg==} + resolution: {integrity: sha512-GsNRis4e8jxn2Y9ENz/8lbJ93CstG8svtMnuRaHbiF2LTJ5tK0/q3t/URPq9Zc7zVWBJnNnJMIp6bevK7bSmNg==, tarball: https://registry.npmjs.org/svgo/-/svgo-3.3.4.tgz} engines: {node: '>=14.0.0'} hasBin: true svgo@4.0.2: - resolution: {integrity: sha512-ekx94z1rRc5LDi6oSUaeRnYhd0UOJxdtQCL2rF8xpWxD3TPAsISWOrxezqGovqS38GRZOdpDfvQe3ts6F7nsng==} + resolution: {integrity: sha512-ekx94z1rRc5LDi6oSUaeRnYhd0UOJxdtQCL2rF8xpWxD3TPAsISWOrxezqGovqS38GRZOdpDfvQe3ts6F7nsng==, tarball: https://registry.npmjs.org/svgo/-/svgo-4.0.2.tgz} engines: {node: '>=16'} hasBin: true symbol-tree@3.2.4: - resolution: {integrity: sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==} + resolution: {integrity: sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==, tarball: https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz} sync-child-process@1.0.2: - resolution: {integrity: sha512-8lD+t2KrrScJ/7KXCSyfhT3/hRq78rC0wBFqNJXv3mZyn6hW2ypM05JmlSvtqRbeq6jqA94oHbxAr2vYsJ8vDA==} + resolution: {integrity: sha512-8lD+t2KrrScJ/7KXCSyfhT3/hRq78rC0wBFqNJXv3mZyn6hW2ypM05JmlSvtqRbeq6jqA94oHbxAr2vYsJ8vDA==, tarball: https://registry.npmjs.org/sync-child-process/-/sync-child-process-1.0.2.tgz} engines: {node: '>=16.0.0'} sync-message-port@1.2.0: - resolution: {integrity: sha512-gAQ9qrUN/UCypHtGFbbe7Rc/f9bzO88IwrG8TDo/aMKAApKyD6E3W4Cm0EfhfBb6Z6SKt59tTCTfD+n1xmAvMg==} + resolution: {integrity: sha512-gAQ9qrUN/UCypHtGFbbe7Rc/f9bzO88IwrG8TDo/aMKAApKyD6E3W4Cm0EfhfBb6Z6SKt59tTCTfD+n1xmAvMg==, tarball: https://registry.npmjs.org/sync-message-port/-/sync-message-port-1.2.0.tgz} engines: {node: '>=16.0.0'} synckit@0.11.13: - resolution: {integrity: sha512-eNRKgb3z66Yp3D2CixVujOUvXLFUTij/zVnV8KRyvFdQwpz7I5DS8UfRkTeLzb64u+dkzDSdelE24izu+zSSUg==} + resolution: {integrity: sha512-eNRKgb3z66Yp3D2CixVujOUvXLFUTij/zVnV8KRyvFdQwpz7I5DS8UfRkTeLzb64u+dkzDSdelE24izu+zSSUg==, tarball: https://registry.npmjs.org/synckit/-/synckit-0.11.13.tgz} engines: {node: ^14.18.0 || >=16.0.0} tailwind-csstree@0.3.3: - resolution: {integrity: sha512-je9J5UYRsTJqAjYrIBMMlge8T/rreRd44pJxgG5Zx/zeo4kAC/liUKqzztRZrGlYRJLvIf2Cb1DVJMTXSzEShA==} + resolution: {integrity: sha512-je9J5UYRsTJqAjYrIBMMlge8T/rreRd44pJxgG5Zx/zeo4kAC/liUKqzztRZrGlYRJLvIf2Cb1DVJMTXSzEShA==, tarball: https://registry.npmjs.org/tailwind-csstree/-/tailwind-csstree-0.3.3.tgz} engines: {node: '>=18.18'} peerDependencies: '@eslint/css': '>=1.0.0' @@ -14175,33 +14238,33 @@ packages: optional: true tailwindcss-primeui@0.6.1: - resolution: {integrity: sha512-T69Rylcrmnt8zy9ik+qZvsLuRIrS9/k6rYJSIgZ1trnbEzGDDQSCIdmfyZknevqiHwpSJHSmQ9XT2C+S/hJY4A==} + resolution: {integrity: sha512-T69Rylcrmnt8zy9ik+qZvsLuRIrS9/k6rYJSIgZ1trnbEzGDDQSCIdmfyZknevqiHwpSJHSmQ9XT2C+S/hJY4A==, tarball: https://registry.npmjs.org/tailwindcss-primeui/-/tailwindcss-primeui-0.6.1.tgz} peerDependencies: tailwindcss: '>=3.1.0' tailwindcss@4.1.17: - resolution: {integrity: sha512-j9Ee2YjuQqYT9bbRTfTZht9W/ytp5H+jJpZKiYdP/bpnXARAuELt9ofP0lPnmHjbga7SNQIxdTAXCmtKVYjN+Q==} + resolution: {integrity: sha512-j9Ee2YjuQqYT9bbRTfTZht9W/ytp5H+jJpZKiYdP/bpnXARAuELt9ofP0lPnmHjbga7SNQIxdTAXCmtKVYjN+Q==, tarball: https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.17.tgz} tailwindcss@4.2.1: - resolution: {integrity: sha512-/tBrSQ36vCleJkAOsy9kbNTgaxvGbyOamC30PRePTQe/o1MFwEKHQk4Cn7BNGaPtjp+PuUrByJehM1hgxfq4sw==} + resolution: {integrity: sha512-/tBrSQ36vCleJkAOsy9kbNTgaxvGbyOamC30PRePTQe/o1MFwEKHQk4Cn7BNGaPtjp+PuUrByJehM1hgxfq4sw==, tarball: https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.2.1.tgz} tapable@2.3.0: - resolution: {integrity: sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg==} + resolution: {integrity: sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg==, tarball: https://registry.npmjs.org/tapable/-/tapable-2.3.0.tgz} engines: {node: '>=6'} tapable@2.3.3: - resolution: {integrity: sha512-uxc/zpqFg6x7C8vOE7lh6Lbda8eEL9zmVm/PLeTPBRhh1xCgdWaQ+J1CUieGpIfm2HdtsUpRv+HshiasBMcc6A==} + resolution: {integrity: sha512-uxc/zpqFg6x7C8vOE7lh6Lbda8eEL9zmVm/PLeTPBRhh1xCgdWaQ+J1CUieGpIfm2HdtsUpRv+HshiasBMcc6A==, tarball: https://registry.npmjs.org/tapable/-/tapable-2.3.3.tgz} engines: {node: '>=6'} tar-stream@2.2.0: - resolution: {integrity: sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==} + resolution: {integrity: sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==, tarball: https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz} engines: {node: '>=6'} tcp-port-used@1.0.3: - resolution: {integrity: sha512-4CEQ3qRJYo+mtEbJ+OoQu3dF4TDkwaO3RDVC4UzP5cpAOIUWwuwPjD7sdxDFFqsMUjsXVVYBMlg/boAaloThMA==} + resolution: {integrity: sha512-4CEQ3qRJYo+mtEbJ+OoQu3dF4TDkwaO3RDVC4UzP5cpAOIUWwuwPjD7sdxDFFqsMUjsXVVYBMlg/boAaloThMA==, tarball: https://registry.npmjs.org/tcp-port-used/-/tcp-port-used-1.0.3.tgz} terser-webpack-plugin@5.6.1: - resolution: {integrity: sha512-201R5j+sJpK8nFWwKVyNfZot8FaJbLZDq5evriVzbV1wDtSXDjRUDRfJzHpAaxFDMEhsZL1QkeqM61wgsS3KaQ==} + resolution: {integrity: sha512-201R5j+sJpK8nFWwKVyNfZot8FaJbLZDq5evriVzbV1wDtSXDjRUDRfJzHpAaxFDMEhsZL1QkeqM61wgsS3KaQ==, tarball: https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.6.1.tgz} engines: {node: '>= 10.13.0'} peerDependencies: '@minify-html/node': '*' @@ -14244,140 +14307,140 @@ packages: optional: true terser@5.28.1: - resolution: {integrity: sha512-wM+bZp54v/E9eRRGXb5ZFDvinrJIOaTapx3WUokyVGZu5ucVCK55zEgGd5Dl2fSr3jUo5sDiERErUWLY6QPFyA==} + resolution: {integrity: sha512-wM+bZp54v/E9eRRGXb5ZFDvinrJIOaTapx3WUokyVGZu5ucVCK55zEgGd5Dl2fSr3jUo5sDiERErUWLY6QPFyA==, tarball: https://registry.npmjs.org/terser/-/terser-5.28.1.tgz} engines: {node: '>=10'} hasBin: true terser@5.49.0: - resolution: {integrity: sha512-SNiDnXyHSrxVcIOtVbULzcTmniUiwcV7Nwdyj1twVubeTmbjoa8p69KKDpfkdoOavuM4/GRm1+ykI8qqnavHoA==} + resolution: {integrity: sha512-SNiDnXyHSrxVcIOtVbULzcTmniUiwcV7Nwdyj1twVubeTmbjoa8p69KKDpfkdoOavuM4/GRm1+ykI8qqnavHoA==, tarball: https://registry.npmjs.org/terser/-/terser-5.49.0.tgz} engines: {node: '>=10'} hasBin: true test-exclude@6.0.0: - resolution: {integrity: sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==} + resolution: {integrity: sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==, tarball: https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz} engines: {node: '>=8'} thingies@2.6.0: - resolution: {integrity: sha512-rMHRjmlFLM1R96UYPvpmnc3LYtdFrT33JIB7L9hetGue1qAPfn1N2LJeEjxUSidu1Iku+haLZXDuEXUHNGO/lg==} + resolution: {integrity: sha512-rMHRjmlFLM1R96UYPvpmnc3LYtdFrT33JIB7L9hetGue1qAPfn1N2LJeEjxUSidu1Iku+haLZXDuEXUHNGO/lg==, tarball: https://registry.npmjs.org/thingies/-/thingies-2.6.0.tgz} engines: {node: '>=10.18'} peerDependencies: tslib: ^2 thunky@1.1.0: - resolution: {integrity: sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==} + resolution: {integrity: sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==, tarball: https://registry.npmjs.org/thunky/-/thunky-1.1.0.tgz} ticky@1.0.1: - resolution: {integrity: sha512-RX35iq/D+lrsqhcPWIazM9ELkjOe30MSeoBHQHSsRwd1YuhJO5ui1K1/R0r7N3mFvbLBs33idw+eR6j+w6i/DA==} + resolution: {integrity: sha512-RX35iq/D+lrsqhcPWIazM9ELkjOe30MSeoBHQHSsRwd1YuhJO5ui1K1/R0r7N3mFvbLBs33idw+eR6j+w6i/DA==, tarball: https://registry.npmjs.org/ticky/-/ticky-1.0.1.tgz} tiny-emitter@2.1.0: - resolution: {integrity: sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q==} + resolution: {integrity: sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q==, tarball: https://registry.npmjs.org/tiny-emitter/-/tiny-emitter-2.1.0.tgz} tiny-invariant@1.3.3: - resolution: {integrity: sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==} + resolution: {integrity: sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==, tarball: https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.3.tgz} tinybench@2.9.0: - resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==} + resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==, tarball: https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz} tinyexec@1.2.4: - resolution: {integrity: sha512-SHf/r48b7vOrjve9PxJo3MN5v5yuyjHvdUcrQffT3WXMUfnGmHDVbC4k3sHJaJTgZCwpUplIaAo5ANtMyp3YHg==} + resolution: {integrity: sha512-SHf/r48b7vOrjve9PxJo3MN5v5yuyjHvdUcrQffT3WXMUfnGmHDVbC4k3sHJaJTgZCwpUplIaAo5ANtMyp3YHg==, tarball: https://registry.npmjs.org/tinyexec/-/tinyexec-1.2.4.tgz} engines: {node: '>=18'} tinyglobby@0.2.17: - resolution: {integrity: sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==} + resolution: {integrity: sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==, tarball: https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz} engines: {node: '>=12.0.0'} tinymce@6.8.3: - resolution: {integrity: sha512-3fCHKAeqT+xNwBVESf6iDbDV0VNwZNmfrkx9c/6Gz5iB8piMfaO6s7FvoiTrj1hf1gVbfyLTnz1DooI6DhgINQ==} + resolution: {integrity: sha512-3fCHKAeqT+xNwBVESf6iDbDV0VNwZNmfrkx9c/6Gz5iB8piMfaO6s7FvoiTrj1hf1gVbfyLTnz1DooI6DhgINQ==, tarball: https://registry.npmjs.org/tinymce/-/tinymce-6.8.3.tgz} tinyrainbow@2.0.0: - resolution: {integrity: sha512-op4nsTR47R6p0vMUUoYl/a+ljLFVtlfaXkLQmqfLR1qHma1h/ysYk4hEXZ880bf2CYgTskvTa/e196Vd5dDQXw==} + resolution: {integrity: sha512-op4nsTR47R6p0vMUUoYl/a+ljLFVtlfaXkLQmqfLR1qHma1h/ysYk4hEXZ880bf2CYgTskvTa/e196Vd5dDQXw==, tarball: https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-2.0.0.tgz} engines: {node: '>=14.0.0'} tinyrainbow@3.1.0: - resolution: {integrity: sha512-Bf+ILmBgretUrdJxzXM0SgXLZ3XfiaUuOj/IKQHuTXip+05Xn+uyEYdVg0kYDipTBcLrCVyUzAPz7QmArb0mmw==} + resolution: {integrity: sha512-Bf+ILmBgretUrdJxzXM0SgXLZ3XfiaUuOj/IKQHuTXip+05Xn+uyEYdVg0kYDipTBcLrCVyUzAPz7QmArb0mmw==, tarball: https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-3.1.0.tgz} engines: {node: '>=14.0.0'} tinyspy@4.0.4: - resolution: {integrity: sha512-azl+t0z7pw/z958Gy9svOTuzqIk6xq+NSheJzn5MMWtWTFywIacg2wUlzKFGtt3cthx0r2SxMK0yzJOR0IES7Q==} + resolution: {integrity: sha512-azl+t0z7pw/z958Gy9svOTuzqIk6xq+NSheJzn5MMWtWTFywIacg2wUlzKFGtt3cthx0r2SxMK0yzJOR0IES7Q==, tarball: https://registry.npmjs.org/tinyspy/-/tinyspy-4.0.4.tgz} engines: {node: '>=14.0.0'} tippy.js@6.3.7: - resolution: {integrity: sha512-E1d3oP2emgJ9dRQZdf3Kkn0qJgI6ZLpyS5z6ZkY1DF3kaQaBsGZsndEpHwx+eC+tYM41HaSNvNtLx8tU57FzTQ==} + resolution: {integrity: sha512-E1d3oP2emgJ9dRQZdf3Kkn0qJgI6ZLpyS5z6ZkY1DF3kaQaBsGZsndEpHwx+eC+tYM41HaSNvNtLx8tU57FzTQ==, tarball: https://registry.npmjs.org/tippy.js/-/tippy.js-6.3.7.tgz} tldts-core@7.4.8: - resolution: {integrity: sha512-c1P7u0EhACHj7lPy4MJm8iTFEU8+nB0LCtddH0fhP7noaVoXAqafMtOOeX+ulpuPBqnrRgRhw494RICT3mbhnw==} + resolution: {integrity: sha512-c1P7u0EhACHj7lPy4MJm8iTFEU8+nB0LCtddH0fhP7noaVoXAqafMtOOeX+ulpuPBqnrRgRhw494RICT3mbhnw==, tarball: https://registry.npmjs.org/tldts-core/-/tldts-core-7.4.8.tgz} tldts@7.4.8: - resolution: {integrity: sha512-htwgN/8KRB3z3vnC0BOETVh2m499g5GmyTK9Wq5JBLX3FNz6tSBveAd+fQhzy9hkjif8vy2jwDMR1sGhLtZl2A==} + resolution: {integrity: sha512-htwgN/8KRB3z3vnC0BOETVh2m499g5GmyTK9Wq5JBLX3FNz6tSBveAd+fQhzy9hkjif8vy2jwDMR1sGhLtZl2A==, tarball: https://registry.npmjs.org/tldts/-/tldts-7.4.8.tgz} hasBin: true tmp@0.2.7: - resolution: {integrity: sha512-e0votIpp4Uo2AJYSzVHV6xCcawuiez3DzqDAbrTc3YxBkplN6e+dM13ZeIcZnDg/QpSuU2zfZ3rzwY8ukEnaXw==} + resolution: {integrity: sha512-e0votIpp4Uo2AJYSzVHV6xCcawuiez3DzqDAbrTc3YxBkplN6e+dM13ZeIcZnDg/QpSuU2zfZ3rzwY8ukEnaXw==, tarball: https://registry.npmjs.org/tmp/-/tmp-0.2.7.tgz} engines: {node: '>=14.14'} tmpl@1.0.5: - resolution: {integrity: sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==} + resolution: {integrity: sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==, tarball: https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz} to-regex-range@5.0.1: - resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} + resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==, tarball: https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz} engines: {node: '>=8.0'} toidentifier@1.0.1: - resolution: {integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==} + resolution: {integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==, tarball: https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz} engines: {node: '>=0.6'} totalist@1.1.0: - resolution: {integrity: sha512-gduQwd1rOdDMGxFG1gEvhV88Oirdo2p+KjoYFU7k2g+i7n6AFFbDQ5kMPUsW0pNbfQsB/cwXvT1i4Bue0s9g5g==} + resolution: {integrity: sha512-gduQwd1rOdDMGxFG1gEvhV88Oirdo2p+KjoYFU7k2g+i7n6AFFbDQ5kMPUsW0pNbfQsB/cwXvT1i4Bue0s9g5g==, tarball: https://registry.npmjs.org/totalist/-/totalist-1.1.0.tgz} engines: {node: '>=6'} totalist@3.0.1: - resolution: {integrity: sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==} + resolution: {integrity: sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==, tarball: https://registry.npmjs.org/totalist/-/totalist-3.0.1.tgz} engines: {node: '>=6'} tough-cookie@4.1.4: - resolution: {integrity: sha512-Loo5UUvLD9ScZ6jh8beX1T6sO1w2/MpCRpEP7V280GKMVUQ0Jzar2U3UJPsrdbziLEMMhu3Ujnq//rhiFuIeag==} + resolution: {integrity: sha512-Loo5UUvLD9ScZ6jh8beX1T6sO1w2/MpCRpEP7V280GKMVUQ0Jzar2U3UJPsrdbziLEMMhu3Ujnq//rhiFuIeag==, tarball: https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.4.tgz} engines: {node: '>=6'} tough-cookie@6.0.2: - resolution: {integrity: sha512-exgYmnmL/sJpR3upZfXG5PoatXQii55xAiXGXzY+sROLZ/Y+SLcp9PgJNI9Vz37HpQ74WvDcLT8eqm+kV3FzrA==} + resolution: {integrity: sha512-exgYmnmL/sJpR3upZfXG5PoatXQii55xAiXGXzY+sROLZ/Y+SLcp9PgJNI9Vz37HpQ74WvDcLT8eqm+kV3FzrA==, tarball: https://registry.npmjs.org/tough-cookie/-/tough-cookie-6.0.2.tgz} engines: {node: '>=16'} tr46@0.0.3: - resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==} + resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==, tarball: https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz} tr46@1.0.1: - resolution: {integrity: sha512-dTpowEjclQ7Kgx5SdBkqRzVhERQXov8/l9Ft9dVM9fmg0W0KQSVaXX9T4i6twCPNtYiZM53lpSSUAwJbFPOHxA==} + resolution: {integrity: sha512-dTpowEjclQ7Kgx5SdBkqRzVhERQXov8/l9Ft9dVM9fmg0W0KQSVaXX9T4i6twCPNtYiZM53lpSSUAwJbFPOHxA==, tarball: https://registry.npmjs.org/tr46/-/tr46-1.0.1.tgz} tr46@3.0.0: - resolution: {integrity: sha512-l7FvfAHlcmulp8kr+flpQZmVwtu7nfRV7NZujtN0OqES8EL4O4e0qqzL0DC5gAvx/ZC/9lk6rhcUwYvkBnBnYA==} + resolution: {integrity: sha512-l7FvfAHlcmulp8kr+flpQZmVwtu7nfRV7NZujtN0OqES8EL4O4e0qqzL0DC5gAvx/ZC/9lk6rhcUwYvkBnBnYA==, tarball: https://registry.npmjs.org/tr46/-/tr46-3.0.0.tgz} engines: {node: '>=12'} tr46@6.0.0: - resolution: {integrity: sha512-bLVMLPtstlZ4iMQHpFHTR7GAGj2jxi8Dg0s2h2MafAE4uSWF98FC/3MomU51iQAMf8/qDUbKWf5GxuvvVcXEhw==} + resolution: {integrity: sha512-bLVMLPtstlZ4iMQHpFHTR7GAGj2jxi8Dg0s2h2MafAE4uSWF98FC/3MomU51iQAMf8/qDUbKWf5GxuvvVcXEhw==, tarball: https://registry.npmjs.org/tr46/-/tr46-6.0.0.tgz} engines: {node: '>=20'} tree-dump@1.1.0: - resolution: {integrity: sha512-rMuvhU4MCDbcbnleZTFezWsaZXRFemSqAM+7jPnzUl1fo9w3YEKOxAeui0fz3OI4EU4hf23iyA7uQRVko+UaBA==} + resolution: {integrity: sha512-rMuvhU4MCDbcbnleZTFezWsaZXRFemSqAM+7jPnzUl1fo9w3YEKOxAeui0fz3OI4EU4hf23iyA7uQRVko+UaBA==, tarball: https://registry.npmjs.org/tree-dump/-/tree-dump-1.1.0.tgz} engines: {node: '>=10.0'} peerDependencies: tslib: '2' tree-kill@1.2.2: - resolution: {integrity: sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==} + resolution: {integrity: sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==, tarball: https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz} hasBin: true trim-repeated@1.0.0: - resolution: {integrity: sha512-pkonvlKk8/ZuR0D5tLW8ljt5I8kmxp2XKymhepUeOdCEfKpZaktSArkLHZt76OB1ZvO9bssUsDty4SWhLvZpLg==} + resolution: {integrity: sha512-pkonvlKk8/ZuR0D5tLW8ljt5I8kmxp2XKymhepUeOdCEfKpZaktSArkLHZt76OB1ZvO9bssUsDty4SWhLvZpLg==, tarball: https://registry.npmjs.org/trim-repeated/-/trim-repeated-1.0.0.tgz} engines: {node: '>=0.10.0'} ts-api-utils@2.5.0: - resolution: {integrity: sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA==} + resolution: {integrity: sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA==, tarball: https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.5.0.tgz} engines: {node: '>=18.12'} peerDependencies: typescript: '>=4.8.4' ts-checker-rspack-plugin@1.5.2: - resolution: {integrity: sha512-YtdNbLnJ9fU6itfQky0K9Lebb/N0TUSbSVUByyoGt27em3hiTCVnfW2MzxFUHpdMv5+7xPmoS8YBwdDyHfEaGQ==} + resolution: {integrity: sha512-YtdNbLnJ9fU6itfQky0K9Lebb/N0TUSbSVUByyoGt27em3hiTCVnfW2MzxFUHpdMv5+7xPmoS8YBwdDyHfEaGQ==, tarball: https://registry.npmjs.org/ts-checker-rspack-plugin/-/ts-checker-rspack-plugin-1.5.2.tgz} peerDependencies: '@rspack/core': ^1.0.0 || ^2.0.0 '@typescript/native-preview': ^7.0.0-0 @@ -14389,11 +14452,11 @@ packages: optional: true ts-dedent@2.3.0: - resolution: {integrity: sha512-JfJeIHke7y2egdGGgRAvpCwYFUsHlM2gPcrVOxFkznt/4uzQ7HFmvE63iFHVLBJNDuyDOQgijDK/tXH/f6Msjg==} + resolution: {integrity: sha512-JfJeIHke7y2egdGGgRAvpCwYFUsHlM2gPcrVOxFkznt/4uzQ7HFmvE63iFHVLBJNDuyDOQgijDK/tXH/f6Msjg==, tarball: https://registry.npmjs.org/ts-dedent/-/ts-dedent-2.3.0.tgz} engines: {node: '>=6.10'} ts-jest@29.4.11: - resolution: {integrity: sha512-IrFl7l9AuB/qrNw5quqvAv/hmKMb8dhWOH4jQOGo0Oq8tCeo1O86/iTFG1FaRimgUkF13l4PcepO8ATFT6Ns4g==} + resolution: {integrity: sha512-IrFl7l9AuB/qrNw5quqvAv/hmKMb8dhWOH4jQOGo0Oq8tCeo1O86/iTFG1FaRimgUkF13l4PcepO8ATFT6Ns4g==, tarball: https://registry.npmjs.org/ts-jest/-/ts-jest-29.4.11.tgz} engines: {node: ^14.15.0 || ^16.10.0 || ^18.0.0 || >=20.0.0} hasBin: true peerDependencies: @@ -14420,7 +14483,7 @@ packages: optional: true ts-jest@29.4.6: - resolution: {integrity: sha512-fSpWtOO/1AjSNQguk43hb/JCo16oJDnMJf3CdEGNkqsEX3t0KX96xvyX1D7PfLCpVoKu4MfVrqUkFyblYoY4lA==} + resolution: {integrity: sha512-fSpWtOO/1AjSNQguk43hb/JCo16oJDnMJf3CdEGNkqsEX3t0KX96xvyX1D7PfLCpVoKu4MfVrqUkFyblYoY4lA==, tarball: https://registry.npmjs.org/ts-jest/-/ts-jest-29.4.6.tgz} engines: {node: ^14.15.0 || ^16.10.0 || ^18.0.0 || >=20.0.0} hasBin: true peerDependencies: @@ -14447,7 +14510,7 @@ packages: optional: true ts-loader@9.6.2: - resolution: {integrity: sha512-R4iuczmtgxvtuI556s+hTZ6/7Ee03VCAk/l/M8LY1OAsUgB7YydsCxkgq9D9pKRaD7GJqUi2u8fp9zZP/ufjKA==} + resolution: {integrity: sha512-R4iuczmtgxvtuI556s+hTZ6/7Ee03VCAk/l/M8LY1OAsUgB7YydsCxkgq9D9pKRaD7GJqUi2u8fp9zZP/ufjKA==, tarball: https://registry.npmjs.org/ts-loader/-/ts-loader-9.6.2.tgz} engines: {node: '>=12.0.0'} peerDependencies: loader-utils: '*' @@ -14458,7 +14521,7 @@ packages: optional: true ts-node@10.9.2: - resolution: {integrity: sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==} + resolution: {integrity: sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==, tarball: https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz} hasBin: true peerDependencies: '@swc/core': '>=1.2.50' @@ -14472,10 +14535,10 @@ packages: optional: true ts-results@3.3.0: - resolution: {integrity: sha512-FWqxGX2NHp5oCyaMd96o2y2uMQmSu8Dey6kvyuFdRJ2AzfmWo3kWa4UsPlCGlfQ/qu03m09ZZtppMoY8EMHuiA==} + resolution: {integrity: sha512-FWqxGX2NHp5oCyaMd96o2y2uMQmSu8Dey6kvyuFdRJ2AzfmWo3kWa4UsPlCGlfQ/qu03m09ZZtppMoY8EMHuiA==, tarball: https://registry.npmjs.org/ts-results/-/ts-results-3.3.0.tgz} tsconfck@3.1.6: - resolution: {integrity: sha512-ks6Vjr/jEw0P1gmOVwutM3B7fWxoWBL2KRDb1JfqGVawBmO5UsvmWOQFGHBPl5yxYz4eERr19E6L7NMv+Fej4w==} + resolution: {integrity: sha512-ks6Vjr/jEw0P1gmOVwutM3B7fWxoWBL2KRDb1JfqGVawBmO5UsvmWOQFGHBPl5yxYz4eERr19E6L7NMv+Fej4w==, tarball: https://registry.npmjs.org/tsconfck/-/tsconfck-3.1.6.tgz} engines: {node: ^18 || >=20} deprecated: unmaintained hasBin: true @@ -14486,229 +14549,229 @@ packages: optional: true tsconfig-paths-webpack-plugin@4.2.0: - resolution: {integrity: sha512-zbem3rfRS8BgeNK50Zz5SIQgXzLafiHjOwUAvk/38/o1jHn/V5QAgVUcz884or7WYcPaH3N2CIfUc2u0ul7UcA==} + resolution: {integrity: sha512-zbem3rfRS8BgeNK50Zz5SIQgXzLafiHjOwUAvk/38/o1jHn/V5QAgVUcz884or7WYcPaH3N2CIfUc2u0ul7UcA==, tarball: https://registry.npmjs.org/tsconfig-paths-webpack-plugin/-/tsconfig-paths-webpack-plugin-4.2.0.tgz} engines: {node: '>=10.13.0'} tsconfig-paths@3.15.0: - resolution: {integrity: sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==} + resolution: {integrity: sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==, tarball: https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz} tsconfig-paths@4.2.0: - resolution: {integrity: sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg==} + resolution: {integrity: sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg==, tarball: https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-4.2.0.tgz} engines: {node: '>=6'} tslib@1.14.1: - resolution: {integrity: sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==} + resolution: {integrity: sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==, tarball: https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz} tslib@2.3.0: - resolution: {integrity: sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg==} + resolution: {integrity: sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg==, tarball: https://registry.npmjs.org/tslib/-/tslib-2.3.0.tgz} tslib@2.8.1: - resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} + resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==, tarball: https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz} tsscmp@1.0.6: - resolution: {integrity: sha512-LxhtAkPDTkVCMQjt2h6eBVY28KCjikZqZfMcC15YBeNjkgUpdCfBu5HoiOTDu86v6smE8yOjyEktJ8hlbANHQA==} + resolution: {integrity: sha512-LxhtAkPDTkVCMQjt2h6eBVY28KCjikZqZfMcC15YBeNjkgUpdCfBu5HoiOTDu86v6smE8yOjyEktJ8hlbANHQA==, tarball: https://registry.npmjs.org/tsscmp/-/tsscmp-1.0.6.tgz} engines: {node: '>=0.6.x'} tsx@4.19.0: - resolution: {integrity: sha512-bV30kM7bsLZKZIOCHeMNVMJ32/LuJzLVajkQI/qf92J2Qr08ueLQvW00PUZGiuLPP760UINwupgUj8qrSCPUKg==} + resolution: {integrity: sha512-bV30kM7bsLZKZIOCHeMNVMJ32/LuJzLVajkQI/qf92J2Qr08ueLQvW00PUZGiuLPP760UINwupgUj8qrSCPUKg==, tarball: https://registry.npmjs.org/tsx/-/tsx-4.19.0.tgz} engines: {node: '>=18.0.0'} hasBin: true tsyringe@4.10.0: - resolution: {integrity: sha512-axr3IdNuVIxnaK5XGEUFTu3YmAQ6lllgrvqfEoR16g/HGnYY/6We4oWENtAnzK6/LpJ2ur9PAb80RBt7/U4ugw==} + resolution: {integrity: sha512-axr3IdNuVIxnaK5XGEUFTu3YmAQ6lllgrvqfEoR16g/HGnYY/6We4oWENtAnzK6/LpJ2ur9PAb80RBt7/U4ugw==, tarball: https://registry.npmjs.org/tsyringe/-/tsyringe-4.10.0.tgz} engines: {node: '>= 6.0.0'} turndown@7.2.0: - resolution: {integrity: sha512-eCZGBN4nNNqM9Owkv9HAtWRYfLA4h909E/WGAWWBpmB275ehNhZyk87/Tpvjbp0jjNl9XwCsbe6bm6CqFsgD+A==} + resolution: {integrity: sha512-eCZGBN4nNNqM9Owkv9HAtWRYfLA4h909E/WGAWWBpmB275ehNhZyk87/Tpvjbp0jjNl9XwCsbe6bm6CqFsgD+A==, tarball: https://registry.npmjs.org/turndown/-/turndown-7.2.0.tgz} type-check@0.4.0: - resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} + resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==, tarball: https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz} engines: {node: '>= 0.8.0'} type-detect@4.0.8: - resolution: {integrity: sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==} + resolution: {integrity: sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==, tarball: https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz} engines: {node: '>=4'} type-fest@0.20.2: - resolution: {integrity: sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==} + resolution: {integrity: sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==, tarball: https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz} engines: {node: '>=10'} type-fest@0.21.3: - resolution: {integrity: sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==} + resolution: {integrity: sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==, tarball: https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz} engines: {node: '>=10'} type-fest@4.41.0: - resolution: {integrity: sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==} + resolution: {integrity: sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==, tarball: https://registry.npmjs.org/type-fest/-/type-fest-4.41.0.tgz} engines: {node: '>=16'} type-func@1.0.3: - resolution: {integrity: sha512-YA90CUk+i00tWESPNRMahywXhAz+12NLJLKlOWrgHIbqaFXjdZrWstRghaibOW/IxhPjui4SmXxO/03XSGRIjA==} + resolution: {integrity: sha512-YA90CUk+i00tWESPNRMahywXhAz+12NLJLKlOWrgHIbqaFXjdZrWstRghaibOW/IxhPjui4SmXxO/03XSGRIjA==, tarball: https://registry.npmjs.org/type-func/-/type-func-1.0.3.tgz} type-is@1.6.18: - resolution: {integrity: sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==} + resolution: {integrity: sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==, tarball: https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz} engines: {node: '>= 0.6'} type-is@2.1.0: - resolution: {integrity: sha512-faYHw0anBbc/kWF3zFTEnxSFOAGUX9GFbOBthvDdLsIlEoWOFOtS0zgCiQYwIskL9iGXZL3kAXD8OoZ4GmMATA==} + resolution: {integrity: sha512-faYHw0anBbc/kWF3zFTEnxSFOAGUX9GFbOBthvDdLsIlEoWOFOtS0zgCiQYwIskL9iGXZL3kAXD8OoZ4GmMATA==, tarball: https://registry.npmjs.org/type-is/-/type-is-2.1.0.tgz} engines: {node: '>= 18'} typed-array-buffer@1.0.3: - resolution: {integrity: sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==} + resolution: {integrity: sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==, tarball: https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.3.tgz} engines: {node: '>= 0.4'} typed-array-byte-length@1.0.3: - resolution: {integrity: sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg==} + resolution: {integrity: sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg==, tarball: https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.3.tgz} engines: {node: '>= 0.4'} typed-array-byte-offset@1.0.4: - resolution: {integrity: sha512-bTlAFB/FBYMcuX81gbL4OcpH5PmlFHqlCCpAl8AlEzMz5k53oNDvN8p1PNOWLEmI2x4orp3raOFB51tv9X+MFQ==} + resolution: {integrity: sha512-bTlAFB/FBYMcuX81gbL4OcpH5PmlFHqlCCpAl8AlEzMz5k53oNDvN8p1PNOWLEmI2x4orp3raOFB51tv9X+MFQ==, tarball: https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.4.tgz} engines: {node: '>= 0.4'} typed-array-length@1.0.8: - resolution: {integrity: sha512-phPGCwqr2+Qo0fwniCE8e4pKnGu/yFb5nD5Y8bf0EEeiI5GklnACYA9GFy/DrAeRrKHXvHn+1SUsOWgJp6RO+g==} + resolution: {integrity: sha512-phPGCwqr2+Qo0fwniCE8e4pKnGu/yFb5nD5Y8bf0EEeiI5GklnACYA9GFy/DrAeRrKHXvHn+1SUsOWgJp6RO+g==, tarball: https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.8.tgz} engines: {node: '>= 0.4'} typed-assert@1.0.9: - resolution: {integrity: sha512-KNNZtayBCtmnNmbo5mG47p1XsCyrx6iVqomjcZnec/1Y5GGARaxPs6r49RnSPeUP3YjNYiU9sQHAtY4BBvnZwg==} + resolution: {integrity: sha512-KNNZtayBCtmnNmbo5mG47p1XsCyrx6iVqomjcZnec/1Y5GGARaxPs6r49RnSPeUP3YjNYiU9sQHAtY4BBvnZwg==, tarball: https://registry.npmjs.org/typed-assert/-/typed-assert-1.0.9.tgz} typedoc@0.25.4: - resolution: {integrity: sha512-Du9ImmpBCw54bX275yJrxPVnjdIyJO/84co0/L9mwe0R3G4FSR6rQ09AlXVRvZEGMUg09+z/usc8mgygQ1aidA==} + resolution: {integrity: sha512-Du9ImmpBCw54bX275yJrxPVnjdIyJO/84co0/L9mwe0R3G4FSR6rQ09AlXVRvZEGMUg09+z/usc8mgygQ1aidA==, tarball: https://registry.npmjs.org/typedoc/-/typedoc-0.25.4.tgz} engines: {node: '>= 16'} hasBin: true peerDependencies: typescript: 4.6.x || 4.7.x || 4.8.x || 4.9.x || 5.0.x || 5.1.x || 5.2.x || 5.3.x typescript-eslint@8.62.0: - resolution: {integrity: sha512-8QxXi+ZACKX0kaqO4gY8kn0RSD9gFfaHDWwjqtEN48aWCBkX4MJaufWN+c3BzlrXLOxfywDL8CaoqUwcRq4j4Q==} + resolution: {integrity: sha512-8QxXi+ZACKX0kaqO4gY8kn0RSD9gFfaHDWwjqtEN48aWCBkX4MJaufWN+c3BzlrXLOxfywDL8CaoqUwcRq4j4Q==, tarball: https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.62.0.tgz} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 typescript: '>=4.8.4 <6.1.0' typescript@5.9.3: - resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==} + resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==, tarball: https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz} engines: {node: '>=14.17'} hasBin: true typescript@6.0.3: - resolution: {integrity: sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==} + resolution: {integrity: sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==, tarball: https://registry.npmjs.org/typescript/-/typescript-6.0.3.tgz} engines: {node: '>=14.17'} hasBin: true uc.micro@2.1.0: - resolution: {integrity: sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==} + resolution: {integrity: sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==, tarball: https://registry.npmjs.org/uc.micro/-/uc.micro-2.1.0.tgz} ufo@1.6.4: - resolution: {integrity: sha512-JFNbkD1Svwe0KvGi8GOeLcP4kAWQ609twvCdcHxq1oSL8svv39ZuSvajcD8B+5D0eL4+s1Is2D/O6KN3qcTeRA==} + resolution: {integrity: sha512-JFNbkD1Svwe0KvGi8GOeLcP4kAWQ609twvCdcHxq1oSL8svv39ZuSvajcD8B+5D0eL4+s1Is2D/O6KN3qcTeRA==, tarball: https://registry.npmjs.org/ufo/-/ufo-1.6.4.tgz} uglify-js@3.19.3: - resolution: {integrity: sha512-v3Xu+yuwBXisp6QYTcH4UbH+xYJXqnq2m/LtQVWKWzYc1iehYnLixoQDN9FH6/j9/oybfd6W9Ghwkl8+UMKTKQ==} + resolution: {integrity: sha512-v3Xu+yuwBXisp6QYTcH4UbH+xYJXqnq2m/LtQVWKWzYc1iehYnLixoQDN9FH6/j9/oybfd6W9Ghwkl8+UMKTKQ==, tarball: https://registry.npmjs.org/uglify-js/-/uglify-js-3.19.3.tgz} engines: {node: '>=0.8.0'} hasBin: true unbox-primitive@1.1.0: - resolution: {integrity: sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==} + resolution: {integrity: sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==, tarball: https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.1.0.tgz} engines: {node: '>= 0.4'} undici-types@6.21.0: - resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==} + resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==, tarball: https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz} undici@7.28.0: - resolution: {integrity: sha512-cRZYrTDwWznlnRiPjggAGxZXanty6M8RV1ff8Wm4LWXBp7/IG8v5DnOm74DtUBp9OONpK75YlPnIjQqX0dBDtA==} + resolution: {integrity: sha512-cRZYrTDwWznlnRiPjggAGxZXanty6M8RV1ff8Wm4LWXBp7/IG8v5DnOm74DtUBp9OONpK75YlPnIjQqX0dBDtA==, tarball: https://registry.npmjs.org/undici/-/undici-7.28.0.tgz} engines: {node: '>=20.18.1'} unicode-canonical-property-names-ecmascript@2.0.1: - resolution: {integrity: sha512-dA8WbNeb2a6oQzAQ55YlT5vQAWGV9WXOsi3SskE3bcCdM0P4SDd+24zS/OCacdRq5BkdsRj9q3Pg6YyQoxIGqg==} + resolution: {integrity: sha512-dA8WbNeb2a6oQzAQ55YlT5vQAWGV9WXOsi3SskE3bcCdM0P4SDd+24zS/OCacdRq5BkdsRj9q3Pg6YyQoxIGqg==, tarball: https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.1.tgz} engines: {node: '>=4'} unicode-match-property-ecmascript@2.0.0: - resolution: {integrity: sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==} + resolution: {integrity: sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==, tarball: https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz} engines: {node: '>=4'} unicode-match-property-value-ecmascript@2.2.1: - resolution: {integrity: sha512-JQ84qTuMg4nVkx8ga4A16a1epI9H6uTXAknqxkGF/aFfRLw1xC/Bp24HNLaZhHSkWd3+84t8iXnp1J0kYcZHhg==} + resolution: {integrity: sha512-JQ84qTuMg4nVkx8ga4A16a1epI9H6uTXAknqxkGF/aFfRLw1xC/Bp24HNLaZhHSkWd3+84t8iXnp1J0kYcZHhg==, tarball: https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.2.1.tgz} engines: {node: '>=4'} unicode-property-aliases-ecmascript@2.2.0: - resolution: {integrity: sha512-hpbDzxUY9BFwX+UeBnxv3Sh1q7HFxj48DTmXchNgRa46lO8uj3/1iEn3MiNUYTg1g9ctIqXCCERn8gYZhHC5lQ==} + resolution: {integrity: sha512-hpbDzxUY9BFwX+UeBnxv3Sh1q7HFxj48DTmXchNgRa46lO8uj3/1iEn3MiNUYTg1g9ctIqXCCERn8gYZhHC5lQ==, tarball: https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.2.0.tgz} engines: {node: '>=4'} unicorn-magic@0.3.0: - resolution: {integrity: sha512-+QBBXBCvifc56fsbuxZQ6Sic3wqqc3WWaqxs58gvJrcOuN83HGTCwz3oS5phzU9LthRNE9VrJCFCLUgHeeFnfA==} + resolution: {integrity: sha512-+QBBXBCvifc56fsbuxZQ6Sic3wqqc3WWaqxs58gvJrcOuN83HGTCwz3oS5phzU9LthRNE9VrJCFCLUgHeeFnfA==, tarball: https://registry.npmjs.org/unicorn-magic/-/unicorn-magic-0.3.0.tgz} engines: {node: '>=18'} union@0.5.0: - resolution: {integrity: sha512-N6uOhuW6zO95P3Mel2I2zMsbsanvvtgn6jVqJv4vbVcz/JN0OkL9suomjQGmWtxJQXOCqUJvquc1sMeNz/IwlA==} + resolution: {integrity: sha512-N6uOhuW6zO95P3Mel2I2zMsbsanvvtgn6jVqJv4vbVcz/JN0OkL9suomjQGmWtxJQXOCqUJvquc1sMeNz/IwlA==, tarball: https://registry.npmjs.org/union/-/union-0.5.0.tgz} engines: {node: '>= 0.8.0'} universalify@0.1.2: - resolution: {integrity: sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==} + resolution: {integrity: sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==, tarball: https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz} engines: {node: '>= 4.0.0'} universalify@0.2.0: - resolution: {integrity: sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==} + resolution: {integrity: sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==, tarball: https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz} engines: {node: '>= 4.0.0'} universalify@2.0.1: - resolution: {integrity: sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==} + resolution: {integrity: sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==, tarball: https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz} engines: {node: '>= 10.0.0'} unpipe@1.0.0: - resolution: {integrity: sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==} + resolution: {integrity: sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==, tarball: https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz} engines: {node: '>= 0.8'} unrs-resolver@1.12.2: - resolution: {integrity: sha512-dmlRxBJJayXjqTwC+JtF1HhJmgf3ftQ3YejFcZrf4+KKtJv0qDsK1pjqaaVjG7wJ5NJ6UVP1OqRMQ71Z4C3rxQ==} + resolution: {integrity: sha512-dmlRxBJJayXjqTwC+JtF1HhJmgf3ftQ3YejFcZrf4+KKtJv0qDsK1pjqaaVjG7wJ5NJ6UVP1OqRMQ71Z4C3rxQ==, tarball: https://registry.npmjs.org/unrs-resolver/-/unrs-resolver-1.12.2.tgz} upath@2.0.1: - resolution: {integrity: sha512-1uEe95xksV1O0CYKXo8vQvN1JEbtJp7lb7C5U9HMsIp6IVwntkH/oNUzyVNQSd4S1sYk2FpSSW44FqMc8qee5w==} + resolution: {integrity: sha512-1uEe95xksV1O0CYKXo8vQvN1JEbtJp7lb7C5U9HMsIp6IVwntkH/oNUzyVNQSd4S1sYk2FpSSW44FqMc8qee5w==, tarball: https://registry.npmjs.org/upath/-/upath-2.0.1.tgz} engines: {node: '>=4'} update-browserslist-db@1.2.3: - resolution: {integrity: sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==} + resolution: {integrity: sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==, tarball: https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz} hasBin: true peerDependencies: browserslist: '>= 4.21.0' uri-js@4.4.1: - resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} + resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==, tarball: https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz} url-join@4.0.1: - resolution: {integrity: sha512-jk1+QP6ZJqyOiuEI9AEWQfju/nB2Pw466kbA0LEZljHwKeMgd9WrAEgEGxjPDD2+TNbbb37rTyhEfrCXfuKXnA==} + resolution: {integrity: sha512-jk1+QP6ZJqyOiuEI9AEWQfju/nB2Pw466kbA0LEZljHwKeMgd9WrAEgEGxjPDD2+TNbbb37rTyhEfrCXfuKXnA==, tarball: https://registry.npmjs.org/url-join/-/url-join-4.0.1.tgz} url-parse@1.5.10: - resolution: {integrity: sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==} + resolution: {integrity: sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==, tarball: https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz} util-deprecate@1.0.2: - resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} + resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==, tarball: https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz} utils-merge@1.0.1: - resolution: {integrity: sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==} + resolution: {integrity: sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==, tarball: https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz} engines: {node: '>= 0.4.0'} uuid@14.0.1: - resolution: {integrity: sha512-6ZxzVpzDXDa3bJWaHilVayA+BH/1zmxCJoVgvmqJnid/gPoKHxUrS/aC/T6LGQtNHT+XHG9fXPJB4d+IrU30Ew==} + resolution: {integrity: sha512-6ZxzVpzDXDa3bJWaHilVayA+BH/1zmxCJoVgvmqJnid/gPoKHxUrS/aC/T6LGQtNHT+XHG9fXPJB4d+IrU30Ew==, tarball: https://registry.npmjs.org/uuid/-/uuid-14.0.1.tgz} hasBin: true uuid@8.3.2: - resolution: {integrity: sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==} + resolution: {integrity: sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==, tarball: https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz} deprecated: uuid@10 and below is no longer supported. For ESM codebases, update to uuid@latest. For CommonJS codebases, use uuid@11 (but be aware this version will likely be deprecated in 2028). hasBin: true uuid@9.0.0: - resolution: {integrity: sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg==} + resolution: {integrity: sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg==, tarball: https://registry.npmjs.org/uuid/-/uuid-9.0.0.tgz} deprecated: uuid@10 and below is no longer supported. For ESM codebases, update to uuid@latest. For CommonJS codebases, use uuid@11 (but be aware this version will likely be deprecated in 2028). hasBin: true v8-compile-cache-lib@3.0.1: - resolution: {integrity: sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==} + resolution: {integrity: sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==, tarball: https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz} v8-to-istanbul@9.3.0: - resolution: {integrity: sha512-kiGUalWN+rgBJ/1OHZsBtU4rXZOfj/7rKQxULKlIzwzQSvMJUUNgPwJEEh7gU6xEVxC0ahoOBvN2YI8GH6FNgA==} + resolution: {integrity: sha512-kiGUalWN+rgBJ/1OHZsBtU4rXZOfj/7rKQxULKlIzwzQSvMJUUNgPwJEEh7gU6xEVxC0ahoOBvN2YI8GH6FNgA==, tarball: https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.3.0.tgz} engines: {node: '>=10.12.0'} valibot@1.4.2: - resolution: {integrity: sha512-gjdCvJ6d3RyHAneqxMYMW9QMCwYMb3jpOO0IyHZV1bnRHFBHrX3VkIILt5XYR0WhwHiH7Mty8ovuPZ/O3gamrg==} + resolution: {integrity: sha512-gjdCvJ6d3RyHAneqxMYMW9QMCwYMb3jpOO0IyHZV1bnRHFBHrX3VkIILt5XYR0WhwHiH7Mty8ovuPZ/O3gamrg==, tarball: https://registry.npmjs.org/valibot/-/valibot-1.4.2.tgz} peerDependencies: typescript: '>=5' peerDependenciesMeta: @@ -14716,22 +14779,22 @@ packages: optional: true validate-npm-package-name@5.0.1: - resolution: {integrity: sha512-OljLrQ9SQdOUqTaQxqL5dEfZWrXExyyWsozYlAWFawPVNuD83igl7uJD2RTkNMbniIYgt8l81eCJGIdQF7avLQ==} + resolution: {integrity: sha512-OljLrQ9SQdOUqTaQxqL5dEfZWrXExyyWsozYlAWFawPVNuD83igl7uJD2RTkNMbniIYgt8l81eCJGIdQF7avLQ==, tarball: https://registry.npmjs.org/validate-npm-package-name/-/validate-npm-package-name-5.0.1.tgz} engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} validate-npm-package-name@8.0.0: - resolution: {integrity: sha512-SCv6OOV6Xj2/3cXy3dGmADluJTNcL3o7hZAglNPTe+WYuEuvxgJzxPrSDLZhF+CwyQOubqgecjMmTJGMVLWjYQ==} + resolution: {integrity: sha512-SCv6OOV6Xj2/3cXy3dGmADluJTNcL3o7hZAglNPTe+WYuEuvxgJzxPrSDLZhF+CwyQOubqgecjMmTJGMVLWjYQ==, tarball: https://registry.npmjs.org/validate-npm-package-name/-/validate-npm-package-name-8.0.0.tgz} engines: {node: ^22.22.2 || ^24.15.0 || >=26.0.0} varint@6.0.0: - resolution: {integrity: sha512-cXEIW6cfr15lFv563k4GuVuW/fiwjknytD37jIOLSdSWuOI6WnO/oKwmP2FQTU2l01LP8/M5TSAJpzUaGe3uWg==} + resolution: {integrity: sha512-cXEIW6cfr15lFv563k4GuVuW/fiwjknytD37jIOLSdSWuOI6WnO/oKwmP2FQTU2l01LP8/M5TSAJpzUaGe3uWg==, tarball: https://registry.npmjs.org/varint/-/varint-6.0.0.tgz} vary@1.1.2: - resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==} + resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==, tarball: https://registry.npmjs.org/vary/-/vary-1.1.2.tgz} engines: {node: '>= 0.8'} vite-plugin-dts@4.5.4: - resolution: {integrity: sha512-d4sOM8M/8z7vRXHHq/ebbblfaxENjogAAekcfcDCCwAyvGqnPrc7f4NZbvItS+g4WTgerW0xDwSz5qz11JT3vg==} + resolution: {integrity: sha512-d4sOM8M/8z7vRXHHq/ebbblfaxENjogAAekcfcDCCwAyvGqnPrc7f4NZbvItS+g4WTgerW0xDwSz5qz11JT3vg==, tarball: https://registry.npmjs.org/vite-plugin-dts/-/vite-plugin-dts-4.5.4.tgz} peerDependencies: typescript: '*' vite: '*' @@ -14740,13 +14803,13 @@ packages: optional: true vite-plugin-static-copy@4.1.1: - resolution: {integrity: sha512-GrlA8YklrAfSyxJ4M3fdQLOo9oNkp56IM9FYgX/WtEgeIFkPwhu4wzpufBCIuNKCa6Fn77FkRdYxkHqV0FwjAw==} + resolution: {integrity: sha512-GrlA8YklrAfSyxJ4M3fdQLOo9oNkp56IM9FYgX/WtEgeIFkPwhu4wzpufBCIuNKCa6Fn77FkRdYxkHqV0FwjAw==, tarball: https://registry.npmjs.org/vite-plugin-static-copy/-/vite-plugin-static-copy-4.1.1.tgz} engines: {node: ^22.0.0 || >=24.0.0} peerDependencies: vite: ^6.0.0 || ^7.0.0 || ^8.0.0 vite-tsconfig-paths@5.1.4: - resolution: {integrity: sha512-cYj0LRuLV2c2sMqhqhGpaO3LretdtMn/BVX4cPLanIZuwwrkVl+lK84E/miEXkCHWXuq65rhNN4rXsBcOB3S4w==} + resolution: {integrity: sha512-cYj0LRuLV2c2sMqhqhGpaO3LretdtMn/BVX4cPLanIZuwwrkVl+lK84E/miEXkCHWXuq65rhNN4rXsBcOB3S4w==, tarball: https://registry.npmjs.org/vite-tsconfig-paths/-/vite-tsconfig-paths-5.1.4.tgz} peerDependencies: vite: '*' peerDependenciesMeta: @@ -14754,7 +14817,7 @@ packages: optional: true vite@7.3.2: - resolution: {integrity: sha512-Bby3NOsna2jsjfLVOHKes8sGwgl4TT0E6vvpYgnAYDIF/tie7MRaFthmKuHx1NSXjiTueXH3do80FMQgvEktRg==} + resolution: {integrity: sha512-Bby3NOsna2jsjfLVOHKes8sGwgl4TT0E6vvpYgnAYDIF/tie7MRaFthmKuHx1NSXjiTueXH3do80FMQgvEktRg==, tarball: https://registry.npmjs.org/vite/-/vite-7.3.2.tgz} engines: {node: ^20.19.0 || >=22.12.0} hasBin: true peerDependencies: @@ -14794,7 +14857,7 @@ packages: optional: true vite@8.1.5: - resolution: {integrity: sha512-7ULLwsCdYx/nRyrpiEwvqb5TFHrMVZyBt+rg/OAXT7rgj/z+DtTDyKFeLAdDkubDVDKD8jOsndmy7m55XcfUsw==} + resolution: {integrity: sha512-7ULLwsCdYx/nRyrpiEwvqb5TFHrMVZyBt+rg/OAXT7rgj/z+DtTDyKFeLAdDkubDVDKD8jOsndmy7m55XcfUsw==, tarball: https://registry.npmjs.org/vite/-/vite-8.1.5.tgz} engines: {node: ^20.19.0 || >=22.12.0} hasBin: true peerDependencies: @@ -14837,7 +14900,7 @@ packages: optional: true vitest@4.0.18: - resolution: {integrity: sha512-hOQuK7h0FGKgBAas7v0mSAsnvrIgAvWmRFjmzpJ7SwFHH3g1k2u37JtYwOwmEKhK6ZO3v9ggDBBm0La1LCK4uQ==} + resolution: {integrity: sha512-hOQuK7h0FGKgBAas7v0mSAsnvrIgAvWmRFjmzpJ7SwFHH3g1k2u37JtYwOwmEKhK6ZO3v9ggDBBm0La1LCK4uQ==, tarball: https://registry.npmjs.org/vitest/-/vitest-4.0.18.tgz} engines: {node: ^20.0.0 || ^22.0.0 || >=24.0.0} hasBin: true peerDependencies: @@ -14871,37 +14934,37 @@ packages: optional: true vscode-oniguruma@1.7.0: - resolution: {integrity: sha512-L9WMGRfrjOhgHSdOYgCt/yRMsXzLDJSL7BPrOZt73gU0iWO4mpqzqQzOz5srxqTvMBaR0XZTSrVWo4j55Rc6cA==} + resolution: {integrity: sha512-L9WMGRfrjOhgHSdOYgCt/yRMsXzLDJSL7BPrOZt73gU0iWO4mpqzqQzOz5srxqTvMBaR0XZTSrVWo4j55Rc6cA==, tarball: https://registry.npmjs.org/vscode-oniguruma/-/vscode-oniguruma-1.7.0.tgz} vscode-textmate@8.0.0: - resolution: {integrity: sha512-AFbieoL7a5LMqcnOF04ji+rpXadgOXnZsxQr//r83kLPr7biP7am3g9zbaZIaBGwBRWeSvoMD4mgPdX3e4NWBg==} + resolution: {integrity: sha512-AFbieoL7a5LMqcnOF04ji+rpXadgOXnZsxQr//r83kLPr7biP7am3g9zbaZIaBGwBRWeSvoMD4mgPdX3e4NWBg==, tarball: https://registry.npmjs.org/vscode-textmate/-/vscode-textmate-8.0.0.tgz} vscode-uri@3.1.0: - resolution: {integrity: sha512-/BpdSx+yCQGnCvecbyXdxHDkuk55/G3xwnC0GqY4gmQ3j+A+g8kzzgB4Nk/SINjqn6+waqw3EgbVF2QKExkRxQ==} + resolution: {integrity: sha512-/BpdSx+yCQGnCvecbyXdxHDkuk55/G3xwnC0GqY4gmQ3j+A+g8kzzgB4Nk/SINjqn6+waqw3EgbVF2QKExkRxQ==, tarball: https://registry.npmjs.org/vscode-uri/-/vscode-uri-3.1.0.tgz} vue-component-type-helpers@3.3.7: - resolution: {integrity: sha512-Skkhw9agYSgsWqv7bxSOGJZa9SaiJbZVGdXuFWnrzKaQYHnw9qbjD630rw6RyMqDbp54nfLCLw5SZA55if7JLg==} + resolution: {integrity: sha512-Skkhw9agYSgsWqv7bxSOGJZa9SaiJbZVGdXuFWnrzKaQYHnw9qbjD630rw6RyMqDbp54nfLCLw5SZA55if7JLg==, tarball: https://registry.npmjs.org/vue-component-type-helpers/-/vue-component-type-helpers-3.3.7.tgz} vue-eslint-parser@10.4.1: - resolution: {integrity: sha512-Gk6gRDj0n/fkRa3C3l0bBheoBckUq/Rs0F/TvMWIS6nzzx67amAViMe9CkNgsP2tXyQONvGiHQESHwFtZ3aYDA==} + resolution: {integrity: sha512-Gk6gRDj0n/fkRa3C3l0bBheoBckUq/Rs0F/TvMWIS6nzzx67amAViMe9CkNgsP2tXyQONvGiHQESHwFtZ3aYDA==, tarball: https://registry.npmjs.org/vue-eslint-parser/-/vue-eslint-parser-10.4.1.tgz} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 vue-eslint-parser@9.4.3: - resolution: {integrity: sha512-2rYRLWlIpaiN8xbPiDyXZXRgLGOtWxERV7ND5fFAv5qo1D2N9Fu9MNajBNc6o13lZ+24DAWCkQCvj4klgmcITg==} + resolution: {integrity: sha512-2rYRLWlIpaiN8xbPiDyXZXRgLGOtWxERV7ND5fFAv5qo1D2N9Fu9MNajBNc6o13lZ+24DAWCkQCvj4klgmcITg==, tarball: https://registry.npmjs.org/vue-eslint-parser/-/vue-eslint-parser-9.4.3.tgz} engines: {node: ^14.17.0 || >=16.0.0} peerDependencies: eslint: '>=6.0.0' vue-tsc@2.2.12: - resolution: {integrity: sha512-P7OP77b2h/Pmk+lZdJ0YWs+5tJ6J2+uOQPo7tlBnY44QqQSPYvS0qVT4wqDJgwrZaLe47etJLLQRFia71GYITw==} + resolution: {integrity: sha512-P7OP77b2h/Pmk+lZdJ0YWs+5tJ6J2+uOQPo7tlBnY44QqQSPYvS0qVT4wqDJgwrZaLe47etJLLQRFia71GYITw==, tarball: https://registry.npmjs.org/vue-tsc/-/vue-tsc-2.2.12.tgz} hasBin: true peerDependencies: typescript: '>=5.0.0' vue@3.5.39: - resolution: {integrity: sha512-xmZCYabFGcirU8r0fTuvl/LICc1OU620rnqepaJDL/a141ZigkG7AyaxQLdqJ02ZRYzWe6YPaDHeQx7MfknQfA==} + resolution: {integrity: sha512-xmZCYabFGcirU8r0fTuvl/LICc1OU620rnqepaJDL/a141ZigkG7AyaxQLdqJ02ZRYzWe6YPaDHeQx7MfknQfA==, tarball: https://registry.npmjs.org/vue/-/vue-3.5.39.tgz} peerDependencies: typescript: '*' peerDependenciesMeta: @@ -14909,57 +14972,57 @@ packages: optional: true w3c-keyname@2.2.8: - resolution: {integrity: sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ==} + resolution: {integrity: sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ==, tarball: https://registry.npmjs.org/w3c-keyname/-/w3c-keyname-2.2.8.tgz} w3c-xmlserializer@4.0.0: - resolution: {integrity: sha512-d+BFHzbiCx6zGfz0HyQ6Rg69w9k19nviJspaj4yNscGjrHu94sVP+aRm75yEbCh+r2/yR+7q6hux9LVtbuTGBw==} + resolution: {integrity: sha512-d+BFHzbiCx6zGfz0HyQ6Rg69w9k19nviJspaj4yNscGjrHu94sVP+aRm75yEbCh+r2/yR+7q6hux9LVtbuTGBw==, tarball: https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-4.0.0.tgz} engines: {node: '>=14'} w3c-xmlserializer@5.0.0: - resolution: {integrity: sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==} + resolution: {integrity: sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==, tarball: https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-5.0.0.tgz} engines: {node: '>=18'} walker@1.0.8: - resolution: {integrity: sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==} + resolution: {integrity: sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==, tarball: https://registry.npmjs.org/walker/-/walker-1.0.8.tgz} watchpack@2.4.0: - resolution: {integrity: sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg==} + resolution: {integrity: sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg==, tarball: https://registry.npmjs.org/watchpack/-/watchpack-2.4.0.tgz} engines: {node: '>=10.13.0'} watchpack@2.5.2: - resolution: {integrity: sha512-6i/00NBjP4yGPs+caKSyRfpTF/8Torsu0MOW3mMzIbhgISFder8i7xbqgHlLMwJrdiN8ndBV3UA1/AfzPSr+jg==} + resolution: {integrity: sha512-6i/00NBjP4yGPs+caKSyRfpTF/8Torsu0MOW3mMzIbhgISFder8i7xbqgHlLMwJrdiN8ndBV3UA1/AfzPSr+jg==, tarball: https://registry.npmjs.org/watchpack/-/watchpack-2.5.2.tgz} engines: {node: '>=10.13.0'} wbuf@1.7.3: - resolution: {integrity: sha512-O84QOnr0icsbFGLS0O3bI5FswxzRr8/gHwWkDlQFskhSPryQXvrTMxjxGP4+iWYoauLoBvfDpkrOauZ+0iZpDA==} + resolution: {integrity: sha512-O84QOnr0icsbFGLS0O3bI5FswxzRr8/gHwWkDlQFskhSPryQXvrTMxjxGP4+iWYoauLoBvfDpkrOauZ+0iZpDA==, tarball: https://registry.npmjs.org/wbuf/-/wbuf-1.7.3.tgz} wcwidth@1.0.1: - resolution: {integrity: sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==} + resolution: {integrity: sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==, tarball: https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz} weak-lru-cache@1.2.2: - resolution: {integrity: sha512-DEAoo25RfSYMuTGc9vPJzZcZullwIqRDSI9LOy+fkCJPi6hykCnfKaXTuPBDuXAUcqHXyOgFtHNp/kB2FjYHbw==} + resolution: {integrity: sha512-DEAoo25RfSYMuTGc9vPJzZcZullwIqRDSI9LOy+fkCJPi6hykCnfKaXTuPBDuXAUcqHXyOgFtHNp/kB2FjYHbw==, tarball: https://registry.npmjs.org/weak-lru-cache/-/weak-lru-cache-1.2.2.tgz} webidl-conversions@3.0.1: - resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==} + resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==, tarball: https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz} webidl-conversions@4.0.2: - resolution: {integrity: sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==} + resolution: {integrity: sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==, tarball: https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-4.0.2.tgz} webidl-conversions@7.0.0: - resolution: {integrity: sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==} + resolution: {integrity: sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==, tarball: https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz} engines: {node: '>=12'} webidl-conversions@8.0.1: - resolution: {integrity: sha512-BMhLD/Sw+GbJC21C/UgyaZX41nPt8bUTg+jWyDeg7e7YN4xOM05YPSIXceACnXVtqyEw/LMClUQMtMZ+PGGpqQ==} + resolution: {integrity: sha512-BMhLD/Sw+GbJC21C/UgyaZX41nPt8bUTg+jWyDeg7e7YN4xOM05YPSIXceACnXVtqyEw/LMClUQMtMZ+PGGpqQ==, tarball: https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-8.0.1.tgz} engines: {node: '>=20'} webpack-bundle-analyzer@4.5.0: - resolution: {integrity: sha512-GUMZlM3SKwS8Z+CKeIFx7CVoHn3dXFcUAjT/dcZQQmfSZGvitPfMob2ipjai7ovFFqPvTqkEZ/leL4O0YOdAYQ==} + resolution: {integrity: sha512-GUMZlM3SKwS8Z+CKeIFx7CVoHn3dXFcUAjT/dcZQQmfSZGvitPfMob2ipjai7ovFFqPvTqkEZ/leL4O0YOdAYQ==, tarball: https://registry.npmjs.org/webpack-bundle-analyzer/-/webpack-bundle-analyzer-4.5.0.tgz} engines: {node: '>= 10.13.0'} hasBin: true webpack-dev-middleware@7.4.5: - resolution: {integrity: sha512-uxQ6YqGdE4hgDKNf7hUiPXOdtkXvBJXrfEGYSx7P7LC8hnUYGK70X6xQXUvXeNyBDDcsiQXpG2m3G9vxowaEuA==} + resolution: {integrity: sha512-uxQ6YqGdE4hgDKNf7hUiPXOdtkXvBJXrfEGYSx7P7LC8hnUYGK70X6xQXUvXeNyBDDcsiQXpG2m3G9vxowaEuA==, tarball: https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-7.4.5.tgz} engines: {node: '>= 18.12.0'} peerDependencies: webpack: ^5.0.0 @@ -14968,7 +15031,7 @@ packages: optional: true webpack-dev-middleware@8.0.3: - resolution: {integrity: sha512-zWrde9VZDiRaFuWsjHO40wm9LxxtXEk8DdzFXdU7eU5ZpiANnZZDBbZgN3guxbEoKqUHd9YupBmynyioz42nkA==} + resolution: {integrity: sha512-zWrde9VZDiRaFuWsjHO40wm9LxxtXEk8DdzFXdU7eU5ZpiANnZZDBbZgN3guxbEoKqUHd9YupBmynyioz42nkA==, tarball: https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-8.0.3.tgz} engines: {node: '>= 20.9.0'} peerDependencies: webpack: ^5.101.0 @@ -14977,7 +15040,7 @@ packages: optional: true webpack-dev-server@5.2.6: - resolution: {integrity: sha512-HNLRmamRvVavZQ+avceZifmv8hmdUjg43t6MI4SqJDwFdW7RPQwH5vzGhDRZSX59SgfbeHhLnq3g+uooWo7pVw==} + resolution: {integrity: sha512-HNLRmamRvVavZQ+avceZifmv8hmdUjg43t6MI4SqJDwFdW7RPQwH5vzGhDRZSX59SgfbeHhLnq3g+uooWo7pVw==, tarball: https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-5.2.6.tgz} engines: {node: '>= 18.12.0'} hasBin: true peerDependencies: @@ -14990,23 +15053,23 @@ packages: optional: true webpack-merge@5.10.0: - resolution: {integrity: sha512-+4zXKdx7UnO+1jaN4l2lHVD+mFvnlZQP/6ljaJVb4SZiwIKeUnrT5l0gkT8z+n4hKpC+jpOv6O9R+gLtag7pSA==} + resolution: {integrity: sha512-+4zXKdx7UnO+1jaN4l2lHVD+mFvnlZQP/6ljaJVb4SZiwIKeUnrT5l0gkT8z+n4hKpC+jpOv6O9R+gLtag7pSA==, tarball: https://registry.npmjs.org/webpack-merge/-/webpack-merge-5.10.0.tgz} engines: {node: '>=10.0.0'} webpack-merge@6.0.1: - resolution: {integrity: sha512-hXXvrjtx2PLYx4qruKl+kyRSLc52V+cCvMxRjmKwoA+CBbbF5GfIBtR6kCvl0fYGqTUPKB+1ktVmTHqMOzgCBg==} + resolution: {integrity: sha512-hXXvrjtx2PLYx4qruKl+kyRSLc52V+cCvMxRjmKwoA+CBbbF5GfIBtR6kCvl0fYGqTUPKB+1ktVmTHqMOzgCBg==, tarball: https://registry.npmjs.org/webpack-merge/-/webpack-merge-6.0.1.tgz} engines: {node: '>=18.0.0'} webpack-node-externals@3.0.0: - resolution: {integrity: sha512-LnL6Z3GGDPht/AigwRh2dvL9PQPFQ8skEpVrWZXLWBYmqcaojHNN0onvHzie6rq7EWKrrBfPYqNEzTJgiwEQDQ==} + resolution: {integrity: sha512-LnL6Z3GGDPht/AigwRh2dvL9PQPFQ8skEpVrWZXLWBYmqcaojHNN0onvHzie6rq7EWKrrBfPYqNEzTJgiwEQDQ==, tarball: https://registry.npmjs.org/webpack-node-externals/-/webpack-node-externals-3.0.0.tgz} engines: {node: '>=6'} webpack-sources@3.5.1: - resolution: {integrity: sha512-jyuiGJdtvY434z5bUZrjz67v76/ePNvFZTp9Mdz29IlH4+GPsgyGjiv0fKI+M7BdkU6ADjulUcKAd3tUK3WlEw==} + resolution: {integrity: sha512-jyuiGJdtvY434z5bUZrjz67v76/ePNvFZTp9Mdz29IlH4+GPsgyGjiv0fKI+M7BdkU6ADjulUcKAd3tUK3WlEw==, tarball: https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.5.1.tgz} engines: {node: '>=10.13.0'} webpack-subresource-integrity@5.1.0: - resolution: {integrity: sha512-sacXoX+xd8r4WKsy9MvH/q/vBtEHr86cpImXwyg74pFIpERKt6FmB8cXpeuh0ZLgclOlHI4Wcll7+R5L02xk9Q==} + resolution: {integrity: sha512-sacXoX+xd8r4WKsy9MvH/q/vBtEHr86cpImXwyg74pFIpERKt6FmB8cXpeuh0ZLgclOlHI4Wcll7+R5L02xk9Q==, tarball: https://registry.npmjs.org/webpack-subresource-integrity/-/webpack-subresource-integrity-5.1.0.tgz} engines: {node: '>= 12'} peerDependencies: html-webpack-plugin: '>= 5.0.0-beta.1 < 6' @@ -15016,7 +15079,7 @@ packages: optional: true webpack@5.105.2: - resolution: {integrity: sha512-dRXm0a2qcHPUBEzVk8uph0xWSjV/xZxenQQbLwnwP7caQCYpqG1qddwlyEkIDkYn0K8tvmcrZ+bOrzoQ3HxCDw==} + resolution: {integrity: sha512-dRXm0a2qcHPUBEzVk8uph0xWSjV/xZxenQQbLwnwP7caQCYpqG1qddwlyEkIDkYn0K8tvmcrZ+bOrzoQ3HxCDw==, tarball: https://registry.npmjs.org/webpack/-/webpack-5.105.2.tgz} engines: {node: '>=10.13.0'} hasBin: true peerDependencies: @@ -15026,7 +15089,7 @@ packages: optional: true webpack@5.108.4: - resolution: {integrity: sha512-yur8LyJoeiWh47dErD+Ok7vlbmDsJ3UbbRPAoxbGJ54WpE2y5yVo5G/inUzujnYgw3tPmBRdn+G7PoxXaYC33w==} + resolution: {integrity: sha512-yur8LyJoeiWh47dErD+Ok7vlbmDsJ3UbbRPAoxbGJ54WpE2y5yVo5G/inUzujnYgw3tPmBRdn+G7PoxXaYC33w==, tarball: https://registry.npmjs.org/webpack/-/webpack-5.108.4.tgz} engines: {node: '>=10.13.0'} hasBin: true peerDependencies: @@ -15036,7 +15099,7 @@ packages: optional: true webpack@5.109.2: - resolution: {integrity: sha512-U9/cvLzxObKNEZ9+TtdqrHM5/9z3lgl2c+c4BzbqGxFQvQvBAq87yql5A8pQ+rrMbS496MZJeF5enVBndIy2hw==} + resolution: {integrity: sha512-U9/cvLzxObKNEZ9+TtdqrHM5/9z3lgl2c+c4BzbqGxFQvQvBAq87yql5A8pQ+rrMbS496MZJeF5enVBndIy2hw==, tarball: https://registry.npmjs.org/webpack/-/webpack-5.109.2.tgz} engines: {node: '>=10.13.0'} hasBin: true peerDependencies: @@ -15046,7 +15109,7 @@ packages: optional: true webpack@5.64.0: - resolution: {integrity: sha512-UclnN24m054HaPC45nmDEosX6yXWD+UGC12YtUs5i356DleAUGMDC9LBAw37xRRfgPKYIdCYjGA7RZ1AA+ZnGg==} + resolution: {integrity: sha512-UclnN24m054HaPC45nmDEosX6yXWD+UGC12YtUs5i356DleAUGMDC9LBAw37xRRfgPKYIdCYjGA7RZ1AA+ZnGg==, tarball: https://registry.npmjs.org/webpack/-/webpack-5.64.0.tgz} engines: {node: '>=10.13.0'} hasBin: true peerDependencies: @@ -15056,118 +15119,118 @@ packages: optional: true websocket-driver@0.7.5: - resolution: {integrity: sha512-ZL2+3c7kMBdIRCMz6l8jQMHyGVxj+UL+xVk74Ombiciboca8rHa15L86B19E5oh1pL9Ii/uj54gtsIrZGMo6zA==} + resolution: {integrity: sha512-ZL2+3c7kMBdIRCMz6l8jQMHyGVxj+UL+xVk74Ombiciboca8rHa15L86B19E5oh1pL9Ii/uj54gtsIrZGMo6zA==, tarball: https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.5.tgz} engines: {node: '>=0.8.0'} websocket-extensions@0.1.4: - resolution: {integrity: sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==} + resolution: {integrity: sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==, tarball: https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.4.tgz} engines: {node: '>=0.8.0'} whatwg-encoding@2.0.0: - resolution: {integrity: sha512-p41ogyeMUrw3jWclHWTQg1k05DSVXPLcVxRTYsXUk+ZooOCZLcoYgPZ/HL/D/N+uQPOtcp1me1WhBEaX02mhWg==} + resolution: {integrity: sha512-p41ogyeMUrw3jWclHWTQg1k05DSVXPLcVxRTYsXUk+ZooOCZLcoYgPZ/HL/D/N+uQPOtcp1me1WhBEaX02mhWg==, tarball: https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-2.0.0.tgz} engines: {node: '>=12'} deprecated: Use @exodus/bytes instead for a more spec-conformant and faster implementation whatwg-mimetype@3.0.0: - resolution: {integrity: sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q==} + resolution: {integrity: sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q==, tarball: https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-3.0.0.tgz} engines: {node: '>=12'} whatwg-mimetype@5.0.0: - resolution: {integrity: sha512-sXcNcHOC51uPGF0P/D4NVtrkjSU2fNsm9iog4ZvZJsL3rjoDAzXZhkm2MWt1y+PUdggKAYVoMAIYcs78wJ51Cw==} + resolution: {integrity: sha512-sXcNcHOC51uPGF0P/D4NVtrkjSU2fNsm9iog4ZvZJsL3rjoDAzXZhkm2MWt1y+PUdggKAYVoMAIYcs78wJ51Cw==, tarball: https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-5.0.0.tgz} engines: {node: '>=20'} whatwg-url@11.0.0: - resolution: {integrity: sha512-RKT8HExMpoYx4igMiVMY83lN6UeITKJlBQ+vR/8ZJ8OCdSiN3RwCq+9gH0+Xzj0+5IrM6i4j/6LuvzbZIQgEcQ==} + resolution: {integrity: sha512-RKT8HExMpoYx4igMiVMY83lN6UeITKJlBQ+vR/8ZJ8OCdSiN3RwCq+9gH0+Xzj0+5IrM6i4j/6LuvzbZIQgEcQ==, tarball: https://registry.npmjs.org/whatwg-url/-/whatwg-url-11.0.0.tgz} engines: {node: '>=12'} whatwg-url@16.0.1: - resolution: {integrity: sha512-1to4zXBxmXHV3IiSSEInrreIlu02vUOvrhxJJH5vcxYTBDAx51cqZiKdyTxlecdKNSjj8EcxGBxNf6Vg+945gw==} + resolution: {integrity: sha512-1to4zXBxmXHV3IiSSEInrreIlu02vUOvrhxJJH5vcxYTBDAx51cqZiKdyTxlecdKNSjj8EcxGBxNf6Vg+945gw==, tarball: https://registry.npmjs.org/whatwg-url/-/whatwg-url-16.0.1.tgz} engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} whatwg-url@5.0.0: - resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==} + resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==, tarball: https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz} whatwg-url@6.5.0: - resolution: {integrity: sha512-rhRZRqx/TLJQWUpQ6bmrt2UV4f0HCQ463yQuONJqC6fO2VoEb1pTYddbe59SkYq87aoM5A3bdhMZiUiVws+fzQ==} + resolution: {integrity: sha512-rhRZRqx/TLJQWUpQ6bmrt2UV4f0HCQ463yQuONJqC6fO2VoEb1pTYddbe59SkYq87aoM5A3bdhMZiUiVws+fzQ==, tarball: https://registry.npmjs.org/whatwg-url/-/whatwg-url-6.5.0.tgz} which-boxed-primitive@1.1.1: - resolution: {integrity: sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==} + resolution: {integrity: sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==, tarball: https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.1.1.tgz} engines: {node: '>= 0.4'} which-builtin-type@1.2.1: - resolution: {integrity: sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q==} + resolution: {integrity: sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q==, tarball: https://registry.npmjs.org/which-builtin-type/-/which-builtin-type-1.2.1.tgz} engines: {node: '>= 0.4'} which-collection@1.0.2: - resolution: {integrity: sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==} + resolution: {integrity: sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==, tarball: https://registry.npmjs.org/which-collection/-/which-collection-1.0.2.tgz} engines: {node: '>= 0.4'} which-typed-array@1.1.22: - resolution: {integrity: sha512-fvO4ExWMFsqyhG3AiPAObMuY1lxaqgYcxbc49CNdWDDECOJNgQyvsOWVwbZc+qf3rzRtxojBK+CMEv0Ld5CYpw==} + resolution: {integrity: sha512-fvO4ExWMFsqyhG3AiPAObMuY1lxaqgYcxbc49CNdWDDECOJNgQyvsOWVwbZc+qf3rzRtxojBK+CMEv0Ld5CYpw==, tarball: https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.22.tgz} engines: {node: '>= 0.4'} which@1.3.1: - resolution: {integrity: sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==} + resolution: {integrity: sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==, tarball: https://registry.npmjs.org/which/-/which-1.3.1.tgz} hasBin: true which@2.0.2: - resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} + resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==, tarball: https://registry.npmjs.org/which/-/which-2.0.2.tgz} engines: {node: '>= 8'} hasBin: true which@3.0.1: - resolution: {integrity: sha512-XA1b62dzQzLfaEOSQFTCOd5KFf/1VSzZo7/7TUjnya6u0vGGKzU96UQBZTAThCb2j4/xjBAyii1OhRLJEivHvg==} + resolution: {integrity: sha512-XA1b62dzQzLfaEOSQFTCOd5KFf/1VSzZo7/7TUjnya6u0vGGKzU96UQBZTAThCb2j4/xjBAyii1OhRLJEivHvg==, tarball: https://registry.npmjs.org/which/-/which-3.0.1.tgz} engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} hasBin: true why-is-node-running@2.3.0: - resolution: {integrity: sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==} + resolution: {integrity: sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==, tarball: https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.3.0.tgz} engines: {node: '>=8'} hasBin: true wicg-inert@3.1.3: - resolution: {integrity: sha512-5L0PKK7iP+0Q/jv2ccgmkz/pfXbumZtlEyWS/xnX+L+Og3f7WjL4+iEs18k4IuldOX3PgGpza3qGndL9xUBjCQ==} + resolution: {integrity: sha512-5L0PKK7iP+0Q/jv2ccgmkz/pfXbumZtlEyWS/xnX+L+Og3f7WjL4+iEs18k4IuldOX3PgGpza3qGndL9xUBjCQ==, tarball: https://registry.npmjs.org/wicg-inert/-/wicg-inert-3.1.3.tgz} wildcard@2.0.1: - resolution: {integrity: sha512-CC1bOL87PIWSBhDcTrdeLo6eGT7mCFtrg0uIJtqJUFyK+eJnzl8A1niH56uu7KMa5XFrtiV+AQuHO3n7DsHnLQ==} + resolution: {integrity: sha512-CC1bOL87PIWSBhDcTrdeLo6eGT7mCFtrg0uIJtqJUFyK+eJnzl8A1niH56uu7KMa5XFrtiV+AQuHO3n7DsHnLQ==, tarball: https://registry.npmjs.org/wildcard/-/wildcard-2.0.1.tgz} window-size@1.1.1: - resolution: {integrity: sha512-5D/9vujkmVQ7pSmc0SCBmHXbkv6eaHwXEx65MywhmUMsI8sGqJ972APq1lotfcwMKPFLuCFfL8xGHLIp7jaBmA==} + resolution: {integrity: sha512-5D/9vujkmVQ7pSmc0SCBmHXbkv6eaHwXEx65MywhmUMsI8sGqJ972APq1lotfcwMKPFLuCFfL8xGHLIp7jaBmA==, tarball: https://registry.npmjs.org/window-size/-/window-size-1.1.1.tgz} engines: {node: '>= 0.10.0'} hasBin: true word-wrap@1.2.5: - resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} + resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==, tarball: https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz} engines: {node: '>=0.10.0'} wordwrap@1.0.0: - resolution: {integrity: sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==} + resolution: {integrity: sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==, tarball: https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz} wrap-ansi@10.0.0: - resolution: {integrity: sha512-SGcvg80f0wUy2/fXES19feHMz8E0JoXv2uNgHOu4Dgi2OrCy1lqwFYEJz1BLbDI0exjPMe/ZdzZ/YpGECBG/aQ==} + resolution: {integrity: sha512-SGcvg80f0wUy2/fXES19feHMz8E0JoXv2uNgHOu4Dgi2OrCy1lqwFYEJz1BLbDI0exjPMe/ZdzZ/YpGECBG/aQ==, tarball: https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-10.0.0.tgz} engines: {node: '>=20'} wrap-ansi@7.0.0: - resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} + resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==, tarball: https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz} engines: {node: '>=10'} wrap-ansi@8.1.0: - resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==} + resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==, tarball: https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz} engines: {node: '>=12'} wrap-ansi@9.0.2: - resolution: {integrity: sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww==} + resolution: {integrity: sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww==, tarball: https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.2.tgz} engines: {node: '>=18'} wrappy@1.0.2: - resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} + resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==, tarball: https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz} write-file-atomic@5.0.1: - resolution: {integrity: sha512-+QU2zd6OTD8XWIJCbffaiQeH9U73qIqafo1x6V1snCWYGJf6cVE0cDR4D8xRzcEnfI21IFrUPzPGtcPf8AC+Rw==} + resolution: {integrity: sha512-+QU2zd6OTD8XWIJCbffaiQeH9U73qIqafo1x6V1snCWYGJf6cVE0cDR4D8xRzcEnfI21IFrUPzPGtcPf8AC+Rw==, tarball: https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-5.0.1.tgz} engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} ws@7.5.11: - resolution: {integrity: sha512-zS54Oen9bITtp7kp2XM3AydrCIq1D+HwJOuH+c+e4LfpL/lotP5osijd+UoMnxwAam1GN8R4KtLAyIrIcBNpiA==} + resolution: {integrity: sha512-zS54Oen9bITtp7kp2XM3AydrCIq1D+HwJOuH+c+e4LfpL/lotP5osijd+UoMnxwAam1GN8R4KtLAyIrIcBNpiA==, tarball: https://registry.npmjs.org/ws/-/ws-7.5.11.tgz} engines: {node: '>=8.3.0'} peerDependencies: bufferutil: ^4.0.1 @@ -15179,7 +15242,7 @@ packages: optional: true ws@8.18.0: - resolution: {integrity: sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==} + resolution: {integrity: sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==, tarball: https://registry.npmjs.org/ws/-/ws-8.18.0.tgz} engines: {node: '>=10.0.0'} peerDependencies: bufferutil: ^4.0.1 @@ -15191,7 +15254,7 @@ packages: optional: true ws@8.21.0: - resolution: {integrity: sha512-Vsp28b7DRcimFQvrqu2Wek3z1iYxDCWqHYB8Qsnk/S4RfaCQzPGPyBNuVjJV3cd6UiKtUtp6sNM77gWvzcCH+g==} + resolution: {integrity: sha512-Vsp28b7DRcimFQvrqu2Wek3z1iYxDCWqHYB8Qsnk/S4RfaCQzPGPyBNuVjJV3cd6UiKtUtp6sNM77gWvzcCH+g==, tarball: https://registry.npmjs.org/ws/-/ws-8.21.0.tgz} engines: {node: '>=10.0.0'} peerDependencies: bufferutil: ^4.0.1 @@ -15203,15 +15266,15 @@ packages: optional: true wsl-utils@0.1.0: - resolution: {integrity: sha512-h3Fbisa2nKGPxCpm89Hk33lBLsnaGBvctQopaBSOW/uIs6FTe1ATyAnKFJrzVs9vpGdsTe73WF3V4lIsk4Gacw==} + resolution: {integrity: sha512-h3Fbisa2nKGPxCpm89Hk33lBLsnaGBvctQopaBSOW/uIs6FTe1ATyAnKFJrzVs9vpGdsTe73WF3V4lIsk4Gacw==, tarball: https://registry.npmjs.org/wsl-utils/-/wsl-utils-0.1.0.tgz} engines: {node: '>=18'} wsl-utils@0.3.1: - resolution: {integrity: sha512-g/eziiSUNBSsdDJtCLB8bdYEUMj4jR7AGeUo96p/3dTafgjHhpF4RiCFPiRILwjQoDXx5MqkBr4fwWtR3Ky4Wg==} + resolution: {integrity: sha512-g/eziiSUNBSsdDJtCLB8bdYEUMj4jR7AGeUo96p/3dTafgjHhpF4RiCFPiRILwjQoDXx5MqkBr4fwWtR3Ky4Wg==, tarball: https://registry.npmjs.org/wsl-utils/-/wsl-utils-0.3.1.tgz} engines: {node: '>=20'} xmcp@0.6.4: - resolution: {integrity: sha512-aigY10YfKfG7BZ2MCq+Lbudgp25F28at8IzXt/rDYAD+iBfUeMsTkzCAO4s81L0miVCtJUB3arY8s4/hY3FsSA==} + resolution: {integrity: sha512-aigY10YfKfG7BZ2MCq+Lbudgp25F28at8IzXt/rDYAD+iBfUeMsTkzCAO4s81L0miVCtJUB3arY8s4/hY3FsSA==, tarball: https://registry.npmjs.org/xmcp/-/xmcp-0.6.4.tgz} hasBin: true peerDependencies: react: '>=19.0.0' @@ -15224,21 +15287,21 @@ packages: optional: true xml-name-validator@4.0.0: - resolution: {integrity: sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw==} + resolution: {integrity: sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw==, tarball: https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-4.0.0.tgz} engines: {node: '>=12'} xml-name-validator@5.0.0: - resolution: {integrity: sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==} + resolution: {integrity: sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==, tarball: https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-5.0.0.tgz} engines: {node: '>=18'} xml@1.0.1: - resolution: {integrity: sha512-huCv9IH9Tcf95zuYCsQraZtWnJvBtLVE0QHMOs8bWyZAFZNDcYjsPq1nEx8jKA9y+Beo9v+7OBPRisQTjinQMw==} + resolution: {integrity: sha512-huCv9IH9Tcf95zuYCsQraZtWnJvBtLVE0QHMOs8bWyZAFZNDcYjsPq1nEx8jKA9y+Beo9v+7OBPRisQTjinQMw==, tarball: https://registry.npmjs.org/xml/-/xml-1.0.1.tgz} xmlchars@2.2.0: - resolution: {integrity: sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==} + resolution: {integrity: sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==, tarball: https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz} y-prosemirror@1.2.5: - resolution: {integrity: sha512-T/JATxC8P2Dbvq/dAiaiztD1a8KEwRP8oLRlT8YlaZdNlLGE1Ea0IJ8If25UlDYmk+4+uqLbqT/S+dzUmwwgbA==} + resolution: {integrity: sha512-T/JATxC8P2Dbvq/dAiaiztD1a8KEwRP8oLRlT8YlaZdNlLGE1Ea0IJ8If25UlDYmk+4+uqLbqT/S+dzUmwwgbA==, tarball: https://registry.npmjs.org/y-prosemirror/-/y-prosemirror-1.2.5.tgz} engines: {node: '>=16.0.0', npm: '>=8.0.0'} peerDependencies: prosemirror-model: ^1.7.1 @@ -15248,81 +15311,81 @@ packages: yjs: ^13.5.38 y-protocols@1.0.1: - resolution: {integrity: sha512-QP3fCM7c2gGfUi2nqf8gspyO4VW23zv3kNqPNdD3wNxMbuNQenMyoDVZYEo12jzR4RQ3aaDfPK62Sf31SVOmfg==} + resolution: {integrity: sha512-QP3fCM7c2gGfUi2nqf8gspyO4VW23zv3kNqPNdD3wNxMbuNQenMyoDVZYEo12jzR4RQ3aaDfPK62Sf31SVOmfg==, tarball: https://registry.npmjs.org/y-protocols/-/y-protocols-1.0.1.tgz} y18n@5.0.8: - resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} + resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==, tarball: https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz} engines: {node: '>=10'} yallist@3.1.1: - resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} + resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==, tarball: https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz} yaml@1.10.3: - resolution: {integrity: sha512-vIYeF1u3CjlhAFekPPAk2h/Kv4T3mAkMox5OymRiJQB0spDP10LHvt+K7G9Ny6NuuMAb25/6n1qyUjAcGNf/AA==} + resolution: {integrity: sha512-vIYeF1u3CjlhAFekPPAk2h/Kv4T3mAkMox5OymRiJQB0spDP10LHvt+K7G9Ny6NuuMAb25/6n1qyUjAcGNf/AA==, tarball: https://registry.npmjs.org/yaml/-/yaml-1.10.3.tgz} engines: {node: '>= 6'} yaml@2.5.1: - resolution: {integrity: sha512-bLQOjaX/ADgQ20isPJRvF0iRUHIxVhYvr53Of7wGcWlO2jvtUlH5m87DsmulFVxRpNLOnI4tB6p/oh8D7kpn9Q==} + resolution: {integrity: sha512-bLQOjaX/ADgQ20isPJRvF0iRUHIxVhYvr53Of7wGcWlO2jvtUlH5m87DsmulFVxRpNLOnI4tB6p/oh8D7kpn9Q==, tarball: https://registry.npmjs.org/yaml/-/yaml-2.5.1.tgz} engines: {node: '>= 14'} hasBin: true yaml@2.9.0: - resolution: {integrity: sha512-2AvhNX3mb8zd6Zy7INTtSpl1F15HW6Wnqj0srWlkKLcpYl/gMIMJiyuGq2KeI2YFxUPjdlB+3Lc10seMLtL4cA==} + resolution: {integrity: sha512-2AvhNX3mb8zd6Zy7INTtSpl1F15HW6Wnqj0srWlkKLcpYl/gMIMJiyuGq2KeI2YFxUPjdlB+3Lc10seMLtL4cA==, tarball: https://registry.npmjs.org/yaml/-/yaml-2.9.0.tgz} engines: {node: '>= 14.6'} hasBin: true yargs-parser@21.1.1: - resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==} + resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==, tarball: https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz} engines: {node: '>=12'} yargs-parser@22.0.0: - resolution: {integrity: sha512-rwu/ClNdSMpkSrUb+d6BRsSkLUq1fmfsY6TOpYzTwvwkg1/NRG85KBy3kq++A8LKQwX6lsu+aWad+2khvuXrqw==} + resolution: {integrity: sha512-rwu/ClNdSMpkSrUb+d6BRsSkLUq1fmfsY6TOpYzTwvwkg1/NRG85KBy3kq++A8LKQwX6lsu+aWad+2khvuXrqw==, tarball: https://registry.npmjs.org/yargs-parser/-/yargs-parser-22.0.0.tgz} engines: {node: ^20.19.0 || ^22.12.0 || >=23} yargs@17.7.2: - resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==} + resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==, tarball: https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz} engines: {node: '>=12'} yargs@17.7.3: - resolution: {integrity: sha512-GZtjxm/J/4TSxuL3FNYjCmLktBTnIw/rVmKSIyKeYAZpmJB2ig9VauCC5xsa82GNKVKDAqpOn3KVzNt0zmrU0g==} + resolution: {integrity: sha512-GZtjxm/J/4TSxuL3FNYjCmLktBTnIw/rVmKSIyKeYAZpmJB2ig9VauCC5xsa82GNKVKDAqpOn3KVzNt0zmrU0g==, tarball: https://registry.npmjs.org/yargs/-/yargs-17.7.3.tgz} engines: {node: '>=12'} yargs@18.0.0: - resolution: {integrity: sha512-4UEqdc2RYGHZc7Doyqkrqiln3p9X2DZVxaGbwhn2pi7MrRagKaOcIKe8L3OxYcbhXLgLFUS3zAYuQjKBQgmuNg==} + resolution: {integrity: sha512-4UEqdc2RYGHZc7Doyqkrqiln3p9X2DZVxaGbwhn2pi7MrRagKaOcIKe8L3OxYcbhXLgLFUS3zAYuQjKBQgmuNg==, tarball: https://registry.npmjs.org/yargs/-/yargs-18.0.0.tgz} engines: {node: ^20.19.0 || ^22.12.0 || >=23} yjs@13.5.38: - resolution: {integrity: sha512-YCHj6DkgxhIRqdxqTI+htGAhvfmgkS974pz+/OOiuLOj0EgGfUKvtp4yYyQGg1Wf3m4oLet9x7gEvCrxaGiVZQ==} + resolution: {integrity: sha512-YCHj6DkgxhIRqdxqTI+htGAhvfmgkS974pz+/OOiuLOj0EgGfUKvtp4yYyQGg1Wf3m4oLet9x7gEvCrxaGiVZQ==, tarball: https://registry.npmjs.org/yjs/-/yjs-13.5.38.tgz} yn@3.1.1: - resolution: {integrity: sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==} + resolution: {integrity: sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==, tarball: https://registry.npmjs.org/yn/-/yn-3.1.1.tgz} engines: {node: '>=6'} yocto-queue@0.1.0: - resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} + resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==, tarball: https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz} engines: {node: '>=10'} yocto-queue@1.2.2: - resolution: {integrity: sha512-4LCcse/U2MHZ63HAJVE+v71o7yOdIe4cZ70Wpf8D/IyjDKYQLV5GD46B+hSTjJsvV5PztjvHoU580EftxjDZFQ==} + resolution: {integrity: sha512-4LCcse/U2MHZ63HAJVE+v71o7yOdIe4cZ70Wpf8D/IyjDKYQLV5GD46B+hSTjJsvV5PztjvHoU580EftxjDZFQ==, tarball: https://registry.npmjs.org/yocto-queue/-/yocto-queue-1.2.2.tgz} engines: {node: '>=12.20'} yoctocolors@2.1.2: - resolution: {integrity: sha512-CzhO+pFNo8ajLM2d2IW/R93ipy99LWjtwblvC1RsoSUMZgyLbYFr221TnSNT7GjGdYui6P459mw9JH/g/zW2ug==} + resolution: {integrity: sha512-CzhO+pFNo8ajLM2d2IW/R93ipy99LWjtwblvC1RsoSUMZgyLbYFr221TnSNT7GjGdYui6P459mw9JH/g/zW2ug==, tarball: https://registry.npmjs.org/yoctocolors/-/yoctocolors-2.1.2.tgz} engines: {node: '>=18'} zod-to-json-schema@3.25.2: - resolution: {integrity: sha512-O/PgfnpT1xKSDeQYSCfRI5Gy3hPf91mKVDuYLUHZJMiDFptvP41MSnWofm8dnCm0256ZNfZIM7DSzuSMAFnjHA==} + resolution: {integrity: sha512-O/PgfnpT1xKSDeQYSCfRI5Gy3hPf91mKVDuYLUHZJMiDFptvP41MSnWofm8dnCm0256ZNfZIM7DSzuSMAFnjHA==, tarball: https://registry.npmjs.org/zod-to-json-schema/-/zod-to-json-schema-3.25.2.tgz} peerDependencies: zod: ^3.25.28 || ^4 zod@4.1.9: - resolution: {integrity: sha512-HI32jTq0AUAC125z30E8bQNz0RQ+9Uc+4J7V97gLYjZVKRjeydPgGt6dvQzFrav7MYOUGFqqOGiHpA/fdbd0cQ==} + resolution: {integrity: sha512-HI32jTq0AUAC125z30E8bQNz0RQ+9Uc+4J7V97gLYjZVKRjeydPgGt6dvQzFrav7MYOUGFqqOGiHpA/fdbd0cQ==, tarball: https://registry.npmjs.org/zod/-/zod-4.1.9.tgz} zod@4.4.3: - resolution: {integrity: sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ==} + resolution: {integrity: sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ==, tarball: https://registry.npmjs.org/zod/-/zod-4.4.3.tgz} zone.js@0.15.1: - resolution: {integrity: sha512-XE96n56IQpJM7NAoXswY3XRLcWFW83xe0BiAOeMD7K5k5xecOeul3Qcpx6GqEeeHNkW5DWL5zOyTbEfB4eti8w==} + resolution: {integrity: sha512-XE96n56IQpJM7NAoXswY3XRLcWFW83xe0BiAOeMD7K5k5xecOeul3Qcpx6GqEeeHNkW5DWL5zOyTbEfB4eti8w==, tarball: https://registry.npmjs.org/zone.js/-/zone.js-0.15.1.tgz} ignoredOptionalDependencies: - canvas @@ -15850,27 +15913,6 @@ snapshots: tinyexec: 1.2.4 optional: true - '@apidevtools/json-schema-ref-parser@11.7.2': - dependencies: - '@jsdevtools/ono': 7.1.3 - '@types/json-schema': 7.0.15 - js-yaml: 4.3.0 - - '@apidevtools/openapi-schemas@2.1.0': {} - - '@apidevtools/swagger-methods@3.0.2': {} - - '@apidevtools/swagger-parser@10.1.1(openapi-types@12.1.3)': - dependencies: - '@apidevtools/json-schema-ref-parser': 11.7.2 - '@apidevtools/openapi-schemas': 2.1.0 - '@apidevtools/swagger-methods': 3.0.2 - '@jsdevtools/ono': 7.1.3 - ajv: 8.20.0 - ajv-draft-04: 1.0.0(ajv@8.20.0) - call-me-maybe: 1.0.2 - openapi-types: 12.1.3 - '@asamuzakjp/css-color@5.1.11': dependencies: '@asamuzakjp/generational-cache': 1.0.1 @@ -17904,6 +17946,10 @@ snapshots: dependencies: hono: 4.12.30 + '@hono/node-server@2.0.12(hono@4.12.30)': + dependencies: + hono: 4.12.30 + '@humanfs/core@0.19.2': dependencies: '@humanfs/types': 0.15.0 @@ -18452,8 +18498,6 @@ snapshots: '@jridgewell/resolve-uri': 3.1.2 '@jridgewell/sourcemap-codec': 1.5.5 - '@jsdevtools/ono@7.1.3': {} - '@jsonjoy.com/base64@1.1.2(tslib@2.3.0)': dependencies: tslib: 2.3.0 @@ -21185,6 +21229,10 @@ snapshots: '@one-ini/wasm@0.1.1': {} + '@opencode-ai/sdk@1.18.13': + dependencies: + cross-spawn: 7.0.6 + '@openng/spectator@1.0.1(@angular/animations@22.1.0(@angular/core@22.1.0(@angular/compiler@22.1.0)(rxjs@7.8.2)(zone.js@0.15.1)))(@angular/common@22.1.0(@angular/core@22.1.0(@angular/compiler@22.1.0)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/router@22.1.0(@angular/common@22.1.0(@angular/core@22.1.0(@angular/compiler@22.1.0)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@22.1.0(@angular/compiler@22.1.0)(rxjs@7.8.2)(zone.js@0.15.1))(@angular/platform-browser@22.1.0(@angular/animations@22.1.0(@angular/core@22.1.0(@angular/compiler@22.1.0)(rxjs@7.8.2)(zone.js@0.15.1)))(@angular/common@22.1.0(@angular/core@22.1.0(@angular/compiler@22.1.0)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@22.1.0(@angular/compiler@22.1.0)(rxjs@7.8.2)(zone.js@0.15.1)))(rxjs@7.8.2))': dependencies: '@angular/animations': 22.1.0(@angular/core@22.1.0(@angular/compiler@22.1.0)(rxjs@7.8.2)(zone.js@0.15.1)) @@ -21194,6 +21242,11 @@ snapshots: jquery: 3.7.1 tslib: 2.8.1 + '@openrouter/ai-sdk-provider@2.10.0(ai@6.0.225(zod@4.1.9))(zod@4.1.9)': + dependencies: + ai: 6.0.225(zod@4.1.9) + zod: 4.1.9 + '@opentelemetry/api@1.9.1': {} '@oxc-parser/binding-android-arm-eabi@0.142.0': @@ -23990,6 +24043,13 @@ snapshots: agent-base@9.0.0: {} + ai-sdk-provider-opencode-sdk@3.0.6(zod@4.1.9): + dependencies: + '@ai-sdk/provider': 3.0.14 + '@ai-sdk/provider-utils': 4.0.38(zod@4.1.9) + '@opencode-ai/sdk': 1.18.13 + zod: 4.1.9 + ai@6.0.225(zod@4.1.9): dependencies: '@ai-sdk/gateway': 3.0.149(zod@4.1.9) @@ -24002,10 +24062,6 @@ snapshots: optionalDependencies: ajv: 8.18.0 - ajv-draft-04@1.0.0(ajv@8.20.0): - optionalDependencies: - ajv: 8.20.0 - ajv-formats@2.1.1(ajv@8.20.0): optionalDependencies: ajv: 8.20.0 @@ -24535,6 +24591,8 @@ snapshots: boolbase@1.0.0: {} + boolbase@2.0.0: {} + brace-expansion@1.1.16: dependencies: balanced-match: 1.0.2 @@ -24614,8 +24672,6 @@ snapshots: call-bind-apply-helpers: 1.0.2 get-intrinsic: 1.3.0 - call-me-maybe@1.0.2: {} - callsites@3.1.0: {} camelcase@5.3.1: {} @@ -25119,6 +25175,14 @@ snapshots: domutils: 3.2.2 nth-check: 2.1.1 + css-select@7.0.0: + dependencies: + boolbase: 2.0.0 + css-what: 8.0.0 + domhandler: 6.0.1 + domutils: 4.0.2 + nth-check: 3.0.1 + css-tree@1.1.3: dependencies: mdn-data: 2.0.14 @@ -25143,6 +25207,8 @@ snapshots: css-what@7.0.0: {} + css-what@8.0.0: {} + css.escape@1.5.1: {} cssesc@3.0.0: {} @@ -25733,6 +25799,12 @@ snapshots: domhandler: 5.0.3 entities: 4.5.0 + dom-serializer@3.1.1: + dependencies: + domelementtype: 3.0.0 + domhandler: 6.0.1 + entities: 8.0.0 + dom-set@1.1.1: dependencies: array-from: 2.1.1 @@ -25741,6 +25813,8 @@ snapshots: domelementtype@2.3.0: {} + domelementtype@3.0.0: {} + domexception@4.0.0: dependencies: webidl-conversions: 7.0.0 @@ -25753,6 +25827,10 @@ snapshots: dependencies: domelementtype: 2.3.0 + domhandler@6.0.1: + dependencies: + domelementtype: 3.0.0 + dompurify@3.4.12: optionalDependencies: '@types/trusted-types': 2.0.7 @@ -25770,6 +25848,12 @@ snapshots: domelementtype: 2.3.0 domhandler: 5.0.3 + domutils@4.0.2: + dependencies: + dom-serializer: 3.1.1 + domelementtype: 3.0.0 + domhandler: 6.0.1 + dot-case@3.0.4: dependencies: no-case: 3.0.4 @@ -27171,6 +27255,13 @@ snapshots: domutils: 3.2.2 entities: 7.0.1 + htmlparser2@12.0.0: + dependencies: + domelementtype: 3.0.0 + domhandler: 6.0.1 + domutils: 4.0.2 + entities: 8.0.0 + http-assert@1.5.0: dependencies: deep-equal: 1.0.1 @@ -29491,6 +29582,10 @@ snapshots: dependencies: boolbase: 1.0.0 + nth-check@3.0.1: + dependencies: + boolbase: 2.0.0 + nwsapi@2.2.24: {} nx@21.6.11(@swc-node/register@1.11.1(@swc/core@1.15.8(@swc/helpers@0.5.23))(@swc/types@0.1.27)(typescript@6.0.3))(@swc/core@1.15.8(@swc/helpers@0.5.23)): @@ -29779,8 +29874,6 @@ snapshots: is-docker: 2.2.1 is-wsl: 2.2.0 - openapi-types@12.1.3: {} - opener@1.5.2: {} optionator@0.9.4: diff --git a/core-web/tsconfig.base.json b/core-web/tsconfig.base.json index 6151940a067c..70844e64484d 100644 --- a/core-web/tsconfig.base.json +++ b/core-web/tsconfig.base.json @@ -20,6 +20,7 @@ "paths": { "@components/*": ["apps/dotcms-ui/src/app/view/components/*"], "@directives/*": ["apps/dotcms-ui/src/app/view/directives/*"], + "@dotcms/ai-ui": ["libs/ai-ui/src/index.ts"], "@dotcms/analytics": ["libs/sdk/analytics/src/index.ts"], "@dotcms/angular": ["libs/sdk/angular/src/public_api.ts"], "@dotcms/app/*": ["apps/dotcms-ui/src/app/*"], @@ -109,13 +110,13 @@ "@shared/*": ["apps/dotcms-ui/src/app/shared/*"], "@tests/*": ["apps/dotcms-ui/src/app/test/*"], "sdk-create-app": ["libs/sdk/create-app/src/index.ts"], - "@dotcms/agentic-tools": ["libs/agentic-tools/src/index.ts"], "@dotcms/image-editor": ["libs/image-editor/src/index.ts"], "@dotcms/ai/runtime": ["libs/sdk/ai/src/runtime.ts"], "@dotcms/ai/sandbox": ["libs/sdk/ai/src/sandbox/index.ts"], "@dotcms/ai/adapter": ["libs/sdk/ai/src/adapter/index.ts"], "@dotcms/ai/spec": ["libs/sdk/ai/src/spec/index.ts"], - "@dotcms/vue": ["./libs/sdk/vue/src/index.ts"] + "@dotcms/vue": ["./libs/sdk/vue/src/index.ts"], + "@dotcms/portlets/dot-agents/portlet": ["libs/portlets/dot-agents/src/index.ts"] } }, "exclude": ["node_modules", "tmp"] diff --git a/dotCMS/src/main/java/com/dotcms/content/elasticsearch/business/ContentFactoryIndexOperationsES.java b/dotCMS/src/main/java/com/dotcms/content/elasticsearch/business/ContentFactoryIndexOperationsES.java index 1d40cb3e2d6b..6b6c588b2241 100644 --- a/dotCMS/src/main/java/com/dotcms/content/elasticsearch/business/ContentFactoryIndexOperationsES.java +++ b/dotCMS/src/main/java/com/dotcms/content/elasticsearch/business/ContentFactoryIndexOperationsES.java @@ -398,11 +398,20 @@ SearchSourceBuilder createSearchSourceBuilder(final String query, final String s return searchSourceBuilder; } + /** + * Adds keyword-field sorts. The public/canonical form remains an unsuffixed field name; + * accepting an existing {@code _dotraw} suffix is a compatibility path and must not append a + * second suffix. Thus existing consumers keep the same generated field while callers that + * historically supplied the mapped field directly no longer target a nonexistent mapping. + */ public static void addBuilderSort(@NotNull String sortBy, SearchSourceBuilder srb) { String[] sortByArr = sortBy.split(","); for (String sort : sortByArr) { String[] x = sort.trim().split(" "); - srb.sort(SortBuilders.fieldSort(x[0].toLowerCase() + "_dotraw") + final String requestedField = x[0].toLowerCase(); + final String field = requestedField.endsWith("_dotraw") + ? requestedField : requestedField + "_dotraw"; + srb.sort(SortBuilders.fieldSort(field) .order(x.length > 1 && x[1].equalsIgnoreCase("desc") ? SortOrder.DESC : SortOrder.ASC)); } diff --git a/dotCMS/src/main/java/com/dotcms/content/index/opensearch/ContentFactoryIndexOperationsOS.java b/dotCMS/src/main/java/com/dotcms/content/index/opensearch/ContentFactoryIndexOperationsOS.java index 0f57d2c45b4a..1e56fdeb85bd 100644 --- a/dotCMS/src/main/java/com/dotcms/content/index/opensearch/ContentFactoryIndexOperationsOS.java +++ b/dotCMS/src/main/java/com/dotcms/content/index/opensearch/ContentFactoryIndexOperationsOS.java @@ -351,13 +351,22 @@ private void addSorting(SearchRequest.Builder searchRequestBuilder, String sortB } } + /** + * Adds keyword-field sorts. The public/canonical form remains an unsuffixed field name; + * accepting an existing {@code _dotraw} suffix is a compatibility path and must not append a + * second suffix. Thus existing consumers keep the same generated field while callers that + * historically supplied the mapped field directly no longer target a nonexistent mapping. + */ public static void addBuilderSort(@NotNull String sortBy, SearchRequest.Builder searchRequestBuilder) { String[] sortByArr = sortBy.split(","); for (String sort : sortByArr) { String[] x = sort.trim().split(" "); SortOrder order = x.length > 1 && x[1].equalsIgnoreCase("desc") ? SortOrder.Desc : SortOrder.Asc; + final String requestedField = x[0].toLowerCase(); + final String field = requestedField.endsWith("_dotraw") + ? requestedField : requestedField + "_dotraw"; searchRequestBuilder.sort(SortOptions.of(so -> so.field(FieldSort.of(fs -> fs - .field(x[0].toLowerCase() + "_dotraw") + .field(field) .order(order) .unmappedType(FieldType.Keyword))))); } @@ -474,4 +483,4 @@ public IndexContentletScroll createScrollQuery(String luceneQuery, User user, boolean respectFrontendRoles, int batchSize) { return createScrollQuery(luceneQuery, user, respectFrontendRoles, batchSize, "title asc"); } -} \ No newline at end of file +} diff --git a/dotCMS/src/main/java/com/dotcms/contenttype/model/field/Field.java b/dotCMS/src/main/java/com/dotcms/contenttype/model/field/Field.java index 3e7323ca0b4e..b60a0d8a0bd1 100644 --- a/dotCMS/src/main/java/com/dotcms/contenttype/model/field/Field.java +++ b/dotCMS/src/main/java/com/dotcms/contenttype/model/field/Field.java @@ -345,7 +345,16 @@ public ClassNameAliasResolver() { public JavaType typeFromId(final DatabindContext context, final String id) throws IOException { final String packageName = Field.class.getPackageName(); if( !id.contains(".") && !id.startsWith(packageName)){ - final String className = String.format("%s.Immutable%s",packageName,id); + // Accept ergonomic short forms for the `clazz` discriminator so callers (e.g. AI agents) + // don't have to know the fully-qualified Immutable* field class name: + // - a field-type name or legacy value -> "TEXT"/"text", "STORY_BLOCK_FIELD", + // "CHECKBOX", "SELECT", "KEY_VALUE", "CUSTOM_FIELD", ... (case-insensitive) + // - the concrete simple class name -> "TextField", "StoryBlockField", ... + // In every case we resolve to the generated Immutable* class Jackson expects. + final String simpleName = Optional.ofNullable(LegacyFieldTypes.implClassForName(id)) + .map(Class::getSimpleName) + .orElse(id); + final String className = String.format("%s.Immutable%s", packageName, simpleName); return super.typeFromId(context, className); } return super.typeFromId(context, id); diff --git a/dotCMS/src/main/java/com/dotcms/contenttype/model/field/LegacyFieldTypes.java b/dotCMS/src/main/java/com/dotcms/contenttype/model/field/LegacyFieldTypes.java index 4b4981d6e320..0fe6ef56a86f 100644 --- a/dotCMS/src/main/java/com/dotcms/contenttype/model/field/LegacyFieldTypes.java +++ b/dotCMS/src/main/java/com/dotcms/contenttype/model/field/LegacyFieldTypes.java @@ -105,10 +105,37 @@ public Class<? extends Field> implClass (){ return this.implClass; } + /** + * Resolves an ergonomic short field-type name to its concrete {@link Field} implementation + * class, so callers (e.g. AI agents) don't have to know the fully-qualified {@code Immutable*} + * class name. Two case-insensitive forms are accepted: + * <ul> + * <li>the enum name — {@code "TEXT"}, {@code "STORY_BLOCK_FIELD"}, {@code "CHECKBOX"}</li> + * <li>the legacy value — {@code "text"}, {@code "story_block_field"}, {@code "checkbox"}</li> + * </ul> + * + * @param name The short field-type name. + * @return The matching field implementation class, or {@code null} if {@code name} is not a + * known short field-type name. + */ + public static Class<? extends Field> implClassForName(final String name) { + if (name == null) { + return null; + } + final String trimmed = name.trim(); + for (final LegacyFieldTypes fieldType : LegacyFieldTypes.values()) { + if (fieldType.name().equalsIgnoreCase(trimmed) + || fieldType.legacyValue.equalsIgnoreCase(trimmed)) { + return fieldType.implClass; + } + } + return null; + } + /** * Returns the new field class associated to the specified legacy field * type. - * + * * @param legacyValue * - The legacy field type. * @return The class of the new field. diff --git a/dotCMS/src/main/java/com/dotcms/contenttype/model/type/ContentType.java b/dotCMS/src/main/java/com/dotcms/contenttype/model/type/ContentType.java index 72daa9c02dba..5d49f58f5bea 100644 --- a/dotCMS/src/main/java/com/dotcms/contenttype/model/type/ContentType.java +++ b/dotCMS/src/main/java/com/dotcms/contenttype/model/type/ContentType.java @@ -27,6 +27,9 @@ import com.dotmarketing.util.Logger; import com.dotmarketing.util.UUIDUtil; import com.dotmarketing.util.UtilMethods; + +import java.util.Arrays; +import java.util.Optional; import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonSubTypes; import com.fasterxml.jackson.annotation.JsonSubTypes.Type; @@ -534,11 +537,45 @@ public ClassNameAliasResolver() { ); } + /** + * Finds the {@link BaseContentType} whose name or alternate name matches {@code id}, + * without the exception-and-INFO-log that {@link BaseContentType#getBaseContentType(String)} + * raises on a miss. A miss is the NORMAL case here - the caller may equally be passing a + * concrete class simple name - so it must not be an exceptional path. + * + * @param id the discriminator value supplied by the caller + * @return the matching base type, or empty when {@code id} does not name one + */ + private static Optional<BaseContentType> findBaseContentType(final String id) { + return Arrays.stream(BaseContentType.values()) + .filter(baseType -> baseType != BaseContentType.ANY) + .filter(baseType -> baseType.name().equalsIgnoreCase(id) + || (UtilMethods.isSet(baseType.alternateName) + && baseType.alternateName.equalsIgnoreCase(id))) + .findFirst(); + } + @Override public JavaType typeFromId(final DatabindContext context, final String id) throws IOException { final String packageName = ContentType.class.getPackageName(); if( !id.contains(".") && !id.startsWith(packageName)){ - final String className = String.format("%s.Immutable%s",packageName,id); + // Accept ergonomic short forms for the `clazz` discriminator so callers (e.g. AI agents) + // don't have to know the fully-qualified Immutable* class name: + // - a base-type name or alias -> "CONTENT"/"Content", "WIDGET", "FORM"/"Form", + // "FILEASSET"/"File", "HTMLPAGE"/"Page", "PERSONA", "VANITY_URL"/"VanityURL", + // "KEY_VALUE"/"KeyValue", "DOTASSET"/"DotAsset" + // - the concrete simple class name -> "SimpleContentType", "WidgetContentType", ... + // In every case we resolve to the generated Immutable* class Jackson expects. + // Resolved by lookup rather than by catching: BaseContentType.getBaseContentType + // logs at INFO and throws IllegalArgumentException on every miss, and the + // simple-class-name form below (still supported, e.g. "WidgetContentType") ALWAYS + // misses that lookup. Catching it per object meant one thrown exception and one INFO + // line for every content type in a push-publish bundle or CT import. Field.typeFromId + // already does the same job this way; the two now match. + final String simpleName = findBaseContentType(id) + .map(baseType -> baseType.immutableClass().getSimpleName()) + .orElse(id); + final String className = String.format("%s.Immutable%s", packageName, simpleName); return super.typeFromId(context, className); } return super.typeFromId(context, id); diff --git a/dotCMS/src/main/java/com/dotcms/rendering/velocity/viewtools/content/util/ContentUtils.java b/dotCMS/src/main/java/com/dotcms/rendering/velocity/viewtools/content/util/ContentUtils.java index 54ef33fc8403..8534057c3912 100644 --- a/dotCMS/src/main/java/com/dotcms/rendering/velocity/viewtools/content/util/ContentUtils.java +++ b/dotCMS/src/main/java/com/dotcms/rendering/velocity/viewtools/content/util/ContentUtils.java @@ -181,7 +181,10 @@ public static Contentlet find(final String inodeOrIdentifierIn, final User user, * Returns empty List if no results are found * @param query - Lucene Query used to search for content - Will append live, working, deleted, and language if not passed * @param limit 0 is the dotCMS max limit which is 10000. Becareful when searching for unlimited amount as all content will load into memory - * @param sort - Velocity variable name to sort by. this is a string and can contain multiple values "sort1 acs, sort2 desc" + * @param sort - Velocity variable name to sort by. This is a string and can contain multiple values + * such as "Book.title asc, modDate desc". The search layer sorts text fields on their + * keyword mapping and appends {@code _dotraw} automatically; callers should prefer the + * unsuffixed field name. An already-suffixed name is also accepted and is not doubled. * @return Returns empty List if no results are found */ public static List<Contentlet> pull(String query, String limit, String sort,User user, String tmDate){ diff --git a/dotCMS/src/main/java/com/dotcms/rest/SearchForm.java b/dotCMS/src/main/java/com/dotcms/rest/SearchForm.java index 5ea96197d110..d63a2514bd77 100644 --- a/dotCMS/src/main/java/com/dotcms/rest/SearchForm.java +++ b/dotCMS/src/main/java/com/dotcms/rest/SearchForm.java @@ -3,22 +3,60 @@ import com.dotmarketing.business.APILocator; import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import io.swagger.v3.oas.annotations.media.Schema; /** * Search Form to make a ES query * @author jsanca */ +@Schema(description = "Content search request. `query` is a Lucene expression; the remaining fields " + + "control paging, sorting, language, and how results are rendered.") @JsonDeserialize(builder = SearchForm.Builder.class) public class SearchForm { + @Schema( + description = "Lucene query. IMPORTANT: custom (user-defined) fields MUST be qualified with the " + + "content type's variable name — `ContentTypeVar.fieldVar`. A BARE field name matches " + + "nothing and returns zero results with NO error (a common silent failure). " + + "For example, to find featured Books use `+AwazonBook.featured:true`, not `+featured:true`; " + + "to match a slug use `+AwazonBook.slug:my-slug`. " + + "Restrict the type with `+contentType:AwazonBook` (contentType is a system field, unqualified). " + + "\n\nResults are also HOST-scoped: unless you add a host clause, the search resolves against " + + "the current request's site and will NOT return content that lives on a different host " + + "(e.g. content saved to `SYSTEM_HOST`/default while you query as another site). " + + "Constrain the host explicitly with `+conHost:<siteIdentifier>` (or `+conHost:SYSTEM_HOST`), " + + "and add `+live:true` / `+working:true` and `+deleted:false` as needed. " + + "See the Lucene content-search syntax docs.", + example = "+contentType:AwazonBook +AwazonBook.featured:true +live:true +deleted:false") private final String query; + + @Schema(description = "Sort clause, e.g. `AwazonBook.title asc` or `modDate desc`. Custom fields are " + + "qualified the same way as in `query`.", example = "modDate desc") private final String sort; + + @Schema(description = "Maximum number of contentlets to return (page size).", example = "20", defaultValue = "20") private final int limit; + + @Schema(description = "Zero-based result offset for paging.", example = "0", defaultValue = "0") private final int offset; + + @Schema(description = "Optional user id to run the search as (permissions are applied for this user). " + + "Defaults to the authenticated caller when omitted.") private final String userId; + + @Schema(description = "When set to `true`, each matching contentlet's `htmlpageasset`/widget content is " + + "rendered and included in the response. Omit for raw field data only.") private final String render; + + @Schema(description = "Relationship-loading depth (0-3): how many levels of related content to inline. " + + "`-1` (default) loads none.", example = "1", defaultValue = "-1") private final int depth; + + @Schema(description = "Language id to search in. Defaults to the system default language when omitted.", + example = "1") private final long languageId; + + @Schema(description = "When `true`, include full category metadata for category fields in the results.") private final boolean allCategoriesInfo; private SearchForm (final Builder builder) { diff --git a/dotCMS/src/main/java/com/dotcms/rest/api/v1/a11yagent/A11yAgentResource.java b/dotCMS/src/main/java/com/dotcms/rest/api/v1/a11yagent/A11yAgentResource.java new file mode 100644 index 000000000000..b3c049dd67fe --- /dev/null +++ b/dotCMS/src/main/java/com/dotcms/rest/api/v1/a11yagent/A11yAgentResource.java @@ -0,0 +1,907 @@ +package com.dotcms.rest.api.v1.a11yagent; + +import com.dotcms.auth.providers.jwt.beans.ApiToken; +import com.dotcms.rest.ErrorEntity; +import com.dotcms.rest.InitDataObject; +import com.dotcms.rest.ResponseEntityView; +import com.dotcms.rest.WebResource; +import com.dotcms.rest.api.v1.DotObjectMapperProvider; +import com.dotcms.rest.annotation.NoCache; +import com.dotcms.security.apps.AppSecrets; +import com.dotcms.security.apps.Secret; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.dotmarketing.beans.Host; +import com.dotmarketing.business.APILocator; +import com.dotmarketing.portlets.contentlet.model.Contentlet; +import com.dotmarketing.portlets.htmlpageasset.model.IHTMLPage; +import com.dotmarketing.util.Config; +import com.dotmarketing.util.Logger; +import com.dotmarketing.util.UtilMethods; +import com.liferay.portal.model.User; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.responses.ApiResponses; +import io.swagger.v3.oas.annotations.tags.Tag; +import io.vavr.control.Try; +import org.glassfish.jersey.media.sse.EventOutput; +import org.glassfish.jersey.media.sse.OutboundEvent; +import org.glassfish.jersey.media.sse.SseFeature; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.ws.rs.Consumes; +import javax.ws.rs.GET; +import javax.ws.rs.POST; +import javax.ws.rs.Path; +import javax.ws.rs.Produces; +import javax.ws.rs.core.Context; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.net.URI; +import java.net.http.HttpClient; +import java.net.http.HttpRequest; +import java.net.http.HttpResponse; +import java.nio.charset.StandardCharsets; +import java.time.Duration; +import java.util.Date; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.Optional; + +/** + * REST resource that acts as the a11y-fix agent proxy. + * + * <p>Auth half: reuses {@code PageScannerResource}'s pattern — authenticates the backend + * user, mints a short-lived JWT, resolves the page identifier to a fully-qualified payload. + * + * <p>Forward half: + * <ul> + * <li>{@code POST /fix} — plain JSON relay (agent returns the run report)</li> + * <li>{@code POST /fix/stream} — streaming SSE relay ({@link EventOutput}); relays + * agent SSE frames as they arrive via {@code BodyHandlers.ofInputStream()}</li> + * <li>{@code POST /stop} — forwards to agent /stop, passes the minted JWT</li> + * <li>{@code GET /active-run} — forwards to agent /active-run, passes the minted JWT</li> + * </ul> + * + * <p>GZIPFilter is not registered in {@code web.xml} so no buffering risk for the SSE path. + */ +@Path("/v1/agents/a11y") +@Tag(name = "Accessibility Agent", description = "Streaming a11y-fix agent proxy") +public class A11yAgentResource { + + static final String APP_KEY = "dotPageScanner-config"; + + private final WebResource webResource; + private final HttpClient httpClient; + + public A11yAgentResource() { + this.webResource = new WebResource(); + this.httpClient = HttpClient.newBuilder() + .connectTimeout(Duration.ofSeconds(30)) + .build(); + } + + /** Package-private constructor for unit tests. */ + A11yAgentResource(final WebResource webResource, final HttpClient httpClient) { + this.webResource = webResource; + this.httpClient = httpClient; + } + + // ------------------------------------------------------------------------- + // POST /fix — plain JSON relay + // ------------------------------------------------------------------------- + + /** + * Proxies a fix request to the agent service and returns the JSON run report. + */ + @POST + @Path("/fix") + @NoCache + @Consumes(MediaType.APPLICATION_JSON) + @Produces(MediaType.APPLICATION_JSON) + @Operation( + operationId = "runA11yAgentFix", + summary = "Run the accessibility fix agent on a page", + description = "Resolves the page identifier to a live URL, URI and host id, mints a " + + "short-lived token for the calling user, and forwards the request to the " + + "configured a11y agent service. Returns the agent's report once the run " + + "completes. This call is synchronous and a full run can take minutes - use " + + "/fix/stream to receive progress as it happens. Requires the " + + "dotPageScanner-config App to carry the agent url and auth token." + ) + @ApiResponses(value = { + @ApiResponse(responseCode = "200", + description = "The agent's fix report, relayed verbatim from the agent service", + content = @Content(mediaType = "application/json")), + @ApiResponse(responseCode = "400", + description = "identifier is missing, or the page could not be resolved", + content = @Content(mediaType = "application/json", + schema = @Schema(implementation = ResponseEntityView.class))), + @ApiResponse(responseCode = "401", + description = "Authentication required", + content = @Content(mediaType = "application/json")), + @ApiResponse(responseCode = "500", + description = "The agent App is not configured, or the agent service failed", + content = @Content(mediaType = "application/json", + schema = @Schema(implementation = ResponseEntityView.class))) + }) + public Response fix( + @Context final HttpServletRequest request, + @Context final HttpServletResponse response, + final A11yAgentFixForm body) { + + final AgentContext ctx = buildContext(request, response, body); + if (ctx.errorResponse != null) { + return ctx.errorResponse; + } + + return forwardJson(ctx.agentUrl + "/fix", ctx.agentPayload, + ctx.serviceAuthToken, ctx.shortLivedToken); + } + + // ------------------------------------------------------------------------- + // POST /fix/stream — SSE streaming relay + // ------------------------------------------------------------------------- + + /** + * Proxies a fix request to the agent service and relays SSE frames as they arrive. + * + * <p>Uses {@code BodyHandlers.ofInputStream()} so the body is never buffered; frames are + * written to {@link EventOutput} line-by-line as they arrive from the upstream agent. + */ + @POST + @Path("/fix/stream") + @NoCache + @Consumes(MediaType.APPLICATION_JSON) + @Produces(SseFeature.SERVER_SENT_EVENTS) + @Operation( + operationId = "streamA11yAgentFix", + summary = "Run the accessibility fix agent, streaming progress over SSE", + description = "Same as /fix, but relays the agent's Server-Sent Events as they " + + "arrive rather than waiting for the run to finish. Frames carry the run id, " + + "phase steps, progress counts, heartbeats, and a terminal done, aborted or " + + "error event. A configuration failure is reported as an SSE error frame " + + "rather than an HTTP status, because the response has already begun." + ) + @ApiResponses(value = { + @ApiResponse(responseCode = "200", + description = "SSE stream of agent events (text/event-stream)", + content = @Content(mediaType = SseFeature.SERVER_SENT_EVENTS)), + @ApiResponse(responseCode = "401", + description = "Authentication required", + content = @Content(mediaType = "application/json")) + }) + public EventOutput fixStream( + @Context final HttpServletRequest request, + @Context final HttpServletResponse response, + final A11yAgentFixForm body) { + + final AgentContext ctx = buildContext(request, response, body); + final EventOutput output = new EventOutput(); + + if (ctx.errorResponse != null) { + writeErrorEvent(output, ctx.errorResponse.getStatus(), + "Proxy configuration error — check a11y-agent App secrets"); + return output; + } + + // Relay SSE frames asynchronously so the calling thread is not blocked. + final Thread relayThread = Thread.ofVirtual().start( + () -> relayStream(ctx.agentUrl + "/fix/stream", ctx.agentPayload, + ctx.serviceAuthToken, ctx.shortLivedToken, output)); + Logger.debug(this, () -> "SSE relay thread started: " + relayThread.getName()); + + return output; + } + + // ------------------------------------------------------------------------- + // POST /stop — stop the caller's in-flight run + // ------------------------------------------------------------------------- + + /** + * Forwards a stop request to the agent service, passing through the {@code runId} + * the client received from /fix or /fix/stream. Stop is addressed by runId (not by + * the caller's identity) because the proxy mints a fresh token per request, so the + * JWT {@code sub} differs between /fix and /stop — see {@link A11yAgentStopForm}. + */ + @POST + @Path("/stop") + @NoCache + @Consumes(MediaType.APPLICATION_JSON) + @Produces(MediaType.APPLICATION_JSON) + @Operation( + operationId = "stopA11yAgentRun", + summary = "Stop an in-flight accessibility fix run", + description = "Cooperatively stops the run identified by runId. The agent stops at " + + "its next safe checkpoint and the open /fix/stream connection receives a " + + "terminal aborted event carrying a partial report - fixes already applied " + + "are kept. Runs are addressed by runId rather than by caller identity, " + + "because the proxy mints a fresh token per request." + ) + @ApiResponses(value = { + @ApiResponse(responseCode = "202", + description = "Stop signalled, or no such run was active - both are success", + content = @Content(mediaType = "application/json")), + @ApiResponse(responseCode = "400", + description = "runId is missing", + content = @Content(mediaType = "application/json", + schema = @Schema(implementation = ResponseEntityView.class))), + @ApiResponse(responseCode = "401", + description = "Authentication required", + content = @Content(mediaType = "application/json")) + }) + public Response stop( + @Context final HttpServletRequest request, + @Context final HttpServletResponse response, + final A11yAgentStopForm body) { + + if (body == null || !UtilMethods.isSet(body.runId())) { + return Response.status(Response.Status.BAD_REQUEST) + .entity(new ResponseEntityView<>(new ErrorEntity( + "MISSING_RUN_ID", "runId is required"))) + .build(); + } + + final TokenContext ctx = buildTokenContext(request, response); + if (ctx.errorResponse != null) { + return ctx.errorResponse; + } + + final String payload = writeJson(Map.of("runId", body.runId())); + return forwardJson(ctx.agentUrl + "/stop", payload, + ctx.serviceAuthToken, ctx.shortLivedToken, "POST"); + } + + // ------------------------------------------------------------------------- + // GET /active-run — retrieve the caller's active or last run + // ------------------------------------------------------------------------- + + /** + * Forwards an active-run query to the agent service using the caller's minted JWT. + */ + @GET + @Path("/active-run") + @NoCache + @Produces(MediaType.APPLICATION_JSON) + @Operation( + operationId = "getA11yAgentActiveRun", + summary = "Get the caller's active or most recent agent run", + description = "Returns the run the agent service currently associates with the " + + "calling user, so a client that reconnects (a reload, or a second tab) can " + + "rejoin a run already in progress instead of starting a duplicate." + ) + @ApiResponses(value = { + @ApiResponse(responseCode = "200", + description = "The active or last run, relayed from the agent service", + content = @Content(mediaType = "application/json")), + @ApiResponse(responseCode = "401", + description = "Authentication required", + content = @Content(mediaType = "application/json")) + }) + public Response activeRun( + @Context final HttpServletRequest request, + @Context final HttpServletResponse response) { + + final TokenContext ctx = buildTokenContext(request, response); + if (ctx.errorResponse != null) { + return ctx.errorResponse; + } + + return forwardJson(ctx.agentUrl + "/active-run", null, + ctx.serviceAuthToken, ctx.shortLivedToken, "GET"); + } + + // ------------------------------------------------------------------------- + // Private helpers — context building + // ------------------------------------------------------------------------- + + /** + * Authenticates the user, resolves the page, mints a JWT, and builds the agent payload. + * Returns an {@link AgentContext} whose {@code errorResponse} is non-null on failure. + */ + private AgentContext buildContext( + final HttpServletRequest request, + final HttpServletResponse response, + final A11yAgentFixForm body) { + + final InitDataObject initData = new WebResource.InitBuilder(webResource) + .requiredBackendUser(true) + .requiredFrontendUser(false) + .requestAndResponse(request, response) + .rejectWhenNoUser(true) + .init(); + + final Optional<String[]> agentConfig = resolveAgentConfig(request); + if (agentConfig.isEmpty()) { + return AgentContext.error(Response.status(Response.Status.SERVICE_UNAVAILABLE) + .entity(new ResponseEntityView<>(new ErrorEntity( + "A11Y_AGENT_NOT_CONFIGURED", + "A11y Agent service is not available."))) + .build()); + } + final String agentUrl = agentConfig.get()[0]; + final String authToken = agentConfig.get()[1]; + + final User user = initData.getUser(); + + if (body == null || !UtilMethods.isSet(body.identifier())) { + return AgentContext.error(Response.status(Response.Status.BAD_REQUEST) + .entity(new ResponseEntityView<>(new ErrorEntity( + "MISSING_IDENTIFIER", "page.identifier is required"))) + .build()); + } + + final PageInfo pageInfo = resolvePage(body.identifier(), body.languageId(), request); + if (pageInfo == null) { + return AgentContext.error(Response.status(Response.Status.NOT_FOUND) + .entity(new ResponseEntityView<>(new ErrorEntity( + "PAGE_NOT_FOUND", "No page found for identifier: " + body.identifier()))) + .build()); + } + + final String shortLivedToken = mintShortLivedToken(user, request); + if (!UtilMethods.isSet(shortLivedToken)) { + return AgentContext.error(Response.status(Response.Status.INTERNAL_SERVER_ERROR) + .entity(new ResponseEntityView<>(new ErrorEntity( + "TOKEN_GENERATION_FAILED", "Unable to generate authentication token."))) + .build()); + } + + final String dotcmsBaseUrl = buildBaseUrl(request); + // No runId is minted here: the agent service owns run identity, so that it can key a + // run on the page being fixed (hostId + identifier + languageId, all sent below) and + // return the run already in flight instead of starting a second agent on the same + // page. The client learns the id from the stream's first `run` frame, and passes it + // back to /stop. + final String payload = buildAgentPayload(dotcmsBaseUrl, pageInfo, body.skipCss()); + + return new AgentContext(agentUrl, authToken, shortLivedToken, payload, null); + } + + /** Builds context for /stop and /active-run (no page needed, only auth + token). */ + private TokenContext buildTokenContext( + final HttpServletRequest request, + final HttpServletResponse response) { + + final InitDataObject initData = new WebResource.InitBuilder(webResource) + .requiredBackendUser(true) + .requiredFrontendUser(false) + .requestAndResponse(request, response) + .rejectWhenNoUser(true) + .init(); + + final Optional<String[]> agentConfig = resolveAgentConfig(request); + if (agentConfig.isEmpty()) { + return TokenContext.error(Response.status(Response.Status.SERVICE_UNAVAILABLE) + .entity(new ResponseEntityView<>(new ErrorEntity( + "A11Y_AGENT_NOT_CONFIGURED", + "A11y Agent service is not available."))) + .build()); + } + + final String shortLivedToken = mintShortLivedToken(initData.getUser(), request); + if (!UtilMethods.isSet(shortLivedToken)) { + return TokenContext.error(Response.status(Response.Status.INTERNAL_SERVER_ERROR) + .entity(new ResponseEntityView<>(new ErrorEntity( + "TOKEN_GENERATION_FAILED", "Unable to generate authentication token."))) + .build()); + } + + final String[] config = agentConfig.get(); + return new TokenContext(config[0], config[1], shortLivedToken, null); + } + + // ------------------------------------------------------------------------- + // Private helpers — forwarding + // ------------------------------------------------------------------------- + + /** Forward a request and return the upstream JSON body verbatim. */ + private Response forwardJson( + final String url, + final String payload, + final String serviceAuthToken, + final String shortLivedToken) { + return forwardJson(url, payload, serviceAuthToken, shortLivedToken, + payload != null ? "POST" : "GET"); + } + + private Response forwardJson( + final String url, + final String payload, + final String serviceAuthToken, + final String shortLivedToken, + final String method) { + + try { + final HttpRequest.Builder builder = HttpRequest.newBuilder() + .uri(URI.create(url)) + .timeout(Duration.ofSeconds(300)) + .header("Content-Type", MediaType.APPLICATION_JSON) + .header("auth-token", serviceAuthToken) + .header("Authorization", "Bearer " + shortLivedToken); + + if ("POST".equalsIgnoreCase(method) && payload != null) { + builder.POST(HttpRequest.BodyPublishers.ofString(payload)); + } else if ("POST".equalsIgnoreCase(method)) { + builder.POST(HttpRequest.BodyPublishers.noBody()); + } else { + builder.GET(); + } + + final HttpResponse<String> upstream = + httpClient.send(builder.build(), HttpResponse.BodyHandlers.ofString()); + + final int status = upstream.statusCode(); + if (status == 401 || status == 403) { + Logger.warn(A11yAgentResource.class, + "A11y agent returned " + status + " — check apiAuthToken in App config"); + } + + // Relay the upstream status and body verbatim — the agent owns its error + // shape and the Studio surfaces it directly. Only failures that never + // reached the agent (below) are synthesized here. + return Response.status(status).entity(upstream.body()) + .type(MediaType.APPLICATION_JSON).build(); + + } catch (Exception e) { + Logger.error(A11yAgentResource.class, + "Network error forwarding to a11y agent: " + e.getMessage(), e); + return Response.status(Response.Status.BAD_GATEWAY) + .entity(new ResponseEntityView<>(new ErrorEntity( + "A11Y_AGENT_UNREACHABLE", "Unable to reach the a11y agent service."))) + .build(); + } + } + + /** + * Opens an SSE connection to the upstream agent and relays each frame to {@code output} + * without buffering. Runs on a virtual thread. + * + * <p>SSE frames from the Hono agent follow the standard format: + * <pre> + * event: step + * data: {...} + * + * event: done + * data: {...} + * + * </pre> + * We relay the raw lines as-is into a single unnamed {@link OutboundEvent} per logical + * frame (the data value carries the raw SSE text so the Studio's EventSource parses it + * correctly). We detect the end of a frame by the blank-line delimiter, then flush. + */ + private void relayStream( + final String url, + final String payload, + final String serviceAuthToken, + final String shortLivedToken, + final EventOutput output) { + + try { + Logger.info(A11yAgentResource.class, "SSE relay → " + url); + final HttpRequest httpRequest = HttpRequest.newBuilder() + .uri(URI.create(url)) + .timeout(Duration.ofSeconds(300)) + .header("Content-Type", MediaType.APPLICATION_JSON) + .header("Accept", SseFeature.SERVER_SENT_EVENTS) + .header("auth-token", serviceAuthToken) + .header("Authorization", "Bearer " + shortLivedToken) + .POST(HttpRequest.BodyPublishers.ofString(payload)) + .build(); + + final HttpResponse<InputStream> upstream = + httpClient.send(httpRequest, HttpResponse.BodyHandlers.ofInputStream()); + + final int status = upstream.statusCode(); + Logger.info(A11yAgentResource.class, "SSE relay upstream status: " + status); + if (status == 401 || status == 403) { + Logger.warn(A11yAgentResource.class, + "A11y agent returned " + status + " — check apiAuthToken in App config"); + } + if (status >= 400) { + // Relay the agent's own error body verbatim rather than synthesizing + // one — the agent owns its error shape. + writeUpstreamErrorEvent(output, upstream.body()); + return; + } + Logger.info(A11yAgentResource.class, "SSE relay: reading frames from upstream"); + + try (final BufferedReader reader = new BufferedReader( + new InputStreamReader(upstream.body(), StandardCharsets.UTF_8))) { + + String eventName = null; + final StringBuilder dataBuilder = new StringBuilder(); + + String line; + while ((line = reader.readLine()) != null) { + if (output.isClosed()) { + Logger.debug(A11yAgentResource.class, + "Client disconnected; stopping SSE relay"); + break; + } + + if (line.startsWith("event:")) { + eventName = line.substring("event:".length()).trim(); + } else if (line.startsWith("data:")) { + if (dataBuilder.length() > 0) { + dataBuilder.append('\n'); + } + dataBuilder.append(line.substring("data:".length()).trim()); + } else if (line.isEmpty()) { + // blank line = end of frame; flush if we have data + if (dataBuilder.length() > 0) { + final String name = eventName; + final String data = dataBuilder.toString(); + final OutboundEvent.Builder evtBuilder = new OutboundEvent.Builder() + .mediaType(MediaType.APPLICATION_JSON_TYPE) + .data(String.class, data); + if (name != null) { + evtBuilder.name(name); + } + output.write(evtBuilder.build()); + } + eventName = null; + dataBuilder.setLength(0); + } + } + + // Flush any trailing frame (stream ended without trailing blank line) + if (dataBuilder.length() > 0 && !output.isClosed()) { + final OutboundEvent.Builder evtBuilder = new OutboundEvent.Builder() + .mediaType(MediaType.APPLICATION_JSON_TYPE) + .data(String.class, dataBuilder.toString()); + if (eventName != null) { + evtBuilder.name(eventName); + } + output.write(evtBuilder.build()); + } + } + + } catch (Exception e) { + Logger.error(A11yAgentResource.class, + "Error relaying SSE stream from a11y agent: " + e.getMessage(), e); + writeErrorEvent(output, 502, "Stream relay error: " + e.getMessage()); + } finally { + try { + output.close(); + } catch (IOException e) { + Logger.warn(A11yAgentResource.class, + "Error closing EventOutput: " + e.getMessage()); + } + } + } + + /** + * Relays the agent's own error body as the terminal {@code error} SSE frame, byte for + * byte, so the Studio sees exactly what the agent sent. Falls back to a synthesized + * frame only when the upstream body is empty or unreadable — i.e. when there is no + * agent error to pass through. + */ + private static void writeUpstreamErrorEvent(final EventOutput output, + final InputStream upstreamBody) { + String body = null; + try (final InputStream in = upstreamBody) { + body = new String(in.readAllBytes(), StandardCharsets.UTF_8).trim(); + } catch (Exception e) { + Logger.warn(A11yAgentResource.class, + "Could not read a11y agent error body: " + e.getMessage()); + } + + if (!UtilMethods.isSet(body)) { + writeErrorEvent(output, 502, "Agent returned an error with no body."); + return; + } + + // A JSON body is relayed byte for byte. A non-JSON body (an HTML error page from + // an intermediary, say) is wrapped into {"message": "..."} so the text still + // reaches the client instead of failing JSON.parse into a generic message. + final String data = body.startsWith("{") || body.startsWith("[") + ? body + : writeJson(Map.of("message", body)); + + try { + output.write(new OutboundEvent.Builder() + .name("error") + .mediaType(MediaType.APPLICATION_JSON_TYPE) + .data(String.class, data) + .build()); + } catch (IOException e) { + Logger.warn(A11yAgentResource.class, + "Error writing SSE error event: " + e.getMessage()); + } finally { + try { + output.close(); + } catch (IOException e) { + Logger.warn(A11yAgentResource.class, "Error closing EventOutput: " + e.getMessage()); + } + } + } + + private static void writeErrorEvent(final EventOutput output, final int status, + final String message) { + try { + final Map<String, Object> error = new LinkedHashMap<>(); + error.put("type", "error"); + error.put("status", status); + error.put("message", message); + final String data = writeJson(error); + output.write(new OutboundEvent.Builder() + .name("error") + .mediaType(MediaType.APPLICATION_JSON_TYPE) + .data(String.class, data) + .build()); + } catch (IOException e) { + Logger.warn(A11yAgentResource.class, "Error writing SSE error event: " + e.getMessage()); + } finally { + try { + output.close(); + } catch (IOException e) { + Logger.warn(A11yAgentResource.class, "Error closing EventOutput: " + e.getMessage()); + } + } + } + + // ------------------------------------------------------------------------- + // Private helpers — page resolution + // ------------------------------------------------------------------------- + + private PageInfo resolvePage(final String identifier, final int languageId, + final HttpServletRequest request) { + try { + final Contentlet contentlet = APILocator.getContentletAPI() + .findContentletByIdentifierAnyLanguage(identifier, false); + if (contentlet == null) { + return null; + } + + final IHTMLPage page = APILocator.getHTMLPageAssetAPI() + .fromContentlet(contentlet); + + final Host host = APILocator.getHostAPI() + .find(page.getHost(), APILocator.systemUser(), false); + + final String hostname = host != null ? host.getHostname() : request.getServerName(); + final String uri = page.getURI(); + final String baseUrl = buildBaseUrl(request); + + return new PageInfo( + identifier, + uri, + baseUrl + uri, + hostname, + page.getHost(), + languageId); + + } catch (Exception e) { + Logger.error(A11yAgentResource.class, + "Error resolving page for identifier " + identifier + ": " + e.getMessage(), e); + return null; + } + } + + // ------------------------------------------------------------------------- + // Private helpers — App config + token + // ------------------------------------------------------------------------- + + /** + * Reads {@code apiUrl} and {@code apiAuthToken} from the Page Scanner App secrets + * (same keys the scanner uses). The agent runs on the same host as the scanner, + * so {@code apiUrl} is the shared base — we append {@code /agents/a11y} to reach + * the agent routes. + * + * @return array {@code [agentBaseUrl, apiAuthToken]}, or empty if not configured + */ + private Optional<String[]> resolveAgentConfig(final HttpServletRequest request) { + final Host currentHost = Try.<Host>of( + () -> com.dotmarketing.business.web.WebAPILocator.getHostWebAPI() + .getCurrentHost(request)) + .getOrElse(APILocator.systemHost()); + + final Optional<AppSecrets> secretsOpt = Try.of( + () -> APILocator.getAppsAPI().getSecrets(APP_KEY, true, + currentHost, APILocator.systemUser())) + .getOrElse(Optional.empty()); + + if (secretsOpt.isEmpty()) { + Logger.warn(A11yAgentResource.class, + "Page Scanner App is not configured in the Apps portlet."); + return Optional.empty(); + } + + final Map<String, Secret> secrets = secretsOpt.get().getSecrets(); + final String apiUrl = sanitizeSecret( + Try.of(() -> secrets.get("apiUrl").getString()).getOrElse((String) null)); + final String apiAuthToken = sanitizeSecret( + Try.of(() -> secrets.get("apiAuthToken").getString()).getOrElse((String) null)); + + if (!UtilMethods.isSet(apiUrl) || !UtilMethods.isSet(apiAuthToken)) { + Logger.warn(A11yAgentResource.class, + "Page Scanner App is missing required configuration: apiUrl and apiAuthToken must be set."); + return Optional.empty(); + } + + final String base = apiUrl.endsWith("/") ? apiUrl.substring(0, apiUrl.length() - 1) : apiUrl; + return Optional.of(new String[]{ base + "/agents/a11y", apiAuthToken }); + } + + /** + * Mints the short-lived JWT the agent service uses to call back into dotCMS as this user. + * + * <p>On the {@code requestingIp} argument: it is AUDIT metadata recording who asked for the + * token, not an enforcement field, so passing the browser's address here is correct even + * though the agent calls back from a different egress. Enforcement is + * {@code ApiToken.allowNetwork}, checked by {@code JsonWebTokenFactory} via + * {@link ApiToken#isInIpRange(String)}; a null {@code allowNetwork} means unrestricted, and + * this token deliberately leaves it unset because the agent's egress address is not known + * to dotCMS. Setting it would need the operator to supply the agent's CIDR.</p> + * + * <p>The token carries the user's full rights for {@code DOT_PAGE_SCANNER_TOKEN_TTL_MS} + * (default 5 minutes) and is not revoked after use.</p> + * + * @param user the authenticated backend user the token acts as + * @param request the originating request, used only for the audit IP + * @return the signed JWT, or null when minting failed + */ + private String mintShortLivedToken(final User user, final HttpServletRequest request) { + try { + final long ttlMs = Config.getLongProperty("DOT_PAGE_SCANNER_TOKEN_TTL_MS", + 5L * 60L * 1000L); + final Date expiry = new Date(System.currentTimeMillis() + ttlMs); + final String ip = request.getRemoteAddr(); + + final ApiToken apiToken = APILocator.getApiTokenAPI() + .persistApiToken(user.getUserId(), expiry, user.getUserId(), ip, + "a11y-agent-short-lived"); + + return APILocator.getApiTokenAPI().getJWT(apiToken, user); + } catch (Exception e) { + Logger.error(A11yAgentResource.class, + "Error generating short-lived token: " + e.getMessage(), e); + return null; + } + } + + // ------------------------------------------------------------------------- + // Private helpers — payload construction + // ------------------------------------------------------------------------- + + private String buildAgentPayload( + final String dotcmsBaseUrl, + final PageInfo p, + final boolean skipCss) { + + // The minted token goes in Authorization: Bearer, not the body. + // The body carries only the resolved page fields (FixRequestSchema contract). + // hostId is required at the top level by the agent; it is also kept inside the + // page object since the agent still reads it there. + final Map<String, Object> page = new LinkedHashMap<>(); + page.put("identifier", p.identifier); + page.put("uri", p.uri); + page.put("liveUrl", p.liveUrl); + page.put("host", p.host); + page.put("hostId", p.hostId); + page.put("languageId", p.languageId); + + final Map<String, Object> payload = new LinkedHashMap<>(); + payload.put("dotcmsBaseUrl", dotcmsBaseUrl); + payload.put("hostId", p.hostId); + payload.put("page", page); + payload.put("options", Map.of("skipCss", skipCss)); + + return writeJson(payload); + } + + /** + * Serializes a payload with the shared Jackson mapper. + * + * <p>Jackson rather than string concatenation: the hand-rolled escaping this replaces + * covered only backslash, quote, newline, carriage return and tab, leaving the rest of the + * U+0000-U+001F control range (notably backspace and form feed) raw. A page title or URI + * carrying one of those produced JSON the agent could not parse, for a request that was + * otherwise perfectly valid. + * + * @param payload the object graph to serialize + * @return the JSON representation + */ + private static String writeJson(final Object payload) { + try { + return DotObjectMapperProvider.getInstance().getDefaultObjectMapper() + .writeValueAsString(payload); + } catch (JsonProcessingException e) { + // Only reachable if the maps above stop being plain data, which would be a bug + // here rather than bad input - fail loudly instead of forwarding a malformed body. + throw new IllegalStateException("Unable to serialize the a11y agent payload", e); + } + } + + private static String buildBaseUrl(final HttpServletRequest request) { + final String scheme = UtilMethods.isSet(request.getScheme()) + ? request.getScheme() : "http"; + final int port = request.getServerPort(); + final boolean defaultPort = ("http".equalsIgnoreCase(scheme) && port == 80) + || ("https".equalsIgnoreCase(scheme) && port == 443); + return scheme + "://" + request.getServerName() + (defaultPort ? "" : ":" + port); + } + + private String sanitizeSecret(final String value) { + if (value == null) { + return null; + } + return value.replaceAll("[^\\u0020-\\u007E\\u0080-\\u00FF]", "").trim(); + } + + + // ------------------------------------------------------------------------- + // Private record-like holders + // ------------------------------------------------------------------------- + + private static final class AgentContext { + final String agentUrl; + /** Static service secret — sent as {@code auth-token} header. */ + final String serviceAuthToken; + /** Short-lived JWT — sent as {@code Authorization: Bearer} for agent's API calls. */ + final String shortLivedToken; + final String agentPayload; + final Response errorResponse; + + AgentContext(final String agentUrl, final String serviceAuthToken, + final String shortLivedToken, final String agentPayload, + final Response errorResponse) { + this.agentUrl = agentUrl; + this.serviceAuthToken = serviceAuthToken; + this.shortLivedToken = shortLivedToken; + this.agentPayload = agentPayload; + this.errorResponse = errorResponse; + } + + static AgentContext error(final Response r) { + return new AgentContext(null, null, null, null, r); + } + } + + private static final class TokenContext { + final String agentUrl; + /** Static service secret — sent as {@code auth-token} header. */ + final String serviceAuthToken; + /** Short-lived JWT — sent as {@code Authorization: Bearer}. */ + final String shortLivedToken; + final Response errorResponse; + + TokenContext(final String agentUrl, final String serviceAuthToken, + final String shortLivedToken, final Response errorResponse) { + this.agentUrl = agentUrl; + this.serviceAuthToken = serviceAuthToken; + this.shortLivedToken = shortLivedToken; + this.errorResponse = errorResponse; + } + + static TokenContext error(final Response r) { + return new TokenContext(null, null, null, r); + } + } + + private static final class PageInfo { + final String identifier; + final String uri; + final String liveUrl; + final String host; + final String hostId; + final int languageId; + + PageInfo(final String identifier, final String uri, final String liveUrl, + final String host, final String hostId, final int languageId) { + this.identifier = identifier; + this.uri = uri; + this.liveUrl = liveUrl; + this.host = host; + this.hostId = hostId; + this.languageId = languageId; + } + } +} diff --git a/dotCMS/src/main/java/com/dotcms/rest/api/v1/a11yagent/AbstractA11yAgentFixForm.java b/dotCMS/src/main/java/com/dotcms/rest/api/v1/a11yagent/AbstractA11yAgentFixForm.java new file mode 100644 index 000000000000..239a4e68e891 --- /dev/null +++ b/dotCMS/src/main/java/com/dotcms/rest/api/v1/a11yagent/AbstractA11yAgentFixForm.java @@ -0,0 +1,72 @@ +package com.dotcms.rest.api.v1.a11yagent; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import io.swagger.v3.oas.annotations.media.Schema; +import org.immutables.value.Value; + +import javax.annotation.Nullable; + +/** + * Request body for {@code POST /api/v1/agents/a11y/fix} and {@code POST /api/v1/agents/a11y/fix/stream}. + * + * <p>The proxy resolves the identifier to a live URL, URI, and hostId before forwarding to the + * agent service — the agent receives a fully-resolved payload and never performs + * its own page resolution. + */ +@Value.Style(typeImmutable = "*", typeAbstract = "Abstract*", + additionalJsonAnnotations = JsonIgnoreProperties.class) +@Value.Immutable +@JsonSerialize(as = A11yAgentFixForm.class) +@JsonDeserialize(as = A11yAgentFixForm.class) +@JsonIgnoreProperties(ignoreUnknown = true) +@Schema(description = "Request body for the a11y-fix agent proxy") +public interface AbstractA11yAgentFixForm { + + /** + * Identifier of the page to fix. Declared nullable so a missing value surfaces as the + * resource's {@code MISSING_IDENTIFIER} 400 rather than a deserialization failure. + * + * @return the dotCMS content identifier of the page + */ + @Nullable + @Schema( + description = "dotCMS content identifier of the page to fix", + example = "a9f30020-54ef-494e-92ed-645e757171c2", + requiredMode = Schema.RequiredMode.REQUIRED + ) + String identifier(); + + /** + * Language the page is fixed in. + * + * @return the language id, defaults to 1 + */ + @Value.Default + @Schema( + description = "Language id of the page version to fix", + example = "1", + defaultValue = "1" + ) + default int languageId() { + return 1; + } + + /** + * When true the agent fixes only VTL and reports CSS contrast issues without changing + * stylesheets. + * + * @return true to skip CSS fixes, defaults to false + */ + @Value.Default + @Schema( + description = "When true the agent fixes only VTL and reports CSS contrast issues " + + "instead of editing stylesheets", + example = "false", + defaultValue = "false" + ) + default boolean skipCss() { + return false; + } +} diff --git a/dotCMS/src/main/java/com/dotcms/rest/api/v1/a11yagent/AbstractA11yAgentStopForm.java b/dotCMS/src/main/java/com/dotcms/rest/api/v1/a11yagent/AbstractA11yAgentStopForm.java new file mode 100644 index 000000000000..657cc5ae1d0e --- /dev/null +++ b/dotCMS/src/main/java/com/dotcms/rest/api/v1/a11yagent/AbstractA11yAgentStopForm.java @@ -0,0 +1,42 @@ +package com.dotcms.rest.api.v1.a11yagent; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import io.swagger.v3.oas.annotations.media.Schema; +import org.immutables.value.Value; + +import javax.annotation.Nullable; + +/** + * Request body for {@code POST /api/v1/agents/a11y/stop}. + * + * <p>Carries the {@code runId} the client received from {@code /fix} (the report) or + * {@code /fix/stream} (the {@code run} event). Stop is addressed by this id, NOT by the + * caller's identity: the proxy mints a fresh short-lived token per request, so the JWT + * {@code sub} differs between the /fix and /stop calls — the runId is the stable handle + * the agent uses to find the in-flight run. + */ +@Value.Style(typeImmutable = "*", typeAbstract = "Abstract*", + additionalJsonAnnotations = JsonIgnoreProperties.class) +@Value.Immutable +@JsonSerialize(as = A11yAgentStopForm.class) +@JsonDeserialize(as = A11yAgentStopForm.class) +@JsonIgnoreProperties(ignoreUnknown = true) +@Schema(description = "Request body for stopping an in-flight a11y-fix agent run") +public interface AbstractA11yAgentStopForm { + + /** + * Id of the run to stop. Declared nullable so a missing value surfaces as the resource's + * {@code MISSING_RUN_ID} 400 rather than a deserialization failure. + * + * @return the run id returned by /fix or /fix/stream + */ + @Nullable + @Schema( + description = "Run id returned by /fix (report) or /fix/stream (the `run` event)", + example = "r_1f0c2b7d9a4e4c1fb0d5e6a7c8b9d0e1", + requiredMode = Schema.RequiredMode.REQUIRED + ) + String runId(); +} diff --git a/dotCMS/src/main/java/com/dotcms/rest/api/v1/contenttype/ContentTypeFieldView.java b/dotCMS/src/main/java/com/dotcms/rest/api/v1/contenttype/ContentTypeFieldView.java new file mode 100644 index 000000000000..76d957eb6c61 --- /dev/null +++ b/dotCMS/src/main/java/com/dotcms/rest/api/v1/contenttype/ContentTypeFieldView.java @@ -0,0 +1,170 @@ +package com.dotcms.rest.api.v1.contenttype; + +import io.swagger.v3.oas.annotations.media.Schema; + +/** + * Swagger-only schema view describing a single entry in a content type's {@code fields[]} array. + * + * <p>This class is never instantiated or deserialized; it exists purely to publish a typed OpenAPI + * schema for the polymorphic field DTO accepted by {@code POST /contenttype} (and the field + * endpoints). The actual runtime model is the polymorphic {@code com.dotcms.contenttype.model.field.Field} + * hierarchy, which the {@code clazz} discriminator selects.</p> + * + * @see ContentTypeResource#createType + */ +@Schema(description = "A single field within a content type's 'fields[]' array. The 'clazz' property is the " + + "discriminator that selects the concrete field type; the remaining properties apply across field types.") +public class ContentTypeFieldView { + + @Schema(description = "Field identifier. Preserve this when updating an existing field.") + private String id; + + @Schema(description = "Identifier of the content type that owns this field.") + private String contentTypeId; + + @Schema( + requiredMode = Schema.RequiredMode.REQUIRED, + description = "Field type, as a case-insensitive short field-type name (the discriminator that " + + "selects the concrete field). The fully-qualified 'Immutable*' class name and the bare " + + "simple class name (e.g. 'TextField') are also still accepted, but the short names below " + + "are the preferred form. Example: \"TEXT\".", + example = "TEXT", + allowableValues = { + "TEXT", + "TEXT_AREA", + "STORY_BLOCK_FIELD", + "WYSIWYG", + "CONSTANT", + "HIDDEN", + "CUSTOM_FIELD", + "JSON_FIELD", + "BINARY", + "IMAGE", + "FILE", + "TAG", + "CATEGORY", + "CHECKBOX", + "RADIO", + "SELECT", + "MULTI_SELECT", + "DATE", + "TIME", + "DATE_TIME", + "KEY_VALUE", + "HOST_OR_FOLDER", + "RELATIONSHIP", + "RELATIONSHIPS_TAB", + "PERMISSIONS_TAB", + "LINE_DIVIDER", + "TAB_DIVIDER", + "ROW_FIELD", + "COLUMN_FIELD" + }) + private String clazz; + + @Schema(requiredMode = Schema.RequiredMode.REQUIRED, description = "Display name of the field.") + private String name; + + @Schema(description = "Velocity variable name of the field (unique within the content type; " + + "auto-generated from 'name' if omitted).") + private String variable; + + @Schema( + description = "Storage/column data type backing the field. This is the **storage** type, which often " + + "differs from the field's UI class. In particular, fields that store their payload elsewhere — " + + "such as 'ImmutableImageField', 'ImmutableFileField', and 'ImmutableBinaryField' — use " + + "dataType 'TEXT' (they keep an asset reference in a text column), **not** 'SYSTEM'. " + + "Reserve 'SYSTEM' for true layout/tab/relationship system fields. Use 'LONG_TEXT' for " + + "text-area/story-block/WYSIWYG content.", + allowableValues = {"TEXT", "LONG_TEXT", "SYSTEM", "BOOL", "INTEGER", "FLOAT", "DATE"}) + private String dataType; + + @Schema(description = "Whether a value is required to save content.") + private boolean required; + + @Schema(description = "Whether the field is added to the search index.") + private boolean indexed; + + @Schema(description = "Whether the field appears in content list/table views.") + private boolean listed; + + @Schema(description = "Whether the field is unique across content of this type.") + private boolean unique; + + @Schema(description = "Position of the field within the 'fields[]' array (also drives row/column layout order).") + private int sortOrder; + + @Schema(description = "Options for Radio/Select/Checkbox/Multi-Select fields: newline-separated 'Display|value' " + + "pairs. For a boolean choice use ImmutableRadioField + dataType 'BOOL' + " + + "values 'True|true\\r\\nFalse|false' (there is no dedicated boolean field class).") + private String values; + + @Schema(description = "Default value applied when content is created.") + private String defaultValue; + + @Schema(description = "Help text shown beneath the field in the editor.") + private String hint; + + @Schema(description = "Regular expression used to validate the field value.") + private String regexCheck; + + public String getClazz() { + return clazz; + } + + public String getId() { + return id; + } + + public String getContentTypeId() { + return contentTypeId; + } + + public String getName() { + return name; + } + + public String getVariable() { + return variable; + } + + public String getDataType() { + return dataType; + } + + public boolean isRequired() { + return required; + } + + public boolean isIndexed() { + return indexed; + } + + public boolean isListed() { + return listed; + } + + public boolean isUnique() { + return unique; + } + + public int getSortOrder() { + return sortOrder; + } + + public String getValues() { + return values; + } + + public String getDefaultValue() { + return defaultValue; + } + + public String getHint() { + return hint; + } + + public String getRegexCheck() { + return regexCheck; + } +} diff --git a/dotCMS/src/main/java/com/dotcms/rest/api/v1/contenttype/ContentTypeRequestView.java b/dotCMS/src/main/java/com/dotcms/rest/api/v1/contenttype/ContentTypeRequestView.java new file mode 100644 index 000000000000..84a69fd6bea9 --- /dev/null +++ b/dotCMS/src/main/java/com/dotcms/rest/api/v1/contenttype/ContentTypeRequestView.java @@ -0,0 +1,152 @@ +package com.dotcms.rest.api.v1.contenttype; + +import io.swagger.v3.oas.annotations.media.ArraySchema; +import io.swagger.v3.oas.annotations.media.Schema; + +import java.util.List; +import java.util.Map; + +/** + * Swagger-only schema view describing the request body of {@code POST /api/v1/contenttype}. + * + * <p>This class is never instantiated or deserialized; it exists purely to publish a typed OpenAPI + * schema for the content-type object the endpoint accepts. The runtime request type is + * {@code ContentTypeForm}, but that class has a custom {@code @JsonDeserialize} that reads the + * <b>bare</b> {@code com.dotcms.contenttype.model.type.ContentType} object (or an array of them) at + * the top level — it is NOT a {@code {"contentType": ...}} envelope. Introspecting the + * {@code ContentTypeForm} bean would publish its internal {@code entries}/{@code requestJson} shape, + * which bears no relation to the wire format. This view publishes the real shape instead.</p> + * + * @see ContentTypeResource#createType + * @see ContentTypeFieldView + */ +@Schema(description = "A content-type object, posted directly (NOT wrapped in a 'contentType' envelope). " + + "The endpoint also accepts an array of these objects to create several types in one call. " + + "Creating clazz `WIDGET` automatically adds `widgetTitle`, `widgetUsage`, `widgetCode`, and " + + "`widgetPreexecute`. `widgetCode` is an ImmutableConstantField: its shared code belongs in the " + + "field's `values` property, not in individual widget contentlets.") +public class ContentTypeRequestView { + + @Schema( + requiredMode = Schema.RequiredMode.REQUIRED, + description = "Base type of the content type, as a case-insensitive base-type name: " + + "`CONTENT`, `WIDGET`, `FORM`, `FILEASSET`, `HTMLPAGE`, `PERSONA`, `VANITY_URL`, " + + "`KEY_VALUE`, or `DOTASSET`. Example: `\"WIDGET\"`.", + example = "WIDGET", + allowableValues = { + "CONTENT", + "WIDGET", + "FORM", + "FILEASSET", + "HTMLPAGE", + "PERSONA", + "VANITY_URL", + "KEY_VALUE", + "DOTASSET" + }) + private String clazz; + + @Schema(requiredMode = Schema.RequiredMode.REQUIRED, description = "Display name of the content type.") + private String name; + + @Schema(description = "Velocity variable name (unique, alphanumeric, starts with a letter; " + + "auto-generated from 'name' if omitted).") + private String variable; + + @Schema(description = "Site identifier UUID this content type lives on, or the literal 'SYSTEM_HOST' " + + "(defaults to the default site).") + private String host; + + @Schema(description = "Folder identifier UUID, or the literal 'SYSTEM_FOLDER' (the default).") + private String folder; + + @Schema(description = "Description of the content type.") + private String description; + + @Schema(description = "Whether this is the default content type.") + private boolean defaultType; + + @Schema(description = "Whether the content type is fixed (system-managed).") + private boolean fixed; + + @Schema(description = "Whether the content type is a system type.") + private boolean system; + + @ArraySchema( + arraySchema = @Schema( + description = "Workflow scheme identifiers to associate with the content type, e.g. " + + "[\"d61a59e1-a49c-46f2-a929-db2b4bfa88b2\"] for the System Workflow. " + + "NOTE: this key is 'workflow' (singular) in the REQUEST; GET responses return " + + "'workflows' (plural, array of objects) — rename the key when round-tripping."), + schema = @Schema(type = "string")) + private List<String> workflow; + + @ArraySchema( + arraySchema = @Schema(description = "Fields that make up the content type, in order. Rows and columns are " + + "regular entries: 'ImmutableRowField' begins a row, 'ImmutableColumnField' begins a column, and " + + "subsequent content fields belong to the most-recent column."), + schema = @Schema(implementation = ContentTypeFieldView.class)) + private List<ContentTypeFieldView> fields; + + @Schema(description = "Content-type metadata. Known keys: 'CONTENT_EDITOR2_ENABLED' (boolean), " + + "'DOT_STYLE_EDITOR_SCHEMA' (JSON string).", + type = "object") + private Map<String, Object> metadata; + + @Schema(description = "Maps system actions (NEW, EDIT, PUBLISH, UNPUBLISH, ARCHIVE, UNARCHIVE, DELETE, DESTROY) " + + "to workflow action identifiers.", + type = "object") + private Map<String, String> systemActionMappings; + + public String getClazz() { + return clazz; + } + + public String getName() { + return name; + } + + public String getVariable() { + return variable; + } + + public String getHost() { + return host; + } + + public String getFolder() { + return folder; + } + + public String getDescription() { + return description; + } + + public boolean isDefaultType() { + return defaultType; + } + + public boolean isFixed() { + return fixed; + } + + public boolean isSystem() { + return system; + } + + public List<String> getWorkflow() { + return workflow; + } + + public List<ContentTypeFieldView> getFields() { + return fields; + } + + public Map<String, Object> getMetadata() { + return metadata; + } + + public Map<String, String> getSystemActionMappings() { + return systemActionMappings; + } +} diff --git a/dotCMS/src/main/java/com/dotcms/rest/api/v1/contenttype/ContentTypeResource.java b/dotCMS/src/main/java/com/dotcms/rest/api/v1/contenttype/ContentTypeResource.java index 6f9ee97e76cd..19811fee6f40 100644 --- a/dotCMS/src/main/java/com/dotcms/rest/api/v1/contenttype/ContentTypeResource.java +++ b/dotCMS/src/main/java/com/dotcms/rest/api/v1/contenttype/ContentTypeResource.java @@ -490,16 +490,8 @@ public final Response createType(@Context final HttpServletRequest req, description = "Accepts either a single content-type object or an array. " + "The body is the content-type object directly (not wrapped in a 'contentType' envelope).\n\n" + "**Required properties:**\n" + - "- `clazz` *(string)* — fully-qualified class name. One of: " + - "`com.dotcms.contenttype.model.type.ImmutableSimpleContentType`, " + - "`com.dotcms.contenttype.model.type.ImmutableWidgetContentType`, " + - "`com.dotcms.contenttype.model.type.ImmutableFormContentType`, " + - "`com.dotcms.contenttype.model.type.ImmutableFileAssetContentType`, " + - "`com.dotcms.contenttype.model.type.ImmutablePageContentType`, " + - "`com.dotcms.contenttype.model.type.ImmutablePersonaContentType`, " + - "`com.dotcms.contenttype.model.type.ImmutableVanityUrlContentType`, " + - "`com.dotcms.contenttype.model.type.ImmutableKeyValueContentType`, " + - "`com.dotcms.contenttype.model.type.ImmutableDotAssetContentType`\n" + + "- `clazz` *(string)* — the base type, as a case-insensitive base-type name: " + + "`CONTENT`, `WIDGET`, `FORM`, `FILEASSET`, `HTMLPAGE`, `PERSONA`, `VANITY_URL`, `KEY_VALUE`, or `DOTASSET`.\n" + "- `name` *(string)* — display name\n\n" + "**Common optional properties:**\n" + "- `variable` *(string)* — Velocity variable name (unique, alphanumeric, starts with a letter; auto-generated if omitted)\n" + @@ -509,15 +501,24 @@ public final Response createType(@Context final HttpServletRequest req, "- `workflow` *(array of workflow scheme UUIDs)* — e.g. `[\"d61a59e1-a49c-46f2-a929-db2b4bfa88b2\"]` for System Workflow. " + "⚠️ **Note:** this is `workflow` (singular) in the request. GET responses return `workflows` (plural, array of objects) — " + "clients round-tripping an object must rename this key.\n" + - "- `fields` *(array of field objects)* — see field schema below\n" + + "- `fields` *(array of field objects)* — see field schema below (`ContentTypeFieldView`)\n" + "- `metadata` *(object)* — known keys: `CONTENT_EDITOR2_ENABLED` (boolean), `DOT_STYLE_EDITOR_SCHEMA` (JSON string)\n" + "- `systemActionMappings` *(object)* — maps system actions (`NEW`, `EDIT`, `PUBLISH`, `UNPUBLISH`, `ARCHIVE`, `UNARCHIVE`, `DELETE`, `DESTROY`) to workflow action UUIDs\n\n" + + "**WIDGET content types:** Creating `clazz: WIDGET` automatically adds `widgetTitle`, `widgetUsage`, `widgetCode`, and `widgetPreexecute`. " + + "`widgetCode` is an `ImmutableConstantField`; set the field's `values` property on the content type. " + + "Putting `widgetCode` in a workflow contentlet body is silently ignored.\n\n" + "**Field object schema** (each item in `fields[]`):\n" + - "- `clazz` *(string, required)* — e.g. `com.dotcms.contenttype.model.field.ImmutableTextField`, `ImmutableTextAreaField`, " + - "`ImmutableStoryBlockField`, `ImmutableBinaryField`, `ImmutableTagField`, `ImmutableRadioField`, `ImmutableSelectField`, " + - "`ImmutableDateField`, `ImmutableDateTimeField`, `ImmutableRowField` *(layout marker)*, `ImmutableColumnField` *(layout marker)*\n" + + "- `clazz` *(string, required)* — the field type as a case-insensitive short name: " + + "`TEXT`, `TEXT_AREA`, `STORY_BLOCK_FIELD`, `WYSIWYG`, `BINARY`, `IMAGE`, `FILE`, `TAG`, `CATEGORY`, " + + "`CHECKBOX`, `RADIO`, `SELECT`, `MULTI_SELECT`, `DATE`, `TIME`, `DATE_TIME`, `KEY_VALUE`, `JSON_FIELD`, " + + "`CONSTANT`, `HIDDEN`, `CUSTOM_FIELD`, `RELATIONSHIP`, `ROW_FIELD` *(layout marker)*, `COLUMN_FIELD` *(layout marker)*. " + + "The fully-qualified `Immutable*` class name is also accepted.\n" + "- `name`, `variable`, `dataType` (one of `TEXT`, `LONG_TEXT`, `SYSTEM`, `BOOL`, `INTEGER`, `FLOAT`, `DATE`), " + "`required`, `indexed`, `listed`, `sortOrder` *(integer, position in the fields array)*\n" + + "- ⚠️ **`dataType` is the storage type, not the UI type.** Asset-reference fields — " + + "`ImmutableImageField`, `ImmutableFileField`, `ImmutableBinaryField` — use `dataType: TEXT` " + + "(they store a reference in a text column), **never** `SYSTEM`. Reserve `SYSTEM` for true " + + "layout/tab/relationship system fields (rows, columns, dividers, permission/relationship tabs).\n" + "- `values` *(string)* — for Radio/Select/Checkbox: newline-separated `Display|value` pairs. " + "For a boolean field use `ImmutableRadioField` + `dataType: BOOL` + `values: 'True|true\\r\\nFalse|false'` — there is no dedicated Boolean field class.\n\n" + "**Layout encoding:** Rows and columns are regular field entries placed in `fields[]`. " + @@ -525,12 +526,12 @@ public final Response createType(@Context final HttpServletRequest req, "following content fields belong to the most-recent column until the next marker.", required = true, content = @Content( - schema = @Schema(implementation = ContentTypeForm.class), + schema = @Schema(implementation = ContentTypeRequestView.class), examples = { @ExampleObject( value = "[\n" + " {\n" + - " \"clazz\": \"com.dotcms.contenttype.model.type.ImmutableSimpleContentType\",\n" + + " \"clazz\": \"CONTENT\",\n" + " \"defaultType\": false,\n" + " \"name\": \"The Content Type 1\",\n" + " \"description\": \"THE DESCRIPTION\",\n" + @@ -549,7 +550,7 @@ public final Response createType(@Context final HttpServletRequest req, " ]\n" + " },\n" + " {\n" + - " \"clazz\": \"com.dotcms.contenttype.model.type.ImmutableSimpleContentType\",\n" + + " \"clazz\": \"CONTENT\",\n" + " \"defaultType\": false,\n" + " \"name\": \"The Content Type 2\",\n" + " \"description\": \"THE DESCRIPTION\",\n" + @@ -741,7 +742,7 @@ public Response updateType(@PathParam("idOrVar") @Parameter( "schemes to be associated with the content type.", required = true, content = @Content( - schema = @Schema(implementation = ContentTypeForm.class), + schema = @Schema(implementation = ContentTypeRequestView.class), examples = { @ExampleObject( value = "{\n" + diff --git a/dotCMS/src/main/java/com/dotcms/rest/api/v1/contenttype/FieldResource.java b/dotCMS/src/main/java/com/dotcms/rest/api/v1/contenttype/FieldResource.java index 0b153b32cc2c..1d8feda5aad9 100644 --- a/dotCMS/src/main/java/com/dotcms/rest/api/v1/contenttype/FieldResource.java +++ b/dotCMS/src/main/java/com/dotcms/rest/api/v1/contenttype/FieldResource.java @@ -44,6 +44,7 @@ import org.glassfish.jersey.server.JSONP; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.media.ArraySchema; import io.swagger.v3.oas.annotations.media.Content; import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.parameters.RequestBody; @@ -105,9 +106,10 @@ public FieldResource(final WebResource webresource, final FieldAPI fieldAPI) { @Produces({ MediaType.APPLICATION_JSON }) public Response updateFields(@Parameter(description = "Content type ID", required = true) @PathParam("typeId") final String typeId, - @RequestBody(description = "Fields JSON data", + @RequestBody(description = "Array of field objects to save on the content type.", required = true, - content = @Content(schema = @Schema(implementation = String.class))) + content = @Content( + array = @ArraySchema(schema = @Schema(implementation = ContentTypeFieldView.class)))) final String fieldsJson, @Context final HttpServletRequest req) throws DotDataException, DotSecurityException { @@ -173,9 +175,14 @@ public Response updateFields(@Parameter(description = "Content type ID", require @Produces({ MediaType.APPLICATION_JSON }) public Response createContentTypeField(@Parameter(description = "Content type ID", required = true) @PathParam("typeId") final String typeId, - @RequestBody(description = "Field JSON data", + @RequestBody(description = "A SINGLE field object to create on the content type. " + + "This endpoint creates exactly one field: if you pass a JSON array, only the " + + "first element is saved (it returns 200 and silently drops the rest). To add " + + "multiple fields at once, use PUT /api/v1/contenttype/{typeId}/fields (which takes " + + "an array), or include them inline as the 'fields' array when creating the content " + + "type via POST /api/v1/contenttype.", required = true, - content = @Content(schema = @Schema(implementation = String.class))) + content = @Content(schema = @Schema(implementation = ContentTypeFieldView.class))) final String fieldJson, @Context final HttpServletRequest req) throws DotDataException, DotSecurityException { diff --git a/dotCMS/src/main/java/com/dotcms/rest/api/v1/folder/FolderResource.java b/dotCMS/src/main/java/com/dotcms/rest/api/v1/folder/FolderResource.java index 82cdab2b0b16..9f49b1384209 100644 --- a/dotCMS/src/main/java/com/dotcms/rest/api/v1/folder/FolderResource.java +++ b/dotCMS/src/main/java/com/dotcms/rest/api/v1/folder/FolderResource.java @@ -269,7 +269,15 @@ public final Response selectFolder(@Context final HttpServletRequest httpServlet @Produces({MediaType.APPLICATION_JSON, "application/javascript"}) public final Response loadFolderByURI(@Context final HttpServletRequest httpServletRequest, @Context final HttpServletResponse httpServletResponse, + @Parameter(description = "Site hostname the folder lives on (e.g. 'demo.dotcms.com').", + required = true) @PathParam("siteName") final String siteName, + @Parameter(description = "Folder path within the site, as a plain path — " + + "e.g. 'application/themes/travel' (a leading slash is optional and added " + + "if missing). Embedded slashes are allowed (they select nested folders). " + + "Pass the raw path; do NOT percent-encode the slashes (a pre-encoded " + + "'%2F...' will not match).", + required = true) @PathParam("uri") final String uri){ Response response = null; final InitDataObject initData = this.webResource.init(null, httpServletRequest, httpServletResponse, true, null); diff --git a/dotCMS/src/main/java/com/dotcms/rest/api/v1/page/PageForm.java b/dotCMS/src/main/java/com/dotcms/rest/api/v1/page/PageForm.java index 4d2d605f0687..66f262fe3c87 100644 --- a/dotCMS/src/main/java/com/dotcms/rest/api/v1/page/PageForm.java +++ b/dotCMS/src/main/java/com/dotcms/rest/api/v1/page/PageForm.java @@ -12,6 +12,7 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.annotation.JsonDeserialize; import com.google.common.collect.ImmutableMap; +import io.swagger.v3.oas.annotations.media.Schema; import java.io.IOException; import java.util.HashMap; @@ -29,11 +30,26 @@ * @since Nov 22nd, 2017 */ @JsonDeserialize(builder = PageForm.Builder.class) +@Schema(description = "Layout payload used to create or update the anonymous Template backing a page. " + + "Wrap this object under a top-level 'PageForm' property in the request body. " + + "'layout' is required; omitting 'title' creates an anonymous (page-scoped) template.") class PageForm { + @Schema(description = "Theme folder identifier (not a path) supplying the template's CSS/JS and VTL fragments. " + + "Resolve from a path via GET /api/v1/folder/sitename/{site}/uri/{uri}.") private final String themeId; + + @Schema(description = "Title for the resulting template. Omit to create an anonymous, page-scoped template " + + "(a generated name is assigned automatically).") private final String title; + + @Schema(description = "Identifier of the site (host) the template belongs to. Sent as the JSON property " + + "'hostId'. Defaults to the site resolved from the current HTTP request context when omitted.") private final String siteId; + + @Schema(description = "Required. The row/column/container structure of the layout: a 'body' of rows, each row " + + "holding columns, each column holding containers referenced by identifier + uuid, plus optional " + + "'header', 'footer', and 'sidebar'.", requiredMode = Schema.RequiredMode.REQUIRED) private final TemplateLayout layout; diff --git a/dotCMS/src/main/java/com/dotcms/rest/api/v1/page/PageResource.java b/dotCMS/src/main/java/com/dotcms/rest/api/v1/page/PageResource.java index c1c07a7b24aa..565da2816537 100644 --- a/dotCMS/src/main/java/com/dotcms/rest/api/v1/page/PageResource.java +++ b/dotCMS/src/main/java/com/dotcms/rest/api/v1/page/PageResource.java @@ -359,7 +359,10 @@ public Response getRenderSources( description = "Returns the metadata (the objects that make up an HTML Page) in JSON format based on the " + "specified URI. If the URI maps to a Vanity URL, a 200 Forward returns the actual page metadata, " + "while a 301/302 redirect returns an empty page JSON with the Vanity URL properties. " - + "Supports Time Machine via the publishDate parameter (ISO 8601 format)." + + "Supports Time Machine via the publishDate parameter (ISO 8601 format).\n\n" + + "The URI must be a plain page path (no embedded host). To read a page on a NON-default site, " + + "pass the `host_id` query parameter (backend users only); without it the current/default site " + + "is used. The `//host/uri` path form is NOT supported." ) @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "Page metadata retrieved successfully", @@ -376,8 +379,12 @@ public Response getRenderSources( @Path("/json/{uri: .*}") public Response loadJson(@Context final HttpServletRequest originalRequest, @Context final HttpServletResponse response, - @Parameter(description = "Path to the HTML Page or Vanity URL (e.g., 'about-us/locations/index')", required = true) + @Parameter(description = "Path to the HTML Page or Vanity URL (e.g., 'about-us/locations/index'). " + + "Must be a plain path with no embedded host; use host_id to target a specific site.", required = true) @PathParam("uri") final String uri, + @Parameter(description = "Explicit site to read against, given as a host identifier (UUID). " + + "Backend users only; if omitted the current/default site is used.") + @QueryParam("host_id") final String hostId, @Parameter(description = "Page mode for rendering (e.g., EDIT_MODE, PREVIEW_MODE, LIVE)") @QueryParam(WebKeys.PAGE_MODE_PARAMETER) final String modeParam, @Parameter(description = "Persona identifier to render the page with personalization") @@ -462,7 +469,10 @@ public Response loadJson(@Context final HttpServletRequest originalRequest, + "the actual rendered page, while a 301/302 redirect returns an empty page JSON with Vanity URL properties. " + "Supports Time Machine via the publishDate parameter (ISO 8601 format). " + "For EMA (Enterprise Marketing Automation) requests, this may delegate to the JSON endpoint when " - + "the rendered attribute is not required." + + "the rendered attribute is not required.\n\n" + + "The URI must be a plain page path (no embedded host). To render a page on a NON-default site, " + + "pass the `host_id` query parameter (backend users only); without it the current/default site " + + "is used. The `//host/uri` path form is NOT supported." ) @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "Rendered page retrieved successfully", @@ -479,8 +489,12 @@ public Response loadJson(@Context final HttpServletRequest originalRequest, @Path("/render/{uri: .*}") public Response render(@Context final HttpServletRequest originalRequest, @Context final HttpServletResponse response, - @Parameter(description = "Path to the HTML Page or Vanity URL (e.g., 'about-us/locations/index')", required = true) + @Parameter(description = "Path to the HTML Page or Vanity URL (e.g., 'about-us/locations/index'). " + + "Must be a plain path with no embedded host; use host_id to target a specific site.", required = true) @PathParam("uri") final String uri, + @Parameter(description = "Explicit site to render against, given as a host identifier (UUID). " + + "Backend users only; if omitted the current/default site is used.") + @QueryParam("host_id") final String hostId, @Parameter(description = "Page mode for rendering (e.g., EDIT_MODE, PREVIEW_MODE, LIVE)") @QueryParam(WebKeys.PAGE_MODE_PARAMETER) final String modeParam, @Parameter(description = "Persona identifier to render the page with personalization") @@ -498,7 +512,7 @@ public Response render(@Context final HttpServletRequest originalRequest, if (UtilMethods.isSet(depth)) { HttpServletRequestThreadLocal.INSTANCE.getRequest().setAttribute(WebKeys.HTMLPAGE_DEPTH, depth); } - return this.loadJson(originalRequest, response, uri, modeParam, personaId, languageId + return this.loadJson(originalRequest, response, uri, hostId, modeParam, personaId, languageId , deviceInode, timeMachineDateAsISO8601); } Logger.debug(this, () -> String.format( @@ -1167,7 +1181,11 @@ private List<ContainerEntry> reduce(final List<ContainerEntry> containerEntries) operationId = "renderPageHtmlOnly", summary = "Render page as raw HTML", description = "Returns the rendered HTML content of a page without the JSON metadata wrapper. " - + "Useful for retrieving the raw HTML output of a page for embedding or server-side rendering." + + "Useful for retrieving the raw HTML output of a page for embedding or server-side rendering.\n\n" + + "The page is identified by a plain URI path (e.g. `index`, `about/team`). To render a page " + + "on a NON-default site, pass the `host_id` query parameter (backend users only); without it the " + + "current/default site is used. The `//host/uri` path form is NOT supported — the URI must not " + + "embed a host." ) @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "Page HTML rendered successfully", @@ -1182,8 +1200,12 @@ private List<ContainerEntry> reduce(final List<ContainerEntry> containerEntries) @Path("/renderHTML/{uri: .*}") public Response renderHTMLOnly(@Context final HttpServletRequest request, @Context final HttpServletResponse response, - @Parameter(description = "Path to the HTML Page to render", required = true) + @Parameter(description = "Plain page URI path (e.g. 'index' or 'about/team'). " + + "Must not embed a host; use host_id to target a specific site.", required = true) @PathParam("uri") final String uri, + @Parameter(description = "Explicit site to render against, given as a host identifier (UUID). " + + "Backend users only; if omitted the current/default site is used.") + @QueryParam("host_id") final String hostId, @Parameter(description = "Page mode for rendering (default: LIVE_ADMIN)") @QueryParam("mode") @DefaultValue("LIVE_ADMIN") final String modeStr) throws DotDataException, DotSecurityException { diff --git a/dotCMS/src/main/java/com/dotcms/rest/api/v1/pagescanner/PageScannerResource.java b/dotCMS/src/main/java/com/dotcms/rest/api/v1/pagescanner/PageScannerResource.java index 77aff9459d16..71ec59da20bb 100644 --- a/dotCMS/src/main/java/com/dotcms/rest/api/v1/pagescanner/PageScannerResource.java +++ b/dotCMS/src/main/java/com/dotcms/rest/api/v1/pagescanner/PageScannerResource.java @@ -57,6 +57,9 @@ public class PageScannerResource { static final String DEFAULT_API_URL = "https://a11y.api.dotcms.site"; + /** Version prefix the upstream Page Scanner service exposes its check endpoints under. */ + static final String UPSTREAM_API_VERSION = "v1"; + private static final String NOT_CONFIGURED_MSG = "Page Scanner service is not available."; @@ -77,7 +80,7 @@ public PageScannerResource() { } /** - * Proxies a POST request to the upstream {@code /a11y/check} endpoint. + * Proxies a POST request to the upstream {@code /v1/a11y/check} endpoint. * * @param request the HTTP servlet request * @param response the HTTP servlet response @@ -98,7 +101,7 @@ public Response a11yCheck( } /** - * Proxies a POST request to the upstream {@code /geo/check} endpoint. + * Proxies a POST request to the upstream {@code /v1/geo/check} endpoint. * * @param request the HTTP servlet request * @param response the HTTP servlet response @@ -219,7 +222,7 @@ private String buildPayload(final String url, final String shortLivedToken) { private String buildUpstreamUrl(final String apiUrl, final CheckType checkType) { final String base = apiUrl.endsWith("/") ? apiUrl.substring(0, apiUrl.length() - 1) : apiUrl; - return base + "/" + checkType.pathSegment() + "/check"; + return base + "/" + UPSTREAM_API_VERSION + "/" + checkType.pathSegment() + "/check"; } private Response forwardRequest( diff --git a/dotCMS/src/main/java/com/dotcms/rest/api/v1/site/SiteForm.java b/dotCMS/src/main/java/com/dotcms/rest/api/v1/site/SiteForm.java index 20787a7f5eb2..f3527c9d06a4 100644 --- a/dotCMS/src/main/java/com/dotcms/rest/api/v1/site/SiteForm.java +++ b/dotCMS/src/main/java/com/dotcms/rest/api/v1/site/SiteForm.java @@ -2,48 +2,73 @@ import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; +import io.swagger.v3.oas.annotations.media.Schema; import java.util.List; /** * Form to create a site * @author jsanca */ +@Schema(description = "Form used to create a Site (Host) in dotCMS. 'siteName' (the hostname) is the only " + + "required field; the new site is created unpublished and must be published separately.") public class SiteForm { + @Schema(description = "Identifier of the site. Ignored on creation; server-generated.") private final String identifier; + @Schema(description = "Inode (version identifier) of the site. Ignored on creation; server-generated.") private final String inode; + @Schema(description = "Comma- or newline-separated list of host aliases (alternate hostnames) for this site.") private final String aliases; + @Schema(description = "The hostname of the site, e.g. 'www.example.com'. This is the site's primary name.", + requiredMode = Schema.RequiredMode.REQUIRED) private final String siteName; + @Schema(description = "Identifier of the site whose tag storage this site shares. " + + "Defaults to this site itself when omitted.") private final String tagStorage; + @Schema(description = "Identifier of the image asset used as the site thumbnail.") private final String siteThumbnail; + @Schema(description = "Whether the analytics dashboard runs for this site.") private final boolean runDashboard; + @Schema(description = "Default meta keywords applied to pages on this site.") private final String keywords; + @Schema(description = "Default meta description applied to pages on this site.") private final String description; + @Schema(description = "Google Maps API key for this site.") private final String googleMap; + @Schema(description = "Google Analytics tracking ID for this site.") private final String googleAnalytics; + @Schema(description = "AddThis sharing-widget account ID for this site.") private final String addThis; + @Schema(description = "Proxy URL used to render the site in edit mode.") private final String proxyUrlForEditMode; + @Schema(description = "Embedded dashboard markup for this site.") private final String embeddedDashboard; + @Schema(description = "Default language ID for this site. Defaults to the system default language when 0/omitted.") private final long languageId; + @Schema(description = "Whether this site should become the default site. The JSON property name is 'default'. " + + "Only one site can be the default at a time.") private final boolean isDefault; + @Schema(description = "Whether to force creation even when validation would otherwise warn " + + "(e.g. a duplicate alias).") private final boolean forceExecution; + @Schema(description = "Optional list of site variables (key/value pairs) to create alongside the site.") private final List<SimpleSiteVariableForm> variables; @JsonCreator diff --git a/dotCMS/src/main/java/com/dotcms/rest/api/v1/template/TemplateForm.java b/dotCMS/src/main/java/com/dotcms/rest/api/v1/template/TemplateForm.java index 7719694d3cc3..9c652f1146cb 100644 --- a/dotCMS/src/main/java/com/dotcms/rest/api/v1/template/TemplateForm.java +++ b/dotCMS/src/main/java/com/dotcms/rest/api/v1/template/TemplateForm.java @@ -4,37 +4,94 @@ import com.dotcms.rest.api.Validated; import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import io.swagger.v3.oas.annotations.media.Schema; /** * Template Input Form * @author jsanca */ @JsonDeserialize(builder = TemplateForm.Builder.class) +@Schema(description = "Form used to create or update a Template in dotCMS. A Template can be either " + + "drawn with the layout builder (set 'drawed:true' and provide 'layout') or authored as raw " + + "Velocity markup (provide 'body').") public class TemplateForm extends Validated { + @Schema(description = "Identifier of the site (host) where this template will be created. " + + "This is a site identifier UUID, not 'hostId'. If not provided, the template is " + + "assigned to the site resolved from the current HTTP request context.") private final String siteId; + + @Schema(description = "Identifier of the template. Required for updates, ignored on creation.") private final String identifier; + + @Schema(description = "Inode (version identifier) of the template. Server-managed; typically omitted on input.") private final String inode; + + @Schema(description = "Raw Velocity markup that renders the template. Required even when 'drawed:true' — " + + "send an empty string ('') when using the layout builder ('layout') instead of hand-written body.") private final String body; + + @Schema(description = "Identifier of the image asset currently selected as the template thumbnail in the UI.") private final String selectedimage; + + @Schema(description = "Identifier of the image asset used as the template thumbnail.") private final String image; + + @Schema(description = "Whether this template was built with the visual layout builder. " + + "When true, provide 'layout' (the row/column/container structure); when false, provide 'body'.") private final boolean drawed; + + @Schema(description = "Whether this template should appear in navigation menus.") private final boolean showOnMenu; + + @Schema(description = "Velocity body generated by the layout builder for a drawn template. Server-managed.") private final String drawedBody; + + @Schema(description = "Count of 'add container' placeholders in the layout. Server-managed; typically omitted.") private final int countAddContainer; + + @Schema(description = "Count of containers in the layout. Server-managed; typically omitted.") private final int countContainers; + + @Schema(description = "Velocity/HTML injected into the page <head> for pages using this template.") private final String headCode; + + @Schema(description = "Theme folder identifier (not a path) that supplies the template's CSS/JS and " + + "VTL fragments. Resolve the folder identifier from a path via " + + "GET /api/v1/folder/sitename/{site}/uri/{uri}.") private final String theme; + + @Schema(description = "Display name of the theme. Informational; 'theme' (the folder identifier) is authoritative.") private final String themeName; + + @Schema(description = "Velocity/HTML footer fragment for pages using this template.") private final String footer; + + @Schema(description = "Friendly (human-readable) name of the template.") private final String friendlyName; + + @Schema(description = "Velocity/HTML header fragment for pages using this template.") private final String header; + + @Schema(description = "Internal/asset name of the template.") private final String name; + @NotNull + @Schema(description = "Title of the template (displayed in the UI).", requiredMode = Schema.RequiredMode.REQUIRED) private final String title; + + @Schema(description = "Sort order for display purposes.") private final int sortOrder; + + @Schema(description = "Whether the header fragment is enabled for this template.") private final boolean headerCheck; + + @Schema(description = "Whether the footer fragment is enabled for this template.") private final boolean footerCheck; + + @Schema(description = "Layout produced by the visual builder: a 'body' of rows, each row holding columns, " + + "and each column holding containers (referenced by identifier + uuid), plus optional 'header', " + + "'footer', 'sidebar', and 'title'. Provide this when 'drawed:true'.") private final TemplateLayoutView layout; private TemplateForm(final Builder builder) { diff --git a/dotCMS/src/main/java/com/dotcms/rest/api/v1/template/TemplateResource.java b/dotCMS/src/main/java/com/dotcms/rest/api/v1/template/TemplateResource.java index b2c00b05b457..d36d4a25ff63 100644 --- a/dotCMS/src/main/java/com/dotcms/rest/api/v1/template/TemplateResource.java +++ b/dotCMS/src/main/java/com/dotcms/rest/api/v1/template/TemplateResource.java @@ -6,6 +6,7 @@ import com.dotcms.rest.ResponseEntityView; import com.dotcms.rest.WebResource; import com.dotcms.rest.annotation.NoCache; +import com.dotcms.rest.exception.BadRequestException; import com.dotcms.rest.api.BulkResultView; import com.dotcms.rest.api.FailedResultView; import com.dotcms.util.PaginationUtil; @@ -22,6 +23,7 @@ import com.dotmarketing.exception.DotSecurityException; import com.dotmarketing.portlets.containers.business.ContainerAPI; import com.dotmarketing.portlets.contentlet.model.Contentlet; +import com.dotmarketing.portlets.folders.model.Folder; import com.dotmarketing.portlets.templates.business.TemplateAPI; import com.dotmarketing.portlets.templates.business.TemplateSaveParameters; import com.dotmarketing.portlets.templates.design.bean.TemplateLayout; @@ -318,7 +320,15 @@ public final Response getWorkingById(@Context final HttpServletRequest httpRequ summary = "Create a new template", description = "Creates a new working version of a template. The 'theme' field in the form " + "corresponds to the theme folder identifier (referred to as 'themeId' in other endpoints). " + - "If a layout is provided, the template is saved as a designed (drawed) template with its layout." + "If a layout is provided, the template is saved as a designed (drawed) template with its layout.\n\n" + + "When `drawed` is true (a layout-designer template): `body` is REQUIRED and must be non-empty " + + "(a null body returns 400 'body required when drawed'), and `theme` MUST resolve to a theme " + + "**folder** identifier — a host id or other non-folder id returns 400 'theme must be a folder " + + "identifier'. Provide `drawedBody` (the layout JSON) as well so the template is a real drawn template.\n\n" + + "For a themed drawn template, `body` is a generated compatibility shell and is not the render " + + "source; rendering flows through the theme's `template.vtl` and `drawedBody`. The stored shell " + + "may contain `/themes/null/` even when `theme` and `themeName` are correct. That value is benign " + + "for this template kind, and PUT-updating `body` will only cause the shell to be regenerated." ) @ApiResponses(value = { @ApiResponse(responseCode = "200", @@ -371,7 +381,14 @@ public final Response saveNew(@Context final HttpServletRequest request, summary = "Update an existing template", description = "Saves a new working version of an existing template. The form must contain the template " + "identifier. The 'theme' field in the form corresponds to the theme folder identifier " + - "(referred to as 'themeId' in other endpoints). Returns 404 if the template does not exist." + "(referred to as 'themeId' in other endpoints). Returns 404 if the template does not exist.\n\n" + + "When `drawed` is true: `body` is REQUIRED and non-empty (else 400 'body required when drawed'), " + + "and `theme` MUST resolve to a theme **folder** identifier (else 400 'theme must be a folder " + + "identifier'). Include `drawedBody` (the layout JSON) so the template stays a real drawn template.\n\n" + + "For a themed drawn template, `body` is a generated compatibility shell and is not used to " + + "assemble the rendered page; the theme's `template.vtl` and `drawedBody` are authoritative. A " + + "persisted `/themes/null/` reference in that shell is benign, and changing `body` does not repair " + + "or affect themed drawn rendering because the server regenerates it." ) @ApiResponses(value = { @ApiResponse(responseCode = "200", @@ -558,7 +575,26 @@ private static void fillTemplate(TemplateForm templateForm, User user, Host site template.setHeader(templateForm.getHeader()); if (templateForm.isDrawed()) { - final String themeHostId = APILocator.getFolderAPI().find(templateForm.getTheme(), user, respectAnonPerms).getHostId(); + + // Identify the failing Template in every error below so Support can trace it. + final String templateRef = describeTemplate(template); + + // A drawn template's body is parsed by jsoup; a null body NPEs downstream. + if (template.getBody() == null) { + throw new BadRequestException("body required when drawed for " + templateRef); + } + + // 'theme' must resolve to a theme folder; FolderAPI.find returns null for a + // non-folder identifier (e.g. a host id), which would NPE on getHostId(). + final Folder themeFolder = APILocator.getFolderAPI() + .find(templateForm.getTheme(), user, respectAnonPerms); + if (themeFolder == null || !InodeUtils.isSet(themeFolder.getInode())) { + throw new BadRequestException("theme must be a folder identifier; '" + + templateForm.getTheme() + "' does not resolve to a folder for " + + templateRef); + } + + final String themeHostId = themeFolder.getHostId(); final String themePath = themeHostId.equals(site.getInode())? Template.THEMES_PATH + template.getThemeName() + "/": "//" + APILocator.getHostAPI().find(themeHostId, user, respectAnonPerms).getHostname() @@ -570,6 +606,21 @@ private static void fillTemplate(TemplateForm templateForm, User user, Host site } } + /** + * Renders a Template as {@code Template 'title' [id]} for error messages, so Support can + * tell which Template failed. A Template being created has no identifier yet, in which + * case only the title is reported. + * + * @param template the Template being saved + * @return a human-readable reference to the Template + */ + private static String describeTemplate(final Template template) { + final String title = UtilMethods.isSet(template.getTitle()) ? template.getTitle() : "unknown"; + return UtilMethods.isSet(template.getIdentifier()) + ? "Template '" + title + "' [" + template.getIdentifier() + "]" + : "new Template '" + title + "'"; + } + /** * Saves and publish a template. The templateForm must contain the identifier of the template. * @@ -1196,4 +1247,3 @@ public Map<String, Object> fetchTemplateImage(@Context final HttpServletRequest throw new DoesNotExistException("Working Version of the Template with Id: " + templateId + " does not exist"); } } - diff --git a/dotCMS/src/main/java/com/dotcms/rest/api/v1/vtl/CollectingInvalidReferenceHandler.java b/dotCMS/src/main/java/com/dotcms/rest/api/v1/vtl/CollectingInvalidReferenceHandler.java new file mode 100644 index 000000000000..bec3b2aec02e --- /dev/null +++ b/dotCMS/src/main/java/com/dotcms/rest/api/v1/vtl/CollectingInvalidReferenceHandler.java @@ -0,0 +1,87 @@ +package com.dotcms.rest.api.v1.vtl; + +import java.util.ArrayList; +import java.util.List; +import org.apache.velocity.app.event.InvalidReferenceEventHandler; +import org.apache.velocity.context.Context; +import org.apache.velocity.util.introspection.Info; + +/** + * A per-evaluation {@link InvalidReferenceEventHandler} that <em>collects</em> invalid references + * as warnings instead of failing. dotCMS runs Velocity in non-strict mode + * ({@code runtime.references.strict = false}), so a typo like {@code $noSuchVar} renders as literal + * text and {@code $obj.noSuchMethod()} silently yields {@code null}. Attaching this handler to the + * evaluation {@link Context} (via an {@code EventCartridge}) lets the {@code /api/vtl/dynamic} + * endpoints report those mistakes back to the caller while still returning the rendered output. + * + * <p>All callbacks preserve default behavior (they never substitute a value), so attaching this + * handler cannot change what the script produces — it only observes.</p> + */ +public class CollectingInvalidReferenceHandler implements InvalidReferenceEventHandler { + + /** Cap so a pathological script can't accumulate an unbounded warning list. */ + static final int MAX_WARNINGS = 50; + + private final List<VelocityWarningView> warnings = new ArrayList<>(); + + public List<VelocityWarningView> getWarnings() { + return warnings; + } + + private void add(final String type, final String message, final String reference, final Info info) { + if (warnings.size() >= MAX_WARNINGS) { + return; + } + final Integer line = info != null && info.getLine() > 0 ? info.getLine() : null; + final Integer column = info != null && info.getColumn() > 0 ? info.getColumn() : null; + warnings.add(new VelocityWarningView(type, message, reference, line, column)); + } + + @Override + public Object invalidGetMethod(final Context context, final String reference, final Object object, + final String property, final Info info) { + // A null base object with no property is a top-level undefined variable ($noSuchVar); + // a non-null base object means a null/missing property on a real object ($real.missing). + if (object == null && property == null) { + add("UNDEFINED_REFERENCE", + "Undefined reference '" + reference + "' — renders as literal text in non-strict mode", + reference, info); + } else { + add("NULL_METHOD_RESULT", + "Reference '" + reference + "' resolved to null" + describeProperty(property), + reference, info); + } + return null; // keep default (non-strict) behavior + } + + @Override + public Object invalidMethod(final Context context, final String reference, final Object object, + final String method, final Info info) { + // object == null here means the method was invoked on a null reference; otherwise the method + // does not exist on the (non-null) object or it returned null. + final String type = object == null ? "UNDEFINED_REFERENCE" : "INVALID_METHOD"; + add(type, + "Method '" + safe(method) + "()' on '" + reference + "' " + + (object == null ? "was called on a null reference" : "does not exist or returned null"), + reference, info); + return null; // keep default behavior + } + + @Override + public boolean invalidSetMethod(final Context context, final String leftreference, + final String rightreference, final Info info) { + add("NULL_SET", + "#set assigned null to '" + leftreference + "'" + + (rightreference != null ? " from '" + rightreference + "'" : ""), + leftreference, info); + return false; // keep default behavior (do not log-and-swallow differently) + } + + private static String describeProperty(final String property) { + return property != null ? " (property '" + property + "')" : ""; + } + + private static String safe(final String value) { + return value != null ? value : "?"; + } +} diff --git a/dotCMS/src/main/java/com/dotcms/rest/api/v1/vtl/VTLResource.java b/dotCMS/src/main/java/com/dotcms/rest/api/v1/vtl/VTLResource.java index 02d1ab9d8eae..634303e47ec4 100644 --- a/dotCMS/src/main/java/com/dotcms/rest/api/v1/vtl/VTLResource.java +++ b/dotCMS/src/main/java/com/dotcms/rest/api/v1/vtl/VTLResource.java @@ -26,6 +26,7 @@ import java.io.IOException; import java.io.Reader; import java.io.StringWriter; +import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.List; @@ -45,9 +46,17 @@ import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; import javax.ws.rs.core.UriInfo; +import org.apache.velocity.app.event.EventCartridge; import org.apache.velocity.exception.MethodInvocationException; +import org.apache.velocity.exception.ParseErrorException; +import org.apache.velocity.exception.VelocityException; import org.glassfish.jersey.media.multipart.FormDataMultiPart; import org.glassfish.jersey.server.JSONP; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.responses.ApiResponses; import io.swagger.v3.oas.annotations.tags.Tag; @@ -57,6 +66,31 @@ public class VTLResource { public static final String IDENTIFIER = "identifier"; public static final String VELOCITY = "velocity"; + + private static final String DYNAMIC_DESCRIPTION = + "Evaluates Velocity (VTL) code supplied directly in the request body — no `.vtl` file on " + + "disk is required — and returns the result. The code is read from a `velocity` property " + + "of the JSON body (properly escaped), or the body may be the raw VTL itself.\n\n" + + "The caller requires the **Scripting Developer** role.\n\n" + + "**Response shape** is decided by the submitted code:\n" + + "- If the code populates `$dotJSON` (e.g. `$dotJSON.put(\"key\", ...)`), the response is that " + + "JSON object.\n" + + "- Otherwise the raw evaluated output is returned, with the content type set by the script " + + "(defaults to `text/plain`).\n\n" + + "**Velocity errors** (syntax/parse errors, method-invocation failures, missing resources) are " + + "reported as a `400` with a structured body so an automated caller can locate and fix the " + + "offending code instead of receiving partial output. Application-level errors set by the " + + "script via `$dotJSON.put(\"errors\", ...)` are also returned as `400`.\n\n" + + "**Warnings** — dotCMS evaluates Velocity in non-strict mode, so an undefined reference " + + "(`$noSuchVar`) renders as literal text and a method returning `null` produces no output. " + + "These likely-typos are collected and, on a successful response, returned in the " + + "`X-Dot-Velocity-Warnings` header (a JSON array); on a `400` they appear in the `warnings` " + + "field of the body."; + + private static final String DYNAMIC_400_DESCRIPTION = + "The submitted Velocity failed to parse or evaluate, or the script reported errors. The body " + + "carries the Velocity error detail (message, error type, and line/column when available)."; + private final MultiPartUtils multiPartUtils; private final WebResource webResource; @VisibleForTesting @@ -90,7 +124,7 @@ public Response get(@Context final HttpServletRequest request, @Context final Ht @Context UriInfo uriInfo, @PathParam("folder") final String folderName, @PathParam("pathParam") final String pathParam, final Map<String, Object> bodyMap) { - return processRequest(request, response, uriInfo, folderName, pathParam, HTTPMethod.GET, bodyMap); + return processRequest(request, response, uriInfo, folderName, pathParam, HTTPMethod.GET, false, bodyMap); } /** @@ -106,7 +140,7 @@ public Response get(@Context final HttpServletRequest request, @Context final Ht @Context UriInfo uriInfo, @PathParam("folder") final String folderName, final Map<String, Object> bodyMap) { - return processRequest(request, response, uriInfo, folderName, null, HTTPMethod.GET, bodyMap); + return processRequest(request, response, uriInfo, folderName, null, HTTPMethod.GET, false, bodyMap); } /** @@ -126,7 +160,7 @@ public final Response post(@Context final HttpServletRequest request, @Context f @PathParam("pathParam") final String pathParam, final Map<String, Object> bodyMap) { - return processRequest(request, response, uriInfo, folderName, pathParam, HTTPMethod.POST, bodyMap); + return processRequest(request, response, uriInfo, folderName, pathParam, HTTPMethod.POST, false, bodyMap); } @POST @@ -139,7 +173,7 @@ public final Response post(@Context final HttpServletRequest request, @Context f @Context UriInfo uriInfo, @PathParam("folder") final String folderName, final Map<String, Object> bodyMap) { - return processRequest(request, response, uriInfo, folderName, null, HTTPMethod.POST, bodyMap); + return processRequest(request, response, uriInfo, folderName, null, HTTPMethod.POST, false, bodyMap); } /** @@ -159,7 +193,7 @@ public final Response put(@Context final HttpServletRequest request, @Context fi @PathParam("pathParam") final String pathParam, final Map<String, Object> bodyMap) { - return processRequest(request, response, uriInfo, folderName, pathParam, HTTPMethod.PUT, bodyMap); + return processRequest(request, response, uriInfo, folderName, pathParam, HTTPMethod.PUT, false, bodyMap); } @PUT @@ -172,7 +206,7 @@ public final Response put(@Context final HttpServletRequest request, @Context fi @Context UriInfo uriInfo, @PathParam("folder") final String folderName, final Map<String, Object> bodyMap) { - return processRequest(request, response, uriInfo, folderName, null, HTTPMethod.PUT, bodyMap); + return processRequest(request, response, uriInfo, folderName, null, HTTPMethod.PUT, false, bodyMap); } /** @@ -192,7 +226,7 @@ public final Response patch(@Context final HttpServletRequest request, @Context @PathParam("pathParam") final String pathParam, final Map<String, Object> bodyMap) { - return processRequest(request, response, uriInfo, folderName, pathParam, HTTPMethod.PATCH, bodyMap); + return processRequest(request, response, uriInfo, folderName, pathParam, HTTPMethod.PATCH, false, bodyMap); } @PATCH @@ -205,7 +239,7 @@ public final Response patch(@Context final HttpServletRequest request, @Context @Context UriInfo uriInfo, @PathParam("folder") final String folderName, final Map<String, Object> bodyMap) { - return processRequest(request, response, uriInfo, folderName, null, HTTPMethod.PATCH, bodyMap); + return processRequest(request, response, uriInfo, folderName, null, HTTPMethod.PATCH, false, bodyMap); } /** @@ -225,7 +259,7 @@ public final Response delete(@Context final HttpServletRequest request, @Context @PathParam("pathParam") final String pathParam, final Map<String, Object> requestJSONMap) { - return processRequest(request, response, uriInfo, folderName, pathParam, HTTPMethod.DELETE, requestJSONMap); + return processRequest(request, response, uriInfo, folderName, pathParam, HTTPMethod.DELETE, false, requestJSONMap); } @DELETE @@ -238,7 +272,7 @@ public final Response delete(@Context final HttpServletRequest request, @Context @Context UriInfo uriInfo, @PathParam("folder") final String folderName, final Map<String, Object> requestJSONMap) { - return processRequest(request, response, uriInfo, folderName, null, HTTPMethod.DELETE, requestJSONMap); + return processRequest(request, response, uriInfo, folderName, null, HTTPMethod.DELETE, false, requestJSONMap); } /** @@ -342,6 +376,16 @@ public final Response patchMultipart(@Context final HttpServletRequest request, * @deprecated This GET method accepts a request body, which is not standard HTTP practice. * Consider using POST for operations that require request bodies. */ + @Operation(operationId = "dynamicGet", summary = "Evaluate inline Velocity code (GET)", + description = DYNAMIC_DESCRIPTION, tags = {"Templates"}) + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "Velocity evaluated successfully; body is the " + + "raw output or the JSON object produced by the script"), + @ApiResponse(responseCode = "400", description = DYNAMIC_400_DESCRIPTION, + content = @Content(mediaType = "application/json", + schema = @Schema(implementation = VelocityErrorResponseView.class))), + @ApiResponse(responseCode = "403", description = "User lacks the Scripting Developer role") + }) @GET @Path("/dynamic/{pathParam:.*}") @NoCache @@ -353,13 +397,23 @@ public Response dynamicGet(@Context final HttpServletRequest request, @Context f final Map<String, Object> bodyMap = parseBodyMap(bodyMapString); - return processRequest(request, response, uriInfo, null, pathParam, HTTPMethod.GET, bodyMap); + return processRequest(request, response, uriInfo, null, pathParam, HTTPMethod.GET, true, bodyMap); } /** * @deprecated This GET method accepts a request body, which is not standard HTTP practice. * Consider using POST for operations that require request bodies. */ + @Operation(operationId = "dynamicGetNoPath", summary = "Evaluate inline Velocity code (GET)", + description = DYNAMIC_DESCRIPTION, tags = {"Templates"}) + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "Velocity evaluated successfully; body is the " + + "raw output or the JSON object produced by the script"), + @ApiResponse(responseCode = "400", description = DYNAMIC_400_DESCRIPTION, + content = @Content(mediaType = "application/json", + schema = @Schema(implementation = VelocityErrorResponseView.class))), + @ApiResponse(responseCode = "403", description = "User lacks the Scripting Developer role") + }) @GET @Path("/dynamic") @NoCache @@ -376,6 +430,16 @@ public Response dynamicGet(@Context final HttpServletRequest request, @Context f * Same as {@link #post} but supporting sending the velocity to be rendered embedded (properly escaped) in the JSON * in a "velocity" property */ + @Operation(operationId = "dynamicPost", summary = "Evaluate inline Velocity code (POST)", + description = DYNAMIC_DESCRIPTION, tags = {"Templates"}) + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "Velocity evaluated successfully; body is the " + + "raw output or the JSON object produced by the script"), + @ApiResponse(responseCode = "400", description = DYNAMIC_400_DESCRIPTION, + content = @Content(mediaType = "application/json", + schema = @Schema(implementation = VelocityErrorResponseView.class))), + @ApiResponse(responseCode = "403", description = "User lacks the Scripting Developer role") + }) @POST @Path("/dynamic/{pathParam:.*}") @NoCache @@ -387,9 +451,19 @@ public Response dynamicPost(@Context final HttpServletRequest request, @Context final Map<String, Object> bodyMap = parseBodyMap(bodyMapString); - return processRequest(request, response, uriInfo, null, pathParam, HTTPMethod.POST, bodyMap); + return processRequest(request, response, uriInfo, null, pathParam, HTTPMethod.POST, true, bodyMap); } + @Operation(operationId = "dynamicPostNoPath", summary = "Evaluate inline Velocity code (POST)", + description = DYNAMIC_DESCRIPTION, tags = {"Templates"}) + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "Velocity evaluated successfully; body is the " + + "raw output or the JSON object produced by the script"), + @ApiResponse(responseCode = "400", description = DYNAMIC_400_DESCRIPTION, + content = @Content(mediaType = "application/json", + schema = @Schema(implementation = VelocityErrorResponseView.class))), + @ApiResponse(responseCode = "403", description = "User lacks the Scripting Developer role") + }) @POST @Path("/dynamic") @NoCache @@ -406,6 +480,16 @@ public Response dynamicPost(@Context final HttpServletRequest request, @Context * Same as {@link #put} but supporting sending the velocity to be rendered embedded (properly escaped) in the JSON * in a "velocity" property */ + @Operation(operationId = "dynamicPut", summary = "Evaluate inline Velocity code (PUT)", + description = DYNAMIC_DESCRIPTION, tags = {"Templates"}) + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "Velocity evaluated successfully; body is the " + + "raw output or the JSON object produced by the script"), + @ApiResponse(responseCode = "400", description = DYNAMIC_400_DESCRIPTION, + content = @Content(mediaType = "application/json", + schema = @Schema(implementation = VelocityErrorResponseView.class))), + @ApiResponse(responseCode = "403", description = "User lacks the Scripting Developer role") + }) @PUT @Path("/dynamic/{pathParam:.*}") @NoCache @@ -417,9 +501,19 @@ public Response dynamicPut(@Context final HttpServletRequest request, @Context f final Map<String, Object> bodyMap = parseBodyMap(bodyMapString); - return processRequest(request, response, uriInfo, null, pathParam, HTTPMethod.PUT, bodyMap); + return processRequest(request, response, uriInfo, null, pathParam, HTTPMethod.PUT, true, bodyMap); } + @Operation(operationId = "dynamicPutNoPath", summary = "Evaluate inline Velocity code (PUT)", + description = DYNAMIC_DESCRIPTION, tags = {"Templates"}) + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "Velocity evaluated successfully; body is the " + + "raw output or the JSON object produced by the script"), + @ApiResponse(responseCode = "400", description = DYNAMIC_400_DESCRIPTION, + content = @Content(mediaType = "application/json", + schema = @Schema(implementation = VelocityErrorResponseView.class))), + @ApiResponse(responseCode = "403", description = "User lacks the Scripting Developer role") + }) @PUT @Path("/dynamic") @NoCache @@ -436,6 +530,16 @@ public Response dynamicPut(@Context final HttpServletRequest request, @Context f * Same as {@link #patch} but supporting sending the velocity to be rendered embedded (properly escaped) in the JSON * in a "velocity" property */ + @Operation(operationId = "dynamicPatch", summary = "Evaluate inline Velocity code (PATCH)", + description = DYNAMIC_DESCRIPTION, tags = {"Templates"}) + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "Velocity evaluated successfully; body is the " + + "raw output or the JSON object produced by the script"), + @ApiResponse(responseCode = "400", description = DYNAMIC_400_DESCRIPTION, + content = @Content(mediaType = "application/json", + schema = @Schema(implementation = VelocityErrorResponseView.class))), + @ApiResponse(responseCode = "403", description = "User lacks the Scripting Developer role") + }) @PATCH @Path("/dynamic/{pathParam:.*}") @NoCache @@ -447,7 +551,7 @@ public Response dynamicPatch(@Context final HttpServletRequest request, @Context final Map<String, Object> bodyMap = parseBodyMap(bodyMapString); - return processRequest(request, response, uriInfo, null, pathParam, HTTPMethod.PATCH, bodyMap); + return processRequest(request, response, uriInfo, null, pathParam, HTTPMethod.PATCH, true, bodyMap); } @@ -455,6 +559,16 @@ public Response dynamicPatch(@Context final HttpServletRequest request, @Context * Same as {@link #delete} but supporting sending the velocity to be rendered embedded (properly escaped) in the JSON * in a "velocity" property */ + @Operation(operationId = "dynamicDelete", summary = "Evaluate inline Velocity code (DELETE)", + description = DYNAMIC_DESCRIPTION, tags = {"Templates"}) + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "Velocity evaluated successfully; body is the " + + "raw output or the JSON object produced by the script"), + @ApiResponse(responseCode = "400", description = DYNAMIC_400_DESCRIPTION, + content = @Content(mediaType = "application/json", + schema = @Schema(implementation = VelocityErrorResponseView.class))), + @ApiResponse(responseCode = "403", description = "User lacks the Scripting Developer role") + }) @DELETE @Path("/dynamic/{pathParam:.*}") @NoCache @@ -466,7 +580,7 @@ public Response dynamicDelete(@Context final HttpServletRequest request, @Contex final Map<String, Object> bodyMap = parseBodyMap(bodyMapString); - return processRequest(request, response, uriInfo, null, pathParam, HTTPMethod.DELETE, bodyMap); + return processRequest(request, response, uriInfo, null, pathParam, HTTPMethod.DELETE, true, bodyMap); } private Response processMultiPartRequest(final HttpServletRequest request, final HttpServletResponse response, @@ -478,7 +592,7 @@ private Response processMultiPartRequest(final HttpServletRequest request, final final List<File> binaries = getBinariesFromMultipart(multipart); final Map<String, Object> bodyMap = getBodyMapFromMultipart(multipart); - return processRequest(request, response, uriInfo, folderName, pathParam, httpMethod, bodyMap, + return processRequest(request, response, uriInfo, folderName, pathParam, httpMethod, false, bodyMap, binaries.toArray(new File[0])); } catch(Exception e) { Logger.error(this,"Exception on VTL endpoint. POST method: " + e.getMessage(), e); @@ -490,6 +604,7 @@ private Response processRequest(final HttpServletRequest request, final HttpServ final UriInfo uriInfo, final String folderName, final String pathParam, final HTTPMethod httpMethod, + final boolean dynamic, final Map<String, Object> bodyMap, final File...binaries) { @@ -530,7 +645,7 @@ private Response processRequest(final HttpServletRequest request, final HttpServ try(Reader reader = velocityReader.getVelocity(velocityReaderParams)){ return evalVelocity(request, response, reader, contextParams, - initDataObject.getUser(), cache); + initDataObject.getUser(), cache, dynamic); } } catch(Exception e) { Logger.error(this,"Exception on VTL endpoint. GET method: " + e.getMessage(), e); @@ -540,27 +655,54 @@ private Response processRequest(final HttpServletRequest request, final HttpServ private Response evalVelocity(final HttpServletRequest request, final HttpServletResponse response, final Reader velocityReader, final Map<String, Object> contextParams, - final User user, final DotJSONCache cache) + final User user, final DotJSONCache cache, final boolean dynamic) throws Exception { final org.apache.velocity.context.Context context = VelocityUtil.getInstance().getContext(request, response); contextParams.forEach(context::put); context.put("dotJSON", new DotJSON()); + // For the /dynamic endpoints, attach a per-evaluation handler that collects invalid + // references (undefined vars, null method results) as warnings. dotCMS runs Velocity in + // non-strict mode, so these otherwise fail silently — catching them here helps a caller + // spot typos. The handler never substitutes a value, so output is unchanged. + final CollectingInvalidReferenceHandler warningsHandler = + dynamic ? new CollectingInvalidReferenceHandler() : null; + if (warningsHandler != null) { + final EventCartridge eventCartridge = new EventCartridge(); + eventCartridge.addEventHandler(warningsHandler); + eventCartridge.attachToContext(context); + } + final StringWriter evalResult = new StringWriter(); + // A meaningful log tag so parse errors reference the submitted script rather than an empty name. + final String logTag = dynamic ? "dynamic velocity" : ""; try { - VelocityUtil.getEngine().evaluate(context, evalResult, "", velocityReader); + VelocityUtil.getEngine().evaluate(context, evalResult, logTag, velocityReader); } catch(MethodInvocationException e) { + // For the /dynamic endpoints the caller owns the submitted code, so surface the Velocity + // error instead of returning whatever partial output was rendered before it failed. + if (dynamic) { + return velocityErrorResponse(e, warningsHandler); + } if(e.getCause() instanceof DotToolException) { Logger.error(this,"Error evaluating velocity: " + (e.getCause()).getCause().getMessage()); throw (Exception) (e.getCause()).getCause(); } + } catch(VelocityException e) { + // Parse errors, resource-not-found and other engine failures propagate as-is for the + // convention-based endpoints, but the /dynamic endpoints report them so the caller can fix + // the code it submitted rather than getting a generic 500. + if (dynamic) { + return velocityErrorResponse(e, warningsHandler); + } + throw e; } final DotJSON dotJSON = (DotJSON) context.get("dotJSON"); if(dotJSON.size()==0) { // If dotJSON is not used let's return the raw evaluation of the velocity file final HttpServletResponse velocityResponse = (HttpServletResponse) context.get("response"); - + final String contentType = (velocityResponse!=null && velocityResponse.getContentType()!=null) ? velocityResponse.getContentType() : MediaType.TEXT_PLAIN_TYPE.toString(); if(velocityResponse!=null && velocityResponse.getHeaderNames()!=null){ for(final String headerName : velocityResponse.getHeaderNames()) { @@ -568,9 +710,10 @@ private Response evalVelocity(final HttpServletRequest request, final HttpServle } } - return UtilMethods.isSet(contentType) - ? Response.ok(evalResult.toString()).type(contentType).build() - : Response.ok(evalResult.toString()).type(MediaType.TEXT_PLAIN_TYPE).build(); + final Response.ResponseBuilder builder = UtilMethods.isSet(contentType) + ? Response.ok(evalResult.toString()).type(contentType) + : Response.ok(evalResult.toString()).type(MediaType.TEXT_PLAIN_TYPE); + return withWarningsHeader(builder, warningsHandler).build(); } else { // let's add it to cache @@ -579,8 +722,152 @@ private Response evalVelocity(final HttpServletRequest request, final HttpServle } cache.add(request, user, dotJSON); - return Response.ok(dotJSON.getMap()).build(); + return withWarningsHeader(Response.ok(dotJSON.getMap()), warningsHandler).build(); + } + } + + /** + * Maximum serialized length of the {@code X-Dot-Velocity-Warnings} header value. + * + * <p>Tomcat's default {@code maxHttpHeaderSize} is 8 KB for the whole header block, and a + * reverse proxy may enforce a tighter limit. {@link CollectingInvalidReferenceHandler#MAX_WARNINGS} + * warnings carrying long chained references serialize to well over that, which would make the + * container reject or truncate an otherwise successful 200. Cap the value at a conservative + * budget that leaves room for the rest of the response headers.</p> + */ + private static final int MAX_WARNINGS_HEADER_LENGTH = 4096; + + /** + * Attaches collected Velocity warnings to a successful response as the {@code X-Dot-Velocity-Warnings} + * header (JSON array), keeping the response body byte-for-byte the script's output. No-op when + * there are no warnings or the handler is absent (non-dynamic requests). + * + * <p>The serialized value is bounded by {@link #MAX_WARNINGS_HEADER_LENGTH}: warnings are dropped + * from the tail until the JSON fits, and a trailing marker records how many were omitted so a + * caller never mistakes a truncated list for the complete one. The full set is always logged.</p> + */ + private Response.ResponseBuilder withWarningsHeader(final Response.ResponseBuilder builder, + final CollectingInvalidReferenceHandler warningsHandler) { + if (warningsHandler == null || warningsHandler.getWarnings().isEmpty()) { + return builder; + } + try { + final ObjectMapper objectMapper = new ObjectMapper(); + final List<VelocityWarningView> warnings = warningsHandler.getWarnings(); + String serialized = objectMapper.writeValueAsString(warnings); + + if (serialized.length() > MAX_WARNINGS_HEADER_LENGTH) { + // Drop from the tail until the payload (plus its truncation marker) fits. The + // earliest warnings are the most useful — they point at the first mistake. + final List<VelocityWarningView> kept = new ArrayList<>(warnings); + String candidate = serialized; + while (!kept.isEmpty() && candidate.length() > MAX_WARNINGS_HEADER_LENGTH) { + kept.remove(kept.size() - 1); + final List<Object> withMarker = new ArrayList<>(kept); + withMarker.add(Map.of( + "type", "TRUNCATED", + "message", (warnings.size() - kept.size()) + + " additional warning(s) omitted to stay within the response " + + "header size limit; see the server log for the full list")); + candidate = objectMapper.writeValueAsString(withMarker); + } + Logger.warn(this, "Velocity warnings header truncated: kept " + kept.size() + + " of " + warnings.size() + " warnings. Full list: " + warnings); + serialized = candidate; + } + + return builder.header("X-Dot-Velocity-Warnings", serialized); + } catch (final IOException e) { + Logger.warn(this, "Unable to serialize Velocity warnings header: " + e.getMessage()); + return builder; + } + } + + /** + * Builds a {@code 400 Bad Request} carrying a structured description of a Velocity error, so the + * caller (typically an automated agent that submitted the VTL) can locate and fix the offending + * code instead of receiving partial output or a generic {@code 500}. + * + * <p>The {@code errorType} is normalized to the public Velocity exception (dotCMS-internal + * subclasses such as {@code PreviewEditParseErrorException} are reported as their + * {@link ParseErrorException} parent). {@code message} is a concise one-liner; the full engine + * output — including the exhaustive grammar-token list a parse error carries — is kept in + * {@code detail}. Any warnings collected before the failure are included too.</p> + * + * @param e the Velocity engine exception thrown while evaluating the submitted code + * @param warningsHandler collected invalid-reference warnings, or {@code null} + * @return a bad-request {@link Response} with a {@link VelocityErrorResponseView} body + */ + private Response velocityErrorResponse(final VelocityException e, + final CollectingInvalidReferenceHandler warningsHandler) { + Logger.warn(this, "Velocity error on /dynamic endpoint: " + e.getMessage()); + + final String fullMessage = e.getMessage(); + String templateName = null; + Integer line = null; + Integer column = null; + + if (e instanceof ParseErrorException) { + final ParseErrorException parseError = (ParseErrorException) e; + templateName = parseError.getTemplateName(); + line = parseError.getLineNumber() > 0 ? parseError.getLineNumber() : null; + column = parseError.getColumnNumber() > 0 ? parseError.getColumnNumber() : null; + } else if (e instanceof MethodInvocationException) { + final MethodInvocationException methodError = (MethodInvocationException) e; + templateName = methodError.getTemplateName(); + line = methodError.getLineNumber() > 0 ? methodError.getLineNumber() : null; + column = methodError.getColumnNumber() > 0 ? methodError.getColumnNumber() : null; + } + + // The actionable message for tool/runtime failures lives on the root cause; use it as the + // concise summary when present. + final String rootCauseMessage = (e instanceof MethodInvocationException && e.getCause() != null + && UtilMethods.isSet(e.getCause().getMessage())) ? e.getCause().getMessage() : null; + + final String summary = rootCauseMessage != null ? firstLine(rootCauseMessage) : firstLine(fullMessage); + // Only keep `detail` when it adds something beyond the one-line summary (parse errors do). + final String detail = (fullMessage != null && !fullMessage.trim().equals(summary)) ? fullMessage : null; + + final VelocityErrorView error = new VelocityErrorView(summary, normalizeErrorType(e), + UtilMethods.isSet(templateName) ? templateName : null, line, column, detail); + final List<VelocityErrorView> errors = new ArrayList<>(); + errors.add(error); + + final List<VelocityWarningView> warnings = + warningsHandler != null ? warningsHandler.getWarnings() : List.of(); + + return Response.status(Response.Status.BAD_REQUEST) + .entity(new VelocityErrorResponseView(errors, warnings)) + .type(MediaType.APPLICATION_JSON_TYPE) + .build(); + } + + /** + * Reports the public Velocity exception name rather than a dotCMS-internal subclass. For example + * {@code PreviewEditParseErrorException} — an internal wrapper that only marks preview/edit mode — + * is reported as {@code ParseErrorException}, which is meaningful to an API consumer. + */ + private static String normalizeErrorType(final VelocityException e) { + if (e instanceof ParseErrorException) { + return ParseErrorException.class.getSimpleName(); + } + if (e instanceof MethodInvocationException) { + return MethodInvocationException.class.getSimpleName(); + } + return e.getClass().getSimpleName(); + } + + /** Returns the first non-blank line of a (possibly multi-line) message, trimmed. */ + private static String firstLine(final String message) { + if (message == null) { + return ""; + } + for (final String line : message.split("\\r?\\n")) { + if (UtilMethods.isSet(line.trim())) { + return line.trim(); + } } + return message.trim(); } private Map<String, Object> getBodyMapFromMultipart(final FormDataMultiPart multipart) throws IOException, JSONException { diff --git a/dotCMS/src/main/java/com/dotcms/rest/api/v1/vtl/VelocityErrorResponseView.java b/dotCMS/src/main/java/com/dotcms/rest/api/v1/vtl/VelocityErrorResponseView.java new file mode 100644 index 000000000000..a46645a97dc8 --- /dev/null +++ b/dotCMS/src/main/java/com/dotcms/rest/api/v1/vtl/VelocityErrorResponseView.java @@ -0,0 +1,40 @@ +package com.dotcms.rest.api.v1.vtl; + +import com.fasterxml.jackson.annotation.JsonInclude; +import io.swagger.v3.oas.annotations.media.Schema; +import java.util.List; + +/** + * Error payload returned with an HTTP 400 by the {@code /api/vtl/dynamic} endpoints when the + * submitted Velocity code fails to parse or evaluate. Mirrors the {@code {"errors": [...]}} shape + * that VTL authors already produce via {@code $dotJSON.put("errors", ...)}, so consumers can handle + * both application-level and engine-level errors uniformly. + * + * <p>{@code warnings} carries any non-fatal issues (undefined references, null method results) + * observed before the fatal error — useful context when a typo cascades into a hard failure. It is + * omitted when empty.</p> + */ +public class VelocityErrorResponseView { + + @Schema(description = "List of Velocity errors detected while evaluating the submitted code.") + private final List<VelocityErrorView> errors; + + @JsonInclude(JsonInclude.Include.NON_EMPTY) + @Schema(description = "Non-fatal warnings (undefined references, null method results) observed before the " + + "error. Omitted when there are none.") + private final List<VelocityWarningView> warnings; + + public VelocityErrorResponseView(final List<VelocityErrorView> errors, + final List<VelocityWarningView> warnings) { + this.errors = errors; + this.warnings = warnings; + } + + public List<VelocityErrorView> getErrors() { + return errors; + } + + public List<VelocityWarningView> getWarnings() { + return warnings; + } +} diff --git a/dotCMS/src/main/java/com/dotcms/rest/api/v1/vtl/VelocityErrorView.java b/dotCMS/src/main/java/com/dotcms/rest/api/v1/vtl/VelocityErrorView.java new file mode 100644 index 000000000000..069ce6acd612 --- /dev/null +++ b/dotCMS/src/main/java/com/dotcms/rest/api/v1/vtl/VelocityErrorView.java @@ -0,0 +1,89 @@ +package com.dotcms.rest.api.v1.vtl; + +import com.fasterxml.jackson.annotation.JsonInclude; +import io.swagger.v3.oas.annotations.media.Schema; + +/** + * Structured description of a single Velocity evaluation error surfaced by the + * {@code /api/vtl/dynamic} endpoints. It is designed so an automated caller (for example, an AI + * agent that generated the VTL) can locate and fix the offending code without having to parse a + * free-form stack trace. + * + * <p>The {@code line} and {@code column} fields are only populated when the underlying Velocity + * exception reports a position (parse and method-invocation errors do; resource-not-found errors + * usually do not). A value of {@code 0} means "not available" and is omitted from the JSON.</p> + * + * <p>{@code message} is a concise, single-line summary; the full engine output (for parse errors, + * the exhaustive "was expecting one of ..." token list) is preserved in {@code detail} for human + * display.</p> + */ +@JsonInclude(JsonInclude.Include.NON_NULL) +@Schema(description = "A single Velocity evaluation error, structured so an automated caller can locate and fix " + + "the offending code without parsing a stack trace.") +public class VelocityErrorView { + + @Schema(description = "Concise, single-line Velocity error message including the offending token and position " + + "when available. See `detail` for the full engine output.", + example = "Encountered \"<EOF>\" at line 6, column 39") + private final String message; + + @Schema(description = "Simple class name of the underlying Velocity error, normalized to the public Velocity " + + "type (e.g. ParseErrorException, MethodInvocationException, ResourceNotFoundException) rather than a " + + "dotCMS-internal subclass. Lets the caller distinguish a syntax error from a runtime error.", + example = "ParseErrorException") + private final String errorType; + + @Schema(description = "Name Velocity associated with the evaluated template. For dynamic requests this is a " + + "synthetic name identifying the submitted script.", + example = "dynamic velocity") + private final String templateName; + + @Schema(description = "1-based line number in the submitted velocity where the error occurred, when Velocity " + + "reports it. Omitted when unavailable.", + example = "6") + private final Integer line; + + @Schema(description = "1-based column number in the submitted velocity where the error occurred, when Velocity " + + "reports it. Omitted when unavailable.", + example = "39") + private final Integer column; + + @Schema(description = "Full, multi-line engine output for the error, including the complete grammar-token list " + + "for parse errors. Intended for human display; omitted when it adds nothing beyond `message`.", + example = "Encountered \"<EOF>\" at line 6, column 39\nWas expecting one of:\n \"[\" ...\n \"(\" ...") + private final String detail; + + public VelocityErrorView(final String message, final String errorType, final String templateName, + final Integer line, final Integer column, final String detail) { + this.message = message; + this.errorType = errorType; + this.templateName = templateName; + this.line = line; + this.column = column; + this.detail = detail; + } + + public String getMessage() { + return message; + } + + public String getErrorType() { + return errorType; + } + + public String getTemplateName() { + return templateName; + } + + public Integer getLine() { + return line; + } + + public Integer getColumn() { + return column; + } + + public String getDetail() { + return detail; + } +} diff --git a/dotCMS/src/main/java/com/dotcms/rest/api/v1/vtl/VelocityWarningView.java b/dotCMS/src/main/java/com/dotcms/rest/api/v1/vtl/VelocityWarningView.java new file mode 100644 index 000000000000..3244a966825f --- /dev/null +++ b/dotCMS/src/main/java/com/dotcms/rest/api/v1/vtl/VelocityWarningView.java @@ -0,0 +1,71 @@ +package com.dotcms.rest.api.v1.vtl; + +import com.fasterxml.jackson.annotation.JsonInclude; +import io.swagger.v3.oas.annotations.media.Schema; + +/** + * A non-fatal Velocity warning surfaced by the {@code /api/vtl/dynamic} endpoints. + * + * <p>Because dotCMS evaluates Velocity in non-strict mode (an undefined reference renders as its + * literal text and a method call that returns {@code null} produces no output), typos such as + * {@code $noSuchVar} or {@code $obj.noSuchMethod()} would otherwise fail silently. The dynamic + * endpoints attach a per-evaluation handler that collects these as warnings so a caller (typically + * an automated agent) can spot the mistake — the script still runs and its output is returned.</p> + */ +@JsonInclude(JsonInclude.Include.NON_NULL) +@Schema(description = "A non-fatal Velocity warning, such as an undefined reference or a method call that " + + "returned null. The script still evaluated; warnings flag likely typos in non-strict mode.") +public class VelocityWarningView { + + @Schema(description = "The kind of warning.", + allowableValues = {"UNDEFINED_REFERENCE", "NULL_METHOD_RESULT", "INVALID_METHOD", "NULL_SET"}, + example = "UNDEFINED_REFERENCE") + private final String type; + + @Schema(description = "Human-readable description of the warning.", + example = "Undefined reference '$noSuchVar' — renders as literal text in non-strict mode") + private final String message; + + @Schema(description = "The reference or method expression that triggered the warning, when known.", + example = "$noSuchVar") + private final String reference; + + @Schema(description = "1-based line number where the reference appears, when Velocity reports it. " + + "Omitted when unavailable.", + example = "3") + private final Integer line; + + @Schema(description = "1-based column number where the reference appears, when Velocity reports it. " + + "Omitted when unavailable.", + example = "1") + private final Integer column; + + public VelocityWarningView(final String type, final String message, final String reference, + final Integer line, final Integer column) { + this.type = type; + this.message = message; + this.reference = reference; + this.line = line; + this.column = column; + } + + public String getType() { + return type; + } + + public String getMessage() { + return message; + } + + public String getReference() { + return reference; + } + + public Integer getLine() { + return line; + } + + public Integer getColumn() { + return column; + } +} diff --git a/dotCMS/src/main/java/com/dotcms/rest/api/v1/workflow/WorkflowResource.java b/dotCMS/src/main/java/com/dotcms/rest/api/v1/workflow/WorkflowResource.java index a0e2525d4af7..586c8196dd4f 100644 --- a/dotCMS/src/main/java/com/dotcms/rest/api/v1/workflow/WorkflowResource.java +++ b/dotCMS/src/main/java/com/dotcms/rest/api/v1/workflow/WorkflowResource.java @@ -1590,7 +1590,13 @@ public final Response findSystemActionsByScheme(@Context final HttpServletReques @Operation(operationId = "getSystemActionMappingsByContentType", summary = "Find default system actions mapped to a content type", description = "Returns a list of [default system actions](https://www.dotcms.com/docs/latest/managing-" + "workflows#DefaultActions) associated with a specified [content type](https://www.dotcms.com" + - "/docs/latest/content-types).", + "/docs/latest/content-types).\n\n" + + "An empty list means only that no *default system-action mappings* (e.g. NEW, PUBLISH) are " + + "configured for this content type — it does **not** mean the content type lacks a workflow or " + + "that publishing will fail. You can still fire actions on its content by ID via " + + "`PUT /api/v1/workflow/actions/{actionId}/fire`, or fire a default system action via " + + "`PUT /api/v1/workflow/actions/default/fire/{systemAction}` (which resolves the action from " + + "the scheme attached to the content type). Do not treat an empty response as a blocker.", tags = {"Workflow"}, responses = { @ApiResponse(responseCode = "200", description = "Action(s) returned successfully from content type", @@ -3210,7 +3216,8 @@ private Set<String> validateFireActionForm(final FireActionForm fireActionForm, // (see 'pathToMove'). Logger.warn(this, String.format( "Fire action payload contains system field(s) %s; these are ignored by " - + "this endpoint. To change a contentlet's location, fire a workflow " + + "this endpoint. Did you mean 'contentHost' (host id) or 'hostFolder' " + + "(folder id)? To change a contentlet's location, fire a workflow " + "action that includes the Move actionlet (see 'pathToMove').", protectedFields)); @@ -3234,9 +3241,10 @@ private List<MessageEntity> ignoredSystemFieldsMessages(final Set<String> ignore return List.of(new MessageEntity(String.format( "System field(s) %s were ignored: this endpoint does not set a contentlet's " - + "location. The content was saved at its existing/default location. To " - + "place or move content, fire a workflow action that includes the Move " - + "actionlet and pass 'pathToMove'.", + + "location. Did you mean 'contentHost' (host id) or 'hostFolder' (folder " + + "id)? The content was saved at its existing/default location. To place or " + + "move content, fire a workflow action that includes the Move actionlet and " + + "pass 'pathToMove'.", ignoredFields))); } @@ -3290,6 +3298,12 @@ private boolean needSave (final FireActionForm fireActionForm) { "by name on a target contentlet.\n\nReturns a map of the resultant contentlet, " + "with an additional `AUTO_ASSIGN_WORKFLOW` property, which can be referenced by delegate " + "services that handle automatically assigning workflow schemes to content with none.\n\n" + + "**Use `PUT` for a single contentlet.** This path also accepts `POST`, but that is a " + + "**different** operation that fires over *multiple* contentlets and returns a different envelope " + + "(`entity.results[]`, a list). Sending a single-contentlet body via `POST` will not return the " + + "created contentlet's `identifier` where you expect it, even though the record may still be " + + "(half-)created by the content type's default workflow — a common silent trap. For one item, " + + "always use `PUT`.\n\n" + "**Request body** — wrap field values in a `contentlet` key:\n\n" + "```json\n" + "{\n" + @@ -3579,7 +3593,10 @@ public final Response fireActionDefaultSinglePart(@Context final HttpServletRequ description = "Fire a [default system action](https://www.dotcms.com/docs/latest/managing-workflows#DefaultActions) " + "by name on multiple target contentlets.\n\nReturns a list of resultant contentlet maps, each with an additional " + "`AUTO_ASSIGN_WORKFLOW` property, which can be referenced by delegate " + - "services that handle automatically assigning workflow schemes to content with none.", + "services that handle automatically assigning workflow schemes to content with none.\n\n" + + "This is the **multi-contentlet** variant and returns a list envelope (`entity.results[]`). " + + "To fire on a **single** contentlet, use `PUT` on this same path instead — it returns the single " + + "resultant contentlet map (with its `identifier`) directly.", tags = {"Workflow"}, responses = { @ApiResponse(responseCode = "200", description = "Fired action successfully", diff --git a/dotCMS/src/main/java/com/dotcms/rest/api/v3/contenttype/FieldResource.java b/dotCMS/src/main/java/com/dotcms/rest/api/v3/contenttype/FieldResource.java index b2ea5ad9e6d1..cac482fa9bea 100644 --- a/dotCMS/src/main/java/com/dotcms/rest/api/v3/contenttype/FieldResource.java +++ b/dotCMS/src/main/java/com/dotcms/rest/api/v3/contenttype/FieldResource.java @@ -96,9 +96,27 @@ public FieldResource() { @Operation( operationId = "updateContentTypeField", summary = "Updates a field in a Content Type", - description = "Updates a field in a Content Type. The request body must have the follow " + - "syntax:", + description = "Updates a field in a Content Type. The request body is wrapped in a required " + + "top-level `field` property. To update a constant field such as a WIDGET's `widgetCode`, " + + "fetch the existing field, preserve its attributes, change `values`, and send " + + "`{\"field\":{...}}`.", tags = {"Content Type Field"}, + requestBody = @io.swagger.v3.oas.annotations.parameters.RequestBody( + description = "Field update wrapper. The top-level `field` property is required.", + required = true, + content = @Content( + schema = @Schema(implementation = UpdateFieldRequestView.class), + examples = @ExampleObject(value = "{\n" + + " \"field\": {\n" + + " \"id\": \"<widgetCode-field-id>\",\n" + + " \"contentTypeId\": \"<widget-content-type-id>\",\n" + + " \"clazz\": \"com.dotcms.contenttype.model.field.ImmutableConstantField\",\n" + + " \"name\": \"Widget Code\",\n" + + " \"variable\": \"widgetCode\",\n" + + " \"dataType\": \"LONG_TEXT\",\n" + + " \"values\": \"#dotParse('/application/vtl/widget.vtl')\"\n" + + " }\n" + + "}"))), responses = { @ApiResponse( responseCode = "200", @@ -236,9 +254,7 @@ public Response updateField( @PathParam("id") @Parameter( description = "The ID of the Field that is being updated.", schema = @Schema(type = "String")) final String fieldId, - @Parameter( - description = "The object containing the updated attributes of the Field.", - schema = @Schema(type = "UpdateFieldForm")) final UpdateFieldForm updateFieldForm, + final UpdateFieldForm updateFieldForm, @Context final HttpServletRequest httpRequest) throws DotDataException, DotSecurityException { final InitDataObject initData = @@ -484,7 +500,22 @@ public Response moveFields( @JSONP @NoCache @Produces({ MediaType.APPLICATION_JSON, "application/javascript" }) + @Operation( + operationId = "getContentTypeFieldLayout", + summary = "Gets a Content Type's field layout", + description = "Returns the Content Type's current field layout (rows, columns and the " + + "fields within them). If the layout is invalid it is repaired before being " + + "returned; this endpoint does not modify data in the database.", + tags = {"Content Type Field"}, + responses = { + @ApiResponse(responseCode = "200", description = "The Content Type's field layout"), + @ApiResponse(responseCode = "401", description = "Unauthorized access"), + @ApiResponse(responseCode = "404", description = "Content Type not found"), + @ApiResponse(responseCode = "500", description = "Internal Server Error") + } + ) public final Response getContentTypeFields( + @Parameter(description = "The ID or Velocity Variable Name of the Content Type.") @PathParam("typeIdOrVarName") final String typeIdOrVarName, @Context final HttpServletRequest req ) throws DotDataException, DotSecurityException { @@ -510,8 +541,29 @@ public final Response getContentTypeFields( @DELETE @JSONP @NoCache + @Consumes(MediaType.APPLICATION_JSON) @Produces({ MediaType.APPLICATION_JSON, "application/javascript" }) + @Operation( + operationId = "deleteContentTypeFields", + summary = "Deletes fields from a Content Type", + description = "Deletes one or more fields from a Content Type and returns the updated " + + "field layout together with the IDs that were actually deleted. A field being " + + "used as the Content Type's Publish or Expire date field cannot be deleted " + + "until it is unlinked.", + tags = {"Content Type Field"}, + requestBody = @io.swagger.v3.oas.annotations.parameters.RequestBody( + description = "The IDs of the fields to delete.", + required = true, + content = @Content(schema = @Schema(implementation = DeleteFieldsForm.class))), + responses = { + @ApiResponse(responseCode = "200", description = "Fields deleted; returns the new layout and deleted IDs"), + @ApiResponse(responseCode = "401", description = "Unauthorized access"), + @ApiResponse(responseCode = "404", description = "Content Type not found"), + @ApiResponse(responseCode = "500", description = "Internal Server Error") + } + ) public Response deleteFields( + @Parameter(description = "The ID or Velocity Variable Name of the Content Type.") @PathParam("typeIdOrVarName") final String typeIdOrVarName, final DeleteFieldsForm deleteFieldsForm, @Context final HttpServletRequest req diff --git a/dotCMS/src/main/java/com/dotcms/rest/api/v3/contenttype/UpdateFieldRequestView.java b/dotCMS/src/main/java/com/dotcms/rest/api/v3/contenttype/UpdateFieldRequestView.java new file mode 100644 index 000000000000..bb32f3784ebc --- /dev/null +++ b/dotCMS/src/main/java/com/dotcms/rest/api/v3/contenttype/UpdateFieldRequestView.java @@ -0,0 +1,19 @@ +package com.dotcms.rest.api.v3.contenttype; + +import com.dotcms.rest.api.v1.contenttype.ContentTypeFieldView; +import io.swagger.v3.oas.annotations.media.Schema; + +/** Swagger-only view for the wire envelope accepted by the single-field update endpoint. */ +@Schema(description = "Request body for updating one content-type field. The field object must be " + + "wrapped in the top-level `field` property.") +public class UpdateFieldRequestView { + + @Schema(requiredMode = Schema.RequiredMode.REQUIRED, + description = "The complete field definition to update. Preserve the existing field attributes " + + "and change only the intended values; constant fields store their shared value in `values`.") + private ContentTypeFieldView field; + + public ContentTypeFieldView getField() { + return field; + } +} diff --git a/dotCMS/src/main/java/com/dotcms/rest/config/DotRestApplication.java b/dotCMS/src/main/java/com/dotcms/rest/config/DotRestApplication.java index f44ca0470f7f..9a98dc7ec5ad 100644 --- a/dotCMS/src/main/java/com/dotcms/rest/config/DotRestApplication.java +++ b/dotCMS/src/main/java/com/dotcms/rest/config/DotRestApplication.java @@ -41,6 +41,7 @@ description = "dotCMS Server", url = "/"), tags = { + @Tag(name = "Accessibility Agent", description = "Streaming a11y-fix agent proxy"), @Tag(name = "Accessibility Checker", description = "Web accessibility checking and compliance"), @Tag(name = "Administration", description = "System administration and management tools"), @Tag(name = "AI", description = "AI-powered content generation and analysis endpoints"), diff --git a/dotCMS/src/main/java/com/dotcms/rest/exception/mapper/UnrecognizedPropertyExceptionMapper.java b/dotCMS/src/main/java/com/dotcms/rest/exception/mapper/UnrecognizedPropertyExceptionMapper.java index b6f775af3b90..46d56c34b972 100644 --- a/dotCMS/src/main/java/com/dotcms/rest/exception/mapper/UnrecognizedPropertyExceptionMapper.java +++ b/dotCMS/src/main/java/com/dotcms/rest/exception/mapper/UnrecognizedPropertyExceptionMapper.java @@ -1,28 +1,68 @@ package com.dotcms.rest.exception.mapper; import com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException; +import java.util.Collection; +import java.util.Objects; +import java.util.stream.Collectors; import javax.ws.rs.core.Response; import javax.ws.rs.ext.ExceptionMapper; import javax.ws.rs.ext.Provider; import com.dotmarketing.util.Logger; +import com.dotmarketing.util.UtilMethods; +/** + * Maps Jackson's {@link UnrecognizedPropertyException} (an unknown field in a JSON request body) + * to a clean 400 response. This {@code @Provider} is auto-discovered, so it applies to every write + * endpoint whose form is deserialized with standard Jackson semantics. + * + * <p>The raw {@code exception.getMessage()} is verbose and leaks Java class names and JSON pointer + * paths. We replace it with a concise, caller-actionable message: the offending property plus the + * sorted list of valid field names for that form.</p> + */ @Provider public class UnrecognizedPropertyExceptionMapper implements ExceptionMapper<UnrecognizedPropertyException> { @Override - public Response toResponse(UnrecognizedPropertyException exception) - { - //Log into our logs first. + public Response toResponse(final UnrecognizedPropertyException exception) { + + //Log into our logs first (full detail, including the original Jackson message). Logger.warn(this.getClass(), exception.getMessage(), exception); - //Create the message. - String message = exception.getMessage(); + final String message = buildMessage(exception); //Creating the message in JSON format. - String entity = ExceptionMapperUtil.getJsonErrorAsString(message); + final String entity = ExceptionMapperUtil.getJsonErrorAsString(message); //Return 4xx message to the client. return ExceptionMapperUtil.createResponse(entity, message); } + + /** + * Builds a concise message naming the unrecognized field and listing the valid field names for + * the target form, e.g. + * {@code Unrecognized field 'notARealField'. Valid fields are: [body, drawed, theme, title]}. + */ + private String buildMessage(final UnrecognizedPropertyException exception) { + + final String unknownField = exception.getPropertyName(); + + final Collection<Object> knownIds = exception.getKnownPropertyIds(); + final String knownFields = knownIds == null ? "" : + knownIds.stream() + .filter(Objects::nonNull) + .map(Object::toString) + .sorted() + .collect(Collectors.joining(", ")); + + final StringBuilder builder = new StringBuilder("Unrecognized field"); + if (UtilMethods.isSet(unknownField)) { + builder.append(" '").append(unknownField).append('\''); + } + builder.append('.'); + if (UtilMethods.isSet(knownFields)) { + builder.append(" Valid fields are: [").append(knownFields).append(']'); + } + return builder.toString(); + } } diff --git a/dotCMS/src/main/java/com/dotcms/workflow/form/FireActionForm.java b/dotCMS/src/main/java/com/dotcms/workflow/form/FireActionForm.java index 5c38e9a1056f..ef823bd8d00b 100644 --- a/dotCMS/src/main/java/com/dotcms/workflow/form/FireActionForm.java +++ b/dotCMS/src/main/java/com/dotcms/workflow/form/FireActionForm.java @@ -5,6 +5,7 @@ import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.databind.annotation.JsonDeserialize; import com.dotcms.rest.api.Validated; +import io.swagger.v3.oas.annotations.media.Schema; import java.util.List; import java.util.Map; @@ -15,24 +16,87 @@ */ @JsonIgnoreProperties(ignoreUnknown = true) @JsonDeserialize(builder = FireActionForm.Builder.class) +@Schema(description = "Form used to fire a workflow action. The content being acted on is supplied in the " + + "'contentlet' map; the remaining fields are workflow/publishing options applied by the action.") public class FireActionForm extends Validated { + @Schema(description = "Optional comment recorded on the workflow task history.") private final String comments; + + @Schema(description = "User or role ID to assign the task to (used by actions that reassign).") private final String assign; + + @Schema(description = "Publish date for the 'Publish' step, in the content type's configured date format.") private final String publishDate; + + @Schema(description = "Publish time for the 'Publish' step, in the content type's configured time format.") private final String publishTime; + + @Schema(description = "Expiration date, in the content type's configured date format.") private final String expireDate; + + @Schema(description = "Expiration time, in the content type's configured time format.") private final String expireTime; + + @Schema(description = "Set to 'true' to mark the content as never expiring.") private final String neverExpire; + + @Schema(description = "Push-publishing target environment(s) for a 'Push Publish' action.") private final String whereToSend; + + @Schema(description = "Push-publishing filter key for a 'Push Publish' action.") private final String filterKey; + + @Schema(description = "Free-text label describing the intent of the action (audit/UI hint).") private final String iWantTo; + + @Schema(description = "Lucene query selecting the content to act on, as an alternative to 'contentlet'.") private final String query; + + @Schema(description = "Target folder path for a 'Move' action.") private final String pathToMove; + + @Schema(description = "Timezone ID (e.g. 'America/New_York') used to interpret the publish/expire date-times.") private final String timezoneId; + + @Schema(description = "Per-content individual permissions to apply, keyed by permission type " + + "(READ, WRITE, PUBLISH, etc.) to a list of user/role IDs.") private final Map<PermissionAPI.Type, List<String>> individualPermissions; @JsonProperty("contentlet") + @Schema(type = "object", description = "The contentlet to create or edit, as a flat map of field-variable " + + "names to values. Polymorphic: the allowed fields depend on the content type.\n\n" + + "**System fields (all content):**\n" + + "- `contentType` *(string)* — the content type's variable name (e.g. 'webPageContent'). Required when creating.\n" + + "- `languageId` *(number)* — language ID; defaults to the system default language when omitted.\n" + + "- `contentHost` *(string)* — host (site) **identifier** the content belongs to, **or** `hostFolder` " + + "*(string)* — a folder **identifier**. Supply one of these.\n" + + "- `inode` / `identifier` *(string)* — include the existing identifier (and optionally inode) to edit " + + "existing content; omit both to create new.\n\n" + + "⚠️ Do **not** set `host` — use `contentHost` (host id) or `hostFolder` (folder id) instead.\n\n" + + "**Host defaulting (common wrong-host trap):** if you omit `contentHost`/`hostFolder` when creating, " + + "the content does NOT go to the current/default site — it inherits the **content type's own host**, " + + "which is `SYSTEM_HOST` unless the type was explicitly created on a site. So content of a " + + "`SYSTEM_HOST` content type silently lands on `SYSTEM_HOST`, where it is invisible to another site's " + + "URL-maps and to host-scoped searches (`+conHost:<siteId>`). Always pass `contentHost:<siteId>` " + + "(the site **identifier UUID**, not the hostname) to place content on a specific site.\n\n" + + "**Constant fields are content-type configuration, not contentlet data.** Fields whose `clazz` is " + + "`ImmutableConstantField` are shared by the content type. Keys for them in this `contentlet` map are " + + "silently ignored even when the workflow response is HTTP 200/live. For example, a WIDGET's " + + "`widgetCode` must be written to the field's `values` property via the content-type field API, not " + + "included in a widget contentlet.\n\n" + + "**Pages (`contentType: 'htmlpageasset'`)** additionally use:\n" + + "- `title` *(string)* — the page title.\n" + + "- `url` *(string)* — the page name (the last URL segment) within `hostFolder`.\n" + + "- `template` *(string)* — identifier of the template to render the page.\n" + + "- `cachettl` *(number)* — page cache time-to-live in seconds.\n" + + "- `sortOrder` *(number)* — sort order within the folder.\n\n" + + "Note: there is no dedicated page-create endpoint — pages are created by firing an action with an " + + "`htmlpageasset` contentlet.", + example = "{\"contentType\":\"htmlpageasset\",\"languageId\":1," + + "\"hostFolder\":\"48190c8c-42c4-46af-8d1a-0cd5db894797\"," + + "\"title\":\"My Page\",\"url\":\"my-page\"," + + "\"template\":\"8e63a9c0-...\",\"cachettl\":15,\"sortOrder\":0}") private final Map<String, Object> contentletFormData; public String getComments() { diff --git a/dotCMS/src/main/java/com/dotmarketing/portlets/templates/design/bean/ContainerUUID.java b/dotCMS/src/main/java/com/dotmarketing/portlets/templates/design/bean/ContainerUUID.java index aa0723121dbf..4dc52346742b 100644 --- a/dotCMS/src/main/java/com/dotmarketing/portlets/templates/design/bean/ContainerUUID.java +++ b/dotCMS/src/main/java/com/dotmarketing/portlets/templates/design/bean/ContainerUUID.java @@ -5,6 +5,7 @@ import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; +import io.swagger.v3.oas.annotations.media.Schema; import java.io.Serializable; import java.util.ArrayList; @@ -27,6 +28,21 @@ public class ContainerUUID implements Serializable{ public static final String UUID_START_VALUE = "1"; public static final String UUID_DEFAULT_VALUE = "-1"; + @Schema(description = + "Reference to the Container placed in this layout slot. Accepts any ONE of three forms:\n" + + "- a Container **identifier** for a database-backed Container — a full UUID " + + "('2cef9f97-5faf-4d18-8c9b-df22b6c17111') or a dotCMS 'shorty' (short) id; or\n" + + "- a **file path** for a file-based (Container-as-File) Container — host-qualified " + + "('//demo.dotcms.com/application/containers/default/') or host-relative " + + "('/application/containers/default/', resolved against the current site); or\n" + + "- the literal string 'SYSTEM_CONTAINER' for the built-in system Container.\n" + + "The server chooses the resolution strategy by inspecting the value: a string containing " + + "'/application/containers' is resolved as a file-path Container, 'SYSTEM_CONTAINER' resolves to " + + "the system Container, and anything else is looked up as a database identifier. A value that does " + + "not resolve to an existing Container produces no container at render time (the slot renders " + + "empty) rather than falling back to another Container — pass the exact identifier or the full " + + "host-qualified path.", + example = "//demo.dotcms.com/application/containers/default/") private final String identifier; private String uuid; diff --git a/dotCMS/src/main/webapp/WEB-INF/messages/Language.properties b/dotCMS/src/main/webapp/WEB-INF/messages/Language.properties index 6c979ff2ddc7..65d1aa3d2c2c 100644 --- a/dotCMS/src/main/webapp/WEB-INF/messages/Language.properties +++ b/dotCMS/src/main/webapp/WEB-INF/messages/Language.properties @@ -1819,6 +1819,8 @@ velocityPlayground.output.timing=ms velocityPlayground.output.empty=Run a snippet to see results velocityPlayground.output.empty.hint=Enter a Velocity snippet in the editor and press Run to evaluate. velocityPlayground.error.unknown=Velocity evaluation failed. +velocityPlayground.error.location=Line {0}, column {1} +velocityPlayground.warnings.summary={0} warning(s) — the script ran, but check for typos: velocityPlayground.unlicensed.title=Velocity Playground is an Enterprise feature velocityPlayground.unlicensed.description=An Enterprise license is required to evaluate Velocity snippets from this tool. velocityPlayground.help.title=Velocity examples @@ -7516,21 +7518,29 @@ accessibility.studio.working=WORKING accessibility.studio.scanning=Scanning page… accessibility.studio.fixing=Fixing accessibility issues… accessibility.studio.error.title=The agent run could not complete +# Live "working" bubble — reassurance copy shown while the agent is thinking +# between steps; cycled as the current action keeps running (heartbeat), so a long +# quiet step doesn't look hung. {0} = elapsed seconds on the current action. +accessibility.studio.working.thinking=Thinking… +accessibility.studio.working.analyzing=Analyzing the page… +accessibility.studio.working.reasoning=Working through the fix… +accessibility.studio.working.stillworking=Still working on it… +accessibility.studio.working.elapsed={0}s # Picker -accessibility.studio.picker.subtitle=Pick a page to scan and fix accessibility issues. -accessibility.studio.picker.search.placeholder=Search pages by title or path… -accessibility.studio.picker.count=Showing {0} of {1} pages -accessibility.studio.picker.sorted=Sorted by last edited -accessibility.studio.picker.col.title=Title -accessibility.studio.picker.col.url=URL -accessibility.studio.picker.col.type=Type -accessibility.studio.picker.col.status=Status -accessibility.studio.picker.col.edited=Last Edited -accessibility.studio.picker.status.published=Published -accessibility.studio.picker.status.draft=Draft -accessibility.studio.picker.empty.title=No pages found -accessibility.studio.picker.empty.description=Refine your search to find a page to scan. -accessibility.studio.picker.hint=Opening a page lets you scan it for accessibility issues. +accessibility.studio.pagelist.subtitle=Pick a page to scan and fix accessibility issues. +accessibility.studio.pagelist.search.placeholder=Search pages by title or path… +accessibility.studio.pagelist.count=Showing {0} of {1} pages +accessibility.studio.pagelist.sorted=Sorted by last edited +accessibility.studio.pagelist.col.title=Title +accessibility.studio.pagelist.col.url=URL +accessibility.studio.pagelist.col.type=Type +accessibility.studio.pagelist.col.status=Status +accessibility.studio.pagelist.col.edited=Last Edited +accessibility.studio.pagelist.status.published=Published +accessibility.studio.pagelist.status.draft=Draft +accessibility.studio.pagelist.empty.title=No pages found +accessibility.studio.pagelist.empty.description=Refine your search to find a page to scan. +accessibility.studio.pagelist.hint=Opening a page lets you scan it for accessibility issues. # Score widget accessibility.studio.score.open=open accessibility.studio.score.toscan=to scan @@ -7579,7 +7589,7 @@ accessibility.studio.footer.scanning.sub=Running the accessibility check against accessibility.studio.footer.scanned.title={0} issues ready to fix accessibility.studio.footer.scanned.sub=The agent fixes what it can and reports anything left. accessibility.studio.footer.fixing.title={0} fixed to working so far -accessibility.studio.footer.fixing.sub=Stop any time — fixes already applied are kept. +accessibility.studio.footer.fixing.sub=Stop any time — fixes already saved as draft. accessibility.studio.footer.done.title={0} fixed to working · {1} need attention accessibility.studio.footer.done.sub=Review the fixes, then publish or discard the batch. accessibility.studio.footer.published.title=Fixes published to live @@ -7594,7 +7604,12 @@ accessibility.studio.action.fixing=Working… accessibility.studio.action.stopagent=Stop agent accessibility.studio.action.discard=Discard accessibility.studio.action.publish=Publish {0} fixes +accessibility.studio.action.apply=Publish page +accessibility.studio.action.reviewfiles=Review files accessibility.studio.action.allpages=All pages +# Side panel accordion +accessibility.studio.panel.scanner=Accessibility scan +accessibility.studio.panel.files=Files changed # Preview pane accessibility.studio.preview.label=Preview accessibility.studio.preview.mode.label=Page version @@ -7603,6 +7618,27 @@ accessibility.studio.preview.mode.live=Live (published) accessibility.studio.legend.detected=Detected accessibility.studio.legend.fixed=Fixed accessibility.studio.legend.attention=Needs attention +# Working vs live file diff +accessibility.studio.diff.fileschanged=Files changed +accessibility.studio.diff.backtopreview=Back to preview +accessibility.studio.diff.working=Working +accessibility.studio.diff.live=Live +accessibility.studio.diff.loading=Loading file changes… +accessibility.studio.diff.empty.title=No files changed +accessibility.studio.diff.empty.sub=This page has no unpublished source-file changes. +accessibility.studio.diff.error.title=Couldn't load file changes +accessibility.studio.diff.error.sub=Something went wrong resolving this page's source files. Try again. + +# AI Agents shell +agents.landing.title=AI Agents +agents.landing.subtitle=Pick an agent to get started. Each one automates a task across your content. +agents.status.coming-soon=Coming soon +agents.a11y.label=Accessibility Studio +agents.a11y.description=Scan a page and automatically fix accessibility issues. +agents.geo-fixer.label=Geo Fixer +agents.geo-fixer.description=Localize and adapt content for different regions. +agents.page-builder.label=Page Builder +agents.page-builder.description=Generate and assemble pages from a prompt. # Plugins portlet plugins.show-system-bundles=Show System Bundles diff --git a/dotCMS/src/main/webapp/WEB-INF/openapi/openapi.yaml b/dotCMS/src/main/webapp/WEB-INF/openapi/openapi.yaml index 099e017443ea..719d42df6240 100644 --- a/dotCMS/src/main/webapp/WEB-INF/openapi/openapi.yaml +++ b/dotCMS/src/main/webapp/WEB-INF/openapi/openapi.yaml @@ -20,6 +20,8 @@ tags: name: Content Type - description: Legacy tag management endpoints (deprecated - use v2 TagResource instead) name: Tag (v1) +- description: Streaming a11y-fix agent proxy + name: Accessibility Agent - description: Endpoints that perform operations related to validating accessibility in content. name: Accessibility Checker @@ -2621,6 +2623,118 @@ paths: summary: Retrieves Accessibility Guidelines tags: - Accessibility Checker + /v1/agents/a11y/active-run: + get: + description: "Returns the run the agent service currently associates with the\ + \ calling user, so a client that reconnects (a reload, or a second tab) can\ + \ rejoin a run already in progress instead of starting a duplicate." + operationId: getA11yAgentActiveRun + responses: + "200": + content: + application/json: {} + description: "The active or last run, relayed from the agent service" + "401": + content: + application/json: {} + description: Authentication required + summary: Get the caller's active or most recent agent run + tags: + - Accessibility Agent + /v1/agents/a11y/fix: + post: + description: "Resolves the page identifier to a live URL, URI and host id, mints\ + \ a short-lived token for the calling user, and forwards the request to the\ + \ configured a11y agent service. Returns the agent's report once the run completes.\ + \ This call is synchronous and a full run can take minutes - use /fix/stream\ + \ to receive progress as it happens. Requires the dotPageScanner-config App\ + \ to carry the agent url and auth token." + operationId: runA11yAgentFix + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/A11yAgentFixForm" + responses: + "200": + content: + application/json: {} + description: "The agent's fix report, relayed verbatim from the agent service" + "400": + content: + application/json: + schema: + $ref: "#/components/schemas/ResponseEntityView" + description: "identifier is missing, or the page could not be resolved" + "401": + content: + application/json: {} + description: Authentication required + "500": + content: + application/json: + schema: + $ref: "#/components/schemas/ResponseEntityView" + description: "The agent App is not configured, or the agent service failed" + summary: Run the accessibility fix agent on a page + tags: + - Accessibility Agent + /v1/agents/a11y/fix/stream: + post: + description: "Same as /fix, but relays the agent's Server-Sent Events as they\ + \ arrive rather than waiting for the run to finish. Frames carry the run id,\ + \ phase steps, progress counts, heartbeats, and a terminal done, aborted or\ + \ error event. A configuration failure is reported as an SSE error frame rather\ + \ than an HTTP status, because the response has already begun." + operationId: streamA11yAgentFix + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/A11yAgentFixForm" + responses: + "200": + content: + text/event-stream: {} + description: SSE stream of agent events (text/event-stream) + "401": + content: + application/json: {} + description: Authentication required + summary: "Run the accessibility fix agent, streaming progress over SSE" + tags: + - Accessibility Agent + /v1/agents/a11y/stop: + post: + description: "Cooperatively stops the run identified by runId. The agent stops\ + \ at its next safe checkpoint and the open /fix/stream connection receives\ + \ a terminal aborted event carrying a partial report - fixes already applied\ + \ are kept. Runs are addressed by runId rather than by caller identity, because\ + \ the proxy mints a fresh token per request." + operationId: stopA11yAgentRun + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/A11yAgentStopForm" + responses: + "202": + content: + application/json: {} + description: "Stop signalled, or no such run was active - both are success" + "400": + content: + application/json: + schema: + $ref: "#/components/schemas/ResponseEntityView" + description: runId is missing + "401": + content: + application/json: {} + description: Authentication required + summary: Stop an in-flight accessibility fix run + tags: + - Accessibility Agent /v1/ai/completions: post: description: Creates AI-powered content summaries and completions based on provided @@ -7757,7 +7871,7 @@ paths: content: application/json: example: - - clazz: com.dotcms.contenttype.model.type.ImmutableSimpleContentType + - clazz: CONTENT defaultType: false name: The Content Type 1 description: THE DESCRIPTION @@ -7772,7 +7886,7 @@ paths: PUBLISH: ceca71a0-deee-4999-bd47-b01baa1bcfc8 workflow: - d61a59e1-a49c-46f2-a929-db2b4bfa88b2 - - clazz: com.dotcms.contenttype.model.type.ImmutableSimpleContentType + - clazz: CONTENT defaultType: false name: The Content Type 2 description: THE DESCRIPTION @@ -7785,12 +7899,12 @@ paths: workflow: - d61a59e1-a49c-46f2-a929-db2b4bfa88b2 schema: - $ref: "#/components/schemas/ContentTypeForm" + $ref: "#/components/schemas/ContentTypeRequestView" description: |- Accepts either a single content-type object or an array. The body is the content-type object directly (not wrapped in a 'contentType' envelope). **Required properties:** - - `clazz` *(string)* — fully-qualified class name. One of: `com.dotcms.contenttype.model.type.ImmutableSimpleContentType`, `com.dotcms.contenttype.model.type.ImmutableWidgetContentType`, `com.dotcms.contenttype.model.type.ImmutableFormContentType`, `com.dotcms.contenttype.model.type.ImmutableFileAssetContentType`, `com.dotcms.contenttype.model.type.ImmutablePageContentType`, `com.dotcms.contenttype.model.type.ImmutablePersonaContentType`, `com.dotcms.contenttype.model.type.ImmutableVanityUrlContentType`, `com.dotcms.contenttype.model.type.ImmutableKeyValueContentType`, `com.dotcms.contenttype.model.type.ImmutableDotAssetContentType` + - `clazz` *(string)* — the base type, as a case-insensitive base-type name: `CONTENT`, `WIDGET`, `FORM`, `FILEASSET`, `HTMLPAGE`, `PERSONA`, `VANITY_URL`, `KEY_VALUE`, or `DOTASSET`. - `name` *(string)* — display name **Common optional properties:** @@ -7799,13 +7913,16 @@ paths: - `folder` *(string)* — folder identifier UUID or the literal `SYSTEM_FOLDER` (defaults to `SYSTEM_FOLDER`) - `description` *(string)* - `workflow` *(array of workflow scheme UUIDs)* — e.g. `["d61a59e1-a49c-46f2-a929-db2b4bfa88b2"]` for System Workflow. ⚠️ **Note:** this is `workflow` (singular) in the request. GET responses return `workflows` (plural, array of objects) — clients round-tripping an object must rename this key. - - `fields` *(array of field objects)* — see field schema below + - `fields` *(array of field objects)* — see field schema below (`ContentTypeFieldView`) - `metadata` *(object)* — known keys: `CONTENT_EDITOR2_ENABLED` (boolean), `DOT_STYLE_EDITOR_SCHEMA` (JSON string) - `systemActionMappings` *(object)* — maps system actions (`NEW`, `EDIT`, `PUBLISH`, `UNPUBLISH`, `ARCHIVE`, `UNARCHIVE`, `DELETE`, `DESTROY`) to workflow action UUIDs + **WIDGET content types:** Creating `clazz: WIDGET` automatically adds `widgetTitle`, `widgetUsage`, `widgetCode`, and `widgetPreexecute`. `widgetCode` is an `ImmutableConstantField`; set the field's `values` property on the content type. Putting `widgetCode` in a workflow contentlet body is silently ignored. + **Field object schema** (each item in `fields[]`): - - `clazz` *(string, required)* — e.g. `com.dotcms.contenttype.model.field.ImmutableTextField`, `ImmutableTextAreaField`, `ImmutableStoryBlockField`, `ImmutableBinaryField`, `ImmutableTagField`, `ImmutableRadioField`, `ImmutableSelectField`, `ImmutableDateField`, `ImmutableDateTimeField`, `ImmutableRowField` *(layout marker)*, `ImmutableColumnField` *(layout marker)* + - `clazz` *(string, required)* — the field type as a case-insensitive short name: `TEXT`, `TEXT_AREA`, `STORY_BLOCK_FIELD`, `WYSIWYG`, `BINARY`, `IMAGE`, `FILE`, `TAG`, `CATEGORY`, `CHECKBOX`, `RADIO`, `SELECT`, `MULTI_SELECT`, `DATE`, `TIME`, `DATE_TIME`, `KEY_VALUE`, `JSON_FIELD`, `CONSTANT`, `HIDDEN`, `CUSTOM_FIELD`, `RELATIONSHIP`, `ROW_FIELD` *(layout marker)*, `COLUMN_FIELD` *(layout marker)*. The fully-qualified `Immutable*` class name is also accepted. - `name`, `variable`, `dataType` (one of `TEXT`, `LONG_TEXT`, `SYSTEM`, `BOOL`, `INTEGER`, `FLOAT`, `DATE`), `required`, `indexed`, `listed`, `sortOrder` *(integer, position in the fields array)* + - ⚠️ **`dataType` is the storage type, not the UI type.** Asset-reference fields — `ImmutableImageField`, `ImmutableFileField`, `ImmutableBinaryField` — use `dataType: TEXT` (they store a reference in a text column), **never** `SYSTEM`. Reserve `SYSTEM` for true layout/tab/relationship system fields (rows, columns, dividers, permission/relationship tabs). - `values` *(string)* — for Radio/Select/Checkbox: newline-separated `Display|value` pairs. For a boolean field use `ImmutableRadioField` + `dataType: BOOL` + `values: 'True|true\r\nFalse|false'` — there is no dedicated Boolean field class. **Layout encoding:** Rows and columns are regular field entries placed in `fields[]`. `ImmutableRowField` begins a new row; `ImmutableColumnField` begins a new column inside that row; following content fields belong to the most-recent column until the next marker. @@ -8128,7 +8245,7 @@ paths: workflow: - d61a59e1-a49c-46f2-a929-db2b4bfa88b2 schema: - $ref: "#/components/schemas/ContentTypeForm" + $ref: "#/components/schemas/ContentTypeRequestView" description: |- The minimum required properties for a successful update are `clazz`, `id`, and `name`. @@ -8678,8 +8795,13 @@ paths: content: application/json: schema: - type: string - description: Field JSON data + $ref: "#/components/schemas/ContentTypeFieldView" + description: "A SINGLE field object to create on the content type. This endpoint\ + \ creates exactly one field: if you pass a JSON array, only the first element\ + \ is saved (it returns 200 and silently drops the rest). To add multiple\ + \ fields at once, use PUT /api/v1/contenttype/{typeId}/fields (which takes\ + \ an array), or include them inline as the 'fields' array when creating\ + \ the content type via POST /api/v1/contenttype." required: true responses: "200": @@ -8722,8 +8844,10 @@ paths: content: application/json: schema: - type: string - description: Fields JSON data + type: array + items: + $ref: "#/components/schemas/ContentTypeFieldView" + description: Array of field objects to save on the content type. required: true responses: "200": @@ -10544,12 +10668,17 @@ paths: description: Retrieves a folder by its URI path within the specified site. operationId: loadFolderByURI parameters: - - in: path + - description: Site hostname the folder lives on (e.g. 'demo.dotcms.com'). + in: path name: siteName required: true schema: type: string - - in: path + - description: "Folder path within the site, as a plain path — e.g. 'application/themes/travel'\ + \ (a leading slash is optional and added if missing). Embedded slashes are\ + \ allowed (they select nested folders). Pass the raw path; do NOT percent-encode\ + \ the slashes (a pre-encoded '%2F...' will not match)." + in: path name: uri required: true schema: @@ -12791,20 +12920,27 @@ paths: - Page /v1/page/json/{uri}: get: - description: "Returns the metadata (the objects that make up an HTML Page) in\ - \ JSON format based on the specified URI. If the URI maps to a Vanity URL,\ - \ a 200 Forward returns the actual page metadata, while a 301/302 redirect\ - \ returns an empty page JSON with the Vanity URL properties. Supports Time\ - \ Machine via the publishDate parameter (ISO 8601 format)." + description: |- + Returns the metadata (the objects that make up an HTML Page) in JSON format based on the specified URI. If the URI maps to a Vanity URL, a 200 Forward returns the actual page metadata, while a 301/302 redirect returns an empty page JSON with the Vanity URL properties. Supports Time Machine via the publishDate parameter (ISO 8601 format). + + The URI must be a plain page path (no embedded host). To read a page on a NON-default site, pass the `host_id` query parameter (backend users only); without it the current/default site is used. The `//host/uri` path form is NOT supported. operationId: getPageJsonByUri parameters: - - description: "Path to the HTML Page or Vanity URL (e.g., 'about-us/locations/index')" + - description: "Path to the HTML Page or Vanity URL (e.g., 'about-us/locations/index').\ + \ Must be a plain path with no embedded host; use host_id to target a specific\ + \ site." in: path name: uri required: true schema: type: string pattern: .* + - description: "Explicit site to read against, given as a host identifier (UUID).\ + \ Backend users only; if omitted the current/default site is used." + in: query + name: host_id + schema: + type: string - description: "Page mode for rendering (e.g., EDIT_MODE, PREVIEW_MODE, LIVE)" in: query name: mode @@ -12878,22 +13014,27 @@ paths: - Page /v1/page/render/{uri}: get: - description: "Returns the metadata of an HTML Page including its rendered HTML\ - \ code and container content in JSON format based on the specified URI. If\ - \ the URI maps to a Vanity URL, a 200 Forward returns the actual rendered\ - \ page, while a 301/302 redirect returns an empty page JSON with Vanity URL\ - \ properties. Supports Time Machine via the publishDate parameter (ISO 8601\ - \ format). For EMA (Enterprise Marketing Automation) requests, this may delegate\ - \ to the JSON endpoint when the rendered attribute is not required." + description: |- + Returns the metadata of an HTML Page including its rendered HTML code and container content in JSON format based on the specified URI. If the URI maps to a Vanity URL, a 200 Forward returns the actual rendered page, while a 301/302 redirect returns an empty page JSON with Vanity URL properties. Supports Time Machine via the publishDate parameter (ISO 8601 format). For EMA (Enterprise Marketing Automation) requests, this may delegate to the JSON endpoint when the rendered attribute is not required. + + The URI must be a plain page path (no embedded host). To render a page on a NON-default site, pass the `host_id` query parameter (backend users only); without it the current/default site is used. The `//host/uri` path form is NOT supported. operationId: getPageRenderByUri parameters: - - description: "Path to the HTML Page or Vanity URL (e.g., 'about-us/locations/index')" + - description: "Path to the HTML Page or Vanity URL (e.g., 'about-us/locations/index').\ + \ Must be a plain path with no embedded host; use host_id to target a specific\ + \ site." in: path name: uri required: true schema: type: string pattern: .* + - description: "Explicit site to render against, given as a host identifier\ + \ (UUID). Backend users only; if omitted the current/default site is used." + in: query + name: host_id + schema: + type: string - description: "Page mode for rendering (e.g., EDIT_MODE, PREVIEW_MODE, LIVE)" in: query name: mode @@ -12940,18 +13081,26 @@ paths: - Page /v1/page/renderHTML/{uri}: get: - description: Returns the rendered HTML content of a page without the JSON metadata - wrapper. Useful for retrieving the raw HTML output of a page for embedding - or server-side rendering. + description: |- + Returns the rendered HTML content of a page without the JSON metadata wrapper. Useful for retrieving the raw HTML output of a page for embedding or server-side rendering. + + The page is identified by a plain URI path (e.g. `index`, `about/team`). To render a page on a NON-default site, pass the `host_id` query parameter (backend users only); without it the current/default site is used. The `//host/uri` path form is NOT supported — the URI must not embed a host. operationId: renderPageHtmlOnly parameters: - - description: Path to the HTML Page to render + - description: Plain page URI path (e.g. 'index' or 'about/team'). Must not + embed a host; use host_id to target a specific site. in: path name: uri required: true schema: type: string pattern: .* + - description: "Explicit site to render against, given as a host identifier\ + \ (UUID). Backend users only; if omitted the current/default site is used." + in: query + name: host_id + schema: + type: string - description: "Page mode for rendering (default: LIVE_ADMIN)" in: query name: mode @@ -17186,10 +17335,12 @@ paths: tags: - Template post: - description: "Creates a new working version of a template. The 'theme' field\ - \ in the form corresponds to the theme folder identifier (referred to as 'themeId'\ - \ in other endpoints). If a layout is provided, the template is saved as a\ - \ designed (drawed) template with its layout." + description: |- + Creates a new working version of a template. The 'theme' field in the form corresponds to the theme folder identifier (referred to as 'themeId' in other endpoints). If a layout is provided, the template is saved as a designed (drawed) template with its layout. + + When `drawed` is true (a layout-designer template): `body` is REQUIRED and must be non-empty (a null body returns 400 'body required when drawed'), and `theme` MUST resolve to a theme **folder** identifier — a host id or other non-folder id returns 400 'theme must be a folder identifier'. Provide `drawedBody` (the layout JSON) as well so the template is a real drawn template. + + For a themed drawn template, `body` is a generated compatibility shell and is not the render source; rendering flows through the theme's `template.vtl` and `drawedBody`. The stored shell may contain `/themes/null/` even when `theme` and `themeName` are correct. That value is benign for this template kind, and PUT-updating `body` will only cause the shell to be regenerated. operationId: createTemplate requestBody: content: @@ -17217,10 +17368,12 @@ paths: tags: - Template put: - description: Saves a new working version of an existing template. The form must - contain the template identifier. The 'theme' field in the form corresponds - to the theme folder identifier (referred to as 'themeId' in other endpoints). - Returns 404 if the template does not exist. + description: |- + Saves a new working version of an existing template. The form must contain the template identifier. The 'theme' field in the form corresponds to the theme folder identifier (referred to as 'themeId' in other endpoints). Returns 404 if the template does not exist. + + When `drawed` is true: `body` is REQUIRED and non-empty (else 400 'body required when drawed'), and `theme` MUST resolve to a theme **folder** identifier (else 400 'theme must be a folder identifier'). Include `drawedBody` (the layout JSON) so the template stays a real drawn template. + + For a themed drawn template, `body` is a generated compatibility shell and is not used to assemble the rendered page; the theme's `template.vtl` and `drawedBody` are authoritative. A persisted `/themes/null/` reference in that shell is benign, and changing `body` does not repair or affect themed drawn rendering because the server regenerates it. operationId: updateTemplate requestBody: content: @@ -18654,6 +18807,8 @@ paths: Fire a [default system action](https://www.dotcms.com/docs/latest/managing-workflows#DefaultActions) by name on multiple target contentlets. Returns a list of resultant contentlet maps, each with an additional `AUTO_ASSIGN_WORKFLOW` property, which can be referenced by delegate services that handle automatically assigning workflow schemes to content with none. + + This is the **multi-contentlet** variant and returns a list envelope (`entity.results[]`). To fire on a **single** contentlet, use `PUT` on this same path instead — it returns the single resultant contentlet map (with its `identifier`) directly. operationId: postFireSystemActionByNameMulti parameters: - description: Default system action. @@ -18775,6 +18930,8 @@ paths: Returns a map of the resultant contentlet, with an additional `AUTO_ASSIGN_WORKFLOW` property, which can be referenced by delegate services that handle automatically assigning workflow schemes to content with none. + **Use `PUT` for a single contentlet.** This path also accepts `POST`, but that is a **different** operation that fires over *multiple* contentlets and returns a different envelope (`entity.results[]`, a list). Sending a single-contentlet body via `POST` will not return the created contentlet's `identifier` where you expect it, even though the record may still be (half-)created by the content type's default workflow — a common silent trap. For one item, always use `PUT`. + **Request body** — wrap field values in a `contentlet` key: ```json @@ -20121,8 +20278,10 @@ paths: - Workflow /v1/workflow/contenttypes/{contentTypeVarOrId}/system/actions: get: - description: "Returns a list of [default system actions](https://www.dotcms.com/docs/latest/managing-workflows#DefaultActions)\ - \ associated with a specified [content type](https://www.dotcms.com/docs/latest/content-types)." + description: |- + Returns a list of [default system actions](https://www.dotcms.com/docs/latest/managing-workflows#DefaultActions) associated with a specified [content type](https://www.dotcms.com/docs/latest/content-types). + + An empty list means only that no *default system-action mappings* (e.g. NEW, PUBLISH) are configured for this content type — it does **not** mean the content type lacks a workflow or that publishing will fail. You can still fire actions on its content by ID via `PUT /api/v1/workflow/actions/{actionId}/fire`, or fire a default system action via `PUT /api/v1/workflow/actions/default/fire/{systemAction}` (which resolves the action from the scheme attached to the content type). Do not treat an empty response as a blocker. operationId: getSystemActionMappingsByContentType parameters: - description: |- @@ -22930,40 +23089,59 @@ paths: - Tags /v3/contenttype/{typeIdOrVarName}/fields: delete: - operationId: deleteFields_1 + description: Deletes one or more fields from a Content Type and returns the + updated field layout together with the IDs that were actually deleted. A field + being used as the Content Type's Publish or Expire date field cannot be deleted + until it is unlinked. + operationId: deleteContentTypeFields parameters: - - in: path + - description: The ID or Velocity Variable Name of the Content Type. + in: path name: typeIdOrVarName required: true schema: type: string requestBody: content: - '*/*': + application/json: schema: $ref: "#/components/schemas/DeleteFieldsForm" + description: The IDs of the fields to delete. + required: true responses: - default: - content: - application/javascript: {} - application/json: {} - description: default response + "200": + description: Fields deleted; returns the new layout and deleted IDs + "401": + description: Unauthorized access + "404": + description: Content Type not found + "500": + description: Internal Server Error + summary: Deletes fields from a Content Type tags: - Content Type Field get: - operationId: getContentTypeFields_1 + description: "Returns the Content Type's current field layout (rows, columns\ + \ and the fields within them). If the layout is invalid it is repaired before\ + \ being returned; this endpoint does not modify data in the database." + operationId: getContentTypeFieldLayout parameters: - - in: path + - description: The ID or Velocity Variable Name of the Content Type. + in: path name: typeIdOrVarName required: true schema: type: string responses: - default: - content: - application/javascript: {} - application/json: {} - description: default response + "200": + description: The Content Type's field layout + "401": + description: Unauthorized access + "404": + description: Content Type not found + "500": + description: Internal Server Error + summary: Gets a Content Type's field layout tags: - Content Type Field /v3/contenttype/{typeIdOrVarName}/fields/allfields: @@ -23180,8 +23358,10 @@ paths: - Content Type Field /v3/contenttype/{typeIdOrVarName}/fields/{id}: put: - description: "Updates a field in a Content Type. The request body must have\ - \ the follow syntax:" + description: "Updates a field in a Content Type. The request body is wrapped\ + \ in a required top-level `field` property. To update a constant field such\ + \ as a WIDGET's `widgetCode`, fetch the existing field, preserve its attributes,\ + \ change `values`, and send `{\"field\":{...}}`." operationId: updateContentTypeField parameters: - description: The ID or Velocity Variable Name of the Content Type that the @@ -23200,9 +23380,19 @@ paths: requestBody: content: application/json: + example: + field: + id: <widgetCode-field-id> + contentTypeId: <widget-content-type-id> + clazz: com.dotcms.contenttype.model.field.ImmutableConstantField + name: Widget Code + variable: widgetCode + dataType: LONG_TEXT + values: "#dotParse('/application/vtl/widget.vtl')" schema: - $ref: "#/components/schemas/UpdateFieldForm" - description: The object containing the updated attributes of the Field. + $ref: "#/components/schemas/UpdateFieldRequestView" + description: Field update wrapper. The top-level `field` property is required. + required: true responses: "200": content: @@ -23335,7 +23525,19 @@ paths: - Content Type Field /vtl/dynamic: get: - operationId: dynamicGet_2 + description: |- + Evaluates Velocity (VTL) code supplied directly in the request body — no `.vtl` file on disk is required — and returns the result. The code is read from a `velocity` property of the JSON body (properly escaped), or the body may be the raw VTL itself. + + The caller requires the **Scripting Developer** role. + + **Response shape** is decided by the submitted code: + - If the code populates `$dotJSON` (e.g. `$dotJSON.put("key", ...)`), the response is that JSON object. + - Otherwise the raw evaluated output is returned, with the content type set by the script (defaults to `text/plain`). + + **Velocity errors** (syntax/parse errors, method-invocation failures, missing resources) are reported as a `400` with a structured body so an automated caller can locate and fix the offending code instead of receiving partial output. Application-level errors set by the script via `$dotJSON.put("errors", ...)` are also returned as `400`. + + **Warnings** — dotCMS evaluates Velocity in non-strict mode, so an undefined reference (`$noSuchVar`) renders as literal text and a method returning `null` produces no output. These likely-typos are collected and, on a successful response, returned in the `X-Dot-Velocity-Warnings` header (a JSON array); on a `400` they appear in the `warnings` field of the body. + operationId: dynamicGetNoPath requestBody: content: application/json: @@ -23345,16 +23547,36 @@ paths: schema: type: string responses: - default: + "200": + description: Velocity evaluated successfully; body is the raw output or + the JSON object produced by the script + "400": content: - application/json: {} - application/xml: {} - text/plain: {} - description: default response + application/json: + schema: + $ref: "#/components/schemas/VelocityErrorResponseView" + description: "The submitted Velocity failed to parse or evaluate, or the\ + \ script reported errors. The body carries the Velocity error detail (message,\ + \ error type, and line/column when available)." + "403": + description: User lacks the Scripting Developer role + summary: Evaluate inline Velocity code (GET) tags: - Templates post: - operationId: dynamicPost_2 + description: |- + Evaluates Velocity (VTL) code supplied directly in the request body — no `.vtl` file on disk is required — and returns the result. The code is read from a `velocity` property of the JSON body (properly escaped), or the body may be the raw VTL itself. + + The caller requires the **Scripting Developer** role. + + **Response shape** is decided by the submitted code: + - If the code populates `$dotJSON` (e.g. `$dotJSON.put("key", ...)`), the response is that JSON object. + - Otherwise the raw evaluated output is returned, with the content type set by the script (defaults to `text/plain`). + + **Velocity errors** (syntax/parse errors, method-invocation failures, missing resources) are reported as a `400` with a structured body so an automated caller can locate and fix the offending code instead of receiving partial output. Application-level errors set by the script via `$dotJSON.put("errors", ...)` are also returned as `400`. + + **Warnings** — dotCMS evaluates Velocity in non-strict mode, so an undefined reference (`$noSuchVar`) renders as literal text and a method returning `null` produces no output. These likely-typos are collected and, on a successful response, returned in the `X-Dot-Velocity-Warnings` header (a JSON array); on a `400` they appear in the `warnings` field of the body. + operationId: dynamicPostNoPath requestBody: content: application/json: @@ -23364,16 +23586,36 @@ paths: schema: type: string responses: - default: + "200": + description: Velocity evaluated successfully; body is the raw output or + the JSON object produced by the script + "400": content: - application/json: {} - application/xml: {} - text/plain: {} - description: default response + application/json: + schema: + $ref: "#/components/schemas/VelocityErrorResponseView" + description: "The submitted Velocity failed to parse or evaluate, or the\ + \ script reported errors. The body carries the Velocity error detail (message,\ + \ error type, and line/column when available)." + "403": + description: User lacks the Scripting Developer role + summary: Evaluate inline Velocity code (POST) tags: - Templates put: - operationId: dynamicPut_2 + description: |- + Evaluates Velocity (VTL) code supplied directly in the request body — no `.vtl` file on disk is required — and returns the result. The code is read from a `velocity` property of the JSON body (properly escaped), or the body may be the raw VTL itself. + + The caller requires the **Scripting Developer** role. + + **Response shape** is decided by the submitted code: + - If the code populates `$dotJSON` (e.g. `$dotJSON.put("key", ...)`), the response is that JSON object. + - Otherwise the raw evaluated output is returned, with the content type set by the script (defaults to `text/plain`). + + **Velocity errors** (syntax/parse errors, method-invocation failures, missing resources) are reported as a `400` with a structured body so an automated caller can locate and fix the offending code instead of receiving partial output. Application-level errors set by the script via `$dotJSON.put("errors", ...)` are also returned as `400`. + + **Warnings** — dotCMS evaluates Velocity in non-strict mode, so an undefined reference (`$noSuchVar`) renders as literal text and a method returning `null` produces no output. These likely-typos are collected and, on a successful response, returned in the `X-Dot-Velocity-Warnings` header (a JSON array); on a `400` they appear in the `warnings` field of the body. + operationId: dynamicPutNoPath requestBody: content: application/json: @@ -23383,16 +23625,36 @@ paths: schema: type: string responses: - default: + "200": + description: Velocity evaluated successfully; body is the raw output or + the JSON object produced by the script + "400": content: - application/json: {} - application/xml: {} - text/plain: {} - description: default response + application/json: + schema: + $ref: "#/components/schemas/VelocityErrorResponseView" + description: "The submitted Velocity failed to parse or evaluate, or the\ + \ script reported errors. The body carries the Velocity error detail (message,\ + \ error type, and line/column when available)." + "403": + description: User lacks the Scripting Developer role + summary: Evaluate inline Velocity code (PUT) tags: - Templates /vtl/dynamic/{pathParam}: delete: + description: |- + Evaluates Velocity (VTL) code supplied directly in the request body — no `.vtl` file on disk is required — and returns the result. The code is read from a `velocity` property of the JSON body (properly escaped), or the body may be the raw VTL itself. + + The caller requires the **Scripting Developer** role. + + **Response shape** is decided by the submitted code: + - If the code populates `$dotJSON` (e.g. `$dotJSON.put("key", ...)`), the response is that JSON object. + - Otherwise the raw evaluated output is returned, with the content type set by the script (defaults to `text/plain`). + + **Velocity errors** (syntax/parse errors, method-invocation failures, missing resources) are reported as a `400` with a structured body so an automated caller can locate and fix the offending code instead of receiving partial output. Application-level errors set by the script via `$dotJSON.put("errors", ...)` are also returned as `400`. + + **Warnings** — dotCMS evaluates Velocity in non-strict mode, so an undefined reference (`$noSuchVar`) renders as literal text and a method returning `null` produces no output. These likely-typos are collected and, on a successful response, returned in the `X-Dot-Velocity-Warnings` header (a JSON array); on a `400` they appear in the `warnings` field of the body. operationId: dynamicDelete_1 parameters: - in: path @@ -23410,16 +23672,36 @@ paths: schema: type: string responses: - default: + "200": + description: Velocity evaluated successfully; body is the raw output or + the JSON object produced by the script + "400": content: - application/json: {} - application/xml: {} - text/plain: {} - description: default response + application/json: + schema: + $ref: "#/components/schemas/VelocityErrorResponseView" + description: "The submitted Velocity failed to parse or evaluate, or the\ + \ script reported errors. The body carries the Velocity error detail (message,\ + \ error type, and line/column when available)." + "403": + description: User lacks the Scripting Developer role + summary: Evaluate inline Velocity code (DELETE) tags: - Templates get: - operationId: dynamicGet_3 + description: |- + Evaluates Velocity (VTL) code supplied directly in the request body — no `.vtl` file on disk is required — and returns the result. The code is read from a `velocity` property of the JSON body (properly escaped), or the body may be the raw VTL itself. + + The caller requires the **Scripting Developer** role. + + **Response shape** is decided by the submitted code: + - If the code populates `$dotJSON` (e.g. `$dotJSON.put("key", ...)`), the response is that JSON object. + - Otherwise the raw evaluated output is returned, with the content type set by the script (defaults to `text/plain`). + + **Velocity errors** (syntax/parse errors, method-invocation failures, missing resources) are reported as a `400` with a structured body so an automated caller can locate and fix the offending code instead of receiving partial output. Application-level errors set by the script via `$dotJSON.put("errors", ...)` are also returned as `400`. + + **Warnings** — dotCMS evaluates Velocity in non-strict mode, so an undefined reference (`$noSuchVar`) renders as literal text and a method returning `null` produces no output. These likely-typos are collected and, on a successful response, returned in the `X-Dot-Velocity-Warnings` header (a JSON array); on a `400` they appear in the `warnings` field of the body. + operationId: dynamicGet_2 parameters: - in: path name: pathParam @@ -23436,15 +23718,35 @@ paths: schema: type: string responses: - default: + "200": + description: Velocity evaluated successfully; body is the raw output or + the JSON object produced by the script + "400": content: - application/json: {} - application/xml: {} - text/plain: {} - description: default response + application/json: + schema: + $ref: "#/components/schemas/VelocityErrorResponseView" + description: "The submitted Velocity failed to parse or evaluate, or the\ + \ script reported errors. The body carries the Velocity error detail (message,\ + \ error type, and line/column when available)." + "403": + description: User lacks the Scripting Developer role + summary: Evaluate inline Velocity code (GET) tags: - Templates patch: + description: |- + Evaluates Velocity (VTL) code supplied directly in the request body — no `.vtl` file on disk is required — and returns the result. The code is read from a `velocity` property of the JSON body (properly escaped), or the body may be the raw VTL itself. + + The caller requires the **Scripting Developer** role. + + **Response shape** is decided by the submitted code: + - If the code populates `$dotJSON` (e.g. `$dotJSON.put("key", ...)`), the response is that JSON object. + - Otherwise the raw evaluated output is returned, with the content type set by the script (defaults to `text/plain`). + + **Velocity errors** (syntax/parse errors, method-invocation failures, missing resources) are reported as a `400` with a structured body so an automated caller can locate and fix the offending code instead of receiving partial output. Application-level errors set by the script via `$dotJSON.put("errors", ...)` are also returned as `400`. + + **Warnings** — dotCMS evaluates Velocity in non-strict mode, so an undefined reference (`$noSuchVar`) renders as literal text and a method returning `null` produces no output. These likely-typos are collected and, on a successful response, returned in the `X-Dot-Velocity-Warnings` header (a JSON array); on a `400` they appear in the `warnings` field of the body. operationId: dynamicPatch_1 parameters: - in: path @@ -23462,16 +23764,36 @@ paths: schema: type: string responses: - default: + "200": + description: Velocity evaluated successfully; body is the raw output or + the JSON object produced by the script + "400": content: - application/json: {} - application/xml: {} - text/plain: {} - description: default response + application/json: + schema: + $ref: "#/components/schemas/VelocityErrorResponseView" + description: "The submitted Velocity failed to parse or evaluate, or the\ + \ script reported errors. The body carries the Velocity error detail (message,\ + \ error type, and line/column when available)." + "403": + description: User lacks the Scripting Developer role + summary: Evaluate inline Velocity code (PATCH) tags: - Templates post: - operationId: dynamicPost_3 + description: |- + Evaluates Velocity (VTL) code supplied directly in the request body — no `.vtl` file on disk is required — and returns the result. The code is read from a `velocity` property of the JSON body (properly escaped), or the body may be the raw VTL itself. + + The caller requires the **Scripting Developer** role. + + **Response shape** is decided by the submitted code: + - If the code populates `$dotJSON` (e.g. `$dotJSON.put("key", ...)`), the response is that JSON object. + - Otherwise the raw evaluated output is returned, with the content type set by the script (defaults to `text/plain`). + + **Velocity errors** (syntax/parse errors, method-invocation failures, missing resources) are reported as a `400` with a structured body so an automated caller can locate and fix the offending code instead of receiving partial output. Application-level errors set by the script via `$dotJSON.put("errors", ...)` are also returned as `400`. + + **Warnings** — dotCMS evaluates Velocity in non-strict mode, so an undefined reference (`$noSuchVar`) renders as literal text and a method returning `null` produces no output. These likely-typos are collected and, on a successful response, returned in the `X-Dot-Velocity-Warnings` header (a JSON array); on a `400` they appear in the `warnings` field of the body. + operationId: dynamicPost_2 parameters: - in: path name: pathParam @@ -23488,16 +23810,36 @@ paths: schema: type: string responses: - default: + "200": + description: Velocity evaluated successfully; body is the raw output or + the JSON object produced by the script + "400": content: - application/json: {} - application/xml: {} - text/plain: {} - description: default response + application/json: + schema: + $ref: "#/components/schemas/VelocityErrorResponseView" + description: "The submitted Velocity failed to parse or evaluate, or the\ + \ script reported errors. The body carries the Velocity error detail (message,\ + \ error type, and line/column when available)." + "403": + description: User lacks the Scripting Developer role + summary: Evaluate inline Velocity code (POST) tags: - Templates put: - operationId: dynamicPut_3 + description: |- + Evaluates Velocity (VTL) code supplied directly in the request body — no `.vtl` file on disk is required — and returns the result. The code is read from a `velocity` property of the JSON body (properly escaped), or the body may be the raw VTL itself. + + The caller requires the **Scripting Developer** role. + + **Response shape** is decided by the submitted code: + - If the code populates `$dotJSON` (e.g. `$dotJSON.put("key", ...)`), the response is that JSON object. + - Otherwise the raw evaluated output is returned, with the content type set by the script (defaults to `text/plain`). + + **Velocity errors** (syntax/parse errors, method-invocation failures, missing resources) are reported as a `400` with a structured body so an automated caller can locate and fix the offending code instead of receiving partial output. Application-level errors set by the script via `$dotJSON.put("errors", ...)` are also returned as `400`. + + **Warnings** — dotCMS evaluates Velocity in non-strict mode, so an undefined reference (`$noSuchVar`) renders as literal text and a method returning `null` produces no output. These likely-typos are collected and, on a successful response, returned in the `X-Dot-Velocity-Warnings` header (a JSON array); on a `400` they appear in the `warnings` field of the body. + operationId: dynamicPut_2 parameters: - in: path name: pathParam @@ -23514,12 +23856,20 @@ paths: schema: type: string responses: - default: + "200": + description: Velocity evaluated successfully; body is the raw output or + the JSON object produced by the script + "400": content: - application/json: {} - application/xml: {} - text/plain: {} - description: default response + application/json: + schema: + $ref: "#/components/schemas/VelocityErrorResponseView" + description: "The submitted Velocity failed to parse or evaluate, or the\ + \ script reported errors. The body carries the Velocity error detail (message,\ + \ error type, and line/column when available)." + "403": + description: User lacks the Scripting Developer role + summary: Evaluate inline Velocity code (PUT) tags: - Templates /vtl/{folder}: @@ -23835,6 +24185,37 @@ paths: - Administration components: schemas: + A11yAgentFixForm: + type: object + properties: + identifier: + type: string + description: dotCMS content identifier of the page to fix + example: a9f30020-54ef-494e-92ed-645e757171c2 + languageId: + type: integer + format: int32 + default: 1 + description: Language id of the page version to fix + example: 1 + skipCss: + type: boolean + default: false + description: When true the agent fixes only VTL and reports CSS contrast + issues instead of editing stylesheets + example: false + required: + - identifier + A11yAgentStopForm: + type: object + properties: + runId: + type: string + description: Run id returned by /fix (report) or /fix/stream (the `run` + event) + example: r_1f0c2b7d9a4e4c1fb0d5e6a7c8b9d0e1 + required: + - runId AIImageRequestDTO: type: object properties: @@ -25472,6 +25853,13 @@ components: type: string identifier: type: string + description: |- + Reference to the Container placed in this layout slot. Accepts any ONE of three forms: + - a Container **identifier** for a database-backed Container — a full UUID ('2cef9f97-5faf-4d18-8c9b-df22b6c17111') or a dotCMS 'shorty' (short) id; or + - a **file path** for a file-based (Container-as-File) Container — host-qualified ('//demo.dotcms.com/application/containers/default/') or host-relative ('/application/containers/default/', resolved against the current site); or + - the literal string 'SYSTEM_CONTAINER' for the built-in system Container. + The server chooses the resolution strategy by inspecting the value: a string containing '/application/containers' is resolved as a file-path Container, 'SYSTEM_CONTAINER' resolves to the system Container, and anything else is looked up as a database identifier. A value that does not resolve to an existing Container produces no container at render time (the slot renders empty) rather than falling back to another Container — pass the exact identifier or the full host-qualified path. + example: //demo.dotcms.com/application/containers/default/ uuid: type: string ContainerView: @@ -25623,6 +26011,115 @@ components: path: type: string description: Host-qualified path to the VTL file (FILE containers only) + ContentTypeFieldView: + type: object + description: "A single field within a content type's 'fields[]' array. The 'clazz'\ + \ property is the discriminator that selects the concrete field type; the\ + \ remaining properties apply across field types." + properties: + clazz: + type: string + description: "Field type, as a case-insensitive short field-type name (the\ + \ discriminator that selects the concrete field). The fully-qualified\ + \ 'Immutable*' class name and the bare simple class name (e.g. 'TextField')\ + \ are also still accepted, but the short names below are the preferred\ + \ form. Example: \"TEXT\"." + enum: + - TEXT + - TEXT_AREA + - STORY_BLOCK_FIELD + - WYSIWYG + - CONSTANT + - HIDDEN + - CUSTOM_FIELD + - JSON_FIELD + - BINARY + - IMAGE + - FILE + - TAG + - CATEGORY + - CHECKBOX + - RADIO + - SELECT + - MULTI_SELECT + - DATE + - TIME + - DATE_TIME + - KEY_VALUE + - HOST_OR_FOLDER + - RELATIONSHIP + - RELATIONSHIPS_TAB + - PERMISSIONS_TAB + - LINE_DIVIDER + - TAB_DIVIDER + - ROW_FIELD + - COLUMN_FIELD + example: TEXT + contentTypeId: + type: string + description: Identifier of the content type that owns this field. + dataType: + type: string + description: "Storage/column data type backing the field. This is the **storage**\ + \ type, which often differs from the field's UI class. In particular,\ + \ fields that store their payload elsewhere — such as 'ImmutableImageField',\ + \ 'ImmutableFileField', and 'ImmutableBinaryField' — use dataType 'TEXT'\ + \ (they keep an asset reference in a text column), **not** 'SYSTEM'. Reserve\ + \ 'SYSTEM' for true layout/tab/relationship system fields. Use 'LONG_TEXT'\ + \ for text-area/story-block/WYSIWYG content." + enum: + - TEXT + - LONG_TEXT + - SYSTEM + - BOOL + - INTEGER + - FLOAT + - DATE + defaultValue: + type: string + description: Default value applied when content is created. + hint: + type: string + description: Help text shown beneath the field in the editor. + id: + type: string + description: Field identifier. Preserve this when updating an existing field. + indexed: + type: boolean + description: Whether the field is added to the search index. + listed: + type: boolean + description: Whether the field appears in content list/table views. + name: + type: string + description: Display name of the field. + regexCheck: + type: string + description: Regular expression used to validate the field value. + required: + type: boolean + description: Whether a value is required to save content. + sortOrder: + type: integer + format: int32 + description: "Position of the field within the 'fields[]' array (also drives\ + \ row/column layout order)." + unique: + type: boolean + description: Whether the field is unique across content of this type. + values: + type: string + description: "Options for Radio/Select/Checkbox/Multi-Select fields: newline-separated\ + \ 'Display|value' pairs. For a boolean choice use ImmutableRadioField\ + \ + dataType 'BOOL' + values 'True|true\\r\\nFalse|false' (there is no\ + \ dedicated boolean field class)." + variable: + type: string + description: Velocity variable name of the field (unique within the content + type; auto-generated from 'name' if omitted). + required: + - clazz + - name ContentTypeForm: type: object properties: @@ -25640,6 +26137,94 @@ components: type: array items: $ref: "#/components/schemas/WorkflowFormEntry" + ContentTypeRequestView: + type: object + description: "A content-type object, posted directly (NOT wrapped in a 'contentType'\ + \ envelope). The endpoint also accepts an array of these objects to create\ + \ several types in one call. Creating clazz `WIDGET` automatically adds `widgetTitle`,\ + \ `widgetUsage`, `widgetCode`, and `widgetPreexecute`. `widgetCode` is an\ + \ ImmutableConstantField: its shared code belongs in the field's `values`\ + \ property, not in individual widget contentlets." + properties: + clazz: + type: string + description: "Base type of the content type, as a case-insensitive base-type\ + \ name: `CONTENT`, `WIDGET`, `FORM`, `FILEASSET`, `HTMLPAGE`, `PERSONA`,\ + \ `VANITY_URL`, `KEY_VALUE`, or `DOTASSET`. Example: `\"WIDGET\"`." + enum: + - CONTENT + - WIDGET + - FORM + - FILEASSET + - HTMLPAGE + - PERSONA + - VANITY_URL + - KEY_VALUE + - DOTASSET + example: WIDGET + defaultType: + type: boolean + description: Whether this is the default content type. + description: + type: string + description: Description of the content type. + fields: + type: array + description: "Fields that make up the content type, in order. Rows and columns\ + \ are regular entries: 'ImmutableRowField' begins a row, 'ImmutableColumnField'\ + \ begins a column, and subsequent content fields belong to the most-recent\ + \ column." + items: + $ref: "#/components/schemas/ContentTypeFieldView" + fixed: + type: boolean + description: Whether the content type is fixed (system-managed). + folder: + type: string + description: "Folder identifier UUID, or the literal 'SYSTEM_FOLDER' (the\ + \ default)." + host: + type: string + description: "Site identifier UUID this content type lives on, or the literal\ + \ 'SYSTEM_HOST' (defaults to the default site)." + metadata: + type: object + additionalProperties: + type: object + description: "Content-type metadata. Known keys: 'CONTENT_EDITOR2_ENABLED'\ + \ (boolean), 'DOT_STYLE_EDITOR_SCHEMA' (JSON string)." + description: "Content-type metadata. Known keys: 'CONTENT_EDITOR2_ENABLED'\ + \ (boolean), 'DOT_STYLE_EDITOR_SCHEMA' (JSON string)." + name: + type: string + description: Display name of the content type. + system: + type: boolean + description: Whether the content type is a system type. + systemActionMappings: + type: object + additionalProperties: + type: string + description: "Maps system actions (NEW, EDIT, PUBLISH, UNPUBLISH, ARCHIVE,\ + \ UNARCHIVE, DELETE, DESTROY) to workflow action identifiers." + description: "Maps system actions (NEW, EDIT, PUBLISH, UNPUBLISH, ARCHIVE,\ + \ UNARCHIVE, DELETE, DESTROY) to workflow action identifiers." + variable: + type: string + description: "Velocity variable name (unique, alphanumeric, starts with\ + \ a letter; auto-generated from 'name' if omitted)." + workflow: + type: array + description: "Workflow scheme identifiers to associate with the content\ + \ type, e.g. [\"d61a59e1-a49c-46f2-a929-db2b4bfa88b2\"] for the System\ + \ Workflow. NOTE: this key is 'workflow' (singular) in the REQUEST; GET\ + \ responses return 'workflows' (plural, array of objects) — rename the\ + \ key when round-tripping." + items: + type: string + required: + - clazz + - name ContentTypeView: type: object properties: @@ -27182,79 +27767,260 @@ components: type: string assign: type: string + description: User or role ID to assign the task to (used by actions that + reassign). comments: type: string + description: Optional comment recorded on the workflow task history. contentlet: type: object additionalProperties: type: object + description: |- + The contentlet to create or edit, as a flat map of field-variable names to values. Polymorphic: the allowed fields depend on the content type. + + **System fields (all content):** + - `contentType` *(string)* — the content type's variable name (e.g. 'webPageContent'). Required when creating. + - `languageId` *(number)* — language ID; defaults to the system default language when omitted. + - `contentHost` *(string)* — host (site) **identifier** the content belongs to, **or** `hostFolder` *(string)* — a folder **identifier**. Supply one of these. + - `inode` / `identifier` *(string)* — include the existing identifier (and optionally inode) to edit existing content; omit both to create new. + + ⚠️ Do **not** set `host` — use `contentHost` (host id) or `hostFolder` (folder id) instead. + + **Host defaulting (common wrong-host trap):** if you omit `contentHost`/`hostFolder` when creating, the content does NOT go to the current/default site — it inherits the **content type's own host**, which is `SYSTEM_HOST` unless the type was explicitly created on a site. So content of a `SYSTEM_HOST` content type silently lands on `SYSTEM_HOST`, where it is invisible to another site's URL-maps and to host-scoped searches (`+conHost:<siteId>`). Always pass `contentHost:<siteId>` (the site **identifier UUID**, not the hostname) to place content on a specific site. + + **Constant fields are content-type configuration, not contentlet data.** Fields whose `clazz` is `ImmutableConstantField` are shared by the content type. Keys for them in this `contentlet` map are silently ignored even when the workflow response is HTTP 200/live. For example, a WIDGET's `widgetCode` must be written to the field's `values` property via the content-type field API, not included in a widget contentlet. + + **Pages (`contentType: 'htmlpageasset'`)** additionally use: + - `title` *(string)* — the page title. + - `url` *(string)* — the page name (the last URL segment) within `hostFolder`. + - `template` *(string)* — identifier of the template to render the page. + - `cachettl` *(number)* — page cache time-to-live in seconds. + - `sortOrder` *(number)* — sort order within the folder. + + Note: there is no dedicated page-create endpoint — pages are created by firing an action with an `htmlpageasset` contentlet. + example: + cachettl: 15 + contentType: htmlpageasset + hostFolder: 48190c8c-42c4-46af-8d1a-0cd5db894797 + languageId: 1 + sortOrder: 0 + template: 8e63a9c0-... + title: My Page + url: my-page + description: |- + The contentlet to create or edit, as a flat map of field-variable names to values. Polymorphic: the allowed fields depend on the content type. + + **System fields (all content):** + - `contentType` *(string)* — the content type's variable name (e.g. 'webPageContent'). Required when creating. + - `languageId` *(number)* — language ID; defaults to the system default language when omitted. + - `contentHost` *(string)* — host (site) **identifier** the content belongs to, **or** `hostFolder` *(string)* — a folder **identifier**. Supply one of these. + - `inode` / `identifier` *(string)* — include the existing identifier (and optionally inode) to edit existing content; omit both to create new. + + ⚠️ Do **not** set `host` — use `contentHost` (host id) or `hostFolder` (folder id) instead. + + **Host defaulting (common wrong-host trap):** if you omit `contentHost`/`hostFolder` when creating, the content does NOT go to the current/default site — it inherits the **content type's own host**, which is `SYSTEM_HOST` unless the type was explicitly created on a site. So content of a `SYSTEM_HOST` content type silently lands on `SYSTEM_HOST`, where it is invisible to another site's URL-maps and to host-scoped searches (`+conHost:<siteId>`). Always pass `contentHost:<siteId>` (the site **identifier UUID**, not the hostname) to place content on a specific site. + + **Constant fields are content-type configuration, not contentlet data.** Fields whose `clazz` is `ImmutableConstantField` are shared by the content type. Keys for them in this `contentlet` map are silently ignored even when the workflow response is HTTP 200/live. For example, a WIDGET's `widgetCode` must be written to the field's `values` property via the content-type field API, not included in a widget contentlet. + + **Pages (`contentType: 'htmlpageasset'`)** additionally use: + - `title` *(string)* — the page title. + - `url` *(string)* — the page name (the last URL segment) within `hostFolder`. + - `template` *(string)* — identifier of the template to render the page. + - `cachettl` *(number)* — page cache time-to-live in seconds. + - `sortOrder` *(number)* — sort order within the folder. + + Note: there is no dedicated page-create endpoint — pages are created by firing an action with an `htmlpageasset` contentlet. + example: + cachettl: 15 + contentType: htmlpageasset + hostFolder: 48190c8c-42c4-46af-8d1a-0cd5db894797 + languageId: 1 + sortOrder: 0 + template: 8e63a9c0-... + title: My Page + url: my-page expireDate: type: string + description: "Expiration date, in the content type's configured date format." expireTime: type: string + description: "Expiration time, in the content type's configured time format." filterKey: type: string + description: Push-publishing filter key for a 'Push Publish' action. individualPermissions: type: object additionalProperties: type: array + description: "Per-content individual permissions to apply, keyed by permission\ + \ type (READ, WRITE, PUBLISH, etc.) to a list of user/role IDs." items: type: string + description: "Per-content individual permissions to apply, keyed by\ + \ permission type (READ, WRITE, PUBLISH, etc.) to a list of user/role\ + \ IDs." + description: "Per-content individual permissions to apply, keyed by permission\ + \ type (READ, WRITE, PUBLISH, etc.) to a list of user/role IDs." iwantTo: type: string neverExpire: type: string + description: Set to 'true' to mark the content as never expiring. pathToMove: type: string + description: Target folder path for a 'Move' action. publishDate: type: string + description: "Publish date for the 'Publish' step, in the content type's\ + \ configured date format." publishTime: type: string + description: "Publish time for the 'Publish' step, in the content type's\ + \ configured time format." query: type: string + description: "Lucene query selecting the content to act on, as an alternative\ + \ to 'contentlet'." timezoneId: type: string + description: Timezone ID (e.g. 'America/New_York') used to interpret the + publish/expire date-times. whereToSend: type: string + description: Push-publishing target environment(s) for a 'Push Publish' + action. FireActionForm: type: object + description: Form used to fire a workflow action. The content being acted on + is supplied in the 'contentlet' map; the remaining fields are workflow/publishing + options applied by the action. properties: assign: type: string + description: User or role ID to assign the task to (used by actions that + reassign). comments: type: string + description: Optional comment recorded on the workflow task history. contentlet: type: object additionalProperties: type: object + description: |- + The contentlet to create or edit, as a flat map of field-variable names to values. Polymorphic: the allowed fields depend on the content type. + + **System fields (all content):** + - `contentType` *(string)* — the content type's variable name (e.g. 'webPageContent'). Required when creating. + - `languageId` *(number)* — language ID; defaults to the system default language when omitted. + - `contentHost` *(string)* — host (site) **identifier** the content belongs to, **or** `hostFolder` *(string)* — a folder **identifier**. Supply one of these. + - `inode` / `identifier` *(string)* — include the existing identifier (and optionally inode) to edit existing content; omit both to create new. + + ⚠️ Do **not** set `host` — use `contentHost` (host id) or `hostFolder` (folder id) instead. + + **Host defaulting (common wrong-host trap):** if you omit `contentHost`/`hostFolder` when creating, the content does NOT go to the current/default site — it inherits the **content type's own host**, which is `SYSTEM_HOST` unless the type was explicitly created on a site. So content of a `SYSTEM_HOST` content type silently lands on `SYSTEM_HOST`, where it is invisible to another site's URL-maps and to host-scoped searches (`+conHost:<siteId>`). Always pass `contentHost:<siteId>` (the site **identifier UUID**, not the hostname) to place content on a specific site. + + **Constant fields are content-type configuration, not contentlet data.** Fields whose `clazz` is `ImmutableConstantField` are shared by the content type. Keys for them in this `contentlet` map are silently ignored even when the workflow response is HTTP 200/live. For example, a WIDGET's `widgetCode` must be written to the field's `values` property via the content-type field API, not included in a widget contentlet. + + **Pages (`contentType: 'htmlpageasset'`)** additionally use: + - `title` *(string)* — the page title. + - `url` *(string)* — the page name (the last URL segment) within `hostFolder`. + - `template` *(string)* — identifier of the template to render the page. + - `cachettl` *(number)* — page cache time-to-live in seconds. + - `sortOrder` *(number)* — sort order within the folder. + + Note: there is no dedicated page-create endpoint — pages are created by firing an action with an `htmlpageasset` contentlet. + example: + cachettl: 15 + contentType: htmlpageasset + hostFolder: 48190c8c-42c4-46af-8d1a-0cd5db894797 + languageId: 1 + sortOrder: 0 + template: 8e63a9c0-... + title: My Page + url: my-page + description: |- + The contentlet to create or edit, as a flat map of field-variable names to values. Polymorphic: the allowed fields depend on the content type. + + **System fields (all content):** + - `contentType` *(string)* — the content type's variable name (e.g. 'webPageContent'). Required when creating. + - `languageId` *(number)* — language ID; defaults to the system default language when omitted. + - `contentHost` *(string)* — host (site) **identifier** the content belongs to, **or** `hostFolder` *(string)* — a folder **identifier**. Supply one of these. + - `inode` / `identifier` *(string)* — include the existing identifier (and optionally inode) to edit existing content; omit both to create new. + + ⚠️ Do **not** set `host` — use `contentHost` (host id) or `hostFolder` (folder id) instead. + + **Host defaulting (common wrong-host trap):** if you omit `contentHost`/`hostFolder` when creating, the content does NOT go to the current/default site — it inherits the **content type's own host**, which is `SYSTEM_HOST` unless the type was explicitly created on a site. So content of a `SYSTEM_HOST` content type silently lands on `SYSTEM_HOST`, where it is invisible to another site's URL-maps and to host-scoped searches (`+conHost:<siteId>`). Always pass `contentHost:<siteId>` (the site **identifier UUID**, not the hostname) to place content on a specific site. + + **Constant fields are content-type configuration, not contentlet data.** Fields whose `clazz` is `ImmutableConstantField` are shared by the content type. Keys for them in this `contentlet` map are silently ignored even when the workflow response is HTTP 200/live. For example, a WIDGET's `widgetCode` must be written to the field's `values` property via the content-type field API, not included in a widget contentlet. + + **Pages (`contentType: 'htmlpageasset'`)** additionally use: + - `title` *(string)* — the page title. + - `url` *(string)* — the page name (the last URL segment) within `hostFolder`. + - `template` *(string)* — identifier of the template to render the page. + - `cachettl` *(number)* — page cache time-to-live in seconds. + - `sortOrder` *(number)* — sort order within the folder. + + Note: there is no dedicated page-create endpoint — pages are created by firing an action with an `htmlpageasset` contentlet. + example: + cachettl: 15 + contentType: htmlpageasset + hostFolder: 48190c8c-42c4-46af-8d1a-0cd5db894797 + languageId: 1 + sortOrder: 0 + template: 8e63a9c0-... + title: My Page + url: my-page expireDate: type: string + description: "Expiration date, in the content type's configured date format." expireTime: type: string + description: "Expiration time, in the content type's configured time format." filterKey: type: string + description: Push-publishing filter key for a 'Push Publish' action. individualPermissions: type: object additionalProperties: type: array + description: "Per-content individual permissions to apply, keyed by permission\ + \ type (READ, WRITE, PUBLISH, etc.) to a list of user/role IDs." items: type: string + description: "Per-content individual permissions to apply, keyed by\ + \ permission type (READ, WRITE, PUBLISH, etc.) to a list of user/role\ + \ IDs." + description: "Per-content individual permissions to apply, keyed by permission\ + \ type (READ, WRITE, PUBLISH, etc.) to a list of user/role IDs." iwantTo: type: string neverExpire: type: string + description: Set to 'true' to mark the content as never expiring. pathToMove: type: string + description: Target folder path for a 'Move' action. publishDate: type: string + description: "Publish date for the 'Publish' step, in the content type's\ + \ configured date format." publishTime: type: string + description: "Publish time for the 'Publish' step, in the content type's\ + \ configured time format." query: type: string + description: "Lucene query selecting the content to act on, as an alternative\ + \ to 'contentlet'." timezoneId: type: string + description: Timezone ID (e.g. 'America/New_York') used to interpret the + publish/expire date-times. whereToSend: type: string + description: Push-publishing target environment(s) for a 'Push Publish' + action. FireBulkActionsForm: type: object description: "Request body for PUT /api/v1/workflow/contentlet/actions/bulk/fire.\ @@ -29850,6 +30616,10 @@ components: type: boolean PageForm: type: object + description: Layout payload used to create or update the anonymous Template + backing a page. Wrap this object under a top-level 'PageForm' property in + the request body. 'layout' is required; omitting 'title' creates an anonymous + (page-scoped) template. properties: anonymousLayout: type: boolean @@ -29857,10 +30627,19 @@ components: $ref: "#/components/schemas/TemplateLayout" siteId: type: string + description: Identifier of the site (host) the template belongs to. Sent + as the JSON property 'hostId'. Defaults to the site resolved from the + current HTTP request context when omitted. themeId: type: string + description: "Theme folder identifier (not a path) supplying the template's\ + \ CSS/JS and VTL fragments. Resolve from a path via GET /api/v1/folder/sitename/{site}/uri/{uri}." title: type: string + description: "Title for the resulting template. Omit to create an anonymous,\ + \ page-scoped template (a generated name is assigned automatically)." + required: + - layout PageLivePreviewVersionBean: type: object properties: @@ -35474,29 +36253,68 @@ components: type: string SearchForm: type: object + description: "Content search request. `query` is a Lucene expression; the remaining\ + \ fields control paging, sorting, language, and how results are rendered." properties: allCategoriesInfo: type: boolean + description: "When `true`, include full category metadata for category fields\ + \ in the results." depth: type: integer format: int32 + default: -1 + description: "Relationship-loading depth (0-3): how many levels of related\ + \ content to inline. `-1` (default) loads none." + example: 1 languageId: type: integer format: int64 + description: Language id to search in. Defaults to the system default language + when omitted. + example: 1 limit: type: integer format: int32 + default: 20 + description: Maximum number of contentlets to return (page size). + example: 20 offset: type: integer format: int32 + default: 0 + description: Zero-based result offset for paging. + example: 0 query: type: string + description: "Lucene query. IMPORTANT: custom (user-defined) fields MUST\ + \ be qualified with the content type's variable name — `ContentTypeVar.fieldVar`.\ + \ A BARE field name matches nothing and returns zero results with NO error\ + \ (a common silent failure). For example, to find featured Books use `+AwazonBook.featured:true`,\ + \ not `+featured:true`; to match a slug use `+AwazonBook.slug:my-slug`.\ + \ Restrict the type with `+contentType:AwazonBook` (contentType is a system\ + \ field, unqualified). \n\nResults are also HOST-scoped: unless you add\ + \ a host clause, the search resolves against the current request's site\ + \ and will NOT return content that lives on a different host (e.g. content\ + \ saved to `SYSTEM_HOST`/default while you query as another site). Constrain\ + \ the host explicitly with `+conHost:<siteIdentifier>` (or `+conHost:SYSTEM_HOST`),\ + \ and add `+live:true` / `+working:true` and `+deleted:false` as needed.\ + \ See the Lucene content-search syntax docs." + example: +contentType:AwazonBook +AwazonBook.featured:true +live:true +deleted:false render: type: string + description: "When set to `true`, each matching contentlet's `htmlpageasset`/widget\ + \ content is rendered and included in the response. Omit for raw field\ + \ data only." sort: type: string + description: "Sort clause, e.g. `AwazonBook.title asc` or `modDate desc`.\ + \ Custom fields are qualified the same way as in `query`." + example: modDate desc userId: type: string + description: Optional user id to run the search as (permissions are applied + for this user). Defaults to the authenticated caller when omitted. SearchSiteByNameForm: type: object properties: @@ -35783,6 +36601,8 @@ components: type: boolean SimpleSiteVariableForm: type: object + description: Optional list of site variables (key/value pairs) to create alongside + the site. properties: id: type: string @@ -35794,46 +36614,75 @@ components: type: string SiteForm: type: object + description: Form used to create a Site (Host) in dotCMS. 'siteName' (the hostname) + is the only required field; the new site is created unpublished and must be + published separately. properties: addThis: type: string + description: AddThis sharing-widget account ID for this site. aliases: type: string + description: Comma- or newline-separated list of host aliases (alternate + hostnames) for this site. default: type: boolean description: type: string + description: Default meta description applied to pages on this site. embeddedDashboard: type: string + description: Embedded dashboard markup for this site. forceExecution: type: boolean + description: Whether to force creation even when validation would otherwise + warn (e.g. a duplicate alias). googleAnalytics: type: string + description: Google Analytics tracking ID for this site. googleMap: type: string + description: Google Maps API key for this site. identifier: type: string + description: Identifier of the site. Ignored on creation; server-generated. inode: type: string + description: Inode (version identifier) of the site. Ignored on creation; + server-generated. keywords: type: string + description: Default meta keywords applied to pages on this site. languageId: type: integer format: int64 + description: Default language ID for this site. Defaults to the system default + language when 0/omitted. proxyUrlForEditMode: type: string + description: Proxy URL used to render the site in edit mode. runDashboard: type: boolean + description: Whether the analytics dashboard runs for this site. siteName: type: string + description: "The hostname of the site, e.g. 'www.example.com'. This is\ + \ the site's primary name." siteThumbnail: type: string + description: Identifier of the image asset used as the site thumbnail. tagStorage: type: string + description: Identifier of the site whose tag storage this site shares. + Defaults to this site itself when omitted. variables: type: array + description: Optional list of site variables (key/value pairs) to create + alongside the site. items: $ref: "#/components/schemas/SimpleSiteVariableForm" + required: + - siteName SiteVariableForm: type: object properties: @@ -36249,56 +37098,98 @@ components: type: boolean TemplateForm: type: object + description: Form used to create or update a Template in dotCMS. A Template + can be either drawn with the layout builder (set 'drawed:true' and provide + 'layout') or authored as raw Velocity markup (provide 'body'). properties: body: type: string + description: Raw Velocity markup that renders the template. Required even + when 'drawed:true' — send an empty string ('') when using the layout builder + ('layout') instead of hand-written body. countAddContainer: type: integer format: int32 + description: Count of 'add container' placeholders in the layout. Server-managed; + typically omitted. countContainers: type: integer format: int32 + description: Count of containers in the layout. Server-managed; typically + omitted. drawed: type: boolean + description: "Whether this template was built with the visual layout builder.\ + \ When true, provide 'layout' (the row/column/container structure); when\ + \ false, provide 'body'." drawedBody: type: string + description: Velocity body generated by the layout builder for a drawn template. + Server-managed. footer: type: string + description: Velocity/HTML footer fragment for pages using this template. footerCheck: type: boolean + description: Whether the footer fragment is enabled for this template. friendlyName: type: string + description: Friendly (human-readable) name of the template. headCode: type: string + description: Velocity/HTML injected into the page <head> for pages using + this template. header: type: string + description: Velocity/HTML header fragment for pages using this template. headerCheck: type: boolean + description: Whether the header fragment is enabled for this template. identifier: type: string + description: "Identifier of the template. Required for updates, ignored\ + \ on creation." image: type: string + description: Identifier of the image asset used as the template thumbnail. inode: type: string + description: Inode (version identifier) of the template. Server-managed; + typically omitted on input. layout: $ref: "#/components/schemas/TemplateLayoutView" name: type: string + description: Internal/asset name of the template. selectedimage: type: string + description: Identifier of the image asset currently selected as the template + thumbnail in the UI. showOnMenu: type: boolean + description: Whether this template should appear in navigation menus. siteId: type: string + description: "Identifier of the site (host) where this template will be\ + \ created. This is a site identifier UUID, not 'hostId'. If not provided,\ + \ the template is assigned to the site resolved from the current HTTP\ + \ request context." sortOrder: type: integer format: int32 + description: Sort order for display purposes. theme: type: string + description: "Theme folder identifier (not a path) that supplies the template's\ + \ CSS/JS and VTL fragments. Resolve the folder identifier from a path\ + \ via GET /api/v1/folder/sitename/{site}/uri/{uri}." themeName: type: string + description: Display name of the theme. Informational; 'theme' (the folder + identifier) is authoritative. title: type: string + description: Title of the template (displayed in the UI). required: - title TemplateImageForm: @@ -36308,6 +37199,9 @@ components: type: string TemplateLayout: type: object + description: "Required. The row/column/container structure of the layout: a\ + \ 'body' of rows, each row holding columns, each column holding containers\ + \ referenced by identifier + uuid, plus optional 'header', 'footer', and 'sidebar'." properties: body: $ref: "#/components/schemas/Body" @@ -36403,6 +37297,10 @@ components: type: string TemplateLayoutView: type: object + description: "Layout produced by the visual builder: a 'body' of rows, each\ + \ row holding columns, and each column holding containers (referenced by identifier\ + \ + uuid), plus optional 'header', 'footer', 'sidebar', and 'title'. Provide\ + \ this when 'drawed:true'." properties: body: $ref: "#/components/schemas/BodyView" @@ -37089,6 +37987,15 @@ components: properties: field: $ref: "#/components/schemas/Field" + UpdateFieldRequestView: + type: object + description: Request body for updating one content-type field. The field object + must be wrapped in the top-level `field` property. + properties: + field: + $ref: "#/components/schemas/ContentTypeFieldView" + required: + - field UpdateFolderDetail: type: object description: Folder configuration details @@ -38017,6 +38924,103 @@ components: weight: type: number format: float + VelocityErrorResponseView: + type: object + properties: + errors: + type: array + description: List of Velocity errors detected while evaluating the submitted + code. + items: + $ref: "#/components/schemas/VelocityErrorView" + warnings: + type: array + description: "Non-fatal warnings (undefined references, null method results)\ + \ observed before the error. Omitted when there are none." + items: + $ref: "#/components/schemas/VelocityWarningView" + VelocityErrorView: + type: object + description: "A single Velocity evaluation error, structured so an automated\ + \ caller can locate and fix the offending code without parsing a stack trace." + properties: + column: + type: integer + format: int32 + description: "1-based column number in the submitted velocity where the\ + \ error occurred, when Velocity reports it. Omitted when unavailable." + example: 39 + detail: + type: string + description: "Full, multi-line engine output for the error, including the\ + \ complete grammar-token list for parse errors. Intended for human display;\ + \ omitted when it adds nothing beyond `message`." + example: |- + Encountered "<EOF>" at line 6, column 39 + Was expecting one of: + "[" ... + "(" ... + errorType: + type: string + description: "Simple class name of the underlying Velocity error, normalized\ + \ to the public Velocity type (e.g. ParseErrorException, MethodInvocationException,\ + \ ResourceNotFoundException) rather than a dotCMS-internal subclass. Lets\ + \ the caller distinguish a syntax error from a runtime error." + example: ParseErrorException + line: + type: integer + format: int32 + description: "1-based line number in the submitted velocity where the error\ + \ occurred, when Velocity reports it. Omitted when unavailable." + example: 6 + message: + type: string + description: "Concise, single-line Velocity error message including the\ + \ offending token and position when available. See `detail` for the full\ + \ engine output." + example: "Encountered \"<EOF>\" at line 6, column 39" + templateName: + type: string + description: Name Velocity associated with the evaluated template. For dynamic + requests this is a synthetic name identifying the submitted script. + example: dynamic velocity + VelocityWarningView: + type: object + description: "A non-fatal Velocity warning, such as an undefined reference or\ + \ a method call that returned null. The script still evaluated; warnings flag\ + \ likely typos in non-strict mode." + properties: + column: + type: integer + format: int32 + description: "1-based column number where the reference appears, when Velocity\ + \ reports it. Omitted when unavailable." + example: 1 + line: + type: integer + format: int32 + description: "1-based line number where the reference appears, when Velocity\ + \ reports it. Omitted when unavailable." + example: 3 + message: + type: string + description: Human-readable description of the warning. + example: Undefined reference '$noSuchVar' — renders as literal text in non-strict + mode + reference: + type: string + description: "The reference or method expression that triggered the warning,\ + \ when known." + example: $noSuchVar + type: + type: string + description: The kind of warning. + enum: + - UNDEFINED_REFERENCE + - NULL_METHOD_RESULT + - INVALID_METHOD + - NULL_SET + example: UNDEFINED_REFERENCE Version: type: object properties: diff --git a/dotCMS/src/test/java/com/dotcms/content/elasticsearch/business/ContentFactoryIndexOperationsESTest.java b/dotCMS/src/test/java/com/dotcms/content/elasticsearch/business/ContentFactoryIndexOperationsESTest.java new file mode 100644 index 000000000000..f7f88fe1d831 --- /dev/null +++ b/dotCMS/src/test/java/com/dotcms/content/elasticsearch/business/ContentFactoryIndexOperationsESTest.java @@ -0,0 +1,26 @@ +package com.dotcms.content.elasticsearch.business; + +import static org.junit.Assert.assertEquals; + +import org.elasticsearch.search.builder.SearchSourceBuilder; +import org.elasticsearch.search.sort.FieldSortBuilder; +import org.elasticsearch.search.sort.SortOrder; +import org.junit.Test; + +public class ContentFactoryIndexOperationsESTest { + + @Test + public void addBuilderSort_acceptsCanonicalAndDotrawFieldNames() { + final SearchSourceBuilder builder = new SearchSourceBuilder(); + + ContentFactoryIndexOperationsES.addBuilderSort( + "Book.title asc,Book.author_dotraw desc", builder); + + final FieldSortBuilder title = (FieldSortBuilder) builder.sorts().get(0); + final FieldSortBuilder author = (FieldSortBuilder) builder.sorts().get(1); + assertEquals("book.title_dotraw", title.getFieldName()); + assertEquals(SortOrder.ASC, title.order()); + assertEquals("book.author_dotraw", author.getFieldName()); + assertEquals(SortOrder.DESC, author.order()); + } +} diff --git a/dotCMS/src/test/java/com/dotcms/content/index/opensearch/ContentFactoryIndexOperationsOSTest.java b/dotCMS/src/test/java/com/dotcms/content/index/opensearch/ContentFactoryIndexOperationsOSTest.java new file mode 100644 index 000000000000..12eb3d025877 --- /dev/null +++ b/dotCMS/src/test/java/com/dotcms/content/index/opensearch/ContentFactoryIndexOperationsOSTest.java @@ -0,0 +1,24 @@ +package com.dotcms.content.index.opensearch; + +import static org.junit.Assert.assertEquals; + +import org.junit.Test; +import org.opensearch.client.opensearch._types.SortOrder; +import org.opensearch.client.opensearch.core.SearchRequest; + +public class ContentFactoryIndexOperationsOSTest { + + @Test + public void addBuilderSort_acceptsCanonicalAndDotrawFieldNames() { + final SearchRequest.Builder builder = new SearchRequest.Builder(); + + ContentFactoryIndexOperationsOS.addBuilderSort( + "Book.title asc,Book.author_dotraw desc", builder); + + final SearchRequest request = builder.build(); + assertEquals("book.title_dotraw", request.sort().get(0).field().field()); + assertEquals(SortOrder.Asc, request.sort().get(0).field().order()); + assertEquals("book.author_dotraw", request.sort().get(1).field().field()); + assertEquals(SortOrder.Desc, request.sort().get(1).field().order()); + } +} diff --git a/dotCMS/src/test/java/com/dotcms/contenttype/model/field/FieldClazzAliasTest.java b/dotCMS/src/test/java/com/dotcms/contenttype/model/field/FieldClazzAliasTest.java new file mode 100644 index 000000000000..1f09d5df4ccf --- /dev/null +++ b/dotCMS/src/test/java/com/dotcms/contenttype/model/field/FieldClazzAliasTest.java @@ -0,0 +1,96 @@ +package com.dotcms.contenttype.model.field; + +import static org.junit.Assert.assertEquals; + +import com.dotcms.UnitTestBase; +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.json.JsonMapper; +import org.junit.Test; + +/** + * Unit tests for the {@code clazz} discriminator resolution performed by + * {@link Field.ClassNameAliasResolver}. In addition to the fully-qualified {@code Immutable*} field + * class name, the resolver accepts ergonomic short forms so callers (e.g. AI agents) can pass a + * field-type name/legacy value ({@code "TEXT"}, {@code "text"}, {@code "STORY_BLOCK_FIELD"}, ...) or + * the concrete simple class name ({@code "TextField"}). Every accepted form must deserialize to the + * same generated {@code Immutable*} field class. + * + * <p>These deserialize straight into {@link Field} (the polymorphic type carrying the + * {@code @JsonTypeIdResolver}), which is the exact chokepoint every field {@code clazz} value flows + * through and needs no database.</p> + */ +public class FieldClazzAliasTest extends UnitTestBase { + + private static final ObjectMapper MAPPER = JsonMapper.builder() + .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false) + .build(); + + private String resolvedClassName(final String clazz) throws Exception { + final String json = "{\"clazz\":\"" + clazz + "\",\"name\":\"Test\",\"variable\":\"testVar\"}"; + return MAPPER.readValue(json, Field.class).getClass().getSimpleName(); + } + + private void assertResolves(final String clazz, final String expectedImmutableClass) + throws Exception { + assertEquals("clazz '" + clazz + "' should deserialize to " + expectedImmutableClass, + expectedImmutableClass, resolvedClassName(clazz)); + } + + @Test + public void test_fieldType_enum_names_resolve() throws Exception { + assertResolves("TEXT", "ImmutableTextField"); + assertResolves("TEXT_AREA", "ImmutableTextAreaField"); + assertResolves("WYSIWYG", "ImmutableWysiwygField"); + assertResolves("CHECKBOX", "ImmutableCheckboxField"); + assertResolves("RADIO", "ImmutableRadioField"); + assertResolves("SELECT", "ImmutableSelectField"); + assertResolves("MULTI_SELECT", "ImmutableMultiSelectField"); + assertResolves("DATE", "ImmutableDateField"); + assertResolves("TIME", "ImmutableTimeField"); + assertResolves("DATE_TIME", "ImmutableDateTimeField"); + assertResolves("TAG", "ImmutableTagField"); + assertResolves("CONSTANT", "ImmutableConstantField"); + assertResolves("HIDDEN", "ImmutableHiddenField"); + assertResolves("BINARY", "ImmutableBinaryField"); + assertResolves("CUSTOM_FIELD", "ImmutableCustomField"); + assertResolves("KEY_VALUE", "ImmutableKeyValueField"); + assertResolves("STORY_BLOCK_FIELD", "ImmutableStoryBlockField"); + assertResolves("JSON_FIELD", "ImmutableJSONField"); + assertResolves("CATEGORY", "ImmutableCategoryField"); + assertResolves("RELATIONSHIP", "ImmutableRelationshipField"); + } + + @Test + public void test_fieldType_names_are_case_insensitive() throws Exception { + assertResolves("text", "ImmutableTextField"); + assertResolves("Text", "ImmutableTextField"); + assertResolves("story_block_field", "ImmutableStoryBlockField"); + assertResolves("Custom_Field", "ImmutableCustomField"); + } + + @Test + public void test_legacy_values_resolve() throws Exception { + // The lowercase legacy values (LegacyFieldTypes#legacyValue) also resolve. + assertResolves("checkbox", "ImmutableCheckboxField"); + assertResolves("multi_select", "ImmutableMultiSelectField"); + assertResolves("wysiwyg", "ImmutableWysiwygField"); + } + + @Test + public void test_concrete_simple_class_name_still_resolves() throws Exception { + // Pre-existing leniency (bare simple class name -> Immutable*) must be preserved. + assertResolves("TextField", "ImmutableTextField"); + assertResolves("StoryBlockField", "ImmutableStoryBlockField"); + assertResolves("CustomField", "ImmutableCustomField"); + } + + @Test + public void test_fully_qualified_immutable_class_name_still_resolves() throws Exception { + // The documented canonical form must keep working unchanged. + assertResolves("com.dotcms.contenttype.model.field.ImmutableTextField", + "ImmutableTextField"); + assertResolves("com.dotcms.contenttype.model.field.ImmutableStoryBlockField", + "ImmutableStoryBlockField"); + } +} diff --git a/dotCMS/src/test/java/com/dotcms/contenttype/transform/contenttype/ContentTypeClazzAliasTest.java b/dotCMS/src/test/java/com/dotcms/contenttype/transform/contenttype/ContentTypeClazzAliasTest.java new file mode 100644 index 000000000000..757f122bfd53 --- /dev/null +++ b/dotCMS/src/test/java/com/dotcms/contenttype/transform/contenttype/ContentTypeClazzAliasTest.java @@ -0,0 +1,86 @@ +package com.dotcms.contenttype.transform.contenttype; + +import static org.junit.Assert.assertEquals; + +import com.dotcms.UnitTestBase; +import com.dotcms.contenttype.model.type.ContentType; +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.json.JsonMapper; +import org.junit.Test; + +/** + * Unit tests for the {@code clazz} discriminator resolution performed by + * {@link ContentType.ClassNameAliasResolver}. In addition to the fully-qualified {@code Immutable*} + * class name, the resolver accepts ergonomic short forms so callers (e.g. AI agents) can pass a + * base-type name/alias ({@code "WIDGET"}, {@code "Form"}, {@code "File"}, ...) or the concrete + * simple class name ({@code "SimpleContentType"}). Every accepted form must deserialize to the same + * generated {@code Immutable*} class. + * + * <p>These deserialize straight into {@link ContentType} (the polymorphic type carrying the + * {@code @JsonTypeIdResolver}), which is the exact chokepoint every {@code clazz} value flows + * through and needs no database.</p> + */ +public class ContentTypeClazzAliasTest extends UnitTestBase { + + private static final ObjectMapper MAPPER = JsonMapper.builder() + .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false) + .build(); + + private String resolvedClassName(final String clazz) throws Exception { + final String json = "{\"clazz\":\"" + clazz + "\",\"name\":\"Test\",\"variable\":\"testVar\"}"; + return MAPPER.readValue(json, ContentType.class).getClass().getSimpleName(); + } + + private void assertResolves(final String clazz, final String expectedImmutableClass) + throws Exception { + assertEquals("clazz '" + clazz + "' should deserialize to " + expectedImmutableClass, + expectedImmutableClass, resolvedClassName(clazz)); + } + + @Test + public void test_baseType_enum_names_resolve() throws Exception { + assertResolves("CONTENT", "ImmutableSimpleContentType"); + assertResolves("WIDGET", "ImmutableWidgetContentType"); + assertResolves("FORM", "ImmutableFormContentType"); + assertResolves("FILEASSET", "ImmutableFileAssetContentType"); + assertResolves("HTMLPAGE", "ImmutablePageContentType"); + assertResolves("PERSONA", "ImmutablePersonaContentType"); + assertResolves("VANITY_URL", "ImmutableVanityUrlContentType"); + assertResolves("KEY_VALUE", "ImmutableKeyValueContentType"); + assertResolves("DOTASSET", "ImmutableDotAssetContentType"); + } + + @Test + public void test_baseType_names_are_case_insensitive() throws Exception { + assertResolves("content", "ImmutableSimpleContentType"); + assertResolves("Widget", "ImmutableWidgetContentType"); + assertResolves("dotasset", "ImmutableDotAssetContentType"); + } + + @Test + public void test_baseType_alternate_names_resolve() throws Exception { + assertResolves("Form", "ImmutableFormContentType"); + assertResolves("File", "ImmutableFileAssetContentType"); + assertResolves("Page", "ImmutablePageContentType"); + assertResolves("VanityURL", "ImmutableVanityUrlContentType"); + assertResolves("KeyValue", "ImmutableKeyValueContentType"); + assertResolves("DotAsset", "ImmutableDotAssetContentType"); + } + + @Test + public void test_concrete_simple_class_name_still_resolves() throws Exception { + // Pre-existing leniency must be preserved. + assertResolves("SimpleContentType", "ImmutableSimpleContentType"); + assertResolves("WidgetContentType", "ImmutableWidgetContentType"); + } + + @Test + public void test_fully_qualified_immutable_class_name_still_resolves() throws Exception { + // The documented canonical form must keep working unchanged. + assertResolves("com.dotcms.contenttype.model.type.ImmutableSimpleContentType", + "ImmutableSimpleContentType"); + assertResolves("com.dotcms.contenttype.model.type.ImmutableWidgetContentType", + "ImmutableWidgetContentType"); + } +} diff --git a/dotCMS/src/test/java/com/dotcms/rest/api/v1/a11yagent/A11yAgentResourceTest.java b/dotCMS/src/test/java/com/dotcms/rest/api/v1/a11yagent/A11yAgentResourceTest.java new file mode 100644 index 000000000000..a20f5a831c00 --- /dev/null +++ b/dotCMS/src/test/java/com/dotcms/rest/api/v1/a11yagent/A11yAgentResourceTest.java @@ -0,0 +1,332 @@ +package com.dotcms.rest.api.v1.a11yagent; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.mockStatic; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import com.dotcms.auth.providers.jwt.factories.ApiTokenAPI; +import com.dotcms.rest.InitDataObject; +import com.dotcms.rest.WebResource; +import com.dotcms.security.apps.AppSecrets; +import com.dotcms.security.apps.AppsAPI; +import com.dotmarketing.beans.Host; +import com.dotmarketing.business.APILocator; +import com.dotmarketing.business.web.HostWebAPI; +import com.dotmarketing.business.web.WebAPILocator; +import com.liferay.portal.model.User; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.MockedStatic; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.ws.rs.core.Response; +import java.net.http.HttpClient; +import java.util.Optional; + +/** + * Unit tests for {@link A11yAgentResource}. + * + * <p>The resource is a proxy that authenticates the caller, reads {@code apiUrl} and + * {@code apiAuthToken} from App secrets, mints a short-lived JWT, resolves the page, and + * forwards to an external agent service. These tests cover the config-resolution and + * early-exit branches — the paths that decide whether anything is forwarded at all — without + * touching the network.</p> + * + * <p>Mirrors {@code PageScannerResourceTest}, the sibling proxy this resource was modelled on, + * and uses the package-private constructor seam the resource exposes for exactly this.</p> + */ +public class A11yAgentResourceTest { + + private WebResource webResource; + private HttpClient httpClient; + private A11yAgentResource resource; + + private HttpServletRequest request; + private HttpServletResponse response; + + private AppsAPI appsAPI; + private ApiTokenAPI apiTokenAPI; + + private MockedStatic<APILocator> mockedApiLocator; + private MockedStatic<WebAPILocator> mockedWebApiLocator; + + // ----------------------------------------------------------------------- + // Setup / teardown + // ----------------------------------------------------------------------- + + @BeforeEach + void setUp() throws Exception { + webResource = mock(WebResource.class); + httpClient = mock(HttpClient.class); + resource = new A11yAgentResource(webResource, httpClient); + + request = mock(HttpServletRequest.class); + response = mock(HttpServletResponse.class); + when(request.getScheme()).thenReturn("https"); + when(request.getServerName()).thenReturn("demo.dotcms.com"); + when(request.getServerPort()).thenReturn(443); + when(request.getRemoteAddr()).thenReturn("10.0.0.9"); + + final User user = mock(User.class); + when(user.getUserId()).thenReturn("test-user"); + + final InitDataObject initData = mock(InitDataObject.class); + when(initData.getUser()).thenReturn(user); + when(webResource.init(any(WebResource.InitBuilder.class))).thenReturn(initData); + + appsAPI = mock(AppsAPI.class); + apiTokenAPI = mock(ApiTokenAPI.class); + + mockedApiLocator = mockStatic(APILocator.class); + final Host systemHost = mock(Host.class); + final User systemUser = mock(User.class); + mockedApiLocator.when(APILocator::systemHost).thenReturn(systemHost); + mockedApiLocator.when(APILocator::systemUser).thenReturn(systemUser); + mockedApiLocator.when(APILocator::getAppsAPI).thenReturn(appsAPI); + mockedApiLocator.when(APILocator::getApiTokenAPI).thenReturn(apiTokenAPI); + + mockedWebApiLocator = mockStatic(WebAPILocator.class); + final HostWebAPI hostWebAPI = mock(HostWebAPI.class); + when(hostWebAPI.getCurrentHost(request)).thenReturn(systemHost); + mockedWebApiLocator.when(WebAPILocator::getHostWebAPI).thenReturn(hostWebAPI); + + // Default: no App configured. Tests that need one override this. + when(appsAPI.getSecrets(anyString(), anyBoolean(), any(Host.class), any(User.class))) + .thenReturn(Optional.empty()); + } + + @AfterEach + void tearDown() { + mockedApiLocator.close(); + mockedWebApiLocator.close(); + } + + /** Configure the App with the given secrets, omitting any passed as null. */ + private void configureApp(final String apiUrl, final String apiAuthToken) + throws Exception { + final AppSecrets.Builder builder = AppSecrets.builder().withKey(A11yAgentResource.APP_KEY); + if (apiUrl != null) { + builder.withSecret("apiUrl", apiUrl); + } + if (apiAuthToken != null) { + builder.withSecret("apiAuthToken", apiAuthToken); + } + when(appsAPI.getSecrets(anyString(), anyBoolean(), any(Host.class), any(User.class))) + .thenReturn(Optional.of(builder.build())); + } + + private static A11yAgentFixForm fixForm(final String identifier) { + final A11yAgentFixForm.Builder builder = A11yAgentFixForm.builder(); + if (identifier != null) { + builder.identifier(identifier); + } + return builder.build(); + } + + // ----------------------------------------------------------------------- + // App configuration + // ----------------------------------------------------------------------- + + /** + * Method to test: {@link A11yAgentResource#fix} + * Given scenario: The Page Scanner App is not configured in the Apps portlet. + * Expected result: 503 SERVICE_UNAVAILABLE, and nothing is forwarded upstream. + */ + @Test + void fix_appNotConfigured_returns503() { + final Response resp = resource.fix(request, response, fixForm("page-id")); + + assertEquals(Response.Status.SERVICE_UNAVAILABLE.getStatusCode(), resp.getStatus()); + } + + /** + * Method to test: {@link A11yAgentResource#fix} + * Given scenario: The App exists but {@code apiAuthToken} is absent. + * Expected result: 503 — a half-configured App is treated as not configured, so the + * proxy never forwards an unauthenticated request to the agent service. + */ + @Test + void fix_missingApiAuthToken_returns503() throws Exception { + configureApp("https://agent.example.com", null); + + final Response resp = resource.fix(request, response, fixForm("page-id")); + + assertEquals(Response.Status.SERVICE_UNAVAILABLE.getStatusCode(), resp.getStatus()); + } + + /** + * Method to test: {@link A11yAgentResource#fix} + * Given scenario: The App exists but {@code apiUrl} is absent. + * Expected result: 503 for the same reason as a missing token. + */ + @Test + void fix_missingApiUrl_returns503() throws Exception { + configureApp(null, "secret-token"); + + final Response resp = resource.fix(request, response, fixForm("page-id")); + + assertEquals(Response.Status.SERVICE_UNAVAILABLE.getStatusCode(), resp.getStatus()); + } + + /** + * Method to test: {@link A11yAgentResource#stop} + * Given scenario: The App is not configured. + * Expected result: 503 — /stop resolves the same config as /fix, so it fails the same way. + */ + @Test + void stop_appNotConfigured_returns503() { + final Response resp = resource.stop(request, response, + A11yAgentStopForm.builder().runId("r_1").build()); + + assertEquals(Response.Status.SERVICE_UNAVAILABLE.getStatusCode(), resp.getStatus()); + } + + /** + * Method to test: {@link A11yAgentResource#activeRun} + * Given scenario: The App is not configured. + * Expected result: 503, and no token is minted for a call that cannot go anywhere. + */ + @Test + void activeRun_appNotConfigured_returns503() { + final Response resp = resource.activeRun(request, response); + + assertEquals(Response.Status.SERVICE_UNAVAILABLE.getStatusCode(), resp.getStatus()); + verify(apiTokenAPI, never()).persistApiToken(anyString(), any(), anyString(), anyString(), + anyString()); + } + + // ----------------------------------------------------------------------- + // Request validation + // ----------------------------------------------------------------------- + + /** + * Method to test: {@link A11yAgentResource#fix} + * Given scenario: A configured App, but the body carries no identifier. + * Expected result: 400 MISSING_IDENTIFIER. The form declares identifier as nullable + * precisely so this surfaces here rather than as a Jackson deserialization failure. + */ + @Test + void fix_missingIdentifier_returns400() throws Exception { + configureApp("https://agent.example.com", "secret-token"); + + final Response resp = resource.fix(request, response, fixForm(null)); + + assertEquals(Response.Status.BAD_REQUEST.getStatusCode(), resp.getStatus()); + } + + /** + * Method to test: {@link A11yAgentResource#fix} + * Given scenario: A configured App and a null body. + * Expected result: 400 rather than an NPE. + */ + @Test + void fix_nullBody_returns400() throws Exception { + configureApp("https://agent.example.com", "secret-token"); + + final Response resp = resource.fix(request, response, null); + + assertEquals(Response.Status.BAD_REQUEST.getStatusCode(), resp.getStatus()); + } + + /** + * Method to test: {@link A11yAgentResource#stop} + * Given scenario: No runId in the body. + * Expected result: 400 MISSING_RUN_ID, checked BEFORE any config or token work — a + * malformed request must not mint a token or touch App secrets. + */ + @Test + void stop_missingRunId_returns400WithoutMintingAToken() throws Exception { + configureApp("https://agent.example.com", "secret-token"); + + final Response resp = resource.stop(request, response, + A11yAgentStopForm.builder().build()); + + assertEquals(Response.Status.BAD_REQUEST.getStatusCode(), resp.getStatus()); + verify(apiTokenAPI, never()).persistApiToken(anyString(), any(), anyString(), anyString(), + anyString()); + } + + /** + * Method to test: {@link A11yAgentResource#stop} + * Given scenario: A null body. + * Expected result: 400 rather than an NPE. + */ + @Test + void stop_nullBody_returns400() throws Exception { + configureApp("https://agent.example.com", "secret-token"); + + final Response resp = resource.stop(request, response, null); + + assertEquals(Response.Status.BAD_REQUEST.getStatusCode(), resp.getStatus()); + } + + // ----------------------------------------------------------------------- + // SSE relay — a configuration failure cannot use an HTTP status + // ----------------------------------------------------------------------- + + /** + * Method to test: {@link A11yAgentResource#fixStream} + * Given scenario: The App is not configured. + * Expected result: an EventOutput is still returned and closed, carrying an SSE error + * frame. The stream endpoint cannot report the failure as an HTTP status the way /fix + * does, because the response has already begun. + */ + @Test + void fixStream_appNotConfigured_returnsClosedErrorStream() { + final var output = resource.fixStream(request, response, fixForm("page-id")); + + assertNotNull(output); + assertTrue(output.isClosed(), + "the error frame is terminal, so the stream must be closed behind it"); + } + + /** + * Method to test: {@link A11yAgentResource#fixStream} + * Given scenario: A configured App and a valid identifier that resolves to no page. + * Expected result: still an EventOutput rather than a thrown exception — the relay + * reports upstream problems in-band. + */ + @Test + void fixStream_pageNotFound_returnsClosedErrorStream() throws Exception { + configureApp("https://agent.example.com", "secret-token"); + + final var output = resource.fixStream(request, response, fixForm("no-such-page")); + + assertNotNull(output); + assertTrue(output.isClosed()); + } + + // ----------------------------------------------------------------------- + // Secrets must not leak + // ----------------------------------------------------------------------- + + /** + * Method to test: {@link A11yAgentResource#fix} + * Given scenario: A configured App whose auth token has a recognisable value, and a + * request that fails before forwarding. + * Expected result: the token never appears in the response body. The proxy is the auth + * boundary, so a relayed error is the most likely place for a secret to escape. + */ + @Test + void fix_errorResponse_neverCarriesTheAuthToken() throws Exception { + configureApp("https://agent.example.com", "super-secret-token-value"); + + final Response resp = resource.fix(request, response, fixForm(null)); + + final String body = String.valueOf(resp.getEntity()); + assertFalse(body.contains("super-secret-token-value"), + "the App auth token must never reach the client"); + } +} diff --git a/dotCMS/src/test/java/com/dotcms/rest/api/v1/vtl/CollectingInvalidReferenceHandlerTest.java b/dotCMS/src/test/java/com/dotcms/rest/api/v1/vtl/CollectingInvalidReferenceHandlerTest.java new file mode 100644 index 000000000000..55fdea5f0453 --- /dev/null +++ b/dotCMS/src/test/java/com/dotcms/rest/api/v1/vtl/CollectingInvalidReferenceHandlerTest.java @@ -0,0 +1,112 @@ +package com.dotcms.rest.api.v1.vtl; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; + +import java.util.List; +import org.apache.velocity.util.introspection.Info; +import org.junit.Test; + +/** + * Unit test for {@link CollectingInvalidReferenceHandler}. Velocity's + * {@code InvalidReferenceEventHandler} callbacks are invoked directly with the same arguments the + * engine passes at evaluation time (verified against the {@code ASTReference} call sites in + * velocity-1.7): a bare undefined variable arrives at {@code invalidGetMethod} with a null base + * object and null property; a bad method call arrives at {@code invalidMethod}. This keeps the test + * hermetic — it does not boot the dotCMS-coupled Velocity engine — while still exercising the + * classification, reference capture, position mapping, cap, and the "never substitute a value" + * contract that guarantees output is unchanged. + */ +public class CollectingInvalidReferenceHandlerTest { + + private static Info info(final int line, final int column) { + return new Info("dynamic velocity", line, column); + } + + @Test + public void undefined_top_level_reference_is_classified_and_captured() { + final CollectingInvalidReferenceHandler handler = new CollectingInvalidReferenceHandler(); + + // Bare $noSuchVar: engine passes object == null, property == null. + final Object substituted = + handler.invalidGetMethod(null, "$noSuchVar", null, null, info(3, 1)); + + assertNull("handler must not substitute a value (output stays unchanged)", substituted); + final List<VelocityWarningView> warnings = handler.getWarnings(); + assertEquals(1, warnings.size()); + final VelocityWarningView w = warnings.get(0); + assertEquals("UNDEFINED_REFERENCE", w.getType()); + assertEquals("$noSuchVar", w.getReference()); + assertEquals(Integer.valueOf(3), w.getLine()); + assertEquals(Integer.valueOf(1), w.getColumn()); + assertTrue(w.getMessage().contains("noSuchVar")); + } + + @Test + public void null_property_on_real_object_is_a_null_result_not_undefined() { + final CollectingInvalidReferenceHandler handler = new CollectingInvalidReferenceHandler(); + + // $real.missing where $real resolves but .missing is null: object != null. + handler.invalidGetMethod(null, "$real.missing", new Object(), "missing", info(4, 7)); + + final VelocityWarningView w = handler.getWarnings().get(0); + assertEquals("NULL_METHOD_RESULT", w.getType()); + assertTrue(w.getMessage().contains("missing")); + } + + @Test + public void bad_method_on_real_object_is_invalid_method() { + final CollectingInvalidReferenceHandler handler = new CollectingInvalidReferenceHandler(); + + final Object substituted = + handler.invalidMethod(null, "$list.badMethod()", new Object(), "badMethod", info(2, 5)); + + assertNull(substituted); + final VelocityWarningView w = handler.getWarnings().get(0); + assertEquals("INVALID_METHOD", w.getType()); + assertTrue(w.getMessage().contains("badMethod")); + } + + @Test + public void method_on_null_reference_is_reported_as_undefined() { + final CollectingInvalidReferenceHandler handler = new CollectingInvalidReferenceHandler(); + + handler.invalidMethod(null, "$missing.call()", null, "call", info(1, 1)); + + assertEquals("UNDEFINED_REFERENCE", handler.getWarnings().get(0).getType()); + } + + @Test + public void null_set_is_collected() { + final CollectingInvalidReferenceHandler handler = new CollectingInvalidReferenceHandler(); + + final boolean handled = + handler.invalidSetMethod(null, "$x", "$nullThing", info(5, 1)); + + assertTrue("must not swallow the default #set behavior", !handled); + assertEquals("NULL_SET", handler.getWarnings().get(0).getType()); + } + + @Test + public void position_is_omitted_when_velocity_reports_zero() { + final CollectingInvalidReferenceHandler handler = new CollectingInvalidReferenceHandler(); + + handler.invalidGetMethod(null, "$x", null, null, info(0, 0)); + + final VelocityWarningView w = handler.getWarnings().get(0); + assertNull(w.getLine()); + assertNull(w.getColumn()); + } + + @Test + public void warnings_are_capped() { + final CollectingInvalidReferenceHandler handler = new CollectingInvalidReferenceHandler(); + + for (int i = 0; i < CollectingInvalidReferenceHandler.MAX_WARNINGS + 25; i++) { + handler.invalidGetMethod(null, "$missing" + i, null, null, info(i + 1, 1)); + } + + assertEquals(CollectingInvalidReferenceHandler.MAX_WARNINGS, handler.getWarnings().size()); + } +} diff --git a/dotcms-integration/src/test/java/com/dotcms/rest/api/v1/page/PageResourceTest.java b/dotcms-integration/src/test/java/com/dotcms/rest/api/v1/page/PageResourceTest.java index a7acc2cf1900..39908269a566 100644 --- a/dotcms-integration/src/test/java/com/dotcms/rest/api/v1/page/PageResourceTest.java +++ b/dotcms-integration/src/test/java/com/dotcms/rest/api/v1/page/PageResourceTest.java @@ -881,7 +881,7 @@ public void testRender() throws DotDataException, DotSecurityException { final Contentlet checkin = APILocator.getContentletAPIImpl().checkin(checkout, systemUser, false); final Response response = pageResource - .loadJson(request, this.response, pageUri, null, null, + .loadJson(request, this.response, pageUri, null, null, null, String.valueOf(languageId), null, null); RestUtilTest.verifySuccessResponse(response); @@ -962,7 +962,7 @@ public void testRenderWithContent() throws DotDataException, DotSecurityExceptio when(request.getAttribute(WebKeys.HTMLPAGE_LANGUAGE)).thenReturn(String.valueOf(languageId)); final Response response = pageResource - .loadJson(request, this.response, pagePath, "PREVIEW_MODE", null, + .loadJson(request, this.response, pagePath, null, "PREVIEW_MODE", null, "1", null, null); RestUtilTest.verifySuccessResponse(response); @@ -972,7 +972,7 @@ public void testRenderWithContent() throws DotDataException, DotSecurityExceptio } /** - * Method to test: {@link PageResource#loadJson(HttpServletRequest, HttpServletResponse, String, String, String, String, String, String)} + * Method to test: {@link PageResource#loadJson(HttpServletRequest, HttpServletResponse, String, String, String, String, String, String, String)} * Given Scenario: A page has a container with a single contentlet, and that contentlet is then * archived. Archiving keeps the working version (it only sets deleted=true on the * version info), so a showLive=false lookup still resolves it in EDIT/PREVIEW mode. @@ -1046,7 +1046,7 @@ public void testArchivedContentNotRenderedInEditAndPreviewMode() private int renderAndCountContents(final PageMode mode) throws DotDataException, DotSecurityException { final Response response = pageResource - .loadJson(request, this.response, pagePath, mode.name(), null, "1", null, null); + .loadJson(request, this.response, pagePath, null, mode.name(), null, "1", null, null); RestUtilTest.verifySuccessResponse(response); final PageView pageView = (PageView) ((ResponseEntityView) response.getEntity()).getEntity(); return pageView.getNumberContents(); @@ -1088,7 +1088,7 @@ public void shouldReturnPageByURLPattern() Thread.sleep(500); final Response response = pageResource - .render(request, this.response, String.format("%s/text", baseUrl), "PREVIEW_MODE", null, + .render(request, this.response, String.format("%s/text", baseUrl), null, "PREVIEW_MODE", null, "1", null, null); RestUtilTest.verifySuccessResponse(response); @@ -1096,7 +1096,7 @@ public void shouldReturnPageByURLPattern() /** - * methodToTest {@link PageResource#render(HttpServletRequest, HttpServletResponse, String, String, String, String, String, String)} + * methodToTest {@link PageResource#render(HttpServletRequest, HttpServletResponse, String, String, String, String, String, String, String)} * Given Scenario: Create a page with URL Pattern, with a no publish content, and try to get it in ADMIN_MODE * ExpectedResult: Should return a 404 HTTP error * @@ -1136,12 +1136,12 @@ public void shouldReturn404ForPageWithURLPatternWithNotLIVEContentInAdminMode() Thread.sleep(500); pageResource - .render(request, this.response, String.format("%s/text", baseUrl), PageMode.ADMIN_MODE.toString(), null, + .render(request, this.response, String.format("%s/text", baseUrl), null, PageMode.ADMIN_MODE.toString(), null, "1", null, null); } /** - * methodToTest {@link PageResource#render(HttpServletRequest, HttpServletResponse, String, String, String, String, String, String)} + * methodToTest {@link PageResource#render(HttpServletRequest, HttpServletResponse, String, String, String, String, String, String, String)} * Given Scenario: Create a page with URL Pattern, with a no publish content, and try to get it in ADMIN_MODE * ExpectedResult: Should return a 404 HTTP error * @@ -1181,7 +1181,7 @@ public void shouldReturn404ForPageWithURLPatternWithNotLIVEContentInLiveMode() Thread.sleep(500); pageResource - .render(request, this.response, String.format("%s/text", baseUrl), PageMode.LIVE.toString(), null, + .render(request, this.response, String.format("%s/text", baseUrl), null, PageMode.LIVE.toString(), null, "1", null, null); } @@ -1237,7 +1237,7 @@ public void testNumberContentWithNotDrawTemplate() throws DotDataException, DotS when(request.getAttribute(WebKeys.HTMLPAGE_LANGUAGE)).thenReturn(String.valueOf(languageId)); final Response response = pageResource - .loadJson(request, this.response, pageUri, null, null, + .loadJson(request, this.response, pageUri, null, null, null, String.valueOf(languageId), null, null); RestUtilTest.verifySuccessResponse(response); @@ -1277,7 +1277,7 @@ public void testRenderNotPersonalizationVersion() when(initDataObject.getUser()).thenReturn(APILocator.systemUser()); final Response response = pageResource - .render(request, this.response, page.getURI(), modeParam, persona.getIdentifier(), + .render(request, this.response, page.getURI(), null, modeParam, persona.getIdentifier(), String.valueOf(languageId), null, null); final PageView pageView = (PageView) ((ResponseEntityView) response.getEntity()).getEntity(); @@ -1340,7 +1340,7 @@ public void testRenderWithVanityUrlWithRegex() when(initDataObject.getUser()).thenReturn(APILocator.systemUser()); final Response response = pageResourceWithHelper - .render(request, this.response, pageAsset.getURI(), modeParam, null, + .render(request, this.response, pageAsset.getURI(), null, modeParam, null, String.valueOf(languageId), null, null); final EmptyPageView pageView = (EmptyPageView) ((ResponseEntityView) response.getEntity()).getEntity(); @@ -1360,7 +1360,7 @@ public void testRenderWithVanityUrlWithRegex() filtersUtil.publishVanityUrl(vanityURLContentlet2); final Response response2 = pageResourceWithHelper - .render(request, this.response, pageAsset.getURI(), modeParam, null, + .render(request, this.response, pageAsset.getURI(), null, modeParam, null, String.valueOf(languageId), null, null); final EmptyPageView pageView2 = (EmptyPageView) ((ResponseEntityView) response2.getEntity()).getEntity(); @@ -1406,7 +1406,7 @@ public void testRenderPersonalizationVersion() when(request.getAttribute(WebKeys.HTMLPAGE_LANGUAGE)).thenReturn(String.valueOf(languageId)); final Response response = pageResource - .render(request, this.response, page.getURI(), null, persona.getIdentifier(), + .render(request, this.response, page.getURI(), null, null, persona.getIdentifier(), String.valueOf(languageId), null, null); final PageView pageView = (PageView) ((ResponseEntityView) response.getEntity()).getEntity(); @@ -1416,7 +1416,7 @@ public void testRenderPersonalizationVersion() } /*** - * methodToTest {@link PageResource#render(HttpServletRequest, HttpServletResponse, String, String, String, String, String, String)} + * methodToTest {@link PageResource#render(HttpServletRequest, HttpServletResponse, String, String, String, String, String, String, String)} * Given Scenario: Create a page with two containers and a content in each of then * ExpectedResult: Should render the containers with the contents, the check it look into the render code the * content div <pre>assertTrue(code.indexOf("data-dot-object=\"contentlet\"") != -1)</pre> @@ -1447,7 +1447,7 @@ public void testShouldRenderContainers() when(request.getAttribute(WebKeys.HTMLPAGE_LANGUAGE)).thenReturn(String.valueOf(languageId)); final Response response = pageResource - .render(request, this.response, page.getURI(), "EDIT_MODE", null, + .render(request, this.response, page.getURI(), null, "EDIT_MODE", null, String.valueOf(languageId), null, null); final PageView pageView = (PageView) ((ResponseEntityView) response.getEntity()).getEntity(); @@ -1471,7 +1471,7 @@ public void testShouldRenderContainers() /** - * methodToTest {@link PageResource#render(HttpServletRequest, HttpServletResponse, String, String, String, String, String, String)} + * methodToTest {@link PageResource#render(HttpServletRequest, HttpServletResponse, String, String, String, String, String, String, String)} * Given Scenario: Create a page with not LIVE version, then publish the page, and then update the page to crate a * new working version * ExpectedResult: Should return a LIVE attribute to true just in after the page is publish @@ -1492,7 +1492,7 @@ public void shouldReturnLIVE() when(initDataObject.getUser()).thenReturn(APILocator.systemUser()); Response response = pageResource - .render(request, this.response, page.getURI(), PageMode.PREVIEW_MODE.toString(), null, + .render(request, this.response, page.getURI(), null, PageMode.PREVIEW_MODE.toString(), null, String.valueOf(languageId), null, null); PageView pageView = (PageView) ((ResponseEntityView) response.getEntity()).getEntity(); @@ -1503,7 +1503,7 @@ public void shouldReturnLIVE() APILocator.getContentletAPI().publish(page, user, false); response = pageResource - .render(request, this.response, page.getURI(), PageMode.PREVIEW_MODE.toString(), null, + .render(request, this.response, page.getURI(), null, PageMode.PREVIEW_MODE.toString(), null, String.valueOf(languageId), null, null); pageView = (PageView) ((ResponseEntityView) response.getEntity()).getEntity(); @@ -1514,7 +1514,7 @@ public void shouldReturnLIVE() APILocator.getContentletAPI().checkin(checkout, user, false); response = pageResource - .render(request, this.response, page.getURI(), PageMode.PREVIEW_MODE.toString(), null, + .render(request, this.response, page.getURI(), null, PageMode.PREVIEW_MODE.toString(), null, String.valueOf(languageId), null, null); pageView = (PageView) ((ResponseEntityView) response.getEntity()).getEntity(); @@ -1574,7 +1574,7 @@ public void shouldKeepTheAParserContainerContentAfterLayoutSaved() APILocator.getMultiTreeAPI().saveMultiTree(multiTree); final Response response = pageResource - .render(request, this.response, page.getURI(), modeParam, null, + .render(request, this.response, page.getURI(), null, modeParam, null, String.valueOf(languageId), null, null); final HTMLPageAssetRendered htmlPageAssetRendered = (HTMLPageAssetRendered) ((ResponseEntityView) response.getEntity()).getEntity(); @@ -1681,7 +1681,7 @@ public void shouldResponseWith() APILocator.getMultiTreeAPI().saveMultiTree(multiTree); final Response response = pageResource - .render(request, this.response, page.getURI(), modeParam, null, + .render(request, this.response, page.getURI(), null, modeParam, null, String.valueOf(languageId), null, null); final HTMLPageAssetRendered htmlPageAssetRendered = (HTMLPageAssetRendered) ((ResponseEntityView) response.getEntity()).getEntity(); @@ -1715,7 +1715,7 @@ private PageContainerForm createPageContainerForm(final String containerId, fina /** * <ul> - * <li><b>Method to Test:</b> {@link PageResource#render(HttpServletRequest, HttpServletResponse, String, String, String, String, String, String)}</li> + * <li><b>Method to Test:</b> {@link PageResource#render(HttpServletRequest, HttpServletResponse, String, String, String, String, String, String, String)}</li> * <li><b>Given Scenario:</b> In Edit Mode, test the rest API</li> * <li><b>Expected Result:</b> Receive the on-number-of-pages data attribute for the contentlet object inside rendered element.</li> * </ul> @@ -1736,7 +1736,7 @@ public void testOnNumberOfPagesDataAttribute_render() throws DotDataException, S final HTMLPageAsset pageOne = pageRenderTestOne.getPage(); final Container container = pageRenderTestOne.getFirstContainer(); final Contentlet testContent = pageRenderTestOne.addContent(container); - Response pageResponse = this.pageResource.render(this.request, this.response, pageOne.getURI(), modeParam, null, + Response pageResponse = this.pageResource.render(this.request, this.response, pageOne.getURI(), null, modeParam, null, String.valueOf(languageId), null, null); final HTMLPageAssetRendered htmlPageAssetRendered = @@ -1749,7 +1749,7 @@ public void testOnNumberOfPagesDataAttribute_render() throws DotDataException, S /** * <ul> - * <li><b>Method to Test:</b> {@link PageResource#render(HttpServletRequest, HttpServletResponse, String, String, String, String, String, String)}</li> + * <li><b>Method to Test:</b> {@link PageResource#render(HttpServletRequest, HttpServletResponse, String, String, String, String, String, String, String)}</li> * <li><b>Given Scenario:</b> The deviceInode is not set as part of the request</li> * <li><b>Expected Result:</b> The {@link WebKeys#CURRENT_DEVICE} is removed from session</li> * </ul> @@ -1758,14 +1758,14 @@ public void testOnNumberOfPagesDataAttribute_render() throws DotDataException, S public void testCleanUpSessionWhenDeviceInodeIsNull() throws Exception { when(request.getAttribute(com.liferay.portal.util.WebKeys.USER)).thenReturn(user); - pageResource.render(request, response, pagePath, null, null, APILocator.getLanguageAPI().getDefaultLanguage().getLanguage(), null, null); + pageResource.render(request, response, pagePath, null, null, null, APILocator.getLanguageAPI().getDefaultLanguage().getLanguage(), null, null); verify(session).removeAttribute(WebKeys.CURRENT_DEVICE); } /** * <ul> - * <li><b>Method to Test:</b> {@link PageResource#render(HttpServletRequest, HttpServletResponse, String, String, String, String, String, String)}</li> + * <li><b>Method to Test:</b> {@link PageResource#render(HttpServletRequest, HttpServletResponse, String, String, String, String, String, String, String)}</li> * <li><b>Given Scenario:</b> The deviceInode in the request is blank</li> * <li><b>Expected Result:</b> The {@link WebKeys#CURRENT_DEVICE} is removed from session</li> * </ul> @@ -1774,7 +1774,7 @@ public void testCleanUpSessionWhenDeviceInodeIsNull() throws Exception { public void testCleanUpSessionWhenDeviceInodeIsBlank() throws Exception { when(request.getAttribute(com.liferay.portal.util.WebKeys.USER)).thenReturn(user); - pageResource.render(request, response, pagePath, null, null, APILocator.getLanguageAPI().getDefaultLanguage().getLanguage(), "", null); + pageResource.render(request, response, pagePath, null, null, null, APILocator.getLanguageAPI().getDefaultLanguage().getLanguage(), "", null); verify(session).removeAttribute(WebKeys.CURRENT_DEVICE); } @@ -1986,7 +1986,7 @@ private void validatePageRendering(final PageMode mode, final boolean expectCont final String myPagePath = String.format("/%s/%s", myFolderName, myPageName); final Response myResponse = pageResource - .loadJson(this.request, this.response, myPagePath, mode.name(), null, + .loadJson(this.request, this.response, myPagePath, null, mode.name(), null, String.valueOf(languageId), null, futureIso8601); RestUtilTest.verifySuccessResponse(myResponse); @@ -2073,7 +2073,7 @@ public void TestRenderWithTimeMachineUsingContainers() private void validatePageContents(final String pageUri, final String futureTimeMachineIso8601, final String expectedTitle, final boolean live) throws DotDataException, DotSecurityException { final Response endpointResponse = pageResource - .loadJson(this.request, this.response, pageUri, PageMode.LIVE.name(), null, + .loadJson(this.request, this.response, pageUri, null, PageMode.LIVE.name(), null, "1", null, futureTimeMachineIso8601); RestUtilTest.verifySuccessResponse(endpointResponse); @@ -2387,7 +2387,7 @@ public void Test_Rendering_Working_Content_Using_Limited_User() throws Exception addPermission(host, user, PermissionAPI.INDIVIDUAL_PERMISSION_TYPE, PermissionAPI.PERMISSION_READ); final Response endpointResponse = pageResource - .loadJson(this.request, this.response, pageInfo.pageUri, PageMode.LIVE.name(), null, + .loadJson(this.request, this.response, pageInfo.pageUri, null, PageMode.LIVE.name(), null, "1", null, matchingFutureIso8601); RestUtilTest.verifySuccessResponse(endpointResponse); @@ -2651,7 +2651,7 @@ public void TestPageWithFutureDateShowsCorrectVersions() throws Exception { // Test: PageMode.LIVE with future date before scheduled publication final Response pareResponse = pageResource - .loadJson(this.request, this.response, pageInfo.pageUri, PageMode.LIVE.name(), null, + .loadJson(this.request, this.response, pageInfo.pageUri, null, PageMode.LIVE.name(), null, "1", null, queryDateIso8601); final PageView pageView = PageScenarioUtils.extractPageViewFromResponse(pareResponse); @@ -2707,7 +2707,7 @@ public void TestPageWithExpiredContentNotShowing() throws Exception{ addPermission(host, user, PermissionAPI.INDIVIDUAL_PERMISSION_TYPE, PermissionAPI.PERMISSION_READ); final Response noPublishDateResponse = pageResource - .loadJson(this.request, this.response, pageInfo.pageUri, PageMode.LIVE.name(), null, + .loadJson(this.request, this.response, pageInfo.pageUri, null, PageMode.LIVE.name(), null, "1", null, null); //When no publish date is passed, we should get all contentlets that are valid! @@ -2715,7 +2715,7 @@ public void TestPageWithExpiredContentNotShowing() throws Exception{ validateNoContentlets(noPublishDateResponse)); final Response withFutureDatePassed = pageResource - .loadJson(this.request, this.response, pageInfo.pageUri, PageMode.LIVE.name(), null, + .loadJson(this.request, this.response, pageInfo.pageUri, null, PageMode.LIVE.name(), null, "1", null, matchingFutureIso8601); //When publish date is passed, we should still get only valid content since the base case only created expired content in the past, so we should only get valid content @@ -2754,7 +2754,7 @@ public void TestPageWithOverlappingTimeRanges() throws Exception{ // Test with current date - should only show valid content final Response currentDateResponse = pageResource - .loadJson(this.request, this.response, pageInfo.pageUri, PageMode.LIVE.name(), null, + .loadJson(this.request, this.response, pageInfo.pageUri, null, PageMode.LIVE.name(), null, "1", null, matchingFutureIso8601); final PageView pageView = extractPageViewFromResponse(currentDateResponse); @@ -2922,7 +2922,7 @@ public void render_inEditMode_withSchemaContentType_returnsStyleEditorSchemas() .thenReturn(APILocator.systemUser()); final Response response = pageResource - .render(this.request, this.response, pageRenderTest.getPage().getURI(), + .render(this.request, this.response, pageRenderTest.getPage().getURI(), null, PageMode.EDIT_MODE.name(), null, "1", null, null); final PageView pageView = (PageView) ((ResponseEntityView<?>) response.getEntity()).getEntity(); @@ -2976,7 +2976,7 @@ public void render_inNonEditMode_withSchemaContentType_doesNotReturnStyleEditorS .thenReturn(APILocator.systemUser()); final Response response = pageResource - .render(this.request, this.response, pageRenderTest.getPage().getURI(), + .render(this.request, this.response, pageRenderTest.getPage().getURI(), null, PageMode.LIVE.name(), null, "1", null, null); final PageView pageView = (PageView) ((ResponseEntityView<?>) response.getEntity()).getEntity();