diff --git a/.agents/rules/agents.md b/.agents/rules/agents.md new file mode 100644 index 0000000..dbbabdb --- /dev/null +++ b/.agents/rules/agents.md @@ -0,0 +1,23 @@ +# Agent Guidance Rules + +Use these rules when creating or updating project agent guidance. + +## Skills + +- Store project skills in `.agents/skills//SKILL.md`. +- Use lowercase hyphen-case skill names. +- Keep skills short and consistent with the existing files in `.agents/skills`. +- Include only `name` and `description` in YAML frontmatter. +- Make `description` specific enough to trigger the skill in the right tasks. +- In the body, list required context and a short workflow. +- Reference `.agents/rules/*` instead of duplicating rule text in skills. +- Do not add `agents/openai.yaml`, scripts, references, assets, or extra docs + unless the user explicitly asks or the skill cannot work without them. +- Add every new project skill to `AGENTS.md`. + +## Rules + +- Store project rules in `.agents/rules/.md`. +- Keep rules concise, task-focused, and reusable by multiple skills. +- Add every new project rule to `AGENTS.md`. +- Prefer updating an existing rule over creating overlapping guidance. diff --git a/.agents/rules/architecture.md b/.agents/rules/architecture.md new file mode 100644 index 0000000..cbc5cec --- /dev/null +++ b/.agents/rules/architecture.md @@ -0,0 +1,10 @@ +# Architecture Rules + +Architecture source of truth: + +- `docs/ARCHITECTURE.md` +- `docs/DOM-TO-RAG-PIPELINE.md` + +Read these docs before architectural or DOM-to-RAG pipeline changes. + +If docs conflict with code, treat code as primary and update docs. diff --git a/.agents/rules/changelog.md b/.agents/rules/changelog.md new file mode 100644 index 0000000..bca2287 --- /dev/null +++ b/.agents/rules/changelog.md @@ -0,0 +1,32 @@ +# Changelog Rules + +Use these rules when creating, updating, or reviewing `CHANGELOG.md`. + +## Goal + +Keep `CHANGELOG.md` as a concise record of notable project changes. + +## Source of truth + +Use the current repository diff and existing `CHANGELOG.md` format as the source +of truth. Do not add entries for work that is not present in the codebase. + +## Format + +- Keep the top-level title as `# Changelog`. +- Keep the introductory sentence below the title. +- Put unreleased branch changes under `## [Unreleased]`. +- Group released changes by version using `## [x.y.z] - YYYY-MM-DD`. +- Keep `## [Unreleased]` above released versions; keep released versions in reverse chronological order. +- Group entries under `### Added`, `### Changed`, `### Fixed`, `### Removed`, or another Keep a Changelog-style category only when it is needed. +- Write entries as Markdown bullet points. +- Keep each entry short, user-visible, and specific. +- Use links only when they add useful context, such as a public demo URL. + +## Rules + +- Record notable user-visible, behavior, documentation, packaging, or architecture changes. +- Do not include low-level implementation noise unless it changes behavior, public contracts, or project usage. +- Do not duplicate the backlog, commit history, or pull request description. +- Do not invent release dates or versions. If `## [Unreleased]` is absent, create it above the latest released version and add branch changes there. +- Preserve existing wording and categories unless the current change requires a precise update. diff --git a/.agents/rules/code-style.md b/.agents/rules/code-style.md new file mode 100644 index 0000000..a47b651 --- /dev/null +++ b/.agents/rules/code-style.md @@ -0,0 +1,39 @@ +# Code Style and Engineering Practices + +Use these rules when writing or reviewing code in this repository. + +## General + +- Keep modules small, explicit, and easy to reason about. +- Prefer clear domain names over generic abstractions. +- Keep one source of truth for state; avoid mirrored state unless it is intentionally derived and synchronized. +- Handle async failures at the boundary where the user or caller can act on them. +- Treat external input, selectors, page data, model output, and browser APIs as unreliable. +- Prefer typed contracts over loosely shaped objects. +- Avoid adding dependencies unless they materially reduce project complexity. +- Keep generated UI text and diagnostics concise and actionable. + +## `packages/page-trail` + +- Maintain strong test coverage for every behavior change. +- Cover collectors, extractors, importance scoring, semantic formatting, markdown generation, and utility edge cases. +- Use DOM fixtures for behavior that depends on real document structure. +- Test privacy and safety behavior, especially sensitive value filtering. +- Keep extraction deterministic; avoid hidden global state. +- Preserve stable output shapes because backend indexing and extension navigation depend on them. + +## `apps/extension` + +- Keep the extension lightweight: minimize runtime dependencies, bundle size, DOM work, and long-lived listeners. +- Keep UI components mostly presentational; place messaging, storage, and page effects in hooks or services. +- Avoid duplicated component state. Parent view models should own workflow state such as wizard progress. +- Guard async UI actions against duplicate submits, races, and unavailable content scripts. +- Keep accessibility claims truthful: do not mark UI as modal without modal behavior, and keep tabs, dialogs, buttons, and panels keyboard-accessible. +- Preserve page isolation through Shadow DOM, scoped class names, prefixed attributes, and minimal global CSS impact. +- Prefix extension-owned CSS classes, custom properties, data attributes, and DOM IDs with `flowforge`, not abbreviations like `ff`. +- Be careful with host-page selectors and DOM mutation. Escape selector values and fail safely when target elements disappear. +- Prefer Preact-compatible patterns that avoid unnecessary re-renders and expensive layout work. + +## `apps/backend` + +Reserved for backend-specific rules. diff --git a/.agents/rules/documentation.md b/.agents/rules/documentation.md new file mode 100644 index 0000000..ea27940 --- /dev/null +++ b/.agents/rules/documentation.md @@ -0,0 +1,126 @@ +# Documentation Rules + +Use these rules when creating or updating project documentation. + +## Goal + +Update documentation based strictly on the current codebase and existing docs. + +Rewrite and align documentation. Do not invent or expand it beyond the +implemented project. + +## Sources of truth + +1. Codebase +2. Existing documentation + +If something is unclear or missing, omit it. + +## Files to update + +- `README.md` +- `docs/ARCHITECTURE.md` +- `docs/DOM-TO-RAG-PIPELINE.md` +- `packages/page-trail/README.md` +- `apps/backend/README.md` +- `apps/extension/README.md` + +## Rules + +- Do not invent features, APIs, workflows, or architecture. +- Keep terminology consistent across all docs. +- Align all documents with each other and the codebase. +- Avoid duplication between files. +- Be concise, clear, and structured. +- Keep links valid. +- Keep commands correct. +- Keep Markdown clean. +- If content exceeds scope, trim it instead of expanding other docs. + +## Document roles + +- `README.md` — entry point: what it is and how to run it. +- `docs/ARCHITECTURE.md` — high-level system overview. +- `docs/DOM-TO-RAG-PIPELINE.md` — DOM-to-RAG pipeline description. +- `packages/page-trail/README.md` — canonical DOM representation (`PageTrail`). +- `apps/backend/README.md` — backend responsibilities and usage. +- `apps/extension/README.md` — extension responsibilities and usage. + +## Target structure + +### `README.md` + +Target length: 100–120 lines. + +- "Forging your experience..." +- Demo +- Overview +- Use cases +- Disclaimer +- Quick start +- Usage +- Security +- Roadmap +- Documentation +- License + +### `docs/ARCHITECTURE.md` + +Target length: 80–100 lines. + +- Overview +- Components +- Interaction flow +- Pipeline (high-level) +- Key decisions +- Contracts +- Constraints + +### `docs/DOM-TO-RAG-PIPELINE.md` + +Target length: 80–100 lines. + +- Link to `packages/page-trail/README.md` for more information about the canonical DOM snapshot format. +- Overview +- Stages: + - Extraction to structure representation + - Transforming to semantic representation + - Indexing / retrieval + - Reranking + - Resolution to tool results + +### `packages/page-trail/README.md` + +Target length: 60–80 lines. + +- Overview +- Structure +- Content elements +- Interactive elements +- Context +- Importance +- Format +- Usage + +### `apps/backend/README.md` + +Target length: 50–60 lines. + +- Purpose +- Responsibilities +- Run +- Configuration +- API (short) +- Key parts +- Notes + +### `apps/extension/README.md` + +Target length: 50–60 lines. + +- Purpose +- Responsibilities +- Run +- Load in Chrome +- Key parts +- Notes diff --git a/.agents/skills/code-review/SKILL.md b/.agents/skills/code-review/SKILL.md new file mode 100644 index 0000000..2b1587c --- /dev/null +++ b/.agents/skills/code-review/SKILL.md @@ -0,0 +1,27 @@ +--- +name: code-review +description: Review FlowForge branch changes against master/main, architecture rules, code-style rules, changelog rules, CHANGELOG.md, and docs/BACKLOG.md. +--- + +# Code Review + +Use this skill when reviewing branch changes. + +## Required context + +- `.agents/rules/architecture.md` +- `.agents/rules/code-style.md` +- `.agents/rules/changelog.md` +- `CHANGELOG.md` +- `docs/BACKLOG.md` + +## Workflow + +1. Compare the branch with `master`; use `main` only if `master` is absent. +2. Inspect `git status --short` and the diff from the merge base. +3. Review architecture and code style using the required context. +4. For `packages/page-trail`, check that behavior changes are well covered by tests. +5. For `apps/extension`, check lightweight implementation, component state ownership, async handling, a11y truthfulness, page isolation, and selector safety. +6. Verify behavior or user-visible changes are reflected in `CHANGELOG.md` according to `.agents/rules/changelog.md`. +7. Verify completed work is reflected in `docs/BACKLOG.md`: if a backlog item was implemented, it should be removed from the backlog. +8. Report findings by severity with file, problem, impact, and suggested fix. diff --git a/.agents/skills/update-changelog/SKILL.md b/.agents/skills/update-changelog/SKILL.md new file mode 100644 index 0000000..0a9137b --- /dev/null +++ b/.agents/skills/update-changelog/SKILL.md @@ -0,0 +1,24 @@ +--- +name: update-changelog +description: Update FlowForge CHANGELOG.md from changes since the latest release tag using changelog rules. +--- + +# Update Changelog + +Use this skill when updating `CHANGELOG.md` from changes since the latest release tag. + +## Required context + +- `.agents/rules/changelog.md` +- `CHANGELOG.md` + +## Workflow + +1. Find the latest release tag using version tags such as `vX.Y.Z`; do not use `master` or `main` as the comparison baseline. +2. Inspect `git status --short` and the diff from the latest release tag. +3. Read `.agents/rules/changelog.md` and follow it strictly. +4. Inspect existing `CHANGELOG.md` format and target section. +5. Add only notable changes present in the branch diff. +6. Do not add low-level implementation noise, duplicate existing entries, or invent versions/dates. +7. If the target release section is unclear, ask before editing. +8. Report the section changed and a short summary of entries added or omitted. diff --git a/.agents/skills/update-docs/SKILL.md b/.agents/skills/update-docs/SKILL.md new file mode 100644 index 0000000..be9ba5b --- /dev/null +++ b/.agents/skills/update-docs/SKILL.md @@ -0,0 +1,21 @@ +--- +name: update-docs +description: Update project documentation based strictly on the current codebase and existing docs. +--- + +# Update Docs + +Use this skill when updating project documentation. + +## Required context + +- `.agents/rules/documentation.md` +- `.agents/rules/architecture.md` + +## Workflow + +1. Review the documentation targets. +2. Inspect the relevant code before changing docs. +3. Identify stale, duplicated, or unsupported claims. +4. Update docs according to the documentation rules. +5. Run relevant formatting or validation checks if available. diff --git a/.claude/commands/update-docs.md b/.claude/commands/update-docs.md deleted file mode 100644 index 97ff05a..0000000 --- a/.claude/commands/update-docs.md +++ /dev/null @@ -1,141 +0,0 @@ -You are updating project documentation based strictly on the current codebase and existing docs. - -Your goal is to rewrite and align documentation — not to invent or expand it. - ---- - -SOURCE OF TRUTH - -- Codebase (primary) -- Existing documentation (secondary) - -If something is unclear or missing — omit it. Do NOT assume. - ---- - -FILES TO UPDATE - -- README.md -- docs/ARCHITECTURE.md -- docs/DOM-TO-RAG-PIPELINE.md -- packages/page-model/README.md -- apps/backend/README.md -- apps/extension/README.md - ---- - -RULES - -- Do not invent features, APIs, or workflows -- Keep terminology consistent across all docs -- Align all documents with each other and the codebase -- Avoid duplication between files -- Be concise, clear, and structured - ---- - -DOCUMENT ROLES - -- README.md → entry point (what it is, how to run) -- ARCHITECTURE.md → high-level system overview -- DOM-TO-RAG-PIPELINE.md → pipeline description -- page-model README → canonical DOM representation (PageModel) -- backend README → backend responsibilities and usage -- extension README → extension responsibilities and usage - ---- - -TARGET STRUCTURE - -README.md (100–120 lines) - -- "Forging your experience..." -- Demo -- Overview -- Use Cases -- Disclaimer -- Quick Start -- Usage -- Security -- Roadmap -- Documentation -- License - ---- - -docs/ARCHITECTURE.md (80–100 lines) - -- Overview -- Components -- Interaction Flow -- Pipeline (high-level) -- Key Decisions -- Contracts -- Constraints - ---- - -docs/DOM-TO-RAG-PIPELINE.md (80–100 lines) - -- Overview -- Stages: - - Extraction to Structure Representation - - Transforming to Semantic Representation - - Indexing / Retrieval - - Reranking - - Resolution to Tool Results - ---- - -packages/page-model/README.md (60–80 lines) - -- Overview -- Structure -- Content elements -- Interactive elements -- Context -- Importance -- Format -- Usage - ---- - -apps/backend/README.md (50–60 lines) - -- Purpose -- Responsibilities -- Run -- Configuration -- API (short) -- Key Parts -- Notes - ---- - -apps/extension/README.md (50–60 lines) - -- Purpose -- Responsibilities -- Run -- Load in Chrome -- Key Parts -- Notes - ---- - -IMPORTANT - -- Keep links valid -- Keep commands correct -- Keep markdown clean -- If content exceeds scope → trim, don’t expand other docs - ---- - -TASK - -1. Review all files -2. Rewrite to match structure and size limits -3. Align terminology across all docs -4. Remove duplication -5. Output updated content for all files diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..eebeacb --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,76 @@ +name: CI + +on: + pull_request: + branches: + - master + +jobs: + linters: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v5 + - uses: actions/setup-node@v5 + with: + node-version: 22 + cache: npm + - run: npm ci + - run: npm run format:check + - run: npm run lint -ws + + build-packages: + runs-on: ubuntu-latest + permissions: + contents: read + actions: read + checks: write + needs: + - linters + steps: + - uses: actions/checkout@v5 + - uses: actions/setup-node@v5 + with: + node-version: 22 + cache: npm + - run: npm ci + # page-trail + - run: npm run typecheck -w @flowforge/page-trail + - run: npm run test -w @flowforge/page-trail + - if: ${{ !cancelled() }} + uses: dorny/test-reporter@v3 + with: + name: page-trail/unit-tests + path: packages/page-trail/reports/unit-tests/results/junit.xml + reporter: jest-junit + fail-on-error: false + - run: npm run build -w @flowforge/page-trail + # contract + - run: npm run typecheck -w @flowforge/contract + - run: npm run build -w @flowforge/contract + # artifacts + - uses: actions/upload-artifact@v4 + with: + name: build-packages-artifacts + path: | + packages/page-trail/dist + packages/contract/dist + + build-apps: + runs-on: ubuntu-latest + needs: + - build-packages + steps: + - uses: actions/checkout@v5 + - uses: actions/setup-node@v5 + with: + node-version: 22 + cache: npm + - run: npm ci + - uses: actions/download-artifact@v4 + with: + name: build-packages-artifacts + path: packages + - run: npm run typecheck -w @flowforge/backend + - run: npm run typecheck -w @flowforge/extension + - run: npm run build -w @flowforge/backend + - run: npm run build -w @flowforge/extension diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..14161c9 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,28 @@ +# Agent Instructions + +## Overview + +FlowForge is a Web Onboarding Assistant that provides contextual UI guidance in +web applications. It is an npm workspace project with a browser extension, +backend, and shared packages. + +## Precedence + +`AGENTS.md` is the canonical shared instruction file. `CLAUDE.md` is only a +Claude compatibility shim that imports `AGENTS.md`; if they ever conflict, +`AGENTS.md` takes precedence. + +## Guidance + +Shared agent guidance lives in `.agents/`. + +- `.agents/rules/architecture.md` +- `.agents/rules/agents.md` +- `.agents/rules/changelog.md` +- `.agents/rules/code-style.md` +- `.agents/rules/documentation.md` +- `.agents/skills/code-review/SKILL.md` +- `.agents/skills/update-changelog/SKILL.md` +- `.agents/skills/update-docs/SKILL.md` + +Use relevant rules and skills before making changes. diff --git a/CHANGELOG.md b/CHANGELOG.md index 972cae1..440c0c9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,26 +2,45 @@ All notable changes to the project will be documented in this file. +## [Unreleased] + +### Added + +- Page Inspector for viewing PageTrail `basics`, `content`, `interactive` elements, `metadata`, and semantic Markdown output. +- PageTrail snapshot `metadata` with collection counts, limits, timestamps, and duration. +- CI workflow for linting, type checks, builds, and PageTrail unit tests. +- Project context for agents, including shared rules, skills, and navigation guidance. +- Added initial `settings` and `openPageInspector` support for embed delivery via Runtime API. + +### Changed + +- Renamed PageModel to PageTrail across the project. +- Improved PageTrail data collection performance and semantic formatting. + ## [0.1.4] - 2026-05-03 ### Added + - Early build for embed integration, used for Demo. - Landing and live Demo at [useflowforge.app](https://useflowforge.app/). - Added a sandbox for extension testing. ### Changed + - Improved semantics and A11y for extension components. - Moved PageModel to a separated package; supported more ARIA roles. ## [0.1.3] - 2026-04-20 ### Added + - Dark theme for the extension. - Token usage display in results. - Use `Bbox` for importance scoring on extraction. - Use `inViewport` and `aboveTheFold` for semantic formatting. ### Changed + - Improved `getPageSummary` tool with element importance scoring. - Improved semantic formatting for indexable documents. - Refactored UI components and design tokens. @@ -30,4 +49,5 @@ All notable changes to the project will be documented in this file. ## [0.1.2] - 2026-04-12 ### Added + - Initial FlowForge MVP release. diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..43c994c --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1 @@ +@AGENTS.md diff --git a/README.md b/README.md index 0f286b7..1b4fcd3 100644 --- a/README.md +++ b/README.md @@ -4,8 +4,8 @@ _Forging your experience..._ FlowForge is a Web Onboarding Assistant that turns user intent into actionable UI guidance inside web applications. -- Explore content and interface instantly -- Ask anything about the page +- Ask questions about the current page +- Find relevant UI elements - Get step-by-step onboarding flows ## Demo @@ -14,20 +14,18 @@ Try the live **Demo** at [useflowforge.app](https://useflowforge.app/) ✦ ## Overview -**Why?** Modern web apps are powerful – but hard to navigate. - -The Assistant lets you ask questions in natural language and get immediate, contextual guidance directly in the UI. +Modern web apps are powerful but hard to navigate. The Assistant lets you ask natural-language questions and get contextual guidance directly in the UI. It can: + - highlight relevant elements - explain what's on the page - guide users step-by-step through workflows -It works on any website out of the box and becomes product-aware when integrated. - **Under the hood:** browser extension + AI agent (ReAct) + RAG pipeline. -## Use Cases +## Use cases + - Ask a question and get a clear explanation of what the page is about. - Locate links, buttons, or contacts without manual search. - Follow step-by-step actions directly in the interface. @@ -46,7 +44,7 @@ It is not production-ready, and the core engine is still evolving. Expect limita [Changelog](./CHANGELOG.md) – see the list of changes. -## Quick Start +## Quick start ### Prerequisites @@ -61,12 +59,7 @@ npm i npm start ``` -This will: - -- Install dependencies -- Build backend and extension -- Guide you to install the extension in Chrome -- Start the backend at http://localhost:3477 +This installs dependencies, builds backend and extension, guides Chrome installation, and starts the backend at http://localhost:3477.

@@ -77,9 +70,9 @@ This will: 1. Open any website 2. Click the Assistant extension icon 3. Ask a question, e.g.: - - "Where is the login button?" - - "How do I checkout?" - - "Show me the search bar" + - "Where is the login button?" + - "How do I checkout?" + - "Show me the search bar" 4. View the answer and highlighted elements directly on the page ## Security @@ -94,6 +87,7 @@ Note: As an early-stage MVP, security is evolving and not yet production-ready. ## Roadmap Focus areas: + 1. Improve the core engine: extractors, embeddings, reasoning, and tools 2. Extend context from single pages to full websites 3. Build a proper standalone extension distribution @@ -103,8 +97,8 @@ Focus areas: ## Documentation - [Architecture](docs/ARCHITECTURE.md) — system design and component interaction -- [DOM to RAG Pipeline](docs/DOM-TO-RAG-PIPELINE.md) – from DOM to actionable intelligence -- [PageModel](packages/page-model/README.md) — DOM representation for UI understanding +- [DOM to RAG pipeline](docs/DOM-TO-RAG-PIPELINE.md) – from DOM to actionable intelligence +- [PageTrail](packages/page-trail/README.md) — DOM representation for UI understanding - [Backend](apps/backend/README.md) — AI agent backend and RAG pipeline - [Extension](apps/extension/README.md) — browser extension and page extraction diff --git a/apps/backend/.env.example b/apps/backend/.env.example index e034069..46f7a1f 100644 --- a/apps/backend/.env.example +++ b/apps/backend/.env.example @@ -20,7 +20,7 @@ OLLAMA_LOCAL_EMBEDDING=nomic-embed-text # OpenAI API, requires API key OPENAI_API_KEY= OPENAI_MODEL=gpt-4o-mini -OPENAI_EMBEDDING_MODEL=text-embedding-3-small +OPENAI_EMBEDDING=text-embedding-3-small # Agent Settings AGENT_TOOL_CALL_LIMIT=6 diff --git a/apps/backend/README.md b/apps/backend/README.md index 162f586..5c93f21 100644 --- a/apps/backend/README.md +++ b/apps/backend/README.md @@ -9,8 +9,9 @@ AI agent backend that processes user queries, understands page context, and gene - Run AI agent with ReAct pattern (LangGraph) - Process user queries and page data - Perform semantic search over indexed content -- Manage vector storage and embeddings (LanceDB) +- Manage embeddings and LanceDB vector storage - Expose HTTP API for extension +- Track in-memory query analytics ## Run @@ -19,24 +20,19 @@ npm i cp .env.example .env npm run build npm start -``` - -Development mode with auto-reload: - -```bash npm run dev ``` -Server runs on http://localhost:3477 - ## Configuration Configured via `.env` file: +- `PORT`, `LOG_LEVEL` - `LLM_PROVIDER` — `ollama-local` or `openai` -- `OLLAMA_LOCAL_MODEL` / `OPENAI_MODEL` — model names -- `INDEXER_CHUNK_SIZE` — document chunk size for RAG -- `AGENT_TOOL_CALL_LIMIT` — max tool calls per query +- `OLLAMA_LOCAL_MODEL`, `OPENAI_MODEL` +- `OLLAMA_LOCAL_EMBEDDING`, `OPENAI_EMBEDDING` +- `INDEXER_CHUNK_SIZE`, `INDEXER_CHUNK_OVERLAP_RATIO` — RAG chunking +- `AGENT_TOOL_CALL_LIMIT`, `AGENT_RECURSION_LIMIT`, `AGENT_TEMPERATURE`, `AGENT_MAX_TOKENS` — agent runtime See [.env.example](.env.example) for all options. @@ -44,18 +40,13 @@ See [.env.example](.env.example) for all options. - `POST /query` — main agent entry point (question + page data) - `POST /search` — semantic search over indexed content -- `GET /analytics` — usage data and query tracking -- `GET /health` — service status - -## Key Parts +- `GET /analytics` / `GET /health` — analytics and service status -- `agent/` — ReAct agent implementation and tool definitions -- `indexer/` — RAG pipeline, embeddings, and vector search -- `analytics/` — query tracking and usage metrics +`/query` expects `question`, `pageTrail`, and `domain`; it returns answer, mode, optional topic, matched elements, and execution metadata. ## Notes -- Single-page context only (no cross-page memory) -- In-memory analytics (resets on restart) -- Requires running LLM provider (Ollama or OpenAI API key) +- Single-page context; no cross-page memory +- In-memory analytics; resets on restart +- Requires Ollama or OpenAI API key - See [Architecture](../../docs/ARCHITECTURE.md) for system design diff --git a/apps/backend/eslint.config.mjs b/apps/backend/eslint.config.mjs new file mode 100644 index 0000000..37f0527 --- /dev/null +++ b/apps/backend/eslint.config.mjs @@ -0,0 +1,23 @@ +import js from '@eslint/js'; +import globals from 'globals'; +import tseslint from 'typescript-eslint'; + +export default tseslint.config( + { + ignores: ['dist/**', 'data/**'], + }, + js.configs.recommended, + ...tseslint.configs.recommended, + { + files: ['src/**/*.ts', 'scripts/**/*.{js,ts}'], + languageOptions: { + globals: { + ...globals.node, + ...globals.es2022, + }, + }, + rules: { + 'no-undef': 'off', + }, + }, +); diff --git a/apps/backend/package.json b/apps/backend/package.json index f47c526..445ee98 100644 --- a/apps/backend/package.json +++ b/apps/backend/package.json @@ -8,22 +8,17 @@ "engines": { "node": ">=22" }, - "imports": { - "#self/config": "./dist/config.js", - "#self/types": "./dist/types/index.js", - "#self/agent": "./dist/agent/index.js", - "#self/indexer": "./dist/indexer/index.js", - "#self/analytics": "./dist/analytics/index.js", - "#self/server": "./dist/server/index.js" - }, "scripts": { "clean:dist": "rm -rf dist/", "clean:data": "rm -rf data/", "clean": "npm run clean:dist && npm run clean:data", + "typecheck": "tsc -p tsconfig.json --noEmit", "setup": "node scripts/quick-setup.js", - "build": "npm run clean:dist && tsc -p tsconfig.json", + "build": "npm run clean:dist && tsc -p tsconfig.json && tsc-alias -p tsconfig.json", "start": "node dist/server.js", - "dev": "tsc -p tsconfig.json --watch" + "lint": "eslint .", + "lint:fix": "eslint . --fix", + "dev": "concurrently \"tsc -p tsconfig.json --watch\" \"tsc-alias -p tsconfig.json --watch\"" }, "keywords": [ "ai", @@ -36,8 +31,8 @@ "user-experience" ], "dependencies": { - "@flowforge/page-model": "*", "@flowforge/contract": "*", + "@flowforge/page-trail": "*", "@lancedb/lancedb": "^0.27.1", "@langchain/core": "^1.1.35", "@langchain/ollama": "^1.2.6", @@ -51,9 +46,16 @@ "zod": "^4.3.6" }, "devDependencies": { + "@eslint/js": "^10.0.1", + "@tsconfig/node22": "^22.0.5", "@types/cors": "^2.8.17", "@types/express": "^5.0.6", "@types/node": "^25.5.0", - "typescript": "^6.0.2" + "concurrently": "^10.0.4", + "eslint": "^10.8.1", + "globals": "^17.9.0", + "tsc-alias": "^1.9.1", + "typescript": "^6.0.2", + "typescript-eslint": "^8.66.0" } } diff --git a/apps/backend/src/agent/WebNavigationAgent.ts b/apps/backend/src/agent/WebNavigationAgent.ts index 9947cf9..b9b1f07 100644 --- a/apps/backend/src/agent/WebNavigationAgent.ts +++ b/apps/backend/src/agent/WebNavigationAgent.ts @@ -3,8 +3,8 @@ import { HumanMessage, AIMessage, createAgent } from 'langchain'; import type { BaseMessage } from 'langchain'; import { buildSystemPrompt, buildStructuredOutputPrompt } from './prompts.ts'; -import { PageContextProvider } from '#self/indexer'; -import type { AgentExecResult, ToolCallInfo, AgentResponse, LlmProviderInfo } from '#self/types'; +import { PageContextProvider } from '@/indexer'; +import type { AgentExecResult, ToolCallInfo, AgentResponse, LlmProviderInfo } from '@/types'; import { ToolsRegistry } from './tools/ToolsRegistry.ts'; import { type AgentResult, type UsageMetadata, validateDataId, AgentResultSchema } from '@flowforge/contract'; @@ -107,7 +107,7 @@ export class WebNavigationAgent { try { const parsed = JSON.parse(aiMessageContent); agentResult = AgentResultSchema.parse(parsed); - } catch (error) { + } catch { if (this.verbose) { console.log(`[Agent] The execution result is not valid JSON, invoking structured prompt...`); } @@ -140,7 +140,7 @@ export class WebNavigationAgent { const usageMetadata: UsageMetadata = { inputTokens: 0, outputTokens: 0, - totalTokens: 0 + totalTokens: 0, }; for (const message of invokeState.messages) { diff --git a/apps/backend/src/agent/llm/LlmProviderFactory.ts b/apps/backend/src/agent/llm/LlmProviderFactory.ts index b56f223..4955b6c 100644 --- a/apps/backend/src/agent/llm/LlmProviderFactory.ts +++ b/apps/backend/src/agent/llm/LlmProviderFactory.ts @@ -1,5 +1,5 @@ -import type { AppConfig } from '#self/config'; -import type { LlmProvider } from '#self/types'; +import type { AppConfig } from '@/config'; +import type { LlmProvider } from '@/types'; import { OpenAiLlmProvider } from './OpenAiLlmProvider.ts'; import { OllamaLocalLlmProvider } from './OllamaLocalLlmProvider.ts'; diff --git a/apps/backend/src/agent/llm/OllamaLocalEmbeddingProvider.ts b/apps/backend/src/agent/llm/OllamaLocalEmbeddingProvider.ts index 384ca81..420d69f 100644 --- a/apps/backend/src/agent/llm/OllamaLocalEmbeddingProvider.ts +++ b/apps/backend/src/agent/llm/OllamaLocalEmbeddingProvider.ts @@ -1,4 +1,10 @@ -import type { EmbeddingProvider } from '#self/types'; +import type { EmbeddingProvider } from '@/types'; +import { z } from 'zod'; + +export const OllamaApiEmbedSchema = z.object({ + model: z.string(), + embeddings: z.array(z.array(z.number())), +}); export class OllamaLocalEmbeddingProvider implements EmbeddingProvider { private readonly baseUrl: string; @@ -34,8 +40,9 @@ export class OllamaLocalEmbeddingProvider implements EmbeddingProvider { keep_alive: '5m', }), }); - const json = await res.json(); + const body = await res.json(); + const data = OllamaApiEmbedSchema.parse(body); - return json.embeddings; + return data.embeddings; } } diff --git a/apps/backend/src/agent/llm/OllamaLocalLlmProvider.ts b/apps/backend/src/agent/llm/OllamaLocalLlmProvider.ts index 36efd3e..246fc82 100644 --- a/apps/backend/src/agent/llm/OllamaLocalLlmProvider.ts +++ b/apps/backend/src/agent/llm/OllamaLocalLlmProvider.ts @@ -1,7 +1,16 @@ import type { BaseChatModel } from '@langchain/core/language_models/chat_models'; import { ChatOllama } from '@langchain/ollama'; -import type { ChatModelParams, EmbeddingProvider, LlmProvider, LlmProviderInfo } from '#self/types'; +import type { ChatModelParams, EmbeddingProvider, LlmProvider, LlmProviderInfo } from '@/types'; import { OllamaLocalEmbeddingProvider } from './OllamaLocalEmbeddingProvider.ts'; +import { z } from 'zod'; + +const OllamaApiTagsSchema = z.object({ + models: z.array( + z.object({ + name: z.string(), + }), + ), +}); export class OllamaLocalLlmProvider implements LlmProvider { private readonly baseUrl: string; @@ -35,8 +44,10 @@ export class OllamaLocalLlmProvider implements LlmProvider { if (!res.ok) { throw new Error('[LLM] Ollama Local is not serving'); } - const data = await res.json(); - if (!data.models?.some((m: any) => m.name === this.model)) { + const body = await res.json(); + const data = OllamaApiTagsSchema.parse(body); + + if (!data.models.some((m) => m.name === this.model)) { throw new Error(`[LLM] Model ${this.model} not found in Ollama Local`); } return true; diff --git a/apps/backend/src/agent/llm/OpenAiEmbeddingProvider.ts b/apps/backend/src/agent/llm/OpenAiEmbeddingProvider.ts index a53e465..78bb871 100644 --- a/apps/backend/src/agent/llm/OpenAiEmbeddingProvider.ts +++ b/apps/backend/src/agent/llm/OpenAiEmbeddingProvider.ts @@ -1,4 +1,4 @@ -import type { EmbeddingProvider } from '#self/types'; +import type { EmbeddingProvider } from '@/types'; import { OpenAIEmbeddings } from '@langchain/openai'; export class OpenAiEmbeddingProvider implements EmbeddingProvider { diff --git a/apps/backend/src/agent/llm/OpenAiLlmProvider.ts b/apps/backend/src/agent/llm/OpenAiLlmProvider.ts index 23c17c8..c230a2e 100644 --- a/apps/backend/src/agent/llm/OpenAiLlmProvider.ts +++ b/apps/backend/src/agent/llm/OpenAiLlmProvider.ts @@ -1,6 +1,6 @@ import type { BaseChatModel } from '@langchain/core/language_models/chat_models'; import { ChatOpenAI, OpenAIEmbeddings } from '@langchain/openai'; -import type { ChatModelParams, EmbeddingProvider, LlmProvider, LlmProviderInfo } from '#self/types'; +import type { ChatModelParams, EmbeddingProvider, LlmProvider, LlmProviderInfo } from '@/types'; import { OpenAiEmbeddingProvider } from './OpenAiEmbeddingProvider.ts'; export class OpenAiLlmProvider implements LlmProvider { diff --git a/apps/backend/src/agent/prompts.ts b/apps/backend/src/agent/prompts.ts index 77b0bf1..9d462c2 100644 --- a/apps/backend/src/agent/prompts.ts +++ b/apps/backend/src/agent/prompts.ts @@ -32,7 +32,7 @@ Return only valid JSON: "elements": [ { "dataId": "string", - "selector": "string", + "cssSelector": "string", "text": "string", "action": "click|input|navigate|select|highlight" } @@ -62,7 +62,7 @@ ELEMENTS GENERAL RULES: - If the answer refers to a specific page element or text fragment, include that element in "elements" - If a tool returns a relevant element used for the answer, include it in "elements" - Return an empty "elements" array only when no valid relevant element is available from tool results -- Use dataId and selector exactly as returned by tools +- Map tool elementDataId to dataId and tool elementCssSelector to cssSelector exactly - Do not modify or invent them WORKFLOW ELEMENTS RULES: @@ -72,7 +72,7 @@ WORKFLOW ELEMENTS RULES: - Prefer broader coverage over minimal sufficiency - Exclude only clearly irrelevant, duplicate, or contradictory items - Map each selected step into one item in "elements" -- Preserve dataId and selector exactly as returned +- Map each step elementDataId to dataId and elementCssSelector to cssSelector exactly - Rewrite only the user-facing "text" and choose the appropriate "action" CONTENT ELEMENTS RULES: @@ -142,7 +142,7 @@ Extract valid JSON from the agent answer. TARGET SCHEMA: { "answer": string, - "elements": [{"dataId": string, "selector": string, "text": string, "action": "click|navigate|input|select|highlight"}], + "elements": [{"dataId": string, "cssSelector": string, "text": string, "action": "click|navigate|input|select|highlight"}], "mode": "direct|steps", "topic": string | null } @@ -152,10 +152,10 @@ RULES: - If valid JSON is present, extract it exactly - Do not reconstruct missing fields from prose - Do not create elements from answer text -- Include elements only if full data (dataId and selector) is provided +- Include elements only if full data (dataId and cssSelector) is provided - Omit elements with missing fields - Do not invent values or use placeholders (e.g. "unknown") -- Do not generate selectors or ids from text +- Do not generate CSS selectors or ids from text - Keep empty arrays as empty arrays Question: ${params.question} diff --git a/apps/backend/src/agent/rank/reranker.ts b/apps/backend/src/agent/rank/reranker.ts index 9176032..5779ac5 100644 --- a/apps/backend/src/agent/rank/reranker.ts +++ b/apps/backend/src/agent/rank/reranker.ts @@ -1,4 +1,4 @@ -import type { RerankedDocument, RetrievedDocument } from '#self/types'; +import type { RerankedDocument, RetrievedDocument } from '@/types'; /** * Reranks retrieved documents using a scoring function and returns them in descending score order. diff --git a/apps/backend/src/agent/rank/scoring.ts b/apps/backend/src/agent/rank/scoring.ts index 8131223..a8c3638 100644 --- a/apps/backend/src/agent/rank/scoring.ts +++ b/apps/backend/src/agent/rank/scoring.ts @@ -1,4 +1,4 @@ -import type { RetrievedDocument } from '#self/types'; +import type { RetrievedDocument } from '@/types'; /** * Computes the final score for an element lookup document diff --git a/apps/backend/src/agent/tools/AbstractCallableTool.ts b/apps/backend/src/agent/tools/AbstractCallableTool.ts index 111d6ed..4e45440 100644 --- a/apps/backend/src/agent/tools/AbstractCallableTool.ts +++ b/apps/backend/src/agent/tools/AbstractCallableTool.ts @@ -1,7 +1,7 @@ import type { DynamicStructuredTool } from '@langchain/core/tools'; -import { PageContextProvider } from '#self/indexer'; -import type { CallableTool, CallableToolResult, CallableToolResultData, ToolResultElement } from '#self/types'; -import { formantElementContextPath, type BaseElement } from '@flowforge/page-model'; +import { PageContextProvider } from '@/indexer'; +import type { CallableTool, CallableToolResult, CallableToolResultData, ToolResultElement } from '@/types'; +import { formantElementContextPath, type BaseElement } from '@flowforge/page-trail'; export abstract class AbstractCallableTool implements CallableTool { readonly name: string; @@ -10,15 +10,12 @@ export abstract class AbstractCallableTool implements CallableTool { this.name = name; } - protected abstract callFn( - contextProvider: PageContextProvider, - query: string, - ): Promise; + protected abstract callFn(contextProvider: PageContextProvider, query: string): Promise; abstract createStructuredTool(ctx: PageContextProvider): DynamicStructuredTool; async call(ctx: PageContextProvider, query: string): Promise { - console.log(`[Tool] Call ${this.name} for ${ctx.pageModel.basics.url}: ${query}`); + console.log(`[Tool] Call ${this.name} for ${ctx.pageTrail.basics.url}: ${query}`); try { const resultData = await this.callFn(ctx, query); const result: CallableToolResult = { @@ -27,7 +24,7 @@ export abstract class AbstractCallableTool implements CallableTool { }; return AbstractCallableTool.serialiseResult(result); } catch (error) { - console.error(`[Tool] Error calling ${this.name} for ${ctx.pageModel.basics.url}:`, error); + console.error(`[Tool] Error calling ${this.name} for ${ctx.pageTrail.basics.url}:`, error); const result: CallableToolResult = { success: false, error: error instanceof Error ? error.message : 'Unknown error', @@ -41,7 +38,7 @@ export abstract class AbstractCallableTool implements CallableTool { elementPath: formantElementContextPath(element.context.path), elementSectionName: element.context.sectionName ?? '', elementDataId: element.dataId, - elementSelector: element.selector, + elementCssSelector: element.cssSelector ?? '', }; } diff --git a/apps/backend/src/agent/tools/ToolFindElement.ts b/apps/backend/src/agent/tools/ToolFindElement.ts index 1779b73..2c545bf 100644 --- a/apps/backend/src/agent/tools/ToolFindElement.ts +++ b/apps/backend/src/agent/tools/ToolFindElement.ts @@ -1,8 +1,8 @@ import { DynamicStructuredTool } from '@langchain/core/tools'; import { z } from 'zod'; import { AbstractCallableTool } from './AbstractCallableTool.ts'; -import { PageContextProvider } from '#self/indexer'; -import type { ToolFindElementResultData } from '#self/types'; +import { PageContextProvider } from '@/indexer'; +import type { ToolFindElementResultData } from '@/types'; import { rerankRetrievedDocuments } from '../rank/reranker.ts'; import { scoreForLookup } from '../rank/scoring.ts'; @@ -51,7 +51,7 @@ WHAT IT RETURNS: - Best matching element (if any) - Description of the element - Location context (section, path) -- Selector and dataId +- CSS selector and dataId IMPORTANT: - Returns only the best match, which may be imperfect diff --git a/apps/backend/src/agent/tools/ToolFindWorkflow.ts b/apps/backend/src/agent/tools/ToolFindWorkflow.ts index ef8f179..f3e0bcd 100644 --- a/apps/backend/src/agent/tools/ToolFindWorkflow.ts +++ b/apps/backend/src/agent/tools/ToolFindWorkflow.ts @@ -1,8 +1,8 @@ import { DynamicStructuredTool } from '@langchain/core/tools'; import { z } from 'zod'; import { AbstractCallableTool } from './AbstractCallableTool.ts'; -import { PageContextProvider } from '#self/indexer'; -import type { ToolFindWorkflowResultData } from '#self/types'; +import { PageContextProvider } from '@/indexer'; +import type { ToolFindWorkflowResultData } from '@/types'; import { rerankRetrievedDocuments } from '../rank/reranker.ts'; import { scoreForAction } from '../rank/scoring.ts'; @@ -46,7 +46,7 @@ WHEN TO USE: WHAT IT RETURNS: - A list of relevant interactive elements (candidates) -- Each includes description, location, selector, and dataId +- Each includes description, location, CSS selector, and dataId IMPORTANT: - Results are candidates, not ordered steps diff --git a/apps/backend/src/agent/tools/ToolGetPageSummary.ts b/apps/backend/src/agent/tools/ToolGetPageSummary.ts index 8350718..3db0823 100644 --- a/apps/backend/src/agent/tools/ToolGetPageSummary.ts +++ b/apps/backend/src/agent/tools/ToolGetPageSummary.ts @@ -1,15 +1,9 @@ import { DynamicStructuredTool } from '@langchain/core/tools'; import { z } from 'zod'; import { AbstractCallableTool } from './AbstractCallableTool.ts'; -import { - PageContextProvider, -} from '#self/indexer'; -import type { ToolGetPageSummaryResultData } from '#self/types'; -import { - formatConcatElements, - formatContentElementShort, - formatInteractiveElementShort, -} from '@flowforge/page-model'; +import { PageContextProvider } from '@/indexer'; +import type { ToolGetPageSummaryResultData } from '@/types'; +import { formatSampleHeadings, formatSampleInteractions } from '@flowforge/page-trail'; export class ToolGetPageSummary extends AbstractCallableTool { private readonly elementsHeadingsLimit: number; @@ -21,28 +15,14 @@ export class ToolGetPageSummary extends AbstractCallableTool { this.elementsInteractionsLimit = params.elementsInteractionsLimit; } - override async callFn(ctx: PageContextProvider, query: string): Promise { - // Get top headings - const sampleHeadings = ctx.pageModel.content - .filter((el) => el.type === 'heading') - .sort((a, b) => b.importanceScore - a.importanceScore) - .slice(0, this.elementsHeadingsLimit) - .map((el) => formatContentElementShort(el)); - - // Get top interactions - const sampleInteractions = ctx.pageModel.interactive - .filter((el) => el.labels.length > 0 || el.text) - .sort((a, b) => b.importanceScore - a.importanceScore) - .slice(0, this.elementsInteractionsLimit) - .map((el) => formatInteractiveElementShort(el)); - + override async callFn(ctx: PageContextProvider): Promise { return { - title: ctx.pageModel.basics.title, - url: ctx.pageModel.basics.url, - description: ctx.pageModel.basics.description, - language: ctx.pageModel.basics.language, - sampleHeadings: formatConcatElements(sampleHeadings), - sampleInteractions: formatConcatElements(sampleInteractions), + title: ctx.pageTrail.basics.title, + url: ctx.pageTrail.basics.url, + description: ctx.pageTrail.basics.description, + language: ctx.pageTrail.basics.language, + sampleHeadings: formatSampleHeadings(ctx.pageTrail.content, this.elementsHeadingsLimit), + sampleInteractions: formatSampleInteractions(ctx.pageTrail.interactive, this.elementsInteractionsLimit), }; } diff --git a/apps/backend/src/agent/tools/ToolSearchInContent.ts b/apps/backend/src/agent/tools/ToolSearchInContent.ts index e2b7596..90aa77e 100644 --- a/apps/backend/src/agent/tools/ToolSearchInContent.ts +++ b/apps/backend/src/agent/tools/ToolSearchInContent.ts @@ -1,8 +1,8 @@ import { DynamicStructuredTool } from '@langchain/core/tools'; import { z } from 'zod'; import { AbstractCallableTool } from './AbstractCallableTool.ts'; -import { PageContextProvider } from '#self/indexer'; -import type { ToolSearchInContentResultData } from '#self/types'; +import { PageContextProvider } from '@/indexer'; +import type { ToolSearchInContentResultData } from '@/types'; import { rerankRetrievedDocuments } from '../rank/reranker.ts'; import { scoreForAnswer } from '../rank/scoring.ts'; diff --git a/apps/backend/src/agent/tools/ToolsRegistry.ts b/apps/backend/src/agent/tools/ToolsRegistry.ts index 9ff5fab..c77b965 100644 --- a/apps/backend/src/agent/tools/ToolsRegistry.ts +++ b/apps/backend/src/agent/tools/ToolsRegistry.ts @@ -4,7 +4,7 @@ import { ToolFindElement } from './ToolFindElement.ts'; import { ToolGetPageSummary } from './ToolGetPageSummary.ts'; import { ToolFindWorkflow } from './ToolFindWorkflow.ts'; import { ToolSearchInContent } from './ToolSearchInContent.ts'; -import { PageContextProvider } from '#self/indexer'; +import { PageContextProvider } from '@/indexer'; export class ToolsRegistry { private readonly tools: AbstractCallableTool[]; diff --git a/apps/backend/src/analytics/Analytics.ts b/apps/backend/src/analytics/Analytics.ts index cafb8bc..ca1eaa0 100644 --- a/apps/backend/src/analytics/Analytics.ts +++ b/apps/backend/src/analytics/Analytics.ts @@ -1,4 +1,4 @@ -import type { AgentResponse, AnalyticsData, AnalyticsEvent } from '#self/types'; +import type { AgentResponse, AnalyticsData, AnalyticsEvent } from '@/types'; // TODO: provide a persistence layer export class Analytics { diff --git a/apps/backend/src/indexer/PageContextProvider.ts b/apps/backend/src/indexer/PageContextProvider.ts index 8eba8bb..bcc945a 100644 --- a/apps/backend/src/indexer/PageContextProvider.ts +++ b/apps/backend/src/indexer/PageContextProvider.ts @@ -1,17 +1,17 @@ -import type { DocumentRetriever, RetrievedDocument, RetrieveOptions } from '#self/types'; +import type { DocumentRetriever, RetrievedDocument, RetrieveOptions } from '@/types'; import { PageIndexer } from './PageIndexer.ts'; -import type { PageModel } from '@flowforge/page-model'; +import type { PageTrail } from '@flowforge/page-trail'; export class PageContextProvider implements DocumentRetriever { - readonly pageModel: PageModel; + readonly pageTrail: PageTrail; private readonly indexer: PageIndexer; - constructor(pageModel: PageModel, indexer: PageIndexer) { - this.pageModel = pageModel; + constructor(pageTrail: PageTrail, indexer: PageIndexer) { + this.pageTrail = pageTrail; this.indexer = indexer; } async retrieve(query: string, params: RetrieveOptions = { k: 5 }): Promise { - return this.indexer.searchForPage(this.pageModel, query, params); + return this.indexer.searchForPage(this.pageTrail, query, params); } } diff --git a/apps/backend/src/indexer/PageIndexer.ts b/apps/backend/src/indexer/PageIndexer.ts index 34f48bd..792247b 100644 --- a/apps/backend/src/indexer/PageIndexer.ts +++ b/apps/backend/src/indexer/PageIndexer.ts @@ -5,10 +5,10 @@ import type { EmbeddingProvider, RetrievedDocument, RetrieveOptions, -} from '#self/types'; +} from '@/types'; import { VectorStorageFactory } from './vector/VectorStorageFactory.ts'; import { CompositeDocumentTransformer } from './transformers/CompositeDocumentTransformer.ts'; -import type { PageModel } from '@flowforge/page-model'; +import type { PageTrail } from '@flowforge/page-trail'; interface PageIndexerOptions { embeddingProvider: EmbeddingProvider; @@ -62,27 +62,27 @@ export class PageIndexer { } /** - * Transform documents from a page model and indexes it into the vector store + * Transform documents from a page trail and indexes it into the vector store * - * @param pageModel - Structured page content used for transformation and indexing. - * @param pageModel.basics.url - Canonical page URL used to derive the dataset name. + * @param pageTrail - Structured page content used for transformation and indexing. + * @param pageTrail.basics.url - Canonical page URL used to derive the dataset name. * @returns Promise resolving to the dataset name where documents were indexed. * - * @throws {Error} If no documents are transformed from the page model. + * @throws {Error} If no documents are transformed from the page trail. * @throws {Error} If indexing fails in the vector storage backend. */ - async indexPage(pageModel: PageModel): Promise { - const docs = await this.transformer.transform(pageModel); + async indexPage(pageTrail: PageTrail): Promise { + const docs = await this.transformer.transform(pageTrail); if (docs.length === 0) { throw new Error('Nothing to index'); } - const datasetName = this.createDatasetName(pageModel.basics.url); + const datasetName = this.createDatasetName(pageTrail.basics.url); try { await this.vectorStorage.index(datasetName, docs); - console.log(`[Indexer] Indexed ${datasetName} with ${docs.length} documents for ${pageModel.basics.url}`); + console.log(`[Indexer] Indexed ${datasetName} with ${docs.length} documents for ${pageTrail.basics.url}`); return datasetName; } catch (error) { - console.error(`[Indexer] Error while indexing ${pageModel.basics.url}:`, error); + console.error(`[Indexer] Error while indexing ${pageTrail.basics.url}:`, error); throw error; } } @@ -106,15 +106,15 @@ export class PageIndexer { /** * Searches indexed content for a given page * - * @param pageModel - Structured page content containing the URL to search. + * @param pageTrail - Structured page content containing the URL to search. * @param query - Natural language query text. * @param options - Retrieval options including number of results and optional type filter. * * @throws {Error} If the underlying dataset cannot be found or queried. */ - async searchForPage(pageModel: PageModel, query: string, options: RetrieveOptions): Promise { - const datasetName = this.createDatasetName(pageModel.basics.url); - console.log(`[Indexer] Searching in ${datasetName} for Page with url: ${pageModel.basics.url}`); + async searchForPage(pageTrail: PageTrail, query: string, options: RetrieveOptions): Promise { + const datasetName = this.createDatasetName(pageTrail.basics.url); + console.log(`[Indexer] Searching in ${datasetName} for Page with url: ${pageTrail.basics.url}`); return this.search(datasetName, query, options); } diff --git a/apps/backend/src/indexer/transformers/AbstractDocumentTransformer.ts b/apps/backend/src/indexer/transformers/AbstractDocumentTransformer.ts index 4b1317c..3535a2c 100644 --- a/apps/backend/src/indexer/transformers/AbstractDocumentTransformer.ts +++ b/apps/backend/src/indexer/transformers/AbstractDocumentTransformer.ts @@ -1,6 +1,6 @@ -import type { IndexableDocument, DocumentTransformer, DocumentMetadata, DocumentType } from '#self/types'; +import type { IndexableDocument, DocumentTransformer } from '@/types'; import { randomUUID } from 'crypto'; -import type { BaseElement, PageModel } from '@flowforge/page-model'; +import type { BaseElement, PageTrail } from '@flowforge/page-trail'; export abstract class AbstractDocumentTransformer implements DocumentTransformer { readonly name: string; @@ -9,7 +9,7 @@ export abstract class AbstractDocumentTransformer implements DocumentTransformer this.name = name; } - protected abstract transformFn(pageModel: PageModel): Promise; + protected abstract transformFn(pageTrail: PageTrail): Promise; private createDocumentId() { return randomUUID(); @@ -26,11 +26,11 @@ export abstract class AbstractDocumentTransformer implements DocumentTransformer }; } - async transform(pageModel: PageModel): Promise { + async transform(pageTrail: PageTrail): Promise { try { - return await this.transformFn(pageModel); + return await this.transformFn(pageTrail); } catch (error) { - console.error(`[Indexer] Error transforming via ${this.name} for ${pageModel.basics.url}:`, error); + console.error(`[Indexer] Error transforming via ${this.name} for ${pageTrail.basics.url}:`, error); return []; } } diff --git a/apps/backend/src/indexer/transformers/CompositeDocumentTransformer.ts b/apps/backend/src/indexer/transformers/CompositeDocumentTransformer.ts index 1f8ef17..600fe72 100644 --- a/apps/backend/src/indexer/transformers/CompositeDocumentTransformer.ts +++ b/apps/backend/src/indexer/transformers/CompositeDocumentTransformer.ts @@ -1,8 +1,8 @@ -import type { IndexableDocument, DocumentTransformer } from '#self/types'; +import type { IndexableDocument, DocumentTransformer } from '@/types'; import { ContentElementsTransformer } from './ContentElementsTransformer.ts'; import { InteractiveElementsTransformer } from './InteractiveElementsTransformer.ts'; import { AbstractDocumentTransformer } from './AbstractDocumentTransformer.ts'; -import type { PageModel } from '@flowforge/page-model'; +import type { PageTrail } from '@flowforge/page-trail'; export class CompositeDocumentTransformer implements DocumentTransformer { private readonly transformers: AbstractDocumentTransformer[]; @@ -10,7 +10,10 @@ export class CompositeDocumentTransformer implements DocumentTransformer { constructor(params: { chunkSize: number; chunkOverlapRatio: number; verbose: boolean }) { this.transformers = [ - new ContentElementsTransformer({ chunkSize: params.chunkSize, chunkOverlapRatio: params.chunkOverlapRatio }), + new ContentElementsTransformer({ + chunkSize: params.chunkSize, + chunkOverlapRatio: params.chunkOverlapRatio, + }), new InteractiveElementsTransformer(), ]; this.verbose = params.verbose; @@ -20,13 +23,13 @@ export class CompositeDocumentTransformer implements DocumentTransformer { return this.transformers.map((t) => t.name); } - async transform(pageModel: PageModel): Promise { + async transform(pageTrail: PageTrail): Promise { const docs: IndexableDocument[] = []; for (const t of this.transformers) { - const transformed = await t.transform(pageModel); + const transformed = await t.transform(pageTrail); if (this.verbose) { console.log( - `[Indexer] Transformed ${transformed.length} documents via ${t.name} for ${pageModel.basics.url}`, + `[Indexer] Transformed ${transformed.length} documents via ${t.name} for ${pageTrail.basics.url}`, transformed.map((doc) => doc.content), ); } diff --git a/apps/backend/src/indexer/transformers/ContentElementsTransformer.ts b/apps/backend/src/indexer/transformers/ContentElementsTransformer.ts index 9172f54..b27eb6a 100644 --- a/apps/backend/src/indexer/transformers/ContentElementsTransformer.ts +++ b/apps/backend/src/indexer/transformers/ContentElementsTransformer.ts @@ -1,11 +1,11 @@ -import type { IndexableDocument } from '#self/types'; +import type { IndexableDocument } from '@/types'; import { AbstractDocumentTransformer } from './AbstractDocumentTransformer.ts'; import { type ContentElement, - type PageModel, + type PageTrail, formatContentElement, formatContentElementShort, -} from '@flowforge/page-model'; +} from '@flowforge/page-trail'; import { RecursiveCharacterTextSplitter, TextSplitter } from '@langchain/textsplitters'; export const CONTENT_TEMPLATE_TEXT_PLACEHOLDER = '{{TEXT}}'; @@ -32,10 +32,10 @@ export class ContentElementsTransformer extends AbstractDocumentTransformer { }); } - override async transformFn(pageModel: PageModel): Promise { + override async transformFn(pageTrail: PageTrail): Promise { const docs: IndexableDocument[] = []; - for (const el of pageModel.content) { + for (const el of pageTrail.content) { const contentTemplate = this.createContentTemplate(el); const templatedChunkSize = this.chunkSize - contentTemplate.length + CONTENT_TEMPLATE_TEXT_PLACEHOLDER.length; diff --git a/apps/backend/src/indexer/transformers/InteractiveElementsTransformer.ts b/apps/backend/src/indexer/transformers/InteractiveElementsTransformer.ts index 1a8af60..76241ea 100644 --- a/apps/backend/src/indexer/transformers/InteractiveElementsTransformer.ts +++ b/apps/backend/src/indexer/transformers/InteractiveElementsTransformer.ts @@ -1,15 +1,15 @@ import { AbstractDocumentTransformer } from './AbstractDocumentTransformer.ts'; -import type { IndexableDocument } from '#self/types'; -import { formatInteractiveElement, type PageModel } from '@flowforge/page-model'; +import type { IndexableDocument } from '@/types'; +import { formatInteractiveElement, type PageTrail } from '@flowforge/page-trail'; export class InteractiveElementsTransformer extends AbstractDocumentTransformer { constructor() { super('interactive_elements'); } - override async transformFn(pageModel: PageModel): Promise { + override async transformFn(pageTrail: PageTrail): Promise { const docs: IndexableDocument[] = []; - for (const el of pageModel.interactive) { + for (const el of pageTrail.interactive) { const content = formatInteractiveElement(el); docs.push(this.createDocument(content, el)); } diff --git a/apps/backend/src/indexer/vector/LanceVectorStorage.ts b/apps/backend/src/indexer/vector/LanceVectorStorage.ts index ad41a77..c6c5208 100644 --- a/apps/backend/src/indexer/vector/LanceVectorStorage.ts +++ b/apps/backend/src/indexer/vector/LanceVectorStorage.ts @@ -7,7 +7,7 @@ import type { IndexableDocument, RetrievedDocument, RetrieveOptions, -} from '#self/types'; +} from '@/types'; const DEFAULT_DB_PATH = './data/lancedb'; diff --git a/apps/backend/src/indexer/vector/VectorStorageFactory.ts b/apps/backend/src/indexer/vector/VectorStorageFactory.ts index b62a845..6972657 100644 --- a/apps/backend/src/indexer/vector/VectorStorageFactory.ts +++ b/apps/backend/src/indexer/vector/VectorStorageFactory.ts @@ -1,4 +1,4 @@ -import type { DocumentStorage, EmbeddingProvider } from '#self/types'; +import type { DocumentStorage, EmbeddingProvider } from '@/types'; import { LanceVectorStorage } from './LanceVectorStorage.ts'; /** diff --git a/apps/backend/src/server.ts b/apps/backend/src/server.ts index 3115976..cc07a0f 100644 --- a/apps/backend/src/server.ts +++ b/apps/backend/src/server.ts @@ -1,5 +1,5 @@ -import { loadAppConfig } from '#self/config'; -import { Server } from '#self/server'; +import { loadAppConfig } from '@/config'; +import { Server } from '@/server'; const server = new Server(loadAppConfig()); diff --git a/apps/backend/src/server/Server.ts b/apps/backend/src/server/Server.ts index fa29c76..27c676e 100644 --- a/apps/backend/src/server/Server.ts +++ b/apps/backend/src/server/Server.ts @@ -3,11 +3,11 @@ import type { Express } from 'express'; import cors from 'cors'; import boxen from 'boxen'; -import type { LlmProvider } from '#self/types'; -import { PageIndexer } from '#self/indexer'; -import { LlmProviderFactory, WebNavigationAgent } from '#self/agent'; -import type { AppConfig } from '#self/config'; -import { Analytics } from '#self/analytics'; +import type { LlmProvider } from '@/types'; +import { PageIndexer } from '@/indexer'; +import { LlmProviderFactory, WebNavigationAgent } from '@/agent'; +import type { AppConfig } from '@/config'; +import { Analytics } from '@/analytics'; import { createHealthHandler } from './routes/health.ts'; import { createQueryHandler } from './routes/query.ts'; @@ -103,10 +103,10 @@ export class Server { console.log( boxen( [ - `FlowForge – Backend ${this.config.pkg.version}`, + `FlowForge – backend ${this.config.pkg.version}`, ' ─── ', `Server: running on port ${this.config.port}`, - `LLM Provider: ${LlmProviderInfo.provider}`, + `LLM provider: ${LlmProviderInfo.provider}`, `- Model: ${LlmProviderInfo.model}`, `- Embedding: ${LlmProviderInfo.embedding}`, LlmProviderInfo.details ? `- Details: ${LlmProviderInfo.details}` : '', diff --git a/apps/backend/src/server/routes/analytics.ts b/apps/backend/src/server/routes/analytics.ts index 71236c8..cea49f1 100644 --- a/apps/backend/src/server/routes/analytics.ts +++ b/apps/backend/src/server/routes/analytics.ts @@ -1,12 +1,12 @@ import type { Request, Response } from 'express'; -import type { AnalyticsResponse } from '#self/types'; -import { Analytics } from '#self/analytics'; +import type { AnalyticsResponse } from '@/types'; +import { Analytics } from '@/analytics'; interface AnalyticsHandlerDeps { analytics: Analytics; } -export function createAnalyticsHandler({ analytics }: AnalyticsHandlerDeps) { +export function createAnalyticsHandler({ analytics }: AnalyticsHandlerDeps) { return function handleAnalytics(_req: Request, res: Response): void { res.json({ data: Object.fromEntries( diff --git a/apps/backend/src/server/routes/health.ts b/apps/backend/src/server/routes/health.ts index b20d4fa..979d9b9 100644 --- a/apps/backend/src/server/routes/health.ts +++ b/apps/backend/src/server/routes/health.ts @@ -1,5 +1,5 @@ import type { Request, Response } from 'express'; -import type { HealthResponse } from '#self/types'; +import type { HealthResponse } from '@/types'; export function createHealthHandler() { return function handleHealth(_req: Request, res: Response): void { diff --git a/apps/backend/src/server/routes/query.ts b/apps/backend/src/server/routes/query.ts index 18110cc..12ba468 100644 --- a/apps/backend/src/server/routes/query.ts +++ b/apps/backend/src/server/routes/query.ts @@ -1,9 +1,9 @@ import type { Request, Response } from 'express'; import type { QueryRequest, QueryResponse } from '@flowforge/contract'; -import type { ErrorResponse } from '#self/types'; -import { PageContextProvider, PageIndexer } from '#self/indexer'; -import { WebNavigationAgent } from '#self/agent'; -import { Analytics } from '#self/analytics'; +import type { ErrorResponse } from '@/types'; +import { PageContextProvider, PageIndexer } from '@/indexer'; +import { WebNavigationAgent } from '@/agent'; +import { Analytics } from '@/analytics'; interface QueryHandlerDeps { indexer: PageIndexer; @@ -13,24 +13,24 @@ interface QueryHandlerDeps { export function createQueryHandler({ indexer, agent, analytics }: QueryHandlerDeps) { return async function handleQuery( - req: Request<{}, QueryResponse | ErrorResponse, QueryRequest>, + req: Request, QueryResponse | ErrorResponse, QueryRequest>, res: Response, ): Promise { try { - const { question, pageModel, domain } = req.body; + const { question, pageTrail, domain } = req.body; - if (!question || !pageModel) { + if (!question || !pageTrail) { res.status(400).json({ - error: 'Missing required fields: question, pageModel', + error: 'Missing required fields: question, pageTrail', }); return; } console.log(`[Server] Query: ${domain} / ${question}`); - await indexer.indexPage(pageModel); - const pageContext = new PageContextProvider(pageModel, indexer); + await indexer.indexPage(pageTrail); + const pageContext = new PageContextProvider(pageTrail, indexer); const agentResponse = await agent.processQuery(question, pageContext); - analytics.trackQA(domain, pageModel.basics.url, question, agentResponse); + analytics.trackQA(domain, pageTrail.basics.url, question, agentResponse); res.json({ result: agentResponse.result, diff --git a/apps/backend/src/server/routes/search.ts b/apps/backend/src/server/routes/search.ts index 090d20a..501e13c 100644 --- a/apps/backend/src/server/routes/search.ts +++ b/apps/backend/src/server/routes/search.ts @@ -1,6 +1,6 @@ import type { Request, Response } from 'express'; -import type { ErrorResponse, RetrievedDocument, SearchRequest, SearchResponse } from '#self/types'; -import { PageIndexer } from '#self/indexer'; +import type { ErrorResponse, RetrievedDocument, SearchRequest, SearchResponse } from '@/types'; +import { PageIndexer } from '@/indexer'; interface SearchHandlerDeps { indexer: PageIndexer; @@ -8,7 +8,7 @@ interface SearchHandlerDeps { export function createSearchHandler({ indexer }: SearchHandlerDeps) { return async function handleSearch( - req: Request<{}, SearchResponse, SearchRequest>, + req: Request, SearchResponse, SearchRequest>, res: Response | ErrorResponse>, ): Promise { try { @@ -31,5 +31,5 @@ export function createSearchHandler({ indexer }: SearchHandlerDeps) { message: error instanceof Error ? error.message : 'Unknown error', }); } - } + }; } diff --git a/apps/backend/src/types/documents.ts b/apps/backend/src/types/documents.ts index ba48ddc..de90502 100644 --- a/apps/backend/src/types/documents.ts +++ b/apps/backend/src/types/documents.ts @@ -1,4 +1,4 @@ -import type { BaseElement, ElementKind, PageModel } from '@flowforge/page-model'; +import type { BaseElement, ElementKind, PageTrail } from '@flowforge/page-trail'; export type DocumentType = ElementKind; @@ -25,7 +25,7 @@ export interface RerankedDocument extends RetrievedDocument { } export interface DocumentTransformer { - transform(pageModel: PageModel): Promise; + transform(pageTrail: PageTrail): Promise; } export interface RetrieveOptions { diff --git a/apps/backend/src/types/llm.ts b/apps/backend/src/types/llm.ts index f8b8b25..f2ea0bd 100644 --- a/apps/backend/src/types/llm.ts +++ b/apps/backend/src/types/llm.ts @@ -1,5 +1,5 @@ import type { BaseChatModel } from '@langchain/core/language_models/chat_models'; -import type { LlmProviderType } from '#self/config'; +import type { LlmProviderType } from '@/config'; export interface ChatModelParams { temperature: number; diff --git a/apps/backend/src/types/tools.ts b/apps/backend/src/types/tools.ts index aeb2fa5..143317b 100644 --- a/apps/backend/src/types/tools.ts +++ b/apps/backend/src/types/tools.ts @@ -1,4 +1,4 @@ -import { PageContextProvider } from '#self/indexer'; +import { PageContextProvider } from '@/indexer'; export interface CallableTool { call(ctx: PageContextProvider, query: string): Promise; @@ -36,7 +36,7 @@ export interface ToolResultElement { elementPath: string; elementSectionName: string; elementDataId: string; - elementSelector: string; + elementCssSelector: string; } export interface ToolFindElementFoundResultData extends ToolResultElement { diff --git a/apps/backend/tsconfig.json b/apps/backend/tsconfig.json index 6cd915b..ce936f8 100644 --- a/apps/backend/tsconfig.json +++ b/apps/backend/tsconfig.json @@ -1,37 +1,28 @@ { + "extends": ["@tsconfig/node22/tsconfig.json"], "compilerOptions": { - "target": "esnext", - "module": "nodenext", - "moduleResolution": "nodenext", "types": ["node"], - "moduleDetection": "force", - "strict": true, - "esModuleInterop": true, - "isolatedModules": true, - "skipLibCheck": true, - "forceConsistentCasingInFileNames": true, - "rewriteRelativeImportExtensions": true, - "verbatimModuleSyntax": true, + "resolveJsonModule": true, - "declaration": true, - "declarationMap": true, - "sourceMap": true, + "noImplicitOverride": true, "erasableSyntaxOnly": true, + "verbatimModuleSyntax": true, "noUncheckedIndexedAccess": true, - "noUncheckedSideEffectImports": true, - "noImplicitOverride": true, - "useDefineForClassFields": true, - "rootDir": "./src", - "outDir": "./dist", + "rewriteRelativeImportExtensions": true, + + "sourceMap": true, + "paths": { - "#self/config": ["./src/config.ts"], - "#self/types": ["./src/types/index.ts"], - "#self/agent": ["./src/agent/index.ts"], - "#self/indexer": ["./src/indexer/index.ts"], - "#self/analytics": ["./src/analytics/index.ts"], - "#self/server": ["./src/server/index.ts"] - } + "@/config": ["./src/config.ts"], + "@/types": ["./src/types/index.ts"], + "@/agent": ["./src/agent/index.ts"], + "@/indexer": ["./src/indexer/index.ts"], + "@/analytics": ["./src/analytics/index.ts"], + "@/server": ["./src/server/index.ts"] + }, + "rootDir": "./src", + "outDir": "./dist" }, "include": ["src/**/*"], - "exclude": ["node_modules", "dist", "**/*.test.ts"] + "exclude": ["**/*.test.ts"] } diff --git a/apps/extension/README.md b/apps/extension/README.md index 9108924..f430d21 100644 --- a/apps/extension/README.md +++ b/apps/extension/README.md @@ -2,37 +2,30 @@ ## Purpose -Chrome extension that handles UI interaction, page data extraction, and visual guidance (highlighting and workflows) based on backend responses. +Chrome extension and embeddable browser runtime that handle UI interaction, page data extraction, and visual guidance based on backend responses. ## Responsibilities -- Extract page structure (content, interactive elements, navigation) +- Extract page structure (`PageTrail`) - Send user queries and page context to backend -- Render answers in popup UI -- Highlight elements and guide workflows on the page -- Handle messaging between popup, page runtime (content scripts), and backend +- Render answers, highlights, wizard steps, and Inspector UI +- Route messages between popup, page runtime, and backend +- Store settings and per-domain question history locally ## Run ```bash npm i npm run build -``` - -Development mode with auto-rebuild: - -```bash npm run dev +npm run sandbox ``` -## Build Options - -The extension has two build modes: - -- **Chrome Extension** (`build:chrome`) — Full browser extension for Chrome -- **Embed Integration** (`build:embed`) — Standalone runtime for embedding on any website +## Builds -Both builds are created by default with `npm run build`, or can be built individually. +- `npm run build:chrome` — Chrome extension +- `npm run build:embed` — embeddable runtime +- `npm run build` — both builds ## Load in Chrome @@ -41,31 +34,32 @@ Both builds are created by default with `npm run build`, or can be built individ 3. Click **Load unpacked** 4. Select `apps/extension/dist/chrome` -## Sandbox (Testing Environment) +Sandbox opens `http://localhost:3007` with demo mode and backend mode. -Test the embed integration locally: +## Embed runtime -```bash -npm run sandbox -``` +`build:embed` creates a bundle and declaration file under `dist/embed`: -Opens test environment at `http://localhost:3007` with: -- Live embed integration demo -- Demo mode with predefined Q&A (no backend required) -- Full backend integration mode (requires backend on http://localhost:3477) +- `Runtime.start()` — uses the local backend +- `Runtime.demo()` — uses predefined demo Q&A responses +- `openPopup()`, `closePopup()`, `openPageInspector()`, `destroy()` on runtime instances + +Initial settings can be passed to `Runtime.start()` or `Runtime.demo()`: + +```ts +await FlowForge.start({ settings: { theme: 'dark' } }); +``` -## Key Parts +## Key parts - `popup/` — user interface and interaction logic -- `page/` — DOM extraction and element highlighting -- `background/` — message routing and background services -- `chrome/` — Chrome extension specific files -- `embed/` — standalone runtime for embedding on any website +- `page/` — page overlay, highlighting, wizard, inspector, and collection hooks +- `background/`, `chrome/`, `embed/` — worker, extension shell, and embed runtime +- `core/` / `adapters/` — API, storage, locator, root injection, and transport ## Notes -- Chrome extension requires backend running on http://localhost:3477 -- Embed integration supports both backend mode and Demo mode (predefined responses) -- Works on any website (`` permission) +- Chrome extension requires backend on http://localhost:3477 +- Embed integration supports backend mode and demo mode - Limited by browser security (iframes, cross-origin content) - See [Architecture](../../docs/ARCHITECTURE.md) for system design diff --git a/apps/extension/eslint.config.mjs b/apps/extension/eslint.config.mjs new file mode 100644 index 0000000..a388ae6 --- /dev/null +++ b/apps/extension/eslint.config.mjs @@ -0,0 +1,41 @@ +import js from '@eslint/js'; +import globals from 'globals'; +import tseslint from 'typescript-eslint'; + +export default tseslint.config( + { + ignores: ['dist/**', 'sandbox/**'], + }, + js.configs.recommended, + ...tseslint.configs.recommended, + { + files: ['src/**/*.{ts,tsx}'], + languageOptions: { + globals: { + ...globals.browser, + ...globals.es2022, + chrome: 'readonly', + }, + parserOptions: { + ecmaFeatures: { + jsx: true, + }, + }, + }, + rules: { + 'no-undef': 'off', + }, + }, + { + files: ['scripts/**/*.{js,ts}', 'vite.config.*.ts'], + languageOptions: { + globals: { + ...globals.node, + ...globals.es2022, + }, + }, + rules: { + 'no-undef': 'off', + }, + }, +); diff --git a/apps/extension/package.json b/apps/extension/package.json index 17bc16c..816f3ba 100644 --- a/apps/extension/package.json +++ b/apps/extension/package.json @@ -17,6 +17,8 @@ "install:chrome": "node scripts/install-chrome.js", "dev:chrome": "vite build --config vite.config.chrome.ts --watch", "dev:embed": "vite build --config vite.config.embed.ts --watch", + "lint": "eslint .", + "lint:fix": "eslint . --fix", "dev": "concurrently \"npm run dev:chrome\" \"npm run dev:embed\"", "sandbox": "node sandbox/server.js" }, @@ -31,18 +33,24 @@ "user-experience" ], "dependencies": { - "@flowforge/page-model": "*", - "@flowforge/contract": "*" + "@flowforge/contract": "*", + "@flowforge/page-trail": "*", + "lucide-preact": "^1.31.0" }, "devDependencies": { "@crxjs/vite-plugin": "^2.4.0", + "@eslint/js": "^10.0.1", "@preact/preset-vite": "^2.10.5", + "@tsconfig/vite-react": "^8.1.1", "@types/chrome": "^0.1.38", - "concurrently": "^9.2.1", + "concurrently": "^10.0.4", + "eslint": "^10.8.1", "express": "^5.2.1", + "globals": "^17.9.0", "marked": "^18.0.2", "preact": "^10.29.1", "typescript": "^6.0.2", + "typescript-eslint": "^8.66.0", "vite": "^8.0.8" } } diff --git a/apps/extension/sandbox/index.html b/apps/extension/sandbox/index.html index 1d30e25..844268b 100644 --- a/apps/extension/sandbox/index.html +++ b/apps/extension/sandbox/index.html @@ -1,202 +1,254 @@ - + - - - - FlowForge — Extension Sandbox - - - - -

-
-

Explore Embed

-

Manage the Runtime on the page

-
    -
  • Click Start to launch the extension with a real backend,
  • -
  • Click Demo to run a simulated version with predefined questions and responses,
  • -
  • Click Destroy to completely remove it from the page and clean up all resources.
  • -
-
- - - + section pre { + background: var(--sandbox-color-text); + color: var(--sandbox-color-text-on-dark); + padding: 16px; + border-radius: 8px; + overflow: auto; + } + section pre code { + background: transparent; + color: inherit; + padding: 0; + border-radius: 0; + } + section :not(pre) > code { + background: var(--sandbox-color-hover); + padding: 2px 6px; + border-radius: 4px; + } + div[role='toolbar'] { + display: flex; + gap: 8px; + } + button { + padding: 4px 12px; + cursor: pointer; + } + button:disabled { + opacity: 0.5; + cursor: not-allowed; + } + button:not(:disabled):hover { + background-color: var(--sandbox-color-hover); + } + + + +
+
+

Explore Embed

+

Manage the Runtime on the page

+
    +
  • Click Start to launch the extension with a real backend,
  • +
  • + Click Demo to run a simulated version with predefined questions and responses, +
  • +
  • + Click Destroy to completely remove it from the page and clean up all resources, +
  • +
  • + Click Inspector to open page inspector directly to see how the extension + understands the page context. +
  • +
+
+ + + + +
+

+ See Embed extension docs for more + details. +

+
+
%SECTION_README%
+
+

+ If anything, you may email + flowforge@gmail.com +

+
-

See Embed extension docs for more details.

-
-
%SECTION_README%
-
-

If anything, you may email flowforge@gmail.com

-
-
- - - + })(); + + + diff --git a/apps/extension/src/adapters/chrome/ChromeTransportService.ts b/apps/extension/src/adapters/chrome/ChromeTransportService.ts index 02c4f47..f406d9b 100644 --- a/apps/extension/src/adapters/chrome/ChromeTransportService.ts +++ b/apps/extension/src/adapters/chrome/ChromeTransportService.ts @@ -1,4 +1,4 @@ -import type { Message, MessageResponse } from '#self/types'; +import type { Message, MessageResponse } from '@/types'; import type { TransportService } from '../interface'; /** @@ -49,19 +49,19 @@ export class ChromeTransportService implements TransportService { _sender: chrome.runtime.MessageSender, sendResponse: (response: MessageResponse) => void, ): boolean => { - Promise.resolve(handler(message)) - .then((response) => { - if (response !== undefined) { - sendResponse(response); - } - }) + const response = handler(message); + + if (response === undefined) { + return false; + } + Promise.resolve(response) + .then(sendResponse) .catch((reason: unknown) => { sendResponse({ success: false, error: reason instanceof Error ? reason.message : String(reason), }); }); - return true; }; diff --git a/apps/extension/src/adapters/embed/EmbedTransportService.ts b/apps/extension/src/adapters/embed/EmbedTransportService.ts index 01c83ab..d86cd57 100644 --- a/apps/extension/src/adapters/embed/EmbedTransportService.ts +++ b/apps/extension/src/adapters/embed/EmbedTransportService.ts @@ -1,4 +1,4 @@ -import type { Message, MessageResponse } from '#self/types'; +import type { Message, MessageResponse } from '@/types'; import type { TransportService } from '../interface'; export class EmbedTransportService implements TransportService { @@ -10,7 +10,7 @@ export class EmbedTransportService implements TransportService { return 1; // not applicable } - async getSenderHostname(_senderId: number): Promise { + async getSenderHostname(): Promise { return window.location.hostname; } diff --git a/apps/extension/src/adapters/interface.ts b/apps/extension/src/adapters/interface.ts index 74fc924..bfda16d 100644 --- a/apps/extension/src/adapters/interface.ts +++ b/apps/extension/src/adapters/interface.ts @@ -1,4 +1,4 @@ -import type { Message, MessageResponse } from '#self/types'; +import type { Message, MessageResponse } from '@/types'; export interface LocalStorage { get(key: string): Promise; diff --git a/apps/extension/src/background/BackgroundWorker.ts b/apps/extension/src/background/BackgroundWorker.ts index 953d233..fe58cf6 100644 --- a/apps/extension/src/background/BackgroundWorker.ts +++ b/apps/extension/src/background/BackgroundWorker.ts @@ -1,30 +1,34 @@ -import type { TransportService } from '#self/adapters/interface'; -import type { ApiClient } from '#self/core/services/ApiClient'; -import { HistoryStorage } from '#self/core/services/HistoryStorage'; +import type { TransportService } from '@/adapters/interface'; +import type { ApiClient } from '@/core/services/ApiClient'; +import { HistoryStorage } from '@/core/services/HistoryStorage'; import { type ApplySettingsMessage, type AskQuestionMessage, type AskQuestionMessageResponse, type ClearPageMessage, - type CollectPageModelMessage, - type CollectPageModelMessageResponse, + type CollectPageTrailMessage, + type CollectPageTrailMessageResponse, type GetPrevQuestionsMessage, type GetPrevQuestionsMessageResponse, - type GetSettingsMessage, type GetSettingsMessageResponse, type HighlightElementMessage, isGetSettingsMessage, + isOpenPageInspectorMessage, + isPopupInitializeMessage, isUpdateSettingsMessage, type Message, type MessageResponse, type NavigateToElementMessage, + type OpenInspectorMessage, + type OpenPageInspectorMessage, + type PopupInitializeMessage, type StartOnboardingMessage, type UpdateSettingsMessage, type UpdateSettingsMessageResponse, -} from '#self/types'; -import { isAskQuestionMessage, isGetPrevQuestionsMessage, isNavigateToElementMessage } from '#self/types'; +} from '@/types'; +import { isAskQuestionMessage, isGetPrevQuestionsMessage, isNavigateToElementMessage } from '@/types'; import type { QueryRequest } from '@flowforge/contract'; -import type { SettingsStorage } from '#self/core/services/SettignsStorage'; +import type { SettingsStorage } from '@/core/services/SettingsStorage'; export class BackgroundWorker { private readonly transport: TransportService; @@ -47,8 +51,11 @@ export class BackgroundWorker { start(): void { this.unsubscribe = this.transport.addMessageListener((message: Message) => { + if (isPopupInitializeMessage(message)) { + return this.handlePopupInitialize(message); + } if (isGetSettingsMessage(message)) { - return this.handleGetSettings(message); + return this.handleGetSettings(); } if (isUpdateSettingsMessage(message)) { return this.handleUpdateSettings(message); @@ -62,6 +69,9 @@ export class BackgroundWorker { if (isNavigateToElementMessage(message)) { return this.handleNavigateToElement(message); } + if (isOpenPageInspectorMessage(message)) { + return this.handleOpenPageInspector(message); + } }); console.log('[FlowForge] Background worker loaded and started'); } @@ -71,14 +81,31 @@ export class BackgroundWorker { console.log('[FlowForge] Background worker stopped'); } + /** + * Clears page UI state when the popup opens. + * + * This intentionally does not await the page response. Opening the popup must + * not depend on content script availability or page runtime health. + * + * @param message - Popup initialization message containing the target sender ID. + */ + private handlePopupInitialize(message: PopupInitializeMessage): MessageResponse { + void this.transport + .sendToPage(message.senderId, { + type: 'CLEAR_PAGE', + }) + .catch(() => undefined); + + return { success: true }; + } + /** * Retrieves extension settings from storage * - * @param message - Incoming background message * @returns A successful response containing the stored settings. * @throws Rethrows any error that occurs while reading settings storage. */ - private async handleGetSettings(message: GetSettingsMessage): Promise { + private async handleGetSettings(): Promise { try { const settings = await this.settingsStorage.get(); return { success: true, data: settings }; @@ -113,38 +140,38 @@ export class BackgroundWorker { /** * Handles a user question for the given tab. * - * Clears existing highlights, collects a page model from the page, + * Clears existing highlights, collects a page trail from the page, * sends the query to the backend, highlights matched elements, and stores * the question in domain-specific history. * * @param message - Message containing the tab ID and user question. * @returns Promise resolving to a successful response with the query result. - * @throws Rethrows any error that occurs during page model collection, + * @throws Rethrows any error that occurs during page trail collection, * backend querying, highlighting, or history persistence. */ private async handleAskQuestion(message: AskQuestionMessage): Promise { try { - // Clear existing highlights + // Clear page await this.transport.sendToPage(message.senderId, { type: 'CLEAR_PAGE', }); - // Get a page model from page runtime - const pageModelResponse = await this.transport.sendToPage< - CollectPageModelMessage, - CollectPageModelMessageResponse - >(message.senderId, { type: 'COLLECT_PAGE_MODEL' }); - if (!pageModelResponse.success) { - throw new Error('Failed to collect page model: ' + pageModelResponse.error); + // Get a page trail from page runtime + const pageTrailResponse = await this.transport.sendToPage< + CollectPageTrailMessage, + CollectPageTrailMessageResponse + >(message.senderId, { type: 'COLLECT_PAGE_TRAIL' }); + if (!pageTrailResponse.success) { + throw new Error('Failed to collect page trail: ' + pageTrailResponse.error); } - console.log('[Background] Collected page model:', pageModelResponse); + console.log('[Background] Collected page trail:', pageTrailResponse); - const pageModel = pageModelResponse.data; + const pageTrail = pageTrailResponse.data; const domain = await this.transport.getSenderHostname(message.senderId); // Send it to the backend server const requestData: QueryRequest = { question: message.data.question, - pageModel: pageModel, + pageTrail: pageTrail, domain, userContext: { previousQuestions: await this.historyStorage.getPreviousQuestions(domain), @@ -205,7 +232,7 @@ export class BackgroundWorker { */ private async handleNavigateToElement(message: NavigateToElementMessage): Promise { try { - // Clear existing highlights + // Clear page await this.transport.sendToPage(message.senderId, { type: 'CLEAR_PAGE', }); @@ -220,4 +247,27 @@ export class BackgroundWorker { throw error; } } + + /** + * Clears the page state and opens the inspector. + * + * @param message - Message with the sender page ID. + * @returns Success response when the inspector request is sent. + */ + private async handleOpenPageInspector(message: OpenPageInspectorMessage): Promise { + try { + // Clear page + await this.transport.sendToPage(message.senderId, { + type: 'CLEAR_PAGE', + }); + // Open inspector + await this.transport.sendToPage(message.senderId, { + type: 'OPEN_INSPECTOR', + }); + return { success: true }; + } catch (error) { + console.error('[Background] Error opening page inspector:', error); + throw error; + } + } } diff --git a/apps/extension/src/chrome/action/popup/index.html b/apps/extension/src/chrome/action/popup/index.html index f7d314e..4af8de4 100644 --- a/apps/extension/src/chrome/action/popup/index.html +++ b/apps/extension/src/chrome/action/popup/index.html @@ -1,19 +1,19 @@ - - - - FlowForge Extension - - - - - - + + + + FlowForge extension + + + + + + diff --git a/apps/extension/src/chrome/action/popup/popup.tsx b/apps/extension/src/chrome/action/popup/popup.tsx index fb50c27..215fdf5 100644 --- a/apps/extension/src/chrome/action/popup/popup.tsx +++ b/apps/extension/src/chrome/action/popup/popup.tsx @@ -1,21 +1,27 @@ import { render } from 'preact'; -import '#self/popup/popup.css'; +import '@/popup/popup.css'; -import { PopupApp } from '#self/popup/PopupApp'; -import { ChromeTransportService } from '#self/adapters/chrome/ChromeTransportService'; -import { DocumentRootInjector } from '#self/core/services/RootInjector'; -import { chromeConstants } from '#self/chrome/constants'; -import { useSettings } from '#self/shared/hooks/useSettings'; -import { Main } from '#self/shared/components/Main'; -import type { TransportService } from '#self/adapters/interface'; +import { PopupApp } from '@/popup/PopupApp'; +import { ChromeTransportService } from '@/adapters/chrome/ChromeTransportService'; +import { DocumentRootInjector } from '@/core/services/RootInjector'; +import { chromeConstants } from '@/chrome/constants'; +import { useSettings } from '@/shared/hooks/useSettings'; +import { Main } from '@/shared/components/Main'; +import type { TransportService } from '@/adapters/interface'; function PopupAppRoot({ transport }: { transport: TransportService }) { const { theme, toggleTheme } = useSettings({ transport }); return (
- + window.close()} + />
); } @@ -28,7 +34,7 @@ function PopupAppRoot({ transport }: { transport: TransportService }) { const root = rootInjector.inject(document, chromeConstants.POPUP_ROOT_ID); render(, root.mountPoint); console.log('[FlowForge] Popup loaded'); - } + }; if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', doMount); diff --git a/apps/extension/src/chrome/background/worker.ts b/apps/extension/src/chrome/background/worker.ts index 522bb21..ba05d5c 100644 --- a/apps/extension/src/chrome/background/worker.ts +++ b/apps/extension/src/chrome/background/worker.ts @@ -1,10 +1,10 @@ -import { config } from '#self/config'; -import { HttpApiClient } from '#self/core/services/ApiClient'; -import { HistoryStorage } from '#self/core/services/HistoryStorage'; -import { ChromeTransportService } from '#self/adapters/chrome/ChromeTransportService'; -import { ChromeLocalStorage } from '#self/adapters/chrome/ChromeLocalStorage'; -import { BackgroundWorker } from '#self/background/BackgroundWorker'; -import { SettingsStorage } from '#self/core/services/SettignsStorage'; +import { config } from '@/config'; +import { HttpApiClient } from '@/core/services/ApiClient'; +import { HistoryStorage } from '@/core/services/HistoryStorage'; +import { ChromeTransportService } from '@/adapters/chrome/ChromeTransportService'; +import { ChromeLocalStorage } from '@/adapters/chrome/ChromeLocalStorage'; +import { BackgroundWorker } from '@/background/BackgroundWorker'; +import { SettingsStorage } from '@/core/services/SettingsStorage'; (function main() { const transport = new ChromeTransportService(); diff --git a/apps/extension/src/chrome/contentScripts/page.tsx b/apps/extension/src/chrome/contentScripts/page.tsx index cfc8910..d39ff3b 100644 --- a/apps/extension/src/chrome/contentScripts/page.tsx +++ b/apps/extension/src/chrome/contentScripts/page.tsx @@ -1,14 +1,14 @@ import { render } from 'preact'; -import styles from '#self/page/page.css?inline'; +import styles from '@/page/page.css?inline'; -import { PageApp } from '#self/page/PageApp'; -import { ChromeTransportService } from '#self/adapters/chrome/ChromeTransportService'; -import { ShadowRootInjector } from '#self/core/services/RootInjector'; -import { chromeConstants } from '#self/chrome/constants'; -import { Main } from '#self/shared/components/Main'; -import { useSettings } from '#self/shared/hooks/useSettings'; -import type { TransportService } from '#self/adapters/interface'; +import { PageApp } from '@/page/PageApp'; +import { ChromeTransportService } from '@/adapters/chrome/ChromeTransportService'; +import { ShadowRootInjector } from '@/core/services/RootInjector'; +import { chromeConstants } from '@/chrome/constants'; +import { Main } from '@/shared/components/Main'; +import { useSettings } from '@/shared/hooks/useSettings'; +import type { TransportService } from '@/adapters/interface'; function PageAppRoot({ transport }: { transport: TransportService }) { const { theme } = useSettings({ transport }); diff --git a/apps/extension/src/chrome/manifest.config.ts b/apps/extension/src/chrome/manifest.config.ts index b133412..61e6397 100644 --- a/apps/extension/src/chrome/manifest.config.ts +++ b/apps/extension/src/chrome/manifest.config.ts @@ -1,5 +1,5 @@ import { defineManifest } from '@crxjs/vite-plugin'; -import pkg from '../../package.json'; +import pkg from '../../package.json' with { type: 'json' }; export default defineManifest({ manifest_version: 3, diff --git a/apps/extension/src/config.ts b/apps/extension/src/config.ts index a3ddace..035647a 100644 --- a/apps/extension/src/config.ts +++ b/apps/extension/src/config.ts @@ -1,5 +1,5 @@ -import pkg from '../package.json'; -import type { ExtensionSettings } from '#self/types'; +import pkg from '../package.json' with { type: 'json' }; +import type { ExtensionSettings } from '@/types'; export interface ExtensionConfig { // The base URL of the backend server. diff --git a/apps/extension/src/core/locator/locate.ts b/apps/extension/src/core/locator/locate.ts index 349935f..f49e029 100644 --- a/apps/extension/src/core/locator/locate.ts +++ b/apps/extension/src/core/locator/locate.ts @@ -1,4 +1,4 @@ -import { constants } from '#self/constants'; +import { constants } from '@/constants'; import { generateDataId } from '@flowforge/contract'; /** @@ -24,22 +24,22 @@ export function getOrCreateDataId(el: Element): string { * * @param doc - The document to search within. * @param dataId - The value of the data id attribute to look up. - * @param fallbackSelector - Optional CSS selector to use if the data id lookup fails. + * @param cssSelector - Optional CSS selector to use if the data id lookup fails. * @returns The matching element, or `null` if not found. */ -export function findElement(doc: Document, dataId: string, fallbackSelector?: string): Element | null { +export function findElement(doc: Document, dataId: string, cssSelector?: string): Element | null { let el: Element | null = null; try { if (dataId) { el = doc.querySelector(`[${constants.DATA_ID_ATTRIBUTE}="${dataId}"]`); } - if (!el && fallbackSelector) { - el = doc.querySelector(fallbackSelector); + if (!el && cssSelector) { + el = doc.querySelector(cssSelector); } return el; } finally { if (!el) { - console.warn('[FlowForge] Element not found:', { dataId, fallbackSelector }); + console.warn('[FlowForge] Element not found:', { dataId, fallbackSelector: cssSelector }); } } } diff --git a/apps/extension/src/core/services/ApiClient.ts b/apps/extension/src/core/services/ApiClient.ts index c662256..50eca92 100644 --- a/apps/extension/src/core/services/ApiClient.ts +++ b/apps/extension/src/core/services/ApiClient.ts @@ -1,5 +1,5 @@ import type { AgentResult, QueryRequest, QueryResponse } from '@flowforge/contract'; -import { constants } from '#self/constants'; +import { constants } from '@/constants'; export interface ApiClient { query(request: QueryRequest): Promise; @@ -38,10 +38,10 @@ export class HttpApiClient implements ApiClient { clearTimeout(timeoutId); if (error instanceof DOMException && error.name === 'AbortError') { - throw new Error('Request timeout. Please try again.'); + throw new Error('Request timeout. Please try again.', { cause: error }); } if (error instanceof TypeError) { - throw new Error('Network error. Make sure the backend server is running.'); + throw new Error('Network error. Make sure the backend server is running.', { cause: error }); } throw error instanceof Error ? error : new Error('Unknown error'); } diff --git a/apps/extension/src/core/services/HistoryStorage.ts b/apps/extension/src/core/services/HistoryStorage.ts index 741cfd6..1fa01b5 100644 --- a/apps/extension/src/core/services/HistoryStorage.ts +++ b/apps/extension/src/core/services/HistoryStorage.ts @@ -1,5 +1,5 @@ -import type { LocalStorage } from '#self/adapters/interface'; -import { constants } from '#self/constants'; +import type { LocalStorage } from '@/adapters/interface'; +import { constants } from '@/constants'; export class HistoryStorage { private readonly storage: LocalStorage; diff --git a/apps/extension/src/core/services/RootInjector.ts b/apps/extension/src/core/services/RootInjector.ts index 0a33051..31c8635 100644 --- a/apps/extension/src/core/services/RootInjector.ts +++ b/apps/extension/src/core/services/RootInjector.ts @@ -10,11 +10,12 @@ export interface InjectedRoot { } function createRootStyle(options?: InjectOptions) { - return 'box-sizing: border-box; margin: 0; padding: 0; ' - + (options?.overlay + return ( + 'box-sizing: border-box; margin: 0; padding: 0; ' + + (options?.overlay ? 'position: fixed; inset: 0; isolation: isolate; pointer-events: none; z-index: 2147483647; ' - : 'position: relative; z-index: 0; ' - ); + : 'position: relative; z-index: 0; ') + ); } export interface RootInjector { @@ -47,7 +48,7 @@ export class DocumentRootInjector implements RootInjector { mountPoint.style.cssText = createRootStyle(options); doc.body.appendChild(mountPoint); } - return { mountPoint, destroy: () => mountPoint.remove()}; + return { mountPoint, destroy: () => mountPoint.remove() }; } } @@ -59,9 +60,7 @@ export interface InjectedShadowRoot extends InjectedRoot { } function createShadowMountStyle(options?: InjectOptions) { - return options?.overlay - ? 'position: relative; width: 100%; height: 100%; pointer-events: none; ' - : ''; + return options?.overlay ? 'position: relative; width: 100%; height: 100%; pointer-events: none; ' : ''; } export class ShadowRootInjector implements RootInjector { diff --git a/apps/extension/src/core/services/SettignsStorage.ts b/apps/extension/src/core/services/SettingsStorage.ts similarity index 50% rename from apps/extension/src/core/services/SettignsStorage.ts rename to apps/extension/src/core/services/SettingsStorage.ts index e2b917a..f5807ef 100644 --- a/apps/extension/src/core/services/SettignsStorage.ts +++ b/apps/extension/src/core/services/SettingsStorage.ts @@ -1,6 +1,6 @@ -import type { LocalStorage } from '#self/adapters/interface'; -import { constants } from '#self/constants'; -import type { ExtensionSettings } from '#self/types'; +import type { LocalStorage } from '@/adapters/interface'; +import { constants } from '@/constants'; +import type { ExtensionSettings } from '@/types'; const SETTINGS_STORAGE_KEY = `${constants.LOCAL_STORAGE_NAMESPACE}_settings`; @@ -8,16 +8,19 @@ export class SettingsStorage { private readonly storage: LocalStorage; private readonly defaults: ExtensionSettings; - constructor(storage: LocalStorage, defaults: ExtensionSettings) { + constructor(storage: LocalStorage, defaults: ExtensionSettings, initialSettings: Partial = {}) { this.storage = storage; - this.defaults = { ...defaults }; + this.defaults = { + ...defaults, + ...initialSettings, + }; } /** * Get the current settings */ async get(): Promise { - const stored = (await this.storage.get(SETTINGS_STORAGE_KEY)) ?? {}; + const stored = (await this.storage.get>(SETTINGS_STORAGE_KEY)) ?? {}; return { ...this.defaults, ...stored, @@ -28,12 +31,12 @@ export class SettingsStorage { * Update settings with a partial patch, merging it with existing settings */ async update(patch: Partial): Promise { - const current = await this.get(); - const next: ExtensionSettings = { - ...current, + const stored = (await this.storage.get>(SETTINGS_STORAGE_KEY)) ?? {}; + const nextStored: Partial = { + ...stored, ...patch, }; - await this.storage.set(SETTINGS_STORAGE_KEY, next); - return next; + await this.storage.set(SETTINGS_STORAGE_KEY, nextStored); + return this.get(); } } diff --git a/apps/extension/src/core/types/messages.ts b/apps/extension/src/core/types/messages.ts index b2c7f54..70772fc 100644 --- a/apps/extension/src/core/types/messages.ts +++ b/apps/extension/src/core/types/messages.ts @@ -1,23 +1,36 @@ -import type { AgentResultElement, AgentResultMode, PageModel, QueryResponse } from '@flowforge/contract'; -import type { ExtensionSettings } from '#self/core/types/settings'; +import type { AgentResultElement, AgentResultMode, PageTrail, QueryResponse } from '@flowforge/contract'; +import type { ExtensionSettings } from '@/core/types/settings'; type MessageTypeToBackground = 'GET_SETTINGS'; -type MessageTypePopupToBackground = 'ASK_QUESTION' | 'GET_PREV_QUESTIONS' | 'NAVIGATE_TO_ELEMENT' | 'UPDATE_SETTINGS'; -type MessageTypeBackgroundToPage = 'COLLECT_PAGE_MODEL' | 'START_ONBOARDING' | 'HIGHLIGHT_ELEMENT' | 'CLEAR_PAGE' | 'APPLY_SETTINGS'; + +type MessageTypePopupToBackground = + | 'POPUP_INITIALISE' + | 'ASK_QUESTION' + | 'GET_PREV_QUESTIONS' + | 'NAVIGATE_TO_ELEMENT' + | 'UPDATE_SETTINGS' + | 'OPEN_PAGE_INSPECTOR'; + +type MessageTypeBackgroundToPage = + | 'COLLECT_PAGE_TRAIL' + | 'START_ONBOARDING' + | 'HIGHLIGHT_ELEMENT' + | 'CLEAR_PAGE' + | 'APPLY_SETTINGS' + | 'OPEN_INSPECTOR'; type MessageType = MessageTypeToBackground | MessageTypePopupToBackground | MessageTypeBackgroundToPage; export type Message = T extends undefined ? { type: MessageType } : { type: MessageType; data: T }; export type MessageResponse = - | (T extends undefined ? { success: true } : { success: true; data: T }) - | { success: false; error: string }; + (T extends undefined ? { success: true } : { success: true; data: T }) | { success: false; error: string }; // Popup, Page -> Background export type GetSettingsMessage = Message & { type: 'GET_SETTINGS'; -} +}; export type GetSettingsMessageResponseData = ExtensionSettings; @@ -25,9 +38,14 @@ export type GetSettingsMessageResponse = MessageResponse Background +export type PopupInitializeMessage = Message & { + type: 'POPUP_INITIALISE'; + senderId: number; +}; + export type UpdateSettingsMessageData = { patch: Partial; -} +}; export type UpdateSettingsMessage = Message & { type: 'UPDATE_SETTINGS'; @@ -71,6 +89,11 @@ export type NavigateToElementMessage = Message & { senderId: number; }; +export type OpenPageInspectorMessage = Message & { + type: 'OPEN_PAGE_INSPECTOR'; + senderId: number; +}; + // Background -> Page export interface ApplySettingsMessageData { @@ -81,13 +104,13 @@ export type ApplySettingsMessage = Message & { type: 'APPLY_SETTINGS'; }; -export type CollectPageModelMessage = Message & { - type: 'COLLECT_PAGE_MODEL'; +export type CollectPageTrailMessage = Message & { + type: 'COLLECT_PAGE_TRAIL'; }; -export type CollectPageModelMessageResponseData = PageModel; +export type CollectPageTrailMessageResponseData = PageTrail; -export type CollectPageModelMessageResponse = MessageResponse; +export type CollectPageTrailMessageResponse = MessageResponse; export interface StartOnboardingMessageData { title: string; @@ -112,8 +135,16 @@ export type HighlightElementMessage = Message & { type: 'HIGHLIGHT_ELEMENT'; }; +export type OpenInspectorMessage = Message & { + type: 'OPEN_INSPECTOR'; +}; + // Type guards +export function isPopupInitializeMessage(message: Message): message is PopupInitializeMessage { + return message.type === 'POPUP_INITIALISE'; +} + export function isGetSettingsMessage(message: Message): message is GetSettingsMessage { return message.type === 'GET_SETTINGS'; } @@ -134,8 +165,8 @@ export function isGetPrevQuestionsMessage(message: Message): message is GetPrevQ return message.type === 'GET_PREV_QUESTIONS'; } -export function isCollectPageModelMessage(message: Message): message is CollectPageModelMessage { - return message.type === 'COLLECT_PAGE_MODEL'; +export function isCollectPageTrailMessage(message: Message): message is CollectPageTrailMessage { + return message.type === 'COLLECT_PAGE_TRAIL'; } export function isStartOnboardingMessage(message: Message): message is StartOnboardingMessage { @@ -146,6 +177,14 @@ export function isClearPageMessage(message: Message): message is ClearPageMessag return message.type === 'CLEAR_PAGE'; } +export function isOpenInspectorMessage(message: Message): message is OpenInspectorMessage { + return message.type === 'OPEN_INSPECTOR'; +} + +export function isOpenPageInspectorMessage(message: Message): message is OpenPageInspectorMessage { + return message.type === 'OPEN_PAGE_INSPECTOR'; +} + export function isNavigateToElementMessage(message: Message): message is NavigateToElementMessage { return message.type === 'NAVIGATE_TO_ELEMENT'; } diff --git a/apps/extension/src/embed/Runtime.tsx b/apps/extension/src/embed/Runtime.tsx index 8100ea2..9a1332d 100644 --- a/apps/extension/src/embed/Runtime.tsx +++ b/apps/extension/src/embed/Runtime.tsx @@ -1,24 +1,26 @@ -import { EmbedTransportService } from '#self/adapters/embed/EmbedTransportService'; -import { type InjectedRoot, ShadowRootInjector } from '#self/core/services/RootInjector'; +import { EmbedTransportService } from '@/adapters/embed/EmbedTransportService'; +import { type InjectedRoot, ShadowRootInjector } from '@/core/services/RootInjector'; import { render } from 'preact'; import shellStyles from './shell/shell.css?inline'; -import type { TransportService } from '#self/adapters/interface'; -import { type ApiClient, DemoApiClient, HttpApiClient } from '#self/core/services/ApiClient'; -import { config } from '#self/config'; -import { HistoryStorage } from '#self/core/services/HistoryStorage'; -import { SettingsStorage } from '#self/core/services/SettignsStorage'; -import { BackgroundWorker } from '#self/background/BackgroundWorker'; -import { ShellApp, type ShellAppDemoProps, type ShellAppRef } from '#self/embed/shell/ShellApp'; +import type { TransportService } from '@/adapters/interface'; +import { type ApiClient, DemoApiClient, HttpApiClient } from '@/core/services/ApiClient'; +import { config } from '@/config'; +import { HistoryStorage } from '@/core/services/HistoryStorage'; +import { SettingsStorage } from '@/core/services/SettingsStorage'; +import { BackgroundWorker } from '@/background/BackgroundWorker'; +import { ShellApp, type ShellAppDemoProps, type ShellAppRef } from '@/embed/shell/ShellApp'; import { createRef, type RefObject } from 'preact/compat'; -import { EmbedLocalStorage } from '#self/adapters/embed/EmbedLocalStorage'; +import { EmbedLocalStorage } from '@/adapters/embed/EmbedLocalStorage'; import type { AgentResult } from '@flowforge/contract'; -import { embedConstants } from '#self/embed/constants'; -import type { TriggerSize } from '#self/embed/components/Trigger/Trigger'; +import { embedConstants } from '@/embed/constants'; +import type { TriggerSize } from '@/embed/components/Trigger/Trigger'; +import type { ExtensionSettings, MessageResponse, OpenPageInspectorMessage } from '@/types'; interface RuntimeStartOptions { triggerSize?: TriggerSize; + settings?: Partial; } interface RuntimeDemoOptions extends RuntimeStartOptions { @@ -38,6 +40,7 @@ interface MountShellOptions { interface RuntimeApi { openPopup(question?: string): void; closePopup(): void; + openPageInspector(): Promise; destroy(): void; } @@ -66,9 +69,9 @@ export class Runtime implements RuntimeApi { static async start(options: RuntimeStartOptions = {}): Promise { const runtime = new Runtime(); const apiClient = new HttpApiClient(config.serverUrl); - runtime.startBackground(apiClient); + runtime.startBackground(apiClient, options.settings); await runtime.mountShell({ - triggerSize: options.triggerSize + triggerSize: options.triggerSize, }); console.log('[FlowForge] Runtime successfully started'); @@ -91,7 +94,7 @@ export class Runtime implements RuntimeApi { stubModel: options.stubModel, stubQA: options.stubQA, }); - runtime.startBackground(apiClient); + runtime.startBackground(apiClient, options.settings); await runtime.mountShell({ triggerSize: options.triggerSize, demoProps: { @@ -100,7 +103,7 @@ export class Runtime implements RuntimeApi { topic: options.topic, stubQuestions: options.stubQA?.map((s) => s.question) ?? [], }, - } + }, }); console.log('[FlowForge] Demo runtime successfully started'); @@ -115,16 +118,24 @@ export class Runtime implements RuntimeApi { this.shellRef.current?.close(); } + async openPageInspector(): Promise { + const message: OpenPageInspectorMessage = { + type: 'OPEN_PAGE_INSPECTOR', + senderId: await this.transport.getActiveSenderId(), + }; + await this.transport.sendToBackground(message); + } + destroy() { this.stopBackground(); this.unmountShell(); console.log('[FlowForge] Runtime successfully destroyed'); } - private startBackground(apiClient: ApiClient): void { + private startBackground(apiClient: ApiClient, initialSettings: Partial = {}): void { const localStorage = new EmbedLocalStorage(); const historyStorage = new HistoryStorage(localStorage, config.questionsHistoryLimit); - const settingsStorage = new SettingsStorage(localStorage, config.defaultSettings); + const settingsStorage = new SettingsStorage(localStorage, config.defaultSettings, initialSettings); this.backgroundWorker = new BackgroundWorker(this.transport, apiClient, historyStorage, settingsStorage); this.backgroundWorker.start(); @@ -142,7 +153,12 @@ export class Runtime implements RuntimeApi { const shellRoot = rootInjector.inject(document, embedConstants.SHELL_ROOT_ID, { overlay: true }); rootInjector.injectStyles(shellRoot, shellStyles); render( - , + , shellRoot.mountPoint, ); this.shellRoot = shellRoot; diff --git a/apps/extension/src/embed/components/Trigger/Trigger.css b/apps/extension/src/embed/components/Trigger/Trigger.css index df89d2f..9740686 100644 --- a/apps/extension/src/embed/components/Trigger/Trigger.css +++ b/apps/extension/src/embed/components/Trigger/Trigger.css @@ -1,42 +1,56 @@ /* Trigger */ .flowforge-trigger { - position: fixed; - top: var(--flowforge-app-offset); - right: 0; + position: fixed; + top: var(--flowforge-app-offset); + right: 0; - display: flex; - padding: var(--flowforge-pad-sm) calc(var(--flowforge-pad-sm) * 2) var(--flowforge-pad-sm) var(--flowforge-pad-sm); - border-radius: var(--flowforge-pad-sm) 0 0 var(--flowforge-pad-sm); - border: none; - cursor: pointer; - color: var(--flowforge-color-text-on-dark); - font: var(--flowforge-text-h3); - pointer-events: auto; - box-shadow: 0 4px 12px rgba(var(--flowforge-rgb-shadow), 0.4); - background: - url('../../../shared/img/header-bg-sm.webp') right bottom no-repeat, - linear-gradient(135deg, var(--flowforge-color-primary) 0%, var(--flowforge-color-primary-hover) 100%); - background-position: center center, 0 0; - animation: flowforge-button-stars-parallax 60s ease-in-out infinite alternate; - transition: var(--flowforge-transition-default); + display: flex; + padding: var(--flowforge-pad-sm) calc(var(--flowforge-pad-sm) * 2) var(--flowforge-pad-sm) + var(--flowforge-pad-sm); + border-radius: var(--flowforge-pad-sm) 0 0 var(--flowforge-pad-sm); + border: none; + cursor: pointer; + color: var(--flowforge-color-text-on-dark); + font: var(--flowforge-text-h3); + pointer-events: auto; + box-shadow: 0 4px 12px rgba(var(--flowforge-rgb-shadow), 0.4); + background: + url('../../../shared/img/header-bg-sm.webp') right bottom no-repeat, + linear-gradient( + 135deg, + var(--flowforge-color-primary) 0%, + var(--flowforge-color-primary-hover) 100% + ); + background-position: + center center, + 0 0; + animation: flowforge-button-stars-parallax 60s ease-in-out infinite alternate; + transition: var(--flowforge-transition-default); } @keyframes flowforge-button-stars-parallax { - 0% { - background-position: calc(50% - 95px) calc(50% - 15px), 0 0; - } - 50% { - background-position: 50% calc(50% + 15px), 0 0; - } - 100% { - background-position: calc(50% + 95px) calc(50% - 15px), 0 0; - } + 0% { + background-position: + calc(50% - 95px) calc(50% - 15px), + 0 0; + } + 50% { + background-position: + 50% calc(50% + 15px), + 0 0; + } + 100% { + background-position: + calc(50% + 95px) calc(50% - 15px), + 0 0; + } } .flowforge-trigger:hover { - transform: translateY(-2px); - animation-play-state: paused; + transform: translateY(-2px); + animation-play-state: paused; } .flowforge-trigger--lg { - font: var(--flowforge-text-h2); - padding: var(--flowforge-pad-md) calc(var(--flowforge-pad-md) * 2) var(--flowforge-pad-md) var(--flowforge-pad-md); - border-radius: var(--flowforge-pad-md) 0 0 var(--flowforge-pad-md); + font: var(--flowforge-text-h2); + padding: var(--flowforge-pad-md) calc(var(--flowforge-pad-md) * 2) var(--flowforge-pad-md) + var(--flowforge-pad-md); + border-radius: var(--flowforge-pad-md) 0 0 var(--flowforge-pad-md); } diff --git a/apps/extension/src/embed/components/Trigger/Trigger.tsx b/apps/extension/src/embed/components/Trigger/Trigger.tsx index 53d59a3..2633107 100644 --- a/apps/extension/src/embed/components/Trigger/Trigger.tsx +++ b/apps/extension/src/embed/components/Trigger/Trigger.tsx @@ -10,7 +10,7 @@ interface TriggerProps { ref?: Ref; } -export const Trigger = forwardRef(({ size='medium', isOpen, onToggle }, ref) => { +export const Trigger = forwardRef(({ size = 'medium', isOpen, onToggle }, ref) => { const classes = ['flowforge-trigger', size === 'large' && 'flowforge-trigger--lg'].filter(Boolean).join(' '); return ( diff --git a/apps/extension/src/embed/flowforge-runtime.d.ts b/apps/extension/src/embed/flowforge-runtime.d.ts index 5263c81..387371c 100644 --- a/apps/extension/src/embed/flowforge-runtime.d.ts +++ b/apps/extension/src/embed/flowforge-runtime.d.ts @@ -7,19 +7,31 @@ declare global { export interface FlowForgeRuntime { readonly version: string; - start(options?: { triggerSize?: 'medium' | 'large' }): Promise; + start(options?: { + triggerSize?: 'medium' | 'large'; + settings?: { + theme?: 'light' | 'dark'; + }; + }): Promise; demo(options?: { triggerSize?: 'medium' | 'large'; + settings?: { + theme?: 'light' | 'dark'; + }; topic?: string; stubModel?: string; - stubQA?: { question: string; result: unknown }[]; + stubQA?: { + question: string; + result: unknown; + }[]; }): Promise; } export interface FlowForgeInstance { openPopup(question?: string): void; closePopup(): void; + openPageInspector(): Promise; destroy(): void; } diff --git a/apps/extension/src/embed/shell/ShellApp.tsx b/apps/extension/src/embed/shell/ShellApp.tsx index 81c475b..2743724 100644 --- a/apps/extension/src/embed/shell/ShellApp.tsx +++ b/apps/extension/src/embed/shell/ShellApp.tsx @@ -1,14 +1,14 @@ -import type { TransportService } from '#self/adapters/interface'; +import type { TransportService } from '@/adapters/interface'; -import { Main } from '#self/shared/components/Main'; -import { PopupApp, type PopupAppDemoProps } from '#self/popup/PopupApp'; +import { Main } from '@/shared/components/Main'; +import { PopupApp, type PopupAppDemoProps } from '@/popup/PopupApp'; import { useCallback, useEffect, useImperativeHandle, useRef, useState } from 'preact/hooks'; -import { getEventTarget } from '#self/core/utils/dom'; +import { getEventTarget } from '@/core/utils/dom'; import { forwardRef } from 'preact/compat'; -import { PageApp } from '#self/page/PageApp'; -import { useSettings } from '#self/shared/hooks/useSettings'; -import { Trigger } from '#self/embed/components/Trigger'; -import type { TriggerSize } from '#self/embed/components/Trigger/Trigger'; +import { PageApp } from '@/page/PageApp'; +import { useSettings } from '@/shared/hooks/useSettings'; +import { Trigger } from '@/embed/components/Trigger'; +import type { TriggerSize } from '@/embed/components/Trigger/Trigger'; export type ShellAppDemoProps = PopupAppDemoProps; @@ -23,7 +23,10 @@ export interface ShellAppRef { close: () => void; } -export const ShellApp = forwardRef(function ShellApp({ transport, demoProps, triggerSize }, ref) { +export const ShellApp = forwardRef(function ShellApp( + { transport, demoProps, triggerSize }, + ref, +) { const [isOpen, setIsOpen] = useState(false); const [initialQuestion, setInitialQuestion] = useState(); const { theme, toggleTheme } = useSettings({ transport }); @@ -58,34 +61,36 @@ export const ShellApp = forwardRef(function ShellApp if (!isOpen) return; // Close popup when clicking outside of it - function handleClickOutside(e: MouseEvent) { + function handlePointerDownOutside(e: PointerEvent) { const target = getEventTarget(e); - if ( - popupRef.current && - !popupRef.current.contains(target) && - triggerRef.current && - !triggerRef.current.contains(target) - ) { + + if (!target) return; + + const isInsidePopup = popupRef.current?.contains(target) ?? false; + const isInsideTrigger = triggerRef.current?.contains(target) ?? false; + + if (!isInsidePopup && !isInsideTrigger) { closePopup(); } } + // Close popup when pressing escape const handleKeyDown = (e: KeyboardEvent) => { + if (e.key !== 'Escape') return; + const target = getEventTarget(e); // if typing in an input, don't escape if (target?.closest('input, textarea, select, [contenteditable="true"]')) return; - if (e.key === 'Escape') { - e.preventDefault(); - closePopup(); - } + e.preventDefault(); + closePopup(); }; - document.addEventListener('mousedown', handleClickOutside); + document.addEventListener('pointerdown', handlePointerDownOutside); document.addEventListener('keydown', handleKeyDown); return () => { - document.removeEventListener('mousedown', handleClickOutside); + document.removeEventListener('pointerdown', handlePointerDownOutside); document.removeEventListener('keydown', handleKeyDown); }; }, [isOpen, closePopup]); @@ -102,8 +107,9 @@ export const ShellApp = forwardRef(function ShellApp transport={transport} demoProps={demoProps} theme={theme} - toggleTheme={toggleTheme} + onToggleTheme={toggleTheme} initialQuestion={initialQuestion} + onClose={closePopup} /> )} diff --git a/apps/extension/src/embed/shell/shell.css b/apps/extension/src/embed/shell/shell.css index aa6d76d..055cc56 100644 --- a/apps/extension/src/embed/shell/shell.css +++ b/apps/extension/src/embed/shell/shell.css @@ -5,21 +5,21 @@ @import '../../popup/popup.css'; .flowforge-shell .flowforge-page { - z-index: 1; + z-index: 1; } .flowforge-shell .flowforge-trigger { - z-index: 2; + z-index: 2; } .flowforge-shell .flowforge-popup-container { - z-index: 3; + z-index: 3; } /* Popup container */ .flowforge-popup-container { - position: fixed; - top: 0; - right: 0; - border-radius: 0; - border: none; - box-shadow: 0 4px 12px rgba(var(--flowforge-rgb-shadow), 0.4); + position: fixed; + top: 0; + right: 0; + border-radius: 0; + border: none; + box-shadow: 0 4px 12px rgba(var(--flowforge-rgb-shadow), 0.4); } diff --git a/apps/extension/src/page/PageApp.tsx b/apps/extension/src/page/PageApp.tsx index 13a75b3..c7645d1 100644 --- a/apps/extension/src/page/PageApp.tsx +++ b/apps/extension/src/page/PageApp.tsx @@ -1,15 +1,16 @@ -import type { TransportService } from '#self/adapters/interface'; +import type { TransportService } from '@/adapters/interface'; -import { usePage } from '#self/page/hooks/usePage'; -import { Highlight } from '#self/page/components/Highlight'; -import { Wizard } from '#self/page/components/Wizard'; +import { usePage } from '@/page/hooks/usePage'; +import { Highlight } from '@/page/components/Highlight'; +import { Wizard } from '@/page/components/Wizard'; +import { Inspector } from '@/page/components/Inspector'; export interface PageAppProps { transport: TransportService; } export function PageApp({ transport }: PageAppProps) { - const { highlights, wizard } = usePage({ transport }); + const { highlights, wizard, inspector } = usePage({ transport }); return (
@@ -21,6 +22,7 @@ export function PageApp({ transport }: PageAppProps) {
)} {wizard && } + {inspector && } ); } diff --git a/apps/extension/src/page/components/Highlight/Highlight.css b/apps/extension/src/page/components/Highlight/Highlight.css index f9fe770..180138d 100644 --- a/apps/extension/src/page/components/Highlight/Highlight.css +++ b/apps/extension/src/page/components/Highlight/Highlight.css @@ -2,124 +2,100 @@ .flowforge-highlights-container { } .flowforge-highlight { - position: absolute; - z-index: 1; - pointer-events: none; - box-sizing: border-box; - opacity: 0; - transform: translateY(6px) scale(0.96); - transition: opacity 180ms ease, transform 240ms cubic-bezier(0.22, 1, 0.36, 1); + position: absolute; + z-index: 1; + pointer-events: none; + box-sizing: border-box; + opacity: 0; + transform: translateY(6px) scale(0.96); + transition: + opacity 180ms ease, + transform 240ms cubic-bezier(0.22, 1, 0.36, 1); } .flowforge-highlight--visible { - opacity: 1; - transform: translateY(0) scale(1); + opacity: 1; + transform: translateY(0) scale(1); } .flowforge-highlight--hiding { - opacity: 0; - transform: translateY(6px) scale(0.96); + opacity: 0; + transform: translateY(6px) scale(0.96); } .flowforge-highlight__ring, .flowforge-highlight__glow { - position: absolute; - inset: 0; - border-radius: var(--flowforge-pad-sm); + position: absolute; + inset: 0; + border-radius: var(--flowforge-pad-sm); } .flowforge-highlight__ring { - border: 2px solid var(--flowforge-color-primary); - background: rgba(var(--flowforge-rgb-primary), 0.1); - box-shadow: - 0 0 0 4px rgba(var(--flowforge-rgb-primary), 0.16), - 0 6px 18px rgba(var(--flowforge-rgb-shadow), 0.12); - animation: flowforge-highlight-pulse 1.6s ease-in-out infinite; + border: 2px solid var(--flowforge-color-primary); + background: rgba(var(--flowforge-rgb-primary), 0.1); + box-shadow: + 0 0 0 4px rgba(var(--flowforge-rgb-primary), 0.16), + 0 6px 18px rgba(var(--flowforge-rgb-shadow), 0.12); + animation: flowforge-highlight-pulse 1.6s ease-in-out infinite; } .flowforge-highlight__glow { - inset: -2px; - border-radius: var(--flowforge-pad-md); - background: var(--flowforge-color-primary); - opacity: 0.06; - filter: blur(var(--flowforge-pad-sm)); + inset: -2px; + border-radius: var(--flowforge-pad-md); + background: var(--flowforge-color-primary); + opacity: 0.06; + filter: blur(var(--flowforge-pad-sm)); } .flowforge-highlight__label { - position: absolute; - left: 0; - max-width: 360px; - min-width: 160px; - min-height: 28px; - padding: var(--flowforge-pad-sm) 28px var(--flowforge-pad-sm) var(--flowforge-pad-lg); - border: 1px solid var(--flowforge-color-border); - border-left: 4px solid var(--flowforge-color-primary); - border-radius: var(--flowforge-pad-sm); - background: var(--flowforge-color-surface); - color: var(--flowforge-color-text); - font: var(--flowforge-text-sm); - white-space: normal; - overflow-wrap: anywhere; - word-break: break-word; - box-shadow: 0 4px 12px rgba(var(--flowforge-rgb-shadow), 0.4); - display: flex; - flex-direction: column; - text-align: left; - gap: var(--flowforge-pad-xs); - pointer-events: auto; + position: absolute; + left: 0; + max-width: 360px; + min-width: 160px; + min-height: 28px; + padding: var(--flowforge-pad-sm) 28px var(--flowforge-pad-sm) var(--flowforge-pad-lg); + border: 1px solid var(--flowforge-color-border); + border-left: 4px solid var(--flowforge-color-primary); + border-radius: var(--flowforge-pad-sm); + background: var(--flowforge-color-surface); + color: var(--flowforge-color-text); + font: var(--flowforge-text-sm); + white-space: normal; + overflow-wrap: anywhere; + word-break: break-word; + box-shadow: 0 4px 12px rgba(var(--flowforge-rgb-shadow), 0.4); + display: flex; + flex-direction: column; + text-align: left; + gap: var(--flowforge-pad-xs); + pointer-events: auto; } .flowforge-highlight__label-text, .flowforge-highlight__label-action { - text-transform: none; - letter-spacing: normal; - color: var(--flowforge-color-text); - font: var(--flowforge-text-sm); + text-transform: none; + letter-spacing: normal; + color: var(--flowforge-color-text); + font: var(--flowforge-text-sm); } .flowforge-highlight__label-text > span, .flowforge-highlight__label-action > span { - text-transform: inherit; - letter-spacing: inherit; - color: inherit; - font: inherit; - font-weight: 600; -} -.flowforge-highlight__label-star { - position: absolute; - display: block; - top: 6px; - right: var(--flowforge-pad-sm); - font-size: 14px; - color: var(--flowforge-color-primary); - text-transform: none; - letter-spacing: normal; - text-shadow: - 0 0 4px rgba(var(--flowforge-rgb-primary), 0.45), - 0 0 10px rgba(var(--flowforge-rgb-primary), 0.22); - animation: flowforge-highlight-star-twinkle 1.8s ease-in-out infinite; + text-transform: inherit; + letter-spacing: inherit; + color: inherit; + font: inherit; + font-weight: 600; } .flowforge-highlight__label--top { - top: -36px; + top: -36px; } .flowforge-highlight__label--bottom { - top: calc(100% + 8px); -} - -@keyframes flowforge-highlight-star-twinkle { - 0%, - 100% { - opacity: 0.6; - transform: scale(0.85); - } - 50% { - opacity: 1; - transform: scale(1.2); - } + top: calc(100% + 8px); } @keyframes flowforge-highlight-pulse { - 0%, - 100% { - box-shadow: - 0 0 0 4px rgba(var(--flowforge-rgb-primary), 0.14), - 0 6px 18px rgba(var(--flowforge-rgb-shadow), 0.12); - } - 50% { - box-shadow: - 0 0 0 6px rgba(var(--flowforge-rgb-primary), 0.2), - 0 8px 22px rgba(var(--flowforge-rgb-shadow), 0.14); - } + 0%, + 100% { + box-shadow: + 0 0 0 4px rgba(var(--flowforge-rgb-primary), 0.14), + 0 6px 18px rgba(var(--flowforge-rgb-shadow), 0.12); + } + 50% { + box-shadow: + 0 0 0 6px rgba(var(--flowforge-rgb-primary), 0.2), + 0 8px 22px rgba(var(--flowforge-rgb-shadow), 0.14); + } } diff --git a/apps/extension/src/page/components/Highlight/Highlight.tsx b/apps/extension/src/page/components/Highlight/Highlight.tsx index 70f287b..9b3ee69 100644 --- a/apps/extension/src/page/components/Highlight/Highlight.tsx +++ b/apps/extension/src/page/components/Highlight/Highlight.tsx @@ -1,7 +1,7 @@ import { useCallback, useEffect, useLayoutEffect, useRef, useState } from 'preact/hooks'; -import { calcLabelLayout, calcElementPosition, isNewPosition, isNewLabelLayout } from '#self/page/utils/position'; -import type { HighlightViewModel } from '#self/page/hooks/usePage.types'; -import { constants } from '#self/constants'; +import { calcLabelLayout, calcElementPosition, isNewPosition, isNewLabelLayout } from '@/page/utils/position'; +import type { HighlightViewModel } from '@/page/hooks/usePage'; +import { constants } from '@/constants'; export function Highlight({ el, element, stepIndex, duration, remove }: HighlightViewModel) { const [isVisible, setIsVisible] = useState(false); @@ -129,6 +129,7 @@ export function Highlight({ el, element, stepIndex, duration, remove }: Highligh 'flowforge-highlight__label', labelLayout?.placement === 'top' ? 'flowforge-highlight__label--top' : null, labelLayout?.placement === 'bottom' ? 'flowforge-highlight__label--bottom' : null, + 'flowforge-stared-twinkle', ] .filter(Boolean) .join(' ')} @@ -148,9 +149,6 @@ export function Highlight({ el, element, stepIndex, duration, remove }: Highligh {element.action} )} - )} diff --git a/apps/extension/src/page/components/Inspector/Inspector.css b/apps/extension/src/page/components/Inspector/Inspector.css new file mode 100644 index 0000000..634396c --- /dev/null +++ b/apps/extension/src/page/components/Inspector/Inspector.css @@ -0,0 +1,126 @@ +/* Inspector */ +.flowforge-inspector { + --flowforge-inspector-max-width: 1080px; +} +.flowforge-inspector-container { + position: fixed; + inset: 0; + display: flex; + align-items: center; + justify-content: center; + padding: var(--flowforge-app-offset); + background: rgba(var(--flowforge-rgb-shadow), 0.4); + pointer-events: auto; + z-index: 3; +} +.flowforge-inspector { + width: min(100%, var(--flowforge-inspector-max-width)); + height: calc(100vh - var(--flowforge-app-offset) * 2); + overflow: hidden; + display: flex; + flex-direction: column; + box-sizing: border-box; + background: var(--flowforge-color-surface); + border: 1px solid var(--flowforge-color-border); + border-radius: var(--flowforge-pad-sm); + padding: 0; + box-shadow: + 0 0 0 4px rgba(var(--flowforge-rgb-primary), 0.14), + 0 6px 18px rgba(var(--flowforge-rgb-shadow), 0.12); + color: var(--flowforge-color-text); + font: var(--flowforge-text-md); +} + +/* Header */ +.flowforge-inspector__header { + flex-shrink: 0; + display: flex; + align-items: flex-start; + justify-content: space-between; + background-color: var(--flowforge-color-bg); + gap: var(--flowforge-pad-lg); + padding: var(--flowforge-pad-lg) var(--flowforge-pad-lg) var(--flowforge-pad-xl) + var(--flowforge-pad-lg); +} +.flowforge-inspector__header-title { + margin: 0 0 var(--flowforge-pad-lg); + font: var(--flowforge-text-h3); + color: var(--flowforge-color-text); +} +.flowforge-inspector__header-subtitle { + margin: 0; + font: var(--flowforge-text-md); + color: var(--flowforge-color-text); + white-space: pre-wrap; +} + +/* Navigation */ +.flowforge-inspector__nav { + position: relative; + z-index: 1; + flex-shrink: 0; + padding: 0; + background-color: var(--flowforge-color-bg); +} +.flowforge-inspector__nav::after { + content: ''; + position: absolute; + right: 0; + bottom: -8px; + left: 0; + height: 8px; + background: linear-gradient( + rgba(var(--flowforge-rgb-shadow), 0.4), + rgba(var(--flowforge-rgb-shadow), 0) + ); + pointer-events: none; +} +.flowforge-inspector__nav .flowforge-tabs__tab:first-child { + margin-left: var(--flowforge-pad-lg); +} +.flowforge-inspector__nav .flowforge-tabs__tab:last-child { + margin-right: var(--flowforge-pad-lg); +} + +/* Content */ +.flowforge-inspector__content { + flex: 1; + min-height: 0; + overflow-y: auto; + overflow-x: hidden; + padding: var(--flowforge-pad-lg); +} +.flowforge-inspector__content::-webkit-scrollbar { + width: var(--flowforge-pad-xs); +} +.flowforge-inspector__content::-webkit-scrollbar-thumb { + background: rgba(var(--flowforge-rgb-primary), 0.5); + border-radius: 999px; +} + +/* Footer */ +.flowforge-inspector__footer { + position: relative; + z-index: 1; + flex-shrink: 0; + padding: var(--flowforge-pad-lg); + background-color: var(--flowforge-color-bg); + border-top: 1px solid var(--flowforge-color-border); +} +.flowforge-inspector__footer::before { + content: ''; + position: absolute; + top: -8px; + right: 0; + left: 0; + height: 8px; + background: linear-gradient( + rgba(var(--flowforge-rgb-shadow), 0), + rgba(var(--flowforge-rgb-shadow), 0.4) + ); + pointer-events: none; +} +.flowforge-inspector-page-metadata { + font: var(--flowforge-text-monospace-xs); + color: var(--flowforge-color-muted); +} diff --git a/apps/extension/src/page/components/Inspector/Inspector.tsx b/apps/extension/src/page/components/Inspector/Inspector.tsx new file mode 100644 index 0000000..e0a7b41 --- /dev/null +++ b/apps/extension/src/page/components/Inspector/Inspector.tsx @@ -0,0 +1,138 @@ +import type { TargetedPointerEvent } from 'preact'; +import { useEffect, useId, useState } from 'preact/hooks'; +import { BadgeInfo, BookOpenText, FileText, MousePointerClick } from 'lucide-preact'; +import { getEventTarget } from '@/core/utils/dom'; +import type { InspectorViewModel } from '@/page/hooks/usePage'; +import { Button } from '@/shared/components/Button'; +import { JsonViewer } from '@/shared/components/JsonViewer'; +import { MarkdownViewer } from '@/shared/components/MarkdownViewer'; +import { Tabs } from '@/shared/components/Tabs'; +import { PageMetadata } from '@/page/components/Inspector/components/Metadata'; +import { formatContentElement, formatInteractiveElement, generateSemanticMarkdown } from '@flowforge/page-trail'; + +const inspectorTabs = [ + { id: 'basics', label: 'Basics', icon: BadgeInfo }, + { id: 'content', label: 'Content', icon: BookOpenText }, + { id: 'interactive', label: 'Interactive', icon: MousePointerClick }, + { id: 'semanticView', label: 'Semantic view', icon: FileText }, +] as const; + +type InspectorTabId = (typeof inspectorTabs)[number]['id']; + +function getSemanticDescription(value: unknown): string | undefined { + if ( + typeof value === 'object' && + value !== null && + 'importanceScore' in value && + 'semanticDescription' in value && + typeof value.importanceScore === 'number' && + typeof value.semanticDescription === 'string' + ) { + return `${value.importanceScore.toFixed(2)} · ${value.semanticDescription}`; + } + return undefined; +} + +export function Inspector({ pageTrail, close }: InspectorViewModel) { + const [activeTab, setActiveTab] = useState('basics'); + const tabsIdPrefix = `flowforge-inspector-tabs-${useId()}`; + const getTabId = (id: string) => `${tabsIdPrefix}-tab-${id}`; + const getPanelId = (id: string) => `${tabsIdPrefix}-panel-${id}`; + + // Close on esc + useEffect(() => { + const handleKeyDown = (e: KeyboardEvent) => { + if (e.key !== 'Escape') return; + + const target = getEventTarget(e); + // if typing in an input, don't close + if (target?.closest('input, textarea, select, [contenteditable="true"]')) return; + + e.preventDefault(); + close(); + }; + document.addEventListener('keydown', handleKeyDown); + return () => { + document.removeEventListener('keydown', handleKeyDown); + }; + }, [close]); + + // Close on click outside the inspector + const handleContainerPointerDown = (e: TargetedPointerEvent) => { + if (e.target === e.currentTarget) { + close(); + } + }; + + return ( +
+
+
+
+

+ Inspect page context +

+

+ See what FlowForge understands about this page +

+
+
+ +
+
+
+ setActiveTab(id as InspectorTabId)} + getTabId={getTabId} + getPanelId={getPanelId} + autoFocus + /> +
+
+ {activeTab === 'basics' && } + {activeTab === 'content' && ( + ({ + ...contentElement, + semanticDescription: formatContentElement(contentElement), + }))} + /> + )} + {activeTab === 'interactive' && ( + ({ + ...interactiveElement, + semanticDescription: formatInteractiveElement(interactiveElement), + }))} + /> + )} + {activeTab === 'semanticView' && } +
+
+ +
+
+
+ ); +} diff --git a/apps/extension/src/page/components/Inspector/components/Metadata/PageMetadata.tsx b/apps/extension/src/page/components/Inspector/components/Metadata/PageMetadata.tsx new file mode 100644 index 0000000..85d7739 --- /dev/null +++ b/apps/extension/src/page/components/Inspector/components/Metadata/PageMetadata.tsx @@ -0,0 +1,41 @@ +import type { PageTrail } from '@flowforge/contract'; + +export interface PageMetadataProps { + metadata: PageTrail['metadata']; +} + +export function PageMetadata({ metadata }: PageMetadataProps) { + return ( +
+ Selected{' '} + {metadata.contentElementsLimitReached ? ( + <> + + {metadata.contentElements} + + /{metadata.contentElementsTotal} + + ) : ( + <>{metadata.contentElements} + )}{' '} + content elements,{' '} + {metadata.interactiveElementsLimitReached ? ( + <> + + {metadata.interactiveElements} + + /{metadata.interactiveElementsTotal} + + ) : ( + <>{metadata.interactiveElements} + )}{' '} + interactive elements · {metadata.durationMs}ms +
+ ); +} diff --git a/apps/extension/src/page/components/Inspector/components/Metadata/index.tsx b/apps/extension/src/page/components/Inspector/components/Metadata/index.tsx new file mode 100644 index 0000000..2add8c0 --- /dev/null +++ b/apps/extension/src/page/components/Inspector/components/Metadata/index.tsx @@ -0,0 +1 @@ +export { PageMetadata } from './PageMetadata'; diff --git a/apps/extension/src/page/components/Inspector/index.ts b/apps/extension/src/page/components/Inspector/index.ts new file mode 100644 index 0000000..f077f12 --- /dev/null +++ b/apps/extension/src/page/components/Inspector/index.ts @@ -0,0 +1 @@ +export { Inspector } from './Inspector'; diff --git a/apps/extension/src/page/components/Wizard/Wizard.css b/apps/extension/src/page/components/Wizard/Wizard.css index f4884d2..f8c083c 100644 --- a/apps/extension/src/page/components/Wizard/Wizard.css +++ b/apps/extension/src/page/components/Wizard/Wizard.css @@ -1,66 +1,66 @@ /* Wizard */ .flowforge-wizard { - --flowforge-wizard-width: 320px; + --flowforge-wizard-width: 320px; } .flowforge-wizard { - position: fixed; - bottom: var(--flowforge-app-offset); - right: var(--flowforge-app-offset); - background: var(--flowforge-color-surface); - border: 1px solid var(--flowforge-color-border); - border-top: 4px solid var(--flowforge-color-primary); - border-radius: var(--flowforge-pad-sm); - padding: var(--flowforge-pad-lg); - box-shadow: - 0 0 0 4px rgba(var(--flowforge-rgb-primary), 0.14), - 0 6px 18px rgba(var(--flowforge-rgb-shadow), 0.12); - color: var(--flowforge-color-text); - z-index: 2; - font: var(--flowforge-text-md); - width: var(--flowforge-wizard-width); - display: flex; - flex-direction: column; - gap: var(--flowforge-pad-lg); - pointer-events: auto; + position: fixed; + bottom: var(--flowforge-app-offset); + right: var(--flowforge-app-offset); + background: var(--flowforge-color-surface); + border: 1px solid var(--flowforge-color-border); + border-top: 4px solid var(--flowforge-color-primary); + border-radius: var(--flowforge-pad-sm); + padding: var(--flowforge-pad-lg); + box-shadow: + 0 0 0 4px rgba(var(--flowforge-rgb-primary), 0.14), + 0 6px 18px rgba(var(--flowforge-rgb-shadow), 0.12); + color: var(--flowforge-color-text); + z-index: 2; + font: var(--flowforge-text-md); + width: var(--flowforge-wizard-width); + display: flex; + flex-direction: column; + gap: var(--flowforge-pad-lg); + pointer-events: auto; } .flowforge-wizard__drag-handle { - cursor: grab; - user-select: none; + cursor: grab; + user-select: none; } .flowforge-wizard--dragging .flowforge-wizard__drag-handle { - cursor: grabbing; + cursor: grabbing; } .flowforge-wizard__title { - margin: 0; - font: var(--flowforge-text-h3); - color: var(--flowforge-color-text); + margin: 0; + font: var(--flowforge-text-h3); + color: var(--flowforge-color-text); } .flowforge-wizard__text { - margin: 0; - font: var(--flowforge-text-md); - color: var(--flowforge-color-text); - padding-bottom: var(--flowforge-pad-lg); - border-bottom: 1px solid var(--flowforge-color-border); - white-space: pre-wrap; + margin: 0; + font: var(--flowforge-text-md); + color: var(--flowforge-color-text); + padding-bottom: var(--flowforge-pad-lg); + border-bottom: 1px solid var(--flowforge-color-border); + white-space: pre-wrap; } /* Wizard Controls */ .flowforge-wizard-controls { - display: flex; - flex-direction: column; - gap: var(--flowforge-pad-sm); + display: flex; + flex-direction: column; + gap: var(--flowforge-pad-sm); } .flowforge-wizard-controls__title { - margin: 0; - font: var(--flowforge-text-h4); - color: var(--flowforge-color-text); + margin: 0; + font: var(--flowforge-text-h4); + color: var(--flowforge-color-text); } .flowforge-wizard-controls-list { - display: grid; - gap: var(--flowforge-pad-sm); + display: grid; + gap: var(--flowforge-pad-sm); } .flowforge-wizard-controls-list--start { - grid-template-columns: 2fr 1fr; + grid-template-columns: 2fr 1fr; } .flowforge-wizard-controls-list--steps { - grid-template-columns: repeat(3, 1fr); + grid-template-columns: repeat(3, 1fr); } diff --git a/apps/extension/src/page/components/Wizard/Wizard.tsx b/apps/extension/src/page/components/Wizard/Wizard.tsx index 545d944..40c71f3 100644 --- a/apps/extension/src/page/components/Wizard/Wizard.tsx +++ b/apps/extension/src/page/components/Wizard/Wizard.tsx @@ -1,7 +1,7 @@ import { useEffect, useRef, useState } from 'preact/hooks'; -import type { WizardViewModel } from '#self/page/hooks/usePage.types'; -import { getEventTarget } from '#self/core/utils/dom'; -import { Button } from '#self/shared/components/Button'; +import type { WizardViewModel } from '@/page/hooks/usePage'; +import { getEventTarget } from '@/core/utils/dom'; +import { Button } from '@/shared/components/Button'; interface DragState { startX: number; @@ -10,19 +10,13 @@ interface DragState { originBottom: number; } -export function Wizard({ title, description, steps, close, changeStep }: WizardViewModel) { - const [currentStep, setCurrentStep] = useState(0); +export function Wizard({ title, description, steps, currentStep, close, changeStep }: WizardViewModel) { const [position, setPosition] = useState({ right: 20, bottom: 20 }); const [isDragging, setIsDragging] = useState(false); const dragRef = useRef(null); const wizardRef = useRef(null); - // Notify parent of step change - useEffect(() => { - changeStep(currentStep); - }, [currentStep, changeStep]); - // Dragging logic useEffect(() => { if (!isDragging) return; @@ -99,11 +93,10 @@ export function Wizard({ title, description, steps, close, changeStep }: WizardV }, [currentStep, close]); // Handle wizard controls - const handleStart = () => setCurrentStep(1); - const handleNext = () => setCurrentStep(Math.min(steps.length, currentStep + 1)); - const handlePrev = () => setCurrentStep(Math.max(1, currentStep - 1)); + const handleStart = () => changeStep(1); + const handleNext = () => changeStep(Math.min(steps.length, currentStep + 1)); + const handlePrev = () => changeStep(Math.max(1, currentStep - 1)); const handleFinish = () => { - setCurrentStep(0); close(); }; // Handle drag handle diff --git a/apps/extension/src/page/hooks/usePage.ts b/apps/extension/src/page/hooks/usePage.ts index 3a1799c..314bb8e 100644 --- a/apps/extension/src/page/hooks/usePage.ts +++ b/apps/extension/src/page/hooks/usePage.ts @@ -1,35 +1,91 @@ import { isClearPageMessage, - isCollectPageModelMessage, + isCollectPageTrailMessage, isHighlightElementMessage, + isOpenInspectorMessage, isStartOnboardingMessage, -} from '#self/types'; -import type { Message, StartOnboardingMessageData } from '#self/types'; -import { useCallback, useEffect, useState } from 'preact/hooks'; -import { findElement, getOrCreateDataId } from '#self/core/locator/locate'; -import type { PageViewModel, HighlightState, WizardState } from './usePage.types'; -import type { TransportService } from '#self/adapters/interface'; -import { constants } from '#self/constants'; -import type { AgentResultElement } from '@flowforge/contract'; -import { PageModelCollector } from '@flowforge/page-model'; +} from '@/types'; +import type { Message, StartOnboardingMessageData } from '@/types'; +import { useCallback, useLayoutEffect, useState } from 'preact/hooks'; +import { findElement, getOrCreateDataId } from '@/core/locator/locate'; +import type { TransportService } from '@/adapters/interface'; +import { constants } from '@/constants'; +import type { AgentResultElement, PageTrail } from '@flowforge/contract'; +import { PageTrailCollector } from '@flowforge/page-trail'; + +function collectPageTrail(): PageTrail { + return PageTrailCollector.collectFor(window, document, { + contentElementsLimit: constants.CONTENT_ELEMENTS_LIMIT, + interactiveElementsLimit: constants.INTERACTIVE_ELEMENTS_LIMIT, + getElementDataId: getOrCreateDataId, + }); +} export interface UsePageOptions { transport: TransportService; } +interface HighlightState { + id: string; + el: Element; + element: AgentResultElement; + stepIndex?: number; + duration: number; +} + +export interface HighlightViewModel extends HighlightState { + remove: () => void; +} + +interface WizardState { + title: string; + description: string; + steps: AgentResultElement[]; + currentStep: number; +} + +export interface WizardViewModel extends WizardState { + close: () => void; + changeStep: (step: number) => void; +} + +interface InspectorState { + pageTrail: PageTrail; +} + +export interface InspectorViewModel extends InspectorState { + close: () => void; +} + +export interface PageViewModel { + highlights: HighlightViewModel[]; + wizard: WizardViewModel | null; + inspector: InspectorViewModel | null; +} + export function usePage({ transport }: UsePageOptions): PageViewModel { const [highlights, setHighlights] = useState([]); const [wizard, setWizard] = useState(null); + const [inspector, setInspector] = useState(null); - // Clear all highlights and wizard - const clearPage = useCallback(() => { + const closeWizard = useCallback(() => { setHighlights([]); setWizard(null); }, []); + const closeInspector = useCallback(() => { + setInspector(null); + }, []); + + // Close wizard (with all highlights) and inspector + const clearPage = useCallback(() => { + closeWizard(); + closeInspector(); + }, [closeWizard, closeInspector]); + // Highlight an element const highlightElement = useCallback((element: AgentResultElement) => { - const el = findElement(document, element.dataId, element.selector); + const el = findElement(document, element.dataId, element.cssSelector); if (el) { setHighlights([ @@ -65,6 +121,11 @@ export function usePage({ transport }: UsePageOptions): PageViewModel { [highlightElement], ); + // Open inspector + const openInspector = useCallback((pageTrail: PageTrail) => { + setInspector({ pageTrail }); + }, []); + // Handle wizard step change const handleWizardChangeStep = useCallback((stepIndex: number) => { // Use functional update to avoid dependency on wizard @@ -77,7 +138,7 @@ export function usePage({ transport }: UsePageOptions): PageViewModel { // Show highlight for current step if (stepIndex > 0 && stepIndex <= prevWizard.steps.length) { const stepData = prevWizard.steps[stepIndex - 1]!; - const el = findElement(document, stepData.dataId, stepData.selector); + const el = findElement(document, stepData.dataId, stepData.cssSelector); if (el) { setHighlights([ @@ -97,26 +158,17 @@ export function usePage({ transport }: UsePageOptions): PageViewModel { }); }, []); - // Handle close wizard - const handleWizardClose = useCallback(() => { - clearPage(); - }, [clearPage]); - // Remove highlight by id const handleHighlightRemove = useCallback((id: string) => { setHighlights((prev) => prev.filter((h) => h.id !== id)); }, []); // Listen to messages from background - useEffect(() => { + useLayoutEffect(() => { return transport.addMessageListener((message: Message) => { - if (isCollectPageModelMessage(message)) { - const pageModel = PageModelCollector.collectFor(window, document, { - contentElementsLimit: constants.CONTENT_ELEMENTS_LIMIT, - interactiveElementsLimit: constants.INTERACTIVE_ELEMENTS_LIMIT, - getElementDataId: getOrCreateDataId, - }); - return { success: true, data: pageModel }; + if (isCollectPageTrailMessage(message)) { + const pageTrail = collectPageTrail(); + return { success: true, data: pageTrail }; } if (isClearPageMessage(message)) { clearPage(); @@ -130,9 +182,14 @@ export function usePage({ transport }: UsePageOptions): PageViewModel { highlightElement(message.data.element); return { success: true }; } + if (isOpenInspectorMessage(message)) { + const pageTrail = collectPageTrail(); + openInspector(pageTrail); + return { success: true }; + } return undefined; }); - }, [transport, startOnboarding, highlightElement, clearPage]); + }, [transport, startOnboarding, highlightElement, openInspector, clearPage]); return { highlights: highlights.map((highlight) => ({ @@ -142,9 +199,15 @@ export function usePage({ transport }: UsePageOptions): PageViewModel { wizard: wizard ? { ...wizard, - close: handleWizardClose, + close: closeWizard, changeStep: handleWizardChangeStep, } : null, + inspector: inspector + ? { + ...inspector, + close: closeInspector, + } + : null, }; } diff --git a/apps/extension/src/page/hooks/usePage.types.ts b/apps/extension/src/page/hooks/usePage.types.ts deleted file mode 100644 index 92b5a5a..0000000 --- a/apps/extension/src/page/hooks/usePage.types.ts +++ /dev/null @@ -1,34 +0,0 @@ -import type { AgentResultElement } from '@flowforge/contract'; - -export interface HighlightState { - id: string; - el: Element; - element: AgentResultElement; - stepIndex?: number; - duration: number; -} - -export interface HighlightActions { - remove: () => void; -} - -export type HighlightViewModel = HighlightState & HighlightActions; - -export interface WizardState { - title: string; - description: string; - steps: AgentResultElement[]; - currentStep: number; -} - -export interface WizardActions { - close: () => void; - changeStep: (step: number) => void; -} - -export type WizardViewModel = WizardState & WizardActions; - -export interface PageViewModel { - highlights: HighlightViewModel[]; - wizard: WizardViewModel | null; -} diff --git a/apps/extension/src/page/page.css b/apps/extension/src/page/page.css index ea224f8..9950c1c 100644 --- a/apps/extension/src/page/page.css +++ b/apps/extension/src/page/page.css @@ -2,8 +2,10 @@ @import './components/Highlight/Highlight.css'; @import './components/Wizard/Wizard.css'; +@import './components/Inspector/Inspector.css'; /* Page */ .flowforge-page { - + position: fixed; + pointer-events: none; } diff --git a/apps/extension/src/page/utils/position.ts b/apps/extension/src/page/utils/position.ts index c8a8aac..e2a725a 100644 --- a/apps/extension/src/page/utils/position.ts +++ b/apps/extension/src/page/utils/position.ts @@ -1,4 +1,4 @@ -import { constants } from '#self/constants'; +import { constants } from '@/constants'; interface Position { top: number; diff --git a/apps/extension/src/popup/PopupApp.tsx b/apps/extension/src/popup/PopupApp.tsx index 0e63646..6b47fa1 100644 --- a/apps/extension/src/popup/PopupApp.tsx +++ b/apps/extension/src/popup/PopupApp.tsx @@ -1,34 +1,46 @@ import { useCallback, useRef } from 'preact/hooks'; -import type { TransportService } from '#self/adapters/interface'; +import type { TransportService } from '@/adapters/interface'; import { usePopup } from './hooks/usePopup'; import { Question } from './components/Question'; import { Loading } from './components/Loading'; import { Result } from './components/Result'; import { Examples } from './components/Examples'; -import { ButtonText } from '#self/shared/components/Button'; -import { Link } from '#self/shared/components/Link'; -import { Notice } from '#self/shared/components/Notice'; -import type { ExtensionSettingsTheme } from '#self/types'; +import { Developer } from '@/popup/components/Developer'; +import { ButtonText } from '@/shared/components/Button'; +import { Link } from '@/shared/components/Link'; +import { Notice } from '@/shared/components/Notice'; +import type { ExtensionSettingsTheme } from '@/types'; -export type PopupAppDemoProps = { - enabled: true; - setup: { - topic?: string; - stubQuestions?: string[]; - }; -} | { enabled: false }; +export type PopupAppDemoProps = + | { + enabled: true; + setup: { + topic?: string; + stubQuestions?: string[]; + }; + } + | { enabled: false }; export interface PopupAppProps { variant: 'page' | 'dialog'; transport: TransportService; demoProps?: PopupAppDemoProps; theme: ExtensionSettingsTheme; - toggleTheme: () => Promise; + onToggleTheme: () => Promise; initialQuestion?: string; + onClose?: () => void; } -export function PopupApp({ variant, transport, demoProps, theme, toggleTheme, initialQuestion }: PopupAppProps) { +export function PopupApp({ + variant, + transport, + demoProps, + theme, + onToggleTheme, + initialQuestion, + onClose, +}: PopupAppProps) { const { question, setQuestion, @@ -42,6 +54,7 @@ export function PopupApp({ variant, transport, demoProps, theme, toggleTheme, in askQuestion, applyExampleQuestion, navigateToElement, + openPageInspector, } = usePopup({ transport, presetQuestions: demoProps?.enabled ? demoProps.setup.stubQuestions : undefined, @@ -63,6 +76,12 @@ export function PopupApp({ variant, transport, demoProps, theme, toggleTheme, in [applyExampleQuestion], ); + // Handle open page inspector and close popup + const handleOpenPageInspector = useCallback(() => { + openPageInspector(); + onClose?.(); + }, [openPageInspector, onClose]); + const isDialog = variant === 'dialog'; const Root = isDialog ? 'section' : 'div'; @@ -93,8 +112,8 @@ export function PopupApp({ variant, transport, demoProps, theme, toggleTheme, in )} )} - + + +
{copyright}
Star me - {theme === 'light' ? 'Dark' : 'Light'} theme + {theme === 'light' ? 'Dark' : 'Light'} theme
diff --git a/apps/extension/src/popup/components/Developer/Developer.css b/apps/extension/src/popup/components/Developer/Developer.css new file mode 100644 index 0000000..adf8795 --- /dev/null +++ b/apps/extension/src/popup/components/Developer/Developer.css @@ -0,0 +1,16 @@ +/* Developer */ +.flowforge-developer-list { +} +.flowforge-developer-list ul { + list-style: none; + display: flex; + flex-direction: column; + margin: 0; + padding: 0; + gap: var(--flowforge-pad-xs); +} +.flowforge-developer-list li { + margin: 0; + padding: 0; + font: var(--flowforge-text-sm); +} diff --git a/apps/extension/src/popup/components/Developer/Developer.tsx b/apps/extension/src/popup/components/Developer/Developer.tsx new file mode 100644 index 0000000..883db96 --- /dev/null +++ b/apps/extension/src/popup/components/Developer/Developer.tsx @@ -0,0 +1,26 @@ +import { Card } from '@/shared/components/Card'; +import { ButtonText } from '@/shared/components/Button'; + +interface DeveloperProps { + onOpenPageInspector: () => void; +} + +export function Developer({ onOpenPageInspector }: DeveloperProps) { + return ( + +
+
    +
  • + + Inspect page context + +
  • +
+
+
+ ); +} diff --git a/apps/extension/src/popup/components/Developer/index.ts b/apps/extension/src/popup/components/Developer/index.ts new file mode 100644 index 0000000..43044c9 --- /dev/null +++ b/apps/extension/src/popup/components/Developer/index.ts @@ -0,0 +1 @@ +export { Developer } from './Developer'; diff --git a/apps/extension/src/popup/components/Examples/Examples.css b/apps/extension/src/popup/components/Examples/Examples.css index 2ea06f3..da7d11b 100644 --- a/apps/extension/src/popup/components/Examples/Examples.css +++ b/apps/extension/src/popup/components/Examples/Examples.css @@ -2,38 +2,38 @@ .flowforge-examples-list { } .flowforge-examples-list ul { - list-style: none; - display: flex; - flex-wrap: wrap; - margin: 0; - padding: 0; - gap: var(--flowforge-pad-xs); + list-style: none; + display: flex; + flex-wrap: wrap; + margin: 0; + padding: 0; + gap: var(--flowforge-pad-xs); } .flowforge-examples-list li { - margin: 0; - padding: 0; + margin: 0; + padding: 0; } .flowforge-example-chip { - display: inline-flex; - align-items: center; - padding: var(--flowforge-pad-xs) var(--flowforge-pad-sm); - border-radius: var(--flowforge-pad-xs); - background: var(--flowforge-color-surface); - border: 1px solid var(--flowforge-color-border); - color: var(--flowforge-color-text-muted); - font: var(--flowforge-text-sm); - line-height: 1; - cursor: pointer; - transition: var(--flowforge-transition-default); + display: inline-flex; + align-items: center; + padding: var(--flowforge-pad-xs) var(--flowforge-pad-sm); + border-radius: var(--flowforge-pad-xs); + background: var(--flowforge-color-surface); + border: 1px solid var(--flowforge-color-border); + color: var(--flowforge-color-text-muted); + font: var(--flowforge-text-sm); + line-height: 1; + cursor: pointer; + transition: var(--flowforge-transition-default); } .flowforge-example-chip:hover { - background: var(--flowforge-color-bg); + background: var(--flowforge-color-bg); } .flowforge-example-chip--primary:hover { - border-color: var(--flowforge-color-primary-hover); - color: var(--flowforge-color-primary-hover); + border-color: var(--flowforge-color-primary-hover); + color: var(--flowforge-color-primary-hover); } .flowforge-example-chip--secondary:hover { - border-color: var(--flowforge-color-secondary-hover); - color: var(--flowforge-color-secondary-hover); + border-color: var(--flowforge-color-secondary-hover); + color: var(--flowforge-color-secondary-hover); } diff --git a/apps/extension/src/popup/components/Examples/Examples.tsx b/apps/extension/src/popup/components/Examples/Examples.tsx index 1e9e67a..ba2ab89 100644 --- a/apps/extension/src/popup/components/Examples/Examples.tsx +++ b/apps/extension/src/popup/components/Examples/Examples.tsx @@ -1,7 +1,12 @@ -import type { ExamplesViewModel } from '#self/popup/hooks/usePopup.types'; -import { Card } from '#self/shared/components/Card'; +import type { PopupExampleItem } from '@/popup/utils/data'; +import { Card } from '@/shared/components/Card'; -export function Examples({ examples, applyExampleQuestion }: ExamplesViewModel) { +interface ExamplesProps { + examples: PopupExampleItem[]; + onExampleQuestionSelect: (question: string) => void; +} + +export function Examples({ examples, onExampleQuestionSelect }: ExamplesProps) { if (examples.length === 0) { return null; } @@ -16,7 +21,7 @@ export function Examples({ examples, applyExampleQuestion }: ExamplesViewModel) className={`flowforge-example-chip flowforge-example-chip--${ example.type === 'default' ? 'primary' : 'secondary' }`} - onClick={() => applyExampleQuestion(example.question)} + onClick={() => onExampleQuestionSelect(example.question)} > {example.question} diff --git a/apps/extension/src/popup/components/Loading/Loading.css b/apps/extension/src/popup/components/Loading/Loading.css index 295a039..1437302 100644 --- a/apps/extension/src/popup/components/Loading/Loading.css +++ b/apps/extension/src/popup/components/Loading/Loading.css @@ -1,185 +1,276 @@ /* Loading */ .flowforge-loading { - border: 1px solid var(--flowforge-color-border); - display: flex; - flex-direction: column; - border-radius: var(--flowforge-pad-sm); - padding: var(--flowforge-pad-lg); - background: linear-gradient( - 180deg, - rgba(var(--flowforge-rgb-primary), 0.06), - rgba(var(--flowforge-rgb-primary), 0.03) - ); - box-shadow: 0 4px 12px rgba(var(--flowforge-rgb-shadow), 0.06); + border: 1px solid var(--flowforge-color-border); + display: flex; + flex-direction: column; + border-radius: var(--flowforge-pad-sm); + padding: var(--flowforge-pad-lg); + background: linear-gradient( + 180deg, + rgba(var(--flowforge-rgb-primary), 0.06), + rgba(var(--flowforge-rgb-primary), 0.03) + ); + box-shadow: 0 4px 12px rgba(var(--flowforge-rgb-shadow), 0.06); } .flowforge-loading__content { - position: relative; - display: flex; - justify-content: center; - align-items: center; + position: relative; + display: flex; + justify-content: center; + align-items: center; } .flowforge-loading__text { - position: absolute; - margin: 0; - bottom: 4px; - left: 50%; - transform: translateX(-50%); - font: var(--flowforge-text-sm); - color: var(--flowforge-color-text-muted); - white-space: nowrap; - pointer-events: none; + position: absolute; + margin: 0; + bottom: 4px; + left: 50%; + transform: translateX(-50%); + font: var(--flowforge-text-sm); + color: var(--flowforge-color-text-muted); + white-space: nowrap; + pointer-events: none; } .flowforge-cosmic-loader { - position: relative; - width: 80px; - height: 80px; + position: relative; + width: 80px; + height: 80px; } .flowforge-cosmic-loader__core { - position: absolute; - inset: 50% auto auto 50%; - width: 16px; - height: 16px; - transform: translate(-50%, -50%); - border-radius: 50%; - background: var(--flowforge-color-secondary); - box-shadow: - 0 0 12px rgba(var(--flowforge-rgb-secondary), 0.9), - 0 0 28px rgba(var(--flowforge-rgb-secondary), 0.5), - 0 0 48px rgba(var(--flowforge-rgb-secondary), 0.25); - animation: flowforge-core-pulse 1.2s ease-in-out infinite; + position: absolute; + inset: 50% auto auto 50%; + width: 16px; + height: 16px; + transform: translate(-50%, -50%); + border-radius: 50%; + background: var(--flowforge-color-secondary); + box-shadow: + 0 0 12px rgba(var(--flowforge-rgb-secondary), 0.9), + 0 0 28px rgba(var(--flowforge-rgb-secondary), 0.5), + 0 0 48px rgba(var(--flowforge-rgb-secondary), 0.25); + animation: flowforge-core-pulse 1.2s ease-in-out infinite; } .flowforge-cosmic-loader__orbit { - position: absolute; - inset: 0; - animation: flowforge-orbit-rotate 3.2s linear infinite; + position: absolute; + inset: 0; + animation: flowforge-orbit-rotate 3.2s linear infinite; } .flowforge-cosmic-loader__star { - position: absolute; - color: var(--flowforge-color-secondary); - font-size: 12px; - text-shadow: - 0 0 6px rgba(var(--flowforge-rgb-secondary), 0.7), - 0 0 12px rgba(var(--flowforge-rgb-secondary), 0.35); - animation: flowforge-star-twinkle 1.6s ease-in-out infinite; + position: absolute; + color: var(--flowforge-color-secondary); + font-size: 12px; + text-shadow: + 0 0 6px rgba(var(--flowforge-rgb-secondary), 0.7), + 0 0 12px rgba(var(--flowforge-rgb-secondary), 0.35); + animation: flowforge-star-twinkle 1.6s ease-in-out infinite; } -.flowforge-s1 { top: 4px; left: 50%; transform: translateX(-50%); animation-delay: 0s; } -.flowforge-s2 { right: 6px; top: 50%; transform: translateY(-50%); animation-delay: 0.3s; } -.flowforge-s3 { bottom: 6px; left: 50%; transform: translateX(-50%); animation-delay: 0.6s; } -.flowforge-s4 { left: 6px; top: 50%; transform: translateY(-50%); animation-delay: 0.9s; } +.flowforge-s1 { + top: 4px; + left: 50%; + transform: translateX(-50%); + animation-delay: 0s; +} +.flowforge-s2 { + right: 6px; + top: 50%; + transform: translateY(-50%); + animation-delay: 0.3s; +} +.flowforge-s3 { + bottom: 6px; + left: 50%; + transform: translateX(-50%); + animation-delay: 0.6s; +} +.flowforge-s4 { + left: 6px; + top: 50%; + transform: translateY(-50%); + animation-delay: 0.9s; +} .flowforge-cosmic-loader__sparks { - position: absolute; - inset: 0; + position: absolute; + inset: 0; } .flowforge-spark { - position: absolute; - left: 50%; - top: 50%; - width: 4px; - height: 4px; - margin: -2px; - border-radius: 50%; - background: var(--flowforge-color-secondary); - box-shadow: - 0 0 6px rgba(var(--flowforge-rgb-secondary), 0.9), - 0 0 12px rgba(var(--flowforge-rgb-secondary), 0.4); + position: absolute; + left: 50%; + top: 50%; + width: 4px; + height: 4px; + margin: -2px; + border-radius: 50%; + background: var(--flowforge-color-secondary); + box-shadow: + 0 0 6px rgba(var(--flowforge-rgb-secondary), 0.9), + 0 0 12px rgba(var(--flowforge-rgb-secondary), 0.4); - opacity: 0; + opacity: 0; } -.flowforge-k1 { animation: flowforge-spark-1 1.2s ease-out infinite; } -.flowforge-k2 { animation: flowforge-spark-2 1.2s ease-out infinite 0.15s; } -.flowforge-k3 { animation: flowforge-spark-3 1.2s ease-out infinite 0.3s; } -.flowforge-k4 { animation: flowforge-spark-4 1.2s ease-out infinite 0.45s; } -.flowforge-k5 { animation: flowforge-spark-5 1.2s ease-out infinite 0.6s; } +.flowforge-k1 { + animation: flowforge-spark-1 1.2s ease-out infinite; +} +.flowforge-k2 { + animation: flowforge-spark-2 1.2s ease-out infinite 0.15s; +} +.flowforge-k3 { + animation: flowforge-spark-3 1.2s ease-out infinite 0.3s; +} +.flowforge-k4 { + animation: flowforge-spark-4 1.2s ease-out infinite 0.45s; +} +.flowforge-k5 { + animation: flowforge-spark-5 1.2s ease-out infinite 0.6s; +} .flowforge-cosmic-loader__flash-stars { - position: absolute; - inset: 0; - pointer-events: none; + position: absolute; + inset: 0; + pointer-events: none; } .flowforge-flash-star { - position: absolute; - color: var(--flowforge-color-primary); - font-size: 14px; - opacity: 0; + position: absolute; + color: var(--flowforge-color-primary); + font-size: 14px; + opacity: 0; - text-shadow: - 0 0 6px rgba(var(--flowforge-rgb-primary), 0.9), - 0 0 16px rgba(var(--flowforge-rgb-primary), 0.6), - 0 0 28px rgba(var(--flowforge-rgb-primary), 0.35); + text-shadow: + 0 0 6px rgba(var(--flowforge-rgb-primary), 0.9), + 0 0 16px rgba(var(--flowforge-rgb-primary), 0.6), + 0 0 28px rgba(var(--flowforge-rgb-primary), 0.35); } -.flowforge-f1 { top: 8px; left: 12px; } -.flowforge-f2 { top: 16px; right: 10px; } -.flowforge-f3 { bottom: 18px; left: 22px; } +.flowforge-f1 { + top: 8px; + left: 12px; +} +.flowforge-f2 { + top: 16px; + right: 10px; +} +.flowforge-f3 { + bottom: 18px; + left: 22px; +} -.flowforge-f1 { animation: flowforge-flash-star 6s linear infinite; } -.flowforge-f2 { animation: flowforge-flash-star 7s linear infinite 1.5s; } -.flowforge-f3 { animation: flowforge-flash-star 5.5s linear infinite 3s; } +.flowforge-f1 { + animation: flowforge-flash-star 6s linear infinite; +} +.flowforge-f2 { + animation: flowforge-flash-star 7s linear infinite 1.5s; +} +.flowforge-f3 { + animation: flowforge-flash-star 5.5s linear infinite 3s; +} @keyframes flowforge-core-pulse { - 0%, 100% { - transform: translate(-50%, -50%) scale(0.9); - opacity: 0.9; - } - 50% { - transform: translate(-50%, -50%) scale(1.2); - opacity: 1; - } + 0%, + 100% { + transform: translate(-50%, -50%) scale(0.9); + opacity: 0.9; + } + 50% { + transform: translate(-50%, -50%) scale(1.2); + opacity: 1; + } } @keyframes flowforge-orbit-rotate { - to { transform: rotate(360deg); } + to { + transform: rotate(360deg); + } } @keyframes flowforge-star-twinkle { - 0%, 100% { - opacity: 0.5; - transform: scale(0.85); - } - 50% { - opacity: 1; - transform: scale(1.25); - } + 0%, + 100% { + opacity: 0.5; + transform: scale(0.85); + } + 50% { + opacity: 1; + transform: scale(1.25); + } } @keyframes flowforge-spark-1 { - 0% { transform: translate(0, 0); opacity: 0; } - 20% { opacity: 1; } - 100% { transform: translate(-30px, -20px); opacity: 0; } + 0% { + transform: translate(0, 0); + opacity: 0; + } + 20% { + opacity: 1; + } + 100% { + transform: translate(-30px, -20px); + opacity: 0; + } } @keyframes flowforge-spark-2 { - 0% { transform: translate(0, 0); opacity: 0; } - 20% { opacity: 1; } - 100% { transform: translate(24px, -28px); opacity: 0; } + 0% { + transform: translate(0, 0); + opacity: 0; + } + 20% { + opacity: 1; + } + 100% { + transform: translate(24px, -28px); + opacity: 0; + } } @keyframes flowforge-spark-3 { - 0% { transform: translate(0, 0); opacity: 0; } - 20% { opacity: 1; } - 100% { transform: translate(-18px, 30px); opacity: 0; } + 0% { + transform: translate(0, 0); + opacity: 0; + } + 20% { + opacity: 1; + } + 100% { + transform: translate(-18px, 30px); + opacity: 0; + } } @keyframes flowforge-spark-4 { - 0% { transform: translate(0, 0); opacity: 0; } - 20% { opacity: 1; } - 100% { transform: translate(28px, 18px); opacity: 0; } + 0% { + transform: translate(0, 0); + opacity: 0; + } + 20% { + opacity: 1; + } + 100% { + transform: translate(28px, 18px); + opacity: 0; + } } @keyframes flowforge-spark-5 { - 0% { transform: translate(0, 0); opacity: 0; } - 20% { opacity: 1; } - 100% { transform: translate(0, -34px); opacity: 0; } + 0% { + transform: translate(0, 0); + opacity: 0; + } + 20% { + opacity: 1; + } + 100% { + transform: translate(0, -34px); + opacity: 0; + } } @keyframes flowforge-flash-star { - 0%, 85%, 100% { - opacity: 0; - transform: scale(0.6) translate(0, 0); - } - 88% { - opacity: 1; - transform: scale(1.4) translate(2px, -3px); - } - 92% { - opacity: 0; - transform: scale(0.8) translate(-1px, 2px); - } + 0%, + 85%, + 100% { + opacity: 0; + transform: scale(0.6) translate(0, 0); + } + 88% { + opacity: 1; + transform: scale(1.4) translate(2px, -3px); + } + 92% { + opacity: 0; + transform: scale(0.8) translate(-1px, 2px); + } } diff --git a/apps/extension/src/popup/components/Question/Question.css b/apps/extension/src/popup/components/Question/Question.css index 48145c6..5950ced 100644 --- a/apps/extension/src/popup/components/Question/Question.css +++ b/apps/extension/src/popup/components/Question/Question.css @@ -1,27 +1,27 @@ /* Question form */ .flowforge-input-textarea { - width: 100%; - padding: var(--flowforge-pad-lg); - border: 1px solid var(--flowforge-color-border); - border-radius: var(--flowforge-pad-sm); - resize: vertical; - font: var(--flowforge-text-md); - color: var(--flowforge-color-text); - background: rgba(var(--flowforge-rgb-secondary), 0.04); - transition: var(--flowforge-transition-default); + width: 100%; + padding: var(--flowforge-pad-lg); + border: 1px solid var(--flowforge-color-border); + border-radius: var(--flowforge-pad-sm); + resize: vertical; + font: var(--flowforge-text-md); + color: var(--flowforge-color-text); + background: rgba(var(--flowforge-rgb-secondary), 0.04); + transition: var(--flowforge-transition-default); } .flowforge-input-textarea::placeholder { - color: var(--flowforge-color-text-muted); + color: var(--flowforge-color-text-muted); } .flowforge-input-textarea:hover { - background: rgba(var(--flowforge-rgb-secondary), 0.06); + background: rgba(var(--flowforge-rgb-secondary), 0.06); } .flowforge-input-textarea:focus { - outline: none; - border-color: var(--flowforge-color-secondary); - background: var(--flowforge-color-surface); - box-shadow: 0 0 0 4px rgba(var(--flowforge-rgb-secondary), 0.16); + outline: none; + border-color: var(--flowforge-color-secondary); + background: var(--flowforge-color-surface); + box-shadow: 0 0 0 4px rgba(var(--flowforge-rgb-secondary), 0.16); } .flowforge-question-form > .flowforge-button { - margin-top: var(--flowforge-pad-sm); + margin-top: var(--flowforge-pad-sm); } diff --git a/apps/extension/src/popup/components/Question/Question.tsx b/apps/extension/src/popup/components/Question/Question.tsx index 40a0976..ba09008 100644 --- a/apps/extension/src/popup/components/Question/Question.tsx +++ b/apps/extension/src/popup/components/Question/Question.tsx @@ -1,16 +1,25 @@ -import type { QuestionViewModel } from '#self/popup/hooks/usePopup.types'; import type { TargetedInputEvent, TargetedSubmitEvent } from 'preact'; -import { Button } from '#self/shared/components/Button'; -import { Card } from '#self/shared/components/Card'; +import { Button } from '@/shared/components/Button'; +import { Card } from '@/shared/components/Card'; import { useEffect, useRef } from 'preact/hooks'; -interface QuestionProps extends QuestionViewModel { +interface QuestionProps { + question: string; + onQuestionChange: (value: string) => void; + onAskQuestion: () => Promise; placeholder: string; selectOnly: boolean; disabled: boolean; } -export function Question({ question, setQuestion, askQuestion, placeholder, selectOnly, disabled }: QuestionProps) { +export function Question({ + question, + onQuestionChange, + onAskQuestion, + placeholder, + selectOnly, + disabled, +}: QuestionProps) { const textareaRef = useRef(null); useEffect(() => { @@ -18,17 +27,17 @@ export function Question({ question, setQuestion, askQuestion, placeholder, sele }, []); const handleOnInput = (e: TargetedInputEvent) => { - setQuestion((e.currentTarget as HTMLTextAreaElement).value); + onQuestionChange((e.currentTarget as HTMLTextAreaElement).value); }; const handleOnKeyDown = (e: KeyboardEvent) => { if (e.key === 'Enter' && !e.shiftKey) { e.preventDefault(); - askQuestion(); + void onAskQuestion(); } }; const handleOnSubmit = (e: TargetedSubmitEvent) => { e.preventDefault(); - askQuestion(); + void onAskQuestion(); }; return ( diff --git a/apps/extension/src/popup/components/Result/Result.css b/apps/extension/src/popup/components/Result/Result.css index 8fa8fc7..0bff635 100644 --- a/apps/extension/src/popup/components/Result/Result.css +++ b/apps/extension/src/popup/components/Result/Result.css @@ -1,63 +1,63 @@ /* Elements */ .flowforge-elements { - display: flex; - flex-direction: column; - gap: var(--flowforge-pad-sm); + display: flex; + flex-direction: column; + gap: var(--flowforge-pad-sm); } .flowforge-elements__title { - margin: 0; - font: var(--flowforge-text-h4); - color: var(--flowforge-color-text); + margin: 0; + font: var(--flowforge-text-h4); + color: var(--flowforge-color-text); } .flowforge-elements-list { - display: flex; - flex-direction: column; - gap: var(--flowforge-pad-sm); + display: flex; + flex-direction: column; + gap: var(--flowforge-pad-sm); } .flowforge-element-item { - display: block; - text-align: left; - padding: var(--flowforge-pad-sm) var(--flowforge-pad-lg); - border-radius: var(--flowforge-pad-sm); - background: var(--flowforge-color-bg); - border: 1px solid var(--flowforge-color-border); - color: var(--flowforge-color-text-muted); - font: var(--flowforge-text-sm); + display: block; + text-align: left; + padding: var(--flowforge-pad-sm) var(--flowforge-pad-lg); + border-radius: var(--flowforge-pad-sm); + background: var(--flowforge-color-bg); + border: 1px solid var(--flowforge-color-border); + color: var(--flowforge-color-text-muted); + font: var(--flowforge-text-sm); } .flowforge-element-item > span { - font: inherit; - color: inherit; - font-weight: 600; + font: inherit; + color: inherit; + font-weight: 600; } .flowforge-element-item--chip { - cursor: pointer; - transition: var(--flowforge-transition-default); + cursor: pointer; + transition: var(--flowforge-transition-default); } .flowforge-element-item--chip:hover { - border-color: var(--flowforge-color-primary-hover); + border-color: var(--flowforge-color-primary-hover); } .flowforge-elements-list--steps { - gap: 0; + gap: 0; } .flowforge-elements-list--steps .flowforge-element-item { - border-radius: 0; + border-radius: 0; } .flowforge-elements-list--steps .flowforge-element-item:first-child { - border-top-left-radius: var(--flowforge-pad-sm); - border-top-right-radius: var(--flowforge-pad-sm); + border-top-left-radius: var(--flowforge-pad-sm); + border-top-right-radius: var(--flowforge-pad-sm); } .flowforge-elements-list--steps .flowforge-element-item:last-child { - border-bottom-left-radius: var(--flowforge-pad-sm); - border-bottom-right-radius: var(--flowforge-pad-sm); + border-bottom-left-radius: var(--flowforge-pad-sm); + border-bottom-right-radius: var(--flowforge-pad-sm); } .flowforge-elements-list--steps .flowforge-element-item + .flowforge-element-item { - border-top: none; + border-top: none; } .flowforge-result-metadata { - display: flex; - justify-content: center; - font: 400 11px / 1.6 var(--flowforge-font-family); - color: var(--flowforge-color-text-muted); - opacity: 0.8; - flex-wrap: wrap; + display: flex; + justify-content: center; + font: 400 11px / 1.6 var(--flowforge-font-family); + color: var(--flowforge-color-text-muted); + opacity: 0.8; + flex-wrap: wrap; } diff --git a/apps/extension/src/popup/components/Result/Result.tsx b/apps/extension/src/popup/components/Result/Result.tsx index 85fced9..5826425 100644 --- a/apps/extension/src/popup/components/Result/Result.tsx +++ b/apps/extension/src/popup/components/Result/Result.tsx @@ -1,10 +1,17 @@ import { ResultElements } from './ResultElements'; -import type { ResultViewModel } from '#self/popup/hooks/usePopup.types'; -import { Card } from '#self/shared/components/Card'; +import { Card } from '@/shared/components/Card'; +import type { AgentResult, AgentResultElement } from '@flowforge/contract'; -export function Result({ result, resultMetadata, error, navigateToElement }: ResultViewModel) { +interface ResultProps { + result: AgentResult | null; + resultMetadata: string | null; + error: string | null; + onNavigateToElement: (element: AgentResultElement) => void; +} + +export function Result({ result, resultMetadata, error, onNavigateToElement }: ResultProps) { if (error) { - return ; + return ; } if (result) { return ( @@ -13,7 +20,7 @@ export function Result({ result, resultMetadata, error, navigateToElement }: Res )} {resultMetadata && ( diff --git a/apps/extension/src/popup/components/Result/ResultElements.tsx b/apps/extension/src/popup/components/Result/ResultElements.tsx index 2c12e53..d5c7863 100644 --- a/apps/extension/src/popup/components/Result/ResultElements.tsx +++ b/apps/extension/src/popup/components/Result/ResultElements.tsx @@ -3,10 +3,10 @@ import type { AgentResultElement, AgentResultMode } from '@flowforge/contract'; interface ResultElementsProps { elements: AgentResultElement[]; mode: AgentResultMode; - navigateToElement: (element: AgentResultElement) => void; + onNavigateToElement: (element: AgentResultElement) => void; } -export function ResultElements({ elements, mode, navigateToElement }: ResultElementsProps) { +export function ResultElements({ elements, mode, onNavigateToElement }: ResultElementsProps) { const isSteps = mode === 'steps'; return ( @@ -23,7 +23,7 @@ export function ResultElements({ elements, mode, navigateToElement }: ResultElem key={i} type="button" className="flowforge-element-item flowforge-element-item--chip" - onClick={() => navigateToElement(element)} + onClick={() => onNavigateToElement(element)} > {element.text} diff --git a/apps/extension/src/popup/hooks/usePopup.ts b/apps/extension/src/popup/hooks/usePopup.ts index 398c51f..f5aa57f 100644 --- a/apps/extension/src/popup/hooks/usePopup.ts +++ b/apps/extension/src/popup/hooks/usePopup.ts @@ -6,15 +6,16 @@ import type { GetPrevQuestionsMessageResponse, MessageResponse, NavigateToElementMessage, -} from '#self/types'; -import { config } from '#self/config'; -import { normalizeText } from '#self/core/utils/text'; -import { buildPresetExampleItems, type PopupExampleItem } from '#self/popup/utils/data'; -import { buildTryExampleItems } from '#self/popup/utils/data'; -import type { PopupViewModel } from './usePopup.types'; -import type { TransportService } from '#self/adapters/interface'; + OpenPageInspectorMessage, + PopupInitializeMessage, +} from '@/types'; +import { config } from '@/config'; +import { normalizeText } from '@/core/utils/text'; +import { buildPresetExampleItems, type PopupExampleItem } from '@/popup/utils/data'; +import { buildTryExampleItems } from '@/popup/utils/data'; +import type { TransportService } from '@/adapters/interface'; import type { AgentResult, AgentResultElement } from '@flowforge/contract'; -import { formatQueryResponseMetadata } from '#self/popup/utils/format'; +import { formatQueryResponseMetadata } from '@/popup/utils/format'; export interface UsePopupOptions { transport: TransportService; @@ -22,6 +23,22 @@ export interface UsePopupOptions { initialQuestion?: string; } +export interface PopupViewModel { + question: string; + setQuestion: (value: string) => void; + isLoading: boolean; + result: AgentResult | null; + resultMetadata: string | null; + error: string | null; + examples: PopupExampleItem[]; + copyright: string; + github: string; + askQuestion: () => Promise; + applyExampleQuestion: (question: string) => void; + navigateToElement: (element: AgentResultElement) => void; + openPageInspector: () => void; +} + export function usePopup({ transport, presetQuestions, initialQuestion }: UsePopupOptions): PopupViewModel { const [question, setQuestion] = useState(''); const [isLoading, setIsLoading] = useState(false); @@ -30,6 +47,21 @@ export function usePopup({ transport, presetQuestions, initialQuestion }: UsePop const [error, setError] = useState(null); const [examples, setExamples] = useState([]); + // Clear page UI state when popup opens. + useEffect(() => { + void (async () => { + try { + const message: PopupInitializeMessage = { + type: 'POPUP_INITIALISE', + senderId: await transport.getActiveSenderId(), + }; + await transport.sendToBackground(message); + } catch { + // Popup startup should not fail if the active tab cannot receive page messages. + } + })(); + }, [transport]); + // Initialize questions examples useEffect(() => { if (presetQuestions && presetQuestions.length > 0) { @@ -62,6 +94,7 @@ export function usePopup({ transport, presetQuestions, initialQuestion }: UsePop void loadPreviousQuestions(); }, [presetQuestions, transport]); + // Initialize initial question if presented useEffect(() => { if (initialQuestion !== undefined) { setQuestion(initialQuestion); @@ -131,6 +164,15 @@ export function usePopup({ transport, presetQuestions, initialQuestion }: UsePop [transport], ); + // Handle open inspector + const handleOpenPageInspector = useCallback(async () => { + const message: OpenPageInspectorMessage = { + type: 'OPEN_PAGE_INSPECTOR', + senderId: await transport.getActiveSenderId(), + }; + await transport.sendToBackground(message); + }, [transport]); + return { question, isLoading, @@ -145,5 +187,6 @@ export function usePopup({ transport, presetQuestions, initialQuestion }: UsePop askQuestion: handleAskQuestion, applyExampleQuestion: handleSelectExample, navigateToElement: handleNavigateToElement, + openPageInspector: handleOpenPageInspector, }; } diff --git a/apps/extension/src/popup/hooks/usePopup.types.ts b/apps/extension/src/popup/hooks/usePopup.types.ts deleted file mode 100644 index 603fcf3..0000000 --- a/apps/extension/src/popup/hooks/usePopup.types.ts +++ /dev/null @@ -1,45 +0,0 @@ -import type { PopupExampleItem } from '#self/popup/utils/data'; -import type { AgentResult, AgentResultElement } from '@flowforge/contract'; - -export interface QuestionState { - question: string; -} - -export interface QuestionActions { - setQuestion: (value: string) => void; - askQuestion: () => Promise; -} - -export type QuestionViewModel = QuestionState & QuestionActions; - -export interface ExamplesState { - examples: PopupExampleItem[]; -} - -export interface ExamplesActions { - applyExampleQuestion: (question: string) => void; -} - -export type ExamplesViewModel = ExamplesState & ExamplesActions; - -export interface ResultState { - result: AgentResult | null; - resultMetadata: string | null; - error: string | null; -} - -export interface ResultActions { - navigateToElement: (element: AgentResultElement) => void; -} - -export type ResultViewModel = ResultState & ResultActions; - -export interface PopupState extends QuestionState, ExamplesState, ResultState { - isLoading: boolean; - copyright: string; - github: string; -} - -export type PopupActions = QuestionActions & ExamplesActions & ResultActions; - -export type PopupViewModel = PopupState & PopupActions; diff --git a/apps/extension/src/popup/popup.css b/apps/extension/src/popup/popup.css index 967cd88..6a8520e 100644 --- a/apps/extension/src/popup/popup.css +++ b/apps/extension/src/popup/popup.css @@ -4,89 +4,116 @@ @import './components/Loading/Loading.css'; @import './components/Result/Result.css'; @import './components/Examples/Examples.css'; +@import './components/Developer/Developer.css'; /* Popup */ .flowforge-popup { - --flowforge-popup-width: 400px; - --flowforge-popup-height: 560px; - --flowforge-popup-header-height: 80px; + --flowforge-popup-width: 400px; + --flowforge-popup-height: 560px; + --flowforge-popup-header-height: 80px; } .flowforge-popup { - width: var(--flowforge-popup-width); - height: var(--flowforge-popup-height); - overflow: hidden; - display: flex; - flex-direction: column; - background: var(--flowforge-color-bg); - pointer-events: auto; + width: var(--flowforge-popup-width); + height: var(--flowforge-popup-height); + overflow: hidden; + display: flex; + flex-direction: column; + background: var(--flowforge-color-bg); + pointer-events: auto; + position: static; } .flowforge-popup__header { - position: relative; - z-index: 1; - margin: 0; - flex-shrink: 0; - height: var(--flowforge-popup-header-height); - display: flex; - flex-direction: column; - justify-content: center; - align-items: center; - text-align: center; - color: var(--flowforge-color-text-on-dark); - box-shadow: 0 4px 12px rgba(var(--flowforge-rgb-shadow), 0.4); - background: - url('../shared/img/header-bg-sm.webp') right bottom no-repeat, - linear-gradient(135deg, var(--flowforge-color-primary) 0%, var(--flowforge-color-primary-hover) 100%); + position: relative; + z-index: 1; + margin: 0; + flex-shrink: 0; + height: var(--flowforge-popup-header-height); + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + text-align: center; + color: var(--flowforge-color-text-on-dark); + background: + url('../shared/img/header-bg-sm.webp') right bottom no-repeat, + linear-gradient( + 135deg, + var(--flowforge-color-primary) 0%, + var(--flowforge-color-primary-hover) 100% + ); +} +.flowforge-popup__header::after { + content: ''; + position: absolute; + right: 0; + bottom: -12px; + left: 0; + height: 12px; + background: linear-gradient( + rgba(var(--flowforge-rgb-shadow), 0.4), + rgba(var(--flowforge-rgb-shadow), 0) + ); + pointer-events: none; } .flowforge-popup__header--parallax { - background-position: center center, 0 0; - animation: flowforge-header-parallax 60s ease-in-out infinite alternate; + background-position: + center center, + 0 0; + animation: flowforge-header-parallax 60s ease-in-out infinite alternate; } @keyframes flowforge-header-parallax { - 0% { - background-position: calc(50% - 95px) calc(50% - 15px), 0 0; - } - 50% { - background-position: 50% calc(50% + 15px), 0 0; - } - 100% { - background-position: calc(50% + 95px) calc(50% - 15px), 0 0; - } + 0% { + background-position: + calc(50% - 95px) calc(50% - 15px), + 0 0; + } + 50% { + background-position: + 50% calc(50% + 15px), + 0 0; + } + 100% { + background-position: + calc(50% + 95px) calc(50% - 15px), + 0 0; + } } .flowforge-popup__header-title { - font: var(--flowforge-text-h2); - position: relative; + font: var(--flowforge-text-h2); + position: relative; } .flowforge-popup__header-subtitle { - margin-top: 2px; - font: var(--flowforge-text-lg); - opacity: 0.9; - position: relative; + margin-top: 2px; + font: var(--flowforge-text-lg); + opacity: 0.9; + position: relative; } .flowforge-popup__content { - flex: 1; - overflow-y: auto; - padding: var(--flowforge-pad-xl); - display: flex; - flex-direction: column; - gap: var(--flowforge-pad-lg); + flex: 1; + margin-top: -1px; + overflow-y: auto; + padding: var(--flowforge-pad-xl); + display: flex; + flex-direction: column; + gap: var(--flowforge-pad-lg); } .flowforge-popup__content::-webkit-scrollbar { - width: var(--flowforge-pad-xs); + width: var(--flowforge-pad-xs); } .flowforge-popup__content::-webkit-scrollbar-thumb { - background: rgba(var(--flowforge-rgb-primary), 0.5); - border-radius: 999px; + background: rgba(var(--flowforge-rgb-primary), 0.5); + border-radius: 999px; } .flowforge-popup__footer { - display: flex; - justify-content: center; - padding-top: var(--flowforge-pad-sm); - margin-top: auto; - font: var(--flowforge-text-xs); - gap: var(--flowforge-pad-md); + display: flex; + justify-content: center; + padding-top: var(--flowforge-pad-sm); + margin-top: auto; + font: var(--flowforge-text-xs); + gap: var(--flowforge-pad-md); } .flowforge-popup__copyright { - color: var(--flowforge-color-text-muted); - opacity: 0.8; - flex-wrap: wrap; + color: var(--flowforge-color-text-muted); + opacity: 0.8; + flex-wrap: wrap; } diff --git a/apps/extension/src/popup/utils/data.ts b/apps/extension/src/popup/utils/data.ts index 517d3e4..37da71b 100644 --- a/apps/extension/src/popup/utils/data.ts +++ b/apps/extension/src/popup/utils/data.ts @@ -1,5 +1,5 @@ -import { constants } from '#self/constants'; -import { normalizeText } from '#self/core/utils/text'; +import { constants } from '@/constants'; +import { normalizeText } from '@/core/utils/text'; export interface PopupExampleItem { question: string; diff --git a/apps/extension/src/shared/components/Button/Button.css b/apps/extension/src/shared/components/Button/Button.css index c9f3aa2..910f326 100644 --- a/apps/extension/src/shared/components/Button/Button.css +++ b/apps/extension/src/shared/components/Button/Button.css @@ -1,41 +1,47 @@ /* Button */ .flowforge-button { - padding: var(--flowforge-pad-sm) var(--flowforge-pad-lg); - border-radius: var(--flowforge-pad-xs); - font: var(--flowforge-text-md); - cursor: pointer; - border: none; - transition: var(--flowforge-transition-default); + appearance: none; + padding: var(--flowforge-pad-sm) var(--flowforge-pad-lg); + border-radius: var(--flowforge-pad-xs); + font: var(--flowforge-text-md); + cursor: pointer; + border: none; + transition: var(--flowforge-transition-default); +} +.flowforge-button--sm { + padding: var(--flowforge-pad-xs) var(--flowforge-pad-sm); + border-radius: var(--flowforge-pad-xs); + font: var(--flowforge-text-sm); } .flowforge-button--lg { - padding: var(--flowforge-pad-lg); - border-radius: var(--flowforge-pad-sm); - font: var(--flowforge-text-h3); + padding: var(--flowforge-pad-lg); + border-radius: var(--flowforge-pad-sm); + font: var(--flowforge-text-h3); } .flowforge-button--wide { - width: 100%; + width: 100%; } .flowforge-button, .flowforge-button--primary { - background: var(--flowforge-color-primary); - color: var(--flowforge-color-text-on-dark); + background: var(--flowforge-color-primary); + color: var(--flowforge-color-text-on-dark); } .flowforge-button--secondary { - background: var(--flowforge-color-secondary); + background: var(--flowforge-color-secondary); } .flowforge-button:hover, .flowforge-button--primary:hover { - background: var(--flowforge-color-primary-hover); - transform: translateY(-1px); + background: var(--flowforge-color-primary-hover); + transform: translateY(-1px); } .flowforge-button--secondary:hover { - background: var(--flowforge-color-secondary-hover); + background: var(--flowforge-color-secondary-hover); } .flowforge-button:active { - transform: translateY(0); + transform: translateY(0); } .flowforge-button:disabled { - background: var(--flowforge-color-muted); - cursor: not-allowed; - opacity: 0.5; + background: var(--flowforge-color-muted); + cursor: not-allowed; + opacity: 0.5; } diff --git a/apps/extension/src/shared/components/Button/Button.tsx b/apps/extension/src/shared/components/Button/Button.tsx index 8545920..a5059dd 100644 --- a/apps/extension/src/shared/components/Button/Button.tsx +++ b/apps/extension/src/shared/components/Button/Button.tsx @@ -2,7 +2,7 @@ import type { ComponentProps } from 'preact'; interface ButtonProps extends ComponentProps<'button'> { variant?: 'primary' | 'secondary'; - size?: 'medium' | 'large'; + size?: 'small' | 'medium' | 'large'; wide?: boolean; } @@ -10,6 +10,7 @@ export function Button({ variant = 'primary', size = 'medium', wide = false, ... const classes = [ 'flowforge-button', `flowforge-button--${variant}`, + size === 'small' && 'flowforge-button--sm', size === 'large' && 'flowforge-button--lg', wide && 'flowforge-button--wide', ] diff --git a/apps/extension/src/shared/components/Button/ButtonText.css b/apps/extension/src/shared/components/Button/ButtonText.css index fb85366..1b92e67 100644 --- a/apps/extension/src/shared/components/Button/ButtonText.css +++ b/apps/extension/src/shared/components/Button/ButtonText.css @@ -1,21 +1,21 @@ /* Button Text */ .flowforge-button-text { - border: none; - background: none; - cursor: pointer; - font: inherit; - color: var(--flowforge-color-text); - transition: var(--flowforge-transition-default); + border: none; + background: none; + cursor: pointer; + font: inherit; + color: var(--flowforge-color-text); + transition: var(--flowforge-transition-default); } .flowforge-button-text:hover, .flowforge-button-text--primary:hover { - color: var(--flowforge-color-primary-hover); + color: var(--flowforge-color-primary-hover); } .flowforge-button-text--secondary:hover { - color: var(--flowforge-color-secondary-hover); + color: var(--flowforge-color-secondary-hover); } .flowforge-button-text:disabled { - color: var(--flowforge-color-text-muted); - cursor: not-allowed; - opacity: 0.5; + color: var(--flowforge-color-text-muted); + cursor: not-allowed; + opacity: 0.5; } diff --git a/apps/extension/src/shared/components/Button/ButtonText.tsx b/apps/extension/src/shared/components/Button/ButtonText.tsx index 7e7bf60..f39249a 100644 --- a/apps/extension/src/shared/components/Button/ButtonText.tsx +++ b/apps/extension/src/shared/components/Button/ButtonText.tsx @@ -2,10 +2,11 @@ import type { ComponentProps } from 'preact'; interface ButtonTextProps extends ComponentProps<'button'> { variant?: 'primary' | 'secondary'; + tooltip?: string; } -export function ButtonText({ variant = 'primary', ...props }: ButtonTextProps) { +export function ButtonText({ variant = 'primary', tooltip, ...props }: ButtonTextProps) { const classes = ['flowforge-button-text', `flowforge-button-text--${variant}`].filter(Boolean).join(' '); - return + ); + })} + + ); +} diff --git a/apps/extension/src/shared/components/Tabs/index.ts b/apps/extension/src/shared/components/Tabs/index.ts new file mode 100644 index 0000000..941e6ba --- /dev/null +++ b/apps/extension/src/shared/components/Tabs/index.ts @@ -0,0 +1 @@ +export { Tabs } from './Tabs'; diff --git a/apps/extension/src/shared/hooks/useSettings.ts b/apps/extension/src/shared/hooks/useSettings.ts index 8502025..f538946 100644 --- a/apps/extension/src/shared/hooks/useSettings.ts +++ b/apps/extension/src/shared/hooks/useSettings.ts @@ -1,5 +1,5 @@ import { useCallback, useEffect, useState } from 'preact/hooks'; -import type { TransportService } from '#self/adapters/interface'; +import type { TransportService } from '@/adapters/interface'; import { type ExtensionSettings, type ExtensionSettingsTheme, @@ -8,14 +8,17 @@ import { isApplySettingsMessage, type UpdateSettingsMessage, type UpdateSettingsMessageResponse, -} from '#self/types'; -import { config } from '#self/config'; -import type { SettingsViewModel } from './useSettings.types'; +} from '@/types'; +import { config } from '@/config'; interface UseSettingsParams { transport: TransportService; } +export interface SettingsViewModel extends ExtensionSettings { + toggleTheme: () => Promise; +} + export function useSettings({ transport }: UseSettingsParams): SettingsViewModel { const [settings, setSettings] = useState(config.defaultSettings); diff --git a/apps/extension/src/shared/hooks/useSettings.types.ts b/apps/extension/src/shared/hooks/useSettings.types.ts deleted file mode 100644 index 7434d74..0000000 --- a/apps/extension/src/shared/hooks/useSettings.types.ts +++ /dev/null @@ -1,7 +0,0 @@ -import type { ExtensionSettings } from '#self/types'; - -export interface SettingsActions { - toggleTheme: () => Promise; -} - -export type SettingsViewModel = ExtensionSettings & SettingsActions; diff --git a/apps/extension/src/shared/index.css b/apps/extension/src/shared/index.css index 878d502..727bf89 100644 --- a/apps/extension/src/shared/index.css +++ b/apps/extension/src/shared/index.css @@ -1,6 +1,14 @@ +@import './styles/tokens.css'; +@import './styles/modifiers.css'; + @import './components/Main/Main.css'; @import './components/Link/Link.css'; @import './components/Button/Button.css'; @import './components/Button/ButtonText.css'; +@import './components/Icon/Icon.css'; @import './components/Card/Card.css'; @import './components/Notice/Notice.css'; +@import './components/Star/StarTwinkle.css'; +@import './components/Tabs/Tabs.css'; +@import './components/JsonViewer/JsonViewer.css'; +@import './components/MarkdownViewer/MarkdownViewer.css'; diff --git a/apps/extension/src/shared/styles/modifiers.css b/apps/extension/src/shared/styles/modifiers.css new file mode 100644 index 0000000..2a927d6 --- /dev/null +++ b/apps/extension/src/shared/styles/modifiers.css @@ -0,0 +1,13 @@ +/* Modifiers: color */ +.flowforge-u-color-primary { + color: var(--flowforge-color-primary); +} +.flowforge-u-color-secondary { + color: var(--flowforge-color-secondary); +} +.flowforge-u-color-danger { + color: var(--flowforge-color-danger); +} +.flowforge-u-color-muted { + color: var(--flowforge-color-text-muted); +} diff --git a/apps/extension/src/shared/styles/tokens.css b/apps/extension/src/shared/styles/tokens.css new file mode 100644 index 0000000..738fe10 --- /dev/null +++ b/apps/extension/src/shared/styles/tokens.css @@ -0,0 +1,83 @@ +/* Tokens */ +.flowforge-main, +.flowforge-main[data-theme='light'] { + /** Colors **/ + --flowforge-color-primary: #6d28d9; + --flowforge-color-primary-hover: #7c3aed; + --flowforge-color-secondary: #f97316; + --flowforge-color-secondary-hover: #fb923c; + --flowforge-color-danger: #dc2626; + --flowforge-color-bg: #f1f5f9; + --flowforge-color-surface: #ffffff; + --flowforge-color-border: #cbd5f5; + --flowforge-color-muted: #475569; + --flowforge-color-text: #0f172a; + --flowforge-color-text-on-dark: #e5e7eb; + --flowforge-color-text-muted: var(--flowforge-color-muted); + + /* RGB versions */ + --flowforge-rgb-text: 15, 23, 42; + --flowforge-rgb-primary: 109, 40, 217; + --flowforge-rgb-secondary: 249, 115, 22; + --flowforge-rgb-danger: 220, 38, 38; + --flowforge-rgb-shadow: 0, 0, 0; +} + +.flowforge-main[data-theme='dark'] { + /** Colors **/ + --flowforge-color-primary: #8b5cf6; + --flowforge-color-primary-hover: #a78bfa; + --flowforge-color-secondary: #fb923c; + --flowforge-color-secondary-hover: #fdba74; + --flowforge-color-danger: #f87171; + --flowforge-color-bg: #0b1020; + --flowforge-color-surface: #12172a; + --flowforge-color-border: #2a3150; + --flowforge-color-muted: #94a3b8; + --flowforge-color-text: #e5e7eb; + --flowforge-color-text-on-dark: #e5e7eb; + --flowforge-color-text-muted: var(--flowforge-color-muted); + + /* RGB versions */ + --flowforge-rgb-text: 229, 231, 235; + --flowforge-rgb-primary: 139, 92, 246; + --flowforge-rgb-secondary: 251, 146, 60; + --flowforge-rgb-danger: 248, 113, 113; + --flowforge-rgb-shadow: 120, 120, 120; +} + +.flowforge-main { + /** Typography **/ + --flowforge-font-family: + system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; + --flowforge-font-family-monospace: + ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, 'Liberation Mono', monospace; + + --flowforge-text-xs: 400 11px / 1.6 var(--flowforge-font-family); + --flowforge-text-sm: 400 12px / 1.5 var(--flowforge-font-family); + --flowforge-text-md: 400 14px / 1.4 var(--flowforge-font-family); + --flowforge-text-lg: 400 16px / 1.5 var(--flowforge-font-family); + + --flowforge-text-h4: 600 13px / 1.4 var(--flowforge-font-family); + --flowforge-text-h3: 600 14px / 1.4 var(--flowforge-font-family); + --flowforge-text-h2: 600 18px / 1.2 var(--flowforge-font-family); + + --flowforge-text-monospace-xs: 400 11px / 1.65 var(--flowforge-font-family-monospace); + --flowforge-text-monospace-sm: 400 12px / 1.55 var(--flowforge-font-family-monospace); + + /** Sizes **/ + --flowforge-pad-xs: 6px; + --flowforge-pad-sm: 8px; + --flowforge-pad-md: 10px; + --flowforge-pad-lg: 12px; + --flowforge-pad-xl: 16px; + + --flowforge-icon-size-sm: 14px; + --flowforge-icon-size-md: 16px; + --flowforge-icon-size-lg: 18px; + + --flowforge-app-offset: 20px; + + /** Animation **/ + --flowforge-transition-default: background 0.2s ease, transform 0.2s ease, opacity 0.2s ease; +} diff --git a/apps/extension/tsconfig.json b/apps/extension/tsconfig.json index 536d9e8..8a2d1e3 100644 --- a/apps/extension/tsconfig.json +++ b/apps/extension/tsconfig.json @@ -1,32 +1,21 @@ { + "extends": ["@tsconfig/vite-react/tsconfig.json"], "compilerOptions": { - "target": "esnext", "lib": ["DOM", "DOM.Iterable", "ESNext"], - "types": ["chrome"], - "module": "esnext", - "moduleResolution": "bundler", - "moduleDetection": "force", - "allowArbitraryExtensions": true, + "types": ["chrome", "vite/client"], + "jsxImportSource": "preact", + "resolveJsonModule": true, - "paths": { - "#self/config": ["./src/config.ts"], - "#self/types": ["./src/core/types/index.ts"], - "#self/constants": ["./src/core/constants.ts"], - "#self/*": ["./src/*"] - }, - "noEmit": true, - "strict": true, - "noUncheckedIndexedAccess": true, - "noUncheckedSideEffectImports": true, "noImplicitOverride": true, - "isolatedModules": true, - "verbatimModuleSyntax": true, - "allowSyntheticDefaultImports": true, - "skipLibCheck": true, - "forceConsistentCasingInFileNames": true, - "jsx": "react-jsx", - "jsxImportSource": "preact" + "noUncheckedIndexedAccess": true, + + "paths": { + "@/config": ["./src/config.ts"], + "@/types": ["./src/core/types/index.ts"], + "@/constants": ["./src/core/constants.ts"], + "@/*": ["./src/*"] + } }, "include": ["src/**/*"], - "exclude": ["node_modules", "dist", "sandbox", "**/*.test.ts"] + "exclude": ["sandbox", "**/*.test.ts"] } diff --git a/apps/extension/vite.config.chrome.ts b/apps/extension/vite.config.chrome.ts index 2d14260..f745127 100644 --- a/apps/extension/vite.config.chrome.ts +++ b/apps/extension/vite.config.chrome.ts @@ -1,7 +1,8 @@ import { defineConfig } from 'vite'; import preact from '@preact/preset-vite'; import { crx } from '@crxjs/vite-plugin'; -import manifest from './src/chrome/manifest.config'; +// @ts-expect-error an explicit extension for Vite import +import manifest from './src/chrome/manifest.config.ts'; export default defineConfig({ root: 'src/chrome', @@ -17,6 +18,7 @@ export default defineConfig({ build: { target: 'esnext', sourcemap: true, + modulePreload: false, outDir: '../../dist/chrome', emptyOutDir: true, }, diff --git a/docs/ARCHITECTURE.md b/docs/ARCHITECTURE.md index e11debb..8fbb929 100644 --- a/docs/ARCHITECTURE.md +++ b/docs/ARCHITECTURE.md @@ -2,106 +2,98 @@ ## Overview -FlowForge is a client–server system that connects user intent to UI-level actions inside web applications. +FlowForge is a client–server system that connects user intent to UI-level actions. A Chrome extension or embeddable runtime handles page/UI work; the backend runs the AI agent and RAG pipeline. -It consists of a Chrome extension (UI + page understanding) and a backend (AI agent + RAG pipeline) working together to provide contextual, actionable guidance. - -![Architecture Overview](assets/architecture-overview.webp) +![Architecture overview](assets/architecture-overview.webp) ## Components ### Extension -Extracts page data, handles UI, highlights elements, and manages user interaction. Runs in the browser as a Chrome extension with popup, background, and content scripts. +Extracts page data, handles UI, highlights elements, and manages user interaction. Runs as a Manifest V3 extension with popup, background service worker, and content script. + +### Embed runtime + +Website-embedded bundle that can use the local backend or a demo API client with predefined responses. ### Backend -Processes queries, runs the AI agent, manages vector storage, and exposes HTTP API. Built with Express and LangGraph. +Processes queries, runs the AI agent, manages vector storage, and exposes HTTP API. Built with Express, LangChain/LangGraph primitives, and LanceDB. ### Agent -Interprets user intent and orchestrates tool usage using the ReAct pattern. Powered by LangGraph with structured tool calls. +Interprets user intent and orchestrates tool usage with the ReAct pattern and structured tool calls. -### RAG Pipeline +### RAG pipeline -Indexes page content into vector database (LanceDB) and retrieves relevant context for queries using semantic search. +Indexes page content into LanceDB and retrieves relevant context for queries using semantic search. -### LLM Provider +### LLM provider -Inference layer supporting local (Ollama) or cloud (OpenAI) models for embeddings and generation. +Inference layer supporting Ollama or OpenAI models for embeddings and generation. -## Interaction Flow +## Interaction flow -### Query Flow +### Query flow -1. User asks a question in the extension popup -2. Extension sends `pageModel + question` to backend (`POST /query`) -3. Backend checks if page is indexed; indexes if needed +1. User asks a question in the extension popup or embed shell +2. Extension sends `pageTrail + question` to backend (`POST /query`) +3. Backend indexes the submitted page snapshot 4. Agent executes with access to tools and vector search 5. Backend returns structured result (answer + target elements) -6. Extension highlights elements and displays response in popup +6. Browser runtime highlights elements and displays the response -### Indexing Flow +### Indexing flow -1. Extension extracts page structure (content, interactive elements, navigation) +1. Browser runtime extracts page structure (`basics`, `content`, `interactive`, `metadata`) 2. Backend splits data into documents with metadata 3. Embeddings are generated via LLM provider 4. Documents stored in vector database (LanceDB) -5. Available for semantic search during query processing ## Pipeline High-level overview of the DOM-to-RAG pipeline: -1. **Extraction** — DOM → structured `PageModel` (content + interactive elements + context) -2. **Transformation** — `PageModel` → semantic `IndexableDocuments` with metadata +1. **Extraction** — DOM → structured `PageTrail` (content + interactive elements + context) +2. **Transformation** — `PageTrail` → semantic `IndexableDocuments` with metadata 3. **Indexing** — Documents → embeddings → vector storage (LanceDB) 4. **Retrieval** — Query → Top-K relevant documents via semantic search 5. **Reranking** — Hybrid scoring (semantic + importance signals) 6. **Resolution** — Documents → actionable tool results (selectors + descriptions) -See [DOM-TO-RAG-PIPELINE.md](DOM-TO-RAG-PIPELINE.md) for detailed pipeline documentation. - -## Key Decisions +## Key decisions **ReAct agent (LangGraph)** -Provides reliable tool-based reasoning with full control over execution flow and observability. +Provides tool-based reasoning with controlled execution flow and observability. **RAG over page context** -Enables accurate, contextual answers by retrieving relevant page content instead of relying solely on agent memory. +Retrieves relevant page content instead of relying only on agent memory. **LanceDB** -Embedded vector storage with no external dependencies. Runs locally alongside the backend. +Embedded vector storage that runs locally alongside the backend. -**Local + Cloud LLM** -Flexibility between privacy (Ollama) and performance (OpenAI) depending on user needs. +**Local + cloud LLM** +Switches between local Ollama and OpenAI providers. -## Contracts +**Typed contracts** +Shared request/response contracts live in `@flowforge/contract`; DOM snapshots live in `@flowforge/page-trail`. -### Extension ↔ Backend +## Contracts -HTTP API with main endpoints: +Extension ↔ backend: - `POST /query` — submit user question with page data - `POST /search` — semantic search over indexed content -- `GET /analytics` — usage data and query tracking -- `GET /health` — service status - -### Agent ↔ Tools +- `GET /analytics` / `GET /health` — analytics and service status -Structured tool calls with typed inputs/outputs defined via Zod schemas. +`POST /query` accepts `question`, `pageTrail`, `domain`, and optional question history. It returns an `AgentResult` with `answer`, `mode`, optional `topic`, and target `elements`. -### Indexer ↔ Storage - -Embeddings + metadata stored per page. Each document includes source URL, type (content/element), and original text. +Agent tools use structured Zod schemas. Indexer documents are stored per page URL and embedding provider with content text and source element metadata. ## Constraints **Single-page context** -No cross-page memory or session continuity. Each query operates on current page only. +No cross-page DOM memory. Each query operates on the submitted page snapshot. The extension keeps short per-domain question history. **Local backend** Designed for single-user local deployment. No authentication or multi-tenancy. - -**In-memory analytics** -Query tracking and usage data stored in memory. No persistence across restarts. diff --git a/docs/BACKLOG.md b/docs/BACKLOG.md index 42d275e..96bb195 100644 --- a/docs/BACKLOG.md +++ b/docs/BACKLOG.md @@ -5,6 +5,12 @@ - Test coverage (unit/integration, vitest, playwright, promptfoo) - Create extendable abstraction over extractors, embeddings, tooling +## Page Trail + +- Cache for the collector heavy computes +- Adjust importance score for headings (h1-h4) +- Parse structured data (Schema.org, og, x-card, etc.) for basics info + ## Backend ### Models @@ -36,7 +42,7 @@ - Answer the question: How does the product work? What alternatives do I have? What is the next step? - Fetch data from links to provide website context -### Tech Improvements +### Tech improvements - Consider using Vercel AI SDK, OpenRouter - Use pnpm for package management instead of npm @@ -52,8 +58,13 @@ ## Extension - Support standalone mode, properly -- Cache for pageModel transfer, with RAG cache -- Enhance dataId and selector usage for El locator +- Enhance dataId and CSS selector usage for Element locator + +### Page inspector + +- Loading state +- Lazy rendering in JsonViewer +- Provide the history data about the page usage ## DX diff --git a/docs/DOM-TO-RAG-PIPELINE.md b/docs/DOM-TO-RAG-PIPELINE.md index 6c8757a..f04489b 100644 --- a/docs/DOM-TO-RAG-PIPELINE.md +++ b/docs/DOM-TO-RAG-PIPELINE.md @@ -1,46 +1,46 @@ -# DOM-to-RAG Pipeline +# DOM-to-RAG pipeline ## Overview -A lightweight architecture that transforms raw DOM into actionable, UI-aware answers using a Retrieval-Augmented Generation (RAG) approach. +FlowForge converts raw DOM into structured, searchable, UI-aware context for Retrieval-Augmented Generation (RAG). -Modern web applications expose rich information through the DOM, but it is not directly usable for AI reasoning. +For more information about the canonical DOM snapshot format, see +[`packages/page-trail/README.md`](../packages/page-trail/README.md). -This pipeline converts the DOM into a structured, searchable, and actionable representation. - -![Pipeline Schema](assets/dom-rag-pipeline.webp) +![Pipeline schema](assets/dom-rag-pipeline.webp) --- ## Stages -### 1. Extraction to Structure Representation - -The DOM is parsed and normalized into a structured `PageModel`. +### 1. Extraction to structure representation -This stage captures both the raw structure and contextual meaning of the UI. +The browser runtime parses the DOM into a structured `PageTrail`. Includes: -- Page basics (title, URL, metadata) + +- Page basics (URL, title, description, language, viewport) - Content elements (text, headings) - Interactive elements (buttons, inputs, links) +- Collection metadata (counts, limit flags, collected time, duration) + +Elements include: -Each element contains: - Attributes and properties - Semantic roles and labels - Embedded layout and context: - Section (e.g., header, main, form) - Hierarchical path within the page +- Stable `dataId` and optional CSS selector for browser-side lookup -Each element is also assigned an importance score. - -This layer defines *what exists on the page and how it is structured*. +Each element is assigned an importance score. This layer defines _what exists on the page and how it is structured_. -### 2. Transforming to Semantic Representation +### 2. Transforming to semantic representation -Transforms structured page data into AI-friendly, human-readable `IndexableDocuments` enriched with metadata for retrieval. +Transforms `PageTrail` into AI-friendly `IndexableDocuments`. Includes: + - Chunking (splitting content into manageable pieces) - Semantic formatting (constructing descriptions using element attributes, roles, labels, and context) - Metadata enrichment: @@ -48,48 +48,56 @@ Includes: - Element references (selector, dataId) - Importance signals - Separation into: - - Content Documents (informational text) - - Interactive Documents (actionable UI elements) + - Content documents (informational text) + - Interactive documents (actionable UI elements) -This layer defines *what the page means and how it can be interpreted and retrieved by AI*. +This layer defines _what the page means and how it can be retrieved by AI_. -### 3. Indexing / Retrieval +### 3. Indexing / retrieval -Documents are embedded, stored in a vector database, and retrieved at query time based on semantic similarity. +Documents are embedded, stored in LanceDB, and retrieved at query time by semantic similarity. Includes: + - Embedding documents into vector representations -- Storing them for efficient similarity search -- Retrieving Top-K relevant documents for a given query +- Storing them in LanceDB under a dataset derived from provider + page URL +- Retrieving top-k relevant documents for a given query +- Optional filtering by document type (`content` or `interactive`) -This layer enables fast and context-aware access to relevant UI information. +This layer provides context-aware access to relevant UI information. ### 4. Reranking -Retrieved documents are rescored to improve relevance using a combination of semantic similarity and UI-specific signals. +Retrieved documents are rescored with semantic similarity and UI-specific signals. + +A hybrid scoring function is applied by the agent tools: -A hybrid scoring function is applied: ``` -score = semantic_score * A + importance_score * B +score = semantic similarity + UI importance signals ``` Where: + - `semantic_score` reflects how well the document matches the query -- `importance_score` reflects UI relevance (e.g., visibility, role, position, interaction potential) +- `importance_score` reflects UI relevance such as visibility, role, position, and interaction potential -This step prioritizes elements that are not only semantically relevant, but also meaningful and actionable for the user. +Different tools use different scoring profiles for lookup, answer, and action-oriented requests. -### 5. Resolution to Tool Results +### 5. Resolution to tool results -Transforms ranked documents into final, actionable outputs that Agent can directly use. +Transforms ranked documents into actionable outputs that the agent can use. Includes: + - **Selection** — choosing the most relevant candidates from reranked documents - **Mapping** — converting documents into structured UI targets Produces: + - Element references (selector, dataId) - User-facing descriptions -- Action hints (click, input, navigate) +- Action hints (`click`, `input`, `select`, `navigate`, `highlight`) + +The final backend response is validated as an `AgentResult`; invalid `dataId` values are filtered before response. This stage bridges retrieval and real UI interaction by turning data into executable guidance. diff --git a/docs/assets/architecture-overview.png b/docs/assets/architecture-overview.png index cee69c5..c73ef8f 100644 Binary files a/docs/assets/architecture-overview.png and b/docs/assets/architecture-overview.png differ diff --git a/docs/assets/architecture-overview.webp b/docs/assets/architecture-overview.webp index 2d442f7..1fa2c8c 100644 Binary files a/docs/assets/architecture-overview.webp and b/docs/assets/architecture-overview.webp differ diff --git a/docs/assets/dom-rag-pipeline.png b/docs/assets/dom-rag-pipeline.png index e71a9a9..3fbc478 100644 Binary files a/docs/assets/dom-rag-pipeline.png and b/docs/assets/dom-rag-pipeline.png differ diff --git a/docs/assets/dom-rag-pipeline.webp b/docs/assets/dom-rag-pipeline.webp index b96a537..a4df1a7 100644 Binary files a/docs/assets/dom-rag-pipeline.webp and b/docs/assets/dom-rag-pipeline.webp differ diff --git a/package-lock.json b/package-lock.json index d07372d..ceeb799 100644 --- a/package-lock.json +++ b/package-lock.json @@ -19,7 +19,7 @@ "version": "0.1.4", "dependencies": { "@flowforge/contract": "*", - "@flowforge/page-model": "*", + "@flowforge/page-trail": "*", "@lancedb/lancedb": "^0.27.1", "@langchain/core": "^1.1.35", "@langchain/ollama": "^1.2.6", @@ -33,10 +33,17 @@ "zod": "^4.3.6" }, "devDependencies": { + "@eslint/js": "^10.0.1", + "@tsconfig/node22": "^22.0.5", "@types/cors": "^2.8.17", "@types/express": "^5.0.6", "@types/node": "^25.5.0", - "typescript": "^6.0.2" + "concurrently": "^10.0.4", + "eslint": "^10.8.1", + "globals": "^17.9.0", + "tsc-alias": "^1.9.1", + "typescript": "^6.0.2", + "typescript-eslint": "^8.66.0" }, "engines": { "node": ">=22" @@ -47,17 +54,23 @@ "version": "0.1.4", "dependencies": { "@flowforge/contract": "*", - "@flowforge/page-model": "*" + "@flowforge/page-trail": "*", + "lucide-preact": "^1.31.0" }, "devDependencies": { "@crxjs/vite-plugin": "^2.4.0", + "@eslint/js": "^10.0.1", "@preact/preset-vite": "^2.10.5", + "@tsconfig/vite-react": "^8.1.1", "@types/chrome": "^0.1.38", - "concurrently": "^9.2.1", + "concurrently": "^10.0.4", + "eslint": "^10.8.1", "express": "^5.2.1", + "globals": "^17.9.0", "marked": "^18.0.2", "preact": "^10.29.1", "typescript": "^6.0.2", + "typescript-eslint": "^8.66.0", "vite": "^8.0.8" }, "engines": { @@ -65,13 +78,13 @@ } }, "node_modules/@babel/code-frame": { - "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.0.tgz", - "integrity": "sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.7.tgz", + "integrity": "sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-validator-identifier": "^7.28.5", + "@babel/helper-validator-identifier": "^7.29.7", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" }, @@ -80,9 +93,9 @@ } }, "node_modules/@babel/compat-data": { - "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.0.tgz", - "integrity": "sha512-T1NCJqT/j9+cn8fvkt7jtwbLBfLC/1y1c7NtCeXFRgzGTsafi68MRv8yzkYSapBnFA6L3U2VSc02ciDzoAJhJg==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.7.tgz", + "integrity": "sha512-locTkQyKvwIEgBzVrn8693ebc97F2U8ZHjbXwDXJ5Fn2TCpNwTlKcaKLkdHop5c/icOFE7qt7Q9JC5hnKNa6Gg==", "dev": true, "license": "MIT", "engines": { @@ -90,21 +103,21 @@ } }, "node_modules/@babel/core": { - "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.0.tgz", - "integrity": "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.7.tgz", + "integrity": "sha512-RgHBCvtjbOK2gXSNBNIkNoEc9qoVEtau3hj8gEqKQuL3HZAibKarWFEI3Lfm6EYKkLalOh8eSrj9b+ch9H/VBA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.29.0", - "@babel/generator": "^7.29.0", - "@babel/helper-compilation-targets": "^7.28.6", - "@babel/helper-module-transforms": "^7.28.6", - "@babel/helpers": "^7.28.6", - "@babel/parser": "^7.29.0", - "@babel/template": "^7.28.6", - "@babel/traverse": "^7.29.0", - "@babel/types": "^7.29.0", + "@babel/code-frame": "^7.29.7", + "@babel/generator": "^7.29.7", + "@babel/helper-compilation-targets": "^7.29.7", + "@babel/helper-module-transforms": "^7.29.7", + "@babel/helpers": "^7.29.7", + "@babel/parser": "^7.29.7", + "@babel/template": "^7.29.7", + "@babel/traverse": "^7.29.7", + "@babel/types": "^7.29.7", "@jridgewell/remapping": "^2.3.5", "convert-source-map": "^2.0.0", "debug": "^4.1.0", @@ -120,32 +133,15 @@ "url": "https://opencollective.com/babel" } }, - "node_modules/@babel/core/node_modules/convert-source-map": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", - "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", - "dev": true, - "license": "MIT" - }, - "node_modules/@babel/core/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - } - }, "node_modules/@babel/generator": { - "version": "7.29.1", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.1.tgz", - "integrity": "sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw==", + "version": "7.29.8", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.8.tgz", + "integrity": "sha512-gZbepsdh3WDtgZKWL+vTPh71LSBrm/Y4/QDZBVCcYfmeTEEuoOYwlSy+G1StfJg+/Zy550u/3TATbm7qDbbMtg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/parser": "^7.29.0", - "@babel/types": "^7.29.0", + "@babel/parser": "^7.29.8", + "@babel/types": "^7.29.8", "@jridgewell/gen-mapping": "^0.3.12", "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" @@ -155,27 +151,27 @@ } }, "node_modules/@babel/helper-annotate-as-pure": { - "version": "7.27.3", - "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.27.3.tgz", - "integrity": "sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.29.7.tgz", + "integrity": "sha512-OoK6239jHPuSQOoS0kfTVKn0b/rVTk0seKq4Gd2UMLtmOVLjDC0ki3e+c90Trqv2gMfvJFqkiljrr568+qddiw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/types": "^7.27.3" + "@babel/types": "^7.29.7" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-compilation-targets": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.28.6.tgz", - "integrity": "sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.29.7.tgz", + "integrity": "sha512-wem6WaBj4NaVYVdNhLPPVacES6ZJ+KBBfSkTMD3YZxbP3rm3Di85tJU5ljaUNhaOynt+Aj0xruhYuzQBt8n71g==", "dev": true, "license": "MIT", "dependencies": { - "@babel/compat-data": "^7.28.6", - "@babel/helper-validator-option": "^7.27.1", + "@babel/compat-data": "^7.29.7", + "@babel/helper-validator-option": "^7.29.7", "browserslist": "^4.24.0", "lru-cache": "^5.1.1", "semver": "^6.3.1" @@ -184,20 +180,10 @@ "node": ">=6.9.0" } }, - "node_modules/@babel/helper-compilation-targets/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - } - }, "node_modules/@babel/helper-globals": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz", - "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.29.7.tgz", + "integrity": "sha512-3nQVUAtvkKH9zahfWgw96Jc/uFOmjACE1kQz82E2lqWmHBgjzbNlsC22nuQTfahmWeQtTq5nQ/4Nnd2A1wj4zA==", "dev": true, "license": "MIT", "engines": { @@ -205,29 +191,29 @@ } }, "node_modules/@babel/helper-module-imports": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.28.6.tgz", - "integrity": "sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.29.7.tgz", + "integrity": "sha512-ejHwrQQYcm9xnTivShn2IDOlIzInN34AXskvq9QicvCtEzq1Vzclu/tKF8Jq1Cg8JG2GL6/EmjgsCT7lXepE3g==", "dev": true, "license": "MIT", "dependencies": { - "@babel/traverse": "^7.28.6", - "@babel/types": "^7.28.6" + "@babel/traverse": "^7.29.7", + "@babel/types": "^7.29.7" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-module-transforms": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.6.tgz", - "integrity": "sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.29.7.tgz", + "integrity": "sha512-UPUVSyXbOh627KiCIGQSgwWzGeBKLkaJ9PJEdrngIwMSzxLR4jS4+f1f1jb7VzBbg8nFLaYotvVPFCTqdrmTAg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-module-imports": "^7.28.6", - "@babel/helper-validator-identifier": "^7.28.5", - "@babel/traverse": "^7.28.6" + "@babel/helper-module-imports": "^7.29.7", + "@babel/helper-validator-identifier": "^7.29.7", + "@babel/traverse": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -237,9 +223,9 @@ } }, "node_modules/@babel/helper-plugin-utils": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.28.6.tgz", - "integrity": "sha512-S9gzZ/bz83GRysI7gAD4wPT/AI3uCnY+9xn+Mx/KPs2JwHJIz1W8PZkg2cqyt3RNOBM8ejcXhV6y8Og7ly/Dug==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.29.7.tgz", + "integrity": "sha512-G7sHYigPY17oO5SYWnfD/0MTBwVR781S/JI643e/JhUYgVgWE/61SoW3NH9KWUKyKq5LVh3npif99Wkt6j86Jw==", "dev": true, "license": "MIT", "engines": { @@ -247,9 +233,9 @@ } }, "node_modules/@babel/helper-string-parser": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", - "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.29.7.tgz", + "integrity": "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==", "dev": true, "license": "MIT", "engines": { @@ -257,9 +243,9 @@ } }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", - "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.29.7.tgz", + "integrity": "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==", "dev": true, "license": "MIT", "engines": { @@ -267,9 +253,9 @@ } }, "node_modules/@babel/helper-validator-option": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", - "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.29.7.tgz", + "integrity": "sha512-N9ZErrD+yW5geCDtBqnOoxmR8+tNKiGuxKlDpuJxfsqpa2dFcexaziGAE/qoHLiDDreVNMupxGmSoNlyvsA3gw==", "dev": true, "license": "MIT", "engines": { @@ -277,27 +263,27 @@ } }, "node_modules/@babel/helpers": { - "version": "7.29.2", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.29.2.tgz", - "integrity": "sha512-HoGuUs4sCZNezVEKdVcwqmZN8GoHirLUcLaYVNBK2J0DadGtdcqgr3BCbvH8+XUo4NGjNl3VOtSjEKNzqfFgKw==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.29.7.tgz", + "integrity": "sha512-1k2lAGRMfHTcwuNYcCNUmaUffmQv8KWMfh2iJUUeRlwlwH4FdNG7mfPI10NPfLHJFThE4Tyr4mv7kTNZOiPuBg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/template": "^7.28.6", - "@babel/types": "^7.29.0" + "@babel/template": "^7.29.7", + "@babel/types": "^7.29.7" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/parser": { - "version": "7.29.2", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.2.tgz", - "integrity": "sha512-4GgRzy/+fsBa72/RZVJmGKPmZu9Byn8o4MoLpmNe1m8ZfYnz5emHLQz3U4gLud6Zwl0RZIcgiLD7Uq7ySFuDLA==", + "version": "7.29.8", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.8.tgz", + "integrity": "sha512-E8lTAYNB1KW+FH+VGJuZM1ioAx2E6oVlvQFRrf5P8ZZmsiJXYAD9vTFV7yyEURNzgh1dFqMZuO6tUwcARbqFCA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/types": "^7.29.0" + "@babel/types": "^7.29.8" }, "bin": { "parser": "bin/babel-parser.js" @@ -307,13 +293,13 @@ } }, "node_modules/@babel/plugin-syntax-jsx": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.28.6.tgz", - "integrity": "sha512-wgEmr06G6sIpqr8YDwA2dSRTE3bJ+V0IfpzfSY3Lfgd7YWOaAdlykvJi13ZKBt8cZHfgH1IXN+CL656W3uUa4w==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.29.7.tgz", + "integrity": "sha512-TSu8+mHCoEaaCDEZ0I3+6mvTBYR4PCxQwf2z9/r5Tbztv6NaLR3B9thGTTxX2WGuGHJqRiAbKPeGTJ5XWXVg6A==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.28.6" + "@babel/helper-plugin-utils": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -323,17 +309,17 @@ } }, "node_modules/@babel/plugin-transform-react-jsx": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.28.6.tgz", - "integrity": "sha512-61bxqhiRfAACulXSLd/GxqmAedUSrRZIu/cbaT18T1CetkTmtDN15it7i80ru4DVqRK1WMxQhXs+Lf9kajm5Ow==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.29.7.tgz", + "integrity": "sha512-WsZulLVBUHXVj2cUcPVx6UE21TpalB6bHbSFErKT0Ib++ax24jjXe73FqlWvdylFOjiuPHYi6VCcgRad1ItN+A==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-annotate-as-pure": "^7.27.3", - "@babel/helper-module-imports": "^7.28.6", - "@babel/helper-plugin-utils": "^7.28.6", - "@babel/plugin-syntax-jsx": "^7.28.6", - "@babel/types": "^7.28.6" + "@babel/helper-annotate-as-pure": "^7.29.7", + "@babel/helper-module-imports": "^7.29.7", + "@babel/helper-plugin-utils": "^7.29.7", + "@babel/plugin-syntax-jsx": "^7.29.7", + "@babel/types": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -343,13 +329,13 @@ } }, "node_modules/@babel/plugin-transform-react-jsx-development": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-development/-/plugin-transform-react-jsx-development-7.27.1.tgz", - "integrity": "sha512-ykDdF5yI4f1WrAolLqeF3hmYU12j9ntLQl/AOG1HAS21jxyg1Q0/J/tpREuYLfatGdGmXp/3yS0ZA76kOlVq9Q==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-development/-/plugin-transform-react-jsx-development-7.29.7.tgz", + "integrity": "sha512-Xfy3UVMF04+ypnFbkhvfqtmvwfe92qwQdbGZVonhE+6v35GzlofmOnA1szaZqzb9xYWr0nl1e5EMmzi0DNON1g==", "dev": true, "license": "MIT", "dependencies": { - "@babel/plugin-transform-react-jsx": "^7.27.1" + "@babel/plugin-transform-react-jsx": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -359,33 +345,33 @@ } }, "node_modules/@babel/template": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.28.6.tgz", - "integrity": "sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.29.7.tgz", + "integrity": "sha512-puq+Gf35oI24FeN11LkoUQFqv9uwNeWpxXZi/Ji3rRIoKAzKnxRaZ+Gkj0vKS9ZCiTESfng1N9LyOyXvo+m+Gg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.28.6", - "@babel/parser": "^7.28.6", - "@babel/types": "^7.28.6" + "@babel/code-frame": "^7.29.7", + "@babel/parser": "^7.29.7", + "@babel/types": "^7.29.7" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/traverse": { - "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.0.tgz", - "integrity": "sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA==", + "version": "7.29.8", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.8.tgz", + "integrity": "sha512-I5z7H3bf/41ktsNVLtpN0wAa336HkqIHQ5BuPLEhTkt1jVSyZpeNKIzTgEWmlxjdg81R0IgUCcaE+Ok3NvrfZg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.29.0", - "@babel/generator": "^7.29.0", - "@babel/helper-globals": "^7.28.0", - "@babel/parser": "^7.29.0", - "@babel/template": "^7.28.6", - "@babel/types": "^7.29.0", + "@babel/code-frame": "^7.29.7", + "@babel/generator": "^7.29.8", + "@babel/helper-globals": "^7.29.7", + "@babel/parser": "^7.29.8", + "@babel/template": "^7.29.7", + "@babel/types": "^7.29.8", "debug": "^4.3.1" }, "engines": { @@ -393,19 +379,29 @@ } }, "node_modules/@babel/types": { - "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.0.tgz", - "integrity": "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==", + "version": "7.29.8", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.8.tgz", + "integrity": "sha512-Vj1jF3cPfxg7OAfoI7QnVKLoILlm2JF9pnVHrX8qx7AHMiYWT+NDAA7jChlNgRS4WTLc/fD1lXLmPixluj+3Gg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-string-parser": "^7.27.1", - "@babel/helper-validator-identifier": "^7.28.5" + "@babel/helper-string-parser": "^7.29.7", + "@babel/helper-validator-identifier": "^7.29.7" }, "engines": { "node": ">=6.9.0" } }, + "node_modules/@bcoe/v8-coverage": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-1.0.2.tgz", + "integrity": "sha512-6zABk/ECA/QYSCQ1NGiVwwbQerUCZ+TQbp64Q3AgmfNvurHH0j8TtXa1qbShXA6qqkpAj4V5W8pP6mLe1mcMqA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, "node_modules/@cfworker/json-schema": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/@cfworker/json-schema/-/json-schema-4.1.1.tgz", @@ -413,566 +409,266 @@ "license": "MIT" }, "node_modules/@crxjs/vite-plugin": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/@crxjs/vite-plugin/-/vite-plugin-2.4.0.tgz", - "integrity": "sha512-bDLdq0W2V1SkMQDJjrcYyjK9/uKtdl4joT7GRImcootCjZdKRiRYt+cv9z8tJoU/tK3o1lX48LTqN7JMsk5AQg==", + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/@crxjs/vite-plugin/-/vite-plugin-2.7.1.tgz", + "integrity": "sha512-mn77Lc1ilIvbO7ZqijjNy+AnRgc8srkfHOtV6HrGrYZgRe8YI3aIcSjHJCU5Kq75SejEZGrdAoHdn3gBGeKpEA==", "dev": true, "license": "MIT", "dependencies": { - "@rollup/pluginutils": "^4.1.2", "@webcomponents/custom-elements": "^1.5.0", - "acorn-walk": "^8.2.0", - "convert-source-map": "^1.7.0", - "debug": "^4.3.3", + "acorn-walk": "^8.3.5", + "convert-source-map": "^2.0.0", + "debug": "^4.4.3", "es-module-lexer": "^0.10.0", - "fast-glob": "^3.2.11", - "fs-extra": "^10.0.1", + "fs-extra": "^11.3.5", "jsesc": "^3.0.2", "magic-string": "^0.30.12", - "node-html-parser": "^7.0.2", + "node-html-parser": "^7.1.0", "pathe": "^2.0.1", "picocolors": "^1.1.1", - "react-refresh": "^0.13.0", - "rollup": "2.79.2", - "rxjs": "7.5.7" + "rollup": "2.80.0", + "rxjs": "7.5.7", + "tinyglobby": "^0.2.17" }, "peerDependencies": { "vite": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0" } }, - "node_modules/@emnapi/core": { - "version": "1.9.2", - "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.9.2.tgz", - "integrity": "sha512-UC+ZhH3XtczQYfOlu3lNEkdW/p4dsJ1r/bP7H8+rhao3TTTMO1ATq/4DdIi23XuGoFY+Cz0JmCbdVl0hz9jZcA==", + "node_modules/@crxjs/vite-plugin/node_modules/rxjs": { + "version": "7.5.7", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.5.7.tgz", + "integrity": "sha512-z9MzKh/UcOqB3i20H6rtrlaE/CgjLOvheWK/9ILrbhROGTweAi1BaFsTT9FbwZi5Trr1qNRs+MXkhmR06awzQA==", "dev": true, - "license": "MIT", - "optional": true, + "license": "Apache-2.0", "dependencies": { - "@emnapi/wasi-threads": "1.2.1", - "tslib": "^2.4.0" + "tslib": "^2.1.0" } }, - "node_modules/@emnapi/runtime": { - "version": "1.9.2", - "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.9.2.tgz", - "integrity": "sha512-3U4+MIWHImeyu1wnmVygh5WlgfYDtyf0k8AbLhMFxOipihf6nrWC4syIm/SwEeec0mNSafiiNnMJwbza/Is6Lw==", + "node_modules/@eslint-community/eslint-utils": { + "version": "4.10.1", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.10.1.tgz", + "integrity": "sha512-cuadcxVFE8sDK6iWJbs8Sn0av2Nrh2QSGQhVlBW9AaAHqHwjWsZHT8LJ4hFGPh7ASBV2deFdM7H/DPjulmh8rg==", "dev": true, "license": "MIT", - "optional": true, "dependencies": { - "tslib": "^2.4.0" + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" } }, - "node_modules/@emnapi/wasi-threads": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.1.tgz", - "integrity": "sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==", + "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "tslib": "^2.4.0" + "license": "Apache-2.0", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" } }, - "node_modules/@esbuild/aix-ppc64": { - "version": "0.27.4", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.4.tgz", - "integrity": "sha512-cQPwL2mp2nSmHHJlCyoXgHGhbEPMrEEU5xhkcy3Hs/O7nGZqEpZ2sUtLaL9MORLtDfRvVl2/3PAuEkYZH0Ty8Q==", - "cpu": [ - "ppc64" - ], + "node_modules/@eslint-community/regexpp": { + "version": "4.12.2", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz", + "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "aix" - ], - "peer": true, "engines": { - "node": ">=18" + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" } }, - "node_modules/@esbuild/android-arm": { - "version": "0.27.4", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.4.tgz", - "integrity": "sha512-X9bUgvxiC8CHAGKYufLIHGXPJWnr0OCdR0anD2e21vdvgCI8lIfqFbnoeOz7lBjdrAGUhqLZLcQo6MLhTO2DKQ==", - "cpu": [ - "arm" - ], + "node_modules/@eslint/config-array": { + "version": "0.23.5", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.23.5.tgz", + "integrity": "sha512-Y3kKLvC1dvTOT+oGlqNQ1XLqK6D1HU2YXPc52NmAlJZbMMWDzGYXMiPRJ8TYD39muD/OTjlZmNJ4ib7dvSrMBA==", "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "peer": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/object-schema": "^3.0.5", + "debug": "^4.3.1", + "minimatch": "^10.2.4" + }, "engines": { - "node": ">=18" + "node": "^20.19.0 || ^22.13.0 || >=24" } }, - "node_modules/@esbuild/android-arm64": { - "version": "0.27.4", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.4.tgz", - "integrity": "sha512-gdLscB7v75wRfu7QSm/zg6Rx29VLdy9eTr2t44sfTW7CxwAtQghZ4ZnqHk3/ogz7xao0QAgrkradbBzcqFPasw==", - "cpu": [ - "arm64" - ], + "node_modules/@eslint/config-helpers": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.7.0.tgz", + "integrity": "sha512-DObd/KKUsU+FaFv4PLxSRenpXfQWmPXXP3pPZ6/K1PCrMu2vQpMDMuQe/BqYeoLcz8ro0bVDF1RxOJgfVEdhUw==", "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "peer": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^1.2.1" + }, "engines": { - "node": ">=18" + "node": "^20.19.0 || ^22.13.0 || >=24" } }, - "node_modules/@esbuild/android-x64": { - "version": "0.27.4", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.4.tgz", - "integrity": "sha512-PzPFnBNVF292sfpfhiyiXCGSn9HZg5BcAz+ivBuSsl6Rk4ga1oEXAamhOXRFyMcjwr2DVtm40G65N3GLeH1Lvw==", - "cpu": [ - "x64" - ], + "node_modules/@eslint/core": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-1.2.1.tgz", + "integrity": "sha512-MwcE1P+AZ4C6DWlpin/OmOA54mmIZ/+xZuJiQd4SyB29oAJjN30UW9wkKNptW2ctp4cEsvhlLY/CsQ1uoHDloQ==", "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "peer": true, + "license": "Apache-2.0", + "dependencies": { + "@types/json-schema": "^7.0.15" + }, "engines": { - "node": ">=18" + "node": "^20.19.0 || ^22.13.0 || >=24" } }, - "node_modules/@esbuild/darwin-arm64": { - "version": "0.27.4", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.4.tgz", - "integrity": "sha512-b7xaGIwdJlht8ZFCvMkpDN6uiSmnxxK56N2GDTMYPr2/gzvfdQN8rTfBsvVKmIVY/X7EM+/hJKEIbbHs9oA4tQ==", - "cpu": [ - "arm64" - ], + "node_modules/@eslint/js": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-10.0.1.tgz", + "integrity": "sha512-zeR9k5pd4gxjZ0abRoIaxdc7I3nDktoXZk2qOv9gCNWx3mVwEn32VRhyLaRsDiJjTs0xq/T8mfPtyuXu7GWBcA==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "peer": true, "engines": { - "node": ">=18" + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "eslint": "^10.0.0" + }, + "peerDependenciesMeta": { + "eslint": { + "optional": true + } } }, - "node_modules/@esbuild/darwin-x64": { - "version": "0.27.4", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.4.tgz", - "integrity": "sha512-sR+OiKLwd15nmCdqpXMnuJ9W2kpy0KigzqScqHI3Hqwr7IXxBp3Yva+yJwoqh7rE8V77tdoheRYataNKL4QrPw==", - "cpu": [ - "x64" - ], + "node_modules/@eslint/object-schema": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-3.0.5.tgz", + "integrity": "sha512-vqTaUEgxzm+YDSdElad6PiRoX4t8VGDjCtt05zn4nU810UIx/uNEV7/lZJ6KwFThKZOzOxzXy48da+No7HZaMw==", "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "peer": true, + "license": "Apache-2.0", "engines": { - "node": ">=18" + "node": "^20.19.0 || ^22.13.0 || >=24" } }, - "node_modules/@esbuild/freebsd-arm64": { - "version": "0.27.4", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.4.tgz", - "integrity": "sha512-jnfpKe+p79tCnm4GVav68A7tUFeKQwQyLgESwEAUzyxk/TJr4QdGog9sqWNcUbr/bZt/O/HXouspuQDd9JxFSw==", - "cpu": [ - "arm64" - ], + "node_modules/@eslint/plugin-kit": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.7.2.tgz", + "integrity": "sha512-+CNAzxglkrpNf/kKywqQfk74QjtceuOE7Qm+AF8miRvPF/wmmK5+OJOgVh3AVTT3RP2mH3+FOaxlE5v72owk0A==", "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "peer": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^1.2.1", + "levn": "^0.4.1" + }, "engines": { - "node": ">=18" + "node": "^20.19.0 || ^22.13.0 || >=24" } }, - "node_modules/@esbuild/freebsd-x64": { - "version": "0.27.4", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.4.tgz", - "integrity": "sha512-2kb4ceA/CpfUrIcTUl1wrP/9ad9Atrp5J94Lq69w7UwOMolPIGrfLSvAKJp0RTvkPPyn6CIWrNy13kyLikZRZQ==", - "cpu": [ - "x64" - ], + "node_modules/@flowforge/backend": { + "resolved": "apps/backend", + "link": true + }, + "node_modules/@flowforge/contract": { + "resolved": "packages/contract", + "link": true + }, + "node_modules/@flowforge/extension": { + "resolved": "apps/extension", + "link": true + }, + "node_modules/@flowforge/page-trail": { + "resolved": "packages/page-trail", + "link": true + }, + "node_modules/@humanfs/core": { + "version": "0.19.2", + "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.2.tgz", + "integrity": "sha512-UhXNm+CFMWcbChXywFwkmhqjs3PRCmcSa/hfBgLIb7oQ5HNb1wS0icWsGtSAUNgefHeI+eBrA8I1fxmbHsGdvA==", "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "peer": true, + "license": "Apache-2.0", + "dependencies": { + "@humanfs/types": "^0.15.0" + }, "engines": { - "node": ">=18" + "node": ">=18.18.0" } }, - "node_modules/@esbuild/linux-arm": { - "version": "0.27.4", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.4.tgz", - "integrity": "sha512-aBYgcIxX/wd5n2ys0yESGeYMGF+pv6g0DhZr3G1ZG4jMfruU9Tl1i2Z+Wnj9/KjGz1lTLCcorqE2viePZqj4Eg==", - "cpu": [ - "arm" - ], + "node_modules/@humanfs/node": { + "version": "0.16.8", + "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.8.tgz", + "integrity": "sha512-gE1eQNZ3R++kTzFUpdGlpmy8kDZD/MLyHqDwqjkVQI0JMdI1D51sy1H958PNXYkM2rAac7e5/CnIKZrHtPh3BQ==", "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "peer": true, + "license": "Apache-2.0", + "dependencies": { + "@humanfs/core": "^0.19.2", + "@humanfs/types": "^0.15.0", + "@humanwhocodes/retry": "^0.4.0" + }, "engines": { - "node": ">=18" + "node": ">=18.18.0" } }, - "node_modules/@esbuild/linux-arm64": { - "version": "0.27.4", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.4.tgz", - "integrity": "sha512-7nQOttdzVGth1iz57kxg9uCz57dxQLHWxopL6mYuYthohPKEK0vU0C3O21CcBK6KDlkYVcnDXY099HcCDXd9dA==", - "cpu": [ - "arm64" - ], + "node_modules/@humanfs/types": { + "version": "0.15.0", + "resolved": "https://registry.npmjs.org/@humanfs/types/-/types-0.15.0.tgz", + "integrity": "sha512-ZZ1w0aoQkwuUuC7Yf+7sdeaNfqQiiLcSRbfI08oAxqLtpXQr9AIVX7Ay7HLDuiLYAaFPu8oBYNq/QIi9URHJ3Q==", "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "peer": true, + "license": "Apache-2.0", "engines": { - "node": ">=18" + "node": ">=18.18.0" } }, - "node_modules/@esbuild/linux-ia32": { - "version": "0.27.4", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.4.tgz", - "integrity": "sha512-oPtixtAIzgvzYcKBQM/qZ3R+9TEUd1aNJQu0HhGyqtx6oS7qTpvjheIWBbes4+qu1bNlo2V4cbkISr8q6gRBFA==", - "cpu": [ - "ia32" - ], + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "peer": true, + "license": "Apache-2.0", "engines": { - "node": ">=18" + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" } }, - "node_modules/@esbuild/linux-loong64": { - "version": "0.27.4", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.4.tgz", - "integrity": "sha512-8mL/vh8qeCoRcFH2nM8wm5uJP+ZcVYGGayMavi8GmRJjuI3g1v6Z7Ni0JJKAJW+m0EtUuARb6Lmp4hMjzCBWzA==", - "cpu": [ - "loong64" - ], + "node_modules/@humanwhocodes/retry": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz", + "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==", "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "peer": true, + "license": "Apache-2.0", "engines": { - "node": ">=18" + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" } }, - "node_modules/@esbuild/linux-mips64el": { - "version": "0.27.4", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.4.tgz", - "integrity": "sha512-1RdrWFFiiLIW7LQq9Q2NES+HiD4NyT8Itj9AUeCl0IVCA459WnPhREKgwrpaIfTOe+/2rdntisegiPWn/r/aAw==", - "cpu": [ - "mips64el" - ], + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "peer": true, - "engines": { - "node": ">=18" + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" } }, - "node_modules/@esbuild/linux-ppc64": { - "version": "0.27.4", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.4.tgz", - "integrity": "sha512-tLCwNG47l3sd9lpfyx9LAGEGItCUeRCWeAx6x2Jmbav65nAwoPXfewtAdtbtit/pJFLUWOhpv0FpS6GQAmPrHA==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "peer": true, - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-riscv64": { - "version": "0.27.4", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.4.tgz", - "integrity": "sha512-BnASypppbUWyqjd1KIpU4AUBiIhVr6YlHx/cnPgqEkNoVOhHg+YiSVxM1RLfiy4t9cAulbRGTNCKOcqHrEQLIw==", - "cpu": [ - "riscv64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "peer": true, - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-s390x": { - "version": "0.27.4", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.4.tgz", - "integrity": "sha512-+eUqgb/Z7vxVLezG8bVB9SfBie89gMueS+I0xYh2tJdw3vqA/0ImZJ2ROeWwVJN59ihBeZ7Tu92dF/5dy5FttA==", - "cpu": [ - "s390x" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "peer": true, - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-x64": { - "version": "0.27.4", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.4.tgz", - "integrity": "sha512-S5qOXrKV8BQEzJPVxAwnryi2+Iq5pB40gTEIT69BQONqR7JH1EPIcQ/Uiv9mCnn05jff9umq/5nqzxlqTOg9NA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "peer": true, - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/netbsd-arm64": { - "version": "0.27.4", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.4.tgz", - "integrity": "sha512-xHT8X4sb0GS8qTqiwzHqpY00C95DPAq7nAwX35Ie/s+LO9830hrMd3oX0ZMKLvy7vsonee73x0lmcdOVXFzd6Q==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "netbsd" - ], - "peer": true, - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/netbsd-x64": { - "version": "0.27.4", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.4.tgz", - "integrity": "sha512-RugOvOdXfdyi5Tyv40kgQnI0byv66BFgAqjdgtAKqHoZTbTF2QqfQrFwa7cHEORJf6X2ht+l9ABLMP0dnKYsgg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "netbsd" - ], - "peer": true, - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/openbsd-arm64": { - "version": "0.27.4", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.4.tgz", - "integrity": "sha512-2MyL3IAaTX+1/qP0O1SwskwcwCoOI4kV2IBX1xYnDDqthmq5ArrW94qSIKCAuRraMgPOmG0RDTA74mzYNQA9ow==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ], - "peer": true, - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/openbsd-x64": { - "version": "0.27.4", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.4.tgz", - "integrity": "sha512-u8fg/jQ5aQDfsnIV6+KwLOf1CmJnfu1ShpwqdwC0uA7ZPwFws55Ngc12vBdeUdnuWoQYx/SOQLGDcdlfXhYmXQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ], - "peer": true, - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/openharmony-arm64": { - "version": "0.27.4", - "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.4.tgz", - "integrity": "sha512-JkTZrl6VbyO8lDQO3yv26nNr2RM2yZzNrNHEsj9bm6dOwwu9OYN28CjzZkH57bh4w0I2F7IodpQvUAEd1mbWXg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openharmony" - ], - "peer": true, - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/sunos-x64": { - "version": "0.27.4", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.4.tgz", - "integrity": "sha512-/gOzgaewZJfeJTlsWhvUEmUG4tWEY2Spp5M20INYRg2ZKl9QPO3QEEgPeRtLjEWSW8FilRNacPOg8R1uaYkA6g==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "sunos" - ], - "peer": true, - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/win32-arm64": { - "version": "0.27.4", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.4.tgz", - "integrity": "sha512-Z9SExBg2y32smoDQdf1HRwHRt6vAHLXcxD2uGgO/v2jK7Y718Ix4ndsbNMU/+1Qiem9OiOdaqitioZwxivhXYg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "peer": true, - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/win32-ia32": { - "version": "0.27.4", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.4.tgz", - "integrity": "sha512-DAyGLS0Jz5G5iixEbMHi5KdiApqHBWMGzTtMiJ72ZOLhbu/bzxgAe8Ue8CTS3n3HbIUHQz/L51yMdGMeoxXNJw==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "peer": true, - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/win32-x64": { - "version": "0.27.4", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.4.tgz", - "integrity": "sha512-+knoa0BDoeXgkNvvV1vvbZX4+hizelrkwmGJBdT17t8FNPwG2lKemmuMZlmaNQ3ws3DKKCxpb4zRZEIp3UxFCg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "peer": true, - "engines": { - "node": ">=18" - } - }, - "node_modules/@flowforge/backend": { - "resolved": "apps/backend", - "link": true - }, - "node_modules/@flowforge/contract": { - "resolved": "packages/contract", - "link": true - }, - "node_modules/@flowforge/extension": { - "resolved": "apps/extension", - "link": true - }, - "node_modules/@flowforge/page-model": { - "resolved": "packages/page-model", - "link": true - }, - "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.13", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", - "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/sourcemap-codec": "^1.5.0", - "@jridgewell/trace-mapping": "^0.3.24" - } - }, - "node_modules/@jridgewell/remapping": { - "version": "2.3.5", - "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", - "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", + "node_modules/@jridgewell/remapping": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", + "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", "dev": true, "license": "MIT", "dependencies": { @@ -1009,9 +705,9 @@ } }, "node_modules/@lancedb/lancedb": { - "version": "0.27.1", - "resolved": "https://registry.npmjs.org/@lancedb/lancedb/-/lancedb-0.27.1.tgz", - "integrity": "sha512-LqXw19VYXaG1bV/03wN5JrYOSrRv38RPbKHW0hukbyfsNV4+VuZ3VpObk3gDeJGPkkJ56MS3lbbB2pqqg1DiNQ==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@lancedb/lancedb/-/lancedb-0.27.2.tgz", + "integrity": "sha512-JQpZHV5KzUzDI3flYCjtZcfHlEbL8lM54E0NT+jrRYe29aKYegfavvPsAsuZp0VdcMwFMZcpMkaBhjQMo/fwvg==", "cpu": [ "x64", "arm64" @@ -1029,22 +725,22 @@ "node": ">= 18" }, "optionalDependencies": { - "@lancedb/lancedb-darwin-arm64": "0.27.1", - "@lancedb/lancedb-linux-arm64-gnu": "0.27.1", - "@lancedb/lancedb-linux-arm64-musl": "0.27.1", - "@lancedb/lancedb-linux-x64-gnu": "0.27.1", - "@lancedb/lancedb-linux-x64-musl": "0.27.1", - "@lancedb/lancedb-win32-arm64-msvc": "0.27.1", - "@lancedb/lancedb-win32-x64-msvc": "0.27.1" + "@lancedb/lancedb-darwin-arm64": "0.27.2", + "@lancedb/lancedb-linux-arm64-gnu": "0.27.2", + "@lancedb/lancedb-linux-arm64-musl": "0.27.2", + "@lancedb/lancedb-linux-x64-gnu": "0.27.2", + "@lancedb/lancedb-linux-x64-musl": "0.27.2", + "@lancedb/lancedb-win32-arm64-msvc": "0.27.2", + "@lancedb/lancedb-win32-x64-msvc": "0.27.2" }, "peerDependencies": { "apache-arrow": ">=15.0.0 <=18.1.0" } }, "node_modules/@lancedb/lancedb-darwin-arm64": { - "version": "0.27.1", - "resolved": "https://registry.npmjs.org/@lancedb/lancedb-darwin-arm64/-/lancedb-darwin-arm64-0.27.1.tgz", - "integrity": "sha512-yF9N0Kyq2uIlfaoAoxBrCJ5M3cLRB+Iwx/uvwtt99UMZ5ZbUFXgMzJb8FYJwj/5voteal+53jQcbIbAH41MwUw==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@lancedb/lancedb-darwin-arm64/-/lancedb-darwin-arm64-0.27.2.tgz", + "integrity": "sha512-+XM68V/Rou8kKWDnUeKvg9ChKS0zGeQC2sKAop+06Ty4LwIjEGkeYBYrK0vMhZkBN5EFaOjTOp8E8hGQxdFwXA==", "cpu": [ "arm64" ], @@ -1058,9 +754,9 @@ } }, "node_modules/@lancedb/lancedb-linux-arm64-gnu": { - "version": "0.27.1", - "resolved": "https://registry.npmjs.org/@lancedb/lancedb-linux-arm64-gnu/-/lancedb-linux-arm64-gnu-0.27.1.tgz", - "integrity": "sha512-7P7TKxHjn7wfa7SQuDfxPU+DMt6thZJE0ccvc9RSifFVd8OxJdsWa2hvopsR3r985t68ggx8GjhW81ILr4iJ8w==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@lancedb/lancedb-linux-arm64-gnu/-/lancedb-linux-arm64-gnu-0.27.2.tgz", + "integrity": "sha512-laiTTDeMUTzm7t+t6ME5nNQMDoERjmkeuWAFWekbXiFdmp62Dqu34Lvf2BvpWnKwxLMZ5JcBJFIw32WS8/8Jnw==", "cpu": [ "arm64" ], @@ -1074,9 +770,9 @@ } }, "node_modules/@lancedb/lancedb-linux-arm64-musl": { - "version": "0.27.1", - "resolved": "https://registry.npmjs.org/@lancedb/lancedb-linux-arm64-musl/-/lancedb-linux-arm64-musl-0.27.1.tgz", - "integrity": "sha512-qD/6iRwi18y3QgWlydZ3KwC5PNMkGuKP9UZI1NGt/37S9DdRDgPDoKD67ftdc4HOVeg4gffEsaxoV781Sfdc9Q==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@lancedb/lancedb-linux-arm64-musl/-/lancedb-linux-arm64-musl-0.27.2.tgz", + "integrity": "sha512-bK5Mc50EvwGZaaiym5CoPu8Y4GNSyEEvTQ0dTC2AUIm83qdQu1rGw6kkYtc/rTH/hbvAvPQot4agHDZfMVxfYw==", "cpu": [ "arm64" ], @@ -1090,9 +786,9 @@ } }, "node_modules/@lancedb/lancedb-linux-x64-gnu": { - "version": "0.27.1", - "resolved": "https://registry.npmjs.org/@lancedb/lancedb-linux-x64-gnu/-/lancedb-linux-x64-gnu-0.27.1.tgz", - "integrity": "sha512-5ciKVaUH/tuBR2hAeM/4se+C18YsbNjgUDq4kxGfMh+/YI2v4Gl7mniYKh9jx93S2lQJJsVowqWGnOdkNB0Zhw==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@lancedb/lancedb-linux-x64-gnu/-/lancedb-linux-x64-gnu-0.27.2.tgz", + "integrity": "sha512-qe+ML0YmPru0o84f33RBHqoNk6zsHBjiXTLKsEBDiiFYKks/XMsrkKy9NQYcTxShBrg/nx/MLzCzd7dihqgNYw==", "cpu": [ "x64" ], @@ -1106,9 +802,9 @@ } }, "node_modules/@lancedb/lancedb-linux-x64-musl": { - "version": "0.27.1", - "resolved": "https://registry.npmjs.org/@lancedb/lancedb-linux-x64-musl/-/lancedb-linux-x64-musl-0.27.1.tgz", - "integrity": "sha512-eeXZsryHux4bg03nPAkNzgL9Zp6cXMvizYxQ0N6mdcpumnkoRoJC3VqwUzaeplMzqMhjATmoaes4GkTRzHsVwA==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@lancedb/lancedb-linux-x64-musl/-/lancedb-linux-x64-musl-0.27.2.tgz", + "integrity": "sha512-ZpX6Oxn06qvzAdm+D/gNb3SRp/A9lgRAPvPg6nnMmSQk5XamC/hbGO07uK1wwop7nlqXUH/thk4is2y2ieWdTw==", "cpu": [ "x64" ], @@ -1122,9 +818,9 @@ } }, "node_modules/@lancedb/lancedb-win32-arm64-msvc": { - "version": "0.27.1", - "resolved": "https://registry.npmjs.org/@lancedb/lancedb-win32-arm64-msvc/-/lancedb-win32-arm64-msvc-0.27.1.tgz", - "integrity": "sha512-xSUNYZQwgDI782lb8XHFvM57QEDHnIS4n+SYBLHH428urdEoJdA4tVrwOJZoIBayKHdv7sPFPIumP40U56FDIg==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@lancedb/lancedb-win32-arm64-msvc/-/lancedb-win32-arm64-msvc-0.27.2.tgz", + "integrity": "sha512-4ffpFvh49MiUtkdFJOmBytXEbgUPXORphTOuExnJAgT1VAKwQcu4ZzdsgNoK6mumKBaU+pYQU/MedNkgTzx/Lw==", "cpu": [ "arm64" ], @@ -1138,9 +834,9 @@ } }, "node_modules/@lancedb/lancedb-win32-x64-msvc": { - "version": "0.27.1", - "resolved": "https://registry.npmjs.org/@lancedb/lancedb-win32-x64-msvc/-/lancedb-win32-x64-msvc-0.27.1.tgz", - "integrity": "sha512-pPR0wHhxtXp57R69g40P2Qs3iNyM4mZqCB/bOyRFhIVreaGooSDtEQREZzALoO/xpP7pj/MeFJpsM8LGyfz1bw==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@lancedb/lancedb-win32-x64-msvc/-/lancedb-win32-x64-msvc-0.27.2.tgz", + "integrity": "sha512-XlwiI6CK2Gkqq+FFVAStHojao/XjIJpDPTm7Tb9SpLL64IlwGw3yaT2hnWKTm90W4KlSrpfSldPly+s+y4U7JQ==", "cpu": [ "x64" ], @@ -1154,21 +850,17 @@ } }, "node_modules/@langchain/core": { - "version": "1.1.38", - "resolved": "https://registry.npmjs.org/@langchain/core/-/core-1.1.38.tgz", - "integrity": "sha512-C340wH1YL10CiVOFlEpQMp0zQE85/eBLKX/gi1Lv7shAyUmR3CQ0t/mXlCd5RsZa6ntAN1kDJnp64ArWey9XAA==", + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/@langchain/core/-/core-1.2.5.tgz", + "integrity": "sha512-4lXj3fTPQYGdEtOG9gWDnvmp6wpXNMo9MmWzfZxxPUxMcjulZJa93pYAZ90luFLg2YVdVuUl2tuwdD7tY5K9MA==", "license": "MIT", "dependencies": { "@cfworker/json-schema": "^4.0.2", "@standard-schema/spec": "^1.1.0", - "ansi-styles": "^5.0.0", - "camelcase": "6", - "decamelize": "1.2.0", "js-tiktoken": "^1.0.12", "langsmith": ">=0.5.0 <1.0.0", "mustache": "^4.2.0", "p-queue": "^6.6.2", - "uuid": "^11.1.0", "zod": "^3.25.76 || ^4" }, "engines": { @@ -1176,80 +868,55 @@ } }, "node_modules/@langchain/langgraph": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/@langchain/langgraph/-/langgraph-1.2.6.tgz", - "integrity": "sha512-5cX402dNGN6w9+0mlMU2dgUiKx6Y1tlENp7x05e9ByDbQCHSDc0kyqRWNFLGc7vatQ92S4ylxQrcCJvi8Fr4SQ==", + "version": "1.4.9", + "resolved": "https://registry.npmjs.org/@langchain/langgraph/-/langgraph-1.4.9.tgz", + "integrity": "sha512-EvD9rS66Cya09y6rbMgD3Ir8miAkJQFo7FyJOPRPO736Kz3y5TeyeBDOS8ctff/jRc788bPijHx2NVFM79Qqig==", "license": "MIT", "dependencies": { - "@langchain/langgraph-checkpoint": "^1.0.1", - "@langchain/langgraph-sdk": "~1.8.1", - "@standard-schema/spec": "1.1.0", - "uuid": "^10.0.0" + "@langchain/langgraph-checkpoint": "^1.1.3", + "@langchain/langgraph-sdk": "~1.9.28", + "@langchain/protocol": "^0.0.18", + "@standard-schema/spec": "1.1.0" }, "engines": { "node": ">=18" }, "peerDependencies": { - "@langchain/core": "^1.1.16", - "zod": "^3.25.32 || ^4.2.0", - "zod-to-json-schema": "^3.x" - }, - "peerDependenciesMeta": { - "zod-to-json-schema": { - "optional": true - } + "@langchain/core": "^1.1.48", + "zod": "^3.25.32 || ^4.2.0" } }, "node_modules/@langchain/langgraph-checkpoint": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@langchain/langgraph-checkpoint/-/langgraph-checkpoint-1.0.1.tgz", - "integrity": "sha512-HM0cJLRpIsSlWBQ/xuDC67l52SqZ62Bh2Y61DX+Xorqwoh5e1KxYvfCD7GnSTbWWhjBOutvnR0vPhu4orFkZfw==", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@langchain/langgraph-checkpoint/-/langgraph-checkpoint-1.1.3.tgz", + "integrity": "sha512-wgzdQNeEsdw1e+4lvlj0tdq/RYR/k1vPin10g0ymGoehZDDgd9nvIllGXSXN4TFgF9sf5qQP/KTkOcLfeseIhA==", "license": "MIT", - "dependencies": { - "uuid": "^10.0.0" - }, "engines": { "node": ">=18" }, "peerDependencies": { - "@langchain/core": "^1.0.1" - } - }, - "node_modules/@langchain/langgraph-checkpoint/node_modules/uuid": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-10.0.0.tgz", - "integrity": "sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==", - "funding": [ - "https://github.com/sponsors/broofa", - "https://github.com/sponsors/ctavan" - ], - "license": "MIT", - "bin": { - "uuid": "dist/bin/uuid" + "@langchain/core": "^1.1.48" } }, "node_modules/@langchain/langgraph-sdk": { - "version": "1.8.2", - "resolved": "https://registry.npmjs.org/@langchain/langgraph-sdk/-/langgraph-sdk-1.8.2.tgz", - "integrity": "sha512-TQSw5O0NiE0W6D+UKcv6AdvCqQD2Xbv96oaUFUSSL3nnQ0WfZLcF5nPNTQm6KLUo4EuhKZeeoAgnDb2hRDW2Hw==", + "version": "1.9.29", + "resolved": "https://registry.npmjs.org/@langchain/langgraph-sdk/-/langgraph-sdk-1.9.29.tgz", + "integrity": "sha512-W+ccugM5EzBHvaOieyYxlSbhAy6HWF8Tyn6b/BlTWuFd8xtcnQOz2WuFyofcAc9+aQHE+6yHJfIywGOvjxS+bA==", "license": "MIT", "dependencies": { + "@langchain/protocol": "^0.0.18", "@types/json-schema": "^7.0.15", "p-queue": "^9.0.1", - "p-retry": "^7.1.1", - "uuid": "^13.0.0" + "p-retry": "^7.1.1" }, "peerDependencies": { - "@langchain/core": "^1.1.16", + "@langchain/core": "^1.1.48", "react": "^18 || ^19", "react-dom": "^18 || ^19", "svelte": "^4.0.0 || ^5.0.0", "vue": "^3.0.0" }, "peerDependenciesMeta": { - "@langchain/core": { - "optional": true - }, "react": { "optional": true }, @@ -1271,12 +938,12 @@ "license": "MIT" }, "node_modules/@langchain/langgraph-sdk/node_modules/p-queue": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/p-queue/-/p-queue-9.1.0.tgz", - "integrity": "sha512-O/ZPaXuQV29uSLbxWBGGZO1mCQXV2BLIwUr59JUU9SoH76mnYvtms7aafH/isNSNGwuEfP6W/4xD0/TJXxrizw==", + "version": "9.3.3", + "resolved": "https://registry.npmjs.org/p-queue/-/p-queue-9.3.3.tgz", + "integrity": "sha512-NXAOdnEe5FsZJfT4oK84lE1Y5cFFdWlRuOo5tww8DyNMxyRXwn39fIkUtNLKppcPC+UYU/bXujNCUGDv01y7CA==", "license": "MIT", "dependencies": { - "eventemitter3": "^5.0.1", + "eventemitter3": "^5.0.4", "p-timeout": "^7.0.0" }, "engines": { @@ -1298,40 +965,13 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@langchain/langgraph-sdk/node_modules/uuid": { - "version": "13.0.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-13.0.0.tgz", - "integrity": "sha512-XQegIaBTVUjSHliKqcnFqYypAd4S+WCYt5NIeRs6w/UAry7z8Y9j5ZwRRL4kzq9U3sD6v+85er9FvkEaBpji2w==", - "funding": [ - "https://github.com/sponsors/broofa", - "https://github.com/sponsors/ctavan" - ], - "license": "MIT", - "bin": { - "uuid": "dist-node/bin/uuid" - } - }, - "node_modules/@langchain/langgraph/node_modules/uuid": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-10.0.0.tgz", - "integrity": "sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==", - "funding": [ - "https://github.com/sponsors/broofa", - "https://github.com/sponsors/ctavan" - ], - "license": "MIT", - "bin": { - "uuid": "dist/bin/uuid" - } - }, "node_modules/@langchain/ollama": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/@langchain/ollama/-/ollama-1.2.6.tgz", - "integrity": "sha512-wEfjRjyB20SMduqjriIBEalXZf1twbfaNTxxLIjKCVrufHPtKJKGy1a0tQHqa+27HwektNNXlcMre7MTuaS5Rw==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@langchain/ollama/-/ollama-1.3.0.tgz", + "integrity": "sha512-uzLkZVTXN0SI5zAoJbJzL4y/QN7gbmJ1txBgFfqJ5M0Qf+Yu2Zu8q98L3jIrfM+akWMaNmRU4R4APhcfQmeIsw==", "license": "MIT", "dependencies": { - "ollama": "^0.6.3", - "uuid": "^10.0.0" + "ollama": "^0.6.3" }, "engines": { "node": ">=20" @@ -1340,36 +980,29 @@ "@langchain/core": "^1.0.0" } }, - "node_modules/@langchain/ollama/node_modules/uuid": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-10.0.0.tgz", - "integrity": "sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==", - "funding": [ - "https://github.com/sponsors/broofa", - "https://github.com/sponsors/ctavan" - ], - "license": "MIT", - "bin": { - "uuid": "dist/bin/uuid" - } - }, "node_modules/@langchain/openai": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/@langchain/openai/-/openai-1.4.1.tgz", - "integrity": "sha512-jaHk4TnLqWrQ1KYmavvwCImW6x8pBy6LLTK73tzSMg7HBLbq0g/l7EkpMcxZWDOvyufuCXUqO2bj47apcOhw6Q==", + "version": "1.5.6", + "resolved": "https://registry.npmjs.org/@langchain/openai/-/openai-1.5.6.tgz", + "integrity": "sha512-1cesvhCXw30tMYWXXQaK2gN4aDIKq266LcMSjZn7O/kue/vPnCOAxiwy54HcGQQf7m/sVKfhOn4QwVRObSeAsg==", "license": "MIT", "dependencies": { "js-tiktoken": "^1.0.12", - "openai": "^6.32.0", + "openai": "^6.41.0", "zod": "^3.25.76 || ^4" }, "engines": { "node": ">=20" }, "peerDependencies": { - "@langchain/core": "^1.1.38" + "@langchain/core": "^1.2.5" } }, + "node_modules/@langchain/protocol": { + "version": "0.0.18", + "resolved": "https://registry.npmjs.org/@langchain/protocol/-/protocol-0.0.18.tgz", + "integrity": "sha512-XW1egQtPfsGI41w2AMZNFZrUIwFSQHTjVMZs0OaTpCAvht/QLoaPN8FQcsysMVypOhupG28J29yOorrc70otBQ==", + "license": "MIT" + }, "node_modules/@langchain/textsplitters": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/@langchain/textsplitters/-/textsplitters-1.0.1.tgz", @@ -1385,25 +1018,6 @@ "@langchain/core": "^1.0.0" } }, - "node_modules/@napi-rs/wasm-runtime": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.3.tgz", - "integrity": "sha512-xK9sGVbJWYb08+mTJt3/YV24WxvxpXcXtP6B172paPZ+Ts69Re9dAr7lKwJoeIx8OoeuimEiRZ7umkiUVClmmQ==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "@tybys/wasm-util": "^0.10.1" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/Brooooooklyn" - }, - "peerDependencies": { - "@emnapi/core": "^1.7.1", - "@emnapi/runtime": "^1.7.1" - } - }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -1443,9 +1057,9 @@ } }, "node_modules/@oxc-project/types": { - "version": "0.124.0", - "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.124.0.tgz", - "integrity": "sha512-VBFWMTBvHxS11Z5Lvlr3IWgrwhMTXV+Md+EQF0Xf60+wAdsGFTBx7X7K/hP4pi8N7dcm1RvcHwDxZ16Qx8keUg==", + "version": "0.143.0", + "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.143.0.tgz", + "integrity": "sha512-u6JZdLBTLotrNC9Vd6vPssINdzcCzleKAH6EJKImQb7GtYvX5keN2dxkoK44stCc4tffE6QQRtZTXVSzsLUlWA==", "dev": true, "license": "MIT", "funding": { @@ -1453,9 +1067,9 @@ } }, "node_modules/@preact/preset-vite": { - "version": "2.10.5", - "resolved": "https://registry.npmjs.org/@preact/preset-vite/-/preset-vite-2.10.5.tgz", - "integrity": "sha512-p0vJpxiVO7KWWazWny3LUZ+saXyZKWv6Ju0bYMWNJRp2YveufRPgSUB1C4MTqGJfz07EehMgfN+AJNwQy+w6Iw==", + "version": "2.10.6", + "resolved": "https://registry.npmjs.org/@preact/preset-vite/-/preset-vite-2.10.6.tgz", + "integrity": "sha512-ZZ5RIT2ZVXg8UxRA8VZpoT8CdpDG7RFIqOT4bELqNBp85+HKvQBbgmh3gsoW1UOQXx26TLe+ZRNKI7q281Kckw==", "dev": true, "license": "MIT", "dependencies": { @@ -1475,53 +1089,17 @@ "vite": "2.x || 3.x || 4.x || 5.x || 6.x || 7.x || 8.x" } }, - "node_modules/@preact/preset-vite/node_modules/@rollup/pluginutils": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.3.0.tgz", - "integrity": "sha512-5EdhGZtnu3V88ces7s53hhfK5KSASnJZv8Lulpc04cWO3REESroJXg73DFsOmgbU2BhwV0E20bu2IDZb3VKW4Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/estree": "^1.0.0", - "estree-walker": "^2.0.2", - "picomatch": "^4.0.2" - }, - "engines": { - "node": ">=14.0.0" - }, - "peerDependencies": { - "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" - }, - "peerDependenciesMeta": { - "rollup": { - "optional": true - } - } - }, - "node_modules/@preact/preset-vite/node_modules/picomatch": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", - "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/@prefresh/babel-plugin": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/@prefresh/babel-plugin/-/babel-plugin-0.5.3.tgz", - "integrity": "sha512-57LX2SHs4BX2s1IwCjNzTE2OJeEepRCNf1VTEpbNcUyHfMO68eeOWGDIt4ob9aYlW6PEWZ1SuwNikuoIXANDtQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/@prefresh/core": { - "version": "1.5.9", - "resolved": "https://registry.npmjs.org/@prefresh/core/-/core-1.5.9.tgz", - "integrity": "sha512-IKBKCPaz34OFVC+adiQ2qaTF5qdztO2/4ZPf4KsRTgjKosWqxVXmEbxCiUydYZRY8GVie+DQlKzQr9gt6HQ+EQ==", + "node_modules/@prefresh/babel-plugin": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/@prefresh/babel-plugin/-/babel-plugin-0.5.3.tgz", + "integrity": "sha512-57LX2SHs4BX2s1IwCjNzTE2OJeEepRCNf1VTEpbNcUyHfMO68eeOWGDIt4ob9aYlW6PEWZ1SuwNikuoIXANDtQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@prefresh/core": { + "version": "1.5.10", + "resolved": "https://registry.npmjs.org/@prefresh/core/-/core-1.5.10.tgz", + "integrity": "sha512-7yPTFbG56sutaFu8krp3B4a200KOFUvrtlllKWRuLjsYXo9UUucHOZRcer+gtgMkFTpv6ob8TGcTwA32bSwa1w==", "dev": true, "license": "MIT", "peerDependencies": { @@ -1553,10 +1131,37 @@ "vite": ">=2.0.0" } }, + "node_modules/@prefresh/vite/node_modules/@rollup/pluginutils": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-4.2.1.tgz", + "integrity": "sha512-iKnFXr7NkdZAIHiIWE+BX5ULi/ucVFYWD6TbAV+rZctiRTY2PL6tsIKhoIOaoskiWAkgu+VsbXgUVDNLHf+InQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "estree-walker": "^2.0.1", + "picomatch": "^2.2.2" + }, + "engines": { + "node": ">= 8.0.0" + } + }, + "node_modules/@prefresh/vite/node_modules/picomatch": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", + "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, "node_modules/@rolldown/binding-android-arm64": { - "version": "1.0.0-rc.15", - "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.0.0-rc.15.tgz", - "integrity": "sha512-YYe6aWruPZDtHNpwu7+qAHEMbQ/yRl6atqb/AhznLTnD3UY99Q1jE7ihLSahNWkF4EqRPVC4SiR4O0UkLK02tA==", + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.2.3.tgz", + "integrity": "sha512-zrJtHDcaZJ1Fp7xf4hNl+7seH9Cn/N5TwLYkhgXREtBwAd/jaqW3uqeHxpDugJLVICWg4eW44kOQEGJ1r6jCGw==", "cpu": [ "arm64" ], @@ -1571,9 +1176,9 @@ } }, "node_modules/@rolldown/binding-darwin-arm64": { - "version": "1.0.0-rc.15", - "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.0.0-rc.15.tgz", - "integrity": "sha512-oArR/ig8wNTPYsXL+Mzhs0oxhxfuHRfG7Ikw7jXsw8mYOtk71W0OkF2VEVh699pdmzjPQsTjlD1JIOoHkLP1Fg==", + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.2.3.tgz", + "integrity": "sha512-ieIiibVCp0tX7TLu2cafoNPv8wJyYi01ekXpbf8q2j7F4rGAhhXb/eQh7ge9DRBY78GwmRQtvjZDux7EDbA8kA==", "cpu": [ "arm64" ], @@ -1588,9 +1193,9 @@ } }, "node_modules/@rolldown/binding-darwin-x64": { - "version": "1.0.0-rc.15", - "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.0.0-rc.15.tgz", - "integrity": "sha512-YzeVqOqjPYvUbJSWJ4EDL8ahbmsIXQpgL3JVipmN+MX0XnXMeWomLN3Fb+nwCmP/jfyqte5I3XRSm7OfQrbyxw==", + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.2.3.tgz", + "integrity": "sha512-Zh9tCon19eDXJoihx0rqKhMUlMYqzwj3aPsSuHmI4RWZh62dWUL+DJN4C5YQya5TcQBJU/Fe8+rY0jhXTQITqA==", "cpu": [ "x64" ], @@ -1605,9 +1210,9 @@ } }, "node_modules/@rolldown/binding-freebsd-x64": { - "version": "1.0.0-rc.15", - "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.0.0-rc.15.tgz", - "integrity": "sha512-9Erhx956jeQ0nNTyif1+QWAXDRD38ZNjr//bSHrt6wDwB+QkAfl2q6Mn1k6OBPerznjRmbM10lgRb1Pli4xZPw==", + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.2.3.tgz", + "integrity": "sha512-nGbJWewA1wrXXZiQhjAT5rhibGfns5ZNkDVqxsO6zJ3f3YvpoDNNmGMSbbhLuXKjNScaBJVOAboztAWVespQMg==", "cpu": [ "x64" ], @@ -1622,9 +1227,9 @@ } }, "node_modules/@rolldown/binding-linux-arm-gnueabihf": { - "version": "1.0.0-rc.15", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.0.0-rc.15.tgz", - "integrity": "sha512-cVwk0w8QbZJGTnP/AHQBs5yNwmpgGYStL88t4UIaqcvYJWBfS0s3oqVLZPwsPU6M0zlW4GqjP0Zq5MnAGwFeGA==", + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.2.3.tgz", + "integrity": "sha512-QNniJr5Kml0kDEB98jiDOJjXNroxIIi0IXIbdYzY26Xt1pVbeP62+KnoIZLwirOymX/0jDk/2gI/bNUv7A7OIw==", "cpu": [ "arm" ], @@ -1639,9 +1244,9 @@ } }, "node_modules/@rolldown/binding-linux-arm64-gnu": { - "version": "1.0.0-rc.15", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.0.0-rc.15.tgz", - "integrity": "sha512-eBZ/u8iAK9SoHGanqe/jrPnY0JvBN6iXbVOsbO38mbz+ZJsaobExAm1Iu+rxa4S1l2FjG0qEZn4Rc6X8n+9M+w==", + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.2.3.tgz", + "integrity": "sha512-TkqEAcmmvH3I/q4114NB4RVt6241Dao48pF45uLcFGrwAaIn0iITgTAKP/dLjbN0R4buJjGb91+UHSoFmpgIWw==", "cpu": [ "arm64" ], @@ -1656,9 +1261,9 @@ } }, "node_modules/@rolldown/binding-linux-arm64-musl": { - "version": "1.0.0-rc.15", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.0.0-rc.15.tgz", - "integrity": "sha512-ZvRYMGrAklV9PEkgt4LQM6MjQX2P58HPAuecwYObY2DhS2t35R0I810bKi0wmaYORt6m/2Sm+Z+nFgb0WhXNcQ==", + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.2.3.tgz", + "integrity": "sha512-NHqjnxpsndf4MPymxteFAWHHfkTL8HjWh1KB7z23ofZ6QO2euONuxDXjat69dKZRALnGypg8k8SsK8vZJoXv1Q==", "cpu": [ "arm64" ], @@ -1673,9 +1278,9 @@ } }, "node_modules/@rolldown/binding-linux-ppc64-gnu": { - "version": "1.0.0-rc.15", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.0.0-rc.15.tgz", - "integrity": "sha512-VDpgGBzgfg5hLg+uBpCLoFG5kVvEyafmfxGUV0UHLcL5irxAK7PKNeC2MwClgk6ZAiNhmo9FLhRYgvMmedLtnQ==", + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.2.3.tgz", + "integrity": "sha512-6tbrbwfz5GB9DQ4Jwo6hy9v+vR31xZlvzZ6n5Xut6Hhx5PvrA9q/HsK8KMaYQp063iqZGXwNvZtYNLD7EM/x0w==", "cpu": [ "ppc64" ], @@ -1690,9 +1295,9 @@ } }, "node_modules/@rolldown/binding-linux-s390x-gnu": { - "version": "1.0.0-rc.15", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.0.0-rc.15.tgz", - "integrity": "sha512-y1uXY3qQWCzcPgRJATPSOUP4tCemh4uBdY7e3EZbVwCJTY3gLJWnQABgeUetvED+bt1FQ01OeZwvhLS2bpNrAQ==", + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.2.3.tgz", + "integrity": "sha512-oyuXxXmoZHjXC917IAPFAAv4wWAa0cM9afk8nx1+9/jNNOX1uPf8yDA6p7G0RypOfw/X0PQt5IfoquY1um+zSg==", "cpu": [ "s390x" ], @@ -1707,9 +1312,9 @@ } }, "node_modules/@rolldown/binding-linux-x64-gnu": { - "version": "1.0.0-rc.15", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.0.0-rc.15.tgz", - "integrity": "sha512-023bTPBod7J3Y/4fzAN6QtpkSABR0rigtrwaP+qSEabUh5zf6ELr9Nc7GujaROuPY3uwdSIXWrvhn1KxOvurWA==", + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.2.3.tgz", + "integrity": "sha512-TytMwF2KVGqP2tgd0I1OY0PAv78dZRAYcF5ssDzjM34SUXCED3uXvSd5+lHoC0bTD6eEdFz7LdQNCO1y0oVk9w==", "cpu": [ "x64" ], @@ -1724,9 +1329,9 @@ } }, "node_modules/@rolldown/binding-linux-x64-musl": { - "version": "1.0.0-rc.15", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.0.0-rc.15.tgz", - "integrity": "sha512-witB2O0/hU4CgfOOKUoeFgQ4GktPi1eEbAhaLAIpgD6+ZnhcPkUtPsoKKHRzmOoWPZue46IThdSgdo4XneOLYw==", + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.2.3.tgz", + "integrity": "sha512-/E9m3qstrJFVPoULV25mVQblSNExY2+kBsYe4sy0Tn0yOOgJ8wZbZt3KnRbF/XeU2Gl1STKUQnDNTqhIE5MD4A==", "cpu": [ "x64" ], @@ -1741,9 +1346,9 @@ } }, "node_modules/@rolldown/binding-openharmony-arm64": { - "version": "1.0.0-rc.15", - "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.0.0-rc.15.tgz", - "integrity": "sha512-UCL68NJ0Ud5zRipXZE9dF5PmirzJE4E4BCIOOssEnM7wLDsxjc6Qb0sGDxTNRTP53I6MZpygyCpY8Aa8sPfKPg==", + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.2.3.tgz", + "integrity": "sha512-Kr0OcsoQI816i6HOl3vFHpd1K0eZyh76zgfj4c1nTyaTsd5r2Mj1lwM4R90y/qaCfmTn9eHy0SKwi98eitRxug==", "cpu": [ "arm64" ], @@ -1757,29 +1362,10 @@ "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/@rolldown/binding-wasm32-wasi": { - "version": "1.0.0-rc.15", - "resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.0.0-rc.15.tgz", - "integrity": "sha512-ApLruZq/ig+nhaE7OJm4lDjayUnOHVUa77zGeqnqZ9pn0ovdVbbNPerVibLXDmWeUZXjIYIT8V3xkT58Rm9u5Q==", - "cpu": [ - "wasm32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "@emnapi/core": "1.9.2", - "@emnapi/runtime": "1.9.2", - "@napi-rs/wasm-runtime": "^1.1.3" - }, - "engines": { - "node": ">=14.0.0" - } - }, "node_modules/@rolldown/binding-win32-arm64-msvc": { - "version": "1.0.0-rc.15", - "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.0.0-rc.15.tgz", - "integrity": "sha512-KmoUoU7HnN+Si5YWJigfTws1jz1bKBYDQKdbLspz0UaqjjFkddHsqorgiW1mxcAj88lYUE6NC/zJNwT+SloqtA==", + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.2.3.tgz", + "integrity": "sha512-hOtMwTqnME+/gJcH/PCZ0wn0zPUjiWOgkHpxbSJpfGKMezHltx1S7/k1SitzVa7Ww2cqrDDaFbZEhcJZO8o+Jw==", "cpu": [ "arm64" ], @@ -1794,9 +1380,9 @@ } }, "node_modules/@rolldown/binding-win32-x64-msvc": { - "version": "1.0.0-rc.15", - "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.0.0-rc.15.tgz", - "integrity": "sha512-3P2A8L+x75qavWLe/Dll3EYBJLQmtkJN8rfh+U/eR3MqMgL/h98PhYI+JFfXuDPgPeCB7iZAKiqii5vqOvnA0g==", + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.2.3.tgz", + "integrity": "sha512-ekcqMMkI2PlhYnfzQnB/cEdYUVVJViWvoUyLrbzgDoi3Snfc1mVBwdnc306ufA5ejy8JSPjT2RlW1nQSjW7efg==", "cpu": [ "x64" ], @@ -1811,24 +1397,33 @@ } }, "node_modules/@rolldown/pluginutils": { - "version": "1.0.0-rc.15", - "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-rc.15.tgz", - "integrity": "sha512-UromN0peaE53IaBRe9W7CjrZgXl90fqGpK+mIZbA3qSTeYqg3pqpROBdIPvOG3F5ereDHNwoHBI2e50n1BDr1g==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.1.tgz", + "integrity": "sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==", "dev": true, "license": "MIT" }, "node_modules/@rollup/pluginutils": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-4.2.1.tgz", - "integrity": "sha512-iKnFXr7NkdZAIHiIWE+BX5ULi/ucVFYWD6TbAV+rZctiRTY2PL6tsIKhoIOaoskiWAkgu+VsbXgUVDNLHf+InQ==", + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.4.0.tgz", + "integrity": "sha512-MfPp06CjRLfXQ3wY0R8vJDYBy/MvVcc9OulEfR0B8Iv9ko+GCNaRZ+EpJYFl27LhKsZK0o420sYCRHCjfCgeUg==", "dev": true, "license": "MIT", "dependencies": { - "estree-walker": "^2.0.1", - "picomatch": "^2.2.2" + "@types/estree": "^1.0.0", + "estree-walker": "^2.0.2", + "picomatch": "^4.0.2" }, "engines": { - "node": ">= 8.0.0" + "node": ">=14.0.0" + }, + "peerDependencies": { + "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } } }, "node_modules/@standard-schema/spec": { @@ -1838,25 +1433,28 @@ "license": "MIT" }, "node_modules/@swc/helpers": { - "version": "0.5.20", - "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.20.tgz", - "integrity": "sha512-2egEBHUMasdypIzrprsu8g+OEVd7Vp2MM3a2eVlM/cyFYto0nGz5BX5BTgh/ShZZI9ed+ozEq+Ngt+rgmUs8tw==", + "version": "0.5.23", + "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.23.tgz", + "integrity": "sha512-5lSsMOTXURePglDfvuAQUqkGek9Hg2kksOYay2m0+XR++b2NWYL/4sWyuvVBIs8oKnJaxkdi9whaL/sqN13afw==", "license": "Apache-2.0", "peer": true, "dependencies": { "tslib": "^2.8.0" } }, - "node_modules/@tybys/wasm-util": { - "version": "0.10.1", - "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.1.tgz", - "integrity": "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==", + "node_modules/@tsconfig/node22": { + "version": "22.0.5", + "resolved": "https://registry.npmjs.org/@tsconfig/node22/-/node22-22.0.5.tgz", + "integrity": "sha512-hLf2ld+sYN/BtOJjHUWOk568dvjFQkHnLNa6zce25GIH+vxKfvTgm3qpaH6ToF5tu/NN0IH66s+Bb5wElHrLcw==", "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "tslib": "^2.4.0" - } + "license": "MIT" + }, + "node_modules/@tsconfig/vite-react": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/@tsconfig/vite-react/-/vite-react-8.1.1.tgz", + "integrity": "sha512-TdLtRE9INt7aupEC2yhem6y4RN8T+B4oa2OUYsay0ioxSDl8Q4yaq0k2yc86Gb2BEHjKp4RUxgIzqOTk4IQ8dQ==", + "dev": true, + "license": "MIT" }, "node_modules/@types/body-parser": { "version": "1.19.6", @@ -1869,10 +1467,21 @@ "@types/node": "*" } }, + "node_modules/@types/chai": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/@types/chai/-/chai-5.2.3.tgz", + "integrity": "sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/deep-eql": "*", + "assertion-error": "^2.0.1" + } + }, "node_modules/@types/chrome": { - "version": "0.1.38", - "resolved": "https://registry.npmjs.org/@types/chrome/-/chrome-0.1.38.tgz", - "integrity": "sha512-5aK4m9wZqoWAoB98aElESLm/5pXpqJnFWMNoiCs/XdPsXR6wNdVkJFSdQ9Wr4PnTuUrxD0SuNuDHh3EG5QeBzA==", + "version": "0.1.43", + "resolved": "https://registry.npmjs.org/@types/chrome/-/chrome-0.1.43.tgz", + "integrity": "sha512-ukH/HhmR6ht+UTX3PLUWJxgJ/RQcK2Foj4lBzsF24SIWsXgqhGuXqjd8FFuwioPP7d/JUKLM4g8GZxw3F4HTcA==", "dev": true, "license": "MIT", "dependencies": { @@ -1914,10 +1523,24 @@ "@types/node": "*" } }, + "node_modules/@types/deep-eql": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/deep-eql/-/deep-eql-4.0.2.tgz", + "integrity": "sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/esrecurse": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/@types/esrecurse/-/esrecurse-4.3.1.tgz", + "integrity": "sha512-xJBAbDifo5hpffDBuHl0Y8ywswbiAp/Wi7Y/GtAgSlZyIABppyurxVueOPE8LUQOxdlgi6Zqce7uoEpqNTeiUw==", + "dev": true, + "license": "MIT" + }, "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==", + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz", + "integrity": "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==", "dev": true, "license": "MIT" }, @@ -1934,9 +1557,9 @@ } }, "node_modules/@types/express-serve-static-core": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-5.1.1.tgz", - "integrity": "sha512-v4zIMr/cX7/d2BpAEX3KNKL/JrT1s43s96lLvvdTmza1oEvDudCqK9aF/djc/SWgy8Yh0h30TZx5VpzqFCxk5A==", + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-5.1.3.tgz", + "integrity": "sha512-dPfW8NFiOF4wOHc7+N/QSxlY9cfSsenewGbAz8C8U/MULPd/YZ27LvJUIlzaXie7e6Ove9YunJGgC9tbHD2cKw==", "dev": true, "license": "MIT", "dependencies": { @@ -1983,57 +1606,465 @@ "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", "license": "MIT" }, - "node_modules/@types/node": { - "version": "25.5.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-25.5.0.tgz", - "integrity": "sha512-jp2P3tQMSxWugkCUKLRPVUpGaL5MVFwF8RDuSRztfwgN1wmqJeMSbKlnEtQqU8UrhTmzEmZdu2I6v2dpp7XIxw==", + "node_modules/@types/node": { + "version": "25.9.5", + "resolved": "https://registry.npmjs.org/@types/node/-/node-25.9.5.tgz", + "integrity": "sha512-OScDchr2fwuUmWdf4kZ9h7PcJiYDVInhJizG/biAq3cAvqwYktuy/TYGGdZNMtNTFUP7rnb0NU4TUdm82kt4Rg==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": ">=7.24.0 <7.24.7" + } + }, + "node_modules/@types/qs": { + "version": "6.15.1", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.15.1.tgz", + "integrity": "sha512-GZHUBZR9hckSUhrxmp1nG6NwdpM9fCunJwyThLW1X3AyHgd9IlHb6VANpQQqDr2o/qQp6McZ3y/IA2rVzKzSbw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/range-parser": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz", + "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/send": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@types/send/-/send-1.2.1.tgz", + "integrity": "sha512-arsCikDvlU99zl1g69TcAB3mzZPpxgw0UQnaHeC1Nwb015xp8bknZv5rIfri9xTOcMuaVgvabfIRA7PSZVuZIQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/serve-static": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-2.2.0.tgz", + "integrity": "sha512-8mam4H1NHLtu7nmtalF7eyBH14QyOASmcxHhSfEoRyr0nP/YdoesEtU+uSRvMe96TW/HPTtkoKqQLl53N7UXMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/http-errors": "*", + "@types/node": "*" + } + }, + "node_modules/@types/whatwg-mimetype": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/whatwg-mimetype/-/whatwg-mimetype-3.0.2.tgz", + "integrity": "sha512-c2AKvDT8ToxLIOUlN51gTiHXflsfIFisS4pO7pDPoKouJCESkhZnEy623gwP9laCy5lnLDAw1vAzu2vM2YLOrA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/ws": { + "version": "8.18.1", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.18.1.tgz", + "integrity": "sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "8.67.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.67.0.tgz", + "integrity": "sha512-Un7Heoyj65NREbKAyIrFxeM143NZpExWmy1Nep4DLeQOeLlTeumPjoNKnBrU5D5moWXbPJgRa5Uwcdu0faVNGQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/regexpp": "^4.12.2", + "@typescript-eslint/scope-manager": "8.67.0", + "@typescript-eslint/type-utils": "8.67.0", + "@typescript-eslint/utils": "8.67.0", + "@typescript-eslint/visitor-keys": "8.67.0", + "ignore": "^7.0.5", + "natural-compare": "^1.4.0", + "ts-api-utils": "^2.5.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^8.67.0", + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.6.tgz", + "integrity": "sha512-BAg6QkE8W+TuQLrrw0Ugr7HegXduRuuj8/ti2kSOc+jz1dmx8/WNcjr6XGnq5YpDWxFwwaavqD0+jIUOKelTsw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/@typescript-eslint/parser": { + "version": "8.67.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.67.0.tgz", + "integrity": "sha512-fUBfTuuEulWqX6V8+O3PtScV01tzYYRUDTAirHFKoRAt7nOzoGiPt0M/bB47wWNy0coOOcgEwAMUtBpykMxl6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/scope-manager": "8.67.0", + "@typescript-eslint/types": "8.67.0", + "@typescript-eslint/typescript-estree": "8.67.0", + "@typescript-eslint/visitor-keys": "8.67.0", + "debug": "^4.4.3" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/project-service": { + "version": "8.67.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.67.0.tgz", + "integrity": "sha512-cvE8c7ulYeXN9fYuszhCeCsbzyVEXuhrRCybnBre7TUmqb5nRmBfQAwCj0O3WJFDeyAZt4VYv51vMCC9LHSdYw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/tsconfig-utils": "^8.67.0", + "@typescript-eslint/types": "^8.67.0", + "debug": "^4.4.3" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "8.67.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.67.0.tgz", + "integrity": "sha512-EgvsleTwS4E+WzzSvem8fAUubLwatMNF1B5hHSLQxcvs7q2dtRhGyujHwLJSYlG41niJ7GP24Aha2+0mb1b2kg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.67.0", + "@typescript-eslint/visitor-keys": "8.67.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/tsconfig-utils": { + "version": "8.67.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.67.0.tgz", + "integrity": "sha512-vV+LUSv5njUWsknE71fqKTlXUva+R76SaeORd6Zojcunk/6DvKFXONU3BrAs2H49mbygUXt6gbYunzwqNwlhdg==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/type-utils": { + "version": "8.67.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.67.0.tgz", + "integrity": "sha512-aVWDXbRmdXO9siTfX4ditQI1T9+zVcNazT48EJCD0v40/9RIFoUgZ05CmGEq9H2gixRpjUn/iplwvlcvutJW/Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.67.0", + "@typescript-eslint/typescript-estree": "8.67.0", + "@typescript-eslint/utils": "8.67.0", + "debug": "^4.4.3", + "ts-api-utils": "^2.5.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/types": { + "version": "8.67.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.67.0.tgz", + "integrity": "sha512-sBtgslww8nsMYUjhdPBiSyUqSzT8uR6g93A2QXnQC8+cGdjz0CyaOdqHDRJb1AtORbZCNUJBBeFA/tNR2uQmww==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "8.67.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.67.0.tgz", + "integrity": "sha512-EKQBCE9yNlRJYm7jdTW5AhDacDUmSwQb0FAJAmK2EKYrNXIsa2vxcSZx6PvJ/dEdI6lS+Y9W+EXckLj0iPFGcw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/project-service": "8.67.0", + "@typescript-eslint/tsconfig-utils": "8.67.0", + "@typescript-eslint/types": "8.67.0", + "@typescript-eslint/visitor-keys": "8.67.0", + "debug": "^4.4.3", + "minimatch": "^10.2.2", + "semver": "^7.7.3", + "tinyglobby": "^0.2.15", + "ts-api-utils": "^2.5.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": { + "version": "7.8.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz", + "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@typescript-eslint/utils": { + "version": "8.67.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.67.0.tgz", + "integrity": "sha512-U9D1FdwEWBwok3hxxSdhclMb0twvt9QnjIQ0VfQ1AiX2epnpSgv2ubVDsayOFyY8K6FX+AQ7E0FKWVG3iKsj1A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.9.1", + "@typescript-eslint/scope-manager": "8.67.0", + "@typescript-eslint/types": "8.67.0", + "@typescript-eslint/typescript-estree": "8.67.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "8.67.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.67.0.tgz", + "integrity": "sha512-fkv8dHRDqfGtTHuJeebdrQ7cX6Ad4WAS00rgHh9UGvMycF1mjBfsxry1XsLIFhWZ6Judlh6UdzK+TYlbpCXgnA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.67.0", + "eslint-visitor-keys": "^5.0.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@vitest/coverage-v8": { + "version": "4.1.10", + "resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-4.1.10.tgz", + "integrity": "sha512-IM49HmthevbgAO4anp1hwtoT9wYe59w0LR00gr+eagHE+ZJ5lK4sLPeO0ubgoJcwLk6dehU3R24N+FbEEKDc8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@bcoe/v8-coverage": "^1.0.2", + "@vitest/utils": "4.1.10", + "ast-v8-to-istanbul": "^1.0.0", + "istanbul-lib-coverage": "^3.2.2", + "istanbul-lib-report": "^3.0.1", + "istanbul-reports": "^3.2.0", + "magicast": "^0.5.2", + "obug": "^2.1.1", + "std-env": "^4.0.0-rc.1", + "tinyrainbow": "^3.1.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "@vitest/browser": "4.1.10", + "vitest": "4.1.10" + }, + "peerDependenciesMeta": { + "@vitest/browser": { + "optional": true + } + } + }, + "node_modules/@vitest/expect": { + "version": "4.1.10", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.1.10.tgz", + "integrity": "sha512-YsCn+qAk1GWjQOWFEsEcL2gNQ0zmVmQu3T03qP6UyjhtmdtwtbuI+DASn/7iQB3HGTXkdBwGddzxPlmiql5vlA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@standard-schema/spec": "^1.1.0", + "@types/chai": "^5.2.2", + "@vitest/spy": "4.1.10", + "@vitest/utils": "4.1.10", + "chai": "^6.2.2", + "tinyrainbow": "^3.1.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/mocker": { + "version": "4.1.10", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-4.1.10.tgz", + "integrity": "sha512-v0xaezt+DKEmKfaxg133ldzADrwLGd7Ze1MfQQTYfvs8OqZIwbxyxaYURivwV7sWy5fqn3rH5uOrSp07bp44Ow==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/spy": "4.1.10", + "estree-walker": "^3.0.3", + "magic-string": "^0.30.21" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "msw": "^2.4.9", + "vite": "^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "msw": { + "optional": true + }, + "vite": { + "optional": true + } + } + }, + "node_modules/@vitest/mocker/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/@vitest/pretty-format": { + "version": "4.1.10", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.1.10.tgz", + "integrity": "sha512-W1HsjSH4MXQ9YfmmhLAoIYf1HRfekQCGngeIgcei6MP5QQGWUe0gkopdZQaVCFO+JDJMrAJGwa5pRpNpvy4P8Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "tinyrainbow": "^3.1.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/runner": { + "version": "4.1.10", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-4.1.10.tgz", + "integrity": "sha512-IKI6kpIH+LmpROplyLwBBaCfMgOZOMsygVa6BARD6ahA04VRuJSa6OaVG7kRvSEMD870Vd91rSSw0eegtWyLGg==", "dev": true, "license": "MIT", "dependencies": { - "undici-types": "~7.18.0" + "@vitest/utils": "4.1.10", + "pathe": "^2.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" } }, - "node_modules/@types/qs": { - "version": "6.15.0", - "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.15.0.tgz", - "integrity": "sha512-JawvT8iBVWpzTrz3EGw9BTQFg3BQNmwERdKE22vlTxawwtbyUSlMppvZYKLZzB5zgACXdXxbD3m1bXaMqP/9ow==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/range-parser": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz", - "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==", + "node_modules/@vitest/snapshot": { + "version": "4.1.10", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-4.1.10.tgz", + "integrity": "sha512-xRkfOT1qpTAi/Ti4Y1LtfRc3kEuqxGw59eN2jN9pRWMtS/XDevekhcFSqvQqjUNGksfjMJu3Y+oJ+4Ypn2OaJw==", "dev": true, - "license": "MIT" + "license": "MIT", + "dependencies": { + "@vitest/pretty-format": "4.1.10", + "@vitest/utils": "4.1.10", + "magic-string": "^0.30.21", + "pathe": "^2.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } }, - "node_modules/@types/send": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@types/send/-/send-1.2.1.tgz", - "integrity": "sha512-arsCikDvlU99zl1g69TcAB3mzZPpxgw0UQnaHeC1Nwb015xp8bknZv5rIfri9xTOcMuaVgvabfIRA7PSZVuZIQ==", + "node_modules/@vitest/spy": { + "version": "4.1.10", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-4.1.10.tgz", + "integrity": "sha512-PLf/Ugvoq5wO/b4rwYCR1h2PSIdXz7wnkQFMiUpLdtM7l6pqVFcQIBEHyT1+l+cj7mNwAfZHzqXqDyjvOuwbDw==", "dev": true, "license": "MIT", - "dependencies": { - "@types/node": "*" + "funding": { + "url": "https://opencollective.com/vitest" } }, - "node_modules/@types/serve-static": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-2.2.0.tgz", - "integrity": "sha512-8mam4H1NHLtu7nmtalF7eyBH14QyOASmcxHhSfEoRyr0nP/YdoesEtU+uSRvMe96TW/HPTtkoKqQLl53N7UXMQ==", + "node_modules/@vitest/utils": { + "version": "4.1.10", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-4.1.10.tgz", + "integrity": "sha512-fy9am/HWxbaGt/Sawrp90vt6Y6jQwf1RX77cz3uwoJwJVMli/e1IEwRPnMNJ7vKfPTwo0diXifkpPvwH9v7nGA==", "dev": true, "license": "MIT", "dependencies": { - "@types/http-errors": "*", - "@types/node": "*" + "@vitest/pretty-format": "4.1.10", + "convert-source-map": "^2.0.0", + "tinyrainbow": "^3.1.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" } }, - "node_modules/@types/uuid": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-10.0.0.tgz", - "integrity": "sha512-7gqG38EyHgyP1S+7+xomFtL+ZNHcKv6DwNaCZmJmo1vgMugyF3TCnXVg4t1uk89mLNwnLtnY3TpOpCOyp1/xHQ==", - "license": "MIT" - }, "node_modules/@webcomponents/custom-elements": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/@webcomponents/custom-elements/-/custom-elements-1.6.0.tgz", @@ -2055,9 +2086,9 @@ } }, "node_modules/acorn": { - "version": "8.16.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz", - "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==", + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.18.0.tgz", + "integrity": "sha512-lGq+9yr1/GuAWaVYIHRjvvySG5/4VfKIvC8EWxStPdcDh/Ka7FG3twP6v4d5BkravUilhIAsG4Qj83t02LWUPQ==", "dev": true, "license": "MIT", "bin": { @@ -2067,6 +2098,16 @@ "node": ">=0.4.0" } }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, "node_modules/acorn-walk": { "version": "8.3.5", "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.5.tgz", @@ -2080,6 +2121,23 @@ "node": ">=0.4.0" } }, + "node_modules/ajv": { + "version": "6.15.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.15.0.tgz", + "integrity": "sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "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", @@ -2143,12 +2201,16 @@ } }, "node_modules/ansi-styles": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "license": "MIT", + "peer": true, + "dependencies": { + "color-convert": "^2.0.1" + }, "engines": { - "node": ">=10" + "node": ">=8" }, "funding": { "url": "https://github.com/chalk/ansi-styles?sponsor=1" @@ -2168,6 +2230,19 @@ "node": ">= 8" } }, + "node_modules/anymatch/node_modules/picomatch": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", + "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, "node_modules/apache-arrow": { "version": "18.1.0", "resolved": "https://registry.npmjs.org/apache-arrow/-/apache-arrow-18.1.0.tgz", @@ -2190,9 +2265,9 @@ } }, "node_modules/apache-arrow/node_modules/@types/node": { - "version": "20.19.37", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.37.tgz", - "integrity": "sha512-8kzdPJ3FsNsVIurqBs7oodNnCEVbni9yUEkaHbgptDACOPW04jimGagZ51E6+lXUwJjgnBw+hyko/lkFWCldqw==", + "version": "20.19.43", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.43.tgz", + "integrity": "sha512-6oYBAi5ikg4Pl+kGsoYtawUMBT2zZMCvPNF7pVLnHZfd1zf38DRiWn/gT01RYCdUqkv7Fhr+C9ot4/tb+2sVvA==", "license": "MIT", "peer": true, "dependencies": { @@ -2226,6 +2301,45 @@ "node": ">=8" } }, + "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/ast-v8-to-istanbul": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/ast-v8-to-istanbul/-/ast-v8-to-istanbul-1.0.5.tgz", + "integrity": "sha512-UPAgKJFSEGMWSDr3LX4tqnAb4f7KGT8O40Tyx8wbYmmZ/yn58lNCm8h3svs3eXgiGd5AXxz8NDOvXWvicq+rJA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.31", + "estree-walker": "^3.0.3", + "js-tokens": "^10.0.0" + } + }, + "node_modules/ast-v8-to-istanbul/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/ast-v8-to-istanbul/node_modules/js-tokens": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-10.0.0.tgz", + "integrity": "sha512-lM/UBzQmfJRo9ABXbPWemivdCW8V2G8FHaHdypQaIy523snUjog0W71ayWXTjiR+ixeMyVHN2XcpnTd/liPg/Q==", + "dev": true, + "license": "MIT" + }, "node_modules/babel-plugin-transform-hook-names": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/babel-plugin-transform-hook-names/-/babel-plugin-transform-hook-names-1.0.2.tgz", @@ -2236,6 +2350,16 @@ "@babel/core": "^7.12.10" } }, + "node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "18 || 20 || >=22" + } + }, "node_modules/base64-js": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", @@ -2257,9 +2381,9 @@ "license": "MIT" }, "node_modules/baseline-browser-mapping": { - "version": "2.10.17", - "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.17.tgz", - "integrity": "sha512-HdrkN8eVG2CXxeifv/VdJ4A4RSra1DTW8dc/hdxzhGHN8QePs6gKaWM9pHPcpCoxYZJuOZ8drHmbdpLHjCYjLA==", + "version": "2.11.13", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.11.13.tgz", + "integrity": "sha512-k9HNuUVMlqVjQ9UHzfPjIqiDbWw7WqT1AoT7GL8VwvF3r0ZfArtgiSPAlmupyNquNgOJHTuH4CKYf8ttMTWBTQ==", "dev": true, "license": "Apache-2.0", "bin": { @@ -2283,20 +2407,20 @@ } }, "node_modules/body-parser": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.2.tgz", - "integrity": "sha512-oP5VkATKlNwcgvxi0vM0p/D3n2C3EReYVX+DNYs5TjZFn/oQt2j+4sVJtSMr18pdRr8wjTcBl6LoV+FUwzPmNA==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.3.0.tgz", + "integrity": "sha512-2cGmJupaNgg+QUwVLAucDuWuoMZ6EX9iHDRswZ5lsNYEmwPaRknMPCLZz07yTzVq/83p4o/wzbDZbBrTvGGTIw==", "license": "MIT", "dependencies": { "bytes": "^3.1.2", - "content-type": "^1.0.5", + "content-type": "^2.0.0", "debug": "^4.4.3", - "http-errors": "^2.0.0", - "iconv-lite": "^0.7.0", + "http-errors": "^2.0.1", + "iconv-lite": "^0.7.2", "on-finished": "^2.4.1", - "qs": "^6.14.1", - "raw-body": "^3.0.1", - "type-is": "^2.0.1" + "qs": "^6.15.2", + "raw-body": "^3.0.2", + "type-is": "^2.1.0" }, "engines": { "node": ">=18" @@ -2306,6 +2430,19 @@ "url": "https://opencollective.com/express" } }, + "node_modules/body-parser/node_modules/content-type": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-2.0.0.tgz", + "integrity": "sha512-j/O/d7GcZCyNl7/hwZAb606rzqkyvaDctLmckbxLzHvFBzTJHuGEdodATcP3yIRoDrLHkIATJuvzbFlp/ki2cQ==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, "node_modules/boolbase": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", @@ -2335,16 +2472,29 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/boxen/node_modules/camelcase": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-8.0.0.tgz", - "integrity": "sha512-8WB3Jcas3swSvjIeA2yvCJ+Miyz5l1ZmB6HFb9R1317dt9LCQoswg/BGrmAmkWVEszSrrg4RwmO46qIm2OEnSA==", + "node_modules/boxen/node_modules/chalk": { + "version": "5.6.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz", + "integrity": "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==", "license": "MIT", "engines": { - "node": ">=16" + "node": "^12.17.0 || ^14.13 || >=16.0.0" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/brace-expansion": { + "version": "5.0.9", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.9.tgz", + "integrity": "sha512-ScQ4IuvIEF1TMlP7Zt+vjJ//9zlPb2SDcxWxM3bk8s6t6GGdJ7KO1dCcTidOPJKePW30LE/2cT7wCyPho9/Wxg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "20 || >=22" } }, "node_modules/braces": { @@ -2361,9 +2511,9 @@ } }, "node_modules/browserslist": { - "version": "4.28.2", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.2.tgz", - "integrity": "sha512-48xSriZYYg+8qXna9kwqjIVzuQxi+KYWp2+5nCYnYKPTr0LvD89Jqk2Or5ogxz0NUMfIjhh2lIUX/LyX9B4oIg==", + "version": "4.28.8", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.8.tgz", + "integrity": "sha512-V2NpofLblG64mfOtSgDhOJESZEGogzDMBv/q+W6oc4LXWP/q75eOXoOaaOu1EOadB9U4Bwx/e0yzbvwKH8zalA==", "dev": true, "funding": [ { @@ -2381,11 +2531,11 @@ ], "license": "MIT", "dependencies": { - "baseline-browser-mapping": "^2.10.12", - "caniuse-lite": "^1.0.30001782", - "electron-to-chromium": "^1.5.328", - "node-releases": "^2.0.36", - "update-browserslist-db": "^1.2.3" + "baseline-browser-mapping": "^2.11.12", + "caniuse-lite": "^1.0.30001809", + "electron-to-chromium": "^1.5.402", + "node-releases": "^2.0.53", + "update-browserslist-db": "^1.3.0" }, "bin": { "browserslist": "cli.js" @@ -2394,6 +2544,19 @@ "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" } }, + "node_modules/buffer-image-size": { + "version": "0.6.4", + "resolved": "https://registry.npmjs.org/buffer-image-size/-/buffer-image-size-0.6.4.tgz", + "integrity": "sha512-nEh+kZOPY1w+gcCMobZ6ETUp9WfibndnosbpwB1iJk/8Gt5ZF2bhS6+B6bPYz424KtwsR6Rflc3tCz1/ghX2dQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + }, + "engines": { + "node": ">=4.0" + } + }, "node_modules/bytes": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", @@ -2433,21 +2596,21 @@ } }, "node_modules/camelcase": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", - "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-8.0.0.tgz", + "integrity": "sha512-8WB3Jcas3swSvjIeA2yvCJ+Miyz5l1ZmB6HFb9R1317dt9LCQoswg/BGrmAmkWVEszSrrg4RwmO46qIm2OEnSA==", "license": "MIT", "engines": { - "node": ">=10" + "node": ">=16" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/caniuse-lite": { - "version": "1.0.30001787", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001787.tgz", - "integrity": "sha512-mNcrMN9KeI68u7muanUpEejSLghOKlVhRqS/Za2IeyGllJ9I9otGpR9g3nsw7n4W378TE/LyIteA0+/FOZm4Kg==", + "version": "1.0.30001809", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001809.tgz", + "integrity": "sha512-xxWVywk6a6Arlk+hymeycyn/VgqEfLDxupvhH/xiY5SJ/18kmi9o6MiO320DCUzypORHLtvh0I4i04tUhCNHNQ==", "dev": true, "funding": [ { @@ -2465,51 +2628,17 @@ ], "license": "CC-BY-4.0" }, - "node_modules/chalk": { - "version": "5.6.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz", - "integrity": "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==", - "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==", - "license": "MIT", - "peer": true, - "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==", + "node_modules/chai": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/chai/-/chai-6.2.2.tgz", + "integrity": "sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==", + "dev": true, "license": "MIT", - "peer": true, - "dependencies": { - "color-convert": "^2.0.1" - }, "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "node": ">=18" } }, - "node_modules/chalk-template/node_modules/chalk": { + "node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", @@ -2526,135 +2655,83 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/chokidar": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", - "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", - "dev": true, + "node_modules/chalk-template": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/chalk-template/-/chalk-template-0.4.0.tgz", + "integrity": "sha512-/ghrgmhfY8RaSdeo43hNXxpoHAtxdbskUHjPpfqUWGttFgycUhYPGx3YZBCnUCvOa7Doivn1IZec3DEGFoMgLg==", "license": "MIT", + "peer": true, "dependencies": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "glob-parent": "~5.1.2", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.6.0" - }, - "engines": { - "node": ">= 8.10.0" - }, - "funding": { - "url": "https://paulmillr.com/funding/" + "chalk": "^4.1.2" }, - "optionalDependencies": { - "fsevents": "~2.3.2" - } - }, - "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==", - "license": "MIT", "engines": { - "node": ">=10" + "node": ">=12" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/cliui": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", - "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", - "dev": true, - "license": "ISC", - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^7.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/cliui/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" + "url": "https://github.com/chalk/chalk-template?sponsor=1" } }, - "node_modules/cliui/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, + "node_modules/chalk/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==", "license": "MIT", + "peer": true, "dependencies": { - "color-convert": "^2.0.1" + "has-flag": "^4.0.0" }, "engines": { "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/cliui/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/cliui/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==", + "node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", "dev": true, "license": "MIT", "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" }, "engines": { - "node": ">=8" + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" } }, - "node_modules/cliui/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==", + "node_modules/chokidar/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", "dev": true, - "license": "MIT", + "license": "ISC", "dependencies": { - "ansi-regex": "^5.0.1" + "is-glob": "^4.0.1" }, "engines": { - "node": ">=8" + "node": ">= 6" } }, - "node_modules/cliui/node_modules/wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, + "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==", "license": "MIT", - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, "engines": { "node": ">=10" }, "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/color-convert": { @@ -2662,6 +2739,7 @@ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "license": "MIT", + "peer": true, "dependencies": { "color-name": "~1.1.4" }, @@ -2673,7 +2751,8 @@ "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/command-line-args": { "version": "5.2.1", @@ -2738,115 +2817,103 @@ } }, "node_modules/concurrently": { - "version": "9.2.1", - "resolved": "https://registry.npmjs.org/concurrently/-/concurrently-9.2.1.tgz", - "integrity": "sha512-fsfrO0MxV64Znoy8/l1vVIjjHa29SZyyqPgQBwhiDcaW8wJc2W3XWVOGx4M3oJBnv/zdUZIIp1gDeS98GzP8Ng==", + "version": "10.0.4", + "resolved": "https://registry.npmjs.org/concurrently/-/concurrently-10.0.4.tgz", + "integrity": "sha512-trZql+7l/0+WRAsAnEdctr4+iiOS6ZrViI6H8QWcCF9MFS/LT0dKpe8vluB1to6it+OxSI4VospFTIFMW8DJRw==", "dev": true, "license": "MIT", "dependencies": { - "chalk": "4.1.2", + "chalk": "5.6.2", "rxjs": "7.8.2", - "shell-quote": "1.8.3", - "supports-color": "8.1.1", + "shell-quote": "1.9.0", + "supports-color": "10.2.2", "tree-kill": "1.2.2", - "yargs": "17.7.2" + "yargs": "18.0.0" }, "bin": { - "conc": "dist/bin/concurrently.js", - "concurrently": "dist/bin/concurrently.js" + "conc": "dist/bin/index.js", + "concurrently": "dist/bin/index.js" }, "engines": { - "node": ">=18" + "node": ">=22" }, "funding": { "url": "https://github.com/open-cli-tools/concurrently?sponsor=1" } }, - "node_modules/concurrently/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/concurrently/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "version": "5.6.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz", + "integrity": "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==", "dev": true, "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, "engines": { - "node": ">=10" + "node": "^12.17.0 || ^14.13 || >=16.0.0" }, "funding": { "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/concurrently/node_modules/chalk/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==", + "node_modules/concurrently/node_modules/cliui": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-9.0.1.tgz", + "integrity": "sha512-k7ndgKhwoQveBL+/1tqGJYNz097I7WOvwbmmU2AR5+magtbjPWQTS1C5vzGkBC8Ym8UWRzfKUzUUqFLypY4Q+w==", "dev": true, - "license": "MIT", + "license": "ISC", "dependencies": { - "has-flag": "^4.0.0" + "string-width": "^7.2.0", + "strip-ansi": "^7.1.0", + "wrap-ansi": "^9.0.0" }, "engines": { - "node": ">=8" - } - }, - "node_modules/concurrently/node_modules/rxjs": { - "version": "7.8.2", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.2.tgz", - "integrity": "sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.1.0" + "node": ">=20" } }, "node_modules/concurrently/node_modules/supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-10.2.2.tgz", + "integrity": "sha512-SS+jx45GF1QjgEXQx4NJZV9ImqmO2NPz5FNsIHrsDjh2YsHnawpan7SNQ1o8NuhrbHZy9AZhIoCUiCeaW/C80g==", "dev": true, "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, "engines": { - "node": ">=10" + "node": ">=18" }, "funding": { "url": "https://github.com/chalk/supports-color?sponsor=1" } }, - "node_modules/console-table-printer": { - "version": "2.15.0", - "resolved": "https://registry.npmjs.org/console-table-printer/-/console-table-printer-2.15.0.tgz", - "integrity": "sha512-SrhBq4hYVjLCkBVOWaTzceJalvn5K1Zq5aQA6wXC/cYjI3frKWNPEMK3sZsJfNNQApvCQmgBcc13ZKmFj8qExw==", + "node_modules/concurrently/node_modules/yargs": { + "version": "18.0.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-18.0.0.tgz", + "integrity": "sha512-4UEqdc2RYGHZc7Doyqkrqiln3p9X2DZVxaGbwhn2pi7MrRagKaOcIKe8L3OxYcbhXLgLFUS3zAYuQjKBQgmuNg==", + "dev": true, "license": "MIT", "dependencies": { - "simple-wcswidth": "^1.1.2" + "cliui": "^9.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "string-width": "^7.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^22.0.0" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=23" + } + }, + "node_modules/concurrently/node_modules/yargs-parser": { + "version": "22.0.0", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-22.0.0.tgz", + "integrity": "sha512-rwu/ClNdSMpkSrUb+d6BRsSkLUq1fmfsY6TOpYzTwvwkg1/NRG85KBy3kq++A8LKQwX6lsu+aWad+2khvuXrqw==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=23" } }, "node_modules/content-disposition": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.0.1.tgz", - "integrity": "sha512-oIXISMynqSqm241k6kcQ5UwttDILMK4BiurCfGEREw6+X9jkkpEe5T9FZaApyLGGOnFuyMWZpdolTXMtvEJ08Q==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.1.0.tgz", + "integrity": "sha512-5jRCH9Z/+DRP7rkvY83B+yGIGX96OYdJmzngqnw2SBSxqCFPd0w2km3s5iawpGX8krnwSGmF0FW5Nhr0Hfai3g==", "license": "MIT", "engines": { "node": ">=18" @@ -2866,9 +2933,9 @@ } }, "node_modules/convert-source-map": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", - "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", "dev": true, "license": "MIT" }, @@ -2907,6 +2974,21 @@ "url": "https://opencollective.com/express" } }, + "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/css-select": { "version": "5.2.2", "resolved": "https://registry.npmjs.org/css-select/-/css-select-5.2.2.tgz", @@ -2924,12 +3006,6 @@ "url": "https://github.com/sponsors/fb55" } }, - "node_modules/css-selector-generator": { - "version": "3.9.2", - "resolved": "https://registry.npmjs.org/css-selector-generator/-/css-selector-generator-3.9.2.tgz", - "integrity": "sha512-VvPJovWN5NC6PdilCV/eq7ezPx8zfVcaGcOjKVQqVYvZ1ooicqB6PkklM9UfxjjkmO9fwQr80idYUoJEowrOPg==", - "license": "MIT" - }, "node_modules/css-what": { "version": "6.2.2", "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.2.2.tgz", @@ -2960,14 +3036,12 @@ } } }, - "node_modules/decamelize": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", - "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true, + "license": "MIT" }, "node_modules/depd": { "version": "2.0.0", @@ -3016,6 +3090,19 @@ "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" } }, + "node_modules/dom-serializer/node_modules/entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, "node_modules/domelementtype": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", @@ -3061,9 +3148,9 @@ } }, "node_modules/dotenv": { - "version": "17.3.1", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-17.3.1.tgz", - "integrity": "sha512-IO8C/dzEb6O3F9/twg6ZLXz164a2fhTnEWb95H23Dm4OuN+92NmEAlTrupP9VW6Jm3sO26tQlqyvyi4CsnY9GA==", + "version": "17.4.2", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-17.4.2.tgz", + "integrity": "sha512-nI4U3TottKAcAD9LLud4Cb7b2QztQMUEfHbvhTH09bqXTxnSie8WnjPALV/WMCrJZ6UV/qHJ6L03OqO3LcdYZw==", "license": "BSD-2-Clause", "engines": { "node": ">=12" @@ -3093,9 +3180,9 @@ "license": "MIT" }, "node_modules/electron-to-chromium": { - "version": "1.5.334", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.334.tgz", - "integrity": "sha512-mgjZAz7Jyx1SRCwEpy9wefDS7GvNPazLthHg8eQMJ76wBdGQQDW33TCrUTvQ4wzpmOrv2zrFoD3oNufMdyMpog==", + "version": "1.5.404", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.404.tgz", + "integrity": "sha512-3WJtd7/lVq2Jnuz6wed1l9+1ZD2u2Tet1/1NBc4Iedkmgbu+I7YuAqdAQ8T+VZtnwysMsAf3IqSq9D1gyZjA2g==", "dev": true, "license": "ISC" }, @@ -3115,9 +3202,9 @@ } }, "node_modules/entities": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", - "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-7.0.1.tgz", + "integrity": "sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA==", "dev": true, "license": "BSD-2-Clause", "engines": { @@ -3133,97 +3220,211 @@ "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", "license": "MIT", "engines": { - "node": ">= 0.4" + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-module-lexer": { + "version": "0.10.5", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-0.10.5.tgz", + "integrity": "sha512-+7IwY/kiGAacQfY+YBhKMvEmyAJnw5grTUgjG85Pe7vcUI/6b7pZjZG8nQ7+48YhzEAEqrEgD2dCz/JIK+AYvw==", + "dev": true, + "license": "MIT" + }, + "node_modules/es-object-atoms": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.2.tgz", + "integrity": "sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", + "license": "MIT" + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint": { + "version": "10.8.1", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-10.8.1.tgz", + "integrity": "sha512-wqA7W2jbsC/BnV9Iv1UZpKVFkO1AdNoSmYW8NWG4HNOBbkAMvIqDZ27pI2f07dqn583NcIC44ckjAcOXDL1QbQ==", + "dev": true, + "license": "MIT", + "workspaces": [ + "packages/*" + ], + "dependencies": { + "@eslint-community/eslint-utils": "^4.8.0", + "@eslint-community/regexpp": "^4.12.2", + "@eslint/config-array": "^0.23.5", + "@eslint/config-helpers": "^0.7.0", + "@eslint/core": "^1.2.1", + "@eslint/plugin-kit": "^0.7.2", + "@humanfs/node": "^0.16.6", + "@humanwhocodes/module-importer": "^1.0.1", + "@humanwhocodes/retry": "^0.4.2", + "@types/estree": "^1.0.6", + "ajv": "^6.14.0", + "cross-spawn": "^7.0.6", + "debug": "^4.3.2", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^9.1.2", + "eslint-visitor-keys": "^5.0.1", + "espree": "^11.2.0", + "esquery": "^1.7.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^8.0.0", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "minimatch": "^10.2.5", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "jiti": "*" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + } + } + }, + "node_modules/eslint-scope": { + "version": "9.1.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-9.1.2.tgz", + "integrity": "sha512-xS90H51cKw0jltxmvmHy2Iai1LIqrfbw57b79w/J7MfvDfkIkFZ+kj6zC3BjtUwh150HsSSdxXZcsuv72miDFQ==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "@types/esrecurse": "^4.3.1", + "@types/estree": "^1.0.8", + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz", + "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://opencollective.com/eslint" } }, - "node_modules/es-errors": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", - "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", - "license": "MIT", + "node_modules/espree": { + "version": "11.2.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-11.2.0.tgz", + "integrity": "sha512-7p3DrVEIopW1B1avAGLuCSh1jubc01H2JHc8B4qqGblmg5gI9yumBgACjWo4JlIc04ufug4xJ3SQI8HkS/Rgzw==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "acorn": "^8.16.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^5.0.1" + }, "engines": { - "node": ">= 0.4" + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://opencollective.com/eslint" } }, - "node_modules/es-module-lexer": { - "version": "0.10.5", - "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-0.10.5.tgz", - "integrity": "sha512-+7IwY/kiGAacQfY+YBhKMvEmyAJnw5grTUgjG85Pe7vcUI/6b7pZjZG8nQ7+48YhzEAEqrEgD2dCz/JIK+AYvw==", + "node_modules/esquery": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.7.0.tgz", + "integrity": "sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==", "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==", - "license": "MIT", + "license": "BSD-3-Clause", "dependencies": { - "es-errors": "^1.3.0" + "estraverse": "^5.1.0" }, "engines": { - "node": ">= 0.4" + "node": ">=0.10" } }, - "node_modules/esbuild": { - "version": "0.27.4", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.4.tgz", - "integrity": "sha512-Rq4vbHnYkK5fws5NF7MYTU68FPRE1ajX7heQ/8QXXWqNgqqJ/GkmmyxIzUnf2Sr/bakf8l54716CcMGHYhMrrQ==", + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", "dev": true, - "hasInstallScript": true, - "license": "MIT", - "optional": true, - "peer": true, - "bin": { - "esbuild": "bin/esbuild" + "license": "BSD-2-Clause", + "dependencies": { + "estraverse": "^5.2.0" }, "engines": { - "node": ">=18" - }, - "optionalDependencies": { - "@esbuild/aix-ppc64": "0.27.4", - "@esbuild/android-arm": "0.27.4", - "@esbuild/android-arm64": "0.27.4", - "@esbuild/android-x64": "0.27.4", - "@esbuild/darwin-arm64": "0.27.4", - "@esbuild/darwin-x64": "0.27.4", - "@esbuild/freebsd-arm64": "0.27.4", - "@esbuild/freebsd-x64": "0.27.4", - "@esbuild/linux-arm": "0.27.4", - "@esbuild/linux-arm64": "0.27.4", - "@esbuild/linux-ia32": "0.27.4", - "@esbuild/linux-loong64": "0.27.4", - "@esbuild/linux-mips64el": "0.27.4", - "@esbuild/linux-ppc64": "0.27.4", - "@esbuild/linux-riscv64": "0.27.4", - "@esbuild/linux-s390x": "0.27.4", - "@esbuild/linux-x64": "0.27.4", - "@esbuild/netbsd-arm64": "0.27.4", - "@esbuild/netbsd-x64": "0.27.4", - "@esbuild/openbsd-arm64": "0.27.4", - "@esbuild/openbsd-x64": "0.27.4", - "@esbuild/openharmony-arm64": "0.27.4", - "@esbuild/sunos-x64": "0.27.4", - "@esbuild/win32-arm64": "0.27.4", - "@esbuild/win32-ia32": "0.27.4", - "@esbuild/win32-x64": "0.27.4" + "node": ">=4.0" } }, - "node_modules/escalade": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", - "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", "dev": true, - "license": "MIT", + "license": "BSD-2-Clause", "engines": { - "node": ">=6" + "node": ">=4.0" } }, - "node_modules/escape-html": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", - "license": "MIT" - }, "node_modules/estree-walker": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", @@ -3231,6 +3432,16 @@ "dev": true, "license": "MIT" }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/etag": { "version": "1.8.1", "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", @@ -3246,6 +3457,16 @@ "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==", "license": "MIT" }, + "node_modules/expect-type": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.4.0.tgz", + "integrity": "sha512-KfYbmpRm0VbLjEvVa9yGwCi9GI34xvi7A/HXYWQO65CSD2u3MczUJSuwXKFIxlGsgBQizV9q5J9NHj4VG0n+pA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.0.0" + } + }, "node_modules/express": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/express/-/express-5.2.1.tgz", @@ -3289,6 +3510,13 @@ "url": "https://opencollective.com/express" } }, + "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/fast-glob": { "version": "3.3.3", "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", @@ -3306,6 +3534,33 @@ "node": ">=8.6.0" } }, + "node_modules/fast-glob/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true, + "license": "MIT" + }, "node_modules/fastq": { "version": "1.20.1", "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.20.1.tgz", @@ -3316,6 +3571,37 @@ "reusify": "^1.0.4" } }, + "node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/file-entry-cache": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", + "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "flat-cache": "^4.0.0" + }, + "engines": { + "node": ">=16.0.0" + } + }, "node_modules/fill-range": { "version": "7.1.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", @@ -3363,6 +3649,37 @@ "node": ">=4.0.0" } }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat-cache": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", + "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.4" + }, + "engines": { + "node": ">=16" + } + }, "node_modules/flatbuffers": { "version": "24.12.23", "resolved": "https://registry.npmjs.org/flatbuffers/-/flatbuffers-24.12.23.tgz", @@ -3370,6 +3687,13 @@ "license": "Apache-2.0", "peer": true }, + "node_modules/flatted": { + "version": "3.4.4", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.4.tgz", + "integrity": "sha512-5+ybhBZANEJxaH3X5evAFatUxLfEHSr7n6kYJ+1Qd0mUqr4eu9gIf6GDbWHf8RJijHrjjO8G+la14SlL2SeS1Q==", + "dev": true, + "license": "ISC" + }, "node_modules/forwarded": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", @@ -3389,9 +3713,9 @@ } }, "node_modules/fs-extra": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", - "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", + "version": "11.4.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.4.0.tgz", + "integrity": "sha512-EQsFzMUJkCKGr1ePqlYADkIUmHW1s3ZXr5Yqy6wbGrfUCphpl2maM/kyOIRA2HpP3AaFQTZXD4ldjek+nccddA==", "dev": true, "license": "MIT", "dependencies": { @@ -3400,7 +3724,7 @@ "universalify": "^2.0.0" }, "engines": { - "node": ">=12" + "node": ">=14.14" } }, "node_modules/fsevents": { @@ -3448,9 +3772,9 @@ } }, "node_modules/get-east-asian-width": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.5.0.tgz", - "integrity": "sha512-CQ+bEO+Tva/qlmw24dCejulK5pMzVnUOFOijVogd3KQs07HnRIgp8TGipvCCRT06xeYEbpbgwaCxglFyiuIcmA==", + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.6.0.tgz", + "integrity": "sha512-QRbvDIbx6YklUe6RxeTeleMR0yv3cYH6PsPZHcnVn7xv7zO1BHN8r0XETu8n6Ye3Q+ahtSarc3WgtNWmehIBfA==", "license": "MIT", "engines": { "node": ">=18" @@ -3497,9 +3821,9 @@ } }, "node_modules/get-tsconfig": { - "version": "4.14.0", - "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.14.0.tgz", - "integrity": "sha512-yTb+8DXzDREzgvYmh6s9vHsSVCHeC0G3PI5bEXNBHtmshPnO+S5O7qgLEOn0I5QvMy6kpZN8K1NKGyilLb93wA==", + "version": "4.14.1", + "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.14.1.tgz", + "integrity": "sha512-Dz/6HxkrxgNehhxLVeyv8sad9UzF2xBVeaKBQNDfJ5XiSXmp2gTR0eO0RWiT2NCKS5aGP9jjkOMggTN90qU50A==", "dev": true, "license": "MIT", "dependencies": { @@ -3510,16 +3834,29 @@ } }, "node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", "dev": true, "license": "ISC", "dependencies": { - "is-glob": "^4.0.1" + "is-glob": "^4.0.3" }, "engines": { - "node": ">= 6" + "node": ">=10.13.0" + } + }, + "node_modules/globals": { + "version": "17.9.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-17.9.0.tgz", + "integrity": "sha512-m/MvAW61QVU5VDNF1Vj8axt016h8w7L5TU1e9zlab7XIttAT2YAlCwl75K1fOqvMM9apmD7lbCIRhpfkhmxhCg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/globby": { @@ -3562,6 +3899,25 @@ "dev": true, "license": "ISC" }, + "node_modules/happy-dom": { + "version": "20.11.2", + "resolved": "https://registry.npmjs.org/happy-dom/-/happy-dom-20.11.2.tgz", + "integrity": "sha512-7MB+bJLkxu3SowAfBJbjW+c55kNz5tkR45gu2qzrxznezhLeN5YIlJbwUgSzlGc+qWoZ8Ykg71H5ezz69xixrw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": ">=20.0.0", + "@types/whatwg-mimetype": "^3.0.2", + "@types/ws": "^8.18.1", + "buffer-image-size": "^0.6.4", + "entities": "^7.0.1", + "whatwg-mimetype": "^3.0.0", + "ws": "^8.21.0" + }, + "engines": { + "node": ">=20.0.0" + } + }, "node_modules/has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", @@ -3584,9 +3940,9 @@ } }, "node_modules/hasown": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", - "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.4.tgz", + "integrity": "sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==", "license": "MIT", "dependencies": { "function-bind": "^1.1.2" @@ -3605,6 +3961,13 @@ "he": "bin/he" } }, + "node_modules/html-escaper": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", + "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", + "dev": true, + "license": "MIT" + }, "node_modules/http-errors": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz", @@ -3626,9 +3989,9 @@ } }, "node_modules/iconv-lite": { - "version": "0.7.2", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.2.tgz", - "integrity": "sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw==", + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.3.tgz", + "integrity": "sha512-IKXpvIzjnC9XTAUbVBcMfGS0EPaIXtW6v+zr+RRp+hqULEpo0owZax6wyRwPOJbWbzjYspQwusTsfVr0ifh4uQ==", "license": "MIT", "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" @@ -3651,6 +4014,16 @@ "node": ">= 4" } }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, "node_modules/inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", @@ -3712,9 +4085,9 @@ } }, "node_modules/is-network-error": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/is-network-error/-/is-network-error-1.3.1.tgz", - "integrity": "sha512-6QCxa49rQbmUWLfk0nuGqzql9U8uaV2H6279bRErPBHe/109hCzsLUBUHfbEtvLIHBd6hyXbgedBSHevm43Edw==", + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/is-network-error/-/is-network-error-1.3.2.tgz", + "integrity": "sha512-PhBY86zaxNZUuWP6h13Vu5oFe0XY6/UlKzQnYFELzGVHygP3MxmvTfYSG7GN3aIab/iWudSMgjSnG9Dq+nHrgA==", "license": "MIT", "engines": { "node": ">=16" @@ -3739,6 +4112,65 @@ "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==", "license": "MIT" }, + "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/istanbul-lib-coverage": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", + "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-report": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", + "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^4.0.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-lib-report/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/istanbul-reports": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.2.0.tgz", + "integrity": "sha512-HGYWWS/ehqTV3xN10i23tkPkpH46MLCIMFNCaaKNavAXTF1RkqxawEPtnjnGZ6XKSInBKkiOA5BKS+aZiY3AvA==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/js-tiktoken": { "version": "1.0.21", "resolved": "https://registry.npmjs.org/js-tiktoken/-/js-tiktoken-1.0.21.tgz", @@ -3777,6 +4209,27 @@ "node": ">=0.8" } }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true, + "license": "MIT" + }, "node_modules/json5": { "version": "2.2.3", "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", @@ -3791,9 +4244,9 @@ } }, "node_modules/jsonfile": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.0.tgz", - "integrity": "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==", + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.1.tgz", + "integrity": "sha512-zwOTdL3rFQ/lRdBnntKVOX6k5cKJwEc1HdilT71BWEu7J41gXIB2MRp+vxduPSwZJPWBxEzv4yH1wYLJGUHX4Q==", "dev": true, "license": "MIT", "dependencies": { @@ -3803,6 +4256,16 @@ "graceful-fs": "^4.1.6" } }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "json-buffer": "3.0.1" + } + }, "node_modules/kolorist": { "version": "1.8.0", "resolved": "https://registry.npmjs.org/kolorist/-/kolorist-1.8.0.tgz", @@ -3811,36 +4274,30 @@ "license": "MIT" }, "node_modules/langchain": { - "version": "1.2.39", - "resolved": "https://registry.npmjs.org/langchain/-/langchain-1.2.39.tgz", - "integrity": "sha512-3foM0VptB5NFnd3drLTVieZhNBcITS/9lnaKzfGgJ7a7YL0eEAM0ZHofMOi86V2KbpeSaHrJ08vOOMSc+CpfEQ==", + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/langchain/-/langchain-1.5.5.tgz", + "integrity": "sha512-KhedvQDRvaLIFMSvEy45U/pqR4G0dEfN7wPyHTBMgMIUXbcrVkaqRVlDSMPfMCwNuiRCfWFUHueYGtgSrwBcBA==", "license": "MIT", "dependencies": { - "@langchain/langgraph": "^1.2.6", - "@langchain/langgraph-checkpoint": "^1.0.1", + "@langchain/langgraph": "^1.4.8", + "@langchain/langgraph-checkpoint": "^1.1.3", "langsmith": ">=0.5.0 <1.0.0", - "uuid": "^11.1.0", "zod": "^3.25.76 || ^4" }, "engines": { "node": ">=20" }, "peerDependencies": { - "@langchain/core": "^1.1.38" + "@langchain/core": "^1.2.5" } }, "node_modules/langsmith": { - "version": "0.5.15", - "resolved": "https://registry.npmjs.org/langsmith/-/langsmith-0.5.15.tgz", - "integrity": "sha512-S20JnYmIgqGBjA/WEn12ZZJjqd03O5wd8K9KgGBvsKXQBn0bYuFrr1w20L37PpcMmX3/cftpgJ6g2y8KoEmHLw==", + "version": "0.8.9", + "resolved": "https://registry.npmjs.org/langsmith/-/langsmith-0.8.9.tgz", + "integrity": "sha512-6ikTB5SO+3SlBQ/+oH8K/JzhglxB8AM4RAe5bHzgWSEIHTgTe+b71bSjf1fDvlYuEXoiPn6uP10BbczrbznUnA==", "license": "MIT", "dependencies": { - "@types/uuid": "^10.0.0", - "chalk": "^5.6.2", - "console-table-printer": "^2.12.1", - "p-queue": "^6.6.2", - "semver": "^7.6.3", - "uuid": "^10.0.0" + "p-queue": "6.6.2" }, "peerDependencies": { "@opentelemetry/api": "*", @@ -3867,23 +4324,24 @@ } } }, - "node_modules/langsmith/node_modules/uuid": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-10.0.0.tgz", - "integrity": "sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==", - "funding": [ - "https://github.com/sponsors/broofa", - "https://github.com/sponsors/ctavan" - ], + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, "license": "MIT", - "bin": { - "uuid": "dist/bin/uuid" + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" } }, "node_modules/lightningcss": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.32.0.tgz", - "integrity": "sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==", + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.33.0.tgz", + "integrity": "sha512-WkUDrojuJs0xkgGf2udWxa3yGBRxPtxUkB79i6aCZLRgc7PM8fZe9TosfPDcvEpQZbuFASnHYmRLBLUbmLOIIA==", "dev": true, "license": "MPL-2.0", "dependencies": { @@ -3897,23 +4355,23 @@ "url": "https://opencollective.com/parcel" }, "optionalDependencies": { - "lightningcss-android-arm64": "1.32.0", - "lightningcss-darwin-arm64": "1.32.0", - "lightningcss-darwin-x64": "1.32.0", - "lightningcss-freebsd-x64": "1.32.0", - "lightningcss-linux-arm-gnueabihf": "1.32.0", - "lightningcss-linux-arm64-gnu": "1.32.0", - "lightningcss-linux-arm64-musl": "1.32.0", - "lightningcss-linux-x64-gnu": "1.32.0", - "lightningcss-linux-x64-musl": "1.32.0", - "lightningcss-win32-arm64-msvc": "1.32.0", - "lightningcss-win32-x64-msvc": "1.32.0" + "lightningcss-android-arm64": "1.33.0", + "lightningcss-darwin-arm64": "1.33.0", + "lightningcss-darwin-x64": "1.33.0", + "lightningcss-freebsd-x64": "1.33.0", + "lightningcss-linux-arm-gnueabihf": "1.33.0", + "lightningcss-linux-arm64-gnu": "1.33.0", + "lightningcss-linux-arm64-musl": "1.33.0", + "lightningcss-linux-x64-gnu": "1.33.0", + "lightningcss-linux-x64-musl": "1.33.0", + "lightningcss-win32-arm64-msvc": "1.33.0", + "lightningcss-win32-x64-msvc": "1.33.0" } }, "node_modules/lightningcss-android-arm64": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.32.0.tgz", - "integrity": "sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==", + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.33.0.tgz", + "integrity": "sha512-gEpRTalKdosp4Bb8qWtc2iOgE5SeIHlpS1up9bFq2wAyYhl1UdTObYiHe98zEM9SQvSoqQZ1IQD0JNpg3Ml5pg==", "cpu": [ "arm64" ], @@ -3932,9 +4390,9 @@ } }, "node_modules/lightningcss-darwin-arm64": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.32.0.tgz", - "integrity": "sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==", + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.33.0.tgz", + "integrity": "sha512-Sciaz8eenNTKn9b3t7+xr0ipTp9YxKQY4npwQ3mrRuL0BAVHBLyZxofhaKBAVtzmtRZ/zTyo0/to4B1uWG/Djg==", "cpu": [ "arm64" ], @@ -3953,9 +4411,9 @@ } }, "node_modules/lightningcss-darwin-x64": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.32.0.tgz", - "integrity": "sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==", + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.33.0.tgz", + "integrity": "sha512-Z5UPAxzrjlWNNyGy6i65cJzzvgJ5D3T6wMvs+gWpY9d7qRhANrxqAp6LhxIgZhWEw18RfJTGcRxjuLIBr+m8XQ==", "cpu": [ "x64" ], @@ -3974,9 +4432,9 @@ } }, "node_modules/lightningcss-freebsd-x64": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.32.0.tgz", - "integrity": "sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==", + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.33.0.tgz", + "integrity": "sha512-QQM/Ti/hQajJwCY+RiWuCZ9sdtI/XQk7nDK5vC8kkdwixezOlDgvDx7+RT+QjK6FcFT4MpsuoBnHIo/O3StRRg==", "cpu": [ "x64" ], @@ -3995,9 +4453,9 @@ } }, "node_modules/lightningcss-linux-arm-gnueabihf": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.32.0.tgz", - "integrity": "sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==", + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.33.0.tgz", + "integrity": "sha512-N7FVBe6iS24MlM6R/4RBTxGhQheZGs7tiQ9U32UtF75NzP5Q7xWPRqLBCKxlRQRk3rY1jCIPLzx7WzOhuUIRLQ==", "cpu": [ "arm" ], @@ -4016,9 +4474,9 @@ } }, "node_modules/lightningcss-linux-arm64-gnu": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.32.0.tgz", - "integrity": "sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==", + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.33.0.tgz", + "integrity": "sha512-j2v/itmy4HlNxlc6voKXYgBqNi0Ng2LShg4z7GufpEgs05P+2suBVyi9I6YHq5uoVFx9ETin3eCEhLVyXGQnKg==", "cpu": [ "arm64" ], @@ -4037,9 +4495,9 @@ } }, "node_modules/lightningcss-linux-arm64-musl": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.32.0.tgz", - "integrity": "sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==", + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.33.0.tgz", + "integrity": "sha512-yiO5ROMuYQgXbC60yjZU5CYSFZGKXL0HFATXt9mHJn1+zW55oCtMI9NfcVhYLMFDL7gV7oBPon/EmMMGg2OvtQ==", "cpu": [ "arm64" ], @@ -4058,9 +4516,9 @@ } }, "node_modules/lightningcss-linux-x64-gnu": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.32.0.tgz", - "integrity": "sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==", + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.33.0.tgz", + "integrity": "sha512-ar+Ju7LmcN0Jo4FpL4hpFybwNG9/3A/Br5KW2n2jyODg3MEZXaDYADdemoNS+BDNfMgKvylJLj4S5tyRActuAg==", "cpu": [ "x64" ], @@ -4079,9 +4537,9 @@ } }, "node_modules/lightningcss-linux-x64-musl": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.32.0.tgz", - "integrity": "sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==", + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.33.0.tgz", + "integrity": "sha512-RYiYbkokw0trfKqqzfF55lginwEPrD3OJDfTuJzFs1MK6iFnDenaz1fqLLtX4ITG3OktJQXOeTaw1awrBAlZPw==", "cpu": [ "x64" ], @@ -4100,9 +4558,9 @@ } }, "node_modules/lightningcss-win32-arm64-msvc": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.32.0.tgz", - "integrity": "sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==", + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.33.0.tgz", + "integrity": "sha512-1K+MPfLSFVpphzpdbfkhlWk6wBrTObBzS2T6db10PNOZgR9GoVsAWzwNyuhUYYbTp23j+4RrncfujZ4uAzXvwA==", "cpu": [ "arm64" ], @@ -4121,9 +4579,9 @@ } }, "node_modules/lightningcss-win32-x64-msvc": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.32.0.tgz", - "integrity": "sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==", + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.33.0.tgz", + "integrity": "sha512-OlEICDx/Xl0FqSp4bry8zFnCvGpig3Gl4gCquvYwHuqJKEC1+n9NgDniFvqHGmMv1ZkqDJrDqKKSykTDX+ehuA==", "cpu": [ "x64" ], @@ -4134,11 +4592,27 @@ "win32" ], "engines": { - "node": ">= 12.0.0" + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/lodash.camelcase": { @@ -4158,6 +4632,15 @@ "yallist": "^3.0.2" } }, + "node_modules/lucide-preact": { + "version": "1.31.0", + "resolved": "https://registry.npmjs.org/lucide-preact/-/lucide-preact-1.31.0.tgz", + "integrity": "sha512-Of9TS1ZFAnWpDZZ/iQl4ydyNHU8peDEtqgQFDOhVAU3SdYosUvP/BJeP0lFLZ4QWx5dxABhToeD03Btoa7G/ug==", + "license": "ISC", + "peerDependencies": { + "preact": "^10.27.2" + } + }, "node_modules/magic-string": { "version": "0.30.21", "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", @@ -4168,10 +4651,51 @@ "@jridgewell/sourcemap-codec": "^1.5.5" } }, + "node_modules/magicast": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/magicast/-/magicast-0.5.4.tgz", + "integrity": "sha512-llBEhWm1SacoRwgHUoQJYtwp4PBLF4faQi5TCpIGyGs9n4y5+juI0tDgyKIfpqxckRHaHzouUEph3THklWh03w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.29.7", + "@babel/types": "^7.29.7", + "source-map-js": "^1.2.1" + } + }, + "node_modules/make-dir": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", + "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", + "dev": true, + "license": "MIT", + "dependencies": { + "semver": "^7.5.3" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/make-dir/node_modules/semver": { + "version": "7.8.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz", + "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/marked": { - "version": "18.0.2", - "resolved": "https://registry.npmjs.org/marked/-/marked-18.0.2.tgz", - "integrity": "sha512-NsmlUYBS/Zg57rgDWMYdnre6OTj4e+qq/JS2ot3KrYLSoHLw+sDu0Nm1ZGpRgYAq6c+b1ekaY5NzVchMCQnzcg==", + "version": "18.0.9", + "resolved": "https://registry.npmjs.org/marked/-/marked-18.0.9.tgz", + "integrity": "sha512-/Sa4qiiHZxf0/FQdBBowr9q4r10krCwMvpK48FUBdXdUXScDxiQGR9zCPrFgRVR5LU3iySOiIjy09ZQvADir1w==", "dev": true, "license": "MIT", "bin": { @@ -4191,12 +4715,16 @@ } }, "node_modules/media-typer": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz", - "integrity": "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.1.tgz", + "integrity": "sha512-yz3xRaG20c6/BOzvYoDaGtPmGscs7YivItZEEqe6GbwNfHuxu9YNmvnEkMzKldAGY4/80pRcQRZSEnhquk9XuQ==", "license": "MIT", "engines": { "node": ">= 0.8" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/merge-descriptors": { @@ -4235,6 +4763,19 @@ "node": ">=8.6" } }, + "node_modules/micromatch/node_modules/picomatch": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", + "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, "node_modules/mime-db": { "version": "1.54.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", @@ -4260,6 +4801,22 @@ "url": "https://opencollective.com/express" } }, + "node_modules/minimatch": { + "version": "10.2.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.6.tgz", + "integrity": "sha512-vpLQEs+VLCr1nU0BXS07maYoFwlDAH0gngQuuttxIwutDFEMHq2blX+8vpgxDdK3J1PwjCJiep77OitTZ4Ll1A==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "brace-expansion": "^5.0.8" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", @@ -4290,9 +4847,9 @@ } }, "node_modules/nanoid": { - "version": "3.3.11", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", - "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "version": "3.3.18", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.18.tgz", + "integrity": "sha512-DTg4MJbGMWkfi6VZFdNt2/caMbQy4Ou+Op/hJQvGEWcnVfoA1QA+xzRKAzw9jD6+GVOOeYr/mIcuDSdug6F6+w==", "dev": true, "funding": [ { @@ -4308,6 +4865,13 @@ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" } }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true, + "license": "MIT" + }, "node_modules/negotiator": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz", @@ -4329,11 +4893,14 @@ } }, "node_modules/node-releases": { - "version": "2.0.37", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.37.tgz", - "integrity": "sha512-1h5gKZCF+pO/o3Iqt5Jp7wc9rH3eJJ0+nh/CIoiRwjRxde/hAHyLPXYN4V3CqKAbiZPSeJFSWHmJsbkicta0Eg==", + "version": "2.0.53", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.53.tgz", + "integrity": "sha512-D9UOmYG3UH1V+ENW56t5QXBwJw1YEY18ruVeus89Rw+SyIgjPkCO84bRzO3uNIYosJbNwiabWVn48o3uJLjxFQ==", "dev": true, - "license": "MIT" + "license": "MIT", + "engines": { + "node": ">=18" + } }, "node_modules/normalize-path": { "version": "3.0.0", @@ -4379,6 +4946,20 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/obug": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/obug/-/obug-2.1.4.tgz", + "integrity": "sha512-4a+OsYv9UktOJKE+l1A4OufDgdRF9PifWj+tJnHURo/P+WOxpG4GzUFL9qCalmWauao6ogiG+QvnCovwPoyAWA==", + "dev": true, + "funding": [ + "https://github.com/sponsors/sxzz", + "https://opencollective.com/debug" + ], + "license": "MIT", + "engines": { + "node": ">=12.20.0" + } + }, "node_modules/ollama": { "version": "0.6.3", "resolved": "https://registry.npmjs.org/ollama/-/ollama-0.6.3.tgz", @@ -4410,18 +4991,27 @@ } }, "node_modules/openai": { - "version": "6.33.0", - "resolved": "https://registry.npmjs.org/openai/-/openai-6.33.0.tgz", - "integrity": "sha512-xAYN1W3YsDXJWA5F277135YfkEk6H7D3D6vWwRhJ3OEkzRgcyK8z/P5P9Gyi/wB4N8kK9kM5ZjprfvyHagKmpw==", + "version": "6.49.0", + "resolved": "https://registry.npmjs.org/openai/-/openai-6.49.0.tgz", + "integrity": "sha512-aYCc0C6L864eR6WSYIwQGyXriw/nIyZx0ObvhzOEVuk0zoBDpynjSbrionWI7q65B5H8jJX0DXR9snEzM6bfPg==", "license": "Apache-2.0", - "bin": { - "openai": "bin/cli" - }, "peerDependencies": { + "@aws-sdk/credential-provider-node": ">=3.972.0 <4", + "@smithy/hash-node": ">=4.3.0 <5", + "@smithy/signature-v4": ">=5.4.0 <6", "ws": "^8.18.0", "zod": "^3.25 || ^4.0" }, "peerDependenciesMeta": { + "@aws-sdk/credential-provider-node": { + "optional": true + }, + "@smithy/hash-node": { + "optional": true + }, + "@smithy/signature-v4": { + "optional": true + }, "ws": { "optional": true }, @@ -4430,6 +5020,24 @@ } } }, + "node_modules/optionator": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.5" + }, + "engines": { + "node": ">= 0.8.0" + } + }, "node_modules/p-finally": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", @@ -4439,6 +5047,38 @@ "node": ">=4" } }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/p-queue": { "version": "6.6.2", "resolved": "https://registry.npmjs.org/p-queue/-/p-queue-6.6.2.tgz", @@ -4491,10 +5131,30 @@ "node": ">= 0.8" } }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "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": "8.4.1", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.4.1.tgz", - "integrity": "sha512-fvU78fIjZ+SBM9YwCknCvKOUKkLVqtWDVctl0s7xIqfmfb38t2TT4ZU2gHm+Z8xGwgW+QWEU3oQSAzIbo89Ggw==", + "version": "8.4.2", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.4.2.tgz", + "integrity": "sha512-qRcuIdP69NPm4qbACK+aDogI5CBDMi1jKe0ry5rSQJz8JVLsC7jV8XpiJjGRLLol3N+R5ihGYcrPLTno6pAdBA==", "license": "MIT", "funding": { "type": "opencollective", @@ -4526,13 +5186,13 @@ "license": "ISC" }, "node_modules/picomatch": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", - "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.5.tgz", + "integrity": "sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A==", "dev": true, "license": "MIT", "engines": { - "node": ">=8.6" + "node": ">=12" }, "funding": { "url": "https://github.com/sponsors/jonschlinkert" @@ -4552,9 +5212,9 @@ } }, "node_modules/postcss": { - "version": "8.5.9", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.9.tgz", - "integrity": "sha512-7a70Nsot+EMX9fFU3064K/kdHWZqGVY+BADLyXc8Dfv+mTLLVl6JzJpPaCZ2kQL9gIJvKXSLMHhqdRRjwQeFtw==", + "version": "8.5.26", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.26.tgz", + "integrity": "sha512-u82N74LFzG8ca+dD8puPnplTXoGH4fTPpVGuIbt36G3qvNlkvfD0lEAZSxaly3KX8TS/L1A1gsCEmvKmBcVbkQ==", "dev": true, "funding": [ { @@ -4572,7 +5232,7 @@ ], "license": "MIT", "dependencies": { - "nanoid": "^3.3.11", + "nanoid": "^3.3.17", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" }, @@ -4581,20 +5241,37 @@ } }, "node_modules/preact": { - "version": "10.29.1", - "resolved": "https://registry.npmjs.org/preact/-/preact-10.29.1.tgz", - "integrity": "sha512-gQCLc/vWroE8lIpleXtdJhTFDogTdZG9AjMUpVkDf2iTCNwYNWA+u16dL41TqUDJO4gm2IgrcMv3uTpjd4Pwmg==", - "dev": true, + "version": "10.29.8", + "resolved": "https://registry.npmjs.org/preact/-/preact-10.29.8.tgz", + "integrity": "sha512-ej2aVZ+vZ8WO7tvlQWRM9N63A0KzF9q4mWJfDUHgYaIofWY9hu74QdnQrjoPMmZi2/nZ5gN0bJCQF49xQqx09Q==", "license": "MIT", "funding": { "type": "opencollective", "url": "https://opencollective.com/preact" + }, + "peerDependencies": { + "preact-render-to-string": ">=5" + }, + "peerDependenciesMeta": { + "preact-render-to-string": { + "optional": true + } + } + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8.0" } }, "node_modules/prettier": { - "version": "3.8.1", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.8.1.tgz", - "integrity": "sha512-UOnG6LftzbdaHZcKoPFtOcCKztrQ57WkHDeRD9t/PTQtmT0NHSeWWepj6pS0z/N7+08BHFDQVUrfmfMRcZwbMg==", + "version": "3.9.6", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.9.6.tgz", + "integrity": "sha512-OpN0zzVdiaiAhxpuuj5efpIS4sY9j7bY6uR5mnj5yPzGkdkjNKSJeUThPb60Jw29QuAZgA4o+/iB49kFiaBX6g==", "dev": true, "license": "MIT", "bin": { @@ -4620,13 +5297,24 @@ "node": ">= 0.10" } }, + "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/qs": { - "version": "6.15.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.15.0.tgz", - "integrity": "sha512-mAZTtNCeetKMH+pSjrb76NAM8V9a05I9aBZOHztWy/UqcJdQYNsf59vrRKWnojAT9Y+GbIvoTBC++CPHqpDBhQ==", + "version": "6.15.3", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.15.3.tgz", + "integrity": "sha512-O9gl3zCl5h5blw1KGUzQKhA5oUXSl8rwUIM5o0S3nCXMliSvy5Dzx7/DJcI+SwgICv+IneSZwhBh1oSyEHA71A==", "license": "BSD-3-Clause", "dependencies": { - "side-channel": "^1.1.0" + "es-define-property": "^1.0.1", + "side-channel": "^1.1.1" }, "engines": { "node": ">=0.6" @@ -4667,12 +5355,16 @@ "license": "MIT" }, "node_modules/range-parser": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", - "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.3.0.tgz", + "integrity": "sha512-hek2mFQpPuI4E1BBKrSto+BU3e3x4xuarsbiwr3+lf7p44juvFMV0XFWQAP3xUyqXA4RrXLIoaSUGbSt056ZMw==", "license": "MIT", "engines": { "node": ">= 0.6" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/raw-body": { @@ -4690,16 +5382,6 @@ "node": ">= 0.10" } }, - "node_modules/react-refresh": { - "version": "0.13.0", - "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.13.0.tgz", - "integrity": "sha512-XP8A9BT0CpRBD+NYLLeIhld/RqG9+gktUjW1FkE+Vm7OCinbG1SshcK5tb9ls4kzvjZr9mOQc7HYgBngEyPAXg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/readdirp": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", @@ -4713,22 +5395,25 @@ "node": ">=8.10.0" } }, + "node_modules/readdirp/node_modules/picomatch": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", + "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, "node_modules/reflect-metadata": { "version": "0.2.2", "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.2.2.tgz", "integrity": "sha512-urBwgfrvVP/eAyXx4hluJivBKzuEbSQs9rKWCrCkbSxNv8mxPcUZKeuoF3Uy4mJl3Lwprp6yy5/39VWigZ4K6Q==", "license": "Apache-2.0" }, - "node_modules/require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/resolve-pkg-maps": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", @@ -4751,14 +5436,14 @@ } }, "node_modules/rolldown": { - "version": "1.0.0-rc.15", - "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.0.0-rc.15.tgz", - "integrity": "sha512-Ff31guA5zT6WjnGp0SXw76X6hzGRk/OQq2hE+1lcDe+lJdHSgnSX6nK3erbONHyCbpSj9a9E+uX/OvytZoWp2g==", + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.2.3.tgz", + "integrity": "sha512-rn9wpmxplLf7NLNyCk9FyWh3FM43DbY8jOzCdEPzH7uflhTftRbCEpqi6Ly2osgoU8OwObtmavMbWLaWy4LX7A==", "dev": true, "license": "MIT", "dependencies": { - "@oxc-project/types": "=0.124.0", - "@rolldown/pluginutils": "1.0.0-rc.15" + "@oxc-project/types": "=0.143.0", + "@rolldown/pluginutils": "^1.0.0" }, "bin": { "rolldown": "bin/cli.mjs" @@ -4767,27 +5452,26 @@ "node": "^20.19.0 || >=22.12.0" }, "optionalDependencies": { - "@rolldown/binding-android-arm64": "1.0.0-rc.15", - "@rolldown/binding-darwin-arm64": "1.0.0-rc.15", - "@rolldown/binding-darwin-x64": "1.0.0-rc.15", - "@rolldown/binding-freebsd-x64": "1.0.0-rc.15", - "@rolldown/binding-linux-arm-gnueabihf": "1.0.0-rc.15", - "@rolldown/binding-linux-arm64-gnu": "1.0.0-rc.15", - "@rolldown/binding-linux-arm64-musl": "1.0.0-rc.15", - "@rolldown/binding-linux-ppc64-gnu": "1.0.0-rc.15", - "@rolldown/binding-linux-s390x-gnu": "1.0.0-rc.15", - "@rolldown/binding-linux-x64-gnu": "1.0.0-rc.15", - "@rolldown/binding-linux-x64-musl": "1.0.0-rc.15", - "@rolldown/binding-openharmony-arm64": "1.0.0-rc.15", - "@rolldown/binding-wasm32-wasi": "1.0.0-rc.15", - "@rolldown/binding-win32-arm64-msvc": "1.0.0-rc.15", - "@rolldown/binding-win32-x64-msvc": "1.0.0-rc.15" + "@rolldown/binding-android-arm64": "1.2.3", + "@rolldown/binding-darwin-arm64": "1.2.3", + "@rolldown/binding-darwin-x64": "1.2.3", + "@rolldown/binding-freebsd-x64": "1.2.3", + "@rolldown/binding-linux-arm-gnueabihf": "1.2.3", + "@rolldown/binding-linux-arm64-gnu": "1.2.3", + "@rolldown/binding-linux-arm64-musl": "1.2.3", + "@rolldown/binding-linux-ppc64-gnu": "1.2.3", + "@rolldown/binding-linux-s390x-gnu": "1.2.3", + "@rolldown/binding-linux-x64-gnu": "1.2.3", + "@rolldown/binding-linux-x64-musl": "1.2.3", + "@rolldown/binding-openharmony-arm64": "1.2.3", + "@rolldown/binding-win32-arm64-msvc": "1.2.3", + "@rolldown/binding-win32-x64-msvc": "1.2.3" } }, "node_modules/rollup": { - "version": "2.79.2", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.79.2.tgz", - "integrity": "sha512-fS6iqSPZDs3dr/y7Od6y5nha8dW1YnbgtsyotCVvoFGKbERG++CVRFv1meyGDE1SNItQA8BrnCw7ScdAhRJ3XQ==", + "version": "2.80.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.80.0.tgz", + "integrity": "sha512-cIFJOD1DESzpjOBl763Kp1AH7UE/0fcdHe6rZXUdQ9c50uvgigvW97u3IcSeBwOkgqL/PXPBktBCh0KEu5L8XQ==", "dev": true, "license": "MIT", "bin": { @@ -4841,9 +5525,9 @@ } }, "node_modules/rxjs": { - "version": "7.5.7", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.5.7.tgz", - "integrity": "sha512-z9MzKh/UcOqB3i20H6rtrlaE/CgjLOvheWK/9ILrbhROGTweAi1BaFsTT9FbwZi5Trr1qNRs+MXkhmR06awzQA==", + "version": "7.8.2", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.2.tgz", + "integrity": "sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -4857,15 +5541,13 @@ "license": "MIT" }, "node_modules/semver": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", - "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, "license": "ISC", "bin": { "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" } }, "node_modules/send": { @@ -4919,10 +5601,33 @@ "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", "license": "ISC" }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "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/shell-quote": { - "version": "1.8.3", - "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.3.tgz", - "integrity": "sha512-ObmnIF4hXNg1BqhnHmgbDETF8dLPCggZWBjkQfhZpbszZnYur5DUljTcCHii5LC3J5E0yeO/1LIMyH+UvHQgyw==", + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.9.0.tgz", + "integrity": "sha512-Iov+JwFv/2HcTpcwNMKd8+IWNb8tboQJNQTkAY/LLVK7gGH9jy+LGkVqPxfekHl+yMmiqXszdGWXgkfml7hjqA==", "dev": true, "license": "MIT", "engines": { @@ -4933,14 +5638,14 @@ } }, "node_modules/side-channel": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", - "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.1.tgz", + "integrity": "sha512-6x6dK6zJdpTzF4sQeNYxwtvBzf6Eg4GtlesS94HOvTudUeyK2WXAaIfmDgsyslYrRBeFIlsi54AYsFGUuhmvrQ==", "license": "MIT", "dependencies": { "es-errors": "^1.3.0", - "object-inspect": "^1.13.3", - "side-channel-list": "^1.0.0", + "object-inspect": "^1.13.4", + "side-channel-list": "^1.0.1", "side-channel-map": "^1.0.1", "side-channel-weakmap": "^1.0.2" }, @@ -4952,13 +5657,13 @@ } }, "node_modules/side-channel-list": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", - "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.1.tgz", + "integrity": "sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w==", "license": "MIT", "dependencies": { "es-errors": "^1.3.0", - "object-inspect": "^1.13.3" + "object-inspect": "^1.13.4" }, "engines": { "node": ">= 0.4" @@ -5004,6 +5709,13 @@ "url": "https://github.com/sponsors/ljharb" } }, + "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/simple-code-frame": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/simple-code-frame/-/simple-code-frame-1.3.0.tgz", @@ -5014,12 +5726,6 @@ "kolorist": "^1.6.0" } }, - "node_modules/simple-wcswidth": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/simple-wcswidth/-/simple-wcswidth-1.1.2.tgz", - "integrity": "sha512-j7piyCjAeTDSjzTSQ7DokZtMNwNlEAyxqSZeCS+CXH7fJ4jx3FuJ/mTW3mE+6JLs4VJBbcll0Kjn+KXI5t21Iw==", - "license": "MIT" - }, "node_modules/slash": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", @@ -5051,15 +5757,22 @@ } }, "node_modules/stack-trace": { - "version": "1.0.0-pre2", - "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-1.0.0-pre2.tgz", - "integrity": "sha512-2ztBJRek8IVofG9DBJqdy2N5kulaacX30Nz7xmkYF6ale9WBVmIy6mFBchvGX7Vx/MyjBhx+Rcxqrj+dbOnQ6A==", + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-1.0.0.tgz", + "integrity": "sha512-H6D7134xi6qONvh7ZHKgviXf+rd3vhGBSvebPZCaUkd8zvQ+7PtDw6CljPTe4cXWNf2IKZGNqw6VJXSb9IgBpA==", "dev": true, "license": "MIT", "engines": { - "node": ">=16" + "node": ">=20.0.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/statuses": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", @@ -5069,6 +5782,13 @@ "node": ">= 0.8" } }, + "node_modules/std-env": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/std-env/-/std-env-4.2.0.tgz", + "integrity": "sha512-oCUKSupKTHX53EyjDtuZQ64pjLJ6yYCtpmEw0goYxtjG9KpbRe8KAsl2tBUGU9DyMcJ0RwJ8GqJAFzMXcXW1Rw==", + "dev": true, + "license": "MIT" + }, "node_modules/string-width": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", @@ -5092,26 +5812,13 @@ "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==", "license": "MIT", "dependencies": { - "ansi-regex": "^6.2.2" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" - } - }, - "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==", - "license": "MIT", - "peer": true, - "dependencies": { - "has-flag": "^4.0.0" + "ansi-regex": "^6.2.2" }, "engines": { - "node": ">=8" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" } }, "node_modules/table-layout": { @@ -5138,10 +5845,27 @@ "node": ">=12.17" } }, + "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": "1.3.0", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.3.0.tgz", + "integrity": "sha512-QKAl9m8gWWGHV8jZcPeym6j+XULi6tOf1mT83WYJ4Lk2ytW/uwAWkrP0uFsdoYMdueVJ0qs26wZ+23xeB4ibNQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, "node_modules/tinyglobby": { - "version": "0.2.16", - "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.16.tgz", - "integrity": "sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg==", + "version": "0.2.17", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz", + "integrity": "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==", "dev": true, "license": "MIT", "dependencies": { @@ -5155,35 +5879,14 @@ "url": "https://github.com/sponsors/SuperchupuDev" } }, - "node_modules/tinyglobby/node_modules/fdir": { - "version": "6.5.0", - "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", - "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12.0.0" - }, - "peerDependencies": { - "picomatch": "^3 || ^4" - }, - "peerDependenciesMeta": { - "picomatch": { - "optional": true - } - } - }, - "node_modules/tinyglobby/node_modules/picomatch": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", - "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", + "node_modules/tinyrainbow": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-3.1.1.tgz", + "integrity": "sha512-yau8yJdTt989Mm0Bd/236QnzEiPf2xLLTqUZRUJOo/3CB078LSwzei343DgtJVmfJKJE3TMINY1u42SQsP6mXw==", "dev": true, "license": "MIT", "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" + "node": ">=14.0.0" } }, "node_modules/to-regex-range": { @@ -5218,10 +5921,23 @@ "tree-kill": "cli.js" } }, + "node_modules/ts-api-utils": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.5.0.tgz", + "integrity": "sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.12" + }, + "peerDependencies": { + "typescript": ">=4.8.4" + } + }, "node_modules/tsc-alias": { - "version": "1.8.17", - "resolved": "https://registry.npmjs.org/tsc-alias/-/tsc-alias-1.8.17.tgz", - "integrity": "sha512-EIduCZHqbNwPm8BZYfq1aD7BQ697A4h6uSGMOFQfYGoQwfrYFTKwYfy9Bv42YxHkduVBcn9Zx0DkX111DKskyg==", + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/tsc-alias/-/tsc-alias-1.9.1.tgz", + "integrity": "sha512-sFZdVFthH8uvdplPJrOYGOHcxu6UPtcAcY678JPwEQiMzgLZYFO7Qc/rzELp7ingTc+OxtzH6n+8Pn2eVQep6w==", "dev": true, "license": "MIT", "dependencies": { @@ -5246,6 +5962,19 @@ "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", "license": "0BSD" }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, "node_modules/type-fest": { "version": "4.41.0", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.41.0.tgz", @@ -5259,23 +5988,40 @@ } }, "node_modules/type-is": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.0.1.tgz", - "integrity": "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.1.0.tgz", + "integrity": "sha512-faYHw0anBbc/kWF3zFTEnxSFOAGUX9GFbOBthvDdLsIlEoWOFOtS0zgCiQYwIskL9iGXZL3kAXD8OoZ4GmMATA==", "license": "MIT", "dependencies": { - "content-type": "^1.0.5", + "content-type": "^2.0.0", "media-typer": "^1.1.0", "mime-types": "^3.0.0" }, "engines": { - "node": ">= 0.6" + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/type-is/node_modules/content-type": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-2.0.0.tgz", + "integrity": "sha512-j/O/d7GcZCyNl7/hwZAb606rzqkyvaDctLmckbxLzHvFBzTJHuGEdodATcP3yIRoDrLHkIATJuvzbFlp/ki2cQ==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/typescript": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-6.0.2.tgz", - "integrity": "sha512-bGdAIrZ0wiGDo5l8c++HWtbaNCWTS4UTv7RaTH/ThVIgjkveJt83m74bBHMJkuCbslY8ixgLBVZJIOiQlQTjfQ==", + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-6.0.3.tgz", + "integrity": "sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==", "dev": true, "license": "Apache-2.0", "bin": { @@ -5286,6 +6032,30 @@ "node": ">=14.17" } }, + "node_modules/typescript-eslint": { + "version": "8.67.0", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.67.0.tgz", + "integrity": "sha512-S2udFs8tCKEKffuJ4TB1idGUZiXdCPGi3IPBGWXarbLQ5UPXORV8QEVzJ4gCRduURMb5EkpNCdjbk0eDIuI8Yg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/eslint-plugin": "8.67.0", + "@typescript-eslint/parser": "8.67.0", + "@typescript-eslint/typescript-estree": "8.67.0", + "@typescript-eslint/utils": "8.67.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, "node_modules/typical": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/typical/-/typical-4.0.0.tgz", @@ -5297,9 +6067,9 @@ } }, "node_modules/undici-types": { - "version": "7.18.2", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.18.2.tgz", - "integrity": "sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.24.6.tgz", + "integrity": "sha512-WRNW+sJgj5OBN4/0JpHFqtqzhpbnV0GuB+OozA9gCL7a993SmU+1JBZCzLNxYsbMfIeDL+lTsphD5jN5N+n0zg==", "dev": true, "license": "MIT" }, @@ -5323,9 +6093,9 @@ } }, "node_modules/update-browserslist-db": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz", - "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==", + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.3.1.tgz", + "integrity": "sha512-ZZ61DsRsOnakl74HAmp3oSN4aXUmEWXf+i/yv0h7tIBfICc3VdrFErQKUUKPgu3AMsTUMbcongALEN4l6GSUrQ==", "dev": true, "funding": [ { @@ -5353,17 +6123,14 @@ "browserslist": ">= 4.21.0" } }, - "node_modules/uuid": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-11.1.0.tgz", - "integrity": "sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A==", - "funding": [ - "https://github.com/sponsors/broofa", - "https://github.com/sponsors/ctavan" - ], - "license": "MIT", - "bin": { - "uuid": "dist/esm/bin/uuid" + "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": { @@ -5376,17 +6143,17 @@ } }, "node_modules/vite": { - "version": "8.0.8", - "resolved": "https://registry.npmjs.org/vite/-/vite-8.0.8.tgz", - "integrity": "sha512-dbU7/iLVa8KZALJyLOBOQ88nOXtNG8vxKuOT4I2mD+Ya70KPceF4IAmDsmU0h1Qsn5bPrvsY9HJstCRh3hG6Uw==", + "version": "8.2.1", + "resolved": "https://registry.npmjs.org/vite/-/vite-8.2.1.tgz", + "integrity": "sha512-EU/eS7BH3XROHh2YnBefjM6DBKA6ZeMZEYQbj7NLWg5wHYlhB8B/Mayd5XsgWq+NFYccDOTemRpdETWR6Ka/lw==", "dev": true, "license": "MIT", "dependencies": { - "lightningcss": "^1.32.0", - "picomatch": "^4.0.4", - "postcss": "^8.5.8", - "rolldown": "1.0.0-rc.15", - "tinyglobby": "^0.2.15" + "lightningcss": "^1.33.0", + "picomatch": "^4.0.5", + "postcss": "^8.5.25", + "rolldown": "~1.2.1", + "tinyglobby": "^0.2.17" }, "bin": { "vite": "bin/vite.js" @@ -5402,7 +6169,7 @@ }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", - "@vitejs/devtools": "^0.1.0", + "@vitejs/devtools": "^0.4.0", "esbuild": "^0.27.0 || ^0.28.0", "jiti": ">=1.21.0", "less": "^4.0.0", @@ -5482,25 +6249,152 @@ "he": "1.2.0" } }, - "node_modules/vite/node_modules/picomatch": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", - "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", + "node_modules/vitest": { + "version": "4.1.10", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-4.1.10.tgz", + "integrity": "sha512-R9jUTe5S4Qb0HCd4TNqpC7oGcrMssMRGXLW80ubjWsW9VH5GF8y1Y0SFLY9AbqSk6nt0PnOx4H4WNJYZ13GUPw==", "dev": true, "license": "MIT", + "dependencies": { + "@vitest/expect": "4.1.10", + "@vitest/mocker": "4.1.10", + "@vitest/pretty-format": "4.1.10", + "@vitest/runner": "4.1.10", + "@vitest/snapshot": "4.1.10", + "@vitest/spy": "4.1.10", + "@vitest/utils": "4.1.10", + "es-module-lexer": "^2.0.0", + "expect-type": "^1.3.0", + "magic-string": "^0.30.21", + "obug": "^2.1.1", + "pathe": "^2.0.3", + "picomatch": "^4.0.3", + "std-env": "^4.0.0-rc.1", + "tinybench": "^2.9.0", + "tinyexec": "^1.0.2", + "tinyglobby": "^0.2.15", + "tinyrainbow": "^3.1.0", + "vite": "^6.0.0 || ^7.0.0 || ^8.0.0", + "why-is-node-running": "^2.3.0" + }, + "bin": { + "vitest": "vitest.mjs" + }, "engines": { - "node": ">=12" + "node": "^20.0.0 || ^22.0.0 || >=24.0.0" }, "funding": { - "url": "https://github.com/sponsors/jonschlinkert" + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "@edge-runtime/vm": "*", + "@opentelemetry/api": "^1.9.0", + "@types/node": "^20.0.0 || ^22.0.0 || >=24.0.0", + "@vitest/browser-playwright": "4.1.10", + "@vitest/browser-preview": "4.1.10", + "@vitest/browser-webdriverio": "4.1.10", + "@vitest/coverage-istanbul": "4.1.10", + "@vitest/coverage-v8": "4.1.10", + "@vitest/ui": "4.1.10", + "happy-dom": "*", + "jsdom": "*", + "vite": "^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "@edge-runtime/vm": { + "optional": true + }, + "@opentelemetry/api": { + "optional": true + }, + "@types/node": { + "optional": true + }, + "@vitest/browser-playwright": { + "optional": true + }, + "@vitest/browser-preview": { + "optional": true + }, + "@vitest/browser-webdriverio": { + "optional": true + }, + "@vitest/coverage-istanbul": { + "optional": true + }, + "@vitest/coverage-v8": { + "optional": true + }, + "@vitest/ui": { + "optional": true + }, + "happy-dom": { + "optional": true + }, + "jsdom": { + "optional": true + }, + "vite": { + "optional": false + } } }, + "node_modules/vitest/node_modules/es-module-lexer": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-2.3.1.tgz", + "integrity": "sha512-shc1dbU90Yl/xq1QrC7QRtfcwURZuVRfPhZbDoldJ1cn1gzDvBaBWlv0eFolj5+0znnPJz5TXLxsN77X/12KTA==", + "dev": true, + "license": "MIT" + }, "node_modules/whatwg-fetch": { "version": "3.6.20", "resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-3.6.20.tgz", "integrity": "sha512-EqhiFU6daOA8kpjOWTL0olhVOF3i7OrFzSYiGsEMB8GcXS+RrzauAERX65xMeNWVqxA6HXH2m69Z9LaKKdisfg==", "license": "MIT" }, + "node_modules/whatwg-mimetype": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-3.0.0.tgz", + "integrity": "sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + } + }, + "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": "5.0.0", "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-5.0.0.tgz", @@ -5516,6 +6410,16 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/wordwrapjs": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/wordwrapjs/-/wordwrapjs-5.1.1.tgz", @@ -5561,6 +6465,28 @@ "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", "license": "ISC" }, + "node_modules/ws": { + "version": "8.21.3", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.21.3.tgz", + "integrity": "sha512-201TZ/kPWxoPr/OKWjquZR1SWKXcvxdH+e1xrx89b3YbmzLMFCLfnaG1HFIgWzJOEWZ7MvpK++odZufgYR50Rw==", + "devOptional": 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/y18n": { "version": "5.0.8", "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", @@ -5578,78 +6504,17 @@ "dev": true, "license": "ISC" }, - "node_modules/yargs": { - "version": "17.7.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", - "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", - "dev": true, - "license": "MIT", - "dependencies": { - "cliui": "^8.0.1", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.3", - "y18n": "^5.0.5", - "yargs-parser": "^21.1.1" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/yargs-parser": { - "version": "21.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", - "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=12" - } - }, - "node_modules/yargs/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/yargs/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/yargs/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==", + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", "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/yargs/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" + "node": ">=10" }, - "engines": { - "node": ">=8" + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/zimmerframe": { @@ -5660,9 +6525,9 @@ "license": "MIT" }, "node_modules/zod": { - "version": "4.3.6", - "resolved": "https://registry.npmjs.org/zod/-/zod-4.3.6.tgz", - "integrity": "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg==", + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/zod/-/zod-4.4.3.tgz", + "integrity": "sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ==", "license": "MIT", "funding": { "url": "https://github.com/sponsors/colinhacks" @@ -5672,33 +6537,31 @@ "name": "@flowforge/contract", "version": "0.1.0", "dependencies": { - "@flowforge/page-model": "*", + "@flowforge/page-trail": "*", "zod": "^4.3.6" }, "devDependencies": { - "typescript": "^6.0.2" - } - }, - "packages/page-model": { - "name": "@flowforge/page-model", - "version": "0.1.0", - "dependencies": { - "css-selector-generator": "^3.9.2" - }, - "devDependencies": { - "tsc-alias": "^1.8.17", - "typescript": "^6.0.2" + "@eslint/js": "^10.0.1", + "@tsconfig/node22": "^22.0.5", + "eslint": "^10.8.1", + "globals": "^17.9.0", + "typescript": "^6.0.2", + "typescript-eslint": "^8.66.0" } }, - "packages/shared": { - "name": "@flowforge/shared", + "packages/page-trail": { + "name": "@flowforge/page-trail", "version": "0.1.0", - "extraneous": true, - "dependencies": { - "zod": "^4.3.6" - }, "devDependencies": { - "typescript": "^6.0.2" + "@eslint/js": "^10.0.1", + "@tsconfig/node22": "^22.0.5", + "@vitest/coverage-v8": "^4.1.10", + "eslint": "^10.8.1", + "globals": "^17.9.0", + "happy-dom": "^20.11.2", + "typescript": "^6.0.2", + "typescript-eslint": "^8.66.0", + "vitest": "^4.1.10" } } } diff --git a/package.json b/package.json index 50596cc..6c03aed 100644 --- a/package.json +++ b/package.json @@ -6,11 +6,11 @@ "packages/*" ], "scripts": { - "clean": "npm run clean --workspaces", - "setup": "npm run setup --workspace @flowforge/backend", - "build:packages": "npm run build --workspace @flowforge/page-model && npm run build --workspace @flowforge/contract", - "build": "npm run build:packages && npm run build --workspace @flowforge/backend && npm run build:chrome --workspace @flowforge/extension", - "start": "npm run setup && npm run build && npm run install:chrome --workspace @flowforge/extension && npm run start --workspace @flowforge/backend", + "clean": "npm run clean -ws", + "setup": "npm run setup -w @flowforge/backend", + "build:packages": "npm run build -w @flowforge/page-trail && npm run build -w @flowforge/contract", + "build": "npm run build:packages && npm run build -w @flowforge/backend && npm run build:chrome -w @flowforge/extension", + "start": "npm run setup && npm run build && npm run install:chrome -w @flowforge/extension && npm run start -w @flowforge/backend", "format": "prettier . --write --cache", "format:check": "prettier . --check" }, diff --git a/packages/contract/eslint.config.mjs b/packages/contract/eslint.config.mjs new file mode 100644 index 0000000..ac9a37f --- /dev/null +++ b/packages/contract/eslint.config.mjs @@ -0,0 +1,22 @@ +import js from '@eslint/js'; +import globals from 'globals'; +import tseslint from 'typescript-eslint'; + +export default tseslint.config( + { + ignores: ['dist/**'], + }, + js.configs.recommended, + ...tseslint.configs.recommended, + { + files: ['src/**/*.ts'], + languageOptions: { + globals: { + ...globals.es2022, + }, + }, + rules: { + 'no-undef': 'off', + }, + }, +); diff --git a/packages/contract/package.json b/packages/contract/package.json index b319fba..d50f227 100644 --- a/packages/contract/package.json +++ b/packages/contract/package.json @@ -11,14 +11,22 @@ }, "scripts": { "clean": "rm -rf dist/", + "typecheck": "tsc -p tsconfig.json --noEmit", "build": "npm run clean && tsc -p tsconfig.json", + "lint": "eslint .", + "lint:fix": "eslint . --fix", "dev": "tsc -p tsconfig.json --watch" }, "dependencies": { - "@flowforge/page-model": "*", + "@flowforge/page-trail": "*", "zod": "^4.3.6" }, "devDependencies": { - "typescript": "^6.0.2" + "@eslint/js": "^10.0.1", + "@tsconfig/node22": "^22.0.5", + "eslint": "^10.8.1", + "globals": "^17.9.0", + "typescript": "^6.0.2", + "typescript-eslint": "^8.66.0" } } diff --git a/packages/contract/src/core/dataId.ts b/packages/contract/src/core/dataId.ts index c573c6c..29be4fb 100644 --- a/packages/contract/src/core/dataId.ts +++ b/packages/contract/src/core/dataId.ts @@ -11,4 +11,3 @@ export function generateDataId(): string { export function validateDataId(dataId: string): boolean { return /^ff[0-9a-f]{8}$/i.test(dataId); } - diff --git a/packages/contract/src/index.ts b/packages/contract/src/index.ts index 5aba8d8..50a1d0f 100644 --- a/packages/contract/src/index.ts +++ b/packages/contract/src/index.ts @@ -1,6 +1,6 @@ export type * from './types/api.ts'; export * from './types/agentResult.ts'; -export type { PageModel } from '@flowforge/page-model'; +export type { PageTrail } from '@flowforge/page-trail'; -export { generateDataId, validateDataId } from './core/dataId.ts'; \ No newline at end of file +export { generateDataId, validateDataId } from './core/dataId.ts'; diff --git a/packages/contract/src/types/agentResult.ts b/packages/contract/src/types/agentResult.ts index 5855f05..26276b1 100644 --- a/packages/contract/src/types/agentResult.ts +++ b/packages/contract/src/types/agentResult.ts @@ -5,7 +5,7 @@ export const AgentResultElementActionSchema = z.enum(['click', 'navigate', 'inpu export const AgentResultElementSchema = z.object({ text: z.string(), dataId: z.string(), - selector: z.string(), + cssSelector: z.string(), action: AgentResultElementActionSchema, }); diff --git a/packages/contract/src/types/api.ts b/packages/contract/src/types/api.ts index f08e979..0156ef2 100644 --- a/packages/contract/src/types/api.ts +++ b/packages/contract/src/types/api.ts @@ -1,4 +1,4 @@ -import type { PageModel } from '@flowforge/page-model'; +import type { PageTrail } from '@flowforge/page-trail'; import type { AgentResult, UsageMetadata } from './agentResult.ts'; // POST: /query @@ -9,7 +9,7 @@ export interface UserContext { export interface QueryRequest { question: string; - pageModel: PageModel; + pageTrail: PageTrail; domain: string; userContext?: UserContext; } diff --git a/packages/contract/tsconfig.json b/packages/contract/tsconfig.json index 84a88b6..1bf7bb4 100644 --- a/packages/contract/tsconfig.json +++ b/packages/contract/tsconfig.json @@ -1,23 +1,22 @@ { + "extends": ["@tsconfig/node22/tsconfig.json"], "compilerOptions": { - "target": "esnext", - "module": "nodenext", - "moduleResolution": "nodenext", - "moduleDetection": "force", - "strict": true, + "lib": ["DOM", "ES2022"], + + "isolatedModules": true, + "noImplicitOverride": true, + "verbatimModuleSyntax": true, "noUncheckedIndexedAccess": true, + "allowImportingTsExtensions": true, "rewriteRelativeImportExtensions": true, - "noImplicitOverride": true, - "esModuleInterop": true, - "skipLibCheck": true, - "resolveJsonModule": true, + "declaration": true, "declarationMap": true, "sourceMap": true, - "isolatedModules": true, + "rootDir": "./src", "outDir": "./dist" }, "include": ["src/**/*"], - "exclude": ["node_modules", "dist", "**/*.test.ts"] + "exclude": ["**/*.test.ts"] } diff --git a/packages/page-model/README.md b/packages/page-model/README.md deleted file mode 100644 index 0b8f66a..0000000 --- a/packages/page-model/README.md +++ /dev/null @@ -1,123 +0,0 @@ -# PageModel - -Normalized representation of a web page extracted from the DOM. - -Used as the core input for RAG, search, and UI guidance. - -## Structure - -```ts -interface PageModel { - basics: PageBasics; - content: ContentElement[]; - interactive: InteractiveElement[]; - timestamp: number; -} -``` - -## Basics -- url, title, description, language -- viewport (width, height, scroll) - -## Content - -Extracted from text elements (headings, paragraphs, lists, etc). - -Each item: - -- text -- type: heading | text -- selector, dataId, bbox -- context (path + optional sectionName) -- importanceScore [0..1] - -Filtered: - -- hidden elements -- very short text - -## Interactive - -Extracted from buttons, links, inputs, ARIA roles. - -Each item: - -- role → type (button | input | select | link) -- text + labels -- state (disabled, checked, etc) -- link (if any) -- selector, dataId, bbox -- inViewport, aboveTheFold -- context -- importanceScore [0..1] - -Filtered: - -- hidden elements -- sensitive inputs (passwords, OTP, card data, etc) - -## Context - -Derived from ancestor containers: - -- main content, navigation, footer, dialog, etc - -Also resolves optional sectionName from: - -- aria-labelledby / aria-label / heading / legend - -## Importance - -Heuristic scoring normalized to [0..1]. - -Used to: - -- rank elements -- apply top-N limits -- improve retrieval quality - -## Usage - -```ts -const model = PageModelCollector.collectFor(window, document, { - getElementDataId: (el) => getOrCreateDataId(el), -}); -``` - -## Format - -PageModel includes helpers to convert elements into compact semantic strings. - -Used for indexing, retrieval, and tool responses. - -### Content - -```ts -formatContentElement(el) -``` - -→ heading. Pricing. in section "Plans". inside "main content" - -### Interactive - -```ts -formatInteractiveElement(el) -``` - -→ internal link. click action. name "Pricing". visible on initial screen. inside "navigation" - -### Short format - -```ts -formatContentElementShort(el) -formatInteractiveElementShort(el) -formatConcatElements(items) -``` -Used for previews and compact lists. - -## Notes - -- DOM → structured model (LLM-independent) -- Single-page snapshot -- Optimized for search and UI actions -- Safe by default (sensitive fields excluded) \ No newline at end of file diff --git a/packages/page-model/package.json b/packages/page-model/package.json deleted file mode 100644 index 8b336b4..0000000 --- a/packages/page-model/package.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "name": "@flowforge/page-model", - "version": "0.1.0", - "private": true, - "type": "module", - "exports": { - ".": { - "import": "./dist/index.js", - "types": "./dist/index.d.ts" - } - }, - "scripts": { - "clean": "rm -rf dist/", - "build": "npm run clean && tsc -p tsconfig.json && tsc-alias -p tsconfig.json", - "dev": "tsc -p tsconfig.json --watch" - }, - "dependencies": { - "css-selector-generator": "^3.9.2" - }, - "devDependencies": { - "tsc-alias": "^1.8.17", - "typescript": "^6.0.2" - } -} diff --git a/packages/page-model/src/collector/PageModelCollector.ts b/packages/page-model/src/collector/PageModelCollector.ts deleted file mode 100644 index 5c4b6ef..0000000 --- a/packages/page-model/src/collector/PageModelCollector.ts +++ /dev/null @@ -1,201 +0,0 @@ -import type { BaseElement, ContentElement, InteractiveElement, PageBasics, PageModel } from '#self/types'; - -import { getElementLabels } from './extractors/primitive/label.ts'; -import { getInteractiveRole, roleToInteractiveElementType } from './extractors/primitive/role.ts'; -import { isAboveTheFold, isElementVisible, isInViewport } from './extractors/primitive/view.ts'; -import { getInteractiveElementState } from './extractors/primitive/state.ts'; -import { getElementText } from './extractors/primitive/text.ts'; -import { getElementLink } from './extractors/primitive/link.ts'; -import { getElementContext } from './extractors/context.ts'; -import { extractElementDescriptor } from './extractors/descriptor.ts'; -import { isSensitiveElement } from './extractors/primitive/sensitive.ts'; -import { scoreContentElement, scoreInteractiveElement } from './importance/scoring.ts'; - -// constants -const CONTENT_MIN_TEXT_LENGTH = 5; - -export interface CollectorOptions { - contentElementsLimit?: number; - interactiveElementsLimit?: number; - getElementDataId: (el: Element) => string; -} - -/** - * Collects a normalized PageModel from the DOM - * - * TODO: implement a cache, but with dataId ref consistency - * TODO: provide a plugins API to extend the collector - * TODO: provide metrics on the model collection - * - * Extracts page metadata, content, and interactive elements, and assigns - * stable `dataId` identifiers to elements for downstream usage. - */ -export class PageModelCollector { - private readonly window: Window; - private readonly document: Document; - private readonly options: Required; - - constructor(window: Window, document: Document, options: CollectorOptions) { - this.window = window; - this.document = document; - - this.options = { - contentElementsLimit: 250, - interactiveElementsLimit: 150, - ...options, - }; - } - - collect(): PageModel { - const basics = this.collectPageModelBasics(); - - return { - basics, - content: this.collectPageModelContent(basics), - interactive: this.collectPageModelInteractive(basics), - timestamp: Date.now(), - }; - } - - // shortcut for common usage - static collectFor(window: Window, document: Document, options: CollectorOptions): PageModel { - return new PageModelCollector(window, document, options).collect(); - } - - /** - * Collect basic page metadata from the current document - * - * @returns Page URL, title, description, and language. - */ - private collectPageModelBasics(): PageBasics { - return { - url: this.window.location.href, - title: this.document.title, - description: - (this.document.querySelector('meta[name="description"]') as HTMLMetaElement | null)?.content ?? '', - language: this.document.documentElement.lang ?? 'en', - viewport: { - width: this.window.innerWidth, - height: this.window.innerHeight, - scrollY: this.window.scrollY, - scrollHeight: this.document.documentElement.scrollHeight, - }, - }; - } - - /** - * Collect visible text content elements from the page - * - * Scans common content tags and returns normalized entries that include - * locator metadata, content type, and extracted text. - * - * @param basics - Page basics data extracted from the document. - * @returns {ContentElement[]} A list of extracted content elements. - */ - private collectPageModelContent(basics: PageBasics): ContentElement[] { - const allElements: ContentElement[] = []; - const contentSelectors = 'h1, h2, h3, h4, h5, h6, p, li, blockquote, figcaption'; - - this.selectElements(contentSelectors).forEach((el) => { - // skip hidden text blocks - if (!isElementVisible(el, this.window)) return; - // skip too small text blocks - const text = getElementText(el); - if (!text || text.length < CONTENT_MIN_TEXT_LENGTH) return; - - const desc = extractElementDescriptor(el, this.options.getElementDataId); - const element: ContentElement = { - ...desc, - context: getElementContext(el, this.document), - kind: 'content', - type: /^h[1-4]$/i.test(el.tagName) ? 'heading' : 'text', - text, - importanceScore: 0, // set up later - }; - element.importanceScore = scoreContentElement(element); - allElements.push(element); - }); - return this.topElements(allElements, this.options.contentElementsLimit); - } - - /** - * Collect interactive elements from the page - * - * Scans common native and ARIA-based interactive elements, filters out - * hidden or unsupported nodes, and returns structured metadata including - * locator info, role, type, text, labels, state, and link target. - * - * @param basics - Page basics data extracted from the document. - * @returns {InteractiveElement[]} A list of extracted interactive elements. - */ - private collectPageModelInteractive(basics: PageBasics): InteractiveElement[] { - const allElements: InteractiveElement[] = []; - - const interactiveSelector = - 'button, a[href], input, textarea, select, summary' + - ', [role="button"], [role="link"], [role="checkbox"], [role="radio"], [role="textbox"], [role="combobox"], [role="slider"]'; - - this.selectElements(interactiveSelector).forEach((el) => { - // skip hidden elements - if (!isElementVisible(el, this.window)) return; - // skip sensitive elements - if (isSensitiveElement(el)) return; - // skip elements with no resolved role (by ARIA or implicitly) - const role = getInteractiveRole(el); - if (!role) return; - // skip elements with no resolved type - const type = roleToInteractiveElementType(role); - if (!type) return; - - const desc = extractElementDescriptor(el, this.options.getElementDataId); - const element: InteractiveElement = { - ...desc, - context: getElementContext(el, this.document), - kind: 'interactive', - type, - role, - text: getElementText(el), - labels: getElementLabels(el, this.document), - state: getInteractiveElementState(el), - link: getElementLink(el), - inViewport: isInViewport(desc.bbox, basics.viewport), - aboveTheFold: isAboveTheFold(desc.bbox, basics.viewport), - importanceScore: 0, // set up later - }; - element.importanceScore = scoreInteractiveElement(element); - allElements.push(element); - }); - return this.topElements(allElements, this.options.interactiveElementsLimit); - } - - /** - * TODO: consider to use TreeWalker and iterator instead - * - * @param selector - * @returns List of elements - */ - private selectElements(selector: string): Element[] { - return Array.from(this.document.querySelectorAll(selector)); - } - - /** - * Sorts and returns only top-N elements by importanceScore - * - * @param elements The list of collected elements - * @param limit Must be provided - * @returns Limited list of elements - */ - private topElements(elements: T[], limit: number): T[] { - const sorted = [...elements].sort((a, b) => b.importanceScore - a.importanceScore); - - if (limit === 0) { - return sorted; - } - if (elements.length > limit) { - console.log( - `[FlowForge] ${elements[0]!.kind} elements limit exceeded (${elements.length} > ${limit}). Applying top-N filtering after scoring.`, - ); - } - return sorted.slice(0, limit); - } -} diff --git a/packages/page-model/src/collector/extractors/descriptor.ts b/packages/page-model/src/collector/extractors/descriptor.ts deleted file mode 100644 index f5359a0..0000000 --- a/packages/page-model/src/collector/extractors/descriptor.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { getElementBoundingBox } from './primitive/view.ts'; -import type { ElementDescriptor } from '#self/types'; -import getCssSelector from 'css-selector-generator'; - -/** - * Builds a stable descriptor payload for a DOM element - * - * @param el - Source DOM element. - * @param getElementDataId - Get an unique element dataId to locate it. - * @returns Element descriptor metadata including tag, generated data id, CSS selector, and bounding box. - */ -export function extractElementDescriptor(el: Element, getElementDataId: (el: Element) => string): ElementDescriptor { - const selector = getCssSelector(el); - const bbox = getElementBoundingBox(el); - - return { - tag: el.tagName.toLowerCase(), - dataId: getElementDataId(el), - selector, - bbox, - }; -} diff --git a/packages/page-model/src/collector/importance/scoring.ts b/packages/page-model/src/collector/importance/scoring.ts deleted file mode 100644 index 38559fd..0000000 --- a/packages/page-model/src/collector/importance/scoring.ts +++ /dev/null @@ -1,2 +0,0 @@ -export { scoreContentElement } from './content.ts'; -export { scoreInteractiveElement } from './interactive.ts'; \ No newline at end of file diff --git a/packages/page-model/src/index.ts b/packages/page-model/src/index.ts deleted file mode 100644 index a530cef..0000000 --- a/packages/page-model/src/index.ts +++ /dev/null @@ -1,4 +0,0 @@ -export type * from './types/pageModel.ts'; - -export * from './semantic/format.ts'; -export { PageModelCollector, type CollectorOptions } from './collector/PageModelCollector.ts'; diff --git a/packages/page-model/src/types/index.ts b/packages/page-model/src/types/index.ts deleted file mode 100644 index 98bddda..0000000 --- a/packages/page-model/src/types/index.ts +++ /dev/null @@ -1 +0,0 @@ -export type * from './pageModel.ts'; \ No newline at end of file diff --git a/packages/page-model/src/utils/index.ts b/packages/page-model/src/utils/index.ts deleted file mode 100644 index 0392cd1..0000000 --- a/packages/page-model/src/utils/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from './text.ts'; -export * from './array.ts'; \ No newline at end of file diff --git a/packages/page-model/tsconfig.json b/packages/page-model/tsconfig.json deleted file mode 100644 index d7cc2fb..0000000 --- a/packages/page-model/tsconfig.json +++ /dev/null @@ -1,33 +0,0 @@ -{ - "compilerOptions": { - "target": "esnext", - "module": "nodenext", - "moduleResolution": "nodenext", - "lib": ["DOM", "DOM.Iterable", "ES2022"], - "types": ["chrome"], - "paths": { - "#self/types": ["./src/types/index.ts"], - "#self/utils": ["./src/utils/index.ts"] - }, - "strict": true, - "noUncheckedIndexedAccess": true, - "noImplicitOverride": true, - "isolatedModules": true, - "verbatimModuleSyntax": true, - "resolveJsonModule": true, - "allowSyntheticDefaultImports": true, - "esModuleInterop": true, - "skipLibCheck": true, - "forceConsistentCasingInFileNames": true, - "allowImportingTsExtensions": true, - "rewriteRelativeImportExtensions": true, - "moduleDetection": "force", - "declaration": true, - "declarationMap": true, - "sourceMap": true, - "rootDir": "./src", - "outDir": "./dist" - }, - "include": ["src/**/*"], - "exclude": ["node_modules", "dist", "**/*.test.ts"] -} diff --git a/packages/page-model/.gitignore b/packages/page-trail/.gitignore similarity index 90% rename from packages/page-model/.gitignore rename to packages/page-trail/.gitignore index b8e53c6..cd86c27 100644 --- a/packages/page-model/.gitignore +++ b/packages/page-trail/.gitignore @@ -3,6 +3,7 @@ node_modules/ # Build output dist/ +reports/ # Logs logs/ diff --git a/packages/page-trail/README.md b/packages/page-trail/README.md new file mode 100644 index 0000000..2dbd2f5 --- /dev/null +++ b/packages/page-trail/README.md @@ -0,0 +1,78 @@ +# PageTrail + +Normalized DOM snapshot used as the core input for RAG, search, and UI guidance. + +## Structure + +```ts +interface PageTrail { + basics: PageBasics; + content: ContentElement[]; + interactive: InteractiveElement[]; + metadata: CollectionMetadata; +} +``` + +## Content elements + +Extracted from visible headings, paragraphs, list items, blockquotes, and figcaptions. Very short text is skipped. Default limit: 250. + +- text +- type: heading | text +- selector, dataId, bbox +- context (path + optional sectionName) +- importanceScore [0..1] + +## Interactive elements + +Extracted from visible buttons, links, inputs, textarea/select/summary, and supported ARIA roles. Sensitive inputs are skipped. Default limit: 150. + +- role → type (button | input | select | link) +- text + labels +- state (disabled, checked, etc) +- link (if any) +- selector, dataId, bbox +- inViewport, aboveTheFold +- context +- importanceScore [0..1] + +## Context + +Derived from ancestor containers such as main content, navigation, footer, dialog, form, section, and table. Optional `sectionName` is resolved from aria labels, headings, or legends. + +## Importance + +Heuristic scoring normalized to [0..1]. It ranks elements, applies top-N limits, and improves retrieval quality. + +## Metadata + +`basics` stores URL, title, description, language, and viewport. `metadata` stores selected and total element counts, limit flags, `collectedAt`, and `durationMs`. + +## Usage + +```ts +const pt = PageTrailCollector.collectFor(window, document, { + getElementDataId: (el) => getOrCreateDataId(el), +}); +``` + +## Format + +Helpers convert PageTrail data into semantic strings. + +```ts +formatContentElement(el); +formatInteractiveElement(el); +formatContentElementShort(el); +formatInteractiveElementShort(el); +formatSampleHeadings(el, limit); +formatSampleInteractions(el, limit); +generateSemanticMarkdown(pageTrail); +``` + +## Notes + +- DOM → structured model (LLM-independent) +- Single-page snapshot +- Optimized for search and UI actions +- Safe by default (sensitive fields excluded) diff --git a/packages/page-trail/eslint.config.mjs b/packages/page-trail/eslint.config.mjs new file mode 100644 index 0000000..12654b3 --- /dev/null +++ b/packages/page-trail/eslint.config.mjs @@ -0,0 +1,23 @@ +import js from '@eslint/js'; +import globals from 'globals'; +import tseslint from 'typescript-eslint'; + +export default tseslint.config( + { + ignores: ['dist/**'], + }, + js.configs.recommended, + ...tseslint.configs.recommended, + { + files: ['src/**/*.ts', 'test/**/*.ts'], + languageOptions: { + globals: { + ...globals.browser, + ...globals.es2022, + }, + }, + rules: { + 'no-undef': 'off', + }, + }, +); diff --git a/packages/page-trail/package.json b/packages/page-trail/package.json new file mode 100644 index 0000000..ee44b30 --- /dev/null +++ b/packages/page-trail/package.json @@ -0,0 +1,33 @@ +{ + "name": "@flowforge/page-trail", + "version": "0.1.0", + "private": true, + "type": "module", + "exports": { + ".": { + "import": "./dist/index.js", + "types": "./dist/index.d.ts" + } + }, + "scripts": { + "clean": "rm -rf dist/", + "typecheck": "tsc -p tsconfig.json --noEmit", + "build": "npm run clean && tsc -p tsconfig.json", + "test": "vitest run --coverage", + "test:watch": "vitest", + "lint": "eslint .", + "lint:fix": "eslint . --fix", + "dev": "tsc -p tsconfig.json --watch" + }, + "devDependencies": { + "@eslint/js": "^10.0.1", + "@tsconfig/node22": "^22.0.5", + "@vitest/coverage-v8": "^4.1.10", + "eslint": "^10.8.1", + "globals": "^17.9.0", + "happy-dom": "^20.11.2", + "typescript": "^6.0.2", + "typescript-eslint": "^8.66.0", + "vitest": "^4.1.10" + } +} diff --git a/packages/page-trail/src/collector/PageTrailCollector.test.ts b/packages/page-trail/src/collector/PageTrailCollector.test.ts new file mode 100644 index 0000000..7be8e12 --- /dev/null +++ b/packages/page-trail/src/collector/PageTrailCollector.test.ts @@ -0,0 +1,207 @@ +import { afterEach, describe, expect, it, vi } from 'vitest'; + +import { markHidden, markVisible, resetDocument, setViewport } from '../../test/domUtils'; +import { PageTrailCollector } from './PageTrailCollector'; + +afterEach(() => { + resetDocument(); + vi.restoreAllMocks(); +}); + +describe('PageTrailCollector', () => { + it('collects page basics', () => { + document.documentElement.lang = 'en'; + document.head.innerHTML = ``; + document.title = 'Test page'; + + setViewport({ width: 1024, height: 768, scrollY: 100, scrollHeight: 2000 }); + + const model = collect(); + + expect(model.basics).toEqual({ + url: 'http://localhost:3000/', + title: 'Test page', + description: 'Page description', + language: 'en', + viewport: { + width: 1024, + height: 768, + scrollY: 100, + scrollHeight: 2000, + }, + }); + expect(model.metadata.collectedAt).toBeTypeOf('number'); + expect(model.metadata.durationMs).toBeTypeOf('number'); + }); + + it('normalizes page basics text', () => { + document.documentElement.lang = ' en '; + document.head.innerHTML = ``; + document.title = ' Test page '; + + const model = collect(); + + expect(model.basics).toEqual( + expect.objectContaining({ + title: 'Test page', + description: 'Page description', + language: 'en', + }), + ); + }); + + it('collects visible content elements', () => { + document.body.innerHTML = ` +
+

Welcome

+

Useful paragraph text

+

No

+
+ `; + markVisible('#title'); + markVisible('#intro'); + markVisible('#short'); + + const model = collect(); + + expect(model.content).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + kind: 'content', + type: 'heading', + tag: 'h1', + text: 'Welcome', + dataId: 'title', + cssSelector: '#title', + }), + expect.objectContaining({ + kind: 'content', + type: 'text', + tag: 'p', + text: 'Useful paragraph text', + dataId: 'intro', + cssSelector: '#intro', + }), + ]), + ); + expect(model.content.some((el) => el.dataId === 'short')).toBeFalsy(); + }); + + it('collects visible interactive elements', () => { + document.body.innerHTML = ` +
+ + Docs + +
+ `; + markVisible('#save'); + markVisible('#docs'); + markVisible('#email'); + + const model = collect(); + + expect(model.interactive).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + kind: 'interactive', + type: 'button', + role: 'button', + dataId: 'save', + cssSelector: '#save', + labels: [{ source: 'aria-label', value: 'Save changes' }], + }), + expect.objectContaining({ + kind: 'interactive', + type: 'link', + role: 'link', + dataId: 'docs', + cssSelector: '#docs', + link: { + type: 'internal', + href: 'http://localhost:3000/docs', + }, + }), + expect.objectContaining({ + kind: 'interactive', + type: 'input', + role: 'textbox', + dataId: 'email', + cssSelector: '#email', + labels: [{ source: 'placeholder', value: 'Email' }], + }), + ]), + ); + }); + + it('skips hidden and sensitive interactive elements', () => { + document.body.innerHTML = ` + + + + `; + markVisible('#visible'); + markHidden('#hidden'); + markVisible('#password'); + + const model = collect(); + + expect(model.interactive.map((el) => el.dataId)).toEqual(['visible']); + }); + + it('applies content and interactive limits after scoring', () => { + document.body.innerHTML = ` +
+

Important heading

+

Regular paragraph text

+ + Docs +
+ `; + markVisible('#heading'); + markVisible('#paragraph'); + markVisible('#button'); + markVisible('#link'); + + const model = collect({ + contentElementsLimit: 1, + interactiveElementsLimit: 1, + }); + + expect(model.content).toHaveLength(1); + expect(model.content[0]).toEqual(expect.objectContaining({ dataId: 'heading' })); + expect(model.interactive).toHaveLength(1); + expect(model.interactive[0]).toEqual(expect.objectContaining({ dataId: 'button' })); + }); + + it('keeps cssSelector undefined when css selector resolver is not configured', () => { + document.body.innerHTML = ``; + markVisible('#save'); + + const model = PageTrailCollector.collectFor(window, document, { + getElementDataId: (el) => el.id, + }); + + expect(model.interactive[0]).toEqual(expect.objectContaining({ dataId: 'save', cssSelector: undefined })); + }); + + it('collectFor returns a collected page trail', () => { + document.body.innerHTML = ``; + markVisible('#save'); + + const model = PageTrailCollector.collectFor(window, document, { + getElementDataId: (el) => el.id, + }); + + expect(model.interactive).toHaveLength(1); + expect(model.interactive[0]).toEqual(expect.objectContaining({ dataId: 'save' })); + }); +}); + +function collect(options: Partial[2]> = {}) { + return new PageTrailCollector(window, document, { + getElementDataId: (el) => el.id, + getElementCssSelector: (el) => `#${el.id}`, + ...options, + }).collect(); +} diff --git a/packages/page-trail/src/collector/PageTrailCollector.ts b/packages/page-trail/src/collector/PageTrailCollector.ts new file mode 100644 index 0000000..9a37d47 --- /dev/null +++ b/packages/page-trail/src/collector/PageTrailCollector.ts @@ -0,0 +1,257 @@ +import type { ContentElement, ElementIdentifier, InteractiveElement, PageBasics, PageTrail } from '../types/index.ts'; + +import { getElementLabels } from './extractors/primitive/label.ts'; +import { getInteractiveRole, roleToInteractiveElementType } from './extractors/primitive/role.ts'; +import { getElementBoundingBox, isAboveTheFold, isElementVisible, isInViewport } from './extractors/primitive/view.ts'; +import { getInteractiveElementState } from './extractors/primitive/state.ts'; +import { getElementText } from './extractors/primitive/text.ts'; +import { getElementLink } from './extractors/primitive/link.ts'; +import { getElementContext } from './extractors/context.ts'; +import { isSensitiveElement } from './extractors/primitive/sensitive.ts'; +import { scoreContentElement, scoreInteractiveElement } from './importance/scoring.ts'; +import { type TopElements, topElements } from './importance/topEl.ts'; +import type { InteractiveElementScoringData } from './importance/interactive.ts'; +import type { ContentElementScoringData } from './importance/content.ts'; +import { normalizeText } from '../utils/index.ts'; + +// constants +const CONTENT_MIN_TEXT_LENGTH = 5; + +export interface CollectorOptions { + contentElementsLimit?: number; + interactiveElementsLimit?: number; + getElementDataId: (el: Element) => string; + // For example: css-selector-generator + getElementCssSelector?: (el: Element) => string; +} + +type ResolvedCollectorOptions = Required> & + Pick; + +/** + * Collects a normalized PageTrail from the DOM + * + * TODO: implement a cache, but with dataId ref consistency + * TODO: provide a plugins API to extend the collector + * + * Extracts page metadata, content, and interactive elements, and assigns + * stable `dataId` identifiers to elements for downstream usage. + */ +export class PageTrailCollector { + // window.location.href - reads the current page URL + // window.innerWidth - reads viewport width + // window.innerHeight - reads viewport height + // window.scrollY - reads current vertical scroll position + // window.getComputedStyle(element) - checks computed CSS styles for visibility + private readonly window: Window; + // document.title - reads the page title + // document.querySelector('meta[name="description"]') - finds the meta description element + // document.documentElement.lang - reads the page language from + // document.documentElement.scrollHeight - reads the full scrollable page height + // document.querySelectorAll(selector) - finds content and interactive elements by CSS selector + // document.getElementById(id) - resolves IDs from aria-labelledby to label elements + // document.querySelector('label[for="..."]') - finds a