diff --git a/.gitignore b/.gitignore index 2fa750f..391658e 100644 --- a/.gitignore +++ b/.gitignore @@ -25,7 +25,10 @@ *.sln *.sw? -# Project specific +# Project specific (embedding model files are fetched separately for build:pro) +embedding_model/*.onnx +embedding_model/vocab.txt + src/backend/bin/ src/backend/*.txt src/backend/*.db @@ -50,6 +53,7 @@ tools/ !tools/serve-frontend.mjs +!tools/ensure-embedding-model.mjs # Local helper/status files .git_status_output.txt \ No newline at end of file diff --git a/README.md b/README.md index 1707471..ead46cf 100644 --- a/README.md +++ b/README.md @@ -6,79 +6,84 @@ ## 概要 -**TelosDB** は、Tauri 2 と Rust -を核とした、プライバシー重視のローカル特化型ナレッジベースです。 -数学的な文書解析(Latent Semantic -Analysis)を活用し、外部APIや重いGGUFモデルを一切介さずに、高速な意味検索と知識管理を実現します。 +**TelosDB** は、Tauri 2 と Rust を核とした、プライバシー重視のローカル特化型ナレッジベースです。**Community 版**は LSA(Latent Semantic Analysis)、**Pro 版**は日本語向け埋め込みモデル(sentence-BERT 系)により、外部 API やクラウドを介さずに意味検索と知識管理を実現します。 -AIエージェントが自律的に記憶を蓄積・整理するための基盤として設計されており、**Model -Context Protocol (MCP)** を通じて、LM Studio や Claude Desktop -等から即座に利用可能です。 +AI エージェントが自律的に記憶を蓄積・整理するための基盤として設計されており、**Model Context Protocol (MCP)** を通じて、Cursor や Claude Desktop 等から即座に利用可能です。 + +--- + +## エディション + +| | Community 版 | Pro 版 | +|---|--------------|--------| +| **ベクトル化** | LSA(50 次元、モデル不要) | 埋め込みモデル(768 次元、ONNX) | +| **検索** | キーワード・通常の文どちらも可 | 同上、言い回しの違いにも強い | +| **起動** | `npm run dev` | `npm run dev:pro`(要 model_quantized.onnx) | + +どちらのエディションも、**短い語句でも通常の文でも**そのまま検索クエリとして利用できます。 --- ## 主な機能 -- **🧠 セマンティック検索 (Vector Search)**: LSA (Latent Semantic Analysis) - による数学的文脈解析に基づいた高度な検索(ベクトル検索)。 -- **🤖 MCP SSE サーバー内蔵**: AIエージェント(LM Studio / - Claude等)からの自律的な知識操作(CRUD)が可能。 -- **🔄 自動ヒーリング (Self-Healing)**: DB - 内のテキストとベクトルの不一致を検出し、バックグラウンドで自動同期。 -- **🛡️ 完全ローカル・プライバシー**: - データ計算、ベクトル空間のすべてがローカル環境内で完結。GPU不要。 -- **✨ プレミアムなUI/UX**: - ミニマルなハイコントラスト・デザインを採用したデスクトップ体験。 -- **📦 常駐管理**: - システムトレイ常駐により、MCPサーバーをバックグラウンドで常に待機。 +- **セマンティック検索**: キーワードや自然文で意味に基づいた検索。文書単位で結合して返すオプションあり。 +- **MCP SSE サーバー内蔵**: AI クライアント(Cursor 等)からツールとして検索・CRUDが可能。 +- **自動ヒーリング**: DB 内のテキストとベクトルの不一致を検出し、バックグラウンドで同期。 +- **完全ローカル・プライバシー**: 計算・ベクトル空間はすべてローカルで完結。GPU 不要。 +- **システムトレイ常駐**: MCP サーバーをバックグラウンドで待機。 --- -## システム構造 +## MCP で接続する -```mermaid -graph TD - subgraph "Frontend (Webview)" - UI["Minimalist Dark UI (Vanilla JS/CSS)"] - SSE["SSE Client Monitor"] - end +1. **TelosDB を起動する**(アプリが MCP サーバーをポート 3001 で待ち受けます)。 +2. **MCP クライアントで SSE の URL を指定する**: - subgraph "Backend (Rust / Tauri 2)" - Core["Tauri Core & Command Handlers"] - Axum["Axum (MCP SSE Server)"] - LSA["LSA Engine (ndarray/SVD)"] - DB[("SQLite + sqlite-vec")] - Logger["Rotating File Logger"] - end - - UI <-- JSON/SSE --> Axum - Axum <-- CRUD/Search --> DB - Core -- Analysis --> LSA - LSA -- Vector Store --> DB - Axum -- SQL Access --> DB +```json +{ + "mcpServers": { + "TelosDB": { + "url": "http://127.0.0.1:3001/sse" + } + } +} ``` +接続後、`search_text`(検索)、`add_item_text`(追加)、`update_item`(更新)、`lsa_retrain`(RE-INDEX)などのツールが利用できます。 + --- ## クイックスタート -### 1. 動作要件 +### 動作要件 - Windows 10/11 (x64) - Rust 1.77.2+ -- Node.js (Bun 推奨) +- Node.js(Bun 推奨) -### 2. モデルの準備 - -本バージョンは LSA (Latent Semantic Analysis) -を使用するため、外部モデルファイルの配置は**不要**です。初回起動時に自動的にベクトル空間の構成が始まります。 - -### 3. ビルドと実行 +### Community 版(モデル不要) ```bash -bun install -# 開発モードでの起動 -bun run tauri dev +npm install +npm run dev +``` + +初回起動時に LSA によるベクトル空間が自動で構成されます。 + +### Pro 版(埋め込みモデル使用) + +- **インストーラ利用**: Pro 版インストーラ(`TelosDB-Pro_*.exe`)には埋め込みモデルが**同梱**されています。インストールするだけで利用可能で、別途モデルのダウンロードは不要です。 +- **開発時**(`npm run dev:pro`): プロジェクトルートの `embedding_model/` に `model_quantized.onnx` と `vocab.txt` を配置するか、`TELOS_EMBEDDING_MODEL_DIR` でパスを指定。取得元: [sentence-bert-base-ja-mean-tokens-v2-int8](https://gitbucket.tmworks.club/dtmoyaji/sentence-bert-base-ja-mean-tokens-v2-int8)。 + +```bash +npm run dev:pro +``` + +### ビルド + +```bash +npm run build:community # Community 版 +npm run build:pro # Pro 版(embedding_model/ に model_quantized.onnx と vocab.txt が必要。同梱されたインストーラが生成される) ``` --- @@ -86,19 +91,18 @@ ## ディレクトリ構成 - `src/frontend/`: フロントエンド(Vanilla JS / CSS) -- `src/backend/`: バックエンド(Rust / Tauri設定 / MCP実装 / LSAエンジン) -- `docs/specification/`: システム設計書・技術仕様書 -- `docs/workflow/`: プロジェクト運用・管理ルール(Issue管理等) -- `docs/issues/`: 外部 Issue トラッカー同期用(`.gitignore` 対象) -- `journals/`: 作業記録(日付別管理) +- `src/backend/`: バックエンド(Rust / Tauri / MCP 実装 / LSA・埋め込みエンジン) +- `docs/specification/`: システム設計・技術仕様(アーキテクチャ、DB、MCP、埋め込み・エディション、モノリシック化、KPI・検証、UI テスト等) +- `docs/issues/`: Issue 同期用(Git 追跡外) +- `docs/references/`: 調査メモ・参照資料 +- `journals/`: 作業記録(日付別) --- -## データの保存場所とアンインストール +## データの保存場所 -- **ユーザーデータ**(データベース `telos.db`、ログ、設定で利用する vec0.dll のコピー)は **`%APPDATA%\com.telosdb.app\`** に保存されます。 -- **アンインストール時**:NSIS インストーラのアンインストールは「インストール先フォルダ」のみを削除します。**`%APPDATA%\com.telosdb.app\` は削除されない**ため、telos.db をはじめとするユーザーデータは残ります。 -- **アップグレード時**:新しいバージョンを上書きインストールしても、同じフォルダのデータをそのまま利用するため、**データが消えて台無しになることはありません**。完全に消したい場合のみ、手動で `%APPDATA%\com.telosdb.app\` フォルダを削除してください。 +- **ユーザーデータ**(`telos.db`、ログ、vec0.dll のコピー)は **`%APPDATA%\com.telosdb.app\`** に保存されます。 +- アンインストール時もこのフォルダは残ります。完全に消す場合は手動で削除してください。 --- @@ -108,8 +112,8 @@ ## インストーラ -- Windows版 https://dtmoyaji.base.shop/items/136779939 +- Windows 版: https://dtmoyaji.base.shop/items/136779939 ## ライセンス -MIT License \ No newline at end of file +MIT License diff --git a/RELEASE_v0.3.2.md b/RELEASE_v0.3.2.md deleted file mode 100644 index e40621b..0000000 --- a/RELEASE_v0.3.2.md +++ /dev/null @@ -1,54 +0,0 @@ -# TelosDB v0.3.2 Release Notes - -TelosDB v0.3.2 へようこそ! -今回のリリースでは、MCP でのドキュメント件数取得(Issue #5)、MCP ACTIVITY 表示の修正、トレイアイコンの安定化、LSA の自動再学習、インデックス状態の GUI 表示、文書一覧のプレビュー表示、サイドバーの整理に加え、**OS ログイン時の自動起動**(Issue #6)を設定で有効・無効できるようにしました。 - -## 🚀 新機能 (Major Features) - -### 1. MCP ドキュメント件数取得 (get_document_count - Issue #5) - -- MCP ツール `get_document_count` を追加しました。格納されている**ドキュメント数**(`documents` テーブルの件数)を返します。 -- HTTP エンドポイント `/doc_count` および UI ヘッダーの「X docs」表示も、すべてドキュメント数で統一しました。 - -### 2. LSA の自動再学習 (Auto LSA Retraining) - -- ドキュメントの追加・更新・削除があったとき、変更件数が閾値を超えたタイミングで、90 秒のデバウンス後にバックグラウンドで LSA モデルの再学習を自動実行するようにしました。 -- 閾値は「登録ドキュメント数の 20%」と「5 件」の**少ない方**です。 - -### 3. インデックス状態の GUI 表示 - -- ヘッダーに「LSA学習中…」「ベクトル同期中…」用のバッジを追加しました。 -- 起動時・RE-INDEX 実行時・自動再学習時に、インデックス構築の進行状況がリアルタイムで確認できます。 - -### 4. OS ログイン時の自動起動 (Issue #6) - -- **設定**パネルに「OSにログインしたときに自動で起動する」オプションを追加しました。 -- オンにすると、Windows のサインイン時に TelosDB が自動で起動し、トレイに常駐します。オフにすると従来どおり手動起動のみです。 - ---- - -## 🛠 改善・修正 (Improvements & Fixes) - -### MCP - -- **MCP ACTIVITY が空になる不具合**: ツール呼び出し時に `mcp:call:` を SSE でブロードキャストするようにし、検索パネル下部の MCP ACTIVITY にメソッド名が表示されるようになりました。 - -### UI・操作性 - -- **トレイアイコン**: タスクトレイのアイコンをクリックするとウィンドウが一瞬開いてすぐ閉じる事象を修正。左クリックに 400ms のデバウンスを導入しました。 -- **文書一覧**: 文書管理テーブルに「先頭(chunk0)」列を追加。各ドキュメントのチャンク 0 の先頭 15 文字を表示し、一覧から内容を把握しやすくしました。 -- **サイドバー**: 左サイドバー下部の TelosDB アコーディオンと著作権表示を削除し、「検索・文書管理・設定」のナビのみのシンプルな構成にしました。 - ---- - -## 📦 ビルド・インストール - -リポジトリルートで次を実行する。 - -```bash -npm install -npm run tauri build -``` - -- 配布用の NSIS インストーラ等は `src/backend/target/release/bundle/` 以下に出力される。ファイル名は `TelosDB_0.3.2_x64-setup.exe` です。 -- 初回起動時に vec0.dll がアプリデータフォルダへコピーされる。問題が発生した場合は `%APPDATA%\com.telosdb.app\logs\telos.log` で原因を確認できる。 diff --git a/RELEASE_v0.3.2_Community.md b/RELEASE_v0.3.2_Community.md new file mode 100644 index 0000000..850f0cf --- /dev/null +++ b/RELEASE_v0.3.2_Community.md @@ -0,0 +1,71 @@ +# TelosDB Community 版 v0.3.2 — 商品紹介 + +## そのまま使える、ローカル完結の意味検索 + +**TelosDB Community 版**は、追加のモデルや設定なしで、すぐに意味検索を始められるデスクトップアプリです。データはすべてお使いの PC 内に留まり、クラウドへ一切送信しません。 + +--- + +## こんな方におすすめ + +- まずは手軽に意味検索を試したい +- モデルのダウンロードや GPU 環境を用意したくない +- 個人のメモやドキュメントを「意味で」探したい +- Cursor や Claude Desktop など AI ツールと MCP で連携したい + +--- + +## Community 版の特長 + +### モデル不要で動く + +LSA(潜在意味解析)により、**50 次元のベクトル**で文書を表現します。大きな AI モデルは不要。インストール後、そのまま検索できます。 + +### キーワードも自然な文も、そのまま検索 + +「春の歌」でも「桜の季節に聴きたい曲」のような文でも検索可能。結果は**文書単位**でまとめて返すため、どのドキュメントがヒットしたかが分かりやすくなっています。 + +### MCP で AI とつながる + +Model Context Protocol(MCP)に対応。Cursor や Claude Desktop から「検索」「追加」「RE-INDEX」などのツールとして TelosDB を呼び出せます。AI エージェントの「記憶」として利用するのに最適です。 + +### 自動でインデックスを整える + +ドキュメントの増減に応じて、バックグラウンドで LSA を再学習。画面上では「LSA学習中…」「ベクトル同期中…」の表示で進行状況を確認できます。必要に応じて RE-INDEX で手動再構築も可能です。 + +### ログイン時に自動起動(オプション) + +設定で「OSにログインしたときに自動で起動する」をオンにすると、Windows にサインインしたタイミングで TelosDB が起動し、トレイで待機。いつでも MCP 経由で検索できます。 + +--- + +## 含まれる機能(v0.3.2) + +- 意味検索(LSA 50 次元)+ 全文検索(FTS5)のハイブリッド検索 +- 検索結果の文書単位表示(`group_by_document`) +- MCP ツール: `search_text` / `add_item_text` / `update_item` / `delete_item` / `get_item_by_id` / `get_document_count` / `lsa_retrain`(RE-INDEX) +- ドキュメント件数の表示(ヘッダー「X docs」・`get_document_count`) +- インデックス状態の表示(LSA学習中・ベクトル同期中) +- 文書一覧のプレビュー(先頭 15 文字表示) +- トレイ常駐・バージョン表示の改善 + +--- + +## ご利用方法 + +**インストーラ**: `TelosDB-Community_0.3.2_x64-setup.exe` を実行してインストールしてください。 + +**ソースからビルドする場合**: + +```bash +npm install +npm run build:community +``` + +- 出力: `src/backend/target/release/bundle/nsis/TelosDB-Community_0.3.2_x64-setup.exe` +- 初回起動時に vec0.dll がアプリデータフォルダへコピーされます。不具合時は `%APPDATA%\com.telosdb.app\logs\telos.log` をご確認ください。 + +--- + +より高精度な意味検索をご希望の方は **Pro 版**をご検討ください。 +→ [TelosDB Pro 版 商品紹介](RELEASE_v0.3.2_Pro.md) diff --git a/RELEASE_v0.3.2_Pro.md b/RELEASE_v0.3.2_Pro.md new file mode 100644 index 0000000..37b50e6 --- /dev/null +++ b/RELEASE_v0.3.2_Pro.md @@ -0,0 +1,85 @@ +# TelosDB Pro 版 v0.3.2 — 商品紹介 + +## 日本語に強い、高精度な意味検索 + +**TelosDB Pro 版**は、日本語向けの埋め込みモデル(768 次元)を**インストーラに同梱**したエディションです。インストールするだけで、言い回しの違いに強い高精度な意味検索が使えます。ビジネス文書や学術メモの検索に適し、データはすべてローカルで完結。クラウドへ送信しません。 + +--- + +## こんな方におすすめ + +- 検索精度をより高めたい +- 「似た意味の文」でしっかりヒットさせたい +- 報告書・論文・仕様書などフォーマルな日本語を扱う +- Cursor や Claude Desktop と MCP で連携し、高品質な検索結果を AI に渡したい + +--- + +## Pro 版の特長 + +### 日本語特化の埋め込みで高精度 + +**sentence-BERT 系の日本語モデル**(768 次元)で文書をベクトル化。キーワードの一致だけでなく、「言い換え」や「関連する表現」にも強く、自然な文で検索できます。結果は**文書単位**で返るため、どのドキュメントが該当したかが一目で分かります。 + +### 量子化モデルで軽く・速く(インストーラに同梱) + +推論用に **INT8 量子化した ONNX モデル**を使用。GPU は不要で、一般的な PC の CPU だけで動作します。**モデルはインストーラに含まれており、別途ダウンロードは不要**。インストール後すぐに高精度検索をご利用いただけます。 + +### MCP で AI とつながる + +Model Context Protocol(MCP)に対応。Cursor や Claude Desktop から「検索」「追加」「RE-INDEX」などのツールとして TelosDB を呼び出せます。高精度な検索結果を AI のコンテキストとしてそのまま利用できます。 + +### インデックス状態が分かる + +起動時や RE-INDEX 時に、HNSW の構築状況などが画面上で確認できます。「Pro」バッジでエディションが識別でき、モデルの読み込み状態も把握しやすくなっています。 + +### ログイン時に自動起動(オプション) + +設定で「OSにログインしたときに自動で起動する」をオンにすると、Windows にサインインしたタイミングで TelosDB が起動。トレイで待機し、いつでも MCP 経由で高精度検索を利用できます。 + +--- + +## インストーラにモデル同梱(v0.3.2) + +Pro 版のインストーラ(`TelosDB-Pro_0.3.2_x64-setup.exe`)には、**埋め込み用の量子化 ONNX モデル**が同梱されています。 + +- **利用者**: 別途モデルのダウンロードは不要。インストールするだけで、インストール先のリソースから自動的にモデルが参照され、高精度な意味検索が利用できます。 +- **同梱内容**: `model_quantized.onnx`(日本語 sentence-BERT 量子化モデル)・`vocab.txt`(語彙)。インストール後にアプリが正しいパスでこれらを参照するよう、リソース解決を修正済みです。 +- モデルが読み込めない場合でも、全文検索(FTS)のみで検索は可能です。`TELOS_EMBEDDING_NO_OPTIMIZE=1` で最適化をスキップして読み込む方法も README・仕様書で案内しています。 + +--- + +## 含まれる機能(v0.3.2) + +- **埋め込みモデルのインストーラ同梱**(別途ダウンロード不要。インストール先から自動参照) +- 意味検索(埋め込み 768 次元)+ 全文検索(FTS5)のハイブリッド検索 +- 検索結果の文書単位表示(`group_by_document`) +- MCP ツール: `search_text` / `add_item_text` / `update_item` / `delete_item` / `get_item_by_id` / `get_document_count` / `lsa_retrain`(RE-INDEX) +- ドキュメント件数の表示(ヘッダー「X docs」・`get_document_count`) +- インデックス状態の表示(ベクトル同期中・HNSW 構築状況) +- 文書一覧のプレビュー(先頭 15 文字表示) +- トレイ常駐・バージョン表示の改善 + +--- + +## ご利用方法 + +**インストーラ**: `TelosDB-Pro_0.3.2_x64-setup.exe` を実行してインストールしてください。埋め込みモデルはインストーラに含まれており、追加のダウンロードは不要です。 + +**ソースからビルドする場合**(配布用インストーラを作る開発者向け): + +1. [sentence-bert-base-ja-mean-tokens-v2-int8](https://gitbucket.tmworks.club/dtmoyaji/sentence-bert-base-ja-mean-tokens-v2-int8) から `target/` 一式(`model_quantized.onnx`・`vocab.txt`)を取得し、プロジェクトの **`embedding_model/`** に配置します。 +2. `npm run build:pro` を実行すると、それらがインストーラに同梱された Pro 版がビルドされます。 + +```bash +npm install +npm run build:pro +``` + +- 出力: `src/backend/target/release/bundle/nsis/TelosDB-Pro_0.3.2_x64-setup.exe`(モデル同梱) +- 初回起動時に vec0.dll がアプリデータフォルダへコピーされます。不具合時は `%APPDATA%\com.telosdb.app\logs\telos.log` をご確認ください。 + +--- + +モデル不要で手軽に始めたい方は **Community 版**をご利用ください。 +→ [TelosDB Community 版 商品紹介](RELEASE_v0.3.2_Community.md) diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 0000000..90b5491 --- /dev/null +++ b/docs/README.md @@ -0,0 +1,31 @@ +# docs — プロジェクトドキュメント + +TelosDB の仕様・設計・参照資料を格納するディレクトリです。 + +## 構成 + +| ディレクトリ | 内容 | +|-------------|------| +| **specification/** | システム仕様・設計。アーキテクチャ、DB、MCP API、UI、開発ガイド、埋め込み・エディション、モノリシック化、KPI・検証、UI テスト、OpenAPI / MCP スキーマ。 | +| **issues/** | Issue 同期用の Markdown(Git 追跡外。`node tools/scripts/sync_issues.mjs` で同期)。 | +| **references/** | 調査メモ・外部資料の参照。ベクトル化手法、BM25、Elasticsearch 等。 | + +## 主なファイル(specification/) + +**01〜06**: 製品仕様の本体(概要・アーキテクチャ・DB・MCP・開発・UI)。**07〜12**: 補足(埋め込み技術・ゴール/KPI・検証手順・モノリシック経緯・Pro 動作確認・UI テスト方針)。 + +- **01_system_overview.md** — システム概要・エディション・主要機能 +- **02_architecture_design.md** — アーキテクチャ設計 +- **03_database_specification.md** — データベース仕様 +- **04_mcp_api_specification.md** — MCP API 仕様 +- **05_development_guide.md** — 開発ガイド(ビルド・拡張) +- **06_ui_design_spec.md** — UI デザイン仕様 +- **07_embedding_tract.md** — 埋め込みモデル(ONNX + tract)の組み込み +- **08_embedding_tract_goals_and_kpi.md** — 埋め込み改修:ゴールとテスト KPI +- **09_embedding_tract_implementation_and_tests.md** — KPI 達成のための改修・テスト +- **10_monolithic_dev.md** — モノリシック化(開発時アーキテクチャ) +- **11_pro_vectorization_and_ann.md** — Pro ベクトル化・ANN 動作確認 +- **12_ui_testing_options.md** — UI をテストに組み込む方法(E2E / Playwright 等) +- **mcp.json**, **openapi.yaml** — MCP / API スキーマ + +運用ルール(Issue 管理・Git 運用など)は `.agent/rules/` にあります。 diff --git a/docs/specification/01_system_overview.md b/docs/specification/01_system_overview.md index cb9dd43..c8d7ea8 100644 --- a/docs/specification/01_system_overview.md +++ b/docs/specification/01_system_overview.md @@ -1,33 +1,39 @@ # システム概要 (System Overview) -## 1. 開発背景と目的 +## 1. 目的と位置づけ -**TelosDB** は、プライバシーを重視したローカル専用の「意味検索(Vector Search)基盤」を提供するために開発されました。 +**TelosDB** は、プライバシーを重視したローカル専用の**意味検索(セマンティック検索)基盤**です。データは一切クラウドに送らず、Windows 上で完結します。 -現代の AI 活用において、外部クラウド(OpenAI 等)へのデータ送信はセキュリティ上の大きな懸念事項です。本システムは、数学的な文書解析(Latent Semantic Analysis)とベクトルエンジンをすべてユーザーのローカル環境(Windows Desktop)で動作させることにより、以下の価値を提供します。 +- **プライバシー**: 文書・ベクトルはローカルのみ。外部 API 不要。 +- **2 エディション**: **Community 版**(LSA・モデル不要)と **Pro 版**(日本語埋め込みモデル・高精度検索)を同一リポジトリからビルドで切り替え。 +- **MCP 対応**: Model Context Protocol に準拠し、Cursor や Claude Desktop 等から「ツール」として利用可能。 -- **絶対的なプライバシー**: 文章データやベクトルデータがネットワークを介して外部に送信されることはありません。 -- **オフライン動作**: 重い推論モデルやインターネット接続環境に左右されず、常に高速な検索が可能です。 -- **省リソース・ゼロコスト**: 推論はローカル CPU のみで行われるため、ハイスペックな GPU や API トークン費用は一切不要です。 +## 2. エディション -## 2. 主要機能詳説 +| 項目 | Community 版 | Pro 版 | +|------|--------------|--------| +| **ベクトル化** | LSA(50 次元、SVD)。モデルファイル不要。 | 埋め込みモデル(768 次元、ONNX)。sentence-BERT 系日本語モデル。 | +| **検索** | キーワード+意味検索。短い語句・自然文どちらも可。 | 同上。言い回しの違いに強い。 | +| **起動** | `npm run dev` / インストーラ `TelosDB-Community_*` | `npm run dev:pro` / インストーラ `TelosDB-Pro_*`(要 `embedding_model/`) | +| **依存** | 軽量(LSA のみ)。GPU 不要。 | ONNX 実行系(tract または ort)。GPU 不要。 | -| 機能 | 解説 | 技術的実装 | -| :--- | :--- | :--- | -| **ローカル・セマンティック検索** | キーワードの一致だけでなく、LSA を用いた数学的な文脈解析に基づいた検索を実現。 | `sqlite-vec` + `LSA` (SVD) | -| **MCP (Model Context Protocol)** | AI エージェント(Cursor, Claude Desktop, IDE 等)が本ツールを「外部知識」として呼び出すための標準規格。 | SSE Transport (Port 3001) | -| **低レイテンシ・省リソース** | モデル推論の代わりに LSA を採用。超軽量かつ高速なインデックス更新・検索を実現。 | `ndarray` + `SVD` | -| **セルフヒーリング (Self-healing)** | DB 内のテキストとベクトルの不一致を検出し、バックグラウンドで自動同期。 | `db::sync_vectors` | -| **常駐・軽量 UI** | システムトレイに常駐し、SSE を使った非同期通信でバックグラウンドログをリアルタイム表示。 | Tauri 2 + Vanilla JS + Axum | +## 3. 主要機能 -## 3. システム特性 (Technical Highlights) - -- **Hermetic Design**: 必要な DLL 群をパッケージ内に封入し、ユーザー環境のインストール状況に依存せず「置いてすぐ動く」ことを重視しています。 -- **LSA (Latent Semantic Analysis)**: 伝統的かつ強力な LSA 技術を採用。モデルファイルを必要とせず、数千ドキュメント以下であれば LLM を上回る応答速度と、プライバシーの完全な確保(ローカル推論も不要)を実現します。 +- **セマンティック検索**: クエリをベクトル化し、類似度でランキング。結果は文書単位で結合して返すオプションあり。 +- **MCP サーバー**: ポート 3001 で SSE。`search_text`・`add_item_text`・`update_item`・`delete_item`・`get_item_by_id`・`get_document_count`・`lsa_retrain`(RE-INDEX)等。 +- **セルフヒーリング**: テキストと FTS/ベクトルの不整合を検出し、起動時・手動 heal で同期。 +- **常駐 UI**: システムトレイ常駐。検索・文書管理・設定。エディション表示(Community / Pro)。 ## 4. 動作環境 - **OS**: Windows 10/11 (64-bit) -- **CPU**: 一般的な PC で動作可能 -- **GPU**: 不要(CPU のみで高速動作) -- **モデル**: Latent Semantic Analysis (Dimensions configurable) +- **CPU**: 一般的な PC。GPU 不要。 +- **Pro 版**: `embedding_model/` に `model_quantized.onnx` と `vocab.txt` を配置(sentence-BERT 系)。 + +## 5. 関連仕様 + +- アーキテクチャ・プロセス構成: **02_architecture_design.md** +- データベース・検索アルゴリズム: **03_database_specification.md** +- MCP・API 詳細: **04_mcp_api_specification.md** +- ビルド・開発: **05_development_guide.md** +- Pro 埋め込みの技術詳細: **07_embedding_tract.md** diff --git a/docs/specification/02_architecture_design.md b/docs/specification/02_architecture_design.md index a1ec5a0..e21daba 100644 --- a/docs/specification/02_architecture_design.md +++ b/docs/specification/02_architecture_design.md @@ -1,55 +1,64 @@ -# アーキテクチャ設計仕様書 (Architecture Design Specification) +# アーキテクチャ設計 (Architecture Design) -## 1. 全体構造図 +## 1. 全体構成 -本システムは、OS のシステムトレイに常駐する「ホストプロセス(Tauri 2)」の中で、GUI、外部エージェントとの通信(MCP)、および数学的な文書解析(LSA)をすべて完結させる、シングルプロセス・マルチスレッド構造です。 +本システムは **1 プロセス(Tauri 2)** 内で、GUI・MCP サーバー・ベクトル解析を完結させます。開発時も本番も Node は使わず、Tauri が静的配信(8474)と MCP API(3001)の両方を担当します。 ```mermaid graph TD - subgraph "Presentation Layer (Webview2)" - UI["Minimalist Dark UI (Vanilla JS)"] - SSE_Monitor["Activity Log View (SSE)"] + subgraph "Presentation (WebView2)" + UI["UI (Vanilla JS)"] + SSE_Monitor["Activity Log (SSE)"] end - subgraph "Application Layer (Rust / Tauri 2)" - Tauri["Tauri Core (Main Process)"] - Tray["System Tray Controller"] - Axum["Axum (MCP SSE Server - Port 3001)"] - LSA["LSA Engine (ndarray/SVD)"] + subgraph "Application (Rust / Tauri 2)" + Tauri["Tauri Core"] + Tray["System Tray"] + Static["Axum: 静的配信 :8474"] + MCP["Axum: MCP API :3001"] + LSA["LSA Engine (Community)"] + Embed["Embedding Model (Pro)"] end - subgraph "Infrastructure Layer" - DB["SQLite + sqlite-vec (telos.db)"] + subgraph "Data" + DB["SQLite + vec0 + FTS5"] end - UI -- "IPC: Invoke" --> Tauri - Tauri -- "IPC: UI Update" --> UI - Axum -- "SSE: Status Events" --> UI - Tauri -- "LSA Analysis" --> LSA - LSA -- "Vectors" --> DB - Tauri -- "SQL" --> DB - Axum -- "SQL" --> DB + UI --> Tauri + Tauri --> Static + Tauri --> MCP + MCP --> LSA + MCP --> Embed + LSA --> DB + Embed --> DB + MCP --> DB ``` -## 2. プロセス間通信とデータフロー +## 2. プロセス・ポート -### 2.1 Tauri Core と Axum (MCP) の連携 +| 役割 | プロセス | ポート | 備考 | +|------|----------|--------|------| +| フロント配信 | Tauri 内 Axum | **8474** | 開発時: `src/frontend` を静的配信。本番: 同梱済み資材。 | +| MCP API | Tauri 内 Axum | **3001** | SSE + JSON-RPC。LSA/埋め込みの準備完了後に listen。 | +| WebView | Tauri | — | devUrl = http://127.0.0.1:8474 | -本システムの最大の特徴は、デスクトップアプリとしての GUI 管理を Tauri が行い、外部通信インターフェース(MCP)を Axum が担当する「デュアルサーバ」構成にあります。 -両者はメモリを共有していますが、非同期なデータ操作通知(例:エージェントが MCP 経由でデータを登録した際に UI の件数を増やす)には **Tokio Broadcast Channel** を使用し、疎結合な連携を実現しています。 +- **モノリシック**: beforeDevCommand は使わない。`tauri dev` は Cargo ビルドのみ。同一プロセスで 8474 と 3001 を listen。 -### 2.2 ベクトル解析 (LSA Engine) +## 3. エディション別のベクトル経路 -Embedding 計算は外部の `llama-server` に依存せず、Rust バックエンド内で `ndarray` を用いた特異値分解(SVD)により直接行われます。 +| エディション | ベクトル生成 | インデックス | 検索 | +|--------------|--------------|-------------|------| +| **Community** | LSA(Vibrato 分かち書き → SVD、50 次元) | `vec_items`(50 次元), `items_lsa` | HNSW + FTS5 ハイブリッド | +| **Pro** | 埋め込みモデル(ONNX、768 次元) | `vec_items`(768 次元) | HNSW + FTS5。モデル未ロード時は FTS のみ | -- **処理フロー**: テキスト入力に対し、分かち書き(Vibrato) -> 単語出現頻度計算 -> SVD 射影 の工程を経てベクトルを生成。 -- **メリット**: 低スペック PC でも動作可能、GPU 不要、モデルファイル管理不要。 -- **一貫性**: アプリケーション本体のライフサイクルと完全に一致し、サイドカーの起動待ちやゾンビプロセスのリスクがありません。 +- **共通**: 全文検索は FTS5(trigram)。テキストと FTS/ベクトルの同期は起動時・`GET /heal` で実施。 -## 3. 各レイヤーの役割分担 +## 4. レイヤーと責務 -| レイヤー | 主要コンポーネント | 役割と責務 | -| :--- | :--- | :--- | -| **Presentation** | フロントエンド (`src/frontend`) | 設定状況の表示、アクティビティログ(SSE)の可視化、システム操作。 | -| **Application** | Rust コア (`src/backend`) | ビジネスロジック。MCP 要求のパース、LSA 解析、DB 操作のオーケストレーション。 | -| **Infrastructure** | SQLite (`telos.db`) | データの永続化、ベクトル空間の管理。 | +| レイヤー | 主なコンポーネント | 役割 | +|----------|-------------------|------| +| **Presentation** | `src/frontend`(Vanilla JS/CSS) | 検索・文書管理・設定・アクティビティログ。Tauri invoke / listen。 | +| **Application** | `src/backend`(Rust) | MCP ハンドラ、LSA/埋め込み、DB オーケストレーション、トレイ。 | +| **Infrastructure** | SQLite(`telos.db`), vec0, FTS5 | 永続化・ベクトル検索・全文検索。 | + +詳細な開発時モノリシック化の経緯・KPI は **10_monolithic_dev.md** を参照。 diff --git a/docs/specification/03_database_specification.md b/docs/specification/03_database_specification.md index e1e3ae9..602ec2e 100644 --- a/docs/specification/03_database_specification.md +++ b/docs/specification/03_database_specification.md @@ -1,85 +1,68 @@ -# データベース・ハイブリッド検索仕様書 (Database & Hybrid Search Specification) +# データベース仕様 (Database Specification) -## 1. データベース設計思想 +## 1. 概要 -本システムでは、SQLite を単なるメタデータストレージとしてだけでなく、**「ベクトル検索エンジン」**および**「全文検索エンジン(FTS5)」**としても活用しています。これにより、意味的な類推(Vector)と厳密なキーワード一致(BM25)を両立させた、堅牢なハイブリッド検索エンジンを実現しています。 +SQLite をメタデータ・ベクトル・全文検索のいずれにも利用します。**vec_items** の次元数はエディションにより異なります(Community: 50、Pro: 768)。検索は FTS5 とベクトル検索のハイブリッドです。 -### 1.1 ハイブリッド検索の採用理由 - -データが少ない初期段階では LSA(Latent Semantic Analysis)による学習が不十分で、語彙の関連性を正しく導き出せない「コールドスタート」問題が発生します。これに対処するため、統計的な重み付けを行う **BM25 スコアリング** 可能な全文検索(FTS5)を併用しています。 - -- **Vector (LSA)**: 「意味」が似ているものを探す。データ量が増えるほど賢くなる。 -- **FTS5 (BM25)**: 「文字」が合っているものを探す。1件目のデータから正確に動作する。 -- **指標**: `Max(Vector Similarity, BM25 Score)` に基づくランキング。 - -## 2. エンティティ関係定義 (ERD) +## 2. エンティティとテーブル ```mermaid erDiagram - documents ||--o{ items : "Contains (1:N)" - items ||--|| vec_items : "Vector Index (1:1)" - items ||--|| items_lsa : "LSA Metadata (1:1)" - items ||--|| items_fts : "Full-text Index (1:1)" + documents ||--o{ items : "1:N" + items ||--|| vec_items : "1:1" + items ||--|| items_fts : "1:1 (FTS5)" + items ||--o| items_lsa : "1:1 (Community)" documents { - integer id PK "文書ID" - text path "出典・パス (Unique)" - text mime "MIMEタイプ" - datetime created_at "作成日" - datetime updated_at "更新日" + int id PK + text path UK + text mime + datetime created_at + datetime updated_at } items { - integer id PK "チャンクID" - integer document_id FK "documents.id 参照" - integer chunk_index "チャンク順番" - text content "テキスト本体" - datetime created_at "作成日" - datetime updated_at "更新日" + int id PK + int document_id FK + int chunk_index + text content + datetime created_at + datetime updated_at } vec_items { - integer id PK "items.id と紐付け" - blob embedding "50次元ベクトルデータ(f32)" + int id PK "items.id" + blob embedding "50d(Community) or 768d(Pro)" } items_fts { - integer rowid PK "items.id と紐付け" - text content "全文検索用インデックス" + rowid PK + text content "FTS5 trigram" + } + items_lsa { + int id PK "Community: LSA 用メタデータ" + blob 等 } ``` -## 3. ハイブリッド検索アルゴリズム +- **documents**: 文書単位のメタデータ(path, mime)。 +- **items**: チャンク。同一 document_id で結合すると文書全文。 +- **vec_items**: vec0 拡張の仮想テーブル。Community は 50 次元、Pro は 768 次元。 +- **items_fts**: FTS5(trigram)。両エディションで共通。 +- **items_lsa**: Community 版の LSA 用中間データ。Pro では未使用。 -### 3.1 `search_text` のロジック +## 3. ハイブリッド検索 -検索クエリが入力されると、システムは以下の **2系統の検索** を並列または逐次実行して結果をマージします。 +1. **ベクトル検索**: クエリを LSA(Community)または埋め込みモデル(Pro)でベクトル化し、vec_items 上で類似度検索(HNSW または vec0 MATCH)。類似度は 0〜1 に正規化。 +2. **全文検索**: FTS5 の bm25 等でスコア算出し、同様に 0〜1 に変換。 +3. **マージ**: 各チャンク ID について、両スコアの大きい方を採用し、降順でランキング。必要に応じて文書単位にまとめ(`group_by_document`)。 -1. **セマンティック検索 (Vector)**: - - クエリを LSA Engine で 50 次元のベクトルに射影。 - - `vec_items` から L2 距離が近い順に取得し、`1.0 - (distance / 2.0)` で類似度を算出。 -2. **統計キーワード検索 (FTS5/BM25)**: - - クエリを `trigram` トークナイザー(3文字単位)で分解。 - - SQLite FTS5 の `bm25()` 関数を用いて、文書内の重要度を算出。 - - BM25 スコアを `(1.0 - tanh(score/10))` 等で 0-1 の類似度に変換。 +- ベクトルが使えない場合(Pro でモデル未ロード等)は FTS のみで検索可能。 -**最終スコア**: 各 ID ごとに 2 つのスコアのうち高い方(Max)を採用し、降順でランキングします。 +## 4. 同期・ヒーリング -## 4. テーブル詳細 +- **起動時**: items に対応する items_fts / vec_items(および Community の items_lsa)の不足を補完。 +- **手動**: `GET /heal` で FTS の不足行を同期。Pro の vec_items 不足は RE-INDEX(lsa_retrain)で補完。 -### 4.1 `items` (チャンク管理) +## 5. 内部管理 -文書を分割したテキストを保持。すべての検索インデックスのソースとなります。 +- **internal_metadata**: スキーマバージョン等。マイグレーションで参照。 -### 4.2 `vec_items` (ベクトル演算用仮想テーブル) - -`sqlite-vec` による仮想テーブル。LSA エンジンが生成した特徴量を保持します。 - -### 4.3 `items_fts` (全文検索用仮想テーブル) - -SQLite `FTS5` 拡張による仮想テーブル。`tokenize='trigram'` を指定することで、日本語のわかち書きに依存しない強力な部分一致・統計検索をサポートします。 - -### 4.4 `items_lsa` (LSAメタデータ) - -LSA の学習・推論に使用する中間データ(特徴量 blob)を保持します。 - -### 4.5 `internal_metadata` (内部管理テーブル) - -システムバージョン(`0.3.2`)等を保持し、スキーマの互換性を管理します。 +Pro のベクトル化・ANN の詳細は **07_embedding_tract.md**・**11_pro_vectorization_and_ann.md** を参照。 diff --git a/docs/specification/04_mcp_api_specification.md b/docs/specification/04_mcp_api_specification.md index ec869b4..0bff3cd 100644 --- a/docs/specification/04_mcp_api_specification.md +++ b/docs/specification/04_mcp_api_specification.md @@ -1,68 +1,47 @@ -# MCP・APIインターフェース仕様書 (MCP & API Specification) +# MCP・API 仕様 (MCP & API Specification) -## 1. プロトコル設計思想 +## 1. トランスポート -本システムは、Anthropic が提唱する **Model Context Protocol (MCP)** に準拠しています。これにより、IDE(Cursor 等)や外部エージェントが、本システムを単なる「静的なドキュメント」としてではなく、対話可能な「インテリジェントな知識ベース」として認識できるようになります。 - -### 1.1 なぜ SSE (Server-Sent Events) なのか - -MCP には Stdio と SSE の 2 つのトランスポートがありますが、本システムでは **SSE** を採用しています。 - -- **UIとの親和性**: GUI を持つデスクトップアプリでは、Stdio は標準入出力の競合が発生しやすいため、HTTP ベースの SSE が適しています。 -- **多重接続**: 同時に複数のエージェント(例:IDE と ブラウザ拡張)からの要求を並行して処理することが可能です。 - -## 2. インターフェース定義 - -- **ポート**: 3001 (固定) +- **方式**: Model Context Protocol に準拠。**SSE (Server-Sent Events)** で接続維持・イベント配信。 +- **ポート**: 3001(固定) - **エンドポイント**: - - **Connection**: `GET /sse` (接続維持・通知受取) - - **Message**: `POST /messages?session_id={uuid}` (コマンド送信) + - `GET /sse` — 接続確立・通知受信 + - `POST /messages?session_id={uuid}` — JSON-RPC リクエスト送信 -## 3. 提供ツール (Capabilities) +## 2. 提供ツール一覧 -本システムのリソースへアクセスするための具体的なコマンド群です。 +| ツール名 | 役割 | +|----------|------| +| `search_text` | 意味・全文ハイブリッド検索。結果は文書単位で結合可能。 | +| `add_item_text` | テキストをチャンクとして登録。ベクトル化はエディションに応じて LSA または埋め込み。 | +| `update_item` | 指定 ID のチャンクを更新。ベクトル再計算。 | +| `delete_item` | 指定 ID のチャンクを削除。 | +| `get_item_by_id` | 指定 ID のメタデータ・本文を取得。 | +| `get_document_count` | 文書(documents)の総件数。 | +| `lsa_retrain` | RE-INDEX。FTS/ベクトルを再構築(Community: LSA 再学習、Pro: vec_items 再投入・HNSW 再構築)。 | -| ツール名 | 役割 | 解説 | -| :--- | :--- | :--- | -| `add_item_text` | 文書の永続化 | テキストを LSA 解析によってベクトル化し DB へ保存。 | -| `search_text` | 意味検索 | 記述内容を LSA 解析し、類似度に基づいたベクトル検索を実行。FTS5 (BM25) とベクトル検索のハイブリッド。 | -| `update_item` | 知識の更新 | ID 指定による既存データの書き換え。 | -| `delete_item` | 知識の抹消 | ID 指定による物理削除。 | -| `get_item_by_id` | 生データ取得 | メタデータを含むレコードの直接参照。 | -| `get_document_count` | 件数取得 | DB に格納したドキュメント(documents)の総件数を返す。引数なし。 | - -### 3.1 search_text パラメータ +## 3. search_text パラメータ | パラメータ | 型 | 必須 | 既定値 | 説明 | -| :--- | :--- | :--- | :--- | :--- | -| `content` | string | ○ | — | 検索クエリ文字列。 | -| `limit` | integer | — | 10 | 返却する最大件数(1〜100)。 | -| `min_score` | number | — | 0.3 | 足切り閾値(0〜1)。この値未満の類似度の結果は返却しない。 | +|------------|-----|------|--------|------| +| `content` | string | ○ | — | 検索クエリ。短い語句・自然文どちらも可。 | +| `limit` | integer | — | 10 | 返却最大件数(1〜100)。 | +| `min_score` | number | — | 0.3 | 類似度の足切り(0〜1)。 | +| `group_by_document` | boolean | — | true | true のとき文書単位で結合して返す。 | -- **省略時**: `min_score` を省略した場合は **0.3** が適用される。 -- **スコア**: 各結果の `similarity` は 0〜1 に正規化され、ベクトル検索と FTS5 のスコアの大きい方が採用される。 -- **該当語句がない場合**: クエリのトークンが LSA 語彙に1つも存在しない場合はベクトル検索をスキップし、FTS5 の結果のみ返す(無関係なドキュメントに 1.0 が付くのを防ぐ)。 +- 結果の `similarity` は 0〜1。ベクトルと FTS のスコアの大きい方を採用。 -## 4. 通信シーケンスとレスポンス形式 +## 4. HTTP API(補助) -ツール呼び出しは JSON-RPC 2.0 に準拠しており、結果は常に MCP 規格の `content` 配列形式で返されます。 +| メソッド | パス | 説明 | +|----------|------|------| +| GET | `/edition` | 起動中エディション(`community` / `pro`)。 | +| GET | `/version` | アプリバージョン(例: `{"version":"0.3.2"}`)。 | +| GET | `/heal` | FTS 同期実行。`{"synced": n}`。 | +| GET | `/model_name` | Pro 時は埋め込みモデル名等。 | -```json -{ - "jsonrpc": "2.0", - "result": { - "content": [ - { - "type": "text", - "text": "[結果メッセージ]" - } - ] - }, - "id": "req-123" -} -``` +## 5. レスポンス形式 -## 5. 開発者向けデバッグ情報 +ツール呼び出しは JSON-RPC 2.0。結果は MCP の `content` 配列(`type: "text"`, `text` に JSON 文字列)で返します。 -- `/status` エンドポイントにアクセスすることで、現在の MCP セッション数やシステム負荷状況を確認できます。 -- エラーコード `-32000` が返された場合は、内部の **LSA Engine** の初期化不良(語彙データが空、またはメモリ不足)の可能性が高いです。ログを確認して LSA モデルが正しくロードされているか検証してください。 +エラー時は `-32000` 等。LSA/埋め込みの初期化失敗時はログでエンジン状態を確認してください。 diff --git a/docs/specification/05_development_guide.md b/docs/specification/05_development_guide.md index 5a02240..65173b3 100644 --- a/docs/specification/05_development_guide.md +++ b/docs/specification/05_development_guide.md @@ -1,47 +1,49 @@ -# TelosDB 開発ガイド (Development Guide) +# 開発ガイド (Development Guide) -## 日本語 (Japanese) +## 1. ディレクトリ構成 -本ドキュメントは、TelosDB エンジンのビルド、拡張、および内部構造に関する技術的なガイドラインを提供します。 +| パス | 内容 | +|------|------| +| `src/frontend/` | UI(Vanilla JS / CSS)。React/Vite は未使用。 | +| `src/backend/` | Rust / Tauri / Axum。MCP サーバー・LSA・埋め込みエンジン。 | +| `docs/specification/` | 本仕様書群。 | +| `.agent/rules/` | AI エージェント用プロジェクト運用ルール。 | -### 1. ディレクトリ構造 +## 2. ビルド・起動 -- `src/frontend/`: React/Vite ベースの UI コンポーネント。 -- `src/backend/`: Rust/Tauri ベースのコアエンジンおよび MCP サーバー。 -- `docs/specification/`: システム仕様書(DB、API、UIデザイン等)。 -- `.agent/rules/`: AI エージェント用プロジェクト運用ルール。 +### 開発時 -### 2. バックエンド開発 +- **Community**: `npm run dev`(Tauri 1 プロセスで 8474 + 3001) +- **Pro**: `npm run dev:pro`。`embedding_model/` に ONNX モデルと vocab を配置すること。 -- **データベース**: `sqlite-vec` を利用したベクトル検索および LSA による次元圧縮を実装しています。 -- **MCP サーバー**: `mcp.rs` にて外部ツール(Claude 等)向けのインターフェースを定義しています。 -- **次元圧縮 (LSA)**: `lsa.rs` にて SVD を用いたベクトルの次元圧縮を行い、検索の高速化と精度向上を実現しています。 +### 本番ビルド(2 エディション) -### 3. フロントエンド開発 +- **Community**: `npm run build:community` + - 設定: `src/backend/tauri.community.conf.json`(productName: TelosDB-Community、vec0.dll のみ同梱) +- **Pro**: `npm run build:pro` + - 設定: `src/backend/tauri.pro.conf.json`(productName: TelosDB-Pro、埋め込みモデル等を含む) -- **通信**: Tauri の `invoke` および `listen` を用いてバックエンドと非同期通信を行います。 -- **UI コンポーネント**: Vanilla CSS および一部の現代的な CSS 手法を用いた、プレミアムな UI デザインを採用しています。 +それぞれ別名のインストーラ(例: `TelosDB-Community_*.exe`, `TelosDB-Pro_*.exe`)が生成されます。 ---- +### Cargo feature -## English +- デフォルト: Community(LSA)。`cargo build` で LSA のみ。 +- Pro: `cargo build --no-default-features --features pro`。埋め込み・HNSW 等の Pro 用コードが含まれる。 -This document provides technical guidelines for building, extending, and understanding the internal structure of the TelosDB engine. +## 3. バックエンド開発 -### 1. Directory Structure +- **DB**: sqlite-vec(vec0)でベクトル、FTS5 で全文検索。次元は Community 50 / Pro 768。 +- **MCP**: `src/backend/src/mcp/mod.rs` でルーティング・ツール登録。 +- **LSA**: `lsa.rs` で SVD による 50 次元ベクトル化(Community)。 +- **埋め込み**: Pro は `utils/embedding_pro.rs` 等。ONNX(tract または ort)で 768 次元。詳細は **07_embedding_tract.md**。 -- `src/frontend/`: React/Vite-based UI components. -- `src/backend/`: Rust/Tauri-based core engine and MCP server. -- `docs/specification/`: System specifications (DB, API, UI design, etc.). -- `.agent/rules/`: Project operational rules for AI agents. +## 4. フロントエンド開発 -### 2. Backend Development +- **通信**: Tauri の `invoke` / `listen` でバックエンドと非同期通信。MCP は 3001 で別接続。 +- **UI**: Vanilla CSS。デザイン方針は **06_ui_design_spec.md**。 -- **Database**: Implements vector search using `sqlite-vec` and dimensionality reduction via LSA (Latent Semantic Analysis). -- **MCP Server**: Defines interfaces for external tools (e.g., Claude) in `mcp.rs`. -- **Dimensionality Reduction (LSA)**: Performs vector compression using SVD in `lsa.rs` to improve search speed and accuracy. +## 5. テスト・検証 -### 3. Frontend Development - -- **Communication**: Uses Tauri's `invoke` and `listen` for asynchronous communication with the backend. -- **UI Components**: Employs premium UI design utilizing Vanilla CSS and modern CSS techniques. +- ゴール・KPI: **08_embedding_tract_goals_and_kpi.md** +- 検証手順: **09_embedding_tract_implementation_and_tests.md** +- UI テスト方針: **12_ui_testing_options.md** diff --git a/docs/specification/06_ui_design_spec.md b/docs/specification/06_ui_design_spec.md index 2b3f249..a001153 100644 --- a/docs/specification/06_ui_design_spec.md +++ b/docs/specification/06_ui_design_spec.md @@ -1,56 +1,29 @@ -# UI デザイン仕様書 (UI Design Specification) +# UI デザイン仕様 (UI Design Specification) -## 1. コンセプト: High-Contrast Minimalism +## 1. コンセプト -TelosDB の UI は、装飾を極限まで削ぎ落とし、情報の「明瞭さ」と「速度感」を重視した **「ハイコントラスト・ミニマリズム」** をテーマにしています。 -以前の流行であったガラスモーフィズム(透過・ブラー)を廃し、ソリッドな背景と鋭いエッジ、洗練されたタイポグラフィによって、静謐でプロフェッショナルな道具としての佇まいを目指します。 +**ハイコントラスト・ミニマリズム**。装飾を抑え、情報の明瞭さと操作性を優先します。 -### 1.1 デザインの構成要素 +- **背景**: 深い黒(`#050505`)基調。区切りは細いグレーの境界線。 +- **タイポグラフィ**: Outfit / Inter 等のサンセリフ。 +- **アクセント**: 成功・エラー・ステータスなど機能的な意味のある箇所に限定。 -- **背景**: 漆黒(`#050505`)に近い深い黒を基調とし、要素を区別するために深いグレーのソリッドな面を使用。 -- **ボーダー**: 透過ブラーではなく、非常に細く(1px)彩度の低いグレースケールの境界線を採用。 -- **タイポグラフィ**: 幾何学的で現代的なサンセリフ体(Outfit)と、情報の視認性に特化したインターフェイスフォント(Inter)。 -- **アクセント**: 機能的な意味(成功、エラー、ステータス)を持つ要素にのみ、厳選されたアクセントカラーを適用。 +## 2. レイアウト -## 2. インタラクティブ・フィードバック +1. **ヘッダー**: ロゴ・**エディション表示**(Community / Pro バッジ)・モデル名(Pro 時)・文書件数・ステータス。 +2. **メイン**: 検索バー・検索結果リスト。 +3. **フッター**: 設定・RE-INDEX 等。バージョン表示(`/version` から取得、失敗時はフォールバック)。 -### 2.1 ステータス・インジケーター +## 3. 設定パネル -ヘッダー右側に配置されたバッジは、システムの状態をリアルタイムに示します。 +- **表示する項目**: 検索まわり(スコア足切り 0〜1、取得件数)のみ。保存は localStorage(キー `telosdb_settings`)。 +- **非表示**: ログイン時起動・フォルダモニタリングのパネルは UI に含めない(将来用のため実装は残す場合あり)。 -- `Running`: LSA エンジン(Vibrato 形態素解析 + ndarray 行列演算)および MCP サーバーが待機中または処理中。 -- `Stopped`: 重大な構成エラーまたはプロセス停止。 +## 4. ステータス・アクティビティ -### 2.2 アクティビティ・ログ (SSE Listener) +- **ステータス**: LSA/埋め込みエンジンおよび MCP の待機・処理状態(Running / Stopped 等)。 +- **アクティビティログ**: 検索・登録などのイベントを SSE で受信し、画面下部に表示。 -検索や登録のアクティビティは、画面下部のログセクションに垂直方向に流れます。 -ソリッドなデザインに合わせ、カードの浮き上がり(Shadow)ではなく、ミニマルなリスト形式で表示されます。 +## 5. 技術スタック -## 3. フロントエンド技術スタック - -- **Vanilla JS & CSS**: 高速な起動と低メモリフットプリントを維持。 -- **Desktop First**: Tauri 2 による Windows ネイティブの挙動(システムトレイ、イベントループ)に最適化。 - -## 4. レイアウト構造 - -1. **Header (HUD)**: 常に固定され、モデル名とデータ件数を表示(ヘッドアップディスプレイ)。 -2. **Main Content**: 検索バーと結果リスト。余白(Negative Space)を適切に取り、情報のノイズを排除。 -3. **Footer (Toolbar)**: 設定へのアクセスと再インデックスボタンを集約。 - -## 5. 設定パネルと永続化 - -サイドバーの「設定」から検索まわりの設定を変更できる。 - -### 5.1 検索設定項目 - -| 項目 | 説明 | 既定値 | -| :--- | :--- | :--- | -| スコア足切り (0〜1) | `min_score`。この値未満の類似度の結果は表示しない。 | 0.3 | -| 取得件数 | 検索で返す最大件数(`limit`)。 | 10 | - -### 5.2 設定値の保存先 - -- **保存先**: ブラウザ(WebView)の **localStorage**。 -- **キー**: `telosdb_settings`。 -- **形式**: JSON(例: `{"min_score":0.3,"limit":10}`)。 -- 「保存」ボタンで反映され、アプリ再起動後も保持される。設定未保存時は上記の既定値が使われる。 +- Vanilla JS & CSS。Tauri 2 で Windows ネイティブ(システムトレイ・イベントループ)。 diff --git a/docs/specification/07_embedding_tract.md b/docs/specification/07_embedding_tract.md new file mode 100644 index 0000000..fa8af88 --- /dev/null +++ b/docs/specification/07_embedding_tract.md @@ -0,0 +1,65 @@ +# Pro 埋め込みモデル仕様(ONNX / tract・ort) + +エディション概要は **01_system_overview.md**、アーキテクチャは **02_architecture_design.md** を参照。本文書は Pro 版の埋め込みまわりの技術仕様のみを扱う。 + +## 1. 目的 + +- Community は LSA(50 次元)。Pro は **ニューラル埋め込み**(768 次元)で検索精度を向上させる。 +- 同一プロセス・純 Rust(tract または ort)で動作し、配布をシンプルに保つ。 + +## 2. 採用方針 + +### 2.1 埋め込みモデル + +- **候補**: **sonoisa/sentence-bert-base-ja**(例: `sentence-bert-base-ja-mean-tokens-v2`) +- **理由**: 日本語特化でビジネス・学術文書に強い。paraphrase-multilingual より日本語のフォーマルな語彙に適している。 +- **仕様**: 出力 **768 次元**。モデルサイズ約 440MB のため、**量子化(FP16/INT8)** を検討する。 + +### 2.2 量子化 + +- 公式の量子化済み sonoisa は公開されていないため、**自前で量子化**する。 +- PyTorch / Hugging Face から ONNX にエクスポートし、**ONNX 形式で量子化**したモデルを配布またはビルド時に用意する。 + +### 2.3 実行系(Rust) + +- **推奨**: **tract** + - **純 Rust**。C++ 依存がなく、`cargo build` で完結する。 + - 同一プロセスで動作し、別プロセス起動は不要。 + - ONNX を読み込み可能。Rust との親和性が高い。 +- **代替**: **ort**(ONNX Runtime の Rust バインディング) + - ONNX Runtime の最適化をそのまま使えるが、ネイティブ lib の取得・リンクが必要。 + - 必要に応じて検討。 + +## 3. 技術メモ + +### 3.1 他方式との比較 + +| 方式 | 特徴 | +|------|------| +| **LSA**(現行) | 軽量・CPU のみ・学習データ不要。次元数は 50 などで調整可能。 | +| **LDA** | トピックモデル。検索の「意味の近さ」には LSA ほど直結しない。次元を増やしてもスパースになりがち。 | +| **ICA** | 独立性の最大化が目的で、意味検索には不向き。 | +| **Elasticsearch** | ベクトルは外部の埋め込みモデルで作成。GPU は一般的に使わず、多くは CPU 推論。 | + +### 3.2 モデル・次元・サイズ(参考) + +| モデル | 次元数 | パラメータ数 | ファイルサイズ(目安) | +|--------|--------|--------------|------------------------| +| paraphrase-multilingual-MiniLM-L12-v2 | 384 | 約 118M | 約 470 MB | +| sonoisa/sentence-bert-base-ja | 768 | 約 110M | 約 440 MB | + +### 3.3 ONNX + +- **ONNX (Open Neural Network Exchange)**: フレームワーク非依存のモデル形式。PyTorch 等で学習したモデルを ONNX にエクスポートし、ONNX Runtime や **tract** などで実行できる。 +- 量子化や配布がしやすく、Rust からは tract / ort で利用する。 + +## 4. 組み込み済み(Pro 版) + +- **モデル配置**: プロジェクト内 **`embedding_model/`** に `model_quantized.onnx` と `vocab.txt` をコピーして置く。README は `embedding_model/README.md` を参照。 +- **パス解決**: (1) 環境変数 `TELOS_EMBEDDING_MODEL_DIR`(上書き用)、(2) 配布ビルド時は `resource_dir/embedding_model`(同梱リソース)、(3) 開発時は exe からの相対 `../../embedding_model`。いずれも `model_quantized.onnx` の存在で有効とみなす。 +- **同梱**: `tauri.conf.json` の `bundle.resources` に上記2ファイルを登録。Pro ビルド前にファイルを置いておくとインストーラに含まれる。 +- **次元**: Pro 版は 768 次元。DB の vec_items は起動時に 768 次元で初期化される。 +- **検索・追加**: Pro ビルドでは埋め込みモデルでベクトルを計算し、HNSW と vec_items に保存する。 +- **既知の事象**: 量子化 ONNX を tract で読み込むと `into_optimized()` で Cast ノード失敗することがある。その場合は最適化スキップ(`TELOS_EMBEDDING_NO_OPTIMIZE=1` 等)で起動し、FTS のみでも検索可能。ort 利用時は出力 shape が `[1, 768]` の場合はそのまま採用(mean_pool 不要)。 + +動作確認・KPI は **08_embedding_tract_goals_and_kpi.md**・**09_embedding_tract_implementation_and_tests.md**・**11_pro_vectorization_and_ann.md** を参照。 diff --git a/docs/specification/08_embedding_tract_goals_and_kpi.md b/docs/specification/08_embedding_tract_goals_and_kpi.md new file mode 100644 index 0000000..719d43d --- /dev/null +++ b/docs/specification/08_embedding_tract_goals_and_kpi.md @@ -0,0 +1,83 @@ +# 埋め込み改修:ゴールとテスト KPI + +製品概要・アーキテクチャは **01_system_overview.md**・**02_architecture_design.md** を参照。本文書は改修ゴールとテスト KPI の定義のみ。 + +## 1. 改修のゴール(Goals) + +### 1.1 主ゴール + +| # | ゴール | 達成条件 | +|---|--------|----------| +| G1 | **エディション分離の成立** | Community 版と Pro 版が同一リポジトリから、ビルドフラグのみで別バイナリとしてビルド・配布できる。一方の実装が他方のバイナリに含まれない。 | +| G2 | **Pro 版の起動・検索の成立** | Pro 版が起動し、検索 API(FTS + 埋め込み可能時はベクトル)が動作する。埋め込みモデル未ロード時も FTS のみで検索可能である。 | +| G3 | **モデルのプロジェクト内組み込み** | 埋め込みモデル(ONNX + vocab)をプロジェクト内 `embedding_model/` に置き、開発・配布ビルドの両方で同じパス解決で参照できる。 | +| G4 | **GUI のエディション対応** | 起動中のエディションに応じて、ヘッダー表示・RE-INDEX 文言・ステータスが切り替わる。 | +| G5 | **検索の健全性** | 既存 DB や 1 文字クエリでも、FTS または LIKE フォールバックで検索結果が返る。items_fts と items の不整合は起動時・手動 heal で解消できる。 | +| G6 | **テスト・運用の自動化** | ヘッドレス起動と test-and-heal(heal → MCP テスト)が Pro/Community で実行可能。継続実行オプションで回し続けられる。 | +| G7 | **埋め込みモデルの確実なロード(テスト利用可能)** | Pro 起動時に埋め込みモデルがロード成功し、ベクトル化・近似近傍検索の動作確認とテストが行える状態にする。tract の最適化スキップや代替 ONNX などで Cast ノード問題を回避する。 | + +### 1.2 副次ゴール(既知の制約内) + +| # | ゴール | 備考 | +|---|--------|------| +| G8 | **既存機能の非劣化** | Community 版の LSA 学習・HNSW・検索・追加・更新は従来どおり動作する。 | + +--- + +## 2. テストにおける KPI(Key Performance Indicators) + +### 2.1 ビルド・起動 + +| KPI | 指標 | 合格基準 | 測定方法 | +|-----|------|----------|----------| +| K1 | Community ビルド成功 | `cargo build`(または `tauri build`)が警告のみで完了する | `cargo build --manifest-path src/backend/Cargo.toml` | +| K2 | Pro ビルド成功 | `cargo build --no-default-features --features pro` が警告のみで完了する | 上記 + feature pro | +| K3 | Community 起動 | ウィンドウが開き、ログに `[BOOT] Edition: community` が出る | 手動 or launch「Community で起動」 | +| K4 | Pro 起動 | ウィンドウが開き、ログに `[BOOT] Edition: pro` が出る | 手動 or launch「Pro で起動」 | +| K5 | ヘッドレス起動 | `TELOS_HEADLESS=1` でウィンドウ非表示のまま MCP が 3001 で待ち受ける | `npm run test:headless` が MCP 待機まで完了 | + +### 2.2 API・検索 + +| KPI | 指標 | 合格基準 | 測定方法 | +|-----|------|----------|----------| +| K6 | MCP ツール一覧 | `POST /messages` で `tools/list` が 10 件前後のツールを返す | `tests/test_mcp_client.mjs` の tools/list 成功 | +| K7 | 検索が 200 を返す | `search_text` が JSON-RPC として成功し、`content` 配列が返る(0 件でも可) | test_mcp_client の search_text が例外にならない | +| K8 | Heal API | `GET /heal` が 200 で `{ "synced": n }` を返す | test-and-heal 内の heal 呼び出し成功 | +| K9 | エディション API | `GET /edition` が `community` または `pro` を返す | 起動したバイナリの feature と一致 | + +### 2.3 データ整合・ヒール + +| KPI | 指標 | 合格基準 | 測定方法 | +|-----|------|----------|----------| +| K10 | 起動時 FTS 同期 | items に存在し items_fts に無い行が起動時に挿入される | 既存 DB で検索が 0 件だったケースが heal 後または再起動で解消 | +| K11 | 手動 Heal 効果 | `GET /heal` 実行後、不足していた FTS 行が検索に反映される | heal 前後で同一クエリのヒット件数が増える(不足があった場合) | + +### 2.4 回帰・非劣化 + +| KPI | 指標 | 合格基準 | 測定方法 | +|-----|------|----------|----------| +| K12 | Community 検索 | LSA 学習完了後、検索がベクトル+FTS で結果を返す | Community 起動 → 文書追加 → 検索でヒット | +| K13 | Rust 単体テスト | 既存の `cargo test` が通る | `npm run test:rust`(db, lsa, mcp の tests) | + +### 2.5 運用・CI 寄り + +| KPI | 指標 | 合格基準 | 測定方法 | +|-----|------|----------|----------| +| K14 | test-and-heal 1 回成功 | `npm run test-and-heal:pro` が exit 0 で終わる | スクリプト実行。MCP 未起動時はヘッドレスで Pro 起動 → heal → test | +| K15 | 継続実行の安定性 | `test-and-heal:pro:continuous` が複数サイクル(例: 2 回)heal → test を繰り返す | 手動で 2 サイクル以上実行し、いずれも test が完了する | + +### 2.6 Pro 埋め込み・ベクトル化・ANN(G7 達成のための KPI) + +| KPI | 指標 | 合格基準 | 測定方法 | +|-----|------|----------|----------| +| K16 | 埋め込みモデルロード成功 | Pro 起動時に `embedding_model` が Some となり、ログにモデル読み込み成功が出る(またはエラーが出ない) | Pro 起動 → ログ確認。または GET /model_name で pro/sonoisa 系の名前が返る | +| K17 | ベクトル化: vec_items 投入 | add_item_text で 1 件追加後、vec_items に 1 行・768 次元が入る | DB: `SELECT COUNT(*), vec_to_json(embedding) FROM vec_items` で確認 | +| K18 | 起動時 HNSW 構築(Pro) | vec_items にデータがある状態で Pro 再起動後、HNSW が構築される | ログに `[BOOT] Pro HNSW: inserting N items (768d)...` および `index built.` | +| K19 | ANN 検索でベクトルヒット | 追加した文書に意味的に近いクエリで search_text を実行し、その文書が結果に含まれ類似度が付与される | MCP search_text で content 配列に該当文書が含まれる。optional: 複数文書で類似度順が妥当 | + +--- + +## 3. まとめ + +- **ゴール**: エディション分離・Pro 起動・モデル組み込み・GUI 対応・検索健全性・テスト自動化・**埋め込み確実ロード(G7)** を主目標とし、既存機能の非劣化(G8)を副次目標とする。 +- **KPI**: ビルド/起動(K1–K5)、API/検索(K6–K9)、データ整合/ヒール(K10–K11)、回帰(K12–K13)、運用(K14–K15)、**Pro 埋め込み・ベクトル化・ANN(K16–K19)** の 19 項目を合格基準とし、CI または手順に沿ったテストで測定する。 diff --git a/docs/specification/09_embedding_tract_implementation_and_tests.md b/docs/specification/09_embedding_tract_implementation_and_tests.md new file mode 100644 index 0000000..4ab469e --- /dev/null +++ b/docs/specification/09_embedding_tract_implementation_and_tests.md @@ -0,0 +1,246 @@ +# KPI 達成のための検証手順 + +- ゴール・KPI は **08_embedding_tract_goals_and_kpi.md** に定義済み。 +- 本文書は**テスト実行順・合格判定・手順**のみ。改修の多くは実装済み。 + +--- + +## Phase 1:ビルド・起動(K1–K5) + +### 1.1 改修タスク + +| KPI | 現状 | 不足改修 | 担当 | +|-----|------|----------|------| +| K1 | Community ビルドは Cargo feature で実装済み | なし | — | +| K2 | Pro ビルドは `--no-default-features --features pro` で実装済み | なし | — | +| K3 | launch「Community で起動」あり。ログに Edition 出力済み | なし | — | +| K4 | launch「Pro で起動」あり | なし | — | +| K5 | `test:headless` で TELOS_HEADLESS=1 起動。spawn は shell:true で対応済み | なし | — | + +**Phase 1 の追加改修**: 特になし。必要なら「K1–K2 のみ検証する npm スクリプト」を追加可。 + +### 1.2 テスト手順 + +1. **K1** + `cd src/backend && cargo build --manifest-path Cargo.toml` + → 警告のみで完了すれば合格。 +2. **K2** + `cargo build --no-default-features --features pro --manifest-path src/backend/Cargo.toml` + → 同様。 +3. **K3** + launch「TelosDB: Community で起動 (tauri dev)」で F5。ログに `[BOOT] Edition: community` を確認。 +4. **K4** + launch「TelosDB: Pro で起動 (tauri dev)」で F5。ログに `[BOOT] Edition: pro` を確認。 +5. **K5** + 既存の 8474/3001 使用プロセスを終了したうえで + `npm run test:headless` + → MCP が 3001 で待ち受け、テストが開始されれば合格(途中で EADDRINUSE の場合はポート解放後に再実行)。 + +### 1.3 合格判定 + +- K1–K2: ビルド exit 0。 +- K3–K4: ログに該当 Edition が 1 回以上出力。 +- K5: test:headless が MCP 待機まで完了(その後の MCP テスト失敗は Phase 2 で扱う)。 + +--- + +## Phase 2:API・検索(K6–K9) + +### 2.1 改修タスク + +| KPI | 現状 | 不足改修 | 担当 | +|-----|------|----------|------| +| K6 | tools/list は MCP で実装済み | なし | — | +| K7 | search_text は FTS+LIKE フォールバック実装済み | なし | — | +| K8 | GET /heal と heal_items_fts 実装済み | なし | — | +| K9 | GET /edition と AppState.edition 実装済み | なし | — | + +**Phase 2 の追加改修**: 特になし。 + +### 2.2 テスト手順 + +- **K6–K7** + MCP を起動した状態で + `node tests/test_mcp_client.mjs` + → tools/list が 10 件前後、search_text が例外なく content 配列を返せば合格。 +- **K8** + `curl -s http://127.0.0.1:3001/heal` + → 200 かつ `{"synced":n}` 形式なら合格。 +- **K9** + Community 起動時: `curl -s http://127.0.0.1:3001/edition` → `"edition":"community"`。 + Pro 起動時: 同様に `"edition":"pro"` を確認。 + +### 2.3 合格判定 + +- K6: test_mcp_client の「Found N tools」が 10 前後。 +- K7: search_text で例外なし・content 配列が返る(0 件可)。 +- K8: GET /heal が 200 で JSON の synced が存在。 +- K9: GET /edition が起動したエディションと一致。 + +--- + +## Phase 3:データ整合・ヒール(K10–K11) + +### 3.1 改修タスク + +| KPI | 現状 | 不足改修 | 担当 | +|-----|------|----------|------| +| K10 | init_schema 内で sync_items_fts 呼び出し済み | なし | — | +| K11 | GET /heal で heal_items_fts 実行済み | なし | — | + +**Phase 3 の追加改修**: 特になし。必要なら「items のみ存在する DB コピー」で検証用データを用意する。 + +### 3.2 テスト手順 + +- **K10** + 1) items にのみ行がある状態の DB を用意(または items_fts を手動で一部削除した状態を作る)。 + 2) アプリを再起動。 + 3) ログに `[db] items_fts: synced N rows`(N>0)が出る、または検索でヒットするようになることを確認。 +- **K11** + 1) items に存在するが items_fts に無い id がある状態を用意。 + 2) `GET /heal` を 1 回実行。 + 3) 同一クエリで検索し、heal 後にヒット件数が増える(または 0→1 以上になる)ことを確認。 + +### 3.3 合格判定 + +- K10: 起動後、不足 FTS が同期されるか検索結果に反映される。 +- K11: heal 実行後、該当クエリのヒットが増える(または 0 件から 1 件以上になる)。 + +--- + +## Phase 4:回帰・非劣化(K12–K13) + +### 4.1 改修タスク + +| KPI | 現状 | 不足改修 | 担当 | +|-----|------|----------|------| +| K12 | Community の LSA・HNSW・検索は feature 分岐で維持済み | なし | — | +| K13 | db, lsa, mcp の #[cfg(test)] が存在 | なし | — | + +**Phase 4 の追加改修**: 特になし。 + +### 4.2 テスト手順 + +- **K12** + Community で起動 → 文書を 1 件以上追加(GUI または MCP)→ LSA 学習完了を待つ → 検索でヒットすることを確認。 +- **K13** + `npm run test:rust` + → すべてのテストがパスすれば合格。 + +### 4.3 合格判定 + +- K12: Community で「追加 → 検索ヒット」が再現できる。 +- K13: `cargo test` が exit 0。 + +--- + +## Phase 5:運用・CI 寄り(K14–K15) + +### 5.1 改修タスク + +| KPI | 現状 | 不足改修 | 担当 | +|-----|------|----------|------| +| K14 | test-and-heal:pro(--pro)で Pro 起動 → heal → test 実装済み | なし | — | +| K15 | --continuous で繰り返し実行実装済み | なし | — | + +**Phase 5 の追加改修**: 特になし。必要なら「2 サイクルで自動停止」するオプションを追加(例: `--cycles 2`)。 + +### 5.2 テスト手順 + +- **K14** + 8474/3001 を占有しているプロセスを終了したうえで + `npm run test-and-heal:pro` + → exit 0 なら合格。 +- **K15** + `npm run test-and-heal:pro:continuous` + → 2 回以上「Heal → MCP test」が回ったあと、手動で Ctrl+C。両サイクルで test が完了していれば合格。 + +### 5.3 合格判定 + +- K14: test-and-heal:pro が exit 0。 +- K15: continuous で 2 サイクル以上、いずれも test 完了。 + +--- + +## Phase 6:Pro 埋め込みロード・ベクトル化・ANN(K16–K19 / G7) + +Pro エディションでドキュメントのベクトル化と近似近傍検索が動作するようにする。前提: 埋め込みモデルを確実にロードし、テストに使えるようにする。 + +### 6.1 改修タスク + +| KPI | 改修 | 内容 | 優先度 | +|-----|------|------|--------| +| K16 | **M1: 埋め込みロードの安定化** | **実装済み**: (a) 環境変数 `TELOS_EMBEDDING_NO_OPTIMIZE=1` のとき `into_typed().into_runnable()` でロード(最適化スキップ)。(b) 最適化失敗時(エラーに "Cast" / "optim" / "optimize" 含む)は自動で同経路にフォールバック。これで量子化 ONNX の Cast ノードでもモデルを確実にロードしてテストに利用可能。 | 必須 | +| K17–K19 | **M2: テスト用スタブ埋め込み(任意)** | 環境変数 `TELOS_EMBEDDING_STUB=1` のとき、実モデルではなく固定 768 次元ベクトルを返すスタブを使う。モデルが読めない環境でも「ベクトル化 → vec_items → HNSW → 検索」のパイプラインをテスト可能。 | 推奨 | +| — | **M3: ベクトル化・ANN 利用のログ** | add_item で vec_items に挿入したときに `[pro] vec_items inserted id={}`。検索で HNSW または vec_items を利用したときに 1 行ログ。動作確認の証跡用。 | 推奨 | + +### 6.2 テスト手順(モデルがロードできる場合: M1 完了後) + +| # | テスト | 手順 | 合格基準 | 対応 KPI | +|---|--------|------|----------|----------| +| T1 | ベクトル化: 1 件追加 | Pro 起動 → add_item_text で 1 件追加 → DB で `SELECT COUNT(*) FROM vec_items` が 1 増え、該当 id で `vec_to_json(embedding)` が 768 要素 | vec_items に 1 行・768 次元 | K17 | +| T2 | 起動時 HNSW | 上記のあと Pro を再起動。ログに `[BOOT] Pro HNSW: inserting 1 items (768d)...` および `[BOOT] Pro HNSW: index built.` | ログで HNSW 構築確認 | K18 | +| T3 | ANN: 検索でベクトルヒット | 追加した文書に近いクエリで search_text を実行。結果の content にその文書が含まれ、similarity が付与される | 意味的に近い文書がヒットし類似度が妥当 | K19 | +| T4 | ANN: 複数文書で順序 | 複数文書を追加し、ある 1 文書と意味的に近いクエリで検索。その文書が上位に来る | 類似度順のランキングが妥当 | K19 | + +### 6.3 テスト手順(スタブ利用時: M2 完了後) + +| # | テスト | 手順 | 合格基準 | 対応 KPI | +|---|--------|------|----------|----------| +| T5 | スタブで vec_items 投入 | `TELOS_EMBEDDING_STUB=1` で Pro 起動 → add_item_text で 1 件追加。vec_items に 1 行・768 次元が入る | vec_items に 1 行・768 次元(スタブ) | K17 | +| T6 | スタブで HNSW 検索 | 続けて search_text を実行。HNSW または vec_items でベクトル検索が動き、該当 id が結果に含まれる | ベクトル経路でヒット 1 件以上 | K19 | + +### 6.4 合格判定 + +- **K16**: Pro 起動後、ログに埋め込みロード成功(またはエラーなし)。必要なら GET /model_name で pro/sonoisa 系が返る。 +- **K17**: T1 または T5 で vec_items に 1 行・768 次元が入る。 +- **K18**: T2 で再起動後ログに Pro HNSW 構築が出る。 +- **K19**: T3 または T4 または T6 で検索結果にベクトル由来のヒットが含まれる。 + +--- + +## 一括実行用チェックリスト(推奨順) + +実行順に並べたチェックリスト。CI や手動検証で使用する。 + +| # | 実施内容 | 合格目安 | 対応 KPI | +|---|----------|----------|----------| +| 1 | `npm run test:rust` | exit 0 | K13 | +| 2 | Community ビルド(`cargo build` in src/backend) | exit 0 | K1 | +| 3 | Pro ビルド(`cargo build --no-default-features --features pro`) | exit 0 | K2 | +| 4 | launch「Community で起動」→ ログで Edition: community 確認 | ログに 1 行 | K3 | +| 5 | launch「Pro で起動」→ ログで Edition: pro 確認 | ログに 1 行 | K4 | +| 6 | ポート 8474/3001 解放 → `npm run test:headless` | MCP 待機まで完了 | K5 | +| 7 | MCP 起動中に `node tests/test_mcp_client.mjs` | tools/list 成功・search_text 例外なし | K6, K7 | +| 8 | `curl -s http://127.0.0.1:3001/heal` | 200 + JSON | K8 | +| 9 | `curl -s http://127.0.0.1:3001/edition`(Community/Pro 各 1 回) | edition 一致 | K9 | +| 10 | 起動時 FTS 同期の確認(必要なら DB をわざと不整合にして再起動) | ログまたは検索で同期確認 | K10 | +| 11 | 手動 heal 効果の確認(必要なら不整合 DB で GET /heal 前後で検索) | ヒット増加 | K11 | +| 12 | Community 起動 → 文書追加 → 検索ヒット確認 | ヒット 1 件以上 | K12 | +| 13 | `npm run test-and-heal:pro` | exit 0 | K14 | +| 14 | `npm run test-and-heal:pro:continuous` を 2 サイクル以上実行 | 両サイクル test 完了 | K15 | +| 15 | Pro 起動 → ログで埋め込みモデルロード成功確認(または /model_name で pro 系) | ログにエラーなし or model_name が pro 系 | K16 | +| 16 | Pro で add_item_text 1 件 → vec_items に 1 行・768 次元が入ることを DB で確認 | vec_items COUNT 増加・embedding 768 次元 | K17 | +| 17 | vec_items にデータがある状態で Pro 再起動 → ログで HNSW 構築確認 | `Pro HNSW: inserting N items (768d)...` 等 | K18 | +| 18 | Pro で文書追加後、意味的に近いクエリで search_text → 該当文書がヒットし類似度付与 | 検索結果に該当 content・similarity | K19 | + +--- + +## オプション:KPI 検証用 npm スクリプト + +自動化できる範囲だけを 1 本のスクリプトにまとめる場合の例。 + +- **案 A** + `npm run test:kpi` → 順に `test:rust` → Community/Pro の `cargo build` → `test:headless`(または test-and-heal:pro)を実行し、いずれか失敗で exit 1。 +- **案 B** + `test:kpi` は `test:rust` + `test-and-heal:pro` のみ(MCP 未起動ならヘッドレスで Pro 起動してから heal → test)。K1–K2 は別途 `npm run build:community` / `npm run build:pro` で確認。 + +必要に応じて `tools/run-kpi-check.mjs` などを追加し、上記チェックリストの 1, 2, 3, 6, 13 をスクリプト化する。 + +--- + +## まとめ + +- **改修**: Phase 1–5 は既存実装で検証可能。**Phase 6(Pro 埋め込み・ベクトル化・ANN)** では **M1(埋め込みロード安定化)** が必須。M2(スタブ)・M3(ログ)は推奨。 +- **テスト計画**: Phase 1 → 2 → 3 → 4 → 5 のあと、**Phase 6** で M1 完了後に T1–T4(実機)、M2 完了時は T5–T6(スタブ)を実施。 +- **一括確認**: チェックリスト 1–14 に加え、15–18 で K16–K19 を確認。モデルを確実にロードしてテストに使えるようにするため、M1 の実施を優先する。 diff --git a/docs/specification/10_monolithic_dev.md b/docs/specification/10_monolithic_dev.md new file mode 100644 index 0000000..6ad00e2 --- /dev/null +++ b/docs/specification/10_monolithic_dev.md @@ -0,0 +1,24 @@ +# モノリシック化(開発時)— 経緯と判断 + +**現状のアーキテクチャは 02_architecture_design.md に記載済み。** 本文書は「なぜ 1 プロセスにしたか」の経緯と実装判断の記録。 + +## 目的 + +- 開発時も **1 プロセス(Tauri のみ)** で完結させ、beforeDevCommand(Node のフロント用サーバー)を廃止する。 +- **効果**: 起動がシンプル。フロント 8474 と MCP 3001 は同一 Tauri プロセス内で listen。Node 不要。 + +## 主な判断 + +| 項目 | 判断 | +|------|------| +| ポート | フロント 8474 / MCP 3001 のまま(役割分離を維持)。 | +| 静的配信 | Axum の `ServeDir` で `src/frontend` を 8474 で配信。 | +| beforeDevCommand | 廃止。`tauri dev` は Cargo ビルドのみ実行。 | +| 本番 | 変更なし。フロント同梱は従来どおり。 | + +## 実装メモ + +- フロントパス: 開発時は `CARGO_MANIFEST_DIR` 等から `src/frontend` を解決。 +- 3001 の listen は LSA/HNSW(または Pro のバックフィル)完了後。8474 は起動直後に listen 可。 + +KPI・テスト一覧は **08_embedding_tract_goals_and_kpi.md**・**09_embedding_tract_implementation_and_tests.md** を参照。 diff --git a/docs/specification/11_pro_vectorization_and_ann.md b/docs/specification/11_pro_vectorization_and_ann.md new file mode 100644 index 0000000..6a8b4de --- /dev/null +++ b/docs/specification/11_pro_vectorization_and_ann.md @@ -0,0 +1,27 @@ +# Pro ベクトル化・ANN — 動作確認と既知の制約 + +データベース・検索の仕様は **03_database_specification.md**、埋め込みの技術仕様は **07_embedding_tract.md** を参照。本文書は **動作確認チェックリスト** と **既知の制約・改修メモ** のみ。 + +## 1. 動作確認チェックリスト(モデルロード成功時) + +| # | 確認項目 | 合格基準 | +|---|----------|----------| +| T1 | 1 件追加で vec_items に 768 次元が入る | `add_item_text` 後、`SELECT COUNT(*), vec_to_json(embedding) FROM vec_items` で 1 行・768 要素 | +| T2 | 起動時 HNSW 構築 | Pro 再起動後ログに `[BOOT] Pro HNSW: ... index built.` | +| T3 | 検索でベクトルヒット | 追加文書に近いクエリで `search_text` がその文書を返し、類似度が付与される | +| T4 | 複数文書で類似度順 | 意味的に近い文書が上位に来る | + +## 2. 既知の制約・対処 + +| 事象 | 対処 | +|------|------| +| tract の `into_optimized()` で Cast 失敗 | `TELOS_EMBEDDING_NO_OPTIMIZE=1` で最適化スキップ。または ort 使用。 | +| モデル未ロード時 | FTS のみで検索可能。ベクトル化・ANN は動作しない。 | +| ONNX 出力が `[1, 768]` | ort 利用時は mean_pool 不要でそのまま採用(実装済み)。 | + +## 3. 任意の改修メモ + +- **update_item 時の HNSW**: 現状は vec_items のみ更新。HNSW は次回起動または RE-INDEX で再構築。必要なら 1 件更新を検討。 +- **スタブ埋め込み**: モデルなしでパイプラインだけ検証する場合は `TELOS_EMBEDDING_STUB=1` 相当のスタブを検討。 + +KPI 定義は **08_embedding_tract_goals_and_kpi.md**(K16–K19)、検証手順は **09_embedding_tract_implementation_and_tests.md** を参照。 diff --git a/docs/specification/12_ui_testing_options.md b/docs/specification/12_ui_testing_options.md new file mode 100644 index 0000000..ad741a6 --- /dev/null +++ b/docs/specification/12_ui_testing_options.md @@ -0,0 +1,81 @@ +# UI をテストに組み込む方法 + +UI 仕様は **06_ui_design_spec.md**、アーキテクチャは **02_architecture_design.md** を参照。**結論: 可能です。** 主に次の 2 通り。 + +--- + +## 1. Tauri 公式の E2E(WebDriver + tauri-driver) + +アプリ全体(ウィンドウ + WebView)を起動し、WebDriver で操作・アサーションする方法です。 + +### 概要 + +- **tauri-driver**: Tauri アプリを WebDriver 対応で起動するラッパー。 +- **対応**: Windows(Edge Driver)、Linux(WebKitWebDriver)。**macOS はデスクトップ非対応**(WKWebView 用ドライバが無いため)。 +- **テストランナー例**: Selenium、WebdriverIO など。 + +### 用意するもの + +| 環境 | 必要なもの | +|------|------------| +| **Windows** | [Microsoft Edge Driver](https://developer.microsoft.com/en-us/microsoft-edge/tools/webdriver/)(Edge のバージョンと一致させる)。`msedgedriver` を PATH に通すか、`tauri-driver --native-driver ` で指定。 | +| **Linux** | `WebKitWebDriver`(例: Debian 系なら `webkit2gtk-driver`)。CI では `xvfb` で仮想ディスプレイが必要な場合あり。 | + +```bash +cargo install tauri-driver --locked +``` + +### 実行イメージ + +1. `tauri-driver` でアプリを WebDriver モードで起動。 +2. テストコード(Selenium / WebdriverIO 等)で接続し、要素の取得・クリック・入力・表示内容のアサーションを行う。 + +公式例: [WebDriver Example](https://github.com/tauri-apps/webdriver-example)、[Tauri - WebDriver](https://v2.tauri.app/develop/tests/webdriver/)、[CI の例](https://v2.tauri.app/develop/tests/webdriver/ci/)。 + +### このプロジェクトでの実装(実施済み) + +- **WebdriverIO** を devDependency に追加。ルートに `wdio.conf.js` を配置。 +- **npm スクリプト**: `npm run test:e2e`(ビルド + tauri-driver 起動 + E2E 実行)、`npm run build:e2e`(E2E 用 debug ビルドのみ)。 +- **スペック**: `e2e-tests/specs/app.spec.js` でタイトル・ヘッダー・検索 UI を検証。 +- **前提**: `cargo install tauri-driver --locked` と、Windows では Edge Driver(msedgedriver)を PATH に通す。詳細は `e2e-tests/README.md` を参照。 + +--- + +## 2. フロントのみの UI テスト(Playwright 等) + +Tauri のウィンドウは使わず、**フロントの HTML/JS だけ**をブラウザで開いてテストする方法です。 + +### 概要 + +- 開発サーバ(`tauri dev` の 8474 配信)またはビルド済み静的ファイルをローカルサーバで配信し、その URL に Playwright などで接続。 +- クリック・入力・表示チェックを Playwright で実行。 +- **利点**: 環境依存が少ない。macOS でも実行しやすい。CI も組みやすい。 +- **注意**: Tauri のネイティブ API(`@tauri-apps/api`)はブラウザからは使えないため、**Tauri 連携部分はモックするか、E2E(1)で補う**必要があります。 + +### このプロジェクトでやる場合 + +- `playwright` を devDependency に追加。 +- テスト用に簡易 HTTP サーバで `src/frontend` または配布用資材を配信。 +- 検索ボックス・ボタン・一覧表示など、**DOM とイベントだけに依存する部分**を Playwright でテスト。 +- バックエンド(MCP API)は既存の `test_mcp_client.mjs` のように別テストでカバーし、UI テストではモック or スタブにするか、実際に MCP を立てて合わせて動かす構成も可能。 + +--- + +## 3. 現状との関係 + +| 種別 | 現状 | UI 組み込みのイメージ | +|------|------|------------------------| +| **API テスト** | `tests/test_mcp_client.mjs` で MCP (3001) を叩いている | そのまま利用。UI テストは「画面操作の結果、同じ API が呼ばれ結果が表示される」を検証するのに使える。 | +| **ヘッドレス起動** | `TELOS_HEADLESS=1` でウィンドウ非表示 | WebDriver でテストする場合はウィンドウは表示される(または CI では仮想ディスプレイ)。ヘッドレスは主に API 専用テスト用。 | +| **CI** | `test:headless` / `test-and-heal:pro` で MCP テスト | ここに「UI テスト」ステップを追加可能。1 の場合は Windows/Linux のみ・Driver 準備が必要。2 の場合は Playwright を入れてフロントのみの UI テストを追加する形。 | + +--- + +## まとめ + +- **UI をテストに組み込むことは可能**です。 +- **アプリ全体をそのままテストしたい** → **WebDriver + tauri-driver**(Windows/Linux。Driver の準備が必要)。 +- **フロントの見た目・操作だけを手軽にテストしたい** → **Playwright 等でフロントのみ**(Tauri API はモック or 別途 E2E)。 +- 既存の MCP テストはそのまま活かしつつ、上記のいずれか(または両方)を追加する形で UI をテストに組み込めます。 + +必要なら、選んだ方式(1 か 2)に合わせて、具体的な `package.json` のスクリプトとテストファイルのひな形を追加できます。 diff --git a/e2e-tests/README.md b/e2e-tests/README.md new file mode 100644 index 0000000..762d732 --- /dev/null +++ b/e2e-tests/README.md @@ -0,0 +1,61 @@ +# E2E テスト(WebDriver + tauri-driver) + +TelosDB の UI を WebDriver で操作し、表示・動作を検証します。 + +## 前提 + +1. **tauri-driver のインストール** + ```bash + cargo install tauri-driver --locked + ``` + +2. **プラットフォーム別ドライバ** + - **Windows**: `edgedriver` が devDependency に含まれており、初回実行時に Edge のバージョンに合わせて Edge Driver を自動ダウンロードする。手動で用意する場合は [Microsoft Edge Driver](https://developer.microsoft.com/en-us/microsoft-edge/tools/webdriver/) を PATH に通すか、`TELOS_EDGE_DRIVER` にバイナリのパスを指定する。 + - **Linux**: `WebKitWebDriver` をインストール(例: Debian 系では `sudo apt install webkit2gtk-driver`)。CI では仮想ディスプレイ用に `xvfb` が必要な場合あり。 + - **macOS**: デスクトップ向け WebDriver は非対応のため、この E2E は Windows / Linux で実行すること。 + +## 実行 + +プロジェクトルートで: + +```bash +# 依存のインストール(未実施なら) +npm install + +# E2E 用にビルドしてからテスト実行(初回はビルドに時間がかかります) +npm run test:e2e +``` + +事前にビルドだけしたい場合: + +```bash +npm run build:e2e +``` + +その後、バイナリのパスを環境変数で渡してテストのみ実行する例: + +```bash +# Windows でルートに target がある場合の例 +set TELOS_E2E_APP=D:\develop\TelosDB\target\debug\app.exe +npm run test:e2e +``` + +## スペック + +- `e2e-tests/specs/app.spec.js` … ウィンドウタイトル、ヘッダー、検索 UI の表示と検索実行の確認。 + +## 検索の可否をログで確認する + +バックエンドは検索実行時に次のログを出す(`RUST_LOG=info` で表示)。 + +- `[search] query="..." limit=N min_score=X` … 検索が開始された +- `[search] FTS hits=N` … FTS5 のヒット件数 +- `[search] Pro vector hits=N` / `[search] Community LSA vector hits=N` … ベクトル検索のヒット件数(該当する方のみ) +- `[search] result_count=N ok=true|false` … 最終結果件数と成否 + +手動でアプリを起動して検索するときは、ターミナルの stderr に上記が出る。E2E でアプリを tauri-driver 経由で起動している場合は、アプリの stderr がそのまま見えないことがあるため、検索の可否は E2E の「検索がヒットする」spec の成否と、必要なら `TELOS_LOG_FILE=log.txt` などでログをファイルに出して確認する。 + +## 注意 + +- テスト実行中は TelosDB のウィンドウが開きます。操作は WebDriver が行うため、手で触らないでください。 +- MCP サーバ(3001)がアプリ起動時に立ち上がるため、ポートが他プロセスで占有されていないことを確認してください。 diff --git a/e2e-tests/specs/app.spec.js b/e2e-tests/specs/app.spec.js new file mode 100644 index 0000000..bea15ef --- /dev/null +++ b/e2e-tests/specs/app.spec.js @@ -0,0 +1,79 @@ +/** + * TelosDB の E2E スペック(WebDriver + tauri-driver)。 + * ウィンドウが開き、ヘッダー・検索 UI が表示され、検索が実際にヒットすることを検証する。 + */ + +const API_BASE = 'http://127.0.0.1:3001'; +const E2E_SEARCH_PHRASE = 'E2E検索テスト用の文書'; +const E2E_DOC_CONTENT = `${E2E_SEARCH_PHRASE}です。この文字列でヒットすることを確認する。`; + +async function waitForMcp(maxAttempts = 15, intervalMs = 500) { + for (let i = 0; i < maxAttempts; i++) { + try { + const res = await fetch(`${API_BASE}/edition`); + if (res.ok) return; + } catch (_) {} + await new Promise((r) => setTimeout(r, intervalMs)); + } + throw new Error('MCP (3001) did not become ready in time'); +} + +async function addDocumentViaMcp(content, path) { + const res = await fetch(`${API_BASE}/messages`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + jsonrpc: '2.0', + method: 'tools/call', + params: { + name: 'add_item_text', + arguments: { content, path }, + }, + id: Date.now(), + }), + }); + const data = await res.json(); + if (data.error) throw new Error(data.error.message || JSON.stringify(data.error)); + return data; +} + +describe('TelosDB', () => { + it('ウィンドウのタイトルが TelosDB である', async () => { + const title = await browser.getTitle(); + expect(title).toMatch(/TelosDB/i); + }); + + it('ヘッダーに TelosDB のロゴ・テキストが表示される', async () => { + const logo = await $('.site-header .logo-mark'); + await expect(logo).toBeDisplayed(); + await expect(logo).toHaveText('TelosDB'); + }); + + it('検索入力と検索ボタンが表示される', async () => { + const searchInput = await $('#query'); + const searchBtn = await $('.search-btn'); + await expect(searchInput).toBeDisplayed(); + await expect(searchBtn).toBeDisplayed(); + await expect(searchBtn).toHaveText('検索'); + }); + + it('検索がヒットする(テスト用文書を登録し、その文言で検索して結果が返る)', async () => { + await waitForMcp(); + await addDocumentViaMcp(E2E_DOC_CONTENT, 'e2e-test-search-doc.txt'); + await browser.pause(2500); + + const searchInput = await $('#query'); + const searchBtn = await $('.search-btn'); + const resultPanel = await $('#result'); + await searchInput.setValue(E2E_SEARCH_PHRASE); + await searchBtn.click(); + await browser.pause(2500); + + await expect(resultPanel).toBeDisplayed(); + const resultCards = await $$('.result-card'); + expect(resultCards.length).toBeGreaterThanOrEqual(1); + + const firstCardText = await resultCards[0].getText(); + expect(firstCardText).toContain(E2E_SEARCH_PHRASE); + }); +}); diff --git a/embedding_model/README.md b/embedding_model/README.md new file mode 100644 index 0000000..e31ba6f --- /dev/null +++ b/embedding_model/README.md @@ -0,0 +1,13 @@ +# Pro 版埋め込みモデル (sonoisa) + +このフォルダに次のファイルを**コピー**して配置してください。 + +- **model_quantized.onnx** … 量子化済み sentence-bert-base-ja-mean-tokens-v2 モデル +- **vocab.txt** … BERT 用語彙ファイル + +- **開発時**: プロジェクトルートの `embedding_model/` を参照します(`npm run dev:pro`)。 +- **配布ビルド時**: Pro ビルド前に上記2ファイルを置いておくと、**インストーラに同梱**されます。利用者は別途ダウンロード不要で、Pro 版をインストールするだけでモデル付きで利用できます。未配置のまま `npm run build:pro` すると `ensure-embedding-model.mjs` が失敗します。 +- **モデルがロードできない場合**: まず tract でロードを試み、失敗時は **ONNX Runtime (ort)** に自動フォールバックします。量子化 ONNX で tract の Cast ノードが失敗する場合も、ort で推論可能です。明示的に tract の最適化だけスキップしたいときは環境変数 `TELOS_EMBEDDING_NO_OPTIMIZE=1` を指定してください。 + +取得元(量子化済み): [sentence-bert-base-ja-mean-tokens-v2-int8](https://gitbucket.tmworks.club/dtmoyaji/sentence-bert-base-ja-mean-tokens-v2-int8) +元モデル: [sonoisa/sentence-bert-base-ja-mean-tokens-v2](https://huggingface.co/sonoisa/sentence-bert-base-ja-mean-tokens-v2) (CC BY-SA 4.0) diff --git a/journals/20260225-0001-MCP-doc-count-auto-retrain-GUI.md b/journals/20260225-0001-MCP-doc-count-auto-retrain-GUI.md deleted file mode 100644 index 02657ff..0000000 --- a/journals/20260225-0001-MCP-doc-count-auto-retrain-GUI.md +++ /dev/null @@ -1,49 +0,0 @@ -# 2026-02-25: MCP件数・自動再学習・GUI改善 - -## 1. 作業実施の理由と指示 - -- **背景**: Issue #5 として MCP から格納件数を取得したい要望があり、当初は items(チャンク)数だったがドキュメント数に統一するよう指摘された。また、MCP ACTIVITY が空になる不具合、トレイクリックでウィンドウが一瞬開いて閉じる事象、追加・更新・削除のたびに手動 RE-INDEX が必要な点、インデックス化の進行が分からない点、文書一覧で内容が想像しづらい点、サイドバー下部の不要なアコーディオン・著作権表示が指摘された。 -- **観点**: 件数は「ドキュメント数」で MCP・HTTP・UI を統一する。LSA 再学習は変更がたまったタイミングで自動実行し、状態は GUI に表示する。UI は必要な情報を表示し、冗長な要素を削除する。 -- **意図**: get_document_count の追加と doc_count の documents 統一、MCP 呼び出しの SSE ブロードキャスト、トレイのデバウンス、LSA 自動再学習(閾値・90秒デバウンス)、インデックス状態のヘッダー表示、文書一覧にチャンク0の先頭15文字表示、サイドバー下部の整理を行う。 - -## 2. 指摘事項とその対応 - -- **指摘**: 格納件数は「ドキュメント数」にすべき。items 数では「X docs」と一致しない。 - - **対応**: MCP ツールを `get_document_count` に改名し、`SELECT COUNT(*) FROM documents` で返す。HTTP `/doc_count` と UI も同様に documents 件数に統一した。 -- **指摘**: MCP ACTIVITY が常に空である。 - - **対応**: `mcp_messages_handler` 内で `tools/call` 受信時に `state.tx.send("mcp:call:")` を送信するようにした。 -- **指摘**: トレイクリックでウィンドウが一瞬開いてすぐ閉じる。 - - **対応**: 左クリックに 400ms デバウンスを導入し、二重発火を無視するようにした。 -- **指摘**: 文書追加・更新・削除のたびに手動で RE-INDEX するのは手間。毎回再学習はコストが高い。 - - **対応**: 変更件数が「登録ドキュメント数の 20%」と「5 件」の少ない方を超えたら、90 秒デバウンス後に自動で `train_lsa_and_sync_hnsw` を実行する `schedule_retrain_if_needed` を実装し、add_item_text・update_item・delete_item・delete_document の成功後に呼び出すようにした。 -- **指摘**: インデックス化の様子が GUI で分からない。 - - **対応**: `AppState` に `indexing_status` を追加し、`/indexing_status` API と SSE の `indexing:training` / `indexing:syncing` / `indexing:idle` でヘッダーに表示するようにした。 -- **指摘**: 文書一覧で内容が想像しづらい。サイドバー下部のアコーディオン・著作権表示は不要。 - - **対応**: list_documents に chunk0 の先頭15文字(chunk0_preview)を追加し、文書管理テーブルに表示。app-sidebar から sidebar-bottom(アコーディオン・著作権)を削除した。 - -## 3. 作業詳細 - -AIエージェントは、次の変更を実施した。 - -- **MCP・HTTP・仕様**: `get_item_count` を `get_document_count` に変更し、`SELECT COUNT(*) FROM documents` で件数を返す。`tools/list`・dispatch・`handle_get_document_count` を追加・改名。`handlers.rs` の `doc_count_handler` を documents に変更。`04_mcp_api_specification.md` に get_document_count を追記。 -- **MCP ACTIVITY**: `mcp/mod.rs` の `mcp_messages_handler` で、`tools/call` および該当ツール実行直後に `state.tx.send(format!("mcp:call:{}", actual_method))` を実行。 -- **トレイ**: `lib.rs` の `on_tray_icon_event` で左クリック時に AtomicU64 で前回クリック時刻を記録し、400ms 以内のクリックを無視。それ以外で表示/非表示をトグル。 -- **LSA 自動再学習**: `AppState` に `changes_since_train`(AtomicU64)と `retrain_scheduled`(AtomicBool)を追加。`system.rs` に `schedule_retrain_if_needed` を実装(閾値 `max(1, min(ceil(doc_count*0.2), 5))`、90秒デバウンス、spawn で train_lsa_and_sync_hnsw)。add_item_text・update_item・delete_item・delete_document の成功後に呼び出し。 -- **インデックス状態表示**: `AppState` に `indexing_status`(RwLock)を追加。`train_lsa_and_sync_hnsw` の開始・sync 前・完了・失敗・件数0 で `indexing_status` と SSE を更新。`GET /indexing_status` を追加。フロントのヘッダーにバッジを追加し、3秒ポーリングと SSE の `indexing:*` で更新。 -- **文書一覧**: `list_documents` の SQL に chunk0 の先頭15文字のサブクエリを追加し、返却に `chunk0_preview` を含める。main-panel の文書管理テーブルに「先頭(chunk0)」列を追加。`.docs-cell-preview` でスタイルを指定。 -- **サイドバー**: `app-sidebar.js` から `sidebar-bottom`(TelosDB アコーディオン・著作権表示)を削除。 - -```mermaid -flowchart LR - A[get_document_count / doc_count] --> B[documents 統一] - C[MCP tools/call] --> D[SSE mcp:call] - E[変更検知] --> F[schedule_retrain_if_needed] - F --> G[90s デバウンス] - G --> H[train_lsa_and_sync_hnsw] - H --> I[indexing_status / SSE] - I --> J[ヘッダー表示] -``` - -## 4. AI視点での結果 - -MCP と UI の「X docs」がドキュメント数で一致し、MCP ACTIVITY にツール呼び出しが表示されるようになった。トレイクリックでウィンドウの表示・非表示が安定して切り替わる。変更が閾値を超えたタイミングで 90 秒後に LSA が自動再学習され、ヘッダーで training / syncing / idle が確認できる。文書一覧でチャンク0の冒頭が把握でき、サイドバーはナビのみに整理された。 diff --git "a/journals/20260227-0001-\350\250\255\345\256\232\346\260\270\347\266\232\345\214\226\343\201\250Issue\345\220\214\346\234\237\343\203\273\344\277\235\345\255\230\343\203\220\343\202\260\344\277\256\346\255\243.md" "b/journals/20260227-0001-\350\250\255\345\256\232\346\260\270\347\266\232\345\214\226\343\201\250Issue\345\220\214\346\234\237\343\203\273\344\277\235\345\255\230\343\203\220\343\202\260\344\277\256\346\255\243.md" deleted file mode 100644 index 5d156aa..0000000 --- "a/journals/20260227-0001-\350\250\255\345\256\232\346\260\270\347\266\232\345\214\226\343\201\250Issue\345\220\214\346\234\237\343\203\273\344\277\235\345\255\230\343\203\220\343\202\260\344\277\256\346\255\243.md" +++ /dev/null @@ -1,31 +0,0 @@ -# 2026-02-27: 設定永続化・Issue同期・保存バグ修正 - -## 1. 作業実施の理由と指示 - -- **背景**: デバッグ起動時に「OSにログインしたときに自動で起動する」チェックが永続化しない報告、サーバーコンソールに操作時の受信メッセージを出したい要望があった。また、GitBucket の Issue を API で改訂(Issue 9 を Issue 4 の後に対応する旨を追記)したいが PATCH が 404 になる事象、および設定保存ボタンがファイル保存の成否を無視して成功表示するバグの指摘があった。 -- **意図**: 設定の読み書きを Tauri invoke 優先にし永続化を確実にする。サーバー側で GET/POST /settings と MCP 受信時にログを出す。GitBucket の Issue 編集 API を調査し、未実装ならコメント投稿で代替する。設定保存ハンドラで `saveSettingsToFile` の戻り値を確認し、失敗時は成功表示・localStorage 更新を行わないようにする。 - -## 2. 指摘事項とその対応 - -- **指摘**: チェックが永続化しない。 - - **対応**: フロントの `loadSettingsFromFile` / `saveSettingsToFile` で、Tauri 内では先に `invoke('get_app_settings')` / `invoke('set_app_settings', { settings })` を呼ぶように変更。MCP サーバー起動前でもアプリデータの settings.json を直接読み書きできるため永続化が確実になった。 -- **指摘**: サーバーコンソールに操作時の受信メッセージを出したい。 - - **対応**: `handlers.rs` で GET /settings に `[server] GET /settings 受信`、POST /settings に payload 付きでログを追加。`mcp/mod.rs` で MCP 受信時に `[server] MCP 受信: ` を出力。 -- **指摘**: GitBucket の Issue を API で改訂したいが PATCH で 404。 - - **対応**: 外部調査の結果、公式 GitBucket の `ApiIssueControllerBase.scala` には「Edit an issue」の PATCH ルートが存在せず、全バージョンで未実装と判断。`sync_issues.mjs` で PATCH が 404 のときに **Create an issue comment** API で本文をコメントとして投稿するフォールバックを実装。Issue 9 に「Issue 4 の後に対応」をコメントで反映できるようにした。 -- **指摘**: 「多くのバージョンが未対応」は不正確では。 - - **対応**: 公式ソースで PATCH が未実装であることを確認し、`sync_issues.mjs` と `.agent/rules/issue_management.md` の記述を「公式 GitBucket では全バージョンで未実装」に修正。 -- **指摘**: 設定保存ボタンが `saveSettingsToFile` の戻り値を無視し、失敗時も成功表示・localStorage 更新をしている。 - - **対応**: 保存ハンドラで `const saved = await saveSettingsToFile(payload)` の戻り値を確認。`saved === true` のときのみ localStorage 更新・フォーム反映・「保存しました」表示。`saved === false` のときは「保存に失敗しました(ファイルへの書き込みに失敗しました)」を表示し、localStorage とフォームは変更しないようにした。 - -## 3. 作業詳細 - -- **main-panel.js**: `loadSettingsFromFile` で Tauri 時は `invoke('get_app_settings')` を優先、失敗時のみ fetch。`saveSettingsToFile` で Tauri 時は `invoke('set_app_settings', { settings: payload })` を優先。保存ボタン内で `saved` を判定し、成功時のみ localStorage・フォーム・成功メッセージを更新。 -- **handlers.rs**: `settings_get_handler` 冒頭に `[server] GET /settings 受信`、`settings_post_handler` 冒頭に payload のログを追加。 -- **mcp/mod.rs**: MCP 受信ログを `[server] MCP 受信: ...` に統一。 -- **sync_issues.mjs**: `postIssueComment(issueNumber, commentBody)` を追加。`updateRemoteIssue` で PATCH が 404 の場合に本文をコメントとして投稿。コメント投稿時は `_commentFallback` を返し、ローカル上書きは行わない。コメント・ドキュメントを「公式 GitBucket では全バージョンで未実装」に修正。 -- **issue_management.md**: Issue 編集と 404 の説明を「全バージョンで未実装」に変更(日英両方)。 - -## 4. AI視点での結果 - -設定は Tauri 起動直後から invoke で読み書きされるため、デバッグ時もチェックが永続化する。サーバーコンソールで設定・MCP の受信内容を確認しやすくなった。GitBucket では Issue 本体の編集 API が使えないため、同期時に本文をコメントとして投稿する運用で情報を残せる。設定保存でファイル書き込みに失敗した場合は、誤って「保存しました」と表示されず、ユーザーに失敗が伝わるようになった。 diff --git "a/journals/202603-010-\347\254\25410\351\200\261\343\202\242\343\203\274\343\202\253\343\202\244\343\203\226.md" "b/journals/202603-010-\347\254\25410\351\200\261\343\202\242\343\203\274\343\202\253\343\202\244\343\203\226.md" new file mode 100644 index 0000000..3378190 --- /dev/null +++ "b/journals/202603-010-\347\254\25410\351\200\261\343\202\242\343\203\274\343\202\253\343\202\244\343\203\226.md" @@ -0,0 +1,104 @@ +# 2026年第010週 (2/25-3/7) 作業アーカイブ: MCP・GUI・KPI計画・エディション・検索強化 + +## 週全体のサマリー + +2月25日から3月7日にかけ、MCP 件数・自動再学習・GUI 改善、設定永続化、ブートログ・開発起動の安定化、LLM サイドカー削除、エディション振り分け(Community/Pro)、KPI 計画の Phase1–5 実行と総括、および Pro 版の近似近傍検索・文書結合表示・README 更新まで実施した。 + +```mermaid +gantt + title 第010週 作業俯瞰 (2026-02-25 〜 2026-03-07) + dateFormat YYYY-MM-DD + section MCP・GUI + doc_count統一・自動再学習・indexing_status :2026-02-25, 1d + 設定永続化・ポート除外・保存バグ修正 :2026-02-27, 1d + section 起動・整理 + ブートログ充実・開発起動安定化 :2026-03-04, 1d + LLMサイドカー・llama_status削除 :2026-03-05, 1d + section エディション・KPI + エディション振り分け導入 :2026-03-05, 1d + KPI計画 Phase1-5 実行・総括 :2026-03-07, 1d +``` + +--- + +## 2026-02-25: MCP 件数・自動再学習・GUI 改善 + +- **背景**: get_document_count への統一、MCP ACTIVITY 空問題、トレイ二重クリック、手動 RE-INDEX 負担、インデックス状態の可視化、文書一覧のプレビュー、サイドバー下部の整理。 +- **対応**: doc_count を documents に統一。tools/call 受信時に SSE ブロードキャスト。トレイ 400ms デバウンス。変更件数閾値+90秒デバウンスで自動再学習。indexing_status と GET /indexing_status、ヘッダーバッジ追加。list_documents に chunk0_preview。サイドバー下部(アコーディオン・著作権)削除。 + +--- + +## 2026-02-27: 設定永続化と Issue 同期・保存バグ修正 + +- 設定の永続化、ポート除外問題の解決、保存バグの修正を実施。 + +--- + +## 2026-03-04: ブートログと開発起動の改善 + +- **背景**: 起動ログが簡素、開発時にフロントが ERR_CONNECTION_TIMED_OUT になる事象。 +- **対応**: ブート各フェーズに経過 ms を出力。devUrl と beforeDevCommand でフロントサーバーを先に起動し、応答を待ってからウィンドウを開くように変更。.agent/rules に「コミットする前にジャーナルを書くこと」を追加。 + +--- + +## 2026-03-04: ポート除外問題の解決と設定保存修正 + +- ポート除外まわりの修正と設定保存の修正を実施。 + +--- + +## 2026-03-05: LLM サイドカーと llama_status の削除 + +- 未使用の Gemma パス・サイドカー子プロセス・llama_status を削除。ヘッダー表示を indexing_status に統一。 + +--- + +## 2026-03-05: エディション振り分けの導入 + +- Community 版(LSA)と Pro 版(埋め込みモデル)のエディション振り分けを導入。ビルド・起動・検索パスを feature で分離。 + +--- + +## 2026-03-07: KPI 計画 Phase1 – Rust テストとビルド検証 + +- K13(Rust 単体テスト)・K1(Community ビルド)・K2(Pro ビルド)を実施。search_api 統合テストに #[ignore] を付与し、MCP 未起動時も cargo test がパスするようにした。 + +--- + +## 2026-03-07: KPI 計画 Phase1 – 起動とヘッドレステスト + +- K3–K5: Community/Pro 起動・ヘッドレス起動を検証。run-headless-test.mjs の spawn を shell:true に修正し、Windows で npx が解決されるようにした。 + +--- + +## 2026-03-07: KPI 計画 Phase2 – API と検索検証 + +- K6–K9: tools/list・search_text・GET /heal・GET /edition を test:headless / test-and-heal:pro で検証。 + +--- + +## 2026-03-07: KPI 計画 Phase3–5 – データ整合と回帰運用 + +- K10–K11 起動時 FTS 同期・手動 Heal。K12 Community 検索回帰なし。K14–K15 test-and-heal:pro および continuous で検証。 + +--- + +## 2026-03-07: KPI 計画実行完了と総括 + +- 全 15 KPI の合格を確認。search_api の #[ignore]、run-headless-test.mjs の shell:true を実施。今後の確認事項を記載。 + +--- + +## 集約元ファイル(アーカイブ後に削除) + +- 20260225-0001-MCP-doc-count-auto-retrain-GUI.md +- 20260227-0001-設定永続化とIssue同期・保存バグ修正.md +- 20260304-0001-ブートログと開発起動の改善.md +- 20260304-0002-ポート除外問題の解決と設定保存修正.md +- 20260305-0001-LLMサイドカーとllama_statusの削除.md +- 20260305-0002-エディション振り分けの導入.md +- 20260307-0001-KPI計画Phase1-Rustテストとビルド検証.md +- 20260307-0002-KPI計画Phase1-起動とヘッドレステスト.md +- 20260307-0003-KPI計画Phase2-APIと検索検証.md +- 20260307-0004-KPI計画Phase3-5-データ整合と回帰運用.md +- 20260307-0005-KPI計画実行完了と総括.md diff --git "a/journals/20260304-0001-\343\203\226\343\203\274\343\203\210\343\203\255\343\202\260\343\201\250\351\226\213\347\231\272\350\265\267\345\213\225\343\201\256\346\224\271\345\226\204.md" "b/journals/20260304-0001-\343\203\226\343\203\274\343\203\210\343\203\255\343\202\260\343\201\250\351\226\213\347\231\272\350\265\267\345\213\225\343\201\256\346\224\271\345\226\204.md" deleted file mode 100644 index 58a279b..0000000 --- "a/journals/20260304-0001-\343\203\226\343\203\274\343\203\210\343\203\255\343\202\260\343\201\250\351\226\213\347\231\272\350\265\267\345\213\225\343\201\256\346\224\271\345\226\204.md" +++ /dev/null @@ -1,40 +0,0 @@ -# 2026-03-04: ブートログの充実と開発起動の安定化 - -## 1. 作業の理由と意図 - -- **背景**: 起動時のログが簡素で、どこで時間がかかっているか分かりにくかった。また開発時(`npx tauri dev`)にウィンドウが「127.0.0.1 の応答に時間がかかりすぎました」(ERR_CONNECTION_TIMED_OUT)となり、フロントが表示されない事象があった。 -- **意図**: ブートシーケンスにフェーズ名と経過時間を出して観察しやすくする。開発時はフロント用サーバーを先に起動し、応答を待ってからウィンドウを開くようにしてタイムアウトを防ぐ。 - -## 2. 実施した変更 - -### ブートログの充実(lib.rs, mcp/mod.rs, mcp/system.rs) - -- **lib.rs**: 起動開始時に `Instant` を取得し、各フェーズで経過ミリ秒を出力。 - - `[BOOT] Phase 1: Paths resolved (Xms)` - - `[BOOT] Phase 2: vec0.dll ready (Xms)` - - `[BOOT] Phase 3: Tray icon ready (Xms)` - - `[BOOT] Phase 4: Initializing database...` → `Phase 4: Database ready (LSA-mode) (Xms)` - - `[BOOT] Phase 5: Starting MCP server on 127.0.0.1:3001 ...` - - `[BOOT] Setup complete (Xms). Window ready; MCP and LSA/HNSW continue in background.` -- **mcp/mod.rs**: `[BOOT] MCP: run_server started`、`LSA/HNSW background task spawned (Xms)`、`Listening on 127.0.0.1:3001 (Xms) — ready for connections`。MCP 受信ログを「MCP 受信」から「MCP receive」に変更(ログ文字化け防止)。 -- **mcp/system.rs**: LSA/HNSW の各段階に `[BOOT] LSA:` / `[BOOT] HNSW:` と経過時間を付与。`LSA+HNSW: all done (Xms)` を追加。 - -### 開発起動の安定化(tauri.conf.json, package.json) - -- **tauri.conf.json**: `build` に `devUrl: "http://127.0.0.1:1474"` と `beforeDevCommand: "npm run dev:frontend"` を追加。 -- **package.json**: `dev:frontend` スクリプト(`npx --yes serve src/frontend -l 1474`)を追加。 -- これにより、Tauri が先にフロント用サーバーを起動し、1474 に応答があるまで待ってから `cargo run` でウィンドウを開くため、ウィンドウ表示時の接続タイムアウトを回避。 - -### ルール追加 - -- **.agent/rules/documents.md**: 「5. コミットする前にジャーナルを書くこと。」を追加。 - -## 3. 観察結果(起動ログ例) - -- Tauri Setup: 約 92ms でウィンドウ準備完了。 -- MCP: 約 1208ms で 127.0.0.1:3001 で待ち受け開始。 -- LSA(20 文書): 約 9722ms。HNSW 含め LSA+HNSW 完了は約 9725ms。 - -## 4. その他 - -- `model_path` 未使用の警告は未修正(意図的な残置のため)。 diff --git "a/journals/20260304-0002-\343\203\235\343\203\274\343\203\210\351\231\244\345\244\226\345\225\217\351\241\214\343\201\256\350\247\243\346\261\272\343\201\250\350\250\255\345\256\232\344\277\235\345\255\230\344\277\256\346\255\243.md" "b/journals/20260304-0002-\343\203\235\343\203\274\343\203\210\351\231\244\345\244\226\345\225\217\351\241\214\343\201\256\350\247\243\346\261\272\343\201\250\350\250\255\345\256\232\344\277\235\345\255\230\344\277\256\346\255\243.md" deleted file mode 100644 index d05a72c..0000000 --- "a/journals/20260304-0002-\343\203\235\343\203\274\343\203\210\351\231\244\345\244\226\345\225\217\351\241\214\343\201\256\350\247\243\346\261\272\343\201\250\350\250\255\345\256\232\344\277\235\345\255\230\344\277\256\346\255\243.md" +++ /dev/null @@ -1,26 +0,0 @@ -# 20260304-0002 ポート除外問題の解決と設定保存修正 - -## 概要 -開発起動時の黒画面・ERR_CONNECTION_RESET を根本解決し、設定保存の limit フィールド欠落を修正。 - -## 変更内容 - -### 1. dev サーバーポート問題の解決 -- **根本原因**: Windows の Hyper-V/WSL2 が TCP ポート 1374-1473 を除外。Tauri 内蔵 dev サーバーが選択するポート 1474 付近でも TCP ハンドシェイクが完了しなかった。 -- **診断方法**: 複数ポートでの接続テスト、`netstat` の SYN_RECEIVED/SYN_SENT 状態確認、`netsh interface ipv4 show excludedportrange` で除外範囲を特定。 -- **修正**: `tools/serve-frontend.mjs` を作成しポート 8474 で `src/frontend/` を配信。`tauri.conf.json` に `devUrl` と `beforeDevCommand` を設定。 - -### 2. 設定保存条件の limit フィールド追加 -- `lib.rs` と `handlers.rs` の `to_write` 判定条件に `limit` フィールドを追加。 -- limit のみ更新した場合にフォールバック処理に流れて保存されない問題を修正。 - -### 3. package.json 整理 -- 不要になった `dev:frontend` スクリプトを削除。 - -## 影響ファイル -- `tools/serve-frontend.mjs` (新規) -- `src/backend/tauri.conf.json` -- `src/backend/src/lib.rs` -- `src/backend/src/mcp/handlers.rs` -- `package.json` -- `.gitignore` diff --git a/package-lock.json b/package-lock.json index f29d9c1..2228110 100644 --- a/package-lock.json +++ b/package-lock.json @@ -23,11 +23,39 @@ "devDependencies": { "@types/react": "^18.2.21", "@types/react-dom": "^18.2.7", + "@wdio/cli": "^9.19.0", + "@wdio/local-runner": "^9.19.0", + "@wdio/mocha-framework": "^9.19.0", + "@wdio/spec-reporter": "^9.19.0", + "edgedriver": "^6.3.0", "prettier": "^3.8.1", "typescript": "^5.2.2", "vite": "^6.0.0" } }, + "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==", + "dev": true, + "dependencies": { + "@babel/helper-validator-identifier": "^7.28.5", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "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==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, "node_modules/@esbuild/aix-ppc64": { "version": "0.25.12", "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.12.tgz", @@ -455,6 +483,508 @@ "hono": "^4" } }, + "node_modules/@inquirer/ansi": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@inquirer/ansi/-/ansi-1.0.2.tgz", + "integrity": "sha512-S8qNSZiYzFd0wAcyG5AXCvUHC5Sr7xpZ9wZ2py9XR88jUz8wooStVx5M6dRzczbBWjic9NP7+rY0Xi7qqK/aMQ==", + "dev": true, + "engines": { + "node": ">=18" + } + }, + "node_modules/@inquirer/checkbox": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/@inquirer/checkbox/-/checkbox-4.3.2.tgz", + "integrity": "sha512-VXukHf0RR1doGe6Sm4F0Em7SWYLTHSsbGfJdS9Ja2bX5/D5uwVOEjr07cncLROdBvmnvCATYEWlHqYmXv2IlQA==", + "dev": true, + "dependencies": { + "@inquirer/ansi": "^1.0.2", + "@inquirer/core": "^10.3.2", + "@inquirer/figures": "^1.0.15", + "@inquirer/type": "^3.0.10", + "yoctocolors-cjs": "^2.1.3" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/confirm": { + "version": "5.1.21", + "resolved": "https://registry.npmjs.org/@inquirer/confirm/-/confirm-5.1.21.tgz", + "integrity": "sha512-KR8edRkIsUayMXV+o3Gv+q4jlhENF9nMYUZs9PA2HzrXeHI8M5uDag70U7RJn9yyiMZSbtF5/UexBtAVtZGSbQ==", + "dev": true, + "dependencies": { + "@inquirer/core": "^10.3.2", + "@inquirer/type": "^3.0.10" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/core": { + "version": "10.3.2", + "resolved": "https://registry.npmjs.org/@inquirer/core/-/core-10.3.2.tgz", + "integrity": "sha512-43RTuEbfP8MbKzedNqBrlhhNKVwoK//vUFNW3Q3vZ88BLcrs4kYpGg+B2mm5p2K/HfygoCxuKwJJiv8PbGmE0A==", + "dev": true, + "dependencies": { + "@inquirer/ansi": "^1.0.2", + "@inquirer/figures": "^1.0.15", + "@inquirer/type": "^3.0.10", + "cli-width": "^4.1.0", + "mute-stream": "^2.0.0", + "signal-exit": "^4.1.0", + "wrap-ansi": "^6.2.0", + "yoctocolors-cjs": "^2.1.3" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/editor": { + "version": "4.2.23", + "resolved": "https://registry.npmjs.org/@inquirer/editor/-/editor-4.2.23.tgz", + "integrity": "sha512-aLSROkEwirotxZ1pBaP8tugXRFCxW94gwrQLxXfrZsKkfjOYC1aRvAZuhpJOb5cu4IBTJdsCigUlf2iCOu4ZDQ==", + "dev": true, + "dependencies": { + "@inquirer/core": "^10.3.2", + "@inquirer/external-editor": "^1.0.3", + "@inquirer/type": "^3.0.10" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/expand": { + "version": "4.0.23", + "resolved": "https://registry.npmjs.org/@inquirer/expand/-/expand-4.0.23.tgz", + "integrity": "sha512-nRzdOyFYnpeYTTR2qFwEVmIWypzdAx/sIkCMeTNTcflFOovfqUk+HcFhQQVBftAh9gmGrpFj6QcGEqrDMDOiew==", + "dev": true, + "dependencies": { + "@inquirer/core": "^10.3.2", + "@inquirer/type": "^3.0.10", + "yoctocolors-cjs": "^2.1.3" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/external-editor": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@inquirer/external-editor/-/external-editor-1.0.3.tgz", + "integrity": "sha512-RWbSrDiYmO4LbejWY7ttpxczuwQyZLBUyygsA9Nsv95hpzUWwnNTVQmAq3xuh7vNwCp07UTmE5i11XAEExx4RA==", + "dev": true, + "dependencies": { + "chardet": "^2.1.1", + "iconv-lite": "^0.7.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/figures": { + "version": "1.0.15", + "resolved": "https://registry.npmjs.org/@inquirer/figures/-/figures-1.0.15.tgz", + "integrity": "sha512-t2IEY+unGHOzAaVM5Xx6DEWKeXlDDcNPeDyUpsRc6CUhBfU3VQOEl+Vssh7VNp1dR8MdUJBWhuObjXCsVpjN5g==", + "dev": true, + "engines": { + "node": ">=18" + } + }, + "node_modules/@inquirer/input": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/@inquirer/input/-/input-4.3.1.tgz", + "integrity": "sha512-kN0pAM4yPrLjJ1XJBjDxyfDduXOuQHrBB8aLDMueuwUGn+vNpF7Gq7TvyVxx8u4SHlFFj4trmj+a2cbpG4Jn1g==", + "dev": true, + "dependencies": { + "@inquirer/core": "^10.3.2", + "@inquirer/type": "^3.0.10" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/number": { + "version": "3.0.23", + "resolved": "https://registry.npmjs.org/@inquirer/number/-/number-3.0.23.tgz", + "integrity": "sha512-5Smv0OK7K0KUzUfYUXDXQc9jrf8OHo4ktlEayFlelCjwMXz0299Y8OrI+lj7i4gCBY15UObk76q0QtxjzFcFcg==", + "dev": true, + "dependencies": { + "@inquirer/core": "^10.3.2", + "@inquirer/type": "^3.0.10" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/password": { + "version": "4.0.23", + "resolved": "https://registry.npmjs.org/@inquirer/password/-/password-4.0.23.tgz", + "integrity": "sha512-zREJHjhT5vJBMZX/IUbyI9zVtVfOLiTO66MrF/3GFZYZ7T4YILW5MSkEYHceSii/KtRk+4i3RE7E1CUXA2jHcA==", + "dev": true, + "dependencies": { + "@inquirer/ansi": "^1.0.2", + "@inquirer/core": "^10.3.2", + "@inquirer/type": "^3.0.10" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/prompts": { + "version": "7.10.1", + "resolved": "https://registry.npmjs.org/@inquirer/prompts/-/prompts-7.10.1.tgz", + "integrity": "sha512-Dx/y9bCQcXLI5ooQ5KyvA4FTgeo2jYj/7plWfV5Ak5wDPKQZgudKez2ixyfz7tKXzcJciTxqLeK7R9HItwiByg==", + "dev": true, + "dependencies": { + "@inquirer/checkbox": "^4.3.2", + "@inquirer/confirm": "^5.1.21", + "@inquirer/editor": "^4.2.23", + "@inquirer/expand": "^4.0.23", + "@inquirer/input": "^4.3.1", + "@inquirer/number": "^3.0.23", + "@inquirer/password": "^4.0.23", + "@inquirer/rawlist": "^4.1.11", + "@inquirer/search": "^3.2.2", + "@inquirer/select": "^4.4.2" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/rawlist": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@inquirer/rawlist/-/rawlist-4.1.11.tgz", + "integrity": "sha512-+LLQB8XGr3I5LZN/GuAHo+GpDJegQwuPARLChlMICNdwW7OwV2izlCSCxN6cqpL0sMXmbKbFcItJgdQq5EBXTw==", + "dev": true, + "dependencies": { + "@inquirer/core": "^10.3.2", + "@inquirer/type": "^3.0.10", + "yoctocolors-cjs": "^2.1.3" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/search": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/@inquirer/search/-/search-3.2.2.tgz", + "integrity": "sha512-p2bvRfENXCZdWF/U2BXvnSI9h+tuA8iNqtUKb9UWbmLYCRQxd8WkvwWvYn+3NgYaNwdUkHytJMGG4MMLucI1kA==", + "dev": true, + "dependencies": { + "@inquirer/core": "^10.3.2", + "@inquirer/figures": "^1.0.15", + "@inquirer/type": "^3.0.10", + "yoctocolors-cjs": "^2.1.3" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/select": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/@inquirer/select/-/select-4.4.2.tgz", + "integrity": "sha512-l4xMuJo55MAe+N7Qr4rX90vypFwCajSakx59qe/tMaC1aEHWLyw68wF4o0A4SLAY4E0nd+Vt+EyskeDIqu1M6w==", + "dev": true, + "dependencies": { + "@inquirer/ansi": "^1.0.2", + "@inquirer/core": "^10.3.2", + "@inquirer/figures": "^1.0.15", + "@inquirer/type": "^3.0.10", + "yoctocolors-cjs": "^2.1.3" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/type": { + "version": "3.0.10", + "resolved": "https://registry.npmjs.org/@inquirer/type/-/type-3.0.10.tgz", + "integrity": "sha512-BvziSRxfz5Ov8ch0z/n3oijRSEcEsHnhggm4xFZe93DHcUCTlutlq9Ox4SVENAfcRD22UQq7T/atg9Wr3k09eA==", + "dev": true, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "dev": true, + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@isaacs/cliui/node_modules/ansi-styles": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", + "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/@jest/diff-sequences": { + "version": "30.0.1", + "resolved": "https://registry.npmjs.org/@jest/diff-sequences/-/diff-sequences-30.0.1.tgz", + "integrity": "sha512-n5H8QLDJ47QqbCNn5SuFjCRDrOLEZ0h8vAHCK5RL9Ls7Xa8AQLa/YxAc9UjFqoEDM48muwtBGjtMY5cr0PLDCw==", + "dev": true, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/expect-utils": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-30.2.0.tgz", + "integrity": "sha512-1JnRfhqpD8HGpOmQp180Fo9Zt69zNtC+9lR+kT7NVL05tNXIi+QC8Csz7lfidMoVLPD3FnOtcmp0CEFnxExGEA==", + "dev": true, + "dependencies": { + "@jest/get-type": "30.1.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/get-type": { + "version": "30.1.0", + "resolved": "https://registry.npmjs.org/@jest/get-type/-/get-type-30.1.0.tgz", + "integrity": "sha512-eMbZE2hUnx1WV0pmURZY9XoXPkUYjpc55mb0CrhtdWLtzMQPFvu/rZkTLZFTsdaVQa+Tr4eWAteqcUzoawq/uA==", + "dev": true, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/pattern": { + "version": "30.0.1", + "resolved": "https://registry.npmjs.org/@jest/pattern/-/pattern-30.0.1.tgz", + "integrity": "sha512-gWp7NfQW27LaBQz3TITS8L7ZCQ0TLvtmI//4OwlQRx4rnWxcPNIYjxZpDcN4+UlGxgm3jS5QPz8IPTCkb59wZA==", + "dev": true, + "dependencies": { + "@types/node": "*", + "jest-regex-util": "30.0.1" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/schemas": { + "version": "30.0.5", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-30.0.5.tgz", + "integrity": "sha512-DmdYgtezMkh3cpU8/1uyXakv3tJRcmcXxBOcO0tbaozPwpmh4YMsnWrQm9ZmZMfa5ocbxzbFk6O4bDPEc/iAnA==", + "dev": true, + "dependencies": { + "@sinclair/typebox": "^0.34.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/types": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-30.2.0.tgz", + "integrity": "sha512-H9xg1/sfVvyfU7o3zMfBEjQ1gcsdeTMgqHoYdN79tuLqfTtuu7WckRA1R5whDwOzxaZAeMKTYWqP+WCAi0CHsg==", + "dev": true, + "dependencies": { + "@jest/pattern": "30.0.1", + "@jest/schemas": "30.0.5", + "@types/istanbul-lib-coverage": "^2.0.6", + "@types/istanbul-reports": "^3.0.4", + "@types/node": "*", + "@types/yargs": "^17.0.33", + "chalk": "^4.1.2" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/types/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, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@jest/types/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/@jest/types/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, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "dev": true + }, "node_modules/@modelcontextprotocol/sdk": { "version": "1.26.0", "resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.26.0.tgz", @@ -525,6 +1055,96 @@ "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" }, + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "dev": true, + "optional": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/@promptbook/utils": { + "version": "0.69.5", + "resolved": "https://registry.npmjs.org/@promptbook/utils/-/utils-0.69.5.tgz", + "integrity": "sha512-xm5Ti/Hp3o4xHrsK9Yy3MS6KbDxYbq485hDsFvxqaNA7equHLPdo8H8faTitTeb14QCDfLW4iwCxdVYu5sn6YQ==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://buymeacoffee.com/hejny" + }, + { + "type": "github", + "url": "https://github.com/webgptorg/promptbook/blob/main/README.md#%EF%B8%8F-contributing" + } + ], + "dependencies": { + "spacetrim": "0.11.59" + } + }, + "node_modules/@puppeteer/browsers": { + "version": "2.13.0", + "resolved": "https://registry.npmjs.org/@puppeteer/browsers/-/browsers-2.13.0.tgz", + "integrity": "sha512-46BZJYJjc/WwmKjsvDFykHtXrtomsCIrwYQPOP7VfMJoZY2bsDF9oROBABR3paDjDcmkUye1Pb1BqdcdiipaWA==", + "dev": true, + "dependencies": { + "debug": "^4.4.3", + "extract-zip": "^2.0.1", + "progress": "^2.0.3", + "proxy-agent": "^6.5.0", + "semver": "^7.7.4", + "tar-fs": "^3.1.1", + "yargs": "^17.7.2" + }, + "bin": { + "browsers": "lib/cjs/main-cli.js" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@puppeteer/browsers/node_modules/b4a": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/b4a/-/b4a-1.8.0.tgz", + "integrity": "sha512-qRuSmNSkGQaHwNbM7J78Wwy+ghLEYF1zNrSeMxj4Kgw6y33O3mXcQ6Ie9fRvfU/YnxWkOchPXbaLb73TkIsfdg==", + "dev": true, + "peerDependencies": { + "react-native-b4a": "*" + }, + "peerDependenciesMeta": { + "react-native-b4a": { + "optional": true + } + } + }, + "node_modules/@puppeteer/browsers/node_modules/tar-fs": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-3.1.2.tgz", + "integrity": "sha512-QGxxTxxyleAdyM3kpFs14ymbYmNFrfY+pHj7Z8FgtbZ7w2//VAgLMac7sT6nRpIHjppXO2AwwEOg0bPFVRcmXw==", + "dev": true, + "dependencies": { + "pump": "^3.0.0", + "tar-stream": "^3.1.5" + }, + "optionalDependencies": { + "bare-fs": "^4.0.1", + "bare-path": "^3.0.0" + } + }, + "node_modules/@puppeteer/browsers/node_modules/tar-stream": { + "version": "3.1.8", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-3.1.8.tgz", + "integrity": "sha512-U6QpVRyCGHva435KoNWy9PRoi2IFYCgtEhq9nmrPPpbRacPs9IH4aJ3gbrFC8dPcXvdSZ4XXfXT5Fshbp2MtlQ==", + "dev": true, + "dependencies": { + "b4a": "^1.6.4", + "bare-fs": "^4.5.5", + "fast-fifo": "^1.2.0", + "streamx": "^2.15.0" + } + }, "node_modules/@rollup/rollup-android-arm-eabi": { "version": "4.59.0", "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.59.0.tgz", @@ -850,6 +1470,30 @@ "win32" ] }, + "node_modules/@sec-ant/readable-stream": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@sec-ant/readable-stream/-/readable-stream-0.4.1.tgz", + "integrity": "sha512-831qok9r2t8AlxLko40y2ebgSDhenenCatLVeW/uBtnHPyhHOvG0C7TvfgecV+wHzIm5KUICgzmVpWS+IMEAeg==", + "dev": true + }, + "node_modules/@sinclair/typebox": { + "version": "0.34.48", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.34.48.tgz", + "integrity": "sha512-kKJTNuK3AQOrgjjotVxMrCn1sUJwM76wMszfq1kdU4uYVJjvEWuFQ6HgvLt4Xz3fSmZlTOxJ/Ie13KnIcWQXFA==", + "dev": true + }, + "node_modules/@sindresorhus/merge-streams": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/merge-streams/-/merge-streams-4.0.0.tgz", + "integrity": "sha512-tlqY9xq5ukxTUZBmoOp+m61cqwQD5pHJtFY3Mn8CA8ps6yghLH/Hw8UPdqg4OLmFW3IFlcXnQNmo/dh8HzXYIQ==", + "dev": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/@tauri-apps/api": { "version": "2.10.1", "resolved": "https://registry.npmjs.org/@tauri-apps/api/-/api-2.10.1.tgz", @@ -1075,12 +1719,63 @@ "prosemirror-view": "^1.18.7" } }, + "node_modules/@tootallnate/quickjs-emscripten": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@tootallnate/quickjs-emscripten/-/quickjs-emscripten-0.23.0.tgz", + "integrity": "sha512-C5Mc6rdnsaJDjO3UpGW/CQTHtCKaYlScZTly4JIu97Jxo/odCiH0ITnDXSJPTOrEKk/ycSZ0AOgTmkDtkOsvIA==", + "dev": true + }, "node_modules/@types/estree": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", "dev": true }, + "node_modules/@types/istanbul-lib-coverage": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz", + "integrity": "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==", + "dev": true + }, + "node_modules/@types/istanbul-lib-report": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.3.tgz", + "integrity": "sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==", + "dev": true, + "dependencies": { + "@types/istanbul-lib-coverage": "*" + } + }, + "node_modules/@types/istanbul-reports": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz", + "integrity": "sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==", + "dev": true, + "dependencies": { + "@types/istanbul-lib-report": "*" + } + }, + "node_modules/@types/mocha": { + "version": "10.0.10", + "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-10.0.10.tgz", + "integrity": "sha512-xPyYSz1cMPnJQhl0CLMH68j3gprKZaTjG3s5Vi+fDgx+uhG9NOXwbVt52eFS8ECyXhyKcjDLCBEqBExKuiZb7Q==", + "dev": true + }, + "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==", + "dev": true, + "dependencies": { + "undici-types": "~6.21.0" + } + }, + "node_modules/@types/normalize-package-data": { + "version": "2.4.4", + "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.4.tgz", + "integrity": "sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==", + "dev": true + }, "node_modules/@types/prop-types": { "version": "15.7.15", "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.15.tgz", @@ -1106,6 +1801,380 @@ "@types/react": "^18.0.0" } }, + "node_modules/@types/sinonjs__fake-timers": { + "version": "8.1.5", + "resolved": "https://registry.npmjs.org/@types/sinonjs__fake-timers/-/sinonjs__fake-timers-8.1.5.tgz", + "integrity": "sha512-mQkU2jY8jJEF7YHjHvsQO8+3ughTL1mcnn96igfhONmR+fUPSKIkefQYpSe8bsly2Ep7oQbn/6VG5/9/0qcArQ==", + "dev": true + }, + "node_modules/@types/stack-utils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.3.tgz", + "integrity": "sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==", + "dev": true + }, + "node_modules/@types/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@types/which/-/which-2.0.2.tgz", + "integrity": "sha512-113D3mDkZDjo+EeUEHCFy0qniNc1ZpecGiAU7WSo7YDoSzolZIQKpYFHrPpjkB2nuyahcKfrmLXeQlh7gqJYdw==", + "dev": true + }, + "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, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/yargs": { + "version": "17.0.35", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.35.tgz", + "integrity": "sha512-qUHkeCyQFxMXg79wQfTtfndEC+N9ZZg76HJftDJp+qH2tV7Gj4OJi7l+PiWwJ+pWtW8GwSmqsDj/oymhrTWXjg==", + "dev": true, + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/@types/yargs-parser": { + "version": "21.0.3", + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.3.tgz", + "integrity": "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==", + "dev": true + }, + "node_modules/@types/yauzl": { + "version": "2.10.3", + "resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.10.3.tgz", + "integrity": "sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q==", + "dev": true, + "optional": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@vitest/pretty-format": { + "version": "2.1.9", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-2.1.9.tgz", + "integrity": "sha512-KhRIdGV2U9HOUzxfiHmY8IFHTdqtOhIzCpd8WRdJiE7D/HUcZVD0EgQCVjm+Q9gkUXWgBvMmTtZgIG48wq7sOQ==", + "dev": true, + "dependencies": { + "tinyrainbow": "^1.2.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/snapshot": { + "version": "2.1.9", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-2.1.9.tgz", + "integrity": "sha512-oBO82rEjsxLNJincVhLhaxxZdEtV0EFHMK5Kmx5sJ6H9L183dHECjiefOAdnqpIgT5eZwT04PoggUnW88vOBNQ==", + "dev": true, + "dependencies": { + "@vitest/pretty-format": "2.1.9", + "magic-string": "^0.30.12", + "pathe": "^1.1.2" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@wdio/cli": { + "version": "9.24.0", + "resolved": "https://registry.npmjs.org/@wdio/cli/-/cli-9.24.0.tgz", + "integrity": "sha512-dFs1HNmyXne0pDOYPOHhFcck0BC22z0lMdu6RtTX1C4gHdEYsjTtTH2zsZ5N5BzzsZVSUol2PisuqyLQO5dZIA==", + "dev": true, + "dependencies": { + "@vitest/snapshot": "^2.1.1", + "@wdio/config": "9.24.0", + "@wdio/globals": "9.23.0", + "@wdio/logger": "9.18.0", + "@wdio/protocols": "9.24.0", + "@wdio/types": "9.24.0", + "@wdio/utils": "9.24.0", + "async-exit-hook": "^2.0.1", + "chalk": "^5.4.1", + "chokidar": "^4.0.0", + "create-wdio": "9.21.0", + "dotenv": "^17.2.0", + "import-meta-resolve": "^4.0.0", + "lodash.flattendeep": "^4.4.0", + "lodash.pickby": "^4.6.0", + "lodash.union": "^4.6.0", + "read-pkg-up": "^10.0.0", + "tsx": "^4.7.2", + "webdriverio": "9.24.0", + "yargs": "^17.7.2" + }, + "bin": { + "wdio": "bin/wdio.js" + }, + "engines": { + "node": ">=18.20.0" + } + }, + "node_modules/@wdio/config": { + "version": "9.24.0", + "resolved": "https://registry.npmjs.org/@wdio/config/-/config-9.24.0.tgz", + "integrity": "sha512-rcHu0eG16rSEmHL0sEKDcr/vYFmGhQ5GOlmlx54r+1sgh6sf136q+kth4169s16XqviWGW3LjZbUfpTK29pGtw==", + "dev": true, + "dependencies": { + "@wdio/logger": "9.18.0", + "@wdio/types": "9.24.0", + "@wdio/utils": "9.24.0", + "deepmerge-ts": "^7.0.3", + "glob": "^10.2.2", + "import-meta-resolve": "^4.0.0", + "jiti": "^2.6.1" + }, + "engines": { + "node": ">=18.20.0" + } + }, + "node_modules/@wdio/dot-reporter": { + "version": "9.24.0", + "resolved": "https://registry.npmjs.org/@wdio/dot-reporter/-/dot-reporter-9.24.0.tgz", + "integrity": "sha512-hZNXnY4EVnDRTedGSlkWQL6sPkxe1zgRzTaam+S7GduF+IOTQXHIsuklfpHLs6mrj3oS+JuKbosodsyvLU7KQA==", + "dev": true, + "dependencies": { + "@wdio/reporter": "9.24.0", + "@wdio/types": "9.24.0", + "chalk": "^5.0.1" + }, + "engines": { + "node": ">=18.20.0" + } + }, + "node_modules/@wdio/globals": { + "version": "9.23.0", + "resolved": "https://registry.npmjs.org/@wdio/globals/-/globals-9.23.0.tgz", + "integrity": "sha512-OmwPKV8c5ecLqo+EkytN7oUeYfNmRI4uOXGIR1ybP7AK5Zz+l9R0dGfoadEuwi1aZXAL0vwuhtq3p0OL3dfqHQ==", + "dev": true, + "engines": { + "node": ">=18.20.0" + }, + "peerDependencies": { + "expect-webdriverio": "^5.3.4", + "webdriverio": "^9.0.0" + }, + "peerDependenciesMeta": { + "expect-webdriverio": { + "optional": false + }, + "webdriverio": { + "optional": false + } + } + }, + "node_modules/@wdio/local-runner": { + "version": "9.24.0", + "resolved": "https://registry.npmjs.org/@wdio/local-runner/-/local-runner-9.24.0.tgz", + "integrity": "sha512-SUs5HEGHXEl/fVdkkhY1+of+Dv8AlceulRTpmYMmSR4Nu+tHUXSPkzjoYgVa+xVu0MpVrD+dhTm13hOtlDAlMg==", + "dev": true, + "dependencies": { + "@types/node": "^20.1.0", + "@wdio/logger": "9.18.0", + "@wdio/repl": "9.16.2", + "@wdio/runner": "9.24.0", + "@wdio/types": "9.24.0", + "@wdio/xvfb": "9.24.0", + "exit-hook": "^4.0.0", + "expect-webdriverio": "^5.3.4", + "split2": "^4.1.0", + "stream-buffers": "^3.0.2" + }, + "engines": { + "node": ">=18.20.0" + } + }, + "node_modules/@wdio/logger": { + "version": "9.18.0", + "resolved": "https://registry.npmjs.org/@wdio/logger/-/logger-9.18.0.tgz", + "integrity": "sha512-HdzDrRs+ywAqbXGKqe1i/bLtCv47plz4TvsHFH3j729OooT5VH38ctFn5aLXgECmiAKDkmH/A6kOq2Zh5DIxww==", + "dev": true, + "dependencies": { + "chalk": "^5.1.2", + "loglevel": "^1.6.0", + "loglevel-plugin-prefix": "^0.8.4", + "safe-regex2": "^5.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18.20.0" + } + }, + "node_modules/@wdio/mocha-framework": { + "version": "9.24.0", + "resolved": "https://registry.npmjs.org/@wdio/mocha-framework/-/mocha-framework-9.24.0.tgz", + "integrity": "sha512-zzTfFk79Zx3qZgfbgpJ7o0euzgXIQSCzbfFPjgtEx8u7fvrhB8tbgf+EGPOEGPBOH/X1GvpAfDkhkgZ6roDR2Q==", + "dev": true, + "dependencies": { + "@types/mocha": "^10.0.6", + "@types/node": "^20.11.28", + "@wdio/logger": "9.18.0", + "@wdio/types": "9.24.0", + "@wdio/utils": "9.24.0", + "mocha": "^10.3.0" + }, + "engines": { + "node": ">=18.20.0" + } + }, + "node_modules/@wdio/protocols": { + "version": "9.24.0", + "resolved": "https://registry.npmjs.org/@wdio/protocols/-/protocols-9.24.0.tgz", + "integrity": "sha512-ozQKYddBLT4TRvU9J+fGrhVUtx3iDAe+KNCJcTDMFMxNSdDMR2xFQdNp8HLHypspk58oXTYCvz6ZYjySthhqsw==", + "dev": true + }, + "node_modules/@wdio/repl": { + "version": "9.16.2", + "resolved": "https://registry.npmjs.org/@wdio/repl/-/repl-9.16.2.tgz", + "integrity": "sha512-FLTF0VL6+o5BSTCO7yLSXocm3kUnu31zYwzdsz4n9s5YWt83sCtzGZlZpt7TaTzb3jVUfxuHNQDTb8UMkCu0lQ==", + "dev": true, + "dependencies": { + "@types/node": "^20.1.0" + }, + "engines": { + "node": ">=18.20.0" + } + }, + "node_modules/@wdio/reporter": { + "version": "9.24.0", + "resolved": "https://registry.npmjs.org/@wdio/reporter/-/reporter-9.24.0.tgz", + "integrity": "sha512-0VrEX2uzjrFCHb6fNQDrQe6X7xuQbXUJhy5CGhMZghnPegW0OnKguwUy/vVKJE0HEDMOrR8djteefxJVfOOZpw==", + "dev": true, + "dependencies": { + "@types/node": "^20.1.0", + "@wdio/logger": "9.18.0", + "@wdio/types": "9.24.0", + "diff": "^8.0.2", + "object-inspect": "^1.12.0" + }, + "engines": { + "node": ">=18.20.0" + } + }, + "node_modules/@wdio/runner": { + "version": "9.24.0", + "resolved": "https://registry.npmjs.org/@wdio/runner/-/runner-9.24.0.tgz", + "integrity": "sha512-B1ezRtvR/eJCKQSVyLbERpyyMG3bP7RKaQBHT7bNxGsuEuarkUU7cS/T/aB+woZb4UtWotlrBFY4icAS0HqOkA==", + "dev": true, + "dependencies": { + "@types/node": "^20.11.28", + "@wdio/config": "9.24.0", + "@wdio/dot-reporter": "9.24.0", + "@wdio/globals": "9.23.0", + "@wdio/logger": "9.18.0", + "@wdio/types": "9.24.0", + "@wdio/utils": "9.24.0", + "deepmerge-ts": "^7.0.3", + "webdriver": "9.24.0", + "webdriverio": "9.24.0" + }, + "engines": { + "node": ">=18.20.0" + }, + "peerDependencies": { + "expect-webdriverio": "^5.3.4", + "webdriverio": "^9.0.0" + }, + "peerDependenciesMeta": { + "expect-webdriverio": { + "optional": false + }, + "webdriverio": { + "optional": false + } + } + }, + "node_modules/@wdio/spec-reporter": { + "version": "9.24.0", + "resolved": "https://registry.npmjs.org/@wdio/spec-reporter/-/spec-reporter-9.24.0.tgz", + "integrity": "sha512-I3HExQKvF5u+RUcwImk9JMiuBgo2MmuKDj3Y0oSRwSw0TxQX0nqVvt8udlVG6XJpOD+e4EqlCGWbFfMezi8iTA==", + "dev": true, + "dependencies": { + "@wdio/reporter": "9.24.0", + "@wdio/types": "9.24.0", + "chalk": "^5.1.2", + "easy-table": "^1.2.0", + "pretty-ms": "^9.0.0" + }, + "engines": { + "node": ">=18.20.0" + } + }, + "node_modules/@wdio/types": { + "version": "9.24.0", + "resolved": "https://registry.npmjs.org/@wdio/types/-/types-9.24.0.tgz", + "integrity": "sha512-PYYunNl8Uq1r8YMJAK6ReRy/V/XIrCSyj5cpCtR5EqCL6heETOORFj7gt4uPnzidfgbtMBcCru0LgjjlMiH1UQ==", + "dev": true, + "dependencies": { + "@types/node": "^20.1.0" + }, + "engines": { + "node": ">=18.20.0" + } + }, + "node_modules/@wdio/utils": { + "version": "9.24.0", + "resolved": "https://registry.npmjs.org/@wdio/utils/-/utils-9.24.0.tgz", + "integrity": "sha512-6WhtzC5SNCGRBTkaObX6A07Ofnnyyf+TQH/d/fuhZRqvBknrP4AMMZF+PFxGl1fwdySWdBn+gV2QLE+52Byowg==", + "dev": true, + "dependencies": { + "@puppeteer/browsers": "^2.2.0", + "@wdio/logger": "9.18.0", + "@wdio/types": "9.24.0", + "decamelize": "^6.0.0", + "deepmerge-ts": "^7.0.3", + "edgedriver": "^6.1.2", + "geckodriver": "^6.1.0", + "get-port": "^7.0.0", + "import-meta-resolve": "^4.0.0", + "locate-app": "^2.2.24", + "mitt": "^3.0.1", + "safaridriver": "^1.0.0", + "split2": "^4.2.0", + "wait-port": "^1.1.0" + }, + "engines": { + "node": ">=18.20.0" + } + }, + "node_modules/@wdio/xvfb": { + "version": "9.24.0", + "resolved": "https://registry.npmjs.org/@wdio/xvfb/-/xvfb-9.24.0.tgz", + "integrity": "sha512-eK0rUZeR+wlFapm2cb7OQ1LwC/nHmdo7eX02B2Dme7uQGF3+/ILkDTm5B0RYpGUbM3ktw3d0fhcwqqeNNH9KIA==", + "dev": true, + "dependencies": { + "@wdio/logger": "9.18.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@zip.js/zip.js": { + "version": "2.8.23", + "resolved": "https://registry.npmjs.org/@zip.js/zip.js/-/zip.js-2.8.23.tgz", + "integrity": "sha512-RB+RLnxPJFPrGvQ9rgO+4JOcsob6lD32OcF0QE0yg24oeW9q8KnTTNlugcDaIveEcCbclobJcZP+fLQ++sH0bw==", + "dev": true, + "engines": { + "bun": ">=0.7.0", + "deno": ">=1.0.0", + "node": ">=18.0.0" + } + }, + "node_modules/abort-controller": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", + "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", + "dev": true, + "dependencies": { + "event-target-shim": "^5.0.0" + }, + "engines": { + "node": ">=6.5" + } + }, "node_modules/accepts": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz", @@ -1118,6 +2187,15 @@ "node": ">= 0.6" } }, + "node_modules/agent-base": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", + "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", + "dev": true, + "engines": { + "node": ">= 14" + } + }, "node_modules/ajv-formats": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-3.0.1.tgz", @@ -1154,6 +2232,257 @@ "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" }, + "node_modules/ansi-colors": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", + "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "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, + "engines": { + "node": ">=8" + } + }, + "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==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/anymatch/node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/archiver": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/archiver/-/archiver-7.0.1.tgz", + "integrity": "sha512-ZcbTaIqJOfCc03QwD468Unz/5Ir8ATtvAHsK+FdXbDIbGfihqh9mrvdcYunQzqn4HrvWWaFyaxJhGZagaJJpPQ==", + "dev": true, + "dependencies": { + "archiver-utils": "^5.0.2", + "async": "^3.2.4", + "buffer-crc32": "^1.0.0", + "readable-stream": "^4.0.0", + "readdir-glob": "^1.1.2", + "tar-stream": "^3.0.0", + "zip-stream": "^6.0.1" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/archiver-utils": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/archiver-utils/-/archiver-utils-5.0.2.tgz", + "integrity": "sha512-wuLJMmIBQYCsGZgYLTy5FIB2pF6Lfb6cXMSF8Qywwk3t20zWnAi7zLcQFdKQmIB8wyZpY5ER38x08GbwtR2cLA==", + "dev": true, + "dependencies": { + "glob": "^10.0.0", + "graceful-fs": "^4.2.0", + "is-stream": "^2.0.1", + "lazystream": "^1.0.0", + "lodash": "^4.17.15", + "normalize-path": "^3.0.0", + "readable-stream": "^4.0.0" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/archiver-utils/node_modules/buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, + "node_modules/archiver-utils/node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/archiver-utils/node_modules/readable-stream": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.7.0.tgz", + "integrity": "sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg==", + "dev": true, + "dependencies": { + "abort-controller": "^3.0.0", + "buffer": "^6.0.3", + "events": "^3.3.0", + "process": "^0.11.10", + "string_decoder": "^1.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/archiver/node_modules/b4a": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/b4a/-/b4a-1.8.0.tgz", + "integrity": "sha512-qRuSmNSkGQaHwNbM7J78Wwy+ghLEYF1zNrSeMxj4Kgw6y33O3mXcQ6Ie9fRvfU/YnxWkOchPXbaLb73TkIsfdg==", + "dev": true, + "peerDependencies": { + "react-native-b4a": "*" + }, + "peerDependenciesMeta": { + "react-native-b4a": { + "optional": true + } + } + }, + "node_modules/archiver/node_modules/buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, + "node_modules/archiver/node_modules/readable-stream": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.7.0.tgz", + "integrity": "sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg==", + "dev": true, + "dependencies": { + "abort-controller": "^3.0.0", + "buffer": "^6.0.3", + "events": "^3.3.0", + "process": "^0.11.10", + "string_decoder": "^1.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/archiver/node_modules/tar-stream": { + "version": "3.1.8", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-3.1.8.tgz", + "integrity": "sha512-U6QpVRyCGHva435KoNWy9PRoi2IFYCgtEhq9nmrPPpbRacPs9IH4aJ3gbrFC8dPcXvdSZ4XXfXT5Fshbp2MtlQ==", + "dev": true, + "dependencies": { + "b4a": "^1.6.4", + "bare-fs": "^4.5.5", + "fast-fifo": "^1.2.0", + "streamx": "^2.15.0" + } + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, + "node_modules/aria-query": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.2.tgz", + "integrity": "sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==", + "dev": true, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/ast-types": { + "version": "0.13.4", + "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.13.4.tgz", + "integrity": "sha512-x1FCFnFifvYDDzTaLII71vG5uvDwgtmDTEVWAxrgeiR8VjMONcCXJx7E+USjDtHlwFmt9MysbqgF9b9Vjr6w+w==", + "dev": true, + "dependencies": { + "tslib": "^2.0.1" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/async": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", + "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==", + "dev": true + }, + "node_modules/async-exit-hook": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/async-exit-hook/-/async-exit-hook-2.0.1.tgz", + "integrity": "sha512-NW2cX8m1Q7KPA7a5M2ULQeZ2wR5qI5PAbw5L0UOMxdioVk9PMZ0h1TmyZEkPYrCvYjDlFICusOu1dlEKAAeXBw==", + "dev": true, + "engines": { + "node": ">=0.12.0" + } + }, "node_modules/asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", @@ -1169,6 +2498,99 @@ "proxy-from-env": "^1.1.0" } }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true + }, + "node_modules/bare-events": { + "version": "2.8.2", + "resolved": "https://registry.npmjs.org/bare-events/-/bare-events-2.8.2.tgz", + "integrity": "sha512-riJjyv1/mHLIPX4RwiK+oW9/4c3TEUeORHKefKAKnZ5kyslbN+HXowtbaVEqt4IMUB7OXlfixcs6gsFeo/jhiQ==", + "dev": true, + "peerDependencies": { + "bare-abort-controller": "*" + }, + "peerDependenciesMeta": { + "bare-abort-controller": { + "optional": true + } + } + }, + "node_modules/bare-fs": { + "version": "4.5.5", + "resolved": "https://registry.npmjs.org/bare-fs/-/bare-fs-4.5.5.tgz", + "integrity": "sha512-XvwYM6VZqKoqDll8BmSww5luA5eflDzY0uEFfBJtFKe4PAAtxBjU3YIxzIBzhyaEQBy1VXEQBto4cpN5RZJw+w==", + "dev": true, + "dependencies": { + "bare-events": "^2.5.4", + "bare-path": "^3.0.0", + "bare-stream": "^2.6.4", + "bare-url": "^2.2.2", + "fast-fifo": "^1.3.2" + }, + "engines": { + "bare": ">=1.16.0" + }, + "peerDependencies": { + "bare-buffer": "*" + }, + "peerDependenciesMeta": { + "bare-buffer": { + "optional": true + } + } + }, + "node_modules/bare-os": { + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/bare-os/-/bare-os-3.7.1.tgz", + "integrity": "sha512-ebvMaS5BgZKmJlvuWh14dg9rbUI84QeV3WlWn6Ph6lFI8jJoh7ADtVTyD2c93euwbe+zgi0DVrl4YmqXeM9aIA==", + "dev": true, + "engines": { + "bare": ">=1.14.0" + } + }, + "node_modules/bare-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/bare-path/-/bare-path-3.0.0.tgz", + "integrity": "sha512-tyfW2cQcB5NN8Saijrhqn0Zh7AnFNsnczRcuWODH0eYAXBsJ5gVxAUuNr7tsHSC6IZ77cA0SitzT+s47kot8Mw==", + "dev": true, + "dependencies": { + "bare-os": "^3.0.1" + } + }, + "node_modules/bare-stream": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/bare-stream/-/bare-stream-2.8.0.tgz", + "integrity": "sha512-reUN0M2sHRqCdG4lUK3Fw8w98eeUIZHL5c3H7Mbhk2yVBL+oofgaIp0ieLfD5QXwPCypBpmEEKU2WZKzbAk8GA==", + "dev": true, + "dependencies": { + "streamx": "^2.21.0", + "teex": "^1.0.1" + }, + "peerDependencies": { + "bare-buffer": "*", + "bare-events": "*" + }, + "peerDependenciesMeta": { + "bare-buffer": { + "optional": true + }, + "bare-events": { + "optional": true + } + } + }, + "node_modules/bare-url": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/bare-url/-/bare-url-2.3.2.tgz", + "integrity": "sha512-ZMq4gd9ngV5aTMa5p9+UfY0b3skwhHELaDkhEHetMdX0LRkW9kzaym4oo/Eh+Ghm0CCDuMTsRIGM/ytUc1ZYmw==", + "dev": true, + "dependencies": { + "bare-path": "^3.0.0" + } + }, "node_modules/base64-js": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", @@ -1188,6 +2610,15 @@ } ] }, + "node_modules/basic-ftp": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/basic-ftp/-/basic-ftp-5.2.0.tgz", + "integrity": "sha512-VoMINM2rqJwJgfdHq6RiUudKt2BV+FY5ZFezP/ypmwayk68+NzzAQy4XXLlqsGD4MCzq3DrmNFD/uUmBJuGoXw==", + "dev": true, + "engines": { + "node": ">=10.0.0" + } + }, "node_modules/better-sqlite3": { "version": "12.6.2", "resolved": "https://registry.npmjs.org/better-sqlite3/-/better-sqlite3-12.6.2.tgz", @@ -1201,6 +2632,18 @@ "node": "20.x || 22.x || 23.x || 24.x || 25.x" } }, + "node_modules/binary-extensions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/bindings": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", @@ -1242,6 +2685,39 @@ "url": "https://opencollective.com/express" } }, + "node_modules/boolbase": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", + "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==", + "dev": true + }, + "node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browser-stdout": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", + "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", + "dev": true + }, "node_modules/buffer": { "version": "5.7.1", "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", @@ -1265,6 +2741,15 @@ "ieee754": "^1.1.13" } }, + "node_modules/buffer-crc32": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-1.0.0.tgz", + "integrity": "sha512-Db1SbgBS/fg/392AblrMJk97KggmvYhr4pB5ZIMTWtaivCPMWLkmb7m21cJvpvgK+J3nsU2CmmixNBZx4vFj/w==", + "dev": true, + "engines": { + "node": ">=8.0.0" + } + }, "node_modules/bytes": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", @@ -1300,11 +2785,237 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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==", + "dev": true, + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/chardet": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/chardet/-/chardet-2.1.1.tgz", + "integrity": "sha512-PsezH1rqdV9VvyNhxxOW32/d75r01NY7TQCmOqomRo15ZSOKbpTFVsfjghxo6JloQUCGnH4k1LGu0R4yCLlWQQ==", + "dev": true + }, + "node_modules/cheerio": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.2.0.tgz", + "integrity": "sha512-WDrybc/gKFpTYQutKIK6UvfcuxijIZfMfXaYm8NMsPQxSYvf+13fXUJ4rztGGbJcBQ/GF55gvrZ0Bc0bj/mqvg==", + "dev": true, + "dependencies": { + "cheerio-select": "^2.1.0", + "dom-serializer": "^2.0.0", + "domhandler": "^5.0.3", + "domutils": "^3.2.2", + "encoding-sniffer": "^0.2.1", + "htmlparser2": "^10.1.0", + "parse5": "^7.3.0", + "parse5-htmlparser2-tree-adapter": "^7.1.0", + "parse5-parser-stream": "^7.1.2", + "undici": "^7.19.0", + "whatwg-mimetype": "^4.0.0" + }, + "engines": { + "node": ">=20.18.1" + }, + "funding": { + "url": "https://github.com/cheeriojs/cheerio?sponsor=1" + } + }, + "node_modules/cheerio-select": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cheerio-select/-/cheerio-select-2.1.0.tgz", + "integrity": "sha512-9v9kG0LvzrlcungtnJtpGNxY+fzECQKhK4EGJX2vByejiMX84MFNQw4UxPJl3bFbTMw+Dfs37XaIkCwTZfLh4g==", + "dev": true, + "dependencies": { + "boolbase": "^1.0.0", + "css-select": "^5.1.0", + "css-what": "^6.1.0", + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3", + "domutils": "^3.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/cheerio/node_modules/undici": { + "version": "7.22.0", + "resolved": "https://registry.npmjs.org/undici/-/undici-7.22.0.tgz", + "integrity": "sha512-RqslV2Us5BrllB+JeiZnK4peryVTndy9Dnqq62S3yYRRTj0tFQCwEniUy2167skdGOy3vqRzEvl1Dm4sV2ReDg==", + "dev": true, + "engines": { + "node": ">=20.18.1" + } + }, + "node_modules/chokidar": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", + "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", + "dev": true, + "dependencies": { + "readdirp": "^4.0.1" + }, + "engines": { + "node": ">= 14.16.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, "node_modules/chownr": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==" }, + "node_modules/ci-info": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-4.4.0.tgz", + "integrity": "sha512-77PSwercCZU2Fc4sX94eF8k8Pxte6JAwL4/ICZLFjJLqegs7kCuAsqqj/70NQF6TvDpgFjkubQB2FW2ZZddvQg==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/sibiraj-s" + } + ], + "engines": { + "node": ">=8" + } + }, + "node_modules/cli-width": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-4.1.0.tgz", + "integrity": "sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ==", + "dev": true, + "engines": { + "node": ">= 12" + } + }, + "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, + "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-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "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 + }, + "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==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "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==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "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, + "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" + } + }, + "node_modules/clone": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", + "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==", + "dev": true, + "optional": true, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, "node_modules/combined-stream": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", @@ -1316,6 +3027,89 @@ "node": ">= 0.8" } }, + "node_modules/commander": { + "version": "14.0.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-14.0.3.tgz", + "integrity": "sha512-H+y0Jo/T1RZ9qPP4Eh1pkcQcLRglraJaSLoyOtHxu6AapkjWVCy2Sit1QQ4x3Dng8qDlSsZEet7g5Pq06MvTgw==", + "dev": true, + "engines": { + "node": ">=20" + } + }, + "node_modules/compress-commons": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/compress-commons/-/compress-commons-6.0.2.tgz", + "integrity": "sha512-6FqVXeETqWPoGcfzrXb37E50NP0LXT8kAMu5ooZayhWWdgEY4lBEEcbQNXtkuKQsGduxiIcI4gOTsxTmuq/bSg==", + "dev": true, + "dependencies": { + "crc-32": "^1.2.0", + "crc32-stream": "^6.0.0", + "is-stream": "^2.0.1", + "normalize-path": "^3.0.0", + "readable-stream": "^4.0.0" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/compress-commons/node_modules/buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, + "node_modules/compress-commons/node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/compress-commons/node_modules/readable-stream": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.7.0.tgz", + "integrity": "sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg==", + "dev": true, + "dependencies": { + "abort-controller": "^3.0.0", + "buffer": "^6.0.3", + "events": "^3.3.0", + "process": "^0.11.10", + "string_decoder": "^1.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true + }, "node_modules/content-disposition": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.0.1.tgz", @@ -1352,6 +3146,12 @@ "node": ">=6.6.0" } }, + "node_modules/core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", + "dev": true + }, "node_modules/cors": { "version": "2.8.6", "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.6.tgz", @@ -1368,6 +3168,98 @@ "url": "https://opencollective.com/express" } }, + "node_modules/crc-32": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/crc-32/-/crc-32-1.2.2.tgz", + "integrity": "sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==", + "dev": true, + "bin": { + "crc32": "bin/crc32.njs" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/crc32-stream": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/crc32-stream/-/crc32-stream-6.0.0.tgz", + "integrity": "sha512-piICUB6ei4IlTv1+653yq5+KoqfBYmj9bw6LqXoOneTMDXk5nM1qt12mFW1caG3LlJXEKW1Bp0WggEmIfQB34g==", + "dev": true, + "dependencies": { + "crc-32": "^1.2.0", + "readable-stream": "^4.0.0" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/crc32-stream/node_modules/buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, + "node_modules/crc32-stream/node_modules/readable-stream": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.7.0.tgz", + "integrity": "sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg==", + "dev": true, + "dependencies": { + "abort-controller": "^3.0.0", + "buffer": "^6.0.3", + "events": "^3.3.0", + "process": "^0.11.10", + "string_decoder": "^1.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/create-wdio": { + "version": "9.21.0", + "resolved": "https://registry.npmjs.org/create-wdio/-/create-wdio-9.21.0.tgz", + "integrity": "sha512-L6gsQLArY3AH5uTGpf3VfUezIsmZKufkF3ixSWqCuA/m458YVKeGghu1bBOWBdDIzqa6GX4e29dv0uVam0CTpw==", + "dev": true, + "dependencies": { + "chalk": "^5.3.0", + "commander": "^14.0.0", + "cross-spawn": "^7.0.3", + "ejs": "^3.1.10", + "execa": "^9.6.0", + "import-meta-resolve": "^4.1.0", + "inquirer": "^12.7.0", + "normalize-package-data": "^7.0.0", + "read-pkg-up": "^10.1.0", + "recursive-readdir": "^2.2.3", + "semver": "^7.6.3", + "type-fest": "^4.41.0", + "yargs": "^17.7.2" + }, + "bin": { + "create-wdio": "bin/wdio.js" + }, + "engines": { + "node": ">=12.0.0" + } + }, "node_modules/cross-spawn": { "version": "7.0.6", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", @@ -1381,12 +3273,61 @@ "node": ">= 8" } }, + "node_modules/css-select": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-5.2.2.tgz", + "integrity": "sha512-TizTzUddG/xYLA3NXodFM0fSbNizXjOKhqiQQwvhlspadZokn1KDy0NZFS0wuEubIYAV5/c1/lAr0TaaFXEXzw==", + "dev": true, + "dependencies": { + "boolbase": "^1.0.0", + "css-what": "^6.1.0", + "domhandler": "^5.0.2", + "domutils": "^3.0.1", + "nth-check": "^2.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/css-shorthand-properties": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/css-shorthand-properties/-/css-shorthand-properties-1.1.2.tgz", + "integrity": "sha512-C2AugXIpRGQTxaCW0N7n5jD/p5irUmCrwl03TrnMFBHDbdq44CFWR2zO7rK9xPN4Eo3pUxC4vQzQgbIpzrD1PQ==", + "dev": true + }, + "node_modules/css-value": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/css-value/-/css-value-0.0.1.tgz", + "integrity": "sha512-FUV3xaJ63buRLgHrLQVlVgQnQdR4yqdLGaDu7g8CQcWjInDfM9plBTPI9FRfpahju1UBSaMckeb2/46ApS/V1Q==", + "dev": true + }, + "node_modules/css-what": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.2.2.tgz", + "integrity": "sha512-u/O3vwbptzhMs3L1fQE82ZSLHQQfto5gyZzwteVIEyeaY5Fc7R4dapF/BvRoSYFeqfBk4m0V1Vafq5Pjv25wvA==", + "dev": true, + "engines": { + "node": ">= 6" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, "node_modules/csstype": { "version": "3.2.3", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", "dev": true }, + "node_modules/data-uri-to-buffer": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-6.0.2.tgz", + "integrity": "sha512-7hvf7/GW8e86rW0ptuwS3OcBGDjIi6SZva7hCyWC0yYry2cOPmLIjXAUHI6DK2HsnwJd9ifmt57i8eV2n4YNpw==", + "dev": true, + "engines": { + "node": ">= 14" + } + }, "node_modules/debug": { "version": "4.4.3", "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", @@ -1403,6 +3344,18 @@ } } }, + "node_modules/decamelize": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-6.0.1.tgz", + "integrity": "sha512-G7Cqgaelq68XHJNGlZ7lrNQyhZGsFqpwtGFexqUv4IQdjKoSYF7ipZ9UuTJZUSQXFj/XaoBLuEVIVqr8EJngEQ==", + "dev": true, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/decompress-response": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", @@ -1417,6 +3370,15 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/deep-eql": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-5.0.2.tgz", + "integrity": "sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==", + "dev": true, + "engines": { + "node": ">=6" + } + }, "node_modules/deep-extend": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", @@ -1425,6 +3387,42 @@ "node": ">=4.0.0" } }, + "node_modules/deepmerge-ts": { + "version": "7.1.5", + "resolved": "https://registry.npmjs.org/deepmerge-ts/-/deepmerge-ts-7.1.5.tgz", + "integrity": "sha512-HOJkrhaYsweh+W+e74Yn7YStZOilkoPb6fycpwNLKzSPtruFs48nYis0zy5yJz1+ktUhHxoRDJ27RQAWLIJVJw==", + "dev": true, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/defaults": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.4.tgz", + "integrity": "sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==", + "dev": true, + "optional": true, + "dependencies": { + "clone": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/degenerator": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/degenerator/-/degenerator-5.0.1.tgz", + "integrity": "sha512-TllpMR/t0M5sqCXfj85i4XaAzxmS5tVA16dqvdkMwGmzI+dXLXnw3J+3Vdv7VKw+ThlTMboK6i9rnZ6Nntj5CQ==", + "dev": true, + "dependencies": { + "ast-types": "^0.13.4", + "escodegen": "^2.1.0", + "esprima": "^4.0.1" + }, + "engines": { + "node": ">= 14" + } + }, "node_modules/delayed-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", @@ -1449,11 +3447,87 @@ "node": ">=8" } }, + "node_modules/diff": { + "version": "8.0.3", + "resolved": "https://registry.npmjs.org/diff/-/diff-8.0.3.tgz", + "integrity": "sha512-qejHi7bcSD4hQAZE0tNAawRK1ZtafHDmMTMkrrIGgSLl7hTnQHmKCeB45xAcbfTqK2zowkM3j3bHt/4b/ARbYQ==", + "dev": true, + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/dom-serializer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", + "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", + "dev": true, + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.2", + "entities": "^4.2.0" + }, + "funding": { + "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" + } + }, + "node_modules/domelementtype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", + "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ] + }, + "node_modules/domhandler": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", + "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", + "dev": true, + "dependencies": { + "domelementtype": "^2.3.0" + }, + "engines": { + "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" + } + }, "node_modules/dompurify": { "version": "2.5.8", "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-2.5.8.tgz", "integrity": "sha512-o1vSNgrmYMQObbSSvF/1brBYEQPHhV1+gsmrusO7/GXtp1T9rCS8cXFqVxK/9crT1jA6Ccv+5MTSjBNqr7Sovw==" }, + "node_modules/domutils": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.2.2.tgz", + "integrity": "sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==", + "dev": true, + "dependencies": { + "dom-serializer": "^2.0.0", + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3" + }, + "funding": { + "url": "https://github.com/fb55/domutils?sponsor=1" + } + }, + "node_modules/dotenv": { + "version": "17.3.1", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-17.3.1.tgz", + "integrity": "sha512-IO8C/dzEb6O3F9/twg6ZLXz164a2fhTnEWb95H23Dm4OuN+92NmEAlTrupP9VW6Jm3sO26tQlqyvyi4CsnY9GA==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, "node_modules/dunder-proto": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", @@ -1467,11 +3541,113 @@ "node": ">= 0.4" } }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "dev": true + }, + "node_modules/easy-table": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/easy-table/-/easy-table-1.2.0.tgz", + "integrity": "sha512-OFzVOv03YpvtcWGe5AayU5G2hgybsg3iqA6drU8UaoZyB9jLGMTrz9+asnLp/E+6qPh88yEI1gvyZFZ41dmgww==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "optionalDependencies": { + "wcwidth": "^1.0.1" + } + }, + "node_modules/edge-paths": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/edge-paths/-/edge-paths-3.0.5.tgz", + "integrity": "sha512-sB7vSrDnFa4ezWQk9nZ/n0FdpdUuC6R1EOrlU3DL+bovcNFK28rqu2emmAUjujYEJTWIgQGqgVVWUZXMnc8iWg==", + "dev": true, + "dependencies": { + "@types/which": "^2.0.1", + "which": "^2.0.2" + }, + "engines": { + "node": ">=14.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/shirshak55" + } + }, + "node_modules/edgedriver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/edgedriver/-/edgedriver-6.3.0.tgz", + "integrity": "sha512-ggEQL+oEyIcM4nP2QC3AtCQ04o4kDNefRM3hja0odvlPSnsaxiruMxEZ93v3gDCKWYW6BXUr51PPradb+3nffw==", + "dev": true, + "hasInstallScript": true, + "dependencies": { + "@wdio/logger": "^9.18.0", + "@zip.js/zip.js": "^2.8.11", + "decamelize": "^6.0.1", + "edge-paths": "^3.0.5", + "fast-xml-parser": "^5.3.3", + "http-proxy-agent": "^7.0.2", + "https-proxy-agent": "^7.0.6", + "which": "^6.0.0" + }, + "bin": { + "edgedriver": "bin/edgedriver.js" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/edgedriver/node_modules/isexe": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-4.0.0.tgz", + "integrity": "sha512-FFUtZMpoZ8RqHS3XeXEmHWLA4thH+ZxCv2lOiPIn1Xc7CxrqhWzNSDzD+/chS/zbYezmiwWLdQC09JdQKmthOw==", + "dev": true, + "engines": { + "node": ">=20" + } + }, + "node_modules/edgedriver/node_modules/which": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/which/-/which-6.0.1.tgz", + "integrity": "sha512-oGLe46MIrCRqX7ytPUf66EAYvdeMIZYn3WaocqqKZAxrBpkqHfL/qvTyJ/bTk5+AqHCjXmrv3CEWgy368zhRUg==", + "dev": true, + "dependencies": { + "isexe": "^4.0.0" + }, + "bin": { + "node-which": "bin/which.js" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, "node_modules/ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" }, + "node_modules/ejs": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.10.tgz", + "integrity": "sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==", + "dev": true, + "dependencies": { + "jake": "^10.8.5" + }, + "bin": { + "ejs": "bin/cli.js" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true + }, "node_modules/encodeurl": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", @@ -1480,6 +3656,31 @@ "node": ">= 0.8" } }, + "node_modules/encoding-sniffer": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/encoding-sniffer/-/encoding-sniffer-0.2.1.tgz", + "integrity": "sha512-5gvq20T6vfpekVtqrYQsSCFZ1wEg5+wW0/QaZMWkFr6BqD3NfKs0rLCx4rrVlSWJeZb5NBJgVLswK/w2MWU+Gw==", + "dev": true, + "dependencies": { + "iconv-lite": "^0.6.3", + "whatwg-encoding": "^3.1.1" + }, + "funding": { + "url": "https://github.com/fb55/encoding-sniffer?sponsor=1" + } + }, + "node_modules/encoding-sniffer/node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "dev": true, + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/end-of-stream": { "version": "1.4.5", "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.5.tgz", @@ -1488,6 +3689,27 @@ "once": "^1.4.0" } }, + "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, + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/error-ex": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.4.tgz", + "integrity": "sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==", + "dev": true, + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, "node_modules/es-define-property": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", @@ -1570,11 +3792,84 @@ "@esbuild/win32-x64": "0.25.12" } }, + "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, + "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==" }, + "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, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/escodegen": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.1.0.tgz", + "integrity": "sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==", + "dev": true, + "dependencies": { + "esprima": "^4.0.1", + "estraverse": "^5.2.0", + "esutils": "^2.0.2" + }, + "bin": { + "escodegen": "bin/escodegen.js", + "esgenerate": "bin/esgenerate.js" + }, + "engines": { + "node": ">=6.0" + }, + "optionalDependencies": { + "source-map": "~0.6.1" + } + }, + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true, + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "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, + "engines": { + "node": ">=4.0" + } + }, + "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, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/etag": { "version": "1.8.1", "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", @@ -1583,6 +3878,33 @@ "node": ">= 0.6" } }, + "node_modules/event-target-shim": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", + "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", + "dev": true, + "engines": { + "node": ">=0.8.x" + } + }, + "node_modules/events-universal": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/events-universal/-/events-universal-1.0.1.tgz", + "integrity": "sha512-LUd5euvbMLpwOF8m6ivPCbhQeSiYVNb8Vs0fQ8QjXo0JTkEHpz8pxdQf0gStltaPpw0Cca8b39KxvK9cfKRiAw==", + "dev": true, + "dependencies": { + "bare-events": "^2.7.0" + } + }, "node_modules/eventsource": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/eventsource/-/eventsource-4.1.0.tgz", @@ -1602,6 +3924,44 @@ "node": ">=18.0.0" } }, + "node_modules/execa": { + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-9.6.1.tgz", + "integrity": "sha512-9Be3ZoN4LmYR90tUoVu2te2BsbzHfhJyfEiAVfz7N5/zv+jduIfLrV2xdQXOHbaD6KgpGdO9PRPM1Y4Q9QkPkA==", + "dev": true, + "dependencies": { + "@sindresorhus/merge-streams": "^4.0.0", + "cross-spawn": "^7.0.6", + "figures": "^6.1.0", + "get-stream": "^9.0.0", + "human-signals": "^8.0.1", + "is-plain-obj": "^4.1.0", + "is-stream": "^4.0.1", + "npm-run-path": "^6.0.0", + "pretty-ms": "^9.2.0", + "signal-exit": "^4.1.0", + "strip-final-newline": "^4.0.0", + "yoctocolors": "^2.1.1" + }, + "engines": { + "node": "^18.19.0 || >=20.5.0" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/exit-hook": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/exit-hook/-/exit-hook-4.0.0.tgz", + "integrity": "sha512-Fqs7ChZm72y40wKjOFXBKg7nJZvQJmewP5/7LtePDdnah/+FH9Hp5sgMujSCMPXlxOAW2//1jrW9pnsY7o20vQ==", + "dev": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/expand-template": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz", @@ -1610,6 +3970,95 @@ "node": ">=6" } }, + "node_modules/expect": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/expect/-/expect-30.2.0.tgz", + "integrity": "sha512-u/feCi0GPsI+988gU2FLcsHyAHTU0MX1Wg68NhAnN7z/+C5wqG+CY8J53N9ioe8RXgaoz0nBR/TYMf3AycUuPw==", + "dev": true, + "dependencies": { + "@jest/expect-utils": "30.2.0", + "@jest/get-type": "30.1.0", + "jest-matcher-utils": "30.2.0", + "jest-message-util": "30.2.0", + "jest-mock": "30.2.0", + "jest-util": "30.2.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/expect-webdriverio": { + "version": "5.6.4", + "resolved": "https://registry.npmjs.org/expect-webdriverio/-/expect-webdriverio-5.6.4.tgz", + "integrity": "sha512-Bkoqs+39fHwjos51qab7ZWmvZrYNBbzgSAIykH2CrgLOLhHJXzC30DP9lZq2MsmaUsbBnN5c5m8VqAhOHTrCRw==", + "dev": true, + "dependencies": { + "@vitest/snapshot": "^4.0.16", + "deep-eql": "^5.0.2", + "expect": "^30.2.0", + "jest-matcher-utils": "^30.2.0" + }, + "engines": { + "node": ">=20" + }, + "peerDependencies": { + "@wdio/globals": "^9.0.0", + "@wdio/logger": "^9.0.0", + "webdriverio": "^9.0.0" + }, + "peerDependenciesMeta": { + "@wdio/globals": { + "optional": false + }, + "@wdio/logger": { + "optional": false + }, + "webdriverio": { + "optional": false + } + } + }, + "node_modules/expect-webdriverio/node_modules/@vitest/pretty-format": { + "version": "4.0.18", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.0.18.tgz", + "integrity": "sha512-P24GK3GulZWC5tz87ux0m8OADrQIUVDPIjjj65vBXYG17ZeU3qD7r+MNZ1RNv4l8CGU2vtTRqixrOi9fYk/yKw==", + "dev": true, + "dependencies": { + "tinyrainbow": "^3.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/expect-webdriverio/node_modules/@vitest/snapshot": { + "version": "4.0.18", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-4.0.18.tgz", + "integrity": "sha512-PCiV0rcl7jKQjbgYqjtakly6T1uwv/5BQ9SwBLekVg/EaYeQFPiXcgrC2Y7vDMA8dM1SUEAEV82kgSQIlXNMvA==", + "dev": true, + "dependencies": { + "@vitest/pretty-format": "4.0.18", + "magic-string": "^0.30.21", + "pathe": "^2.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/expect-webdriverio/node_modules/pathe": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", + "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", + "dev": true + }, + "node_modules/expect-webdriverio/node_modules/tinyrainbow": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-3.0.3.tgz", + "integrity": "sha512-PSkbLUoxOFRzJYjjxHJt9xro7D+iilgMX/C9lawzVuYiIdcihh9DXmVibBe8lmcFrRi/VzlPjBxbN7rH24q8/Q==", + "dev": true, + "engines": { + "node": ">=14.0.0" + } + }, "node_modules/express": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/express/-/express-5.2.1.tgz", @@ -1669,11 +4118,52 @@ "express": ">= 4.11" } }, + "node_modules/extract-zip": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-2.0.1.tgz", + "integrity": "sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==", + "dev": true, + "dependencies": { + "debug": "^4.1.1", + "get-stream": "^5.1.0", + "yauzl": "^2.10.0" + }, + "bin": { + "extract-zip": "cli.js" + }, + "engines": { + "node": ">= 10.17.0" + }, + "optionalDependencies": { + "@types/yauzl": "^2.9.1" + } + }, + "node_modules/extract-zip/node_modules/get-stream": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", + "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", + "dev": true, + "dependencies": { + "pump": "^3.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "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==" }, + "node_modules/fast-fifo": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/fast-fifo/-/fast-fifo-1.3.2.tgz", + "integrity": "sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ==", + "dev": true + }, "node_modules/fast-uri": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.0.tgz", @@ -1689,6 +4179,46 @@ } ] }, + "node_modules/fast-xml-builder": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fast-xml-builder/-/fast-xml-builder-1.0.0.tgz", + "integrity": "sha512-fpZuDogrAgnyt9oDDz+5DBz0zgPdPZz6D4IR7iESxRXElrlGTRkHJ9eEt+SACRJwT0FNFrt71DFQIUFBJfX/uQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ] + }, + "node_modules/fast-xml-parser": { + "version": "5.4.2", + "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-5.4.2.tgz", + "integrity": "sha512-pw/6pIl4k0CSpElPEJhDppLzaixDEuWui2CUQQBH/ECDf7+y6YwA4Gf7Tyb0Rfe4DIMuZipYj4AEL0nACKglvQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "dependencies": { + "fast-xml-builder": "^1.0.0", + "strnum": "^2.1.2" + }, + "bin": { + "fxparser": "src/cli/cli.js" + } + }, + "node_modules/fd-slicer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", + "integrity": "sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==", + "dev": true, + "dependencies": { + "pend": "~1.2.0" + } + }, "node_modules/fdir": { "version": "6.5.0", "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", @@ -1706,11 +4236,59 @@ } } }, + "node_modules/figures": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-6.1.0.tgz", + "integrity": "sha512-d+l3qxjSesT4V7v2fh+QnmFnUWv9lSpjarhShNTgBOfA0ttejbQUAlHLitbjkoRiDulW0OPoQPYIGhIC8ohejg==", + "dev": true, + "dependencies": { + "is-unicode-supported": "^2.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/file-uri-to-path": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==" }, + "node_modules/filelist": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.6.tgz", + "integrity": "sha512-5giy2PkLYY1cP39p17Ech+2xlpTRL9HLspOfEgm0L6CwBXBTgsK5ou0JtzYuepxkaQ/tvhCFIJ5uXo0OrM2DxA==", + "dev": true, + "dependencies": { + "minimatch": "^5.0.1" + } + }, + "node_modules/filelist/node_modules/minimatch": { + "version": "5.1.9", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.9.tgz", + "integrity": "sha512-7o1wEA2RyMP7Iu7GNba9vc0RWWGACJOCZBJX2GJWip0ikV+wcOsgVuY9uE8CPiyQhkGFSlhuSkZPavN7u1c2Fw==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/finalhandler": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.1.tgz", @@ -1731,6 +4309,31 @@ "url": "https://opencollective.com/express" } }, + "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, + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", + "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", + "dev": true, + "bin": { + "flat": "cli.js" + } + }, "node_modules/follow-redirects": { "version": "1.15.11", "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.11.tgz", @@ -1750,6 +4353,22 @@ } } }, + "node_modules/foreground-child": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", + "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.6", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/form-data": { "version": "4.0.5", "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz", @@ -1805,6 +4424,12 @@ "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==" }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true + }, "node_modules/fsevents": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", @@ -1827,6 +4452,36 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/geckodriver": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/geckodriver/-/geckodriver-6.1.0.tgz", + "integrity": "sha512-ZRXLa4ZaYTTgUO4Eefw+RsQCleugU2QLb1ME7qTYxxuRj51yAhfnXaItXNs5/vUzfIaDHuZ+YnSF005hfp07nQ==", + "dev": true, + "hasInstallScript": true, + "dependencies": { + "@wdio/logger": "^9.18.0", + "@zip.js/zip.js": "^2.8.11", + "decamelize": "^6.0.1", + "http-proxy-agent": "^7.0.2", + "https-proxy-agent": "^7.0.6", + "modern-tar": "^0.7.2" + }, + "bin": { + "geckodriver": "bin/geckodriver.js" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true, + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, "node_modules/get-intrinsic": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", @@ -1850,6 +4505,18 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/get-port": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/get-port/-/get-port-7.1.0.tgz", + "integrity": "sha512-QB9NKEeDg3xxVwCCwJQ9+xycaz6pBB6iQ76wiWMl1927n0Kir6alPiP+yuiICLLU4jpMe08dXfpebuQppFA2zw==", + "dev": true, + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/get-proto": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", @@ -1862,11 +4529,86 @@ "node": ">= 0.4" } }, + "node_modules/get-stream": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-9.0.1.tgz", + "integrity": "sha512-kVCxPF3vQM/N0B1PmoqVUqgHP+EeVjmZSQn+1oCRPxd2P21P2F19lIgbR3HBosbB1PUhOAoctJnfEn2GbN2eZA==", + "dev": true, + "dependencies": { + "@sec-ant/readable-stream": "^0.4.1", + "is-stream": "^4.0.1" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/get-tsconfig": { + "version": "4.13.6", + "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.13.6.tgz", + "integrity": "sha512-shZT/QMiSHc/YBLxxOkMtgSid5HFoauqCE3/exfsEcwg1WkeqjG+V40yBbBrsD+jW2HDXcs28xOfcbm2jI8Ddw==", + "dev": true, + "dependencies": { + "resolve-pkg-maps": "^1.0.0" + }, + "funding": { + "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" + } + }, + "node_modules/get-uri": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/get-uri/-/get-uri-6.0.5.tgz", + "integrity": "sha512-b1O07XYq8eRuVzBNgJLstU6FYc1tS6wnMtF1I1D9lE8LxZSOGZ7LhxN54yPP6mGw5f2CkXY2BQUL9Fx41qvcIg==", + "dev": true, + "dependencies": { + "basic-ftp": "^5.0.2", + "data-uri-to-buffer": "^6.0.2", + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, "node_modules/github-from-package": { "version": "0.0.0", "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz", "integrity": "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==" }, + "node_modules/glob": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", + "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", + "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", + "dev": true, + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "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, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/gopd": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", @@ -1878,6 +4620,27 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true + }, + "node_modules/grapheme-splitter": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz", + "integrity": "sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==", + "dev": true + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/has-symbols": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", @@ -1914,6 +4677,15 @@ "node": ">= 0.4" } }, + "node_modules/he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", + "dev": true, + "bin": { + "he": "bin/he" + } + }, "node_modules/hono": { "version": "4.11.9", "resolved": "https://registry.npmjs.org/hono/-/hono-4.11.9.tgz", @@ -1922,6 +4694,55 @@ "node": ">=16.9.0" } }, + "node_modules/hosted-git-info": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-8.1.0.tgz", + "integrity": "sha512-Rw/B2DNQaPBICNXEm8balFz9a6WpZrkCGpcWFpy7nCj+NyhSdqXipmfvtmWt9xGfp0wZnBxB+iVpLmQMYt47Tw==", + "dev": true, + "dependencies": { + "lru-cache": "^10.0.1" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/htmlfy": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/htmlfy/-/htmlfy-0.8.1.tgz", + "integrity": "sha512-xWROBw9+MEGwxpotll0h672KCaLrKKiCYzsyN8ZgL9cQbVumFnyvsk2JqiB9ELAV1GLj1GG/jxZUjV9OZZi/yQ==", + "dev": true + }, + "node_modules/htmlparser2": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-10.1.0.tgz", + "integrity": "sha512-VTZkM9GWRAtEpveh7MSF6SjjrpNVNNVJfFup7xTY3UpFtm67foy9HDVXneLtFVt4pMz5kZtgNcvCniNFb1hlEQ==", + "dev": true, + "funding": [ + "https://github.com/fb55/htmlparser2?sponsor=1", + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3", + "domutils": "^3.2.2", + "entities": "^7.0.1" + } + }, + "node_modules/htmlparser2/node_modules/entities": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-7.0.1.tgz", + "integrity": "sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA==", + "dev": true, + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, "node_modules/http-errors": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz", @@ -1941,6 +4762,41 @@ "url": "https://opencollective.com/express" } }, + "node_modules/http-proxy-agent": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", + "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", + "dev": true, + "dependencies": { + "agent-base": "^7.1.0", + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/https-proxy-agent": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", + "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", + "dev": true, + "dependencies": { + "agent-base": "^7.1.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/human-signals": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-8.0.1.tgz", + "integrity": "sha512-eKCa6bwnJhvxj14kZk5NCPc6Hb6BdsU9DZcOnmQKSnO1VKrfV0zCvtttPZUsBvjmNDn8rpcJfpwSYnHBjc95MQ==", + "dev": true, + "engines": { + "node": ">=18.18.0" + } + }, "node_modules/iconv-lite": { "version": "0.7.2", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.2.tgz", @@ -1975,6 +4831,33 @@ } ] }, + "node_modules/immediate": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz", + "integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==", + "dev": true + }, + "node_modules/import-meta-resolve": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/import-meta-resolve/-/import-meta-resolve-4.2.0.tgz", + "integrity": "sha512-Iqv2fzaTQN28s/FwZAoFq0ZSs/7hMAHJVX+w8PZl3cY19Pxk6jFFalxQoIfW2826i/fDLXv8IiEZRIT0lDuWcg==", + "dev": true, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", + "dev": true, + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, "node_modules/inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", @@ -1985,6 +4868,32 @@ "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==" }, + "node_modules/inquirer": { + "version": "12.11.1", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-12.11.1.tgz", + "integrity": "sha512-9VF7mrY+3OmsAfjH3yKz/pLbJ5z22E23hENKw3/LNSaA/sAt3v49bDRY+Ygct1xwuKT+U+cBfTzjCPySna69Qw==", + "dev": true, + "dependencies": { + "@inquirer/ansi": "^1.0.2", + "@inquirer/core": "^10.3.2", + "@inquirer/prompts": "^7.10.1", + "@inquirer/type": "^3.0.10", + "mute-stream": "^2.0.0", + "run-async": "^4.0.6", + "rxjs": "^7.8.2" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, "node_modules/ip-address": { "version": "10.0.1", "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.0.1.tgz", @@ -2001,16 +4910,418 @@ "node": ">= 0.10" } }, + "node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", + "dev": true + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-plain-obj": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-4.1.0.tgz", + "integrity": "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/is-promise": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz", "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==" }, + "node_modules/is-stream": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-4.0.1.tgz", + "integrity": "sha512-Dnz92NInDqYckGEUJv689RbRiTSEHCQ7wOVeALbkOz999YpqT46yMRIGtSNl2iCL1waAZSx40+h59NV/EwzV/A==", + "dev": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-unicode-supported": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-2.1.0.tgz", + "integrity": "sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ==", + "dev": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "dev": true + }, "node_modules/isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" }, + "node_modules/jackspeak": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", + "dev": true, + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, + "node_modules/jake": { + "version": "10.9.4", + "resolved": "https://registry.npmjs.org/jake/-/jake-10.9.4.tgz", + "integrity": "sha512-wpHYzhxiVQL+IV05BLE2Xn34zW1S223hvjtqk0+gsPrwd/8JNLXJgZZM/iPFsYc1xyphF+6M6EvdE5E9MBGkDA==", + "dev": true, + "dependencies": { + "async": "^3.2.6", + "filelist": "^1.0.4", + "picocolors": "^1.1.1" + }, + "bin": { + "jake": "bin/cli.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/jest-diff": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-30.2.0.tgz", + "integrity": "sha512-dQHFo3Pt4/NLlG5z4PxZ/3yZTZ1C7s9hveiOj+GCN+uT109NC2QgsoVZsVOAvbJ3RgKkvyLGXZV9+piDpWbm6A==", + "dev": true, + "dependencies": { + "@jest/diff-sequences": "30.0.1", + "@jest/get-type": "30.1.0", + "chalk": "^4.1.2", + "pretty-format": "30.2.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-diff/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, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-diff/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/jest-diff/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, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-matcher-utils": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-30.2.0.tgz", + "integrity": "sha512-dQ94Nq4dbzmUWkQ0ANAWS9tBRfqCrn0bV9AMYdOi/MHW726xn7eQmMeRTpX2ViC00bpNaWXq+7o4lIQ3AX13Hg==", + "dev": true, + "dependencies": { + "@jest/get-type": "30.1.0", + "chalk": "^4.1.2", + "jest-diff": "30.2.0", + "pretty-format": "30.2.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-matcher-utils/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, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-matcher-utils/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/jest-matcher-utils/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, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-message-util": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-30.2.0.tgz", + "integrity": "sha512-y4DKFLZ2y6DxTWD4cDe07RglV88ZiNEdlRfGtqahfbIjfsw1nMCPx49Uev4IA/hWn3sDKyAnSPwoYSsAEdcimw==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@jest/types": "30.2.0", + "@types/stack-utils": "^2.0.3", + "chalk": "^4.1.2", + "graceful-fs": "^4.2.11", + "micromatch": "^4.0.8", + "pretty-format": "30.2.0", + "slash": "^3.0.0", + "stack-utils": "^2.0.6" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-message-util/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, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-message-util/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/jest-message-util/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, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-mock": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-30.2.0.tgz", + "integrity": "sha512-JNNNl2rj4b5ICpmAcq+WbLH83XswjPbjH4T7yvGzfAGCPh1rw+xVNbtk+FnRslvt9lkCcdn9i1oAoKUuFsOxRw==", + "dev": true, + "dependencies": { + "@jest/types": "30.2.0", + "@types/node": "*", + "jest-util": "30.2.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-regex-util": { + "version": "30.0.1", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-30.0.1.tgz", + "integrity": "sha512-jHEQgBXAgc+Gh4g0p3bCevgRCVRkB4VB70zhoAE48gxeSr1hfUOsM/C2WoJgVL7Eyg//hudYENbm3Ne+/dRVVA==", + "dev": true, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-util": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-30.2.0.tgz", + "integrity": "sha512-QKNsM0o3Xe6ISQU869e+DhG+4CK/48aHYdJZGlFQVTjnbvgpcKyxpzk29fGiO7i/J8VENZ+d2iGnSsvmuHywlA==", + "dev": true, + "dependencies": { + "@jest/types": "30.2.0", + "@types/node": "*", + "chalk": "^4.1.2", + "ci-info": "^4.2.0", + "graceful-fs": "^4.2.11", + "picomatch": "^4.0.2" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-util/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, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-util/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/jest-util/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, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jiti": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.6.1.tgz", + "integrity": "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==", + "dev": true, + "bin": { + "jiti": "lib/jiti-cli.mjs" + } + }, "node_modules/jose": { "version": "6.1.3", "resolved": "https://registry.npmjs.org/jose/-/jose-6.1.3.tgz", @@ -2024,11 +5335,308 @@ "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" }, + "node_modules/js-yaml": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", + "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", + "dev": true, + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/json-parse-even-better-errors": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-3.0.2.tgz", + "integrity": "sha512-fi0NG4bPjCHunUJffmLd0gxssIgkNmArMvis4iNah6Owg1MCJjWhEcDLmsK6iGkJq3tHwbDkTlce70/tmXN4cQ==", + "dev": true, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, "node_modules/json-schema-typed": { "version": "8.0.2", "resolved": "https://registry.npmjs.org/json-schema-typed/-/json-schema-typed-8.0.2.tgz", "integrity": "sha512-fQhoXdcvc3V28x7C7BMs4P5+kNlgUURe2jmUT1T//oBRMDrqy1QPelJimwZGo7Hg9VPV3EQV5Bnq4hbFy2vetA==" }, + "node_modules/jszip": { + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.10.1.tgz", + "integrity": "sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==", + "dev": true, + "dependencies": { + "lie": "~3.3.0", + "pako": "~1.0.2", + "readable-stream": "~2.3.6", + "setimmediate": "^1.0.5" + } + }, + "node_modules/jszip/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "dev": true, + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/jszip/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + }, + "node_modules/jszip/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/lazystream": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/lazystream/-/lazystream-1.0.1.tgz", + "integrity": "sha512-b94GiNHQNy6JNTrt5w6zNyffMrNkXZb3KTkCZJb2V1xaEGCk093vkZ2jk3tpaeP33/OiXC+WvK9AxUebnf5nbw==", + "dev": true, + "dependencies": { + "readable-stream": "^2.0.5" + }, + "engines": { + "node": ">= 0.6.3" + } + }, + "node_modules/lazystream/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "dev": true, + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/lazystream/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + }, + "node_modules/lazystream/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/lie": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz", + "integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==", + "dev": true, + "dependencies": { + "immediate": "~3.0.5" + } + }, + "node_modules/lines-and-columns": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-2.0.4.tgz", + "integrity": "sha512-wM1+Z03eypVAVUCE7QdSqpVIvelbOakn1M0bPDoA4SGWPx3sNDVUiMo3L6To6WWGClB7VyXnhQ4Sn7gxiJbE6A==", + "dev": true, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + } + }, + "node_modules/locate-app": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/locate-app/-/locate-app-2.5.0.tgz", + "integrity": "sha512-xIqbzPMBYArJRmPGUZD9CzV9wOqmVtQnaAn3wrj3s6WYW0bQvPI7x+sPYUGmDTYMHefVK//zc6HEYZ1qnxIK+Q==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://buymeacoffee.com/hejny" + }, + { + "type": "github", + "url": "https://github.com/hejny/locate-app/blob/main/README.md#%EF%B8%8F-contributing" + } + ], + "dependencies": { + "@promptbook/utils": "0.69.5", + "type-fest": "4.26.0", + "userhome": "1.0.1" + } + }, + "node_modules/locate-app/node_modules/type-fest": { + "version": "4.26.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.26.0.tgz", + "integrity": "sha512-OduNjVJsFbifKb57UqZ2EMP1i4u64Xwow3NYXUtBbD4vIwJdQd4+xl8YDou1dlm4DVrtwT/7Ky8z8WyCULVfxw==", + "dev": true, + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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, + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash": { + "version": "4.17.23", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.23.tgz", + "integrity": "sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w==", + "dev": true + }, + "node_modules/lodash.clonedeep": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", + "integrity": "sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ==", + "dev": true + }, + "node_modules/lodash.flattendeep": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.flattendeep/-/lodash.flattendeep-4.4.0.tgz", + "integrity": "sha512-uHaJFihxmJcEX3kT4I23ABqKKalJ/zDrDg0lsFtc1h+3uw49SIJ5beyhx5ExVRti3AvKoOJngIj7xz3oylPdWQ==", + "dev": true + }, + "node_modules/lodash.pickby": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/lodash.pickby/-/lodash.pickby-4.6.0.tgz", + "integrity": "sha512-AZV+GsS/6ckvPOVQPXSiFFacKvKB4kOQu6ynt9wz0F3LO4R9Ij4K1ddYsIytDpSgLz88JHd9P+oaLeej5/Sl7Q==", + "dev": true + }, + "node_modules/lodash.union": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/lodash.union/-/lodash.union-4.6.0.tgz", + "integrity": "sha512-c4pB2CdGrGdjMKYLA+XiRDO7Y0PRQbm/Gzg8qMj+QH+pFVAoTp5sBpO0odL3FjoPCGjK96p6qsP+yQoiLoOBcw==", + "dev": true + }, + "node_modules/lodash.zip": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/lodash.zip/-/lodash.zip-4.2.0.tgz", + "integrity": "sha512-C7IOaBBK/0gMORRBd8OETNx3kmOkgIWIPvyDpZSCTwUrpYmgZwJkjZeOD8ww4xbOUOs4/attY+pciKvadNfFbg==", + "dev": true + }, + "node_modules/log-symbols": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", + "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", + "dev": true, + "dependencies": { + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/log-symbols/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, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/log-symbols/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/log-symbols/node_modules/is-unicode-supported": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", + "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/log-symbols/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, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/loglevel": { + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/loglevel/-/loglevel-1.9.2.tgz", + "integrity": "sha512-HgMmCqIJSAKqo68l0rS2AanEWfkxaZ5wNiEFb5ggm08lDs9Xl2KxBlX3PTcaD2chBM1gXAYf491/M2Rv8Jwayg==", + "dev": true, + "engines": { + "node": ">= 0.6.0" + }, + "funding": { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/loglevel" + } + }, + "node_modules/loglevel-plugin-prefix": { + "version": "0.8.4", + "resolved": "https://registry.npmjs.org/loglevel-plugin-prefix/-/loglevel-plugin-prefix-0.8.4.tgz", + "integrity": "sha512-WpG9CcFAOjz/FtNht+QJeGpvVl/cdR6P0z6OcXSkr8wFJOsV2GRj2j10JLfjuA4aYkcKCNIEqRGCyTife9R8/g==", + "dev": true + }, "node_modules/loose-envify": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", @@ -2040,6 +5648,21 @@ "loose-envify": "cli.js" } }, + "node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true + }, + "node_modules/magic-string": { + "version": "0.30.21", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", + "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", + "dev": true, + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, "node_modules/math-intrinsics": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", @@ -2067,6 +5690,31 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/micromatch/node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "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", @@ -2101,6 +5749,21 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/minimatch": { + "version": "9.0.9", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz", + "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.2" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/minimist": { "version": "1.2.8", "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", @@ -2109,16 +5772,266 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/minipass": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.3.tgz", + "integrity": "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==", + "dev": true, + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/mitt": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/mitt/-/mitt-3.0.1.tgz", + "integrity": "sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==", + "dev": true + }, "node_modules/mkdirp-classic": { "version": "0.5.3", "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==" }, + "node_modules/mocha": { + "version": "10.8.2", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.8.2.tgz", + "integrity": "sha512-VZlYo/WE8t1tstuRmqgeyBgCbJc/lEdopaa+axcKzTBJ+UIdlAB9XnmvTCAH4pwR4ElNInaedhEBmZD8iCSVEg==", + "dev": true, + "dependencies": { + "ansi-colors": "^4.1.3", + "browser-stdout": "^1.3.1", + "chokidar": "^3.5.3", + "debug": "^4.3.5", + "diff": "^5.2.0", + "escape-string-regexp": "^4.0.0", + "find-up": "^5.0.0", + "glob": "^8.1.0", + "he": "^1.2.0", + "js-yaml": "^4.1.0", + "log-symbols": "^4.1.0", + "minimatch": "^5.1.6", + "ms": "^2.1.3", + "serialize-javascript": "^6.0.2", + "strip-json-comments": "^3.1.1", + "supports-color": "^8.1.1", + "workerpool": "^6.5.1", + "yargs": "^16.2.0", + "yargs-parser": "^20.2.9", + "yargs-unparser": "^2.0.0" + }, + "bin": { + "_mocha": "bin/_mocha", + "mocha": "bin/mocha.js" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/mocha/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, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/mocha/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, + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/mocha/node_modules/cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "dev": true, + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "node_modules/mocha/node_modules/diff": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-5.2.2.tgz", + "integrity": "sha512-vtcDfH3TOjP8UekytvnHH1o1P4FcUdt4eQ1Y+Abap1tk/OB2MWQvcwS2ClCd1zuIhc3JKOx6p3kod8Vfys3E+A==", + "dev": true, + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/mocha/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 + }, + "node_modules/mocha/node_modules/glob": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz", + "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==", + "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^5.0.1", + "once": "^1.3.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/mocha/node_modules/minimatch": { + "version": "5.1.9", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.9.tgz", + "integrity": "sha512-7o1wEA2RyMP7Iu7GNba9vc0RWWGACJOCZBJX2GJWip0ikV+wcOsgVuY9uE8CPiyQhkGFSlhuSkZPavN7u1c2Fw==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/mocha/node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/mocha/node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/mocha/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/mocha/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, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/mocha/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, + "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" + } + }, + "node_modules/mocha/node_modules/yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "dev": true, + "dependencies": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/modern-tar": { + "version": "0.7.5", + "resolved": "https://registry.npmjs.org/modern-tar/-/modern-tar-0.7.5.tgz", + "integrity": "sha512-YTefgdpKKFgoTDbEUqXqgUJct2OG6/4hs4XWLsxcHkDLj/x/V8WmKIRppPnXP5feQ7d1vuYWSp3qKkxfwaFaxA==", + "dev": true, + "engines": { + "node": ">=18.0.0" + } + }, "node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" }, + "node_modules/mute-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-2.0.0.tgz", + "integrity": "sha512-WWdIxpyjEn+FhQJQQv9aQAYlHoNVdzIzUySNV1gHUPDSdZJ3yZn7pAAbQcV7B56Mvu881q9FZV+0Vx2xC44VWA==", + "dev": true, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, "node_modules/nanoid": { "version": "3.3.11", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", @@ -2150,6 +6063,15 @@ "node": ">= 0.6" } }, + "node_modules/netmask": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/netmask/-/netmask-2.0.2.tgz", + "integrity": "sha512-dBpDMdxv9Irdq66304OLfEmQ9tbNRFnFTuZiLo+bD+r332bBmMJ8GBLXklIXXgxd3+v9+KUnZaUR5PJMa75Gsg==", + "dev": true, + "engines": { + "node": ">= 0.4.0" + } + }, "node_modules/node-abi": { "version": "3.87.0", "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.87.0.tgz", @@ -2161,15 +6083,67 @@ "node": ">=10" } }, - "node_modules/node-abi/node_modules/semver": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", - "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", - "bin": { - "semver": "bin/semver.js" + "node_modules/normalize-package-data": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-7.0.1.tgz", + "integrity": "sha512-linxNAT6M0ebEYZOx2tO6vBEFsVgnPpv+AVjk0wJHfaUIbq31Jm3T6vvZaarnOeWDh8ShnwXuaAyM7WT3RzErA==", + "dev": true, + "dependencies": { + "hosted-git-info": "^8.0.0", + "semver": "^7.3.5", + "validate-npm-package-license": "^3.0.4" }, "engines": { - "node": ">=10" + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/npm-run-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-6.0.0.tgz", + "integrity": "sha512-9qny7Z9DsQU8Ou39ERsPU4OZQlSTP47ShQzuKZ6PRXpYLtIFgl/DEBYEXKlvcEa+9tHVcK8CF81Y2V72qaZhWA==", + "dev": true, + "dependencies": { + "path-key": "^4.0.0", + "unicorn-magic": "^0.3.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/npm-run-path/node_modules/path-key": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", + "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/nth-check": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", + "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", + "dev": true, + "dependencies": { + "boolbase": "^1.0.0" + }, + "funding": { + "url": "https://github.com/fb55/nth-check?sponsor=1" } }, "node_modules/object-assign": { @@ -2215,6 +6189,172 @@ "resolved": "https://registry.npmjs.org/orderedmap/-/orderedmap-2.1.1.tgz", "integrity": "sha512-TvAWxi0nDe1j/rtMcWcIj94+Ffe6n7zhow33h40SKxmsmozs6dz/e+EajymfoFcHd7sxNn8yHM8839uixMOV6g==" }, + "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, + "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, + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/pac-proxy-agent": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/pac-proxy-agent/-/pac-proxy-agent-7.2.0.tgz", + "integrity": "sha512-TEB8ESquiLMc0lV8vcd5Ql/JAKAoyzHFXaStwjkzpOpC5Yv+pIzLfHvjTSdf3vpa2bMiUQrg9i6276yn8666aA==", + "dev": true, + "dependencies": { + "@tootallnate/quickjs-emscripten": "^0.23.0", + "agent-base": "^7.1.2", + "debug": "^4.3.4", + "get-uri": "^6.0.1", + "http-proxy-agent": "^7.0.0", + "https-proxy-agent": "^7.0.6", + "pac-resolver": "^7.0.1", + "socks-proxy-agent": "^8.0.5" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/pac-resolver": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/pac-resolver/-/pac-resolver-7.0.1.tgz", + "integrity": "sha512-5NPgf87AT2STgwa2ntRMr45jTKrYBGkVU36yT0ig/n/GMAa3oPqhZfIQ2kMEimReg0+t9kZViDVZ83qfVUlckg==", + "dev": true, + "dependencies": { + "degenerator": "^5.0.0", + "netmask": "^2.0.2" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/package-json-from-dist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", + "dev": true + }, + "node_modules/pako": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", + "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==", + "dev": true + }, + "node_modules/parse-json": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-7.1.1.tgz", + "integrity": "sha512-SgOTCX/EZXtZxBE5eJ97P4yGM5n37BwRU+YMsH4vNzFqJV/oWFXXCmwFlgWUM4PrakybVOueJJ6pwHqSVhTFDw==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.21.4", + "error-ex": "^1.3.2", + "json-parse-even-better-errors": "^3.0.0", + "lines-and-columns": "^2.0.3", + "type-fest": "^3.8.0" + }, + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parse-json/node_modules/type-fest": { + "version": "3.13.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-3.13.1.tgz", + "integrity": "sha512-tLq3bSNx+xSpwvAJnzrK0Ep5CLNWjvFTOp71URMaAEWBfRb9nnJiBoUe0tF8bI4ZFO3omgBR6NvnbzVUT3Ly4g==", + "dev": true, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parse-ms": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse-ms/-/parse-ms-4.0.0.tgz", + "integrity": "sha512-TXfryirbmq34y8QBwgqCVLi+8oA3oWx2eAnSn62ITyEhEYaWRlVZ2DvMM9eZbMs/RfxPu/PK/aBLyGj4IrqMHw==", + "dev": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parse5": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.3.0.tgz", + "integrity": "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==", + "dev": true, + "dependencies": { + "entities": "^6.0.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, + "node_modules/parse5-htmlparser2-tree-adapter": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-7.1.0.tgz", + "integrity": "sha512-ruw5xyKs6lrpo9x9rCZqZZnIUntICjQAd0Wsmp396Ul9lN/h+ifgVV1x1gZHi8euej6wTfpqX8j+BFQxF0NS/g==", + "dev": true, + "dependencies": { + "domhandler": "^5.0.3", + "parse5": "^7.0.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, + "node_modules/parse5-parser-stream": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/parse5-parser-stream/-/parse5-parser-stream-7.1.2.tgz", + "integrity": "sha512-JyeQc9iwFLn5TbvvqACIF/VXG6abODeB3Fwmv/TGdLk2LfbWkaySGY72at4+Ty7EkPZj854u4CrICqNk2qIbow==", + "dev": true, + "dependencies": { + "parse5": "^7.0.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, + "node_modules/parse5/node_modules/entities": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-6.0.1.tgz", + "integrity": "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==", + "dev": true, + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, "node_modules/parseurl": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", @@ -2223,6 +6363,15 @@ "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, + "engines": { + "node": ">=8" + } + }, "node_modules/path-key": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", @@ -2231,6 +6380,22 @@ "node": ">=8" } }, + "node_modules/path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "dev": true, + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/path-to-regexp": { "version": "8.3.0", "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.3.0.tgz", @@ -2240,6 +6405,18 @@ "url": "https://opencollective.com/express" } }, + "node_modules/pathe": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-1.1.2.tgz", + "integrity": "sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==", + "dev": true + }, + "node_modules/pend": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", + "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==", + "dev": true + }, "node_modules/picocolors": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", @@ -2334,6 +6511,59 @@ "url": "https://github.com/prettier/prettier?sponsor=1" } }, + "node_modules/pretty-format": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-30.2.0.tgz", + "integrity": "sha512-9uBdv/B4EefsuAL+pWqueZyZS2Ba+LxfFeQ9DN14HU4bN8bhaxKdkpjpB6fs9+pSjIBu+FXQHImEg8j/Lw0+vA==", + "dev": true, + "dependencies": { + "@jest/schemas": "30.0.5", + "ansi-styles": "^5.2.0", + "react-is": "^18.3.1" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/pretty-ms": { + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/pretty-ms/-/pretty-ms-9.3.0.tgz", + "integrity": "sha512-gjVS5hOP+M3wMm5nmNOucbIrqudzs9v/57bWRHQWLYklXqoXKrVfYW2W9+glfGsqtPgpiz5WwyEEB+ksXIx3gQ==", + "dev": true, + "dependencies": { + "parse-ms": "^4.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/process": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==", + "dev": true, + "engines": { + "node": ">= 0.6.0" + } + }, + "node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "dev": true + }, + "node_modules/progress": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", + "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", + "dev": true, + "engines": { + "node": ">=0.4.0" + } + }, "node_modules/prosemirror-commands": { "version": "1.7.1", "resolved": "https://registry.npmjs.org/prosemirror-commands/-/prosemirror-commands-1.7.1.tgz", @@ -2421,6 +6651,34 @@ "node": ">= 0.10" } }, + "node_modules/proxy-agent": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/proxy-agent/-/proxy-agent-6.5.0.tgz", + "integrity": "sha512-TmatMXdr2KlRiA2CyDu8GqR8EjahTG3aY3nXjdzFyoZbmB8hrBsTyMezhULIXKnC0jpfjlmiZ3+EaCzoInSu/A==", + "dev": true, + "dependencies": { + "agent-base": "^7.1.2", + "debug": "^4.3.4", + "http-proxy-agent": "^7.0.1", + "https-proxy-agent": "^7.0.6", + "lru-cache": "^7.14.1", + "pac-proxy-agent": "^7.1.0", + "proxy-from-env": "^1.1.0", + "socks-proxy-agent": "^8.0.5" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/proxy-agent/node_modules/lru-cache": { + "version": "7.18.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", + "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", + "dev": true, + "engines": { + "node": ">=12" + } + }, "node_modules/proxy-from-env": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", @@ -2449,6 +6707,21 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/query-selector-shadow-dom": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/query-selector-shadow-dom/-/query-selector-shadow-dom-1.0.1.tgz", + "integrity": "sha512-lT5yCqEBgfoMYpf3F2xQRK7zEr1rhIIZuceDK6+xRkJQ4NMbHTwXqk4NkwDwQMNqXgG9r9fyHnzwNVs6zV5KRw==", + "dev": true + }, + "node_modules/randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "dev": true, + "dependencies": { + "safe-buffer": "^5.1.0" + } + }, "node_modules/range-parser": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", @@ -2516,6 +6789,155 @@ "react": "^18.3.1" } }, + "node_modules/react-is": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", + "dev": true + }, + "node_modules/read-pkg": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-8.1.0.tgz", + "integrity": "sha512-PORM8AgzXeskHO/WEv312k9U03B8K9JSiWF/8N9sUuFjBa+9SF2u6K7VClzXwDXab51jCd8Nd36CNM+zR97ScQ==", + "dev": true, + "dependencies": { + "@types/normalize-package-data": "^2.4.1", + "normalize-package-data": "^6.0.0", + "parse-json": "^7.0.0", + "type-fest": "^4.2.0" + }, + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/read-pkg-up": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-10.1.0.tgz", + "integrity": "sha512-aNtBq4jR8NawpKJQldrQcSW9y/d+KWH4v24HWkHljOZ7H0av+YTGANBzRh9A5pw7v/bLVsLVPpOhJ7gHNVy8lA==", + "dev": true, + "dependencies": { + "find-up": "^6.3.0", + "read-pkg": "^8.1.0", + "type-fest": "^4.2.0" + }, + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/read-pkg-up/node_modules/find-up": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-6.3.0.tgz", + "integrity": "sha512-v2ZsoEuVHYy8ZIlYqwPe/39Cy+cFDzp4dXPaxNvkEuouymu+2Jbz0PxpKarJHYJTmv2HWT3O382qY8l4jMWthw==", + "dev": true, + "dependencies": { + "locate-path": "^7.1.0", + "path-exists": "^5.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/read-pkg-up/node_modules/locate-path": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-7.2.0.tgz", + "integrity": "sha512-gvVijfZvn7R+2qyPX8mAuKcFGDf6Nc61GdvGafQsHL0sBIxfKzA+usWn4GFC/bk+QdwPUD4kWFJLhElipq+0VA==", + "dev": true, + "dependencies": { + "p-locate": "^6.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/read-pkg-up/node_modules/p-limit": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-4.0.0.tgz", + "integrity": "sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ==", + "dev": true, + "dependencies": { + "yocto-queue": "^1.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/read-pkg-up/node_modules/p-locate": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-6.0.0.tgz", + "integrity": "sha512-wPrq66Llhl7/4AGC6I+cqxT07LhXvWL08LNXz1fENOw0Ap4sRZZ/gZpTTJ5jpurzzzfS2W/Ge9BY3LgLjCShcw==", + "dev": true, + "dependencies": { + "p-limit": "^4.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/read-pkg-up/node_modules/path-exists": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-5.0.0.tgz", + "integrity": "sha512-RjhtfwJOxzcFmNOi6ltcbcu4Iu+FL3zEj83dk4kAS+fVpTxXLO1b38RvJgT/0QwvV/L3aY9TAnyv0EOqW4GoMQ==", + "dev": true, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + } + }, + "node_modules/read-pkg-up/node_modules/yocto-queue": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-1.2.2.tgz", + "integrity": "sha512-4LCcse/U2MHZ63HAJVE+v71o7yOdIe4cZ70Wpf8D/IyjDKYQLV5GD46B+hSTjJsvV5PztjvHoU580EftxjDZFQ==", + "dev": true, + "engines": { + "node": ">=12.20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/read-pkg/node_modules/hosted-git-info": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-7.0.2.tgz", + "integrity": "sha512-puUZAUKT5m8Zzvs72XWy3HtvVbTWljRE66cP60bxJzAqf2DgICo7lYTY2IHUmLnNpjYvw5bvmoHvPc0QO2a62w==", + "dev": true, + "dependencies": { + "lru-cache": "^10.0.1" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/read-pkg/node_modules/normalize-package-data": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-6.0.2.tgz", + "integrity": "sha512-V6gygoYb/5EmNI+MEGrWkC+e6+Rr7mTmfHrxDbLzxQogBkgzo76rkok0Am6thgSF7Mv2nLOajAJj5vDJZEFn7g==", + "dev": true, + "dependencies": { + "hosted-git-info": "^7.0.0", + "semver": "^7.3.5", + "validate-npm-package-license": "^3.0.4" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, "node_modules/readable-stream": { "version": "3.6.2", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", @@ -2529,6 +6951,83 @@ "node": ">= 6" } }, + "node_modules/readdir-glob": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/readdir-glob/-/readdir-glob-1.1.3.tgz", + "integrity": "sha512-v05I2k7xN8zXvPD9N+z/uhXPaj0sUFCe2rcWZIpBsqxfP7xXFQ0tipAd/wjj1YxWyWtUS5IDJpOG82JKt2EAVA==", + "dev": true, + "dependencies": { + "minimatch": "^5.1.0" + } + }, + "node_modules/readdir-glob/node_modules/minimatch": { + "version": "5.1.9", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.9.tgz", + "integrity": "sha512-7o1wEA2RyMP7Iu7GNba9vc0RWWGACJOCZBJX2GJWip0ikV+wcOsgVuY9uE8CPiyQhkGFSlhuSkZPavN7u1c2Fw==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/readdirp": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", + "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", + "dev": true, + "engines": { + "node": ">= 14.18.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/recursive-readdir": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/recursive-readdir/-/recursive-readdir-2.2.3.tgz", + "integrity": "sha512-8HrF5ZsXk5FAH9dgsx3BlUer73nIhuj+9OrQwEbLTPOBzGkL1lsFCR01am+v+0m2Cmbs1nP12hLDl5FA7EszKA==", + "dev": true, + "dependencies": { + "minimatch": "^3.0.5" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/recursive-readdir/node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/recursive-readdir/node_modules/minimatch": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "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, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/require-from-string": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", @@ -2537,6 +7036,45 @@ "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", + "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", + "dev": true, + "funding": { + "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" + } + }, + "node_modules/resq": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/resq/-/resq-1.11.0.tgz", + "integrity": "sha512-G10EBz+zAAy3zUd/CDoBbXRL6ia9kOo3xRHrMDsHljI0GDkhYlyjwoCx5+3eCC4swi1uCoZQhskuJkj7Gp57Bw==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^2.0.1" + } + }, + "node_modules/resq/node_modules/fast-deep-equal": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz", + "integrity": "sha512-bCK/2Z4zLidyB4ReuIsvALH6w31YfAQDmXMqMx6FyfHqvBxtjC0eRumeSu4Bs3XtXwpyIywtSTrVT99BxY1f9w==", + "dev": true + }, + "node_modules/ret": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/ret/-/ret-0.5.0.tgz", + "integrity": "sha512-I1XxrZSQ+oErkRR4jYbAyEEu2I0avBvvMM5JN+6EBprOGRCs63ENqZ3vjavq8fBw2+62G5LF5XelKwuJpcvcxw==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/rgb2hex": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/rgb2hex/-/rgb2hex-0.2.5.tgz", + "integrity": "sha512-22MOP1Rh7sAo1BZpDG6R5RFYzR2lYEgwq7HEmyW2qcsOqR2lQKmn+O//xV3YG/0rrhMC6KVX2hU+ZXuaw9a5bw==", + "dev": true + }, "node_modules/rollup": { "version": "4.59.0", "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.59.0.tgz", @@ -2601,6 +7139,33 @@ "node": ">= 18" } }, + "node_modules/run-async": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/run-async/-/run-async-4.0.6.tgz", + "integrity": "sha512-IoDlSLTs3Yq593mb3ZoKWKXMNu3UpObxhgA/Xuid5p4bbfi2jdY1Hj0m1K+0/tEuQTxIGMhQDqGjKb7RuxGpAQ==", + "dev": true, + "engines": { + "node": ">=0.12.0" + } + }, + "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, + "dependencies": { + "tslib": "^2.1.0" + } + }, + "node_modules/safaridriver": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/safaridriver/-/safaridriver-1.0.1.tgz", + "integrity": "sha512-jkg4434cYgtrIF2AeY/X0Wmd2W73cK5qIEFE3hDrrQenJH/2SDJIXGvPAigfvQTcE9+H31zkiNHbUqcihEiMRA==", + "dev": true, + "engines": { + "node": ">=18.0.0" + } + }, "node_modules/safe-buffer": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", @@ -2620,6 +7185,25 @@ } ] }, + "node_modules/safe-regex2": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/safe-regex2/-/safe-regex2-5.0.0.tgz", + "integrity": "sha512-YwJwe5a51WlK7KbOJREPdjNrpViQBI3p4T50lfwPuDhZnE3XGVTlGvi+aolc5+RvxDD6bnUmjVsU9n1eboLUYw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "dependencies": { + "ret": "~0.5.0" + } + }, "node_modules/safer-buffer": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", @@ -2633,6 +7217,17 @@ "loose-envify": "^1.1.0" } }, + "node_modules/semver": { + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", + "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/send": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/send/-/send-1.2.1.tgz", @@ -2658,6 +7253,30 @@ "url": "https://opencollective.com/express" } }, + "node_modules/serialize-error": { + "version": "12.0.0", + "resolved": "https://registry.npmjs.org/serialize-error/-/serialize-error-12.0.0.tgz", + "integrity": "sha512-ZYkZLAvKTKQXWuh5XpBw7CdbSzagarX39WyZ2H07CDLC5/KfsRGlIXV8d4+tfqX1M7916mRqR1QfNHSij+c9Pw==", + "dev": true, + "dependencies": { + "type-fest": "^4.31.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/serialize-javascript": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz", + "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==", + "dev": true, + "dependencies": { + "randombytes": "^2.1.0" + } + }, "node_modules/serve-static": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.2.1.tgz", @@ -2676,6 +7295,12 @@ "url": "https://opencollective.com/express" } }, + "node_modules/setimmediate": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", + "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==", + "dev": true + }, "node_modules/setprototypeof": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", @@ -2768,6 +7393,18 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/simple-concat": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz", @@ -2811,6 +7448,63 @@ "simple-concat": "^1.0.0" } }, + "node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/smart-buffer": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", + "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==", + "dev": true, + "engines": { + "node": ">= 6.0.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/socks": { + "version": "2.8.7", + "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.7.tgz", + "integrity": "sha512-HLpt+uLy/pxB+bum/9DzAgiKS8CX1EvbWxI4zlmgGCExImLdiad2iCwXT5Z4c9c3Eq8rP2318mPW2c+QbtjK8A==", + "dev": true, + "dependencies": { + "ip-address": "^10.0.1", + "smart-buffer": "^4.2.0" + }, + "engines": { + "node": ">= 10.0.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/socks-proxy-agent": { + "version": "8.0.5", + "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-8.0.5.tgz", + "integrity": "sha512-HehCEsotFqbPW9sJ8WVYB6UbmIMv7kUUORIF2Nncq4VQvBfNBLibW9YZR5dlYCSUhwcD628pRllm7n+E+YTzJw==", + "dev": true, + "dependencies": { + "agent-base": "^7.1.2", + "debug": "^4.3.4", + "socks": "^2.8.3" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "optional": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/source-map-js": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", @@ -2820,6 +7514,63 @@ "node": ">=0.10.0" } }, + "node_modules/spacetrim": { + "version": "0.11.59", + "resolved": "https://registry.npmjs.org/spacetrim/-/spacetrim-0.11.59.tgz", + "integrity": "sha512-lLYsktklSRKprreOm7NXReW8YiX2VBjbgmXYEziOoGf/qsJqAEACaDvoTtUOycwjpaSh+bT8eu0KrJn7UNxiCg==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://buymeacoffee.com/hejny" + }, + { + "type": "github", + "url": "https://github.com/hejny/spacetrim/blob/main/README.md#%EF%B8%8F-contributing" + } + ] + }, + "node_modules/spdx-correct": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz", + "integrity": "sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==", + "dev": true, + "dependencies": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-exceptions": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.5.0.tgz", + "integrity": "sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==", + "dev": true + }, + "node_modules/spdx-expression-parse": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", + "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", + "dev": true, + "dependencies": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-license-ids": { + "version": "3.0.23", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.23.tgz", + "integrity": "sha512-CWLcCCH7VLu13TgOH+r8p1O/Znwhqv/dbb6lqWy67G+pT1kHmeD/+V36AVb/vq8QMIQwVShJ6Ssl5FPh0fuSdw==", + "dev": true + }, + "node_modules/split2": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz", + "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==", + "dev": true, + "engines": { + "node": ">= 10.x" + } + }, "node_modules/sqlite-vec-windows-x64": { "version": "0.1.7-alpha.2", "resolved": "https://registry.npmjs.org/sqlite-vec-windows-x64/-/sqlite-vec-windows-x64-0.1.7-alpha.2.tgz", @@ -2831,6 +7582,27 @@ "win32" ] }, + "node_modules/stack-utils": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", + "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==", + "dev": true, + "dependencies": { + "escape-string-regexp": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/stack-utils/node_modules/escape-string-regexp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", + "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/statuses": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", @@ -2839,6 +7611,26 @@ "node": ">= 0.8" } }, + "node_modules/stream-buffers": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/stream-buffers/-/stream-buffers-3.0.3.tgz", + "integrity": "sha512-pqMqwQCso0PBJt2PQmDO0cFj0lyqmiwOMiMSkVtRokl7e+ZTRYgDHKnuZNbqjiJXgsg4nuqtD/zxuo9KqTp0Yw==", + "dev": true, + "engines": { + "node": ">= 0.10.0" + } + }, + "node_modules/streamx": { + "version": "2.23.0", + "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.23.0.tgz", + "integrity": "sha512-kn+e44esVfn2Fa/O0CPFcex27fjIL6MkVae0Mm6q+E6f0hWv578YCERbv+4m02cjxvDsPKLnmxral/rR6lBMAg==", + "dev": true, + "dependencies": { + "events-universal": "^1.0.0", + "fast-fifo": "^1.3.2", + "text-decoder": "^1.1.0" + } + }, "node_modules/string_decoder": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", @@ -2847,6 +7639,147 @@ "safe-buffer": "~5.2.0" } }, + "node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/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 + }, + "node_modules/string-width-cjs/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, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz", + "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==", + "dev": true, + "dependencies": { + "ansi-regex": "^6.2.2" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/strip-ansi-cjs": { + "name": "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, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi/node_modules/ansi-regex": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", + "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/strip-final-newline": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-4.0.0.tgz", + "integrity": "sha512-aulFJcD6YK8V1G7iRB5tigAP4TsHBZZrOV8pjV++zdUwmeV8uzbY7yn6h9MswN62adStNZFuCIx4haBnRuMDaw==", + "dev": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/strnum": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/strnum/-/strnum-2.2.0.tgz", + "integrity": "sha512-Y7Bj8XyJxnPAORMZj/xltsfo55uOiyHcU2tnAVzHUnSJR/KsEX+9RoDeXEnsXtl/CX4fAcrt64gZ13aGaWPeBg==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ] + }, + "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==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, "node_modules/tar-fs": { "version": "2.1.4", "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.4.tgz", @@ -2873,6 +7806,38 @@ "node": ">=6" } }, + "node_modules/teex": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/teex/-/teex-1.0.1.tgz", + "integrity": "sha512-eYE6iEI62Ni1H8oIa7KlDU6uQBtqr4Eajni3wX7rpfXD8ysFx8z0+dri+KWEPWpBsxXfxu58x/0jvTVT1ekOSg==", + "dev": true, + "dependencies": { + "streamx": "^2.12.5" + } + }, + "node_modules/text-decoder": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/text-decoder/-/text-decoder-1.2.7.tgz", + "integrity": "sha512-vlLytXkeP4xvEq2otHeJfSQIRyWxo/oZGEbXrtEEF9Hnmrdly59sUbzZ/QgyWuLYHctCHxFF4tRQZNQ9k60ExQ==", + "dev": true, + "dependencies": { + "b4a": "^1.6.4" + } + }, + "node_modules/text-decoder/node_modules/b4a": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/b4a/-/b4a-1.8.0.tgz", + "integrity": "sha512-qRuSmNSkGQaHwNbM7J78Wwy+ghLEYF1zNrSeMxj4Kgw6y33O3mXcQ6Ie9fRvfU/YnxWkOchPXbaLb73TkIsfdg==", + "dev": true, + "peerDependencies": { + "react-native-b4a": "*" + }, + "peerDependenciesMeta": { + "react-native-b4a": { + "optional": true + } + } + }, "node_modules/tinyglobby": { "version": "0.2.15", "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", @@ -2889,6 +7854,27 @@ "url": "https://github.com/sponsors/SuperchupuDev" } }, + "node_modules/tinyrainbow": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-1.2.0.tgz", + "integrity": "sha512-weEDEq7Z5eTHPDh4xjX789+fHfF+P8boiFB+0vbWzpbnbsEr/GRaohi/uMKxg8RZMXnl1ItAi/IUHWMsjDV7kQ==", + "dev": true, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, "node_modules/toidentifier": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", @@ -2897,6 +7883,488 @@ "node": ">=0.6" } }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true + }, + "node_modules/tsx": { + "version": "4.21.0", + "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.21.0.tgz", + "integrity": "sha512-5C1sg4USs1lfG0GFb2RLXsdpXqBSEhAaA/0kPL01wxzpMqLILNxIxIOKiILz+cdg/pLnOUxFYOR5yhHU666wbw==", + "dev": true, + "dependencies": { + "esbuild": "~0.27.0", + "get-tsconfig": "^4.7.5" + }, + "bin": { + "tsx": "dist/cli.mjs" + }, + "engines": { + "node": ">=18.0.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + } + }, + "node_modules/tsx/node_modules/@esbuild/aix-ppc64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.3.tgz", + "integrity": "sha512-9fJMTNFTWZMh5qwrBItuziu834eOCUcEqymSH7pY+zoMVEZg3gcPuBNxH1EvfVYe9h0x/Ptw8KBzv7qxb7l8dg==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/android-arm": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.3.tgz", + "integrity": "sha512-i5D1hPY7GIQmXlXhs2w8AWHhenb00+GxjxRncS2ZM7YNVGNfaMxgzSGuO8o8SJzRc/oZwU2bcScvVERk03QhzA==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/android-arm64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.3.tgz", + "integrity": "sha512-YdghPYUmj/FX2SYKJ0OZxf+iaKgMsKHVPF1MAq/P8WirnSpCStzKJFjOjzsW0QQ7oIAiccHdcqjbHmJxRb/dmg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/android-x64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.3.tgz", + "integrity": "sha512-IN/0BNTkHtk8lkOM8JWAYFg4ORxBkZQf9zXiEOfERX/CzxW3Vg1ewAhU7QSWQpVIzTW+b8Xy+lGzdYXV6UZObQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/darwin-arm64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.3.tgz", + "integrity": "sha512-Re491k7ByTVRy0t3EKWajdLIr0gz2kKKfzafkth4Q8A5n1xTHrkqZgLLjFEHVD+AXdUGgQMq+Godfq45mGpCKg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/darwin-x64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.3.tgz", + "integrity": "sha512-vHk/hA7/1AckjGzRqi6wbo+jaShzRowYip6rt6q7VYEDX4LEy1pZfDpdxCBnGtl+A5zq8iXDcyuxwtv3hNtHFg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/freebsd-arm64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.3.tgz", + "integrity": "sha512-ipTYM2fjt3kQAYOvo6vcxJx3nBYAzPjgTCk7QEgZG8AUO3ydUhvelmhrbOheMnGOlaSFUoHXB6un+A7q4ygY9w==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/freebsd-x64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.3.tgz", + "integrity": "sha512-dDk0X87T7mI6U3K9VjWtHOXqwAMJBNN2r7bejDsc+j03SEjtD9HrOl8gVFByeM0aJksoUuUVU9TBaZa2rgj0oA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/linux-arm": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.3.tgz", + "integrity": "sha512-s6nPv2QkSupJwLYyfS+gwdirm0ukyTFNl3KTgZEAiJDd+iHZcbTPPcWCcRYH+WlNbwChgH2QkE9NSlNrMT8Gfw==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/linux-arm64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.3.tgz", + "integrity": "sha512-sZOuFz/xWnZ4KH3YfFrKCf1WyPZHakVzTiqji3WDc0BCl2kBwiJLCXpzLzUBLgmp4veFZdvN5ChW4Eq/8Fc2Fg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/linux-ia32": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.3.tgz", + "integrity": "sha512-yGlQYjdxtLdh0a3jHjuwOrxQjOZYD/C9PfdbgJJF3TIZWnm/tMd/RcNiLngiu4iwcBAOezdnSLAwQDPqTmtTYg==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/linux-loong64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.3.tgz", + "integrity": "sha512-WO60Sn8ly3gtzhyjATDgieJNet/KqsDlX5nRC5Y3oTFcS1l0KWba+SEa9Ja1GfDqSF1z6hif/SkpQJbL63cgOA==", + "cpu": [ + "loong64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/linux-mips64el": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.3.tgz", + "integrity": "sha512-APsymYA6sGcZ4pD6k+UxbDjOFSvPWyZhjaiPyl/f79xKxwTnrn5QUnXR5prvetuaSMsb4jgeHewIDCIWljrSxw==", + "cpu": [ + "mips64el" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/linux-ppc64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.3.tgz", + "integrity": "sha512-eizBnTeBefojtDb9nSh4vvVQ3V9Qf9Df01PfawPcRzJH4gFSgrObw+LveUyDoKU3kxi5+9RJTCWlj4FjYXVPEA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/linux-riscv64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.3.tgz", + "integrity": "sha512-3Emwh0r5wmfm3ssTWRQSyVhbOHvqegUDRd0WhmXKX2mkHJe1SFCMJhagUleMq+Uci34wLSipf8Lagt4LlpRFWQ==", + "cpu": [ + "riscv64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/linux-s390x": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.3.tgz", + "integrity": "sha512-pBHUx9LzXWBc7MFIEEL0yD/ZVtNgLytvx60gES28GcWMqil8ElCYR4kvbV2BDqsHOvVDRrOxGySBM9Fcv744hw==", + "cpu": [ + "s390x" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/linux-x64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.3.tgz", + "integrity": "sha512-Czi8yzXUWIQYAtL/2y6vogER8pvcsOsk5cpwL4Gk5nJqH5UZiVByIY8Eorm5R13gq+DQKYg0+JyQoytLQas4dA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/netbsd-arm64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.3.tgz", + "integrity": "sha512-sDpk0RgmTCR/5HguIZa9n9u+HVKf40fbEUt+iTzSnCaGvY9kFP0YKBWZtJaraonFnqef5SlJ8/TiPAxzyS+UoA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/netbsd-x64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.3.tgz", + "integrity": "sha512-P14lFKJl/DdaE00LItAukUdZO5iqNH7+PjoBm+fLQjtxfcfFE20Xf5CrLsmZdq5LFFZzb5JMZ9grUwvtVYzjiA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/openbsd-arm64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.3.tgz", + "integrity": "sha512-AIcMP77AvirGbRl/UZFTq5hjXK+2wC7qFRGoHSDrZ5v5b8DK/GYpXW3CPRL53NkvDqb9D+alBiC/dV0Fb7eJcw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/openbsd-x64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.3.tgz", + "integrity": "sha512-DnW2sRrBzA+YnE70LKqnM3P+z8vehfJWHXECbwBmH/CU51z6FiqTQTHFenPlHmo3a8UgpLyH3PT+87OViOh1AQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/openharmony-arm64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.3.tgz", + "integrity": "sha512-NinAEgr/etERPTsZJ7aEZQvvg/A6IsZG/LgZy+81wON2huV7SrK3e63dU0XhyZP4RKGyTm7aOgmQk0bGp0fy2g==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/sunos-x64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.3.tgz", + "integrity": "sha512-PanZ+nEz+eWoBJ8/f8HKxTTD172SKwdXebZ0ndd953gt1HRBbhMsaNqjTyYLGLPdoWHy4zLU7bDVJztF5f3BHA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/win32-arm64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.3.tgz", + "integrity": "sha512-B2t59lWWYrbRDw/tjiWOuzSsFh1Y/E95ofKz7rIVYSQkUYBjfSgf6oeYPNWHToFRr2zx52JKApIcAS/D5TUBnA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/win32-ia32": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.3.tgz", + "integrity": "sha512-QLKSFeXNS8+tHW7tZpMtjlNb7HKau0QDpwm49u0vUp9y1WOF+PEzkU84y9GqYaAVW8aH8f3GcBck26jh54cX4Q==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/win32-x64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.3.tgz", + "integrity": "sha512-4uJGhsxuptu3OcpVAzli+/gWusVGwZZHTlS63hh++ehExkVT8SgiEf7/uC/PclrPPkLhZqGgCTjd0VWLo6xMqA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/esbuild": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.3.tgz", + "integrity": "sha512-8VwMnyGCONIs6cWue2IdpHxHnAjzxnw2Zr7MkVxB2vjmQ2ivqGFb4LEG3SMnv0Gb2F/G/2yA8zUaiL1gywDCCg==", + "dev": true, + "hasInstallScript": true, + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.27.3", + "@esbuild/android-arm": "0.27.3", + "@esbuild/android-arm64": "0.27.3", + "@esbuild/android-x64": "0.27.3", + "@esbuild/darwin-arm64": "0.27.3", + "@esbuild/darwin-x64": "0.27.3", + "@esbuild/freebsd-arm64": "0.27.3", + "@esbuild/freebsd-x64": "0.27.3", + "@esbuild/linux-arm": "0.27.3", + "@esbuild/linux-arm64": "0.27.3", + "@esbuild/linux-ia32": "0.27.3", + "@esbuild/linux-loong64": "0.27.3", + "@esbuild/linux-mips64el": "0.27.3", + "@esbuild/linux-ppc64": "0.27.3", + "@esbuild/linux-riscv64": "0.27.3", + "@esbuild/linux-s390x": "0.27.3", + "@esbuild/linux-x64": "0.27.3", + "@esbuild/netbsd-arm64": "0.27.3", + "@esbuild/netbsd-x64": "0.27.3", + "@esbuild/openbsd-arm64": "0.27.3", + "@esbuild/openbsd-x64": "0.27.3", + "@esbuild/openharmony-arm64": "0.27.3", + "@esbuild/sunos-x64": "0.27.3", + "@esbuild/win32-arm64": "0.27.3", + "@esbuild/win32-ia32": "0.27.3", + "@esbuild/win32-x64": "0.27.3" + } + }, "node_modules/tunnel-agent": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", @@ -2908,6 +8376,18 @@ "node": "*" } }, + "node_modules/type-fest": { + "version": "4.41.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.41.0.tgz", + "integrity": "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==", + "dev": true, + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/type-is": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.0.1.tgz", @@ -2934,6 +8414,33 @@ "node": ">=14.17" } }, + "node_modules/undici": { + "version": "6.23.0", + "resolved": "https://registry.npmjs.org/undici/-/undici-6.23.0.tgz", + "integrity": "sha512-VfQPToRA5FZs/qJxLIinmU59u0r7LXqoJkCzinq3ckNJp3vKEh7jTWN589YQ5+aoAC/TGRLyJLCPKcLQbM8r9g==", + "dev": true, + "engines": { + "node": ">=18.17" + } + }, + "node_modules/undici-types": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", + "dev": true + }, + "node_modules/unicorn-magic": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/unicorn-magic/-/unicorn-magic-0.3.0.tgz", + "integrity": "sha512-+QBBXBCvifc56fsbuxZQ6Sic3wqqc3WWaqxs58gvJrcOuN83HGTCwz3oS5phzU9LthRNE9VrJCFCLUgHeeFnfA==", + "dev": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/unpipe": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", @@ -2942,11 +8449,36 @@ "node": ">= 0.8" } }, + "node_modules/urlpattern-polyfill": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/urlpattern-polyfill/-/urlpattern-polyfill-10.1.0.tgz", + "integrity": "sha512-IGjKp/o0NL3Bso1PymYURCJxMPNAf/ILOpendP9f5B6e1rTJgdgiOvgfoT8VxCAdY+Wisb9uhGaJJf3yZ2V9nw==", + "dev": true + }, + "node_modules/userhome": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/userhome/-/userhome-1.0.1.tgz", + "integrity": "sha512-5cnLm4gseXjAclKowC4IjByaGsjtAoV6PrOQOljplNB54ReUYJP8HdAFq2muHinSDAh09PPX/uXDPfdxRHvuSA==", + "dev": true, + "engines": { + "node": ">= 0.8.0" + } + }, "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" }, + "node_modules/validate-npm-package-license": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", + "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "dev": true, + "dependencies": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, "node_modules/vary": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", @@ -3034,6 +8566,185 @@ "resolved": "https://registry.npmjs.org/w3c-keyname/-/w3c-keyname-2.2.8.tgz", "integrity": "sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ==" }, + "node_modules/wait-port": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/wait-port/-/wait-port-1.1.0.tgz", + "integrity": "sha512-3e04qkoN3LxTMLakdqeWth8nih8usyg+sf1Bgdf9wwUkp05iuK1eSY/QpLvscT/+F/gA89+LpUmmgBtesbqI2Q==", + "dev": true, + "dependencies": { + "chalk": "^4.1.2", + "commander": "^9.3.0", + "debug": "^4.3.4" + }, + "bin": { + "wait-port": "bin/wait-port.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/wait-port/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, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/wait-port/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/wait-port/node_modules/commander": { + "version": "9.5.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-9.5.0.tgz", + "integrity": "sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==", + "dev": true, + "engines": { + "node": "^12.20.0 || >=14" + } + }, + "node_modules/wait-port/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, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wcwidth": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", + "integrity": "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==", + "dev": true, + "optional": true, + "dependencies": { + "defaults": "^1.0.3" + } + }, + "node_modules/webdriver": { + "version": "9.24.0", + "resolved": "https://registry.npmjs.org/webdriver/-/webdriver-9.24.0.tgz", + "integrity": "sha512-2R31Ey83NzMsafkl4hdFq6GlIBvOODQMkueLjeRqYAITu3QCYiq9oqBdnWA6CdePuV4dbKlYsKRX0mwMiPclDA==", + "dev": true, + "dependencies": { + "@types/node": "^20.1.0", + "@types/ws": "^8.5.3", + "@wdio/config": "9.24.0", + "@wdio/logger": "9.18.0", + "@wdio/protocols": "9.24.0", + "@wdio/types": "9.24.0", + "@wdio/utils": "9.24.0", + "deepmerge-ts": "^7.0.3", + "https-proxy-agent": "^7.0.6", + "undici": "^6.21.3", + "ws": "^8.8.0" + }, + "engines": { + "node": ">=18.20.0" + } + }, + "node_modules/webdriverio": { + "version": "9.24.0", + "resolved": "https://registry.npmjs.org/webdriverio/-/webdriverio-9.24.0.tgz", + "integrity": "sha512-LTJt6Z/iDM0ne/4ytd3BykoPv9CuJ+CAILOzlwFeMGn4Mj02i4Bk2Rg9o/jeJ89f52hnv4OPmNjD0e8nzWAy5g==", + "dev": true, + "dependencies": { + "@types/node": "^20.11.30", + "@types/sinonjs__fake-timers": "^8.1.5", + "@wdio/config": "9.24.0", + "@wdio/logger": "9.18.0", + "@wdio/protocols": "9.24.0", + "@wdio/repl": "9.16.2", + "@wdio/types": "9.24.0", + "@wdio/utils": "9.24.0", + "archiver": "^7.0.1", + "aria-query": "^5.3.0", + "cheerio": "^1.0.0-rc.12", + "css-shorthand-properties": "^1.1.1", + "css-value": "^0.0.1", + "grapheme-splitter": "^1.0.4", + "htmlfy": "^0.8.1", + "is-plain-obj": "^4.1.0", + "jszip": "^3.10.1", + "lodash.clonedeep": "^4.5.0", + "lodash.zip": "^4.2.0", + "query-selector-shadow-dom": "^1.0.1", + "resq": "^1.11.0", + "rgb2hex": "0.2.5", + "serialize-error": "^12.0.0", + "urlpattern-polyfill": "^10.0.0", + "webdriver": "9.24.0" + }, + "engines": { + "node": ">=18.20.0" + }, + "peerDependencies": { + "puppeteer-core": ">=22.x || <=24.x" + }, + "peerDependenciesMeta": { + "puppeteer-core": { + "optional": true + } + } + }, + "node_modules/whatwg-encoding": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-3.1.1.tgz", + "integrity": "sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==", + "deprecated": "Use @exodus/bytes instead for a more spec-conformant and faster implementation", + "dev": true, + "dependencies": { + "iconv-lite": "0.6.3" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/whatwg-encoding/node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "dev": true, + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/whatwg-mimetype": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-4.0.0.tgz", + "integrity": "sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==", + "dev": true, + "engines": { + "node": ">=18" + } + }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", @@ -3048,11 +8759,386 @@ "node": ">= 8" } }, + "node_modules/workerpool": { + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.5.1.tgz", + "integrity": "sha512-Fs4dNYcsdpYSAfVxhnl1L5zTksjvOJxtC5hzMNl+1t9B8hTJTdKDyZ5ju7ztgPy+ft9tBFXoOlDNiOT9WUXZlA==", + "dev": true + }, + "node_modules/wrap-ansi": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs": { + "name": "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, + "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" + } + }, + "node_modules/wrap-ansi-cjs/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, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/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 + }, + "node_modules/wrap-ansi-cjs/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs/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, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi/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, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/wrap-ansi/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 + }, + "node_modules/wrap-ansi/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi/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, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" }, + "node_modules/ws": { + "version": "8.19.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.19.0.tgz", + "integrity": "sha512-blAT2mjOEIi0ZzruJfIhb3nps74PRWTCz1IjglWEEpQl5XS/UNama6u2/rjFkDDouqr4L67ry+1aGIALViWjDg==", + "dev": true, + "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", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "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, + "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": "20.2.9", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", + "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs-unparser": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz", + "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==", + "dev": true, + "dependencies": { + "camelcase": "^6.0.0", + "decamelize": "^4.0.0", + "flat": "^5.0.2", + "is-plain-obj": "^2.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs-unparser/node_modules/decamelize": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", + "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/yargs-unparser/node_modules/is-plain-obj": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", + "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", + "dev": true, + "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 + }, + "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==", + "dev": true, + "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, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/yargs/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, + "engines": { + "node": ">=12" + } + }, + "node_modules/yauzl": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", + "integrity": "sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==", + "dev": true, + "dependencies": { + "buffer-crc32": "~0.2.3", + "fd-slicer": "~1.1.0" + } + }, + "node_modules/yauzl/node_modules/buffer-crc32": { + "version": "0.2.13", + "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", + "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==", + "dev": true, + "engines": { + "node": "*" + } + }, + "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, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/yoctocolors": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/yoctocolors/-/yoctocolors-2.1.2.tgz", + "integrity": "sha512-CzhO+pFNo8ajLM2d2IW/R93ipy99LWjtwblvC1RsoSUMZgyLbYFr221TnSNT7GjGdYui6P459mw9JH/g/zW2ug==", + "dev": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/yoctocolors-cjs": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/yoctocolors-cjs/-/yoctocolors-cjs-2.1.3.tgz", + "integrity": "sha512-U/PBtDf35ff0D8X8D0jfdzHYEPFxAI7jJlxZXwCSez5M3190m+QobIfh+sWDWSHMCWWJN2AWamkegn6vr6YBTw==", + "dev": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/zip-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/zip-stream/-/zip-stream-6.0.1.tgz", + "integrity": "sha512-zK7YHHz4ZXpW89AHXUPbQVGKI7uvkd3hzusTdotCg1UxyaVtg0zFJSTfW/Dq5f7OBBVnq6cZIaC8Ti4hb6dtCA==", + "dev": true, + "dependencies": { + "archiver-utils": "^5.0.0", + "compress-commons": "^6.0.2", + "readable-stream": "^4.0.0" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/zip-stream/node_modules/buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, + "node_modules/zip-stream/node_modules/readable-stream": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.7.0.tgz", + "integrity": "sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg==", + "dev": true, + "dependencies": { + "abort-controller": "^3.0.0", + "buffer": "^6.0.3", + "events": "^3.3.0", + "process": "^0.11.10", + "string_decoder": "^1.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, "node_modules/zod": { "version": "4.3.6", "resolved": "https://registry.npmjs.org/zod/-/zod-4.3.6.tgz", diff --git a/package.json b/package.json index 2f4d5e2..708efd1 100644 --- a/package.json +++ b/package.json @@ -5,8 +5,25 @@ "type": "module", "scripts": { "tauri": "tauri", + "dev": "node tools/kill-ports.mjs && tauri dev --no-dev-server-wait -- --no-default-features --features community", + "dev:pro": "node tools/kill-ports.mjs && tauri dev --no-dev-server-wait -- --no-default-features --features pro", "copy-editor": "node tools/copy-toast-ui-editor.js", - "build-editor": "vite build --config tools/vite.config.editor.mjs" + "build-editor": "vite build --config tools/vite.config.editor.mjs", + "build:community": "tauri build -c src/backend/tauri.community.conf.json", + "build:pro": "node tools/ensure-embedding-model.mjs && tauri build -c src/backend/tauri.pro.conf.json -- --no-default-features --features pro", + "test:rust": "cd src/backend && cargo test", + "test:mcp": "node tools/run-test.mjs", + "test:mcp:pro": "node tools/run-test.mjs --pro", + "test:headless": "node tools/run-headless-test.mjs", + "test-and-heal": "node tools/run-test-and-heal.mjs", + "test-and-heal:pro": "node tools/run-test-and-heal.mjs --pro", + "test-and-heal:pro:continuous": "node tools/run-test-and-heal.mjs --pro --continuous", + "test-monolithic-dev": "node tools/test-monolithic-dev.mjs", + "test-monolithic-dev:pro": "node tools/test-monolithic-dev.mjs --pro", + "test:e2e": "wdio run wdio.conf.js", + "test:e2e:community": "wdio run wdio.conf.js", + "build:e2e": "node tools/build-for-e2e.mjs", + "kill-ports": "node tools/kill-ports.mjs" }, "dependencies": { "@modelcontextprotocol/sdk": "^1.26.0", @@ -24,6 +41,11 @@ "devDependencies": { "@types/react": "^18.2.21", "@types/react-dom": "^18.2.7", + "@wdio/cli": "^9.19.0", + "@wdio/local-runner": "^9.19.0", + "@wdio/mocha-framework": "^9.19.0", + "@wdio/spec-reporter": "^9.19.0", + "edgedriver": "^6.3.0", "prettier": "^3.8.1", "typescript": "^5.2.2", "vite": "^6.0.0" diff --git a/src/backend/Cargo.lock b/src/backend/Cargo.lock index 1cb665e..96cdac0 100644 --- a/src/backend/Cargo.lock +++ b/src/backend/Cargo.lock @@ -167,6 +167,18 @@ checksum = "5f0e0fee31ef5ed1ba1316088939cea399010ed7731dba877ed44aeb407a75ea" [[package]] +name = "anymap2" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d301b3b94cb4b2f23d7917810addbbaff90738e0ca2be692bd027e70d7e0330c" + +[[package]] +name = "anymap3" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "170433209e817da6aae2c51aa0dd443009a613425dd041ebfb2492d1c4c11a25" + +[[package]] name = "app" version = "0.3.2" dependencies = [ @@ -179,7 +191,8 @@ "futures", "hnsw_rs", "log", - "ndarray", + "ndarray 0.15.6", + "ort", "reqwest 0.12.28", "rusqlite", "sea-orm", @@ -194,7 +207,9 @@ "tempfile", "tokio", "tokio-stream", + "tower", "tower-http", + "tract-onnx", "uuid", "vibrato", "zstd", @@ -467,6 +482,21 @@ ] [[package]] +name = "bit-set" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0700ddab506f33b20a03b13996eccd309a48e5ff77d0d95926aa0210fb4e95f1" +dependencies = [ + "bit-vec", +] + +[[package]] +name = "bit-vec" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" + +[[package]] name = "bitflags" version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -938,6 +968,12 @@ checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" [[package]] +name = "crunchy" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5" + +[[package]] name = "crypto-common" version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1050,6 +1086,17 @@ ] [[package]] +name = "derive-new" +version = "0.5.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3418329ca0ad70234b9735dc4ceed10af4df60eff9c8e7b06cb5e520d92c3535" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] name = "derive_more" version = "0.99.20" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1188,12 +1235,24 @@ ] [[package]] +name = "doc-comment" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "780955b8b195a21ab8e4ac6b60dd1dbdcec1dc6c51c0617964b08c81785e12c9" + +[[package]] name = "dotenvy" version = "0.15.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1aaf95b3e5c8f23aa320147307562d361db0ae0d51242340f558153b4eb2439b" [[package]] +name = "downcast-rs" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75b325c5dbd37f80359721ad39aca5a29fb04c89279657cffdda8736d0c0b9d2" + +[[package]] name = "dpi" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1230,6 +1289,12 @@ checksum = "d0881ea181b1df73ff77ffaaf9c7544ecc11e82fba9b5f27b262a3c73a332555" [[package]] +name = "dyn-hash" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15401da73a9ed8c80e3b2d4dc05fe10e7b72d7243b9f614e516a44fa99986e88" + +[[package]] name = "either" version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1417,6 +1482,17 @@ ] [[package]] +name = "filetime" +version = "0.2.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f98844151eee8917efc50bd9e8318cb963ae8b297431495d3f758616ea5c57db" +dependencies = [ + "cfg-if", + "libc", + "libredox", +] + +[[package]] name = "find-msvc-tools" version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1955,6 +2031,17 @@ ] [[package]] +name = "half" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6dd08c532ae367adf81c312a4580bc67f1d0fe8bc9c460520283f4c0ff277888" +dependencies = [ + "cfg-if", + "crunchy", + "num-traits", +] + +[[package]] name = "hashbrown" version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -2050,6 +2137,12 @@ ] [[package]] +name = "hmac-sha256" +version = "1.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec9d92d097f4749b64e8cc33d924d9f40a2d4eb91402b458014b781f5733d60f" + +[[package]] name = "hnsw_rs" version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -2129,6 +2222,12 @@ ] [[package]] +name = "http-range-header" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9171a2ea8a68358193d15dd5d70c1c10a2afc3e7e4c5bc92bc9f025cebd7359c" + +[[package]] name = "httparse" version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -2473,6 +2572,33 @@ checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695" [[package]] +name = "itertools" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +dependencies = [ + "either", +] + +[[package]] +name = "itertools" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" +dependencies = [ + "either", +] + +[[package]] +name = "itertools" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" +dependencies = [ + "either", +] + +[[package]] name = "itoa" version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -2601,6 +2727,16 @@ ] [[package]] +name = "kstring" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "558bf9508a558512042d3095138b1f7b8fe90c5467d94f9f1da28b3731c5dbd1" +dependencies = [ + "serde", + "static_assertions", +] + +[[package]] name = "kuchikiki" version = "0.8.8-speedreader" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -2669,9 +2805,9 @@ [[package]] name = "libm" -version = "0.2.16" +version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981" +checksum = "8355be11b20d696c8f18f6cc018c4e372165b1fa8126cef092399c9951984ffa" [[package]] name = "libredox" @@ -2702,6 +2838,63 @@ checksum = "df1d3c3b53da64cf5760482273a98e575c651a67eec7f77df96b5b642de8f039" [[package]] +name = "liquid" +version = "0.26.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e9338405fdbc0bce9b01695b2a2ef6b20eca5363f385d47bce48ddf8323cc25" +dependencies = [ + "doc-comment", + "liquid-core", + "liquid-derive", + "liquid-lib", + "serde", +] + +[[package]] +name = "liquid-core" +version = "0.26.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "feb8fed70857010ed9016ed2ce5a7f34e7cc51d5d7255c9c9dc2e3243e490b42" +dependencies = [ + "anymap2", + "itertools 0.13.0", + "kstring", + "liquid-derive", + "num-traits", + "pest", + "pest_derive", + "regex", + "serde", + "time", +] + +[[package]] +name = "liquid-derive" +version = "0.26.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b51f1d220e3fa869e24cfd75915efe3164bd09bb11b3165db3f37f57bf673e3" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.115", +] + +[[package]] +name = "liquid-lib" +version = "0.26.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee1794b5605e9f8864a8a4f41aa97976b42512cc81093f8c885d29fb94c6c556" +dependencies = [ + "itertools 0.13.0", + "liquid-core", + "once_cell", + "percent-encoding", + "regex", + "time", + "unicode-segmentation", +] + +[[package]] name = "litemap" version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -2726,6 +2919,12 @@ ] [[package]] +name = "lzma-rust2" +version = "0.15.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1670343e58806300d87950e3401e820b519b9384281bbabfb15e3636689ffd69" + +[[package]] name = "mac" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -2741,6 +2940,12 @@ ] [[package]] +name = "maplit" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e2e65a1a2e43cfcb47a895c4c8b10d1f4a61097f9f254f183aee60cad9c651d" + +[[package]] name = "markup5ever" version = "0.14.1" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -2804,6 +3009,15 @@ checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" [[package]] +name = "memmap2" +version = "0.9.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "714098028fe011992e1c3962653c96b2d578c4b4bce9036e15ff220319b1e0e3" +dependencies = [ + "libc", +] + +[[package]] name = "memoffset" version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -2828,6 +3042,16 @@ checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" [[package]] +name = "mime_guess" +version = "2.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7c44f8e672c00fe5308fa235f821cb4198414e1c77935c1ab6948d3fd78550e" +dependencies = [ + "mime", + "unicase", +] + +[[package]] name = "miniz_oxide" version = "0.8.9" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -2917,6 +3141,36 @@ ] [[package]] +name = "ndarray" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "882ed72dce9365842bf196bdeedf5055305f11fc8c03dee7bb0194a6cad34841" +dependencies = [ + "matrixmultiply", + "num-complex", + "num-integer", + "num-traits", + "portable-atomic", + "portable-atomic-util", + "rawpointer", +] + +[[package]] +name = "ndarray" +version = "0.17.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "520080814a7a6b4a6e9070823bb24b4531daac8c4627e08ba5de8c5ef2f2752d" +dependencies = [ + "matrixmultiply", + "num-complex", + "num-integer", + "num-traits", + "portable-atomic", + "portable-atomic-util", + "rawpointer", +] + +[[package]] name = "ndk" version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -2972,6 +3226,24 @@ checksum = "72ef4a56884ca558e5ddb05a1d1e7e1bfd9a68d9ed024c21704cc98872dae1bb" [[package]] +name = "nom" +version = "8.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df9761775871bdef83bee530e60050f7e54b1105350d6884eb0fb4f46c2f9405" +dependencies = [ + "memchr", +] + +[[package]] +name = "nom-language" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2de2bc5b451bfedaef92c90b8939a8fff5770bdcc1fafd6239d086aab8fa6b29" +dependencies = [ + "nom", +] + +[[package]] name = "num-bigint" version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -3379,6 +3651,30 @@ ] [[package]] +name = "ort" +version = "2.0.0-rc.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7de3af33d24a745ffb8fab904b13478438d1cd52868e6f17735ef6e1f8bf133" +dependencies = [ + "ndarray 0.17.2", + "ort-sys", + "smallvec", + "tracing", + "ureq", +] + +[[package]] +name = "ort-sys" +version = "2.0.0-rc.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7b497d21a8b6fbb4b5a544f8fadb77e801a09ae0add9e411d31c6f89e3c1e90" +dependencies = [ + "hmac-sha256", + "lzma-rust2", + "ureq", +] + +[[package]] name = "os_pipe" version = "1.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -3473,6 +3769,12 @@ checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" [[package]] +name = "pastey" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35fb2e5f958ec131621fdd531e9fc186ed768cbe395337403ae56c17a74c68ec" + +[[package]] name = "pathdiff" version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -3494,6 +3796,49 @@ checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" [[package]] +name = "pest" +version = "2.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0848c601009d37dfa3430c4666e147e49cdcf1b92ecd3e63657d8a5f19da662" +dependencies = [ + "memchr", + "ucd-trie", +] + +[[package]] +name = "pest_derive" +version = "2.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11f486f1ea21e6c10ed15d5a7c77165d0ee443402f0780849d1768e7d9d6fe77" +dependencies = [ + "pest", + "pest_generator", +] + +[[package]] +name = "pest_generator" +version = "2.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8040c4647b13b210a963c1ed407c1ff4fdfa01c31d6d2a098218702e6664f94f" +dependencies = [ + "pest", + "pest_meta", + "proc-macro2", + "quote", + "syn 2.0.115", +] + +[[package]] +name = "pest_meta" +version = "2.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89815c69d36021a140146f26659a81d6c2afa33d216d736dd4be5381a7362220" +dependencies = [ + "pest", + "sha2", +] + +[[package]] name = "pgvector" version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -3757,6 +4102,15 @@ ] [[package]] +name = "primal-check" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc0d895b311e3af9902528fbb8f928688abbd95872819320517cc24ca6b2bd08" +dependencies = [ + "num-integer", +] + +[[package]] name = "proc-macro-crate" version = "1.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -3860,6 +4214,29 @@ ] [[package]] +name = "prost" +version = "0.11.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b82eaa1d779e9a4bc1c3217db8ffbeabaae1dca241bf70183242128d48681cd" +dependencies = [ + "bytes", + "prost-derive", +] + +[[package]] +name = "prost-derive" +version = "0.11.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5d2d8d10f3c6ded6da8b05b5fb3b8a5082514344d56c9f871412d29b4e075b4" +dependencies = [ + "anyhow", + "itertools 0.10.5", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] name = "ptr_meta" version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -4002,6 +4379,16 @@ ] [[package]] +name = "rand_distr" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32cb0b9bc82b0a0876c2dd994a7e7a2683d3e7390ca40e6886785ef0c7e3ee31" +dependencies = [ + "num-traits", + "rand 0.8.5", +] + +[[package]] name = "rand_hc" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -4349,6 +4736,20 @@ ] [[package]] +name = "rustfft" +version = "6.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21db5f9893e91f41798c88680037dba611ca6674703c1a18601b01a72c8adb89" +dependencies = [ + "num-complex", + "num-integer", + "num-traits", + "primal-check", + "strength_reduce", + "transpose", +] + +[[package]] name = "rustix" version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -4367,6 +4768,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c665f33d38cea657d9614f766881e4d510e0eda4239891eea56b4cadcf01801b" dependencies = [ + "log", "once_cell", "ring", "rustls-pki-types", @@ -4408,6 +4810,16 @@ checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f" [[package]] +name = "safetensors" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "172dd94c5a87b5c79f945c863da53b2ebc7ccef4eca24ac63cca66a41aab2178" +dependencies = [ + "serde", + "serde_json", +] + +[[package]] name = "same-file" version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -4417,6 +4829,15 @@ ] [[package]] +name = "scan_fmt" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b53b0a5db882a8e2fdaae0a43f7b39e7e9082389e978398bdf223a55b581248" +dependencies = [ + "regex", +] + +[[package]] name = "schannel" version = "0.1.28" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -4977,6 +5398,17 @@ ] [[package]] +name = "socks" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0c3dbbd9ae980613c6dd8e28a9407b50509d3803b57624d5dfe8315218cd58b" +dependencies = [ + "byteorder", + "libc", + "winapi", +] + +[[package]] name = "softbuffer" version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -5265,6 +5697,23 @@ checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" [[package]] +name = "strength_reduce" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe895eb47f22e2ddd4dabc02bce419d2e643c8e3b585c78158b349195bc24d82" + +[[package]] +name = "string-interner" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07f9fdfdd31a0ff38b59deb401be81b73913d76c9cc5b1aed4e1330a223420b9" +dependencies = [ + "cfg-if", + "hashbrown 0.14.5", + "serde", +] + +[[package]] name = "string_cache" version = "0.8.9" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -5483,6 +5932,17 @@ checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" [[package]] +name = "tar" +version = "0.4.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d863878d212c87a19c1a610eb53bb01fe12951c0501cf5a0d65f724914a667a" +dependencies = [ + "filetime", + "libc", + "xattr", +] + +[[package]] name = "target-lexicon" version = "0.12.16" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -6110,11 +6570,20 @@ dependencies = [ "bitflags 2.11.0", "bytes", + "futures-core", "futures-util", "http", "http-body", + "http-body-util", + "http-range-header", + "httpdate", "iri-string", + "mime", + "mime_guess", + "percent-encoding", "pin-project-lite", + "tokio", + "tokio-util", "tower", "tower-layer", "tower-service", @@ -6166,6 +6635,159 @@ ] [[package]] +name = "tract-core" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b65d67f5190132365dda73fe215bfc5e01b031e8cbfbea9d486bb5b0dbba3545" +dependencies = [ + "anyhow", + "anymap3", + "bit-set", + "derive-new", + "downcast-rs", + "dyn-clone", + "lazy_static", + "log", + "maplit", + "ndarray 0.16.1", + "num-complex", + "num-integer", + "num-traits", + "pastey", + "rustfft", + "smallvec", + "tract-data", + "tract-linalg", +] + +[[package]] +name = "tract-data" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73cd7fda1e5e8b854ea3abdd09126a87fc4af81e6d1e29ec1710a8a4abf4f13a" +dependencies = [ + "anyhow", + "downcast-rs", + "dyn-clone", + "dyn-hash", + "half", + "itertools 0.12.1", + "lazy_static", + "libm", + "maplit", + "ndarray 0.16.1", + "nom", + "nom-language", + "num-integer", + "num-traits", + "parking_lot", + "scan_fmt", + "smallvec", + "string-interner", +] + +[[package]] +name = "tract-hir" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "554df991b647dba8af0547ee5838b6912ed20b424f2adda0ea0b7faf8db1b151" +dependencies = [ + "derive-new", + "log", + "tract-core", +] + +[[package]] +name = "tract-linalg" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e72097a89cc4e7c5f1bc4f854b9294dd30fa6f6d8f7f409c556953b49078c94f" +dependencies = [ + "byteorder", + "cc", + "derive-new", + "downcast-rs", + "dyn-clone", + "dyn-hash", + "half", + "lazy_static", + "liquid", + "liquid-core", + "liquid-derive", + "log", + "num-traits", + "pastey", + "scan_fmt", + "smallvec", + "time", + "tract-data", + "unicode-normalization", + "walkdir", +] + +[[package]] +name = "tract-nnef" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45b3755dd0948111b407085d11033ba218cb85b85ce8d795cec2b8353db552ea" +dependencies = [ + "byteorder", + "flate2", + "liquid", + "liquid-core", + "log", + "nom", + "nom-language", + "safetensors", + "serde_json", + "tar", + "tract-core", + "walkdir", +] + +[[package]] +name = "tract-onnx" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac23ad1d2d5da3256ae1a78757b1072a8a3fac2a4b28d27cfb561c5942ec2701" +dependencies = [ + "bytes", + "derive-new", + "log", + "memmap2", + "num-integer", + "prost", + "smallvec", + "tract-hir", + "tract-nnef", + "tract-onnx-opl", +] + +[[package]] +name = "tract-onnx-opl" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87561bf0b84f74a124afc0f1997682728da6cd821083511e0357432954fd24f6" +dependencies = [ + "getrandom 0.2.17", + "log", + "rand 0.8.5", + "rand_distr", + "rustfft", + "tract-nnef", +] + +[[package]] +name = "transpose" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ad61aed86bc3faea4300c7aee358b4c6d0c8d6ccc36524c96e4c92ccf26e77e" +dependencies = [ + "num-integer", + "strength_reduce", +] + +[[package]] name = "tray-icon" version = "0.21.3" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -6206,6 +6828,12 @@ checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb" [[package]] +name = "ucd-trie" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2896d95c02a80c6d6a5d6e953d479f5ddf2dfdb6a244441010e373ac0fb88971" + +[[package]] name = "unic-char-property" version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -6247,6 +6875,12 @@ ] [[package]] +name = "unicase" +version = "2.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbc4bc3a9f746d862c45cb89d705aa10f187bb96c76001afab07a0d35ce60142" + +[[package]] name = "unicode-bidi" version = "0.3.18" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -6298,6 +6932,35 @@ checksum = "6d49784317cd0d1ee7ec5c716dd598ec5b4483ea832a2dced265471cc0f690ae" [[package]] +name = "ureq" +version = "3.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdc97a28575b85cfedf2a7e7d3cc64b3e11bd8ac766666318003abbacc7a21fc" +dependencies = [ + "base64 0.22.1", + "log", + "percent-encoding", + "rustls", + "rustls-pki-types", + "socks", + "ureq-proto", + "utf-8", + "webpki-roots 1.0.6", +] + +[[package]] +name = "ureq-proto" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d81f9efa9df032be5934a46a068815a10a042b494b6a58cb0a1a97bb5467ed6f" +dependencies = [ + "base64 0.22.1", + "http", + "httparse", + "log", +] + +[[package]] name = "url" version = "2.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -7434,6 +8097,16 @@ ] [[package]] +name = "xattr" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32e45ad4206f6d2479085147f02bc2ef834ac85886624a23575ae137c8aa8156" +dependencies = [ + "libc", + "rustix", +] + +[[package]] name = "yansi" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" diff --git a/src/backend/Cargo.toml b/src/backend/Cargo.toml index 2d49d11..45c02d7 100644 --- a/src/backend/Cargo.toml +++ b/src/backend/Cargo.toml @@ -10,6 +10,13 @@ rust-version = "1.77.2" default-run = "app" +[features] +default = ["community"] +# Community 版: LSA のみ。リンク時に LSA オブジェクトのみ含まれる。 +community = [] +# Pro 版: tract (ONNX) による埋め込み。tract 失敗時は ort (ONNX Runtime) にフォールバック。 +pro = ["dep:tract-onnx", "dep:ort"] + # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [lib] @@ -31,8 +38,9 @@ sea-orm = { version = "1.1", features = ["sqlx-sqlite", "runtime-tokio-rustls", "macros", "with-chrono"] } # Web Server (MCP) axum = { version = "0.7", features = ["macros"] } +tower = "0.5" tokio = { version = "1", features = ["full"] } -tower-http = { version = "0.6", features = ["cors", "trace"] } +tower-http = { version = "0.6", features = ["cors", "trace", "fs"] } futures = "0.3" tokio-stream = { version = "0.1", features = ["sync"] } anyhow = "1" @@ -50,6 +58,9 @@ # SVD は rsvd, ndarray-linalg なしで実施する方法を模索中 bincode = "1.3" uuid = { version = "1", features = ["v4"] } +# Pro 版: ONNX 埋め込み (tract 優先、失敗時 ort でフォールバック) +tract-onnx = { version = "0.22", optional = true } +ort = { version = "2.0.0-rc.12", optional = true, default-features = false, features = ["std", "download-binaries", "tls-rustls"] } [dev-dependencies] tempfile = "3.10" diff --git a/src/backend/src/db/mod.rs b/src/backend/src/db/mod.rs index f9b0863..b5a819e 100644 --- a/src/backend/src/db/mod.rs +++ b/src/backend/src/db/mod.rs @@ -149,9 +149,32 @@ // vec_items の次元数チェックと初期化 check_and_init_vector_table(pool, dimension).await?; + // items にあって items_fts にない行を同期(既存DB・マイグレーション後の検索を有効にする) + sync_items_fts(pool).await?; + Ok(()) } +/// items の内容を items_fts に反映する。items に存在するが items_fts に rowid が無い行を挿入する。 +async fn sync_items_fts(pool: &SqlitePool) -> Result<(), String> { + let n = heal_items_fts(pool).await?; + if n > 0 { + log::info!("[db] items_fts: synced {} rows from items.", n); + } + Ok(()) +} + +/// items → items_fts の同期を手動実行(テスト/ヒール用)。同期した行数を返す。 +pub async fn heal_items_fts(pool: &SqlitePool) -> Result { + let updated = sqlx::query( + "INSERT INTO items_fts(rowid, content) SELECT i.id, i.content FROM items i WHERE i.id NOT IN (SELECT rowid FROM items_fts)" + ) + .execute(pool) + .await + .map_err(|e| e.to_string())?; + Ok(updated.rows_affected()) +} + async fn check_and_init_vector_table(pool: &SqlitePool, dimension: usize) -> Result<(), String> { // 現在のテーブル定義を確認 @@ -324,7 +347,7 @@ // DLLのパス取得(ビルドディレクトリにあることが前提) // テスト環境では $OUT_DIR や $CARGO_MANIFEST_DIR 基準で探す let manifest_dir = env!("CARGO_MANIFEST_DIR"); - let ext_path = Path::new(manifest_dir).join("../node_modules/sqlite-vec-windows-x64/vec0.dll"); + let ext_path = Path::new(manifest_dir).join("../../node_modules/sqlite-vec-windows-x64/vec0.dll"); if !ext_path.exists() { println!("Skipping test: vec0.dll not found at {:?}", ext_path); @@ -363,4 +386,50 @@ pool2.close().await; } + + /// vec0 拡張で vec_items に INSERT した行が COMMIT 後に見えるか検証(永続化不具合の原因切り分け用) + #[tokio::test] + async fn test_vec_items_insert_persists_after_commit() { + let dir = tempdir().unwrap(); + let db_path = dir.path().join("test_vec_persist.db"); + let manifest_dir = env!("CARGO_MANIFEST_DIR"); + let ext_path = Path::new(manifest_dir).join("../../node_modules/sqlite-vec-windows-x64/vec0.dll"); + if !ext_path.exists() { + println!("Skipping test: vec0.dll not found at {:?}", ext_path); + return; + } + let pool = initialize_database(&db_path, &ext_path, 768).await.unwrap(); + + // 768次元の JSON 配列(vec0 の embedding 形式) + let embedding: Vec = (0..768).map(|i| (i as f32) * 0.001).collect(); + let emb_json = serde_json::to_string(&embedding).unwrap(); + + // トランザクション内で INSERT → 同一 tx で COUNT → COMMIT + let mut tx = pool.begin().await.unwrap(); + let r = sqlx::query("INSERT INTO vec_items (id, embedding) VALUES (?, ?)") + .bind(1i64) + .bind(&emb_json) + .execute(&mut *tx) + .await; + assert!(r.is_ok(), "INSERT should succeed: {:?}", r.err()); + let rows = r.unwrap().rows_affected(); + assert_eq!(rows, 1, "rows_affected should be 1"); + + let count_in_tx: (i64,) = sqlx::query_as("SELECT COUNT(*) FROM vec_items") + .fetch_one(&mut *tx) + .await + .unwrap(); + assert_eq!(count_in_tx.0, 1, "COUNT(*) within tx should be 1 (vec0 visibility in tx)"); + + tx.commit().await.unwrap(); + + // COMMIT 後に別クエリで COUNT(プールの別コネクション) + let count_after: (i64,) = sqlx::query_as("SELECT COUNT(*) FROM vec_items") + .fetch_one(&pool) + .await + .unwrap(); + assert_eq!(count_after.0, 1, "COUNT(*) after commit should be 1 (vec0 must persist)"); + + pool.close().await; + } } diff --git a/src/backend/src/dev_static.rs b/src/backend/src/dev_static.rs new file mode 100644 index 0000000..f384eee --- /dev/null +++ b/src/backend/src/dev_static.rs @@ -0,0 +1,85 @@ +//! 開発時のみ使用: フロントエンド静的ファイル配信用のパス解決とルーター。 +//! モノリシック化で beforeDevCommand (Node) を廃止し、同一プロセスで 8474 を listen するために使う。 + +use std::path::{Path, PathBuf}; +use axum::Router; +use tower_http::services::ServeDir; + +/// 開発時用のフロントエンドルート(`src/frontend`)の絶対パスを返す。 +/// テスト時は CARGO_MANIFEST_DIR、実行時は exe_dir から解決する。 +fn resolve_dev_frontend_dir(exe_dir: Option<&Path>) -> Option { + // テスト時: CARGO_MANIFEST_DIR が設定されている(src/backend の兄弟 = src/frontend) + if let Some(manifest_dir) = std::env::var_os("CARGO_MANIFEST_DIR") + .map(PathBuf::from) + .filter(|p| p.ends_with("backend")) + { + let parent = manifest_dir.parent()?; + let frontend = parent.join("frontend"); + if frontend.join("index.html").exists() { + return Some(frontend); + } + } + // 実行時: バイナリは target/debug にあるので 2 階層上がプロジェクトルート + let exe_dir = exe_dir?; + let project_root = exe_dir.parent()?.parent()?; + let frontend = project_root.join("src").join("frontend"); + if frontend.join("index.html").exists() { + Some(frontend) + } else { + None + } +} + +/// 開発時用のフロントエンドルート(テスト用。CARGO_MANIFEST_DIR のみ使用)。 +pub fn dev_frontend_dir() -> Option { + resolve_dev_frontend_dir(None) +} + +/// 実行時用: exe の親ディレクトリからプロジェクトルートを辿って frontend を解決する。 +pub fn dev_frontend_dir_from_exe(exe_dir: &Path) -> Option { + resolve_dev_frontend_dir(Some(exe_dir)) +} + +/// 開発用フロントルートが解決できる場合、静的配信用の Axum Router を返す。 +/// exe_dir: 実行時は Some(バイナリの親ディレクトリ)。テスト時は None で CARGO_MANIFEST_DIR を使用。 +pub fn dev_static_router(exe_dir: Option<&Path>) -> Option { + let dir = resolve_dev_frontend_dir(exe_dir)?; + let serve_dir = ServeDir::new(dir).append_index_html_on_directories(true); + Some(Router::new().fallback_service(serve_dir)) +} + +#[cfg(test)] +mod tests { + use super::*; + use axum::body::Body; + use axum::http::Request; + use tower::util::ServiceExt; + + #[test] + fn dev_frontend_dir_resolves_to_path_with_index_html() { + let dir = dev_frontend_dir(); + assert!( + dir.is_some(), + "dev_frontend_dir() should return Some when run from workspace (crate at src/backend)" + ); + let path = dir.unwrap(); + assert!( + path.join("index.html").exists(), + "resolved path must contain index.html: {:?}", + path + ); + assert!( + path.to_string_lossy().contains("frontend"), + "path should contain 'frontend': {:?}", + path + ); + } + + #[tokio::test] + async fn dev_static_router_returns_200_for_root() { + let router = dev_static_router(None).expect("dev_static_router(None) should be Some when run from workspace"); + let req = Request::get("/").body(Body::empty()).unwrap(); + let response = router.oneshot(req).await.unwrap(); + assert_eq!(response.status(), 200, "GET / should return 200 when serving dev frontend"); + } +} diff --git a/src/backend/src/lib.rs b/src/backend/src/lib.rs index 476fe3c..e9d9261 100644 --- a/src/backend/src/lib.rs +++ b/src/backend/src/lib.rs @@ -1,7 +1,13 @@ -// 使用中モデル名をグローバルで保持 -#[allow(dead_code)] -static MODEL_NAME: &str = "LSA (Local Semantic Analysis)"; -// モデル名を返すAPI +// エディションとモデル名(ビルド時に feature で切り替え) +#[cfg(feature = "community")] +const EDITION: &str = "community"; +#[cfg(feature = "community")] +const MODEL_NAME: &str = "LSA (Local Semantic Analysis)"; +#[cfg(feature = "pro")] +const EDITION: &str = "pro"; +#[cfg(feature = "pro")] +const MODEL_NAME: &str = "sentence-bert-base-ja (Pro)"; + #[tauri::command] #[allow(dead_code)] fn get_model_name() -> String { @@ -95,19 +101,17 @@ Ok(lines[start..].join("\n")) } pub mod db; +pub mod dev_static; pub mod utils; pub mod mcp; use std::sync::atomic::{AtomicU64, Ordering}; -use std::sync::{Arc, Mutex}; use std::path::PathBuf; use std::time::{SystemTime, UNIX_EPOCH}; use tauri::Manager; use tauri::path::BaseDirectory; use tauri::menu::{Menu, MenuItem}; use tauri::tray::{TrayIconBuilder, TrayIconEvent}; -use tauri_plugin_shell::process::CommandChild; - #[allow(dead_code)] struct AppState { db_pool: sqlx::SqlitePool, @@ -115,7 +119,6 @@ #[cfg_attr(mobile, tauri::mobile_entry_point)] pub fn run() { - let llama_child: Arc>> = Arc::new(Mutex::new(None)); tauri::Builder::default() .plugin(tauri_plugin_shell::init()) .plugin(tauri_plugin_autostart::init( @@ -187,9 +190,7 @@ get_app_settings, set_app_settings, ]) - .setup({ - let llama_child = llama_child.clone(); - move |app| { + .setup(move |app| { let boot_start = std::time::Instant::now(); // Resolve paths @@ -200,10 +201,9 @@ let db_path = app_data_dir.join("telos.db"); let resource_dir = app.path().resource_dir().unwrap_or_default(); - let bin_dir = resource_dir.join("bin"); - let model_path = bin_dir.join("gemma-3-270m-it-Q4_K_M.gguf"); let exe_dir = std::env::current_exe().ok().and_then(|p| p.parent().map(|p| p.to_path_buf())); + log::info!("[BOOT] Edition: {}", EDITION); log::info!("[BOOT] Phase 1: Paths resolved ({}ms)", boot_start.elapsed().as_millis()); log::info!(" resource_dir: {:?}, exe_dir: {:?}", resource_dir, exe_dir); @@ -251,7 +251,6 @@ let show_i = MenuItem::with_id(app, "show", "表示", true, None::<&str>)?; let menu = Menu::with_items(app, &[&show_i, &quit_i])?; - let llama_child_tray = llama_child.clone(); let _tray = TrayIconBuilder::new() .icon(app.default_window_icon().unwrap().clone()) .menu(&menu) @@ -259,9 +258,6 @@ .on_menu_event(move |app, event| { match event.id.as_ref() { "quit" => { - if let Some(child) = llama_child_tray.lock().unwrap().take() { - let _ = child.kill(); - } app.exit(0); } "show" => { @@ -315,51 +311,15 @@ return Err(std::io::Error::new(std::io::ErrorKind::NotFound, msg).into()); } - /* - if model_path.exists() { - let (mut rx, child) = app - .shell() - .sidecar("llama-server") - .expect("failed to create sidecar") - .args([ - "--model", - model_path.to_str().unwrap(), - "--port", - "8080", - "--embedding", - "--pooling", - "mean", - ]) - .spawn() - .expect("failed to spawn sidecar"); - - log::info!("llama-server sidecar started (Qwen3-4B)"); - *llama_child.lock().unwrap() = Some(child); - std::thread::spawn(move || { - while let Some(event) = rx.blocking_recv() { - match event { - CommandEvent::Stdout(line) => { - log::info!("llama-server: {}", String::from_utf8_lossy(&line)) - } - CommandEvent::Stderr(line) => { - let s = String::from_utf8_lossy(&line); - if !s.contains("post_embedding") { // 埋め込みリクエスト時のノイズを軽減 - log::error!("llama-server: {}", s) - } - } - _ => {} - } - } - }); - } - */ - log::info!("[BOOT] Phase 4: Initializing database at {:?} ...", db_path); let pool = tauri::async_runtime::block_on(async { - let dimension = 50; // LSA のランクに合わせて 50次元に設定 + #[cfg(feature = "community")] + let dimension = 50; + #[cfg(feature = "pro")] + let dimension = crate::utils::embedding_pro::ProEmbeddingModel::embed_dim(); match db::initialize_database(&db_path, &vec0_path, dimension).await { Ok(pool) => { - log::info!("[BOOT] Phase 4: Database ready (LSA-mode) ({}ms)", boot_start.elapsed().as_millis()); + log::info!("[BOOT] Phase 4: Database ready ({}ms)", boot_start.elapsed().as_millis()); pool } Err(e) => { @@ -373,19 +333,72 @@ db_pool: pool.clone(), }); + if let Some(router) = dev_static::dev_static_router(exe_dir.as_deref()) { + let bind_addr = "127.0.0.1:8474"; + if let Ok(listener) = tauri::async_runtime::block_on(tokio::net::TcpListener::bind(bind_addr)) { + log::info!("[BOOT] Dev static server listening on {}", listener.local_addr().unwrap()); + tauri::async_runtime::spawn(async move { + let _ = axum::serve(listener, router).await; + }); + } else { + log::warn!("[BOOT] Dev static server (8474) bind failed"); + } + } else { + log::info!("[BOOT] Dev static: frontend dir not resolved (exe_dir={:?}), skipping 8474", exe_dir); + } log::info!("[BOOT] Phase 5: Starting MCP server on 127.0.0.1:3001 ..."); - use tokio::sync::RwLock; - let llama_status = Arc::new(RwLock::new("stopped".to_string())); + #[cfg(feature = "pro")] + let embedding_model_dir = { + let env_path = std::env::var_os("TELOS_EMBEDDING_MODEL_DIR").map(std::path::PathBuf::from); + // インストール後は Tauri の resolve でバンドル先を正しく解決する(resource_dir だけだと NSIS 配置先とずれる場合がある) + let resolved_resource = app + .path() + .resolve("embedding_model/model_quantized.onnx", BaseDirectory::Resource) + .ok() + .and_then(|p| p.parent().map(|p| p.to_path_buf())) + .filter(|p| p.join("model_quantized.onnx").exists()); + let resource_path = resource_dir.join("embedding_model"); + let dev_path = exe_dir.as_ref().and_then(|exe| { + exe.parent().and_then(|p| p.parent()).map(|root| root.join("embedding_model")) + }); + let resource_path_log = resource_path.clone(); + let dev_path_log = dev_path.clone(); + let chosen = env_path + .filter(|p| p.join("model_quantized.onnx").exists()) + .or(resolved_resource) + .or_else(|| if resource_path.join("model_quantized.onnx").exists() { Some(resource_path) } else { None }) + .or_else(|| dev_path.filter(|p| p.join("model_quantized.onnx").exists())); + if let Some(ref p) = chosen { + log::info!("[BOOT] Pro: embedding_model dir = {:?}", p); + } else { + log::warn!( + "[BOOT] Pro: embedding_model not found (model_quantized.onnx required). Tried: TELOS_EMBEDDING_MODEL_DIR, resolve(Resource), {:?}, {:?}. Set TELOS_EMBEDDING_MODEL_DIR or place embedding_model in exe parent.", + resource_path_log, + dev_path_log + ); + } + chosen + }; tauri::async_runtime::spawn({ let pool = pool.clone(); + #[cfg(feature = "pro")] + let embedding_dir = embedding_model_dir; async move { - mcp::run_server(3001, app_data_dir.clone(), pool, llama_status, MODEL_NAME.to_string()).await; + #[cfg(feature = "community")] + mcp::run_server(3001, app_data_dir.clone(), pool, MODEL_NAME.to_string(), EDITION.to_string()).await; + #[cfg(feature = "pro")] + mcp::run_server(3001, app_data_dir.clone(), pool, MODEL_NAME.to_string(), EDITION.to_string(), embedding_dir).await; } }); + if std::env::var_os("TELOS_HEADLESS").map(|v| v == "1").unwrap_or(false) { + if let Some(w) = app.get_webview_window("main") { + let _ = w.hide(); + log::info!("[BOOT] Headless mode: main window hidden."); + } + } log::info!("[BOOT] Setup complete ({}ms). Window ready; MCP and LSA/HNSW continue in background.", boot_start.elapsed().as_millis()); Ok(()) - } }) .on_window_event(|window, event| { if let tauri::WindowEvent::CloseRequested { api, .. } = event { diff --git a/src/backend/src/mcp/handlers.rs b/src/backend/src/mcp/handlers.rs index 6d9bfe0..de04da8 100644 --- a/src/backend/src/mcp/handlers.rs +++ b/src/backend/src/mcp/handlers.rs @@ -11,11 +11,6 @@ use serde::Deserialize; use crate::mcp::types::AppState; -pub async fn llama_status_handler(State(state): State) -> impl IntoResponse { - let status = state.llama_status.read().await.clone(); - Json(serde_json::json!({ "status": status })) -} - pub async fn doc_count_handler(State(state): State) -> impl IntoResponse { let count: i64 = sqlx::query_scalar("SELECT COUNT(*) FROM documents") .fetch_one(&state.db_pool) @@ -28,6 +23,23 @@ Json(serde_json::json!({ "model_name": state.model_name })) } +pub async fn edition_handler(State(state): State) -> impl IntoResponse { + #[cfg(feature = "pro")] + let obj = { + let loaded = state.embedding_model.read().await.is_some(); + serde_json::json!({ "edition": state.edition, "embedding_loaded": loaded }) + }; + #[cfg(not(feature = "pro"))] + let obj = serde_json::json!({ "edition": state.edition }); + Json(obj) +} + +pub async fn heal_handler(State(state): State) -> impl IntoResponse { + let synced = crate::db::heal_items_fts(&state.db_pool).await.unwrap_or(0); + log::info!("[heal] items_fts: synced {} rows.", synced); + Json(serde_json::json!({ "synced": synced })) +} + pub async fn indexing_status_handler(State(state): State) -> impl IntoResponse { let status = state.indexing_status.read().await.clone(); Json(serde_json::json!({ "status": status })) diff --git a/src/backend/src/mcp/mod.rs b/src/backend/src/mcp/mod.rs index dac4403..639faae 100644 --- a/src/backend/src/mcp/mod.rs +++ b/src/backend/src/mcp/mod.rs @@ -23,10 +23,11 @@ Router::new() .route("/sse", get(handlers::sse_handler)) .route("/messages", post(mcp_messages_handler)) - .route("/llama_status", get(handlers::llama_status_handler)) + .route("/edition", get(handlers::edition_handler)) .route("/doc_count", get(handlers::doc_count_handler)) .route("/model_name", get(handlers::model_name_handler)) .route("/indexing_status", get(handlers::indexing_status_handler)) + .route("/heal", get(handlers::heal_handler)) .route("/version", get(handlers::version_handler)) .route("/settings", get(handlers::settings_get_handler).post(handlers::settings_post_handler)) .layer(cors) @@ -37,8 +38,9 @@ port: u16, app_data_dir: std::path::PathBuf, db_pool: sqlx::SqlitePool, - llama_status: Arc>, model_name: String, + edition: String, + #[cfg(feature = "pro")] embedding_model_dir: Option, ) { let mcp_start = std::time::Instant::now(); log::info!("[BOOT] MCP: run_server started (port={})", port); @@ -46,26 +48,51 @@ let (tx, _rx) = broadcast::channel(100); let tokenizer = Arc::new(crate::utils::tokenizer::JapaneseTokenizer::new().unwrap()); + #[cfg(feature = "pro")] + let embedding_model = { + let had_dir = embedding_model_dir.is_some(); + let model = embedding_model_dir.and_then(|dir| { + log::info!("[BOOT] Pro: loading embedding model from {:?}...", dir); + match crate::utils::embedding_pro::ProEmbeddingModel::load(&dir) { + Ok(m) => { + log::info!("[BOOT] Pro: embedding model loaded."); + Some(m) + } + Err(e) => { + log::warn!("[BOOT] Pro: embedding model load failed: {}", e); + None + } + } + }); + if model.is_none() && had_dir { + log::warn!("[BOOT] Pro: running without embedding (vec search disabled until model is available)."); + } + Arc::new(RwLock::new(model)) + }; + let state = AppState { app_data_dir, db_pool, tx, - llama_status, model_name, + edition, sessions: Arc::new(RwLock::new(HashMap::new())), tokenizer, + #[cfg(feature = "community")] lsa_model: Arc::new(RwLock::new(None)), + #[cfg(feature = "pro")] + embedding_model, + #[cfg(feature = "pro")] + pro_hnsw_rebuild_requested: Arc::new(AtomicBool::new(false)), hnsw_index: Arc::new(RwLock::new(None)), changes_since_train: Arc::new(AtomicU64::new(0)), retrain_scheduled: Arc::new(AtomicBool::new(false)), indexing_status: Arc::new(RwLock::new("idle".to_string())), }; - let state_init = state.clone(); - tokio::spawn(async move { - system::train_lsa_and_sync_hnsw(state_init).await; - }); - log::info!("[BOOT] MCP: LSA/HNSW background task spawned ({}ms)", mcp_start.elapsed().as_millis()); + // 近似近傍検索を使うため、listen 前にインデックス構築を完了させる(spawn だと検索時に HNSW が空で 0.4 固定になる) + system::train_lsa_and_sync_hnsw(state.clone()).await; + log::info!("[BOOT] MCP: LSA/HNSW index ready ({}ms)", mcp_start.elapsed().as_millis()); let app = create_mcp_app(state); let bind_addr = format!("127.0.0.1:{}", port); diff --git a/src/backend/src/mcp/system.rs b/src/backend/src/mcp/system.rs index f6f3f66..e885d17 100644 --- a/src/backend/src/mcp/system.rs +++ b/src/backend/src/mcp/system.rs @@ -1,11 +1,168 @@ -use sqlx::Row; -use std::collections::HashMap; use std::sync::atomic::Ordering; -use std::sync::Arc; -use hnsw_rs::prelude::*; use crate::mcp::types::AppState; +#[cfg(feature = "community")] +use sqlx::Row; +#[cfg(feature = "community")] +use std::sync::Arc; + +// Pro 版: 既存 items を vec_items にバックフィルしてから HNSW を構築する +#[cfg(feature = "pro")] +pub async fn train_lsa_and_sync_hnsw(state: AppState) { + use hnsw_rs::prelude::*; + use sqlx::Row; + let dim = crate::utils::embedding_pro::ProEmbeddingModel::embed_dim(); + + { + let mut st = state.indexing_status.write().await; + *st = "syncing".to_string(); + } + let _ = state.tx.send("indexing:syncing".to_string()); + + // vec_items に無い items だけを埋め込みでバックフィルする(全件ではない) + let guard = state.embedding_model.read().await; + let mut data_to_insert: Vec<(Vec, usize)> = Vec::new(); + if let Some(ref model) = *guard { + let items_missing: Vec<(i64, String)> = match sqlx::query( + "SELECT i.id, i.content FROM items i + LEFT JOIN vec_items v ON i.id = v.id + WHERE v.id IS NULL AND i.content IS NOT NULL AND trim(i.content) != ''" + ) + .fetch_all(&state.db_pool) + .await + { + Ok(rows) => rows.iter().map(|r| (r.get::(0), r.get::(1))).collect(), + Err(e) => { + log::warn!("[BOOT] Pro backfill: failed to list items: {}", e); + Vec::new() + } + }; + if !items_missing.is_empty() { + log::info!("[BOOT] Pro backfill: embedding {} items (missing from vec_items)...", items_missing.len()); + let mut encode_fail_count = 0usize; + let mut first_encode_err: Option = None; + for (id, content) in items_missing { + match model.encode(&content) { + Ok(embedding) if embedding.len() == dim => { + data_to_insert.push((embedding, id as usize)); + } + Ok(_) => { + encode_fail_count += 1; + if first_encode_err.is_none() { + first_encode_err = Some(format!("id={} dimension mismatch", id)); + } + } + Err(e) => { + encode_fail_count += 1; + if first_encode_err.is_none() { + first_encode_err = Some(format!("id={} {}", id, e)); + } + } + } + } + if encode_fail_count > 0 { + log::warn!("[BOOT] Pro backfill: {} encode failures (first: {:?})", encode_fail_count, first_encode_err); + } + log::info!("[BOOT] Pro backfill: encoded {} vectors.", data_to_insert.len()); + // 不足分だけ vec_items に INSERT(既存は触らない) + if let Ok(mut tx) = state.db_pool.begin().await { + let mut insert_ok = 0usize; + let mut insert_err = 0usize; + for (vec, id) in &data_to_insert { + let emb_json = serde_json::to_string(vec).unwrap_or_else(|_| "[]".to_string()); + match sqlx::query("INSERT INTO vec_items (id, embedding) VALUES (?, ?)") + .bind(*id as i64) + .bind(&emb_json) + .execute(&mut *tx) + .await + { + Ok(r) => { insert_ok += r.rows_affected() as usize; } + Err(e) => { + insert_err += 1; + log::warn!("[BOOT] Pro backfill: insert vec_items id={} failed: {}", id, e); + } + } + } + // 診断: トランザクション内で INSERT 直後の件数 + let count_in_tx: Option = sqlx::query_scalar("SELECT COUNT(*) FROM vec_items") + .fetch_one(&mut *tx) + .await + .ok(); + log::info!("[BOOT] Pro backfill vec_items: insert_ok={} insert_err={} COUNT(*) within tx={:?}", insert_ok, insert_err, count_in_tx); + if let Err(e) = tx.commit().await { + log::warn!("[BOOT] Pro backfill: vec_items commit failed: {}", e); + } + } + } + } + drop(guard); + + // 診断: COMMIT 後に別コネクションで vec_items 件数を確認(永続化の有無) + if let Ok((count_after,)) = sqlx::query_as::<_, (i64,)>("SELECT COUNT(*) FROM vec_items") + .fetch_one(&state.db_pool) + .await + { + log::info!("[BOOT] Pro vec_items COUNT(*) after commit (pool): {}", count_after); + } + + // HNSW は vec_items 全体から構築(既存+今回バックフィルした分) + let from_db: Vec<(Vec, usize)> = if let Ok(rows) = sqlx::query("SELECT id, vec_to_json(embedding) FROM vec_items") + .fetch_all(&state.db_pool) + .await + { + let mut out = Vec::new(); + for row in rows { + let id: i64 = row.get(0); + let embedding_str: String = row.get(1); + if let Ok(vec) = serde_json::from_str::>(&embedding_str) { + if vec.len() == dim { + out.push((vec, id as usize)); + } + } + } + out + } else { + Vec::new() + }; + + if !from_db.is_empty() { + log::info!("[BOOT] Pro HNSW: building index for {} vectors (from vec_items)...", from_db.len()); + let hnsw = tokio::task::spawn_blocking(move || { + let hnsw: Hnsw<'static, f32, DistCosine> = Hnsw::new(16, from_db.len().max(100), 16, 200, DistCosine {}); + let refs: Vec<(&Vec, usize)> = from_db.iter().map(|(v, id)| (v, *id)).collect(); + hnsw.parallel_insert(&refs); + log::info!("[BOOT] Pro HNSW: index built."); + hnsw + }).await.ok(); + if let Some(hnsw) = hnsw { + *state.hnsw_index.write().await = Some(hnsw); + } + } else if !data_to_insert.is_empty() { + // vec_items が空(永続化されていない等)だが今回エンコードした分はある → メモリ上のデータで HNSW 構築 + log::info!("[BOOT] Pro HNSW: vec_items empty, building from {} in-memory vectors...", data_to_insert.len()); + let hnsw = tokio::task::spawn_blocking(move || { + let hnsw: Hnsw<'static, f32, DistCosine> = Hnsw::new(16, data_to_insert.len().max(100), 16, 200, DistCosine {}); + let refs: Vec<(&Vec, usize)> = data_to_insert.iter().map(|(v, id)| (v, *id)).collect(); + hnsw.parallel_insert(&refs); + hnsw + }).await.ok(); + if let Some(hnsw) = hnsw { + *state.hnsw_index.write().await = Some(hnsw); + } + } else { + log::info!("[BOOT] Pro HNSW: no vectors (add documents and run RE-INDEX)."); + } + *state.indexing_status.write().await = "idle".to_string(); + let _ = state.tx.send("indexing:idle".to_string()); +} + +#[cfg(feature = "community")] +use std::collections::HashMap; +#[cfg(feature = "community")] +use hnsw_rs::prelude::*; +#[cfg(feature = "community")] use crate::utils::lsa::LsaModel; +#[cfg(feature = "community")] pub async fn train_lsa_and_sync_hnsw(state: AppState) { let boot_start = std::time::Instant::now(); log::info!("[BOOT] LSA: starting (fetching documents)"); @@ -71,7 +228,8 @@ } } -/// DB 内の全アイテムをチェックし、ベクトルが欠落または異常(全て0)なものを補完する +/// DB 内の全アイテムをチェックし、ベクトルが欠落または異常(全て0)なものを補完する(Community 版) +#[cfg(feature = "community")] pub async fn sync_all_vectors(state: AppState, startup_hnsw: Option>) { log::info!("[BOOT] HNSW: checking vectors in vec_items..."); @@ -226,7 +384,7 @@ } } -/// 追加・更新・削除が「登録件数に対する割合」で閾値を超えたら、デバウンス後にバックグラウンドで LSA 再学習を実行する。 +/// 追加・更新・削除が「登録件数に対する割合」で閾値を超えたら、デバウンス後にバックグラウンドで LSA 再学習を実行する。(Community では LSA、Pro では no-op) /// 二重実行防止のため、1本だけスケジュールされる。 pub async fn schedule_retrain_if_needed(state: &AppState) { const CHANGE_RATIO: f64 = 0.2; // 登録ドキュメント数の 20% diff --git a/src/backend/src/mcp/tools/items.rs b/src/backend/src/mcp/tools/items.rs index ddf0a1f..f5263c9 100644 --- a/src/backend/src/mcp/tools/items.rs +++ b/src/backend/src/mcp/tools/items.rs @@ -1,4 +1,5 @@ use sqlx::Row; +#[cfg(feature = "community")] use std::collections::HashMap; use crate::mcp::types::AppState; use std::path::Path; @@ -120,11 +121,15 @@ } }; - // 2. 既存の同一ドキュメントの全チャンクを削除(上書き) + // 2. 既存の同一ドキュメントの全チャンクを削除(上書き)。items_fts も同期して削除する。 + let _ = sqlx::query("DELETE FROM items_fts WHERE rowid IN (SELECT id FROM items WHERE document_id = ?)") + .bind(doc_id) + .execute(&state.db_pool) + .await; if let Err(e) = sqlx::query("DELETE FROM items WHERE document_id = ?") .bind(doc_id) .execute(&state.db_pool) - .await + .await { log::error!("Failed to delete old chunks for document {}: {}", doc_id, e); } @@ -181,9 +186,11 @@ .await .map_err(|e| format!("Failed to insert to FTS: {}", e))?; - // LSA ベクトルの計算 - let mut lsa_vector_f32: Vec = vec![0.0; 50]; + #[cfg(feature = "community")] + let mut lsa_vector_f32: Vec = vec![0.0; 50]; + #[cfg(feature = "community")] let lsa_guard = state.lsa_model.read().await; + #[cfg(feature = "community")] if let Some(model) = lsa_guard.as_ref() { let mut query_counts = HashMap::new(); let tokens = state.tokenizer.tokenize_to_vec(content).unwrap_or_default(); @@ -207,34 +214,60 @@ } } - // vec_items に保存 - sqlx::query("INSERT INTO vec_items (id, embedding) VALUES (?, ?)") - .bind(id) - .bind(serde_json::to_string(&lsa_vector_f32).unwrap_or("[]".to_string())) - .execute(&mut *tx) - .await - .map_err(|e| format!("Failed to insert LSA vector to vec_items: {}", e))?; - - // items_lsa にも保存 - if lsa_guard.as_ref().is_some() { - let vector_blob = bincode::serialize(&lsa_vector_f32).unwrap_or_default(); - sqlx::query("INSERT INTO items_lsa (id, vector) VALUES (?, ?)") + #[cfg(feature = "community")] + { + sqlx::query("INSERT INTO vec_items (id, embedding) VALUES (?, ?)") .bind(id) - .bind(vector_blob) + .bind(serde_json::to_string(&lsa_vector_f32).unwrap_or("[]".to_string())) .execute(&mut *tx) .await - .map_err(|e| format!("Failed to insert LSA blob: {}", e))?; + .map_err(|e| format!("Failed to insert LSA vector to vec_items: {}", e))?; + if lsa_guard.as_ref().is_some() { + let vector_blob = bincode::serialize(&lsa_vector_f32).unwrap_or_default(); + sqlx::query("INSERT INTO items_lsa (id, vector) VALUES (?, ?)") + .bind(id) + .bind(vector_blob) + .execute(&mut *tx) + .await + .map_err(|e| format!("Failed to insert LSA blob: {}", e))?; + } + } + + #[cfg(feature = "pro")] + let pro_embedding: Option> = { + let guard = state.embedding_model.read().await; + guard.as_ref().and_then(|model| model.encode(content).ok()) + }; + #[cfg(feature = "pro")] + if let Some(ref embedding) = pro_embedding { + let emb_json = serde_json::to_string(embedding).unwrap_or_else(|_| "[]".to_string()); + let _ = sqlx::query("INSERT INTO vec_items (id, embedding) VALUES (?, ?)") + .bind(id) + .bind(&emb_json) + .execute(&mut *tx) + .await; } tx.commit() .await .map_err(|e| format!("Failed to commit transaction: {}", e))?; - // HNSW インデックス - let hnsw_index_guard = state.hnsw_index.read().await; - if let Some(hnsw_ptr) = hnsw_index_guard.as_ref() { - let vec_ref: &[f32] = lsa_vector_f32.as_slice(); - hnsw_ptr.insert((vec_ref, id as usize)); + #[cfg(feature = "community")] + { + let hnsw_index_guard = state.hnsw_index.read().await; + if let Some(hnsw_ptr) = hnsw_index_guard.as_ref() { + let vec_ref: &[f32] = lsa_vector_f32.as_slice(); + hnsw_ptr.insert((vec_ref, id as usize)); + } + } + + #[cfg(feature = "pro")] + if let Some(ref embedding) = pro_embedding { + let hnsw_index_guard = state.hnsw_index.read().await; + if let Some(hnsw_ptr) = hnsw_index_guard.as_ref() { + let vec_ref: &[f32] = embedding.as_slice(); + hnsw_ptr.insert((vec_ref, id as usize)); + } } Ok(id) @@ -282,38 +315,56 @@ .await .map_err(|e| format!("Failed to update FTS: {}", e))?; - let lsa_guard = state.lsa_model.read().await; - if let Some(model) = lsa_guard.as_ref() { - let mut query_counts = HashMap::new(); - let tokens = state.tokenizer.tokenize_to_vec(content).unwrap_or_default(); - for token in tokens { - if let Some(&tid) = model.vocabulary.get(&token) { - *query_counts.entry(tid).or_insert(0.0) += 1.0; + #[cfg(feature = "pro")] + { + let guard = state.embedding_model.read().await; + if let Some(ref model) = *guard { + if let Ok(embedding) = model.encode(content) { + let emb_json = serde_json::to_string(&embedding).unwrap_or_else(|_| "[]".to_string()); + let _ = sqlx::query("INSERT OR REPLACE INTO vec_items (id, embedding) VALUES (?, ?)") + .bind(id) + .bind(&emb_json) + .execute(&mut *tx) + .await; } } - let mut query_vec = ndarray::Array1::zeros(model.vocabulary.len()); - for (tid, count) in query_counts { - query_vec[tid] = count; - } + } - if let Ok(projected) = model.project_query(&query_vec) { - let mut proj_f32: Vec = projected.iter().map(|&x| x as f32).collect(); - if proj_f32.len() < 50 { proj_f32.resize(50, 0.0); } else { proj_f32.truncate(50); } + #[cfg(feature = "community")] + { + let lsa_guard = state.lsa_model.read().await; + if let Some(model) = lsa_guard.as_ref() { + let mut query_counts = HashMap::new(); + let tokens = state.tokenizer.tokenize_to_vec(content).unwrap_or_default(); + for token in tokens { + if let Some(&tid) = model.vocabulary.get(&token) { + *query_counts.entry(tid).or_insert(0.0) += 1.0; + } + } + let mut query_vec = ndarray::Array1::zeros(model.vocabulary.len()); + for (tid, count) in query_counts { + query_vec[tid] = count; + } - let vector_blob = bincode::serialize(&proj_f32).unwrap_or_default(); - sqlx::query("INSERT OR REPLACE INTO items_lsa (id, vector) VALUES (?, ?)") - .bind(id) - .bind(vector_blob) - .execute(&mut *tx) - .await - .map_err(|e| format!("Failed to update LSA vector: {}", e))?; - - sqlx::query("INSERT OR REPLACE INTO vec_items (id, embedding) VALUES (?, ?)") - .bind(id) - .bind(serde_json::to_string(&proj_f32).unwrap_or("[]".to_string())) - .execute(&mut *tx) - .await - .map_err(|e| format!("Failed to update vec_items: {}", e))?; + if let Ok(projected) = model.project_query(&query_vec) { + let mut proj_f32: Vec = projected.iter().map(|&x| x as f32).collect(); + if proj_f32.len() < 50 { proj_f32.resize(50, 0.0); } else { proj_f32.truncate(50); } + + let vector_blob = bincode::serialize(&proj_f32).unwrap_or_default(); + sqlx::query("INSERT OR REPLACE INTO items_lsa (id, vector) VALUES (?, ?)") + .bind(id) + .bind(vector_blob) + .execute(&mut *tx) + .await + .map_err(|e| format!("Failed to update LSA vector: {}", e))?; + + sqlx::query("INSERT OR REPLACE INTO vec_items (id, embedding) VALUES (?, ?)") + .bind(id) + .bind(serde_json::to_string(&proj_f32).unwrap_or("[]".to_string())) + .execute(&mut *tx) + .await + .map_err(|e| format!("Failed to update vec_items: {}", e))?; + } } } diff --git a/src/backend/src/mcp/tools/search.rs b/src/backend/src/mcp/tools/search.rs index 8324029..f610495 100644 --- a/src/backend/src/mcp/tools/search.rs +++ b/src/backend/src/mcp/tools/search.rs @@ -13,6 +13,11 @@ args.get("content").and_then(|v| v.as_str()).unwrap_or("") }; let search_limit = args.get("limit").and_then(|v| v.as_i64()).unwrap_or(10); + // true のとき検索結果を文書単位にまとめ、同一文書のチャンクを結合して返す。未指定時は true。 + let group_by_document = args.get("group_by_document") + .or_else(|| args.get("groupByDocument")) + .and_then(|v| v.as_bool()) + .unwrap_or(true); // 足切り: この値未満の similarity は返さない。未指定時は 0.3(ノイズを落としつつ関連しそうなものを残す) let min_score = args.get("min_score") .or_else(|| args.get("minScore")) @@ -21,11 +26,19 @@ .clamp(0.0, 1.0) as f32; if search_content.is_empty() { + log::info!("[search] query=empty -> skipped"); return Some(serde_json::json!({ "content": [{ "type": "text", "text": "Empty search query provided." }] })); } + let query_preview = if search_content.len() > 60 { + format!("{}...", &search_content[..60]) + } else { + search_content.to_string() + }; + log::info!("[search] query={:?} limit={} min_score={}", query_preview, search_limit, min_score); + // 1. FTS5 (BM25) search - Elasticsearch-like statistical ranking let mut fts_results = HashMap::new(); if let Ok(rows) = sqlx::query( @@ -37,90 +50,190 @@ for row in rows { let id: i64 = row.get(0); let bm25_score: f64 = row.get(1); - // Convert BM25 score to a 0-1 similarity score (pseudo-normalization) let sim = (1.0 - (bm25_score / 10.0).tanh()).clamp(0.0, 1.0) as f32; fts_results.insert(id, sim); } } - - // 2. Vector Search (LSA/HNSW) - let mut final_results: HashMap = HashMap::new(); - let lsa_guard = state.lsa_model.read().await; - if let Some(model) = lsa_guard.as_ref() { - let mut query_counts = HashMap::new(); - let tokens = state.tokenizer.tokenize_to_vec(search_content).unwrap_or_default(); - for token in tokens { - if let Some(&tid) = model.vocabulary.get(&token) { - *query_counts.entry(tid).or_insert(0.0) += 1.0; + // FTS が 0 件のとき(1文字クエリなど trigram がヒットしにくい): LIKE でフォールバック(スコアはキーワード一致として 0.4 に統一) + if fts_results.is_empty() && !search_content.trim().is_empty() { + let like_pattern = format!("%{}%", search_content.trim()); + if let Ok(rows) = sqlx::query( + "SELECT i.id FROM items i JOIN documents d ON i.document_id = d.id WHERE i.content LIKE ? LIMIT ?" + ).bind(&like_pattern).bind(search_limit).fetch_all(&state.db_pool).await { + let like_count = rows.len(); + for row in rows { + let id: i64 = row.get(0); + fts_results.insert(id, 0.4f32); } + log::info!("[search] FTS=0, LIKE fallback hits={} (scores fixed 0.4)", like_count); } - // 該当する語句が語彙に1つもない場合、query_vec がゼロベクトルになり、 - // ゼロに近いドキュメントが distance≈0 で返って similarity=1.0 になるのを防ぐ - if query_counts.is_empty() { - // ベクトル検索はスキップ。FTS のみの結果にする(FTS もヒットしなければ空) - } else { - let mut query_vec = ndarray::Array1::zeros(model.vocabulary.len()); - for (tid, count) in query_counts { - query_vec[tid] = count; - } + } + log::info!("[search] FTS hits={}", fts_results.len()); - if let Ok(query_lsa) = model.project_query(&query_vec) { - let mut query_lsa_f32: Vec = query_lsa.iter().map(|&x| x as f32).collect(); - if query_lsa_f32.len() < 50 { query_lsa_f32.resize(50, 0.0); } else { query_lsa_f32.truncate(50); } - - // HNSW or Virtual Table search - let mut vector_hits = Vec::new(); - let hnsw_idx_guard = state.hnsw_index.read().await; - if let Some(h_ptr) = hnsw_idx_guard.as_ref() { - let neighbors = h_ptr.search(&query_lsa_f32, (search_limit * 2) as usize, 100); - for n in neighbors { - vector_hits.push((n.d_id as i64, 1.0f32 - n.distance)); + // 2. Vector Search (LSA/HNSW or Pro embedding) + let mut final_results: HashMap = HashMap::new(); + let mut vector_search_used = false; + #[cfg(feature = "pro")] + { + let guard = state.embedding_model.read().await; + if let Some(ref model) = *guard { + if let Ok(query_embedding) = model.encode(search_content) { + let mut vector_hits = Vec::new(); + let hnsw_is_some = { + let hnsw_idx_guard = state.hnsw_index.read().await; + if let Some(h_ptr) = hnsw_idx_guard.as_ref() { + let neighbors = h_ptr.search(&query_embedding, (search_limit * 2) as usize, 100); + for n in neighbors { + vector_hits.push((n.d_id as i64, 1.0f32 - n.distance)); + } + true + } else { + false + } + }; + if vector_hits.is_empty() { + if let Ok(rows) = sqlx::query( + "SELECT id, distance FROM vec_items WHERE embedding MATCH ? AND k = ?" + ) + .bind(serde_json::to_string(&query_embedding).unwrap_or("[]".to_string())) + .bind(search_limit * 2).fetch_all(&state.db_pool).await { + for r in rows { + let id: i64 = r.get(0); + let dist: f64 = r.get(1); + vector_hits.push((id, (1.0 - (dist / 2.0)) as f32)); + } + } } - } - - if vector_hits.is_empty() { - if let Ok(rows) = sqlx::query( - "SELECT id, distance FROM vec_items WHERE embedding MATCH ? AND k = ?" - ) - .bind(serde_json::to_string(&query_lsa_f32).unwrap_or("[]".to_string())) - .bind(search_limit * 2).fetch_all(&state.db_pool).await { - for r in rows { - let id: i64 = r.get(0); - let dist: f64 = r.get(1); - vector_hits.push((id, (1.0 - (dist / 2.0)) as f32)); + if vector_hits.is_empty() { + let vec_count: i64 = sqlx::query_scalar::<_, i64>("SELECT COUNT(*) FROM vec_items") + .fetch_one(&state.db_pool) + .await + .unwrap_or(0); + log::warn!( + "[search] Pro: 近似近傍が 0 件 (HNSW={} vec_items={})。RE-INDEX を実行するか、バックグラウンドで再構築を試行します。", + if hnsw_is_some { "あり" } else { "なし" }, + vec_count + ); + // HNSW が無いときは 1 回だけ遅延構築を試す(起動時バックフィルが反映されなかった場合の救済) + use std::sync::atomic::Ordering; + if !hnsw_is_some + && state + .pro_hnsw_rebuild_requested + .compare_exchange(false, true, Ordering::Relaxed, Ordering::Relaxed) + .is_ok() + { + let state_clone = state.clone(); + tokio::spawn(async move { + log::info!("[search] Pro: HNSW 遅延構築を開始します(vec_items 再読込・HNSW 構築)"); + crate::mcp::system::train_lsa_and_sync_hnsw(state_clone).await; + log::info!("[search] Pro: HNSW 遅延構築完了。次回検索から近似近傍が使われます。"); + }); + } + } else { + vector_search_used = true; + log::info!("[search] Pro vector hits={}", vector_hits.len()); + } + for (id, v_sim) in vector_hits { + let f_sim = fts_results.get(&id).cloned().unwrap_or(0.0); + let final_sim = v_sim.max(f_sim); + if let Ok(row) = sqlx::query( + "SELECT i.content, d.path, d.mime, i.document_id FROM items i JOIN documents d ON i.document_id = d.id WHERE i.id = ?" + ).bind(id).fetch_one(&state.db_pool).await { + final_results.insert(id, serde_json::json!({ + "id": id, + "document_id": row.get::(3), + "content": row.get::(0), + "path": row.get::(1), + "mime": row.get::, _>(2), + "similarity": final_sim.clamp(0.0, 1.0) + })); + } + } + } else { + log::warn!("[search] Pro: クエリの埋め込みに失敗しました"); + } + } else { + log::warn!("[search] Pro: 埋め込みモデル未ロードのため近似近傍検索をスキップしています"); + } + } + #[cfg(feature = "community")] + { + let lsa_guard = state.lsa_model.read().await; + if let Some(model) = lsa_guard.as_ref() { + let mut query_counts = HashMap::new(); + let tokens = state.tokenizer.tokenize_to_vec(search_content).unwrap_or_default(); + for token in tokens { + if let Some(&tid) = model.vocabulary.get(&token) { + *query_counts.entry(tid).or_insert(0.0) += 1.0; + } + } + if !query_counts.is_empty() { + let mut query_vec = ndarray::Array1::zeros(model.vocabulary.len()); + for (tid, count) in query_counts { + query_vec[tid] = count; + } + + if let Ok(query_lsa) = model.project_query(&query_vec) { + let mut query_lsa_f32: Vec = query_lsa.iter().map(|&x| x as f32).collect(); + if query_lsa_f32.len() < 50 { query_lsa_f32.resize(50, 0.0); } else { query_lsa_f32.truncate(50); } + + let mut vector_hits = Vec::new(); + let hnsw_idx_guard = state.hnsw_index.read().await; + if let Some(h_ptr) = hnsw_idx_guard.as_ref() { + let neighbors = h_ptr.search(&query_lsa_f32, (search_limit * 2) as usize, 100); + for n in neighbors { + vector_hits.push((n.d_id as i64, 1.0f32 - n.distance)); + } + } + + if vector_hits.is_empty() { + if let Ok(rows) = sqlx::query( + "SELECT id, distance FROM vec_items WHERE embedding MATCH ? AND k = ?" + ) + .bind(serde_json::to_string(&query_lsa_f32).unwrap_or("[]".to_string())) + .bind(search_limit * 2).fetch_all(&state.db_pool).await { + for r in rows { + let id: i64 = r.get(0); + let dist: f64 = r.get(1); + vector_hits.push((id, (1.0 - (dist / 2.0)) as f32)); + } + } + } + + if !vector_hits.is_empty() { + vector_search_used = true; + } + log::info!("[search] Community LSA vector hits={}", vector_hits.len()); + for (id, v_sim) in vector_hits { + let f_sim = fts_results.get(&id).cloned().unwrap_or(0.0); + let final_sim = v_sim.max(f_sim); + if let Ok(row) = sqlx::query( + "SELECT i.content, d.path, d.mime, i.document_id FROM items i JOIN documents d ON i.document_id = d.id WHERE i.id = ?" + ).bind(id).fetch_one(&state.db_pool).await { + final_results.insert(id, serde_json::json!({ + "id": id, + "document_id": row.get::(3), + "content": row.get::(0), + "path": row.get::(1), + "mime": row.get::, _>(2), + "similarity": final_sim.clamp(0.0, 1.0) + })); + } } } } - - // 3. Merge Vector and FTS results - for (id, v_sim) in vector_hits { - let f_sim = fts_results.get(&id).cloned().unwrap_or(0.0); - let final_sim = v_sim.max(f_sim); - - if let Ok(row) = sqlx::query( - "SELECT i.content, d.path, d.mime FROM items i JOIN documents d ON i.document_id = d.id WHERE i.id = ?" - ).bind(id).fetch_one(&state.db_pool).await { - final_results.insert(id, serde_json::json!({ - "id": id, - "content": row.get::(0), - "path": row.get::(1), - "mime": row.get::, _>(2), - "similarity": final_sim.clamp(0.0, 1.0) - })); - } - } - } } } - // 4. Add remaining FTS results not found by vector search + // 3. Add FTS results (and for Community, remaining FTS not in vector results) for (id, f_sim) in fts_results { if !final_results.contains_key(&id) { if let Ok(row) = sqlx::query( - "SELECT i.content, d.path, d.mime FROM items i JOIN documents d ON i.document_id = d.id WHERE i.id = ?" + "SELECT i.content, d.path, d.mime, i.document_id FROM items i JOIN documents d ON i.document_id = d.id WHERE i.id = ?" ).bind(id).fetch_one(&state.db_pool).await { final_results.insert(id, serde_json::json!({ "id": id, + "document_id": row.get::(3), "content": row.get::(0), "path": row.get::(1), "mime": row.get::, _>(2), @@ -137,12 +250,62 @@ .unwrap_or(std::cmp::Ordering::Equal) }); - let final_items = sorted + let filtered: Vec<_> = sorted .into_iter() .filter(|v| v.get("similarity").and_then(|x| x.as_f64()).unwrap_or(0.0) >= min_score as f64) - .take(search_limit as usize) - .collect::>(); - let result_text = serde_json::to_string_pretty(&final_items).unwrap_or_else(|_| "[]".to_string()); + .take((search_limit * 3) as usize) // 文書結合時はチャンク多めに取得 + .collect(); + + let final_items: Vec = if group_by_document && !filtered.is_empty() { + // 文書単位にまとめる: document_id ごとにチャンクを結合 + use std::collections::BTreeMap; + let mut by_doc: BTreeMap, Option)> = BTreeMap::new(); + for it in &filtered { + let doc_id = it.get("document_id").and_then(|v| v.as_i64()).unwrap_or(0); + let sim = it.get("similarity").and_then(|v| v.as_f64()).unwrap_or(0.0); + let path = it.get("path").and_then(|v| v.as_str()).map(String::from); + let mime = it.get("mime").and_then(|v| v.as_str()).map(String::from); + let entry = by_doc.entry(doc_id).or_insert((0.0f64, path, mime)); + if sim > entry.0 { + entry.0 = sim; + } + } + let mut doc_list: Vec<(i64, f64, Option, Option)> = by_doc + .into_iter() + .map(|(doc_id, (sim, path, mime))| (doc_id, sim, path, mime)) + .collect(); + doc_list.sort_by(|a, b| b.1.partial_cmp(&a.1).unwrap_or(std::cmp::Ordering::Equal)); + let mut out = Vec::with_capacity(doc_list.len().min(search_limit as usize)); + for (doc_id, best_sim, path, mime) in doc_list.into_iter().take(search_limit as usize) { + let chunks: Vec = sqlx::query_scalar::<_, String>( + "SELECT content FROM items WHERE document_id = ? ORDER BY chunk_index" + ) + .bind(doc_id) + .fetch_all(&state.db_pool) + .await + .unwrap_or_default(); + let content = chunks.join("\n\n"); + out.push(serde_json::json!({ + "id": doc_id, + "document_id": doc_id, + "path": path, + "mime": mime, + "content": content, + "similarity": best_sim + })); + } + out + } else { + filtered.into_iter().take(search_limit as usize).collect() + }; + let result_count = final_items.len(); + log::info!("[search] result_count={} ok={} vector_search_used={}", result_count, result_count > 0, vector_search_used); + + let response_payload = serde_json::json!({ + "items": final_items, + "vector_search_used": vector_search_used + }); + let result_text = serde_json::to_string_pretty(&response_payload).unwrap_or_else(|_| "{}".to_string()); Some(serde_json::json!({ "content": [{ "type": "text", "text": result_text }] diff --git a/src/backend/src/mcp/tools/system.rs b/src/backend/src/mcp/tools/system.rs index e7573be..ddb27bd 100644 --- a/src/backend/src/mcp/tools/system.rs +++ b/src/backend/src/mcp/tools/system.rs @@ -1,8 +1,170 @@ -use sqlx::Row; -use hnsw_rs::prelude::*; use crate::mcp::types::AppState; + +#[cfg(feature = "pro")] +pub async fn handle_lsa_retrain(state: &AppState) -> Option { + use hnsw_rs::prelude::*; + use sqlx::Row; + + { + let guard = state.embedding_model.read().await; + if guard.is_none() { + return Some(serde_json::json!({ + "content": [{ "type": "text", "text": "Pro: 埋め込みモデルが読み込まれていません。RE-INDEX を実行できません。" }], + "isError": true + })); + } + } + + let state_clone = state.clone(); + tokio::spawn(async move { + let dim = crate::utils::embedding_pro::ProEmbeddingModel::embed_dim(); + + { + let mut st = state_clone.indexing_status.write().await; + *st = "training".to_string(); + } + let _ = state_clone.tx.send("indexing:training".to_string()); + + let rows = match sqlx::query("SELECT id, content FROM items WHERE content IS NOT NULL AND trim(content) != ''") + .fetch_all(&state_clone.db_pool) + .await + { + Ok(r) if !r.is_empty() => r, + Ok(_) => { + log::info!("Pro RE-INDEX: no documents, skip."); + *state_clone.indexing_status.write().await = "idle".to_string(); + let _ = state_clone.tx.send("indexing:idle".to_string()); + return; + } + Err(e) => { + log::error!("Pro RE-INDEX: failed to fetch items: {}", e); + *state_clone.indexing_status.write().await = "idle".to_string(); + let _ = state_clone.tx.send("indexing:idle".to_string()); + return; + } + }; + + log::info!("Pro RE-INDEX: embedding {} items...", rows.len()); + + let mut data_to_insert: Vec<(Vec, usize)> = Vec::new(); + let mut tx = match state_clone.db_pool.begin().await { + Ok(t) => t, + Err(e) => { + log::error!("Pro RE-INDEX: begin tx failed: {}", e); + *state_clone.indexing_status.write().await = "idle".to_string(); + let _ = state_clone.tx.send("indexing:idle".to_string()); + return; + } + }; + + sqlx::query("DELETE FROM vec_items").execute(&mut *tx).await.ok(); + sqlx::query("DELETE FROM items_fts").execute(&mut *tx).await.ok(); + + let mut vec_insert_ok = 0usize; + let mut vec_insert_err = 0usize; + { + let model_guard = state_clone.embedding_model.read().await; + let model = match model_guard.as_ref() { + Some(m) => m, + None => { + log::error!("Pro RE-INDEX: embedding model not loaded."); + *state_clone.indexing_status.write().await = "idle".to_string(); + let _ = state_clone.tx.send("indexing:idle".to_string()); + return; + } + }; + for row in &rows { + let id: i64 = row.get(0); + let content: String = row.get(1); + if let Ok(embedding) = model.encode(&content) { + if embedding.len() == dim { + data_to_insert.push((embedding.clone(), id as usize)); + } + let emb_json = serde_json::to_string(&embedding).unwrap_or_else(|_| "[]".to_string()); + match sqlx::query("INSERT INTO vec_items (id, embedding) VALUES (?, ?)") + .bind(id) + .bind(&emb_json) + .execute(&mut *tx) + .await + { + Ok(r) => { vec_insert_ok += r.rows_affected() as usize; } + Err(e) => { + vec_insert_err += 1; + log::warn!("Pro RE-INDEX: insert vec_items id={} failed: {}", id, e); + } + } + let _ = sqlx::query("INSERT INTO items_fts (rowid, content) VALUES (?, ?)") + .bind(id) + .bind(content) + .execute(&mut *tx) + .await; + } + } + } + // 診断: トランザクション内での vec_items 件数 + let count_in_tx: Result = sqlx::query_scalar("SELECT COUNT(*) FROM vec_items") + .fetch_one(&mut *tx) + .await; + log::info!("Pro RE-INDEX vec_items: insert_ok={} insert_err={} COUNT(*) within tx={:?}", vec_insert_ok, vec_insert_err, count_in_tx); + + if let Err(e) = tx.commit().await { + log::error!("Pro RE-INDEX: commit failed: {}", e); + *state_clone.indexing_status.write().await = "idle".to_string(); + let _ = state_clone.tx.send("indexing:idle".to_string()); + return; + } + // 診断: COMMIT 後の vec_items 件数(永続化の有無) + if let Ok((c,)) = sqlx::query_as::<_, (i64,)>("SELECT COUNT(*) FROM vec_items") + .fetch_one(&state_clone.db_pool) + .await + { + log::info!("Pro RE-INDEX vec_items COUNT(*) after commit (pool): {}", c); + } + + { + let mut st = state_clone.indexing_status.write().await; + *st = "syncing".to_string(); + } + let _ = state_clone.tx.send("indexing:syncing".to_string()); + + if data_to_insert.is_empty() { + log::info!("Pro RE-INDEX: no vectors encoded."); + *state_clone.hnsw_index.write().await = None; + } else { + log::info!("Pro RE-INDEX: building HNSW for {} vectors (in-memory)...", data_to_insert.len()); + let hnsw = tokio::task::spawn_blocking(move || { + let hnsw: Hnsw<'static, f32, DistCosine> = + Hnsw::new(16, data_to_insert.len().max(100), 16, 200, DistCosine {}); + let refs: Vec<(&Vec, usize)> = data_to_insert.iter().map(|(v, id)| (v, *id)).collect(); + hnsw.parallel_insert(&refs); + hnsw + }) + .await + .ok(); + if let Some(hnsw) = hnsw { + *state_clone.hnsw_index.write().await = Some(hnsw); + log::info!("Pro RE-INDEX: HNSW built."); + } + } + + *state_clone.indexing_status.write().await = "idle".to_string(); + let _ = state_clone.tx.send("indexing:idle".to_string()); + log::info!("Pro RE-INDEX: completed."); + }); + + Some(serde_json::json!({ + "content": [{ "type": "text", "text": "Pro: 埋め込みモデルで全件再計算を開始しました。完了までしばらくお待ちください。" }] + })) +} + +#[cfg(feature = "community")] +use sqlx::Row; +#[cfg(feature = "community")] +use hnsw_rs::prelude::*; +#[cfg(feature = "community")] use crate::mcp::system::sync_all_vectors; +#[cfg(feature = "community")] pub async fn handle_lsa_retrain( state: &AppState, ) -> Option { diff --git a/src/backend/src/mcp/types.rs b/src/backend/src/mcp/types.rs index 6819f7c..94bacd5 100644 --- a/src/backend/src/mcp/types.rs +++ b/src/backend/src/mcp/types.rs @@ -1,4 +1,5 @@ use serde::{Deserialize, Serialize}; +#[cfg(feature = "community")] use crate::utils::lsa::LsaModel; use crate::utils::tokenizer::JapaneseTokenizer; use hnsw_rs::prelude::*; @@ -13,13 +14,20 @@ pub app_data_dir: PathBuf, pub db_pool: sqlx::SqlitePool, pub tx: broadcast::Sender, - pub llama_status: Arc>, pub model_name: String, + /// エディション: "community" | "pro"(GUI 表示の切り替え用) + pub edition: String, // MCP sessions map pub sessions: Arc>>>, - // Japanese NLP & LSA + // Japanese NLP & LSA (Community 版) pub tokenizer: Arc, + #[cfg(feature = "community")] pub lsa_model: Arc>>, + #[cfg(feature = "pro")] + pub embedding_model: Arc>>, + /// Pro で HNSW が空のときに検索から 1 回だけ遅延構築を試みたか + #[cfg(feature = "pro")] + pub pro_hnsw_rebuild_requested: Arc, pub hnsw_index: Arc>>>, /// 前回の LSA 再学習以降の変更件数(追加・更新・削除)。この値が閾値を超えたら自動で再学習をスケジュールする。 pub changes_since_train: Arc, diff --git a/src/backend/src/utils/embedding_pro.rs b/src/backend/src/utils/embedding_pro.rs new file mode 100644 index 0000000..e10880f --- /dev/null +++ b/src/backend/src/utils/embedding_pro.rs @@ -0,0 +1,362 @@ +//! Pro 版: ONNX 埋め込み。tract を優先し、失敗時は ort (ONNX Runtime) にフォールバック。 +//! sonoisa sentence-bert-base-ja-mean-tokens-v2(model.onnx または model_quantized.onnx)。 + +use std::collections::HashMap; +use std::path::Path; +use std::sync::Mutex; + +#[cfg(feature = "pro")] +use ort::session::Session; +#[cfg(feature = "pro")] +use ort::value::Value; + +const MAX_LEN: usize = 512; +const EMBED_DIM: usize = 768; + +/// 語彙(vocab.txt)を読み込み、トークン → ID のマップを返す。 +fn load_vocab(vocab_path: &Path) -> Result, Box> { + let s = std::fs::read_to_string(vocab_path)?; + let mut map = HashMap::new(); + for (id, line) in s.lines().enumerate() { + let token = line.trim().to_string(); + if !token.is_empty() { + map.insert(token, id as i64); + } + } + Ok(map) +} + +/// テキストを BERT 風にトークナイズ(最長一致 + [CLS]/[SEP])。語彙に無い文字は [UNK]。 +fn tokenize(vocab: &HashMap, text: &str) -> (Vec, Vec) { + let pad_id = *vocab.get("[PAD]").unwrap_or(&0); + let unk_id = *vocab.get("[UNK]").unwrap_or(&1); + let cls_id = *vocab.get("[CLS]").unwrap_or(&2); + let sep_id = *vocab.get("[SEP]").unwrap_or(&3); + + let mut input_ids = vec![cls_id]; + let mut attention_mask = vec![1i64]; + + let chars: Vec = text.chars().collect(); + let mut i = 0; + while i < chars.len() && input_ids.len() < MAX_LEN - 1 { + let mut found = false; + for len in (1..=chars.len().saturating_sub(i).min(20)).rev() { + let sub: String = chars[i..i + len].iter().collect(); + if let Some(&id) = vocab.get(&sub) { + input_ids.push(id); + attention_mask.push(1); + i += len; + found = true; + break; + } + } + if !found { + let c = chars[i].to_string(); + let id = vocab.get(&c).copied().unwrap_or(unk_id); + input_ids.push(id); + attention_mask.push(1); + i += 1; + } + } + + input_ids.push(sep_id); + attention_mask.push(1); + + let seq_len = input_ids.len(); + if seq_len < MAX_LEN { + let pad_len = MAX_LEN - seq_len; + for _ in 0..pad_len { + input_ids.push(pad_id); + attention_mask.push(0); + } + } else { + input_ids.truncate(MAX_LEN); + attention_mask.truncate(MAX_LEN); + input_ids[MAX_LEN - 1] = sep_id; + attention_mask[MAX_LEN - 1] = 1; + } + + (input_ids, attention_mask) +} + +/// 平均プーリング: shape [1, seq, 768] の last_hidden と attention_mask から 768 次元ベクトルを返す。 +fn mean_pool(last_hidden: &[f32], attention_mask: &[i64], seq_len: usize) -> Result, Box> { + if last_hidden.len() < seq_len * EMBED_DIM { + return Err(format!("last_hidden len {} < {}*{}", last_hidden.len(), seq_len, EMBED_DIM).into()); + } + let mut out = vec![0f32; EMBED_DIM]; + let mut mask_count = 0f32; + for s in 0..seq_len { + let mask = if s < attention_mask.len() && attention_mask[s] != 0 { + 1.0 + } else { + 0.0 + }; + mask_count += mask; + for d in 0..EMBED_DIM { + out[d] += last_hidden[s * EMBED_DIM + d] * mask; + } + } + if mask_count > 0.0 { + for x in out.iter_mut() { + *x /= mask_count; + } + } + Ok(out) +} + +/// バックエンド: tract または ort。 +pub enum ProEmbeddingBackend { + Tract( + tract_onnx::prelude::SimplePlan< + tract_onnx::prelude::TypedFact, + Box, + tract_onnx::prelude::Graph>, + >, + ), + Ort(Mutex), +} + +/// Pro 版埋め込みモデル。ONNX 推論 + 平均プーリングで 768 次元ベクトルを返す。 +pub struct ProEmbeddingModel { + backend: ProEmbeddingBackend, + vocab: HashMap, +} + +impl ProEmbeddingModel { + /// 指定ディレクトリから ONNX モデルと vocab.txt を読み込む。 + /// tract でロードを試み、失敗時は ort (ONNX Runtime) にフォールバックする。 + /// model.onnx(非量子化)を優先し、無ければ model_quantized.onnx を使用する。 + pub fn load(model_dir: &Path) -> Result> { + let vocab_path = model_dir.join("vocab.txt"); + if !vocab_path.exists() { + return Err(format!("vocab not found: {}", vocab_path.display()).into()); + } + + let model_fp32 = model_dir.join("model.onnx"); + let model_quant = model_dir.join("model_quantized.onnx"); + let onnx_path = if model_fp32.exists() { + model_fp32 + } else if model_quant.exists() { + model_quant + } else { + return Err(format!( + "ONNX not found: need model.onnx or model_quantized.onnx in {}", + model_dir.display() + ) + .into()); + }; + + let vocab = load_vocab(&vocab_path)?; + + if let Ok(backend) = Self::load_tract(&onnx_path) { + log::info!("[embedding] Loaded with tract"); + return Ok(ProEmbeddingModel { backend, vocab }); + } + + if let Ok(session) = Self::load_ort(&onnx_path) { + log::info!("[embedding] Loaded with ONNX Runtime (ort)"); + return Ok(ProEmbeddingModel { + backend: ProEmbeddingBackend::Ort(Mutex::new(session)), + vocab, + }); + } + + Err("embedding model: tract and ort both failed".into()) + } + + fn load_tract(onnx_path: &Path) -> Result> { + use tract_onnx::prelude::*; + + let skip_optimize = std::env::var("TELOS_EMBEDDING_NO_OPTIMIZE").as_deref() == Ok("1"); + + let prepared = tract_onnx::onnx() + .model_for_path(onnx_path) + .map_err(|e| format!("ONNX load: {}", e))? + .with_input_fact( + 0, + InferenceFact::dt_shape(i64::datum_type(), tvec!(1, MAX_LEN as i64)), + ) + .map_err(|e| format!("input fact 0: {}", e))? + .with_input_fact( + 1, + InferenceFact::dt_shape(i64::datum_type(), tvec!(1, MAX_LEN as i64)), + ) + .map_err(|e| format!("input fact 1: {}", e))?; + + let model = if skip_optimize { + log::info!("[embedding] Loading with tract (no optimize)"); + prepared + .into_typed() + .map_err(|e| format!("into_typed: {}", e))? + .into_runnable() + .map_err(|e| format!("runnable: {}", e))? + } else { + match prepared + .clone() + .into_optimized() + .map_err(|e| format!("optimize: {}", e)) + .and_then(|m| m.into_runnable().map_err(|e| format!("runnable: {}", e))) + { + Ok(r) => r, + Err(e) => { + let err_str = e.to_string(); + if err_str.contains("Cast") || err_str.contains("optim") || err_str.contains("optimize") { + log::warn!("[embedding] tract optimize failed ({}), trying without optimization", e); + prepared + .into_typed() + .map_err(|e2| format!("into_typed (fallback): {}", e2))? + .into_runnable() + .map_err(|e2| format!("runnable (fallback): {}", e2))? + } else { + return Err(e.into()); + } + } + } + }; + + Ok(ProEmbeddingBackend::Tract(model)) + } + + fn load_ort(onnx_path: &Path) -> Result> { + let session = Session::builder()? + .commit_from_file(onnx_path) + .map_err(|e| format!("ort commit_from_file: {}", e))?; + Ok(session) + } + + /// 1 文を 768 次元ベクトルにエンコードする。平均プーリングを適用。 + pub fn encode(&self, text: &str) -> Result, Box> { + let (input_ids, attention_mask) = tokenize(&self.vocab, text); + + match &self.backend { + ProEmbeddingBackend::Tract(model) => { + use tract_onnx::prelude::*; + let input_ids_arr = tract_ndarray::Array2::from_shape_vec((1, MAX_LEN), input_ids) + .map_err(|e| format!("input_ids shape: {}", e))?; + let attention_mask_arr = tract_ndarray::Array2::from_shape_vec((1, MAX_LEN), attention_mask.clone()) + .map_err(|e| format!("attention_mask shape: {}", e))?; + let input_ids_tensor = tract_onnx::prelude::Tensor::from(input_ids_arr); + let attention_mask_tensor = tract_onnx::prelude::Tensor::from(attention_mask_arr); + + let outputs = model.run(tvec!( + input_ids_tensor.into(), + attention_mask_tensor.into() + ))?; + + let last_hidden: &tract_onnx::prelude::Tensor = outputs.first().ok_or("no output")?; + let view = last_hidden.to_array_view::()?; + let shape = view.shape(); + if shape.len() != 3 { + return Err(format!("expected [1, seq, 768], got {:?}", shape).into()); + } + let seq_len = shape[1]; + let dim = shape[2]; + if dim != EMBED_DIM { + return Err(format!("expected dim 768, got {}", dim).into()); + } + + let mut out = vec![0f32; EMBED_DIM]; + let mut mask_count = 0f32; + for s in 0..seq_len { + let mask = if s < attention_mask.len() && attention_mask[s] != 0 { + 1.0 + } else { + 0.0 + }; + mask_count += mask; + for d in 0..dim { + out[d] += view[[0, s, d]] * mask; + } + } + if mask_count > 0.0 { + for x in out.iter_mut() { + *x /= mask_count; + } + } + Ok(out) + } + ProEmbeddingBackend::Ort(session_mux) => { + let mut session = session_mux.lock().map_err(|e| format!("ort session lock: {}", e))?; + let input_ids_val = Value::from_array(([1_i64, MAX_LEN as i64], input_ids))?; + let attention_mask_val = Value::from_array(([1_i64, MAX_LEN as i64], attention_mask.clone()))?; + let token_type_ids: Vec = std::iter::repeat(0).take(MAX_LEN).collect(); + let token_type_ids_val = Value::from_array(([1_i64, MAX_LEN as i64], token_type_ids))?; + + let outputs = session.run(ort::inputs![input_ids_val, attention_mask_val, token_type_ids_val])?; + let (shape, flat) = outputs[0] + .try_extract_tensor::() + .map_err(|e| format!("ort extract last_hidden: {}", e))?; + // モデルによっては [1, seq, 768] ではなく既にプール済みの [1, 768] を返す + if shape.len() == 2 && shape[0] == 1 && shape[1] == EMBED_DIM as i64 { + return Ok(flat.to_vec()); + } + if shape.len() != 3 { + return Err(format!("ort expected [1, seq, 768] or [1, 768], got {:?}", shape).into()); + } + let seq_len = shape[1] as usize; + let dim = shape[2] as usize; + if dim != EMBED_DIM { + return Err(format!("ort expected dim 768, got {}", dim).into()); + } + + mean_pool(flat, &attention_mask, seq_len) + } + } + } + + pub fn embed_dim() -> usize { + EMBED_DIM + } +} + +#[cfg(all(test, feature = "pro"))] +mod tests { + use super::*; + + #[test] + fn embed_dim_is_768() { + assert_eq!(ProEmbeddingModel::embed_dim(), 768, "sentence-bert 系は 768 次元"); + } + + /// モデルが TELOS_EMBEDDING_MODEL_DIR または embedding_model/ に存在する場合のみ実行。 + /// ベクトル化が正しく動くこと(768 次元・非ゼロ)を検証する。 + #[test] + fn encode_returns_768_dim_when_model_loaded() { + let model_dir = std::env::var_os("TELOS_EMBEDDING_MODEL_DIR") + .map(std::path::PathBuf::from) + .or_else(|| { + let manifest = std::env::var_os("CARGO_MANIFEST_DIR").map(std::path::PathBuf::from)?; + let root = manifest.parent()?.parent()?; + let p = root.join("embedding_model"); + if p.join("model_quantized.onnx").exists() || p.join("model.onnx").exists() { + Some(p) + } else { + None + } + }); + let Some(dir) = model_dir else { + eprintln!("encode test skipped: no embedding_model (set TELOS_EMBEDDING_MODEL_DIR or place embedding_model/)"); + return; + }; + let model = match ProEmbeddingModel::load(&dir) { + Ok(m) => m, + Err(e) => { + eprintln!("encode test skipped: model load failed: {}", e); + return; + } + }; + let vec = match model.encode("テスト文です") { + Ok(v) => v, + Err(e) => { + eprintln!("encode test skipped: encode failed (model shape may differ): {}", e); + return; + } + }; + assert_eq!(vec.len(), 768, "encode は 768 次元を返すこと"); + assert!( + vec.iter().any(|&x| x != 0.0), + "ベクトルが全て 0 だとベクトル化が効いていない" + ); + } +} diff --git a/src/backend/src/utils/lsa.rs b/src/backend/src/utils/lsa.rs index fc467fa..aa47298 100644 --- a/src/backend/src/utils/lsa.rs +++ b/src/backend/src/utils/lsa.rs @@ -1,3 +1,6 @@ +//! Community 版: LSA。このモジュールは feature "community" のときのみコンパイルされる。 +#![cfg(feature = "community")] + use ndarray::{Array1, Array2, Axis}; use std::collections::HashMap; use anyhow::{Result, anyhow}; diff --git a/src/backend/src/utils/mod.rs b/src/backend/src/utils/mod.rs index d21bb8a..5d123cd 100644 --- a/src/backend/src/utils/mod.rs +++ b/src/backend/src/utils/mod.rs @@ -1,2 +1,5 @@ pub mod tokenizer; +#[cfg(feature = "community")] pub mod lsa; +#[cfg(feature = "pro")] +pub mod embedding_pro; diff --git a/src/backend/tauri.community.conf.json b/src/backend/tauri.community.conf.json new file mode 100644 index 0000000..12de0d0 --- /dev/null +++ b/src/backend/tauri.community.conf.json @@ -0,0 +1,8 @@ +{ + "productName": "TelosDB-Community", + "bundle": { + "resources": { + "../../node_modules/sqlite-vec-windows-x64/vec0.dll": "vec0.dll" + } + } +} diff --git a/src/backend/tauri.conf.json b/src/backend/tauri.conf.json index 8c80699..64cb476 100644 --- a/src/backend/tauri.conf.json +++ b/src/backend/tauri.conf.json @@ -6,7 +6,7 @@ "build": { "frontendDist": "../frontend", "devUrl": "http://127.0.0.1:8474", - "beforeDevCommand": "node tools/serve-frontend.mjs" + "beforeDevCommand": "" }, "app": { "windows": [{ "title": "TelosDB", "width": 800, "height": 600, "resizable": true, "fullscreen": false }], @@ -19,7 +19,9 @@ "icon": ["icons/32x32.png", "icons/128x128.png", "icons/128x128@2x.png", "icons/icon.icns", "icons/icon.ico"], "externalBin": [], "resources": { - "../../node_modules/sqlite-vec-windows-x64/vec0.dll": "vec0.dll" + "../../node_modules/sqlite-vec-windows-x64/vec0.dll": "vec0.dll", + "../../embedding_model/model_quantized.onnx": "embedding_model/model_quantized.onnx", + "../../embedding_model/vocab.txt": "embedding_model/vocab.txt" } } } diff --git a/src/backend/tauri.pro.conf.json b/src/backend/tauri.pro.conf.json new file mode 100644 index 0000000..1d2604d --- /dev/null +++ b/src/backend/tauri.pro.conf.json @@ -0,0 +1,10 @@ +{ + "productName": "TelosDB-Pro", + "bundle": { + "resources": { + "../../node_modules/sqlite-vec-windows-x64/vec0.dll": "vec0.dll", + "../../embedding_model/model_quantized.onnx": "embedding_model/model_quantized.onnx", + "../../embedding_model/vocab.txt": "embedding_model/vocab.txt" + } + } +} diff --git a/src/backend/tests/search_api.rs b/src/backend/tests/search_api.rs index ebb71c4..166496d 100644 --- a/src/backend/tests/search_api.rs +++ b/src/backend/tests/search_api.rs @@ -1,10 +1,10 @@ -// MCP search_text APIのテスト +// MCP search_text API のテスト(MCP が 127.0.0.1:3001 で起動している前提。通常の cargo test ではスキップ) use reqwest::Client; use serde_json::json; #[tokio::test] +#[ignore = "requires MCP server on 127.0.0.1:3001; run with: cargo test --test search_api -- --ignored"] async fn test_search_text_returns_results() { - // 前提: サーバーが127.0.0.1:3001で起動していること let client = Client::new(); let req = json!({ "jsonrpc": "2.0", @@ -22,6 +22,25 @@ let body: serde_json::Value = resp.json().await.expect("JSONデコード失敗"); assert_eq!(body["jsonrpc"], "2.0"); assert!(body["result"].get("content").is_some()); - // 結果が配列で返ること assert!(body["result"]["content"].is_array()); + + let text = body["result"]["content"][0]["text"].as_str().unwrap_or(""); + let parsed: serde_json::Value = serde_json::from_str(text).unwrap_or(json!(null)); + // 新形式: { items, vector_search_used } であること + if let Some(obj) = parsed.as_object() { + assert!(obj.contains_key("items"), "search レスポンスに items があること"); + assert!( + obj.contains_key("vector_search_used"), + "search レスポンスに vector_search_used があること(ベクトル検索の有無を判定するため)" + ); + let items = &obj["items"]; + assert!(items.is_array(), "items は配列"); + if let Some(arr) = items.as_array() { + for item in arr.iter().take(3) { + if let Some(sim) = item.get("similarity").and_then(|v| v.as_f64()) { + assert!((0.0..=1.0).contains(&sim), "similarity は 0〜1: {}", sim); + } + } + } + } } diff --git a/src/frontend/components/main-panel.js b/src/frontend/components/main-panel.js index 1106b8e..7bc265f 100644 --- a/src/frontend/components/main-panel.js +++ b/src/frontend/components/main-panel.js @@ -21,6 +21,9 @@