From c89786f81558768a851ee11468e0bd8153c884d4 Mon Sep 17 00:00:00 2001 From: Josh Ribakoff Date: Sun, 14 Dec 2025 23:40:40 -0800 Subject: [PATCH 01/35] Add documentation website that dogfoods all 5 Sailkit packages MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Creates a full Astro documentation site in docs/ that demonstrates: - Compass: Sidebar navigation structure + prev/next links - Teleport: Vim keybindings (j/k, h/l, Ctrl+d/u) - Lantern: Dark/light theme toggle with flash prevention - Lighthouse: Smart 404 with fuzzy URL matching - Atlas: Wiki-style [[magic-links]] throughout content Includes 16 documentation pages covering: - Introduction and getting started guides - Individual package documentation - Task-based guides (vim-navigation, theming, magic-links, smart-404) - Architecture overview πŸ€– Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- docs/.gitignore | 24 ++ docs/.vscode/extensions.json | 4 + docs/.vscode/launch.json | 11 + docs/README.md | 43 ++++ docs/astro.config.mjs | 15 ++ docs/package.json | 19 ++ docs/public/favicon.svg | 9 + docs/src/components/PrevNext.astro | 110 ++++++++ docs/src/components/Sidebar.astro | 141 ++++++++++ docs/src/content/config.ts | 13 + docs/src/content/docs/architecture.md | 293 +++++++++++++++++++++ docs/src/content/docs/atlas.md | 201 +++++++++++++++ docs/src/content/docs/compass.md | 176 +++++++++++++ docs/src/content/docs/getting-started.md | 64 +++++ docs/src/content/docs/guides.md | 85 ++++++ docs/src/content/docs/installation.md | 60 +++++ docs/src/content/docs/introduction.md | 52 ++++ docs/src/content/docs/lantern.md | 192 ++++++++++++++ docs/src/content/docs/lighthouse.md | 200 ++++++++++++++ docs/src/content/docs/magic-links.md | 253 ++++++++++++++++++ docs/src/content/docs/packages.md | 81 ++++++ docs/src/content/docs/quick-start.md | 147 +++++++++++ docs/src/content/docs/smart-404.md | 279 ++++++++++++++++++++ docs/src/content/docs/teleport.md | 211 +++++++++++++++ docs/src/content/docs/theming.md | 265 +++++++++++++++++++ docs/src/content/docs/vim-navigation.md | 269 +++++++++++++++++++ docs/src/layouts/DocsLayout.astro | 315 +++++++++++++++++++++++ docs/src/navigation.ts | 59 +++++ docs/src/pages/404.astro | 191 ++++++++++++++ docs/src/pages/docs/[...slug].astro | 19 ++ docs/src/pages/index.astro | 285 ++++++++++++++++++++ docs/tsconfig.json | 5 + package.json | 3 +- 33 files changed, 4093 insertions(+), 1 deletion(-) create mode 100644 docs/.gitignore create mode 100644 docs/.vscode/extensions.json create mode 100644 docs/.vscode/launch.json create mode 100644 docs/README.md create mode 100644 docs/astro.config.mjs create mode 100644 docs/package.json create mode 100644 docs/public/favicon.svg create mode 100644 docs/src/components/PrevNext.astro create mode 100644 docs/src/components/Sidebar.astro create mode 100644 docs/src/content/config.ts create mode 100644 docs/src/content/docs/architecture.md create mode 100644 docs/src/content/docs/atlas.md create mode 100644 docs/src/content/docs/compass.md create mode 100644 docs/src/content/docs/getting-started.md create mode 100644 docs/src/content/docs/guides.md create mode 100644 docs/src/content/docs/installation.md create mode 100644 docs/src/content/docs/introduction.md create mode 100644 docs/src/content/docs/lantern.md create mode 100644 docs/src/content/docs/lighthouse.md create mode 100644 docs/src/content/docs/magic-links.md create mode 100644 docs/src/content/docs/packages.md create mode 100644 docs/src/content/docs/quick-start.md create mode 100644 docs/src/content/docs/smart-404.md create mode 100644 docs/src/content/docs/teleport.md create mode 100644 docs/src/content/docs/theming.md create mode 100644 docs/src/content/docs/vim-navigation.md create mode 100644 docs/src/layouts/DocsLayout.astro create mode 100644 docs/src/navigation.ts create mode 100644 docs/src/pages/404.astro create mode 100644 docs/src/pages/docs/[...slug].astro create mode 100644 docs/src/pages/index.astro create mode 100644 docs/tsconfig.json diff --git a/docs/.gitignore b/docs/.gitignore new file mode 100644 index 0000000..16d54bb --- /dev/null +++ b/docs/.gitignore @@ -0,0 +1,24 @@ +# build output +dist/ +# generated types +.astro/ + +# dependencies +node_modules/ + +# logs +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* + + +# environment variables +.env +.env.production + +# macOS-specific files +.DS_Store + +# jetbrains setting folder +.idea/ diff --git a/docs/.vscode/extensions.json b/docs/.vscode/extensions.json new file mode 100644 index 0000000..22a1505 --- /dev/null +++ b/docs/.vscode/extensions.json @@ -0,0 +1,4 @@ +{ + "recommendations": ["astro-build.astro-vscode"], + "unwantedRecommendations": [] +} diff --git a/docs/.vscode/launch.json b/docs/.vscode/launch.json new file mode 100644 index 0000000..d642209 --- /dev/null +++ b/docs/.vscode/launch.json @@ -0,0 +1,11 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "command": "./node_modules/.bin/astro dev", + "name": "Development server", + "request": "launch", + "type": "node-terminal" + } + ] +} diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 0000000..87b813a --- /dev/null +++ b/docs/README.md @@ -0,0 +1,43 @@ +# Astro Starter Kit: Minimal + +```sh +npm create astro@latest -- --template minimal +``` + +> πŸ§‘β€πŸš€ **Seasoned astronaut?** Delete this file. Have fun! + +## πŸš€ Project Structure + +Inside of your Astro project, you'll see the following folders and files: + +```text +/ +β”œβ”€β”€ public/ +β”œβ”€β”€ src/ +β”‚ └── pages/ +β”‚ └── index.astro +└── package.json +``` + +Astro looks for `.astro` or `.md` files in the `src/pages/` directory. Each page is exposed as a route based on its file name. + +There's nothing special about `src/components/`, but that's where we like to put any Astro/React/Vue/Svelte/Preact components. + +Any static assets, like images, can be placed in the `public/` directory. + +## 🧞 Commands + +All commands are run from the root of the project, from a terminal: + +| Command | Action | +| :------------------------ | :----------------------------------------------- | +| `npm install` | Installs dependencies | +| `npm run dev` | Starts local dev server at `localhost:4321` | +| `npm run build` | Build your production site to `./dist/` | +| `npm run preview` | Preview your build locally, before deploying | +| `npm run astro ...` | Run CLI commands like `astro add`, `astro check` | +| `npm run astro -- --help` | Get help using the Astro CLI | + +## πŸ‘€ Want to learn more? + +Feel free to check [our documentation](https://docs.astro.build) or jump into our [Discord server](https://astro.build/chat). diff --git a/docs/astro.config.mjs b/docs/astro.config.mjs new file mode 100644 index 0000000..d54c871 --- /dev/null +++ b/docs/astro.config.mjs @@ -0,0 +1,15 @@ +// @ts-check +import { defineConfig } from 'astro/config'; +import { remarkMagicLinks } from '@sailkit/atlas'; + +// https://astro.build/config +export default defineConfig({ + markdown: { + remarkPlugins: [ + [remarkMagicLinks, { + urlBuilder: (id) => `/docs/${id}/`, + syntax: 'wiki', + }], + ], + }, +}); diff --git a/docs/package.json b/docs/package.json new file mode 100644 index 0000000..51af389 --- /dev/null +++ b/docs/package.json @@ -0,0 +1,19 @@ +{ + "name": "docs", + "type": "module", + "version": "0.0.1", + "scripts": { + "dev": "astro dev", + "build": "astro build", + "preview": "astro preview", + "astro": "astro" + }, + "dependencies": { + "astro": "^5.16.5", + "@sailkit/compass": "*", + "@sailkit/teleport": "*", + "@sailkit/lantern": "*", + "@sailkit/lighthouse": "*", + "@sailkit/atlas": "*" + } +} diff --git a/docs/public/favicon.svg b/docs/public/favicon.svg new file mode 100644 index 0000000..f157bd1 --- /dev/null +++ b/docs/public/favicon.svg @@ -0,0 +1,9 @@ + + + + diff --git a/docs/src/components/PrevNext.astro b/docs/src/components/PrevNext.astro new file mode 100644 index 0000000..71fede9 --- /dev/null +++ b/docs/src/components/PrevNext.astro @@ -0,0 +1,110 @@ +--- +import { getTitle } from '../navigation'; + +interface Props { + prev: string | null; + next: string | null; +} + +const { prev, next } = Astro.props; +--- + +{(prev || next) && ( + +)} + + diff --git a/docs/src/components/Sidebar.astro b/docs/src/components/Sidebar.astro new file mode 100644 index 0000000..65bb290 --- /dev/null +++ b/docs/src/components/Sidebar.astro @@ -0,0 +1,141 @@ +--- +import { isBranch, getSlug } from '@sailkit/compass'; +import type { NavItem } from '@sailkit/compass'; +import { navigation, getTitle } from '../navigation'; + +interface Props { + currentSlug: string; +} + +const { currentSlug } = Astro.props; + +function isActive(slug: string): boolean { + return currentSlug === slug; +} + +function isInSection(item: NavItem, targetSlug: string): boolean { + if (!isBranch(item)) return item === targetSlug; + if (item.slug === targetSlug) return true; + return item.children.some(child => isInSection(child, targetSlug)); +} +--- + + + + diff --git a/docs/src/content/config.ts b/docs/src/content/config.ts new file mode 100644 index 0000000..cffd600 --- /dev/null +++ b/docs/src/content/config.ts @@ -0,0 +1,13 @@ +import { defineCollection, z } from 'astro:content'; + +const docs = defineCollection({ + type: 'content', + schema: z.object({ + title: z.string(), + description: z.string().optional(), + }), +}); + +export const collections = { + docs, +}; diff --git a/docs/src/content/docs/architecture.md b/docs/src/content/docs/architecture.md new file mode 100644 index 0000000..02deda8 --- /dev/null +++ b/docs/src/content/docs/architecture.md @@ -0,0 +1,293 @@ +--- +title: Architecture +description: How Sailkit packages work together. +--- + +# Architecture + +This page explains how Sailkit's five packages compose into a cohesive documentation system. + +## System Overview + +``` +β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” +β”‚ Documentation Site β”‚ +β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€ +β”‚ β”‚ +β”‚ BUILD TIME RUNTIME β”‚ +β”‚ ─────────── ─────── β”‚ +β”‚ β”‚ +β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ +β”‚ β”‚ Atlas β”‚ β”‚ Teleport β”‚ β”‚ +β”‚ β”‚ (Remark) β”‚ β”‚ (Keyboard)β”‚ β”‚ +β”‚ β””β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”˜ β”‚ +β”‚ β”‚ β”‚ β”‚ +β”‚ β–Ό β–Ό β”‚ +β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ +β”‚ β”‚ Markdown │───▢│ HTML │────▢│ DOM β”‚ β”‚ +β”‚ β”‚ Files β”‚ β”‚ Pages β”‚ β”‚ (Browser) β”‚ β”‚ +β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”˜ β”‚ +β”‚ β”‚ β”‚ β”‚ +β”‚ β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ β”‚ +β”‚ └─────────▢│ Compass β”‚β—€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ +β”‚ β”‚(Nav State)β”‚ β”‚ +β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ +β”‚ β”‚ +β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ +β”‚ β”‚ Lantern β”‚ β”‚Lighthouse β”‚ β”‚ +β”‚ β”‚ (Theming) β”‚ β”‚ (404) β”‚ β”‚ +β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ +β”‚ β”‚ +β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ +``` + +## Package Responsibilities + +### [[atlas|Atlas]] - Build Time + +**When**: During `astro build` or `astro dev` +**What**: Transforms `[[wiki-links]]` to standard HTML links +**How**: Remark plugin in markdown pipeline + +``` +author.md author.html +───────── ──────────── +See [[compass]] β†’ See compass +``` + +Atlas has zero runtime footprintβ€”it's purely a build-time transformation. + +### [[compass|Compass]] - Build + Runtime + +**Build time**: Generate prev/next links statically + +```typescript +// In Astro component +const { prev, next } = getNeighbors(navigation, slug); +``` + +**Runtime**: Manage navigation state in SPAs + +```typescript +// In client JavaScript +const nav = createNavigator({ items: navigation }); +nav.next(); +``` + +Compass provides the navigation data structure that other packages consume. + +### [[teleport|Teleport]] - Runtime + +**When**: In the browser, after page load +**What**: Handles keyboard events, manages DOM highlights +**How**: Event listeners + DOM manipulation + +``` +User presses 'j' β†’ Teleport intercepts β†’ Highlights next nav item +User presses 'l' β†’ Teleport calls onNextPage() β†’ Your code navigates +``` + +Teleport can work standalone or integrate with Compass for intelligent page navigation. + +### [[lantern|Lantern]] - Build + Runtime + +**Build time**: Inject inline script for flash prevention + +```astro + From 7706d118b314287255e1a304bc63d29ec6c0d1b6 Mon Sep 17 00:00:00 2001 From: Josh Ribakoff Date: Mon, 15 Dec 2025 00:13:07 -0800 Subject: [PATCH 03/35] Add documentation for Spyglass and Scribe packages MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add spyglass.md: Site search UI layer (adapts Fuse.js/MiniSearch/Pagefind) - Add scribe.md: Documentation testing (extract and verify code blocks) - Update packages.md: Now 7 packages (5 implemented, 2 planned) - Update navigation.ts: Add spyglass and scribe to sidebar - Update comparison table with status column πŸ€– Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- docs/src/content/docs/packages.md | 46 +++++++--- docs/src/content/docs/scribe.md | 78 +++++++++++++++++ docs/src/content/docs/spyglass.md | 136 ++++++++++++++++++++++++++++++ docs/src/navigation.ts | 4 + 4 files changed, 254 insertions(+), 10 deletions(-) create mode 100644 docs/src/content/docs/scribe.md create mode 100644 docs/src/content/docs/spyglass.md diff --git a/docs/src/content/docs/packages.md b/docs/src/content/docs/packages.md index 3e7a80c..f15b52b 100644 --- a/docs/src/content/docs/packages.md +++ b/docs/src/content/docs/packages.md @@ -5,9 +5,9 @@ description: Detailed documentation for each Sailkit package. # Packages -Sailkit consists of five focused packages. Each solves one problem well and can be used independently or together. +Sailkit consists of seven packages. Five are implemented, two are planned. Each solves one problem well and can be used independently or together. -## The Five Packages +## Implemented Packages ### [[compass]] - Navigation State @@ -56,6 +56,28 @@ Wikipedia-style link syntax for markdown. Write `[[page]]` instead of `[page](/d - Colon syntax: `[:page]` for alternative style - Remark plugin for Astro/Next.js/etc. +## Planned Packages + +### [[spyglass]] - Site Search UI + +Command palette and sidebar filtering. A **UI layer** that integrates with search engines like Fuse.js, MiniSearch, or Pagefind. + +**Planned features:** +- Command palette modal (⌘K) +- Inline sidebar filter mode +- Adapters for multiple search engines +- Keyboard navigation within results + +### [[scribe]] - Documentation Testing + +Extract and test code from markdown documentation. Prevents documentation rot by verifying code examples actually work. + +**Planned features:** +- Extract code blocks from markdown +- Language-aware execution (TS, JS, bash) +- Skip patterns for incomplete examples +- CI integration + ## Package Dependencies ``` @@ -63,19 +85,23 @@ atlas ─────────────────┐ lighthouse ───────────── lantern ───────────────┼─► Your App teleport ─────────────── -compass β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ +compass ──────────────── +spyglass (planned) ───── +scribe (planned) β”€β”€β”€β”€β”€β”€β”˜ ``` No Sailkit package depends on another. Use any combination you need. ## Comparison Table -| Package | Build-time | Runtime | Astro Component | Headless | -|---------|------------|---------|-----------------|----------| -| [[compass]] | Yes | Yes | No | Yes | -| [[teleport]] | No | Yes | Yes | Partial | -| [[lantern]] | Partial | Yes | Yes | Yes | -| [[lighthouse]] | No | Yes | Yes | Yes | -| [[atlas]] | Yes | No | No | Yes | +| Package | Status | Build-time | Runtime | Astro Component | Headless | +|---------|--------|------------|---------|-----------------|----------| +| [[compass]] | Implemented | Yes | Yes | No | Yes | +| [[teleport]] | Implemented | No | Yes | Yes | Partial | +| [[lantern]] | Implemented | Partial | Yes | Yes | Yes | +| [[lighthouse]] | Implemented | No | Yes | Yes | Yes | +| [[atlas]] | Implemented | Yes | No | No | Yes | +| [[spyglass]] | Planned | No | Yes | Yes | No | +| [[scribe]] | Planned | Yes | Yes | No | Yes | Select a package from the sidebar to learn more. diff --git a/docs/src/content/docs/scribe.md b/docs/src/content/docs/scribe.md new file mode 100644 index 0000000..8458231 --- /dev/null +++ b/docs/src/content/docs/scribe.md @@ -0,0 +1,78 @@ +--- +title: Scribe +description: Extract and test code from markdown documentation. +--- + +# Scribe + +Extract and test code from markdown documentation. + +> **Status**: Planned β€” not yet implemented + +## The Problem + +Documentation code examples drift out of sync with actual APIs. AI assistants and humans alike write examples that look plausible but don't compile or run correctly. Traditional testing ignores documentation entirely. + +## The Solution + +Scribe treats code fences as testable units: + +```typescript +import { extractCodeBlocks, testCodeBlocks } from '@sailkit/scribe'; + +const blocks = await extractCodeBlocks('./docs/**/*.md'); +const results = await testCodeBlocks(blocks); + +results.forEach(result => { + if (!result.passed) { + console.error(`Failed: ${result.file}:${result.line}`); + } +}); +``` + +## Features + +### Language-Aware Execution + +Different strategies for different code blocks: + +- **TypeScript/JavaScript**: Transpile and execute +- **Bash**: Execute in shell, verify exit code +- **JSON**: Parse and validate structure +- **Custom**: Register your own runners + +### Skip Patterns + +Mark blocks that shouldn't be tested: + +````markdown +```typescript skip +// This example is intentionally incomplete +const partial = +``` +```` + +### CI Integration + +```bash +npx scribe test ./docs/**/*.md --fail-fast +``` + +## Use Cases + +- **CI pipelines**: Fail builds when examples break +- **Pre-commit hooks**: Catch issues before merge +- **Documentation audits**: Find stale examples across a codebase +- **Tutorial validation**: Ensure learning materials work + +## Philosophy + +Documentation is code. Code should be tested. Therefore documentation should be tested. + +## Dogfooding Opportunity + +This documentation site itself contains dozens of code examples. Once Scribe is implemented, we can use it to verify that all the examples in these docs actually work. + +## Learn More + +See the full design specification in the [scribe package README](https://github.com/joshribakoff/sailkit/tree/scribe-codefence-testing/packages/scribe). diff --git a/docs/src/content/docs/spyglass.md b/docs/src/content/docs/spyglass.md new file mode 100644 index 0000000..1ceafc4 --- /dev/null +++ b/docs/src/content/docs/spyglass.md @@ -0,0 +1,136 @@ +--- +title: Spyglass +description: Site search UI with fuzzy filtering and command palette. +--- + +# Spyglass + +Site search UI with fuzzy filtering and command palette. + +> **Status**: Planned β€” not yet implemented + +## What It Does + +Spyglass is a **UI layer** for site search. It provides: + +- Command palette modal (⌘K) +- Sidebar filter input +- Search results rendering +- Keyboard navigation within results + +Spyglass does **not** implement fuzzy matching itself. It's an adapter that integrates with existing search libraries like Fuse.js, MiniSearch, or Pagefind. + +## Architecture + +``` +β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” +β”‚ Spyglass UI β”‚ +β”‚ (modal, input, results, highlighting) β”‚ +β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ + β”‚ +β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” +β”‚ Adapter Layer β”‚ +β”‚ (normalize input/output for engines) β”‚ +β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ + β”‚ +β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” +β”‚ Search Engine (BYO) β”‚ +β”‚ Fuse.js β”‚ MiniSearch β”‚ Pagefind β”‚ etc β”‚ +β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ +``` + +## Why an Adapter? + +Search engines have different APIs: + +```typescript +// Fuse.js +fuse.search('query') + +// MiniSearch +miniSearch.search('query', { fuzzy: 0.2 }) + +// Pagefind +await pagefind.search('query') +``` + +Spyglass normalizes these into a consistent interface and renders the UI. + +## Integration with Sailkit + +### With [[compass]] + +Build searchable items from your Compass navigation structure: + +```typescript +import { flattenSlugs } from '@sailkit/compass'; +import { Spyglass } from '@sailkit/spyglass'; + +const slugs = flattenSlugs(navigation, true); +const items = slugs.map(slug => ({ + id: slug, + title: getTitle(slug), + url: `/docs/${slug}/`, +})); + +// Pass items to your search engine, then to Spyglass +``` + +### With [[teleport]] + +Teleport emits `teleport:open-finder` when user presses `t`. Wire it up: + +```typescript +document.addEventListener('teleport:open-finder', () => { + spyglass.open(); // opens command palette +}); +``` + +## API Preview + +```typescript +import { createSpyglass } from '@sailkit/spyglass'; +import Fuse from 'fuse.js'; + +// Configure with your search engine +const spyglass = createSpyglass({ + engine: 'fuse', + fuse: new Fuse(items, { keys: ['title', 'description'] }), + + // UI options + placeholder: 'Search docs...', + hotkey: 'mod+k', + maxResults: 10, +}); + +// Open/close +spyglass.open(); +spyglass.close(); +spyglass.toggle(); + +// Events +spyglass.on('select', (item) => { + window.location.href = item.url; +}); +``` + +## Features + +- **Multiple engines** β€” Adapters for Fuse.js, MiniSearch, Pagefind +- **Command palette** β€” Modal UI with keyboard navigation +- **Sidebar filter** β€” Inline filter mode +- **Hotkey support** β€” Configurable keyboard shortcut +- **Result highlighting** β€” Visual match indicators +- **Keyboard navigation** β€” Arrow keys, Enter to select, Escape to close + +## Supported Search Engines + +| Engine | Type | Best For | +|--------|------|----------| +| Fuse.js | Client-side fuzzy | Small-medium sites | +| MiniSearch | Client-side full-text | Medium sites | +| Pagefind | Static + WASM | Large sites | + +## Learn More + +See the full design specification in the [spyglass package README](https://github.com/joshribakoff/sailkit/tree/spyglass-fuzzy-finder/packages/spyglass). diff --git a/docs/src/navigation.ts b/docs/src/navigation.ts index e34e978..6a7cbdf 100644 --- a/docs/src/navigation.ts +++ b/docs/src/navigation.ts @@ -19,6 +19,8 @@ export const navigation: NavItem[] = [ 'lantern', 'lighthouse', 'atlas', + 'spyglass', + 'scribe', ], }, { @@ -45,6 +47,8 @@ export const titles: Record = { 'lantern': 'Lantern', 'lighthouse': 'Lighthouse', 'atlas': 'Atlas', + 'spyglass': 'Spyglass', + 'scribe': 'Scribe', 'guides': 'Guides', 'vim-navigation': 'Vim Navigation', 'theming': 'Theming', From 09e9b9e706d14fa3a32be052cb819bf810342042 Mon Sep 17 00:00:00 2001 From: Josh Ribakoff Date: Mon, 15 Dec 2025 18:37:14 -0800 Subject: [PATCH 04/35] Add @sailkit/scribe package for testing code fences MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Extracts TypeScript/JavaScript code blocks from markdown - Executes code and checks exit codes for pass/fail - Parallel execution with configurable concurrency - TTY detection for interactive vs CI output - Vitest-style progress display with spinners πŸ€– Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- packages/scribe/README.md | 30 +++++ packages/scribe/package.json | 28 +++++ packages/scribe/src/cli.ts | 220 ++++++++++++++++++++++++++++++++++ packages/scribe/src/index.ts | 10 ++ packages/scribe/src/parser.ts | 40 +++++++ packages/scribe/src/runner.ts | 87 ++++++++++++++ packages/scribe/tsconfig.json | 14 +++ 7 files changed, 429 insertions(+) create mode 100644 packages/scribe/README.md create mode 100644 packages/scribe/package.json create mode 100644 packages/scribe/src/cli.ts create mode 100644 packages/scribe/src/index.ts create mode 100644 packages/scribe/src/parser.ts create mode 100644 packages/scribe/src/runner.ts create mode 100644 packages/scribe/tsconfig.json diff --git a/packages/scribe/README.md b/packages/scribe/README.md new file mode 100644 index 0000000..7e09674 --- /dev/null +++ b/packages/scribe/README.md @@ -0,0 +1,30 @@ +# @sailkit/scribe + +Extract and test code fences from markdown documentation. + +## Usage + +```bash +npx scribe [directory] +``` + +Scribe scans markdown files for TypeScript/JavaScript code fences and executes them, reporting pass/fail based on exit codes. + +## Inline Assertions + +Use simple assertions in your code blocks: + +```typescript +const result = add(1, 2) +if (result !== 3) throw new Error('Expected 3') +``` + +## API + +```typescript +import { parseMarkdown, filterTestableBlocks, runBlocks } from '@sailkit/scribe' + +const blocks = parseMarkdown(content, 'file.md') +const testable = filterTestableBlocks(blocks) +const results = await runBlocks(testable) +``` diff --git a/packages/scribe/package.json b/packages/scribe/package.json new file mode 100644 index 0000000..d5862c2 --- /dev/null +++ b/packages/scribe/package.json @@ -0,0 +1,28 @@ +{ + "name": "@sailkit/scribe", + "version": "0.0.1", + "description": "Extract and test code fences from markdown documentation", + "type": "module", + "main": "dist/index.js", + "types": "dist/index.d.ts", + "bin": { + "scribe": "dist/cli.js" + }, + "scripts": { + "build": "tsc", + "test": "node dist/cli.js" + }, + "keywords": [ + "documentation", + "testing", + "markdown", + "code-fence" + ], + "license": "MIT", + "devDependencies": { + "typescript": "^5.0.0" + }, + "dependencies": { + "log-update": "^7.0.2" + } +} diff --git a/packages/scribe/src/cli.ts b/packages/scribe/src/cli.ts new file mode 100644 index 0000000..893fa30 --- /dev/null +++ b/packages/scribe/src/cli.ts @@ -0,0 +1,220 @@ +#!/usr/bin/env node +/** + * Scribe CLI - test code fences from markdown files + */ + +import { readFile, readdir, stat } from 'node:fs/promises' +import { join, extname } from 'node:path' +import { cpus } from 'node:os' +import logUpdate from 'log-update' +import { parseMarkdown, filterTestableBlocks, type CodeBlock } from './parser.js' +import { runBlock, type RunResult } from './runner.js' + +// Detect interactive TTY vs CI/piped output +const isTTY = process.stdout.isTTY ?? false +const isCI = process.env.CI === 'true' || process.env.GITHUB_ACTIONS === 'true' +const useInteractiveOutput = isTTY && !isCI + +// Spinner frames +const spinnerFrames = ['β ‹', 'β ™', 'β Ή', 'β Έ', 'β Ό', 'β ΄', 'β ¦', 'β §', 'β ‡', '⠏'] +let spinnerIndex = 0 + +interface RunningTest { + label: string + index: number +} + +async function findMarkdownFiles(dir: string): Promise { + const files: string[] = [] + + async function walk(currentDir: string) { + const entries = await readdir(currentDir) + for (const entry of entries) { + const fullPath = join(currentDir, entry) + const info = await stat(fullPath) + + if (info.isDirectory() && !entry.startsWith('.') && entry !== 'node_modules') { + await walk(fullPath) + } else if (info.isFile() && ['.md', '.mdx'].includes(extname(entry))) { + files.push(fullPath) + } + } + } + + await walk(dir) + return files +} + +function parseArgs(): { targetDir: string; concurrency: number } { + const args = process.argv.slice(2) + let targetDir = process.cwd() + let concurrency = cpus().length + + for (let i = 0; i < args.length; i++) { + const arg = args[i] + if (arg === '--runInBand' || arg === '-i') { + concurrency = 1 + } else if (arg === '--parallel' || arg === '-j') { + const next = args[i + 1] + if (next && !next.startsWith('-')) { + concurrency = parseInt(next, 10) || cpus().length + i++ + } + } else if (!arg.startsWith('-')) { + targetDir = arg + } + } + + return { targetDir, concurrency } +} + +function cleanErrorMessage(error: string): string { + return error + .replace(/file:\/\/[^\s]+\[eval\d*\]:\d+/g, '') + .replace(/\[stdin\]:\d+/g, '') + .split('\n') + .filter(line => !line.includes('node:internal') && !line.includes('file://')) + .map(line => line.trim()) + .filter(Boolean) + .slice(0, 1) + .join('') +} + +function renderRunningTests(running: RunningTest[]): string { + if (running.length === 0) return '' + + const spinner = spinnerFrames[spinnerIndex % spinnerFrames.length] + const lines = running.map(t => ` \x1b[33m${spinner}\x1b[0m ${t.label}`) + return lines.join('\n') +} + +async function main() { + const { targetDir, concurrency } = parseArgs() + + console.log('Scanning for markdown files...') + const mdFiles = await findMarkdownFiles(targetDir) + console.log(`Found ${mdFiles.length} markdown file(s) (${concurrency} workers)\n`) + + // Collect all blocks + const allBlocks: { block: CodeBlock; shortFile: string }[] = [] + + for (const file of mdFiles) { + const content = await readFile(file, 'utf-8') + const blocks = filterTestableBlocks(parseMarkdown(content, file)) + const shortFile = file.replace(targetDir, '').replace(/^\//, '') + + for (const block of blocks) { + allBlocks.push({ block, shortFile }) + } + } + + if (allBlocks.length === 0) { + console.log('No testable code blocks found.') + return + } + + // Track state + const runningTests: Map = new Map() + let passed = 0 + let failed = 0 + + // Spinner animation interval (only updates the bottom running section) + let animationInterval: ReturnType | undefined + if (useInteractiveOutput) { + animationInterval = setInterval(() => { + spinnerIndex++ + const running = Array.from(runningTests.values()) + if (running.length > 0) { + logUpdate(renderRunningTests(running)) + } + }, 80) + } + + // Helper to print a completed test (goes into scrollback) + function printCompleted(label: string, success: boolean, error?: string) { + if (useInteractiveOutput) { + // Clear the running section, print result, then redraw running section + logUpdate.clear() + } + + if (success) { + console.log(`\x1b[32mβœ“\x1b[0m ${label}`) + } else { + console.log(`\x1b[31mβœ—\x1b[0m ${label}`) + if (error) { + console.log(` \x1b[90m${error}\x1b[0m`) + } + } + + if (useInteractiveOutput) { + // Redraw running tests at bottom + const running = Array.from(runningTests.values()) + if (running.length > 0) { + logUpdate(renderRunningTests(running)) + } + } + } + + // Run with concurrency + const results: RunResult[] = new Array(allBlocks.length) + let nextIndex = 0 + + async function worker() { + while (nextIndex < allBlocks.length) { + const index = nextIndex++ + const { shortFile, block } = allBlocks[index] + const label = `${shortFile}:${block.line}` + + // Mark as running + runningTests.set(index, { label, index }) + + if (useInteractiveOutput) { + logUpdate(renderRunningTests(Array.from(runningTests.values()))) + } + + const result = await runBlock(block) + results[index] = result + + // Remove from running + runningTests.delete(index) + + const cleanError = result.error ? cleanErrorMessage(result.error) : undefined + + if (result.success) { + passed++ + printCompleted(label, true) + } else { + failed++ + printCompleted(label, false, cleanError) + } + } + } + + const workers = Array.from( + { length: Math.min(concurrency, allBlocks.length) }, + () => worker() + ) + await Promise.all(workers) + + // Cleanup + if (animationInterval) { + clearInterval(animationInterval) + } + + if (useInteractiveOutput) { + logUpdate.clear() + } + + console.log() + const color = failed > 0 ? '\x1b[31m' : '\x1b[32m' + console.log(`${color}Results: ${passed}/${allBlocks.length} passed\x1b[0m`) + + if (failed > 0) { + process.exit(1) + } +} + +main().catch((err) => { + console.error('Scribe error:', err.message) + process.exit(1) +}) diff --git a/packages/scribe/src/index.ts b/packages/scribe/src/index.ts new file mode 100644 index 0000000..cb7c57d --- /dev/null +++ b/packages/scribe/src/index.ts @@ -0,0 +1,10 @@ +/** + * @sailkit/scribe + * Extract and test code fences from markdown documentation + */ + +export { parseMarkdown, filterTestableBlocks } from './parser.js' +export type { CodeBlock } from './parser.js' + +export { runBlock, runBlocks } from './runner.js' +export type { RunResult } from './runner.js' diff --git a/packages/scribe/src/parser.ts b/packages/scribe/src/parser.ts new file mode 100644 index 0000000..b330b0f --- /dev/null +++ b/packages/scribe/src/parser.ts @@ -0,0 +1,40 @@ +/** + * Code fence parser - extracts fenced code blocks from markdown + */ + +export interface CodeBlock { + language: string + code: string + file: string + line: number +} + +const CODE_FENCE_REGEX = /^```(\w*)\n([\s\S]*?)^```/gm + +export function parseMarkdown(content: string, filePath: string): CodeBlock[] { + const blocks: CodeBlock[] = [] + let match: RegExpExecArray | null + + while ((match = CODE_FENCE_REGEX.exec(content)) !== null) { + const language = match[1] || 'text' + const code = match[2] + + // Calculate line number + const beforeMatch = content.slice(0, match.index) + const line = beforeMatch.split('\n').length + + blocks.push({ + language, + code, + file: filePath, + line + }) + } + + return blocks +} + +export function filterTestableBlocks(blocks: CodeBlock[]): CodeBlock[] { + const testableLanguages = ['typescript', 'ts', 'javascript', 'js'] + return blocks.filter(block => testableLanguages.includes(block.language.toLowerCase())) +} diff --git a/packages/scribe/src/runner.ts b/packages/scribe/src/runner.ts new file mode 100644 index 0000000..20602a9 --- /dev/null +++ b/packages/scribe/src/runner.ts @@ -0,0 +1,87 @@ +/** + * Simple runner - executes code blocks via stdin (no temp files) + */ + +import { spawn } from 'node:child_process' +import type { CodeBlock } from './parser.js' + +export interface RunResult { + block: CodeBlock + success: boolean + output: string + error?: string +} + +export async function runBlock(block: CodeBlock): Promise { + const isTypeScript = block.language === 'typescript' || block.language === 'ts' + + try { + const result = await executeCode(block.code, isTypeScript) + + return { + block, + success: result.exitCode === 0, + output: result.stdout, + error: result.stderr || undefined + } + } catch (err) { + return { + block, + success: false, + output: '', + error: err instanceof Error ? err.message : String(err) + } + } +} + +async function executeCode(code: string, isTypeScript: boolean): Promise<{ exitCode: number; stdout: string; stderr: string }> { + return new Promise((resolve) => { + // Use tsx for TypeScript, node for JavaScript - both read from stdin + const cmd = isTypeScript ? 'npx' : 'node' + const args = isTypeScript ? ['tsx', '--input-type=module'] : ['--input-type=module'] + + const proc = spawn(cmd, args, { + stdio: ['pipe', 'pipe', 'pipe'], + timeout: 30000 + }) + + let stdout = '' + let stderr = '' + + proc.stdout.on('data', (data) => { + stdout += data.toString() + }) + + proc.stderr.on('data', (data) => { + stderr += data.toString() + }) + + proc.on('close', (code) => { + resolve({ + exitCode: code ?? 1, + stdout, + stderr + }) + }) + + proc.on('error', (err) => { + resolve({ + exitCode: 1, + stdout, + stderr: err.message + }) + }) + + // Write code to stdin and close + proc.stdin.write(code) + proc.stdin.end() + }) +} + +export async function runBlocks(blocks: CodeBlock[]): Promise { + const results: RunResult[] = [] + for (const block of blocks) { + results.push(await runBlock(block)) + } + return results +} diff --git a/packages/scribe/tsconfig.json b/packages/scribe/tsconfig.json new file mode 100644 index 0000000..f0423d6 --- /dev/null +++ b/packages/scribe/tsconfig.json @@ -0,0 +1,14 @@ +{ + "compilerOptions": { + "target": "ES2022", + "module": "NodeNext", + "moduleResolution": "NodeNext", + "declaration": true, + "outDir": "dist", + "rootDir": "src", + "strict": true, + "esModuleInterop": true, + "skipLibCheck": true + }, + "include": ["src"] +} From 406c62e2f0e7781aca38b704107ef3a50441f1ac Mon Sep 17 00:00:00 2001 From: Josh Ribakoff Date: Mon, 15 Dec 2025 18:41:25 -0800 Subject: [PATCH 05/35] perf: use esbuild + vm for ~240x faster code execution MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace child process spawning with in-process execution: - Use esbuild transform for TypeScript transpilation - Execute code in vm sandbox with captured console output - Reduces test time from ~62s to ~0.26s πŸ€– Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- packages/scribe/package.json | 1 + packages/scribe/src/runner.ts | 96 +++++++++++++++++------------------ 2 files changed, 47 insertions(+), 50 deletions(-) diff --git a/packages/scribe/package.json b/packages/scribe/package.json index d5862c2..0b2a647 100644 --- a/packages/scribe/package.json +++ b/packages/scribe/package.json @@ -23,6 +23,7 @@ "typescript": "^5.0.0" }, "dependencies": { + "esbuild": "^0.27.1", "log-update": "^7.0.2" } } diff --git a/packages/scribe/src/runner.ts b/packages/scribe/src/runner.ts index 20602a9..d36681c 100644 --- a/packages/scribe/src/runner.ts +++ b/packages/scribe/src/runner.ts @@ -1,8 +1,10 @@ /** - * Simple runner - executes code blocks via stdin (no temp files) + * In-process runner using esbuild transform + vm module + * Much faster than spawning child processes (~240x speedup) */ -import { spawn } from 'node:child_process' +import vm from 'node:vm' +import { transform } from 'esbuild' import type { CodeBlock } from './parser.js' export interface RunResult { @@ -16,13 +18,51 @@ export async function runBlock(block: CodeBlock): Promise { const isTypeScript = block.language === 'typescript' || block.language === 'ts' try { - const result = await executeCode(block.code, isTypeScript) + // Transpile TypeScript to JavaScript if needed + let code = block.code + if (isTypeScript) { + const result = await transform(code, { + loader: 'ts', + format: 'cjs', // vm.runInContext works better with CJS + target: 'node18' + }) + code = result.code + } + + // Capture console output + let stdout = '' + const mockConsole = { + log: (...args: unknown[]) => { stdout += args.map(String).join(' ') + '\n' }, + error: (...args: unknown[]) => { stdout += args.map(String).join(' ') + '\n' }, + warn: (...args: unknown[]) => { stdout += args.map(String).join(' ') + '\n' }, + info: (...args: unknown[]) => { stdout += args.map(String).join(' ') + '\n' } + } + + // Create sandbox with common globals + const sandbox = { + console: mockConsole, + setTimeout, + setInterval, + clearTimeout, + clearInterval, + Buffer, + process: { env: process.env }, + Error, + TypeError, + ReferenceError, + SyntaxError + } + + vm.createContext(sandbox) + + // Execute with timeout + const script = new vm.Script(code, { filename: 'code-block.js' }) + script.runInContext(sandbox, { timeout: 5000 }) return { block, - success: result.exitCode === 0, - output: result.stdout, - error: result.stderr || undefined + success: true, + output: stdout } } catch (err) { return { @@ -34,50 +74,6 @@ export async function runBlock(block: CodeBlock): Promise { } } -async function executeCode(code: string, isTypeScript: boolean): Promise<{ exitCode: number; stdout: string; stderr: string }> { - return new Promise((resolve) => { - // Use tsx for TypeScript, node for JavaScript - both read from stdin - const cmd = isTypeScript ? 'npx' : 'node' - const args = isTypeScript ? ['tsx', '--input-type=module'] : ['--input-type=module'] - - const proc = spawn(cmd, args, { - stdio: ['pipe', 'pipe', 'pipe'], - timeout: 30000 - }) - - let stdout = '' - let stderr = '' - - proc.stdout.on('data', (data) => { - stdout += data.toString() - }) - - proc.stderr.on('data', (data) => { - stderr += data.toString() - }) - - proc.on('close', (code) => { - resolve({ - exitCode: code ?? 1, - stdout, - stderr - }) - }) - - proc.on('error', (err) => { - resolve({ - exitCode: 1, - stdout, - stderr: err.message - }) - }) - - // Write code to stdin and close - proc.stdin.write(code) - proc.stdin.end() - }) -} - export async function runBlocks(blocks: CodeBlock[]): Promise { const results: RunResult[] = [] for (const block of blocks) { From 6e048ae99abb3c3d34cff89d8ad0e163c9f4397a Mon Sep 17 00:00:00 2001 From: Josh Ribakoff Date: Mon, 15 Dec 2025 18:51:12 -0800 Subject: [PATCH 06/35] feat: require path argument, add commander + vitest tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Use commander for CLI argument parsing - Make path argument required (like ESLint/Prettier) - Add IO interface for testability (mock stdout/stderr) - Add vitest with 8 unit tests using mock IO - Fix parser to handle info strings with 'scribe' marker πŸ€– Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- packages/scribe/package.json | 6 +- packages/scribe/src/cli.test.ts | 160 ++++++++++++++++++++++++++++++++ packages/scribe/src/cli.ts | 129 ++++++++++++++++--------- packages/scribe/src/parser.ts | 16 +++- 4 files changed, 260 insertions(+), 51 deletions(-) create mode 100644 packages/scribe/src/cli.test.ts diff --git a/packages/scribe/package.json b/packages/scribe/package.json index 0b2a647..865801e 100644 --- a/packages/scribe/package.json +++ b/packages/scribe/package.json @@ -10,7 +10,7 @@ }, "scripts": { "build": "tsc", - "test": "node dist/cli.js" + "test": "vitest run" }, "keywords": [ "documentation", @@ -20,9 +20,11 @@ ], "license": "MIT", "devDependencies": { - "typescript": "^5.0.0" + "typescript": "^5.0.0", + "vitest": "^4.0.15" }, "dependencies": { + "commander": "^14.0.2", "esbuild": "^0.27.1", "log-update": "^7.0.2" } diff --git a/packages/scribe/src/cli.test.ts b/packages/scribe/src/cli.test.ts new file mode 100644 index 0000000..d6ce7e6 --- /dev/null +++ b/packages/scribe/src/cli.test.ts @@ -0,0 +1,160 @@ +import { describe, it, expect, beforeEach } from 'vitest' +import { run, type IO, type RunOptions } from './cli.js' +import { writeFile, mkdir, rm } from 'node:fs/promises' +import { join } from 'node:path' +import { tmpdir } from 'node:os' + +// Mock IO that captures output +function createMockIO(): IO & { stdout: string; stderr: string } { + const io = { + stdout: '', + stderr: '', + write(text: string) { + io.stdout += text + }, + writeError(text: string) { + io.stderr += text + } + } + return io +} + +describe('scribe CLI', () => { + let testDir: string + + beforeEach(async () => { + testDir = join(tmpdir(), `scribe-test-${Date.now()}`) + await mkdir(testDir, { recursive: true }) + }) + + it('reports error for non-existent path', async () => { + const io = createMockIO() + const result = await run({ targetPath: '/nonexistent/path', concurrency: 1 }, io) + + expect(result.failed).toBe(1) + expect(io.stderr).toContain('Error: Path not found') + }) + + it('reports no testable blocks for markdown without code fences', async () => { + const mdPath = join(testDir, 'empty.md') + await writeFile(mdPath, '# Hello\n\nNo code here.') + + const io = createMockIO() + const result = await run({ targetPath: mdPath, concurrency: 1 }, io) + + expect(result.passed).toBe(0) + expect(result.failed).toBe(0) + expect(io.stdout).toContain('No testable code blocks found') + }) + + it('runs passing JavaScript code blocks', async () => { + const mdPath = join(testDir, 'passing.md') + await writeFile(mdPath, `# Test + +\`\`\`javascript scribe +console.log("hello") +\`\`\` +`) + + const io = createMockIO() + const result = await run({ targetPath: mdPath, concurrency: 1 }, io) + + expect(result.passed).toBe(1) + expect(result.failed).toBe(0) + expect(io.stdout).toContain('Results: 1/1 passed') + }) + + it('runs passing TypeScript code blocks', async () => { + const mdPath = join(testDir, 'typescript.md') + await writeFile(mdPath, `# TypeScript Test + +\`\`\`typescript scribe +const x: number = 42 +console.log(x) +\`\`\` +`) + + const io = createMockIO() + const result = await run({ targetPath: mdPath, concurrency: 1 }, io) + + expect(result.passed).toBe(1) + expect(result.failed).toBe(0) + }) + + it('reports failing code blocks', async () => { + const mdPath = join(testDir, 'failing.md') + await writeFile(mdPath, `# Failing Test + +\`\`\`javascript scribe +throw new Error("oops") +\`\`\` +`) + + const io = createMockIO() + const result = await run({ targetPath: mdPath, concurrency: 1 }, io) + + expect(result.passed).toBe(0) + expect(result.failed).toBe(1) + expect(io.stdout).toContain('Results: 0/1 passed') + }) + + it('scans directories for markdown files', async () => { + await writeFile(join(testDir, 'a.md'), `\`\`\`js scribe +console.log(1) +\`\`\``) + await writeFile(join(testDir, 'b.md'), `\`\`\`js scribe +console.log(2) +\`\`\``) + + const io = createMockIO() + const result = await run({ targetPath: testDir, concurrency: 1 }, io) + + expect(result.passed).toBe(2) + expect(result.failed).toBe(0) + expect(io.stdout).toContain('Found 2 markdown file(s)') + }) + + it('ignores code blocks without scribe marker', async () => { + const mdPath = join(testDir, 'unmarked.md') + await writeFile(mdPath, `# Test + +\`\`\`javascript +// This should be ignored +throw new Error("not run") +\`\`\` + +\`\`\`javascript scribe +console.log("this runs") +\`\`\` +`) + + const io = createMockIO() + const result = await run({ targetPath: mdPath, concurrency: 1 }, io) + + expect(result.passed).toBe(1) + expect(result.failed).toBe(0) + }) + + it('runs multiple blocks in parallel when concurrency > 1', async () => { + const mdPath = join(testDir, 'parallel.md') + await writeFile(mdPath, ` +\`\`\`js scribe +console.log(1) +\`\`\` + +\`\`\`js scribe +console.log(2) +\`\`\` + +\`\`\`js scribe +console.log(3) +\`\`\` +`) + + const io = createMockIO() + const result = await run({ targetPath: mdPath, concurrency: 3 }, io) + + expect(result.passed).toBe(3) + expect(result.failed).toBe(0) + }) +}) diff --git a/packages/scribe/src/cli.ts b/packages/scribe/src/cli.ts index 893fa30..abda40a 100644 --- a/packages/scribe/src/cli.ts +++ b/packages/scribe/src/cli.ts @@ -6,10 +6,23 @@ import { readFile, readdir, stat } from 'node:fs/promises' import { join, extname } from 'node:path' import { cpus } from 'node:os' +import { Command } from 'commander' import logUpdate from 'log-update' import { parseMarkdown, filterTestableBlocks, type CodeBlock } from './parser.js' import { runBlock, type RunResult } from './runner.js' +// IO interface for testability +export interface IO { + write(text: string): void + writeError(text: string): void +} + +// Real IO implementation +const realIO: IO = { + write: (text) => process.stdout.write(text), + writeError: (text) => process.stderr.write(text) +} + // Detect interactive TTY vs CI/piped output const isTTY = process.stdout.isTTY ?? false const isCI = process.env.CI === 'true' || process.env.GITHUB_ACTIONS === 'true' @@ -24,6 +37,11 @@ interface RunningTest { index: number } +export interface RunOptions { + targetPath: string + concurrency: number +} + async function findMarkdownFiles(dir: string): Promise { const files: string[] = [] @@ -45,29 +63,6 @@ async function findMarkdownFiles(dir: string): Promise { return files } -function parseArgs(): { targetDir: string; concurrency: number } { - const args = process.argv.slice(2) - let targetDir = process.cwd() - let concurrency = cpus().length - - for (let i = 0; i < args.length; i++) { - const arg = args[i] - if (arg === '--runInBand' || arg === '-i') { - concurrency = 1 - } else if (arg === '--parallel' || arg === '-j') { - const next = args[i + 1] - if (next && !next.startsWith('-')) { - concurrency = parseInt(next, 10) || cpus().length - i++ - } - } else if (!arg.startsWith('-')) { - targetDir = arg - } - } - - return { targetDir, concurrency } -} - function cleanErrorMessage(error: string): string { return error .replace(/file:\/\/[^\s]+\[eval\d*\]:\d+/g, '') @@ -88,12 +83,29 @@ function renderRunningTests(running: RunningTest[]): string { return lines.join('\n') } -async function main() { - const { targetDir, concurrency } = parseArgs() +export async function run(options: RunOptions, io: IO = realIO): Promise<{ passed: number; failed: number }> { + const { targetPath, concurrency } = options + + // Determine if target is file or directory + const targetStat = await stat(targetPath).catch(() => null) + if (!targetStat) { + io.writeError(`Error: Path not found: ${targetPath}\n`) + return { passed: 0, failed: 1 } + } - console.log('Scanning for markdown files...') - const mdFiles = await findMarkdownFiles(targetDir) - console.log(`Found ${mdFiles.length} markdown file(s) (${concurrency} workers)\n`) + let mdFiles: string[] + let baseDir: string + + if (targetStat.isFile()) { + mdFiles = [targetPath] + baseDir = targetPath.includes('/') ? targetPath.substring(0, targetPath.lastIndexOf('/')) : '.' + } else { + io.write('Scanning for markdown files...\n') + mdFiles = await findMarkdownFiles(targetPath) + baseDir = targetPath + } + + io.write(`Found ${mdFiles.length} markdown file(s) (${concurrency} workers)\n\n`) // Collect all blocks const allBlocks: { block: CodeBlock; shortFile: string }[] = [] @@ -101,7 +113,11 @@ async function main() { for (const file of mdFiles) { const content = await readFile(file, 'utf-8') const blocks = filterTestableBlocks(parseMarkdown(content, file)) - const shortFile = file.replace(targetDir, '').replace(/^\//, '') + // Get relative path for display + let shortFile = file + if (baseDir !== '.' && file.startsWith(baseDir)) { + shortFile = file.slice(baseDir.length).replace(/^\//, '') + } for (const block of blocks) { allBlocks.push({ block, shortFile }) @@ -109,8 +125,8 @@ async function main() { } if (allBlocks.length === 0) { - console.log('No testable code blocks found.') - return + io.write('No testable code blocks found.\n') + return { passed: 0, failed: 0 } } // Track state @@ -133,21 +149,19 @@ async function main() { // Helper to print a completed test (goes into scrollback) function printCompleted(label: string, success: boolean, error?: string) { if (useInteractiveOutput) { - // Clear the running section, print result, then redraw running section logUpdate.clear() } if (success) { - console.log(`\x1b[32mβœ“\x1b[0m ${label}`) + io.write(`\x1b[32mβœ“\x1b[0m ${label}\n`) } else { - console.log(`\x1b[31mβœ—\x1b[0m ${label}`) + io.write(`\x1b[31mβœ—\x1b[0m ${label}\n`) if (error) { - console.log(` \x1b[90m${error}\x1b[0m`) + io.write(` \x1b[90m${error}\x1b[0m\n`) } } if (useInteractiveOutput) { - // Redraw running tests at bottom const running = Array.from(runningTests.values()) if (running.length > 0) { logUpdate(renderRunningTests(running)) @@ -165,7 +179,6 @@ async function main() { const { shortFile, block } = allBlocks[index] const label = `${shortFile}:${block.line}` - // Mark as running runningTests.set(index, { label, index }) if (useInteractiveOutput) { @@ -175,7 +188,6 @@ async function main() { const result = await runBlock(block) results[index] = result - // Remove from running runningTests.delete(index) const cleanError = result.error ? cleanErrorMessage(result.error) : undefined @@ -196,7 +208,6 @@ async function main() { ) await Promise.all(workers) - // Cleanup if (animationInterval) { clearInterval(animationInterval) } @@ -205,16 +216,44 @@ async function main() { logUpdate.clear() } - console.log() + io.write('\n') const color = failed > 0 ? '\x1b[31m' : '\x1b[32m' - console.log(`${color}Results: ${passed}/${allBlocks.length} passed\x1b[0m`) + io.write(`${color}Results: ${passed}/${allBlocks.length} passed\x1b[0m\n`) + + return { passed, failed } +} + +// CLI entry point +const program = new Command() + .name('scribe') + .description('Test code fences from markdown files') + .argument('', 'File or directory to test') + .option('-i, --runInBand', 'Run tests sequentially (no parallelism)') + .option('-j, --parallel ', 'Set number of parallel workers', String(cpus().length)) + +async function main() { + program.parse() + + const targetPath = program.args[0] + const opts = program.opts() + + let concurrency = parseInt(opts.parallel, 10) || cpus().length + if (opts.runInBand) { + concurrency = 1 + } + + const { failed } = await run({ targetPath, concurrency }) if (failed > 0) { process.exit(1) } } -main().catch((err) => { - console.error('Scribe error:', err.message) - process.exit(1) -}) +// Only run when executed directly, not when imported for testing +const isMainModule = import.meta.url === `file://${process.argv[1]}` +if (isMainModule) { + main().catch((err) => { + console.error('Scribe error:', err.message) + process.exit(1) + }) +} diff --git a/packages/scribe/src/parser.ts b/packages/scribe/src/parser.ts index b330b0f..546d900 100644 --- a/packages/scribe/src/parser.ts +++ b/packages/scribe/src/parser.ts @@ -7,9 +7,12 @@ export interface CodeBlock { code: string file: string line: number + meta?: string } -const CODE_FENCE_REGEX = /^```(\w*)\n([\s\S]*?)^```/gm +// Matches code fences with optional info string (e.g., ```javascript scribe) +// Captures: 1=language, 2=rest of info line (may contain "scribe" marker), 3=code +const CODE_FENCE_REGEX = /^```(\w*)([^\n]*)\n([\s\S]*?)^```/gm export function parseMarkdown(content: string, filePath: string): CodeBlock[] { const blocks: CodeBlock[] = [] @@ -17,7 +20,8 @@ export function parseMarkdown(content: string, filePath: string): CodeBlock[] { while ((match = CODE_FENCE_REGEX.exec(content)) !== null) { const language = match[1] || 'text' - const code = match[2] + const infoString = match[2]?.trim() || '' + const code = match[3] // Calculate line number const beforeMatch = content.slice(0, match.index) @@ -27,7 +31,8 @@ export function parseMarkdown(content: string, filePath: string): CodeBlock[] { language, code, file: filePath, - line + line, + meta: infoString }) } @@ -36,5 +41,8 @@ export function parseMarkdown(content: string, filePath: string): CodeBlock[] { export function filterTestableBlocks(blocks: CodeBlock[]): CodeBlock[] { const testableLanguages = ['typescript', 'ts', 'javascript', 'js'] - return blocks.filter(block => testableLanguages.includes(block.language.toLowerCase())) + return blocks.filter(block => + testableLanguages.includes(block.language.toLowerCase()) && + block.meta?.includes('scribe') + ) } From b6347e72575622b2e4e2e0d062f8276d2e5651e7 Mon Sep 17 00:00:00 2001 From: Josh Ribakoff Date: Mon, 15 Dec 2025 18:57:10 -0800 Subject: [PATCH 07/35] feat: test all JS/TS code blocks by default, opt-out with nocheck MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Remove scribe marker requirement - all JS/TS blocks tested by default - Add nocheck marker to skip specific blocks (```ts nocheck) - Add vitest.config.ts to exclude dist folder from tests πŸ€– Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- packages/scribe/src/cli.test.ts | 24 ++++++++++++------------ packages/scribe/src/parser.ts | 2 +- packages/scribe/vitest.config.ts | 8 ++++++++ 3 files changed, 21 insertions(+), 13 deletions(-) create mode 100644 packages/scribe/vitest.config.ts diff --git a/packages/scribe/src/cli.test.ts b/packages/scribe/src/cli.test.ts index d6ce7e6..8167f03 100644 --- a/packages/scribe/src/cli.test.ts +++ b/packages/scribe/src/cli.test.ts @@ -51,7 +51,7 @@ describe('scribe CLI', () => { const mdPath = join(testDir, 'passing.md') await writeFile(mdPath, `# Test -\`\`\`javascript scribe +\`\`\`javascript console.log("hello") \`\`\` `) @@ -68,7 +68,7 @@ console.log("hello") const mdPath = join(testDir, 'typescript.md') await writeFile(mdPath, `# TypeScript Test -\`\`\`typescript scribe +\`\`\`typescript const x: number = 42 console.log(x) \`\`\` @@ -85,7 +85,7 @@ console.log(x) const mdPath = join(testDir, 'failing.md') await writeFile(mdPath, `# Failing Test -\`\`\`javascript scribe +\`\`\`javascript throw new Error("oops") \`\`\` `) @@ -99,10 +99,10 @@ throw new Error("oops") }) it('scans directories for markdown files', async () => { - await writeFile(join(testDir, 'a.md'), `\`\`\`js scribe + await writeFile(join(testDir, 'a.md'), `\`\`\`js console.log(1) \`\`\``) - await writeFile(join(testDir, 'b.md'), `\`\`\`js scribe + await writeFile(join(testDir, 'b.md'), `\`\`\`js console.log(2) \`\`\``) @@ -114,16 +114,16 @@ console.log(2) expect(io.stdout).toContain('Found 2 markdown file(s)') }) - it('ignores code blocks without scribe marker', async () => { - const mdPath = join(testDir, 'unmarked.md') + it('ignores code blocks with nocheck marker', async () => { + const mdPath = join(testDir, 'nocheck.md') await writeFile(mdPath, `# Test -\`\`\`javascript +\`\`\`javascript nocheck // This should be ignored throw new Error("not run") \`\`\` -\`\`\`javascript scribe +\`\`\`javascript console.log("this runs") \`\`\` `) @@ -138,15 +138,15 @@ console.log("this runs") it('runs multiple blocks in parallel when concurrency > 1', async () => { const mdPath = join(testDir, 'parallel.md') await writeFile(mdPath, ` -\`\`\`js scribe +\`\`\`js console.log(1) \`\`\` -\`\`\`js scribe +\`\`\`js console.log(2) \`\`\` -\`\`\`js scribe +\`\`\`js console.log(3) \`\`\` `) diff --git a/packages/scribe/src/parser.ts b/packages/scribe/src/parser.ts index 546d900..32db45d 100644 --- a/packages/scribe/src/parser.ts +++ b/packages/scribe/src/parser.ts @@ -43,6 +43,6 @@ export function filterTestableBlocks(blocks: CodeBlock[]): CodeBlock[] { const testableLanguages = ['typescript', 'ts', 'javascript', 'js'] return blocks.filter(block => testableLanguages.includes(block.language.toLowerCase()) && - block.meta?.includes('scribe') + !block.meta?.includes('nocheck') // opt-out with ```ts nocheck ) } diff --git a/packages/scribe/vitest.config.ts b/packages/scribe/vitest.config.ts new file mode 100644 index 0000000..7aa4024 --- /dev/null +++ b/packages/scribe/vitest.config.ts @@ -0,0 +1,8 @@ +import { defineConfig } from 'vitest/config' + +export default defineConfig({ + test: { + include: ['src/**/*.test.ts'], + exclude: ['dist/**', 'node_modules/**'] + } +}) From 71b449f2683bf525a5c893e5b377d81f8d54744e Mon Sep 17 00:00:00 2001 From: Josh Ribakoff Date: Mon, 15 Dec 2025 19:18:10 -0800 Subject: [PATCH 08/35] update scribe code docs --- docs/src/content/docs/compass.md | 183 +++++++++++-------------------- 1 file changed, 62 insertions(+), 121 deletions(-) diff --git a/docs/src/content/docs/compass.md b/docs/src/content/docs/compass.md index 9a255db..72ac7ca 100644 --- a/docs/src/content/docs/compass.md +++ b/docs/src/content/docs/compass.md @@ -7,134 +7,99 @@ description: Navigation state machine for nested content structures. **@sailkit/compass** is a headless navigation state machine for nested content structures. It provides DFS traversal, prev/next neighbors, and parent/child navigation without any UI assumptions. -## Installation +## Navigation Structure -```bash -npm install @sailkit/compass -``` - -## Core Concepts - -### NavItem Type - -Navigation is defined as a forest of `NavItem` elements: +Navigation is defined as a tree of items. Strings are leaves, objects with `children` are branches: ```typescript -type NavItem = string | NavBranch; - -interface NavBranch { - slug: string; - children: NavItem[]; -} -``` +import { isBranch, getSlug } from '@sailkit/compass' -Strings are leaf pages, objects are sections with children. +const nav = [ + 'a', + { slug: 'b', children: ['b1', 'b2'] }, + { slug: 'c', children: ['c1', 'c2', 'c3'] }, +] -### Example Structure +// strings are leaves, objects with children are branches +assert.strictEqual(isBranch(nav[0]), false) +assert.strictEqual(isBranch(nav[1]), true) -```typescript -const navigation: NavItem[] = [ - 'introduction', - { - slug: 'getting-started', - children: ['installation', 'quick-start'], - }, - { - slug: 'packages', - children: ['compass', 'teleport', 'lantern'], - }, -]; +// getSlug extracts the slug from either form +assert.strictEqual(getSlug(nav[0]), 'a') +assert.strictEqual(getSlug(nav[1]), 'b') ``` -This represents: -- introduction -- getting-started/ - - installation - - quick-start -- packages/ - - compass - - teleport - - lantern - -## Stateless Functions (SSG) +## Flattening to DFS Order -Use these at build time for static generation: +Flatten the tree to a list of slugs in depth-first order: -### `flattenSlugs()` +```typescript +import { flattenSlugs } from '@sailkit/compass' -Get all slugs in DFS order: +const nav = [ + 'a', + { slug: 'b', children: ['b1', 'b2'] }, + { slug: 'c', children: ['c1', 'c2', 'c3'] }, +] -```typescript -import { flattenSlugs } from '@sailkit/compass'; +const all = flattenSlugs(nav) +const leaves = flattenSlugs(nav, true) -const all = flattenSlugs(navigation); -// ['introduction', 'getting-started', 'installation', 'quick-start', 'packages', 'compass', 'teleport', 'lantern'] +// DFS order: a, b, b1, b2, c, c1, c2, c3 +assert.strictEqual(all.length, 8) +assert.deepStrictEqual(all, ['a', 'b', 'b1', 'b2', 'c', 'c1', 'c2', 'c3']) -const leaves = flattenSlugs(navigation, true); // leavesOnly -// ['introduction', 'installation', 'quick-start', 'compass', 'teleport', 'lantern'] +// leavesOnly=true excludes branches (b and c) +assert.strictEqual(leaves.length, 6) +assert.deepStrictEqual(leaves, ['a', 'b1', 'b2', 'c1', 'c2', 'c3']) ``` -### `getNeighbors()` +## Getting Neighbors -Get prev/next for a specific slug: +Get prev/next for any slug - useful for pagination: ```typescript -import { getNeighbors } from '@sailkit/compass'; +import { getNeighbors } from '@sailkit/compass' -const { prev, next } = getNeighbors(navigation, 'installation'); -// prev: 'introduction', next: 'quick-start' +const nav = [ + 'a', + { slug: 'b', children: ['b1', 'b2'] }, + { slug: 'c', children: ['c1', 'c2', 'c3'] }, +] -const { prev, next } = getNeighbors(navigation, 'installation', { leavesOnly: true }); -// prev: 'introduction', next: 'quick-start' (skips section pages) -``` +// flattened: a, b, b1, b2, c, c1, c2, c3 +const { prev, next } = getNeighbors(nav, 'b1') -This is used by the [[quick-start|PrevNext component]] in this documentation. +// b1's neighbors in flattened order +assert.strictEqual(prev, 'b') +assert.strictEqual(next, 'b2') +``` -## State Machine (Runtime) +## Runtime Navigation -For SPAs or interactive navigation: +For SPAs, create a stateful navigator: ```typescript -import { createNavigator } from '@sailkit/compass'; - -const nav = createNavigator({ - items: navigation, - wrap: true, - leavesOnly: false, - onChange: (prev, next, index) => { - console.log(`Moved from ${prev} to ${next}`); - }, -}); - -// Properties -nav.current; // Current slug -nav.currentIndex; // Current position in flat list -nav.count; // Total items - -// Navigation -nav.next(); // Move forward -nav.prev(); // Move backward -nav.nextSibling(); // Skip to next sibling -nav.prevSibling(); // Skip to previous sibling -nav.parent(); // Go to parent section -nav.firstChild(); // Go to first child -nav.goTo(5); // Jump to index -nav.goToSlug('compass'); // Jump to slug -nav.reset(); // Go to first item -``` +import { createNavigator } from '@sailkit/compass' -## Type Guards +const nav = createNavigator({ items: ['a', 'b', 'c'], wrap: true }) -```typescript -import { isBranch, getSlug } from '@sailkit/compass'; +// starts at first item +assert.strictEqual(nav.current, 'a') +assert.strictEqual(nav.count, 3) -const item = navigation[1]; +// next() advances position +nav.next() +assert.strictEqual(nav.current, 'b') -if (isBranch(item)) { - console.log(item.children); // TypeScript knows it's a NavBranch -} +// wraps around when wrap=true +nav.next() +nav.next() +assert.strictEqual(nav.current, 'a') -const slug = getSlug(item); // Works for both string and NavBranch +// goToSlug jumps directly +nav.goToSlug('c') +assert.strictEqual(nav.current, 'c') ``` ## Configuration Options @@ -146,31 +111,7 @@ const slug = getSlug(item); // Works for both string and NavBranch | `leavesOnly` | `boolean` | `false` | Skip section pages | | `onChange` | `function` | - | Callback on navigation | -## Integration with Teleport - -Compass provides the data structure, [[teleport]] handles keyboard events: - -```typescript -import { createNavigator } from '@sailkit/compass'; -import { initTeleport } from '@sailkit/teleport'; - -const nav = createNavigator({ items: navigation }); - -initTeleport({ - itemSelector: '.nav-item', - onNextPage: () => { - nav.next(); - window.location.href = `/docs/${nav.current}/`; - }, - onPrevPage: () => { - nav.prev(); - window.location.href = `/docs/${nav.current}/`; - }, -}); -``` - ## Related - [[teleport]] - Keyboard navigation that works with Compass - [[architecture]] - How Compass fits into the larger system -- [[vim-navigation]] - Guide for customizing keyboard navigation From 64d26f645567e1f267969f8e9a515d61d10a3ac3 Mon Sep 17 00:00:00 2001 From: Josh Ribakoff Date: Mon, 15 Dec 2025 19:29:48 -0800 Subject: [PATCH 09/35] feat(teleport): add comprehensive validation and edge case tests for key binding DSL MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add validation for parseKey() to reject invalid key binding syntax with clear error messages. Includes tests for: empty strings, whitespace, leading/trailing/ consecutive plus signs, unrecognized modifiers, duplicate modifiers, modifier-only bindings, and suspicious multi-character keys. πŸ€– Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- packages/teleport/src/keys.test.ts | 140 +++++++++++++++++++++++++++++ packages/teleport/src/keys.ts | 113 ++++++++++++++++++++++- 2 files changed, 249 insertions(+), 4 deletions(-) diff --git a/packages/teleport/src/keys.test.ts b/packages/teleport/src/keys.test.ts index d9abeac..caf4bba 100644 --- a/packages/teleport/src/keys.test.ts +++ b/packages/teleport/src/keys.test.ts @@ -74,6 +74,146 @@ describe('parseKey', () => { meta: false, }); }); + + it('parses all four modifiers together', () => { + expect(parseKey('Ctrl+Alt+Shift+Meta+x')).toEqual({ + key: 'x', + ctrl: true, + alt: true, + shift: true, + meta: true, + }); + }); + + it('handles modifiers in any order', () => { + expect(parseKey('Shift+Ctrl+d')).toEqual({ + key: 'd', + ctrl: true, + alt: false, + shift: true, + meta: false, + }); + }); +}); + +describe('parseKey edge cases - invalid syntax', () => { + it('throws on empty string', () => { + expect(() => parseKey('')).toThrow('Invalid key binding: empty string'); + }); + + it('throws on whitespace-only string', () => { + expect(() => parseKey(' ')).toThrow('Invalid key binding: empty string'); + }); + + it('throws on modifier-only (no key)', () => { + expect(() => parseKey('Ctrl')).toThrow( + 'Invalid key binding "Ctrl": modifier-only binding, missing key' + ); + expect(() => parseKey('Ctrl+Shift')).toThrow( + 'Invalid key binding "Ctrl+Shift": modifier-only binding, missing key' + ); + }); + + it('throws on trailing plus sign (empty key)', () => { + expect(() => parseKey('Ctrl+')).toThrow( + 'Invalid key binding "Ctrl+": empty key after modifier' + ); + expect(() => parseKey('Ctrl+Shift+')).toThrow( + 'Invalid key binding "Ctrl+Shift+": empty key after modifier' + ); + }); + + it('throws on leading plus sign', () => { + expect(() => parseKey('+d')).toThrow( + 'Invalid key binding "+d": leading plus sign' + ); + expect(() => parseKey('+Ctrl+d')).toThrow( + 'Invalid key binding "+Ctrl+d": leading plus sign' + ); + }); + + it('throws on consecutive plus signs', () => { + expect(() => parseKey('Ctrl++d')).toThrow( + 'Invalid key binding "Ctrl++d": consecutive plus signs' + ); + expect(() => parseKey('Ctrl+++d')).toThrow( + 'Invalid key binding "Ctrl+++d": consecutive plus signs' + ); + }); + + it('throws on unrecognized modifier', () => { + expect(() => parseKey('Cntrl+d')).toThrow( + 'Invalid key binding "Cntrl+d": unrecognized modifier "Cntrl"' + ); + expect(() => parseKey('Control+d')).toThrow( + 'Invalid key binding "Control+d": unrecognized modifier "Control"' + ); + expect(() => parseKey('Foo+d')).toThrow( + 'Invalid key binding "Foo+d": unrecognized modifier "Foo"' + ); + }); + + it('throws on duplicate modifiers', () => { + expect(() => parseKey('Ctrl+Ctrl+d')).toThrow( + 'Invalid key binding "Ctrl+Ctrl+d": duplicate modifier "Ctrl"' + ); + expect(() => parseKey('Shift+Shift+d')).toThrow( + 'Invalid key binding "Shift+Shift+d": duplicate modifier "Shift"' + ); + }); + + it('throws on whitespace in binding', () => { + expect(() => parseKey('Ctrl + d')).toThrow( + 'Invalid key binding "Ctrl + d": contains whitespace' + ); + expect(() => parseKey(' Ctrl+d')).toThrow( + 'Invalid key binding " Ctrl+d": contains whitespace' + ); + expect(() => parseKey('Ctrl+d ')).toThrow( + 'Invalid key binding "Ctrl+d ": contains whitespace' + ); + }); + + it('throws on plus-only string', () => { + expect(() => parseKey('+')).toThrow( + 'Invalid key binding "+": plus sign only' + ); + expect(() => parseKey('++')).toThrow( + 'Invalid key binding "++"' + ); + }); + + it('throws on multi-character key (likely missing plus)', () => { + expect(() => parseKey('Ctrld')).toThrow( + 'Invalid key binding "Ctrld": key "ctrld" looks like a missing plus sign' + ); + expect(() => parseKey('jk')).toThrow( + 'Invalid key binding "jk": key "jk" looks like a missing plus sign' + ); + }); + + it('allows valid multi-character keys like ArrowDown, Enter, Escape', () => { + expect(() => parseKey('ArrowDown')).not.toThrow(); + expect(() => parseKey('ArrowUp')).not.toThrow(); + expect(() => parseKey('ArrowLeft')).not.toThrow(); + expect(() => parseKey('ArrowRight')).not.toThrow(); + expect(() => parseKey('Enter')).not.toThrow(); + expect(() => parseKey('Escape')).not.toThrow(); + expect(() => parseKey('Tab')).not.toThrow(); + expect(() => parseKey('Backspace')).not.toThrow(); + expect(() => parseKey('Delete')).not.toThrow(); + expect(() => parseKey('Home')).not.toThrow(); + expect(() => parseKey('End')).not.toThrow(); + expect(() => parseKey('PageUp')).not.toThrow(); + expect(() => parseKey('PageDown')).not.toThrow(); + expect(() => parseKey('Space')).not.toThrow(); + }); + + it('allows function keys F1-F12', () => { + expect(() => parseKey('F1')).not.toThrow(); + expect(() => parseKey('F12')).not.toThrow(); + expect(() => parseKey('Ctrl+F5')).not.toThrow(); + }); }); describe('matchesKey', () => { diff --git a/packages/teleport/src/keys.ts b/packages/teleport/src/keys.ts index a2260f2..fe8adce 100644 --- a/packages/teleport/src/keys.ts +++ b/packages/teleport/src/keys.ts @@ -27,22 +27,127 @@ export const DEFAULT_BINDINGS: Required = { escape: ['Escape'], }; +/** + * Valid modifier names (case-insensitive) + */ +const VALID_MODIFIERS = new Set(['ctrl', 'alt', 'shift', 'meta', 'cmd']); + +/** + * Valid multi-character key names that are not modifiers + */ +const VALID_SPECIAL_KEYS = new Set([ + 'arrowdown', + 'arrowup', + 'arrowleft', + 'arrowright', + 'enter', + 'escape', + 'tab', + 'backspace', + 'delete', + 'home', + 'end', + 'pageup', + 'pagedown', + 'space', + 'insert', + 'capslock', + 'numlock', + 'scrolllock', + 'pause', + 'printscreen', + 'contextmenu', + // Function keys F1-F24 + 'f1', 'f2', 'f3', 'f4', 'f5', 'f6', 'f7', 'f8', 'f9', 'f10', 'f11', 'f12', + 'f13', 'f14', 'f15', 'f16', 'f17', 'f18', 'f19', 'f20', 'f21', 'f22', 'f23', 'f24', +]); + /** * Parse a key pattern string into components. * * Supports modifiers: Ctrl, Alt, Shift, Meta (Cmd on Mac) * Examples: 'j', 'Ctrl+d', 'Shift+Tab', 'Meta+k' + * + * @throws Error if the pattern is invalid */ export function parseKey(pattern: string): ParsedKey { + // Check for empty or whitespace-only + if (!pattern || pattern.trim() === '') { + throw new Error('Invalid key binding: empty string'); + } + + // Check for whitespace + if (/\s/.test(pattern)) { + throw new Error(`Invalid key binding "${pattern}": contains whitespace`); + } + + // Check for plus-only + if (pattern === '+') { + throw new Error(`Invalid key binding "+": plus sign only`); + } + + // Check for leading plus + if (pattern.startsWith('+')) { + throw new Error(`Invalid key binding "${pattern}": leading plus sign`); + } + + // Check for consecutive plus signs + if (/\+\+/.test(pattern)) { + throw new Error(`Invalid key binding "${pattern}": consecutive plus signs`); + } + + // Check for trailing plus (empty key after modifier) + if (pattern.endsWith('+')) { + throw new Error(`Invalid key binding "${pattern}": empty key after modifier`); + } + const parts = pattern.split('+'); const key = parts.pop()!.toLowerCase(); + const modifierParts = parts; + + // Track seen modifiers to detect duplicates + const seenModifiers = new Set(); + + // Validate each modifier + for (const part of modifierParts) { + const lowerPart = part.toLowerCase(); + + if (!VALID_MODIFIERS.has(lowerPart)) { + throw new Error( + `Invalid key binding "${pattern}": unrecognized modifier "${part}"` + ); + } + + // Normalize cmd to meta for duplicate detection + const normalizedMod = lowerPart === 'cmd' ? 'meta' : lowerPart; + if (seenModifiers.has(normalizedMod)) { + throw new Error( + `Invalid key binding "${pattern}": duplicate modifier "${part}"` + ); + } + seenModifiers.add(normalizedMod); + } + + // If the key is itself a modifier, it's a modifier-only binding (missing key) + if (VALID_MODIFIERS.has(key)) { + throw new Error( + `Invalid key binding "${pattern}": modifier-only binding, missing key` + ); + } + + // Check for suspicious multi-character keys (likely missing +) + if (key.length > 1 && !VALID_SPECIAL_KEYS.has(key)) { + throw new Error( + `Invalid key binding "${pattern}": key "${key}" looks like a missing plus sign` + ); + } return { key, - ctrl: parts.some((p) => p.toLowerCase() === 'ctrl'), - alt: parts.some((p) => p.toLowerCase() === 'alt'), - shift: parts.some((p) => p.toLowerCase() === 'shift'), - meta: parts.some((p) => p.toLowerCase() === 'meta' || p.toLowerCase() === 'cmd'), + ctrl: seenModifiers.has('ctrl'), + alt: seenModifiers.has('alt'), + shift: seenModifiers.has('shift'), + meta: seenModifiers.has('meta'), }; } From c3419f7e0047acdc7ba2aebfa8ac453ad26d968a Mon Sep 17 00:00:00 2001 From: Josh Ribakoff Date: Mon, 15 Dec 2025 20:59:25 -0800 Subject: [PATCH 10/35] wip(teleport): add playwright e2e tests (not yet passing) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add initial playwright test setup for teleport package: - playwright.config.ts configuration - e2e scroll tests with fixture HTML files - Basic teleport spec placeholder Tests are a work in progress and not yet passing. πŸ€– Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- packages/teleport/playwright.config.ts | 19 ++ .../tests/e2e/scroll-fallback-fixture.html | 73 ++++++ .../teleport/tests/e2e/scroll-fixture.html | 71 ++++++ packages/teleport/tests/e2e/scroll.spec.ts | 232 ++++++++++++++++++ packages/teleport/tests/e2e/teleport.spec.ts | 10 + packages/teleport/tests/fixtures/index.html | 107 ++++++++ 6 files changed, 512 insertions(+) create mode 100644 packages/teleport/playwright.config.ts create mode 100644 packages/teleport/tests/e2e/scroll-fallback-fixture.html create mode 100644 packages/teleport/tests/e2e/scroll-fixture.html create mode 100644 packages/teleport/tests/e2e/scroll.spec.ts create mode 100644 packages/teleport/tests/e2e/teleport.spec.ts create mode 100644 packages/teleport/tests/fixtures/index.html diff --git a/packages/teleport/playwright.config.ts b/packages/teleport/playwright.config.ts new file mode 100644 index 0000000..74a21bb --- /dev/null +++ b/packages/teleport/playwright.config.ts @@ -0,0 +1,19 @@ +import { defineConfig } from '@playwright/test'; + +export default defineConfig({ + testDir: './tests/e2e', + fullyParallel: true, + forbidOnly: !!process.env.CI, + retries: process.env.CI ? 2 : 0, + workers: process.env.CI ? 1 : undefined, + reporter: 'html', + use: { + baseURL: 'http://localhost:3000', + trace: 'on-first-retry', + }, + webServer: { + command: 'npx serve . -l 3000', + url: 'http://localhost:3000', + reuseExistingServer: !process.env.CI, + }, +}); diff --git a/packages/teleport/tests/e2e/scroll-fallback-fixture.html b/packages/teleport/tests/e2e/scroll-fallback-fixture.html new file mode 100644 index 0000000..ece933b --- /dev/null +++ b/packages/teleport/tests/e2e/scroll-fallback-fixture.html @@ -0,0 +1,73 @@ + + + + + + Teleport Scroll Fallback Test Fixture + + + + +
+
Block 1 - Window scroll fallback test
+
Block 2
+
Block 3
+
Block 4
+
Block 5
+
Block 6
+
+ + + diff --git a/packages/teleport/tests/e2e/scroll-fixture.html b/packages/teleport/tests/e2e/scroll-fixture.html new file mode 100644 index 0000000..5558db8 --- /dev/null +++ b/packages/teleport/tests/e2e/scroll-fixture.html @@ -0,0 +1,71 @@ + + + + + + Teleport Scroll Test Fixture + + + + +
+
Block 1 - Scroll test content
+
Block 2
+
Block 3
+
Block 4
+
Block 5
+
Block 6
+
Block 7
+
Block 8
+
+ + + diff --git a/packages/teleport/tests/e2e/scroll.spec.ts b/packages/teleport/tests/e2e/scroll.spec.ts new file mode 100644 index 0000000..725679e --- /dev/null +++ b/packages/teleport/tests/e2e/scroll.spec.ts @@ -0,0 +1,232 @@ +import { test, expect } from '@playwright/test'; + +/** + * Scroll binding tests for Teleport + * + * Tests Ctrl+d/Ctrl+u scroll behavior for main content area. + * j/k navigate sidebar items (which scroll via scrollIntoView - tested elsewhere). + */ + +const TEST_HTML = ` + + + + + + + +
+
Block 1
+
Block 2
+
Block 3
+
Block 4
+
Block 5
+
Block 6
+
Block 7
+
Block 8
+
+ + + +`; + +test.describe('Scroll bindings', () => { + test.beforeEach(async ({ page }) => { + // Navigate to the test fixture server and set content + await page.goto('/tests/e2e/scroll-fixture.html'); + }); + + test('Ctrl+d scrolls main content down', async ({ page }) => { + const mainContent = page.locator('.main-content'); + + // Get initial scroll position + const initialScrollTop = await mainContent.evaluate((el) => el.scrollTop); + expect(initialScrollTop).toBe(0); + + // Press Ctrl+d + await page.keyboard.down('Control'); + await page.keyboard.press('d'); + await page.keyboard.up('Control'); + + // Wait for smooth scroll to complete + await page.waitForTimeout(500); + + // Verify scroll position increased + const newScrollTop = await mainContent.evaluate((el) => el.scrollTop); + expect(newScrollTop).toBeGreaterThan(0); + }); + + test('Ctrl+u scrolls main content up', async ({ page }) => { + const mainContent = page.locator('.main-content'); + + // First scroll down to have room to scroll up + await mainContent.evaluate((el) => { + el.scrollTop = 500; + }); + + const initialScrollTop = await mainContent.evaluate((el) => el.scrollTop); + expect(initialScrollTop).toBe(500); + + // Press Ctrl+u + await page.keyboard.down('Control'); + await page.keyboard.press('u'); + await page.keyboard.up('Control'); + + // Wait for smooth scroll to complete + await page.waitForTimeout(500); + + // Verify scroll position decreased + const newScrollTop = await mainContent.evaluate((el) => el.scrollTop); + expect(newScrollTop).toBeLessThan(500); + }); + + test('Scrolls specified container, not window', async ({ page }) => { + const mainContent = page.locator('.main-content'); + + // Get initial positions + const initialMainScroll = await mainContent.evaluate((el) => el.scrollTop); + const initialWindowScroll = await page.evaluate(() => window.scrollY); + + expect(initialMainScroll).toBe(0); + expect(initialWindowScroll).toBe(0); + + // Press Ctrl+d + await page.keyboard.down('Control'); + await page.keyboard.press('d'); + await page.keyboard.up('Control'); + + // Wait for smooth scroll + await page.waitForTimeout(500); + + // Main content should have scrolled + const newMainScroll = await mainContent.evaluate((el) => el.scrollTop); + expect(newMainScroll).toBeGreaterThan(0); + + // Window should NOT have scrolled (body has overflow: hidden) + const newWindowScroll = await page.evaluate(() => window.scrollY); + expect(newWindowScroll).toBe(0); + }); + + test('No scroll when at boundary - bottom', async ({ page }) => { + const mainContent = page.locator('.main-content'); + + // Scroll to absolute bottom + await mainContent.evaluate((el) => { + el.scrollTop = el.scrollHeight - el.clientHeight; + }); + + const scrollAtBottom = await mainContent.evaluate((el) => el.scrollTop); + + // Press Ctrl+d (should do nothing, no error) + await page.keyboard.down('Control'); + await page.keyboard.press('d'); + await page.keyboard.up('Control'); + + await page.waitForTimeout(500); + + // Should still be at bottom (or very close due to rounding) + const scrollAfter = await mainContent.evaluate((el) => el.scrollTop); + expect(Math.abs(scrollAfter - scrollAtBottom)).toBeLessThan(5); + }); + + test('No scroll when at boundary - top', async ({ page }) => { + const mainContent = page.locator('.main-content'); + + // Ensure at top + const scrollAtTop = await mainContent.evaluate((el) => el.scrollTop); + expect(scrollAtTop).toBe(0); + + // Press Ctrl+u (should do nothing, no error) + await page.keyboard.down('Control'); + await page.keyboard.press('u'); + await page.keyboard.up('Control'); + + await page.waitForTimeout(500); + + // Should still be at top + const scrollAfter = await mainContent.evaluate((el) => el.scrollTop); + expect(scrollAfter).toBe(0); + }); +}); + +test.describe('Scroll fallback behavior', () => { + test('Fallback to window when no contentContainer specified', async ({ page }) => { + // Use a different fixture that doesn't specify contentContainer + await page.goto('/tests/e2e/scroll-fallback-fixture.html'); + + // Get initial window scroll + const initialWindowScroll = await page.evaluate(() => window.scrollY); + expect(initialWindowScroll).toBe(0); + + // Press Ctrl+d + await page.keyboard.down('Control'); + await page.keyboard.press('d'); + await page.keyboard.up('Control'); + + // Wait for smooth scroll + await page.waitForTimeout(500); + + // Window should have scrolled + const newWindowScroll = await page.evaluate(() => window.scrollY); + expect(newWindowScroll).toBeGreaterThan(0); + }); +}); + +// Future tests for auto-detection feature +test.describe('Future: Auto-detection scroll context', () => { + test.skip('hover over nested scrollable - scrolls that container', async () => { + // Future: When scrollContext='auto', hovering over a scrollable code block + // and pressing Ctrl+d should scroll that block, not the main content + }); + + test.skip('focus takes precedence over hover', async () => { + // Future: Tab-focusing into a scrollable region should make it the scroll target + // even if mouse is hovering elsewhere + }); + + test.skip('scroll context exhaustion bubbles up', async () => { + // Future: When inner scrollable hits bottom, next Ctrl+d scrolls parent + }); +}); diff --git a/packages/teleport/tests/e2e/teleport.spec.ts b/packages/teleport/tests/e2e/teleport.spec.ts new file mode 100644 index 0000000..af33c89 --- /dev/null +++ b/packages/teleport/tests/e2e/teleport.spec.ts @@ -0,0 +1,10 @@ +import { test, expect } from '@playwright/test'; + +test('page loads successfully', async ({ page }) => { + await page.goto('/tests/fixtures/index.html'); + + // Verify the page structure is present + await expect(page.locator('.sidebar')).toBeVisible(); + await expect(page.locator('main')).toBeVisible(); + await expect(page.locator('.nav-item')).toHaveCount(5); +}); diff --git a/packages/teleport/tests/fixtures/index.html b/packages/teleport/tests/fixtures/index.html new file mode 100644 index 0000000..8198e4b --- /dev/null +++ b/packages/teleport/tests/fixtures/index.html @@ -0,0 +1,107 @@ + + + + + + Teleport E2E Test Fixture + + + + + +
+
+

Section 1

+

Content for section 1. This is some sample content to test scrolling behavior.

+
+
+

Section 2

+

Content for section 2. More sample content here.

+
+
+

Section 3

+

Content for section 3. Even more content.

+
+
+

Section 4

+

Content for section 4. Keep going.

+
+
+

Section 5

+

Content for section 5. Last section.

+
+
+ + + + From 478b90a088d1b13fa4d2291878dbf07da3ad7899 Mon Sep 17 00:00:00 2001 From: Josh Ribakoff Date: Mon, 15 Dec 2025 21:00:11 -0800 Subject: [PATCH 11/35] chore: add playwright artifacts to gitignore MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit πŸ€– Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- .gitignore | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.gitignore b/.gitignore index cd8400f..4381684 100644 --- a/.gitignore +++ b/.gitignore @@ -9,3 +9,7 @@ package-lock.json # OS files .DS_Store + +# Playwright +playwright-report/ +test-results/ From a8f4c0de0cdc99104153f6ad0886e1a623925ca2 Mon Sep 17 00:00:00 2001 From: Josh Ribakoff Date: Mon, 15 Dec 2025 21:18:03 -0800 Subject: [PATCH 12/35] =?UTF-8?q?refactor(teleport):=20remove=20duplicate?= =?UTF-8?q?=20navigation=20state=20=E2=80=94=20compass=20is=20now=20the=20?= =?UTF-8?q?single=20source=20of=20truth?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit πŸ€– Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- .gitignore | 20 +- README.md | 2 +- package-lock.json | 3394 ++++++++++++++++++++++++++++ package.json | 4 +- packages/compass/README.md | 2 +- packages/compass/package-lock.json | 1392 ++++++++++++ packages/compass/package.json | 4 +- packages/lantern/README.md | 12 +- packages/lantern/package.json | 4 +- packages/lantern/src/index.ts | 2 +- packages/teleport/README.md | 14 +- packages/teleport/Teleport.astro | 236 +- packages/teleport/package.json | 14 +- packages/teleport/src/dom.ts | 163 +- packages/teleport/src/index.ts | 50 +- packages/teleport/src/keys.ts | 48 +- packages/teleport/src/teleport.ts | 137 +- packages/teleport/src/types.ts | 100 +- 18 files changed, 4964 insertions(+), 634 deletions(-) create mode 100644 package-lock.json create mode 100644 packages/compass/package-lock.json diff --git a/.gitignore b/.gitignore index 4381684..b56e5b2 100644 --- a/.gitignore +++ b/.gitignore @@ -1,15 +1,5 @@ -# Dependencies -node_modules/ - -# Build output -dist/ - -# Lock files (using npm) -package-lock.json - -# OS files -.DS_Store - -# Playwright -playwright-report/ -test-results/ +.astro +test-results +node_modules +dist +playwright-report diff --git a/README.md b/README.md index 923974b..505ee5d 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# Sailkit +# Bearing Lightweight, composable utilities for documentation sites. diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..41bfeeb --- /dev/null +++ b/package-lock.json @@ -0,0 +1,3394 @@ +{ + "name": "sailkit", + "version": "0.1.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "sailkit", + "version": "0.1.0", + "workspaces": [ + "packages/*" + ], + "devDependencies": { + "typescript": "^5.3.0" + } + }, + "docs": { + "version": "0.0.1", + "extraneous": true, + "dependencies": { + "@sailkit/atlas": "*", + "@sailkit/compass": "*", + "@sailkit/lantern": "*", + "@sailkit/lighthouse": "*", + "@sailkit/teleport": "*", + "astro": "^5.16.5" + } + }, + "node_modules/@asamuzakjp/css-color": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/@asamuzakjp/css-color/-/css-color-3.2.0.tgz", + "integrity": "sha512-K1A6z8tS3XsmCMM86xoWdn7Fkdn9m6RSVtocUrJYIwZnFVkng/PvkEoWtOWmP+Scc6saYWHWZYbndEEXxl24jw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@csstools/css-calc": "^2.1.3", + "@csstools/css-color-parser": "^3.0.9", + "@csstools/css-parser-algorithms": "^3.0.4", + "@csstools/css-tokenizer": "^3.0.3", + "lru-cache": "^10.4.3" + } + }, + "node_modules/@csstools/color-helpers": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@csstools/color-helpers/-/color-helpers-5.1.0.tgz", + "integrity": "sha512-S11EXWJyy0Mz5SYvRmY8nJYTFFd1LCNV+7cXyAgQtOOuzb4EsgfqDufL+9esx72/eLhsRdGZwaldu/h+E4t4BA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT-0", + "engines": { + "node": ">=18" + } + }, + "node_modules/@csstools/css-calc": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@csstools/css-calc/-/css-calc-2.1.4.tgz", + "integrity": "sha512-3N8oaj+0juUw/1H3YwmDDJXCgTB1gKU6Hc/bB502u9zR0q2vd786XJH9QfrKIEgFlZmhZiq6epXl4rHqhzsIgQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@csstools/css-parser-algorithms": "^3.0.5", + "@csstools/css-tokenizer": "^3.0.4" + } + }, + "node_modules/@csstools/css-color-parser": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@csstools/css-color-parser/-/css-color-parser-3.1.0.tgz", + "integrity": "sha512-nbtKwh3a6xNVIp/VRuXV64yTKnb1IjTAEEh3irzS+HkKjAOYLTGNb9pmVNntZ8iVBHcWDA2Dof0QtPgFI1BaTA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "dependencies": { + "@csstools/color-helpers": "^5.1.0", + "@csstools/css-calc": "^2.1.4" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@csstools/css-parser-algorithms": "^3.0.5", + "@csstools/css-tokenizer": "^3.0.4" + } + }, + "node_modules/@csstools/css-parser-algorithms": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@csstools/css-parser-algorithms/-/css-parser-algorithms-3.0.5.tgz", + "integrity": "sha512-DaDeUkXZKjdGhgYaHNJTV9pV7Y9B3b644jCLs9Upc3VeNGg6LWARAT6O+Q+/COo+2gg/bM5rhpMAtf70WqfBdQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@csstools/css-tokenizer": "^3.0.4" + } + }, + "node_modules/@csstools/css-tokenizer": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@csstools/css-tokenizer/-/css-tokenizer-3.0.4.tgz", + "integrity": "sha512-Vd/9EVDiu6PPJt9yAh6roZP6El1xHrdvIVGjyBsHR0RYwNHgL7FJPyIIW4fANJNG6FtyZfvlRPpFI4ZM/lubvw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz", + "integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.21.5.tgz", + "integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz", + "integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.21.5.tgz", + "integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz", + "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz", + "integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz", + "integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz", + "integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz", + "integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz", + "integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz", + "integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz", + "integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz", + "integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz", + "integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz", + "integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz", + "integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz", + "integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz", + "integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz", + "integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz", + "integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz", + "integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz", + "integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz", + "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "dev": true, + "license": "MIT" + }, + "node_modules/@playwright/test": { + "version": "1.57.0", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.57.0.tgz", + "integrity": "sha512-6TyEnHgd6SArQO8UO2OMTxshln3QMWBtPGrOCgs3wVEmQmwyuNtB10IZMfmYDE0riwNR1cu4q+pPcxMVtaG3TA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "playwright": "1.57.0" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.53.3.tgz", + "integrity": "sha512-mRSi+4cBjrRLoaal2PnqH82Wqyb+d3HsPUN/W+WslCXsZsyHa9ZeQQX/pQsZaVIWDkPcpV6jJ+3KLbTbgnwv8w==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.53.3.tgz", + "integrity": "sha512-CbDGaMpdE9sh7sCmTrTUyllhrg65t6SwhjlMJsLr+J8YjFuPmCEjbBSx4Z/e4SmDyH3aB5hGaJUP2ltV/vcs4w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.53.3.tgz", + "integrity": "sha512-Nr7SlQeqIBpOV6BHHGZgYBuSdanCXuw09hon14MGOLGmXAFYjx1wNvquVPmpZnl0tLjg25dEdr4IQ6GgyToCUA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.53.3.tgz", + "integrity": "sha512-DZ8N4CSNfl965CmPktJ8oBnfYr3F8dTTNBQkRlffnUarJ2ohudQD17sZBa097J8xhQ26AwhHJ5mvUyQW8ddTsQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.53.3.tgz", + "integrity": "sha512-yMTrCrK92aGyi7GuDNtGn2sNW+Gdb4vErx4t3Gv/Tr+1zRb8ax4z8GWVRfr3Jw8zJWvpGHNpss3vVlbF58DZ4w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.53.3.tgz", + "integrity": "sha512-lMfF8X7QhdQzseM6XaX0vbno2m3hlyZFhwcndRMw8fbAGUGL3WFMBdK0hbUBIUYcEcMhVLr1SIamDeuLBnXS+Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.53.3.tgz", + "integrity": "sha512-k9oD15soC/Ln6d2Wv/JOFPzZXIAIFLp6B+i14KhxAfnq76ajt0EhYc5YPeX6W1xJkAdItcVT+JhKl1QZh44/qw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.53.3.tgz", + "integrity": "sha512-vTNlKq+N6CK/8UktsrFuc+/7NlEYVxgaEgRXVUVK258Z5ymho29skzW1sutgYjqNnquGwVUObAaxae8rZ6YMhg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.53.3.tgz", + "integrity": "sha512-RGrFLWgMhSxRs/EWJMIFM1O5Mzuz3Xy3/mnxJp/5cVhZ2XoCAxJnmNsEyeMJtpK+wu0FJFWz+QF4mjCA7AUQ3w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.53.3.tgz", + "integrity": "sha512-kASyvfBEWYPEwe0Qv4nfu6pNkITLTb32p4yTgzFCocHnJLAHs+9LjUu9ONIhvfT/5lv4YS5muBHyuV84epBo/A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-gnu": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.53.3.tgz", + "integrity": "sha512-JiuKcp2teLJwQ7vkJ95EwESWkNRFJD7TQgYmCnrPtlu50b4XvT5MOmurWNrCj3IFdyjBQ5p9vnrX4JM6I8OE7g==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.53.3.tgz", + "integrity": "sha512-EoGSa8nd6d3T7zLuqdojxC20oBfNT8nexBbB/rkxgKj5T5vhpAQKKnD+h3UkoMuTyXkP5jTjK/ccNRmQrPNDuw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.53.3.tgz", + "integrity": "sha512-4s+Wped2IHXHPnAEbIB0YWBv7SDohqxobiiPA1FIWZpX+w9o2i4LezzH/NkFUl8LRci/8udci6cLq+jJQlh+0g==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.53.3.tgz", + "integrity": "sha512-68k2g7+0vs2u9CxDt5ktXTngsxOQkSEV/xBbwlqYcUrAVh6P9EgMZvFsnHy4SEiUl46Xf0IObWVbMvPrr2gw8A==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.53.3.tgz", + "integrity": "sha512-VYsFMpULAz87ZW6BVYw3I6sWesGpsP9OPcyKe8ofdg9LHxSbRMd7zrVrr5xi/3kMZtpWL/wC+UIJWJYVX5uTKg==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.53.3.tgz", + "integrity": "sha512-3EhFi1FU6YL8HTUJZ51imGJWEX//ajQPfqWLI3BQq4TlvHy4X0MOr5q3D2Zof/ka0d5FNdPwZXm3Yyib/UEd+w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.53.3.tgz", + "integrity": "sha512-eoROhjcc6HbZCJr+tvVT8X4fW3/5g/WkGvvmwz/88sDtSJzO7r/blvoBDgISDiCjDRZmHpwud7h+6Q9JxFwq1Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.53.3.tgz", + "integrity": "sha512-OueLAWgrNSPGAdUdIjSWXw+u/02BRTcnfw9PN41D2vq/JSEPnJnVuBgw18VkN8wcd4fjUs+jFHVM4t9+kBSNLw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.53.3.tgz", + "integrity": "sha512-GOFuKpsxR/whszbF/bzydebLiXIHSgsEUp6M0JI8dWvi+fFa1TD6YQa4aSZHtpmh2/uAlj/Dy+nmby3TJ3pkTw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.53.3.tgz", + "integrity": "sha512-iah+THLcBJdpfZ1TstDFbKNznlzoxa8fmnFYK4V67HvmuNYkVdAywJSoteUszvBQ9/HqN2+9AZghbajMsFT+oA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.53.3.tgz", + "integrity": "sha512-J9QDiOIZlZLdcot5NXEepDkstocktoVjkaKUtqzgzpt2yWjGlbYiKyp05rWwk4nypbYUNoFAztEgixoLaSETkg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.53.3.tgz", + "integrity": "sha512-UhTd8u31dXadv0MopwGgNOBpUVROFKWVQgAg5N1ESyCz8AuBcMqm4AuTjrwgQKGDfoFuz02EuMRHQIw/frmYKQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@sailkit/compass": { + "resolved": "packages/compass", + "link": true + }, + "node_modules/@sailkit/lantern": { + "resolved": "packages/lantern", + "link": true + }, + "node_modules/@sailkit/teleport": { + "resolved": "packages/teleport", + "link": true + }, + "node_modules/@types/estree": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "20.19.27", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.27.tgz", + "integrity": "sha512-N2clP5pJhB2YnZJ3PIHFk5RkygRX5WO/5f0WC08tp0wd+sv0rsJk3MqWn3CbNmT2J505a5336jaQj4ph1AdMug==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "undici-types": "~6.21.0" + } + }, + "node_modules/@vitest/expect": { + "version": "2.1.9", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-2.1.9.tgz", + "integrity": "sha512-UJCIkTBenHeKT1TTlKMJWy1laZewsRIzYighyYiJKZreqtdxSos/S1t+ktRMQWu2CKqaarrkeszJx1cgC5tGZw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/spy": "2.1.9", + "@vitest/utils": "2.1.9", + "chai": "^5.1.2", + "tinyrainbow": "^1.2.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/mocker": { + "version": "2.1.9", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-2.1.9.tgz", + "integrity": "sha512-tVL6uJgoUdi6icpxmdrn5YNo3g3Dxv+IHJBr0GXHaEdTcw3F+cPKnsXFhli6nO+f/6SDKPHEK1UN+k+TQv0Ehg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/spy": "2.1.9", + "estree-walker": "^3.0.3", + "magic-string": "^0.30.12" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "msw": "^2.4.9", + "vite": "^5.0.0" + }, + "peerDependenciesMeta": { + "msw": { + "optional": true + }, + "vite": { + "optional": true + } + } + }, + "node_modules/@vitest/pretty-format": { + "version": "2.1.9", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-2.1.9.tgz", + "integrity": "sha512-KhRIdGV2U9HOUzxfiHmY8IFHTdqtOhIzCpd8WRdJiE7D/HUcZVD0EgQCVjm+Q9gkUXWgBvMmTtZgIG48wq7sOQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "tinyrainbow": "^1.2.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/runner": { + "version": "2.1.9", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-2.1.9.tgz", + "integrity": "sha512-ZXSSqTFIrzduD63btIfEyOmNcBmQvgOVsPNPe0jYtESiXkhd8u2erDLnMxmGrDCwHCCHE7hxwRDCT3pt0esT4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/utils": "2.1.9", + "pathe": "^1.1.2" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/snapshot": { + "version": "2.1.9", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-2.1.9.tgz", + "integrity": "sha512-oBO82rEjsxLNJincVhLhaxxZdEtV0EFHMK5Kmx5sJ6H9L183dHECjiefOAdnqpIgT5eZwT04PoggUnW88vOBNQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/pretty-format": "2.1.9", + "magic-string": "^0.30.12", + "pathe": "^1.1.2" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/spy": { + "version": "2.1.9", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-2.1.9.tgz", + "integrity": "sha512-E1B35FwzXXTs9FHNK6bDszs7mtydNi5MIfUWpceJ8Xbfb1gBMscAnwLbEu+B44ed6W3XjL9/ehLPHR1fkf1KLQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "tinyspy": "^3.0.2" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/utils": { + "version": "2.1.9", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-2.1.9.tgz", + "integrity": "sha512-v0psaMSkNJ3A2NMrUEHFRzJtDPFn+/VWZ5WxImB21T9fjucJRmS7xCS3ppEnARb9y11OAzaD+P2Ps+b+BGX5iQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/pretty-format": "2.1.9", + "loupe": "^3.1.2", + "tinyrainbow": "^1.2.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@zeit/schemas": { + "version": "2.36.0", + "resolved": "https://registry.npmjs.org/@zeit/schemas/-/schemas-2.36.0.tgz", + "integrity": "sha512-7kjMwcChYEzMKjeex9ZFXkt1AyNov9R5HZtjBKVsmVpw7pa7ZtlCGvCBC2vnnXctaYN+aRI61HjIqeetZW5ROg==", + "dev": true, + "license": "MIT" + }, + "node_modules/agent-base": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", + "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14" + } + }, + "node_modules/ajv": { + "version": "8.12.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", + "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ansi-align": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-3.0.1.tgz", + "integrity": "sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^4.1.0" + } + }, + "node_modules/ansi-align/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-align/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/ansi-align/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-align/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-regex": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", + "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/ansi-styles": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", + "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/arch": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/arch/-/arch-2.2.0.tgz", + "integrity": "sha512-Of/R0wqp83cgHozfIYLbBMnej79U/SVGOOyuB3VVFv1NRM/PSFMK12x9KVtiYzJqmnU5WR2qp0Z5rHb7sWGnFQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/arg": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", + "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==", + "dev": true, + "license": "MIT" + }, + "node_modules/assertion-error": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz", + "integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + } + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/boxen": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/boxen/-/boxen-7.0.0.tgz", + "integrity": "sha512-j//dBVuyacJbvW+tvZ9HuH03fZ46QcaKvvhZickZqtB271DxJ7SNRSNxrV/dZX0085m7hISRZWbzWlJvx/rHSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-align": "^3.0.1", + "camelcase": "^7.0.0", + "chalk": "^5.0.1", + "cli-boxes": "^3.0.0", + "string-width": "^5.1.2", + "type-fest": "^2.13.0", + "widest-line": "^4.0.1", + "wrap-ansi": "^8.0.1" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/cac": { + "version": "6.7.14", + "resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz", + "integrity": "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/camelcase": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-7.0.1.tgz", + "integrity": "sha512-xlx1yCK2Oc1APsPXDL2LdlNP6+uu8OCDdhOBSVT279M/S+y75O30C2VuD8T2ogdePBBl7PfPF4504tnLgX3zfw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/chai": { + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/chai/-/chai-5.3.3.tgz", + "integrity": "sha512-4zNhdJD/iOjSH0A05ea+Ke6MU5mmpQcbQsSOkgdaUMJ9zTlDTD/GYlwohmIE2u0gaxHYiVHEn1Fw9mZ/ktJWgw==", + "dev": true, + "license": "MIT", + "dependencies": { + "assertion-error": "^2.0.1", + "check-error": "^2.1.1", + "deep-eql": "^5.0.1", + "loupe": "^3.1.0", + "pathval": "^2.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/chalk": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.0.1.tgz", + "integrity": "sha512-Fo07WOYGqMfCWHOzSXOt2CxDbC6skS/jO9ynEcmpANMoPrD+W1r1K6Vx7iNm+AQmETU1Xr2t+n8nzkV9t6xh3w==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/chalk-template": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/chalk-template/-/chalk-template-0.4.0.tgz", + "integrity": "sha512-/ghrgmhfY8RaSdeo43hNXxpoHAtxdbskUHjPpfqUWGttFgycUhYPGx3YZBCnUCvOa7Doivn1IZec3DEGFoMgLg==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^4.1.2" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/chalk-template?sponsor=1" + } + }, + "node_modules/chalk-template/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/chalk-template/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/check-error": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/check-error/-/check-error-2.1.1.tgz", + "integrity": "sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 16" + } + }, + "node_modules/cli-boxes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-3.0.0.tgz", + "integrity": "sha512-/lzGpEWL/8PfI0BmBOPRwp0c/wFNX1RdUML3jK/RcSBA9T8mZDdQpqYBKtCFTOfQbwPqWEOpjqW+Fnayc0969g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/clipboardy": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/clipboardy/-/clipboardy-3.0.0.tgz", + "integrity": "sha512-Su+uU5sr1jkUy1sGRpLKjKrvEOVXgSgiSInwa/qeID6aJ07yh+5NWc3h2QfjHjBnfX4LhtFcuAWKUsJ3r+fjbg==", + "dev": true, + "license": "MIT", + "dependencies": { + "arch": "^2.2.0", + "execa": "^5.1.1", + "is-wsl": "^2.2.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dev": true, + "license": "MIT", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/compressible": { + "version": "2.0.18", + "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz", + "integrity": "sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==", + "dev": true, + "license": "MIT", + "dependencies": { + "mime-db": ">= 1.43.0 < 2" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/compression": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/compression/-/compression-1.8.1.tgz", + "integrity": "sha512-9mAqGPHLakhCLeNyxPkK4xVo746zQ/czLH1Ky+vkitMnWfWZps8r0qXuwhwizagCRttsL4lfG4pIOvaWLpAP0w==", + "dev": true, + "license": "MIT", + "dependencies": { + "bytes": "3.1.2", + "compressible": "~2.0.18", + "debug": "2.6.9", + "negotiator": "~0.6.4", + "on-headers": "~1.1.0", + "safe-buffer": "5.2.1", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/compression/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/compression/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true, + "license": "MIT" + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true, + "license": "MIT" + }, + "node_modules/content-disposition": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.2.tgz", + "integrity": "sha512-kRGRZw3bLlFISDBgwTSA1TMBFN6J6GWDeubmDE3AF+3+yXL8hTWv8r5rkLbqYXY4RjPk/EzHnClI3zQf1cFmHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "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/cssstyle": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-4.6.0.tgz", + "integrity": "sha512-2z+rWdzbbSZv6/rhtvzvqeZQHrBaqgogqt85sqFNbabZOuFbCVFb8kPeEtZjiKkbrm395irpNKiYeFeLiQnFPg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@asamuzakjp/css-color": "^3.2.0", + "rrweb-cssom": "^0.8.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/cssstyle/node_modules/rrweb-cssom": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/rrweb-cssom/-/rrweb-cssom-0.8.0.tgz", + "integrity": "sha512-guoltQEx+9aMf2gDZ0s62EcV8lsXR+0w8915TC3ITdn2YueuNjdAYh/levpU9nFaoChh9RUS5ZdQMrKfVEN9tw==", + "dev": true, + "license": "MIT" + }, + "node_modules/data-urls": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-5.0.0.tgz", + "integrity": "sha512-ZYP5VBHshaDAiVZxjbRVcFJpc+4xGgT0bK3vzy1HLN8jTO975HEbuYzZJcHoQEY5K1a0z8YayJkyVETa08eNTg==", + "dev": true, + "license": "MIT", + "dependencies": { + "whatwg-mimetype": "^4.0.0", + "whatwg-url": "^14.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decimal.js": { + "version": "10.6.0", + "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.6.0.tgz", + "integrity": "sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==", + "dev": true, + "license": "MIT" + }, + "node_modules/deep-eql": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-5.0.2.tgz", + "integrity": "sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/deep-extend": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "dev": true, + "license": "MIT" + }, + "node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true, + "license": "MIT" + }, + "node_modules/entities": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-6.0.1.tgz", + "integrity": "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-module-lexer": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.7.0.tgz", + "integrity": "sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==", + "dev": true, + "license": "MIT" + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/esbuild": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz", + "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.21.5", + "@esbuild/android-arm": "0.21.5", + "@esbuild/android-arm64": "0.21.5", + "@esbuild/android-x64": "0.21.5", + "@esbuild/darwin-arm64": "0.21.5", + "@esbuild/darwin-x64": "0.21.5", + "@esbuild/freebsd-arm64": "0.21.5", + "@esbuild/freebsd-x64": "0.21.5", + "@esbuild/linux-arm": "0.21.5", + "@esbuild/linux-arm64": "0.21.5", + "@esbuild/linux-ia32": "0.21.5", + "@esbuild/linux-loong64": "0.21.5", + "@esbuild/linux-mips64el": "0.21.5", + "@esbuild/linux-ppc64": "0.21.5", + "@esbuild/linux-riscv64": "0.21.5", + "@esbuild/linux-s390x": "0.21.5", + "@esbuild/linux-x64": "0.21.5", + "@esbuild/netbsd-x64": "0.21.5", + "@esbuild/openbsd-x64": "0.21.5", + "@esbuild/sunos-x64": "0.21.5", + "@esbuild/win32-arm64": "0.21.5", + "@esbuild/win32-ia32": "0.21.5", + "@esbuild/win32-x64": "0.21.5" + } + }, + "node_modules/estree-walker": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", + "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0" + } + }, + "node_modules/execa": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "dev": true, + "license": "MIT", + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/expect-type": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.3.0.tgz", + "integrity": "sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/form-data": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz", + "integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==", + "dev": true, + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.2", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/html-encoding-sniffer": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-4.0.0.tgz", + "integrity": "sha512-Y22oTqIU4uuPgEemfz7NDJz6OeKf12Lsu+QC+s3BVpda64lTiMYCyGwg5ki4vFxkMwQdeZDl2adZoqUgdFuTgQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "whatwg-encoding": "^3.1.1" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/http-proxy-agent": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", + "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.0", + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/https-proxy-agent": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", + "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/human-signals": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", + "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=10.17.0" + } + }, + "node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "dev": true, + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", + "dev": true, + "license": "ISC" + }, + "node_modules/is-docker": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", + "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", + "dev": true, + "license": "MIT", + "bin": { + "is-docker": "cli.js" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-port-reachable": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-port-reachable/-/is-port-reachable-4.0.0.tgz", + "integrity": "sha512-9UoipoxYmSk6Xy7QFgRv2HDyaysmgSG75TFQs6S+3pDM7ZhKTF/bskZV+0UlABHzKjNVhPjYCLfeZUEg1wXxig==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-potential-custom-element-name": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz", + "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-wsl": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", + "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-docker": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true, + "license": "ISC" + }, + "node_modules/jsdom": { + "version": "25.0.1", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-25.0.1.tgz", + "integrity": "sha512-8i7LzZj7BF8uplX+ZyOlIz86V6TAsSs+np6m1kpW9u0JWi4z/1t+FzcK1aek+ybTnAC4KhBL4uXCNT0wcUIeCw==", + "dev": true, + "license": "MIT", + "dependencies": { + "cssstyle": "^4.1.0", + "data-urls": "^5.0.0", + "decimal.js": "^10.4.3", + "form-data": "^4.0.0", + "html-encoding-sniffer": "^4.0.0", + "http-proxy-agent": "^7.0.2", + "https-proxy-agent": "^7.0.5", + "is-potential-custom-element-name": "^1.0.1", + "nwsapi": "^2.2.12", + "parse5": "^7.1.2", + "rrweb-cssom": "^0.7.1", + "saxes": "^6.0.0", + "symbol-tree": "^3.2.4", + "tough-cookie": "^5.0.0", + "w3c-xmlserializer": "^5.0.0", + "webidl-conversions": "^7.0.0", + "whatwg-encoding": "^3.1.1", + "whatwg-mimetype": "^4.0.0", + "whatwg-url": "^14.0.0", + "ws": "^8.18.0", + "xml-name-validator": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "canvas": "^2.11.2" + }, + "peerDependenciesMeta": { + "canvas": { + "optional": true + } + } + }, + "node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true, + "license": "MIT" + }, + "node_modules/loupe": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/loupe/-/loupe-3.2.1.tgz", + "integrity": "sha512-CdzqowRJCeLU72bHvWqwRBBlLcMEtIvGrlvef74kMnV2AolS9Y8xUv1I0U/MNAWMhBlKIoyuEgoJ0t/bbwHbLQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/magic-string": { + "version": "0.30.21", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", + "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true, + "license": "MIT" + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dev": true, + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/nanoid": { + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/negotiator": { + "version": "0.6.4", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.4.tgz", + "integrity": "sha512-myRT3DiWPHqho5PrJaIRyaMv2kgYf0mUVgBNOYMuCH5Ki1yEiQaf/ZJuQ62nvpc44wL5WDbTX7yGJi1Neevw8w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/nwsapi": { + "version": "2.2.23", + "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.23.tgz", + "integrity": "sha512-7wfH4sLbt4M0gCDzGE6vzQBo0bfTKjU7Sfpqy/7gs1qBfYz2vEJH6vXcBKpO3+6Yu1telwd0t9HpyOoLEQQbIQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/on-headers": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.1.0.tgz", + "integrity": "sha512-737ZY3yNnXy37FHkQxPzt4UZ2UWPWiCZWLvFZ4fu5cueciegX0zGPnrlY6bwRg4FdQOe9YU8MkmJwGhoMybl8A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parse5": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.3.0.tgz", + "integrity": "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==", + "dev": true, + "license": "MIT", + "dependencies": { + "entities": "^6.0.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, + "node_modules/path-is-inside": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz", + "integrity": "sha512-DUWJr3+ULp4zXmol/SZkFf3JGsS9/SIv+Y3Rt93/UjPpDpklB5f1er4O3POIbUuUJ3FXgqte2Q7SrU6zAqwk8w==", + "dev": true, + "license": "(WTFPL OR MIT)" + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-to-regexp": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-3.3.0.tgz", + "integrity": "sha512-qyCH421YQPS2WFDxDjftfc1ZR5WKQzVzqsp4n9M2kQhVOo/ByahFoUNJfl58kOcEGfQ//7weFTDhm+ss8Ecxgw==", + "dev": true, + "license": "MIT" + }, + "node_modules/pathe": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-1.1.2.tgz", + "integrity": "sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/pathval": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pathval/-/pathval-2.0.1.tgz", + "integrity": "sha512-//nshmD55c46FuFw26xV/xFAaB5HF9Xdap7HJBBnrKdAd6/GxDBaNA1870O79+9ueg61cZLSVc+OaFlfmObYVQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14.16" + } + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, + "node_modules/playwright": { + "version": "1.57.0", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.57.0.tgz", + "integrity": "sha512-ilYQj1s8sr2ppEJ2YVadYBN0Mb3mdo9J0wQ+UuDhzYqURwSoW4n1Xs5vs7ORwgDGmyEh33tRMeS8KhdkMoLXQw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "playwright-core": "1.57.0" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "fsevents": "2.3.2" + } + }, + "node_modules/playwright-core": { + "version": "1.57.0", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.57.0.tgz", + "integrity": "sha512-agTcKlMw/mjBWOnD6kFZttAAGHgi/Nw0CZ2o6JqWSbMlI219lAFLZZCyqByTsvVAJq5XA5H8cA6PrvBRpBWEuQ==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "playwright-core": "cli.js" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/playwright/node_modules/fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/postcss": { + "version": "8.5.6", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", + "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.11", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/range-parser": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.0.tgz", + "integrity": "sha512-kA5WQoNVo4t9lNx2kQNFCxKeBl5IbbSNBl1M/tLkw9WCn+hxNBAW5Qh8gdhs63CJnhjJ2zQWFoqPJP2sK1AV5A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/rc": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", + "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", + "dev": true, + "license": "(BSD-2-Clause OR MIT OR Apache-2.0)", + "dependencies": { + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + }, + "bin": { + "rc": "cli.js" + } + }, + "node_modules/registry-auth-token": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-3.3.2.tgz", + "integrity": "sha512-JL39c60XlzCVgNrO+qq68FoNb56w/m7JYvGR2jT5iR1xBrUA3Mfx5Twk5rqTThPmQKMWydGmq8oFtDlxfrmxnQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "rc": "^1.1.6", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/registry-url": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/registry-url/-/registry-url-3.1.0.tgz", + "integrity": "sha512-ZbgR5aZEdf4UKZVBPYIgaglBmSF2Hi94s2PcIHhRGFjKYu+chjJdYfHn4rt3hB6eCKLJ8giVIIfgMa1ehDfZKA==", + "dev": true, + "license": "MIT", + "dependencies": { + "rc": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/rollup": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.53.3.tgz", + "integrity": "sha512-w8GmOxZfBmKknvdXU1sdM9NHcoQejwF/4mNgj2JuEEdRaHwwF12K7e9eXn1nLZ07ad+du76mkVsyeb2rKGllsA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.8" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.53.3", + "@rollup/rollup-android-arm64": "4.53.3", + "@rollup/rollup-darwin-arm64": "4.53.3", + "@rollup/rollup-darwin-x64": "4.53.3", + "@rollup/rollup-freebsd-arm64": "4.53.3", + "@rollup/rollup-freebsd-x64": "4.53.3", + "@rollup/rollup-linux-arm-gnueabihf": "4.53.3", + "@rollup/rollup-linux-arm-musleabihf": "4.53.3", + "@rollup/rollup-linux-arm64-gnu": "4.53.3", + "@rollup/rollup-linux-arm64-musl": "4.53.3", + "@rollup/rollup-linux-loong64-gnu": "4.53.3", + "@rollup/rollup-linux-ppc64-gnu": "4.53.3", + "@rollup/rollup-linux-riscv64-gnu": "4.53.3", + "@rollup/rollup-linux-riscv64-musl": "4.53.3", + "@rollup/rollup-linux-s390x-gnu": "4.53.3", + "@rollup/rollup-linux-x64-gnu": "4.53.3", + "@rollup/rollup-linux-x64-musl": "4.53.3", + "@rollup/rollup-openharmony-arm64": "4.53.3", + "@rollup/rollup-win32-arm64-msvc": "4.53.3", + "@rollup/rollup-win32-ia32-msvc": "4.53.3", + "@rollup/rollup-win32-x64-gnu": "4.53.3", + "@rollup/rollup-win32-x64-msvc": "4.53.3", + "fsevents": "~2.3.2" + } + }, + "node_modules/rrweb-cssom": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/rrweb-cssom/-/rrweb-cssom-0.7.1.tgz", + "integrity": "sha512-TrEMa7JGdVm0UThDJSx7ddw5nVm3UJS9o9CCIZ72B1vSyEZoziDqBYP3XIoi/12lKrJR8rE3jeFHMok2F/Mnsg==", + "dev": true, + "license": "MIT" + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "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" + }, + "node_modules/saxes": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/saxes/-/saxes-6.0.0.tgz", + "integrity": "sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==", + "dev": true, + "license": "ISC", + "dependencies": { + "xmlchars": "^2.2.0" + }, + "engines": { + "node": ">=v12.22.7" + } + }, + "node_modules/serve": { + "version": "14.2.5", + "resolved": "https://registry.npmjs.org/serve/-/serve-14.2.5.tgz", + "integrity": "sha512-Qn/qMkzCcMFVPb60E/hQy+iRLpiU8PamOfOSYoAHmmF+fFFmpPpqa6Oci2iWYpTdOUM3VF+TINud7CfbQnsZbA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@zeit/schemas": "2.36.0", + "ajv": "8.12.0", + "arg": "5.0.2", + "boxen": "7.0.0", + "chalk": "5.0.1", + "chalk-template": "0.4.0", + "clipboardy": "3.0.0", + "compression": "1.8.1", + "is-port-reachable": "4.0.0", + "serve-handler": "6.1.6", + "update-check": "1.5.4" + }, + "bin": { + "serve": "build/main.js" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/serve-handler": { + "version": "6.1.6", + "resolved": "https://registry.npmjs.org/serve-handler/-/serve-handler-6.1.6.tgz", + "integrity": "sha512-x5RL9Y2p5+Sh3D38Fh9i/iQ5ZK+e4xuXRd/pGbM4D13tgo/MGwbttUk8emytcr1YYzBYs+apnUngBDFYfpjPuQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "bytes": "3.0.0", + "content-disposition": "0.5.2", + "mime-types": "2.1.18", + "minimatch": "3.1.2", + "path-is-inside": "1.0.2", + "path-to-regexp": "3.3.0", + "range-parser": "1.2.0" + } + }, + "node_modules/serve-handler/node_modules/bytes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", + "integrity": "sha512-pMhOfFDPiv9t5jjIXkHosWmkSyQbvsgEVNkz0ERHbuLh2T/7j4Mqqpz523Fe8MVY89KC6Sh/QfS2sM+SjgFDcw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/serve-handler/node_modules/mime-db": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.33.0.tgz", + "integrity": "sha512-BHJ/EKruNIqJf/QahvxwQZXKygOQ256myeN/Ew+THcAa5q+PjyTTMMeNQC4DZw5AwfvelsUrA6B67NKMqXDbzQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/serve-handler/node_modules/mime-types": { + "version": "2.1.18", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.18.tgz", + "integrity": "sha512-lc/aahn+t4/SWV/qcmumYjymLsWfN3ELhpmVuUFjgsORruuZPVSwAQryq+HHGvO/SI2KVX26bx+En+zhM8g8hQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "mime-db": "~1.33.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/siginfo": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz", + "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==", + "dev": true, + "license": "ISC" + }, + "node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/stackback": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz", + "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==", + "dev": true, + "license": "MIT" + }, + "node_modules/std-env": { + "version": "3.10.0", + "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.10.0.tgz", + "integrity": "sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==", + "dev": true, + "license": "MIT" + }, + "node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/strip-ansi": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.2.tgz", + "integrity": "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/symbol-tree": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", + "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==", + "dev": true, + "license": "MIT" + }, + "node_modules/tinybench": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz", + "integrity": "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==", + "dev": true, + "license": "MIT" + }, + "node_modules/tinyexec": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-0.3.2.tgz", + "integrity": "sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==", + "dev": true, + "license": "MIT" + }, + "node_modules/tinypool": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-1.1.1.tgz", + "integrity": "sha512-Zba82s87IFq9A9XmjiX5uZA/ARWDrB03OHlq+Vw1fSdt0I+4/Kutwy8BP4Y/y/aORMo61FQ0vIb5j44vSo5Pkg==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.0.0 || >=20.0.0" + } + }, + "node_modules/tinyrainbow": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-1.2.0.tgz", + "integrity": "sha512-weEDEq7Z5eTHPDh4xjX789+fHfF+P8boiFB+0vbWzpbnbsEr/GRaohi/uMKxg8RZMXnl1ItAi/IUHWMsjDV7kQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/tinyspy": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/tinyspy/-/tinyspy-3.0.2.tgz", + "integrity": "sha512-n1cw8k1k0x4pgA2+9XrOkFydTerNcJ1zWCO5Nn9scWHTD+5tp8dghT2x1uduQePZTZgd3Tupf+x9BxJjeJi77Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/tldts": { + "version": "6.1.86", + "resolved": "https://registry.npmjs.org/tldts/-/tldts-6.1.86.tgz", + "integrity": "sha512-WMi/OQ2axVTf/ykqCQgXiIct+mSQDFdH2fkwhPwgEwvJ1kSzZRiinb0zF2Xb8u4+OqPChmyI6MEu4EezNJz+FQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "tldts-core": "^6.1.86" + }, + "bin": { + "tldts": "bin/cli.js" + } + }, + "node_modules/tldts-core": { + "version": "6.1.86", + "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-6.1.86.tgz", + "integrity": "sha512-Je6p7pkk+KMzMv2XXKmAE3McmolOQFdxkKw0R8EYNr7sELW46JqnNeTX8ybPiQgvg1ymCoF8LXs5fzFaZvJPTA==", + "dev": true, + "license": "MIT" + }, + "node_modules/tough-cookie": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-5.1.2.tgz", + "integrity": "sha512-FVDYdxtnj0G6Qm/DhNPSb8Ju59ULcup3tuJxkFb5K8Bv2pUXILbf0xZWU8PX8Ov19OXljbUyveOFwRMwkXzO+A==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "tldts": "^6.1.32" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/tr46": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-5.1.1.tgz", + "integrity": "sha512-hdF5ZgjTqgAntKkklYw0R03MG2x/bSzTtkxmIRw/sTNV8YXsCJ1tfLAX23lhxhHJlEf3CRCOCGGWw3vI3GaSPw==", + "dev": true, + "license": "MIT", + "dependencies": { + "punycode": "^2.3.1" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/type-fest": { + "version": "2.19.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.19.0.tgz", + "integrity": "sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=12.20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/typescript": { + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/undici-types": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true + }, + "node_modules/update-check": { + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/update-check/-/update-check-1.5.4.tgz", + "integrity": "sha512-5YHsflzHP4t1G+8WGPlvKbJEbAJGCgw+Em+dGR1KmBUbr1J36SJBqlHLjR7oob7sco5hWHGQVcr9B2poIVDDTQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "registry-auth-token": "3.3.2", + "registry-url": "3.1.0" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/vite": { + "version": "5.4.21", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.21.tgz", + "integrity": "sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "^0.21.3", + "postcss": "^8.4.43", + "rollup": "^4.20.0" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^18.0.0 || >=20.0.0", + "less": "*", + "lightningcss": "^1.21.0", + "sass": "*", + "sass-embedded": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.4.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + } + } + }, + "node_modules/vite-node": { + "version": "2.1.9", + "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-2.1.9.tgz", + "integrity": "sha512-AM9aQ/IPrW/6ENLQg3AGY4K1N2TGZdR5e4gu/MmmR2xR3Ll1+dib+nook92g4TV3PXVyeyxdWwtaCAiUL0hMxA==", + "dev": true, + "license": "MIT", + "dependencies": { + "cac": "^6.7.14", + "debug": "^4.3.7", + "es-module-lexer": "^1.5.4", + "pathe": "^1.1.2", + "vite": "^5.0.0" + }, + "bin": { + "vite-node": "vite-node.mjs" + }, + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/vitest": { + "version": "2.1.9", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-2.1.9.tgz", + "integrity": "sha512-MSmPM9REYqDGBI8439mA4mWhV5sKmDlBKWIYbA3lRb2PTHACE0mgKwA8yQ2xq9vxDTuk4iPrECBAEW2aoFXY0Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/expect": "2.1.9", + "@vitest/mocker": "2.1.9", + "@vitest/pretty-format": "^2.1.9", + "@vitest/runner": "2.1.9", + "@vitest/snapshot": "2.1.9", + "@vitest/spy": "2.1.9", + "@vitest/utils": "2.1.9", + "chai": "^5.1.2", + "debug": "^4.3.7", + "expect-type": "^1.1.0", + "magic-string": "^0.30.12", + "pathe": "^1.1.2", + "std-env": "^3.8.0", + "tinybench": "^2.9.0", + "tinyexec": "^0.3.1", + "tinypool": "^1.0.1", + "tinyrainbow": "^1.2.0", + "vite": "^5.0.0", + "vite-node": "2.1.9", + "why-is-node-running": "^2.3.0" + }, + "bin": { + "vitest": "vitest.mjs" + }, + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "@edge-runtime/vm": "*", + "@types/node": "^18.0.0 || >=20.0.0", + "@vitest/browser": "2.1.9", + "@vitest/ui": "2.1.9", + "happy-dom": "*", + "jsdom": "*" + }, + "peerDependenciesMeta": { + "@edge-runtime/vm": { + "optional": true + }, + "@types/node": { + "optional": true + }, + "@vitest/browser": { + "optional": true + }, + "@vitest/ui": { + "optional": true + }, + "happy-dom": { + "optional": true + }, + "jsdom": { + "optional": true + } + } + }, + "node_modules/w3c-xmlserializer": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-5.0.0.tgz", + "integrity": "sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==", + "dev": true, + "license": "MIT", + "dependencies": { + "xml-name-validator": "^5.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/webidl-conversions": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", + "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + } + }, + "node_modules/whatwg-encoding": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-3.1.1.tgz", + "integrity": "sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "iconv-lite": "0.6.3" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/whatwg-mimetype": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-4.0.0.tgz", + "integrity": "sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/whatwg-url": { + "version": "14.2.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-14.2.0.tgz", + "integrity": "sha512-De72GdQZzNTUBBChsXueQUnPKDkg/5A5zp7pFDuQAj5UFoENpiACU0wlCvzpAGnTkj++ihpKwKyYewn/XNUbKw==", + "dev": true, + "license": "MIT", + "dependencies": { + "tr46": "^5.1.0", + "webidl-conversions": "^7.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/why-is-node-running": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.3.0.tgz", + "integrity": "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==", + "dev": true, + "license": "MIT", + "dependencies": { + "siginfo": "^2.0.0", + "stackback": "0.0.2" + }, + "bin": { + "why-is-node-running": "cli.js" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/widest-line": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-4.0.1.tgz", + "integrity": "sha512-o0cyEG0e8GPzT4iGHphIOh0cJOV8fivsXxddQasHPHfoZf1ZexrfeA21w2NaEN1RHE+fXlfISmOE8R9N3u3Qig==", + "dev": true, + "license": "MIT", + "dependencies": { + "string-width": "^5.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/ws": { + "version": "8.18.3", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.3.tgz", + "integrity": "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/xml-name-validator": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-5.0.0.tgz", + "integrity": "sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/xmlchars": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", + "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==", + "dev": true, + "license": "MIT" + }, + "packages/atlas": { + "name": "@sailkit/atlas", + "version": "0.1.0", + "extraneous": true, + "license": "MIT", + "dependencies": { + "unist-util-visit": "^5.0.0" + }, + "devDependencies": { + "@types/mdast": "^4.0.0", + "@types/node": "^20.0.0", + "remark-parse": "^11.0.0", + "remark-stringify": "^11.0.0", + "typescript": "^5.3.0", + "unified": "^11.0.0", + "vitest": "^2.0.0" + }, + "peerDependencies": { + "unified": ">=10.0.0" + }, + "peerDependenciesMeta": { + "unified": { + "optional": true + } + } + }, + "packages/compass": { + "name": "@sailkit/compass", + "version": "0.1.0", + "license": "MIT", + "devDependencies": { + "typescript": "^5.3.0", + "vitest": "^2.0.0" + } + }, + "packages/lantern": { + "name": "@sailkit/lantern", + "version": "0.1.0", + "license": "MIT", + "devDependencies": { + "jsdom": "^25.0.0", + "typescript": "^5.3.0", + "vitest": "^2.0.0" + } + }, + "packages/lighthouse": { + "name": "@sailkit/lighthouse", + "version": "0.1.0", + "extraneous": true, + "license": "MIT", + "devDependencies": { + "typescript": "^5.3.0", + "vitest": "^2.0.0" + } + }, + "packages/scribe": { + "name": "@sailkit/scribe", + "version": "0.0.1", + "extraneous": true, + "license": "MIT", + "dependencies": { + "commander": "^14.0.2", + "esbuild": "^0.27.1", + "log-update": "^7.0.2" + }, + "bin": { + "scribe": "dist/cli.js" + }, + "devDependencies": { + "typescript": "^5.0.0", + "vitest": "^4.0.15" + } + }, + "packages/teleport": { + "name": "@sailkit/teleport", + "version": "0.1.0", + "license": "MIT", + "devDependencies": { + "@playwright/test": "^1.57.0", + "@sailkit/compass": "file:../compass", + "jsdom": "^25.0.0", + "serve": "^14.2.5", + "typescript": "^5.3.0", + "vitest": "^2.0.0" + }, + "peerDependencies": { + "@sailkit/compass": ">=0.1.0" + }, + "peerDependenciesMeta": { + "@sailkit/compass": { + "optional": true + } + } + } + } +} diff --git a/package.json b/package.json index d635b36..85b8c43 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { - "name": "sailkit", - "version": "0.1.0", + "name": "bearing-dev", + "version": "0.0.1", "private": true, "type": "module", "workspaces": [ diff --git a/packages/compass/README.md b/packages/compass/README.md index 67fb16a..62a85e6 100644 --- a/packages/compass/README.md +++ b/packages/compass/README.md @@ -1,4 +1,4 @@ -# @sailkit/compass +# @bearing-dev/compass Headless navigation cursor for ordered and nested content. diff --git a/packages/compass/package-lock.json b/packages/compass/package-lock.json new file mode 100644 index 0000000..d683881 --- /dev/null +++ b/packages/compass/package-lock.json @@ -0,0 +1,1392 @@ +{ + "name": "@sailkit/compass", + "version": "0.1.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "@sailkit/compass", + "version": "0.1.0", + "license": "MIT", + "devDependencies": { + "typescript": "^5.3.0", + "vitest": "^2.0.0" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz", + "integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.21.5.tgz", + "integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz", + "integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.21.5.tgz", + "integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz", + "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz", + "integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz", + "integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz", + "integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz", + "integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz", + "integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz", + "integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz", + "integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz", + "integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz", + "integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz", + "integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz", + "integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz", + "integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz", + "integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz", + "integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz", + "integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz", + "integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz", + "integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz", + "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "dev": true, + "license": "MIT" + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.53.3.tgz", + "integrity": "sha512-mRSi+4cBjrRLoaal2PnqH82Wqyb+d3HsPUN/W+WslCXsZsyHa9ZeQQX/pQsZaVIWDkPcpV6jJ+3KLbTbgnwv8w==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.53.3.tgz", + "integrity": "sha512-CbDGaMpdE9sh7sCmTrTUyllhrg65t6SwhjlMJsLr+J8YjFuPmCEjbBSx4Z/e4SmDyH3aB5hGaJUP2ltV/vcs4w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.53.3.tgz", + "integrity": "sha512-Nr7SlQeqIBpOV6BHHGZgYBuSdanCXuw09hon14MGOLGmXAFYjx1wNvquVPmpZnl0tLjg25dEdr4IQ6GgyToCUA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.53.3.tgz", + "integrity": "sha512-DZ8N4CSNfl965CmPktJ8oBnfYr3F8dTTNBQkRlffnUarJ2ohudQD17sZBa097J8xhQ26AwhHJ5mvUyQW8ddTsQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.53.3.tgz", + "integrity": "sha512-yMTrCrK92aGyi7GuDNtGn2sNW+Gdb4vErx4t3Gv/Tr+1zRb8ax4z8GWVRfr3Jw8zJWvpGHNpss3vVlbF58DZ4w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.53.3.tgz", + "integrity": "sha512-lMfF8X7QhdQzseM6XaX0vbno2m3hlyZFhwcndRMw8fbAGUGL3WFMBdK0hbUBIUYcEcMhVLr1SIamDeuLBnXS+Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.53.3.tgz", + "integrity": "sha512-k9oD15soC/Ln6d2Wv/JOFPzZXIAIFLp6B+i14KhxAfnq76ajt0EhYc5YPeX6W1xJkAdItcVT+JhKl1QZh44/qw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.53.3.tgz", + "integrity": "sha512-vTNlKq+N6CK/8UktsrFuc+/7NlEYVxgaEgRXVUVK258Z5ymho29skzW1sutgYjqNnquGwVUObAaxae8rZ6YMhg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.53.3.tgz", + "integrity": "sha512-RGrFLWgMhSxRs/EWJMIFM1O5Mzuz3Xy3/mnxJp/5cVhZ2XoCAxJnmNsEyeMJtpK+wu0FJFWz+QF4mjCA7AUQ3w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.53.3.tgz", + "integrity": "sha512-kASyvfBEWYPEwe0Qv4nfu6pNkITLTb32p4yTgzFCocHnJLAHs+9LjUu9ONIhvfT/5lv4YS5muBHyuV84epBo/A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-gnu": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.53.3.tgz", + "integrity": "sha512-JiuKcp2teLJwQ7vkJ95EwESWkNRFJD7TQgYmCnrPtlu50b4XvT5MOmurWNrCj3IFdyjBQ5p9vnrX4JM6I8OE7g==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.53.3.tgz", + "integrity": "sha512-EoGSa8nd6d3T7zLuqdojxC20oBfNT8nexBbB/rkxgKj5T5vhpAQKKnD+h3UkoMuTyXkP5jTjK/ccNRmQrPNDuw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.53.3.tgz", + "integrity": "sha512-4s+Wped2IHXHPnAEbIB0YWBv7SDohqxobiiPA1FIWZpX+w9o2i4LezzH/NkFUl8LRci/8udci6cLq+jJQlh+0g==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.53.3.tgz", + "integrity": "sha512-68k2g7+0vs2u9CxDt5ktXTngsxOQkSEV/xBbwlqYcUrAVh6P9EgMZvFsnHy4SEiUl46Xf0IObWVbMvPrr2gw8A==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.53.3.tgz", + "integrity": "sha512-VYsFMpULAz87ZW6BVYw3I6sWesGpsP9OPcyKe8ofdg9LHxSbRMd7zrVrr5xi/3kMZtpWL/wC+UIJWJYVX5uTKg==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.53.3.tgz", + "integrity": "sha512-3EhFi1FU6YL8HTUJZ51imGJWEX//ajQPfqWLI3BQq4TlvHy4X0MOr5q3D2Zof/ka0d5FNdPwZXm3Yyib/UEd+w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.53.3.tgz", + "integrity": "sha512-eoROhjcc6HbZCJr+tvVT8X4fW3/5g/WkGvvmwz/88sDtSJzO7r/blvoBDgISDiCjDRZmHpwud7h+6Q9JxFwq1Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.53.3.tgz", + "integrity": "sha512-OueLAWgrNSPGAdUdIjSWXw+u/02BRTcnfw9PN41D2vq/JSEPnJnVuBgw18VkN8wcd4fjUs+jFHVM4t9+kBSNLw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.53.3.tgz", + "integrity": "sha512-GOFuKpsxR/whszbF/bzydebLiXIHSgsEUp6M0JI8dWvi+fFa1TD6YQa4aSZHtpmh2/uAlj/Dy+nmby3TJ3pkTw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.53.3.tgz", + "integrity": "sha512-iah+THLcBJdpfZ1TstDFbKNznlzoxa8fmnFYK4V67HvmuNYkVdAywJSoteUszvBQ9/HqN2+9AZghbajMsFT+oA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.53.3.tgz", + "integrity": "sha512-J9QDiOIZlZLdcot5NXEepDkstocktoVjkaKUtqzgzpt2yWjGlbYiKyp05rWwk4nypbYUNoFAztEgixoLaSETkg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.53.3.tgz", + "integrity": "sha512-UhTd8u31dXadv0MopwGgNOBpUVROFKWVQgAg5N1ESyCz8AuBcMqm4AuTjrwgQKGDfoFuz02EuMRHQIw/frmYKQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@types/estree": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@vitest/expect": { + "version": "2.1.9", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-2.1.9.tgz", + "integrity": "sha512-UJCIkTBenHeKT1TTlKMJWy1laZewsRIzYighyYiJKZreqtdxSos/S1t+ktRMQWu2CKqaarrkeszJx1cgC5tGZw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/spy": "2.1.9", + "@vitest/utils": "2.1.9", + "chai": "^5.1.2", + "tinyrainbow": "^1.2.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/mocker": { + "version": "2.1.9", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-2.1.9.tgz", + "integrity": "sha512-tVL6uJgoUdi6icpxmdrn5YNo3g3Dxv+IHJBr0GXHaEdTcw3F+cPKnsXFhli6nO+f/6SDKPHEK1UN+k+TQv0Ehg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/spy": "2.1.9", + "estree-walker": "^3.0.3", + "magic-string": "^0.30.12" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "msw": "^2.4.9", + "vite": "^5.0.0" + }, + "peerDependenciesMeta": { + "msw": { + "optional": true + }, + "vite": { + "optional": true + } + } + }, + "node_modules/@vitest/pretty-format": { + "version": "2.1.9", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-2.1.9.tgz", + "integrity": "sha512-KhRIdGV2U9HOUzxfiHmY8IFHTdqtOhIzCpd8WRdJiE7D/HUcZVD0EgQCVjm+Q9gkUXWgBvMmTtZgIG48wq7sOQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "tinyrainbow": "^1.2.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/runner": { + "version": "2.1.9", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-2.1.9.tgz", + "integrity": "sha512-ZXSSqTFIrzduD63btIfEyOmNcBmQvgOVsPNPe0jYtESiXkhd8u2erDLnMxmGrDCwHCCHE7hxwRDCT3pt0esT4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/utils": "2.1.9", + "pathe": "^1.1.2" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/snapshot": { + "version": "2.1.9", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-2.1.9.tgz", + "integrity": "sha512-oBO82rEjsxLNJincVhLhaxxZdEtV0EFHMK5Kmx5sJ6H9L183dHECjiefOAdnqpIgT5eZwT04PoggUnW88vOBNQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/pretty-format": "2.1.9", + "magic-string": "^0.30.12", + "pathe": "^1.1.2" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/spy": { + "version": "2.1.9", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-2.1.9.tgz", + "integrity": "sha512-E1B35FwzXXTs9FHNK6bDszs7mtydNi5MIfUWpceJ8Xbfb1gBMscAnwLbEu+B44ed6W3XjL9/ehLPHR1fkf1KLQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "tinyspy": "^3.0.2" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/utils": { + "version": "2.1.9", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-2.1.9.tgz", + "integrity": "sha512-v0psaMSkNJ3A2NMrUEHFRzJtDPFn+/VWZ5WxImB21T9fjucJRmS7xCS3ppEnARb9y11OAzaD+P2Ps+b+BGX5iQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/pretty-format": "2.1.9", + "loupe": "^3.1.2", + "tinyrainbow": "^1.2.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/assertion-error": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz", + "integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + } + }, + "node_modules/cac": { + "version": "6.7.14", + "resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz", + "integrity": "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/chai": { + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/chai/-/chai-5.3.3.tgz", + "integrity": "sha512-4zNhdJD/iOjSH0A05ea+Ke6MU5mmpQcbQsSOkgdaUMJ9zTlDTD/GYlwohmIE2u0gaxHYiVHEn1Fw9mZ/ktJWgw==", + "dev": true, + "license": "MIT", + "dependencies": { + "assertion-error": "^2.0.1", + "check-error": "^2.1.1", + "deep-eql": "^5.0.1", + "loupe": "^3.1.0", + "pathval": "^2.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/check-error": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/check-error/-/check-error-2.1.1.tgz", + "integrity": "sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 16" + } + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/deep-eql": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-5.0.2.tgz", + "integrity": "sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/es-module-lexer": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.7.0.tgz", + "integrity": "sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==", + "dev": true, + "license": "MIT" + }, + "node_modules/esbuild": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz", + "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.21.5", + "@esbuild/android-arm": "0.21.5", + "@esbuild/android-arm64": "0.21.5", + "@esbuild/android-x64": "0.21.5", + "@esbuild/darwin-arm64": "0.21.5", + "@esbuild/darwin-x64": "0.21.5", + "@esbuild/freebsd-arm64": "0.21.5", + "@esbuild/freebsd-x64": "0.21.5", + "@esbuild/linux-arm": "0.21.5", + "@esbuild/linux-arm64": "0.21.5", + "@esbuild/linux-ia32": "0.21.5", + "@esbuild/linux-loong64": "0.21.5", + "@esbuild/linux-mips64el": "0.21.5", + "@esbuild/linux-ppc64": "0.21.5", + "@esbuild/linux-riscv64": "0.21.5", + "@esbuild/linux-s390x": "0.21.5", + "@esbuild/linux-x64": "0.21.5", + "@esbuild/netbsd-x64": "0.21.5", + "@esbuild/openbsd-x64": "0.21.5", + "@esbuild/sunos-x64": "0.21.5", + "@esbuild/win32-arm64": "0.21.5", + "@esbuild/win32-ia32": "0.21.5", + "@esbuild/win32-x64": "0.21.5" + } + }, + "node_modules/estree-walker": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", + "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0" + } + }, + "node_modules/expect-type": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.3.0.tgz", + "integrity": "sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/loupe": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/loupe/-/loupe-3.2.1.tgz", + "integrity": "sha512-CdzqowRJCeLU72bHvWqwRBBlLcMEtIvGrlvef74kMnV2AolS9Y8xUv1I0U/MNAWMhBlKIoyuEgoJ0t/bbwHbLQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/magic-string": { + "version": "0.30.21", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", + "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/nanoid": { + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/pathe": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-1.1.2.tgz", + "integrity": "sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/pathval": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pathval/-/pathval-2.0.1.tgz", + "integrity": "sha512-//nshmD55c46FuFw26xV/xFAaB5HF9Xdap7HJBBnrKdAd6/GxDBaNA1870O79+9ueg61cZLSVc+OaFlfmObYVQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14.16" + } + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, + "node_modules/postcss": { + "version": "8.5.6", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", + "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.11", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/rollup": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.53.3.tgz", + "integrity": "sha512-w8GmOxZfBmKknvdXU1sdM9NHcoQejwF/4mNgj2JuEEdRaHwwF12K7e9eXn1nLZ07ad+du76mkVsyeb2rKGllsA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.8" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.53.3", + "@rollup/rollup-android-arm64": "4.53.3", + "@rollup/rollup-darwin-arm64": "4.53.3", + "@rollup/rollup-darwin-x64": "4.53.3", + "@rollup/rollup-freebsd-arm64": "4.53.3", + "@rollup/rollup-freebsd-x64": "4.53.3", + "@rollup/rollup-linux-arm-gnueabihf": "4.53.3", + "@rollup/rollup-linux-arm-musleabihf": "4.53.3", + "@rollup/rollup-linux-arm64-gnu": "4.53.3", + "@rollup/rollup-linux-arm64-musl": "4.53.3", + "@rollup/rollup-linux-loong64-gnu": "4.53.3", + "@rollup/rollup-linux-ppc64-gnu": "4.53.3", + "@rollup/rollup-linux-riscv64-gnu": "4.53.3", + "@rollup/rollup-linux-riscv64-musl": "4.53.3", + "@rollup/rollup-linux-s390x-gnu": "4.53.3", + "@rollup/rollup-linux-x64-gnu": "4.53.3", + "@rollup/rollup-linux-x64-musl": "4.53.3", + "@rollup/rollup-openharmony-arm64": "4.53.3", + "@rollup/rollup-win32-arm64-msvc": "4.53.3", + "@rollup/rollup-win32-ia32-msvc": "4.53.3", + "@rollup/rollup-win32-x64-gnu": "4.53.3", + "@rollup/rollup-win32-x64-msvc": "4.53.3", + "fsevents": "~2.3.2" + } + }, + "node_modules/siginfo": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz", + "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==", + "dev": true, + "license": "ISC" + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/stackback": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz", + "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==", + "dev": true, + "license": "MIT" + }, + "node_modules/std-env": { + "version": "3.10.0", + "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.10.0.tgz", + "integrity": "sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==", + "dev": true, + "license": "MIT" + }, + "node_modules/tinybench": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz", + "integrity": "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==", + "dev": true, + "license": "MIT" + }, + "node_modules/tinyexec": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-0.3.2.tgz", + "integrity": "sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==", + "dev": true, + "license": "MIT" + }, + "node_modules/tinypool": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-1.1.1.tgz", + "integrity": "sha512-Zba82s87IFq9A9XmjiX5uZA/ARWDrB03OHlq+Vw1fSdt0I+4/Kutwy8BP4Y/y/aORMo61FQ0vIb5j44vSo5Pkg==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.0.0 || >=20.0.0" + } + }, + "node_modules/tinyrainbow": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-1.2.0.tgz", + "integrity": "sha512-weEDEq7Z5eTHPDh4xjX789+fHfF+P8boiFB+0vbWzpbnbsEr/GRaohi/uMKxg8RZMXnl1ItAi/IUHWMsjDV7kQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/tinyspy": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/tinyspy/-/tinyspy-3.0.2.tgz", + "integrity": "sha512-n1cw8k1k0x4pgA2+9XrOkFydTerNcJ1zWCO5Nn9scWHTD+5tp8dghT2x1uduQePZTZgd3Tupf+x9BxJjeJi77Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/typescript": { + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/vite": { + "version": "5.4.21", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.21.tgz", + "integrity": "sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "^0.21.3", + "postcss": "^8.4.43", + "rollup": "^4.20.0" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^18.0.0 || >=20.0.0", + "less": "*", + "lightningcss": "^1.21.0", + "sass": "*", + "sass-embedded": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.4.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + } + } + }, + "node_modules/vite-node": { + "version": "2.1.9", + "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-2.1.9.tgz", + "integrity": "sha512-AM9aQ/IPrW/6ENLQg3AGY4K1N2TGZdR5e4gu/MmmR2xR3Ll1+dib+nook92g4TV3PXVyeyxdWwtaCAiUL0hMxA==", + "dev": true, + "license": "MIT", + "dependencies": { + "cac": "^6.7.14", + "debug": "^4.3.7", + "es-module-lexer": "^1.5.4", + "pathe": "^1.1.2", + "vite": "^5.0.0" + }, + "bin": { + "vite-node": "vite-node.mjs" + }, + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/vitest": { + "version": "2.1.9", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-2.1.9.tgz", + "integrity": "sha512-MSmPM9REYqDGBI8439mA4mWhV5sKmDlBKWIYbA3lRb2PTHACE0mgKwA8yQ2xq9vxDTuk4iPrECBAEW2aoFXY0Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/expect": "2.1.9", + "@vitest/mocker": "2.1.9", + "@vitest/pretty-format": "^2.1.9", + "@vitest/runner": "2.1.9", + "@vitest/snapshot": "2.1.9", + "@vitest/spy": "2.1.9", + "@vitest/utils": "2.1.9", + "chai": "^5.1.2", + "debug": "^4.3.7", + "expect-type": "^1.1.0", + "magic-string": "^0.30.12", + "pathe": "^1.1.2", + "std-env": "^3.8.0", + "tinybench": "^2.9.0", + "tinyexec": "^0.3.1", + "tinypool": "^1.0.1", + "tinyrainbow": "^1.2.0", + "vite": "^5.0.0", + "vite-node": "2.1.9", + "why-is-node-running": "^2.3.0" + }, + "bin": { + "vitest": "vitest.mjs" + }, + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "@edge-runtime/vm": "*", + "@types/node": "^18.0.0 || >=20.0.0", + "@vitest/browser": "2.1.9", + "@vitest/ui": "2.1.9", + "happy-dom": "*", + "jsdom": "*" + }, + "peerDependenciesMeta": { + "@edge-runtime/vm": { + "optional": true + }, + "@types/node": { + "optional": true + }, + "@vitest/browser": { + "optional": true + }, + "@vitest/ui": { + "optional": true + }, + "happy-dom": { + "optional": true + }, + "jsdom": { + "optional": true + } + } + }, + "node_modules/why-is-node-running": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.3.0.tgz", + "integrity": "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==", + "dev": true, + "license": "MIT", + "dependencies": { + "siginfo": "^2.0.0", + "stackback": "0.0.2" + }, + "bin": { + "why-is-node-running": "cli.js" + }, + "engines": { + "node": ">=8" + } + } + } +} diff --git a/packages/compass/package.json b/packages/compass/package.json index e7fe2aa..b25cfb3 100644 --- a/packages/compass/package.json +++ b/packages/compass/package.json @@ -1,6 +1,6 @@ { - "name": "@sailkit/compass", - "version": "0.1.0", + "name": "@bearing-dev/compass", + "version": "0.0.1", "description": "Headless navigation state for lists and trees", "type": "module", "main": "./dist/index.js", diff --git a/packages/lantern/README.md b/packages/lantern/README.md index 233e32f..f386609 100644 --- a/packages/lantern/README.md +++ b/packages/lantern/README.md @@ -1,4 +1,4 @@ -# @sailkit/lantern +# @bearing-dev/lantern Theme toggle with flash-free hydration. @@ -6,8 +6,8 @@ Theme toggle with flash-free hydration. ```astro --- -import { initScript } from '@sailkit/lantern'; -import ThemeToggle from '@sailkit/lantern/ThemeToggle.astro'; +import { initScript } from '@bearing-dev/lantern'; +import ThemeToggle from '@bearing-dev/lantern/ThemeToggle.astro'; --- @@ -22,7 +22,7 @@ import ThemeToggle from '@sailkit/lantern/ThemeToggle.astro'; ## API ```typescript -import { initTheme, toggleTheme, getTheme } from '@sailkit/lantern'; +import { initTheme, toggleTheme, getTheme } from '@bearing-dev/lantern'; initTheme(); // restore from localStorage toggleTheme(); // dark <-> light @@ -66,8 +66,8 @@ Include `initScript` before paint to prevent flash of wrong theme: ## Optional Example CSS ```typescript -import '@sailkit/lantern/theme-dark.css'; -import '@sailkit/lantern/theme-light.css'; +import '@bearing-dev/lantern/theme-dark.css'; +import '@bearing-dev/lantern/theme-light.css'; ``` These are minimal examples. Override with your own theme CSS. diff --git a/packages/lantern/package.json b/packages/lantern/package.json index 898bc66..f9ac601 100644 --- a/packages/lantern/package.json +++ b/packages/lantern/package.json @@ -1,6 +1,6 @@ { - "name": "@sailkit/lantern", - "version": "0.1.0", + "name": "@bearing-dev/lantern", + "version": "0.0.1", "description": "Theme toggle with flash-free hydration", "type": "module", "main": "./dist/index.js", diff --git a/packages/lantern/src/index.ts b/packages/lantern/src/index.ts index 6824cb9..7a2d534 100644 --- a/packages/lantern/src/index.ts +++ b/packages/lantern/src/index.ts @@ -3,7 +3,7 @@ * * @example * ```typescript - * import { initTheme, toggleTheme, onThemeChange } from '@sailkit/lantern'; + * import { initTheme, toggleTheme, onThemeChange } from '@bearing-dev/lantern'; * * // Initialize on page load (call early to prevent flash) * initTheme(); diff --git a/packages/teleport/README.md b/packages/teleport/README.md index b3cfe31..a3459ae 100644 --- a/packages/teleport/README.md +++ b/packages/teleport/README.md @@ -1,4 +1,4 @@ -# @sailkit/teleport +# @bearing-dev/teleport Vim-style keyboard navigation bindings with DOM integration. @@ -6,7 +6,7 @@ Vim-style keyboard navigation bindings with DOM integration. ```astro --- -import Teleport from '@sailkit/teleport/Teleport.astro'; +import Teleport from '@bearing-dev/teleport/Teleport.astro'; --- @@ -52,7 +52,7 @@ Three layers of abstraction for custom integrations: ```typescript // Layer 3: Full integration (batteries included) -import { initTeleport } from '@sailkit/teleport'; +import { initTeleport } from '@bearing-dev/teleport'; const teleport = initTeleport({ itemSelector: '.nav-item', @@ -65,7 +65,7 @@ const teleport = initTeleport({ teleport.destroy(); // Layer 2: DOM adapter only -import { createDOMNavigator } from '@sailkit/teleport'; +import { createDOMNavigator } from '@bearing-dev/teleport'; const navigator = createDOMNavigator({ getItems: () => document.querySelectorAll('.item'), @@ -78,7 +78,7 @@ navigator.prev(); navigator.goTo(5); // Layer 1: Pure key bindings -import { createKeyboardHandler, DEFAULT_BINDINGS } from '@sailkit/teleport'; +import { createKeyboardHandler, DEFAULT_BINDINGS } from '@bearing-dev/teleport'; const handler = createKeyboardHandler({ bindings: { ...DEFAULT_BINDINGS, nextItem: ['n'] }, @@ -92,7 +92,7 @@ document.addEventListener('keydown', handler.handleKeydown); ## Custom Bindings ```typescript -import { initTeleport } from '@sailkit/teleport'; +import { initTeleport } from '@bearing-dev/teleport'; initTeleport({ itemSelector: '.nav-item', @@ -123,6 +123,6 @@ Listen for the `teleport:open-finder` event: ```javascript document.addEventListener('teleport:open-finder', () => { // Open your fuzzy finder UI - // Use @sailkit/compass data structure for items + // Use @bearing-dev/compass data structure for items }); ``` diff --git a/packages/teleport/Teleport.astro b/packages/teleport/Teleport.astro index 9c5f39e..3cf449f 100644 --- a/packages/teleport/Teleport.astro +++ b/packages/teleport/Teleport.astro @@ -14,7 +14,7 @@ interface Props { sidebarSelector?: string; /** Class to add to highlighted item (default: 'teleport-highlight') */ highlightClass?: string; - /** Enable fuzzy finder (requires onOpenFinder callback) */ + /** Enable fuzzy finder (emits teleport:open-finder event) */ enableFinder?: boolean; } @@ -27,7 +27,16 @@ const { } = Astro.props; --- - - diff --git a/packages/teleport/package.json b/packages/teleport/package.json index e91770c..86f0c07 100644 --- a/packages/teleport/package.json +++ b/packages/teleport/package.json @@ -1,6 +1,6 @@ { - "name": "@sailkit/teleport", - "version": "0.1.0", + "name": "@bearing-dev/teleport", + "version": "0.0.1", "description": "Vim-style keyboard navigation bindings with DOM integration", "type": "module", "main": "./dist/index.js", @@ -21,19 +21,23 @@ "prepare": "npm run build", "test": "vitest run", "test:watch": "vitest", + "test:e2e": "playwright test", + "test:e2e:ui": "playwright test --ui", "typecheck": "tsc --noEmit" }, "peerDependencies": { - "@sailkit/compass": ">=0.1.0" + "@bearing-dev/compass": ">=0.0.1" }, "peerDependenciesMeta": { - "@sailkit/compass": { + "@bearing-dev/compass": { "optional": true } }, "devDependencies": { - "@sailkit/compass": "file:../compass", + "@playwright/test": "^1.57.0", + "@bearing-dev/compass": "file:../compass", "jsdom": "^25.0.0", + "serve": "^14.2.5", "typescript": "^5.3.0", "vitest": "^2.0.0" }, diff --git a/packages/teleport/src/dom.ts b/packages/teleport/src/dom.ts index 085fbba..0eb6f8e 100644 --- a/packages/teleport/src/dom.ts +++ b/packages/teleport/src/dom.ts @@ -1,146 +1,11 @@ /** - * Layer 2: DOM adapter + * DOM utilities for Teleport * - * Handles DOM operations like highlighting elements and scrolling. - * Works with any list of elements - can be used standalone or with compass. + * Helper functions for scrolling. No navigation state here. */ -import type { DOMNavigatorConfig, DOMNavigator } from './types.js'; - -/** - * Default scroll behavior - */ -const DEFAULT_SCROLL_BEHAVIOR: ScrollIntoViewOptions = { - behavior: 'smooth', - block: 'nearest', - inline: 'nearest', -}; - -/** - * Create a DOM navigator for a list of elements. - * - * Manages highlighting and scrolling for keyboard navigation. - * The getItems function is called each time to support dynamic lists. - */ -export function createDOMNavigator(config: DOMNavigatorConfig): DOMNavigator { - const { - getItems, - highlightClass = 'teleport-highlight', - scrollBehavior = DEFAULT_SCROLL_BEHAVIOR, - onSelect, - onHighlightChange, - } = config; - - let currentIndex = -1; - let items: HTMLElement[] = []; - - function refresh(): void { - items = getItems(); - // Clamp current index to valid range - if (items.length === 0) { - currentIndex = -1; - } else if (currentIndex >= items.length) { - currentIndex = items.length - 1; - } - } - - function clearHighlight(): void { - // Remove highlight from all items (not just current, in case list changed) - items.forEach((item) => item.classList.remove(highlightClass)); - } - - function applyHighlight(): void { - clearHighlight(); - if (currentIndex >= 0 && currentIndex < items.length) { - const item = items[currentIndex]; - item.classList.add(highlightClass); - // scrollIntoView may not exist in test environments - if (typeof item.scrollIntoView === 'function') { - item.scrollIntoView(scrollBehavior); - } - onHighlightChange?.(item, currentIndex); - } else { - onHighlightChange?.(null, -1); - } - } - - function goTo(index: number): void { - refresh(); - if (index >= 0 && index < items.length) { - currentIndex = index; - applyHighlight(); - } - } - - function next(): void { - refresh(); - if (items.length === 0) return; - - if (currentIndex < 0) { - // Start at first item - currentIndex = 0; - } else if (currentIndex < items.length - 1) { - currentIndex++; - } else { - // Wrap to start - currentIndex = 0; - } - applyHighlight(); - } - - function prev(): void { - refresh(); - if (items.length === 0) return; - - if (currentIndex < 0) { - // Start at last item - currentIndex = items.length - 1; - } else if (currentIndex > 0) { - currentIndex--; - } else { - // Wrap to end - currentIndex = items.length - 1; - } - applyHighlight(); - } - - function clear(): void { - clearHighlight(); - currentIndex = -1; - onHighlightChange?.(null, -1); - } - - function select(): void { - if (currentIndex >= 0 && currentIndex < items.length) { - onSelect?.(items[currentIndex], currentIndex); - } - } - - // Initialize items - refresh(); - - return { - get currentIndex() { - return currentIndex; - }, - get currentItem() { - return currentIndex >= 0 && currentIndex < items.length - ? items[currentIndex] - : null; - }, - get count() { - return items.length; - }, - next, - prev, - goTo, - clear, - refresh, - }; -} - /** - * Scroll an element by a given amount. + * Scroll an element or window by a given amount. */ export function scrollElement( element: HTMLElement | Window, @@ -162,25 +27,3 @@ export function scrollElement( export function getViewportHeight(): number { return window.innerHeight; } - -/** - * Sync DOM navigator with current URL. - * Finds the item matching the current path and highlights it. - */ -export function syncWithCurrentPath( - navigator: DOMNavigator, - getItems: () => HTMLElement[], - pathAttribute: string = 'href' -): void { - const currentPath = window.location.pathname; - const items = getItems(); - - const index = items.findIndex((item) => { - const href = item.getAttribute(pathAttribute); - return href === currentPath; - }); - - if (index !== -1) { - navigator.goTo(index); - } -} diff --git a/packages/teleport/src/index.ts b/packages/teleport/src/index.ts index ea74283..be70427 100644 --- a/packages/teleport/src/index.ts +++ b/packages/teleport/src/index.ts @@ -1,32 +1,27 @@ /** - * Teleport - Vim-style keyboard navigation bindings + * Teleport - Vim-style keyboard bindings * - * Three layers of abstraction: - * - Layer 1: Pure key binding functions (keys.ts) - * - Layer 2: DOM adapter for highlighting/scrolling (dom.ts) - * - Layer 3: Full integration (teleport.ts) + * A thin layer that maps keypresses to directional callbacks. + * No navigation state - use @bearing-dev/compass for that. * * @example * ```typescript - * // Layer 3: Full integration (most common) - * import { initTeleport } from '@sailkit/teleport'; + * // Standalone - just key bindings + * import { initTeleport } from '@bearing-dev/teleport'; * * const teleport = initTeleport({ - * itemSelector: '.nav-item', - * onNextPage: () => navigate('next'), - * onPrevPage: () => navigate('prev'), + * onDown: () => console.log('down'), + * onUp: () => console.log('up'), * }); * - * // Layer 1 + 2: Custom integration - * import { createKeyboardHandler, createDOMNavigator } from '@sailkit/teleport'; + * // With compass for navigation state + * import { initTeleport } from '@bearing-dev/teleport'; + * import { createNavigator } from '@bearing-dev/compass'; * - * const navigator = createDOMNavigator({ - * getItems: () => document.querySelectorAll('.item'), - * }); - * - * const keyHandler = createKeyboardHandler({ - * onNextItem: () => navigator.next(), - * onPrevItem: () => navigator.prev(), + * const nav = createNavigator({ items: ['a', 'b', 'c'], wrap: false }); + * const teleport = initTeleport({ + * onDown: () => nav.next(), + * onUp: () => nav.prev(), * }); * ``` */ @@ -37,15 +32,11 @@ export type { ParsedKey, KeyboardHandlerConfig, KeyboardHandler, - DOMNavigatorConfig, - DOMNavigator, TeleportConfig, Teleport, - FinderItem, - FuzzyFinderConfig, } from './types.js'; -// Layer 1: Key bindings +// Key binding utilities export { DEFAULT_BINDINGS, parseKey, @@ -55,13 +46,8 @@ export { createKeyboardHandler, } from './keys.js'; -// Layer 2: DOM adapter -export { - createDOMNavigator, - scrollElement, - getViewportHeight, - syncWithCurrentPath, -} from './dom.js'; +// DOM utilities +export { scrollElement, getViewportHeight } from './dom.js'; -// Layer 3: Full integration +// Main entry point export { initTeleport, injectTeleportStyles } from './teleport.js'; diff --git a/packages/teleport/src/keys.ts b/packages/teleport/src/keys.ts index fe8adce..835bb8a 100644 --- a/packages/teleport/src/keys.ts +++ b/packages/teleport/src/keys.ts @@ -13,15 +13,15 @@ import type { } from './types.js'; /** - * Default key bindings + * Default key bindings - vim-style hjkl */ export const DEFAULT_BINDINGS: Required = { - nextItem: ['j', 'ArrowDown'], - prevItem: ['k', 'ArrowUp'], + down: ['j', 'ArrowDown'], + up: ['k', 'ArrowUp'], + left: ['h', 'ArrowLeft'], + right: ['l', 'ArrowRight'], scrollDown: ['Ctrl+d'], scrollUp: ['Ctrl+u'], - nextPage: ['l', 'ArrowRight'], - prevPage: ['h', 'ArrowLeft'], select: ['Enter'], openFinder: ['t'], escape: ['Escape'], @@ -199,19 +199,19 @@ export function isTypingContext(event: KeyboardEvent): boolean { } /** - * Create a keyboard handler that maps key events to callbacks. + * Create a keyboard handler that maps key events to directional callbacks. */ export function createKeyboardHandler( config: KeyboardHandlerConfig ): KeyboardHandler { const { bindings = {}, - onNextItem, - onPrevItem, + onDown, + onUp, + onLeft, + onRight, onScrollDown, onScrollUp, - onNextPage, - onPrevPage, onSelect, onOpenFinder, onEscape, @@ -229,43 +229,45 @@ export function createKeyboardHandler( return false; } - // Check each binding - if (onNextItem && matchesAnyKey(event, mergedBindings.nextItem)) { + // Directional bindings + if (onDown && matchesAnyKey(event, mergedBindings.down)) { event.preventDefault(); - onNextItem(); + onDown(); return true; } - if (onPrevItem && matchesAnyKey(event, mergedBindings.prevItem)) { + if (onUp && matchesAnyKey(event, mergedBindings.up)) { event.preventDefault(); - onPrevItem(); + onUp(); return true; } - if (onScrollDown && matchesAnyKey(event, mergedBindings.scrollDown)) { + if (onLeft && matchesAnyKey(event, mergedBindings.left)) { event.preventDefault(); - onScrollDown(); + onLeft(); return true; } - if (onScrollUp && matchesAnyKey(event, mergedBindings.scrollUp)) { + if (onRight && matchesAnyKey(event, mergedBindings.right)) { event.preventDefault(); - onScrollUp(); + onRight(); return true; } - if (onNextPage && matchesAnyKey(event, mergedBindings.nextPage)) { + // Scroll bindings + if (onScrollDown && matchesAnyKey(event, mergedBindings.scrollDown)) { event.preventDefault(); - onNextPage(); + onScrollDown(); return true; } - if (onPrevPage && matchesAnyKey(event, mergedBindings.prevPage)) { + if (onScrollUp && matchesAnyKey(event, mergedBindings.scrollUp)) { event.preventDefault(); - onPrevPage(); + onScrollUp(); return true; } + // Action bindings if (onSelect && matchesAnyKey(event, mergedBindings.select)) { event.preventDefault(); onSelect(); diff --git a/packages/teleport/src/teleport.ts b/packages/teleport/src/teleport.ts index 35e5fed..05af436 100644 --- a/packages/teleport/src/teleport.ts +++ b/packages/teleport/src/teleport.ts @@ -1,24 +1,29 @@ /** - * Layer 3: Full Teleport integration + * Teleport - Vim-style keyboard bindings * - * Wires together key bindings, DOM navigation, and callbacks - * for a complete vim-style navigation experience. + * A thin layer that maps keypresses to directional callbacks. + * No navigation state - just key bindings. */ import type { TeleportConfig, Teleport } from './types.js'; import { createKeyboardHandler } from './keys.js'; -import { createDOMNavigator, scrollElement, getViewportHeight } from './dom.js'; /** - * Initialize Teleport with full keyboard navigation. + * Initialize Teleport keyboard bindings. * * @example * ```typescript + * // Standalone - just key bindings * const teleport = initTeleport({ - * itemSelector: '.nav-item', - * onSelect: (item) => window.location.href = item.getAttribute('href'), - * onNextPage: () => navigateToNext(), - * onPrevPage: () => navigateToPrev(), + * onDown: () => console.log('down pressed'), + * onUp: () => console.log('up pressed'), + * }); + * + * // With compass for navigation state + * const nav = createNavigator({ items: ['a', 'b', 'c'], wrap: false }); + * const teleport = initTeleport({ + * onDown: () => nav.next(), + * onUp: () => nav.prev(), * }); * * // Cleanup when done @@ -27,98 +32,32 @@ import { createDOMNavigator, scrollElement, getViewportHeight } from './dom.js'; */ export function initTeleport(config: TeleportConfig): Teleport { const { - itemSelector, - contentContainer, - sidebarContainer, - highlightClass = 'teleport-highlight', bindings, + onDown, + onUp, + onLeft, + onRight, + onScrollDown, + onScrollUp, onSelect, - onNextPage, - onPrevPage, onOpenFinder, - scrollAmount, + onEscape, + ignoreWhenTyping = true, } = config; - // Resolve containers - const resolveContainer = ( - container: HTMLElement | string | undefined, - fallbackSelector: string - ): HTMLElement | null => { - if (container instanceof HTMLElement) return container; - if (typeof container === 'string') return document.querySelector(container); - return document.querySelector(fallbackSelector); - }; - - const content = - resolveContainer(contentContainer, 'main') || document.documentElement; - const sidebar = resolveContainer(sidebarContainer, '.sidebar'); - - // Create DOM navigator for sidebar items - const navigator = createDOMNavigator({ - getItems: () => Array.from(document.querySelectorAll(itemSelector)), - highlightClass, - scrollBehavior: { behavior: 'smooth', block: 'nearest' }, - onSelect: (item, index) => { - // Default: navigate to href if present - const href = item.getAttribute('href'); - if (href && onSelect) { - onSelect(item); - } else if (href) { - window.location.href = href; - } - }, - }); - - // Calculate scroll amount (default: half viewport) - const getScrollAmount = () => scrollAmount ?? getViewportHeight() / 2; - // Create keyboard handler const keyHandler = createKeyboardHandler({ bindings, - onNextItem: () => navigator.next(), - onPrevItem: () => navigator.prev(), - onScrollDown: () => { - scrollElement(content, getScrollAmount(), 'down'); - }, - onScrollUp: () => { - scrollElement(content, getScrollAmount(), 'up'); - }, - onNextPage: () => { - if (onNextPage) { - onNextPage(); - } else { - // Default: click current highlighted item if it has a "next" sibling - const current = navigator.currentItem; - if (current) { - const href = current.getAttribute('href'); - if (href) window.location.href = href; - } - } - }, - onPrevPage: () => { - if (onPrevPage) { - onPrevPage(); - } - }, - onSelect: () => { - const current = navigator.currentItem; - if (current) { - if (onSelect) { - onSelect(current); - } else { - const href = current.getAttribute('href'); - if (href) window.location.href = href; - } - } - }, - onOpenFinder: () => { - if (onOpenFinder) { - onOpenFinder(); - } - }, - onEscape: () => { - navigator.clear(); - }, + onDown, + onUp, + onLeft, + onRight, + onScrollDown, + onScrollUp, + onSelect, + onOpenFinder, + onEscape, + ignoreWhenTyping, }); // Attach global keydown listener @@ -128,21 +67,9 @@ export function initTeleport(config: TeleportConfig): Teleport { document.addEventListener('keydown', handleKeydown); - // Sync with current URL on init - const currentPath = window.location.pathname; - const items = Array.from(document.querySelectorAll(itemSelector)); - const currentIndex = items.findIndex( - (item) => item.getAttribute('href') === currentPath - ); - if (currentIndex !== -1) { - navigator.goTo(currentIndex); - } - return { - navigator, destroy() { document.removeEventListener('keydown', handleKeydown); - navigator.clear(); keyHandler.destroy(); }, }; diff --git a/packages/teleport/src/types.ts b/packages/teleport/src/types.ts index ecd9a52..376bf5d 100644 --- a/packages/teleport/src/types.ts +++ b/packages/teleport/src/types.ts @@ -3,23 +3,23 @@ */ /** - * Key binding configuration - maps actions to key patterns. - * Each action can have multiple keys that trigger it. + * Key binding configuration - maps directions to key patterns. + * Each direction can have multiple keys that trigger it. */ export interface KeyBindings { - /** Navigate to next item in sidebar (default: ['j', 'ArrowDown']) */ - nextItem?: string[]; - /** Navigate to previous item in sidebar (default: ['k', 'ArrowUp']) */ - prevItem?: string[]; + /** Move down (default: ['j', 'ArrowDown']) */ + down?: string[]; + /** Move up (default: ['k', 'ArrowUp']) */ + up?: string[]; + /** Move left (default: ['h', 'ArrowLeft']) */ + left?: string[]; + /** Move right (default: ['l', 'ArrowRight']) */ + right?: string[]; /** Scroll content down (default: ['Ctrl+d']) */ scrollDown?: string[]; /** Scroll content up (default: ['Ctrl+u']) */ scrollUp?: string[]; - /** Go to next page (default: ['l', 'ArrowRight']) */ - nextPage?: string[]; - /** Go to previous page (default: ['h', 'ArrowLeft']) */ - prevPage?: string[]; - /** Select/navigate to current item (default: ['Enter']) */ + /** Select/confirm (default: ['Enter']) */ select?: string[]; /** Open fuzzy finder (default: ['t']) */ openFinder?: string[]; @@ -43,12 +43,12 @@ export interface ParsedKey { */ export interface KeyboardHandlerConfig { bindings?: KeyBindings; - onNextItem?: () => void; - onPrevItem?: () => void; + onDown?: () => void; + onUp?: () => void; + onLeft?: () => void; + onRight?: () => void; onScrollDown?: () => void; onScrollUp?: () => void; - onNextPage?: () => void; - onPrevPage?: () => void; onSelect?: () => void; onOpenFinder?: () => void; onEscape?: () => void; @@ -66,76 +66,32 @@ export interface KeyboardHandler { destroy: () => void; } -/** - * DOM navigator configuration - */ -export interface DOMNavigatorConfig { - /** Function to get navigable items */ - getItems: () => HTMLElement[]; - /** Class to add to highlighted item (default: 'teleport-highlight') */ - highlightClass?: string; - /** Scroll behavior options */ - scrollBehavior?: ScrollIntoViewOptions; - /** Callback when an item is selected */ - onSelect?: (item: HTMLElement, index: number) => void; - /** Callback when highlight changes */ - onHighlightChange?: (item: HTMLElement | null, index: number) => void; -} - -/** - * DOM navigator return type - */ -export interface DOMNavigator { - /** Currently highlighted index (-1 if none) */ - readonly currentIndex: number; - /** Currently highlighted element */ - readonly currentItem: HTMLElement | null; - /** Total item count */ - readonly count: number; - /** Move to next item */ - next(): void; - /** Move to previous item */ - prev(): void; - /** Go to specific index */ - goTo(index: number): void; - /** Clear highlight */ - clear(): void; - /** Refresh items list */ - refresh(): void; -} - /** * Full teleport configuration */ export interface TeleportConfig { - /** Selector for navigable items in sidebar */ - itemSelector: string; - /** Container for scrolling content (default: document.documentElement) */ - contentContainer?: HTMLElement | string; - /** Sidebar container for scrolling nav (default: first .sidebar) */ - sidebarContainer?: HTMLElement | string; - /** Class to add to highlighted item */ - highlightClass?: string; /** Custom key bindings */ bindings?: KeyBindings; - /** Callback when item is selected */ - onSelect?: (item: HTMLElement) => void; - /** Callback for next page navigation */ - onNextPage?: () => void; - /** Callback for previous page navigation */ - onPrevPage?: () => void; - /** Callback to open fuzzy finder */ + /** Directional callbacks */ + onDown?: () => void; + onUp?: () => void; + onLeft?: () => void; + onRight?: () => void; + /** Scroll callbacks */ + onScrollDown?: () => void; + onScrollUp?: () => void; + /** Action callbacks */ + onSelect?: () => void; onOpenFinder?: () => void; - /** Scroll amount for Ctrl+d/u (default: half viewport) */ - scrollAmount?: number; + onEscape?: () => void; + /** Ignore keystrokes when typing in input/textarea (default: true) */ + ignoreWhenTyping?: boolean; } /** * Teleport instance */ export interface Teleport { - /** DOM navigator for sidebar items */ - readonly navigator: DOMNavigator; /** Clean up all event listeners */ destroy(): void; } From 664c841c2c8aee339b5b6ae687ff173bf3387ca0 Mon Sep 17 00:00:00 2001 From: Josh Ribakoff Date: Mon, 15 Dec 2025 21:27:10 -0800 Subject: [PATCH 13/35] Rename npm scope from @sailkit to @bearing-dev MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update package names and all import references across compass and lantern packages. πŸ€– Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- packages/compass/README.md | 2 +- packages/compass/package.json | 2 +- packages/lantern/README.md | 12 ++++++------ packages/lantern/package.json | 2 +- packages/lantern/src/index.ts | 2 +- 5 files changed, 10 insertions(+), 10 deletions(-) diff --git a/packages/compass/README.md b/packages/compass/README.md index 67fb16a..62a85e6 100644 --- a/packages/compass/README.md +++ b/packages/compass/README.md @@ -1,4 +1,4 @@ -# @sailkit/compass +# @bearing-dev/compass Headless navigation cursor for ordered and nested content. diff --git a/packages/compass/package.json b/packages/compass/package.json index e7fe2aa..aa3f38c 100644 --- a/packages/compass/package.json +++ b/packages/compass/package.json @@ -1,5 +1,5 @@ { - "name": "@sailkit/compass", + "name": "@bearing-dev/compass", "version": "0.1.0", "description": "Headless navigation state for lists and trees", "type": "module", diff --git a/packages/lantern/README.md b/packages/lantern/README.md index 233e32f..f386609 100644 --- a/packages/lantern/README.md +++ b/packages/lantern/README.md @@ -1,4 +1,4 @@ -# @sailkit/lantern +# @bearing-dev/lantern Theme toggle with flash-free hydration. @@ -6,8 +6,8 @@ Theme toggle with flash-free hydration. ```astro --- -import { initScript } from '@sailkit/lantern'; -import ThemeToggle from '@sailkit/lantern/ThemeToggle.astro'; +import { initScript } from '@bearing-dev/lantern'; +import ThemeToggle from '@bearing-dev/lantern/ThemeToggle.astro'; --- @@ -22,7 +22,7 @@ import ThemeToggle from '@sailkit/lantern/ThemeToggle.astro'; ## API ```typescript -import { initTheme, toggleTheme, getTheme } from '@sailkit/lantern'; +import { initTheme, toggleTheme, getTheme } from '@bearing-dev/lantern'; initTheme(); // restore from localStorage toggleTheme(); // dark <-> light @@ -66,8 +66,8 @@ Include `initScript` before paint to prevent flash of wrong theme: ## Optional Example CSS ```typescript -import '@sailkit/lantern/theme-dark.css'; -import '@sailkit/lantern/theme-light.css'; +import '@bearing-dev/lantern/theme-dark.css'; +import '@bearing-dev/lantern/theme-light.css'; ``` These are minimal examples. Override with your own theme CSS. diff --git a/packages/lantern/package.json b/packages/lantern/package.json index 898bc66..cbfe896 100644 --- a/packages/lantern/package.json +++ b/packages/lantern/package.json @@ -1,5 +1,5 @@ { - "name": "@sailkit/lantern", + "name": "@bearing-dev/lantern", "version": "0.1.0", "description": "Theme toggle with flash-free hydration", "type": "module", diff --git a/packages/lantern/src/index.ts b/packages/lantern/src/index.ts index 6824cb9..7a2d534 100644 --- a/packages/lantern/src/index.ts +++ b/packages/lantern/src/index.ts @@ -3,7 +3,7 @@ * * @example * ```typescript - * import { initTheme, toggleTheme, onThemeChange } from '@sailkit/lantern'; + * import { initTheme, toggleTheme, onThemeChange } from '@bearing-dev/lantern'; * * // Initialize on page load (call early to prevent flash) * initTheme(); From 61ee71383bb86846eba4d602f26601d5748d4bd8 Mon Sep 17 00:00:00 2001 From: Josh Ribakoff Date: Mon, 15 Dec 2025 22:09:54 -0800 Subject: [PATCH 14/35] feat(teleport): add createTeleport() for drop-in keyboard navigation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add createTeleport() that scans DOM, uses Compass internally, handles highlighting - Simplify Teleport.astro to thin wrapper around createTeleport() - Update README with design philosophy: drop-in, framework-agnostic - Add toggleSidebar binding (t key) πŸ€– Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- package-lock.json | 57 +++---- packages/teleport/README.md | 23 ++- packages/teleport/Teleport.astro | 36 ++--- packages/teleport/package.json | 7 +- packages/teleport/src/index.ts | 3 + packages/teleport/src/keys.ts | 10 +- packages/teleport/src/navigator.ts | 244 +++++++++++++++++++++++++++++ packages/teleport/src/teleport.ts | 2 + packages/teleport/src/types.ts | 6 +- 9 files changed, 323 insertions(+), 65 deletions(-) create mode 100644 packages/teleport/src/navigator.ts diff --git a/package-lock.json b/package-lock.json index 41bfeeb..835f7c8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { - "name": "sailkit", - "version": "0.1.0", + "name": "bearing-dev", + "version": "0.0.1", "lockfileVersion": 3, "requires": true, "packages": { "": { - "name": "sailkit", - "version": "0.1.0", + "name": "bearing-dev", + "version": "0.0.1", "workspaces": [ "packages/*" ], @@ -40,6 +40,18 @@ "lru-cache": "^10.4.3" } }, + "node_modules/@bearing-dev/compass": { + "resolved": "packages/compass", + "link": true + }, + "node_modules/@bearing-dev/lantern": { + "resolved": "packages/lantern", + "link": true + }, + "node_modules/@bearing-dev/teleport": { + "resolved": "packages/teleport", + "link": true + }, "node_modules/@csstools/color-helpers": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/@csstools/color-helpers/-/color-helpers-5.1.0.tgz", @@ -877,18 +889,6 @@ "win32" ] }, - "node_modules/@sailkit/compass": { - "resolved": "packages/compass", - "link": true - }, - "node_modules/@sailkit/lantern": { - "resolved": "packages/lantern", - "link": true - }, - "node_modules/@sailkit/teleport": { - "resolved": "packages/teleport", - "link": true - }, "node_modules/@types/estree": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", @@ -3323,8 +3323,8 @@ } }, "packages/compass": { - "name": "@sailkit/compass", - "version": "0.1.0", + "name": "@bearing-dev/compass", + "version": "0.0.1", "license": "MIT", "devDependencies": { "typescript": "^5.3.0", @@ -3332,8 +3332,8 @@ } }, "packages/lantern": { - "name": "@sailkit/lantern", - "version": "0.1.0", + "name": "@bearing-dev/lantern", + "version": "0.0.1", "license": "MIT", "devDependencies": { "jsdom": "^25.0.0", @@ -3370,24 +3370,19 @@ } }, "packages/teleport": { - "name": "@sailkit/teleport", - "version": "0.1.0", + "name": "@bearing-dev/teleport", + "version": "0.0.1", "license": "MIT", + "dependencies": { + "@bearing-dev/compass": ">=0.0.1" + }, "devDependencies": { + "@bearing-dev/compass": "file:../compass", "@playwright/test": "^1.57.0", - "@sailkit/compass": "file:../compass", "jsdom": "^25.0.0", "serve": "^14.2.5", "typescript": "^5.3.0", "vitest": "^2.0.0" - }, - "peerDependencies": { - "@sailkit/compass": ">=0.1.0" - }, - "peerDependenciesMeta": { - "@sailkit/compass": { - "optional": true - } } } } diff --git a/packages/teleport/README.md b/packages/teleport/README.md index a3459ae..83633d3 100644 --- a/packages/teleport/README.md +++ b/packages/teleport/README.md @@ -1,6 +1,27 @@ # @bearing-dev/teleport -Vim-style keyboard navigation bindings with DOM integration. +Vim-style keyboard navigation for any website. + +## Design Philosophy + +**Drop-in for any website.** Teleport is a vanilla JavaScript library that works with any DOM. Just provide CSS selectors and it handles everything else. + +**Internally uses Compass.** Teleport scans the DOM and builds a navigation tree using [@bearing-dev/compass](../compass). You get hierarchical navigation for free. + +**Framework-agnostic.** The core is pure JavaScript. The Astro component is a convenience wrapper. + +```javascript +// Works anywhere - no framework required +import { createTeleport } from '@bearing-dev/teleport'; + +const teleport = createTeleport({ + itemSelector: '.nav-item', + highlightClass: 'highlight', +}); + +// Cleanup when done +teleport.destroy(); +``` ## Quick Start (Astro) diff --git a/packages/teleport/Teleport.astro b/packages/teleport/Teleport.astro index 3cf449f..0b3f1c1 100644 --- a/packages/teleport/Teleport.astro +++ b/packages/teleport/Teleport.astro @@ -2,37 +2,29 @@ /** * Teleport - Vim-style keyboard navigation for Astro sites. * - * Provides j/k navigation in sidebar, Ctrl+d/u for content scroll, - * h/l for prev/next page, and t for fuzzy finder. + * A thin Astro wrapper around createTeleport(). For vanilla JS usage, + * import createTeleport directly from '@bearing-dev/teleport'. */ interface Props { /** CSS selector for navigable items (default: '.nav-item') */ itemSelector?: string; - /** CSS selector for content container (default: 'main') */ - contentSelector?: string; - /** CSS selector for sidebar container (default: '.sidebar') */ - sidebarSelector?: string; /** Class to add to highlighted item (default: 'teleport-highlight') */ highlightClass?: string; - /** Enable fuzzy finder (emits teleport:open-finder event) */ - enableFinder?: boolean; + /** Enable wrapping at ends (default: true) */ + wrap?: boolean; } const { itemSelector = '.nav-item', - contentSelector = 'main', - sidebarSelector = '.sidebar', highlightClass = 'teleport-highlight', - enableFinder = false, + wrap = true, } = Astro.props; --- @@ -45,26 +37,20 @@ const { diff --git a/packages/teleport/tests/e2e/scroll-fixture.html b/packages/teleport/tests/e2e/scroll-fixture.html index 5558db8..45a346c 100644 --- a/packages/teleport/tests/e2e/scroll-fixture.html +++ b/packages/teleport/tests/e2e/scroll-fixture.html @@ -58,13 +58,24 @@
Block 8
diff --git a/packages/teleport/tests/e2e/scroll.spec.ts b/packages/teleport/tests/e2e/scroll.spec.ts index 725679e..80b0530 100644 --- a/packages/teleport/tests/e2e/scroll.spec.ts +++ b/packages/teleport/tests/e2e/scroll.spec.ts @@ -75,7 +75,7 @@ const TEST_HTML = ` test.describe('Scroll bindings', () => { test.beforeEach(async ({ page }) => { // Navigate to the test fixture server and set content - await page.goto('/tests/e2e/scroll-fixture.html'); + await page.goto('/packages/teleport/tests/e2e/scroll-fixture.html'); }); test('Ctrl+d scrolls main content down', async ({ page }) => { @@ -194,7 +194,7 @@ test.describe('Scroll bindings', () => { test.describe('Scroll fallback behavior', () => { test('Fallback to window when no contentContainer specified', async ({ page }) => { // Use a different fixture that doesn't specify contentContainer - await page.goto('/tests/e2e/scroll-fallback-fixture.html'); + await page.goto('/packages/teleport/tests/e2e/scroll-fallback-fixture.html'); // Get initial window scroll const initialWindowScroll = await page.evaluate(() => window.scrollY); diff --git a/packages/teleport/tests/e2e/sidebar-visibility-fixture.html b/packages/teleport/tests/e2e/sidebar-visibility-fixture.html new file mode 100644 index 0000000..49d9a60 --- /dev/null +++ b/packages/teleport/tests/e2e/sidebar-visibility-fixture.html @@ -0,0 +1,189 @@ + + + + + + Sidebar Visibility Test Fixture + + + + + +
+

Sidebar Visibility Test

+

Use the buttons to test different hiding methods.

+
+ + + + + + + + +
+
+ +
Sidebar: visible
+ + + + diff --git a/packages/teleport/tests/e2e/sidebar-visibility.spec.ts b/packages/teleport/tests/e2e/sidebar-visibility.spec.ts new file mode 100644 index 0000000..2895bde --- /dev/null +++ b/packages/teleport/tests/e2e/sidebar-visibility.spec.ts @@ -0,0 +1,348 @@ +import { test, expect } from '@playwright/test'; + +/** + * Sidebar Visibility Tests for Teleport + * + * Tests the whenHidden behavior: + * - 'ignore': j/k/Enter do nothing when sidebar is hidden + * - 'show-sidebar': j/k/Enter trigger toggle-sidebar event, then navigate + */ + +test.describe('Sidebar visibility detection', () => { + test('detects sidebar hidden via display:none', async ({ page }) => { + await page.goto('/packages/teleport/tests/e2e/sidebar-visibility-fixture.html?whenHidden=ignore'); + + // Verify sidebar is initially visible and j works + await page.keyboard.press('j'); + await expect(page.locator('.nav-item').first()).toHaveClass(/teleport-highlight/); + + // Hide sidebar via display:none + await page.evaluate(() => window.hideSidebar('display')); + + // Clear highlight first + await page.keyboard.press('Escape'); + + // Press j - should be ignored + const initialIndex = await page.evaluate(() => window.getHighlightedIndex()); + await page.keyboard.press('j'); + const afterIndex = await page.evaluate(() => window.getHighlightedIndex()); + + expect(afterIndex).toBe(initialIndex); // No change + }); + + test('detects sidebar hidden via visibility:hidden', async ({ page }) => { + await page.goto('/packages/teleport/tests/e2e/sidebar-visibility-fixture.html?whenHidden=ignore'); + + await page.evaluate(() => window.hideSidebar('visibility')); + await page.keyboard.press('Escape'); // Clear any highlight + + const initialIndex = await page.evaluate(() => window.getHighlightedIndex()); + await page.keyboard.press('j'); + const afterIndex = await page.evaluate(() => window.getHighlightedIndex()); + + expect(afterIndex).toBe(initialIndex); + }); + + test('detects sidebar hidden via opacity:0', async ({ page }) => { + await page.goto('/packages/teleport/tests/e2e/sidebar-visibility-fixture.html?whenHidden=ignore'); + + await page.evaluate(() => window.hideSidebar('opacity')); + await page.keyboard.press('Escape'); + + const initialIndex = await page.evaluate(() => window.getHighlightedIndex()); + await page.keyboard.press('j'); + const afterIndex = await page.evaluate(() => window.getHighlightedIndex()); + + expect(afterIndex).toBe(initialIndex); + }); + + test('detects sidebar hidden via translateX(-100%)', async ({ page }) => { + await page.goto('/packages/teleport/tests/e2e/sidebar-visibility-fixture.html?whenHidden=ignore'); + + await page.evaluate(() => window.hideSidebar('transform')); + await page.waitForTimeout(350); // Wait for CSS transition + await page.keyboard.press('Escape'); + + const initialIndex = await page.evaluate(() => window.getHighlightedIndex()); + await page.keyboard.press('j'); + const afterIndex = await page.evaluate(() => window.getHighlightedIndex()); + + expect(afterIndex).toBe(initialIndex); + }); + + test('detects sidebar hidden via .hidden class', async ({ page }) => { + await page.goto('/packages/teleport/tests/e2e/sidebar-visibility-fixture.html?whenHidden=ignore'); + + await page.evaluate(() => window.hideSidebar('hidden-class')); + await page.keyboard.press('Escape'); + + const initialIndex = await page.evaluate(() => window.getHighlightedIndex()); + await page.keyboard.press('j'); + const afterIndex = await page.evaluate(() => window.getHighlightedIndex()); + + expect(afterIndex).toBe(initialIndex); + }); + + test('detects sidebar hidden via .collapsed class', async ({ page }) => { + await page.goto('/packages/teleport/tests/e2e/sidebar-visibility-fixture.html?whenHidden=ignore'); + + await page.evaluate(() => window.hideSidebar('collapsed-class')); + await page.keyboard.press('Escape'); + + const initialIndex = await page.evaluate(() => window.getHighlightedIndex()); + await page.keyboard.press('j'); + const afterIndex = await page.evaluate(() => window.getHighlightedIndex()); + + expect(afterIndex).toBe(initialIndex); + }); + + test('detects sidebar hidden via [hidden] attribute', async ({ page }) => { + await page.goto('/packages/teleport/tests/e2e/sidebar-visibility-fixture.html?whenHidden=ignore'); + + await page.evaluate(() => window.hideSidebar('hidden-attr')); + await page.keyboard.press('Escape'); + + const initialIndex = await page.evaluate(() => window.getHighlightedIndex()); + await page.keyboard.press('j'); + const afterIndex = await page.evaluate(() => window.getHighlightedIndex()); + + expect(afterIndex).toBe(initialIndex); + }); +}); + +test.describe('whenHidden: ignore', () => { + test.beforeEach(async ({ page }) => { + await page.goto('/packages/teleport/tests/e2e/sidebar-visibility-fixture.html?whenHidden=ignore'); + }); + + test('j/k work when sidebar is visible', async ({ page }) => { + // j should highlight first item + await page.keyboard.press('j'); + expect(await page.evaluate(() => window.getHighlightedIndex())).toBe(0); + + // j again should move to second item + await page.keyboard.press('j'); + expect(await page.evaluate(() => window.getHighlightedIndex())).toBe(1); + + // k should move back + await page.keyboard.press('k'); + expect(await page.evaluate(() => window.getHighlightedIndex())).toBe(0); + }); + + test('j does nothing when sidebar is hidden', async ({ page }) => { + await page.evaluate(() => window.hideSidebar('transform')); + await page.waitForTimeout(350); // Wait for CSS transition + await page.keyboard.press('Escape'); + + const before = await page.evaluate(() => window.getHighlightedIndex()); + await page.keyboard.press('j'); + await page.keyboard.press('j'); + await page.keyboard.press('j'); + const after = await page.evaluate(() => window.getHighlightedIndex()); + + expect(after).toBe(before); + }); + + test('k does nothing when sidebar is hidden', async ({ page }) => { + // First navigate to an item while visible + await page.keyboard.press('j'); + await page.keyboard.press('j'); + expect(await page.evaluate(() => window.getHighlightedIndex())).toBe(1); + + // Hide sidebar + await page.evaluate(() => window.hideSidebar('transform')); + await page.waitForTimeout(350); // Wait for CSS transition + + // k should do nothing + const before = await page.evaluate(() => window.getHighlightedIndex()); + await page.keyboard.press('k'); + const after = await page.evaluate(() => window.getHighlightedIndex()); + + expect(after).toBe(before); + }); + + test('Enter does nothing when sidebar is hidden', async ({ page }) => { + // Navigate to first item + await page.keyboard.press('j'); + + // Hide sidebar + await page.evaluate(() => window.hideSidebar('transform')); + await page.waitForTimeout(350); // Wait for CSS transition + + // Get current URL + const beforeUrl = page.url(); + + // Enter should do nothing (not navigate) + await page.keyboard.press('Enter'); + await page.waitForTimeout(100); + + const afterUrl = page.url(); + expect(afterUrl).toBe(beforeUrl); + }); + + test('does NOT emit toggle-sidebar event', async ({ page }) => { + await page.evaluate(() => window.hideSidebar('transform')); + await page.waitForTimeout(350); // Wait for CSS transition + + const beforeCount = await page.evaluate(() => window.toggleCount); + await page.keyboard.press('j'); + const afterCount = await page.evaluate(() => window.toggleCount); + + expect(afterCount).toBe(beforeCount); + }); + + test('navigation resumes when sidebar becomes visible again', async ({ page }) => { + // Hide sidebar + await page.evaluate(() => window.hideSidebar('transform')); + await page.waitForTimeout(350); // Wait for CSS transition + + // j should do nothing + await page.keyboard.press('j'); + expect(await page.evaluate(() => window.getHighlightedIndex())).toBe(-1); + + // Show sidebar + await page.evaluate(() => window.showSidebar()); + await page.waitForTimeout(350); // Wait for CSS transition + + // Now j should work + await page.keyboard.press('j'); + expect(await page.evaluate(() => window.getHighlightedIndex())).toBe(0); + }); +}); + +test.describe('whenHidden: show-sidebar', () => { + test.beforeEach(async ({ page }) => { + await page.goto('/packages/teleport/tests/e2e/sidebar-visibility-fixture.html?whenHidden=show-sidebar'); + }); + + test('j/k work normally when sidebar is visible', async ({ page }) => { + await page.keyboard.press('j'); + expect(await page.evaluate(() => window.getHighlightedIndex())).toBe(0); + + await page.keyboard.press('j'); + expect(await page.evaluate(() => window.getHighlightedIndex())).toBe(1); + }); + + test('j emits toggle-sidebar event when sidebar is hidden', async ({ page }) => { + await page.evaluate(() => window.hideSidebar('transform')); + await page.waitForTimeout(350); // Wait for CSS transition + + const beforeCount = await page.evaluate(() => window.toggleCount); + await page.keyboard.press('j'); + const afterCount = await page.evaluate(() => window.toggleCount); + + expect(afterCount).toBe(beforeCount + 1); + }); + + test('k emits toggle-sidebar event when sidebar is hidden', async ({ page }) => { + await page.evaluate(() => window.hideSidebar('transform')); + await page.waitForTimeout(350); // Wait for CSS transition + + const beforeCount = await page.evaluate(() => window.toggleCount); + await page.keyboard.press('k'); + const afterCount = await page.evaluate(() => window.toggleCount); + + expect(afterCount).toBe(beforeCount + 1); + }); + + test('Enter emits toggle-sidebar event when sidebar is hidden', async ({ page }) => { + // First highlight an item while visible + await page.keyboard.press('j'); + + // Hide sidebar + await page.evaluate(() => window.hideSidebar('transform')); + await page.waitForTimeout(350); // Wait for CSS transition + + const beforeCount = await page.evaluate(() => window.toggleCount); + await page.keyboard.press('Enter'); + const afterCount = await page.evaluate(() => window.toggleCount); + + expect(afterCount).toBe(beforeCount + 1); + }); + + test('navigates after toggle event (simulated sidebar open)', async ({ page }) => { + await page.evaluate(() => window.hideSidebar('transform')); + await page.waitForTimeout(350); // Wait for CSS transition + + // Set up listener to show sidebar when toggle event fires + await page.evaluate(() => { + document.addEventListener('teleport:toggle-sidebar', () => { + window.showSidebar(); + }, { once: true }); + }); + + // Press j - should trigger toggle, sidebar opens, then navigate + await page.keyboard.press('j'); + await page.waitForTimeout(100); // Wait for the delayed navigation + + // After toggle + delay, should have navigated + expect(await page.evaluate(() => window.getHighlightedIndex())).toBe(0); + }); +}); + +test.describe('Other bindings when sidebar hidden', () => { + test('Ctrl+d still scrolls when sidebar hidden (ignore mode)', async ({ page }) => { + await page.goto('/packages/teleport/tests/e2e/sidebar-visibility-fixture.html?whenHidden=ignore'); + await page.evaluate(() => window.hideSidebar('transform')); + await page.waitForTimeout(350); // Wait for CSS transition + + // Make body scrollable by removing height constraint and adding content + await page.evaluate(() => { + document.body.style.height = 'auto'; + document.body.style.minHeight = '300vh'; + }); + + const initialScroll = await page.evaluate(() => window.scrollY); + expect(initialScroll).toBe(0); + + await page.keyboard.down('Control'); + await page.keyboard.press('d'); + await page.keyboard.up('Control'); + + await page.waitForTimeout(500); + + const afterScroll = await page.evaluate(() => window.scrollY); + expect(afterScroll).toBeGreaterThan(initialScroll); + }); + + test('t (toggle sidebar) still works when sidebar hidden', async ({ page }) => { + await page.goto('/packages/teleport/tests/e2e/sidebar-visibility-fixture.html?whenHidden=ignore'); + await page.evaluate(() => window.hideSidebar('transform')); + await page.waitForTimeout(350); // Wait for CSS transition + + const beforeCount = await page.evaluate(() => window.toggleCount); + await page.keyboard.press('t'); + const afterCount = await page.evaluate(() => window.toggleCount); + + expect(afterCount).toBe(beforeCount + 1); + }); + + test('Escape clears highlight regardless of sidebar visibility', async ({ page }) => { + await page.goto('/packages/teleport/tests/e2e/sidebar-visibility-fixture.html?whenHidden=ignore'); + + // Highlight an item + await page.keyboard.press('j'); + expect(await page.evaluate(() => window.getHighlightedIndex())).toBe(0); + + // Hide sidebar + await page.evaluate(() => window.hideSidebar('transform')); + await page.waitForTimeout(350); // Wait for CSS transition + + // Escape should still clear + await page.keyboard.press('Escape'); + expect(await page.evaluate(() => window.getHighlightedIndex())).toBe(-1); + }); +}); + +test.describe('No sidebarSelector (backwards compatibility)', () => { + test('j/k always work when no sidebarSelector provided', async ({ page }) => { + // Use the basic fixture which doesn't have sidebarSelector + await page.goto('/packages/teleport/tests/fixtures/index.html'); + + await page.keyboard.press('j'); + await expect(page.locator('.nav-item').first()).toHaveClass(/teleport-highlight/); + + await page.keyboard.press('j'); + await expect(page.locator('.nav-item').nth(1)).toHaveClass(/teleport-highlight/); + }); +}); diff --git a/packages/teleport/tests/e2e/teleport.spec.ts b/packages/teleport/tests/e2e/teleport.spec.ts index af33c89..49cb70d 100644 --- a/packages/teleport/tests/e2e/teleport.spec.ts +++ b/packages/teleport/tests/e2e/teleport.spec.ts @@ -1,7 +1,7 @@ import { test, expect } from '@playwright/test'; test('page loads successfully', async ({ page }) => { - await page.goto('/tests/fixtures/index.html'); + await page.goto('/packages/teleport/tests/fixtures/index.html'); // Verify the page structure is present await expect(page.locator('.sidebar')).toBeVisible(); diff --git a/packages/teleport/tests/fixtures/index.html b/packages/teleport/tests/fixtures/index.html index 8198e4b..5b62794 100644 --- a/packages/teleport/tests/fixtures/index.html +++ b/packages/teleport/tests/fixtures/index.html @@ -4,6 +4,13 @@ Teleport E2E Test Fixture + diff --git a/packages/teleport/src/index.ts b/packages/teleport/src/index.ts index 60db9d6..a728273 100644 --- a/packages/teleport/src/index.ts +++ b/packages/teleport/src/index.ts @@ -58,4 +58,5 @@ export { type CreateTeleportConfig, type TeleportInstance, type WhenHiddenBehavior, + type BreakpointMode, } from './navigator.js'; diff --git a/packages/teleport/src/navigator.ts b/packages/teleport/src/navigator.ts index d0a7e46..ae90f6d 100644 --- a/packages/teleport/src/navigator.ts +++ b/packages/teleport/src/navigator.ts @@ -15,6 +15,13 @@ import { scrollElement, getViewportHeight } from './dom.js'; */ export type WhenHiddenBehavior = 'ignore' | 'show-sidebar'; +/** + * Breakpoint mode affecting keyboard behavior + * - 'mobile': arrows and j/k both navigate sidebar when visible (default) + * - 'desktop': arrows always pass through for native scroll, j/k navigate sidebar + */ +export type BreakpointMode = 'mobile' | 'desktop'; + /** * Configuration for createTeleport */ @@ -35,6 +42,8 @@ export interface CreateTeleportConfig { ignoreWhenTyping?: boolean; /** What to do when navigation keys pressed while sidebar hidden (default: 'ignore' - arrows scroll natively, j/k/Enter do nothing) */ whenHidden?: WhenHiddenBehavior; + /** Breakpoint mode: 'mobile' = arrows+j/k both navigate, 'desktop' = arrows always pass through for scroll (default: 'mobile') */ + breakpoint?: BreakpointMode; /** Callback when item is selected (Enter pressed) */ onSelect?: (element: HTMLElement, slug: string) => void; /** Callback when sidebar toggle is triggered */ @@ -92,6 +101,7 @@ export function createTeleport(config: CreateTeleportConfig): TeleportInstance { bindings = {}, ignoreWhenTyping = true, whenHidden = 'ignore', + breakpoint = 'mobile', onSelect, onToggleSidebar, onOpenFinder, @@ -146,10 +156,15 @@ export function createTeleport(config: CreateTeleportConfig): TeleportInstance { } /** - * Handle navigation action with sidebar visibility awareness. + * Handle navigation action with sidebar visibility and breakpoint awareness. * Returns false if the event should pass through (not preventDefault). */ function handleNavigationAction(event: KeyboardEvent, action: () => void): boolean { + // Desktop mode: arrows always pass through for native scroll + if (breakpoint === 'desktop' && isArrowKey(event)) { + return false; // don't preventDefault, let native scroll happen + } + if (isSidebarVisible()) { action(); return true; // handled, preventDefault @@ -188,6 +203,9 @@ export function createTeleport(config: CreateTeleportConfig): TeleportInstance { }); } + // Track if this is the initial load (don't animate scroll on page load) + let isInitialLoad = true; + /** * Update DOM to reflect current position */ @@ -199,7 +217,8 @@ export function createTeleport(config: CreateTeleportConfig): TeleportInstance { if (index >= 0 && index < elements.length) { const el = elements[index]; el.classList.add(highlightClass); - el.scrollIntoView({ behavior: 'smooth', block: 'nearest' }); + // Use instant scroll on initial load, smooth scroll for user navigation + el.scrollIntoView({ behavior: isInitialLoad ? 'instant' : 'smooth', block: 'nearest' }); } } @@ -219,6 +238,7 @@ export function createTeleport(config: CreateTeleportConfig): TeleportInstance { // Initial scan scanDOM(); syncWithUrl(); + isInitialLoad = false; // Set up keyboard handler const mergedBindings: KeyBindings = { @@ -285,14 +305,12 @@ export function createTeleport(config: CreateTeleportConfig): TeleportInstance { onOpenFinder: onOpenFinder ? () => onOpenFinder() : () => document.dispatchEvent(new CustomEvent('teleport:open-finder')), - onGoToTop: (event) => handleNavigationAction(event, () => { - navigator?.goTo(0); - }), - onGoToBottom: (event) => handleNavigationAction(event, () => { - if (navigator && elements.length > 0) { - navigator.goTo(elements.length - 1); - } - }), + onGoToTop: () => { + window.scrollTo({ top: 0, behavior: 'smooth' }); + }, + onGoToBottom: () => { + window.scrollTo({ top: document.body.scrollHeight, behavior: 'smooth' }); + }, }); // Attach global keydown listener diff --git a/packages/teleport/tests/e2e/sidebar-visibility-fixture.html b/packages/teleport/tests/e2e/sidebar-visibility-fixture.html index 49d9a60..f8a9f9b 100644 --- a/packages/teleport/tests/e2e/sidebar-visibility-fixture.html +++ b/packages/teleport/tests/e2e/sidebar-visibility-fixture.html @@ -116,17 +116,20 @@

Sidebar Visibility Test

// Get config from URL params (for test flexibility) const params = new URLSearchParams(window.location.search); const whenHidden = params.get('whenHidden') || 'ignore'; + const breakpoint = params.get('breakpoint') || 'mobile'; const teleport = createTeleport({ itemSelector: '.nav-item', sidebarSelector: '.sidebar', highlightClass: 'teleport-highlight', whenHidden: whenHidden, + breakpoint: breakpoint, }); // Expose for testing window.teleport = teleport; window.whenHidden = whenHidden; + window.breakpoint = breakpoint; // Track toggle events window.toggleCount = 0; diff --git a/packages/teleport/tests/e2e/sidebar-visibility.spec.ts b/packages/teleport/tests/e2e/sidebar-visibility.spec.ts index 235f482..26eb91e 100644 --- a/packages/teleport/tests/e2e/sidebar-visibility.spec.ts +++ b/packages/teleport/tests/e2e/sidebar-visibility.spec.ts @@ -327,3 +327,110 @@ test.describe('No sidebarSelector (backwards compatibility)', () => { await expect(page.locator('.nav-item').nth(2)).toHaveClass(/teleport-highlight/); }); }); + +test.describe('Desktop breakpoint mode', () => { + test.beforeEach(async ({ page }) => { + await page.goto('/packages/teleport/tests/e2e/sidebar-visibility-fixture.html?breakpoint=desktop'); + }); + + test('j/k navigate sidebar when visible', async ({ page }) => { + // Position 0 β†’ 1, highlight at 1 + await page.keyboard.press('j'); + expect(await page.evaluate(() => window.getHighlightedIndex())).toBe(1); + + // Position 1 β†’ 2, highlight at 2 + await page.keyboard.press('j'); + expect(await page.evaluate(() => window.getHighlightedIndex())).toBe(2); + + // k moves back + await page.keyboard.press('k'); + expect(await page.evaluate(() => window.getHighlightedIndex())).toBe(1); + }); + + test('ArrowDown does NOT navigate sidebar (passes through)', async ({ page }) => { + // First navigate with j to set initial position + await page.keyboard.press('j'); + expect(await page.evaluate(() => window.getHighlightedIndex())).toBe(1); + + // ArrowDown should NOT change the highlight (passes through for native scroll) + const before = await page.evaluate(() => window.getHighlightedIndex()); + await page.keyboard.press('ArrowDown'); + const after = await page.evaluate(() => window.getHighlightedIndex()); + + expect(after).toBe(before); + }); + + test('ArrowUp does NOT navigate sidebar (passes through)', async ({ page }) => { + // Navigate to position 2 with j + await page.keyboard.press('j'); + await page.keyboard.press('j'); + expect(await page.evaluate(() => window.getHighlightedIndex())).toBe(2); + + // ArrowUp should NOT change the highlight (passes through for native scroll) + const before = await page.evaluate(() => window.getHighlightedIndex()); + await page.keyboard.press('ArrowUp'); + const after = await page.evaluate(() => window.getHighlightedIndex()); + + expect(after).toBe(before); + }); + + test('arrows pass through even when sidebar is visible', async ({ page }) => { + // Make main area scrollable + await page.evaluate(() => { + const main = document.querySelector('main'); + if (main) { + main.style.height = '200px'; + main.style.overflow = 'auto'; + main.innerHTML += '
Scrollable content
'; + } + }); + + // Navigate to an item first + await page.keyboard.press('j'); + const highlightBefore = await page.evaluate(() => window.getHighlightedIndex()); + + // Press ArrowDown - should NOT change highlight + await page.keyboard.press('ArrowDown'); + const highlightAfter = await page.evaluate(() => window.getHighlightedIndex()); + + expect(highlightAfter).toBe(highlightBefore); + }); +}); + +test.describe('Mobile breakpoint mode (default)', () => { + test.beforeEach(async ({ page }) => { + await page.goto('/packages/teleport/tests/e2e/sidebar-visibility-fixture.html?breakpoint=mobile'); + }); + + test('ArrowDown navigates sidebar when visible', async ({ page }) => { + // ArrowDown should navigate sidebar + await page.keyboard.press('ArrowDown'); + expect(await page.evaluate(() => window.getHighlightedIndex())).toBe(1); + + await page.keyboard.press('ArrowDown'); + expect(await page.evaluate(() => window.getHighlightedIndex())).toBe(2); + }); + + test('ArrowUp navigates sidebar when visible', async ({ page }) => { + // Navigate down first + await page.keyboard.press('ArrowDown'); + await page.keyboard.press('ArrowDown'); + expect(await page.evaluate(() => window.getHighlightedIndex())).toBe(2); + + // ArrowUp should navigate back + await page.keyboard.press('ArrowUp'); + expect(await page.evaluate(() => window.getHighlightedIndex())).toBe(1); + }); + + test('arrows pass through when sidebar is hidden (whenHidden: ignore)', async ({ page }) => { + await page.evaluate(() => window.hideSidebar('transform')); + await page.waitForTimeout(350); + + // Arrows should NOT navigate when hidden + const before = await page.evaluate(() => window.getHighlightedIndex()); + await page.keyboard.press('ArrowDown'); + const after = await page.evaluate(() => window.getHighlightedIndex()); + + expect(after).toBe(before); + }); +}); diff --git a/packages/teleport/tests/e2e/teleport.spec.ts b/packages/teleport/tests/e2e/teleport.spec.ts index 653a789..6b78185 100644 --- a/packages/teleport/tests/e2e/teleport.spec.ts +++ b/packages/teleport/tests/e2e/teleport.spec.ts @@ -9,68 +9,75 @@ test('page loads successfully', async ({ page }) => { await expect(page.locator('.nav-item')).toHaveCount(5); }); -test.describe('Go to top/bottom navigation', () => { - test('gg goes to first item', async ({ page }) => { - await page.goto('/packages/teleport/tests/fixtures/index.html'); +test.describe('Go to top/bottom page scroll', () => { + test('gg scrolls to top of page', async ({ page }) => { + await page.goto('/packages/teleport/tests/fixtures/scroll-fixture.html'); - // Navigate to middle of list first - await page.keyboard.press('j'); - await page.keyboard.press('j'); - await page.keyboard.press('j'); - await expect(page.locator('.nav-item').nth(3)).toHaveClass(/teleport-highlight/); + // Scroll down first + await page.evaluate(() => window.scrollTo(0, 500)); + await page.waitForTimeout(100); + expect(await page.evaluate(() => window.scrollY)).toBeGreaterThan(0); // Press gg (two g's in quick succession) await page.keyboard.press('g'); await page.keyboard.press('g'); - // Should be at first item - await expect(page.locator('.nav-item').nth(0)).toHaveClass(/teleport-highlight/); + // Wait for smooth scroll + await page.waitForTimeout(500); + + // Should be at top + expect(await page.evaluate(() => window.scrollY)).toBe(0); }); - test('G (Shift+g) goes to last item', async ({ page }) => { - await page.goto('/packages/teleport/tests/fixtures/index.html'); + test('G (Shift+g) scrolls to bottom of page', async ({ page }) => { + await page.goto('/packages/teleport/tests/fixtures/scroll-fixture.html'); - // Start at first item - await page.keyboard.press('j'); - await expect(page.locator('.nav-item').nth(1)).toHaveClass(/teleport-highlight/); + // Start at top + expect(await page.evaluate(() => window.scrollY)).toBe(0); // Press G (Shift+g) await page.keyboard.press('Shift+g'); - // Should be at last item (5 items, index 4) - await expect(page.locator('.nav-item').nth(4)).toHaveClass(/teleport-highlight/); + // Wait for smooth scroll to complete + await page.waitForTimeout(1000); + + // Should have scrolled significantly (at least halfway down) + const scrollY = await page.evaluate(() => window.scrollY); + const maxScroll = await page.evaluate(() => document.body.scrollHeight - window.innerHeight); + expect(scrollY).toBeGreaterThan(maxScroll * 0.5); }); - test('single g does not trigger goToTop', async ({ page }) => { - await page.goto('/packages/teleport/tests/fixtures/index.html'); + test('single g does not trigger scroll', async ({ page }) => { + await page.goto('/packages/teleport/tests/fixtures/scroll-fixture.html'); - // Navigate to middle - await page.keyboard.press('j'); - await page.keyboard.press('j'); - await expect(page.locator('.nav-item').nth(2)).toHaveClass(/teleport-highlight/); + // Scroll to middle + await page.evaluate(() => window.scrollTo(0, 500)); + await page.waitForTimeout(100); + const initialScroll = await page.evaluate(() => window.scrollY); // Press single g await page.keyboard.press('g'); + await page.waitForTimeout(100); // Should still be at same position - await expect(page.locator('.nav-item').nth(2)).toHaveClass(/teleport-highlight/); + expect(await page.evaluate(() => window.scrollY)).toBe(initialScroll); }); test('g sequence times out after 500ms', async ({ page }) => { - await page.goto('/packages/teleport/tests/fixtures/index.html'); + await page.goto('/packages/teleport/tests/fixtures/scroll-fixture.html'); - // Navigate to middle - await page.keyboard.press('j'); - await page.keyboard.press('j'); - await page.keyboard.press('j'); - await expect(page.locator('.nav-item').nth(3)).toHaveClass(/teleport-highlight/); + // Scroll to middle + await page.evaluate(() => window.scrollTo(0, 500)); + await page.waitForTimeout(100); + const initialScroll = await page.evaluate(() => window.scrollY); // Press g, wait too long, then press g again await page.keyboard.press('g'); await page.waitForTimeout(600); // Wait longer than 500ms timeout await page.keyboard.press('g'); + await page.waitForTimeout(100); - // Should still be at same position (sequence timed out) - await expect(page.locator('.nav-item').nth(3)).toHaveClass(/teleport-highlight/); + // Should still be at same position (sequence timed out, second g starts new sequence) + expect(await page.evaluate(() => window.scrollY)).toBe(initialScroll); }); }); diff --git a/packages/teleport/tests/fixtures/scroll-fixture.html b/packages/teleport/tests/fixtures/scroll-fixture.html new file mode 100644 index 0000000..881b7b4 --- /dev/null +++ b/packages/teleport/tests/fixtures/scroll-fixture.html @@ -0,0 +1,69 @@ + + + + + + Teleport Scroll Test Fixture + + + + +
+
Block 1 - Scroll test content
+
Block 2
+
Block 3
+
Block 4
+
Block 5
+
+ + + From 408b9766c3936d5d88b6b241dfab9864d7fb6825 Mon Sep 17 00:00:00 2001 From: Josh Ribakoff Date: Fri, 19 Dec 2025 20:58:02 -0800 Subject: [PATCH 23/35] fix: externalize compass in teleport build --- packages/teleport/tsup.config.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/teleport/tsup.config.ts b/packages/teleport/tsup.config.ts index 4d44881..7f32c71 100644 --- a/packages/teleport/tsup.config.ts +++ b/packages/teleport/tsup.config.ts @@ -5,8 +5,8 @@ export default defineConfig({ format: ['esm'], dts: true, clean: true, - // Bundle all dependencies for browser use - noExternal: ['@bearing-dev/compass'], + // Keep compass as external dependency (resolved at runtime) + external: ['@bearing-dev/compass'], target: 'es2020', minify: false, sourcemap: true, From 737e6362b58dc084ee30b1560bc63ec6bdcffe62 Mon Sep 17 00:00:00 2001 From: Josh Ribakoff Date: Fri, 19 Dec 2025 20:59:06 -0800 Subject: [PATCH 24/35] fix: ensure compass builds before teleport --- package.json | 2 +- packages/teleport/package.json | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index 85b8c43..edb2439 100644 --- a/package.json +++ b/package.json @@ -7,7 +7,7 @@ "packages/*" ], "scripts": { - "build": "npm run build --workspaces --if-present", + "build": "npm run build -w @bearing-dev/compass -w @bearing-dev/lantern && npm run build -w @bearing-dev/teleport --if-present", "prepare": "npm run build" }, "devDependencies": { diff --git a/packages/teleport/package.json b/packages/teleport/package.json index 5b807dc..5ec4ad1 100644 --- a/packages/teleport/package.json +++ b/packages/teleport/package.json @@ -26,10 +26,9 @@ "typecheck": "tsc --noEmit" }, "dependencies": { - "@bearing-dev/compass": ">=0.0.1" + "@bearing-dev/compass": "*" }, "devDependencies": { - "@bearing-dev/compass": "file:../compass", "@playwright/test": "^1.57.0", "jsdom": "^25.0.0", "serve": "^14.2.5", From 394c9973a5ca7c8830a00bf5134b6faf7cdf2228 Mon Sep 17 00:00:00 2001 From: Josh Ribakoff Date: Fri, 19 Dec 2025 21:00:52 -0800 Subject: [PATCH 25/35] fix: disable DTS to fix git install build order --- packages/teleport/tsup.config.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/teleport/tsup.config.ts b/packages/teleport/tsup.config.ts index 7f32c71..c0897af 100644 --- a/packages/teleport/tsup.config.ts +++ b/packages/teleport/tsup.config.ts @@ -3,7 +3,7 @@ import { defineConfig } from 'tsup'; export default defineConfig({ entry: ['src/index.ts'], format: ['esm'], - dts: true, + dts: false, // Disabled due to workspace build order issues clean: true, // Keep compass as external dependency (resolved at runtime) external: ['@bearing-dev/compass'], From ea5989ffa8b6ef884e4298db4230a34304f1de1d Mon Sep 17 00:00:00 2001 From: Josh Ribakoff Date: Fri, 19 Dec 2025 21:07:58 -0800 Subject: [PATCH 26/35] feat: add Turborepo for proper build ordering --- .turbo/cache/024b5851ec647970-meta.json | 1 + .turbo/cache/024b5851ec647970.tar.zst | Bin 0 -> 7346 bytes .turbo/cache/9ff760350eb49416-meta.json | 1 + .turbo/cache/9ff760350eb49416.tar.zst | Bin 0 -> 19590 bytes .turbo/cache/a16c9688c2994863-meta.json | 1 + .turbo/cache/a16c9688c2994863.tar.zst | Bin 0 -> 2312 bytes package-lock.json | 110 ++++++++++++++++++++++- package.json | 4 +- packages/compass/.turbo/turbo-build.log | 4 + packages/lantern/.turbo/turbo-build.log | 4 + packages/lantern/package.json | 1 - packages/teleport/.turbo/turbo-build.log | 17 ++++ packages/teleport/package.json | 3 +- packages/teleport/tsup.config.ts | 5 +- turbo.json | 15 ++++ 15 files changed, 155 insertions(+), 11 deletions(-) create mode 100644 .turbo/cache/024b5851ec647970-meta.json create mode 100644 .turbo/cache/024b5851ec647970.tar.zst create mode 100644 .turbo/cache/9ff760350eb49416-meta.json create mode 100644 .turbo/cache/9ff760350eb49416.tar.zst create mode 100644 .turbo/cache/a16c9688c2994863-meta.json create mode 100644 .turbo/cache/a16c9688c2994863.tar.zst create mode 100644 packages/compass/.turbo/turbo-build.log create mode 100644 packages/lantern/.turbo/turbo-build.log create mode 100644 packages/teleport/.turbo/turbo-build.log create mode 100644 turbo.json diff --git a/.turbo/cache/024b5851ec647970-meta.json b/.turbo/cache/024b5851ec647970-meta.json new file mode 100644 index 0000000..8983607 --- /dev/null +++ b/.turbo/cache/024b5851ec647970-meta.json @@ -0,0 +1 @@ +{"hash":"024b5851ec647970","duration":1614} \ No newline at end of file diff --git a/.turbo/cache/024b5851ec647970.tar.zst b/.turbo/cache/024b5851ec647970.tar.zst new file mode 100644 index 0000000000000000000000000000000000000000..2a623f4391b5ee9fa1155cc5a380d85a8daae06c GIT binary patch literal 7346 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.0.1" + "@bearing-dev/compass": "*" }, "devDependencies": { - "@bearing-dev/compass": "file:../compass", "@playwright/test": "^1.57.0", "jsdom": "^25.0.0", "serve": "^14.2.5", diff --git a/package.json b/package.json index edb2439..4d1a6ab 100644 --- a/package.json +++ b/package.json @@ -3,14 +3,16 @@ "version": "0.0.1", "private": true, "type": "module", + "packageManager": "npm@10.9.3", "workspaces": [ "packages/*" ], "scripts": { - "build": "npm run build -w @bearing-dev/compass -w @bearing-dev/lantern && npm run build -w @bearing-dev/teleport --if-present", + "build": "turbo run build", "prepare": "npm run build" }, "devDependencies": { + "turbo": "^2.7.0", "typescript": "^5.3.0" } } diff --git a/packages/compass/.turbo/turbo-build.log b/packages/compass/.turbo/turbo-build.log new file mode 100644 index 0000000..f3d16ba --- /dev/null +++ b/packages/compass/.turbo/turbo-build.log @@ -0,0 +1,4 @@ + +> @bearing-dev/compass@0.1.0 build +> tsc + diff --git a/packages/lantern/.turbo/turbo-build.log b/packages/lantern/.turbo/turbo-build.log new file mode 100644 index 0000000..4221eed --- /dev/null +++ b/packages/lantern/.turbo/turbo-build.log @@ -0,0 +1,4 @@ + +> @bearing-dev/lantern@0.1.0 build +> tsc + diff --git a/packages/lantern/package.json b/packages/lantern/package.json index cbfe896..1c3152f 100644 --- a/packages/lantern/package.json +++ b/packages/lantern/package.json @@ -22,7 +22,6 @@ ], "scripts": { "build": "tsc", - "prepare": "npm run build", "test": "vitest run", "test:watch": "vitest", "typecheck": "tsc --noEmit" diff --git a/packages/teleport/.turbo/turbo-build.log b/packages/teleport/.turbo/turbo-build.log new file mode 100644 index 0000000..6775a49 --- /dev/null +++ b/packages/teleport/.turbo/turbo-build.log @@ -0,0 +1,17 @@ + +> @bearing-dev/teleport@0.0.1 build +> tsup + +CLI Building entry: src/index.ts +CLI Using tsconfig: tsconfig.json +CLI tsup v8.5.1 +CLI Using tsup config: /Users/joshribakoff/bearing-dev/packages/teleport/tsup.config.ts +CLI Target: es2020 +CLI Cleaning output folder +ESM Build start +ESM dist/index.js 20.58 KB +ESM dist/index.js.map 47.13 KB +ESM ⚑️ Build success in 32ms +DTS Build start +DTS ⚑️ Build success in 1318ms +DTS dist/index.d.ts 8.70 KB diff --git a/packages/teleport/package.json b/packages/teleport/package.json index 5ec4ad1..a2fff6c 100644 --- a/packages/teleport/package.json +++ b/packages/teleport/package.json @@ -18,7 +18,6 @@ ], "scripts": { "build": "tsup", - "prepare": "npm run build", "test": "vitest run", "test:watch": "vitest", "test:e2e": "playwright test", @@ -26,7 +25,7 @@ "typecheck": "tsc --noEmit" }, "dependencies": { - "@bearing-dev/compass": "*" + "@bearing-dev/compass": ">=0.0.1" }, "devDependencies": { "@playwright/test": "^1.57.0", diff --git a/packages/teleport/tsup.config.ts b/packages/teleport/tsup.config.ts index c0897af..345cb0e 100644 --- a/packages/teleport/tsup.config.ts +++ b/packages/teleport/tsup.config.ts @@ -3,10 +3,9 @@ import { defineConfig } from 'tsup'; export default defineConfig({ entry: ['src/index.ts'], format: ['esm'], - dts: false, // Disabled due to workspace build order issues + dts: true, clean: true, - // Keep compass as external dependency (resolved at runtime) - external: ['@bearing-dev/compass'], + noExternal: ['@bearing-dev/compass'], target: 'es2020', minify: false, sourcemap: true, diff --git a/turbo.json b/turbo.json new file mode 100644 index 0000000..27002df --- /dev/null +++ b/turbo.json @@ -0,0 +1,15 @@ +{ + "$schema": "https://turbo.build/schema.json", + "tasks": { + "build": { + "dependsOn": ["^build"], + "outputs": ["dist/**"] + }, + "test": { + "dependsOn": ["build"] + }, + "typecheck": { + "dependsOn": ["^build"] + } + } +} From 44fd82018fff2be1690911fd9d60942ea66fa474 Mon Sep 17 00:00:00 2001 From: Josh Ribakoff Date: Fri, 19 Dec 2025 21:10:23 -0800 Subject: [PATCH 27/35] chore: gitignore turbo cache, remove accidentally committed cache --- .gitignore | 1 + .turbo/cache/024b5851ec647970-meta.json | 1 - .turbo/cache/024b5851ec647970.tar.zst | Bin 7346 -> 0 bytes .turbo/cache/9ff760350eb49416-meta.json | 1 - .turbo/cache/9ff760350eb49416.tar.zst | Bin 19590 -> 0 bytes .turbo/cache/a16c9688c2994863-meta.json | 1 - .turbo/cache/a16c9688c2994863.tar.zst | Bin 2312 -> 0 bytes packages/compass/.turbo/turbo-build.log | 4 ---- packages/lantern/.turbo/turbo-build.log | 4 ---- packages/teleport/.turbo/turbo-build.log | 17 ----------------- 10 files changed, 1 insertion(+), 28 deletions(-) delete mode 100644 .turbo/cache/024b5851ec647970-meta.json delete mode 100644 .turbo/cache/024b5851ec647970.tar.zst delete mode 100644 .turbo/cache/9ff760350eb49416-meta.json delete mode 100644 .turbo/cache/9ff760350eb49416.tar.zst delete mode 100644 .turbo/cache/a16c9688c2994863-meta.json delete mode 100644 .turbo/cache/a16c9688c2994863.tar.zst delete mode 100644 packages/compass/.turbo/turbo-build.log delete mode 100644 packages/lantern/.turbo/turbo-build.log delete mode 100644 packages/teleport/.turbo/turbo-build.log diff --git a/.gitignore b/.gitignore index b56e5b2..90fc523 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ test-results node_modules dist playwright-report +.turbo diff --git a/.turbo/cache/024b5851ec647970-meta.json b/.turbo/cache/024b5851ec647970-meta.json deleted file mode 100644 index 8983607..0000000 --- a/.turbo/cache/024b5851ec647970-meta.json +++ /dev/null @@ -1 +0,0 @@ -{"hash":"024b5851ec647970","duration":1614} \ No newline at end of file diff --git a/.turbo/cache/024b5851ec647970.tar.zst b/.turbo/cache/024b5851ec647970.tar.zst deleted file mode 100644 index 2a623f4391b5ee9fa1155cc5a380d85a8daae06c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 7346 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 @bearing-dev/compass@0.1.0 build -> tsc - diff --git a/packages/lantern/.turbo/turbo-build.log b/packages/lantern/.turbo/turbo-build.log deleted file mode 100644 index 4221eed..0000000 --- a/packages/lantern/.turbo/turbo-build.log +++ /dev/null @@ -1,4 +0,0 @@ - -> @bearing-dev/lantern@0.1.0 build -> tsc - diff --git a/packages/teleport/.turbo/turbo-build.log b/packages/teleport/.turbo/turbo-build.log deleted file mode 100644 index 6775a49..0000000 --- a/packages/teleport/.turbo/turbo-build.log +++ /dev/null @@ -1,17 +0,0 @@ - -> @bearing-dev/teleport@0.0.1 build -> tsup - -CLI Building entry: src/index.ts -CLI Using tsconfig: tsconfig.json -CLI tsup v8.5.1 -CLI Using tsup config: /Users/joshribakoff/bearing-dev/packages/teleport/tsup.config.ts -CLI Target: es2020 -CLI Cleaning output folder -ESM Build start -ESM dist/index.js 20.58 KB -ESM dist/index.js.map 47.13 KB -ESM ⚑️ Build success in 32ms -DTS Build start -DTS ⚑️ Build success in 1318ms -DTS dist/index.d.ts 8.70 KB From 7e68cb586dedc5b623c5b435ae1f849308d15386 Mon Sep 17 00:00:00 2001 From: Josh Ribakoff Date: Fri, 19 Dec 2025 21:24:20 -0800 Subject: [PATCH 28/35] refactor: move highlight styling to consumer MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remove hardcoded .teleport-highlight styles from library. Consumer is now responsible for styling the highlight class. πŸ€– Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- packages/teleport/Teleport.astro | 8 -------- packages/teleport/src/index.ts | 2 +- packages/teleport/src/teleport.ts | 19 ------------------- 3 files changed, 1 insertion(+), 28 deletions(-) diff --git a/packages/teleport/Teleport.astro b/packages/teleport/Teleport.astro index 522e44e..100c479 100644 --- a/packages/teleport/Teleport.astro +++ b/packages/teleport/Teleport.astro @@ -40,14 +40,6 @@ const { style="display: none;" > - -