diff --git a/.github/workflows/checks.yml b/.github/workflows/checks.yml index 5084d2011..201814f9d 100644 --- a/.github/workflows/checks.yml +++ b/.github/workflows/checks.yml @@ -12,7 +12,7 @@ jobs: steps: - name: Check if PRs have conflicts uses: eps1lon/actions-label-merge-conflict@v3 - if: github.repository == ${{ vars.REPO_MAIN }} + if: github.repository == ${{ vars.REPO_MAIN }} && ${{secrets.MERGE_CONFLICT_LABEL_PAT}} with: dirtyLabel: "merge-conflicts" repoToken: "${{ secrets.MERGE_CONFLICT_LABEL_PAT }}" diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 876273d64..c523c2f1d 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -67,7 +67,7 @@ jobs: # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init@v3 + uses: github/codeql-action/init@v4 with: languages: ${{ matrix.language }} build-mode: ${{ matrix.build-mode }} @@ -95,6 +95,6 @@ jobs: exit 1 - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v3 + uses: github/codeql-action/analyze@v4 with: category: "/language:${{matrix.language}}" diff --git a/.github/workflows/deploy-docs.yml b/.github/workflows/deploy-docs.yml index 6abd9af47..6a1376a61 100644 --- a/.github/workflows/deploy-docs.yml +++ b/.github/workflows/deploy-docs.yml @@ -55,7 +55,7 @@ jobs: - name: Setup Python uses: actions/setup-python@v6 with: - python-version: '3.13' + python-version: '3.14' cache: 'pip' cache-dependency-path: 'requirements-docs.txt' @@ -118,6 +118,7 @@ jobs: - name: Deploy uses: ./.github/actions/deploy-to-cloudflare-pages + if: github.repository == ${{ vars.REPO_MAIN }} && ${{secrets.CLOUDFLARE_API_TOKEN}} with: project_name: "trilium-docs" comment_body: "📚 Documentation preview is ready" diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml index 15dd91acb..50c97417c 100644 --- a/.github/workflows/nightly.yml +++ b/.github/workflows/nightly.yml @@ -77,7 +77,7 @@ jobs: GPG_SIGNING_KEY: ${{ secrets.GPG_SIGN_KEY }} - name: Publish release - uses: softprops/action-gh-release@v2.3.4 + uses: softprops/action-gh-release@v2.4.0 if: ${{ github.event_name != 'pull_request' }} with: make_latest: false @@ -118,7 +118,7 @@ jobs: arch: ${{ matrix.arch }} - name: Publish release - uses: softprops/action-gh-release@v2.3.4 + uses: softprops/action-gh-release@v2.4.0 if: ${{ github.event_name != 'pull_request' }} with: make_latest: false diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index f73cdb54c..f8e77159a 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -127,7 +127,7 @@ jobs: path: upload - name: Publish stable release - uses: softprops/action-gh-release@v2.3.4 + uses: softprops/action-gh-release@v2.4.0 with: draft: false body_path: docs/Release Notes/Release Notes/${{ github.ref_name }}.md diff --git a/_regroup/package.json b/_regroup/package.json index 8ad1dd51f..443e36ad5 100644 --- a/_regroup/package.json +++ b/_regroup/package.json @@ -44,7 +44,7 @@ "eslint": "9.37.0", "eslint-plugin-simple-import-sort": "12.1.1", "esm": "3.2.25", - "jsdoc": "4.0.4", + "jsdoc": "4.0.5", "lorem-ipsum": "2.0.8", "rcedit": "4.0.1", "rimraf": "6.0.1", diff --git a/apps/client/package.json b/apps/client/package.json index 0abafc87a..65e93030b 100644 --- a/apps/client/package.json +++ b/apps/client/package.json @@ -51,9 +51,9 @@ "leaflet": "1.9.4", "leaflet-gpx": "2.2.0", "mark.js": "8.11.1", - "marked": "16.3.0", + "marked": "16.4.0", "mermaid": "11.12.0", - "mind-elixir": "5.1.1", + "mind-elixir": "5.3.2", "normalize.css": "8.0.1", "panzoom": "9.4.3", "preact": "10.27.2", diff --git a/apps/client/src/translations/ar/translation.json b/apps/client/src/translations/ar/translation.json index 647fd6697..2d9b73f97 100644 --- a/apps/client/src/translations/ar/translation.json +++ b/apps/client/src/translations/ar/translation.json @@ -6,7 +6,8 @@ "sync_version": "اصدار المزامنه:", "build_date": "تاريخ الانشاء:", "build_revision": "مراجعة الاصدار:", - "data_directory": "مجلد البيانات:" + "data_directory": "مجلد البيانات:", + "db_version": "اصدار قاعدة البيانات:" }, "toast": { "critical-error": { @@ -26,6 +27,369 @@ "save": "حفظ" }, "bulk_actions": { - "bulk_actions": "اجراءات جماعية" + "bulk_actions": "اجراءات جماعية", + "available_actions": "الاجراءات المتاحة", + "chosen_actions": "الأجراءات المختارة", + "execute_bulk_actions": "تنفيذ الأجراءات الجماعية", + "bulk_actions_executed": "تم تنفيذ الاجراءات الجماعية بنجاح،", + "none_yet": "لايوجد أجراء بعد... اضف اجراء بالنقر على احد الأجراءات المتاحة اعلاه.", + "relations": "العلاقات", + "notes": "الملاحظات", + "other": "أخرى", + "affected_notes": "الملاحظات المتأثرة" + }, + "upload_attachments": { + "options": "خيارات", + "upload": "تحميل" + }, + "attribute_detail": { + "name": "الاسم", + "value": "قيمة", + "promoted": "تمت ترقيته", + "promoted_alias": "اسم مستعار", + "label_type": "نوع", + "text": "نص", + "date": "تاريخ", + "time": "وقت", + "precision": "دفة", + "digits": "رقم", + "delete": "حذف", + "color_type": "لون", + "multiplicity": "تعددية", + "number": "عدد", + "boolean": "منطقي", + "url": "عنوان الويب", + "inheritable": "قابل للوراثة" + }, + "rename_label": { + "to": "الى" + }, + "move_note": { + "to": "الى" + }, + "add_relation": { + "to": "الى" + }, + "rename_relation": { + "to": "الى" + }, + "update_relation_target": { + "to": "الى" + }, + "attachments_actions": { + "download": "تنزيل" + }, + "calendar": { + "week": "أسبوع", + "month": "شهر", + "year": "سنة", + "list": "قائمة", + "today": "اليوم", + "mon": "الأثنين", + "tue": "الثلاثاء", + "wed": "الأربعاء", + "thu": "الخميس", + "fri": "الجمعة", + "sat": "السبت", + "sun": "الأحد", + "january": "يناير", + "march": "مارس", + "april": "ابريل", + "may": "مايو", + "june": "يونيو", + "july": "يوليو", + "august": "أغسطس", + "september": "سبتمبر", + "october": "اكتوبر", + "november": "نوفمبر", + "december": "ديسمبر" + }, + "global_menu": { + "menu": "القائمة", + "options": "خيارات", + "advanced": "متقدمة", + "logout": "تسجيل خروج", + "zoom": "تكبير/تصغير" + }, + "zpetne_odkazy": { + "relation": "العلاقة" + }, + "note_icon": { + "category": "الفئة:", + "search": "بحث:" + }, + "basic_properties": { + "language": "اللغة", + "editable": "قابل للتعديل" + }, + "book_properties": { + "list": "قائمة", + "expand": "توسيع", + "calendar": "التقويم", + "table": "جدول", + "board": "لوحة", + "grid": "خطوط شبكة", + "collapse": "طي" + }, + "file_properties": { + "download": "تنزيل", + "open": "فتح", + "title": "ملف" + }, + "image_properties": { + "download": "تنزيل", + "open": "فتح", + "title": "صورة" + }, + "note_info_widget": { + "created": "انشاء", + "type": "نوع", + "modified": "معدل", + "calculate": "حساب" + }, + "note_paths": { + "search": "بحث", + "archived": "مؤرشف" + }, + "script_executor": { + "query": "استعلام", + "script": "برنامج نصي" + }, + "search_definition": { + "ancestor": "السلف", + "limit": "الحد الاقصى", + "action": "أجراء", + "search_button": "بحث", + "debug": "تصحيح الاخطاء" + }, + "ancestor": { + "label": "السلف", + "depth_label": "العمق" + }, + "limit": { + "limit": "الحد الاقصى" + }, + "debug": { + "debug": "تصحيح الاخطاء" + }, + "order_by": { + "title": "عنوان", + "desc": "تنازلي" + }, + "search_string": { + "search_prefix": "بحث:" + }, + "sync": { + "title": "مزامنة" + }, + "fonts": { + "fonts": "خطوط", + "size": "حجم", + "serif": "خط ومزخرف", + "monospace": "خط بعرض ثابت" + }, + "confirm": { + "confirmation": "تأكيد", + "cancel": "الغاء", + "ok": "نعم" + }, + "delete_notes": { + "close": "غلق", + "cancel": "الغاء", + "ok": "نعم" + }, + "export": { + "close": "غلق", + "export": "تصدير", + "export_note_title": "تصدير الملاحظة", + "export_status": "حالة التصدير" + }, + "help": { + "troubleshooting": "أستكشاف الاخطاء واصلاحها", + "other": "أخرى", + "title": "ورقة المراجعة السريعة", + "noteNavigation": "التنقل بين الملاحظات", + "collapseExpand": "طي/توسيع العقدة", + "notSet": "غير محدد", + "collapseSubTree": "طي الشجرة الفرعية", + "tabShortcuts": "أختصارات التبويب", + "creatingNotes": "انشاء الملاحظات", + "selectNote": "تحديد الملاحظة" + }, + "import": { + "options": "خيارات", + "import": "استيراد" + }, + "include_note": { + "label_note": "ملاحظة" + }, + "info": { + "closeButton": "أغلاق", + "okButton": "نعم" + }, + "markdown_import": { + "import_button": "أستيراد" + }, + "note_type_chooser": { + "templates": "قوالب" + }, + "prompt": { + "title": "ترقية", + "ok": "نعم", + "defaultTitle": "ترقية" + }, + "protected_session_password": { + "close_label": "أغلاق" + }, + "revisions": { + "delete_button": "حذف", + "download_button": "تنزيل", + "restore_button": "أستعادة", + "preview": "معاينة:" + }, + "sort_child_notes": { + "title": "عنوان", + "ascending": "تصاعدي", + "descending": "تنازلي", + "folders": "مجلدات", + "sort": "فرز" + }, + "recent_changes": { + "undelete_link": "الغاء الحذف" + }, + "edited_notes": { + "deleted": "(حذف)" + }, + "note_properties": { + "info": "معلومات" + }, + "backend_log": { + "refresh": "تحديث" + }, + "max_content_width": { + "max_width_unit": "بكسل" + }, + "native_title_bar": { + "enabled": "مفعل", + "disabled": "معطل" + }, + "theme": { + "theme_label": "السمة", + "layout": "تخطيط", + "layout-vertical-title": "عمودي", + "layout-horizontal-title": "أفقي" + }, + "ui-performance": { + "title": "أداء" + }, + "ai_llm": { + "progress": "تقدم", + "openai_tab": "OpenAI", + "actions": "أجراءات", + "retry": "أعد المحاولة", + "reprocessing_index": "جار اعادة البناء...", + "never": "ابدٱ", + "agent": { + "processing": "جار المعالجة...", + "thinking": "جار التفكير...", + "loading": "جار التحميل...", + "generating": "جار الانشاء..." + }, + "name": "الذكاء الأصطناعي", + "openai": "OpenAI", + "sources": "مصادر" + }, + "code_auto_read_only_size": { + "unit": "حروف" + }, + "code-editor-options": { + "title": "محرر" + }, + "images": { + "images_section_title": "صور", + "max_image_dimensions_unit": "بكسل" + }, + "revisions_snapshot_limit": { + "snapshot_number_limit_unit": "لقطات" + }, + "search_engine": { + "bing": "Bing", + "duckduckgo": "DuckDuckGo", + "google": "جوجل", + "save_button": "حفظ" + }, + "heading_style": { + "plain": "بسيط" + }, + "text_auto_read_only_size": { + "unit": "حروف" + }, + "i18n": { + "language": "لغة", + "sunday": "الأحد", + "monday": "الأثنين" + }, + "backup": { + "path": "مسار" + }, + "etapi": { + "wiki": "ويكي", + "created": "تم الأنشاء", + "actions": "أجراءات" + }, + "password": { + "heading": "كلمة السر", + "wiki": "ويكي" + }, + "shortcuts": { + "shortcuts": "أختصارات", + "description": "الوصف" + }, + "sync_2": { + "timeout_unit": "ميلي ثانية", + "note": "ملاحظة", + "save": "حفظ", + "help": "المساعدة" + }, + "api_log": { + "close": "أغلاق" + }, + "bookmark_switch": { + "bookmark": "علامة مرجعية" + }, + "editability_select": { + "auto": "تلقائي", + "read_only": "قراءة-فقط" + }, + "tab_row": { + "close": "اغلاق" + }, + "toc": { + "options": "خيارات" + }, + "tasks": { + "due": { + "yesterday": "أمس" + } + }, + "code_theme": { + "title": "المظهر" + }, + "table_view": { + "sort-column-ascending": "تصاعدي", + "sort-column-descending": "تنازلي", + "new-column-relation": "العلاقة" + }, + "modal": { + "close": "اغلاق" + }, + "call_to_action": { + "dismiss": "تجاهل" + }, + "units": { + "percentage": "%" + }, + "clone_to": { + "prefix_optional": "بادئة (اختياري)" } } diff --git a/apps/client/src/translations/id/translation.json b/apps/client/src/translations/id/translation.json new file mode 100644 index 000000000..0b488eb5c --- /dev/null +++ b/apps/client/src/translations/id/translation.json @@ -0,0 +1,8 @@ +{ + "about": { + "title": "Tentang Trilium Notes", + "homepage": "Halaman utama:", + "app_version": "Versi Aplikasi:", + "db_version": "Versi DB:" + } +} diff --git a/apps/client/src/translations/ja/translation.json b/apps/client/src/translations/ja/translation.json index d7401af9c..b101d56a8 100644 --- a/apps/client/src/translations/ja/translation.json +++ b/apps/client/src/translations/ja/translation.json @@ -67,7 +67,8 @@ "switch_to_mobile_version": "モバイル版に切り替え", "switch_to_desktop_version": "デスクトップ版に切り替え", "configure_launchbar": "ランチャーバーの設定", - "show_shared_notes_subtree": "共有ノートのサブツリーを表示" + "show_shared_notes_subtree": "共有ノートのサブツリーを表示", + "update_available": "バージョン {{latestVersion}} が利用可能です。クリックしてダウンロードしてください。" }, "left_pane_toggle": { "show_panel": "パネルを表示", @@ -1290,7 +1291,7 @@ "cut": "カット", "copy": "コピー", "copy-link": "リンクをコピー", - "paste": "ペースト", + "paste": "貼り付け", "paste-as-plain-text": "プレーンテキストで貼り付け", "search_online": "{{searchEngine}} で \"{{term}}\" を検索" }, diff --git a/apps/desktop/package.json b/apps/desktop/package.json index bf27b33b9..2e305e2a8 100644 --- a/apps/desktop/package.json +++ b/apps/desktop/package.json @@ -34,15 +34,15 @@ "@triliumnext/commons": "workspace:*", "@triliumnext/server": "workspace:*", "copy-webpack-plugin": "13.0.1", - "electron": "38.2.1", - "@electron-forge/cli": "7.9.0", - "@electron-forge/maker-deb": "7.9.0", - "@electron-forge/maker-dmg": "7.9.0", - "@electron-forge/maker-flatpak": "7.9.0", - "@electron-forge/maker-rpm": "7.9.0", - "@electron-forge/maker-squirrel": "7.9.0", - "@electron-forge/maker-zip": "7.9.0", - "@electron-forge/plugin-auto-unpack-natives": "7.9.0", + "electron": "38.2.2", + "@electron-forge/cli": "7.10.0", + "@electron-forge/maker-deb": "7.10.0", + "@electron-forge/maker-dmg": "7.10.0", + "@electron-forge/maker-flatpak": "7.10.0", + "@electron-forge/maker-rpm": "7.10.0", + "@electron-forge/maker-squirrel": "7.10.0", + "@electron-forge/maker-zip": "7.10.0", + "@electron-forge/plugin-auto-unpack-natives": "7.10.0", "prebuild-install": "7.1.3" } } \ No newline at end of file diff --git a/apps/edit-docs/package.json b/apps/edit-docs/package.json index 2fb17e9ab..91fe601ac 100644 --- a/apps/edit-docs/package.json +++ b/apps/edit-docs/package.json @@ -12,7 +12,7 @@ "@triliumnext/desktop": "workspace:*", "@types/fs-extra": "11.0.4", "copy-webpack-plugin": "13.0.1", - "electron": "38.2.1", + "electron": "38.2.2", "fs-extra": "11.3.2" }, "scripts": { diff --git a/apps/server/package.json b/apps/server/package.json index 3acd2702a..df940da85 100644 --- a/apps/server/package.json +++ b/apps/server/package.json @@ -81,7 +81,7 @@ "debounce": "2.2.0", "debug": "4.4.3", "ejs": "3.1.10", - "electron": "38.2.1", + "electron": "38.2.2", "electron-debug": "4.1.0", "electron-window-state": "5.0.3", "escape-html": "1.0.3", @@ -105,7 +105,7 @@ "is-svg": "6.1.0", "jimp": "1.6.0", "js-yaml": "4.1.0", - "marked": "16.3.0", + "marked": "16.4.0", "mime-types": "3.0.1", "multer": "2.0.2", "normalize-strings": "1.1.1", diff --git a/apps/server/src/assets/translations/ar/server.json b/apps/server/src/assets/translations/ar/server.json index c7080eb21..99736f312 100644 --- a/apps/server/src/assets/translations/ar/server.json +++ b/apps/server/src/assets/translations/ar/server.json @@ -18,6 +18,134 @@ "copy-notes-to-clipboard": "نسخ الملاحظات المحددة الى الحافظة", "paste-notes-from-clipboard": "لصق الملاحظا من الحافظة الى الملاحظة الحالية", "cut-notes-to-clipboard": "قص الملاحظات المحددة الى الحافظة", - "select-all-notes-in-parent": "تحديد جميع الملاحظات من مستوى الملاحظة الحالي" + "select-all-notes-in-parent": "تحديد جميع الملاحظات من مستوى الملاحظة الحالي", + "back-in-note-history": "الانتقال الى الملاحظة السابقة في السجل", + "forward-in-note-history": "الانتقال الى الملاحظة التالية في السجل", + "scroll-to-active-note": "تمرير شجرة الملاحظات الى الملاحظة النشطة", + "search-in-subtree": "البحث عن الملاحظات في الشجرة الفرعية للملاحظة النشطة", + "expand-subtree": "توسيع الشجرة الفرعية للملاحظة الحالية", + "create-note-into-inbox": "انشاء ملاحظة في صندوق الوارد (اذا كان معرفا) او في ملاحظة اليوم", + "move-note-up-in-hierarchy": "نقل الملاحظة للاعلى في التسلسل الهرمي", + "move-note-down-in-hierarchy": "نقل الملاحظة للاسفل في التسلسل الهرمي", + "edit-note-title": "الانتقال من شجرة الملاحظات إلى تفاصيل الملاحظة وتحرير العنوان", + "edit-branch-prefix": "عرض مربع حوار \"تعديل بادئة الفرع\"", + "add-note-above-to-the-selection": "اضافة ملاحظة فوق الملاحظة المحددة", + "add-note-below-to-selection": "اضافة ملاحظة اسفل الملاحظة المحددة", + "duplicate-subtree": "استنساخ الشجرة الفرعية", + "tabs-and-windows": "التبويبات والنوافذ", + "open-new-tab": "فتح تبويب جديد", + "close-active-tab": "غلق التبويب النشط", + "reopen-last-tab": "اعادة فتح اخر تبويب مغلق", + "activate-next-tab": "تنشيط التبويب الموجود على اليمين", + "activate-previous-tab": "تنشيط التبويب الموجود على اليسار", + "open-new-window": "فتح نافذة جديدة فارغة", + "first-tab": "تنشيط التبويب الاول في القائمة", + "second-tab": "تنشيط التبويب الثاني في القائمة", + "third-tab": "تنشيط التبويب الثالث في الثائمة", + "fourth-tab": "تنشيط التبويب الرابع في القائمة", + "fifth-tab": "تنشيط التبويب الخامس في القائمة", + "sixth-tab": "تنشيط التبويب السادس في القائمة", + "seventh-tab": "تنشيط التبويب السابع في القائمة", + "eight-tab": "تنشيط التبويب الثامن في القائمة", + "ninth-tab": "تنشيط التبويب التاسع في القائمة", + "last-tab": "تنشيط التبويب الاخير في القائمة", + "other": "أخرى", + "dialogs": "مربعات الحوار" + }, + "setup_sync-from-server": { + "note": "ملاحظة:", + "password": "كلمة السر", + "password-placeholder": "كلمة السر", + "back": "رجوع", + "server-host-placeholder": "https://:", + "proxy-server-placeholder": "https://:" + }, + "weekdays": { + "monday": "الأثنين", + "tuesday": "الثلاثاء", + "wednesday": "الاربعاء", + "thursday": "الخميس", + "friday": "الجمعة", + "saturday": "السبت", + "sunday": "الأحد" + }, + "months": { + "january": "يناير", + "february": "فبراير", + "march": "مارس", + "april": "ابريل", + "may": "مايو", + "june": "يونيو", + "july": "يوليو", + "august": "أغسطس", + "september": "سبتمبر", + "october": "أكتوبر", + "november": "نوفمبر", + "december": "ديسمبر" + }, + "special_notes": { + "search_prefix": "بحث:" + }, + "hidden-subtree": { + "calendar-title": "تقويم", + "bookmarks-title": "العلامات المرجعية", + "settings-title": "أعدادات", + "options-title": "خيارات", + "appearance-title": "المظهر", + "shortcuts-title": "أختصارات", + "images-title": "صور", + "password-title": "كلمة السر", + "backup-title": "نسخة أحتياطية", + "sync-title": "مزامنة", + "other": "أخرى", + "advanced-title": "متقدم", + "inbox-title": "صندوق الوارد", + "spacer-title": "فاصل", + "spellcheck-title": "التدقيق الاملائي", + "multi-factor-authentication-title": "المصادقة متعددة العوامل" + }, + "tray": { + "bookmarks": "العلامات المرجعية" + }, + "modals": { + "error_title": "خطأ" + }, + "share_theme": { + "search_placeholder": "بحث...", + "subpages": "الصفحات الفرعية:", + "expand": "توسيع" + }, + "hidden_subtree_templates": { + "description": "الوصف", + "calendar": "التقويم", + "table": "جدول", + "geolocation": "الموقع الجغرافي", + "board": "لوحة", + "status": "الحالة", + "board_status_done": "تمت" + }, + "login": { + "title": "تسجيل الدخول", + "password": "كلمة السر", + "button": "تسجيل الدخول" + }, + "set_password": { + "password": "كلمة السر" + }, + "setup": { + "next": "التالي", + "title": "تثبيت" + }, + "setup_sync-from-desktop": { + "step6-here": "هنا" + }, + "setup_sync-in-progress": { + "outstanding-items-default": "غير متوفر" + }, + "share_page": { + "parent": "الأصل:" + }, + "notes": { + "duplicate-note-suffix": "(مكرر)" } } diff --git a/apps/server/src/assets/translations/id/server.json b/apps/server/src/assets/translations/id/server.json new file mode 100644 index 000000000..9a6086a1a --- /dev/null +++ b/apps/server/src/assets/translations/id/server.json @@ -0,0 +1,8 @@ +{ + "keyboard_actions": { + "back-in-note-history": "Navigasi ke catatan sebelumnya di history", + "forward-in-note-history": "Navigasi ke catatan selanjutnya di history", + "open-jump-to-note-dialog": "Buka dialog \"Menuju ke catatan\"", + "open-command-palette": "Buka palet perintah" + } +} diff --git a/apps/server/src/routes/api/clipper.spec.ts b/apps/server/src/routes/api/clipper.spec.ts index 1efa6915e..17b43f3b9 100644 --- a/apps/server/src/routes/api/clipper.spec.ts +++ b/apps/server/src/routes/api/clipper.spec.ts @@ -1,4 +1,5 @@ import { BNote } from "../../services/backend_script_entrypoint"; +import cls from "../../services/cls"; import { buildNote } from "../../test/becca_easy_mocking"; import { processContent } from "./clipper"; @@ -6,7 +7,9 @@ let note!: BNote; describe("processContent", () => { beforeAll(() => { - note = buildNote({}); + note = buildNote({ + content: "Hi there" + }); note.saveAttachment = () => {}; vi.mock("../../services/image.js", () => ({ default: { @@ -21,29 +24,29 @@ describe("processContent", () => { }); it("processes basic note", () => { - const processed = processContent([], note, "

Hello world.

"); + const processed = cls.init(() => processContent([], note, "

Hello world.

")); expect(processed).toStrictEqual("

Hello world.

") }); it("processes plain text", () => { - const processed = processContent([], note, "Hello world."); + const processed = cls.init(() => processContent([], note, "Hello world.")); expect(processed).toStrictEqual("

Hello world.

") }); it("replaces images", () => { - const processed = processContent( + const processed = cls.init(() => processContent( [{"imageId":"OKZxZA3MonZJkwFcEhId","src":"inline.png","dataUrl":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAkAAAAQCAYAAADESFVDAAAAF0lEQVQoU2P8DwQMBADjqKLRIGAgKggAzHs/0SoYCGwAAAAASUVORK5CYII="}], note, `` - ); + )); expect(processed).toStrictEqual(``); }); it("skips over non-data images", () => { for (const url of [ "foo", "" ]) { - const processed = processContent( + const processed = cls.init(() => processContent( [{"imageId":"OKZxZA3MonZJkwFcEhId","src":"inline.png","dataUrl": url}], note, `` - ); + )); expect(processed).toStrictEqual(``); } }); diff --git a/apps/server/src/routes/api/llm.spec.ts b/apps/server/src/routes/api/llm.spec.ts index 81bd9e039..f8afddfa6 100644 --- a/apps/server/src/routes/api/llm.spec.ts +++ b/apps/server/src/routes/api/llm.spec.ts @@ -3,7 +3,7 @@ import { beforeAll, describe, expect, it, vi, beforeEach, afterEach } from "vite import supertest from "supertest"; import config from "../../services/config.js"; import { refreshAuth } from "../../services/auth.js"; -import type { WebSocket } from 'ws'; +import { sleepFor } from "@triliumnext/commons"; // Mock the CSRF protection middleware to allow tests to pass vi.mock("../csrf_protection.js", () => ({ @@ -72,7 +72,11 @@ vi.mock("../../services/options.js", () => ({ getOptionMap: vi.fn(() => new Map()), createOption: vi.fn(), getOption: vi.fn(() => '0'), - getOptionOrNull: vi.fn(() => null) + getOptionOrNull: vi.fn(() => null), + getOptionInt: vi.fn(name => { + if (name === "protectedSessionTimeout") return Number.MAX_SAFE_INTEGER; + return 0; + }) } })); @@ -499,6 +503,7 @@ describe("LLM API Tests", () => { const ws = (await import("../../services/ws.js")).default; // Verify thinking message was sent + await sleepFor(1_000); expect(ws.sendMessageToAllClients).toHaveBeenCalledWith({ type: 'llm-stream', chatNoteId: testChatId, diff --git a/apps/server/src/services/llm/ai_service_manager.spec.ts b/apps/server/src/services/llm/ai_service_manager.spec.ts index c31d90d26..14305cf6b 100644 --- a/apps/server/src/services/llm/ai_service_manager.spec.ts +++ b/apps/server/src/services/llm/ai_service_manager.spec.ts @@ -12,7 +12,11 @@ import type { AIService, ChatCompletionOptions, Message } from './ai_interface.j vi.mock('../options.js', () => ({ default: { getOption: vi.fn(), - getOptionBool: vi.fn() + getOptionBool: vi.fn(), + getOptionInt: vi.fn(name => { + if (name === "protectedSessionTimeout") return Number.MAX_SAFE_INTEGER; + return 0; + }) } })); @@ -110,26 +114,26 @@ describe('AIServiceManager', () => { describe('getSelectedProviderAsync', () => { it('should return the selected provider', async () => { vi.mocked(configHelpers.getSelectedProvider).mockResolvedValueOnce('openai'); - + const result = await manager.getSelectedProviderAsync(); - + expect(result).toBe('openai'); expect(configHelpers.getSelectedProvider).toHaveBeenCalled(); }); it('should return null if no provider is selected', async () => { vi.mocked(configHelpers.getSelectedProvider).mockResolvedValueOnce(null); - + const result = await manager.getSelectedProviderAsync(); - + expect(result).toBeNull(); }); it('should handle errors and return null', async () => { vi.mocked(configHelpers.getSelectedProvider).mockRejectedValueOnce(new Error('Config error')); - + const result = await manager.getSelectedProviderAsync(); - + expect(result).toBeNull(); }); }); @@ -141,9 +145,9 @@ describe('AIServiceManager', () => { errors: [], warnings: [] }); - + const result = await manager.validateConfiguration(); - + expect(result).toBeNull(); }); @@ -153,9 +157,9 @@ describe('AIServiceManager', () => { errors: ['Missing API key', 'Invalid model'], warnings: [] }); - + const result = await manager.validateConfiguration(); - + expect(result).toContain('There are issues with your AI configuration'); expect(result).toContain('Missing API key'); expect(result).toContain('Invalid model'); @@ -167,9 +171,9 @@ describe('AIServiceManager', () => { errors: [], warnings: ['Model not optimal'] }); - + const result = await manager.validateConfiguration(); - + expect(result).toBeNull(); }); }); @@ -178,21 +182,21 @@ describe('AIServiceManager', () => { it('should create and return the selected provider service', async () => { vi.mocked(configHelpers.getSelectedProvider).mockResolvedValueOnce('openai'); vi.mocked(options.getOption).mockReturnValueOnce('test-api-key'); - + const mockService = { isAvailable: vi.fn().mockReturnValue(true), generateChatCompletion: vi.fn() }; vi.mocked(OpenAIService).mockImplementationOnce(() => mockService as any); - + const result = await manager.getOrCreateAnyService(); - + expect(result).toBe(mockService); }); it('should throw error if no provider is selected', async () => { vi.mocked(configHelpers.getSelectedProvider).mockResolvedValueOnce(null); - + await expect(manager.getOrCreateAnyService()).rejects.toThrow( 'No AI provider is selected' ); @@ -201,7 +205,7 @@ describe('AIServiceManager', () => { it('should throw error if selected provider is not available', async () => { vi.mocked(configHelpers.getSelectedProvider).mockResolvedValueOnce('openai'); vi.mocked(options.getOption).mockReturnValueOnce(''); // No API key - + await expect(manager.getOrCreateAnyService()).rejects.toThrow( 'Selected AI provider (openai) is not available' ); @@ -211,17 +215,17 @@ describe('AIServiceManager', () => { describe('isAnyServiceAvailable', () => { it('should return true if any provider is available', () => { vi.mocked(options.getOption).mockReturnValueOnce('test-api-key'); - + const result = manager.isAnyServiceAvailable(); - + expect(result).toBe(true); }); it('should return false if no providers are available', () => { vi.mocked(options.getOption).mockReturnValue(''); - + const result = manager.isAnyServiceAvailable(); - + expect(result).toBe(false); }); }); @@ -232,18 +236,18 @@ describe('AIServiceManager', () => { .mockReturnValueOnce('openai-key') .mockReturnValueOnce('anthropic-key') .mockReturnValueOnce(''); // No Ollama URL - + const result = manager.getAvailableProviders(); - + expect(result).toEqual(['openai', 'anthropic']); }); it('should include already created services', () => { // Mock that OpenAI has API key configured vi.mocked(options.getOption).mockReturnValueOnce('test-api-key'); - + const result = manager.getAvailableProviders(); - + expect(result).toContain('openai'); }); }); @@ -255,23 +259,23 @@ describe('AIServiceManager', () => { it('should generate completion with selected provider', async () => { vi.mocked(configHelpers.getSelectedProvider).mockResolvedValueOnce('openai'); - + // Mock the getAvailableProviders to include openai vi.mocked(options.getOption) .mockReturnValueOnce('test-api-key') // for availability check .mockReturnValueOnce('') // for anthropic .mockReturnValueOnce('') // for ollama .mockReturnValueOnce('test-api-key'); // for service creation - + const mockResponse = { content: 'Hello response' }; const mockService = { isAvailable: vi.fn().mockReturnValue(true), generateChatCompletion: vi.fn().mockResolvedValueOnce(mockResponse) }; vi.mocked(OpenAIService).mockImplementationOnce(() => mockService as any); - + const result = await manager.generateChatCompletion(messages); - + expect(result).toBe(mockResponse); expect(mockService.generateChatCompletion).toHaveBeenCalledWith(messages, {}); }); @@ -283,28 +287,28 @@ describe('AIServiceManager', () => { modelId: 'gpt-4', fullIdentifier: 'openai:gpt-4' }); - + // Mock the getAvailableProviders to include openai vi.mocked(options.getOption) .mockReturnValueOnce('test-api-key') // for availability check .mockReturnValueOnce('') // for anthropic .mockReturnValueOnce('') // for ollama .mockReturnValueOnce('test-api-key'); // for service creation - + const mockResponse = { content: 'Hello response' }; const mockService = { isAvailable: vi.fn().mockReturnValue(true), generateChatCompletion: vi.fn().mockResolvedValueOnce(mockResponse) }; vi.mocked(OpenAIService).mockImplementationOnce(() => mockService as any); - - const result = await manager.generateChatCompletion(messages, { - model: 'openai:gpt-4' + + const result = await manager.generateChatCompletion(messages, { + model: 'openai:gpt-4' }); - + expect(result).toBe(mockResponse); expect(mockService.generateChatCompletion).toHaveBeenCalledWith( - messages, + messages, { model: 'gpt-4' } ); }); @@ -317,7 +321,7 @@ describe('AIServiceManager', () => { it('should throw error if no provider selected', async () => { vi.mocked(configHelpers.getSelectedProvider).mockResolvedValueOnce(null); - + await expect(manager.generateChatCompletion(messages)).rejects.toThrow( 'No AI provider is selected' ); @@ -330,13 +334,13 @@ describe('AIServiceManager', () => { modelId: 'claude-3', fullIdentifier: 'anthropic:claude-3' }); - + // Mock that openai is available vi.mocked(options.getOption) .mockReturnValueOnce('test-api-key') // for availability check .mockReturnValueOnce('') // for anthropic .mockReturnValueOnce(''); // for ollama - + await expect( manager.generateChatCompletion(messages, { model: 'anthropic:claude-3' }) ).rejects.toThrow( @@ -348,9 +352,9 @@ describe('AIServiceManager', () => { describe('getAIEnabledAsync', () => { it('should return AI enabled status', async () => { vi.mocked(configHelpers.isAIEnabled).mockResolvedValueOnce(true); - + const result = await manager.getAIEnabledAsync(); - + expect(result).toBe(true); expect(configHelpers.isAIEnabled).toHaveBeenCalled(); }); @@ -359,9 +363,9 @@ describe('AIServiceManager', () => { describe('getAIEnabled', () => { it('should return AI enabled status synchronously', () => { vi.mocked(options.getOptionBool).mockReturnValueOnce(true); - + const result = manager.getAIEnabled(); - + expect(result).toBe(true); expect(options.getOptionBool).toHaveBeenCalledWith('aiEnabled'); }); @@ -370,17 +374,17 @@ describe('AIServiceManager', () => { describe('initialize', () => { it('should initialize if AI is enabled', async () => { vi.mocked(configHelpers.isAIEnabled).mockResolvedValueOnce(true); - + await manager.initialize(); - + expect(configHelpers.isAIEnabled).toHaveBeenCalled(); }); it('should not initialize if AI is disabled', async () => { vi.mocked(configHelpers.isAIEnabled).mockResolvedValueOnce(false); - + await manager.initialize(); - + expect(configHelpers.isAIEnabled).toHaveBeenCalled(); }); }); @@ -388,36 +392,36 @@ describe('AIServiceManager', () => { describe('getService', () => { it('should return service for specified provider', async () => { vi.mocked(options.getOption).mockReturnValueOnce('test-api-key'); - + const mockService = { isAvailable: vi.fn().mockReturnValue(true), generateChatCompletion: vi.fn() }; vi.mocked(OpenAIService).mockImplementationOnce(() => mockService as any); - + const result = await manager.getService('openai'); - + expect(result).toBe(mockService); }); it('should return selected provider service if no provider specified', async () => { vi.mocked(configHelpers.getSelectedProvider).mockResolvedValueOnce('anthropic'); vi.mocked(options.getOption).mockReturnValueOnce('test-api-key'); - + const mockService = { isAvailable: vi.fn().mockReturnValue(true), generateChatCompletion: vi.fn() }; vi.mocked(AnthropicService).mockImplementationOnce(() => mockService as any); - + const result = await manager.getService(); - + expect(result).toBe(mockService); }); it('should throw error if specified provider not available', async () => { vi.mocked(options.getOption).mockReturnValueOnce(''); // No API key - + await expect(manager.getService('openai')).rejects.toThrow( 'Specified provider openai is not available' ); @@ -427,17 +431,17 @@ describe('AIServiceManager', () => { describe('getSelectedProvider', () => { it('should return selected provider synchronously', () => { vi.mocked(options.getOption).mockReturnValueOnce('anthropic'); - + const result = manager.getSelectedProvider(); - + expect(result).toBe('anthropic'); }); it('should return default provider if none selected', () => { vi.mocked(options.getOption).mockReturnValueOnce(''); - + const result = manager.getSelectedProvider(); - + expect(result).toBe('openai'); }); }); @@ -446,18 +450,18 @@ describe('AIServiceManager', () => { it('should return true if provider service is available', () => { // Mock that OpenAI has API key configured vi.mocked(options.getOption).mockReturnValueOnce('test-api-key'); - + const result = manager.isProviderAvailable('openai'); - + expect(result).toBe(true); }); it('should return false if provider service not created', () => { // Mock that OpenAI has no API key configured vi.mocked(options.getOption).mockReturnValueOnce(''); - + const result = manager.isProviderAvailable('openai'); - + expect(result).toBe(false); }); }); @@ -467,13 +471,13 @@ describe('AIServiceManager', () => { // Since getProviderMetadata only returns metadata for the current active provider, // and we don't have a current provider set, it should return null const result = manager.getProviderMetadata('openai'); - + expect(result).toBeNull(); }); it('should return null for non-existing provider', () => { const result = manager.getProviderMetadata('openai'); - + expect(result).toBeNull(); }); }); @@ -485,4 +489,4 @@ describe('AIServiceManager', () => { expect(manager).toBeDefined(); }); }); -}); \ No newline at end of file +}); diff --git a/apps/server/src/services/llm/chat/rest_chat_service.spec.ts b/apps/server/src/services/llm/chat/rest_chat_service.spec.ts index 61edd71ca..03e52887d 100644 --- a/apps/server/src/services/llm/chat/rest_chat_service.spec.ts +++ b/apps/server/src/services/llm/chat/rest_chat_service.spec.ts @@ -15,7 +15,11 @@ vi.mock('../../log.js', () => ({ vi.mock('../../options.js', () => ({ default: { getOption: vi.fn(), - getOptionBool: vi.fn() + getOptionBool: vi.fn(), + getOptionInt: vi.fn(name => { + if (name === "protectedSessionTimeout") return Number.MAX_SAFE_INTEGER; + return 0; + }) } })); @@ -66,14 +70,14 @@ describe('RestChatService', () => { beforeEach(async () => { vi.clearAllMocks(); - + // Get mocked modules mockOptions = (await import('../../options.js')).default; mockAiServiceManager = (await import('../ai_service_manager.js')).default; mockChatStorageService = (await import('../chat_storage_service.js')).default; - + restChatService = (await import('./rest_chat_service.js')).default; - + // Setup mock request and response mockReq = { params: {}, @@ -81,7 +85,7 @@ describe('RestChatService', () => { query: {}, method: 'POST' }; - + mockRes = { status: vi.fn().mockReturnThis(), json: vi.fn().mockReturnThis(), @@ -240,7 +244,7 @@ describe('RestChatService', () => { it('should handle GET request with stream parameter', async () => { mockReq.method = 'GET'; - mockReq.query = { + mockReq.query = { stream: 'true', useAdvancedContext: 'true', showThinking: 'false' @@ -419,4 +423,4 @@ describe('RestChatService', () => { expect(mockChatStorageService.getChat).toHaveBeenCalledWith('chat-123'); }); }); -}); \ No newline at end of file +}); diff --git a/apps/server/src/services/llm/config/configuration_helpers.spec.ts b/apps/server/src/services/llm/config/configuration_helpers.spec.ts index 82d912301..7ea5c9420 100644 --- a/apps/server/src/services/llm/config/configuration_helpers.spec.ts +++ b/apps/server/src/services/llm/config/configuration_helpers.spec.ts @@ -18,7 +18,11 @@ vi.mock('./configuration_manager.js', () => ({ vi.mock('../../options.js', () => ({ default: { getOption: vi.fn(), - getOptionBool: vi.fn() + getOptionBool: vi.fn(), + getOptionInt: vi.fn(name => { + if (name === "protectedSessionTimeout") return Number.MAX_SAFE_INTEGER; + return 0; + }) } })); @@ -42,26 +46,26 @@ describe('configuration_helpers', () => { describe('getSelectedProvider', () => { it('should return the selected provider', async () => { vi.mocked(optionService.getOption).mockReturnValueOnce('openai'); - + const result = await configHelpers.getSelectedProvider(); - + expect(result).toBe('openai'); expect(optionService.getOption).toHaveBeenCalledWith('aiSelectedProvider'); }); it('should return null if no provider is selected', async () => { vi.mocked(optionService.getOption).mockReturnValueOnce(''); - + const result = await configHelpers.getSelectedProvider(); - + expect(result).toBeNull(); }); it('should handle invalid provider and return null', async () => { vi.mocked(optionService.getOption).mockReturnValueOnce('invalid-provider'); - + const result = await configHelpers.getSelectedProvider(); - + expect(result).toBe('invalid-provider' as ProviderType); }); }); @@ -69,7 +73,7 @@ describe('configuration_helpers', () => { describe('parseModelIdentifier', () => { it('should parse model identifier directly', () => { const result = configHelpers.parseModelIdentifier('openai:gpt-4'); - + expect(result).toStrictEqual({ provider: 'openai', modelId: 'gpt-4', @@ -79,7 +83,7 @@ describe('configuration_helpers', () => { it('should handle model without provider', () => { const result = configHelpers.parseModelIdentifier('gpt-4'); - + expect(result).toStrictEqual({ modelId: 'gpt-4', fullIdentifier: 'gpt-4' @@ -88,7 +92,7 @@ describe('configuration_helpers', () => { it('should handle empty model string', () => { const result = configHelpers.parseModelIdentifier(''); - + expect(result).toStrictEqual({ modelId: '', fullIdentifier: '' @@ -98,7 +102,7 @@ describe('configuration_helpers', () => { // Tests for special characters in model names it('should handle model names with periods', () => { const result = configHelpers.parseModelIdentifier('gpt-4.1-turbo-preview'); - + expect(result).toStrictEqual({ modelId: 'gpt-4.1-turbo-preview', fullIdentifier: 'gpt-4.1-turbo-preview' @@ -107,7 +111,7 @@ describe('configuration_helpers', () => { it('should handle model names with provider prefix and periods', () => { const result = configHelpers.parseModelIdentifier('openai:gpt-4.1-turbo'); - + expect(result).toStrictEqual({ provider: 'openai', modelId: 'gpt-4.1-turbo', @@ -117,7 +121,7 @@ describe('configuration_helpers', () => { it('should handle model names with multiple colons', () => { const result = configHelpers.parseModelIdentifier('custom:model:v1.2:latest'); - + expect(result).toStrictEqual({ modelId: 'custom:model:v1.2:latest', fullIdentifier: 'custom:model:v1.2:latest' @@ -126,7 +130,7 @@ describe('configuration_helpers', () => { it('should handle Ollama model names with colons', () => { const result = configHelpers.parseModelIdentifier('ollama:llama3.1:70b-instruct-q4_K_M'); - + expect(result).toStrictEqual({ provider: 'ollama', modelId: 'llama3.1:70b-instruct-q4_K_M', @@ -136,7 +140,7 @@ describe('configuration_helpers', () => { it('should handle model names with slashes', () => { const result = configHelpers.parseModelIdentifier('library/mistral:7b-instruct'); - + expect(result).toStrictEqual({ modelId: 'library/mistral:7b-instruct', fullIdentifier: 'library/mistral:7b-instruct' @@ -146,7 +150,7 @@ describe('configuration_helpers', () => { it('should handle complex model names with special characters', () => { const complexName = 'org/model-v1.2.3:tag@version#variant'; const result = configHelpers.parseModelIdentifier(complexName); - + expect(result).toStrictEqual({ modelId: complexName, fullIdentifier: complexName @@ -155,7 +159,7 @@ describe('configuration_helpers', () => { it('should handle model names with @ symbols', () => { const result = configHelpers.parseModelIdentifier('claude-3.5-sonnet@20241022'); - + expect(result).toStrictEqual({ modelId: 'claude-3.5-sonnet@20241022', fullIdentifier: 'claude-3.5-sonnet@20241022' @@ -165,7 +169,7 @@ describe('configuration_helpers', () => { it('should not modify or encode special characters', () => { const specialChars = 'model!@#$%^&*()_+-=[]{}|;:\'",.<>?/~`'; const result = configHelpers.parseModelIdentifier(specialChars); - + expect(result).toStrictEqual({ modelId: specialChars, fullIdentifier: specialChars @@ -176,7 +180,7 @@ describe('configuration_helpers', () => { describe('createModelConfig', () => { it('should create model config directly', () => { const result = configHelpers.createModelConfig('gpt-4', 'openai'); - + expect(result).toStrictEqual({ provider: 'openai', modelId: 'gpt-4', @@ -186,7 +190,7 @@ describe('configuration_helpers', () => { it('should handle model with provider prefix', () => { const result = configHelpers.createModelConfig('openai:gpt-4'); - + expect(result).toStrictEqual({ provider: 'openai', modelId: 'gpt-4', @@ -196,7 +200,7 @@ describe('configuration_helpers', () => { it('should fallback to openai provider when none specified', () => { const result = configHelpers.createModelConfig('gpt-4'); - + expect(result).toStrictEqual({ provider: 'openai', modelId: 'gpt-4', @@ -208,27 +212,27 @@ describe('configuration_helpers', () => { describe('getDefaultModelForProvider', () => { it('should return default model for provider', async () => { vi.mocked(optionService.getOption).mockReturnValue('gpt-4'); - + const result = await configHelpers.getDefaultModelForProvider('openai'); - + expect(result).toBe('gpt-4'); expect(optionService.getOption).toHaveBeenCalledWith('openaiDefaultModel'); }); it('should return undefined if no default model', async () => { vi.mocked(optionService.getOption).mockReturnValue(''); - + const result = await configHelpers.getDefaultModelForProvider('anthropic'); - + expect(result).toBeUndefined(); expect(optionService.getOption).toHaveBeenCalledWith('anthropicDefaultModel'); }); it('should handle ollama provider', async () => { vi.mocked(optionService.getOption).mockReturnValue('llama2'); - + const result = await configHelpers.getDefaultModelForProvider('ollama'); - + expect(result).toBe('llama2'); expect(optionService.getOption).toHaveBeenCalledWith('ollamaDefaultModel'); }); @@ -237,27 +241,27 @@ describe('configuration_helpers', () => { it('should handle OpenAI model names with periods', async () => { const modelName = 'gpt-4.1-turbo-preview'; vi.mocked(optionService.getOption).mockReturnValue(modelName); - + const result = await configHelpers.getDefaultModelForProvider('openai'); - + expect(result).toBe(modelName); }); it('should handle Anthropic model names with periods and @ symbols', async () => { const modelName = 'claude-3.5-sonnet@20241022'; vi.mocked(optionService.getOption).mockReturnValue(modelName); - + const result = await configHelpers.getDefaultModelForProvider('anthropic'); - + expect(result).toBe(modelName); }); it('should handle Ollama model names with colons and slashes', async () => { const modelName = 'library/llama3.1:70b-instruct-q4_K_M'; vi.mocked(optionService.getOption).mockReturnValue(modelName); - + const result = await configHelpers.getDefaultModelForProvider('ollama'); - + expect(result).toBe(modelName); }); }); @@ -268,9 +272,9 @@ describe('configuration_helpers', () => { .mockReturnValueOnce('test-key') // openaiApiKey .mockReturnValueOnce('https://api.openai.com') // openaiBaseUrl .mockReturnValueOnce('gpt-4'); // openaiDefaultModel - + const result = await configHelpers.getProviderSettings('openai'); - + expect(result).toStrictEqual({ apiKey: 'test-key', baseUrl: 'https://api.openai.com', @@ -283,9 +287,9 @@ describe('configuration_helpers', () => { .mockReturnValueOnce('anthropic-key') // anthropicApiKey .mockReturnValueOnce('https://api.anthropic.com') // anthropicBaseUrl .mockReturnValueOnce('claude-3'); // anthropicDefaultModel - + const result = await configHelpers.getProviderSettings('anthropic'); - + expect(result).toStrictEqual({ apiKey: 'anthropic-key', baseUrl: 'https://api.anthropic.com', @@ -297,9 +301,9 @@ describe('configuration_helpers', () => { vi.mocked(optionService.getOption) .mockReturnValueOnce('http://localhost:11434') // ollamaBaseUrl .mockReturnValueOnce('llama2'); // ollamaDefaultModel - + const result = await configHelpers.getProviderSettings('ollama'); - + expect(result).toStrictEqual({ baseUrl: 'http://localhost:11434', defaultModel: 'llama2' @@ -308,7 +312,7 @@ describe('configuration_helpers', () => { it('should return empty object for unknown provider', async () => { const result = await configHelpers.getProviderSettings('unknown' as ProviderType); - + expect(result).toStrictEqual({}); }); }); @@ -316,18 +320,18 @@ describe('configuration_helpers', () => { describe('isAIEnabled', () => { it('should return true if AI is enabled', async () => { vi.mocked(optionService.getOptionBool).mockReturnValue(true); - + const result = await configHelpers.isAIEnabled(); - + expect(result).toBe(true); expect(optionService.getOptionBool).toHaveBeenCalledWith('aiEnabled'); }); it('should return false if AI is disabled', async () => { vi.mocked(optionService.getOptionBool).mockReturnValue(false); - + const result = await configHelpers.isAIEnabled(); - + expect(result).toBe(false); expect(optionService.getOptionBool).toHaveBeenCalledWith('aiEnabled'); }); @@ -339,9 +343,9 @@ describe('configuration_helpers', () => { .mockReturnValueOnce('test-key') // openaiApiKey .mockReturnValueOnce('') // openaiBaseUrl .mockReturnValueOnce(''); // openaiDefaultModel - + const result = await configHelpers.isProviderConfigured('openai'); - + expect(result).toBe(true); }); @@ -350,9 +354,9 @@ describe('configuration_helpers', () => { .mockReturnValueOnce('') // openaiApiKey (empty) .mockReturnValueOnce('') // openaiBaseUrl .mockReturnValueOnce(''); // openaiDefaultModel - + const result = await configHelpers.isProviderConfigured('openai'); - + expect(result).toBe(false); }); @@ -361,9 +365,9 @@ describe('configuration_helpers', () => { .mockReturnValueOnce('anthropic-key') // anthropicApiKey .mockReturnValueOnce('') // anthropicBaseUrl .mockReturnValueOnce(''); // anthropicDefaultModel - + const result = await configHelpers.isProviderConfigured('anthropic'); - + expect(result).toBe(true); }); @@ -371,15 +375,15 @@ describe('configuration_helpers', () => { vi.mocked(optionService.getOption) .mockReturnValueOnce('http://localhost:11434') // ollamaBaseUrl .mockReturnValueOnce(''); // ollamaDefaultModel - + const result = await configHelpers.isProviderConfigured('ollama'); - + expect(result).toBe(true); }); it('should return false for unknown provider', async () => { const result = await configHelpers.isProviderConfigured('unknown' as ProviderType); - + expect(result).toBe(false); }); }); @@ -391,17 +395,17 @@ describe('configuration_helpers', () => { .mockReturnValueOnce('test-key') // openaiApiKey .mockReturnValueOnce('') // openaiBaseUrl .mockReturnValueOnce(''); // openaiDefaultModel - + const result = await configHelpers.getAvailableSelectedProvider(); - + expect(result).toBe('openai'); }); it('should return null if no provider selected', async () => { vi.mocked(optionService.getOption).mockReturnValueOnce(''); - + const result = await configHelpers.getAvailableSelectedProvider(); - + expect(result).toBeNull(); }); @@ -411,9 +415,9 @@ describe('configuration_helpers', () => { .mockReturnValueOnce('') // openaiApiKey (empty) .mockReturnValueOnce('') // openaiBaseUrl .mockReturnValueOnce(''); // openaiDefaultModel - + const result = await configHelpers.getAvailableSelectedProvider(); - + expect(result).toBeNull(); }); }); @@ -427,9 +431,9 @@ describe('configuration_helpers', () => { .mockReturnValueOnce('test-key') // openaiApiKey .mockReturnValueOnce('') // openaiBaseUrl .mockReturnValueOnce('gpt-4'); // openaiDefaultModel - + const result = await configHelpers.validateConfiguration(); - + expect(result).toStrictEqual({ isValid: true, errors: [], @@ -439,9 +443,9 @@ describe('configuration_helpers', () => { it('should return warning when AI is disabled', async () => { vi.mocked(optionService.getOptionBool).mockReturnValue(false); - + const result = await configHelpers.validateConfiguration(); - + expect(result).toStrictEqual({ isValid: true, errors: [], @@ -452,9 +456,9 @@ describe('configuration_helpers', () => { it('should return error when no provider selected', async () => { vi.mocked(optionService.getOptionBool).mockReturnValue(true); vi.mocked(optionService.getOption).mockReturnValue(''); // no aiSelectedProvider - + const result = await configHelpers.validateConfiguration(); - + expect(result).toStrictEqual({ isValid: false, errors: ['No AI provider selected'], @@ -469,9 +473,9 @@ describe('configuration_helpers', () => { .mockReturnValueOnce('') // openaiApiKey (empty) .mockReturnValueOnce('') // openaiBaseUrl .mockReturnValueOnce(''); // openaiDefaultModel - + const result = await configHelpers.validateConfiguration(); - + expect(result).toStrictEqual({ isValid: true, errors: [], @@ -495,9 +499,9 @@ describe('configuration_helpers', () => { .mockReturnValueOnce('test-key') // openaiApiKey .mockReturnValueOnce('') // openaiBaseUrl .mockReturnValueOnce(''); // openaiDefaultModel - + const result = await configHelpers.getValidModelConfig('openai'); - + expect(result).toStrictEqual({ model: modelName, provider: 'openai' @@ -511,9 +515,9 @@ describe('configuration_helpers', () => { .mockReturnValueOnce('anthropic-key') // anthropicApiKey .mockReturnValueOnce('') // anthropicBaseUrl .mockReturnValueOnce(''); // anthropicDefaultModel - + const result = await configHelpers.getValidModelConfig('anthropic'); - + expect(result).toStrictEqual({ model: modelName, provider: 'anthropic' @@ -526,9 +530,9 @@ describe('configuration_helpers', () => { .mockReturnValueOnce(modelName) // ollamaDefaultModel .mockReturnValueOnce('http://localhost:11434') // ollamaBaseUrl .mockReturnValueOnce(''); // ollamaDefaultModel - + const result = await configHelpers.getValidModelConfig('ollama'); - + expect(result).toStrictEqual({ model: modelName, provider: 'ollama' @@ -545,9 +549,9 @@ describe('configuration_helpers', () => { .mockReturnValueOnce('test-key') // openaiApiKey .mockReturnValueOnce('') // openaiBaseUrl .mockReturnValueOnce(''); // openaiDefaultModel - + const result = await configHelpers.getSelectedModelConfig(); - + expect(result).toStrictEqual({ model: modelName, provider: 'openai' @@ -562,9 +566,9 @@ describe('configuration_helpers', () => { .mockReturnValueOnce('test-key') // openaiApiKey .mockReturnValueOnce('') // openaiBaseUrl .mockReturnValueOnce(''); // openaiDefaultModel - + const result = await configHelpers.getSelectedModelConfig(); - + expect(result).toStrictEqual({ model: modelName, provider: 'openai' @@ -578,9 +582,9 @@ describe('configuration_helpers', () => { .mockReturnValueOnce(modelName) // ollamaDefaultModel .mockReturnValueOnce('http://localhost:11434') // ollamaBaseUrl .mockReturnValueOnce(''); // ollamaDefaultModel - + const result = await configHelpers.getSelectedModelConfig(); - + expect(result).toStrictEqual({ model: modelName, provider: 'ollama' @@ -595,13 +599,13 @@ describe('configuration_helpers', () => { .mockReturnValueOnce('test-key') // anthropicApiKey .mockReturnValueOnce('') // anthropicBaseUrl .mockReturnValueOnce(''); // anthropicDefaultModel - + const result = await configHelpers.getSelectedModelConfig(); - + expect(result).toStrictEqual({ model: modelName, provider: 'anthropic' }); }); }); -}); \ No newline at end of file +}); diff --git a/apps/server/src/services/llm/providers/anthropic_service.spec.ts b/apps/server/src/services/llm/providers/anthropic_service.spec.ts index 47266fd19..365b529f4 100644 --- a/apps/server/src/services/llm/providers/anthropic_service.spec.ts +++ b/apps/server/src/services/llm/providers/anthropic_service.spec.ts @@ -10,7 +10,11 @@ import { PROVIDER_CONSTANTS } from '../constants/provider_constants.js'; vi.mock('../../options.js', () => ({ default: { getOption: vi.fn(), - getOptionBool: vi.fn() + getOptionBool: vi.fn(), + getOptionInt: vi.fn(name => { + if (name === "protectedSessionTimeout") return Number.MAX_SAFE_INTEGER; + return 0; + }) } })); @@ -79,7 +83,7 @@ describe('AnthropicService', () => { beforeEach(() => { vi.clearAllMocks(); - + // Get the mocked Anthropic instance before creating the service const AnthropicMock = vi.mocked(Anthropic); mockAnthropicInstance = { @@ -122,9 +126,9 @@ describe('AnthropicService', () => { }) } }; - + AnthropicMock.mockImplementation(() => mockAnthropicInstance); - + service = new AnthropicService(); }); @@ -144,26 +148,26 @@ describe('AnthropicService', () => { it('should return true when AI is enabled and API key exists', () => { vi.mocked(options.getOptionBool).mockReturnValueOnce(true); // AI enabled vi.mocked(options.getOption).mockReturnValueOnce('test-api-key'); // API key - + const result = service.isAvailable(); - + expect(result).toBe(true); }); it('should return false when AI is disabled', () => { vi.mocked(options.getOptionBool).mockReturnValueOnce(false); // AI disabled - + const result = service.isAvailable(); - + expect(result).toBe(false); }); it('should return false when no API key', () => { vi.mocked(options.getOptionBool).mockReturnValueOnce(true); // AI enabled vi.mocked(options.getOption).mockReturnValueOnce(''); // No API key - + const result = service.isAvailable(); - + expect(result).toBe(false); }); }); @@ -190,9 +194,9 @@ describe('AnthropicService', () => { stream: false }; vi.mocked(providers.getAnthropicOptions).mockReturnValueOnce(mockOptions); - + const result = await service.generateChatCompletion(messages); - + expect(result).toEqual({ text: 'Hello! How can I help you today?', provider: 'Anthropic', @@ -214,11 +218,11 @@ describe('AnthropicService', () => { stream: false }; vi.mocked(providers.getAnthropicOptions).mockReturnValueOnce(mockOptions); - + const createSpy = vi.spyOn(mockAnthropicInstance.messages, 'create'); - + await service.generateChatCompletion(messages); - + const calledParams = createSpy.mock.calls[0][0] as any; expect(calledParams.messages).toEqual([ { role: 'user', content: 'Hello' } @@ -235,12 +239,12 @@ describe('AnthropicService', () => { onChunk: vi.fn() }; vi.mocked(providers.getAnthropicOptions).mockReturnValueOnce(mockOptions); - + const result = await service.generateChatCompletion(messages); - + // Wait for chunks to be processed await new Promise(resolve => setTimeout(resolve, 100)); - + // Check that the result exists (streaming logic is complex, so we just verify basic structure) expect(result).toBeDefined(); expect(result).toHaveProperty('text'); @@ -256,7 +260,7 @@ describe('AnthropicService', () => { properties: {} } }]; - + const mockOptions = { apiKey: 'test-key', baseUrl: 'https://api.anthropic.com', @@ -267,7 +271,7 @@ describe('AnthropicService', () => { tool_choice: { type: 'any' } }; vi.mocked(providers.getAnthropicOptions).mockReturnValueOnce(mockOptions); - + // Mock response with tool use mockAnthropicInstance.messages.create.mockResolvedValueOnce({ id: 'msg_123', @@ -287,9 +291,9 @@ describe('AnthropicService', () => { output_tokens: 25 } }); - + const result = await service.generateChatCompletion(messages); - + expect(result).toEqual({ text: '', provider: 'Anthropic', @@ -312,7 +316,7 @@ describe('AnthropicService', () => { it('should throw error if service not available', async () => { vi.mocked(options.getOptionBool).mockReturnValueOnce(false); // AI disabled - + await expect(service.generateChatCompletion(messages)).rejects.toThrow( 'Anthropic service is not available' ); @@ -326,12 +330,12 @@ describe('AnthropicService', () => { stream: false }; vi.mocked(providers.getAnthropicOptions).mockReturnValueOnce(mockOptions); - + // Mock API error mockAnthropicInstance.messages.create.mockRejectedValueOnce( new Error('API Error: Invalid API key') ); - + await expect(service.generateChatCompletion(messages)).rejects.toThrow( 'API Error: Invalid API key' ); @@ -347,15 +351,15 @@ describe('AnthropicService', () => { stream: false }; vi.mocked(providers.getAnthropicOptions).mockReturnValueOnce(mockOptions); - + // Spy on Anthropic constructor const AnthropicMock = vi.mocked(Anthropic); AnthropicMock.mockClear(); - + // Create new service to trigger client creation const newService = new AnthropicService(); await newService.generateChatCompletion(messages); - + expect(AnthropicMock).toHaveBeenCalledWith({ apiKey: 'test-key', baseURL: 'https://api.anthropic.com', @@ -374,15 +378,15 @@ describe('AnthropicService', () => { stream: false }; vi.mocked(providers.getAnthropicOptions).mockReturnValueOnce(mockOptions); - + // Spy on Anthropic constructor const AnthropicMock = vi.mocked(Anthropic); AnthropicMock.mockClear(); - + // Create new service to trigger client creation const newService = new AnthropicService(); await newService.generateChatCompletion(messages); - + expect(AnthropicMock).toHaveBeenCalledWith({ apiKey: 'test-key', baseURL: 'https://api.anthropic.com', @@ -401,7 +405,7 @@ describe('AnthropicService', () => { stream: false }; vi.mocked(providers.getAnthropicOptions).mockReturnValueOnce(mockOptions); - + // Mock response with mixed content mockAnthropicInstance.messages.create.mockResolvedValueOnce({ id: 'msg_123', @@ -420,9 +424,9 @@ describe('AnthropicService', () => { output_tokens: 25 } }); - + const result = await service.generateChatCompletion(messages); - + expect(result.text).toBe('Here is the result: The calculation is complete.'); expect(result.tool_calls).toHaveLength(1); expect(result.tool_calls![0].function.name).toBe('calculate'); @@ -431,8 +435,8 @@ describe('AnthropicService', () => { it('should handle tool results in messages', async () => { const messagesWithToolResult: Message[] = [ { role: 'user', content: 'Calculate 5 + 3' }, - { - role: 'assistant', + { + role: 'assistant', content: '', tool_calls: [{ id: 'call_123', @@ -440,13 +444,13 @@ describe('AnthropicService', () => { function: { name: 'calculate', arguments: '{"x": 5, "y": 3}' } }] }, - { - role: 'tool', + { + role: 'tool', content: '8', tool_call_id: 'call_123' } ]; - + const mockOptions = { apiKey: 'test-key', baseUrl: 'https://api.anthropic.com', @@ -454,11 +458,11 @@ describe('AnthropicService', () => { stream: false }; vi.mocked(providers.getAnthropicOptions).mockReturnValueOnce(mockOptions); - + const createSpy = vi.spyOn(mockAnthropicInstance.messages, 'create'); - + await service.generateChatCompletion(messagesWithToolResult); - + const formattedMessages = (createSpy.mock.calls[0][0] as any).messages; expect(formattedMessages).toHaveLength(3); expect(formattedMessages[2]).toEqual({ @@ -471,4 +475,4 @@ describe('AnthropicService', () => { }); }); }); -}); \ No newline at end of file +}); diff --git a/apps/server/src/services/llm/providers/model_selection.spec.ts b/apps/server/src/services/llm/providers/model_selection.spec.ts index c6fdfae1e..916dcd92b 100644 --- a/apps/server/src/services/llm/providers/model_selection.spec.ts +++ b/apps/server/src/services/llm/providers/model_selection.spec.ts @@ -10,7 +10,11 @@ import options from '../../options.js'; vi.mock('../../options.js', () => ({ default: { getOption: vi.fn(), - getOptionBool: vi.fn() + getOptionBool: vi.fn(), + getOptionInt: vi.fn(name => { + if (name === "protectedSessionTimeout") return Number.MAX_SAFE_INTEGER; + return 0; + }) } })); @@ -82,7 +86,7 @@ describe('LLM Model Selection with Special Characters', () => { // Spy on getOpenAIOptions to verify model name is passed correctly const getOpenAIOptionsSpy = vi.spyOn(providers, 'getOpenAIOptions'); - + try { await service.generateChatCompletion([{ role: 'user', content: 'test' }], opts); } catch (error) { @@ -108,7 +112,7 @@ describe('LLM Model Selection with Special Characters', () => { }; const getOpenAIOptionsSpy = vi.spyOn(providers, 'getOpenAIOptions'); - + try { await service.generateChatCompletion([{ role: 'user', content: 'test' }], opts); } catch (error) { @@ -127,7 +131,7 @@ describe('LLM Model Selection with Special Characters', () => { }; const getOpenAIOptionsSpy = vi.spyOn(providers, 'getOpenAIOptions'); - + const openaiOptions = providers.getOpenAIOptions(opts); expect(openaiOptions.model).toBe(modelName); }); @@ -153,7 +157,7 @@ describe('LLM Model Selection with Special Characters', () => { }); const service = new OpenAIService(); - + // Access the private openai client through the service const client = (service as any).getClient('test-key'); const createSpy = vi.spyOn(client.chat.completions, 'create'); @@ -213,7 +217,7 @@ describe('LLM Model Selection with Special Characters', () => { }); const service = new AnthropicService(); - + // Access the private anthropic client const client = (service as any).getClient('test-key'); const createSpy = vi.spyOn(client.messages, 'create'); @@ -278,7 +282,7 @@ describe('LLM Model Selection with Special Characters', () => { const ollamaOptions = await providers.getOllamaOptions(opts); expect(ollamaOptions.model).toBe(modelName); - + // Also test with model specified in options const optsWithModel: ChatCompletionOptions = { model: 'another/model:v2.0@beta', @@ -370,7 +374,7 @@ describe('LLM Model Selection with Special Characters', () => { describe('Integration with REST API', () => { it('should pass model names correctly through REST chat service', async () => { const modelName = 'gpt-4.1-turbo-preview@latest'; - + // Mock the configuration helpers vi.doMock('../config/configuration_helpers.js', () => ({ getSelectedModelConfig: vi.fn().mockResolvedValue({ @@ -382,8 +386,8 @@ describe('LLM Model Selection with Special Characters', () => { const { getSelectedModelConfig } = await import('../config/configuration_helpers.js'); const config = await getSelectedModelConfig(); - + expect(config?.model).toBe(modelName); }); }); -}); \ No newline at end of file +}); diff --git a/apps/server/src/services/llm/providers/ollama_service.spec.ts b/apps/server/src/services/llm/providers/ollama_service.spec.ts index 5d03137fb..e2bee52d2 100644 --- a/apps/server/src/services/llm/providers/ollama_service.spec.ts +++ b/apps/server/src/services/llm/providers/ollama_service.spec.ts @@ -9,7 +9,11 @@ import { Ollama } from 'ollama'; vi.mock('../../options.js', () => ({ default: { getOption: vi.fn(), - getOptionBool: vi.fn() + getOptionBool: vi.fn(), + getOptionInt: vi.fn(name => { + if (name === "protectedSessionTimeout") return Number.MAX_SAFE_INTEGER; + return 0; + }) } })); @@ -134,7 +138,7 @@ describe('OllamaService', () => { beforeEach(() => { vi.clearAllMocks(); - + // Create the mock instance before creating the service const OllamaMock = vi.mocked(Ollama); mockOllamaInstance = { @@ -191,11 +195,11 @@ describe('OllamaService', () => { ] }) }; - + OllamaMock.mockImplementation(() => mockOllamaInstance); - + service = new OllamaService(); - + // Replace the formatter with a mock after construction (service as any).formatter = { formatMessages: vi.fn().mockReturnValue([ @@ -231,26 +235,26 @@ describe('OllamaService', () => { it('should return true when AI is enabled and base URL exists', () => { vi.mocked(options.getOptionBool).mockReturnValueOnce(true); // AI enabled vi.mocked(options.getOption).mockReturnValueOnce('http://localhost:11434'); // Base URL - + const result = service.isAvailable(); - + expect(result).toBe(true); }); it('should return false when AI is disabled', () => { vi.mocked(options.getOptionBool).mockReturnValueOnce(false); // AI disabled - + const result = service.isAvailable(); - + expect(result).toBe(false); }); it('should return false when no base URL', () => { vi.mocked(options.getOptionBool).mockReturnValueOnce(true); // AI enabled vi.mocked(options.getOption).mockReturnValueOnce(''); // No base URL - + const result = service.isAvailable(); - + expect(result).toBe(false); }); }); @@ -275,9 +279,9 @@ describe('OllamaService', () => { }; vi.mocked(providers.getOllamaOptions).mockResolvedValueOnce(mockOptions); vi.mocked(options.getOption).mockReturnValue('http://localhost:11434'); - + const result = await service.generateChatCompletion(messages); - + expect(result).toEqual({ text: 'Hello! How can I help you today?', provider: 'ollama', @@ -296,12 +300,12 @@ describe('OllamaService', () => { }; vi.mocked(providers.getOllamaOptions).mockResolvedValueOnce(mockOptions); vi.mocked(options.getOption).mockReturnValue('http://localhost:11434'); - + const result = await service.generateChatCompletion(messages); - + // Wait for chunks to be processed await new Promise(resolve => setTimeout(resolve, 100)); - + // For streaming, we expect a different response structure expect(result).toBeDefined(); expect(result).toHaveProperty('text'); @@ -310,7 +314,7 @@ describe('OllamaService', () => { it('should handle tools when enabled', async () => { vi.mocked(options.getOption).mockReturnValue('http://localhost:11434'); - + const mockTools = [{ name: 'test_tool', description: 'Test tool', @@ -320,7 +324,7 @@ describe('OllamaService', () => { required: [] } }]; - + const mockOptions = { baseUrl: 'http://localhost:11434', model: 'llama2', @@ -329,18 +333,18 @@ describe('OllamaService', () => { tools: mockTools }; vi.mocked(providers.getOllamaOptions).mockResolvedValueOnce(mockOptions); - + const chatSpy = vi.spyOn(mockOllamaInstance, 'chat'); - + await service.generateChatCompletion(messages); - + const calledParams = chatSpy.mock.calls[0][0] as any; expect(calledParams.tools).toEqual(mockTools); }); it('should throw error if service not available', async () => { vi.mocked(options.getOptionBool).mockReturnValueOnce(false); // AI disabled - + await expect(service.generateChatCompletion(messages)).rejects.toThrow( 'Ollama service is not available' ); @@ -350,14 +354,14 @@ describe('OllamaService', () => { vi.mocked(options.getOption) .mockReturnValueOnce('') // Empty base URL for ollamaBaseUrl .mockReturnValue(''); // Ensure all subsequent calls return empty - + const mockOptions = { baseUrl: '', model: 'llama2', stream: false }; vi.mocked(providers.getOllamaOptions).mockResolvedValueOnce(mockOptions); - + await expect(service.generateChatCompletion(messages)).rejects.toThrow( 'Ollama service is not available' ); @@ -365,19 +369,19 @@ describe('OllamaService', () => { it('should handle API errors', async () => { vi.mocked(options.getOption).mockReturnValue('http://localhost:11434'); - + const mockOptions = { baseUrl: 'http://localhost:11434', model: 'llama2', stream: false }; vi.mocked(providers.getOllamaOptions).mockResolvedValueOnce(mockOptions); - + // Mock API error mockOllamaInstance.chat.mockRejectedValueOnce( new Error('Connection refused') ); - + await expect(service.generateChatCompletion(messages)).rejects.toThrow( 'Connection refused' ); @@ -385,30 +389,30 @@ describe('OllamaService', () => { it('should create client with custom fetch for debugging', async () => { vi.mocked(options.getOption).mockReturnValue('http://localhost:11434'); - + const mockOptions = { baseUrl: 'http://localhost:11434', model: 'llama2', stream: false }; vi.mocked(providers.getOllamaOptions).mockResolvedValueOnce(mockOptions); - + // Spy on Ollama constructor const OllamaMock = vi.mocked(Ollama); OllamaMock.mockClear(); - + // Create new service to trigger client creation const newService = new OllamaService(); - + // Replace the formatter with a mock for the new service (newService as any).formatter = { formatMessages: vi.fn().mockReturnValue([ { role: 'user', content: 'Hello' } ]) }; - + await newService.generateChatCompletion(messages); - + expect(OllamaMock).toHaveBeenCalledWith({ host: 'http://localhost:11434', fetch: expect.any(Function) @@ -417,7 +421,7 @@ describe('OllamaService', () => { it('should handle tool execution feedback', async () => { vi.mocked(options.getOption).mockReturnValue('http://localhost:11434'); - + const mockOptions = { baseUrl: 'http://localhost:11434', model: 'llama2', @@ -426,7 +430,7 @@ describe('OllamaService', () => { tools: [{ name: 'test_tool', description: 'Test', parameters: {} }] }; vi.mocked(providers.getOllamaOptions).mockResolvedValueOnce(mockOptions); - + // Mock response with tool call (arguments should be a string for Ollama) mockOllamaInstance.chat.mockResolvedValueOnce({ message: { @@ -442,9 +446,9 @@ describe('OllamaService', () => { }, done: true }); - + const result = await service.generateChatCompletion(messages); - + expect(result.tool_calls).toEqual([{ id: 'call_123', type: 'function', @@ -457,14 +461,14 @@ describe('OllamaService', () => { it('should handle mixed text and tool content', async () => { vi.mocked(options.getOption).mockReturnValue('http://localhost:11434'); - + const mockOptions = { baseUrl: 'http://localhost:11434', model: 'llama2', stream: false }; vi.mocked(providers.getOllamaOptions).mockResolvedValueOnce(mockOptions); - + // Mock response with both text and tool calls mockOllamaInstance.chat.mockResolvedValueOnce({ message: { @@ -480,30 +484,30 @@ describe('OllamaService', () => { }, done: true }); - + const result = await service.generateChatCompletion(messages); - + expect(result.text).toBe('Let me help you with that.'); expect(result.tool_calls).toHaveLength(1); }); it('should format messages using the formatter', async () => { vi.mocked(options.getOption).mockReturnValue('http://localhost:11434'); - + const mockOptions = { baseUrl: 'http://localhost:11434', model: 'llama2', stream: false }; vi.mocked(providers.getOllamaOptions).mockResolvedValueOnce(mockOptions); - + const formattedMessages = [{ role: 'user', content: 'Hello' }]; (service as any).formatter.formatMessages.mockReturnValueOnce(formattedMessages); - + const chatSpy = vi.spyOn(mockOllamaInstance, 'chat'); - + await service.generateChatCompletion(messages); - + expect((service as any).formatter.formatMessages).toHaveBeenCalled(); expect(chatSpy).toHaveBeenCalledWith( expect.objectContaining({ @@ -514,23 +518,23 @@ describe('OllamaService', () => { it('should handle network errors gracefully', async () => { vi.mocked(options.getOption).mockReturnValue('http://localhost:11434'); - + const mockOptions = { baseUrl: 'http://localhost:11434', model: 'llama2', stream: false }; vi.mocked(providers.getOllamaOptions).mockResolvedValueOnce(mockOptions); - + // Mock network error global.fetch = vi.fn().mockRejectedValueOnce( new Error('Network error') ); - + mockOllamaInstance.chat.mockRejectedValueOnce( new Error('fetch failed') ); - + await expect(service.generateChatCompletion(messages)).rejects.toThrow( 'fetch failed' ); @@ -538,19 +542,19 @@ describe('OllamaService', () => { it('should validate model availability', async () => { vi.mocked(options.getOption).mockReturnValue('http://localhost:11434'); - + const mockOptions = { baseUrl: 'http://localhost:11434', model: 'nonexistent-model', stream: false }; vi.mocked(providers.getOllamaOptions).mockResolvedValueOnce(mockOptions); - + // Mock model not found error mockOllamaInstance.chat.mockRejectedValueOnce( new Error('model "nonexistent-model" not found') ); - + await expect(service.generateChatCompletion(messages)).rejects.toThrow( 'model "nonexistent-model" not found' ); @@ -561,23 +565,23 @@ describe('OllamaService', () => { it('should reuse existing client', async () => { vi.mocked(options.getOptionBool).mockReturnValue(true); vi.mocked(options.getOption).mockReturnValue('http://localhost:11434'); - + const mockOptions = { baseUrl: 'http://localhost:11434', model: 'llama2', stream: false }; vi.mocked(providers.getOllamaOptions).mockResolvedValue(mockOptions); - + const OllamaMock = vi.mocked(Ollama); OllamaMock.mockClear(); - + // Make two calls await service.generateChatCompletion([{ role: 'user', content: 'Hello' }]); await service.generateChatCompletion([{ role: 'user', content: 'Hi' }]); - + // Should only create client once expect(OllamaMock).toHaveBeenCalledTimes(1); }); }); -}); \ No newline at end of file +}); diff --git a/apps/server/src/services/llm/providers/openai_service.spec.ts b/apps/server/src/services/llm/providers/openai_service.spec.ts index 39544fabc..8adb03d84 100644 --- a/apps/server/src/services/llm/providers/openai_service.spec.ts +++ b/apps/server/src/services/llm/providers/openai_service.spec.ts @@ -2,13 +2,17 @@ import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'; import { OpenAIService } from './openai_service.js'; import options from '../../options.js'; import * as providers from './providers.js'; -import type { ChatCompletionOptions, Message } from '../ai_interface.js'; +import type { Message } from '../ai_interface.js'; // Mock dependencies vi.mock('../../options.js', () => ({ default: { getOption: vi.fn(), - getOptionBool: vi.fn() + getOptionBool: vi.fn(), + getOptionInt: vi.fn(name => { + if (name === "protectedSessionTimeout") return Number.MAX_SAFE_INTEGER; + return 0; + }) } })); @@ -53,17 +57,17 @@ describe('OpenAIService', () => { describe('isAvailable', () => { it('should return true when base checks pass', () => { vi.mocked(options.getOptionBool).mockReturnValueOnce(true); // AI enabled - + const result = service.isAvailable(); - + expect(result).toBe(true); }); it('should return false when AI is disabled', () => { vi.mocked(options.getOptionBool).mockReturnValueOnce(false); // AI disabled - + const result = service.isAvailable(); - + expect(result).toBe(false); }); }); @@ -89,7 +93,7 @@ describe('OpenAIService', () => { enableTools: false }; vi.mocked(providers.getOpenAIOptions).mockReturnValueOnce(mockOptions); - + // Mock the getClient method to return our mock client const mockCompletion = { id: 'chatcmpl-123', @@ -120,9 +124,9 @@ describe('OpenAIService', () => { }; vi.spyOn(service as any, 'getClient').mockReturnValue(mockClient); - + const result = await service.generateChatCompletion(messages); - + expect(result).toEqual({ text: 'Hello! How can I help you today?', model: 'gpt-3.5-turbo', @@ -144,7 +148,7 @@ describe('OpenAIService', () => { stream: true }; vi.mocked(providers.getOpenAIOptions).mockReturnValueOnce(mockOptions); - + // Mock the streaming response const mockStream = { [Symbol.asyncIterator]: async function* () { @@ -162,7 +166,7 @@ describe('OpenAIService', () => { }; } }; - + const mockClient = { chat: { completions: { @@ -172,9 +176,9 @@ describe('OpenAIService', () => { }; vi.spyOn(service as any, 'getClient').mockReturnValue(mockClient); - + const result = await service.generateChatCompletion(messages); - + expect(result).toHaveProperty('stream'); expect(result.text).toBe(''); expect(result.model).toBe('gpt-3.5-turbo'); @@ -183,7 +187,7 @@ describe('OpenAIService', () => { it('should throw error if service not available', async () => { vi.mocked(options.getOptionBool).mockReturnValueOnce(false); // AI disabled - + await expect(service.generateChatCompletion(messages)).rejects.toThrow( 'OpenAI service is not available' ); @@ -197,7 +201,7 @@ describe('OpenAIService', () => { stream: false }; vi.mocked(providers.getOpenAIOptions).mockReturnValueOnce(mockOptions); - + const mockClient = { chat: { completions: { @@ -207,7 +211,7 @@ describe('OpenAIService', () => { }; vi.spyOn(service as any, 'getClient').mockReturnValue(mockClient); - + await expect(service.generateChatCompletion(messages)).rejects.toThrow( 'API Error: Invalid API key' ); @@ -222,7 +226,7 @@ describe('OpenAIService', () => { parameters: {} } }]; - + const mockOptions = { apiKey: 'test-key', baseUrl: 'https://api.openai.com/v1', @@ -233,7 +237,7 @@ describe('OpenAIService', () => { tool_choice: 'auto' }; vi.mocked(providers.getOpenAIOptions).mockReturnValueOnce(mockOptions); - + const mockCompletion = { id: 'chatcmpl-123', object: 'chat.completion', @@ -263,9 +267,9 @@ describe('OpenAIService', () => { }; vi.spyOn(service as any, 'getClient').mockReturnValue(mockClient); - + await service.generateChatCompletion(messages); - + const createCall = mockClient.chat.completions.create.mock.calls[0][0]; expect(createCall.tools).toEqual(mockTools); expect(createCall.tool_choice).toBe('auto'); @@ -281,7 +285,7 @@ describe('OpenAIService', () => { tools: [{ type: 'function' as const, function: { name: 'test', description: 'test' } }] }; vi.mocked(providers.getOpenAIOptions).mockReturnValueOnce(mockOptions); - + const mockCompletion = { id: 'chatcmpl-123', object: 'chat.completion', @@ -319,9 +323,9 @@ describe('OpenAIService', () => { }; vi.spyOn(service as any, 'getClient').mockReturnValue(mockClient); - + const result = await service.generateChatCompletion(messages); - + expect(result).toEqual({ text: '', model: 'gpt-3.5-turbo', @@ -342,4 +346,4 @@ describe('OpenAIService', () => { }); }); }); -}); \ No newline at end of file +}); diff --git a/apps/server/src/services/protected_session.ts b/apps/server/src/services/protected_session.ts index 57a163e18..d69d318d1 100644 --- a/apps/server/src/services/protected_session.ts +++ b/apps/server/src/services/protected_session.ts @@ -1,9 +1,6 @@ "use strict"; -import log from "./log.js"; import dataEncryptionService from "./encryption/data_encryption.js"; -import options from "./options.js"; -import ws from "./ws.js"; let dataKey: Buffer | null = null; @@ -15,11 +12,11 @@ function getDataKey() { return dataKey; } -function resetDataKey() { +export function resetDataKey() { dataKey = null; } -function isProtectedSessionAvailable() { +export function isProtectedSessionAvailable() { return !!dataKey; } @@ -57,15 +54,8 @@ function touchProtectedSession() { } } -function checkProtectedSessionExpiration() { - const protectedSessionTimeout = options.getOptionInt("protectedSessionTimeout"); - if (isProtectedSessionAvailable() && lastProtectedSessionOperationDate && Date.now() - lastProtectedSessionOperationDate > protectedSessionTimeout * 1000) { - resetDataKey(); - - log.info("Expiring protected session"); - - ws.reloadFrontend("leaving protected session"); - } +export function getLastProtectedSessionOperationDate() { + return lastProtectedSessionOperationDate; } export default { @@ -75,6 +65,5 @@ export default { encrypt, decrypt, decryptString, - touchProtectedSession, - checkProtectedSessionExpiration + touchProtectedSession }; diff --git a/apps/server/src/services/scheduler.ts b/apps/server/src/services/scheduler.ts index 803507442..e58ef07b4 100644 --- a/apps/server/src/services/scheduler.ts +++ b/apps/server/src/services/scheduler.ts @@ -4,9 +4,11 @@ import sqlInit from "./sql_init.js"; import config from "./config.js"; import log from "./log.js"; import attributeService from "../services/attributes.js"; -import protectedSessionService from "../services/protected_session.js"; import hiddenSubtreeService from "./hidden_subtree.js"; import type BNote from "../becca/entities/bnote.js"; +import options from "./options.js"; +import { getLastProtectedSessionOperationDate, isProtectedSessionAvailable, resetDataKey } from "./protected_session.js"; +import ws from "./ws.js"; function getRunAtHours(note: BNote): number[] { try { @@ -64,5 +66,15 @@ sqlInit.dbReady.then(() => { ); } - setInterval(() => protectedSessionService.checkProtectedSessionExpiration(), 30000); + setInterval(() => checkProtectedSessionExpiration(), 1); }); + +function checkProtectedSessionExpiration() { + const protectedSessionTimeout = options.getOptionInt("protectedSessionTimeout"); + const lastProtectedSessionOperationDate = getLastProtectedSessionOperationDate(); + if (isProtectedSessionAvailable() && lastProtectedSessionOperationDate && Date.now() - lastProtectedSessionOperationDate > protectedSessionTimeout * 1000) { + resetDataKey(); + log.info("Expiring protected session"); + ws.reloadFrontend("leaving protected session"); + } +} diff --git a/docs/README-ZH_CN.md b/docs/README-ZH_CN.md index 2d1099d10..2efbd4f83 100644 --- a/docs/README-ZH_CN.md +++ b/docs/README-ZH_CN.md @@ -211,7 +211,8 @@ described in the "Discuss with us" section above. * [zadam](https://github.com/zadam) for the original concept and implementation of the application. -* [Larsa](https://github.com/LarsaSara) for designing the application icon. +* [Sarah Hussein](https://github.com/Sarah-Hussein) for designing the + application icon. * [nriver](https://github.com/nriver) for his work on internationalization. * [Thomas Frei](https://github.com/thfrei) for his original work on the Canvas. * [antoniotejada](https://github.com/nriver) for the original syntax highlight diff --git a/docs/README-ZH_TW.md b/docs/README-ZH_TW.md index 6b85e8396..7fa895a16 100644 --- a/docs/README-ZH_TW.md +++ b/docs/README-ZH_TW.md @@ -202,7 +202,8 @@ pnpm run --filter desktop electron-forge:make --arch=x64 --platform=win32 * [zadam](https://github.com/zadam) for the original concept and implementation of the application. -* [Larsa](https://github.com/LarsaSara) for designing the application icon. +* [Sarah Hussein](https://github.com/Sarah-Hussein) for designing the + application icon. * [nriver](https://github.com/nriver) for his work on internationalization. * [Thomas Frei](https://github.com/thfrei) for his original work on the Canvas. * [antoniotejada](https://github.com/nriver) for the original syntax highlight diff --git a/docs/README-ar.md b/docs/README-ar.md index 8371f2ddd..cc18bd0ce 100644 --- a/docs/README-ar.md +++ b/docs/README-ar.md @@ -135,7 +135,7 @@ compatible with the latest zadam/trilium version of versions of TriliumNext/Trilium have their sync versions incremented which prevents direct migration. -## تحدث معنا +## 💬تحدث معنا Feel free to join our official conversations. We would love to hear what features, suggestions, or issues you may have! @@ -157,7 +157,7 @@ Download the binary release for your platform from the [latest release page](https://github.com/TriliumNext/Trilium/releases/latest), unzip the package and run the `trilium` executable. -### Linux +### لينكس If your distribution is listed in the table below, use your distribution's package. @@ -179,7 +179,7 @@ interface (which is almost identical to the desktop app). Currently only the latest versions of Chrome & Firefox are supported (and tested). -### Mobile +### هاتف المحمول To use TriliumNext on a mobile device, you can use a mobile web browser to access the mobile interface of a server installation (see below). @@ -194,7 +194,7 @@ repository](https://github.com/FliegendeWurst/TriliumDroid). Note: It is best to disable automatic updates on your server installation (see below) when using TriliumDroid since the sync version must match between Trilium and TriliumDroid. -### Server +### الخادم To install TriliumNext on your own server (including via Docker from [Dockerhub](https://hub.docker.com/r/triliumnext/trilium)) follow [the server @@ -203,7 +203,7 @@ installation docs](https://triliumnext.github.io/Docs/Wiki/server-installation). ## 💻 Contribute -### Translations +### ترجمات If you are a native speaker, help us translate Trilium by heading over to our [Weblate page](https://hosted.weblate.org/engage/trilium/). @@ -213,7 +213,7 @@ Here's the language coverage we have so far: [![Translation status](https://hosted.weblate.org/widget/trilium/multi-auto.svg)](https://hosted.weblate.org/engage/trilium/) -### Code +### كود Download the repository, install dependencies using `pnpm` and then run the server (available at http://localhost:8080): @@ -224,7 +224,7 @@ pnpm install pnpm run server:start ``` -### Documentation +### التوثيق Download the repository, install dependencies using `pnpm` and then run the environment required to edit the documentation: @@ -259,7 +259,8 @@ described in the "Discuss with us" section above. * [zadam](https://github.com/zadam) for the original concept and implementation of the application. -* [Larsa](https://github.com/LarsaSara) for designing the application icon. +* [Sarah Hussein](https://github.com/Sarah-Hussein) for designing the + application icon. * [nriver](https://github.com/nriver) for his work on internationalization. * [Thomas Frei](https://github.com/thfrei) for his original work on the Canvas. * [antoniotejada](https://github.com/nriver) for the original syntax highlight diff --git a/docs/README-ca.md b/docs/README-ca.md index 4e8740808..348eb9d3e 100644 --- a/docs/README-ca.md +++ b/docs/README-ca.md @@ -259,7 +259,8 @@ described in the "Discuss with us" section above. * [zadam](https://github.com/zadam) for the original concept and implementation of the application. -* [Larsa](https://github.com/LarsaSara) for designing the application icon. +* [Sarah Hussein](https://github.com/Sarah-Hussein) for designing the + application icon. * [nriver](https://github.com/nriver) for his work on internationalization. * [Thomas Frei](https://github.com/thfrei) for his original work on the Canvas. * [antoniotejada](https://github.com/nriver) for the original syntax highlight diff --git a/docs/README-cs.md b/docs/README-cs.md index 4e8740808..348eb9d3e 100644 --- a/docs/README-cs.md +++ b/docs/README-cs.md @@ -259,7 +259,8 @@ described in the "Discuss with us" section above. * [zadam](https://github.com/zadam) for the original concept and implementation of the application. -* [Larsa](https://github.com/LarsaSara) for designing the application icon. +* [Sarah Hussein](https://github.com/Sarah-Hussein) for designing the + application icon. * [nriver](https://github.com/nriver) for his work on internationalization. * [Thomas Frei](https://github.com/thfrei) for his original work on the Canvas. * [antoniotejada](https://github.com/nriver) for the original syntax highlight diff --git a/docs/README-de.md b/docs/README-de.md index 55baa915c..459469fe9 100644 --- a/docs/README-de.md +++ b/docs/README-de.md @@ -259,7 +259,8 @@ described in the "Discuss with us" section above. * [zadam](https://github.com/zadam) for the original concept and implementation of the application. -* [Larsa](https://github.com/LarsaSara) for designing the application icon. +* [Sarah Hussein](https://github.com/Sarah-Hussein) for designing the + application icon. * [nriver](https://github.com/nriver) for his work on internationalization. * [Thomas Frei](https://github.com/thfrei) for his original work on the Canvas. * [antoniotejada](https://github.com/nriver) for the original syntax highlight diff --git a/docs/README-el.md b/docs/README-el.md index 4e8740808..348eb9d3e 100644 --- a/docs/README-el.md +++ b/docs/README-el.md @@ -259,7 +259,8 @@ described in the "Discuss with us" section above. * [zadam](https://github.com/zadam) for the original concept and implementation of the application. -* [Larsa](https://github.com/LarsaSara) for designing the application icon. +* [Sarah Hussein](https://github.com/Sarah-Hussein) for designing the + application icon. * [nriver](https://github.com/nriver) for his work on internationalization. * [Thomas Frei](https://github.com/thfrei) for his original work on the Canvas. * [antoniotejada](https://github.com/nriver) for the original syntax highlight diff --git a/docs/README-es.md b/docs/README-es.md index 4e8740808..348eb9d3e 100644 --- a/docs/README-es.md +++ b/docs/README-es.md @@ -259,7 +259,8 @@ described in the "Discuss with us" section above. * [zadam](https://github.com/zadam) for the original concept and implementation of the application. -* [Larsa](https://github.com/LarsaSara) for designing the application icon. +* [Sarah Hussein](https://github.com/Sarah-Hussein) for designing the + application icon. * [nriver](https://github.com/nriver) for his work on internationalization. * [Thomas Frei](https://github.com/thfrei) for his original work on the Canvas. * [antoniotejada](https://github.com/nriver) for the original syntax highlight diff --git a/docs/README-fa.md b/docs/README-fa.md index 4e8740808..348eb9d3e 100644 --- a/docs/README-fa.md +++ b/docs/README-fa.md @@ -259,7 +259,8 @@ described in the "Discuss with us" section above. * [zadam](https://github.com/zadam) for the original concept and implementation of the application. -* [Larsa](https://github.com/LarsaSara) for designing the application icon. +* [Sarah Hussein](https://github.com/Sarah-Hussein) for designing the + application icon. * [nriver](https://github.com/nriver) for his work on internationalization. * [Thomas Frei](https://github.com/thfrei) for his original work on the Canvas. * [antoniotejada](https://github.com/nriver) for the original syntax highlight diff --git a/docs/README-fi.md b/docs/README-fi.md index 4e8740808..348eb9d3e 100644 --- a/docs/README-fi.md +++ b/docs/README-fi.md @@ -259,7 +259,8 @@ described in the "Discuss with us" section above. * [zadam](https://github.com/zadam) for the original concept and implementation of the application. -* [Larsa](https://github.com/LarsaSara) for designing the application icon. +* [Sarah Hussein](https://github.com/Sarah-Hussein) for designing the + application icon. * [nriver](https://github.com/nriver) for his work on internationalization. * [Thomas Frei](https://github.com/thfrei) for his original work on the Canvas. * [antoniotejada](https://github.com/nriver) for the original syntax highlight diff --git a/docs/README-fr.md b/docs/README-fr.md index 4e8740808..348eb9d3e 100644 --- a/docs/README-fr.md +++ b/docs/README-fr.md @@ -259,7 +259,8 @@ described in the "Discuss with us" section above. * [zadam](https://github.com/zadam) for the original concept and implementation of the application. -* [Larsa](https://github.com/LarsaSara) for designing the application icon. +* [Sarah Hussein](https://github.com/Sarah-Hussein) for designing the + application icon. * [nriver](https://github.com/nriver) for his work on internationalization. * [Thomas Frei](https://github.com/thfrei) for his original work on the Canvas. * [antoniotejada](https://github.com/nriver) for the original syntax highlight diff --git a/docs/README-hr.md b/docs/README-hr.md index 4e8740808..348eb9d3e 100644 --- a/docs/README-hr.md +++ b/docs/README-hr.md @@ -259,7 +259,8 @@ described in the "Discuss with us" section above. * [zadam](https://github.com/zadam) for the original concept and implementation of the application. -* [Larsa](https://github.com/LarsaSara) for designing the application icon. +* [Sarah Hussein](https://github.com/Sarah-Hussein) for designing the + application icon. * [nriver](https://github.com/nriver) for his work on internationalization. * [Thomas Frei](https://github.com/thfrei) for his original work on the Canvas. * [antoniotejada](https://github.com/nriver) for the original syntax highlight diff --git a/docs/README-hu.md b/docs/README-hu.md index 4e8740808..348eb9d3e 100644 --- a/docs/README-hu.md +++ b/docs/README-hu.md @@ -259,7 +259,8 @@ described in the "Discuss with us" section above. * [zadam](https://github.com/zadam) for the original concept and implementation of the application. -* [Larsa](https://github.com/LarsaSara) for designing the application icon. +* [Sarah Hussein](https://github.com/Sarah-Hussein) for designing the + application icon. * [nriver](https://github.com/nriver) for his work on internationalization. * [Thomas Frei](https://github.com/thfrei) for his original work on the Canvas. * [antoniotejada](https://github.com/nriver) for the original syntax highlight diff --git a/docs/README-id.md b/docs/README-id.md new file mode 100644 index 000000000..8fa1b0962 --- /dev/null +++ b/docs/README-id.md @@ -0,0 +1,315 @@ +# Trilium Notes + +![GitHub Sponsors](https://img.shields.io/github/sponsors/eliandoran) +![LiberaPay patrons](https://img.shields.io/liberapay/patrons/ElianDoran)\ +![Docker Pulls](https://img.shields.io/docker/pulls/triliumnext/trilium) +![Unduhan GitHub (semua aset, semua +rilis)](https://img.shields.io/github/downloads/triliumnext/trilium/total)\ +[![RelativeCI](https://badges.relative-ci.com/badges/Di5q7dz9daNDZ9UXi0Bp?branch=develop)](https://app.relative-ci.com/projects/Di5q7dz9daNDZ9UXi0Bp) +[![Status +terjemahan](https://hosted.weblate.org/widget/trilium/svg-badge.svg)](https://hosted.weblate.org/engage/trilium/) + +[English](./README.md) | [Chinese (Simplified)](./docs/README-ZH_CN.md) | +[Chinese (Traditional)](./docs/README-ZH_TW.md) | [Russian](./docs/README-ru.md) +| [Japanese](./docs/README-ja.md) | [Italian](./docs/README-it.md) | +[Spanish](./docs/README-es.md) + +Trilium Notes adalah aplikasi pencatatan hierarkis lintas platform yang gratis +dan sumber terbuka dengan fokus untuk mengembangkan pengetahuan pribadi yang +luas. + +See [screenshots](https://triliumnext.github.io/Docs/Wiki/screenshot-tour) for +quick overview: + +Trilium Screenshot + +## 📚 Dokumentasi + +**Kunjungi dokumentasi lengkap kami di +[docs.triliumnotes.org](https://docs.triliumnotes.org/)** + +Dokumentasi kami tersedia dalam berbagai format: +- **Dokumentasi Online**: Telusuri dokumentasi lengkap di + [docs.triliumnotes.org](https://docs.triliumnotes.org/) +- **Bantuan Dalam Aplikasi**: Tekan `F1` di dalam Trilium untuk mengakses + dokumentasi yang sama langsung di aplikasi +- **GitHub**: Navigasi melalui [Panduan + Pengguna](./docs/User%20Guide/User%20Guide/) di repositori ini + +### Tautan Cepat +- [Panduan Memulai](https://docs.triliumnotes.org/) +- [Petunjuk + Instalasi](./docs/User%20Guide/User%20Guide/Installation%20&%20Setup/Server%20Installation.md) +- [Pengaturan + Docker](./docs/User%20Guide/User%20Guide/Installation%20&%20Setup/Server%20Installation/1.%20Installing%20the%20server/Using%20Docker.md) +- [Upgrading + TriliumNext](./docs/User%20Guide/User%20Guide/Installation%20%26%20Setup/Upgrading%20TriliumNext.md) +- [Basic Concepts and + Features](./docs/User%20Guide/User%20Guide/Basic%20Concepts%20and%20Features/Notes.md) +- [Patterns of Personal Knowledge + Base](https://triliumnext.github.io/Docs/Wiki/patterns-of-personal-knowledge) + +## 🎁 Features + +* Notes can be arranged into arbitrarily deep tree. Single note can be placed + into multiple places in the tree (see + [cloning](https://triliumnext.github.io/Docs/Wiki/cloning-notes)) +* Rich WYSIWYG note editor including e.g. tables, images and + [math](https://triliumnext.github.io/Docs/Wiki/text-notes) with markdown + [autoformat](https://triliumnext.github.io/Docs/Wiki/text-notes#autoformat) +* Support for editing [notes with source + code](https://triliumnext.github.io/Docs/Wiki/code-notes), including syntax + highlighting +* Fast and easy [navigation between + notes](https://triliumnext.github.io/Docs/Wiki/note-navigation), full text + search and [note + hoisting](https://triliumnext.github.io/Docs/Wiki/note-hoisting) +* Seamless [note + versioning](https://triliumnext.github.io/Docs/Wiki/note-revisions) +* Note [attributes](https://triliumnext.github.io/Docs/Wiki/attributes) can be + used for note organization, querying and advanced + [scripting](https://triliumnext.github.io/Docs/Wiki/scripts) +* UI available in English, German, Spanish, French, Romanian, and Chinese + (simplified and traditional) +* Direct [OpenID and TOTP + integration](./docs/User%20Guide/User%20Guide/Installation%20%26%20Setup/Server%20Installation/Multi-Factor%20Authentication.md) + for more secure login +* [Synchronization](https://triliumnext.github.io/Docs/Wiki/synchronization) + with self-hosted sync server + * there's a [3rd party service for hosting synchronisation + server](https://trilium.cc/paid-hosting) +* [Sharing](https://triliumnext.github.io/Docs/Wiki/sharing) (publishing) notes + to public internet +* Strong [note + encryption](https://triliumnext.github.io/Docs/Wiki/protected-notes) with + per-note granularity +* Sketching diagrams, based on [Excalidraw](https://excalidraw.com/) (note type + "canvas") +* [Relation maps](https://triliumnext.github.io/Docs/Wiki/relation-map) and + [link maps](https://triliumnext.github.io/Docs/Wiki/link-map) for visualizing + notes and their relations +* Mind maps, based on [Mind Elixir](https://docs.mind-elixir.com/) +* [Geo maps](./docs/User%20Guide/User%20Guide/Note%20Types/Geo%20Map.md) with + location pins and GPX tracks +* [Scripting](https://triliumnext.github.io/Docs/Wiki/scripts) - see [Advanced + showcases](https://triliumnext.github.io/Docs/Wiki/advanced-showcases) +* [REST API](https://triliumnext.github.io/Docs/Wiki/etapi) for automation +* Scales well in both usability and performance upwards of 100 000 notes +* Touch optimized [mobile + frontend](https://triliumnext.github.io/Docs/Wiki/mobile-frontend) for + smartphones and tablets +* Built-in [dark theme](https://triliumnext.github.io/Docs/Wiki/themes), support + for user themes +* [Evernote](https://triliumnext.github.io/Docs/Wiki/evernote-import) and + [Markdown import & export](https://triliumnext.github.io/Docs/Wiki/markdown) +* [Web Clipper](https://triliumnext.github.io/Docs/Wiki/web-clipper) for easy + saving of web content +* Customizable UI (sidebar buttons, user-defined widgets, ...) +* [Metrics](./docs/User%20Guide/User%20Guide/Advanced%20Usage/Metrics.md), along + with a [Grafana + Dashboard](./docs/User%20Guide/User%20Guide/Advanced%20Usage/Metrics/grafana-dashboard.json) + +✨ Check out the following third-party resources/communities for more TriliumNext +related goodies: + +- [awesome-trilium](https://github.com/Nriver/awesome-trilium) for 3rd party + themes, scripts, plugins and more. +- [TriliumRocks!](https://trilium.rocks/) for tutorials, guides, and much more. + +## ❓Why TriliumNext? + +The original Trilium developer ([Zadam](https://github.com/zadam)) has +graciously given the Trilium repository to the community project which resides +at https://github.com/TriliumNext + +### ⬆️Migrating from Zadam/Trilium? + +There are no special migration steps to migrate from a zadam/Trilium instance to +a TriliumNext/Trilium instance. Simply [install +TriliumNext/Trilium](#-installation) as usual and it will use your existing +database. + +Versions up to and including +[v0.90.4](https://github.com/TriliumNext/Trilium/releases/tag/v0.90.4) are +compatible with the latest zadam/trilium version of +[v0.63.7](https://github.com/zadam/trilium/releases/tag/v0.63.7). Any later +versions of TriliumNext/Trilium have their sync versions incremented which +prevents direct migration. + +## 💬 Discuss with us + +Feel free to join our official conversations. We would love to hear what +features, suggestions, or issues you may have! + +- [Matrix](https://matrix.to/#/#triliumnext:matrix.org) (For synchronous + discussions.) + - The `General` Matrix room is also bridged to + [XMPP](xmpp:discuss@trilium.thisgreat.party?join) +- [Github Discussions](https://github.com/TriliumNext/Trilium/discussions) (For + asynchronous discussions.) +- [Github Issues](https://github.com/TriliumNext/Trilium/issues) (For bug + reports and feature requests.) + +## 🏗 Installation + +### Windows / MacOS + +Download the binary release for your platform from the [latest release +page](https://github.com/TriliumNext/Trilium/releases/latest), unzip the package +and run the `trilium` executable. + +### Linux + +If your distribution is listed in the table below, use your distribution's +package. + +[![Packaging +status](https://repology.org/badge/vertical-allrepos/triliumnext.svg)](https://repology.org/project/triliumnext/versions) + +You may also download the binary release for your platform from the [latest +release page](https://github.com/TriliumNext/Trilium/releases/latest), unzip the +package and run the `trilium` executable. + +TriliumNext is also provided as a Flatpak, but not yet published on FlatHub. + +### Browser (any OS) + +If you use a server installation (see below), you can directly access the web +interface (which is almost identical to the desktop app). + +Currently only the latest versions of Chrome & Firefox are supported (and +tested). + +### Mobile + +To use TriliumNext on a mobile device, you can use a mobile web browser to +access the mobile interface of a server installation (see below). + +See issue https://github.com/TriliumNext/Trilium/issues/4962 for more +information on mobile app support. + +If you prefer a native Android app, you can use +[TriliumDroid](https://apt.izzysoft.de/fdroid/index/apk/eu.fliegendewurst.triliumdroid). +Report bugs and missing features at [their +repository](https://github.com/FliegendeWurst/TriliumDroid). Note: It is best to +disable automatic updates on your server installation (see below) when using +TriliumDroid since the sync version must match between Trilium and TriliumDroid. + +### Server + +To install TriliumNext on your own server (including via Docker from +[Dockerhub](https://hub.docker.com/r/triliumnext/trilium)) follow [the server +installation docs](https://triliumnext.github.io/Docs/Wiki/server-installation). + + +## 💻 Contribute + +### Translations + +If you are a native speaker, help us translate Trilium by heading over to our +[Weblate page](https://hosted.weblate.org/engage/trilium/). + +Here's the language coverage we have so far: + +[![Translation +status](https://hosted.weblate.org/widget/trilium/multi-auto.svg)](https://hosted.weblate.org/engage/trilium/) + +### Code + +Download the repository, install dependencies using `pnpm` and then run the +server (available at http://localhost:8080): +```shell +git clone https://github.com/TriliumNext/Trilium.git +cd Trilium +pnpm install +pnpm run server:start +``` + +### Documentation + +Download the repository, install dependencies using `pnpm` and then run the +environment required to edit the documentation: +```shell +git clone https://github.com/TriliumNext/Trilium.git +cd Trilium +pnpm install +pnpm edit-docs:edit-docs +``` + +### Building the Executable +Download the repository, install dependencies using `pnpm` and then build the +desktop app for Windows: +```shell +git clone https://github.com/TriliumNext/Trilium.git +cd Trilium +pnpm install +pnpm run --filter desktop electron-forge:make --arch=x64 --platform=win32 +``` + +For more details, see the [development +docs](https://github.com/TriliumNext/Trilium/tree/main/docs/Developer%20Guide/Developer%20Guide). + +### Developer Documentation + +Please view the [documentation +guide](https://github.com/TriliumNext/Trilium/blob/main/docs/Developer%20Guide/Developer%20Guide/Environment%20Setup.md) +for details. If you have more questions, feel free to reach out via the links +described in the "Discuss with us" section above. + +## 👏 Shoutouts + +* [zadam](https://github.com/zadam) for the original concept and implementation + of the application. +* [Sarah Hussein](https://github.com/Sarah-Hussein) for designing the + application icon. +* [nriver](https://github.com/nriver) for his work on internationalization. +* [Thomas Frei](https://github.com/thfrei) for his original work on the Canvas. +* [antoniotejada](https://github.com/nriver) for the original syntax highlight + widget. +* [Dosu](https://dosu.dev/) for providing us with the automated responses to + GitHub issues and discussions. +* [Tabler Icons](https://tabler.io/icons) for the system tray icons. + +Trilium would not be possible without the technologies behind it: + +* [CKEditor 5](https://github.com/ckeditor/ckeditor5) - the visual editor behind + text notes. We are grateful for being offered a set of the premium features. +* [CodeMirror](https://github.com/codemirror/CodeMirror) - code editor with + support for huge amount of languages. +* [Excalidraw](https://github.com/excalidraw/excalidraw) - the infinite + whiteboard used in Canvas notes. +* [Mind Elixir](https://github.com/SSShooter/mind-elixir-core) - providing the + mind map functionality. +* [Leaflet](https://github.com/Leaflet/Leaflet) - for rendering geographical + maps. +* [Tabulator](https://github.com/olifolkerd/tabulator) - for the interactive + table used in collections. +* [FancyTree](https://github.com/mar10/fancytree) - feature-rich tree library + without real competition. +* [jsPlumb](https://github.com/jsplumb/jsplumb) - visual connectivity library. + Used in [relation + maps](https://triliumnext.github.io/Docs/Wiki/relation-map.html) and [link + maps](https://triliumnext.github.io/Docs/Wiki/note-map.html#link-map) + +## 🤝 Support + +Trilium is built and maintained with [hundreds of hours of +work](https://github.com/TriliumNext/Trilium/graphs/commit-activity). Your +support keeps it open-source, improves features, and covers costs such as +hosting. + +Consider supporting the main developer +([eliandoran](https://github.com/eliandoran)) of the application via: + +- [GitHub Sponsors](https://github.com/sponsors/eliandoran) +- [PayPal](https://paypal.me/eliandoran) +- [Buy Me a Coffee](https://buymeacoffee.com/eliandoran) + +## 🔑 License + +Copyright 2017-2025 zadam, Elian Doran, and other contributors + +This program is free software: you can redistribute it and/or modify it under +the terms of the GNU Affero General Public License as published by the Free +Software Foundation, either version 3 of the License, or (at your option) any +later version. diff --git a/docs/README-it.md b/docs/README-it.md index 056de7e0d..c24005f3e 100644 --- a/docs/README-it.md +++ b/docs/README-it.md @@ -260,7 +260,8 @@ described in the "Discuss with us" section above. * [zadam](https://github.com/zadam) for the original concept and implementation of the application. -* [Larsa](https://github.com/LarsaSara) for designing the application icon. +* [Sarah Hussein](https://github.com/Sarah-Hussein) for designing the + application icon. * [nriver](https://github.com/nriver) for his work on internationalization. * [Thomas Frei](https://github.com/thfrei) for his original work on the Canvas. * [antoniotejada](https://github.com/nriver) for the original syntax highlight diff --git a/docs/README-ja.md b/docs/README-ja.md index c3a7de695..e6d048523 100644 --- a/docs/README-ja.md +++ b/docs/README-ja.md @@ -215,7 +215,7 @@ pnpm run --filter desktop electron-forge:make --arch=x64 --platform=win32 ## 👏 シャウトアウト * [zadam](https://github.com/zadam) アプリケーションのオリジナルのコンセプトと実装に対して感謝します。 -* [Larsa](https://github.com/LarsaSara) アプリケーションアイコンをデザイン。 +* [Sarah Hussein](https://github.com/Sarah-Hussein) アプリケーションアイコンをデザイン。 * [nriver](https://github.com/nriver) 国際化への取り組み。 * [Thomas Frei](https://github.com/thfrei) Canvasへのオリジナルな取り組み。 * [antoniotejada](https://github.com/nriver) オリジナルの構文ハイライトウィジェット。 diff --git a/docs/README-ko.md b/docs/README-ko.md index 4e8740808..348eb9d3e 100644 --- a/docs/README-ko.md +++ b/docs/README-ko.md @@ -259,7 +259,8 @@ described in the "Discuss with us" section above. * [zadam](https://github.com/zadam) for the original concept and implementation of the application. -* [Larsa](https://github.com/LarsaSara) for designing the application icon. +* [Sarah Hussein](https://github.com/Sarah-Hussein) for designing the + application icon. * [nriver](https://github.com/nriver) for his work on internationalization. * [Thomas Frei](https://github.com/thfrei) for his original work on the Canvas. * [antoniotejada](https://github.com/nriver) for the original syntax highlight diff --git a/docs/README-md.md b/docs/README-md.md index 4e8740808..348eb9d3e 100644 --- a/docs/README-md.md +++ b/docs/README-md.md @@ -259,7 +259,8 @@ described in the "Discuss with us" section above. * [zadam](https://github.com/zadam) for the original concept and implementation of the application. -* [Larsa](https://github.com/LarsaSara) for designing the application icon. +* [Sarah Hussein](https://github.com/Sarah-Hussein) for designing the + application icon. * [nriver](https://github.com/nriver) for his work on internationalization. * [Thomas Frei](https://github.com/thfrei) for his original work on the Canvas. * [antoniotejada](https://github.com/nriver) for the original syntax highlight diff --git a/docs/README-nb_NO.md b/docs/README-nb_NO.md index 4e8740808..348eb9d3e 100644 --- a/docs/README-nb_NO.md +++ b/docs/README-nb_NO.md @@ -259,7 +259,8 @@ described in the "Discuss with us" section above. * [zadam](https://github.com/zadam) for the original concept and implementation of the application. -* [Larsa](https://github.com/LarsaSara) for designing the application icon. +* [Sarah Hussein](https://github.com/Sarah-Hussein) for designing the + application icon. * [nriver](https://github.com/nriver) for his work on internationalization. * [Thomas Frei](https://github.com/thfrei) for his original work on the Canvas. * [antoniotejada](https://github.com/nriver) for the original syntax highlight diff --git a/docs/README-nl.md b/docs/README-nl.md index 4e8740808..348eb9d3e 100644 --- a/docs/README-nl.md +++ b/docs/README-nl.md @@ -259,7 +259,8 @@ described in the "Discuss with us" section above. * [zadam](https://github.com/zadam) for the original concept and implementation of the application. -* [Larsa](https://github.com/LarsaSara) for designing the application icon. +* [Sarah Hussein](https://github.com/Sarah-Hussein) for designing the + application icon. * [nriver](https://github.com/nriver) for his work on internationalization. * [Thomas Frei](https://github.com/thfrei) for his original work on the Canvas. * [antoniotejada](https://github.com/nriver) for the original syntax highlight diff --git a/docs/README-pl.md b/docs/README-pl.md index 4e8740808..348eb9d3e 100644 --- a/docs/README-pl.md +++ b/docs/README-pl.md @@ -259,7 +259,8 @@ described in the "Discuss with us" section above. * [zadam](https://github.com/zadam) for the original concept and implementation of the application. -* [Larsa](https://github.com/LarsaSara) for designing the application icon. +* [Sarah Hussein](https://github.com/Sarah-Hussein) for designing the + application icon. * [nriver](https://github.com/nriver) for his work on internationalization. * [Thomas Frei](https://github.com/thfrei) for his original work on the Canvas. * [antoniotejada](https://github.com/nriver) for the original syntax highlight diff --git a/docs/README-pt.md b/docs/README-pt.md index 4e8740808..348eb9d3e 100644 --- a/docs/README-pt.md +++ b/docs/README-pt.md @@ -259,7 +259,8 @@ described in the "Discuss with us" section above. * [zadam](https://github.com/zadam) for the original concept and implementation of the application. -* [Larsa](https://github.com/LarsaSara) for designing the application icon. +* [Sarah Hussein](https://github.com/Sarah-Hussein) for designing the + application icon. * [nriver](https://github.com/nriver) for his work on internationalization. * [Thomas Frei](https://github.com/thfrei) for his original work on the Canvas. * [antoniotejada](https://github.com/nriver) for the original syntax highlight diff --git a/docs/README-pt_BR.md b/docs/README-pt_BR.md index 4e8740808..348eb9d3e 100644 --- a/docs/README-pt_BR.md +++ b/docs/README-pt_BR.md @@ -259,7 +259,8 @@ described in the "Discuss with us" section above. * [zadam](https://github.com/zadam) for the original concept and implementation of the application. -* [Larsa](https://github.com/LarsaSara) for designing the application icon. +* [Sarah Hussein](https://github.com/Sarah-Hussein) for designing the + application icon. * [nriver](https://github.com/nriver) for his work on internationalization. * [Thomas Frei](https://github.com/thfrei) for his original work on the Canvas. * [antoniotejada](https://github.com/nriver) for the original syntax highlight diff --git a/docs/README-ro.md b/docs/README-ro.md index fcbfbf68f..37a4f5683 100644 --- a/docs/README-ro.md +++ b/docs/README-ro.md @@ -263,7 +263,8 @@ legăturile descrise în secțiunea „Discutați cu noi” de mai sus. * [zadam](https://github.com/zadam) pentru conceptul și implementarea originală a aplicației. -* [Larsa](https://github.com/LarsaSara) pentru pictograma aplicației. +* [Sarah Hussein](https://github.com/Sarah-Hussein) for designing the + application icon. * [nriver](https://github.com/nriver) pentru sistemul de internaționalizare. * [Thomas Frei](https://github.com/thfrei) pentru munca sa originală pentru notițele de tip schiță. diff --git a/docs/README-ru.md b/docs/README-ru.md index 02f324930..40e79d56a 100644 --- a/docs/README-ru.md +++ b/docs/README-ru.md @@ -261,7 +261,8 @@ described in the "Discuss with us" section above. * [zadam](https://github.com/zadam) for the original concept and implementation of the application. -* [Larsa](https://github.com/LarsaSara) for designing the application icon. +* [Sarah Hussein](https://github.com/Sarah-Hussein) for designing the + application icon. * [nriver](https://github.com/nriver) for his work on internationalization. * [Thomas Frei](https://github.com/thfrei) for his original work on the Canvas. * [antoniotejada](https://github.com/nriver) for the original syntax highlight @@ -307,7 +308,7 @@ Consider supporting the main developer ## 🔑 Лицензия -Copyright 2017-2025 zadam, Elian Doran, and other contributors +Copyright 2017-2025 zadam, Elian Doran и другие авторы Эта программа является бесплатным программным обеспечением: вы можете распространять и/или изменять ее в соответствии с условиями GNU Affero General diff --git a/docs/README-sl.md b/docs/README-sl.md index 4e8740808..348eb9d3e 100644 --- a/docs/README-sl.md +++ b/docs/README-sl.md @@ -259,7 +259,8 @@ described in the "Discuss with us" section above. * [zadam](https://github.com/zadam) for the original concept and implementation of the application. -* [Larsa](https://github.com/LarsaSara) for designing the application icon. +* [Sarah Hussein](https://github.com/Sarah-Hussein) for designing the + application icon. * [nriver](https://github.com/nriver) for his work on internationalization. * [Thomas Frei](https://github.com/thfrei) for his original work on the Canvas. * [antoniotejada](https://github.com/nriver) for the original syntax highlight diff --git a/docs/README-sr.md b/docs/README-sr.md index 4e8740808..348eb9d3e 100644 --- a/docs/README-sr.md +++ b/docs/README-sr.md @@ -259,7 +259,8 @@ described in the "Discuss with us" section above. * [zadam](https://github.com/zadam) for the original concept and implementation of the application. -* [Larsa](https://github.com/LarsaSara) for designing the application icon. +* [Sarah Hussein](https://github.com/Sarah-Hussein) for designing the + application icon. * [nriver](https://github.com/nriver) for his work on internationalization. * [Thomas Frei](https://github.com/thfrei) for his original work on the Canvas. * [antoniotejada](https://github.com/nriver) for the original syntax highlight diff --git a/docs/README-tr.md b/docs/README-tr.md index 4e8740808..348eb9d3e 100644 --- a/docs/README-tr.md +++ b/docs/README-tr.md @@ -259,7 +259,8 @@ described in the "Discuss with us" section above. * [zadam](https://github.com/zadam) for the original concept and implementation of the application. -* [Larsa](https://github.com/LarsaSara) for designing the application icon. +* [Sarah Hussein](https://github.com/Sarah-Hussein) for designing the + application icon. * [nriver](https://github.com/nriver) for his work on internationalization. * [Thomas Frei](https://github.com/thfrei) for his original work on the Canvas. * [antoniotejada](https://github.com/nriver) for the original syntax highlight diff --git a/docs/README-uk.md b/docs/README-uk.md index 4e8740808..348eb9d3e 100644 --- a/docs/README-uk.md +++ b/docs/README-uk.md @@ -259,7 +259,8 @@ described in the "Discuss with us" section above. * [zadam](https://github.com/zadam) for the original concept and implementation of the application. -* [Larsa](https://github.com/LarsaSara) for designing the application icon. +* [Sarah Hussein](https://github.com/Sarah-Hussein) for designing the + application icon. * [nriver](https://github.com/nriver) for his work on internationalization. * [Thomas Frei](https://github.com/thfrei) for his original work on the Canvas. * [antoniotejada](https://github.com/nriver) for the original syntax highlight diff --git a/docs/README-vi.md b/docs/README-vi.md index 4e8740808..348eb9d3e 100644 --- a/docs/README-vi.md +++ b/docs/README-vi.md @@ -259,7 +259,8 @@ described in the "Discuss with us" section above. * [zadam](https://github.com/zadam) for the original concept and implementation of the application. -* [Larsa](https://github.com/LarsaSara) for designing the application icon. +* [Sarah Hussein](https://github.com/Sarah-Hussein) for designing the + application icon. * [nriver](https://github.com/nriver) for his work on internationalization. * [Thomas Frei](https://github.com/thfrei) for his original work on the Canvas. * [antoniotejada](https://github.com/nriver) for the original syntax highlight diff --git a/package.json b/package.json index c2b9ba42e..74830ee85 100644 --- a/package.json +++ b/package.json @@ -50,12 +50,12 @@ "eslint": "9.37.0", "eslint-config-prettier": "10.1.8", "eslint-plugin-playwright": "2.2.2", - "eslint-plugin-react-hooks": "6.1.1", + "eslint-plugin-react-hooks": "7.0.0", "happy-dom": "~19.0.0", "jiti": "2.6.1", "jsonc-eslint-parser": "2.4.1", "react-refresh": "0.18.0", - "rollup-plugin-webpack-stats": "2.1.5", + "rollup-plugin-webpack-stats": "2.1.6", "tslib": "2.8.1", "tsx": "4.20.6", "typescript": "~5.9.0", diff --git a/packages/ckeditor5/package.json b/packages/ckeditor5/package.json index b1dc7c24b..f87fa37f2 100644 --- a/packages/ckeditor5/package.json +++ b/packages/ckeditor5/package.json @@ -15,7 +15,7 @@ "ckeditor5-premium-features": "47.0.0" }, "devDependencies": { - "@smithy/middleware-retry": "4.4.0", + "@smithy/middleware-retry": "4.4.1", "@types/jquery": "3.5.33" } } diff --git a/packages/codemirror/package.json b/packages/codemirror/package.json index 227618ec2..40ac2acf6 100644 --- a/packages/codemirror/package.json +++ b/packages/codemirror/package.json @@ -16,7 +16,7 @@ "@codemirror/lang-xml": "6.1.0", "@codemirror/legacy-modes": "6.5.2", "@codemirror/search": "6.5.11", - "@codemirror/view": "6.38.4", + "@codemirror/view": "6.38.5", "@fsegurai/codemirror-theme-abcdef": "6.2.2", "@fsegurai/codemirror-theme-abyss": "6.2.2", "@fsegurai/codemirror-theme-android-studio": "6.2.2", diff --git a/packages/commons/src/lib/test-utils.ts b/packages/commons/src/lib/test-utils.ts index edfe90ef0..86ebfb0d6 100644 --- a/packages/commons/src/lib/test-utils.ts +++ b/packages/commons/src/lib/test-utils.ts @@ -54,3 +54,11 @@ export function trimIndentation(strings: TemplateStringsArray, ...values: any[]) } return output.join("\n"); } + +export function flushPromises() { + return new Promise(setImmediate); +} + +export function sleepFor(duration: number) { + return new Promise(resolve => setTimeout(resolve, duration)); +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 947cfadd4..aeb814f0a 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -80,8 +80,8 @@ importers: specifier: 2.2.2 version: 2.2.2(eslint@9.37.0(jiti@2.6.1)) eslint-plugin-react-hooks: - specifier: 6.1.1 - version: 6.1.1(eslint@9.37.0(jiti@2.6.1)) + specifier: 7.0.0 + version: 7.0.0(eslint@9.37.0(jiti@2.6.1)) happy-dom: specifier: ~19.0.0 version: 19.0.2 @@ -95,8 +95,8 @@ importers: specifier: 0.18.0 version: 0.18.0 rollup-plugin-webpack-stats: - specifier: 2.1.5 - version: 2.1.5(rolldown@1.0.0-beta.29)(rollup@4.52.0)(vite@7.1.9(@types/node@22.18.8)(jiti@2.6.1)(less@4.1.3)(lightningcss@1.30.1)(sass-embedded@1.91.0)(sass@1.91.0)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)) + specifier: 2.1.6 + version: 2.1.6(rolldown@1.0.0-beta.29)(rollup@4.52.0)(vite@7.1.9(@types/node@22.18.8)(jiti@2.6.1)(less@4.1.3)(lightningcss@1.30.1)(sass-embedded@1.91.0)(sass@1.91.0)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)) tslib: specifier: 2.8.1 version: 2.8.1 @@ -156,7 +156,7 @@ importers: version: 0.2.0(mermaid@11.12.0) '@mind-elixir/node-menu': specifier: 5.0.0 - version: 5.0.0(mind-elixir@5.1.1) + version: 5.0.0(mind-elixir@5.3.2) '@popperjs/core': specifier: 2.11.8 version: 2.11.8 @@ -233,14 +233,14 @@ importers: specifier: 8.11.1 version: 8.11.1 marked: - specifier: 16.3.0 - version: 16.3.0 + specifier: 16.4.0 + version: 16.4.0 mermaid: specifier: 11.12.0 version: 11.12.0 mind-elixir: - specifier: 5.1.1 - version: 5.1.1 + specifier: 5.3.2 + version: 5.3.2 normalize.css: specifier: 8.0.1 version: 8.0.1 @@ -322,7 +322,7 @@ importers: dependencies: '@electron/remote': specifier: 2.1.3 - version: 2.1.3(electron@38.2.1) + version: 2.1.3(electron@38.2.2) better-sqlite3: specifier: 12.4.1 version: 12.4.1 @@ -343,29 +343,29 @@ importers: version: 2.38.5(jquery@3.7.1) devDependencies: '@electron-forge/cli': - specifier: 7.9.0 - version: 7.9.0(encoding@0.1.13) + specifier: 7.10.0 + version: 7.10.0(encoding@0.1.13) '@electron-forge/maker-deb': - specifier: 7.9.0 - version: 7.9.0 + specifier: 7.10.0 + version: 7.10.0 '@electron-forge/maker-dmg': - specifier: 7.9.0 - version: 7.9.0 + specifier: 7.10.0 + version: 7.10.0 '@electron-forge/maker-flatpak': - specifier: 7.9.0 - version: 7.9.0 + specifier: 7.10.0 + version: 7.10.0 '@electron-forge/maker-rpm': - specifier: 7.9.0 - version: 7.9.0 + specifier: 7.10.0 + version: 7.10.0 '@electron-forge/maker-squirrel': - specifier: 7.9.0 - version: 7.9.0 + specifier: 7.10.0 + version: 7.10.0 '@electron-forge/maker-zip': - specifier: 7.9.0 - version: 7.9.0 + specifier: 7.10.0 + version: 7.10.0 '@electron-forge/plugin-auto-unpack-natives': - specifier: 7.9.0 - version: 7.9.0 + specifier: 7.10.0 + version: 7.10.0 '@triliumnext/commons': specifier: workspace:* version: link:../../packages/commons @@ -379,8 +379,8 @@ importers: specifier: 13.0.1 version: 13.0.1(webpack@5.101.3(esbuild@0.25.10)) electron: - specifier: 38.2.1 - version: 38.2.1 + specifier: 38.2.2 + version: 38.2.2 prebuild-install: specifier: 7.1.3 version: 7.1.3 @@ -435,8 +435,8 @@ importers: specifier: 13.0.1 version: 13.0.1(webpack@5.101.3(esbuild@0.25.10)) electron: - specifier: 38.2.1 - version: 38.2.1 + specifier: 38.2.2 + version: 38.2.2 fs-extra: specifier: 11.3.2 version: 11.3.2 @@ -458,7 +458,7 @@ importers: version: 7.1.1 '@electron/remote': specifier: 2.1.3 - version: 2.1.3(electron@38.2.1) + version: 2.1.3(electron@38.2.2) '@preact/preset-vite': specifier: 2.10.2 version: 2.10.2(@babel/core@7.28.0)(preact@10.27.2)(vite@7.1.9(@types/node@24.6.0)(jiti@2.6.1)(less@4.1.3)(lightningcss@1.30.1)(sass-embedded@1.91.0)(sass@1.91.0)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)) @@ -607,8 +607,8 @@ importers: specifier: 3.1.10 version: 3.1.10 electron: - specifier: 38.2.1 - version: 38.2.1 + specifier: 38.2.2 + version: 38.2.2 electron-debug: specifier: 4.1.0 version: 4.1.0 @@ -679,8 +679,8 @@ importers: specifier: 4.1.0 version: 4.1.0 marked: - specifier: 16.3.0 - version: 16.3.0 + specifier: 16.4.0 + version: 16.4.0 mime-types: specifier: 3.0.1 version: 3.0.1 @@ -819,8 +819,8 @@ importers: version: 47.0.0(bufferutil@4.0.9)(ckeditor5@47.0.0(patch_hash=8331a09d41443b39ea1c784daaccfeb0da4f9065ed556e7de92e9c77edd9eb41))(utf-8-validate@6.0.5) devDependencies: '@smithy/middleware-retry': - specifier: 4.4.0 - version: 4.4.0 + specifier: 4.4.1 + version: 4.4.1 '@types/jquery': specifier: 3.5.33 version: 3.5.33 @@ -1175,89 +1175,89 @@ importers: specifier: 6.5.11 version: 6.5.11 '@codemirror/view': - specifier: 6.38.4 - version: 6.38.4 + specifier: 6.38.5 + version: 6.38.5 '@fsegurai/codemirror-theme-abcdef': specifier: 6.2.2 - version: 6.2.2(@codemirror/language@6.11.0)(@codemirror/state@6.5.2)(@codemirror/view@6.38.4)(@lezer/highlight@1.2.1) + version: 6.2.2(@codemirror/language@6.11.0)(@codemirror/state@6.5.2)(@codemirror/view@6.38.5)(@lezer/highlight@1.2.1) '@fsegurai/codemirror-theme-abyss': specifier: 6.2.2 - version: 6.2.2(@codemirror/language@6.11.0)(@codemirror/state@6.5.2)(@codemirror/view@6.38.4)(@lezer/highlight@1.2.1) + version: 6.2.2(@codemirror/language@6.11.0)(@codemirror/state@6.5.2)(@codemirror/view@6.38.5)(@lezer/highlight@1.2.1) '@fsegurai/codemirror-theme-android-studio': specifier: 6.2.2 - version: 6.2.2(@codemirror/language@6.11.0)(@codemirror/state@6.5.2)(@codemirror/view@6.38.4)(@lezer/highlight@1.2.1) + version: 6.2.2(@codemirror/language@6.11.0)(@codemirror/state@6.5.2)(@codemirror/view@6.38.5)(@lezer/highlight@1.2.1) '@fsegurai/codemirror-theme-andromeda': specifier: 6.2.2 - version: 6.2.2(@codemirror/language@6.11.0)(@codemirror/state@6.5.2)(@codemirror/view@6.38.4)(@lezer/highlight@1.2.1) + version: 6.2.2(@codemirror/language@6.11.0)(@codemirror/state@6.5.2)(@codemirror/view@6.38.5)(@lezer/highlight@1.2.1) '@fsegurai/codemirror-theme-basic-dark': specifier: 6.2.2 - version: 6.2.2(@codemirror/language@6.11.0)(@codemirror/state@6.5.2)(@codemirror/view@6.38.4)(@lezer/highlight@1.2.1) + version: 6.2.2(@codemirror/language@6.11.0)(@codemirror/state@6.5.2)(@codemirror/view@6.38.5)(@lezer/highlight@1.2.1) '@fsegurai/codemirror-theme-basic-light': specifier: 6.2.2 - version: 6.2.2(@codemirror/language@6.11.0)(@codemirror/state@6.5.2)(@codemirror/view@6.38.4)(@lezer/highlight@1.2.1) + version: 6.2.2(@codemirror/language@6.11.0)(@codemirror/state@6.5.2)(@codemirror/view@6.38.5)(@lezer/highlight@1.2.1) '@fsegurai/codemirror-theme-cobalt2': specifier: 6.0.2 - version: 6.0.2(@codemirror/language@6.11.0)(@codemirror/state@6.5.2)(@codemirror/view@6.38.4)(@lezer/highlight@1.2.1) + version: 6.0.2(@codemirror/language@6.11.0)(@codemirror/state@6.5.2)(@codemirror/view@6.38.5)(@lezer/highlight@1.2.1) '@fsegurai/codemirror-theme-forest': specifier: 6.2.2 - version: 6.2.2(@codemirror/language@6.11.0)(@codemirror/state@6.5.2)(@codemirror/view@6.38.4)(@lezer/highlight@1.2.1) + version: 6.2.2(@codemirror/language@6.11.0)(@codemirror/state@6.5.2)(@codemirror/view@6.38.5)(@lezer/highlight@1.2.1) '@fsegurai/codemirror-theme-github-dark': specifier: 6.2.2 - version: 6.2.2(@codemirror/language@6.11.0)(@codemirror/state@6.5.2)(@codemirror/view@6.38.4)(@lezer/highlight@1.2.1) + version: 6.2.2(@codemirror/language@6.11.0)(@codemirror/state@6.5.2)(@codemirror/view@6.38.5)(@lezer/highlight@1.2.1) '@fsegurai/codemirror-theme-github-light': specifier: 6.2.2 - version: 6.2.2(@codemirror/language@6.11.0)(@codemirror/state@6.5.2)(@codemirror/view@6.38.4)(@lezer/highlight@1.2.1) + version: 6.2.2(@codemirror/language@6.11.0)(@codemirror/state@6.5.2)(@codemirror/view@6.38.5)(@lezer/highlight@1.2.1) '@fsegurai/codemirror-theme-gruvbox-dark': specifier: 6.2.2 - version: 6.2.2(@codemirror/language@6.11.0)(@codemirror/state@6.5.2)(@codemirror/view@6.38.4)(@lezer/highlight@1.2.1) + version: 6.2.2(@codemirror/language@6.11.0)(@codemirror/state@6.5.2)(@codemirror/view@6.38.5)(@lezer/highlight@1.2.1) '@fsegurai/codemirror-theme-gruvbox-light': specifier: 6.2.2 - version: 6.2.2(@codemirror/language@6.11.0)(@codemirror/state@6.5.2)(@codemirror/view@6.38.4)(@lezer/highlight@1.2.1) + version: 6.2.2(@codemirror/language@6.11.0)(@codemirror/state@6.5.2)(@codemirror/view@6.38.5)(@lezer/highlight@1.2.1) '@fsegurai/codemirror-theme-material-dark': specifier: 6.2.2 - version: 6.2.2(@codemirror/language@6.11.0)(@codemirror/state@6.5.2)(@codemirror/view@6.38.4)(@lezer/highlight@1.2.1) + version: 6.2.2(@codemirror/language@6.11.0)(@codemirror/state@6.5.2)(@codemirror/view@6.38.5)(@lezer/highlight@1.2.1) '@fsegurai/codemirror-theme-material-light': specifier: 6.2.2 - version: 6.2.2(@codemirror/language@6.11.0)(@codemirror/state@6.5.2)(@codemirror/view@6.38.4)(@lezer/highlight@1.2.1) + version: 6.2.2(@codemirror/language@6.11.0)(@codemirror/state@6.5.2)(@codemirror/view@6.38.5)(@lezer/highlight@1.2.1) '@fsegurai/codemirror-theme-monokai': specifier: 6.2.2 - version: 6.2.2(@codemirror/language@6.11.0)(@codemirror/state@6.5.2)(@codemirror/view@6.38.4)(@lezer/highlight@1.2.1) + version: 6.2.2(@codemirror/language@6.11.0)(@codemirror/state@6.5.2)(@codemirror/view@6.38.5)(@lezer/highlight@1.2.1) '@fsegurai/codemirror-theme-nord': specifier: 6.2.2 - version: 6.2.2(@codemirror/language@6.11.0)(@codemirror/state@6.5.2)(@codemirror/view@6.38.4)(@lezer/highlight@1.2.1) + version: 6.2.2(@codemirror/language@6.11.0)(@codemirror/state@6.5.2)(@codemirror/view@6.38.5)(@lezer/highlight@1.2.1) '@fsegurai/codemirror-theme-palenight': specifier: 6.2.2 - version: 6.2.2(@codemirror/language@6.11.0)(@codemirror/state@6.5.2)(@codemirror/view@6.38.4)(@lezer/highlight@1.2.1) + version: 6.2.2(@codemirror/language@6.11.0)(@codemirror/state@6.5.2)(@codemirror/view@6.38.5)(@lezer/highlight@1.2.1) '@fsegurai/codemirror-theme-solarized-dark': specifier: 6.2.2 - version: 6.2.2(@codemirror/language@6.11.0)(@codemirror/state@6.5.2)(@codemirror/view@6.38.4)(@lezer/highlight@1.2.1) + version: 6.2.2(@codemirror/language@6.11.0)(@codemirror/state@6.5.2)(@codemirror/view@6.38.5)(@lezer/highlight@1.2.1) '@fsegurai/codemirror-theme-solarized-light': specifier: 6.2.2 - version: 6.2.2(@codemirror/language@6.11.0)(@codemirror/state@6.5.2)(@codemirror/view@6.38.4)(@lezer/highlight@1.2.1) + version: 6.2.2(@codemirror/language@6.11.0)(@codemirror/state@6.5.2)(@codemirror/view@6.38.5)(@lezer/highlight@1.2.1) '@fsegurai/codemirror-theme-tokyo-night-day': specifier: 6.2.2 - version: 6.2.2(@codemirror/language@6.11.0)(@codemirror/state@6.5.2)(@codemirror/view@6.38.4)(@lezer/highlight@1.2.1) + version: 6.2.2(@codemirror/language@6.11.0)(@codemirror/state@6.5.2)(@codemirror/view@6.38.5)(@lezer/highlight@1.2.1) '@fsegurai/codemirror-theme-tokyo-night-storm': specifier: 6.2.2 - version: 6.2.2(@codemirror/language@6.11.0)(@codemirror/state@6.5.2)(@codemirror/view@6.38.4)(@lezer/highlight@1.2.1) + version: 6.2.2(@codemirror/language@6.11.0)(@codemirror/state@6.5.2)(@codemirror/view@6.38.5)(@lezer/highlight@1.2.1) '@fsegurai/codemirror-theme-volcano': specifier: 6.2.2 - version: 6.2.2(@codemirror/language@6.11.0)(@codemirror/state@6.5.2)(@codemirror/view@6.38.4)(@lezer/highlight@1.2.1) + version: 6.2.2(@codemirror/language@6.11.0)(@codemirror/state@6.5.2)(@codemirror/view@6.38.5)(@lezer/highlight@1.2.1) '@fsegurai/codemirror-theme-vscode-dark': specifier: 6.2.2 - version: 6.2.2(@codemirror/language@6.11.0)(@codemirror/state@6.5.2)(@codemirror/view@6.38.4)(@lezer/highlight@1.2.1) + version: 6.2.2(@codemirror/language@6.11.0)(@codemirror/state@6.5.2)(@codemirror/view@6.38.5)(@lezer/highlight@1.2.1) '@fsegurai/codemirror-theme-vscode-light': specifier: 6.2.2 - version: 6.2.2(@codemirror/language@6.11.0)(@codemirror/state@6.5.2)(@codemirror/view@6.38.4)(@lezer/highlight@1.2.1) + version: 6.2.2(@codemirror/language@6.11.0)(@codemirror/state@6.5.2)(@codemirror/view@6.38.5)(@lezer/highlight@1.2.1) '@replit/codemirror-indentation-markers': specifier: 6.5.3 - version: 6.5.3(@codemirror/language@6.11.0)(@codemirror/state@6.5.2)(@codemirror/view@6.38.4) + version: 6.5.3(@codemirror/language@6.11.0)(@codemirror/state@6.5.2)(@codemirror/view@6.38.5) '@replit/codemirror-lang-nix': specifier: 6.0.1 - version: 6.0.1(@codemirror/autocomplete@6.18.6)(@codemirror/language@6.11.0)(@codemirror/state@6.5.2)(@codemirror/view@6.38.4)(@lezer/common@1.2.3)(@lezer/highlight@1.2.1)(@lezer/lr@1.4.2) + version: 6.0.1(@codemirror/autocomplete@6.18.6)(@codemirror/language@6.11.0)(@codemirror/state@6.5.2)(@codemirror/view@6.38.5)(@lezer/common@1.2.3)(@lezer/highlight@1.2.1)(@lezer/lr@1.4.2) '@replit/codemirror-vim': specifier: 6.3.0 - version: 6.3.0(@codemirror/commands@6.9.0)(@codemirror/language@6.11.0)(@codemirror/search@6.5.11)(@codemirror/state@6.5.2)(@codemirror/view@6.38.4) + version: 6.3.0(@codemirror/commands@6.9.0)(@codemirror/language@6.11.0)(@codemirror/search@6.5.11)(@codemirror/state@6.5.2)(@codemirror/view@6.38.5) '@ssddanbrown/codemirror-lang-smarty': specifier: 1.0.0 version: 1.0.0 @@ -2015,8 +2015,8 @@ packages: '@codemirror/theme-one-dark@6.1.2': resolution: {integrity: sha512-F+sH0X16j/qFLMAfbciKTxVOwkdAS336b7AXTKOZhy8BR3eH/RelsnLgLFINrpST63mmN2OuwUt0W2ndUgYwUA==} - '@codemirror/view@6.38.4': - resolution: {integrity: sha512-hduz0suCcUSC/kM8Fq3A9iLwInJDl8fD1xLpTIk+5xkNm8z/FT7UsIa9sOXrkpChh+XXc18RzswE8QqELsVl+g==} + '@codemirror/view@6.38.5': + resolution: {integrity: sha512-SFVsNAgsAoou+BjRewMqN+m9jaztB9wCWN9RSRgePqUbq8UVlvJfku5zB2KVhLPgH/h0RLk38tvd4tGeAhygnw==} '@cspotcode/source-map-support@0.8.1': resolution: {integrity: sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==} @@ -2093,85 +2093,85 @@ packages: '@dual-bundle/import-meta-resolve@4.2.1': resolution: {integrity: sha512-id+7YRUgoUX6CgV0DtuhirQWodeeA7Lf4i2x71JS/vtA5pRb/hIGWlw+G6MeXvsM+MXrz0VAydTGElX1rAfgPg==} - '@electron-forge/cli@7.9.0': - resolution: {integrity: sha512-FIs9rV6gN1v8rY73j/dvhQWF8tht035bN2LatY/z99H2KpWr0lhD03MOkIM2lRgrPEqVTExA4JEl9ja9yXBD3Q==} + '@electron-forge/cli@7.10.0': + resolution: {integrity: sha512-48lBvgQjkhj/S/TwbtvAauD0oSDrtIAqYMUZ3gyW1TgiZEMmBk4E56zbpjQ7AU0krGE7NUiLj8i8hvFpEJSljw==} engines: {node: '>= 16.4.0'} hasBin: true - '@electron-forge/core-utils@7.9.0': - resolution: {integrity: sha512-votOf1+20cUJim0smYZW7OylXrl89JlpuFTgFpn5WFpDfAV6C0iH9202K1sa33v9OQ989JnpWDZtQNQzQHHNtg==} + '@electron-forge/core-utils@7.10.0': + resolution: {integrity: sha512-9byO4RGuP2na3xHzzzbup3Q4jC3QyXIgmZadL4N+A2Q2QH/DIMp/rOIB/zytqV0pNsdbeDSMvee7nj4jO0PeZg==} engines: {node: '>= 16.4.0'} - '@electron-forge/core@7.9.0': - resolution: {integrity: sha512-S5uFv9sFAVYSO80/K4HZsJL9L2Bs51IxCqR0a2Lk4NdKr9Fpzp6txwuALSJzM1bIpohtqbjmgut49o95wg30ZA==} + '@electron-forge/core@7.10.0': + resolution: {integrity: sha512-jM4HZWCyJeHpqh4Oc0gjPwMj7/BpO10Dex9L3RLUrR/pxsf5RrfmNOYbezTr1LlH1E4cTWGg5ziG23XMhZDjnA==} engines: {node: '>= 16.4.0'} - '@electron-forge/maker-base@7.9.0': - resolution: {integrity: sha512-u0jo2kaYRxh/Rai6DyFSGJcNLRVWxiKaGUjMhX1LrKolufUkCxxR4TEmv4Hvl03WTr9pQb06umlIrVMaNb+j9A==} + '@electron-forge/maker-base@7.10.0': + resolution: {integrity: sha512-ydpwyCsNXS4xDknf5Aas7j2CyUmdGIBJRr760VemM11ts55zu3HOndvLCfsBSCBq4wpOIa6plQ4CaQQn15SrgQ==} engines: {node: '>= 16.4.0'} - '@electron-forge/maker-deb@7.9.0': - resolution: {integrity: sha512-b1TRMKkeuZ2PTKVsTMWLY+82O6UQVPQ17x5hkWe0lyIxRyCfJr6hD+AqYbvEJ50ncDkvyCtENBT6GnhfbKOGxA==} + '@electron-forge/maker-deb@7.10.0': + resolution: {integrity: sha512-RlzhL/NtbYqphBuNWINwCR2kuCIE0RpYvkqa1ynDdu0n/OOiR/E1dpT0T/ZzSoyTIAZLYIcX3cHXwObzvV9bHQ==} engines: {node: '>= 16.4.0'} - '@electron-forge/maker-dmg@7.9.0': - resolution: {integrity: sha512-W3kqbFuBIqxe/56eZmBv80Fw0RdXIL1DXF0D4O56ILnx7jW/d/j+8fJdie5dcw6nBySDIZbWnxsZiOIV2k2hrg==} + '@electron-forge/maker-dmg@7.10.0': + resolution: {integrity: sha512-lC0ztzd2r5z+Of0Z3ty1/wmCdrw/0o+YivNGUo0lAlBlpjicLTEuJxTXFTH78qKrYA4L4bBruR76v9hRM7WJ0g==} engines: {node: '>= 16.4.0'} - '@electron-forge/maker-flatpak@7.9.0': - resolution: {integrity: sha512-LqFWp61+ozlioFtqu+UryKjdsIdv+BLunDDjMRcdoMqiAblURz8bRgqq8FAdshiUt+iq4NPz6K9N0dKtSjhvuA==} + '@electron-forge/maker-flatpak@7.10.0': + resolution: {integrity: sha512-n6nOLXTqZT5B/g/icQ3Iw1L4FK3ZgVZzgZVPJIZnwmVQurhABsErCX9mjTSq/rU7zVKmRI9dZtFNOE7wrmYZLA==} engines: {node: '>= 16.4.0'} - '@electron-forge/maker-rpm@7.9.0': - resolution: {integrity: sha512-if7Odo/LFn1MIGW6ftEnhP77+0pTeMd9e9YvM8+qiaztV/jVNB0RGuuzKOtQXzbrrjps/YWG15x+U4/XUIUylg==} + '@electron-forge/maker-rpm@7.10.0': + resolution: {integrity: sha512-C6AGjIqzNqF2iqXcA5a4JAfMcuS6603nkPhmtYnVqOXFA/vucTeZvFwPyMMN75LPAv120juMaUEr5us/jsN4TQ==} engines: {node: '>= 16.4.0'} - '@electron-forge/maker-squirrel@7.9.0': - resolution: {integrity: sha512-Ea3MrieWC1KRct1QSZeOBY+GIqHZO5bXj6xCuj81f6nz8JMCsXdgvKosdbfcJMSKu4SYZ52PtoweA6uAACfLww==} + '@electron-forge/maker-squirrel@7.10.0': + resolution: {integrity: sha512-lChxafIApRQ2Bjw94sW/GZwnaB5fVYlDxmN54NLCqmUiYNyH9EnH4lZvMOYScA2QzizDHA0oWWYtUAIdtkzyhA==} engines: {node: '>= 16.4.0'} - '@electron-forge/maker-zip@7.9.0': - resolution: {integrity: sha512-UGeziReiz8yuDTjliOjvbdyulIHpKAWkDeW3kOcMTUmRcCgrCkBNr+Pp6ih8Q3aBhG+CCd4++oe2rDnnuVvxFw==} + '@electron-forge/maker-zip@7.10.0': + resolution: {integrity: sha512-e+NpgbEGo0xJzWxEvr2bewg/khTIVTdL75YFH2mayURKj1wia+bI8ukFdJvga5kkr842loUm38uW7dALYN3jQQ==} engines: {node: '>= 16.4.0'} - '@electron-forge/plugin-auto-unpack-natives@7.9.0': - resolution: {integrity: sha512-PQeTq7Mp2bQkj/fdf+DjnSFKLWyBCSdLqZqNadszA9+2QcxFVv+v2ckTuwkHAdoecVNOdza/VZZIDbVFLhUHkQ==} + '@electron-forge/plugin-auto-unpack-natives@7.10.0': + resolution: {integrity: sha512-UubBCrB+om5JcW+mSp6Tmx6hqanqFu5o4z9D+zHVHhutROEgG5CwDEM0RbHrAP98bjokq3kZM6DTidXSttZnow==} engines: {node: '>= 16.4.0'} - '@electron-forge/plugin-base@7.9.0': - resolution: {integrity: sha512-2cnShgfes0sqH7A3+54fWhfJEfU++1OC2HE50a4sWtWEDwyWLGbwW7tp9BgSXrvIexO2AGKHQ1pKIjpZYVC0fA==} + '@electron-forge/plugin-base@7.10.0': + resolution: {integrity: sha512-WxIwQZVhvCNHXePFvzE79OBKw7OrLzzxk4bb/PVnAyiDSHGdUyd35EzH5TAYENUACdM96B+nlIIU1PqTbPx63Q==} engines: {node: '>= 16.4.0'} - '@electron-forge/publisher-base@7.9.0': - resolution: {integrity: sha512-z3eH4+C++LiDqlKmri04IbSNNWLRYLvc49uNiOgfvnejplLsos720TTbwgruyI3D2fbpmhrVmz9kp9H9YzXdVg==} + '@electron-forge/publisher-base@7.10.0': + resolution: {integrity: sha512-YAWnpTgzrf3xnjjk6pUwNfYAEMZ6YuOASEfPTKCKRo0MUpcUVvP1kB/LYlwI0LItyrGoTk0cfAw5fN/kFlB1Ng==} engines: {node: '>= 16.4.0'} - '@electron-forge/shared-types@7.9.0': - resolution: {integrity: sha512-6jZF+zq3SYMnweQpgr5fwlSgOd2yOZ5qlfz/CgXyVljiv0e0UThzpOjfTLuwuVgZX7a60xV+h0mg1h82Glu3wQ==} + '@electron-forge/shared-types@7.10.0': + resolution: {integrity: sha512-FpLBZ1kdIQjRmo09dD/zwDAARGLTzRB2Ja4eplUx5c8ctoblCnvC+KEOYA7R5DaRpKChaW0lqpDRF3ou2W+hHA==} engines: {node: '>= 16.4.0'} - '@electron-forge/template-base@7.9.0': - resolution: {integrity: sha512-DvXdJHh4qP+LBX6xNBlO0nfljvJNTmiQNfLRfSouRdYCWbr5hC7wyWAX803HqwRsVLRZj2oiLYZjmyG3jESp8Q==} + '@electron-forge/template-base@7.10.0': + resolution: {integrity: sha512-wdQc17WwBS2RR97Nmjfuo4dqopMKmwDhqVmZd3VgKrR+5iQyxupjND+nf9VMzXd8exoyZzot06hj/ZQ/BAkJOA==} engines: {node: '>= 16.4.0'} - '@electron-forge/template-vite-typescript@7.9.0': - resolution: {integrity: sha512-qGK649YnC3iOVrj/hHUu/TXG7Nn/a7QykEOPrCrxROWL7mtw8CMUE+FOVETd+eew7/4ldUNzl+5b9ebP8jHOmg==} + '@electron-forge/template-vite-typescript@7.10.0': + resolution: {integrity: sha512-OtLNgq2/DUV6G02ERqhJ/RPmd3QgcRiM/KQd0dIkQMtvJ+X8TBgD/WKvr0iVtlrsPH7Y4DQtGJHx1a25tofAIA==} engines: {node: '>= 16.4.0'} - '@electron-forge/template-vite@7.9.0': - resolution: {integrity: sha512-m8Fi40XaF7YDJo7YqmfJbkjRRsttN9EEHkmq+McLrDUbln15Ppm59RAwUhizDQzs4enmLcNQPaFllbAg/2t8vQ==} + '@electron-forge/template-vite@7.10.0': + resolution: {integrity: sha512-inSwFTo8zULrFsyXk9fLjNc6eK4xiHUj3GBaKwGt6emJzErO2mXXrUB8C6eTfWhVSDXtCHnjadkRSyNxAfgMcw==} engines: {node: '>= 16.4.0'} - '@electron-forge/template-webpack-typescript@7.9.0': - resolution: {integrity: sha512-z7PB72sI11LBSPz9w9nwwk0le/lZ3VT8TrLc0NYh4F4LbZ19Z+lxcraaAdf21efvWzodhWx8khAyA8WvkNxVSw==} + '@electron-forge/template-webpack-typescript@7.10.0': + resolution: {integrity: sha512-9cs371U7ktuRkQUWQPRBM02nRjAVVBWAiEGYkF8eeDiDoJkHfYk1jgsmZkJufasONhNvNbX41VlxfCPJ2iDf+g==} engines: {node: '>= 16.4.0'} - '@electron-forge/template-webpack@7.9.0': - resolution: {integrity: sha512-ser22QczYGor7N6bnMUwexdWbLMNApr6zpBoT+Xfn6P4Ks67ttzcfkAges7GXHNjXwox4ePYZ3+D7cWQem5C4g==} + '@electron-forge/template-webpack@7.10.0': + resolution: {integrity: sha512-sOyYDoRjxMq6g+eoy9JZsMXc8vMlY+Qc0vkWtgzYfsVbuVnWU0ktVfeHeeALxb9iwWB/dXupn6XITExChsnzzg==} engines: {node: '>= 16.4.0'} - '@electron-forge/tracer@7.9.0': - resolution: {integrity: sha512-7itsjW1WJQADg7Ly61ggI5CCRt+QDVx3HOZC1w69jMUtnipKyPRCbvTBf1oplsNqbIzZxceXdfex6W53YNehvA==} + '@electron-forge/tracer@7.10.0': + resolution: {integrity: sha512-Qoj8bFBcwH3etG3YwJXHVeauKO3zyiZEelXMXgOp/du2QA23fc2+Ar7mzxcjY5fWDScd/8CN7M3+LxpovcKIDA==} engines: {node: '>= 14.17.5'} '@electron/asar@3.4.1': @@ -4182,6 +4182,10 @@ packages: resolution: {integrity: sha512-XJ4z5FxvY/t0Dibms/+gLJrI5niRoY0BCmE02fwmPcRYFPI4KI876xaE79YGWIKnEslMbuQPsIEsoU/DXa0DoA==} engines: {node: '>=18.0.0'} + '@smithy/core@3.15.0': + resolution: {integrity: sha512-VJWncXgt+ExNn0U2+Y7UywuATtRYaodGQKFo9mDyh70q+fJGedfrqi2XuKU1BhiLeXgg6RZrW7VEKfeqFhHAJA==} + engines: {node: '>=18.0.0'} + '@smithy/credential-provider-imds@4.0.6': resolution: {integrity: sha512-hKMWcANhUiNbCJouYkZ9V3+/Qf9pteR1dnwgdyzR09R4ODEYx8BbUysHwRSyex4rZ9zapddZhLFTnT4ZijR4pw==} engines: {node: '>=18.0.0'} @@ -4210,6 +4214,10 @@ packages: resolution: {integrity: sha512-BG3KSmsx9A//KyIfw+sqNmWFr1YBUr+TwpxFT7yPqAk0yyDh7oSNgzfNH7pS6OC099EGx2ltOULvumCFe8bcgw==} engines: {node: '>=18.0.0'} + '@smithy/fetch-http-handler@5.3.1': + resolution: {integrity: sha512-3AvYYbB+Dv5EPLqnJIAgYw/9+WzeBiUYS8B+rU0pHq5NMQMvrZmevUROS4V2GAt0jEOn9viBzPLrZE+riTNd5Q==} + engines: {node: '>=18.0.0'} + '@smithy/hash-node@4.0.4': resolution: {integrity: sha512-qnbTPUhCVnCgBp4z4BUJUhOEkVwxiEi1cyFM+Zj6o+aY8OFGxUQleKWq8ltgp3dujuhXojIvJWdoqpm6dVO3lQ==} engines: {node: '>=18.0.0'} @@ -4234,8 +4242,12 @@ packages: resolution: {integrity: sha512-jFVjuQeV8TkxaRlcCNg0GFVgg98tscsmIrIwRFeC74TIUyLE3jmY9xgc1WXrPQYRjQNK3aRoaIk6fhFRGOIoGw==} engines: {node: '>=18.0.0'} - '@smithy/middleware-retry@4.4.0': - resolution: {integrity: sha512-yaVBR0vQnOnzex45zZ8ZrPzUnX73eUC8kVFaAAbn04+6V7lPtxn56vZEBBAhgS/eqD6Zm86o6sJs6FuQVoX5qg==} + '@smithy/middleware-endpoint@4.3.1': + resolution: {integrity: sha512-JtM4SjEgImLEJVXdsbvWHYiJ9dtuKE8bqLlvkvGi96LbejDL6qnVpVxEFUximFodoQbg0Gnkyff9EKUhFhVJFw==} + engines: {node: '>=18.0.0'} + + '@smithy/middleware-retry@4.4.1': + resolution: {integrity: sha512-wXxS4ex8cJJteL0PPQmWYkNi9QKDWZIpsndr0wZI2EL+pSSvA/qqxXU60gBOJoIc2YgtZSWY/PE86qhKCCKP1w==} engines: {node: '>=18.0.0'} '@smithy/middleware-serde@4.2.0': @@ -4286,6 +4298,10 @@ packages: resolution: {integrity: sha512-3BDx/aCCPf+kkinYf5QQhdQ9UAGihgOVqI3QO5xQfSaIWvUE4KYLtiGRWsNe1SR7ijXC0QEPqofVp5Sb0zC8xQ==} engines: {node: '>=18.0.0'} + '@smithy/smithy-client@4.7.1': + resolution: {integrity: sha512-WXVbiyNf/WOS/RHUoFMkJ6leEVpln5ojCjNBnzoZeMsnCg3A0BRhLK3WYc4V7PmYcYPZh9IYzzAg9XcNSzYxYQ==} + engines: {node: '>=18.0.0'} + '@smithy/types@4.6.0': resolution: {integrity: sha512-4lI9C8NzRPOv66FaY1LL1O/0v0aLVrq/mXP/keUa9mJOApEeae43LsLd2kZRUJw91gxOQfLIrV3OvqPgWz1YsA==} engines: {node: '>=18.0.0'} @@ -4298,6 +4314,10 @@ packages: resolution: {integrity: sha512-+erInz8WDv5KPe7xCsJCp+1WCjSbah9gWcmUXc9NqmhyPx59tf7jqFz+za1tRG1Y5KM1Cy1rWCcGypylFp4mvA==} engines: {node: '>=18.0.0'} + '@smithy/util-base64@4.3.0': + resolution: {integrity: sha512-GkXZ59JfyxsIwNTWFnjmFEI8kZpRNIBfxKjv09+nkAWPt/4aGaEWMM04m4sxgNVWkbt2MdSvE3KF/PfX4nFedQ==} + engines: {node: '>=18.0.0'} + '@smithy/util-body-length-browser@4.2.0': resolution: {integrity: sha512-Fkoh/I76szMKJnBXWPdFkQJl2r9SjPt3cMzLdOB6eJ4Pnpas8hVoWPYemX/peO0yrrvldgCUVJqOAjUrOLjbxg==} engines: {node: '>=18.0.0'} @@ -4346,6 +4366,10 @@ packages: resolution: {integrity: sha512-vtO7ktbixEcrVzMRmpQDnw/Ehr9UWjBvSJ9fyAbadKkC4w5Cm/4lMO8cHz8Ysb8uflvQUNRcuux/oNHKPXkffg==} engines: {node: '>=18.0.0'} + '@smithy/util-stream@4.5.0': + resolution: {integrity: sha512-0TD5M5HCGu5diEvZ/O/WquSjhJPasqv7trjoqHyWjNh/FBeBl7a0ztl9uFMOsauYtRfd8jvpzIAQhDHbx+nvZw==} + engines: {node: '>=18.0.0'} + '@smithy/util-uri-escape@4.2.0': resolution: {integrity: sha512-igZpCKV9+E/Mzrpq6YacdTQ0qTiLm85gD6N/IrmyDvQFA4UnU3d5g3m8tMT/6zG/vVkWSU+VxeUyGonL62DuxA==} engines: {node: '>=18.0.0'} @@ -5526,10 +5550,6 @@ packages: resolution: {integrity: sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==} engines: {node: '>=10'} - ansi-styles@6.2.1: - resolution: {integrity: sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==} - engines: {node: '>=12'} - ansi-styles@6.2.3: resolution: {integrity: sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==} engines: {node: '>=12'} @@ -7176,8 +7196,8 @@ packages: resolution: {integrity: sha512-bO3y10YikuUwUuDUQRM4KfwNkKhnpVO7IPdbsrejwN9/AABJzzTQ4GeHwyzNSrVO+tEH3/Np255a3sVZpZDjvg==} engines: {node: '>=8.0.0'} - electron@38.2.1: - resolution: {integrity: sha512-P4pE2RpRg3kM8IeOK+heg6iAxR5wcXnNHrbVchn7M3GBnYAhjfJRkROusdOro5PlKzdtfKjesbbqaG4MqQXccg==} + electron@38.2.2: + resolution: {integrity: sha512-OXSaVNXDlonXDjMRsFNQo1j5tzTKwKXh5/m46IjAFccBcZJZMISI+EjSI07oexIuhvKM8AZLuFuihVn4YjWWrA==} engines: {node: '>= 12.20.55'} hasBin: true @@ -7407,8 +7427,8 @@ packages: peerDependencies: eslint: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0 - eslint-plugin-react-hooks@6.1.1: - resolution: {integrity: sha512-St9EKZzOAQF704nt2oJvAKZHjhrpg25ClQoaAlHmPZuajFldVLqRDW4VBNAS01NzeiQF0m0qhG1ZA807K6aVaQ==} + eslint-plugin-react-hooks@7.0.0: + resolution: {integrity: sha512-fNXaOwvKwq2+pXiRpXc825Vd63+KM4DLL40Rtlycb8m7fYpp6efrTp1sa6ZbP/Ap58K2bEKFXRmhURE+CJAQWw==} engines: {node: '>=18'} peerDependencies: eslint: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0 @@ -8209,6 +8229,12 @@ packages: resolution: {integrity: sha512-jOiHyAZsmnr8LqoPGmCjYAaiuWwjAPLgY8ZX2XrmHawt99/u1y6RgrZMTeoPfpUbV96HOalYgz1qzkRbw54Pmg==} engines: {node: '>=18.0.0'} + hermes-estree@0.25.1: + resolution: {integrity: sha512-0wUoCcLp+5Ev5pDW2OriHC2MJCbwLwuRx+gAqMTOkGKJJiBCLjtrvy4PWUGn6MIVefecRpzoOZ/UV6iGdOr+Cw==} + + hermes-parser@0.25.1: + resolution: {integrity: sha512-6pEjquH3rqaI6cYAXYPcz9MS4rY6R4ngRgrgfDshRptUZIc3lw0MCIJIGDj9++mfySOuPTHB4nrSW99BCvOPIA==} + highlight.js@11.11.1: resolution: {integrity: sha512-Xwwo44whKBVCYoliBQwaPvtd/2tYFkRQtXDWj1nackaV2JPXx3L0+Jvd8/qCJ2p+ML0/XVkJ2q+Mr+UVdpJK5w==} engines: {node: '>=12.0.0'} @@ -9372,8 +9398,8 @@ packages: markdown-table@3.0.4: resolution: {integrity: sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw==} - marked@16.3.0: - resolution: {integrity: sha512-K3UxuKu6l6bmA5FUwYho8CfJBlsUWAooKtdGgMcERSpF7gcBUrCGsLH7wDaaNOzwq18JzSUDyoEb/YsrqMac3w==} + marked@16.4.0: + resolution: {integrity: sha512-CTPAcRBq57cn3R8n3hwc2REddc28hjR7RzDXQ+lXLmMJYqn20BaI2cGw6QjgZGIgVfp2Wdfw4aMzgNteQ6qJgQ==} engines: {node: '>= 20'} hasBin: true @@ -9621,8 +9647,8 @@ packages: resolution: {integrity: sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==} engines: {node: '>=10'} - mind-elixir@5.1.1: - resolution: {integrity: sha512-Ao5VCby3iqyd80ReErCbb5uZLL7Zs/Nh+GpmmtUCH4WnmRvmlrdz0l3KNha+iHFeTlkxG27MbN9iPyxkTWWrRA==} + mind-elixir@5.3.2: + resolution: {integrity: sha512-dAhsRELKAmrs0UFTrCFeItrCEi0vtslr1kKkeBfu5FSDkuuu4U9imBcHdkeYnC0rUVfXe5Henirmz7yTkcdVVg==} mini-css-extract-plugin@2.4.7: resolution: {integrity: sha512-euWmddf0sk9Nv1O0gfeeUAvAkoSlWncNLF77C0TP2+WoPvy8mAHKOzMajcCz2dzvyt3CNgxb1obIEVFIRxaipg==} @@ -11767,14 +11793,16 @@ packages: peerDependencies: rollup: ^3.0.0||^4.0.0 - rollup-plugin-webpack-stats@2.1.5: - resolution: {integrity: sha512-dUYAd/7B7zNxosBQFK0xa2uNeBS1ESoWo8cYYeRX9kYYkI9ciHIBf2jGwAOQNg0V2KJjMmwkJEvqXfE1MColjg==} + rollup-plugin-webpack-stats@2.1.6: + resolution: {integrity: sha512-njKotmo0lWIbrTKJ5CrIPk9DuDsQziOo73rE3aQIAhecJj5o0ECBbE0vxgMor37o6TQ/IEAK8pDxzs4CqLdIJw==} engines: {node: '>=18'} peerDependencies: rolldown: ^1.0.0-beta.0 rollup: ^3.0.0 || ^4.0.0 vite: ^5.0.0 || ^6.0.0 || ^7.0.0 peerDependenciesMeta: + rolldown: + optional: true rollup: optional: true vite: @@ -13956,7 +13984,7 @@ snapshots: '@smithy/invalid-dependency': 4.0.4 '@smithy/middleware-content-length': 4.0.4 '@smithy/middleware-endpoint': 4.3.0 - '@smithy/middleware-retry': 4.4.0 + '@smithy/middleware-retry': 4.4.1 '@smithy/middleware-serde': 4.2.0 '@smithy/middleware-stack': 4.2.0 '@smithy/node-config-provider': 4.3.0 @@ -14002,7 +14030,7 @@ snapshots: '@smithy/invalid-dependency': 4.0.4 '@smithy/middleware-content-length': 4.0.4 '@smithy/middleware-endpoint': 4.3.0 - '@smithy/middleware-retry': 4.4.0 + '@smithy/middleware-retry': 4.4.1 '@smithy/middleware-serde': 4.2.0 '@smithy/middleware-stack': 4.2.0 '@smithy/node-config-provider': 4.3.0 @@ -14196,7 +14224,7 @@ snapshots: '@smithy/invalid-dependency': 4.0.4 '@smithy/middleware-content-length': 4.0.4 '@smithy/middleware-endpoint': 4.3.0 - '@smithy/middleware-retry': 4.4.0 + '@smithy/middleware-retry': 4.4.1 '@smithy/middleware-serde': 4.2.0 '@smithy/middleware-stack': 4.2.0 '@smithy/node-config-provider': 4.3.0 @@ -14661,8 +14689,6 @@ snapshots: '@ckeditor/ckeditor5-core': 47.0.0 '@ckeditor/ckeditor5-utils': 47.0.0 ckeditor5: 47.0.0(patch_hash=8331a09d41443b39ea1c784daaccfeb0da4f9065ed556e7de92e9c77edd9eb41) - transitivePeerDependencies: - - supports-color '@ckeditor/ckeditor5-code-block@47.0.0(patch_hash=2361d8caad7d6b5bddacc3a3b4aa37dbfba260b1c1b22a450413a79c1bb1ce95)': dependencies: @@ -14724,8 +14750,6 @@ snapshots: '@ckeditor/ckeditor5-utils': 47.0.0 '@ckeditor/ckeditor5-watchdog': 47.0.0 es-toolkit: 1.39.5 - transitivePeerDependencies: - - supports-color '@ckeditor/ckeditor5-dev-build-tools@43.1.0(@swc/helpers@0.5.17)(tslib@2.8.1)(typescript@5.9.3)': dependencies: @@ -15414,8 +15438,6 @@ snapshots: '@ckeditor/ckeditor5-ui': 47.0.0 '@ckeditor/ckeditor5-utils': 47.0.0 ckeditor5: 47.0.0(patch_hash=8331a09d41443b39ea1c784daaccfeb0da4f9065ed556e7de92e9c77edd9eb41) - transitivePeerDependencies: - - supports-color '@ckeditor/ckeditor5-restricted-editing@47.0.0': dependencies: @@ -15491,7 +15513,7 @@ snapshots: '@codemirror/language': 6.11.0 '@codemirror/state': 6.5.2 '@codemirror/theme-one-dark': 6.1.2 - '@codemirror/view': 6.38.4 + '@codemirror/view': 6.38.5 ckeditor5: 47.0.0(patch_hash=8331a09d41443b39ea1c784daaccfeb0da4f9065ed556e7de92e9c77edd9eb41) '@ckeditor/ckeditor5-source-editing@47.0.0': @@ -15613,6 +15635,8 @@ snapshots: '@ckeditor/ckeditor5-icons': 47.0.0 '@ckeditor/ckeditor5-ui': 47.0.0 '@ckeditor/ckeditor5-utils': 47.0.0 + transitivePeerDependencies: + - supports-color '@ckeditor/ckeditor5-upload@47.0.0': dependencies: @@ -15677,21 +15701,21 @@ snapshots: dependencies: '@codemirror/language': 6.11.0 '@codemirror/state': 6.5.2 - '@codemirror/view': 6.38.4 + '@codemirror/view': 6.38.5 '@lezer/common': 1.2.3 '@codemirror/commands@6.8.1': dependencies: '@codemirror/language': 6.11.0 '@codemirror/state': 6.5.2 - '@codemirror/view': 6.38.4 + '@codemirror/view': 6.38.5 '@lezer/common': 1.2.3 '@codemirror/commands@6.9.0': dependencies: '@codemirror/language': 6.11.0 '@codemirror/state': 6.5.2 - '@codemirror/view': 6.38.4 + '@codemirror/view': 6.38.5 '@lezer/common': 1.2.3 '@codemirror/lang-css@6.3.1': @@ -15709,7 +15733,7 @@ snapshots: '@codemirror/lang-javascript': 6.2.4 '@codemirror/language': 6.11.0 '@codemirror/state': 6.5.2 - '@codemirror/view': 6.38.4 + '@codemirror/view': 6.38.5 '@lezer/common': 1.2.3 '@lezer/css': 1.1.11 '@lezer/html': 1.3.12 @@ -15720,7 +15744,7 @@ snapshots: '@codemirror/language': 6.11.0 '@codemirror/lint': 6.8.5 '@codemirror/state': 6.5.2 - '@codemirror/view': 6.38.4 + '@codemirror/view': 6.38.5 '@lezer/common': 1.2.3 '@lezer/javascript': 1.5.1 @@ -15735,7 +15759,7 @@ snapshots: '@codemirror/lang-html': 6.4.11 '@codemirror/language': 6.11.0 '@codemirror/state': 6.5.2 - '@codemirror/view': 6.38.4 + '@codemirror/view': 6.38.5 '@lezer/common': 1.2.3 '@lezer/markdown': 1.4.3 @@ -15745,7 +15769,7 @@ snapshots: '@codemirror/lang-html': 6.4.11 '@codemirror/language': 6.11.0 '@codemirror/state': 6.5.2 - '@codemirror/view': 6.38.4 + '@codemirror/view': 6.38.5 '@lezer/common': 1.2.3 '@lezer/markdown': 1.4.3 @@ -15771,14 +15795,14 @@ snapshots: '@codemirror/autocomplete': 6.18.6 '@codemirror/language': 6.11.0 '@codemirror/state': 6.5.2 - '@codemirror/view': 6.38.4 + '@codemirror/view': 6.38.5 '@lezer/common': 1.2.3 '@lezer/xml': 1.0.6 '@codemirror/language@6.11.0': dependencies: '@codemirror/state': 6.5.2 - '@codemirror/view': 6.38.4 + '@codemirror/view': 6.38.5 '@lezer/common': 1.2.3 '@lezer/highlight': 1.2.1 '@lezer/lr': 1.4.2 @@ -15791,13 +15815,13 @@ snapshots: '@codemirror/lint@6.8.5': dependencies: '@codemirror/state': 6.5.2 - '@codemirror/view': 6.38.4 + '@codemirror/view': 6.38.5 crelt: 1.0.6 '@codemirror/search@6.5.11': dependencies: '@codemirror/state': 6.5.2 - '@codemirror/view': 6.38.4 + '@codemirror/view': 6.38.5 crelt: 1.0.6 '@codemirror/state@6.5.2': @@ -15808,10 +15832,10 @@ snapshots: dependencies: '@codemirror/language': 6.11.0 '@codemirror/state': 6.5.2 - '@codemirror/view': 6.38.4 + '@codemirror/view': 6.38.5 '@lezer/highlight': 1.2.1 - '@codemirror/view@6.38.4': + '@codemirror/view@6.38.5': dependencies: '@codemirror/state': 6.5.2 crelt: 1.0.6 @@ -15877,17 +15901,17 @@ snapshots: '@dual-bundle/import-meta-resolve@4.2.1': {} - '@electron-forge/cli@7.9.0(encoding@0.1.13)': + '@electron-forge/cli@7.10.0(encoding@0.1.13)': dependencies: - '@electron-forge/core': 7.9.0(encoding@0.1.13) - '@electron-forge/core-utils': 7.9.0 - '@electron-forge/shared-types': 7.9.0 + '@electron-forge/core': 7.10.0(encoding@0.1.13) + '@electron-forge/core-utils': 7.10.0 + '@electron-forge/shared-types': 7.10.0 '@electron/get': 3.1.0 '@inquirer/prompts': 6.0.1 '@listr2/prompt-adapter-inquirer': 2.0.22(@inquirer/prompts@6.0.1) chalk: 4.1.2 commander: 11.1.0 - debug: 4.4.1 + debug: 4.4.3(supports-color@6.0.0) fs-extra: 10.1.0 listr2: 7.0.2 log-symbols: 4.1.0 @@ -15897,9 +15921,9 @@ snapshots: - encoding - supports-color - '@electron-forge/core-utils@7.9.0': + '@electron-forge/core-utils@7.10.0': dependencies: - '@electron-forge/shared-types': 7.9.0 + '@electron-forge/shared-types': 7.10.0 '@electron/rebuild': 3.7.2 '@malept/cross-spawn-promise': 2.0.0 chalk: 4.1.2 @@ -15907,24 +15931,25 @@ snapshots: find-up: 5.0.0 fs-extra: 10.1.0 log-symbols: 4.1.0 + parse-author: 2.0.0 semver: 7.7.2 transitivePeerDependencies: - bluebird - supports-color - '@electron-forge/core@7.9.0(encoding@0.1.13)': + '@electron-forge/core@7.10.0(encoding@0.1.13)': dependencies: - '@electron-forge/core-utils': 7.9.0 - '@electron-forge/maker-base': 7.9.0 - '@electron-forge/plugin-base': 7.9.0 - '@electron-forge/publisher-base': 7.9.0 - '@electron-forge/shared-types': 7.9.0 - '@electron-forge/template-base': 7.9.0 - '@electron-forge/template-vite': 7.9.0 - '@electron-forge/template-vite-typescript': 7.9.0 - '@electron-forge/template-webpack': 7.9.0 - '@electron-forge/template-webpack-typescript': 7.9.0 - '@electron-forge/tracer': 7.9.0 + '@electron-forge/core-utils': 7.10.0 + '@electron-forge/maker-base': 7.10.0 + '@electron-forge/plugin-base': 7.10.0 + '@electron-forge/publisher-base': 7.10.0 + '@electron-forge/shared-types': 7.10.0 + '@electron-forge/template-base': 7.10.0 + '@electron-forge/template-vite': 7.10.0 + '@electron-forge/template-vite-typescript': 7.10.0 + '@electron-forge/template-webpack': 7.10.0 + '@electron-forge/template-webpack-typescript': 7.10.0 + '@electron-forge/tracer': 7.10.0 '@electron/get': 3.1.0 '@electron/packager': 18.3.6 '@electron/rebuild': 3.7.2 @@ -15953,29 +15978,29 @@ snapshots: - encoding - supports-color - '@electron-forge/maker-base@7.9.0': + '@electron-forge/maker-base@7.10.0': dependencies: - '@electron-forge/shared-types': 7.9.0 + '@electron-forge/shared-types': 7.10.0 fs-extra: 10.1.0 which: 2.0.2 transitivePeerDependencies: - bluebird - supports-color - '@electron-forge/maker-deb@7.9.0': + '@electron-forge/maker-deb@7.10.0': dependencies: - '@electron-forge/maker-base': 7.9.0 - '@electron-forge/shared-types': 7.9.0 + '@electron-forge/maker-base': 7.10.0 + '@electron-forge/shared-types': 7.10.0 optionalDependencies: electron-installer-debian: 3.2.0 transitivePeerDependencies: - bluebird - supports-color - '@electron-forge/maker-dmg@7.9.0': + '@electron-forge/maker-dmg@7.10.0': dependencies: - '@electron-forge/maker-base': 7.9.0 - '@electron-forge/shared-types': 7.9.0 + '@electron-forge/maker-base': 7.10.0 + '@electron-forge/shared-types': 7.10.0 fs-extra: 10.1.0 optionalDependencies: electron-installer-dmg: 5.0.1 @@ -15983,10 +16008,10 @@ snapshots: - bluebird - supports-color - '@electron-forge/maker-flatpak@7.9.0': + '@electron-forge/maker-flatpak@7.10.0': dependencies: - '@electron-forge/maker-base': 7.9.0 - '@electron-forge/shared-types': 7.9.0 + '@electron-forge/maker-base': 7.10.0 + '@electron-forge/shared-types': 7.10.0 fs-extra: 10.1.0 optionalDependencies: '@malept/electron-installer-flatpak': 0.11.4 @@ -15994,20 +16019,20 @@ snapshots: - bluebird - supports-color - '@electron-forge/maker-rpm@7.9.0': + '@electron-forge/maker-rpm@7.10.0': dependencies: - '@electron-forge/maker-base': 7.9.0 - '@electron-forge/shared-types': 7.9.0 + '@electron-forge/maker-base': 7.10.0 + '@electron-forge/shared-types': 7.10.0 optionalDependencies: electron-installer-redhat: 3.4.0 transitivePeerDependencies: - bluebird - supports-color - '@electron-forge/maker-squirrel@7.9.0': + '@electron-forge/maker-squirrel@7.10.0': dependencies: - '@electron-forge/maker-base': 7.9.0 - '@electron-forge/shared-types': 7.9.0 + '@electron-forge/maker-base': 7.10.0 + '@electron-forge/shared-types': 7.10.0 fs-extra: 10.1.0 optionalDependencies: electron-winstaller: 5.4.0 @@ -16015,10 +16040,10 @@ snapshots: - bluebird - supports-color - '@electron-forge/maker-zip@7.9.0': + '@electron-forge/maker-zip@7.10.0': dependencies: - '@electron-forge/maker-base': 7.9.0 - '@electron-forge/shared-types': 7.9.0 + '@electron-forge/maker-base': 7.10.0 + '@electron-forge/shared-types': 7.10.0 cross-zip: 4.0.1 fs-extra: 10.1.0 got: 11.8.6 @@ -16026,31 +16051,31 @@ snapshots: - bluebird - supports-color - '@electron-forge/plugin-auto-unpack-natives@7.9.0': + '@electron-forge/plugin-auto-unpack-natives@7.10.0': dependencies: - '@electron-forge/plugin-base': 7.9.0 - '@electron-forge/shared-types': 7.9.0 + '@electron-forge/plugin-base': 7.10.0 + '@electron-forge/shared-types': 7.10.0 transitivePeerDependencies: - bluebird - supports-color - '@electron-forge/plugin-base@7.9.0': + '@electron-forge/plugin-base@7.10.0': dependencies: - '@electron-forge/shared-types': 7.9.0 + '@electron-forge/shared-types': 7.10.0 transitivePeerDependencies: - bluebird - supports-color - '@electron-forge/publisher-base@7.9.0': + '@electron-forge/publisher-base@7.10.0': dependencies: - '@electron-forge/shared-types': 7.9.0 + '@electron-forge/shared-types': 7.10.0 transitivePeerDependencies: - bluebird - supports-color - '@electron-forge/shared-types@7.9.0': + '@electron-forge/shared-types@7.10.0': dependencies: - '@electron-forge/tracer': 7.9.0 + '@electron-forge/tracer': 7.10.0 '@electron/packager': 18.3.6 '@electron/rebuild': 3.7.2 listr2: 7.0.2 @@ -16058,10 +16083,10 @@ snapshots: - bluebird - supports-color - '@electron-forge/template-base@7.9.0': + '@electron-forge/template-base@7.10.0': dependencies: - '@electron-forge/core-utils': 7.9.0 - '@electron-forge/shared-types': 7.9.0 + '@electron-forge/core-utils': 7.10.0 + '@electron-forge/shared-types': 7.10.0 '@malept/cross-spawn-promise': 2.0.0 debug: 4.4.3(supports-color@6.0.0) fs-extra: 10.1.0 @@ -16071,43 +16096,43 @@ snapshots: - bluebird - supports-color - '@electron-forge/template-vite-typescript@7.9.0': + '@electron-forge/template-vite-typescript@7.10.0': dependencies: - '@electron-forge/shared-types': 7.9.0 - '@electron-forge/template-base': 7.9.0 + '@electron-forge/shared-types': 7.10.0 + '@electron-forge/template-base': 7.10.0 fs-extra: 10.1.0 transitivePeerDependencies: - bluebird - supports-color - '@electron-forge/template-vite@7.9.0': + '@electron-forge/template-vite@7.10.0': dependencies: - '@electron-forge/shared-types': 7.9.0 - '@electron-forge/template-base': 7.9.0 + '@electron-forge/shared-types': 7.10.0 + '@electron-forge/template-base': 7.10.0 fs-extra: 10.1.0 transitivePeerDependencies: - bluebird - supports-color - '@electron-forge/template-webpack-typescript@7.9.0': + '@electron-forge/template-webpack-typescript@7.10.0': dependencies: - '@electron-forge/shared-types': 7.9.0 - '@electron-forge/template-base': 7.9.0 + '@electron-forge/shared-types': 7.10.0 + '@electron-forge/template-base': 7.10.0 fs-extra: 10.1.0 transitivePeerDependencies: - bluebird - supports-color - '@electron-forge/template-webpack@7.9.0': + '@electron-forge/template-webpack@7.10.0': dependencies: - '@electron-forge/shared-types': 7.9.0 - '@electron-forge/template-base': 7.9.0 + '@electron-forge/shared-types': 7.10.0 + '@electron-forge/template-base': 7.10.0 fs-extra: 10.1.0 transitivePeerDependencies: - bluebird - supports-color - '@electron-forge/tracer@7.9.0': + '@electron-forge/tracer@7.10.0': dependencies: chrome-trace-event: 1.0.4 @@ -16243,9 +16268,9 @@ snapshots: transitivePeerDependencies: - supports-color - '@electron/remote@2.1.3(electron@38.2.1)': + '@electron/remote@2.1.3(electron@38.2.2)': dependencies: - electron: 38.2.1 + electron: 38.2.2 '@electron/universal@2.0.2': dependencies: @@ -16524,172 +16549,172 @@ snapshots: '@floating-ui/utils@0.2.9': {} - '@fsegurai/codemirror-theme-abcdef@6.2.2(@codemirror/language@6.11.0)(@codemirror/state@6.5.2)(@codemirror/view@6.38.4)(@lezer/highlight@1.2.1)': + '@fsegurai/codemirror-theme-abcdef@6.2.2(@codemirror/language@6.11.0)(@codemirror/state@6.5.2)(@codemirror/view@6.38.5)(@lezer/highlight@1.2.1)': dependencies: '@codemirror/language': 6.11.0 '@codemirror/state': 6.5.2 - '@codemirror/view': 6.38.4 + '@codemirror/view': 6.38.5 '@lezer/highlight': 1.2.1 - '@fsegurai/codemirror-theme-abyss@6.2.2(@codemirror/language@6.11.0)(@codemirror/state@6.5.2)(@codemirror/view@6.38.4)(@lezer/highlight@1.2.1)': + '@fsegurai/codemirror-theme-abyss@6.2.2(@codemirror/language@6.11.0)(@codemirror/state@6.5.2)(@codemirror/view@6.38.5)(@lezer/highlight@1.2.1)': dependencies: '@codemirror/language': 6.11.0 '@codemirror/state': 6.5.2 - '@codemirror/view': 6.38.4 + '@codemirror/view': 6.38.5 '@lezer/highlight': 1.2.1 - '@fsegurai/codemirror-theme-android-studio@6.2.2(@codemirror/language@6.11.0)(@codemirror/state@6.5.2)(@codemirror/view@6.38.4)(@lezer/highlight@1.2.1)': + '@fsegurai/codemirror-theme-android-studio@6.2.2(@codemirror/language@6.11.0)(@codemirror/state@6.5.2)(@codemirror/view@6.38.5)(@lezer/highlight@1.2.1)': dependencies: '@codemirror/language': 6.11.0 '@codemirror/state': 6.5.2 - '@codemirror/view': 6.38.4 + '@codemirror/view': 6.38.5 '@lezer/highlight': 1.2.1 - '@fsegurai/codemirror-theme-andromeda@6.2.2(@codemirror/language@6.11.0)(@codemirror/state@6.5.2)(@codemirror/view@6.38.4)(@lezer/highlight@1.2.1)': + '@fsegurai/codemirror-theme-andromeda@6.2.2(@codemirror/language@6.11.0)(@codemirror/state@6.5.2)(@codemirror/view@6.38.5)(@lezer/highlight@1.2.1)': dependencies: '@codemirror/language': 6.11.0 '@codemirror/state': 6.5.2 - '@codemirror/view': 6.38.4 + '@codemirror/view': 6.38.5 '@lezer/highlight': 1.2.1 - '@fsegurai/codemirror-theme-basic-dark@6.2.2(@codemirror/language@6.11.0)(@codemirror/state@6.5.2)(@codemirror/view@6.38.4)(@lezer/highlight@1.2.1)': + '@fsegurai/codemirror-theme-basic-dark@6.2.2(@codemirror/language@6.11.0)(@codemirror/state@6.5.2)(@codemirror/view@6.38.5)(@lezer/highlight@1.2.1)': dependencies: '@codemirror/language': 6.11.0 '@codemirror/state': 6.5.2 - '@codemirror/view': 6.38.4 + '@codemirror/view': 6.38.5 '@lezer/highlight': 1.2.1 - '@fsegurai/codemirror-theme-basic-light@6.2.2(@codemirror/language@6.11.0)(@codemirror/state@6.5.2)(@codemirror/view@6.38.4)(@lezer/highlight@1.2.1)': + '@fsegurai/codemirror-theme-basic-light@6.2.2(@codemirror/language@6.11.0)(@codemirror/state@6.5.2)(@codemirror/view@6.38.5)(@lezer/highlight@1.2.1)': dependencies: '@codemirror/language': 6.11.0 '@codemirror/state': 6.5.2 - '@codemirror/view': 6.38.4 + '@codemirror/view': 6.38.5 '@lezer/highlight': 1.2.1 - '@fsegurai/codemirror-theme-cobalt2@6.0.2(@codemirror/language@6.11.0)(@codemirror/state@6.5.2)(@codemirror/view@6.38.4)(@lezer/highlight@1.2.1)': + '@fsegurai/codemirror-theme-cobalt2@6.0.2(@codemirror/language@6.11.0)(@codemirror/state@6.5.2)(@codemirror/view@6.38.5)(@lezer/highlight@1.2.1)': dependencies: '@codemirror/language': 6.11.0 '@codemirror/state': 6.5.2 - '@codemirror/view': 6.38.4 + '@codemirror/view': 6.38.5 '@lezer/highlight': 1.2.1 - '@fsegurai/codemirror-theme-forest@6.2.2(@codemirror/language@6.11.0)(@codemirror/state@6.5.2)(@codemirror/view@6.38.4)(@lezer/highlight@1.2.1)': + '@fsegurai/codemirror-theme-forest@6.2.2(@codemirror/language@6.11.0)(@codemirror/state@6.5.2)(@codemirror/view@6.38.5)(@lezer/highlight@1.2.1)': dependencies: '@codemirror/language': 6.11.0 '@codemirror/state': 6.5.2 - '@codemirror/view': 6.38.4 + '@codemirror/view': 6.38.5 '@lezer/highlight': 1.2.1 - '@fsegurai/codemirror-theme-github-dark@6.2.2(@codemirror/language@6.11.0)(@codemirror/state@6.5.2)(@codemirror/view@6.38.4)(@lezer/highlight@1.2.1)': + '@fsegurai/codemirror-theme-github-dark@6.2.2(@codemirror/language@6.11.0)(@codemirror/state@6.5.2)(@codemirror/view@6.38.5)(@lezer/highlight@1.2.1)': dependencies: '@codemirror/language': 6.11.0 '@codemirror/state': 6.5.2 - '@codemirror/view': 6.38.4 + '@codemirror/view': 6.38.5 '@lezer/highlight': 1.2.1 - '@fsegurai/codemirror-theme-github-light@6.2.2(@codemirror/language@6.11.0)(@codemirror/state@6.5.2)(@codemirror/view@6.38.4)(@lezer/highlight@1.2.1)': + '@fsegurai/codemirror-theme-github-light@6.2.2(@codemirror/language@6.11.0)(@codemirror/state@6.5.2)(@codemirror/view@6.38.5)(@lezer/highlight@1.2.1)': dependencies: '@codemirror/language': 6.11.0 '@codemirror/state': 6.5.2 - '@codemirror/view': 6.38.4 + '@codemirror/view': 6.38.5 '@lezer/highlight': 1.2.1 - '@fsegurai/codemirror-theme-gruvbox-dark@6.2.2(@codemirror/language@6.11.0)(@codemirror/state@6.5.2)(@codemirror/view@6.38.4)(@lezer/highlight@1.2.1)': + '@fsegurai/codemirror-theme-gruvbox-dark@6.2.2(@codemirror/language@6.11.0)(@codemirror/state@6.5.2)(@codemirror/view@6.38.5)(@lezer/highlight@1.2.1)': dependencies: '@codemirror/language': 6.11.0 '@codemirror/state': 6.5.2 - '@codemirror/view': 6.38.4 + '@codemirror/view': 6.38.5 '@lezer/highlight': 1.2.1 - '@fsegurai/codemirror-theme-gruvbox-light@6.2.2(@codemirror/language@6.11.0)(@codemirror/state@6.5.2)(@codemirror/view@6.38.4)(@lezer/highlight@1.2.1)': + '@fsegurai/codemirror-theme-gruvbox-light@6.2.2(@codemirror/language@6.11.0)(@codemirror/state@6.5.2)(@codemirror/view@6.38.5)(@lezer/highlight@1.2.1)': dependencies: '@codemirror/language': 6.11.0 '@codemirror/state': 6.5.2 - '@codemirror/view': 6.38.4 + '@codemirror/view': 6.38.5 '@lezer/highlight': 1.2.1 - '@fsegurai/codemirror-theme-material-dark@6.2.2(@codemirror/language@6.11.0)(@codemirror/state@6.5.2)(@codemirror/view@6.38.4)(@lezer/highlight@1.2.1)': + '@fsegurai/codemirror-theme-material-dark@6.2.2(@codemirror/language@6.11.0)(@codemirror/state@6.5.2)(@codemirror/view@6.38.5)(@lezer/highlight@1.2.1)': dependencies: '@codemirror/language': 6.11.0 '@codemirror/state': 6.5.2 - '@codemirror/view': 6.38.4 + '@codemirror/view': 6.38.5 '@lezer/highlight': 1.2.1 - '@fsegurai/codemirror-theme-material-light@6.2.2(@codemirror/language@6.11.0)(@codemirror/state@6.5.2)(@codemirror/view@6.38.4)(@lezer/highlight@1.2.1)': + '@fsegurai/codemirror-theme-material-light@6.2.2(@codemirror/language@6.11.0)(@codemirror/state@6.5.2)(@codemirror/view@6.38.5)(@lezer/highlight@1.2.1)': dependencies: '@codemirror/language': 6.11.0 '@codemirror/state': 6.5.2 - '@codemirror/view': 6.38.4 + '@codemirror/view': 6.38.5 '@lezer/highlight': 1.2.1 - '@fsegurai/codemirror-theme-monokai@6.2.2(@codemirror/language@6.11.0)(@codemirror/state@6.5.2)(@codemirror/view@6.38.4)(@lezer/highlight@1.2.1)': + '@fsegurai/codemirror-theme-monokai@6.2.2(@codemirror/language@6.11.0)(@codemirror/state@6.5.2)(@codemirror/view@6.38.5)(@lezer/highlight@1.2.1)': dependencies: '@codemirror/language': 6.11.0 '@codemirror/state': 6.5.2 - '@codemirror/view': 6.38.4 + '@codemirror/view': 6.38.5 '@lezer/highlight': 1.2.1 - '@fsegurai/codemirror-theme-nord@6.2.2(@codemirror/language@6.11.0)(@codemirror/state@6.5.2)(@codemirror/view@6.38.4)(@lezer/highlight@1.2.1)': + '@fsegurai/codemirror-theme-nord@6.2.2(@codemirror/language@6.11.0)(@codemirror/state@6.5.2)(@codemirror/view@6.38.5)(@lezer/highlight@1.2.1)': dependencies: '@codemirror/language': 6.11.0 '@codemirror/state': 6.5.2 - '@codemirror/view': 6.38.4 + '@codemirror/view': 6.38.5 '@lezer/highlight': 1.2.1 - '@fsegurai/codemirror-theme-palenight@6.2.2(@codemirror/language@6.11.0)(@codemirror/state@6.5.2)(@codemirror/view@6.38.4)(@lezer/highlight@1.2.1)': + '@fsegurai/codemirror-theme-palenight@6.2.2(@codemirror/language@6.11.0)(@codemirror/state@6.5.2)(@codemirror/view@6.38.5)(@lezer/highlight@1.2.1)': dependencies: '@codemirror/language': 6.11.0 '@codemirror/state': 6.5.2 - '@codemirror/view': 6.38.4 + '@codemirror/view': 6.38.5 '@lezer/highlight': 1.2.1 - '@fsegurai/codemirror-theme-solarized-dark@6.2.2(@codemirror/language@6.11.0)(@codemirror/state@6.5.2)(@codemirror/view@6.38.4)(@lezer/highlight@1.2.1)': + '@fsegurai/codemirror-theme-solarized-dark@6.2.2(@codemirror/language@6.11.0)(@codemirror/state@6.5.2)(@codemirror/view@6.38.5)(@lezer/highlight@1.2.1)': dependencies: '@codemirror/language': 6.11.0 '@codemirror/state': 6.5.2 - '@codemirror/view': 6.38.4 + '@codemirror/view': 6.38.5 '@lezer/highlight': 1.2.1 - '@fsegurai/codemirror-theme-solarized-light@6.2.2(@codemirror/language@6.11.0)(@codemirror/state@6.5.2)(@codemirror/view@6.38.4)(@lezer/highlight@1.2.1)': + '@fsegurai/codemirror-theme-solarized-light@6.2.2(@codemirror/language@6.11.0)(@codemirror/state@6.5.2)(@codemirror/view@6.38.5)(@lezer/highlight@1.2.1)': dependencies: '@codemirror/language': 6.11.0 '@codemirror/state': 6.5.2 - '@codemirror/view': 6.38.4 + '@codemirror/view': 6.38.5 '@lezer/highlight': 1.2.1 - '@fsegurai/codemirror-theme-tokyo-night-day@6.2.2(@codemirror/language@6.11.0)(@codemirror/state@6.5.2)(@codemirror/view@6.38.4)(@lezer/highlight@1.2.1)': + '@fsegurai/codemirror-theme-tokyo-night-day@6.2.2(@codemirror/language@6.11.0)(@codemirror/state@6.5.2)(@codemirror/view@6.38.5)(@lezer/highlight@1.2.1)': dependencies: '@codemirror/language': 6.11.0 '@codemirror/state': 6.5.2 - '@codemirror/view': 6.38.4 + '@codemirror/view': 6.38.5 '@lezer/highlight': 1.2.1 - '@fsegurai/codemirror-theme-tokyo-night-storm@6.2.2(@codemirror/language@6.11.0)(@codemirror/state@6.5.2)(@codemirror/view@6.38.4)(@lezer/highlight@1.2.1)': + '@fsegurai/codemirror-theme-tokyo-night-storm@6.2.2(@codemirror/language@6.11.0)(@codemirror/state@6.5.2)(@codemirror/view@6.38.5)(@lezer/highlight@1.2.1)': dependencies: '@codemirror/language': 6.11.0 '@codemirror/state': 6.5.2 - '@codemirror/view': 6.38.4 + '@codemirror/view': 6.38.5 '@lezer/highlight': 1.2.1 - '@fsegurai/codemirror-theme-volcano@6.2.2(@codemirror/language@6.11.0)(@codemirror/state@6.5.2)(@codemirror/view@6.38.4)(@lezer/highlight@1.2.1)': + '@fsegurai/codemirror-theme-volcano@6.2.2(@codemirror/language@6.11.0)(@codemirror/state@6.5.2)(@codemirror/view@6.38.5)(@lezer/highlight@1.2.1)': dependencies: '@codemirror/language': 6.11.0 '@codemirror/state': 6.5.2 - '@codemirror/view': 6.38.4 + '@codemirror/view': 6.38.5 '@lezer/highlight': 1.2.1 - '@fsegurai/codemirror-theme-vscode-dark@6.2.2(@codemirror/language@6.11.0)(@codemirror/state@6.5.2)(@codemirror/view@6.38.4)(@lezer/highlight@1.2.1)': + '@fsegurai/codemirror-theme-vscode-dark@6.2.2(@codemirror/language@6.11.0)(@codemirror/state@6.5.2)(@codemirror/view@6.38.5)(@lezer/highlight@1.2.1)': dependencies: '@codemirror/language': 6.11.0 '@codemirror/state': 6.5.2 - '@codemirror/view': 6.38.4 + '@codemirror/view': 6.38.5 '@lezer/highlight': 1.2.1 - '@fsegurai/codemirror-theme-vscode-light@6.2.2(@codemirror/language@6.11.0)(@codemirror/state@6.5.2)(@codemirror/view@6.38.4)(@lezer/highlight@1.2.1)': + '@fsegurai/codemirror-theme-vscode-light@6.2.2(@codemirror/language@6.11.0)(@codemirror/state@6.5.2)(@codemirror/view@6.38.5)(@lezer/highlight@1.2.1)': dependencies: '@codemirror/language': 6.11.0 '@codemirror/state': 6.5.2 - '@codemirror/view': 6.38.4 + '@codemirror/view': 6.38.5 '@lezer/highlight': 1.2.1 '@fullcalendar/core@6.1.19': @@ -17351,9 +17376,9 @@ snapshots: '@microsoft/tsdoc@0.15.1': {} - '@mind-elixir/node-menu@5.0.0(mind-elixir@5.1.1)': + '@mind-elixir/node-menu@5.0.0(mind-elixir@5.3.2)': dependencies: - mind-elixir: 5.1.1 + mind-elixir: 5.3.2 '@mixmark-io/domino@2.2.0': {} @@ -17493,9 +17518,11 @@ snapshots: '@open-draft/until@2.1.0': optional: true - '@oxc-project/runtime@0.77.3': {} + '@oxc-project/runtime@0.77.3': + optional: true - '@oxc-project/types@0.77.3': {} + '@oxc-project/types@0.77.3': + optional: true '@panva/asn1.js@1.0.0': {} @@ -17931,29 +17958,29 @@ snapshots: '@radix-ui/rect@1.1.0': {} - '@replit/codemirror-indentation-markers@6.5.3(@codemirror/language@6.11.0)(@codemirror/state@6.5.2)(@codemirror/view@6.38.4)': + '@replit/codemirror-indentation-markers@6.5.3(@codemirror/language@6.11.0)(@codemirror/state@6.5.2)(@codemirror/view@6.38.5)': dependencies: '@codemirror/language': 6.11.0 '@codemirror/state': 6.5.2 - '@codemirror/view': 6.38.4 + '@codemirror/view': 6.38.5 - '@replit/codemirror-lang-nix@6.0.1(@codemirror/autocomplete@6.18.6)(@codemirror/language@6.11.0)(@codemirror/state@6.5.2)(@codemirror/view@6.38.4)(@lezer/common@1.2.3)(@lezer/highlight@1.2.1)(@lezer/lr@1.4.2)': + '@replit/codemirror-lang-nix@6.0.1(@codemirror/autocomplete@6.18.6)(@codemirror/language@6.11.0)(@codemirror/state@6.5.2)(@codemirror/view@6.38.5)(@lezer/common@1.2.3)(@lezer/highlight@1.2.1)(@lezer/lr@1.4.2)': dependencies: '@codemirror/autocomplete': 6.18.6 '@codemirror/language': 6.11.0 '@codemirror/state': 6.5.2 - '@codemirror/view': 6.38.4 + '@codemirror/view': 6.38.5 '@lezer/common': 1.2.3 '@lezer/highlight': 1.2.1 '@lezer/lr': 1.4.2 - '@replit/codemirror-vim@6.3.0(@codemirror/commands@6.9.0)(@codemirror/language@6.11.0)(@codemirror/search@6.5.11)(@codemirror/state@6.5.2)(@codemirror/view@6.38.4)': + '@replit/codemirror-vim@6.3.0(@codemirror/commands@6.9.0)(@codemirror/language@6.11.0)(@codemirror/search@6.5.11)(@codemirror/state@6.5.2)(@codemirror/view@6.38.5)': dependencies: '@codemirror/commands': 6.9.0 '@codemirror/language': 6.11.0 '@codemirror/search': 6.5.11 '@codemirror/state': 6.5.2 - '@codemirror/view': 6.38.4 + '@codemirror/view': 6.38.5 '@rolldown/binding-android-arm64@1.0.0-beta.29': optional: true @@ -17999,7 +18026,8 @@ snapshots: '@rolldown/binding-win32-x64-msvc@1.0.0-beta.29': optional: true - '@rolldown/pluginutils@1.0.0-beta.29': {} + '@rolldown/pluginutils@1.0.0-beta.29': + optional: true '@rollup/plugin-commonjs@25.0.8(rollup@4.40.0)': dependencies: @@ -18304,6 +18332,19 @@ snapshots: '@smithy/uuid': 1.1.0 tslib: 2.8.1 + '@smithy/core@3.15.0': + dependencies: + '@smithy/middleware-serde': 4.2.0 + '@smithy/protocol-http': 5.3.0 + '@smithy/types': 4.6.0 + '@smithy/util-base64': 4.3.0 + '@smithy/util-body-length-browser': 4.2.0 + '@smithy/util-middleware': 4.2.0 + '@smithy/util-stream': 4.5.0 + '@smithy/util-utf8': 4.2.0 + '@smithy/uuid': 1.1.0 + tslib: 2.8.1 + '@smithy/credential-provider-imds@4.0.6': dependencies: '@smithy/node-config-provider': 4.3.0 @@ -18350,6 +18391,14 @@ snapshots: '@smithy/util-base64': 4.2.0 tslib: 2.8.1 + '@smithy/fetch-http-handler@5.3.1': + dependencies: + '@smithy/protocol-http': 5.3.0 + '@smithy/querystring-builder': 4.2.0 + '@smithy/types': 4.6.0 + '@smithy/util-base64': 4.3.0 + tslib: 2.8.1 + '@smithy/hash-node@4.0.4': dependencies: '@smithy/types': 4.6.0 @@ -18387,12 +18436,23 @@ snapshots: '@smithy/util-middleware': 4.2.0 tslib: 2.8.1 - '@smithy/middleware-retry@4.4.0': + '@smithy/middleware-endpoint@4.3.1': + dependencies: + '@smithy/core': 3.15.0 + '@smithy/middleware-serde': 4.2.0 + '@smithy/node-config-provider': 4.3.0 + '@smithy/shared-ini-file-loader': 4.3.0 + '@smithy/types': 4.6.0 + '@smithy/url-parser': 4.2.0 + '@smithy/util-middleware': 4.2.0 + tslib: 2.8.1 + + '@smithy/middleware-retry@4.4.1': dependencies: '@smithy/node-config-provider': 4.3.0 '@smithy/protocol-http': 5.3.0 '@smithy/service-error-classification': 4.2.0 - '@smithy/smithy-client': 4.7.0 + '@smithy/smithy-client': 4.7.1 '@smithy/types': 4.6.0 '@smithy/util-middleware': 4.2.0 '@smithy/util-retry': 4.2.0 @@ -18476,6 +18536,16 @@ snapshots: '@smithy/util-stream': 4.4.0 tslib: 2.8.1 + '@smithy/smithy-client@4.7.1': + dependencies: + '@smithy/core': 3.15.0 + '@smithy/middleware-endpoint': 4.3.1 + '@smithy/middleware-stack': 4.2.0 + '@smithy/protocol-http': 5.3.0 + '@smithy/types': 4.6.0 + '@smithy/util-stream': 4.5.0 + tslib: 2.8.1 + '@smithy/types@4.6.0': dependencies: tslib: 2.8.1 @@ -18492,6 +18562,12 @@ snapshots: '@smithy/util-utf8': 4.2.0 tslib: 2.8.1 + '@smithy/util-base64@4.3.0': + dependencies: + '@smithy/util-buffer-from': 4.2.0 + '@smithy/util-utf8': 4.2.0 + tslib: 2.8.1 + '@smithy/util-body-length-browser@4.2.0': dependencies: tslib: 2.8.1 @@ -18564,6 +18640,17 @@ snapshots: '@smithy/util-utf8': 4.2.0 tslib: 2.8.1 + '@smithy/util-stream@4.5.0': + dependencies: + '@smithy/fetch-http-handler': 5.3.1 + '@smithy/node-http-handler': 4.3.0 + '@smithy/types': 4.6.0 + '@smithy/util-base64': 4.3.0 + '@smithy/util-buffer-from': 4.2.0 + '@smithy/util-hex-encoding': 4.2.0 + '@smithy/util-utf8': 4.2.0 + tslib: 2.8.1 + '@smithy/util-uri-escape@4.2.0': dependencies: tslib: 2.8.1 @@ -19994,11 +20081,10 @@ snapshots: ansi-styles@5.2.0: {} - ansi-styles@6.2.1: {} - ansi-styles@6.2.3: {} - ansis@4.2.0: {} + ansis@4.2.0: + optional: true any-base@1.1.0: {} @@ -22141,7 +22227,7 @@ snapshots: - supports-color optional: true - electron@38.2.1: + electron@38.2.2: dependencies: '@electron/get': 2.0.3 '@types/node': 22.18.8 @@ -22531,11 +22617,12 @@ snapshots: dependencies: eslint: 9.37.0(jiti@2.6.1) - eslint-plugin-react-hooks@6.1.1(eslint@9.37.0(jiti@2.6.1)): + eslint-plugin-react-hooks@7.0.0(eslint@9.37.0(jiti@2.6.1)): dependencies: '@babel/core': 7.28.0 '@babel/parser': 7.28.4 eslint: 9.37.0(jiti@2.6.1) + hermes-parser: 0.25.1 zod: 3.24.4 zod-validation-error: 3.5.3(zod@3.24.4) transitivePeerDependencies: @@ -23612,6 +23699,12 @@ snapshots: helmet@8.1.0: {} + hermes-estree@0.25.1: {} + + hermes-parser@0.25.1: + dependencies: + hermes-estree: 0.25.1 + highlight.js@11.11.1: {} highlightjs-cobol@0.3.3: @@ -24918,7 +25011,7 @@ snapshots: markdown-table@3.0.4: {} - marked@16.3.0: {} + marked@16.4.0: {} matcher@3.0.0: dependencies: @@ -25113,7 +25206,7 @@ snapshots: katex: 0.16.23 khroma: 2.1.0 lodash-es: 4.17.21 - marked: 16.3.0 + marked: 16.4.0 roughjs: 4.6.6 stylis: 4.3.6 ts-dedent: 2.2.0 @@ -25352,7 +25445,7 @@ snapshots: mimic-response@3.1.0: {} - mind-elixir@5.1.1: {} + mind-elixir@5.3.2: {} mini-css-extract-plugin@2.4.7(webpack@5.101.3(@swc/core@1.11.29(@swc/helpers@0.5.17))(esbuild@0.25.10)): dependencies: @@ -27780,6 +27873,7 @@ snapshots: '@rolldown/binding-win32-arm64-msvc': 1.0.0-beta.29 '@rolldown/binding-win32-ia32-msvc': 1.0.0-beta.29 '@rolldown/binding-win32-x64-msvc': 1.0.0-beta.29 + optional: true rollup-plugin-stats@1.5.1(rolldown@1.0.0-beta.29)(rollup@4.52.0)(vite@7.1.9(@types/node@22.18.8)(jiti@2.6.1)(less@4.1.3)(lightningcss@1.30.1)(sass-embedded@1.91.0)(sass@1.91.0)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)): optionalDependencies: @@ -27814,11 +27908,11 @@ snapshots: '@rollup/pluginutils': 5.1.4(rollup@4.40.0) rollup: 4.40.0 - rollup-plugin-webpack-stats@2.1.5(rolldown@1.0.0-beta.29)(rollup@4.52.0)(vite@7.1.9(@types/node@22.18.8)(jiti@2.6.1)(less@4.1.3)(lightningcss@1.30.1)(sass-embedded@1.91.0)(sass@1.91.0)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)): + rollup-plugin-webpack-stats@2.1.6(rolldown@1.0.0-beta.29)(rollup@4.52.0)(vite@7.1.9(@types/node@22.18.8)(jiti@2.6.1)(less@4.1.3)(lightningcss@1.30.1)(sass-embedded@1.91.0)(sass@1.91.0)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)): dependencies: - rolldown: 1.0.0-beta.29 rollup-plugin-stats: 1.5.1(rolldown@1.0.0-beta.29)(rollup@4.52.0)(vite@7.1.9(@types/node@22.18.8)(jiti@2.6.1)(less@4.1.3)(lightningcss@1.30.1)(sass-embedded@1.91.0)(sass@1.91.0)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)) optionalDependencies: + rolldown: 1.0.0-beta.29 rollup: 4.52.0 vite: 7.1.9(@types/node@22.18.8)(jiti@2.6.1)(less@4.1.3)(lightningcss@1.30.1)(sass-embedded@1.91.0)(sass@1.91.0)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1) @@ -30249,7 +30343,7 @@ snapshots: wrap-ansi@8.1.0: dependencies: - ansi-styles: 6.2.1 + ansi-styles: 6.2.3 string-width: 5.1.2 strip-ansi: 7.1.2