diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..6398665 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,112 @@ +# See https://pre-commit.com for more information +# See https://pre-commit.com/hooks.html for more hooks +default_language_version: + python: python3.11 +default_install_hook_types: + - pre-commit + - commit-msg +repos: +- repo: https://github.com/compilerla/conventional-pre-commit + rev: v4.0.0 + hooks: + - id: conventional-pre-commit + stages: [commit-msg] + args: [] +- repo: https://github.com/pre-commit/pre-commit-hooks + rev: v5.0.0 + hooks: + - id: no-commit-to-branch + - id: check-executables-have-shebangs + - id: check-ast + - id: check-toml + - id: trailing-whitespace + - id: end-of-file-fixer + - id: check-yaml + - id: check-added-large-files + args: + - --maxkb=999 + - id: check-case-conflict + - id: check-json + - id: check-merge-conflict + - id: check-symlinks + - id: pretty-format-json + args: + - --autofix + exclude: ^rmcryptpad/ui/ +- repo: https://github.com/psf/black + rev: 25.1.0 + hooks: + - id: black + language_version: python3.11 + files: ^rmcryptpad/ + types: [python] +- repo: https://github.com/pre-commit/mirrors-mypy + rev: v1.15.0 + hooks: + - id: mypy + language: system + args: ["--strict"] + files: ^rmcryptpad/ + types: [python] +- repo: https://github.com/pycqa/pylint + rev: v3.3.6 + hooks: + - id: pylint + language: system + files: ^rmcryptpad/ + types: [python] + args: ["--rcfile=rmcryptpad/pyproject.toml", "--source-roots=rmcryptpad/src"] +- repo: https://github.com/Lucas-C/pre-commit-hooks + rev: v1.5.5 + hooks: + - id: forbid-crlf + - id: remove-crlf + - id: forbid-tabs + - id: remove-tabs +- repo: https://github.com/PyCQA/bandit + rev: 1.8.3 + hooks: + - id: bandit + args: ["--skip=B101"] + files: ^rmcryptpad/ + types: [python] +- repo: https://github.com/Lucas-C/pre-commit-hooks-markup + rev: v1.0.1 + hooks: + - id: rst-linter +- repo: https://github.com/Yelp/detect-secrets + rev: v1.5.0 + hooks: + - id: detect-secrets + language: system + exclude: "poetry\\.lock|tests/data/.*|rmcryptpad/ui/pnpm-lock\\.yaml" + # args: ['--baseline', '.secrets.baseline'] +- repo: https://github.com/shellcheck-py/shellcheck-py + rev: v0.10.0.1 + hooks: + - id: shellcheck + args: ["--external-sources"] +- repo: https://github.com/python-poetry/poetry + rev: '2.1.1' + hooks: + - id: poetry-check + args: ["-C", "rmcryptpad"] + - id: poetry-lock + args: ["-C", "rmcryptpad"] +- repo: https://github.com/pre-commit/mirrors-prettier + rev: v3.0.3 + hooks: + - id: prettier + files: ^rmcryptpad/ui/ +- repo: https://github.com/pre-commit/mirrors-eslint + rev: v8.50.0 + hooks: + - id: eslint + files: ^rmcryptpad/ui/ + types_or: [javascript, jsx, ts, tsx] + additional_dependencies: + - "eslint@8.50.0" + - "@typescript-eslint/parser" + - "@typescript-eslint/eslint-plugin" + - "eslint-plugin-react-hooks" + - "eslint-plugin-react-refresh" diff --git a/compose.yaml b/compose.yaml index a5afde6..76e18b6 100644 --- a/compose.yaml +++ b/compose.yaml @@ -40,7 +40,7 @@ services: - ./volumes/cryptpad/data:/cryptpad/data - ./volumes/cryptpad/files:/cryptpad/datastore - ./config/config.js:/cryptpad/config/config.js:ro - - ./customize/application_config.js:/cryptpad/customize/application_config.js:ro + - ./customize:/cryptpad/customize.local:ro expose: - "3000" - "3003" diff --git a/config/config.js b/config/config.js index 7d07cd9..dc5af7d 100644 --- a/config/config.js +++ b/config/config.js @@ -24,4 +24,3 @@ module.exports = { logToStdout: true, installMethod: 'docker', }; - diff --git a/customize/CryptPad_logo_hero.svg b/customize/CryptPad_logo_hero.svg new file mode 100644 index 0000000..b76b7dc --- /dev/null +++ b/customize/CryptPad_logo_hero.svg @@ -0,0 +1 @@ + diff --git a/customize/application_config.js b/customize/application_config.js index 377cbf7..18592f1 100644 --- a/customize/application_config.js +++ b/customize/application_config.js @@ -1,7 +1,16 @@ (() => { const factory = (AppConfig) => { - AppConfig.loginSalt = 'ac73deb9ef8944e51b2d4feafc2d5713764c26911aa12fd70168cc8be2754a33'; + AppConfig.loginSalt = '__LOGIN_SALT__'; // replaced at container startup AppConfig.minimumPasswordLength = 8; + AppConfig.defaultDarkTheme = 'true'; + + // Hide donation/crowdfunding prompts + AppConfig.disableFeedback = true; + + // Hide footer links that don't apply to a private deployment + AppConfig.source = false; + AppConfig.roadmap = false; + return AppConfig; }; @@ -11,4 +20,3 @@ define(['/common/application_config_internal.js'], factory); } })(); - diff --git a/customize/src/less2/include/colortheme-dark.less b/customize/src/less2/include/colortheme-dark.less new file mode 100644 index 0000000..11ab0d6 --- /dev/null +++ b/customize/src/less2/include/colortheme-dark.less @@ -0,0 +1,504 @@ +/* + * CryptPad dark theme – Deploy App 'fdf' color alignment + * + * Only the brand color and grey scale are overridden here. + * CryptPad derives all other UI colors from these base values, + * so changing @cryptpad_color_brand is enough to re-skin the + * entire interface. + * + * Deploy App 'fdf' palette reference (from rmuiv2 theme.json): + * primary: #cc66bb + * primary-light: #882255 + * secondary: #2195d6 + * background: #1a1a1a + * card/muted: #222222 / #333333 + * ring: #aa4499 + */ + +@colortheme_font: 'Open Sans', 'Helvetica Neue', sans-serif; +@colortheme_app-font-size: 16px; +@colortheme_app-font-size-small: 13px; +@colortheme_app-font: @colortheme_app-font-size @colortheme_font; + +// ── Brand ──────────────────────────────────────────────────── +@cryptpad_color_brand: #882255; +@cryptpad_color_brand_300: lighten(@cryptpad_color_brand, 30%); +@cryptpad_color_brand_fade: fade(@cryptpad_color_brand, 75%); +@cryptpad_color_brand_fader: fade(@cryptpad_color_brand, 50%); +@cryptpad_color_brand_fadest: fade(@cryptpad_color_brand, 25%); + +// ── Application palette ────────────────────────────────────── +@colortheme_apps: { + default: @cryptpad_color_brand; + drive: @cryptpad_color_brand; + pad: #aa4499; + code: #cc66bb; + slide: #882255; + poll: #2195d6; + form: #2195d6; + whiteboard: #cc66bb; + diagram: #aa4499; + kanban: #4e778e; + sheet: #4e778e; + doc: #aa4499; + presentation: #882255; + file: #dc2626; +} + +@colortheme_static_apps: { + default: @cryptpad_color_brand; + teams: #aa4499; + contacts: #4e778e; +} + +// ── Grey scale ─────────── +@cryptpad_color_white: #FFF; +@cryptpad_color_grey_50: #FAFAFA; +@cryptpad_color_grey_100: #F5F5F5; +@cryptpad_color_grey_200: #EEEEEE; +@cryptpad_color_grey_300: #E0E0E0; +@cryptpad_color_grey_400: #999999; +@cryptpad_color_grey_500: #888888; +@cryptpad_color_grey_600: #666666; +@cryptpad_color_grey_700: #444444; +@cryptpad_color_grey_800: #333333; +@cryptpad_color_grey_850: #282828; +@cryptpad_color_grey_900: #1a1a1a; +@cryptpad_color_grey_950: #111111; +@cryptpad_color_black: #000; + +@cryptpad_logo_grey: #999999; + +@cryptpad_text_col: @cryptpad_color_grey_200; +@cryptpad_text_col_inv: @cryptpad_color_grey_900; + +@cryptpad_ui_shadow: 0px 0px 5px 1px #00000030; + +@cryptpad_color_blue: #2195d6; +@cryptpad_color_light_blue: #2195d6; +@cryptpad_color_red: #dc2626; +@cryptpad_color_red_fade: fade(@cryptpad_color_red, 50%); +@cryptpad_color_red_fader: fade(@cryptpad_color_red, 15%); +@cryptpad_color_warn_red: @cryptpad_color_red_fade; +@cryptpad_color_dark_red: #9e0000; +@cryptpad_color_darker_red: darken(@cryptpad_color_dark_red, 15%); +@cryptpad_color_mid_red: #FF8880; +@cryptpad_color_light_red: #FFD4D4; +@cryptpad_color_light_red_fade: fade(@cryptpad_color_light_red, 20%); +@cryptpad_color_orange: #f49842; +@cryptpad_color_green: #5cb85c; +@cryptpad_color_green_fade: fade(@cryptpad_color_green, 50%); +@cryptpad_color_light_green: #c5ffa8; +@cryptpad_color_light_green_fade: fade(@cryptpad_color_light_green, 20%); +@cryptpad_color_light_yellow: #FFE69C; +@cryptpad_color_yellow_fade: fade(@cryptpad_color_light_yellow, 50%); +@cryptpad_color_yellow_fader: fade(#FFE69C, 15%); +@cryptpad_color_lighter_blue: fade(#2195d6, 30%); + +@cp_palette: + #FFD4D4, + #FFDECA, + #FFE69C, + #DBFFB7, + #AFFDC2, + #C9FFFE, + #C8D6FF, + #E4CAFF; +@cp_palette-dark: + darken(desaturate(extract(@cp_palette, 1),60%), 60%), + darken(desaturate(extract(@cp_palette, 2),60%), 60%), + darken(desaturate(extract(@cp_palette, 3),55%), 60%), + darken(desaturate(extract(@cp_palette, 4),55%), 70%), + darken(desaturate(extract(@cp_palette, 5),60%), 65%), + darken(desaturate(extract(@cp_palette, 6),60%), 70%), + darken(desaturate(extract(@cp_palette, 7),60%), 60%), + darken(desaturate(extract(@cp_palette, 8),70%), 60%); + +@cp_badges-color: @cp_app-bg; +@cp_badges-bg: { + admin: #cc66bb; + moderator: #aa4499; + premium: #cc66bb; + error: @cryptpad_color_red,; +} + +@cryptpad_color_link: @cryptpad_color_brand_300; + +// Premium plans colors +@cryptpad_color_basic: darken(desaturate(#DDEFFF, 70%), 75%); +@cryptpad_color_pro: darken(desaturate(#E4FFDD, 70%), 75%); +@cryptpad_color_power: darken(desaturate(#F6DDFF, 70%), 75%); + +@cp_scrollbar-fg: hsla(0,0%,100%,.2); +@scrollbar_color: @cp_scrollbar-fg transparent; + +// Loading screen +@cp_loading-bg: @cryptpad_color_grey_900; +@cp_loading-fg: @cryptpad_text_col; +@cp_loading-color: @cryptpad_color_grey_400; +@cp_loading-msg-bg: @cryptpad_color_grey_700; +@cp_loading-link: @cryptpad_color_brand_300; +@cp_loading-error-bg: @cryptpad_color_warn_red; +@cp_loading-error-fg: @cryptpad_text_col; +@cp_loading-progress-bg: @cryptpad_color_grey_800; +@cp_loading-progress-bar-bg: @cryptpad_color_brand; +@cp_loading-logo: @cryptpad_logo_grey; + +// Chat +@cp_messenger-bg: @cryptpad_color_grey_900; +@cp_messenger-fg: @cryptpad_text_col; +@cp_messenger-notif: @cryptpad_color_black; +@cp_messenger-friend: @cryptpad_color_black; +@cp_messenger-online: @cryptpad_color_light_green; + +// Alertify +@cp_alertify-bg: @cryptpad_color_grey_800; +@cp_alertify-fg: @cryptpad_color_brand_300; +@cp_alertify-hover: fade(@cp_alertify-fg, 25%); +@cp_alertify-overlay: fade(@cryptpad_color_grey_900, 50%); +@cp_alertify-log-bg: fade(@cryptpad_color_brand_300, 90%); +@cp_alertify-log-fg: @cryptpad_color_grey_900; +@cp_alertify-warn-bg: rgba(205, 37, 50); +@cp_alertify-disable-border: @cryptpad_color_grey_600; + +// Forms +@cp_forms-fg: @cryptpad_text_col; +@cp_forms-bg: @cryptpad_color_grey_700; +@cp_forms-border: @cryptpad_color_brand_300; +@cp_forms-placeholder: fade(@cryptpad_text_col, 75%); +@cp_forms-readonly: @cryptpad_color_brand_fadest; +@cp_forms-readonly-border: @cryptpad_color_brand; +@cp_forms-disabled: @cryptpad_color_grey_500; + +// Bootstrap alerts +@cp_alerts-warning-bg: @cryptpad_color_yellow_fader; +@cp_alerts-warning-fg: @cryptpad_color_light_yellow; +@cp_alerts-warning-text: @cryptpad_color_light_yellow; +@cp_alerts-danger-bg: @cryptpad_color_darker_red; +@cp_alerts-danger-fg: @cryptpad_color_light_red; +@cp_alerts-danger-text: @cryptpad_color_light_red; +@cp_alerts-info-bg: @cryptpad_color_brand_fadest; +@cp_alerts-info-text: @cryptpad_color_brand_300; +@cp_alerts-info-fg: @cryptpad_color_brand_300; +@cp_alerts-success-bg: @cryptpad_color_light_green_fade; +@cp_alerts-success-fg: @cryptpad_color_green; +@cp_alerts-success-text: @cryptpad_color_light_green; + +// Buttons +@cp_buttons-fg: @cryptpad_color_brand_300; +@cp_buttons-hover: @cryptpad_color_brand_fadest; +@cp_buttons-default: @cryptpad_color_grey_700; +@cp_buttons-default-color: @cryptpad_text_col; +@cp_buttons-default-border: @cryptpad_text_col; +@cp_buttons-default-bg: @cryptpad_text_col; +@cp_buttons-default-fg: @cryptpad_color_grey_900; +@cp_buttons-default-alt-bg: @cryptpad_color_grey_900; +@cp_buttons-default-alt-fg: @cryptpad_text_col; +@cp_buttons-red: #dc2626; +@cp_buttons-red-text: @cryptpad_color_light_red; +@cp_buttons-red-color: #FFF; +@cp_buttons-red-border: transparent; +@cp_buttons-primary: @cryptpad_color_brand; +@cp_buttons-primary-text: #FFFFFF; +@cp_buttons-primary-border: transparent; +@cp_buttons-disabled: #6c757d; +@cp_buttons-disabled-text: #ffffff; +@cp_buttons-disabled-border: #6c757d; +@cp_buttons-cancel: transparent; +@cp_buttons-cancel-border: @cryptpad_color_grey_400; + +// Sidebar layout +@cp_sidebar-left-bg: @cryptpad_color_grey_900; +@cp_sidebar-left-fg: @cryptpad_text_col; +@cp_sidebar-left-item-bg: @cryptpad_color_grey_800; +@cp_sidebar-right-bg: @cryptpad_color_grey_900; +@cp_sidebar-right-fg: @cryptpad_text_col; +@cp_sidebar-left-active: @cryptpad_color_grey_400; +@cp_sidebar-left-active-fg: @cryptpad_color_grey_900; +@cp_sidebar-hint: fade(@cryptpad_text_col, 80%); + +// Settings +@cp_settings_enabled: @cryptpad_color_light_green; +@cp_settings_disabled: @cryptpad_color_mid_red; + +// Drive +@cp_drive-bg: @cp_sidebar-right-bg; +@cp_drive-fg: @cp_sidebar-right-fg; +@cp_drive-header-fg: fade(@cryptpad_text_col, 70%); +@cp_drive-icon-hover: fade(@cryptpad_text_col, 5%); +@cp_drive-icon-border: fade(@cryptpad_text_col, 20%); +@cp_drive-thumb-bg: transparent; +@cp_drive-selected-bg: fade(@cryptpad_text_col, 10%); +@cp_drive-selected-fg: @cryptpad_text_col; +@cp_drive-selection-overlay: fade(@cryptpad_text_col, 5%); +@cp_drive-selection-bg: @cryptpad_color_grey_800; +@cp_drive-selection-fg: @cryptpad_text_col; +@cp_drive-droppable-bg: @cryptpad_color_grey_700; +@cp_drive-droppable-fg: @cryptpad_text_col; +@cp_drive-infobox-bg: @cryptpad_color_brand_fadest; +@cp_drive-infobox-fg: @cryptpad_color_brand_300; +@cp_drive-warnbox-bg: @cryptpad_color_red_fader; +@cp_drive-warnbox-fg: @cryptpad_color_light_red; +@cp_drive-tree-branch: @cryptpad_text_col; + +// Contextmenu +@cp_context-bg: @cryptpad_color_grey_800; +@cp_context-border: @cryptpad_color_grey_600; +@cp_context-bg-hover: @cryptpad_color_grey_700; +@cp_context-bg-active: @cryptpad_color_grey_600; +@cp_context-fg: @cryptpad_text_col; +@cp_context-icon: @cryptpad_text_col; +@cp_context-disabled: @cryptpad_color_grey_600; + +// Tooltip +@cp_tooltip-bg: @cryptpad_color_grey_700; +@cp_tooltip-fg: @cryptpad_text_col; + +// Dropdown +@cp_dropdown-fg: @cryptpad_text_col; +@cp_dropdown-bg: @cryptpad_color_grey_800; +@cp_dropdown-bg-hover: @cryptpad_color_grey_700; +@cp_dropdown-bg-active: @cryptpad_color_grey_600; + +// Rendered Markdown +@cp_markdown-bg: @cryptpad_color_grey_900; +@cp_markdown-border: @cryptpad_color_grey_700; +@cp_markdown-block-fg: @cryptpad_text_col; +@cp_markdown-block-bg: @cryptpad_color_grey_800; + +// Avatar +@cp_avatar-bg: @cryptpad_color_grey_700; +@cp_avatar-fg: @cryptpad_text_col; + +// Corner +@cp_corner-bg: @cryptpad_color_grey_800; +@cp_corner-fg: @cryptpad_color_brand_300; +@cp_corner-text: @cryptpad_text_col; + +// Pad Creation Screen +@cp_creation-bg: @cryptpad_color_grey_800; +@cp_creation-fg: @cryptpad_text_col; +@cp_creation-template: @cryptpad_color_grey_700; +@cp_creation-button-bg: @cryptpad_color_brand; +@cp_creation-button-fg: @cryptpad_color_white; +@cp_creation-error-bg: @cryptpad_color_blue; +@cp_creation-error-fg: @cryptpad_color_grey_100; + +// Export +@cp_export-bg: @cryptpad_color_grey_900; +@cp_export-fg: @cryptpad_text_col; +@cp_export-hint: fade(@cryptpad_text_col, 80%); +@cp_export-error-bg: @cryptpad_color_grey_800; +@cp_export-error-bg2: @cryptpad_color_red_fader; + +// File upload +@cp_upload-fg: @cryptpad_color_brand_300; +@cp_upload-header: @cryptpad_color_grey_700; +@cp_upload-progress: @cryptpad_color_grey_700; + +// Help +@cp_help-bg: @cryptpad_color_grey_800; +@cp_help-fg: @cryptpad_text_col; +@cp_help-link: @cryptpad_color_link; + +// Static pages (home, about, etc.) +@cp_static-bg: @cryptpad_color_grey_900; +@cp_static-fg: @cryptpad_text_col; +@cp_static-link: @cryptpad_color_brand_300; +@cp_static-title: @cryptpad_color_brand; +@cp_static-footer: fade(@cryptpad_color_brand, 20%); +@cp_static-footer-border: @cryptpad_color_grey_600; +@cp_static-topbar-fg: @cryptpad_color_brand_300; +@cp_static-card-bg: @cryptpad_color_grey_800; +@cp_static-danger: @cryptpad_color_red_fade; +@cp_static-img-invert-filter: hue-rotate(200grad) invert(); + +// Limit bar +@cp_limit-fg: @cryptpad_text_col; +@cp-limit-bar-bg: @cryptpad_color_grey_600; +@cp-limit-bar-normal: @cryptpad_color_green; +@cp-limit-bar-warning: @cryptpad_color_orange; +@cp-limit-bar-above: @cryptpad_color_red; + +// Mentions +@cp_mentions-bg: @cryptpad_color_grey_700; +@cp_mentions-hover: @cryptpad_color_grey_900; + +// Autocomplete +@cp_autocomplete-bg: @cryptpad_color_grey_900; +@cp_autocomplete-fg: @cryptpad_text_col; +@cp_autocomplete-border: @cryptpad_color_grey_700; +@cp_autocomplete-hover: @cryptpad_color_brand_fadest; + +// Modals +@cp_access-overlay: fade(@cp_alertify-bg, 50%); +@cp_snapshots-hover: @cryptpad_color_grey_850; + +// Support +@cp_support-bg: @cryptpad_color_grey_800; +@cp_support-msg-fg: @cryptpad_text_col; +@cp_support-msg-bg: @cryptpad_color_grey_700; +@cp_support-header-bg: @cryptpad_color_grey_700; + +// Toolbar +@cp_toolbar-bg: @cryptpad_color_grey_900; +@cp_toolbar-fg: @cryptpad_text_col; +@cp_toolbar-bottom-bg: @cryptpad_text_col; +@cp_toolbar-bottom-fg: @cryptpad_color_grey_900; +@cp_toolbar-logo-bg: @cryptpad_color_grey_200; +@cp_toolbar-bg2: @cryptpad_color_grey_850; +@cp_toolbar-bg3: @cryptpad_color_grey_800; + +@cp_toolbar-fade1: fade(@cryptpad_text_col, 10%); +@cp_toolbar-fade3: fade(@cryptpad_text_col, 30%); +@cp_toolbar-warn: @cp_buttons-red; + +@cp_history-line-bg: @cryptpad_color_grey_900; +@cp_history-bg1: @cryptpad_color_grey_600; +@cp_history-bg2: @cryptpad_color_grey_800; +@cp_history-fg: @cp_toolbar-fg; + +// Tokenfield +@cp_token-bg: @cryptpad_color_grey_700; +@cp_token-fg: @cryptpad_text_col; +@cp_token-bg-hover: @cryptpad_color_grey_600; +@cp_token-invalid: @cryptpad_color_warn_red; + +// Usergrid +@cp_usergrid-fg: @cryptpad_text_col; +@cp_usergrid-selected-bg: @cryptpad_color_brand; +@cp_usergrid-selected-fg: @cryptpad_color_white; + +// Other +@cp_shadow-color: fade(@cryptpad_color_black, 60%); + +// Apps +@cp_app-bg: @cryptpad_color_grey_900; +@cp_app-fg: @cryptpad_text_col; + +// Accounts +@cp_accounts-basic: @cryptpad_color_basic; +@cp_accounts-pro: @cryptpad_color_pro; +@cp_accounts-power: @cryptpad_color_power; +@cp_accounts-active: @cryptpad_color_green_fade; +@cp_accounts-inactive: @cryptpad_color_red_fade; +@cp_accounts-mysubs-alert: @cryptpad_color_grey_700; +@cp_accounts-mysubs-bg: @cryptpad_color_grey_800; +@cp_accounts-mysubs-fg: @cryptpad_text_col; +@cp_accounts-contact-hover: fade(@cryptpad_color_black, 20%); +@cp_accounts-tab-bg: @cryptpad_color_grey_800; +@cp_accounts-tab-hover: @cryptpad_color_grey_700; +@cp_accounts-tab-border: @cryptpad_color_grey_600; + +// Admin Support +@cp_admin-isadmin-bg: @cryptpad_color_brand_fadest; +@cp_admin-premium-bg: @cryptpad_color_red_fader; +@cp_admin-last-fg: @cryptpad_text_col; +@cp_admin-last-bg: @cryptpad_color_grey_700; + +// Code preview +@cp_preview-bg: @cryptpad_color_grey_900; +@cp_preview-fg: @cryptpad_text_col; +@cp_preview-link: @cryptpad_color_brand_300; + +// Debug +@cp_debug-hover: fade(@cryptpad_color_black, 10%); +@cp_debug-icon-hover: @cryptpad_color_grey_500; + +// Mediatag +@cp_mediatag-text-bg: @cryptpad_color_white; +@cp_mediatag-text-fg: @cryptpad_text_col; + +// File +@cp_file-progress-bg: @cryptpad_color_brand; +@cp_file-progress-fg: @cryptpad_text_col; + +// Kanban +@cp_kanban-fg: @cryptpad_text_col; +@cp_kanban-item-bg: @cryptpad_color_grey_800; +@cp_kanban-link: @cryptpad_color_brand_300; +@cp_kanban-board-bg: @cryptpad_color_grey_700; +@cp_kanban-conflict-bg: @cryptpad_color_grey_700; +@cp_kanban-tags-bg: @cryptpad_color_grey_700; +@cp_kanban-add-hover: fade(@cryptpad_color_black, 10%); +@cp_kanban-trash-bg: @cryptpad_color_warn_red; +@cp_kanban-color0: @cryptpad_color_grey_400; +@cp_kanban-colors: @cp_palette; +@cp_kanban-card-colors: @cp_palette-dark; + +// Notifications +@cp_notif-hover: fade(@cryptpad_color_black, 10%); +@cp_notif-header-bg: @cryptpad_color_grey_700; +@cp_notif-header-fg: @cryptpad_text_col; +@cp_notif-table-border: @cryptpad_color_grey_900; +@cp_notif-bg: @cryptpad_color_grey_800; +@cp_notif-fg: @cryptpad_text_col; + +// OO +@cp_oo-offline-overlay: fade(@cryptpad_color_white, 50%); + +// Rich text +@cp_pad-fg: @cryptpad_text_col; +@cp_pad-icon-filter: invert(); +@cp_pad-resize: @cryptpad_color_grey_800; +// Comments +@cp_comments-fg: @cryptpad_text_col; +@cp_comments-bg: @cryptpad_color_grey_800; +@cp_comments-header: @cryptpad_color_grey_500; +@cp_comments-notif: @cryptpad_color_light_red; + +// Poll +@cp_poll-th-bg: @cryptpad_color_brand_fadest; +@cp_poll-th-fg: @cryptpad_text_col; +@cp_poll-uncommitted-bg: @cryptpad_color_grey_800; +@cp_poll-border-color: @cryptpad_color_grey_600; +@cp_poll-cell-fg: @cryptpad_text_col; +@cp_poll-unset: @cryptpad_color_grey_700; +@cp_poll-yes: @cryptpad_color_green_fade; +@cp_poll-no: @cryptpad_color_red_fade; +@cp_poll-maybe: @cryptpad_color_grey_600; +@cp_poll-hint: @cryptpad_color_grey_500; + +// Profile +@cp_profile-border: @cryptpad_color_grey_500; +@cp_profile-hint: @cryptpad_color_grey_400; + +// Slide +@cp_slide-fg: @cp_app-fg; +@cp_slide-css-error: @cryptpad_color_red_fader; + +// Teams +@cp_teams-card-bg: @cryptpad_color_grey_800; +@cp_teams-leftside-bg: @cp_sidebar-left-bg; +@cp_teams-invite-bg: fade(@cryptpad_color_brand, 50%); +@cp_teams-invite-fg: @cryptpad_text_col; +@cp_teams-roster-odd: fade(@cryptpad_text_col, 15%); + +// Whiteboard +@cp_whiteboard-board-bg: @cryptpad_color_grey_950; +@cp_whiteboard-board-border: @cryptpad_color_grey_800; +@cp_whiteboard-bg: @cp_app-bg; +@cp_whiteboard-fg: @cryptpad_text_col; + +// Flatpickr +@cp_flatpickr-bg: @cryptpad_color_grey_800; +@cp_flatpickr-highlight: @cryptpad_color_brand_300; +@cp_flatpickr-highlight-text: @cryptpad_color_grey_800; + +// Calendar +@cp_calendar-border: @cryptpad_color_grey_600; +@cp_calendar-now: @cryptpad_color_brand_300; +@cp_calendar-now-fg: @cryptpad_color_grey_800; + +// Forms app +@cp_form-bg1: @cryptpad_color_grey_800; +@cp_form-bg2: @cryptpad_color_grey_900; +@cp_form-border: @cryptpad_color_grey_800; +@cp_form-poll-color: @cryptpad_color_grey_800; +@cp_form-poll-no: fade(@cryptpad_color_red, 25%); +@cp_form-poll-yes: fade(@cryptpad_color_green, 25%); +@cp_form-poll-maybe: @cryptpad_color_grey_700; +@cp_form-poll-yes-color: @cryptpad_color_green; +@cp_form-invalid: @cryptpad_color_light_red; +@cp_form-palette: @cp_palette-dark; +@cp_form-palette2: @cp_palette; diff --git a/customize/translations/messages.js.disabled b/customize/translations/messages.js.disabled new file mode 100644 index 0000000..96bee19 --- /dev/null +++ b/customize/translations/messages.js.disabled @@ -0,0 +1,17 @@ +// Custom translation overrides for this CryptPad instance. +// Hides donation and contact buttons that are not relevant +// for a private deployment. +define(['/common/translations/messages.js'], function (Messages) { + // Remove donation / crowdfunding UI + Messages.crowdfunding_button = ""; + Messages.crowdfunding_button2 = ""; + Messages.crowdfunding_popup_text = ""; + Messages.crowdfunding_popup_no = ""; + Messages.footer_donate = ""; + + // Remove "contact the developers" link (admin contact stays) + Messages.contact_dev = ""; + Messages.contact_devHint = ""; + + return Messages; +}); diff --git a/docker/cryptpad/docker-entrypoint.sh b/docker/cryptpad/docker-entrypoint.sh index e2217dd..c22562d 100644 --- a/docker/cryptpad/docker-entrypoint.sh +++ b/docker/cryptpad/docker-entrypoint.sh @@ -28,6 +28,24 @@ if [ ! -f "$CPAD_CONF" ]; then cp /cryptpad/config/config.example.js "$CPAD_CONF" fi +# ── loginSalt: generate once, persist in data volume ───────────────────────── +CPAD_SALT_FILE="/cryptpad/data/.login-salt" +if [ ! -f "$CPAD_SALT_FILE" ]; then + node -e "process.stdout.write(require('crypto').randomBytes(32).toString('hex'))" > "$CPAD_SALT_FILE" + echo "Generated new loginSalt (stored in $CPAD_SALT_FILE)" >&2 +fi +LOGIN_SALT="$(cat "$CPAD_SALT_FILE")" + +# ── Assemble /cryptpad/customize from read-only source with salt injected ──── +CPAD_CUSTOMIZE_SRC="/cryptpad/customize.local" +if [ -d "$CPAD_CUSTOMIZE_SRC" ]; then + cp -a "$CPAD_CUSTOMIZE_SRC"/. /cryptpad/customize/ + if [ -f /cryptpad/customize/application_config.js ]; then + sed -i "s/__LOGIN_SALT__/${LOGIN_SALT}/" /cryptpad/customize/application_config.js + echo "Injected loginSalt into application_config.js" >&2 + fi +fi + mkdir -p "$CPAD_DECREE_DIR" if [ ! -f "$CPAD_DECREE_FILE" ] || ! grep -q '"SET_BEARER_SECRET"' "$CPAD_DECREE_FILE"; then bearer_secret="$(node -e "process.stdout.write(require('crypto').randomBytes(32).toString('base64'))")" diff --git a/docker/nginx/Dockerfile b/docker/nginx/Dockerfile index db399f3..e32cadb 100644 --- a/docker/nginx/Dockerfile +++ b/docker/nginx/Dockerfile @@ -12,4 +12,3 @@ RUN chmod 0755 /usr/local/bin/nginx-entrypoint.sh /usr/local/bin/generate-local- ENTRYPOINT ["/usr/local/bin/nginx-entrypoint.sh"] CMD ["nginx", "-g", "daemon off;"] - diff --git a/docker/nginx/docker-entrypoint.sh b/docker/nginx/docker-entrypoint.sh index 9de244c..5bd2018 100644 --- a/docker/nginx/docker-entrypoint.sh +++ b/docker/nginx/docker-entrypoint.sh @@ -4,4 +4,3 @@ set -eu /usr/local/bin/generate-local-cert.sh exec "$@" - diff --git a/rmcryptpad/README.md b/rmcryptpad/README.md index 5ea6664..5f417c7 100644 --- a/rmcryptpad/README.md +++ b/rmcryptpad/README.md @@ -1,3 +1,28 @@ # rmcryptpad Deploy App integration layer for CryptPad. + +Development +----------- + +TLDR: + +- Create and activate a Python 3.11 virtualenv (assuming virtualenvwrapper):: + + mkvirtualenv -p `which python3.11` my_virtualenv + +- change to a branch:: + + git checkout -b my_branch + +- install Poetry: https://python-poetry.org/docs/#installation +- Install project deps and pre-commit hooks:: + + poetry install + pre-commit install --install-hooks + pre-commit run --all-files + +- Ready to go. + +Remember to activate your virtualenv whenever working on the repo, this is needed +because pylint and mypy pre-commit hooks use the "system" python for now (because reasons). diff --git a/rmcryptpad/poetry.lock b/rmcryptpad/poetry.lock index 5576f21..d06689e 100644 --- a/rmcryptpad/poetry.lock +++ b/rmcryptpad/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 2.3.2 and should not be changed by hand. +# This file is automatically @generated by Poetry 2.1.1 and should not be changed by hand. [[package]] name = "annotated-doc" @@ -828,7 +828,7 @@ pytest = ">=4.0,<10.0" [package.extras] docker-compose-v1 = ["docker-compose (>=1.27.3,<2.0)"] -tests = ["mypy (>=0.500,<2.0)", "pytest-mypy (>=0.10,<1.0)", "pytest-pycodestyle (>=2.0.0,<3.0)", "pytest-pylint (>=0.14.1,<1.0)", "requests (>=2.22.0,<3.0)", "types-requests (>=2.31,<3.0)", "types-setuptools (>=69.0,<70.0)"] +tests = ["mypy (>=0.500,<2.000)", "pytest-mypy (>=0.10,<1.0)", "pytest-pycodestyle (>=2.0.0,<3.0)", "pytest-pylint (>=0.14.1,<1.0)", "requests (>=2.22.0,<3.0)", "types-requests (>=2.31,<3.0)", "types-setuptools (>=69.0,<70.0)"] [[package]] name = "python-dotenv" diff --git a/rmcryptpad/pyproject.toml b/rmcryptpad/pyproject.toml index 11fd0df..0d9bd82 100644 --- a/rmcryptpad/pyproject.toml +++ b/rmcryptpad/pyproject.toml @@ -26,6 +26,16 @@ pytest = "^8.0" pytest-asyncio = ">=1.2,<2.0" pytest-docker = "^3.2" +[tool.pylint.messages_control] +disable = ["fixme", "W1202", "C0209", "E0402"] + +[tool.pylint.design] +max-args = 10 + +[tool.pylint.similarities] +min-similarity-lines = 8 +ignore-imports = true + [build-system] requires = ["poetry-core>=2.1.0"] build-backend = "poetry.core.masonry.api" diff --git a/rmcryptpad/src/rmcryptpad/config.py b/rmcryptpad/src/rmcryptpad/config.py index 16f05e0..b0ddc62 100644 --- a/rmcryptpad/src/rmcryptpad/config.py +++ b/rmcryptpad/src/rmcryptpad/config.py @@ -49,7 +49,10 @@ def singleton(cls) -> "DBSettings": class RMCryptPadSettings(BaseSettings): """Runtime settings for rmcryptpad.""" - rmcn: str = Field(default="rasenmaeher", description="Expected CN for the Rasenmaeher client certificate") + rmcn: str = Field( + default="rasenmaeher", + description="Expected CN for the Rasenmaeher client certificate", + ) public_url: str = "https://cryptpad.localhost:8443" public_sandbox_url: str = "https://sandbox.cryptpad.localhost:8443" docs_url: str = "https://docs.cryptpad.org/en/admin_guide/installation.html" diff --git a/rmcryptpad/src/rmcryptpad/console.py b/rmcryptpad/src/rmcryptpad/console.py index ccf59a8..72ea742 100644 --- a/rmcryptpad/src/rmcryptpad/console.py +++ b/rmcryptpad/src/rmcryptpad/console.py @@ -9,4 +9,4 @@ def rmcryptpad_cli() -> None: """Run the API server.""" - uvicorn.run(get_app(), host="0.0.0.0", port=8000) + uvicorn.run(get_app(), host="0.0.0.0", port=8000) # nosec B104 - runs in container diff --git a/rmcryptpad/src/rmcryptpad/db/__init__.py b/rmcryptpad/src/rmcryptpad/db/__init__.py index cba7c12..1c701ac 100644 --- a/rmcryptpad/src/rmcryptpad/db/__init__.py +++ b/rmcryptpad/src/rmcryptpad/db/__init__.py @@ -1,2 +1 @@ """Database package for rmcryptpad.""" - diff --git a/rmcryptpad/src/rmcryptpad/db/base.py b/rmcryptpad/src/rmcryptpad/db/base.py index 06aeeef..87ee557 100644 --- a/rmcryptpad/src/rmcryptpad/db/base.py +++ b/rmcryptpad/src/rmcryptpad/db/base.py @@ -21,8 +21,12 @@ class ORMBaseModel(SQLModel, table=False): __table_args__ = {"schema": "rmcryptpad"} pk: uuid.UUID = Field(primary_key=True, default_factory=uuid.uuid4) - created: datetime.datetime = Field(sa_column_kwargs={"default": utcnow}, nullable=False) - updated: datetime.datetime = Field(sa_column_kwargs={"default": utcnow, "onupdate": utcnow}, nullable=False) + created: datetime.datetime = Field( + sa_column_kwargs={"default": utcnow}, nullable=False + ) + updated: datetime.datetime = Field( + sa_column_kwargs={"default": utcnow, "onupdate": utcnow}, nullable=False + ) deleted: datetime.datetime | None = Field(default=None, nullable=True) @classmethod @@ -45,4 +49,3 @@ async def soft_delete(self) -> Self: session.commit() session.refresh(self) return self - diff --git a/rmcryptpad/src/rmcryptpad/db/dbinit.py b/rmcryptpad/src/rmcryptpad/db/dbinit.py index 88710cf..c64e45a 100644 --- a/rmcryptpad/src/rmcryptpad/db/dbinit.py +++ b/rmcryptpad/src/rmcryptpad/db/dbinit.py @@ -31,7 +31,9 @@ async def init_db() -> None: await asyncio.sleep(random.random() * 1.5) # nosec lock.acquire(timeout=0.0) with wrapper.engine.connect() as connection: - if not sa.inspect(connection).has_schema(ORMBaseModel.__table_args__["schema"]): + if not sa.inspect(connection).has_schema( + ORMBaseModel.__table_args__["schema"] + ): connection.execute(CreateSchema(ORMBaseModel.__table_args__["schema"])) connection.commit() SQLModel.metadata.create_all(connection) diff --git a/rmcryptpad/src/rmcryptpad/db/engine.py b/rmcryptpad/src/rmcryptpad/db/engine.py index d1c680b..55c969a 100644 --- a/rmcryptpad/src/rmcryptpad/db/engine.py +++ b/rmcryptpad/src/rmcryptpad/db/engine.py @@ -20,7 +20,9 @@ class EngineWrapper: _singleton: ClassVar[Optional["EngineWrapper"]] = None def __post_init__(self) -> None: - self.engine = create_engine(self.settings.dsn, pool_pre_ping=True, echo=self.settings.echo) + self.engine = create_engine( + self.settings.dsn, pool_pre_ping=True, echo=self.settings.echo + ) @classmethod def singleton(cls, **kwargs: Any) -> "EngineWrapper": @@ -37,4 +39,3 @@ def get_session(cls) -> Session: def session(self) -> Session: """Return a session bound to the singleton engine.""" return Session(self.engine) - diff --git a/rmcryptpad/src/rmcryptpad/db/errors.py b/rmcryptpad/src/rmcryptpad/db/errors.py index 76d0de4..822c00e 100644 --- a/rmcryptpad/src/rmcryptpad/db/errors.py +++ b/rmcryptpad/src/rmcryptpad/db/errors.py @@ -7,4 +7,3 @@ class NotFound(Exception): class Deleted(Exception): """Requested record exists but is deleted or otherwise inactive.""" - diff --git a/rmcryptpad/src/rmcryptpad/db/oidc_code.py b/rmcryptpad/src/rmcryptpad/db/oidc_code.py index b082863..1637a68 100644 --- a/rmcryptpad/src/rmcryptpad/db/oidc_code.py +++ b/rmcryptpad/src/rmcryptpad/db/oidc_code.py @@ -88,7 +88,11 @@ async def consume(cls, *, code: str, client_id: str, redirect_uri: str) -> Self: raise NotFound() expires_at = _as_utc(obj.expires_at) used_at = _as_utc(obj.used_at) if obj.used_at else None - if obj.deleted or used_at or expires_at <= datetime.datetime.now(datetime.UTC): + if ( + obj.deleted + or used_at + or expires_at <= datetime.datetime.now(datetime.UTC) + ): raise Deleted() obj.used_at = datetime.datetime.now(datetime.UTC) session.add(obj) diff --git a/rmcryptpad/src/rmcryptpad/db/product.py b/rmcryptpad/src/rmcryptpad/db/product.py index d6fa93a..a522e52 100644 --- a/rmcryptpad/src/rmcryptpad/db/product.py +++ b/rmcryptpad/src/rmcryptpad/db/product.py @@ -56,4 +56,3 @@ async def create_or_update(cls, *, certcn: str) -> Self: session.commit() session.refresh(obj) return obj - diff --git a/rmcryptpad/src/rmcryptpad/db/user.py b/rmcryptpad/src/rmcryptpad/db/user.py index c125a0b..0608cb0 100644 --- a/rmcryptpad/src/rmcryptpad/db/user.py +++ b/rmcryptpad/src/rmcryptpad/db/user.py @@ -17,7 +17,9 @@ def fingerprint_pem(cert_pem: str) -> str: """Generate a stable fingerprint for a stored certificate PEM.""" certificate = x509.load_pem_x509_certificate(cert_pem.encode("utf-8")) - return certificate.fingerprint(hashes.SHA1()).hex() + return certificate.fingerprint( + hashes.SHA1() # nosec B303 - x509 fingerprint convention + ).hex() class User(ORMBaseModel, table=True): @@ -25,10 +27,16 @@ class User(ORMBaseModel, table=True): __tablename__ = "users" - callsign: str = Field(index=True, unique=True, description="Canonical CryptPad identity") - rmuuid: str = Field(index=True, description="Latest RM UUID observed for this callsign") + callsign: str = Field( + index=True, unique=True, description="Canonical CryptPad identity" + ) + rmuuid: str = Field( + index=True, description="Latest RM UUID observed for this callsign" + ) cert_pem: str = Field(description="Current client certificate PEM") - cert_fingerprint: str = Field(index=True, description="SHA-256 fingerprint of cert_pem") + cert_fingerprint: str = Field( + index=True, description="SHA-256 fingerprint of cert_pem" + ) is_rmadmin: bool = Field(default=False) revoked: datetime.datetime | None = Field(default=None, index=True) diff --git a/rmcryptpad/src/rmcryptpad/oidc/keys.py b/rmcryptpad/src/rmcryptpad/oidc/keys.py index bdf3305..e11026c 100644 --- a/rmcryptpad/src/rmcryptpad/oidc/keys.py +++ b/rmcryptpad/src/rmcryptpad/oidc/keys.py @@ -4,12 +4,13 @@ import base64 import hashlib +import json from dataclasses import dataclass, field from pathlib import Path from typing import Any, ClassVar, Optional from cryptography.hazmat.primitives import hashes, serialization -from cryptography.hazmat.primitives.asymmetric import rsa +from cryptography.hazmat.primitives.asymmetric import padding, rsa from ..config import RMCryptPadSettings @@ -27,7 +28,9 @@ def _b64url_int(value: int) -> str: class OIDCKeyManager: """Load or create the RSA signing key used for OIDC tokens.""" - key_dir: Path = field(default_factory=lambda: Path(RMCryptPadSettings.singleton().oidc_key_dir)) + key_dir: Path = field( + default_factory=lambda: Path(RMCryptPadSettings.singleton().oidc_key_dir) + ) _private_key: rsa.RSAPrivateKey | None = field(default=None, init=False, repr=False) _singleton: ClassVar[Optional["OIDCKeyManager"]] = None @@ -40,7 +43,9 @@ def __post_init__(self) -> None: def singleton(cls) -> "OIDCKeyManager": """Return the cached key manager.""" if cls._singleton is None: - cls._singleton = cls(key_dir=Path(RMCryptPadSettings.singleton().oidc_key_dir)) + cls._singleton = cls( + key_dir=Path(RMCryptPadSettings.singleton().oidc_key_dir) + ) return cls._singleton @property @@ -51,10 +56,12 @@ def private_key(self) -> rsa.RSAPrivateKey: @property def private_key_path(self) -> Path: + """Return the path to the PEM key file.""" return self.key_dir / "oidc-signing-key.pem" @property def kid(self) -> str: + """Return the SHA-256 thumbprint of the public key.""" pub = self.private_key.public_key().public_bytes( encoding=serialization.Encoding.DER, format=serialization.PublicFormat.SubjectPublicKeyInfo, @@ -65,7 +72,10 @@ def _load_or_create_private_key(self) -> rsa.RSAPrivateKey: key_path = self.private_key_path if key_path.is_file(): data = key_path.read_bytes() - return serialization.load_pem_private_key(data, password=None) + key = serialization.load_pem_private_key(data, password=None) + if not isinstance(key, rsa.RSAPrivateKey): + raise TypeError(f"Expected RSA private key, got {type(key).__name__}") + return key private_key = rsa.generate_private_key(public_exponent=65537, key_size=2048) key_path.write_bytes( private_key.private_bytes( @@ -90,30 +100,33 @@ def public_jwk(self) -> dict[str, Any]: def sign_jwt(self, header: dict[str, Any], payload: dict[str, Any]) -> str: """Sign a JWT using RS256.""" - import json - from cryptography.hazmat.primitives.asymmetric import padding - - header_b64 = _b64url(json.dumps(header, separators=(",", ":"), sort_keys=True).encode("utf-8")) - payload_b64 = _b64url(json.dumps(payload, separators=(",", ":"), sort_keys=True).encode("utf-8")) + header_b64 = _b64url( + json.dumps(header, separators=(",", ":"), sort_keys=True).encode("utf-8") + ) + payload_b64 = _b64url( + json.dumps(payload, separators=(",", ":"), sort_keys=True).encode("utf-8") + ) signing_input = f"{header_b64}.{payload_b64}".encode("utf-8") - signature = self.private_key.sign(signing_input, padding.PKCS1v15(), hashes.SHA256()) + signature = self.private_key.sign( + signing_input, padding.PKCS1v15(), hashes.SHA256() + ) return f"{header_b64}.{payload_b64}.{_b64url(signature)}" def verify_jwt(self, token: str) -> dict[str, Any]: """Verify and decode a JWT signed with the managed key.""" - import json - from cryptography.hazmat.primitives.asymmetric import padding - header_b64, payload_b64, signature_b64 = token.split(".") - header = json.loads(base64.urlsafe_b64decode(_pad(header_b64))) + header: dict[str, Any] = json.loads(base64.urlsafe_b64decode(_pad(header_b64))) if header.get("alg") != "RS256": raise ValueError("Unsupported JWT algorithm") if header.get("kid") != self.kid: raise ValueError("Unknown JWT key id") signing_input = f"{header_b64}.{payload_b64}".encode("utf-8") signature = base64.urlsafe_b64decode(_pad(signature_b64)) - self.private_key.public_key().verify(signature, signing_input, padding.PKCS1v15(), hashes.SHA256()) - return json.loads(base64.urlsafe_b64decode(_pad(payload_b64))) + self.private_key.public_key().verify( + signature, signing_input, padding.PKCS1v15(), hashes.SHA256() + ) + result: dict[str, Any] = json.loads(base64.urlsafe_b64decode(_pad(payload_b64))) + return result def _pad(value: str) -> bytes: diff --git a/rmcryptpad/src/rmcryptpad/oidc/service.py b/rmcryptpad/src/rmcryptpad/oidc/service.py index 43e2469..01a2ad1 100644 --- a/rmcryptpad/src/rmcryptpad/oidc/service.py +++ b/rmcryptpad/src/rmcryptpad/oidc/service.py @@ -5,7 +5,6 @@ import base64 import datetime import hashlib -import json import urllib.parse from dataclasses import dataclass, field from typing import Any @@ -40,7 +39,7 @@ def _parse_basic_auth(header: str | None) -> tuple[str | None, str | None]: raw = base64.b64decode(header[6:].encode("utf-8")).decode("utf-8") client_id, client_secret = raw.split(":", 1) return client_id, client_secret - except Exception: # pragma: no cover - defensive parse guard + except (ValueError, UnicodeDecodeError): # pragma: no cover - defensive parse guard return None, None @@ -58,9 +57,11 @@ class OIDCProvider: @property def issuer(self) -> str: + """Return the OIDC issuer URL.""" return self.settings.oidc_issuer.rstrip("/") def discovery_document(self) -> dict[str, Any]: + """Return the OpenID Connect discovery document.""" return { "issuer": self.issuer, "authorization_endpoint": f"{self.issuer}/oidc/authorize", @@ -71,43 +72,71 @@ def discovery_document(self) -> dict[str, Any]: "subject_types_supported": ["public"], "id_token_signing_alg_values_supported": ["RS256"], "grant_types_supported": ["authorization_code"], - "token_endpoint_auth_methods_supported": ["client_secret_basic", "client_secret_post"], + "token_endpoint_auth_methods_supported": [ + "client_secret_basic", + "client_secret_post", + ], "code_challenge_methods_supported": ["S256"], "scopes_supported": ["openid", "profile"], "claims_supported": ["sub", "preferred_username", "name", "nonce", "scope"], } def jwks_document(self) -> dict[str, list[dict[str, Any]]]: + """Return the JSON Web Key Set.""" return {"keys": [self.keys.public_jwk()]} async def authorize(self, request: Request) -> RedirectResponse: + """Handle an OIDC authorization request and redirect with a code.""" cn = getattr(getattr(request.state, "mtlsdn", {}), "get", lambda *_: None)("CN") if not cn: - raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="Missing client identity") + raise HTTPException( + status_code=status.HTTP_403_FORBIDDEN, detail="Missing client identity" + ) try: user = await User.by_callsign(cn) except (NotFound, Deleted): - raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="Inactive callsign") from None - request_fingerprint = normalize_client_fingerprint(getattr(request.state, "mtlsfingerprint", None)) - if request_fingerprint and user.cert_fingerprint and request_fingerprint != user.cert_fingerprint: - raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="Certificate fingerprint mismatch") + raise HTTPException( + status_code=status.HTTP_403_FORBIDDEN, detail="Inactive callsign" + ) from None + request_fingerprint = normalize_client_fingerprint( + getattr(request.state, "mtlsfingerprint", None) + ) + if ( + request_fingerprint + and user.cert_fingerprint + and request_fingerprint != user.cert_fingerprint + ): + raise HTTPException( + status_code=status.HTTP_403_FORBIDDEN, + detail="Certificate fingerprint mismatch", + ) params = request.query_params if params.get("response_type") != "code": - raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="Unsupported response_type") + raise HTTPException( + status_code=status.HTTP_400_BAD_REQUEST, + detail="Unsupported response_type", + ) if params.get("client_id") != self.settings.oidc_client_id: - raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="Unknown client_id") + raise HTTPException( + status_code=status.HTTP_400_BAD_REQUEST, detail="Unknown client_id" + ) redirect_uri = params.get("redirect_uri") if not redirect_uri: - raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="Missing redirect_uri") + raise HTTPException( + status_code=status.HTTP_400_BAD_REQUEST, detail="Missing redirect_uri" + ) code_challenge = params.get("code_challenge") code_challenge_method = params.get("code_challenge_method") or "S256" if code_challenge_method != "S256" or not code_challenge: - raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="PKCE S256 required") + raise HTTPException( + status_code=status.HTTP_400_BAD_REQUEST, detail="PKCE S256 required" + ) code = await OIDCAuthorizationCode.issue( callsign=user.callsign, client_id=self.settings.oidc_client_id, redirect_uri=redirect_uri, - expires_at=_now() + datetime.timedelta(seconds=self.settings.oidc_code_ttl_seconds), + expires_at=_now() + + datetime.timedelta(seconds=self.settings.oidc_code_ttl_seconds), code_challenge=code_challenge, code_challenge_method=code_challenge_method, nonce=params.get("nonce"), @@ -121,20 +150,33 @@ async def authorize(self, request: Request) -> RedirectResponse: return RedirectResponse(url=location, status_code=status.HTTP_302_FOUND) async def token(self, request: Request) -> dict[str, Any]: - body = await request.body() - form = _parse_form(body) - header_client_id, header_client_secret = _parse_basic_auth(request.headers.get("authorization")) + """Exchange an authorization code for access and ID tokens.""" + form = _parse_form(await request.body()) + header_client_id, header_client_secret = _parse_basic_auth( + request.headers.get("authorization") + ) client_id = form.get("client_id") or header_client_id client_secret = form.get("client_secret") or header_client_secret - if client_id != self.settings.oidc_client_id or client_secret != self.settings.oidc_client_secret: - raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="Invalid client credentials") + if ( + client_id != self.settings.oidc_client_id + or client_secret != self.settings.oidc_client_secret + ): + raise HTTPException( + status_code=status.HTTP_401_UNAUTHORIZED, + detail="Invalid client credentials", + ) if form.get("grant_type") != "authorization_code": - raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="Unsupported grant_type") + raise HTTPException( + status_code=status.HTTP_400_BAD_REQUEST, detail="Unsupported grant_type" + ) code = form.get("code") redirect_uri = form.get("redirect_uri") code_verifier = form.get("code_verifier") if not code or not redirect_uri or not code_verifier: - raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="Missing token request fields") + raise HTTPException( + status_code=status.HTTP_400_BAD_REQUEST, + detail="Missing token request fields", + ) try: code_row = await OIDCAuthorizationCode.consume( code=code, @@ -142,19 +184,35 @@ async def token(self, request: Request) -> dict[str, Any]: redirect_uri=redirect_uri, ) except (NotFound, Deleted): - raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="invalid_grant") from None - if code_row.code_challenge_method == "S256" and _sha256_b64url(code_verifier) != code_row.code_challenge: - raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="invalid_grant") + raise HTTPException( + status_code=status.HTTP_400_BAD_REQUEST, detail="invalid_grant" + ) from None + if ( + code_row.code_challenge_method == "S256" + and _sha256_b64url(code_verifier) != code_row.code_challenge + ): + raise HTTPException( + status_code=status.HTTP_400_BAD_REQUEST, detail="invalid_grant" + ) if code_row.code_challenge_method not in {None, "S256"}: - raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="invalid_grant") + raise HTTPException( + status_code=status.HTTP_400_BAD_REQUEST, detail="invalid_grant" + ) try: user = await User.by_callsign(code_row.callsign) except (NotFound, Deleted): - raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="invalid_grant") from None - access_payload = self._base_claims(user.callsign, code_row.nonce, code_row.scope, "access") - id_payload = self._base_claims(user.callsign, code_row.nonce, code_row.scope, "id") - access_token = self.keys.sign_jwt({"alg": "RS256", "kid": self.keys.kid, "typ": "JWT"}, access_payload) - id_token = self.keys.sign_jwt({"alg": "RS256", "kid": self.keys.kid, "typ": "JWT"}, id_payload) + raise HTTPException( + status_code=status.HTTP_400_BAD_REQUEST, detail="invalid_grant" + ) from None + jwt_header = {"alg": "RS256", "kid": self.keys.kid, "typ": "JWT"} + access_token = self.keys.sign_jwt( + jwt_header, + self._base_claims(user.callsign, code_row.nonce, code_row.scope, "access"), + ) + id_token = self.keys.sign_jwt( + jwt_header, + self._base_claims(user.callsign, code_row.nonce, code_row.scope, "id"), + ) return { "access_token": access_token, "id_token": id_token, @@ -164,20 +222,31 @@ async def token(self, request: Request) -> dict[str, Any]: } async def userinfo(self, request: Request) -> dict[str, Any]: + """Return user claims for a valid access token.""" authorization = request.headers.get("authorization") or "" if not authorization.startswith("Bearer "): - raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="Missing bearer token") + raise HTTPException( + status_code=status.HTTP_401_UNAUTHORIZED, detail="Missing bearer token" + ) token = authorization.removeprefix("Bearer ").strip() try: claims = self.keys.verify_jwt(token) except Exception as exc: # pragma: no cover - defensive verify guard - raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="Invalid token") from exc + raise HTTPException( + status_code=status.HTTP_401_UNAUTHORIZED, detail="Invalid token" + ) from exc if claims.get("iss") != self.issuer: - raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="Invalid token") + raise HTTPException( + status_code=status.HTTP_401_UNAUTHORIZED, detail="Invalid token" + ) if claims.get("token_use") != "access": - raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="Invalid token") + raise HTTPException( + status_code=status.HTTP_401_UNAUTHORIZED, detail="Invalid token" + ) if int(claims.get("exp", 0)) <= int(_now().timestamp()): - raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="Invalid token") + raise HTTPException( + status_code=status.HTTP_401_UNAUTHORIZED, detail="Invalid token" + ) return { "sub": claims["sub"], "preferred_username": claims["preferred_username"], @@ -186,7 +255,9 @@ async def userinfo(self, request: Request) -> dict[str, Any]: "scope": claims.get("scope"), } - def _base_claims(self, callsign: str, nonce: str | None, scope: str, token_use: str) -> dict[str, Any]: + def _base_claims( + self, callsign: str, nonce: str | None, scope: str, token_use: str + ) -> dict[str, Any]: now = int(_now().timestamp()) return { "iss": self.issuer, diff --git a/rmcryptpad/src/rmcryptpad/py.typed b/rmcryptpad/src/rmcryptpad/py.typed index 8b13789..e69de29 100644 --- a/rmcryptpad/src/rmcryptpad/py.typed +++ b/rmcryptpad/src/rmcryptpad/py.typed @@ -1 +0,0 @@ - diff --git a/rmcryptpad/src/rmcryptpad/web/application.py b/rmcryptpad/src/rmcryptpad/web/application.py index 41a5cdc..ce0d47d 100644 --- a/rmcryptpad/src/rmcryptpad/web/application.py +++ b/rmcryptpad/src/rmcryptpad/web/application.py @@ -4,6 +4,7 @@ import asyncio import logging +from collections.abc import AsyncIterator from contextlib import asynccontextmanager from fastapi import FastAPI @@ -27,7 +28,8 @@ @asynccontextmanager -async def app_lifespan(app: FastAPI): +async def app_lifespan(app: FastAPI) -> AsyncIterator[None]: + """Initialize the database and OIDC key manager on startup.""" _ = app await asyncio.gather(init_db()) OIDCKeyManager.singleton() diff --git a/rmcryptpad/src/rmcryptpad/web/clients.py b/rmcryptpad/src/rmcryptpad/web/clients.py index af85ee8..14d04e6 100644 --- a/rmcryptpad/src/rmcryptpad/web/clients.py +++ b/rmcryptpad/src/rmcryptpad/web/clients.py @@ -13,7 +13,8 @@ router_admin = APIRouter(dependencies=[Depends(require_verified_mtls_header)]) -async def _build_client_data(user: UserCRUDRequest, request: Request) -> ClientDataResponse: +def _build_client_data(request: Request) -> ClientDataResponse: + """Build the client data response from current settings.""" require_rm_caller(request) settings = RMCryptPadSettings.singleton() return ClientDataResponse( @@ -27,10 +28,14 @@ async def _build_client_data(user: UserCRUDRequest, request: Request) -> ClientD @router.post("/clients/data", response_model=ClientDataResponse) -async def client_data(user: UserCRUDRequest, request: Request) -> ClientDataResponse: - return await _build_client_data(user, request) +async def client_data(_user: UserCRUDRequest, request: Request) -> ClientDataResponse: + """Return client configuration for a user.""" + return _build_client_data(request) @router_admin.post("/admin/clients/data", response_model=ClientDataResponse) -async def admin_client_data(user: UserCRUDRequest, request: Request) -> ClientDataResponse: - return await _build_client_data(user, request) +async def admin_client_data( + _user: UserCRUDRequest, request: Request +) -> ClientDataResponse: + """Return client configuration for an admin user.""" + return _build_client_data(request) diff --git a/rmcryptpad/src/rmcryptpad/web/description.py b/rmcryptpad/src/rmcryptpad/web/description.py index d9d932a..542f40a 100644 --- a/rmcryptpad/src/rmcryptpad/web/description.py +++ b/rmcryptpad/src/rmcryptpad/web/description.py @@ -19,7 +19,10 @@ def _description_text(language: str) -> str: @router.get("/description/{language}", response_model=ProductDescription) -async def return_product_description(language: str, request: Request) -> ProductDescription: +async def return_product_description( + language: str, request: Request +) -> ProductDescription: + """Return a localized product description.""" require_rm_caller(request) return ProductDescription( shortname="cryptpad", @@ -31,13 +34,16 @@ async def return_product_description(language: str, request: Request) -> Product @router_v2.get("/description/{language}", response_model=ProductDescriptionExtended) -async def return_product_description_extended(language: str, request: Request) -> ProductDescriptionExtended: +async def return_product_description_extended( + language: str, request: Request +) -> ProductDescriptionExtended: + """Return a localized product description with component metadata.""" require_rm_caller(request) settings = RMCryptPadSettings.singleton() return ProductDescriptionExtended( shortname="cryptpad", title="CryptPad", - icon=None, + icon="/ui/cryptpad/cryptpad-mark.svg", description=_description_text(language), language=language, docs=settings.docs_url, diff --git a/rmcryptpad/src/rmcryptpad/web/instructions.py b/rmcryptpad/src/rmcryptpad/web/instructions.py index 2849960..b5d2156 100644 --- a/rmcryptpad/src/rmcryptpad/web/instructions.py +++ b/rmcryptpad/src/rmcryptpad/web/instructions.py @@ -11,11 +11,16 @@ @router.post("/instructions/{language}", response_model=InstructionsResponse) -async def user_instructions(user: UserCRUDRequest, request: Request, language: str) -> InstructionsResponse: +async def user_instructions( + user: UserCRUDRequest, request: Request, language: str +) -> InstructionsResponse: """Return product guidance for the current callsign.""" require_rm_caller(request) return InstructionsResponse( callsign=user.callsign, language=language, - instructions="Open CryptPad through the Deploy App mTLS host and keep the certificate-backed account in sync.", + instructions=( + "Open CryptPad through the Deploy App mTLS host" + " and keep the certificate-backed account in sync." + ), ) diff --git a/rmcryptpad/src/rmcryptpad/web/interop.py b/rmcryptpad/src/rmcryptpad/web/interop.py index 35c66c7..208b11d 100644 --- a/rmcryptpad/src/rmcryptpad/web/interop.py +++ b/rmcryptpad/src/rmcryptpad/web/interop.py @@ -13,7 +13,9 @@ @interoprouter.post("/add", response_model=OperationResultResponse) -async def add_product(product: ProductAddRequest, request: Request) -> OperationResultResponse: +async def add_product( + product: ProductAddRequest, request: Request +) -> OperationResultResponse: """Register a peer product.""" require_rm_caller(request) await Product.create_or_update(certcn=product.certcn) @@ -24,4 +26,6 @@ async def add_product(product: ProductAddRequest, request: Request) -> Operation async def get_authz(request: Request) -> ProductAuthzResponse: """Return product-to-product authz credentials.""" product = await Product.by_cn(get_client_cn(request)) - return ProductAuthzResponse(type="basic", username=product.api_username, password=product.api_password) + return ProductAuthzResponse( + type="basic", username=product.api_username, password=product.api_password + ) diff --git a/rmcryptpad/src/rmcryptpad/web/oidc.py b/rmcryptpad/src/rmcryptpad/web/oidc.py index 94d38d9..db90874 100644 --- a/rmcryptpad/src/rmcryptpad/web/oidc.py +++ b/rmcryptpad/src/rmcryptpad/web/oidc.py @@ -3,7 +3,7 @@ from __future__ import annotations from fastapi import APIRouter, Depends, Request -from fastapi.responses import JSONResponse +from fastapi.responses import JSONResponse, RedirectResponse from ..oidc import OIDCProvider from .security import require_verified_mtls_header @@ -13,24 +13,29 @@ @router.get("/.well-known/openid-configuration") async def discovery() -> JSONResponse: + """Return the OIDC discovery document.""" return JSONResponse(OIDCProvider().discovery_document()) @router.get("/oidc/jwks.json") async def jwks() -> JSONResponse: + """Return the OIDC JSON Web Key Set.""" return JSONResponse(OIDCProvider().jwks_document()) @router.get("/oidc/authorize", dependencies=[Depends(require_verified_mtls_header)]) -async def authorize(request: Request): +async def authorize(request: Request) -> RedirectResponse: + """Handle OIDC authorization requests.""" return await OIDCProvider().authorize(request) @router.post("/oidc/token") async def token(request: Request) -> JSONResponse: + """Exchange an authorization code for tokens.""" return JSONResponse(await OIDCProvider().token(request)) @router.get("/oidc/userinfo") async def userinfo(request: Request) -> JSONResponse: + """Return user info for the authenticated token.""" return JSONResponse(await OIDCProvider().userinfo(request)) diff --git a/rmcryptpad/src/rmcryptpad/web/security.py b/rmcryptpad/src/rmcryptpad/web/security.py index ded7ff9..05b24be 100644 --- a/rmcryptpad/src/rmcryptpad/web/security.py +++ b/rmcryptpad/src/rmcryptpad/web/security.py @@ -25,16 +25,23 @@ def normalize_client_fingerprint(fingerprint: str | None) -> str | None: """Normalize forwarded fingerprints to lowercase hex without separators.""" if not fingerprint: return None - return "".join(character for character in fingerprint if character not in ": \t\r\n").lower() + return "".join( + character for character in fingerprint if character not in ": \t\r\n" + ).lower() def require_mtls_header(request: Request) -> str: """Require a forwarded client certificate DN header.""" dn = request.headers.get(CLIENT_DN_HEADER) if not dn: - raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="Missing client certificate header") + raise HTTPException( + status_code=status.HTTP_403_FORBIDDEN, + detail="Missing client certificate header", + ) request.state.mtlsdn = {"DN": dn, "CN": extract_cn(dn) or dn.strip()} - request.state.mtlsfingerprint = normalize_client_fingerprint(request.headers.get(CLIENT_FINGERPRINT_HEADER)) + request.state.mtlsfingerprint = normalize_client_fingerprint( + request.headers.get(CLIENT_FINGERPRINT_HEADER) + ) return dn @@ -42,14 +49,17 @@ def require_verified_mtls_header(request: Request) -> str: """Require a forwarded client certificate DN header and a trusted proxy verification signal.""" dn = require_mtls_header(request) if request.headers.get(CLIENT_VERIFY_HEADER) != CLIENT_VERIFY_SUCCESS: - raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="Unverified client certificate") + raise HTTPException( + status_code=status.HTTP_403_FORBIDDEN, + detail="Unverified client certificate", + ) request.state.mtlsverified = True return dn def get_client_cn(request: Request) -> str: """Return the forwarded client CN.""" - payload = getattr(request.state, "mtlsdn", None) + payload: dict[str, str] | None = getattr(request.state, "mtlsdn", None) if payload and payload.get("CN"): return payload["CN"] dn = require_verified_mtls_header(request) diff --git a/rmcryptpad/src/rmcryptpad/web/usercrud.py b/rmcryptpad/src/rmcryptpad/web/usercrud.py index 497efc2..f2b3da6 100644 --- a/rmcryptpad/src/rmcryptpad/web/usercrud.py +++ b/rmcryptpad/src/rmcryptpad/web/usercrud.py @@ -19,21 +19,29 @@ @crudrouter.post("/created", response_model=OperationResultResponse) -async def user_created(user: UserCRUDRequest, request: Request) -> OperationResultResponse: +async def user_created( + user: UserCRUDRequest, request: Request +) -> OperationResultResponse: """Create or refresh a callsign.""" require_rm_caller(request) - await User.create_or_update(callsign=user.callsign, rmuuid=user.uuid, cert_pem=user.x509cert) + await User.create_or_update( + callsign=user.callsign, rmuuid=user.uuid, cert_pem=user.x509cert + ) return OperationResultResponse(success=True) @crudrouter.post("/revoked", response_model=OperationResultResponse) -async def user_revoked(user: UserCRUDRequest, request: Request) -> OperationResultResponse: +async def user_revoked( + user: UserCRUDRequest, request: Request +) -> OperationResultResponse: """Revoke a callsign.""" require_rm_caller(request) try: dbuser = await User.by_callsign(user.callsign, allow_inactive=True) except NotFound: - dbuser = await User.create_or_update(callsign=user.callsign, rmuuid=user.uuid, cert_pem=user.x509cert) + dbuser = await User.create_or_update( + callsign=user.callsign, rmuuid=user.uuid, cert_pem=user.x509cert + ) dbuser.revoked = datetime.datetime.now(datetime.UTC) with EngineWrapper.get_session() as session: session.add(dbuser) @@ -43,24 +51,42 @@ async def user_revoked(user: UserCRUDRequest, request: Request) -> OperationResu @crudrouter.post("/promoted", response_model=OperationResultResponse) -async def user_promoted(user: UserCRUDRequest, request: Request) -> OperationResultResponse: +async def user_promoted( + user: UserCRUDRequest, request: Request +) -> OperationResultResponse: """Promote a callsign to admin.""" require_rm_caller(request) - await User.create_or_update(callsign=user.callsign, rmuuid=user.uuid, cert_pem=user.x509cert, is_rmadmin=True) + await User.create_or_update( + callsign=user.callsign, + rmuuid=user.uuid, + cert_pem=user.x509cert, + is_rmadmin=True, + ) return OperationResultResponse(success=True) @crudrouter.post("/demoted", response_model=OperationResultResponse) -async def user_demoted(user: UserCRUDRequest, request: Request) -> OperationResultResponse: +async def user_demoted( + user: UserCRUDRequest, request: Request +) -> OperationResultResponse: """Demote a callsign from admin.""" require_rm_caller(request) - await User.create_or_update(callsign=user.callsign, rmuuid=user.uuid, cert_pem=user.x509cert, is_rmadmin=False) + await User.create_or_update( + callsign=user.callsign, + rmuuid=user.uuid, + cert_pem=user.x509cert, + is_rmadmin=False, + ) return OperationResultResponse(success=True) @crudrouter.put("/updated", response_model=OperationResultResponse) -async def user_updated(user: UserCRUDRequest, request: Request) -> OperationResultResponse: +async def user_updated( + user: UserCRUDRequest, request: Request +) -> OperationResultResponse: """Refresh a callsign without migrating identity.""" require_rm_caller(request) - await User.create_or_update(callsign=user.callsign, rmuuid=user.uuid, cert_pem=user.x509cert) + await User.create_or_update( + callsign=user.callsign, rmuuid=user.uuid, cert_pem=user.x509cert + ) return OperationResultResponse(success=True) diff --git a/rmcryptpad/tests/conftest.py b/rmcryptpad/tests/conftest.py index d9751a5..ec4d9a4 100644 --- a/rmcryptpad/tests/conftest.py +++ b/rmcryptpad/tests/conftest.py @@ -1,8 +1,11 @@ """Test helpers for rmcryptpad.""" +# pylint: disable=redefined-outer-name + from __future__ import annotations import asyncio +import datetime import shutil import sys from pathlib import Path @@ -10,6 +13,10 @@ import pytest import pytest_asyncio +from cryptography import x509 +from cryptography.hazmat.primitives import hashes, serialization +from cryptography.hazmat.primitives.asymmetric import rsa +from cryptography.x509.oid import NameOID from pytest_docker.plugin import Services @@ -20,6 +27,51 @@ sys.path.insert(0, str(SRC)) +# --------------------------------------------------------------------------- +# Shared test helpers +# --------------------------------------------------------------------------- + + +def build_cert_pem(common_name: str) -> str: + """Generate a self-signed certificate PEM for testing.""" + key = rsa.generate_private_key(public_exponent=65537, key_size=2048) + subject = issuer = x509.Name([x509.NameAttribute(NameOID.COMMON_NAME, common_name)]) + cert = ( + x509.CertificateBuilder() + .subject_name(subject) + .issuer_name(issuer) + .public_key(key.public_key()) + .serial_number(x509.random_serial_number()) + .not_valid_before( + datetime.datetime.now(datetime.UTC) - datetime.timedelta(days=1) + ) + .not_valid_after( + datetime.datetime.now(datetime.UTC) + datetime.timedelta(days=30) + ) + .sign(key, hashes.SHA256()) + ) + return cert.public_bytes(serialization.Encoding.PEM).decode("utf-8") + + +def rm_headers() -> dict[str, str]: + """Return mTLS headers simulating the Rasenmaeher control plane.""" + return {"X-ClientCert-DN": "CN=rasenmaeher,O=RM", "X-SSL-Client-Verify": "SUCCESS"} + + +def make_user_payload(callsign: str) -> dict[str, str]: + """Build a user CRUD request body with a fresh certificate.""" + return { + "uuid": f"uuid-{callsign}", + "callsign": callsign, + "x509cert": build_cert_pem(callsign), + } + + +# --------------------------------------------------------------------------- +# Fixtures +# --------------------------------------------------------------------------- + + @pytest.fixture(scope="session") def docker_compose_file() -> str: """Point pytest-docker at the local test compose file.""" @@ -57,16 +109,27 @@ async def dbinstance( patch = pytest.MonkeyPatch() try: patch.setenv("RMCRYPTPAD_DATABASE_HOST", docker_ip) - patch.setenv("RMCRYPTPAD_DATABASE_PORT", str(docker_services.port_for("postgres", 5432))) - from rmcryptpad.config import DBSettings # pylint: disable=import-outside-toplevel - from rmcryptpad.oidc.keys import OIDCKeyManager # pylint: disable=import-outside-toplevel - from rmcryptpad.db.engine import EngineWrapper # pylint: disable=import-outside-toplevel - - DBSettings._singleton = None - EngineWrapper._singleton = None - OIDCKeyManager._singleton = None + patch.setenv( + "RMCRYPTPAD_DATABASE_PORT", str(docker_services.port_for("postgres", 5432)) + ) + from rmcryptpad.config import ( # pylint: disable=import-outside-toplevel + DBSettings, + ) + from rmcryptpad.oidc.keys import ( # pylint: disable=import-outside-toplevel + OIDCKeyManager, + ) + from rmcryptpad.db.engine import ( # pylint: disable=import-outside-toplevel + EngineWrapper, + ) + + DBSettings._singleton = None # pylint: disable=protected-access + EngineWrapper._singleton = None # pylint: disable=protected-access + OIDCKeyManager._singleton = None # pylint: disable=protected-access await asyncio.sleep(1.0) - from rmcryptpad.db.dbinit import drop_db, init_db # pylint: disable=import-outside-toplevel + from rmcryptpad.db.dbinit import ( # pylint: disable=import-outside-toplevel + drop_db, + init_db, + ) await init_db() yield None diff --git a/rmcryptpad/tests/docker-compose.yml b/rmcryptpad/tests/docker-compose.yml index ea450c4..4c4752e 100644 --- a/rmcryptpad/tests/docker-compose.yml +++ b/rmcryptpad/tests/docker-compose.yml @@ -6,7 +6,7 @@ services: environment: POSTGRES_DB: rmcryptpad POSTGRES_USER: rmcryptpad - POSTGRES_PASSWORD: rmcryptpadtestpwd + POSTGRES_PASSWORD: rmcryptpadtestpwd # pragma: allowlist secret healthcheck: test: "pg_isready --dbname=$$POSTGRES_DB --username=$$POSTGRES_USER || exit 1" interval: 5s diff --git a/rmcryptpad/tests/test_clients.py b/rmcryptpad/tests/test_clients.py index 7dbb3a2..045d024 100644 --- a/rmcryptpad/tests/test_clients.py +++ b/rmcryptpad/tests/test_clients.py @@ -2,13 +2,8 @@ from __future__ import annotations -import datetime - import pytest -from cryptography import x509 -from cryptography.hazmat.primitives import hashes, serialization -from cryptography.hazmat.primitives.asymmetric import rsa -from cryptography.x509.oid import NameOID +from conftest import make_user_payload, rm_headers from fastapi.testclient import TestClient from rmcryptpad.db.errors import Deleted @@ -16,37 +11,16 @@ from rmcryptpad.web.application import get_app_no_init -def _rm_headers() -> dict[str, str]: - return {"X-ClientCert-DN": "CN=rasenmaeher,O=RM", "X-SSL-Client-Verify": "SUCCESS"} - - -def _user_payload(callsign: str) -> dict[str, str]: - key = rsa.generate_private_key(public_exponent=65537, key_size=2048) - subject = issuer = x509.Name([x509.NameAttribute(NameOID.COMMON_NAME, callsign)]) - cert = ( - x509.CertificateBuilder() - .subject_name(subject) - .issuer_name(issuer) - .public_key(key.public_key()) - .serial_number(x509.random_serial_number()) - .not_valid_before(datetime.datetime.now(datetime.UTC) - datetime.timedelta(days=1)) - .not_valid_after(datetime.datetime.now(datetime.UTC) + datetime.timedelta(days=30)) - .sign(key, hashes.SHA256()) - ) - return { - "uuid": f"uuid-{callsign}", - "callsign": callsign, - "x509cert": cert.public_bytes(serialization.Encoding.PEM).decode("utf-8"), - } - - def test_client_data_routes_return_settings_urls(dbinstance: None) -> None: + """Verify both client data endpoints return correct CryptPad URLs.""" _ = dbinstance app = get_app_no_init() with TestClient(app) as client: for path in ("/api/v2/clients/data", "/api/v2/admin/clients/data"): - response = client.post(path, headers=_rm_headers(), json=_user_payload("VIRTA-1")) + response = client.post( + path, headers=rm_headers(), json=make_user_payload("VIRTA-1") + ) assert response.status_code == 200 payload = response.json()["data"] assert payload["url"] == "https://cryptpad.localhost:8443" @@ -56,25 +30,28 @@ def test_client_data_routes_return_settings_urls(dbinstance: None) -> None: @pytest.mark.asyncio -async def test_revoked_user_stays_revoked_after_client_data_access(dbinstance: None) -> None: +async def test_revoked_user_stays_revoked_after_client_data_access( + dbinstance: None, +) -> None: + """Ensure accessing client data does not re-enable a revoked user.""" _ = dbinstance app = get_app_no_init() with TestClient(app) as client: client.post( "/api/v1/users/created", - headers=_rm_headers(), - json=_user_payload("VIRTA-REVOKED"), + headers=rm_headers(), + json=make_user_payload("VIRTA-REVOKED"), ) client.post( "/api/v1/users/revoked", - headers=_rm_headers(), - json=_user_payload("VIRTA-REVOKED"), + headers=rm_headers(), + json=make_user_payload("VIRTA-REVOKED"), ) response = client.post( "/api/v2/clients/data", - headers=_rm_headers(), - json=_user_payload("VIRTA-REVOKED"), + headers=rm_headers(), + json=make_user_payload("VIRTA-REVOKED"), ) assert response.status_code == 200 diff --git a/rmcryptpad/tests/test_description.py b/rmcryptpad/tests/test_description.py index 69fbf52..193a0c9 100644 --- a/rmcryptpad/tests/test_description.py +++ b/rmcryptpad/tests/test_description.py @@ -2,13 +2,8 @@ from __future__ import annotations -import datetime - import pytest -from cryptography import x509 -from cryptography.hazmat.primitives import hashes, serialization -from cryptography.hazmat.primitives.asymmetric import rsa -from cryptography.x509.oid import NameOID +from conftest import make_user_payload, rm_headers from fastapi.testclient import TestClient from rmcryptpad.db.errors import Deleted @@ -16,40 +11,17 @@ from rmcryptpad.web.application import get_app_no_init -def _rm_headers() -> dict[str, str]: - return {"X-ClientCert-DN": "CN=rasenmaeher,O=RM", "X-SSL-Client-Verify": "SUCCESS"} - - -def _user_payload(callsign: str) -> dict[str, str]: - key = rsa.generate_private_key(public_exponent=65537, key_size=2048) - subject = issuer = x509.Name([x509.NameAttribute(NameOID.COMMON_NAME, callsign)]) - cert = ( - x509.CertificateBuilder() - .subject_name(subject) - .issuer_name(issuer) - .public_key(key.public_key()) - .serial_number(x509.random_serial_number()) - .not_valid_before(datetime.datetime.now(datetime.UTC) - datetime.timedelta(days=1)) - .not_valid_after(datetime.datetime.now(datetime.UTC) + datetime.timedelta(days=30)) - .sign(key, hashes.SHA256()) - ) - return { - "uuid": f"uuid-{callsign}", - "callsign": callsign, - "x509cert": cert.public_bytes(serialization.Encoding.PEM).decode("utf-8"), - } - - def test_description_routes_return_cryptpad_metadata(dbinstance: None) -> None: + """Verify v1 and v2 description routes return correct product metadata.""" _ = dbinstance app = get_app_no_init() with TestClient(app) as client: - v1 = client.get("/api/v1/description/en", headers=_rm_headers()) + v1 = client.get("/api/v1/description/en", headers=rm_headers()) assert v1.status_code == 200 assert v1.json()["shortname"] == "cryptpad" - v2 = client.get("/api/v2/description/en", headers=_rm_headers()) + v2 = client.get("/api/v2/description/en", headers=rm_headers()) assert v2.status_code == 200 payload = v2.json() assert payload["shortname"] == "cryptpad" @@ -58,14 +30,27 @@ def test_description_routes_return_cryptpad_metadata(dbinstance: None) -> None: @pytest.mark.asyncio async def test_instructions_route_returns_product_guidance(dbinstance: None) -> None: + """Verify instructions route returns language-specific guidance.""" _ = dbinstance app = get_app_no_init() - payload = _user_payload("VIRTA-1") + payload = make_user_payload("VIRTA-1") with TestClient(app) as client: - assert client.post("/api/v1/users/created", headers=_rm_headers(), json=payload).status_code == 200 - assert client.post("/api/v1/users/revoked", headers=_rm_headers(), json=payload).status_code == 200 - response = client.post("/api/v1/instructions/en", headers=_rm_headers(), json=payload) + assert ( + client.post( + "/api/v1/users/created", headers=rm_headers(), json=payload + ).status_code + == 200 + ) + assert ( + client.post( + "/api/v1/users/revoked", headers=rm_headers(), json=payload + ).status_code + == 200 + ) + response = client.post( + "/api/v1/instructions/en", headers=rm_headers(), json=payload + ) assert response.status_code == 200 body = response.json() assert body["language"] == "en" @@ -76,6 +61,7 @@ async def test_instructions_route_returns_product_guidance(dbinstance: None) -> def test_healthcheck_is_available(dbinstance: None) -> None: + """Verify the health check endpoint returns healthy.""" _ = dbinstance app = get_app_no_init() diff --git a/rmcryptpad/tests/test_interop.py b/rmcryptpad/tests/test_interop.py index bfc0699..b235f74 100644 --- a/rmcryptpad/tests/test_interop.py +++ b/rmcryptpad/tests/test_interop.py @@ -2,53 +2,44 @@ from __future__ import annotations -import datetime - import pytest -from cryptography import x509 -from cryptography.hazmat.primitives import hashes, serialization -from cryptography.hazmat.primitives.asymmetric import rsa -from cryptography.x509.oid import NameOID +from conftest import build_cert_pem, rm_headers from fastapi.testclient import TestClient from rmcryptpad.db.product import Product from rmcryptpad.web.application import get_app_no_init -def _rm_headers() -> dict[str, str]: - return {"X-ClientCert-DN": "CN=rasenmaeher,O=RM", "X-SSL-Client-Verify": "SUCCESS"} - - def _product_headers(certcn: str) -> dict[str, str]: return {"X-ClientCert-DN": f"CN={certcn},O=RM", "X-SSL-Client-Verify": "SUCCESS"} def _payload(certcn: str) -> dict[str, str]: - key = rsa.generate_private_key(public_exponent=65537, key_size=2048) - subject = issuer = x509.Name([x509.NameAttribute(NameOID.COMMON_NAME, certcn)]) - cert = ( - x509.CertificateBuilder() - .subject_name(subject) - .issuer_name(issuer) - .public_key(key.public_key()) - .serial_number(x509.random_serial_number()) - .not_valid_before(datetime.datetime.now(datetime.UTC) - datetime.timedelta(days=1)) - .not_valid_after(datetime.datetime.now(datetime.UTC) + datetime.timedelta(days=30)) - .sign(key, hashes.SHA256()) - ) - return {"certcn": certcn, "x509cert": cert.public_bytes(serialization.Encoding.PEM).decode("utf-8")} + return { + "certcn": certcn, + "x509cert": build_cert_pem(certcn), + } @pytest.mark.asyncio -async def test_interop_add_and_authz_return_product_credentials(dbinstance: None) -> None: +async def test_interop_add_and_authz_return_product_credentials( + dbinstance: None, +) -> None: + """Verify interop add creates a product and authz returns credentials.""" _ = dbinstance app = get_app_no_init() with TestClient(app) as client: - response = client.post("/api/v1/interop/add", headers=_rm_headers(), json=_payload("cryptpad.localhost")) + response = client.post( + "/api/v1/interop/add", + headers=rm_headers(), + json=_payload("cryptpad.localhost"), + ) assert response.status_code == 200 - authz = client.get("/api/v1/interop/authz", headers=_product_headers("cryptpad.localhost")) + authz = client.get( + "/api/v1/interop/authz", headers=_product_headers("cryptpad.localhost") + ) assert authz.status_code == 200 payload = authz.json() assert payload["type"] == "basic" diff --git a/rmcryptpad/tests/test_oidc.py b/rmcryptpad/tests/test_oidc.py index ca84574..cede293 100644 --- a/rmcryptpad/tests/test_oidc.py +++ b/rmcryptpad/tests/test_oidc.py @@ -3,17 +3,12 @@ from __future__ import annotations import base64 -import datetime import hashlib -import json import urllib.parse from pathlib import Path import pytest -from cryptography import x509 -from cryptography.hazmat.primitives import hashes, serialization -from cryptography.hazmat.primitives.asymmetric import rsa -from cryptography.x509.oid import NameOID +from conftest import build_cert_pem, rm_headers from fastapi.testclient import TestClient from rmcryptpad.config import RMCryptPadSettings @@ -22,33 +17,15 @@ from rmcryptpad.web.application import get_app_no_init -def _build_cert_pem(common_name: str) -> str: - key = rsa.generate_private_key(public_exponent=65537, key_size=2048) - subject = issuer = x509.Name([x509.NameAttribute(NameOID.COMMON_NAME, common_name)]) - cert = ( - x509.CertificateBuilder() - .subject_name(subject) - .issuer_name(issuer) - .public_key(key.public_key()) - .serial_number(x509.random_serial_number()) - .not_valid_before(datetime.datetime.now(datetime.UTC) - datetime.timedelta(days=1)) - .not_valid_after(datetime.datetime.now(datetime.UTC) + datetime.timedelta(days=30)) - .sign(key, hashes.SHA256()) - ) - return cert.public_bytes(serialization.Encoding.PEM).decode("utf-8") - - -def _rm_headers() -> dict[str, str]: - return {"X-ClientCert-DN": "CN=rasenmaeher,O=RM", "X-SSL-Client-Verify": "SUCCESS"} - - def _user_headers(callsign: str) -> dict[str, str]: return {"X-ClientCert-DN": f"CN={callsign},O=RM", "X-SSL-Client-Verify": "SUCCESS"} def _fingerprint_header(cert_pem: str) -> str: fingerprint = fingerprint_pem(cert_pem) - return ":".join(fingerprint[i : i + 2] for i in range(0, len(fingerprint), 2)).upper() + return ":".join( + fingerprint[i : i + 2] for i in range(0, len(fingerprint), 2) + ).upper() def _configure_oidc(monkeypatch: pytest.MonkeyPatch, tmp_path: Path) -> None: @@ -58,17 +35,24 @@ def _configure_oidc(monkeypatch: pytest.MonkeyPatch, tmp_path: Path) -> None: monkeypatch.setenv("RMCRYPTPAD_OIDC_CLIENT_SECRET", "cryptpad-secret") monkeypatch.setenv("RMCRYPTPAD_OIDC_CODE_TTL_SECONDS", "300") monkeypatch.setenv("RMCRYPTPAD_OIDC_TOKEN_TTL_SECONDS", "3600") - RMCryptPadSettings._singleton = None - OIDCKeyManager._singleton = None + RMCryptPadSettings._singleton = None # pylint: disable=protected-access + OIDCKeyManager._singleton = None # pylint: disable=protected-access def _pkce_pair() -> tuple[str, str]: verifier = "correct-horse-battery-staple" - challenge = base64.urlsafe_b64encode(hashlib.sha256(verifier.encode("utf-8")).digest()).decode("utf-8").rstrip("=") + challenge = ( + base64.urlsafe_b64encode(hashlib.sha256(verifier.encode("utf-8")).digest()) + .decode("utf-8") + .rstrip("=") + ) return verifier, challenge -def test_discovery_document_returns_expected_urls(dbinstance: None, monkeypatch: pytest.MonkeyPatch, tmp_path) -> None: +def test_discovery_document_returns_expected_urls( + dbinstance: None, monkeypatch: pytest.MonkeyPatch, tmp_path: Path +) -> None: + """Verify the OIDC discovery document contains correct endpoint URLs.""" _ = dbinstance _configure_oidc(monkeypatch, tmp_path) app = get_app_no_init() @@ -85,19 +69,38 @@ def test_discovery_document_returns_expected_urls(dbinstance: None, monkeypatch: assert payload["jwks_uri"].endswith("/oidc/jwks.json") -def test_authorize_rejects_missing_or_revoked_active_callsign(dbinstance: None, monkeypatch: pytest.MonkeyPatch, tmp_path) -> None: +def test_authorize_rejects_missing_or_revoked_active_callsign( + dbinstance: None, monkeypatch: pytest.MonkeyPatch, tmp_path: Path +) -> None: + """Verify authorize rejects unknown and revoked callsigns.""" _ = dbinstance _configure_oidc(monkeypatch, tmp_path) app = get_app_no_init() - cert_pem = _build_cert_pem("VIRTA-REVOKED") + cert_pem = build_cert_pem("VIRTA-REVOKED") with TestClient(app) as client: assert ( - client.post("/api/v1/users/created", headers=_rm_headers(), json={"uuid": "uuid-1", "callsign": "VIRTA-REVOKED", "x509cert": cert_pem}).status_code + client.post( + "/api/v1/users/created", + headers=rm_headers(), + json={ + "uuid": "uuid-1", + "callsign": "VIRTA-REVOKED", + "x509cert": cert_pem, + }, + ).status_code == 200 ) assert ( - client.post("/api/v1/users/revoked", headers=_rm_headers(), json={"uuid": "uuid-1", "callsign": "VIRTA-REVOKED", "x509cert": cert_pem}).status_code + client.post( + "/api/v1/users/revoked", + headers=rm_headers(), + json={ + "uuid": "uuid-1", + "callsign": "VIRTA-REVOKED", + "x509cert": cert_pem, + }, + ).status_code == 200 ) missing = client.get("/oidc/authorize") @@ -120,16 +123,23 @@ def test_authorize_rejects_missing_or_revoked_active_callsign(dbinstance: None, assert revoked.status_code == 403 -def test_authorize_issues_code_for_active_callsign(dbinstance: None, monkeypatch: pytest.MonkeyPatch, tmp_path) -> None: +def test_authorize_issues_code_for_active_callsign( + dbinstance: None, monkeypatch: pytest.MonkeyPatch, tmp_path: Path +) -> None: + """Verify authorize redirects with an authorization code for active users.""" _ = dbinstance _configure_oidc(monkeypatch, tmp_path) app = get_app_no_init() - cert_pem = _build_cert_pem("VIRTA-1") + cert_pem = build_cert_pem("VIRTA-1") verifier, challenge = _pkce_pair() with TestClient(app) as client: assert ( - client.post("/api/v1/users/created", headers=_rm_headers(), json={"uuid": "uuid-1", "callsign": "VIRTA-1", "x509cert": cert_pem}).status_code + client.post( + "/api/v1/users/created", + headers=rm_headers(), + json={"uuid": "uuid-1", "callsign": "VIRTA-1", "x509cert": cert_pem}, + ).status_code == 200 ) response = client.get( @@ -158,22 +168,32 @@ def test_authorize_issues_code_for_active_callsign(dbinstance: None, monkeypatch assert verifier -def test_authorize_rejects_mismatched_certificate_fingerprint(dbinstance: None, monkeypatch: pytest.MonkeyPatch, tmp_path) -> None: +def test_authorize_rejects_mismatched_certificate_fingerprint( + dbinstance: None, monkeypatch: pytest.MonkeyPatch, tmp_path: Path +) -> None: + """Verify authorize rejects requests with wrong certificate fingerprint.""" _ = dbinstance _configure_oidc(monkeypatch, tmp_path) app = get_app_no_init() - active_cert = _build_cert_pem("VIRTA-1") - wrong_cert = _build_cert_pem("VIRTA-1") + active_cert = build_cert_pem("VIRTA-1") + wrong_cert = build_cert_pem("VIRTA-1") _, challenge = _pkce_pair() with TestClient(app) as client: assert ( - client.post("/api/v1/users/created", headers=_rm_headers(), json={"uuid": "uuid-1", "callsign": "VIRTA-1", "x509cert": active_cert}).status_code + client.post( + "/api/v1/users/created", + headers=rm_headers(), + json={"uuid": "uuid-1", "callsign": "VIRTA-1", "x509cert": active_cert}, + ).status_code == 200 ) response = client.get( "/oidc/authorize", - headers={**_user_headers("VIRTA-1"), "X-SSL-Client-Fingerprint": _fingerprint_header(wrong_cert)}, + headers={ + **_user_headers("VIRTA-1"), + "X-SSL-Client-Fingerprint": _fingerprint_header(wrong_cert), + }, params={ "client_id": "cryptpad", "redirect_uri": "https://cryptpad.localhost/ssoauth", @@ -190,16 +210,23 @@ def test_authorize_rejects_mismatched_certificate_fingerprint(dbinstance: None, assert response.status_code == 403 -def test_authorize_rejects_wrong_callsign(dbinstance: None, monkeypatch: pytest.MonkeyPatch, tmp_path) -> None: +def test_authorize_rejects_wrong_callsign( + dbinstance: None, monkeypatch: pytest.MonkeyPatch, tmp_path: Path +) -> None: + """Verify authorize rejects a callsign that doesn't match any user.""" _ = dbinstance _configure_oidc(monkeypatch, tmp_path) app = get_app_no_init() - active_cert = _build_cert_pem("VIRTA-1") + active_cert = build_cert_pem("VIRTA-1") wrong_headers = _user_headers("VIRTA-2") with TestClient(app) as client: assert ( - client.post("/api/v1/users/created", headers=_rm_headers(), json={"uuid": "uuid-1", "callsign": "VIRTA-1", "x509cert": active_cert}).status_code + client.post( + "/api/v1/users/created", + headers=rm_headers(), + json={"uuid": "uuid-1", "callsign": "VIRTA-1", "x509cert": active_cert}, + ).status_code == 200 ) response = client.get( @@ -220,16 +247,23 @@ def test_authorize_rejects_wrong_callsign(dbinstance: None, monkeypatch: pytest. assert response.status_code == 403 -def test_token_exchange_and_userinfo_work_with_pkce_and_basic_auth(dbinstance: None, monkeypatch: pytest.MonkeyPatch, tmp_path) -> None: +def test_token_exchange_and_userinfo_work_with_pkce_and_basic_auth( + dbinstance: None, monkeypatch: pytest.MonkeyPatch, tmp_path: Path +) -> None: + """Verify full OIDC flow: authorize, token exchange, and userinfo.""" _ = dbinstance _configure_oidc(monkeypatch, tmp_path) app = get_app_no_init() - cert_pem = _build_cert_pem("VIRTA-1") + cert_pem = build_cert_pem("VIRTA-1") verifier, challenge = _pkce_pair() with TestClient(app) as client: assert ( - client.post("/api/v1/users/created", headers=_rm_headers(), json={"uuid": "uuid-1", "callsign": "VIRTA-1", "x509cert": cert_pem}).status_code + client.post( + "/api/v1/users/created", + headers=rm_headers(), + json={"uuid": "uuid-1", "callsign": "VIRTA-1", "x509cert": cert_pem}, + ).status_code == 200 ) authz = client.get( @@ -247,7 +281,9 @@ def test_token_exchange_and_userinfo_work_with_pkce_and_basic_auth(dbinstance: N }, follow_redirects=False, ) - code = urllib.parse.parse_qs(urllib.parse.urlparse(authz.headers["location"]).query)["code"][0] + code = urllib.parse.parse_qs( + urllib.parse.urlparse(authz.headers["location"]).query + )["code"][0] token = client.post( "/oidc/token", auth=("cryptpad", "cryptpad-secret"), @@ -266,7 +302,10 @@ def test_token_exchange_and_userinfo_work_with_pkce_and_basic_auth(dbinstance: N assert payload["id_token"] with TestClient(app) as client: - userinfo = client.get("/oidc/userinfo", headers={"Authorization": f"Bearer {payload['access_token']}"}) + userinfo = client.get( + "/oidc/userinfo", + headers={"Authorization": f"Bearer {payload['access_token']}"}, + ) assert userinfo.status_code == 200 claims = userinfo.json() @@ -276,16 +315,23 @@ def test_token_exchange_and_userinfo_work_with_pkce_and_basic_auth(dbinstance: N assert claims["nonce"] == "nonce-a" -def test_token_rejects_reuse_and_invalid_code(dbinstance: None, monkeypatch: pytest.MonkeyPatch, tmp_path) -> None: +def test_token_rejects_reuse_and_invalid_code( + dbinstance: None, monkeypatch: pytest.MonkeyPatch, tmp_path: Path +) -> None: + """Verify authorization codes cannot be reused and invalid codes are rejected.""" _ = dbinstance _configure_oidc(monkeypatch, tmp_path) app = get_app_no_init() - cert_pem = _build_cert_pem("VIRTA-1") + cert_pem = build_cert_pem("VIRTA-1") verifier, challenge = _pkce_pair() with TestClient(app) as client: assert ( - client.post("/api/v1/users/created", headers=_rm_headers(), json={"uuid": "uuid-1", "callsign": "VIRTA-1", "x509cert": cert_pem}).status_code + client.post( + "/api/v1/users/created", + headers=rm_headers(), + json={"uuid": "uuid-1", "callsign": "VIRTA-1", "x509cert": cert_pem}, + ).status_code == 200 ) authz = client.get( @@ -303,7 +349,9 @@ def test_token_rejects_reuse_and_invalid_code(dbinstance: None, monkeypatch: pyt }, follow_redirects=False, ) - code = urllib.parse.parse_qs(urllib.parse.urlparse(authz.headers["location"]).query)["code"][0] + code = urllib.parse.parse_qs( + urllib.parse.urlparse(authz.headers["location"]).query + )["code"][0] first = client.post( "/oidc/token", auth=("cryptpad", "cryptpad-secret"), diff --git a/rmcryptpad/tests/test_orm.py b/rmcryptpad/tests/test_orm.py index 0fac413..4ed542c 100644 --- a/rmcryptpad/tests/test_orm.py +++ b/rmcryptpad/tests/test_orm.py @@ -5,10 +5,9 @@ import datetime import pytest +from conftest import build_cert_pem from cryptography import x509 -from cryptography.hazmat.primitives import hashes, serialization -from cryptography.hazmat.primitives.asymmetric import rsa -from cryptography.x509.oid import NameOID +from cryptography.hazmat.primitives import hashes from rmcryptpad.db.errors import Deleted from rmcryptpad.db.oidc_code import OIDCAuthorizationCode @@ -16,36 +15,12 @@ from rmcryptpad.db.user import User, fingerprint_pem -def _build_test_cert_pem(common_name: str = "virta.example.local") -> tuple[str, str]: - """Create a deterministic-ish PEM certificate for fingerprint tests.""" - key = rsa.generate_private_key(public_exponent=65537, key_size=2048) - subject = issuer = x509.Name([x509.NameAttribute(NameOID.COMMON_NAME, common_name)]) - cert = ( - x509.CertificateBuilder() - .subject_name(subject) - .issuer_name(issuer) - .public_key(key.public_key()) - .serial_number(x509.random_serial_number()) - .not_valid_before(datetime.datetime.now(datetime.UTC) - datetime.timedelta(days=1)) - .not_valid_after(datetime.datetime.now(datetime.UTC) + datetime.timedelta(days=30)) - .sign(key, hashes.SHA256()) - ) - pem = cert.public_bytes(serialization.Encoding.PEM).decode("utf-8") - return pem, cert.fingerprint(hashes.SHA1()).hex() - - -def _build_test_cert_only(common_name: str) -> str: - """Create a PEM certificate for ORM persistence tests.""" - cert_pem, _ = _build_test_cert_pem(common_name) - return cert_pem - - @pytest.mark.asyncio async def test_callsign_is_identity(dbinstance: None) -> None: """Refreshing the same callsign should update the same row.""" _ = dbinstance - first_cert = _build_test_cert_only("VIRTA-1") - second_cert = _build_test_cert_only("VIRTA-1-refresh") + first_cert = build_cert_pem("VIRTA-1") + second_cert = build_cert_pem("VIRTA-1-refresh") created = await User.create_or_update( callsign="VIRTA-1", rmuuid="uuid-a", @@ -63,11 +38,13 @@ async def test_callsign_is_identity(dbinstance: None) -> None: @pytest.mark.asyncio -async def test_user_admin_state_is_preserved_when_not_explicitly_changed(dbinstance: None) -> None: +async def test_user_admin_state_is_preserved_when_not_explicitly_changed( + dbinstance: None, +) -> None: """Existing admin state should survive a normal callsign refresh.""" _ = dbinstance - first_cert = _build_test_cert_only("VIRTA-ADMIN") - second_cert = _build_test_cert_only("VIRTA-ADMIN-REFRESH") + first_cert = build_cert_pem("VIRTA-ADMIN") + second_cert = build_cert_pem("VIRTA-ADMIN-REFRESH") created = await User.create_or_update( callsign="VIRTA-ADMIN", rmuuid="uuid-admin", @@ -89,8 +66,8 @@ async def test_user_admin_state_is_preserved_when_not_explicitly_changed(dbinsta async def test_user_admin_state_changes_when_requested(dbinstance: None) -> None: """Explicit admin changes should still be applied.""" _ = dbinstance - first_cert = _build_test_cert_only("VIRTA-ADMIN-2") - second_cert = _build_test_cert_only("VIRTA-ADMIN-2-REFRESH") + first_cert = build_cert_pem("VIRTA-ADMIN-2") + second_cert = build_cert_pem("VIRTA-ADMIN-2-REFRESH") await User.create_or_update( callsign="VIRTA-ADMIN-2", rmuuid="uuid-admin-2", @@ -112,9 +89,11 @@ async def test_user_admin_state_changes_when_requested(dbinstance: None) -> None async def test_cert_fingerprint_matches_certificate_bytes(dbinstance: None) -> None: """Stored certificate fingerprints should match the actual certificate fingerprint.""" _ = dbinstance - cert_pem, expected_fingerprint = _build_test_cert_pem() + cert_pem = build_cert_pem("virta.example.local") + cert = x509.load_pem_x509_certificate(cert_pem.encode("utf-8")) + expected = cert.fingerprint(hashes.SHA1()).hex() # nosec B303 - assert fingerprint_pem(cert_pem) == expected_fingerprint + assert fingerprint_pem(cert_pem) == expected @pytest.mark.asyncio @@ -124,12 +103,12 @@ async def test_new_callsign_stays_separate(dbinstance: None) -> None: await User.create_or_update( callsign="VIRTA-1", rmuuid="uuid-a", - cert_pem=_build_test_cert_only("VIRTA-1"), + cert_pem=build_cert_pem("VIRTA-1"), ) await User.create_or_update( callsign="VIRTA-2", rmuuid="uuid-a", - cert_pem=_build_test_cert_only("VIRTA-2"), + cert_pem=build_cert_pem("VIRTA-2"), ) assert (await User.by_callsign("VIRTA-1")).callsign == "VIRTA-1" diff --git a/rmcryptpad/tests/test_package.py b/rmcryptpad/tests/test_package.py index 2c56d38..c232b89 100644 --- a/rmcryptpad/tests/test_package.py +++ b/rmcryptpad/tests/test_package.py @@ -1,12 +1,16 @@ +"""Tests for package metadata and default settings.""" + from rmcryptpad import __version__ from rmcryptpad.config import RMCryptPadSettings def test_version() -> None: + """Verify the package version string.""" assert __version__ == "0.1.0" def test_settings_defaults() -> None: + """Verify default settings values are sensible.""" settings = RMCryptPadSettings.singleton() assert settings.rmcn == "rasenmaeher" assert settings.public_url == "https://cryptpad.localhost:8443" diff --git a/rmcryptpad/tests/test_security.py b/rmcryptpad/tests/test_security.py index 7ba8251..38f2c81 100644 --- a/rmcryptpad/tests/test_security.py +++ b/rmcryptpad/tests/test_security.py @@ -2,13 +2,8 @@ from __future__ import annotations -import datetime - import pytest -from cryptography import x509 -from cryptography.hazmat.primitives import hashes, serialization -from cryptography.hazmat.primitives.asymmetric import rsa -from cryptography.x509.oid import NameOID +from conftest import build_cert_pem from fastapi import Request from fastapi.testclient import TestClient @@ -16,39 +11,31 @@ from rmcryptpad.web.security import require_verified_mtls_header -def _build_cert_pem(common_name: str) -> str: - key = rsa.generate_private_key(public_exponent=65537, key_size=2048) - subject = issuer = x509.Name([x509.NameAttribute(NameOID.COMMON_NAME, common_name)]) - cert = ( - x509.CertificateBuilder() - .subject_name(subject) - .issuer_name(issuer) - .public_key(key.public_key()) - .serial_number(x509.random_serial_number()) - .not_valid_before(datetime.datetime.now(datetime.UTC) - datetime.timedelta(days=1)) - .not_valid_after(datetime.datetime.now(datetime.UTC) + datetime.timedelta(days=30)) - .sign(key, hashes.SHA256()) - ) - return cert.public_bytes(serialization.Encoding.PEM).decode("utf-8") - - def _format_fingerprint_header(raw_fingerprint: str) -> str: - return ":".join(raw_fingerprint[i : i + 2] for i in range(0, len(raw_fingerprint), 2)).upper() + return ":".join( + raw_fingerprint[i : i + 2] for i in range(0, len(raw_fingerprint), 2) + ).upper() def test_missing_mtls_header_is_forbidden(dbinstance: None) -> None: + """Verify requests without mTLS headers are rejected.""" _ = dbinstance app = get_app_no_init() with TestClient(app) as client: response = client.post( "/api/v1/users/created", - json={"uuid": "uuid-a", "callsign": "VIRTA-1", "x509cert": _build_cert_pem("VIRTA-1")}, + json={ + "uuid": "uuid-a", + "callsign": "VIRTA-1", + "x509cert": build_cert_pem("VIRTA-1"), + }, ) assert response.status_code == 403 def test_forwarded_mtls_fingerprint_is_normalized(dbinstance: None) -> None: + """Verify forwarded fingerprint headers are normalized to lowercase hex.""" _ = dbinstance app = get_app_no_init() @@ -63,23 +50,36 @@ async def probe(request: Request) -> dict[str, str | None]: headers={ "X-ClientCert-DN": "CN=VIRTA-1,O=RM", "X-SSL-Client-Verify": "SUCCESS", - "X-SSL-Client-Fingerprint": _format_fingerprint_header("aabbccddeeff00112233445566778899"), + "X-SSL-Client-Fingerprint": _format_fingerprint_header( + "aabbccddeeff00112233445566778899" # pragma: allowlist secret + ), }, ) assert response.status_code == 200 - assert response.json()["fingerprint"] == "aabbccddeeff00112233445566778899" + assert ( + response.json()["fingerprint"] + == "aabbccddeeff00112233445566778899" # pragma: allowlist secret + ) @pytest.mark.asyncio async def test_unverified_proxy_header_is_forbidden(dbinstance: None) -> None: + """Verify unverified proxy verification header is rejected.""" _ = dbinstance app = get_app_no_init() with TestClient(app) as client: response = client.post( "/api/v1/users/created", - headers={"X-ClientCert-DN": "CN=rasenmaeher,O=RM", "X-SSL-Client-Verify": "FAILED"}, - json={"uuid": "uuid-a", "callsign": "VIRTA-1", "x509cert": _build_cert_pem("VIRTA-1")}, + headers={ + "X-ClientCert-DN": "CN=rasenmaeher,O=RM", + "X-SSL-Client-Verify": "FAILED", + }, + json={ + "uuid": "uuid-a", + "callsign": "VIRTA-1", + "x509cert": build_cert_pem("VIRTA-1"), + }, ) assert response.status_code == 403 diff --git a/rmcryptpad/tests/test_ui_static.py b/rmcryptpad/tests/test_ui_static.py index d045704..0278313 100644 --- a/rmcryptpad/tests/test_ui_static.py +++ b/rmcryptpad/tests/test_ui_static.py @@ -4,13 +4,18 @@ from pathlib import Path +import pytest from fastapi.testclient import TestClient from rmcryptpad.config import RMCryptPadSettings from rmcryptpad.web.application import get_app_no_init -def test_ui_bundle_is_served_from_nested_mount(monkeypatch, tmp_path: Path) -> None: +def test_ui_bundle_is_served_from_nested_mount( + monkeypatch: pytest.MonkeyPatch, tmp_path: Path +) -> None: + """Verify the UI static mount serves the built bundle.""" + async def noop_init_db() -> None: return None @@ -19,9 +24,11 @@ async def noop_init_db() -> None: (ui_dir / "remoteEntry.js").write_text("// remote bundle\n", encoding="utf-8") monkeypatch.setattr("rmcryptpad.web.application.init_db", noop_init_db) - monkeypatch.setattr("rmcryptpad.web.application.OIDCKeyManager.singleton", lambda: None) + monkeypatch.setattr( + "rmcryptpad.web.application.OIDCKeyManager.singleton", lambda: None + ) monkeypatch.setenv("RMCRYPTPAD_UI_DIR", str(ui_dir)) - RMCryptPadSettings._singleton = None + RMCryptPadSettings._singleton = None # pylint: disable=protected-access app = get_app_no_init() with TestClient(app) as client: diff --git a/rmcryptpad/tests/test_usercrud.py b/rmcryptpad/tests/test_usercrud.py index d09db77..6f6b37e 100644 --- a/rmcryptpad/tests/test_usercrud.py +++ b/rmcryptpad/tests/test_usercrud.py @@ -2,63 +2,41 @@ from __future__ import annotations -import datetime - import pytest -from cryptography import x509 -from cryptography.hazmat.primitives import hashes, serialization -from cryptography.hazmat.primitives.asymmetric import rsa -from cryptography.x509.oid import NameOID +from conftest import build_cert_pem, rm_headers from fastapi.testclient import TestClient from rmcryptpad.db.user import User from rmcryptpad.web.application import get_app_no_init -def _rm_headers() -> dict[str, str]: - return {"X-ClientCert-DN": "CN=rasenmaeher,O=RM", "X-SSL-Client-Verify": "SUCCESS"} - - -def _build_cert_pem(common_name: str) -> str: - key = rsa.generate_private_key(public_exponent=65537, key_size=2048) - subject = issuer = x509.Name([x509.NameAttribute(NameOID.COMMON_NAME, common_name)]) - cert = ( - x509.CertificateBuilder() - .subject_name(subject) - .issuer_name(issuer) - .public_key(key.public_key()) - .serial_number(x509.random_serial_number()) - .not_valid_before(datetime.datetime.now(datetime.UTC) - datetime.timedelta(days=1)) - .not_valid_after(datetime.datetime.now(datetime.UTC) + datetime.timedelta(days=30)) - .sign(key, hashes.SHA256()) - ) - return cert.public_bytes(serialization.Encoding.PEM).decode("utf-8") - - @pytest.mark.asyncio -async def test_user_created_and_updated_keep_callsign_identity(dbinstance: None) -> None: +async def test_user_created_and_updated_keep_callsign_identity( + dbinstance: None, +) -> None: + """Verify create and update keep user identity stable across cert refreshes.""" _ = dbinstance app = get_app_no_init() - first_cert = _build_cert_pem("VIRTA-1") - second_cert = _build_cert_pem("VIRTA-1-refresh") - separate_cert = _build_cert_pem("VIRTA-2") + first_cert = build_cert_pem("VIRTA-1") + second_cert = build_cert_pem("VIRTA-1-refresh") + separate_cert = build_cert_pem("VIRTA-2") with TestClient(app) as client: created = client.post( "/api/v1/users/created", - headers=_rm_headers(), + headers=rm_headers(), json={"uuid": "uuid-a", "callsign": "VIRTA-1", "x509cert": first_cert}, ) assert created.status_code == 200 refreshed = client.put( "/api/v1/users/updated", - headers=_rm_headers(), + headers=rm_headers(), json={"uuid": "uuid-a", "callsign": "VIRTA-1", "x509cert": second_cert}, ) assert refreshed.status_code == 200 separate = client.put( "/api/v1/users/updated", - headers=_rm_headers(), + headers=rm_headers(), json={"uuid": "uuid-a", "callsign": "VIRTA-2", "x509cert": separate_cert}, ) assert separate.status_code == 200 @@ -73,17 +51,24 @@ async def test_user_created_and_updated_keep_callsign_identity(dbinstance: None) @pytest.mark.asyncio -async def test_user_created_accepts_cfssl_escaped_certificate_payload(dbinstance: None) -> None: +async def test_user_created_accepts_cfssl_escaped_certificate_payload( + dbinstance: None, +) -> None: + """Verify the CRUD endpoint handles cfssl-style escaped newlines in PEM.""" _ = dbinstance app = get_app_no_init() - cert_pem = _build_cert_pem("VIRTA-CFSSL") + cert_pem = build_cert_pem("VIRTA-CFSSL") escaped_cert_pem = cert_pem.replace("\n", "\\n") with TestClient(app) as client: created = client.post( "/api/v1/users/created", - headers=_rm_headers(), - json={"uuid": "uuid-cfssl", "callsign": "VIRTA-CFSSL", "x509cert": escaped_cert_pem}, + headers=rm_headers(), + json={ + "uuid": "uuid-cfssl", + "callsign": "VIRTA-CFSSL", + "x509cert": escaped_cert_pem, + }, ) assert created.status_code == 200 @@ -94,24 +79,33 @@ async def test_user_created_accepts_cfssl_escaped_certificate_payload(dbinstance @pytest.mark.asyncio async def test_user_revoked_promoted_and_demoted_update_state(dbinstance: None) -> None: + """Verify promote, demote, and revoke correctly update user state.""" _ = dbinstance app = get_app_no_init() - cert_pem = _build_cert_pem("VIRTA-ADMIN") + cert_pem = build_cert_pem("VIRTA-ADMIN") with TestClient(app) as client: assert ( client.post( "/api/v1/users/created", - headers=_rm_headers(), - json={"uuid": "uuid-admin", "callsign": "VIRTA-ADMIN", "x509cert": cert_pem}, + headers=rm_headers(), + json={ + "uuid": "uuid-admin", + "callsign": "VIRTA-ADMIN", + "x509cert": cert_pem, + }, ).status_code == 200 ) assert ( client.post( "/api/v1/users/promoted", - headers=_rm_headers(), - json={"uuid": "uuid-admin", "callsign": "VIRTA-ADMIN", "x509cert": cert_pem}, + headers=rm_headers(), + json={ + "uuid": "uuid-admin", + "callsign": "VIRTA-ADMIN", + "x509cert": cert_pem, + }, ).status_code == 200 ) @@ -123,16 +117,24 @@ async def test_user_revoked_promoted_and_demoted_update_state(dbinstance: None) assert ( client.post( "/api/v1/users/demoted", - headers=_rm_headers(), - json={"uuid": "uuid-admin", "callsign": "VIRTA-ADMIN", "x509cert": cert_pem}, + headers=rm_headers(), + json={ + "uuid": "uuid-admin", + "callsign": "VIRTA-ADMIN", + "x509cert": cert_pem, + }, ).status_code == 200 ) assert ( client.post( "/api/v1/users/revoked", - headers=_rm_headers(), - json={"uuid": "uuid-admin", "callsign": "VIRTA-ADMIN", "x509cert": cert_pem}, + headers=rm_headers(), + json={ + "uuid": "uuid-admin", + "callsign": "VIRTA-ADMIN", + "x509cert": cert_pem, + }, ).status_code == 200 ) @@ -144,17 +146,21 @@ async def test_user_revoked_promoted_and_demoted_update_state(dbinstance: None) @pytest.mark.asyncio async def test_wrong_rm_cn_is_forbidden_on_rm_only_routes(dbinstance: None) -> None: + """Verify non-RM callers are rejected on RM-only endpoints.""" _ = dbinstance app = get_app_no_init() with TestClient(app) as client: response = client.post( "/api/v1/users/created", - headers={"X-ClientCert-DN": "CN=not-rasenmaeher,O=RM", "X-SSL-Client-Verify": "SUCCESS"}, + headers={ + "X-ClientCert-DN": "CN=not-rasenmaeher,O=RM", + "X-SSL-Client-Verify": "SUCCESS", + }, json={ "uuid": "uuid-bad", "callsign": "VIRTA-BAD", - "x509cert": _build_cert_pem("VIRTA-BAD"), + "x509cert": build_cert_pem("VIRTA-BAD"), }, ) assert response.status_code == 403 diff --git a/rmcryptpad/ui/.editorconfig b/rmcryptpad/ui/.editorconfig new file mode 100644 index 0000000..79da928 --- /dev/null +++ b/rmcryptpad/ui/.editorconfig @@ -0,0 +1,9 @@ +root = true + +[*.{ts,tsx}] +indent_style = space +indent_size = 2 + +[*.py] +indent_style = space +indent_size = 4 diff --git a/rmcryptpad/ui/.eslintrc.cjs b/rmcryptpad/ui/.eslintrc.cjs new file mode 100644 index 0000000..94b9b36 --- /dev/null +++ b/rmcryptpad/ui/.eslintrc.cjs @@ -0,0 +1,37 @@ +/* eslint-env node */ + +module.exports = { + root: true, + env: { browser: true, es2020: true }, + extends: [ + "eslint:recommended", + "plugin:@typescript-eslint/recommended", + "plugin:@typescript-eslint/recommended-requiring-type-checking", + "plugin:react-hooks/recommended", + ], + parser: "@typescript-eslint/parser", + parserOptions: { + ecmaVersion: "latest", + sourceType: "module", + project: true, + tsconfigRootDir: __dirname, + }, + plugins: ["react-refresh"], + rules: { + "react-refresh/only-export-components": [ + "warn", + { allowConstantExport: true }, + ], + "@typescript-eslint/no-non-null-assertion": "off", + }, + ignorePatterns: [ + "dist/**", + "scripts/**", + ".eslintrc.cjs", + "vite.config.ts", + "tailwind.config.js", + "src/mocks/**/*", + "mocksBrowser.ts", + "public/mockServiceWorker.js", + ], +}; diff --git a/rmcryptpad/ui/.prettierignore b/rmcryptpad/ui/.prettierignore new file mode 100644 index 0000000..af9ca86 --- /dev/null +++ b/rmcryptpad/ui/.prettierignore @@ -0,0 +1,3 @@ +dist +node_modules +.__mf__temp diff --git a/rmcryptpad/ui/components.json b/rmcryptpad/ui/components.json new file mode 100644 index 0000000..9a9bc57 --- /dev/null +++ b/rmcryptpad/ui/components.json @@ -0,0 +1,22 @@ +{ + "$schema": "https://ui.shadcn.com/schema.json", + "aliases": { + "components": "@/components", + "hooks": "@/hooks", + "lib": "@/lib", + "ui": "@/components/ui", + "utils": "@/lib/utils" + }, + "iconLibrary": "lucide", + "registries": {}, + "rsc": false, + "style": "new-york", + "tailwind": { + "baseColor": "neutral", + "config": "", + "css": "src/index.css", + "cssVariables": true, + "prefix": "" + }, + "tsx": true +} diff --git a/rmcryptpad/ui/package.json b/rmcryptpad/ui/package.json index 618760f..48f28d2 100644 --- a/rmcryptpad/ui/package.json +++ b/rmcryptpad/ui/package.json @@ -1,30 +1,57 @@ { - "name": "rmcryptpad-ui", - "private": true, - "type": "module", - "scripts": { - "build": "tsc --noEmit && vite build", - "dev": "vite", - "preview": "vite preview --port 4174", - "test": "vitest --run" - }, "dependencies": { "@module-federation/enhanced": "^0.21.2", + "@radix-ui/react-dialog": "^1.1.15", + "@radix-ui/react-slot": "^1.2.4", + "@tailwindcss/vite": "^4.1.16", + "@tanstack/react-router": "^1.133.22", + "class-variance-authority": "^0.7.1", + "clsx": "^2.1.1", + "i18next": "^25.6.2", + "lucide-react": "^0.552.0", + "next-themes": "^0.4.6", "react": "^18.3.1", - "react-dom": "^18.3.1" + "react-dom": "^18.3.1", + "react-i18next": "^16.3.3", + "sonner": "^2.0.7", + "tailwind-merge": "^3.3.1", + "tailwindcss": "^4.1.16", + "vaul": "^1.1.2" }, "devDependencies": { "@module-federation/enhanced": "^0.21.2", "@module-federation/vite": "1.7.1", + "@tailwindcss/typography": "^0.5.19", "@testing-library/jest-dom": "^6.8.0", "@testing-library/react": "^16.0.1", + "@types/node": "^22.15.30", "@types/react": "18.2.79", "@types/react-dom": "18.2.25", - "@types/node": "^22.15.30", + "@typescript-eslint/eslint-plugin": "^5.61.0", + "@typescript-eslint/parser": "^5.61.0", "@vitejs/plugin-react": "4.2.1", + "eslint": "^8.44.0", + "eslint-plugin-react-hooks": "^4.6.0", + "eslint-plugin-react-refresh": "^0.4.1", + "sharp": "^0.34.2", "jsdom": "^25.0.1", + "prettier": "3.6.2", + "tw-animate-css": "^1.4.0", "typescript": "5.4.5", + "typescript-eslint": "^8.48.1", "vite": "5.2.10", "vitest": "^2.1.9" - } + }, + "name": "cryptpad-module-federation", + "private": true, + "scripts": { + "build": "tsc && vite build", + "dev": "VITE_USE_GLOBAL_CSS=true vite --port 4174", + "lint": "eslint . --ext ts,tsx --report-unused-disable-directives", + "preview": "npm run build && vite preview --port 4174", + "test": "vitest --run", + "img:min": "node scripts/minify-images.mjs" + }, + "type": "module", + "version": "0.0.0" } diff --git a/rmcryptpad/ui/pnpm-lock.yaml b/rmcryptpad/ui/pnpm-lock.yaml index 35b489c..98975db 100644 --- a/rmcryptpad/ui/pnpm-lock.yaml +++ b/rmcryptpad/ui/pnpm-lock.yaml @@ -1,372 +1,895 @@ -lockfileVersion: '9.0' +lockfileVersion: "9.0" settings: autoInstallPeers: true excludeLinksFromLockfile: false importers: - .: dependencies: - '@module-federation/enhanced': + "@module-federation/enhanced": specifier: ^0.21.2 version: 0.21.6(@rspack/core@1.7.9)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.4.5) + "@radix-ui/react-dialog": + specifier: ^1.1.15 + version: 1.1.15(@types/react-dom@18.2.25)(@types/react@18.2.79)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + "@radix-ui/react-slot": + specifier: ^1.2.4 + version: 1.2.4(@types/react@18.2.79)(react@18.3.1) + "@tailwindcss/vite": + specifier: ^4.1.16 + version: 4.2.2(vite@5.2.10(@types/node@22.19.15)(lightningcss@1.32.0)) + "@tanstack/react-router": + specifier: ^1.133.22 + version: 1.168.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + class-variance-authority: + specifier: ^0.7.1 + version: 0.7.1 + clsx: + specifier: ^2.1.1 + version: 2.1.1 + i18next: + specifier: ^25.6.2 + version: 25.10.2(typescript@5.4.5) + lucide-react: + specifier: ^0.552.0 + version: 0.552.0(react@18.3.1) + next-themes: + specifier: ^0.4.6 + version: 0.4.6(react-dom@18.3.1(react@18.3.1))(react@18.3.1) react: specifier: ^18.3.1 version: 18.3.1 react-dom: specifier: ^18.3.1 version: 18.3.1(react@18.3.1) + react-i18next: + specifier: ^16.3.3 + version: 16.6.0(i18next@25.10.2(typescript@5.4.5))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.4.5) + sonner: + specifier: ^2.0.7 + version: 2.0.7(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + tailwind-merge: + specifier: ^3.3.1 + version: 3.5.0 + tailwindcss: + specifier: ^4.1.16 + version: 4.2.2 + vaul: + specifier: ^1.1.2 + version: 1.1.2(@types/react-dom@18.2.25)(@types/react@18.2.79)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) devDependencies: - '@module-federation/vite': + "@module-federation/vite": specifier: 1.7.1 version: 1.7.1(rollup@4.59.0) - '@testing-library/jest-dom': + "@tailwindcss/typography": + specifier: ^0.5.19 + version: 0.5.19(tailwindcss@4.2.2) + "@testing-library/jest-dom": specifier: ^6.8.0 version: 6.9.1 - '@testing-library/react': + "@testing-library/react": specifier: ^16.0.1 version: 16.3.2(@testing-library/dom@10.4.1)(@types/react-dom@18.2.25)(@types/react@18.2.79)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@types/node': + "@types/node": specifier: ^22.15.30 version: 22.19.15 - '@types/react': + "@types/react": specifier: 18.2.79 version: 18.2.79 - '@types/react-dom': + "@types/react-dom": specifier: 18.2.25 version: 18.2.25 - '@vitejs/plugin-react': + "@typescript-eslint/eslint-plugin": + specifier: ^5.61.0 + version: 5.62.0(@typescript-eslint/parser@5.62.0(eslint@8.57.1)(typescript@5.4.5))(eslint@8.57.1)(typescript@5.4.5) + "@typescript-eslint/parser": + specifier: ^5.61.0 + version: 5.62.0(eslint@8.57.1)(typescript@5.4.5) + "@vitejs/plugin-react": specifier: 4.2.1 - version: 4.2.1(vite@5.2.10(@types/node@22.19.15)) + version: 4.2.1(vite@5.2.10(@types/node@22.19.15)(lightningcss@1.32.0)) + eslint: + specifier: ^8.44.0 + version: 8.57.1 + eslint-plugin-react-hooks: + specifier: ^4.6.0 + version: 4.6.2(eslint@8.57.1) + eslint-plugin-react-refresh: + specifier: ^0.4.1 + version: 0.4.26(eslint@8.57.1) jsdom: specifier: ^25.0.1 version: 25.0.1 + prettier: + specifier: 3.6.2 + version: 3.6.2 + sharp: + specifier: ^0.34.2 + version: 0.34.5 + tw-animate-css: + specifier: ^1.4.0 + version: 1.4.0 typescript: specifier: 5.4.5 version: 5.4.5 + typescript-eslint: + specifier: ^8.48.1 + version: 8.57.2(eslint@8.57.1)(typescript@5.4.5) vite: specifier: 5.2.10 - version: 5.2.10(@types/node@22.19.15) + version: 5.2.10(@types/node@22.19.15)(lightningcss@1.32.0) vitest: specifier: ^2.1.9 - version: 2.1.9(@types/node@22.19.15)(@vitest/ui@2.1.9)(jsdom@25.0.1) + version: 2.1.9(@types/node@22.19.15)(@vitest/ui@2.1.9)(jsdom@25.0.1)(lightningcss@1.32.0) packages: - - '@adobe/css-tools@4.4.4': - resolution: {integrity: sha512-Elp+iwUx5rN5+Y8xLt5/GRoG20WGoDCQ/1Fb+1LiGtvwbDavuSk0jhD/eZdckHAuzcDzccnkv+rEjyWfRx18gg==} - - '@asamuzakjp/css-color@3.2.0': - resolution: {integrity: sha512-K1A6z8tS3XsmCMM86xoWdn7Fkdn9m6RSVtocUrJYIwZnFVkng/PvkEoWtOWmP+Scc6saYWHWZYbndEEXxl24jw==} - - '@babel/code-frame@7.29.0': - resolution: {integrity: sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==} - engines: {node: '>=6.9.0'} - - '@babel/compat-data@7.29.0': - resolution: {integrity: sha512-T1NCJqT/j9+cn8fvkt7jtwbLBfLC/1y1c7NtCeXFRgzGTsafi68MRv8yzkYSapBnFA6L3U2VSc02ciDzoAJhJg==} - engines: {node: '>=6.9.0'} - - '@babel/core@7.29.0': - resolution: {integrity: sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==} - engines: {node: '>=6.9.0'} - - '@babel/generator@7.29.1': - resolution: {integrity: sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw==} - engines: {node: '>=6.9.0'} - - '@babel/helper-compilation-targets@7.28.6': - resolution: {integrity: sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA==} - engines: {node: '>=6.9.0'} - - '@babel/helper-globals@7.28.0': - resolution: {integrity: sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==} - engines: {node: '>=6.9.0'} - - '@babel/helper-module-imports@7.28.6': - resolution: {integrity: sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw==} - engines: {node: '>=6.9.0'} - - '@babel/helper-module-transforms@7.28.6': - resolution: {integrity: sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA==} - engines: {node: '>=6.9.0'} + "@adobe/css-tools@4.4.4": + resolution: + { + integrity: sha512-Elp+iwUx5rN5+Y8xLt5/GRoG20WGoDCQ/1Fb+1LiGtvwbDavuSk0jhD/eZdckHAuzcDzccnkv+rEjyWfRx18gg==, + } + + "@asamuzakjp/css-color@3.2.0": + resolution: + { + integrity: sha512-K1A6z8tS3XsmCMM86xoWdn7Fkdn9m6RSVtocUrJYIwZnFVkng/PvkEoWtOWmP+Scc6saYWHWZYbndEEXxl24jw==, + } + + "@babel/code-frame@7.29.0": + resolution: + { + integrity: sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==, + } + engines: { node: ">=6.9.0" } + + "@babel/compat-data@7.29.0": + resolution: + { + integrity: sha512-T1NCJqT/j9+cn8fvkt7jtwbLBfLC/1y1c7NtCeXFRgzGTsafi68MRv8yzkYSapBnFA6L3U2VSc02ciDzoAJhJg==, + } + engines: { node: ">=6.9.0" } + + "@babel/core@7.29.0": + resolution: + { + integrity: sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==, + } + engines: { node: ">=6.9.0" } + + "@babel/generator@7.29.1": + resolution: + { + integrity: sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw==, + } + engines: { node: ">=6.9.0" } + + "@babel/helper-compilation-targets@7.28.6": + resolution: + { + integrity: sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA==, + } + engines: { node: ">=6.9.0" } + + "@babel/helper-globals@7.28.0": + resolution: + { + integrity: sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==, + } + engines: { node: ">=6.9.0" } + + "@babel/helper-module-imports@7.28.6": + resolution: + { + integrity: sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw==, + } + engines: { node: ">=6.9.0" } + + "@babel/helper-module-transforms@7.28.6": + resolution: + { + integrity: sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA==, + } + engines: { node: ">=6.9.0" } peerDependencies: - '@babel/core': ^7.0.0 - - '@babel/helper-plugin-utils@7.28.6': - resolution: {integrity: sha512-S9gzZ/bz83GRysI7gAD4wPT/AI3uCnY+9xn+Mx/KPs2JwHJIz1W8PZkg2cqyt3RNOBM8ejcXhV6y8Og7ly/Dug==} - engines: {node: '>=6.9.0'} - - '@babel/helper-string-parser@7.27.1': - resolution: {integrity: sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==} - engines: {node: '>=6.9.0'} - - '@babel/helper-validator-identifier@7.28.5': - resolution: {integrity: sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==} - engines: {node: '>=6.9.0'} - - '@babel/helper-validator-option@7.27.1': - resolution: {integrity: sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==} - engines: {node: '>=6.9.0'} - - '@babel/helpers@7.29.2': - resolution: {integrity: sha512-HoGuUs4sCZNezVEKdVcwqmZN8GoHirLUcLaYVNBK2J0DadGtdcqgr3BCbvH8+XUo4NGjNl3VOtSjEKNzqfFgKw==} - engines: {node: '>=6.9.0'} - - '@babel/parser@7.29.2': - resolution: {integrity: sha512-4GgRzy/+fsBa72/RZVJmGKPmZu9Byn8o4MoLpmNe1m8ZfYnz5emHLQz3U4gLud6Zwl0RZIcgiLD7Uq7ySFuDLA==} - engines: {node: '>=6.0.0'} + "@babel/core": ^7.0.0 + + "@babel/helper-plugin-utils@7.28.6": + resolution: + { + integrity: sha512-S9gzZ/bz83GRysI7gAD4wPT/AI3uCnY+9xn+Mx/KPs2JwHJIz1W8PZkg2cqyt3RNOBM8ejcXhV6y8Og7ly/Dug==, + } + engines: { node: ">=6.9.0" } + + "@babel/helper-string-parser@7.27.1": + resolution: + { + integrity: sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==, + } + engines: { node: ">=6.9.0" } + + "@babel/helper-validator-identifier@7.28.5": + resolution: + { + integrity: sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==, + } + engines: { node: ">=6.9.0" } + + "@babel/helper-validator-option@7.27.1": + resolution: + { + integrity: sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==, + } + engines: { node: ">=6.9.0" } + + "@babel/helpers@7.29.2": + resolution: + { + integrity: sha512-HoGuUs4sCZNezVEKdVcwqmZN8GoHirLUcLaYVNBK2J0DadGtdcqgr3BCbvH8+XUo4NGjNl3VOtSjEKNzqfFgKw==, + } + engines: { node: ">=6.9.0" } + + "@babel/parser@7.29.2": + resolution: + { + integrity: sha512-4GgRzy/+fsBa72/RZVJmGKPmZu9Byn8o4MoLpmNe1m8ZfYnz5emHLQz3U4gLud6Zwl0RZIcgiLD7Uq7ySFuDLA==, + } + engines: { node: ">=6.0.0" } hasBin: true - '@babel/plugin-transform-react-jsx-self@7.27.1': - resolution: {integrity: sha512-6UzkCs+ejGdZ5mFFC/OCUrv028ab2fp1znZmCZjAOBKiBK2jXD1O+BPSfX8X2qjJ75fZBMSnQn3Rq2mrBJK2mw==} - engines: {node: '>=6.9.0'} + "@babel/plugin-transform-react-jsx-self@7.27.1": + resolution: + { + integrity: sha512-6UzkCs+ejGdZ5mFFC/OCUrv028ab2fp1znZmCZjAOBKiBK2jXD1O+BPSfX8X2qjJ75fZBMSnQn3Rq2mrBJK2mw==, + } + engines: { node: ">=6.9.0" } peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-transform-react-jsx-source@7.27.1': - resolution: {integrity: sha512-zbwoTsBruTeKB9hSq73ha66iFeJHuaFkUbwvqElnygoNbj/jHRsSeokowZFN3CZ64IvEqcmmkVe89OPXc7ldAw==} - engines: {node: '>=6.9.0'} + "@babel/core": ^7.0.0-0 + + "@babel/plugin-transform-react-jsx-source@7.27.1": + resolution: + { + integrity: sha512-zbwoTsBruTeKB9hSq73ha66iFeJHuaFkUbwvqElnygoNbj/jHRsSeokowZFN3CZ64IvEqcmmkVe89OPXc7ldAw==, + } + engines: { node: ">=6.9.0" } peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/runtime@7.29.2': - resolution: {integrity: sha512-JiDShH45zKHWyGe4ZNVRrCjBz8Nh9TMmZG1kh4QTK8hCBTWBi8Da+i7s1fJw7/lYpM4ccepSNfqzZ/QvABBi5g==} - engines: {node: '>=6.9.0'} - - '@babel/template@7.28.6': - resolution: {integrity: sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==} - engines: {node: '>=6.9.0'} - - '@babel/traverse@7.29.0': - resolution: {integrity: sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA==} - engines: {node: '>=6.9.0'} - - '@babel/types@7.29.0': - resolution: {integrity: sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==} - engines: {node: '>=6.9.0'} - - '@csstools/color-helpers@5.1.0': - resolution: {integrity: sha512-S11EXWJyy0Mz5SYvRmY8nJYTFFd1LCNV+7cXyAgQtOOuzb4EsgfqDufL+9esx72/eLhsRdGZwaldu/h+E4t4BA==} - engines: {node: '>=18'} - - '@csstools/css-calc@2.1.4': - resolution: {integrity: sha512-3N8oaj+0juUw/1H3YwmDDJXCgTB1gKU6Hc/bB502u9zR0q2vd786XJH9QfrKIEgFlZmhZiq6epXl4rHqhzsIgQ==} - engines: {node: '>=18'} + "@babel/core": ^7.0.0-0 + + "@babel/runtime@7.29.2": + resolution: + { + integrity: sha512-JiDShH45zKHWyGe4ZNVRrCjBz8Nh9TMmZG1kh4QTK8hCBTWBi8Da+i7s1fJw7/lYpM4ccepSNfqzZ/QvABBi5g==, + } + engines: { node: ">=6.9.0" } + + "@babel/template@7.28.6": + resolution: + { + integrity: sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==, + } + engines: { node: ">=6.9.0" } + + "@babel/traverse@7.29.0": + resolution: + { + integrity: sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA==, + } + engines: { node: ">=6.9.0" } + + "@babel/types@7.29.0": + resolution: + { + integrity: sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==, + } + engines: { node: ">=6.9.0" } + + "@csstools/color-helpers@5.1.0": + resolution: + { + integrity: sha512-S11EXWJyy0Mz5SYvRmY8nJYTFFd1LCNV+7cXyAgQtOOuzb4EsgfqDufL+9esx72/eLhsRdGZwaldu/h+E4t4BA==, + } + engines: { node: ">=18" } + + "@csstools/css-calc@2.1.4": + resolution: + { + integrity: sha512-3N8oaj+0juUw/1H3YwmDDJXCgTB1gKU6Hc/bB502u9zR0q2vd786XJH9QfrKIEgFlZmhZiq6epXl4rHqhzsIgQ==, + } + engines: { node: ">=18" } peerDependencies: - '@csstools/css-parser-algorithms': ^3.0.5 - '@csstools/css-tokenizer': ^3.0.4 - - '@csstools/css-color-parser@3.1.0': - resolution: {integrity: sha512-nbtKwh3a6xNVIp/VRuXV64yTKnb1IjTAEEh3irzS+HkKjAOYLTGNb9pmVNntZ8iVBHcWDA2Dof0QtPgFI1BaTA==} - engines: {node: '>=18'} + "@csstools/css-parser-algorithms": ^3.0.5 + "@csstools/css-tokenizer": ^3.0.4 + + "@csstools/css-color-parser@3.1.0": + resolution: + { + integrity: sha512-nbtKwh3a6xNVIp/VRuXV64yTKnb1IjTAEEh3irzS+HkKjAOYLTGNb9pmVNntZ8iVBHcWDA2Dof0QtPgFI1BaTA==, + } + engines: { node: ">=18" } peerDependencies: - '@csstools/css-parser-algorithms': ^3.0.5 - '@csstools/css-tokenizer': ^3.0.4 - - '@csstools/css-parser-algorithms@3.0.5': - resolution: {integrity: sha512-DaDeUkXZKjdGhgYaHNJTV9pV7Y9B3b644jCLs9Upc3VeNGg6LWARAT6O+Q+/COo+2gg/bM5rhpMAtf70WqfBdQ==} - engines: {node: '>=18'} + "@csstools/css-parser-algorithms": ^3.0.5 + "@csstools/css-tokenizer": ^3.0.4 + + "@csstools/css-parser-algorithms@3.0.5": + resolution: + { + integrity: sha512-DaDeUkXZKjdGhgYaHNJTV9pV7Y9B3b644jCLs9Upc3VeNGg6LWARAT6O+Q+/COo+2gg/bM5rhpMAtf70WqfBdQ==, + } + engines: { node: ">=18" } peerDependencies: - '@csstools/css-tokenizer': ^3.0.4 - - '@csstools/css-tokenizer@3.0.4': - resolution: {integrity: sha512-Vd/9EVDiu6PPJt9yAh6roZP6El1xHrdvIVGjyBsHR0RYwNHgL7FJPyIIW4fANJNG6FtyZfvlRPpFI4ZM/lubvw==} - engines: {node: '>=18'} - - '@emnapi/core@1.9.0': - resolution: {integrity: sha512-0DQ98G9ZQZOxfUcQn1waV2yS8aWdZ6kJMbYCJB3oUBecjWYO1fqJ+a1DRfPF3O5JEkwqwP1A9QEN/9mYm2Yd0w==} - - '@emnapi/runtime@1.9.0': - resolution: {integrity: sha512-QN75eB0IH2ywSpRpNddCRfQIhmJYBCJ1x5Lb3IscKAL8bMnVAKnRg8dCoXbHzVLLH7P38N2Z3mtulB7W0J0FKw==} - - '@emnapi/wasi-threads@1.2.0': - resolution: {integrity: sha512-N10dEJNSsUx41Z6pZsXU8FjPjpBEplgH24sfkmITrBED1/U2Esum9F3lfLrMjKHHjmi557zQn7kR9R+XWXu5Rg==} - - '@esbuild/aix-ppc64@0.20.2': - resolution: {integrity: sha512-D+EBOJHXdNZcLJRBkhENNG8Wji2kgc9AZ9KiPr1JuZjsNtyHzrsfLRrY0tk2H2aoFu6RANO1y1iPPUCDYWkb5g==} - engines: {node: '>=12'} + "@csstools/css-tokenizer": ^3.0.4 + + "@csstools/css-tokenizer@3.0.4": + resolution: + { + integrity: sha512-Vd/9EVDiu6PPJt9yAh6roZP6El1xHrdvIVGjyBsHR0RYwNHgL7FJPyIIW4fANJNG6FtyZfvlRPpFI4ZM/lubvw==, + } + engines: { node: ">=18" } + + "@emnapi/core@1.9.0": + resolution: + { + integrity: sha512-0DQ98G9ZQZOxfUcQn1waV2yS8aWdZ6kJMbYCJB3oUBecjWYO1fqJ+a1DRfPF3O5JEkwqwP1A9QEN/9mYm2Yd0w==, + } + + "@emnapi/runtime@1.9.0": + resolution: + { + integrity: sha512-QN75eB0IH2ywSpRpNddCRfQIhmJYBCJ1x5Lb3IscKAL8bMnVAKnRg8dCoXbHzVLLH7P38N2Z3mtulB7W0J0FKw==, + } + + "@emnapi/wasi-threads@1.2.0": + resolution: + { + integrity: sha512-N10dEJNSsUx41Z6pZsXU8FjPjpBEplgH24sfkmITrBED1/U2Esum9F3lfLrMjKHHjmi557zQn7kR9R+XWXu5Rg==, + } + + "@esbuild/aix-ppc64@0.20.2": + resolution: + { + integrity: sha512-D+EBOJHXdNZcLJRBkhENNG8Wji2kgc9AZ9KiPr1JuZjsNtyHzrsfLRrY0tk2H2aoFu6RANO1y1iPPUCDYWkb5g==, + } + engines: { node: ">=12" } cpu: [ppc64] os: [aix] - '@esbuild/android-arm64@0.20.2': - resolution: {integrity: sha512-mRzjLacRtl/tWU0SvD8lUEwb61yP9cqQo6noDZP/O8VkwafSYwZ4yWy24kan8jE/IMERpYncRt2dw438LP3Xmg==} - engines: {node: '>=12'} + "@esbuild/android-arm64@0.20.2": + resolution: + { + integrity: sha512-mRzjLacRtl/tWU0SvD8lUEwb61yP9cqQo6noDZP/O8VkwafSYwZ4yWy24kan8jE/IMERpYncRt2dw438LP3Xmg==, + } + engines: { node: ">=12" } cpu: [arm64] os: [android] - '@esbuild/android-arm@0.20.2': - resolution: {integrity: sha512-t98Ra6pw2VaDhqNWO2Oph2LXbz/EJcnLmKLGBJwEwXX/JAN83Fym1rU8l0JUWK6HkIbWONCSSatf4sf2NBRx/w==} - engines: {node: '>=12'} + "@esbuild/android-arm@0.20.2": + resolution: + { + integrity: sha512-t98Ra6pw2VaDhqNWO2Oph2LXbz/EJcnLmKLGBJwEwXX/JAN83Fym1rU8l0JUWK6HkIbWONCSSatf4sf2NBRx/w==, + } + engines: { node: ">=12" } cpu: [arm] os: [android] - '@esbuild/android-x64@0.20.2': - resolution: {integrity: sha512-btzExgV+/lMGDDa194CcUQm53ncxzeBrWJcncOBxuC6ndBkKxnHdFJn86mCIgTELsooUmwUm9FkhSp5HYu00Rg==} - engines: {node: '>=12'} + "@esbuild/android-x64@0.20.2": + resolution: + { + integrity: sha512-btzExgV+/lMGDDa194CcUQm53ncxzeBrWJcncOBxuC6ndBkKxnHdFJn86mCIgTELsooUmwUm9FkhSp5HYu00Rg==, + } + engines: { node: ">=12" } cpu: [x64] os: [android] - '@esbuild/darwin-arm64@0.20.2': - resolution: {integrity: sha512-4J6IRT+10J3aJH3l1yzEg9y3wkTDgDk7TSDFX+wKFiWjqWp/iCfLIYzGyasx9l0SAFPT1HwSCR+0w/h1ES/MjA==} - engines: {node: '>=12'} + "@esbuild/darwin-arm64@0.20.2": + resolution: + { + integrity: sha512-4J6IRT+10J3aJH3l1yzEg9y3wkTDgDk7TSDFX+wKFiWjqWp/iCfLIYzGyasx9l0SAFPT1HwSCR+0w/h1ES/MjA==, + } + engines: { node: ">=12" } cpu: [arm64] os: [darwin] - '@esbuild/darwin-x64@0.20.2': - resolution: {integrity: sha512-tBcXp9KNphnNH0dfhv8KYkZhjc+H3XBkF5DKtswJblV7KlT9EI2+jeA8DgBjp908WEuYll6pF+UStUCfEpdysA==} - engines: {node: '>=12'} + "@esbuild/darwin-x64@0.20.2": + resolution: + { + integrity: sha512-tBcXp9KNphnNH0dfhv8KYkZhjc+H3XBkF5DKtswJblV7KlT9EI2+jeA8DgBjp908WEuYll6pF+UStUCfEpdysA==, + } + engines: { node: ">=12" } cpu: [x64] os: [darwin] - '@esbuild/freebsd-arm64@0.20.2': - resolution: {integrity: sha512-d3qI41G4SuLiCGCFGUrKsSeTXyWG6yem1KcGZVS+3FYlYhtNoNgYrWcvkOoaqMhwXSMrZRl69ArHsGJ9mYdbbw==} - engines: {node: '>=12'} + "@esbuild/freebsd-arm64@0.20.2": + resolution: + { + integrity: sha512-d3qI41G4SuLiCGCFGUrKsSeTXyWG6yem1KcGZVS+3FYlYhtNoNgYrWcvkOoaqMhwXSMrZRl69ArHsGJ9mYdbbw==, + } + engines: { node: ">=12" } cpu: [arm64] os: [freebsd] - '@esbuild/freebsd-x64@0.20.2': - resolution: {integrity: sha512-d+DipyvHRuqEeM5zDivKV1KuXn9WeRX6vqSqIDgwIfPQtwMP4jaDsQsDncjTDDsExT4lR/91OLjRo8bmC1e+Cw==} - engines: {node: '>=12'} + "@esbuild/freebsd-x64@0.20.2": + resolution: + { + integrity: sha512-d+DipyvHRuqEeM5zDivKV1KuXn9WeRX6vqSqIDgwIfPQtwMP4jaDsQsDncjTDDsExT4lR/91OLjRo8bmC1e+Cw==, + } + engines: { node: ">=12" } cpu: [x64] os: [freebsd] - '@esbuild/linux-arm64@0.20.2': - resolution: {integrity: sha512-9pb6rBjGvTFNira2FLIWqDk/uaf42sSyLE8j1rnUpuzsODBq7FvpwHYZxQ/It/8b+QOS1RYfqgGFNLRI+qlq2A==} - engines: {node: '>=12'} + "@esbuild/linux-arm64@0.20.2": + resolution: + { + integrity: sha512-9pb6rBjGvTFNira2FLIWqDk/uaf42sSyLE8j1rnUpuzsODBq7FvpwHYZxQ/It/8b+QOS1RYfqgGFNLRI+qlq2A==, + } + engines: { node: ">=12" } cpu: [arm64] os: [linux] - '@esbuild/linux-arm@0.20.2': - resolution: {integrity: sha512-VhLPeR8HTMPccbuWWcEUD1Az68TqaTYyj6nfE4QByZIQEQVWBB8vup8PpR7y1QHL3CpcF6xd5WVBU/+SBEvGTg==} - engines: {node: '>=12'} + "@esbuild/linux-arm@0.20.2": + resolution: + { + integrity: sha512-VhLPeR8HTMPccbuWWcEUD1Az68TqaTYyj6nfE4QByZIQEQVWBB8vup8PpR7y1QHL3CpcF6xd5WVBU/+SBEvGTg==, + } + engines: { node: ">=12" } cpu: [arm] os: [linux] - '@esbuild/linux-ia32@0.20.2': - resolution: {integrity: sha512-o10utieEkNPFDZFQm9CoP7Tvb33UutoJqg3qKf1PWVeeJhJw0Q347PxMvBgVVFgouYLGIhFYG0UGdBumROyiig==} - engines: {node: '>=12'} + "@esbuild/linux-ia32@0.20.2": + resolution: + { + integrity: sha512-o10utieEkNPFDZFQm9CoP7Tvb33UutoJqg3qKf1PWVeeJhJw0Q347PxMvBgVVFgouYLGIhFYG0UGdBumROyiig==, + } + engines: { node: ">=12" } cpu: [ia32] os: [linux] - '@esbuild/linux-loong64@0.20.2': - resolution: {integrity: sha512-PR7sp6R/UC4CFVomVINKJ80pMFlfDfMQMYynX7t1tNTeivQ6XdX5r2XovMmha/VjR1YN/HgHWsVcTRIMkymrgQ==} - engines: {node: '>=12'} + "@esbuild/linux-loong64@0.20.2": + resolution: + { + integrity: sha512-PR7sp6R/UC4CFVomVINKJ80pMFlfDfMQMYynX7t1tNTeivQ6XdX5r2XovMmha/VjR1YN/HgHWsVcTRIMkymrgQ==, + } + engines: { node: ">=12" } cpu: [loong64] os: [linux] - '@esbuild/linux-mips64el@0.20.2': - resolution: {integrity: sha512-4BlTqeutE/KnOiTG5Y6Sb/Hw6hsBOZapOVF6njAESHInhlQAghVVZL1ZpIctBOoTFbQyGW+LsVYZ8lSSB3wkjA==} - engines: {node: '>=12'} + "@esbuild/linux-mips64el@0.20.2": + resolution: + { + integrity: sha512-4BlTqeutE/KnOiTG5Y6Sb/Hw6hsBOZapOVF6njAESHInhlQAghVVZL1ZpIctBOoTFbQyGW+LsVYZ8lSSB3wkjA==, + } + engines: { node: ">=12" } cpu: [mips64el] os: [linux] - '@esbuild/linux-ppc64@0.20.2': - resolution: {integrity: sha512-rD3KsaDprDcfajSKdn25ooz5J5/fWBylaaXkuotBDGnMnDP1Uv5DLAN/45qfnf3JDYyJv/ytGHQaziHUdyzaAg==} - engines: {node: '>=12'} + "@esbuild/linux-ppc64@0.20.2": + resolution: + { + integrity: sha512-rD3KsaDprDcfajSKdn25ooz5J5/fWBylaaXkuotBDGnMnDP1Uv5DLAN/45qfnf3JDYyJv/ytGHQaziHUdyzaAg==, + } + engines: { node: ">=12" } cpu: [ppc64] os: [linux] - '@esbuild/linux-riscv64@0.20.2': - resolution: {integrity: sha512-snwmBKacKmwTMmhLlz/3aH1Q9T8v45bKYGE3j26TsaOVtjIag4wLfWSiZykXzXuE1kbCE+zJRmwp+ZbIHinnVg==} - engines: {node: '>=12'} + "@esbuild/linux-riscv64@0.20.2": + resolution: + { + integrity: sha512-snwmBKacKmwTMmhLlz/3aH1Q9T8v45bKYGE3j26TsaOVtjIag4wLfWSiZykXzXuE1kbCE+zJRmwp+ZbIHinnVg==, + } + engines: { node: ">=12" } cpu: [riscv64] os: [linux] - '@esbuild/linux-s390x@0.20.2': - resolution: {integrity: sha512-wcWISOobRWNm3cezm5HOZcYz1sKoHLd8VL1dl309DiixxVFoFe/o8HnwuIwn6sXre88Nwj+VwZUvJf4AFxkyrQ==} - engines: {node: '>=12'} + "@esbuild/linux-s390x@0.20.2": + resolution: + { + integrity: sha512-wcWISOobRWNm3cezm5HOZcYz1sKoHLd8VL1dl309DiixxVFoFe/o8HnwuIwn6sXre88Nwj+VwZUvJf4AFxkyrQ==, + } + engines: { node: ">=12" } cpu: [s390x] os: [linux] - '@esbuild/linux-x64@0.20.2': - resolution: {integrity: sha512-1MdwI6OOTsfQfek8sLwgyjOXAu+wKhLEoaOLTjbijk6E2WONYpH9ZU2mNtR+lZ2B4uwr+usqGuVfFT9tMtGvGw==} - engines: {node: '>=12'} + "@esbuild/linux-x64@0.20.2": + resolution: + { + integrity: sha512-1MdwI6OOTsfQfek8sLwgyjOXAu+wKhLEoaOLTjbijk6E2WONYpH9ZU2mNtR+lZ2B4uwr+usqGuVfFT9tMtGvGw==, + } + engines: { node: ">=12" } cpu: [x64] os: [linux] - '@esbuild/netbsd-x64@0.20.2': - resolution: {integrity: sha512-K8/DhBxcVQkzYc43yJXDSyjlFeHQJBiowJ0uVL6Tor3jGQfSGHNNJcWxNbOI8v5k82prYqzPuwkzHt3J1T1iZQ==} - engines: {node: '>=12'} + "@esbuild/netbsd-x64@0.20.2": + resolution: + { + integrity: sha512-K8/DhBxcVQkzYc43yJXDSyjlFeHQJBiowJ0uVL6Tor3jGQfSGHNNJcWxNbOI8v5k82prYqzPuwkzHt3J1T1iZQ==, + } + engines: { node: ">=12" } cpu: [x64] os: [netbsd] - '@esbuild/openbsd-x64@0.20.2': - resolution: {integrity: sha512-eMpKlV0SThJmmJgiVyN9jTPJ2VBPquf6Kt/nAoo6DgHAoN57K15ZghiHaMvqjCye/uU4X5u3YSMgVBI1h3vKrQ==} - engines: {node: '>=12'} + "@esbuild/openbsd-x64@0.20.2": + resolution: + { + integrity: sha512-eMpKlV0SThJmmJgiVyN9jTPJ2VBPquf6Kt/nAoo6DgHAoN57K15ZghiHaMvqjCye/uU4X5u3YSMgVBI1h3vKrQ==, + } + engines: { node: ">=12" } cpu: [x64] os: [openbsd] - '@esbuild/sunos-x64@0.20.2': - resolution: {integrity: sha512-2UyFtRC6cXLyejf/YEld4Hajo7UHILetzE1vsRcGL3earZEW77JxrFjH4Ez2qaTiEfMgAXxfAZCm1fvM/G/o8w==} - engines: {node: '>=12'} + "@esbuild/sunos-x64@0.20.2": + resolution: + { + integrity: sha512-2UyFtRC6cXLyejf/YEld4Hajo7UHILetzE1vsRcGL3earZEW77JxrFjH4Ez2qaTiEfMgAXxfAZCm1fvM/G/o8w==, + } + engines: { node: ">=12" } cpu: [x64] os: [sunos] - '@esbuild/win32-arm64@0.20.2': - resolution: {integrity: sha512-GRibxoawM9ZCnDxnP3usoUDO9vUkpAxIIZ6GQI+IlVmr5kP3zUq+l17xELTHMWTWzjxa2guPNyrpq1GWmPvcGQ==} - engines: {node: '>=12'} + "@esbuild/win32-arm64@0.20.2": + resolution: + { + integrity: sha512-GRibxoawM9ZCnDxnP3usoUDO9vUkpAxIIZ6GQI+IlVmr5kP3zUq+l17xELTHMWTWzjxa2guPNyrpq1GWmPvcGQ==, + } + engines: { node: ">=12" } cpu: [arm64] os: [win32] - '@esbuild/win32-ia32@0.20.2': - resolution: {integrity: sha512-HfLOfn9YWmkSKRQqovpnITazdtquEW8/SoHW7pWpuEeguaZI4QnCRW6b+oZTztdBnZOS2hqJ6im/D5cPzBTTlQ==} - engines: {node: '>=12'} + "@esbuild/win32-ia32@0.20.2": + resolution: + { + integrity: sha512-HfLOfn9YWmkSKRQqovpnITazdtquEW8/SoHW7pWpuEeguaZI4QnCRW6b+oZTztdBnZOS2hqJ6im/D5cPzBTTlQ==, + } + engines: { node: ">=12" } cpu: [ia32] os: [win32] - '@esbuild/win32-x64@0.20.2': - resolution: {integrity: sha512-N49X4lJX27+l9jbLKSqZ6bKNjzQvHaT8IIFUy+YIqmXQdjYCToGWwOItDrfby14c78aDd5NHQl29xingXfCdLQ==} - engines: {node: '>=12'} + "@esbuild/win32-x64@0.20.2": + resolution: + { + integrity: sha512-N49X4lJX27+l9jbLKSqZ6bKNjzQvHaT8IIFUy+YIqmXQdjYCToGWwOItDrfby14c78aDd5NHQl29xingXfCdLQ==, + } + engines: { node: ">=12" } cpu: [x64] os: [win32] - '@jridgewell/gen-mapping@0.3.13': - resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==} + "@eslint-community/eslint-utils@4.9.1": + resolution: + { + integrity: sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==, + } + engines: { node: ^12.22.0 || ^14.17.0 || >=16.0.0 } + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 + + "@eslint-community/regexpp@4.12.2": + resolution: + { + integrity: sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==, + } + engines: { node: ^12.0.0 || ^14.0.0 || >=16.0.0 } + + "@eslint/eslintrc@2.1.4": + resolution: + { + integrity: sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==, + } + engines: { node: ^12.22.0 || ^14.17.0 || >=16.0.0 } + + "@eslint/js@8.57.1": + resolution: + { + integrity: sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q==, + } + engines: { node: ^12.22.0 || ^14.17.0 || >=16.0.0 } + + "@humanwhocodes/config-array@0.13.0": + resolution: + { + integrity: sha512-DZLEEqFWQFiyK6h5YIeynKx7JlvCYWL0cImfSRXZ9l4Sg2efkFGTuFf6vzXjK1cq6IYkU+Eg/JizXw+TD2vRNw==, + } + engines: { node: ">=10.10.0" } + deprecated: Use @eslint/config-array instead + + "@humanwhocodes/module-importer@1.0.1": + resolution: + { + integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==, + } + engines: { node: ">=12.22" } + + "@humanwhocodes/object-schema@2.0.3": + resolution: + { + integrity: sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==, + } + deprecated: Use @eslint/object-schema instead + + "@img/colour@1.1.0": + resolution: + { + integrity: sha512-Td76q7j57o/tLVdgS746cYARfSyxk8iEfRxewL9h4OMzYhbW4TAcppl0mT4eyqXddh6L/jwoM75mo7ixa/pCeQ==, + } + engines: { node: ">=18" } + + "@img/sharp-darwin-arm64@0.34.5": + resolution: + { + integrity: sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w==, + } + engines: { node: ^18.17.0 || ^20.3.0 || >=21.0.0 } + cpu: [arm64] + os: [darwin] + + "@img/sharp-darwin-x64@0.34.5": + resolution: + { + integrity: sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw==, + } + engines: { node: ^18.17.0 || ^20.3.0 || >=21.0.0 } + cpu: [x64] + os: [darwin] + + "@img/sharp-libvips-darwin-arm64@1.2.4": + resolution: + { + integrity: sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g==, + } + cpu: [arm64] + os: [darwin] + + "@img/sharp-libvips-darwin-x64@1.2.4": + resolution: + { + integrity: sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg==, + } + cpu: [x64] + os: [darwin] + + "@img/sharp-libvips-linux-arm64@1.2.4": + resolution: + { + integrity: sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw==, + } + cpu: [arm64] + os: [linux] + + "@img/sharp-libvips-linux-arm@1.2.4": + resolution: + { + integrity: sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==, + } + cpu: [arm] + os: [linux] + + "@img/sharp-libvips-linux-ppc64@1.2.4": + resolution: + { + integrity: sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA==, + } + cpu: [ppc64] + os: [linux] + + "@img/sharp-libvips-linux-riscv64@1.2.4": + resolution: + { + integrity: sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA==, + } + cpu: [riscv64] + os: [linux] + + "@img/sharp-libvips-linux-s390x@1.2.4": + resolution: + { + integrity: sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ==, + } + cpu: [s390x] + os: [linux] + + "@img/sharp-libvips-linux-x64@1.2.4": + resolution: + { + integrity: sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==, + } + cpu: [x64] + os: [linux] + + "@img/sharp-libvips-linuxmusl-arm64@1.2.4": + resolution: + { + integrity: sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==, + } + cpu: [arm64] + os: [linux] + + "@img/sharp-libvips-linuxmusl-x64@1.2.4": + resolution: + { + integrity: sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==, + } + cpu: [x64] + os: [linux] + + "@img/sharp-linux-arm64@0.34.5": + resolution: + { + integrity: sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==, + } + engines: { node: ^18.17.0 || ^20.3.0 || >=21.0.0 } + cpu: [arm64] + os: [linux] + + "@img/sharp-linux-arm@0.34.5": + resolution: + { + integrity: sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw==, + } + engines: { node: ^18.17.0 || ^20.3.0 || >=21.0.0 } + cpu: [arm] + os: [linux] + + "@img/sharp-linux-ppc64@0.34.5": + resolution: + { + integrity: sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA==, + } + engines: { node: ^18.17.0 || ^20.3.0 || >=21.0.0 } + cpu: [ppc64] + os: [linux] + + "@img/sharp-linux-riscv64@0.34.5": + resolution: + { + integrity: sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw==, + } + engines: { node: ^18.17.0 || ^20.3.0 || >=21.0.0 } + cpu: [riscv64] + os: [linux] + + "@img/sharp-linux-s390x@0.34.5": + resolution: + { + integrity: sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg==, + } + engines: { node: ^18.17.0 || ^20.3.0 || >=21.0.0 } + cpu: [s390x] + os: [linux] + + "@img/sharp-linux-x64@0.34.5": + resolution: + { + integrity: sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==, + } + engines: { node: ^18.17.0 || ^20.3.0 || >=21.0.0 } + cpu: [x64] + os: [linux] + + "@img/sharp-linuxmusl-arm64@0.34.5": + resolution: + { + integrity: sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==, + } + engines: { node: ^18.17.0 || ^20.3.0 || >=21.0.0 } + cpu: [arm64] + os: [linux] - '@jridgewell/remapping@2.3.5': - resolution: {integrity: sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==} + "@img/sharp-linuxmusl-x64@0.34.5": + resolution: + { + integrity: sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==, + } + engines: { node: ^18.17.0 || ^20.3.0 || >=21.0.0 } + cpu: [x64] + os: [linux] - '@jridgewell/resolve-uri@3.1.2': - resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} - engines: {node: '>=6.0.0'} + "@img/sharp-wasm32@0.34.5": + resolution: + { + integrity: sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw==, + } + engines: { node: ^18.17.0 || ^20.3.0 || >=21.0.0 } + cpu: [wasm32] - '@jridgewell/sourcemap-codec@1.5.5': - resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==} + "@img/sharp-win32-arm64@0.34.5": + resolution: + { + integrity: sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g==, + } + engines: { node: ^18.17.0 || ^20.3.0 || >=21.0.0 } + cpu: [arm64] + os: [win32] - '@jridgewell/trace-mapping@0.3.31': - resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==} + "@img/sharp-win32-ia32@0.34.5": + resolution: + { + integrity: sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg==, + } + engines: { node: ^18.17.0 || ^20.3.0 || >=21.0.0 } + cpu: [ia32] + os: [win32] - '@module-federation/bridge-react-webpack-plugin@0.21.6': - resolution: {integrity: sha512-lJMmdhD4VKVkeg8RHb+Jwe6Ou9zKVgjtb1inEURDG/sSS2ksdZA8pVKLYbRPRbdmjr193Y8gJfqFbI2dqoyc/g==} + "@img/sharp-win32-x64@0.34.5": + resolution: + { + integrity: sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw==, + } + engines: { node: ^18.17.0 || ^20.3.0 || >=21.0.0 } + cpu: [x64] + os: [win32] - '@module-federation/cli@0.21.6': - resolution: {integrity: sha512-qNojnlc8pTyKtK7ww3i/ujLrgWwgXqnD5DcDPsjADVIpu7STaoaVQ0G5GJ7WWS/ajXw6EyIAAGW/AMFh4XUxsQ==} - engines: {node: '>=16.0.0'} + "@jridgewell/gen-mapping@0.3.13": + resolution: + { + integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==, + } + + "@jridgewell/remapping@2.3.5": + resolution: + { + integrity: sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==, + } + + "@jridgewell/resolve-uri@3.1.2": + resolution: + { + integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==, + } + engines: { node: ">=6.0.0" } + + "@jridgewell/sourcemap-codec@1.5.5": + resolution: + { + integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==, + } + + "@jridgewell/trace-mapping@0.3.31": + resolution: + { + integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==, + } + + "@module-federation/bridge-react-webpack-plugin@0.21.6": + resolution: + { + integrity: sha512-lJMmdhD4VKVkeg8RHb+Jwe6Ou9zKVgjtb1inEURDG/sSS2ksdZA8pVKLYbRPRbdmjr193Y8gJfqFbI2dqoyc/g==, + } + + "@module-federation/cli@0.21.6": + resolution: + { + integrity: sha512-qNojnlc8pTyKtK7ww3i/ujLrgWwgXqnD5DcDPsjADVIpu7STaoaVQ0G5GJ7WWS/ajXw6EyIAAGW/AMFh4XUxsQ==, + } + engines: { node: ">=16.0.0" } hasBin: true - '@module-federation/data-prefetch@0.21.6': - resolution: {integrity: sha512-8HD7ZhtWZ9vl6i3wA7M8cEeCRdtvxt09SbMTfqIPm+5eb/V4ijb8zGTYSRhNDb5RCB+BAixaPiZOWKXJ63/rVw==} + "@module-federation/data-prefetch@0.21.6": + resolution: + { + integrity: sha512-8HD7ZhtWZ9vl6i3wA7M8cEeCRdtvxt09SbMTfqIPm+5eb/V4ijb8zGTYSRhNDb5RCB+BAixaPiZOWKXJ63/rVw==, + } peerDependencies: - react: '>=16.9.0' - react-dom: '>=16.9.0' - - '@module-federation/dts-plugin@0.21.6': - resolution: {integrity: sha512-YIsDk8/7QZIWn0I1TAYULniMsbyi2LgKTi9OInzVmZkwMC6644x/ratTWBOUDbdY1Co+feNkoYeot1qIWv2L7w==} + react: ">=16.9.0" + react-dom: ">=16.9.0" + + "@module-federation/dts-plugin@0.21.6": + resolution: + { + integrity: sha512-YIsDk8/7QZIWn0I1TAYULniMsbyi2LgKTi9OInzVmZkwMC6644x/ratTWBOUDbdY1Co+feNkoYeot1qIWv2L7w==, + } peerDependencies: typescript: ^4.9.0 || ^5.0.0 - vue-tsc: '>=1.0.24' + vue-tsc: ">=1.0.24" peerDependenciesMeta: vue-tsc: optional: true - '@module-federation/enhanced@0.21.6': - resolution: {integrity: sha512-8PFQxtmXc6ukBC4CqGIoc96M2Ly9WVwCPu4Ffvt+K/SB6rGbeFeZoYAwREV1zGNMJ5v5ly6+AHIEOBxNuSnzSg==} + "@module-federation/enhanced@0.21.6": + resolution: + { + integrity: sha512-8PFQxtmXc6ukBC4CqGIoc96M2Ly9WVwCPu4Ffvt+K/SB6rGbeFeZoYAwREV1zGNMJ5v5ly6+AHIEOBxNuSnzSg==, + } hasBin: true peerDependencies: typescript: ^4.9.0 || ^5.0.0 - vue-tsc: '>=1.0.24' + vue-tsc: ">=1.0.24" webpack: ^5.0.0 peerDependenciesMeta: typescript: @@ -376,377 +899,1221 @@ packages: webpack: optional: true - '@module-federation/error-codes@0.17.1': - resolution: {integrity: sha512-n6Elm4qKSjwAPxLUGtwnl7qt4y1dxB8OpSgVvXBIzqI9p27a3ZXshLPLnumlpPg1Qudaj8sLnSnFtt9yGpt5yQ==} - - '@module-federation/error-codes@0.21.6': - resolution: {integrity: sha512-MLJUCQ05KnoVl8xd6xs9a5g2/8U+eWmVxg7xiBMeR0+7OjdWUbHwcwgVFatRIwSZvFgKHfWEiI7wsU1q1XbTRQ==} - - '@module-federation/error-codes@0.22.0': - resolution: {integrity: sha512-xF9SjnEy7vTdx+xekjPCV5cIHOGCkdn3pIxo9vU7gEZMIw0SvAEdsy6Uh17xaCpm8V0FWvR0SZoK9Ik6jGOaug==} - - '@module-federation/inject-external-runtime-core-plugin@0.21.6': - resolution: {integrity: sha512-DJQne7NQ988AVi3QB8byn12FkNb+C2lBeU1NRf8/WbL0gmHsr6kW8hiEJCm8LYaURwtsQqtsEV7i+8+51qjSmQ==} + "@module-federation/error-codes@0.17.1": + resolution: + { + integrity: sha512-n6Elm4qKSjwAPxLUGtwnl7qt4y1dxB8OpSgVvXBIzqI9p27a3ZXshLPLnumlpPg1Qudaj8sLnSnFtt9yGpt5yQ==, + } + + "@module-federation/error-codes@0.21.6": + resolution: + { + integrity: sha512-MLJUCQ05KnoVl8xd6xs9a5g2/8U+eWmVxg7xiBMeR0+7OjdWUbHwcwgVFatRIwSZvFgKHfWEiI7wsU1q1XbTRQ==, + } + + "@module-federation/error-codes@0.22.0": + resolution: + { + integrity: sha512-xF9SjnEy7vTdx+xekjPCV5cIHOGCkdn3pIxo9vU7gEZMIw0SvAEdsy6Uh17xaCpm8V0FWvR0SZoK9Ik6jGOaug==, + } + + "@module-federation/inject-external-runtime-core-plugin@0.21.6": + resolution: + { + integrity: sha512-DJQne7NQ988AVi3QB8byn12FkNb+C2lBeU1NRf8/WbL0gmHsr6kW8hiEJCm8LYaURwtsQqtsEV7i+8+51qjSmQ==, + } peerDependencies: - '@module-federation/runtime-tools': 0.21.6 - - '@module-federation/managers@0.21.6': - resolution: {integrity: sha512-BeV6m2/7kF5MDVz9JJI5T8h8lMosnXkH2bOxxFewcra7ZjvDOgQu7WIio0mgk5l1zjNPvnEVKhnhrenEdcCiWg==} - - '@module-federation/manifest@0.21.6': - resolution: {integrity: sha512-yg93+I1qjRs5B5hOSvjbjmIoI2z3th8/yst9sfwvx4UDOG1acsE3HHMyPN0GdoIGwplC/KAnU5NmUz4tREUTGQ==} - - '@module-federation/rspack@0.21.6': - resolution: {integrity: sha512-SB+z1P+Bqe3R6geZje9dp0xpspX6uash+zO77nodmUy8PTTBlkL7800Cq2FMLKUdoTZHJTBVXf0K6CqQWSlItg==} + "@module-federation/runtime-tools": 0.21.6 + + "@module-federation/managers@0.21.6": + resolution: + { + integrity: sha512-BeV6m2/7kF5MDVz9JJI5T8h8lMosnXkH2bOxxFewcra7ZjvDOgQu7WIio0mgk5l1zjNPvnEVKhnhrenEdcCiWg==, + } + + "@module-federation/manifest@0.21.6": + resolution: + { + integrity: sha512-yg93+I1qjRs5B5hOSvjbjmIoI2z3th8/yst9sfwvx4UDOG1acsE3HHMyPN0GdoIGwplC/KAnU5NmUz4tREUTGQ==, + } + + "@module-federation/rspack@0.21.6": + resolution: + { + integrity: sha512-SB+z1P+Bqe3R6geZje9dp0xpspX6uash+zO77nodmUy8PTTBlkL7800Cq2FMLKUdoTZHJTBVXf0K6CqQWSlItg==, + } peerDependencies: - '@rspack/core': '>=0.7' + "@rspack/core": ">=0.7" typescript: ^4.9.0 || ^5.0.0 - vue-tsc: '>=1.0.24' + vue-tsc: ">=1.0.24" peerDependenciesMeta: typescript: optional: true vue-tsc: optional: true - '@module-federation/runtime-core@0.17.1': - resolution: {integrity: sha512-LCtIFuKgWPQ3E+13OyrVpuTPOWBMI/Ggwsq1Q874YeT8Px28b8tJRCj09DjyRFyhpSPyV/uG80T6iXPAUoLIfQ==} + "@module-federation/runtime-core@0.17.1": + resolution: + { + integrity: sha512-LCtIFuKgWPQ3E+13OyrVpuTPOWBMI/Ggwsq1Q874YeT8Px28b8tJRCj09DjyRFyhpSPyV/uG80T6iXPAUoLIfQ==, + } + + "@module-federation/runtime-core@0.21.6": + resolution: + { + integrity: sha512-5Hd1Y5qp5lU/aTiK66lidMlM/4ji2gr3EXAtJdreJzkY+bKcI5+21GRcliZ4RAkICmvdxQU5PHPL71XmNc7Lsw==, + } + + "@module-federation/runtime-core@0.22.0": + resolution: + { + integrity: sha512-GR1TcD6/s7zqItfhC87zAp30PqzvceoeDGYTgF3Vx2TXvsfDrhP6Qw9T4vudDQL3uJRne6t7CzdT29YyVxlgIA==, + } + + "@module-federation/runtime-tools@0.21.6": + resolution: + { + integrity: sha512-fnP+ZOZTFeBGiTAnxve+axGmiYn2D60h86nUISXjXClK3LUY1krUfPgf6MaD4YDJ4i51OGXZWPekeMe16pkd8Q==, + } + + "@module-federation/runtime-tools@0.22.0": + resolution: + { + integrity: sha512-4ScUJ/aUfEernb+4PbLdhM/c60VHl698Gn1gY21m9vyC1Ucn69fPCA1y2EwcCB7IItseRMoNhdcWQnzt/OPCNA==, + } + + "@module-federation/runtime@0.17.1": + resolution: + { + integrity: sha512-vKEN32MvUbpeuB/s6UXfkHDZ9N5jFyDDJnj83UTJ8n4N1jHIJu9VZ6Yi4/Ac8cfdvU8UIK9bIbfVXWbUYZUDsw==, + } + + "@module-federation/runtime@0.21.6": + resolution: + { + integrity: sha512-+caXwaQqwTNh+CQqyb4mZmXq7iEemRDrTZQGD+zyeH454JAYnJ3s/3oDFizdH6245pk+NiqDyOOkHzzFQorKhQ==, + } + + "@module-federation/runtime@0.22.0": + resolution: + { + integrity: sha512-38g5iPju2tPC3KHMPxRKmy4k4onNp6ypFPS1eKGsNLUkXgHsPMBFqAjDw96iEcjri91BrahG4XcdyKi97xZzlA==, + } + + "@module-federation/sdk@0.17.1": + resolution: + { + integrity: sha512-nlUcN6UTEi+3HWF+k8wPy7gH0yUOmCT+xNatihkIVR9REAnr7BUvHFGlPJmx7WEbLPL46+zJUbtQHvLzXwFhng==, + } + + "@module-federation/sdk@0.21.6": + resolution: + { + integrity: sha512-x6hARETb8iqHVhEsQBysuWpznNZViUh84qV2yE7AD+g7uIzHKiYdoWqj10posbo5XKf/147qgWDzKZoKoEP2dw==, + } + + "@module-federation/sdk@0.22.0": + resolution: + { + integrity: sha512-x4aFNBKn2KVQRuNVC5A7SnrSCSqyfIWmm1DvubjbO9iKFe7ith5niw8dqSFBekYBg2Fwy+eMg4sEFNVvCAdo6g==, + } + + "@module-federation/third-party-dts-extractor@0.21.6": + resolution: + { + integrity: sha512-Il6x4hLsvCgZNk1DFwuMBNeoxD1BsZ5AW2BI/nUgu0k5FiAvfcz1OFawRFEHtaM/kVrCsymMOW7pCao90DaX3A==, + } + + "@module-federation/vite@1.7.1": + resolution: + { + integrity: sha512-SIjmBXOPqh9nH+Qb2nROWpOlIeWek/8WHYD6yhpDSGnGEeWzobymru6c5hab9nyWhkYSzjGxOEsBZ4HDZZXB9Q==, + } + + "@module-federation/webpack-bundler-runtime@0.21.6": + resolution: + { + integrity: sha512-7zIp3LrcWbhGuFDTUMLJ2FJvcwjlddqhWGxi/MW3ur1a+HaO8v5tF2nl+vElKmbG1DFLU/52l3PElVcWf/YcsQ==, + } + + "@module-federation/webpack-bundler-runtime@0.22.0": + resolution: + { + integrity: sha512-aM8gCqXu+/4wBmJtVeMeeMN5guw3chf+2i6HajKtQv7SJfxV/f4IyNQJUeUQu9HfiAZHjqtMV5Lvq/Lvh8LdyA==, + } + + "@napi-rs/wasm-runtime@1.0.7": + resolution: + { + integrity: sha512-SeDnOO0Tk7Okiq6DbXmmBODgOAb9dp9gjlphokTUxmt8U3liIP1ZsozBahH69j/RJv+Rfs6IwUKHTgQYJ/HBAw==, + } + + "@nodelib/fs.scandir@2.1.5": + resolution: + { + integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==, + } + engines: { node: ">= 8" } + + "@nodelib/fs.stat@2.0.5": + resolution: + { + integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==, + } + engines: { node: ">= 8" } + + "@nodelib/fs.walk@1.2.8": + resolution: + { + integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==, + } + engines: { node: ">= 8" } + + "@polka/url@1.0.0-next.29": + resolution: + { + integrity: sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww==, + } + + "@radix-ui/primitive@1.1.3": + resolution: + { + integrity: sha512-JTF99U/6XIjCBo0wqkU5sK10glYe27MRRsfwoiq5zzOEZLHU3A3KCMa5X/azekYRCJ0HlwI0crAXS/5dEHTzDg==, + } + + "@radix-ui/react-compose-refs@1.1.2": + resolution: + { + integrity: sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg==, + } + peerDependencies: + "@types/react": "*" + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + "@types/react": + optional: true - '@module-federation/runtime-core@0.21.6': - resolution: {integrity: sha512-5Hd1Y5qp5lU/aTiK66lidMlM/4ji2gr3EXAtJdreJzkY+bKcI5+21GRcliZ4RAkICmvdxQU5PHPL71XmNc7Lsw==} + "@radix-ui/react-context@1.1.2": + resolution: + { + integrity: sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA==, + } + peerDependencies: + "@types/react": "*" + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + "@types/react": + optional: true - '@module-federation/runtime-core@0.22.0': - resolution: {integrity: sha512-GR1TcD6/s7zqItfhC87zAp30PqzvceoeDGYTgF3Vx2TXvsfDrhP6Qw9T4vudDQL3uJRne6t7CzdT29YyVxlgIA==} + "@radix-ui/react-dialog@1.1.15": + resolution: + { + integrity: sha512-TCglVRtzlffRNxRMEyR36DGBLJpeusFcgMVD9PZEzAKnUs1lKCgX5u9BmC2Yg+LL9MgZDugFFs1Vl+Jp4t/PGw==, + } + peerDependencies: + "@types/react": "*" + "@types/react-dom": "*" + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + "@types/react": + optional: true + "@types/react-dom": + optional: true - '@module-federation/runtime-tools@0.21.6': - resolution: {integrity: sha512-fnP+ZOZTFeBGiTAnxve+axGmiYn2D60h86nUISXjXClK3LUY1krUfPgf6MaD4YDJ4i51OGXZWPekeMe16pkd8Q==} + "@radix-ui/react-dismissable-layer@1.1.11": + resolution: + { + integrity: sha512-Nqcp+t5cTB8BinFkZgXiMJniQH0PsUt2k51FUhbdfeKvc4ACcG2uQniY/8+h1Yv6Kza4Q7lD7PQV0z0oicE0Mg==, + } + peerDependencies: + "@types/react": "*" + "@types/react-dom": "*" + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + "@types/react": + optional: true + "@types/react-dom": + optional: true - '@module-federation/runtime-tools@0.22.0': - resolution: {integrity: sha512-4ScUJ/aUfEernb+4PbLdhM/c60VHl698Gn1gY21m9vyC1Ucn69fPCA1y2EwcCB7IItseRMoNhdcWQnzt/OPCNA==} + "@radix-ui/react-focus-guards@1.1.3": + resolution: + { + integrity: sha512-0rFg/Rj2Q62NCm62jZw0QX7a3sz6QCQU0LpZdNrJX8byRGaGVTqbrW9jAoIAHyMQqsNpeZ81YgSizOt5WXq0Pw==, + } + peerDependencies: + "@types/react": "*" + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + "@types/react": + optional: true - '@module-federation/runtime@0.17.1': - resolution: {integrity: sha512-vKEN32MvUbpeuB/s6UXfkHDZ9N5jFyDDJnj83UTJ8n4N1jHIJu9VZ6Yi4/Ac8cfdvU8UIK9bIbfVXWbUYZUDsw==} + "@radix-ui/react-focus-scope@1.1.7": + resolution: + { + integrity: sha512-t2ODlkXBQyn7jkl6TNaw/MtVEVvIGelJDCG41Okq/KwUsJBwQ4XVZsHAVUkK4mBv3ewiAS3PGuUWuY2BoK4ZUw==, + } + peerDependencies: + "@types/react": "*" + "@types/react-dom": "*" + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + "@types/react": + optional: true + "@types/react-dom": + optional: true - '@module-federation/runtime@0.21.6': - resolution: {integrity: sha512-+caXwaQqwTNh+CQqyb4mZmXq7iEemRDrTZQGD+zyeH454JAYnJ3s/3oDFizdH6245pk+NiqDyOOkHzzFQorKhQ==} + "@radix-ui/react-id@1.1.1": + resolution: + { + integrity: sha512-kGkGegYIdQsOb4XjsfM97rXsiHaBwco+hFI66oO4s9LU+PLAC5oJ7khdOVFxkhsmlbpUqDAvXw11CluXP+jkHg==, + } + peerDependencies: + "@types/react": "*" + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + "@types/react": + optional: true - '@module-federation/runtime@0.22.0': - resolution: {integrity: sha512-38g5iPju2tPC3KHMPxRKmy4k4onNp6ypFPS1eKGsNLUkXgHsPMBFqAjDw96iEcjri91BrahG4XcdyKi97xZzlA==} + "@radix-ui/react-portal@1.1.9": + resolution: + { + integrity: sha512-bpIxvq03if6UNwXZ+HTK71JLh4APvnXntDc6XOX8UVq4XQOVl7lwok0AvIl+b8zgCw3fSaVTZMpAPPagXbKmHQ==, + } + peerDependencies: + "@types/react": "*" + "@types/react-dom": "*" + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + "@types/react": + optional: true + "@types/react-dom": + optional: true - '@module-federation/sdk@0.17.1': - resolution: {integrity: sha512-nlUcN6UTEi+3HWF+k8wPy7gH0yUOmCT+xNatihkIVR9REAnr7BUvHFGlPJmx7WEbLPL46+zJUbtQHvLzXwFhng==} + "@radix-ui/react-presence@1.1.5": + resolution: + { + integrity: sha512-/jfEwNDdQVBCNvjkGit4h6pMOzq8bHkopq458dPt2lMjx+eBQUohZNG9A7DtO/O5ukSbxuaNGXMjHicgwy6rQQ==, + } + peerDependencies: + "@types/react": "*" + "@types/react-dom": "*" + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + "@types/react": + optional: true + "@types/react-dom": + optional: true - '@module-federation/sdk@0.21.6': - resolution: {integrity: sha512-x6hARETb8iqHVhEsQBysuWpznNZViUh84qV2yE7AD+g7uIzHKiYdoWqj10posbo5XKf/147qgWDzKZoKoEP2dw==} + "@radix-ui/react-primitive@2.1.3": + resolution: + { + integrity: sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ==, + } + peerDependencies: + "@types/react": "*" + "@types/react-dom": "*" + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + "@types/react": + optional: true + "@types/react-dom": + optional: true - '@module-federation/sdk@0.22.0': - resolution: {integrity: sha512-x4aFNBKn2KVQRuNVC5A7SnrSCSqyfIWmm1DvubjbO9iKFe7ith5niw8dqSFBekYBg2Fwy+eMg4sEFNVvCAdo6g==} + "@radix-ui/react-slot@1.2.3": + resolution: + { + integrity: sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==, + } + peerDependencies: + "@types/react": "*" + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + "@types/react": + optional: true - '@module-federation/third-party-dts-extractor@0.21.6': - resolution: {integrity: sha512-Il6x4hLsvCgZNk1DFwuMBNeoxD1BsZ5AW2BI/nUgu0k5FiAvfcz1OFawRFEHtaM/kVrCsymMOW7pCao90DaX3A==} + "@radix-ui/react-slot@1.2.4": + resolution: + { + integrity: sha512-Jl+bCv8HxKnlTLVrcDE8zTMJ09R9/ukw4qBs/oZClOfoQk/cOTbDn+NceXfV7j09YPVQUryJPHurafcSg6EVKA==, + } + peerDependencies: + "@types/react": "*" + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + "@types/react": + optional: true - '@module-federation/vite@1.7.1': - resolution: {integrity: sha512-SIjmBXOPqh9nH+Qb2nROWpOlIeWek/8WHYD6yhpDSGnGEeWzobymru6c5hab9nyWhkYSzjGxOEsBZ4HDZZXB9Q==} + "@radix-ui/react-use-callback-ref@1.1.1": + resolution: + { + integrity: sha512-FkBMwD+qbGQeMu1cOHnuGB6x4yzPjho8ap5WtbEJ26umhgqVXbhekKUQO+hZEL1vU92a3wHwdp0HAcqAUF5iDg==, + } + peerDependencies: + "@types/react": "*" + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + "@types/react": + optional: true - '@module-federation/webpack-bundler-runtime@0.21.6': - resolution: {integrity: sha512-7zIp3LrcWbhGuFDTUMLJ2FJvcwjlddqhWGxi/MW3ur1a+HaO8v5tF2nl+vElKmbG1DFLU/52l3PElVcWf/YcsQ==} + "@radix-ui/react-use-controllable-state@1.2.2": + resolution: + { + integrity: sha512-BjasUjixPFdS+NKkypcyyN5Pmg83Olst0+c6vGov0diwTEo6mgdqVR6hxcEgFuh4QrAs7Rc+9KuGJ9TVCj0Zzg==, + } + peerDependencies: + "@types/react": "*" + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + "@types/react": + optional: true - '@module-federation/webpack-bundler-runtime@0.22.0': - resolution: {integrity: sha512-aM8gCqXu+/4wBmJtVeMeeMN5guw3chf+2i6HajKtQv7SJfxV/f4IyNQJUeUQu9HfiAZHjqtMV5Lvq/Lvh8LdyA==} + "@radix-ui/react-use-effect-event@0.0.2": + resolution: + { + integrity: sha512-Qp8WbZOBe+blgpuUT+lw2xheLP8q0oatc9UpmiemEICxGvFLYmHm9QowVZGHtJlGbS6A6yJ3iViad/2cVjnOiA==, + } + peerDependencies: + "@types/react": "*" + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + "@types/react": + optional: true - '@napi-rs/wasm-runtime@1.0.7': - resolution: {integrity: sha512-SeDnOO0Tk7Okiq6DbXmmBODgOAb9dp9gjlphokTUxmt8U3liIP1ZsozBahH69j/RJv+Rfs6IwUKHTgQYJ/HBAw==} + "@radix-ui/react-use-escape-keydown@1.1.1": + resolution: + { + integrity: sha512-Il0+boE7w/XebUHyBjroE+DbByORGR9KKmITzbR7MyQ4akpORYP/ZmbhAr0DG7RmmBqoOnZdy2QlvajJ2QA59g==, + } + peerDependencies: + "@types/react": "*" + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + "@types/react": + optional: true - '@polka/url@1.0.0-next.29': - resolution: {integrity: sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww==} + "@radix-ui/react-use-layout-effect@1.1.1": + resolution: + { + integrity: sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ==, + } + peerDependencies: + "@types/react": "*" + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + "@types/react": + optional: true - '@rollup/pluginutils@5.3.0': - resolution: {integrity: sha512-5EdhGZtnu3V88ces7s53hhfK5KSASnJZv8Lulpc04cWO3REESroJXg73DFsOmgbU2BhwV0E20bu2IDZb3VKW4Q==} - engines: {node: '>=14.0.0'} + "@rollup/pluginutils@5.3.0": + resolution: + { + integrity: sha512-5EdhGZtnu3V88ces7s53hhfK5KSASnJZv8Lulpc04cWO3REESroJXg73DFsOmgbU2BhwV0E20bu2IDZb3VKW4Q==, + } + engines: { node: ">=14.0.0" } peerDependencies: rollup: ^1.20.0||^2.0.0||^3.0.0||^4.0.0 peerDependenciesMeta: rollup: optional: true - '@rollup/rollup-android-arm-eabi@4.59.0': - resolution: {integrity: sha512-upnNBkA6ZH2VKGcBj9Fyl9IGNPULcjXRlg0LLeaioQWueH30p6IXtJEbKAgvyv+mJaMxSm1l6xwDXYjpEMiLMg==} + "@rollup/rollup-android-arm-eabi@4.59.0": + resolution: + { + integrity: sha512-upnNBkA6ZH2VKGcBj9Fyl9IGNPULcjXRlg0LLeaioQWueH30p6IXtJEbKAgvyv+mJaMxSm1l6xwDXYjpEMiLMg==, + } cpu: [arm] os: [android] - '@rollup/rollup-android-arm64@4.59.0': - resolution: {integrity: sha512-hZ+Zxj3SySm4A/DylsDKZAeVg0mvi++0PYVceVyX7hemkw7OreKdCvW2oQ3T1FMZvCaQXqOTHb8qmBShoqk69Q==} + "@rollup/rollup-android-arm64@4.59.0": + resolution: + { + integrity: sha512-hZ+Zxj3SySm4A/DylsDKZAeVg0mvi++0PYVceVyX7hemkw7OreKdCvW2oQ3T1FMZvCaQXqOTHb8qmBShoqk69Q==, + } cpu: [arm64] os: [android] - '@rollup/rollup-darwin-arm64@4.59.0': - resolution: {integrity: sha512-W2Psnbh1J8ZJw0xKAd8zdNgF9HRLkdWwwdWqubSVk0pUuQkoHnv7rx4GiF9rT4t5DIZGAsConRE3AxCdJ4m8rg==} + "@rollup/rollup-darwin-arm64@4.59.0": + resolution: + { + integrity: sha512-W2Psnbh1J8ZJw0xKAd8zdNgF9HRLkdWwwdWqubSVk0pUuQkoHnv7rx4GiF9rT4t5DIZGAsConRE3AxCdJ4m8rg==, + } cpu: [arm64] os: [darwin] - '@rollup/rollup-darwin-x64@4.59.0': - resolution: {integrity: sha512-ZW2KkwlS4lwTv7ZVsYDiARfFCnSGhzYPdiOU4IM2fDbL+QGlyAbjgSFuqNRbSthybLbIJ915UtZBtmuLrQAT/w==} + "@rollup/rollup-darwin-x64@4.59.0": + resolution: + { + integrity: sha512-ZW2KkwlS4lwTv7ZVsYDiARfFCnSGhzYPdiOU4IM2fDbL+QGlyAbjgSFuqNRbSthybLbIJ915UtZBtmuLrQAT/w==, + } cpu: [x64] os: [darwin] - '@rollup/rollup-freebsd-arm64@4.59.0': - resolution: {integrity: sha512-EsKaJ5ytAu9jI3lonzn3BgG8iRBjV4LxZexygcQbpiU0wU0ATxhNVEpXKfUa0pS05gTcSDMKpn3Sx+QB9RlTTA==} + "@rollup/rollup-freebsd-arm64@4.59.0": + resolution: + { + integrity: sha512-EsKaJ5ytAu9jI3lonzn3BgG8iRBjV4LxZexygcQbpiU0wU0ATxhNVEpXKfUa0pS05gTcSDMKpn3Sx+QB9RlTTA==, + } cpu: [arm64] os: [freebsd] - '@rollup/rollup-freebsd-x64@4.59.0': - resolution: {integrity: sha512-d3DuZi2KzTMjImrxoHIAODUZYoUUMsuUiY4SRRcJy6NJoZ6iIqWnJu9IScV9jXysyGMVuW+KNzZvBLOcpdl3Vg==} + "@rollup/rollup-freebsd-x64@4.59.0": + resolution: + { + integrity: sha512-d3DuZi2KzTMjImrxoHIAODUZYoUUMsuUiY4SRRcJy6NJoZ6iIqWnJu9IScV9jXysyGMVuW+KNzZvBLOcpdl3Vg==, + } cpu: [x64] os: [freebsd] - '@rollup/rollup-linux-arm-gnueabihf@4.59.0': - resolution: {integrity: sha512-t4ONHboXi/3E0rT6OZl1pKbl2Vgxf9vJfWgmUoCEVQVxhW6Cw/c8I6hbbu7DAvgp82RKiH7TpLwxnJeKv2pbsw==} + "@rollup/rollup-linux-arm-gnueabihf@4.59.0": + resolution: + { + integrity: sha512-t4ONHboXi/3E0rT6OZl1pKbl2Vgxf9vJfWgmUoCEVQVxhW6Cw/c8I6hbbu7DAvgp82RKiH7TpLwxnJeKv2pbsw==, + } cpu: [arm] os: [linux] - libc: [glibc] - '@rollup/rollup-linux-arm-musleabihf@4.59.0': - resolution: {integrity: sha512-CikFT7aYPA2ufMD086cVORBYGHffBo4K8MQ4uPS/ZnY54GKj36i196u8U+aDVT2LX4eSMbyHtyOh7D7Zvk2VvA==} + "@rollup/rollup-linux-arm-musleabihf@4.59.0": + resolution: + { + integrity: sha512-CikFT7aYPA2ufMD086cVORBYGHffBo4K8MQ4uPS/ZnY54GKj36i196u8U+aDVT2LX4eSMbyHtyOh7D7Zvk2VvA==, + } cpu: [arm] os: [linux] - libc: [musl] - '@rollup/rollup-linux-arm64-gnu@4.59.0': - resolution: {integrity: sha512-jYgUGk5aLd1nUb1CtQ8E+t5JhLc9x5WdBKew9ZgAXg7DBk0ZHErLHdXM24rfX+bKrFe+Xp5YuJo54I5HFjGDAA==} + "@rollup/rollup-linux-arm64-gnu@4.59.0": + resolution: + { + integrity: sha512-jYgUGk5aLd1nUb1CtQ8E+t5JhLc9x5WdBKew9ZgAXg7DBk0ZHErLHdXM24rfX+bKrFe+Xp5YuJo54I5HFjGDAA==, + } cpu: [arm64] os: [linux] - libc: [glibc] - '@rollup/rollup-linux-arm64-musl@4.59.0': - resolution: {integrity: sha512-peZRVEdnFWZ5Bh2KeumKG9ty7aCXzzEsHShOZEFiCQlDEepP1dpUl/SrUNXNg13UmZl+gzVDPsiCwnV1uI0RUA==} + "@rollup/rollup-linux-arm64-musl@4.59.0": + resolution: + { + integrity: sha512-peZRVEdnFWZ5Bh2KeumKG9ty7aCXzzEsHShOZEFiCQlDEepP1dpUl/SrUNXNg13UmZl+gzVDPsiCwnV1uI0RUA==, + } cpu: [arm64] os: [linux] - libc: [musl] - '@rollup/rollup-linux-loong64-gnu@4.59.0': - resolution: {integrity: sha512-gbUSW/97f7+r4gHy3Jlup8zDG190AuodsWnNiXErp9mT90iCy9NKKU0Xwx5k8VlRAIV2uU9CsMnEFg/xXaOfXg==} + "@rollup/rollup-linux-loong64-gnu@4.59.0": + resolution: + { + integrity: sha512-gbUSW/97f7+r4gHy3Jlup8zDG190AuodsWnNiXErp9mT90iCy9NKKU0Xwx5k8VlRAIV2uU9CsMnEFg/xXaOfXg==, + } cpu: [loong64] os: [linux] - libc: [glibc] - '@rollup/rollup-linux-loong64-musl@4.59.0': - resolution: {integrity: sha512-yTRONe79E+o0FWFijasoTjtzG9EBedFXJMl888NBEDCDV9I2wGbFFfJQQe63OijbFCUZqxpHz1GzpbtSFikJ4Q==} + "@rollup/rollup-linux-loong64-musl@4.59.0": + resolution: + { + integrity: sha512-yTRONe79E+o0FWFijasoTjtzG9EBedFXJMl888NBEDCDV9I2wGbFFfJQQe63OijbFCUZqxpHz1GzpbtSFikJ4Q==, + } cpu: [loong64] os: [linux] - libc: [musl] - '@rollup/rollup-linux-ppc64-gnu@4.59.0': - resolution: {integrity: sha512-sw1o3tfyk12k3OEpRddF68a1unZ5VCN7zoTNtSn2KndUE+ea3m3ROOKRCZxEpmT9nsGnogpFP9x6mnLTCaoLkA==} + "@rollup/rollup-linux-ppc64-gnu@4.59.0": + resolution: + { + integrity: sha512-sw1o3tfyk12k3OEpRddF68a1unZ5VCN7zoTNtSn2KndUE+ea3m3ROOKRCZxEpmT9nsGnogpFP9x6mnLTCaoLkA==, + } cpu: [ppc64] os: [linux] - libc: [glibc] - '@rollup/rollup-linux-ppc64-musl@4.59.0': - resolution: {integrity: sha512-+2kLtQ4xT3AiIxkzFVFXfsmlZiG5FXYW7ZyIIvGA7Bdeuh9Z0aN4hVyXS/G1E9bTP/vqszNIN/pUKCk/BTHsKA==} + "@rollup/rollup-linux-ppc64-musl@4.59.0": + resolution: + { + integrity: sha512-+2kLtQ4xT3AiIxkzFVFXfsmlZiG5FXYW7ZyIIvGA7Bdeuh9Z0aN4hVyXS/G1E9bTP/vqszNIN/pUKCk/BTHsKA==, + } cpu: [ppc64] os: [linux] - libc: [musl] - '@rollup/rollup-linux-riscv64-gnu@4.59.0': - resolution: {integrity: sha512-NDYMpsXYJJaj+I7UdwIuHHNxXZ/b/N2hR15NyH3m2qAtb/hHPA4g4SuuvrdxetTdndfj9b1WOmy73kcPRoERUg==} + "@rollup/rollup-linux-riscv64-gnu@4.59.0": + resolution: + { + integrity: sha512-NDYMpsXYJJaj+I7UdwIuHHNxXZ/b/N2hR15NyH3m2qAtb/hHPA4g4SuuvrdxetTdndfj9b1WOmy73kcPRoERUg==, + } cpu: [riscv64] os: [linux] - libc: [glibc] - '@rollup/rollup-linux-riscv64-musl@4.59.0': - resolution: {integrity: sha512-nLckB8WOqHIf1bhymk+oHxvM9D3tyPndZH8i8+35p/1YiVoVswPid2yLzgX7ZJP0KQvnkhM4H6QZ5m0LzbyIAg==} + "@rollup/rollup-linux-riscv64-musl@4.59.0": + resolution: + { + integrity: sha512-nLckB8WOqHIf1bhymk+oHxvM9D3tyPndZH8i8+35p/1YiVoVswPid2yLzgX7ZJP0KQvnkhM4H6QZ5m0LzbyIAg==, + } cpu: [riscv64] os: [linux] - libc: [musl] - '@rollup/rollup-linux-s390x-gnu@4.59.0': - resolution: {integrity: sha512-oF87Ie3uAIvORFBpwnCvUzdeYUqi2wY6jRFWJAy1qus/udHFYIkplYRW+wo+GRUP4sKzYdmE1Y3+rY5Gc4ZO+w==} + "@rollup/rollup-linux-s390x-gnu@4.59.0": + resolution: + { + integrity: sha512-oF87Ie3uAIvORFBpwnCvUzdeYUqi2wY6jRFWJAy1qus/udHFYIkplYRW+wo+GRUP4sKzYdmE1Y3+rY5Gc4ZO+w==, + } cpu: [s390x] os: [linux] - libc: [glibc] - '@rollup/rollup-linux-x64-gnu@4.59.0': - resolution: {integrity: sha512-3AHmtQq/ppNuUspKAlvA8HtLybkDflkMuLK4DPo77DfthRb71V84/c4MlWJXixZz4uruIH4uaa07IqoAkG64fg==} + "@rollup/rollup-linux-x64-gnu@4.59.0": + resolution: + { + integrity: sha512-3AHmtQq/ppNuUspKAlvA8HtLybkDflkMuLK4DPo77DfthRb71V84/c4MlWJXixZz4uruIH4uaa07IqoAkG64fg==, + } cpu: [x64] os: [linux] - libc: [glibc] - '@rollup/rollup-linux-x64-musl@4.59.0': - resolution: {integrity: sha512-2UdiwS/9cTAx7qIUZB/fWtToJwvt0Vbo0zmnYt7ED35KPg13Q0ym1g442THLC7VyI6JfYTP4PiSOWyoMdV2/xg==} + "@rollup/rollup-linux-x64-musl@4.59.0": + resolution: + { + integrity: sha512-2UdiwS/9cTAx7qIUZB/fWtToJwvt0Vbo0zmnYt7ED35KPg13Q0ym1g442THLC7VyI6JfYTP4PiSOWyoMdV2/xg==, + } cpu: [x64] os: [linux] - libc: [musl] - '@rollup/rollup-openbsd-x64@4.59.0': - resolution: {integrity: sha512-M3bLRAVk6GOwFlPTIxVBSYKUaqfLrn8l0psKinkCFxl4lQvOSz8ZrKDz2gxcBwHFpci0B6rttydI4IpS4IS/jQ==} + "@rollup/rollup-openbsd-x64@4.59.0": + resolution: + { + integrity: sha512-M3bLRAVk6GOwFlPTIxVBSYKUaqfLrn8l0psKinkCFxl4lQvOSz8ZrKDz2gxcBwHFpci0B6rttydI4IpS4IS/jQ==, + } cpu: [x64] os: [openbsd] - '@rollup/rollup-openharmony-arm64@4.59.0': - resolution: {integrity: sha512-tt9KBJqaqp5i5HUZzoafHZX8b5Q2Fe7UjYERADll83O4fGqJ49O1FsL6LpdzVFQcpwvnyd0i+K/VSwu/o/nWlA==} + "@rollup/rollup-openharmony-arm64@4.59.0": + resolution: + { + integrity: sha512-tt9KBJqaqp5i5HUZzoafHZX8b5Q2Fe7UjYERADll83O4fGqJ49O1FsL6LpdzVFQcpwvnyd0i+K/VSwu/o/nWlA==, + } cpu: [arm64] os: [openharmony] - '@rollup/rollup-win32-arm64-msvc@4.59.0': - resolution: {integrity: sha512-V5B6mG7OrGTwnxaNUzZTDTjDS7F75PO1ae6MJYdiMu60sq0CqN5CVeVsbhPxalupvTX8gXVSU9gq+Rx1/hvu6A==} + "@rollup/rollup-win32-arm64-msvc@4.59.0": + resolution: + { + integrity: sha512-V5B6mG7OrGTwnxaNUzZTDTjDS7F75PO1ae6MJYdiMu60sq0CqN5CVeVsbhPxalupvTX8gXVSU9gq+Rx1/hvu6A==, + } cpu: [arm64] os: [win32] - '@rollup/rollup-win32-ia32-msvc@4.59.0': - resolution: {integrity: sha512-UKFMHPuM9R0iBegwzKF4y0C4J9u8C6MEJgFuXTBerMk7EJ92GFVFYBfOZaSGLu6COf7FxpQNqhNS4c4icUPqxA==} + "@rollup/rollup-win32-ia32-msvc@4.59.0": + resolution: + { + integrity: sha512-UKFMHPuM9R0iBegwzKF4y0C4J9u8C6MEJgFuXTBerMk7EJ92GFVFYBfOZaSGLu6COf7FxpQNqhNS4c4icUPqxA==, + } cpu: [ia32] os: [win32] - '@rollup/rollup-win32-x64-gnu@4.59.0': - resolution: {integrity: sha512-laBkYlSS1n2L8fSo1thDNGrCTQMmxjYY5G0WFWjFFYZkKPjsMBsgJfGf4TLxXrF6RyhI60L8TMOjBMvXiTcxeA==} + "@rollup/rollup-win32-x64-gnu@4.59.0": + resolution: + { + integrity: sha512-laBkYlSS1n2L8fSo1thDNGrCTQMmxjYY5G0WFWjFFYZkKPjsMBsgJfGf4TLxXrF6RyhI60L8TMOjBMvXiTcxeA==, + } cpu: [x64] os: [win32] - '@rollup/rollup-win32-x64-msvc@4.59.0': - resolution: {integrity: sha512-2HRCml6OztYXyJXAvdDXPKcawukWY2GpR5/nxKp4iBgiO3wcoEGkAaqctIbZcNB6KlUQBIqt8VYkNSj2397EfA==} + "@rollup/rollup-win32-x64-msvc@4.59.0": + resolution: + { + integrity: sha512-2HRCml6OztYXyJXAvdDXPKcawukWY2GpR5/nxKp4iBgiO3wcoEGkAaqctIbZcNB6KlUQBIqt8VYkNSj2397EfA==, + } cpu: [x64] os: [win32] - '@rspack/binding-darwin-arm64@1.7.9': - resolution: {integrity: sha512-64dgstte0If5czi9bA/cpOe0ryY6wC9AIQRtyJ3DlOF6Tt+y9cKkmUoGu3V+WYaYIZRT7HNk8V7kL8amVjFTYw==} + "@rspack/binding-darwin-arm64@1.7.9": + resolution: + { + integrity: sha512-64dgstte0If5czi9bA/cpOe0ryY6wC9AIQRtyJ3DlOF6Tt+y9cKkmUoGu3V+WYaYIZRT7HNk8V7kL8amVjFTYw==, + } cpu: [arm64] os: [darwin] - '@rspack/binding-darwin-x64@1.7.9': - resolution: {integrity: sha512-2QSLs3w4rLy4UUGVnIlkt6IlIKOzR1e0RPsq2FYQW6s3p9JrwRCtOeHohyh7EJSqF54dtfhe9UZSAwba3LqH1Q==} + "@rspack/binding-darwin-x64@1.7.9": + resolution: + { + integrity: sha512-2QSLs3w4rLy4UUGVnIlkt6IlIKOzR1e0RPsq2FYQW6s3p9JrwRCtOeHohyh7EJSqF54dtfhe9UZSAwba3LqH1Q==, + } cpu: [x64] os: [darwin] - '@rspack/binding-linux-arm64-gnu@1.7.9': - resolution: {integrity: sha512-qhUGI/uVfvLmKWts4QkVHGL8yfUyJkblZs+OFD5Upa2y676EOsbQgWsCwX4xGB6Tv+TOzFP0SLh/UfO8ZfdE+w==} + "@rspack/binding-linux-arm64-gnu@1.7.9": + resolution: + { + integrity: sha512-qhUGI/uVfvLmKWts4QkVHGL8yfUyJkblZs+OFD5Upa2y676EOsbQgWsCwX4xGB6Tv+TOzFP0SLh/UfO8ZfdE+w==, + } cpu: [arm64] os: [linux] - libc: [glibc] - '@rspack/binding-linux-arm64-musl@1.7.9': - resolution: {integrity: sha512-VjfmR1hgO9n3L6MaE5KG+DXSrrLVqHHOkVcOtS2LMq3bjMTwbBywY7ycymcLnX5KJsol8d3ZGYep6IfSOt3lFA==} + "@rspack/binding-linux-arm64-musl@1.7.9": + resolution: + { + integrity: sha512-VjfmR1hgO9n3L6MaE5KG+DXSrrLVqHHOkVcOtS2LMq3bjMTwbBywY7ycymcLnX5KJsol8d3ZGYep6IfSOt3lFA==, + } cpu: [arm64] os: [linux] - libc: [musl] - '@rspack/binding-linux-x64-gnu@1.7.9': - resolution: {integrity: sha512-0kldV+3WTs/VYDWzxJ7K40hCW26IHtnk8xPK3whKoo1649rgeXXa0EdsU5P7hG8Ef5SWQjHHHZ/fuHYSO3Y6HA==} + "@rspack/binding-linux-x64-gnu@1.7.9": + resolution: + { + integrity: sha512-0kldV+3WTs/VYDWzxJ7K40hCW26IHtnk8xPK3whKoo1649rgeXXa0EdsU5P7hG8Ef5SWQjHHHZ/fuHYSO3Y6HA==, + } cpu: [x64] os: [linux] - libc: [glibc] - '@rspack/binding-linux-x64-musl@1.7.9': - resolution: {integrity: sha512-Gi4872cFtc2d83FKATR6Qcf2VBa/tFCqffI/IwRRl6Hx5FulEBqx+tH7gAuRVF693vrbXNxK+FQ+k4iEsEJxrw==} + "@rspack/binding-linux-x64-musl@1.7.9": + resolution: + { + integrity: sha512-Gi4872cFtc2d83FKATR6Qcf2VBa/tFCqffI/IwRRl6Hx5FulEBqx+tH7gAuRVF693vrbXNxK+FQ+k4iEsEJxrw==, + } cpu: [x64] os: [linux] - libc: [musl] - '@rspack/binding-wasm32-wasi@1.7.9': - resolution: {integrity: sha512-5QEzqo6EaolpuZmK6w/mgSueorgGnnzp7dJaAvBj6ECFIg/aLXhXXmWCWbxt7Ws2gKvG5/PgaxDqbUxYL51juA==} + "@rspack/binding-wasm32-wasi@1.7.9": + resolution: + { + integrity: sha512-5QEzqo6EaolpuZmK6w/mgSueorgGnnzp7dJaAvBj6ECFIg/aLXhXXmWCWbxt7Ws2gKvG5/PgaxDqbUxYL51juA==, + } cpu: [wasm32] - '@rspack/binding-win32-arm64-msvc@1.7.9': - resolution: {integrity: sha512-MMqvcrIc8aOqTuHjWkjdzilvoZ3Hv07Od0Foogiyq3JMudsS3Wcmh7T1dFerGg19MOJcRUeEkrg2NQOMOQ6xDA==} + "@rspack/binding-win32-arm64-msvc@1.7.9": + resolution: + { + integrity: sha512-MMqvcrIc8aOqTuHjWkjdzilvoZ3Hv07Od0Foogiyq3JMudsS3Wcmh7T1dFerGg19MOJcRUeEkrg2NQOMOQ6xDA==, + } cpu: [arm64] os: [win32] - '@rspack/binding-win32-ia32-msvc@1.7.9': - resolution: {integrity: sha512-4kYYS+NZ2CuNbKjq40yB/UEyB51o1PHj5wpr+Y943oOJXpEKWU2Q4vkF8VEohPEcnA9cKVotYCnqStme+02suA==} + "@rspack/binding-win32-ia32-msvc@1.7.9": + resolution: + { + integrity: sha512-4kYYS+NZ2CuNbKjq40yB/UEyB51o1PHj5wpr+Y943oOJXpEKWU2Q4vkF8VEohPEcnA9cKVotYCnqStme+02suA==, + } cpu: [ia32] os: [win32] - '@rspack/binding-win32-x64-msvc@1.7.9': - resolution: {integrity: sha512-1g+QyXXvs+838Un/4GaUvJfARDGHMCs15eXDYWBl5m/Skubyng8djWAgr6ag1+cVoJZXCPOvybTItcblWF3gbQ==} + "@rspack/binding-win32-x64-msvc@1.7.9": + resolution: + { + integrity: sha512-1g+QyXXvs+838Un/4GaUvJfARDGHMCs15eXDYWBl5m/Skubyng8djWAgr6ag1+cVoJZXCPOvybTItcblWF3gbQ==, + } cpu: [x64] os: [win32] - '@rspack/binding@1.7.9': - resolution: {integrity: sha512-A56e0NdfNwbOSJoilMkxzaPuVYaKCNn1shuiwWnCIBmhV9ix1n9S1XvquDjkGyv+gCdR1+zfJBOa5DMB7htLHw==} - - '@rspack/core@1.7.9': - resolution: {integrity: sha512-VHuSKvRkuv42Ya+TxEGO0LE0r9+8P4tKGokmomj4R1f/Nu2vtS3yoaIMfC4fR6VuHGd3MZ+KTI0cNNwHfFcskw==} - engines: {node: '>=18.12.0'} + "@rspack/binding@1.7.9": + resolution: + { + integrity: sha512-A56e0NdfNwbOSJoilMkxzaPuVYaKCNn1shuiwWnCIBmhV9ix1n9S1XvquDjkGyv+gCdR1+zfJBOa5DMB7htLHw==, + } + + "@rspack/core@1.7.9": + resolution: + { + integrity: sha512-VHuSKvRkuv42Ya+TxEGO0LE0r9+8P4tKGokmomj4R1f/Nu2vtS3yoaIMfC4fR6VuHGd3MZ+KTI0cNNwHfFcskw==, + } + engines: { node: ">=18.12.0" } peerDependencies: - '@swc/helpers': '>=0.5.1' + "@swc/helpers": ">=0.5.1" peerDependenciesMeta: - '@swc/helpers': + "@swc/helpers": optional: true - '@rspack/lite-tapable@1.1.0': - resolution: {integrity: sha512-E2B0JhYFmVAwdDiG14+DW0Di4Ze4Jg10Pc4/lILUrd5DRCaklduz2OvJ5HYQ6G+hd+WTzqQb3QnDNfK4yvAFYw==} + "@rspack/lite-tapable@1.1.0": + resolution: + { + integrity: sha512-E2B0JhYFmVAwdDiG14+DW0Di4Ze4Jg10Pc4/lILUrd5DRCaklduz2OvJ5HYQ6G+hd+WTzqQb3QnDNfK4yvAFYw==, + } + + "@tailwindcss/node@4.2.2": + resolution: + { + integrity: sha512-pXS+wJ2gZpVXqFaUEjojq7jzMpTGf8rU6ipJz5ovJV6PUGmlJ+jvIwGrzdHdQ80Sg+wmQxUFuoW1UAAwHNEdFA==, + } + + "@tailwindcss/oxide-android-arm64@4.2.2": + resolution: + { + integrity: sha512-dXGR1n+P3B6748jZO/SvHZq7qBOqqzQ+yFrXpoOWWALWndF9MoSKAT3Q0fYgAzYzGhxNYOoysRvYlpixRBBoDg==, + } + engines: { node: ">= 20" } + cpu: [arm64] + os: [android] - '@testing-library/dom@10.4.1': - resolution: {integrity: sha512-o4PXJQidqJl82ckFaXUeoAW+XysPLauYI43Abki5hABd853iMhitooc6znOnczgbTYmEP6U6/y1ZyKAIsvMKGg==} - engines: {node: '>=18'} + "@tailwindcss/oxide-darwin-arm64@4.2.2": + resolution: + { + integrity: sha512-iq9Qjr6knfMpZHj55/37ouZeykwbDqF21gPFtfnhCCKGDcPI/21FKC9XdMO/XyBM7qKORx6UIhGgg6jLl7BZlg==, + } + engines: { node: ">= 20" } + cpu: [arm64] + os: [darwin] - '@testing-library/jest-dom@6.9.1': - resolution: {integrity: sha512-zIcONa+hVtVSSep9UT3jZ5rizo2BsxgyDYU7WFD5eICBE7no3881HGeb/QkGfsJs6JTkY1aQhT7rIPC7e+0nnA==} - engines: {node: '>=14', npm: '>=6', yarn: '>=1'} + "@tailwindcss/oxide-darwin-x64@4.2.2": + resolution: + { + integrity: sha512-BlR+2c3nzc8f2G639LpL89YY4bdcIdUmiOOkv2GQv4/4M0vJlpXEa0JXNHhCHU7VWOKWT/CjqHdTP8aUuDJkuw==, + } + engines: { node: ">= 20" } + cpu: [x64] + os: [darwin] - '@testing-library/react@16.3.2': - resolution: {integrity: sha512-XU5/SytQM+ykqMnAnvB2umaJNIOsLF3PVv//1Ew4CTcpz0/BRyy/af40qqrt7SjKpDdT1saBMc42CUok5gaw+g==} - engines: {node: '>=18'} - peerDependencies: - '@testing-library/dom': ^10.0.0 - '@types/react': ^18.0.0 || ^19.0.0 - '@types/react-dom': ^18.0.0 || ^19.0.0 - react: ^18.0.0 || ^19.0.0 - react-dom: ^18.0.0 || ^19.0.0 - peerDependenciesMeta: - '@types/react': - optional: true - '@types/react-dom': - optional: true + "@tailwindcss/oxide-freebsd-x64@4.2.2": + resolution: + { + integrity: sha512-YUqUgrGMSu2CDO82hzlQ5qSb5xmx3RUrke/QgnoEx7KvmRJHQuZHZmZTLSuuHwFf0DJPybFMXMYf+WJdxHy/nQ==, + } + engines: { node: ">= 20" } + cpu: [x64] + os: [freebsd] - '@tybys/wasm-util@0.10.1': - resolution: {integrity: sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==} + "@tailwindcss/oxide-linux-arm-gnueabihf@4.2.2": + resolution: + { + integrity: sha512-FPdhvsW6g06T9BWT0qTwiVZYE2WIFo2dY5aCSpjG/S/u1tby+wXoslXS0kl3/KXnULlLr1E3NPRRw0g7t2kgaQ==, + } + engines: { node: ">= 20" } + cpu: [arm] + os: [linux] + + "@tailwindcss/oxide-linux-arm64-gnu@4.2.2": + resolution: + { + integrity: sha512-4og1V+ftEPXGttOO7eCmW7VICmzzJWgMx+QXAJRAhjrSjumCwWqMfkDrNu1LXEQzNAwz28NCUpucgQPrR4S2yw==, + } + engines: { node: ">= 20" } + cpu: [arm64] + os: [linux] - '@types/aria-query@5.0.4': - resolution: {integrity: sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==} + "@tailwindcss/oxide-linux-arm64-musl@4.2.2": + resolution: + { + integrity: sha512-oCfG/mS+/+XRlwNjnsNLVwnMWYH7tn/kYPsNPh+JSOMlnt93mYNCKHYzylRhI51X+TbR+ufNhhKKzm6QkqX8ag==, + } + engines: { node: ">= 20" } + cpu: [arm64] + os: [linux] - '@types/babel__core@7.20.5': - resolution: {integrity: sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==} + "@tailwindcss/oxide-linux-x64-gnu@4.2.2": + resolution: + { + integrity: sha512-rTAGAkDgqbXHNp/xW0iugLVmX62wOp2PoE39BTCGKjv3Iocf6AFbRP/wZT/kuCxC9QBh9Pu8XPkv/zCZB2mcMg==, + } + engines: { node: ">= 20" } + cpu: [x64] + os: [linux] - '@types/babel__generator@7.27.0': - resolution: {integrity: sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==} + "@tailwindcss/oxide-linux-x64-musl@4.2.2": + resolution: + { + integrity: sha512-XW3t3qwbIwiSyRCggeO2zxe3KWaEbM0/kW9e8+0XpBgyKU4ATYzcVSMKteZJ1iukJ3HgHBjbg9P5YPRCVUxlnQ==, + } + engines: { node: ">= 20" } + cpu: [x64] + os: [linux] - '@types/babel__template@7.4.4': - resolution: {integrity: sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==} + "@tailwindcss/oxide-wasm32-wasi@4.2.2": + resolution: + { + integrity: sha512-eKSztKsmEsn1O5lJ4ZAfyn41NfG7vzCg496YiGtMDV86jz1q/irhms5O0VrY6ZwTUkFy/EKG3RfWgxSI3VbZ8Q==, + } + engines: { node: ">=14.0.0" } + cpu: [wasm32] + bundledDependencies: + - "@napi-rs/wasm-runtime" + - "@emnapi/core" + - "@emnapi/runtime" + - "@tybys/wasm-util" + - "@emnapi/wasi-threads" + - tslib + + "@tailwindcss/oxide-win32-arm64-msvc@4.2.2": + resolution: + { + integrity: sha512-qPmaQM4iKu5mxpsrWZMOZRgZv1tOZpUm+zdhhQP0VhJfyGGO3aUKdbh3gDZc/dPLQwW4eSqWGrrcWNBZWUWaXQ==, + } + engines: { node: ">= 20" } + cpu: [arm64] + os: [win32] - '@types/babel__traverse@7.28.0': - resolution: {integrity: sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==} + "@tailwindcss/oxide-win32-x64-msvc@4.2.2": + resolution: + { + integrity: sha512-1T/37VvI7WyH66b+vqHj/cLwnCxt7Qt3WFu5Q8hk65aOvlwAhs7rAp1VkulBJw/N4tMirXjVnylTR72uI0HGcA==, + } + engines: { node: ">= 20" } + cpu: [x64] + os: [win32] - '@types/estree@1.0.8': - resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} + "@tailwindcss/oxide@4.2.2": + resolution: + { + integrity: sha512-qEUA07+E5kehxYp9BVMpq9E8vnJuBHfJEC0vPC5e7iL/hw7HR61aDKoVoKzrG+QKp56vhNZe4qwkRmMC0zDLvg==, + } + engines: { node: ">= 20" } + + "@tailwindcss/typography@0.5.19": + resolution: + { + integrity: sha512-w31dd8HOx3k9vPtcQh5QHP9GwKcgbMp87j58qi6xgiBnFFtKEAgCWnDw4qUT8aHwkCp8bKvb/KGKWWHedP0AAg==, + } + peerDependencies: + tailwindcss: ">=3.0.0 || insiders || >=4.0.0-alpha.20 || >=4.0.0-beta.1" - '@types/json-schema@7.0.15': - resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} + "@tailwindcss/vite@4.2.2": + resolution: + { + integrity: sha512-mEiF5HO1QqCLXoNEfXVA1Tzo+cYsrqV7w9Juj2wdUFyW07JRenqMG225MvPwr3ZD9N1bFQj46X7r33iHxLUW0w==, + } + peerDependencies: + vite: ^5.2.0 || ^6 || ^7 || ^8 + + "@tanstack/history@1.161.6": + resolution: + { + integrity: sha512-NaOGLRrddszbQj9upGat6HG/4TKvXLvu+osAIgfxPYA+eIvYKv8GKDJOrY2D3/U9MRnKfMWD7bU4jeD4xmqyIg==, + } + engines: { node: ">=20.19" } + + "@tanstack/react-router@1.168.1": + resolution: + { + integrity: sha512-DsQzbfwcr2Xugqs4G8yShUO9hVQ/tbWhIiLNJSxmZZOgaZCB3JP+ngN1EJBYZz+JBQdvyVHfiEsPXy0P1h3yVA==, + } + engines: { node: ">=20.19" } + peerDependencies: + react: ">=18.0.0 || >=19.0.0" + react-dom: ">=18.0.0 || >=19.0.0" + + "@tanstack/react-store@0.9.2": + resolution: + { + integrity: sha512-Vt5usJE5sHG/cMechQfmwvwne6ktGCELe89Lmvoxe3LKRoFrhPa8OCKWs0NliG8HTJElEIj7PLtaBQIcux5pAQ==, + } + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + + "@tanstack/router-core@1.168.1": + resolution: + { + integrity: sha512-RtpshTLZsMOkwW7rI52WFWGZSSfMAyDR1zWP9kVm91UX28gedc+LXih1CTP6TchS+TvxK4q8oW7ApMTvnpiY1w==, + } + engines: { node: ">=20.19" } + hasBin: true - '@types/node@22.19.15': - resolution: {integrity: sha512-F0R/h2+dsy5wJAUe3tAU6oqa2qbWY5TpNfL/RGmo1y38hiyO1w3x2jPtt76wmuaJI4DQnOBu21cNXQ2STIUUWg==} + "@tanstack/store@0.9.2": + resolution: + { + integrity: sha512-K013lUJEFJK2ofFQ/hZKJUmCnpcV00ebLyOyFOWQvyQHUOZp/iYO84BM6aOGiV81JzwbX0APTVmW8YI7yiG5oA==, + } + + "@testing-library/dom@10.4.1": + resolution: + { + integrity: sha512-o4PXJQidqJl82ckFaXUeoAW+XysPLauYI43Abki5hABd853iMhitooc6znOnczgbTYmEP6U6/y1ZyKAIsvMKGg==, + } + engines: { node: ">=18" } + + "@testing-library/jest-dom@6.9.1": + resolution: + { + integrity: sha512-zIcONa+hVtVSSep9UT3jZ5rizo2BsxgyDYU7WFD5eICBE7no3881HGeb/QkGfsJs6JTkY1aQhT7rIPC7e+0nnA==, + } + engines: { node: ">=14", npm: ">=6", yarn: ">=1" } + + "@testing-library/react@16.3.2": + resolution: + { + integrity: sha512-XU5/SytQM+ykqMnAnvB2umaJNIOsLF3PVv//1Ew4CTcpz0/BRyy/af40qqrt7SjKpDdT1saBMc42CUok5gaw+g==, + } + engines: { node: ">=18" } + peerDependencies: + "@testing-library/dom": ^10.0.0 + "@types/react": ^18.0.0 || ^19.0.0 + "@types/react-dom": ^18.0.0 || ^19.0.0 + react: ^18.0.0 || ^19.0.0 + react-dom: ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + "@types/react": + optional: true + "@types/react-dom": + optional: true - '@types/prop-types@15.7.15': - resolution: {integrity: sha512-F6bEyamV9jKGAFBEmlQnesRPGOQqS2+Uwi0Em15xenOxHaf2hv6L8YCVn3rPdPJOiJfPiCnLIRyvwVaqMY3MIw==} + "@tybys/wasm-util@0.10.1": + resolution: + { + integrity: sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==, + } + + "@types/aria-query@5.0.4": + resolution: + { + integrity: sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==, + } + + "@types/babel__core@7.20.5": + resolution: + { + integrity: sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==, + } + + "@types/babel__generator@7.27.0": + resolution: + { + integrity: sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==, + } + + "@types/babel__template@7.4.4": + resolution: + { + integrity: sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==, + } + + "@types/babel__traverse@7.28.0": + resolution: + { + integrity: sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==, + } + + "@types/estree@1.0.8": + resolution: + { + integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==, + } + + "@types/json-schema@7.0.15": + resolution: + { + integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==, + } + + "@types/node@22.19.15": + resolution: + { + integrity: sha512-F0R/h2+dsy5wJAUe3tAU6oqa2qbWY5TpNfL/RGmo1y38hiyO1w3x2jPtt76wmuaJI4DQnOBu21cNXQ2STIUUWg==, + } + + "@types/prop-types@15.7.15": + resolution: + { + integrity: sha512-F6bEyamV9jKGAFBEmlQnesRPGOQqS2+Uwi0Em15xenOxHaf2hv6L8YCVn3rPdPJOiJfPiCnLIRyvwVaqMY3MIw==, + } + + "@types/react-dom@18.2.25": + resolution: + { + integrity: sha512-o/V48vf4MQh7juIKZU2QGDfli6p1+OOi5oXx36Hffpc9adsHeXjVp8rHuPkjd8VT8sOJ2Zp05HR7CdpGTIUFUA==, + } + + "@types/react@18.2.79": + resolution: + { + integrity: sha512-RwGAGXPl9kSXwdNTafkOEuFrTBD5SA2B3iEB96xi8+xu5ddUa/cpvyVCSNn+asgLCTHkb5ZxN8gbuibYJi4s1w==, + } + + "@types/semver@7.5.8": + resolution: + { + integrity: sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==, + } + + "@typescript-eslint/eslint-plugin@5.62.0": + resolution: + { + integrity: sha512-TiZzBSJja/LbhNPvk6yc0JrX9XqhQ0hdh6M2svYfsHGejaKFIAGd9MQ+ERIMzLGlN/kZoYIgdxFV0PuljTKXag==, + } + engines: { node: ^12.22.0 || ^14.17.0 || >=16.0.0 } + peerDependencies: + "@typescript-eslint/parser": ^5.0.0 + eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 + typescript: "*" + peerDependenciesMeta: + typescript: + optional: true - '@types/react-dom@18.2.25': - resolution: {integrity: sha512-o/V48vf4MQh7juIKZU2QGDfli6p1+OOi5oXx36Hffpc9adsHeXjVp8rHuPkjd8VT8sOJ2Zp05HR7CdpGTIUFUA==} + "@typescript-eslint/eslint-plugin@8.57.2": + resolution: + { + integrity: sha512-NZZgp0Fm2IkD+La5PR81sd+g+8oS6JwJje+aRWsDocxHkjyRw0J5L5ZTlN3LI1LlOcGL7ph3eaIUmTXMIjLk0w==, + } + engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + peerDependencies: + "@typescript-eslint/parser": ^8.57.2 + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + typescript: ">=4.8.4 <6.0.0" + + "@typescript-eslint/parser@5.62.0": + resolution: + { + integrity: sha512-VlJEV0fOQ7BExOsHYAGrgbEiZoi8D+Bl2+f6V2RrXerRSylnp+ZBHmPvaIa8cz0Ajx7WO7Z5RqfgYg7ED1nRhA==, + } + engines: { node: ^12.22.0 || ^14.17.0 || >=16.0.0 } + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 + typescript: "*" + peerDependenciesMeta: + typescript: + optional: true - '@types/react@18.2.79': - resolution: {integrity: sha512-RwGAGXPl9kSXwdNTafkOEuFrTBD5SA2B3iEB96xi8+xu5ddUa/cpvyVCSNn+asgLCTHkb5ZxN8gbuibYJi4s1w==} + "@typescript-eslint/parser@8.57.2": + resolution: + { + integrity: sha512-30ScMRHIAD33JJQkgfGW1t8CURZtjc2JpTrq5n2HFhOefbAhb7ucc7xJwdWcrEtqUIYJ73Nybpsggii6GtAHjA==, + } + engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + typescript: ">=4.8.4 <6.0.0" + + "@typescript-eslint/project-service@8.57.2": + resolution: + { + integrity: sha512-FuH0wipFywXRTHf+bTTjNyuNQQsQC3qh/dYzaM4I4W0jrCqjCVuUh99+xd9KamUfmCGPvbO8NDngo/vsnNVqgw==, + } + engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + peerDependencies: + typescript: ">=4.8.4 <6.0.0" + + "@typescript-eslint/scope-manager@5.62.0": + resolution: + { + integrity: sha512-VXuvVvZeQCQb5Zgf4HAxc04q5j+WrNAtNh9OwCsCgpKqESMTu3tF/jhZ3xG6T4NZwWl65Bg8KuS2uEvhSfLl0w==, + } + engines: { node: ^12.22.0 || ^14.17.0 || >=16.0.0 } + + "@typescript-eslint/scope-manager@8.57.2": + resolution: + { + integrity: sha512-snZKH+W4WbWkrBqj4gUNRIGb/jipDW3qMqVJ4C9rzdFc+wLwruxk+2a5D+uoFcKPAqyqEnSb4l2ULuZf95eSkw==, + } + engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + + "@typescript-eslint/tsconfig-utils@8.57.2": + resolution: + { + integrity: sha512-3Lm5DSM+DCowsUOJC+YqHHnKEfFh5CoGkj5Z31NQSNF4l5wdOwqGn99wmwN/LImhfY3KJnmordBq/4+VDe2eKw==, + } + engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + peerDependencies: + typescript: ">=4.8.4 <6.0.0" + + "@typescript-eslint/type-utils@5.62.0": + resolution: + { + integrity: sha512-xsSQreu+VnfbqQpW5vnCJdq1Z3Q0U31qiWmRhr98ONQmcp/yhiPJFPq8MXiJVLiksmOKSjIldZzkebzHuCGzew==, + } + engines: { node: ^12.22.0 || ^14.17.0 || >=16.0.0 } + peerDependencies: + eslint: "*" + typescript: "*" + peerDependenciesMeta: + typescript: + optional: true - '@types/semver@7.5.8': - resolution: {integrity: sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==} + "@typescript-eslint/type-utils@8.57.2": + resolution: + { + integrity: sha512-Co6ZCShm6kIbAM/s+oYVpKFfW7LBc6FXoPXjTRQ449PPNBY8U0KZXuevz5IFuuUj2H9ss40atTaf9dlGLzbWZg==, + } + engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + typescript: ">=4.8.4 <6.0.0" + + "@typescript-eslint/types@5.62.0": + resolution: + { + integrity: sha512-87NVngcbVXUahrRTqIK27gD2t5Cu1yuCXxbLcFtCzZGlfyVWWh8mLHkoxzjsB6DDNnvdL+fW8MiwPEJyGJQDgQ==, + } + engines: { node: ^12.22.0 || ^14.17.0 || >=16.0.0 } + + "@typescript-eslint/types@8.57.2": + resolution: + { + integrity: sha512-/iZM6FnM4tnx9csuTxspMW4BOSegshwX5oBDznJ7S4WggL7Vczz5d2W11ecc4vRrQMQHXRSxzrCsyG5EsPPTbA==, + } + engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + + "@typescript-eslint/typescript-estree@5.62.0": + resolution: + { + integrity: sha512-CmcQ6uY7b9y694lKdRB8FEel7JbU/40iSAPomu++SjLMntB+2Leay2LO6i8VnJk58MtE9/nQSFIH6jpyRWyYzA==, + } + engines: { node: ^12.22.0 || ^14.17.0 || >=16.0.0 } + peerDependencies: + typescript: "*" + peerDependenciesMeta: + typescript: + optional: true - '@vitejs/plugin-react@4.2.1': - resolution: {integrity: sha512-oojO9IDc4nCUUi8qIR11KoQm0XFFLIwsRBwHRR4d/88IWghn1y6ckz/bJ8GHDCsYEJee8mDzqtJxh15/cisJNQ==} - engines: {node: ^14.18.0 || >=16.0.0} + "@typescript-eslint/typescript-estree@8.57.2": + resolution: + { + integrity: sha512-2MKM+I6g8tJxfSmFKOnHv2t8Sk3T6rF20A1Puk0svLK+uVapDZB/4pfAeB7nE83uAZrU6OxW+HmOd5wHVdXwXA==, + } + engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + peerDependencies: + typescript: ">=4.8.4 <6.0.0" + + "@typescript-eslint/utils@5.62.0": + resolution: + { + integrity: sha512-n8oxjeb5aIbPFEtmQxQYOLI0i9n5ySBEY/ZEHHZqKQSFnxio1rv6dthascc9dLuwrL0RC5mPCxB7vnAVGAYWAQ==, + } + engines: { node: ^12.22.0 || ^14.17.0 || >=16.0.0 } + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 + + "@typescript-eslint/utils@8.57.2": + resolution: + { + integrity: sha512-krRIbvPK1ju1WBKIefiX+bngPs+odIQUtR7kymzPfo1POVw3jlF+nLkmexdSSd4UCbDcQn+wMBATOOmpBbqgKg==, + } + engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + typescript: ">=4.8.4 <6.0.0" + + "@typescript-eslint/visitor-keys@5.62.0": + resolution: + { + integrity: sha512-07ny+LHRzQXepkGg6w0mFY41fVUNBrL2Roj/++7V1txKugfjm/Ci/qSND03r2RhlJhJYMcTn9AhhSSqQp0Ysyw==, + } + engines: { node: ^12.22.0 || ^14.17.0 || >=16.0.0 } + + "@typescript-eslint/visitor-keys@8.57.2": + resolution: + { + integrity: sha512-zhahknjobV2FiD6Ee9iLbS7OV9zi10rG26odsQdfBO/hjSzUQbkIYgda+iNKK1zNiW2ey+Lf8MU5btN17V3dUw==, + } + engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + + "@ungap/structured-clone@1.3.0": + resolution: + { + integrity: sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==, + } + + "@vitejs/plugin-react@4.2.1": + resolution: + { + integrity: sha512-oojO9IDc4nCUUi8qIR11KoQm0XFFLIwsRBwHRR4d/88IWghn1y6ckz/bJ8GHDCsYEJee8mDzqtJxh15/cisJNQ==, + } + engines: { node: ^14.18.0 || >=16.0.0 } peerDependencies: vite: ^4.2.0 || ^5.0.0 - '@vitest/expect@2.1.9': - resolution: {integrity: sha512-UJCIkTBenHeKT1TTlKMJWy1laZewsRIzYighyYiJKZreqtdxSos/S1t+ktRMQWu2CKqaarrkeszJx1cgC5tGZw==} - - '@vitest/mocker@2.1.9': - resolution: {integrity: sha512-tVL6uJgoUdi6icpxmdrn5YNo3g3Dxv+IHJBr0GXHaEdTcw3F+cPKnsXFhli6nO+f/6SDKPHEK1UN+k+TQv0Ehg==} + "@vitest/expect@2.1.9": + resolution: + { + integrity: sha512-UJCIkTBenHeKT1TTlKMJWy1laZewsRIzYighyYiJKZreqtdxSos/S1t+ktRMQWu2CKqaarrkeszJx1cgC5tGZw==, + } + + "@vitest/mocker@2.1.9": + resolution: + { + integrity: sha512-tVL6uJgoUdi6icpxmdrn5YNo3g3Dxv+IHJBr0GXHaEdTcw3F+cPKnsXFhli6nO+f/6SDKPHEK1UN+k+TQv0Ehg==, + } peerDependencies: msw: ^2.4.9 vite: ^5.0.0 @@ -756,40 +2123,86 @@ packages: vite: optional: true - '@vitest/pretty-format@2.1.9': - resolution: {integrity: sha512-KhRIdGV2U9HOUzxfiHmY8IFHTdqtOhIzCpd8WRdJiE7D/HUcZVD0EgQCVjm+Q9gkUXWgBvMmTtZgIG48wq7sOQ==} - - '@vitest/runner@2.1.9': - resolution: {integrity: sha512-ZXSSqTFIrzduD63btIfEyOmNcBmQvgOVsPNPe0jYtESiXkhd8u2erDLnMxmGrDCwHCCHE7hxwRDCT3pt0esT4g==} - - '@vitest/snapshot@2.1.9': - resolution: {integrity: sha512-oBO82rEjsxLNJincVhLhaxxZdEtV0EFHMK5Kmx5sJ6H9L183dHECjiefOAdnqpIgT5eZwT04PoggUnW88vOBNQ==} - - '@vitest/spy@2.1.9': - resolution: {integrity: sha512-E1B35FwzXXTs9FHNK6bDszs7mtydNi5MIfUWpceJ8Xbfb1gBMscAnwLbEu+B44ed6W3XjL9/ehLPHR1fkf1KLQ==} - - '@vitest/ui@2.1.9': - resolution: {integrity: sha512-izzd2zmnk8Nl5ECYkW27328RbQ1nKvkm6Bb5DAaz1Gk59EbLkiCMa6OLT0NoaAYTjOFS6N+SMYW1nh4/9ljPiw==} + "@vitest/pretty-format@2.1.9": + resolution: + { + integrity: sha512-KhRIdGV2U9HOUzxfiHmY8IFHTdqtOhIzCpd8WRdJiE7D/HUcZVD0EgQCVjm+Q9gkUXWgBvMmTtZgIG48wq7sOQ==, + } + + "@vitest/runner@2.1.9": + resolution: + { + integrity: sha512-ZXSSqTFIrzduD63btIfEyOmNcBmQvgOVsPNPe0jYtESiXkhd8u2erDLnMxmGrDCwHCCHE7hxwRDCT3pt0esT4g==, + } + + "@vitest/snapshot@2.1.9": + resolution: + { + integrity: sha512-oBO82rEjsxLNJincVhLhaxxZdEtV0EFHMK5Kmx5sJ6H9L183dHECjiefOAdnqpIgT5eZwT04PoggUnW88vOBNQ==, + } + + "@vitest/spy@2.1.9": + resolution: + { + integrity: sha512-E1B35FwzXXTs9FHNK6bDszs7mtydNi5MIfUWpceJ8Xbfb1gBMscAnwLbEu+B44ed6W3XjL9/ehLPHR1fkf1KLQ==, + } + + "@vitest/ui@2.1.9": + resolution: + { + integrity: sha512-izzd2zmnk8Nl5ECYkW27328RbQ1nKvkm6Bb5DAaz1Gk59EbLkiCMa6OLT0NoaAYTjOFS6N+SMYW1nh4/9ljPiw==, + } peerDependencies: vitest: 2.1.9 - '@vitest/utils@2.1.9': - resolution: {integrity: sha512-v0psaMSkNJ3A2NMrUEHFRzJtDPFn+/VWZ5WxImB21T9fjucJRmS7xCS3ppEnARb9y11OAzaD+P2Ps+b+BGX5iQ==} + "@vitest/utils@2.1.9": + resolution: + { + integrity: sha512-v0psaMSkNJ3A2NMrUEHFRzJtDPFn+/VWZ5WxImB21T9fjucJRmS7xCS3ppEnARb9y11OAzaD+P2Ps+b+BGX5iQ==, + } accepts@1.3.8: - resolution: {integrity: sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==} - engines: {node: '>= 0.6'} + resolution: + { + integrity: sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==, + } + engines: { node: ">= 0.6" } + + acorn-jsx@5.3.2: + resolution: + { + integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==, + } + peerDependencies: + acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 + + acorn@8.16.0: + resolution: + { + integrity: sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==, + } + engines: { node: ">=0.4.0" } + hasBin: true adm-zip@0.5.16: - resolution: {integrity: sha512-TGw5yVi4saajsSEgz25grObGHEUaDrniwvA2qwSC060KfqGPdglhvPMA2lPIoxs3PQIItj2iag35fONcQqgUaQ==} - engines: {node: '>=12.0'} + resolution: + { + integrity: sha512-TGw5yVi4saajsSEgz25grObGHEUaDrniwvA2qwSC060KfqGPdglhvPMA2lPIoxs3PQIItj2iag35fONcQqgUaQ==, + } + engines: { node: ">=12.0" } agent-base@7.1.4: - resolution: {integrity: sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==} - engines: {node: '>= 14'} + resolution: + { + integrity: sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==, + } + engines: { node: ">= 14" } ajv-formats@2.1.1: - resolution: {integrity: sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==} + resolution: + { + integrity: sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==, + } peerDependencies: ajv: ^8.0.0 peerDependenciesMeta: @@ -797,263 +2210,748 @@ packages: optional: true ajv-keywords@5.1.0: - resolution: {integrity: sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==} + resolution: + { + integrity: sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==, + } peerDependencies: ajv: ^8.8.2 + ajv@6.14.0: + resolution: + { + integrity: sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw==, + } + ajv@8.18.0: - resolution: {integrity: sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==} + resolution: + { + integrity: sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==, + } ansi-colors@4.1.3: - resolution: {integrity: sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==} - engines: {node: '>=6'} + resolution: + { + integrity: sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==, + } + engines: { node: ">=6" } ansi-regex@5.0.1: - resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} - engines: {node: '>=8'} + resolution: + { + integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==, + } + engines: { node: ">=8" } ansi-styles@4.3.0: - resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} - engines: {node: '>=8'} + resolution: + { + integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==, + } + engines: { node: ">=8" } ansi-styles@5.2.0: - resolution: {integrity: sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==} - engines: {node: '>=10'} + resolution: + { + integrity: sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==, + } + engines: { node: ">=10" } + + argparse@2.0.1: + resolution: + { + integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==, + } + + aria-hidden@1.2.6: + resolution: + { + integrity: sha512-ik3ZgC9dY/lYVVM++OISsaYDeg1tb0VtP5uL3ouh1koGOaUMDPpbFIei4JkFimWUFPn90sbMNMXQAIVOlnYKJA==, + } + engines: { node: ">=10" } aria-query@5.3.0: - resolution: {integrity: sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==} + resolution: + { + integrity: sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==, + } aria-query@5.3.2: - resolution: {integrity: sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==} - engines: {node: '>= 0.4'} + resolution: + { + integrity: sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==, + } + engines: { node: ">= 0.4" } + + array-union@2.1.0: + resolution: + { + integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==, + } + engines: { node: ">=8" } assertion-error@2.0.1: - resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==} - engines: {node: '>=12'} + resolution: + { + integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==, + } + engines: { node: ">=12" } asynckit@0.4.0: - resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} + resolution: + { + integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==, + } at-least-node@1.0.0: - resolution: {integrity: sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==} - engines: {node: '>= 4.0.0'} + resolution: + { + integrity: sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==, + } + engines: { node: ">= 4.0.0" } axios@1.13.6: - resolution: {integrity: sha512-ChTCHMouEe2kn713WHbQGcuYrr6fXTBiu460OTwWrWob16g1bXn4vtz07Ope7ewMozJAnEquLk5lWQWtBig9DQ==} + resolution: + { + integrity: sha512-ChTCHMouEe2kn713WHbQGcuYrr6fXTBiu460OTwWrWob16g1bXn4vtz07Ope7ewMozJAnEquLk5lWQWtBig9DQ==, + } + + balanced-match@1.0.2: + resolution: + { + integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==, + } + + balanced-match@4.0.4: + resolution: + { + integrity: sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==, + } + engines: { node: 18 || 20 || >=22 } baseline-browser-mapping@2.10.8: - resolution: {integrity: sha512-PCLz/LXGBsNTErbtB6i5u4eLpHeMfi93aUv5duMmj6caNu6IphS4q6UevDnL36sZQv9lrP11dbPKGMaXPwMKfQ==} - engines: {node: '>=6.0.0'} + resolution: + { + integrity: sha512-PCLz/LXGBsNTErbtB6i5u4eLpHeMfi93aUv5duMmj6caNu6IphS4q6UevDnL36sZQv9lrP11dbPKGMaXPwMKfQ==, + } + engines: { node: ">=6.0.0" } hasBin: true + brace-expansion@1.1.12: + resolution: + { + integrity: sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==, + } + + brace-expansion@5.0.4: + resolution: + { + integrity: sha512-h+DEnpVvxmfVefa4jFbCf5HdH5YMDXRsmKflpf1pILZWRFlTbJpxeU55nJl4Smt5HQaGzg1o6RHFPJaOqnmBDg==, + } + engines: { node: 18 || 20 || >=22 } + + braces@3.0.3: + resolution: + { + integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==, + } + engines: { node: ">=8" } + browserslist@4.28.1: - resolution: {integrity: sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==} - engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} + resolution: + { + integrity: sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==, + } + engines: { node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7 } hasBin: true btoa@1.2.1: - resolution: {integrity: sha512-SB4/MIGlsiVkMcHmT+pSmIPoNDoHg+7cMzmt3Uxt628MTz2487DKSqK/fuhFBrkuqrYv5UCEnACpF4dTFNKc/g==} - engines: {node: '>= 0.4.0'} + resolution: + { + integrity: sha512-SB4/MIGlsiVkMcHmT+pSmIPoNDoHg+7cMzmt3Uxt628MTz2487DKSqK/fuhFBrkuqrYv5UCEnACpF4dTFNKc/g==, + } + engines: { node: ">= 0.4.0" } hasBin: true cac@6.7.14: - resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==} - engines: {node: '>=8'} + resolution: + { + integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==, + } + engines: { node: ">=8" } call-bind-apply-helpers@1.0.2: - resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==} - engines: {node: '>= 0.4'} + resolution: + { + integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==, + } + engines: { node: ">= 0.4" } + + callsites@3.1.0: + resolution: + { + integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==, + } + engines: { node: ">=6" } caniuse-lite@1.0.30001780: - resolution: {integrity: sha512-llngX0E7nQci5BPJDqoZSbuZ5Bcs9F5db7EtgfwBerX9XGtkkiO4NwfDDIRzHTTwcYC8vC7bmeUEPGrKlR/TkQ==} + resolution: + { + integrity: sha512-llngX0E7nQci5BPJDqoZSbuZ5Bcs9F5db7EtgfwBerX9XGtkkiO4NwfDDIRzHTTwcYC8vC7bmeUEPGrKlR/TkQ==, + } chai@5.3.3: - resolution: {integrity: sha512-4zNhdJD/iOjSH0A05ea+Ke6MU5mmpQcbQsSOkgdaUMJ9zTlDTD/GYlwohmIE2u0gaxHYiVHEn1Fw9mZ/ktJWgw==} - engines: {node: '>=18'} + resolution: + { + integrity: sha512-4zNhdJD/iOjSH0A05ea+Ke6MU5mmpQcbQsSOkgdaUMJ9zTlDTD/GYlwohmIE2u0gaxHYiVHEn1Fw9mZ/ktJWgw==, + } + engines: { node: ">=18" } chalk@3.0.0: - resolution: {integrity: sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==} - engines: {node: '>=8'} + resolution: + { + integrity: sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==, + } + engines: { node: ">=8" } + + chalk@4.1.2: + resolution: + { + integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==, + } + engines: { node: ">=10" } check-error@2.1.3: - resolution: {integrity: sha512-PAJdDJusoxnwm1VwW07VWwUN1sl7smmC3OKggvndJFadxxDRyFJBX/ggnu/KE4kQAB7a3Dp8f/YXC1FlUprWmA==} - engines: {node: '>= 16'} + resolution: + { + integrity: sha512-PAJdDJusoxnwm1VwW07VWwUN1sl7smmC3OKggvndJFadxxDRyFJBX/ggnu/KE4kQAB7a3Dp8f/YXC1FlUprWmA==, + } + engines: { node: ">= 16" } + + class-variance-authority@0.7.1: + resolution: + { + integrity: sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg==, + } + + clsx@2.1.1: + resolution: + { + integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==, + } + engines: { node: ">=6" } color-convert@2.0.1: - resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} - engines: {node: '>=7.0.0'} + resolution: + { + integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==, + } + engines: { node: ">=7.0.0" } color-name@1.1.4: - resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + resolution: + { + integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==, + } combined-stream@1.0.8: - resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} - engines: {node: '>= 0.8'} + resolution: + { + integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==, + } + engines: { node: ">= 0.8" } commander@11.1.0: - resolution: {integrity: sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ==} - engines: {node: '>=16'} + resolution: + { + integrity: sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ==, + } + engines: { node: ">=16" } + + concat-map@0.0.1: + resolution: + { + integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==, + } content-disposition@0.5.4: - resolution: {integrity: sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==} - engines: {node: '>= 0.6'} + resolution: + { + integrity: sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==, + } + engines: { node: ">= 0.6" } content-type@1.0.5: - resolution: {integrity: sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==} - engines: {node: '>= 0.6'} + resolution: + { + integrity: sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==, + } + engines: { node: ">= 0.6" } convert-source-map@2.0.0: - resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} + resolution: + { + integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==, + } + + cookie-es@2.0.0: + resolution: + { + integrity: sha512-RAj4E421UYRgqokKUmotqAwuplYw15qtdXfY+hGzgCJ/MBjCVZcSoHK/kH9kocfjRjcDME7IiDWR/1WX1TM2Pg==, + } cookies@0.9.1: - resolution: {integrity: sha512-TG2hpqe4ELx54QER/S3HQ9SRVnQnGBtKUz5bLQWtYAQ+o6GpgMs6sYUvaiJjVxb+UXwhRhAEP3m7LbsIZ77Hmw==} - engines: {node: '>= 0.8'} + resolution: + { + integrity: sha512-TG2hpqe4ELx54QER/S3HQ9SRVnQnGBtKUz5bLQWtYAQ+o6GpgMs6sYUvaiJjVxb+UXwhRhAEP3m7LbsIZ77Hmw==, + } + engines: { node: ">= 0.8" } cron-parser@4.9.0: - resolution: {integrity: sha512-p0SaNjrHOnQeR8/VnfGbmg9te2kfyYSQ7Sc/j/6DtPL3JQvKxmjO9TSjNFpujqV3vEYYBvNNvXSxzyksBWAx1Q==} - engines: {node: '>=12.0.0'} + resolution: + { + integrity: sha512-p0SaNjrHOnQeR8/VnfGbmg9te2kfyYSQ7Sc/j/6DtPL3JQvKxmjO9TSjNFpujqV3vEYYBvNNvXSxzyksBWAx1Q==, + } + engines: { node: ">=12.0.0" } + + cross-spawn@7.0.6: + resolution: + { + integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==, + } + engines: { node: ">= 8" } css.escape@1.5.1: - resolution: {integrity: sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg==} + resolution: + { + integrity: sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg==, + } + + cssesc@3.0.0: + resolution: + { + integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==, + } + engines: { node: ">=4" } + hasBin: true cssstyle@4.6.0: - resolution: {integrity: sha512-2z+rWdzbbSZv6/rhtvzvqeZQHrBaqgogqt85sqFNbabZOuFbCVFb8kPeEtZjiKkbrm395irpNKiYeFeLiQnFPg==} - engines: {node: '>=18'} + resolution: + { + integrity: sha512-2z+rWdzbbSZv6/rhtvzvqeZQHrBaqgogqt85sqFNbabZOuFbCVFb8kPeEtZjiKkbrm395irpNKiYeFeLiQnFPg==, + } + engines: { node: ">=18" } csstype@3.2.3: - resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==} + resolution: + { + integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==, + } data-urls@5.0.0: - resolution: {integrity: sha512-ZYP5VBHshaDAiVZxjbRVcFJpc+4xGgT0bK3vzy1HLN8jTO975HEbuYzZJcHoQEY5K1a0z8YayJkyVETa08eNTg==} - engines: {node: '>=18'} + resolution: + { + integrity: sha512-ZYP5VBHshaDAiVZxjbRVcFJpc+4xGgT0bK3vzy1HLN8jTO975HEbuYzZJcHoQEY5K1a0z8YayJkyVETa08eNTg==, + } + engines: { node: ">=18" } date-format@4.0.14: - resolution: {integrity: sha512-39BOQLs9ZjKh0/patS9nrT8wc3ioX3/eA/zgbKNopnF2wCqJEoxywwwElATYvRsXdnOxA/OQeQoFZ3rFjVajhg==} - engines: {node: '>=4.0'} + resolution: + { + integrity: sha512-39BOQLs9ZjKh0/patS9nrT8wc3ioX3/eA/zgbKNopnF2wCqJEoxywwwElATYvRsXdnOxA/OQeQoFZ3rFjVajhg==, + } + engines: { node: ">=4.0" } debug@4.4.3: - resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==} - engines: {node: '>=6.0'} + resolution: + { + integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==, + } + engines: { node: ">=6.0" } peerDependencies: - supports-color: '*' + supports-color: "*" peerDependenciesMeta: supports-color: optional: true decimal.js@10.6.0: - resolution: {integrity: sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==} + resolution: + { + integrity: sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==, + } deep-eql@5.0.2: - resolution: {integrity: sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==} - engines: {node: '>=6'} + resolution: + { + integrity: sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==, + } + engines: { node: ">=6" } deep-equal@1.0.1: - resolution: {integrity: sha512-bHtC0iYvWhyaTzvV3CZgPeZQqCOBGyGsVV7v4eevpdkLHfiSrXUdBG+qAuSz4RI70sszvjQ1QSZ98An1yNwpSw==} + resolution: + { + integrity: sha512-bHtC0iYvWhyaTzvV3CZgPeZQqCOBGyGsVV7v4eevpdkLHfiSrXUdBG+qAuSz4RI70sszvjQ1QSZ98An1yNwpSw==, + } + + deep-is@0.1.4: + resolution: + { + integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==, + } defu@6.1.4: - resolution: {integrity: sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg==} + resolution: + { + integrity: sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg==, + } delayed-stream@1.0.0: - resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} - engines: {node: '>=0.4.0'} + resolution: + { + integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==, + } + engines: { node: ">=0.4.0" } delegates@1.0.0: - resolution: {integrity: sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==} + resolution: + { + integrity: sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==, + } depd@1.1.2: - resolution: {integrity: sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==} - engines: {node: '>= 0.6'} + resolution: + { + integrity: sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==, + } + engines: { node: ">= 0.6" } depd@2.0.0: - resolution: {integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==} - engines: {node: '>= 0.8'} + resolution: + { + integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==, + } + engines: { node: ">= 0.8" } dequal@2.0.3: - resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==} - engines: {node: '>=6'} + resolution: + { + integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==, + } + engines: { node: ">=6" } destroy@1.2.0: - resolution: {integrity: sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==} - engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16} + resolution: + { + integrity: sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==, + } + engines: { node: ">= 0.8", npm: 1.2.8000 || >= 1.4.16 } + + detect-libc@2.1.2: + resolution: + { + integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==, + } + engines: { node: ">=8" } + + detect-node-es@1.1.0: + resolution: + { + integrity: sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==, + } + + dir-glob@3.0.1: + resolution: + { + integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==, + } + engines: { node: ">=8" } + + doctrine@3.0.0: + resolution: + { + integrity: sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==, + } + engines: { node: ">=6.0.0" } dom-accessibility-api@0.5.16: - resolution: {integrity: sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==} + resolution: + { + integrity: sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==, + } dom-accessibility-api@0.6.3: - resolution: {integrity: sha512-7ZgogeTnjuHbo+ct10G9Ffp0mif17idi0IyWNVA/wcwcm7NPOD/WEHVP3n7n3MhXqxoIYm8d6MuZohYWIZ4T3w==} + resolution: + { + integrity: sha512-7ZgogeTnjuHbo+ct10G9Ffp0mif17idi0IyWNVA/wcwcm7NPOD/WEHVP3n7n3MhXqxoIYm8d6MuZohYWIZ4T3w==, + } dunder-proto@1.0.1: - resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} - engines: {node: '>= 0.4'} + resolution: + { + integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==, + } + engines: { node: ">= 0.4" } ee-first@1.1.1: - resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==} + resolution: + { + integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==, + } electron-to-chromium@1.5.321: - resolution: {integrity: sha512-L2C7Q279W2D/J4PLZLk7sebOILDSWos7bMsMNN06rK482umHUrh/3lM8G7IlHFOYip2oAg5nha1rCMxr/rs6ZQ==} + resolution: + { + integrity: sha512-L2C7Q279W2D/J4PLZLk7sebOILDSWos7bMsMNN06rK482umHUrh/3lM8G7IlHFOYip2oAg5nha1rCMxr/rs6ZQ==, + } encodeurl@2.0.0: - resolution: {integrity: sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==} - engines: {node: '>= 0.8'} + resolution: + { + integrity: sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==, + } + engines: { node: ">= 0.8" } + + enhanced-resolve@5.20.1: + resolution: + { + integrity: sha512-Qohcme7V1inbAfvjItgw0EaxVX5q2rdVEZHRBrEQdRZTssLDGsL8Lwrznl8oQ/6kuTJONLaDcGjkNP247XEhcA==, + } + engines: { node: ">=10.13.0" } entities@6.0.1: - resolution: {integrity: sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==} - engines: {node: '>=0.12'} + resolution: + { + integrity: sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==, + } + engines: { node: ">=0.12" } es-define-property@1.0.1: - resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==} - engines: {node: '>= 0.4'} + resolution: + { + integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==, + } + engines: { node: ">= 0.4" } es-errors@1.3.0: - resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} - engines: {node: '>= 0.4'} + resolution: + { + integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==, + } + engines: { node: ">= 0.4" } es-module-lexer@1.7.0: - resolution: {integrity: sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==} + resolution: + { + integrity: sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==, + } es-object-atoms@1.1.1: - resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==} - engines: {node: '>= 0.4'} + resolution: + { + integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==, + } + engines: { node: ">= 0.4" } es-set-tostringtag@2.1.0: - resolution: {integrity: sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==} - engines: {node: '>= 0.4'} + resolution: + { + integrity: sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==, + } + engines: { node: ">= 0.4" } esbuild@0.20.2: - resolution: {integrity: sha512-WdOOppmUNU+IbZ0PaDiTst80zjnrOkyJNHoKupIcVyU8Lvla3Ugx94VzkQ32Ijqd7UhHJy75gNWDMUekcrSJ6g==} - engines: {node: '>=12'} + resolution: + { + integrity: sha512-WdOOppmUNU+IbZ0PaDiTst80zjnrOkyJNHoKupIcVyU8Lvla3Ugx94VzkQ32Ijqd7UhHJy75gNWDMUekcrSJ6g==, + } + engines: { node: ">=12" } hasBin: true escalade@3.2.0: - resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} - engines: {node: '>=6'} + resolution: + { + integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==, + } + engines: { node: ">=6" } escape-html@1.0.3: - resolution: {integrity: sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==} + resolution: + { + integrity: sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==, + } + + escape-string-regexp@4.0.0: + resolution: + { + integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==, + } + engines: { node: ">=10" } + + eslint-plugin-react-hooks@4.6.2: + resolution: + { + integrity: sha512-QzliNJq4GinDBcD8gPB5v0wh6g8q3SUi6EFF0x8N/BL9PoVs0atuGc47ozMRyOWAKdwaZ5OnbOEa3WR+dSGKuQ==, + } + engines: { node: ">=10" } + peerDependencies: + eslint: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 + + eslint-plugin-react-refresh@0.4.26: + resolution: + { + integrity: sha512-1RETEylht2O6FM/MvgnyvT+8K21wLqDNg4qD51Zj3guhjt433XbnnkVttHMyaVyAFD03QSV4LPS5iE3VQmO7XQ==, + } + peerDependencies: + eslint: ">=8.40" + + eslint-scope@5.1.1: + resolution: + { + integrity: sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==, + } + engines: { node: ">=8.0.0" } + + eslint-scope@7.2.2: + resolution: + { + integrity: sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==, + } + engines: { node: ^12.22.0 || ^14.17.0 || >=16.0.0 } + + eslint-visitor-keys@3.4.3: + resolution: + { + integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==, + } + engines: { node: ^12.22.0 || ^14.17.0 || >=16.0.0 } + + eslint-visitor-keys@5.0.1: + resolution: + { + integrity: sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==, + } + engines: { node: ^20.19.0 || ^22.13.0 || >=24 } + + eslint@8.57.1: + resolution: + { + integrity: sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA==, + } + engines: { node: ^12.22.0 || ^14.17.0 || >=16.0.0 } + deprecated: This version is no longer supported. Please see https://eslint.org/version-support for other options. + hasBin: true + + espree@9.6.1: + resolution: + { + integrity: sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==, + } + engines: { node: ^12.22.0 || ^14.17.0 || >=16.0.0 } + + esquery@1.7.0: + resolution: + { + integrity: sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==, + } + engines: { node: ">=0.10" } + + esrecurse@4.3.0: + resolution: + { + integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==, + } + engines: { node: ">=4.0" } + + estraverse@4.3.0: + resolution: + { + integrity: sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==, + } + engines: { node: ">=4.0" } + + estraverse@5.3.0: + resolution: + { + integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==, + } + engines: { node: ">=4.0" } estree-walker@2.0.2: - resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==} + resolution: + { + integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==, + } estree-walker@3.0.3: - resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==} + resolution: + { + integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==, + } + + esutils@2.0.3: + resolution: + { + integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==, + } + engines: { node: ">=0.10.0" } expand-tilde@2.0.2: - resolution: {integrity: sha512-A5EmesHW6rfnZ9ysHQjPdJRni0SRar0tjtG5MNtm9n5TUvsYU8oozprtRD4AqHxcZWWlVuAmQo2nWKfN9oyjTw==} - engines: {node: '>=0.10.0'} + resolution: + { + integrity: sha512-A5EmesHW6rfnZ9ysHQjPdJRni0SRar0tjtG5MNtm9n5TUvsYU8oozprtRD4AqHxcZWWlVuAmQo2nWKfN9oyjTw==, + } + engines: { node: ">=0.10.0" } expect-type@1.3.0: - resolution: {integrity: sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA==} - engines: {node: '>=12.0.0'} + resolution: + { + integrity: sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA==, + } + engines: { node: ">=12.0.0" } fast-deep-equal@3.1.3: - resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} + resolution: + { + integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==, + } + + fast-glob@3.3.3: + resolution: + { + integrity: sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==, + } + engines: { node: ">=8.6.0" } + + fast-json-stable-stringify@2.1.0: + resolution: + { + integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==, + } + + fast-levenshtein@2.0.6: + resolution: + { + integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==, + } fast-uri@3.1.0: - resolution: {integrity: sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==} + resolution: + { + integrity: sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==, + } + + fastq@1.20.1: + resolution: + { + integrity: sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==, + } fdir@6.5.0: - resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} - engines: {node: '>=12.0.0'} + resolution: + { + integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==, + } + engines: { node: ">=12.0.0" } peerDependencies: picomatch: ^3 || ^4 peerDependenciesMeta: @@ -1061,166 +2959,472 @@ packages: optional: true fflate@0.8.2: - resolution: {integrity: sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==} + resolution: + { + integrity: sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==, + } + + file-entry-cache@6.0.1: + resolution: + { + integrity: sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==, + } + engines: { node: ^10.12.0 || >=12.0.0 } + + fill-range@7.1.1: + resolution: + { + integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==, + } + engines: { node: ">=8" } find-file-up@2.0.1: - resolution: {integrity: sha512-qVdaUhYO39zmh28/JLQM5CoYN9byEOKEH4qfa8K1eNV17W0UUMJ9WgbR/hHFH+t5rcl+6RTb5UC7ck/I+uRkpQ==} - engines: {node: '>=8'} + resolution: + { + integrity: sha512-qVdaUhYO39zmh28/JLQM5CoYN9byEOKEH4qfa8K1eNV17W0UUMJ9WgbR/hHFH+t5rcl+6RTb5UC7ck/I+uRkpQ==, + } + engines: { node: ">=8" } find-pkg@2.0.0: - resolution: {integrity: sha512-WgZ+nKbELDa6N3i/9nrHeNznm+lY3z4YfhDDWgW+5P0pdmMj26bxaxU11ookgY3NyP9GC7HvZ9etp0jRFqGEeQ==} - engines: {node: '>=8'} + resolution: + { + integrity: sha512-WgZ+nKbELDa6N3i/9nrHeNznm+lY3z4YfhDDWgW+5P0pdmMj26bxaxU11ookgY3NyP9GC7HvZ9etp0jRFqGEeQ==, + } + engines: { node: ">=8" } + + find-up@5.0.0: + resolution: + { + integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==, + } + engines: { node: ">=10" } + + flat-cache@3.2.0: + resolution: + { + integrity: sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==, + } + engines: { node: ^10.12.0 || >=12.0.0 } flatted@3.4.2: - resolution: {integrity: sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==} + resolution: + { + integrity: sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==, + } follow-redirects@1.15.11: - resolution: {integrity: sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==} - engines: {node: '>=4.0'} + resolution: + { + integrity: sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==, + } + engines: { node: ">=4.0" } peerDependencies: - debug: '*' + debug: "*" peerDependenciesMeta: debug: optional: true form-data@4.0.5: - resolution: {integrity: sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==} - engines: {node: '>= 6'} + resolution: + { + integrity: sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==, + } + engines: { node: ">= 6" } fresh@0.5.2: - resolution: {integrity: sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==} - engines: {node: '>= 0.6'} + resolution: + { + integrity: sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==, + } + engines: { node: ">= 0.6" } fs-extra@8.1.0: - resolution: {integrity: sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==} - engines: {node: '>=6 <7 || >=8'} + resolution: + { + integrity: sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==, + } + engines: { node: ">=6 <7 || >=8" } fs-extra@9.1.0: - resolution: {integrity: sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==} - engines: {node: '>=10'} + resolution: + { + integrity: sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==, + } + engines: { node: ">=10" } + + fs.realpath@1.0.0: + resolution: + { + integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==, + } fsevents@2.3.3: - resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} - engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + resolution: + { + integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==, + } + engines: { node: ^8.16.0 || ^10.6.0 || >=11.0.0 } os: [darwin] function-bind@1.1.2: - resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} + resolution: + { + integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==, + } gensync@1.0.0-beta.2: - resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} - engines: {node: '>=6.9.0'} + resolution: + { + integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==, + } + engines: { node: ">=6.9.0" } get-intrinsic@1.3.0: - resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==} - engines: {node: '>= 0.4'} + resolution: + { + integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==, + } + engines: { node: ">= 0.4" } + + get-nonce@1.0.1: + resolution: + { + integrity: sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q==, + } + engines: { node: ">=6" } get-proto@1.0.1: - resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==} - engines: {node: '>= 0.4'} + resolution: + { + integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==, + } + engines: { node: ">= 0.4" } + + glob-parent@5.1.2: + resolution: + { + integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==, + } + engines: { node: ">= 6" } + + glob-parent@6.0.2: + resolution: + { + integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==, + } + engines: { node: ">=10.13.0" } + + glob@7.2.3: + resolution: + { + integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==, + } + deprecated: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me global-modules@1.0.0: - resolution: {integrity: sha512-sKzpEkf11GpOFuw0Zzjzmt4B4UZwjOcG757PPvrfhxcLFbq0wpsgpOqxpxtxFiCG4DtG93M6XRVbF2oGdev7bg==} - engines: {node: '>=0.10.0'} + resolution: + { + integrity: sha512-sKzpEkf11GpOFuw0Zzjzmt4B4UZwjOcG757PPvrfhxcLFbq0wpsgpOqxpxtxFiCG4DtG93M6XRVbF2oGdev7bg==, + } + engines: { node: ">=0.10.0" } global-prefix@1.0.2: - resolution: {integrity: sha512-5lsx1NUDHtSjfg0eHlmYvZKv8/nVqX4ckFbM+FrGcQ+04KWcWFo9P5MxPZYSzUvyzmdTbI7Eix8Q4IbELDqzKg==} - engines: {node: '>=0.10.0'} + resolution: + { + integrity: sha512-5lsx1NUDHtSjfg0eHlmYvZKv8/nVqX4ckFbM+FrGcQ+04KWcWFo9P5MxPZYSzUvyzmdTbI7Eix8Q4IbELDqzKg==, + } + engines: { node: ">=0.10.0" } + + globals@13.24.0: + resolution: + { + integrity: sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==, + } + engines: { node: ">=8" } + + globby@11.1.0: + resolution: + { + integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==, + } + engines: { node: ">=10" } gopd@1.2.0: - resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==} - engines: {node: '>= 0.4'} + resolution: + { + integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==, + } + engines: { node: ">= 0.4" } graceful-fs@4.2.11: - resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} + resolution: + { + integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==, + } + + graphemer@1.4.0: + resolution: + { + integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==, + } has-flag@4.0.0: - resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} - engines: {node: '>=8'} + resolution: + { + integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==, + } + engines: { node: ">=8" } has-symbols@1.1.0: - resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==} - engines: {node: '>= 0.4'} + resolution: + { + integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==, + } + engines: { node: ">= 0.4" } has-tostringtag@1.0.2: - resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==} - engines: {node: '>= 0.4'} + resolution: + { + integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==, + } + engines: { node: ">= 0.4" } hasown@2.0.2: - resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} - engines: {node: '>= 0.4'} + resolution: + { + integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==, + } + engines: { node: ">= 0.4" } homedir-polyfill@1.0.3: - resolution: {integrity: sha512-eSmmWE5bZTK2Nou4g0AI3zZ9rswp7GRKoKXS1BLUkvPviOqs4YTN1djQIqrXy9k5gEtdLPy86JjRwsNM9tnDcA==} - engines: {node: '>=0.10.0'} + resolution: + { + integrity: sha512-eSmmWE5bZTK2Nou4g0AI3zZ9rswp7GRKoKXS1BLUkvPviOqs4YTN1djQIqrXy9k5gEtdLPy86JjRwsNM9tnDcA==, + } + engines: { node: ">=0.10.0" } html-encoding-sniffer@4.0.0: - resolution: {integrity: sha512-Y22oTqIU4uuPgEemfz7NDJz6OeKf12Lsu+QC+s3BVpda64lTiMYCyGwg5ki4vFxkMwQdeZDl2adZoqUgdFuTgQ==} - engines: {node: '>=18'} + resolution: + { + integrity: sha512-Y22oTqIU4uuPgEemfz7NDJz6OeKf12Lsu+QC+s3BVpda64lTiMYCyGwg5ki4vFxkMwQdeZDl2adZoqUgdFuTgQ==, + } + engines: { node: ">=18" } + + html-parse-stringify@3.0.1: + resolution: + { + integrity: sha512-KknJ50kTInJ7qIScF3jeaFRpMpE8/lfiTdzf/twXyPBLAGrLRTmkz3AdTnKeh40X8k9L2fdYwEp/42WGXIRGcg==, + } http-assert@1.5.0: - resolution: {integrity: sha512-uPpH7OKX4H25hBmU6G1jWNaqJGpTXxey+YOUizJUAgu0AjLUeC8D73hTrhvDS5D+GJN1DN1+hhc/eF/wpxtp0w==} - engines: {node: '>= 0.8'} + resolution: + { + integrity: sha512-uPpH7OKX4H25hBmU6G1jWNaqJGpTXxey+YOUizJUAgu0AjLUeC8D73hTrhvDS5D+GJN1DN1+hhc/eF/wpxtp0w==, + } + engines: { node: ">= 0.8" } http-errors@1.8.1: - resolution: {integrity: sha512-Kpk9Sm7NmI+RHhnj6OIWDI1d6fIoFAtFt9RLaTMRlg/8w49juAStsrBgp0Dp4OdxdVbRIeKhtCUvoi/RuAhO4g==} - engines: {node: '>= 0.6'} + resolution: + { + integrity: sha512-Kpk9Sm7NmI+RHhnj6OIWDI1d6fIoFAtFt9RLaTMRlg/8w49juAStsrBgp0Dp4OdxdVbRIeKhtCUvoi/RuAhO4g==, + } + engines: { node: ">= 0.6" } http-errors@2.0.1: - resolution: {integrity: sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==} - engines: {node: '>= 0.8'} + resolution: + { + integrity: sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==, + } + engines: { node: ">= 0.8" } http-proxy-agent@7.0.2: - resolution: {integrity: sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==} - engines: {node: '>= 14'} + resolution: + { + integrity: sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==, + } + engines: { node: ">= 14" } https-proxy-agent@7.0.6: - resolution: {integrity: sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==} - engines: {node: '>= 14'} + resolution: + { + integrity: sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==, + } + engines: { node: ">= 14" } + + i18next@25.10.2: + resolution: + { + integrity: sha512-eI/yYvivefa3sX6kRRhIuAHFNWS3NVa5joa0I80tDZ2Kw6uI2Cjhxb9R89A1f06N4DcryQbdGUieMitMnDA+1A==, + } + peerDependencies: + typescript: ^5 + peerDependenciesMeta: + typescript: + optional: true iconv-lite@0.6.3: - resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==} - engines: {node: '>=0.10.0'} + resolution: + { + integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==, + } + engines: { node: ">=0.10.0" } + + ignore@5.3.2: + resolution: + { + integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==, + } + engines: { node: ">= 4" } + + ignore@7.0.5: + resolution: + { + integrity: sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==, + } + engines: { node: ">= 4" } + + import-fresh@3.3.1: + resolution: + { + integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==, + } + engines: { node: ">=6" } + + imurmurhash@0.1.4: + resolution: + { + integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==, + } + engines: { node: ">=0.8.19" } indent-string@4.0.0: - resolution: {integrity: sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==} - engines: {node: '>=8'} + resolution: + { + integrity: sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==, + } + engines: { node: ">=8" } + + inflight@1.0.6: + resolution: + { + integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==, + } + deprecated: This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful. inherits@2.0.4: - resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} + resolution: + { + integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==, + } ini@1.3.8: - resolution: {integrity: sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==} + resolution: + { + integrity: sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==, + } is-core-module@2.16.1: - resolution: {integrity: sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==} - engines: {node: '>= 0.4'} + resolution: + { + integrity: sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==, + } + engines: { node: ">= 0.4" } + + is-extglob@2.1.1: + resolution: + { + integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==, + } + engines: { node: ">=0.10.0" } + + is-glob@4.0.3: + resolution: + { + integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==, + } + engines: { node: ">=0.10.0" } + + is-number@7.0.0: + resolution: + { + integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==, + } + engines: { node: ">=0.12.0" } + + is-path-inside@3.0.3: + resolution: + { + integrity: sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==, + } + engines: { node: ">=8" } is-potential-custom-element-name@1.0.1: - resolution: {integrity: sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==} + resolution: + { + integrity: sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==, + } is-windows@1.0.2: - resolution: {integrity: sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==} - engines: {node: '>=0.10.0'} + resolution: + { + integrity: sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==, + } + engines: { node: ">=0.10.0" } + + isbot@5.1.36: + resolution: + { + integrity: sha512-C/ZtXyJqDPZ7G7JPr06ApWyYoHjYexQbS6hPYD4WYCzpv2Qes6Z+CCEfTX4Owzf+1EJ933PoI2p+B9v7wpGZBQ==, + } + engines: { node: ">=18" } isexe@2.0.0: - resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + resolution: + { + integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==, + } isomorphic-ws@5.0.0: - resolution: {integrity: sha512-muId7Zzn9ywDsyXgTIafTry2sV3nySZeUDe6YedVd1Hvuuep5AsIlqK+XefWpYTyJG5e503F2xIuT2lcU6rCSw==} + resolution: + { + integrity: sha512-muId7Zzn9ywDsyXgTIafTry2sV3nySZeUDe6YedVd1Hvuuep5AsIlqK+XefWpYTyJG5e503F2xIuT2lcU6rCSw==, + } peerDependencies: - ws: '*' + ws: "*" jiti@2.4.2: - resolution: {integrity: sha512-rg9zJN+G4n2nfJl5MW3BMygZX56zKPNVEYYqq7adpmMh4Jn2QNEwhvQlFy6jPVdcod7txZtKHWnyZiA3a0zP7A==} + resolution: + { + integrity: sha512-rg9zJN+G4n2nfJl5MW3BMygZX56zKPNVEYYqq7adpmMh4Jn2QNEwhvQlFy6jPVdcod7txZtKHWnyZiA3a0zP7A==, + } + hasBin: true + + jiti@2.6.1: + resolution: + { + integrity: sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==, + } hasBin: true js-tokens@4.0.0: - resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} + resolution: + { + integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==, + } + + js-yaml@4.1.1: + resolution: + { + integrity: sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==, + } + hasBin: true jsdom@25.0.1: - resolution: {integrity: sha512-8i7LzZj7BF8uplX+ZyOlIz86V6TAsSs+np6m1kpW9u0JWi4z/1t+FzcK1aek+ybTnAC4KhBL4uXCNT0wcUIeCw==} - engines: {node: '>=18'} + resolution: + { + integrity: sha512-8i7LzZj7BF8uplX+ZyOlIz86V6TAsSs+np6m1kpW9u0JWi4z/1t+FzcK1aek+ybTnAC4KhBL4uXCNT0wcUIeCw==, + } + engines: { node: ">=18" } peerDependencies: canvas: ^2.11.2 peerDependenciesMeta: @@ -1228,400 +3432,1326 @@ packages: optional: true jsesc@3.1.0: - resolution: {integrity: sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==} - engines: {node: '>=6'} + resolution: + { + integrity: sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==, + } + engines: { node: ">=6" } hasBin: true + json-buffer@3.0.1: + resolution: + { + integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==, + } + + json-schema-traverse@0.4.1: + resolution: + { + integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==, + } + json-schema-traverse@1.0.0: - resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==} + resolution: + { + integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==, + } + + json-stable-stringify-without-jsonify@1.0.1: + resolution: + { + integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==, + } json5@2.2.3: - resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==} - engines: {node: '>=6'} + resolution: + { + integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==, + } + engines: { node: ">=6" } hasBin: true jsonfile@4.0.0: - resolution: {integrity: sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==} + resolution: + { + integrity: sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==, + } jsonfile@6.2.0: - resolution: {integrity: sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==} + resolution: + { + integrity: sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==, + } keygrip@1.1.0: - resolution: {integrity: sha512-iYSchDJ+liQ8iwbSI2QqsQOvqv58eJCEanyJPJi+Khyu8smkcKSFUCbPwzFcL7YVtZ6eONjqRX/38caJ7QjRAQ==} - engines: {node: '>= 0.6'} + resolution: + { + integrity: sha512-iYSchDJ+liQ8iwbSI2QqsQOvqv58eJCEanyJPJi+Khyu8smkcKSFUCbPwzFcL7YVtZ6eONjqRX/38caJ7QjRAQ==, + } + engines: { node: ">= 0.6" } + + keyv@4.5.4: + resolution: + { + integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==, + } koa-compose@4.1.0: - resolution: {integrity: sha512-8ODW8TrDuMYvXRwra/Kh7/rJo9BtOfPc6qO8eAfC80CnCvSjSl0bkRM24X6/XBBEyj0v1nRUQ1LyOy3dbqOWXw==} + resolution: + { + integrity: sha512-8ODW8TrDuMYvXRwra/Kh7/rJo9BtOfPc6qO8eAfC80CnCvSjSl0bkRM24X6/XBBEyj0v1nRUQ1LyOy3dbqOWXw==, + } koa@3.0.3: - resolution: {integrity: sha512-MeuwbCoN1daWS32/Ni5qkzmrOtQO2qrnfdxDHjrm6s4b59yG4nexAJ0pTEFyzjLp0pBVO80CZp0vW8Ze30Ebow==} - engines: {node: '>= 18'} + resolution: + { + integrity: sha512-MeuwbCoN1daWS32/Ni5qkzmrOtQO2qrnfdxDHjrm6s4b59yG4nexAJ0pTEFyzjLp0pBVO80CZp0vW8Ze30Ebow==, + } + engines: { node: ">= 18" } + + levn@0.4.1: + resolution: + { + integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==, + } + engines: { node: ">= 0.8.0" } + + lightningcss-android-arm64@1.32.0: + resolution: + { + integrity: sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==, + } + engines: { node: ">= 12.0.0" } + cpu: [arm64] + os: [android] + + lightningcss-darwin-arm64@1.32.0: + resolution: + { + integrity: sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==, + } + engines: { node: ">= 12.0.0" } + cpu: [arm64] + os: [darwin] + + lightningcss-darwin-x64@1.32.0: + resolution: + { + integrity: sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==, + } + engines: { node: ">= 12.0.0" } + cpu: [x64] + os: [darwin] + + lightningcss-freebsd-x64@1.32.0: + resolution: + { + integrity: sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==, + } + engines: { node: ">= 12.0.0" } + cpu: [x64] + os: [freebsd] + + lightningcss-linux-arm-gnueabihf@1.32.0: + resolution: + { + integrity: sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==, + } + engines: { node: ">= 12.0.0" } + cpu: [arm] + os: [linux] + + lightningcss-linux-arm64-gnu@1.32.0: + resolution: + { + integrity: sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==, + } + engines: { node: ">= 12.0.0" } + cpu: [arm64] + os: [linux] + + lightningcss-linux-arm64-musl@1.32.0: + resolution: + { + integrity: sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==, + } + engines: { node: ">= 12.0.0" } + cpu: [arm64] + os: [linux] + + lightningcss-linux-x64-gnu@1.32.0: + resolution: + { + integrity: sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==, + } + engines: { node: ">= 12.0.0" } + cpu: [x64] + os: [linux] + + lightningcss-linux-x64-musl@1.32.0: + resolution: + { + integrity: sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==, + } + engines: { node: ">= 12.0.0" } + cpu: [x64] + os: [linux] + + lightningcss-win32-arm64-msvc@1.32.0: + resolution: + { + integrity: sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==, + } + engines: { node: ">= 12.0.0" } + cpu: [arm64] + os: [win32] + + lightningcss-win32-x64-msvc@1.32.0: + resolution: + { + integrity: sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==, + } + engines: { node: ">= 12.0.0" } + cpu: [x64] + os: [win32] + + lightningcss@1.32.0: + resolution: + { + integrity: sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==, + } + engines: { node: ">= 12.0.0" } + + locate-path@6.0.0: + resolution: + { + integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==, + } + engines: { node: ">=10" } lodash.clonedeepwith@4.5.0: - resolution: {integrity: sha512-QRBRSxhbtsX1nc0baxSkkK5WlVTTm/s48DSukcGcWZwIyI8Zz+lB+kFiELJXtzfH4Aj6kMWQ1VWW4U5uUDgZMA==} + resolution: + { + integrity: sha512-QRBRSxhbtsX1nc0baxSkkK5WlVTTm/s48DSukcGcWZwIyI8Zz+lB+kFiELJXtzfH4Aj6kMWQ1VWW4U5uUDgZMA==, + } + + lodash.merge@4.6.2: + resolution: + { + integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==, + } log4js@6.9.1: - resolution: {integrity: sha512-1somDdy9sChrr9/f4UlzhdaGfDR2c/SaD2a4T7qEkG4jTS57/B3qmnjLYePwQ8cqWnUHZI0iAKxMBpCZICiZ2g==} - engines: {node: '>=8.0'} + resolution: + { + integrity: sha512-1somDdy9sChrr9/f4UlzhdaGfDR2c/SaD2a4T7qEkG4jTS57/B3qmnjLYePwQ8cqWnUHZI0iAKxMBpCZICiZ2g==, + } + engines: { node: ">=8.0" } long-timeout@0.1.1: - resolution: {integrity: sha512-BFRuQUqc7x2NWxfJBCyUrN8iYUYznzL9JROmRz1gZ6KlOIgmoD+njPVbb+VNn2nGMKggMsK79iUNErillsrx7w==} + resolution: + { + integrity: sha512-BFRuQUqc7x2NWxfJBCyUrN8iYUYznzL9JROmRz1gZ6KlOIgmoD+njPVbb+VNn2nGMKggMsK79iUNErillsrx7w==, + } loose-envify@1.4.0: - resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==} + resolution: + { + integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==, + } hasBin: true loupe@3.2.1: - resolution: {integrity: sha512-CdzqowRJCeLU72bHvWqwRBBlLcMEtIvGrlvef74kMnV2AolS9Y8xUv1I0U/MNAWMhBlKIoyuEgoJ0t/bbwHbLQ==} + resolution: + { + integrity: sha512-CdzqowRJCeLU72bHvWqwRBBlLcMEtIvGrlvef74kMnV2AolS9Y8xUv1I0U/MNAWMhBlKIoyuEgoJ0t/bbwHbLQ==, + } lru-cache@10.4.3: - resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==} + resolution: + { + integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==, + } lru-cache@5.1.1: - resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} + resolution: + { + integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==, + } + + lucide-react@0.552.0: + resolution: + { + integrity: sha512-g9WCjmfwqbexSnZE+2cl21PCfXOcqnGeWeMTNAOGEfpPbm/ZF4YIq77Z8qWrxbu660EKuLB4nSLggoKnCb+isw==, + } + peerDependencies: + react: ^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0 luxon@3.7.2: - resolution: {integrity: sha512-vtEhXh/gNjI9Yg1u4jX/0YVPMvxzHuGgCm6tC5kZyb08yjGWGnqAjGJvcXbqQR2P3MyMEFnRbpcdFS6PBcLqew==} - engines: {node: '>=12'} + resolution: + { + integrity: sha512-vtEhXh/gNjI9Yg1u4jX/0YVPMvxzHuGgCm6tC5kZyb08yjGWGnqAjGJvcXbqQR2P3MyMEFnRbpcdFS6PBcLqew==, + } + engines: { node: ">=12" } lz-string@1.5.0: - resolution: {integrity: sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==} + resolution: + { + integrity: sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==, + } hasBin: true magic-string@0.30.21: - resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==} + resolution: + { + integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==, + } math-intrinsics@1.1.0: - resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} - engines: {node: '>= 0.4'} + resolution: + { + integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==, + } + engines: { node: ">= 0.4" } media-typer@1.1.0: - resolution: {integrity: sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==} - engines: {node: '>= 0.8'} + resolution: + { + integrity: sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==, + } + engines: { node: ">= 0.8" } + + merge2@1.4.1: + resolution: + { + integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==, + } + engines: { node: ">= 8" } + + micromatch@4.0.8: + resolution: + { + integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==, + } + engines: { node: ">=8.6" } mime-db@1.52.0: - resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} - engines: {node: '>= 0.6'} + resolution: + { + integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==, + } + engines: { node: ">= 0.6" } mime-db@1.54.0: - resolution: {integrity: sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==} - engines: {node: '>= 0.6'} + resolution: + { + integrity: sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==, + } + engines: { node: ">= 0.6" } mime-types@2.1.35: - resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} - engines: {node: '>= 0.6'} + resolution: + { + integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==, + } + engines: { node: ">= 0.6" } mime-types@3.0.2: - resolution: {integrity: sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==} - engines: {node: '>=18'} + resolution: + { + integrity: sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==, + } + engines: { node: ">=18" } min-indent@1.0.1: - resolution: {integrity: sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==} - engines: {node: '>=4'} + resolution: + { + integrity: sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==, + } + engines: { node: ">=4" } + + minimatch@10.2.4: + resolution: + { + integrity: sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg==, + } + engines: { node: 18 || 20 || >=22 } + + minimatch@3.1.5: + resolution: + { + integrity: sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==, + } mrmime@2.0.1: - resolution: {integrity: sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ==} - engines: {node: '>=10'} + resolution: + { + integrity: sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ==, + } + engines: { node: ">=10" } ms@2.1.3: - resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + resolution: + { + integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==, + } nanoid@3.3.11: - resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==} - engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + resolution: + { + integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==, + } + engines: { node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1 } hasBin: true + natural-compare-lite@1.4.0: + resolution: + { + integrity: sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==, + } + + natural-compare@1.4.0: + resolution: + { + integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==, + } + negotiator@0.6.3: - resolution: {integrity: sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==} - engines: {node: '>= 0.6'} + resolution: + { + integrity: sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==, + } + engines: { node: ">= 0.6" } + + next-themes@0.4.6: + resolution: + { + integrity: sha512-pZvgD5L0IEvX5/9GWyHMf3m8BKiVQwsCMHfoFosXtXBMnaS0ZnIJ9ST4b4NqLVKDEm8QBxoNNGNaBv2JNF6XNA==, + } + peerDependencies: + react: ^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc + react-dom: ^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc node-releases@2.0.36: - resolution: {integrity: sha512-TdC8FSgHz8Mwtw9g5L4gR/Sh9XhSP/0DEkQxfEFXOpiul5IiHgHan2VhYYb6agDSfp4KuvltmGApc8HMgUrIkA==} + resolution: + { + integrity: sha512-TdC8FSgHz8Mwtw9g5L4gR/Sh9XhSP/0DEkQxfEFXOpiul5IiHgHan2VhYYb6agDSfp4KuvltmGApc8HMgUrIkA==, + } node-schedule@2.1.1: - resolution: {integrity: sha512-OXdegQq03OmXEjt2hZP33W2YPs/E5BcFQks46+G2gAxs4gHOIVD1u7EqlYLYSKsaIpyKCK9Gbk0ta1/gjRSMRQ==} - engines: {node: '>=6'} + resolution: + { + integrity: sha512-OXdegQq03OmXEjt2hZP33W2YPs/E5BcFQks46+G2gAxs4gHOIVD1u7EqlYLYSKsaIpyKCK9Gbk0ta1/gjRSMRQ==, + } + engines: { node: ">=6" } nwsapi@2.2.23: - resolution: {integrity: sha512-7wfH4sLbt4M0gCDzGE6vzQBo0bfTKjU7Sfpqy/7gs1qBfYz2vEJH6vXcBKpO3+6Yu1telwd0t9HpyOoLEQQbIQ==} + resolution: + { + integrity: sha512-7wfH4sLbt4M0gCDzGE6vzQBo0bfTKjU7Sfpqy/7gs1qBfYz2vEJH6vXcBKpO3+6Yu1telwd0t9HpyOoLEQQbIQ==, + } on-finished@2.4.1: - resolution: {integrity: sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==} - engines: {node: '>= 0.8'} + resolution: + { + integrity: sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==, + } + engines: { node: ">= 0.8" } + + once@1.4.0: + resolution: + { + integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==, + } + + optionator@0.9.4: + resolution: + { + integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==, + } + engines: { node: ">= 0.8.0" } + + p-limit@3.1.0: + resolution: + { + integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==, + } + engines: { node: ">=10" } + + p-locate@5.0.0: + resolution: + { + integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==, + } + engines: { node: ">=10" } + + parent-module@1.0.1: + resolution: + { + integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==, + } + engines: { node: ">=6" } parse-passwd@1.0.0: - resolution: {integrity: sha512-1Y1A//QUXEZK7YKz+rD9WydcE1+EuPr6ZBgKecAB8tmoW6UFv0NREVJe1p+jRxtThkcbbKkfwIbWJe/IeE6m2Q==} - engines: {node: '>=0.10.0'} + resolution: + { + integrity: sha512-1Y1A//QUXEZK7YKz+rD9WydcE1+EuPr6ZBgKecAB8tmoW6UFv0NREVJe1p+jRxtThkcbbKkfwIbWJe/IeE6m2Q==, + } + engines: { node: ">=0.10.0" } parse5@7.3.0: - resolution: {integrity: sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==} + resolution: + { + integrity: sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==, + } parseurl@1.3.3: - resolution: {integrity: sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==} - engines: {node: '>= 0.8'} + resolution: + { + integrity: sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==, + } + engines: { node: ">= 0.8" } + + path-exists@4.0.0: + resolution: + { + integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==, + } + engines: { node: ">=8" } + + path-is-absolute@1.0.1: + resolution: + { + integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==, + } + engines: { node: ">=0.10.0" } + + path-key@3.1.1: + resolution: + { + integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==, + } + engines: { node: ">=8" } path-parse@1.0.7: - resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} + resolution: + { + integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==, + } + + path-type@4.0.0: + resolution: + { + integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==, + } + engines: { node: ">=8" } pathe@1.1.2: - resolution: {integrity: sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==} + resolution: + { + integrity: sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==, + } pathval@2.0.1: - resolution: {integrity: sha512-//nshmD55c46FuFw26xV/xFAaB5HF9Xdap7HJBBnrKdAd6/GxDBaNA1870O79+9ueg61cZLSVc+OaFlfmObYVQ==} - engines: {node: '>= 14.16'} + resolution: + { + integrity: sha512-//nshmD55c46FuFw26xV/xFAaB5HF9Xdap7HJBBnrKdAd6/GxDBaNA1870O79+9ueg61cZLSVc+OaFlfmObYVQ==, + } + engines: { node: ">= 14.16" } picocolors@1.1.1: - resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} + resolution: + { + integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==, + } + + picomatch@2.3.2: + resolution: + { + integrity: sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==, + } + engines: { node: ">=8.6" } picomatch@4.0.3: - resolution: {integrity: sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==} - engines: {node: '>=12'} + resolution: + { + integrity: sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==, + } + engines: { node: ">=12" } + + postcss-selector-parser@6.0.10: + resolution: + { + integrity: sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w==, + } + engines: { node: ">=4" } postcss@8.5.8: - resolution: {integrity: sha512-OW/rX8O/jXnm82Ey1k44pObPtdblfiuWnrd8X7GJ7emImCOstunGbXUpp7HdBrFQX6rJzn3sPT397Wp5aCwCHg==} - engines: {node: ^10 || ^12 || >=14} + resolution: + { + integrity: sha512-OW/rX8O/jXnm82Ey1k44pObPtdblfiuWnrd8X7GJ7emImCOstunGbXUpp7HdBrFQX6rJzn3sPT397Wp5aCwCHg==, + } + engines: { node: ^10 || ^12 || >=14 } + + prelude-ls@1.2.1: + resolution: + { + integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==, + } + engines: { node: ">= 0.8.0" } + + prettier@3.6.2: + resolution: + { + integrity: sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==, + } + engines: { node: ">=14" } + hasBin: true pretty-format@27.5.1: - resolution: {integrity: sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==} - engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} + resolution: + { + integrity: sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==, + } + engines: { node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0 } proxy-from-env@1.1.0: - resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==} + resolution: + { + integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==, + } punycode@2.3.1: - resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} - engines: {node: '>=6'} + resolution: + { + integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==, + } + engines: { node: ">=6" } + + queue-microtask@1.2.3: + resolution: + { + integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==, + } rambda@9.4.2: - resolution: {integrity: sha512-++euMfxnl7OgaEKwXh9QqThOjMeta2HH001N1v4mYQzBjJBnmXBh2BCK6dZAbICFVXOFUVD3xFG0R3ZPU0mxXw==} + resolution: + { + integrity: sha512-++euMfxnl7OgaEKwXh9QqThOjMeta2HH001N1v4mYQzBjJBnmXBh2BCK6dZAbICFVXOFUVD3xFG0R3ZPU0mxXw==, + } react-dom@18.3.1: - resolution: {integrity: sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==} + resolution: + { + integrity: sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==, + } peerDependencies: react: ^18.3.1 + react-i18next@16.6.0: + resolution: + { + integrity: sha512-bxVftl2q/pfupKVmBH80ui1rHl3ia2sdcR0Yhn6cr0PyoHfO8JLZ19fccwOIH+0dMBCIkdO5gAmEQFCTAggeQg==, + } + peerDependencies: + i18next: ">= 25.6.2" + react: ">= 16.8.0" + react-dom: "*" + react-native: "*" + typescript: ^5 + peerDependenciesMeta: + react-dom: + optional: true + react-native: + optional: true + typescript: + optional: true + react-is@17.0.2: - resolution: {integrity: sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==} + resolution: + { + integrity: sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==, + } react-refresh@0.14.2: - resolution: {integrity: sha512-jCvmsr+1IUSMUyzOkRcvnVbX3ZYC6g9TDrDbFuFmRDq7PD4yaGbLKNQL6k2jnArV8hjYxh7hVhAZB6s9HDGpZA==} - engines: {node: '>=0.10.0'} + resolution: + { + integrity: sha512-jCvmsr+1IUSMUyzOkRcvnVbX3ZYC6g9TDrDbFuFmRDq7PD4yaGbLKNQL6k2jnArV8hjYxh7hVhAZB6s9HDGpZA==, + } + engines: { node: ">=0.10.0" } + + react-remove-scroll-bar@2.3.8: + resolution: + { + integrity: sha512-9r+yi9+mgU33AKcj6IbT9oRCO78WriSj6t/cF8DWBZJ9aOGPOTEDvdUDz1FwKim7QXWwmHqtdHnRJfhAxEG46Q==, + } + engines: { node: ">=10" } + peerDependencies: + "@types/react": "*" + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + "@types/react": + optional: true + + react-remove-scroll@2.7.2: + resolution: + { + integrity: sha512-Iqb9NjCCTt6Hf+vOdNIZGdTiH1QSqr27H/Ek9sv/a97gfueI/5h1s3yRi1nngzMUaOOToin5dI1dXKdXiF+u0Q==, + } + engines: { node: ">=10" } + peerDependencies: + "@types/react": "*" + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc + peerDependenciesMeta: + "@types/react": + optional: true + + react-style-singleton@2.2.3: + resolution: + { + integrity: sha512-b6jSvxvVnyptAiLjbkWLE/lOnR4lfTtDAl+eUC7RZy+QQWc6wRzIV2CE6xBuMmDxc2qIihtDCZD5NPOFl7fRBQ==, + } + engines: { node: ">=10" } + peerDependencies: + "@types/react": "*" + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc + peerDependenciesMeta: + "@types/react": + optional: true react@18.3.1: - resolution: {integrity: sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==} - engines: {node: '>=0.10.0'} + resolution: + { + integrity: sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==, + } + engines: { node: ">=0.10.0" } redent@3.0.0: - resolution: {integrity: sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==} - engines: {node: '>=8'} + resolution: + { + integrity: sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==, + } + engines: { node: ">=8" } require-from-string@2.0.2: - resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} - engines: {node: '>=0.10.0'} + resolution: + { + integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==, + } + engines: { node: ">=0.10.0" } resolve-dir@1.0.1: - resolution: {integrity: sha512-R7uiTjECzvOsWSfdM0QKFNBVFcK27aHOUwdvK53BcW8zqnGdYp0Fbj82cy54+2A4P2tFM22J5kRfe1R+lM/1yg==} - engines: {node: '>=0.10.0'} + resolution: + { + integrity: sha512-R7uiTjECzvOsWSfdM0QKFNBVFcK27aHOUwdvK53BcW8zqnGdYp0Fbj82cy54+2A4P2tFM22J5kRfe1R+lM/1yg==, + } + engines: { node: ">=0.10.0" } + + resolve-from@4.0.0: + resolution: + { + integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==, + } + engines: { node: ">=4" } resolve@1.22.8: - resolution: {integrity: sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==} + resolution: + { + integrity: sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==, + } hasBin: true + reusify@1.1.0: + resolution: + { + integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==, + } + engines: { iojs: ">=1.0.0", node: ">=0.10.0" } + rfdc@1.4.1: - resolution: {integrity: sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==} + resolution: + { + integrity: sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==, + } + + rimraf@3.0.2: + resolution: + { + integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==, + } + deprecated: Rimraf versions prior to v4 are no longer supported + hasBin: true rollup@4.59.0: - resolution: {integrity: sha512-2oMpl67a3zCH9H79LeMcbDhXW/UmWG/y2zuqnF2jQq5uq9TbM9TVyXvA4+t+ne2IIkBdrLpAaRQAvo7YI/Yyeg==} - engines: {node: '>=18.0.0', npm: '>=8.0.0'} + resolution: + { + integrity: sha512-2oMpl67a3zCH9H79LeMcbDhXW/UmWG/y2zuqnF2jQq5uq9TbM9TVyXvA4+t+ne2IIkBdrLpAaRQAvo7YI/Yyeg==, + } + engines: { node: ">=18.0.0", npm: ">=8.0.0" } hasBin: true rrweb-cssom@0.7.1: - resolution: {integrity: sha512-TrEMa7JGdVm0UThDJSx7ddw5nVm3UJS9o9CCIZ72B1vSyEZoziDqBYP3XIoi/12lKrJR8rE3jeFHMok2F/Mnsg==} + resolution: + { + integrity: sha512-TrEMa7JGdVm0UThDJSx7ddw5nVm3UJS9o9CCIZ72B1vSyEZoziDqBYP3XIoi/12lKrJR8rE3jeFHMok2F/Mnsg==, + } rrweb-cssom@0.8.0: - resolution: {integrity: sha512-guoltQEx+9aMf2gDZ0s62EcV8lsXR+0w8915TC3ITdn2YueuNjdAYh/levpU9nFaoChh9RUS5ZdQMrKfVEN9tw==} + resolution: + { + integrity: sha512-guoltQEx+9aMf2gDZ0s62EcV8lsXR+0w8915TC3ITdn2YueuNjdAYh/levpU9nFaoChh9RUS5ZdQMrKfVEN9tw==, + } + + run-parallel@1.2.0: + resolution: + { + integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==, + } safe-buffer@5.2.1: - resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} + resolution: + { + integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==, + } safer-buffer@2.1.2: - resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} + resolution: + { + integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==, + } saxes@6.0.0: - resolution: {integrity: sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==} - engines: {node: '>=v12.22.7'} + resolution: + { + integrity: sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==, + } + engines: { node: ">=v12.22.7" } scheduler@0.23.2: - resolution: {integrity: sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==} + resolution: + { + integrity: sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==, + } schema-utils@4.3.3: - resolution: {integrity: sha512-eflK8wEtyOE6+hsaRVPxvUKYCpRgzLqDTb8krvAsRIwOGlHoSgYLgBXoubGgLd2fT41/OUYdb48v4k4WWHQurA==} - engines: {node: '>= 10.13.0'} + resolution: + { + integrity: sha512-eflK8wEtyOE6+hsaRVPxvUKYCpRgzLqDTb8krvAsRIwOGlHoSgYLgBXoubGgLd2fT41/OUYdb48v4k4WWHQurA==, + } + engines: { node: ">= 10.13.0" } semver@6.3.1: - resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} + resolution: + { + integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==, + } hasBin: true semver@7.6.3: - resolution: {integrity: sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==} - engines: {node: '>=10'} + resolution: + { + integrity: sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==, + } + engines: { node: ">=10" } hasBin: true + semver@7.7.4: + resolution: + { + integrity: sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==, + } + engines: { node: ">=10" } + hasBin: true + + seroval-plugins@1.5.1: + resolution: + { + integrity: sha512-4FbuZ/TMl02sqv0RTFexu0SP6V+ywaIe5bAWCCEik0fk17BhALgwvUDVF7e3Uvf9pxmwCEJsRPmlkUE6HdzLAw==, + } + engines: { node: ">=10" } + peerDependencies: + seroval: ^1.0 + + seroval@1.5.1: + resolution: + { + integrity: sha512-OwrZRZAfhHww0WEnKHDY8OM0U/Qs8OTfIDWhUD4BLpNJUfXK4cGmjiagGze086m+mhI+V2nD0gfbHEnJjb9STA==, + } + engines: { node: ">=10" } + setprototypeof@1.2.0: - resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==} + resolution: + { + integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==, + } + + sharp@0.34.5: + resolution: + { + integrity: sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg==, + } + engines: { node: ^18.17.0 || ^20.3.0 || >=21.0.0 } + + shebang-command@2.0.0: + resolution: + { + integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==, + } + engines: { node: ">=8" } + + shebang-regex@3.0.0: + resolution: + { + integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==, + } + engines: { node: ">=8" } siginfo@2.0.0: - resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==} + resolution: + { + integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==, + } sirv@3.0.2: - resolution: {integrity: sha512-2wcC/oGxHis/BoHkkPwldgiPSYcpZK3JU28WoMVv55yHJgcZ8rlXvuG9iZggz+sU1d4bRgIGASwyWqjxu3FM0g==} - engines: {node: '>=18'} + resolution: + { + integrity: sha512-2wcC/oGxHis/BoHkkPwldgiPSYcpZK3JU28WoMVv55yHJgcZ8rlXvuG9iZggz+sU1d4bRgIGASwyWqjxu3FM0g==, + } + engines: { node: ">=18" } + + slash@3.0.0: + resolution: + { + integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==, + } + engines: { node: ">=8" } + + sonner@2.0.7: + resolution: + { + integrity: sha512-W6ZN4p58k8aDKA4XPcx2hpIQXBRAgyiWVkYhT7CvK6D3iAu7xjvVyhQHg2/iaKJZ1XVJ4r7XuwGL+WGEK37i9w==, + } + peerDependencies: + react: ^18.0.0 || ^19.0.0 || ^19.0.0-rc + react-dom: ^18.0.0 || ^19.0.0 || ^19.0.0-rc sorted-array-functions@1.3.0: - resolution: {integrity: sha512-2sqgzeFlid6N4Z2fUQ1cvFmTOLRi/sEDzSQ0OKYchqgoPmQBVyM3959qYx3fpS6Esef80KjmpgPeEr028dP3OA==} + resolution: + { + integrity: sha512-2sqgzeFlid6N4Z2fUQ1cvFmTOLRi/sEDzSQ0OKYchqgoPmQBVyM3959qYx3fpS6Esef80KjmpgPeEr028dP3OA==, + } source-map-js@1.2.1: - resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} - engines: {node: '>=0.10.0'} + resolution: + { + integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==, + } + engines: { node: ">=0.10.0" } stackback@0.0.2: - resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} + resolution: + { + integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==, + } statuses@1.5.0: - resolution: {integrity: sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==} - engines: {node: '>= 0.6'} + resolution: + { + integrity: sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==, + } + engines: { node: ">= 0.6" } statuses@2.0.2: - resolution: {integrity: sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==} - engines: {node: '>= 0.8'} + resolution: + { + integrity: sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==, + } + engines: { node: ">= 0.8" } std-env@3.10.0: - resolution: {integrity: sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==} + resolution: + { + integrity: sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==, + } streamroller@3.1.5: - resolution: {integrity: sha512-KFxaM7XT+irxvdqSP1LGLgNWbYN7ay5owZ3r/8t77p+EtSUAfUgtl7be3xtqtOmGUl9K9YPO2ca8133RlTjvKw==} - engines: {node: '>=8.0'} + resolution: + { + integrity: sha512-KFxaM7XT+irxvdqSP1LGLgNWbYN7ay5owZ3r/8t77p+EtSUAfUgtl7be3xtqtOmGUl9K9YPO2ca8133RlTjvKw==, + } + engines: { node: ">=8.0" } + + strip-ansi@6.0.1: + resolution: + { + integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==, + } + engines: { node: ">=8" } strip-indent@3.0.0: - resolution: {integrity: sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==} - engines: {node: '>=8'} + resolution: + { + integrity: sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==, + } + engines: { node: ">=8" } + + strip-json-comments@3.1.1: + resolution: + { + integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==, + } + engines: { node: ">=8" } supports-color@7.2.0: - resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} - engines: {node: '>=8'} + resolution: + { + integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==, + } + engines: { node: ">=8" } supports-preserve-symlinks-flag@1.0.0: - resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} - engines: {node: '>= 0.4'} + resolution: + { + integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==, + } + engines: { node: ">= 0.4" } symbol-tree@3.2.4: - resolution: {integrity: sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==} + resolution: + { + integrity: sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==, + } + + tailwind-merge@3.5.0: + resolution: + { + integrity: sha512-I8K9wewnVDkL1NTGoqWmVEIlUcB9gFriAEkXkfCjX5ib8ezGxtR3xD7iZIxrfArjEsH7F1CHD4RFUtxefdqV/A==, + } + + tailwindcss@4.2.2: + resolution: + { + integrity: sha512-KWBIxs1Xb6NoLdMVqhbhgwZf2PGBpPEiwOqgI4pFIYbNTfBXiKYyWoTsXgBQ9WFg/OlhnvHaY+AEpW7wSmFo2Q==, + } + + tapable@2.3.0: + resolution: + { + integrity: sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg==, + } + engines: { node: ">=6" } + + text-table@0.2.0: + resolution: + { + integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==, + } + + tiny-invariant@1.3.3: + resolution: + { + integrity: sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==, + } + + tiny-warning@1.0.3: + resolution: + { + integrity: sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA==, + } tinybench@2.9.0: - resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==} + resolution: + { + integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==, + } tinyexec@0.3.2: - resolution: {integrity: sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==} + resolution: + { + integrity: sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==, + } tinyglobby@0.2.15: - resolution: {integrity: sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==} - engines: {node: '>=12.0.0'} + resolution: + { + integrity: sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==, + } + engines: { node: ">=12.0.0" } tinypool@1.1.1: - resolution: {integrity: sha512-Zba82s87IFq9A9XmjiX5uZA/ARWDrB03OHlq+Vw1fSdt0I+4/Kutwy8BP4Y/y/aORMo61FQ0vIb5j44vSo5Pkg==} - engines: {node: ^18.0.0 || >=20.0.0} + resolution: + { + integrity: sha512-Zba82s87IFq9A9XmjiX5uZA/ARWDrB03OHlq+Vw1fSdt0I+4/Kutwy8BP4Y/y/aORMo61FQ0vIb5j44vSo5Pkg==, + } + engines: { node: ^18.0.0 || >=20.0.0 } tinyrainbow@1.2.0: - resolution: {integrity: sha512-weEDEq7Z5eTHPDh4xjX789+fHfF+P8boiFB+0vbWzpbnbsEr/GRaohi/uMKxg8RZMXnl1ItAi/IUHWMsjDV7kQ==} - engines: {node: '>=14.0.0'} + resolution: + { + integrity: sha512-weEDEq7Z5eTHPDh4xjX789+fHfF+P8boiFB+0vbWzpbnbsEr/GRaohi/uMKxg8RZMXnl1ItAi/IUHWMsjDV7kQ==, + } + engines: { node: ">=14.0.0" } tinyspy@3.0.2: - resolution: {integrity: sha512-n1cw8k1k0x4pgA2+9XrOkFydTerNcJ1zWCO5Nn9scWHTD+5tp8dghT2x1uduQePZTZgd3Tupf+x9BxJjeJi77Q==} - engines: {node: '>=14.0.0'} + resolution: + { + integrity: sha512-n1cw8k1k0x4pgA2+9XrOkFydTerNcJ1zWCO5Nn9scWHTD+5tp8dghT2x1uduQePZTZgd3Tupf+x9BxJjeJi77Q==, + } + engines: { node: ">=14.0.0" } tldts-core@6.1.86: - resolution: {integrity: sha512-Je6p7pkk+KMzMv2XXKmAE3McmolOQFdxkKw0R8EYNr7sELW46JqnNeTX8ybPiQgvg1ymCoF8LXs5fzFaZvJPTA==} + resolution: + { + integrity: sha512-Je6p7pkk+KMzMv2XXKmAE3McmolOQFdxkKw0R8EYNr7sELW46JqnNeTX8ybPiQgvg1ymCoF8LXs5fzFaZvJPTA==, + } tldts@6.1.86: - resolution: {integrity: sha512-WMi/OQ2axVTf/ykqCQgXiIct+mSQDFdH2fkwhPwgEwvJ1kSzZRiinb0zF2Xb8u4+OqPChmyI6MEu4EezNJz+FQ==} + resolution: + { + integrity: sha512-WMi/OQ2axVTf/ykqCQgXiIct+mSQDFdH2fkwhPwgEwvJ1kSzZRiinb0zF2Xb8u4+OqPChmyI6MEu4EezNJz+FQ==, + } hasBin: true + to-regex-range@5.0.1: + resolution: + { + integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==, + } + engines: { node: ">=8.0" } + toidentifier@1.0.1: - resolution: {integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==} - engines: {node: '>=0.6'} + resolution: + { + integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==, + } + engines: { node: ">=0.6" } totalist@3.0.1: - resolution: {integrity: sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==} - engines: {node: '>=6'} + resolution: + { + integrity: sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==, + } + engines: { node: ">=6" } tough-cookie@5.1.2: - resolution: {integrity: sha512-FVDYdxtnj0G6Qm/DhNPSb8Ju59ULcup3tuJxkFb5K8Bv2pUXILbf0xZWU8PX8Ov19OXljbUyveOFwRMwkXzO+A==} - engines: {node: '>=16'} + resolution: + { + integrity: sha512-FVDYdxtnj0G6Qm/DhNPSb8Ju59ULcup3tuJxkFb5K8Bv2pUXILbf0xZWU8PX8Ov19OXljbUyveOFwRMwkXzO+A==, + } + engines: { node: ">=16" } tr46@5.1.1: - resolution: {integrity: sha512-hdF5ZgjTqgAntKkklYw0R03MG2x/bSzTtkxmIRw/sTNV8YXsCJ1tfLAX23lhxhHJlEf3CRCOCGGWw3vI3GaSPw==} - engines: {node: '>=18'} + resolution: + { + integrity: sha512-hdF5ZgjTqgAntKkklYw0R03MG2x/bSzTtkxmIRw/sTNV8YXsCJ1tfLAX23lhxhHJlEf3CRCOCGGWw3vI3GaSPw==, + } + engines: { node: ">=18" } + + ts-api-utils@2.5.0: + resolution: + { + integrity: sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA==, + } + engines: { node: ">=18.12" } + peerDependencies: + typescript: ">=4.8.4" + + tslib@1.14.1: + resolution: + { + integrity: sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==, + } tslib@2.8.1: - resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} + resolution: + { + integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==, + } tsscmp@1.0.6: - resolution: {integrity: sha512-LxhtAkPDTkVCMQjt2h6eBVY28KCjikZqZfMcC15YBeNjkgUpdCfBu5HoiOTDu86v6smE8yOjyEktJ8hlbANHQA==} - engines: {node: '>=0.6.x'} + resolution: + { + integrity: sha512-LxhtAkPDTkVCMQjt2h6eBVY28KCjikZqZfMcC15YBeNjkgUpdCfBu5HoiOTDu86v6smE8yOjyEktJ8hlbANHQA==, + } + engines: { node: ">=0.6.x" } + + tsutils@3.21.0: + resolution: + { + integrity: sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==, + } + engines: { node: ">= 6" } + peerDependencies: + typescript: ">=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta" + + tw-animate-css@1.4.0: + resolution: + { + integrity: sha512-7bziOlRqH0hJx80h/3mbicLW7o8qLsH5+RaLR2t+OHM3D0JlWGODQKQ4cxbK7WlvmUxpcj6Kgu6EKqjrGFe3QQ==, + } + + type-check@0.4.0: + resolution: + { + integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==, + } + engines: { node: ">= 0.8.0" } + + type-fest@0.20.2: + resolution: + { + integrity: sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==, + } + engines: { node: ">=10" } type-is@2.0.1: - resolution: {integrity: sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==} - engines: {node: '>= 0.6'} + resolution: + { + integrity: sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==, + } + engines: { node: ">= 0.6" } + + typescript-eslint@8.57.2: + resolution: + { + integrity: sha512-VEPQ0iPgWO/sBaZOU1xo4nuNdODVOajPnTIbog2GKYr31nIlZ0fWPoCQgGfF3ETyBl1vn63F/p50Um9Z4J8O8A==, + } + engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + typescript: ">=4.8.4 <6.0.0" typescript@5.4.5: - resolution: {integrity: sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==} - engines: {node: '>=14.17'} + resolution: + { + integrity: sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==, + } + engines: { node: ">=14.17" } hasBin: true undici-types@6.21.0: - resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==} + resolution: + { + integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==, + } universalify@0.1.2: - resolution: {integrity: sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==} - engines: {node: '>= 4.0.0'} + resolution: + { + integrity: sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==, + } + engines: { node: ">= 4.0.0" } universalify@2.0.1: - resolution: {integrity: sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==} - engines: {node: '>= 10.0.0'} + resolution: + { + integrity: sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==, + } + engines: { node: ">= 10.0.0" } upath@2.0.1: - resolution: {integrity: sha512-1uEe95xksV1O0CYKXo8vQvN1JEbtJp7lb7C5U9HMsIp6IVwntkH/oNUzyVNQSd4S1sYk2FpSSW44FqMc8qee5w==} - engines: {node: '>=4'} + resolution: + { + integrity: sha512-1uEe95xksV1O0CYKXo8vQvN1JEbtJp7lb7C5U9HMsIp6IVwntkH/oNUzyVNQSd4S1sYk2FpSSW44FqMc8qee5w==, + } + engines: { node: ">=4" } update-browserslist-db@1.2.3: - resolution: {integrity: sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==} + resolution: + { + integrity: sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==, + } hasBin: true peerDependencies: - browserslist: '>= 4.21.0' + browserslist: ">= 4.21.0" + + uri-js@4.4.1: + resolution: + { + integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==, + } + + use-callback-ref@1.3.3: + resolution: + { + integrity: sha512-jQL3lRnocaFtu3V00JToYz/4QkNWswxijDaCVNZRiRTO3HQDLsdu1ZtmIUvV4yPp+rvWm5j0y0TG/S61cuijTg==, + } + engines: { node: ">=10" } + peerDependencies: + "@types/react": "*" + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc + peerDependenciesMeta: + "@types/react": + optional: true + + use-sidecar@1.1.3: + resolution: + { + integrity: sha512-Fedw0aZvkhynoPYlA5WXrMCAMm+nSWdZt6lzJQ7Ok8S6Q+VsHmHpRWndVRJ8Be0ZbkfPc5LRYH+5XrzXcEeLRQ==, + } + engines: { node: ">=10" } + peerDependencies: + "@types/react": "*" + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc + peerDependenciesMeta: + "@types/react": + optional: true + + use-sync-external-store@1.6.0: + resolution: + { + integrity: sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w==, + } + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + + util-deprecate@1.0.2: + resolution: + { + integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==, + } vary@1.1.2: - resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==} - engines: {node: '>= 0.8'} + resolution: + { + integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==, + } + engines: { node: ">= 0.8" } + + vaul@1.1.2: + resolution: + { + integrity: sha512-ZFkClGpWyI2WUQjdLJ/BaGuV6AVQiJ3uELGk3OYtP+B6yCO7Cmn9vPFXVJkRaGkOJu3m8bQMgtyzNHixULceQA==, + } + peerDependencies: + react: ^16.8 || ^17.0 || ^18.0 || ^19.0.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0.0 || ^19.0.0-rc vite-node@2.1.9: - resolution: {integrity: sha512-AM9aQ/IPrW/6ENLQg3AGY4K1N2TGZdR5e4gu/MmmR2xR3Ll1+dib+nook92g4TV3PXVyeyxdWwtaCAiUL0hMxA==} - engines: {node: ^18.0.0 || >=20.0.0} + resolution: + { + integrity: sha512-AM9aQ/IPrW/6ENLQg3AGY4K1N2TGZdR5e4gu/MmmR2xR3Ll1+dib+nook92g4TV3PXVyeyxdWwtaCAiUL0hMxA==, + } + engines: { node: ^18.0.0 || >=20.0.0 } hasBin: true vite@5.2.10: - resolution: {integrity: sha512-PAzgUZbP7msvQvqdSD+ErD5qGnSFiGOoWmV5yAKUEI0kdhjbH6nMWVyZQC/hSc4aXwc0oJ9aEdIiF9Oje0JFCw==} - engines: {node: ^18.0.0 || >=20.0.0} + resolution: + { + integrity: sha512-PAzgUZbP7msvQvqdSD+ErD5qGnSFiGOoWmV5yAKUEI0kdhjbH6nMWVyZQC/hSc4aXwc0oJ9aEdIiF9Oje0JFCw==, + } + engines: { node: ^18.0.0 || >=20.0.0 } hasBin: true peerDependencies: - '@types/node': ^18.0.0 || >=20.0.0 - less: '*' + "@types/node": ^18.0.0 || >=20.0.0 + less: "*" lightningcss: ^1.21.0 - sass: '*' - stylus: '*' - sugarss: '*' + sass: "*" + stylus: "*" + sugarss: "*" terser: ^5.4.0 peerDependenciesMeta: - '@types/node': + "@types/node": optional: true less: optional: true @@ -1637,66 +4767,121 @@ packages: optional: true vitest@2.1.9: - resolution: {integrity: sha512-MSmPM9REYqDGBI8439mA4mWhV5sKmDlBKWIYbA3lRb2PTHACE0mgKwA8yQ2xq9vxDTuk4iPrECBAEW2aoFXY0Q==} - engines: {node: ^18.0.0 || >=20.0.0} + resolution: + { + integrity: sha512-MSmPM9REYqDGBI8439mA4mWhV5sKmDlBKWIYbA3lRb2PTHACE0mgKwA8yQ2xq9vxDTuk4iPrECBAEW2aoFXY0Q==, + } + engines: { node: ^18.0.0 || >=20.0.0 } hasBin: true peerDependencies: - '@edge-runtime/vm': '*' - '@types/node': ^18.0.0 || >=20.0.0 - '@vitest/browser': 2.1.9 - '@vitest/ui': 2.1.9 - happy-dom: '*' - jsdom: '*' + "@edge-runtime/vm": "*" + "@types/node": ^18.0.0 || >=20.0.0 + "@vitest/browser": 2.1.9 + "@vitest/ui": 2.1.9 + happy-dom: "*" + jsdom: "*" peerDependenciesMeta: - '@edge-runtime/vm': + "@edge-runtime/vm": optional: true - '@types/node': + "@types/node": optional: true - '@vitest/browser': + "@vitest/browser": optional: true - '@vitest/ui': + "@vitest/ui": optional: true happy-dom: optional: true jsdom: optional: true + void-elements@3.1.0: + resolution: + { + integrity: sha512-Dhxzh5HZuiHQhbvTW9AMetFfBHDMYpo23Uo9btPXgdYP+3T5S+p+jgNy7spra+veYhBP2dCSgxR/i2Y02h5/6w==, + } + engines: { node: ">=0.10.0" } + w3c-xmlserializer@5.0.0: - resolution: {integrity: sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==} - engines: {node: '>=18'} + resolution: + { + integrity: sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==, + } + engines: { node: ">=18" } webidl-conversions@7.0.0: - resolution: {integrity: sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==} - engines: {node: '>=12'} + resolution: + { + integrity: sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==, + } + engines: { node: ">=12" } whatwg-encoding@3.1.1: - resolution: {integrity: sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==} - engines: {node: '>=18'} + resolution: + { + integrity: sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==, + } + engines: { node: ">=18" } deprecated: Use @exodus/bytes instead for a more spec-conformant and faster implementation whatwg-mimetype@4.0.0: - resolution: {integrity: sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==} - engines: {node: '>=18'} + resolution: + { + integrity: sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==, + } + engines: { node: ">=18" } whatwg-url@14.2.0: - resolution: {integrity: sha512-De72GdQZzNTUBBChsXueQUnPKDkg/5A5zp7pFDuQAj5UFoENpiACU0wlCvzpAGnTkj++ihpKwKyYewn/XNUbKw==} - engines: {node: '>=18'} + resolution: + { + integrity: sha512-De72GdQZzNTUBBChsXueQUnPKDkg/5A5zp7pFDuQAj5UFoENpiACU0wlCvzpAGnTkj++ihpKwKyYewn/XNUbKw==, + } + engines: { node: ">=18" } which@1.3.1: - resolution: {integrity: sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==} + resolution: + { + integrity: sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==, + } + hasBin: true + + which@2.0.2: + resolution: + { + integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==, + } + engines: { node: ">= 8" } hasBin: true why-is-node-running@2.3.0: - resolution: {integrity: sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==} - engines: {node: '>=8'} + resolution: + { + integrity: sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==, + } + engines: { node: ">=8" } hasBin: true + word-wrap@1.2.5: + resolution: + { + integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==, + } + engines: { node: ">=0.10.0" } + + wrappy@1.0.2: + resolution: + { + integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==, + } + ws@8.18.0: - resolution: {integrity: sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==} - engines: {node: '>=10.0.0'} + resolution: + { + integrity: sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==, + } + engines: { node: ">=10.0.0" } peerDependencies: bufferutil: ^4.0.1 - utf-8-validate: '>=5.0.2' + utf-8-validate: ">=5.0.2" peerDependenciesMeta: bufferutil: optional: true @@ -1704,11 +4889,14 @@ packages: optional: true ws@8.19.0: - resolution: {integrity: sha512-blAT2mjOEIi0ZzruJfIhb3nps74PRWTCz1IjglWEEpQl5XS/UNama6u2/rjFkDDouqr4L67ry+1aGIALViWjDg==} - engines: {node: '>=10.0.0'} + resolution: + { + integrity: sha512-blAT2mjOEIi0ZzruJfIhb3nps74PRWTCz1IjglWEEpQl5XS/UNama6u2/rjFkDDouqr4L67ry+1aGIALViWjDg==, + } + engines: { node: ">=10.0.0" } peerDependencies: bufferutil: ^4.0.1 - utf-8-validate: '>=5.0.2' + utf-8-validate: ">=5.0.2" peerDependenciesMeta: bufferutil: optional: true @@ -1716,47 +4904,62 @@ packages: optional: true xml-name-validator@5.0.0: - resolution: {integrity: sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==} - engines: {node: '>=18'} + resolution: + { + integrity: sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==, + } + engines: { node: ">=18" } xmlchars@2.2.0: - resolution: {integrity: sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==} + resolution: + { + integrity: sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==, + } yallist@3.1.1: - resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} + resolution: + { + integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==, + } + + yocto-queue@0.1.0: + resolution: + { + integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==, + } + engines: { node: ">=10" } snapshots: + "@adobe/css-tools@4.4.4": {} - '@adobe/css-tools@4.4.4': {} - - '@asamuzakjp/css-color@3.2.0': + "@asamuzakjp/css-color@3.2.0": dependencies: - '@csstools/css-calc': 2.1.4(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4) - '@csstools/css-color-parser': 3.1.0(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4) - '@csstools/css-parser-algorithms': 3.0.5(@csstools/css-tokenizer@3.0.4) - '@csstools/css-tokenizer': 3.0.4 + "@csstools/css-calc": 2.1.4(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4) + "@csstools/css-color-parser": 3.1.0(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4) + "@csstools/css-parser-algorithms": 3.0.5(@csstools/css-tokenizer@3.0.4) + "@csstools/css-tokenizer": 3.0.4 lru-cache: 10.4.3 - '@babel/code-frame@7.29.0': + "@babel/code-frame@7.29.0": dependencies: - '@babel/helper-validator-identifier': 7.28.5 + "@babel/helper-validator-identifier": 7.28.5 js-tokens: 4.0.0 picocolors: 1.1.1 - '@babel/compat-data@7.29.0': {} + "@babel/compat-data@7.29.0": {} - '@babel/core@7.29.0': + "@babel/core@7.29.0": dependencies: - '@babel/code-frame': 7.29.0 - '@babel/generator': 7.29.1 - '@babel/helper-compilation-targets': 7.28.6 - '@babel/helper-module-transforms': 7.28.6(@babel/core@7.29.0) - '@babel/helpers': 7.29.2 - '@babel/parser': 7.29.2 - '@babel/template': 7.28.6 - '@babel/traverse': 7.29.0 - '@babel/types': 7.29.0 - '@jridgewell/remapping': 2.3.5 + "@babel/code-frame": 7.29.0 + "@babel/generator": 7.29.1 + "@babel/helper-compilation-targets": 7.28.6 + "@babel/helper-module-transforms": 7.28.6(@babel/core@7.29.0) + "@babel/helpers": 7.29.2 + "@babel/parser": 7.29.2 + "@babel/template": 7.28.6 + "@babel/traverse": 7.29.0 + "@babel/types": 7.29.0 + "@jridgewell/remapping": 2.3.5 convert-source-map: 2.0.0 debug: 4.4.3 gensync: 1.0.0-beta.2 @@ -1765,226 +4968,357 @@ snapshots: transitivePeerDependencies: - supports-color - '@babel/generator@7.29.1': + "@babel/generator@7.29.1": dependencies: - '@babel/parser': 7.29.2 - '@babel/types': 7.29.0 - '@jridgewell/gen-mapping': 0.3.13 - '@jridgewell/trace-mapping': 0.3.31 + "@babel/parser": 7.29.2 + "@babel/types": 7.29.0 + "@jridgewell/gen-mapping": 0.3.13 + "@jridgewell/trace-mapping": 0.3.31 jsesc: 3.1.0 - '@babel/helper-compilation-targets@7.28.6': + "@babel/helper-compilation-targets@7.28.6": dependencies: - '@babel/compat-data': 7.29.0 - '@babel/helper-validator-option': 7.27.1 + "@babel/compat-data": 7.29.0 + "@babel/helper-validator-option": 7.27.1 browserslist: 4.28.1 lru-cache: 5.1.1 semver: 6.3.1 - '@babel/helper-globals@7.28.0': {} + "@babel/helper-globals@7.28.0": {} - '@babel/helper-module-imports@7.28.6': + "@babel/helper-module-imports@7.28.6": dependencies: - '@babel/traverse': 7.29.0 - '@babel/types': 7.29.0 + "@babel/traverse": 7.29.0 + "@babel/types": 7.29.0 transitivePeerDependencies: - supports-color - '@babel/helper-module-transforms@7.28.6(@babel/core@7.29.0)': + "@babel/helper-module-transforms@7.28.6(@babel/core@7.29.0)": dependencies: - '@babel/core': 7.29.0 - '@babel/helper-module-imports': 7.28.6 - '@babel/helper-validator-identifier': 7.28.5 - '@babel/traverse': 7.29.0 + "@babel/core": 7.29.0 + "@babel/helper-module-imports": 7.28.6 + "@babel/helper-validator-identifier": 7.28.5 + "@babel/traverse": 7.29.0 transitivePeerDependencies: - supports-color - '@babel/helper-plugin-utils@7.28.6': {} + "@babel/helper-plugin-utils@7.28.6": {} - '@babel/helper-string-parser@7.27.1': {} + "@babel/helper-string-parser@7.27.1": {} - '@babel/helper-validator-identifier@7.28.5': {} + "@babel/helper-validator-identifier@7.28.5": {} - '@babel/helper-validator-option@7.27.1': {} + "@babel/helper-validator-option@7.27.1": {} - '@babel/helpers@7.29.2': + "@babel/helpers@7.29.2": dependencies: - '@babel/template': 7.28.6 - '@babel/types': 7.29.0 + "@babel/template": 7.28.6 + "@babel/types": 7.29.0 - '@babel/parser@7.29.2': + "@babel/parser@7.29.2": dependencies: - '@babel/types': 7.29.0 + "@babel/types": 7.29.0 - '@babel/plugin-transform-react-jsx-self@7.27.1(@babel/core@7.29.0)': + "@babel/plugin-transform-react-jsx-self@7.27.1(@babel/core@7.29.0)": dependencies: - '@babel/core': 7.29.0 - '@babel/helper-plugin-utils': 7.28.6 + "@babel/core": 7.29.0 + "@babel/helper-plugin-utils": 7.28.6 - '@babel/plugin-transform-react-jsx-source@7.27.1(@babel/core@7.29.0)': + "@babel/plugin-transform-react-jsx-source@7.27.1(@babel/core@7.29.0)": dependencies: - '@babel/core': 7.29.0 - '@babel/helper-plugin-utils': 7.28.6 + "@babel/core": 7.29.0 + "@babel/helper-plugin-utils": 7.28.6 - '@babel/runtime@7.29.2': {} + "@babel/runtime@7.29.2": {} - '@babel/template@7.28.6': + "@babel/template@7.28.6": dependencies: - '@babel/code-frame': 7.29.0 - '@babel/parser': 7.29.2 - '@babel/types': 7.29.0 + "@babel/code-frame": 7.29.0 + "@babel/parser": 7.29.2 + "@babel/types": 7.29.0 - '@babel/traverse@7.29.0': + "@babel/traverse@7.29.0": dependencies: - '@babel/code-frame': 7.29.0 - '@babel/generator': 7.29.1 - '@babel/helper-globals': 7.28.0 - '@babel/parser': 7.29.2 - '@babel/template': 7.28.6 - '@babel/types': 7.29.0 + "@babel/code-frame": 7.29.0 + "@babel/generator": 7.29.1 + "@babel/helper-globals": 7.28.0 + "@babel/parser": 7.29.2 + "@babel/template": 7.28.6 + "@babel/types": 7.29.0 debug: 4.4.3 transitivePeerDependencies: - supports-color - '@babel/types@7.29.0': + "@babel/types@7.29.0": dependencies: - '@babel/helper-string-parser': 7.27.1 - '@babel/helper-validator-identifier': 7.28.5 + "@babel/helper-string-parser": 7.27.1 + "@babel/helper-validator-identifier": 7.28.5 - '@csstools/color-helpers@5.1.0': {} + "@csstools/color-helpers@5.1.0": {} - '@csstools/css-calc@2.1.4(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4)': + "@csstools/css-calc@2.1.4(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4)": dependencies: - '@csstools/css-parser-algorithms': 3.0.5(@csstools/css-tokenizer@3.0.4) - '@csstools/css-tokenizer': 3.0.4 + "@csstools/css-parser-algorithms": 3.0.5(@csstools/css-tokenizer@3.0.4) + "@csstools/css-tokenizer": 3.0.4 - '@csstools/css-color-parser@3.1.0(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4)': + "@csstools/css-color-parser@3.1.0(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4)": dependencies: - '@csstools/color-helpers': 5.1.0 - '@csstools/css-calc': 2.1.4(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4) - '@csstools/css-parser-algorithms': 3.0.5(@csstools/css-tokenizer@3.0.4) - '@csstools/css-tokenizer': 3.0.4 + "@csstools/color-helpers": 5.1.0 + "@csstools/css-calc": 2.1.4(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4) + "@csstools/css-parser-algorithms": 3.0.5(@csstools/css-tokenizer@3.0.4) + "@csstools/css-tokenizer": 3.0.4 - '@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4)': + "@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4)": dependencies: - '@csstools/css-tokenizer': 3.0.4 + "@csstools/css-tokenizer": 3.0.4 - '@csstools/css-tokenizer@3.0.4': {} + "@csstools/css-tokenizer@3.0.4": {} - '@emnapi/core@1.9.0': + "@emnapi/core@1.9.0": dependencies: - '@emnapi/wasi-threads': 1.2.0 + "@emnapi/wasi-threads": 1.2.0 tslib: 2.8.1 optional: true - '@emnapi/runtime@1.9.0': + "@emnapi/runtime@1.9.0": dependencies: tslib: 2.8.1 optional: true - '@emnapi/wasi-threads@1.2.0': + "@emnapi/wasi-threads@1.2.0": dependencies: tslib: 2.8.1 optional: true - '@esbuild/aix-ppc64@0.20.2': + "@esbuild/aix-ppc64@0.20.2": + optional: true + + "@esbuild/android-arm64@0.20.2": + optional: true + + "@esbuild/android-arm@0.20.2": + optional: true + + "@esbuild/android-x64@0.20.2": + optional: true + + "@esbuild/darwin-arm64@0.20.2": + optional: true + + "@esbuild/darwin-x64@0.20.2": optional: true - '@esbuild/android-arm64@0.20.2': + "@esbuild/freebsd-arm64@0.20.2": optional: true - '@esbuild/android-arm@0.20.2': + "@esbuild/freebsd-x64@0.20.2": optional: true - '@esbuild/android-x64@0.20.2': + "@esbuild/linux-arm64@0.20.2": optional: true - '@esbuild/darwin-arm64@0.20.2': + "@esbuild/linux-arm@0.20.2": optional: true - '@esbuild/darwin-x64@0.20.2': + "@esbuild/linux-ia32@0.20.2": optional: true - '@esbuild/freebsd-arm64@0.20.2': + "@esbuild/linux-loong64@0.20.2": optional: true - '@esbuild/freebsd-x64@0.20.2': + "@esbuild/linux-mips64el@0.20.2": optional: true - '@esbuild/linux-arm64@0.20.2': + "@esbuild/linux-ppc64@0.20.2": optional: true - '@esbuild/linux-arm@0.20.2': + "@esbuild/linux-riscv64@0.20.2": optional: true - '@esbuild/linux-ia32@0.20.2': + "@esbuild/linux-s390x@0.20.2": optional: true - '@esbuild/linux-loong64@0.20.2': + "@esbuild/linux-x64@0.20.2": optional: true - '@esbuild/linux-mips64el@0.20.2': + "@esbuild/netbsd-x64@0.20.2": optional: true - '@esbuild/linux-ppc64@0.20.2': + "@esbuild/openbsd-x64@0.20.2": optional: true - '@esbuild/linux-riscv64@0.20.2': + "@esbuild/sunos-x64@0.20.2": optional: true - '@esbuild/linux-s390x@0.20.2': + "@esbuild/win32-arm64@0.20.2": optional: true - '@esbuild/linux-x64@0.20.2': + "@esbuild/win32-ia32@0.20.2": optional: true - '@esbuild/netbsd-x64@0.20.2': + "@esbuild/win32-x64@0.20.2": optional: true - '@esbuild/openbsd-x64@0.20.2': + "@eslint-community/eslint-utils@4.9.1(eslint@8.57.1)": + dependencies: + eslint: 8.57.1 + eslint-visitor-keys: 3.4.3 + + "@eslint-community/regexpp@4.12.2": {} + + "@eslint/eslintrc@2.1.4": + dependencies: + ajv: 6.14.0 + debug: 4.4.3 + espree: 9.6.1 + globals: 13.24.0 + ignore: 5.3.2 + import-fresh: 3.3.1 + js-yaml: 4.1.1 + minimatch: 3.1.5 + strip-json-comments: 3.1.1 + transitivePeerDependencies: + - supports-color + + "@eslint/js@8.57.1": {} + + "@humanwhocodes/config-array@0.13.0": + dependencies: + "@humanwhocodes/object-schema": 2.0.3 + debug: 4.4.3 + minimatch: 3.1.5 + transitivePeerDependencies: + - supports-color + + "@humanwhocodes/module-importer@1.0.1": {} + + "@humanwhocodes/object-schema@2.0.3": {} + + "@img/colour@1.1.0": {} + + "@img/sharp-darwin-arm64@0.34.5": + optionalDependencies: + "@img/sharp-libvips-darwin-arm64": 1.2.4 + optional: true + + "@img/sharp-darwin-x64@0.34.5": + optionalDependencies: + "@img/sharp-libvips-darwin-x64": 1.2.4 + optional: true + + "@img/sharp-libvips-darwin-arm64@1.2.4": + optional: true + + "@img/sharp-libvips-darwin-x64@1.2.4": + optional: true + + "@img/sharp-libvips-linux-arm64@1.2.4": + optional: true + + "@img/sharp-libvips-linux-arm@1.2.4": + optional: true + + "@img/sharp-libvips-linux-ppc64@1.2.4": + optional: true + + "@img/sharp-libvips-linux-riscv64@1.2.4": + optional: true + + "@img/sharp-libvips-linux-s390x@1.2.4": + optional: true + + "@img/sharp-libvips-linux-x64@1.2.4": + optional: true + + "@img/sharp-libvips-linuxmusl-arm64@1.2.4": + optional: true + + "@img/sharp-libvips-linuxmusl-x64@1.2.4": + optional: true + + "@img/sharp-linux-arm64@0.34.5": + optionalDependencies: + "@img/sharp-libvips-linux-arm64": 1.2.4 + optional: true + + "@img/sharp-linux-arm@0.34.5": + optionalDependencies: + "@img/sharp-libvips-linux-arm": 1.2.4 + optional: true + + "@img/sharp-linux-ppc64@0.34.5": + optionalDependencies: + "@img/sharp-libvips-linux-ppc64": 1.2.4 optional: true - '@esbuild/sunos-x64@0.20.2': + "@img/sharp-linux-riscv64@0.34.5": + optionalDependencies: + "@img/sharp-libvips-linux-riscv64": 1.2.4 optional: true - '@esbuild/win32-arm64@0.20.2': + "@img/sharp-linux-s390x@0.34.5": + optionalDependencies: + "@img/sharp-libvips-linux-s390x": 1.2.4 optional: true - '@esbuild/win32-ia32@0.20.2': + "@img/sharp-linux-x64@0.34.5": + optionalDependencies: + "@img/sharp-libvips-linux-x64": 1.2.4 optional: true - '@esbuild/win32-x64@0.20.2': + "@img/sharp-linuxmusl-arm64@0.34.5": + optionalDependencies: + "@img/sharp-libvips-linuxmusl-arm64": 1.2.4 + optional: true + + "@img/sharp-linuxmusl-x64@0.34.5": + optionalDependencies: + "@img/sharp-libvips-linuxmusl-x64": 1.2.4 optional: true - '@jridgewell/gen-mapping@0.3.13': + "@img/sharp-wasm32@0.34.5": dependencies: - '@jridgewell/sourcemap-codec': 1.5.5 - '@jridgewell/trace-mapping': 0.3.31 + "@emnapi/runtime": 1.9.0 + optional: true + + "@img/sharp-win32-arm64@0.34.5": + optional: true - '@jridgewell/remapping@2.3.5': + "@img/sharp-win32-ia32@0.34.5": + optional: true + + "@img/sharp-win32-x64@0.34.5": + optional: true + + "@jridgewell/gen-mapping@0.3.13": + dependencies: + "@jridgewell/sourcemap-codec": 1.5.5 + "@jridgewell/trace-mapping": 0.3.31 + + "@jridgewell/remapping@2.3.5": dependencies: - '@jridgewell/gen-mapping': 0.3.13 - '@jridgewell/trace-mapping': 0.3.31 + "@jridgewell/gen-mapping": 0.3.13 + "@jridgewell/trace-mapping": 0.3.31 - '@jridgewell/resolve-uri@3.1.2': {} + "@jridgewell/resolve-uri@3.1.2": {} - '@jridgewell/sourcemap-codec@1.5.5': {} + "@jridgewell/sourcemap-codec@1.5.5": {} - '@jridgewell/trace-mapping@0.3.31': + "@jridgewell/trace-mapping@0.3.31": dependencies: - '@jridgewell/resolve-uri': 3.1.2 - '@jridgewell/sourcemap-codec': 1.5.5 + "@jridgewell/resolve-uri": 3.1.2 + "@jridgewell/sourcemap-codec": 1.5.5 - '@module-federation/bridge-react-webpack-plugin@0.21.6': + "@module-federation/bridge-react-webpack-plugin@0.21.6": dependencies: - '@module-federation/sdk': 0.21.6 - '@types/semver': 7.5.8 + "@module-federation/sdk": 0.21.6 + "@types/semver": 7.5.8 semver: 7.6.3 - '@module-federation/cli@0.21.6(typescript@5.4.5)': + "@module-federation/cli@0.21.6(typescript@5.4.5)": dependencies: - '@module-federation/dts-plugin': 0.21.6(typescript@5.4.5) - '@module-federation/sdk': 0.21.6 + "@module-federation/dts-plugin": 0.21.6(typescript@5.4.5) + "@module-federation/sdk": 0.21.6 chalk: 3.0.0 commander: 11.1.0 jiti: 2.4.2 @@ -1996,20 +5330,20 @@ snapshots: - utf-8-validate - vue-tsc - '@module-federation/data-prefetch@0.21.6(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + "@module-federation/data-prefetch@0.21.6(react-dom@18.3.1(react@18.3.1))(react@18.3.1)": dependencies: - '@module-federation/runtime': 0.21.6 - '@module-federation/sdk': 0.21.6 + "@module-federation/runtime": 0.21.6 + "@module-federation/sdk": 0.21.6 fs-extra: 9.1.0 react: 18.3.1 react-dom: 18.3.1(react@18.3.1) - '@module-federation/dts-plugin@0.21.6(typescript@5.4.5)': + "@module-federation/dts-plugin@0.21.6(typescript@5.4.5)": dependencies: - '@module-federation/error-codes': 0.21.6 - '@module-federation/managers': 0.21.6 - '@module-federation/sdk': 0.21.6 - '@module-federation/third-party-dts-extractor': 0.21.6 + "@module-federation/error-codes": 0.21.6 + "@module-federation/managers": 0.21.6 + "@module-federation/sdk": 0.21.6 + "@module-federation/third-party-dts-extractor": 0.21.6 adm-zip: 0.5.16 ansi-colors: 4.1.3 axios: 1.13.6 @@ -2029,26 +5363,26 @@ snapshots: - supports-color - utf-8-validate - '@module-federation/enhanced@0.21.6(@rspack/core@1.7.9)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.4.5)': - dependencies: - '@module-federation/bridge-react-webpack-plugin': 0.21.6 - '@module-federation/cli': 0.21.6(typescript@5.4.5) - '@module-federation/data-prefetch': 0.21.6(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@module-federation/dts-plugin': 0.21.6(typescript@5.4.5) - '@module-federation/error-codes': 0.21.6 - '@module-federation/inject-external-runtime-core-plugin': 0.21.6(@module-federation/runtime-tools@0.21.6) - '@module-federation/managers': 0.21.6 - '@module-federation/manifest': 0.21.6(typescript@5.4.5) - '@module-federation/rspack': 0.21.6(@rspack/core@1.7.9)(typescript@5.4.5) - '@module-federation/runtime-tools': 0.21.6 - '@module-federation/sdk': 0.21.6 + "@module-federation/enhanced@0.21.6(@rspack/core@1.7.9)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.4.5)": + dependencies: + "@module-federation/bridge-react-webpack-plugin": 0.21.6 + "@module-federation/cli": 0.21.6(typescript@5.4.5) + "@module-federation/data-prefetch": 0.21.6(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + "@module-federation/dts-plugin": 0.21.6(typescript@5.4.5) + "@module-federation/error-codes": 0.21.6 + "@module-federation/inject-external-runtime-core-plugin": 0.21.6(@module-federation/runtime-tools@0.21.6) + "@module-federation/managers": 0.21.6 + "@module-federation/manifest": 0.21.6(typescript@5.4.5) + "@module-federation/rspack": 0.21.6(@rspack/core@1.7.9)(typescript@5.4.5) + "@module-federation/runtime-tools": 0.21.6 + "@module-federation/sdk": 0.21.6 btoa: 1.2.1 schema-utils: 4.3.3 upath: 2.0.1 optionalDependencies: typescript: 5.4.5 transitivePeerDependencies: - - '@rspack/core' + - "@rspack/core" - bufferutil - debug - react @@ -2056,27 +5390,27 @@ snapshots: - supports-color - utf-8-validate - '@module-federation/error-codes@0.17.1': {} + "@module-federation/error-codes@0.17.1": {} - '@module-federation/error-codes@0.21.6': {} + "@module-federation/error-codes@0.21.6": {} - '@module-federation/error-codes@0.22.0': {} + "@module-federation/error-codes@0.22.0": {} - '@module-federation/inject-external-runtime-core-plugin@0.21.6(@module-federation/runtime-tools@0.21.6)': + "@module-federation/inject-external-runtime-core-plugin@0.21.6(@module-federation/runtime-tools@0.21.6)": dependencies: - '@module-federation/runtime-tools': 0.21.6 + "@module-federation/runtime-tools": 0.21.6 - '@module-federation/managers@0.21.6': + "@module-federation/managers@0.21.6": dependencies: - '@module-federation/sdk': 0.21.6 + "@module-federation/sdk": 0.21.6 find-pkg: 2.0.0 fs-extra: 9.1.0 - '@module-federation/manifest@0.21.6(typescript@5.4.5)': + "@module-federation/manifest@0.21.6(typescript@5.4.5)": dependencies: - '@module-federation/dts-plugin': 0.21.6(typescript@5.4.5) - '@module-federation/managers': 0.21.6 - '@module-federation/sdk': 0.21.6 + "@module-federation/dts-plugin": 0.21.6(typescript@5.4.5) + "@module-federation/managers": 0.21.6 + "@module-federation/sdk": 0.21.6 chalk: 3.0.0 find-pkg: 2.0.0 transitivePeerDependencies: @@ -2087,16 +5421,16 @@ snapshots: - utf-8-validate - vue-tsc - '@module-federation/rspack@0.21.6(@rspack/core@1.7.9)(typescript@5.4.5)': + "@module-federation/rspack@0.21.6(@rspack/core@1.7.9)(typescript@5.4.5)": dependencies: - '@module-federation/bridge-react-webpack-plugin': 0.21.6 - '@module-federation/dts-plugin': 0.21.6(typescript@5.4.5) - '@module-federation/inject-external-runtime-core-plugin': 0.21.6(@module-federation/runtime-tools@0.21.6) - '@module-federation/managers': 0.21.6 - '@module-federation/manifest': 0.21.6(typescript@5.4.5) - '@module-federation/runtime-tools': 0.21.6 - '@module-federation/sdk': 0.21.6 - '@rspack/core': 1.7.9 + "@module-federation/bridge-react-webpack-plugin": 0.21.6 + "@module-federation/dts-plugin": 0.21.6(typescript@5.4.5) + "@module-federation/inject-external-runtime-core-plugin": 0.21.6(@module-federation/runtime-tools@0.21.6) + "@module-federation/managers": 0.21.6 + "@module-federation/manifest": 0.21.6(typescript@5.4.5) + "@module-federation/runtime-tools": 0.21.6 + "@module-federation/sdk": 0.21.6 + "@rspack/core": 1.7.9 btoa: 1.2.1 optionalDependencies: typescript: 5.4.5 @@ -2106,65 +5440,65 @@ snapshots: - supports-color - utf-8-validate - '@module-federation/runtime-core@0.17.1': + "@module-federation/runtime-core@0.17.1": dependencies: - '@module-federation/error-codes': 0.17.1 - '@module-federation/sdk': 0.17.1 + "@module-federation/error-codes": 0.17.1 + "@module-federation/sdk": 0.17.1 - '@module-federation/runtime-core@0.21.6': + "@module-federation/runtime-core@0.21.6": dependencies: - '@module-federation/error-codes': 0.21.6 - '@module-federation/sdk': 0.21.6 + "@module-federation/error-codes": 0.21.6 + "@module-federation/sdk": 0.21.6 - '@module-federation/runtime-core@0.22.0': + "@module-federation/runtime-core@0.22.0": dependencies: - '@module-federation/error-codes': 0.22.0 - '@module-federation/sdk': 0.22.0 + "@module-federation/error-codes": 0.22.0 + "@module-federation/sdk": 0.22.0 - '@module-federation/runtime-tools@0.21.6': + "@module-federation/runtime-tools@0.21.6": dependencies: - '@module-federation/runtime': 0.21.6 - '@module-federation/webpack-bundler-runtime': 0.21.6 + "@module-federation/runtime": 0.21.6 + "@module-federation/webpack-bundler-runtime": 0.21.6 - '@module-federation/runtime-tools@0.22.0': + "@module-federation/runtime-tools@0.22.0": dependencies: - '@module-federation/runtime': 0.22.0 - '@module-federation/webpack-bundler-runtime': 0.22.0 + "@module-federation/runtime": 0.22.0 + "@module-federation/webpack-bundler-runtime": 0.22.0 - '@module-federation/runtime@0.17.1': + "@module-federation/runtime@0.17.1": dependencies: - '@module-federation/error-codes': 0.17.1 - '@module-federation/runtime-core': 0.17.1 - '@module-federation/sdk': 0.17.1 + "@module-federation/error-codes": 0.17.1 + "@module-federation/runtime-core": 0.17.1 + "@module-federation/sdk": 0.17.1 - '@module-federation/runtime@0.21.6': + "@module-federation/runtime@0.21.6": dependencies: - '@module-federation/error-codes': 0.21.6 - '@module-federation/runtime-core': 0.21.6 - '@module-federation/sdk': 0.21.6 + "@module-federation/error-codes": 0.21.6 + "@module-federation/runtime-core": 0.21.6 + "@module-federation/sdk": 0.21.6 - '@module-federation/runtime@0.22.0': + "@module-federation/runtime@0.22.0": dependencies: - '@module-federation/error-codes': 0.22.0 - '@module-federation/runtime-core': 0.22.0 - '@module-federation/sdk': 0.22.0 + "@module-federation/error-codes": 0.22.0 + "@module-federation/runtime-core": 0.22.0 + "@module-federation/sdk": 0.22.0 - '@module-federation/sdk@0.17.1': {} + "@module-federation/sdk@0.17.1": {} - '@module-federation/sdk@0.21.6': {} + "@module-federation/sdk@0.21.6": {} - '@module-federation/sdk@0.22.0': {} + "@module-federation/sdk@0.22.0": {} - '@module-federation/third-party-dts-extractor@0.21.6': + "@module-federation/third-party-dts-extractor@0.21.6": dependencies: find-pkg: 2.0.0 fs-extra: 9.1.0 resolve: 1.22.8 - '@module-federation/vite@1.7.1(rollup@4.59.0)': + "@module-federation/vite@1.7.1(rollup@4.59.0)": dependencies: - '@module-federation/runtime': 0.17.1 - '@rollup/pluginutils': 5.3.0(rollup@4.59.0) + "@module-federation/runtime": 0.17.1 + "@rollup/pluginutils": 5.3.0(rollup@4.59.0) defu: 6.1.4 estree-walker: 2.0.2 magic-string: 0.30.21 @@ -2172,301 +5506,744 @@ snapshots: transitivePeerDependencies: - rollup - '@module-federation/webpack-bundler-runtime@0.21.6': + "@module-federation/webpack-bundler-runtime@0.21.6": dependencies: - '@module-federation/runtime': 0.21.6 - '@module-federation/sdk': 0.21.6 + "@module-federation/runtime": 0.21.6 + "@module-federation/sdk": 0.21.6 - '@module-federation/webpack-bundler-runtime@0.22.0': + "@module-federation/webpack-bundler-runtime@0.22.0": dependencies: - '@module-federation/runtime': 0.22.0 - '@module-federation/sdk': 0.22.0 + "@module-federation/runtime": 0.22.0 + "@module-federation/sdk": 0.22.0 - '@napi-rs/wasm-runtime@1.0.7': + "@napi-rs/wasm-runtime@1.0.7": dependencies: - '@emnapi/core': 1.9.0 - '@emnapi/runtime': 1.9.0 - '@tybys/wasm-util': 0.10.1 + "@emnapi/core": 1.9.0 + "@emnapi/runtime": 1.9.0 + "@tybys/wasm-util": 0.10.1 optional: true - '@polka/url@1.0.0-next.29': + "@nodelib/fs.scandir@2.1.5": + dependencies: + "@nodelib/fs.stat": 2.0.5 + run-parallel: 1.2.0 + + "@nodelib/fs.stat@2.0.5": {} + + "@nodelib/fs.walk@1.2.8": + dependencies: + "@nodelib/fs.scandir": 2.1.5 + fastq: 1.20.1 + + "@polka/url@1.0.0-next.29": optional: true - '@rollup/pluginutils@5.3.0(rollup@4.59.0)': + "@radix-ui/primitive@1.1.3": {} + + "@radix-ui/react-compose-refs@1.1.2(@types/react@18.2.79)(react@18.3.1)": + dependencies: + react: 18.3.1 + optionalDependencies: + "@types/react": 18.2.79 + + "@radix-ui/react-context@1.1.2(@types/react@18.2.79)(react@18.3.1)": + dependencies: + react: 18.3.1 + optionalDependencies: + "@types/react": 18.2.79 + + "@radix-ui/react-dialog@1.1.15(@types/react-dom@18.2.25)(@types/react@18.2.79)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)": + dependencies: + "@radix-ui/primitive": 1.1.3 + "@radix-ui/react-compose-refs": 1.1.2(@types/react@18.2.79)(react@18.3.1) + "@radix-ui/react-context": 1.1.2(@types/react@18.2.79)(react@18.3.1) + "@radix-ui/react-dismissable-layer": 1.1.11(@types/react-dom@18.2.25)(@types/react@18.2.79)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + "@radix-ui/react-focus-guards": 1.1.3(@types/react@18.2.79)(react@18.3.1) + "@radix-ui/react-focus-scope": 1.1.7(@types/react-dom@18.2.25)(@types/react@18.2.79)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + "@radix-ui/react-id": 1.1.1(@types/react@18.2.79)(react@18.3.1) + "@radix-ui/react-portal": 1.1.9(@types/react-dom@18.2.25)(@types/react@18.2.79)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + "@radix-ui/react-presence": 1.1.5(@types/react-dom@18.2.25)(@types/react@18.2.79)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + "@radix-ui/react-primitive": 2.1.3(@types/react-dom@18.2.25)(@types/react@18.2.79)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + "@radix-ui/react-slot": 1.2.3(@types/react@18.2.79)(react@18.3.1) + "@radix-ui/react-use-controllable-state": 1.2.2(@types/react@18.2.79)(react@18.3.1) + aria-hidden: 1.2.6 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + react-remove-scroll: 2.7.2(@types/react@18.2.79)(react@18.3.1) + optionalDependencies: + "@types/react": 18.2.79 + "@types/react-dom": 18.2.25 + + "@radix-ui/react-dismissable-layer@1.1.11(@types/react-dom@18.2.25)(@types/react@18.2.79)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)": + dependencies: + "@radix-ui/primitive": 1.1.3 + "@radix-ui/react-compose-refs": 1.1.2(@types/react@18.2.79)(react@18.3.1) + "@radix-ui/react-primitive": 2.1.3(@types/react-dom@18.2.25)(@types/react@18.2.79)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + "@radix-ui/react-use-callback-ref": 1.1.1(@types/react@18.2.79)(react@18.3.1) + "@radix-ui/react-use-escape-keydown": 1.1.1(@types/react@18.2.79)(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + optionalDependencies: + "@types/react": 18.2.79 + "@types/react-dom": 18.2.25 + + "@radix-ui/react-focus-guards@1.1.3(@types/react@18.2.79)(react@18.3.1)": dependencies: - '@types/estree': 1.0.8 + react: 18.3.1 + optionalDependencies: + "@types/react": 18.2.79 + + "@radix-ui/react-focus-scope@1.1.7(@types/react-dom@18.2.25)(@types/react@18.2.79)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)": + dependencies: + "@radix-ui/react-compose-refs": 1.1.2(@types/react@18.2.79)(react@18.3.1) + "@radix-ui/react-primitive": 2.1.3(@types/react-dom@18.2.25)(@types/react@18.2.79)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + "@radix-ui/react-use-callback-ref": 1.1.1(@types/react@18.2.79)(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + optionalDependencies: + "@types/react": 18.2.79 + "@types/react-dom": 18.2.25 + + "@radix-ui/react-id@1.1.1(@types/react@18.2.79)(react@18.3.1)": + dependencies: + "@radix-ui/react-use-layout-effect": 1.1.1(@types/react@18.2.79)(react@18.3.1) + react: 18.3.1 + optionalDependencies: + "@types/react": 18.2.79 + + "@radix-ui/react-portal@1.1.9(@types/react-dom@18.2.25)(@types/react@18.2.79)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)": + dependencies: + "@radix-ui/react-primitive": 2.1.3(@types/react-dom@18.2.25)(@types/react@18.2.79)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + "@radix-ui/react-use-layout-effect": 1.1.1(@types/react@18.2.79)(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + optionalDependencies: + "@types/react": 18.2.79 + "@types/react-dom": 18.2.25 + + "@radix-ui/react-presence@1.1.5(@types/react-dom@18.2.25)(@types/react@18.2.79)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)": + dependencies: + "@radix-ui/react-compose-refs": 1.1.2(@types/react@18.2.79)(react@18.3.1) + "@radix-ui/react-use-layout-effect": 1.1.1(@types/react@18.2.79)(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + optionalDependencies: + "@types/react": 18.2.79 + "@types/react-dom": 18.2.25 + + "@radix-ui/react-primitive@2.1.3(@types/react-dom@18.2.25)(@types/react@18.2.79)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)": + dependencies: + "@radix-ui/react-slot": 1.2.3(@types/react@18.2.79)(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + optionalDependencies: + "@types/react": 18.2.79 + "@types/react-dom": 18.2.25 + + "@radix-ui/react-slot@1.2.3(@types/react@18.2.79)(react@18.3.1)": + dependencies: + "@radix-ui/react-compose-refs": 1.1.2(@types/react@18.2.79)(react@18.3.1) + react: 18.3.1 + optionalDependencies: + "@types/react": 18.2.79 + + "@radix-ui/react-slot@1.2.4(@types/react@18.2.79)(react@18.3.1)": + dependencies: + "@radix-ui/react-compose-refs": 1.1.2(@types/react@18.2.79)(react@18.3.1) + react: 18.3.1 + optionalDependencies: + "@types/react": 18.2.79 + + "@radix-ui/react-use-callback-ref@1.1.1(@types/react@18.2.79)(react@18.3.1)": + dependencies: + react: 18.3.1 + optionalDependencies: + "@types/react": 18.2.79 + + "@radix-ui/react-use-controllable-state@1.2.2(@types/react@18.2.79)(react@18.3.1)": + dependencies: + "@radix-ui/react-use-effect-event": 0.0.2(@types/react@18.2.79)(react@18.3.1) + "@radix-ui/react-use-layout-effect": 1.1.1(@types/react@18.2.79)(react@18.3.1) + react: 18.3.1 + optionalDependencies: + "@types/react": 18.2.79 + + "@radix-ui/react-use-effect-event@0.0.2(@types/react@18.2.79)(react@18.3.1)": + dependencies: + "@radix-ui/react-use-layout-effect": 1.1.1(@types/react@18.2.79)(react@18.3.1) + react: 18.3.1 + optionalDependencies: + "@types/react": 18.2.79 + + "@radix-ui/react-use-escape-keydown@1.1.1(@types/react@18.2.79)(react@18.3.1)": + dependencies: + "@radix-ui/react-use-callback-ref": 1.1.1(@types/react@18.2.79)(react@18.3.1) + react: 18.3.1 + optionalDependencies: + "@types/react": 18.2.79 + + "@radix-ui/react-use-layout-effect@1.1.1(@types/react@18.2.79)(react@18.3.1)": + dependencies: + react: 18.3.1 + optionalDependencies: + "@types/react": 18.2.79 + + "@rollup/pluginutils@5.3.0(rollup@4.59.0)": + dependencies: + "@types/estree": 1.0.8 estree-walker: 2.0.2 picomatch: 4.0.3 optionalDependencies: rollup: 4.59.0 - '@rollup/rollup-android-arm-eabi@4.59.0': + "@rollup/rollup-android-arm-eabi@4.59.0": optional: true - '@rollup/rollup-android-arm64@4.59.0': + "@rollup/rollup-android-arm64@4.59.0": optional: true - '@rollup/rollup-darwin-arm64@4.59.0': + "@rollup/rollup-darwin-arm64@4.59.0": optional: true - '@rollup/rollup-darwin-x64@4.59.0': + "@rollup/rollup-darwin-x64@4.59.0": optional: true - '@rollup/rollup-freebsd-arm64@4.59.0': + "@rollup/rollup-freebsd-arm64@4.59.0": optional: true - '@rollup/rollup-freebsd-x64@4.59.0': + "@rollup/rollup-freebsd-x64@4.59.0": optional: true - '@rollup/rollup-linux-arm-gnueabihf@4.59.0': + "@rollup/rollup-linux-arm-gnueabihf@4.59.0": optional: true - '@rollup/rollup-linux-arm-musleabihf@4.59.0': + "@rollup/rollup-linux-arm-musleabihf@4.59.0": optional: true - '@rollup/rollup-linux-arm64-gnu@4.59.0': + "@rollup/rollup-linux-arm64-gnu@4.59.0": optional: true - '@rollup/rollup-linux-arm64-musl@4.59.0': + "@rollup/rollup-linux-arm64-musl@4.59.0": optional: true - '@rollup/rollup-linux-loong64-gnu@4.59.0': + "@rollup/rollup-linux-loong64-gnu@4.59.0": optional: true - '@rollup/rollup-linux-loong64-musl@4.59.0': + "@rollup/rollup-linux-loong64-musl@4.59.0": optional: true - '@rollup/rollup-linux-ppc64-gnu@4.59.0': + "@rollup/rollup-linux-ppc64-gnu@4.59.0": optional: true - '@rollup/rollup-linux-ppc64-musl@4.59.0': + "@rollup/rollup-linux-ppc64-musl@4.59.0": optional: true - '@rollup/rollup-linux-riscv64-gnu@4.59.0': + "@rollup/rollup-linux-riscv64-gnu@4.59.0": optional: true - '@rollup/rollup-linux-riscv64-musl@4.59.0': + "@rollup/rollup-linux-riscv64-musl@4.59.0": optional: true - '@rollup/rollup-linux-s390x-gnu@4.59.0': + "@rollup/rollup-linux-s390x-gnu@4.59.0": optional: true - '@rollup/rollup-linux-x64-gnu@4.59.0': + "@rollup/rollup-linux-x64-gnu@4.59.0": optional: true - '@rollup/rollup-linux-x64-musl@4.59.0': + "@rollup/rollup-linux-x64-musl@4.59.0": optional: true - '@rollup/rollup-openbsd-x64@4.59.0': + "@rollup/rollup-openbsd-x64@4.59.0": optional: true - '@rollup/rollup-openharmony-arm64@4.59.0': + "@rollup/rollup-openharmony-arm64@4.59.0": optional: true - '@rollup/rollup-win32-arm64-msvc@4.59.0': + "@rollup/rollup-win32-arm64-msvc@4.59.0": optional: true - '@rollup/rollup-win32-ia32-msvc@4.59.0': + "@rollup/rollup-win32-ia32-msvc@4.59.0": optional: true - '@rollup/rollup-win32-x64-gnu@4.59.0': + "@rollup/rollup-win32-x64-gnu@4.59.0": optional: true - '@rollup/rollup-win32-x64-msvc@4.59.0': + "@rollup/rollup-win32-x64-msvc@4.59.0": optional: true - '@rspack/binding-darwin-arm64@1.7.9': + "@rspack/binding-darwin-arm64@1.7.9": optional: true - '@rspack/binding-darwin-x64@1.7.9': + "@rspack/binding-darwin-x64@1.7.9": optional: true - '@rspack/binding-linux-arm64-gnu@1.7.9': + "@rspack/binding-linux-arm64-gnu@1.7.9": optional: true - '@rspack/binding-linux-arm64-musl@1.7.9': + "@rspack/binding-linux-arm64-musl@1.7.9": optional: true - '@rspack/binding-linux-x64-gnu@1.7.9': + "@rspack/binding-linux-x64-gnu@1.7.9": optional: true - '@rspack/binding-linux-x64-musl@1.7.9': + "@rspack/binding-linux-x64-musl@1.7.9": optional: true - '@rspack/binding-wasm32-wasi@1.7.9': + "@rspack/binding-wasm32-wasi@1.7.9": dependencies: - '@napi-rs/wasm-runtime': 1.0.7 + "@napi-rs/wasm-runtime": 1.0.7 + optional: true + + "@rspack/binding-win32-arm64-msvc@1.7.9": + optional: true + + "@rspack/binding-win32-ia32-msvc@1.7.9": + optional: true + + "@rspack/binding-win32-x64-msvc@1.7.9": optional: true - '@rspack/binding-win32-arm64-msvc@1.7.9': + "@rspack/binding@1.7.9": + optionalDependencies: + "@rspack/binding-darwin-arm64": 1.7.9 + "@rspack/binding-darwin-x64": 1.7.9 + "@rspack/binding-linux-arm64-gnu": 1.7.9 + "@rspack/binding-linux-arm64-musl": 1.7.9 + "@rspack/binding-linux-x64-gnu": 1.7.9 + "@rspack/binding-linux-x64-musl": 1.7.9 + "@rspack/binding-wasm32-wasi": 1.7.9 + "@rspack/binding-win32-arm64-msvc": 1.7.9 + "@rspack/binding-win32-ia32-msvc": 1.7.9 + "@rspack/binding-win32-x64-msvc": 1.7.9 + + "@rspack/core@1.7.9": + dependencies: + "@module-federation/runtime-tools": 0.22.0 + "@rspack/binding": 1.7.9 + "@rspack/lite-tapable": 1.1.0 + + "@rspack/lite-tapable@1.1.0": {} + + "@tailwindcss/node@4.2.2": + dependencies: + "@jridgewell/remapping": 2.3.5 + enhanced-resolve: 5.20.1 + jiti: 2.6.1 + lightningcss: 1.32.0 + magic-string: 0.30.21 + source-map-js: 1.2.1 + tailwindcss: 4.2.2 + + "@tailwindcss/oxide-android-arm64@4.2.2": optional: true - '@rspack/binding-win32-ia32-msvc@1.7.9': + "@tailwindcss/oxide-darwin-arm64@4.2.2": optional: true - '@rspack/binding-win32-x64-msvc@1.7.9': + "@tailwindcss/oxide-darwin-x64@4.2.2": optional: true - '@rspack/binding@1.7.9': + "@tailwindcss/oxide-freebsd-x64@4.2.2": + optional: true + + "@tailwindcss/oxide-linux-arm-gnueabihf@4.2.2": + optional: true + + "@tailwindcss/oxide-linux-arm64-gnu@4.2.2": + optional: true + + "@tailwindcss/oxide-linux-arm64-musl@4.2.2": + optional: true + + "@tailwindcss/oxide-linux-x64-gnu@4.2.2": + optional: true + + "@tailwindcss/oxide-linux-x64-musl@4.2.2": + optional: true + + "@tailwindcss/oxide-wasm32-wasi@4.2.2": + optional: true + + "@tailwindcss/oxide-win32-arm64-msvc@4.2.2": + optional: true + + "@tailwindcss/oxide-win32-x64-msvc@4.2.2": + optional: true + + "@tailwindcss/oxide@4.2.2": optionalDependencies: - '@rspack/binding-darwin-arm64': 1.7.9 - '@rspack/binding-darwin-x64': 1.7.9 - '@rspack/binding-linux-arm64-gnu': 1.7.9 - '@rspack/binding-linux-arm64-musl': 1.7.9 - '@rspack/binding-linux-x64-gnu': 1.7.9 - '@rspack/binding-linux-x64-musl': 1.7.9 - '@rspack/binding-wasm32-wasi': 1.7.9 - '@rspack/binding-win32-arm64-msvc': 1.7.9 - '@rspack/binding-win32-ia32-msvc': 1.7.9 - '@rspack/binding-win32-x64-msvc': 1.7.9 - - '@rspack/core@1.7.9': - dependencies: - '@module-federation/runtime-tools': 0.22.0 - '@rspack/binding': 1.7.9 - '@rspack/lite-tapable': 1.1.0 - - '@rspack/lite-tapable@1.1.0': {} - - '@testing-library/dom@10.4.1': - dependencies: - '@babel/code-frame': 7.29.0 - '@babel/runtime': 7.29.2 - '@types/aria-query': 5.0.4 + "@tailwindcss/oxide-android-arm64": 4.2.2 + "@tailwindcss/oxide-darwin-arm64": 4.2.2 + "@tailwindcss/oxide-darwin-x64": 4.2.2 + "@tailwindcss/oxide-freebsd-x64": 4.2.2 + "@tailwindcss/oxide-linux-arm-gnueabihf": 4.2.2 + "@tailwindcss/oxide-linux-arm64-gnu": 4.2.2 + "@tailwindcss/oxide-linux-arm64-musl": 4.2.2 + "@tailwindcss/oxide-linux-x64-gnu": 4.2.2 + "@tailwindcss/oxide-linux-x64-musl": 4.2.2 + "@tailwindcss/oxide-wasm32-wasi": 4.2.2 + "@tailwindcss/oxide-win32-arm64-msvc": 4.2.2 + "@tailwindcss/oxide-win32-x64-msvc": 4.2.2 + + "@tailwindcss/typography@0.5.19(tailwindcss@4.2.2)": + dependencies: + postcss-selector-parser: 6.0.10 + tailwindcss: 4.2.2 + + "@tailwindcss/vite@4.2.2(vite@5.2.10(@types/node@22.19.15)(lightningcss@1.32.0))": + dependencies: + "@tailwindcss/node": 4.2.2 + "@tailwindcss/oxide": 4.2.2 + tailwindcss: 4.2.2 + vite: 5.2.10(@types/node@22.19.15)(lightningcss@1.32.0) + + "@tanstack/history@1.161.6": {} + + "@tanstack/react-router@1.168.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1)": + dependencies: + "@tanstack/history": 1.161.6 + "@tanstack/react-store": 0.9.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + "@tanstack/router-core": 1.168.1 + isbot: 5.1.36 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + tiny-invariant: 1.3.3 + tiny-warning: 1.0.3 + + "@tanstack/react-store@0.9.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1)": + dependencies: + "@tanstack/store": 0.9.2 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + use-sync-external-store: 1.6.0(react@18.3.1) + + "@tanstack/router-core@1.168.1": + dependencies: + "@tanstack/history": 1.161.6 + cookie-es: 2.0.0 + seroval: 1.5.1 + seroval-plugins: 1.5.1(seroval@1.5.1) + tiny-invariant: 1.3.3 + tiny-warning: 1.0.3 + + "@tanstack/store@0.9.2": {} + + "@testing-library/dom@10.4.1": + dependencies: + "@babel/code-frame": 7.29.0 + "@babel/runtime": 7.29.2 + "@types/aria-query": 5.0.4 aria-query: 5.3.0 dom-accessibility-api: 0.5.16 lz-string: 1.5.0 picocolors: 1.1.1 pretty-format: 27.5.1 - '@testing-library/jest-dom@6.9.1': + "@testing-library/jest-dom@6.9.1": dependencies: - '@adobe/css-tools': 4.4.4 + "@adobe/css-tools": 4.4.4 aria-query: 5.3.2 css.escape: 1.5.1 dom-accessibility-api: 0.6.3 picocolors: 1.1.1 redent: 3.0.0 - '@testing-library/react@16.3.2(@testing-library/dom@10.4.1)(@types/react-dom@18.2.25)(@types/react@18.2.79)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + "@testing-library/react@16.3.2(@testing-library/dom@10.4.1)(@types/react-dom@18.2.25)(@types/react@18.2.79)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)": dependencies: - '@babel/runtime': 7.29.2 - '@testing-library/dom': 10.4.1 + "@babel/runtime": 7.29.2 + "@testing-library/dom": 10.4.1 react: 18.3.1 react-dom: 18.3.1(react@18.3.1) optionalDependencies: - '@types/react': 18.2.79 - '@types/react-dom': 18.2.25 + "@types/react": 18.2.79 + "@types/react-dom": 18.2.25 - '@tybys/wasm-util@0.10.1': + "@tybys/wasm-util@0.10.1": dependencies: tslib: 2.8.1 optional: true - '@types/aria-query@5.0.4': {} + "@types/aria-query@5.0.4": {} - '@types/babel__core@7.20.5': + "@types/babel__core@7.20.5": dependencies: - '@babel/parser': 7.29.2 - '@babel/types': 7.29.0 - '@types/babel__generator': 7.27.0 - '@types/babel__template': 7.4.4 - '@types/babel__traverse': 7.28.0 + "@babel/parser": 7.29.2 + "@babel/types": 7.29.0 + "@types/babel__generator": 7.27.0 + "@types/babel__template": 7.4.4 + "@types/babel__traverse": 7.28.0 - '@types/babel__generator@7.27.0': + "@types/babel__generator@7.27.0": dependencies: - '@babel/types': 7.29.0 + "@babel/types": 7.29.0 - '@types/babel__template@7.4.4': + "@types/babel__template@7.4.4": dependencies: - '@babel/parser': 7.29.2 - '@babel/types': 7.29.0 + "@babel/parser": 7.29.2 + "@babel/types": 7.29.0 - '@types/babel__traverse@7.28.0': + "@types/babel__traverse@7.28.0": dependencies: - '@babel/types': 7.29.0 + "@babel/types": 7.29.0 - '@types/estree@1.0.8': {} + "@types/estree@1.0.8": {} - '@types/json-schema@7.0.15': {} + "@types/json-schema@7.0.15": {} - '@types/node@22.19.15': + "@types/node@22.19.15": dependencies: undici-types: 6.21.0 - '@types/prop-types@15.7.15': {} + "@types/prop-types@15.7.15": {} - '@types/react-dom@18.2.25': + "@types/react-dom@18.2.25": dependencies: - '@types/react': 18.2.79 + "@types/react": 18.2.79 - '@types/react@18.2.79': + "@types/react@18.2.79": dependencies: - '@types/prop-types': 15.7.15 + "@types/prop-types": 15.7.15 csstype: 3.2.3 - '@types/semver@7.5.8': {} + "@types/semver@7.5.8": {} - '@vitejs/plugin-react@4.2.1(vite@5.2.10(@types/node@22.19.15))': + "@typescript-eslint/eslint-plugin@5.62.0(@typescript-eslint/parser@5.62.0(eslint@8.57.1)(typescript@5.4.5))(eslint@8.57.1)(typescript@5.4.5)": dependencies: - '@babel/core': 7.29.0 - '@babel/plugin-transform-react-jsx-self': 7.27.1(@babel/core@7.29.0) - '@babel/plugin-transform-react-jsx-source': 7.27.1(@babel/core@7.29.0) - '@types/babel__core': 7.20.5 + "@eslint-community/regexpp": 4.12.2 + "@typescript-eslint/parser": 5.62.0(eslint@8.57.1)(typescript@5.4.5) + "@typescript-eslint/scope-manager": 5.62.0 + "@typescript-eslint/type-utils": 5.62.0(eslint@8.57.1)(typescript@5.4.5) + "@typescript-eslint/utils": 5.62.0(eslint@8.57.1)(typescript@5.4.5) + debug: 4.4.3 + eslint: 8.57.1 + graphemer: 1.4.0 + ignore: 5.3.2 + natural-compare-lite: 1.4.0 + semver: 7.6.3 + tsutils: 3.21.0(typescript@5.4.5) + optionalDependencies: + typescript: 5.4.5 + transitivePeerDependencies: + - supports-color + + "@typescript-eslint/eslint-plugin@8.57.2(@typescript-eslint/parser@8.57.2(eslint@8.57.1)(typescript@5.4.5))(eslint@8.57.1)(typescript@5.4.5)": + dependencies: + "@eslint-community/regexpp": 4.12.2 + "@typescript-eslint/parser": 8.57.2(eslint@8.57.1)(typescript@5.4.5) + "@typescript-eslint/scope-manager": 8.57.2 + "@typescript-eslint/type-utils": 8.57.2(eslint@8.57.1)(typescript@5.4.5) + "@typescript-eslint/utils": 8.57.2(eslint@8.57.1)(typescript@5.4.5) + "@typescript-eslint/visitor-keys": 8.57.2 + eslint: 8.57.1 + ignore: 7.0.5 + natural-compare: 1.4.0 + ts-api-utils: 2.5.0(typescript@5.4.5) + typescript: 5.4.5 + transitivePeerDependencies: + - supports-color + + "@typescript-eslint/parser@5.62.0(eslint@8.57.1)(typescript@5.4.5)": + dependencies: + "@typescript-eslint/scope-manager": 5.62.0 + "@typescript-eslint/types": 5.62.0 + "@typescript-eslint/typescript-estree": 5.62.0(typescript@5.4.5) + debug: 4.4.3 + eslint: 8.57.1 + optionalDependencies: + typescript: 5.4.5 + transitivePeerDependencies: + - supports-color + + "@typescript-eslint/parser@8.57.2(eslint@8.57.1)(typescript@5.4.5)": + dependencies: + "@typescript-eslint/scope-manager": 8.57.2 + "@typescript-eslint/types": 8.57.2 + "@typescript-eslint/typescript-estree": 8.57.2(typescript@5.4.5) + "@typescript-eslint/visitor-keys": 8.57.2 + debug: 4.4.3 + eslint: 8.57.1 + typescript: 5.4.5 + transitivePeerDependencies: + - supports-color + + "@typescript-eslint/project-service@8.57.2(typescript@5.4.5)": + dependencies: + "@typescript-eslint/tsconfig-utils": 8.57.2(typescript@5.4.5) + "@typescript-eslint/types": 8.57.2 + debug: 4.4.3 + typescript: 5.4.5 + transitivePeerDependencies: + - supports-color + + "@typescript-eslint/scope-manager@5.62.0": + dependencies: + "@typescript-eslint/types": 5.62.0 + "@typescript-eslint/visitor-keys": 5.62.0 + + "@typescript-eslint/scope-manager@8.57.2": + dependencies: + "@typescript-eslint/types": 8.57.2 + "@typescript-eslint/visitor-keys": 8.57.2 + + "@typescript-eslint/tsconfig-utils@8.57.2(typescript@5.4.5)": + dependencies: + typescript: 5.4.5 + + "@typescript-eslint/type-utils@5.62.0(eslint@8.57.1)(typescript@5.4.5)": + dependencies: + "@typescript-eslint/typescript-estree": 5.62.0(typescript@5.4.5) + "@typescript-eslint/utils": 5.62.0(eslint@8.57.1)(typescript@5.4.5) + debug: 4.4.3 + eslint: 8.57.1 + tsutils: 3.21.0(typescript@5.4.5) + optionalDependencies: + typescript: 5.4.5 + transitivePeerDependencies: + - supports-color + + "@typescript-eslint/type-utils@8.57.2(eslint@8.57.1)(typescript@5.4.5)": + dependencies: + "@typescript-eslint/types": 8.57.2 + "@typescript-eslint/typescript-estree": 8.57.2(typescript@5.4.5) + "@typescript-eslint/utils": 8.57.2(eslint@8.57.1)(typescript@5.4.5) + debug: 4.4.3 + eslint: 8.57.1 + ts-api-utils: 2.5.0(typescript@5.4.5) + typescript: 5.4.5 + transitivePeerDependencies: + - supports-color + + "@typescript-eslint/types@5.62.0": {} + + "@typescript-eslint/types@8.57.2": {} + + "@typescript-eslint/typescript-estree@5.62.0(typescript@5.4.5)": + dependencies: + "@typescript-eslint/types": 5.62.0 + "@typescript-eslint/visitor-keys": 5.62.0 + debug: 4.4.3 + globby: 11.1.0 + is-glob: 4.0.3 + semver: 7.6.3 + tsutils: 3.21.0(typescript@5.4.5) + optionalDependencies: + typescript: 5.4.5 + transitivePeerDependencies: + - supports-color + + "@typescript-eslint/typescript-estree@8.57.2(typescript@5.4.5)": + dependencies: + "@typescript-eslint/project-service": 8.57.2(typescript@5.4.5) + "@typescript-eslint/tsconfig-utils": 8.57.2(typescript@5.4.5) + "@typescript-eslint/types": 8.57.2 + "@typescript-eslint/visitor-keys": 8.57.2 + debug: 4.4.3 + minimatch: 10.2.4 + semver: 7.7.4 + tinyglobby: 0.2.15 + ts-api-utils: 2.5.0(typescript@5.4.5) + typescript: 5.4.5 + transitivePeerDependencies: + - supports-color + + "@typescript-eslint/utils@5.62.0(eslint@8.57.1)(typescript@5.4.5)": + dependencies: + "@eslint-community/eslint-utils": 4.9.1(eslint@8.57.1) + "@types/json-schema": 7.0.15 + "@types/semver": 7.5.8 + "@typescript-eslint/scope-manager": 5.62.0 + "@typescript-eslint/types": 5.62.0 + "@typescript-eslint/typescript-estree": 5.62.0(typescript@5.4.5) + eslint: 8.57.1 + eslint-scope: 5.1.1 + semver: 7.6.3 + transitivePeerDependencies: + - supports-color + - typescript + + "@typescript-eslint/utils@8.57.2(eslint@8.57.1)(typescript@5.4.5)": + dependencies: + "@eslint-community/eslint-utils": 4.9.1(eslint@8.57.1) + "@typescript-eslint/scope-manager": 8.57.2 + "@typescript-eslint/types": 8.57.2 + "@typescript-eslint/typescript-estree": 8.57.2(typescript@5.4.5) + eslint: 8.57.1 + typescript: 5.4.5 + transitivePeerDependencies: + - supports-color + + "@typescript-eslint/visitor-keys@5.62.0": + dependencies: + "@typescript-eslint/types": 5.62.0 + eslint-visitor-keys: 3.4.3 + + "@typescript-eslint/visitor-keys@8.57.2": + dependencies: + "@typescript-eslint/types": 8.57.2 + eslint-visitor-keys: 5.0.1 + + "@ungap/structured-clone@1.3.0": {} + + "@vitejs/plugin-react@4.2.1(vite@5.2.10(@types/node@22.19.15)(lightningcss@1.32.0))": + dependencies: + "@babel/core": 7.29.0 + "@babel/plugin-transform-react-jsx-self": 7.27.1(@babel/core@7.29.0) + "@babel/plugin-transform-react-jsx-source": 7.27.1(@babel/core@7.29.0) + "@types/babel__core": 7.20.5 react-refresh: 0.14.2 - vite: 5.2.10(@types/node@22.19.15) + vite: 5.2.10(@types/node@22.19.15)(lightningcss@1.32.0) transitivePeerDependencies: - supports-color - '@vitest/expect@2.1.9': + "@vitest/expect@2.1.9": dependencies: - '@vitest/spy': 2.1.9 - '@vitest/utils': 2.1.9 + "@vitest/spy": 2.1.9 + "@vitest/utils": 2.1.9 chai: 5.3.3 tinyrainbow: 1.2.0 - '@vitest/mocker@2.1.9(vite@5.2.10(@types/node@22.19.15))': + "@vitest/mocker@2.1.9(vite@5.2.10(@types/node@22.19.15)(lightningcss@1.32.0))": dependencies: - '@vitest/spy': 2.1.9 + "@vitest/spy": 2.1.9 estree-walker: 3.0.3 magic-string: 0.30.21 optionalDependencies: - vite: 5.2.10(@types/node@22.19.15) + vite: 5.2.10(@types/node@22.19.15)(lightningcss@1.32.0) - '@vitest/pretty-format@2.1.9': + "@vitest/pretty-format@2.1.9": dependencies: tinyrainbow: 1.2.0 - '@vitest/runner@2.1.9': + "@vitest/runner@2.1.9": dependencies: - '@vitest/utils': 2.1.9 + "@vitest/utils": 2.1.9 pathe: 1.1.2 - '@vitest/snapshot@2.1.9': + "@vitest/snapshot@2.1.9": dependencies: - '@vitest/pretty-format': 2.1.9 + "@vitest/pretty-format": 2.1.9 magic-string: 0.30.21 pathe: 1.1.2 - '@vitest/spy@2.1.9': + "@vitest/spy@2.1.9": dependencies: tinyspy: 3.0.2 - '@vitest/ui@2.1.9(vitest@2.1.9)': + "@vitest/ui@2.1.9(vitest@2.1.9)": dependencies: - '@vitest/utils': 2.1.9 + "@vitest/utils": 2.1.9 fflate: 0.8.2 flatted: 3.4.2 pathe: 1.1.2 sirv: 3.0.2 tinyglobby: 0.2.15 tinyrainbow: 1.2.0 - vitest: 2.1.9(@types/node@22.19.15)(@vitest/ui@2.1.9)(jsdom@25.0.1) + vitest: 2.1.9(@types/node@22.19.15)(@vitest/ui@2.1.9)(jsdom@25.0.1)(lightningcss@1.32.0) optional: true - '@vitest/utils@2.1.9': + "@vitest/utils@2.1.9": dependencies: - '@vitest/pretty-format': 2.1.9 + "@vitest/pretty-format": 2.1.9 loupe: 3.2.1 tinyrainbow: 1.2.0 @@ -2475,6 +6252,12 @@ snapshots: mime-types: 2.1.35 negotiator: 0.6.3 + acorn-jsx@5.3.2(acorn@8.16.0): + dependencies: + acorn: 8.16.0 + + acorn@8.16.0: {} + adm-zip@0.5.16: {} agent-base@7.1.4: {} @@ -2488,6 +6271,13 @@ snapshots: ajv: 8.18.0 fast-deep-equal: 3.1.3 + ajv@6.14.0: + dependencies: + fast-deep-equal: 3.1.3 + fast-json-stable-stringify: 2.1.0 + json-schema-traverse: 0.4.1 + uri-js: 4.4.1 + ajv@8.18.0: dependencies: fast-deep-equal: 3.1.3 @@ -2505,12 +6295,20 @@ snapshots: ansi-styles@5.2.0: {} + argparse@2.0.1: {} + + aria-hidden@1.2.6: + dependencies: + tslib: 2.8.1 + aria-query@5.3.0: dependencies: dequal: 2.0.3 aria-query@5.3.2: {} + array-union@2.1.0: {} + assertion-error@2.0.1: {} asynckit@0.4.0: {} @@ -2525,8 +6323,25 @@ snapshots: transitivePeerDependencies: - debug + balanced-match@1.0.2: {} + + balanced-match@4.0.4: {} + baseline-browser-mapping@2.10.8: {} + brace-expansion@1.1.12: + dependencies: + balanced-match: 1.0.2 + concat-map: 0.0.1 + + brace-expansion@5.0.4: + dependencies: + balanced-match: 4.0.4 + + braces@3.0.3: + dependencies: + fill-range: 7.1.1 + browserslist@4.28.1: dependencies: baseline-browser-mapping: 2.10.8 @@ -2544,6 +6359,8 @@ snapshots: es-errors: 1.3.0 function-bind: 1.1.2 + callsites@3.1.0: {} + caniuse-lite@1.0.30001780: {} chai@5.3.3: @@ -2559,8 +6376,19 @@ snapshots: ansi-styles: 4.3.0 supports-color: 7.2.0 + chalk@4.1.2: + dependencies: + ansi-styles: 4.3.0 + supports-color: 7.2.0 + check-error@2.1.3: {} + class-variance-authority@0.7.1: + dependencies: + clsx: 2.1.1 + + clsx@2.1.1: {} + color-convert@2.0.1: dependencies: color-name: 1.1.4 @@ -2573,6 +6401,8 @@ snapshots: commander@11.1.0: {} + concat-map@0.0.1: {} + content-disposition@0.5.4: dependencies: safe-buffer: 5.2.1 @@ -2581,6 +6411,8 @@ snapshots: convert-source-map@2.0.0: {} + cookie-es@2.0.0: {} + cookies@0.9.1: dependencies: depd: 2.0.0 @@ -2590,11 +6422,19 @@ snapshots: dependencies: luxon: 3.7.2 + cross-spawn@7.0.6: + dependencies: + path-key: 3.1.1 + shebang-command: 2.0.0 + which: 2.0.2 + css.escape@1.5.1: {} + cssesc@3.0.0: {} + cssstyle@4.6.0: dependencies: - '@asamuzakjp/css-color': 3.2.0 + "@asamuzakjp/css-color": 3.2.0 rrweb-cssom: 0.8.0 csstype@3.2.3: {} @@ -2616,6 +6456,8 @@ snapshots: deep-equal@1.0.1: {} + deep-is@0.1.4: {} + defu@6.1.4: {} delayed-stream@1.0.0: {} @@ -2630,6 +6472,18 @@ snapshots: destroy@1.2.0: {} + detect-libc@2.1.2: {} + + detect-node-es@1.1.0: {} + + dir-glob@3.0.1: + dependencies: + path-type: 4.0.0 + + doctrine@3.0.0: + dependencies: + esutils: 2.0.3 + dom-accessibility-api@0.5.16: {} dom-accessibility-api@0.6.3: {} @@ -2646,6 +6500,11 @@ snapshots: encodeurl@2.0.0: {} + enhanced-resolve@5.20.1: + dependencies: + graceful-fs: 4.2.11 + tapable: 2.3.0 + entities@6.0.1: {} es-define-property@1.0.1: {} @@ -2667,39 +6526,126 @@ snapshots: esbuild@0.20.2: optionalDependencies: - '@esbuild/aix-ppc64': 0.20.2 - '@esbuild/android-arm': 0.20.2 - '@esbuild/android-arm64': 0.20.2 - '@esbuild/android-x64': 0.20.2 - '@esbuild/darwin-arm64': 0.20.2 - '@esbuild/darwin-x64': 0.20.2 - '@esbuild/freebsd-arm64': 0.20.2 - '@esbuild/freebsd-x64': 0.20.2 - '@esbuild/linux-arm': 0.20.2 - '@esbuild/linux-arm64': 0.20.2 - '@esbuild/linux-ia32': 0.20.2 - '@esbuild/linux-loong64': 0.20.2 - '@esbuild/linux-mips64el': 0.20.2 - '@esbuild/linux-ppc64': 0.20.2 - '@esbuild/linux-riscv64': 0.20.2 - '@esbuild/linux-s390x': 0.20.2 - '@esbuild/linux-x64': 0.20.2 - '@esbuild/netbsd-x64': 0.20.2 - '@esbuild/openbsd-x64': 0.20.2 - '@esbuild/sunos-x64': 0.20.2 - '@esbuild/win32-arm64': 0.20.2 - '@esbuild/win32-ia32': 0.20.2 - '@esbuild/win32-x64': 0.20.2 + "@esbuild/aix-ppc64": 0.20.2 + "@esbuild/android-arm": 0.20.2 + "@esbuild/android-arm64": 0.20.2 + "@esbuild/android-x64": 0.20.2 + "@esbuild/darwin-arm64": 0.20.2 + "@esbuild/darwin-x64": 0.20.2 + "@esbuild/freebsd-arm64": 0.20.2 + "@esbuild/freebsd-x64": 0.20.2 + "@esbuild/linux-arm": 0.20.2 + "@esbuild/linux-arm64": 0.20.2 + "@esbuild/linux-ia32": 0.20.2 + "@esbuild/linux-loong64": 0.20.2 + "@esbuild/linux-mips64el": 0.20.2 + "@esbuild/linux-ppc64": 0.20.2 + "@esbuild/linux-riscv64": 0.20.2 + "@esbuild/linux-s390x": 0.20.2 + "@esbuild/linux-x64": 0.20.2 + "@esbuild/netbsd-x64": 0.20.2 + "@esbuild/openbsd-x64": 0.20.2 + "@esbuild/sunos-x64": 0.20.2 + "@esbuild/win32-arm64": 0.20.2 + "@esbuild/win32-ia32": 0.20.2 + "@esbuild/win32-x64": 0.20.2 escalade@3.2.0: {} escape-html@1.0.3: {} + escape-string-regexp@4.0.0: {} + + eslint-plugin-react-hooks@4.6.2(eslint@8.57.1): + dependencies: + eslint: 8.57.1 + + eslint-plugin-react-refresh@0.4.26(eslint@8.57.1): + dependencies: + eslint: 8.57.1 + + eslint-scope@5.1.1: + dependencies: + esrecurse: 4.3.0 + estraverse: 4.3.0 + + eslint-scope@7.2.2: + dependencies: + esrecurse: 4.3.0 + estraverse: 5.3.0 + + eslint-visitor-keys@3.4.3: {} + + eslint-visitor-keys@5.0.1: {} + + eslint@8.57.1: + dependencies: + "@eslint-community/eslint-utils": 4.9.1(eslint@8.57.1) + "@eslint-community/regexpp": 4.12.2 + "@eslint/eslintrc": 2.1.4 + "@eslint/js": 8.57.1 + "@humanwhocodes/config-array": 0.13.0 + "@humanwhocodes/module-importer": 1.0.1 + "@nodelib/fs.walk": 1.2.8 + "@ungap/structured-clone": 1.3.0 + ajv: 6.14.0 + chalk: 4.1.2 + cross-spawn: 7.0.6 + debug: 4.4.3 + doctrine: 3.0.0 + escape-string-regexp: 4.0.0 + eslint-scope: 7.2.2 + eslint-visitor-keys: 3.4.3 + espree: 9.6.1 + esquery: 1.7.0 + esutils: 2.0.3 + fast-deep-equal: 3.1.3 + file-entry-cache: 6.0.1 + find-up: 5.0.0 + glob-parent: 6.0.2 + globals: 13.24.0 + graphemer: 1.4.0 + ignore: 5.3.2 + imurmurhash: 0.1.4 + is-glob: 4.0.3 + is-path-inside: 3.0.3 + js-yaml: 4.1.1 + json-stable-stringify-without-jsonify: 1.0.1 + levn: 0.4.1 + lodash.merge: 4.6.2 + minimatch: 3.1.5 + natural-compare: 1.4.0 + optionator: 0.9.4 + strip-ansi: 6.0.1 + text-table: 0.2.0 + transitivePeerDependencies: + - supports-color + + espree@9.6.1: + dependencies: + acorn: 8.16.0 + acorn-jsx: 5.3.2(acorn@8.16.0) + eslint-visitor-keys: 3.4.3 + + esquery@1.7.0: + dependencies: + estraverse: 5.3.0 + + esrecurse@4.3.0: + dependencies: + estraverse: 5.3.0 + + estraverse@4.3.0: {} + + estraverse@5.3.0: {} + estree-walker@2.0.2: {} estree-walker@3.0.3: dependencies: - '@types/estree': 1.0.8 + "@types/estree": 1.0.8 + + esutils@2.0.3: {} expand-tilde@2.0.2: dependencies: @@ -2709,16 +6655,39 @@ snapshots: fast-deep-equal@3.1.3: {} + fast-glob@3.3.3: + dependencies: + "@nodelib/fs.stat": 2.0.5 + "@nodelib/fs.walk": 1.2.8 + glob-parent: 5.1.2 + merge2: 1.4.1 + micromatch: 4.0.8 + + fast-json-stable-stringify@2.1.0: {} + + fast-levenshtein@2.0.6: {} + fast-uri@3.1.0: {} + fastq@1.20.1: + dependencies: + reusify: 1.1.0 + fdir@6.5.0(picomatch@4.0.3): optionalDependencies: picomatch: 4.0.3 - optional: true fflate@0.8.2: optional: true + file-entry-cache@6.0.1: + dependencies: + flat-cache: 3.2.0 + + fill-range@7.1.1: + dependencies: + to-regex-range: 5.0.1 + find-file-up@2.0.1: dependencies: resolve-dir: 1.0.1 @@ -2727,6 +6696,17 @@ snapshots: dependencies: find-file-up: 2.0.1 + find-up@5.0.0: + dependencies: + locate-path: 6.0.0 + path-exists: 4.0.0 + + flat-cache@3.2.0: + dependencies: + flatted: 3.4.2 + keyv: 4.5.4 + rimraf: 3.0.2 + flatted@3.4.2: {} follow-redirects@1.15.11: {} @@ -2754,6 +6734,8 @@ snapshots: jsonfile: 6.2.0 universalify: 2.0.1 + fs.realpath@1.0.0: {} + fsevents@2.3.3: optional: true @@ -2774,11 +6756,30 @@ snapshots: hasown: 2.0.2 math-intrinsics: 1.1.0 + get-nonce@1.0.1: {} + get-proto@1.0.1: dependencies: dunder-proto: 1.0.1 es-object-atoms: 1.1.1 + glob-parent@5.1.2: + dependencies: + is-glob: 4.0.3 + + glob-parent@6.0.2: + dependencies: + is-glob: 4.0.3 + + glob@7.2.3: + dependencies: + fs.realpath: 1.0.0 + inflight: 1.0.6 + inherits: 2.0.4 + minimatch: 3.1.5 + once: 1.4.0 + path-is-absolute: 1.0.1 + global-modules@1.0.0: dependencies: global-prefix: 1.0.2 @@ -2793,10 +6794,25 @@ snapshots: is-windows: 1.0.2 which: 1.3.1 + globals@13.24.0: + dependencies: + type-fest: 0.20.2 + + globby@11.1.0: + dependencies: + array-union: 2.1.0 + dir-glob: 3.0.1 + fast-glob: 3.3.3 + ignore: 5.3.2 + merge2: 1.4.1 + slash: 3.0.0 + gopd@1.2.0: {} graceful-fs@4.2.11: {} + graphemer@1.4.0: {} + has-flag@4.0.0: {} has-symbols@1.1.0: {} @@ -2817,6 +6833,10 @@ snapshots: dependencies: whatwg-encoding: 3.1.1 + html-parse-stringify@3.0.1: + dependencies: + void-elements: 3.1.0 + http-assert@1.5.0: dependencies: deep-equal: 1.0.1 @@ -2852,12 +6872,34 @@ snapshots: transitivePeerDependencies: - supports-color + i18next@25.10.2(typescript@5.4.5): + dependencies: + "@babel/runtime": 7.29.2 + optionalDependencies: + typescript: 5.4.5 + iconv-lite@0.6.3: dependencies: safer-buffer: 2.1.2 + ignore@5.3.2: {} + + ignore@7.0.5: {} + + import-fresh@3.3.1: + dependencies: + parent-module: 1.0.1 + resolve-from: 4.0.0 + + imurmurhash@0.1.4: {} + indent-string@4.0.0: {} + inflight@1.0.6: + dependencies: + once: 1.4.0 + wrappy: 1.0.2 + inherits@2.0.4: {} ini@1.3.8: {} @@ -2866,10 +6908,22 @@ snapshots: dependencies: hasown: 2.0.2 + is-extglob@2.1.1: {} + + is-glob@4.0.3: + dependencies: + is-extglob: 2.1.1 + + is-number@7.0.0: {} + + is-path-inside@3.0.3: {} + is-potential-custom-element-name@1.0.1: {} is-windows@1.0.2: {} + isbot@5.1.36: {} + isexe@2.0.0: {} isomorphic-ws@5.0.0(ws@8.18.0): @@ -2878,8 +6932,14 @@ snapshots: jiti@2.4.2: {} + jiti@2.6.1: {} + js-tokens@4.0.0: {} + js-yaml@4.1.1: + dependencies: + argparse: 2.0.1 + jsdom@25.0.1: dependencies: cssstyle: 4.6.0 @@ -2910,8 +6970,14 @@ snapshots: jsesc@3.1.0: {} + json-buffer@3.0.1: {} + + json-schema-traverse@0.4.1: {} + json-schema-traverse@1.0.0: {} + json-stable-stringify-without-jsonify@1.0.1: {} + json5@2.2.3: {} jsonfile@4.0.0: @@ -2928,6 +6994,10 @@ snapshots: dependencies: tsscmp: 1.0.6 + keyv@4.5.4: + dependencies: + json-buffer: 3.0.1 + koa-compose@4.1.0: {} koa@3.0.3: @@ -2951,8 +7021,68 @@ snapshots: type-is: 2.0.1 vary: 1.1.2 + levn@0.4.1: + dependencies: + prelude-ls: 1.2.1 + type-check: 0.4.0 + + lightningcss-android-arm64@1.32.0: + optional: true + + lightningcss-darwin-arm64@1.32.0: + optional: true + + lightningcss-darwin-x64@1.32.0: + optional: true + + lightningcss-freebsd-x64@1.32.0: + optional: true + + lightningcss-linux-arm-gnueabihf@1.32.0: + optional: true + + lightningcss-linux-arm64-gnu@1.32.0: + optional: true + + lightningcss-linux-arm64-musl@1.32.0: + optional: true + + lightningcss-linux-x64-gnu@1.32.0: + optional: true + + lightningcss-linux-x64-musl@1.32.0: + optional: true + + lightningcss-win32-arm64-msvc@1.32.0: + optional: true + + lightningcss-win32-x64-msvc@1.32.0: + optional: true + + lightningcss@1.32.0: + dependencies: + detect-libc: 2.1.2 + optionalDependencies: + lightningcss-android-arm64: 1.32.0 + lightningcss-darwin-arm64: 1.32.0 + lightningcss-darwin-x64: 1.32.0 + lightningcss-freebsd-x64: 1.32.0 + lightningcss-linux-arm-gnueabihf: 1.32.0 + lightningcss-linux-arm64-gnu: 1.32.0 + lightningcss-linux-arm64-musl: 1.32.0 + lightningcss-linux-x64-gnu: 1.32.0 + lightningcss-linux-x64-musl: 1.32.0 + lightningcss-win32-arm64-msvc: 1.32.0 + lightningcss-win32-x64-msvc: 1.32.0 + + locate-path@6.0.0: + dependencies: + p-locate: 5.0.0 + lodash.clonedeepwith@4.5.0: {} + lodash.merge@4.6.2: {} + log4js@6.9.1: dependencies: date-format: 4.0.14 @@ -2977,18 +7107,29 @@ snapshots: dependencies: yallist: 3.1.1 + lucide-react@0.552.0(react@18.3.1): + dependencies: + react: 18.3.1 + luxon@3.7.2: {} lz-string@1.5.0: {} magic-string@0.30.21: dependencies: - '@jridgewell/sourcemap-codec': 1.5.5 + "@jridgewell/sourcemap-codec": 1.5.5 math-intrinsics@1.1.0: {} media-typer@1.1.0: {} + merge2@1.4.1: {} + + micromatch@4.0.8: + dependencies: + braces: 3.0.3 + picomatch: 2.3.2 + mime-db@1.52.0: {} mime-db@1.54.0: {} @@ -3003,6 +7144,14 @@ snapshots: min-indent@1.0.1: {} + minimatch@10.2.4: + dependencies: + brace-expansion: 5.0.4 + + minimatch@3.1.5: + dependencies: + brace-expansion: 1.1.12 + mrmime@2.0.1: optional: true @@ -3010,8 +7159,17 @@ snapshots: nanoid@3.3.11: {} + natural-compare-lite@1.4.0: {} + + natural-compare@1.4.0: {} + negotiator@0.6.3: {} + next-themes@0.4.6(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + node-releases@2.0.36: {} node-schedule@2.1.1: @@ -3026,6 +7184,31 @@ snapshots: dependencies: ee-first: 1.1.1 + once@1.4.0: + dependencies: + wrappy: 1.0.2 + + optionator@0.9.4: + dependencies: + deep-is: 0.1.4 + fast-levenshtein: 2.0.6 + levn: 0.4.1 + prelude-ls: 1.2.1 + type-check: 0.4.0 + word-wrap: 1.2.5 + + p-limit@3.1.0: + dependencies: + yocto-queue: 0.1.0 + + p-locate@5.0.0: + dependencies: + p-limit: 3.1.0 + + parent-module@1.0.1: + dependencies: + callsites: 3.1.0 + parse-passwd@1.0.0: {} parse5@7.3.0: @@ -3034,22 +7217,41 @@ snapshots: parseurl@1.3.3: {} + path-exists@4.0.0: {} + + path-is-absolute@1.0.1: {} + + path-key@3.1.1: {} + path-parse@1.0.7: {} + path-type@4.0.0: {} + pathe@1.1.2: {} pathval@2.0.1: {} picocolors@1.1.1: {} + picomatch@2.3.2: {} + picomatch@4.0.3: {} + postcss-selector-parser@6.0.10: + dependencies: + cssesc: 3.0.0 + util-deprecate: 1.0.2 + postcss@8.5.8: dependencies: nanoid: 3.3.11 picocolors: 1.1.1 source-map-js: 1.2.1 + prelude-ls@1.2.1: {} + + prettier@3.6.2: {} + pretty-format@27.5.1: dependencies: ansi-regex: 5.0.1 @@ -3060,6 +7262,8 @@ snapshots: punycode@2.3.1: {} + queue-microtask@1.2.3: {} + rambda@9.4.2: {} react-dom@18.3.1(react@18.3.1): @@ -3068,10 +7272,48 @@ snapshots: react: 18.3.1 scheduler: 0.23.2 + react-i18next@16.6.0(i18next@25.10.2(typescript@5.4.5))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.4.5): + dependencies: + "@babel/runtime": 7.29.2 + html-parse-stringify: 3.0.1 + i18next: 25.10.2(typescript@5.4.5) + react: 18.3.1 + use-sync-external-store: 1.6.0(react@18.3.1) + optionalDependencies: + react-dom: 18.3.1(react@18.3.1) + typescript: 5.4.5 + react-is@17.0.2: {} react-refresh@0.14.2: {} + react-remove-scroll-bar@2.3.8(@types/react@18.2.79)(react@18.3.1): + dependencies: + react: 18.3.1 + react-style-singleton: 2.2.3(@types/react@18.2.79)(react@18.3.1) + tslib: 2.8.1 + optionalDependencies: + "@types/react": 18.2.79 + + react-remove-scroll@2.7.2(@types/react@18.2.79)(react@18.3.1): + dependencies: + react: 18.3.1 + react-remove-scroll-bar: 2.3.8(@types/react@18.2.79)(react@18.3.1) + react-style-singleton: 2.2.3(@types/react@18.2.79)(react@18.3.1) + tslib: 2.8.1 + use-callback-ref: 1.3.3(@types/react@18.2.79)(react@18.3.1) + use-sidecar: 1.1.3(@types/react@18.2.79)(react@18.3.1) + optionalDependencies: + "@types/react": 18.2.79 + + react-style-singleton@2.2.3(@types/react@18.2.79)(react@18.3.1): + dependencies: + get-nonce: 1.0.1 + react: 18.3.1 + tslib: 2.8.1 + optionalDependencies: + "@types/react": 18.2.79 + react@18.3.1: dependencies: loose-envify: 1.4.0 @@ -3088,49 +7330,61 @@ snapshots: expand-tilde: 2.0.2 global-modules: 1.0.0 + resolve-from@4.0.0: {} + resolve@1.22.8: dependencies: is-core-module: 2.16.1 path-parse: 1.0.7 supports-preserve-symlinks-flag: 1.0.0 + reusify@1.1.0: {} + rfdc@1.4.1: {} + rimraf@3.0.2: + dependencies: + glob: 7.2.3 + rollup@4.59.0: dependencies: - '@types/estree': 1.0.8 + "@types/estree": 1.0.8 optionalDependencies: - '@rollup/rollup-android-arm-eabi': 4.59.0 - '@rollup/rollup-android-arm64': 4.59.0 - '@rollup/rollup-darwin-arm64': 4.59.0 - '@rollup/rollup-darwin-x64': 4.59.0 - '@rollup/rollup-freebsd-arm64': 4.59.0 - '@rollup/rollup-freebsd-x64': 4.59.0 - '@rollup/rollup-linux-arm-gnueabihf': 4.59.0 - '@rollup/rollup-linux-arm-musleabihf': 4.59.0 - '@rollup/rollup-linux-arm64-gnu': 4.59.0 - '@rollup/rollup-linux-arm64-musl': 4.59.0 - '@rollup/rollup-linux-loong64-gnu': 4.59.0 - '@rollup/rollup-linux-loong64-musl': 4.59.0 - '@rollup/rollup-linux-ppc64-gnu': 4.59.0 - '@rollup/rollup-linux-ppc64-musl': 4.59.0 - '@rollup/rollup-linux-riscv64-gnu': 4.59.0 - '@rollup/rollup-linux-riscv64-musl': 4.59.0 - '@rollup/rollup-linux-s390x-gnu': 4.59.0 - '@rollup/rollup-linux-x64-gnu': 4.59.0 - '@rollup/rollup-linux-x64-musl': 4.59.0 - '@rollup/rollup-openbsd-x64': 4.59.0 - '@rollup/rollup-openharmony-arm64': 4.59.0 - '@rollup/rollup-win32-arm64-msvc': 4.59.0 - '@rollup/rollup-win32-ia32-msvc': 4.59.0 - '@rollup/rollup-win32-x64-gnu': 4.59.0 - '@rollup/rollup-win32-x64-msvc': 4.59.0 + "@rollup/rollup-android-arm-eabi": 4.59.0 + "@rollup/rollup-android-arm64": 4.59.0 + "@rollup/rollup-darwin-arm64": 4.59.0 + "@rollup/rollup-darwin-x64": 4.59.0 + "@rollup/rollup-freebsd-arm64": 4.59.0 + "@rollup/rollup-freebsd-x64": 4.59.0 + "@rollup/rollup-linux-arm-gnueabihf": 4.59.0 + "@rollup/rollup-linux-arm-musleabihf": 4.59.0 + "@rollup/rollup-linux-arm64-gnu": 4.59.0 + "@rollup/rollup-linux-arm64-musl": 4.59.0 + "@rollup/rollup-linux-loong64-gnu": 4.59.0 + "@rollup/rollup-linux-loong64-musl": 4.59.0 + "@rollup/rollup-linux-ppc64-gnu": 4.59.0 + "@rollup/rollup-linux-ppc64-musl": 4.59.0 + "@rollup/rollup-linux-riscv64-gnu": 4.59.0 + "@rollup/rollup-linux-riscv64-musl": 4.59.0 + "@rollup/rollup-linux-s390x-gnu": 4.59.0 + "@rollup/rollup-linux-x64-gnu": 4.59.0 + "@rollup/rollup-linux-x64-musl": 4.59.0 + "@rollup/rollup-openbsd-x64": 4.59.0 + "@rollup/rollup-openharmony-arm64": 4.59.0 + "@rollup/rollup-win32-arm64-msvc": 4.59.0 + "@rollup/rollup-win32-ia32-msvc": 4.59.0 + "@rollup/rollup-win32-x64-gnu": 4.59.0 + "@rollup/rollup-win32-x64-msvc": 4.59.0 fsevents: 2.3.3 rrweb-cssom@0.7.1: {} rrweb-cssom@0.8.0: {} + run-parallel@1.2.0: + dependencies: + queue-microtask: 1.2.3 + safe-buffer@5.2.1: {} safer-buffer@2.1.2: {} @@ -3145,7 +7399,7 @@ snapshots: schema-utils@4.3.3: dependencies: - '@types/json-schema': 7.0.15 + "@types/json-schema": 7.0.15 ajv: 8.18.0 ajv-formats: 2.1.1(ajv@8.18.0) ajv-keywords: 5.1.0(ajv@8.18.0) @@ -3154,17 +7408,69 @@ snapshots: semver@7.6.3: {} + semver@7.7.4: {} + + seroval-plugins@1.5.1(seroval@1.5.1): + dependencies: + seroval: 1.5.1 + + seroval@1.5.1: {} + setprototypeof@1.2.0: {} + sharp@0.34.5: + dependencies: + "@img/colour": 1.1.0 + detect-libc: 2.1.2 + semver: 7.7.4 + optionalDependencies: + "@img/sharp-darwin-arm64": 0.34.5 + "@img/sharp-darwin-x64": 0.34.5 + "@img/sharp-libvips-darwin-arm64": 1.2.4 + "@img/sharp-libvips-darwin-x64": 1.2.4 + "@img/sharp-libvips-linux-arm": 1.2.4 + "@img/sharp-libvips-linux-arm64": 1.2.4 + "@img/sharp-libvips-linux-ppc64": 1.2.4 + "@img/sharp-libvips-linux-riscv64": 1.2.4 + "@img/sharp-libvips-linux-s390x": 1.2.4 + "@img/sharp-libvips-linux-x64": 1.2.4 + "@img/sharp-libvips-linuxmusl-arm64": 1.2.4 + "@img/sharp-libvips-linuxmusl-x64": 1.2.4 + "@img/sharp-linux-arm": 0.34.5 + "@img/sharp-linux-arm64": 0.34.5 + "@img/sharp-linux-ppc64": 0.34.5 + "@img/sharp-linux-riscv64": 0.34.5 + "@img/sharp-linux-s390x": 0.34.5 + "@img/sharp-linux-x64": 0.34.5 + "@img/sharp-linuxmusl-arm64": 0.34.5 + "@img/sharp-linuxmusl-x64": 0.34.5 + "@img/sharp-wasm32": 0.34.5 + "@img/sharp-win32-arm64": 0.34.5 + "@img/sharp-win32-ia32": 0.34.5 + "@img/sharp-win32-x64": 0.34.5 + + shebang-command@2.0.0: + dependencies: + shebang-regex: 3.0.0 + + shebang-regex@3.0.0: {} + siginfo@2.0.0: {} sirv@3.0.2: dependencies: - '@polka/url': 1.0.0-next.29 + "@polka/url": 1.0.0-next.29 mrmime: 2.0.1 totalist: 3.0.1 optional: true + slash@3.0.0: {} + + sonner@2.0.7(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + sorted-array-functions@1.3.0: {} source-map-js@1.2.1: {} @@ -3185,10 +7491,16 @@ snapshots: transitivePeerDependencies: - supports-color + strip-ansi@6.0.1: + dependencies: + ansi-regex: 5.0.1 + strip-indent@3.0.0: dependencies: min-indent: 1.0.1 + strip-json-comments@3.1.1: {} + supports-color@7.2.0: dependencies: has-flag: 4.0.0 @@ -3197,6 +7509,18 @@ snapshots: symbol-tree@3.2.4: {} + tailwind-merge@3.5.0: {} + + tailwindcss@4.2.2: {} + + tapable@2.3.0: {} + + text-table@0.2.0: {} + + tiny-invariant@1.3.3: {} + + tiny-warning@1.0.3: {} + tinybench@2.9.0: {} tinyexec@0.3.2: {} @@ -3205,7 +7529,6 @@ snapshots: dependencies: fdir: 6.5.0(picomatch@4.0.3) picomatch: 4.0.3 - optional: true tinypool@1.1.1: {} @@ -3219,6 +7542,10 @@ snapshots: dependencies: tldts-core: 6.1.86 + to-regex-range@5.0.1: + dependencies: + is-number: 7.0.0 + toidentifier@1.0.1: {} totalist@3.0.1: @@ -3232,17 +7559,46 @@ snapshots: dependencies: punycode: 2.3.1 - tslib@2.8.1: - optional: true + ts-api-utils@2.5.0(typescript@5.4.5): + dependencies: + typescript: 5.4.5 + + tslib@1.14.1: {} + + tslib@2.8.1: {} tsscmp@1.0.6: {} + tsutils@3.21.0(typescript@5.4.5): + dependencies: + tslib: 1.14.1 + typescript: 5.4.5 + + tw-animate-css@1.4.0: {} + + type-check@0.4.0: + dependencies: + prelude-ls: 1.2.1 + + type-fest@0.20.2: {} + type-is@2.0.1: dependencies: content-type: 1.0.5 media-typer: 1.1.0 mime-types: 3.0.2 + typescript-eslint@8.57.2(eslint@8.57.1)(typescript@5.4.5): + dependencies: + "@typescript-eslint/eslint-plugin": 8.57.2(@typescript-eslint/parser@8.57.2(eslint@8.57.1)(typescript@5.4.5))(eslint@8.57.1)(typescript@5.4.5) + "@typescript-eslint/parser": 8.57.2(eslint@8.57.1)(typescript@5.4.5) + "@typescript-eslint/typescript-estree": 8.57.2(typescript@5.4.5) + "@typescript-eslint/utils": 8.57.2(eslint@8.57.1)(typescript@5.4.5) + eslint: 8.57.1 + typescript: 5.4.5 + transitivePeerDependencies: + - supports-color + typescript@5.4.5: {} undici-types@6.21.0: {} @@ -3259,17 +7615,51 @@ snapshots: escalade: 3.2.0 picocolors: 1.1.1 + uri-js@4.4.1: + dependencies: + punycode: 2.3.1 + + use-callback-ref@1.3.3(@types/react@18.2.79)(react@18.3.1): + dependencies: + react: 18.3.1 + tslib: 2.8.1 + optionalDependencies: + "@types/react": 18.2.79 + + use-sidecar@1.1.3(@types/react@18.2.79)(react@18.3.1): + dependencies: + detect-node-es: 1.1.0 + react: 18.3.1 + tslib: 2.8.1 + optionalDependencies: + "@types/react": 18.2.79 + + use-sync-external-store@1.6.0(react@18.3.1): + dependencies: + react: 18.3.1 + + util-deprecate@1.0.2: {} + vary@1.1.2: {} - vite-node@2.1.9(@types/node@22.19.15): + vaul@1.1.2(@types/react-dom@18.2.25)(@types/react@18.2.79)(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + "@radix-ui/react-dialog": 1.1.15(@types/react-dom@18.2.25)(@types/react@18.2.79)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + transitivePeerDependencies: + - "@types/react" + - "@types/react-dom" + + vite-node@2.1.9(@types/node@22.19.15)(lightningcss@1.32.0): dependencies: cac: 6.7.14 debug: 4.4.3 es-module-lexer: 1.7.0 pathe: 1.1.2 - vite: 5.2.10(@types/node@22.19.15) + vite: 5.2.10(@types/node@22.19.15)(lightningcss@1.32.0) transitivePeerDependencies: - - '@types/node' + - "@types/node" - less - lightningcss - sass @@ -3278,24 +7668,25 @@ snapshots: - supports-color - terser - vite@5.2.10(@types/node@22.19.15): + vite@5.2.10(@types/node@22.19.15)(lightningcss@1.32.0): dependencies: esbuild: 0.20.2 postcss: 8.5.8 rollup: 4.59.0 optionalDependencies: - '@types/node': 22.19.15 + "@types/node": 22.19.15 fsevents: 2.3.3 + lightningcss: 1.32.0 - vitest@2.1.9(@types/node@22.19.15)(@vitest/ui@2.1.9)(jsdom@25.0.1): + vitest@2.1.9(@types/node@22.19.15)(@vitest/ui@2.1.9)(jsdom@25.0.1)(lightningcss@1.32.0): dependencies: - '@vitest/expect': 2.1.9 - '@vitest/mocker': 2.1.9(vite@5.2.10(@types/node@22.19.15)) - '@vitest/pretty-format': 2.1.9 - '@vitest/runner': 2.1.9 - '@vitest/snapshot': 2.1.9 - '@vitest/spy': 2.1.9 - '@vitest/utils': 2.1.9 + "@vitest/expect": 2.1.9 + "@vitest/mocker": 2.1.9(vite@5.2.10(@types/node@22.19.15)(lightningcss@1.32.0)) + "@vitest/pretty-format": 2.1.9 + "@vitest/runner": 2.1.9 + "@vitest/snapshot": 2.1.9 + "@vitest/spy": 2.1.9 + "@vitest/utils": 2.1.9 chai: 5.3.3 debug: 4.4.3 expect-type: 1.3.0 @@ -3306,12 +7697,12 @@ snapshots: tinyexec: 0.3.2 tinypool: 1.1.1 tinyrainbow: 1.2.0 - vite: 5.2.10(@types/node@22.19.15) - vite-node: 2.1.9(@types/node@22.19.15) + vite: 5.2.10(@types/node@22.19.15)(lightningcss@1.32.0) + vite-node: 2.1.9(@types/node@22.19.15)(lightningcss@1.32.0) why-is-node-running: 2.3.0 optionalDependencies: - '@types/node': 22.19.15 - '@vitest/ui': 2.1.9(vitest@2.1.9) + "@types/node": 22.19.15 + "@vitest/ui": 2.1.9(vitest@2.1.9) jsdom: 25.0.1 transitivePeerDependencies: - less @@ -3323,6 +7714,8 @@ snapshots: - supports-color - terser + void-elements@3.1.0: {} + w3c-xmlserializer@5.0.0: dependencies: xml-name-validator: 5.0.0 @@ -3344,11 +7737,19 @@ snapshots: dependencies: isexe: 2.0.0 + which@2.0.2: + dependencies: + isexe: 2.0.0 + why-is-node-running@2.3.0: dependencies: siginfo: 2.0.0 stackback: 0.0.2 + word-wrap@1.2.5: {} + + wrappy@1.0.2: {} + ws@8.18.0: {} ws@8.19.0: {} @@ -3358,3 +7759,5 @@ snapshots: xmlchars@2.2.0: {} yallist@3.1.1: {} + + yocto-queue@0.1.0: {} diff --git a/rmcryptpad/ui/public/assets/cryptpad-mark.svg b/rmcryptpad/ui/public/assets/cryptpad-mark.svg new file mode 100644 index 0000000..0e6c24f --- /dev/null +++ b/rmcryptpad/ui/public/assets/cryptpad-mark.svg @@ -0,0 +1 @@ + diff --git a/rmcryptpad/ui/public/assets/cryptpad1.png b/rmcryptpad/ui/public/assets/cryptpad1.png new file mode 100644 index 0000000..56cbf8b Binary files /dev/null and b/rmcryptpad/ui/public/assets/cryptpad1.png differ diff --git a/rmcryptpad/ui/public/assets/cryptpad2.png b/rmcryptpad/ui/public/assets/cryptpad2.png new file mode 100644 index 0000000..9904218 Binary files /dev/null and b/rmcryptpad/ui/public/assets/cryptpad2.png differ diff --git a/rmcryptpad/ui/public/assets/cryptpad3.png b/rmcryptpad/ui/public/assets/cryptpad3.png new file mode 100644 index 0000000..61e16c6 Binary files /dev/null and b/rmcryptpad/ui/public/assets/cryptpad3.png differ diff --git a/rmcryptpad/ui/public/assets/cryptpad4.png b/rmcryptpad/ui/public/assets/cryptpad4.png new file mode 100644 index 0000000..794167b Binary files /dev/null and b/rmcryptpad/ui/public/assets/cryptpad4.png differ diff --git a/rmcryptpad/ui/public/assets/e2e.png b/rmcryptpad/ui/public/assets/e2e.png new file mode 100644 index 0000000..5e0356a Binary files /dev/null and b/rmcryptpad/ui/public/assets/e2e.png differ diff --git a/rmcryptpad/ui/public/assets/features/docs/docs1.png b/rmcryptpad/ui/public/assets/features/docs/docs1.png new file mode 100644 index 0000000..cc46a95 Binary files /dev/null and b/rmcryptpad/ui/public/assets/features/docs/docs1.png differ diff --git a/rmcryptpad/ui/public/assets/features/docs/docs2.png b/rmcryptpad/ui/public/assets/features/docs/docs2.png new file mode 100644 index 0000000..ef08d1b Binary files /dev/null and b/rmcryptpad/ui/public/assets/features/docs/docs2.png differ diff --git a/rmcryptpad/ui/public/assets/features/docs/docs3.png b/rmcryptpad/ui/public/assets/features/docs/docs3.png new file mode 100644 index 0000000..178ee74 Binary files /dev/null and b/rmcryptpad/ui/public/assets/features/docs/docs3.png differ diff --git a/rmcryptpad/ui/public/assets/features/kanban/kanban1.png b/rmcryptpad/ui/public/assets/features/kanban/kanban1.png new file mode 100644 index 0000000..2f228a6 Binary files /dev/null and b/rmcryptpad/ui/public/assets/features/kanban/kanban1.png differ diff --git a/rmcryptpad/ui/public/assets/features/kanban/kanban2.png b/rmcryptpad/ui/public/assets/features/kanban/kanban2.png new file mode 100644 index 0000000..cd16f61 Binary files /dev/null and b/rmcryptpad/ui/public/assets/features/kanban/kanban2.png differ diff --git a/rmcryptpad/ui/public/assets/features/kanban/kanban3.png b/rmcryptpad/ui/public/assets/features/kanban/kanban3.png new file mode 100644 index 0000000..b7f8503 Binary files /dev/null and b/rmcryptpad/ui/public/assets/features/kanban/kanban3.png differ diff --git a/rmcryptpad/ui/public/assets/features/kanban/kanban4.png b/rmcryptpad/ui/public/assets/features/kanban/kanban4.png new file mode 100644 index 0000000..f9e76d3 Binary files /dev/null and b/rmcryptpad/ui/public/assets/features/kanban/kanban4.png differ diff --git a/rmcryptpad/ui/public/assets/features/present/present1.png b/rmcryptpad/ui/public/assets/features/present/present1.png new file mode 100644 index 0000000..1f5cb67 Binary files /dev/null and b/rmcryptpad/ui/public/assets/features/present/present1.png differ diff --git a/rmcryptpad/ui/public/assets/features/present/present2.png b/rmcryptpad/ui/public/assets/features/present/present2.png new file mode 100644 index 0000000..77779bd Binary files /dev/null and b/rmcryptpad/ui/public/assets/features/present/present2.png differ diff --git a/rmcryptpad/ui/public/assets/features/unitUse/calender1.png b/rmcryptpad/ui/public/assets/features/unitUse/calender1.png new file mode 100644 index 0000000..d331d5a Binary files /dev/null and b/rmcryptpad/ui/public/assets/features/unitUse/calender1.png differ diff --git a/rmcryptpad/ui/public/assets/features/unitUse/calender2.png b/rmcryptpad/ui/public/assets/features/unitUse/calender2.png new file mode 100644 index 0000000..2ff0b52 Binary files /dev/null and b/rmcryptpad/ui/public/assets/features/unitUse/calender2.png differ diff --git a/rmcryptpad/ui/public/assets/features/unitUse/calender3.png b/rmcryptpad/ui/public/assets/features/unitUse/calender3.png new file mode 100644 index 0000000..2c16573 Binary files /dev/null and b/rmcryptpad/ui/public/assets/features/unitUse/calender3.png differ diff --git a/rmcryptpad/ui/public/assets/features/unitUse/end.png b/rmcryptpad/ui/public/assets/features/unitUse/end.png new file mode 100644 index 0000000..23800bc Binary files /dev/null and b/rmcryptpad/ui/public/assets/features/unitUse/end.png differ diff --git a/rmcryptpad/ui/public/assets/features/unitUse/setup1.png b/rmcryptpad/ui/public/assets/features/unitUse/setup1.png new file mode 100644 index 0000000..6ff54b6 Binary files /dev/null and b/rmcryptpad/ui/public/assets/features/unitUse/setup1.png differ diff --git a/rmcryptpad/ui/public/assets/features/unitUse/setup2.png b/rmcryptpad/ui/public/assets/features/unitUse/setup2.png new file mode 100644 index 0000000..2d20f93 Binary files /dev/null and b/rmcryptpad/ui/public/assets/features/unitUse/setup2.png differ diff --git a/rmcryptpad/ui/public/assets/features/unitUse/setup3.png b/rmcryptpad/ui/public/assets/features/unitUse/setup3.png new file mode 100644 index 0000000..b9faa44 Binary files /dev/null and b/rmcryptpad/ui/public/assets/features/unitUse/setup3.png differ diff --git a/rmcryptpad/ui/public/assets/features/unitUse/setup4.png b/rmcryptpad/ui/public/assets/features/unitUse/setup4.png new file mode 100644 index 0000000..a298b88 Binary files /dev/null and b/rmcryptpad/ui/public/assets/features/unitUse/setup4.png differ diff --git a/rmcryptpad/ui/public/assets/features/unitUse/setup5.png b/rmcryptpad/ui/public/assets/features/unitUse/setup5.png new file mode 100644 index 0000000..de41057 Binary files /dev/null and b/rmcryptpad/ui/public/assets/features/unitUse/setup5.png differ diff --git a/rmcryptpad/ui/public/assets/features/unitUse/setup6.png b/rmcryptpad/ui/public/assets/features/unitUse/setup6.png new file mode 100644 index 0000000..6b467ad Binary files /dev/null and b/rmcryptpad/ui/public/assets/features/unitUse/setup6.png differ diff --git a/rmcryptpad/ui/public/assets/features/unitUse/setup7.png b/rmcryptpad/ui/public/assets/features/unitUse/setup7.png new file mode 100644 index 0000000..642b211 Binary files /dev/null and b/rmcryptpad/ui/public/assets/features/unitUse/setup7.png differ diff --git a/rmcryptpad/ui/public/assets/features/unitUse/setup8.png b/rmcryptpad/ui/public/assets/features/unitUse/setup8.png new file mode 100644 index 0000000..4edc24d Binary files /dev/null and b/rmcryptpad/ui/public/assets/features/unitUse/setup8.png differ diff --git a/rmcryptpad/ui/public/assets/share.png b/rmcryptpad/ui/public/assets/share.png new file mode 100644 index 0000000..1f6514f Binary files /dev/null and b/rmcryptpad/ui/public/assets/share.png differ diff --git a/rmcryptpad/ui/public/cryptpad-mark.svg b/rmcryptpad/ui/public/cryptpad-mark.svg new file mode 100644 index 0000000..b76b7dc --- /dev/null +++ b/rmcryptpad/ui/public/cryptpad-mark.svg @@ -0,0 +1 @@ + diff --git a/rmcryptpad/ui/scripts/minify-images.mjs b/rmcryptpad/ui/scripts/minify-images.mjs new file mode 100644 index 0000000..6aef048 --- /dev/null +++ b/rmcryptpad/ui/scripts/minify-images.mjs @@ -0,0 +1,91 @@ +#!/usr/bin/env node +// Minifies PNG/JPEG images in-place using sharp (no external binaries needed). + +import { readdirSync, statSync, renameSync, rmSync } from "fs"; +import { join, extname } from "path"; +import sharp from "sharp"; + +const PNG_EXTS = new Set([".png"]); +const JPG_EXTS = new Set([".jpg", ".jpeg"]); +const SEARCH_DIRS = ["src/assets", "public/assets"]; + +function findImages(dir) { + const results = []; + for (const entry of readdirSync(dir, { withFileTypes: true })) { + const full = join(dir, entry.name); + if (entry.isDirectory()) { + results.push(...findImages(full)); + } else { + const ext = extname(entry.name).toLowerCase(); + if (PNG_EXTS.has(ext) || JPG_EXTS.has(ext)) results.push(full); + } + } + return results; +} + +const images = SEARCH_DIRS.filter((d) => { + try { + statSync(d); + return true; + } catch { + return false; + } +}).flatMap(findImages); + +if (images.length === 0) { + console.log("No images found."); + process.exit(0); +} + +console.log(`Minifying ${images.length} image(s)...\n`); + +let totalBefore = 0; +let totalAfter = 0; + +for (const src of images) { + const tmp = `${src}.min-tmp`; + try { + const ext = extname(src).toLowerCase(); + const sizeBefore = statSync(src).size; + + if (PNG_EXTS.has(ext)) { + await sharp(src).png({ compressionLevel: 9, effort: 10 }).toFile(tmp); + } else { + await sharp(src).jpeg({ quality: 82, mozjpeg: true }).toFile(tmp); + } + + const sizeAfter = statSync(tmp).size; + if (sizeAfter < sizeBefore) { + renameSync(tmp, src); + totalBefore += sizeBefore; + totalAfter += sizeAfter; + const pct = (((sizeBefore - sizeAfter) / sizeBefore) * 100).toFixed(1); + console.log( + ` ${src}: ${(sizeBefore / 1024).toFixed(0)} KB → ${( + sizeAfter / 1024 + ).toFixed(0)} KB (-${pct}%)`, + ); + } else { + rmSync(tmp); + totalBefore += sizeBefore; + totalAfter += sizeBefore; + console.log(` ${src}: already optimal, skipped`); + } + } catch (err) { + try { + rmSync(tmp, { force: true }); + } catch {} + console.warn(` SKIP ${src}: ${err.message}`); + } +} + +if (totalBefore > 0) { + const totalPct = (((totalBefore - totalAfter) / totalBefore) * 100).toFixed( + 1, + ); + console.log( + `\nTotal: ${(totalBefore / 1024).toFixed(0)} KB → ${( + totalAfter / 1024 + ).toFixed(0)} KB (-${totalPct}%)`, + ); +} diff --git a/rmcryptpad/ui/src/.DS_Store b/rmcryptpad/ui/src/.DS_Store new file mode 100644 index 0000000..fce81d9 Binary files /dev/null and b/rmcryptpad/ui/src/.DS_Store differ diff --git a/rmcryptpad/ui/src/App.test.tsx b/rmcryptpad/ui/src/App.test.tsx index fb32400..bddcd6e 100644 --- a/rmcryptpad/ui/src/App.test.tsx +++ b/rmcryptpad/ui/src/App.test.tsx @@ -1,35 +1,6 @@ -import { vi } from "vitest"; -import { render, screen } from "@testing-library/react"; import { expect, test } from "vitest"; -import "@testing-library/jest-dom/vitest"; -vi.mock("./assets/cryptpad-mark.svg", () => ({ - default: "/ui/cryptpad/assets/cryptpad-mark.svg", -})); - -import App from "./App"; - -test("renders an open button for CryptPad", () => { - const { container } = render( - , - ); - - expect(screen.getByText("VIRTA-1")).toBeInTheDocument(); - expect( - screen.getByText("https://mtls.sandbox.cryptpad.example.invalid"), - ).toBeInTheDocument(); - expect(container.querySelector("img")).toHaveAttribute( - "src", - "/ui/cryptpad/assets/cryptpad-mark.svg", - ); - expect(screen.getByRole("link", { name: /open cryptpad/i })).toHaveAttribute( - "href", - "https://mtls.cryptpad.example.invalid/login/", - ); +test("PRODUCT_SHORTNAME is cryptpad", async () => { + const { PRODUCT_SHORTNAME } = await import("./App"); + expect(PRODUCT_SHORTNAME).toBe("cryptpad"); }); diff --git a/rmcryptpad/ui/src/App.tsx b/rmcryptpad/ui/src/App.tsx index 8a2c9da..e281705 100644 --- a/rmcryptpad/ui/src/App.tsx +++ b/rmcryptpad/ui/src/App.tsx @@ -1,59 +1,85 @@ +import { useEffect, useMemo, useState } from "react"; +import { useTranslation } from "react-i18next"; import { - buildCardDetails, - buildLoginUrl, - type CryptPadCardData, - type CryptPadCardMeta, -} from "@/lib/metadata"; -import cryptpadMarkUrl from "./assets/cryptpad-mark.svg"; + createRootRoute, + createRoute, + createRouter, + Outlet, + RouterProvider, +} from "@tanstack/react-router"; -import "./index.css"; +import { HomePage } from "./pages/HomePage"; -export interface AppProps { +import enLang from "./locales/en.json"; +import fiLang from "./locales/fi.json"; +import svLang from "./locales/sv.json"; +import { MetaData, MetaProvider, CryptPadCardData } from "./lib/metadata"; + +export const PRODUCT_SHORTNAME = "cryptpad"; + +function RootLayoutComponent() { + return ( +
+ +
+ ); +} + +interface Props { data: CryptPadCardData; - meta: CryptPadCardMeta; + meta: MetaData; } -export default function App({ data, meta }: AppProps) { - const card = buildCardDetails(meta); - const loginUrl = buildLoginUrl(data.url); +export default function CryptPadApp({ data, meta }: Props) { + const [ready, setReady] = useState(false); + const { i18n } = useTranslation(PRODUCT_SHORTNAME); + + const rootRoute = useMemo( + () => + createRootRoute({ + component: RootLayoutComponent, + }), + [], + ); + + const homeRoute = useMemo( + () => + createRoute({ + getParentRoute: () => rootRoute, + path: "/", + component: () => , + }), + [rootRoute, data], + ); + + const routeTree = useMemo( + () => rootRoute.addChildren([homeRoute]), + [rootRoute, homeRoute], + ); + + const router = useMemo( + () => createRouter({ routeTree, basepath: "/product/cryptpad" }), + [routeTree], + ); + + useEffect(() => { + async function load() { + i18n.addResourceBundle("en", PRODUCT_SHORTNAME, enLang); + i18n.addResourceBundle("fi", PRODUCT_SHORTNAME, fiLang); + i18n.addResourceBundle("sv", PRODUCT_SHORTNAME, svLang); + + await i18n.loadNamespaces(PRODUCT_SHORTNAME); + setReady(true); + } + + void load(); + }, [i18n]); + + if (!ready) return null; return ( -
-
-
- -
-

Federated workspace

-

CryptPad

-
-
- -

- Secure collaborative documents for certificate-backed Deploy App users. -

- -
-
-
Signed in as
-
{card.callsign}
-
-
-
Sandbox origin
-
{data.sandbox_url}
-
-
- -
- - Open CryptPad - -
-
-
+ + + ); } diff --git a/rmcryptpad/ui/src/assets/cryptpad-mark.svg b/rmcryptpad/ui/src/assets/cryptpad-mark.svg deleted file mode 100644 index c43ccf0..0000000 --- a/rmcryptpad/ui/src/assets/cryptpad-mark.svg +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - - - - - - diff --git a/rmcryptpad/ui/src/assets/features/.DS_Store b/rmcryptpad/ui/src/assets/features/.DS_Store new file mode 100644 index 0000000..e998f85 Binary files /dev/null and b/rmcryptpad/ui/src/assets/features/.DS_Store differ diff --git a/rmcryptpad/ui/src/components/FeatureGuide.tsx b/rmcryptpad/ui/src/components/FeatureGuide.tsx new file mode 100644 index 0000000..9d1cd36 --- /dev/null +++ b/rmcryptpad/ui/src/components/FeatureGuide.tsx @@ -0,0 +1,238 @@ +import { useState, useCallback, useEffect } from "react"; +import { Drawer, DrawerContent } from "@/components/ui/drawer"; +import { Dialog, DialogContent, DialogTitle } from "@/components/ui/dialog"; +import { PRODUCT_SHORTNAME } from "@/App"; +import { Button } from "@/components/ui/button"; +import { ChevronRight, ChevronLeft, ImageOff } from "lucide-react"; +import { useIsMobile } from "@/hooks/use-mobile"; +import { useTranslation } from "react-i18next"; +import { cn } from "@/lib/utils"; + +interface FeatureStep { + id: string; + title: string; + description: string; + image: string; + mobileImage?: string; +} + +interface FeatureGuideProps { + featureKey: string; + steps: FeatureStep[]; + open: boolean; + onOpenChange: (open: boolean) => void; +} + +const imageCache = new Map(); + +export function FeatureGuide({ + featureKey, + steps, + open, + onOpenChange, +}: FeatureGuideProps) { + const [currentStep, setCurrentStep] = useState(0); + const [imageEnlarged, setImageEnlarged] = useState(false); + const [imageError, setImageError] = useState(false); + const [imageLoading, setImageLoading] = useState(true); + const isMobile = useIsMobile(); + const { t } = useTranslation(PRODUCT_SHORTNAME); + + const getImage = useCallback( + (step: FeatureStep) => { + return isMobile && step.mobileImage ? step.mobileImage : step.image; + }, + [isMobile], + ); + + // Preload the next step's image so it's ready before the user clicks Next + useEffect(() => { + if (currentStep < steps.length - 1) { + const nextUrl = getImage(steps[currentStep + 1]); + if (!imageCache.has(nextUrl)) { + const img = new Image(); + img.onload = () => + imageCache.set(nextUrl, { loaded: true, error: false }); + img.onerror = () => + imageCache.set(nextUrl, { loaded: true, error: true }); + img.src = nextUrl; + } + } + }, [currentStep, steps, getImage]); + + const goToStep = (index: number) => { + const url = getImage(steps[index]); + const cached = imageCache.get(url); + setCurrentStep(index); + setImageError(cached?.error ?? false); + setImageLoading(!cached); + }; + + const handleNext = () => { + if (currentStep < steps.length - 1) goToStep(currentStep + 1); + }; + + const handlePrev = () => { + if (currentStep > 0) goToStep(currentStep - 1); + }; + + const handleFinish = () => { + onOpenChange(false); + setCurrentStep(0); + }; + + const handleOpenChange = (newOpen: boolean) => { + onOpenChange(newOpen); + if (!newOpen) { + setCurrentStep(0); + } + }; + + if (steps.length === 0 || isMobile === undefined) return null; + + const step = steps[currentStep]; + const progress = ((currentStep + 1) / steps.length) * 100; + const imageUrl = getImage(step); + + const contentComponent = ( +
+ + {t(`features.${featureKey}.title`)} + +
+
+
+
+

{t(step.title)}

+
+

+ {t("onboarding.step")} {currentStep + 1} {t("onboarding.of")}{" "} + {steps.length} +

+
+ +
+ !imageError && !imageLoading && setImageEnlarged(true) + } + > + {imageLoading && !imageError && ( +
+
+
+ )} + {imageError ? ( +
+ + + {t("onboarding.imageMissing") || "Image not available"} + +
+ ) : ( + <> + {t(step.title)} { + setImageLoading(false); + imageCache.set(imageUrl, { loaded: true, error: false }); + }} + onError={() => { + setImageError(true); + setImageLoading(false); + imageCache.set(imageUrl, { loaded: true, error: true }); + }} + /> + {!imageLoading && ( +
+ + {t("onboarding.clickToEnlarge") || "Click to enlarge"} + +
+ )} + + )} +
+

+ {t(step.description)} +

+
+
+ +
+ + +
+ +
+
+
+
+ ); + + const enlargedImageModal = ( + + setImageEnlarged(false)} + > +
+ {t(step.title)} + {t(step.title)} +
+
+
+ ); + + if (isMobile) { + return ( + <> + {enlargedImageModal} + + {contentComponent} + + + ); + } + + return ( + <> + {enlargedImageModal} + + + {contentComponent} + + + + ); +} diff --git a/rmcryptpad/ui/src/components/OnboardingGuide.tsx b/rmcryptpad/ui/src/components/OnboardingGuide.tsx new file mode 100644 index 0000000..5be01ef --- /dev/null +++ b/rmcryptpad/ui/src/components/OnboardingGuide.tsx @@ -0,0 +1,448 @@ +import { useState, useEffect, useCallback, useRef, useMemo } from "react"; +import { Drawer, DrawerContent } from "@/components/ui/drawer"; +import { Dialog, DialogContent, DialogTitle } from "@/components/ui/dialog"; +import { PRODUCT_SHORTNAME } from "@/App"; +import { Button } from "@/components/ui/button"; +import { ChevronRight, ChevronLeft, ImageOff, Info } from "lucide-react"; +import { useIsMobile } from "@/hooks/use-mobile"; +import { toast } from "sonner"; +import { useTranslation } from "react-i18next"; +import useHealthCheck from "@/hooks/helpers/useHealthcheck"; +import { cn } from "@/lib/utils"; +import { useMeta } from "@/lib/metadata"; + +const hashString = (str: string): string => { + let hash = 0; + for (let i = 0; i < str.length; i++) { + const char = str.charCodeAt(i); + hash = (hash << 5) - hash + char; + hash = hash & hash; + } + return Math.abs(hash).toString(36).padStart(8, "0").slice(0, 8); +}; + +interface OnboardingStep { + id: string; + title: string; + description: string; + image: string; + mobileImage?: string; +} + +function ONBOARDING_STEPS(): OnboardingStep[] { + return [ + { + id: "welcome", + title: "onboarding.steps.welcome.title", + description: "onboarding.steps.welcome.description", + image: `/ui/cryptpad/assets/cryptpad1.png`, + }, + { + id: "what_is_cryptpad", + title: "onboarding.steps.whatIs.title", + description: "onboarding.steps.whatIs.description", + image: `/ui/cryptpad/assets/e2e.png`, + }, + { + id: "sign_in", + title: "onboarding.steps.signIn.title", + description: "onboarding.steps.signIn.description", + image: `/ui/cryptpad/assets/cryptpad4.png`, + }, + { + id: "dashboard", + title: "onboarding.steps.dashboard.title", + description: "onboarding.steps.dashboard.description", + image: `/ui/cryptpad/assets/cryptpad2.png`, + }, + { + id: "create_document", + title: "onboarding.steps.createDoc.title", + description: "onboarding.steps.createDoc.description", + image: `/ui/cryptpad/assets/cryptpad3.png`, + }, + { + id: "share", + title: "onboarding.steps.share.title", + description: "onboarding.steps.share.description", + image: `/ui/cryptpad/assets/share.png`, + }, + ]; +} + +const getOnboardingImage = ( + step: OnboardingStep, + isMobile: boolean, + forceDesktop = false, +): string => { + const imagePath = forceDesktop + ? step.image + : isMobile && step.mobileImage + ? step.mobileImage + : step.image; + + return imagePath; +}; + +const imageCache = new Map(); + +const preloadImage = (src: string): Promise => { + if (imageCache.has(src)) { + return Promise.resolve(); + } + + return new Promise((resolve) => { + const img = new Image(); + img.onload = () => { + imageCache.set(src, { loaded: true, error: false }); + resolve(); + }; + img.onerror = () => { + imageCache.set(src, { loaded: true, error: true }); + resolve(); + }; + img.src = src; + }); +}; + +export function OnboardingGuide() { + const [open, setOpen] = useState(false); + const [currentStep, setCurrentStep] = useState(0); + const [completed, setCompleted] = useState([]); + const [showCompletion, setShowCompletion] = useState(false); + const [, setCanReview] = useState(false); + const [reviewMode, setReviewMode] = useState(false); + const [imageEnlarged, setImageEnlarged] = useState(false); + const [imageError, setImageError] = useState(false); + const [imageLoading, setImageLoading] = useState(true); + const preloadedForDeviceRef = useRef(null); + const isMobile = useIsMobile(); + const { t } = useTranslation(PRODUCT_SHORTNAME); + const { deployment } = useHealthCheck(); + const meta = useMeta(); + + const relevantSteps = useMemo(() => ONBOARDING_STEPS(), []); + + useEffect(() => { + if (isMobile === undefined || relevantSteps.length === 0) return; + if (preloadedForDeviceRef.current === isMobile) return; + + preloadedForDeviceRef.current = isMobile; + + const preloadAllImages = async () => { + const inlineImages = relevantSteps.map((step) => + getOnboardingImage(step, isMobile, false), + ); + const desktopImages = relevantSteps.map((step) => + getOnboardingImage(step, isMobile, true), + ); + await Promise.all([...inlineImages, ...desktopImages].map(preloadImage)); + }; + + void preloadAllImages(); + }, [relevantSteps, isMobile]); + + const checkImageCache = useCallback((url: string) => { + const cached = imageCache.get(url); + if (cached) { + setImageLoading(false); + setImageError(cached.error); + } else { + setImageLoading(true); + setImageError(false); + } + }, []); + + useEffect(() => { + if (!meta.callsign || !deployment) return; + + const deploymentHash = hashString(deployment); + const storageKey = `${deploymentHash}-cryptpad-onboarding-${meta.callsign}`; + const seenOnboarding = localStorage.getItem(storageKey); + + const completedSteps = localStorage.getItem( + `${deploymentHash}-cryptpad-onboarding-steps-${meta.callsign}`, + ); + + if (!seenOnboarding) { + setOpen(true); + } + + if (completedSteps) { + const parsedCompleted = JSON.parse(completedSteps) as string[]; + setCompleted(parsedCompleted); + + if (parsedCompleted.length > 0 && !seenOnboarding) { + const firstIncompleteIndex = relevantSteps.findIndex( + (step) => !parsedCompleted.includes(step.id), + ); + if (firstIncompleteIndex !== -1) { + setCurrentStep(firstIncompleteIndex); + } else { + setCurrentStep(relevantSteps.length - 1); + } + setCanReview(true); + } + } + }, [meta.callsign, deployment, relevantSteps]); + + useEffect(() => { + if ( + relevantSteps.length > 0 && + isMobile !== undefined && + relevantSteps[currentStep] + ) { + const url = getOnboardingImage( + relevantSteps[currentStep], + isMobile, + false, + ); + if (url) { + checkImageCache(url); + } + } + }, [currentStep, relevantSteps, checkImageCache, isMobile]); + + const handleNext = () => { + if (currentStep < relevantSteps.length - 1) { + setCurrentStep(currentStep + 1); + setImageError(false); + setImageLoading(true); + } + }; + + const handlePrev = () => { + if (currentStep > 0) { + setCurrentStep(currentStep - 1); + setImageError(false); + setImageLoading(true); + } + }; + + const handleReviewClick = () => { + setReviewMode(true); + setCurrentStep(0); + setOpen(true); + }; + + const handleOpenChange = (newOpen: boolean) => { + setOpen(newOpen); + if (!newOpen && reviewMode) { + setReviewMode(false); + } else if (!newOpen && !reviewMode && !showCompletion) { + if (deployment) { + const deploymentHash = hashString(deployment); + const newCompleted = [...completed]; + const step = relevantSteps[currentStep]; + if (!newCompleted.includes(step.id)) { + newCompleted.push(step.id); + } + localStorage.setItem( + `${deploymentHash}-cryptpad-onboarding-steps-${meta.callsign}`, + JSON.stringify(newCompleted), + ); + setCompleted(newCompleted); + } + setCanReview(true); + toast.success(t("onboarding.progressSaved") || "Progress saved", { + duration: 2000, + }); + } + }; + + const handleComplete = () => { + const step = relevantSteps[currentStep]; + if (!completed.includes(step.id)) { + const newCompleted = [...completed, step.id]; + setCompleted(newCompleted); + if (deployment) { + const deploymentHash = hashString(deployment); + localStorage.setItem( + `${deploymentHash}-cryptpad-onboarding-steps-${meta.callsign}`, + JSON.stringify(newCompleted), + ); + } + } + + if (currentStep === relevantSteps.length - 1) { + if (deployment) { + const deploymentHash = hashString(deployment); + localStorage.setItem( + `${deploymentHash}-cryptpad-onboarding-${meta.callsign}`, + "true", + ); + } + setOpen(false); + setShowCompletion(false); + setCanReview(true); + setReviewMode(false); + toast.success(t("onboarding.completion"), { duration: 3000 }); + } else { + handleNext(); + } + }; + + if (relevantSteps.length === 0) return null; + if (isMobile === undefined) return null; + + const step = relevantSteps[currentStep]; + const progress = ((currentStep + 1) / relevantSteps.length) * 100; + const imageUrl = getOnboardingImage(step, isMobile, false); + const enlargedImageUrl = getOnboardingImage(step, isMobile, false); + + if (!open) { + return ( + + ); + } + + const contentComponent = ( +
+ + {t("onboarding.title") || "What is CryptPad?"} + +
+
+
+
+

{t(step.title)}

+
+

+ {t("onboarding.step")} {currentStep + 1} {t("onboarding.of")}{" "} + {relevantSteps.length} +

+
+ +
+ !imageError && !imageLoading && setImageEnlarged(true) + } + > + {imageLoading && !imageError && ( +
+
+
+ )} + {imageError ? ( +
+ + + {t("onboarding.imageMissing") || "Image not available"} + +
+ ) : ( + <> + {t(step.title)} { + setImageLoading(false); + imageCache.set(imageUrl, { loaded: true, error: false }); + }} + onError={() => { + setImageError(true); + setImageLoading(false); + imageCache.set(imageUrl, { loaded: true, error: true }); + }} + /> + {!imageLoading && ( +
+ + {t("onboarding.clickToEnlarge") || "Click to enlarge"} + +
+ )} + + )} +
+

+ {t(step.description)} +

+
+
+ +
+ + +
+ +
+
+
+
+ ); + + const enlargedImageModal = ( + + setImageEnlarged(false)} + > +
+ {t(step.title)} + {t(step.title)} +
+
+
+ ); + + if (isMobile) { + return ( + <> + {enlargedImageModal} + + {contentComponent} + + + ); + } + + return ( + <> + {enlargedImageModal} + + + {contentComponent} + + + + ); +} diff --git a/rmcryptpad/ui/src/components/translated-text.tsx b/rmcryptpad/ui/src/components/translated-text.tsx new file mode 100644 index 0000000..544cede --- /dev/null +++ b/rmcryptpad/ui/src/components/translated-text.tsx @@ -0,0 +1,15 @@ +import { PRODUCT_SHORTNAME } from "@/App"; +import { JSX, HTMLAttributes } from "react"; +import { useTranslation } from "react-i18next"; + +type TranslatedTextProps = { + id: string; +} & HTMLAttributes; + +export function TranslatedText({ + id, + ...props +}: TranslatedTextProps): JSX.Element { + const { t } = useTranslation(PRODUCT_SHORTNAME); + return {t(id)}; +} diff --git a/rmcryptpad/ui/src/components/ui/button.tsx b/rmcryptpad/ui/src/components/ui/button.tsx new file mode 100644 index 0000000..1dd187c --- /dev/null +++ b/rmcryptpad/ui/src/components/ui/button.tsx @@ -0,0 +1,60 @@ +import * as React from "react"; +import { Slot } from "@radix-ui/react-slot"; +import { cva, type VariantProps } from "class-variance-authority"; + +import { cn } from "@/lib/utils"; + +const buttonVariants = cva( + "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive", + { + variants: { + variant: { + default: "bg-primary text-primary-foreground hover:bg-primary/90", + destructive: + "bg-destructive text-white hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60", + outline: + "border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50", + secondary: + "bg-secondary text-secondary-foreground hover:bg-secondary/80", + ghost: + "hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50", + link: "text-primary underline-offset-4 hover:underline", + }, + size: { + default: "h-9 px-4 py-2 has-[>svg]:px-3", + sm: "h-8 rounded-md gap-1.5 px-3 has-[>svg]:px-2.5", + lg: "h-10 rounded-md px-6 has-[>svg]:px-4", + icon: "size-9", + "icon-sm": "size-8", + "icon-lg": "size-10", + }, + }, + defaultVariants: { + variant: "default", + size: "default", + }, + }, +); + +function Button({ + className, + variant, + size, + asChild = false, + ...props +}: React.ComponentProps<"button"> & + VariantProps & { + asChild?: boolean; + }) { + const Comp = asChild ? Slot : "button"; + + return ( + + ); +} + +export { Button, buttonVariants }; diff --git a/rmcryptpad/ui/src/components/ui/card.tsx b/rmcryptpad/ui/src/components/ui/card.tsx new file mode 100644 index 0000000..43b16f5 --- /dev/null +++ b/rmcryptpad/ui/src/components/ui/card.tsx @@ -0,0 +1,78 @@ +import * as React from "react"; + +import { cn } from "@/lib/utils"; + +function Card({ className, ...props }: React.ComponentProps<"div">) { + return ( +
+ ); +} + +function CardHeader({ className, ...props }: React.ComponentProps<"div">) { + return ( +
+ ); +} + +function CardTitle({ className, ...props }: React.ComponentProps<"div">) { + return ( +
+ ); +} + +function CardDescription({ className, ...props }: React.ComponentProps<"div">) { + return ( +
+ ); +} + +function CardContent({ className, ...props }: React.ComponentProps<"div">) { + return ( +
+ ); +} + +function CardFooter({ className, ...props }: React.ComponentProps<"div">) { + return ( +
+ ); +} + +export { + Card, + CardHeader, + CardFooter, + CardTitle, + CardDescription, + CardContent, +}; diff --git a/rmcryptpad/ui/src/components/ui/dialog.tsx b/rmcryptpad/ui/src/components/ui/dialog.tsx new file mode 100644 index 0000000..8220f1a --- /dev/null +++ b/rmcryptpad/ui/src/components/ui/dialog.tsx @@ -0,0 +1,146 @@ +import * as React from "react"; +import * as DialogPrimitive from "@radix-ui/react-dialog"; +import { XIcon } from "lucide-react"; + +import { cn } from "@/lib/utils"; + +function Dialog({ + ...props +}: React.ComponentProps) { + return ; +} + +function DialogTrigger({ + ...props +}: React.ComponentProps) { + return ; +} + +function DialogPortal({ + ...props +}: React.ComponentProps) { + return ; +} + +function DialogClose({ + ...props +}: React.ComponentProps) { + return ; +} + +function DialogOverlay({ + className, + ...props +}: React.ComponentProps) { + return ( + + ); +} + +function DialogContent({ + className, + children, + showCloseButton = true, + disableOutsideClose = false, + ...props +}: React.ComponentProps & { + showCloseButton?: boolean; + disableOutsideClose?: boolean; +}) { + return ( + + + event.preventDefault() : undefined + } + {...props} + > + {children} + {showCloseButton && ( + + + Close + + )} + + + ); +} + +function DialogHeader({ className, ...props }: React.ComponentProps<"div">) { + return ( +
+ ); +} + +function DialogFooter({ className, ...props }: React.ComponentProps<"div">) { + return ( +
+ ); +} + +function DialogTitle({ + className, + ...props +}: React.ComponentProps) { + return ( + + ); +} + +function DialogDescription({ + className, + ...props +}: React.ComponentProps) { + return ( + + ); +} + +export { + Dialog, + DialogClose, + DialogContent, + DialogDescription, + DialogFooter, + DialogHeader, + DialogOverlay, + DialogPortal, + DialogTitle, + DialogTrigger, +}; diff --git a/rmcryptpad/ui/src/components/ui/drawer.tsx b/rmcryptpad/ui/src/components/ui/drawer.tsx new file mode 100644 index 0000000..d0c72ec --- /dev/null +++ b/rmcryptpad/ui/src/components/ui/drawer.tsx @@ -0,0 +1,133 @@ +import * as React from "react"; +import { Drawer as DrawerPrimitive } from "vaul"; + +import { cn } from "@/lib/utils"; + +function Drawer({ + ...props +}: React.ComponentProps) { + return ; +} + +function DrawerTrigger({ + ...props +}: React.ComponentProps) { + return ; +} + +function DrawerPortal({ + ...props +}: React.ComponentProps) { + return ; +} + +function DrawerClose({ + ...props +}: React.ComponentProps) { + return ; +} + +function DrawerOverlay({ + className, + ...props +}: React.ComponentProps) { + return ( + + ); +} + +function DrawerContent({ + className, + children, + ...props +}: React.ComponentProps) { + return ( + + + +
+ {children} + + + ); +} + +function DrawerHeader({ className, ...props }: React.ComponentProps<"div">) { + return ( +
+ ); +} + +function DrawerFooter({ className, ...props }: React.ComponentProps<"div">) { + return ( +
+ ); +} + +function DrawerTitle({ + className, + ...props +}: React.ComponentProps) { + return ( + + ); +} + +function DrawerDescription({ + className, + ...props +}: React.ComponentProps) { + return ( + + ); +} + +export { + Drawer, + DrawerPortal, + DrawerOverlay, + DrawerTrigger, + DrawerClose, + DrawerContent, + DrawerHeader, + DrawerFooter, + DrawerTitle, + DrawerDescription, +}; diff --git a/rmcryptpad/ui/src/components/ui/sonner.tsx b/rmcryptpad/ui/src/components/ui/sonner.tsx new file mode 100644 index 0000000..69108b5 --- /dev/null +++ b/rmcryptpad/ui/src/components/ui/sonner.tsx @@ -0,0 +1,25 @@ +import { Toaster as Sonner } from "sonner"; + +type ToasterProps = React.ComponentProps; + +const Toaster = ({ ...props }: ToasterProps) => { + return ( + + ); +}; + +export { Toaster }; diff --git a/rmcryptpad/ui/src/hooks/helpers/useHealthcheck.ts b/rmcryptpad/ui/src/hooks/helpers/useHealthcheck.ts new file mode 100644 index 0000000..5784077 --- /dev/null +++ b/rmcryptpad/ui/src/hooks/helpers/useHealthcheck.ts @@ -0,0 +1,35 @@ +import { useState, useEffect } from "react"; + +const useHealthCheck = () => { + const [fqdn, setFqdn] = useState(""); + const [version, setVersion] = useState(""); + const [deployment, setDeployment] = useState(""); + + interface HealthCheckResponse { + dns: string; + version: string; + deployment: string; + } + + useEffect(() => { + fetch("/api/v1/healthcheck") + .then((response) => { + if (!response.ok) { + throw new Error("Network response was not ok"); + } + return response.json(); + }) + .then((data: HealthCheckResponse) => { + setFqdn(data.dns); + setVersion(data.version); + setDeployment(data.deployment); + }) + .catch((error) => { + console.error("Failed to fetch health check data", error); + }); + }, []); + + return { fqdn, version, deployment }; +}; + +export default useHealthCheck; diff --git a/rmcryptpad/ui/src/hooks/use-mobile.tsx b/rmcryptpad/ui/src/hooks/use-mobile.tsx new file mode 100644 index 0000000..a93d583 --- /dev/null +++ b/rmcryptpad/ui/src/hooks/use-mobile.tsx @@ -0,0 +1,21 @@ +import * as React from "react"; + +const MOBILE_BREAKPOINT = 768; + +export function useIsMobile() { + const [isMobile, setIsMobile] = React.useState( + undefined, + ); + + React.useEffect(() => { + const mql = window.matchMedia(`(max-width: ${MOBILE_BREAKPOINT - 1}px)`); + const onChange = () => { + setIsMobile(window.innerWidth < MOBILE_BREAKPOINT); + }; + mql.addEventListener("change", onChange); + setIsMobile(window.innerWidth < MOBILE_BREAKPOINT); + return () => mql.removeEventListener("change", onChange); + }, []); + + return !!isMobile; +} diff --git a/rmcryptpad/ui/src/i18n.ts b/rmcryptpad/ui/src/i18n.ts new file mode 100644 index 0000000..319519a --- /dev/null +++ b/rmcryptpad/ui/src/i18n.ts @@ -0,0 +1,14 @@ +import i18n from "i18next"; +import { initReactI18next } from "react-i18next"; + +const savedLanguage = localStorage.getItem("language") || "en"; + +void i18n.use(initReactI18next).init({ + lng: savedLanguage, + fallbackLng: "en", + interpolation: { + escapeValue: false, + }, +}); + +export default i18n; diff --git a/rmcryptpad/ui/src/index.css b/rmcryptpad/ui/src/index.css index 153a01f..0debf9d 100644 --- a/rmcryptpad/ui/src/index.css +++ b/rmcryptpad/ui/src/index.css @@ -1,173 +1,110 @@ -:root { - color-scheme: dark; - font-family: - Inter, "SF Pro Display", "Segoe UI", system-ui, -apple-system, BlinkMacSystemFont, - sans-serif; - line-height: 1.5; - font-weight: 400; - --cp-bg: #07111c; - --cp-bg-soft: rgba(15, 23, 42, 0.72); - --cp-surface: rgba(7, 17, 28, 0.88); - --cp-surface-border: rgba(148, 163, 184, 0.18); - --cp-text: #ecf6f7; - --cp-muted: #9eb3c6; - --cp-accent: #6fe3ba; - --cp-accent-strong: #2dd4bf; - --cp-shadow: 0 28px 80px rgba(3, 10, 17, 0.45); -} - -*, -*::before, -*::after { - box-sizing: border-box; -} - -html, -body, -#root { - min-height: 100%; -} - -body { - margin: 0; - min-height: 100vh; - background: - radial-gradient(circle at top left, rgba(45, 212, 191, 0.18), transparent 28%), - radial-gradient(circle at bottom right, rgba(110, 227, 186, 0.12), transparent 24%), - linear-gradient(180deg, #040b13 0%, var(--cp-bg) 100%); - color: var(--cp-text); -} - -a { - color: inherit; - text-decoration: none; -} - -.cryptpad-shell { - min-height: 100vh; - display: grid; - place-items: center; - padding: 32px 20px; -} - -.theme-light { - --cp-bg: #f5f7fb; - --cp-bg-soft: rgba(255, 255, 255, 0.84); - --cp-surface: rgba(255, 255, 255, 0.9); - --cp-surface-border: rgba(15, 23, 42, 0.1); - --cp-text: #0f172a; - --cp-muted: #4b5b70; - --cp-accent: #0f766e; - --cp-accent-strong: #115e59; - --cp-shadow: 0 30px 90px rgba(15, 23, 42, 0.14); - background: - radial-gradient(circle at top left, rgba(45, 212, 191, 0.16), transparent 28%), - linear-gradient(180deg, #ffffff 0%, #eff6f8 100%); -} - -.cryptpad-card { - width: min(100%, 560px); - border-radius: 28px; - padding: 28px; - border: 1px solid var(--cp-surface-border); - background: linear-gradient(180deg, var(--cp-bg-soft) 0%, var(--cp-surface) 100%); - box-shadow: var(--cp-shadow); - backdrop-filter: blur(20px); -} - -.cryptpad-card__header { - display: flex; - align-items: center; - gap: 16px; -} - -.cryptpad-card__mark { - width: 52px; - height: 52px; - flex: 0 0 auto; -} - -.cryptpad-card__eyebrow { - margin: 0 0 4px; - letter-spacing: 0.12em; - text-transform: uppercase; - font-size: 0.72rem; - color: var(--cp-muted); +@import "tailwindcss"; +@import "tw-animate-css"; +@plugin '@tailwindcss/typography'; +@custom-variant dark (&:is(.dark *)); + +@theme inline { + --radius: 0.75rem; + --radius-sm: calc(var(--radius) - 4px); + --radius-md: calc(var(--radius) - 2px); + --radius-lg: var(--radius); + --radius-xl: calc(var(--radius) + 4px); + + --color-background: var(--background); + --color-foreground: var(--foreground); + --color-card: var(--card); + --color-card-foreground: var(--card-foreground); + --color-popover: var(--popover); + --color-popover-foreground: var(--popover-foreground); + --color-primary: var(--primary); + --color-primary-foreground: var(--primary-foreground); + --color-secondary: var(--secondary); + --color-secondary-foreground: var(--secondary-foreground); + --color-muted: var(--muted); + --color-muted-foreground: var(--muted-foreground); + --color-accent: var(--accent); + --color-accent-foreground: var(--accent-foreground); + --color-destructive: var(--destructive); + --color-border: var(--border); + --color-input: var(--input); + --color-ring: var(--ring); + --color-chart-1: var(--chart-1); + --color-chart-2: var(--chart-2); + --color-chart-3: var(--chart-3); + --color-chart-4: var(--chart-4); + --color-chart-5: var(--chart-5); + --color-sidebar: var(--sidebar); + --color-sidebar-foreground: var(--sidebar-foreground); + --color-sidebar-primary: var(--sidebar-primary); + --color-sidebar-primary-foreground: var(--sidebar-primary-foreground); + --color-sidebar-accent: var(--sidebar-accent); + --color-sidebar-accent-foreground: var(--sidebar-accent-foreground); + --color-sidebar-border: var(--sidebar-border); + --color-sidebar-ring: var(--sidebar-ring); } - -.cryptpad-card__brand h1 { - margin: 0; - font-size: clamp(1.9rem, 3vw, 2.5rem); - line-height: 1.05; -} - -.cryptpad-card__lead { - margin: 18px 0 0; - color: var(--cp-muted); - font-size: 1rem; - max-width: 42ch; -} - -.cryptpad-card__details { - display: grid; - gap: 14px; - margin: 24px 0 0; -} - -.cryptpad-card__details div { - padding: 14px 16px; - border-radius: 18px; - background: rgba(148, 163, 184, 0.08); - border: 1px solid rgba(148, 163, 184, 0.12); -} - -.cryptpad-card__details dt { - margin: 0 0 4px; - font-size: 0.72rem; - text-transform: uppercase; - letter-spacing: 0.1em; - color: var(--cp-muted); -} - -.cryptpad-card__details dd { - margin: 0; - font-size: 0.95rem; - word-break: break-word; -} - -.cryptpad-card__actions { - display: flex; - gap: 12px; - align-items: center; - margin-top: 24px; -} - -.cryptpad-card__button { - display: inline-flex; - align-items: center; - justify-content: center; - min-height: 48px; - padding: 0 20px; - border-radius: 999px; - background: linear-gradient(135deg, var(--cp-accent) 0%, var(--cp-accent-strong) 100%); - color: #041018; - font-weight: 700; - letter-spacing: 0.01em; - box-shadow: 0 18px 34px rgba(45, 212, 191, 0.2); -} - -.cryptpad-card__button:hover { - filter: brightness(1.02); -} - -@media (max-width: 520px) { - .cryptpad-card { - padding: 22px; - border-radius: 22px; +:root { + --background: #121113; + --foreground: #c1c1c1; + --card: #121212; + --card-foreground: #c1c1c1; + --popover: #121113; + --popover-foreground: #c1c1c1; + --primary: #e78a53; + --primary-foreground: #121113; + --secondary: #5f8787; + --secondary-foreground: #121113; + --muted: #222222; + --muted-foreground: #888888; + --accent: #333333; + --accent-foreground: #c1c1c1; + --destructive: #5f8787; + --destructive-foreground: #121113; + --border: #222222; + --input: #222222; + --ring: #e78a53; + --chart-1: #5f8787; + --chart-2: #e78a53; + --chart-3: #fbcb97; + --chart-4: #888888; + --chart-5: #999999; + --sidebar: #121212; + --sidebar-foreground: #c1c1c1; + --sidebar-primary: #e78a53; + --sidebar-primary-foreground: #121113; + --sidebar-accent: #333333; + --sidebar-accent-foreground: #c1c1c1; + --sidebar-border: #222222; + --sidebar-ring: #e78a53; + --font-sans: Geist Mono, ui-monospace, monospace; + --font-serif: serif; + --font-mono: JetBrains Mono, monospace; + --radius: 0.75rem; + --shadow-x: 0px; + --shadow-y: 1px; + --shadow-blur: 4px; + --shadow-spread: 0px; + --shadow-opacity: 0.05; + --shadow-color: #000000; + --shadow-2xs: 0px 1px 4px 0px hsl(0 0% 0% / 0.03); + --shadow-xs: 0px 1px 4px 0px hsl(0 0% 0% / 0.03); + --shadow-sm: 0px 1px 4px 0px hsl(0 0% 0% / 0.05), + 0px 1px 2px -1px hsl(0 0% 0% / 0.05); + --shadow: 0px 1px 4px 0px hsl(0 0% 0% / 0.05), + 0px 1px 2px -1px hsl(0 0% 0% / 0.05); + --shadow-md: 0px 1px 4px 0px hsl(0 0% 0% / 0.05), + 0px 2px 4px -1px hsl(0 0% 0% / 0.05); + --shadow-lg: 0px 1px 4px 0px hsl(0 0% 0% / 0.05), + 0px 4px 6px -1px hsl(0 0% 0% / 0.05); + --shadow-xl: 0px 1px 4px 0px hsl(0 0% 0% / 0.05), + 0px 8px 10px -1px hsl(0 0% 0% / 0.05); + --shadow-2xl: 0px 1px 4px 0px hsl(0 0% 0% / 0.13); +} + +@layer base { + * { + @apply border-border outline-ring/50; } - - .cryptpad-card__actions { - flex-direction: column; - align-items: stretch; + body { + @apply bg-background text-foreground antialiased; } } diff --git a/rmcryptpad/ui/src/lib/metadata.ts b/rmcryptpad/ui/src/lib/metadata.ts deleted file mode 100644 index 842cd7f..0000000 --- a/rmcryptpad/ui/src/lib/metadata.ts +++ /dev/null @@ -1,35 +0,0 @@ -export type CardTheme = "light" | "dark"; - -export interface CryptPadCardData { - url: string; - sandbox_url: string; -} - -export interface CryptPadCardMeta { - callsign: string; - theme: string; -} - -export interface CryptPadCardDetails { - callsign: string; - theme: CardTheme; -} - -export function normalizeTheme(theme: string): CardTheme { - return theme.trim().toLowerCase() === "light" ? "light" : "dark"; -} - -export function buildCardDetails(meta: CryptPadCardMeta): CryptPadCardDetails { - return { - callsign: meta.callsign.trim() || "Unknown", - theme: normalizeTheme(meta.theme), - }; -} - -export function buildLoginUrl(url: string): string { - try { - return new URL("/login/", url).toString(); - } catch { - return `${url.replace(/\/+$/, "")}/login/`; - } -} diff --git a/rmcryptpad/ui/src/lib/metadata.tsx b/rmcryptpad/ui/src/lib/metadata.tsx new file mode 100644 index 0000000..fa5c778 --- /dev/null +++ b/rmcryptpad/ui/src/lib/metadata.tsx @@ -0,0 +1,41 @@ +import { createContext, useContext, ReactNode, useMemo } from "react"; + +export interface CryptPadCardData { + url: string; + sandbox_url: string; +} + +export interface MetaData { + theme: string; + callsign: string; +} + +const MetaContext = createContext(undefined); + +export const MetaProvider = ({ + children, + meta, +}: { + children: ReactNode; + meta: MetaData; +}) => { + const value = useMemo(() => meta, [meta]); + + return {children}; +}; + +export const useMeta = () => { + const context = useContext(MetaContext); + if (context === undefined) { + throw new Error("useMeta must be used within a MetaProvider"); + } + return context; +}; + +export function buildLoginUrl(url: string): string { + try { + return new URL("/login/", url).toString(); + } catch { + return `${url.replace(/\/+$/, "")}/login/`; + } +} diff --git a/rmcryptpad/ui/src/lib/utils.ts b/rmcryptpad/ui/src/lib/utils.ts new file mode 100644 index 0000000..a5ef193 --- /dev/null +++ b/rmcryptpad/ui/src/lib/utils.ts @@ -0,0 +1,6 @@ +import { clsx, type ClassValue } from "clsx"; +import { twMerge } from "tailwind-merge"; + +export function cn(...inputs: ClassValue[]) { + return twMerge(clsx(inputs)); +} diff --git a/rmcryptpad/ui/src/locales/en.json b/rmcryptpad/ui/src/locales/en.json new file mode 100644 index 0000000..c6a61e6 --- /dev/null +++ b/rmcryptpad/ui/src/locales/en.json @@ -0,0 +1,182 @@ +{ + "features": { + "docs": { + "steps": { + "onword": { + "title": "Other File Formats", + "description": "While CryptPad doesn't support real-time browser editing of e.g. .docx files, you can share them on CryptDrive and edit them locally." + }, + "collaborate": { + "description": "Write a document simultaneously with others.", + "title": "Real-time Collaboration" + }, + "create": { + "description": "Click '+' in the dashboard and select 'Rich Text'. A new encrypted document will be created.", + "title": "Creating a Document" + }, + "intro": { + "description": "CryptPad supports creating and editing Rich Text (RTF) documents in real-time directly in your browser.", + "title": "RTF Text Documents" + }, + "share": { + "description": "You can also discuss the document directly inside the document editing view.", + "title": "Discuss the Document" + } + }, + "title": "Write & Share Docs" + }, + "kanban": { + "steps": { + "create1": { + "title": "Creating a Kanban Board", + "description": "Click '+' and select 'Kanban'." + }, + "create2": { + "title": "Editing a Kanban Board", + "description": "Create columns for workflow stages and add cards for tasks. Shown here as an example is a task completion tracking board." + }, + "intro": { + "description": "CryptPad's Kanban app is like a sticky-note or whiteboard. You can arrange tasks by completion stage, or maintain a situation board.", + "title": "Kanban Task Boards" + }, + "manage": { + "description": "Click a card to add details. You can also add images to cards. Drag and drop cards between columns to update status.", + "title": "Managing Task Cards" + }, + "situation": { + "title": "Situation Board", + "description": "The Kanban app can also be used as a situation board. Update your understanding of units' mission progress, task, and enemy situation into cards, and share your surface picture with the command centre." + } + }, + "title": "Task Boards (Kanban)" + }, + "presentations": { + "steps": { + "onword": { + "title": "Other File Formats", + "description": "While CryptPad doesn't support real-time browser editing of e.g. PowerPoint files, you can share them on CryptDrive and edit and present them locally." + }, + "create": { + "description": "Click '+' and select 'Slide'", + "title": "Start Creating a Presentation" + }, + "intro": { + "description": "CryptPad includes a Markdown-based presentation tool for quickly creating slide decks. Unlike traditional PowerPoint, you write your presentation as text and the tool places it into the presentation.", + "title": "Create Presentations" + }, + "present": { + "description": "Write your slides as text on the left as shown. Separate slides with '---'. Click 'Present' to enter fullscreen mode. Navigate slides with arrow keys.", + "title": "Create & Present Slides" + } + }, + "title": "Presentations" + }, + "unitUse": { + "steps": { + "intro": { + "title": "Decentralized Service", + "description": "This CryptPad instance is unique — it exists only on this Deploy App server. All data stays on your own infrastructure and disappears completely when the server is shut down. A new server can be started empty but otherwise identical." + }, + "setup": { + "title": "Recommended Usage — Example", + "description": "The following describes a simple and effective way to use CryptPad in your team or unit. As the person responsible for the command system, start by creating a top-level team in CryptPad." + }, + "setup1": { + "title": "Create a Team for Your Unit (1/8)", + "description": "Create a team for your unit in CryptPad. This gives you a shared drive and calendar easily. Start by finding the Teams menu in the top-right corner." + }, + "setup2": { + "title": "Create a Team for Your Unit (2/8)", + "description": "The Teams menu shows CryptPad teams you belong to. Create a new one." + }, + "setup3": { + "title": "Create a Team for Your Unit (3/8)", + "description": "Give your team a descriptive name and continue." + }, + "setup4": { + "title": "Create a Team for Your Unit (4/8)", + "description": "Your team is created and visible in the teams menu. Continue by clicking your team's icon." + }, + "setup5": { + "title": "Create a Team for Your Unit (5/8)", + "description": "Here you'll find your team's shared drive. Next, navigate to the Members menu to create an invitation to your team." + }, + "setup6": { + "title": "Create a Team for Your Unit (6/8)", + "description": "To let others join your team, create an invite link. The image shows our recommended settings. Press Create link to generate a shareable invite link." + }, + "setup7": { + "title": "Create a Team for Your Unit (7/8)", + "description": "Copy the link and share it via your preferred messaging service. The recipient must be on your unit's Deploy App server — without Deploy App's mTLS certificate, your CryptPad cannot be accessed for security reasons." + }, + "setup8": { + "title": "Create a Team for Your Unit (8/8)", + "description": "This is what the joining member's view looks like. After this, they will see your team's shared drive." + }, + "calender1": { + "title": "Shared Calendar (1/3)", + "description": "In addition to files and collaborative editing, you can use shared calendars for the team. To do so, first navigate to the Calendars menu." + }, + "calender2": { + "title": "Shared Calendar (2/3)", + "description": "Create a new calendar and name it appropriately. Then select the Sharing menu." + }, + "calender3": { + "title": "Shared Calendar (3/3)", + "description": "Share the calendar with the team. After this, your team members will find this shared calendar in their own Calendars menu." + }, + "end": { + "title": "Moving to the Next Mission — Back Up Your Data", + "description": "From the team's Settings menu, you can easily back up the team's shared CryptDrive. When your mission ends, take a backup. You can upload it to your next Deploy App CryptPad server or another system as needed. Remember safe data handling!" + } + }, + "title": "CryptPad in Unit's Use" + } + }, + "home": { + "featuresTitle": "How to use CryptPad", + "launch": "Launch CryptPad!", + "loginAction": "Log in to CryptPad", + "loginInfo": "Certificate login as {{callsign}}" + }, + "onboarding": { + "back": "Back", + "clickToEnlarge": "Click to enlarge", + "completion": "Onboarding Completed!", + "completionDesc": "You're all set to start using CryptPad.", + "finish": "Finish", + "imageMissing": "Image not available", + "next": "Next", + "of": "of", + "progressSaved": "Your onboarding progress has been saved.", + "review": "What is CryptPad?", + "step": "Step", + "steps": { + "createDoc": { + "description": "Click the '+' button on your dashboard to create a new document. Choose from rich text, code, spreadsheets, kanban boards, presentations, whiteboards, forms, and more. All documents are encrypted automatically.", + "title": "Create Your First Document" + }, + "dashboard": { + "description": "After signing in, you'll see your CryptPad dashboard. From here you can create new documents, access recent files, manage your CryptDrive, and collaborate with team members.", + "title": "Your CryptPad Dashboard" + }, + "share": { + "description": "To share a document, click the share button in the toolbar. You can share with specific team members or create a link. Multiple users can edit simultaneously and see each other's changes in real time.", + "title": "Share with Your Team" + }, + "signIn": { + "description": "CryptPad is integrated with Deploy App's certificate-based authentication. Simply click 'Log in to CryptPad' and you will be automatically authenticated using your Deploy App credentials. No separate account or password is needed.", + "title": "Sign In with Deploy App" + }, + "welcome": { + "description": "CryptPad is a secure, end-to-end encrypted collaboration suite. Your team can create and collaboratively edit documents and share files securely. This Deploy App server has your team's own, decentralized CryptPad service.", + "title": "Welcome to CryptPad!" + }, + "whatIs": { + "description": "CryptPad ensures security such that not even the server administrator can read your files. You decide who to share with. Other users on this Deploy App server also need an invitation from you to read your files.", + "title": "Why is CryptPad Secure?" + } + }, + "title": "What is CryptPad?" + } +} diff --git a/rmcryptpad/ui/src/locales/fi.json b/rmcryptpad/ui/src/locales/fi.json new file mode 100644 index 0000000..8fe94ba --- /dev/null +++ b/rmcryptpad/ui/src/locales/fi.json @@ -0,0 +1,182 @@ +{ + "features": { + "docs": { + "steps": { + "onword": { + "description": "Vaikka CryptPad ei tue esimerkiksi .docx-asiakirjojen muokkausta reaaliaikaisesti selaimellasi, voit toki jakaa niitä CryptDrivellä ja muokata niitä paikallisesti.", + "title": "Muut tiedostoformaatit" + }, + "collaborate": { + "description": "Kirjoita dokumenttia samanaikaisesti muiden kanssa.", + "title": "Reaaliaikainen yhteistyö" + }, + "create": { + "description": "Klikkaa hallintapaneelissa '+' ja valitse 'Teksti'. Uusi salattu dokumentti luodaan.", + "title": "Dokumentin luominen" + }, + "intro": { + "description": "CryptPad tukee Rich Text-(rtf) dokumenttien luontia ja muokkausta reaaliaikaisesti suoraan selaimesta.", + "title": "Rtf-tekstidokumentit" + }, + "share": { + "description": "Voit myös keskustella asiakirjasta suoraan asiakirjan muokkausruudussa.", + "title": "Keskustele asiakirjasta" + } + }, + "title": "Kirjoita ja jaa dokumentteja" + }, + "kanban": { + "steps": { + "create1": { + "description": "Klikkaa '+' ja valitse 'Kanban'.", + "title": "Kanban-taulun luominen" + }, + "create2": { + "description": "Luo sarakkeita työnkulun vaiheille ja lisää kortteja tehtäville. Ohessa esimerkkinä kanban-tehtävien suoritusvaiheen seurantataulukko.", + "title": "Kanban-taulun muokkaaminen" + }, + "intro": { + "description": "CryptPadin Kanban-sovellus on kuin lappu- tai valkotaulu. Voit esimerkiksi järjestää tehtävät suoritusvaiheen perusteella, tai ylläpitää tilannekuvataulua.", + "title": "Kanban-tehtävätaulut" + }, + "manage": { + "description": "Klikkaa korttia lisätäksesi tietoja. Voit lisätä myös kuvia kortteihin. Vedä ja pudota kortteja sarakkeiden valilla päivittääksesi tilaa.", + "title": "Tehtäväkorttien hallinta" + }, + "situation": { + "description": "Kanban-sovellusta voi käyttää myös tilannetauluna. Päivitä käsityksesi yksikköjen tehtävän suoritusvaiheesta, tehtävästä ja vihollistilanteesta kortteihin, ja jaa näin tilannekeskukselle ymmärryksesi pintatilanteesta.", + "title": "Tehtäväkorttien hallinta" + } + }, + "title": "Tehtävätaulut (Kanban)" + }, + "presentations": { + "steps": { + "onword": { + "description": "Vaikka CryptPad ei tue esimerkiksi Powerpointien muokkausta reaaliaikaisesti selaimellasi, voit toki jakaa niitä CryptDrivellä ja muokata sekä esittää niitä paikallisesti.", + "title": "Muut tiedostoformaatit" + }, + "create": { + "description": "Klikkaa '+' ja valitse 'Dia'", + "title": "Aloita diaesityksen luonti" + }, + "intro": { + "description": "CryptPad sisältää Markdown-pohjaisen esitystyökalun, jolla voit luoda diaesityksiä nopeasti. Toisin kuin tavanomainen powerpoint, kirjoitat esityksesi tekstillä ja työkalu sijoittaa tekstisi esitykseen.", + "title": "Luo esityksiä" + }, + "present": { + "description": "Kirjoita diasi tekstinä vasemmalle kuvan osoittamalla tavalla. Diojen erotus tapahtuu merkinnällä '---'.Klikkaa 'Esitä'-painiketta siirtyäksesi koko nayton esitystilaan. Navigoi dioja nuolinäppäimilla.", + "title": "Luo ja esitä dioja" + } + }, + "title": "Esitykset" + }, + "unitUse": { + "steps": { + "intro": { + "description": "Tämä Cryptpad-instanssi on uniikki eli mukana vain tässä Deploy App-palvelimessa. Kaikki data pysyy omassa infrastruktuurissa - ja katoaa täysin, kun palvelin suljetaan. Uusi palvelin voidaan helposti luoda tyhjänä mutta muutoin samanlaisena.", + "title": "Hajautettu palvelu" + }, + "setup": { + "description": "Seuraavassa kuvataan yksinkertainen ja tehokas tapa käyttää Cryptpadia tiimissäsi tai yksikössäsi. Johtamisjärjestelmästä vastaavana, aloita luomalla kattotason tiimi Cryptpadissa.", + "title": "Suositeltu käyttötapa - esimerkki" + }, + "setup1": { + "description": "Luo yksiköllesi tiimi Cryptpadissa. Näin saat jaetun driven ja esimerkiksi kalenterin helposti. Aloita etsimällä Tiimi-valikko oikealta yläkulmasta.", + "title": "Luo tiimi yksiköllesi (1/8)" + }, + "setup2": { + "description": "Tiimi-valikossan näkyy Cryptpad-tiimit, johon kuulut. Luo uusi.", + "title": "Luo tiimi yksiköllesi (2/8)" + }, + "setup3": { + "description": "Anna tiimillesi kuvaava nimi ja jatka.", + "title": "Luo tiimi yksiköllesi (3/8)" + }, + "setup4": { + "description": "Tiimisi on luotu ja näkyy tiimivalikossa. Jatka painamalla tiimisi kuvaketta.", + "title": "Luo tiimi yksiköllesi (4/8)" + }, + "setup5": { + "description": "Täältä löydät tiimisi jaetun driven. Seuraavaksi navigoi Members (Jäsenet)-valikkoon luodaksesi kutsun tiimiisi.", + "title": "Luo tiimi yksiköllesi (5/8)" + }, + "setup6": { + "description": "Jotta muut pääsevät tiimisi, sinun on luotava kutsulinkki tiimiisi. Kuvassa on suosittelemamme asetukset. Paina lopuksi Create link luodaksesi jaettavan kutsulinkin.", + "title": "Luo tiimi yksiköllesi (6/8)" + }, + "setup7": { + "description": "Kopioi linkki ja jaa se haluamassasi viestipalvelussa. Vastaanottajan tulee olla yksikkösi Deploy App-palvelimessa mukana - ilman Deploy Appin mTLS-sertifikaattia Cryptpadiinne ei turvallisuussyistä pääse.", + "title": "Luo tiimi yksiköllesi (7/8)" + }, + "setup8": { + "description": "Liittyvän tiiminjäsenen näkymä on seuraavankaltainen. Tämän jälkeen hän näkee tiimisi jaetun driven.", + "title": "Luo tiimi yksiköllesi (8/8)" + }, + "calender1": { + "description": "Tiedostojen ja yhteismuokkauksen lisäksi voit käyttää tiimille jaettuja kalentereita. Tehdäksesi niin, siirry ensin Kalenterit-valikkoon", + "title": "Jaettu kalenteri (1/3)" + }, + "calender2": { + "description": "Luo uusi kalenteri ja nimeä se sopivasti. Tämän jälkeen valitse Jakaminen-valikko.", + "title": "Jaettu kalenteri (2/3)" + }, + "calender3": { + "description": "Jaa kalenteri tiimille. Tämän jälkeen tiimisi jäsenet löytävät omasta Kalenterit-valikostaan tämän jaetun kalenterin.", + "title": "Jaettu kalenteri (3/3)" + }, + "end": { + "description": "Tiimin Asetukset-valikosta voit helposti varmuuskopioida tiimin jaetun Cryptdriven, eli kaikki sinne luodut tiedot. Tehtävänne päättyessä, ota varmuuskopio. Voit ladata sen seuraavalle Deploy App Cryptpad-palvelimellesi, tai muuhun järjestelmään tarpeen mukaan. Muista turvallinen tietojenkäsittely!", + "title": "Siirtyminen seuraavaan tehtävään - varmuuskopioi tiedot" + } + }, + "title": "CryptPad yksikön käytössa" + } + }, + "home": { + "featuresTitle": "CryptPadin käyttö", + "launch": "Avaa CryptPad!", + "loginAction": "Kirjaudu CryptPadiin!", + "loginInfo": "Tunnistaudut peitenimellä {{callsign}}" + }, + "onboarding": { + "back": "Takaisin", + "clickToEnlarge": "Klikkaa suurentaaksesi", + "completion": "Perehdytys valmis!", + "completionDesc": "Olet valmis aloittamaan CryptPadin käytön.", + "finish": "Valmis", + "imageMissing": "Kuva ei saatavilla", + "next": "Seuraava", + "of": "/", + "progressSaved": "Perehdytyksen edistyminen tallennettu.", + "review": "Mikä on CryptPad?", + "step": "Vaihe", + "steps": { + "createDoc": { + "description": "Klikkaa '+'-painiketta hallintapaneelissa luodaksesi uuden dokumentin. Valitse tekstidokumenteista, koodieditorista, taulukoista, kanban-tauluista, esityksistä, piirtotauluista, lomakkeista ja muista. Kaikki dokumentit salataan automaattisesti.", + "title": "Luo ensimmäinen dokumentti" + }, + "dashboard": { + "description": "Kirjautumisen jälkeen näkyviin tulee CryptPad-hallintapaneeli. Täältä voit luoda uusia dokumentteja, käyttää viimeaikaisia tiedostoja, hallita CryptDrivea (tiedostojen tallennus) ja tehdä yhteistyötä tiimin jäsenien kanssa.", + "title": "CryptPad-hallintapaneeli" + }, + "share": { + "description": "Jakaaksesi dokumentin, klikkaa jakopainiketta työkalupalkissa. Voit jakaa tietyille tiimin jäsenille tai luoda linkin. Jaetut dokumentit tukevat reaaliaikaista yhteistyötä: useat käyttäjät voivat muokata samanaikaisesti.", + "title": "Jaa tiimisi kanssa" + }, + "signIn": { + "description": "CryptPad on integroitu Deploy Appin sertifikaattipohjaiseen kirjautumiseen. Klikkaa 'Avaa CryptPad!' ja kirjaudut automaattisesti Deploy App -tunnuksillasi. Erillistä tiliä tai salasanaa ei tarvita.", + "title": "Kirjaudu Deploy Appilla" + }, + "welcome": { + "description": "CryptPad on turvallinen, päästä päähän salattu yhteistyöalusta. Sen avulla tiimisi voi luoda ja yhtäaikaisesti muokata dokumentteja sekä jakaa tiedostoja turvallisesti. Tässä Deploy App-palvelimessa on sinun tiimisi oma, hajautettu Cryptpad-palvelu.", + "title": "Tervetuloa CryptPadiin!" + }, + "whatIs": { + "description": "CryptPad varmistaa turvallisuuden siten, että edes palvelimen ylläpitäjä ei voi lukea tiedostojasi. Sinä päätät, kenelle jaat tiedostot. Myös muut tämän Deploy App-palvelimen käyttäjät tarvitsevat sinulta kutsun, jotta he voivat lukea tiedostojasi.", + "title": "Miksi Cryptpad on turvallinen?" + } + }, + "title": "Mikä on CryptPad?" + } +} diff --git a/rmcryptpad/ui/src/locales/sv.json b/rmcryptpad/ui/src/locales/sv.json new file mode 100644 index 0000000..0e154a9 --- /dev/null +++ b/rmcryptpad/ui/src/locales/sv.json @@ -0,0 +1,182 @@ +{ + "features": { + "docs": { + "steps": { + "onword": { + "title": "Andra filformat", + "description": "Även om CryptPad inte stöder realtidsredigering av t.ex. .docx-filer i webbläsaren kan du dela dem via CryptDrive och redigera dem lokalt." + }, + "collaborate": { + "description": "Skriv ett dokument samtidigt med andra.", + "title": "Realtidssamarbete" + }, + "create": { + "description": "Klicka på '+' i instrumentpanelen och välj 'Rich Text'. Ett nytt krypterat dokument skapas.", + "title": "Skapa ett dokument" + }, + "intro": { + "description": "CryptPad stöder skapande och redigering av RTF-dokument i realtid direkt i webbläsaren.", + "title": "RTF-textdokument" + }, + "share": { + "description": "Du kan också diskutera dokumentet direkt i dokumentredigeringsvyn.", + "title": "Diskutera dokumentet" + } + }, + "title": "Skriv och dela dokument" + }, + "kanban": { + "steps": { + "create1": { + "title": "Skapa en Kanban-tavla", + "description": "Klicka på '+' och välj 'Kanban'." + }, + "create2": { + "title": "Redigera en Kanban-tavla", + "description": "Skapa kolumner för arbetsflödessteg och lägg till kort för uppgifter. Visas här som ett exempel på en uppgiftsuppföljningstavla." + }, + "intro": { + "description": "CryptPads Kanban-app är som en lapp- eller whiteboard-tavla. Du kan arrangera uppgifter efter utförandesteg eller underhålla en situationstavla.", + "title": "Kanban-uppgiftstavlor" + }, + "manage": { + "description": "Klicka på ett kort för att lägga till detaljer. Du kan också lägga till bilder på korten. Dra och släpp kort mellan kolumner för att uppdatera status.", + "title": "Hantera uppgiftskort" + }, + "situation": { + "title": "Situationstavla", + "description": "Kanban-appen kan också användas som situationstavla. Uppdatera din förståelse av enheternas uppdragsläge, uppgift och fiendesituation i kort och dela din lägesbild med ledningscentralen." + } + }, + "title": "Uppgiftstavlor (Kanban)" + }, + "presentations": { + "steps": { + "onword": { + "title": "Andra filformat", + "description": "Även om CryptPad inte stöder realtidsredigering av t.ex. PowerPoint-filer i webbläsaren kan du dela dem via CryptDrive och redigera och presentera dem lokalt." + }, + "create": { + "description": "Klicka på '+' och välj 'Slide'", + "title": "Börja skapa en presentation" + }, + "intro": { + "description": "CryptPad inkluderar ett Markdown-baserat presentationsverktyg för att snabbt skapa bildspel. Till skillnad från traditionell PowerPoint skriver du din presentation som text och verktyget placerar den i presentationen.", + "title": "Skapa presentationer" + }, + "present": { + "description": "Skriv dina bilder som text till vänster som visas. Separera bilder med '---'. Klicka på 'Presentera' för att gå in i helskärmsläge. Navigera bilder med piltangenterna.", + "title": "Skapa och presentera bilder" + } + }, + "title": "Presentationer" + }, + "unitUse": { + "steps": { + "intro": { + "title": "Decentraliserad tjänst", + "description": "Denna CryptPad-instans är unik — den finns bara på denna Deploy App-server. All data stannar på din egen infrastruktur och försvinner helt när servern stängs av. En ny server kan startas tom men annars identisk." + }, + "setup": { + "title": "Rekommenderad användning — Exempel", + "description": "Nedan beskrivs ett enkelt och effektivt sätt att använda CryptPad i ditt team eller enhet. Som den person som ansvarar för ledningssystemet, börja med att skapa ett toppnivåteam i CryptPad." + }, + "setup1": { + "title": "Skapa ett team för din enhet (1/8)", + "description": "Skapa ett team för din enhet i CryptPad. Detta ger dig enkelt en delad enhet och kalender. Börja med att hitta Team-menyn i det övre högra hörnet." + }, + "setup2": { + "title": "Skapa ett team för din enhet (2/8)", + "description": "Team-menyn visar CryptPad-team du tillhör. Skapa ett nytt." + }, + "setup3": { + "title": "Skapa ett team för din enhet (3/8)", + "description": "Ge ditt team ett beskrivande namn och fortsätt." + }, + "setup4": { + "title": "Skapa ett team för din enhet (4/8)", + "description": "Ditt team är skapat och syns i teammenyn. Fortsätt genom att klicka på ditt teams ikon." + }, + "setup5": { + "title": "Skapa ett team för din enhet (5/8)", + "description": "Här hittar du ditt teams delade enhet. Navigera sedan till Medlemmar-menyn för att skapa en inbjudan till ditt team." + }, + "setup6": { + "title": "Skapa ett team för din enhet (6/8)", + "description": "För att låta andra gå med i ditt team, skapa en inbjudningslänk. Bilden visar våra rekommenderade inställningar. Tryck på Skapa länk för att generera en delbar inbjudningslänk." + }, + "setup7": { + "title": "Skapa ett team för din enhet (7/8)", + "description": "Kopiera länken och dela den via din föredragna meddelandetjänst. Mottagaren måste vara på din enhets Deploy App-server — utan Deploy Apps mTLS-certifikat kan din CryptPad inte nås av säkerhetsskäl." + }, + "setup8": { + "title": "Skapa ett team för din enhet (8/8)", + "description": "Så här ser den anslutande teammedlemmens vy ut. Efter detta ser de ditt teams delade enhet." + }, + "calender1": { + "title": "Delad kalender (1/3)", + "description": "Förutom filer och gemensam redigering kan du använda delade kalendrar för teamet. För att göra det, navigera först till Kalendrar-menyn." + }, + "calender2": { + "title": "Delad kalender (2/3)", + "description": "Skapa en ny kalender och namnge den lämpligt. Välj sedan Dela-menyn." + }, + "calender3": { + "title": "Delad kalender (3/3)", + "description": "Dela kalendern med teamet. Därefter hittar dina teammedlemmar denna delade kalender i sin egen Kalendrar-meny." + }, + "end": { + "title": "Övergång till nästa uppdrag — Säkerhetskopiera dina data", + "description": "Från teamets Inställningar-meny kan du enkelt säkerhetskopiera teamets delade CryptDrive. När ditt uppdrag är slut, ta en säkerhetskopia. Du kan ladda upp den till din nästa Deploy App CryptPad-server eller annat system vid behov. Kom ihåg säker datahantering!" + } + }, + "title": "CryptPad i enhetens användning" + } + }, + "home": { + "featuresTitle": "Hur man använder CryptPad", + "launch": "Starta CryptPad!", + "loginAction": "Logga in på CryptPad", + "loginInfo": "Certifikatinloggning som {{callsign}}" + }, + "onboarding": { + "back": "Tillbaka", + "clickToEnlarge": "Klicka för att förstora", + "completion": "Introduktion klar!", + "completionDesc": "Du är redo att börja använda CryptPad.", + "finish": "Klar", + "imageMissing": "Bilden är inte tillgänglig", + "next": "Nästa", + "of": "av", + "progressSaved": "Din introduktionsframsteg har sparats.", + "review": "Vad är CryptPad?", + "step": "Steg", + "steps": { + "createDoc": { + "description": "Klicka på '+'-knappen på instrumentpanelen för att skapa ett nytt dokument. Välj från textdokument, kodredigerare, kalkylblad, kanban-tavlor, presentationer, whiteboards, formulär och mer. Alla dokument krypteras automatiskt.", + "title": "Skapa ditt första dokument" + }, + "dashboard": { + "description": "Efter inloggning ser du din CryptPad-instrumentpanel. Härifrån kan du skapa nya dokument, komma åt senaste filer, hantera din CryptDrive och samarbeta med teammedlemmar.", + "title": "Din CryptPad-instrumentpanel" + }, + "share": { + "description": "Klicka på delningsknappen i verktygsfältet för att dela ett dokument. Du kan dela med specifika teammedlemmar eller skapa en länk. Flera användare kan redigera samtidigt och se varandras ändringar i realtid.", + "title": "Dela med ditt team" + }, + "signIn": { + "description": "CryptPad är integrerat med Deploy Apps certifikatbaserade autentisering. Klicka bara på 'Logga in på CryptPad' så autentiseras du automatiskt med dina Deploy App-uppgifter. Inget separat konto eller lösenord behövs.", + "title": "Logga in med Deploy App" + }, + "welcome": { + "description": "CryptPad är en säker, ändtill-ända-krypterad samarbetsplattform. Ditt team kan skapa och gemensamt redigera dokument samt dela filer säkert. Denna Deploy App-server har din enhets egna, decentraliserade CryptPad-tjänst.", + "title": "Välkommen till CryptPad!" + }, + "whatIs": { + "description": "CryptPad säkerställer att inte ens serveradministratören kan läsa dina filer. Du bestämmer vem du delar med. Även andra användare på denna Deploy App-server behöver en inbjudan från dig för att kunna läsa dina filer.", + "title": "Varför är CryptPad säkert?" + } + }, + "title": "Vad är CryptPad?" + } +} diff --git a/rmcryptpad/ui/src/main.tsx b/rmcryptpad/ui/src/main.tsx index a4159ac..b8e7fd5 100644 --- a/rmcryptpad/ui/src/main.tsx +++ b/rmcryptpad/ui/src/main.tsx @@ -1,17 +1,70 @@ -import { createRoot } from "react-dom/client"; - +import React from "react"; +import ReactDOM from "react-dom/client"; import App from "./App"; +import "./i18n"; + +import { + Outlet, + RouterProvider, + createRouter, + createRootRoute, + createRoute, + redirect, +} from "@tanstack/react-router"; + +const rootRoute = createRootRoute({ + component: () => ( + <> + + + ), +}); + +const cryptpadRoute = createRoute({ + getParentRoute: () => rootRoute, + path: "product/cryptpad/$", + component: () => { + const SAMPLE_DATA = { + url: "https://mtls.cryptpad.localhost:8443", + sandbox_url: "https://mtls.sandbox.cryptpad.localhost:8443", + }; + return ( + + ); + }, +}); + +const indexRoute = createRoute({ + getParentRoute: () => rootRoute, + path: "/", + beforeLoad: () => { + throw redirect({ + // @ts-expect-error: TanStack Router strict mode requires registered routes + to: "/product/cryptpad", + }); + }, + component: () =>

Redirecting...

, +}); -const root = document.getElementById("root"); - -if (root) { - createRoot(root).render( - , - ); +const routeTree = rootRoute.addChildren([cryptpadRoute, indexRoute]); + +const router = createRouter({ routeTree }); + +declare module "@tanstack/react-router" { + interface Register { + router: typeof router; + } } + +if (__USE_GLOBAL_CSS__ == true) { + import("./index.css"); +} + +ReactDOM.createRoot(document.getElementById("root") as HTMLElement).render( + + + , +); diff --git a/rmcryptpad/ui/src/pages/HomePage.tsx b/rmcryptpad/ui/src/pages/HomePage.tsx new file mode 100644 index 0000000..1c90fca --- /dev/null +++ b/rmcryptpad/ui/src/pages/HomePage.tsx @@ -0,0 +1,320 @@ +import { useState } from "react"; +import { + ExternalLink, + FileText, + KanbanSquare, + Presentation, + Users, +} from "lucide-react"; +import { Button } from "@/components/ui/button"; +import { OnboardingGuide } from "@/components/OnboardingGuide"; +import { FeatureGuide } from "@/components/FeatureGuide"; +import { Toaster } from "@/components/ui/sonner"; +import { useTranslation } from "react-i18next"; +import { PRODUCT_SHORTNAME } from "@/App"; +import { useMeta } from "@/lib/metadata"; +import { buildLoginUrl, type CryptPadCardData } from "@/lib/metadata"; + +interface FeatureStepDef { + id: string; + title: string; + description: string; + image: string; + mobileImage?: string; +} + +function getFeatureSteps(featureKey: string): FeatureStepDef[] { + // Map feature keys to their actual asset folder names + const folderName = featureKey === "presentations" ? "present" : featureKey; + const basePath = `/ui/cryptpad/assets/features/${folderName}`; + const steps: Record = { + docs: [ + { + id: "docs_intro", + title: "features.docs.steps.intro.title", + description: "features.docs.steps.intro.description", + image: `/ui/cryptpad/assets/cryptpad1.png`, + }, + { + id: "docs_create", + title: "features.docs.steps.create.title", + description: "features.docs.steps.create.description", + image: `${basePath}/docs1.png`, + }, + { + id: "docs_collaborate", + title: "features.docs.steps.collaborate.title", + description: "features.docs.steps.collaborate.description", + image: `${basePath}/docs2.png`, + }, + { + id: "docs_share", + title: "features.docs.steps.share.title", + description: "features.docs.steps.share.description", + image: `${basePath}/docs3.png`, + }, + { + id: "docs_onword", + title: "features.docs.steps.onword.title", + description: "features.docs.steps.onword.description", + image: `/ui/cryptpad/assets/cryptpad2.png`, + }, + ], + kanban: [ + { + id: "kanban_intro", + title: "features.kanban.steps.intro.title", + description: "features.kanban.steps.intro.description", + image: `/ui/cryptpad/assets/cryptpad1.png`, + }, + { + id: "kanban_create1", + title: "features.kanban.steps.create1.title", + description: "features.kanban.steps.create1.description", + image: `${basePath}/kanban1.png`, + }, + { + id: "kanban_create2", + title: "features.kanban.steps.create2.title", + description: "features.kanban.steps.create2.description", + image: `${basePath}/kanban2.png`, + }, + { + id: "kanban_manage", + title: "features.kanban.steps.manage.title", + description: "features.kanban.steps.manage.description", + image: `${basePath}/kanban3.png`, + }, + { + id: "kanban_situation", + title: "features.kanban.steps.situation.title", + description: "features.kanban.steps.situation.description", + image: `${basePath}/kanban4.png`, + }, + ], + presentations: [ + { + id: "pres_intro", + title: "features.presentations.steps.intro.title", + description: "features.presentations.steps.intro.description", + image: `/ui/cryptpad/assets/cryptpad1.png`, + }, + { + id: "pres_create", + title: "features.presentations.steps.create.title", + description: "features.presentations.steps.create.description", + image: `${basePath}/present1.png`, + }, + { + id: "pres_present", + title: "features.presentations.steps.present.title", + description: "features.presentations.steps.present.description", + image: `${basePath}/present2.png`, + }, + { + id: "pres_onword", + title: "features.presentations.steps.onword.title", + description: "features.presentations.steps.onword.description", + image: `/ui/cryptpad/assets/cryptpad2.png`, + }, + ], + unitUse: [ + { + id: "unit_intro", + title: "features.unitUse.steps.intro.title", + description: "features.unitUse.steps.intro.description", + image: `/ui/cryptpad/assets/cryptpad1.png`, + }, + { + id: "unit_setup", + title: "features.unitUse.steps.setup.title", + description: "features.unitUse.steps.setup.description", + image: `/ui/cryptpad/assets/cryptpad2.png`, + }, + { + id: "unit_setup1", + title: "features.unitUse.steps.setup1.title", + description: "features.unitUse.steps.setup1.description", + image: `${basePath}/setup1.png`, + }, + { + id: "unit_setup2", + title: "features.unitUse.steps.setup2.title", + description: "features.unitUse.steps.setup2.description", + image: `${basePath}/setup2.png`, + }, + { + id: "unit_setup3", + title: "features.unitUse.steps.setup3.title", + description: "features.unitUse.steps.setup3.description", + image: `${basePath}/setup3.png`, + }, + { + id: "unit_setup4", + title: "features.unitUse.steps.setup4.title", + description: "features.unitUse.steps.setup4.description", + image: `${basePath}/setup4.png`, + }, + { + id: "unit_setup5", + title: "features.unitUse.steps.setup5.title", + description: "features.unitUse.steps.setup5.description", + image: `${basePath}/setup5.png`, + }, + { + id: "unit_setup6", + title: "features.unitUse.steps.setup6.title", + description: "features.unitUse.steps.setup6.description", + image: `${basePath}/setup6.png`, + }, + { + id: "unit_setup7", + title: "features.unitUse.steps.setup7.title", + description: "features.unitUse.steps.setup7.description", + image: `${basePath}/setup7.png`, + }, + { + id: "unit_setup8", + title: "features.unitUse.steps.setup8.title", + description: "features.unitUse.steps.setup8.description", + image: `${basePath}/setup8.png`, + }, + { + id: "unit_calender1", + title: "features.unitUse.steps.calender1.title", + description: "features.unitUse.steps.calender1.description", + image: `${basePath}/calender1.png`, + }, + { + id: "unit_calender2", + title: "features.unitUse.steps.calender2.title", + description: "features.unitUse.steps.calender2.description", + image: `${basePath}/calender2.png`, + }, + { + id: "unit_calender3", + title: "features.unitUse.steps.calender3.title", + description: "features.unitUse.steps.calender3.description", + image: `${basePath}/calender3.png`, + }, + { + id: "unit_end", + title: "features.unitUse.steps.end.title", + description: "features.unitUse.steps.end.description", + image: `${basePath}/end.png`, + }, + ], + }; + + return steps[featureKey] || []; +} + +interface HomePageProps { + data: CryptPadCardData; +} + +export const HomePage = ({ data }: HomePageProps) => { + const { t } = useTranslation(PRODUCT_SHORTNAME); + const meta = useMeta(); + const loginUrl = buildLoginUrl(data.url); + + const [activeFeature, setActiveFeature] = useState(null); + + const featureButtons = [ + { + key: "docs", + icon: FileText, + label: "features.docs.title", + }, + { + key: "kanban", + icon: KanbanSquare, + label: "features.kanban.title", + }, + { + key: "presentations", + icon: Presentation, + label: "features.presentations.title", + }, + { + key: "unitUse", + icon: Users, + label: "features.unitUse.title", + }, + ]; + + return ( +
+ {/* Launch CryptPad Button */} + + + {/* How to use CryptPad's features */} +
+

+ {t("home.featuresTitle")} +

+
+ {featureButtons.map(({ key, icon: Icon, label }) => ( + + ))} +
+
+ + {/* Feature Guide Drawers */} + {featureButtons.map(({ key }) => ( + { + if (!open) setActiveFeature(null); + }} + /> + ))} + + + +
+ ); +}; diff --git a/rmcryptpad/ui/src/vite-env.d.ts b/rmcryptpad/ui/src/vite-env.d.ts new file mode 100644 index 0000000..55c7f7b --- /dev/null +++ b/rmcryptpad/ui/src/vite-env.d.ts @@ -0,0 +1,2 @@ +/// +declare const __USE_GLOBAL_CSS__: boolean; diff --git a/rmcryptpad/ui/tsconfig.json b/rmcryptpad/ui/tsconfig.json index 9d27d80..d935db8 100644 --- a/rmcryptpad/ui/tsconfig.json +++ b/rmcryptpad/ui/tsconfig.json @@ -1,22 +1,30 @@ { "compilerOptions": { - "target": "ES2022", - "useDefineForClassFields": true, - "lib": ["ES2022", "DOM", "DOM.Iterable"], - "module": "ESNext", - "skipLibCheck": true, - "moduleResolution": "Bundler", - "allowImportingTsExtensions": false, - "resolveJsonModule": true, + "allowJs": false, + "allowSyntheticDefaultImports": true, + "baseUrl": ".", + "esModuleInterop": false, + "forceConsistentCasingInFileNames": true, "isolatedModules": true, - "noEmit": true, "jsx": "react-jsx", + "lib": ["DOM", "DOM.Iterable", "ESNext"], + "module": "ESNext", + "moduleResolution": "Node", + "noEmit": true, + "paths": { + "@/*": ["./src/*"] + }, + "resolveJsonModule": true, + "skipLibCheck": true, "strict": true, + "target": "ESNext", "types": ["vite/client", "vitest/globals"], - "baseUrl": ".", - "paths": { - "@/*": ["src/*"] - } + "useDefineForClassFields": true }, - "include": ["src", "vite.config.ts"] + "include": ["src"], + "references": [ + { + "path": "./tsconfig.node.json" + } + ] } diff --git a/rmcryptpad/ui/tsconfig.node.json b/rmcryptpad/ui/tsconfig.node.json new file mode 100644 index 0000000..bd6ccfd --- /dev/null +++ b/rmcryptpad/ui/tsconfig.node.json @@ -0,0 +1,10 @@ +{ + "compilerOptions": { + "allowSyntheticDefaultImports": true, + "composite": true, + "module": "ESNext", + "moduleResolution": "Node", + "skipLibCheck": true + }, + "include": ["vite.config.ts"] +} diff --git a/rmcryptpad/ui/vite.config.ts b/rmcryptpad/ui/vite.config.ts index 50cf0d4..01668d4 100644 --- a/rmcryptpad/ui/vite.config.ts +++ b/rmcryptpad/ui/vite.config.ts @@ -3,13 +3,23 @@ import react from "@vitejs/plugin-react"; import { federation } from "@module-federation/vite"; import path from "path"; import { fileURLToPath } from "url"; - -import { cryptpadUiBasePath } from "./src/lib/public-path"; +import tailwindcss from "@tailwindcss/vite"; const __dirname = path.dirname(fileURLToPath(import.meta.url)); +const isTesting = !!process.env.VITEST; export default defineConfig({ - base: cryptpadUiBasePath, + server: { + fs: { + allow: [".", "../shared"], + }, + proxy: { + "/ui/cryptpad": { + target: "http://localhost:4174", + rewrite: (path) => path.replace(/^\/ui\/cryptpad/, ""), + }, + }, + }, build: { target: "chrome89", emptyOutDir: true, @@ -18,31 +28,47 @@ export default defineConfig({ }, }, plugins: [ - federation({ - filename: "remoteEntry.js", - name: "cryptpad-integration", - exposes: { - "./remote-ui": "./src/App.tsx", - }, - remotes: {}, - shared: { - react: { - requiredVersion: "^18.3.1", - singleton: true, - }, - "react-dom": { - requiredVersion: "^18.3.1", - singleton: true, - }, - }, - runtime: "@module-federation/enhanced/runtime", - }), + ...(isTesting + ? [] + : [ + federation({ + filename: "remoteEntry.js", + name: "cryptpad-integration", + exposes: { + "./remote-ui": "./src/App.tsx", + }, + remotes: {}, + shared: { + react: { + requiredVersion: "18.3.1", + singleton: true, + }, + i18next: { + requiredVersion: "25.6.2", + singleton: true, + }, + "react-i18next": { + requiredVersion: "16.3.3", + singleton: true, + }, + "@tanstack/react-router": { + requiredVersion: "1.135.2", + singleton: true, + }, + }, + runtime: "@module-federation/enhanced/runtime", + }), + ]), react(), + tailwindcss(), ], resolve: { - alias: { - "@": path.resolve(__dirname, "./src"), - }, + alias: { "@": path.resolve(__dirname, "./src") }, + }, + define: { + __USE_GLOBAL_CSS__: JSON.stringify( + process.env.VITE_USE_GLOBAL_CSS === "true", + ), }, test: { environment: "jsdom", diff --git a/tests/test_compose_smoke.sh b/tests/test_compose_smoke.sh index a26e9d7..8199174 100644 --- a/tests/test_compose_smoke.sh +++ b/tests/test_compose_smoke.sh @@ -127,8 +127,8 @@ curl_status() { wait_for_http() { local host="$1" local url="$2" - local attempt status - for attempt in $(seq 1 30); do + local _attempt status + for _attempt in $(seq 1 30); do status="$(curl_status "${host}" "${url}" || true)" case "${status}" in 200|301|302|303) diff --git a/volumes/postgres/.gitkeep b/volumes/postgres/.gitkeep index 8b13789..e69de29 100644 --- a/volumes/postgres/.gitkeep +++ b/volumes/postgres/.gitkeep @@ -1 +0,0 @@ - diff --git a/volumes/rmcryptpad/.gitkeep b/volumes/rmcryptpad/.gitkeep index 8b13789..e69de29 100644 --- a/volumes/rmcryptpad/.gitkeep +++ b/volumes/rmcryptpad/.gitkeep @@ -1 +0,0 @@ -