From 268ef4673c1cf71cd35c69c7a8a56b285ae7be45 Mon Sep 17 00:00:00 2001 From: Morgan Roderick Date: Fri, 3 Apr 2026 22:10:32 +0200 Subject: [PATCH 01/14] docs: add learn section import design --- .../2026-04-03-learn-section-import-design.md | 192 ++++++++++++++++++ 1 file changed, 192 insertions(+) create mode 100644 docs/superpowers/specs/2026-04-03-learn-section-import-design.md diff --git a/docs/superpowers/specs/2026-04-03-learn-section-import-design.md b/docs/superpowers/specs/2026-04-03-learn-section-import-design.md new file mode 100644 index 0000000..1fe90fc --- /dev/null +++ b/docs/superpowers/specs/2026-04-03-learn-section-import-design.md @@ -0,0 +1,192 @@ +# Learn Section Import Design + +## Overview + +Import the `/learn/` section from the idempot-js VitePress site into idempot.dev, replacing the existing "Why Idempotency" page. + +## Goals + +- Replace the simple "Why Idempotency" page with a comprehensive learn section +- Preserve all content including mermaid diagrams and YouTube embed +- Maintain site coherence while acknowledging framework-specific examples + +## Scope + +**Import:** 5 pages from idempot-js/docs/learn/: + +- index.md (overview) +- why.md (with mermaid diagrams, YouTube video) +- duplicated-vs-repeated.md (with code examples) +- client-key-strategies.md (JavaScript patterns) +- spec.md (IETF compliance) + +**Replace:** Current docs/why-idempotency.md + +## Architecture + +### File Structure + +``` +docs/ + ├── learn/ + │ ├── index.md + │ ├── why.md + │ ├── duplicated-vs-repeated.md + │ ├── client-key-strategies.md + │ └── spec.md + ├── index.md (existing) + └── specs/ (existing) + +Delete: docs/why-idempotency.md +``` + +### Navigation + +**Top nav:** Simple link to /learn/ (not a dropdown) + +```javascript +{ text: "Learn", link: "/learn/" } +``` + +**Sidebar:** Context-aware sidebar for /learn/ section + +```javascript +sidebar: { + "/learn/": [ + { + text: "Learn", + items: [ + { text: "Overview", link: "/learn/" }, + { text: "Why Idempotency", link: "/learn/why" }, + { text: "Duplicated vs Repeated", link: "/learn/duplicated-vs-repeated" }, + { text: "Client Key Strategies", link: "/learn/client-key-strategies" }, + { text: "Spec Compliance", link: "/learn/spec" } + ] + } + ], + // other paths... +} +``` + +## Technical Changes + +### Dependencies + +Add to package.json: + +```json +"vitepress-plugin-mermaid": "^2.0.17" +``` + +### VitePress Configuration + +```javascript +import { defineConfig } from "vitepress"; +import { withMermaid } from "vitepress-plugin-mermaid"; + +export default withMermaid( + defineConfig({ + srcDir: "docs", + markdown: { mermaid: true }, + vite: { + optimizeDeps: { + include: [ + "dayjs", + "@braintree/sanitize-url", + "debug", + "cytoscape", + "cytoscape-cose-bilkent" + ] + } + }, + themeConfig: { + nav: [ + { text: "Home", link: "/" }, + { text: "Learn", link: "/learn/" }, + { text: "Specs", link: "/specs" } + ], + sidebar: { + "/learn/": [ + /* learn sidebar items */ + ] + // existing sidebars... + } + } + }) +); +``` + +## Content Adaptations + +### Framework Examples + +Keep framework-specific code (Hono, Redis, etc.) but add context: + +**Example note to add:** + +```markdown +**Note:** This example uses idempot-js middleware with the Hono framework. +See [idempot-js documentation](https://github.com/idempot-dev/idempot-js) +for framework-specific implementations. +``` + +### Content Placement + +Add notes in these files: + +- `duplicated-vs-repeated.md` - Before Hono code example +- `client-key-strategies.md` - At start of strategy examples +- `spec.md` - At start of implementation section + +### YouTube Video + +Keep the video embed in `why.md` as-is (relevant general content about idempotency). + +### Mermaid Diagrams + +Preserve sequence diagrams in `why.md` without modification. + +## Implementation Plan + +**Commit 1: Add mermaid infrastructure** + +- Add vitepress-plugin-mermaid to package.json +- Update .vitepress/config.mjs with mermaid plugin +- Add vite optimizeDeps configuration + +**Commit 2: Copy and adapt learn pages** + +- Create docs/learn/ directory +- Copy 5 markdown files from idempot-js +- Add context notes to framework-specific sections + +**Commit 3: Update navigation** + +- Replace "Why Idempotency" with "Learn" in nav +- Add "/learn/" sidebar configuration +- Remove "Why Idempotency" from existing sidebar + +**Commit 4: Remove old file** + +- Delete docs/why-idempotency.md + +## Testing + +After implementation: + +1. Run `npm run dev` - verify site starts +2. Navigate to /learn/ - verify index loads +3. Check mermaid diagrams render in /learn/why +4. Verify YouTube video embed works +5. Test sidebar navigation through all learn pages +6. Verify code examples display correctly +7. Run `npm run build` - verify production build succeeds + +## Success Criteria + +- All 5 learn pages accessible at /learn/\* paths +- Mermaid diagrams render correctly +- YouTube video embeds properly +- Navigation works for both desktop and mobile +- No broken links +- Production build succeeds From 535603cb66a99b55617b45182e13ab4da2cbcd0d Mon Sep 17 00:00:00 2001 From: Morgan Roderick Date: Fri, 3 Apr 2026 22:14:28 +0200 Subject: [PATCH 02/14] docs: fix sidebar config issue in learn section design --- .../2026-04-03-learn-section-import-design.md | 39 ++++++++++++++++--- 1 file changed, 33 insertions(+), 6 deletions(-) diff --git a/docs/superpowers/specs/2026-04-03-learn-section-import-design.md b/docs/superpowers/specs/2026-04-03-learn-section-import-design.md index 1fe90fc..d8c458d 100644 --- a/docs/superpowers/specs/2026-04-03-learn-section-import-design.md +++ b/docs/superpowers/specs/2026-04-03-learn-section-import-design.md @@ -48,7 +48,9 @@ Delete: docs/why-idempotency.md { text: "Learn", link: "/learn/" } ``` -**Sidebar:** Context-aware sidebar for /learn/ section +**Sidebar:** Context-aware sidebar (object format, not array) + +Current sidebar is array format. Must convert to object format for path-specific sidebars: ```javascript sidebar: { @@ -64,10 +66,26 @@ sidebar: { ] } ], - // other paths... + "/": [ + { + text: "Documentation", + items: [ + { text: "Why Idempotency", link: "/why-idempotency" }, + { text: "Specifications", link: "/specs" } + ] + }, + { + text: "Projects", + items: [ + { text: "idempot-js", link: "https://github.com/idempot-dev/idempot-js" } + ] + } + ] } ``` +**Important:** The "/" path sidebar will be replaced in commit 3 when we update navigation. + ## Technical Changes ### Dependencies @@ -78,6 +96,8 @@ Add to package.json: "vitepress-plugin-mermaid": "^2.0.17" ``` +**Note:** Version matches idempot-js to ensure consistent diagram rendering. + ### VitePress Configuration ```javascript @@ -163,8 +183,9 @@ Preserve sequence diagrams in `why.md` without modification. **Commit 3: Update navigation** - Replace "Why Idempotency" with "Learn" in nav +- Convertsidebar from array to object format - Add "/learn/" sidebar configuration -- Remove "Why Idempotency" from existing sidebar +- Update "/" sidebar: replace "Why Idempotency" with link to "/learn/" (or remove from documentation section) **Commit 4: Remove old file** @@ -178,9 +199,15 @@ After implementation: 2. Navigate to /learn/ - verify index loads 3. Check mermaid diagrams render in /learn/why 4. Verify YouTube video embed works -5. Test sidebar navigation through all learn pages -6. Verify code examples display correctly -7. Run `npm run build` - verify production build succeeds +5. Test sidebar navigation: + - Click each item in learn sidebar + - Verify correct page loads +6. Test internal links: + - Click links between learn pages + - Verify they navigate correctly +7. Verify code examples display correctly with syntax highlighting +8. Run `npm run build` - verify production build succeeds +9. Run `npm run preview` - test production build locally ## Success Criteria From 46f67b8324e0c57b97281d8023ccbeefb9bf08bf Mon Sep 17 00:00:00 2001 From: Morgan Roderick Date: Fri, 3 Apr 2026 22:17:57 +0200 Subject: [PATCH 03/14] docs: add learn section import implementation plan --- .../plans/2026-04-03-learn-section-import.md | 677 ++++++++++++++++++ 1 file changed, 677 insertions(+) create mode 100644 docs/superpowers/plans/2026-04-03-learn-section-import.md diff --git a/docs/superpowers/plans/2026-04-03-learn-section-import.md b/docs/superpowers/plans/2026-04-03-learn-section-import.md new file mode 100644 index 0000000..adfa77e --- /dev/null +++ b/docs/superpowers/plans/2026-04-03-learn-section-import.md @@ -0,0 +1,677 @@ +# Learn Section Import Implementation Plan + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** Import the /learn/ section from idempot-js VitePress site, replacing the existing "Why Idempotency" page. + +**Architecture:** Add mermaid plugin support to VitePress, copy 5 learn pages from idempot-js with context modifications for framework-specific code, convert sidebar from array to object format for path-specific sidebars, update navigation to replace "Why Idempotency" with "Learn". + +**Tech Stack:** VitePress 2.0, vitepress-plugin-mermaid 2.0.17, Markdown, Mermaid diagrams + +--- + +## Task 1: Add Mermaid Plugin Dependency + +**Files:** + +- Modify: `package.json` + +- [ ] **Step 1: Add vitepress-plugin-mermaid dependency** + +Open `package.json` and add the mermaid plugin to devDependencies after vitepress: + +```json +"devDependencies": { + "@commitlint/cli": "^20.5.0", + "@commitlint/config-conventional": "^20.5.0", + "@eslint/js": "^10.0.1", + "eslint": "^10.1.0", + "eslint-config-prettier": "^10.1.8", + "globals": "^17.4.0", + "husky": "^9.1.7", + "lint-staged": "^16.4.0", + "prettier": "^3.8.1", + "vitepress": "^2.0.0-alpha.17", + "vitepress-plugin-mermaid": "^2.0.17" +} +``` + +- [ ] **Step 2: Install dependencies** + +Run: `npm install` + +Expected: Dependencies install successfully, package-lock.json updates. + +- [ ] **Step 3: Verify installation** + +Run: `grep vitepress-plugin-mermaid package-lock.json` + +Expected: Shows vitepress-plugin-mermaid entry with version 2.0.17. + +- [ ] **Step 4: Commit** + +```bash +git add package.json package-lock.json +git commit -m "chore: add vitepress-plugin-mermaid dependency" +``` + +--- + +## Task 2: Configure Mermaid in VitePress + +**Files:** + +- Modify: `.vitepress/config.mjs` + +- [ ] **Step 1: Import mermaid plugin** + +Add import at the top of `.vitepress/config.mjs`: + +```javascript +import { defineConfig } from "vitepress"; +import { withMermaid } from "vitepress-plugin-mermaid"; +``` + +- [ ] **Step 2: Wrap configuration with mermaid** + +Replace the export statement to wrap with `withMermaid`: + +```javascript +export default withMermaid( + defineConfig({ + srcDir: "docs", + + title: "idempot.dev", + description: "Idempotency middlewares for resilient APIs", + + sitemap: { + hostname: "https://idempot.dev/" + }, + + markdown: { + mermaid: true + }, + + vite: { + optimizeDeps: { + include: [ + "dayjs", + "@braintree/sanitize-url", + "debug", + "cytoscape", + "cytoscape-cose-bilkent" + ] + } + }, + + head: [ + [ + "script", + { + async: true, + src: "https://plausible.io/js/pa-9RIBzBG_R4o5GyH7c1n9C.js" + } + ], + [ + "script", + {}, + "window.plausible=window.plausible||function(){(plausible.q=plausible.q||[]).push(arguments)},plausible.init=plausible.init||function(i){plausible.o=i||{}};plausible.init()" + ] + ], + + themeConfig: { + lastUpdated: true, + nav: [ + { text: "Home", link: "/" }, + { text: "Why Idempotency", link: "/why-idempotency" }, + { text: "Specs", link: "/specs" } + ], + sidebar: [ + { + text: "Documentation", + items: [ + { text: "Why Idempotency", link: "/why-idempotency" }, + { text: "Specifications", link: "/specs" } + ] + }, + { + text: "Projects", + items: [ + { + text: "idempot-js", + link: "https://github.com/idempot-dev/idempot-js" + } + ] + } + ], + socialLinks: [{ icon: "github", link: "https://github.com/idempot-dev" }] + } + }) +); +``` + +- [ ] **Step 3: Verify configuration loads** + +Run: `npm run dev` + +Expected: Development server starts without errors. + +- [ ] **Step 4: Stop dev server** + +Press: `Ctrl+C` + +- [ ] **Step 5: Commit** + +```bash +git add .vitepress/config.mjs +git commit -m "feat: configure mermaid diagram support in vitepress" +``` + +--- + +## Task 3: Create Learn Directory and Index Page + +**Files:** + +- Create: `docs/learn/index.md` + +- [ ] **Step 1: Create learn directory** + +Run: `mkdir -p docs/learn` + +- [ ] **Step 2: Create index.md** + +Read source: `~/code/idempot-dev/idempot-js/docs/learn/index.md` + +Create `docs/learn/index.md` with identical content (no modifications needed for index): + +```markdown +# Learn + +Idempotency is essential for reliable distributed systems. When networks fail and clients retry requests, idempotency prevents duplicate transactions—no double charges, no duplicate orders. + +## Key Concepts + +### Why Idempotency Matters + +Every API that processes payments, creates orders, or modifies state needs idempotency. Without it, network failures and client retries create duplicate transactions. **[Learn why →](/learn/why)** + +### Duplicated vs Repeated Operations + +Idempotency protects against duplicates from retries while allowing legitimate repeated operations. Use a different idempotency key for each distinct business operation. **[Learn the difference →](/learn/duplicated-vs-repeated)** + +### Client Key Strategies + +How should you generate idempotency keys? Learn patterns for managing keys in your client applications. **[See strategies →](/learn/client-key-strategies)** + +### IETF Specification + +This library implements the IETF draft standard for idempotency keys. Understanding the spec helps you implement idempotency correctly and interoperate with other systems. **[Read the spec compliance guide →](/learn/spec)** + +## What You'll Learn + +- The problem duplicates create in distributed systems +- How the idempotency-key pattern works +- What the IETF specification requires +- Implementation details for each requirement +``` + +- [ ] **Step 3: Verify file created** + +Run: `ls -la docs/learn/` + +Expected: Shows `index.md` file. + +- [ ] **Step 4: Commit** + +```bash +git add docs/learn/index.md +git commit -m "docs: add learn section index page" +``` + +--- + +## Task 4: Create Why Idempotency Page with Diagrams + +**Files:** + +- Create: `docs/learn/why.md` + +- [ ] **Step 1: Read source file** + +Read source: `~/code/idempot-dev/idempot-js/docs/learn/why.md` + +- [ ] **Step 2: Create why.md** + +Create `docs/learn/why.md` with the following content (exact copy from idempot-js, preserving mermaid diagrams and YouTube embed): + +````markdown +# Why Idempotency Matters + +In distributed systems, networks timeout, load balancers retry, users double-click. Without idempotency, these failures create duplicate transactions: double charges, duplicate orders, inconsistent state. + +## The Problem + +Duplicate requests happen more often than you'd think: + +| Cause | Example | +| ---------------- | ------------------------------------- | +| User behavior | Double-clicking a submit button | +| Client retries | Automatic retry on connection timeout | +| Network issues | Request succeeds but response is lost | +| Load balancers | Backend timeout triggers retry | +| Webhook delivery | Provider retries failed deliveries | + +```mermaid +sequenceDiagram + participant Client + participant Server + + Client->>Server: POST /api/transfers + Server-->>Client: 201 Created (response LOST) + Note over Client: Network timeout
Client retries... + Client->>Server: POST /api/transfers + Server-->>Client: 201 Created + Note over Server: ❌ Duplicate created +``` +```` + +Each duplicate request creates side effects: duplicate payments, duplicate orders, corrupted data. + +## The Pattern + +Major APIs like Stripe and PayPal use a simple pattern to solve this: + +1. **Client generates a unique key** — typically a UUID for each unique operation +2. **Key sent as header** — `Idempotency-Key: ` +3. **Server stores key + response** — in your database or cache +4. **On duplicate request** — server returns cached response instead of reprocessing + +```mermaid +sequenceDiagram + participant Client + participant Server + + Client->>Server: POST /api/transfers
Idempotency-Key: abc-123 + Note over Server: Store key, process request + Server-->>Client: 201 Created + Note over Client: Network timeout
Client retries... + Client->>Server: POST /api/transfers
Idempotency-Key: abc-123 + Note over Server: Key found
Return cached response + Server-->>Client: 201 Created + Note over Server: ✅ No duplicate +``` + +This makes any request safely retryable. The server either processes it once and caches the result, or recognizes the key and returns the previous result. + +## Benefits + +- **Fault tolerance**: Network interruptions don't cause duplicate transactions +- **Simplified retry logic**: Clients can safely retry without complex deduplication +- **Better UX**: Users don't wonder "did that go through?" +- **API reliability**: Stripe, PayPal, and major processors all use this pattern + +Idempot-js implements the [IETF Idempotency-Key Header draft specification](https://datatracker.ietf.org/doc/html/draft-ietf-httpapi-idempotency-key-header-07) for Node.js, Bun, and Deno applications. + +## Further Learning + + + +**[Try, try again](https://www.youtube.com/watch?v=29NNiZhXe2Q)** — Sam Newman explains the importance of idempotency in distributed systems at LeadDev Berlin 2025. + +```` + +- [ ] **Step 3: Commit** + +```bash +git add docs/learn/why.md +git commit -m "docs: add why idempotency page with mermaid diagrams" +```` + +--- + +## Task 5: Create Duplicated vs Repeated Page with Context + +**Files:** + +- Create: `docs/learn/duplicated-vs-repeated.md` + +- [ ] **Step 1: Read source file** + +Read source: `~/code/idempot-dev/idempot-js/docs/learn/duplicated-vs-repeated.md` + +- [ ] **Step 2: Create duplicated-vs-repeated.md with context note** + +Copy the entire content from idempot-js source, then add the context note. The content should be identical to the source file `~/code/idempot-dev/idempot-js/docs/learn/duplicated-vs-repeated.md`, with this addition: + +After the `## Server Implementation` heading (line 93 in source), add before the code block: + +```markdown +**Note:** The following example uses idempot-js middleware with the Hono framework. For framework-specific implementations, see the [idempot-js documentation](https://github.com/idempot-dev/idempot-js). +``` + +The file should maintain all original content including: + +- Table comparing Duplicated vs Repeated +- Example with monthly invoice payments +- Request model JavaScript code +- HTTP request examples +- Server implementation code +- Summary table + +- [ ] **Step 3: Commit** + +```bash +git add docs/learn/duplicated-vs-repeated.md +git commit -m "docs: add duplicated vs repeated page with framework context" +``` + +--- + +## Task 6: Create Client Key Strategies Page + +**Files:** + +- Create: `docs/learn/client-key-strategies.md` + +- [ ] **Step 1: Read source file** + +Read source: `~/code/idempot-dev/idempot-js/docs/learn/client-key-strategies.md` + +- [ ] **Step 2: Create client-key-strategies.md** + +Copy the entire content from `~/code/idempot-dev/idempot-js/docs/learn/client-key-strategies.md` without modifications. The file contains: + +- Strategy 1: Random Keys (with JavaScript code example) +- Strategy 2: Database ID as Key (with JavaScript code example) +- Benefits of each approach + +No context notes needed - code examples are pure JavaScript patterns, not framework-specific. + +- [ ] **Step 3: Commit** + +```bash +git add docs/learn/client-key-strategies.md +git commit -m "docs: add client key strategies page" +``` + +--- + +## Task 7: Create Spec Compliance Page + +**Files:** + +- Create: `docs/learn/spec.md` + +- [ ] **Step 1: Read source file** + +Read source: `~/code/idempot-dev/idempot-js/docs/learn/spec.md` + +- [ ] **Step 2: Create spec.md with context note** + +Copy the entire content from `~/code/idempot-dev/idempot-js/docs/learn/spec.md`, then add context note. After the `## Implemented Requirements` heading (line 8 in source), add: + +```markdown +**Note:** This page describes idempot-js implementation of the IETF specification. For other implementations, refer to your framework's documentation. +``` + +The file should maintain all original content including: + +- MUST requirements table +- SHOULD requirements table +- MAY requirements table +- Error responses table +- What's not covered section +- Compliance status + +- [ ] **Step 3: Commit** + +```bash +git add docs/learn/spec.md +git commit -m "docs: add IETF spec compliance page with implementation context" +``` + +--- + +## Task 8: Update Navigation and Sidebar + +**Files:** + +- Modify: `.vitepress/config.mjs` + +- [ ] **Step 1: Update navigation** + +Replace the nav section to change "Why Idempotency" to "Learn": + +```javascript +nav: [ + { text: "Home", link: "/" }, + { text: "Learn", link: "/learn/" }, + { text: "Specs", link: "/specs" } +]; +``` + +- [ ] **Step 2: Convert sidebar to object format** + +Replace the entire sidebar array with object format for path-specific sidebars: + +```javascript +sidebar: { + "/learn/": [ + { + text: "Learn", + items: [ + { text: "Overview", link: "/learn/" }, + { text: "Why Idempotency", link: "/learn/why" }, + { text: "Duplicated vs Repeated", link: "/learn/duplicated-vs-repeated" }, + { text: "Client Key Strategies", link: "/learn/client-key-strategies" }, + { text: "Spec Compliance", link: "/learn/spec" } + ] + } + ], + "/": [ + { + text: "Documentation", + items: [ + { text: "Specifications", link: "/specs" } + ] + }, + { + text: "Projects", + items: [ + { + text: "idempot-js", + link: "https://github.com/idempot-dev/idempot-js" + } + ] + } + ] +} +``` + +Note: "Why Idempotency" is removed from Documentation section since it's now under Learn. + +- [ ] **Step 3: Verify configuration** + +Run: `npm run dev` + +Navigate to `http://localhost:5173/learn/` + +Expected: Learn index page loads with correct sidebar. + +- [ ] **Step 4: Stop dev server** + +Press: `Ctrl+C` + +- [ ] **Step 5: Commit** + +```bash +git add .vitepress/config.mjs +git commit -m "feat: update navigation to add learn section, remove why-idempotency" +``` + +--- + +## Task 9: Delete Old Why Idempotency Page + +**Files:** + +- Delete: `docs/why-idempotency.md` + +- [ ] **Step 1: Delete file** + +Run: `rm docs/why-idempotency.md` + +- [ ] **Step 2: Verify deletion** + +Run: `git status` + +Expected: Shows `deleted: docs/why-idempotency.md` + +- [ ] **Step 3: Commit** + +```bash +git add docs/why-idempotency.md +git commit -m "docs: remove old why-idempotency page (replaced by learn section)" +``` + +--- + +## Task 10: Run Tests and Verify + +**Files:** + +- None (testing only) + +- [ ] **Step 1: Start development server** + +Run: `npm run dev` + +- [ ] **Step 2: Test learn index page** + +Navigate to: `http://localhost:5173/learn/` + +Expected: Learn index page loads with sidebar showing all 5 pages. + +- [ ] **Step 3: Test mermaid diagrams** + +Navigate to: `http://localhost:5173/learn/why` + +Expected: Two sequence diagrams render correctly after "The Problem" and "The Pattern" sections. + +- [ ] **Step 4: Test YouTube embed** + +On `/learn/why`, scroll to bottom. + +Expected: YouTube video embed appears and can be played. + +- [ ] **Step 5: Test sidebar navigation** + +Click each sidebar link: + +- Overview → /learn/ +- Why Idempotency → /learn/why +- Duplicated vs Repeated → /learn/duplicated-vs-repeated +- Client Key Strategies → /learn/client-key-strategies +- Spec Compliance → /learn/spec + +Expected: Each page loads correctly. + +- [ ] **Step 6: Test internal links** + +On `/learn/`, click "Learn why →" + +Expected: Navigates to `/learn/why` + +- [ ] **Step 7: Test code examples with syntax highlighting** + +Navigate to: `http://localhost:5173/learn/duplicated-vs-repeated` + +Expected: JavaScript/Hono code examples display with syntax highlighting. + +- [ ] **Step 8: Test top navigation** + +Click "Learn" in top nav. + +Expected: Navigates to `/learn/` + +- [ ] **Step 9: Stop dev server** + +Press: `Ctrl+C` + +- [ ] **Step 10: Build for production** + +Run: `npm run build` + +Expected: Build completes successfully without errors. + +- [ ] **Step 11: Preview production build** + +Run: `npm run preview` + +Navigate to: `http://localhost:4173/learn/` + +Expected: Production build works correctly with all pages accessible. + +- [ ] **Step 12: Stop preview** + +Press: `Ctrl+C` + +--- + +## Task 11: Final Verification + +- [ ] **Step 1: Check git status** + +Run: `git status` + +Expected: Working tree clean (all changes committed). + +- [ ] **Step 2: Review commit history** + +Run: `git log --oneline -n 10` + +Expected: See all 9 commits in order: + +1. chore: add vitepress-plugin-mermaid dependency +2. feat: configure mermaid diagram support in vitepress +3. docs: add learn section index page +4. docs: add why idempotency page with mermaid diagrams +5. docs: add duplicated vs repeated page with framework context +6. docs: add client key strategies page +7. docs: add IETF spec compliance page with implementation context +8. feat: update navigation to add learn section, remove why-idempotency +9. docs: remove old why-idempotency page (replaced by learn section) + +- [ ] **Step 3: Verify file structure** + +Run: `find docs/learn -type f` + +Expected: + +``` +docs/learn/client-key-strategies.md +docs/learn/duplicated-vs-repeated.md +docs/learn/index.md +docs/learn/spec.md +docs/learn/why.md +``` + +- [ ] **Step 4: Confirm old file deleted** + +Run: `ls docs/why-idempotency.md` + +Expected: `No such file or directory` + +--- + +## Success Criteria + +✓ All 5 learn pages accessible at /learn/\* paths +✓ Mermaid diagrams render correctly +✓ YouTube video embeds properly +✓ Navigation works for both desktop and mobile +✓ Internal links between learn pages work correctly +✓ Code examples display with syntax highlighting +✓ Production build succeeds +✓ All 9 commits in logical order with descriptive messages From d7a916ffc1a999c52cd59ab8852597b09439d031 Mon Sep 17 00:00:00 2001 From: Morgan Roderick Date: Fri, 3 Apr 2026 22:21:25 +0200 Subject: [PATCH 04/14] docs: fix implementation plan - use file copy commands instead of embedding content --- .../plans/2026-04-03-learn-section-import.md | 190 ++++++++---------- 1 file changed, 80 insertions(+), 110 deletions(-) diff --git a/docs/superpowers/plans/2026-04-03-learn-section-import.md b/docs/superpowers/plans/2026-04-03-learn-section-import.md index adfa77e..eed969a 100644 --- a/docs/superpowers/plans/2026-04-03-learn-section-import.md +++ b/docs/superpowers/plans/2026-04-03-learn-section-import.md @@ -237,96 +237,34 @@ git commit -m "docs: add learn section index page" - Create: `docs/learn/why.md` -- [ ] **Step 1: Read source file** +- [ ] **Step 1: Copy source file** -Read source: `~/code/idempot-dev/idempot-js/docs/learn/why.md` +Run: `cp ~/code/idempot-dev/idempot-js/docs/learn/why.md docs/learn/why.md` -- [ ] **Step 2: Create why.md** +Expected: File copied successfully. -Create `docs/learn/why.md` with the following content (exact copy from idempot-js, preserving mermaid diagrams and YouTube embed): +- [ ] **Step 2: Verify file exists** -````markdown -# Why Idempotency Matters - -In distributed systems, networks timeout, load balancers retry, users double-click. Without idempotency, these failures create duplicate transactions: double charges, duplicate orders, inconsistent state. - -## The Problem - -Duplicate requests happen more often than you'd think: +Run: `ls -la docs/learn/why.md` -| Cause | Example | -| ---------------- | ------------------------------------- | -| User behavior | Double-clicking a submit button | -| Client retries | Automatic retry on connection timeout | -| Network issues | Request succeeds but response is lost | -| Load balancers | Backend timeout triggers retry | -| Webhook delivery | Provider retries failed deliveries | - -```mermaid -sequenceDiagram - participant Client - participant Server - - Client->>Server: POST /api/transfers - Server-->>Client: 201 Created (response LOST) - Note over Client: Network timeout
Client retries... - Client->>Server: POST /api/transfers - Server-->>Client: 201 Created - Note over Server: ❌ Duplicate created -``` -```` - -Each duplicate request creates side effects: duplicate payments, duplicate orders, corrupted data. - -## The Pattern - -Major APIs like Stripe and PayPal use a simple pattern to solve this: - -1. **Client generates a unique key** — typically a UUID for each unique operation -2. **Key sent as header** — `Idempotency-Key: ` -3. **Server stores key + response** — in your database or cache -4. **On duplicate request** — server returns cached response instead of reprocessing - -```mermaid -sequenceDiagram - participant Client - participant Server - - Client->>Server: POST /api/transfers
Idempotency-Key: abc-123 - Note over Server: Store key, process request - Server-->>Client: 201 Created - Note over Client: Network timeout
Client retries... - Client->>Server: POST /api/transfers
Idempotency-Key: abc-123 - Note over Server: Key found
Return cached response - Server-->>Client: 201 Created - Note over Server: ✅ No duplicate -``` +Expected: Shows file details, file exists. -This makes any request safely retryable. The server either processes it once and caches the result, or recognizes the key and returns the previous result. +- [ ] **Step 3: Verify content preserved** -## Benefits +Run: `grep -c "mermaid" docs/learn/why.md` -- **Fault tolerance**: Network interruptions don't cause duplicate transactions -- **Simplified retry logic**: Clients can safely retry without complex deduplication -- **Better UX**: Users don't wonder "did that go through?" -- **API reliability**: Stripe, PayPal, and major processors all use this pattern +Expected: Returns 2 (two mermaid diagrams present). -Idempot-js implements the [IETF Idempotency-Key Header draft specification](https://datatracker.ietf.org/doc/html/draft-ietf-httpapi-idempotency-key-header-07) for Node.js, Bun, and Deno applications. +Run: `grep -c "youtube" docs/learn/why.md` -## Further Learning +Expected: Returns 1 (YouTube embed present). - - -**[Try, try again](https://www.youtube.com/watch?v=29NNiZhXe2Q)** — Sam Newman explains the importance of idempotency in distributed systems at LeadDev Berlin 2025. - -```` - -- [ ] **Step 3: Commit** +- [ ] **Step 4: Commit** ```bash git add docs/learn/why.md git commit -m "docs: add why idempotency page with mermaid diagrams" -```` +``` --- @@ -336,30 +274,45 @@ git commit -m "docs: add why idempotency page with mermaid diagrams" - Create: `docs/learn/duplicated-vs-repeated.md` -- [ ] **Step 1: Read source file** +- [ ] **Step 1: Copy source file** -Read source: `~/code/idempot-dev/idempot-js/docs/learn/duplicated-vs-repeated.md` +Run: `cp ~/code/idempot-dev/idempot-js/docs/learn/duplicated-vs-repeated.md docs/learn/duplicated-vs-repeated.md` -- [ ] **Step 2: Create duplicated-vs-repeated.md with context note** +Expected: File copied successfully. -Copy the entire content from idempot-js source, then add the context note. The content should be identical to the source file `~/code/idempot-dev/idempot-js/docs/learn/duplicated-vs-repeated.md`, with this addition: +- [ ] **Step 2: Add context note before Hono example** -After the `## Server Implementation` heading (line 93 in source), add before the code block: +Use the edit tool to insert the context note after the "## Server Implementation" heading. + +The file currently has: + +````markdown +## Server Implementation + +```javascript +import { Hono } from "hono"; +``` +```` + +Change it to: + +````markdown +## Server Implementation -```markdown **Note:** The following example uses idempot-js middleware with the Hono framework. For framework-specific implementations, see the [idempot-js documentation](https://github.com/idempot-dev/idempot-js). + +```javascript +import { Hono } from "hono"; ``` +```` -The file should maintain all original content including: +- [ ] **Step 3: Verify edit** -- Table comparing Duplicated vs Repeated -- Example with monthly invoice payments -- Request model JavaScript code -- HTTP request examples -- Server implementation code -- Summary table +Run: `grep -A 2 "## Server Implementation" docs/learn/duplicated-vs-repeated.md` -- [ ] **Step 3: Commit** +Expected: Shows the heading followed by the context note. + +- [ ] **Step 4: Commit** ```bash git add docs/learn/duplicated-vs-repeated.md @@ -374,21 +327,25 @@ git commit -m "docs: add duplicated vs repeated page with framework context" - Create: `docs/learn/client-key-strategies.md` -- [ ] **Step 1: Read source file** +- [ ] **Step 1: Copy source file** -Read source: `~/code/idempot-dev/idempot-js/docs/learn/client-key-strategies.md` +Run: `cp ~/code/idempot-dev/idempot-js/docs/learn/client-key-strategies.md docs/learn/client-key-strategies.md` -- [ ] **Step 2: Create client-key-strategies.md** +Expected: File copied successfully. -Copy the entire content from `~/code/idempot-dev/idempot-js/docs/learn/client-key-strategies.md` without modifications. The file contains: +- [ ] **Step 2: Verify file exists** -- Strategy 1: Random Keys (with JavaScript code example) -- Strategy 2: Database ID as Key (with JavaScript code example) -- Benefits of each approach +Run: `ls -la docs/learn/client-key-strategies.md` -No context notes needed - code examples are pure JavaScript patterns, not framework-specific. +Expected: Shows file details, file exists. -- [ ] **Step 3: Commit** +- [ ] **Step 3: Verify content** + +Run: `grep "Strategy" docs/learn/client-key-strategies.md | head -n 2` + +Expected: Shows "Strategy 1: Random Keys" and "Strategy 2: Database ID as Key". + +- [ ] **Step 4: Commit** ```bash git add docs/learn/client-key-strategies.md @@ -403,28 +360,41 @@ git commit -m "docs: add client key strategies page" - Create: `docs/learn/spec.md` -- [ ] **Step 1: Read source file** +- [ ] **Step 1: Copy source file** + +Run: `cp ~/code/idempot-dev/idempot-js/docs/learn/spec.md docs/learn/spec.md` + +Expected: File copied successfully. + +- [ ] **Step 2: Add context note at start of requirements section** + +Use the edit tool to insert the context note after the "## Implemented Requirements" heading. + +The file currently has: -Read source: `~/code/idempot-dev/idempot-js/docs/learn/spec.md` +```markdown +## Implemented Requirements -- [ ] **Step 2: Create spec.md with context note** +### MUST Requirements (Required) +``` -Copy the entire content from `~/code/idempot-dev/idempot-js/docs/learn/spec.md`, then add context note. After the `## Implemented Requirements` heading (line 8 in source), add: +Change it to: ```markdown +## Implemented Requirements + **Note:** This page describes idempot-js implementation of the IETF specification. For other implementations, refer to your framework's documentation. + +### MUST Requirements (Required) ``` -The file should maintain all original content including: +- [ ] **Step 3: Verify edit** -- MUST requirements table -- SHOULD requirements table -- MAY requirements table -- Error responses table -- What's not covered section -- Compliance status +Run: `grep -A 3 "## Implemented Requirements" docs/learn/spec.md` -- [ ] **Step 3: Commit** +Expected: Shows the heading followed by the context note. + +- [ ] **Step 4: Commit** ```bash git add docs/learn/spec.md From 2ab2a9c5637a3911e2c7342d6c99564af63d3e3d Mon Sep 17 00:00:00 2001 From: Morgan Roderick Date: Fri, 3 Apr 2026 22:29:37 +0200 Subject: [PATCH 05/14] fix: update plan to use vitepress-mermaid-renderer for VitePress 2.x compatibility --- .../plans/2026-04-03-learn-section-import.md | 128 ++++++------------ 1 file changed, 43 insertions(+), 85 deletions(-) diff --git a/docs/superpowers/plans/2026-04-03-learn-section-import.md b/docs/superpowers/plans/2026-04-03-learn-section-import.md index eed969a..7e68822 100644 --- a/docs/superpowers/plans/2026-04-03-learn-section-import.md +++ b/docs/superpowers/plans/2026-04-03-learn-section-import.md @@ -16,7 +16,7 @@ - Modify: `package.json` -- [ ] **Step 1: Add vitepress-plugin-mermaid dependency** +- [ ] **Step 1: Add vitepress-mermaid-renderer dependency** Open `package.json` and add the mermaid plugin to devDependencies after vitepress: @@ -32,10 +32,12 @@ Open `package.json` and add the mermaid plugin to devDependencies after vitepres "lint-staged": "^16.4.0", "prettier": "^3.8.1", "vitepress": "^2.0.0-alpha.17", - "vitepress-plugin-mermaid": "^2.0.17" + "vitepress-mermaid-renderer": "^1.1.20" } ``` +Note: Using vitepress-mermaid-renderer instead of vitepress-plugin-mermaid for VitePress 2.x compatibility. + - [ ] **Step 2: Install dependencies** Run: `npm install` @@ -44,9 +46,9 @@ Expected: Dependencies install successfully, package-lock.json updates. - [ ] **Step 3: Verify installation** -Run: `grep vitepress-plugin-mermaid package-lock.json` +Run: `grep vitepress-mermaid-renderer package-lock.json` -Expected: Shows vitepress-plugin-mermaid entry with version 2.0.17. +Expected: Shows vitepress-mermaid-renderer entry with version 1.1.20. - [ ] **Step 4: Commit** @@ -61,93 +63,47 @@ git commit -m "chore: add vitepress-plugin-mermaid dependency" **Files:** -- Modify: `.vitepress/config.mjs` - -- [ ] **Step 1: Import mermaid plugin** +- Create: `.vitepress/theme/index.js` -Add import at the top of `.vitepress/config.mjs`: +- [ ] **Step 1: Create theme directory** -```javascript -import { defineConfig } from "vitepress"; -import { withMermaid } from "vitepress-plugin-mermaid"; -``` +Run: `mkdir -p .vitepress/theme` -- [ ] **Step 2: Wrap configuration with mermaid** +- [ ] **Step 2: Create theme/index.js** -Replace the export statement to wrap with `withMermaid`: +Create `.vitepress/theme/index.js` with the following content: ```javascript -export default withMermaid( - defineConfig({ - srcDir: "docs", - - title: "idempot.dev", - description: "Idempotency middlewares for resilient APIs", - - sitemap: { - hostname: "https://idempot.dev/" - }, - - markdown: { - mermaid: true - }, - - vite: { - optimizeDeps: { - include: [ - "dayjs", - "@braintree/sanitize-url", - "debug", - "cytoscape", - "cytoscape-cose-bilkent" - ] +import { h, nextTick, watch } from "vue"; +import DefaultTheme from "vitepress/theme"; +import { useData } from "vitepress"; +import { createMermaidRenderer } from "vitepress-mermaid-renderer"; + +export default { + extends: DefaultTheme, + Layout: () => { + const { isDark } = useData(); + + const initMermaid = () => { + const mermaidRenderer = createMermaidRenderer({ + theme: isDark.value ? "dark" : "forest" + }); + }; + + // initial mermaid setup + nextTick(() => initMermaid()); + + // on theme change, re-render mermaid charts + watch( + () => isDark.value, + () => { + initMermaid(); } - }, + ); - head: [ - [ - "script", - { - async: true, - src: "https://plausible.io/js/pa-9RIBzBG_R4o5GyH7c1n9C.js" - } - ], - [ - "script", - {}, - "window.plausible=window.plausible||function(){(plausible.q=plausible.q||[]).push(arguments)},plausible.init=plausible.init||function(i){plausible.o=i||{}};plausible.init()" - ] - ], - - themeConfig: { - lastUpdated: true, - nav: [ - { text: "Home", link: "/" }, - { text: "Why Idempotency", link: "/why-idempotency" }, - { text: "Specs", link: "/specs" } - ], - sidebar: [ - { - text: "Documentation", - items: [ - { text: "Why Idempotency", link: "/why-idempotency" }, - { text: "Specifications", link: "/specs" } - ] - }, - { - text: "Projects", - items: [ - { - text: "idempot-js", - link: "https://github.com/idempot-dev/idempot-js" - } - ] - } - ], - socialLinks: [{ icon: "github", link: "https://github.com/idempot-dev" }] - } - }) -); + return h(DefaultTheme.Layout); + } +}; ``` - [ ] **Step 3: Verify configuration loads** @@ -163,10 +119,12 @@ Press: `Ctrl+C` - [ ] **Step 5: Commit** ```bash -git add .vitepress/config.mjs -git commit -m "feat: configure mermaid diagram support in vitepress" +git add .vitepress/theme/index.js +git commit -m "feat: configure mermaid renderer in vitepress theme" ``` +Note: Using vitepress-mermaid-renderer with theme-based configuration (compatible with VitePress 2.x) instead of vitepress-plugin-mermaid (requires VitePress 1.x). + --- ## Task 3: Create Learn Directory and Index Page From 7b30f97e0623629362076f3b99854f5239f770f4 Mon Sep 17 00:00:00 2001 From: Morgan Roderick Date: Fri, 3 Apr 2026 22:30:42 +0200 Subject: [PATCH 06/14] chore: add vitepress-mermaid-renderer dependency --- package-lock.json | 1605 +++++++++++++++++++++++++++++++++++++++++++-- package.json | 3 +- 2 files changed, 1561 insertions(+), 47 deletions(-) diff --git a/package-lock.json b/package-lock.json index a832ac1..ea59f59 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,5 +1,5 @@ { - "name": "sitemap", + "name": "website", "lockfileVersion": 3, "requires": true, "packages": { @@ -14,7 +14,23 @@ "husky": "^9.1.7", "lint-staged": "^16.4.0", "prettier": "^3.8.1", - "vitepress": "^2.0.0-alpha.17" + "vitepress": "^2.0.0-alpha.17", + "vitepress-mermaid-renderer": "^1.1.20" + } + }, + "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==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "package-manager-detector": "^1.3.0", + "tinyexec": "^1.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" } }, "node_modules/@babel/code-frame": { @@ -82,6 +98,79 @@ "node": ">=6.9.0" } }, + "node_modules/@braintree/sanitize-url": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/@braintree/sanitize-url/-/sanitize-url-7.1.2.tgz", + "integrity": "sha512-jigsZK+sMF/cuiB7sERuo9V7N9jx+dhmHHnQyDSVdpZwVutaBu7WvNYqMDLSgFgfB30n452TP3vjDAvFC973mA==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/@chevrotain/cst-dts-gen": { + "version": "11.1.2", + "resolved": "https://registry.npmjs.org/@chevrotain/cst-dts-gen/-/cst-dts-gen-11.1.2.tgz", + "integrity": "sha512-XTsjvDVB5nDZBQB8o0o/0ozNelQtn2KrUVteIHSlPd2VAV2utEb6JzyCJaJ8tGxACR4RiBNWy5uYUHX2eji88Q==", + "dev": true, + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@chevrotain/gast": "11.1.2", + "@chevrotain/types": "11.1.2", + "lodash-es": "4.17.23" + } + }, + "node_modules/@chevrotain/cst-dts-gen/node_modules/lodash-es": { + "version": "4.17.23", + "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.23.tgz", + "integrity": "sha512-kVI48u3PZr38HdYz98UmfPnXl2DXrpdctLrFLCd3kOx1xUkOmpFPx7gCWWM5MPkL/fD8zb+Ph0QzjGFs4+hHWg==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/@chevrotain/gast": { + "version": "11.1.2", + "resolved": "https://registry.npmjs.org/@chevrotain/gast/-/gast-11.1.2.tgz", + "integrity": "sha512-Z9zfXR5jNZb1Hlsd/p+4XWeUFugrHirq36bKzPWDSIacV+GPSVXdk+ahVWZTwjhNwofAWg/sZg58fyucKSQx5g==", + "dev": true, + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@chevrotain/types": "11.1.2", + "lodash-es": "4.17.23" + } + }, + "node_modules/@chevrotain/gast/node_modules/lodash-es": { + "version": "4.17.23", + "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.23.tgz", + "integrity": "sha512-kVI48u3PZr38HdYz98UmfPnXl2DXrpdctLrFLCd3kOx1xUkOmpFPx7gCWWM5MPkL/fD8zb+Ph0QzjGFs4+hHWg==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/@chevrotain/regexp-to-ast": { + "version": "11.1.2", + "resolved": "https://registry.npmjs.org/@chevrotain/regexp-to-ast/-/regexp-to-ast-11.1.2.tgz", + "integrity": "sha512-nMU3Uj8naWer7xpZTYJdxbAs6RIv/dxYzkYU8GSwgUtcAAlzjcPfX1w+RKRcYG8POlzMeayOQ/znfwxEGo5ulw==", + "dev": true, + "license": "Apache-2.0", + "peer": true + }, + "node_modules/@chevrotain/types": { + "version": "11.1.2", + "resolved": "https://registry.npmjs.org/@chevrotain/types/-/types-11.1.2.tgz", + "integrity": "sha512-U+HFai5+zmJCkK86QsaJtoITlboZHBqrVketcO2ROv865xfCMSFpELQoz1GkX5GzME8pTa+3kbKrZHQtI0gdbw==", + "dev": true, + "license": "Apache-2.0", + "peer": true + }, + "node_modules/@chevrotain/utils": { + "version": "11.1.2", + "resolved": "https://registry.npmjs.org/@chevrotain/utils/-/utils-11.1.2.tgz", + "integrity": "sha512-4mudFAQ6H+MqBTfqLmU7G1ZwRzCLfJEooL/fsF6rCX5eePMbGhoy5n4g+G4vlh2muDcsCTJtL+uKbOzWxs5LHA==", + "dev": true, + "license": "Apache-2.0", + "peer": true + }, "node_modules/@commitlint/cli": { "version": "20.5.0", "resolved": "https://registry.npmjs.org/@commitlint/cli/-/cli-20.5.0.tgz", @@ -1025,6 +1114,19 @@ "dev": true, "license": "MIT" }, + "node_modules/@iconify/utils": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@iconify/utils/-/utils-3.1.0.tgz", + "integrity": "sha512-Zlzem1ZXhI1iHeeERabLNzBHdOa4VhQbqAcOQaMKuTuyZCpwKbC2R4Dd0Zo3g9EAc+Y4fiarO8HIHRAth7+skw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@antfu/install-pkg": "^1.1.0", + "@iconify/types": "^2.0.0", + "mlly": "^1.8.0" + } + }, "node_modules/@jridgewell/sourcemap-codec": { "version": "1.5.5", "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", @@ -1032,6 +1134,17 @@ "dev": true, "license": "MIT" }, + "node_modules/@mermaid-js/parser": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@mermaid-js/parser/-/parser-1.1.0.tgz", + "integrity": "sha512-gxK9ZX2+Fex5zu8LhRQoMeMPEHbc73UKZ0FQ54YrQtUxE1VVhMwzeNtKRPAu5aXks4FasbMe4xB4bWrmq6Jlxw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "langium": "^4.0.0" + } + }, "node_modules/@rolldown/pluginutils": { "version": "1.0.0-rc.2", "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-rc.2.tgz", @@ -1503,6 +1616,321 @@ "url": "https://ko-fi.com/dangreen" } }, + "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==", + "dev": true, + "license": "MIT", + "peer": true, + "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==", + "dev": true, + "license": "MIT", + "peer": true + }, + "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==", + "dev": true, + "license": "MIT", + "peer": true, + "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==", + "dev": true, + "license": "MIT", + "peer": true, + "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==", + "dev": true, + "license": "MIT", + "peer": true + }, + "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==", + "dev": true, + "license": "MIT", + "peer": true + }, + "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==", + "dev": true, + "license": "MIT", + "peer": true, + "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==", + "dev": true, + "license": "MIT", + "peer": true + }, + "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==", + "dev": true, + "license": "MIT", + "peer": true + }, + "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==", + "dev": true, + "license": "MIT", + "peer": true, + "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==", + "dev": true, + "license": "MIT", + "peer": true + }, + "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==", + "dev": true, + "license": "MIT", + "peer": true + }, + "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==", + "dev": true, + "license": "MIT", + "peer": true, + "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==", + "dev": true, + "license": "MIT", + "peer": true + }, + "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==", + "dev": true, + "license": "MIT", + "peer": true + }, + "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==", + "dev": true, + "license": "MIT", + "peer": true, + "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==", + "dev": true, + "license": "MIT", + "peer": true + }, + "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==", + "dev": true, + "license": "MIT", + "peer": true, + "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==", + "dev": true, + "license": "MIT", + "peer": true + }, + "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==", + "dev": true, + "license": "MIT", + "peer": true + }, + "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==", + "dev": true, + "license": "MIT", + "peer": true + }, + "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==", + "dev": true, + "license": "MIT", + "peer": true + }, + "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==", + "dev": true, + "license": "MIT", + "peer": true, + "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==", + "dev": true, + "license": "MIT", + "peer": true + }, + "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==", + "dev": true, + "license": "MIT", + "peer": true + }, + "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==", + "dev": true, + "license": "MIT", + "peer": true, + "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==", + "dev": true, + "license": "MIT", + "peer": true + }, + "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==", + "dev": true, + "license": "MIT", + "peer": true + }, + "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==", + "dev": true, + "license": "MIT", + "peer": true + }, + "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==", + "dev": true, + "license": "MIT", + "peer": true, + "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==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@types/d3-interpolate": "*", + "@types/d3-selection": "*" + } + }, "node_modules/@types/esrecurse": { "version": "4.3.1", "resolved": "https://registry.npmjs.org/@types/esrecurse/-/esrecurse-4.3.1.tgz", @@ -1517,6 +1945,14 @@ "dev": true, "license": "MIT" }, + "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==", + "dev": true, + "license": "MIT", + "peer": true + }, "node_modules/@types/hast": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz", @@ -1580,6 +2016,15 @@ "undici-types": "~7.18.0" } }, + "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==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true + }, "node_modules/@types/unist": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", @@ -1601,6 +2046,18 @@ "dev": true, "license": "ISC" }, + "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==", + "dev": true, + "license": "MIT", + "peer": true, + "optionalDependencies": { + "d3-selection": "^3.0.0", + "d3-transition": "^3.0.1" + } + }, "node_modules/@vitejs/plugin-vue": { "version": "6.0.5", "resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-6.0.5.tgz", @@ -2037,6 +2494,44 @@ "url": "https://github.com/sponsors/wooorm" } }, + "node_modules/chevrotain": { + "version": "11.1.2", + "resolved": "https://registry.npmjs.org/chevrotain/-/chevrotain-11.1.2.tgz", + "integrity": "sha512-opLQzEVriiH1uUQ4Kctsd49bRoFDXGGSC4GUqj7pGyxM3RehRhvTlZJc1FL/Flew2p5uwxa1tUDWKzI4wNM8pg==", + "dev": true, + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@chevrotain/cst-dts-gen": "11.1.2", + "@chevrotain/gast": "11.1.2", + "@chevrotain/regexp-to-ast": "11.1.2", + "@chevrotain/types": "11.1.2", + "@chevrotain/utils": "11.1.2", + "lodash-es": "4.17.23" + } + }, + "node_modules/chevrotain-allstar": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/chevrotain-allstar/-/chevrotain-allstar-0.3.1.tgz", + "integrity": "sha512-b7g+y9A0v4mxCW1qUhf3BSVPg+/NvGErk/dOkrDaHA0nQIQGAtrOjlX//9OQtRlSCy+x9rfB5N8yC71lH1nvMw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "lodash-es": "^4.17.21" + }, + "peerDependencies": { + "chevrotain": "^11.0.0" + } + }, + "node_modules/chevrotain/node_modules/lodash-es": { + "version": "4.17.23", + "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.23.tgz", + "integrity": "sha512-kVI48u3PZr38HdYz98UmfPnXl2DXrpdctLrFLCd3kOx1xUkOmpFPx7gCWWM5MPkL/fD8zb+Ph0QzjGFs4+hHWg==", + "dev": true, + "license": "MIT", + "peer": true + }, "node_modules/cli-cursor": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-5.0.0.tgz", @@ -2190,6 +2685,14 @@ "dot-prop": "^5.1.0" } }, + "node_modules/confbox": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/confbox/-/confbox-0.1.8.tgz", + "integrity": "sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==", + "dev": true, + "license": "MIT", + "peer": true + }, "node_modules/conventional-changelog-angular": { "version": "8.3.1", "resolved": "https://registry.npmjs.org/conventional-changelog-angular/-/conventional-changelog-angular-8.3.1.tgz", @@ -2233,72 +2736,685 @@ "node": ">=18" } }, - "node_modules/cosmiconfig": { - "version": "9.0.1", + "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==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "layout-base": "^1.0.0" + } + }, + "node_modules/cosmiconfig": { + "version": "9.0.1", "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-9.0.1.tgz", "integrity": "sha512-hr4ihw+DBqcvrsEDioRO31Z17x71pUYoNe/4h6Z0wB72p7MU7/9gH8Q3s12NFhHPfYBBOV3qyfUxmr/Yn3shnQ==", "dev": true, - "license": "MIT", + "license": "MIT", + "dependencies": { + "env-paths": "^2.2.1", + "import-fresh": "^3.3.0", + "js-yaml": "^4.1.0", + "parse-json": "^5.2.0" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/d-fischer" + }, + "peerDependencies": { + "typescript": ">=4.9.5" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/cosmiconfig-typescript-loader": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/cosmiconfig-typescript-loader/-/cosmiconfig-typescript-loader-6.2.0.tgz", + "integrity": "sha512-GEN39v7TgdxgIoNcdkRE3uiAzQt3UXLyHbRHD6YoL048XAeOomyxaP+Hh/+2C6C2wYjxJ2onhJcsQp+L4YEkVQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "jiti": "^2.6.1" + }, + "engines": { + "node": ">=v18" + }, + "peerDependencies": { + "@types/node": "*", + "cosmiconfig": ">=9", + "typescript": ">=5" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/csstype": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", + "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/cytoscape": { + "version": "3.33.1", + "resolved": "https://registry.npmjs.org/cytoscape/-/cytoscape-3.33.1.tgz", + "integrity": "sha512-iJc4TwyANnOGR1OmWhsS9ayRS3s+XQ185FmuHObThD+5AeJCakAAbWv8KimMTt08xCCLNgneQwFp+JRJOr9qGQ==", + "dev": true, + "license": "MIT", + "peer": true, + "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==", + "dev": true, + "license": "MIT", + "peer": true, + "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==", + "dev": true, + "license": "MIT", + "peer": true, + "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==", + "dev": true, + "license": "MIT", + "peer": true, + "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==", + "dev": true, + "license": "MIT", + "peer": true + }, + "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==", + "dev": true, + "license": "ISC", + "peer": true, + "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==", + "dev": true, + "license": "ISC", + "peer": true, + "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==", + "dev": true, + "license": "ISC", + "peer": true, + "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==", + "dev": true, + "license": "ISC", + "peer": true, + "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==", + "dev": true, + "license": "ISC", + "peer": true, + "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==", + "dev": true, + "license": "ISC", + "peer": true, + "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==", + "dev": true, + "license": "ISC", + "peer": true, + "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==", + "dev": true, + "license": "ISC", + "peer": true, + "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==", + "dev": true, + "license": "ISC", + "peer": true, + "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==", + "dev": true, + "license": "ISC", + "peer": true, + "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==", + "dev": true, + "license": "ISC", + "peer": true, + "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-dsv/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==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">= 10" + } + }, + "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==", + "dev": true, + "license": "BSD-3-Clause", + "peer": true, + "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==", + "dev": true, + "license": "ISC", + "peer": true, + "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==", + "dev": true, + "license": "ISC", + "peer": true, + "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==", + "dev": true, + "license": "ISC", + "peer": true, + "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==", + "dev": true, + "license": "ISC", + "peer": true, + "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==", + "dev": true, + "license": "ISC", + "peer": true, + "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==", + "dev": true, + "license": "ISC", + "peer": true, + "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==", + "dev": true, + "license": "ISC", + "peer": true, + "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==", + "dev": true, + "license": "ISC", + "peer": true, + "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==", + "dev": true, + "license": "ISC", + "peer": true, + "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==", + "dev": true, + "license": "ISC", + "peer": true, + "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==", + "dev": true, + "license": "BSD-3-Clause", + "peer": true, + "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==", + "dev": true, + "license": "BSD-3-Clause", + "peer": true, + "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==", + "dev": true, + "license": "BSD-3-Clause", + "peer": true + }, + "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==", + "dev": true, + "license": "BSD-3-Clause", + "peer": true, + "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==", + "dev": true, + "license": "ISC", + "peer": true + }, + "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==", + "dev": true, + "license": "ISC", + "peer": true, + "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==", + "dev": true, + "license": "ISC", + "peer": true, + "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==", + "dev": true, + "license": "ISC", + "peer": true, + "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==", + "dev": true, + "license": "ISC", + "peer": true, + "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==", + "dev": true, + "license": "ISC", + "peer": true, + "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==", + "dev": true, + "license": "ISC", + "peer": true, + "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==", + "dev": true, + "license": "ISC", + "peer": true, + "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==", + "dev": true, + "license": "ISC", + "peer": true, "dependencies": { - "env-paths": "^2.2.1", - "import-fresh": "^3.3.0", - "js-yaml": "^4.1.0", - "parse-json": "^5.2.0" + "d3-color": "1 - 3", + "d3-dispatch": "1 - 3", + "d3-ease": "1 - 3", + "d3-interpolate": "1 - 3", + "d3-timer": "1 - 3" }, "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/d-fischer" + "node": ">=12" }, "peerDependencies": { - "typescript": ">=4.9.5" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } + "d3-selection": "2 - 3" } }, - "node_modules/cosmiconfig-typescript-loader": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/cosmiconfig-typescript-loader/-/cosmiconfig-typescript-loader-6.2.0.tgz", - "integrity": "sha512-GEN39v7TgdxgIoNcdkRE3uiAzQt3UXLyHbRHD6YoL048XAeOomyxaP+Hh/+2C6C2wYjxJ2onhJcsQp+L4YEkVQ==", + "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==", "dev": true, - "license": "MIT", + "license": "ISC", + "peer": true, "dependencies": { - "jiti": "^2.6.1" + "d3-dispatch": "1 - 3", + "d3-drag": "2 - 3", + "d3-interpolate": "1 - 3", + "d3-selection": "2 - 3", + "d3-transition": "2 - 3" }, "engines": { - "node": ">=v18" - }, - "peerDependencies": { - "@types/node": "*", - "cosmiconfig": ">=9", - "typescript": ">=5" + "node": ">=12" } }, - "node_modules/cross-spawn": { - "version": "7.0.6", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", - "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "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==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - }, - "engines": { - "node": ">= 8" + "d3": "^7.9.0", + "lodash-es": "^4.17.21" } }, - "node_modules/csstype": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", - "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", + "node_modules/dayjs": { + "version": "1.11.20", + "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.20.tgz", + "integrity": "sha512-YbwwqR/uYpeoP4pu043q+LTDLFBLApUP6VxRihdfNTqu4ubqMlGDLd6ErXhEgsyvY0K6nCs7nggYumAN+9uEuQ==", "dev": true, - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/debug": { "version": "4.4.3", @@ -2325,6 +3441,17 @@ "dev": true, "license": "MIT" }, + "node_modules/delaunator": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/delaunator/-/delaunator-5.1.0.tgz", + "integrity": "sha512-AGrQ4QSgssa1NGmWmLPqN5NY2KajF5MqxetNEO+o0n3ZwZZeTmt7bBnvzHWrmkZFxGgr4HdyFgelzgi06otLuQ==", + "dev": true, + "license": "ISC", + "peer": true, + "dependencies": { + "robust-predicates": "^3.0.2" + } + }, "node_modules/dequal": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", @@ -2349,6 +3476,17 @@ "url": "https://github.com/sponsors/wooorm" } }, + "node_modules/dompurify": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.3.3.tgz", + "integrity": "sha512-Oj6pzI2+RqBfFG+qOaOLbFXLQ90ARpcGG6UePL82bJLtdsa6CYJD7nmiU8MW9nQNOtCHV3lZ/Bzq1X0QYbBZCA==", + "dev": true, + "license": "(MPL-2.0 OR Apache-2.0)", + "peer": true, + "optionalDependencies": { + "@types/trusted-types": "^2.0.7" + } + }, "node_modules/dot-prop": { "version": "5.3.0", "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.3.0.tgz", @@ -2900,6 +4038,14 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "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==", + "dev": true, + "license": "MIT", + "peer": true + }, "node_modules/hast-util-to-html": { "version": "9.0.5", "resolved": "https://registry.npmjs.org/hast-util-to-html/-/hast-util-to-html-9.0.5.tgz", @@ -2972,6 +4118,20 @@ "url": "https://github.com/sponsors/typicode" } }, + "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==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/ignore": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", @@ -3040,6 +4200,17 @@ "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, + "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==", + "dev": true, + "license": "ISC", + "peer": true, + "engines": { + "node": ">=12" + } + }, "node_modules/is-arrayish": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", @@ -3168,6 +4339,35 @@ "dev": true, "license": "MIT" }, + "node_modules/katex": { + "version": "0.16.44", + "resolved": "https://registry.npmjs.org/katex/-/katex-0.16.44.tgz", + "integrity": "sha512-EkxoDTk8ufHqHlf9QxGwcxeLkWRR3iOuYfRpfORgYfqc8s13bgb+YtRY59NK5ZpRaCwq1kqA6a5lpX8C/eLphQ==", + "dev": true, + "funding": [ + "https://opencollective.com/katex", + "https://github.com/sponsors/katex" + ], + "license": "MIT", + "peer": true, + "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==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">= 12" + } + }, "node_modules/keyv": { "version": "4.5.4", "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", @@ -3178,6 +4378,40 @@ "json-buffer": "3.0.1" } }, + "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==", + "dev": true, + "peer": true + }, + "node_modules/langium": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/langium/-/langium-4.2.1.tgz", + "integrity": "sha512-zu9QWmjpzJcomzdJQAHgDVhLGq5bLosVak1KVa40NzQHXfqr4eAHupvnPOVXEoLkg6Ocefvf/93d//SB7du4YQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "chevrotain": "~11.1.1", + "chevrotain-allstar": "~0.3.1", + "vscode-languageserver": "~9.0.1", + "vscode-languageserver-textdocument": "~1.0.11", + "vscode-uri": "~3.1.0" + }, + "engines": { + "node": ">=20.10.0", + "npm": ">=10.2.3" + } + }, + "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==", + "dev": true, + "license": "MIT", + "peer": true + }, "node_modules/levn": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", @@ -3342,6 +4576,14 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "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==", + "dev": true, + "license": "MIT", + "peer": true + }, "node_modules/lodash.camelcase": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", @@ -3539,6 +4781,20 @@ "dev": true, "license": "MIT" }, + "node_modules/marked": { + "version": "16.4.2", + "resolved": "https://registry.npmjs.org/marked/-/marked-16.4.2.tgz", + "integrity": "sha512-TI3V8YYWvkVf3KJe1dRkpnjs68JUPyEa5vjKrp1XEEJUAOaQc+Qj+L1qWbPd0SJuAdQkFU0h73sXXqwDYxsiDA==", + "dev": true, + "license": "MIT", + "peer": true, + "bin": { + "marked": "bin/marked.js" + }, + "engines": { + "node": ">= 20" + } + }, "node_modules/mdast-util-to-hast": { "version": "13.2.1", "resolved": "https://registry.npmjs.org/mdast-util-to-hast/-/mdast-util-to-hast-13.2.1.tgz", @@ -3574,6 +4830,37 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/mermaid": { + "version": "11.14.0", + "resolved": "https://registry.npmjs.org/mermaid/-/mermaid-11.14.0.tgz", + "integrity": "sha512-GSGloRsBs+JINmmhl0JDwjpuezCsHB4WGI4NASHxL3fHo3o/BRXTxhDLKnln8/Q0lRFRyDdEjmk1/d5Sn1Xz8g==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@braintree/sanitize-url": "^7.1.1", + "@iconify/utils": "^3.0.2", + "@mermaid-js/parser": "^1.1.0", + "@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", + "katex": "^0.16.25", + "khroma": "^2.1.0", + "lodash-es": "^4.17.23", + "marked": "^16.3.0", + "roughjs": "^4.6.6", + "stylis": "^4.3.6", + "ts-dedent": "^2.2.0", + "uuid": "^11.1.0" + } + }, "node_modules/micromark-util-character": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.1.tgz", @@ -3714,6 +5001,20 @@ "dev": true, "license": "MIT" }, + "node_modules/mlly": { + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/mlly/-/mlly-1.8.2.tgz", + "integrity": "sha512-d+ObxMQFmbt10sretNDytwt85VrbkhhUA/JBGm1MPaWJ65Cl4wOgLaB1NYvJSZ0Ef03MMEU/0xpPMXUIQ29UfA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "acorn": "^8.16.0", + "pathe": "^2.0.3", + "pkg-types": "^1.3.1", + "ufo": "^1.6.3" + } + }, "node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", @@ -3832,6 +5133,14 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "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==", + "dev": true, + "license": "MIT", + "peer": true + }, "node_modules/parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", @@ -3864,6 +5173,14 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "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==", + "dev": true, + "license": "MIT", + "peer": true + }, "node_modules/path-exists": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", @@ -3884,6 +5201,14 @@ "node": ">=8" } }, + "node_modules/pathe": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", + "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", + "dev": true, + "license": "MIT", + "peer": true + }, "node_modules/perfect-debounce": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/perfect-debounce/-/perfect-debounce-2.1.0.tgz", @@ -3911,6 +5236,39 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/pkg-types": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-1.3.1.tgz", + "integrity": "sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "confbox": "^0.1.8", + "mlly": "^1.7.4", + "pathe": "^2.0.1" + } + }, + "node_modules/points-on-curve": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/points-on-curve/-/points-on-curve-0.2.0.tgz", + "integrity": "sha512-0mYKnYYe9ZcqMCWhUjItv/oHjvgEsfKvnUTg8sAtnHr3GVy7rGkXCb6d5cSyqrWqL4k81b9CPg3urd+T7aop3A==", + "dev": true, + "license": "MIT", + "peer": true + }, + "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==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "path-data-parser": "0.1.0", + "points-on-curve": "0.2.0" + } + }, "node_modules/postcss": { "version": "8.5.8", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.8.tgz", @@ -4068,6 +5426,14 @@ "dev": true, "license": "MIT" }, + "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==", + "dev": true, + "license": "Unlicense", + "peer": true + }, "node_modules/rollup": { "version": "4.60.1", "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.60.1.tgz", @@ -4113,6 +5479,36 @@ "fsevents": "~2.3.2" } }, + "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==", + "dev": true, + "license": "MIT", + "peer": true, + "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==", + "dev": true, + "license": "BSD-3-Clause", + "peer": true + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "dev": true, + "license": "MIT", + "peer": true + }, "node_modules/semver": { "version": "7.7.4", "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", @@ -4299,6 +5695,14 @@ "node": ">=8" } }, + "node_modules/stylis": { + "version": "4.3.6", + "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.3.6.tgz", + "integrity": "sha512-yQ3rwFWRfwNUY7H5vpU0wfdkNSnvnJinhF9830Swlaxl03zsOjCfmX0ugac+3LtK0lYSgwL/KXc8oYL3mG4YFQ==", + "dev": true, + "license": "MIT", + "peer": true + }, "node_modules/tabbable": { "version": "6.4.0", "resolved": "https://registry.npmjs.org/tabbable/-/tabbable-6.4.0.tgz", @@ -4344,6 +5748,17 @@ "url": "https://github.com/sponsors/wooorm" } }, + "node_modules/ts-dedent": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/ts-dedent/-/ts-dedent-2.2.0.tgz", + "integrity": "sha512-q5W7tVM71e2xjHZTlgfTDoPF/SmqKG5hddq9SzR49CH2hayqRKJtQ4mtRlSxKaJlR/+9rEM+mnBHf7I2/BQcpQ==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=6.10" + } + }, "node_modules/type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", @@ -4372,6 +5787,14 @@ "node": ">=14.17" } }, + "node_modules/ufo": { + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/ufo/-/ufo-1.6.3.tgz", + "integrity": "sha512-yDJTmhydvl5lJzBmy/hyOAA0d+aqCBuwl818haVdYCRrWV84o7YyeVm4QlVHStqNrrJSTb6jKuFAVqAFsr+K3Q==", + "dev": true, + "license": "MIT", + "peer": true + }, "node_modules/undici-types": { "version": "7.18.2", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.18.2.tgz", @@ -4463,6 +5886,21 @@ "punycode": "^2.1.0" } }, + "node_modules/uuid": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-11.1.0.tgz", + "integrity": "sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A==", + "dev": true, + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", + "peer": true, + "bin": { + "uuid": "dist/esm/bin/uuid" + } + }, "node_modules/vfile": { "version": "6.0.3", "resolved": "https://registry.npmjs.org/vfile/-/vfile-6.0.3.tgz", @@ -4615,6 +6053,81 @@ } } }, + "node_modules/vitepress-mermaid-renderer": { + "version": "1.1.20", + "resolved": "https://registry.npmjs.org/vitepress-mermaid-renderer/-/vitepress-mermaid-renderer-1.1.20.tgz", + "integrity": "sha512-lBe0KBkrTtXZIczNlwlVm7JVcrmCjSR7n5J3hEBg2K4/v5U12oW3MJ1hqh9cpY2avLKL66YBqxn3fJjey3zqWw==", + "dev": true, + "license": "GPL-3.0-only", + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "mermaid": "^11.0.0", + "vue": "^3.0.0" + } + }, + "node_modules/vscode-jsonrpc": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-8.2.0.tgz", + "integrity": "sha512-C+r0eKJUIfiDIfwJhria30+TYWPtuHJXHtI7J0YlOmKAo7ogxP20T0zxB7HZQIFhIyvoBPwWskjxrvAtfjyZfA==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/vscode-languageserver": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/vscode-languageserver/-/vscode-languageserver-9.0.1.tgz", + "integrity": "sha512-woByF3PDpkHFUreUa7Hos7+pUWdeWMXRd26+ZX2A8cFx6v/JPTtd4/uN0/jB6XQHYaOlHbio03NTHCqrgG5n7g==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "vscode-languageserver-protocol": "3.17.5" + }, + "bin": { + "installServerIntoExtension": "bin/installServerIntoExtension" + } + }, + "node_modules/vscode-languageserver-protocol": { + "version": "3.17.5", + "resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.17.5.tgz", + "integrity": "sha512-mb1bvRJN8SVznADSGWM9u/b07H7Ecg0I3OgXDuLdn307rl/J3A9YD6/eYOssqhecL27hK1IPZAsaqh00i/Jljg==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "vscode-jsonrpc": "8.2.0", + "vscode-languageserver-types": "3.17.5" + } + }, + "node_modules/vscode-languageserver-textdocument": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/vscode-languageserver-textdocument/-/vscode-languageserver-textdocument-1.0.12.tgz", + "integrity": "sha512-cxWNPesCnQCcMPeenjKKsOCKQZ/L6Tv19DTRIGuLWe32lyzWhihGVJ/rcckZXJxfdKCFvRLS3fpBIsV/ZGX4zA==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/vscode-languageserver-types": { + "version": "3.17.5", + "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.17.5.tgz", + "integrity": "sha512-Ld1VelNuX9pdF39h2Hgaeb5hEZM2Z3jUrrMgWQAu82jMtZp7p3vJT3BzToKtZI7NgQssZje5o0zryOrhQvzQAg==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/vscode-uri": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-3.1.0.tgz", + "integrity": "sha512-/BpdSx+yCQGnCvecbyXdxHDkuk55/G3xwnC0GqY4gmQ3j+A+g8kzzgB4Nk/SINjqn6+waqw3EgbVF2QKExkRxQ==", + "dev": true, + "license": "MIT", + "peer": true + }, "node_modules/vue": { "version": "3.5.31", "resolved": "https://registry.npmjs.org/vue/-/vue-3.5.31.tgz", diff --git a/package.json b/package.json index 85a667f..cae6dac 100644 --- a/package.json +++ b/package.json @@ -10,7 +10,8 @@ "husky": "^9.1.7", "lint-staged": "^16.4.0", "prettier": "^3.8.1", - "vitepress": "^2.0.0-alpha.17" + "vitepress": "^2.0.0-alpha.17", + "vitepress-mermaid-renderer": "^1.1.20" }, "scripts": { "dev": "vitepress dev", From cc9b7207bf23c9a3fa99070c770e54534aaa6871 Mon Sep 17 00:00:00 2001 From: Morgan Roderick Date: Fri, 3 Apr 2026 22:37:39 +0200 Subject: [PATCH 07/14] feat: configure mermaid renderer in vitepress theme --- .vitepress/theme/index.js | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 .vitepress/theme/index.js diff --git a/.vitepress/theme/index.js b/.vitepress/theme/index.js new file mode 100644 index 0000000..122808b --- /dev/null +++ b/.vitepress/theme/index.js @@ -0,0 +1,30 @@ +import { h, nextTick, watch } from "vue"; +import DefaultTheme from "vitepress/theme"; +import { useData } from "vitepress"; +import { createMermaidRenderer } from "vitepress-mermaid-renderer"; + +export default { + extends: DefaultTheme, + Layout: () => { + const { isDark } = useData(); + + const initMermaid = () => { + const _mermaidRenderer = createMermaidRenderer({ + theme: isDark.value ? "dark" : "forest" + }); + }; + + // initial mermaid setup + nextTick(() => initMermaid()); + + // on theme change, re-render mermaid charts + watch( + () => isDark.value, + () => { + initMermaid(); + } + ); + + return h(DefaultTheme.Layout); + } +}; From 28d1e549774385af6ca9efb5647c789c3b03a753 Mon Sep 17 00:00:00 2001 From: Morgan Roderick Date: Fri, 3 Apr 2026 22:43:37 +0200 Subject: [PATCH 08/14] docs: add learn section index page --- docs/learn/index.md | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 docs/learn/index.md diff --git a/docs/learn/index.md b/docs/learn/index.md new file mode 100644 index 0000000..6168846 --- /dev/null +++ b/docs/learn/index.md @@ -0,0 +1,28 @@ +# Learn + +Idempotency is essential for reliable distributed systems. When networks fail and clients retry requests, idempotency prevents duplicate transactions—no double charges, no duplicate orders. + +## Key Concepts + +### Why Idempotency Matters + +Every API that processes payments, creates orders, or modifies state needs idempotency. Without it, network failures and client retries create duplicate transactions. **[Learn why →](/learn/why)** + +### Duplicated vs Repeated Operations + +Idempotency protects against duplicates from retries while allowing legitimate repeated operations. Use a different idempotency key for each distinct business operation. **[Learn the difference →](/learn/duplicated-vs-repeated)** + +### Client Key Strategies + +How should you generate idempotency keys? Learn patterns for managing keys in your client applications. **[See strategies →](/learn/client-key-strategies)** + +### IETF Specification + +This library implements the IETF draft standard for idempotency keys. Understanding the spec helps you implement idempotency correctly and interoperate with other systems. **[Read the spec compliance guide →](/learn/spec)** + +## What You'll Learn + +- The problem duplicates create in distributed systems +- How the idempotency-key pattern works +- What the IETF specification requires +- Implementation details for each requirement From 822f074dd86790408b6212f7da7bae23ea6f78d5 Mon Sep 17 00:00:00 2001 From: Morgan Roderick Date: Fri, 3 Apr 2026 22:46:30 +0200 Subject: [PATCH 09/14] docs: add why idempotency page with mermaid diagrams --- docs/learn/why.md | 71 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 71 insertions(+) create mode 100644 docs/learn/why.md diff --git a/docs/learn/why.md b/docs/learn/why.md new file mode 100644 index 0000000..6739d2d --- /dev/null +++ b/docs/learn/why.md @@ -0,0 +1,71 @@ +# Why Idempotency Matters + +In distributed systems, networks timeout, load balancers retry, users double-click. Without idempotency, these failures create duplicate transactions: double charges, duplicate orders, inconsistent state. + +## The Problem + +Duplicate requests happen more often than you'd think: + +| Cause | Example | +| ---------------- | ------------------------------------- | +| User behavior | Double-clicking a submit button | +| Client retries | Automatic retry on connection timeout | +| Network issues | Request succeeds but response is lost | +| Load balancers | Backend timeout triggers retry | +| Webhook delivery | Provider retries failed deliveries | + +```mermaid +sequenceDiagram + participant Client + participant Server + + Client->>Server: POST /api/transfers + Server-->>Client: 201 Created (response LOST) + Note over Client: Network timeout
Client retries... + Client->>Server: POST /api/transfers + Server-->>Client: 201 Created + Note over Server: ❌ Duplicate created +``` + +Each duplicate request creates side effects: duplicate payments, duplicate orders, corrupted data. + +## The Pattern + +Major APIs like Stripe and PayPal use a simple pattern to solve this: + +1. **Client generates a unique key** — typically a UUID for each unique operation +2. **Key sent as header** — `Idempotency-Key: ` +3. **Server stores key + response** — in your database or cache +4. **On duplicate request** — server returns cached response instead of reprocessing + +```mermaid +sequenceDiagram + participant Client + participant Server + + Client->>Server: POST /api/transfers
Idempotency-Key: abc-123 + Note over Server: Store key, process request + Server-->>Client: 201 Created + Note over Client: Network timeout
Client retries... + Client->>Server: POST /api/transfers
Idempotency-Key: abc-123 + Note over Server: Key found
Return cached response + Server-->>Client: 201 Created + Note over Server: ✅ No duplicate +``` + +This makes any request safely retryable. The server either processes it once and caches the result, or recognizes the key and returns the previous result. + +## Benefits + +- **Fault tolerance**: Network interruptions don't cause duplicate transactions +- **Simplified retry logic**: Clients can safely retry without complex deduplication +- **Better UX**: Users don't wonder "did that go through?" +- **API reliability**: Stripe, PayPal, and major processors all use this pattern + +Idempot-js implements the [IETF Idempotency-Key Header draft specification](https://datatracker.ietf.org/doc/html/draft-ietf-httpapi-idempotency-key-header-07) for Node.js, Bun, and Deno applications. + +## Further Learning + + + +**[Try, try again](https://www.youtube.com/watch?v=29NNiZhXe2Q)** — Sam Newman explains the importance of idempotency in distributed systems at LeadDev Berlin 2025. From 35a29a31f6e6164fe84a4038b1864e97bdda0975 Mon Sep 17 00:00:00 2001 From: Morgan Roderick Date: Fri, 3 Apr 2026 22:49:25 +0200 Subject: [PATCH 10/14] docs: add duplicated vs repeated page with framework context --- docs/learn/duplicated-vs-repeated.md | 135 +++++++++++++++++++++++++++ 1 file changed, 135 insertions(+) create mode 100644 docs/learn/duplicated-vs-repeated.md diff --git a/docs/learn/duplicated-vs-repeated.md b/docs/learn/duplicated-vs-repeated.md new file mode 100644 index 0000000..3687067 --- /dev/null +++ b/docs/learn/duplicated-vs-repeated.md @@ -0,0 +1,135 @@ +# Duplicated vs Repeated Operations + +Idempotency protects against **duplicated** operations from network retries while allowing **repeated** operations—new requests with the same business parameters. + +## The Difference + +| Duplicated | Repeated | +| ------------------------------------------------------------------------------------- | ------------------------------------------------------ | +| Same request sent multiple times due to network failures, timeouts, or client retries | New operation that happens to have the same parameters | +| Should return the same response | Should create a new result | +| Protected by idempotency | Allowed by idempotency | + +## Example: Monthly Invoice Payments + +Your company pays the same vendor each month: + +- **January**: Transfer €100 to DE89370400440532013000 for invoice INV-001 +- **February**: Transfer €100 to DE89370400440532013000 for invoice INV-002 + +Same IBAN, same amount, same currency—but two distinct operations. + +### Request Model + +```javascript +// POST /api/transfers +{ + "iban": "DE89370400440532013000", + "amount": 10000, // cents + "currency": "EUR", + "description": "Monthly consulting fee", + "internal_reason": "invoice-550e8400-e29b-41d4-a716-446655440000" +} +``` + +The `internal_reason` field uniquely identifies this payment in your system. + +## How Idempotency Works + +### Duplicated Request (Retry) + +```http +POST /api/transfers +Idempotency-Key: 550e8400-e29b-41d4-a716-446655440000 +Content-Type: application/json + +{ + "iban": "DE89370400440532013000", + "amount": 10000, + "currency": "EUR", + "description": "Monthly consulting fee", + "internal_reason": "invoice-550e8400-e29b-41d4-a716-446655440000" +} +``` + +Network timeout occurs. Client retries: + +```http +POST /api/transfers +Idempotency-Key: 550e8400-e29b-41d4-a716-446655440000 +Content-Type: application/json + +{ + "iban": "DE89370400440532013000", + "amount": 10000, + "currency": "EUR", + "description": "Monthly consulting fee", + "internal_reason": "invoice-550e8400-e29b-41d4-a716-446655440000" +} +``` + +**Same key, same body** → server returns cached response. No double payment. + +### Repeated Operation (New Invoice) + +```http +POST /api/transfers +Idempotency-Key: a1b2c3d4-e5f6-7890-abcd-ef1234567890 +Content-Type: application/json + +{ + "iban": "DE89370400440532013000", + "amount": 10000, + "currency": "EUR", + "description": "Monthly consulting fee", + "internal_reason": "invoice-a1b2c3d4-e5f6-7890-abcd-ef1234567890" +} +``` + +**Different key, different body** (different `internal_reason`) → server processes as new transfer. + +**Choosing a key strategy?** See [Client Key Strategies](/learn/client-key-strategies) for patterns on generating and managing idempotency keys. + +## Server Implementation + +**Note:** The following example uses idempot-js middleware with the Hono framework. For framework-specific implementations, see the [idempot-js documentation](https://github.com/idempot-dev/idempot-js). + +```javascript +import { Hono } from "hono"; +import { idempotency } from "idempot-js/hono"; +import { RedisIdempotencyStore } from "idempot-js/stores/redis"; + +const app = new Hono(); +const store = new RedisIdempotencyStore({ client: redis }); + +app.post("/api/transfers", idempotency({ store }), async (c) => { + const { iban, amount, currency, description, internal_reason } = + await c.req.json(); + + // Process transfer - only executed once per unique idempotency key + const transferId = await processTransfer({ + iban, + amount, + currency, + internal_reason + }); + + return c.json( + { + transferId, + status: "completed", + internal_reason + }, + 201 + ); +}); +``` + +## Summary + +| Scenario | Idempotency Key | Request Body | Fingerprint | Result | +| --------------------- | --------------- | --------------------------- | ----------- | --------------- | +| Retry of same request | Same | Same | Same | Cached response | +| New invoice payment | Different | Different `internal_reason` | Different | New operation | + +The combination of **new key per operation** and **unique `internal_reason` in each request body** ensures retries return cached responses while new operations process normally. From 22a1a806983641d57e07d6c5e62075392f99d79f Mon Sep 17 00:00:00 2001 From: Morgan Roderick Date: Fri, 3 Apr 2026 22:51:48 +0200 Subject: [PATCH 11/14] docs: add IETF spec compliance page with implementation context --- docs/learn/client-key-strategies.md | 77 +++++++++++++++++++++++++++++ docs/learn/spec.md | 60 ++++++++++++++++++++++ 2 files changed, 137 insertions(+) create mode 100644 docs/learn/client-key-strategies.md create mode 100644 docs/learn/spec.md diff --git a/docs/learn/client-key-strategies.md b/docs/learn/client-key-strategies.md new file mode 100644 index 0000000..c374091 --- /dev/null +++ b/docs/learn/client-key-strategies.md @@ -0,0 +1,77 @@ +# Client Key Strategies + +The client generates idempotency keys. Both strategies create a transfer record first: + +## Strategy 1: Random Keys + +Create a transfer record, generate a random UUID, store it on the record: + +```javascript +// Create transfer record first +const transfer = await db.transfers.create({ + supplier_id: supplierId, + invoice_id: invoiceId, + iban, + amount: 10000, + currency: "EUR", + description: "Monthly consulting fee", + internal_reason: `invoice-${supplierId}-${invoiceId}`, + idempotency_key: crypto.randomUUID(), + status: "pending" +}); + +await fetch("/api/transfers", { + method: "POST", + headers: { + "Content-Type": "application/json", + "Idempotency-Key": transfer.idempotency_key + }, + body: JSON.stringify({ + iban: transfer.iban, + amount: transfer.amount, + currency: transfer.currency, + description: transfer.description, + internal_reason: transfer.internal_reason + }) +}); +``` + +**Benefit**: No coupling between your database IDs and external API contracts — you can change your ID scheme without affecting idempotency. + +## Strategy 2: Database ID as Key + +Use the transfer's database-generated ID directly: + +```javascript +// Create transfer record first +const transfer = await db.transfers.create({ + supplier_id: supplierId, + invoice_id: invoiceId, + iban, + amount: 10000, + currency: "EUR", + description: "Monthly consulting fee", + internal_reason: `invoice-${supplierId}-${invoiceId}`, + status: "pending" +}); + +// Use transfer ID directly +const idempotencyKey = transfer.id; + +await fetch("/api/transfers", { + method: "POST", + headers: { + "Content-Type": "application/json", + "Idempotency-Key": idempotencyKey + }, + body: JSON.stringify({ + iban: transfer.iban, + amount: transfer.amount, + currency: transfer.currency, + description: transfer.description, + internal_reason: transfer.internal_reason + }) +}); +``` + +**Benefit**: Single source of truth — the transfer ID is your idempotency key. diff --git a/docs/learn/spec.md b/docs/learn/spec.md new file mode 100644 index 0000000..54a1959 --- /dev/null +++ b/docs/learn/spec.md @@ -0,0 +1,60 @@ +# IETF Spec Compliance + +The idempot-js library implements [draft-ietf-httpapi-idempotency-key-header-07](https://datatracker.ietf.org/doc/html/draft-ietf-httpapi-idempotency-key-header-07), the IETF standard for the Idempotency-Key HTTP header. + +This document details how the library complies with each requirement in the specification. + +## Implemented Requirements + +**Note:** This page describes idempot-js implementation of the IETF specification. For other implementations, refer to your framework's documentation. + +### MUST Requirements (Required) + +| Requirement | Section | Implementation | +| ------------------------------------ | ------- | ------------------------------------------------------------------------- | +| Idempotency-Key as String | 2.1 | ✅ Header value extracted as string | +| Unique idempotency keys | 2.2 | ✅ Key stored with request fingerprint to ensure uniqueness | +| Identify idempotency key from header | 2.5.2 | ✅ Parses `Idempotency-Key` header (configurable via `headerName` option) | +| Generate idempotency fingerprint | 2.5.2 | ✅ SHA-256 hash of method + path + body | +| Enforce idempotency | 2.6 | ✅ Returns cached response for duplicate requests | + +### SHOULD Requirements (Recommended) + +| Requirement | Section | Implementation | +| ----------------------------------------------- | ------- | ------------------------------------------------------------------------------ | +| Use UUID or random identifier | 2.2 | ✅ Library doesn't generate keys (client responsibility), but validates format | +| Publish expiration policy | 2.3 | ✅ Configurable via `ttlMs` option | +| Return 400 if key missing | 2.7 | ✅ Optional via `required: true` option | +| Return 422 if key reused with different payload | 2.7 | ✅ Returns 422 with problem details when fingerprint mismatch detected | +| Return 409 for concurrent requests | 2.7 | ✅ Returns 409 Conflict when original request still processing | + +### MAY Requirements (Optional) + +| Requirement | Section | Implementation | +| ----------------------- | ------- | --------------------------------------------------------------------------------- | +| Idempotency fingerprint | 2.4 | ✅ SHA-256 hash of method + path + body (configurable via `hashAlgorithm` option) | +| Time-based expiry | 2.3 | ✅ Configurable via `ttlMs` option, defaults to 24 hours | + +## Error Responses + +The library follows the spec's error handling recommendations: + +| Scenario | Status Code | Response | +| --------------------------------------- | ----------- | ----------------------------------------------------------- | +| Missing Idempotency-Key (when required) | 400 | Problem Details JSON with link to documentation | +| Key reused with different payload | 422 | Problem Details JSON with "Idempotency-Key is already used" | +| Concurrent request (still processing) | 409 | Problem Details JSON with "request is outstanding" | + +## What's Not Covered + +The spec leaves some things to the application layer: + +- **Key format**: The spec recommends UUIDs, but the library accepts any string value. +- **Store implementation**: The spec doesn't mandate storage implementation. We provide Redis, PostgreSQL, MySQL, SQLite, and Bun SQL stores. +- **Key generation**: The spec says clients should generate keys. We don't generate keys—clients provide them. + +## Compliance Status + +✅ **Full compliance** with draft-ietf-httpapi-idempotency-key-header-07 + +All MUST and SHOULD requirements are implemented. The library gives you the flexibility to choose which optional features to enable based on your API's needs. From 9c42042664fc4afc4abb3f440e11a4c7700d9c41 Mon Sep 17 00:00:00 2001 From: Morgan Roderick Date: Fri, 3 Apr 2026 22:53:20 +0200 Subject: [PATCH 12/14] feat: update navigation to add learn section, remove why-idempotency --- .vitepress/config.mjs | 55 ++++++++++++++++++++++++++++--------------- 1 file changed, 36 insertions(+), 19 deletions(-) diff --git a/.vitepress/config.mjs b/.vitepress/config.mjs index 34f8e72..869b033 100644 --- a/.vitepress/config.mjs +++ b/.vitepress/config.mjs @@ -29,27 +29,44 @@ export default defineConfig({ lastUpdated: true, nav: [ { text: "Home", link: "/" }, - { text: "Why Idempotency", link: "/why-idempotency" }, + { text: "Learn", link: "/learn/" }, { text: "Specs", link: "/specs" } ], - sidebar: [ - { - text: "Documentation", - items: [ - { text: "Why Idempotency", link: "/why-idempotency" }, - { text: "Specifications", link: "/specs" } - ] - }, - { - text: "Projects", - items: [ - { - text: "idempot-js", - link: "https://github.com/idempot-dev/idempot-js" - } - ] - } - ], + sidebar: { + "/learn/": [ + { + text: "Learn", + items: [ + { text: "Overview", link: "/learn/" }, + { text: "Why Idempotency", link: "/learn/why" }, + { + text: "Duplicated vs Repeated", + link: "/learn/duplicated-vs-repeated" + }, + { + text: "Client Key Strategies", + link: "/learn/client-key-strategies" + }, + { text: "Spec Compliance", link: "/learn/spec" } + ] + } + ], + "/": [ + { + text: "Documentation", + items: [{ text: "Specifications", link: "/specs" }] + }, + { + text: "Projects", + items: [ + { + text: "idempot-js", + link: "https://github.com/idempot-dev/idempot-js" + } + ] + } + ] + }, socialLinks: [{ icon: "github", link: "https://github.com/idempot-dev" }] } }); From f0eae2fddc8f1d2d8b5d64ea59a79f800007542f Mon Sep 17 00:00:00 2001 From: Morgan Roderick Date: Fri, 3 Apr 2026 22:55:53 +0200 Subject: [PATCH 13/14] docs: remove old why-idempotency page (replaced by learn section) --- docs/why-idempotency.md | 44 ----------------------------------------- 1 file changed, 44 deletions(-) delete mode 100644 docs/why-idempotency.md diff --git a/docs/why-idempotency.md b/docs/why-idempotency.md deleted file mode 100644 index 3b334a2..0000000 --- a/docs/why-idempotency.md +++ /dev/null @@ -1,44 +0,0 @@ ---- -prev: false -next: - text: "Specifications" - link: "/specs" ---- - -# Why Idempotency Matters - -In distributed systems, requests fail. Networks timeout, load balancers retry, users double-click. Without protection, these failures create duplicate transactions—double charges, duplicate orders, inconsistent state. - -## The Problem - -Duplicate requests happen more often than you'd think: - -| Cause | Example | -| ---------------- | ------------------------------------- | -| User behavior | Double-clicking a submit button | -| Client retries | Automatic retry on connection timeout | -| Network issues | Request succeeds but response is lost | -| Load balancers | Backend timeout triggers retry | -| Webhook delivery | Provider retries failed deliveries | - -Each duplicate request can create unintended side effects: duplicate payments, multiple orders, inconsistent data. - -## The Pattern - -Major APIs like Stripe and PayPal use a simple pattern to solve this: - -1. **Client generates a unique key** — typically a UUID for each unique operation -2. **Key sent as header** — `Idempotency-Key: ` -3. **Server stores key + response** — in your database or cache -4. **On duplicate request** — server returns cached response instead of reprocessing - -This makes any request safely retryable. The server either processes it once and caches the result, or recognizes the key and returns the previous result. - -## Benefits - -- **Fault tolerance**: Network interruptions don't cause duplicate transactions -- **Simplified retry logic**: Clients can safely retry without complex deduplication -- **Better UX**: Users don't wonder "did that go through?" -- **API reliability**: Stripe, PayPal, and major processors all use this pattern - -The IETF is standardizing this pattern in [draft-ietf-httpapi-idempotency-key-header-07](https://datatracker.ietf.org/doc/html/draft-ietf-httpapi-idempotency-key-header-07). From 3a26250c2f55df46c24be6b7ac6f681aff5ac693 Mon Sep 17 00:00:00 2001 From: Morgan Roderick Date: Fri, 3 Apr 2026 23:18:17 +0200 Subject: [PATCH 14/14] docs: add links to idempot-js documentation --- docs/learn/duplicated-vs-repeated.md | 2 +- docs/learn/spec.md | 4 ++-- docs/learn/why.md | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/learn/duplicated-vs-repeated.md b/docs/learn/duplicated-vs-repeated.md index 3687067..4fe0d17 100644 --- a/docs/learn/duplicated-vs-repeated.md +++ b/docs/learn/duplicated-vs-repeated.md @@ -92,7 +92,7 @@ Content-Type: application/json ## Server Implementation -**Note:** The following example uses idempot-js middleware with the Hono framework. For framework-specific implementations, see the [idempot-js documentation](https://github.com/idempot-dev/idempot-js). +**Note:** The following example uses [idempot-js](https://js.idempot.dev) middleware with the Hono framework. For framework-specific implementations, see the [idempot-js documentation](https://js.idempot.dev). ```javascript import { Hono } from "hono"; diff --git a/docs/learn/spec.md b/docs/learn/spec.md index 54a1959..100fe99 100644 --- a/docs/learn/spec.md +++ b/docs/learn/spec.md @@ -1,12 +1,12 @@ # IETF Spec Compliance -The idempot-js library implements [draft-ietf-httpapi-idempotency-key-header-07](https://datatracker.ietf.org/doc/html/draft-ietf-httpapi-idempotency-key-header-07), the IETF standard for the Idempotency-Key HTTP header. +The [idempot-js](https://js.idempot.dev) library implements [draft-ietf-httpapi-idempotency-key-header-07](https://datatracker.ietf.org/doc/html/draft-ietf-httpapi-idempotency-key-header-07), the IETF standard for the Idempotency-Key HTTP header. This document details how the library complies with each requirement in the specification. ## Implemented Requirements -**Note:** This page describes idempot-js implementation of the IETF specification. For other implementations, refer to your framework's documentation. +**Note:** This page describes [idempot-js](https://js.idempot.dev) implementation of the IETF specification. ### MUST Requirements (Required) diff --git a/docs/learn/why.md b/docs/learn/why.md index 6739d2d..45a9809 100644 --- a/docs/learn/why.md +++ b/docs/learn/why.md @@ -62,7 +62,7 @@ This makes any request safely retryable. The server either processes it once and - **Better UX**: Users don't wonder "did that go through?" - **API reliability**: Stripe, PayPal, and major processors all use this pattern -Idempot-js implements the [IETF Idempotency-Key Header draft specification](https://datatracker.ietf.org/doc/html/draft-ietf-httpapi-idempotency-key-header-07) for Node.js, Bun, and Deno applications. +[idempot-js](https://js.idempot.dev) implements the [IETF Idempotency-Key Header draft specification](https://datatracker.ietf.org/doc/html/draft-ietf-httpapi-idempotency-key-header-07) for Node.js, Bun, and Deno applications. ## Further Learning