@cryptotaxi247 / CoPilot / commits / 560eba71

UI review (#514)

* refactor: CoPilot Actions list/card * chore: update frontend dependencies * refactor * refactor * refactor: InvokeActionForm * feat: add pagination support to inventory API and response model * feat: add copilot actions pagination * feat: enhance vulnerability search sorting by EPSS score and detection date * refactor: vulnerabilities * refactor: vulnerabilities * refactor: vulnerability card * refactor: vulnerability card * refactor: vulnerability card content * refactor: enhance sorting of SCA results by agent minimum score * refactor: sca list * refactor: sca list * lint * refactor: sca card * lint * refactor: stats * chore: update frontend dependencies * precommit fixes --------- Co-authored-by: Davide Di Modica <webmaster.ddm@gmail.com>

taylor_socfortress committed Sep 19, 2025 at 07:27 UTC 560eba719fcb34e79c8617f0657bb58de13eb8f0
60 files changed +4691 -4701
backend/app/agents/sca/routes/sca.py
+2
@@ -48,6 +48,7 @@ async def search_sca_results_overview(
48 - Efficient pagination for large result sets
49 - Comprehensive statistics and aggregations
50 - No database storage required - direct from Wazuh Manager
51 + - **Intelligent sorting: Results are sorted by agent minimum score (lowest first)**
52
53 **Use Cases:**
54 - Get organization-wide SCA compliance overview
@@ -60,6 +61,7 @@ async def search_sca_results_overview(
61 - Automatic error handling for unavailable agents
62 - Optimized data collection and processing
63 - Smart filtering to reduce data transfer
64 + - **Smart sorting: Agents with lowest compliance scores appear first for priority attention**
65
66 **Filtering Options:**
67 - **customer_code**: Filter by specific customer/organization
backend/app/agents/sca/services/sca.py
+28 -1
@@ -202,6 +202,33 @@ async def search_sca_overview(
202 max_score=max_score,
203 )
204
205 + # Sort results by agent's minimum score (lowest first)
206 + if all_sca_results:
207 + # Group results by agent to find minimum score per agent
208 + agent_min_scores = {}
209 + agent_results = {}
210 +
211 + for result in all_sca_results:
212 + agent_id = result.agent_id
213 + if agent_id not in agent_min_scores:
214 + agent_min_scores[agent_id] = result.score
215 + agent_results[agent_id] = []
216 + else:
217 + agent_min_scores[agent_id] = min(agent_min_scores[agent_id], result.score)
218 + agent_results[agent_id].append(result)
219 +
220 + # Sort agents by their minimum score (lowest first)
221 + sorted_agent_ids = sorted(agent_min_scores.keys(), key=lambda x: agent_min_scores[x])
222 +
223 + # Rebuild the results list with agents sorted by their minimum score
224 + all_sca_results = []
225 + for agent_id in sorted_agent_ids:
226 + # Sort policies within each agent by score (lowest first)
227 + agent_policies = sorted(agent_results[agent_id], key=lambda x: x.score)
228 + all_sca_results.extend(agent_policies)
229 +
230 + logger.info("Sorted SCA results by agent minimum scores (lowest first)")
231 +
232 total_count = len(all_sca_results)
233
234 # Calculate pagination
@@ -240,7 +267,7 @@ async def search_sca_overview(
267 has_next=page < total_pages,
268 has_previous=page > 1,
269 success=True,
243 - message=f"Found {total_count} SCA results across {len(unique_agents)} agents",
270 + message=f"Found {total_count} SCA results across {len(unique_agents)} agents (sorted by agent minimum score, lowest first)",
271 filters_applied=filters_applied,
272 )
273
backend/app/agents/vulnerabilities/routes/vulnerabilities.py
+7 -1
@@ -377,7 +377,8 @@ async def search_vulnerabilities(
377 **Performance:**
378 - Handles large datasets efficiently with pagination
379 - Optimized Elasticsearch queries for fast response times
380 - - Automatic sorting by detection date and severity
380 + - Automatic sorting by EPSS score (highest to lowest) when include_epss=True
381 + - Falls back to detection date and severity sorting when include_epss=False
382 - EPSS scoring can be disabled for faster response times
383
384 **Filtering Options:**
@@ -390,12 +391,17 @@ async def search_vulnerabilities(
391 **EPSS Integration:**
392 - **include_epss**: Include EPSS scores and percentiles for vulnerabilities
393 - Provides risk assessment data from FIRST.org
394 + - Results are automatically sorted by EPSS score (highest to lowest)
395 - May impact response time due to external API calls
396
397 **Pagination:**
398 - **page**: Page number (starts at 1)
399 - **page_size**: Results per page (1-1000, default: 50)
400
401 + **Sorting Behavior:**
402 + - When **include_epss=True**: Results sorted by EPSS score (highest to lowest), then by severity, then by CVE ID
403 + - When **include_epss=False**: Results sorted by detection date (newest first), then by severity
404 +
405 Args:
406 customer_code: Optional customer code filter
407 agent_name: Optional agent hostname filter
backend/app/agents/vulnerabilities/services/vulnerabilities.py
+29
@@ -1061,9 +1061,38 @@ async def search_vulnerabilities_from_indexer(
1061 logger.error(f"Error processing vulnerability document: {e}")
1062 continue
1063
1064 + # Sort vulnerabilities by EPSS score (highest to lowest) if EPSS is included
1065 + if include_epss:
1066 + # Sort by EPSS score descending, treating None/null as 0
1067 + # Then by severity (Critical=0, High=1, Medium=2, Low=3) for tie-breaking
1068 + severity_order = {"Critical": 0, "High": 1, "Medium": 2, "Low": 3}
1069 +
1070 + def get_epss_sort_key(vuln):
1071 + # Convert EPSS score to float for sorting, handle string/None values
1072 + epss_score = vuln.epss_score
1073 + if epss_score is None:
1074 + epss_float = 0.0
1075 + else:
1076 + try:
1077 + epss_float = float(epss_score)
1078 + except (ValueError, TypeError):
1079 + epss_float = 0.0
1080 + return (
1081 + -epss_float, # Negative for descending order
1082 + severity_order.get(vuln.severity, 4), # Secondary sort by severity
1083 + vuln.cve_id, # Tertiary sort by CVE ID for consistency
1084 + )
1085 +
1086 + vulnerabilities.sort(key=get_epss_sort_key)
1087 + logger.info(f"Sorted {len(vulnerabilities)} vulnerabilities by EPSS score (highest to lowest)")
1088 +
1089 message = f"Found {len(vulnerabilities)} vulnerabilities on page {page} of {total_pages}"
1090 if filters_applied:
1091 message += f" with filters: {filters_applied}"
1092 + if include_epss:
1093 + message += " (sorted by EPSS score, highest to lowest)"
1094 + else:
1095 + message += " (sorted by detection date and severity)"
1096
1097 return VulnerabilitySearchResponse(
1098 vulnerabilities=vulnerabilities,
backend/app/integrations/copilot_action/routes/copilot_action.py
+88 -6
@@ -44,6 +44,23 @@ def get_license_key() -> str:
44 return license_key
45
46
47 +def calculate_pagination_info(total: int, limit: int, offset: int) -> dict:
48 + """Calculate pagination metadata for responses."""
49 + current_page = (offset // limit) + 1
50 + total_pages = (total + limit - 1) // limit # Ceiling division
51 + has_next = offset + limit < total
52 + has_prev = offset > 0
53 +
54 + return {
55 + "current_page": current_page,
56 + "total_pages": total_pages,
57 + "has_next": has_next,
58 + "has_prev": has_prev,
59 + "items_per_page": limit,
60 + "total_items": total,
61 + }
62 +
63 +
64 async def get_agents_by_hostnames(session: AsyncSession, hostnames: List[str]) -> List[Agents]:
65 """Retrieve multiple agents from database by hostnames."""
66 agent_details = await session.execute(select(Agents).filter(Agents.hostname.in_(hostnames)))
@@ -175,7 +192,7 @@ async def build_artifact_collection_body(agent: Agents, artifact_name: str, velo
192 @copilot_action_router.get(
193 "/inventory",
194 response_model=InventoryResponse,
178 - description="Get inventory of available active response scripts",
195 + description="Get paginated inventory of available active response scripts",
196 dependencies=[Security(AuthHandler().get_current_user, scopes=["admin"])],
197 )
198 async def get_inventory(
@@ -183,13 +200,33 @@ async def get_inventory(
200 category: Optional[str] = Query(None, description="Filter by category"),
201 tag: Optional[str] = Query(None, description="Filter by tag"),
202 q: Optional[str] = Query(None, description="Free-text search query"),
186 - limit: int = Query(100, ge=1, le=1000, description="Maximum number of results"),
187 - offset: int = Query(0, ge=0, description="Offset for pagination"),
203 + limit: int = Query(100, ge=1, le=1000, description="Maximum number of results per page"),
204 + offset: int = Query(0, ge=0, description="Number of items to skip (for pagination)"),
205 refresh: bool = Query(False, description="Force refresh cache"),
206 include: Optional[str] = Query(None, description="Comma-separated extra fields to include"),
207 ) -> InventoryResponse:
191 - """Retrieve inventory of available active response scripts."""
192 - logger.info(f"Fetching active response inventory with filters: tech={technology}, category={category}, tag={tag}, q={q}")
208 + """
209 + Retrieve paginated inventory of available active response scripts.
210 +
211 + This endpoint supports pagination through the `limit` and `offset` parameters:
212 + - `limit`: Controls how many items are returned per page (1-1000, default 100)
213 + - `offset`: Controls how many items to skip (for pagination, default 0)
214 +
215 + The response includes pagination metadata:
216 + - `total`: Total number of items available
217 + - `count`: Number of items in current response
218 + - `has_more`: Whether there are more items available
219 + - `next_offset`: Offset to use for the next page
220 + - `prev_offset`: Offset to use for the previous page
221 +
222 + Example for paginated requests:
223 + - Page 1: GET /inventory?limit=50&offset=0
224 + - Page 2: GET /inventory?limit=50&offset=50
225 + - Page 3: GET /inventory?limit=50&offset=100
226 + """
227 + logger.info(
228 + f"Fetching active response inventory with filters: tech={technology}, category={category}, tag={tag}, q={q}, limit={limit}, offset={offset}",
229 + )
230
231 license_key = get_license_key()
232
@@ -206,7 +243,7 @@ async def get_inventory(
243 include=include,
244 )
245
209 - logger.info(f"Successfully fetched inventory: {len(response.copilot_actions)} actions")
246 + logger.info(f"Successfully fetched inventory: {response.count} of {response.total} actions (offset: {response.offset})")
247 return response
248
249 except Exception as e:
@@ -214,6 +251,51 @@ async def get_inventory(
251 raise HTTPException(status_code=500, detail=f"Error fetching inventory: {str(e)}")
252
253
254 +@copilot_action_router.get(
255 + "/inventory/count",
256 + description="Get total count of available scripts for pagination calculations",
257 + dependencies=[Security(AuthHandler().get_current_user, scopes=["admin"])],
258 +)
259 +async def get_inventory_count(
260 + technology: Optional[Technology] = Query(None, description="Filter by technology type"),
261 + category: Optional[str] = Query(None, description="Filter by category"),
262 + tag: Optional[str] = Query(None, description="Filter by tag"),
263 + q: Optional[str] = Query(None, description="Free-text search query"),
264 +) -> dict:
265 + """
266 + Get the total count of items matching the filters without fetching the full data.
267 + Useful for pagination calculations on the frontend.
268 + """
269 + logger.info(f"Fetching inventory count with filters: tech={technology}, category={category}, tag={tag}, q={q}")
270 +
271 + license_key = get_license_key()
272 +
273 + try:
274 + # Fetch with minimal data (limit=1) just to get the total
275 + response = await CopilotActionService.get_inventory(
276 + license_key=license_key,
277 + technology=technology,
278 + category=category,
279 + tag=tag,
280 + q=q,
281 + limit=1, # Minimal fetch
282 + offset=0,
283 + refresh=False,
284 + include=None,
285 + )
286 +
287 + return {
288 + "total": response.total,
289 + "message": "Successfully retrieved inventory count",
290 + "success": True,
291 + **calculate_pagination_info(response.total or 0, 100, 0), # Default pagination info
292 + }
293 +
294 + except Exception as e:
295 + logger.error(f"Error fetching inventory count: {str(e)}")
296 + raise HTTPException(status_code=500, detail=f"Error fetching inventory count: {str(e)}")
297 +
298 +
299 @copilot_action_router.get(
300 "/inventory/{copilot_action_name}",
301 response_model=ActionDetailResponse,
backend/app/integrations/copilot_action/schema/copilot_action.py
+9
@@ -84,6 +84,15 @@ class InventoryResponse(BaseModel):
84 message: str
85 success: bool
86
87 + # Pagination metadata
88 + total: Optional[int] = Field(None, description="Total number of items available")
89 + count: Optional[int] = Field(None, description="Number of items in current response")
90 + limit: Optional[int] = Field(None, description="Maximum items per page")
91 + offset: Optional[int] = Field(None, description="Current offset")
92 + has_more: Optional[bool] = Field(None, description="Whether there are more items available")
93 + next_offset: Optional[int] = Field(None, description="Offset for next page")
94 + prev_offset: Optional[int] = Field(None, description="Offset for previous page")
95 +
96
97 class ActionDetailResponse(BaseModel):
98 """Response model for single action details"""
backend/app/integrations/copilot_action/services/copilot_action.py
+68 -3
@@ -80,19 +80,84 @@ class CopilotActionService:
80
81 try:
82 data = response.json()
83 + logger.debug(f"Raw API response: {data}")
84 except ValueError:
85 logger.error(f"Non-JSON response from inventory API: {response.text[:200]}")
86 return InventoryResponse(copilot_actions=[], message="Invalid response format from inventory API", success=False)
87
88 logger.info(f"Successfully fetched inventory: {len(data.get('copilot_actions', []))} actions")
88 - return InventoryResponse(**data)
89 +
90 + # Calculate pagination metadata
91 + copilot_actions = data.get("copilot_actions", [])
92 + count = len(copilot_actions)
93 +
94 + # Try to get total from API response, with fallback parsing from message
95 + total = data.get("total")
96 + if total is None:
97 + # Try to parse total from message like "Returned 1 of 44 matching items"
98 + message = data.get("message", "")
99 + import re
100 +
101 + # Try multiple patterns to be more robust
102 + patterns = [
103 + r"(\d+) of (\d+) matching items",
104 + r"Returned (\d+) of (\d+)",
105 + r"(\d+)/(\d+) items",
106 + r"showing (\d+) of (\d+)",
107 + ]
108 +
109 + for pattern in patterns:
110 + match = re.search(pattern, message, re.IGNORECASE)
111 + if match:
112 + total = int(match.group(2))
113 + logger.info(f"Parsed total from message using pattern '{pattern}': {total}")
114 + break
115 + else:
116 + # Fallback to count if we can't parse
117 + total = count
118 + logger.warning(f"Could not determine total count from message '{message}', using current count: {count}")
119 +
120 + has_more = (offset + count) < total
121 + next_offset = offset + limit if has_more else None
122 + prev_offset = max(0, offset - limit) if offset > 0 else None
123 +
124 + return InventoryResponse(
125 + copilot_actions=copilot_actions,
126 + message=data.get("message", "Successfully fetched inventory"),
127 + success=data.get("success", True),
128 + total=total,
129 + count=count,
130 + limit=limit,
131 + offset=offset,
132 + has_more=has_more,
133 + next_offset=next_offset,
134 + prev_offset=prev_offset,
135 + )
136
137 except httpx.HTTPError as e:
138 logger.error(f"HTTP error fetching inventory: {str(e)}")
92 - return InventoryResponse(copilot_actions=[], message=f"HTTP error fetching inventory: {str(e)}", success=False)
139 + return InventoryResponse(
140 + copilot_actions=[],
141 + message=f"HTTP error fetching inventory: {str(e)}",
142 + success=False,
143 + total=0,
144 + count=0,
145 + limit=limit,
146 + offset=offset,
147 + has_more=False,
148 + )
149 except Exception as e:
150 logger.error(f"Unexpected error fetching inventory: {str(e)}")
95 - return InventoryResponse(copilot_actions=[], message=f"Unexpected error: {str(e)}", success=False)
151 + return InventoryResponse(
152 + copilot_actions=[],
153 + message=f"Unexpected error: {str(e)}",
154 + success=False,
155 + total=0,
156 + count=0,
157 + limit=limit,
158 + offset=offset,
159 + has_more=False,
160 + )
161
162 @classmethod
163 async def get_action_by_name(cls, license_key: str, copilot_action_name: str) -> ActionDetailResponse:
frontend/package.json
+35 -35
@@ -3,7 +3,7 @@
3 "type": "module",
4 "version": "1.0.0",
5 "private": true,
6 - "packageManager": "pnpm@10.13.1",
6 + "packageManager": "pnpm@10.17.0+sha512.fce8a3dd29a4ed2ec566fb53efbb04d8c44a0f05bc6f24a73046910fb9c3ce7afa35a0980500668fa3573345bd644644fa98338fa168235c80f4aa17aa17fbef",
7 "engines": {
8 "node": ">=18.0.0"
9 },
@@ -42,44 +42,44 @@
42 "@codemirror/lang-xml": "^6.1.0",
43 "@codemirror/lint": "^6.8.5",
44 "@codemirror/theme-one-dark": "^6.1.3",
45 - "@codemirror/view": "^6.38.1",
45 + "@codemirror/view": "^6.38.2",
46 "@f3ve/vue-markdown-it": "^0.2.3",
47 - "@fontsource/jetbrains-mono": "^5.2.6",
48 - "@fontsource/lexend": "^5.2.9",
49 - "@fontsource/public-sans": "^5.2.6",
50 - "@shikijs/markdown-it": "^3.8.1",
47 + "@fontsource/jetbrains-mono": "^5.2.8",
48 + "@fontsource/lexend": "^5.2.11",
49 + "@fontsource/public-sans": "^5.2.7",
50 + "@shikijs/markdown-it": "^3.12.2",
51 "@singulio/app-auth-search": "^0.0.3",
52 "@types/codemirror": "^5.60.16",
53 - "@vueuse/core": "^13.6.0",
53 + "@vueuse/core": "^13.9.0",
54 "@vueuse/motion": "^3.0.3",
55 - "axios": "^1.11.0",
55 + "axios": "^1.12.2",
56 "bytes": "^3.1.2",
57 "codemirror": "~6.0.2",
58 "colord": "^2.9.3",
59 - "dayjs": "^1.11.13",
59 + "dayjs": "^1.11.18",
60 "detect-touch-device": "^1.1.6",
61 "echarts": "^6.0.0",
62 "fast-xml-parser": "^5.2.5",
63 "file-saver": "^2.0.5",
64 "html-entities": "^2.6.0",
65 - "jose": "^6.0.12",
65 + "jose": "^6.1.0",
66 "js-md5": "^0.8.3",
67 "lodash": "^4.17.21",
68 "mitt": "^3.0.1",
69 - "naive-ui": "^2.42.0",
69 + "naive-ui": "^2.43.1",
70 "nanoid": "^5.1.5",
71 "password-validator": "^5.3.0",
72 "pinia": "^3.0.3",
73 - "pinia-plugin-persistedstate": "^4.4.1",
73 + "pinia-plugin-persistedstate": "^4.5.0",
74 "secure-ls": "^2.0.0",
75 - "shiki": "^3.8.1",
75 + "shiki": "^3.12.2",
76 "thememirror": "^2.0.1",
77 "validator": "^13.15.15",
78 - "vue": "^3.5.18",
78 + "vue": "^3.5.21",
79 "vue-advanced-cropper": "^2.8.9",
80 "vue-codemirror": "^6.1.1",
81 "vue-highlight-words": "^3.0.1",
82 - "vue-i18n": "^11.1.11",
82 + "vue-i18n": "^11.1.12",
83 "vue-router": "^4.5.1",
84 "vue-sjv": "^0.0.6",
85 "vue3-apexcharts": "^1.8.0",
@@ -94,10 +94,10 @@
94 "vueuc": "^0.4.64"
95 },
96 "devDependencies": {
97 - "@antfu/eslint-config": "^5.0.0",
97 + "@antfu/eslint-config": "~5.3.0",
98 "@clack/prompts": "^0.11.0",
99 "@iconify/vue": "^5.0.0",
100 - "@tailwindcss/vite": "^4.1.11",
100 + "@tailwindcss/vite": "^4.1.13",
101 "@tsconfig/node20": "^20.1.6",
102 "@types/bytes": "^3.1.5",
103 "@types/file-saver": "^2.0.7",
@@ -105,34 +105,34 @@
105 "@types/jsdom": "^21.1.7",
106 "@types/lodash": "^4.17.20",
107 "@types/markdown-it": "^14.1.2",
108 - "@types/node": "^24.1.0",
109 - "@types/validator": "^13.15.2",
108 + "@types/node": "^24.5.2",
109 + "@types/validator": "^13.15.3",
110 "@vitejs/plugin-vue": "^6.0.1",
111 - "@vitejs/plugin-vue-jsx": "^5.0.1",
111 + "@vitejs/plugin-vue-jsx": "^5.1.1",
112 "@vue/test-utils": "^2.4.6",
113 - "@vue/tsconfig": "^0.7.0",
114 - "cypress": "^14.5.3",
113 + "@vue/tsconfig": "~0.7.0",
114 + "cypress": "^15.2.0",
115 "depcheck": "^1.4.7",
116 - "eslint": "^9.32.0",
116 + "eslint": "~9.35.0",
117 "flourite": "^1.3.0",
118 - "fs-extra": "^11.3.0",
119 - "jsdom": "^26.1.0",
118 + "fs-extra": "^11.3.2",
119 + "jsdom": "^27.0.0",
120 "npm-run-all2": "^8.0.4",
121 "prettier": "^3.6.2",
122 "prettier-plugin-tailwindcss": "^0.6.14",
123 - "sass": "^1.89.2",
124 - "start-server-and-test": "^2.0.12",
125 - "tailwindcss": "^4.1.11",
126 - "taze": "^19.1.0",
127 - "type-fest": "^4.41.0",
128 - "typescript": "~5.8.3",
129 - "vite": "^7.0.6",
123 + "sass": "^1.92.1",
124 + "start-server-and-test": "^2.1.1",
125 + "tailwindcss": "^4.1.13",
126 + "taze": "^19.6.0",
127 + "type-fest": "^5.0.0",
128 + "typescript": "~5.9.2",
129 + "vite": "^7.1.6",
130 "vite-bundle-visualizer": "^1.2.1",
131 - "vite-plugin-inspect": "^11.3.2",
132 - "vite-plugin-vue-devtools": "^8.0.0",
131 + "vite-plugin-inspect": "^11.3.3",
132 + "vite-plugin-vue-devtools": "^8.0.2",
133 "vite-svg-loader": "^5.1.0",
134 "vitest": "^3.2.4",
135 - "vue-tsc": "^3.0.4"
135 + "vue-tsc": "^3.0.7"
136 },
137 "pnpm": {
138 "onlyBuiltDependencies": [
frontend/pnpm-lock.yaml
+1695 -1588
@@ -27,23 +27,23 @@ importers:
27 specifier: ^6.1.3
28 version: 6.1.3
29 '@codemirror/view':
30 - specifier: ^6.38.1
31 - version: 6.38.1
30 + specifier: ^6.38.2
31 + version: 6.38.2
32 '@f3ve/vue-markdown-it':
33 specifier: ^0.2.3
34 - version: 0.2.3(vue@3.5.18(typescript@5.8.3))
34 + version: 0.2.3(vue@3.5.21(typescript@5.9.2))
35 '@fontsource/jetbrains-mono':
36 - specifier: ^5.2.6
37 - version: 5.2.6
36 + specifier: ^5.2.8
37 + version: 5.2.8
38 '@fontsource/lexend':
39 - specifier: ^5.2.9
40 - version: 5.2.9
39 + specifier: ^5.2.11
40 + version: 5.2.11
41 '@fontsource/public-sans':
42 - specifier: ^5.2.6
43 - version: 5.2.6
42 + specifier: ^5.2.7
43 + version: 5.2.7
44 '@shikijs/markdown-it':
45 - specifier: ^3.8.1
46 - version: 3.8.1
45 + specifier: ^3.12.2
46 + version: 3.12.2
47 '@singulio/app-auth-search':
48 specifier: ^0.0.3
49 version: 0.0.3
@@ -51,14 +51,14 @@ importers:
51 specifier: ^5.60.16
52 version: 5.60.16
53 '@vueuse/core':
54 - specifier: ^13.6.0
55 - version: 13.6.0(vue@3.5.18(typescript@5.8.3))
54 + specifier: ^13.9.0
55 + version: 13.9.0(vue@3.5.21(typescript@5.9.2))
56 '@vueuse/motion':
57 specifier: ^3.0.3
58 - version: 3.0.3(vue@3.5.18(typescript@5.8.3))
58 + version: 3.0.3(vue@3.5.21(typescript@5.9.2))
59 axios:
60 - specifier: ^1.11.0
61 - version: 1.11.0(debug@4.4.1)
60 + specifier: ^1.12.2
61 + version: 1.12.2(debug@4.4.3)
62 bytes:
63 specifier: ^3.1.2
64 version: 3.1.2
@@ -69,8 +69,8 @@ importers:
69 specifier: ^2.9.3
70 version: 2.9.3
71 dayjs:
72 - specifier: ^1.11.13
73 - version: 1.11.13
72 + specifier: ^1.11.18
73 + version: 1.11.18
74 detect-touch-device:
75 specifier: ^1.1.6
76 version: 1.1.6
@@ -87,8 +87,8 @@ importers:
87 specifier: ^2.6.0
88 version: 2.6.0
89 jose:
90 - specifier: ^6.0.12
91 - version: 6.0.12
90 + specifier: ^6.1.0
91 + version: 6.1.0
92 js-md5:
93 specifier: ^0.8.3
94 version: 0.8.3
@@ -99,8 +99,8 @@ importers:
99 specifier: ^3.0.1
100 version: 3.0.1
101 naive-ui:
102 - specifier: ^2.42.0
103 - version: 2.42.0(vue@3.5.18(typescript@5.8.3))
102 + specifier: ^2.43.1
103 + version: 2.43.1(vue@3.5.21(typescript@5.9.2))
104 nanoid:
105 specifier: ^5.1.5
106 version: 5.1.5
@@ -109,71 +109,71 @@ importers:
109 version: 5.3.0
110 pinia:
111 specifier: ^3.0.3
112 - version: 3.0.3(typescript@5.8.3)(vue@3.5.18(typescript@5.8.3))
112 + version: 3.0.3(typescript@5.9.2)(vue@3.5.21(typescript@5.9.2))
113 pinia-plugin-persistedstate:
114 - specifier: ^4.4.1
115 - version: 4.4.1(@nuxt/kit@3.18.0)(pinia@3.0.3(typescript@5.8.3)(vue@3.5.18(typescript@5.8.3)))
114 + specifier: ^4.5.0
115 + version: 4.5.0(@nuxt/kit@3.19.2)(pinia@3.0.3(typescript@5.9.2)(vue@3.5.21(typescript@5.9.2)))
116 secure-ls:
117 specifier: ^2.0.0
118 version: 2.0.0
119 shiki:
120 - specifier: ^3.8.1
121 - version: 3.8.1
120 + specifier: ^3.12.2
121 + version: 3.12.2
122 thememirror:
123 specifier: ^2.0.1
124 - version: 2.0.1(@codemirror/language@6.11.2)(@codemirror/state@6.5.2)(@codemirror/view@6.38.1)
124 + version: 2.0.1(@codemirror/language@6.11.3)(@codemirror/state@6.5.2)(@codemirror/view@6.38.2)
125 validator:
126 specifier: ^13.15.15
127 version: 13.15.15
128 vue:
129 - specifier: ^3.5.18
130 - version: 3.5.18(typescript@5.8.3)
129 + specifier: ^3.5.21
130 + version: 3.5.21(typescript@5.9.2)
131 vue-advanced-cropper:
132 specifier: ^2.8.9
133 - version: 2.8.9(vue@3.5.18(typescript@5.8.3))
133 + version: 2.8.9(vue@3.5.21(typescript@5.9.2))
134 vue-codemirror:
135 specifier: ^6.1.1
136 - version: 6.1.1(codemirror@6.0.2)(vue@3.5.18(typescript@5.8.3))
136 + version: 6.1.1(codemirror@6.0.2)(vue@3.5.21(typescript@5.9.2))
137 vue-highlight-words:
138 specifier: ^3.0.1
139 - version: 3.0.1(vue@3.5.18(typescript@5.8.3))
139 + version: 3.0.1(vue@3.5.21(typescript@5.9.2))
140 vue-i18n:
141 - specifier: ^11.1.11
142 - version: 11.1.11(vue@3.5.18(typescript@5.8.3))
141 + specifier: ^11.1.12
142 + version: 11.1.12(vue@3.5.21(typescript@5.9.2))
143 vue-router:
144 specifier: ^4.5.1
145 - version: 4.5.1(vue@3.5.18(typescript@5.8.3))
145 + version: 4.5.1(vue@3.5.21(typescript@5.9.2))
146 vue-sjv:
147 specifier: ^0.0.6
148 - version: 0.0.6(vue@3.5.18(typescript@5.8.3))
148 + version: 0.0.6(vue@3.5.21(typescript@5.9.2))
149 vue3-apexcharts:
150 specifier: ^1.8.0
151 - version: 1.8.0(apexcharts@5.3.2)(vue@3.5.18(typescript@5.8.3))
151 + version: 1.8.0(apexcharts@5.3.5)(vue@3.5.21(typescript@5.9.2))
152 vue3-marquee:
153 specifier: ^4.2.2
154 - version: 4.2.2(vue@3.5.18(typescript@5.8.3))
154 + version: 4.2.2(vue@3.5.21(typescript@5.9.2))
155 vuedraggable:
156 specifier: ^4.1.0
157 - version: 4.1.0(vue@3.5.18(typescript@5.8.3))
157 + version: 4.1.0(vue@3.5.21(typescript@5.9.2))
158 xmllint:
159 specifier: ^0.1.1
160 version: 0.1.1
161 xmllint-wasm:
162 specifier: ^5.0.0
163 - version: 5.0.0(@types/node@24.1.0)
163 + version: 5.0.0(@types/node@24.5.2)
164 devDependencies:
165 '@antfu/eslint-config':
166 - specifier: ^5.0.0
167 - version: 5.0.0(@vue/compiler-sfc@3.5.18)(eslint@9.32.0(jiti@2.5.1))(typescript@5.8.3)(vitest@3.2.4(@types/debug@4.1.12)(@types/node@24.1.0)(jiti@2.5.1)(jsdom@26.1.0)(lightningcss@1.30.1)(sass@1.89.2)(yaml@2.8.0))
166 + specifier: ~5.3.0
167 + version: 5.3.0(@vue/compiler-sfc@3.5.21)(eslint@9.35.0(jiti@2.5.1))(typescript@5.9.2)(vitest@3.2.4(@types/debug@4.1.12)(@types/node@24.5.2)(jiti@2.5.1)(jsdom@27.0.0(postcss@8.5.6))(lightningcss@1.30.1)(sass@1.92.1)(yaml@2.8.1))
168 '@clack/prompts':
169 specifier: ^0.11.0
170 version: 0.11.0
171 '@iconify/vue':
172 specifier: ^5.0.0
173 - version: 5.0.0(vue@3.5.18(typescript@5.8.3))
173 + version: 5.0.0(vue@3.5.21(typescript@5.9.2))
174 '@tailwindcss/vite':
175 - specifier: ^4.1.11
176 - version: 4.1.11(vite@7.0.6(@types/node@24.1.0)(jiti@2.5.1)(lightningcss@1.30.1)(sass@1.89.2)(yaml@2.8.0))
175 + specifier: ^4.1.13
176 + version: 4.1.13(vite@7.1.6(@types/node@24.5.2)(jiti@2.5.1)(lightningcss@1.30.1)(sass@1.92.1)(yaml@2.8.1))
177 '@tsconfig/node20':
178 specifier: ^20.1.6
179 version: 20.1.6
@@ -196,41 +196,41 @@ importers:
196 specifier: ^14.1.2
197 version: 14.1.2
198 '@types/node':
199 - specifier: ^24.1.0
200 - version: 24.1.0
199 + specifier: ^24.5.2
200 + version: 24.5.2
201 '@types/validator':
202 - specifier: ^13.15.2
203 - version: 13.15.2
202 + specifier: ^13.15.3
203 + version: 13.15.3
204 '@vitejs/plugin-vue':
205 specifier: ^6.0.1
206 - version: 6.0.1(vite@7.0.6(@types/node@24.1.0)(jiti@2.5.1)(lightningcss@1.30.1)(sass@1.89.2)(yaml@2.8.0))(vue@3.5.18(typescript@5.8.3))
206 + version: 6.0.1(vite@7.1.6(@types/node@24.5.2)(jiti@2.5.1)(lightningcss@1.30.1)(sass@1.92.1)(yaml@2.8.1))(vue@3.5.21(typescript@5.9.2))
207 '@vitejs/plugin-vue-jsx':
208 - specifier: ^5.0.1
209 - version: 5.0.1(vite@7.0.6(@types/node@24.1.0)(jiti@2.5.1)(lightningcss@1.30.1)(sass@1.89.2)(yaml@2.8.0))(vue@3.5.18(typescript@5.8.3))
208 + specifier: ^5.1.1
209 + version: 5.1.1(vite@7.1.6(@types/node@24.5.2)(jiti@2.5.1)(lightningcss@1.30.1)(sass@1.92.1)(yaml@2.8.1))(vue@3.5.21(typescript@5.9.2))
210 '@vue/test-utils':
211 specifier: ^2.4.6
212 version: 2.4.6
213 '@vue/tsconfig':
214 - specifier: ^0.7.0
215 - version: 0.7.0(typescript@5.8.3)(vue@3.5.18(typescript@5.8.3))
214 + specifier: ~0.7.0
215 + version: 0.7.0(typescript@5.9.2)(vue@3.5.21(typescript@5.9.2))
216 cypress:
217 - specifier: ^14.5.3
218 - version: 14.5.3
217 + specifier: ^15.2.0
218 + version: 15.2.0
219 depcheck:
220 specifier: ^1.4.7
221 version: 1.4.7
222 eslint:
223 - specifier: ^9.32.0
224 - version: 9.32.0(jiti@2.5.1)
223 + specifier: ~9.35.0
224 + version: 9.35.0(jiti@2.5.1)
225 flourite:
226 specifier: ^1.3.0
227 version: 1.3.0
228 fs-extra:
229 - specifier: ^11.3.0
230 - version: 11.3.0
229 + specifier: ^11.3.2
230 + version: 11.3.2
231 jsdom:
232 - specifier: ^26.1.0
233 - version: 26.1.0
232 + specifier: ^27.0.0
233 + version: 27.0.0(postcss@8.5.6)
234 npm-run-all2:
235 specifier: ^8.0.4
236 version: 8.0.4
@@ -241,122 +241,118 @@ importers:
241 specifier: ^0.6.14
242 version: 0.6.14(prettier@3.6.2)
243 sass:
244 - specifier: ^1.89.2
245 - version: 1.89.2
244 + specifier: ^1.92.1
245 + version: 1.92.1
246 start-server-and-test:
247 - specifier: ^2.0.12
248 - version: 2.0.12
247 + specifier: ^2.1.1
248 + version: 2.1.1
249 tailwindcss:
250 - specifier: ^4.1.11
251 - version: 4.1.11
250 + specifier: ^4.1.13
251 + version: 4.1.13
252 taze:
253 - specifier: ^19.1.0
254 - version: 19.1.0
253 + specifier: ^19.6.0
254 + version: 19.6.0
255 type-fest:
256 - specifier: ^4.41.0
257 - version: 4.41.0
256 + specifier: ^5.0.0
257 + version: 5.0.0
258 typescript:
259 - specifier: ~5.8.3
260 - version: 5.8.3
259 + specifier: ~5.9.2
260 + version: 5.9.2
261 vite:
262 - specifier: ^7.0.6
263 - version: 7.0.6(@types/node@24.1.0)(jiti@2.5.1)(lightningcss@1.30.1)(sass@1.89.2)(yaml@2.8.0)
262 + specifier: ^7.1.6
263 + version: 7.1.6(@types/node@24.5.2)(jiti@2.5.1)(lightningcss@1.30.1)(sass@1.92.1)(yaml@2.8.1)
264 vite-bundle-visualizer:
265 specifier: ^1.2.1
266 - version: 1.2.1(rollup@4.46.2)
266 + version: 1.2.1(rollup@4.50.2)
267 vite-plugin-inspect:
268 - specifier: ^11.3.2
269 - version: 11.3.2(@nuxt/kit@3.18.0)(vite@7.0.6(@types/node@24.1.0)(jiti@2.5.1)(lightningcss@1.30.1)(sass@1.89.2)(yaml@2.8.0))
268 + specifier: ^11.3.3
269 + version: 11.3.3(@nuxt/kit@3.19.2)(vite@7.1.6(@types/node@24.5.2)(jiti@2.5.1)(lightningcss@1.30.1)(sass@1.92.1)(yaml@2.8.1))
270 vite-plugin-vue-devtools:
271 - specifier: ^8.0.0
272 - version: 8.0.0(@nuxt/kit@3.18.0)(vite@7.0.6(@types/node@24.1.0)(jiti@2.5.1)(lightningcss@1.30.1)(sass@1.89.2)(yaml@2.8.0))(vue@3.5.18(typescript@5.8.3))
271 + specifier: ^8.0.2
272 + version: 8.0.2(@nuxt/kit@3.19.2)(vite@7.1.6(@types/node@24.5.2)(jiti@2.5.1)(lightningcss@1.30.1)(sass@1.92.1)(yaml@2.8.1))(vue@3.5.21(typescript@5.9.2))
273 vite-svg-loader:
274 specifier: ^5.1.0
275 - version: 5.1.0(vue@3.5.18(typescript@5.8.3))
275 + version: 5.1.0(vue@3.5.21(typescript@5.9.2))
276 vitest:
277 specifier: ^3.2.4
278 - version: 3.2.4(@types/debug@4.1.12)(@types/node@24.1.0)(jiti@2.5.1)(jsdom@26.1.0)(lightningcss@1.30.1)(sass@1.89.2)(yaml@2.8.0)
278 + version: 3.2.4(@types/debug@4.1.12)(@types/node@24.5.2)(jiti@2.5.1)(jsdom@27.0.0(postcss@8.5.6))(lightningcss@1.30.1)(sass@1.92.1)(yaml@2.8.1)
279 vue-tsc:
280 - specifier: ^3.0.4
281 - version: 3.0.4(typescript@5.8.3)
280 + specifier: ^3.0.7
281 + version: 3.0.7(typescript@5.9.2)
282 optionalDependencies:
283 '@rollup/rollup-linux-x64-gnu':
284 specifier: ^4.46.2
285 - version: 4.46.2
285 + version: 4.50.2
286 treemate:
287 specifier: ^0.3.11
288 version: 0.3.11
289 vueuc:
290 specifier: ^0.4.64
291 - version: 0.4.64(vue@3.5.18(typescript@5.8.3))
291 + version: 0.4.65(vue@3.5.21(typescript@5.9.2))
292
293 packages:
294
295 '@ajoelp/json-to-formdata@1.5.0':
296 resolution: {integrity: sha512-nrlfeTSL0X0dtx5r2KpzPiqLSIQquiiJjUKsQAKzWaCmO2QoYZCyb5ENZwF3YoffKronOCJr25mxaD8JRJmK8w==}
297
298 - '@algolia/abtesting@1.1.0':
299 - resolution: {integrity: sha512-sEyWjw28a/9iluA37KLGu8vjxEIlb60uxznfTUmXImy7H5NvbpSO6yYgmgH5KiD7j+zTUUihiST0jEP12IoXow==}
298 + '@algolia/abtesting@1.3.0':
299 + resolution: {integrity: sha512-KqPVLdVNfoJzX5BKNGM9bsW8saHeyax8kmPFXul5gejrSPN3qss7PgsFH5mMem7oR8tvjvNkia97ljEYPYCN8Q==}
300 engines: {node: '>= 14.0.0'}
301
302 - '@algolia/client-abtesting@5.35.0':
303 - resolution: {integrity: sha512-uUdHxbfHdoppDVflCHMxRlj49/IllPwwQ2cQ8DLC4LXr3kY96AHBpW0dMyi6ygkn2MtFCc6BxXCzr668ZRhLBQ==}
302 + '@algolia/client-abtesting@5.37.0':
303 + resolution: {integrity: sha512-Dp2Zq+x9qQFnuiQhVe91EeaaPxWBhzwQ6QnznZQnH9C1/ei3dvtmAFfFeaTxM6FzfJXDLvVnaQagTYFTQz3R5g==}
304 engines: {node: '>= 14.0.0'}
305
306 - '@algolia/client-analytics@5.35.0':
307 - resolution: {integrity: sha512-SunAgwa9CamLcRCPnPHx1V2uxdQwJGqb1crYrRWktWUdld0+B2KyakNEeVn5lln4VyeNtW17Ia7V7qBWyM/Skw==}
306 + '@algolia/client-analytics@5.37.0':
307 + resolution: {integrity: sha512-wyXODDOluKogTuZxRII6mtqhAq4+qUR3zIUJEKTiHLe8HMZFxfUEI4NO2qSu04noXZHbv/sRVdQQqzKh12SZuQ==}
308 engines: {node: '>= 14.0.0'}
309
310 - '@algolia/client-common@5.35.0':
311 - resolution: {integrity: sha512-ipE0IuvHu/bg7TjT2s+187kz/E3h5ssfTtjpg1LbWMgxlgiaZIgTTbyynM7NfpSJSKsgQvCQxWjGUO51WSCu7w==}
310 + '@algolia/client-common@5.37.0':
311 + resolution: {integrity: sha512-GylIFlPvLy9OMgFG8JkonIagv3zF+Dx3H401Uo2KpmfMVBBJiGfAb9oYfXtplpRMZnZPxF5FnkWaI/NpVJMC+g==}
312 engines: {node: '>= 14.0.0'}
313
314 - '@algolia/client-insights@5.35.0':
315 - resolution: {integrity: sha512-UNbCXcBpqtzUucxExwTSfAe8gknAJ485NfPN6o1ziHm6nnxx97piIbcBQ3edw823Tej2Wxu1C0xBY06KgeZ7gA==}
314 + '@algolia/client-insights@5.37.0':
315 + resolution: {integrity: sha512-T63afO2O69XHKw2+F7mfRoIbmXWGzgpZxgOFAdP3fR4laid7pWBt20P4eJ+Zn23wXS5kC9P2K7Bo3+rVjqnYiw==}
316 engines: {node: '>= 14.0.0'}
317
318 - '@algolia/client-personalization@5.35.0':
319 - resolution: {integrity: sha512-/KWjttZ6UCStt4QnWoDAJ12cKlQ+fkpMtyPmBgSS2WThJQdSV/4UWcqCUqGH7YLbwlj3JjNirCu3Y7uRTClxvA==}
318 + '@algolia/client-personalization@5.37.0':
319 + resolution: {integrity: sha512-1zOIXM98O9zD8bYDCJiUJRC/qNUydGHK/zRK+WbLXrW1SqLFRXECsKZa5KoG166+o5q5upk96qguOtE8FTXDWQ==}
320 engines: {node: '>= 14.0.0'}
321
322 - '@algolia/client-query-suggestions@5.35.0':
323 - resolution: {integrity: sha512-8oCuJCFf/71IYyvQQC+iu4kgViTODbXDk3m7yMctEncRSRV+u2RtDVlpGGfPlJQOrAY7OONwJlSHkmbbm2Kp/w==}
322 + '@algolia/client-query-suggestions@5.37.0':
323 + resolution: {integrity: sha512-31Nr2xOLBCYVal+OMZn1rp1H4lPs1914Tfr3a34wU/nsWJ+TB3vWjfkUUuuYhWoWBEArwuRzt3YNLn0F/KRVkg==}
324 engines: {node: '>= 14.0.0'}
325
326 - '@algolia/client-search@5.35.0':
327 - resolution: {integrity: sha512-FfmdHTrXhIduWyyuko1YTcGLuicVbhUyRjO3HbXE4aP655yKZgdTIfMhZ/V5VY9bHuxv/fGEh3Od1Lvv2ODNTg==}
326 + '@algolia/client-search@5.37.0':
327 + resolution: {integrity: sha512-DAFVUvEg+u7jUs6BZiVz9zdaUebYULPiQ4LM2R4n8Nujzyj7BZzGr2DCd85ip4p/cx7nAZWKM8pLcGtkTRTdsg==}
328 engines: {node: '>= 14.0.0'}
329
330 - '@algolia/ingestion@1.35.0':
331 - resolution: {integrity: sha512-gPzACem9IL1Co8mM1LKMhzn1aSJmp+Vp434An4C0OBY4uEJRcqsLN3uLBlY+bYvFg8C8ImwM9YRiKczJXRk0XA==}
330 + '@algolia/ingestion@1.37.0':
331 + resolution: {integrity: sha512-pkCepBRRdcdd7dTLbFddnu886NyyxmhgqiRcHHaDunvX03Ij4WzvouWrQq7B7iYBjkMQrLS8wQqSP0REfA4W8g==}
332 engines: {node: '>= 14.0.0'}
333
334 - '@algolia/monitoring@1.35.0':
335 - resolution: {integrity: sha512-w9MGFLB6ashI8BGcQoVt7iLgDIJNCn4OIu0Q0giE3M2ItNrssvb8C0xuwJQyTy1OFZnemG0EB1OvXhIHOvQwWw==}
334 + '@algolia/monitoring@1.37.0':
335 + resolution: {integrity: sha512-fNw7pVdyZAAQQCJf1cc/ih4fwrRdQSgKwgor4gchsI/Q/ss9inmC6bl/69jvoRSzgZS9BX4elwHKdo0EfTli3w==}
336 engines: {node: '>= 14.0.0'}
337
338 - '@algolia/recommend@5.35.0':
339 - resolution: {integrity: sha512-AhrVgaaXAb8Ue0u2nuRWwugt0dL5UmRgS9LXe0Hhz493a8KFeZVUE56RGIV3hAa6tHzmAV7eIoqcWTQvxzlJeQ==}
338 + '@algolia/recommend@5.37.0':
339 + resolution: {integrity: sha512-U+FL5gzN2ldx3TYfQO5OAta2TBuIdabEdFwD5UVfWPsZE5nvOKkc/6BBqP54Z/adW/34c5ZrvvZhlhNTZujJXQ==}
340 engines: {node: '>= 14.0.0'}
341
342 - '@algolia/requester-browser-xhr@5.35.0':
343 - resolution: {integrity: sha512-diY415KLJZ6x1Kbwl9u96Jsz0OstE3asjXtJ9pmk1d+5gPuQ5jQyEsgC+WmEXzlec3iuVszm8AzNYYaqw6B+Zw==}
342 + '@algolia/requester-browser-xhr@5.37.0':
343 + resolution: {integrity: sha512-Ao8GZo8WgWFABrU7iq+JAftXV0t+UcOtCDL4mzHHZ+rQeTTf1TZssr4d0vIuoqkVNnKt9iyZ7T4lQff4ydcTrw==}
344 engines: {node: '>= 14.0.0'}
345
346 - '@algolia/requester-fetch@5.35.0':
347 - resolution: {integrity: sha512-uydqnSmpAjrgo8bqhE9N1wgcB98psTRRQXcjc4izwMB7yRl9C8uuAQ/5YqRj04U0mMQ+fdu2fcNF6m9+Z1BzDQ==}
346 + '@algolia/requester-fetch@5.37.0':
347 + resolution: {integrity: sha512-H7OJOXrFg5dLcGJ22uxx8eiFId0aB9b0UBhoOi4SMSuDBe6vjJJ/LeZyY25zPaSvkXNBN3vAM+ad6M0h6ha3AA==}
348 engines: {node: '>= 14.0.0'}
349
350 - '@algolia/requester-node-http@5.35.0':
351 - resolution: {integrity: sha512-RgLX78ojYOrThJHrIiPzT4HW3yfQa0D7K+MQ81rhxqaNyNBu4F1r+72LNHYH/Z+y9I1Mrjrd/c/Ue5zfDgAEjQ==}
350 + '@algolia/requester-node-http@5.37.0':
351 + resolution: {integrity: sha512-npZ9aeag4SGTx677eqPL3rkSPlQrnzx/8wNrl1P7GpWq9w/eTmRbOq+wKrJ2r78idlY0MMgmY/mld2tq6dc44g==}
352 engines: {node: '>= 14.0.0'}
353
354 - '@ampproject/remapping@2.3.0':
355 - resolution: {integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==}
356 - engines: {node: '>=6.0.0'}
357 -
358 - '@antfu/eslint-config@5.0.0':
359 - resolution: {integrity: sha512-uAMv8PiW9BOAGmIyTDtWXGnNfv6PFV4DmpqmlUpST5k4bue38VRdIfnM4jvgPuny1xnjYX3flN3kB9++6LknMw==}
354 + '@antfu/eslint-config@5.3.0':
355 + resolution: {integrity: sha512-VzBemSi453rd06lF6gG6VkpP3HH7XKTf+sK6frSrGm7uMFkN57jry1XB074tQRKB3qOjhpsx3kKpWtOv9e5FnQ==}
356 hasBin: true
357 peerDependencies:
358 '@eslint-react/eslint-plugin': ^1.38.4
@@ -367,6 +363,7 @@ packages:
363 eslint: ^9.10.0
364 eslint-plugin-astro: ^1.2.0
365 eslint-plugin-format: '>=0.1.0'
366 + eslint-plugin-jsx-a11y: '>=6.10.2'
367 eslint-plugin-react-hooks: ^5.2.0
368 eslint-plugin-react-refresh: ^0.4.19
369 eslint-plugin-solid: ^0.14.3
@@ -390,6 +387,8 @@ packages:
387 optional: true
388 eslint-plugin-format:
389 optional: true
390 + eslint-plugin-jsx-a11y:
391 + optional: true
392 eslint-plugin-react-hooks:
393 optional: true
394 eslint-plugin-react-refresh:
@@ -410,27 +409,33 @@ packages:
409 '@antfu/install-pkg@1.1.0':
410 resolution: {integrity: sha512-MGQsmw10ZyI+EJo45CdSER4zEb+p31LpDAFp2Z3gkSd1yqVZGi0Ebx++YTEMonJy4oChEMLsxZ64j8FH6sSqtQ==}
411
413 - '@antfu/ni@24.4.0':
414 - resolution: {integrity: sha512-ZjriRbGyWGSrBE1RY2qBIXyilejMWLDWh2Go2dqFottyiuOze36+BpPch2z2WnGEgEbzTBVPetMmQvt0xt+iww==}
412 + '@antfu/ni@25.0.0':
413 + resolution: {integrity: sha512-9q/yCljni37pkMr4sPrI3G4jqdIk074+iukc5aFJl7kmDCCsiJrbZ6zKxnES1Gwg+i9RcDZwvktl23puGslmvA==}
414 hasBin: true
415
417 - '@asamuzakjp/css-color@3.2.0':
418 - resolution: {integrity: sha512-K1A6z8tS3XsmCMM86xoWdn7Fkdn9m6RSVtocUrJYIwZnFVkng/PvkEoWtOWmP+Scc6saYWHWZYbndEEXxl24jw==}
416 + '@asamuzakjp/css-color@4.0.4':
417 + resolution: {integrity: sha512-cKjSKvWGmAziQWbCouOsFwb14mp1betm8Y7Fn+yglDMUUu3r9DCbJ9iJbeFDenLMqFbIMC0pQP8K+B8LAxX3OQ==}
418 +
419 + '@asamuzakjp/dom-selector@6.5.5':
420 + resolution: {integrity: sha512-kI2MX9pmImjxWT8nxDZY+MuN6r1jJGe7WxizEbsAEPB/zxfW5wYLIiPG1v3UKgEOOP8EsDkp0ZL99oRFAdPM8g==}
421 +
422 + '@asamuzakjp/nwsapi@2.3.9':
423 + resolution: {integrity: sha512-n8GuYSrI9bF7FFZ/SjhwevlHc8xaVlb/7HmHelnc/PZXBD2ZR49NnN9sMMuDdEGPeeRQ5d0hqlSlEpgCX3Wl0Q==}
424
425 '@babel/code-frame@7.27.1':
426 resolution: {integrity: sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==}
427 engines: {node: '>=6.9.0'}
428
424 - '@babel/compat-data@7.28.0':
425 - resolution: {integrity: sha512-60X7qkglvrap8mn1lh2ebxXdZYtUcpd7gsmy9kLaBJ4i/WdY8PqTSdxyA8qraikqKQK5C1KRBKXqznrVapyNaw==}
429 + '@babel/compat-data@7.28.4':
430 + resolution: {integrity: sha512-YsmSKC29MJwf0gF8Rjjrg5LQCmyh+j/nD8/eP7f+BeoQTKYqs9RoWbjGOdy0+1Ekr68RJZMUOPVQaQisnIo4Rw==}
431 engines: {node: '>=6.9.0'}
432
428 - '@babel/core@7.28.0':
429 - resolution: {integrity: sha512-UlLAnTPrFdNGoFtbSXwcGFQBtQZJCNjaN6hQNP3UPvuNXT1i82N26KL3dZeIpNalWywr9IuQuncaAfUaS1g6sQ==}
433 + '@babel/core@7.28.4':
434 + resolution: {integrity: sha512-2BCOP7TN8M+gVDj7/ht3hsaO/B/n5oDbiAyyvnRlNOs+u1o+JWNYTQrmpuNp1/Wq2gcFrI01JAW+paEKDMx/CA==}
435 engines: {node: '>=6.9.0'}
436
432 - '@babel/generator@7.28.0':
433 - resolution: {integrity: sha512-lJjzvrbEeWrhB4P3QBsH7tey117PjLZnDbLiQEKjQ/fNJTjuq4HSqgFA+UNSwZT8D7dxxbnuSBMsa1lrWzKlQg==}
437 + '@babel/generator@7.28.3':
438 + resolution: {integrity: sha512-3lSpxGgvnmZznmBkCRnVREPUFJv2wrv9iAoFDvADJc0ypmdOxdUtcLeBgBJ6zE0PMeTKnxeQzyk0xTBq4Ep7zw==}
439 engines: {node: '>=6.9.0'}
440
441 '@babel/helper-annotate-as-pure@7.27.3':
@@ -441,8 +446,8 @@ packages:
446 resolution: {integrity: sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==}
447 engines: {node: '>=6.9.0'}
448
444 - '@babel/helper-create-class-features-plugin@7.27.1':
445 - resolution: {integrity: sha512-QwGAmuvM17btKU5VqXfb+Giw4JcN0hjuufz3DYnpeVDvZLAObloM77bhMXiqry3Iio+Ai4phVRDwl6WU10+r5A==}
449 + '@babel/helper-create-class-features-plugin@7.28.3':
450 + resolution: {integrity: sha512-V9f6ZFIYSLNEbuGA/92uOvYsGCJNsuA8ESZ4ldc09bWk/j8H8TKiPw8Mk1eG6olpnO0ALHJmYfZvF4MEE4gajg==}
451 engines: {node: '>=6.9.0'}
452 peerDependencies:
453 '@babel/core': ^7.0.0
@@ -459,8 +464,8 @@ packages:
464 resolution: {integrity: sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==}
465 engines: {node: '>=6.9.0'}
466
462 - '@babel/helper-module-transforms@7.27.3':
463 - resolution: {integrity: sha512-dSOvYwvyLsWBeIRyOeHXp5vPj5l1I011r52FM1+r1jCERv+aFXYk4whgQccYEGYxK2H3ZAIA8nuPkQ0HaUo3qg==}
467 + '@babel/helper-module-transforms@7.28.3':
468 + resolution: {integrity: sha512-gytXUbs8k2sXS9PnQptz5o0QnpLL51SwASIORY6XaBKF88nsOT0Zw9szLqlSGQDP/4TljBAD5y98p2U1fqkdsw==}
469 engines: {node: '>=6.9.0'}
470 peerDependencies:
471 '@babel/core': ^7.0.0
@@ -495,12 +500,12 @@ packages:
500 resolution: {integrity: sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==}
501 engines: {node: '>=6.9.0'}
502
498 - '@babel/helpers@7.28.2':
499 - resolution: {integrity: sha512-/V9771t+EgXz62aCcyofnQhGM8DQACbRhvzKFsXKC9QM+5MadF8ZmIm0crDMaz3+o0h0zXfJnd4EhbYbxsrcFw==}
503 + '@babel/helpers@7.28.4':
504 + resolution: {integrity: sha512-HFN59MmQXGHVyYadKLVumYsA9dBFun/ldYxipEjzA4196jpLZd8UjEEBLkbEkvfYreDqJhZxYAWFPtrfhNpj4w==}
505 engines: {node: '>=6.9.0'}
506
502 - '@babel/parser@7.28.0':
503 - resolution: {integrity: sha512-jVZGvOxOuNSsuQuLRTh13nU0AogFlw32w/MT+LV6D3sP5WdbW61E77RnkbaO2dUvmPAYrBDJXGn5gGS6tH4j8g==}
507 + '@babel/parser@7.28.4':
508 + resolution: {integrity: sha512-yZbBqeM6TkpP9du/I2pUZnJsRMGGvOuIrhjzC1AwHwW+6he4mni6Bp/m8ijn0iOuZuPI2BfkCoSRunpyjnrQKg==}
509 engines: {node: '>=6.0.0'}
510 hasBin: true
511
@@ -549,12 +554,12 @@ packages:
554 resolution: {integrity: sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==}
555 engines: {node: '>=6.9.0'}
556
552 - '@babel/traverse@7.28.0':
553 - resolution: {integrity: sha512-mGe7UK5wWyh0bKRfupsUchrQGqvDbZDbKJw+kcRGSmdHVYrv+ltd0pnpDTVpiTqnaBru9iEvA8pz8W46v0Amwg==}
557 + '@babel/traverse@7.28.4':
558 + resolution: {integrity: sha512-YEzuboP2qvQavAcjgQNVgsvHIDv6ZpwXvcvjmyySP2DIMuByS/6ioU5G9pYrWHM6T2YDfc7xga9iNzYOs12CFQ==}
559 engines: {node: '>=6.9.0'}
560
556 - '@babel/types@7.28.2':
557 - resolution: {integrity: sha512-ruv7Ae4J5dUYULmeXw1gmb7rYRz57OWCPM57pHojnLq/3Z1CK2lNSLTCVjxVk1F/TZHwOZZrOWi0ur95BbLxNQ==}
561 + '@babel/types@7.28.4':
562 + resolution: {integrity: sha512-bkFqkLhh3pMBUQQkpVgWDWq/lqzc2678eUyDlTBhRqhCHFguYYGM0Efga7tYk4TogG/3x0EEl66/OQ+WGbWB/Q==}
563 engines: {node: '>=6.9.0'}
564
565 '@clack/core@0.5.0':
@@ -563,8 +568,8 @@ packages:
568 '@clack/prompts@0.11.0':
569 resolution: {integrity: sha512-pMN5FcrEw9hUkZA4f+zLlzivQSeQf5dRGJjSUbvVYDLvpKCdQx5OaknvKzgbtXOizhP+SJJJjqEbOe55uKKfAw==}
570
566 - '@codemirror/autocomplete@6.18.6':
567 - resolution: {integrity: sha512-PHHBXFomUs5DF+9tCOM/UoW6XQ4R44lLNNhRaW9PKPTU0D7lIjRg3ElxaJnTwsl/oHiR93WSXDBrekhoUGCPtg==}
571 + '@codemirror/autocomplete@6.18.7':
572 + resolution: {integrity: sha512-8EzdeIoWPJDsMBwz3zdzwXnUpCzMiCyz5/A3FIPpriaclFCGDkAzK13sMcnsu5rowqiyeQN2Vs2TsOcoDPZirQ==}
573
574 '@codemirror/commands@6.8.1':
575 resolution: {integrity: sha512-KlGVYufHMQzxbdQONiLyGQDUW0itrLZwq3CcY7xpv9ZLRHqzkBSoteocBHtMCoY7/Ci4xhzSrToIeLg7FxHuaw==}
@@ -575,8 +580,8 @@ packages:
580 '@codemirror/lang-xml@6.1.0':
581 resolution: {integrity: sha512-3z0blhicHLfwi2UgkZYRPioSgVTo9PV5GP5ducFH6FaHy0IAJRg+ixj5gTR1gnT/glAIC8xv4w2VL1LoZfs+Jg==}
582
578 - '@codemirror/language@6.11.2':
579 - resolution: {integrity: sha512-p44TsNArL4IVXDTbapUmEkAlvWs2CFQbcfc0ymDsis1kH2wh0gcY96AS29c/vp2d0y2Tquk1EDSaawpzilUiAw==}
583 + '@codemirror/language@6.11.3':
584 + resolution: {integrity: sha512-9HBM2XnwDj7fnu0551HkGdrUrrqmYq/WC5iv6nbY2WdicXdGbhR/gfbZOH73Aqj4351alY1+aoG9rCNfiwS1RA==}
585
586 '@codemirror/lint@6.8.5':
587 resolution: {integrity: sha512-s3n3KisH7dx3vsoeGMxsbRAgKe4O1vbrnKBClm99PU0fWxmxsx5rR2PfqQgIt+2MMJBHbiJ5rfIdLYfB9NNvsA==}
@@ -590,8 +595,8 @@ packages:
595 '@codemirror/theme-one-dark@6.1.3':
596 resolution: {integrity: sha512-NzBdIvEJmx6fjeremiGp3t/okrLPYT0d9orIc7AFun8oZcRk58aejkqhv6spnz4MLAevrKNPMQYXEWMg4s+sKA==}
597
593 - '@codemirror/view@6.38.1':
594 - resolution: {integrity: sha512-RmTOkE7hRU3OVREqFVITWHz6ocgBjv08GoePscAakgVQfciA3SGCEk7mb9IzwW61cKKmlTpHXG6DUE5Ubx+MGQ==}
598 + '@codemirror/view@6.38.2':
599 + resolution: {integrity: sha512-bTWAJxL6EOFLPzTx+O5P5xAO3gTqpatQ2b/ARQ8itfU/v2LlpS3pH2fkL0A3E/Fx8Y2St2KES7ZEV0sHTsSW/A==}
600
601 '@css-render/plugin-bem@0.15.14':
602 resolution: {integrity: sha512-QK513CJ7yEQxm/P3EwsI+d+ha8kSOcjGvD6SevM41neEMxdULE+18iuQK6tEChAWMOQNQPLG/Rw3Khb69r5neg==}
@@ -603,8 +608,8 @@ packages:
608 peerDependencies:
609 vue: ^3.0.11
610
606 - '@csstools/color-helpers@5.0.2':
607 - resolution: {integrity: sha512-JqWH1vsgdGcw2RR6VliXXdA0/59LttzlU8UlRT/iUUsEeWfYq8I+K0yhihEUTTHLRm1EXvpsCx3083EU15ecsA==}
611 + '@csstools/color-helpers@5.1.0':
612 + resolution: {integrity: sha512-S11EXWJyy0Mz5SYvRmY8nJYTFFd1LCNV+7cXyAgQtOOuzb4EsgfqDufL+9esx72/eLhsRdGZwaldu/h+E4t4BA==}
613 engines: {node: '>=18'}
614
615 '@csstools/css-calc@2.1.4':
@@ -614,8 +619,8 @@ packages:
619 '@csstools/css-parser-algorithms': ^3.0.5
620 '@csstools/css-tokenizer': ^3.0.4
621
617 - '@csstools/css-color-parser@3.0.10':
618 - resolution: {integrity: sha512-TiJ5Ajr6WRd1r8HSiwJvZBiJOqtH86aHpUjq5aEKWHiII2Qfjqd/HCWKPOW8EP4vcspXbHnXrwIDlu5savQipg==}
622 + '@csstools/css-color-parser@3.1.0':
623 + resolution: {integrity: sha512-nbtKwh3a6xNVIp/VRuXV64yTKnb1IjTAEEh3irzS+HkKjAOYLTGNb9pmVNntZ8iVBHcWDA2Dof0QtPgFI1BaTA==}
624 engines: {node: '>=18'}
625 peerDependencies:
626 '@csstools/css-parser-algorithms': ^3.0.5
@@ -627,6 +632,12 @@ packages:
632 peerDependencies:
633 '@csstools/css-tokenizer': ^3.0.4
634
635 + '@csstools/css-syntax-patches-for-csstree@1.0.14':
636 + resolution: {integrity: sha512-zSlIxa20WvMojjpCSy8WrNpcZ61RqfTfX3XTaOeVlGJrt/8HF3YbzgFZa01yTbT4GWQLwfTcC3EB8i3XnB647Q==}
637 + engines: {node: '>=18'}
638 + peerDependencies:
639 + postcss: ^8.4
640 +
641 '@csstools/css-tokenizer@3.0.4':
642 resolution: {integrity: sha512-Vd/9EVDiu6PPJt9yAh6roZP6El1xHrdvIVGjyBsHR0RYwNHgL7FJPyIIW4fANJNG6FtyZfvlRPpFI4ZM/lubvw==}
643 engines: {node: '>=18'}
@@ -645,162 +656,162 @@ packages:
656 resolution: {integrity: sha512-YAdE/IJSpwbOTiaURNCKECdAwqrJuFiZhylmesBcIRawtYKnBR2wxPhoIewMg+Yu+QuYvHfJNReWpoxGBKOChA==}
657 engines: {node: '>=18'}
658
648 - '@es-joy/jsdoccomment@0.52.0':
649 - resolution: {integrity: sha512-BXuN7BII+8AyNtn57euU2Yxo9yA/KUDNzrpXyi3pfqKmBhhysR6ZWOebFh3vyPoqA3/j1SOvGgucElMGwlXing==}
659 + '@es-joy/jsdoccomment@0.56.0':
660 + resolution: {integrity: sha512-c6EW+aA1w2rjqOMjbL93nZlwxp6c1Ln06vTYs5FjRRhmJXK8V/OrSXdT+pUr4aRYgjCgu8/OkiZr0tzeVrRSbw==}
661 engines: {node: '>=20.11.0'}
662
652 - '@esbuild/aix-ppc64@0.25.8':
653 - resolution: {integrity: sha512-urAvrUedIqEiFR3FYSLTWQgLu5tb+m0qZw0NBEasUeo6wuqatkMDaRT+1uABiGXEu5vqgPd7FGE1BhsAIy9QVA==}
663 + '@esbuild/aix-ppc64@0.25.10':
664 + resolution: {integrity: sha512-0NFWnA+7l41irNuaSVlLfgNT12caWJVLzp5eAVhZ0z1qpxbockccEt3s+149rE64VUI3Ml2zt8Nv5JVc4QXTsw==}
665 engines: {node: '>=18'}
666 cpu: [ppc64]
667 os: [aix]
668
658 - '@esbuild/android-arm64@0.25.8':
659 - resolution: {integrity: sha512-OD3p7LYzWpLhZEyATcTSJ67qB5D+20vbtr6vHlHWSQYhKtzUYrETuWThmzFpZtFsBIxRvhO07+UgVA9m0i/O1w==}
669 + '@esbuild/android-arm64@0.25.10':
670 + resolution: {integrity: sha512-LSQa7eDahypv/VO6WKohZGPSJDq5OVOo3UoFR1E4t4Gj1W7zEQMUhI+lo81H+DtB+kP+tDgBp+M4oNCwp6kffg==}
671 engines: {node: '>=18'}
672 cpu: [arm64]
673 os: [android]
674
664 - '@esbuild/android-arm@0.25.8':
665 - resolution: {integrity: sha512-RONsAvGCz5oWyePVnLdZY/HHwA++nxYWIX1atInlaW6SEkwq6XkP3+cb825EUcRs5Vss/lGh/2YxAb5xqc07Uw==}
675 + '@esbuild/android-arm@0.25.10':
676 + resolution: {integrity: sha512-dQAxF1dW1C3zpeCDc5KqIYuZ1tgAdRXNoZP7vkBIRtKZPYe2xVr/d3SkirklCHudW1B45tGiUlz2pUWDfbDD4w==}
677 engines: {node: '>=18'}
678 cpu: [arm]
679 os: [android]
680
670 - '@esbuild/android-x64@0.25.8':
671 - resolution: {integrity: sha512-yJAVPklM5+4+9dTeKwHOaA+LQkmrKFX96BM0A/2zQrbS6ENCmxc4OVoBs5dPkCCak2roAD+jKCdnmOqKszPkjA==}
681 + '@esbuild/android-x64@0.25.10':
682 + resolution: {integrity: sha512-MiC9CWdPrfhibcXwr39p9ha1x0lZJ9KaVfvzA0Wxwz9ETX4v5CHfF09bx935nHlhi+MxhA63dKRRQLiVgSUtEg==}
683 engines: {node: '>=18'}
684 cpu: [x64]
685 os: [android]
686
676 - '@esbuild/darwin-arm64@0.25.8':
677 - resolution: {integrity: sha512-Jw0mxgIaYX6R8ODrdkLLPwBqHTtYHJSmzzd+QeytSugzQ0Vg4c5rDky5VgkoowbZQahCbsv1rT1KW72MPIkevw==}
687 + '@esbuild/darwin-arm64@0.25.10':
688 + resolution: {integrity: sha512-JC74bdXcQEpW9KkV326WpZZjLguSZ3DfS8wrrvPMHgQOIEIG/sPXEN/V8IssoJhbefLRcRqw6RQH2NnpdprtMA==}
689 engines: {node: '>=18'}
690 cpu: [arm64]
691 os: [darwin]
692
682 - '@esbuild/darwin-x64@0.25.8':
683 - resolution: {integrity: sha512-Vh2gLxxHnuoQ+GjPNvDSDRpoBCUzY4Pu0kBqMBDlK4fuWbKgGtmDIeEC081xi26PPjn+1tct+Bh8FjyLlw1Zlg==}
693 + '@esbuild/darwin-x64@0.25.10':
694 + resolution: {integrity: sha512-tguWg1olF6DGqzws97pKZ8G2L7Ig1vjDmGTwcTuYHbuU6TTjJe5FXbgs5C1BBzHbJ2bo1m3WkQDbWO2PvamRcg==}
695 engines: {node: '>=18'}
696 cpu: [x64]
697 os: [darwin]
698
688 - '@esbuild/freebsd-arm64@0.25.8':
689 - resolution: {integrity: sha512-YPJ7hDQ9DnNe5vxOm6jaie9QsTwcKedPvizTVlqWG9GBSq+BuyWEDazlGaDTC5NGU4QJd666V0yqCBL2oWKPfA==}
699 + '@esbuild/freebsd-arm64@0.25.10':
700 + resolution: {integrity: sha512-3ZioSQSg1HT2N05YxeJWYR+Libe3bREVSdWhEEgExWaDtyFbbXWb49QgPvFH8u03vUPX10JhJPcz7s9t9+boWg==}
701 engines: {node: '>=18'}
702 cpu: [arm64]
703 os: [freebsd]
704
694 - '@esbuild/freebsd-x64@0.25.8':
695 - resolution: {integrity: sha512-MmaEXxQRdXNFsRN/KcIimLnSJrk2r5H8v+WVafRWz5xdSVmWLoITZQXcgehI2ZE6gioE6HirAEToM/RvFBeuhw==}
705 + '@esbuild/freebsd-x64@0.25.10':
706 + resolution: {integrity: sha512-LLgJfHJk014Aa4anGDbh8bmI5Lk+QidDmGzuC2D+vP7mv/GeSN+H39zOf7pN5N8p059FcOfs2bVlrRr4SK9WxA==}
707 engines: {node: '>=18'}
708 cpu: [x64]
709 os: [freebsd]
710
700 - '@esbuild/linux-arm64@0.25.8':
701 - resolution: {integrity: sha512-WIgg00ARWv/uYLU7lsuDK00d/hHSfES5BzdWAdAig1ioV5kaFNrtK8EqGcUBJhYqotlUByUKz5Qo6u8tt7iD/w==}
711 + '@esbuild/linux-arm64@0.25.10':
712 + resolution: {integrity: sha512-5luJWN6YKBsawd5f9i4+c+geYiVEw20FVW5x0v1kEMWNq8UctFjDiMATBxLvmmHA4bf7F6hTRaJgtghFr9iziQ==}
713 engines: {node: '>=18'}
714 cpu: [arm64]
715 os: [linux]
716
706 - '@esbuild/linux-arm@0.25.8':
707 - resolution: {integrity: sha512-FuzEP9BixzZohl1kLf76KEVOsxtIBFwCaLupVuk4eFVnOZfU+Wsn+x5Ryam7nILV2pkq2TqQM9EZPsOBuMC+kg==}
717 + '@esbuild/linux-arm@0.25.10':
718 + resolution: {integrity: sha512-oR31GtBTFYCqEBALI9r6WxoU/ZofZl962pouZRTEYECvNF/dtXKku8YXcJkhgK/beU+zedXfIzHijSRapJY3vg==}
719 engines: {node: '>=18'}
720 cpu: [arm]
721 os: [linux]
722
712 - '@esbuild/linux-ia32@0.25.8':
713 - resolution: {integrity: sha512-A1D9YzRX1i+1AJZuFFUMP1E9fMaYY+GnSQil9Tlw05utlE86EKTUA7RjwHDkEitmLYiFsRd9HwKBPEftNdBfjg==}
723 + '@esbuild/linux-ia32@0.25.10':
724 + resolution: {integrity: sha512-NrSCx2Kim3EnnWgS4Txn0QGt0Xipoumb6z6sUtl5bOEZIVKhzfyp/Lyw4C1DIYvzeW/5mWYPBFJU3a/8Yr75DQ==}
725 engines: {node: '>=18'}
726 cpu: [ia32]
727 os: [linux]
728
718 - '@esbuild/linux-loong64@0.25.8':
719 - resolution: {integrity: sha512-O7k1J/dwHkY1RMVvglFHl1HzutGEFFZ3kNiDMSOyUrB7WcoHGf96Sh+64nTRT26l3GMbCW01Ekh/ThKM5iI7hQ==}
729 + '@esbuild/linux-loong64@0.25.10':
730 + resolution: {integrity: sha512-xoSphrd4AZda8+rUDDfD9J6FUMjrkTz8itpTITM4/xgerAZZcFW7Dv+sun7333IfKxGG8gAq+3NbfEMJfiY+Eg==}
731 engines: {node: '>=18'}
732 cpu: [loong64]
733 os: [linux]
734
724 - '@esbuild/linux-mips64el@0.25.8':
725 - resolution: {integrity: sha512-uv+dqfRazte3BzfMp8PAQXmdGHQt2oC/y2ovwpTteqrMx2lwaksiFZ/bdkXJC19ttTvNXBuWH53zy/aTj1FgGw==}
735 + '@esbuild/linux-mips64el@0.25.10':
736 + resolution: {integrity: sha512-ab6eiuCwoMmYDyTnyptoKkVS3k8fy/1Uvq7Dj5czXI6DF2GqD2ToInBI0SHOp5/X1BdZ26RKc5+qjQNGRBelRA==}
737 engines: {node: '>=18'}
738 cpu: [mips64el]
739 os: [linux]
740
730 - '@esbuild/linux-ppc64@0.25.8':
731 - resolution: {integrity: sha512-GyG0KcMi1GBavP5JgAkkstMGyMholMDybAf8wF5A70CALlDM2p/f7YFE7H92eDeH/VBtFJA5MT4nRPDGg4JuzQ==}
741 + '@esbuild/linux-ppc64@0.25.10':
742 + resolution: {integrity: sha512-NLinzzOgZQsGpsTkEbdJTCanwA5/wozN9dSgEl12haXJBzMTpssebuXR42bthOF3z7zXFWH1AmvWunUCkBE4EA==}
743 engines: {node: '>=18'}
744 cpu: [ppc64]
745 os: [linux]
746
736 - '@esbuild/linux-riscv64@0.25.8':
737 - resolution: {integrity: sha512-rAqDYFv3yzMrq7GIcen3XP7TUEG/4LK86LUPMIz6RT8A6pRIDn0sDcvjudVZBiiTcZCY9y2SgYX2lgK3AF+1eg==}
747 + '@esbuild/linux-riscv64@0.25.10':
748 + resolution: {integrity: sha512-FE557XdZDrtX8NMIeA8LBJX3dC2M8VGXwfrQWU7LB5SLOajfJIxmSdyL/gU1m64Zs9CBKvm4UAuBp5aJ8OgnrA==}
749 engines: {node: '>=18'}
750 cpu: [riscv64]
751 os: [linux]
752
742 - '@esbuild/linux-s390x@0.25.8':
743 - resolution: {integrity: sha512-Xutvh6VjlbcHpsIIbwY8GVRbwoviWT19tFhgdA7DlenLGC/mbc3lBoVb7jxj9Z+eyGqvcnSyIltYUrkKzWqSvg==}
753 + '@esbuild/linux-s390x@0.25.10':
754 + resolution: {integrity: sha512-3BBSbgzuB9ajLoVZk0mGu+EHlBwkusRmeNYdqmznmMc9zGASFjSsxgkNsqmXugpPk00gJ0JNKh/97nxmjctdew==}
755 engines: {node: '>=18'}
756 cpu: [s390x]
757 os: [linux]
758
748 - '@esbuild/linux-x64@0.25.8':
749 - resolution: {integrity: sha512-ASFQhgY4ElXh3nDcOMTkQero4b1lgubskNlhIfJrsH5OKZXDpUAKBlNS0Kx81jwOBp+HCeZqmoJuihTv57/jvQ==}
759 + '@esbuild/linux-x64@0.25.10':
760 + resolution: {integrity: sha512-QSX81KhFoZGwenVyPoberggdW1nrQZSvfVDAIUXr3WqLRZGZqWk/P4T8p2SP+de2Sr5HPcvjhcJzEiulKgnxtA==}
761 engines: {node: '>=18'}
762 cpu: [x64]
763 os: [linux]
764
754 - '@esbuild/netbsd-arm64@0.25.8':
755 - resolution: {integrity: sha512-d1KfruIeohqAi6SA+gENMuObDbEjn22olAR7egqnkCD9DGBG0wsEARotkLgXDu6c4ncgWTZJtN5vcgxzWRMzcw==}
765 + '@esbuild/netbsd-arm64@0.25.10':
766 + resolution: {integrity: sha512-AKQM3gfYfSW8XRk8DdMCzaLUFB15dTrZfnX8WXQoOUpUBQ+NaAFCP1kPS/ykbbGYz7rxn0WS48/81l9hFl3u4A==}
767 engines: {node: '>=18'}
768 cpu: [arm64]
769 os: [netbsd]
770
760 - '@esbuild/netbsd-x64@0.25.8':
761 - resolution: {integrity: sha512-nVDCkrvx2ua+XQNyfrujIG38+YGyuy2Ru9kKVNyh5jAys6n+l44tTtToqHjino2My8VAY6Lw9H7RI73XFi66Cg==}
771 + '@esbuild/netbsd-x64@0.25.10':
772 + resolution: {integrity: sha512-7RTytDPGU6fek/hWuN9qQpeGPBZFfB4zZgcz2VK2Z5VpdUxEI8JKYsg3JfO0n/Z1E/6l05n0unDCNc4HnhQGig==}
773 engines: {node: '>=18'}
774 cpu: [x64]
775 os: [netbsd]
776
766 - '@esbuild/openbsd-arm64@0.25.8':
767 - resolution: {integrity: sha512-j8HgrDuSJFAujkivSMSfPQSAa5Fxbvk4rgNAS5i3K+r8s1X0p1uOO2Hl2xNsGFppOeHOLAVgYwDVlmxhq5h+SQ==}
777 + '@esbuild/openbsd-arm64@0.25.10':
778 + resolution: {integrity: sha512-5Se0VM9Wtq797YFn+dLimf2Zx6McttsH2olUBsDml+lm0GOCRVebRWUvDtkY4BWYv/3NgzS8b/UM3jQNh5hYyw==}
779 engines: {node: '>=18'}
780 cpu: [arm64]
781 os: [openbsd]
782
772 - '@esbuild/openbsd-x64@0.25.8':
773 - resolution: {integrity: sha512-1h8MUAwa0VhNCDp6Af0HToI2TJFAn1uqT9Al6DJVzdIBAd21m/G0Yfc77KDM3uF3T/YaOgQq3qTJHPbTOInaIQ==}
783 + '@esbuild/openbsd-x64@0.25.10':
784 + resolution: {integrity: sha512-XkA4frq1TLj4bEMB+2HnI0+4RnjbuGZfet2gs/LNs5Hc7D89ZQBHQ0gL2ND6Lzu1+QVkjp3x1gIcPKzRNP8bXw==}
785 engines: {node: '>=18'}
786 cpu: [x64]
787 os: [openbsd]
788
778 - '@esbuild/openharmony-arm64@0.25.8':
779 - resolution: {integrity: sha512-r2nVa5SIK9tSWd0kJd9HCffnDHKchTGikb//9c7HX+r+wHYCpQrSgxhlY6KWV1nFo1l4KFbsMlHk+L6fekLsUg==}
789 + '@esbuild/openharmony-arm64@0.25.10':
790 + resolution: {integrity: sha512-AVTSBhTX8Y/Fz6OmIVBip9tJzZEUcY8WLh7I59+upa5/GPhh2/aM6bvOMQySspnCCHvFi79kMtdJS1w0DXAeag==}
791 engines: {node: '>=18'}
792 cpu: [arm64]
793 os: [openharmony]
794
784 - '@esbuild/sunos-x64@0.25.8':
785 - resolution: {integrity: sha512-zUlaP2S12YhQ2UzUfcCuMDHQFJyKABkAjvO5YSndMiIkMimPmxA+BYSBikWgsRpvyxuRnow4nS5NPnf9fpv41w==}
795 + '@esbuild/sunos-x64@0.25.10':
796 + resolution: {integrity: sha512-fswk3XT0Uf2pGJmOpDB7yknqhVkJQkAQOcW/ccVOtfx05LkbWOaRAtn5SaqXypeKQra1QaEa841PgrSL9ubSPQ==}
797 engines: {node: '>=18'}
798 cpu: [x64]
799 os: [sunos]
800
790 - '@esbuild/win32-arm64@0.25.8':
791 - resolution: {integrity: sha512-YEGFFWESlPva8hGL+zvj2z/SaK+pH0SwOM0Nc/d+rVnW7GSTFlLBGzZkuSU9kFIGIo8q9X3ucpZhu8PDN5A2sQ==}
801 + '@esbuild/win32-arm64@0.25.10':
802 + resolution: {integrity: sha512-ah+9b59KDTSfpaCg6VdJoOQvKjI33nTaQr4UluQwW7aEwZQsbMCfTmfEO4VyewOxx4RaDT/xCy9ra2GPWmO7Kw==}
803 engines: {node: '>=18'}
804 cpu: [arm64]
805 os: [win32]
806
796 - '@esbuild/win32-ia32@0.25.8':
797 - resolution: {integrity: sha512-hiGgGC6KZ5LZz58OL/+qVVoZiuZlUYlYHNAmczOm7bs2oE1XriPFi5ZHHrS8ACpV5EjySrnoCKmcbQMN+ojnHg==}
807 + '@esbuild/win32-ia32@0.25.10':
808 + resolution: {integrity: sha512-QHPDbKkrGO8/cz9LKVnJU22HOi4pxZnZhhA2HYHez5Pz4JeffhDjf85E57Oyco163GnzNCVkZK0b/n4Y0UHcSw==}
809 engines: {node: '>=18'}
810 cpu: [ia32]
811 os: [win32]
812
802 - '@esbuild/win32-x64@0.25.8':
803 - resolution: {integrity: sha512-cn3Yr7+OaaZq1c+2pe+8yxC8E144SReCQjN6/2ynubzYjvyqZjTXfQJpAcQpsdJq3My7XADANiYGHoFC69pLQw==}
813 + '@esbuild/win32-x64@0.25.10':
814 + resolution: {integrity: sha512-9KpxSVFCu0iK1owoez6aC/s/EdUQLDN3adTxGCqxMVhrPDj6bt5dbrHDXUuq+Bs2vATFBBrQS5vdQ/Ed2P+nbw==}
815 engines: {node: '>=18'}
816 cpu: [x64]
817 os: [win32]
@@ -811,8 +822,8 @@ packages:
822 peerDependencies:
823 eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 || ^9.0.0
824
814 - '@eslint-community/eslint-utils@4.7.0':
815 - resolution: {integrity: sha512-dyybb3AcajC7uha6CvhdVRJqaKyn7w2YKqKyAN37NKYgZT36w+iRb0Dymmc5qEJ549c/S31cMMSFd75bteCpCw==}
825 + '@eslint-community/eslint-utils@4.9.0':
826 + resolution: {integrity: sha512-ayVFHdtZ+hsq1t2Dy24wCmGXGe4q9Gu3smhLYALJrr473ZH27MsnSL+LKUlimp4BWJqMDMLmPpx/Q9R3OAlL4g==}
827 engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
828 peerDependencies:
829 eslint: ^6.0.0 || ^7.0.0 || >=8.0.0
@@ -821,8 +832,8 @@ packages:
832 resolution: {integrity: sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==}
833 engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0}
834
824 - '@eslint/compat@1.3.1':
825 - resolution: {integrity: sha512-k8MHony59I5EPic6EQTCNOuPoVBnoYXkP+20xvwFjN7t0qI3ImyvyBgg+hIVPwC8JaxVjjUZld+cLfBLFDLucg==}
835 + '@eslint/compat@1.3.2':
836 + resolution: {integrity: sha512-jRNwzTbd6p2Rw4sZ1CgWRS8YMtqG15YyZf7zvb6gY2rB2u6n+2Z+ELW0GtL0fQgyl0pr4Y/BzBfng/BdsereRA==}
837 engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
838 peerDependencies:
839 eslint: ^8.40 || 9
@@ -834,32 +845,32 @@ packages:
845 resolution: {integrity: sha512-ENIdc4iLu0d93HeYirvKmrzshzofPw6VkZRKQGe9Nv46ZnWUzcF1xV01dcvEg/1wXUR61OmmlSfyeyO7EvjLxQ==}
846 engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
847
837 - '@eslint/config-helpers@0.3.0':
838 - resolution: {integrity: sha512-ViuymvFmcJi04qdZeDc2whTHryouGcDlaxPqarTD0ZE10ISpxGUVZGZDx4w01upyIynL3iu6IXH2bS1NhclQMw==}
848 + '@eslint/config-helpers@0.3.1':
849 + resolution: {integrity: sha512-xR93k9WhrDYpXHORXpxVL5oHj3Era7wo6k/Wd8/IsQNnZUTzkGS29lyn3nAT05v6ltUuTFVCCYDEGfy2Or/sPA==}
850 engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
851
841 - '@eslint/core@0.15.1':
842 - resolution: {integrity: sha512-bkOp+iumZCCbt1K1CmWf0R9pM5yKpDv+ZXtvSyQpudrI9kuFLp+bM2WOPXImuD/ceQuaa8f5pj93Y7zyECIGNA==}
852 + '@eslint/core@0.15.2':
853 + resolution: {integrity: sha512-78Md3/Rrxh83gCxoUc0EiciuOHsIITzLy53m3d9UyiW8y9Dj2D29FeETqyKA+BRK76tnTp6RXWb3pCay8Oyomg==}
854 engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
855
856 '@eslint/eslintrc@3.3.1':
857 resolution: {integrity: sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==}
858 engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
859
849 - '@eslint/js@9.32.0':
850 - resolution: {integrity: sha512-BBpRFZK3eX6uMLKz8WxFOBIFFcGFJ/g8XuwjTHCqHROSIsopI+ddn/d5Cfh36+7+e5edVS8dbSHnBNhrLEX0zg==}
860 + '@eslint/js@9.35.0':
861 + resolution: {integrity: sha512-30iXE9whjlILfWobBkNerJo+TXYsgVM5ERQwMcMKCHckHflCmf7wXDAHlARoWnh0s1U72WqlbeyE7iAcCzuCPw==}
862 engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
863
853 - '@eslint/markdown@7.1.0':
854 - resolution: {integrity: sha512-Y+X1B1j+/zupKDVJfkKc8uYMjQkGzfnd8lt7vK3y8x9Br6H5dBuhAfFrQ6ff7HAMm/1BwgecyEiRFkYCWPRxmA==}
864 + '@eslint/markdown@7.2.0':
865 + resolution: {integrity: sha512-cmDloByulvKzofM0tIkSGWwxMcrKOLsXZC+EM0FLkRIrxKzW+2RkZAt9TAh37EtQRmx1M4vjBEmlC6R0wiGkog==}
866 engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
867
868 '@eslint/object-schema@2.1.6':
869 resolution: {integrity: sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==}
870 engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
871
861 - '@eslint/plugin-kit@0.3.4':
862 - resolution: {integrity: sha512-Ul5l+lHEcw3L5+k8POx6r74mxEYKG5kOb6Xpy2gCRW6zweT6TEhAf8vhxGgjhqrd/VO/Dirhsb+1hNpD1ue9hw==}
872 + '@eslint/plugin-kit@0.3.5':
873 + resolution: {integrity: sha512-Z5kJ+wU3oA7MMIqVR9tyZRtjYPr4OC004Q4Rw7pgOKUOKkJfZ3O24nz3WYfGRpMDNmcOi3TwQOmgm7B7Tpii0w==}
874 engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
875
876 '@f3ve/vue-markdown-it@0.2.3':
@@ -867,14 +878,14 @@ packages:
878 peerDependencies:
879 vue: ^3.3.4
880
870 - '@fontsource/jetbrains-mono@5.2.6':
871 - resolution: {integrity: sha512-nz//dBr99hXZmHp10wgNI00qThWImkzRR5PQjvRM+rpmuHO5rYBJCqPPWufidCvmkkryXx/GOP/lgqsM3R3Org==}
881 + '@fontsource/jetbrains-mono@5.2.8':
882 + resolution: {integrity: sha512-6w8/SG4kqvIMu7xd7wt6x3idn1Qux3p9N62s6G3rfldOUYHpWcc2FKrqf+Vo44jRvqWj2oAtTHrZXEP23oSKwQ==}
883
873 - '@fontsource/lexend@5.2.9':
874 - resolution: {integrity: sha512-0a5xzwksBilec8Q+QwPvTFKFcXYw31oyf5CthPKd+C5NJZDl1aHw4FcMz9bcRMPhq0LXO69BXI8aVxmj15pzNA==}
884 + '@fontsource/lexend@5.2.11':
885 + resolution: {integrity: sha512-bShbQr2g2sWl7GJNyIxdllZST1faVbebXN6H7AEEDCRms/g4orDLxWnmTVodOtiErdZVUJURq4jpafZ34nIkTw==}
886
876 - '@fontsource/public-sans@5.2.6':
877 - resolution: {integrity: sha512-/IcobGqED86QkmlHs8HE6bwx5rNNi7Rt6jKwUp49+TrVjYh+6pFwJnHSwcXhJUBrGRjUkZxgW0EvCakQlwJ5MA==}
887 + '@fontsource/public-sans@5.2.7':
888 + resolution: {integrity: sha512-pVttDr3HvhVIt2x6sZJfZKksNEpq23C1JCHTQwT9KWJdmFRdNeRM8gQftq8uP72sPW+p3Ku9x8HPk2E392DFvg==}
889
890 '@hapi/hoek@9.3.0':
891 resolution: {integrity: sha512-/c6rf4UJlmHlC9b5BaNvzAcFv7HZ2QHaV0D4/HNlBdvFnvQq8RI4kYdhyPCl7Xj+oWvTWQ8ujhqS53LIgAe6KQ==}
@@ -886,18 +897,14 @@ packages:
897 resolution: {integrity: sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==}
898 engines: {node: '>=18.18.0'}
899
889 - '@humanfs/node@0.16.6':
890 - resolution: {integrity: sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw==}
900 + '@humanfs/node@0.16.7':
901 + resolution: {integrity: sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==}
902 engines: {node: '>=18.18.0'}
903
904 '@humanwhocodes/module-importer@1.0.1':
905 resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==}
906 engines: {node: '>=12.22'}
907
897 - '@humanwhocodes/retry@0.3.1':
898 - resolution: {integrity: sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==}
899 - engines: {node: '>=18.18'}
900 -
908 '@humanwhocodes/retry@0.4.3':
909 resolution: {integrity: sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==}
910 engines: {node: '>=18.18'}
@@ -910,16 +917,16 @@ packages:
917 peerDependencies:
918 vue: '>=3'
919
913 - '@intlify/core-base@11.1.11':
914 - resolution: {integrity: sha512-1Z0N8jTfkcD2Luq9HNZt+GmjpFe4/4PpZF3AOzoO1u5PTtSuXZcfhwBatywbfE2ieB/B5QHIoOFmCXY2jqVKEQ==}
920 + '@intlify/core-base@11.1.12':
921 + resolution: {integrity: sha512-whh0trqRsSqVLNEUCwU59pyJZYpU8AmSWl8M3Jz2Mv5ESPP6kFh4juas2NpZ1iCvy7GlNRffUD1xr84gceimjg==}
922 engines: {node: '>= 16'}
923
917 - '@intlify/message-compiler@11.1.11':
918 - resolution: {integrity: sha512-7PC6neomoc/z7a8JRjPBbu0T2TzR2MQuY5kn2e049MP7+o32Ve7O8husylkA7K9fQRe4iNXZWTPnDJ6vZdtS1Q==}
924 + '@intlify/message-compiler@11.1.12':
925 + resolution: {integrity: sha512-Fv9iQSJoJaXl4ZGkOCN1LDM3trzze0AS2zRz2EHLiwenwL6t0Ki9KySYlyr27yVOj5aVz0e55JePO+kELIvfdQ==}
926 engines: {node: '>= 16'}
927
921 - '@intlify/shared@11.1.11':
922 - resolution: {integrity: sha512-RIBFTIqxZSsxUqlcyoR7iiC632bq7kkOwYvZlvcVObHfrF4NhuKc4FKvu8iPCrEO+e3XsY7/UVpfgzg+M7ETzA==}
928 + '@intlify/shared@11.1.12':
929 + resolution: {integrity: sha512-Om86EjuQtA69hdNj3GQec9ZC0L0vPSAnXzB3gP/gyJ7+mA7t06d9aOAiqMZ+xEOsumGP4eEBlfl8zF2LOTzf2A==}
930 engines: {node: '>= 16'}
931
932 '@isaacs/cliui@8.0.2':
@@ -930,18 +937,21 @@ packages:
937 resolution: {integrity: sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==}
938 engines: {node: '>=18.0.0'}
939
933 - '@jridgewell/gen-mapping@0.3.12':
934 - resolution: {integrity: sha512-OuLGC46TjB5BbN1dH8JULVVZY4WTdkF7tV9Ys6wLL1rubZnCMstOhNHueU5bLCrnRuDhKPDM4g6sw4Bel5Gzqg==}
940 + '@jridgewell/gen-mapping@0.3.13':
941 + resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==}
942 +
943 + '@jridgewell/remapping@2.3.5':
944 + resolution: {integrity: sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==}
945
946 '@jridgewell/resolve-uri@3.1.2':
947 resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==}
948 engines: {node: '>=6.0.0'}
949
940 - '@jridgewell/sourcemap-codec@1.5.4':
941 - resolution: {integrity: sha512-VT2+G1VQs/9oz078bLrYbecdZKs912zQlkelYpuf+SXF+QvZDYJlbx/LSx+meSAwdDFnF8FVXW92AVjjkVmgFw==}
950 + '@jridgewell/sourcemap-codec@1.5.5':
951 + resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==}
952
943 - '@jridgewell/trace-mapping@0.3.29':
944 - resolution: {integrity: sha512-uw6guiW/gcAGPDhLmd77/6lW8QLeiV5RUTsAX46Db6oLhGaVj4lhnPwb184s1bkc8kdVg/+h988dro8GRDpmYQ==}
953 + '@jridgewell/trace-mapping@0.3.31':
954 + resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==}
955
956 '@juggle/resize-observer@3.4.0':
957 resolution: {integrity: sha512-dfLbk+PwWvFzSxwk3n5ySL0hfBog779o8h68wK/7/APo/7cgyWp5jcXockbxdk5kFRkbeXWm4Fbi9FrdN381sA==}
@@ -952,8 +962,8 @@ packages:
962 '@lezer/highlight@1.2.1':
963 resolution: {integrity: sha512-Z5duk4RN/3zuVO7Jq0pGLJ3qynpxUVsh7IbUbGj88+uV2ApSAn6kWg2au3iJb+0Zi7kKtqffIESgNcRXWZWmSA==}
964
955 - '@lezer/javascript@1.5.1':
956 - resolution: {integrity: sha512-ATOImjeVJuvgm3JQ/bpo2Tmv55HSScE2MTPnKRMRIPx2cLhHGyX2VnqpHhtIV1tVzIjZDbcWQm+NCTF40ggZVw==}
965 + '@lezer/javascript@1.5.4':
966 + resolution: {integrity: sha512-vvYx3MhWqeZtGPwDStM2dwgljd5smolYD2lR2UyFcHfxbBQebqx8yjmFmxtJ/E6nN6u1D9srOiVWm3Rb4tmcUA==}
967
968 '@lezer/lr@1.4.2':
969 resolution: {integrity: sha512-pu0K1jCIdnQ12aWNaAVU5bzi7Bd1w54J3ECgANPmYLtQKP0HBj2cE/5coBD66MT10xbtIuUr7tg0Shbsvk0mDA==}
@@ -976,8 +986,8 @@ packages:
986 resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==}
987 engines: {node: '>= 8'}
988
979 - '@nuxt/kit@3.18.0':
980 - resolution: {integrity: sha512-svS1CBEx7gMgEIaNYrQt26J/t5bDSUdIf7GQWr5M6yszOzLw+IVzyfH7TBmuxZEbjovhLaJEG379mgKp82H/lA==}
989 + '@nuxt/kit@3.19.2':
990 + resolution: {integrity: sha512-+QiqO0WcIxsKLUqXdVn3m4rzTRm2fO9MZgd330utCAaagGmHsgiMJp67kE14boJEPutnikfz3qOmrzBnDIHUUg==}
991 engines: {node: '>=18.12.0'}
992
993 '@one-ini/wasm@0.1.1':
@@ -1076,144 +1086,148 @@ packages:
1086 '@polka/url@1.0.0-next.29':
1087 resolution: {integrity: sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww==}
1088
1079 - '@quansync/fs@0.1.3':
1080 - resolution: {integrity: sha512-G0OnZbMWEs5LhDyqy2UL17vGhSVHkQIfVojMtEWVenvj0V5S84VBgy86kJIuNsGDp2p7sTKlpSIpBUWdC35OKg==}
1081 - engines: {node: '>=20.0.0'}
1089 + '@quansync/fs@0.1.5':
1090 + resolution: {integrity: sha512-lNS9hL2aS2NZgNW7BBj+6EBl4rOf8l+tQ0eRY6JWCI8jI2kc53gSoqbjojU0OnAWhzoXiOjFyGsHcDGePB3lhA==}
1091
1092 '@rolldown/pluginutils@1.0.0-beta.29':
1093 resolution: {integrity: sha512-NIJgOsMjbxAXvoGq/X0gD7VPMQ8j9g0BiDaNjVNVjvl+iKXxL3Jre0v31RmBYeLEmkbj2s02v8vFTbUXi5XS2Q==}
1094
1086 - '@rolldown/pluginutils@1.0.0-beta.30':
1087 - resolution: {integrity: sha512-whXaSoNUFiyDAjkUF8OBpOm77Szdbk5lGNqFe6CbVbJFrhCCPinCbRA3NjawwlNHla1No7xvXXh+CpSxnPfUEw==}
1095 + '@rolldown/pluginutils@1.0.0-beta.38':
1096 + resolution: {integrity: sha512-N/ICGKleNhA5nc9XXQG/kkKHJ7S55u0x0XUJbbkmdCnFuoRkM1Il12q9q0eX19+M7KKUEPw/daUPIRnxhcxAIw==}
1097
1089 - '@rollup/rollup-android-arm-eabi@4.46.2':
1090 - resolution: {integrity: sha512-Zj3Hl6sN34xJtMv7Anwb5Gu01yujyE/cLBDB2gnHTAHaWS1Z38L7kuSG+oAh0giZMqG060f/YBStXtMH6FvPMA==}
1098 + '@rollup/rollup-android-arm-eabi@4.50.2':
1099 + resolution: {integrity: sha512-uLN8NAiFVIRKX9ZQha8wy6UUs06UNSZ32xj6giK/rmMXAgKahwExvK6SsmgU5/brh4w/nSgj8e0k3c1HBQpa0A==}
1100 cpu: [arm]
1101 os: [android]
1102
1094 - '@rollup/rollup-android-arm64@4.46.2':
1095 - resolution: {integrity: sha512-nTeCWY83kN64oQ5MGz3CgtPx8NSOhC5lWtsjTs+8JAJNLcP3QbLCtDDgUKQc/Ro/frpMq4SHUaHN6AMltcEoLQ==}
1103 + '@rollup/rollup-android-arm64@4.50.2':
1104 + resolution: {integrity: sha512-oEouqQk2/zxxj22PNcGSskya+3kV0ZKH+nQxuCCOGJ4oTXBdNTbv+f/E3c74cNLeMO1S5wVWacSws10TTSB77g==}
1105 cpu: [arm64]
1106 os: [android]
1107
1099 - '@rollup/rollup-darwin-arm64@4.46.2':
1100 - resolution: {integrity: sha512-HV7bW2Fb/F5KPdM/9bApunQh68YVDU8sO8BvcW9OngQVN3HHHkw99wFupuUJfGR9pYLLAjcAOA6iO+evsbBaPQ==}
1108 + '@rollup/rollup-darwin-arm64@4.50.2':
1109 + resolution: {integrity: sha512-OZuTVTpj3CDSIxmPgGH8en/XtirV5nfljHZ3wrNwvgkT5DQLhIKAeuFSiwtbMto6oVexV0k1F1zqURPKf5rI1Q==}
1110 cpu: [arm64]
1111 os: [darwin]
1112
1104 - '@rollup/rollup-darwin-x64@4.46.2':
1105 - resolution: {integrity: sha512-SSj8TlYV5nJixSsm/y3QXfhspSiLYP11zpfwp6G/YDXctf3Xkdnk4woJIF5VQe0of2OjzTt8EsxnJDCdHd2xMA==}
1113 + '@rollup/rollup-darwin-x64@4.50.2':
1114 + resolution: {integrity: sha512-Wa/Wn8RFkIkr1vy1k1PB//VYhLnlnn5eaJkfTQKivirOvzu5uVd2It01ukeQstMursuz7S1bU+8WW+1UPXpa8A==}
1115 cpu: [x64]
1116 os: [darwin]
1117
1109 - '@rollup/rollup-freebsd-arm64@4.46.2':
1110 - resolution: {integrity: sha512-ZyrsG4TIT9xnOlLsSSi9w/X29tCbK1yegE49RYm3tu3wF1L/B6LVMqnEWyDB26d9Ecx9zrmXCiPmIabVuLmNSg==}
1118 + '@rollup/rollup-freebsd-arm64@4.50.2':
1119 + resolution: {integrity: sha512-QkzxvH3kYN9J1w7D1A+yIMdI1pPekD+pWx7G5rXgnIlQ1TVYVC6hLl7SOV9pi5q9uIDF9AuIGkuzcbF7+fAhow==}
1120 cpu: [arm64]
1121 os: [freebsd]
1122
1114 - '@rollup/rollup-freebsd-x64@4.46.2':
1115 - resolution: {integrity: sha512-pCgHFoOECwVCJ5GFq8+gR8SBKnMO+xe5UEqbemxBpCKYQddRQMgomv1104RnLSg7nNvgKy05sLsY51+OVRyiVw==}
1123 + '@rollup/rollup-freebsd-x64@4.50.2':
1124 + resolution: {integrity: sha512-dkYXB0c2XAS3a3jmyDkX4Jk0m7gWLFzq1C3qUnJJ38AyxIF5G/dyS4N9B30nvFseCfgtCEdbYFhk0ChoCGxPog==}
1125 cpu: [x64]
1126 os: [freebsd]
1127
1119 - '@rollup/rollup-linux-arm-gnueabihf@4.46.2':
1120 - resolution: {integrity: sha512-EtP8aquZ0xQg0ETFcxUbU71MZlHaw9MChwrQzatiE8U/bvi5uv/oChExXC4mWhjiqK7azGJBqU0tt5H123SzVA==}
1128 + '@rollup/rollup-linux-arm-gnueabihf@4.50.2':
1129 + resolution: {integrity: sha512-9VlPY/BN3AgbukfVHAB8zNFWB/lKEuvzRo1NKev0Po8sYFKx0i+AQlCYftgEjcL43F2h9Ui1ZSdVBc4En/sP2w==}
1130 cpu: [arm]
1131 os: [linux]
1132
1124 - '@rollup/rollup-linux-arm-musleabihf@4.46.2':
1125 - resolution: {integrity: sha512-qO7F7U3u1nfxYRPM8HqFtLd+raev2K137dsV08q/LRKRLEc7RsiDWihUnrINdsWQxPR9jqZ8DIIZ1zJJAm5PjQ==}
1133 + '@rollup/rollup-linux-arm-musleabihf@4.50.2':
1134 + resolution: {integrity: sha512-+GdKWOvsifaYNlIVf07QYan1J5F141+vGm5/Y8b9uCZnG/nxoGqgCmR24mv0koIWWuqvFYnbURRqw1lv7IBINw==}
1135 cpu: [arm]
1136 os: [linux]
1137
1129 - '@rollup/rollup-linux-arm64-gnu@4.46.2':
1130 - resolution: {integrity: sha512-3dRaqLfcOXYsfvw5xMrxAk9Lb1f395gkoBYzSFcc/scgRFptRXL9DOaDpMiehf9CO8ZDRJW2z45b6fpU5nwjng==}
1138 + '@rollup/rollup-linux-arm64-gnu@4.50.2':
1139 + resolution: {integrity: sha512-df0Eou14ojtUdLQdPFnymEQteENwSJAdLf5KCDrmZNsy1c3YaCNaJvYsEUHnrg+/DLBH612/R0xd3dD03uz2dg==}
1140 cpu: [arm64]
1141 os: [linux]
1142
1134 - '@rollup/rollup-linux-arm64-musl@4.46.2':
1135 - resolution: {integrity: sha512-fhHFTutA7SM+IrR6lIfiHskxmpmPTJUXpWIsBXpeEwNgZzZZSg/q4i6FU4J8qOGyJ0TR+wXBwx/L7Ho9z0+uDg==}
1143 + '@rollup/rollup-linux-arm64-musl@4.50.2':
1144 + resolution: {integrity: sha512-iPeouV0UIDtz8j1YFR4OJ/zf7evjauqv7jQ/EFs0ClIyL+by++hiaDAfFipjOgyz6y6xbDvJuiU4HwpVMpRFDQ==}
1145 cpu: [arm64]
1146 os: [linux]
1147
1139 - '@rollup/rollup-linux-loongarch64-gnu@4.46.2':
1140 - resolution: {integrity: sha512-i7wfGFXu8x4+FRqPymzjD+Hyav8l95UIZ773j7J7zRYc3Xsxy2wIn4x+llpunexXe6laaO72iEjeeGyUFmjKeA==}
1148 + '@rollup/rollup-linux-loong64-gnu@4.50.2':
1149 + resolution: {integrity: sha512-OL6KaNvBopLlj5fTa5D5bau4W82f+1TyTZRr2BdnfsrnQnmdxh4okMxR2DcDkJuh4KeoQZVuvHvzuD/lyLn2Kw==}
1150 cpu: [loong64]
1151 os: [linux]
1152
1144 - '@rollup/rollup-linux-ppc64-gnu@4.46.2':
1145 - resolution: {integrity: sha512-B/l0dFcHVUnqcGZWKcWBSV2PF01YUt0Rvlurci5P+neqY/yMKchGU8ullZvIv5e8Y1C6wOn+U03mrDylP5q9Yw==}
1153 + '@rollup/rollup-linux-ppc64-gnu@4.50.2':
1154 + resolution: {integrity: sha512-I21VJl1w6z/K5OTRl6aS9DDsqezEZ/yKpbqlvfHbW0CEF5IL8ATBMuUx6/mp683rKTK8thjs/0BaNrZLXetLag==}
1155 cpu: [ppc64]
1156 os: [linux]
1157
1149 - '@rollup/rollup-linux-riscv64-gnu@4.46.2':
1150 - resolution: {integrity: sha512-32k4ENb5ygtkMwPMucAb8MtV8olkPT03oiTxJbgkJa7lJ7dZMr0GCFJlyvy+K8iq7F/iuOr41ZdUHaOiqyR3iQ==}
1158 + '@rollup/rollup-linux-riscv64-gnu@4.50.2':
1159 + resolution: {integrity: sha512-Hq6aQJT/qFFHrYMjS20nV+9SKrXL2lvFBENZoKfoTH2kKDOJqff5OSJr4x72ZaG/uUn+XmBnGhfr4lwMRrmqCQ==}
1160 cpu: [riscv64]
1161 os: [linux]
1162
1154 - '@rollup/rollup-linux-riscv64-musl@4.46.2':
1155 - resolution: {integrity: sha512-t5B2loThlFEauloaQkZg9gxV05BYeITLvLkWOkRXogP4qHXLkWSbSHKM9S6H1schf/0YGP/qNKtiISlxvfmmZw==}
1163 + '@rollup/rollup-linux-riscv64-musl@4.50.2':
1164 + resolution: {integrity: sha512-82rBSEXRv5qtKyr0xZ/YMF531oj2AIpLZkeNYxmKNN6I2sVE9PGegN99tYDLK2fYHJITL1P2Lgb4ZXnv0PjQvw==}
1165 cpu: [riscv64]
1166 os: [linux]
1167
1159 - '@rollup/rollup-linux-s390x-gnu@4.46.2':
1160 - resolution: {integrity: sha512-YKjekwTEKgbB7n17gmODSmJVUIvj8CX7q5442/CK80L8nqOUbMtf8b01QkG3jOqyr1rotrAnW6B/qiHwfcuWQA==}
1168 + '@rollup/rollup-linux-s390x-gnu@4.50.2':
1169 + resolution: {integrity: sha512-4Q3S3Hy7pC6uaRo9gtXUTJ+EKo9AKs3BXKc2jYypEcMQ49gDPFU2P1ariX9SEtBzE5egIX6fSUmbmGazwBVF9w==}
1170 cpu: [s390x]
1171 os: [linux]
1172
1164 - '@rollup/rollup-linux-x64-gnu@4.46.2':
1165 - resolution: {integrity: sha512-Jj5a9RUoe5ra+MEyERkDKLwTXVu6s3aACP51nkfnK9wJTraCC8IMe3snOfALkrjTYd2G1ViE1hICj0fZ7ALBPA==}
1173 + '@rollup/rollup-linux-x64-gnu@4.50.2':
1174 + resolution: {integrity: sha512-9Jie/At6qk70dNIcopcL4p+1UirusEtznpNtcq/u/C5cC4HBX7qSGsYIcG6bdxj15EYWhHiu02YvmdPzylIZlA==}
1175 cpu: [x64]
1176 os: [linux]
1177
1169 - '@rollup/rollup-linux-x64-musl@4.46.2':
1170 - resolution: {integrity: sha512-7kX69DIrBeD7yNp4A5b81izs8BqoZkCIaxQaOpumcJ1S/kmqNFjPhDu1LHeVXv0SexfHQv5cqHsxLOjETuqDuA==}
1178 + '@rollup/rollup-linux-x64-musl@4.50.2':
1179 + resolution: {integrity: sha512-HPNJwxPL3EmhzeAnsWQCM3DcoqOz3/IC6de9rWfGR8ZCuEHETi9km66bH/wG3YH0V3nyzyFEGUZeL5PKyy4xvw==}
1180 cpu: [x64]
1181 os: [linux]
1182
1174 - '@rollup/rollup-win32-arm64-msvc@4.46.2':
1175 - resolution: {integrity: sha512-wiJWMIpeaak/jsbaq2HMh/rzZxHVW1rU6coyeNNpMwk5isiPjSTx0a4YLSlYDwBH/WBvLz+EtsNqQScZTLJy3g==}
1183 + '@rollup/rollup-openharmony-arm64@4.50.2':
1184 + resolution: {integrity: sha512-nMKvq6FRHSzYfKLHZ+cChowlEkR2lj/V0jYj9JnGUVPL2/mIeFGmVM2mLaFeNa5Jev7W7TovXqXIG2d39y1KYA==}
1185 + cpu: [arm64]
1186 + os: [openharmony]
1187 +
1188 + '@rollup/rollup-win32-arm64-msvc@4.50.2':
1189 + resolution: {integrity: sha512-eFUvvnTYEKeTyHEijQKz81bLrUQOXKZqECeiWH6tb8eXXbZk+CXSG2aFrig2BQ/pjiVRj36zysjgILkqarS2YA==}
1190 cpu: [arm64]
1191 os: [win32]
1192
1179 - '@rollup/rollup-win32-ia32-msvc@4.46.2':
1180 - resolution: {integrity: sha512-gBgaUDESVzMgWZhcyjfs9QFK16D8K6QZpwAaVNJxYDLHWayOta4ZMjGm/vsAEy3hvlS2GosVFlBlP9/Wb85DqQ==}
1193 + '@rollup/rollup-win32-ia32-msvc@4.50.2':
1194 + resolution: {integrity: sha512-cBaWmXqyfRhH8zmUxK3d3sAhEWLrtMjWBRwdMMHJIXSjvjLKvv49adxiEz+FJ8AP90apSDDBx2Tyd/WylV6ikA==}
1195 cpu: [ia32]
1196 os: [win32]
1197
1184 - '@rollup/rollup-win32-x64-msvc@4.46.2':
1185 - resolution: {integrity: sha512-CvUo2ixeIQGtF6WvuB87XWqPQkoFAFqW+HUo/WzHwuHDvIwZCtjdWXoYCcr06iKGydiqTclC4jU/TNObC/xKZg==}
1198 + '@rollup/rollup-win32-x64-msvc@4.50.2':
1199 + resolution: {integrity: sha512-APwKy6YUhvZaEoHyM+9xqmTpviEI+9eL7LoCH+aLcvWYHJ663qG5zx7WzWZY+a9qkg5JtzcMyJ9z0WtQBMDmgA==}
1200 cpu: [x64]
1201 os: [win32]
1202
1203 '@sec-ant/readable-stream@0.4.1':
1204 resolution: {integrity: sha512-831qok9r2t8AlxLko40y2ebgSDhenenCatLVeW/uBtnHPyhHOvG0C7TvfgecV+wHzIm5KUICgzmVpWS+IMEAeg==}
1205
1192 - '@shikijs/core@3.8.1':
1193 - resolution: {integrity: sha512-uTSXzUBQ/IgFcUa6gmGShCHr4tMdR3pxUiiWKDm8pd42UKJdYhkAYsAmHX5mTwybQ5VyGDgTjW4qKSsRvGSang==}
1206 + '@shikijs/core@3.12.2':
1207 + resolution: {integrity: sha512-L1Safnhra3tX/oJK5kYHaWmLEBJi1irASwewzY3taX5ibyXyMkkSDZlq01qigjryOBwrXSdFgTiZ3ryzSNeu7Q==}
1208
1195 - '@shikijs/engine-javascript@3.8.1':
1196 - resolution: {integrity: sha512-rZRp3BM1llrHkuBPAdYAzjlF7OqlM0rm/7EWASeCcY7cRYZIrOnGIHE9qsLz5TCjGefxBFnwgIECzBs2vmOyKA==}
1209 + '@shikijs/engine-javascript@3.12.2':
1210 + resolution: {integrity: sha512-Nm3/azSsaVS7hk6EwtHEnTythjQfwvrO5tKqMlaH9TwG1P+PNaR8M0EAKZ+GaH2DFwvcr4iSfTveyxMIvXEHMw==}
1211
1198 - '@shikijs/engine-oniguruma@3.8.1':
1199 - resolution: {integrity: sha512-KGQJZHlNY7c656qPFEQpIoqOuC4LrxjyNndRdzk5WKB/Ie87+NJCF1xo9KkOUxwxylk7rT6nhlZyTGTC4fCe1g==}
1212 + '@shikijs/engine-oniguruma@3.12.2':
1213 + resolution: {integrity: sha512-hozwnFHsLvujK4/CPVHNo3Bcg2EsnG8krI/ZQ2FlBlCRpPZW4XAEQmEwqegJsypsTAN9ehu2tEYe30lYKSZW/w==}
1214
1201 - '@shikijs/langs@3.8.1':
1202 - resolution: {integrity: sha512-TjOFg2Wp1w07oKnXjs0AUMb4kJvujML+fJ1C5cmEj45lhjbUXtziT1x2bPQb9Db6kmPhkG5NI2tgYW1/DzhUuQ==}
1215 + '@shikijs/langs@3.12.2':
1216 + resolution: {integrity: sha512-bVx5PfuZHDSHoBal+KzJZGheFuyH4qwwcwG/n+MsWno5cTlKmaNtTsGzJpHYQ8YPbB5BdEdKU1rga5/6JGY8ww==}
1217
1204 - '@shikijs/markdown-it@3.8.1':
1205 - resolution: {integrity: sha512-5/zTzhQfL4nYq68cw98JLBac3Ex2AzMcMBvQoB4am2uNaqISDqMSSSEvOKRXSQL357M7YxRtbyjgtg1Xpaboog==}
1218 + '@shikijs/markdown-it@3.12.2':
1219 + resolution: {integrity: sha512-L3T7oxs+fB6ireA6EIFpn/3RqU+p8ywiA3cCmdWjC6CtqTJtb3H9g7rtpEFzdYb+E228Z+VYWFsh5zXl/HZuOw==}
1220 peerDependencies:
1221 markdown-it-async: ^2.2.0
1222 peerDependenciesMeta:
1223 markdown-it-async:
1224 optional: true
1225
1212 - '@shikijs/themes@3.8.1':
1213 - resolution: {integrity: sha512-Vu3t3BBLifc0GB0UPg2Pox1naTemrrvyZv2lkiSw3QayVV60me1ujFQwPZGgUTmwXl1yhCPW8Lieesm0CYruLQ==}
1226 + '@shikijs/themes@3.12.2':
1227 + resolution: {integrity: sha512-fTR3QAgnwYpfGczpIbzPjlRnxyONJOerguQv1iwpyQZ9QXX4qy/XFQqXlf17XTsorxnHoJGbH/LXBvwtqDsF5A==}
1228
1215 - '@shikijs/types@3.8.1':
1216 - resolution: {integrity: sha512-5C39Q8/8r1I26suLh+5TPk1DTrbY/kn3IdWA5HdizR0FhlhD05zx5nKCqhzSfDHH3p4S0ZefxWd77DLV+8FhGg==}
1229 + '@shikijs/types@3.12.2':
1230 + resolution: {integrity: sha512-K5UIBzxCyv0YoxN3LMrKB9zuhp1bV+LgewxuVwHdl4Gz5oePoUFrr9EfgJlGlDeXCU1b/yhdnXeuRvAnz8HN8Q==}
1231
1232 '@shikijs/vscode-textmate@10.0.2':
1233 resolution: {integrity: sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg==}
@@ -1234,8 +1248,8 @@ packages:
1248 '@singulio/app-auth-search@0.0.3':
1249 resolution: {integrity: sha512-4U/of6Gry5hal3CdZoMkrdfbSYza43ZcvlJoh4pJ1UCm16Vpegmw3MwmbrZHjg8wiFWe+f5WnX/WkgQpuuZnHw==}
1250
1237 - '@stylistic/eslint-plugin@5.2.2':
1238 - resolution: {integrity: sha512-bE2DUjruqXlHYP3Q2Gpqiuj2bHq7/88FnuaS0FjeGGLCy+X6a07bGVuwtiOYnPSLHR6jmx5Bwdv+j7l8H+G97A==}
1251 + '@stylistic/eslint-plugin@5.3.1':
1252 + resolution: {integrity: sha512-Ykums1VYonM0TgkD0VteVq9mrlO2FhF48MDJnPyv3MktIB2ydtuhlO0AfWm7xnW1kyf5bjOqA6xc7JjviuVTxg==}
1253 engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
1254 peerDependencies:
1255 eslint: '>=9.0.0'
@@ -1249,8 +1263,8 @@ packages:
1263 resolution: {integrity: sha512-/69XMRCDoam2HgC4ldHIaDgeQf1ViHIsa0Ld4uWgiXtZ+E24DWHe/9Ib6kbNiZ7WRIdlVokUDR1Fg0kjIpkfbw==}
1264 engines: {node: '>= 0.8.0'}
1265
1252 - '@svgdotjs/svg.js@3.2.4':
1253 - resolution: {integrity: sha512-BjJ/7vWNowlX3Z8O4ywT58DqbNRyYlkk6Yz/D13aB7hGmfQTvGX4Tkgtm/ApYlu9M7lCQi15xUEidqMUmdMYwg==}
1266 + '@svgdotjs/svg.js@3.2.5':
1267 + resolution: {integrity: sha512-/VNHWYhNu+BS7ktbYoVGrCmsXDh+chFMaONMwGNdIBcFHrWqk2jY8fNyr3DLdtQUIalvkPfM554ZSFa3dm3nxQ==}
1268
1269 '@svgdotjs/svg.resize.js@2.0.5':
1270 resolution: {integrity: sha512-4heRW4B1QrJeENfi7326lUPYBCevj78FJs8kfeDxn5st0IYPIRXoTtOSYvTzFWgaWWXd3YCDE6ao4fmv91RthA==}
@@ -1265,65 +1279,65 @@ packages:
1279 peerDependencies:
1280 '@svgdotjs/svg.js': ^3.2.4
1281
1268 - '@tailwindcss/node@4.1.11':
1269 - resolution: {integrity: sha512-yzhzuGRmv5QyU9qLNg4GTlYI6STedBWRE7NjxP45CsFYYq9taI0zJXZBMqIC/c8fViNLhmrbpSFS57EoxUmD6Q==}
1282 + '@tailwindcss/node@4.1.13':
1283 + resolution: {integrity: sha512-eq3ouolC1oEFOAvOMOBAmfCIqZBJuvWvvYWh5h5iOYfe1HFC6+GZ6EIL0JdM3/niGRJmnrOc+8gl9/HGUaaptw==}
1284
1271 - '@tailwindcss/oxide-android-arm64@4.1.11':
1272 - resolution: {integrity: sha512-3IfFuATVRUMZZprEIx9OGDjG3Ou3jG4xQzNTvjDoKmU9JdmoCohQJ83MYd0GPnQIu89YoJqvMM0G3uqLRFtetg==}
1285 + '@tailwindcss/oxide-android-arm64@4.1.13':
1286 + resolution: {integrity: sha512-BrpTrVYyejbgGo57yc8ieE+D6VT9GOgnNdmh5Sac6+t0m+v+sKQevpFVpwX3pBrM2qKrQwJ0c5eDbtjouY/+ew==}
1287 engines: {node: '>= 10'}
1288 cpu: [arm64]
1289 os: [android]
1290
1277 - '@tailwindcss/oxide-darwin-arm64@4.1.11':
1278 - resolution: {integrity: sha512-ESgStEOEsyg8J5YcMb1xl8WFOXfeBmrhAwGsFxxB2CxY9evy63+AtpbDLAyRkJnxLy2WsD1qF13E97uQyP1lfQ==}
1291 + '@tailwindcss/oxide-darwin-arm64@4.1.13':
1292 + resolution: {integrity: sha512-YP+Jksc4U0KHcu76UhRDHq9bx4qtBftp9ShK/7UGfq0wpaP96YVnnjFnj3ZFrUAjc5iECzODl/Ts0AN7ZPOANQ==}
1293 engines: {node: '>= 10'}
1294 cpu: [arm64]
1295 os: [darwin]
1296
1283 - '@tailwindcss/oxide-darwin-x64@4.1.11':
1284 - resolution: {integrity: sha512-EgnK8kRchgmgzG6jE10UQNaH9Mwi2n+yw1jWmof9Vyg2lpKNX2ioe7CJdf9M5f8V9uaQxInenZkOxnTVL3fhAw==}
1297 + '@tailwindcss/oxide-darwin-x64@4.1.13':
1298 + resolution: {integrity: sha512-aAJ3bbwrn/PQHDxCto9sxwQfT30PzyYJFG0u/BWZGeVXi5Hx6uuUOQEI2Fa43qvmUjTRQNZnGqe9t0Zntexeuw==}
1299 engines: {node: '>= 10'}
1300 cpu: [x64]
1301 os: [darwin]
1302
1289 - '@tailwindcss/oxide-freebsd-x64@4.1.11':
1290 - resolution: {integrity: sha512-xdqKtbpHs7pQhIKmqVpxStnY1skuNh4CtbcyOHeX1YBE0hArj2romsFGb6yUmzkq/6M24nkxDqU8GYrKrz+UcA==}
1303 + '@tailwindcss/oxide-freebsd-x64@4.1.13':
1304 + resolution: {integrity: sha512-Wt8KvASHwSXhKE/dJLCCWcTSVmBj3xhVhp/aF3RpAhGeZ3sVo7+NTfgiN8Vey/Fi8prRClDs6/f0KXPDTZE6nQ==}
1305 engines: {node: '>= 10'}
1306 cpu: [x64]
1307 os: [freebsd]
1308
1295 - '@tailwindcss/oxide-linux-arm-gnueabihf@4.1.11':
1296 - resolution: {integrity: sha512-ryHQK2eyDYYMwB5wZL46uoxz2zzDZsFBwfjssgB7pzytAeCCa6glsiJGjhTEddq/4OsIjsLNMAiMlHNYnkEEeg==}
1309 + '@tailwindcss/oxide-linux-arm-gnueabihf@4.1.13':
1310 + resolution: {integrity: sha512-mbVbcAsW3Gkm2MGwA93eLtWrwajz91aXZCNSkGTx/R5eb6KpKD5q8Ueckkh9YNboU8RH7jiv+ol/I7ZyQ9H7Bw==}
1311 engines: {node: '>= 10'}
1312 cpu: [arm]
1313 os: [linux]
1314
1301 - '@tailwindcss/oxide-linux-arm64-gnu@4.1.11':
1302 - resolution: {integrity: sha512-mYwqheq4BXF83j/w75ewkPJmPZIqqP1nhoghS9D57CLjsh3Nfq0m4ftTotRYtGnZd3eCztgbSPJ9QhfC91gDZQ==}
1315 + '@tailwindcss/oxide-linux-arm64-gnu@4.1.13':
1316 + resolution: {integrity: sha512-wdtfkmpXiwej/yoAkrCP2DNzRXCALq9NVLgLELgLim1QpSfhQM5+ZxQQF8fkOiEpuNoKLp4nKZ6RC4kmeFH0HQ==}
1317 engines: {node: '>= 10'}
1318 cpu: [arm64]
1319 os: [linux]
1320
1307 - '@tailwindcss/oxide-linux-arm64-musl@4.1.11':
1308 - resolution: {integrity: sha512-m/NVRFNGlEHJrNVk3O6I9ggVuNjXHIPoD6bqay/pubtYC9QIdAMpS+cswZQPBLvVvEF6GtSNONbDkZrjWZXYNQ==}
1321 + '@tailwindcss/oxide-linux-arm64-musl@4.1.13':
1322 + resolution: {integrity: sha512-hZQrmtLdhyqzXHB7mkXfq0IYbxegaqTmfa1p9MBj72WPoDD3oNOh1Lnxf6xZLY9C3OV6qiCYkO1i/LrzEdW2mg==}
1323 engines: {node: '>= 10'}
1324 cpu: [arm64]
1325 os: [linux]
1326
1313 - '@tailwindcss/oxide-linux-x64-gnu@4.1.11':
1314 - resolution: {integrity: sha512-YW6sblI7xukSD2TdbbaeQVDysIm/UPJtObHJHKxDEcW2exAtY47j52f8jZXkqE1krdnkhCMGqP3dbniu1Te2Fg==}
1327 + '@tailwindcss/oxide-linux-x64-gnu@4.1.13':
1328 + resolution: {integrity: sha512-uaZTYWxSXyMWDJZNY1Ul7XkJTCBRFZ5Fo6wtjrgBKzZLoJNrG+WderJwAjPzuNZOnmdrVg260DKwXCFtJ/hWRQ==}
1329 engines: {node: '>= 10'}
1330 cpu: [x64]
1331 os: [linux]
1332
1319 - '@tailwindcss/oxide-linux-x64-musl@4.1.11':
1320 - resolution: {integrity: sha512-e3C/RRhGunWYNC3aSF7exsQkdXzQ/M+aYuZHKnw4U7KQwTJotnWsGOIVih0s2qQzmEzOFIJ3+xt7iq67K/p56Q==}
1333 + '@tailwindcss/oxide-linux-x64-musl@4.1.13':
1334 + resolution: {integrity: sha512-oXiPj5mi4Hdn50v5RdnuuIms0PVPI/EG4fxAfFiIKQh5TgQgX7oSuDWntHW7WNIi/yVLAiS+CRGW4RkoGSSgVQ==}
1335 engines: {node: '>= 10'}
1336 cpu: [x64]
1337 os: [linux]
1338
1325 - '@tailwindcss/oxide-wasm32-wasi@4.1.11':
1326 - resolution: {integrity: sha512-Xo1+/GU0JEN/C/dvcammKHzeM6NqKovG+6921MR6oadee5XPBaKOumrJCXvopJ/Qb5TH7LX/UAywbqrP4lax0g==}
1339 + '@tailwindcss/oxide-wasm32-wasi@4.1.13':
1340 + resolution: {integrity: sha512-+LC2nNtPovtrDwBc/nqnIKYh/W2+R69FA0hgoeOn64BdCX522u19ryLh3Vf3F8W49XBcMIxSe665kwy21FkhvA==}
1341 engines: {node: '>=14.0.0'}
1342 cpu: [wasm32]
1343 bundledDependencies:
@@ -1334,24 +1348,24 @@ packages:
1348 - '@emnapi/wasi-threads'
1349 - tslib
1350
1337 - '@tailwindcss/oxide-win32-arm64-msvc@4.1.11':
1338 - resolution: {integrity: sha512-UgKYx5PwEKrac3GPNPf6HVMNhUIGuUh4wlDFR2jYYdkX6pL/rn73zTq/4pzUm8fOjAn5L8zDeHp9iXmUGOXZ+w==}
1351 + '@tailwindcss/oxide-win32-arm64-msvc@4.1.13':
1352 + resolution: {integrity: sha512-dziTNeQXtoQ2KBXmrjCxsuPk3F3CQ/yb7ZNZNA+UkNTeiTGgfeh+gH5Pi7mRncVgcPD2xgHvkFCh/MhZWSgyQg==}
1353 engines: {node: '>= 10'}
1354 cpu: [arm64]
1355 os: [win32]
1356
1343 - '@tailwindcss/oxide-win32-x64-msvc@4.1.11':
1344 - resolution: {integrity: sha512-YfHoggn1j0LK7wR82TOucWc5LDCguHnoS879idHekmmiR7g9HUtMw9MI0NHatS28u/Xlkfi9w5RJWgz2Dl+5Qg==}
1357 + '@tailwindcss/oxide-win32-x64-msvc@4.1.13':
1358 + resolution: {integrity: sha512-3+LKesjXydTkHk5zXX01b5KMzLV1xl2mcktBJkje7rhFUpUlYJy7IMOLqjIRQncLTa1WZZiFY/foAeB5nmaiTw==}
1359 engines: {node: '>= 10'}
1360 cpu: [x64]
1361 os: [win32]
1362
1349 - '@tailwindcss/oxide@4.1.11':
1350 - resolution: {integrity: sha512-Q69XzrtAhuyfHo+5/HMgr1lAiPP/G40OMFAnws7xcFEYqcypZmdW8eGXaOUIeOl1dzPJBPENXgbjsOyhg2nkrg==}
1363 + '@tailwindcss/oxide@4.1.13':
1364 + resolution: {integrity: sha512-CPgsM1IpGRa880sMbYmG1s4xhAy3xEt1QULgTJGQmZUeNgXFR7s1YxYygmJyBGtou4SyEosGAGEeYqY7R53bIA==}
1365 engines: {node: '>= 10'}
1366
1353 - '@tailwindcss/vite@4.1.11':
1354 - resolution: {integrity: sha512-RHYhrR3hku0MJFRV+fN2gNbDNEh3dwKvY8XJvTxCSXeMOsCRSr+uKvDWQcbizrHgjML6ZmTE5OwMrl5wKcujCw==}
1367 + '@tailwindcss/vite@4.1.13':
1368 + resolution: {integrity: sha512-0PmqLQ010N58SbMTJ7BVJ4I2xopiQn/5i6nlb4JmxzQf8zcS5+m2Cv6tqh+sfDwtIdjoEnOvwsGQ1hkUi8QEHQ==}
1369 peerDependencies:
1370 vite: ^5.2.0 || ^6 || ^7
1371
@@ -1425,8 +1439,8 @@ packages:
1439 '@types/ms@2.1.0':
1440 resolution: {integrity: sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==}
1441
1428 - '@types/node@24.1.0':
1429 - resolution: {integrity: sha512-ut5FthK5moxFKH2T1CUOC6ctR67rQRvvHdFLCD2Ql6KXmMuCrjsSsRI9UsLCm9M18BMwClv4pn327UvB7eeO1w==}
1442 + '@types/node@24.5.2':
1443 + resolution: {integrity: sha512-FYxk1I7wPv3K2XBaoyH2cTnocQEu8AOZ60hPbsyukMPLv5/5qr7V1i8PLHdl6Zf87I+xZXFvPCXYjiTFq+YSDQ==}
1444
1445 '@types/parse-json@4.0.2':
1446 resolution: {integrity: sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==}
@@ -1434,8 +1448,8 @@ packages:
1448 '@types/sinonjs__fake-timers@8.1.1':
1449 resolution: {integrity: sha512-0kSuKjAS0TrGLJ0M/+8MaFkGsQhZpB6pxOmvS3K8FYI72K//YmdfoW9X2qPsAKh1mkwxGD5zib9s1FIFed6E8g==}
1450
1437 - '@types/sizzle@2.3.9':
1438 - resolution: {integrity: sha512-xzLEyKB50yqCUPUJkIsrVvoWNfFUbIZI+RspLWt8u+tIW/BetMBZtgV2LY/2o+tYH8dRvQ+eoPf3NdhQCcLE2w==}
1451 + '@types/sizzle@2.3.10':
1452 + resolution: {integrity: sha512-TC0dmN0K8YcWEAEfiPi5gJP14eJe30TTGjkvek3iM/1NdHHsdCA/Td6GvNndMOo/iSnIsZ4HuuhrYPDAmbxzww==}
1453
1454 '@types/tern@0.23.9':
1455 resolution: {integrity: sha512-ypzHFE/wBzh+BlH6rrBgS5I/Z7RD21pGhZ2rltb/+ZrVM1awdZwjx7hE5XfuYgHWk9uvV5HLZN3SloevCAp3Bw==}
@@ -1446,8 +1460,8 @@ packages:
1460 '@types/unist@3.0.3':
1461 resolution: {integrity: sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==}
1462
1449 - '@types/validator@13.15.2':
1450 - resolution: {integrity: sha512-y7pa/oEJJ4iGYBxOpfAKn5b9+xuihvzDVnC/OSvlVnGxVg0pOqmjiMafiJ1KVNQEaPZf9HsEp5icEwGg8uIe5Q==}
1463 + '@types/validator@13.15.3':
1464 + resolution: {integrity: sha512-7bcUmDyS6PN3EuD9SlGGOxM77F8WLVsrwkxyWxKnxzmXoequ6c7741QBrANq6htVRGOITJ7z72mTP6Z4XyuG+Q==}
1465
1466 '@types/web-bluetooth@0.0.21':
1467 resolution: {integrity: sha512-oIQLCGWtcFZy2JW77j9k8nHzAOpqMHLQejDA48XXMWH6tjCQHz5RCFz1bzsmROyL6PUm+LLnUiI4BCn221inxA==}
@@ -1455,70 +1469,70 @@ packages:
1469 '@types/yauzl@2.10.3':
1470 resolution: {integrity: sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q==}
1471
1458 - '@typescript-eslint/eslint-plugin@8.38.0':
1459 - resolution: {integrity: sha512-CPoznzpuAnIOl4nhj4tRr4gIPj5AfKgkiJmGQDaq+fQnRJTYlcBjbX3wbciGmpoPf8DREufuPRe1tNMZnGdanA==}
1472 + '@typescript-eslint/eslint-plugin@8.44.0':
1473 + resolution: {integrity: sha512-EGDAOGX+uwwekcS0iyxVDmRV9HX6FLSM5kzrAToLTsr9OWCIKG/y3lQheCq18yZ5Xh78rRKJiEpP0ZaCs4ryOQ==}
1474 engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
1475 peerDependencies:
1462 - '@typescript-eslint/parser': ^8.38.0
1476 + '@typescript-eslint/parser': ^8.44.0
1477 eslint: ^8.57.0 || ^9.0.0
1464 - typescript: '>=4.8.4 <5.9.0'
1478 + typescript: '>=4.8.4 <6.0.0'
1479
1466 - '@typescript-eslint/parser@8.38.0':
1467 - resolution: {integrity: sha512-Zhy8HCvBUEfBECzIl1PKqF4p11+d0aUJS1GeUiuqK9WmOug8YCmC4h4bjyBvMyAMI9sbRczmrYL5lKg/YMbrcQ==}
1480 + '@typescript-eslint/parser@8.44.0':
1481 + resolution: {integrity: sha512-VGMpFQGUQWYT9LfnPcX8ouFojyrZ/2w3K5BucvxL/spdNehccKhB4jUyB1yBCXpr2XFm0jkECxgrpXBW2ipoAw==}
1482 engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
1483 peerDependencies:
1484 eslint: ^8.57.0 || ^9.0.0
1471 - typescript: '>=4.8.4 <5.9.0'
1485 + typescript: '>=4.8.4 <6.0.0'
1486
1473 - '@typescript-eslint/project-service@8.38.0':
1474 - resolution: {integrity: sha512-dbK7Jvqcb8c9QfH01YB6pORpqX1mn5gDZc9n63Ak/+jD67oWXn3Gs0M6vddAN+eDXBCS5EmNWzbSxsn9SzFWWg==}
1487 + '@typescript-eslint/project-service@8.44.0':
1488 + resolution: {integrity: sha512-ZeaGNraRsq10GuEohKTo4295Z/SuGcSq2LzfGlqiuEvfArzo/VRrT0ZaJsVPuKZ55lVbNk8U6FcL+ZMH8CoyVA==}
1489 engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
1490 peerDependencies:
1477 - typescript: '>=4.8.4 <5.9.0'
1491 + typescript: '>=4.8.4 <6.0.0'
1492
1479 - '@typescript-eslint/scope-manager@8.38.0':
1480 - resolution: {integrity: sha512-WJw3AVlFFcdT9Ri1xs/lg8LwDqgekWXWhH3iAF+1ZM+QPd7oxQ6jvtW/JPwzAScxitILUIFs0/AnQ/UWHzbATQ==}
1493 + '@typescript-eslint/scope-manager@8.44.0':
1494 + resolution: {integrity: sha512-87Jv3E+al8wpD+rIdVJm/ItDBe/Im09zXIjFoipOjr5gHUhJmTzfFLuTJ/nPTMc2Srsroy4IBXwcTCHyRR7KzA==}
1495 engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
1496
1483 - '@typescript-eslint/tsconfig-utils@8.38.0':
1484 - resolution: {integrity: sha512-Lum9RtSE3EroKk/bYns+sPOodqb2Fv50XOl/gMviMKNvanETUuUcC9ObRbzrJ4VSd2JalPqgSAavwrPiPvnAiQ==}
1497 + '@typescript-eslint/tsconfig-utils@8.44.0':
1498 + resolution: {integrity: sha512-x5Y0+AuEPqAInc6yd0n5DAcvtoQ/vyaGwuX5HE9n6qAefk1GaedqrLQF8kQGylLUb9pnZyLf+iEiL9fr8APDtQ==}
1499 engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
1500 peerDependencies:
1487 - typescript: '>=4.8.4 <5.9.0'
1501 + typescript: '>=4.8.4 <6.0.0'
1502
1489 - '@typescript-eslint/type-utils@8.38.0':
1490 - resolution: {integrity: sha512-c7jAvGEZVf0ao2z+nnz8BUaHZD09Agbh+DY7qvBQqLiz8uJzRgVPj5YvOh8I8uEiH8oIUGIfHzMwUcGVco/SJg==}
1503 + '@typescript-eslint/type-utils@8.44.0':
1504 + resolution: {integrity: sha512-9cwsoSxJ8Sak67Be/hD2RNt/fsqmWnNE1iHohG8lxqLSNY8xNfyY7wloo5zpW3Nu9hxVgURevqfcH6vvKCt6yg==}
1505 engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
1506 peerDependencies:
1507 eslint: ^8.57.0 || ^9.0.0
1494 - typescript: '>=4.8.4 <5.9.0'
1508 + typescript: '>=4.8.4 <6.0.0'
1509
1496 - '@typescript-eslint/types@8.38.0':
1497 - resolution: {integrity: sha512-wzkUfX3plUqij4YwWaJyqhiPE5UCRVlFpKn1oCRn2O1bJ592XxWJj8ROQ3JD5MYXLORW84063z3tZTb/cs4Tyw==}
1510 + '@typescript-eslint/types@8.44.0':
1511 + resolution: {integrity: sha512-ZSl2efn44VsYM0MfDQe68RKzBz75NPgLQXuGypmym6QVOWL5kegTZuZ02xRAT9T+onqvM6T8CdQk0OwYMB6ZvA==}
1512 engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
1513
1500 - '@typescript-eslint/typescript-estree@8.38.0':
1501 - resolution: {integrity: sha512-fooELKcAKzxux6fA6pxOflpNS0jc+nOQEEOipXFNjSlBS6fqrJOVY/whSn70SScHrcJ2LDsxWrneFoWYSVfqhQ==}
1514 + '@typescript-eslint/typescript-estree@8.44.0':
1515 + resolution: {integrity: sha512-lqNj6SgnGcQZwL4/SBJ3xdPEfcBuhCG8zdcwCPgYcmiPLgokiNDKlbPzCwEwu7m279J/lBYWtDYL+87OEfn8Jw==}
1516 engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
1517 peerDependencies:
1504 - typescript: '>=4.8.4 <5.9.0'
1518 + typescript: '>=4.8.4 <6.0.0'
1519
1506 - '@typescript-eslint/utils@8.38.0':
1507 - resolution: {integrity: sha512-hHcMA86Hgt+ijJlrD8fX0j1j8w4C92zue/8LOPAFioIno+W0+L7KqE8QZKCcPGc/92Vs9x36w/4MPTJhqXdyvg==}
1520 + '@typescript-eslint/utils@8.44.0':
1521 + resolution: {integrity: sha512-nktOlVcg3ALo0mYlV+L7sWUD58KG4CMj1rb2HUVOO4aL3K/6wcD+NERqd0rrA5Vg06b42YhF6cFxeixsp9Riqg==}
1522 engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
1523 peerDependencies:
1524 eslint: ^8.57.0 || ^9.0.0
1511 - typescript: '>=4.8.4 <5.9.0'
1525 + typescript: '>=4.8.4 <6.0.0'
1526
1513 - '@typescript-eslint/visitor-keys@8.38.0':
1514 - resolution: {integrity: sha512-pWrTcoFNWuwHlA9CvlfSsGWs14JxfN1TH25zM5L7o0pRLhsoZkDnTsXfQRJBEWJoV5DL0jf+Z+sxiud+K0mq1g==}
1527 + '@typescript-eslint/visitor-keys@8.44.0':
1528 + resolution: {integrity: sha512-zaz9u8EJ4GBmnehlrpoKvj/E3dNbuQ7q0ucyZImm3cLqJ8INTc970B1qEqDX/Rzq65r3TvVTN7kHWPBoyW7DWw==}
1529 engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
1530
1531 '@ungap/structured-clone@1.3.0':
1532 resolution: {integrity: sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==}
1533
1520 - '@vitejs/plugin-vue-jsx@5.0.1':
1521 - resolution: {integrity: sha512-X7qmQMXbdDh+sfHUttXokPD0cjPkMFoae7SgbkF9vi3idGUKmxLcnU2Ug49FHwiKXebfzQRIm5yK3sfCJzNBbg==}
1534 + '@vitejs/plugin-vue-jsx@5.1.1':
1535 + resolution: {integrity: sha512-uQkfxzlF8SGHJJVH966lFTdjM/lGcwJGzwAHpVqAPDD/QcsqoUGa+q31ox1BrUfi+FLP2ChVp7uLXE3DkHyDdQ==}
1536 engines: {node: ^20.19.0 || >=22.12.0}
1537 peerDependencies:
1538 vite: ^5.0.0 || ^6.0.0 || ^7.0.0
@@ -1531,8 +1545,8 @@ packages:
1545 vite: ^5.0.0 || ^6.0.0 || ^7.0.0
1546 vue: ^3.2.25
1547
1534 - '@vitest/eslint-plugin@1.3.4':
1535 - resolution: {integrity: sha512-EOg8d0jn3BAiKnR55WkFxmxfWA3nmzrbIIuOXyTe6A72duryNgyU+bdBEauA97Aab3ho9kLmAwgPX63Ckj4QEg==}
1548 + '@vitest/eslint-plugin@1.3.12':
1549 + resolution: {integrity: sha512-cSEyUYGj8j8SLqKrzN7BlfsJ3wG67eRT25819PXuyoSBogLXiyagdKx4MHWHV1zv+EEuyMXsEKkBEKzXpxyBrg==}
1550 peerDependencies:
1551 eslint: '>= 8.57.0'
1552 typescript: '>= 5.0.0'
@@ -1572,42 +1586,42 @@ packages:
1586 '@vitest/utils@3.2.4':
1587 resolution: {integrity: sha512-fB2V0JFrQSMsCo9HiSq3Ezpdv4iYaXRG1Sx8edX3MwxfyNn83mKiGzOcH+Fkxt4MHxr3y42fQi1oeAInqgX2QA==}
1588
1575 - '@volar/language-core@2.4.20':
1576 - resolution: {integrity: sha512-dRDF1G33xaAIDqR6+mXUIjXYdu9vzSxlMGfMEwBxQsfY/JMUEXSpLTR057oTKlUQ2nIvCmP9k94A8h8z2VrNSA==}
1589 + '@volar/language-core@2.4.23':
1590 + resolution: {integrity: sha512-hEEd5ET/oSmBC6pi1j6NaNYRWoAiDhINbT8rmwtINugR39loROSlufGdYMF9TaKGfz+ViGs1Idi3mAhnuPcoGQ==}
1591
1578 - '@volar/source-map@2.4.20':
1579 - resolution: {integrity: sha512-mVjmFQH8mC+nUaVwmbxoYUy8cww+abaO8dWzqPUjilsavjxH0jCJ3Mp8HFuHsdewZs2c+SP+EO7hCd8Z92whJg==}
1592 + '@volar/source-map@2.4.23':
1593 + resolution: {integrity: sha512-Z1Uc8IB57Lm6k7q6KIDu/p+JWtf3xsXJqAX/5r18hYOTpJyBn0KXUR8oTJ4WFYOcDzWC9n3IflGgHowx6U6z9Q==}
1594
1581 - '@volar/typescript@2.4.20':
1582 - resolution: {integrity: sha512-Oc4DczPwQyXcVbd+5RsNEqX6ia0+w3p+klwdZQ6ZKhFjWoBP9PCPQYlKYRi/tDemWphW93P/Vv13vcE9I9D2GQ==}
1595 + '@volar/typescript@2.4.23':
1596 + resolution: {integrity: sha512-lAB5zJghWxVPqfcStmAP1ZqQacMpe90UrP5RJ3arDyrhy4aCUQqmxPPLB2PWDKugvylmO41ljK7vZ+t6INMTag==}
1597
1584 - '@vue/babel-helper-vue-transform-on@1.4.0':
1585 - resolution: {integrity: sha512-mCokbouEQ/ocRce/FpKCRItGo+013tHg7tixg3DUNS+6bmIchPt66012kBMm476vyEIJPafrvOf4E5OYj3shSw==}
1598 + '@vue/babel-helper-vue-transform-on@1.5.0':
1599 + resolution: {integrity: sha512-0dAYkerNhhHutHZ34JtTl2czVQHUNWv6xEbkdF5W+Yrv5pCWsqjeORdOgbtW2I9gWlt+wBmVn+ttqN9ZxR5tzA==}
1600
1587 - '@vue/babel-plugin-jsx@1.4.0':
1588 - resolution: {integrity: sha512-9zAHmwgMWlaN6qRKdrg1uKsBKHvnUU+Py+MOCTuYZBoZsopa90Di10QRjB+YPnVss0BZbG/H5XFwJY1fTxJWhA==}
1601 + '@vue/babel-plugin-jsx@1.5.0':
1602 + resolution: {integrity: sha512-mneBhw1oOqCd2247O0Yw/mRwC9jIGACAJUlawkmMBiNmL4dGA2eMzuNZVNqOUfYTa6vqmND4CtOPzmEEEqLKFw==}
1603 peerDependencies:
1604 '@babel/core': ^7.0.0-0
1605 peerDependenciesMeta:
1606 '@babel/core':
1607 optional: true
1608
1595 - '@vue/babel-plugin-resolve-type@1.4.0':
1596 - resolution: {integrity: sha512-4xqDRRbQQEWHQyjlYSgZsWj44KfiF6D+ktCuXyZ8EnVDYV3pztmXJDf1HveAjUAXxAnR8daCQT51RneWWxtTyQ==}
1609 + '@vue/babel-plugin-resolve-type@1.5.0':
1610 + resolution: {integrity: sha512-Wm/60o+53JwJODm4Knz47dxJnLDJ9FnKnGZJbUUf8nQRAtt6P+undLUAVU3Ha33LxOJe6IPoifRQ6F/0RrU31w==}
1611 peerDependencies:
1612 '@babel/core': ^7.0.0-0
1613
1600 - '@vue/compiler-core@3.5.18':
1601 - resolution: {integrity: sha512-3slwjQrrV1TO8MoXgy3aynDQ7lslj5UqDxuHnrzHtpON5CBinhWjJETciPngpin/T3OuW3tXUf86tEurusnztw==}
1614 + '@vue/compiler-core@3.5.21':
1615 + resolution: {integrity: sha512-8i+LZ0vf6ZgII5Z9XmUvrCyEzocvWT+TeR2VBUVlzIH6Tyv57E20mPZ1bCS+tbejgUgmjrEh7q/0F0bibskAmw==}
1616
1603 - '@vue/compiler-dom@3.5.18':
1604 - resolution: {integrity: sha512-RMbU6NTU70++B1JyVJbNbeFkK+A+Q7y9XKE2EM4NLGm2WFR8x9MbAtWxPPLdm0wUkuZv9trpwfSlL6tjdIa1+A==}
1617 + '@vue/compiler-dom@3.5.21':
1618 + resolution: {integrity: sha512-jNtbu/u97wiyEBJlJ9kmdw7tAr5Vy0Aj5CgQmo+6pxWNQhXZDPsRr1UWPN4v3Zf82s2H3kF51IbzZ4jMWAgPlQ==}
1619
1606 - '@vue/compiler-sfc@3.5.18':
1607 - resolution: {integrity: sha512-5aBjvGqsWs+MoxswZPoTB9nSDb3dhd1x30xrrltKujlCxo48j8HGDNj3QPhF4VIS0VQDUrA1xUfp2hEa+FNyXA==}
1620 + '@vue/compiler-sfc@3.5.21':
1621 + resolution: {integrity: sha512-SXlyk6I5eUGBd2v8Ie7tF6ADHE9kCR6mBEuPyH1nUZ0h6Xx6nZI29i12sJKQmzbDyr2tUHMhhTt51Z6blbkTTQ==}
1622
1609 - '@vue/compiler-ssr@3.5.18':
1610 - resolution: {integrity: sha512-xM16Ak7rSWHkM3m22NlmcdIM+K4BMyFARAfV9hYFl+SFuRzrZ3uGMNW05kA5pmeMa0X9X963Kgou7ufdbpOP9g==}
1623 + '@vue/compiler-ssr@3.5.21':
1624 + resolution: {integrity: sha512-vKQ5olH5edFZdf5ZrlEgSO1j1DMA4u23TVK5XR1uMhvwnYvVdDF0nHXJUblL/GvzlShQbjhZZ2uvYmDlAbgo9w==}
1625
1626 '@vue/compiler-vue2@2.7.16':
1627 resolution: {integrity: sha512-qYC3Psj9S/mfu9uVi5WvNZIzq+xnXMhOwbTFKKDD7b1lhpnn71jXSFdTQ+WsIEk0ONCd7VV2IMm7ONl6tbQ86A==}
@@ -1618,47 +1632,47 @@ packages:
1632 '@vue/devtools-api@7.7.7':
1633 resolution: {integrity: sha512-lwOnNBH2e7x1fIIbVT7yF5D+YWhqELm55/4ZKf45R9T8r9dE2AIOy8HKjfqzGsoTHFbWbr337O4E0A0QADnjBg==}
1634
1621 - '@vue/devtools-core@8.0.0':
1622 - resolution: {integrity: sha512-5bPtF0jAFnaGs4C/4+3vGRR5U+cf6Y8UWK0nJflutEDGepHxl5L9JRaPdHQYCUgrzUaf4cY4waNBEEGXrfcs3A==}
1635 + '@vue/devtools-core@8.0.2':
1636 + resolution: {integrity: sha512-V7eKTTHoS6KfK8PSGMLZMhGv/9yNDrmv6Qc3r71QILulnzPnqK2frsTyx3e2MrhdUZnENPEm6hcb4z0GZOqNhw==}
1637 peerDependencies:
1638 vue: ^3.0.0
1639
1640 '@vue/devtools-kit@7.7.7':
1641 resolution: {integrity: sha512-wgoZtxcTta65cnZ1Q6MbAfePVFxfM+gq0saaeytoph7nEa7yMXoi6sCPy4ufO111B9msnw0VOWjPEFCXuAKRHA==}
1642
1629 - '@vue/devtools-kit@8.0.0':
1630 - resolution: {integrity: sha512-b11OeQODkE0bctdT0RhL684pEV2DPXJ80bjpywVCbFn1PxuL3bmMPDoJKjbMnnoWbrnUYXYzFfmMWBZAMhORkQ==}
1643 + '@vue/devtools-kit@8.0.2':
1644 + resolution: {integrity: sha512-yjZKdEmhJzQqbOh4KFBfTOQjDPMrjjBNCnHBvnTGJX+YLAqoUtY2J+cg7BE+EA8KUv8LprECq04ts75wCoIGWA==}
1645
1646 '@vue/devtools-shared@7.7.7':
1647 resolution: {integrity: sha512-+udSj47aRl5aKb0memBvcUG9koarqnxNM5yjuREvqwK6T3ap4mn3Zqqc17QrBFTqSMjr3HK1cvStEZpMDpfdyw==}
1648
1635 - '@vue/devtools-shared@8.0.0':
1636 - resolution: {integrity: sha512-jrKnbjshQCiOAJanoeJjTU7WaCg0Dz2BUal6SaR6VM/P3hiFdX5Q6Pxl73ZMnrhCxNK9nAg5hvvRGqs+6dtU1g==}
1649 + '@vue/devtools-shared@8.0.2':
1650 + resolution: {integrity: sha512-mLU0QVdy5Lp40PMGSixDw/Kbd6v5dkQXltd2r+mdVQV7iUog2NlZuLxFZApFZ/mObUBDhoCpf0T3zF2FWWdeHw==}
1651
1638 - '@vue/language-core@3.0.4':
1639 - resolution: {integrity: sha512-BvueED4LfBCSNH66eeUQk37MQCb7hjdezzGgxniM0LbriW53AJIyLorgshAtStmjfsAuOCcTl/c1b+nz/ye8xQ==}
1652 + '@vue/language-core@3.0.7':
1653 + resolution: {integrity: sha512-0sqqyqJ0Gn33JH3TdIsZLCZZ8Gr4kwlg8iYOnOrDDkJKSjFurlQY/bEFQx5zs7SX2C/bjMkmPYq/NiyY1fTOkw==}
1654 peerDependencies:
1655 typescript: '*'
1656 peerDependenciesMeta:
1657 typescript:
1658 optional: true
1659
1646 - '@vue/reactivity@3.5.18':
1647 - resolution: {integrity: sha512-x0vPO5Imw+3sChLM5Y+B6G1zPjwdOri9e8V21NnTnlEvkxatHEH5B5KEAJcjuzQ7BsjGrKtfzuQ5eQwXh8HXBg==}
1660 + '@vue/reactivity@3.5.21':
1661 + resolution: {integrity: sha512-3ah7sa+Cwr9iiYEERt9JfZKPw4A2UlbY8RbbnH2mGCE8NwHkhmlZt2VsH0oDA3P08X3jJd29ohBDtX+TbD9AsA==}
1662
1649 - '@vue/runtime-core@3.5.18':
1650 - resolution: {integrity: sha512-DUpHa1HpeOQEt6+3nheUfqVXRog2kivkXHUhoqJiKR33SO4x+a5uNOMkV487WPerQkL0vUuRvq/7JhRgLW3S+w==}
1663 + '@vue/runtime-core@3.5.21':
1664 + resolution: {integrity: sha512-+DplQlRS4MXfIf9gfD1BOJpk5RSyGgGXD/R+cumhe8jdjUcq/qlxDawQlSI8hCKupBlvM+3eS1se5xW+SuNAwA==}
1665
1652 - '@vue/runtime-dom@3.5.18':
1653 - resolution: {integrity: sha512-YwDj71iV05j4RnzZnZtGaXwPoUWeRsqinblgVJwR8XTXYZ9D5PbahHQgsbmzUvCWNF6x7siQ89HgnX5eWkr3mw==}
1666 + '@vue/runtime-dom@3.5.21':
1667 + resolution: {integrity: sha512-3M2DZsOFwM5qI15wrMmNF5RJe1+ARijt2HM3TbzBbPSuBHOQpoidE+Pa+XEaVN+czbHf81ETRoG1ltztP2em8w==}
1668
1655 - '@vue/server-renderer@3.5.18':
1656 - resolution: {integrity: sha512-PvIHLUoWgSbDG7zLHqSqaCoZvHi6NNmfVFOqO+OnwvqMz/tqQr3FuGWS8ufluNddk7ZLBJYMrjcw1c6XzR12mA==}
1669 + '@vue/server-renderer@3.5.21':
1670 + resolution: {integrity: sha512-qr8AqgD3DJPJcGvLcJKQo2tAc8OnXRcfxhOJCPF+fcfn5bBGz7VCcO7t+qETOPxpWK1mgysXvVT/j+xWaHeMWA==}
1671 peerDependencies:
1658 - vue: 3.5.18
1672 + vue: 3.5.21
1673
1660 - '@vue/shared@3.5.18':
1661 - resolution: {integrity: sha512-cZy8Dq+uuIXbxCZpuLd2GJdeSO/lIzIspC2WtkqIpje5QyFbvLaI5wZtdUjLHjGZrlVX6GilejatWwVYYRc8tA==}
1674 + '@vue/shared@3.5.21':
1675 + resolution: {integrity: sha512-+2k1EQpnYuVuu3N7atWyG3/xoFWIVJZq4Mz8XNOdScFI0etES75fbny/oU4lKWk/577P1zmg0ioYvpGEDZ3DLw==}
1676
1677 '@vue/test-utils@2.4.6':
1678 resolution: {integrity: sha512-FMxEjOpYNYiFe0GkaHsnJPXFHxQ6m4t8vI/ElPGpMWxZKpmRvQ33OIrvRXemy6yha03RxhOlQuy+gZMC3CQSow==}
@@ -1674,21 +1688,21 @@ packages:
1688 vue:
1689 optional: true
1690
1677 - '@vueuse/core@13.6.0':
1678 - resolution: {integrity: sha512-DJbD5fV86muVmBgS9QQPddVX7d9hWYswzlf4bIyUD2dj8GC46R1uNClZhVAmsdVts4xb2jwp1PbpuiA50Qee1A==}
1691 + '@vueuse/core@13.9.0':
1692 + resolution: {integrity: sha512-ts3regBQyURfCE2BcytLqzm8+MmLlo5Ln/KLoxDVcsZ2gzIwVNnQpQOL/UKV8alUqjSZOlpFZcRNsLRqj+OzyA==}
1693 peerDependencies:
1694 vue: ^3.5.0
1695
1682 - '@vueuse/metadata@13.6.0':
1683 - resolution: {integrity: sha512-rnIH7JvU7NjrpexTsl2Iwv0V0yAx9cw7+clymjKuLSXG0QMcLD0LDgdNmXic+qL0SGvgSVPEpM9IDO/wqo1vkQ==}
1696 + '@vueuse/metadata@13.9.0':
1697 + resolution: {integrity: sha512-1AFRvuiGphfF7yWixZa0KwjYH8ulyjDCC0aFgrGRz8+P4kvDFSdXLVfTk5xAN9wEuD1J6z4/myMoYbnHoX07zg==}
1698
1699 '@vueuse/motion@3.0.3':
1700 resolution: {integrity: sha512-4B+ITsxCI9cojikvrpaJcLXyq0spj3sdlzXjzesWdMRd99hhtFI6OJ/1JsqwtF73YooLe0hUn/xDR6qCtmn5GQ==}
1701 peerDependencies:
1702 vue: '>=3.0.0'
1703
1690 - '@vueuse/shared@13.6.0':
1691 - resolution: {integrity: sha512-pDykCSoS2T3fsQrYqf9SyF0QXWHmcGPQ+qiOVjlYSzlWd9dgppB2bFSM1GgKKkt7uzn0BBMV3IbJsUfHG2+BCg==}
1704 + '@vueuse/shared@13.9.0':
1705 + resolution: {integrity: sha512-e89uuTLMh0U5cZ9iDpEI2senqPGfbPRTHM/0AaQkcxnpqjkZqDYP8rpfm7edOz8s+pOCOROEy1PIveSW8+fL5g==}
1706 peerDependencies:
1707 vue: ^3.5.0
1708
@@ -1720,12 +1734,12 @@ packages:
1734 ajv@6.12.6:
1735 resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==}
1736
1723 - algoliasearch@5.35.0:
1724 - resolution: {integrity: sha512-Y+moNhsqgLmvJdgTsO4GZNgsaDWv8AOGAaPeIeHKlDn/XunoAqYbA+XNpBd1dW8GOXAUDyxC9Rxc7AV4kpFcIg==}
1737 + algoliasearch@5.37.0:
1738 + resolution: {integrity: sha512-y7gau/ZOQDqoInTQp0IwTOjkrHc4Aq4R8JgpmCleFwiLl+PbN2DMWoDUWZnrK8AhNJwT++dn28Bt4NZYNLAmuA==}
1739 engines: {node: '>= 14.0.0'}
1740
1727 - alien-signals@2.0.5:
1728 - resolution: {integrity: sha512-PdJB6+06nUNAClInE3Dweq7/2xVAYM64vvvS1IHVHSJmgeOtEdrAGyp7Z2oJtYm0B342/Exd2NT0uMJaThcjLQ==}
1741 + alien-signals@2.0.7:
1742 + resolution: {integrity: sha512-wE7y3jmYeb0+h6mr5BOovuqhFv22O/MV9j5p0ndJsa7z1zJNPGQ4ph5pQk/kTTCWRC3xsA4SmtwmkzQO+7NCNg==}
1743
1744 ansi-colors@4.1.3:
1745 resolution: {integrity: sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==}
@@ -1739,24 +1753,24 @@ packages:
1753 resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==}
1754 engines: {node: '>=8'}
1755
1742 - ansi-regex@6.1.0:
1743 - resolution: {integrity: sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==}
1756 + ansi-regex@6.2.2:
1757 + resolution: {integrity: sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==}
1758 engines: {node: '>=12'}
1759
1760 ansi-styles@4.3.0:
1761 resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==}
1762 engines: {node: '>=8'}
1763
1750 - ansi-styles@6.2.1:
1751 - resolution: {integrity: sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==}
1764 + ansi-styles@6.2.3:
1765 + resolution: {integrity: sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==}
1766 engines: {node: '>=12'}
1767
1768 ansis@4.1.0:
1769 resolution: {integrity: sha512-BGcItUBWSMRgOCe+SVZJ+S7yTRG0eGt9cXAHev72yuGcY23hnLA7Bky5L/xLyPINoSN95geovfBkqoTlNZYa7w==}
1770 engines: {node: '>=14'}
1771
1758 - apexcharts@5.3.2:
1759 - resolution: {integrity: sha512-qeKIS5CS/n+CoNNwbd69G4rRc3we5/8g5Mu46OumqH7pCMSN4MhI2lr0xDY/ktBlFh94YuM9psc9WX6EWtC90g==}
1772 + apexcharts@5.3.5:
1773 + resolution: {integrity: sha512-I04DY/WBZbJgJD2uixeV5EzyiL+J5LgKQXEu8rctqAwyRmKv44aDVeofJoLdTJe3ao4r2KEQfCgtVzXn6pqirg==}
1774
1775 arch@2.2.0:
1776 resolution: {integrity: sha512-Of/R0wqp83cgHozfIYLbBMnej79U/SVGOOyuB3VVFv1NRM/PSFMK12x9KVtiYzJqmnU5WR2qp0Z5rHb7sWGnFQ==}
@@ -1804,9 +1818,6 @@ packages:
1818 async-validator@4.2.5:
1819 resolution: {integrity: sha512-7HhHjtERjqlNbZtqNqy2rckN/SpOOlmDliet+lP7k+eKZEjPk3DgyeU9lIXLdeLz0uBbbVp+9Qdow9wJWgwwfg==}
1820
1807 - async@3.2.6:
1808 - resolution: {integrity: sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==}
1809 -
1821 asynckit@0.4.0:
1822 resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==}
1823
@@ -1820,8 +1831,8 @@ packages:
1831 aws4@1.13.2:
1832 resolution: {integrity: sha512-lHe62zvbTB5eEABUVi/AwVh0ZKY9rMMDhmm+eeyuuUQbQ3+J+fONVQOZyj+DdrvD4BY33uYniyRJ4UJIaSKAfw==}
1833
1823 - axios@1.11.0:
1824 - resolution: {integrity: sha512-1Lx3WLFQWm3ooKDYZD1eXmoGO9fxYQjrycfHFC8P0sCfQVXyROp0p9PFWBehewBOdCwHc+f/b8I0fMto5eSfwA==}
1834 + axios@1.12.2:
1835 + resolution: {integrity: sha512-vMJzPewAlRyOgxV2dU0Cuz2O8zzzx9VYtbJOaBgXFeLc4IV/Eg50n4LowmehOOR61S8ZMpc2K5Sa7g6A4jfkUw==}
1836
1837 balanced-match@1.0.2:
1838 resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==}
@@ -1829,9 +1840,16 @@ packages:
1840 base64-js@1.5.1:
1841 resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==}
1842
1843 + baseline-browser-mapping@2.8.6:
1844 + resolution: {integrity: sha512-wrH5NNqren/QMtKUEEJf7z86YjfqW/2uw3IL3/xpqZUC95SSVIFXYQeeGjL6FT/X68IROu6RMehZQS5foy2BXw==}
1845 + hasBin: true
1846 +
1847 bcrypt-pbkdf@1.0.2:
1848 resolution: {integrity: sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==}
1849
1850 + bidi-js@1.0.3:
1851 + resolution: {integrity: sha512-RKshQI1R3YQ+n9YJz2QQ147P66ELpa1FQEg20Dk8oW9t2KgLbpDLLp9aGZ7y8WHSshDknG0bknqGw5/tyCs5tw==}
1852 +
1853 birpc@2.5.0:
1854 resolution: {integrity: sha512-VSWO/W6nNQdyP520F1mhf+Lc2f8pjGQOtoHHm7Ze8Go1kX7akpVIrtTa0fn+HB0QJEDVacl6aO08YE0PgXfdnQ==}
1855
@@ -1854,8 +1872,8 @@ packages:
1872 resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==}
1873 engines: {node: '>=8'}
1874
1857 - browserslist@4.25.1:
1858 - resolution: {integrity: sha512-KGj0KoOMXLpSNkkEI6Z6mShmQy0bc1I+T7K9N81k4WWMrfz+6fQ6es80B/YLAeRoKvjYE1YSHHOW1qe9xIVzHw==}
1875 + browserslist@4.26.2:
1876 + resolution: {integrity: sha512-ECFzp6uFOSB+dcZ5BK/IBaGWssbSYBHvuMeMt3MMFyhI0Z8SqGgEkBLARgpRH3hutIgPVsALcMwbDrJqPxQ65A==}
1877 engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7}
1878 hasBin: true
1879
@@ -1877,8 +1895,8 @@ packages:
1895 resolution: {integrity: sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==}
1896 engines: {node: '>= 0.8'}
1897
1880 - c12@3.2.0:
1881 - resolution: {integrity: sha512-ixkEtbYafL56E6HiFuonMm1ZjoKtIo7TH68/uiEq4DAwv9NcUX2nJ95F8TrbMeNjqIkZpruo3ojXQJ+MGG5gcQ==}
1898 + c12@3.3.0:
1899 + resolution: {integrity: sha512-K9ZkuyeJQeqLEyqldbYLG3wjqwpw4BVaAqvmxq3GYKK0b1A/yYQdIcJxkzAOWcNVWhJpRXAPfZFueekiY/L8Dw==}
1900 peerDependencies:
1901 magicast: ^0.3.5
1902 peerDependenciesMeta:
@@ -1912,8 +1930,8 @@ packages:
1930 resolution: {integrity: sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==}
1931 engines: {node: '>=10'}
1932
1915 - caniuse-lite@1.0.30001731:
1916 - resolution: {integrity: sha512-lDdp2/wrOmTRWuoB5DpfNkC0rJDU8DqRa6nYL6HK6sytw70QMopt/NIc/9SM7ylItlBWfACXk0tEn37UWM/+mg==}
1933 + caniuse-lite@1.0.30001743:
1934 + resolution: {integrity: sha512-e6Ojr7RV14Un7dz6ASD0aZDmQPT/A+eZU+nuTNfjqmRrmkmQlnTNWH0SKmqagx9PeW87UVqapSurtAXifmtdmw==}
1935
1936 caseless@0.12.0:
1937 resolution: {integrity: sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==}
@@ -1921,8 +1939,8 @@ packages:
1939 ccount@2.0.1:
1940 resolution: {integrity: sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==}
1941
1924 - chai@5.2.1:
1925 - resolution: {integrity: sha512-5nFxhUrX0PqtyogoYOA8IPswy5sZFTOsBFl/9bNsmDLgsxYTzSZQJDPppDnZPTQbzSEm0hqGjWPzRemQCYbD6A==}
1942 + chai@5.3.3:
1943 + resolution: {integrity: sha512-4zNhdJD/iOjSH0A05ea+Ke6MU5mmpQcbQsSOkgdaUMJ9zTlDTD/GYlwohmIE2u0gaxHYiVHEn1Fw9mZ/ktJWgw==}
1944 engines: {node: '>=18'}
1945
1946 chalk@4.1.2:
@@ -2064,8 +2082,8 @@ packages:
2082 resolution: {integrity: sha512-yCEafptTtb4bk7GLEQoM8KVJpxAfdBJYaXyzQEgQQQgYrZiDp8SJmGKlYza6CYjEDNstAdNdKA3UuoULlEbS6w==}
2083 engines: {node: '>=12.13'}
2084
2067 - core-js-compat@3.44.0:
2068 - resolution: {integrity: sha512-JepmAj2zfl6ogy34qfWtcE7nHKAJnKsQFRn++scjVS2bZFllwptzw61BZcZFYBPpUznLfAvh0LGhxKppk04ClA==}
2085 + core-js-compat@3.45.1:
2086 + resolution: {integrity: sha512-tqTt5T4PzsMIZ430XGviK4vzYSoeNJ6CXODi6c/voxOT6IZqBht5/EKaSNnYiEjjRYxjVz7DQIsOsY0XNi8PIA==}
2087
2088 core-util-is@1.0.2:
2089 resolution: {integrity: sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==}
@@ -2098,6 +2116,10 @@ packages:
2116 resolution: {integrity: sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw==}
2117 engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0}
2118
2119 + css-tree@3.1.0:
2120 + resolution: {integrity: sha512-0eW44TGN5SQXU1mWSkKwFstI/22X2bG1nYzZTYMAWjylYURhse752YgbE4Cx46AC+bAvI+/dYTPRk1LqSUnu6w==}
2121 + engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0}
2122 +
2123 css-what@6.2.2:
2124 resolution: {integrity: sha512-u/O3vwbptzhMs3L1fQE82ZSLHQQfto5gyZzwteVIEyeaY5Fc7R4dapF/BvRoSYFeqfBk4m0V1Vafq5Pjv25wvA==}
2125 engines: {node: '>= 6'}
@@ -2111,9 +2133,9 @@ packages:
2133 resolution: {integrity: sha512-0LrrStPOdJj+SPCCrGhzryycLjwcgUSHBtxNA8aIDxf0GLsRh1cKYhB00Gd1lDOS4yGH69+SNn13+TWbVHETFQ==}
2134 engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0, npm: '>=7.0.0'}
2135
2114 - cssstyle@4.6.0:
2115 - resolution: {integrity: sha512-2z+rWdzbbSZv6/rhtvzvqeZQHrBaqgogqt85sqFNbabZOuFbCVFb8kPeEtZjiKkbrm395irpNKiYeFeLiQnFPg==}
2116 - engines: {node: '>=18'}
2136 + cssstyle@5.3.0:
2137 + resolution: {integrity: sha512-RveJPnk3m7aarYQ2bJ6iw+Urh55S6FzUiqtBq+TihnTDP4cI8y/TYDqGOyqgnG1J1a6BxJXZsV9JFSTulm9Z7g==}
2138 + engines: {node: '>=20'}
2139
2140 csstype@3.0.11:
2141 resolution: {integrity: sha512-sa6P2wJ+CAbgyy4KFssIb/JNMLxFvKF1pCYCSXS8ZMuqZnMsrxqI2E5sPyoTpxoPU/gVZMzr2zjOfg8GIZOMsw==}
@@ -2121,18 +2143,18 @@ packages:
2143 csstype@3.1.3:
2144 resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==}
2145
2124 - cypress@14.5.3:
2125 - resolution: {integrity: sha512-syLwKjDeMg77FRRx68bytLdlqHXDT4yBVh0/PPkcgesChYDjUZbwxLqMXuryYKzAyJsPsQHUDW1YU74/IYEUIA==}
2126 - engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0}
2146 + cypress@15.2.0:
2147 + resolution: {integrity: sha512-J4ehSzOSb58SkXyldCe9y/oZ8ep8Bl6+q9kDUjnkqNqc2ZKzDq5KSbhIc2lHFAFR5Jtj10oNqr9JRAZbr8DA8A==}
2148 + engines: {node: ^20.0.0 || ^22.0.0 || >=24.0.0}
2149 hasBin: true
2150
2151 dashdash@1.14.1:
2152 resolution: {integrity: sha512-jRFi8UDGo6j+odZiEpjazZaWqEal3w/basFjQHQEwVtZJGDpxbH1MeYluwCS8Xq5wmLJooDlMgvVarmWfGM44g==}
2153 engines: {node: '>=0.10'}
2154
2133 - data-urls@5.0.0:
2134 - resolution: {integrity: sha512-ZYP5VBHshaDAiVZxjbRVcFJpc+4xGgT0bK3vzy1HLN8jTO975HEbuYzZJcHoQEY5K1a0z8YayJkyVETa08eNTg==}
2135 - engines: {node: '>=18'}
2155 + data-urls@6.0.0:
2156 + resolution: {integrity: sha512-BnBS08aLUM+DKamupXs3w2tJJoqU+AkaE/+6vQxi/G/DPmIZFJJp9Dkb1kM03AZx8ADehDUZgsNxju3mPXZYIA==}
2157 + engines: {node: '>=20'}
2158
2159 date-fns-tz@3.2.0:
2160 resolution: {integrity: sha512-sg8HqoTEulcbbbVXeg84u5UnlsQa8GS5QXMqjjYIhS4abEVVKIUwe0/l/UhrZdKaL/W5eWZNlbTeEIiOXTcsBQ==}
@@ -2142,8 +2164,8 @@ packages:
2164 date-fns@3.6.0:
2165 resolution: {integrity: sha512-fRHTG8g/Gif+kSh50gaGEdToemgfj74aRX3swtiouboip5JDLAyDE9F11nHMIcvOaXeOC6D7SpNhi7uFyB7Uww==}
2166
2145 - dayjs@1.11.13:
2146 - resolution: {integrity: sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg==}
2167 + dayjs@1.11.18:
2168 + resolution: {integrity: sha512-zFBQ7WFRvVRhKcWoUh+ZA1g2HVgUbsZm9sbddh8EC5iv93sui8DVVz1Npvz+r6meo9VKfa8NyLWBsQK1VvIKPA==}
2169
2170 de-indent@1.0.2:
2171 resolution: {integrity: sha512-e/1zu3xH5MQryN2zdVaF0OrdNLUbvWxzMbi+iNA6Bky7l1RoP8a2fIbRocyHclXt/arDrrR6lL3TqFD9pMQTsg==}
@@ -2159,8 +2181,8 @@ packages:
2181 supports-color:
2182 optional: true
2183
2162 - debug@4.4.1:
2163 - resolution: {integrity: sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==}
2184 + debug@4.4.3:
2185 + resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==}
2186 engines: {node: '>=6.0'}
2187 peerDependencies:
2188 supports-color: '*'
@@ -2231,8 +2253,8 @@ packages:
2253 engines: {node: '>=0.10'}
2254 hasBin: true
2255
2234 - detect-libc@2.0.4:
2235 - resolution: {integrity: sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA==}
2256 + detect-libc@2.1.0:
2257 + resolution: {integrity: sha512-vEtk+OcP7VBRtQZ1EJ3bdgzSfBjgnEalLTp5zjJrS+2Z1w2KZly4SBdac/WDU3hhsNAZ9E8SC96ME4Ey8MZ7cg==}
2258 engines: {node: '>=8'}
2259
2260 detect-touch-device@1.1.6:
@@ -2254,8 +2276,8 @@ packages:
2276 domutils@3.2.2:
2277 resolution: {integrity: sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==}
2278
2257 - dotenv@17.2.1:
2258 - resolution: {integrity: sha512-kQhDYKZecqnM0fCnzI5eIv5L4cAe/iRI+HqMbO/hbRdTAeXDG+M9FjipUxNfbARuEg4iHIbhnhs78BCHNbSxEQ==}
2279 + dotenv@17.2.2:
2280 + resolution: {integrity: sha512-Sf2LSQP+bOlhKWWyhFsn0UsfdK/kCWRv1iuA2gXAwt3dyNabr6QSj00I2V10pidqz69soatm9ZwZvpQMTIOd5Q==}
2281 engines: {node: '>=12'}
2282
2283 dunder-proto@1.0.1:
@@ -2282,8 +2304,8 @@ packages:
2304 engines: {node: '>=14'}
2305 hasBin: true
2306
2285 - electron-to-chromium@1.5.192:
2286 - resolution: {integrity: sha512-rP8Ez0w7UNw/9j5eSXCe10o1g/8B1P5SM90PCCMVkIRQn2R0LEHWz4Eh9RnxkniuDe1W0cTSOB3MLlkTGDcuCg==}
2307 + electron-to-chromium@1.5.222:
2308 + resolution: {integrity: sha512-gA7psSwSwQRE60CEoLz6JBCQPIxNeuzB2nL8vE03GK/OHxlvykbLyeiumQy1iH5C2f3YbRAZpGCMT12a/9ih9w==}
2309
2310 emoji-regex@8.0.0:
2311 resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==}
@@ -2291,11 +2313,15 @@ packages:
2313 emoji-regex@9.2.2:
2314 resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==}
2315
2316 + empathic@2.0.0:
2317 + resolution: {integrity: sha512-i6UzDscO/XfAcNYD75CfICkmfLedpyPDdozrLMmQc5ORaQcdMoc21OnlEylMIqI7U8eniKrPMxxtj8k0vhmJhA==}
2318 + engines: {node: '>=14'}
2319 +
2320 end-of-stream@1.4.5:
2321 resolution: {integrity: sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==}
2322
2297 - enhanced-resolve@5.18.2:
2298 - resolution: {integrity: sha512-6Jw4sE1maoRJo3q8MsSIn2onJFbLTOjY9hlx4DZXmOKvLRd1Ok2kXmAGXaafL2+ijsJZ1ClYbl/pmqr9+k4iUQ==}
2323 + enhanced-resolve@5.18.3:
2324 + resolution: {integrity: sha512-d4lC8xfavMeBjzGr2vECC3fsGXziXZQyJxD868h2M/mBI3PwAuODxAkLkq5HYuvrPYcUtiLzsTo8U3PgX3Ocww==}
2325 engines: {node: '>=10.13.0'}
2326
2327 enquirer@2.4.1:
@@ -2310,8 +2336,8 @@ packages:
2336 resolution: {integrity: sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==}
2337 engines: {node: '>=0.12'}
2338
2313 - error-ex@1.3.2:
2314 - resolution: {integrity: sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==}
2339 + error-ex@1.3.4:
2340 + resolution: {integrity: sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==}
2341
2342 error-stack-parser-es@1.0.5:
2343 resolution: {integrity: sha512-5qucVt2XcuGMcEGgWI7i+yZpmpByQ8J1lHhcL7PwqCwu9FPP3VUXzT4ltHe5i2z9dePwEHcDVOAfSnHsOlCXRA==}
@@ -2338,8 +2364,8 @@ packages:
2364 resolution: {integrity: sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==}
2365 engines: {node: '>= 0.4'}
2366
2341 - esbuild@0.25.8:
2342 - resolution: {integrity: sha512-vVC0USHGtMi8+R4Kz8rt6JhEWLxsv9Rnu/lGYbPR8u47B+DCBksq9JarW0zOO7bs37hyOK1l2/oqtbciutL5+Q==}
2367 + esbuild@0.25.10:
2368 + resolution: {integrity: sha512-9RiGKvCwaqxO2owP61uQ4BgNborAQskMR6QusfWzQqv7AZOg5oGehdY2pRJMTKuwxd1IDBP4rSbI5lHzU7SMsQ==}
2369 engines: {node: '>=18'}
2370 hasBin: true
2371
@@ -2421,8 +2447,8 @@ packages:
2447 typescript:
2448 optional: true
2449
2424 - eslint-plugin-jsdoc@51.4.1:
2425 - resolution: {integrity: sha512-y4CA9OkachG8v5nAtrwvcvjIbdcKgSyS6U//IfQr4FZFFyeBFwZFf/tfSsMr46mWDJgidZjBTqoCRlXywfFBMg==}
2450 + eslint-plugin-jsdoc@54.7.0:
2451 + resolution: {integrity: sha512-u5Na4he2+6kY1rWqxzbQaAwJL3/tDCuT5ElDRc5UJ9stOeQeQ5L1JJ1kRRu7ldYMlOHMCJLsY8Mg/Tu3ExdZiQ==}
2452 engines: {node: '>=20.11.0'}
2453 peerDependencies:
2454 eslint: ^7.0.0 || ^8.0.0 || ^9.0.0
@@ -2433,8 +2459,8 @@ packages:
2459 peerDependencies:
2460 eslint: '>=6.0.0'
2461
2436 - eslint-plugin-n@17.21.3:
2437 - resolution: {integrity: sha512-MtxYjDZhMQgsWRm/4xYLL0i2EhusWT7itDxlJ80l1NND2AL2Vi5Mvneqv/ikG9+zpran0VsVRXTEHrpLmUZRNw==}
2462 + eslint-plugin-n@17.23.1:
2463 + resolution: {integrity: sha512-68PealUpYoHOBh332JLLD9Sj7OQUDkFpmcfqt8R9sySfFSeuGJjMTJQvCRRB96zO3A/PELRLkPrzsHmzEFQQ5A==}
2464 engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
2465 peerDependencies:
2466 eslint: '>=8.23.0'
@@ -2449,13 +2475,13 @@ packages:
2475 peerDependencies:
2476 eslint: '>=8.45.0'
2477
2452 - eslint-plugin-pnpm@1.1.0:
2453 - resolution: {integrity: sha512-sL93w0muBtjnogzk/loDsxzMbmXQOLP5Blw3swLDBXZgfb+qQI73bPcUbjVR+ZL+K62vGJdErV+43i3r5DsZPg==}
2478 + eslint-plugin-pnpm@1.1.1:
2479 + resolution: {integrity: sha512-gNo+swrLCgvT8L6JX6hVmxuKeuStGK2l8IwVjDxmYIn+wP4SW/d0ORLKyUiYamsp+UxknQo3f2M1irrTpqahCw==}
2480 peerDependencies:
2481 eslint: ^9.0.0
2482
2457 - eslint-plugin-regexp@2.9.0:
2458 - resolution: {integrity: sha512-9WqJMnOq8VlE/cK+YAo9C9YHhkOtcEtEk9d12a+H7OSZFwlpI6stiHmYPGa2VE0QhTzodJyhlyprUaXDZLgHBw==}
2483 + eslint-plugin-regexp@2.10.0:
2484 + resolution: {integrity: sha512-ovzQT8ESVn5oOe5a7gIDPD5v9bCSjIFJu57sVPDqgPRXicQzOnYfFN21WoQBQF18vrhT5o7UMKFwJQVVjyJ0ng==}
2485 engines: {node: ^18 || >=20}
2486 peerDependencies:
2487 eslint: '>=8.44.0'
@@ -2466,14 +2492,14 @@ packages:
2492 peerDependencies:
2493 eslint: '>=6.0.0'
2494
2469 - eslint-plugin-unicorn@60.0.0:
2470 - resolution: {integrity: sha512-QUzTefvP8stfSXsqKQ+vBQSEsXIlAiCduS/V1Em+FKgL9c21U/IIm20/e3MFy1jyCf14tHAhqC1sX8OTy6VUCg==}
2495 + eslint-plugin-unicorn@61.0.2:
2496 + resolution: {integrity: sha512-zLihukvneYT7f74GNbVJXfWIiNQmkc/a9vYBTE4qPkQZswolWNdu+Wsp9sIXno1JOzdn6OUwLPd19ekXVkahRA==}
2497 engines: {node: ^20.10.0 || >=21.0.0}
2498 peerDependencies:
2499 eslint: '>=9.29.0'
2500
2475 - eslint-plugin-unused-imports@4.1.4:
2476 - resolution: {integrity: sha512-YptD6IzQjDardkl0POxnnRBhU1OEePMV0nd6siHaRBbd+lyh6NAhFEobiznKU7kTsSsDeSD62Pe7kAM1b7dAZQ==}
2501 + eslint-plugin-unused-imports@4.2.0:
2502 + resolution: {integrity: sha512-hLbJ2/wnjKq4kGA9AUaExVFIbNzyxYdVo49QZmKCnhk5pc9wcYRbfgLHvWJ8tnsdcseGhoUAddm9gn/lt+d74w==}
2503 peerDependencies:
2504 '@typescript-eslint/eslint-plugin': ^8.0.0-0 || ^7.0.0 || ^6.0.0 || ^5.0.0
2505 eslint: ^9.0.0 || ^8.0.0
@@ -2481,8 +2507,8 @@ packages:
2507 '@typescript-eslint/eslint-plugin':
2508 optional: true
2509
2484 - eslint-plugin-vue@10.3.0:
2485 - resolution: {integrity: sha512-A0u9snqjCfYaPnqqOaH6MBLVWDUIN4trXn8J3x67uDcXvR7X6Ut8p16N+nYhMCQ9Y7edg2BIRGzfyZsY0IdqoQ==}
2510 + eslint-plugin-vue@10.4.0:
2511 + resolution: {integrity: sha512-K6tP0dW8FJVZLQxa2S7LcE1lLw3X8VvB3t887Q6CLrFVxHYBXGANbXvwNzYIu6Ughx1bSJ5BDT0YB3ybPT39lw==}
2512 engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
2513 peerDependencies:
2514 '@typescript-eslint/parser': ^7.0.0 || ^8.0.0
@@ -2516,8 +2542,8 @@ packages:
2542 resolution: {integrity: sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==}
2543 engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
2544
2519 - eslint@9.32.0:
2520 - resolution: {integrity: sha512-LSehfdpgMeWcTZkWZVIJl+tkZ2nuSkyyB9C27MZqFWXuph7DvaowgcTvKqxvpLW1JZIk8PN7hFY3Rj9LQ7m7lg==}
2545 + eslint@9.35.0:
2546 + resolution: {integrity: sha512-QePbBFMJFjgmlE+cXAlbHZbHpdFVS2E/6vzCy7aKlebddvl1vadiC4JFV5u/wqTkNUwEV8WrQi257jf5f06hrg==}
2547 engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
2548 hasBin: true
2549 peerDependencies:
@@ -2635,8 +2661,9 @@ packages:
2661 fd-slicer@1.1.0:
2662 resolution: {integrity: sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==}
2663
2638 - fdir@6.4.6:
2639 - resolution: {integrity: sha512-hiFoqpyZcfNm1yc4u8oWCf9A2c4D3QjCrks3zmoVKVxpQRzmPNar1hUJcBG2RQHvEVGDN+Jm81ZheVLAQMK6+w==}
2664 + fdir@6.5.0:
2665 + resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==}
2666 + engines: {node: '>=12.0.0'}
2667 peerDependencies:
2668 picomatch: ^3 || ^4
2669 peerDependenciesMeta:
@@ -2685,8 +2712,8 @@ packages:
2712 resolution: {integrity: sha512-iuhWXuX07QwHMnJ1Irh4sD1bk/QFMHg8jVgWsjSAqoIqgIyJtRPnUNKyZAPXrw7pQkDvxb5AIz2KPihEoyVcqw==}
2713 engines: {node: '>=16'}
2714
2688 - follow-redirects@1.15.9:
2689 - resolution: {integrity: sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==}
2715 + follow-redirects@1.15.11:
2716 + resolution: {integrity: sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==}
2717 engines: {node: '>=4.0'}
2718 peerDependencies:
2719 debug: '*'
@@ -2715,8 +2742,8 @@ packages:
2742 from@0.1.7:
2743 resolution: {integrity: sha512-twe20eF1OxVxp/ML/kq2p1uc6KvFK/+vs8WjEbeKmV2He22MKm7YF2ANIt+EOqhJ5L3K/SuuPhk0hWQDjOM23g==}
2744
2718 - fs-extra@11.3.0:
2719 - resolution: {integrity: sha512-Z4XaCL6dUDHfP/jT25jJKMmtxvuwbkrD1vNSMFlo9lNLY2c5FHYSQgHPRZUjAB26TpDEoW9HCOgplrdbaPV/ew==}
2745 + fs-extra@11.3.2:
2746 + resolution: {integrity: sha512-Xr9F6z6up6Ws+NjzMCZc6WXg2YFRlrLP9NQDO3VQrWrfiojdhS56TzueT88ze0uBdCTwEIhQ3ptnmKeWGFAe0A==}
2747 engines: {node: '>=14.14'}
2748
2749 fs-extra@9.1.0:
@@ -2765,9 +2792,6 @@ packages:
2792 get-tsconfig@4.10.1:
2793 resolution: {integrity: sha512-auHyJ4AgMz7vgS8Hp3N6HXSmlMdUyhSUrfBF16w153rxtLIEOE+HGqaBppczZvnHLqQJfiHotCYpNhl0lUROFQ==}
2794
2768 - getos@3.2.1:
2769 - resolution: {integrity: sha512-U56CfOK17OKgTVqozZjUKNdkfEv6jk5WISBJ8SHoagjE6L69zOwl3Z+O8myjY9MEW3i2HPWQBt/LTbCgcC973Q==}
2770 -
2795 getpass@0.1.7:
2796 resolution: {integrity: sha512-0fzj9JxOLfJ+XGLhR8ze3unN0KZCgZwiSSDz168VERjK8Wl8kVSdcu2kspd4s4wtAa1y/qrVRiAA0WclVsu0ng==}
2797
@@ -2810,8 +2834,8 @@ packages:
2834 resolution: {integrity: sha512-7ACyT3wmyp3I61S4fG682L0VA2RGD9otkqGJIwNUMF1SWUombIIk+af1unuDYgMm082aHYwD+mzJvv9Iu8dsgg==}
2835 engines: {node: '>=18'}
2836
2813 - globals@16.3.0:
2814 - resolution: {integrity: sha512-bqWEnJ1Nt3neqx2q5SFfGS8r/ahumIakg3HcwtNlrVlwXIeNumWn/c7Pn/wKzGhf6SaW6H6uWXLqC30STCMchQ==}
2837 + globals@16.4.0:
2838 + resolution: {integrity: sha512-ob/2LcVVaVGCYN+r14cnwnoDPUufjiYgSqRhiFD0Q1iI4Odora5RE8Iv1D24hAz5oMophRGkGz+yuvQmmUMnMw==}
2839 engines: {node: '>=18'}
2840
2841 globrex@0.1.2:
@@ -2934,8 +2958,8 @@ packages:
2958 resolution: {integrity: sha512-7EyUlPFC0HOlBDpUFGfYstsU7XHxZJKAAMzCT8wZ0hMW7b+hG51LIKTDcsgtz8Pu6YC0HqRVbX+rVUtsGMUKvg==}
2959 engines: {node: '>=16.20'}
2960
2937 - import-meta-resolve@4.1.0:
2938 - resolution: {integrity: sha512-I6fiaX09Xivtk+THaMfAwnA3MVA5Big1WHF1Dfx9hFuvNIWpXnorlkzhcQf6ehrqQiiZECRt1poOAkPmer3ruw==}
2961 + import-meta-resolve@4.2.0:
2962 + resolution: {integrity: sha512-Iqv2fzaTQN28s/FwZAoFq0ZSs/7hMAHJVX+w8PZl3cY19Pxk6jFFalxQoIfW2826i/fDLXv8IiEZRIT0lDuWcg==}
2963
2964 imurmurhash@0.1.4:
2965 resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==}
@@ -3068,8 +3092,8 @@ packages:
3092 joi@17.13.3:
3093 resolution: {integrity: sha512-otDA4ldcIx+ZXsKHWmp0YizCweVRZG96J10b0FevjfuncLO1oX59THoAmHkNubYJ+9gWsYsp5k8v4ib6oDv1fA==}
3094
3071 - jose@6.0.12:
3072 - resolution: {integrity: sha512-T8xypXs8CpmiIi78k0E+Lk7T2zlK4zDyg+o1CZ4AkOHgDg98ogdP2BeZ61lTFKFyoEwJ9RgAgN+SdM3iPgNonQ==}
3095 + jose@6.1.0:
3096 + resolution: {integrity: sha512-TTQJyoEoKcC1lscpVDCSsVgYzUDg/0Bt3WE//WiTPK6uOCQC2KZS4MpugbMWt/zyjkopgZoXhZuCi00gLudfUA==}
3097
3098 js-beautify@1.15.4:
3099 resolution: {integrity: sha512-9/KXeZUKKJwqCXUdBxFJ3vPh467OCckSBmYDwSK/EtV090K+iMJ7zx2S3HLVDIWFQdqMIsZWbnaGiba18aWhaA==}
@@ -3104,9 +3128,17 @@ packages:
3128 resolution: {integrity: sha512-Hicd6JK5Njt2QB6XYFS7ok9e37O8AYk3jTcppG4YVQnYjOemymvTcmc7OWsmq/Qqj5TdRFO5/x/tIPmBeRtGHg==}
3129 engines: {node: '>=12.0.0'}
3130
3107 - jsdom@26.1.0:
3108 - resolution: {integrity: sha512-Cvc9WUhxSMEo4McES3P7oK3QaXldCfNWp7pl2NNeiIFlCoLr3kfq9kb1fxftiwk1FLV7CvpvDfonxtzUDeSOPg==}
3109 - engines: {node: '>=18'}
3131 + jsdoc-type-pratt-parser@4.8.0:
3132 + resolution: {integrity: sha512-iZ8Bdb84lWRuGHamRXFyML07r21pcwBrLkHEuHgEY5UbCouBwv7ECknDRKzsQIXMiqpPymqtIf8TC/shYKB5rw==}
3133 + engines: {node: '>=12.0.0'}
3134 +
3135 + jsdoc-type-pratt-parser@5.1.1:
3136 + resolution: {integrity: sha512-DYYlVP1fe4QBMh2xTIs20/YeTz2GYVbWAEZweHSZD+qQ/Cx2d5RShuhhsdk64eTjNq0FeVnteP/qVOgaywSRbg==}
3137 + engines: {node: '>=12.0.0'}
3138 +
3139 + jsdom@27.0.0:
3140 + resolution: {integrity: sha512-lIHeR1qlIRrIN5VMccd8tI2Sgw6ieYXSVktcSHaNe3Z5nE/tcPQYQWOq00wxMvYOsz+73eAkNenVvmPC6bba9A==}
3141 + engines: {node: '>=20'}
3142 peerDependencies:
3143 canvas: ^3.0.0
3144 peerDependenciesMeta:
@@ -3154,8 +3186,8 @@ packages:
3186 resolution: {integrity: sha512-WYDyuc/uFcGp6YtM2H0uKmUwieOuzeE/5YocFJLnLfclZ4inf3mRn8ZVy1s7Hxji7Jxm6Ss8gqpexD/GlKoGgg==}
3187 engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
3188
3157 - jsonfile@6.1.0:
3158 - resolution: {integrity: sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==}
3189 + jsonfile@6.2.0:
3190 + resolution: {integrity: sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==}
3191
3192 jsprim@2.0.2:
3193 resolution: {integrity: sha512-gqXddjPqQ6G40VdnI6T6yObEC+pDNvyP95wdQhkWkg7crHH3km5qP1FsOXEkzEQwnz6gz5qGTn1c2Y52wP3OyQ==}
@@ -3261,8 +3293,8 @@ packages:
3293 enquirer:
3294 optional: true
3295
3264 - local-pkg@1.1.1:
3265 - resolution: {integrity: sha512-WunYko2W1NcdfAFpuLUoucsgULmgDBRkdxHxWQ7mK0cQqwPiy8E1enjuRBrhLtZkB5iScJ1XIPdhVEFK8aOLSg==}
3296 + local-pkg@1.1.2:
3297 + resolution: {integrity: sha512-arhlxbFRmoQHl33a0Zkle/YWlmNwoyt6QNZEIJcqNbdrsix5Lvc4HyyI3EnwxTYlZYc32EbYrQ8SzEZ7dqgg9A==}
3298 engines: {node: '>=14'}
3299
3300 locate-path@6.0.0:
@@ -3292,12 +3324,16 @@ packages:
3324 longest-streak@3.1.0:
3325 resolution: {integrity: sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==}
3326
3295 - loupe@3.2.0:
3296 - resolution: {integrity: sha512-2NCfZcT5VGVNX9mSZIxLRkEAegDGBpuQZBy13desuHeVORmBDyAET4TkJr4SjqQy3A8JDofMN6LpkK8Xcm/dlw==}
3327 + loupe@3.2.1:
3328 + resolution: {integrity: sha512-CdzqowRJCeLU72bHvWqwRBBlLcMEtIvGrlvef74kMnV2AolS9Y8xUv1I0U/MNAWMhBlKIoyuEgoJ0t/bbwHbLQ==}
3329
3330 lru-cache@10.4.3:
3331 resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==}
3332
3333 + lru-cache@11.2.1:
3334 + resolution: {integrity: sha512-r8LA6i4LP4EeWOhqBaZZjDWwehd1xUJPCJd9Sv300H0ZmcUER4+JPh7bqqZeqs1o5pgtgvXm+d9UGrB5zZGDiQ==}
3335 + engines: {node: 20 || >=22}
3336 +
3337 lru-cache@5.1.1:
3338 resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==}
3339
@@ -3305,8 +3341,8 @@ packages:
3341 resolution: {integrity: sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==}
3342 hasBin: true
3343
3308 - magic-string@0.30.17:
3309 - resolution: {integrity: sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==}
3344 + magic-string@0.30.19:
3345 + resolution: {integrity: sha512-2N21sPY9Ws53PZvsEpVtNuSW+ScYbQdp4b9qUaL+9QkHUrGFKo56Lg9Emg5s9V/qrtNBmiR01sYhUOwu3H+VOw==}
3346
3347 map-stream@0.1.0:
3348 resolution: {integrity: sha512-CkYQrPYZfWnu/DAmVCpTSX/xHpKZ80eKh2lAkyA6AJTef6bW+6JpbQZN5rofum7da+SyN1bi5ctTm+lTfcCW3g==}
@@ -3367,6 +3403,9 @@ packages:
3403 mdn-data@2.0.30:
3404 resolution: {integrity: sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==}
3405
3406 + mdn-data@2.12.2:
3407 + resolution: {integrity: sha512-IEn+pegP1aManZuckezWCO+XZQDplx1366JoVhTpMpBB1sPey/SbveZQUosKiKiGYjg1wH4pMlNgXbCiYgihQA==}
3408 +
3409 mdurl@2.0.0:
3410 resolution: {integrity: sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==}
3411
@@ -3488,10 +3527,6 @@ packages:
3527 resolution: {integrity: sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==}
3528 engines: {node: '>=18'}
3529
3491 - min-indent@1.0.1:
3492 - resolution: {integrity: sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==}
3493 - engines: {node: '>=4'}
3494 -
3530 minimatch@3.1.2:
3531 resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==}
3532
@@ -3526,8 +3561,8 @@ packages:
3561 engines: {node: '>=10'}
3562 hasBin: true
3563
3529 - mlly@1.7.4:
3530 - resolution: {integrity: sha512-qmdSIPC4bDJXgZTCR7XosJiNKySV7O215tsPtDN9iEO/7q/76b/ijtgRu/+epFXSJhijtTCCGp3DWS549P3xKw==}
3564 + mlly@1.8.0:
3565 + resolution: {integrity: sha512-l8D9ODSRWLe2KHJSifWGwBqpTZXIXTeo8mlKjY+E2HAakaTeNpqAyBZ8GSqLzHgw4XmHmC8whvpjJNMbFZN7/g==}
3566
3567 mrmime@2.0.1:
3568 resolution: {integrity: sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ==}
@@ -3543,8 +3578,8 @@ packages:
3578 resolution: {integrity: sha512-ypMKuglUrZUD99Tk2bUQ+xNQj43lPEfAeX2o9cTteAmShXy2VHDJpuwu1o0xqoKCt9jLVAvwyFKdLTPXKAfJyA==}
3579 engines: {node: '>=10'}
3580
3546 - naive-ui@2.42.0:
3547 - resolution: {integrity: sha512-c7cXR2YgOjgtBadXHwiWL4Y0tpGLAI5W5QzzHksOi22iuHXoSGMAzdkVTGVPE/PM0MSGQ/JtUIzCx2Y0hU0vTQ==}
3581 + naive-ui@2.43.1:
3582 + resolution: {integrity: sha512-w52W0mOhdOGt4uucFSZmP0DI44PCsFyuxeLSs9aoUThfIuxms90MYjv46Qrr7xprjyJRw5RU6vYpCx4o9ind3A==}
3583 peerDependencies:
3584 vue: ^3.0.0
3585
@@ -3568,11 +3603,11 @@ packages:
3603 node-addon-api@7.1.1:
3604 resolution: {integrity: sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==}
3605
3571 - node-fetch-native@1.6.6:
3572 - resolution: {integrity: sha512-8Mc2HhqPdlIfedsuZoc3yioPuzp6b+L5jRCRY1QzuWZh2EGJVQrGppC6V6cF0bLdbW0+O2YpqCA25aF/1lvipQ==}
3606 + node-fetch-native@1.6.7:
3607 + resolution: {integrity: sha512-g9yhqoedzIUm0nTnTqAQvueMPVOuIY16bqgAJJC8XOOubYFNwz6IER9qs0Gq2Xd0+CecCKFjtdDTMA4u4xG06Q==}
3608
3574 - node-releases@2.0.19:
3575 - resolution: {integrity: sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==}
3609 + node-releases@2.0.21:
3610 + resolution: {integrity: sha512-5b0pgg78U3hwXkCM8Z9b2FJdPZlr9Psr9V2gQPESdGHqbntyFJKFW4r5TeWGFzafGY3hzs1JC62VEQMbl1JFkw==}
3611
3612 nopt@7.2.1:
3613 resolution: {integrity: sha512-taM24ViiimT/XntxbPyJQzCG+p4EKOpgD3mxFwW38mGjVUrfERQOeY4EDHjdnptttfHuHQXFx+lTP08Q+mLa/w==}
@@ -3599,11 +3634,8 @@ packages:
3634 nth-check@2.1.1:
3635 resolution: {integrity: sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==}
3636
3602 - nwsapi@2.2.21:
3603 - resolution: {integrity: sha512-o6nIY3qwiSXl7/LuOU0Dmuctd34Yay0yeuZRLFmDPrrdHpXKFndPj3hM+YEPVHYC5fx2otBx4Ilc/gyYSAUaIA==}
3604 -
3605 - nypm@0.6.1:
3606 - resolution: {integrity: sha512-hlacBiRiv1k9hZFiphPUkfSQ/ZfQzZDzC+8z0wL3lvDAOUu/2NnChkKuMoMjNur/9OpKuz2QsIeiPVN0xM5Q0w==}
3637 + nypm@0.6.2:
3638 + resolution: {integrity: sha512-7eM+hpOtrKrBDCh7Ypu2lJ9Z7PNZBdi/8AT3AX8xoCj43BBVHD0hPSTEvMtkMpfs8FCqBGhxB+uToIQimA111g==}
3639 engines: {node: ^14.16.0 || >=16.10.0}
3640 hasBin: true
3641
@@ -3742,6 +3774,9 @@ packages:
3774 perfect-debounce@1.0.0:
3775 resolution: {integrity: sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA==}
3776
3777 + perfect-debounce@2.0.0:
3778 + resolution: {integrity: sha512-fkEH/OBiKrqqI/yIgjR92lMfs2K8105zt/VT6+7eTjNwisrsh47CeIED9z58zI7DfKdH3uHAn25ziRZn3kgAow==}
3779 +
3780 performance-now@2.1.0:
3781 resolution: {integrity: sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==}
3782
@@ -3765,8 +3800,8 @@ packages:
3800 resolution: {integrity: sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==}
3801 engines: {node: '>=0.10.0'}
3802
3768 - pinia-plugin-persistedstate@4.4.1:
3769 - resolution: {integrity: sha512-lmuMPpXla2zJKjxEq34e1E9P9jxkWEhcVwwioCCE0izG45kkTOvQfCzvwhW3i38cvnaWC7T1eRdkd15Re59ldw==}
3803 + pinia-plugin-persistedstate@4.5.0:
3804 + resolution: {integrity: sha512-QTkP1xJVyCdr2I2p3AKUZM84/e+IS+HktRxKGAIuDzkyaKKV48mQcYkJFVVDuvTxlI5j6X3oZObpqoVB8JnWpw==}
3805 peerDependencies:
3806 '@nuxt/kit': '>=3.0.0'
3807 '@pinia/nuxt': '>=0.10.0'
@@ -3791,8 +3826,8 @@ packages:
3826 pkg-types@1.3.1:
3827 resolution: {integrity: sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==}
3828
3794 - pkg-types@2.2.0:
3795 - resolution: {integrity: sha512-2SM/GZGAEkPp3KWORxQZns4M+WSeXbC2HEvmOIJe3Cmiv6ieAJvdVhDldtHqM5J1Y7MrR1XhkBT/rMlhh9FdqQ==}
3829 + pkg-types@2.3.0:
3830 + resolution: {integrity: sha512-SIqCzDRg0s9npO5XQ3tNZioRY1uK06lA41ynBC1YmFTmnY6FjUjVt6s4LoADmwoig1qqD0oK8h1p/8mlMx8Oig==}
3831
3832 please-upgrade-node@3.2.0:
3833 resolution: {integrity: sha512-gQR3WpIgNIKwBMVLkpMUeR3e1/E1y42bqDQZfql+kDeXd8COYfM8PQA4X6y7a8u9Ua9FHmsrrmirW2vHs45hWg==}
@@ -3801,11 +3836,8 @@ packages:
3836 resolution: {integrity: sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==}
3837 engines: {node: '>=4'}
3838
3804 - pnpm-workspace-yaml@0.3.1:
3805 - resolution: {integrity: sha512-3nW5RLmREmZ8Pm8MbPsO2RM+99RRjYd25ynj3NV0cFsN7CcEl4sDFzgoFmSyduFwxFQ2Qbu3y2UdCh6HlyUOeA==}
3806 -
3807 - pnpm-workspace-yaml@1.1.0:
3808 - resolution: {integrity: sha512-OWUzBxtitpyUV0fBYYwLAfWxn3mSzVbVB7cwgNaHvTTU9P0V2QHjyaY5i7f1hEiT9VeKsNH1Skfhe2E3lx/zhA==}
3839 + pnpm-workspace-yaml@1.1.1:
3840 + resolution: {integrity: sha512-nGBB7h3Ped3g9dBrR6d3YNwXCKYsEg8K9J3GMmSrwGEXq3RHeGW44/B4MZW51p4FRMnyxJzTY5feSBbUjRhIHQ==}
3841
3842 popmotion@11.0.5:
3843 resolution: {integrity: sha512-la8gPM1WYeFznb/JqF4GiTkRRPZsfaj2+kCxqQgr2MJylMmIKUwBfWW8Wa5fml/8gmtlD5yI01MP1QCZPWmppA==}
@@ -3892,8 +3924,8 @@ packages:
3924 resolution: {integrity: sha512-FFw039TmrBqFK8ma/7OL3sDz/VytdtJr044/QUJtH0wK9lb9jLq9tJyIxUwtQJHwar2BqtiA4iCWSwo9JLkzFg==}
3925 engines: {node: '>=6'}
3926
3895 - pretty-ms@9.2.0:
3896 - resolution: {integrity: sha512-4yf0QO/sllf/1zbZWYnvWw3NxCQwLXKzIj0G849LSufP15BXKM0rbD2Z3wVnkMfjdn/CB0Dpp444gYAACdsplg==}
3927 + pretty-ms@9.3.0:
3928 + resolution: {integrity: sha512-gjVS5hOP+M3wMm5nmNOucbIrqudzs9v/57bWRHQWLYklXqoXKrVfYW2W9+glfGsqtPgpiz5WwyEEB+ksXIx3gQ==}
3929 engines: {node: '>=18'}
3930
3931 process@0.11.10:
@@ -3932,8 +3964,8 @@ packages:
3964 resolution: {integrity: sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==}
3965 engines: {node: '>=0.6'}
3966
3935 - quansync@0.2.10:
3936 - resolution: {integrity: sha512-t41VRkMYbkHyCYmOvx/6URnN80H7k4X0lLdBMGsz+maAwrJQYB1djpV6vHrQIBE0WBSGqhtEHrK9U3DWWH8v7A==}
3967 + quansync@0.2.11:
3968 + resolution: {integrity: sha512-AifT7QEbW9Nri4tAwR5M/uzpBuqfZf+zwaEM/QkzEjj7NBuFD2rBuy0K3dE+8wltbezDV7JMA0WfnCPYRSYbXA==}
3969
3970 queue-microtask@1.2.3:
3971 resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==}
@@ -3985,6 +4017,10 @@ packages:
4017 resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==}
4018 engines: {node: '>=0.10.0'}
4019
4020 + require-from-string@2.0.2:
4021 + resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==}
4022 + engines: {node: '>=0.10.0'}
4023 +
4024 require-package-name@2.0.1:
4025 resolution: {integrity: sha512-uuoJ1hU/k6M0779t3VMVIYpb2VMJk05cehCaABFhXaibcbvfgR8wKiozLjVFSzJPmQMRqIcO0HMyTFqfV09V6Q==}
4026
@@ -4036,16 +4072,16 @@ packages:
4072 rollup:
4073 optional: true
4074
4039 - rollup@4.46.2:
4040 - resolution: {integrity: sha512-WMmLFI+Boh6xbop+OAGo9cQ3OgX9MIg7xOQjn+pTCwOkk+FNDAeAemXkJ3HzDJrVXleLOFVa1ipuc1AmEx1Dwg==}
4075 + rollup@4.50.2:
4076 + resolution: {integrity: sha512-BgLRGy7tNS9H66aIMASq1qSYbAAJV6Z6WR4QYTvj5FgF15rZ/ympT1uixHXwzbZUBDbkvqUI1KR0fH1FhMaQ9w==}
4077 engines: {node: '>=18.0.0', npm: '>=8.0.0'}
4078 hasBin: true
4079
4080 rrweb-cssom@0.8.0:
4081 resolution: {integrity: sha512-guoltQEx+9aMf2gDZ0s62EcV8lsXR+0w8915TC3ITdn2YueuNjdAYh/levpU9nFaoChh9RUS5ZdQMrKfVEN9tw==}
4082
4047 - run-applescript@7.0.0:
4048 - resolution: {integrity: sha512-9by4Ij99JUr/MCFBUkDKLWK3G9HVXmabKz9U5MlIAIuvuzkiOicRYs8XJLxX+xahD+mLiiCYDqF9dKAgtzKP1A==}
4083 + run-applescript@7.1.0:
4084 + resolution: {integrity: sha512-DPe5pVFaAsinSaV6QjQ6gdiedWDcRCbUuiQfQa2wmWV7+xC9bGulGI8+TdRmoFkAPaBXk8CrAbnlY2ISniJ47Q==}
4085 engines: {node: '>=18'}
4086
4087 run-parallel@1.2.0:
@@ -4060,8 +4096,8 @@ packages:
4096 safer-buffer@2.1.2:
4097 resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==}
4098
4063 - sass@1.89.2:
4064 - resolution: {integrity: sha512-xCmtksBKd/jdJ9Bt9p7nPKiuqrlBMBuuGkQlkhZjjQk3Ty48lv93k5Dq6OPkKt4XwxDJ7tvlfrTa1MPA9bf+QA==}
4099 + sass@1.92.1:
4100 + resolution: {integrity: sha512-ffmsdbwqb3XeyR8jJR6KelIXARM9bFQe8A6Q3W4Klmwy5Ckd5gz7jgUNHo4UOqutU5Sk1DtKLbpDP0nLCg1xqQ==}
4101 engines: {node: '>=14.0.0'}
4102 hasBin: true
4103
@@ -4107,8 +4143,8 @@ packages:
4143 resolution: {integrity: sha512-ObmnIF4hXNg1BqhnHmgbDETF8dLPCggZWBjkQfhZpbszZnYur5DUljTcCHii5LC3J5E0yeO/1LIMyH+UvHQgyw==}
4144 engines: {node: '>= 0.4'}
4145
4110 - shiki@3.8.1:
4111 - resolution: {integrity: sha512-+MYIyjwGPCaegbpBeFN9+oOifI8CKiKG3awI/6h3JeT85c//H2wDW/xCJEGuQ5jPqtbboKNqNy+JyX9PYpGwNg==}
4146 + shiki@3.12.2:
4147 + resolution: {integrity: sha512-uIrKI+f9IPz1zDT+GMz+0RjzKJiijVr6WDWm9Pe3NNY6QigKCfifCEv9v9R2mDASKKjzjQ2QpFLcxaR3iHSnMA==}
4148
4149 side-channel-list@1.0.0:
4150 resolution: {integrity: sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==}
@@ -4136,8 +4172,8 @@ packages:
4172 resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==}
4173 engines: {node: '>=14'}
4174
4139 - sirv@3.0.1:
4140 - resolution: {integrity: sha512-FoqMu0NCGBLCcAkS1qA+XJIQTR6/JHfQXl+uGteNCQ76T91DMUjPa9xfmeqMY3z80nLSg9yQmNjK0Px6RWsH/A==}
4175 + sirv@3.0.2:
4176 + resolution: {integrity: sha512-2wcC/oGxHis/BoHkkPwldgiPSYcpZK3JU28WoMVv55yHJgcZ8rlXvuG9iZggz+sU1d4bRgIGASwyWqjxu3FM0g==}
4177 engines: {node: '>=18'}
4178
4179 sisteransi@1.0.5:
@@ -4171,8 +4207,8 @@ packages:
4207 spdx-expression-parse@4.0.0:
4208 resolution: {integrity: sha512-Clya5JIij/7C6bRR22+tnGXbc4VKlibKSVj2iHvVeX5iMW7s1SIQlqu699JkODJJIhh/pUu8L0/VLh8xflD+LQ==}
4209
4174 - spdx-license-ids@3.0.21:
4175 - resolution: {integrity: sha512-Bvg/8F5XephndSK3JffaRqdT+gyhfqIPwDHpX80tJrF8QQRYMo8sNMeaZ2Dp5+jhwKnUmIOyFFQfHRkjJm5nXg==}
4210 + spdx-license-ids@3.0.22:
4211 + resolution: {integrity: sha512-4PRT4nh1EImPbt2jASOKHX7PB7I+e4IWNLvkKFDxNhJlfjbYlleYQh285Z/3mPTHSAK/AvdMmw5BNNuYH8ShgQ==}
4212
4213 speakingurl@14.0.1:
4214 resolution: {integrity: sha512-1POYv7uv2gXoyGFpBCmpDVSNV74IfsWlDW216UPjbWufNf+bSU6GdbDsxdcxtfwb4xlI3yxzOTKClUosxARYrQ==}
@@ -4192,8 +4228,8 @@ packages:
4228 stackback@0.0.2:
4229 resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==}
4230
4195 - start-server-and-test@2.0.12:
4196 - resolution: {integrity: sha512-U6QiS5qsz+DN5RfJJrkAXdooxMDnLZ+n5nR8kaX//ZH19SilF6b58Z3zM9zTfrNIkJepzauHo4RceSgvgUSX9w==}
4231 + start-server-and-test@2.1.1:
4232 + resolution: {integrity: sha512-5RIIrYAsNnauKHCNVYNR8YMczqWM6s+n4vMHTMBbDtObeZr4q1nbRB4fmrnl0dqTWSAqBeE6sxrr2/7EuDtQQQ==}
4233 engines: {node: '>=16'}
4234 hasBin: true
4235
@@ -4218,8 +4254,8 @@ packages:
4254 resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==}
4255 engines: {node: '>=8'}
4256
4221 - strip-ansi@7.1.0:
4222 - resolution: {integrity: sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==}
4257 + strip-ansi@7.1.2:
4258 + resolution: {integrity: sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==}
4259 engines: {node: '>=12'}
4260
4261 strip-final-newline@2.0.0:
@@ -4230,8 +4266,8 @@ packages:
4266 resolution: {integrity: sha512-aulFJcD6YK8V1G7iRB5tigAP4TsHBZZrOV8pjV++zdUwmeV8uzbY7yn6h9MswN62adStNZFuCIx4haBnRuMDaw==}
4267 engines: {node: '>=18'}
4268
4233 - strip-indent@4.0.0:
4234 - resolution: {integrity: sha512-mnVSV2l+Zv6BLpSD/8V87CW/y9EmmbYzGCIavsnsI6/nwn26DwffM/yztm30Z/I2DY9wdS3vXVCMnHDgZaVNoA==}
4269 + strip-indent@4.1.0:
4270 + resolution: {integrity: sha512-OA95x+JPmL7kc7zCu+e+TeYxEiaIyndRx0OrBcK2QPPH09oAndr2ALvymxWA+Lx1PYYvFUm4O63pRkdJAaW96w==}
4271 engines: {node: '>=12'}
4272
4273 strip-json-comments@3.1.1:
@@ -4278,19 +4314,29 @@ packages:
4314 resolution: {integrity: sha512-MeQTA1r0litLUf0Rp/iisCaL8761lKAZHaimlbGK4j0HysC4PLfqygQj9srcs0m2RdtDYnF8UuYyKpbjHYp7Jw==}
4315 engines: {node: ^14.18.0 || >=16.0.0}
4316
4281 - tailwindcss@4.1.11:
4282 - resolution: {integrity: sha512-2E9TBm6MDD/xKYe+dvJZAmg3yxIEDNRc0jwlNyDg/4Fil2QcSLjFKGVff0lAf1jjeaArlG/M75Ey/EYr/OJtBA==}
4317 + systeminformation@5.27.7:
4318 + resolution: {integrity: sha512-saaqOoVEEFaux4v0K8Q7caiauRwjXC4XbD2eH60dxHXbpKxQ8kH9Rf7Jh+nryKpOUSEFxtCdBlSUx0/lO6rwRg==}
4319 + engines: {node: '>=8.0.0'}
4320 + os: [darwin, linux, win32, freebsd, openbsd, netbsd, sunos, android]
4321 + hasBin: true
4322
4284 - tapable@2.2.2:
4285 - resolution: {integrity: sha512-Re10+NauLTMCudc7T5WLFLAwDhQ0JWdrMK+9B2M8zR5hRExKmsRDCBA7/aV/pNJFltmBFO5BAMlQFi/vq3nKOg==}
4323 + tagged-tag@1.0.0:
4324 + resolution: {integrity: sha512-yEFYrVhod+hdNyx7g5Bnkkb0G6si8HJurOoOEgC8B/O0uXLHlaey/65KRv6cuWBNhBgHKAROVpc7QyYqE5gFng==}
4325 + engines: {node: '>=20'}
4326 +
4327 + tailwindcss@4.1.13:
4328 + resolution: {integrity: sha512-i+zidfmTqtwquj4hMEwdjshYYgMbOrPzb9a0M3ZgNa0JMoZeFC6bxZvO8yr8ozS6ix2SDz0+mvryPeBs2TFE+w==}
4329 +
4330 + tapable@2.2.3:
4331 + resolution: {integrity: sha512-ZL6DDuAlRlLGghwcfmSn9sK3Hr6ArtyudlSAiCqQ6IfE+b+HHbydbYDIG15IfS5do+7XQQBdBiubF/cV2dnDzg==}
4332 engines: {node: '>=6'}
4333
4334 tar@7.4.3:
4335 resolution: {integrity: sha512-5S7Va8hKfV7W5U6g3aYxXmlPoZVAwUMy9AOKyF2fVuZa2UD3qZjg578OrLRt8PcNN1PleVaL/5/yYATNL0ICUw==}
4336 engines: {node: '>=18'}
4337
4292 - taze@19.1.0:
4293 - resolution: {integrity: sha512-MDN2WZb7TgsIvtFxqsLJ4GYy9dTDG5Dea/ZfPHrG98Cy7UH1EFIOzH+zDjnoP38ImuBbxZy1Zl8AbiwOZpYMUQ==}
4338 + taze@19.6.0:
4339 + resolution: {integrity: sha512-hQGQH4WVtV9BqsZbrGzOmOP4NdWqie948BnqtH+NPwdVt5mI+qALVRDvgzgdf+neN7bcrVVpV4ToyFkxg0U0xQ==}
4340 hasBin: true
4341
4342 thememirror@2.0.1:
@@ -4315,8 +4361,8 @@ packages:
4361 tinyexec@1.0.1:
4362 resolution: {integrity: sha512-5uC6DDlmeqiOwCPmK9jMSdOuZTh8bU39Ys6yidB+UTt5hfZUPGAypSgFRiEp+jbi9qH40BLDvy85jIU88wKSqw==}
4363
4318 - tinyglobby@0.2.14:
4319 - resolution: {integrity: sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ==}
4364 + tinyglobby@0.2.15:
4365 + resolution: {integrity: sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==}
4366 engines: {node: '>=12.0.0'}
4367
4368 tinypool@1.1.1:
@@ -4327,19 +4373,26 @@ packages:
4373 resolution: {integrity: sha512-op4nsTR47R6p0vMUUoYl/a+ljLFVtlfaXkLQmqfLR1qHma1h/ysYk4hEXZ880bf2CYgTskvTa/e196Vd5dDQXw==}
4374 engines: {node: '>=14.0.0'}
4375
4330 - tinyspy@4.0.3:
4331 - resolution: {integrity: sha512-t2T/WLB2WRgZ9EpE4jgPJ9w+i66UZfDc8wHh0xrwiRNN+UwH98GIJkTeZqX9rg0i0ptwzqW+uYeIF0T4F8LR7A==}
4376 + tinyspy@4.0.4:
4377 + resolution: {integrity: sha512-azl+t0z7pw/z958Gy9svOTuzqIk6xq+NSheJzn5MMWtWTFywIacg2wUlzKFGtt3cthx0r2SxMK0yzJOR0IES7Q==}
4378 engines: {node: '>=14.0.0'}
4379
4380 tldts-core@6.1.86:
4381 resolution: {integrity: sha512-Je6p7pkk+KMzMv2XXKmAE3McmolOQFdxkKw0R8EYNr7sELW46JqnNeTX8ybPiQgvg1ymCoF8LXs5fzFaZvJPTA==}
4382
4383 + tldts-core@7.0.14:
4384 + resolution: {integrity: sha512-viZGNK6+NdluOJWwTO9olaugx0bkKhscIdriQQ+lNNhwitIKvb+SvhbYgnCz6j9p7dX3cJntt4agQAKMXLjJ5g==}
4385 +
4386 tldts@6.1.86:
4387 resolution: {integrity: sha512-WMi/OQ2axVTf/ykqCQgXiIct+mSQDFdH2fkwhPwgEwvJ1kSzZRiinb0zF2Xb8u4+OqPChmyI6MEu4EezNJz+FQ==}
4388 hasBin: true
4389
4341 - tmp@0.2.3:
4342 - resolution: {integrity: sha512-nZD7m9iCPC5g0pYmcaxogYKggSfLsdxl8of3Q/oIbqCqLLIO9IAF0GWjX1z9NZRHPiXv8Wex4yDCaZsgEw0Y8w==}
4390 + tldts@7.0.14:
4391 + resolution: {integrity: sha512-lMNHE4aSI3LlkMUMicTmAG3tkkitjOQGDTFboPJwAg2kJXKP1ryWEyqujktg5qhrFZOkk5YFzgkxg3jErE+i5w==}
4392 + hasBin: true
4393 +
4394 + tmp@0.2.5:
4395 + resolution: {integrity: sha512-voyz6MApa1rQGUxT3E+BK7/ROe8itEx7vD8/HEvt4xwXucvQ5G5oeEiHkmHZJuBO21RpOf+YYm9MOivj709jow==}
4396 engines: {node: '>=14.14'}
4397
4398 to-regex-range@5.0.1:
@@ -4358,9 +4411,13 @@ packages:
4411 resolution: {integrity: sha512-FVDYdxtnj0G6Qm/DhNPSb8Ju59ULcup3tuJxkFb5K8Bv2pUXILbf0xZWU8PX8Ov19OXljbUyveOFwRMwkXzO+A==}
4412 engines: {node: '>=16'}
4413
4361 - tr46@5.1.1:
4362 - resolution: {integrity: sha512-hdF5ZgjTqgAntKkklYw0R03MG2x/bSzTtkxmIRw/sTNV8YXsCJ1tfLAX23lhxhHJlEf3CRCOCGGWw3vI3GaSPw==}
4363 - engines: {node: '>=18'}
4414 + tough-cookie@6.0.0:
4415 + resolution: {integrity: sha512-kXuRi1mtaKMrsLUxz3sQYvVl37B0Ns6MzfrtV5DvJceE9bPyspOqk9xxv7XbZWcfLWbFmm997vl83qUWVJA64w==}
4416 + engines: {node: '>=16'}
4417 +
4418 + tr46@6.0.0:
4419 + resolution: {integrity: sha512-bLVMLPtstlZ4iMQHpFHTR7GAGj2jxi8Dg0s2h2MafAE4uSWF98FC/3MomU51iQAMf8/qDUbKWf5GxuvvVcXEhw==}
4420 + engines: {node: '>=20'}
4421
4422 tree-kill@1.2.2:
4423 resolution: {integrity: sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==}
@@ -4410,12 +4467,12 @@ packages:
4467 resolution: {integrity: sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==}
4468 engines: {node: '>=8'}
4469
4413 - type-fest@4.41.0:
4414 - resolution: {integrity: sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==}
4415 - engines: {node: '>=16'}
4470 + type-fest@5.0.0:
4471 + resolution: {integrity: sha512-GeJop7+u7BYlQ6yQCAY1nBQiRSHR+6OdCEtd8Bwp9a3NK3+fWAVjOaPKJDteB9f6cIJ0wt4IfnScjLG450EpXA==}
4472 + engines: {node: '>=20'}
4473
4417 - typescript@5.8.3:
4418 - resolution: {integrity: sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==}
4474 + typescript@5.9.2:
4475 + resolution: {integrity: sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A==}
4476 engines: {node: '>=14.17'}
4477 hasBin: true
4478
@@ -4425,21 +4482,21 @@ packages:
4482 ufo@1.6.1:
4483 resolution: {integrity: sha512-9a4/uxlTWJ4+a5i0ooc1rU7C7YOw3wT+UGqdeNNHWnOF9qcMBgLRS+4IYUqbczewFx4mLEig6gawh7X6mFlEkA==}
4484
4428 - unconfig@7.3.2:
4429 - resolution: {integrity: sha512-nqG5NNL2wFVGZ0NA/aCFw0oJ2pxSf1lwg4Z5ill8wd7K4KX/rQbHlwbh+bjctXL5Ly1xtzHenHGOK0b+lG6JVg==}
4485 + unconfig@7.3.3:
4486 + resolution: {integrity: sha512-QCkQoOnJF8L107gxfHL0uavn7WD9b3dpBcFX6HtfQYmjw2YzWxGuFQ0N0J6tE9oguCBJn9KOvfqYDCMPHIZrBA==}
4487
4488 unctx@2.4.1:
4489 resolution: {integrity: sha512-AbaYw0Nm4mK4qjhns67C+kgxR2YWiwlDBPzxrN8h8C6VtAdCgditAY5Dezu3IJy4XVqAnbrXt9oQJvsn3fyozg==}
4490
4434 - undici-types@7.8.0:
4435 - resolution: {integrity: sha512-9UJ2xGDvQ43tYyVMpuHlsgApydB8ZKfVYTsLDhXkFL/6gfkp+U8xTGdh8pMJv1SpZna0zxG1DwsKZsreLbXBxw==}
4491 + undici-types@7.12.0:
4492 + resolution: {integrity: sha512-goOacqME2GYyOZZfb5Lgtu+1IDmAlAEu5xnD3+xTzS10hT0vzpf0SPjkXwAw9Jm+4n/mQGDP3LO8CPbYROeBfQ==}
4493
4494 unicorn-magic@0.3.0:
4495 resolution: {integrity: sha512-+QBBXBCvifc56fsbuxZQ6Sic3wqqc3WWaqxs58gvJrcOuN83HGTCwz3oS5phzU9LthRNE9VrJCFCLUgHeeFnfA==}
4496 engines: {node: '>=18'}
4497
4441 - unimport@5.2.0:
4442 - resolution: {integrity: sha512-bTuAMMOOqIAyjV4i4UH7P07pO+EsVxmhOzQ2YJ290J6mkLUdozNhb5I/YoOEheeNADC03ent3Qj07X0fWfUpmw==}
4498 + unimport@5.3.0:
4499 + resolution: {integrity: sha512-cty7t1DESgm0OPfCy9oyn5u9B5t0tMW6tH6bXTjAGIO3SkJsbg/DXYHjrPrUKqultqbAAoltAfYsuu/FEDocjg==}
4500 engines: {node: '>=18.12.0'}
4501
4502 unist-util-is@6.0.0:
@@ -4461,12 +4518,12 @@ packages:
4518 resolution: {integrity: sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==}
4519 engines: {node: '>= 10.0.0'}
4520
4464 - unplugin-utils@0.2.4:
4465 - resolution: {integrity: sha512-8U/MtpkPkkk3Atewj1+RcKIjb5WBimZ/WSLhhR3w6SsIj8XJuKTacSP8g+2JhfSGw0Cb125Y+2zA/IzJZDVbhA==}
4466 - engines: {node: '>=18.12.0'}
4521 + unplugin-utils@0.3.0:
4522 + resolution: {integrity: sha512-JLoggz+PvLVMJo+jZt97hdIIIZ2yTzGgft9e9q8iMrC4ewufl62ekeW7mixBghonn2gVb/ICjyvlmOCUBnJLQg==}
4523 + engines: {node: '>=20.19.0'}
4524
4468 - unplugin@2.3.5:
4469 - resolution: {integrity: sha512-RyWSb5AHmGtjjNQ6gIlA67sHOsWpsbWpwDokLwTcejVdOjEkJZh7QKu14J00gDDVSh8kGH4KYC/TNBceXFZhtw==}
4525 + unplugin@2.3.10:
4526 + resolution: {integrity: sha512-6NCPkv1ClwH+/BGE9QeoTIl09nuiAt0gS28nn1PvYXsGKRwM2TCbFA2QiilmehPDTXIe684k4rZI1yl3A1PCUw==}
4527 engines: {node: '>=18.12.0'}
4528
4529 untildify@4.0.0:
@@ -4532,8 +4589,8 @@ packages:
4589 engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0}
4590 hasBin: true
4591
4535 - vite-plugin-inspect@11.3.2:
4536 - resolution: {integrity: sha512-nzwvyFQg58XSMAmKVLr2uekAxNYvAbz1lyPmCAFVIBncCgN9S/HPM+2UM9Q9cvc4JEbC5ZBgwLAdaE2onmQuKg==}
4592 + vite-plugin-inspect@11.3.3:
4593 + resolution: {integrity: sha512-u2eV5La99oHoYPHE6UvbwgEqKKOQGz86wMg40CCosP6q8BkB6e5xPneZfYagK4ojPJSj5anHCrnvC20DpwVdRA==}
4594 engines: {node: '>=14'}
4595 peerDependencies:
4596 '@nuxt/kit': '*'
@@ -4542,8 +4599,8 @@ packages:
4599 '@nuxt/kit':
4600 optional: true
4601
4545 - vite-plugin-vue-devtools@8.0.0:
4546 - resolution: {integrity: sha512-9bWQig8UMu3nPbxX86NJv56aelpFYoBHxB5+pxuQz3pa3Tajc1ezRidj/0dnADA4/UHuVIfwIVYHOvMXYcPshg==}
4602 + vite-plugin-vue-devtools@8.0.2:
4603 + resolution: {integrity: sha512-1069qvMBcyAu3yXQlvYrkwoyLOk0lSSR/gTKy/vy+Det7TXnouGei6ZcKwr5TIe938v/14oLlp0ow6FSJkkORA==}
4604 engines: {node: '>=v14.21.3'}
4605 peerDependencies:
4606 vite: ^6.0.0 || ^7.0.0-0
@@ -4558,8 +4615,8 @@ packages:
4615 peerDependencies:
4616 vue: '>=3.2.13'
4617
4561 - vite@7.0.6:
4562 - resolution: {integrity: sha512-MHFiOENNBd+Bd9uvc8GEsIzdkn1JxMmEeYX35tI3fv0sJBUTfW5tQsoaOwuY4KhBI09A3dUJ/DXf2yxPVPUceg==}
4618 + vite@7.1.6:
4619 + resolution: {integrity: sha512-SRYIB8t/isTwNn8vMB3MR6E+EQZM/WG1aKmmIUCfDXfVvKfc20ZpamngWHKzAmmu9ppsgxsg4b2I7c90JZudIQ==}
4620 engines: {node: ^20.19.0 || >=22.12.0}
4621 hasBin: true
4622 peerDependencies:
@@ -4660,8 +4717,8 @@ packages:
4717 peerDependencies:
4718 vue: ^3.0.0
4719
4663 - vue-i18n@11.1.11:
4664 - resolution: {integrity: sha512-LvyteQoXeQiuILbzqv13LbyBna/TEv2Ha+4ZWK2AwGHUzZ8+IBaZS0TJkCgn5izSPLcgZwXy9yyTrewCb2u/MA==}
4720 + vue-i18n@11.1.12:
4721 + resolution: {integrity: sha512-BnstPj3KLHLrsqbVU2UOrPmr0+Mv11bsUZG0PyCOzsawCivk8W00GMXHeVUWIDOgNaScCuZah47CZFE+Wnl8mw==}
4722 engines: {node: '>= 16'}
4723 peerDependencies:
4724 vue: ^3.0.0
@@ -4676,8 +4733,8 @@ packages:
4733 peerDependencies:
4734 vue: ^3.3.4
4735
4679 - vue-tsc@3.0.4:
4680 - resolution: {integrity: sha512-kZmSEjGtROApVBuaIcoprrXZsFNGon5ggkTJokmhQ/H1hMzCFRPQ0Ed8IHYFsmYJYvHBcdmEQVGVcRuxzPzNbw==}
4736 + vue-tsc@3.0.7:
4737 + resolution: {integrity: sha512-BSMmW8GGEgHykrv7mRk6zfTdK+tw4MBZY/x6fFa7IkdXK3s/8hQRacPjG9/8YKFDIWGhBocwi6PlkQQ/93OgIQ==}
4738 hasBin: true
4739 peerDependencies:
4740 typescript: '>=5.0.0'
@@ -4694,8 +4751,8 @@ packages:
4751 peerDependencies:
4752 vue: ^3.2
4753
4697 - vue@3.5.18:
4698 - resolution: {integrity: sha512-7W4Y4ZbMiQ3SEo+m9lnoNpV9xG7QVMLa+/0RFwwiAVkeYoyGXqWE85jabU4pllJNUzqfLShJ5YLptewhCWUgNA==}
4754 + vue@3.5.21:
4755 + resolution: {integrity: sha512-xxf9rum9KtOdwdRkiApWL+9hZEMWE90FHh8yS1+KJAiWYh+iGWV1FquPjoO9VUHQ+VIhsCXNNyZ5Sf4++RVZBA==}
4756 peerDependencies:
4757 typescript: '*'
4758 peerDependenciesMeta:
@@ -4707,8 +4764,8 @@ packages:
4764 peerDependencies:
4765 vue: ^3.0.1
4766
4710 - vueuc@0.4.64:
4711 - resolution: {integrity: sha512-wlJQj7fIwKK2pOEoOq4Aro8JdPOGpX8aWQhV8YkTW9OgWD2uj2O8ANzvSsIGjx7LTOc7QbS7sXdxHi6XvRnHPA==}
4767 + vueuc@0.4.65:
4768 + resolution: {integrity: sha512-lXuMl+8gsBmruudfxnMF9HW4be8rFziylXFu1VHVNbLVhRTXXV4njvpRuJapD/8q+oFEMSfQMH16E/85VoWRyQ==}
4769 peerDependencies:
4770 vue: ^3.0.11
4771
@@ -4719,14 +4776,14 @@ packages:
4776 resolution: {integrity: sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==}
4777 engines: {node: '>=18'}
4778
4722 - wait-on@8.0.3:
4723 - resolution: {integrity: sha512-nQFqAFzZDeRxsu7S3C7LbuxslHhk+gnJZHyethuGKAn2IVleIbTB9I3vJSQiSR+DifUqmdzfPMoMPJfLqMF2vw==}
4779 + wait-on@8.0.4:
4780 + resolution: {integrity: sha512-8f9LugAGo4PSc0aLbpKVCVtzayd36sSCp4WLpVngkYq6PK87H79zt77/tlCU6eKCLqR46iFvcl0PU5f+DmtkwA==}
4781 engines: {node: '>=12.0.0'}
4782 hasBin: true
4783
4727 - webidl-conversions@7.0.0:
4728 - resolution: {integrity: sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==}
4729 - engines: {node: '>=12'}
4784 + webidl-conversions@8.0.0:
4785 + resolution: {integrity: sha512-n4W4YFyz5JzOfQeA8oN7dUYpR+MBP3PIUsn2jLjWXwK5ASUzt0Jc/A5sAUZoCYFJRGF0FBKJ+1JjN43rNdsQzA==}
4786 + engines: {node: '>=20'}
4787
4788 webpack-virtual-modules@0.6.2:
4789 resolution: {integrity: sha512-66/V2i5hQanC51vBQKPH4aI8NMAcBW59FVBs+rC7eGHupMyfn34q7rZIE+ETlJ+XTevqfUhVVBgSUNSW2flEUQ==}
@@ -4739,9 +4796,9 @@ packages:
4796 resolution: {integrity: sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==}
4797 engines: {node: '>=18'}
4798
4742 - whatwg-url@14.2.0:
4743 - resolution: {integrity: sha512-De72GdQZzNTUBBChsXueQUnPKDkg/5A5zp7pFDuQAj5UFoENpiACU0wlCvzpAGnTkj++ihpKwKyYewn/XNUbKw==}
4744 - engines: {node: '>=18'}
4799 + whatwg-url@15.1.0:
4800 + resolution: {integrity: sha512-2ytDk0kiEj/yu90JOAp44PVPUkO9+jVhyf+SybKlRHSDlvOOZhdPIrr7xTH64l4WixO2cP+wQIcgujkGBPPz6g==}
4801 + engines: {node: '>=20'}
4802
4803 which@1.3.1:
4804 resolution: {integrity: sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==}
@@ -4836,8 +4893,8 @@ packages:
4893 resolution: {integrity: sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==}
4894 engines: {node: '>= 6'}
4895
4839 - yaml@2.8.0:
4840 - resolution: {integrity: sha512-4lLa/EcQCB0cJkyts+FpIRx5G/llPxfP6VQU5KByHEhLxY3IJCH0f0Hy1MHI8sClTvsIb8qwRJ6R/ZdlDJ/leQ==}
4896 + yaml@2.8.1:
4897 + resolution: {integrity: sha512-lcYcMxX2PO9XMGvAJkJ3OsNMw+/7FKes7/hgerGUYWIoWu5j/+YQqcZr5JnPZWzOsEBgMbSbiSTn/dv/69Mkpw==}
4898 engines: {node: '>= 14.6'}
4899 hasBin: true
4900
@@ -4864,8 +4921,8 @@ packages:
4921 resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==}
4922 engines: {node: '>=10'}
4923
4867 - yoctocolors@2.1.1:
4868 - resolution: {integrity: sha512-GQHQqAopRhwU8Kt1DDM8NjibDXHC8eoh1erhGAJPEyveY9qqVeXvVikNKrDz69sHowPMorbPUrH/mx8c50eiBQ==}
4924 + yoctocolors@2.1.2:
4925 + resolution: {integrity: sha512-CzhO+pFNo8ajLM2d2IW/R93ipy99LWjtwblvC1RsoSUMZgyLbYFr221TnSNT7GjGdYui6P459mw9JH/g/zW2ug==}
4926 engines: {node: '>=18'}
4927
4928 zrender@6.0.0:
@@ -4880,133 +4937,128 @@ snapshots:
4937 dependencies:
4938 lodash: 4.17.21
4939
4883 - '@algolia/abtesting@1.1.0':
4940 + '@algolia/abtesting@1.3.0':
4941 dependencies:
4885 - '@algolia/client-common': 5.35.0
4886 - '@algolia/requester-browser-xhr': 5.35.0
4887 - '@algolia/requester-fetch': 5.35.0
4888 - '@algolia/requester-node-http': 5.35.0
4942 + '@algolia/client-common': 5.37.0
4943 + '@algolia/requester-browser-xhr': 5.37.0
4944 + '@algolia/requester-fetch': 5.37.0
4945 + '@algolia/requester-node-http': 5.37.0
4946
4890 - '@algolia/client-abtesting@5.35.0':
4947 + '@algolia/client-abtesting@5.37.0':
4948 dependencies:
4892 - '@algolia/client-common': 5.35.0
4893 - '@algolia/requester-browser-xhr': 5.35.0
4894 - '@algolia/requester-fetch': 5.35.0
4895 - '@algolia/requester-node-http': 5.35.0
4949 + '@algolia/client-common': 5.37.0
4950 + '@algolia/requester-browser-xhr': 5.37.0
4951 + '@algolia/requester-fetch': 5.37.0
4952 + '@algolia/requester-node-http': 5.37.0
4953
4897 - '@algolia/client-analytics@5.35.0':
4954 + '@algolia/client-analytics@5.37.0':
4955 dependencies:
4899 - '@algolia/client-common': 5.35.0
4900 - '@algolia/requester-browser-xhr': 5.35.0
4901 - '@algolia/requester-fetch': 5.35.0
4902 - '@algolia/requester-node-http': 5.35.0
4956 + '@algolia/client-common': 5.37.0
4957 + '@algolia/requester-browser-xhr': 5.37.0
4958 + '@algolia/requester-fetch': 5.37.0
4959 + '@algolia/requester-node-http': 5.37.0
4960
4904 - '@algolia/client-common@5.35.0': {}
4905 -
4906 - '@algolia/client-insights@5.35.0':
4907 - dependencies:
4908 - '@algolia/client-common': 5.35.0
4909 - '@algolia/requester-browser-xhr': 5.35.0
4910 - '@algolia/requester-fetch': 5.35.0
4911 - '@algolia/requester-node-http': 5.35.0
4961 + '@algolia/client-common@5.37.0': {}
4962
4913 - '@algolia/client-personalization@5.35.0':
4963 + '@algolia/client-insights@5.37.0':
4964 dependencies:
4915 - '@algolia/client-common': 5.35.0
4916 - '@algolia/requester-browser-xhr': 5.35.0
4917 - '@algolia/requester-fetch': 5.35.0
4918 - '@algolia/requester-node-http': 5.35.0
4965 + '@algolia/client-common': 5.37.0
4966 + '@algolia/requester-browser-xhr': 5.37.0
4967 + '@algolia/requester-fetch': 5.37.0
4968 + '@algolia/requester-node-http': 5.37.0
4969
4920 - '@algolia/client-query-suggestions@5.35.0':
4970 + '@algolia/client-personalization@5.37.0':
4971 dependencies:
4922 - '@algolia/client-common': 5.35.0
4923 - '@algolia/requester-browser-xhr': 5.35.0
4924 - '@algolia/requester-fetch': 5.35.0
4925 - '@algolia/requester-node-http': 5.35.0
4972 + '@algolia/client-common': 5.37.0
4973 + '@algolia/requester-browser-xhr': 5.37.0
4974 + '@algolia/requester-fetch': 5.37.0
4975 + '@algolia/requester-node-http': 5.37.0
4976
4927 - '@algolia/client-search@5.35.0':
4977 + '@algolia/client-query-suggestions@5.37.0':
4978 dependencies:
4929 - '@algolia/client-common': 5.35.0
4930 - '@algolia/requester-browser-xhr': 5.35.0
4931 - '@algolia/requester-fetch': 5.35.0
4932 - '@algolia/requester-node-http': 5.35.0
4979 + '@algolia/client-common': 5.37.0
4980 + '@algolia/requester-browser-xhr': 5.37.0
4981 + '@algolia/requester-fetch': 5.37.0
4982 + '@algolia/requester-node-http': 5.37.0
4983
4934 - '@algolia/ingestion@1.35.0':
4984 + '@algolia/client-search@5.37.0':
4985 dependencies:
4936 - '@algolia/client-common': 5.35.0
4937 - '@algolia/requester-browser-xhr': 5.35.0
4938 - '@algolia/requester-fetch': 5.35.0
4939 - '@algolia/requester-node-http': 5.35.0
4986 + '@algolia/client-common': 5.37.0
4987 + '@algolia/requester-browser-xhr': 5.37.0
4988 + '@algolia/requester-fetch': 5.37.0
4989 + '@algolia/requester-node-http': 5.37.0
4990
4941 - '@algolia/monitoring@1.35.0':
4991 + '@algolia/ingestion@1.37.0':
4992 dependencies:
4943 - '@algolia/client-common': 5.35.0
4944 - '@algolia/requester-browser-xhr': 5.35.0
4945 - '@algolia/requester-fetch': 5.35.0
4946 - '@algolia/requester-node-http': 5.35.0
4993 + '@algolia/client-common': 5.37.0
4994 + '@algolia/requester-browser-xhr': 5.37.0
4995 + '@algolia/requester-fetch': 5.37.0
4996 + '@algolia/requester-node-http': 5.37.0
4997
4948 - '@algolia/recommend@5.35.0':
4998 + '@algolia/monitoring@1.37.0':
4999 dependencies:
4950 - '@algolia/client-common': 5.35.0
4951 - '@algolia/requester-browser-xhr': 5.35.0
4952 - '@algolia/requester-fetch': 5.35.0
4953 - '@algolia/requester-node-http': 5.35.0
5000 + '@algolia/client-common': 5.37.0
5001 + '@algolia/requester-browser-xhr': 5.37.0
5002 + '@algolia/requester-fetch': 5.37.0
5003 + '@algolia/requester-node-http': 5.37.0
5004
4955 - '@algolia/requester-browser-xhr@5.35.0':
5005 + '@algolia/recommend@5.37.0':
5006 dependencies:
4957 - '@algolia/client-common': 5.35.0
5007 + '@algolia/client-common': 5.37.0
5008 + '@algolia/requester-browser-xhr': 5.37.0
5009 + '@algolia/requester-fetch': 5.37.0
5010 + '@algolia/requester-node-http': 5.37.0
5011
4959 - '@algolia/requester-fetch@5.35.0':
5012 + '@algolia/requester-browser-xhr@5.37.0':
5013 dependencies:
4961 - '@algolia/client-common': 5.35.0
5014 + '@algolia/client-common': 5.37.0
5015
4963 - '@algolia/requester-node-http@5.35.0':
5016 + '@algolia/requester-fetch@5.37.0':
5017 dependencies:
4965 - '@algolia/client-common': 5.35.0
5018 + '@algolia/client-common': 5.37.0
5019
4967 - '@ampproject/remapping@2.3.0':
5020 + '@algolia/requester-node-http@5.37.0':
5021 dependencies:
4969 - '@jridgewell/gen-mapping': 0.3.12
4970 - '@jridgewell/trace-mapping': 0.3.29
5022 + '@algolia/client-common': 5.37.0
5023
4972 - '@antfu/eslint-config@5.0.0(@vue/compiler-sfc@3.5.18)(eslint@9.32.0(jiti@2.5.1))(typescript@5.8.3)(vitest@3.2.4(@types/debug@4.1.12)(@types/node@24.1.0)(jiti@2.5.1)(jsdom@26.1.0)(lightningcss@1.30.1)(sass@1.89.2)(yaml@2.8.0))':
5024 + '@antfu/eslint-config@5.3.0(@vue/compiler-sfc@3.5.21)(eslint@9.35.0(jiti@2.5.1))(typescript@5.9.2)(vitest@3.2.4(@types/debug@4.1.12)(@types/node@24.5.2)(jiti@2.5.1)(jsdom@27.0.0(postcss@8.5.6))(lightningcss@1.30.1)(sass@1.92.1)(yaml@2.8.1))':
5025 dependencies:
5026 '@antfu/install-pkg': 1.1.0
5027 '@clack/prompts': 0.11.0
4976 - '@eslint-community/eslint-plugin-eslint-comments': 4.5.0(eslint@9.32.0(jiti@2.5.1))
4977 - '@eslint/markdown': 7.1.0
4978 - '@stylistic/eslint-plugin': 5.2.2(eslint@9.32.0(jiti@2.5.1))
4979 - '@typescript-eslint/eslint-plugin': 8.38.0(@typescript-eslint/parser@8.38.0(eslint@9.32.0(jiti@2.5.1))(typescript@5.8.3))(eslint@9.32.0(jiti@2.5.1))(typescript@5.8.3)
4980 - '@typescript-eslint/parser': 8.38.0(eslint@9.32.0(jiti@2.5.1))(typescript@5.8.3)
4981 - '@vitest/eslint-plugin': 1.3.4(eslint@9.32.0(jiti@2.5.1))(typescript@5.8.3)(vitest@3.2.4(@types/debug@4.1.12)(@types/node@24.1.0)(jiti@2.5.1)(jsdom@26.1.0)(lightningcss@1.30.1)(sass@1.89.2)(yaml@2.8.0))
5028 + '@eslint-community/eslint-plugin-eslint-comments': 4.5.0(eslint@9.35.0(jiti@2.5.1))
5029 + '@eslint/markdown': 7.2.0
5030 + '@stylistic/eslint-plugin': 5.3.1(eslint@9.35.0(jiti@2.5.1))
5031 + '@typescript-eslint/eslint-plugin': 8.44.0(@typescript-eslint/parser@8.44.0(eslint@9.35.0(jiti@2.5.1))(typescript@5.9.2))(eslint@9.35.0(jiti@2.5.1))(typescript@5.9.2)
5032 + '@typescript-eslint/parser': 8.44.0(eslint@9.35.0(jiti@2.5.1))(typescript@5.9.2)
5033 + '@vitest/eslint-plugin': 1.3.12(eslint@9.35.0(jiti@2.5.1))(typescript@5.9.2)(vitest@3.2.4(@types/debug@4.1.12)(@types/node@24.5.2)(jiti@2.5.1)(jsdom@27.0.0(postcss@8.5.6))(lightningcss@1.30.1)(sass@1.92.1)(yaml@2.8.1))
5034 ansis: 4.1.0
5035 cac: 6.7.14
4984 - eslint: 9.32.0(jiti@2.5.1)
4985 - eslint-config-flat-gitignore: 2.1.0(eslint@9.32.0(jiti@2.5.1))
5036 + eslint: 9.35.0(jiti@2.5.1)
5037 + eslint-config-flat-gitignore: 2.1.0(eslint@9.35.0(jiti@2.5.1))
5038 eslint-flat-config-utils: 2.1.1
4987 - eslint-merge-processors: 2.0.0(eslint@9.32.0(jiti@2.5.1))
4988 - eslint-plugin-antfu: 3.1.1(eslint@9.32.0(jiti@2.5.1))
4989 - eslint-plugin-command: 3.3.1(eslint@9.32.0(jiti@2.5.1))
4990 - eslint-plugin-import-lite: 0.3.0(eslint@9.32.0(jiti@2.5.1))(typescript@5.8.3)
4991 - eslint-plugin-jsdoc: 51.4.1(eslint@9.32.0(jiti@2.5.1))
4992 - eslint-plugin-jsonc: 2.20.1(eslint@9.32.0(jiti@2.5.1))
4993 - eslint-plugin-n: 17.21.3(eslint@9.32.0(jiti@2.5.1))(typescript@5.8.3)
5039 + eslint-merge-processors: 2.0.0(eslint@9.35.0(jiti@2.5.1))
5040 + eslint-plugin-antfu: 3.1.1(eslint@9.35.0(jiti@2.5.1))
5041 + eslint-plugin-command: 3.3.1(eslint@9.35.0(jiti@2.5.1))
5042 + eslint-plugin-import-lite: 0.3.0(eslint@9.35.0(jiti@2.5.1))(typescript@5.9.2)
5043 + eslint-plugin-jsdoc: 54.7.0(eslint@9.35.0(jiti@2.5.1))
5044 + eslint-plugin-jsonc: 2.20.1(eslint@9.35.0(jiti@2.5.1))
5045 + eslint-plugin-n: 17.23.1(eslint@9.35.0(jiti@2.5.1))(typescript@5.9.2)
5046 eslint-plugin-no-only-tests: 3.3.0
4995 - eslint-plugin-perfectionist: 4.15.0(eslint@9.32.0(jiti@2.5.1))(typescript@5.8.3)
4996 - eslint-plugin-pnpm: 1.1.0(eslint@9.32.0(jiti@2.5.1))
4997 - eslint-plugin-regexp: 2.9.0(eslint@9.32.0(jiti@2.5.1))
4998 - eslint-plugin-toml: 0.12.0(eslint@9.32.0(jiti@2.5.1))
4999 - eslint-plugin-unicorn: 60.0.0(eslint@9.32.0(jiti@2.5.1))
5000 - eslint-plugin-unused-imports: 4.1.4(@typescript-eslint/eslint-plugin@8.38.0(@typescript-eslint/parser@8.38.0(eslint@9.32.0(jiti@2.5.1))(typescript@5.8.3))(eslint@9.32.0(jiti@2.5.1))(typescript@5.8.3))(eslint@9.32.0(jiti@2.5.1))
5001 - eslint-plugin-vue: 10.3.0(@typescript-eslint/parser@8.38.0(eslint@9.32.0(jiti@2.5.1))(typescript@5.8.3))(eslint@9.32.0(jiti@2.5.1))(vue-eslint-parser@10.2.0(eslint@9.32.0(jiti@2.5.1)))
5002 - eslint-plugin-yml: 1.18.0(eslint@9.32.0(jiti@2.5.1))
5003 - eslint-processor-vue-blocks: 2.0.0(@vue/compiler-sfc@3.5.18)(eslint@9.32.0(jiti@2.5.1))
5004 - globals: 16.3.0
5047 + eslint-plugin-perfectionist: 4.15.0(eslint@9.35.0(jiti@2.5.1))(typescript@5.9.2)
5048 + eslint-plugin-pnpm: 1.1.1(eslint@9.35.0(jiti@2.5.1))
5049 + eslint-plugin-regexp: 2.10.0(eslint@9.35.0(jiti@2.5.1))
5050 + eslint-plugin-toml: 0.12.0(eslint@9.35.0(jiti@2.5.1))
5051 + eslint-plugin-unicorn: 61.0.2(eslint@9.35.0(jiti@2.5.1))
5052 + eslint-plugin-unused-imports: 4.2.0(@typescript-eslint/eslint-plugin@8.44.0(@typescript-eslint/parser@8.44.0(eslint@9.35.0(jiti@2.5.1))(typescript@5.9.2))(eslint@9.35.0(jiti@2.5.1))(typescript@5.9.2))(eslint@9.35.0(jiti@2.5.1))
5053 + eslint-plugin-vue: 10.4.0(@typescript-eslint/parser@8.44.0(eslint@9.35.0(jiti@2.5.1))(typescript@5.9.2))(eslint@9.35.0(jiti@2.5.1))(vue-eslint-parser@10.2.0(eslint@9.35.0(jiti@2.5.1)))
5054 + eslint-plugin-yml: 1.18.0(eslint@9.35.0(jiti@2.5.1))
5055 + eslint-processor-vue-blocks: 2.0.0(@vue/compiler-sfc@3.5.21)(eslint@9.35.0(jiti@2.5.1))
5056 + globals: 16.4.0
5057 jsonc-eslint-parser: 2.4.0
5006 - local-pkg: 1.1.1
5058 + local-pkg: 1.1.2
5059 parse-gitignore: 2.0.0
5060 toml-eslint-parser: 0.10.0
5009 - vue-eslint-parser: 10.2.0(eslint@9.32.0(jiti@2.5.1))
5061 + vue-eslint-parser: 10.2.0(eslint@9.35.0(jiti@2.5.1))
5062 yaml-eslint-parser: 1.3.0
5063 transitivePeerDependencies:
5064 - '@eslint/json'
@@ -5020,20 +5072,29 @@ snapshots:
5072 package-manager-detector: 1.3.0
5073 tinyexec: 1.0.1
5074
5023 - '@antfu/ni@24.4.0':
5075 + '@antfu/ni@25.0.0':
5076 dependencies:
5077 ansis: 4.1.0
5078 fzf: 0.5.2
5079 package-manager-detector: 1.3.0
5080 tinyexec: 1.0.1
5081
5030 - '@asamuzakjp/css-color@3.2.0':
5082 + '@asamuzakjp/css-color@4.0.4':
5083 dependencies:
5084 '@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)
5033 - '@csstools/css-color-parser': 3.0.10(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4)
5085 + '@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)
5086 '@csstools/css-parser-algorithms': 3.0.5(@csstools/css-tokenizer@3.0.4)
5087 '@csstools/css-tokenizer': 3.0.4
5036 - lru-cache: 10.4.3
5088 + lru-cache: 11.2.1
5089 +
5090 + '@asamuzakjp/dom-selector@6.5.5':
5091 + dependencies:
5092 + '@asamuzakjp/nwsapi': 2.3.9
5093 + bidi-js: 1.0.3
5094 + css-tree: 3.1.0
5095 + is-potential-custom-element-name: 1.0.1
5096 +
5097 + '@asamuzakjp/nwsapi@2.3.9': {}
5098
5099 '@babel/code-frame@7.27.1':
5100 dependencies:
@@ -5041,57 +5102,57 @@ snapshots:
5102 js-tokens: 4.0.0
5103 picocolors: 1.1.1
5104
5044 - '@babel/compat-data@7.28.0': {}
5105 + '@babel/compat-data@7.28.4': {}
5106
5046 - '@babel/core@7.28.0':
5107 + '@babel/core@7.28.4':
5108 dependencies:
5048 - '@ampproject/remapping': 2.3.0
5109 '@babel/code-frame': 7.27.1
5050 - '@babel/generator': 7.28.0
5110 + '@babel/generator': 7.28.3
5111 '@babel/helper-compilation-targets': 7.27.2
5052 - '@babel/helper-module-transforms': 7.27.3(@babel/core@7.28.0)
5053 - '@babel/helpers': 7.28.2
5054 - '@babel/parser': 7.28.0
5112 + '@babel/helper-module-transforms': 7.28.3(@babel/core@7.28.4)
5113 + '@babel/helpers': 7.28.4
5114 + '@babel/parser': 7.28.4
5115 '@babel/template': 7.27.2
5056 - '@babel/traverse': 7.28.0
5057 - '@babel/types': 7.28.2
5116 + '@babel/traverse': 7.28.4
5117 + '@babel/types': 7.28.4
5118 + '@jridgewell/remapping': 2.3.5
5119 convert-source-map: 2.0.0
5059 - debug: 4.4.1(supports-color@8.1.1)
5120 + debug: 4.4.3(supports-color@8.1.1)
5121 gensync: 1.0.0-beta.2
5122 json5: 2.2.3
5123 semver: 6.3.1
5124 transitivePeerDependencies:
5125 - supports-color
5126
5066 - '@babel/generator@7.28.0':
5127 + '@babel/generator@7.28.3':
5128 dependencies:
5068 - '@babel/parser': 7.28.0
5069 - '@babel/types': 7.28.2
5070 - '@jridgewell/gen-mapping': 0.3.12
5071 - '@jridgewell/trace-mapping': 0.3.29
5129 + '@babel/parser': 7.28.4
5130 + '@babel/types': 7.28.4
5131 + '@jridgewell/gen-mapping': 0.3.13
5132 + '@jridgewell/trace-mapping': 0.3.31
5133 jsesc: 3.1.0
5134
5135 '@babel/helper-annotate-as-pure@7.27.3':
5136 dependencies:
5076 - '@babel/types': 7.28.2
5137 + '@babel/types': 7.28.4
5138
5139 '@babel/helper-compilation-targets@7.27.2':
5140 dependencies:
5080 - '@babel/compat-data': 7.28.0
5141 + '@babel/compat-data': 7.28.4
5142 '@babel/helper-validator-option': 7.27.1
5082 - browserslist: 4.25.1
5143 + browserslist: 4.26.2
5144 lru-cache: 5.1.1
5145 semver: 6.3.1
5146
5086 - '@babel/helper-create-class-features-plugin@7.27.1(@babel/core@7.28.0)':
5147 + '@babel/helper-create-class-features-plugin@7.28.3(@babel/core@7.28.4)':
5148 dependencies:
5088 - '@babel/core': 7.28.0
5149 + '@babel/core': 7.28.4
5150 '@babel/helper-annotate-as-pure': 7.27.3
5151 '@babel/helper-member-expression-to-functions': 7.27.1
5152 '@babel/helper-optimise-call-expression': 7.27.1
5092 - '@babel/helper-replace-supers': 7.27.1(@babel/core@7.28.0)
5153 + '@babel/helper-replace-supers': 7.27.1(@babel/core@7.28.4)
5154 '@babel/helper-skip-transparent-expression-wrappers': 7.27.1
5094 - '@babel/traverse': 7.28.0
5155 + '@babel/traverse': 7.28.4
5156 semver: 6.3.1
5157 transitivePeerDependencies:
5158 - supports-color
@@ -5100,46 +5161,46 @@ snapshots:
5161
5162 '@babel/helper-member-expression-to-functions@7.27.1':
5163 dependencies:
5103 - '@babel/traverse': 7.28.0
5104 - '@babel/types': 7.28.2
5164 + '@babel/traverse': 7.28.4
5165 + '@babel/types': 7.28.4
5166 transitivePeerDependencies:
5167 - supports-color
5168
5169 '@babel/helper-module-imports@7.27.1':
5170 dependencies:
5110 - '@babel/traverse': 7.28.0
5111 - '@babel/types': 7.28.2
5171 + '@babel/traverse': 7.28.4
5172 + '@babel/types': 7.28.4
5173 transitivePeerDependencies:
5174 - supports-color
5175
5115 - '@babel/helper-module-transforms@7.27.3(@babel/core@7.28.0)':
5176 + '@babel/helper-module-transforms@7.28.3(@babel/core@7.28.4)':
5177 dependencies:
5117 - '@babel/core': 7.28.0
5178 + '@babel/core': 7.28.4
5179 '@babel/helper-module-imports': 7.27.1
5180 '@babel/helper-validator-identifier': 7.27.1
5120 - '@babel/traverse': 7.28.0
5181 + '@babel/traverse': 7.28.4
5182 transitivePeerDependencies:
5183 - supports-color
5184
5185 '@babel/helper-optimise-call-expression@7.27.1':
5186 dependencies:
5126 - '@babel/types': 7.28.2
5187 + '@babel/types': 7.28.4
5188
5189 '@babel/helper-plugin-utils@7.27.1': {}
5190
5130 - '@babel/helper-replace-supers@7.27.1(@babel/core@7.28.0)':
5191 + '@babel/helper-replace-supers@7.27.1(@babel/core@7.28.4)':
5192 dependencies:
5132 - '@babel/core': 7.28.0
5193 + '@babel/core': 7.28.4
5194 '@babel/helper-member-expression-to-functions': 7.27.1
5195 '@babel/helper-optimise-call-expression': 7.27.1
5135 - '@babel/traverse': 7.28.0
5196 + '@babel/traverse': 7.28.4
5197 transitivePeerDependencies:
5198 - supports-color
5199
5200 '@babel/helper-skip-transparent-expression-wrappers@7.27.1':
5201 dependencies:
5141 - '@babel/traverse': 7.28.0
5142 - '@babel/types': 7.28.2
5202 + '@babel/traverse': 7.28.4
5203 + '@babel/types': 7.28.4
5204 transitivePeerDependencies:
5205 - supports-color
5206
@@ -5149,79 +5210,79 @@ snapshots:
5210
5211 '@babel/helper-validator-option@7.27.1': {}
5212
5152 - '@babel/helpers@7.28.2':
5213 + '@babel/helpers@7.28.4':
5214 dependencies:
5215 '@babel/template': 7.27.2
5155 - '@babel/types': 7.28.2
5216 + '@babel/types': 7.28.4
5217
5157 - '@babel/parser@7.28.0':
5218 + '@babel/parser@7.28.4':
5219 dependencies:
5159 - '@babel/types': 7.28.2
5220 + '@babel/types': 7.28.4
5221
5161 - '@babel/plugin-proposal-decorators@7.28.0(@babel/core@7.28.0)':
5222 + '@babel/plugin-proposal-decorators@7.28.0(@babel/core@7.28.4)':
5223 dependencies:
5163 - '@babel/core': 7.28.0
5164 - '@babel/helper-create-class-features-plugin': 7.27.1(@babel/core@7.28.0)
5224 + '@babel/core': 7.28.4
5225 + '@babel/helper-create-class-features-plugin': 7.28.3(@babel/core@7.28.4)
5226 '@babel/helper-plugin-utils': 7.27.1
5166 - '@babel/plugin-syntax-decorators': 7.27.1(@babel/core@7.28.0)
5227 + '@babel/plugin-syntax-decorators': 7.27.1(@babel/core@7.28.4)
5228 transitivePeerDependencies:
5229 - supports-color
5230
5170 - '@babel/plugin-syntax-decorators@7.27.1(@babel/core@7.28.0)':
5231 + '@babel/plugin-syntax-decorators@7.27.1(@babel/core@7.28.4)':
5232 dependencies:
5172 - '@babel/core': 7.28.0
5233 + '@babel/core': 7.28.4
5234 '@babel/helper-plugin-utils': 7.27.1
5235
5175 - '@babel/plugin-syntax-import-attributes@7.27.1(@babel/core@7.28.0)':
5236 + '@babel/plugin-syntax-import-attributes@7.27.1(@babel/core@7.28.4)':
5237 dependencies:
5177 - '@babel/core': 7.28.0
5238 + '@babel/core': 7.28.4
5239 '@babel/helper-plugin-utils': 7.27.1
5240
5180 - '@babel/plugin-syntax-import-meta@7.10.4(@babel/core@7.28.0)':
5241 + '@babel/plugin-syntax-import-meta@7.10.4(@babel/core@7.28.4)':
5242 dependencies:
5182 - '@babel/core': 7.28.0
5243 + '@babel/core': 7.28.4
5244 '@babel/helper-plugin-utils': 7.27.1
5245
5185 - '@babel/plugin-syntax-jsx@7.27.1(@babel/core@7.28.0)':
5246 + '@babel/plugin-syntax-jsx@7.27.1(@babel/core@7.28.4)':
5247 dependencies:
5187 - '@babel/core': 7.28.0
5248 + '@babel/core': 7.28.4
5249 '@babel/helper-plugin-utils': 7.27.1
5250
5190 - '@babel/plugin-syntax-typescript@7.27.1(@babel/core@7.28.0)':
5251 + '@babel/plugin-syntax-typescript@7.27.1(@babel/core@7.28.4)':
5252 dependencies:
5192 - '@babel/core': 7.28.0
5253 + '@babel/core': 7.28.4
5254 '@babel/helper-plugin-utils': 7.27.1
5255
5195 - '@babel/plugin-transform-typescript@7.28.0(@babel/core@7.28.0)':
5256 + '@babel/plugin-transform-typescript@7.28.0(@babel/core@7.28.4)':
5257 dependencies:
5197 - '@babel/core': 7.28.0
5258 + '@babel/core': 7.28.4
5259 '@babel/helper-annotate-as-pure': 7.27.3
5199 - '@babel/helper-create-class-features-plugin': 7.27.1(@babel/core@7.28.0)
5260 + '@babel/helper-create-class-features-plugin': 7.28.3(@babel/core@7.28.4)
5261 '@babel/helper-plugin-utils': 7.27.1
5262 '@babel/helper-skip-transparent-expression-wrappers': 7.27.1
5202 - '@babel/plugin-syntax-typescript': 7.27.1(@babel/core@7.28.0)
5263 + '@babel/plugin-syntax-typescript': 7.27.1(@babel/core@7.28.4)
5264 transitivePeerDependencies:
5265 - supports-color
5266
5267 '@babel/template@7.27.2':
5268 dependencies:
5269 '@babel/code-frame': 7.27.1
5209 - '@babel/parser': 7.28.0
5210 - '@babel/types': 7.28.2
5270 + '@babel/parser': 7.28.4
5271 + '@babel/types': 7.28.4
5272
5212 - '@babel/traverse@7.28.0':
5273 + '@babel/traverse@7.28.4':
5274 dependencies:
5275 '@babel/code-frame': 7.27.1
5215 - '@babel/generator': 7.28.0
5276 + '@babel/generator': 7.28.3
5277 '@babel/helper-globals': 7.28.0
5217 - '@babel/parser': 7.28.0
5278 + '@babel/parser': 7.28.4
5279 '@babel/template': 7.27.2
5219 - '@babel/types': 7.28.2
5220 - debug: 4.4.1(supports-color@8.1.1)
5280 + '@babel/types': 7.28.4
5281 + debug: 4.4.3(supports-color@8.1.1)
5282 transitivePeerDependencies:
5283 - supports-color
5284
5224 - '@babel/types@7.28.2':
5285 + '@babel/types@7.28.4':
5286 dependencies:
5287 '@babel/helper-string-parser': 7.27.1
5288 '@babel/helper-validator-identifier': 7.27.1
@@ -5237,43 +5298,43 @@ snapshots:
5298 picocolors: 1.1.1
5299 sisteransi: 1.0.5
5300
5240 - '@codemirror/autocomplete@6.18.6':
5301 + '@codemirror/autocomplete@6.18.7':
5302 dependencies:
5242 - '@codemirror/language': 6.11.2
5303 + '@codemirror/language': 6.11.3
5304 '@codemirror/state': 6.5.2
5244 - '@codemirror/view': 6.38.1
5305 + '@codemirror/view': 6.38.2
5306 '@lezer/common': 1.2.3
5307
5308 '@codemirror/commands@6.8.1':
5309 dependencies:
5249 - '@codemirror/language': 6.11.2
5310 + '@codemirror/language': 6.11.3
5311 '@codemirror/state': 6.5.2
5251 - '@codemirror/view': 6.38.1
5312 + '@codemirror/view': 6.38.2
5313 '@lezer/common': 1.2.3
5314
5315 '@codemirror/lang-javascript@6.2.4':
5316 dependencies:
5256 - '@codemirror/autocomplete': 6.18.6
5257 - '@codemirror/language': 6.11.2
5317 + '@codemirror/autocomplete': 6.18.7
5318 + '@codemirror/language': 6.11.3
5319 '@codemirror/lint': 6.8.5
5320 '@codemirror/state': 6.5.2
5260 - '@codemirror/view': 6.38.1
5321 + '@codemirror/view': 6.38.2
5322 '@lezer/common': 1.2.3
5262 - '@lezer/javascript': 1.5.1
5323 + '@lezer/javascript': 1.5.4
5324
5325 '@codemirror/lang-xml@6.1.0':
5326 dependencies:
5266 - '@codemirror/autocomplete': 6.18.6
5267 - '@codemirror/language': 6.11.2
5327 + '@codemirror/autocomplete': 6.18.7
5328 + '@codemirror/language': 6.11.3
5329 '@codemirror/state': 6.5.2
5269 - '@codemirror/view': 6.38.1
5330 + '@codemirror/view': 6.38.2
5331 '@lezer/common': 1.2.3
5332 '@lezer/xml': 1.0.6
5333
5273 - '@codemirror/language@6.11.2':
5334 + '@codemirror/language@6.11.3':
5335 dependencies:
5336 '@codemirror/state': 6.5.2
5276 - '@codemirror/view': 6.38.1
5337 + '@codemirror/view': 6.38.2
5338 '@lezer/common': 1.2.3
5339 '@lezer/highlight': 1.2.1
5340 '@lezer/lr': 1.4.2
@@ -5282,13 +5343,13 @@ snapshots:
5343 '@codemirror/lint@6.8.5':
5344 dependencies:
5345 '@codemirror/state': 6.5.2
5285 - '@codemirror/view': 6.38.1
5346 + '@codemirror/view': 6.38.2
5347 crelt: 1.0.6
5348
5349 '@codemirror/search@6.5.11':
5350 dependencies:
5351 '@codemirror/state': 6.5.2
5291 - '@codemirror/view': 6.38.1
5352 + '@codemirror/view': 6.38.2
5353 crelt: 1.0.6
5354
5355 '@codemirror/state@6.5.2':
@@ -5297,12 +5358,12 @@ snapshots:
5358
5359 '@codemirror/theme-one-dark@6.1.3':
5360 dependencies:
5300 - '@codemirror/language': 6.11.2
5361 + '@codemirror/language': 6.11.3
5362 '@codemirror/state': 6.5.2
5302 - '@codemirror/view': 6.38.1
5363 + '@codemirror/view': 6.38.2
5364 '@lezer/highlight': 1.2.1
5365
5305 - '@codemirror/view@6.38.1':
5366 + '@codemirror/view@6.38.2':
5367 dependencies:
5368 '@codemirror/state': 6.5.2
5369 crelt: 1.0.6
@@ -5313,20 +5374,20 @@ snapshots:
5374 dependencies:
5375 css-render: 0.15.14
5376
5316 - '@css-render/vue3-ssr@0.15.14(vue@3.5.18(typescript@5.8.3))':
5377 + '@css-render/vue3-ssr@0.15.14(vue@3.5.21(typescript@5.9.2))':
5378 dependencies:
5318 - vue: 3.5.18(typescript@5.8.3)
5379 + vue: 3.5.21(typescript@5.9.2)
5380
5320 - '@csstools/color-helpers@5.0.2': {}
5381 + '@csstools/color-helpers@5.1.0': {}
5382
5383 '@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)':
5384 dependencies:
5385 '@csstools/css-parser-algorithms': 3.0.5(@csstools/css-tokenizer@3.0.4)
5386 '@csstools/css-tokenizer': 3.0.4
5387
5327 - '@csstools/css-color-parser@3.0.10(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4)':
5388 + '@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)':
5389 dependencies:
5329 - '@csstools/color-helpers': 5.0.2
5390 + '@csstools/color-helpers': 5.1.0
5391 '@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)
5392 '@csstools/css-parser-algorithms': 3.0.5(@csstools/css-tokenizer@3.0.4)
5393 '@csstools/css-tokenizer': 3.0.4
@@ -5335,6 +5396,10 @@ snapshots:
5396 dependencies:
5397 '@csstools/css-tokenizer': 3.0.4
5398
5399 + '@csstools/css-syntax-patches-for-csstree@1.0.14(postcss@8.5.6)':
5400 + dependencies:
5401 + postcss: 8.5.6
5402 +
5403 '@csstools/css-tokenizer@3.0.4': {}
5404
5405 '@cypress/request@3.0.9':
@@ -5370,132 +5435,132 @@ snapshots:
5435 '@es-joy/jsdoccomment@0.50.2':
5436 dependencies:
5437 '@types/estree': 1.0.8
5373 - '@typescript-eslint/types': 8.38.0
5438 + '@typescript-eslint/types': 8.44.0
5439 comment-parser: 1.4.1
5440 esquery: 1.6.0
5441 jsdoc-type-pratt-parser: 4.1.0
5442
5378 - '@es-joy/jsdoccomment@0.52.0':
5443 + '@es-joy/jsdoccomment@0.56.0':
5444 dependencies:
5445 '@types/estree': 1.0.8
5381 - '@typescript-eslint/types': 8.38.0
5446 + '@typescript-eslint/types': 8.44.0
5447 comment-parser: 1.4.1
5448 esquery: 1.6.0
5384 - jsdoc-type-pratt-parser: 4.1.0
5449 + jsdoc-type-pratt-parser: 5.1.1
5450
5386 - '@esbuild/aix-ppc64@0.25.8':
5451 + '@esbuild/aix-ppc64@0.25.10':
5452 optional: true
5453
5389 - '@esbuild/android-arm64@0.25.8':
5454 + '@esbuild/android-arm64@0.25.10':
5455 optional: true
5456
5392 - '@esbuild/android-arm@0.25.8':
5457 + '@esbuild/android-arm@0.25.10':
5458 optional: true
5459
5395 - '@esbuild/android-x64@0.25.8':
5460 + '@esbuild/android-x64@0.25.10':
5461 optional: true
5462
5398 - '@esbuild/darwin-arm64@0.25.8':
5463 + '@esbuild/darwin-arm64@0.25.10':
5464 optional: true
5465
5401 - '@esbuild/darwin-x64@0.25.8':
5466 + '@esbuild/darwin-x64@0.25.10':
5467 optional: true
5468
5404 - '@esbuild/freebsd-arm64@0.25.8':
5469 + '@esbuild/freebsd-arm64@0.25.10':
5470 optional: true
5471
5407 - '@esbuild/freebsd-x64@0.25.8':
5472 + '@esbuild/freebsd-x64@0.25.10':
5473 optional: true
5474
5410 - '@esbuild/linux-arm64@0.25.8':
5475 + '@esbuild/linux-arm64@0.25.10':
5476 optional: true
5477
5413 - '@esbuild/linux-arm@0.25.8':
5478 + '@esbuild/linux-arm@0.25.10':
5479 optional: true
5480
5416 - '@esbuild/linux-ia32@0.25.8':
5481 + '@esbuild/linux-ia32@0.25.10':
5482 optional: true
5483
5419 - '@esbuild/linux-loong64@0.25.8':
5484 + '@esbuild/linux-loong64@0.25.10':
5485 optional: true
5486
5422 - '@esbuild/linux-mips64el@0.25.8':
5487 + '@esbuild/linux-mips64el@0.25.10':
5488 optional: true
5489
5425 - '@esbuild/linux-ppc64@0.25.8':
5490 + '@esbuild/linux-ppc64@0.25.10':
5491 optional: true
5492
5428 - '@esbuild/linux-riscv64@0.25.8':
5493 + '@esbuild/linux-riscv64@0.25.10':
5494 optional: true
5495
5431 - '@esbuild/linux-s390x@0.25.8':
5496 + '@esbuild/linux-s390x@0.25.10':
5497 optional: true
5498
5434 - '@esbuild/linux-x64@0.25.8':
5499 + '@esbuild/linux-x64@0.25.10':
5500 optional: true
5501
5437 - '@esbuild/netbsd-arm64@0.25.8':
5502 + '@esbuild/netbsd-arm64@0.25.10':
5503 optional: true
5504
5440 - '@esbuild/netbsd-x64@0.25.8':
5505 + '@esbuild/netbsd-x64@0.25.10':
5506 optional: true
5507
5443 - '@esbuild/openbsd-arm64@0.25.8':
5508 + '@esbuild/openbsd-arm64@0.25.10':
5509 optional: true
5510
5446 - '@esbuild/openbsd-x64@0.25.8':
5511 + '@esbuild/openbsd-x64@0.25.10':
5512 optional: true
5513
5449 - '@esbuild/openharmony-arm64@0.25.8':
5514 + '@esbuild/openharmony-arm64@0.25.10':
5515 optional: true
5516
5452 - '@esbuild/sunos-x64@0.25.8':
5517 + '@esbuild/sunos-x64@0.25.10':
5518 optional: true
5519
5455 - '@esbuild/win32-arm64@0.25.8':
5520 + '@esbuild/win32-arm64@0.25.10':
5521 optional: true
5522
5458 - '@esbuild/win32-ia32@0.25.8':
5523 + '@esbuild/win32-ia32@0.25.10':
5524 optional: true
5525
5461 - '@esbuild/win32-x64@0.25.8':
5526 + '@esbuild/win32-x64@0.25.10':
5527 optional: true
5528
5464 - '@eslint-community/eslint-plugin-eslint-comments@4.5.0(eslint@9.32.0(jiti@2.5.1))':
5529 + '@eslint-community/eslint-plugin-eslint-comments@4.5.0(eslint@9.35.0(jiti@2.5.1))':
5530 dependencies:
5531 escape-string-regexp: 4.0.0
5467 - eslint: 9.32.0(jiti@2.5.1)
5532 + eslint: 9.35.0(jiti@2.5.1)
5533 ignore: 5.3.2
5534
5470 - '@eslint-community/eslint-utils@4.7.0(eslint@9.32.0(jiti@2.5.1))':
5535 + '@eslint-community/eslint-utils@4.9.0(eslint@9.35.0(jiti@2.5.1))':
5536 dependencies:
5472 - eslint: 9.32.0(jiti@2.5.1)
5537 + eslint: 9.35.0(jiti@2.5.1)
5538 eslint-visitor-keys: 3.4.3
5539
5540 '@eslint-community/regexpp@4.12.1': {}
5541
5477 - '@eslint/compat@1.3.1(eslint@9.32.0(jiti@2.5.1))':
5542 + '@eslint/compat@1.3.2(eslint@9.35.0(jiti@2.5.1))':
5543 optionalDependencies:
5479 - eslint: 9.32.0(jiti@2.5.1)
5544 + eslint: 9.35.0(jiti@2.5.1)
5545
5546 '@eslint/config-array@0.21.0':
5547 dependencies:
5548 '@eslint/object-schema': 2.1.6
5484 - debug: 4.4.1(supports-color@8.1.1)
5549 + debug: 4.4.3(supports-color@8.1.1)
5550 minimatch: 3.1.2
5551 transitivePeerDependencies:
5552 - supports-color
5553
5489 - '@eslint/config-helpers@0.3.0': {}
5554 + '@eslint/config-helpers@0.3.1': {}
5555
5491 - '@eslint/core@0.15.1':
5556 + '@eslint/core@0.15.2':
5557 dependencies:
5558 '@types/json-schema': 7.0.15
5559
5560 '@eslint/eslintrc@3.3.1':
5561 dependencies:
5562 ajv: 6.12.6
5498 - debug: 4.4.1(supports-color@8.1.1)
5563 + debug: 4.4.3(supports-color@8.1.1)
5564 espree: 10.4.0
5565 globals: 14.0.0
5566 ignore: 5.3.2
@@ -5506,38 +5571,39 @@ snapshots:
5571 transitivePeerDependencies:
5572 - supports-color
5573
5509 - '@eslint/js@9.32.0': {}
5574 + '@eslint/js@9.35.0': {}
5575
5511 - '@eslint/markdown@7.1.0':
5576 + '@eslint/markdown@7.2.0':
5577 dependencies:
5513 - '@eslint/core': 0.15.1
5514 - '@eslint/plugin-kit': 0.3.4
5578 + '@eslint/core': 0.15.2
5579 + '@eslint/plugin-kit': 0.3.5
5580 github-slugger: 2.0.0
5581 mdast-util-from-markdown: 2.0.2
5582 mdast-util-frontmatter: 2.0.1
5583 mdast-util-gfm: 3.1.0
5584 micromark-extension-frontmatter: 2.0.0
5585 micromark-extension-gfm: 3.0.0
5586 + micromark-util-normalize-identifier: 2.0.1
5587 transitivePeerDependencies:
5588 - supports-color
5589
5590 '@eslint/object-schema@2.1.6': {}
5591
5526 - '@eslint/plugin-kit@0.3.4':
5592 + '@eslint/plugin-kit@0.3.5':
5593 dependencies:
5528 - '@eslint/core': 0.15.1
5594 + '@eslint/core': 0.15.2
5595 levn: 0.4.1
5596
5531 - '@f3ve/vue-markdown-it@0.2.3(vue@3.5.18(typescript@5.8.3))':
5597 + '@f3ve/vue-markdown-it@0.2.3(vue@3.5.21(typescript@5.9.2))':
5598 dependencies:
5599 markdown-it: 14.1.0
5534 - vue: 3.5.18(typescript@5.8.3)
5600 + vue: 3.5.21(typescript@5.9.2)
5601
5536 - '@fontsource/jetbrains-mono@5.2.6': {}
5602 + '@fontsource/jetbrains-mono@5.2.8': {}
5603
5538 - '@fontsource/lexend@5.2.9': {}
5604 + '@fontsource/lexend@5.2.11': {}
5605
5540 - '@fontsource/public-sans@5.2.6': {}
5606 + '@fontsource/public-sans@5.2.7': {}
5607
5608 '@hapi/hoek@9.3.0': {}
5609
@@ -5547,41 +5613,39 @@ snapshots:
5613
5614 '@humanfs/core@0.19.1': {}
5615
5550 - '@humanfs/node@0.16.6':
5616 + '@humanfs/node@0.16.7':
5617 dependencies:
5618 '@humanfs/core': 0.19.1
5553 - '@humanwhocodes/retry': 0.3.1
5619 + '@humanwhocodes/retry': 0.4.3
5620
5621 '@humanwhocodes/module-importer@1.0.1': {}
5622
5557 - '@humanwhocodes/retry@0.3.1': {}
5558 -
5623 '@humanwhocodes/retry@0.4.3': {}
5624
5625 '@iconify/types@2.0.0': {}
5626
5563 - '@iconify/vue@5.0.0(vue@3.5.18(typescript@5.8.3))':
5627 + '@iconify/vue@5.0.0(vue@3.5.21(typescript@5.9.2))':
5628 dependencies:
5629 '@iconify/types': 2.0.0
5566 - vue: 3.5.18(typescript@5.8.3)
5630 + vue: 3.5.21(typescript@5.9.2)
5631
5568 - '@intlify/core-base@11.1.11':
5632 + '@intlify/core-base@11.1.12':
5633 dependencies:
5570 - '@intlify/message-compiler': 11.1.11
5571 - '@intlify/shared': 11.1.11
5634 + '@intlify/message-compiler': 11.1.12
5635 + '@intlify/shared': 11.1.12
5636
5573 - '@intlify/message-compiler@11.1.11':
5637 + '@intlify/message-compiler@11.1.12':
5638 dependencies:
5575 - '@intlify/shared': 11.1.11
5639 + '@intlify/shared': 11.1.12
5640 source-map-js: 1.2.1
5641
5578 - '@intlify/shared@11.1.11': {}
5642 + '@intlify/shared@11.1.12': {}
5643
5644 '@isaacs/cliui@8.0.2':
5645 dependencies:
5646 string-width: 5.1.2
5647 string-width-cjs: string-width@4.2.3
5584 - strip-ansi: 7.1.0
5648 + strip-ansi: 7.1.2
5649 strip-ansi-cjs: strip-ansi@6.0.1
5650 wrap-ansi: 8.1.0
5651 wrap-ansi-cjs: wrap-ansi@7.0.0
@@ -5590,19 +5654,24 @@ snapshots:
5654 dependencies:
5655 minipass: 7.1.2
5656
5593 - '@jridgewell/gen-mapping@0.3.12':
5657 + '@jridgewell/gen-mapping@0.3.13':
5658 + dependencies:
5659 + '@jridgewell/sourcemap-codec': 1.5.5
5660 + '@jridgewell/trace-mapping': 0.3.31
5661 +
5662 + '@jridgewell/remapping@2.3.5':
5663 dependencies:
5595 - '@jridgewell/sourcemap-codec': 1.5.4
5596 - '@jridgewell/trace-mapping': 0.3.29
5664 + '@jridgewell/gen-mapping': 0.3.13
5665 + '@jridgewell/trace-mapping': 0.3.31
5666
5667 '@jridgewell/resolve-uri@3.1.2': {}
5668
5600 - '@jridgewell/sourcemap-codec@1.5.4': {}
5669 + '@jridgewell/sourcemap-codec@1.5.5': {}
5670
5602 - '@jridgewell/trace-mapping@0.3.29':
5671 + '@jridgewell/trace-mapping@0.3.31':
5672 dependencies:
5673 '@jridgewell/resolve-uri': 3.1.2
5605 - '@jridgewell/sourcemap-codec': 1.5.4
5674 + '@jridgewell/sourcemap-codec': 1.5.5
5675
5676 '@juggle/resize-observer@3.4.0': {}
5677
@@ -5612,7 +5681,7 @@ snapshots:
5681 dependencies:
5682 '@lezer/common': 1.2.3
5683
5615 - '@lezer/javascript@1.5.1':
5684 + '@lezer/javascript@1.5.4':
5685 dependencies:
5686 '@lezer/common': 1.2.3
5687 '@lezer/highlight': 1.2.1
@@ -5642,9 +5711,9 @@ snapshots:
5711 '@nodelib/fs.scandir': 2.1.5
5712 fastq: 1.19.1
5713
5645 - '@nuxt/kit@3.18.0':
5714 + '@nuxt/kit@3.19.2':
5715 dependencies:
5647 - c12: 3.2.0
5716 + c12: 3.3.0
5717 consola: 3.4.2
5718 defu: 6.1.4
5719 destr: 2.0.5
@@ -5654,17 +5723,18 @@ snapshots:
5723 jiti: 2.5.1
5724 klona: 2.0.6
5725 knitwork: 1.2.0
5657 - mlly: 1.7.4
5726 + mlly: 1.8.0
5727 ohash: 2.0.11
5728 pathe: 2.0.3
5660 - pkg-types: 2.2.0
5729 + pkg-types: 2.3.0
5730 + rc9: 2.1.2
5731 scule: 1.3.0
5732 semver: 7.7.2
5733 std-env: 3.9.0
5664 - tinyglobby: 0.2.14
5734 + tinyglobby: 0.2.15
5735 ufo: 1.6.1
5736 unctx: 2.4.1
5667 - unimport: 5.2.0
5737 + unimport: 5.3.0
5738 untyped: 2.0.0
5739 transitivePeerDependencies:
5740 - magicast
@@ -5740,108 +5810,111 @@ snapshots:
5810
5811 '@polka/url@1.0.0-next.29': {}
5812
5743 - '@quansync/fs@0.1.3':
5813 + '@quansync/fs@0.1.5':
5814 dependencies:
5745 - quansync: 0.2.10
5815 + quansync: 0.2.11
5816
5817 '@rolldown/pluginutils@1.0.0-beta.29': {}
5818
5749 - '@rolldown/pluginutils@1.0.0-beta.30': {}
5819 + '@rolldown/pluginutils@1.0.0-beta.38': {}
5820
5751 - '@rollup/rollup-android-arm-eabi@4.46.2':
5821 + '@rollup/rollup-android-arm-eabi@4.50.2':
5822 optional: true
5823
5754 - '@rollup/rollup-android-arm64@4.46.2':
5824 + '@rollup/rollup-android-arm64@4.50.2':
5825 optional: true
5826
5757 - '@rollup/rollup-darwin-arm64@4.46.2':
5827 + '@rollup/rollup-darwin-arm64@4.50.2':
5828 optional: true
5829
5760 - '@rollup/rollup-darwin-x64@4.46.2':
5830 + '@rollup/rollup-darwin-x64@4.50.2':
5831 optional: true
5832
5763 - '@rollup/rollup-freebsd-arm64@4.46.2':
5833 + '@rollup/rollup-freebsd-arm64@4.50.2':
5834 optional: true
5835
5766 - '@rollup/rollup-freebsd-x64@4.46.2':
5836 + '@rollup/rollup-freebsd-x64@4.50.2':
5837 optional: true
5838
5769 - '@rollup/rollup-linux-arm-gnueabihf@4.46.2':
5839 + '@rollup/rollup-linux-arm-gnueabihf@4.50.2':
5840 optional: true
5841
5772 - '@rollup/rollup-linux-arm-musleabihf@4.46.2':
5842 + '@rollup/rollup-linux-arm-musleabihf@4.50.2':
5843 optional: true
5844
5775 - '@rollup/rollup-linux-arm64-gnu@4.46.2':
5845 + '@rollup/rollup-linux-arm64-gnu@4.50.2':
5846 optional: true
5847
5778 - '@rollup/rollup-linux-arm64-musl@4.46.2':
5848 + '@rollup/rollup-linux-arm64-musl@4.50.2':
5849 optional: true
5850
5781 - '@rollup/rollup-linux-loongarch64-gnu@4.46.2':
5851 + '@rollup/rollup-linux-loong64-gnu@4.50.2':
5852 optional: true
5853
5784 - '@rollup/rollup-linux-ppc64-gnu@4.46.2':
5854 + '@rollup/rollup-linux-ppc64-gnu@4.50.2':
5855 optional: true
5856
5787 - '@rollup/rollup-linux-riscv64-gnu@4.46.2':
5857 + '@rollup/rollup-linux-riscv64-gnu@4.50.2':
5858 optional: true
5859
5790 - '@rollup/rollup-linux-riscv64-musl@4.46.2':
5860 + '@rollup/rollup-linux-riscv64-musl@4.50.2':
5861 optional: true
5862
5793 - '@rollup/rollup-linux-s390x-gnu@4.46.2':
5863 + '@rollup/rollup-linux-s390x-gnu@4.50.2':
5864 optional: true
5865
5796 - '@rollup/rollup-linux-x64-gnu@4.46.2':
5866 + '@rollup/rollup-linux-x64-gnu@4.50.2':
5867 optional: true
5868
5799 - '@rollup/rollup-linux-x64-musl@4.46.2':
5869 + '@rollup/rollup-linux-x64-musl@4.50.2':
5870 optional: true
5871
5802 - '@rollup/rollup-win32-arm64-msvc@4.46.2':
5872 + '@rollup/rollup-openharmony-arm64@4.50.2':
5873 optional: true
5874
5805 - '@rollup/rollup-win32-ia32-msvc@4.46.2':
5875 + '@rollup/rollup-win32-arm64-msvc@4.50.2':
5876 optional: true
5877
5808 - '@rollup/rollup-win32-x64-msvc@4.46.2':
5878 + '@rollup/rollup-win32-ia32-msvc@4.50.2':
5879 + optional: true
5880 +
5881 + '@rollup/rollup-win32-x64-msvc@4.50.2':
5882 optional: true
5883
5884 '@sec-ant/readable-stream@0.4.1': {}
5885
5813 - '@shikijs/core@3.8.1':
5886 + '@shikijs/core@3.12.2':
5887 dependencies:
5815 - '@shikijs/types': 3.8.1
5888 + '@shikijs/types': 3.12.2
5889 '@shikijs/vscode-textmate': 10.0.2
5890 '@types/hast': 3.0.4
5891 hast-util-to-html: 9.0.5
5892
5820 - '@shikijs/engine-javascript@3.8.1':
5893 + '@shikijs/engine-javascript@3.12.2':
5894 dependencies:
5822 - '@shikijs/types': 3.8.1
5895 + '@shikijs/types': 3.12.2
5896 '@shikijs/vscode-textmate': 10.0.2
5897 oniguruma-to-es: 4.3.3
5898
5826 - '@shikijs/engine-oniguruma@3.8.1':
5899 + '@shikijs/engine-oniguruma@3.12.2':
5900 dependencies:
5828 - '@shikijs/types': 3.8.1
5901 + '@shikijs/types': 3.12.2
5902 '@shikijs/vscode-textmate': 10.0.2
5903
5831 - '@shikijs/langs@3.8.1':
5904 + '@shikijs/langs@3.12.2':
5905 dependencies:
5833 - '@shikijs/types': 3.8.1
5906 + '@shikijs/types': 3.12.2
5907
5835 - '@shikijs/markdown-it@3.8.1':
5908 + '@shikijs/markdown-it@3.12.2':
5909 dependencies:
5910 markdown-it: 14.1.0
5838 - shiki: 3.8.1
5911 + shiki: 3.12.2
5912
5840 - '@shikijs/themes@3.8.1':
5913 + '@shikijs/themes@3.12.2':
5914 dependencies:
5842 - '@shikijs/types': 3.8.1
5915 + '@shikijs/types': 3.12.2
5916
5844 - '@shikijs/types@3.8.1':
5917 + '@shikijs/types@3.12.2':
5918 dependencies:
5919 '@shikijs/vscode-textmate': 10.0.2
5920 '@types/hast': 3.0.4
@@ -5860,107 +5933,107 @@ snapshots:
5933
5934 '@singulio/app-auth-search@0.0.3':
5935 dependencies:
5863 - algoliasearch: 5.35.0
5936 + algoliasearch: 5.37.0
5937
5865 - '@stylistic/eslint-plugin@5.2.2(eslint@9.32.0(jiti@2.5.1))':
5938 + '@stylistic/eslint-plugin@5.3.1(eslint@9.35.0(jiti@2.5.1))':
5939 dependencies:
5867 - '@eslint-community/eslint-utils': 4.7.0(eslint@9.32.0(jiti@2.5.1))
5868 - '@typescript-eslint/types': 8.38.0
5869 - eslint: 9.32.0(jiti@2.5.1)
5940 + '@eslint-community/eslint-utils': 4.9.0(eslint@9.35.0(jiti@2.5.1))
5941 + '@typescript-eslint/types': 8.44.0
5942 + eslint: 9.35.0(jiti@2.5.1)
5943 eslint-visitor-keys: 4.2.1
5944 espree: 10.4.0
5945 estraverse: 5.3.0
5946 picomatch: 4.0.3
5947
5875 - '@svgdotjs/svg.draggable.js@3.0.6(@svgdotjs/svg.js@3.2.4)':
5948 + '@svgdotjs/svg.draggable.js@3.0.6(@svgdotjs/svg.js@3.2.5)':
5949 dependencies:
5877 - '@svgdotjs/svg.js': 3.2.4
5950 + '@svgdotjs/svg.js': 3.2.5
5951
5952 '@svgdotjs/svg.filter.js@3.0.9':
5953 dependencies:
5881 - '@svgdotjs/svg.js': 3.2.4
5954 + '@svgdotjs/svg.js': 3.2.5
5955
5883 - '@svgdotjs/svg.js@3.2.4': {}
5956 + '@svgdotjs/svg.js@3.2.5': {}
5957
5885 - '@svgdotjs/svg.resize.js@2.0.5(@svgdotjs/svg.js@3.2.4)(@svgdotjs/svg.select.js@4.0.3(@svgdotjs/svg.js@3.2.4))':
5958 + '@svgdotjs/svg.resize.js@2.0.5(@svgdotjs/svg.js@3.2.5)(@svgdotjs/svg.select.js@4.0.3(@svgdotjs/svg.js@3.2.5))':
5959 dependencies:
5887 - '@svgdotjs/svg.js': 3.2.4
5888 - '@svgdotjs/svg.select.js': 4.0.3(@svgdotjs/svg.js@3.2.4)
5960 + '@svgdotjs/svg.js': 3.2.5
5961 + '@svgdotjs/svg.select.js': 4.0.3(@svgdotjs/svg.js@3.2.5)
5962
5890 - '@svgdotjs/svg.select.js@4.0.3(@svgdotjs/svg.js@3.2.4)':
5963 + '@svgdotjs/svg.select.js@4.0.3(@svgdotjs/svg.js@3.2.5)':
5964 dependencies:
5892 - '@svgdotjs/svg.js': 3.2.4
5965 + '@svgdotjs/svg.js': 3.2.5
5966
5894 - '@tailwindcss/node@4.1.11':
5967 + '@tailwindcss/node@4.1.13':
5968 dependencies:
5896 - '@ampproject/remapping': 2.3.0
5897 - enhanced-resolve: 5.18.2
5969 + '@jridgewell/remapping': 2.3.5
5970 + enhanced-resolve: 5.18.3
5971 jiti: 2.5.1
5972 lightningcss: 1.30.1
5900 - magic-string: 0.30.17
5973 + magic-string: 0.30.19
5974 source-map-js: 1.2.1
5902 - tailwindcss: 4.1.11
5975 + tailwindcss: 4.1.13
5976
5904 - '@tailwindcss/oxide-android-arm64@4.1.11':
5977 + '@tailwindcss/oxide-android-arm64@4.1.13':
5978 optional: true
5979
5907 - '@tailwindcss/oxide-darwin-arm64@4.1.11':
5980 + '@tailwindcss/oxide-darwin-arm64@4.1.13':
5981 optional: true
5982
5910 - '@tailwindcss/oxide-darwin-x64@4.1.11':
5983 + '@tailwindcss/oxide-darwin-x64@4.1.13':
5984 optional: true
5985
5913 - '@tailwindcss/oxide-freebsd-x64@4.1.11':
5986 + '@tailwindcss/oxide-freebsd-x64@4.1.13':
5987 optional: true
5988
5916 - '@tailwindcss/oxide-linux-arm-gnueabihf@4.1.11':
5989 + '@tailwindcss/oxide-linux-arm-gnueabihf@4.1.13':
5990 optional: true
5991
5919 - '@tailwindcss/oxide-linux-arm64-gnu@4.1.11':
5992 + '@tailwindcss/oxide-linux-arm64-gnu@4.1.13':
5993 optional: true
5994
5922 - '@tailwindcss/oxide-linux-arm64-musl@4.1.11':
5995 + '@tailwindcss/oxide-linux-arm64-musl@4.1.13':
5996 optional: true
5997
5925 - '@tailwindcss/oxide-linux-x64-gnu@4.1.11':
5998 + '@tailwindcss/oxide-linux-x64-gnu@4.1.13':
5999 optional: true
6000
5928 - '@tailwindcss/oxide-linux-x64-musl@4.1.11':
6001 + '@tailwindcss/oxide-linux-x64-musl@4.1.13':
6002 optional: true
6003
5931 - '@tailwindcss/oxide-wasm32-wasi@4.1.11':
6004 + '@tailwindcss/oxide-wasm32-wasi@4.1.13':
6005 optional: true
6006
5934 - '@tailwindcss/oxide-win32-arm64-msvc@4.1.11':
6007 + '@tailwindcss/oxide-win32-arm64-msvc@4.1.13':
6008 optional: true
6009
5937 - '@tailwindcss/oxide-win32-x64-msvc@4.1.11':
6010 + '@tailwindcss/oxide-win32-x64-msvc@4.1.13':
6011 optional: true
6012
5940 - '@tailwindcss/oxide@4.1.11':
6013 + '@tailwindcss/oxide@4.1.13':
6014 dependencies:
5942 - detect-libc: 2.0.4
6015 + detect-libc: 2.1.0
6016 tar: 7.4.3
6017 optionalDependencies:
5945 - '@tailwindcss/oxide-android-arm64': 4.1.11
5946 - '@tailwindcss/oxide-darwin-arm64': 4.1.11
5947 - '@tailwindcss/oxide-darwin-x64': 4.1.11
5948 - '@tailwindcss/oxide-freebsd-x64': 4.1.11
5949 - '@tailwindcss/oxide-linux-arm-gnueabihf': 4.1.11
5950 - '@tailwindcss/oxide-linux-arm64-gnu': 4.1.11
5951 - '@tailwindcss/oxide-linux-arm64-musl': 4.1.11
5952 - '@tailwindcss/oxide-linux-x64-gnu': 4.1.11
5953 - '@tailwindcss/oxide-linux-x64-musl': 4.1.11
5954 - '@tailwindcss/oxide-wasm32-wasi': 4.1.11
5955 - '@tailwindcss/oxide-win32-arm64-msvc': 4.1.11
5956 - '@tailwindcss/oxide-win32-x64-msvc': 4.1.11
5957 -
5958 - '@tailwindcss/vite@4.1.11(vite@7.0.6(@types/node@24.1.0)(jiti@2.5.1)(lightningcss@1.30.1)(sass@1.89.2)(yaml@2.8.0))':
5959 - dependencies:
5960 - '@tailwindcss/node': 4.1.11
5961 - '@tailwindcss/oxide': 4.1.11
5962 - tailwindcss: 4.1.11
5963 - vite: 7.0.6(@types/node@24.1.0)(jiti@2.5.1)(lightningcss@1.30.1)(sass@1.89.2)(yaml@2.8.0)
6018 + '@tailwindcss/oxide-android-arm64': 4.1.13
6019 + '@tailwindcss/oxide-darwin-arm64': 4.1.13
6020 + '@tailwindcss/oxide-darwin-x64': 4.1.13
6021 + '@tailwindcss/oxide-freebsd-x64': 4.1.13
6022 + '@tailwindcss/oxide-linux-arm-gnueabihf': 4.1.13
6023 + '@tailwindcss/oxide-linux-arm64-gnu': 4.1.13
6024 + '@tailwindcss/oxide-linux-arm64-musl': 4.1.13
6025 + '@tailwindcss/oxide-linux-x64-gnu': 4.1.13
6026 + '@tailwindcss/oxide-linux-x64-musl': 4.1.13
6027 + '@tailwindcss/oxide-wasm32-wasi': 4.1.13
6028 + '@tailwindcss/oxide-win32-arm64-msvc': 4.1.13
6029 + '@tailwindcss/oxide-win32-x64-msvc': 4.1.13
6030 +
6031 + '@tailwindcss/vite@4.1.13(vite@7.1.6(@types/node@24.5.2)(jiti@2.5.1)(lightningcss@1.30.1)(sass@1.92.1)(yaml@2.8.1))':
6032 + dependencies:
6033 + '@tailwindcss/node': 4.1.13
6034 + '@tailwindcss/oxide': 4.1.13
6035 + tailwindcss: 4.1.13
6036 + vite: 7.1.6(@types/node@24.5.2)(jiti@2.5.1)(lightningcss@1.30.1)(sass@1.92.1)(yaml@2.8.1)
6037
6038 '@trysound/sax@0.2.0': {}
6039
@@ -5989,7 +6062,7 @@ snapshots:
6062 '@types/fs-extra@11.0.4':
6063 dependencies:
6064 '@types/jsonfile': 6.1.4
5992 - '@types/node': 24.1.0
6065 + '@types/node': 24.5.2
6066
6067 '@types/hast@3.0.4':
6068 dependencies:
@@ -5997,7 +6070,7 @@ snapshots:
6070
6071 '@types/jsdom@21.1.7':
6072 dependencies:
6000 - '@types/node': 24.1.0
6073 + '@types/node': 24.5.2
6074 '@types/tough-cookie': 4.0.5
6075 parse5: 7.3.0
6076
@@ -6005,7 +6078,7 @@ snapshots:
6078
6079 '@types/jsonfile@6.1.4':
6080 dependencies:
6008 - '@types/node': 24.1.0
6081 + '@types/node': 24.5.2
6082
6083 '@types/katex@0.16.7': {}
6084
@@ -6032,15 +6105,15 @@ snapshots:
6105
6106 '@types/ms@2.1.0': {}
6107
6035 - '@types/node@24.1.0':
6108 + '@types/node@24.5.2':
6109 dependencies:
6037 - undici-types: 7.8.0
6110 + undici-types: 7.12.0
6111
6112 '@types/parse-json@4.0.2': {}
6113
6114 '@types/sinonjs__fake-timers@8.1.1': {}
6115
6043 - '@types/sizzle@2.3.9': {}
6116 + '@types/sizzle@2.3.10': {}
6117
6118 '@types/tern@0.23.9':
6119 dependencies:
@@ -6050,134 +6123,136 @@ snapshots:
6123
6124 '@types/unist@3.0.3': {}
6125
6053 - '@types/validator@13.15.2': {}
6126 + '@types/validator@13.15.3': {}
6127
6128 '@types/web-bluetooth@0.0.21': {}
6129
6130 '@types/yauzl@2.10.3':
6131 dependencies:
6059 - '@types/node': 24.1.0
6132 + '@types/node': 24.5.2
6133 optional: true
6134
6062 - '@typescript-eslint/eslint-plugin@8.38.0(@typescript-eslint/parser@8.38.0(eslint@9.32.0(jiti@2.5.1))(typescript@5.8.3))(eslint@9.32.0(jiti@2.5.1))(typescript@5.8.3)':
6135 + '@typescript-eslint/eslint-plugin@8.44.0(@typescript-eslint/parser@8.44.0(eslint@9.35.0(jiti@2.5.1))(typescript@5.9.2))(eslint@9.35.0(jiti@2.5.1))(typescript@5.9.2)':
6136 dependencies:
6137 '@eslint-community/regexpp': 4.12.1
6065 - '@typescript-eslint/parser': 8.38.0(eslint@9.32.0(jiti@2.5.1))(typescript@5.8.3)
6066 - '@typescript-eslint/scope-manager': 8.38.0
6067 - '@typescript-eslint/type-utils': 8.38.0(eslint@9.32.0(jiti@2.5.1))(typescript@5.8.3)
6068 - '@typescript-eslint/utils': 8.38.0(eslint@9.32.0(jiti@2.5.1))(typescript@5.8.3)
6069 - '@typescript-eslint/visitor-keys': 8.38.0
6070 - eslint: 9.32.0(jiti@2.5.1)
6138 + '@typescript-eslint/parser': 8.44.0(eslint@9.35.0(jiti@2.5.1))(typescript@5.9.2)
6139 + '@typescript-eslint/scope-manager': 8.44.0
6140 + '@typescript-eslint/type-utils': 8.44.0(eslint@9.35.0(jiti@2.5.1))(typescript@5.9.2)
6141 + '@typescript-eslint/utils': 8.44.0(eslint@9.35.0(jiti@2.5.1))(typescript@5.9.2)
6142 + '@typescript-eslint/visitor-keys': 8.44.0
6143 + eslint: 9.35.0(jiti@2.5.1)
6144 graphemer: 1.4.0
6145 ignore: 7.0.5
6146 natural-compare: 1.4.0
6074 - ts-api-utils: 2.1.0(typescript@5.8.3)
6075 - typescript: 5.8.3
6147 + ts-api-utils: 2.1.0(typescript@5.9.2)
6148 + typescript: 5.9.2
6149 transitivePeerDependencies:
6150 - supports-color
6151
6079 - '@typescript-eslint/parser@8.38.0(eslint@9.32.0(jiti@2.5.1))(typescript@5.8.3)':
6152 + '@typescript-eslint/parser@8.44.0(eslint@9.35.0(jiti@2.5.1))(typescript@5.9.2)':
6153 dependencies:
6081 - '@typescript-eslint/scope-manager': 8.38.0
6082 - '@typescript-eslint/types': 8.38.0
6083 - '@typescript-eslint/typescript-estree': 8.38.0(typescript@5.8.3)
6084 - '@typescript-eslint/visitor-keys': 8.38.0
6085 - debug: 4.4.1(supports-color@8.1.1)
6086 - eslint: 9.32.0(jiti@2.5.1)
6087 - typescript: 5.8.3
6154 + '@typescript-eslint/scope-manager': 8.44.0
6155 + '@typescript-eslint/types': 8.44.0
6156 + '@typescript-eslint/typescript-estree': 8.44.0(typescript@5.9.2)
6157 + '@typescript-eslint/visitor-keys': 8.44.0
6158 + debug: 4.4.3(supports-color@8.1.1)
6159 + eslint: 9.35.0(jiti@2.5.1)
6160 + typescript: 5.9.2
6161 transitivePeerDependencies:
6162 - supports-color
6163
6091 - '@typescript-eslint/project-service@8.38.0(typescript@5.8.3)':
6164 + '@typescript-eslint/project-service@8.44.0(typescript@5.9.2)':
6165 dependencies:
6093 - '@typescript-eslint/tsconfig-utils': 8.38.0(typescript@5.8.3)
6094 - '@typescript-eslint/types': 8.38.0
6095 - debug: 4.4.1(supports-color@8.1.1)
6096 - typescript: 5.8.3
6166 + '@typescript-eslint/tsconfig-utils': 8.44.0(typescript@5.9.2)
6167 + '@typescript-eslint/types': 8.44.0
6168 + debug: 4.4.3(supports-color@8.1.1)
6169 + typescript: 5.9.2
6170 transitivePeerDependencies:
6171 - supports-color
6172
6100 - '@typescript-eslint/scope-manager@8.38.0':
6173 + '@typescript-eslint/scope-manager@8.44.0':
6174 dependencies:
6102 - '@typescript-eslint/types': 8.38.0
6103 - '@typescript-eslint/visitor-keys': 8.38.0
6175 + '@typescript-eslint/types': 8.44.0
6176 + '@typescript-eslint/visitor-keys': 8.44.0
6177
6105 - '@typescript-eslint/tsconfig-utils@8.38.0(typescript@5.8.3)':
6178 + '@typescript-eslint/tsconfig-utils@8.44.0(typescript@5.9.2)':
6179 dependencies:
6107 - typescript: 5.8.3
6180 + typescript: 5.9.2
6181
6109 - '@typescript-eslint/type-utils@8.38.0(eslint@9.32.0(jiti@2.5.1))(typescript@5.8.3)':
6182 + '@typescript-eslint/type-utils@8.44.0(eslint@9.35.0(jiti@2.5.1))(typescript@5.9.2)':
6183 dependencies:
6111 - '@typescript-eslint/types': 8.38.0
6112 - '@typescript-eslint/typescript-estree': 8.38.0(typescript@5.8.3)
6113 - '@typescript-eslint/utils': 8.38.0(eslint@9.32.0(jiti@2.5.1))(typescript@5.8.3)
6114 - debug: 4.4.1(supports-color@8.1.1)
6115 - eslint: 9.32.0(jiti@2.5.1)
6116 - ts-api-utils: 2.1.0(typescript@5.8.3)
6117 - typescript: 5.8.3
6184 + '@typescript-eslint/types': 8.44.0
6185 + '@typescript-eslint/typescript-estree': 8.44.0(typescript@5.9.2)
6186 + '@typescript-eslint/utils': 8.44.0(eslint@9.35.0(jiti@2.5.1))(typescript@5.9.2)
6187 + debug: 4.4.3(supports-color@8.1.1)
6188 + eslint: 9.35.0(jiti@2.5.1)
6189 + ts-api-utils: 2.1.0(typescript@5.9.2)
6190 + typescript: 5.9.2
6191 transitivePeerDependencies:
6192 - supports-color
6193
6121 - '@typescript-eslint/types@8.38.0': {}
6194 + '@typescript-eslint/types@8.44.0': {}
6195
6123 - '@typescript-eslint/typescript-estree@8.38.0(typescript@5.8.3)':
6196 + '@typescript-eslint/typescript-estree@8.44.0(typescript@5.9.2)':
6197 dependencies:
6125 - '@typescript-eslint/project-service': 8.38.0(typescript@5.8.3)
6126 - '@typescript-eslint/tsconfig-utils': 8.38.0(typescript@5.8.3)
6127 - '@typescript-eslint/types': 8.38.0
6128 - '@typescript-eslint/visitor-keys': 8.38.0
6129 - debug: 4.4.1(supports-color@8.1.1)
6198 + '@typescript-eslint/project-service': 8.44.0(typescript@5.9.2)
6199 + '@typescript-eslint/tsconfig-utils': 8.44.0(typescript@5.9.2)
6200 + '@typescript-eslint/types': 8.44.0
6201 + '@typescript-eslint/visitor-keys': 8.44.0
6202 + debug: 4.4.3(supports-color@8.1.1)
6203 fast-glob: 3.3.3
6204 is-glob: 4.0.3
6205 minimatch: 9.0.5
6206 semver: 7.7.2
6134 - ts-api-utils: 2.1.0(typescript@5.8.3)
6135 - typescript: 5.8.3
6207 + ts-api-utils: 2.1.0(typescript@5.9.2)
6208 + typescript: 5.9.2
6209 transitivePeerDependencies:
6210 - supports-color
6211
6139 - '@typescript-eslint/utils@8.38.0(eslint@9.32.0(jiti@2.5.1))(typescript@5.8.3)':
6212 + '@typescript-eslint/utils@8.44.0(eslint@9.35.0(jiti@2.5.1))(typescript@5.9.2)':
6213 dependencies:
6141 - '@eslint-community/eslint-utils': 4.7.0(eslint@9.32.0(jiti@2.5.1))
6142 - '@typescript-eslint/scope-manager': 8.38.0
6143 - '@typescript-eslint/types': 8.38.0
6144 - '@typescript-eslint/typescript-estree': 8.38.0(typescript@5.8.3)
6145 - eslint: 9.32.0(jiti@2.5.1)
6146 - typescript: 5.8.3
6214 + '@eslint-community/eslint-utils': 4.9.0(eslint@9.35.0(jiti@2.5.1))
6215 + '@typescript-eslint/scope-manager': 8.44.0
6216 + '@typescript-eslint/types': 8.44.0
6217 + '@typescript-eslint/typescript-estree': 8.44.0(typescript@5.9.2)
6218 + eslint: 9.35.0(jiti@2.5.1)
6219 + typescript: 5.9.2
6220 transitivePeerDependencies:
6221 - supports-color
6222
6150 - '@typescript-eslint/visitor-keys@8.38.0':
6223 + '@typescript-eslint/visitor-keys@8.44.0':
6224 dependencies:
6152 - '@typescript-eslint/types': 8.38.0
6225 + '@typescript-eslint/types': 8.44.0
6226 eslint-visitor-keys: 4.2.1
6227
6228 '@ungap/structured-clone@1.3.0': {}
6229
6157 - '@vitejs/plugin-vue-jsx@5.0.1(vite@7.0.6(@types/node@24.1.0)(jiti@2.5.1)(lightningcss@1.30.1)(sass@1.89.2)(yaml@2.8.0))(vue@3.5.18(typescript@5.8.3))':
6230 + '@vitejs/plugin-vue-jsx@5.1.1(vite@7.1.6(@types/node@24.5.2)(jiti@2.5.1)(lightningcss@1.30.1)(sass@1.92.1)(yaml@2.8.1))(vue@3.5.21(typescript@5.9.2))':
6231 dependencies:
6159 - '@babel/core': 7.28.0
6160 - '@babel/plugin-transform-typescript': 7.28.0(@babel/core@7.28.0)
6161 - '@rolldown/pluginutils': 1.0.0-beta.30
6162 - '@vue/babel-plugin-jsx': 1.4.0(@babel/core@7.28.0)
6163 - vite: 7.0.6(@types/node@24.1.0)(jiti@2.5.1)(lightningcss@1.30.1)(sass@1.89.2)(yaml@2.8.0)
6164 - vue: 3.5.18(typescript@5.8.3)
6232 + '@babel/core': 7.28.4
6233 + '@babel/plugin-syntax-typescript': 7.27.1(@babel/core@7.28.4)
6234 + '@babel/plugin-transform-typescript': 7.28.0(@babel/core@7.28.4)
6235 + '@rolldown/pluginutils': 1.0.0-beta.38
6236 + '@vue/babel-plugin-jsx': 1.5.0(@babel/core@7.28.4)
6237 + vite: 7.1.6(@types/node@24.5.2)(jiti@2.5.1)(lightningcss@1.30.1)(sass@1.92.1)(yaml@2.8.1)
6238 + vue: 3.5.21(typescript@5.9.2)
6239 transitivePeerDependencies:
6240 - supports-color
6241
6168 - '@vitejs/plugin-vue@6.0.1(vite@7.0.6(@types/node@24.1.0)(jiti@2.5.1)(lightningcss@1.30.1)(sass@1.89.2)(yaml@2.8.0))(vue@3.5.18(typescript@5.8.3))':
6242 + '@vitejs/plugin-vue@6.0.1(vite@7.1.6(@types/node@24.5.2)(jiti@2.5.1)(lightningcss@1.30.1)(sass@1.92.1)(yaml@2.8.1))(vue@3.5.21(typescript@5.9.2))':
6243 dependencies:
6244 '@rolldown/pluginutils': 1.0.0-beta.29
6171 - vite: 7.0.6(@types/node@24.1.0)(jiti@2.5.1)(lightningcss@1.30.1)(sass@1.89.2)(yaml@2.8.0)
6172 - vue: 3.5.18(typescript@5.8.3)
6245 + vite: 7.1.6(@types/node@24.5.2)(jiti@2.5.1)(lightningcss@1.30.1)(sass@1.92.1)(yaml@2.8.1)
6246 + vue: 3.5.21(typescript@5.9.2)
6247
6174 - '@vitest/eslint-plugin@1.3.4(eslint@9.32.0(jiti@2.5.1))(typescript@5.8.3)(vitest@3.2.4(@types/debug@4.1.12)(@types/node@24.1.0)(jiti@2.5.1)(jsdom@26.1.0)(lightningcss@1.30.1)(sass@1.89.2)(yaml@2.8.0))':
6248 + '@vitest/eslint-plugin@1.3.12(eslint@9.35.0(jiti@2.5.1))(typescript@5.9.2)(vitest@3.2.4(@types/debug@4.1.12)(@types/node@24.5.2)(jiti@2.5.1)(jsdom@27.0.0(postcss@8.5.6))(lightningcss@1.30.1)(sass@1.92.1)(yaml@2.8.1))':
6249 dependencies:
6176 - '@typescript-eslint/utils': 8.38.0(eslint@9.32.0(jiti@2.5.1))(typescript@5.8.3)
6177 - eslint: 9.32.0(jiti@2.5.1)
6250 + '@typescript-eslint/scope-manager': 8.44.0
6251 + '@typescript-eslint/utils': 8.44.0(eslint@9.35.0(jiti@2.5.1))(typescript@5.9.2)
6252 + eslint: 9.35.0(jiti@2.5.1)
6253 optionalDependencies:
6179 - typescript: 5.8.3
6180 - vitest: 3.2.4(@types/debug@4.1.12)(@types/node@24.1.0)(jiti@2.5.1)(jsdom@26.1.0)(lightningcss@1.30.1)(sass@1.89.2)(yaml@2.8.0)
6254 + typescript: 5.9.2
6255 + vitest: 3.2.4(@types/debug@4.1.12)(@types/node@24.5.2)(jiti@2.5.1)(jsdom@27.0.0(postcss@8.5.6))(lightningcss@1.30.1)(sass@1.92.1)(yaml@2.8.1)
6256 transitivePeerDependencies:
6257 - supports-color
6258
@@ -6186,16 +6261,16 @@ snapshots:
6261 '@types/chai': 5.2.2
6262 '@vitest/spy': 3.2.4
6263 '@vitest/utils': 3.2.4
6189 - chai: 5.2.1
6264 + chai: 5.3.3
6265 tinyrainbow: 2.0.0
6266
6192 - '@vitest/mocker@3.2.4(vite@7.0.6(@types/node@24.1.0)(jiti@2.5.1)(lightningcss@1.30.1)(sass@1.89.2)(yaml@2.8.0))':
6267 + '@vitest/mocker@3.2.4(vite@7.1.6(@types/node@24.5.2)(jiti@2.5.1)(lightningcss@1.30.1)(sass@1.92.1)(yaml@2.8.1))':
6268 dependencies:
6269 '@vitest/spy': 3.2.4
6270 estree-walker: 3.0.3
6196 - magic-string: 0.30.17
6271 + magic-string: 0.30.19
6272 optionalDependencies:
6198 - vite: 7.0.6(@types/node@24.1.0)(jiti@2.5.1)(lightningcss@1.30.1)(sass@1.89.2)(yaml@2.8.0)
6273 + vite: 7.1.6(@types/node@24.5.2)(jiti@2.5.1)(lightningcss@1.30.1)(sass@1.92.1)(yaml@2.8.1)
6274
6275 '@vitest/pretty-format@3.2.4':
6276 dependencies:
@@ -6210,89 +6285,89 @@ snapshots:
6285 '@vitest/snapshot@3.2.4':
6286 dependencies:
6287 '@vitest/pretty-format': 3.2.4
6213 - magic-string: 0.30.17
6288 + magic-string: 0.30.19
6289 pathe: 2.0.3
6290
6291 '@vitest/spy@3.2.4':
6292 dependencies:
6218 - tinyspy: 4.0.3
6293 + tinyspy: 4.0.4
6294
6295 '@vitest/utils@3.2.4':
6296 dependencies:
6297 '@vitest/pretty-format': 3.2.4
6223 - loupe: 3.2.0
6298 + loupe: 3.2.1
6299 tinyrainbow: 2.0.0
6300
6226 - '@volar/language-core@2.4.20':
6301 + '@volar/language-core@2.4.23':
6302 dependencies:
6228 - '@volar/source-map': 2.4.20
6303 + '@volar/source-map': 2.4.23
6304
6230 - '@volar/source-map@2.4.20': {}
6305 + '@volar/source-map@2.4.23': {}
6306
6232 - '@volar/typescript@2.4.20':
6307 + '@volar/typescript@2.4.23':
6308 dependencies:
6234 - '@volar/language-core': 2.4.20
6309 + '@volar/language-core': 2.4.23
6310 path-browserify: 1.0.1
6311 vscode-uri: 3.1.0
6312
6238 - '@vue/babel-helper-vue-transform-on@1.4.0': {}
6313 + '@vue/babel-helper-vue-transform-on@1.5.0': {}
6314
6240 - '@vue/babel-plugin-jsx@1.4.0(@babel/core@7.28.0)':
6315 + '@vue/babel-plugin-jsx@1.5.0(@babel/core@7.28.4)':
6316 dependencies:
6317 '@babel/helper-module-imports': 7.27.1
6318 '@babel/helper-plugin-utils': 7.27.1
6244 - '@babel/plugin-syntax-jsx': 7.27.1(@babel/core@7.28.0)
6319 + '@babel/plugin-syntax-jsx': 7.27.1(@babel/core@7.28.4)
6320 '@babel/template': 7.27.2
6246 - '@babel/traverse': 7.28.0
6247 - '@babel/types': 7.28.2
6248 - '@vue/babel-helper-vue-transform-on': 1.4.0
6249 - '@vue/babel-plugin-resolve-type': 1.4.0(@babel/core@7.28.0)
6250 - '@vue/shared': 3.5.18
6321 + '@babel/traverse': 7.28.4
6322 + '@babel/types': 7.28.4
6323 + '@vue/babel-helper-vue-transform-on': 1.5.0
6324 + '@vue/babel-plugin-resolve-type': 1.5.0(@babel/core@7.28.4)
6325 + '@vue/shared': 3.5.21
6326 optionalDependencies:
6252 - '@babel/core': 7.28.0
6327 + '@babel/core': 7.28.4
6328 transitivePeerDependencies:
6329 - supports-color
6330
6256 - '@vue/babel-plugin-resolve-type@1.4.0(@babel/core@7.28.0)':
6331 + '@vue/babel-plugin-resolve-type@1.5.0(@babel/core@7.28.4)':
6332 dependencies:
6333 '@babel/code-frame': 7.27.1
6259 - '@babel/core': 7.28.0
6334 + '@babel/core': 7.28.4
6335 '@babel/helper-module-imports': 7.27.1
6336 '@babel/helper-plugin-utils': 7.27.1
6262 - '@babel/parser': 7.28.0
6263 - '@vue/compiler-sfc': 3.5.18
6337 + '@babel/parser': 7.28.4
6338 + '@vue/compiler-sfc': 3.5.21
6339 transitivePeerDependencies:
6340 - supports-color
6341
6267 - '@vue/compiler-core@3.5.18':
6342 + '@vue/compiler-core@3.5.21':
6343 dependencies:
6269 - '@babel/parser': 7.28.0
6270 - '@vue/shared': 3.5.18
6344 + '@babel/parser': 7.28.4
6345 + '@vue/shared': 3.5.21
6346 entities: 4.5.0
6347 estree-walker: 2.0.2
6348 source-map-js: 1.2.1
6349
6275 - '@vue/compiler-dom@3.5.18':
6350 + '@vue/compiler-dom@3.5.21':
6351 dependencies:
6277 - '@vue/compiler-core': 3.5.18
6278 - '@vue/shared': 3.5.18
6352 + '@vue/compiler-core': 3.5.21
6353 + '@vue/shared': 3.5.21
6354
6280 - '@vue/compiler-sfc@3.5.18':
6355 + '@vue/compiler-sfc@3.5.21':
6356 dependencies:
6282 - '@babel/parser': 7.28.0
6283 - '@vue/compiler-core': 3.5.18
6284 - '@vue/compiler-dom': 3.5.18
6285 - '@vue/compiler-ssr': 3.5.18
6286 - '@vue/shared': 3.5.18
6357 + '@babel/parser': 7.28.4
6358 + '@vue/compiler-core': 3.5.21
6359 + '@vue/compiler-dom': 3.5.21
6360 + '@vue/compiler-ssr': 3.5.21
6361 + '@vue/shared': 3.5.21
6362 estree-walker: 2.0.2
6288 - magic-string: 0.30.17
6363 + magic-string: 0.30.19
6364 postcss: 8.5.6
6365 source-map-js: 1.2.1
6366
6292 - '@vue/compiler-ssr@3.5.18':
6367 + '@vue/compiler-ssr@3.5.21':
6368 dependencies:
6294 - '@vue/compiler-dom': 3.5.18
6295 - '@vue/shared': 3.5.18
6369 + '@vue/compiler-dom': 3.5.21
6370 + '@vue/shared': 3.5.21
6371
6372 '@vue/compiler-vue2@2.7.16':
6373 dependencies:
@@ -6305,15 +6380,15 @@ snapshots:
6380 dependencies:
6381 '@vue/devtools-kit': 7.7.7
6382
6308 - '@vue/devtools-core@8.0.0(vite@7.0.6(@types/node@24.1.0)(jiti@2.5.1)(lightningcss@1.30.1)(sass@1.89.2)(yaml@2.8.0))(vue@3.5.18(typescript@5.8.3))':
6383 + '@vue/devtools-core@8.0.2(vite@7.1.6(@types/node@24.5.2)(jiti@2.5.1)(lightningcss@1.30.1)(sass@1.92.1)(yaml@2.8.1))(vue@3.5.21(typescript@5.9.2))':
6384 dependencies:
6310 - '@vue/devtools-kit': 8.0.0
6311 - '@vue/devtools-shared': 8.0.0
6385 + '@vue/devtools-kit': 8.0.2
6386 + '@vue/devtools-shared': 8.0.2
6387 mitt: 3.0.1
6388 nanoid: 5.1.5
6389 pathe: 2.0.3
6315 - vite-hot-client: 2.1.0(vite@7.0.6(@types/node@24.1.0)(jiti@2.5.1)(lightningcss@1.30.1)(sass@1.89.2)(yaml@2.8.0))
6316 - vue: 3.5.18(typescript@5.8.3)
6390 + vite-hot-client: 2.1.0(vite@7.1.6(@types/node@24.5.2)(jiti@2.5.1)(lightningcss@1.30.1)(sass@1.92.1)(yaml@2.8.1))
6391 + vue: 3.5.21(typescript@5.9.2)
6392 transitivePeerDependencies:
6393 - vite
6394
@@ -6327,13 +6402,13 @@ snapshots:
6402 speakingurl: 14.0.1
6403 superjson: 2.2.2
6404
6330 - '@vue/devtools-kit@8.0.0':
6405 + '@vue/devtools-kit@8.0.2':
6406 dependencies:
6332 - '@vue/devtools-shared': 8.0.0
6407 + '@vue/devtools-shared': 8.0.2
6408 birpc: 2.5.0
6409 hookable: 5.5.3
6410 mitt: 3.0.1
6336 - perfect-debounce: 1.0.0
6411 + perfect-debounce: 2.0.0
6412 speakingurl: 14.0.1
6413 superjson: 2.2.2
6414
@@ -6341,83 +6416,83 @@ snapshots:
6416 dependencies:
6417 rfdc: 1.4.1
6418
6344 - '@vue/devtools-shared@8.0.0':
6419 + '@vue/devtools-shared@8.0.2':
6420 dependencies:
6421 rfdc: 1.4.1
6422
6348 - '@vue/language-core@3.0.4(typescript@5.8.3)':
6423 + '@vue/language-core@3.0.7(typescript@5.9.2)':
6424 dependencies:
6350 - '@volar/language-core': 2.4.20
6351 - '@vue/compiler-dom': 3.5.18
6425 + '@volar/language-core': 2.4.23
6426 + '@vue/compiler-dom': 3.5.21
6427 '@vue/compiler-vue2': 2.7.16
6353 - '@vue/shared': 3.5.18
6354 - alien-signals: 2.0.5
6428 + '@vue/shared': 3.5.21
6429 + alien-signals: 2.0.7
6430 muggle-string: 0.4.1
6431 path-browserify: 1.0.1
6432 picomatch: 4.0.3
6433 optionalDependencies:
6359 - typescript: 5.8.3
6434 + typescript: 5.9.2
6435
6361 - '@vue/reactivity@3.5.18':
6436 + '@vue/reactivity@3.5.21':
6437 dependencies:
6363 - '@vue/shared': 3.5.18
6438 + '@vue/shared': 3.5.21
6439
6365 - '@vue/runtime-core@3.5.18':
6440 + '@vue/runtime-core@3.5.21':
6441 dependencies:
6367 - '@vue/reactivity': 3.5.18
6368 - '@vue/shared': 3.5.18
6442 + '@vue/reactivity': 3.5.21
6443 + '@vue/shared': 3.5.21
6444
6370 - '@vue/runtime-dom@3.5.18':
6445 + '@vue/runtime-dom@3.5.21':
6446 dependencies:
6372 - '@vue/reactivity': 3.5.18
6373 - '@vue/runtime-core': 3.5.18
6374 - '@vue/shared': 3.5.18
6447 + '@vue/reactivity': 3.5.21
6448 + '@vue/runtime-core': 3.5.21
6449 + '@vue/shared': 3.5.21
6450 csstype: 3.1.3
6451
6377 - '@vue/server-renderer@3.5.18(vue@3.5.18(typescript@5.8.3))':
6452 + '@vue/server-renderer@3.5.21(vue@3.5.21(typescript@5.9.2))':
6453 dependencies:
6379 - '@vue/compiler-ssr': 3.5.18
6380 - '@vue/shared': 3.5.18
6381 - vue: 3.5.18(typescript@5.8.3)
6454 + '@vue/compiler-ssr': 3.5.21
6455 + '@vue/shared': 3.5.21
6456 + vue: 3.5.21(typescript@5.9.2)
6457
6383 - '@vue/shared@3.5.18': {}
6458 + '@vue/shared@3.5.21': {}
6459
6460 '@vue/test-utils@2.4.6':
6461 dependencies:
6462 js-beautify: 1.15.4
6463 vue-component-type-helpers: 2.2.12
6464
6390 - '@vue/tsconfig@0.7.0(typescript@5.8.3)(vue@3.5.18(typescript@5.8.3))':
6465 + '@vue/tsconfig@0.7.0(typescript@5.9.2)(vue@3.5.21(typescript@5.9.2))':
6466 optionalDependencies:
6392 - typescript: 5.8.3
6393 - vue: 3.5.18(typescript@5.8.3)
6467 + typescript: 5.9.2
6468 + vue: 3.5.21(typescript@5.9.2)
6469
6395 - '@vueuse/core@13.6.0(vue@3.5.18(typescript@5.8.3))':
6470 + '@vueuse/core@13.9.0(vue@3.5.21(typescript@5.9.2))':
6471 dependencies:
6472 '@types/web-bluetooth': 0.0.21
6398 - '@vueuse/metadata': 13.6.0
6399 - '@vueuse/shared': 13.6.0(vue@3.5.18(typescript@5.8.3))
6400 - vue: 3.5.18(typescript@5.8.3)
6473 + '@vueuse/metadata': 13.9.0
6474 + '@vueuse/shared': 13.9.0(vue@3.5.21(typescript@5.9.2))
6475 + vue: 3.5.21(typescript@5.9.2)
6476
6402 - '@vueuse/metadata@13.6.0': {}
6477 + '@vueuse/metadata@13.9.0': {}
6478
6404 - '@vueuse/motion@3.0.3(vue@3.5.18(typescript@5.8.3))':
6479 + '@vueuse/motion@3.0.3(vue@3.5.21(typescript@5.9.2))':
6480 dependencies:
6406 - '@vueuse/core': 13.6.0(vue@3.5.18(typescript@5.8.3))
6407 - '@vueuse/shared': 13.6.0(vue@3.5.18(typescript@5.8.3))
6481 + '@vueuse/core': 13.9.0(vue@3.5.21(typescript@5.9.2))
6482 + '@vueuse/shared': 13.9.0(vue@3.5.21(typescript@5.9.2))
6483 defu: 6.1.4
6484 framesync: 6.1.2
6485 popmotion: 11.0.5
6486 style-value-types: 5.1.2
6412 - vue: 3.5.18(typescript@5.8.3)
6487 + vue: 3.5.21(typescript@5.9.2)
6488 optionalDependencies:
6414 - '@nuxt/kit': 3.18.0
6489 + '@nuxt/kit': 3.19.2
6490 transitivePeerDependencies:
6491 - magicast
6492
6418 - '@vueuse/shared@13.6.0(vue@3.5.18(typescript@5.8.3))':
6493 + '@vueuse/shared@13.9.0(vue@3.5.21(typescript@5.9.2))':
6494 dependencies:
6420 - vue: 3.5.18(typescript@5.8.3)
6495 + vue: 3.5.21(typescript@5.9.2)
6496
6497 '@yr/monotone-cubic-spline@1.0.3': {}
6498
@@ -6443,24 +6518,24 @@ snapshots:
6518 json-schema-traverse: 0.4.1
6519 uri-js: 4.4.1
6520
6446 - algoliasearch@5.35.0:
6447 - dependencies:
6448 - '@algolia/abtesting': 1.1.0
6449 - '@algolia/client-abtesting': 5.35.0
6450 - '@algolia/client-analytics': 5.35.0
6451 - '@algolia/client-common': 5.35.0
6452 - '@algolia/client-insights': 5.35.0
6453 - '@algolia/client-personalization': 5.35.0
6454 - '@algolia/client-query-suggestions': 5.35.0
6455 - '@algolia/client-search': 5.35.0
6456 - '@algolia/ingestion': 1.35.0
6457 - '@algolia/monitoring': 1.35.0
6458 - '@algolia/recommend': 5.35.0
6459 - '@algolia/requester-browser-xhr': 5.35.0
6460 - '@algolia/requester-fetch': 5.35.0
6461 - '@algolia/requester-node-http': 5.35.0
6462 -
6463 - alien-signals@2.0.5: {}
6521 + algoliasearch@5.37.0:
6522 + dependencies:
6523 + '@algolia/abtesting': 1.3.0
6524 + '@algolia/client-abtesting': 5.37.0
6525 + '@algolia/client-analytics': 5.37.0
6526 + '@algolia/client-common': 5.37.0
6527 + '@algolia/client-insights': 5.37.0
6528 + '@algolia/client-personalization': 5.37.0
6529 + '@algolia/client-query-suggestions': 5.37.0
6530 + '@algolia/client-search': 5.37.0
6531 + '@algolia/ingestion': 1.37.0
6532 + '@algolia/monitoring': 1.37.0
6533 + '@algolia/recommend': 5.37.0
6534 + '@algolia/requester-browser-xhr': 5.37.0
6535 + '@algolia/requester-fetch': 5.37.0
6536 + '@algolia/requester-node-http': 5.37.0
6537 +
6538 + alien-signals@2.0.7: {}
6539
6540 ansi-colors@4.1.3: {}
6541
@@ -6470,23 +6545,23 @@ snapshots:
6545
6546 ansi-regex@5.0.1: {}
6547
6473 - ansi-regex@6.1.0: {}
6548 + ansi-regex@6.2.2: {}
6549
6550 ansi-styles@4.3.0:
6551 dependencies:
6552 color-convert: 2.0.1
6553
6479 - ansi-styles@6.2.1: {}
6554 + ansi-styles@6.2.3: {}
6555
6556 ansis@4.1.0: {}
6557
6483 - apexcharts@5.3.2:
6558 + apexcharts@5.3.5:
6559 dependencies:
6485 - '@svgdotjs/svg.draggable.js': 3.0.6(@svgdotjs/svg.js@3.2.4)
6560 + '@svgdotjs/svg.draggable.js': 3.0.6(@svgdotjs/svg.js@3.2.5)
6561 '@svgdotjs/svg.filter.js': 3.0.9
6487 - '@svgdotjs/svg.js': 3.2.4
6488 - '@svgdotjs/svg.resize.js': 2.0.5(@svgdotjs/svg.js@3.2.4)(@svgdotjs/svg.select.js@4.0.3(@svgdotjs/svg.js@3.2.4))
6489 - '@svgdotjs/svg.select.js': 4.0.3(@svgdotjs/svg.js@3.2.4)
6562 + '@svgdotjs/svg.js': 3.2.5
6563 + '@svgdotjs/svg.resize.js': 2.0.5(@svgdotjs/svg.js@3.2.5)(@svgdotjs/svg.select.js@4.0.3(@svgdotjs/svg.js@3.2.5))
6564 + '@svgdotjs/svg.select.js': 4.0.3(@svgdotjs/svg.js@3.2.5)
6565 '@yr/monotone-cubic-spline': 1.0.3
6566
6567 arch@2.2.0: {}
@@ -6519,8 +6594,6 @@ snapshots:
6594
6595 async-validator@4.2.5: {}
6596
6522 - async@3.2.6: {}
6523 -
6597 asynckit@0.4.0: {}
6598
6599 at-least-node@1.0.0: {}
@@ -6529,9 +6602,9 @@ snapshots:
6602
6603 aws4@1.13.2: {}
6604
6532 - axios@1.11.0(debug@4.4.1):
6605 + axios@1.12.2(debug@4.4.3):
6606 dependencies:
6534 - follow-redirects: 1.15.9(debug@4.4.1)
6607 + follow-redirects: 1.15.11(debug@4.4.3)
6608 form-data: 4.0.4
6609 proxy-from-env: 1.1.0
6610 transitivePeerDependencies:
@@ -6541,10 +6614,16 @@ snapshots:
6614
6615 base64-js@1.5.1: {}
6616
6617 + baseline-browser-mapping@2.8.6: {}
6618 +
6619 bcrypt-pbkdf@1.0.2:
6620 dependencies:
6621 tweetnacl: 0.14.5
6622
6623 + bidi-js@1.0.3:
6624 + dependencies:
6625 + require-from-string: 2.0.2
6626 +
6627 birpc@2.5.0: {}
6628
6629 blob-util@2.0.2: {}
@@ -6566,12 +6645,13 @@ snapshots:
6645 dependencies:
6646 fill-range: 7.1.1
6647
6569 - browserslist@4.25.1:
6648 + browserslist@4.26.2:
6649 dependencies:
6571 - caniuse-lite: 1.0.30001731
6572 - electron-to-chromium: 1.5.192
6573 - node-releases: 2.0.19
6574 - update-browserslist-db: 1.1.3(browserslist@4.25.1)
6650 + baseline-browser-mapping: 2.8.6
6651 + caniuse-lite: 1.0.30001743
6652 + electron-to-chromium: 1.5.222
6653 + node-releases: 2.0.21
6654 + update-browserslist-db: 1.1.3(browserslist@4.26.2)
6655
6656 buffer-crc32@0.2.13: {}
6657
@@ -6584,23 +6664,23 @@ snapshots:
6664
6665 bundle-name@4.1.0:
6666 dependencies:
6587 - run-applescript: 7.0.0
6667 + run-applescript: 7.1.0
6668
6669 bytes@3.1.2: {}
6670
6591 - c12@3.2.0:
6671 + c12@3.3.0:
6672 dependencies:
6673 chokidar: 4.0.3
6674 confbox: 0.2.2
6675 defu: 6.1.4
6596 - dotenv: 17.2.1
6676 + dotenv: 17.2.2
6677 exsolve: 1.0.7
6678 giget: 2.0.0
6679 jiti: 2.5.1
6680 ohash: 2.0.11
6681 pathe: 2.0.3
6602 - perfect-debounce: 1.0.0
6603 - pkg-types: 2.2.0
6682 + perfect-debounce: 2.0.0
6683 + pkg-types: 2.3.0
6684 rc9: 2.1.2
6685 optional: true
6686
@@ -6624,18 +6704,18 @@ snapshots:
6704
6705 camelcase@6.3.0: {}
6706
6627 - caniuse-lite@1.0.30001731: {}
6707 + caniuse-lite@1.0.30001743: {}
6708
6709 caseless@0.12.0: {}
6710
6711 ccount@2.0.1: {}
6712
6633 - chai@5.2.1:
6713 + chai@5.3.3:
6714 dependencies:
6715 assertion-error: 2.0.1
6716 check-error: 2.1.1
6717 deep-eql: 5.0.2
6638 - loupe: 3.2.0
6718 + loupe: 3.2.1
6719 pathval: 2.0.1
6720
6721 chalk@4.1.2:
@@ -6705,13 +6785,13 @@ snapshots:
6785
6786 codemirror@6.0.2:
6787 dependencies:
6708 - '@codemirror/autocomplete': 6.18.6
6788 + '@codemirror/autocomplete': 6.18.7
6789 '@codemirror/commands': 6.8.1
6710 - '@codemirror/language': 6.11.2
6790 + '@codemirror/language': 6.11.3
6791 '@codemirror/lint': 6.8.5
6792 '@codemirror/search': 6.5.11
6793 '@codemirror/state': 6.5.2
6714 - '@codemirror/view': 6.38.1
6794 + '@codemirror/view': 6.38.2
6795
6796 color-convert@2.0.1:
6797 dependencies:
@@ -6762,9 +6842,9 @@ snapshots:
6842 dependencies:
6843 is-what: 4.1.16
6844
6765 - core-js-compat@3.44.0:
6845 + core-js-compat@3.45.1:
6846 dependencies:
6767 - browserslist: 4.25.1
6847 + browserslist: 4.26.2
6848
6849 core-util-is@1.0.2: {}
6850
@@ -6809,6 +6889,11 @@ snapshots:
6889 mdn-data: 2.0.30
6890 source-map-js: 1.2.1
6891
6892 + css-tree@3.1.0:
6893 + dependencies:
6894 + mdn-data: 2.12.2
6895 + source-map-js: 1.2.1
6896 +
6897 css-what@6.2.2: {}
6898
6899 cssesc@3.0.0: {}
@@ -6817,21 +6902,24 @@ snapshots:
6902 dependencies:
6903 css-tree: 2.2.1
6904
6820 - cssstyle@4.6.0:
6905 + cssstyle@5.3.0(postcss@8.5.6):
6906 dependencies:
6822 - '@asamuzakjp/css-color': 3.2.0
6823 - rrweb-cssom: 0.8.0
6907 + '@asamuzakjp/css-color': 4.0.4
6908 + '@csstools/css-syntax-patches-for-csstree': 1.0.14(postcss@8.5.6)
6909 + css-tree: 3.1.0
6910 + transitivePeerDependencies:
6911 + - postcss
6912
6913 csstype@3.0.11: {}
6914
6915 csstype@3.1.3: {}
6916
6829 - cypress@14.5.3:
6917 + cypress@15.2.0:
6918 dependencies:
6919 '@cypress/request': 3.0.9
6920 '@cypress/xvfb': 1.2.4(supports-color@8.1.1)
6921 '@types/sinonjs__fake-timers': 8.1.1
6834 - '@types/sizzle': 2.3.9
6922 + '@types/sizzle': 2.3.10
6923 arch: 2.2.0
6924 blob-util: 2.0.2
6925 bluebird: 3.7.2
@@ -6844,8 +6932,8 @@ snapshots:
6932 cli-table3: 0.6.1
6933 commander: 6.2.1
6934 common-tags: 1.8.2
6847 - dayjs: 1.11.13
6848 - debug: 4.4.1(supports-color@8.1.1)
6935 + dayjs: 1.11.18
6936 + debug: 4.4.3(supports-color@8.1.1)
6937 enquirer: 2.4.1
6938 eventemitter2: 6.4.7
6939 execa: 4.1.0
@@ -6853,7 +6941,6 @@ snapshots:
6941 extract-zip: 2.0.1(supports-color@8.1.1)
6942 figures: 3.2.0
6943 fs-extra: 9.1.0
6856 - getos: 3.2.1
6944 hasha: 5.2.2
6945 is-installed-globally: 0.4.0
6946 lazy-ass: 1.6.0
@@ -6868,7 +6955,8 @@ snapshots:
6955 request-progress: 3.0.0
6956 semver: 7.7.2
6957 supports-color: 8.1.1
6871 - tmp: 0.2.3
6958 + systeminformation: 5.27.7
6959 + tmp: 0.2.5
6960 tree-kill: 1.2.2
6961 untildify: 4.0.0
6962 yauzl: 2.10.0
@@ -6877,10 +6965,10 @@ snapshots:
6965 dependencies:
6966 assert-plus: 1.0.0
6967
6880 - data-urls@5.0.0:
6968 + data-urls@6.0.0:
6969 dependencies:
6970 whatwg-mimetype: 4.0.0
6883 - whatwg-url: 14.2.0
6971 + whatwg-url: 15.1.0
6972
6973 date-fns-tz@3.2.0(date-fns@3.6.0):
6974 dependencies:
@@ -6888,7 +6976,7 @@ snapshots:
6976
6977 date-fns@3.6.0: {}
6978
6891 - dayjs@1.11.13: {}
6979 + dayjs@1.11.18: {}
6980
6981 de-indent@1.0.2: {}
6982
@@ -6900,7 +6988,7 @@ snapshots:
6988 optionalDependencies:
6989 supports-color: 8.1.1
6990
6903 - debug@4.4.1(supports-color@8.1.1):
6991 + debug@4.4.3(supports-color@8.1.1):
6992 dependencies:
6993 ms: 2.1.3
6994 optionalDependencies:
@@ -6935,13 +7023,13 @@ snapshots:
7023
7024 depcheck@1.4.7:
7025 dependencies:
6938 - '@babel/parser': 7.28.0
6939 - '@babel/traverse': 7.28.0
6940 - '@vue/compiler-sfc': 3.5.18
7026 + '@babel/parser': 7.28.4
7027 + '@babel/traverse': 7.28.4
7028 + '@vue/compiler-sfc': 3.5.21
7029 callsite: 1.0.0
7030 camelcase: 6.3.0
7031 cosmiconfig: 7.1.0
6944 - debug: 4.4.1(supports-color@8.1.1)
7032 + debug: 4.4.3(supports-color@8.1.1)
7033 deps-regex: 0.2.0
7034 findup-sync: 5.0.0
7035 ignore: 5.3.2
@@ -6972,7 +7060,7 @@ snapshots:
7060 detect-libc@1.0.3:
7061 optional: true
7062
6975 - detect-libc@2.0.4: {}
7063 + detect-libc@2.1.0: {}
7064
7065 detect-touch-device@1.1.6: {}
7066
@@ -6998,7 +7086,7 @@ snapshots:
7086 domelementtype: 2.3.0
7087 domhandler: 5.0.3
7088
7001 - dotenv@17.2.1:
7089 + dotenv@17.2.2:
7090 optional: true
7091
7092 dunder-proto@1.0.1:
@@ -7030,20 +7118,22 @@ snapshots:
7118 minimatch: 9.0.1
7119 semver: 7.7.2
7120
7033 - electron-to-chromium@1.5.192: {}
7121 + electron-to-chromium@1.5.222: {}
7122
7123 emoji-regex@8.0.0: {}
7124
7125 emoji-regex@9.2.2: {}
7126
7127 + empathic@2.0.0: {}
7128 +
7129 end-of-stream@1.4.5:
7130 dependencies:
7131 once: 1.4.0
7132
7043 - enhanced-resolve@5.18.2:
7133 + enhanced-resolve@5.18.3:
7134 dependencies:
7135 graceful-fs: 4.2.11
7046 - tapable: 2.2.2
7136 + tapable: 2.2.3
7137
7138 enquirer@2.4.1:
7139 dependencies:
@@ -7054,7 +7144,7 @@ snapshots:
7144
7145 entities@6.0.1: {}
7146
7057 - error-ex@1.3.2:
7147 + error-ex@1.3.4:
7148 dependencies:
7149 is-arrayish: 0.2.1
7150
@@ -7080,34 +7170,34 @@ snapshots:
7170 has-tostringtag: 1.0.2
7171 hasown: 2.0.2
7172
7083 - esbuild@0.25.8:
7173 + esbuild@0.25.10:
7174 optionalDependencies:
7085 - '@esbuild/aix-ppc64': 0.25.8
7086 - '@esbuild/android-arm': 0.25.8
7087 - '@esbuild/android-arm64': 0.25.8
7088 - '@esbuild/android-x64': 0.25.8
7089 - '@esbuild/darwin-arm64': 0.25.8
7090 - '@esbuild/darwin-x64': 0.25.8
7091 - '@esbuild/freebsd-arm64': 0.25.8
7092 - '@esbuild/freebsd-x64': 0.25.8
7093 - '@esbuild/linux-arm': 0.25.8
7094 - '@esbuild/linux-arm64': 0.25.8
7095 - '@esbuild/linux-ia32': 0.25.8
7096 - '@esbuild/linux-loong64': 0.25.8
7097 - '@esbuild/linux-mips64el': 0.25.8
7098 - '@esbuild/linux-ppc64': 0.25.8
7099 - '@esbuild/linux-riscv64': 0.25.8
7100 - '@esbuild/linux-s390x': 0.25.8
7101 - '@esbuild/linux-x64': 0.25.8
7102 - '@esbuild/netbsd-arm64': 0.25.8
7103 - '@esbuild/netbsd-x64': 0.25.8
7104 - '@esbuild/openbsd-arm64': 0.25.8
7105 - '@esbuild/openbsd-x64': 0.25.8
7106 - '@esbuild/openharmony-arm64': 0.25.8
7107 - '@esbuild/sunos-x64': 0.25.8
7108 - '@esbuild/win32-arm64': 0.25.8
7109 - '@esbuild/win32-ia32': 0.25.8
7110 - '@esbuild/win32-x64': 0.25.8
7175 + '@esbuild/aix-ppc64': 0.25.10
7176 + '@esbuild/android-arm': 0.25.10
7177 + '@esbuild/android-arm64': 0.25.10
7178 + '@esbuild/android-x64': 0.25.10
7179 + '@esbuild/darwin-arm64': 0.25.10
7180 + '@esbuild/darwin-x64': 0.25.10
7181 + '@esbuild/freebsd-arm64': 0.25.10
7182 + '@esbuild/freebsd-x64': 0.25.10
7183 + '@esbuild/linux-arm': 0.25.10
7184 + '@esbuild/linux-arm64': 0.25.10
7185 + '@esbuild/linux-ia32': 0.25.10
7186 + '@esbuild/linux-loong64': 0.25.10
7187 + '@esbuild/linux-mips64el': 0.25.10
7188 + '@esbuild/linux-ppc64': 0.25.10
7189 + '@esbuild/linux-riscv64': 0.25.10
7190 + '@esbuild/linux-s390x': 0.25.10
7191 + '@esbuild/linux-x64': 0.25.10
7192 + '@esbuild/netbsd-arm64': 0.25.10
7193 + '@esbuild/netbsd-x64': 0.25.10
7194 + '@esbuild/openbsd-arm64': 0.25.10

This file is too large to show in full.

frontend/src/api/endpoints/copilotAction.ts
+17 -27
@@ -1,17 +1,15 @@
1 import type {
2 - ActionDetailResponse,
3 - InventoryMetricsResponse,
4 - InventoryResponse,
5 - InvokeCopilotActionRequest,
6 - InvokeCopilotActionResponse,
7 - TechnologiesResponse,
8 - Technology
2 + CopilotAction,
3 + CopilotActionInvokeResponse,
4 + CopilotActionListResponse,
5 + InvokeCopilotActionRequest
6 } from "@/types/copilotAction.d"
7 +import type { FlaskBaseResponse } from "@/types/flask.d"
8 import { HttpClient } from "../httpClient"
9
10 export interface CopilotActionInventoryQuery {
11 /** Filter by technology type */
14 - technology?: Technology
12 + technology?: string
13 /** Filter by category */
14 category?: string
15 /** Filter by tag */
@@ -33,7 +31,7 @@ export default {
31 * Get inventory of available active response scripts
32 */
33 getInventory(query?: CopilotActionInventoryQuery, signal?: AbortSignal) {
36 - return HttpClient.get<InventoryResponse>(`/copilot_action/inventory`, {
34 + return HttpClient.get<FlaskBaseResponse & CopilotActionListResponse>(`/copilot_action/inventory`, {
35 params: {
36 technology: query?.technology,
37 category: query?.category,
@@ -51,26 +49,17 @@ export default {
49 /**
50 * Get details for a specific active response script
51 */
54 - getActionByName(copilotActionName: string, signal?: AbortSignal) {
55 - return HttpClient.get<ActionDetailResponse>(`/copilot_action/inventory/${copilotActionName}`, {
56 - signal
57 - })
58 - },
59 -
60 - /**
61 - * Get inventory metrics and status
62 - */
63 - getMetrics(signal?: AbortSignal) {
64 - return HttpClient.get<InventoryMetricsResponse>(`/copilot_action/metrics`, {
65 - signal
66 - })
52 + getActionByName(copilotActionName: string) {
53 + return HttpClient.get<FlaskBaseResponse & { copilot_action: CopilotAction }>(
54 + `/copilot_action/inventory/${copilotActionName}`
55 + )
56 },
57
58 /**
59 * Get available technology types
60 */
61 getTechnologies(signal?: AbortSignal) {
73 - return HttpClient.get<TechnologiesResponse>(`/copilot_action/technologies`, {
62 + return HttpClient.get<FlaskBaseResponse & { technologies: string[] }>(`/copilot_action/technologies`, {
63 signal
64 })
65 },
@@ -78,9 +67,10 @@ export default {
67 /**
68 * Invoke a Copilot Action on multiple target agents
69 */
81 - invokeAction(payload: InvokeCopilotActionRequest, signal?: AbortSignal) {
82 - return HttpClient.post<InvokeCopilotActionResponse>(`/copilot_action/invoke`, payload, {
83 - signal
84 - })
70 + invokeAction(payload: InvokeCopilotActionRequest) {
71 + return HttpClient.post<FlaskBaseResponse & { responses: CopilotActionInvokeResponse[] }>(
72 + `/copilot_action/invoke`,
73 + payload
74 + )
75 }
76 }
frontend/src/api/endpoints/incidentManagement/alerts.ts
+1
@@ -64,6 +64,7 @@ export default {
64 url = `/incidents/db_operations/alerts/source/${args.filter.source}`
65 }
66
67 + // TODO: remove any
68 const params: any = {
69 page: args.page || 1,
70 page_size: args.pageSize || 25,
frontend/src/api/endpoints/incidentManagement/exclusionRules.ts
+1
@@ -24,6 +24,7 @@ export interface ExclusionRulePayload {
24
25 export default {
26 getExclusionRulesList(args: Partial<ExclusionRulesQuery>, signal?: AbortSignal) {
27 + // TODO: remove any
28 const params: any = {
29 skip: args.pagination?.skip || 0,
30 limit: args.pagination?.limit || 25
frontend/src/api/endpoints/sca.ts
+1 -5
@@ -1,8 +1,4 @@
1 -import type {
2 - ScaOverviewQuery,
3 - ScaOverviewResponse,
4 - ScaStatsResponse
5 -} from "@/types/sca.d"
1 +import type { ScaOverviewQuery, ScaOverviewResponse, ScaStatsResponse } from "@/types/sca.d"
2 import { HttpClient } from "../httpClient"
3
4 export default {
frontend/src/api/endpoints/threatIntel.ts
+4 -1
@@ -24,7 +24,10 @@ export default {
24 const body = {
25 process_name: processName
26 }
27 - return HttpClient.post<(FlaskBaseResponse & { data: EvaluationData }) | MCPQueryResponse>(`/threat_intel/process_name`, body)
27 + return HttpClient.post<(FlaskBaseResponse & { data: EvaluationData }) | MCPQueryResponse>(
28 + `/threat_intel/process_name`,
29 + body
30 + )
31 },
32 epssScore(cve: string) {
33 const body = {
frontend/src/api/endpoints/vulnerabilities.ts
+1 -4
@@ -1,7 +1,4 @@
1 -import type {
2 - VulnerabilitySearchQuery,
3 - VulnerabilitySearchResponse
4 -} from "@/types/vulnerabilities.d"
1 +import type { VulnerabilitySearchQuery, VulnerabilitySearchResponse } from "@/types/vulnerabilities.d"
2 import { HttpClient } from "../httpClient"
3
4 export default {
frontend/src/api/endpoints/wazuh/groups.ts
+7 -2
@@ -1,5 +1,10 @@
1 import type { FlaskBaseResponse } from "@/types/flask.d"
2 -import type { WazuhGroup, WazuhGroupConfigurationUpdate, WazuhGroupFile, WazuhGroupFileDetails } from "@/types/wazuh/groups.d"
2 +import type {
3 + WazuhGroup,
4 + WazuhGroupConfigurationUpdate,
5 + WazuhGroupFile,
6 + WazuhGroupFileDetails
7 +} from "@/types/wazuh/groups.d"
8 import { HttpClient } from "../../httpClient"
9
10 // Interface for groups query parameters
@@ -102,7 +107,7 @@ export default {
107 configContent,
108 {
109 headers: {
105 - 'Content-Type': 'application/xml'
110 + "Content-Type": "application/xml"
111 }
112 }
113 )
frontend/src/api/endpoints/wazuh/mitre.ts
+2 -2
@@ -8,7 +8,7 @@ import type {
8 MitreSoftwareDetails,
9 MitreTacticDetails,
10 MitreTechnique,
11 - MitreTechniquesDetails
11 + MitreTechniqueDetails
12 } from "@/types/mitre.d"
13 import { HttpClient } from "../../httpClient"
14
@@ -118,7 +118,7 @@ export default {
118 q = `id=${query?.id}`
119 }
120
121 - return HttpClient.get<FlaskBaseResponse & { results: MitreTechniquesDetails[] }>(
121 + return HttpClient.get<FlaskBaseResponse & { results: MitreTechniqueDetails[] }>(
122 `/wazuh_manager/mitre/techniques`,
123 {
124 params: {
frontend/src/app-layouts/common/Navbar/items.tsx
+13 -13
@@ -132,19 +132,19 @@ export default function getItems(): MenuMixedOption[] {
132 ),
133 key: "VulnerabilityOverview"
134 },
135 - {
136 - label: () =>
137 - h(
138 - RouterLink,
139 - {
140 - to: {
141 - name: "ScaOverview"
142 - }
143 - },
144 - { default: () => "SCA Overview" }
145 - ),
146 - key: "ScaOverview"
147 - }
135 + {
136 + label: () =>
137 + h(
138 + RouterLink,
139 + {
140 + to: {
141 + name: "ScaOverview"
142 + }
143 + },
144 + { default: () => "SCA Overview" }
145 + ),
146 + key: "ScaOverview"
147 + }
148 ]
149 },
150 {
frontend/src/components/aiChatbot/ChatContainer.vue
+1 -6
@@ -44,7 +44,6 @@
44 </template>
45
46 <script setup lang="ts">
47 -import type { RemovableRef } from "@vueuse/core"
47 import type { ScrollbarInst } from "naive-ui"
48 import type { ChatBubble } from "./ChatBubble.vue"
49 import type { Message } from "./ChatQuery.vue"
@@ -67,11 +66,7 @@ const emit = defineEmits<{
66
67 const message = useMessage()
68
70 -const list: RemovableRef<ChatBubble[]> = useStorage<ChatBubble[]>(
71 - "ai-chatbot-list-messages",
72 - [],
73 - secureLocalStorage({ session: true })
74 -)
69 +const list = useStorage<ChatBubble[]>("ai-chatbot-list-messages", [], secureLocalStorage({ session: true }))
70 const loading = ref(false)
71 const server = ref<string | null>(null)
72 const input = ref<string | null>(null)
frontend/src/components/cloudSecurityAssessment/FormTypes/AwsTypeForm.vue
+7 -1
@@ -2,11 +2,17 @@
2 <n-form ref="formRef" :model="form" :rules="rules">
3 <div class="flex flex-col gap-2">
4 <n-form-item label="Access Key ID" path="access_key_id">
5 - <n-input v-model:value.trim="form.access_key_id" placeholder="Please insert Access Key ID" clearable />
5 + <n-input
6 + v-model:value.trim="form.access_key_id"
7 + placeholder="Please insert Access Key ID"
8 + clearable
9 + :input-props="{ autocomplete: 'new-password' }"
10 + />
11 </n-form-item>
12 <n-form-item label="Secret Access Key" path="secret_access_key">
13 <n-input
14 v-model:value.trim="form.secret_access_key"
15 + :input-props="{ autocomplete: 'new-password' }"
16 placeholder="Please insert Secret Access Key"
17 type="password"
18 show-password-on="click"
frontend/src/components/common/Badge.vue
+3 -1
@@ -17,6 +17,8 @@
17 </template>
18
19 <script setup lang="ts">
20 +export type BadgeColor = "danger" | "warning" | "success" | "primary"
21 +
22 // TODO: refactor
23 const { type, hintCursor, pointCursor, color, href, fluid } = defineProps<{
24 type?: "splitted" | "muted" | "active" | "cursor"
@@ -24,7 +26,7 @@ const { type, hintCursor, pointCursor, color, href, fluid } = defineProps<{
26 pointCursor?: boolean
27 fluid?: boolean
28 bright?: boolean
27 - color?: "danger" | "warning" | "success" | "primary"
29 + color?: BadgeColor
30 href?: string
31 }>()
32 </script>
frontend/src/components/common/cards/CardEntity.vue
+40 -16
@@ -5,12 +5,18 @@
5 :class="[
6 `card-size-${size}`,
7 `card-status-${status}`,
8 - { embedded, highlighted, clickable, hoverable, disabled }
8 + { embedded, highlighted, clickable, hoverable, disabled },
9 + cardEntityClass
10 ]"
11 >
11 - <n-spin :show="loading" :description="loadingDescription">
12 - <div class="card-entity-wrapper flex flex-col">
13 - <div class="main-box flex flex-col">
12 + <n-spin
13 + :show="loading"
14 + :description="loadingDescription"
15 + content-class="h-full grow"
16 + class="flex h-full flex-col"
17 + >
18 + <div class="card-entity-wrapper flex flex-col" :class="cardEntityWrapperClass">
19 + <div class="main-box flex flex-col" :class="mainBoxClass">
20 <div v-if="$slots.header" class="header-box">
21 <slot name="header" />
22 </div>
@@ -18,6 +24,7 @@
24 <div
25 v-if="!$slots.header && ($slots.headerMain || $slots.headerExtra)"
26 class="header-box flex flex-wrap items-center justify-between"
27 + :class="headerBoxClass"
28 >
29 <div>
30 <slot name="headerMain" />
@@ -59,18 +66,35 @@
66 <script setup lang="ts">
67 import { NCard, NSpin } from "naive-ui"
68
62 -const { size, status, embedded, highlighted, clickable, hoverable, disabled, loading, loadingDescription } =
63 - defineProps<{
64 - size?: "medium" | "small" | "large"
65 - status?: "success" | "warning" | "error"
66 - embedded?: boolean
67 - highlighted?: boolean
68 - clickable?: boolean
69 - hoverable?: boolean
70 - disabled?: boolean
71 - loading?: boolean
72 - loadingDescription?: string
73 - }>()
69 +const {
70 + size,
71 + status,
72 + embedded,
73 + highlighted,
74 + clickable,
75 + hoverable,
76 + disabled,
77 + loading,
78 + loadingDescription,
79 + mainBoxClass,
80 + headerBoxClass,
81 + cardEntityClass,
82 + cardEntityWrapperClass
83 +} = defineProps<{
84 + size?: "medium" | "small" | "large"
85 + status?: "success" | "warning" | "error"
86 + embedded?: boolean
87 + highlighted?: boolean
88 + clickable?: boolean
89 + hoverable?: boolean
90 + disabled?: boolean
91 + loading?: boolean
92 + loadingDescription?: string
93 + mainBoxClass?: string
94 + headerBoxClass?: string
95 + cardEntityClass?: string
96 + cardEntityWrapperClass?: string
97 +}>()
98 </script>
99
100 <style lang="scss" scoped>
frontend/src/components/copilotAction/ActionCard.vue
+33 -60
@@ -1,39 +1,45 @@
1 <template>
2 - <div class="action-card h-full">
3 - <CardEntity hoverable clickable :embedded class="@container h-full flex flex-col" @click.stop="showDetails = true">
2 + <div class="h-full">
3 + <CardEntity
4 + hoverable
5 + clickable
6 + :embedded
7 + class="h-full"
8 + main-box-class="grow"
9 + card-entity-wrapper-class="h-full"
10 + header-box-class="flex-nowrap! items-start"
11 + @click.stop="showDetails = true"
12 + >
13 <template #headerMain>{{ action.copilot_action_name }}</template>
14 <template #headerExtra>
6 - <Badge :color="getTechnologyColor(action.technology)">
7 - <template #iconLeft><Icon :name="getTechnologyIcon(action.technology)" :size="14" /></template>
8 - <template #value>{{ action.technology }}</template>
9 - </Badge>
15 + <TechnologyBadge :action />
16 </template>
17 <template #default>
12 - <div class="flex-1">
13 - <p class="text-base font-medium opacity-90 leading-relaxed line-clamp-3">{{ action.description }}</p>
18 + <div class="line-clamp-3 text-sm">
19 + {{ action.description }}
20 </div>
21 </template>
22 <template #footerMain>
23 <div class="flex flex-wrap items-center gap-2">
18 - <Badge v-if="action.category" class="text-xs">
24 + <Badge v-if="action.category">
25 <template #value>{{ action.category }}</template>
26 </Badge>
27
22 - <Badge v-if="action.version" color="primary" type="splitted" class="text-xs">
23 - <template #label>v</template>
28 + <Badge v-if="action.version" color="primary" type="splitted">
29 + <template #label>version</template>
30 <template #value>{{ action.version }}</template>
31 </Badge>
32
27 - <Badge v-if="action.script_parameters.length > 0" color="warning" type="splitted" class="text-xs">
33 + <Badge v-if="action.script_parameters.length" color="warning" type="splitted">
34 <template #label>params</template>
35 <template #value>{{ action.script_parameters.length }}</template>
36 </Badge>
37
32 - <div v-if="action.tags && action.tags.length > 0" class="flex gap-1">
33 - <Badge v-for="tag of action.tags.slice(0, 2)" :key="tag" size="small" class="text-xs">
38 + <div v-if="action.tags?.length" class="flex items-center gap-1">
39 + <Badge v-for="tag of action.tags.slice(0, 2)" :key="tag" size="small">
40 <template #value>{{ tag }}</template>
41 </Badge>
36 - <Badge v-if="action.tags.length > 2" size="small" class="text-xs">
42 + <Badge v-if="action.tags.length > 2" size="small">
43 <template #value>+{{ action.tags.length - 2 }}</template>
44 </Badge>
45 </div>
@@ -66,76 +72,43 @@
72 v-model:show="showInvokeModal"
73 preset="card"
74 :style="{ maxWidth: 'min(600px, 90vw)' }"
69 - :title="`Invoke: ${action.copilot_action_name}`"
75 :bordered="false"
76 + display-directive="show"
77 segmented
78 >
79 + <template #header>
80 + <div class="flex flex-col gap-2">
81 + <div class="flex">
82 + <TechnologyBadge :action />
83 + </div>
84 + <span class="px-1 text-base">{{ action.copilot_action_name }}</span>
85 + </div>
86 + </template>
87 <InvokeActionForm :action="action" @success="handleInvokeSuccess" @close="showInvokeModal = false" />
88 </n-modal>
89 </div>
90 </template>
91
92 <script setup lang="ts">
79 -import type { ActiveResponseItem } from "@/types/copilotAction.d"
93 +import type { CopilotAction } from "@/types/copilotAction.d"
94 import { NButton, NModal, useMessage } from "naive-ui"
95 import { ref } from "vue"
96 import Badge from "@/components/common/Badge.vue"
97 import CardEntity from "@/components/common/cards/CardEntity.vue"
98 import Icon from "@/components/common/Icon.vue"
85 -import { Technology } from "@/types/copilotAction.d"
99 import ActionCardContent from "./ActionCardContent.vue"
100 import InvokeActionForm from "./InvokeActionForm.vue"
101 +import TechnologyBadge from "./TechnologyBadge.vue"
102
89 -const { action } = defineProps<{ action: ActiveResponseItem; embedded?: boolean }>()
103 +const { action } = defineProps<{ action: CopilotAction; embedded?: boolean }>()
104
105 const showDetails = ref(false)
106 const showInvokeModal = ref(false)
107 const message = useMessage()
108 const PlayIcon = "carbon:play"
109
96 -function getTechnologyIcon(technology: string): string {
97 - const iconMap: Record<string, string> = {
98 - [Technology.WINDOWS]: "carbon:logo-windows",
99 - [Technology.LINUX]: "carbon:logo-linux",
100 - [Technology.MACOS]: "carbon:logo-apple",
101 - [Technology.WAZUH]: "carbon:security",
102 - [Technology.VELOCIRAPTOR]: "carbon:eagle",
103 - [Technology.NETWORK]: "carbon:network-3",
104 - [Technology.CLOUD]: "carbon:cloud"
105 - }
106 - return iconMap[technology] || "carbon:application"
107 -}
108 -
109 -function getTechnologyColor(technology: string): "primary" | "warning" | "success" | "danger" | undefined {
110 - const colorMap: Record<string, "primary" | "warning" | "success" | "danger"> = {
111 - [Technology.WINDOWS]: "primary",
112 - [Technology.LINUX]: "warning",
113 - [Technology.MACOS]: "success",
114 - [Technology.WAZUH]: "success",
115 - [Technology.VELOCIRAPTOR]: "primary",
116 - [Technology.NETWORK]: "primary",
117 - [Technology.CLOUD]: "success"
118 - }
119 - return colorMap[technology]
120 -}
121 -
110 function handleInvokeSuccess() {
111 showInvokeModal.value = false
112 message.success("Action invoked successfully!")
113 }
114 </script>
127 -
128 -<style scoped>
129 -.action-card {
130 - min-height: 200px;
131 -}
132 -
133 -.line-clamp-3 {
134 - display: -webkit-box;
135 - -webkit-line-clamp: 3;
136 - line-clamp: 3;
137 - -webkit-box-orient: vertical;
138 - overflow: hidden;
139 - text-overflow: ellipsis;
140 -}
141 -</style>
frontend/src/components/copilotAction/ActionCardContent.vue
+118 -154
@@ -1,181 +1,145 @@
1 <template>
2 - <div class="action-details">
3 - <n-spin :show="loading" class="min-h-48">
4 - <template v-if="action">
5 - <div class="flex flex-col gap-4">
6 - <!-- Basic Information -->
7 - <div class="grid grid-cols-1 md:grid-cols-2 gap-4">
8 - <div class="flex flex-col gap-2">
9 - <h3 class="text-lg font-semibold">Information</h3>
10 - <div class="flex flex-col gap-1">
11 - <div><strong>Technology:</strong> {{ action.technology }}</div>
12 - <div v-if="action.category"><strong>Category:</strong> {{ action.category }}</div>
13 - <div v-if="action.version"><strong>Version:</strong> {{ action.version }}</div>
14 - <div v-if="action.last_updated"><strong>Last Updated:</strong> {{ formatDate(action.last_updated) }}</div>
15 - </div>
2 + <div class="flex flex-col gap-4 pb-1">
3 + <!-- Basic Information -->
4 + <div class="grid grid-cols-1 gap-4 md:grid-cols-2">
5 + <CardKV>
6 + <template #key>Information</template>
7 + <template #value>
8 + <div class="flex flex-col gap-2 py-0.5">
9 + <div class="flex flex-wrap gap-2">
10 + <strong>Technology:</strong>
11 + <span>{{ action.technology }}</span>
12 </div>
17 - <div class="flex flex-col gap-2">
18 - <h3 class="text-lg font-semibold">Script Details</h3>
19 - <div class="flex flex-col gap-1">
20 - <div v-if="action.script_name"><strong>Script Name:</strong> {{ action.script_name }}</div>
21 - <div>
22 - <strong>Repository:</strong>
23 - <a :href="action.repo_url" target="_blank" class="text-blue-500 hover:underline">
24 - {{ action.repo_url }}
25 - </a>
26 - </div>
27 - </div>
13 + <div v-if="action.category" class="flex flex-wrap gap-2">
14 + <strong>Category:</strong>
15 + <span>{{ action.category }}</span>
16 + </div>
17 + <div v-if="action.version" class="flex flex-wrap gap-2">
18 + <strong>Version:</strong>
19 + <span>{{ action.version }}</span>
20 + </div>
21 + <div v-if="action.last_updated" class="flex flex-wrap gap-2">
22 + <strong>Last Updated:</strong>
23 + <span>{{ formatDate(action.last_updated, dFormats.datetime) }}</span>
24 </div>
25 </div>
30 -
31 - <!-- Description -->
32 - <div class="flex flex-col gap-2">
33 - <h3 class="text-lg font-semibold">Description</h3>
34 - <p class="text-base font-medium opacity-90 leading-relaxed">{{ action.description }}</p>
35 - </div>
36 -
37 - <!-- Tags -->
38 - <div v-if="action.tags && action.tags.length > 0" class="flex flex-col gap-2">
39 - <h3 class="text-lg font-semibold">Tags</h3>
26 + </template>
27 + </CardKV>
28 + <CardKV>
29 + <template #key>Script Details</template>
30 + <template #value>
31 + <div class="flex flex-col gap-2 py-0.5">
32 + <div v-if="action.script_name" class="flex flex-wrap gap-2">
33 + <strong>Script Name:</strong>
34 + <span>{{ action.script_name }}</span>
35 + </div>
36 <div class="flex flex-wrap gap-2">
41 - <Badge v-for="tag of action.tags" :key="tag">
42 - <template #value>{{ tag }}</template>
43 - </Badge>
37 + <strong>Repository:</strong>
38 + <a :href="action.repo_url" target="_blank" rel="noopener">
39 + {{ action.repo_url }}
40 + </a>
41 </div>
42 </div>
43 + </template>
44 + </CardKV>
45 + </div>
46 +
47 + <!-- Description -->
48 + <CardKV>
49 + <template #key>Description</template>
50 + <template #value>{{ action.description }}</template>
51 + </CardKV>
52 +
53 + <!-- Parameters -->
54 + <CardKV v-if="action.script_parameters?.length">
55 + <template #key>Parameters</template>
56 + <template #value>
57 + <div class="grid grid-cols-1 gap-3 py-1 lg:grid-cols-2">
58 + <CardEntity
59 + v-for="param in action.script_parameters"
60 + :key="param.name"
61 + embedded
62 + size="small"
63 + class="h-full"
64 + main-box-class="grow"
65 + card-entity-wrapper-class="h-full"
66 + >
67 + <template #headerMain>
68 + <div class="text-default flex items-center gap-4">
69 + <div class="text-sm font-semibold">{{ param.name }}</div>
70 + <Badge :color="param.required ? 'danger' : 'success'" type="splitted">
71 + <template #value>
72 + <span class="text-xs">{{ param.required ? "Required" : "Optional" }}</span>
73 + </template>
74 + </Badge>
75 + </div>
76 + </template>
77 + <template #headerExtra>
78 + <Badge>
79 + <template #value>
80 + <span class="text-xs">{{ param.type }}</span>
81 + </template>
82 + </Badge>
83 + </template>
84
47 - <!-- Parameters -->
48 - <div v-if="action.script_parameters.length > 0" class="flex flex-col gap-4">
49 - <h3 class="text-lg font-semibold">Parameters</h3>
50 - <div class="grid grid-cols-1 lg:grid-cols-2 gap-4">
51 - <div
52 - v-for="param in action.script_parameters"
53 - :key="param.name"
54 - class="parameter-card border rounded-lg p-4 hover:shadow-sm transition-shadow"
55 - >
56 - <div class="flex items-start justify-between mb-2">
57 - <div class="flex items-center gap-2">
58 - <h4 class="font-mono text-sm font-semibold">{{ param.name }}</h4>
59 - <Badge :color="param.required ? 'danger' : 'success'" size="small">
60 - <template #value>{{ param.required ? 'Required' : 'Optional' }}</template>
61 - </Badge>
62 - </div>
63 - <Badge color="primary" size="small">
64 - <template #value>{{ param.type }}</template>
65 - </Badge>
66 - </div>
85 + <template v-if="param.description" #default>
86 + <p class="text-xs">{{ param.description }}</p>
87 + </template>
88
68 - <div v-if="param.description" class="text-sm opacity-75 mb-3">
69 - {{ param.description }}
89 + <template #footer>
90 + <div class="flex flex-col gap-1">
91 + <div
92 + v-if="param.default !== null && param.default !== undefined"
93 + class="text-xs opacity-60"
94 + >
95 + <span class="font-medium">Default:</span>
96 + <code class="code-block ml-1 rounded px-1 py-0.5 text-xs">
97 + {{ param.default }}
98 + </code>
99 </div>
100
72 - <div class="flex flex-col gap-1">
73 - <div v-if="param.default !== null && param.default !== undefined" class="text-xs opacity-60">
74 - <span class="font-medium">Default:</span>
75 - <code class="code-block px-1 py-0.5 rounded text-xs ml-1">{{ param.default }}</code>
76 - </div>
77 -
78 - <div v-if="param.enum && param.enum.length > 0" class="text-xs opacity-60">
79 - <span class="font-medium">Options:</span>
80 - <div class="flex flex-wrap gap-1 mt-1">
81 - <code
82 - v-for="option in param.enum"
83 - :key="option"
84 - class="enum-option px-1 py-0.5 rounded text-xs"
85 - >
86 - {{ option }}
87 - </code>
88 - </div>
101 + <div v-if="param.enum && param.enum.length > 0" class="text-xs opacity-60">
102 + <span class="font-medium">Options:</span>
103 + <div class="mt-1 flex flex-wrap gap-1">
104 + <code
105 + v-for="option in param.enum"
106 + :key="option"
107 + class="enum-option rounded px-1 py-0.5 text-xs"
108 + >
109 + {{ option }}
110 + </code>
111 </div>
112 + </div>
113
91 - <div v-if="param.arg_position" class="text-xs opacity-60">
92 - <span class="font-medium">Position:</span> {{ param.arg_position }}
93 - </div>
114 + <div v-if="param.arg_position" class="text-xs opacity-60">
115 + <span class="font-medium">Position:</span>
116 + {{ param.arg_position }}
117 </div>
118 </div>
96 - </div>
97 - </div>
119 + </template>
120 + </CardEntity>
121 </div>
122 </template>
100 - <template v-else>
101 - <n-empty v-if="!loading" description="No action details found" class="h-48 justify-center" />
102 - </template>
103 - </n-spin>
123 + </CardKV>
124 +
125 + <!-- Tags -->
126 + <div v-if="action.tags?.length" class="flex flex-wrap gap-2">
127 + <code v-for="tag of action.tags" :key="tag">#{{ tag }}</code>
128 + </div>
129 </div>
130 </template>
131
132 <script setup lang="ts">
108 -import type { ActiveResponseItem } from "@/types/copilotAction.d"
109 -import { NEmpty, NSpin } from "naive-ui"
110 -import { ref } from "vue"
133 +import type { CopilotAction } from "@/types/copilotAction.d"
134 import Badge from "@/components/common/Badge.vue"
135 +import CardEntity from "@/components/common/cards/CardEntity.vue"
136 +import CardKV from "@/components/common/cards/CardKV.vue"
137 +import { useSettingsStore } from "@/stores/settings"
138 +import { formatDate } from "@/utils"
139
140 const { action } = defineProps<{
114 - action: ActiveResponseItem
141 + action: CopilotAction
142 }>()
143
117 -const loading = ref(false)
118 -
119 -function formatDate(date: Date): string {
120 - return new Date(date).toLocaleDateString()
121 -}
144 +const dFormats = useSettingsStore().dateFormat
145 </script>
123 -
124 -<style scoped>
125 -.action-details {
126 - max-height: 70vh;
127 - overflow-y: auto;
128 -}
129 -
130 -/* Custom scrollbar for better UX */
131 -.action-details::-webkit-scrollbar {
132 - width: 6px;
133 -}
134 -
135 -.action-details::-webkit-scrollbar-track {
136 - background: var(--border-color);
137 - border-radius: 3px;
138 -}
139 -
140 -.action-details::-webkit-scrollbar-thumb {
141 - background: var(--text-color-3);
142 - border-radius: 3px;
143 -}
144 -
145 -.action-details::-webkit-scrollbar-thumb:hover {
146 - background: var(--text-color-2);
147 -}
148 -
149 -/* Parameter cards */
150 -.parameter-card {
151 - background-color: var(--card-color);
152 - border-color: var(--border-color);
153 - transition: all 0.2s ease;
154 -}
155 -
156 -.parameter-card:hover {
157 - background-color: var(--hover-color);
158 - border-color: var(--border-color-hover);
159 - box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px 0 rgba(0, 0, 0, 0.06);
160 -}
161 -
162 -/* Code blocks */
163 -.code-block {
164 - background-color: var(--code-color);
165 - color: var(--text-color-1);
166 - border: 1px solid var(--border-color);
167 -}
168 -
169 -.enum-option {
170 - background-color: var(--primary-color-hover);
171 - color: var(--primary-color);
172 - border: 1px solid var(--primary-color-hover);
173 -}
174 -
175 -/* Dark mode adjustments */
176 -@media (prefers-color-scheme: dark) {
177 - .parameter-card:hover {
178 - box-shadow: 0 1px 3px 0 rgba(255, 255, 255, 0.1), 0 1px 2px 0 rgba(255, 255, 255, 0.06);
179 - }
180 -}
181 -</style>
frontend/src/components/copilotAction/InvokeActionForm.vue
+88 -246
@@ -1,27 +1,11 @@
1 <template>
2 - <div class="invoke-action-form">
3 - <n-spin :show="loading">
4 - <div class="flex flex-col gap-6">
5 - <!-- Action Information -->
6 - <div class="action-header p-4 rounded-lg border">
7 - <div class="flex items-start justify-between mb-3">
8 - <div class="flex-1">
9 - <h4 class="text-lg font-semibold mb-2">{{ action.copilot_action_name }}</h4>
10 - <p class="text-base font-medium opacity-90 leading-relaxed">{{ action.description }}</p>
11 - </div>
12 - <Badge :color="getTechnologyColor(action.technology)" class="ml-3">
13 - <template #iconLeft><Icon :name="getTechnologyIcon(action.technology)" :size="14" /></template>
14 - <template #value>{{ action.technology }}</template>
15 - </Badge>
16 - </div>
17 - </div>
2 + <n-spin :show="loading">
3 + <div class="flex flex-col gap-6">
4 + <div class="text-sm leading-relaxed">{{ action.description }}</div>
5
19 - <!-- Target Agents Selection -->
20 - <div class="form-section">
21 - <div class="section-header mb-3">
22 - <h5 class="font-semibold text-base">Target Agents</h5>
23 - <span class="required-indicator">*</span>
24 - </div>
6 + <!-- Target Agents Selection -->
7 + <div class="mt-4 flex flex-col gap-1">
8 + <n-form-item label="Target Agents" required :show-feedback="false">
9 <n-select
10 v-model:value="form.agent_names"
11 :options="agentOptions"
@@ -33,110 +17,67 @@
17 size="large"
18 class="mb-2"
19 />
36 - <p class="helper-text">Select one or more agents to run this action on</p>
37 - </div>
38 -
39 - <!-- Parameters Form -->
40 - <div v-if="action.script_parameters.length > 0" class="form-section">
41 - <div class="section-header mb-4">
42 - <h5 class="font-semibold text-base">Parameters</h5>
43 - </div>
44 -
45 - <!-- Required Parameters -->
46 - <div v-if="requiredParameters.length > 0" class="parameter-group mb-6">
47 - <div class="parameter-group-header mb-4">
48 - <h6 class="text-sm font-medium opacity-90">Required Parameters</h6>
49 - <div class="parameter-group-line"></div>
50 - </div>
51 - <div class="grid grid-cols-1 gap-4">
52 - <div v-for="param in requiredParameters" :key="param.name" class="parameter-field">
53 - <div class="parameter-label mb-2">
54 - <label class="font-medium text-sm">
55 - {{ param.name }}
56 - <span class="required-indicator">*</span>
57 - </label>
58 - <Badge v-if="param.type" color="primary" size="small" class="ml-2">
59 - <template #value>{{ param.type }}</template>
60 - </Badge>
61 - </div>
62 - <component
63 - :is="getInputComponent(param.type)"
64 - v-model:value="form.parameters[param.name]"
65 - :placeholder="getPlaceholder(param)"
66 - :options="param.enum?.map(e => ({ label: e, value: e }))"
67 - clearable
68 - size="large"
69 - class="mb-1"
70 - />
71 - <p v-if="param.description" class="helper-text">{{ param.description }}</p>
72 - </div>
73 - </div>
74 - </div>
20 + </n-form-item>
21 + <p class="px-0.5 text-sm">Select one or more agents to run this action on</p>
22 + </div>
23
76 - <!-- Optional Parameters -->
77 - <div v-if="optionalParameters.length > 0" class="parameter-group">
78 - <div class="parameter-group-header mb-4">
79 - <h6 class="text-sm font-medium opacity-90">Optional Parameters</h6>
80 - <div class="parameter-group-line"></div>
81 - </div>
82 - <div class="grid grid-cols-1 gap-4">
83 - <div v-for="param in optionalParameters" :key="param.name" class="parameter-field">
84 - <div class="parameter-label mb-2">
85 - <label class="font-medium text-sm">{{ param.name }}</label>
86 - <Badge v-if="param.type" color="primary" size="small" class="ml-2">
87 - <template #value>{{ param.type }}</template>
88 - </Badge>
89 - </div>
90 - <component
91 - :is="getInputComponent(param.type)"
92 - v-model:value="form.parameters[param.name]"
93 - :placeholder="getPlaceholder(param)"
94 - :options="param.enum?.map(e => ({ label: e, value: e }))"
95 - clearable
96 - size="large"
97 - class="mb-1"
98 - />
99 - <p v-if="param.description" class="helper-text">{{ param.description }}</p>
100 - </div>
101 - </div>
102 - </div>
103 - </div> <!-- Action Buttons -->
104 - <div class="action-buttons flex justify-end gap-3 pt-6 mt-6 border-t border-opacity-20">
105 - <n-button size="large" class="px-6" @click="$emit('close')">
106 - Cancel
107 - </n-button>
108 - <n-button
109 - type="primary"
110 - size="large"
111 - class="px-8"
112 - :loading="loading"
113 - :disabled="!isFormValid"
114 - @click="handleSubmit"
24 + <!-- Parameters Form -->
25 + <CardEntity v-if="action.script_parameters.length > 0" embedded>
26 + <template #header>Parameters</template>
27 +
28 + <div class="flex flex-col gap-4">
29 + <n-card
30 + v-for="param in parameters"
31 + :key="param.name"
32 + embedded
33 + size="small"
34 + content-class="flex flex-col gap-2"
35 >
116 - <template v-if="!loading" #icon>
117 - <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
118 - <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 10V3L4 14h7v7l9-11h-7z" />
119 - </svg>
120 - </template>
121 - {{ loading ? 'Invoking...' : 'Invoke Action' }}
122 - </n-button>
36 + <n-form-item :required="param.required" :show-feedback="false">
37 + <template #label>
38 + <div class="flex items-center gap-2">
39 + <span>{{ param.name }}</span>
40 + <code>{{ param.type }}</code>
41 + </div>
42 + </template>
43 + <component
44 + :is="getInputComponent(param.type)"
45 + v-model:value="form.parameters[param.name]"
46 + :placeholder="getPlaceholder(param)"
47 + :options="param.enum?.map(e => ({ label: e, value: e }))"
48 + clearable
49 + />
50 + </n-form-item>
51 + <p v-if="param.description" class="px-0.5 text-sm">{{ param.description }}</p>
52 + </n-card>
53 </div>
54 + </CardEntity>
55 +
56 + <!-- Action Buttons -->
57 + <div class="mt-6 flex justify-end gap-3">
58 + <n-button size="large" @click="$emit('close')">Cancel</n-button>
59 + <n-button type="primary" size="large" :loading="loading" :disabled="!isFormValid" @click="handleSubmit">
60 + <template #icon>
61 + <Icon :size="18" :name="InvokeIcon" />
62 + </template>
63 + {{ loading ? "Invoking..." : "Invoke Action" }}
64 + </n-button>
65 </div>
125 - </n-spin>
126 - </div>
66 + </div>
67 + </n-spin>
68 </template>
69
70 <script setup lang="ts">
130 -import type { ActiveResponseItem, InvokeCopilotActionRequest, ScriptParameter } from "@/types/copilotAction.d"
131 -import { NButton, NInput, NInputNumber, NSelect, NSpin, NSwitch, useMessage } from "naive-ui"
132 -import { computed, onMounted, ref } from "vue"
71 +import type { CopilotAction, InvokeCopilotActionRequest, ScriptParameter } from "@/types/copilotAction.d"
72 +import _orderBy from "lodash/orderBy"
73 +import { NButton, NCard, NFormItem, NInput, NInputNumber, NSelect, NSpin, NSwitch, useMessage } from "naive-ui"
74 +import { computed, onBeforeMount, ref } from "vue"
75 import Api from "@/api"
134 -import Badge from "@/components/common/Badge.vue"
76 +import CardEntity from "@/components/common/cards/CardEntity.vue"
77 import Icon from "@/components/common/Icon.vue"
136 -import { Technology } from "@/types/copilotAction.d"
78
79 const { action } = defineProps<{
139 - action: ActiveResponseItem
80 + action: CopilotAction
81 }>()
82
83 const emit = defineEmits<{
@@ -144,6 +85,7 @@ const emit = defineEmits<{
85 close: []
86 }>()
87
88 +const InvokeIcon = "solar:playback-speed-outline"
89 const message = useMessage()
90 const loading = ref(false)
91 const loadingAgents = ref(false)
@@ -152,7 +94,7 @@ const agentOptions = ref<{ label: string; value: string }[]>([])
94
95 const form = ref<{
96 agent_names: string[]
155 - parameters: Record<string, any>
97 + parameters: Record<string, string | number | boolean>
98 }>({
99 agent_names: [],
100 parameters: {}
@@ -160,7 +102,7 @@ const form = ref<{
102
103 // Separate required and optional parameters
104 const requiredParameters = computed(() => action.script_parameters.filter(p => p.required))
163 -const optionalParameters = computed(() => action.script_parameters.filter(p => !p.required))
105 +const parameters = computed(() => _orderBy(action.script_parameters, ["required"], ["desc"]))
106
107 const isFormValid = computed(() => {
108 if (form.value.agent_names.length === 0) return false
@@ -168,7 +110,7 @@ const isFormValid = computed(() => {
110 // Check all required parameters are filled
111 for (const param of requiredParameters.value) {
112 const value = form.value.parameters[param.name]
171 - if (value === null || value === undefined || value === '') {
113 + if (value === null || value === undefined || value === "") {
114 return false
115 }
116 }
@@ -178,15 +120,15 @@ const isFormValid = computed(() => {
120
121 function getInputComponent(type: string) {
122 switch (type.toLowerCase()) {
181 - case 'int':
182 - case 'integer':
183 - case 'float':
184 - case 'number':
123 + case "int":
124 + case "integer":
125 + case "float":
126 + case "number":
127 return NInputNumber
186 - case 'bool':
187 - case 'boolean':
128 + case "bool":
129 + case "boolean":
130 return NSwitch
189 - case 'enum':
131 + case "enum":
132 return NSelect
133 default:
134 return NInput
@@ -200,34 +142,9 @@ function getPlaceholder(param: ScriptParameter): string {
142 return `Enter ${param.name}...`
143 }
144
203 -function getTechnologyIcon(technology: string): string {
204 - const iconMap: Record<string, string> = {
205 - [Technology.WINDOWS]: "carbon:logo-windows",
206 - [Technology.LINUX]: "carbon:logo-linux",
207 - [Technology.MACOS]: "carbon:logo-apple",
208 - [Technology.WAZUH]: "carbon:security",
209 - [Technology.VELOCIRAPTOR]: "carbon:eagle",
210 - [Technology.NETWORK]: "carbon:network-3",
211 - [Technology.CLOUD]: "carbon:cloud"
212 - }
213 - return iconMap[technology] || "carbon:application"
214 -}
215 -
216 -function getTechnologyColor(technology: string): "primary" | "warning" | "success" | "danger" | undefined {
217 - const colorMap: Record<string, "primary" | "warning" | "success" | "danger"> = {
218 - [Technology.WINDOWS]: "primary",
219 - [Technology.LINUX]: "warning",
220 - [Technology.MACOS]: "success",
221 - [Technology.WAZUH]: "success",
222 - [Technology.VELOCIRAPTOR]: "primary",
223 - [Technology.NETWORK]: "primary",
224 - [Technology.CLOUD]: "success"
225 - }
226 - return colorMap[technology]
227 -}
228 -
145 async function loadAgents() {
146 loadingAgents.value = true
147 +
148 try {
149 const response = await Api.agents.getAgents()
150 if (response.data.success) {
@@ -236,10 +153,10 @@ async function loadAgents() {
153 value: agent.hostname
154 }))
155 } else {
239 - message.error('Failed to load agents')
156 + message.error("Failed to load agents")
157 }
158 } catch {
242 - message.error('Error loading agents')
159 + message.error("Error loading agents")
160 } finally {
161 loadingAgents.value = false
162 }
@@ -250,26 +167,29 @@ async function handleSubmit() {
167
168 loading.value = true
169 try {
253 - // Prepare the payload
254 - const payload: InvokeCopilotActionRequest = {
255 - copilot_action_name: action.copilot_action_name,
256 - agent_names: form.value.agent_names,
257 - parameters: {
258 - ScriptURL: action.repo_url,
259 - ...form.value.parameters
260 - }
261 - }
170 + // Prepare the payload
171 + const payload: InvokeCopilotActionRequest = {
172 + copilot_action_name: action.copilot_action_name,
173 + agent_names: form.value.agent_names,
174 + parameters: {
175 + ScriptURL: action.repo_url,
176 + ...form.value.parameters
177 + }
178 + }
179
180 const response = await Api.copilotAction.invokeAction(payload)
181
182 if (response.data.success) {
266 - message.success(`Action invoked successfully on ${form.value.agent_names.length} agent(s). Check the appropriate Grafana dashboard for results.`)
267 - emit('success')
183 + message.success(
184 + `Action invoked successfully on ${form.value.agent_names.length} agent(s). Check the appropriate Grafana dashboard for results.`
185 + )
186 + emit("success")
187 } else {
269 - message.error(response.data.message || 'Failed to invoke action')
188 + message.error(response.data.message || "Failed to invoke action")
189 }
190 } catch (error: any) {
272 - message.error(error.response?.data?.message || 'Error invoking action')
191 + // TODO: remove any
192 + message.error(error.response?.data?.message || "Error invoking action")
193 } finally {
194 loading.value = false
195 }
@@ -277,7 +197,7 @@ async function handleSubmit() {
197
198 // Initialize form with default values
199 function initializeForm() {
280 - const parameters: Record<string, any> = {}
200 + const parameters: Record<string, string | number | boolean> = {}
201
202 action.script_parameters.forEach(param => {
203 if (param.default !== null && param.default !== undefined) {
@@ -288,86 +208,8 @@ function initializeForm() {
208 form.value.parameters = parameters
209 }
210
291 -onMounted(() => {
211 +onBeforeMount(() => {
212 loadAgents()
213 initializeForm()
214 })
215 </script>
296 -
297 -<style scoped>
298 -.invoke-action-form {
299 - max-height: 70vh;
300 - overflow-y: auto;
301 -}
302 -
303 -/* Form section styling */
304 -.form-section {
305 - padding: 1rem;
306 - border: 1px solid var(--border-color);
307 - border-radius: 8px;
308 -}
309 -
310 -.section-header {
311 - display: flex;
312 - align-items: center;
313 - gap: 0.5rem;
314 -}
315 -
316 -.required-indicator {
317 - color: #f56565;
318 - font-weight: 600;
319 -}
320 -
321 -/* Parameter groups */
322 -.parameter-group-header {
323 - display: flex;
324 - align-items: center;
325 - gap: 0.75rem;
326 -}
327 -
328 -.parameter-group-line {
329 - flex: 1;
330 - height: 1px;
331 - background: linear-gradient(to right, var(--border-color) 0%, transparent 100%);
332 -}
333 -
334 -.parameter-field {
335 - position: relative;
336 -}
337 -
338 -.parameter-label {
339 - display: flex;
340 - align-items: center;
341 - gap: 0.5rem;
342 -}
343 -
344 -.helper-text {
345 - font-size: 0.875rem;
346 - opacity: 0.7;
347 - margin-top: 0.25rem;
348 -}
349 -
350 -/* Action header styling */
351 -.action-header {
352 - border-color: var(--border-color);
353 -}
354 -
355 -/* Action buttons styling */
356 -.action-buttons {
357 - border-color: var(--border-color);
358 -}
359 -
360 -/* CSS Variables for theme support */
361 -.light-theme {
362 - --border-color: rgba(0, 0, 0, 0.1);
363 -}
364 -
365 -.dark-theme {
366 - --border-color: rgba(255, 255, 255, 0.1);
367 -}
368 -
369 -/* Default fallback */
370 -:not(.light-theme):not(.dark-theme) {
371 - --border-color: rgba(0, 0, 0, 0.1);
372 -}
373 -</style>
frontend/src/components/copilotAction/List.vue
+77 -66
@@ -1,30 +1,20 @@
1 <template>
2 <div class="flex flex-col gap-4">
3 - <!-- Info Banner -->
4 - <div class="info-banner p-3 rounded-lg border border-blue-200 bg-blue-50 dark:border-blue-800 dark:bg-blue-950/30">
5 - <div class="flex items-start gap-3">
6 - <Icon :name="InfoIcon" class="text-blue-600 dark:text-blue-400 mt-0.5" :size="16" />
7 - <p class="text-sm text-blue-800 dark:text-blue-200 leading-relaxed">
8 - CoPilot Actions leverages Velociraptor to run actions and Grafana to view results. See
9 - <a
10 - href="https://github.com/socfortress/CoPilot-Action"
11 - target="_blank"
12 - class="underline hover:no-underline font-medium"
13 - >
14 - https://github.com/socfortress/CoPilot-Action
15 - </a>
16 - for details.
17 - </p>
18 - </div>
19 - </div>
3 + <n-alert type="info">
4 + CoPilot Actions leverages Velociraptor to run actions and Grafana to view results. See
5 + <a href="https://github.com/socfortress/CoPilot-Action" target="_blank">
6 + https://github.com/socfortress/CoPilot-Action
7 + </a>
8 + for details.
9 + </n-alert>
10
11 <div class="flex flex-col">
22 - <div ref="header" class="header flex items-center justify-end gap-2">
23 - <div class="info flex grow gap-2">
12 + <div class="flex flex-wrap items-center justify-end gap-2">
13 + <div class="flex min-w-80 grow gap-2">
14 <n-popover overlap placement="bottom-start">
15 <template #trigger>
16 <div class="bg-default rounded-lg">
27 - <n-button size="small" class="!cursor-help">
17 + <n-button size="small" class="cursor-help!">
18 <template #icon>
19 <Icon :name="InfoIcon"></Icon>
20 </template>
@@ -34,7 +24,7 @@
24 <div class="flex flex-col gap-2">
25 <div class="box">
26 Total Actions:
37 - <code>{{ total }}</code>
27 + <code>{{ pagination.total }}</code>
28 </div>
29 </div>
30 </n-popover>
@@ -45,23 +35,16 @@
35 clearable
36 size="small"
37 placeholder="Technology"
48 - class="max-w-32"
49 - />
50 -
51 - <n-select
52 - v-model:value="selectedCategory"
53 - :options="categoryOptions"
54 - clearable
55 - size="small"
56 - placeholder="Category"
57 - class="max-w-32"
38 + :loading="loadingTechnologies"
39 + class="max-w-30"
40 + :consistent-menu-width="false"
41 />
42
43 <n-input
44 v-model:value="searchQuery"
45 size="small"
46 placeholder="Search actions..."
64 - class="max-w-48"
47 + class="max-w-120!"
48 clearable
49 >
50 <template #prefix>
@@ -69,61 +52,66 @@
52 </template>
53 </n-input>
54 </div>
55 +
56 + <n-pagination
57 + v-model:page="pagination.current"
58 + :page-size="pagination.size"
59 + :item-count="pagination.total"
60 + :page-slot="5"
61 + />
62 </div>
63
64 <n-spin :show="loading">
65 <div class="my-3">
76 - <template v-if="list.length">
77 - <div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-4">
78 - <ActionCard v-for="item of list" :key="item.copilot_action_name" :action="item" />
79 - </div>
80 - </template>
66 + <div v-if="list.length" class="grid grid-cols-1 gap-4 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4">
67 + <ActionCard v-for="item of list" :key="item.copilot_action_name" :action="item" />
68 + </div>
69 +
70 <template v-else>
71 <n-empty v-if="!loading" description="No actions found" class="h-48 justify-center" />
72 </template>
73 </div>
74 </n-spin>
75 +
76 + <div class="flex justify-end">
77 + <n-pagination
78 + v-if="list.length > 3"
79 + v-model:page="pagination.current"
80 + :page-size="pagination.size"
81 + :item-count="pagination.total"
82 + :page-slot="6"
83 + />
84 + </div>
85 </div>
86 </div>
87 </template>
88
89 <script setup lang="ts">
90 import type { CopilotActionInventoryQuery } from "@/api/endpoints/copilotAction"
92 -import type { ActiveResponseItem } from "@/types/copilotAction.d"
91 +import type { CopilotAction } from "@/types/copilotAction.d"
92 import { watchDebounced } from "@vueuse/core"
93 import axios from "axios"
95 -import { NButton, NEmpty, NInput, NPopover, NSelect, NSpin, useMessage } from "naive-ui"
96 -import { computed, ref } from "vue"
94 +import { NAlert, NButton, NEmpty, NInput, NPagination, NPopover, NSelect, NSpin, useMessage } from "naive-ui"
95 +import { onBeforeMount, ref } from "vue"
96 import Api from "@/api"
97 import Icon from "@/components/common/Icon.vue"
99 -import { Technology } from "@/types/copilotAction.d"
98 import ActionCard from "./ActionCard.vue"
99
100 const loading = ref(false)
101 +const loadingTechnologies = ref(false)
102 const message = useMessage()
104 -const list = ref<ActiveResponseItem[]>([])
105 -const header = ref()
106 -const total = ref(0)
107 -const selectedTechnology = ref<Technology | null>(null)
108 -const selectedCategory = ref<string | null>(null)
109 -const searchQuery = ref<string>("")
103 +const list = ref<CopilotAction[]>([])
104 +const pagination = ref({
105 + current: 1,
106 + size: 24,
107 + total: 0
108 +})
109 +const selectedTechnology = ref<string | null>(null)
110 +const technologyOptions = ref<{ label: string; value: string }[]>([])
111 +const searchQuery = ref<string | null>(null)
112 const InfoIcon = "carbon:information"
113 const SearchIcon = "carbon:search"
114
113 -const technologyOptions = Object.values(Technology).map(tech => ({
114 - label: tech,
115 - value: tech
116 -}))
117 -
118 -// Get unique categories from the loaded actions
119 -const categoryOptions = computed(() => {
120 - const categories = [...new Set(list.value.map(action => action.category).filter(Boolean))]
121 - return categories.map(category => ({
122 - label: category,
123 - value: category
124 - }))
125 -})
126 -
115 let abortController: AbortController | null = null
116
117 function getList() {
@@ -133,10 +121,9 @@ function getList() {
121 loading.value = true
122
123 const query: CopilotActionInventoryQuery = {
136 - limit: 100,
137 - offset: 0,
124 + offset: (pagination.value.current - 1) * pagination.value.size,
125 + limit: pagination.value.size,
126 technology: selectedTechnology.value || undefined,
139 - category: selectedCategory.value || undefined,
127 q: searchQuery.value || undefined
128 }
129
@@ -147,7 +134,7 @@ function getList() {
134
135 if (res.data.success) {
136 list.value = res.data?.copilot_actions || []
150 - total.value = res.data?.copilot_actions?.length || 0
137 + pagination.value.total = res.data?.total || 0
138 } else {
139 message.warning(res.data?.message || "An error occurred. Please try again later.")
140 }
@@ -160,9 +147,33 @@ function getList() {
147 })
148 }
149
163 -watchDebounced([selectedTechnology, selectedCategory, searchQuery], getList, {
150 +function getTechnologies() {
151 + loadingTechnologies.value = true
152 +
153 + Api.copilotAction
154 + .getTechnologies()
155 + .then(res => {
156 + if (res.data.success) {
157 + technologyOptions.value = res.data.technologies.map(o => ({ label: o, value: o }))
158 + } else {
159 + message.warning(res.data?.message || "An error occurred. Please try again later.")
160 + }
161 + })
162 + .catch(err => {
163 + message.error(err.response?.data?.message || "An error occurred. Please try again later.")
164 + })
165 + .finally(() => {
166 + loadingTechnologies.value = false
167 + })
168 +}
169 +
170 +watchDebounced([selectedTechnology, searchQuery, () => pagination.value.current], getList, {
171 deep: true,
172 debounce: 300,
173 immediate: true
174 })
175 +
176 +onBeforeMount(() => {
177 + getTechnologies()
178 +})
179 </script>
frontend/src/components/copilotAction/TechnologyBadge.vue new
+43
@@ -0,0 +1,43 @@
1 +<template>
2 + <Badge :color="getTechnologyColor(action.technology)">
3 + <template #iconLeft><TechnologyIcon :action :size="14" /></template>
4 + <template #value>
5 + <span class="whitespace-nowrap">{{ action.technology }}</span>
6 + </template>
7 + </Badge>
8 +</template>
9 +
10 +<script setup lang="ts">
11 +import type { BadgeColor } from "@/components/common/Badge.vue"
12 +import type { CopilotAction } from "@/types/copilotAction.d"
13 +import Badge from "@/components/common/Badge.vue"
14 +import TechnologyIcon from "./TechnologyIcon.vue"
15 +
16 +const { action } = defineProps<{ action: CopilotAction }>()
17 +
18 +function getTechnologyColor(technology: string): BadgeColor | undefined {
19 + if (technology.toLowerCase().includes("win")) {
20 + return "primary"
21 + }
22 + if (technology.toLowerCase().includes("lin")) {
23 + return "warning"
24 + }
25 + if (technology.toLowerCase().includes("mac")) {
26 + return "success"
27 + }
28 + if (technology.toLowerCase().includes("wazuh")) {
29 + return "success"
30 + }
31 + if (technology.toLowerCase().includes("velociraptor")) {
32 + return "primary"
33 + }
34 + if (technology.toLowerCase().includes("network")) {
35 + return "primary"
36 + }
37 + if (technology.toLowerCase().includes("cloud")) {
38 + return "success"
39 + }
40 +
41 + return undefined
42 +}
43 +</script>
frontend/src/components/copilotAction/TechnologyIcon.vue new
+36
@@ -0,0 +1,36 @@
1 +<template>
2 + <Icon :name="getTechnologyIcon(action.technology)" :size />
3 +</template>
4 +
5 +<script setup lang="ts">
6 +import type { CopilotAction } from "@/types/copilotAction.d"
7 +import Icon from "@/components/common/Icon.vue"
8 +import { iconFromOs } from "@/utils"
9 +
10 +const { action, size = 14 } = defineProps<{ action: CopilotAction; size?: number }>()
11 +
12 +function getTechnologyIcon(technology: string): string {
13 + if (
14 + technology.toLowerCase().includes("win") ||
15 + technology.toLowerCase().includes("lin") ||
16 + technology.toLowerCase().includes("mac")
17 + ) {
18 + return iconFromOs(technology)
19 + }
20 +
21 + if (technology.toLowerCase().includes("wazuh")) {
22 + return "carbon:security"
23 + }
24 + if (technology.toLowerCase().includes("velociraptor")) {
25 + return "fluent-emoji-high-contrast:eagle"
26 + }
27 + if (technology.toLowerCase().includes("network")) {
28 + return "carbon:network-3"
29 + }
30 + if (technology.toLowerCase().includes("cloud")) {
31 + return "carbon:cloud"
32 + }
33 +
34 + return "carbon:application"
35 +}
36 +</script>
frontend/src/components/customers/CustomerWazuhWorker.vue
+5 -2
@@ -136,6 +136,7 @@
136 </template>
137
138 <script setup lang="ts">
139 +import type { SafeAny } from "@/types/common.d"
140 import type { PortainerStack } from "@/types/portainer.d"
141 import _castArray from "lodash/castArray"
142 import _pick from "lodash/pick"
@@ -173,7 +174,7 @@ const portainerStackType = computed(
174 )
175
176 const properties = computed(() => {
176 - const props: Partial<{ [key in keyof PortainerStack]: any }> = _pick(portainerStack.value || {}, [
177 + const props: Partial<{ [key in keyof PortainerStack]: SafeAny | null | Date }> = _pick(portainerStack.value || {}, [
178 "EndpointId",
179 "SwarmId",
180 "EntryPoint",
@@ -191,7 +192,9 @@ const properties = computed(() => {
192 ])
193
194 props.Env = _castArray(props.Env).join(", ") || null
194 - props.UpdateDate = props.UpdateDate ? formatDate(props.UpdateDate, dFormats.datetimesec) : null
195 + props.UpdateDate = props.UpdateDate
196 + ? formatDate(props.UpdateDate as string | number | Date, dFormats.datetimesec)
197 + : null
198 props.UpdatedBy = props.UpdatedBy || null
199 props.Namespace = props.Namespace || null
200
frontend/src/components/graylog/Messages/List.vue
+3 -3
@@ -1,7 +1,7 @@
1 <template>
2 <n-spin :show="loading">
3 - <div class="header flex items-center justify-end gap-2">
4 - <div class="info flex grow gap-5">
3 + <div class="flex items-center justify-end gap-2">
4 + <div class="flex grow gap-5">
5 <n-popover overlap placement="bottom-start">
6 <template #trigger>
7 <div class="bg-default rounded-lg">
@@ -30,7 +30,7 @@
30 <n-empty v-if="!loading" description="No items found" class="h-48 justify-center" />
31 </template>
32 </div>
33 - <div class="footer flex justify-end">
33 + <div class="flex justify-end">
34 <n-pagination
35 v-if="messages.length > 3"
36 v-model:page="currentPage"
frontend/src/components/incidentManagement/alerts/AlertsList.vue
+2 -2
@@ -1,6 +1,6 @@
1 <template>
2 <div class="alerts-list">
3 - <div ref="header" class="header flex items-center justify-end gap-2">
3 + <div ref="header" class="flex items-center justify-end gap-2">
4 <div class="info flex grow gap-2 lg:!hidden">
5 <n-popover overlap placement="left">
6 <template #trigger>
@@ -275,11 +275,11 @@ const availableUsers = ref<string[]>([])
275 const linkableCases = ref<Case[]>([])
276 let abortController: AbortController | null = null
277
278 -const pageSize = ref(25)
278 const currentPage = ref(1)
279 const simpleMode = ref(false)
280 const showSizePicker = ref(true)
281 const pageSizes = [10, 25, 50, 100]
282 +const pageSize = ref(pageSizes[1])
283 const header = ref()
284 const pageSlot = ref(8)
285 const sort = defineModel<"asc" | "desc">("sort", { default: "desc" })
frontend/src/components/mitre/AttackSimulator/ParametersList.vue
+1
@@ -76,6 +76,7 @@ async function getList() {
76 list.value = _uniq(fullList, "name")
77 emit("loaded", list.value)
78 } catch (err: any) {
79 + // TODO: remove any
80 message.error(err.response?.data?.message || "An error occurred. Please try again later.")
81 } finally {
82 loading.value = false
frontend/src/components/reportCreation/Panels.vue
+1 -1
@@ -24,7 +24,7 @@
24 ghost-class="ghost-panel"
25 :group="{
26 name: 'panels',
27 - put(to: any) {
27 + put(to: { el: HTMLElement }) {
28 return to.el.children.length < 4
29 },
30 pull: ['panels']
frontend/src/components/sca/List.vue
+117 -783
@@ -1,439 +1,102 @@
1 <template>
2 <div class="flex flex-col gap-4">
3 - <!-- Info Banner -->
4 - <div class="info-banner p-3 rounded-lg border border-blue-200 bg-blue-50 dark:border-blue-800 dark:bg-blue-950/30">
5 - <div class="flex items-start gap-3">
6 - <Icon :name="InfoIcon" class="text-blue-600 dark:text-blue-400 mt-0.5" :size="16" />
7 - <p class="text-sm text-blue-800 dark:text-blue-200 leading-relaxed">
8 - SCA Overview provides real-time Security Configuration Assessment results from Wazuh Manager across all agents with comprehensive compliance scoring.
9 - </p>
10 - </div>
11 - </div>
12 -
13 - <!-- Filters -->
14 - <div class="flex flex-col">
15 - <div ref="header" class="header flex items-center justify-end gap-2">
16 - <div class="info flex grow gap-2">
17 - <n-popover overlap placement="bottom-start">
18 - <template #trigger>
19 - <div class="bg-default rounded-lg">
20 - <n-button size="small" class="!cursor-help">
21 - <template #icon>
22 - <Icon :name="InfoIcon"></Icon>
23 - </template>
24 - </n-button>
25 - </div>
26 - </template>
27 - <div class="flex flex-col gap-3 p-2 max-w-sm">
28 - <div class="font-medium text-sm mb-2">SCA Overview</div>
29 -
30 - <div class="grid grid-cols-2 gap-3 text-xs">
31 - <div class="flex justify-between">
32 - <span>Total Policies:</span>
33 - <code class="font-mono">{{ totalCount.toLocaleString() }}</code>
34 - </div>
35 - <div class="flex justify-between">
36 - <span>Current Page:</span>
37 - <code class="font-mono">{{ currentPage }} / {{ totalPages }}</code>
38 - </div>
39 - </div>
40 -
41 - <div class="border-t pt-2">
42 - <div class="text-xs font-medium mb-2">Compliance Distribution</div>
43 - <div class="grid grid-cols-2 gap-2 text-xs">
44 - <div class="flex justify-between">
45 - <span class="text-green-600">Excellent:</span>
46 - <span class="font-mono">{{ stats.excellent.toLocaleString() }} ({{ getPercentage(stats.excellent) }}%)</span>
47 - </div>
48 - <div class="flex justify-between">
49 - <span class="text-blue-600">Good:</span>
50 - <span class="font-mono">{{ stats.good.toLocaleString() }} ({{ getPercentage(stats.good) }}%)</span>
51 - </div>
52 - <div class="flex justify-between">
53 - <span class="text-yellow-600">Average:</span>
54 - <span class="font-mono">{{ stats.average.toLocaleString() }} ({{ getPercentage(stats.average) }}%)</span>
55 - </div>
56 - <div class="flex justify-between">
57 - <span class="text-orange-600">Poor:</span>
58 - <span class="font-mono">{{ stats.poor.toLocaleString() }} ({{ getPercentage(stats.poor) }}%)</span>
59 - </div>
60 - <div class="flex justify-between">
61 - <span class="text-red-600">Critical:</span>
62 - <span class="font-mono">{{ stats.critical.toLocaleString() }} ({{ getPercentage(stats.critical) }}%)</span>
63 - </div>
64 - </div>
65 - </div>
66 -
67 - <div class="border-t pt-2">
68 - <div class="text-xs font-medium mb-2">Coverage</div>
69 - <div class="space-y-1 text-xs">
70 - <div class="flex justify-between">
71 - <span>Agents:</span>
72 - <span class="font-mono">{{ overviewData?.total_agents?.toLocaleString() || 0 }}</span>
73 - </div>
74 - <div class="flex justify-between">
75 - <span>Policies:</span>
76 - <span class="font-mono">{{ overviewData?.total_policies?.toLocaleString() || 0 }}</span>
77 - </div>
78 - <div class="flex justify-between">
79 - <span>Avg Score:</span>
80 - <span class="font-mono">{{ overviewData?.average_score?.toFixed(1) || 0 }}%</span>
81 - </div>
82 - </div>
83 - </div>
84 - </div>
85 - </n-popover>
86 -
87 - <n-select
88 - v-model:value="selectedCustomer"
89 - :options="customerOptions"
90 - clearable
91 - size="small"
92 - placeholder="Customer"
93 - class="max-w-32"
94 - :loading="loadingCustomers"
95 - />
96 -
97 - <n-input
98 - v-model:value="searchPolicyId"
99 - size="small"
100 - placeholder="Policy ID..."
101 - class="max-w-40"
102 - clearable
103 - >
104 - <template #prefix>
105 - <Icon :name="PolicyIcon"></Icon>
106 - </template>
107 - </n-input>
108 -
109 - <n-input
110 - v-model:value="searchPolicyName"
111 - size="small"
112 - placeholder="Policy name..."
113 - class="max-w-40"
114 - clearable
115 - >
116 - <template #prefix>
117 - <Icon :name="SearchIcon"></Icon>
118 - </template>
119 - </n-input>
120 -
121 - <n-select
122 - v-model:value="searchAgent"
123 - :options="agentOptions"
124 - size="small"
125 - placeholder="Search agent..."
126 - class="max-w-40"
127 - clearable
128 - filterable
129 - :loading="loadingAgents"
130 - />
131 -
132 - <n-input-number
133 - v-model:value="minScore"
134 - size="small"
135 - placeholder="Min score"
136 - class="max-w-32"
137 - :min="0"
138 - :max="100"
139 - clearable
140 - />
141 -
142 - <n-input-number
143 - v-model:value="maxScore"
144 - size="small"
145 - placeholder="Max score"
146 - class="max-w-32"
147 - :min="0"
148 - :max="100"
149 - clearable
150 - />
151 - </div>
152 - </div>
3 + <n-alert type="info">
4 + SCA Overview provides real-time Security Configuration Assessment results from Wazuh Manager across all
5 + agents with comprehensive compliance scoring.
6 + </n-alert>
7 +
8 + <ScaStats
9 + :filters
10 + class="my-8"
11 + @update:min_score="selectMinScore"
12 + @update:max_score="selectMaxScore"
13 + @update:policy_id="selectPolicyID"
14 + />
15 +
16 + <div ref="header" class="flex items-center justify-end gap-2">
17 + <n-pagination
18 + v-model:page="currentPage"
19 + v-model:page-size="pageSize"
20 + :page-slot
21 + :page-sizes
22 + :item-count="totalCount"
23 + :show-size-picker
24 + />
25 + <n-badge :show="filtered" dot type="success" :offset="[-4, 0]">
26 + <n-button size="small" secondary @click="showFiltersView = !showFiltersView">
27 + <template #icon>
28 + <Icon :name="FilterIcon"></Icon>
29 + </template>
30 + </n-button>
31 + </n-badge>
32 </div>
33
155 - <!-- Statistics Cards -->
156 - <div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 xl:grid-cols-6 gap-4 mb-4">
157 - <div class="stat-card">
158 - <div class="stat-header">
159 - <Icon :name="TotalIcon" :size="20" class="text-blue-600" />
160 - <span class="stat-title">Total</span>
161 - </div>
162 - <div class="stat-value">{{ totalCount.toLocaleString() }}</div>
163 - </div>
164 -
165 - <div class="stat-card excellent clickable" :class="{ selected: selectedComplianceLevel === ScaComplianceLevel.Excellent }" @click="selectComplianceLevel(ScaComplianceLevel.Excellent)">
166 - <div class="stat-header">
167 - <Icon :name="ExcellentIcon" :size="20" class="text-green-600" />
168 - <span class="stat-title">Excellent</span>
169 - </div>
170 - <div class="stat-value">{{ stats.excellent.toLocaleString() }}</div>
171 - <div class="stat-percentage">{{ getPercentage(stats.excellent) }}%</div>
172 - </div>
173 -
174 - <div class="stat-card good clickable" :class="{ selected: selectedComplianceLevel === ScaComplianceLevel.Good }" @click="selectComplianceLevel(ScaComplianceLevel.Good)">
175 - <div class="stat-header">
176 - <Icon :name="GoodIcon" :size="20" class="text-blue-600" />
177 - <span class="stat-title">Good</span>
178 - </div>
179 - <div class="stat-value">{{ stats.good.toLocaleString() }}</div>
180 - <div class="stat-percentage">{{ getPercentage(stats.good) }}%</div>
181 - </div>
182 -
183 - <div class="stat-card average clickable" :class="{ selected: selectedComplianceLevel === ScaComplianceLevel.Average }" @click="selectComplianceLevel(ScaComplianceLevel.Average)">
184 - <div class="stat-header">
185 - <Icon :name="AverageIcon" :size="20" class="text-yellow-600" />
186 - <span class="stat-title">Average</span>
187 - </div>
188 - <div class="stat-value">{{ stats.average.toLocaleString() }}</div>
189 - <div class="stat-percentage">{{ getPercentage(stats.average) }}%</div>
190 - </div>
191 -
192 - <div class="stat-card poor clickable" :class="{ selected: selectedComplianceLevel === ScaComplianceLevel.Poor }" @click="selectComplianceLevel(ScaComplianceLevel.Poor)">
193 - <div class="stat-header">
194 - <Icon :name="PoorIcon" :size="20" class="text-orange-600" />
195 - <span class="stat-title">Poor</span>
196 - </div>
197 - <div class="stat-value">{{ stats.poor.toLocaleString() }}</div>
198 - <div class="stat-percentage">{{ getPercentage(stats.poor) }}%</div>
199 - </div>
200 -
201 - <div class="stat-card critical clickable" :class="{ selected: selectedComplianceLevel === ScaComplianceLevel.Critical }" @click="selectComplianceLevel(ScaComplianceLevel.Critical)">
202 - <div class="stat-header">
203 - <Icon :name="CriticalIcon" :size="20" class="text-red-600" />
204 - <span class="stat-title">Critical</span>
205 - </div>
206 - <div class="stat-value">{{ stats.critical.toLocaleString() }}</div>
207 - <div class="stat-percentage">{{ getPercentage(stats.critical) }}%</div>
208 - </div>
209 - </div>
210 -
211 - <!-- Top 5 Policies by Compliance Score -->
212 - <div v-if="topPoliciesByScore.length > 0" class="mb-4">
213 - <h3 class="text-lg font-semibold mb-3 text-gray-900 dark:text-gray-100 flex items-center gap-2">
214 - <Icon :name="TopPoliciesIcon" :size="20" class="text-green-600" />
215 - Top 5 Policies by Compliance Score
216 - </h3>
217 - <div class="grid grid-cols-1 lg:grid-cols-2 xl:grid-cols-3 gap-4">
218 - <div
219 - v-for="(policy, index) in topPoliciesByScore.slice(0, 5)"
220 - :key="`${policy.policy_id}-${policy.score}`"
221 - class="policy-card clickable"
222 - :class="{
223 - 'rank-1': index === 0,
224 - 'rank-2': index === 1,
225 - 'rank-3': index === 2,
226 - 'selected': searchPolicyId === policy.policy_id
227 - }"
228 - @click="selectPolicy(policy.policy_id)"
229 - >
230 - <div class="policy-header">
231 - <div class="policy-rank">
232 - <Icon
233 - :name="index < 3 ? 'carbon:trophy' : 'carbon:security'"
234 - :size="16"
235 - :class="index === 0 ? 'text-yellow-500' : index === 1 ? 'text-gray-400' : index === 2 ? 'text-amber-600' : 'text-green-500'"
236 - />
237 - <span class="rank-number">#{{ index + 1 }}</span>
238 - </div>
239 - <Badge :color="getComplianceLevelColor(getComplianceLevel(policy.score))" type="splitted" size="small">
240 - <template #label>Score</template>
241 - <template #value>{{ policy.score }}%</template>
242 - </Badge>
243 - </div>
244 -
245 - <div class="policy-name">{{ policy.policy_name }}</div>
246 -
247 - <div class="policy-stats">
248 - <div class="stat-row">
249 - <span class="stat-label">Policy ID:</span>
250 - <span class="stat-value-sm">{{ policy.policy_id }}</span>
251 - </div>
252 - <div class="stat-row">
253 - <span class="stat-label">Agents:</span>
254 - <span class="stat-value-sm">{{ policy.agentCount.toLocaleString() }}</span>
255 - </div>
256 - <div class="stat-row">
257 - <span class="stat-label">Total Checks:</span>
258 - <span class="stat-value-sm">{{ policy.total_checks.toLocaleString() }}</span>
259 - </div>
260 - </div>
261 -
262 - <!-- Pass/Fail indicator -->
263 - <div class="compliance-indicator">
264 - <Badge color="success" size="small">
265 - <template #value>{{ policy.pass }} Pass</template>
266 - </Badge>
267 - <Badge v-if="policy.fail > 0" color="danger" size="small">
268 - <template #value>{{ policy.fail }} Fail</template>
269 - </Badge>
270 - </div>
271 - </div>
272 - </div>
273 - </div>
34 + <CollapseKeepAlive :show="showFiltersView" embedded arrow="top-right">
35 + <ListFilters class="p-3" @submit="applyFilters" @mounted="filtersCTX = $event" />
36 + </CollapseKeepAlive>
37
38 <!-- SCA Results List -->
39 <n-spin :show="loading">
40 <div class="my-3">
278 - <template v-if="list.length">
279 - <div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-4">
280 - <ScaCard v-for="item of list" :key="`${item.policy_id}-${item.agent_name}`" :sca="item" />
281 - </div>
282 -
283 - <!-- Pagination -->
284 - <div class="flex justify-center mt-6">
285 - <n-pagination
286 - v-model:page="currentPage"
287 - :page-count="totalPages"
288 - :page-size="pageSize"
289 - :item-count="totalCount"
290 - show-size-picker
291 - :page-sizes="[25, 50, 100, 200]"
292 - @update:page="updatePage"
293 - @update:page-size="updatePageSize"
294 - />
295 - </div>
296 - </template>
41 + <div v-if="list.length" class="grid grid-cols-1 gap-4 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4">
42 + <ScaCard v-for="item of list" :key="JSON.stringify(item)" :sca="item" />
43 + </div>
44 <template v-else>
45 <n-empty v-if="!loading" description="No SCA results found" class="h-48 justify-center" />
46 </template>
47 </div>
48 </n-spin>
49 +
50 + <div v-if="list.length >= 9" class="flex justify-end">
51 + <n-pagination
52 + v-model:page="currentPage"
53 + v-model:page-size="pageSize"
54 + :page-slot
55 + :page-sizes
56 + :item-count="totalCount"
57 + :show-size-picker
58 + />
59 + </div>
60 </div>
61 </template>
62
63 <script setup lang="ts">
306 -import type { Agent } from "@/types/agents.d"
307 -import type { Customer } from "@/types/customers.d"
308 -import type { AgentScaOverviewItem, ScaOverviewQuery, ScaOverviewResponse } from "@/types/sca.d"
309 -import { watchDebounced } from "@vueuse/core"
64 +import type { ScaOverviewFilter, ScaOverviewFilterTypes } from "./types"
65 +import type { AgentScaOverviewItem, ScaOverviewQuery } from "@/types/sca.d"
66 +import { useResizeObserver, useStorage, watchDebounced } from "@vueuse/core"
67 import axios from "axios"
311 -import { NButton, NEmpty, NInput, NInputNumber, NPagination, NPopover, NSelect, NSpin, useMessage } from "naive-ui"
312 -import { computed, onMounted, ref } from "vue"
68 +import _set from "lodash/set"
69 +import _toNumber from "lodash/toSafeInteger"
70 +import { NAlert, NBadge, NButton, NEmpty, NPagination, NSpin, useMessage } from "naive-ui"
71 +import { computed, ref } from "vue"
72 import Api from "@/api"
73 +import CollapseKeepAlive from "@/components/common/CollapseKeepAlive.vue"
74 import Icon from "@/components/common/Icon.vue"
315 -import { getComplianceLevel, getComplianceLevelColor, ScaComplianceLevel } from "@/types/sca.d"
75 +import ListFilters from "./ListFilters.vue"
76 import ScaCard from "./ScaCard.vue"
77 +import ScaStats from "./ScaStats.vue"
78
79 const loading = ref(false)
80 const message = useMessage()
81 const list = ref<AgentScaOverviewItem[]>([])
82 +const pageSizes = [10, 25, 50, 100]
83 +const pageSize = ref(pageSizes[1])
84 +const pageSlot = ref(8)
85 +const showSizePicker = ref(true)
86 const header = ref()
322 -const totalCount = ref(0)
323 -const currentPage = ref(1)
324 -const pageSize = ref(50)
325 -const totalPages = ref(0)
326 -const selectedCustomer = ref<string | null>(null)
327 -const selectedComplianceLevel = ref<ScaComplianceLevel | null>(null)
328 -const searchPolicyId = ref<string>("")
329 -const searchPolicyName = ref<string>("")
330 -const searchAgent = ref<string>("")
331 -const minScore = ref<number | null>(null)
332 -const maxScore = ref<number | null>(null)
333 -
334 -// Overview data from API response
335 -const overviewData = ref<ScaOverviewResponse | null>(null)
87 +const showFiltersView = useStorage<boolean>("agents-sca-list-filters-view-state", false, localStorage)
88
337 -// Agents data for dropdown
338 -const agents = ref<Agent[]>([])
339 -const loadingAgents = ref(false)
89 +const filtersCTX = ref<{ setFilter: (payload: ScaOverviewFilter[]) => void } | null>(null)
90 +const filters = ref<ScaOverviewFilter[]>([])
91
341 -// Customers data for dropdown
342 -const customers = ref<Customer[]>([])
343 -const loadingCustomers = ref(false)
344 -
345 -const InfoIcon = "carbon:information"
346 -const SearchIcon = "carbon:search"
347 -const PolicyIcon = "carbon:security"
348 -const TotalIcon = "carbon:result"
349 -const ExcellentIcon = "carbon:checkmark-filled"
350 -const GoodIcon = "carbon:checkmark"
351 -const AverageIcon = "carbon:warning-alt"
352 -const PoorIcon = "carbon:warning"
353 -const CriticalIcon = "carbon:warning-filled"
354 -const TopPoliciesIcon = "carbon:trophy"
355 -
356 -// Agent options for dropdown
357 -const agentOptions = computed(() => {
358 - return (agents.value || []).map(agent => ({
359 - label: agent.hostname,
360 - value: agent.hostname
361 - }))
362 -})
363 -
364 -// Customer options for dropdown
365 -const customerOptions = computed(() => {
366 - return (customers.value || []).map(customer => ({
367 - label: customer.customer_code,
368 - value: customer.customer_code
369 - }))
370 -})
371 -
372 -// Calculate statistics from current data
373 -const stats = computed(() => {
374 - // Calculate compliance level distribution from current data
375 - const excellent = list.value.filter(item => getComplianceLevel(item.score) === ScaComplianceLevel.Excellent).length
376 - const good = list.value.filter(item => getComplianceLevel(item.score) === ScaComplianceLevel.Good).length
377 - const average = list.value.filter(item => getComplianceLevel(item.score) === ScaComplianceLevel.Average).length
378 - const poor = list.value.filter(item => getComplianceLevel(item.score) === ScaComplianceLevel.Poor).length
379 - const critical = list.value.filter(item => getComplianceLevel(item.score) === ScaComplianceLevel.Critical).length
380 -
381 - return {
382 - excellent,
383 - good,
384 - average,
385 - poor,
386 - critical
387 - }
92 +const filtered = computed<boolean>(() => {
93 + return !!filters.value.length
94 })
95
390 -// Calculate top policies by compliance score
391 -const topPoliciesByScore = computed(() => {
392 - // Group policies and calculate averages
393 - const policyMap = new Map<string, {
394 - policy_id: string
395 - policy_name: string
396 - score: number
397 - agentCount: number
398 - total_checks: number
399 - pass: number
400 - fail: number
401 - }>()
402 -
403 - list.value.forEach(item => {
404 - const key = item.policy_id
405 - const existing = policyMap.get(key)
406 -
407 - if (existing) {
408 - // Update averages and counts
409 - existing.agentCount++
410 - existing.score = Math.max(existing.score, item.score) // Use highest score for ranking
411 - existing.total_checks += item.total_checks
412 - existing.pass += item.pass
413 - existing.fail += item.fail
414 - } else {
415 - policyMap.set(key, {
416 - policy_id: item.policy_id,
417 - policy_name: item.policy_name,
418 - score: item.score,
419 - agentCount: 1,
420 - total_checks: item.total_checks,
421 - pass: item.pass,
422 - fail: item.fail
423 - })
424 - }
425 - })
426 -
427 - // Convert to array and sort by score
428 - return Array.from(policyMap.values())
429 - .sort((a, b) => b.score - a.score)
430 - .slice(0, 5)
431 -})
96 +const totalCount = ref(0)
97 +const currentPage = ref(1)
98
433 -function getPercentage(count: number): string {
434 - if (totalCount.value === 0) return "0"
435 - return ((count / totalCount.value) * 100).toFixed(1)
436 -}
99 +const FilterIcon = "carbon:filter-edit"
100
101 let abortController: AbortController | null = null
102
@@ -445,38 +108,17 @@ function getList() {
108
109 const query: ScaOverviewQuery = {
110 page: currentPage.value,
448 - page_size: pageSize.value,
449 - customer_code: selectedCustomer.value || undefined,
450 - policy_id: searchPolicyId.value || undefined,
451 - policy_name: searchPolicyName.value || undefined,
452 - agent_name: searchAgent.value || undefined,
453 - min_score: minScore.value || undefined,
454 - max_score: maxScore.value || undefined
111 + page_size: pageSize.value
112 }
113
457 - // Apply compliance level filter by converting to score range
458 - if (selectedComplianceLevel.value) {
459 - switch (selectedComplianceLevel.value) {
460 - case ScaComplianceLevel.Excellent:
461 - query.min_score = 90
462 - query.max_score = 100
463 - break
464 - case ScaComplianceLevel.Good:
465 - query.min_score = 80
466 - query.max_score = 89
467 - break
468 - case ScaComplianceLevel.Average:
469 - query.min_score = 70
470 - query.max_score = 79
471 - break
472 - case ScaComplianceLevel.Poor:
473 - query.min_score = 60
474 - query.max_score = 69
475 - break
476 - case ScaComplianceLevel.Critical:
477 - query.min_score = 0
478 - query.max_score = 59
479 - break
114 + for (const key of ["customer_code", "policy_id", "policy_name", "agent_name"] as ScaOverviewFilterTypes[]) {
115 + if (filters.value.find(o => o.type === key)?.value) {
116 + _set(query, key, `${filters.value.find(o => o.type === key)?.value}`)
117 + }
118 + }
119 + for (const key of ["min_score", "max_score"] as ScaOverviewFilterTypes[]) {
120 + if (filters.value.find(o => o.type === key)?.value) {
121 + _set(query, key, _toNumber(filters.value.find(o => o.type === key)?.value))
122 }
123 }
124
@@ -488,9 +130,7 @@ function getList() {
130 if (res.data.success) {
131 list.value = res.data?.sca_results || []
132 totalCount.value = res.data?.total_count || 0
491 - totalPages.value = res.data?.total_pages || 0
133 currentPage.value = res.data?.page || 1
493 - overviewData.value = res.data
134 } else {
135 message.warning(res.data?.message || "An error occurred. Please try again later.")
136 }
@@ -503,362 +143,56 @@ function getList() {
143 })
144 }
145
506 -function getAgents() {
507 - loadingAgents.value = true
508 -
509 - Api.agents
510 - .getAgents()
511 - .then(res => {
512 - if (res.data.success) {
513 - agents.value = res.data.agents || []
514 - } else {
515 - message.warning(res.data?.message || "Failed to load agents.")
516 - }
517 - })
518 - .catch(err => {
519 - message.error(err.response?.data?.message || "Failed to load agents.")
520 - })
521 - .finally(() => {
522 - loadingAgents.value = false
523 - })
146 +function selectPolicyID(value: string) {
147 + showFiltersView.value = true
148 + filtersCTX.value?.setFilter([{ type: "policy_id", value }])
149 }
150
526 -function getCustomers() {
527 - loadingCustomers.value = true
528 -
529 - Api.customers
530 - .getCustomers()
531 - .then(res => {
532 - if (res.data.success) {
533 - customers.value = res.data.customers || []
534 - } else {
535 - message.warning(res.data?.message || "Failed to load customers.")
536 - }
537 - })
538 - .catch(err => {
539 - message.error(err.response?.data?.message || "Failed to load customers.")
540 - })
541 - .finally(() => {
542 - loadingCustomers.value = false
543 - })
151 +function selectMinScore(value: number) {
152 + showFiltersView.value = true
153 + filtersCTX.value?.setFilter([{ type: "min_score", value }])
154 }
155
546 -function updatePage(page: number) {
547 - currentPage.value = page
548 - getList()
156 +function selectMaxScore(value: number) {
157 + showFiltersView.value = true
158 + filtersCTX.value?.setFilter([{ type: "max_score", value }])
159 }
160
551 -function updatePageSize(size: number) {
552 - pageSize.value = size
553 - currentPage.value = 1
554 - getList()
161 +function applyFilters(newFilters: ScaOverviewFilter[]) {
162 + filters.value = newFilters
163 }
164
557 -function selectPolicy(policyId: string) {
558 - // If the same policy is already selected, clear the filter
559 - if (searchPolicyId.value === policyId) {
560 - searchPolicyId.value = ""
561 - } else {
562 - // Set the policy ID in the search filter
563 - searchPolicyId.value = policyId
564 - }
565 - // Reset to first page when filtering
566 - currentPage.value = 1
567 -}
165 +watchDebounced(
166 + currentPage,
167 + () => {
168 + getList()
169 + },
170 + { debounce: 300 }
171 +)
172
569 -function selectComplianceLevel(level: ScaComplianceLevel) {
570 - // If the same level is already selected, clear the filter
571 - if (selectedComplianceLevel.value === level) {
572 - selectedComplianceLevel.value = null
573 - } else {
574 - // Set the compliance level filter
575 - selectedComplianceLevel.value = level
576 - }
577 - // Reset to first page when filtering
578 - currentPage.value = 1
579 -}
173 +watchDebounced(
174 + pageSize,
175 + () => {
176 + currentPage.value = 1
177 + getList()
178 + },
179 + { debounce: 300 }
180 +)
181
581 -// Load agents and customers when component mounts
582 -onMounted(() => {
583 - getAgents()
584 - getCustomers()
585 -})
182 +watchDebounced(
183 + filters,
184 + () => {
185 + currentPage.value = 1
186 + getList()
187 + },
188 + { deep: true, debounce: 300, immediate: true }
189 +)
190 +
191 +useResizeObserver(header, entries => {
192 + const entry = entries[0]
193 + const { width } = entry.contentRect
194
587 -watchDebounced([selectedCustomer, selectedComplianceLevel, searchPolicyId, searchPolicyName, searchAgent, minScore, maxScore], () => {
588 - currentPage.value = 1
589 - getList()
590 -}, {
591 - deep: true,
592 - debounce: 300,
593 - immediate: true
195 + pageSlot.value = width < 700 ? 5 : 8
196 + showSizePicker.value = width > 550
197 })
198 </script>
596 -
597 -<style scoped>
598 -.stat-card {
599 - background-color: white;
600 - border-radius: 0.5rem;
601 - padding: 1rem;
602 - border: 1px solid rgb(229 231 235);
603 - box-shadow: 0 1px 2px 0 rgb(0 0 0 / 0.05);
604 - transition: all 0.2s ease;
605 -}
606 -
607 -.stat-card.clickable {
608 - cursor: pointer;
609 -}
610 -
611 -.stat-card:hover {
612 - transform: translateY(-2px);
613 - box-shadow: 0 4px 8px 0 rgb(0 0 0 / 0.1);
614 -}
615 -
616 -.stat-card.selected {
617 - border-width: 2px;
618 - box-shadow: 0 4px 12px 0 rgb(59 130 246 / 0.3);
619 -}
620 -
621 -.stat-card.excellent {
622 - border-color: rgb(34 197 94);
623 - background-color: rgb(240 253 244);
624 -}
625 -
626 -.stat-card.excellent.selected {
627 - border-color: rgb(34 197 94);
628 - background-color: rgb(220 252 231);
629 -}
630 -
631 -.stat-card.good {
632 - border-color: rgb(59 130 246);
633 - background-color: rgb(239 246 255);
634 -}
635 -
636 -.stat-card.good.selected {
637 - border-color: rgb(59 130 246);
638 - background-color: rgb(219 234 254);
639 -}
640 -
641 -.stat-card.average {
642 - border-color: rgb(234 179 8);
643 - background-color: rgb(254 252 232);
644 -}
645 -
646 -.stat-card.average.selected {
647 - border-color: rgb(234 179 8);
648 - background-color: rgb(254 249 195);
649 -}
650 -
651 -.stat-card.poor {
652 - border-color: rgb(249 115 22);
653 - background-color: rgb(255 247 237);
654 -}
655 -
656 -.stat-card.poor.selected {
657 - border-color: rgb(249 115 22);
658 - background-color: rgb(255 237 213);
659 -}
660 -
661 -.stat-card.critical {
662 - border-color: rgb(239 68 68);
663 - background-color: rgb(254 242 242);
664 -}
665 -
666 -.stat-card.critical.selected {
667 - border-color: rgb(239 68 68);
668 - background-color: rgb(254 226 226);
669 -}
670 -
671 -.stat-header {
672 - display: flex;
673 - align-items: center;
674 - gap: 0.5rem;
675 - margin-bottom: 0.5rem;
676 -}
677 -
678 -.stat-title {
679 - font-size: 0.875rem;
680 - font-weight: 500;
681 - color: rgb(75 85 99);
682 -}
683 -
684 -.stat-value {
685 - font-size: 1.5rem;
686 - font-weight: 700;
687 - color: rgb(17 24 39);
688 -}
689 -
690 -.stat-percentage {
691 - font-size: 0.75rem;
692 - color: rgb(107 114 128);
693 - margin-top: 0.25rem;
694 -}
695 -
696 -/* Policy Cards */
697 -.policy-card {
698 - background-color: white;
699 - border: 1px solid rgb(229 231 235);
700 - border-radius: 0.5rem;
701 - padding: 1rem;
702 - transition: all 0.2s ease;
703 -}
704 -
705 -.policy-card.clickable {
706 - cursor: pointer;
707 -}
708 -
709 -.policy-card:hover {
710 - border-color: rgb(156 163 175);
711 - box-shadow: 0 4px 6px -1px rgb(0 0 0 / 0.1);
712 - transform: translateY(-2px);
713 -}
714 -
715 -.policy-card.selected {
716 - border-color: rgb(59 130 246);
717 - background-color: rgb(239 246 255);
718 - box-shadow: 0 4px 12px -1px rgb(59 130 246 / 0.2);
719 -}
720 -
721 -.policy-card.rank-1 {
722 - border-color: rgb(234 179 8);
723 - background-color: rgb(255 255 255);
724 -}
725 -
726 -.policy-card.rank-2 {
727 - border-color: rgb(156 163 175);
728 - background-color: rgb(255 255 255);
729 -}
730 -
731 -.policy-card.rank-3 {
732 - border-color: rgb(217 119 6);
733 - background-color: rgb(255 255 255);
734 -}
735 -
736 -.policy-header {
737 - display: flex;
738 - justify-content: space-between;
739 - align-items: center;
740 - margin-bottom: 0.75rem;
741 -}
742 -
743 -.policy-rank {
744 - display: flex;
745 - align-items: center;
746 - gap: 0.5rem;
747 -}
748 -
749 -.rank-number {
750 - font-weight: 600;
751 - font-size: 0.875rem;
752 - color: rgb(75 85 99);
753 -}
754 -
755 -.policy-name {
756 - font-weight: 600;
757 - font-size: 1rem;
758 - color: rgb(17 24 39);
759 - margin-bottom: 0.75rem;
760 - font-family: ui-monospace, SFMono-Regular, "SF Mono", Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
761 -}
762 -
763 -.policy-stats {
764 - display: flex;
765 - flex-direction: column;
766 - gap: 0.5rem;
767 - margin-bottom: 0.75rem;
768 -}
769 -
770 -.stat-row {
771 - display: flex;
772 - justify-content: space-between;
773 - align-items: center;
774 -}
775 -
776 -.stat-label {
777 - font-size: 0.75rem;
778 - color: rgb(107 114 128);
779 - font-weight: 500;
780 -}
781 -
782 -.stat-value-sm {
783 - font-size: 0.875rem;
784 - font-weight: 600;
785 - color: rgb(17 24 39);
786 - font-family: ui-monospace, SFMono-Regular, "SF Mono", Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
787 -}
788 -
789 -.compliance-indicator {
790 - display: flex;
791 - gap: 0.5rem;
792 - flex-wrap: wrap;
793 -}
794 -
795 -/* Dark mode styles */
796 -html.dark .stat-card {
797 - background-color: rgb(31 41 55);
798 - border-color: rgb(75 85 99);
799 -}
800 -
801 -html.dark .stat-card.excellent {
802 - border-color: rgb(34 197 94);
803 - background-color: rgb(20 83 45);
804 -}
805 -
806 -html.dark .stat-card.good {
807 - border-color: rgb(59 130 246);
808 - background-color: rgb(30 64 175);
809 -}
810 -
811 -html.dark .stat-card.average {
812 - border-color: rgb(234 179 8);
813 - background-color: rgb(161 98 7);
814 -}
815 -
816 -html.dark .stat-card.poor {
817 - border-color: rgb(249 115 22);
818 - background-color: rgb(154 52 18);
819 -}
820 -
821 -html.dark .stat-card.critical {
822 - border-color: rgb(239 68 68);
823 - background-color: rgb(127 29 29);
824 -}
825 -
826 -html.dark .stat-title {
827 - color: rgb(209 213 219);
828 -}
829 -
830 -html.dark .stat-value {
831 - color: rgb(255 255 255);
832 -}
833 -
834 -html.dark .stat-percentage {
835 - color: rgb(209 213 219);
836 -}
837 -
838 -html.dark .policy-card {
839 - background-color: rgb(31 41 55);
840 - border-color: rgb(75 85 99);
841 -}
842 -
843 -html.dark .policy-card:hover {
844 - border-color: rgb(156 163 175);
845 -}
846 -
847 -html.dark .policy-card.selected {
848 - border-color: rgb(96 165 250);
849 - background-color: rgb(30 58 138);
850 - box-shadow: 0 4px 12px -1px rgb(96 165 250 / 0.3);
851 -}
852 -
853 -html.dark .policy-name {
854 - color: rgb(243 244 246);
855 -}
856 -
857 -html.dark .stat-label {
858 - color: rgb(156 163 175);
859 -}
860 -
861 -html.dark .stat-value-sm {
862 - color: rgb(243 244 246);
863 -}
864 -</style>
frontend/src/components/sca/ListFilters.vue new
+281
@@ -0,0 +1,281 @@
1 +<!-- eslint-disable vue/operator-linebreak -->
2 +<!-- eslint-disable vue/html-indent -->
3 +<template>
4 + <div class="alerts-filters flex flex-wrap gap-3">
5 + <div v-for="filter of filters" :key="filter.type">
6 + <n-input-group v-if="filter.type === 'customer_code'">
7 + <n-input-group-label size="small" class="flex! items-center gap-2">
8 + <Icon :name="CustomersIcon" />
9 + {{ getFilterLabel(filter.type) }}
10 + </n-input-group-label>
11 + <n-select
12 + v-model:value="filter.value"
13 + size="small"
14 + :options="customersOptions"
15 + placeholder="Select..."
16 + :loading="loadingCustomers"
17 + filterable
18 + class="w-50!"
19 + :consistent-menu-width="false"
20 + />
21 + <n-button size="small" secondary tabindex="-1" @click="delFilter(filter.type)">
22 + <template #icon>
23 + <Icon :name="DelIcon" />
24 + </template>
25 + </n-button>
26 + </n-input-group>
27 +
28 + <n-input-group v-if="filter.type === 'agent_name'">
29 + <n-input-group-label size="small" class="flex! items-center gap-2">
30 + <Icon :name="AgentsIcon" />
31 + {{ getFilterLabel(filter.type) }}
32 + </n-input-group-label>
33 + <n-select
34 + v-model:value="filter.value"
35 + :options="agentsOptions"
36 + placeholder="Select..."
37 + size="small"
38 + filterable
39 + class="w-50!"
40 + :loading="loadingAgents"
41 + :consistent-menu-width="false"
42 + />
43 + <n-button size="small" secondary tabindex="-1" @click="delFilter(filter.type)">
44 + <template #icon>
45 + <Icon :name="DelIcon" />
46 + </template>
47 + </n-button>
48 + </n-input-group>
49 +
50 + <n-input-group
51 + v-if="filter.type === 'policy_id' && (typeof filter.value === 'string' || filter.value === null)"
52 + >
53 + <n-input-group-label size="small" class="flex! items-center gap-2">
54 + <Icon :name="PolicyIDIcon" />
55 + {{ getFilterLabel(filter.type) }}
56 + </n-input-group-label>
57 + <n-input v-model:value="filter.value" autosize placeholder="Input..." size="small" class="min-w-60!" />
58 + <n-button size="small" secondary tabindex="-1" @click="delFilter(filter.type)">
59 + <template #icon>
60 + <Icon :name="DelIcon" />
61 + </template>
62 + </n-button>
63 + </n-input-group>
64 +
65 + <n-input-group
66 + v-if="filter.type === 'policy_name' && (typeof filter.value === 'string' || filter.value === null)"
67 + >
68 + <n-input-group-label size="small" class="flex! items-center gap-2">
69 + <Icon :name="PolicyNameIcon" />
70 + {{ getFilterLabel(filter.type) }}
71 + </n-input-group-label>
72 + <n-input v-model:value="filter.value" autosize placeholder="Input..." size="small" class="min-w-60!" />
73 + <n-button size="small" secondary tabindex="-1" @click="delFilter(filter.type)">
74 + <template #icon>
75 + <Icon :name="DelIcon" />
76 + </template>
77 + </n-button>
78 + </n-input-group>
79 +
80 + <n-input-group
81 + v-if="
82 + (filter.type === 'min_score' || filter.type === 'max_score') &&
83 + (typeof filter.value === 'number' || filter.value === null)
84 + "
85 + >
86 + <n-input-group-label size="small" class="flex! items-center gap-2">
87 + <Icon :name="MinMaxIcon" />
88 + {{ getFilterLabel(filter.type) }}
89 + </n-input-group-label>
90 + <n-input-number
91 + v-model:value="filter.value"
92 + autosize
93 + placeholder="000"
94 + size="small"
95 + class="min-w-60!"
96 + :min="0"
97 + :max="100"
98 + />
99 + <n-button size="small" secondary tabindex="-1" @click="delFilter(filter.type)">
100 + <template #icon>
101 + <Icon :name="DelIcon" />
102 + </template>
103 + </n-button>
104 + </n-input-group>
105 + </div>
106 +
107 + <n-dropdown
108 + v-if="availableFilters.length"
109 + placement="bottom-start"
110 + trigger="click"
111 + :options="availableFilters"
112 + @select="addFilter"
113 + >
114 + <n-button size="small" dashed @click="load()">
115 + <template #icon>
116 + <Icon :name="AddIcon" />
117 + </template>
118 + <span v-if="!filters.length">Add filter</span>
119 + </n-button>
120 + </n-dropdown>
121 +
122 + <n-button v-if="filters.length && isDirty" size="small" secondary type="primary" @click="submit()">
123 + Submit
124 + </n-button>
125 +
126 + <n-button v-if="filters.length" size="small" quaternary @click="reset()">Reset</n-button>
127 + </div>
128 +</template>
129 +
130 +<script setup lang="ts">
131 +import type { ScaOverviewFilter, ScaOverviewFilterTypes } from "./types.d"
132 +import type { Agent } from "@/types/agents.d"
133 +import type { Customer } from "@/types/customers.d"
134 +import _cloneDeep from "lodash/cloneDeep"
135 +import _isEqual from "lodash/isEqual"
136 +import { NButton, NDropdown, NInput, NInputGroup, NInputGroupLabel, NInputNumber, NSelect, useMessage } from "naive-ui"
137 +import { computed, onMounted, ref } from "vue"
138 +import Api from "@/api"
139 +import Icon from "@/components/common/Icon.vue"
140 +
141 +const emit = defineEmits<{
142 + (e: "submit", value: ScaOverviewFilter[]): void
143 + (
144 + e: "mounted",
145 + value: {
146 + setFilter: (payload: ScaOverviewFilter[]) => void
147 + }
148 + ): void
149 +}>()
150 +
151 +const CustomersIcon = "carbon:user-multiple"
152 +const PolicyIDIcon = "carbon:security"
153 +const PolicyNameIcon = "carbon:search"
154 +const AgentsIcon = "carbon:network-3"
155 +const MinMaxIcon = "carbon:hashtag"
156 +
157 +const AddIcon = "carbon:add"
158 +const DelIcon = "carbon:delete"
159 +const message = useMessage()
160 +const loadingAgents = ref(false)
161 +const loadingCustomers = ref(false)
162 +const agentsList = ref<Agent[]>([])
163 +const customersList = ref<Customer[]>([])
164 +
165 +const customersOptions = computed(() =>
166 + customersList.value.map(o => ({ label: `#${o.customer_code} - ${o.customer_name}`, value: o.customer_code }))
167 +)
168 +
169 +const agentsOptions = computed(() => agentsList.value.map(o => ({ label: o.hostname, value: o.hostname })))
170 +
171 +const typeOptions: { label: string; value: ScaOverviewFilterTypes }[] = [
172 + { label: "Customer", value: "customer_code" },
173 + { label: "Policy ID", value: "policy_id" },
174 + { label: "Policy Name", value: "policy_name" },
175 + { label: "Agent", value: "agent_name" },
176 + { label: "Min Score", value: "min_score" },
177 + { label: "Max Score", value: "max_score" }
178 +]
179 +
180 +const filters = ref<ScaOverviewFilter[]>([])
181 +const lastFilters = ref<ScaOverviewFilter[]>([])
182 +
183 +const availableFilters = computed(() =>
184 + typeOptions
185 + .filter(o => !filters.value.map(f => f.type).includes(o.value))
186 + .map(t => ({ key: t.value, label: t.label }))
187 +)
188 +
189 +const isDirty = computed(() => !_isEqual(filters.value, lastFilters.value))
190 +
191 +function getFilterLabel(type: ScaOverviewFilterTypes): string {
192 + return typeOptions.find(o => o.value === type)?.label || type
193 +}
194 +
195 +function addFilter(key: ScaOverviewFilterTypes) {
196 + filters.value.push({ type: key, value: null })
197 +}
198 +
199 +function delFilter(key: ScaOverviewFilterTypes) {
200 + filters.value = filters.value.filter(o => o.type !== key)
201 + submit()
202 +}
203 +
204 +function setFilter(newFilters: ScaOverviewFilter[]) {
205 + for (const newFilter of newFilters) {
206 + const filterIndex = filters.value.findIndex(o => o.type === newFilter.type)
207 +
208 + if (filterIndex !== -1) {
209 + if (newFilter.value) {
210 + filters.value[filterIndex].value = newFilter.value
211 + } else {
212 + delFilter(newFilter.type)
213 + }
214 + } else if (newFilter.value) {
215 + filters.value.push(newFilter)
216 + }
217 + }
218 + submit()
219 +}
220 +
221 +function reset() {
222 + filters.value = []
223 + submit()
224 +}
225 +
226 +function submit() {
227 + lastFilters.value = _cloneDeep(filters.value)
228 + emit("submit", lastFilters.value)
229 +}
230 +
231 +function getAgents() {
232 + loadingAgents.value = true
233 +
234 + Api.agents
235 + .getAgents()
236 + .then(res => {
237 + if (res.data.success) {
238 + agentsList.value = res.data.agents || []
239 + } else {
240 + message.warning(res.data?.message || "Failed to load agents.")
241 + }
242 + })
243 + .catch(err => {
244 + message.error(err.response?.data?.message || "Failed to load agents.")
245 + })
246 + .finally(() => {
247 + loadingAgents.value = false
248 + })
249 +}
250 +
251 +function getCustomers() {
252 + loadingCustomers.value = true
253 +
254 + Api.customers
255 + .getCustomers()
256 + .then(res => {
257 + if (res.data.success) {
258 + customersList.value = res.data?.customers || []
259 + } else {
260 + message.warning(res.data?.message || "An error occurred. Please try again later.")
261 + }
262 + })
263 + .catch(err => {
264 + message.error(err.response?.data?.message || "An error occurred. Please try again later.")
265 + })
266 + .finally(() => {
267 + loadingCustomers.value = false
268 + })
269 +}
270 +
271 +function load() {
272 + getAgents()
273 + getCustomers()
274 +}
275 +
276 +onMounted(() => {
277 + emit("mounted", {
278 + setFilter
279 + })
280 +})
281 +</script>
frontend/src/components/sca/ScaCard.vue
+50 -49
@@ -1,22 +1,30 @@
1 <template>
2 - <div class="sca-card h-full">
3 - <CardEntity hoverable clickable :embedded class="@container h-full flex flex-col" :class="getComplianceBorderClass(sca.score)" @click.stop="showDetails = true">
2 + <div class="h-full">
3 + <CardEntity
4 + clickable
5 + :embedded
6 + class="h-full"
7 + main-box-class="grow"
8 + card-entity-wrapper-class="h-full"
9 + header-box-class="flex-nowrap! items-start"
10 + :class="`${getComplianceBorderClass(sca.score)} transition-all duration-300`"
11 + @click.stop="showDetails = true"
12 + >
13 <template #headerMain>{{ sca.policy_name }}</template>
14 <template #headerExtra>
6 - <Badge :color="getComplianceLevelColor(getComplianceLevel(sca.score))">
7 - <template #iconLeft><Icon :name="getComplianceLevelIcon(getComplianceLevel(sca.score))" :size="14" /></template>
8 - <template #value>{{ getComplianceLevel(sca.score) }}</template>
9 - </Badge>
15 + <ScaLevelBadge :score="sca.score" />
16 </template>
17 <template #default>
12 - <div class="flex-1">
13 - <p class="text-base font-medium opacity-90 leading-relaxed line-clamp-3">{{ sca.description }}</p>
14 - <div class="mt-2 text-sm opacity-75">
18 + <div class="flex flex-col gap-4">
19 + <div class="font-medium leading-snug">
20 + {{ sca.description }}
21 + </div>
22 + <div class="text-secondary flex flex-col gap-0.5 text-sm">
23 <div class="flex items-center gap-2">
24 <Icon :name="HostIcon" :size="14" />
25 <span>{{ sca.agent_name }}</span>
26 </div>
19 - <div class="flex items-center gap-2 mt-1">
27 + <div class="mt-1 flex items-center gap-2">
28 <Icon :name="PolicyIcon" :size="14" />
29 <span>{{ sca.policy_id }}</span>
30 </div>
@@ -25,13 +33,9 @@
33 </template>
34 <template #footerMain>
35 <div class="flex flex-wrap items-center gap-2">
28 - <Badge v-if="sca.customer_code" class="text-xs">
29 - <template #value>{{ sca.customer_code }}</template>
30 - </Badge>
31 -
32 - <Badge color="primary" type="splitted" class="text-xs">
33 - <template #label>Score</template>
34 - <template #value>{{ sca.score }}%</template>
36 + <Badge type="splitted" class="text-xs">
37 + <template #label>Total</template>
38 + <template #value>{{ sca.total_checks }}</template>
39 </Badge>
40
41 <Badge color="success" type="splitted" class="text-xs">
@@ -49,15 +53,22 @@
53 <template #value>{{ sca.invalid }}</template>
54 </Badge>
55
52 - <Badge type="splitted" class="text-xs">
53 - <template #label>Total</template>
54 - <template #value>{{ sca.total_checks }}</template>
56 + <Badge v-if="sca.customer_code" class="text-xs">
57 + <template #value>
58 + <code
59 + class="text-primary cursor-pointer"
60 + @click.stop="gotoCustomer({ code: sca.customer_code })"
61 + >
62 + customer #{{ sca.customer_code }}
63 + <Icon :name="LinkIcon" :size="13" class="relative top-0.5" />
64 + </code>
65 + </template>
66 </Badge>
67 </div>
68 </template>
69 <template #footerExtra>
59 - <div class="text-xs opacity-60">
60 - {{ formatDate(sca.end_scan) }}
70 + <div class="text-tertiary text-xs">
71 + {{ formatDate(sca.end_scan, dFormats.datetime) }}
72 </div>
73 </template>
74 </CardEntity>
@@ -66,12 +77,13 @@
77 <n-modal
78 v-model:show="showDetails"
79 preset="card"
69 - :style="{ maxWidth: 'min(800px, 90vw)', minHeight: 'min(600px, 90vh)' }"
80 + :style="{ maxWidth: 'min(800px, 90vw)', minHeight: 'min(500px, 90vh)' }"
81 :title="`SCA Policy: ${sca.policy_name}`"
82 :bordered="false"
83 + content-class="!p-0"
84 segmented
85 >
74 - <ScaCardContent :sca="sca" />
86 + <ScaCardContent :sca />
87 </n-modal>
88 </div>
89 </template>
@@ -83,43 +95,32 @@ import { ref } from "vue"
95 import Badge from "@/components/common/Badge.vue"
96 import CardEntity from "@/components/common/cards/CardEntity.vue"
97 import Icon from "@/components/common/Icon.vue"
86 -import { getComplianceLevel, getComplianceLevelColor, getComplianceLevelIcon } from "@/types/sca.d"
98 +import { useGoto } from "@/composables/useGoto"
99 +import { useSettingsStore } from "@/stores/settings"
100 +import { formatDate } from "@/utils"
101 import ScaCardContent from "./ScaCardContent.vue"
102 +import ScaLevelBadge from "./ScaLevelBadge.vue"
103 +import { getComplianceLevel } from "./utils"
104
105 const { sca } = defineProps<{ sca: AgentScaOverviewItem; embedded?: boolean }>()
106
107 +const { gotoCustomer } = useGoto()
108 +const dFormats = useSettingsStore().dateFormat
109 +
110 const showDetails = ref(false)
111 +const LinkIcon = "carbon:launch"
112 const HostIcon = "carbon:bare-metal-server"
113 const PolicyIcon = "carbon:security"
114
115 function getComplianceBorderClass(score: number): string {
116 const level = getComplianceLevel(score)
117 const borderMap: Record<string, string> = {
98 - Excellent: "border-l-4 border-l-green-500 dark:border-l-green-400",
99 - Good: "border-l-4 border-l-blue-500 dark:border-l-blue-400",
100 - Average: "border-l-4 border-l-yellow-500 dark:border-l-yellow-400",
101 - Poor: "border-l-4 border-l-orange-500 dark:border-l-orange-400",
102 - Critical: "border-l-4 border-l-red-500 dark:border-l-red-400"
118 + Excellent: "ring-1 ring-success/30 hover:ring-success/80",
119 + Good: "ring-1 ring-info/30 hover:ring-info/80",
120 + Average: "ring-1 ring-warning/30 hover:ring-warning/80",
121 + Poor: "ring-1 ring-orange-500/30 hover:ring-orange-500/80",
122 + Critical: "ring-1 ring-error/30 hover:ring-error/80"
123 }
124 return borderMap[level] || ""
125 }
106 -
107 -function formatDate(dateString: string): string {
108 - return new Date(dateString).toLocaleDateString()
109 -}
126 </script>
111 -
112 -<style scoped>
113 -.sca-card {
114 - min-height: 280px;
115 -}
116 -
117 -.line-clamp-3 {
118 - display: -webkit-box;
119 - -webkit-line-clamp: 3;
120 - line-clamp: 3;
121 - -webkit-box-orient: vertical;
122 - overflow: hidden;
123 - text-overflow: ellipsis;
124 -}
125 -</style>
frontend/src/components/sca/ScaCardContent.vue
+185 -261
@@ -1,196 +1,198 @@
1 <template>
2 - <div class="sca-content">
3 - <!-- Overview Section -->
4 - <div class="mb-6">
5 - <h3 class="text-lg font-semibold mb-3 flex items-center gap-2">
6 - <Icon :name="OverviewIcon" :size="20" />
7 - Policy Overview
8 - </h3>
9 - <div class="grid grid-cols-1 md:grid-cols-2 gap-4">
10 - <div class="space-y-3">
11 - <div class="flex items-center justify-between py-2 px-3 bg-gray-50 dark:bg-gray-800 rounded">
12 - <span class="text-sm font-medium">Policy ID:</span>
13 - <code class="text-sm">{{ sca.policy_id }}</code>
14 - </div>
15 - <div class="flex items-center justify-between py-2 px-3 bg-gray-50 dark:bg-gray-800 rounded">
16 - <span class="text-sm font-medium">Agent:</span>
17 - <span class="text-sm font-mono">{{ sca.agent_name }}</span>
18 - </div>
19 - <div v-if="sca.customer_code" class="flex items-center justify-between py-2 px-3 bg-gray-50 dark:bg-gray-800 rounded">
20 - <span class="text-sm font-medium">Customer:</span>
21 - <Badge class="text-xs">
22 - <template #value>{{ sca.customer_code }}</template>
23 - </Badge>
24 - </div>
25 - </div>
26 - <div class="space-y-3">
27 - <div class="flex items-center justify-between py-2 px-3 bg-gray-50 dark:bg-gray-800 rounded">
28 - <span class="text-sm font-medium">Compliance Level:</span>
29 - <Badge :color="getComplianceLevelColor(getComplianceLevel(sca.score))">
30 - <template #iconLeft><Icon :name="getComplianceLevelIcon(getComplianceLevel(sca.score))" :size="14" /></template>
31 - <template #value>{{ getComplianceLevel(sca.score) }}</template>
32 - </Badge>
33 - </div>
34 - <div class="flex items-center justify-between py-2 px-3 bg-gray-50 dark:bg-gray-800 rounded">
35 - <span class="text-sm font-medium">Score:</span>
36 - <Badge color="primary" type="splitted">
37 - <template #label>Score</template>
38 - <template #value>{{ sca.score }}%</template>
39 - </Badge>
40 - </div>
41 - <div class="flex items-center justify-between py-2 px-3 bg-gray-50 dark:bg-gray-800 rounded">
42 - <span class="text-sm font-medium">Scan Date:</span>
43 - <span class="text-sm">{{ formatDateTime(sca.end_scan) }}</span>
44 - </div>
2 + <n-tabs type="line" animated :tabs-padding="24">
3 + <n-tab-pane name="Overview" tab="Overview" display-directive="show">
4 + <div class="flex flex-col gap-6 p-7 pt-2">
5 + <div class="grid-auto-fit-200 grid gap-2">
6 + <CardKV>
7 + <template #key>Policy ID</template>
8 + <template #value>
9 + {{ sca.policy_id }}
10 + </template>
11 + </CardKV>
12 +
13 + <CardKV>
14 + <template #key>Agent</template>
15 + <template #value>
16 + {{ sca.agent_name }}
17 + </template>
18 + </CardKV>
19 +
20 + <CardKV v-if="sca.customer_code">
21 + <template #key>Customer</template>
22 + <template #value>
23 + <code
24 + class="text-primary cursor-pointer"
25 + @click.stop="gotoCustomer({ code: sca.customer_code })"
26 + >
27 + #{{ sca.customer_code }}
28 + <Icon :name="LinkIcon" :size="13" class="relative top-0.5" />
29 + </code>
30 + </template>
31 + </CardKV>
32 +
33 + <CardKV>
34 + <template #key>Compliance Level</template>
35 + <template #value>
36 + {{ getComplianceLevel(sca.score) }}
37 + </template>
38 + </CardKV>
39 +
40 + <CardKV>
41 + <template #key>Score</template>
42 + <template #value>{{ sca.score }}%</template>
43 + </CardKV>
44 +
45 + <CardKV>
46 + <template #key>Scan Date</template>
47 + <template #value>
48 + {{ formatDate(sca.end_scan, dFormats.datetime) }}
49 + </template>
50 + </CardKV>
51 </div>
46 - </div>
47 - </div>
52
49 - <!-- Description Section -->
50 - <div class="mb-6">
51 - <h3 class="text-lg font-semibold mb-3 flex items-center gap-2">
52 - <Icon :name="DescriptionIcon" :size="20" />
53 - Description
54 - </h3>
55 - <div class="p-4 bg-gray-50 dark:bg-gray-800 rounded-lg">
56 - <p class="text-sm leading-relaxed">{{ sca.description }}</p>
53 + <CardKV>
54 + <template #key>Description</template>
55 + <template #value>
56 + {{ sca.description }}
57 + </template>
58 + </CardKV>
59 +
60 + <CardKV v-if="sca.references">
61 + <template #key>References</template>
62 + <template #value>
63 + <a :href="sca.references" target="_blank" tabindex="-1" class="outline-none">
64 + {{ sca.references }}
65 + </a>
66 + </template>
67 + </CardKV>
68 </div>
58 - </div>
59 -
60 - <!-- Compliance Statistics -->
61 - <div class="mb-6">
62 - <h3 class="text-lg font-semibold mb-3 flex items-center gap-2">
63 - <Icon :name="StatsIcon" :size="20" />
64 - Compliance Statistics
65 - </h3>
66 - <div class="grid grid-cols-2 md:grid-cols-4 gap-4">
67 - <div class="stat-card-small pass">
68 - <div class="stat-header-small">
69 - <Icon :name="PassIcon" :size="16" class="text-green-600" />
70 - <span class="stat-title-small">Passed</span>
71 - </div>
72 - <div class="stat-value-small">{{ sca.pass }}</div>
73 - <div class="stat-percentage-small">{{ getCheckPercentage(sca.pass) }}%</div>
74 - </div>
75 -
76 - <div class="stat-card-small fail">
77 - <div class="stat-header-small">
78 - <Icon :name="FailIcon" :size="16" class="text-red-600" />
79 - <span class="stat-title-small">Failed</span>
80 - </div>
81 - <div class="stat-value-small">{{ sca.fail }}</div>
82 - <div class="stat-percentage-small">{{ getCheckPercentage(sca.fail) }}%</div>
83 - </div>
84 -
85 - <div v-if="sca.invalid > 0" class="stat-card-small invalid">
86 - <div class="stat-header-small">
87 - <Icon :name="InvalidIcon" :size="16" class="text-yellow-600" />
88 - <span class="stat-title-small">Invalid</span>
89 - </div>
90 - <div class="stat-value-small">{{ sca.invalid }}</div>
91 - <div class="stat-percentage-small">{{ getCheckPercentage(sca.invalid) }}%</div>
69 + </n-tab-pane>
70 + <n-tab-pane name="Statistics & Progress" tab="Statistics & Progress" display-directive="show">
71 + <div class="flex flex-col gap-4 p-7 pt-2">
72 + <div class="flex items-center gap-2 text-sm">
73 + <Icon :name="InfoIcon" :size="14" />
74 + Total:
75 + <code>{{ sca.total_checks }}</code>
76 </div>
77
94 - <div class="stat-card-small total">
95 - <div class="stat-header-small">
96 - <Icon :name="TotalIcon" :size="16" class="text-blue-600" />
97 - <span class="stat-title-small">Total</span>
98 - </div>
99 - <div class="stat-value-small">{{ sca.total_checks }}</div>
100 - <div class="stat-percentage-small">100%</div>
101 - </div>
102 - </div>
103 - </div>
104 -
105 - <!-- Progress Bar -->
106 - <div class="mb-6">
107 - <h3 class="text-lg font-semibold mb-3 flex items-center gap-2">
108 - <Icon :name="ProgressIcon" :size="20" />
109 - Compliance Progress
110 - </h3>
111 - <div class="w-full bg-gray-200 dark:bg-gray-700 rounded-full h-6 relative overflow-hidden">
112 - <div
113 - class="bg-green-500 h-6 rounded-full transition-all duration-500 flex items-center justify-end pr-2"
114 - :style="{ width: `${sca.score}%` }"
115 - >
116 - <span v-if="sca.score > 15" class="text-white text-xs font-semibold">{{ sca.score }}%</span>
78 + <div class="grid-auto-fit-200 grid gap-2">
79 + <n-card content-class="flex flex-col gap-2" size="small">
80 + <div class="flex items-center justify-between gap-2 whitespace-nowrap">
81 + <div class="flex items-center gap-2">
82 + <Icon :name="PassIcon" :size="20" class="text-success" />
83 + <span class="text-lg">Passed</span>
84 + </div>
85 + <div class="font-mono text-lg font-bold">{{ sca.pass }}</div>
86 + </div>
87 + <n-progress
88 + type="line"
89 + indicator-placement="inside"
90 + :percentage="getPercentage(sca.pass)"
91 + color="var(--success-color)"
92 + class="custom-progress"
93 + />
94 + </n-card>
95 +
96 + <n-card content-class="flex flex-col gap-2" size="small">
97 + <div class="flex items-center justify-between gap-2 whitespace-nowrap">
98 + <div class="flex items-center gap-2">
99 + <Icon :name="FailIcon" :size="20" class="text-error" />
100 + <span class="text-lg">Failed</span>
101 + </div>
102 + <div class="font-mono text-lg font-bold">{{ sca.fail }}</div>
103 + </div>
104 + <n-progress
105 + type="line"
106 + indicator-placement="inside"
107 + :percentage="getPercentage(sca.fail)"
108 + color="var(--error-color)"
109 + class="custom-progress"
110 + />
111 + </n-card>
112 +
113 + <n-card v-if="sca.invalid > 0" content-class="flex flex-col gap-2" size="small">
114 + <div class="flex items-center justify-between gap-2 whitespace-nowrap">
115 + <div class="flex items-center gap-2">
116 + <Icon :name="InvalidIcon" :size="20" class="text-warning" />
117 + <span class="text-lg">Invalid</span>
118 + </div>
119 + <div class="font-mono text-lg font-bold">{{ sca.invalid }}</div>
120 + </div>
121 + <n-progress
122 + type="line"
123 + indicator-placement="inside"
124 + :percentage="getPercentage(sca.invalid)"
125 + color="var(--warning-color)"
126 + class="custom-progress"
127 + />
128 + </n-card>
129 </div>
118 - <span v-if="sca.score <= 15" class="absolute inset-0 flex items-center justify-center text-xs font-semibold text-gray-700 dark:text-gray-300">
119 - {{ sca.score }}%
120 - </span>
121 - </div>
122 - <div class="flex justify-between text-xs text-gray-500 mt-1">
123 - <span>0%</span>
124 - <span>50%</span>
125 - <span>100%</span>
126 - </div>
127 - </div>
130
129 - <!-- Scan Information -->
130 - <div class="mb-6">
131 - <h3 class="text-lg font-semibold mb-3 flex items-center gap-2">
132 - <Icon :name="ScanIcon" :size="20" />
133 - Scan Information
134 - </h3>
135 - <div class="grid grid-cols-1 md:grid-cols-2 gap-4">
136 - <div class="flex items-center justify-between py-2 px-3 bg-gray-50 dark:bg-gray-800 rounded">
137 - <span class="text-sm font-medium">Scan Started:</span>
138 - <span class="text-sm">{{ formatDateTime(sca.start_scan) }}</span>
139 - </div>
140 - <div class="flex items-center justify-between py-2 px-3 bg-gray-50 dark:bg-gray-800 rounded">
141 - <span class="text-sm font-medium">Scan Completed:</span>
142 - <span class="text-sm">{{ formatDateTime(sca.end_scan) }}</span>
143 - </div>
144 - <div v-if="sca.hash_file" class="flex items-center justify-between py-2 px-3 bg-gray-50 dark:bg-gray-800 rounded">
145 - <span class="text-sm font-medium">Hash File:</span>
146 - <code class="text-xs">{{ sca.hash_file }}</code>
147 - </div>
148 - <div class="flex items-center justify-between py-2 px-3 bg-gray-50 dark:bg-gray-800 rounded">
149 - <span class="text-sm font-medium">Scan Duration:</span>
150 - <span class="text-sm">{{ getScanDuration() }}</span>
151 - </div>
131 + <n-card class="bg-secondary! overflow-hidden" title="Progress">
132 + <n-progress
133 + type="line"
134 + indicator-placement="inside"
135 + :percentage="sca.score"
136 + color="var(--success-color)"
137 + class="custom-progress"
138 + />
139 + </n-card>
140 </div>
153 - </div>
154 -
155 - <!-- References -->
156 - <div v-if="sca.references" class="mb-6">
157 - <h3 class="text-lg font-semibold mb-3 flex items-center gap-2">
158 - <Icon :name="ReferenceIcon" :size="20" />
159 - References
160 - </h3>
161 - <div class="p-4 bg-gray-50 dark:bg-gray-800 rounded-lg">
162 - <p class="text-sm leading-relaxed break-all">{{ sca.references }}</p>
141 + </n-tab-pane>
142 + <n-tab-pane name="Scan Information" tab="Scan Information" display-directive="show">
143 + <div class="flex flex-col gap-4 p-7 pt-2">
144 + <n-card class="bg-secondary! overflow-hidden">
145 + <div class="flex flex-wrap justify-between gap-8">
146 + <n-statistic
147 + label="Scan Started"
148 + :value="`${formatDate(sca.start_scan, dFormats.datetime)}`"
149 + tabular-nums
150 + />
151 + <n-statistic
152 + label="Scan Completed"
153 + :value="`${formatDate(sca.end_scan, dFormats.datetime)}`"
154 + tabular-nums
155 + />
156 + <n-statistic label="Scan Duration" :value="`${getScanDuration()}`" tabular-nums />
157 + </div>
158 + </n-card>
159 +
160 + <CardKV v-if="sca.hash_file">
161 + <template #key>Hash File</template>
162 + <template #value>
163 + {{ sca.hash_file }}
164 + </template>
165 + </CardKV>
166 </div>
164 - </div>
165 - </div>
167 + </n-tab-pane>
168 + </n-tabs>
169 </template>
170
171 <script setup lang="ts">
172 import type { AgentScaOverviewItem } from "@/types/sca.d"
170 -import Badge from "@/components/common/Badge.vue"
173 +import _toNumber from "lodash/toNumber"
174 +import { NCard, NProgress, NStatistic, NTabPane, NTabs } from "naive-ui"
175 +import CardKV from "@/components/common/cards/CardKV.vue"
176 import Icon from "@/components/common/Icon.vue"
172 -import { getComplianceLevel, getComplianceLevelColor, getComplianceLevelIcon } from "@/types/sca.d"
177 +import { useGoto } from "@/composables/useGoto"
178 +import { useSettingsStore } from "@/stores/settings"
179 +import { formatDate } from "@/utils"
180 +import { getComplianceLevel } from "./utils"
181
182 const { sca } = defineProps<{ sca: AgentScaOverviewItem }>()
183
176 -const OverviewIcon = "carbon:overview"
177 -const DescriptionIcon = "carbon:document"
178 -const StatsIcon = "carbon:analytics"
179 -const ProgressIcon = "carbon:progress-bar"
180 -const ScanIcon = "carbon:scan"
181 -const ReferenceIcon = "carbon:link"
184 +const dFormats = useSettingsStore().dateFormat
185 +const { gotoCustomer } = useGoto()
186 +
187 +const InfoIcon = "carbon:information"
188 +const LinkIcon = "carbon:launch"
189 const PassIcon = "carbon:checkmark-filled"
190 const FailIcon = "carbon:close-filled"
191 const InvalidIcon = "carbon:warning-alt"
185 -const TotalIcon = "carbon:result"
186 -
187 -function getCheckPercentage(count: number): string {
188 - if (sca.total_checks === 0) return "0"
189 - return ((count / sca.total_checks) * 100).toFixed(1)
190 -}
192
192 -function formatDateTime(dateString: string): string {
193 - return new Date(dateString).toLocaleString()
193 +function getPercentage(count: number): number {
194 + if (sca.total_checks === 0) return 0
195 + return _toNumber(((count / sca.total_checks) * 100).toFixed(1))
196 }
197
198 function getScanDuration(): string {
@@ -207,95 +209,17 @@ function getScanDuration(): string {
209 }
210 </script>
211
210 -<style scoped>
211 -.stat-card-small {
212 - background-color: white;
213 - border-radius: 0.5rem;
214 - padding: 0.75rem;
215 - border: 1px solid rgb(229 231 235);
216 - box-shadow: 0 1px 2px 0 rgb(0 0 0 / 0.05);
217 -}
218 -
219 -.stat-card-small.pass {
220 - border-color: rgb(34 197 94);
221 - background-color: rgb(240 253 244);
222 -}
223 -
224 -.stat-card-small.fail {
225 - border-color: rgb(239 68 68);
226 - background-color: rgb(254 242 242);
227 -}
228 -
229 -.stat-card-small.invalid {
230 - border-color: rgb(234 179 8);
231 - background-color: rgb(254 252 232);
232 -}
233 -
234 -.stat-card-small.total {
235 - border-color: rgb(59 130 246);
236 - background-color: rgb(239 246 255);
237 -}
238 -
239 -.stat-header-small {
240 - display: flex;
241 - align-items: center;
242 - gap: 0.5rem;
243 - margin-bottom: 0.5rem;
244 -}
245 -
246 -.stat-title-small {
247 - font-size: 0.75rem;
248 - font-weight: 500;
249 - color: rgb(75 85 99);
250 -}
251 -
252 -.stat-value-small {
253 - font-size: 1.25rem;
254 - font-weight: 700;
255 - color: rgb(17 24 39);
256 -}
257 -
258 -.stat-percentage-small {
259 - font-size: 0.7rem;
260 - color: rgb(107 114 128);
261 - margin-top: 0.25rem;
262 -}
263 -
264 -/* Dark mode styles */
265 -html.dark .stat-card-small {
266 - background-color: rgb(31 41 55);
267 - border-color: rgb(75 85 99);
268 -}
269 -
270 -html.dark .stat-card-small.pass {
271 - border-color: rgb(34 197 94);
272 - background-color: rgb(20 83 45);
273 -}
274 -
275 -html.dark .stat-card-small.fail {
276 - border-color: rgb(239 68 68);
277 - background-color: rgb(127 29 29);
278 -}
279 -
280 -html.dark .stat-card-small.invalid {
281 - border-color: rgb(234 179 8);
282 - background-color: rgb(161 98 7);
283 -}
284 -
285 -html.dark .stat-card-small.total {
286 - border-color: rgb(59 130 246);
287 - background-color: rgb(30 64 175);
288 -}
289 -
290 -html.dark .stat-title-small {
291 - color: rgb(209 213 219);
292 -}
293 -
294 -html.dark .stat-value-small {
295 - color: rgb(255 255 255);
296 -}
297 -
298 -html.dark .stat-percentage-small {
299 - color: rgb(209 213 219);
212 +<style lang="scss" scoped>
213 +.custom-progress {
214 + :deep() {
215 + .n-progress-graph-line-indicator {
216 + text-shadow:
217 + 0px 0px 1px black,
218 + 0px 0px 2px black,
219 + 0px 0px 3px black;
220 + font-weight: bold;
221 + color: white !important;
222 + }
223 + }
224 }
225 </style>
frontend/src/components/sca/ScaLevelBadge.vue new
+36
@@ -0,0 +1,36 @@
1 +<template>
2 + <Badge :color="getComplianceLevelColor(getComplianceLevel(score))" type="splitted" bright class="whitespace-nowrap">
3 + <template #iconLeft>
4 + <ScaLevelIcon :level="getComplianceLevel(score)" :size="14" />
5 + </template>
6 + <template #label>Score</template>
7 + <template #value>{{ score }}%</template>
8 + </Badge>
9 +</template>
10 +
11 +<script setup lang="ts">
12 +import type { BadgeColor } from "@/components/common/Badge.vue"
13 +import Badge from "@/components/common/Badge.vue"
14 +import { ScaComplianceLevel } from "@/types/sca.d"
15 +import ScaLevelIcon from "./ScaLevelIcon.vue"
16 +import { getComplianceLevel } from "./utils"
17 +
18 +const { score } = defineProps<{ score: number }>()
19 +
20 +function getComplianceLevelColor(level: ScaComplianceLevel): BadgeColor {
21 + switch (level) {
22 + case ScaComplianceLevel.Excellent:
23 + return "success"
24 + case ScaComplianceLevel.Good:
25 + return "primary"
26 + case ScaComplianceLevel.Average:
27 + return "warning"
28 + case ScaComplianceLevel.Poor:
29 + return "warning"
30 + case ScaComplianceLevel.Critical:
31 + return "danger"
32 + default:
33 + return "primary"
34 + }
35 +}
36 +</script>
frontend/src/components/sca/ScaLevelIcon.vue new
+21
@@ -0,0 +1,21 @@
1 +<template>
2 + <Icon :name="getLevelIcon(level)" :size />
3 +</template>
4 +
5 +<script setup lang="ts">
6 +import Icon from "@/components/common/Icon.vue"
7 +import { ScaComplianceLevel } from "@/types/sca.d"
8 +
9 +const { level, size } = defineProps<{ level: ScaComplianceLevel; size?: number }>()
10 +
11 +function getLevelIcon(level: string): string {
12 + const iconMap: Record<string, string> = {
13 + [ScaComplianceLevel.Excellent]: "carbon:checkmark-filled",
14 + [ScaComplianceLevel.Good]: "carbon:checkmark",
15 + [ScaComplianceLevel.Average]: "carbon:warning",
16 + [ScaComplianceLevel.Poor]: "carbon:warning-alt",
17 + [ScaComplianceLevel.Critical]: "carbon:warning-hex-filled"
18 + }
19 + return iconMap[level] || "carbon:help"
20 +}
21 +</script>
frontend/src/components/sca/ScaStats.vue new
+420
@@ -0,0 +1,420 @@
1 +<template>
2 + <n-spin :show="loading" content-class="flex flex-col gap-4">
3 + <!-- Statistics Cards -->
4 + <div>
5 + <div class="mb-5 flex flex-wrap items-center justify-between gap-3">
6 + <h3 class="flex items-center gap-2 text-lg font-semibold text-gray-900 dark:text-gray-100">
7 + <Icon name="carbon:chart-ring" :size="20" class="text-primary" />
8 + Distribution
9 + </h3>
10 +
11 + <p class="flex items-center gap-2 text-sm">
12 + Total:
13 + <code>{{ totalCount.toLocaleString() }}</code>
14 + <Icon :name="InfoIcon" :size="14" />
15 + </p>
16 + </div>
17 + <div class="grid-auto-fit-250 grid gap-4">
18 + <n-card
19 + v-for="item of statisticsCards"
20 + :key="item.level"
21 + class="ring-primary cursor-pointer transition-all duration-300 hover:ring-1"
22 + content-class="flex flex-col gap-2"
23 + size="small"
24 + @click="selectComplianceLevel(item.level)"
25 + >
26 + <div class="flex items-center justify-between gap-2 whitespace-nowrap">
27 + <div class="flex items-center gap-2">
28 + <ScaLevelIcon :level="item.level" :size="24" :class="item.iconClass" />
29 + <span class="text-xl">{{ item.label }}</span>
30 + </div>
31 + <div class="font-mono text-xl font-bold">{{ stats[item.key].toLocaleString() }}</div>
32 + </div>
33 + <n-progress
34 + type="line"
35 + indicator-placement="inside"
36 + :percentage="getPercentage(stats[item.key])"
37 + :color="item.barColor"
38 + class="custom-progress"
39 + />
40 + </n-card>
41 + </div>
42 + </div>
43 +
44 + <!-- Coverage Cards -->
45 + <div class="mt-8">
46 + <div class="mb-5 flex flex-wrap items-center justify-between gap-3">
47 + <h3 class="flex items-center gap-2 text-lg font-semibold text-gray-900 dark:text-gray-100">
48 + <Icon name="carbon:double-axis-chart-column" :size="20" class="text-primary" />
49 + Coverage
50 + </h3>
51 +
52 + <p class="flex items-center gap-2 text-sm">
53 + Coverage based on the 100 policies with the lowest score.
54 + <Icon :name="InfoIcon" :size="14" />
55 + </p>
56 + </div>
57 +
58 + <n-card class="bg-secondary! overflow-hidden">
59 + <div class="flex flex-wrap justify-between gap-8">
60 + <n-statistic
61 + label="Agents"
62 + :value="overviewData?.total_agents?.toLocaleString() || 0"
63 + tabular-nums
64 + />
65 + <n-statistic
66 + label="Policies"
67 + :value="overviewData?.total_policies?.toLocaleString() || 0"
68 + tabular-nums
69 + />
70 + <n-statistic label="Avg Score" :value="overviewData?.average_score?.toFixed(1) || 0" tabular-nums />
71 + </div>
72 + </n-card>
73 + </div>
74 +
75 + <!-- Top 5 Policies by Compliance Score -->
76 + <div v-if="topPoliciesByScore.length > 0" class="mt-8">
77 + <div class="mb-5 flex flex-wrap items-center justify-between gap-3">
78 + <h3 class="flex items-center gap-2 text-lg font-semibold">
79 + <Icon :name="TopPoliciesIcon" :size="20" class="text-primary" />
80 + Top 5 Policies by Compliance Score
81 + </h3>
82 +
83 + <p class="flex items-center gap-2 text-sm">
84 + Ranking based on the 100 policies with the lowest score.
85 + <Icon :name="InfoIcon" :size="14" />
86 + </p>
87 + </div>
88 + <div class="grid-auto-fit-250 grid gap-10">
89 + <div
90 + v-for="(policy, index) in topPoliciesByScore"
91 + :key="policy.policy_id"
92 + class="@container flex items-stretch gap-2"
93 + >
94 + <div class="flex items-end">
95 + <div
96 + class="w-4 rounded-b-sm rounded-t-2xl"
97 + :class="{
98 + 'h-full bg-yellow-500': index === 0,
99 + 'h-8/12 bg-gray-400': index === 1,
100 + 'h-4/12 bg-amber-600': index === 2
101 + }"
102 + ></div>
103 + </div>
104 +
105 + <div
106 + class="bg-secondary ring-primary flex grow cursor-pointer flex-col gap-2 rounded-md px-3 py-2 transition-all duration-300 hover:ring-1"
107 + @click="selectPolicyID(policy.policy_id)"
108 + >
109 + <div class="flex items-center justify-between gap-4">
110 + <div class="flex items-center gap-2 text-xl">
111 + <Icon
112 + :name="index < 3 ? 'carbon:trophy' : 'carbon:warning-alt'"
113 + :size="20"
114 + :class="
115 + index === 0
116 + ? 'text-yellow-500'
117 + : index === 1
118 + ? 'text-gray-400'
119 + : index === 2
120 + ? 'text-amber-600'
121 + : 'text-info'
122 + "
123 + />
124 + <span>#{{ index + 1 }}</span>
125 + </div>
126 + <ScaLevelBadge :score="policy.score" class="mt-1" />
127 + </div>
128 +
129 + <div class="text-lg font-semibold leading-snug">{{ policy.policy_name }}</div>
130 +
131 + <div class="@md:grid-cols-2 text-secondary grid grid-cols-1 gap-1 break-all text-xs">
132 + <div class="flex items-center gap-2">
133 + <span>Policy ID:</span>
134 + <span class="font-mono font-semibold">{{ policy.policy_id }}</span>
135 + </div>
136 + <div class="flex items-center gap-2">
137 + <span>Agents:</span>
138 + <span class="font-mono font-semibold">{{ policy.agentCount.toLocaleString() }}</span>
139 + </div>
140 + <div class="flex items-center gap-2">
141 + <span>Total Checks:</span>
142 + <span class="font-mono font-semibold">
143 + {{ policy.total_checks.toLocaleString() }}
144 + </span>
145 + </div>
146 + <div class="flex items-center gap-2">
147 + <span class="text-success">Pass:</span>
148 + <span class="font-mono font-semibold">
149 + {{ policy.pass }}
150 + </span>
151 + </div>
152 + <div class="flex items-center gap-2">
153 + <span class="text-error">Fail:</span>
154 + <span class="font-mono font-semibold">{{ policy.fail }}</span>
155 + </div>
156 + </div>
157 + </div>
158 + </div>
159 + </div>
160 + </div>
161 + </n-spin>
162 +</template>
163 +
164 +<script setup lang="ts">
165 +import type { ScaOverviewFilter, ScaOverviewFilterTypes } from "./types"
166 +import type { AgentScaOverviewItem, ScaOverviewQuery, ScaOverviewResponse } from "@/types/sca.d"
167 +import { watchDebounced } from "@vueuse/core"
168 +import axios from "axios"
169 +import _set from "lodash/set"
170 +import _toNumber from "lodash/toNumber"
171 +import { NCard, NProgress, NSpin, NStatistic, useMessage } from "naive-ui"
172 +import { computed, ref, toRefs } from "vue"
173 +import Api from "@/api"
174 +import Icon from "@/components/common/Icon.vue"
175 +import { ScaComplianceLevel } from "@/types/sca.d"
176 +import ScaLevelBadge from "./ScaLevelBadge.vue"
177 +import ScaLevelIcon from "./ScaLevelIcon.vue"
178 +import { getComplianceLevel } from "./utils"
179 +
180 +const props = defineProps<{ filters: ScaOverviewFilter[] }>()
181 +
182 +const emit = defineEmits<{
183 + (e: "update:min_score", value: number): void
184 + (e: "update:max_score", value: number): void
185 + (e: "update:policy_id", value: string): void
186 +}>()
187 +
188 +const { filters } = toRefs(props)
189 +
190 +const loading = ref(false)
191 +const message = useMessage()
192 +const list = ref<AgentScaOverviewItem[]>([])
193 +
194 +const totalCount = ref(0)
195 +
196 +// Overview data from API response
197 +const overviewData = ref<ScaOverviewResponse | null>(null)
198 +
199 +const InfoIcon = "carbon:information"
200 +const TopPoliciesIcon = "carbon:trophy"
201 +
202 +// Define the allowed keys for statistics
203 +type LevelKey = "excellent" | "good" | "average" | "poor" | "critical"
204 +
205 +const statisticsCards: {
206 + label: string
207 + level: ScaComplianceLevel
208 + iconClass: string
209 + barColor: string
210 + key: LevelKey
211 +}[] = [
212 + {
213 + label: "Excellent",
214 + level: ScaComplianceLevel.Excellent,
215 + iconClass: "text-success",
216 + barColor: "var(--success-color)",
217 + key: "excellent"
218 + },
219 + {
220 + label: "Good",
221 + level: ScaComplianceLevel.Good,
222 + iconClass: "text-info",
223 + barColor: "var(--info-color)",
224 + key: "good"
225 + },
226 + {
227 + label: "Average",
228 + level: ScaComplianceLevel.Average,
229 + iconClass: "text-warning",
230 + barColor: "var(--warning-color)",
231 + key: "average"
232 + },
233 + {
234 + label: "Poor",
235 + level: ScaComplianceLevel.Poor,
236 + iconClass: "text-orange-500",
237 + barColor: "var(--color-orange-500)",
238 + key: "poor"
239 + },
240 + {
241 + label: "Critical",
242 + level: ScaComplianceLevel.Critical,
243 + iconClass: "text-error",
244 + barColor: "var(--error-color)",
245 + key: "critical"
246 + }
247 +]
248 +
249 +// Calculate statistics from current data
250 +const stats = computed((): Record<LevelKey, number> => {
251 + // Calculate compliance level distribution from current data
252 + const excellent = list.value.filter(item => getComplianceLevel(item.score) === ScaComplianceLevel.Excellent).length
253 + const good = list.value.filter(item => getComplianceLevel(item.score) === ScaComplianceLevel.Good).length
254 + const average = list.value.filter(item => getComplianceLevel(item.score) === ScaComplianceLevel.Average).length
255 + const poor = list.value.filter(item => getComplianceLevel(item.score) === ScaComplianceLevel.Poor).length
256 + const critical = list.value.filter(item => getComplianceLevel(item.score) === ScaComplianceLevel.Critical).length
257 +
258 + return {
259 + excellent,
260 + good,
261 + average,
262 + poor,
263 + critical
264 + }
265 +})
266 +
267 +// Calculate top policies by compliance score
268 +const topPoliciesByScore = computed(() => {
269 + // Group policies and calculate averages
270 + const policyMap = new Map<
271 + string,
272 + {
273 + policy_id: string
274 + policy_name: string
275 + score: number
276 + agentCount: number
277 + total_checks: number
278 + pass: number
279 + fail: number
280 + }
281 + >()
282 +
283 + list.value.forEach(item => {
284 + const key = item.policy_id
285 + const existing = policyMap.get(key)
286 +
287 + if (existing) {
288 + // Update averages and counts
289 + existing.agentCount++
290 + existing.score = Math.max(existing.score, item.score) // Use highest score for ranking
291 + existing.total_checks += item.total_checks
292 + existing.pass += item.pass
293 + existing.fail += item.fail
294 + } else {
295 + policyMap.set(key, {
296 + policy_id: item.policy_id,
297 + policy_name: item.policy_name,
298 + score: item.score,
299 + agentCount: 1,
300 + total_checks: item.total_checks,
301 + pass: item.pass,
302 + fail: item.fail
303 + })
304 + }
305 + })
306 +
307 + // Convert to array and sort by score
308 + return Array.from(policyMap.values())
309 + .sort((a, b) => b.score - a.score)
310 + .slice(0, 5)
311 +})
312 +
313 +function getPercentage(count: number): number {
314 + if (totalCount.value === 0) return 0
315 + return _toNumber(((count / totalCount.value) * 100).toFixed(1))
316 +}
317 +
318 +let abortController: AbortController | null = null
319 +
320 +function getList() {
321 + abortController?.abort()
322 + abortController = new AbortController()
323 +
324 + loading.value = true
325 +
326 + const query: ScaOverviewQuery = {
327 + page: 1,
328 + page_size: 100
329 + }
330 +
331 + for (const key of ["customer_code", "policy_id", "policy_name", "agent_name"] as ScaOverviewFilterTypes[]) {
332 + if (filters.value.find(o => o.type === key)?.value) {
333 + _set(query, key, `${filters.value.find(o => o.type === key)?.value}`)
334 + }
335 + }
336 + for (const key of ["min_score", "max_score"] as ScaOverviewFilterTypes[]) {
337 + if (filters.value.find(o => o.type === key)?.value) {
338 + _set(query, key, _toNumber(filters.value.find(o => o.type === key)?.value))
339 + }
340 + }
341 +
342 + Api.sca
343 + .searchScaOverview(query, abortController.signal)
344 + .then(res => {
345 + loading.value = false
346 +
347 + if (res.data.success) {
348 + list.value = res.data?.sca_results || []
349 + totalCount.value = res.data?.total_count || 0
350 + overviewData.value = res.data
351 + } else {
352 + message.warning(res.data?.message || "An error occurred. Please try again later.")
353 + }
354 + })
355 + .catch(err => {
356 + if (!axios.isCancel(err)) {
357 + message.error(err.response?.data?.message || "An error occurred. Please try again later.")
358 + loading.value = false
359 + }
360 + })
361 +}
362 +
363 +function selectPolicyID(value: string) {
364 + emit("update:policy_id", value)
365 +}
366 +
367 +function selectComplianceLevel(level: ScaComplianceLevel) {
368 + let min_score = 0
369 + let max_score = 0
370 +
371 + switch (level) {
372 + case ScaComplianceLevel.Excellent:
373 + min_score = 90
374 + max_score = 100
375 + break
376 + case ScaComplianceLevel.Good:
377 + min_score = 80
378 + max_score = 89
379 + break
380 + case ScaComplianceLevel.Average:
381 + min_score = 70
382 + max_score = 79
383 + break
384 + case ScaComplianceLevel.Poor:
385 + min_score = 60
386 + max_score = 69
387 + break
388 + case ScaComplianceLevel.Critical:
389 + min_score = 0
390 + max_score = 59
391 + break
392 + }
393 +
394 + emit("update:min_score", min_score)
395 + emit("update:max_score", max_score)
396 +}
397 +
398 +watchDebounced(
399 + filters,
400 + () => {
401 + getList()
402 + },
403 + { deep: true, debounce: 300, immediate: true }
404 +)
405 +</script>
406 +
407 +<style lang="scss" scoped>
408 +.custom-progress {
409 + :deep() {
410 + .n-progress-graph-line-indicator {
411 + text-shadow:
412 + 0px 0px 1px black,
413 + 0px 0px 2px black,
414 + 0px 0px 3px black;
415 + font-weight: bold;
416 + color: white !important;
417 + }
418 + }
419 +}
420 +</style>
frontend/src/components/sca/types.d.ts new
+8
@@ -0,0 +1,8 @@
1 +import type { ScaOverviewQuery } from "@/types/sca.d"
2 +
3 +export type ScaOverviewFilterTypes = keyof Omit<ScaOverviewQuery, "page" | "page_size">
4 +
5 +export interface ScaOverviewFilter {
6 + type: ScaOverviewFilterTypes
7 + value: string | number | null
8 +}
frontend/src/components/sca/utils.ts new
+9
@@ -0,0 +1,9 @@
1 +import { ScaComplianceLevel } from "@/types/sca.d"
2 +
3 +export function getComplianceLevel(score: number): ScaComplianceLevel {
4 + if (score >= 90) return ScaComplianceLevel.Excellent
5 + if (score >= 80) return ScaComplianceLevel.Good
6 + if (score >= 70) return ScaComplianceLevel.Average
7 + if (score >= 60) return ScaComplianceLevel.Poor
8 + return ScaComplianceLevel.Critical
9 +}
frontend/src/components/threatIntel/ThreatIntelProcessEvaluationBadge.vue
+1 -1
@@ -1,6 +1,6 @@
1 <template>
2 <ThreatIntelProcessEvaluationProvider v-slot="{ openEvaluation }" :process-name>
3 - <code class="text-primary cursor-pointer hover:text-primary-hover transition-colors" @click="openEvaluation()">
3 + <code class="text-primary hover:text-primary-hover cursor-pointer transition-colors" @click="openEvaluation()">
4 {{ processName }}
5 <Icon :name="LinkIcon" :size="13" class="relative top-0.5 ml-1" />
6 </code>
frontend/src/components/threatIntel/ThreatIntelProcessEvaluationProvider.vue
+27 -7
@@ -4,7 +4,7 @@
4 <n-modal
5 v-model:show="showDetails"
6 preset="card"
7 - content-class="!p-0"
7 + content-class="p-0!"
8 :style="{ maxWidth: 'min(800px, 90vw)', minHeight: 'min(550px, 90vh)' }"
9 :title="`Process Analysis: ${processName}`"
10 :bordered="false"
@@ -33,7 +33,13 @@
33 </div>
34 </n-tab-pane>
35 <!-- Legacy tabs for backward compatibility -->
36 - <n-tab-pane v-if="evaluation" name="Overview" tab="Overview" display-directive="show:lazy" class="flex flex-col gap-4 !py-8">
36 + <n-tab-pane
37 + v-if="evaluation"
38 + name="Overview"
39 + tab="Overview"
40 + display-directive="show:lazy"
41 + class="flex flex-col gap-4 !py-8"
42 + >
43 <div class="px-7">
44 <n-card embedded class="overflow-hidden">
45 <div class="flex flex-wrap justify-between gap-8">
@@ -71,7 +77,12 @@
77 percentage-key="percentage"
78 />
79 </n-tab-pane>
74 - <n-tab-pane v-if="evaluation?.network?.length" name="Network" tab="Network" display-directive="show:lazy">
80 + <n-tab-pane
81 + v-if="evaluation?.network?.length"
82 + name="Network"
83 + tab="Network"
84 + display-directive="show:lazy"
85 + >
86 <ListPercentage
87 class="p-7 pt-4"
88 :list="evaluation.network"
@@ -79,7 +90,12 @@
90 percentage-key="usage"
91 />
92 </n-tab-pane>
82 - <n-tab-pane v-if="evaluation?.parents?.length" name="Parents" tab="Parents" display-directive="show:lazy">
93 + <n-tab-pane
94 + v-if="evaluation?.parents?.length"
95 + name="Parents"
96 + tab="Parents"
97 + display-directive="show:lazy"
98 + >
99 <ListPercentage
100 class="p-7 pt-4"
101 :list="evaluation.parents"
@@ -96,7 +112,11 @@
112 />
113 </n-tab-pane>
114 </n-tabs>
99 - <n-empty v-if="!loading && !mcpResponse && !evaluation" description="Process analysis not found" class="h-48 justify-center" />
115 + <n-empty
116 + v-if="!loading && !mcpResponse && !evaluation"
117 + description="Process analysis not found"
118 + class="h-48 justify-center"
119 + />
120 </n-spin>
121 </n-modal>
122 </template>
@@ -138,11 +158,11 @@ function getEvaluation() {
158 .then(res => {
159 if (res.data.success) {
160 // Check if response is in new MCP format by checking for structured_result property
141 - if ('structured_result' in res.data && res.data.structured_result) {
161 + if ("structured_result" in res.data && res.data.structured_result) {
162 mcpResponse.value = res.data as MCPQueryResponse
163 } else {
164 // Fallback to legacy format
145 - const legacyResponse = res.data as any
165 + const legacyResponse = res.data as { data?: EvaluationData }
166 evaluation.value = legacyResponse?.data || null
167 }
168 } else {
frontend/src/components/vulnerabilities/List.vue
+105 -1017
@@ -1,435 +1,98 @@
1 <template>
2 <div class="flex flex-col gap-4">
3 - <!-- Info Banner -->
4 - <div class="info-banner p-3 rounded-lg border border-blue-200 bg-blue-50 dark:border-blue-800 dark:bg-blue-950/30">
5 - <div class="flex items-start gap-3">
6 - <Icon :name="InfoIcon" class="text-blue-600 dark:text-blue-400 mt-0.5" :size="16" />
7 - <p class="text-sm text-blue-800 dark:text-blue-200 leading-relaxed">
8 - Vulnerability Overview provides real-time vulnerability data from Wazuh Indexer with EPSS scoring and detailed package information.
9 - </p>
10 - </div>
11 - </div>
12 -
13 - <!-- Filters -->
14 - <div class="flex flex-col">
15 - <div ref="header" class="header flex items-center justify-end gap-2">
16 - <div class="info flex grow gap-2">
17 - <n-popover overlap placement="bottom-start">
18 - <template #trigger>
19 - <div class="bg-default rounded-lg">
20 - <n-button size="small" class="!cursor-help">
21 - <template #icon>
22 - <Icon :name="InfoIcon"></Icon>
23 - </template>
24 - </n-button>
25 - </div>
26 - </template>
27 - <div class="flex flex-col gap-3 p-2 max-w-sm">
28 - <div class="font-medium text-sm mb-2">Vulnerability Overview</div>
29 -
30 - <div class="grid grid-cols-2 gap-3 text-xs">
31 - <div class="flex justify-between">
32 - <span>Total Vulnerabilities:</span>
33 - <code class="font-mono">{{ totalCount.toLocaleString() }}</code>
34 - </div>
35 - <div class="flex justify-between">
36 - <span>Current Page:</span>
37 - <code class="font-mono">{{ currentPage }} / {{ totalPages }}</code>
38 - </div>
39 - </div>
40 -
41 - <div class="border-t pt-2">
42 - <div class="text-xs font-medium mb-2">Severity Distribution</div>
43 - <div class="grid grid-cols-2 gap-2 text-xs">
44 - <div class="flex justify-between">
45 - <span class="text-red-600">Critical:</span>
46 - <span class="font-mono">{{ stats.critical.toLocaleString() }} ({{ getPercentage(stats.critical) }}%)</span>
47 - </div>
48 - <div class="flex justify-between">
49 - <span class="text-orange-600">High:</span>
50 - <span class="font-mono">{{ stats.high.toLocaleString() }} ({{ getPercentage(stats.high) }}%)</span>
51 - </div>
52 - <div class="flex justify-between">
53 - <span class="text-yellow-600">Medium:</span>
54 - <span class="font-mono">{{ stats.medium.toLocaleString() }} ({{ getPercentage(stats.medium) }}%)</span>
55 - </div>
56 - <div class="flex justify-between">
57 - <span class="text-blue-600">Low:</span>
58 - <span class="font-mono">{{ stats.low.toLocaleString() }} ({{ getPercentage(stats.low) }}%)</span>
59 - </div>
60 - </div>
61 - </div>
62 -
63 - <div class="border-t pt-2">
64 - <div class="text-xs font-medium mb-2">Coverage</div>
65 - <div class="space-y-1 text-xs">
66 - <div class="flex justify-between">
67 - <span>Affected Agents:</span>
68 - <span class="font-mono">{{ stats.uniqueAgents.toLocaleString() }}</span>
69 - </div>
70 - <div class="flex justify-between">
71 - <span>Unique Packages:</span>
72 - <span class="font-mono">{{ stats.uniquePackages.toLocaleString() }}</span>
73 - </div>
74 - <div class="flex justify-between">
75 - <span>Customer Codes:</span>
76 - <span class="font-mono">{{ stats.uniqueCustomers.toLocaleString() }}</span>
77 - </div>
78 - </div>
79 - </div>
80 - </div>
81 - </n-popover> <n-select
82 - v-model:value="selectedCustomer"
83 - :options="customerOptions"
84 - clearable
85 - size="small"
86 - placeholder="Customer"
87 - class="max-w-32"
88 - :loading="loadingCustomers"
89 - />
90 -
91 - <n-select
92 - v-model:value="selectedSeverity"
93 - :options="severityOptions"
94 - clearable
95 - size="small"
96 - placeholder="Severity"
97 - class="max-w-32"
98 - />
99 -
100 - <n-input
101 - v-model:value="searchCVE"
102 - size="small"
103 - placeholder="Search CVE..."
104 - class="max-w-40"
105 - clearable
106 - >
107 - <template #prefix>
108 - <Icon :name="SearchIcon"></Icon>
109 - </template>
110 - </n-input>
111 -
112 - <n-select
113 - v-model:value="searchAgent"
114 - :options="agentOptions"
115 - size="small"
116 - placeholder="Search agent..."
117 - class="max-w-40"
118 - clearable
119 - filterable
120 - :loading="loadingAgents"
121 - />
122 -
123 - <n-input
124 - v-model:value="searchPackage"
125 - size="small"
126 - placeholder="Search package..."
127 - class="max-w-40"
128 - clearable
129 - >
130 - <template #prefix>
131 - <Icon :name="PackageIcon"></Icon>
132 - </template>
133 - </n-input>
134 - </div>
135 - </div>
136 - </div>
137 -
138 - <!-- Statistics Cards -->
139 - <div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 xl:grid-cols-5 gap-4 mb-4">
140 - <div class="stat-card">
141 - <div class="stat-header">
142 - <Icon :name="TotalIcon" :size="20" class="text-blue-600" />
143 - <span class="stat-title">Total</span>
144 - </div>
145 - <div class="stat-value">{{ totalCount.toLocaleString() }}</div>
146 - </div>
147 -
148 - <div class="stat-card critical clickable" :class="{ selected: selectedSeverity === VulnerabilitySeverity.Critical }" @click="selectSeverity(VulnerabilitySeverity.Critical)">
149 - <div class="stat-header">
150 - <Icon :name="CriticalIcon" :size="20" class="text-red-600" />
151 - <span class="stat-title">Critical</span>
152 - </div>
153 - <div class="stat-value">{{ stats.critical.toLocaleString() }}</div>
154 - <div class="stat-percentage">{{ getPercentage(stats.critical) }}%</div>
155 - </div>
156 -
157 - <div class="stat-card high clickable" :class="{ selected: selectedSeverity === VulnerabilitySeverity.High }" @click="selectSeverity(VulnerabilitySeverity.High)">
158 - <div class="stat-header">
159 - <Icon :name="HighIcon" :size="20" class="text-orange-600" />
160 - <span class="stat-title">High</span>
161 - </div>
162 - <div class="stat-value">{{ stats.high.toLocaleString() }}</div>
163 - <div class="stat-percentage">{{ getPercentage(stats.high) }}%</div>
164 - </div>
165 -
166 - <div class="stat-card medium clickable" :class="{ selected: selectedSeverity === VulnerabilitySeverity.Medium }" @click="selectSeverity(VulnerabilitySeverity.Medium)">
167 - <div class="stat-header">
168 - <Icon :name="MediumIcon" :size="20" class="text-yellow-600" />
169 - <span class="stat-title">Medium</span>
170 - </div>
171 - <div class="stat-value">{{ stats.medium.toLocaleString() }}</div>
172 - <div class="stat-percentage">{{ getPercentage(stats.medium) }}%</div>
173 - </div>
174 -
175 - <div class="stat-card low clickable" :class="{ selected: selectedSeverity === VulnerabilitySeverity.Low }" @click="selectSeverity(VulnerabilitySeverity.Low)">
176 - <div class="stat-header">
177 - <Icon :name="LowIcon" :size="20" class="text-blue-600" />
178 - <span class="stat-title">Low</span>
179 - </div>
180 - <div class="stat-value">{{ stats.low.toLocaleString() }}</div>
181 - <div class="stat-percentage">{{ getPercentage(stats.low) }}%</div>
182 - </div>
3 + <n-alert type="info">
4 + Vulnerability Overview provides real-time vulnerability data from Wazuh Indexer with EPSS scoring and
5 + detailed package information.
6 + </n-alert>
7 +
8 + <VulnerabilityStats :filters class="my-8" @update:severity="selectSeverity" @update:package="selectPackage" />
9 +
10 + <div ref="header" class="flex items-center justify-end gap-2">
11 + <n-pagination
12 + v-model:page="currentPage"
13 + v-model:page-size="pageSize"
14 + :page-slot
15 + :page-sizes
16 + :item-count="totalCount"
17 + :show-size-picker
18 + />
19 + <n-badge :show="filtered" dot type="success" :offset="[-4, 0]">
20 + <n-button size="small" secondary @click="showFiltersView = !showFiltersView">
21 + <template #icon>
22 + <Icon :name="FilterIcon"></Icon>
23 + </template>
24 + </n-button>
25 + </n-badge>
26 </div>
27
185 - <!-- Top 5 Packages by EPSS Score -->
186 - <div v-if="topEpssPackages.length > 0" class="mb-4">
187 - <h3 class="text-lg font-semibold mb-3 text-gray-900 dark:text-gray-100 flex items-center gap-2">
188 - <Icon :name="PackageIcon" :size="20" class="text-orange-600" />
189 - Top 5 Packages by EPSS Score
190 - </h3>
191 - <div class="grid grid-cols-1 lg:grid-cols-2 xl:grid-cols-3 gap-4">
192 - <div
193 - v-for="(pkg, index) in topEpssPackages.slice(0, 5)"
194 - :key="`${pkg.package_name}-${pkg.maxEpssScore}`"
195 - class="epss-package-card clickable"
196 - :class="{
197 - 'rank-1': index === 0,
198 - 'rank-2': index === 1,
199 - 'rank-3': index === 2,
200 - 'selected': searchPackage === pkg.package_name
201 - }"
202 - @click="selectPackage(pkg.package_name)"
203 - >
204 - <div class="epss-header">
205 - <div class="epss-rank">
206 - <Icon
207 - :name="index < 3 ? 'carbon:trophy' : 'carbon:warning-alt'"
208 - :size="16"
209 - :class="index === 0 ? 'text-yellow-500' : index === 1 ? 'text-gray-400' : index === 2 ? 'text-amber-600' : 'text-orange-500'"
210 - />
211 - <span class="rank-number">#{{ index + 1 }}</span>
212 - </div>
213 - <Badge color="warning" type="splitted" size="small">
214 - <template #label>EPSS</template>
215 - <template #value>{{ pkg.maxEpssScore.toFixed(3) }}</template>
216 - </Badge>
217 - </div>
218 -
219 - <div class="package-name">{{ pkg.package_name }}</div>
220 -
221 - <div class="package-stats">
222 - <div class="stat-row">
223 - <span class="stat-label">Vulnerabilities:</span>
224 - <span class="stat-value">{{ pkg.vulnCount.toLocaleString() }}</span>
225 - </div>
226 - <div class="stat-row">
227 - <span class="stat-label">Affected Agents:</span>
228 - <span class="stat-value">{{ pkg.affectedAgents.toLocaleString() }}</span>
229 - </div>
230 - <div class="stat-row">
231 - <span class="stat-label">Max CVSS:</span>
232 - <span class="stat-value">{{ pkg.maxCvssScore?.toFixed(1) || 'N/A' }}</span>
233 - </div>
234 - </div>
235 -
236 - <!-- Critical/High severity indicator -->
237 - <div v-if="pkg.criticalCount > 0 || pkg.highCount > 0" class="severity-indicator">
238 - <Badge v-if="pkg.criticalCount > 0" color="danger" size="small">
239 - <template #value>{{ pkg.criticalCount }} Critical</template>
240 - </Badge>
241 - <Badge v-if="pkg.highCount > 0" color="warning" size="small">
242 - <template #value>{{ pkg.highCount }} High</template>
243 - </Badge>
244 - </div>
245 - </div>
246 - </div>
247 - </div>
28 + <CollapseKeepAlive :show="showFiltersView" embedded arrow="top-right">
29 + <ListFilters class="p-3" @submit="applyFilters" @mounted="filtersCTX = $event" />
30 + </CollapseKeepAlive>
31
32 <!-- Vulnerability List -->
33 <n-spin :show="loading">
34 <div class="my-3">
252 - <template v-if="list.length">
253 - <div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-4">
254 - <VulnerabilityCard v-for="item of list" :key="`${item.cve_id}-${item.agent_name}`" :vulnerability="item" />
255 - </div>
256 -
257 - <!-- Pagination -->
258 - <div class="flex justify-center mt-6">
259 - <n-pagination
260 - v-model:page="currentPage"
261 - :page-count="totalPages"
262 - :page-size="pageSize"
263 - :item-count="totalCount"
264 - show-size-picker
265 - :page-sizes="[25, 50, 100, 200]"
266 - @update:page="updatePage"
267 - @update:page-size="updatePageSize"
268 - />
269 - </div>
270 - </template>
35 + <div v-if="list.length" class="grid grid-cols-1 gap-4 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4">
36 + <VulnerabilityCard v-for="item of list" :key="JSON.stringify(item)" :vulnerability="item" />
37 + </div>
38 <template v-else>
39 <n-empty v-if="!loading" description="No vulnerabilities found" class="h-48 justify-center" />
40 </template>
41 </div>
42 </n-spin>
43 +
44 + <div v-if="list.length >= 9" class="flex justify-end">
45 + <n-pagination
46 + v-model:page="currentPage"
47 + v-model:page-size="pageSize"
48 + :page-slot
49 + :page-sizes
50 + :item-count="totalCount"
51 + :show-size-picker
52 + />
53 + </div>
54 </div>
55 </template>
56
57 <script setup lang="ts">
280 -import type { Agent } from "@/types/agents.d"
281 -import type { Customer } from "@/types/customers.d"
282 -import type { VulnerabilitySearchItem, VulnerabilitySearchQuery } from "@/types/vulnerabilities.d"
283 -import { watchDebounced } from "@vueuse/core"
58 +import type { VulnerabilitiesListFilter } from "./types"
59 +import type {
60 + VulnerabilitySearchItem,
61 + VulnerabilitySearchQuery,
62 + VulnerabilitySeverity
63 +} from "@/types/vulnerabilities.d"
64 +import { useResizeObserver, useStorage, watchDebounced } from "@vueuse/core"
65 import axios from "axios"
285 -import { NButton, NEmpty, NInput, NPagination, NPopover, NSelect, NSpin, useMessage } from "naive-ui"
286 -import { computed, onMounted, ref } from "vue"
66 +import { NAlert, NBadge, NButton, NEmpty, NPagination, NSpin, useMessage } from "naive-ui"
67 +import { computed, ref } from "vue"
68 import Api from "@/api"
69 +import CollapseKeepAlive from "@/components/common/CollapseKeepAlive.vue"
70 import Icon from "@/components/common/Icon.vue"
289 -import { VulnerabilitySeverity } from "@/types/vulnerabilities.d"
71 +import ListFilters from "./ListFilters.vue"
72 import VulnerabilityCard from "./VulnerabilityCard.vue"
73 +import VulnerabilityStats from "./VulnerabilityStats.vue"
74
75 const loading = ref(false)
76 const message = useMessage()
77 const list = ref<VulnerabilitySearchItem[]>([])
78 +const pageSizes = [10, 25, 50, 100]
79 +const pageSize = ref(pageSizes[1])
80 +const pageSlot = ref(8)
81 +const showSizePicker = ref(true)
82 const header = ref()
296 -const totalCount = ref(0)
297 -const currentPage = ref(1)
298 -const pageSize = ref(50)
299 -const totalPages = ref(0)
300 -const selectedCustomer = ref<string | null>(null)
301 -const selectedSeverity = ref<VulnerabilitySeverity | null>(null)
302 -const searchCVE = ref<string>("")
303 -const searchAgent = ref<string>("")
304 -const searchPackage = ref<string>("")
305 -
306 -// Severity counts from API response
307 -const criticalCount = ref(0)
308 -const highCount = ref(0)
309 -const mediumCount = ref(0)
310 -const lowCount = ref(0)
311 -
312 -// Agents data for dropdown
313 -const agents = ref<Agent[]>([])
314 -const loadingAgents = ref(false)
315 -
316 -// Customers data for dropdown
317 -const customers = ref<Customer[]>([])
318 -const loadingCustomers = ref(false)
319 -
320 -const InfoIcon = "carbon:information"
321 -const SearchIcon = "carbon:search"
322 -const PackageIcon = "carbon:package"
323 -const TotalIcon = "carbon:result"
324 -const CriticalIcon = "carbon:warning-filled"
325 -const HighIcon = "carbon:warning"
326 -const MediumIcon = "carbon:warning-alt"
327 -const LowIcon = "carbon:information"
328 -
329 -const severityOptions = Object.values(VulnerabilitySeverity).map(severity => ({
330 - label: severity,
331 - value: severity
332 -}))
333 -
334 -// Agent options for dropdown
335 -const agentOptions = computed(() => {
336 - return (agents.value || []).map(agent => ({
337 - label: agent.hostname,
338 - value: agent.hostname
339 - }))
340 -})
341 -
342 -// Calculate statistics from current data
343 -const stats = computed(() => {
344 - // Use API response counts for global statistics across all pages
345 - const critical = criticalCount.value
346 - const high = highCount.value
347 - const medium = mediumCount.value
348 - const low = lowCount.value
349 -
350 - // Calculate unique values from current page data for context
351 - const uniqueAgents = new Set(list.value.map(v => v.agent_name)).size
352 - const uniquePackages = new Set(list.value.map(v => v.package_name).filter(Boolean)).size
353 - const uniqueCustomers = new Set(list.value.map(v => v.customer_code).filter(Boolean)).size
354 -
355 - return {
356 - critical,
357 - high,
358 - medium,
359 - low,
360 - uniqueAgents,
361 - uniquePackages,
362 - uniqueCustomers
363 - }
364 -})
365 -
366 -// Calculate top packages by EPSS score
367 -const topEpssPackages = computed(() => {
368 - // Group vulnerabilities by package name
369 - const packageMap = new Map<string, {
370 - package_name: string
371 - vulnCount: number
372 - maxEpssScore: number
373 - maxCvssScore: number | null
374 - affectedAgents: Set<string>
375 - criticalCount: number
376 - highCount: number
377 - }>()
378 -
379 - list.value.forEach(vuln => {
380 - if (!vuln.package_name || !vuln.epss_score) return
381 -
382 - const epssScore = Number.parseFloat(vuln.epss_score)
383 - if (Number.isNaN(epssScore)) return
83 +const showFiltersView = useStorage<boolean>("agents-vulnerability-list-filters-view-state", false, localStorage)
84
385 - const key = vuln.package_name
386 - const existing = packageMap.get(key)
85 +const filtersCTX = ref<{ setFilter: (payload: VulnerabilitiesListFilter[]) => void } | null>(null)
86 +const filters = ref<VulnerabilitiesListFilter[]>([])
87
388 - if (existing) {
389 - existing.vulnCount++
390 - existing.maxEpssScore = Math.max(existing.maxEpssScore, epssScore)
391 - if (vuln.base_score) {
392 - existing.maxCvssScore = Math.max(existing.maxCvssScore || 0, vuln.base_score)
393 - }
394 - existing.affectedAgents.add(vuln.agent_name)
395 -
396 - if (vuln.severity === VulnerabilitySeverity.Critical) existing.criticalCount++
397 - if (vuln.severity === VulnerabilitySeverity.High) existing.highCount++
398 - } else {
399 - packageMap.set(key, {
400 - package_name: vuln.package_name,
401 - vulnCount: 1,
402 - maxEpssScore: epssScore,
403 - maxCvssScore: vuln.base_score || null,
404 - affectedAgents: new Set([vuln.agent_name]),
405 - criticalCount: vuln.severity === VulnerabilitySeverity.Critical ? 1 : 0,
406 - highCount: vuln.severity === VulnerabilitySeverity.High ? 1 : 0
407 - })
408 - }
409 - })
410 -
411 - // Convert to array and sort by max EPSS score
412 - return Array.from(packageMap.values())
413 - .map(pkg => ({
414 - ...pkg,
415 - affectedAgents: pkg.affectedAgents.size
416 - }))
417 - .sort((a, b) => b.maxEpssScore - a.maxEpssScore)
418 - .slice(0, 5)
88 +const filtered = computed<boolean>(() => {
89 + return !!filters.value.length
90 })
91
421 -function getPercentage(count: number): string {
422 - if (totalCount.value === 0) return "0"
423 - return ((count / totalCount.value) * 100).toFixed(1)
424 -}
92 +const totalCount = ref(0)
93 +const currentPage = ref(1)
94
426 -// Customer options for dropdown
427 -const customerOptions = computed(() => {
428 - return (customers.value || []).map(customer => ({
429 - label: customer.customer_code,
430 - value: customer.customer_code
431 - }))
432 -})
95 +const FilterIcon = "carbon:filter-edit"
96
97 let abortController: AbortController | null = null
98
@@ -442,11 +105,11 @@ function getList() {
105 const query: VulnerabilitySearchQuery = {
106 page: currentPage.value,
107 page_size: pageSize.value,
445 - customer_code: selectedCustomer.value || undefined,
446 - severity: selectedSeverity.value || undefined,
447 - cve_id: searchCVE.value || undefined,
448 - agent_name: searchAgent.value || undefined,
449 - package_name: searchPackage.value || undefined,
108 + customer_code: filters.value.find(o => o.type === "customer_code")?.value || undefined,
109 + severity: (filters.value.find(o => o.type === "severity")?.value as VulnerabilitySeverity) || undefined,
110 + cve_id: filters.value.find(o => o.type === "cve_id")?.value || undefined,
111 + agent_name: filters.value.find(o => o.type === "agent_name")?.value || undefined,
112 + package_name: filters.value.find(o => o.type === "package_name")?.value || undefined,
113 include_epss: true
114 }
115
@@ -458,14 +121,7 @@ function getList() {
121 if (res.data.success) {
122 list.value = res.data?.vulnerabilities || []
123 totalCount.value = res.data?.total_count || 0
461 - totalPages.value = res.data?.total_pages || 0
124 currentPage.value = res.data?.page || 1
463 -
464 - // Store severity counts from API response
465 - criticalCount.value = res.data?.critical_count || 0
466 - highCount.value = res.data?.high_count || 0
467 - mediumCount.value = res.data?.medium_count || 0
468 - lowCount.value = res.data?.low_count || 0
125 } else {
126 message.warning(res.data?.message || "An error occurred. Please try again later.")
127 }
@@ -478,619 +134,51 @@ function getList() {
134 })
135 }
136
481 -function getAgents() {
482 - loadingAgents.value = true
483 -
484 - Api.agents
485 - .getAgents()
486 - .then(res => {
487 - if (res.data.success) {
488 - agents.value = res.data.agents || []
489 - } else {
490 - message.warning(res.data?.message || "Failed to load agents.")
491 - }
492 - })
493 - .catch(err => {
494 - message.error(err.response?.data?.message || "Failed to load agents.")
495 - })
496 - .finally(() => {
497 - loadingAgents.value = false
498 - })
137 +function selectSeverity(value: VulnerabilitySeverity) {
138 + showFiltersView.value = true
139 + filtersCTX.value?.setFilter([{ type: "severity", value }])
140 }
141
501 -function getCustomers() {
502 - loadingCustomers.value = true
503 -
504 - Api.customers
505 - .getCustomers()
506 - .then(res => {
507 - if (res.data.success) {
508 - customers.value = res.data.customers || []
509 - } else {
510 - message.warning(res.data?.message || "Failed to load customers.")
511 - }
512 - })
513 - .catch(err => {
514 - message.error(err.response?.data?.message || "Failed to load customers.")
515 - })
516 - .finally(() => {
517 - loadingCustomers.value = false
518 - })
142 +function selectPackage(value: string) {
143 + showFiltersView.value = true
144 + filtersCTX.value?.setFilter([{ type: "package_name", value }])
145 }
146
521 -function updatePage(page: number) {
522 - currentPage.value = page
523 - getList()
147 +function applyFilters(newFilters: VulnerabilitiesListFilter[]) {
148 + filters.value = newFilters
149 }
150
526 -function updatePageSize(size: number) {
527 - pageSize.value = size
528 - currentPage.value = 1
529 - getList()
530 -}
151 +watchDebounced(
152 + currentPage,
153 + () => {
154 + getList()
155 + },
156 + { debounce: 300 }
157 +)
158
532 -function selectPackage(packageName: string) {
533 - // If the same package is already selected, clear the filter
534 - if (searchPackage.value === packageName) {
535 - searchPackage.value = ""
536 - } else {
537 - // Set the package name in the search filter
538 - searchPackage.value = packageName
539 - }
540 - // Reset to first page when filtering
541 - currentPage.value = 1
542 -}
159 +watchDebounced(
160 + pageSize,
161 + () => {
162 + currentPage.value = 1
163 + getList()
164 + },
165 + { debounce: 300 }
166 +)
167
544 -function selectSeverity(severity: VulnerabilitySeverity) {
545 - // If the same severity is already selected, clear the filter
546 - if (selectedSeverity.value === severity) {
547 - selectedSeverity.value = null
548 - } else {
549 - // Set the severity in the search filter
550 - selectedSeverity.value = severity
551 - }
552 - // Reset to first page when filtering
553 - currentPage.value = 1
554 -}
168 +watchDebounced(
169 + filters,
170 + () => {
171 + currentPage.value = 1
172 + getList()
173 + },
174 + { deep: true, debounce: 300, immediate: true }
175 +)
176
556 -// Load agents and customers when component mounts
557 -onMounted(() => {
558 - getAgents()
559 - getCustomers()
560 -})
177 +useResizeObserver(header, entries => {
178 + const entry = entries[0]
179 + const { width } = entry.contentRect
180
562 -watchDebounced([selectedCustomer, selectedSeverity, searchCVE, searchAgent, searchPackage], () => {
563 - currentPage.value = 1
564 - getList()
565 -}, {
566 - deep: true,
567 - debounce: 300,
568 - immediate: true
181 + pageSlot.value = width < 700 ? 5 : 8
182 + showSizePicker.value = width > 550
183 })
184 </script>
571 -
572 -<style scoped>
573 -.stat-card {
574 - background-color: white;
575 - border-radius: 0.5rem;
576 - padding: 1rem;
577 - border: 1px solid rgb(229 231 235);
578 - box-shadow: 0 1px 2px 0 rgb(0 0 0 / 0.05);
579 - transition: all 0.2s ease;
580 -}
581 -
582 -.stat-card.clickable {
583 - cursor: pointer;
584 -}
585 -
586 -.stat-card:hover {
587 - transform: translateY(-2px);
588 - box-shadow: 0 4px 8px 0 rgb(0 0 0 / 0.1);
589 -}
590 -
591 -.stat-card.selected {
592 - border-width: 2px;
593 - box-shadow: 0 4px 12px 0 rgb(59 130 246 / 0.3);
594 -}
595 -
596 -.stat-card.critical {
597 - border-color: rgb(254 202 202);
598 - background-color: rgb(254 242 242);
599 -}
600 -
601 -.stat-card.critical.selected {
602 - border-color: rgb(220 38 38);
603 - background-color: rgb(254 226 226);
604 -}
605 -
606 -.stat-card.high {
607 - border-color: rgb(254 215 170);
608 - background-color: rgb(255 247 237);
609 -}
610 -
611 -.stat-card.high.selected {
612 - border-color: rgb(234 88 12);
613 - background-color: rgb(255 237 213);
614 -}
615 -
616 -.stat-card.medium {
617 - border-color: rgb(254 240 138);
618 - background-color: rgb(254 252 232);
619 -}
620 -
621 -.stat-card.medium.selected {
622 - border-color: rgb(202 138 4);
623 - background-color: rgb(254 249 195);
624 -}
625 -
626 -.stat-card.low {
627 - border-color: rgb(191 219 254);
628 - background-color: rgb(239 246 255);
629 -}
630 -
631 -.stat-card.low.selected {
632 - border-color: rgb(59 130 246);
633 - background-color: rgb(219 234 254);
634 -}
635 -
636 -.stat-header {
637 - display: flex;
638 - align-items: center;
639 - gap: 0.5rem;
640 - margin-bottom: 0.5rem;
641 -}
642 -
643 -.stat-title {
644 - font-size: 0.875rem;
645 - font-weight: 500;
646 - color: rgb(75 85 99);
647 -}
648 -
649 -.stat-value {
650 - font-size: 1.5rem;
651 - font-weight: 700;
652 - color: rgb(17 24 39);
653 -}
654 -
655 -.stat-percentage {
656 - font-size: 0.75rem;
657 - color: rgb(107 114 128);
658 - margin-top: 0.25rem;
659 -}
660 -
661 -.quick-stat {
662 - display: flex;
663 - align-items: center;
664 - gap: 0.5rem;
665 - padding: 0.75rem;
666 - background-color: rgb(249 250 251);
667 - border-radius: 0.5rem;
668 -}
669 -
670 -/* EPSS Package Cards */
671 -.epss-package-card {
672 - background-color: white;
673 - border: 1px solid rgb(229 231 235);
674 - border-radius: 0.5rem;
675 - padding: 1rem;
676 - transition: all 0.2s ease;
677 -}
678 -
679 -/* Light mode specific styles */
680 -:root .epss-package-card,
681 -html:not(.dark) .epss-package-card,
682 -[data-theme="light"] .epss-package-card {
683 - background-color: white;
684 - border-color: rgb(229 231 235);
685 -}
686 -
687 -.epss-package-card.clickable {
688 - cursor: pointer;
689 -}
690 -
691 -.epss-package-card:hover {
692 - border-color: rgb(156 163 175);
693 - box-shadow: 0 4px 6px -1px rgb(0 0 0 / 0.1);
694 - transform: translateY(-2px);
695 -}
696 -
697 -.epss-package-card.selected {
698 - border-color: rgb(59 130 246);
699 - background-color: rgb(239 246 255);
700 - box-shadow: 0 4px 12px -1px rgb(59 130 246 / 0.2);
701 -}
702 -
703 -.epss-package-card.rank-1 {
704 - border-color: rgb(234 179 8);
705 - background-color: rgb(255 255 255);
706 -}
707 -
708 -.epss-package-card.rank-2 {
709 - border-color: rgb(156 163 175);
710 - background-color: rgb(255 255 255);
711 -}
712 -
713 -.epss-package-card.rank-3 {
714 - border-color: rgb(217 119 6);
715 - background-color: rgb(255 255 255);
716 -}
717 -
718 -/* Light mode ranked cards */
719 -:root .epss-package-card.rank-1,
720 -html:not(.dark) .epss-package-card.rank-1,
721 -[data-theme="light"] .epss-package-card.rank-1 {
722 - border-color: rgb(234 179 8);
723 - background-color: white;
724 -}
725 -
726 -:root .epss-package-card.rank-2,
727 -html:not(.dark) .epss-package-card.rank-2,
728 -[data-theme="light"] .epss-package-card.rank-2 {
729 - border-color: rgb(156 163 175);
730 - background-color: white;
731 -}
732 -
733 -:root .epss-package-card.rank-3,
734 -html:not(.dark) .epss-package-card.rank-3,
735 -[data-theme="light"] .epss-package-card.rank-3 {
736 - border-color: rgb(217 119 6);
737 - background-color: white;
738 -}
739 -
740 -.epss-header {
741 - display: flex;
742 - justify-content: space-between;
743 - align-items: center;
744 - margin-bottom: 0.75rem;
745 -}
746 -
747 -.epss-rank {
748 - display: flex;
749 - align-items: center;
750 - gap: 0.5rem;
751 -}
752 -
753 -.rank-number {
754 - font-weight: 600;
755 - font-size: 0.875rem;
756 - color: rgb(75 85 99);
757 -}
758 -
759 -.package-name {
760 - font-weight: 600;
761 - font-size: 1rem;
762 - color: rgb(17 24 39);
763 - margin-bottom: 0.75rem;
764 - font-family: ui-monospace, SFMono-Regular, "SF Mono", Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
765 -}
766 -
767 -.package-stats {
768 - display: flex;
769 - flex-direction: column;
770 - gap: 0.5rem;
771 - margin-bottom: 0.75rem;
772 -}
773 -
774 -.stat-row {
775 - display: flex;
776 - justify-content: space-between;
777 - align-items: center;
778 -}
779 -
780 -.stat-label {
781 - font-size: 0.75rem;
782 - color: rgb(107 114 128);
783 - font-weight: 500;
784 -}
785 -
786 -.stat-value {
787 - font-size: 1rem;
788 - font-weight: 800;
789 - color: rgb(255 255 255);
790 - background-color: rgb(59 130 246);
791 - padding: 0.375rem 0.75rem;
792 - border-radius: 0.5rem;
793 - font-family: ui-monospace, SFMono-Regular, "SF Mono", Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
794 - text-align: center;
795 - min-width: 3rem;
796 - box-shadow: 0 1px 3px 0 rgb(0 0 0 / 0.1);
797 -}
798 -
799 -.severity-indicator {
800 - display: flex;
801 - gap: 0.5rem;
802 - flex-wrap: wrap;
803 -}
804 -
805 -/* Dark mode styles */
806 -html.dark .stat-card {
807 - background-color: rgb(31 41 55);
808 - border-color: rgb(75 85 99);
809 -}
810 -
811 -html.dark .stat-card.critical {
812 - border-color: rgb(220 38 38);
813 - background-color: rgb(127 29 29);
814 -}
815 -
816 -html.dark .stat-card.high {
817 - border-color: rgb(234 88 12);
818 - background-color: rgb(154 52 18);
819 -}
820 -
821 -html.dark .stat-card.medium {
822 - border-color: rgb(202 138 4);
823 - background-color: rgb(161 98 7);
824 -}
825 -
826 -html.dark .stat-card.low {
827 - border-color: rgb(59 130 246);
828 - background-color: rgb(30 64 175);
829 -}
830 -
831 -html.dark .stat-title {
832 - color: rgb(209 213 219);
833 -}
834 -
835 -html.dark .stat-value {
836 - color: rgb(255 255 255);
837 -}
838 -
839 -html.dark .stat-percentage {
840 - color: rgb(209 213 219);
841 -}
842 -
843 -html.dark .quick-stat {
844 - background-color: rgb(31 41 55);
845 - color: rgb(243 244 246);
846 -}
847 -
848 -/* Dark mode for EPSS Package Cards */
849 -html.dark .epss-package-card {
850 - background-color: rgb(31 41 55) !important;
851 - border-color: rgb(75 85 99);
852 -}
853 -
854 -html.dark .epss-package-card:hover {
855 - border-color: rgb(156 163 175);
856 -}
857 -
858 -html.dark .epss-package-card.rank-1 {
859 - border-color: rgb(234 179 8);
860 - background-color: rgb(31 41 55) !important;
861 -}
862 -
863 -html.dark .epss-package-card.rank-2 {
864 - border-color: rgb(156 163 175);
865 - background-color: rgb(31 41 55) !important;
866 -}
867 -
868 -html.dark .epss-package-card.rank-3 {
869 - border-color: rgb(217 119 6);
870 - background-color: rgb(31 41 55) !important;
871 -}
872 -
873 -html.dark .rank-number {
874 - color: rgb(209 213 219);
875 -}
876 -
877 -html.dark .package-name {
878 - color: rgb(243 244 246);
879 -}
880 -
881 -html.dark .stat-label {
882 - color: rgb(156 163 175);
883 -}
884 -
885 -html.dark .stat-value {
886 - color: rgb(255 255 255);
887 - background-color: rgb(79 70 229);
888 - box-shadow: 0 1px 3px 0 rgb(0 0 0 / 0.3);
889 -}
890 -
891 -/* Alternative dark mode selectors for better compatibility */
892 -.dark .stat-card,
893 -[data-theme="dark"] .stat-card {
894 - background-color: rgb(31 41 55);
895 - border-color: rgb(75 85 99);
896 -}
897 -
898 -.dark .stat-card.critical,
899 -[data-theme="dark"] .stat-card.critical {
900 - border-color: rgb(220 38 38);
901 - background-color: rgb(127 29 29);
902 -}
903 -
904 -.dark .stat-card.critical.selected,
905 -[data-theme="dark"] .stat-card.critical.selected {
906 - border-color: rgb(248 113 113);
907 - background-color: rgb(153 27 27);
908 - box-shadow: 0 4px 12px 0 rgb(248 113 113 / 0.3);
909 -}
910 -
911 -.dark .stat-card.high,
912 -[data-theme="dark"] .stat-card.high {
913 - border-color: rgb(234 88 12);
914 - background-color: rgb(154 52 18);
915 -}
916 -
917 -.dark .stat-card.high.selected,
918 -[data-theme="dark"] .stat-card.high.selected {
919 - border-color: rgb(251 146 60);
920 - background-color: rgb(194 65 14);
921 - box-shadow: 0 4px 12px 0 rgb(251 146 60 / 0.3);
922 -}
923 -
924 -.dark .stat-card.medium,
925 -[data-theme="dark"] .stat-card.medium {
926 - border-color: rgb(202 138 4);
927 - background-color: rgb(161 98 7);
928 -}
929 -
930 -.dark .stat-card.medium.selected,
931 -[data-theme="dark"] .stat-card.medium.selected {
932 - border-color: rgb(250 204 21);
933 - background-color: rgb(180 83 9);
934 - box-shadow: 0 4px 12px 0 rgb(250 204 21 / 0.3);
935 -}
936 -
937 -.dark .stat-card.low,
938 -[data-theme="dark"] .stat-card.low {
939 - border-color: rgb(59 130 246);
940 - background-color: rgb(30 64 175);
941 -}
942 -
943 -.dark .stat-card.low.selected,
944 -[data-theme="dark"] .stat-card.low.selected {
945 - border-color: rgb(96 165 250);
946 - background-color: rgb(37 99 235);
947 - box-shadow: 0 4px 12px 0 rgb(96 165 250 / 0.3);
948 -}
949 -
950 -.dark .stat-title,
951 -[data-theme="dark"] .stat-title {
952 - color: rgb(209 213 219);
953 -}
954 -
955 -.dark .stat-value,
956 -[data-theme="dark"] .stat-value {
957 - color: rgb(255 255 255);
958 -}
959 -
960 -.dark .stat-percentage,
961 -[data-theme="dark"] .stat-percentage {
962 - color: rgb(209 213 219);
963 -}
964 -
965 -.dark .quick-stat,
966 -[data-theme="dark"] .quick-stat {
967 - background-color: rgb(31 41 55);
968 - color: rgb(243 244 246);
969 -}
970 -
971 -/* EPSS Package Cards - Alternative dark mode selectors */
972 -.dark .epss-package-card,
973 -[data-theme="dark"] .epss-package-card {
974 - background-color: rgb(31 41 55) !important;
975 - border-color: rgb(75 85 99);
976 -}
977 -
978 -.dark .epss-package-card.rank-1,
979 -[data-theme="dark"] .epss-package-card.rank-1 {
980 - border-color: rgb(234 179 8);
981 - background-color: rgb(31 41 55) !important;
982 -}
983 -
984 -.dark .epss-package-card.rank-2,
985 -[data-theme="dark"] .epss-package-card.rank-2 {
986 - border-color: rgb(156 163 175);
987 - background-color: rgb(31 41 55) !important;
988 -}
989 -
990 -.dark .epss-package-card.rank-3,
991 -[data-theme="dark"] .epss-package-card.rank-3 {
992 - border-color: rgb(217 119 6);
993 - background-color: rgb(31 41 55) !important;
994 -}
995 -
996 -.dark .epss-package-card:hover,
997 -[data-theme="dark"] .epss-package-card:hover {
998 - border-color: rgb(156 163 175);
999 - box-shadow: 0 4px 6px -1px rgb(0 0 0 / 0.3);
1000 -}
1001 -
1002 -.dark .epss-package-card.selected,
1003 -[data-theme="dark"] .epss-package-card.selected {
1004 - border-color: rgb(96 165 250);
1005 - background-color: rgb(30 58 138);
1006 - box-shadow: 0 4px 12px -1px rgb(96 165 250 / 0.3);
1007 -}
1008 -
1009 -.dark .package-name,
1010 -[data-theme="dark"] .package-name {
1011 - color: rgb(243 244 246);
1012 -}
1013 -
1014 -.dark .stat-label,
1015 -[data-theme="dark"] .stat-label {
1016 - color: rgb(156 163 175);
1017 -}
1018 -
1019 -.dark .stat-value,
1020 -[data-theme="dark"] .stat-value {
1021 - color: rgb(255 255 255);
1022 - background-color: rgb(79 70 229);
1023 - box-shadow: 0 1px 3px 0 rgb(0 0 0 / 0.3);
1024 -}
1025 -
1026 -/* Media query for system dark mode preference */
1027 -@media (prefers-color-scheme: dark) {
1028 - .stat-card {
1029 - background-color: rgb(31 41 55);
1030 - border-color: rgb(75 85 99);
1031 - }
1032 -
1033 - .stat-card.critical {
1034 - border-color: rgb(220 38 38);
1035 - background-color: rgb(127 29 29);
1036 - }
1037 -
1038 - .stat-card.high {
1039 - border-color: rgb(234 88 12);
1040 - background-color: rgb(154 52 18);
1041 - }
1042 -
1043 - .stat-card.medium {
1044 - border-color: rgb(202 138 4);
1045 - background-color: rgb(161 98 7);
1046 - }
1047 -
1048 - .stat-card.low {
1049 - border-color: rgb(59 130 246);
1050 - background-color: rgb(30 64 175);
1051 - }
1052 -
1053 - .stat-title {
1054 - color: rgb(209 213 219);
1055 - }
1056 -
1057 - .stat-value {
1058 - color: rgb(255 255 255);
1059 - background-color: rgb(79 70 229);
1060 - box-shadow: 0 1px 3px 0 rgb(0 0 0 / 0.3);
1061 - }
1062 -
1063 - .stat-percentage {
1064 - color: rgb(209 213 219);
1065 - }
1066 -
1067 - .quick-stat {
1068 - background-color: rgb(31 41 55);
1069 - color: rgb(243 244 246);
1070 - }
1071 -
1072 - .epss-package-card {
1073 - background-color: rgb(31 41 55) !important;
1074 - border-color: rgb(75 85 99);
1075 - }
1076 -
1077 - .epss-package-card:hover {
1078 - border-color: rgb(156 163 175);
1079 - box-shadow: 0 4px 6px -1px rgb(0 0 0 / 0.3);
1080 - }
1081 -
1082 - .epss-package-card.selected {
1083 - border-color: rgb(96 165 250);
1084 - background-color: rgb(30 58 138);
1085 - box-shadow: 0 4px 12px -1px rgb(96 165 250 / 0.3);
1086 - }
1087 -
1088 - .package-name {
1089 - color: rgb(243 244 246);
1090 - }
1091 -
1092 - .stat-label {
1093 - color: rgb(156 163 175);
1094 - }
1095 -}
1096 -</style>
frontend/src/components/vulnerabilities/ListFilters.vue new
+278
@@ -0,0 +1,278 @@
1 +<template>
2 + <div class="alerts-filters flex flex-wrap gap-3">
3 + <div v-for="filter of filters" :key="filter.type">
4 + <n-input-group v-if="filter.type === 'customer_code'">
5 + <n-input-group-label size="small" class="flex! items-center gap-2">
6 + <Icon :name="CustomersIcon" />
7 + {{ getFilterLabel(filter.type) }}
8 + </n-input-group-label>
9 + <n-select
10 + v-model:value="filter.value"
11 + size="small"
12 + :options="customersOptions"
13 + placeholder="Select..."
14 + :loading="loadingCustomers"
15 + filterable
16 + class="w-50!"
17 + :consistent-menu-width="false"
18 + />
19 + <n-button size="small" secondary tabindex="-1" @click="delFilter(filter.type)">
20 + <template #icon>
21 + <Icon :name="DelIcon" />
22 + </template>
23 + </n-button>
24 + </n-input-group>
25 +
26 + <n-input-group v-if="filter.type === 'severity'">
27 + <n-input-group-label size="small" class="flex! items-center gap-2">
28 + <Icon :name="SeverityIcon" />
29 + {{ getFilterLabel(filter.type) }}
30 + </n-input-group-label>
31 + <n-select
32 + v-model:value="filter.value"
33 + :options="severityOptions"
34 + placeholder="Select..."
35 + size="small"
36 + class="w-30!"
37 + :consistent-menu-width="false"
38 + />
39 + <n-button size="small" secondary tabindex="-1" @click="delFilter(filter.type)">
40 + <template #icon>
41 + <Icon :name="DelIcon" />
42 + </template>
43 + </n-button>
44 + </n-input-group>
45 +
46 + <n-input-group v-if="filter.type === 'agent_name'">
47 + <n-input-group-label size="small" class="flex! items-center gap-2">
48 + <Icon :name="AgentsIcon" />
49 + {{ getFilterLabel(filter.type) }}
50 + </n-input-group-label>
51 + <n-select
52 + v-model:value="filter.value"
53 + :options="agentsOptions"
54 + placeholder="Select..."
55 + size="small"
56 + filterable
57 + class="w-50!"
58 + :loading="loadingAgents"
59 + :consistent-menu-width="false"
60 + />
61 + <n-button size="small" secondary tabindex="-1" @click="delFilter(filter.type)">
62 + <template #icon>
63 + <Icon :name="DelIcon" />
64 + </template>
65 + </n-button>
66 + </n-input-group>
67 +
68 + <n-input-group
69 + v-if="filter.type === 'cve_id' && (typeof filter.value === 'string' || filter.value === null)"
70 + >
71 + <n-input-group-label size="small" class="flex! items-center gap-2">
72 + <Icon :name="SearchIcon" />
73 + {{ getFilterLabel(filter.type) }}
74 + </n-input-group-label>
75 + <n-input v-model:value="filter.value" autosize placeholder="Input..." size="small" class="min-w-60!" />
76 + <n-button size="small" secondary tabindex="-1" @click="delFilter(filter.type)">
77 + <template #icon>
78 + <Icon :name="DelIcon" />
79 + </template>
80 + </n-button>
81 + </n-input-group>
82 +
83 + <n-input-group
84 + v-if="filter.type === 'package_name' && (typeof filter.value === 'string' || filter.value === null)"
85 + >
86 + <n-input-group-label size="small" class="flex! items-center gap-2">
87 + <Icon :name="PackageIcon" />
88 + {{ getFilterLabel(filter.type) }}
89 + </n-input-group-label>
90 + <n-input v-model:value="filter.value" autosize placeholder="Input..." size="small" class="min-w-60!" />
91 + <n-button size="small" secondary tabindex="-1" @click="delFilter(filter.type)">
92 + <template #icon>
93 + <Icon :name="DelIcon" />
94 + </template>
95 + </n-button>
96 + </n-input-group>
97 + </div>
98 +
99 + <n-dropdown
100 + v-if="availableFilters.length"
101 + placement="bottom-start"
102 + trigger="click"
103 + :options="availableFilters"
104 + @select="addFilter"
105 + >
106 + <n-button size="small" dashed @click="load()">
107 + <template #icon>
108 + <Icon :name="AddIcon" />
109 + </template>
110 + <span v-if="!filters.length">Add filter</span>
111 + </n-button>
112 + </n-dropdown>
113 +
114 + <n-button v-if="filters.length && isDirty" size="small" secondary type="primary" @click="submit()">
115 + Submit
116 + </n-button>
117 +
118 + <n-button v-if="filters.length" size="small" quaternary @click="reset()">Reset</n-button>
119 + </div>
120 +</template>
121 +
122 +<script setup lang="ts">
123 +import type { VulnerabilitiesFilterTypes, VulnerabilitiesListFilter } from "./types.d"
124 +import type { Agent } from "@/types/agents.d"
125 +import type { Customer } from "@/types/customers.d"
126 +import _cloneDeep from "lodash/cloneDeep"
127 +import _isEqual from "lodash/isEqual"
128 +import { NButton, NDropdown, NInput, NInputGroup, NInputGroupLabel, NSelect, useMessage } from "naive-ui"
129 +import { computed, onMounted, ref } from "vue"
130 +import Api from "@/api"
131 +import Icon from "@/components/common/Icon.vue"
132 +import { VulnerabilitySeverity } from "@/types/vulnerabilities.d"
133 +
134 +const emit = defineEmits<{
135 + (e: "submit", value: VulnerabilitiesListFilter[]): void
136 + (
137 + e: "mounted",
138 + value: {
139 + setFilter: (payload: VulnerabilitiesListFilter[]) => void
140 + }
141 + ): void
142 +}>()
143 +
144 +const SearchIcon = "carbon:search"
145 +const AgentsIcon = "carbon:network-3"
146 +const PackageIcon = "carbon:package"
147 +const CustomersIcon = "carbon:user-multiple"
148 +const SeverityIcon = "carbon:warning"
149 +
150 +const AddIcon = "carbon:add"
151 +const DelIcon = "carbon:delete"
152 +const message = useMessage()
153 +const loadingAgents = ref(false)
154 +const loadingCustomers = ref(false)
155 +const agentsList = ref<Agent[]>([])
156 +const customersList = ref<Customer[]>([])
157 +
158 +const customersOptions = computed(() =>
159 + customersList.value.map(o => ({ label: `#${o.customer_code} - ${o.customer_name}`, value: o.customer_code }))
160 +)
161 +
162 +const agentsOptions = computed(() => agentsList.value.map(o => ({ label: o.hostname, value: o.hostname })))
163 +
164 +const severityOptions = Object.values(VulnerabilitySeverity).map(severity => ({
165 + label: severity,
166 + value: severity
167 +}))
168 +
169 +const typeOptions: { label: string; value: VulnerabilitiesFilterTypes }[] = [
170 + { label: "Customer", value: "customer_code" },
171 + { label: "Severity", value: "severity" },
172 + { label: "CVE", value: "cve_id" },
173 + { label: "Agent", value: "agent_name" },
174 + { label: "Package", value: "package_name" }
175 +]
176 +
177 +const filters = ref<VulnerabilitiesListFilter[]>([])
178 +const lastFilters = ref<VulnerabilitiesListFilter[]>([])
179 +
180 +const availableFilters = computed(() =>
181 + typeOptions
182 + .filter(o => !filters.value.map(f => f.type).includes(o.value))
183 + .map(t => ({ key: t.value, label: t.label }))
184 +)
185 +
186 +const isDirty = computed(() => !_isEqual(filters.value, lastFilters.value))
187 +
188 +function getFilterLabel(type: VulnerabilitiesFilterTypes): string {
189 + return typeOptions.find(o => o.value === type)?.label || type
190 +}
191 +
192 +function addFilter(key: VulnerabilitiesFilterTypes) {
193 + filters.value.push({ type: key, value: null })
194 +}
195 +
196 +function delFilter(key: VulnerabilitiesFilterTypes) {
197 + filters.value = filters.value.filter(o => o.type !== key)
198 + submit()
199 +}
200 +
201 +function setFilter(newFilters: VulnerabilitiesListFilter[]) {
202 + for (const newFilter of newFilters) {
203 + const filterIndex = filters.value.findIndex(o => o.type === newFilter.type)
204 +
205 + if (filterIndex !== -1) {
206 + if (newFilter.value) {
207 + filters.value[filterIndex].value = newFilter.value
208 + } else {
209 + delFilter(newFilter.type)
210 + }
211 + } else if (newFilter.value) {
212 + filters.value.push(newFilter)
213 + }
214 + }
215 + submit()
216 +}
217 +
218 +function reset() {
219 + filters.value = []
220 + submit()
221 +}
222 +
223 +function submit() {
224 + lastFilters.value = _cloneDeep(filters.value)
225 + emit("submit", lastFilters.value)
226 +}
227 +
228 +function getAgents() {
229 + loadingAgents.value = true
230 +
231 + Api.agents
232 + .getAgents()
233 + .then(res => {
234 + if (res.data.success) {
235 + agentsList.value = res.data.agents || []
236 + } else {
237 + message.warning(res.data?.message || "Failed to load agents.")
238 + }
239 + })
240 + .catch(err => {
241 + message.error(err.response?.data?.message || "Failed to load agents.")
242 + })
243 + .finally(() => {
244 + loadingAgents.value = false
245 + })
246 +}
247 +
248 +function getCustomers() {
249 + loadingCustomers.value = true
250 +
251 + Api.customers
252 + .getCustomers()
253 + .then(res => {
254 + if (res.data.success) {
255 + customersList.value = res.data?.customers || []
256 + } else {
257 + message.warning(res.data?.message || "An error occurred. Please try again later.")
258 + }
259 + })
260 + .catch(err => {
261 + message.error(err.response?.data?.message || "An error occurred. Please try again later.")
262 + })
263 + .finally(() => {
264 + loadingCustomers.value = false
265 + })
266 +}
267 +
268 +function load() {
269 + getAgents()
270 + getCustomers()
271 +}
272 +
273 +onMounted(() => {
274 + emit("mounted", {
275 + setFilter
276 + })
277 +})
278 +</script>
frontend/src/components/vulnerabilities/VulnerabilityCard.vue
+55 -63
@@ -1,24 +1,41 @@
1 <template>
2 - <div class="vulnerability-card h-full">
3 - <CardEntity hoverable clickable :embedded class="@container h-full flex flex-col" :class="getSeverityBorderClass(vulnerability.severity)" @click.stop="showDetails = true">
2 + <div class="h-full">
3 + <CardEntity
4 + clickable
5 + :embedded
6 + class="h-full"
7 + main-box-class="grow"
8 + card-entity-wrapper-class="h-full"
9 + header-box-class="flex-nowrap! items-start"
10 + :class="`${getSeverityBorderClass(vulnerability.severity)} transition-all duration-300`"
11 + @click.stop="showDetails = true"
12 + >
13 <template #headerMain>{{ vulnerability.cve_id }}</template>
14 <template #headerExtra>
6 - <Badge :color="getSeverityColor(vulnerability.severity)">
7 - <template #iconLeft><Icon :name="getSeverityIcon(vulnerability.severity)" :size="14" /></template>
8 - <template #value>{{ vulnerability.severity }}</template>
9 - </Badge>
15 + <VulnerabilitySeverityBadge :severity="vulnerability.severity" :size="14" />
16 </template>
17 <template #default>
12 - <div class="flex-1">
13 - <p class="text-base font-medium opacity-90 leading-relaxed line-clamp-3">{{ vulnerability.title }}</p>
14 - <div class="mt-2 text-sm opacity-75">
18 + <div class="flex flex-col gap-4">
19 + <div class="font-medium leading-snug">
20 + {{ vulnerability.title }}
21 + </div>
22 + <div class="text-secondary flex flex-col gap-0.5 text-sm">
23 <div class="flex items-center gap-2">
24 <Icon :name="HostIcon" :size="14" />
25 <span>{{ vulnerability.agent_name }}</span>
26 </div>
19 - <div v-if="vulnerability.package_name" class="flex items-center gap-2 mt-1">
27 + <div v-if="vulnerability.package_name" class="mt-1 flex items-center gap-2">
28 <Icon :name="PackageIcon" :size="14" />
21 - <span>{{ vulnerability.package_name }}{{ vulnerability.package_version ? ` (${vulnerability.package_version})` : '' }}</span>
29 + <span>
30 + {{ vulnerability.package_name
31 + }}{{ vulnerability.package_version ? ` (${vulnerability.package_version})` : "" }}
32 + </span>
33 + </div>
34 + <div v-if="vulnerability.package_architecture" class="mt-1 flex items-center gap-2">
35 + <Icon :name="ArchitectureIcon" :size="14" />
36 + <span>
37 + {{ vulnerability.package_architecture }}
38 + </span>
39 </div>
40 </div>
41 </div>
@@ -26,7 +43,15 @@
43 <template #footerMain>
44 <div class="flex flex-wrap items-center gap-2">
45 <Badge v-if="vulnerability.customer_code" class="text-xs">
29 - <template #value>{{ vulnerability.customer_code }}</template>
46 + <template #value>
47 + <code
48 + class="text-primary cursor-pointer"
49 + @click.stop="gotoCustomer({ code: vulnerability.customer_code })"
50 + >
51 + customer #{{ vulnerability.customer_code }}
52 + <Icon :name="LinkIcon" :size="13" class="relative top-0.5" />
53 + </code>
54 + </template>
55 </Badge>
56
57 <Badge v-if="vulnerability.base_score" color="primary" type="splitted" class="text-xs">
@@ -43,15 +68,11 @@
68 <template #label>EPSS Pct</template>
69 <template #value>{{ parseFloat(vulnerability.epss_percentile).toFixed(1) }}%</template>
70 </Badge>
46 -
47 - <Badge v-if="vulnerability.package_architecture" size="small" class="text-xs">
48 - <template #value>{{ vulnerability.package_architecture }}</template>
49 - </Badge>
71 </div>
72 </template>
73 <template #footerExtra>
53 - <div class="text-xs opacity-60">
54 - {{ formatDate(vulnerability.detected_at) }}
74 + <div class="text-tertiary text-xs">
75 + {{ formatDate(vulnerability.detected_at, dFormats.datetime) }}
76 </div>
77 </template>
78 </CardEntity>
@@ -60,12 +81,13 @@
81 <n-modal
82 v-model:show="showDetails"
83 preset="card"
63 - :style="{ maxWidth: 'min(800px, 90vw)', minHeight: 'min(600px, 90vh)' }"
84 + :style="{ maxWidth: 'min(800px, 90vw)', minHeight: 'min(700px, 90vh)' }"
85 :title="`Vulnerability: ${vulnerability.cve_id}`"
86 :bordered="false"
87 + content-class="!p-0"
88 segmented
89 >
68 - <VulnerabilityCardContent :vulnerability="vulnerability" />
90 + <VulnerabilityCardContent :vulnerability />
91 </n-modal>
92 </div>
93 </template>
@@ -77,61 +99,31 @@ import { ref } from "vue"
99 import Badge from "@/components/common/Badge.vue"
100 import CardEntity from "@/components/common/cards/CardEntity.vue"
101 import Icon from "@/components/common/Icon.vue"
102 +import { useGoto } from "@/composables/useGoto"
103 +import { useSettingsStore } from "@/stores/settings"
104 import { VulnerabilitySeverity } from "@/types/vulnerabilities.d"
105 +import { formatDate } from "@/utils"
106 import VulnerabilityCardContent from "./VulnerabilityCardContent.vue"
107 +import VulnerabilitySeverityBadge from "./VulnerabilitySeverityBadge.vue"
108
109 const { vulnerability } = defineProps<{ vulnerability: VulnerabilitySearchItem; embedded?: boolean }>()
110
111 +const { gotoCustomer } = useGoto()
112 +const dFormats = useSettingsStore().dateFormat
113 +
114 const showDetails = ref(false)
115 const HostIcon = "carbon:bare-metal-server"
116 +const LinkIcon = "carbon:launch"
117 const PackageIcon = "carbon:package"
88 -
89 -function getSeverityIcon(severity: string): string {
90 - const iconMap: Record<string, string> = {
91 - [VulnerabilitySeverity.Critical]: "carbon:warning-filled",
92 - [VulnerabilitySeverity.High]: "carbon:warning",
93 - [VulnerabilitySeverity.Medium]: "carbon:warning-alt",
94 - [VulnerabilitySeverity.Low]: "carbon:information"
95 - }
96 - return iconMap[severity] || "carbon:help"
97 -}
98 -
99 -function getSeverityColor(severity: string): "primary" | "warning" | "success" | "danger" | undefined {
100 - const colorMap: Record<string, "primary" | "warning" | "success" | "danger"> = {
101 - [VulnerabilitySeverity.Critical]: "danger",
102 - [VulnerabilitySeverity.High]: "warning",
103 - [VulnerabilitySeverity.Medium]: "warning",
104 - [VulnerabilitySeverity.Low]: "primary"
105 - }
106 - return colorMap[severity]
107 -}
118 +const ArchitectureIcon = "carbon:chip"
119
120 function getSeverityBorderClass(severity: string): string {
121 const borderMap: Record<string, string> = {
111 - [VulnerabilitySeverity.Critical]: "border-l-4 border-l-red-500 dark:border-l-red-400",
112 - [VulnerabilitySeverity.High]: "border-l-4 border-l-orange-500 dark:border-l-orange-400",
113 - [VulnerabilitySeverity.Medium]: "border-l-4 border-l-yellow-500 dark:border-l-yellow-400",
114 - [VulnerabilitySeverity.Low]: "border-l-4 border-l-blue-500 dark:border-l-blue-400"
122 + [VulnerabilitySeverity.Critical]: "ring-1 ring-error/30 hover:ring-error/80",
123 + [VulnerabilitySeverity.High]: "ring-1 ring-orange-500/30 hover:ring-orange-500/80",
124 + [VulnerabilitySeverity.Medium]: "ring-1 ring-warning/30 hover:ring-warning/80",
125 + [VulnerabilitySeverity.Low]: "ring-1 ring-info/30 hover:ring-info/80"
126 }
127 return borderMap[severity] || ""
128 }
118 -
119 -function formatDate(dateString: string): string {
120 - return new Date(dateString).toLocaleDateString()
121 -}
129 </script>
123 -
124 -<style scoped>
125 -.vulnerability-card {
126 - min-height: 280px;
127 -}
128 -
129 -.line-clamp-3 {
130 - display: -webkit-box;
131 - -webkit-line-clamp: 3;
132 - line-clamp: 3;
133 - -webkit-box-orient: vertical;
134 - overflow: hidden;
135 - text-overflow: ellipsis;
136 -}
137 -</style>
frontend/src/components/vulnerabilities/VulnerabilityCardContent.vue
+120 -145
@@ -1,175 +1,151 @@
1 <template>
2 - <div class="vulnerability-details">
3 - <n-scrollbar class="pr-2">
4 - <div class="flex flex-col gap-6">
5 - <!-- Basic Information -->
6 - <div class="section">
7 - <h3 class="section-title">Basic Information</h3>
8 - <div class="grid grid-cols-1 md:grid-cols-2 gap-4">
9 - <div class="detail-item">
10 - <label>CVE ID</label>
11 - <div class="value font-mono">{{ vulnerability.cve_id }}</div>
12 - </div>
13 - <div class="detail-item">
14 - <label>Severity</label>
15 - <Badge :color="getSeverityColor(vulnerability.severity)">
16 - <template #iconLeft><Icon :name="getSeverityIcon(vulnerability.severity)" :size="14" /></template>
17 - <template #value>{{ vulnerability.severity }}</template>
18 - </Badge>
19 - </div>
20 - <div class="detail-item">
21 - <label>Agent</label>
22 - <div class="value">{{ vulnerability.agent_name }}</div>
23 - </div>
24 - <div v-if="vulnerability.customer_code" class="detail-item">
25 - <label>Customer Code</label>
26 - <div class="value">{{ vulnerability.customer_code }}</div>
27 - </div>
28 - </div>
29 - </div>
30 -
31 - <!-- Description -->
32 - <div class="section">
33 - <h3 class="section-title">Description</h3>
34 - <div class="value">{{ vulnerability.title }}</div>
35 - </div>
36 -
37 - <!-- Package Information -->
38 - <div v-if="vulnerability.package_name" class="section">
39 - <h3 class="section-title">Package Information</h3>
40 - <div class="grid grid-cols-1 md:grid-cols-2 gap-4">
41 - <div class="detail-item">
42 - <label>Package Name</label>
43 - <div class="value font-mono">{{ vulnerability.package_name }}</div>
44 - </div>
45 - <div v-if="vulnerability.package_version" class="detail-item">
46 - <label>Version</label>
47 - <div class="value font-mono">{{ vulnerability.package_version }}</div>
48 - </div>
49 - <div v-if="vulnerability.package_architecture" class="detail-item">
50 - <label>Architecture</label>
51 - <div class="value">{{ vulnerability.package_architecture }}</div>
52 - </div>
53 - </div>
2 + <n-tabs type="line" animated :tabs-padding="24">
3 + <n-tab-pane name="Overview" tab="Overview" display-directive="show">
4 + <div class="flex flex-col gap-6 p-7 pt-2">
5 + <div class="grid-auto-fit-200 grid gap-2">
6 + <CardKV>
7 + <template #key>CVE ID</template>
8 + <template #value>
9 + {{ vulnerability.cve_id }}
10 + </template>
11 + </CardKV>
12 + <CardKV>
13 + <template #key>Severity</template>
14 + <template #value>
15 + <VulnerabilitySeverityBadge :severity="vulnerability.severity" />
16 + </template>
17 + </CardKV>
18 + <CardKV>
19 + <template #key>Title</template>
20 + <template #value>
21 + {{ vulnerability.title }}
22 + </template>
23 + </CardKV>
24 + <CardKV>
25 + <template #key>Agent</template>
26 + <template #value>
27 + {{ vulnerability.agent_name }}
28 + </template>
29 + </CardKV>
30 + <CardKV v-if="vulnerability.customer_code">
31 + <template #key>Customer</template>
32 + <template #value>
33 + <code
34 + class="text-primary cursor-pointer"
35 + @click.stop="gotoCustomer({ code: vulnerability.customer_code })"
36 + >
37 + #{{ vulnerability.customer_code }}
38 + <Icon :name="LinkIcon" :size="13" class="relative top-0.5" />
39 + </code>
40 + </template>
41 + </CardKV>
42 </div>
43
56 - <!-- Scoring Information -->
57 - <div class="section">
58 - <h3 class="section-title">Scoring</h3>
59 - <div class="grid grid-cols-1 md:grid-cols-2 gap-4">
60 - <div v-if="vulnerability.base_score" class="detail-item">
61 - <label>CVSS Base Score</label>
62 - <Badge color="primary" type="splitted">
63 - <template #label>Score</template>
64 - <template #value>{{ vulnerability.base_score }}</template>
65 - </Badge>
66 - </div>
67 - <div v-if="vulnerability.epss_score" class="detail-item">
68 - <label>EPSS Score</label>
69 - <Badge color="warning" type="splitted">
70 - <template #label>Score</template>
71 - <template #value>{{ parseFloat(vulnerability.epss_score).toFixed(3) }}</template>
72 - </Badge>
73 - </div>
74 - <div v-if="vulnerability.epss_percentile" class="detail-item">
75 - <label>EPSS Percentile</label>
76 - <Badge color="warning" type="splitted">
77 - <template #label>Percentile</template>
78 - <template #value>{{ parseFloat(vulnerability.epss_percentile).toFixed(1) }}%</template>
79 - </Badge>
80 - </div>
44 + <n-card
45 + v-if="vulnerability.package_name"
46 + class="bg-secondary! overflow-hidden"
47 + title="Package Information"
48 + >
49 + <div class="flex flex-wrap justify-between gap-8">
50 + <n-statistic label="Package Name" :value="vulnerability.package_name" tabular-nums />
51 + <n-statistic label="Version" :value="vulnerability.package_version || '-'" tabular-nums />
52 + <n-statistic
53 + label="Architecture"
54 + :value="vulnerability.package_architecture || '-'"
55 + tabular-nums
56 + />
57 </div>
82 - </div>
83 -
84 - <!-- Timeline -->
85 - <div class="section">
86 - <h3 class="section-title">Timeline</h3>
87 - <div class="grid grid-cols-1 md:grid-cols-2 gap-4">
88 - <div class="detail-item">
89 - <label>Detected At</label>
90 - <div class="value">{{ formatDateTime(vulnerability.detected_at) }}</div>
91 - </div>
92 - <div v-if="vulnerability.published_at" class="detail-item">
93 - <label>Published At</label>
94 - <div class="value">{{ formatDateTime(vulnerability.published_at) }}</div>
95 - </div>
96 - </div>
97 - </div>
98 -
99 - <!-- References -->
100 - <div class="section">
101 - <h3 class="section-title">References</h3>
102 - <div class="value">
103 - <div v-if="vulnerability.references && parseReferences(vulnerability.references).length > 0">
104 - <div v-for="(ref, index) in parseReferences(vulnerability.references)" :key="index" class="mb-2">
105 - <a :href="ref" target="_blank" rel="noopener noreferrer" class="reference-link">
106 - {{ ref }}
107 - </a>
108 - </div>
109 - </div>
110 - <div v-else class="text-gray-500 dark:text-gray-400 italic">
111 - No references available
112 - </div>
58 + </n-card>
59 +
60 + <n-card class="bg-secondary! overflow-hidden" title="Scoring">
61 + <div class="flex flex-wrap justify-between gap-8">
62 + <n-statistic label="CVSS Base Score" :value="`${vulnerability.base_score}`" tabular-nums />
63 + <n-statistic
64 + label="EPSS Score"
65 + :value="parseFloat(vulnerability.epss_score || '0').toFixed(3)"
66 + tabular-nums
67 + />
68 + <n-statistic
69 + label="EPSS Percentile"
70 + :value="`${parseFloat(vulnerability.epss_percentile || '0').toFixed(1)}%`"
71 + tabular-nums
72 + />
73 </div>
114 - </div>
74 + </n-card>
75 </div>
116 - </n-scrollbar>
117 - </div>
76 + </n-tab-pane>
77 + <n-tab-pane name="Timeline" tab="Timeline" display-directive="show:lazy">
78 + <div class="p-7 pt-4">
79 + <n-timeline>
80 + <n-timeline-item
81 + v-if="vulnerability.published_at"
82 + type="success"
83 + title="Published at"
84 + :time="formatDateTime(vulnerability.published_at)"
85 + />
86 + <n-timeline-item
87 + v-if="vulnerability.detected_at"
88 + title="Detected at"
89 + :time="formatDateTime(vulnerability.detected_at)"
90 + line-type="dashed"
91 + />
92 + </n-timeline>
93 + </div>
94 + </n-tab-pane>
95 + <n-tab-pane name="References" tab="References" display-directive="show">
96 + <div class="p-7 pt-2">
97 + <ul v-if="vulnerability.references && parseReferences(vulnerability.references).length">
98 + <li v-for="refItem in parseReferences(vulnerability.references)" :key="refItem">
99 + <a :href="refItem" target="_blank">{{ refItem }}</a>
100 + </li>
101 + </ul>
102 + <n-empty v-else description="No references available" class="h-48 justify-center" />
103 + </div>
104 + </n-tab-pane>
105 + </n-tabs>
106 </template>
107
108 <script setup lang="ts">
109 import type { VulnerabilitySearchItem } from "@/types/vulnerabilities.d"
122 -import { NScrollbar } from "naive-ui"
123 -import Badge from "@/components/common/Badge.vue"
110 +import { NCard, NEmpty, NStatistic, NTabPane, NTabs, NTimeline, NTimelineItem } from "naive-ui"
111 +import CardKV from "@/components/common/cards/CardKV.vue"
112 import Icon from "@/components/common/Icon.vue"
125 -import { VulnerabilitySeverity } from "@/types/vulnerabilities.d"
113 +import { useGoto } from "@/composables/useGoto"
114 +import { useSettingsStore } from "@/stores/settings"
115 +import { formatDate } from "@/utils"
116 +import VulnerabilitySeverityBadge from "./VulnerabilitySeverityBadge.vue"
117
118 const { vulnerability } = defineProps<{ vulnerability: VulnerabilitySearchItem }>()
119
129 -function getSeverityIcon(severity: string): string {
130 - const iconMap: Record<string, string> = {
131 - [VulnerabilitySeverity.Critical]: "carbon:warning-filled",
132 - [VulnerabilitySeverity.High]: "carbon:warning",
133 - [VulnerabilitySeverity.Medium]: "carbon:warning-alt",
134 - [VulnerabilitySeverity.Low]: "carbon:information"
135 - }
136 - return iconMap[severity] || "carbon:help"
137 -}
120 +const dFormats = useSettingsStore().dateFormat
121
139 -function getSeverityColor(severity: string): "primary" | "warning" | "success" | "danger" | undefined {
140 - const colorMap: Record<string, "primary" | "warning" | "success" | "danger"> = {
141 - [VulnerabilitySeverity.Critical]: "danger",
142 - [VulnerabilitySeverity.High]: "warning",
143 - [VulnerabilitySeverity.Medium]: "warning",
144 - [VulnerabilitySeverity.Low]: "primary"
145 - }
146 - return colorMap[severity]
147 -}
122 +const { gotoCustomer } = useGoto()
123
149 -function formatDateTime(dateString: string): string {
150 - return new Date(dateString).toLocaleString()
151 -}
124 +const LinkIcon = "carbon:launch"
125
126 function parseReferences(references: string): string[] {
154 - if (!references || references.trim() === '') return []
127 + if (!references || references.trim() === "") return []
128
129 // Try to handle different formats:
130 // 1. JSON array string
131 try {
132 const parsed = JSON.parse(references)
133 if (Array.isArray(parsed)) {
161 - return parsed.filter(ref => ref && typeof ref === 'string' && ref.trim().length > 0)
134 + return parsed.filter(ref => ref && typeof ref === "string" && ref.trim().length > 0)
135 }
136 } catch {
137 // Not JSON, continue with other parsing methods
138 }
139
140 // 2. Comma, semicolon, or newline separated
168 - let refs = references.split(/[,;\n|]/).map(ref => ref.trim()).filter(ref => ref.length > 0)
141 + let refs = references
142 + .split(/[,;\n|]/)
143 + .map(ref => ref.trim())
144 + .filter(ref => ref.length > 0)
145
146 // 3. Space separated URLs (if they start with http)
171 - if (refs.length === 1 && refs[0].includes('http')) {
172 - const spaceRefs = refs[0].split(/\s+/).filter(ref => ref.startsWith('http'))
147 + if (refs.length === 1 && refs[0].includes("http")) {
148 + const spaceRefs = refs[0].split(/\s+/).filter(ref => ref.startsWith("http"))
149 if (spaceRefs.length > 1) {
150 refs = spaceRefs
151 }
@@ -177,14 +153,13 @@ function parseReferences(references: string): string[] {
153
154 return refs
155 }
180 -</script>
156
182 -<style scoped>
183 -.vulnerability-details {
184 - max-height: 75vh;
185 - overflow-y: auto;
157 +function formatDateTime(timestamp: number | Date | string): string {
158 + return formatDate(timestamp, dFormats.datetimesec).toString()
159 }
160 +</script>
161
162 +<style scoped>
163 .section {
164 border-bottom: 1px solid rgb(229 231 235);
165 padding-bottom: 1rem;
frontend/src/components/vulnerabilities/VulnerabilitySeverityBadge.vue new
+27
@@ -0,0 +1,27 @@
1 +<template>
2 + <Badge :color="getSeverityColor(severity)" type="splitted" bright>
3 + <template #iconLeft>
4 + <VulnerabilitySeverityIcon :severity :size="14" />
5 + </template>
6 + <template #value>{{ severity }}</template>
7 + </Badge>
8 +</template>
9 +
10 +<script setup lang="ts">
11 +import type { BadgeColor } from "@/components/common/Badge.vue"
12 +import Badge from "@/components/common/Badge.vue"
13 +import { VulnerabilitySeverity } from "@/types/vulnerabilities.d"
14 +import VulnerabilitySeverityIcon from "./VulnerabilitySeverityIcon.vue"
15 +
16 +const { severity } = defineProps<{ severity: VulnerabilitySeverity }>()
17 +
18 +function getSeverityColor(severity: string): BadgeColor | undefined {
19 + const colorMap: Record<string, BadgeColor | undefined> = {
20 + [VulnerabilitySeverity.Critical]: "danger",
21 + [VulnerabilitySeverity.High]: "warning",
22 + [VulnerabilitySeverity.Medium]: "warning",
23 + [VulnerabilitySeverity.Low]: undefined
24 + }
25 + return colorMap[severity]
26 +}
27 +</script>
frontend/src/components/vulnerabilities/VulnerabilitySeverityIcon.vue new
+20
@@ -0,0 +1,20 @@
1 +<template>
2 + <Icon :name="getSeverityIcon(severity)" :size />
3 +</template>
4 +
5 +<script setup lang="ts">
6 +import Icon from "@/components/common/Icon.vue"
7 +import { VulnerabilitySeverity } from "@/types/vulnerabilities.d"
8 +
9 +const { severity, size } = defineProps<{ severity: VulnerabilitySeverity; size?: number }>()
10 +
11 +function getSeverityIcon(severity: string): string {
12 + const iconMap: Record<string, string> = {
13 + [VulnerabilitySeverity.Critical]: "carbon:warning-hex-filled",
14 + [VulnerabilitySeverity.High]: "carbon:warning-alt",
15 + [VulnerabilitySeverity.Medium]: "carbon:warning",
16 + [VulnerabilitySeverity.Low]: "carbon:information"
17 + }
18 + return iconMap[severity] || "carbon:help"
19 +}
20 +</script>
frontend/src/components/vulnerabilities/VulnerabilityStats.vue new
+395
@@ -0,0 +1,395 @@
1 +<template>
2 + <n-spin :show="loading" content-class="flex flex-col gap-4">
3 + <!-- Statistics Cards -->
4 + <div>
5 + <div class="mb-5 flex flex-wrap items-center justify-between gap-3">
6 + <h3 class="flex items-center gap-2 text-lg font-semibold text-gray-900 dark:text-gray-100">
7 + <Icon name="carbon:chart-ring" :size="20" class="text-primary" />
8 + Distribution
9 + </h3>
10 +
11 + <p class="flex items-center gap-2 text-sm">
12 + Total:
13 + <code>{{ totalCount.toLocaleString() }}</code>
14 + <Icon :name="InfoIcon" :size="14" />
15 + </p>
16 + </div>
17 +
18 + <div class="grid grid-cols-1 gap-4 sm:grid-cols-2 xl:grid-cols-4">
19 + <n-card
20 + v-for="item of statisticsCards"
21 + :key="item.severity"
22 + class="ring-primary cursor-pointer transition-all duration-300 hover:ring-1"
23 + content-class="flex flex-col gap-2"
24 + size="small"
25 + @click="selectSeverity(item.severity)"
26 + >
27 + <div class="flex items-center justify-between gap-2 whitespace-nowrap">
28 + <div class="flex items-center gap-2">
29 + <VulnerabilitySeverityIcon :severity="item.severity" :size="24" :class="item.iconClass" />
30 + <span class="text-xl">{{ item.label }}</span>
31 + </div>
32 + <div class="font-mono text-xl font-bold">{{ stats[item.key].toLocaleString() }}</div>
33 + </div>
34 + <n-progress
35 + type="line"
36 + indicator-placement="inside"
37 + :percentage="getPercentage(stats[item.key])"
38 + :color="item.barColor"
39 + class="custom-progress"
40 + />
41 + </n-card>
42 + </div>
43 + </div>
44 +
45 + <!-- Coverage Cards -->
46 + <div class="mt-8">
47 + <div class="mb-5 flex flex-wrap items-center justify-between gap-3">
48 + <h3 class="flex items-center gap-2 text-lg font-semibold text-gray-900 dark:text-gray-100">
49 + <Icon name="carbon:double-axis-chart-column" :size="20" class="text-primary" />
50 + Coverage
51 + </h3>
52 +
53 + <p class="flex items-center gap-2 text-sm">
54 + Coverage based on the 100 vulnerabilities with the highest EPSS score.
55 + <Icon :name="InfoIcon" :size="14" />
56 + </p>
57 + </div>
58 +
59 + <n-card class="bg-secondary! overflow-hidden">
60 + <div class="flex flex-wrap justify-between gap-8">
61 + <n-statistic label="Affected Agents" :value="stats.uniqueAgents.toLocaleString()" tabular-nums />
62 + <n-statistic label="Unique Packages" :value="stats.uniquePackages.toLocaleString()" tabular-nums />
63 + <n-statistic label="Customer Codes" :value="stats.uniqueCustomers.toLocaleString()" tabular-nums />
64 + </div>
65 + </n-card>
66 + </div>
67 +
68 + <!-- Top 5 Packages by EPSS Score -->
69 + <div v-if="topEpssPackages.length > 0" class="mt-8">
70 + <div class="mb-5 flex flex-wrap items-center justify-between gap-3">
71 + <h3 class="flex items-center gap-2 text-lg font-semibold">
72 + <Icon :name="PackageIcon" :size="20" class="text-primary" />
73 + Top 5 Packages by EPSS Score
74 + </h3>
75 +
76 + <p class="flex items-center gap-2 text-sm">
77 + Ranking based on the 100 vulnerabilities with the highest EPSS score.
78 + <Icon :name="InfoIcon" :size="14" />
79 + </p>
80 + </div>
81 + <div class="grid-auto-fit-250 grid gap-10">
82 + <div
83 + v-for="(pkg, index) in topEpssPackages"
84 + :key="pkg.package_name"
85 + class="@container flex items-stretch gap-2"
86 + >
87 + <div class="flex items-end">
88 + <div
89 + class="w-4 rounded-b-sm rounded-t-2xl"
90 + :class="{
91 + 'h-full bg-yellow-500': index === 0,
92 + 'h-8/12 bg-gray-400': index === 1,
93 + 'h-4/12 bg-amber-600': index === 2
94 + }"
95 + ></div>
96 + </div>
97 + <div
98 + class="bg-secondary ring-primary flex grow cursor-pointer flex-col gap-2 rounded-md px-3 py-2 transition-all duration-300 hover:ring-1"
99 + @click="selectPackage(pkg.package_name)"
100 + >
101 + <div class="flex items-center gap-2 text-xl">
102 + <Icon
103 + :name="index < 3 ? 'carbon:trophy' : 'carbon:warning-alt'"
104 + :size="20"
105 + :class="
106 + index === 0
107 + ? 'text-yellow-500'
108 + : index === 1
109 + ? 'text-gray-400'
110 + : index === 2
111 + ? 'text-amber-600'
112 + : 'text-info'
113 + "
114 + />
115 + <span>#{{ index + 1 }}</span>
116 + </div>
117 +
118 + <div class="text-lg font-semibold leading-snug">{{ pkg.package_name }}</div>
119 +
120 + <div class="@md:grid-cols-2 text-secondary grid grid-cols-1 gap-1 break-all text-xs">
121 + <div class="flex items-center gap-2">
122 + <span>EPSS:</span>
123 + <span class="font-mono font-semibold">{{ pkg.maxEpssScore.toLocaleString() }}</span>
124 + </div>
125 + <div class="flex items-center gap-2">
126 + <span>Vulnerabilities:</span>
127 + <span class="font-mono font-semibold">{{ pkg.vulnCount.toLocaleString() }}</span>
128 + </div>
129 + <div class="flex items-center gap-2">
130 + <span>Affected Agents:</span>
131 + <span class="font-mono font-semibold">
132 + {{ pkg.affectedAgents.toLocaleString() }}
133 + </span>
134 + </div>
135 + <div class="flex items-center gap-2">
136 + <span>Max CVSS:</span>
137 + <span class="font-mono font-semibold">
138 + {{ pkg.maxCvssScore?.toFixed(1) || "N/A" }}
139 + </span>
140 + </div>
141 + <div class="flex items-center gap-2">
142 + <span>Critical count:</span>
143 + <span class="font-mono font-semibold">{{ pkg.criticalCount }}</span>
144 + </div>
145 + <div class="flex items-center gap-2">
146 + <span>High count:</span>
147 + <span class="font-mono font-semibold">{{ pkg.highCount }}</span>
148 + </div>
149 + </div>
150 + </div>
151 + </div>
152 + </div>
153 + </div>
154 + </n-spin>
155 +</template>
156 +
157 +<script setup lang="ts">
158 +import type { VulnerabilitiesListFilter } from "./types.d"
159 +import type { VulnerabilitySearchItem, VulnerabilitySearchQuery } from "@/types/vulnerabilities.d"
160 +import { watchDebounced } from "@vueuse/core"
161 +import axios from "axios"
162 +import _toNumber from "lodash/toNumber"
163 +import { NCard, NProgress, NSpin, NStatistic, useMessage } from "naive-ui"
164 +import { computed, ref, toRefs } from "vue"
165 +import Api from "@/api"
166 +import Icon from "@/components/common/Icon.vue"
167 +import { VulnerabilitySeverity } from "@/types/vulnerabilities.d"
168 +import VulnerabilitySeverityIcon from "./VulnerabilitySeverityIcon.vue"
169 +
170 +const props = defineProps<{ filters: VulnerabilitiesListFilter[] }>()
171 +
172 +const emit = defineEmits<{
173 + (e: "update:severity", value: VulnerabilitySeverity): void
174 + (e: "update:package", value: string): void
175 +}>()
176 +
177 +const { filters } = toRefs(props)
178 +
179 +const message = useMessage()
180 +const loading = ref(false)
181 +const list = ref<VulnerabilitySearchItem[]>([])
182 +
183 +// Severity counts from API response
184 +const totalCount = ref(0)
185 +const criticalCount = ref(0)
186 +const highCount = ref(0)
187 +const mediumCount = ref(0)
188 +const lowCount = ref(0)
189 +
190 +const PackageIcon = "carbon:package"
191 +const InfoIcon = "carbon:information"
192 +
193 +// Define the allowed keys for statistics
194 +type SeverityKey = "critical" | "high" | "medium" | "low"
195 +
196 +const statisticsCards: {
197 + label: string
198 + severity: VulnerabilitySeverity
199 + iconClass: string
200 + barColor: string
201 + key: SeverityKey
202 +}[] = [
203 + {
204 + label: "Critical",
205 + severity: VulnerabilitySeverity.Critical,
206 + iconClass: "text-error",
207 + barColor: "var(--error-color)",
208 + key: "critical"
209 + },
210 + {
211 + label: "High",
212 + severity: VulnerabilitySeverity.High,
213 + iconClass: "text-orange-500",
214 + barColor: "var(--color-orange-500)",
215 + key: "high"
216 + },
217 + {
218 + label: "Medium",
219 + severity: VulnerabilitySeverity.Medium,
220 + iconClass: "text-warning",
221 + barColor: "var(--warning-color)",
222 + key: "medium"
223 + },
224 + {
225 + label: "Low",
226 + severity: VulnerabilitySeverity.Low,
227 + iconClass: "text-info",
228 + barColor: "var(--info-color)",
229 + key: "low"
230 + }
231 +]
232 +
233 +// Calculate statistics from current data
234 +const stats = computed((): Record<SeverityKey | "uniqueAgents" | "uniquePackages" | "uniqueCustomers", number> => {
235 + // Use API response counts for global statistics across all pages
236 + const critical = criticalCount.value
237 + const high = highCount.value
238 + const medium = mediumCount.value
239 + const low = lowCount.value
240 +
241 + // Calculate unique values from current page data for context
242 + const uniqueAgents = new Set(list.value.map(v => v.agent_name)).size
243 + const uniquePackages = new Set(list.value.map(v => v.package_name).filter(Boolean)).size
244 + const uniqueCustomers = new Set(list.value.map(v => v.customer_code).filter(Boolean)).size
245 +
246 + return {
247 + critical,
248 + high,
249 + medium,
250 + low,
251 + uniqueAgents,
252 + uniquePackages,
253 + uniqueCustomers
254 + }
255 +})
256 +
257 +// Calculate top packages by EPSS score
258 +const topEpssPackages = computed(() => {
259 + // Group vulnerabilities by package name
260 + const packageMap = new Map<
261 + string,
262 + {
263 + package_name: string
264 + vulnCount: number
265 + maxEpssScore: number
266 + maxCvssScore: number | null
267 + affectedAgents: Set<string>
268 + criticalCount: number
269 + highCount: number
270 + }
271 + >()
272 +
273 + list.value.forEach(vuln => {
274 + if (!vuln.package_name || !vuln.epss_score) return
275 +
276 + const epssScore = Number.parseFloat(vuln.epss_score)
277 + if (Number.isNaN(epssScore)) return
278 +
279 + const key = vuln.package_name
280 + const existing = packageMap.get(key)
281 +
282 + if (existing) {
283 + existing.vulnCount++
284 + existing.maxEpssScore = Math.max(existing.maxEpssScore, epssScore)
285 + if (vuln.base_score) {
286 + existing.maxCvssScore = Math.max(existing.maxCvssScore || 0, vuln.base_score)
287 + }
288 + existing.affectedAgents.add(vuln.agent_name)
289 +
290 + if (vuln.severity === VulnerabilitySeverity.Critical) existing.criticalCount++
291 + if (vuln.severity === VulnerabilitySeverity.High) existing.highCount++
292 + } else {
293 + packageMap.set(key, {
294 + package_name: vuln.package_name,
295 + vulnCount: 1,
296 + maxEpssScore: epssScore,
297 + maxCvssScore: vuln.base_score || null,
298 + affectedAgents: new Set([vuln.agent_name]),
299 + criticalCount: vuln.severity === VulnerabilitySeverity.Critical ? 1 : 0,
300 + highCount: vuln.severity === VulnerabilitySeverity.High ? 1 : 0
301 + })
302 + }
303 + })
304 +
305 + // Convert to array and sort by max EPSS score
306 + return Array.from(packageMap.values())
307 + .map(pkg => ({
308 + ...pkg,
309 + affectedAgents: pkg.affectedAgents.size
310 + }))
311 + .sort((a, b) => b.maxEpssScore - a.maxEpssScore)
312 + .slice(0, 5)
313 +})
314 +
315 +function getPercentage(count: number): number {
316 + if (totalCount.value === 0) return 0
317 + return _toNumber(((count / totalCount.value) * 100).toFixed(1))
318 +}
319 +
320 +let abortController: AbortController | null = null
321 +
322 +function getList() {
323 + abortController?.abort()
324 + abortController = new AbortController()
325 +
326 + loading.value = true
327 +
328 + const query: VulnerabilitySearchQuery = {
329 + page: 1,
330 + page_size: 100,
331 + customer_code: filters.value.find(o => o.type === "customer_code")?.value || undefined,
332 + severity: (filters.value.find(o => o.type === "severity")?.value as VulnerabilitySeverity) || undefined,
333 + cve_id: filters.value.find(o => o.type === "cve_id")?.value || undefined,
334 + agent_name: filters.value.find(o => o.type === "agent_name")?.value || undefined,
335 + package_name: filters.value.find(o => o.type === "package_name")?.value || undefined,
336 + include_epss: true
337 + }
338 +
339 + Api.vulnerabilities
340 + .searchVulnerabilities(query, abortController.signal)
341 + .then(res => {
342 + loading.value = false
343 +
344 + if (res.data.success) {
345 + list.value = res.data?.vulnerabilities || []
346 +
347 + // Store severity counts from API response
348 + totalCount.value = res.data?.total_count || 0
349 + criticalCount.value = res.data?.critical_count || 0
350 + highCount.value = res.data?.high_count || 0
351 + mediumCount.value = res.data?.medium_count || 0
352 + lowCount.value = res.data?.low_count || 0
353 + } else {
354 + message.warning(res.data?.message || "An error occurred. Please try again later.")
355 + }
356 + })
357 + .catch(err => {
358 + if (!axios.isCancel(err)) {
359 + message.error(err.response?.data?.message || "An error occurred. Please try again later.")
360 + loading.value = false
361 + }
362 + })
363 +}
364 +
365 +function selectSeverity(value: VulnerabilitySeverity) {
366 + emit("update:severity", value)
367 +}
368 +
369 +function selectPackage(value: string) {
370 + emit("update:package", value)
371 +}
372 +
373 +watchDebounced(
374 + filters,
375 + () => {
376 + getList()
377 + },
378 + { deep: true, debounce: 300, immediate: true }
379 +)
380 +</script>
381 +
382 +<style lang="scss" scoped>
383 +.custom-progress {
384 + :deep() {
385 + .n-progress-graph-line-indicator {
386 + text-shadow:
387 + 0px 0px 1px black,
388 + 0px 0px 2px black,
389 + 0px 0px 3px black;
390 + font-weight: bold;
391 + color: white !important;
392 + }
393 + }
394 +}
395 +</style>
frontend/src/components/vulnerabilities/types.d.ts new
+8
@@ -0,0 +1,8 @@
1 +import type { VulnerabilitySearchQuery } from "@/types/vulnerabilities.d"
2 +
3 +export type VulnerabilitiesFilterTypes = keyof Omit<VulnerabilitySearchQuery, "page" | "page_size" | "include_epss">
4 +
5 +export interface VulnerabilitiesListFilter {
6 + type: VulnerabilitiesFilterTypes
7 + value: string | null
8 +}
frontend/src/types/common.d.ts
+1 -1
@@ -3,7 +3,7 @@ import type { FlaskBaseResponse } from "./flask"
3
4 export type OsTypesFull = "Unknown" | "Windows" | "MacOS" | "UNIX" | "Linux"
5 export type OsTypesLower = "linux" | "windows" | "macos"
6 -export type SafeAny = string | number | object
6 +export type SafeAny = string | number | boolean | object
7 export type ApiError = AxiosError<FlaskBaseResponse>
8 export type ApiCommonResponse<T = unknown> = AxiosResponse<FlaskBaseResponse & T>
9 export type DeepNullable<T> = {
frontend/src/types/copilotAction.d.ts
+40 -66
@@ -1,39 +1,5 @@
1 -export enum Technology {
2 - WAZUH = "Wazuh",
3 - LINUX = "Linux",
4 - WINDOWS = "Windows",
5 - MACOS = "macOS",
6 - NETWORK = "Network",
7 - CLOUD = "Cloud",
8 - VELOCIRAPTOR = "Velociraptor"
9 -}
10 -
11 -export interface ScriptParameter {
12 - name: string
13 - type: string
14 - required: boolean
15 - description?: string
16 - default?: string | number | boolean | Array<any> | Record<string, any>
17 - enum?: string[]
18 - arg_position?: string
19 -}
20 -
21 -export interface ActiveResponseItem {
22 - copilot_action_name: string
23 - description: string
24 - technology: Technology
25 - icon?: string
26 - script_parameters: ScriptParameter[]
27 - repo_url: string
28 - script_name?: string
29 - version?: string
30 - last_updated?: Date
31 - category?: string
32 - tags?: string[]
33 -}
34 -
1 export interface InventoryQueryRequest {
36 - technology?: Technology
2 + technology?: string
3 category?: string
4 tag?: string
5 q?: string
@@ -43,46 +9,54 @@ export interface InventoryQueryRequest {
9 include?: string
10 }
11
46 -export interface InventoryResponse {
47 - copilot_actions: ActiveResponseItem[]
48 - message: string
49 - success: boolean
50 -}
51 -
52 -export interface ActionDetailResponse {
53 - copilot_action: ActiveResponseItem
54 - message: string
55 - success: boolean
12 +export interface InvokeCopilotActionRequest {
13 + copilot_action_name: string
14 + agent_names: string[]
15 + parameters: Record<string, string | number>
16 }
17
58 -export interface InventoryMetricsResponse {
59 - status: string
60 - metrics: Record<string, any>
61 - message: string
62 - success: boolean
18 +export interface CopilotActionListResponse {
19 + copilot_actions: CopilotAction[]
20 + total: number
21 + count: number
22 + limit: number
23 + offset: number
24 + has_more: boolean
25 + next_offset: number | number
26 + prev_offset: null | number
27 }
28
65 -export interface InvokeCopilotActionRequest {
29 +export interface CopilotAction {
30 copilot_action_name: string
67 - agent_names: string[]
68 - parameters: Record<string, any>
31 + description: string
32 + technology: string
33 + icon?: string
34 + script_parameters: ScriptParameter[]
35 + repo_url: string
36 + script_name?: string
37 + version?: string
38 + last_updated?: Date
39 + category?: null | string
40 + tags?: null | string[]
41 }
42
71 -export interface CollectArtifactResponse {
72 - message: string
73 - success: boolean
74 - session_id?: string
75 - flow_id?: string
43 +export interface ScriptParameter {
44 + name: string
45 + type: ScriptParameterType
46 + required: boolean
47 + description?: string
48 + default?: string | number | boolean | null
49 + enum?: string[] | null
50 + arg_position?: string
51 }
52
78 -export interface InvokeCopilotActionResponse {
79 - responses: CollectArtifactResponse[]
80 - message: string
81 - success: boolean
53 +export enum ScriptParameterType {
54 + Boolean = "boolean",
55 + Integer = "integer",
56 + String = "string"
57 }
58
84 -export interface TechnologiesResponse {
85 - technologies: Technology[]
86 - message: string
87 - success: boolean
59 +export interface CopilotActionInvokeResponse {
60 + session_id?: string
61 + flow_id?: string
62 }
frontend/src/types/mitre.d.ts
-2
@@ -110,8 +110,6 @@ export interface MitreGroupDetails {
110 country: null | string
111 }
112
113 -export type MitreTechniquesDetails = any
114 -
113 export interface MitreEventDetails {
114 data_source_ip: string
115 data_host_architecture: string
frontend/src/types/sca.d.ts
+13 -52
@@ -33,7 +33,7 @@ export interface ScaOverviewResponse {
33 has_previous: boolean
34 success: boolean
35 message: string
36 - filters_applied: Record<string, any>
36 + filters_applied: Record<string, string | number | boolean>
37 }
38
39 export interface ScaOverviewQuery {
@@ -55,15 +55,18 @@ export interface ScaStatsResponse {
55 total_passes_all_agents: number
56 total_fails_all_agents: number
57 total_invalid_all_agents: number
58 - by_customer: Record<string, {
59 - total_agents: number
60 - total_policies: number
61 - average_score: number
62 - total_checks: number
63 - total_passes: number
64 - total_fails: number
65 - total_invalid: number
66 - }>
58 + by_customer: Record<
59 + string,
60 + {
61 + total_agents: number
62 + total_policies: number
63 + average_score: number
64 + total_checks: number
65 + total_passes: number
66 + total_fails: number
67 + total_invalid: number
68 + }
69 + >
70 success: boolean
71 message: string
72 }
@@ -75,45 +78,3 @@ export enum ScaComplianceLevel {
78 Poor = "Poor", // 60-69%
79 Critical = "Critical" // <60%
80 }
78 -
79 -export function getComplianceLevel(score: number): ScaComplianceLevel {
80 - if (score >= 90) return ScaComplianceLevel.Excellent
81 - if (score >= 80) return ScaComplianceLevel.Good
82 - if (score >= 70) return ScaComplianceLevel.Average
83 - if (score >= 60) return ScaComplianceLevel.Poor
84 - return ScaComplianceLevel.Critical
85 -}
86 -
87 -export function getComplianceLevelColor(level: ScaComplianceLevel): "primary" | "warning" | "success" | "danger" {
88 - switch (level) {
89 - case ScaComplianceLevel.Excellent:
90 - return "success"
91 - case ScaComplianceLevel.Good:
92 - return "primary"
93 - case ScaComplianceLevel.Average:
94 - return "warning"
95 - case ScaComplianceLevel.Poor:
96 - return "warning"
97 - case ScaComplianceLevel.Critical:
98 - return "danger"
99 - default:
100 - return "primary"
101 - }
102 -}
103 -
104 -export function getComplianceLevelIcon(level: ScaComplianceLevel): string {
105 - switch (level) {
106 - case ScaComplianceLevel.Excellent:
107 - return "carbon:checkmark-filled"
108 - case ScaComplianceLevel.Good:
109 - return "carbon:checkmark"
110 - case ScaComplianceLevel.Average:
111 - return "carbon:warning-alt"
112 - case ScaComplianceLevel.Poor:
113 - return "carbon:warning"
114 - case ScaComplianceLevel.Critical:
115 - return "carbon:warning-filled"
116 - default:
117 - return "carbon:help"
118 - }
119 -}
frontend/src/types/threatIntel.d.ts
+3 -1
@@ -1,3 +1,5 @@
1 +import type { SafeAny } from "./common"
2 +
3 export interface ThreatIntelResponse {
4 comment: string | null
5 ioc_source: string
@@ -17,7 +19,7 @@ export interface StructuredAgentResponse {
19 export interface MCPQueryResponse {
20 message: string
21 success: boolean
20 - result?: any
22 + result?: SafeAny
23 structured_result?: StructuredAgentResponse
24 execution_time?: number
25 }
frontend/src/types/vulnerabilities.d.ts
+1 -1
@@ -29,7 +29,7 @@ export interface VulnerabilitySearchResponse {
29 has_previous: boolean
30 success: boolean
31 message: string
32 - filters_applied: Record<string, any>
32 + filters_applied: Record<string, string | number | boolean>
33 }
34
35 export enum VulnerabilitySeverity {
frontend/src/utils/secure-storage.ts
+2 -2
@@ -23,7 +23,7 @@ export function removePersistentSessionKey() {
23
24 export function secureLocalStorage(options?: { session?: boolean }) {
25 return {
26 - getItem(key: string): any {
26 + getItem(key: string) {
27 try {
28 return secureLS.get(persistentKey({ session: options?.session })(key))
29 } catch (err) {
@@ -32,7 +32,7 @@ export function secureLocalStorage(options?: { session?: boolean }) {
32 }
33 },
34
35 - setItem(key: string, value: any): void {
35 + setItem(key: string, value: string): void {
36 try {
37 secureLS.set(persistentKey({ session: options?.session })(key), value)
38 } catch (err) {
frontend/src/views/agents/Groups.vue
+2 -4
@@ -41,7 +41,7 @@
41 <div
42 v-for="group of groupsList"
43 :key="group.name"
44 - class="hover:text-warning cursor-pointer break-all px-4.5 py-2.5 text-sm"
44 + class="hover:text-warning px-4.5 cursor-pointer break-all py-2.5 text-sm"
45 :class="{ 'bg-warning/10': group.name === currentGroup?.name }"
46 @click.stop="loadGroup(group)"
47 >
@@ -137,9 +137,7 @@
137 </template>
138 <template #main-content>
139 <div v-if="currentGroup && currentFile" class="px-4.5 break-all py-2.5 text-sm">
140 - <div class="font-mono">
141 - Group: {{ currentGroup?.name }} | File: {{ currentFile?.filename }}
142 - </div>
140 + <div class="font-mono">Group: {{ currentGroup?.name }} | File: {{ currentFile?.filename }}</div>
141 </div>
142 <n-spin
143 :show="loadingFile || uploadingConfig"