From bf7158d7e32e76566afdb1276cdd6cc28992265b Mon Sep 17 00:00:00 2001 From: ooiyeefei <12832335+ooiyeefei@users.noreply.github.com> Date: Fri, 16 Oct 2026 12:35:47 +0000 Subject: [PATCH 1/2] Add reviewable Excalidraw diagrams and local MCP integration --- .github/workflows/test.yml | 2 + .gitignore | 3 + CHANGELOG.md | 15 + README.md | 56 +- bin/htmldrop.js | 15 + .../2026-10-16-excalidraw-milestone-2.md | 49 + package-lock.json | 3837 ++++++++++++++++- package.json | 28 +- scripts/build-editor.mjs | 61 + src/diagrams/document.js | 250 ++ src/diagrams/service.js | 282 ++ src/edit/browser/editor.css | 220 + src/edit/browser/editor.jsx | 897 ++++ src/edit/browser/notices/Assistant.txt | 11 + src/edit/browser/notices/Cascadia.txt | 34 + src/edit/browser/notices/ComicShanns.txt | 89 + src/edit/browser/notices/Excalidraw.txt | 21 + src/edit/browser/notices/Excalifont.txt | 117 + src/edit/browser/notices/GPL-2.0.txt | 338 ++ .../browser/notices/Liberation-LICENSE.txt | 55 + src/edit/browser/notices/Liberation.txt | 18 + src/edit/browser/notices/Lilita.txt | 10 + src/edit/browser/notices/Nunito.txt | 10 + src/edit/browser/notices/SIL-OFL-1.1.txt | 85 + src/edit/browser/notices/Virgil.txt | 106 + src/edit/browser/notices/Xiaolai.txt | 32 + src/edit/diagrams.js | 72 + src/edit/editor-assets.js | 92 + src/edit/remote.js | 4 +- src/edit/runtime.js | 4 +- src/edit/server.js | 25 +- src/mcp/index.js | 36 + src/mcp/server.js | 69 + templates/annotation-widget.html | 139 +- test/diagram-http.test.mjs | 104 + test/diagram-publication.test.mjs | 78 + test/diagram-uat.test.mjs | 488 +++ test/diagrams-core.test.mjs | 277 ++ test/mcp.test.mjs | 113 + worker/src/annotation-widget.html | 139 +- worker/src/index.ts | 5 +- worker/src/schema.ts | 13 +- 42 files changed, 8063 insertions(+), 236 deletions(-) create mode 100644 docs/plans/2026-10-16-excalidraw-milestone-2.md create mode 100644 scripts/build-editor.mjs create mode 100644 src/diagrams/document.js create mode 100644 src/diagrams/service.js create mode 100644 src/edit/browser/editor.css create mode 100644 src/edit/browser/editor.jsx create mode 100644 src/edit/browser/notices/Assistant.txt create mode 100644 src/edit/browser/notices/Cascadia.txt create mode 100644 src/edit/browser/notices/ComicShanns.txt create mode 100644 src/edit/browser/notices/Excalidraw.txt create mode 100644 src/edit/browser/notices/Excalifont.txt create mode 100644 src/edit/browser/notices/GPL-2.0.txt create mode 100644 src/edit/browser/notices/Liberation-LICENSE.txt create mode 100644 src/edit/browser/notices/Liberation.txt create mode 100644 src/edit/browser/notices/Lilita.txt create mode 100644 src/edit/browser/notices/Nunito.txt create mode 100644 src/edit/browser/notices/SIL-OFL-1.1.txt create mode 100644 src/edit/browser/notices/Virgil.txt create mode 100644 src/edit/browser/notices/Xiaolai.txt create mode 100644 src/edit/diagrams.js create mode 100644 src/edit/editor-assets.js create mode 100644 src/mcp/index.js create mode 100644 src/mcp/server.js create mode 100644 test/diagram-http.test.mjs create mode 100644 test/diagram-publication.test.mjs create mode 100644 test/diagram-uat.test.mjs create mode 100644 test/diagrams-core.test.mjs create mode 100644 test/mcp.test.mjs diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index a2b6933..90a521a 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -32,6 +32,8 @@ jobs: path: | .scratch/m1-uat/run-*/*.png .scratch/m1-uat/run-*/*-failure.json + .scratch/m2-uat/run-*/*.png + .scratch/m2-uat/run-*/*-failure.json if-no-files-found: ignore retention-days: 7 - run: npm pack --dry-run diff --git a/.gitignore b/.gitignore index 43652ff..1f30772 100644 --- a/.gitignore +++ b/.gitignore @@ -35,3 +35,6 @@ docs/demo/.stage/ # demo video renders, published to YouTube instead artifacts/ + +# Reproducible packaged Excalidraw browser build (generated by pretest/prepack) +src/edit/editor-dist/ diff --git a/CHANGELOG.md b/CHANGELOG.md index 66a053e..931a4f7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,21 @@ All notable changes to this project are documented here. The format is based on [Keep a Changelog](https://keepachangelog.com/), and this project adheres to [Semantic Versioning](https://semver.org/). +## [1.17.0] — 2026-10-16 + +### Added + +- A locally bundled Excalidraw editor in edit mode, with native scene and SVG export, JSON import and one-way Mermaid import. +- Shape-attached comment threads in the editor and on published feedback pages, retaining stable IDs and revision context when shapes move or are removed. +- Owner-reviewed scene proposals, accepted diagram revisions, and stale-source/revision conflict detection shared by browser and agents. +- A local stdio MCP server bound to one registered artifact, with reviewer tools and explicit owner create/save/accept/reject tools. +- Accepted scenes and static SVG previews embedded in self-contained HTML, surviving publish/pull and automatic encrypted publication history. Private local diagram history remains outside published staging. + +### Changed + +- Diagram editor assets load on demand from the installed package; editor CSP restricts external resources and web embeds are disabled. +- Source reload waits while the diagram editor is open, so accepted source changes do not silently navigate away from an active canvas draft. + ## [1.16.0] — 2026-10-16 ### Added diff --git a/README.md b/README.md index cc5fefa..1eff0c9 100644 --- a/README.md +++ b/README.md @@ -36,6 +36,7 @@ _Click the diagram for the interactive release note._ - [Example walkthroughs](#example-walkthroughs) - [The agent loop](#the-agent-loop) - [Edit mode (local, pre-publish)](#edit-mode-local-pre-publish) +- [Excalidraw diagrams and MCP](#excalidraw-diagrams-and-mcp) - [Multi-provider AI (Anthropic / OpenAI / Gemini)](#multi-provider-ai) - [Architecture](#architecture) - [Security model](#security-model) @@ -275,8 +276,9 @@ since the password gate uses built-in WebCrypto. ## Release notes -Each release ships a short, visual release note — published with htmldrop itself, so you can read it as a live page: +Release summaries are published on [GitHub Releases](https://github.com/ooiyeefei/htmldrop/releases). The complete change history is in [CHANGELOG.md](CHANGELOG.md). Earlier visual release notes remain available below: +- **v1.16.0** — automatic history and redlines on every publish, safe declared sibling assets, expiring private HTTPS review and a layout inbox. → [Release notes](https://github.com/ooiyeefei/htmldrop/releases/tag/v1.16.0) - **v1.10.0** — playbooks, a one-command design starter kit, smarter edit-mode guidance, and `htmldrop pull` + shared team identity (hand a doc off to a teammate, no git). → **[Read the release note](https://yooi.surge.sh/htmldrop-v1.10.0.html)** --- @@ -531,7 +533,7 @@ Unchanged source is deduplicated. Content or protection changes create a revisio For an existing feedback link, password protection is registered before deploying its new page, including when feedback is turned off. If deployment then fails, that remote review link stays protected and its previous password capability may stop working. Retry the same push to synchronize it; the CLI reports this partial transition explicitly. It never automatically reopens private access to recover a failed deploy. -For isolated profiles or test servers, set `HTMLDROP_CONFIG_DIR` to an explicit state directory; the default remains `~/.htmldrop`. Excalidraw editing, shape comments and MCP are planned together for milestone 2 and are not included in this release. +For isolated profiles or test servers, set `HTMLDROP_CONFIG_DIR` to an explicit state directory; the default remains `~/.htmldrop`. Excalidraw editing, shape comments and local MCP are included; see [Excalidraw diagrams and MCP](#excalidraw-diagrams-and-mcp). --- @@ -574,6 +576,56 @@ It’s symmetric: when your teammate publishes *their* doc, they’re the owner --- +## Excalidraw diagrams and MCP + +Edit mode includes a locally bundled Excalidraw canvas. Open **Diagrams** to create a diagram, import native `.excalidraw` JSON, or import supported Mermaid as new editable shapes. Select a diagram, draw or move shapes, then **Save diagram**. Saves update the local HTML; they do not publish it. + +The HTML embeds the accepted native scene, stable document/diagram/shape identifiers and a static SVG image. A published page remains readable without React, Excalidraw or an editor service. With `--feedback`, readers can select a shape beside the preview and comment; the anchor keeps the shape ID across moves and text changes. Removed shapes retain their threads with a visible removed label. A comment records the revision it referred to. + +### Proposals and diagram revisions + +Private edit reviewers can change a canvas and **Propose changes**. The accepted page stays unchanged until the local owner accepts the proposal. Owners can accept or reject in the editor; acceptance renders a fresh preview from the proposed scene. Conflicting source or diagram changes return an explicit conflict rather than replacing someone else's work. Export a conflicting draft before reopening the latest diagram. + +The owner editor lists local accepted diagram revisions and can export each as native JSON or its corresponding SVG preview. Reviewers see only the current accepted revision and proposals based on the current source, so a newly shared review link cannot reveal removed content from older local drafts. This local diagram history is separate from the automatic history of published HTML versions. Current accepted scene/IDs travel with the HTML and survive `pull`; private local proposals and unpublished diagram revisions remain in the original profile. Earlier published diagram source remains available through published document history. + +Incoming comments refresh without discarding unsent text. Source-file reloads are deferred while the diagram editor is open; close or save the diagram before reloading the surrounding page. The canvas loads only when opened. Its editor code and fonts are packaged locally, with an editor CSP restricting network access; there is no required CDN, Excalidraw+ account or hosted collaboration service. + +### Connect a local MCP agent + +First register the HTML artifact in edit mode: + +```bash +htmldrop edit start /absolute/path/report.html --no-open +htmldrop mcp --artifact /absolute/path/report.html --role owner +``` + +The MCP command speaks stdio JSON-RPC and stays running for its client. Configure your client's stdio MCP server with: + +```json +{ + "mcpServers": { + "htmldrop": { + "command": "htmldrop", + "args": ["mcp", "--artifact", "/absolute/path/report.html", "--role", "owner"] + } + } +} +``` + +Each connection binds one already-registered artifact. Tools cannot choose arbitrary files, URLs or another session. The default role is `reviewer`: read the current scene/comments, inspect current proposals, propose changes, comment on a shape and export the current revision. Explicit `--role owner` additionally exposes create, save, accept and reject. Neither role publishes the HTML or exposes edit-server controls. + +Read `diagram_list`, then `diagram_read` for the current IDs and source hash. Mutations require the returned `baseRevisionId` and `baseSourceHash` (creation needs only the source hash). Resources expose the bound artifact's diagram list and revisions. Native JSON export needs no browser; an accepted save requires a safe SVG preview rendered from that scene, and SVG export returns the stored preview for that exact revision. The owner acceptance UI can render a proposal that has no preview. MCP does not invent a preview or silently reuse an older one. + +### First-release boundaries + +- Publication still requires self-contained HTML; diagram images must be embedded PNG, JPEG, WebP or GIF data. Web embeds and shape links are disabled. +- The full accepted source must stay within the existing 2 MB HTML limit; reduce embedded images or diagram size if a save is refused. +- Mermaid import is one-way. There is no claim of arbitrary lossless canvas-to-Mermaid conversion. +- MCP is local stdio; private browser review uses `edit share`. Public remote MCP and simultaneous multiplayer are separate work. +- `push --password` encrypts accepted diagram source and published history under the existing password model. Local diagram revision/proposal archives use the local profile's encryption key; keep the profile private. Publishing does not upload those local archives. + +--- + ## Multi-provider AI > ### The AI key is optional — and bring-your-own diff --git a/bin/htmldrop.js b/bin/htmldrop.js index 63a78aa..1201fdc 100755 --- a/bin/htmldrop.js +++ b/bin/htmldrop.js @@ -34,6 +34,21 @@ program .description('Publish HTML files and get shareable links via Surge.sh') .version(pkg.version); +program + .command('mcp') + .description('Serve diagram tools over local stdio for one registered HTML artifact') + .requiredOption('--artifact ', 'HTML file already registered with htmldrop edit start') + .option('--role ', 'reviewer or owner', 'reviewer') + .action(async (options) => { + try { + const { startMcp } = await import('../src/mcp/index.js'); + await startMcp(options); + } catch (err) { + console.error(`Error: ${err.message}`); + process.exitCode = 1; + } + }); + program .command('init') .description('Set up htmldrop (login to Surge and pick a subdomain)') diff --git a/docs/plans/2026-10-16-excalidraw-milestone-2.md b/docs/plans/2026-10-16-excalidraw-milestone-2.md new file mode 100644 index 0000000..af7053d --- /dev/null +++ b/docs/plans/2026-10-16-excalidraw-milestone-2.md @@ -0,0 +1,49 @@ +# Excalidraw milestone 2 + +Deliver editable Excalidraw diagrams, comments attached to stable shapes, reviewable diagram revisions/proposals, and a local stdio MCP server together. Automatic publication history remains independent of optional feedback. No blog post, simultaneous multiplayer, publicly hosted remote MCP, or arbitrary lossless canvas-to-Mermaid conversion. + +## Shared contract (implementation boundary) + +Accepted diagrams are authoritative inside the HTML source. Use one `` and managed blocks: + +```html + +
+ Diagram title + +
+ +``` + +Embedded JSON: `{version:1, documentId, diagramId, revisionId, parentRevisionId, title, scene}`. `scene` is native `{type:'excalidraw',version:2,elements,appState,files}`. Escape `<` in JSON. UUID identifiers are server-assigned except stable Excalidraw element IDs. Preserve comments for deleted shapes, visibly marking them removed. Managed scenes and SVG previews survive single-file publication and pull. Full diagram edit history and pending proposals stay in private local profile state; published document history retains earlier published diagram source. + +Shared service (browser and MCP call the same HTTP adapter): + +- `GET /api/edit/:key/diagrams` -> `{documentId,sourceHash,diagrams:[{diagramId,revisionId,title}],permissions:{create,save,accept,propose}}`. +- `POST /api/edit/:key/diagrams` owner create -> body `{baseSourceHash,title,scene,svg,summary}`; returns read payload. +- `GET /api/edit/:key/diagrams/:diagramId` -> `{documentId,diagramId,revisionId,parentRevisionId,sourceHash,title,scene,svg,proposals,revisions,permissions}`. +- `POST .../:diagramId/save` -> `{baseRevisionId,baseSourceHash,title,scene,svg,summary}`. +- `POST .../:diagramId/proposals` -> same fields but SVG optional. Immutable proposal; no source write. +- `POST .../:diagramId/proposals/:proposalId/accept` -> `{baseRevisionId,baseSourceHash,svg,summary}`; acceptance uses stored candidate scene and its original base, generates new revision. UI regenerates SVG using Excalidraw before accept. +- `POST .../:diagramId/proposals/:proposalId/reject` -> `{baseRevisionId,baseSourceHash}`. +- `GET .../:diagramId/export?format=excalidraw|svg&revisionId=...` -> JSON `{format,content,mimeType,revisionId}`. SVG is the validated stored preview for precisely that revision. Never return stale preview. +- Existing `/api/feedback/:key` threads accept `anchor:{type:'diagram_shape',documentId,diagramId,shapeId,revisionId}`. MCP comment mutation supplies baseRevision/sourceHash and HTTP adapter checks them before inserting via existing store. +- `POST .../:diagramId/comments` -> `{baseRevisionId,baseSourceHash,shapeId,text,authorName}`; validates shape membership and returns created comment. `GET .../:diagramId/comments` returns `{items}` filtered to diagram. + +Actor comes from authenticated transport, never body fields. Same-origin local author browser can save; remote gateway reviewer cannot create/save/accept/reject at both gateway and backend. Reviewer can read/propose/export/comment for the current revision. Reviewer projections must omit older local scenes and outdated proposals; noncurrent revision export is owner-only, preventing removed confidential content from leaking through a new share. CLI/stdio agent controls keep their separate local discovery credential. Every mutation checks current diagram revision plus source SHA256 inside a per-file serialized mutation. Stale writes and proposals return409 with actionable conflict state; no silent overwrite. Preserve source around managed block and use temp+rename. Recheck source bytes immediately before rename to detect external edits. API errors carry `statusCode` and safe `message`. + +## Module ownership + +- `src/diagrams/document.js`, `src/diagrams/service.js` (core lane): parse/validate/serialize accepted document blocks, safe SVG preview, bounded native scenes, atomic serialized source writes, proposal and revision state. Export `createDiagramService({getSession,configDir})`. Returned methods: `list(key,actor)`, `read(key,id,actor)`, `create(key,body,actor)`, `save(key,id,body,actor)`, `propose(key,id,body,actor)`, `accept(key,id,proposalId,body,actor)`, `reject(key,id,proposalId,body,actor)`, `exportDiagram(key,id,{format,revisionId},actor)`, `checkAnchor(key,id,body,actor)` (validates base+shape, returns canonical anchor). `actor` is `'owner'|'reviewer'`. No source writes for read/list/export/comment checks. Parent can add optional nonbreaking exports as needed. +- `src/edit/diagrams.js`, edit/server.js + remote.js, templates/annotation-widget.html + worker mirror and worker/src/schema.ts (HTTP/annotation lane): route shared service, comments validation and highlighting, SSE, gateway permissions. +- `src/edit/browser/`, `src/edit/editor-assets.js`, `scripts/build-editor.mjs` (editor lane): pinned locally built Excalidraw, lazy iframe editor, diagram toolbar in injected edit runtime, selection/comments, revision/proposal controls, JSON/SVG import/export and optional one-way Mermaid import. No CDN or runtime external requests. Dist files `src/edit/editor-dist/` are reproducible build outputs (not committed), included in npm tarball via prepack. +- `src/mcp/`, Commander command in bin/htmldrop.js and test/mcp.test.mjs (MCP lane): SDK1.32.1 + Zod4.6.7 preserves Node>=18. `htmldrop mcp --artifact /abs/file.html --role reviewer|owner` binds an explicitly registered HTML session, default reviewer. No per-tool path/URL/role arguments. Tools/resources expose list/read/create(owner)/save(owner)/propose/accept(owner)/reject(owner)/history/comments/export. Stdout is JSON-RPC only. +- Parent owns package.json/lock, plan/docs/release integration, final review and verification. No concurrent edits to another lane's owned files; coordinate small seams through parent. + +## Rendering and limits + +Native browser `exportToSvg` is authoritative for graphical rendering. Require fresh SVG on accepted writes; proposals can omit it. MCP exports native scene JSON without a renderer; scene save requires caller-provided safe SVG, with a clear error if absent. The owner acceptance UI regenerates reviewer previews. Embed as SVG data-URL image, never active inline SVG. Reject scripts, event handlers, foreignObject, external URLs, unsafe CSS, invalid XML, ambiguous markers/IDs and unsupported scenes. Raster images are bounded data URLs only; no SVG attachments or web embeds. Enforce finite geometry, unique shape IDs, supported types, per-field/file/scene/HTML byte and element limits. Disabled editor embeds/AI/library links must not cause outbound requests. CSP constrains all editor assets/fonts/network to self/data/blob as needed. + +## Acceptance evidence + +Execute real browser Excalidraw edits and shape selection/comment/replies, proposal/accept/reject, revision preview/export, stale conflict and remote owner-route denial. Actual SDK Client/StdioClientTransport must discover/call tools and read resources without protocol stdout pollution. Verify scene+SVG single-file publish/pull, encrypted private history, deleted-shape anchors, SVG/JSON injection, source conflicts, asset isolation and lazy/offline browser loading. Run existing Node20/22 regression suites, Worker typecheck/dry build, npm pack/install checks, independent high-reasoning review, PR CI and runtime proof before release. Distinguish built/tested/merged/deployed/registry-available proof. Preserve dirty original main checkout and existing shared daemons. diff --git a/package-lock.json b/package-lock.json index f6bdf7a..55e755b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,24 +1,32 @@ { "name": "@yeefeiooi/htmldrop", - "version": "1.16.0", + "version": "1.17.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@yeefeiooi/htmldrop", - "version": "1.16.0", + "version": "1.17.0", "license": "MIT", "dependencies": { + "@modelcontextprotocol/sdk": "1.32.1", "commander": "^12.1.0", "crypto-js": "^4.2.0", - "open": "^10.1.0" + "open": "^10.1.0", + "parse5": "7.3.0", + "zod": "4.6.7" }, "bin": { "htmldrop": "bin/htmldrop.js" }, "devDependencies": { + "@excalidraw/excalidraw": "0.18.1", + "@excalidraw/mermaid-to-excalidraw": "2.2.2", "@mermaid-js/mermaid-cli": "11.4.2", - "playwright": "^1.61.1" + "esbuild": "0.25.12", + "playwright": "^1.61.1", + "react": "18.3.1", + "react-dom": "18.3.1" }, "engines": { "node": ">=18" @@ -73,6 +81,16 @@ "node": ">=6.9.0" } }, + "node_modules/@babel/runtime": { + "version": "7.29.10", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.29.10.tgz", + "integrity": "sha512-NpugTzbKgGrNe45Ui32cN8vCFFMxVtP/9rtfwv0nP5FRDAjvYBxJcLVpYA9mr1jxP6cnIT7x9LUzuDTbWYYNRA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, "node_modules/@braintree/sanitize-url": { "version": "7.1.2", "resolved": "https://registry.npmjs.org/@braintree/sanitize-url/-/sanitize-url-7.1.2.tgz", @@ -80,12 +98,958 @@ "dev": true, "license": "MIT" }, - "node_modules/@chevrotain/types": { - "version": "11.1.2", - "resolved": "https://registry.npmjs.org/@chevrotain/types/-/types-11.1.2.tgz", - "integrity": "sha512-U+HFai5+zmJCkK86QsaJtoITlboZHBqrVketcO2ROv865xfCMSFpELQoz1GkX5GzME8pTa+3kbKrZHQtI0gdbw==", + "node_modules/@chevrotain/cst-dts-gen": { + "version": "11.0.3", + "resolved": "https://registry.npmjs.org/@chevrotain/cst-dts-gen/-/cst-dts-gen-11.0.3.tgz", + "integrity": "sha512-BvIKpRLeS/8UbfxXxgC33xOumsacaeCKAjAeLyOn7Pcp95HiRbrpl14S+9vaZLolnbssPIUuiUd8IvgkRyt6NQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@chevrotain/gast": "11.0.3", + "@chevrotain/types": "11.0.3", + "lodash-es": "4.17.21" + } + }, + "node_modules/@chevrotain/cst-dts-gen/node_modules/@chevrotain/types": { + "version": "11.0.3", + "resolved": "https://registry.npmjs.org/@chevrotain/types/-/types-11.0.3.tgz", + "integrity": "sha512-gsiM3G8b58kZC2HaWR50gu6Y1440cHiJ+i3JUvcp/35JchYejb2+5MVeJK0iKThYpAa/P2PYFV4hoi44HD+aHQ==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/@chevrotain/gast": { + "version": "11.0.3", + "resolved": "https://registry.npmjs.org/@chevrotain/gast/-/gast-11.0.3.tgz", + "integrity": "sha512-+qNfcoNk70PyS/uxmj3li5NiECO+2YKZZQMbmjTqRI3Qchu8Hig/Q9vgkHpI3alNjr7M+a2St5pw5w5F6NL5/Q==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@chevrotain/types": "11.0.3", + "lodash-es": "4.17.21" + } + }, + "node_modules/@chevrotain/gast/node_modules/@chevrotain/types": { + "version": "11.0.3", + "resolved": "https://registry.npmjs.org/@chevrotain/types/-/types-11.0.3.tgz", + "integrity": "sha512-gsiM3G8b58kZC2HaWR50gu6Y1440cHiJ+i3JUvcp/35JchYejb2+5MVeJK0iKThYpAa/P2PYFV4hoi44HD+aHQ==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/@chevrotain/regexp-to-ast": { + "version": "11.0.3", + "resolved": "https://registry.npmjs.org/@chevrotain/regexp-to-ast/-/regexp-to-ast-11.0.3.tgz", + "integrity": "sha512-1fMHaBZxLFvWI067AVbGJav1eRY7N8DDvYCTwGBiE/ytKBgP8azTdgyrKyWZ9Mfh09eHWb5PgTSO8wi7U824RA==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/@chevrotain/types": { + "version": "11.1.2", + "resolved": "https://registry.npmjs.org/@chevrotain/types/-/types-11.1.2.tgz", + "integrity": "sha512-U+HFai5+zmJCkK86QsaJtoITlboZHBqrVketcO2ROv865xfCMSFpELQoz1GkX5GzME8pTa+3kbKrZHQtI0gdbw==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/@chevrotain/utils": { + "version": "11.0.3", + "resolved": "https://registry.npmjs.org/@chevrotain/utils/-/utils-11.0.3.tgz", + "integrity": "sha512-YslZMgtJUyuMbZ+aKvfF3x1f5liK4mWNxghFRv7jqRR9C3R3fAOGTTKvxXDa2Y1s9zSbcpuO0cAxDYsc9SrXoQ==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.12.tgz", + "integrity": "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.12.tgz", + "integrity": "sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.12.tgz", + "integrity": "sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.12.tgz", + "integrity": "sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.12.tgz", + "integrity": "sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.12.tgz", + "integrity": "sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.12.tgz", + "integrity": "sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.12.tgz", + "integrity": "sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.12.tgz", + "integrity": "sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.12.tgz", + "integrity": "sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.12.tgz", + "integrity": "sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.12.tgz", + "integrity": "sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.12.tgz", + "integrity": "sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.12.tgz", + "integrity": "sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.12.tgz", + "integrity": "sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.12.tgz", + "integrity": "sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.12.tgz", + "integrity": "sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.12.tgz", + "integrity": "sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.12.tgz", + "integrity": "sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.12.tgz", + "integrity": "sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.12.tgz", + "integrity": "sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.12.tgz", + "integrity": "sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.12.tgz", + "integrity": "sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.12.tgz", + "integrity": "sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.12.tgz", + "integrity": "sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.12.tgz", + "integrity": "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@excalidraw/excalidraw": { + "version": "0.18.1", + "resolved": "https://registry.npmjs.org/@excalidraw/excalidraw/-/excalidraw-0.18.1.tgz", + "integrity": "sha512-6i5Gt7IDTOH//qa0Z315Ly5iVRhjWpu2whrlQFqkuwrkKUWgRsMk0P5qdE7bpyDpai7jeLeWYkyj1eVAfni1lw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@braintree/sanitize-url": "6.0.2", + "@excalidraw/laser-pointer": "1.3.1", + "@excalidraw/mermaid-to-excalidraw": "2.2.2", + "@excalidraw/random-username": "1.1.0", + "@radix-ui/react-popover": "1.1.6", + "@radix-ui/react-tabs": "1.0.2", + "browser-fs-access": "0.29.1", + "canvas-roundrect-polyfill": "0.0.1", + "clsx": "1.1.1", + "cross-env": "7.0.3", + "es6-promise-pool": "2.5.0", + "fractional-indexing": "3.2.0", + "fuzzy": "0.1.3", + "image-blob-reduce": "3.0.1", + "jotai": "2.11.0", + "jotai-scope": "0.7.2", + "lodash.debounce": "4.0.8", + "lodash.throttle": "4.1.1", + "nanoid": "3.3.3", + "open-color": "1.9.1", + "pako": "2.0.3", + "perfect-freehand": "1.2.0", + "pica": "7.1.1", + "png-chunk-text": "1.0.0", + "png-chunks-encode": "1.0.0", + "png-chunks-extract": "1.0.0", + "points-on-curve": "1.0.1", + "pwacompat": "2.0.17", + "roughjs": "4.6.4", + "sass": "1.51.0", + "tunnel-rat": "0.1.2" + }, + "peerDependencies": { + "react": "^17.0.2 || ^18.2.0 || ^19.0.0", + "react-dom": "^17.0.2 || ^18.2.0 || ^19.0.0" + } + }, + "node_modules/@excalidraw/excalidraw/node_modules/@braintree/sanitize-url": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/@braintree/sanitize-url/-/sanitize-url-6.0.2.tgz", + "integrity": "sha512-Tbsj02wXCbqGmzdnXNk0SOF19ChhRU70BsroIi4Pm6Ehp56in6vch94mfbdQ17DozxkL3BAVjbZ4Qc1a0HFRAg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@excalidraw/excalidraw/node_modules/@radix-ui/primitive": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.0.0.tgz", + "integrity": "sha512-3e7rn8FDMin4CgeL7Z/49smCA3rFYY3Ha2rUQ7HRWFadS5iCRw08ZgVT1LaNTCNqgvrUiyczLflrVrF0SRQtNA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.13.10" + } + }, + "node_modules/@excalidraw/excalidraw/node_modules/@radix-ui/react-tabs": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-tabs/-/react-tabs-1.0.2.tgz", + "integrity": "sha512-gOUwh+HbjCuL0UCo8kZ+kdUEG8QtpdO4sMQduJ34ZEz0r4922g9REOBM+vIsfwtGxSug4Yb1msJMJYN2Bk8TpQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/primitive": "1.0.0", + "@radix-ui/react-context": "1.0.0", + "@radix-ui/react-direction": "1.0.0", + "@radix-ui/react-id": "1.0.0", + "@radix-ui/react-presence": "1.0.0", + "@radix-ui/react-primitive": "1.0.1", + "@radix-ui/react-roving-focus": "1.0.2", + "@radix-ui/react-use-controllable-state": "1.0.0" + }, + "peerDependencies": { + "react": "^16.8 || ^17.0 || ^18.0", + "react-dom": "^16.8 || ^17.0 || ^18.0" + } + }, + "node_modules/@excalidraw/excalidraw/node_modules/@radix-ui/react-tabs/node_modules/@radix-ui/react-context": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.0.0.tgz", + "integrity": "sha512-1pVM9RfOQ+n/N5PJK33kRSKsr1glNxomxONs5c49MliinBY6Yw2Q995qfBUUo0/Mbg05B/sGA0gkgPI7kmSHBg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.13.10" + }, + "peerDependencies": { + "react": "^16.8 || ^17.0 || ^18.0" + } + }, + "node_modules/@excalidraw/excalidraw/node_modules/@radix-ui/react-tabs/node_modules/@radix-ui/react-direction": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-direction/-/react-direction-1.0.0.tgz", + "integrity": "sha512-2HV05lGUgYcA6xgLQ4BKPDmtL+QbIZYH5fCOTAOOcJ5O0QbWS3i9lKaurLzliYUDhORI2Qr3pyjhJh44lKA3rQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.13.10" + }, + "peerDependencies": { + "react": "^16.8 || ^17.0 || ^18.0" + } + }, + "node_modules/@excalidraw/excalidraw/node_modules/@radix-ui/react-tabs/node_modules/@radix-ui/react-id": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-id/-/react-id-1.0.0.tgz", + "integrity": "sha512-Q6iAB/U7Tq3NTolBBQbHTgclPmGWE3OlktGGqrClPozSw4vkQ1DfQAOtzgRPecKsMdJINE05iaoDUG8tRzCBjw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-use-layout-effect": "1.0.0" + }, + "peerDependencies": { + "react": "^16.8 || ^17.0 || ^18.0" + } + }, + "node_modules/@excalidraw/excalidraw/node_modules/@radix-ui/react-tabs/node_modules/@radix-ui/react-id/node_modules/@radix-ui/react-use-layout-effect": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.0.0.tgz", + "integrity": "sha512-6Tpkq+R6LOlmQb1R5NNETLG0B4YP0wc+klfXafpUCj6JGyaUc8il7/kUZ7m59rGbXGczE9Bs+iz2qloqsZBduQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.13.10" + }, + "peerDependencies": { + "react": "^16.8 || ^17.0 || ^18.0" + } + }, + "node_modules/@excalidraw/excalidraw/node_modules/@radix-ui/react-tabs/node_modules/@radix-ui/react-presence": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.0.0.tgz", + "integrity": "sha512-A+6XEvN01NfVWiKu38ybawfHsBjWum42MRPnEuqPsBZ4eV7e/7K321B5VgYMPv3Xx5An6o1/l9ZuDBgmcmWK3w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-compose-refs": "1.0.0", + "@radix-ui/react-use-layout-effect": "1.0.0" + }, + "peerDependencies": { + "react": "^16.8 || ^17.0 || ^18.0", + "react-dom": "^16.8 || ^17.0 || ^18.0" + } + }, + "node_modules/@excalidraw/excalidraw/node_modules/@radix-ui/react-tabs/node_modules/@radix-ui/react-presence/node_modules/@radix-ui/react-compose-refs": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.0.0.tgz", + "integrity": "sha512-0KaSv6sx787/hK3eF53iOkiSLwAGlFMx5lotrqD2pTjB18KbybKoEIgkNZTKC60YECDQTKGTRcDBILwZVqVKvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.13.10" + }, + "peerDependencies": { + "react": "^16.8 || ^17.0 || ^18.0" + } + }, + "node_modules/@excalidraw/excalidraw/node_modules/@radix-ui/react-tabs/node_modules/@radix-ui/react-presence/node_modules/@radix-ui/react-use-layout-effect": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.0.0.tgz", + "integrity": "sha512-6Tpkq+R6LOlmQb1R5NNETLG0B4YP0wc+klfXafpUCj6JGyaUc8il7/kUZ7m59rGbXGczE9Bs+iz2qloqsZBduQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.13.10" + }, + "peerDependencies": { + "react": "^16.8 || ^17.0 || ^18.0" + } + }, + "node_modules/@excalidraw/excalidraw/node_modules/@radix-ui/react-tabs/node_modules/@radix-ui/react-primitive": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-1.0.1.tgz", + "integrity": "sha512-fHbmislWVkZaIdeF6GZxF0A/NH/3BjrGIYj+Ae6eTmTCr7EB0RQAAVEiqsXK6p3/JcRqVSBQoceZroj30Jj3XA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-slot": "1.0.1" + }, + "peerDependencies": { + "react": "^16.8 || ^17.0 || ^18.0", + "react-dom": "^16.8 || ^17.0 || ^18.0" + } + }, + "node_modules/@excalidraw/excalidraw/node_modules/@radix-ui/react-tabs/node_modules/@radix-ui/react-primitive/node_modules/@radix-ui/react-slot": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.0.1.tgz", + "integrity": "sha512-avutXAFL1ehGvAXtPquu0YK5oz6ctS474iM3vNGQIkswrVhdrS52e3uoMQBzZhNRAIE0jBnUyXWNmSjGHhCFcw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-compose-refs": "1.0.0" + }, + "peerDependencies": { + "react": "^16.8 || ^17.0 || ^18.0" + } + }, + "node_modules/@excalidraw/excalidraw/node_modules/@radix-ui/react-tabs/node_modules/@radix-ui/react-primitive/node_modules/@radix-ui/react-slot/node_modules/@radix-ui/react-compose-refs": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.0.0.tgz", + "integrity": "sha512-0KaSv6sx787/hK3eF53iOkiSLwAGlFMx5lotrqD2pTjB18KbybKoEIgkNZTKC60YECDQTKGTRcDBILwZVqVKvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.13.10" + }, + "peerDependencies": { + "react": "^16.8 || ^17.0 || ^18.0" + } + }, + "node_modules/@excalidraw/excalidraw/node_modules/@radix-ui/react-tabs/node_modules/@radix-ui/react-roving-focus": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-roving-focus/-/react-roving-focus-1.0.2.tgz", + "integrity": "sha512-HLK+CqD/8pN6GfJm3U+cqpqhSKYAWiOJDe+A+8MfxBnOue39QEeMa43csUn2CXCHQT0/mewh1LrrG4tfkM9DMA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/primitive": "1.0.0", + "@radix-ui/react-collection": "1.0.1", + "@radix-ui/react-compose-refs": "1.0.0", + "@radix-ui/react-context": "1.0.0", + "@radix-ui/react-direction": "1.0.0", + "@radix-ui/react-id": "1.0.0", + "@radix-ui/react-primitive": "1.0.1", + "@radix-ui/react-use-callback-ref": "1.0.0", + "@radix-ui/react-use-controllable-state": "1.0.0" + }, + "peerDependencies": { + "react": "^16.8 || ^17.0 || ^18.0", + "react-dom": "^16.8 || ^17.0 || ^18.0" + } + }, + "node_modules/@excalidraw/excalidraw/node_modules/@radix-ui/react-tabs/node_modules/@radix-ui/react-roving-focus/node_modules/@radix-ui/react-collection": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-collection/-/react-collection-1.0.1.tgz", + "integrity": "sha512-uuiFbs+YCKjn3X1DTSx9G7BHApu4GHbi3kgiwsnFUbOKCrwejAJv4eE4Vc8C0Oaxt9T0aV4ox0WCOdx+39Xo+g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-compose-refs": "1.0.0", + "@radix-ui/react-context": "1.0.0", + "@radix-ui/react-primitive": "1.0.1", + "@radix-ui/react-slot": "1.0.1" + }, + "peerDependencies": { + "react": "^16.8 || ^17.0 || ^18.0", + "react-dom": "^16.8 || ^17.0 || ^18.0" + } + }, + "node_modules/@excalidraw/excalidraw/node_modules/@radix-ui/react-tabs/node_modules/@radix-ui/react-roving-focus/node_modules/@radix-ui/react-collection/node_modules/@radix-ui/react-slot": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.0.1.tgz", + "integrity": "sha512-avutXAFL1ehGvAXtPquu0YK5oz6ctS474iM3vNGQIkswrVhdrS52e3uoMQBzZhNRAIE0jBnUyXWNmSjGHhCFcw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-compose-refs": "1.0.0" + }, + "peerDependencies": { + "react": "^16.8 || ^17.0 || ^18.0" + } + }, + "node_modules/@excalidraw/excalidraw/node_modules/@radix-ui/react-tabs/node_modules/@radix-ui/react-roving-focus/node_modules/@radix-ui/react-compose-refs": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.0.0.tgz", + "integrity": "sha512-0KaSv6sx787/hK3eF53iOkiSLwAGlFMx5lotrqD2pTjB18KbybKoEIgkNZTKC60YECDQTKGTRcDBILwZVqVKvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.13.10" + }, + "peerDependencies": { + "react": "^16.8 || ^17.0 || ^18.0" + } + }, + "node_modules/@excalidraw/excalidraw/node_modules/@radix-ui/react-tabs/node_modules/@radix-ui/react-roving-focus/node_modules/@radix-ui/react-use-callback-ref": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.0.0.tgz", + "integrity": "sha512-GZtyzoHz95Rhs6S63D2t/eqvdFCm7I+yHMLVQheKM7nBD8mbZIt+ct1jz4536MDnaOGKIxynJ8eHTkVGVVkoTg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.13.10" + }, + "peerDependencies": { + "react": "^16.8 || ^17.0 || ^18.0" + } + }, + "node_modules/@excalidraw/excalidraw/node_modules/@radix-ui/react-tabs/node_modules/@radix-ui/react-use-controllable-state": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.0.0.tgz", + "integrity": "sha512-FohDoZvk3mEXh9AWAVyRTYR4Sq7/gavuofglmiXB2g1aKyboUD4YtgWxKj8O5n+Uak52gXQ4wKz5IFST4vtJHg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-use-callback-ref": "1.0.0" + }, + "peerDependencies": { + "react": "^16.8 || ^17.0 || ^18.0" + } + }, + "node_modules/@excalidraw/excalidraw/node_modules/@radix-ui/react-tabs/node_modules/@radix-ui/react-use-controllable-state/node_modules/@radix-ui/react-use-callback-ref": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.0.0.tgz", + "integrity": "sha512-GZtyzoHz95Rhs6S63D2t/eqvdFCm7I+yHMLVQheKM7nBD8mbZIt+ct1jz4536MDnaOGKIxynJ8eHTkVGVVkoTg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.13.10" + }, + "peerDependencies": { + "react": "^16.8 || ^17.0 || ^18.0" + } + }, + "node_modules/@excalidraw/excalidraw/node_modules/clsx": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-1.1.1.tgz", + "integrity": "sha512-6/bPho624p3S2pMyvP5kKBPXnI3ufHLObBFCfgx+LkeR5lg2XYy2hqZqUf45ypD8COn2bhgGJSUE+l5dhNBieA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/@excalidraw/excalidraw/node_modules/jotai": { + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/jotai/-/jotai-2.11.0.tgz", + "integrity": "sha512-zKfoBBD1uDw3rljwHkt0fWuja1B76R7CjznuBO+mSX6jpsO1EBeWNRKpeaQho9yPI/pvCv4recGfgOXGxwPZvQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.20.0" + }, + "peerDependencies": { + "@types/react": ">=17.0.0", + "react": ">=17.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "react": { + "optional": true + } + } + }, + "node_modules/@excalidraw/excalidraw/node_modules/points-on-curve": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/points-on-curve/-/points-on-curve-1.0.1.tgz", + "integrity": "sha512-3nmX4/LIiyuwGLwuUrfhTlDeQFlAhi7lyK/zcRNGhalwapDWgAGR82bUpmn2mA03vII3fvNCG8jAONzKXwpxAg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@excalidraw/excalidraw/node_modules/roughjs": { + "version": "4.6.4", + "resolved": "https://registry.npmjs.org/roughjs/-/roughjs-4.6.4.tgz", + "integrity": "sha512-s6EZ0BntezkFYMf/9mGn7M8XGIoaav9QQBCnJROWB3brUWQ683Q2LbRD/hq0Z3bAJ/9NVpU/5LpiTWvQMyLDhw==", "dev": true, - "license": "Apache-2.0" + "license": "MIT", + "dependencies": { + "hachure-fill": "^0.5.2", + "path-data-parser": "^0.1.0", + "points-on-curve": "^0.2.0", + "points-on-path": "^0.2.1" + } + }, + "node_modules/@excalidraw/excalidraw/node_modules/roughjs/node_modules/points-on-curve": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/points-on-curve/-/points-on-curve-0.2.0.tgz", + "integrity": "sha512-0mYKnYYe9ZcqMCWhUjItv/oHjvgEsfKvnUTg8sAtnHr3GVy7rGkXCb6d5cSyqrWqL4k81b9CPg3urd+T7aop3A==", + "dev": true, + "license": "MIT" + }, + "node_modules/@excalidraw/laser-pointer": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@excalidraw/laser-pointer/-/laser-pointer-1.3.1.tgz", + "integrity": "sha512-psA1z1N2qeAfsORdXc9JmD2y4CmDwmuMRxnNdJHZexIcPwaNEyIpNcelw+QkL9rz9tosaN9krXuKaRqYpRAR6g==", + "dev": true, + "license": "MIT" + }, + "node_modules/@excalidraw/markdown-to-text": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/@excalidraw/markdown-to-text/-/markdown-to-text-0.1.2.tgz", + "integrity": "sha512-1nDXBNAojfi3oSFwJswKREkFm5wrSjqay81QlyRv2pkITG/XYB5v+oChENVBQLcxQwX4IUATWvXM5BcaNhPiIg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@excalidraw/mermaid-to-excalidraw": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/@excalidraw/mermaid-to-excalidraw/-/mermaid-to-excalidraw-2.2.2.tgz", + "integrity": "sha512-5VKQq5CdRocC82vOIUpQ5ufJOVV9FpBTdHGA+ULqazeIVV+cr299877omQCibsdS3Bpitz2fsnTwnIXEmLVDSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@excalidraw/markdown-to-text": "0.1.2", + "@mermaid-js/parser": "^0.6.3", + "mermaid": "^11.12.1", + "nanoid": "4.0.2" + } + }, + "node_modules/@excalidraw/mermaid-to-excalidraw/node_modules/@mermaid-js/parser": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/@mermaid-js/parser/-/parser-0.6.3.tgz", + "integrity": "sha512-lnjOhe7zyHjc+If7yT4zoedx2vo4sHaTmtkl1+or8BRTnCtDmcTpAjpzDSfCZrshM5bCoz0GyidzadJAH1xobA==", + "dev": true, + "license": "MIT", + "dependencies": { + "langium": "3.3.1" + } + }, + "node_modules/@excalidraw/mermaid-to-excalidraw/node_modules/nanoid": { + "version": "5.1.16", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-5.1.16.tgz", + "integrity": "sha512-kVrnsrJqMR8+oLJnGEmSWw9BivK5mt7H3FZatVRjrc5wGqFYuBxX1yG7+A7Gi5AefkX6t/oCkizcQgpu0cY1dQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.js" + }, + "engines": { + "node": "^18 || >=20" + } + }, + "node_modules/@excalidraw/random-username": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@excalidraw/random-username/-/random-username-1.1.0.tgz", + "integrity": "sha512-nULYsQxkWHnbmHvcs+efMkJ4/9TtvNyFeLyHdeGxW0zHs6P+jYVqcRff9A6Vq9w9JXeDRnRh2VKvTtS19GW2qA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + } }, "node_modules/@floating-ui/core": { "version": "1.8.0", @@ -195,6 +1159,18 @@ "tailwindcss": "^3.0 || ^4.0" } }, + "node_modules/@hono/node-server": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@hono/node-server/-/node-server-2.1.5.tgz", + "integrity": "sha512-MgP/I06GdvsilDspMk/qmRzpk8LOUStP6jFAbSRi40W09DhoyUn+Mj7eyKICPqeYKybOjVFLVIAQzvu1bfIa7Q==", + "license": "MIT", + "engines": { + "node": ">=20" + }, + "peerDependencies": { + "hono": "^4 || ^5.0.0-0" + } + }, "node_modules/@iconify/types": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/@iconify/types/-/types-2.0.0.tgz", @@ -290,15 +1266,194 @@ "@chevrotain/types": "~11.1.2" } }, + "node_modules/@modelcontextprotocol/sdk": { + "version": "1.32.1", + "resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.32.1.tgz", + "integrity": "sha512-2DdE+SJDtzLEEWzY1ZjY7Q+VcPhcV1KisD3zI4u0XZyktsjHum1mwbMI+JaulUBi2OZk+KJAi2uPXzxichPkdw==", + "license": "MIT", + "dependencies": { + "@hono/node-server": "^1.19.9 || ^2.0.5", + "ajv": "^8.17.1", + "ajv-formats": "^3.0.1", + "content-type": "^1.0.5", + "cors": "^2.8.5", + "cross-spawn": "^7.0.5", + "eventsource": "^3.0.2", + "eventsource-parser": "^3.0.0", + "express": "^5.2.1", + "express-rate-limit": "^8.2.1", + "hono": "^4.11.4", + "jose": "^6.1.3", + "json-schema-typed": "^8.0.2", + "pkce-challenge": "^5.0.0", + "raw-body": "^3.0.0", + "zod": "^3.25 || ^4.0", + "zod-to-json-schema": "^3.25.1" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@cfworker/json-schema": "^4.1.1", + "zod": "^3.25 || ^4.0" + }, + "peerDependenciesMeta": { + "@cfworker/json-schema": { + "optional": true + }, + "zod": { + "optional": false + } + } + }, "node_modules/@napi-rs/canvas": { "version": "0.1.100", - "resolved": "https://registry.npmjs.org/@napi-rs/canvas/-/canvas-0.1.100.tgz", - "integrity": "sha512-xglYA6q3XO5P3BNJYxVZ1IV7DLVjp1Py6nwag88YntrS+3vKHyYcMqXVS4ZztJmwz2uGvz1FWhI/4LgbR5uQDA==", + "resolved": "https://registry.npmjs.org/@napi-rs/canvas/-/canvas-0.1.100.tgz", + "integrity": "sha512-xglYA6q3XO5P3BNJYxVZ1IV7DLVjp1Py6nwag88YntrS+3vKHyYcMqXVS4ZztJmwz2uGvz1FWhI/4LgbR5uQDA==", + "dev": true, + "license": "MIT", + "optional": true, + "workspaces": [ + "e2e/*" + ], + "engines": { + "node": ">= 10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + }, + "optionalDependencies": { + "@napi-rs/canvas-android-arm64": "0.1.100", + "@napi-rs/canvas-darwin-arm64": "0.1.100", + "@napi-rs/canvas-darwin-x64": "0.1.100", + "@napi-rs/canvas-linux-arm-gnueabihf": "0.1.100", + "@napi-rs/canvas-linux-arm64-gnu": "0.1.100", + "@napi-rs/canvas-linux-arm64-musl": "0.1.100", + "@napi-rs/canvas-linux-riscv64-gnu": "0.1.100", + "@napi-rs/canvas-linux-x64-gnu": "0.1.100", + "@napi-rs/canvas-linux-x64-musl": "0.1.100", + "@napi-rs/canvas-win32-arm64-msvc": "0.1.100", + "@napi-rs/canvas-win32-x64-msvc": "0.1.100" + } + }, + "node_modules/@napi-rs/canvas-android-arm64": { + "version": "0.1.100", + "resolved": "https://registry.npmjs.org/@napi-rs/canvas-android-arm64/-/canvas-android-arm64-0.1.100.tgz", + "integrity": "sha512-hjhCKhntPv9+t4ckHymdx0phYNcVW+GKQR6Lzw2zE+pOVjOplSmtx9nNNknTjbEDLcuLZqA1y8ufKg1XfgftzQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + } + }, + "node_modules/@napi-rs/canvas-darwin-arm64": { + "version": "0.1.100", + "resolved": "https://registry.npmjs.org/@napi-rs/canvas-darwin-arm64/-/canvas-darwin-arm64-0.1.100.tgz", + "integrity": "sha512-2PcswRaC7Ly645DGt88///zuFDhJxJYdKAs1uU3mfk1atYkXufgcgLfBpk6Tm12nCQBaNt1wpybuPZ4qOhTo8A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + } + }, + "node_modules/@napi-rs/canvas-darwin-x64": { + "version": "0.1.100", + "resolved": "https://registry.npmjs.org/@napi-rs/canvas-darwin-x64/-/canvas-darwin-x64-0.1.100.tgz", + "integrity": "sha512-ePNZtj7pNIva/siZMg+HmbeozkIjqUIYdoymH8HaA3qK7LfzFN4WMBM8G6HQ9ZC+H3+Dnn5pqtiXpgLykaPOhw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + } + }, + "node_modules/@napi-rs/canvas-linux-arm-gnueabihf": { + "version": "0.1.100", + "resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-arm-gnueabihf/-/canvas-linux-arm-gnueabihf-0.1.100.tgz", + "integrity": "sha512-d5cDB48oWFGU8/XPhUOFAlySgb/VAu7D+s8fi55K1Pcfg8aPplHWqMgibhVLU8ky7Pyg/fuiVLz4Nf3JrSTuUA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + } + }, + "node_modules/@napi-rs/canvas-linux-arm64-gnu": { + "version": "0.1.100", + "resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-arm64-gnu/-/canvas-linux-arm64-gnu-0.1.100.tgz", + "integrity": "sha512-rDxgxRu69RvDlX/bh9o22DxLsGr8EqsNgotL9+RwQE1S0b0cqeatqsw6aW45mukm0B42DIAaAacKaYQ8cqS1nw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + } + }, + "node_modules/@napi-rs/canvas-linux-arm64-musl": { + "version": "0.1.100", + "resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-arm64-musl/-/canvas-linux-arm64-musl-0.1.100.tgz", + "integrity": "sha512-K3mDW66N+xT2/V439u1alFANiBUjdEx2gLiNYnCmUsva5jZMxWTjafBYwTzYK+EMFMHrUoabuU+T1BIP5CgbYQ==", + "cpu": [ + "arm64" + ], "dev": true, "license": "MIT", "optional": true, - "workspaces": [ - "e2e/*" + "os": [ + "linux" ], "engines": { "node": ">= 10" @@ -306,33 +1461,20 @@ "funding": { "type": "github", "url": "https://github.com/sponsors/Brooooooklyn" - }, - "optionalDependencies": { - "@napi-rs/canvas-android-arm64": "0.1.100", - "@napi-rs/canvas-darwin-arm64": "0.1.100", - "@napi-rs/canvas-darwin-x64": "0.1.100", - "@napi-rs/canvas-linux-arm-gnueabihf": "0.1.100", - "@napi-rs/canvas-linux-arm64-gnu": "0.1.100", - "@napi-rs/canvas-linux-arm64-musl": "0.1.100", - "@napi-rs/canvas-linux-riscv64-gnu": "0.1.100", - "@napi-rs/canvas-linux-x64-gnu": "0.1.100", - "@napi-rs/canvas-linux-x64-musl": "0.1.100", - "@napi-rs/canvas-win32-arm64-msvc": "0.1.100", - "@napi-rs/canvas-win32-x64-msvc": "0.1.100" } }, - "node_modules/@napi-rs/canvas-android-arm64": { + "node_modules/@napi-rs/canvas-linux-riscv64-gnu": { "version": "0.1.100", - "resolved": "https://registry.npmjs.org/@napi-rs/canvas-android-arm64/-/canvas-android-arm64-0.1.100.tgz", - "integrity": "sha512-hjhCKhntPv9+t4ckHymdx0phYNcVW+GKQR6Lzw2zE+pOVjOplSmtx9nNNknTjbEDLcuLZqA1y8ufKg1XfgftzQ==", + "resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-riscv64-gnu/-/canvas-linux-riscv64-gnu-0.1.100.tgz", + "integrity": "sha512-mooqUBTIsccZpnoQC4NgrC1v6C1vof39etLNMnBwCY+p0gajWJvAHLGQ6g/gGyS5YrpDW+GefSN4+Cvcr08UWw==", "cpu": [ - "arm64" + "riscv64" ], "dev": true, "license": "MIT", "optional": true, "os": [ - "android" + "linux" ], "engines": { "node": ">= 10" @@ -342,18 +1484,18 @@ "url": "https://github.com/sponsors/Brooooooklyn" } }, - "node_modules/@napi-rs/canvas-darwin-arm64": { + "node_modules/@napi-rs/canvas-linux-x64-gnu": { "version": "0.1.100", - "resolved": "https://registry.npmjs.org/@napi-rs/canvas-darwin-arm64/-/canvas-darwin-arm64-0.1.100.tgz", - "integrity": "sha512-2PcswRaC7Ly645DGt88///zuFDhJxJYdKAs1uU3mfk1atYkXufgcgLfBpk6Tm12nCQBaNt1wpybuPZ4qOhTo8A==", + "resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-x64-gnu/-/canvas-linux-x64-gnu-0.1.100.tgz", + "integrity": "sha512-1eCvkDCazm7FFhsT7DfGOdSaHgZVK3bt/dSBl5EWHOWmnz+I7j8tPseJqqD81NF+MH21jKUK4wQSDjN0mdhnTg==", "cpu": [ - "arm64" + "x64" ], "dev": true, "license": "MIT", "optional": true, "os": [ - "darwin" + "linux" ], "engines": { "node": ">= 10" @@ -363,10 +1505,10 @@ "url": "https://github.com/sponsors/Brooooooklyn" } }, - "node_modules/@napi-rs/canvas-darwin-x64": { + "node_modules/@napi-rs/canvas-linux-x64-musl": { "version": "0.1.100", - "resolved": "https://registry.npmjs.org/@napi-rs/canvas-darwin-x64/-/canvas-darwin-x64-0.1.100.tgz", - "integrity": "sha512-ePNZtj7pNIva/siZMg+HmbeozkIjqUIYdoymH8HaA3qK7LfzFN4WMBM8G6HQ9ZC+H3+Dnn5pqtiXpgLykaPOhw==", + "resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-x64-musl/-/canvas-linux-x64-musl-0.1.100.tgz", + "integrity": "sha512-20arT6lnI19S68qNlii73TSEDbECNgzMz2EpldC1V3mZFuRkeujXkcebRk0LRJe9SEUAooYiLokfMViY8IX7yA==", "cpu": [ "x64" ], @@ -374,7 +1516,7 @@ "license": "MIT", "optional": true, "os": [ - "darwin" + "linux" ], "engines": { "node": ">= 10" @@ -384,18 +1526,18 @@ "url": "https://github.com/sponsors/Brooooooklyn" } }, - "node_modules/@napi-rs/canvas-linux-arm-gnueabihf": { + "node_modules/@napi-rs/canvas-win32-arm64-msvc": { "version": "0.1.100", - "resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-arm-gnueabihf/-/canvas-linux-arm-gnueabihf-0.1.100.tgz", - "integrity": "sha512-d5cDB48oWFGU8/XPhUOFAlySgb/VAu7D+s8fi55K1Pcfg8aPplHWqMgibhVLU8ky7Pyg/fuiVLz4Nf3JrSTuUA==", + "resolved": "https://registry.npmjs.org/@napi-rs/canvas-win32-arm64-msvc/-/canvas-win32-arm64-msvc-0.1.100.tgz", + "integrity": "sha512-DZFFT1wIAg37LJw37yhMRFfjATd3vTQzjZ1Yki8u2vhO6Hi5VE6BVaGQ1aaDu7xb4iMErz+9EOwjpS7xcxFeBw==", "cpu": [ - "arm" + "arm64" ], "dev": true, "license": "MIT", "optional": true, "os": [ - "linux" + "win32" ], "engines": { "node": ">= 10" @@ -405,18 +1547,18 @@ "url": "https://github.com/sponsors/Brooooooklyn" } }, - "node_modules/@napi-rs/canvas-linux-arm64-gnu": { + "node_modules/@napi-rs/canvas-win32-x64-msvc": { "version": "0.1.100", - "resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-arm64-gnu/-/canvas-linux-arm64-gnu-0.1.100.tgz", - "integrity": "sha512-rDxgxRu69RvDlX/bh9o22DxLsGr8EqsNgotL9+RwQE1S0b0cqeatqsw6aW45mukm0B42DIAaAacKaYQ8cqS1nw==", + "resolved": "https://registry.npmjs.org/@napi-rs/canvas-win32-x64-msvc/-/canvas-win32-x64-msvc-0.1.100.tgz", + "integrity": "sha512-MyT1j3mHC2+Lu4pBi9mKyMJhtP6U7k7EldY7sj/uS5gJA65gTXt8MefJQXLJo5d/vZbuWmfxzkEUNc/urV3pHA==", "cpu": [ - "arm64" + "x64" ], "dev": true, "license": "MIT", "optional": true, "os": [ - "linux" + "win32" ], "engines": { "node": ">= 10" @@ -426,156 +1568,461 @@ "url": "https://github.com/sponsors/Brooooooklyn" } }, - "node_modules/@napi-rs/canvas-linux-arm64-musl": { - "version": "0.1.100", - "resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-arm64-musl/-/canvas-linux-arm64-musl-0.1.100.tgz", - "integrity": "sha512-K3mDW66N+xT2/V439u1alFANiBUjdEx2gLiNYnCmUsva5jZMxWTjafBYwTzYK+EMFMHrUoabuU+T1BIP5CgbYQ==", - "cpu": [ - "arm64" - ], + "node_modules/@puppeteer/browsers": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/@puppeteer/browsers/-/browsers-2.6.1.tgz", + "integrity": "sha512-aBSREisdsGH890S2rQqK82qmQYU3uFpSH8wcZWHgHzl3LfzsxAKbLNiAG9mO8v1Y0UICBeClICxPJvyr0rcuxg==", + "dev": true, + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "debug": "^4.4.0", + "extract-zip": "^2.0.1", + "progress": "^2.0.3", + "proxy-agent": "^6.5.0", + "semver": "^7.6.3", + "tar-fs": "^3.0.6", + "unbzip2-stream": "^1.4.3", + "yargs": "^17.7.2" + }, + "bin": { + "browsers": "lib/cjs/main-cli.js" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@radix-ui/primitive": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.1.tgz", + "integrity": "sha512-SJ31y+Q/zAyShtXJc8x83i9TYdbAfHZ++tUZnvjJJqFjzsdUnKsxPL6IEtBlxKkU7yzer//GQtZSV4GbldL3YA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@radix-ui/react-arrow": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-arrow/-/react-arrow-1.1.2.tgz", + "integrity": "sha512-G+KcpzXHq24iH0uGG/pF8LyzpFJYGD4RfLjCIBfGdSLXvjLHST31RUiRVrupIBMvIppMgSzQ6l66iAxl03tdlg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@radix-ui/react-primitive": "2.0.2" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-compose-refs": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.1.tgz", + "integrity": "sha512-Y9VzoRDSJtgFMUCoiZBDVo084VQ5hfpXxVE+NgkdNsjiDBByiImMZKKhxMwCbdHvhlENG6a833CbFkOQvTricw==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-context": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.1.1.tgz", + "integrity": "sha512-UASk9zi+crv9WteK/NU4PLvOoL3OuE6BWVKNF6hPRBtYBDXQ2u5iu3O59zUlJiTVvkyuycnqrztsHVJwcK9K+Q==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-dismissable-layer": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.1.5.tgz", + "integrity": "sha512-E4TywXY6UsXNRhFrECa5HAvE5/4BFcGyfTyK36gP+pAW1ed7UTK4vKwdr53gAJYwqbfCWC6ATvJa3J3R/9+Qrg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.1", + "@radix-ui/react-compose-refs": "1.1.1", + "@radix-ui/react-primitive": "2.0.2", + "@radix-ui/react-use-callback-ref": "1.1.0", + "@radix-ui/react-use-escape-keydown": "1.1.0" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-focus-guards": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-guards/-/react-focus-guards-1.1.1.tgz", + "integrity": "sha512-pSIwfrT1a6sIoDASCSpFwOasEwKTZWDw/iBdtnqKO7v6FeOzYJ7U53cPzYFVR3geGGXgVHaH+CdngrrAzqUGxg==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-focus-scope": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-scope/-/react-focus-scope-1.1.2.tgz", + "integrity": "sha512-zxwE80FCU7lcXUGWkdt6XpTTCKPitG1XKOwViTxHVKIJhZl9MvIl2dVHeZENCWD9+EdWv05wlaEkRXUykU27RA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.1", + "@radix-ui/react-primitive": "2.0.2", + "@radix-ui/react-use-callback-ref": "1.1.0" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-id": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-id/-/react-id-1.1.0.tgz", + "integrity": "sha512-EJUrI8yYh7WOjNOqpoJaf1jlFIH2LvtgAl+YcFqNCa+4hj64ZXmPkAKOFs/ukjz3byN6bdb/AVUqHkI8/uWWMA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@radix-ui/react-use-layout-effect": "1.1.0" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-popover": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/@radix-ui/react-popover/-/react-popover-1.1.6.tgz", + "integrity": "sha512-NQouW0x4/GnkFJ/pRqsIS3rM/k97VzKnVb2jB7Gq7VEGPy5g7uNV1ykySFt7eWSp3i2uSGFwaJcvIRJBAHmmFg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.1", + "@radix-ui/react-compose-refs": "1.1.1", + "@radix-ui/react-context": "1.1.1", + "@radix-ui/react-dismissable-layer": "1.1.5", + "@radix-ui/react-focus-guards": "1.1.1", + "@radix-ui/react-focus-scope": "1.1.2", + "@radix-ui/react-id": "1.1.0", + "@radix-ui/react-popper": "1.2.2", + "@radix-ui/react-portal": "1.1.4", + "@radix-ui/react-presence": "1.1.2", + "@radix-ui/react-primitive": "2.0.2", + "@radix-ui/react-slot": "1.1.2", + "@radix-ui/react-use-controllable-state": "1.1.0", + "aria-hidden": "^1.2.4", + "react-remove-scroll": "^2.6.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-popper": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-popper/-/react-popper-1.2.2.tgz", + "integrity": "sha512-Rvqc3nOpwseCyj/rgjlJDYAgyfw7OC1tTkKn2ivhaMGcYt8FSBlahHOZak2i3QwkRXUXgGgzeEe2RuqeEHuHgA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@floating-ui/react-dom": "^2.0.0", + "@radix-ui/react-arrow": "1.1.2", + "@radix-ui/react-compose-refs": "1.1.1", + "@radix-ui/react-context": "1.1.1", + "@radix-ui/react-primitive": "2.0.2", + "@radix-ui/react-use-callback-ref": "1.1.0", + "@radix-ui/react-use-layout-effect": "1.1.0", + "@radix-ui/react-use-rect": "1.1.0", + "@radix-ui/react-use-size": "1.1.0", + "@radix-ui/rect": "1.1.0" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-portal": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.1.4.tgz", + "integrity": "sha512-sn2O9k1rPFYVyKd5LAJfo96JlSGVFpa1fS6UuBJfrZadudiw5tAmru+n1x7aMRQ84qDM71Zh1+SzK5QwU0tJfA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@radix-ui/react-primitive": "2.0.2", + "@radix-ui/react-use-layout-effect": "1.1.0" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-presence": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.1.2.tgz", + "integrity": "sha512-18TFr80t5EVgL9x1SwF/YGtfG+l0BS0PRAlCWBDoBEiDQjeKgnNZRVJp/oVBl24sr3Gbfwc/Qpj4OcWTQMsAEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.1", + "@radix-ui/react-use-layout-effect": "1.1.0" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-primitive": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.0.2.tgz", + "integrity": "sha512-Ec/0d38EIuvDF+GZjcMU/Ze6MxntVJYO/fRlCPhCaVUyPY9WTalHJw54tp9sXeJo3tlShWpy41vQRgLRGOuz+w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@radix-ui/react-slot": "1.1.2" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-slot": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.1.2.tgz", + "integrity": "sha512-YAKxaiGsSQJ38VzKH86/BPRC4rh+b1Jpa+JneA5LRE7skmLPNAyeG8kPJj/oo4STLvlrs8vkf/iYyc3A5stYCQ==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.1" }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/Brooooooklyn" + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } } }, - "node_modules/@napi-rs/canvas-linux-riscv64-gnu": { - "version": "0.1.100", - "resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-riscv64-gnu/-/canvas-linux-riscv64-gnu-0.1.100.tgz", - "integrity": "sha512-mooqUBTIsccZpnoQC4NgrC1v6C1vof39etLNMnBwCY+p0gajWJvAHLGQ6g/gGyS5YrpDW+GefSN4+Cvcr08UWw==", - "cpu": [ - "riscv64" - ], + "node_modules/@radix-ui/react-use-callback-ref": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.1.0.tgz", + "integrity": "sha512-CasTfvsy+frcFkbXtSJ2Zu9JHpN8TYKxkgJGWbjiZhFivxaeW7rMeZt7QELGVLaYVfFMsKHjb7Ak0nMEe+2Vfw==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/Brooooooklyn" + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } } }, - "node_modules/@napi-rs/canvas-linux-x64-gnu": { - "version": "0.1.100", - "resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-x64-gnu/-/canvas-linux-x64-gnu-0.1.100.tgz", - "integrity": "sha512-1eCvkDCazm7FFhsT7DfGOdSaHgZVK3bt/dSBl5EWHOWmnz+I7j8tPseJqqD81NF+MH21jKUK4wQSDjN0mdhnTg==", - "cpu": [ - "x64" - ], + "node_modules/@radix-ui/react-use-controllable-state": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.1.0.tgz", + "integrity": "sha512-MtfMVJiSr2NjzS0Aa90NPTnvTSg6C/JLCV7ma0W6+OMV78vd8OyRpID+Ng9LxzsPbLeuBnWBA1Nq30AtBIDChw==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" + "dependencies": { + "@radix-ui/react-use-callback-ref": "1.1.0" }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/Brooooooklyn" + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } } }, - "node_modules/@napi-rs/canvas-linux-x64-musl": { - "version": "0.1.100", - "resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-x64-musl/-/canvas-linux-x64-musl-0.1.100.tgz", - "integrity": "sha512-20arT6lnI19S68qNlii73TSEDbECNgzMz2EpldC1V3mZFuRkeujXkcebRk0LRJe9SEUAooYiLokfMViY8IX7yA==", - "cpu": [ - "x64" - ], + "node_modules/@radix-ui/react-use-escape-keydown": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-escape-keydown/-/react-use-escape-keydown-1.1.0.tgz", + "integrity": "sha512-L7vwWlR1kTTQ3oh7g1O0CBF3YCyyTj8NmhLR+phShpyA50HCfBFKVJTpshm9PzLiKmehsrQzTYTpX9HvmC9rhw==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" + "dependencies": { + "@radix-ui/react-use-callback-ref": "1.1.0" }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/Brooooooklyn" + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } } }, - "node_modules/@napi-rs/canvas-win32-arm64-msvc": { - "version": "0.1.100", - "resolved": "https://registry.npmjs.org/@napi-rs/canvas-win32-arm64-msvc/-/canvas-win32-arm64-msvc-0.1.100.tgz", - "integrity": "sha512-DZFFT1wIAg37LJw37yhMRFfjATd3vTQzjZ1Yki8u2vhO6Hi5VE6BVaGQ1aaDu7xb4iMErz+9EOwjpS7xcxFeBw==", - "cpu": [ - "arm64" - ], + "node_modules/@radix-ui/react-use-layout-effect": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.1.0.tgz", + "integrity": "sha512-+FPE0rOdziWSrH9athwI1R0HDVbWlEhd+FR+aSDk4uWGmSJ9Z54sdZVDQPZAinJhJXwfT+qnj969mCsT2gfm5w==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10" + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/Brooooooklyn" + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } } }, - "node_modules/@napi-rs/canvas-win32-x64-msvc": { - "version": "0.1.100", - "resolved": "https://registry.npmjs.org/@napi-rs/canvas-win32-x64-msvc/-/canvas-win32-x64-msvc-0.1.100.tgz", - "integrity": "sha512-MyT1j3mHC2+Lu4pBi9mKyMJhtP6U7k7EldY7sj/uS5gJA65gTXt8MefJQXLJo5d/vZbuWmfxzkEUNc/urV3pHA==", - "cpu": [ - "x64" - ], + "node_modules/@radix-ui/react-use-rect": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-rect/-/react-use-rect-1.1.0.tgz", + "integrity": "sha512-0Fmkebhr6PiseyZlYAOtLS+nb7jLmpqTrJyv61Pe68MKYW6OWdRE2kI70TaYY27u7H0lajqM3hSMMLFq18Z7nQ==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10" + "dependencies": { + "@radix-ui/rect": "1.1.0" }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/Brooooooklyn" + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } } }, - "node_modules/@puppeteer/browsers": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/@puppeteer/browsers/-/browsers-2.6.1.tgz", - "integrity": "sha512-aBSREisdsGH890S2rQqK82qmQYU3uFpSH8wcZWHgHzl3LfzsxAKbLNiAG9mO8v1Y0UICBeClICxPJvyr0rcuxg==", + "node_modules/@radix-ui/react-use-size": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-size/-/react-use-size-1.1.0.tgz", + "integrity": "sha512-XW3/vWuIXHa+2Uwcc2ABSfcCledmXhhQPlGbfcRXbiUQI5Icjcg19BGCZVKKInYbvUCut/ufbbLLPFC5cbb1hw==", "dev": true, - "license": "Apache-2.0", - "peer": true, + "license": "MIT", "dependencies": { - "debug": "^4.4.0", - "extract-zip": "^2.0.1", - "progress": "^2.0.3", - "proxy-agent": "^6.5.0", - "semver": "^7.6.3", - "tar-fs": "^3.0.6", - "unbzip2-stream": "^1.4.3", - "yargs": "^17.7.2" + "@radix-ui/react-use-layout-effect": "1.1.0" }, - "bin": { - "browsers": "lib/cjs/main-cli.js" + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, - "engines": { - "node": ">=18" + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } } }, + "node_modules/@radix-ui/rect": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/rect/-/rect-1.1.0.tgz", + "integrity": "sha512-A9+lCBZoaMJlVKcRBz2YByCG+Cp2t6nAnMnNba+XiWxnj6r4JUFqfsgwocMBZU9LPtdxC6wB56ySYpc7LQIoJg==", + "dev": true, + "license": "MIT" + }, "node_modules/@react-aria/focus": { "version": "3.22.1", "resolved": "https://registry.npmjs.org/@react-aria/focus/-/focus-3.22.1.tgz", @@ -628,13 +2075,13 @@ } }, "node_modules/@tanstack/react-virtual": { - "version": "3.14.20", - "resolved": "https://registry.npmjs.org/@tanstack/react-virtual/-/react-virtual-3.14.20.tgz", - "integrity": "sha512-9hbhFlc0lznjXmWYDB/KEl7PJxEPz0Qnj2FzJFse98miGvXtx7jzjE5jlasmtczmZ6TxXsyI4v7PuyGQm2q1nQ==", + "version": "3.14.21", + "resolved": "https://registry.npmjs.org/@tanstack/react-virtual/-/react-virtual-3.14.21.tgz", + "integrity": "sha512-wWE/ZJDNNDp18JHWy7n5PQooqerHePVkEEu6Op0twKQ2InF9DPhYMQptQY0syDEviMd3/r8D0OUvKJibDGm9HQ==", "dev": true, "license": "MIT", "dependencies": { - "@tanstack/virtual-core": "3.19.5" + "@tanstack/virtual-core": "3.19.6" }, "funding": { "type": "github", @@ -646,9 +2093,9 @@ } }, "node_modules/@tanstack/virtual-core": { - "version": "3.19.5", - "resolved": "https://registry.npmjs.org/@tanstack/virtual-core/-/virtual-core-3.19.5.tgz", - "integrity": "sha512-X/wq6DtSK/XClznMM+I4JE63yyIwmyPk4Ts83bD0VKaIbwHNdRsO5QipCPX+3OovqYyr0puQNpbmp8+SPhspNg==", + "version": "3.19.6", + "resolved": "https://registry.npmjs.org/@tanstack/virtual-core/-/virtual-core-3.19.6.tgz", + "integrity": "sha512-CsTW892z23gy7IyxYTTorVu1uZWB+0N349rSMgKz4rFLPpLza7F7TpcCKeI5iUAwYQCt8QUlOU67J+KXCxtpGw==", "dev": true, "license": "MIT", "funding": { @@ -1039,6 +2486,49 @@ } } }, + "node_modules/@zenuml/core/node_modules/react": { + "version": "19.3.1", + "resolved": "https://registry.npmjs.org/react/-/react-19.3.1.tgz", + "integrity": "sha512-QNhWdt7nrgVWWQG58QuGsAR3jv3Hbe4mb4vz7w2csnHavciZJgda1CPNaldF6eVxv90ud3jUXpKlHL69gO38yw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@zenuml/core/node_modules/react-dom": { + "version": "19.3.1", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.3.1.tgz", + "integrity": "sha512-Y0bARz53lhEC1NMCASs0/j+faB/zqt3R8hpvg1e8+61ZVJ4UL0UdC4cBbXfZMcIuhrfgeILTLe+ZeBCavQPtKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "scheduler": "^0.28.0" + }, + "peerDependencies": { + "react": "^19.3.1" + } + }, + "node_modules/@zenuml/core/node_modules/scheduler": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.28.0.tgz", + "integrity": "sha512-juorfCmIkIw8tT+p5BXSm6PJjQF/ycEYmKyzURCIt/RaZIhL+PulbQ9Yu2z1HdOJDdqDTlxA1+xKBmHXJsczAw==", + "dev": true, + "license": "MIT" + }, + "node_modules/accepts": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz", + "integrity": "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==", + "license": "MIT", + "dependencies": { + "mime-types": "^3.0.0", + "negotiator": "^1.0.0" + }, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/agent-base": { "version": "7.1.4", "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", @@ -1050,6 +2540,39 @@ "node": ">= 14" } }, + "node_modules/ajv": { + "version": "8.20.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.20.0.tgz", + "integrity": "sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==", + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ajv-formats": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-3.0.1.tgz", + "integrity": "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==", + "license": "MIT", + "dependencies": { + "ajv": "^8.0.0" + }, + "peerDependencies": { + "ajv": "^8.0.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, "node_modules/ansi-regex": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", @@ -1088,6 +2611,20 @@ "node": ">=14" } }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, + "license": "ISC", + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, "node_modules/argparse": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", @@ -1249,19 +2786,105 @@ } ], "license": "MIT", - "peer": true + "peer": true + }, + "node_modules/basic-ftp": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/basic-ftp/-/basic-ftp-5.3.1.tgz", + "integrity": "sha512-bopVNp6ugyA150DDuZfPFdt1KZ5a94ZDiwX4hMgZDzF+GttD80lEy8kj98kbyhLXnPvhtIo93mdnLIjpCAeeOw==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/binary-extensions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/body-parser": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.3.0.tgz", + "integrity": "sha512-2cGmJupaNgg+QUwVLAucDuWuoMZ6EX9iHDRswZ5lsNYEmwPaRknMPCLZz07yTzVq/83p4o/wzbDZbBrTvGGTIw==", + "license": "MIT", + "dependencies": { + "bytes": "^3.1.2", + "content-type": "^2.0.0", + "debug": "^4.4.3", + "http-errors": "^2.0.1", + "iconv-lite": "^0.7.2", + "on-finished": "^2.4.1", + "qs": "^6.15.2", + "raw-body": "^3.0.2", + "type-is": "^2.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } }, - "node_modules/basic-ftp": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/basic-ftp/-/basic-ftp-5.3.1.tgz", - "integrity": "sha512-bopVNp6ugyA150DDuZfPFdt1KZ5a94ZDiwX4hMgZDzF+GttD80lEy8kj98kbyhLXnPvhtIo93mdnLIjpCAeeOw==", + "node_modules/body-parser/node_modules/content-type": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-2.1.0.tgz", + "integrity": "sha512-mj7UPXE0jaqaOsukNZRUEfEi2AcL7C/vwmwcHV0O97eO1E1pxBZuyjlZrx5seTaNBg1U6+o35wpa35Qfcc+7ag==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/body-parser/node_modules/iconv-lite": { + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.3.tgz", + "integrity": "sha512-IKXpvIzjnC9XTAUbVBcMfGS0EPaIXtW6v+zr+RRp+hqULEpo0owZax6wyRwPOJbWbzjYspQwusTsfVr0ifh4uQ==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", "dev": true, "license": "MIT", - "peer": true, + "dependencies": { + "fill-range": "^7.1.1" + }, "engines": { - "node": ">=10.0.0" + "node": ">=8" } }, + "node_modules/browser-fs-access": { + "version": "0.29.1", + "resolved": "https://registry.npmjs.org/browser-fs-access/-/browser-fs-access-0.29.1.tgz", + "integrity": "sha512-LSvVX5e21LRrXqVMhqtAwj5xPgDb+fXAIH80NsnCQ9xuZPs2xWsOREi24RKgZa1XOiQRbcmVrv87+ulOKsgjxw==", + "dev": true, + "license": "Apache-2.0" + }, "node_modules/buffer": { "version": "5.7.1", "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", @@ -1314,6 +2937,44 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/callsites": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", @@ -1325,6 +2986,13 @@ "node": ">=6" } }, + "node_modules/canvas-roundrect-polyfill": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/canvas-roundrect-polyfill/-/canvas-roundrect-polyfill-0.0.1.tgz", + "integrity": "sha512-yWq+R3U3jE+coOeEb3a3GgE2j/0MMiDKM/QpLb6h9ihf5fGY9UXtvK9o4vNqjWXoZz7/3EaSVU3IX53TvFFUOw==", + "dev": true, + "license": "MIT" + }, "node_modules/chalk": { "version": "5.6.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz", @@ -1338,6 +3006,66 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, + "node_modules/chevrotain": { + "version": "11.0.3", + "resolved": "https://registry.npmjs.org/chevrotain/-/chevrotain-11.0.3.tgz", + "integrity": "sha512-ci2iJH6LeIkvP9eJW6gpueU8cnZhv85ELY8w8WiFtNjMHA5ad6pQLaJo9mEly/9qUyCpvqX8/POVUTf18/HFdw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@chevrotain/cst-dts-gen": "11.0.3", + "@chevrotain/gast": "11.0.3", + "@chevrotain/regexp-to-ast": "11.0.3", + "@chevrotain/types": "11.0.3", + "@chevrotain/utils": "11.0.3", + "lodash-es": "4.17.21" + } + }, + "node_modules/chevrotain-allstar": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/chevrotain-allstar/-/chevrotain-allstar-0.3.1.tgz", + "integrity": "sha512-b7g+y9A0v4mxCW1qUhf3BSVPg+/NvGErk/dOkrDaHA0nQIQGAtrOjlX//9OQtRlSCy+x9rfB5N8yC71lH1nvMw==", + "dev": true, + "license": "MIT", + "dependencies": { + "lodash-es": "^4.17.21" + }, + "peerDependencies": { + "chevrotain": "^11.0.0" + } + }, + "node_modules/chevrotain/node_modules/@chevrotain/types": { + "version": "11.0.3", + "resolved": "https://registry.npmjs.org/@chevrotain/types/-/types-11.0.3.tgz", + "integrity": "sha512-gsiM3G8b58kZC2HaWR50gu6Y1440cHiJ+i3JUvcp/35JchYejb2+5MVeJK0iKThYpAa/P2PYFV4hoi44HD+aHQ==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, "node_modules/chromium-bidi": { "version": "0.11.0", "resolved": "https://registry.npmjs.org/chromium-bidi/-/chromium-bidi-0.11.0.tgz", @@ -1353,6 +3081,17 @@ "devtools-protocol": "*" } }, + "node_modules/chromium-bidi/node_modules/zod": { + "version": "3.23.8", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.23.8.tgz", + "integrity": "sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g==", + "dev": true, + "license": "MIT", + "peer": true, + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, "node_modules/class-variance-authority": { "version": "0.7.1", "resolved": "https://registry.npmjs.org/class-variance-authority/-/class-variance-authority-0.7.1.tgz", @@ -1446,6 +3185,63 @@ "node": ">=18" } }, + "node_modules/content-disposition": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-2.0.1.tgz", + "integrity": "sha512-e+H0ZXHSWYrENhQzw1LPuP4oF5MzVKmDU6d3hxlvaPEYLLg62MxtQNPRx4SYSuYJSBUgnQIG4HIN2tEtNv7Dog==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/content-type": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", + "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-signature": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz", + "integrity": "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==", + "license": "MIT", + "engines": { + "node": ">=6.6.0" + } + }, + "node_modules/cors": { + "version": "2.8.6", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.6.tgz", + "integrity": "sha512-tJtZBBHA6vjIAaF6EnIaq6laBBP9aq/Y3ouVJjEfoHbRBcHBAHYcMh/w8LDrk2PvIMMq8gmopa5D4V8RmbrxGw==", + "license": "MIT", + "dependencies": { + "object-assign": "^4", + "vary": "^1" + }, + "engines": { + "node": ">= 0.10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, "node_modules/cose-base": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/cose-base/-/cose-base-1.0.3.tgz", @@ -1484,6 +3280,49 @@ } } }, + "node_modules/crc-32": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/crc-32/-/crc-32-0.3.0.tgz", + "integrity": "sha512-kucVIjOmMc1f0tv53BJ/5WIX+MGLcKuoBhnGqQrgKJNqLByb/sVMWfW/Aw6hw0jgcqjJ2pi9E5y32zOIpaUlsA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/cross-env": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-env/-/cross-env-7.0.3.tgz", + "integrity": "sha512-+/HKd6EgcQCJGh2PSjZuUitQBQynKor4wrFbRg4DtAgS1aWO+gU52xpH7M9ScGgXSYmAVS9bIJ8EzuaGw0oNAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "cross-spawn": "^7.0.1" + }, + "bin": { + "cross-env": "src/bin/cross-env.js", + "cross-env-shell": "src/bin/cross-env-shell.js" + }, + "engines": { + "node": ">=10.14", + "npm": ">=6", + "yarn": ">=1" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, "node_modules/crypto-js": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-4.2.0.tgz", @@ -2063,9 +3902,7 @@ "version": "4.4.3", "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", - "dev": true, "license": "MIT", - "peer": true, "dependencies": { "ms": "^2.1.3" }, @@ -2144,6 +3981,22 @@ "robust-predicates": "^3.0.2" } }, + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/detect-node-es": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/detect-node-es/-/detect-node-es-1.1.0.tgz", + "integrity": "sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==", + "dev": true, + "license": "MIT" + }, "node_modules/devtools-protocol": { "version": "0.0.1367902", "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1367902.tgz", @@ -2162,6 +4015,26 @@ "@types/trusted-types": "^2.0.7" } }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", + "license": "MIT" + }, "node_modules/emoji-regex": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", @@ -2170,6 +4043,15 @@ "license": "MIT", "peer": true }, + "node_modules/encodeurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/end-of-stream": { "version": "1.4.5", "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.5.tgz", @@ -2181,6 +4063,18 @@ "once": "^1.4.0" } }, + "node_modules/entities": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-6.0.1.tgz", + "integrity": "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, "node_modules/env-paths": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", @@ -2203,6 +4097,36 @@ "is-arrayish": "^0.2.1" } }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.2.tgz", + "integrity": "sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/es-toolkit": { "version": "1.52.0", "resolved": "https://registry.npmjs.org/es-toolkit/-/es-toolkit-1.52.0.tgz", @@ -2216,6 +4140,58 @@ "tests/browser-compat" ] }, + "node_modules/es6-promise-pool": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/es6-promise-pool/-/es6-promise-pool-2.5.0.tgz", + "integrity": "sha512-VHErXfzR/6r/+yyzPKeBvO0lgjfC5cbDCQWjWwMZWSb6YU39TGIl51OUmCfWCq4ylMdJSB8zkz2vIuIeIxXApA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/esbuild": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.12.tgz", + "integrity": "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.25.12", + "@esbuild/android-arm": "0.25.12", + "@esbuild/android-arm64": "0.25.12", + "@esbuild/android-x64": "0.25.12", + "@esbuild/darwin-arm64": "0.25.12", + "@esbuild/darwin-x64": "0.25.12", + "@esbuild/freebsd-arm64": "0.25.12", + "@esbuild/freebsd-x64": "0.25.12", + "@esbuild/linux-arm": "0.25.12", + "@esbuild/linux-arm64": "0.25.12", + "@esbuild/linux-ia32": "0.25.12", + "@esbuild/linux-loong64": "0.25.12", + "@esbuild/linux-mips64el": "0.25.12", + "@esbuild/linux-ppc64": "0.25.12", + "@esbuild/linux-riscv64": "0.25.12", + "@esbuild/linux-s390x": "0.25.12", + "@esbuild/linux-x64": "0.25.12", + "@esbuild/netbsd-arm64": "0.25.12", + "@esbuild/netbsd-x64": "0.25.12", + "@esbuild/openbsd-arm64": "0.25.12", + "@esbuild/openbsd-x64": "0.25.12", + "@esbuild/openharmony-arm64": "0.25.12", + "@esbuild/sunos-x64": "0.25.12", + "@esbuild/win32-arm64": "0.25.12", + "@esbuild/win32-ia32": "0.25.12", + "@esbuild/win32-x64": "0.25.12" + } + }, "node_modules/escalade": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", @@ -2227,6 +4203,12 @@ "node": ">=6" } }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", + "license": "MIT" + }, "node_modules/escodegen": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.1.0.tgz", @@ -2287,6 +4269,15 @@ "node": ">=0.10.0" } }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, "node_modules/events-universal": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/events-universal/-/events-universal-1.0.1.tgz", @@ -2298,6 +4289,102 @@ "bare-events": "^2.7.0" } }, + "node_modules/eventsource": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/eventsource/-/eventsource-3.0.7.tgz", + "integrity": "sha512-CRT1WTyuQoD771GW56XEZFQ/ZoSfWid1alKGDYMmkt2yl8UXrVR4pspqWNEcqKvVIzg6PAltWjxcSSPrboA4iA==", + "license": "MIT", + "dependencies": { + "eventsource-parser": "^3.0.1" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/eventsource-parser": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/eventsource-parser/-/eventsource-parser-3.1.1.tgz", + "integrity": "sha512-EKN1vKAMcZ8MlYMpaNuxN6R9yakzH6uajHcHVTqWJzvu5pWw9DyhbP35HH8MVBQ+dZjAfDxk+A8NiR9KWaXiyQ==", + "license": "MIT", + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/express": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/express/-/express-5.3.0.tgz", + "integrity": "sha512-qCqy1xSoaugxOD4IVHb1ZWdkvCF2W3aE2okKouWj8VwJ+ztskSiXoBnCSJqfKT+cVPJhuJqe9YLbY425+Zh00g==", + "license": "MIT", + "dependencies": { + "accepts": "^2.0.0", + "body-parser": "^2.3.0", + "content-disposition": "^2.0.1", + "content-type": "^2.0.0", + "cookie": "^0.7.1", + "cookie-signature": "^1.2.1", + "debug": "^4.4.0", + "depd": "^2.0.0", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "etag": "^1.8.1", + "finalhandler": "^2.1.0", + "fresh": "^2.0.0", + "http-errors": "^2.0.0", + "merge-descriptors": "^2.0.0", + "mime-types": "^3.0.0", + "on-finished": "^2.4.1", + "once": "^1.4.0", + "parseurl": "^1.3.3", + "proxy-addr": "^2.0.8", + "qs": "^6.16.0", + "range-parser": "^1.2.1", + "router": "^2.2.0", + "send": "^1.1.0", + "serve-static": "^2.2.0", + "statuses": "^2.0.1", + "type-is": "^2.1.0", + "vary": "^1.1.2" + }, + "engines": { + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/express-rate-limit": { + "version": "8.7.1", + "resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-8.7.1.tgz", + "integrity": "sha512-2FXbqkygil3mZiXzEtM0IjE/UBmBNot0J1GEVrJT4JbrvwOSoEtqtO+wHbiMQELWe1eNvHoYRg2dkjOid+6JkA==", + "license": "MIT", + "dependencies": { + "debug": "^4.4.3", + "ip-address": "^10.2.0" + }, + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://github.com/sponsors/express-rate-limit" + }, + "peerDependencies": { + "express": ">= 4.11" + } + }, + "node_modules/express/node_modules/content-type": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-2.1.0.tgz", + "integrity": "sha512-mj7UPXE0jaqaOsukNZRUEfEi2AcL7C/vwmwcHV0O97eO1E1pxBZuyjlZrx5seTaNBg1U6+o35wpa35Qfcc+7ag==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, "node_modules/extract-zip": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-2.0.1.tgz", @@ -2320,6 +4407,12 @@ "@types/yauzl": "^2.9.1" } }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "license": "MIT" + }, "node_modules/fast-fifo": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/fast-fifo/-/fast-fifo-1.3.2.tgz", @@ -2328,6 +4421,22 @@ "license": "MIT", "peer": true }, + "node_modules/fast-uri": { + "version": "3.1.8", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.8.tgz", + "integrity": "sha512-GZMtZUTNRpOVIECoXwLNZS5xUGE+mVNbTB8h/7Rwh2TFWcBQiPzTgyZi05BF9UMZKkLJv8XBRJTlU7zg8+ZfMg==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "BSD-3-Clause" + }, "node_modules/fastdom": { "version": "1.0.12", "resolved": "https://registry.npmjs.org/fastdom/-/fastdom-1.0.12.tgz", @@ -2349,6 +4458,101 @@ "pend": "~1.2.0" } }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/finalhandler": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.1.tgz", + "integrity": "sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA==", + "license": "MIT", + "dependencies": { + "debug": "^4.4.0", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "on-finished": "^2.4.1", + "parseurl": "^1.3.3", + "statuses": "^2.0.1" + }, + "engines": { + "node": ">= 18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fractional-indexing": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/fractional-indexing/-/fractional-indexing-3.2.0.tgz", + "integrity": "sha512-PcOxmqwYCW7O2ovKRU8OoQQj2yqTfEB/yeTYk4gPid6dN5ODRfU1hXd9tTVZzax/0NkO7AxpHykvZnT1aYp/BQ==", + "dev": true, + "license": "CC0-1.0", + "engines": { + "node": "^14.13.1 || >=16.0.0" + } + }, + "node_modules/fresh": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz", + "integrity": "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/fuzzy": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/fuzzy/-/fuzzy-0.1.3.tgz", + "integrity": "sha512-/gZffu4ykarLrCiP3Ygsa86UAo1E5vEVlvTrpkKywXSbP9Xhln3oSp9QSV57gEq3JFFpGJ4GZ+5zdEp3FcUh4w==", + "dev": true, + "engines": { + "node": ">= 0.6.0" + } + }, "node_modules/get-caller-file": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", @@ -2360,6 +4564,53 @@ "node": "6.* || 8.* || >= 10.*" } }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-nonce": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-nonce/-/get-nonce-1.0.1.tgz", + "integrity": "sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/get-stream": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", @@ -2393,6 +4644,38 @@ "node": ">= 14" } }, + "node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/glur": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/glur/-/glur-1.1.2.tgz", + "integrity": "sha512-l+8esYHTKOx2G/Aao4lEQ0bnHWg4fWtJbVoZZT9Knxi01pB8C80BR85nONLFwkkQoFRCmXY+BUcGZN3yZ2QsRA==", + "dev": true, + "license": "MIT" + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/hachure-fill": { "version": "0.5.2", "resolved": "https://registry.npmjs.org/hachure-fill/-/hachure-fill-0.5.2.tgz", @@ -2400,6 +4683,30 @@ "dev": true, "license": "MIT" }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.4.tgz", + "integrity": "sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==", + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/highlight.js": { "version": "11.12.0", "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-11.12.0.tgz", @@ -2410,6 +4717,15 @@ "node": ">=12.0.0" } }, + "node_modules/hono": { + "version": "4.13.13", + "resolved": "https://registry.npmjs.org/hono/-/hono-4.13.13.tgz", + "integrity": "sha512-CQ46U0ZkAGmbT/4UxdzzGJpacP2IeKgY4a5/tOI9AABbpOMfK739wfDXmv1usCk+3RkKj1hQy4/fjhiwa2xlrA==", + "license": "MIT", + "engines": { + "node": ">=16.9.0" + } + }, "node_modules/html-to-image": { "version": "1.11.13", "resolved": "https://registry.npmjs.org/html-to-image/-/html-to-image-1.11.13.tgz", @@ -2417,6 +4733,26 @@ "dev": true, "license": "MIT" }, + "node_modules/http-errors": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz", + "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==", + "license": "MIT", + "dependencies": { + "depd": "~2.0.0", + "inherits": "~2.0.4", + "setprototypeof": "~1.2.0", + "statuses": "~2.0.2", + "toidentifier": "~1.0.1" + }, + "engines": { + "node": ">= 0.8" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, "node_modules/http-proxy-agent": { "version": "7.0.2", "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", @@ -2482,6 +4818,23 @@ "license": "BSD-3-Clause", "peer": true }, + "node_modules/image-blob-reduce": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/image-blob-reduce/-/image-blob-reduce-3.0.1.tgz", + "integrity": "sha512-/VmmWgIryG/wcn4TVrV7cC4mlfUC/oyiKIfSg5eVM3Ten/c1c34RJhMYKCWTnoSMHSqXLt3tsrBR4Q2HInvN+Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "pica": "^7.1.0" + } + }, + "node_modules/immutable": { + "version": "4.3.9", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.3.9.tgz", + "integrity": "sha512-ObHy4YN7ycwZOUCLI1/6svfyAFu7vL8RhAvVu/bh/RZW9EPlOyDaQ9jDQWCtdqzaXUjgXZCW1migtHE7YI7UGQ==", + "dev": true, + "license": "MIT" + }, "node_modules/import-fresh": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", @@ -2511,6 +4864,12 @@ "url": "https://github.com/sponsors/wooorm" } }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "license": "ISC" + }, "node_modules/internmap": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/internmap/-/internmap-2.0.3.tgz", @@ -2525,13 +4884,20 @@ "version": "10.7.4", "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.7.4.tgz", "integrity": "sha512-DWBMKyeM25D70nrIiTkgQth59/G+naKOTep6huF9j+GmeK3DMD60Zvxg5d5qQnz+38AHgou6ean2LECX+lrKDg==", - "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">= 12" } }, + "node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, "node_modules/is-arrayish": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", @@ -2540,6 +4906,19 @@ "license": "MIT", "peer": true }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "license": "MIT", + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/is-docker": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-3.0.0.tgz", @@ -2555,6 +4934,16 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/is-fullwidth-code-point": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", @@ -2566,6 +4955,19 @@ "node": ">=8" } }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/is-inside-container": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-inside-container/-/is-inside-container-1.0.0.tgz", @@ -2584,6 +4986,22 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-promise": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz", + "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==", + "license": "MIT" + }, "node_modules/is-wsl": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-3.1.1.tgz", @@ -2599,6 +5017,21 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "license": "ISC" + }, + "node_modules/jose": { + "version": "6.2.12", + "resolved": "https://registry.npmjs.org/jose/-/jose-6.2.12.tgz", + "integrity": "sha512-9NiFmJEex0sy2Dk58j2UGBSHgUs2ypF9eZSu4L6vjOX3Dp96Sw1F3uL+H+D1sx02jZZdzUT0HgvCy59CuvXcWw==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/panva" + } + }, "node_modules/jotai": { "version": "2.20.3", "resolved": "https://registry.npmjs.org/jotai/-/jotai-2.20.3.tgz", @@ -2629,13 +5062,23 @@ } } }, + "node_modules/jotai-scope": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/jotai-scope/-/jotai-scope-0.7.2.tgz", + "integrity": "sha512-Gwed97f3dDObrO43++2lRcgOqw4O2sdr4JCjP/7eHK1oPACDJ7xKHGScpJX9XaflU+KBHXF+VhwECnzcaQiShg==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "jotai": ">=2.9.2", + "react": ">=17.0.0" + } + }, "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/js-yaml": { "version": "4.3.2", @@ -2669,6 +5112,18 @@ "license": "MIT", "peer": true }, + "node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "license": "MIT" + }, + "node_modules/json-schema-typed": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/json-schema-typed/-/json-schema-typed-8.0.2.tgz", + "integrity": "sha512-fQhoXdcvc3V28x7C7BMs4P5+kNlgUURe2jmUT1T//oBRMDrqy1QPelJimwZGo7Hg9VPV3EQV5Bnq4hbFy2vetA==", + "license": "BSD-2-Clause" + }, "node_modules/katex": { "version": "0.16.47", "resolved": "https://registry.npmjs.org/katex/-/katex-0.16.47.tgz", @@ -2702,6 +5157,23 @@ "integrity": "sha512-Ls993zuzfayK269Svk9hzpeGUKob/sIgZzyHYdjQoAdQetRKpOLj+k/QQQ/6Qi0Yz65mlROrfd+Ev+1+7dz9Kw==", "dev": true }, + "node_modules/langium": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/langium/-/langium-3.3.1.tgz", + "integrity": "sha512-QJv/h939gDpvT+9SiLVlY7tZC3xB2qK57v0J04Sh9wpMb6MP1q8gB21L3WIo8T5P1MSMg3Ep14L7KkDCFG3y4w==", + "dev": true, + "license": "MIT", + "dependencies": { + "chevrotain": "~11.0.3", + "chevrotain-allstar": "~0.3.0", + "vscode-languageserver": "~9.0.1", + "vscode-languageserver-textdocument": "~1.0.11", + "vscode-uri": "~3.0.8" + }, + "engines": { + "node": ">=16.0.0" + } + }, "node_modules/layout-base": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/layout-base/-/layout-base-1.0.2.tgz", @@ -2724,6 +5196,33 @@ "dev": true, "license": "MIT" }, + "node_modules/lodash.debounce": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", + "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.throttle": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.throttle/-/lodash.throttle-4.1.1.tgz", + "integrity": "sha512-wIkUCfVKpVsWo3JSZlc+8MB5it+2AN5W8J7YVMST30UrvcQNZ1Okbj+rbVniijTWE6FGYy4XJq/rHkas8qJMLQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "js-tokens": "^3.0.0 || ^4.0.0" + }, + "bin": { + "loose-envify": "cli.js" + } + }, "node_modules/lru-cache": { "version": "7.18.3", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", @@ -2748,6 +5247,40 @@ "node": ">= 12" } }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/media-typer": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.1.tgz", + "integrity": "sha512-yz3xRaG20c6/BOzvYoDaGtPmGscs7YivItZEEqe6GbwNfHuxu9YNmvnEkMzKldAGY4/80pRcQRZSEnhquk9XuQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/merge-descriptors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-2.0.0.tgz", + "integrity": "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/mermaid": { "version": "11.17.2", "resolved": "https://registry.npmjs.org/mermaid/-/mermaid-11.17.2.tgz", @@ -2785,11 +5318,36 @@ "integrity": "sha512-TI3V8YYWvkVf3KJe1dRkpnjs68JUPyEa5vjKrp1XEEJUAOaQc+Qj+L1qWbPd0SJuAdQkFU0h73sXXqwDYxsiDA==", "dev": true, "license": "MIT", - "bin": { - "marked": "bin/marked.js" + "bin": { + "marked": "bin/marked.js" + }, + "engines": { + "node": ">= 20" + } + }, + "node_modules/mime-db": { + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", + "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.2.tgz", + "integrity": "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==", + "license": "MIT", + "dependencies": { + "mime-db": "^1.54.0" }, "engines": { - "node": ">= 20" + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/mitt": { @@ -2804,9 +5362,66 @@ "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/multimath": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/multimath/-/multimath-2.0.0.tgz", + "integrity": "sha512-toRx66cAMJ+Ccz7pMIg38xSIrtnbozk0dchXezwQDMgQmbGpfxjtv68H+L00iFL8hxDaVjrmwAFSb3I6bg8Q2g==", "dev": true, "license": "MIT", - "peer": true + "dependencies": { + "glur": "^1.1.2", + "object-assign": "^4.1.1" + } + }, + "node_modules/nanoid": { + "version": "3.3.20", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.20.tgz", + "integrity": "sha512-uKdg2G3GNCKQn9byYOpxbGqrT2fGO5KRt5J/8b3pok8rT6qxGWF6hxMyJiEYtAf+FVyYuD9hRaDqX5uPFYJ4ZQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/negotiator": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.1.0.tgz", + "integrity": "sha512-NMPBRMJgiQHjbd8phG3Vebdx4kZ1H121rbl5IkMqeOsahptB9BKo/d7oJ3zTXqTgagn2bWlNSXkh0QUGM31RYg==", + "license": "MIT", + "dependencies": { + "content-type": "^2.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/negotiator/node_modules/content-type": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-2.1.0.tgz", + "integrity": "sha512-mj7UPXE0jaqaOsukNZRUEfEi2AcL7C/vwmwcHV0O97eO1E1pxBZuyjlZrx5seTaNBg1U6+o35wpa35Qfcc+7ag==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } }, "node_modules/netmask": { "version": "2.1.1", @@ -2819,13 +5434,54 @@ "node": ">= 0.4.0" } }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-inspect": { + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "license": "MIT", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "dev": true, "license": "ISC", - "peer": true, "dependencies": { "wrappy": "1" } @@ -2848,6 +5504,13 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/open-color": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/open-color/-/open-color-1.9.1.tgz", + "integrity": "sha512-vCseG/EQ6/RcvxhUcGJiHViOgrtz4x0XbZepXvKik66TMGkvbmjeJrKFyBEx6daG5rNyyd14zYXhz0hZVwQFOw==", + "dev": true, + "license": "MIT" + }, "node_modules/pac-proxy-agent": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/pac-proxy-agent/-/pac-proxy-agent-7.2.0.tgz", @@ -2891,6 +5554,13 @@ "dev": true, "license": "MIT" }, + "node_modules/pako": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/pako/-/pako-2.0.3.tgz", + "integrity": "sha512-WjR1hOeg+kki3ZIOjaf4b5WVcay1jaliKSYiEaB1XzwhMQZJxRdQRv0V31EKBYlxb4T7SK3hjfc/jxyU64BoSw==", + "dev": true, + "license": "(MIT AND Zlib)" + }, "node_modules/parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", @@ -2925,6 +5595,27 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/parse5": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.3.0.tgz", + "integrity": "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==", + "license": "MIT", + "dependencies": { + "entities": "^6.0.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/path-data-parser": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/path-data-parser/-/path-data-parser-0.1.0.tgz", @@ -2932,6 +5623,25 @@ "dev": true, "license": "MIT" }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-to-regexp": { + "version": "8.4.2", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.4.2.tgz", + "integrity": "sha512-qRcuIdP69NPm4qbACK+aDogI5CBDMi1jKe0ry5rSQJz8JVLsC7jV8XpiJjGRLLol3N+R5ihGYcrPLTno6pAdBA==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, "node_modules/pend": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", @@ -2940,6 +5650,27 @@ "license": "MIT", "peer": true }, + "node_modules/perfect-freehand": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/perfect-freehand/-/perfect-freehand-1.2.0.tgz", + "integrity": "sha512-h/0ikF1M3phW7CwpZ5MMvKnfpHficWoOEyr//KVNTxV4F6deRK1eYMtHyBKEAKFK0aXIEUK9oBvlF6PNXMDsAw==", + "dev": true, + "license": "MIT" + }, + "node_modules/pica": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/pica/-/pica-7.1.1.tgz", + "integrity": "sha512-WY73tMvNzXWEld2LicT9Y260L43isrZ85tPuqRyvtkljSDLmnNFQmZICt4xUJMVulmcc6L9O7jbBrtx3DOz/YQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "glur": "^1.1.2", + "inherits": "^2.0.3", + "multimath": "^2.0.0", + "object-assign": "^4.1.1", + "webworkify": "^1.5.0" + } + }, "node_modules/picocolors": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", @@ -2948,6 +5679,28 @@ "license": "ISC", "peer": true }, + "node_modules/picomatch": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", + "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pkce-challenge": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/pkce-challenge/-/pkce-challenge-5.0.1.tgz", + "integrity": "sha512-wQ0b/W4Fr01qtpHlqSqspcj3EhBvimsdh0KlHhH8HRZnMsEa0ea2fTULOXOS9ccQr3om+GcGRk4e+isrZWV8qQ==", + "license": "MIT", + "engines": { + "node": ">=16.20.0" + } + }, "node_modules/playwright": { "version": "1.64.1", "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.64.1.tgz", @@ -2992,6 +5745,34 @@ "node": ">=20" } }, + "node_modules/png-chunk-text": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/png-chunk-text/-/png-chunk-text-1.0.0.tgz", + "integrity": "sha512-DEROKU3SkkLGWNMzru3xPVgxyd48UGuMSZvioErCure6yhOc/pRH2ZV+SEn7nmaf7WNf3NdIpH+UTrRdKyq9Lw==", + "dev": true, + "license": "MIT" + }, + "node_modules/png-chunks-encode": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/png-chunks-encode/-/png-chunks-encode-1.0.0.tgz", + "integrity": "sha512-J1jcHgbQRsIIgx5wxW9UmCymV3wwn4qCCJl6KYgEU/yHCh/L2Mwq/nMOkRPtmV79TLxRZj5w3tH69pvygFkDqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "crc-32": "^0.3.0", + "sliced": "^1.0.1" + } + }, + "node_modules/png-chunks-extract": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/png-chunks-extract/-/png-chunks-extract-1.0.0.tgz", + "integrity": "sha512-ZiVwF5EJ0DNZyzAqld8BP1qyJBaGOFaq9zl579qfbkcmOwWLLO4I9L8i2O4j3HkI6/35i0nKG2n+dZplxiT89Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "crc-32": "^0.3.0" + } + }, "node_modules/points-on-curve": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/points-on-curve/-/points-on-curve-0.2.0.tgz", @@ -3021,6 +5802,23 @@ "node": ">=0.4.0" } }, + "node_modules/proxy-addr": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.8.tgz", + "integrity": "sha512-5nnx0yGyVUcY6t9RnWcARWtwT9F1D8O9rt08htPvnd49W1IgZtmLkhu9WfMzQj1cFxjHIO6connUNVW5k7AVyQ==", + "license": "MIT", + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, "node_modules/proxy-agent": { "version": "6.5.0", "resolved": "https://registry.npmjs.org/proxy-agent/-/proxy-agent-6.5.0.tgz", @@ -3105,12 +5903,82 @@ "node": ">=18" } }, + "node_modules/pwacompat": { + "version": "2.0.17", + "resolved": "https://registry.npmjs.org/pwacompat/-/pwacompat-2.0.17.tgz", + "integrity": "sha512-6Du7IZdIy7cHiv7AhtDy4X2QRM8IAD5DII69mt5qWibC2d15ZU8DmBG1WdZKekG11cChSu4zkSUGPF9sweOl6w==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/qs": { + "version": "6.16.1", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.16.1.tgz", + "integrity": "sha512-5Px6J7rfHBAGUhNQ3es4YzcOdBBw0SDcVtThJml4RdnKw4tKs2epA0kPZ+K8X+WiHktT93RHVcQ/3ym5aTCgtg==", + "license": "BSD-3-Clause", + "dependencies": { + "es-define-property": "^1.0.1", + "side-channel": "^1.1.1" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/range-parser": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.3.0.tgz", + "integrity": "sha512-hek2mFQpPuI4E1BBKrSto+BU3e3x4xuarsbiwr3+lf7p44juvFMV0XFWQAP3xUyqXA4RrXLIoaSUGbSt056ZMw==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/raw-body": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.2.tgz", + "integrity": "sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA==", + "license": "MIT", + "dependencies": { + "bytes": "~3.1.2", + "http-errors": "~2.0.1", + "iconv-lite": "~0.7.0", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/raw-body/node_modules/iconv-lite": { + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.3.tgz", + "integrity": "sha512-IKXpvIzjnC9XTAUbVBcMfGS0EPaIXtW6v+zr+RRp+hqULEpo0owZax6wyRwPOJbWbzjYspQwusTsfVr0ifh4uQ==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, "node_modules/react": { - "version": "19.3.1", - "resolved": "https://registry.npmjs.org/react/-/react-19.3.1.tgz", - "integrity": "sha512-QNhWdt7nrgVWWQG58QuGsAR3jv3Hbe4mb4vz7w2csnHavciZJgda1CPNaldF6eVxv90ud3jUXpKlHL69gO38yw==", + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", + "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", "dev": true, "license": "MIT", + "dependencies": { + "loose-envify": "^1.1.0" + }, "engines": { "node": ">=0.10.0" } @@ -3138,16 +6006,66 @@ } }, "node_modules/react-dom": { - "version": "19.3.1", - "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.3.1.tgz", - "integrity": "sha512-Y0bARz53lhEC1NMCASs0/j+faB/zqt3R8hpvg1e8+61ZVJ4UL0UdC4cBbXfZMcIuhrfgeILTLe+ZeBCavQPtKQ==", + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", + "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==", "dev": true, "license": "MIT", "dependencies": { - "scheduler": "^0.28.0" + "loose-envify": "^1.1.0", + "scheduler": "^0.23.2" }, "peerDependencies": { - "react": "^19.3.1" + "react": "^18.3.1" + } + }, + "node_modules/react-remove-scroll": { + "version": "2.7.2", + "resolved": "https://registry.npmjs.org/react-remove-scroll/-/react-remove-scroll-2.7.2.tgz", + "integrity": "sha512-Iqb9NjCCTt6Hf+vOdNIZGdTiH1QSqr27H/Ek9sv/a97gfueI/5h1s3yRi1nngzMUaOOToin5dI1dXKdXiF+u0Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "react-remove-scroll-bar": "^2.3.7", + "react-style-singleton": "^2.2.3", + "tslib": "^2.1.0", + "use-callback-ref": "^1.3.3", + "use-sidecar": "^1.1.3" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/react-remove-scroll-bar": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/react-remove-scroll-bar/-/react-remove-scroll-bar-2.3.8.tgz", + "integrity": "sha512-9r+yi9+mgU33AKcj6IbT9oRCO78WriSj6t/cF8DWBZJ9aOGPOTEDvdUDz1FwKim7QXWwmHqtdHnRJfhAxEG46Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "react-style-singleton": "^2.2.2", + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } } }, "node_modules/react-stately": { @@ -3168,6 +6086,42 @@ "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, + "node_modules/react-style-singleton": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/react-style-singleton/-/react-style-singleton-2.2.3.tgz", + "integrity": "sha512-b6jSvxvVnyptAiLjbkWLE/lOnR4lfTtDAl+eUC7RZy+QQWc6wRzIV2CE6xBuMmDxc2qIihtDCZD5NPOFl7fRBQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "get-nonce": "^1.0.0", + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, "node_modules/require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", @@ -3179,6 +6133,15 @@ "node": ">=0.10.0" } }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/resolve-from": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", @@ -3204,10 +6167,26 @@ "dev": true, "license": "MIT", "dependencies": { - "hachure-fill": "^0.5.2", - "path-data-parser": "^0.1.0", - "points-on-curve": "^0.2.0", - "points-on-path": "^0.2.1" + "hachure-fill": "^0.5.2", + "path-data-parser": "^0.1.0", + "points-on-curve": "^0.2.0", + "points-on-path": "^0.2.1" + } + }, + "node_modules/router": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/router/-/router-2.2.0.tgz", + "integrity": "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==", + "license": "MIT", + "dependencies": { + "debug": "^4.4.0", + "depd": "^2.0.0", + "is-promise": "^4.0.0", + "parseurl": "^1.3.3", + "path-to-regexp": "^8.0.0" + }, + "engines": { + "node": ">= 18" } }, "node_modules/run-applescript": { @@ -3233,15 +6212,35 @@ "version": "2.1.2", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", - "dev": true, "license": "MIT" }, + "node_modules/sass": { + "version": "1.51.0", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.51.0.tgz", + "integrity": "sha512-haGdpTgywJTvHC2b91GSq+clTKGbtkkZmVAb82jZQN/wTy6qs8DdFm2lhEQbEwrY0QDRgSQ3xDurqM977C3noA==", + "dev": true, + "license": "MIT", + "dependencies": { + "chokidar": ">=3.0.0 <4.0.0", + "immutable": "^4.0.0", + "source-map-js": ">=0.6.2 <2.0.0" + }, + "bin": { + "sass": "sass.js" + }, + "engines": { + "node": ">=12.0.0" + } + }, "node_modules/scheduler": { - "version": "0.28.0", - "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.28.0.tgz", - "integrity": "sha512-juorfCmIkIw8tT+p5BXSm6PJjQF/ycEYmKyzURCIt/RaZIhL+PulbQ9Yu2z1HdOJDdqDTlxA1+xKBmHXJsczAw==", + "version": "0.23.2", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz", + "integrity": "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==", "dev": true, - "license": "MIT" + "license": "MIT", + "dependencies": { + "loose-envify": "^1.1.0" + } }, "node_modules/semver": { "version": "7.8.5", @@ -3257,6 +6256,158 @@ "node": ">=10" } }, + "node_modules/send": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/send/-/send-1.2.1.tgz", + "integrity": "sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ==", + "license": "MIT", + "dependencies": { + "debug": "^4.4.3", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "etag": "^1.8.1", + "fresh": "^2.0.0", + "http-errors": "^2.0.1", + "mime-types": "^3.0.2", + "ms": "^2.1.3", + "on-finished": "^2.4.1", + "range-parser": "^1.2.1", + "statuses": "^2.0.2" + }, + "engines": { + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/serve-static": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.2.1.tgz", + "integrity": "sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw==", + "license": "MIT", + "dependencies": { + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "parseurl": "^1.3.3", + "send": "^1.2.0" + }, + "engines": { + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "license": "ISC" + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/side-channel": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.1.tgz", + "integrity": "sha512-6x6dK6zJdpTzF4sQeNYxwtvBzf6Eg4GtlesS94HOvTudUeyK2WXAaIfmDgsyslYrRBeFIlsi54AYsFGUuhmvrQ==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.4", + "side-channel-list": "^1.0.1", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-list": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.1.tgz", + "integrity": "sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/sliced": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/sliced/-/sliced-1.0.1.tgz", + "integrity": "sha512-VZBmZP8WU3sMOZm1bdgTadsQbcscK0UM8oKxKVBs4XAhUo2Xxzm/OFMGBkPusxw9xL3Uy8LrzEqGqJhclsr0yA==", + "deprecated": "Unsupported", + "dev": true, + "license": "MIT" + }, "node_modules/smart-buffer": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", @@ -3313,6 +6464,25 @@ "node": ">=0.10.0" } }, + "node_modules/source-map-js": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.2.tgz", + "integrity": "sha512-KGj/8Y43x35aZVDtt+J4mK1hoLGHULMYfSkODJNQjNDC3oW1PqPoxMwo0pLUsWM/UEGzON/NxeHywEfNXNP3Vw==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/statuses": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", + "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/streamx": { "version": "2.28.1", "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.28.1.tgz", @@ -3466,6 +6636,28 @@ "node": ">=18" } }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "license": "MIT", + "engines": { + "node": ">=0.6" + } + }, "node_modules/ts-dedent": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/ts-dedent/-/ts-dedent-2.3.0.tgz", @@ -3483,6 +6675,47 @@ "dev": true, "license": "0BSD" }, + "node_modules/tunnel-rat": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/tunnel-rat/-/tunnel-rat-0.1.2.tgz", + "integrity": "sha512-lR5VHmkPhzdhrM092lI2nACsLO4QubF0/yoOhzX7c+wIpbN1GjHNzCc91QlpxBi+cnx8vVJ+Ur6vL5cEoQPFpQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "zustand": "^4.3.2" + } + }, + "node_modules/type-is": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.1.0.tgz", + "integrity": "sha512-faYHw0anBbc/kWF3zFTEnxSFOAGUX9GFbOBthvDdLsIlEoWOFOtS0zgCiQYwIskL9iGXZL3kAXD8OoZ4GmMATA==", + "license": "MIT", + "dependencies": { + "content-type": "^2.0.0", + "media-typer": "^1.1.0", + "mime-types": "^3.0.0" + }, + "engines": { + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/type-is/node_modules/content-type": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-2.1.0.tgz", + "integrity": "sha512-mj7UPXE0jaqaOsukNZRUEfEi2AcL7C/vwmwcHV0O97eO1E1pxBZuyjlZrx5seTaNBg1U6+o35wpa35Qfcc+7ag==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, "node_modules/typed-query-selector": { "version": "2.12.3", "resolved": "https://registry.npmjs.org/typed-query-selector/-/typed-query-selector-2.12.3.tgz", @@ -3512,6 +6745,60 @@ "optional": true, "peer": true }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/use-callback-ref": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/use-callback-ref/-/use-callback-ref-1.3.3.tgz", + "integrity": "sha512-jQL3lRnocaFtu3V00JToYz/4QkNWswxijDaCVNZRiRTO3HQDLsdu1ZtmIUvV4yPp+rvWm5j0y0TG/S61cuijTg==", + "dev": true, + "license": "MIT", + "dependencies": { + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/use-sidecar": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/use-sidecar/-/use-sidecar-1.1.3.tgz", + "integrity": "sha512-Fedw0aZvkhynoPYlA5WXrMCAMm+nSWdZt6lzJQ7Ok8S6Q+VsHmHpRWndVRJ8Be0ZbkfPc5LRYH+5XrzXcEeLRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "detect-node-es": "^1.1.0", + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/use-sync-external-store": { "version": "1.7.0", "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.7.0.tgz", @@ -3536,6 +6823,92 @@ "uuid": "dist-node/bin/uuid" } }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/vscode-jsonrpc": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-8.2.0.tgz", + "integrity": "sha512-C+r0eKJUIfiDIfwJhria30+TYWPtuHJXHtI7J0YlOmKAo7ogxP20T0zxB7HZQIFhIyvoBPwWskjxrvAtfjyZfA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/vscode-languageserver": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/vscode-languageserver/-/vscode-languageserver-9.0.1.tgz", + "integrity": "sha512-woByF3PDpkHFUreUa7Hos7+pUWdeWMXRd26+ZX2A8cFx6v/JPTtd4/uN0/jB6XQHYaOlHbio03NTHCqrgG5n7g==", + "dev": true, + "license": "MIT", + "dependencies": { + "vscode-languageserver-protocol": "3.17.5" + }, + "bin": { + "installServerIntoExtension": "bin/installServerIntoExtension" + } + }, + "node_modules/vscode-languageserver-protocol": { + "version": "3.17.5", + "resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.17.5.tgz", + "integrity": "sha512-mb1bvRJN8SVznADSGWM9u/b07H7Ecg0I3OgXDuLdn307rl/J3A9YD6/eYOssqhecL27hK1IPZAsaqh00i/Jljg==", + "dev": true, + "license": "MIT", + "dependencies": { + "vscode-jsonrpc": "8.2.0", + "vscode-languageserver-types": "3.17.5" + } + }, + "node_modules/vscode-languageserver-textdocument": { + "version": "1.0.15", + "resolved": "https://registry.npmjs.org/vscode-languageserver-textdocument/-/vscode-languageserver-textdocument-1.0.15.tgz", + "integrity": "sha512-Gsfr9S8LU3S6885cS+NaleasJ0n3V5y/SOTqHCifQiXWFw3G9w9/YItDfwHrlCz+dSZWWZ8ilYVMcJe7Sz7Sxw==", + "dev": true, + "license": "MIT" + }, + "node_modules/vscode-languageserver-types": { + "version": "3.17.5", + "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.17.5.tgz", + "integrity": "sha512-Ld1VelNuX9pdF39h2Hgaeb5hEZM2Z3jUrrMgWQAu82jMtZp7p3vJT3BzToKtZI7NgQssZje5o0zryOrhQvzQAg==", + "dev": true, + "license": "MIT" + }, + "node_modules/vscode-uri": { + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-3.0.8.tgz", + "integrity": "sha512-AyFQ0EVmsOZOlAnxoFOGOq1SQDWAB7C6aqMGS23svWAllfOaxbuFvcT8D1i8z3Gyn8fraVeZNNmN6e9bxxXkKw==", + "dev": true, + "license": "MIT" + }, + "node_modules/webworkify": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/webworkify/-/webworkify-1.5.0.tgz", + "integrity": "sha512-AMcUeyXAhbACL8S2hqqdqOLqvJ8ylmIbNwUIqQujRSouf4+eUFaXbG6F1Rbu+srlJMmxQWsiU7mOJi0nMBfM1g==", + "dev": true, + "license": "MIT" + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, "node_modules/wrap-ansi": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", @@ -3559,9 +6932,7 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", - "dev": true, - "license": "ISC", - "peer": true + "license": "ISC" }, "node_modules/ws": { "version": "8.22.0", @@ -3656,15 +7027,51 @@ } }, "node_modules/zod": { - "version": "3.23.8", - "resolved": "https://registry.npmjs.org/zod/-/zod-3.23.8.tgz", - "integrity": "sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g==", - "dev": true, + "version": "4.6.7", + "resolved": "https://registry.npmjs.org/zod/-/zod-4.6.7.tgz", + "integrity": "sha512-g8W2D2kU6a8untc053fX1uVBKzM7yqAxW/3zwk83+6RyF2+SKOuV+c0wBDhUwzV1PaGapmZH+rmcUzPf+3CGwg==", "license": "MIT", - "peer": true, "funding": { "url": "https://github.com/sponsors/colinhacks" } + }, + "node_modules/zod-to-json-schema": { + "version": "3.25.2", + "resolved": "https://registry.npmjs.org/zod-to-json-schema/-/zod-to-json-schema-3.25.2.tgz", + "integrity": "sha512-O/PgfnpT1xKSDeQYSCfRI5Gy3hPf91mKVDuYLUHZJMiDFptvP41MSnWofm8dnCm0256ZNfZIM7DSzuSMAFnjHA==", + "license": "ISC", + "peerDependencies": { + "zod": "^3.25.28 || ^4" + } + }, + "node_modules/zustand": { + "version": "4.5.7", + "resolved": "https://registry.npmjs.org/zustand/-/zustand-4.5.7.tgz", + "integrity": "sha512-CHOUy7mu3lbD6o6LJLfllpjkzhHXSBlX8B9+qPddUsIfeF5S/UZ5q0kmCsnRqT1UHFQZchNFDDzMbQsuesHWlw==", + "dev": true, + "license": "MIT", + "dependencies": { + "use-sync-external-store": "^1.2.2" + }, + "engines": { + "node": ">=12.7.0" + }, + "peerDependencies": { + "@types/react": ">=16.8", + "immer": ">=9.0.6", + "react": ">=16.8" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "immer": { + "optional": true + }, + "react": { + "optional": true + } + } } } } diff --git a/package.json b/package.json index b701d31..371cb04 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@yeefeiooi/htmldrop", - "version": "1.16.0", + "version": "1.17.0", "description": "Publish HTML files and get shareable links via Surge.sh", "type": "module", "repository": { @@ -27,7 +27,10 @@ }, "scripts": { "test": "node --test test/*.test.mjs && node test-fixtures/crypto-roundtrip.test.mjs && node test-fixtures/access-token.test.mjs", - "diagrams": "mmdc -i docs/diagrams/collab-flow.mmd -o docs/assets/collab-flow.svg -c docs/diagrams/theme.json -p docs/diagrams/puppeteer.json -b transparent && mmdc -i docs/diagrams/collab-flow.mmd -o docs/assets/collab-flow.png -c docs/diagrams/theme.json -p docs/diagrams/puppeteer.json -b white -s 2" + "diagrams": "mmdc -i docs/diagrams/collab-flow.mmd -o docs/assets/collab-flow.svg -c docs/diagrams/theme.json -p docs/diagrams/puppeteer.json -b transparent && mmdc -i docs/diagrams/collab-flow.mmd -o docs/assets/collab-flow.png -c docs/diagrams/theme.json -p docs/diagrams/puppeteer.json -b white -s 2", + "build:editor": "node scripts/build-editor.mjs", + "pretest": "npm run build:editor", + "prepack": "npm run build:editor" }, "keywords": [ "html", @@ -41,9 +44,12 @@ "author": "", "license": "MIT", "dependencies": { + "@modelcontextprotocol/sdk": "1.32.1", "commander": "^12.1.0", "crypto-js": "^4.2.0", - "open": "^10.1.0" + "open": "^10.1.0", + "parse5": "7.3.0", + "zod": "4.6.7" }, "peerDependencies": { "surge": ">=0.23.0" @@ -54,7 +60,21 @@ } }, "devDependencies": { + "@excalidraw/excalidraw": "0.18.1", + "@excalidraw/mermaid-to-excalidraw": "2.2.2", "@mermaid-js/mermaid-cli": "11.4.2", - "playwright": "^1.61.1" + "esbuild": "0.25.12", + "playwright": "^1.61.1", + "react": "18.3.1", + "react-dom": "18.3.1" + }, + "overrides": { + "@excalidraw/excalidraw": { + "nanoid": "3.3.20" + }, + "@excalidraw/mermaid-to-excalidraw": { + "nanoid": "5.1.16" + }, + "lodash-es": "4.18.1" } } diff --git a/scripts/build-editor.mjs b/scripts/build-editor.mjs new file mode 100644 index 0000000..54d4b6f --- /dev/null +++ b/scripts/build-editor.mjs @@ -0,0 +1,61 @@ +import { build } from "esbuild"; +import { mkdir, rm, cp, writeFile } from "node:fs/promises"; +import { fileURLToPath } from "node:url"; +import { relative, resolve } from "node:path"; +const root = fileURLToPath(new URL("../", import.meta.url)); +const out = `${root}src/edit/editor-dist`; +await rm(out, { recursive: true, force: true }); +await mkdir(out, { recursive: true }); +await build({ + entryPoints: [`${root}src/edit/browser/editor.jsx`], + outdir: out, + entryNames: "editor", + chunkNames: "chunks/[name]-[hash]", + bundle: true, + splitting: true, + format: "esm", + platform: "browser", + target: ["es2022"], + minify: true, + sourcemap: false, + conditions: ["production"], + define: { "process.env.NODE_ENV": '"production"' }, + loader: { + ".woff2": "file", + ".woff": "file", + ".ttf": "file", + ".wasm": "file", + }, + legalComments: "linked", + metafile: true, +}).then(async (result) => { + await writeFile( + `${out}/manifest.json`, + JSON.stringify( + Object.keys(result.metafile.outputs) + .map((path) => relative(out, resolve(path))) + .sort(), + null, + 2, + ), + ); +}); +await cp( + `${root}node_modules/@excalidraw/excalidraw/dist/prod/fonts`, + `${out}/fonts`, + { recursive: true }, +); +await cp(`${root}src/edit/browser/notices`, `${out}/notices`, { + recursive: true, +}); +for (const name of [ + "react", + "react-dom", + "@excalidraw/mermaid-to-excalidraw", +]) { + await cp( + `${root}node_modules/${name}/LICENSE`, + `${out}/notices/${name.replaceAll("/", "-")}.txt`, + ); +} +console.error("Built packaged Excalidraw editor and local fonts."); diff --git a/src/diagrams/document.js b/src/diagrams/document.js new file mode 100644 index 0000000..eb35e57 --- /dev/null +++ b/src/diagrams/document.js @@ -0,0 +1,250 @@ +import { createHash } from 'node:crypto'; +import { parse, parseFragment } from 'parse5'; + +export const LIMITS = Object.freeze({ html: 2 * 1024 * 1024, scene: 6 * 1024 * 1024, svg: 8 * 1024 * 1024, elements: 5000, files: 100, file: 3 * 1024 * 1024 }); +const UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i; +const SHAPE_ID = /^[\w-]{1,128}$/; +const TYPES = new Set(['rectangle', 'diamond', 'ellipse', 'text', 'line', 'arrow', 'freedraw', 'image', 'frame', 'magicframe']); +export function fail(message, statusCode = 400, extra = {}) { throw Object.assign(new Error(message), { statusCode, ...extra }); } +export const sourceHash = (source) => createHash('sha256').update(source).digest('hex'); +export const isUUID = (id) => typeof id === 'string' && UUID.test(id); +const object = (value) => value && typeof value === 'object' && !Array.isArray(value); +const byteLength = (value) => Buffer.byteLength(value, 'utf8'); + +function jsonClone(value, maxBytes) { + let budget = 200000; + function visit(item, depth) { + if (--budget < 0 || depth > 32) fail('Scene structure exceeds the supported limits'); + if (item === null || typeof item === 'boolean') return; + if (typeof item === 'number') { if (!Number.isFinite(item) || Math.abs(item) > Number.MAX_SAFE_INTEGER) fail('Scene numbers must be finite and bounded'); return; } + if (typeof item === 'string') { if (item.length > LIMITS.file * 2) fail('Scene string exceeds the supported limit'); return; } + if (typeof item !== 'object') fail('Scene must contain JSON values only'); + for (const [key, entry] of Object.entries(item)) { + if (['__proto__', 'prototype', 'constructor'].includes(key)) fail('Unsupported scene property'); + visit(entry, depth + 1); + } + } + visit(value, 0); + const text = JSON.stringify(value); + if (byteLength(text) > maxBytes) fail('Scene exceeds the supported byte limit'); + return JSON.parse(text); +} + +function rasterData(value) { + if (typeof value !== 'string' || !/^data:image\/(png|jpeg|gif|webp);base64,[A-Za-z0-9+/]+={0,2}$/.test(value)) fail('Images must be bounded PNG, JPEG, GIF or WebP data URLs'); + const content = value.slice(value.indexOf(',') + 1); + if (content.length % 4 !== 0 || Buffer.from(content, 'base64').length > LIMITS.file) fail('Image data exceeds the supported limit or is invalid'); +} + +export function validateScene(input) { + if (!object(input) || input.type !== 'excalidraw' || input.version !== 2 || !Array.isArray(input.elements) || !object(input.appState) || !object(input.files)) fail('A native Excalidraw version 2 scene with elements, appState and files is required'); + const scene = jsonClone(input, LIMITS.scene); + if (scene.elements.length > LIMITS.elements) fail('Too many diagram elements'); + const ids = new Set(); + for (const el of scene.elements) { + if (!object(el) || !TYPES.has(el.type) || !SHAPE_ID.test(el.id || '') || ids.has(el.id)) fail('Elements require unique stable IDs and a supported native type'); + ids.add(el.id); + for (const field of ['x', 'y', 'width', 'height']) if (typeof el[field] !== 'number' || !Number.isFinite(el[field]) || Math.abs(el[field]) > 1e7 || (['width', 'height'].includes(field) && el[field] < 0)) fail('Element geometry must be finite and bounded'); + if (el.link != null && el.link !== '') fail('Linked shapes and web embeds are not supported'); + if (el.type === 'text' && (typeof el.text !== 'string' || el.text.length > 100000)) fail('Text elements need bounded text'); + if (el.angle != null && (typeof el.angle !== 'number' || Math.abs(el.angle) > 1e7)) fail('Element angle must be finite and bounded'); + if (el.points != null && (!Array.isArray(el.points) || el.points.length > 20000 || el.points.some((point) => !Array.isArray(point) || point.length !== 2 || point.some((n) => typeof n !== 'number' || Math.abs(n) > 1e7)))) fail('Invalid line or freehand points'); + } + const entries = Object.entries(scene.files); + if (entries.length > LIMITS.files) fail('Too many image files'); + for (const [id, file] of entries) { + if (!SHAPE_ID.test(id) || !object(file) || file.id !== id) fail('Invalid image file identity'); + rasterData(file.dataURL); + if (!['image/png', 'image/jpeg', 'image/gif', 'image/webp'].includes(file.mimeType) || !file.dataURL.startsWith(`data:${file.mimeType};base64,`)) fail('Invalid image MIME type'); + } + for (const el of scene.elements) if (el.type === 'image' && el.fileId != null && !Object.hasOwn(scene.files, el.fileId)) fail('Image shape references a missing file'); + for (const el of scene.elements) { + const reference = (id) => { if (typeof id !== 'string' || !ids.has(id)) fail('Element references a missing shape'); }; + for (const field of ['containerId', 'frameId']) if (el[field] != null) reference(el[field]); + for (const field of ['startBinding', 'endBinding']) if (el[field] != null) { + if (!object(el[field])) fail('Invalid shape binding'); + reference(el[field].elementId); + } + if (el.boundElements != null) { + if (!Array.isArray(el.boundElements) || el.boundElements.length > LIMITS.elements) fail('Invalid bound shape list'); + for (const bound of el.boundElements) { if (!object(bound)) fail('Invalid bound shape'); reference(bound.id); } + } + } + function checkLinks(value) { + if (!object(value) && !Array.isArray(value)) return; + for (const [key, item] of Object.entries(value)) { + if (/^(?:url|href|src|link|iframe|embed|embeddable|libraryItems)$/i.test(key) && item != null && item !== '' && !(Array.isArray(item) && !item.length)) fail('External resources and embeds are not supported in scenes'); + checkLinks(item); + } + } + checkLinks(scene.elements); checkLinks(scene.appState); + return scene; +} + +// XML is checked before the HTML namespace parser. This prevents error recovery +// from silently accepting malformed SVG, entities, duplicate attributes or DTDs. +function checkXml(svg) { + const stack = []; + let offset = 0, roots = 0, nodes = 0; + function badEntities(value) { + if (/&(?!(?:amp|lt|gt|quot|apos|#\d+|#x[\da-fA-F]+);)/.test(value)) return true; + return [...value.matchAll(/&#(x[\da-fA-F]+|\d+);/g)].some((match) => { + const n = match[1][0] === 'x' ? Number.parseInt(match[1].slice(1), 16) : Number(match[1]); + return !([9, 10, 13].includes(n) || n >= 0x20 && n <= 0xd7ff || n >= 0xe000 && n <= 0xfffd || n >= 0x10000 && n <= 0x10ffff); + }); + } + while (offset < svg.length) { + if (++nodes > 100000) fail('SVG exceeds the supported node limit'); + if (svg.startsWith('', offset + 4); + if (end < 0 || svg.slice(offset + 4, end).includes('--')) fail('Invalid SVG XML comment'); + offset = end + 3; continue; + } + if (svg[offset] !== '<') { + const end = svg.indexOf('<', offset), text = svg.slice(offset, end < 0 ? svg.length : end); + if (badEntities(text) || text.includes(']]>') || (!stack.length && text.trim())) fail('Invalid SVG XML text'); + offset = end < 0 ? svg.length : end; continue; + } + if (svg.startsWith('/i); + if (!declaration) fail('Invalid SVG XML declaration'); + offset += declaration[0].length; continue; + } + const close = svg.slice(offset).match(/^<\/([A-Za-z_][\w:.-]*)\s*>/); + if (close) { if (stack.pop() !== close[1]) fail('Unbalanced SVG XML'); offset += close[0].length; continue; } + const open = svg.slice(offset).match(/^<([A-Za-z_][\w:.-]*)/); + if (!open) fail('Invalid SVG XML markup'); + offset += open[0].length; + const attrs = new Set(); + while (!/^\s*\/?>/.test(svg.slice(offset))) { + const attr = svg.slice(offset).match(/^\s+([A-Za-z_][\w:.-]*)\s*=\s*(?:"([^"<]*)"|'([^'<]*)')/); + if (!attr || attrs.has(attr[1]) || badEntities(attr[2] ?? attr[3]) || (attr[1].includes(':') && !/^(?:xml|xmlns|xlink):/.test(attr[1]))) fail('Invalid or duplicate SVG XML attribute'); + attrs.add(attr[1]); offset += attr[0].length; + } + const tail = svg.slice(offset).match(/^\s*(\/?)>/); + offset += tail[0].length; + if (!stack.length && ++roots !== 1) fail('SVG must have exactly one root'); + if (!tail[1]) stack.push(open[1]); + if (stack.length > 100) fail('SVG nesting exceeds the supported limit'); + } + if (stack.length || roots !== 1) fail('Incomplete SVG XML'); +} + +const SVG_TAGS = new Set(['svg', 'metadata', 'g', 'defs', 'style', 'path', 'rect', 'circle', 'ellipse', 'line', 'polyline', 'polygon', 'text', 'tspan', 'textPath', 'image', 'use', 'symbol', 'title', 'desc', 'clipPath', 'mask', 'pattern', 'linearGradient', 'radialGradient', 'stop', 'filter', 'feGaussianBlur', 'feOffset', 'feBlend', 'feColorMatrix', 'feComponentTransfer', 'feFuncA', 'feFuncR', 'feFuncG', 'feFuncB', 'feMerge', 'feMergeNode', 'feFlood', 'feComposite', 'marker']); +function safeCss(value) { + // CSS escapes/comments can disguise URLs and at-rules. Excalidraw does not + // need either; embedded data fonts and same-SVG fragments are supported. + if (/[\\]|\/\*|@import|expression\s*\(|behavior\s*:|-moz-binding|javascript:/i.test(value)) fail('Unsafe SVG CSS'); + let rest = value.replace(/url\(\s*(["']?)([^)'"\s]+)\1\s*\)/gi, (_, quote, url) => { + if (!/^#[\w:.-]+$/.test(url) && !/^data:(?:font\/[\w.+-]+|application\/(?:font-woff|x-font-ttf|octet-stream));base64,[A-Za-z0-9+/=]+$/i.test(url)) fail('SVG CSS may only reference local fragments or embedded fonts'); + return ''; + }); + if (/url\s*\(|https?:|(?:^|[^\w])\/\//i.test(rest) || /@(?!font-face\b)/i.test(rest)) fail('Unsupported SVG CSS'); +} + +export function validateSvg(input) { + let svg = input; + if (typeof svg !== 'string' || !svg.trim()) fail('A fresh SVG preview is required; export it with Excalidraw before saving'); + if (svg.startsWith('data:image/svg+xml;base64,')) { + const encoded = svg.slice(26); + if (!/^[A-Za-z0-9+/]+={0,2}$/.test(encoded) || encoded.length % 4) fail('Invalid SVG data URL'); + svg = Buffer.from(encoded, 'base64').toString('utf8'); + } + if (byteLength(svg) > LIMITS.svg || /[\u0000-\u0008\u000B\u000C\u000E-\u001F\uFFFE\uFFFF]/.test(svg) || /[\uD800-\uDFFF]/u.test(svg)) fail('SVG exceeds limits or contains invalid characters'); + checkXml(svg); + const fragment = parseFragment(svg); + const roots = fragment.childNodes.filter((n) => n.nodeName !== '#comment' && !(n.nodeName === '#text' && !n.value.trim())); + if (roots.length !== 1 || roots[0].tagName !== 'svg') fail('Preview must be an SVG document'); + let count = 0; + function walk(node) { + if (++count > 100000) fail('SVG exceeds the supported node limit'); + if (node.nodeName === '#text' || node.nodeName === '#comment') return; + if (node.namespaceURI !== 'http://www.w3.org/2000/svg' || !SVG_TAGS.has(node.tagName)) fail('Unsupported or active SVG content'); + for (const attr of node.attrs || []) { + const name = attr.prefix ? `${attr.prefix}:${attr.name}` : attr.name; + if (/^on/i.test(name) || /^(?:src|srcset|base|xml:base)$/i.test(name)) fail('SVG event handlers and external resources are forbidden'); + if (/^(?:href|xlink:href)$/i.test(name)) { + if (/^#[\w:.-]+$/.test(attr.value)) continue; + if (node.tagName === 'image') { rasterData(attr.value); continue; } + fail('SVG links must reference a local fragment'); + } + if (name === 'xmlns' && attr.value !== 'http://www.w3.org/2000/svg') fail('Invalid SVG namespace'); + if (name === 'xmlns:xlink' && attr.value !== 'http://www.w3.org/1999/xlink') fail('Invalid SVG link namespace'); + if (!/^xmlns(?::|$)/.test(name)) safeCss(attr.value); + } + if (node.tagName === 'style') safeCss((node.childNodes || []).map((n) => n.value || '').join('')); + for (const child of node.childNodes || []) walk(child); + } + walk(roots[0]); + return svg; +} + +const attrs = (node) => Object.fromEntries((node.attrs || []).map((a) => [a.name, a.value])); +const significant = (nodes) => nodes.filter((node) => !(node.nodeName === '#text' && !node.value.trim())); +export function parseDocument(source) { + if (typeof source !== 'string') fail('HTML must be a string'); + if (byteLength(source) > LIMITS.html) fail('HTML exceeds the 2 MB publication limit; reduce embedded images or diagram size', 413); + const errors = [], tree = parse(source, { sourceCodeLocationInfo: true, onParseError: (e) => errors.push(e) }); + const all = [], parents = new Map(); + function visit(node, parent, inTemplate = false) { + all.push(node); parents.set(node, { parent, inTemplate }); + for (const child of node.childNodes || []) visit(child, node, inTemplate); + if (node.content) visit(node.content, node, true); + } + visit(tree, null); + const metadata = all.filter((node) => node.tagName === 'meta' && attrs(node).name?.toLowerCase() === 'htmldrop-document-id'); + if (metadata.length > 1) fail('Duplicate document identity metadata'); + let documentId = null; + if (metadata.length) { + const node = metadata[0]; documentId = attrs(node).content; + if (!isUUID(documentId) || parents.get(node).inTemplate) fail('Invalid document identity metadata'); + } + const markers = all.filter((node) => node.nodeName === '#comment' && /^\s*htmldrop:diagram:/.test(node.data)); + const figures = all.filter((node) => Object.hasOwn(attrs(node), 'data-htmldrop-diagram')); + const scenes = all.filter((node) => Object.hasOwn(attrs(node), 'data-htmldrop-scene')); + const used = new Set(), diagrams = [], ids = new Set(); + for (const start of markers) { + const match = start.data.trim().match(/^htmldrop:diagram:([^:]+):(start|end)$/); + if (!match || !isUUID(match[1])) fail('Malformed managed diagram marker'); + if (match[2] === 'end') continue; + const id = match[1]; + if (ids.has(id) || !documentId || parents.get(start).inTemplate) fail('Duplicate diagram identity or missing document identity'); + ids.add(id); + const parent = parents.get(start).parent, siblings = significant(parent.childNodes || []), index = siblings.indexOf(start); + const figure = siblings[index + 1], end = siblings[index + 2]; + if (figure?.tagName !== 'figure' || attrs(figure)['data-htmldrop-diagram'] !== id || end?.nodeName !== '#comment' || end.data.trim() !== `htmldrop:diagram:${id}:end`) fail('Managed diagram markers must surround exactly one matching figure'); + const children = significant(figure.childNodes || []), img = children[0], script = children[1]; + if (children.length !== 2 || img?.tagName !== 'img' || script?.tagName !== 'script' || attrs(script).type !== 'application/json' || attrs(script)['data-htmldrop-scene'] !== id) fail('Managed diagram requires one preview image and one JSON scene'); + if (!attrs(img).src?.startsWith('data:image/svg+xml;base64,')) fail('Managed preview requires an SVG data URL'); + let entry; + try { entry = JSON.parse((script.childNodes || []).map((node) => node.value || '').join('')); } catch { fail('Invalid embedded diagram JSON'); } + if (!object(entry) || entry.version !== 1 || entry.documentId !== documentId || entry.diagramId !== id || !isUUID(entry.revisionId) || (entry.parentRevisionId !== null && !isUUID(entry.parentRevisionId)) || typeof entry.title !== 'string' || entry.title.length > 500) fail('Invalid embedded diagram identity'); + const scene = validateScene(entry.scene), svg = validateSvg(attrs(img).src); + const location = { start: start.sourceCodeLocation.startOffset, end: end.sourceCodeLocation.endOffset }; + if (errors.some((error) => error.code === 'duplicate-attribute' && error.startOffset >= location.start && error.startOffset < location.end)) fail('Duplicate managed diagram attribute'); + diagrams.push({ ...entry, scene, svg, location }); used.add(start); used.add(end); used.add(figure); used.add(script); + } + if (markers.some((node) => !used.has(node)) || figures.some((node) => !used.has(node)) || scenes.some((node) => !used.has(node))) fail('Orphaned or ambiguous managed diagram block'); + if (metadata.some((node) => errors.some((error) => error.code === 'duplicate-attribute' && error.startOffset >= node.sourceCodeLocation.startOffset && error.startOffset < node.sourceCodeLocation.endOffset))) fail('Duplicate document metadata attribute'); + const head = all.find((node) => node.tagName === 'head'), body = all.find((node) => node.tagName === 'body'), doctype = all.find((node) => node.nodeName === '#documentType'); + return { source, sourceHash: sourceHash(source), documentId, diagrams, metaOffset: head?.sourceCodeLocation?.startTag?.endOffset ?? doctype?.sourceCodeLocation?.endOffset ?? 0, appendOffset: body?.sourceCodeLocation?.endTag?.startOffset ?? source.length }; +} + +const escapeAttr = (text) => text.replace(/&/g, '&').replace(/"/g, '"').replace(//g, '>'); +export function serializeDiagram(entry, svg) { + const { documentId, diagramId, revisionId, parentRevisionId, title, scene } = entry; + const json = JSON.stringify({ version: 1, documentId, diagramId, revisionId, parentRevisionId, title, scene }).replace(/\n
\n ${escapeAttr(title)}\n \n
\n`; +} + +export function replaceDiagram(document, entry, svg) { + const existing = document.diagrams.find((item) => item.diagramId === entry.diagramId); + const edits = existing ? [{ start: existing.location.start, end: existing.location.end, value: serializeDiagram(entry, svg) }] : [{ start: document.appendOffset, end: document.appendOffset, value: `\n${serializeDiagram(entry, svg)}\n` }]; + if (!document.documentId) edits.push({ start: document.metaOffset, end: document.metaOffset, value: `\n\n` }); + let source = document.source; + // Stable sorting preserves metadata placement when an empty file has both + // insertions at zero (apply the diagram first, then prepend the metadata). + for (const edit of edits.sort((a, b) => b.start - a.start)) source = source.slice(0, edit.start) + edit.value + source.slice(edit.end); + parseDocument(source); + return source; +} diff --git a/src/diagrams/service.js b/src/diagrams/service.js new file mode 100644 index 0000000..b1c34e0 --- /dev/null +++ b/src/diagrams/service.js @@ -0,0 +1,282 @@ +import { randomUUID, randomBytes, createCipheriv, createDecipheriv } from 'node:crypto'; +import { readFile, mkdir, stat, rename, unlink, realpath, lstat, open, link } from 'node:fs/promises'; +import { join, dirname, basename, resolve } from 'node:path'; +import { getConfigDir } from '../config.js'; +import { fail, isUUID, LIMITS, parseDocument, replaceDiagram, validateScene, validateSvg } from './document.js'; + +// Shared by service instances: multiple HTTP adapters cannot race the same file. +const locks = new Map(); +async function serialized(file, operation) { + const previous = locks.get(file) || Promise.resolve(); + const next = previous.catch(() => {}).then(operation); + locks.set(file, next); + try { return await next; } finally { if (locks.get(file) === next) locks.delete(file); } +} +function authenticate(actor, owner = false) { + if (!['owner', 'reviewer'].includes(actor)) fail('An authenticated diagram actor is required', 403); + if (owner && actor !== 'owner') fail('Only the local owner can change accepted diagrams', 403); +} +const permissions = (actor) => ({ create: actor === 'owner', save: actor === 'owner', accept: actor === 'owner', reject: actor === 'owner', propose: true }); +function title(value, fallback = 'Untitled diagram') { + if (value === undefined) return fallback; + if (typeof value !== 'string' || value.length > 500) fail('Diagram title must be at most 500 characters'); + return value; +} +function summary(value) { + if (value === undefined) return ''; + if (typeof value !== 'string' || value.length > 2000) fail('Revision summary must be at most 2000 characters'); + return value; +} +const snapshot = (entry) => { + const { location, ...value } = entry; void location; + return value; +}; + +export function createDiagramService({ getSession, configDir = getConfigDir() } = {}) { + if (typeof getSession !== 'function') throw new TypeError('getSession is required'); + const stateDir = join(configDir, 'edit', 'diagrams'); + let keyPromise; + async function encryptionKey() { + if (!keyPromise) keyPromise = (async () => { + await mkdir(stateDir, { recursive: true, mode: 0o700 }); + const path = join(stateDir, 'history.key'); + return serialized(path, async () => { + const temp = join(stateDir, `.history.key.${randomUUID()}.tmp`); + try { + const handle = await open(temp, 'wx', 0o600); + try { await handle.writeFile(randomBytes(32)); await handle.sync(); } finally { await handle.close(); } + // Publish a fully written key without replacing a key another process + // may already have initialized. A crash cannot leave an empty key. + await link(temp, path); + } catch (error) { if (error.code !== 'EEXIST') throw error; } + finally { await unlink(temp).catch((error) => { if (error.code !== 'ENOENT') throw error; }); } + const key = await readFile(path); + if (key.length !== 32) fail('Local diagram history key is invalid', 500); + return key; + }); + })(); + return keyPromise; + } + function statePath(documentId, diagramId) { + if (!isUUID(documentId) || !isUUID(diagramId)) fail('Invalid diagram identity'); + return join(stateDir, `${documentId}-${diagramId}.json.enc`); + } + async function loadState(documentId, diagramId, currentRevisionId) { + let encrypted; + try { encrypted = await readFile(statePath(documentId, diagramId), 'utf8'); } + catch (error) { if (error.code === 'ENOENT') return { revisions: [], proposals: [] }; throw error; } + try { + const envelope = JSON.parse(encrypted); + if (envelope.version !== 1) throw new Error('version'); + const decipher = createDecipheriv('aes-256-gcm', await encryptionKey(), Buffer.from(envelope.iv, 'base64')); + decipher.setAAD(Buffer.from(`${documentId}/${diagramId}`)); + decipher.setAuthTag(Buffer.from(envelope.tag, 'base64')); + const state = JSON.parse(Buffer.concat([decipher.update(Buffer.from(envelope.data, 'base64')), decipher.final()]).toString('utf8')); + if (!Array.isArray(state.revisions) || !Array.isArray(state.proposals)) throw new Error('shape'); + // The HTML is authoritative after a crash. A staged transaction only + // becomes visible when its exact revision reached the source file. + if (state.pending) return state.pending.revisionId === currentRevisionId ? state.pending.nextState : { revisions: state.revisions, proposals: state.proposals }; + return state; + } catch { fail('Cannot decrypt private diagram history in this profile', 500); } + } + async function writeAtomic(path, bytes, mode = 0o600, beforeRename) { + const temp = join(dirname(path), `.${basename(path)}.${randomUUID()}.tmp`); + let handle; + try { + handle = await open(temp, 'wx', mode); + await handle.writeFile(bytes); await handle.chmod(mode); await handle.sync(); await handle.close(); handle = null; + if (beforeRename) await beforeRename(); + await rename(temp, path); + } finally { + if (handle) await handle.close().catch(() => {}); + await unlink(temp).catch((error) => { if (error.code !== 'ENOENT') throw error; }); + } + } + async function persistState(documentId, diagramId, state) { + const plaintext = JSON.stringify(state); + if (Buffer.byteLength(plaintext) > 32 * 1024 * 1024) fail('Private diagram history reached the 32 MB profile limit. Start a new diagram or profile to continue.', 413); + const key = await encryptionKey(), iv = randomBytes(12), cipher = createCipheriv('aes-256-gcm', key, iv); + cipher.setAAD(Buffer.from(`${documentId}/${diagramId}`)); + const data = Buffer.concat([cipher.update(plaintext, 'utf8'), cipher.final()]); + await writeAtomic(statePath(documentId, diagramId), JSON.stringify({ version: 1, iv: iv.toString('base64'), tag: cipher.getAuthTag().toString('base64'), data: data.toString('base64') })); + } + async function sessionFile(key) { + const session = await getSession(key); + if (!session?.file) fail('Edit session not found', 404); + try { + const file = await realpath(session.file), info = await lstat(session.file); + if (!info.isFile() || file !== resolve(session.file)) fail('Registered artifact path changed or is a symbolic link', 400); + return file; + } catch (error) { if (error.statusCode) throw error; fail('Registered HTML artifact is unavailable', 404); } + } + async function readDocument(file) { + const info = await stat(file); + if (info.size > LIMITS.html) fail('HTML exceeds the 2 MB publication limit; reduce embedded images or diagram size', 413); + const bytes = await readFile(file); + const source = bytes.toString('utf8'); + if (!Buffer.from(source).equals(bytes)) fail('HTML must use valid UTF-8'); + return { ...parseDocument(source), bytes, mode: info.mode & 0o777 }; + } + function diagram(document, id) { + if (!isUUID(id)) fail('Diagram not found', 404); + const entry = document.diagrams.find((item) => item.diagramId === id); + if (!entry) fail('Diagram not found', 404); + return entry; + } + function conflict(document, entry, message = 'Diagram or source changed. Reload the current diagram and reapply your changes.') { + fail(message, 409, { current: { documentId: document.documentId, diagramId: entry?.diagramId, revisionId: entry?.revisionId, sourceHash: document.sourceHash } }); + } + function checkBase(document, entry, body) { + if (!body || typeof body.baseSourceHash !== 'string' || (entry && typeof body.baseRevisionId !== 'string')) fail('baseSourceHash and the current baseRevisionId are required'); + if (body.baseSourceHash !== document.sourceHash || (entry && body.baseRevisionId !== entry.revisionId)) conflict(document, entry); + } + async function recheck(file, document, entry) { + const info = await lstat(file); + if (!info.isFile() || await realpath(file) !== file || !(await readFile(file)).equals(document.bytes)) { + let latest; + try { latest = await readDocument(file); } catch { /* Preserve the original safe conflict if external source is malformed. */ } + conflict(latest || document, latest?.diagrams.find((item) => item.diagramId === entry?.diagramId) || entry, 'HTML changed outside the editor. Reload the file before retrying.'); + } + } + async function writeSource(file, document, entry, source = replaceDiagram(document, entry, entry.svg)) { + await writeAtomic(file, source, document.mode, () => recheck(file, document, entry)); + return { ...parseDocument(source), bytes: Buffer.from(source), mode: document.mode }; + } + async function commitRevision(file, doc, entry, previousState, nextState, source) { + await recheck(file, doc, entry); + await persistState(entry.documentId, entry.diagramId, { ...previousState, pending: { revisionId: entry.revisionId, nextState } }); + const updated = await writeSource(file, doc, entry, source); + // The durable journal already contains all history. If finalization fails, + // read/load resolves it from the authoritative source revision on restart. + await persistState(entry.documentId, entry.diagramId, nextState).catch(() => {}); + return updated; + } + function includeCurrent(state, entry) { + if (!state.revisions.some((item) => item.revisionId === entry.revisionId)) state.revisions.push({ ...snapshot(entry), summary: 'Imported accepted revision', createdAt: null }); + return state; + } + async function payload(document, entry, actor, suppliedState) { + const state = includeCurrent(suppliedState || await loadState(document.documentId, entry.diagramId, entry.revisionId), entry); + const proposals = actor === 'owner' ? state.proposals : state.proposals.filter((item) => item.baseRevisionId === entry.revisionId && item.baseSourceHash === document.sourceHash); + const revisions = actor === 'owner' ? state.revisions.slice().reverse() : [snapshot(entry)]; + return { ...snapshot(entry), sourceHash: document.sourceHash, proposals: proposals.slice().reverse(), revisions, permissions: permissions(actor) }; + } + async function mutate(key, actor, owner, operation) { + authenticate(actor, owner); + const file = await sessionFile(key); + return serialized(file, async () => operation(file, await readDocument(file))); + } + function candidate(body, fallbackTitle) { + if (!body || typeof body !== 'object') fail('A diagram request body is required'); + return { title: title(body.title, fallbackTitle), scene: validateScene(body.scene), summary: summary(body.summary) }; + } + function newRevision(documentId, diagramId, parentRevisionId, value, svg) { + return { version: 1, documentId, diagramId, revisionId: randomUUID(), parentRevisionId, ...value, svg, createdAt: new Date().toISOString() }; + } + const service = { + async list(key, actor) { + authenticate(actor); + const doc = await readDocument(await sessionFile(key)); + return { documentId: doc.documentId, sourceHash: doc.sourceHash, diagrams: doc.diagrams.map(({ diagramId, revisionId, title }) => ({ diagramId, revisionId, title })), permissions: permissions(actor) }; + }, + async read(key, id, actor) { + authenticate(actor); + const file = await sessionFile(key); + return serialized(file, async () => { + const doc = await readDocument(file); + return payload(doc, diagram(doc, id), actor); + }); + }, + async create(key, body, actor) { + return mutate(key, actor, true, async (file, doc) => { + checkBase(doc, null, body); + const value = candidate(body), svg = validateSvg(body.svg), documentId = doc.documentId || randomUUID(), id = randomUUID(); + const entry = newRevision(documentId, id, null, value, svg), state = { revisions: [entry], proposals: [] }; + const source = replaceDiagram(doc, entry, svg); + const updated = await commitRevision(file, doc, entry, { revisions: [], proposals: [] }, state, source); + return payload(updated, diagram(updated, id), actor, state); + }); + }, + async save(key, id, body, actor) { + return mutate(key, actor, true, async (file, doc) => { + const current = diagram(doc, id); checkBase(doc, current, body); + const value = candidate(body, current.title), svg = validateSvg(body.svg); + const state = includeCurrent(await loadState(doc.documentId, id, current.revisionId), current); + if (value.title === current.title && JSON.stringify(value.scene) === JSON.stringify(current.scene) && svg === current.svg) return payload(doc, current, actor, state); + const entry = newRevision(doc.documentId, id, current.revisionId, value, svg); + const source = replaceDiagram(doc, entry, svg), previousState = structuredClone(state); + state.revisions.push(entry); + const updated = await commitRevision(file, doc, entry, previousState, state, source); + return payload(updated, diagram(updated, id), actor, state); + }); + }, + async propose(key, id, body, actor) { + return mutate(key, actor, false, async (file, doc) => { + const current = diagram(doc, id); checkBase(doc, current, body); + const value = candidate(body, current.title), svg = body.svg == null ? undefined : validateSvg(body.svg); + const state = includeCurrent(await loadState(doc.documentId, id, current.revisionId), current); + if (state.proposals.length >= 1000) fail('Private proposal limit reached'); + state.proposals.push({ proposalId: randomUUID(), documentId: doc.documentId, diagramId: id, baseRevisionId: current.revisionId, baseSourceHash: doc.sourceHash, ...value, ...(svg ? { svg } : {}), status: 'pending', actor, createdAt: new Date().toISOString() }); + await recheck(file, doc, current); await persistState(doc.documentId, id, state); + return payload(doc, current, actor, state); + }); + }, + async accept(key, id, proposalId, body, actor) { + return mutate(key, actor, true, async (file, doc) => { + const current = diagram(doc, id); checkBase(doc, current, body); + const state = includeCurrent(await loadState(doc.documentId, id, current.revisionId), current), proposal = state.proposals.find((item) => item.proposalId === proposalId); + if (!proposal) fail('Proposal not found', 404); + if (proposal.status !== 'pending') fail('This proposal has already been decided', 409); + if (proposal.baseRevisionId !== current.revisionId || proposal.baseSourceHash !== doc.sourceHash) conflict(doc, current, 'Proposal was based on an older revision or source. Create a new proposal against the current diagram.'); + const entry = newRevision(doc.documentId, id, current.revisionId, { title: proposal.title, scene: validateScene(proposal.scene), summary: summary(body.summary ?? proposal.summary) }, validateSvg(body.svg)); + const source = replaceDiagram(doc, entry, entry.svg), previousState = structuredClone(state); + state.revisions.push(entry); + proposal.status = 'accepted'; proposal.acceptedRevisionId = entry.revisionId; proposal.decidedAt = new Date().toISOString(); + const updated = await commitRevision(file, doc, entry, previousState, state, source); + return payload(updated, diagram(updated, id), actor, state); + }); + }, + async reject(key, id, proposalId, body, actor) { + return mutate(key, actor, true, async (file, doc) => { + const current = diagram(doc, id); checkBase(doc, current, body); + const state = includeCurrent(await loadState(doc.documentId, id, current.revisionId), current), proposal = state.proposals.find((item) => item.proposalId === proposalId); + if (!proposal) fail('Proposal not found', 404); + if (proposal.status !== 'pending') fail('This proposal has already been decided', 409); + proposal.status = 'rejected'; proposal.decidedAt = new Date().toISOString(); + await recheck(file, doc, current); await persistState(doc.documentId, id, state); + return payload(doc, current, actor, state); + }); + }, + async exportDiagram(key, id, { format = 'excalidraw', revisionId } = {}, actor) { + authenticate(actor); + const file = await sessionFile(key); + return serialized(file, async () => { + const doc = await readDocument(file), current = diagram(doc, id); + if (actor === 'reviewer' && revisionId && revisionId !== current.revisionId) fail('Reviewers may only export the current accepted revision', 403); + let entry = current; + if (revisionId && revisionId !== current.revisionId) { + const state = await loadState(doc.documentId, id, current.revisionId); + entry = state.revisions.find((item) => item.revisionId === revisionId); + if (!entry) fail('Accepted revision not found in this local profile', 404); + } + if (format === 'excalidraw') return { format, content: JSON.stringify(validateScene(entry.scene), null, 2), mimeType: 'application/json', revisionId: entry.revisionId }; + if (format === 'svg') { + if (!entry.svg) fail('This accepted revision has no matching SVG preview', 409); + return { format, content: validateSvg(entry.svg), mimeType: 'image/svg+xml', revisionId: entry.revisionId }; + } + fail('Export format must be excalidraw or svg'); + }); + }, + async checkAnchor(key, id, body, actor, operation) { + return mutate(key, actor, false, async (_file, doc) => { + const current = diagram(doc, id); checkBase(doc, current, body); + if (typeof body.shapeId !== 'string' || !current.scene.elements.some((item) => item.id === body.shapeId && !item.isDeleted)) fail('Shape is absent or removed from the current diagram', 400); + const anchor = { type: 'diagram_shape', documentId: doc.documentId, diagramId: id, shapeId: body.shapeId, revisionId: current.revisionId }; + // The optional local callback lets the HTTP adapter persist a comment + // before a queued diagram save can invalidate its checked revision. + return operation ? operation(anchor) : anchor; + }); + }, + }; + return service; +} diff --git a/src/edit/browser/editor.css b/src/edit/browser/editor.css new file mode 100644 index 0000000..0e6e64a --- /dev/null +++ b/src/edit/browser/editor.css @@ -0,0 +1,220 @@ +@import "@excalidraw/excalidraw/index.css"; +:root { + font-family: Arial, Helvetica, sans-serif; + color: #202124; + background: #f5f6f8; + font-size: 14px; +} +* { + box-sizing: border-box; +} +body { + margin: 0; +} +button, +input, +textarea, +select { + font: inherit; +} +button { + cursor: pointer; + border: 1px solid #c8cbd2; + border-radius: 5px; + padding: 7px 10px; + background: #fff; + color: #25262b; +} +button:hover { + background: #eef0f5; +} +button:disabled { + opacity: 0.45; + cursor: default; +} +button:focus-visible, +input:focus-visible, +textarea:focus-visible, +select:focus-visible { + outline: 3px solid #6366f1; + outline-offset: 2px; +} +.shell { + height: 100vh; + height: 100dvh; + display: flex; + flex-direction: column; +} +.toolbar { + display: flex; + align-items: center; + gap: 8px; + flex-wrap: wrap; + padding: 10px 14px; + background: #fff; + border-bottom: 1px solid #dfe1e6; +} +.toolbar h1 { + font-size: 15px; + margin: 0 7px 0 0; +} +.toolbar input { + min-width: 100px; + flex: 1; + max-width: 280px; + border: 1px solid #c8cbd2; + border-radius: 4px; + padding: 7px; +} +.primary { + background: #5054c7; + color: #fff; + border-color: #5054c7; +} +.primary:hover { + background: #4145b2; +} +.workspace { + display: flex; + flex: 1; + min-height: 0; +} +.canvas { + flex: 1; + min-width: 0; + position: relative; +} +.review { + width: 310px; + max-width: 40vw; + overflow: auto; + border-left: 1px solid #dfe1e6; + padding: 14px; + background: #fff; +} +.review h2 { + font-size: 15px; + margin: 18px 0 8px; +} +.review h2:first-child { + margin-top: 0; +} +.review p { + line-height: 1.45; + overflow-wrap: anywhere; +} +.review textarea { + width: 100%; + min-height: 65px; + border: 1px solid #c8cbd2; + border-radius: 4px; + padding: 8px; +} +.review input { + width: 100%; + margin: 5px 0; + padding: 6px; +} +.status { + padding: 8px 14px; + background: #edf0ff; + color: #35377c; + min-height: 32px; +} +.status.error { + background: #fff0ed; + color: #9c311d; +} +.record { + border-top: 1px solid #e2e3e9; + padding: 10px 0; +} +.record button { + margin: 3px 4px 0 0; +} +.preview { + max-height: 180px; + width: 100%; + object-fit: contain; + background: #f8f9fc; +} +.muted { + color: #686d79; + font-size: 12px; +} +.empty { + display: grid; + place-content: center; + height: 100%; + text-align: center; + padding: 28px; +} +.empty p { + max-width: 460px; + line-height: 1.5; +} +.file { + display: none; +} +.changed { + color: #9c311d; + font-weight: 600; +} +.review label { + display: block; + font-size: 12px; + margin-top: 7px; +} +dialog { + border: 1px solid #c8cbd2; + border-radius: 8px; + max-width: 650px; + width: 90vw; +} +dialog textarea { + width: 100%; + height: 240px; +} +dialog::backdrop { + background: #20212466; +} +@media (max-width: 780px) { + .review { + width: 250px; + max-width: 45vw; + padding: 10px; + } + .toolbar { + gap: 5px; + padding: 8px; + } + .toolbar h1 { + display: none; + } + .toolbar button { + font-size: 12px; + padding: 6px; + } + .toolbar input { + max-width: 160px; + } + .workspace { + flex-direction: column; + } + .review { + width: 100%; + max-width: none; + max-height: 35vh; + border-left: 0; + border-top: 1px solid #dfe1e6; + } + .canvas { + min-height: 300px; + } +} + +/* The embedded document editor has no remote library catalog. Keyboard access + opens the inert DefaultSidebar override rather than the upstream catalog. */ +.excalidraw .default-sidebar-trigger { + display: none !important; +} diff --git a/src/edit/browser/editor.jsx b/src/edit/browser/editor.jsx new file mode 100644 index 0000000..961aeef --- /dev/null +++ b/src/edit/browser/editor.jsx @@ -0,0 +1,897 @@ +import React, { useEffect, useRef, useState } from "react"; +import { createRoot } from "react-dom/client"; +import { + Excalidraw, + DefaultSidebar, + MainMenu, + exportToSvg, + serializeAsJSON, + convertToExcalidrawElements, +} from "@excalidraw/excalidraw"; +import "./editor.css"; +const key = location.pathname.match(/^\/s\/([a-f0-9]{16})\/__editor\//)?.[1]; +const base = `/api/edit/${key}/diagrams`; +window.EXCALIDRAW_ASSET_PATH = `${location.origin}/s/${key}/__editor/`; +const blank = () => ({ + type: "excalidraw", + version: 2, + elements: [], + appState: { viewBackgroundColor: "#ffffff" }, + files: {}, +}); +function notify(type, detail = {}) { + parent.postMessage( + { type: `htmldrop:diagram-${type}`, ...detail }, + location.origin, + ); +} +async function request(path = "", body) { + const res = await fetch( + base + path, + body === undefined + ? {} + : { + method: "POST", + headers: { "content-type": "application/json" }, + body: JSON.stringify(body), + }, + ); + const value = await res.json(); + if (!res.ok) + throw new Error( + res.status === 409 + ? "The document changed. Your draft is preserved. Export your draft, then reopen the current diagram before saving." + : value.error || value.message || "The request failed. Try again.", + ); + return value; +} +function signature(scene) { + return JSON.stringify({ + elements: scene.elements, + files: scene.files, + background: scene.appState?.viewBackgroundColor, + }); +} +function validateImport(scene) { + const object = (value) => + value && typeof value === "object" && !Array.isArray(value); + const types = new Set([ + "rectangle", + "diamond", + "ellipse", + "text", + "line", + "arrow", + "freedraw", + "image", + "frame", + "magicframe", + ]); + if ( + !object(scene) || + scene.type !== "excalidraw" || + scene.version !== 2 || + !Array.isArray(scene.elements) || + scene.elements.length > 5000 || + !object(scene.appState || {}) || + !object(scene.files || {}) || + Object.keys(scene.files || {}).length > 100 + ) + throw Error( + "Import a native version 2 diagram with at most 5,000 shapes and 100 images.", + ); + const ids = new Set(); + for (const element of scene.elements) { + if ( + !object(element) || + !types.has(element.type) || + typeof element.id !== "string" || + !/^[\w-]{1,128}$/.test(element.id) || + ids.has(element.id) || + ["x", "y", "width", "height"].some( + (key) => !Number.isFinite(element[key]) || Math.abs(element[key]) > 1e9, + ) + ) + throw Error( + "The diagram contains an unsupported shape, duplicate ID or invalid geometry.", + ); + ids.add(element.id); + } + let budget = 200000; + function visit(value, depth = 0) { + if (--budget < 0 || depth > 32) + throw Error( + "The imported diagram is too complex. Split it into smaller diagrams.", + ); + if ( + typeof value === "number" && + (!Number.isFinite(value) || Math.abs(value) > Number.MAX_SAFE_INTEGER) + ) + throw Error("The diagram contains an invalid number."); + if (value && typeof value === "object") + for (const [key, child] of Object.entries(value)) { + if (["__proto__", "constructor", "prototype"].includes(key)) + throw Error("The diagram contains an unsupported property."); + visit(child, depth + 1); + } + } + visit(scene); + if (new TextEncoder().encode(JSON.stringify(scene)).length > 6 * 1024 * 1024) + throw Error("Import a diagram smaller than 6 MB."); + safeFiles(scene); +} +function safeFiles(scene) { + for (const file of Object.values(scene.files || {})) + if ( + !/^data:image\/(png|jpeg|webp|gif);base64,[a-z0-9+/=\r\n]+$/i.test( + file.dataURL || "", + ) + ) + throw Error( + "Use PNG, JPEG, WebP or GIF images stored inside the diagram.", + ); + if ( + scene.elements?.some( + (el) => el.type === "embeddable" || el.type === "iframe" || el.link, + ) + ) + throw Error("Remove web embeds and shape links before saving."); +} +function reviewerToken() { + try { + let token = localStorage.getItem("htmldrop_reviewer_token"); + if (!token) { + token = crypto.randomUUID(); + localStorage.setItem("htmldrop_reviewer_token", token); + } + return token; + } catch { + return crypto.randomUUID(); + } +} +function download(content, type, name) { + const url = URL.createObjectURL(new Blob([content], { type })); + const a = document.createElement("a"); + a.href = url; + a.download = name; + a.click(); + setTimeout(() => URL.revokeObjectURL(url), 1000); +} +async function svgFor(scene) { + safeFiles(scene); + const svg = await exportToSvg({ + elements: scene.elements.filter((el) => !el.isDeleted), + appState: { + ...scene.appState, + exportBackground: true, + exportEmbedScene: false, + }, + files: scene.files, + renderEmbeddables: false, + exportPadding: 10, + }); + return svg.outerHTML; +} +function threads(items) { + return items + .filter((c) => !c.parentId) + .map((c) => ({ + ...c, + text: c.content?.text || c.text, + authorName: c.author?.displayName || c.authorName, + replies: items + .filter((r) => r.parentId === c.id) + .map((r) => ({ + ...r, + text: r.content?.text || r.text, + authorName: r.author?.displayName || r.authorName, + })), + })); +} +function App() { + const [listing, setListing] = useState(null), + [doc, setDoc] = useState(null), + [title, setTitle] = useState("Untitled diagram"), + [status, setStatus] = useState("Opening diagrams…"), + [error, setError] = useState(false), + [busy, setBusy] = useState(false), + [dirty, setDirty] = useState(false), + [selected, setSelected] = useState([]), + [comments, setComments] = useState([]), + [text, setText] = useState(""), + [author, setAuthor] = useState(""), + [mount, setMount] = useState(0), + [initialScene, setInitialScene] = useState(blank), + [proposalPreviews, setProposalPreviews] = useState({}), + [review, setReview] = useState(true), + [mermaid, setMermaid] = useState(false), + [syntax, setSyntax] = useState("flowchart LR\n A[Start] --> B[Finish]"); + const scene = useRef(blank()), + baseline = useRef(""), + api = useRef(null), + file = useRef(null), + mermaidDialog = useRef(null), + titleBase = useRef("Untitled diagram"), + dirtyRef = useRef(false), + pendingBaseline = useRef(false), + operations = useRef(0), + generation = useRef(0); + useEffect(() => { + if (mermaid && mermaidDialog.current && !mermaidDialog.current.open) + mermaidDialog.current.showModal(); + }, [mermaid]); + const permissions = doc?.permissions || listing?.permissions || {}; + function message(value, bad = false) { + setStatus(value); + setError(bad); + } + function mark(value) { + dirtyRef.current = value; + setDirty(value); + notify("dirty", { dirty: value }); + } + async function run(fn) { + operations.current += 1; + setBusy(true); + try { + await fn(); + } catch (e) { + message(e.message, true); + } finally { + operations.current -= 1; + setBusy(operations.current > 0); + } + } + async function refreshList() { + const value = await request(); + setListing(value); + return value; + } + async function load(id, force = false) { + if ( + !force && + dirtyRef.current && + !confirm("Discard your unsaved diagram changes?") + ) + return; + await run(async () => { + const value = await request(`/${id}`); + setDoc(value); + scene.current = value.scene; + api.current = null; + setInitialScene(value.scene); + baseline.current = signature(value.scene); + pendingBaseline.current = true; + titleBase.current = value.title; + setTitle(value.title); + mark(false); + setSelected([]); + generation.current += 1; + setMount(generation.current); + const c = await request(`/${id}/comments`); + setComments(threads(c.items || [])); + message("Diagram ready. Select a shape to comment."); + }); + } + useEffect(() => { + if (!doc?.diagramId) return; + const id = doc.diagramId; + const events = new EventSource(`/__edit/events/${key}`); + const refresh = () => + request(`/${id}/comments`) + .then((value) => setComments(threads(value.items || []))) + .catch(() => {}); + events.addEventListener("feedback", refresh); + return () => events.close(); + }, [doc?.diagramId]); + useEffect(() => { + run(async () => { + const value = await refreshList(); + const wanted = new URLSearchParams(location.search).get("diagram"); + if (wanted && value.diagrams.some((d) => d.diagramId === wanted)) + await load(wanted, true); + else + message( + value.diagrams.length + ? "Choose a diagram or create a canvas." + : "Create a canvas or import a diagram to begin.", + ); + }); + const before = (e) => { + if (dirtyRef.current) { + e.preventDefault(); + e.returnValue = ""; + } + }; + const changed = (e) => { + if ( + e.origin === location.origin && + e.source === parent && + e.data?.type === "htmldrop:diagram-source-changed" + ) + message( + "The source changed. Your draft is preserved; export it before reopening to avoid losing changes.", + true, + ); + }; + addEventListener("beforeunload", before); + addEventListener("message", changed); + return () => { + removeEventListener("beforeunload", before); + removeEventListener("message", changed); + }; + }, []); + function newCanvas(candidate = blank()) { + if (dirtyRef.current && !confirm("Discard your unsaved diagram changes?")) + return false; + scene.current = candidate; + api.current = null; + setInitialScene(candidate); + baseline.current = signature(blank()); + pendingBaseline.current = false; + setDoc({ scene: candidate, permissions: listing?.permissions || {} }); + titleBase.current = "Untitled diagram"; + setTitle("Untitled diagram"); + setComments([]); + setSelected([]); + mark(true); + generation.current += 1; + setMount(generation.current); + message("New canvas. Save it to add it to this document."); + return true; + } + function currentScene() { + return { + type: "excalidraw", + version: 2, + source: "htmldrop", + elements: + api.current?.getSceneElementsIncludingDeleted() || + scene.current.elements, + appState: api.current?.getAppState() || scene.current.appState, + files: api.current?.getFiles() || scene.current.files, + }; + } + async function save(propose = false) { + await run(async () => { + const candidate = currentScene(); + const svg = await svgFor(candidate); + const snapshot = doc?.diagramId ? doc : listing; + const payload = { + baseRevisionId: doc?.revisionId, + baseSourceHash: snapshot.sourceHash, + title, + scene: JSON.parse( + serializeAsJSON( + candidate.elements, + candidate.appState, + candidate.files, + "local", + ), + ), + svg, + summary: propose + ? "Proposed from diagram editor" + : "Edited in diagram editor", + }; + const value = await request( + doc?.diagramId + ? `/${doc.diagramId}/${propose ? "proposals" : "save"}` + : "", + payload, + ); + if (propose) { + message("Proposal saved for owner review. Your draft remains open."); + const next = await request(`/${doc.diagramId}`); + setDoc(next); + } else { + mark(false); + setDoc(value); + scene.current = value.scene; + baseline.current = signature(candidate); + titleBase.current = title; + message("Diagram saved."); + notify("saved"); + } + await refreshList(); + }); + } + async function comment(e) { + e.preventDefault(); + if (dirty) { + message( + "Save or propose your changes, then reopen the accepted diagram before commenting.", + true, + ); + return; + } + await run(async () => { + const created = await request(`/${doc.diagramId}/comments`, { + editToken: reviewerToken(), + baseRevisionId: doc.revisionId, + baseSourceHash: doc.sourceHash, + shapeId: selected[0], + text, + authorName: author, + }); + setText(""); + const c = await request(`/${doc.diagramId}/comments`); + setComments(threads(c.items || [])); + message("Shape comment saved."); + notify("comment", { id: created.id }); + }); + } + async function accept(p) { + if (dirtyRef.current) { + message( + "Export or discard your draft before accepting a proposal.", + true, + ); + return; + } + await run(async () => { + const svg = await svgFor(p.scene); + const value = await request( + `/${doc.diagramId}/proposals/${p.proposalId}/accept`, + { + baseRevisionId: doc.revisionId, + baseSourceHash: doc.sourceHash, + svg, + summary: p.summary || "Accepted proposal", + }, + ); + mark(false); + await load(value.diagramId, true); + await refreshList(); + notify("saved"); + }); + } + async function reject(p) { + await run(async () => { + await request(`/${doc.diagramId}/proposals/${p.proposalId}/reject`, { + baseRevisionId: doc.revisionId, + baseSourceHash: doc.sourceHash, + }); + setDoc(await request(`/${doc.diagramId}`)); + message("Proposal rejected."); + }); + } + async function importJson(e) { + const selectedFile = e.target.files?.[0]; + e.target.value = ""; + if (!selectedFile) return; + await run(async () => { + if (selectedFile.size > 12 * 1024 * 1024) + throw Error("Choose a diagram file smaller than 12 MB."); + const parsed = JSON.parse(await selectedFile.text()); + const candidate = parsed.scene || parsed; + if (candidate.type !== "excalidraw" || !Array.isArray(candidate.elements)) + throw Error("Choose a native .excalidraw JSON file."); + validateImport(candidate); + newCanvas({ + ...candidate, + files: candidate.files || {}, + appState: candidate.appState || {}, + }); + }); + } + async function importMermaid() { + await run(async () => { + if (syntax.length > 50000 || syntax.split("\n").length > 1000) + throw Error( + "Keep Mermaid imports under 50,000 characters and 1,000 lines. Split a larger diagram into smaller diagrams.", + ); + const { parseMermaidToExcalidraw } = await import( + "@excalidraw/mermaid-to-excalidraw" + ); + const result = await parseMermaidToExcalidraw(syntax); + if (result.error) + throw Error(result.error.message || "Check the Mermaid syntax."); + const candidate = { + ...blank(), + elements: convertToExcalidrawElements(result.elements), + files: result.files || {}, + }; + validateImport(candidate); + const created = newCanvas(candidate); + if (!created) { + message("Import cancelled. Your current diagram is unchanged."); + return; + } + setMermaid(false); + message("Mermaid imported as a new editable diagram."); + }); + } + async function exportRevision(revisionId, format) { + await run(async () => { + const result = await request( + `/${doc.diagramId}/export?format=${format}&revisionId=${encodeURIComponent(revisionId)}`, + ); + download( + typeof result.content === "string" + ? result.content + : JSON.stringify(result.content, null, 2), + result.mimeType, + `diagram-${revisionId}.${format === "svg" ? "svg" : "excalidraw"}`, + ); + }); + } + return ( +
+
+

Diagrams

+ + {listing?.permissions?.create && ( + <> + + + + + )} + + {doc && ( + <> + { + setTitle(e.target.value); + mark(true); + }} + /> + {(doc.diagramId ? permissions.save : permissions.create) && ( + + )} + {doc.diagramId && permissions.propose && ( + + )} + {doc.diagramId && ( + + )} + + + + )} + + +
+
+ {busy ? "Working… " : ""} + {status} + {dirty && · Unsaved changes} +
+
+
+ {doc ? ( + { + if (mount === generation.current) api.current = value; + }} + initialData={initialScene} + onChange={(elements, appState, files) => { + if (mount !== generation.current) return; + scene.current = { + type: "excalidraw", + version: 2, + elements, + appState, + files, + }; + const ids = Object.keys(appState.selectedElementIds).filter( + (id) => appState.selectedElementIds[id], + ); + setSelected((previous) => + previous.length === ids.length && + previous.every((id, index) => id === ids[index]) + ? previous + : ids, + ); + if (pendingBaseline.current) { + baseline.current = signature(scene.current); + pendingBaseline.current = false; + } + const changed = + signature(scene.current) !== baseline.current || + title !== titleBase.current; + if (changed !== dirtyRef.current) mark(changed); + }} + aiEnabled={false} + validateEmbeddable={false} + onLinkOpen={(_, e) => e.preventDefault()} + UIOptions={{ + canvasActions: { + loadScene: false, + export: false, + saveAsImage: false, + }, + }} + > + +

+ Libraries are unavailable in this document editor. Import a + native diagram instead. +

+
+ + + + +
+ ) : ( +
+
+

Draw together with your agent

+

+ Open a diagram to edit its shapes, attach comments and review + proposed changes. +

+
+
+ )} +
+ {review && ( +