From ac8279f44c044c8406d03a1b2b8e8ab18ed33293 Mon Sep 17 00:00:00 2001 From: Dmitrii Kharitonov Date: Fri, 19 Jun 2026 19:06:59 +0200 Subject: [PATCH 1/3] feat: add c4-diagram skill Agent skill for producing C4 architecture diagrams in Mermaid notation. Uses flowchart + subgraphs (not experimental C4* syntax per bproxy lessons). Includes: - SKILL.md with rendering primitives, workflow, and hard rules - validate_mermaid.mjs script (mermaid.parse with loose security) - 6 validated diagram templates (context, container, component, deployment, dynamic, landscape) - Reference docs: abstractions, diagram selection, anti-patterns, mermaid gotchas --- .gitignore | 1 + skills/c4-diagram/SKILL.md | 231 +++ .../c4-diagram/assets/templates/component.mmd | 52 + .../c4-diagram/assets/templates/container.mmd | 46 + .../c4-diagram/assets/templates/context.mmd | 34 + .../assets/templates/deployment.mmd | 52 + .../c4-diagram/assets/templates/dynamic.mmd | 26 + .../c4-diagram/assets/templates/landscape.mmd | 47 + skills/c4-diagram/references/abstractions.md | 112 ++ skills/c4-diagram/references/anti-patterns.md | 161 ++ .../references/diagram-selection.md | 138 ++ .../c4-diagram/references/mermaid-gotchas.md | 198 ++ skills/c4-diagram/scripts/package-lock.json | 1612 +++++++++++++++++ skills/c4-diagram/scripts/package.json | 10 + .../c4-diagram/scripts/validate_mermaid.mjs | 146 ++ 15 files changed, 2866 insertions(+) create mode 100644 skills/c4-diagram/SKILL.md create mode 100644 skills/c4-diagram/assets/templates/component.mmd create mode 100644 skills/c4-diagram/assets/templates/container.mmd create mode 100644 skills/c4-diagram/assets/templates/context.mmd create mode 100644 skills/c4-diagram/assets/templates/deployment.mmd create mode 100644 skills/c4-diagram/assets/templates/dynamic.mmd create mode 100644 skills/c4-diagram/assets/templates/landscape.mmd create mode 100644 skills/c4-diagram/references/abstractions.md create mode 100644 skills/c4-diagram/references/anti-patterns.md create mode 100644 skills/c4-diagram/references/diagram-selection.md create mode 100644 skills/c4-diagram/references/mermaid-gotchas.md create mode 100644 skills/c4-diagram/scripts/package-lock.json create mode 100644 skills/c4-diagram/scripts/package.json create mode 100644 skills/c4-diagram/scripts/validate_mermaid.mjs diff --git a/.gitignore b/.gitignore index 3c93430..6bfb921 100644 --- a/.gitignore +++ b/.gitignore @@ -12,3 +12,4 @@ skills/convert/scripts/tests/explore_*.py tmp/ skills/convert/scripts/__pycache__/ __pycache__/ +skills/c4-diagram/scripts/node_modules/ diff --git a/skills/c4-diagram/SKILL.md b/skills/c4-diagram/SKILL.md new file mode 100644 index 0000000..f1fea80 --- /dev/null +++ b/skills/c4-diagram/SKILL.md @@ -0,0 +1,231 @@ +--- +name: c4-diagram +description: >- + Produce C4 software architecture diagrams in Mermaid notation (flowchart + + subgraphs, not experimental C4* syntax). Handles system context, container, + component, deployment, dynamic, and landscape views. Use when the user needs + architecture diagrams, wants to visualize system structure, or mentions C4, + Mermaid, or software architecture communication. Validates output syntax. +compatibility: Requires Node.js ≥18 (for mermaid.parse validation via scripts/) +license: MIT +metadata: + author: Dim Kharitonov + version: "0.1.0" +--- + +# C4 Architecture Diagrams → Mermaid + +Emit valid Mermaid `flowchart` code blocks that follow C4 methodology. Never use experimental `C4Context`/`C4Container`/`C4Component`/`C4Deployment` syntax — it breaks across Mermaid versions. + +References (load on demand): [abstractions](references/abstractions.md) · [diagram selection](references/diagram-selection.md) · [anti-patterns](references/anti-patterns.md) · [mermaid gotchas](references/mermaid-gotchas.md) · templates: [context](assets/templates/context.mmd) · [container](assets/templates/container.mmd) · [component](assets/templates/component.mmd) · [deployment](assets/templates/deployment.mmd) · [dynamic](assets/templates/dynamic.mmd) · [landscape](assets/templates/landscape.mmd) + +Validation: `node scripts/validate_mermaid.mjs `. On first use run `npm install` in `scripts/`. + +--- + +## Rendering Primitives + +### Node label formula + +``` +["Name
[Stereotype: Technology]
One-line responsibility"] +``` + +### Shapes → C4 element types + +| C4 Element | Shape | Example | +|---|---|---| +| Person | `(["..."])` | `User(["User
[Person]
Uses the system"])` | +| Software System | `["..."]` | `IBS["IBS
[Software System]
Online banking"]` | +| Container | `["..."]` | `API["API
[Container: Go]
Serves REST endpoints"]` | +| Component | `["..."]` | `Auth["Auth
[Component: Spring Bean]
Handles login"]` | +| Database | `[("...")]` | `DB[("DB
[Container: PostgreSQL]
Stores accounts")]` | +| External system | `["..."]:::external` | `Mail["Mailgun
[External System]
Sends emails"]:::external` | + +### Boundaries = subgraphs + +```mermaid +subgraph systemName ["System Name — [Software System]"] + direction TB + Container1["..."] + Container2["..."] +end +``` + +Nest subgraphs for deployment nodes, trust boundaries, or container internals. + +### Edges + +| Style | Syntax | Use for | +|---|---|---| +| Solid | `-->` | Runtime interaction | +| Dashed | `.->` | Static dependency / ownership / non-runtime | +| Bidirectional | `<-->` | Only when both directions are semantically meaningful | + +Label every edge with **purpose**, not protocol: +``` +A -- "sends commands to" --> B +A -. "reads config from" .-> C +``` + +Protocol belongs one level down (container edges show tech on container diagrams, not on context diagrams). + +### Title (mandatory) + +```mermaid +--- +title: System Name — View Type +--- +flowchart TB + ... +``` + +### Legend (mandatory on structural diagrams) + +```mermaid +subgraph Legend["Legend"] + direction LR + LP(["Person"]):::person + LS["Internal System"]:::system + LE["External System"]:::external + LP ~~~ LS ~~~ LE +end + +classDef person fill:#08427b,color:#fff,stroke:#052e56 +classDef system fill:#1168bd,color:#fff,stroke:#0b4884 +classDef external fill:#999999,color:#fff,stroke:#6b6b6b +``` + +Adapt legend entries to match what appears in the diagram. Use `~~~` (invisible link) for layout. + +### Colors + +| Class | Use for | Default | +|---|---|---| +| `person` | People / actors | `#08427b` dark blue | +| `system` | Internal systems / containers | `#1168bd` blue | +| `external` | External systems / services | `#999999` grey | +| `container` | Containers (when distinguishing from system) | `#438dd5` lighter blue | +| `component` | Components | `#85bbf0` lightest blue | +| `database` | Data stores | `#438dd5` with cylinder shape | + +Colors are not prescribed by C4 — adapt to context. Be consistent within and across diagrams. + +--- + +## Workflow + +### Step 0 — Intake + +Ask: + +> 1. What system (or landscape) are we diagramming? +> 2. Who is the audience? (everyone / technical / architects+devs) +> 3. What's the goal? (communicate to stakeholders / onboard devs / design review / document current state) +> 4. Do you already have named abstractions (systems, containers, components) or should we identify them together? + +Sensible defaults if user skips fields. Proceed once enough context exists. + +### Step 1 — Identify Abstractions + +Before drawing anything, map the architecture to C4 vocabulary: + +- **Software system** — what a single team owns. The team boundary = system boundary. +- **Container** — a running application or data store. Not Docker (though often maps to Docker). +- **Component** — a grouping of related code behind an interface, inside a container. Not separately deployable. + +Load [references/abstractions.md](references/abstractions.md) for: +- Microservice decision: same team → containers; different team → separate system +- Queues/topics: model as individual containers (data stores), not a single "message bus" +- Shared libraries: not containers — show as component copies with color coding + +### Step 2 — Select Diagram Type + +| Audience | Goal | Diagram type | +|---|---|---| +| Everyone | Big picture | System Context | +| Everyone | Enterprise overview | System Landscape | +| Technical | Architecture shape | Container | +| Architects + devs | Internal structure | Component | +| Technical | Runtime behavior | Dynamic (sequence) | +| Ops / infra | Where it runs | Deployment | + +Load [references/diagram-selection.md](references/diagram-selection.md) for detail. Confirm choice with user. + +Most teams need only System Context + Container. Don't over-diagram. + +### Step 3 — Produce Diagram + +1. Load the template for the chosen type from `assets/templates/` +2. Apply the rendering primitives from this file +3. Use `flowchart TB` for hierarchical views, `flowchart LR` for pipelines/flows +4. Keep under 25 nodes — split into focused sub-views if larger +5. Use `click NodeID "url" "tooltip"` for drill-down links between diagrams + +### Step 4 — Validate Syntax + +```bash +node scripts/validate_mermaid.mjs diagram.mmd +``` + +Or pipe directly: +```bash +echo '' | node scripts/validate_mermaid.mjs --strict - +``` + +Fix errors. Re-run until `✓ Valid`. Use `--strict` to also reject banned C4* keywords. + +### Step 5 — Self-Check (notation quality) + +- [ ] Title present (YAML frontmatter in the mermaid block) +- [ ] Every element has `[Stereotype]` in label +- [ ] Every element has one-line responsibility description +- [ ] Every container/component has technology stated +- [ ] Every edge is labeled with purpose +- [ ] Every edge is unidirectional (bidirectional only when both directions meaningful) +- [ ] Legend subgraph present (structural diagrams) +- [ ] Diagram has ≤ 25 nodes +- [ ] No `C4Context` / `C4Container` / `C4Component` / `C4Deployment` keywords +- [ ] Labels with HTML or special chars are quoted + +--- + +## Hard Rules + +1. **NEVER** use `C4Context`, `C4Container`, `C4Component`, `C4Deployment`, `C4Dynamic` — experimental, breaks across versions. +2. **ALWAYS** use `flowchart` + `subgraph` for structural diagrams, `sequenceDiagram` for temporal interactions. +3. **ALWAYS** quote labels containing HTML (``, ``, `
`) or special chars. +4. **ALWAYS** include Legend subgraph in structural diagrams. +5. **ALWAYS** validate with `scripts/validate_mermaid.mjs` before delivering. +6. **Purpose on edges**, not protocol. Protocol belongs one level down. +7. **Abstraction labels** (`[Person]`, `[Software System]`, `[Container: Tech]`, `[Component: Tech]`) are mandatory — they tell the reader what level they are looking at. + +--- + +## Anti-Patterns (top 5) + +1. **Hub-and-spoke message bus** — modeling RabbitMQ/Kafka as one container. Model each queue/topic as its own container instead. See [anti-patterns](references/anti-patterns.md). + +2. **Microservice as component** — a microservice is a container (or group of containers), never a component. Components are not separately deployable. + +3. **Mixing abstraction levels** — a context diagram showing containers, or a container diagram showing components from other systems. Each diagram has one scope. + +4. **Unlabeled edges** — every arrow must say what it does. "Uses" is too vague. Be specific: "sends payment requests to", "reads user profiles from". + +5. **Missing boundaries** — if your container diagram doesn't have a subgraph showing the system boundary, the reader can't tell what's inside vs. outside. + +Full catalog: [references/anti-patterns.md](references/anti-patterns.md) + +--- + +## Mermaid Syntax Pitfalls + +| Error | Fix | +|---|---| +| Unescaped `()[]` in labels | Quote entire label: `["label with (parens)"]` | +| Missing quotes on HTML labels | Always: `["Name
desc"]` | +| `subgraph` without `end` | Every `subgraph` needs matching `end` | +| Empty subgraph | Must contain at least one node | +| HTML stripped at render time | Renderer needs `securityLevel: 'loose'` — note in output | + +Full list: [references/mermaid-gotchas.md](references/mermaid-gotchas.md) diff --git a/skills/c4-diagram/assets/templates/component.mmd b/skills/c4-diagram/assets/templates/component.mmd new file mode 100644 index 0000000..c2424f0 --- /dev/null +++ b/skills/c4-diagram/assets/templates/component.mmd @@ -0,0 +1,52 @@ +%% C4 Component diagram +%% Use: internal structure of a single container +%% Audience: architects and developers +%% Scope: one container +%% When: only if it adds value — consider automating for long-lived docs + +--- +title: Internet Banking System — API Application — Component Diagram +--- +flowchart TB + SPA["Single-Page Application
[Container: JavaScript / Angular]
Provides banking functionality"]:::container + MobileApp["Mobile App
[Container: Xamarin]
Provides mobile banking"]:::container + + subgraph API ["API Application — [Container: Java / Spring MVC]"] + direction TB + SignIn["Sign In Controller
[Component: Spring MVC Controller]
Allows users to sign in"] + Accounts["Accounts Controller
[Component: Spring MVC Controller]
Provides account information"] + ResetPwd["Reset Password Controller
[Component: Spring MVC Controller]
Allows users to reset passwords"] + Security["Security Component
[Component: Spring Bean]
Handles authentication,
password changes, etc."] + EmailComp["E-mail Component
[Component: Spring Bean]
Sends e-mails to users"] + MainframeComp["Mainframe Facade
[Component: Spring Bean]
Facade to the mainframe
banking system"] + end + + DB[("Database
[Container: Oracle 12c]
Stores credentials and logs")]:::container + Email["E-mail System
[Software System, External]
Sends e-mails"]:::external + Mainframe["Mainframe Banking System
[Software System, External]
Core banking data"]:::external + + SPA -- "makes API calls to" --> SignIn + SPA -- "makes API calls to" --> Accounts + SPA -- "makes API calls to" --> ResetPwd + MobileApp -- "makes API calls to" --> SignIn + MobileApp -- "makes API calls to" --> Accounts + MobileApp -- "makes API calls to" --> ResetPwd + SignIn -- "uses" --> Security + Accounts -- "uses" --> MainframeComp + ResetPwd -- "uses" --> Security + ResetPwd -- "uses" --> EmailComp + Security -- "reads credentials from
[JDBC]" --> DB + EmailComp -- "sends e-mails using
[SMTP]" --> Email + MainframeComp -- "makes API calls to
[XML/HTTPS]" --> Mainframe + + subgraph Legend["Legend"] + direction LR + LComp["Component"]:::component + LCont["Container"]:::container + LE["External System"]:::external + LComp ~~~ LCont ~~~ LE + end + + classDef component fill:#85bbf0,color:#000,stroke:#5d99c6 + classDef container fill:#438dd5,color:#fff,stroke:#2e6295 + classDef external fill:#999999,color:#fff,stroke:#6b6b6b diff --git a/skills/c4-diagram/assets/templates/container.mmd b/skills/c4-diagram/assets/templates/container.mmd new file mode 100644 index 0000000..78d6226 --- /dev/null +++ b/skills/c4-diagram/assets/templates/container.mmd @@ -0,0 +1,46 @@ +%% C4 Container diagram +%% Use: high-level architecture — applications and data stores inside the system +%% Audience: technical (architects, developers, ops) +%% Scope: one software system (zoomed in) +%% When: always — shows technology choices and responsibility distribution + +--- +title: Internet Banking System — Container Diagram +--- +flowchart TB + Customer(["Personal Banking Customer
[Person]
A customer of the bank"]) + + subgraph IBS ["Internet Banking System — [Software System]"] + direction TB + WebApp["Web Application
[Container: Java / Spring MVC]
Delivers static content
and the single-page app"] + SPA["Single-Page Application
[Container: JavaScript / Angular]
Provides banking
functionality via browser"] + MobileApp["Mobile App
[Container: Xamarin]
Provides banking
functionality via mobile"] + API["API Application
[Container: Java / Spring MVC]
Provides Internet Banking
functionality via JSON/HTTPS API"] + DB[("Database
[Container: Oracle 12c]
Stores user registration,
hashed auth credentials, access logs")] + end + + Email["E-mail System
[Software System, External]
Sends e-mails"]:::external + Mainframe["Mainframe Banking System
[Software System, External]
Stores core banking data"]:::external + + Customer -- "visits" --> WebApp + Customer -- "views balances,
makes payments using" --> SPA + Customer -- "views balances,
makes payments using" --> MobileApp + WebApp -- "delivers to
customer's browser" --> SPA + SPA -- "makes API calls to
[JSON/HTTPS]" --> API + MobileApp -- "makes API calls to
[JSON/HTTPS]" --> API + API -- "reads from and
writes to [JDBC]" --> DB + API -- "sends e-mails using
[SMTP]" --> Email + API -- "makes API calls to
[XML/HTTPS]" --> Mainframe + + subgraph Legend["Legend"] + direction LR + LP(["Person"]):::person + LC["Container"]:::container + LDB[("Database")]:::container + LE["External System"]:::external + LP ~~~ LC ~~~ LDB ~~~ LE + end + + classDef person fill:#08427b,color:#fff,stroke:#052e56 + classDef container fill:#438dd5,color:#fff,stroke:#2e6295 + classDef external fill:#999999,color:#fff,stroke:#6b6b6b diff --git a/skills/c4-diagram/assets/templates/context.mmd b/skills/c4-diagram/assets/templates/context.mmd new file mode 100644 index 0000000..8aee1bb --- /dev/null +++ b/skills/c4-diagram/assets/templates/context.mmd @@ -0,0 +1,34 @@ +%% C4 System Context diagram +%% Use: the "big picture" — system in its environment, users, external dependencies +%% Audience: everyone (technical and non-technical) +%% Scope: one software system +%% When: always — every system should have one + +--- +title: Internet Banking System — System Context +--- +flowchart TB + Customer(["Personal Banking Customer
[Person]
A customer of the bank
who wants to manage accounts"]) + + IBS["Internet Banking System
[Software System]
Allows customers to view
balances and make payments"] + + Email["E-mail System
[Software System, External]
Sends e-mails
to customers"]:::external + + Mainframe["Mainframe Banking System
[Software System, External]
Stores core banking
information and transactions"]:::external + + Customer -- "views account balances
and makes payments using" --> IBS + IBS -- "sends e-mails using" --> Email + IBS -- "gets account information
and makes payments using" --> Mainframe + Email -- "sends e-mails to" --> Customer + + subgraph Legend["Legend"] + direction LR + LP(["Person"]):::person + LS["Internal System"]:::system + LE["External System"]:::external + LP ~~~ LS ~~~ LE + end + + classDef person fill:#08427b,color:#fff,stroke:#052e56 + classDef system fill:#1168bd,color:#fff,stroke:#0b4884 + classDef external fill:#999999,color:#fff,stroke:#6b6b6b diff --git a/skills/c4-diagram/assets/templates/deployment.mmd b/skills/c4-diagram/assets/templates/deployment.mmd new file mode 100644 index 0000000..c1bb50a --- /dev/null +++ b/skills/c4-diagram/assets/templates/deployment.mmd @@ -0,0 +1,52 @@ +%% C4 Deployment diagram +%% Use: mapping containers to infrastructure +%% Audience: technical (architects, devs, ops, infra) +%% Scope: one or more systems in one environment (dev, staging, prod) +%% When: recommended — shows where containers actually run + +--- +title: Internet Banking System — Deployment (Live Environment) +--- +flowchart TB + subgraph Live ["Live Environment"] + direction TB + + subgraph CustomerDevices ["Customer's Devices — [Deployment Node]"] + direction TB + subgraph Browser ["Web Browser — [Deployment Node: Chrome, Firefox, Safari]"] + SPA["Single-Page Application
[Container: JavaScript / Angular]
Provides banking functionality"] + end + subgraph Mobile ["Mobile Device — [Deployment Node: iOS / Android]"] + MobileApp["Mobile App
[Container: Xamarin]
Mobile banking"] + end + end + + subgraph Cloud ["AWS Cloud — [Deployment Node: us-east-1]"] + direction TB + subgraph WebServer ["Web Server — [Deployment Node: EC2 t3.medium x2]"] + WebApp["Web Application
[Container: Java / Spring MVC]
Delivers static content"] + end + subgraph AppServer ["App Server — [Deployment Node: EC2 m5.large x4]"] + API["API Application
[Container: Java / Spring MVC]
Provides banking API"] + end + subgraph DBServer ["Database Server — [Deployment Node: RDS Multi-AZ]"] + DB[("Database
[Container: Oracle 12c]
Stores user data")] + end + end + end + + SPA -- "makes API calls to
[HTTPS]" --> API + MobileApp -- "makes API calls to
[HTTPS]" --> API + WebApp -- "delivers to browser" --> SPA + API -- "reads/writes
[JDBC]" --> DB + + subgraph Legend["Legend"] + direction LR + LDN["Deployment Node"]:::deploynode + LC["Container"]:::container + LDB[("Database")]:::container + LDN ~~~ LC ~~~ LDB + end + + classDef deploynode fill:#ffffff,color:#000,stroke:#888888,stroke-dasharray: 5 5 + classDef container fill:#438dd5,color:#fff,stroke:#2e6295 diff --git a/skills/c4-diagram/assets/templates/dynamic.mmd b/skills/c4-diagram/assets/templates/dynamic.mmd new file mode 100644 index 0000000..7de71ac --- /dev/null +++ b/skills/c4-diagram/assets/templates/dynamic.mmd @@ -0,0 +1,26 @@ +%% C4 Dynamic diagram — sequence style +%% Use: showing runtime behavior for a specific scenario/use case +%% Audience: technical and non-technical +%% Scope: a feature, story, or use case +%% When: sparingly — for interesting or complex interactions + +--- +title: Internet Banking System — Sign In (Dynamic) +--- +sequenceDiagram + actor Customer as Personal Banking Customer
[Person] + participant SPA as Single-Page Application
[Container: JS/Angular] + participant API as API Application
[Container: Java/Spring] + participant Security as Security Component
[Component: Spring Bean] + participant DB as Database
[Container: Oracle 12c] + + Customer->>SPA: Enters credentials and clicks "Sign In" + SPA->>+API: POST /api/auth/signin [JSON/HTTPS] + API->>+Security: validateCredentials(username, password) + Security->>+DB: SELECT * FROM users WHERE username = ? [JDBC] + DB-->>-Security: user record + Security->>Security: verify password hash + Security-->>-API: AuthResult{authenticated, token} + API-->>-SPA: 200 OK {token, expires} + SPA->>SPA: Store token, redirect to dashboard + SPA-->>Customer: Shows account dashboard diff --git a/skills/c4-diagram/assets/templates/landscape.mmd b/skills/c4-diagram/assets/templates/landscape.mmd new file mode 100644 index 0000000..0a79113 --- /dev/null +++ b/skills/c4-diagram/assets/templates/landscape.mmd @@ -0,0 +1,47 @@ +%% C4 System Landscape diagram +%% Use: enterprise overview — all systems and people in a scope +%% Audience: everyone (technical and non-technical) +%% Scope: entire enterprise, organisation, or department +%% When: recommended for larger organisations — no single focal system + +--- +title: Big Bank plc — System Landscape +--- +flowchart TB + Customer(["Personal Banking Customer
[Person]
A customer of the bank"]) + BackOffice(["Back Office Staff
[Person]
Administration and support"]) + + subgraph BigBank ["Big Bank plc — [Enterprise]"] + direction TB + IBS["Internet Banking System
[Software System]
Allows customers to view
balances and make payments"] + ATM["ATM System
[Software System]
Allows customers to
withdraw cash"] + Mainframe["Mainframe Banking System
[Software System]
Stores core banking data"] + CRM["CRM System
[Software System]
Manages customer
relationships"] + Compliance["Compliance System
[Software System]
Regulatory reporting
and audit"] + end + + Email["E-mail System
[Software System, External]
Sends e-mails"]:::external + CreditBureau["Credit Bureau
[Software System, External]
Credit scoring"]:::external + + Customer -- "manages accounts using" --> IBS + Customer -- "withdraws cash using" --> ATM + BackOffice -- "manages customers using" --> CRM + BackOffice -- "reviews reports in" --> Compliance + IBS -- "gets account data from" --> Mainframe + ATM -- "uses" --> Mainframe + CRM -- "gets customer data from" --> Mainframe + Compliance -- "reads transactions from" --> Mainframe + IBS -- "sends notifications using" --> Email + CRM -- "checks credit via" --> CreditBureau + + subgraph Legend["Legend"] + direction LR + LP(["Person"]):::person + LS["Internal System"]:::system + LE["External System"]:::external + LP ~~~ LS ~~~ LE + end + + classDef person fill:#08427b,color:#fff,stroke:#052e56 + classDef system fill:#1168bd,color:#fff,stroke:#0b4884 + classDef external fill:#999999,color:#fff,stroke:#6b6b6b diff --git a/skills/c4-diagram/references/abstractions.md b/skills/c4-diagram/references/abstractions.md new file mode 100644 index 0000000..6a46f5a --- /dev/null +++ b/skills/c4-diagram/references/abstractions.md @@ -0,0 +1,112 @@ +# C4 Abstractions — Reference + +Load when identifying what things *are* in the architecture before drawing. + +--- + +## The Four Abstractions + +``` +Person → uses → Software System → contains → Container → contains → Component → implemented by → Code +``` + +### Software System + +The highest level. Delivers value to users. What a **single team** owns, builds, and can see inside. + +**Heuristic:** code lives in one repo (or a set of repos the team owns); anyone on the team can modify it; team boundary ≈ system boundary. + +**Not software systems:** product domains, bounded contexts, business capabilities, tribes, squads — these are organizational constructs, not abstractions. + +### Container + +An **application or data store** that must be running for the system to work. A **runtime boundary** around executing code or stored data. + +Examples: server-side web app (Spring Boot, Node.js), SPA (React in browser), mobile app, serverless function (Lambda), database schema, blob store (S3), file system. + +**Not Docker** — though there is often a one-to-one mapping. + +**Web app — one or two containers?** Server-rendered HTML = one. Significant client-side JS (SPA) = two (server API + client app). + +### Component + +A grouping of related functionality behind a **well-defined interface**, running inside a container. **Not separately deployable.** All components share the container's process space. + +By language: +- Java/C#: classes + interfaces +- JavaScript: module (objects + functions) +- Go: package +- Functional: module (functions, types) + +### Code + +Classes, interfaces, functions, enums — language primitives. Usually not worth diagramming manually; auto-generate if needed. + +--- + +## Decision: Microservices + +The key question: **who owns it?** + +| Ownership | Model as | +|---|---| +| Same team owns all services | Each service = **container** (or group of containers) inside one software system | +| Different teams | Each team's services = separate **software system** | + +**Common mistake:** modeling a microservice as a container and its API + database as *components*. Wrong — components are not deployable. The API is a container; the database is a container. The microservice is a *grouping* of containers. + +### Worked example + +1. *One team, monolith* → one software system, one container (the app), one database container. +2. *One team, microservices* → same system context. Container diagram shows individual service containers (API + DB pairs), grouped with color or dashed subgraphs. +3. *Multiple teams* → each team gets its own software system. Your context diagram shows the other team's system as an external system. + +**Rule:** only show containers inside your own system boundary. Cross-team container diagrams encode coupling that shouldn't be there. + +--- + +## Decision: Queues and Topics + +**Wrong:** model the message bus (RabbitMQ, Kafka) as a single container → hub-and-spoke, obscures real coupling. + +**Correct option 1 — explicit:** each queue/topic is its own container (a data store). Shows point-to-point coupling. + +**Correct option 2 — implicit:** omit queue containers; put queue names on relationship arrows ("sends orders via OrderQueue"). Simpler but less explicit. + +For pub/sub: producer publishes to topic; subscribers consume from it. Arrow direction follows data flow. + +--- + +## Decision: Shared Libraries + +**Wrong:** model as a container (libraries don't run). + +**Correct:** at deployment there are *N copies* — one per container that uses it. Show the shared component inside each container. Use color coding to signal "this is shared code." Add a dashed boundary for the jar/package if helpful. + +--- + +## Decision: Layers and Bounded Contexts + +These are **organizational constructs**, not new abstractions. Show them as: +- Dashed `subgraph` boundaries inside a container +- Color coding +- Labels + +The four C4 abstractions stay unchanged. + +--- + +## Quick Reference + +| I have... | It is a C4... | Because... | +|---|---|---| +| A whole product my team builds | Software System | Team boundary = system boundary | +| A Spring Boot app | Container | Deployable, running process | +| A React SPA | Container | Runs in browser, separate from API | +| A PostgreSQL database | Container | Running data store | +| An AWS Lambda function | Container | Deployable unit | +| A Spring Bean / service class | Component | Grouped functionality, not deployable alone | +| RabbitMQ (the broker) | Deployment detail | The individual queues are containers | +| A shared npm package | Component (copy per container) | Not a runtime thing on its own | +| "Our billing subsystem" | Organizational construct | Use a subgraph boundary | +| Another team's API | Software System (external) | You can't see inside it | diff --git a/skills/c4-diagram/references/anti-patterns.md b/skills/c4-diagram/references/anti-patterns.md new file mode 100644 index 0000000..d019f81 --- /dev/null +++ b/skills/c4-diagram/references/anti-patterns.md @@ -0,0 +1,161 @@ +# Anti-Patterns — Reference + +Load when reviewing diagram quality or when something "feels wrong." + +--- + +## C4 Methodology Errors + +### 1. Hub-and-Spoke Message Bus + +**Symptom:** A single "Message Bus" or "RabbitMQ" container in the center with arrows from/to every service. + +**Problem:** Obscures real point-to-point coupling. You can't tell which services actually depend on each other. + +**Fix:** Model each queue/topic as its own container. Or use implicit notation: direct arrows labeled "via OrderQueue". + +```mermaid +%% WRONG +flowchart TB + A["Service A"] --> MQ["Message Bus"] + B["Service B"] --> MQ + MQ --> C["Service C"] + MQ --> D["Service D"] +``` + +```mermaid +%% CORRECT — explicit queues +flowchart TB + A["Service A"] -- "publishes orders to" --> OrderQ[("OrderQueue")] + OrderQ -- "consumed by" --> C["Service C"] + B["Service B"] -- "publishes events to" --> EventQ[("EventQueue")] + EventQ -- "consumed by" --> D["Service D"] +``` + +--- + +### 2. Microservice as Component + +**Symptom:** A microservice modeled as a C4 component, with its API and database as sub-elements. + +**Problem:** Components are not separately deployable. A microservice IS deployable — it's a container (or group of containers). + +**Fix:** Model the microservice's API as a container, its database as a container. Group them visually with a subgraph or color coding. + +--- + +### 3. Mixing Abstraction Levels + +**Symptom:** +- A system context diagram showing containers from inside the system +- A container diagram showing components from a different system +- External system internals visible on your container diagram + +**Problem:** Each diagram has one scope. Mixing levels confuses the reader about what is inside vs. outside, what is deployable vs. logical. + +**Fix:** +- Context diagram: only systems and people +- Container diagram: only containers inside YOUR system + external systems as black boxes +- Component diagram: only components inside ONE container + +--- + +### 4. Unlabeled or Vaguely Labeled Edges + +**Symptom:** Arrows with "uses", "calls", "depends on", or no label at all. + +**Problem:** The reader doesn't know what the relationship means. "Uses" tells you nothing. + +**Fix:** Be specific about purpose: "sends payment requests to", "reads user profiles from", "notifies about order status changes". + +--- + +### 5. Missing System Boundary + +**Symptom:** Container diagram with all containers floating — no visual indication of what's inside the system vs. external. + +**Problem:** Reader can't tell the team's ownership boundary. + +**Fix:** Wrap your containers in a `subgraph` labeled with the system name and `[Software System]`. + +--- + +### 6. Docker Confusion + +**Symptom:** Treating Docker as the definition of "container." Or adding Docker as a C4 container. + +**Problem:** C4 containers are an abstraction — applications and data stores. Docker is a deployment mechanism. A Spring Boot app is the C4 container; Docker is where it runs (deployment diagram). + +**Fix:** Separate static structure (container diagram) from deployment (deployment diagram). The C4 container = the app; Docker = the deployment node it runs in. + +--- + +### 7. Adding New Abstraction Levels + +**Symptom:** Inventing "subsystem", "module", "service layer" as new C4 levels between system and container, or between container and component. + +**Problem:** Defining abstractions is extremely hard. Custom abstractions quickly become vague and team-specific, degrading back to ad hoc boxes. + +**Fix:** Use the four fixed C4 abstractions. Express layers, subsystems, bounded contexts as **organizational constructs** — subgraph boundaries, color coding, labels — overlaid on the existing abstractions. + +--- + +### 8. Architecture Decisions in Diagrams + +**Symptom:** Trying to show WHY the architecture is this way in the diagram itself. + +**Problem:** Category error. C4 diagrams show the *outcome* of decisions (what was decided), not the rationale (why). + +**Fix:** Decisions belong in ADRs (Architecture Decision Records). Keep traceability between diagrams and ADRs, but separate the artifacts. + +--- + +### 9. Deployment on Container Diagrams + +**Symptom:** Container diagrams showing load balancers, DNS, replication, auto-scaling groups. + +**Problem:** Container diagrams show logical architecture. Deployment concerns go in deployment diagrams. + +**Fix:** One container diagram (logical shape) + one deployment diagram per environment (physical mapping). + +--- + +### 10. Shared Database as Neutral Territory + +**Symptom:** A shared database floating between two systems with no clear ownership. + +**Problem:** Shared databases are coupling. Someone owns the schema. + +**Fix:** Assign ownership. The team that owns the schema owns the container. If truly shared and unresolvable, model an intermediate system that owns it. + +--- + +## Notation Errors + +### 11. No Title + +Every diagram needs: `"[Diagram Type] for [Scope]"` — e.g., "Container Diagram for Internet Banking System". + +### 12. No Legend + +If you use colors, shapes, or line styles to convey meaning, the diagram needs a legend explaining them. + +### 13. Missing Element Metadata + +Every element needs: +- **Name** (bold) +- **Type** (`[Person]`, `[Software System]`, `[Container: Technology]`, `[Component: Technology]`) +- **One-line responsibility** (what it does) + +Without the type label, readers can't distinguish containers from components from systems. + +### 14. Inconsistent Naming Across Diagrams + +If it's called "Payment Service" on the context diagram, it must be "Payment Service" on the container diagram. Same names, always. This is where modeling (one definition, multiple views) beats diagramming (copy-paste between separate files). + +### 15. More Than ~25 Nodes + +At 25+ nodes, a diagram becomes unreadable. Split into focused sub-views: +- Per-service focused container diagrams +- Per-container component diagrams +- Per-domain landscape slices diff --git a/skills/c4-diagram/references/diagram-selection.md b/skills/c4-diagram/references/diagram-selection.md new file mode 100644 index 0000000..cdb8aa4 --- /dev/null +++ b/skills/c4-diagram/references/diagram-selection.md @@ -0,0 +1,138 @@ +# Diagram Type Selection — Reference + +Load when choosing which diagram(s) to produce. + +--- + +## Decision Matrix + +| Question to ask | Answer points toward | +|---|---| +| Who reads this? Everyone | System Context or Landscape | +| Who reads this? Technical team | Container | +| Who reads this? Architects + devs | Component | +| What's the goal? Big picture / onboarding | System Context | +| What's the goal? Enterprise map | System Landscape | +| What's the goal? Technology choices visible | Container | +| What's the goal? Internal design of one app | Component | +| What's the goal? Show runtime behavior | Dynamic (sequence) | +| What's the goal? Show infrastructure mapping | Deployment | +| How many systems in scope? One | Context / Container / Component | +| How many systems in scope? Many | System Landscape | + +--- + +## The Seven Diagram Types + +### 1. System Context (C1) + +| | | +|---|---| +| Scope | One software system | +| Shows | The system + surrounding people and external systems | +| Audience | Everyone | +| Recommended | **Yes — always** | +| Mermaid type | `flowchart TB` | + +The most important diagram. Safe for non-technical stakeholders. No technology detail. + +### 2. Container (C2) + +| | | +|---|---| +| Scope | One software system (zoomed in) | +| Shows | Applications and data stores inside the system | +| Audience | Technical | +| Recommended | **Yes — always** | +| Mermaid type | `flowchart TB` with `subgraph` for system boundary | + +Shows architecture shape, responsibility distribution, major technology choices, container communication. Does NOT show deployment (load balancers, replication). + +### 3. Component (C3) + +| | | +|---|---| +| Scope | One container | +| Shows | Components inside the container | +| Audience | Architects and developers | +| Recommended | Only if it adds value | +| Mermaid type | `flowchart TB` with `subgraph` for container boundary | + +Consider auto-generating from code for long-lived documentation. + +### 4. Code (C4) + +| | | +|---|---| +| Scope | One component | +| Shows | Classes, interfaces, functions | +| Audience | Developers | +| Recommended | **No** — auto-generate only | +| Mermaid type | `classDiagram` | + +Almost never worth manual creation. + +### 5. System Landscape + +| | | +|---|---| +| Scope | Enterprise / organisation / department | +| Shows | All people and software systems | +| Audience | Everyone | +| Recommended | Yes for larger organisations | +| Mermaid type | `flowchart TB` with `subgraph` for org boundary | + +A context diagram without a focal system — the whole estate. + +### 6. Dynamic + +| | | +|---|---| +| Scope | A feature, story, or use case | +| Shows | Runtime interactions between elements | +| Audience | Technical and non-technical | +| Recommended | Sparingly — for complex interactions | +| Mermaid type | `sequenceDiagram` | + +Shows *how* the system behaves at runtime for a specific scenario. Numbered interactions indicate ordering. + +### 7. Deployment + +| | | +|---|---| +| Scope | One or more systems in one environment | +| Shows | Where containers run (infra nodes, cloud services) | +| Audience | Technical (architects, ops, infra) | +| Recommended | **Yes** | +| Mermaid type | `flowchart TB` with nested `subgraph` for deployment nodes | + +One diagram per environment (dev, staging, prod). Deployment nodes can nest: physical → VM → Docker → process. + +--- + +## Practical Guidance + +### "Most teams only need two" + +System Context + Container diagrams cover 80% of communication needs. They are: +- Quick to create +- High-value +- Relatively stable (don't change with every feature) + +Component and Code diagrams change with every PR. Create them only when the cost is justified. + +### When to split + +- Container diagram with > 15-20 services → split into per-service focused views (each service + immediate neighbors) +- System Landscape with > 20 systems → group by domain/department into sub-landscapes + +### One static model → multiple views + +The same system has: +- One context diagram (always) +- One container diagram (always) +- N component diagrams (one per container, if needed) +- N deployment diagrams (one per environment) +- N dynamic diagrams (one per interesting scenario) + +All share the same abstractions and naming. diff --git a/skills/c4-diagram/references/mermaid-gotchas.md b/skills/c4-diagram/references/mermaid-gotchas.md new file mode 100644 index 0000000..a792751 --- /dev/null +++ b/skills/c4-diagram/references/mermaid-gotchas.md @@ -0,0 +1,198 @@ +# Mermaid Syntax Gotchas — Reference + +Load when validation fails or output renders incorrectly. + +--- + +## Critical: Security Level + +If labels contain HTML (``, ``, `
`), the renderer **must** use: + +```javascript +mermaid.initialize({ securityLevel: 'loose' }); +``` + +In `strict` mode (default in many renderers), HTML entities are stripped. The syntax is valid but the output looks garbled — `` disappears, `
` becomes literal text. + +**Action for output:** When delivering diagrams, note that renderers must use `securityLevel: 'loose'` for HTML labels to display correctly. GitHub Markdown preview strips HTML from Mermaid — diagrams render correctly in tools like Mermaid Live Editor, documentation sites, or rendered markdown with loose security. + +--- + +## Common Syntax Errors + +| Error | Symptom | Fix | +|---|---|---| +| Unescaped special chars in label | Parse error pointing at `(`, `)`, `[`, `]` | Wrap entire label in quotes: `["label with (parens)"]` | +| Missing quotes on HTML labels | Parse error or mangled output | Always quote: `["Name
desc"]` | +| Comma in label without quotes | Unexpected token error | Quote the label | +| `subgraph` without `end` | "Parse error" at end of file or next statement | Every `subgraph` needs matching `end` | +| Empty subgraph | Silently dropped or parse error | Always include at least one node inside | +| Node ID reuse across subgraphs | Second occurrence is silently moved | Use unique IDs; if same element appears in two places, use different IDs with same label | +| `direction` placement | Layout doesn't respond | `direction LR` must be the first line inside `subgraph`, before any nodes | +| Arrow to subgraph | Inconsistent rendering | Arrow to a specific node inside the subgraph, not the subgraph itself | +| Colons in label text | Mermaid interprets as node type | Quote the label or use HTML entity `:` | +| Pipe `|` in label | Interpreted as edge label syntax | Quote or use HTML entity `|` | +| Parentheses in node ID | Treated as shape delimiter | Keep node IDs alphanumeric + underscore only | + +--- + +## Label Quoting Rules + +**Always quote when the label contains any of:** +- HTML tags: ``, ``, `
` +- Parentheses: `(`, `)` +- Brackets: `[`, `]` +- Commas: `,` +- Colons: `:` +- Pipes: `|` +- Quotes within: use the other quote type or HTML entity + +**Safe unquoted:** pure alphanumeric text without special characters. + +**Rule of thumb:** always quote. There is no penalty for quoting a simple label. + +--- + +## Subgraph Gotchas + +```mermaid +%% CORRECT: direction first, nodes after +subgraph MySystem ["My System — [Software System]"] + direction TB + A["Node A"] + B["Node B"] +end + +%% WRONG: node before direction — layout may not apply +subgraph MySystem ["My System"] + A["Node A"] + direction TB + B["Node B"] +end +``` + +- Subgraphs can nest (useful for deployment nodes) +- Subgraph labels follow same quoting rules as node labels +- A subgraph with no nodes inside may be silently dropped +- You cannot draw an edge directly TO a subgraph in all renderers — draw to a node inside it + +--- + +## Edge Label Gotchas + +```mermaid +%% CORRECT: label in quotes on the edge +A -- "sends requests to" --> B + +%% ALSO CORRECT: label between pipes (works but less readable with long text) +A -->|"sends requests to"| B + +%% WRONG: unquoted label with special chars +A -- sends (HTTP) requests --> B +``` + +- Always quote edge labels +- Long labels: use `
` for line breaks within the label +- Edge labels cannot contain `--` or `-->` (Mermaid interprets these as arrow syntax) + +--- + +## classDef and Styling + +```mermaid +%% Define at bottom of diagram for readability +classDef person fill:#08427b,color:#fff,stroke:#052e56 +classDef system fill:#1168bd,color:#fff,stroke:#0b4884 +classDef external fill:#999999,color:#fff,stroke:#6b6b6b + +%% Apply inline +Node["Label"]:::person + +%% Or apply separately +class Node1,Node2 person +``` + +- `classDef` can appear anywhere in the diagram (top, middle, bottom) +- Place at bottom for readability — after all nodes and edges +- `:::className` applies inline when defining the node +- `class NodeID className` applies separately (useful for multiple nodes) + +--- + +## Frontmatter Title + +```mermaid +--- +title: My Diagram Title +--- +flowchart TB + ... +``` + +- The `---` YAML frontmatter block must be the very first thing in the Mermaid code +- Only `title` is commonly supported +- Some renderers may ignore it — include the title anyway for documentation value + +--- + +## Banned Keywords + +These Mermaid diagram types are **experimental and unreliable** across versions: + +- `C4Context` +- `C4Container` +- `C4Component` +- `C4Deployment` +- `C4Dynamic` + +If you see these in existing diagrams, rewrite using `flowchart` + subgraphs. + +--- + +## Diagram Type Stability + +| Type | Stability | Notes | +|---|---|---| +| `flowchart` | ✅ Stable | Primary choice for all C4 structural diagrams | +| `sequenceDiagram` | ✅ Stable | Use for dynamic/interaction views | +| `stateDiagram-v2` | ✅ Stable | Use for state machines | +| `classDiagram` | ✅ Stable | Use only for code-level (C4 level 4) | +| `C4Context` etc. | ❌ Experimental | NEVER use | +| `mindmap` | ⚠️ Unstable | Layout changes between versions | +| `timeline` | ⚠️ New | Less battle-tested | +| `zenuml` | ❌ Experimental | Avoid | + +--- + +## Layout Hints + +- `flowchart TB` — top-to-bottom (best for hierarchical C4 views) +- `flowchart LR` — left-to-right (for pipeline/flow views) +- `~~~` — invisible link for layout hints without visible edges +- Mermaid's auto-layout (dagre/elk) is non-deterministic — don't rely on exact positioning +- Keep diagrams under ~25 nodes for readability +- `direction` inside subgraph is independent of parent flowchart direction + +--- + +## Click / Drill-Down + +```mermaid +click NodeID "url" "tooltip" _blank +``` + +Use for linking C4 levels: a container node on the context diagram links to its container diagram. A container on the container diagram links to its component diagram. + +--- + +## Rendering Environment Notes + +| Environment | HTML labels | Notes | +|---|---|---| +| Mermaid Live Editor | ✅ Works | `securityLevel: 'loose'` by default | +| GitHub Markdown | ❌ Stripped | GitHub uses `strict` — HTML in labels won't render | +| GitLab Markdown | ✅ Works | Supports loose security | +| Documentation sites (Docusaurus, MkDocs) | ✅ Configurable | Set loose in mermaid config | +| VS Code preview | ✅ Configurable | Depends on extension settings | + +**If targeting GitHub:** avoid HTML in labels; use plain text with `\n` line breaks or accept single-line labels. Or provide both an `.mmd` source file and a rendered `.svg`. diff --git a/skills/c4-diagram/scripts/package-lock.json b/skills/c4-diagram/scripts/package-lock.json new file mode 100644 index 0000000..2e017ec --- /dev/null +++ b/skills/c4-diagram/scripts/package-lock.json @@ -0,0 +1,1612 @@ +{ + "name": "c4-diagram-scripts", + "version": "0.1.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "c4-diagram-scripts", + "version": "0.1.0", + "dependencies": { + "jsdom": "^29.1.1", + "mermaid": "^11.4.0" + } + }, + "node_modules/@antfu/install-pkg": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@antfu/install-pkg/-/install-pkg-1.1.0.tgz", + "integrity": "sha512-MGQsmw10ZyI+EJo45CdSER4zEb+p31LpDAFp2Z3gkSd1yqVZGi0Ebx++YTEMonJy4oChEMLsxZ64j8FH6sSqtQ==", + "license": "MIT", + "dependencies": { + "package-manager-detector": "^1.3.0", + "tinyexec": "^1.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/@asamuzakjp/css-color": { + "version": "5.1.11", + "resolved": "https://registry.npmjs.org/@asamuzakjp/css-color/-/css-color-5.1.11.tgz", + "integrity": "sha512-KVw6qIiCTUQhByfTd78h2yD1/00waTmm9uy/R7Ck/ctUyAPj+AEDLkQIdJW0T8+qGgj3j5bpNKK7Q3G+LedJWg==", + "license": "MIT", + "dependencies": { + "@asamuzakjp/generational-cache": "^1.0.1", + "@csstools/css-calc": "^3.2.0", + "@csstools/css-color-parser": "^4.1.0", + "@csstools/css-parser-algorithms": "^4.0.0", + "@csstools/css-tokenizer": "^4.0.0" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0" + } + }, + "node_modules/@asamuzakjp/dom-selector": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/@asamuzakjp/dom-selector/-/dom-selector-7.1.1.tgz", + "integrity": "sha512-67RZDnYRc8H/8MLDgQCDE//zoqVFwajkepHZgmXrbwybzXOEwOWGPYGmALYl9J2DOLfFPPs6kKCqmbzV895hTQ==", + "license": "MIT", + "dependencies": { + "@asamuzakjp/generational-cache": "^1.0.1", + "@asamuzakjp/nwsapi": "^2.3.9", + "bidi-js": "^1.0.3", + "css-tree": "^3.2.1", + "is-potential-custom-element-name": "^1.0.1" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0" + } + }, + "node_modules/@asamuzakjp/generational-cache": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@asamuzakjp/generational-cache/-/generational-cache-1.0.1.tgz", + "integrity": "sha512-wajfB8KqzMCN2KGNFdLkReeHncd0AslUSrvHVvvYWuU8ghncRJoA50kT3zP9MVL0+9g4/67H+cdvBskj9THPzg==", + "license": "MIT", + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0" + } + }, + "node_modules/@asamuzakjp/nwsapi": { + "version": "2.3.9", + "resolved": "https://registry.npmjs.org/@asamuzakjp/nwsapi/-/nwsapi-2.3.9.tgz", + "integrity": "sha512-n8GuYSrI9bF7FFZ/SjhwevlHc8xaVlb/7HmHelnc/PZXBD2ZR49NnN9sMMuDdEGPeeRQ5d0hqlSlEpgCX3Wl0Q==", + "license": "MIT" + }, + "node_modules/@braintree/sanitize-url": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/@braintree/sanitize-url/-/sanitize-url-7.1.2.tgz", + "integrity": "sha512-jigsZK+sMF/cuiB7sERuo9V7N9jx+dhmHHnQyDSVdpZwVutaBu7WvNYqMDLSgFgfB30n452TP3vjDAvFC973mA==", + "license": "MIT" + }, + "node_modules/@bramus/specificity": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/@bramus/specificity/-/specificity-2.4.2.tgz", + "integrity": "sha512-ctxtJ/eA+t+6q2++vj5j7FYX3nRu311q1wfYH3xjlLOsczhlhxAg2FWNUXhpGvAw3BWo1xBcvOV6/YLc2r5FJw==", + "license": "MIT", + "dependencies": { + "css-tree": "^3.0.0" + }, + "bin": { + "specificity": "bin/cli.js" + } + }, + "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==", + "license": "Apache-2.0" + }, + "node_modules/@csstools/color-helpers": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/@csstools/color-helpers/-/color-helpers-6.0.2.tgz", + "integrity": "sha512-LMGQLS9EuADloEFkcTBR3BwV/CGHV7zyDxVRtVDTwdI2Ca4it0CCVTT9wCkxSgokjE5Ho41hEPgb8OEUwoXr6Q==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT-0", + "engines": { + "node": ">=20.19.0" + } + }, + "node_modules/@csstools/css-calc": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/@csstools/css-calc/-/css-calc-3.2.1.tgz", + "integrity": "sha512-DtdHlgXh5ZkA43cwBcAm+huzgJiwx3ZTWVjBs94kwz2xKqSimDA3lBgCjphYgwgVUMWatSM0pDd8TILB1yrVVg==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "engines": { + "node": ">=20.19.0" + }, + "peerDependencies": { + "@csstools/css-parser-algorithms": "^4.0.0", + "@csstools/css-tokenizer": "^4.0.0" + } + }, + "node_modules/@csstools/css-color-parser": { + "version": "4.1.8", + "resolved": "https://registry.npmjs.org/@csstools/css-color-parser/-/css-color-parser-4.1.8.tgz", + "integrity": "sha512-3chWb7PRLijpJpPIKkDxdu6IBeO5MrFACND57On0j8OPpc0wZibcGc3xAHrSEbOx/KDRyMHoIxGn0w1PhXMYHw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "dependencies": { + "@csstools/color-helpers": "^6.0.2", + "@csstools/css-calc": "^3.2.1" + }, + "engines": { + "node": ">=20.19.0" + }, + "peerDependencies": { + "@csstools/css-parser-algorithms": "^4.0.0", + "@csstools/css-tokenizer": "^4.0.0" + } + }, + "node_modules/@csstools/css-parser-algorithms": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@csstools/css-parser-algorithms/-/css-parser-algorithms-4.0.0.tgz", + "integrity": "sha512-+B87qS7fIG3L5h3qwJ/IFbjoVoOe/bpOdh9hAjXbvx0o8ImEmUsGXN0inFOnk2ChCFgqkkGFQ+TpM5rbhkKe4w==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "engines": { + "node": ">=20.19.0" + }, + "peerDependencies": { + "@csstools/css-tokenizer": "^4.0.0" + } + }, + "node_modules/@csstools/css-syntax-patches-for-csstree": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@csstools/css-syntax-patches-for-csstree/-/css-syntax-patches-for-csstree-1.1.5.tgz", + "integrity": "sha512-oNjBvzLq2GPZtJphCjLqXow/cHySHSgtxvKZb7OqSZ/xHgw6NWNhfad+6AB9cLeVm6eA9d/qMll3JdEHjy6M+A==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT-0", + "peerDependencies": { + "css-tree": "^3.2.1" + }, + "peerDependenciesMeta": { + "css-tree": { + "optional": true + } + } + }, + "node_modules/@csstools/css-tokenizer": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@csstools/css-tokenizer/-/css-tokenizer-4.0.0.tgz", + "integrity": "sha512-QxULHAm7cNu72w97JUNCBFODFaXpbDg+dP8b/oWFAZ2MTRppA3U00Y2L1HqaS4J6yBqxwa/Y3nMBaxVKbB/NsA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "engines": { + "node": ">=20.19.0" + } + }, + "node_modules/@exodus/bytes": { + "version": "1.15.1", + "resolved": "https://registry.npmjs.org/@exodus/bytes/-/bytes-1.15.1.tgz", + "integrity": "sha512-S6mL0yNB/Abt9Ei4tq8gDhcczc4S3+vQ4ra7vxnAf+YHC02srtqxKKZghx2Dq6p0e66THKwR6r8N6P95wEty7Q==", + "license": "MIT", + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0" + }, + "peerDependencies": { + "@noble/hashes": "^1.8.0 || ^2.0.0" + }, + "peerDependenciesMeta": { + "@noble/hashes": { + "optional": true + } + } + }, + "node_modules/@iconify/types": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@iconify/types/-/types-2.0.0.tgz", + "integrity": "sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg==", + "license": "MIT" + }, + "node_modules/@iconify/utils": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/@iconify/utils/-/utils-3.1.3.tgz", + "integrity": "sha512-LPKOXPn/zV+zis1oOfGWogaXVpqUybF3ZS6SCZIsz8vg0ivVp9+fVqyYB7xq0aiST/VhUQYGO1qo6uoYSiEJqw==", + "license": "MIT", + "dependencies": { + "@antfu/install-pkg": "^1.1.0", + "@iconify/types": "^2.0.0", + "import-meta-resolve": "^4.2.0" + } + }, + "node_modules/@mermaid-js/parser": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@mermaid-js/parser/-/parser-1.1.1.tgz", + "integrity": "sha512-VuHdsYMK1bT6X2JbcAaWAhugTRvRBRyuZgd+c22swUeI9g/ntaxF7CY7dYarhZovofCbUNO0G7JesfmNtjYOCw==", + "license": "MIT", + "dependencies": { + "@chevrotain/types": "~11.1.1" + } + }, + "node_modules/@types/d3": { + "version": "7.4.3", + "resolved": "https://registry.npmjs.org/@types/d3/-/d3-7.4.3.tgz", + "integrity": "sha512-lZXZ9ckh5R8uiFVt8ogUNf+pIrK4EsWrx2Np75WvF/eTpJ0FMHNhjXk8CKEx/+gpHbNQyJWehbFaTvqmHWB3ww==", + "license": "MIT", + "dependencies": { + "@types/d3-array": "*", + "@types/d3-axis": "*", + "@types/d3-brush": "*", + "@types/d3-chord": "*", + "@types/d3-color": "*", + "@types/d3-contour": "*", + "@types/d3-delaunay": "*", + "@types/d3-dispatch": "*", + "@types/d3-drag": "*", + "@types/d3-dsv": "*", + "@types/d3-ease": "*", + "@types/d3-fetch": "*", + "@types/d3-force": "*", + "@types/d3-format": "*", + "@types/d3-geo": "*", + "@types/d3-hierarchy": "*", + "@types/d3-interpolate": "*", + "@types/d3-path": "*", + "@types/d3-polygon": "*", + "@types/d3-quadtree": "*", + "@types/d3-random": "*", + "@types/d3-scale": "*", + "@types/d3-scale-chromatic": "*", + "@types/d3-selection": "*", + "@types/d3-shape": "*", + "@types/d3-time": "*", + "@types/d3-time-format": "*", + "@types/d3-timer": "*", + "@types/d3-transition": "*", + "@types/d3-zoom": "*" + } + }, + "node_modules/@types/d3-array": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/@types/d3-array/-/d3-array-3.2.2.tgz", + "integrity": "sha512-hOLWVbm7uRza0BYXpIIW5pxfrKe0W+D5lrFiAEYR+pb6w3N2SwSMaJbXdUfSEv+dT4MfHBLtn5js0LAWaO6otw==", + "license": "MIT" + }, + "node_modules/@types/d3-axis": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@types/d3-axis/-/d3-axis-3.0.6.tgz", + "integrity": "sha512-pYeijfZuBd87T0hGn0FO1vQ/cgLk6E1ALJjfkC0oJ8cbwkZl3TpgS8bVBLZN+2jjGgg38epgxb2zmoGtSfvgMw==", + "license": "MIT", + "dependencies": { + "@types/d3-selection": "*" + } + }, + "node_modules/@types/d3-brush": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@types/d3-brush/-/d3-brush-3.0.6.tgz", + "integrity": "sha512-nH60IZNNxEcrh6L1ZSMNA28rj27ut/2ZmI3r96Zd+1jrZD++zD3LsMIjWlvg4AYrHn/Pqz4CF3veCxGjtbqt7A==", + "license": "MIT", + "dependencies": { + "@types/d3-selection": "*" + } + }, + "node_modules/@types/d3-chord": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@types/d3-chord/-/d3-chord-3.0.6.tgz", + "integrity": "sha512-LFYWWd8nwfwEmTZG9PfQxd17HbNPksHBiJHaKuY1XeqscXacsS2tyoo6OdRsjf+NQYeB6XrNL3a25E3gH69lcg==", + "license": "MIT" + }, + "node_modules/@types/d3-color": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/@types/d3-color/-/d3-color-3.1.3.tgz", + "integrity": "sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A==", + "license": "MIT" + }, + "node_modules/@types/d3-contour": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@types/d3-contour/-/d3-contour-3.0.6.tgz", + "integrity": "sha512-BjzLgXGnCWjUSYGfH1cpdo41/hgdWETu4YxpezoztawmqsvCeep+8QGfiY6YbDvfgHz/DkjeIkkZVJavB4a3rg==", + "license": "MIT", + "dependencies": { + "@types/d3-array": "*", + "@types/geojson": "*" + } + }, + "node_modules/@types/d3-delaunay": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/@types/d3-delaunay/-/d3-delaunay-6.0.4.tgz", + "integrity": "sha512-ZMaSKu4THYCU6sV64Lhg6qjf1orxBthaC161plr5KuPHo3CNm8DTHiLw/5Eq2b6TsNP0W0iJrUOFscY6Q450Hw==", + "license": "MIT" + }, + "node_modules/@types/d3-dispatch": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/@types/d3-dispatch/-/d3-dispatch-3.0.7.tgz", + "integrity": "sha512-5o9OIAdKkhN1QItV2oqaE5KMIiXAvDWBDPrD85e58Qlz1c1kI/J0NcqbEG88CoTwJrYe7ntUCVfeUl2UJKbWgA==", + "license": "MIT" + }, + "node_modules/@types/d3-drag": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/@types/d3-drag/-/d3-drag-3.0.7.tgz", + "integrity": "sha512-HE3jVKlzU9AaMazNufooRJ5ZpWmLIoc90A37WU2JMmeq28w1FQqCZswHZ3xR+SuxYftzHq6WU6KJHvqxKzTxxQ==", + "license": "MIT", + "dependencies": { + "@types/d3-selection": "*" + } + }, + "node_modules/@types/d3-dsv": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/@types/d3-dsv/-/d3-dsv-3.0.7.tgz", + "integrity": "sha512-n6QBF9/+XASqcKK6waudgL0pf/S5XHPPI8APyMLLUHd8NqouBGLsU8MgtO7NINGtPBtk9Kko/W4ea0oAspwh9g==", + "license": "MIT" + }, + "node_modules/@types/d3-ease": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/d3-ease/-/d3-ease-3.0.2.tgz", + "integrity": "sha512-NcV1JjO5oDzoK26oMzbILE6HW7uVXOHLQvHshBUW4UMdZGfiY6v5BeQwh9a9tCzv+CeefZQHJt5SRgK154RtiA==", + "license": "MIT" + }, + "node_modules/@types/d3-fetch": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/@types/d3-fetch/-/d3-fetch-3.0.7.tgz", + "integrity": "sha512-fTAfNmxSb9SOWNB9IoG5c8Hg6R+AzUHDRlsXsDZsNp6sxAEOP0tkP3gKkNSO/qmHPoBFTxNrjDprVHDQDvo5aA==", + "license": "MIT", + "dependencies": { + "@types/d3-dsv": "*" + } + }, + "node_modules/@types/d3-force": { + "version": "3.0.10", + "resolved": "https://registry.npmjs.org/@types/d3-force/-/d3-force-3.0.10.tgz", + "integrity": "sha512-ZYeSaCF3p73RdOKcjj+swRlZfnYpK1EbaDiYICEEp5Q6sUiqFaFQ9qgoshp5CzIyyb/yD09kD9o2zEltCexlgw==", + "license": "MIT" + }, + "node_modules/@types/d3-format": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/d3-format/-/d3-format-3.0.4.tgz", + "integrity": "sha512-fALi2aI6shfg7vM5KiR1wNJnZ7r6UuggVqtDA+xiEdPZQwy/trcQaHnwShLuLdta2rTymCNpxYTiMZX/e09F4g==", + "license": "MIT" + }, + "node_modules/@types/d3-geo": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@types/d3-geo/-/d3-geo-3.1.0.tgz", + "integrity": "sha512-856sckF0oP/diXtS4jNsiQw/UuK5fQG8l/a9VVLeSouf1/PPbBE1i1W852zVwKwYCBkFJJB7nCFTbk6UMEXBOQ==", + "license": "MIT", + "dependencies": { + "@types/geojson": "*" + } + }, + "node_modules/@types/d3-hierarchy": { + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/@types/d3-hierarchy/-/d3-hierarchy-3.1.7.tgz", + "integrity": "sha512-tJFtNoYBtRtkNysX1Xq4sxtjK8YgoWUNpIiUee0/jHGRwqvzYxkq0hGVbbOGSz+JgFxxRu4K8nb3YpG3CMARtg==", + "license": "MIT" + }, + "node_modules/@types/d3-interpolate": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/d3-interpolate/-/d3-interpolate-3.0.4.tgz", + "integrity": "sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA==", + "license": "MIT", + "dependencies": { + "@types/d3-color": "*" + } + }, + "node_modules/@types/d3-path": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@types/d3-path/-/d3-path-3.1.1.tgz", + "integrity": "sha512-VMZBYyQvbGmWyWVea0EHs/BwLgxc+MKi1zLDCONksozI4YJMcTt8ZEuIR4Sb1MMTE8MMW49v0IwI5+b7RmfWlg==", + "license": "MIT" + }, + "node_modules/@types/d3-polygon": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/d3-polygon/-/d3-polygon-3.0.2.tgz", + "integrity": "sha512-ZuWOtMaHCkN9xoeEMr1ubW2nGWsp4nIql+OPQRstu4ypeZ+zk3YKqQT0CXVe/PYqrKpZAi+J9mTs05TKwjXSRA==", + "license": "MIT" + }, + "node_modules/@types/d3-quadtree": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@types/d3-quadtree/-/d3-quadtree-3.0.6.tgz", + "integrity": "sha512-oUzyO1/Zm6rsxKRHA1vH0NEDG58HrT5icx/azi9MF1TWdtttWl0UIUsjEQBBh+SIkrpd21ZjEv7ptxWys1ncsg==", + "license": "MIT" + }, + "node_modules/@types/d3-random": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/d3-random/-/d3-random-3.0.3.tgz", + "integrity": "sha512-Imagg1vJ3y76Y2ea0871wpabqp613+8/r0mCLEBfdtqC7xMSfj9idOnmBYyMoULfHePJyxMAw3nWhJxzc+LFwQ==", + "license": "MIT" + }, + "node_modules/@types/d3-scale": { + "version": "4.0.9", + "resolved": "https://registry.npmjs.org/@types/d3-scale/-/d3-scale-4.0.9.tgz", + "integrity": "sha512-dLmtwB8zkAeO/juAMfnV+sItKjlsw2lKdZVVy6LRr0cBmegxSABiLEpGVmSJJ8O08i4+sGR6qQtb6WtuwJdvVw==", + "license": "MIT", + "dependencies": { + "@types/d3-time": "*" + } + }, + "node_modules/@types/d3-scale-chromatic": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@types/d3-scale-chromatic/-/d3-scale-chromatic-3.1.0.tgz", + "integrity": "sha512-iWMJgwkK7yTRmWqRB5plb1kadXyQ5Sj8V/zYlFGMUBbIPKQScw+Dku9cAAMgJG+z5GYDoMjWGLVOvjghDEFnKQ==", + "license": "MIT" + }, + "node_modules/@types/d3-selection": { + "version": "3.0.11", + "resolved": "https://registry.npmjs.org/@types/d3-selection/-/d3-selection-3.0.11.tgz", + "integrity": "sha512-bhAXu23DJWsrI45xafYpkQ4NtcKMwWnAC/vKrd2l+nxMFuvOT3XMYTIj2opv8vq8AO5Yh7Qac/nSeP/3zjTK0w==", + "license": "MIT" + }, + "node_modules/@types/d3-shape": { + "version": "3.1.8", + "resolved": "https://registry.npmjs.org/@types/d3-shape/-/d3-shape-3.1.8.tgz", + "integrity": "sha512-lae0iWfcDeR7qt7rA88BNiqdvPS5pFVPpo5OfjElwNaT2yyekbM0C9vK+yqBqEmHr6lDkRnYNoTBYlAgJa7a4w==", + "license": "MIT", + "dependencies": { + "@types/d3-path": "*" + } + }, + "node_modules/@types/d3-time": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/d3-time/-/d3-time-3.0.4.tgz", + "integrity": "sha512-yuzZug1nkAAaBlBBikKZTgzCeA+k1uy4ZFwWANOfKw5z5LRhV0gNA7gNkKm7HoK+HRN0wX3EkxGk0fpbWhmB7g==", + "license": "MIT" + }, + "node_modules/@types/d3-time-format": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/@types/d3-time-format/-/d3-time-format-4.0.3.tgz", + "integrity": "sha512-5xg9rC+wWL8kdDj153qZcsJ0FWiFt0J5RB6LYUNZjwSnesfblqrI/bJ1wBdJ8OQfncgbJG5+2F+qfqnqyzYxyg==", + "license": "MIT" + }, + "node_modules/@types/d3-timer": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/d3-timer/-/d3-timer-3.0.2.tgz", + "integrity": "sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw==", + "license": "MIT" + }, + "node_modules/@types/d3-transition": { + "version": "3.0.9", + "resolved": "https://registry.npmjs.org/@types/d3-transition/-/d3-transition-3.0.9.tgz", + "integrity": "sha512-uZS5shfxzO3rGlu0cC3bjmMFKsXv+SmZZcgp0KD22ts4uGXp5EVYGzu/0YdwZeKmddhcAccYtREJKkPfXkZuCg==", + "license": "MIT", + "dependencies": { + "@types/d3-selection": "*" + } + }, + "node_modules/@types/d3-zoom": { + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/@types/d3-zoom/-/d3-zoom-3.0.8.tgz", + "integrity": "sha512-iqMC4/YlFCSlO8+2Ii1GGGliCAY4XdeG748w5vQUbevlbDu0zSjH/+jojorQVBK/se0j6DUFNPBGSqD3YWYnDw==", + "license": "MIT", + "dependencies": { + "@types/d3-interpolate": "*", + "@types/d3-selection": "*" + } + }, + "node_modules/@types/geojson": { + "version": "7946.0.16", + "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.16.tgz", + "integrity": "sha512-6C8nqWur3j98U6+lXDfTUWIfgvZU+EumvpHKcYjujKH7woYyLj2sUmff0tRhrqM7BohUw7Pz3ZB1jj2gW9Fvmg==", + "license": "MIT" + }, + "node_modules/@types/trusted-types": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz", + "integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==", + "license": "MIT", + "optional": true + }, + "node_modules/@upsetjs/venn.js": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@upsetjs/venn.js/-/venn.js-2.0.0.tgz", + "integrity": "sha512-WbBhLrooyePuQ1VZxrJjtLvTc4NVfpOyKx0sKqioq9bX1C1m7Jgykkn8gLrtwumBioXIqam8DLxp88Adbue6Hw==", + "license": "MIT", + "optionalDependencies": { + "d3-selection": "^3.0.0", + "d3-transition": "^3.0.1" + } + }, + "node_modules/bidi-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/bidi-js/-/bidi-js-1.0.3.tgz", + "integrity": "sha512-RKshQI1R3YQ+n9YJz2QQ147P66ELpa1FQEg20Dk8oW9t2KgLbpDLLp9aGZ7y8WHSshDknG0bknqGw5/tyCs5tw==", + "license": "MIT", + "dependencies": { + "require-from-string": "^2.0.2" + } + }, + "node_modules/commander": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", + "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", + "license": "MIT", + "engines": { + "node": ">= 10" + } + }, + "node_modules/cose-base": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/cose-base/-/cose-base-1.0.3.tgz", + "integrity": "sha512-s9whTXInMSgAp/NVXVNuVxVKzGH2qck3aQlVHxDCdAEPgtMKwc4Wq6/QKhgdEdgbLSi9rBTAcPoRa6JpiG4ksg==", + "license": "MIT", + "dependencies": { + "layout-base": "^1.0.0" + } + }, + "node_modules/css-tree": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-3.2.1.tgz", + "integrity": "sha512-X7sjQzceUhu1u7Y/ylrRZFU2FS6LRiFVp6rKLPg23y3x3c3DOKAwuXGDp+PAGjh6CSnCjYeAul8pcT8bAl+lSA==", + "license": "MIT", + "dependencies": { + "mdn-data": "2.27.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0" + } + }, + "node_modules/cytoscape": { + "version": "3.34.0", + "resolved": "https://registry.npmjs.org/cytoscape/-/cytoscape-3.34.0.tgz", + "integrity": "sha512-62rNSrioXw93uliKFBwjukeQyeWwH2PqDrTac31r2P6464u3AUvTk0xS4LVvT251g7IgkFunrI48ZEZGjywSOg==", + "license": "MIT", + "engines": { + "node": ">=0.10" + } + }, + "node_modules/cytoscape-cose-bilkent": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/cytoscape-cose-bilkent/-/cytoscape-cose-bilkent-4.1.0.tgz", + "integrity": "sha512-wgQlVIUJF13Quxiv5e1gstZ08rnZj2XaLHGoFMYXz7SkNfCDOOteKBE6SYRfA9WxxI/iBc3ajfDoc6hb/MRAHQ==", + "license": "MIT", + "dependencies": { + "cose-base": "^1.0.0" + }, + "peerDependencies": { + "cytoscape": "^3.2.0" + } + }, + "node_modules/cytoscape-fcose": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/cytoscape-fcose/-/cytoscape-fcose-2.2.0.tgz", + "integrity": "sha512-ki1/VuRIHFCzxWNrsshHYPs6L7TvLu3DL+TyIGEsRcvVERmxokbf5Gdk7mFxZnTdiGtnA4cfSmjZJMviqSuZrQ==", + "license": "MIT", + "dependencies": { + "cose-base": "^2.2.0" + }, + "peerDependencies": { + "cytoscape": "^3.2.0" + } + }, + "node_modules/cytoscape-fcose/node_modules/cose-base": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/cose-base/-/cose-base-2.2.0.tgz", + "integrity": "sha512-AzlgcsCbUMymkADOJtQm3wO9S3ltPfYOFD5033keQn9NJzIbtnZj+UdBJe7DYml/8TdbtHJW3j58SOnKhWY/5g==", + "license": "MIT", + "dependencies": { + "layout-base": "^2.0.0" + } + }, + "node_modules/cytoscape-fcose/node_modules/layout-base": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/layout-base/-/layout-base-2.0.1.tgz", + "integrity": "sha512-dp3s92+uNI1hWIpPGH3jK2kxE2lMjdXdr+DH8ynZHpd6PUlH6x6cbuXnoMmiNumznqaNO31xu9e79F0uuZ0JFg==", + "license": "MIT" + }, + "node_modules/d3": { + "version": "7.9.0", + "resolved": "https://registry.npmjs.org/d3/-/d3-7.9.0.tgz", + "integrity": "sha512-e1U46jVP+w7Iut8Jt8ri1YsPOvFpg46k+K8TpCb0P+zjCkjkPnV7WzfDJzMHy1LnA+wj5pLT1wjO901gLXeEhA==", + "license": "ISC", + "dependencies": { + "d3-array": "3", + "d3-axis": "3", + "d3-brush": "3", + "d3-chord": "3", + "d3-color": "3", + "d3-contour": "4", + "d3-delaunay": "6", + "d3-dispatch": "3", + "d3-drag": "3", + "d3-dsv": "3", + "d3-ease": "3", + "d3-fetch": "3", + "d3-force": "3", + "d3-format": "3", + "d3-geo": "3", + "d3-hierarchy": "3", + "d3-interpolate": "3", + "d3-path": "3", + "d3-polygon": "3", + "d3-quadtree": "3", + "d3-random": "3", + "d3-scale": "4", + "d3-scale-chromatic": "3", + "d3-selection": "3", + "d3-shape": "3", + "d3-time": "3", + "d3-time-format": "4", + "d3-timer": "3", + "d3-transition": "3", + "d3-zoom": "3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-array": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-3.2.4.tgz", + "integrity": "sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==", + "license": "ISC", + "dependencies": { + "internmap": "1 - 2" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-axis": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-axis/-/d3-axis-3.0.0.tgz", + "integrity": "sha512-IH5tgjV4jE/GhHkRV0HiVYPDtvfjHQlQfJHs0usq7M30XcSBvOotpmH1IgkcXsO/5gEQZD43B//fc7SRT5S+xw==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-brush": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-brush/-/d3-brush-3.0.0.tgz", + "integrity": "sha512-ALnjWlVYkXsVIGlOsuWH1+3udkYFI48Ljihfnh8FZPF2QS9o+PzGLBslO0PjzVoHLZ2KCVgAM8NVkXPJB2aNnQ==", + "license": "ISC", + "dependencies": { + "d3-dispatch": "1 - 3", + "d3-drag": "2 - 3", + "d3-interpolate": "1 - 3", + "d3-selection": "3", + "d3-transition": "3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-chord": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-chord/-/d3-chord-3.0.1.tgz", + "integrity": "sha512-VE5S6TNa+j8msksl7HwjxMHDM2yNK3XCkusIlpX5kwauBfXuyLAtNg9jCp/iHH61tgI4sb6R/EIMWCqEIdjT/g==", + "license": "ISC", + "dependencies": { + "d3-path": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-color": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-3.1.0.tgz", + "integrity": "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-contour": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/d3-contour/-/d3-contour-4.0.2.tgz", + "integrity": "sha512-4EzFTRIikzs47RGmdxbeUvLWtGedDUNkTcmzoeyg4sP/dvCexO47AaQL7VKy/gul85TOxw+IBgA8US2xwbToNA==", + "license": "ISC", + "dependencies": { + "d3-array": "^3.2.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-delaunay": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/d3-delaunay/-/d3-delaunay-6.0.4.tgz", + "integrity": "sha512-mdjtIZ1XLAM8bm/hx3WwjfHt6Sggek7qH043O8KEjDXN40xi3vx/6pYSVTwLjEgiXQTbvaouWKynLBiUZ6SK6A==", + "license": "ISC", + "dependencies": { + "delaunator": "5" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-dispatch": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-dispatch/-/d3-dispatch-3.0.1.tgz", + "integrity": "sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-drag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-drag/-/d3-drag-3.0.0.tgz", + "integrity": "sha512-pWbUJLdETVA8lQNJecMxoXfH6x+mO2UQo8rSmZ+QqxcbyA3hfeprFgIT//HW2nlHChWeIIMwS2Fq+gEARkhTkg==", + "license": "ISC", + "dependencies": { + "d3-dispatch": "1 - 3", + "d3-selection": "3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-dsv": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-dsv/-/d3-dsv-3.0.1.tgz", + "integrity": "sha512-UG6OvdI5afDIFP9w4G0mNq50dSOsXHJaRE8arAS5o9ApWnIElp8GZw1Dun8vP8OyHOZ/QJUKUJwxiiCCnUwm+Q==", + "license": "ISC", + "dependencies": { + "commander": "7", + "iconv-lite": "0.6", + "rw": "1" + }, + "bin": { + "csv2json": "bin/dsv2json.js", + "csv2tsv": "bin/dsv2dsv.js", + "dsv2dsv": "bin/dsv2dsv.js", + "dsv2json": "bin/dsv2json.js", + "json2csv": "bin/json2dsv.js", + "json2dsv": "bin/json2dsv.js", + "json2tsv": "bin/json2dsv.js", + "tsv2csv": "bin/dsv2dsv.js", + "tsv2json": "bin/dsv2json.js" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-ease": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-3.0.1.tgz", + "integrity": "sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-fetch": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-fetch/-/d3-fetch-3.0.1.tgz", + "integrity": "sha512-kpkQIM20n3oLVBKGg6oHrUchHM3xODkTzjMoj7aWQFq5QEM+R6E4WkzT5+tojDY7yjez8KgCBRoj4aEr99Fdqw==", + "license": "ISC", + "dependencies": { + "d3-dsv": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-force": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-force/-/d3-force-3.0.0.tgz", + "integrity": "sha512-zxV/SsA+U4yte8051P4ECydjD/S+qeYtnaIyAs9tgHCqfguma/aAQDjo85A9Z6EKhBirHRJHXIgJUlffT4wdLg==", + "license": "ISC", + "dependencies": { + "d3-dispatch": "1 - 3", + "d3-quadtree": "1 - 3", + "d3-timer": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-format": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-3.1.2.tgz", + "integrity": "sha512-AJDdYOdnyRDV5b6ArilzCPPwc1ejkHcoyFarqlPqT7zRYjhavcT3uSrqcMvsgh2CgoPbK3RCwyHaVyxYcP2Arg==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-geo": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/d3-geo/-/d3-geo-3.1.1.tgz", + "integrity": "sha512-637ln3gXKXOwhalDzinUgY83KzNWZRKbYubaG+fGVuc/dxO64RRljtCTnf5ecMyE1RIdtqpkVcq0IbtU2S8j2Q==", + "license": "ISC", + "dependencies": { + "d3-array": "2.5.0 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-hierarchy": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/d3-hierarchy/-/d3-hierarchy-3.1.2.tgz", + "integrity": "sha512-FX/9frcub54beBdugHjDCdikxThEqjnR93Qt7PvQTOHxyiNCAlvMrHhclk3cD5VeAaq9fxmfRp+CnWw9rEMBuA==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-interpolate": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-3.0.1.tgz", + "integrity": "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==", + "license": "ISC", + "dependencies": { + "d3-color": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-path": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-3.1.0.tgz", + "integrity": "sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-polygon": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-polygon/-/d3-polygon-3.0.1.tgz", + "integrity": "sha512-3vbA7vXYwfe1SYhED++fPUQlWSYTTGmFmQiany/gdbiWgU/iEyQzyymwL9SkJjFFuCS4902BSzewVGsHHmHtXg==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-quadtree": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-quadtree/-/d3-quadtree-3.0.1.tgz", + "integrity": "sha512-04xDrxQTDTCFwP5H6hRhsRcb9xxv2RzkcsygFzmkSIOJy3PeRJP7sNk3VRIbKXcog561P9oU0/rVH6vDROAgUw==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-random": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-random/-/d3-random-3.0.1.tgz", + "integrity": "sha512-FXMe9GfxTxqd5D6jFsQ+DJ8BJS4E/fT5mqqdjovykEB2oFbTMDVdg1MGFxfQW+FBOGoB++k8swBrgwSHT1cUXQ==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-sankey": { + "version": "0.12.3", + "resolved": "https://registry.npmjs.org/d3-sankey/-/d3-sankey-0.12.3.tgz", + "integrity": "sha512-nQhsBRmM19Ax5xEIPLMY9ZmJ/cDvd1BG3UVvt5h3WRxKg5zGRbvnteTyWAbzeSvlh3tW7ZEmq4VwR5mB3tutmQ==", + "license": "BSD-3-Clause", + "dependencies": { + "d3-array": "1 - 2", + "d3-shape": "^1.2.0" + } + }, + "node_modules/d3-sankey/node_modules/d3-array": { + "version": "2.12.1", + "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-2.12.1.tgz", + "integrity": "sha512-B0ErZK/66mHtEsR1TkPEEkwdy+WDesimkM5gpZr5Dsg54BiTA5RXtYW5qTLIAcekaS9xfZrzBLF/OAkB3Qn1YQ==", + "license": "BSD-3-Clause", + "dependencies": { + "internmap": "^1.0.0" + } + }, + "node_modules/d3-sankey/node_modules/d3-path": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-1.0.9.tgz", + "integrity": "sha512-VLaYcn81dtHVTjEHd8B+pbe9yHWpXKZUC87PzoFmsFrJqgFwDe/qxfp5MlfsfM1V5E/iVt0MmEbWQ7FVIXh/bg==", + "license": "BSD-3-Clause" + }, + "node_modules/d3-sankey/node_modules/d3-shape": { + "version": "1.3.7", + "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-1.3.7.tgz", + "integrity": "sha512-EUkvKjqPFUAZyOlhY5gzCxCeI0Aep04LwIRpsZ/mLFelJiUfnK56jo5JMDSE7yyP2kLSb6LtF+S5chMk7uqPqw==", + "license": "BSD-3-Clause", + "dependencies": { + "d3-path": "1" + } + }, + "node_modules/d3-sankey/node_modules/internmap": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/internmap/-/internmap-1.0.1.tgz", + "integrity": "sha512-lDB5YccMydFBtasVtxnZ3MRBHuaoE8GKsppq+EchKL2U4nK/DmEpPHNH8MZe5HkMtpSiTSOZwfN0tzYjO/lJEw==", + "license": "ISC" + }, + "node_modules/d3-scale": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-4.0.2.tgz", + "integrity": "sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==", + "license": "ISC", + "dependencies": { + "d3-array": "2.10.0 - 3", + "d3-format": "1 - 3", + "d3-interpolate": "1.2.0 - 3", + "d3-time": "2.1.1 - 3", + "d3-time-format": "2 - 4" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-scale-chromatic": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-scale-chromatic/-/d3-scale-chromatic-3.1.0.tgz", + "integrity": "sha512-A3s5PWiZ9YCXFye1o246KoscMWqf8BsD9eRiJ3He7C9OBaxKhAd5TFCdEx/7VbKtxxTsu//1mMJFrEt572cEyQ==", + "license": "ISC", + "dependencies": { + "d3-color": "1 - 3", + "d3-interpolate": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-selection": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-3.0.0.tgz", + "integrity": "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-shape": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-3.2.0.tgz", + "integrity": "sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==", + "license": "ISC", + "dependencies": { + "d3-path": "^3.1.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-time": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-3.1.0.tgz", + "integrity": "sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==", + "license": "ISC", + "dependencies": { + "d3-array": "2 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-time-format": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-4.1.0.tgz", + "integrity": "sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==", + "license": "ISC", + "dependencies": { + "d3-time": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-timer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-3.0.1.tgz", + "integrity": "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-transition": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-transition/-/d3-transition-3.0.1.tgz", + "integrity": "sha512-ApKvfjsSR6tg06xrL434C0WydLr7JewBB3V+/39RMHsaXTOG0zmt/OAXeng5M5LBm0ojmxJrpomQVZ1aPvBL4w==", + "license": "ISC", + "dependencies": { + "d3-color": "1 - 3", + "d3-dispatch": "1 - 3", + "d3-ease": "1 - 3", + "d3-interpolate": "1 - 3", + "d3-timer": "1 - 3" + }, + "engines": { + "node": ">=12" + }, + "peerDependencies": { + "d3-selection": "2 - 3" + } + }, + "node_modules/d3-zoom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-zoom/-/d3-zoom-3.0.0.tgz", + "integrity": "sha512-b8AmV3kfQaqWAuacbPuNbL6vahnOJflOhexLzMMNLga62+/nh0JzvJ0aO/5a5MVgUFGS7Hu1P9P03o3fJkDCyw==", + "license": "ISC", + "dependencies": { + "d3-dispatch": "1 - 3", + "d3-drag": "2 - 3", + "d3-interpolate": "1 - 3", + "d3-selection": "2 - 3", + "d3-transition": "2 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/dagre-d3-es": { + "version": "7.0.14", + "resolved": "https://registry.npmjs.org/dagre-d3-es/-/dagre-d3-es-7.0.14.tgz", + "integrity": "sha512-P4rFMVq9ESWqmOgK+dlXvOtLwYg0i7u0HBGJER0LZDJT2VHIPAMZ/riPxqJceWMStH5+E61QxFra9kIS3AqdMg==", + "license": "MIT", + "dependencies": { + "d3": "^7.9.0", + "lodash-es": "^4.17.21" + } + }, + "node_modules/data-urls": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-7.0.0.tgz", + "integrity": "sha512-23XHcCF+coGYevirZceTVD7NdJOqVn+49IHyxgszm+JIiHLoB2TkmPtsYkNWT1pvRSGkc35L6NHs0yHkN2SumA==", + "license": "MIT", + "dependencies": { + "whatwg-mimetype": "^5.0.0", + "whatwg-url": "^16.0.0" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0" + } + }, + "node_modules/dayjs": { + "version": "1.11.21", + "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.21.tgz", + "integrity": "sha512-98IT+HOahAisibz/yjKbzuOBwYcjJ7BCLPzARyHiyEBmRz4fatF+KPJszEHXsGYjUG234aH/cOjW1wwTbKUZlA==", + "license": "MIT" + }, + "node_modules/decimal.js": { + "version": "10.6.0", + "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.6.0.tgz", + "integrity": "sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==", + "license": "MIT" + }, + "node_modules/delaunator": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/delaunator/-/delaunator-5.1.0.tgz", + "integrity": "sha512-AGrQ4QSgssa1NGmWmLPqN5NY2KajF5MqxetNEO+o0n3ZwZZeTmt7bBnvzHWrmkZFxGgr4HdyFgelzgi06otLuQ==", + "license": "ISC", + "dependencies": { + "robust-predicates": "^3.0.2" + } + }, + "node_modules/dompurify": { + "version": "3.4.11", + "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.4.11.tgz", + "integrity": "sha512-zhlUV12GsaRzMsf9q5M254YhA4+VuF0fG+QFqu6aYpoGlKtz+w8//jBcGVYBgQkR5GHjUomejY84AV+/uPbWdw==", + "license": "(MPL-2.0 OR Apache-2.0)", + "optionalDependencies": { + "@types/trusted-types": "^2.0.7" + } + }, + "node_modules/entities": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-8.0.0.tgz", + "integrity": "sha512-zwfzJecQ/Uej6tusMqwAqU/6KL2XaB2VZ2Jg54Je6ahNBGNH6Ek6g3jjNCF0fG9EWQKGZNddNjU5F1ZQn/sBnA==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=20.19.0" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/es-toolkit": { + "version": "1.47.1", + "resolved": "https://registry.npmjs.org/es-toolkit/-/es-toolkit-1.47.1.tgz", + "integrity": "sha512-5RAqEwf4P4E17p+W75KLOWw/nOvKZzSQpxM32IpI2KZLaVonjTrZ0Ai5ghMaVI9eKC2p8eoQgcBdkEDgzFk6+Q==", + "license": "MIT", + "workspaces": [ + "docs", + "benchmarks" + ] + }, + "node_modules/hachure-fill": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/hachure-fill/-/hachure-fill-0.5.2.tgz", + "integrity": "sha512-3GKBOn+m2LX9iq+JC1064cSFprJY4jL1jCXTcpnfER5HYE2l/4EfWSGzkPa/ZDBmYI0ZOEj5VHV/eKnPGkHuOg==", + "license": "MIT" + }, + "node_modules/html-encoding-sniffer": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-6.0.0.tgz", + "integrity": "sha512-CV9TW3Y3f8/wT0BRFc1/KAVQ3TUHiXmaAb6VW9vtiMFf7SLoMd1PdAc4W3KFOFETBJUb90KatHqlsZMWV+R9Gg==", + "license": "MIT", + "dependencies": { + "@exodus/bytes": "^1.6.0" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0" + } + }, + "node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/import-meta-resolve": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/import-meta-resolve/-/import-meta-resolve-4.2.0.tgz", + "integrity": "sha512-Iqv2fzaTQN28s/FwZAoFq0ZSs/7hMAHJVX+w8PZl3cY19Pxk6jFFalxQoIfW2826i/fDLXv8IiEZRIT0lDuWcg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/internmap": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/internmap/-/internmap-2.0.3.tgz", + "integrity": "sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/is-potential-custom-element-name": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz", + "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==", + "license": "MIT" + }, + "node_modules/jsdom": { + "version": "29.1.1", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-29.1.1.tgz", + "integrity": "sha512-ECi4Fi2f7BdJtUKTflYRTiaMxIB0O6zfR1fX0GXpUrf6flp8QIYn1UT20YQqdSOfk2dfkCwS8LAFoJDEppNK5Q==", + "license": "MIT", + "dependencies": { + "@asamuzakjp/css-color": "^5.1.11", + "@asamuzakjp/dom-selector": "^7.1.1", + "@bramus/specificity": "^2.4.2", + "@csstools/css-syntax-patches-for-csstree": "^1.1.3", + "@exodus/bytes": "^1.15.0", + "css-tree": "^3.2.1", + "data-urls": "^7.0.0", + "decimal.js": "^10.6.0", + "html-encoding-sniffer": "^6.0.0", + "is-potential-custom-element-name": "^1.0.1", + "lru-cache": "^11.3.5", + "parse5": "^8.0.1", + "saxes": "^6.0.0", + "symbol-tree": "^3.2.4", + "tough-cookie": "^6.0.1", + "undici": "^7.25.0", + "w3c-xmlserializer": "^5.0.0", + "webidl-conversions": "^8.0.1", + "whatwg-mimetype": "^5.0.0", + "whatwg-url": "^16.0.1", + "xml-name-validator": "^5.0.0" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24.0.0" + }, + "peerDependencies": { + "canvas": "^3.0.0" + }, + "peerDependenciesMeta": { + "canvas": { + "optional": true + } + } + }, + "node_modules/katex": { + "version": "0.16.47", + "resolved": "https://registry.npmjs.org/katex/-/katex-0.16.47.tgz", + "integrity": "sha512-Eeo8Ys1doU1z+x8AZsPpQu+p/QcZBI5PeOo7QGQdy2x2m0MU/hYagBbGOmXwr5KVbEfVuWv9LpnQWeehogurjg==", + "funding": [ + "https://opencollective.com/katex", + "https://github.com/sponsors/katex" + ], + "license": "MIT", + "dependencies": { + "commander": "^8.3.0" + }, + "bin": { + "katex": "cli.js" + } + }, + "node_modules/katex/node_modules/commander": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz", + "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==", + "license": "MIT", + "engines": { + "node": ">= 12" + } + }, + "node_modules/khroma": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/khroma/-/khroma-2.1.0.tgz", + "integrity": "sha512-Ls993zuzfayK269Svk9hzpeGUKob/sIgZzyHYdjQoAdQetRKpOLj+k/QQQ/6Qi0Yz65mlROrfd+Ev+1+7dz9Kw==" + }, + "node_modules/layout-base": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/layout-base/-/layout-base-1.0.2.tgz", + "integrity": "sha512-8h2oVEZNktL4BH2JCOI90iD1yXwL6iNW7KcCKT2QZgQJR2vbqDsldCTPRU9NifTCqHZci57XvQQ15YTu+sTYPg==", + "license": "MIT" + }, + "node_modules/lodash-es": { + "version": "4.18.1", + "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.18.1.tgz", + "integrity": "sha512-J8xewKD/Gk22OZbhpOVSwcs60zhd95ESDwezOFuA3/099925PdHJ7OFHNTGtajL3AlZkykD32HykiMo+BIBI8A==", + "license": "MIT" + }, + "node_modules/lru-cache": { + "version": "11.5.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.5.1.tgz", + "integrity": "sha512-RPimw/7aMdv2oqRrxKwvZXcPfwBrn/JZ2xYcY9Hus/6LaS3VOAKVWKWgNLCFSiOm1ESXinjsDlidVU7JlnCN2A==", + "license": "BlueOak-1.0.0", + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/marked": { + "version": "16.4.2", + "resolved": "https://registry.npmjs.org/marked/-/marked-16.4.2.tgz", + "integrity": "sha512-TI3V8YYWvkVf3KJe1dRkpnjs68JUPyEa5vjKrp1XEEJUAOaQc+Qj+L1qWbPd0SJuAdQkFU0h73sXXqwDYxsiDA==", + "license": "MIT", + "bin": { + "marked": "bin/marked.js" + }, + "engines": { + "node": ">= 20" + } + }, + "node_modules/mdn-data": { + "version": "2.27.1", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.27.1.tgz", + "integrity": "sha512-9Yubnt3e8A0OKwxYSXyhLymGW4sCufcLG6VdiDdUGVkPhpqLxlvP5vl1983gQjJl3tqbrM731mjaZaP68AgosQ==", + "license": "CC0-1.0" + }, + "node_modules/mermaid": { + "version": "11.15.0", + "resolved": "https://registry.npmjs.org/mermaid/-/mermaid-11.15.0.tgz", + "integrity": "sha512-pTMbcf3rWdtLiYGpmoTjHEpeY8seiy6sR+9nD7LOs8KfUbHE4lOUAprTRqRAcWSQ6MQpdX+YEsxShtGsINtPtw==", + "license": "MIT", + "dependencies": { + "@braintree/sanitize-url": "^7.1.1", + "@iconify/utils": "^3.0.2", + "@mermaid-js/parser": "^1.1.1", + "@types/d3": "^7.4.3", + "@upsetjs/venn.js": "^2.0.0", + "cytoscape": "^3.33.1", + "cytoscape-cose-bilkent": "^4.1.0", + "cytoscape-fcose": "^2.2.0", + "d3": "^7.9.0", + "d3-sankey": "^0.12.3", + "dagre-d3-es": "7.0.14", + "dayjs": "^1.11.19", + "dompurify": "^3.3.1", + "es-toolkit": "^1.45.1", + "katex": "^0.16.25", + "khroma": "^2.1.0", + "marked": "^16.3.0", + "roughjs": "^4.6.6", + "stylis": "^4.3.6", + "ts-dedent": "^2.2.0", + "uuid": "^11.1.0 || ^12 || ^13 || ^14.0.0" + } + }, + "node_modules/package-manager-detector": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/package-manager-detector/-/package-manager-detector-1.6.0.tgz", + "integrity": "sha512-61A5ThoTiDG/C8s8UMZwSorAGwMJ0ERVGj2OjoW5pAalsNOg15+iQiPzrLJ4jhZ1HJzmC2PIHT2oEiH3R5fzNA==", + "license": "MIT" + }, + "node_modules/parse5": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-8.0.1.tgz", + "integrity": "sha512-z1e/HMG90obSGeidlli3hj7cbocou0/wa5HacvI3ASx34PecNjNQeaHNo5WIZpWofN9kgkqV1q5YvXe3F0FoPw==", + "license": "MIT", + "dependencies": { + "entities": "^8.0.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, + "node_modules/path-data-parser": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/path-data-parser/-/path-data-parser-0.1.0.tgz", + "integrity": "sha512-NOnmBpt5Y2RWbuv0LMzsayp3lVylAHLPUTut412ZA3l+C4uw4ZVkQbjShYCQ8TCpUMdPapr4YjUqLYD6v68j+w==", + "license": "MIT" + }, + "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==", + "license": "MIT" + }, + "node_modules/points-on-path": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/points-on-path/-/points-on-path-0.2.1.tgz", + "integrity": "sha512-25ClnWWuw7JbWZcgqY/gJ4FQWadKxGWk+3kR/7kD0tCaDtPPMj7oHu2ToLaVhfpnHrZzYby2w6tUA0eOIuUg8g==", + "license": "MIT", + "dependencies": { + "path-data-parser": "0.1.0", + "points-on-curve": "0.2.0" + } + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "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/robust-predicates": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/robust-predicates/-/robust-predicates-3.0.3.tgz", + "integrity": "sha512-NS3levdsRIUOmiJ8FZWCP7LG3QpJyrs/TE0Zpf1yvZu8cAJJ6QMW92H1c7kWpdIHo8RvmLxN/o2JXTKHp74lUA==", + "license": "Unlicense" + }, + "node_modules/roughjs": { + "version": "4.6.6", + "resolved": "https://registry.npmjs.org/roughjs/-/roughjs-4.6.6.tgz", + "integrity": "sha512-ZUz/69+SYpFN/g/lUlo2FXcIjRkSu3nDarreVdGGndHEBJ6cXPdKguS8JGxwj5HA5xIbVKSmLgr5b3AWxtRfvQ==", + "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/rw": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/rw/-/rw-1.3.3.tgz", + "integrity": "sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ==", + "license": "BSD-3-Clause" + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "license": "MIT" + }, + "node_modules/saxes": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/saxes/-/saxes-6.0.0.tgz", + "integrity": "sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==", + "license": "ISC", + "dependencies": { + "xmlchars": "^2.2.0" + }, + "engines": { + "node": ">=v12.22.7" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/stylis": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.4.0.tgz", + "integrity": "sha512-5Z9ZpRzfuH6l/UAvCPAPUo3665Nk2wLaZU3x+TLHKVzIz33+sbJqbtrYoC3KD4/uVOr2Zp+L0LySezP9OHV9yA==", + "license": "MIT" + }, + "node_modules/symbol-tree": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", + "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==", + "license": "MIT" + }, + "node_modules/tinyexec": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.2.4.tgz", + "integrity": "sha512-SHf/r48b7vOrjve9PxJo3MN5v5yuyjHvdUcrQffT3WXMUfnGmHDVbC4k3sHJaJTgZCwpUplIaAo5ANtMyp3YHg==", + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/tldts": { + "version": "7.4.3", + "resolved": "https://registry.npmjs.org/tldts/-/tldts-7.4.3.tgz", + "integrity": "sha512-A3BDQBeeukYPzB4QdQ1DtdlUmp4x2OCH8n5UVhEWbyANxNep8GavottKzd1xYKFJKjUgMyPT7EzOfnBO55s8Sg==", + "license": "MIT", + "dependencies": { + "tldts-core": "^7.4.3" + }, + "bin": { + "tldts": "bin/cli.js" + } + }, + "node_modules/tldts-core": { + "version": "7.4.3", + "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-7.4.3.tgz", + "integrity": "sha512-27ep5H9PzdBrNd5OFM/j3WCU8F3kPwM9D0BOaOf7uYfxMJfyr0K5Tjj69Gri+sZlh2WXd5buIm47NuPF29CDiw==", + "license": "MIT" + }, + "node_modules/tough-cookie": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-6.0.1.tgz", + "integrity": "sha512-LktZQb3IeoUWB9lqR5EWTHgW/VTITCXg4D21M+lvybRVdylLrRMnqaIONLVb5mav8vM19m44HIcGq4qASeu2Qw==", + "license": "BSD-3-Clause", + "dependencies": { + "tldts": "^7.0.5" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/tr46": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-6.0.0.tgz", + "integrity": "sha512-bLVMLPtstlZ4iMQHpFHTR7GAGj2jxi8Dg0s2h2MafAE4uSWF98FC/3MomU51iQAMf8/qDUbKWf5GxuvvVcXEhw==", + "license": "MIT", + "dependencies": { + "punycode": "^2.3.1" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/ts-dedent": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/ts-dedent/-/ts-dedent-2.3.0.tgz", + "integrity": "sha512-JfJeIHke7y2egdGGgRAvpCwYFUsHlM2gPcrVOxFkznt/4uzQ7HFmvE63iFHVLBJNDuyDOQgijDK/tXH/f6Msjg==", + "license": "MIT", + "engines": { + "node": ">=6.10" + } + }, + "node_modules/undici": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/undici/-/undici-7.28.0.tgz", + "integrity": "sha512-cRZYrTDwWznlnRiPjggAGxZXanty6M8RV1ff8Wm4LWXBp7/IG8v5DnOm74DtUBp9OONpK75YlPnIjQqX0dBDtA==", + "license": "MIT", + "engines": { + "node": ">=20.18.1" + } + }, + "node_modules/uuid": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-14.0.0.tgz", + "integrity": "sha512-Qo+uWgilfSmAhXCMav1uYFynlQO7fMFiMVZsQqZRMIXp0O7rR7qjkj+cPvBHLgBqi960QCoo/PH2/6ZtVqKvrg==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", + "bin": { + "uuid": "dist-node/bin/uuid" + } + }, + "node_modules/w3c-xmlserializer": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-5.0.0.tgz", + "integrity": "sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==", + "license": "MIT", + "dependencies": { + "xml-name-validator": "^5.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/webidl-conversions": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-8.0.1.tgz", + "integrity": "sha512-BMhLD/Sw+GbJC21C/UgyaZX41nPt8bUTg+jWyDeg7e7YN4xOM05YPSIXceACnXVtqyEw/LMClUQMtMZ+PGGpqQ==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=20" + } + }, + "node_modules/whatwg-mimetype": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-5.0.0.tgz", + "integrity": "sha512-sXcNcHOC51uPGF0P/D4NVtrkjSU2fNsm9iog4ZvZJsL3rjoDAzXZhkm2MWt1y+PUdggKAYVoMAIYcs78wJ51Cw==", + "license": "MIT", + "engines": { + "node": ">=20" + } + }, + "node_modules/whatwg-url": { + "version": "16.0.1", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-16.0.1.tgz", + "integrity": "sha512-1to4zXBxmXHV3IiSSEInrreIlu02vUOvrhxJJH5vcxYTBDAx51cqZiKdyTxlecdKNSjj8EcxGBxNf6Vg+945gw==", + "license": "MIT", + "dependencies": { + "@exodus/bytes": "^1.11.0", + "tr46": "^6.0.0", + "webidl-conversions": "^8.0.1" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0" + } + }, + "node_modules/xml-name-validator": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-5.0.0.tgz", + "integrity": "sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==", + "license": "Apache-2.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/xmlchars": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", + "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==", + "license": "MIT" + } + } +} diff --git a/skills/c4-diagram/scripts/package.json b/skills/c4-diagram/scripts/package.json new file mode 100644 index 0000000..f73e7b7 --- /dev/null +++ b/skills/c4-diagram/scripts/package.json @@ -0,0 +1,10 @@ +{ + "name": "c4-diagram-scripts", + "version": "0.1.0", + "private": true, + "type": "module", + "dependencies": { + "jsdom": "^26.1.0", + "mermaid": "^11.4.0" + } +} diff --git a/skills/c4-diagram/scripts/validate_mermaid.mjs b/skills/c4-diagram/scripts/validate_mermaid.mjs new file mode 100644 index 0000000..f8f9d65 --- /dev/null +++ b/skills/c4-diagram/scripts/validate_mermaid.mjs @@ -0,0 +1,146 @@ +#!/usr/bin/env node +/** + * Validate Mermaid diagram syntax. + * + * Usage: + * node scripts/validate_mermaid.mjs + * node scripts/validate_mermaid.mjs - # read from stdin + * echo '...' | node scripts/validate_mermaid.mjs + * + * Exit codes: + * 0 — valid syntax + * 1 — syntax error (details on stderr) + * 2 — banned C4* keyword detected (warning on stderr, still validates syntax) + * 3 — usage error + * + * Options: + * --help Show this help + * --strict Treat banned-keyword warnings as errors (exit 2) + */ + +import { readFileSync } from "node:fs"; +import { resolve } from "node:path"; + +// Mermaid requires a DOM-like environment for initialization. +// We use a minimal shim — parse() doesn't actually render SVG. +import { JSDOM } from "jsdom"; +const dom = new JSDOM(""); +globalThis.window = dom.window; +globalThis.document = dom.window.document; +Object.defineProperty(globalThis, "navigator", { + value: dom.window.navigator, + writable: true, + configurable: true, +}); + +const { default: mermaid } = await import("mermaid"); + +mermaid.initialize({ + securityLevel: "loose", + startOnLoad: false, +}); + +// --- CLI parsing --- + +const args = process.argv.slice(2); + +if (args.includes("--help") || args.includes("-h")) { + console.log(`Usage: node validate_mermaid.mjs [OPTIONS] + +Validate Mermaid diagram syntax using mermaid.parse(). +Strips markdown fencing (\`\`\`mermaid ... \`\`\`) if present. + +Options: + --strict Treat banned C4* keywords as errors (exit 2) + --help Show this help + +Exit codes: + 0 Valid syntax + 1 Syntax error + 2 Banned keyword detected (--strict mode) + 3 Usage error`); + process.exit(0); +} + +const strict = args.includes("--strict"); +const fileArgs = args.filter((a) => !a.startsWith("--")); + +if (fileArgs.length > 1) { + console.error("Error: expected at most one file argument."); + process.exit(3); +} + +// --- Read input --- + +let code; + +if (fileArgs.length === 0 || fileArgs[0] === "-") { + // Read from stdin + try { + code = readFileSync(0, "utf-8"); + } catch { + console.error("Error: could not read from stdin."); + process.exit(3); + } +} else { + const filePath = resolve(fileArgs[0]); + try { + code = readFileSync(filePath, "utf-8"); + } catch (e) { + console.error(`Error: could not read file: ${filePath}\n${e.message}`); + process.exit(3); + } +} + +if (!code.trim()) { + console.error("Error: empty input."); + process.exit(3); +} + +// --- Strip markdown fencing --- + +const fencePattern = /^```mermaid\s*\n([\s\S]*?)\n```\s*$/m; +const fenceMatch = code.match(fencePattern); +if (fenceMatch) { + code = fenceMatch[1]; +} + +// --- Check for banned C4* keywords --- + +const BANNED_KEYWORDS = [ + "C4Context", + "C4Container", + "C4Component", + "C4Deployment", + "C4Dynamic", +]; + +const bannedFound = []; +for (const kw of BANNED_KEYWORDS) { + // Match as the diagram type declaration (first non-comment, non-frontmatter line) + const pattern = new RegExp(`^\\s*${kw}\\b`, "m"); + if (pattern.test(code)) { + bannedFound.push(kw); + } +} + +if (bannedFound.length > 0) { + const msg = `Warning: banned C4 keyword(s) detected: ${bannedFound.join(", ")}.\n` + + `These are experimental and unreliable. Use flowchart + subgraphs instead.`; + console.error(msg); + if (strict) { + process.exit(2); + } +} + +// --- Validate syntax --- + +try { + await mermaid.parse(code); + console.log("✓ Valid"); + process.exit(bannedFound.length > 0 && strict ? 2 : 0); +} catch (e) { + const errorMsg = e.message || String(e); + console.error(`Syntax error: ${errorMsg}`); + process.exit(1); +} From 0ddc6b6fcaac1e9e3f7dabbd2f7c836973c54b8a Mon Sep 17 00:00:00 2001 From: Dmitrii Kharitonov Date: Fri, 19 Jun 2026 19:08:58 +0200 Subject: [PATCH 2/3] docs: reframe README as multi-skill solution architecture repo Extends from single distill-knowledge skill to a collection of skills for solution architecture tasks. Adds c4-diagram overview, shared design principles, and per-skill quick examples. --- README.md | 219 +++++++++++++++++++++--------------------------------- 1 file changed, 86 insertions(+), 133 deletions(-) diff --git a/README.md b/README.md index 2a7569e..977092f 100644 --- a/README.md +++ b/README.md @@ -1,191 +1,144 @@ -# Distill knowledge from meetings, interviews, and screen-shares +# Solution Architecture Skills -An [Agent Skill](https://agentskills.io/) that turns recorded meetings into speaker-labelled markdown transcripts, optionally with screenshots and topic-by-topic documents. +A collection of [Agent Skills](https://agentskills.io/) that automate solution architecture tasks — from capturing knowledge in meetings to producing architecture diagrams. -Built for knowledge handovers, interviews, screen-share walkthroughs, and voice notes — anything where the value is in what was said, not in the audio. +Built for architects and engineering teams who want agents to apply domain methodology correctly, not just generate plausible output. -The point of the skill is to draw a clean line between **deterministic work** — audio normalisation, video → audio, chunking, VTT parsing, API calls, frame extraction — handled by the included scripts, and **language work** — picking the transcription path, naming speakers, judging what to repair or mark unclear, choosing which moments deserve a screenshot, and shaping the final document — done by the agent itself. Scripts move bytes; the agent decides meaning and structure. +## Skills + +| Skill | What it does | Status | +|---|---|---| +| [`distill-knowledge`](skills/distill-knowledge/) | Turns recorded meetings, interviews, and screen-shares into speaker-labeled transcripts and structured topic documents | Stable (v1.0) | +| [`c4-diagram`](skills/c4-diagram/) | Produces C4 architecture diagrams in Mermaid notation (flowchart + subgraphs, validated syntax) | Initial (v0.1) | ## Install ```bash +# Install a single skill npx skills add dimdasci/distill-knowledge +npx skills add dimdasci/distill-knowledge --skill c4-diagram ``` -This drops the skill into your agent's skills directory. - -Manual install: clone this repo, copy `skills/distill-knowledge/` into your agent's skills folder. +Manual install: clone this repo, copy the desired `skills//` directory into your agent's skills folder. -The skill follows the [Agent Skills open standard](https://agentskills.io/) — originally developed by Anthropic, now an open spec adopted by Claude Code, Gemini CLI, Cursor, OpenCode, Goose, GitHub Copilot, OpenAI Codex, and many other agents. Any compliant agent will pick it up. +Skills follow the [Agent Skills open standard](https://agentskills.io/) — originally developed by Anthropic, now adopted by Claude Code, Gemini CLI, Cursor, OpenCode, Goose, GitHub Copilot, OpenAI Codex, and many other agents. Any compliant agent will pick them up. ## Prerequisites -| Tool | Why | -|---|---| -| [`uv`](https://docs.astral.sh/uv/) | Runs the Python scripts (PEP 723, no venv to manage) | -| `ffmpeg`, `ffprobe` | Audio normalisation, video → audio extraction, screenshot frames | -| `OPENAI_API_KEY` | Transcription via `gpt-4o-transcribe` | -| Python ≥ 3.10 | `uv` installs the right interpreter automatically | - -The skill checks all of these on first run. - -### How to install the tools - -#### Install uv - -```bash -curl -LsSf https://astral.sh/uv/install.sh | sh # macOS, Linux -brew install uv # macOS, Homebrew -pipx install uv # any platform with pipx -``` - -Windows or alternatives: see . - -#### Install ffmpeg - -```bash -brew install ffmpeg # macOS -sudo apt install ffmpeg # Debian, Ubuntu -sudo dnf install ffmpeg # Fedora -choco install ffmpeg # Windows (Chocolatey) -``` - -`ffprobe` ships inside the same package. - -#### Set the OpenAI API key - -The skill picks the key up automatically from either source — no extra wiring: +Each skill declares its own requirements. At a glance: -- **Shell environment** — `export OPENAI_API_KEY=sk-...` in your shell rc, or set it inline before invoking the agent. -- **Project `.env`** — create a `.env` file in the directory you run the agent from (or any parent), with `OPENAI_API_KEY=sk-...` on its own line. The skill walks up the directory tree to find it. - -If both are set, the shell variable wins. Never commit `.env` — keep it gitignored. +| Skill | Needs | +|---|---| +| `distill-knowledge` | `uv`, `ffmpeg`, `ffprobe`, Python ≥3.10, `OPENAI_API_KEY` | +| `c4-diagram` | Node.js ≥18 (for Mermaid syntax validation) | -## How to use +See individual skill `SKILL.md` files for full setup details. -1. Drop the recording in `inbox/`. Audio or video. If you have a `.vtt` next to it, leave it there — the skill uses it as a speaker-aligned skeleton. -2. Ask the agent in plain English what you want. -3. Answer three intake questions: language, number of speakers, topic + any proper names or specialised terms. -4. The agent emits everything under `outbox/{slug}/`. It never writes outside that folder. +--- -The skill always asks before transcribing. If you do not answer the intake questions, it will not call the API. +## Skill: distill-knowledge -## Examples +Converts audio/video recordings into knowledge artifacts. Draws a clean line between **deterministic work** (audio normalization, chunking, API calls, frame extraction) handled by scripts, and **language work** (transcription path selection, speaker naming, fidelity judgment, document shaping) done by the agent. -### Voice note → transcript only +### Quick example > Process the voice memo I just dropped in inbox. -You get one file: - ``` outbox/quick-thoughts-q3-20260420/ └── transcript.md ``` -`transcript.md` has timestamped speaker turns. If there is one speaker, it is paragraphed by topic. The transcript is faithful to what was said — recoverable garble is repaired, silences are not filled. - -### Process handover → topic docs with screenshots +### Capabilities -Anonymised prompt: +- VTT-aligned retranscription (speaker labels from VTT + clean text from API) +- Direct single-speaker transcription +- Multi-speaker diarization (8-min chunks, quality warning) +- Screenshot extraction from screen-share recordings +- Structured topic documents with inline screenshots -> I have a recording of a process handover from Person A to Person B, in Spanish. The recording with screen-share and the VTT are in `inbox/`, prefixed `GMTYYYYMMDD-Recording`. I need documents that describe the processes as Person A presented them, but in English, split by topic with an index file. All tool explanations must be supported by clear screenshots with sequential numbering and explanatory titles. The VTT transcription is likely low quality and needs re-transcription. +### How to use -You get: +1. Drop the recording in `inbox/` +2. Ask the agent what you want +3. Answer three intake questions: language, number of speakers, topic + proper names +4. Output appears under `outbox/{slug}/` -``` -outbox/process-handover-20260420/ -├── summary.md # index — one row per topic, links into topics/ -├── transcript.md # full transcript, faithful to the source language -├── topics/ -│ ├── 01-big-picture.md -│ ├── 02-receiving-tickets.md -│ ├── 03-priority-rules.md -│ ├── 04-triage-workflow.md -│ └── ... -└── screenshots/ - ├── 01.jpg - ├── 02.jpg - └── ... -``` +--- -The shape above is what the agent chose for *this* prompt — it is not a template the skill enforces. The skill does not prescribe section names, file counts, or filenames. A different prompt yields a different structure: a single flat document, an FAQ, a checklist, a slide-by-slide narration, a Q&A pairing, or whatever else fits the goal. Discuss the target shape with the agent before or during processing — that is the part you have leverage over. +## Skill: c4-diagram -For the prompt above the agent landed on a `summary.md` index (who is who, the big picture, a table of topics), per-topic files in a *What it is → What you do → What you see → Things to watch out for → Source* rhythm, and screenshots numbered continuously across all topics. +Produces C4 software architecture diagrams rendered as Mermaid code. Applies Simon Brown's C4 methodology correctly — abstraction-first thinking, proper scoping, notation rules — and uses stable Mermaid syntax (flowchart + subgraphs, not the experimental C4* diagram types that break across versions). -## What a topic document looks like +### Quick example -The block below is a synthetic excerpt with placeholder screenshots. In real outputs the screenshots are frames extracted from the recording at the right timestamps. The layout below reflects one prompt's intent — yours can ask for something completely different. +> Create a system context diagram for our payment service. +```mermaid +--- +title: Payment Service — System Context --- +flowchart TB + Merchant(["Merchant
[Person]
Submits payment requests"]) + PaySvc["Payment Service
[Software System]
Processes card payments"] + Gateway["Card Network Gateway
[Software System, External]
Routes to card networks"]:::external + + Merchant -- "submits payments via" --> PaySvc + PaySvc -- "authorizes charges through" --> Gateway + ... +``` + +### Capabilities + +- All C4 diagram types: system context, container, component, deployment, dynamic, landscape +- Validated Mermaid syntax (mermaid.parse with loose security) +- Anti-pattern detection (banned C4* keywords, hub-and-spoke bus, mixed levels) +- Self-check against C4 notation rules +- Complete working templates for all diagram types + +### How to use -> ### Step 5 — Triaging an incoming ticket -> -> #### What it is -> -> The triage queue is where every new customer message lands before it reaches an agent. The triager assigns priority, owner, and tags before the ticket leaves the queue. -> -> #### What you do -> -> 1. Open the help-desk → switch to the `Queue` view → filter by `status = New`. -> 2. Open the oldest unassigned ticket. -> 3. Read the customer message. Skim the customer's history in the right panel. -> 4. Set Priority (`Low / Normal / High / Urgent`) and at least one Tag — without a tag the SLA timer is hidden. -> 5. If the issue matches a known engineering bug, fill `Linked issue` so the ticket auto-routes to the right team. -> 6. Click `Assign` and pick the agent. Save. -> -> #### What you see -> -> ![Screenshot 12](docs/assets/example-screenshot-01.png) -> **Screenshot 12 — Ticket detail view, TKT-4821.** Header *"TKT-4821 — Cannot export report to CSV"*; status pills below: `Open · High · Billing team`. Form fields top to bottom: Subject (`Cannot export report to CSV`), Customer (`Lina Park — Borealis Studio`), Priority (`High`), Status (`Open`), Assignee (empty), Linked issue (empty), Tags (empty). Left sidebar: `Inbox | Queue | Customers | Reports | Settings`. -> -> ![Screenshot 13](docs/assets/example-screenshot-02.png) -> **Screenshot 13 — Triage queue view.** Header *"Triage queue — 24 unassigned"*, filter line *"status = New · sort by oldest"*. Table columns: ID, Subject, Customer, Priority, Status. Rows visible: TKT-4821 (Cannot export report to CSV, Borealis Studio, High, New), TKT-4822 (2FA email not arriving, Ceres Logistics, Urgent, New), TKT-4823 (Wrong totals on dashboard, Delta Atelier, Normal, New), TKT-4824 (Bulk import stuck at 30%, Echo Robotics, High, New), TKT-4825 (How do I invite a colleague?, Forge & Sons, Low, New), TKT-4826 (API key rotated unexpectedly, Helix Foods, Urgent, New). -> -> #### Things to watch out for -> -> - Tickets with no Tags are hidden from the SLA timer. Always set at least one tag before saving. -> - The "Internal note" toggle in the reply box defaults to OFF — double-check before pasting customer-facing text. -> -> #### Source -> -> Transcript: parts 34, 35, 36 (timestamps 46:30–52:30). +1. Tell the agent what system you want to diagram and who will read it +2. The agent identifies C4 abstractions, selects diagram type, confirms with you +3. Diagram produced, validated, and delivered as a Mermaid code block --- -## Repo layout +## Repo Layout | Path | What it is | |---|---| -| `skills/distill-knowledge/` | The skill itself — what `npx skills add` installs | -| `inbox/` | Drop recordings here | -| `outbox/` | Generated transcripts and topic docs | -| `tmp/` | Preprocessing intermediates (chunks, manifests). Safe to delete. | -| `eval/` | Trigger-evaluation harness — checks that the skill activates on the right prompts | -| `docs/assets/` | Placeholder images used in this README | +| `skills/distill-knowledge/` | Distill Knowledge skill — transcription and topic docs | +| `skills/c4-diagram/` | C4 Diagram skill — architecture diagrams in Mermaid | +| `inbox/` | Drop recordings here (distill-knowledge) | +| `outbox/` | Generated artifacts | +| `tmp/` | Processing intermediates (safe to delete) | +| `eval/` | Trigger-evaluation harness | +| `docs/assets/` | Images used in documentation | -The published GitHub repo is named `distill-knowledge` to match the skill (skills.sh convention). The local working dir may differ. +## Design Principles -## Two transcription paths +All skills in this repo share a philosophy: -| Input | Path | Notes | -|---|---|---| -| Recording with a good VTT | Render the VTT directly | Cheapest, most accurate. No API call. | -| Recording with a garbled VTT | VTT-aligned re-transcription | VTT gives speaker labels and turn timestamps; `gpt-4o-transcribe` gives clean text; the agent aligns. | -| Recording, no VTT, one speaker | Direct transcription | `gpt-4o-transcribe` on the prepared audio. | -| Recording, no VTT, many speakers | Diarise fallback | `gpt-4o-transcribe-diarize` in 8-minute chunks. Quality is unstable — the skill warns you. | - -The skill picks the path at Gate 1 of the workflow and asks you to confirm before spending API budget. +1. **Methodology over syntax** — skills encode *how to think about the task* (C4 abstractions, transcription fidelity rules), not just tool syntax. +2. **Scripts for I/O, agents for judgment** — deterministic work (file processing, validation, API calls) lives in scripts; decisions about meaning and structure are the agent's job. +3. **Progressive disclosure** — SKILL.md stays compact; heavy references load on demand to respect context budgets. +4. **Validation loops** — every skill validates its output before delivering (syntax check, self-check checklists). +5. **Anti-pattern catalogs** — encode known mistakes so agents avoid them without trial and error. -## Development checks +## Development ```bash -make quality # format check + lint + syntax + tests +make quality # format check + lint + syntax + tests (distill-knowledge) make format # apply formatter make install-hooks # enable commit-time checks ``` -CI runs the same quality checks in `.github/workflows/python-quality.yml`. +For c4-diagram validation: +```bash +cd skills/c4-diagram/scripts && npm install +node validate_mermaid.mjs --strict ../../assets/templates/context.mmd +``` ## License From b472cce546fd223a64c0db99fd05331be51e49ea Mon Sep 17 00:00:00 2001 From: Dmitrii Kharitonov Date: Fri, 19 Jun 2026 19:12:03 +0200 Subject: [PATCH 3/3] fix: correct skills install syntax in README --- README.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 977092f..609d960 100644 --- a/README.md +++ b/README.md @@ -14,8 +14,11 @@ Built for architects and engineering teams who want agents to apply domain metho ## Install ```bash -# Install a single skill -npx skills add dimdasci/distill-knowledge +# Install all skills from this repo +npx skills add dimdasci/distill-knowledge --skill '*' + +# Install a specific skill +npx skills add dimdasci/distill-knowledge --skill distill-knowledge npx skills add dimdasci/distill-knowledge --skill c4-diagram ```