From ae112081489322e4c14a1353c9b98919d0d6c5ec Mon Sep 17 00:00:00 2001 From: Sebastian Kawelke Date: Mon, 2 Mar 2026 18:48:30 +0100 Subject: [PATCH 1/4] Starts adding a instance admin feature Signed-off-by: Sebastian Kawelke --- package-lock.json | 7 + package.json | 1 + src/app/admin/layout.tsx | 19 ++ src/app/admin/page.tsx | 170 ++++++++++++++++++ src/components/ui/field.tsx | 42 +++++ src/context/InstanceAdminContext.tsx | 62 +++++++ src/services/admin-request-signing.ts | 239 ++++++++++++++++++++++++++ src/services/adminApi.ts | 40 +++++ 8 files changed, 580 insertions(+) create mode 100644 src/app/admin/layout.tsx create mode 100644 src/app/admin/page.tsx create mode 100644 src/components/ui/field.tsx create mode 100644 src/context/InstanceAdminContext.tsx create mode 100644 src/services/admin-request-signing.ts create mode 100644 src/services/adminApi.ts diff --git a/package-lock.json b/package-lock.json index 82cab157..28c1f7ca 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,6 +13,7 @@ "@headlessui/react": "^2.2.9", "@heroicons/react": "^2.2.0", "@hookform/resolvers": "^5.2.2", + "@ltonetwork/http-message-signatures": "^0.1.12", "@mdxeditor/editor": "^3.52.4", "@ory/elements-react": "^1.1.0", "@ory/nextjs": "^1.0.0-rc.1", @@ -2991,6 +2992,12 @@ "@lezer/lr": "^1.4.0" } }, + "node_modules/@ltonetwork/http-message-signatures": { + "version": "0.1.12", + "resolved": "https://registry.npmjs.org/@ltonetwork/http-message-signatures/-/http-message-signatures-0.1.12.tgz", + "integrity": "sha512-bk7BemlROclcdXu5ISTKjPeRstJoqWefh6Bvr1q6XaX+KyKgLuKeCSYOKRyBBJ8P/E+tq9toBUIlMRLvW5J9KQ==", + "license": "ISC" + }, "node_modules/@marijn/find-cluster-break": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/@marijn/find-cluster-break/-/find-cluster-break-1.0.2.tgz", diff --git a/package.json b/package.json index ecb76596..ebcb46a1 100644 --- a/package.json +++ b/package.json @@ -15,6 +15,7 @@ "@headlessui/react": "^2.2.9", "@heroicons/react": "^2.2.0", "@hookform/resolvers": "^5.2.2", + "@ltonetwork/http-message-signatures": "^0.1.12", "@mdxeditor/editor": "^3.52.4", "@ory/elements-react": "^1.1.0", "@ory/nextjs": "^1.0.0-rc.1", diff --git a/src/app/admin/layout.tsx b/src/app/admin/layout.tsx new file mode 100644 index 00000000..f238aab8 --- /dev/null +++ b/src/app/admin/layout.tsx @@ -0,0 +1,19 @@ +// Copyright 2026 L3montree GmbH and the DevGuard Contributors. +// SPDX-License-Identifier: AGPL-3.0-or-later + +import React from "react"; +import RootHeader from "@/components/common/RootHeader"; +import { InstanceAdminProvider } from "@/context/InstanceAdminContext"; + +export default function AdminLayout({ + children, +}: { + children: React.ReactNode; +}) { + return ( + + +
{children}
+
+ ); +} diff --git a/src/app/admin/page.tsx b/src/app/admin/page.tsx new file mode 100644 index 00000000..8903ad38 --- /dev/null +++ b/src/app/admin/page.tsx @@ -0,0 +1,170 @@ +// Copyright 2026 L3montree GmbH and the DevGuard Contributors. +// SPDX-License-Identifier: AGPL-3.0-or-later + +"use client"; + +import { useInstanceAdmin } from "@/context/InstanceAdminContext"; +import { adminBrowserApiClient } from "@/services/adminApi"; +import { useCallback, useState } from "react"; +import { toast } from "sonner"; + +import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert"; +import { Button } from "@/components/ui/button"; +import { + Card, + CardContent, + CardDescription, + CardHeader, + CardTitle, +} from "@/components/ui/card"; +import { + Field, + FieldDescription, + FieldGroup, + FieldLabel, +} from "@/components/ui/field"; +import { Input } from "@/components/ui/input"; +import { ShieldCheckIcon } from "@heroicons/react/24/outline"; + +export default function InstanceAdminPage() { + const { isAuthenticated, authenticate, logout, getPrivateKey } = + useInstanceAdmin(); + const [keyInput, setKeyInput] = useState(""); + const [verifying, setVerifying] = useState(false); + + const handleAuthenticate = useCallback(async () => { + const trimmedKey = keyInput.trim(); + if (!trimmedKey) { + toast.error("Please enter your private key."); + return; + } + + if (!/^[0-9a-fA-F]+$/.test(trimmedKey)) { + toast.error("Invalid key format. The private key must be hex-encoded."); + return; + } + + setVerifying(true); + try { + const resp = await adminBrowserApiClient("/admin", trimmedKey); + + if (resp.ok) { + authenticate(trimmedKey); + setKeyInput(""); + toast.success("Authenticated as instance admin."); + } else if (resp.status === 401 || resp.status === 403) { + toast.error( + "Authentication failed. The private key does not match the instance's admin public key.", + ); + } else { + toast.error(`Unexpected response from server: ${resp.status}`); + } + } catch (err) { + toast.error("Failed to verify key. Is the API reachable?"); + } finally { + setVerifying(false); + } + }, [keyInput, authenticate]); + + const handleLogout = useCallback(() => { + logout(); + toast.success("Admin session ended. Private key removed from session."); + }, [logout]); + + if (!isAuthenticated) { + return ( +
+
+ + + Instance Administration + + Authenticate with your ECDSA P-256 private key to access + instance admin features. + + + +
{ + e.preventDefault(); + handleAuthenticate(); + }} + > + + + + Private Key (hex) + + setKeyInput(e.target.value)} + autoComplete="off" + required + /> + + The key is stored in session storage and cleared when you + close the tab. It is never sent to the server — it signs + requests locally. + + + + + + +
+
+
+
+
+ ); + } + + return ( +
+
+
+

Instance Administration

+

+ Manage this DevGuard instance. +

+
+ +
+ + + + Authenticated + + You are authenticated as an instance admin. All requests from this + page are cryptographically signed with your private key. + + + + + + Instance Overview + + Instance administration features will appear here. + + + +

+ Admin endpoints are being developed. This page will provide tools + for managing organizations, users, and instance-wide settings. +

+
+
+
+ ); +} diff --git a/src/components/ui/field.tsx b/src/components/ui/field.tsx new file mode 100644 index 00000000..633186b2 --- /dev/null +++ b/src/components/ui/field.tsx @@ -0,0 +1,42 @@ +import * as React from "react"; + +import { cn } from "@/lib/utils"; +import { Label } from "@/components/ui/label"; + +const FieldGroup = React.forwardRef< + HTMLDivElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( +
+)); +FieldGroup.displayName = "FieldGroup"; + +const Field = React.forwardRef< + HTMLDivElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( +
+)); +Field.displayName = "Field"; + +const FieldLabel = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( +
); } diff --git a/src/components/admin/AdminTools.tsx b/src/components/admin/AdminTools.tsx new file mode 100644 index 00000000..2896ce7a --- /dev/null +++ b/src/components/admin/AdminTools.tsx @@ -0,0 +1,20 @@ +// Copyright 2026 L3montree GmbH and the DevGuard Contributors. +// SPDX-License-Identifier: AGPL-3.0-or-later + +"use client"; + +import ExternalOrgAdminCard from "@/components/admin/ExternalOrgAdminCard"; +import InstanceSettingsCard from "@/components/admin/InstanceSettingsCard"; +import TriggerDaemonsCard from "@/components/admin/TriggerDaemonsCard"; + +export default function AdminTools() { + return ( +
+ +
+ + +
+
+ ); +} diff --git a/src/components/admin/ExternalOrgAdminCard.tsx b/src/components/admin/ExternalOrgAdminCard.tsx new file mode 100644 index 00000000..32c051ba --- /dev/null +++ b/src/components/admin/ExternalOrgAdminCard.tsx @@ -0,0 +1,315 @@ +// Copyright 2026 L3montree GmbH and the DevGuard Contributors. +// SPDX-License-Identifier: AGPL-3.0-or-later + +"use client"; + +import { useCallback, useState } from "react"; +import { toast } from "sonner"; +import { Button } from "@/components/ui/button"; +import { + Card, + CardContent, + CardDescription, + CardHeader, + CardTitle, +} from "@/components/ui/card"; +import { Badge } from "@/components/ui/badge"; +import { Input } from "@/components/ui/input"; +import { + AlertDialog, + AlertDialogAction, + AlertDialogCancel, + AlertDialogContent, + AlertDialogDescription, + AlertDialogFooter, + AlertDialogHeader, + AlertDialogTitle, +} from "@/components/ui/alert-dialog"; +import { + Collapsible, + CollapsibleContent, + CollapsibleTrigger, +} from "@/components/ui/collapsible"; +import { + BuildingOffice2Icon, + ChevronRightIcon, + ShieldCheckIcon, + TrashIcon, + UserPlusIcon, +} from "@heroicons/react/20/solid"; +import { UserCircle2Icon } from "lucide-react"; + +// ── Types & mock data ────────────────────────────────────────── + +interface OrgAdmin { + id: string; + email: string; +} + +interface ExternalOrg { + id: string; + /** The reserved @-prefixed slug, e.g. "@gitlab" or "@opencode" */ + slug: string; + /** Display name for the GitLab instance */ + instanceName: string; + /** Base URL of the GitLab instance */ + instanceUrl: string; + admins: OrgAdmin[]; +} + +const mockExternalOrgs: ExternalOrg[] = [ + { + id: "ext-1", + slug: "@gitlab", + instanceName: "GitLab.com", + instanceUrl: "https://gitlab.com", + admins: [ + { id: "u1", email: "alice@acme.corp" }, + { id: "u2", email: "bob@acme.corp" }, + ], + }, + { + id: "ext-2", + slug: "@opencode", + instanceName: "OpenCode", + instanceUrl: "https://gitlab.opencode.de", + admins: [{ id: "u3", email: "carol@gov.de" }], + }, +]; + +// ── Component ────────────────────────────────────────────────── + +export default function ExternalOrgAdminCard() { + const [orgs, setOrgs] = useState(mockExternalOrgs); + const [emailInputs, setEmailInputs] = useState>({}); + const [addingFor, setAddingFor] = useState(null); + const [confirmAssign, setConfirmAssign] = useState<{ + orgId: string; + email: string; + } | null>(null); + + const requestAssignAdmin = useCallback( + (orgId: string) => { + const email = (emailInputs[orgId] ?? "").trim(); + if (!email) return; + + if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)) { + toast.error("Please enter a valid email address."); + return; + } + + const org = orgs.find((o) => o.id === orgId); + if (org?.admins.some((a) => a.email === email)) { + toast.error(`${email} is already an admin of ${org.slug}.`); + return; + } + + setConfirmAssign({ orgId, email }); + }, + [emailInputs, orgs], + ); + + const handleAssignAdmin = useCallback( + (orgId: string, email: string) => { + const org = orgs.find((o) => o.id === orgId); + + // TODO: POST to admin API to verify the user exists & assign admin role + const newAdmin: OrgAdmin = { + id: `generated-${Date.now()}`, + email, + }; + + setOrgs((prev) => + prev.map((o) => + o.id === orgId ? { ...o, admins: [...o.admins, newAdmin] } : o, + ), + ); + setEmailInputs((prev) => ({ ...prev, [orgId]: "" })); + setAddingFor(null); + setConfirmAssign(null); + toast.success(`Added ${email} as admin for ${org?.slug}.`); + }, + [orgs], + ); + + const handleRevokeAdmin = useCallback( + (orgId: string, adminId: string) => { + const org = orgs.find((o) => o.id === orgId); + const admin = org?.admins.find((a) => a.id === adminId); + setOrgs((prev) => + prev.map((o) => + o.id === orgId + ? { ...o, admins: o.admins.filter((a) => a.id !== adminId) } + : o, + ), + ); + toast.success(`Revoked admin privileges for ${admin?.email ?? adminId}.`); + }, + [orgs], + ); + + return ( + + + + + External Organisation Admin Management + + + Manage admin privileges for organisations with deep project sync. + These are reserved @-prefixed + organisations backed by GitLab integrations. + + + +
+ {orgs.map((org) => ( + + + + + {org.slug} + + + {org.admins.length}{" "} + {org.admins.length === 1 ? "admin" : "admins"} + + + + +
+

+ Connected to{" "} + + {org.instanceName} + +

+ + {/* Admin list */} +
+ {org.admins.length === 0 && ( +

+ No admins assigned. +

+ )} + {org.admins.map((admin) => ( +
+ + + {admin.email} + + +
+ ))} +
+ + {/* Add admin */} + {addingFor === org.id ? ( +
+ + setEmailInputs((prev) => ({ + ...prev, + [org.id]: e.target.value, + })) + } + onKeyDown={(e) => { + if (e.key === "Enter") requestAssignAdmin(org.id); + if (e.key === "Escape") setAddingFor(null); + }} + /> + + +
+ ) : ( + + )} +
+
+
+ ))} +
+ + {/* Confirmation dialog */} + !open && setConfirmAssign(null)} + > + + + Confirm Admin Assignment + + You are about to grant admin privileges to{" "} + + {confirmAssign?.email} + {" "} + for the organisation{" "} + + {orgs.find((o) => o.id === confirmAssign?.orgId)?.slug} + + . This user will be able to manage all projects and settings + within this organisation. + + + + Cancel + { + if (confirmAssign) { + handleAssignAdmin(confirmAssign.orgId, confirmAssign.email); + } + }} + > + Confirm & Add Admin + + + + +
+
+ ); +} diff --git a/src/components/admin/InstanceDashboard.tsx b/src/components/admin/InstanceDashboard.tsx new file mode 100644 index 00000000..39529066 --- /dev/null +++ b/src/components/admin/InstanceDashboard.tsx @@ -0,0 +1,591 @@ +// Copyright 2026 L3montree GmbH and the DevGuard Contributors. +// SPDX-License-Identifier: AGPL-3.0-or-later + +"use client"; + +import { + forwardRef, + useCallback, + useEffect, + useImperativeHandle, + useState, +} from "react"; +import { + Card, + CardContent, + CardDescription, + CardFooter, + CardHeader, + CardTitle, +} from "@/components/ui/card"; +import { + ChartContainer, + ChartLegend, + ChartLegendContent, + ChartTooltip, + ChartTooltipContent, +} from "@/components/ui/chart"; +import { Badge } from "@/components/ui/badge"; +import { Skeleton } from "@/components/ui/skeleton"; +import { + getSeverityClassNames, + severityToColor, +} from "@/components/common/Severity"; +import { classNames } from "@/utils/common"; +import { + Bar, + BarChart, + CartesianGrid, + ResponsiveContainer, + XAxis, + YAxis, +} from "recharts"; +import { + ExclamationTriangleIcon, + ShieldExclamationIcon, +} from "@heroicons/react/24/outline"; +import { + ArrowsRightLeftIcon, + BuildingOffice2Icon, + FolderOpenIcon, + LinkIcon, + TagIcon, + UsersIcon, +} from "@heroicons/react/20/solid"; +import type { InstanceAdminStatsDTO } from "@/types/api/api"; + +export interface InstanceDashboardHandle { + refresh: () => void; +} + +const mockStats: InstanceAdminStatsDTO = { + totalUsers: 1024, + totalOrganisations: 38, + nonEmptyProjects: 267, + totalProjectRefs: 1892, + ticketSyncProjects: 43, + enabledIntegrations: 17, + avgVulnsPerOrg: [ + { org: "Acme Corp", critical: 12, high: 34, medium: 67, low: 120 }, + { org: "WidgetCo", critical: 5, high: 18, medium: 42, low: 85 }, + { org: "Foxtail", critical: 8, high: 27, medium: 53, low: 91 }, + { org: "NovaTech", critical: 3, high: 11, medium: 29, low: 60 }, + { org: "Helios", critical: 15, high: 40, medium: 72, low: 130 }, + ], + topVulnerableAssets: [ + { asset: "api-gateway", org: "Acme Corp", open: 87, critical: 14 }, + { asset: "auth-service", org: "WidgetCo", open: 64, critical: 9 }, + { asset: "frontend-app", org: "Foxtail", open: 52, critical: 6 }, + { asset: "payment-svc", org: "NovaTech", open: 48, critical: 11 }, + { asset: "data-pipeline", org: "Helios", open: 43, critical: 5 }, + { asset: "notification-svc", org: "Acme Corp", open: 39, critical: 3 }, + { asset: "user-mgmt", org: "WidgetCo", open: 31, critical: 2 }, + ], + topCVEs: [ + { cve: "CVE-2025-31498", affected: 42, severity: "CRITICAL" }, + { cve: "CVE-2025-29927", affected: 38, severity: "CRITICAL" }, + { cve: "CVE-2025-27789", affected: 35, severity: "HIGH" }, + { cve: "CVE-2024-50340", affected: 29, severity: "HIGH" }, + { cve: "CVE-2025-21502", affected: 24, severity: "MEDIUM" }, + { cve: "CVE-2024-47176", affected: 22, severity: "HIGH" }, + { cve: "CVE-2024-38816", affected: 19, severity: "MEDIUM" }, + ], + topDependencies: [ + { pkg: "lodash", count: 312, ecosystem: "npm" }, + { pkg: "express", count: 287, ecosystem: "npm" }, + { pkg: "golang.org/x/net", count: 245, ecosystem: "go" }, + { pkg: "com.google.guava:guava", count: 198, ecosystem: "maven" }, + { pkg: "axios", count: 176, ecosystem: "npm" }, + { pkg: "org.slf4j:slf4j-api", count: 164, ecosystem: "maven" }, + { pkg: "requests", count: 149, ecosystem: "pypi" }, + { pkg: "react", count: 142, ecosystem: "npm" }, + ], + avgCodeRisksPerOrg: [ + { org: "Acme Corp", risks: 23 }, + { org: "WidgetCo", risks: 17 }, + { org: "Foxtail", risks: 31 }, + { org: "NovaTech", risks: 9 }, + { org: "Helios", risks: 27 }, + ], + maliciousPackages: [ + { + pkg: "event-stream@3.3.6", + ecosystem: "npm", + affected: 4, + type: "Dependency Injection", + }, + { + pkg: "ua-parser-js@0.7.29", + ecosystem: "npm", + affected: 3, + type: "Cryptominer", + }, + { + pkg: "colors@1.4.1", + ecosystem: "npm", + affected: 2, + type: "Protestware", + }, + ], +}; + +export default forwardRef( + function InstanceDashboard(_, ref) { + const [stats, setStats] = useState(null); + const [loading, setLoading] = useState(true); + + const loadStats = useCallback(async () => { + setLoading(true); + // TODO: Replace with real API call when endpoint is available + await new Promise((r) => setTimeout(r, 600)); + setStats(mockStats); + setLoading(false); + }, []); + + useImperativeHandle(ref, () => ({ refresh: loadStats }), [loadStats]); + + useEffect(() => { + loadStats(); + }, [loadStats]); + + if (loading || !stats) { + return ( +
+ {/* Summary skeleton */} +
+ {Array.from({ length: 6 }).map((_, i) => ( + + + + + + + + + + ))} +
+ {/* Chart skeleton rows */} + {Array.from({ length: 3 }).map((_, row) => ( +
+ {Array.from({ length: 2 }).map((_, col) => ( + + + + + + + + + + ))} +
+ ))} +
+ ); + } + + return ( +
+ {/* Summary cards */} +
+ + + + + Users + + + {stats.totalUsers.toLocaleString()} + + + + Registered users on this instance + + + + + + + + Organisations + + + {stats.totalOrganisations.toLocaleString()} + + + + Total created organisations + + + + + + + + Non-Empty Projects + + + {stats.nonEmptyProjects.toLocaleString()} + + + + Projects with at least one asset + + + + + + + + Project Refs + + + {stats.totalProjectRefs.toLocaleString()} + + + + Total asset versions across projects + + + + + + + + Ticket Sync + + + {stats.ticketSyncProjects.toLocaleString()} + + + + Projects with ticket sync enabled + + + + + + + + Integrations + + + {stats.enabledIntegrations.toLocaleString()} + + + + Active PATs for GitLab integration + + +
+ + {/* Row 1: Avg Open Vulns per Org + Top Vulnerable Assets */} +
+ + + Avg. Open Vulnerabilities per Organisation + + Breakdown by CVSS severity across all organisations. + + + + + + + + + + { + const { content: _, ...rest } = props; + return ( + + ); + }} + /> + } /> + + + + + + + + + + + + + Top Vulnerable Projects + + Assets with the most open vulnerabilities across the instance. + + + +
+ {stats.topVulnerableAssets.map((a) => ( +
+
+
+ + {a.asset} + + + {a.org} + +
+
+
+
+
+
+ + {a.open} + + {a.critical > 0 && ( + + {a.critical} critical + + )} +
+
+ ))} +
+ + +
+ + {/* Row 2: Top CVEs Across Instance + Top Dependencies */} +
+ + + Top CVEs Across Instance + + Most widespread CVEs by number of affected assets. + + + +
+ {stats.topCVEs.map((c) => ( +
+
+ {c.cve} +
+ + {c.severity} + + + {c.affected} assets + +
+ ))} +
+
+
+ + + + Top Used Dependency Packages + + Most common dependencies across the entire instance. + + + +
+ {stats.topDependencies.map((d) => ( +
+
+
+ + {d.pkg} + + + {d.ecosystem} + +
+
+ + {d.count} + + + {((d.count / stats.totalProjectRefs) * 100).toFixed(1)}% + +
+ ))} +
+
+
+
+ + {/* Row 3: Avg Code Risks per Org + Malicious Packages */} +
+ + + Avg. Open Code Risks per Organisation + + Average number of first-party (code) risks across all projects + per organisation. + + + + + + + + + + + } + /> + + + + + + + + + + + + Malicious Packages + + + Known malicious packages detected across the instance. + + + +
+ +
+

+ {stats.maliciousPackages.reduce( + (s, p) => s + p.affected, + 0, + )} +

+

+ affected assets across {stats.maliciousPackages.length}{" "} + malicious packages +

+
+
+
+ {stats.maliciousPackages.map((p) => ( +
+
+
+ + {p.pkg} + + + {p.ecosystem} + +
+ + {p.type} + +
+ + {p.affected} asset{p.affected !== 1 ? "s" : ""} + +
+ ))} +
+
+
+
+
+ ); + }, +); diff --git a/src/components/admin/InstanceSettingsCard.tsx b/src/components/admin/InstanceSettingsCard.tsx new file mode 100644 index 00000000..3cee018d --- /dev/null +++ b/src/components/admin/InstanceSettingsCard.tsx @@ -0,0 +1,63 @@ +// Copyright 2026 L3montree GmbH and the DevGuard Contributors. +// SPDX-License-Identifier: AGPL-3.0-or-later + +"use client"; + +import { useState } from "react"; +import { toast } from "sonner"; +import { + Card, + CardContent, + CardDescription, + CardHeader, + CardTitle, +} from "@/components/ui/card"; +import { Badge } from "@/components/ui/badge"; +import { Switch } from "@/components/ui/switch"; +import { NoSymbolIcon } from "@heroicons/react/20/solid"; + +export default function InstanceSettingsCard() { + const [orgCreationDisabled, setOrgCreationDisabled] = useState(false); + + return ( + + + + + Instance Settings + + + Global configuration toggles for this DevGuard instance. + + + +
+
+
+

+ Disable Organisation Creation +

+

+ When enabled, regular users cannot create new organisations. +

+
+
+ {orgCreationDisabled && Disabled} + { + setOrgCreationDisabled(checked); + toast.success( + checked + ? "Organisation creation disabled for regular users." + : "Organisation creation re-enabled.", + ); + }} + /> +
+
+
+
+
+ ); +} diff --git a/src/components/admin/InstanceTechnicalInfo.tsx b/src/components/admin/InstanceTechnicalInfo.tsx new file mode 100644 index 00000000..d6ef6add --- /dev/null +++ b/src/components/admin/InstanceTechnicalInfo.tsx @@ -0,0 +1,307 @@ +// Copyright 2026 L3montree GmbH and the DevGuard Contributors. +// SPDX-License-Identifier: AGPL-3.0-or-later + +"use client"; + +import { + forwardRef, + useCallback, + useEffect, + useImperativeHandle, + useState, +} from "react"; +import { + Card, + CardContent, + CardDescription, + CardHeader, + CardTitle, +} from "@/components/ui/card"; +import { Badge } from "@/components/ui/badge"; +import { Skeleton } from "@/components/ui/skeleton"; +import { + ArrowTopRightOnSquareIcon, + ServerIcon, + CpuChipIcon, + CircleStackIcon, + CodeBracketIcon, +} from "@heroicons/react/20/solid"; +import type { InstanceInfoDTO } from "@/types/api/api"; +import { adminBrowserApiClient } from "@/services/adminApi"; +import { useInstanceAdmin } from "@/context/InstanceAdminContext"; +import { + checkForUpdate, + type VersionCheckResult, +} from "@/services/versionCheck"; +import { + formatBytes, + formatDuration, + formatUnixTimestamp, +} from "@/utils/format"; + +export interface InstanceTechnicalInfoHandle { + refresh: () => void; +} + +function Row({ + label, + value, + mono, + href, +}: { + label: string; + value: React.ReactNode; + mono?: boolean; + href?: string; +}) { + return ( +
+ {label} + {href ? ( + + {value} + + + ) : ( + + {value} + + )} +
+ ); +} + +export default forwardRef( + function InstanceTechnicalInfo(_, ref) { + const { getPrivateKey } = useInstanceAdmin(); + const [info, setInfo] = useState(null); + const [loading, setLoading] = useState(true); + const [error, setError] = useState(null); + const [versionCheck, setVersionCheck] = useState( + null, + ); + + const loadInfo = useCallback(async () => { + const key = getPrivateKey(); + if (!key) return; + + try { + setLoading(true); + const resp = await adminBrowserApiClient("/info", key); + if (!resp.ok) { + setError(`Server returned ${resp.status}`); + return; + } + const data = (await resp.json()) as InstanceInfoDTO; + setInfo(data); + setError(null); + + // Fire-and-forget version check + checkForUpdate(data.build.version) + .then((result) => setVersionCheck(result)) + .catch(() => { + /* non-critical */ + }); + } catch { + setError("Failed to fetch instance info."); + } finally { + setLoading(false); + } + }, [getPrivateKey]); + + useImperativeHandle(ref, () => ({ refresh: loadInfo }), [loadInfo]); + + useEffect(() => { + loadInfo(); + }, [loadInfo]); + + if (loading) { + return ( +
+ {[4, 3, 5, 4].map((rows, i) => ( + + + + + + + {Array.from({ length: rows }).map((_, j) => ( +
+ + +
+ ))} +
+
+ ))} +
+ ); + } + + if (error || !info) { + return ( + + +

+ {error ?? "No instance info available."} +

+
+
+ ); + } + + const commitShort = info.build.commit.slice(0, 8); + const commitUrl = `https://github.com/l3montree-dev/devguard/commit/${info.build.commit}`; + const vulndbUrl = + "https://github.com/l3montree-dev/devguard/pkgs/container/devguard%2Fvulndb%2Fv1"; + + console.log("Instance info loaded:", info, versionCheck); + + return ( +
+ {/* Build */} + + + + + Build + + + Version and source control information. + + + + + {info.build.version} + {versionCheck?.updateAvailable && ( + + + {versionCheck.latestVersion} available + + + )} + {versionCheck && !versionCheck.updateAvailable && ( + up to date + )} + + } + mono + /> + + + + + + + {/* Process */} + + + + + Process + + + Server process and uptime details. + + + + + + + + + + {/* Runtime */} + + + + + Runtime + + Go runtime and memory statistics. + + + + + + + + + + + {/* Database */} + + + + + Database + + + Connection pool and migration status. + + + + + {info.database.status} + + } + /> + + + v{info.database.migrationVersion} + {info.database.migrationDirty && ( + dirty + )} + + } + /> + + + +
+ ); + }, +); diff --git a/src/components/admin/TriggerDaemonsCard.tsx b/src/components/admin/TriggerDaemonsCard.tsx new file mode 100644 index 00000000..1590360e --- /dev/null +++ b/src/components/admin/TriggerDaemonsCard.tsx @@ -0,0 +1,152 @@ +// Copyright 2026 L3montree GmbH and the DevGuard Contributors. +// SPDX-License-Identifier: AGPL-3.0-or-later + +"use client"; + +import { useCallback, useState } from "react"; +import { toast } from "sonner"; +import { Button } from "@/components/ui/button"; +import { + Card, + CardContent, + CardDescription, + CardHeader, + CardTitle, +} from "@/components/ui/card"; +import { Input } from "@/components/ui/input"; +import { ArrowPathIcon, PlayIcon } from "@heroicons/react/20/solid"; +import { CommandLineIcon } from "@heroicons/react/24/outline"; + +interface Daemon { + id: string; + label: string; + description: string; + command: string; +} + +const daemons: Daemon[] = [ + { + id: "run-pipeline-single", + label: "Run Pipeline (Single Asset)", + description: "Run the asset pipeline for a single asset.", + command: "devguard-cli daemon runPipeline --asset ", + }, + { + id: "run-pipeline-all", + label: "Run Pipeline (All Assets)", + description: "Run the asset pipeline for all assets on this instance.", + command: "devguard-cli daemon runPipeline --all", + }, + { + id: "daemon-trigger", + label: "Trigger Background Jobs", + description: + "Trigger all registered background jobs (sync, scheduled scans, etc.).", + command: "devguard-cli daemon trigger", + }, + { + id: "vulndb-sync", + label: "VulnDB Sync", + description: + "Synchronize vulnerability data from upstream sources (NVD, OSV, ExploitDB, and others).", + command: "devguard-cli vulndb sync", + }, + { + id: "vulndb-cleanup", + label: "VulnDB Cleanup", + description: "Remove orphaned database tables from failed imports.", + command: "devguard-cli vulndb cleanup", + }, +]; + +export default function TriggerDaemonsCard() { + const [running, setRunning] = useState>({}); + const [assetId, setAssetId] = useState(""); + + const handleTriggerDaemon = useCallback( + (daemon: Daemon) => { + if (daemon.id === "run-pipeline-single" && !assetId.trim()) { + toast.error("Please enter an asset ID."); + return; + } + setRunning((prev) => ({ ...prev, [daemon.id]: true })); + toast.info(`Triggered: ${daemon.label}`); + // Simulate async operation + setTimeout(() => { + setRunning((prev) => ({ ...prev, [daemon.id]: false })); + toast.success(`${daemon.label} completed.`); + }, 2500); + }, + [assetId], + ); + + return ( + + + + + Trigger Daemons + + + Manually trigger background daemons and maintenance jobs. + + + +
+ {daemons.map((d) => ( +
+
+
+ {d.label} +
+

+ {d.description} +

+ + ${" "} + {d.id === "run-pipeline-single" + ? `devguard-cli daemon runPipeline --asset ${assetId || ""}` + : d.command} + + {d.id === "run-pipeline-single" && ( + setAssetId(e.target.value)} + /> + )} +
+ +
+ ))} +
+
+
+ ); +} diff --git a/src/components/ui/alert-dialog.tsx b/src/components/ui/alert-dialog.tsx index a6bc9e16..f8974ba3 100644 --- a/src/components/ui/alert-dialog.tsx +++ b/src/components/ui/alert-dialog.tsx @@ -34,7 +34,7 @@ const AlertDialogContent = React.forwardRef< --g or just + const m = version.match(/^(v?\d+\.\d+\.\d+)/); + return m ? m[1] : version; +} + +/** + * Compare two semver tuples. + * Returns negative if a < b, 0 if equal, positive if a > b. + */ +function compareSemver( + a: [number, number, number], + b: [number, number, number], +): number { + for (let i = 0; i < 3; i++) { + if (a[i] !== b[i]) return a[i] - b[i]; + } + return 0; +} + +const GITHUB_RELEASES_API = + "https://api.github.com/repos/l3montree-dev/devguard/releases/latest"; + +/** + * Fetch the latest DevGuard release from GitHub and compare it against + * the running instance version string. + */ +export async function checkForUpdate( + currentVersion: string, +): Promise { + const res = await fetch(GITHUB_RELEASES_API, { + headers: { Accept: "application/vnd.github+json" }, + next: { revalidate: 3600 }, // cache for 1 hour in Next.js fetch + }); + + if (!res.ok) { + throw new Error(`GitHub API responded with ${res.status}`); + } + + const data = (await res.json()) as { tag_name: string; html_url: string }; + const latestTag = data.tag_name; + const latestUrl = data.html_url; + + const currentParsed = parseSemver(baseTag(currentVersion)); + const latestParsed = parseSemver(latestTag); + + // If we can't parse either version, be conservative and say no update + const updateAvailable = + currentParsed && latestParsed + ? compareSemver(currentParsed, latestParsed) < 0 + : false; + + return { + latestVersion: latestTag, + latestUrl, + updateAvailable, + }; +} diff --git a/src/types/api/api.ts b/src/types/api/api.ts index 042a3942..40b03930 100644 --- a/src/types/api/api.ts +++ b/src/types/api/api.ts @@ -925,3 +925,104 @@ export type ExternalReference = { url: string; type: "cyclonedxvex" | "csaf"; }; + +export interface SeverityBreakdown { + critical: number; + high: number; + medium: number; + low: number; +} + +export interface OrgVulnStats extends SeverityBreakdown { + org: string; +} + +export interface VulnerableAssetStats { + asset: string; + org: string; + open: number; + critical: number; +} + +export interface TopCVEStats { + cve: string; + affected: number; + severity: "CRITICAL" | "HIGH" | "MEDIUM" | "LOW"; +} + +export interface TopDependencyStats { + pkg: string; + count: number; + ecosystem: string; +} + +export interface OrgCodeRiskStats { + org: string; + risks: number; +} + +export interface MaliciousPackageStats { + pkg: string; + ecosystem: string; + affected: number; + type: string; +} + +export interface InstanceAdminStatsDTO { + totalUsers: number; + totalOrganisations: number; + nonEmptyProjects: number; + totalProjectRefs: number; + ticketSyncProjects: number; + enabledIntegrations: number; + avgVulnsPerOrg: OrgVulnStats[]; + topVulnerableAssets: VulnerableAssetStats[]; + topCVEs: TopCVEStats[]; + topDependencies: TopDependencyStats[]; + avgCodeRisksPerOrg: OrgCodeRiskStats[]; + maliciousPackages: MaliciousPackageStats[]; +} + +export interface InstanceBuildInfo { + version: string; + commit: string; + branch: string; + buildDate: string; +} + +export interface InstanceProcessInfo { + pid: number; + hostname: string; + uptimeSeconds: number; +} + +export interface InstanceRuntimeMemInfo { + alloc: number; + totalAlloc: number; + sys: number; + heapAlloc: number; +} + +export interface InstanceRuntimeInfo { + goVersion: string; + numGoroutines: number; + mem: InstanceRuntimeMemInfo; +} + +export interface InstanceDatabaseInfo { + maxOpenConnections: number; + openConnections: number; + inUse: number; + idle: number; + status: string; + migrationVersion: number; + migrationDirty: boolean; + vulndbVersion: string; +} + +export interface InstanceInfoDTO { + build: InstanceBuildInfo; + process: InstanceProcessInfo; + runtime: InstanceRuntimeInfo; + database: InstanceDatabaseInfo; +} diff --git a/src/utils/format.ts b/src/utils/format.ts new file mode 100644 index 00000000..ba86b892 --- /dev/null +++ b/src/utils/format.ts @@ -0,0 +1,64 @@ +// Copyright 2026 L3montree GmbH and the DevGuard Contributors. +// SPDX-License-Identifier: AGPL-3.0-or-later + +/** + * Format a byte count into a human-readable string using binary units. + * + * @param bytes - The number of bytes. + * @param decimals - Number of decimal places (default: 1). + * @returns A formatted string like "3.2 MiB". + */ +export function formatBytes(bytes: number, decimals = 1): string { + if (bytes === 0) return "0 B"; + const k = 1024; + const units = ["B", "KiB", "MiB", "GiB", "TiB", "PiB"]; + const i = Math.floor(Math.log(bytes) / Math.log(k)); + return `${(bytes / Math.pow(k, i)).toFixed(decimals)} ${units[i]}`; +} + +/** + * Format a duration given in seconds into a compact human-readable string. + * + * @param seconds - Total number of seconds. + * @returns A string like "3d 5h 12m" or "42m". + */ +export function formatDuration(seconds: number): string { + const d = Math.floor(seconds / 86_400); + const h = Math.floor((seconds % 86_400) / 3_600); + const m = Math.floor((seconds % 3_600) / 60); + const s = Math.floor(seconds % 60); + + const parts: string[] = []; + if (d > 0) parts.push(`${d}d`); + if (h > 0) parts.push(`${h}h`); + if (m > 0) parts.push(`${m}m`); + if (parts.length === 0) parts.push(`${s}s`); + return parts.join(" "); +} + +/** + * Format a Unix timestamp (seconds) into a locale-aware date string. + * If the input is not a valid numeric timestamp it is returned as-is. + * + * @param timestamp - A Unix timestamp in seconds (number or numeric string). + * @param options - Optional `Intl.DateTimeFormatOptions` overrides. + * @returns A formatted date string. + */ +export function formatUnixTimestamp( + timestamp: string | number, + options?: Intl.DateTimeFormatOptions, +): string { + const n = Number(timestamp); + if (Number.isNaN(n) || n === 0) return String(timestamp); + + return new Date(n * 1000).toLocaleDateString( + undefined, + options ?? { + year: "numeric", + month: "short", + day: "numeric", + hour: "2-digit", + minute: "2-digit", + }, + ); +} From 7c8073f1de9cfde4f761efcfb5ce1bbcc8aba158 Mon Sep 17 00:00:00 2001 From: Sebastian Kawelke Date: Fri, 6 Mar 2026 09:53:39 +0100 Subject: [PATCH 3/4] Continues with instance admin implementation Signed-off-by: Sebastian Kawelke --- src/components/admin/InstanceSettingsCard.tsx | 1 + src/components/admin/TriggerDaemonsCard.tsx | 241 ++++++++++++------ src/services/adminApi.ts | 102 ++++++++ 3 files changed, 267 insertions(+), 77 deletions(-) diff --git a/src/components/admin/InstanceSettingsCard.tsx b/src/components/admin/InstanceSettingsCard.tsx index 3cee018d..55464740 100644 --- a/src/components/admin/InstanceSettingsCard.tsx +++ b/src/components/admin/InstanceSettingsCard.tsx @@ -44,6 +44,7 @@ export default function InstanceSettingsCard() {
{orgCreationDisabled && Disabled} { setOrgCreationDisabled(checked); diff --git a/src/components/admin/TriggerDaemonsCard.tsx b/src/components/admin/TriggerDaemonsCard.tsx index 1590360e..36dafc31 100644 --- a/src/components/admin/TriggerDaemonsCard.tsx +++ b/src/components/admin/TriggerDaemonsCard.tsx @@ -3,7 +3,7 @@ "use client"; -import { useCallback, useState } from "react"; +import { useCallback, useRef, useState } from "react"; import { toast } from "sonner"; import { Button } from "@/components/ui/button"; import { @@ -16,68 +16,145 @@ import { import { Input } from "@/components/ui/input"; import { ArrowPathIcon, PlayIcon } from "@heroicons/react/20/solid"; import { CommandLineIcon } from "@heroicons/react/24/outline"; +import { useInstanceAdmin } from "@/context/InstanceAdminContext"; +import { + adminSSETrigger, + AdminAPIError, + type DaemonSSEEvent, +} from "@/services/adminApi"; interface Daemon { id: string; label: string; description: string; - command: string; + /** Admin API endpoint path (relative to /api/v1) */ + endpoint: string; + /** Whether a body payload with assetId is needed */ + requiresAssetId?: boolean; } const daemons: Daemon[] = [ { - id: "run-pipeline-single", - label: "Run Pipeline (Single Asset)", - description: "Run the asset pipeline for a single asset.", - command: "devguard-cli daemon runPipeline --asset ", + id: "openSourceInsights", + label: "Open Source Insights", + description: "Sync open-source project metadata from deps.dev.", + endpoint: "/admin/daemons/open-source-insights/trigger", }, { - id: "run-pipeline-all", - label: "Run Pipeline (All Assets)", - description: "Run the asset pipeline for all assets on this instance.", - command: "devguard-cli daemon runPipeline --all", + id: "vulndb", + label: "VulnDB Import", + description: "Run an incremental VulnDB import from upstream diffs.", + endpoint: "/admin/daemons/vulndb/trigger", }, { - id: "daemon-trigger", - label: "Trigger Background Jobs", - description: - "Trigger all registered background jobs (sync, scheduled scans, etc.).", - command: "devguard-cli daemon trigger", + id: "vulndbCleanup", + label: "VulnDB Cleanup", + description: "Remove orphaned database tables from failed imports.", + endpoint: "/admin/daemons/vulndb-cleanup/trigger", }, { - id: "vulndb-sync", - label: "VulnDB Sync", - description: - "Synchronize vulnerability data from upstream sources (NVD, OSV, ExploitDB, and others).", - command: "devguard-cli vulndb sync", + id: "fixedVersions", + label: "Fixed Versions", + description: "Update known fixed versions for tracked vulnerabilities.", + endpoint: "/admin/daemons/fixed-versions/trigger", }, { - id: "vulndb-cleanup", - label: "VulnDB Cleanup", - description: "Remove orphaned database tables from failed imports.", - command: "devguard-cli vulndb cleanup", + id: "assetPipelineAll", + label: "Asset Pipeline (All)", + description: "Run the asset pipeline for every asset on this instance.", + endpoint: "/admin/daemons/asset-pipeline-all/trigger", + }, + { + id: "assetPipelineSingle", + label: "Asset Pipeline (Single)", + description: "Run the asset pipeline for a single asset by ID.", + endpoint: "/admin/daemons/asset-pipeline-single/trigger", + requiresAssetId: true, }, ]; export default function TriggerDaemonsCard() { + const { getPrivateKey } = useInstanceAdmin(); const [running, setRunning] = useState>({}); + const [logs, setLogs] = useState>({}); const [assetId, setAssetId] = useState(""); + const logContainerRefs = useRef>({}); + + const appendLog = useCallback((daemonId: string, message: string) => { + setLogs((prev) => ({ + ...prev, + [daemonId]: [...(prev[daemonId] ?? []), message], + })); + // Auto-scroll the log container to the bottom without moving the page + setTimeout(() => { + const el = logContainerRefs.current[daemonId]; + if (el) { + el.scrollTop = el.scrollHeight; + } + }, 50); + }, []); const handleTriggerDaemon = useCallback( - (daemon: Daemon) => { - if (daemon.id === "run-pipeline-single" && !assetId.trim()) { + async (daemon: Daemon) => { + if (daemon.requiresAssetId && !assetId.trim()) { toast.error("Please enter an asset ID."); return; } + + const privateKey = getPrivateKey(); + if (!privateKey) { + toast.error("Admin session expired. Please re-authenticate."); + return; + } + + // Clear previous logs and set running + setLogs((prev) => ({ ...prev, [daemon.id]: [] })); setRunning((prev) => ({ ...prev, [daemon.id]: true })); - toast.info(`Triggered: ${daemon.label}`); - // Simulate async operation - setTimeout(() => { + + try { + const body = daemon.requiresAssetId + ? JSON.stringify({ assetId: assetId.trim() }) + : undefined; + + await adminSSETrigger( + daemon.endpoint, + privateKey, + (evt: DaemonSSEEvent) => { + switch (evt.event) { + case "log": + appendLog(daemon.id, evt.data); + break; + case "done": + appendLog(daemon.id, "Completed successfully."); + toast.success(`${daemon.label} completed.`); + break; + case "error": + try { + const parsed = JSON.parse(evt.data); + appendLog(daemon.id, `Error: ${parsed.message}`); + toast.error(`${daemon.label} failed: ${parsed.message}`); + } catch { + appendLog(daemon.id, `Error: ${evt.data}`); + toast.error(`${daemon.label} failed.`); + } + break; + } + }, + body, + ); + } catch (err) { + const message = err instanceof Error ? err.message : "unknown error"; + appendLog(daemon.id, `Error: ${message}`); + if (err instanceof AdminAPIError && err.status === 429) { + toast.warning(`${daemon.label}: ${message}`); + } else { + toast.error(`${daemon.label}: ${message}`); + } + } finally { setRunning((prev) => ({ ...prev, [daemon.id]: false })); - toast.success(`${daemon.label} completed.`); - }, 2500); + } }, - [assetId], + [assetId, getPrivateKey, appendLog], ); return ( @@ -88,7 +165,8 @@ export default function TriggerDaemonsCard() { Trigger Daemons - Manually trigger background daemons and maintenance jobs. + Manually trigger individual background daemons. Each daemon has a + 5-minute cooldown between triggers (shared across all API instances). @@ -96,53 +174,62 @@ export default function TriggerDaemonsCard() { {daemons.map((d) => (
-
-
- {d.label} +
+
+
+ {d.label} +
+

+ {d.description} +

+ {d.requiresAssetId && ( + setAssetId(e.target.value)} + /> + )}
-

- {d.description} -

- - ${" "} - {d.id === "run-pipeline-single" - ? `devguard-cli daemon runPipeline --asset ${assetId || ""}` - : d.command} - - {d.id === "run-pipeline-single" && ( - setAssetId(e.target.value)} - /> - )} +
- + {/* SSE log output */} + {logs[d.id] && logs[d.id].length > 0 && ( +
{ + logContainerRefs.current[d.id] = el; + }} + className="max-h-32 overflow-y-auto rounded bg-muted/50 px-2 py-1.5 font-mono text-xs text-muted-foreground" + > + {logs[d.id].map((line, i) => ( +
{line}
+ ))} +
+ )}
))}
diff --git a/src/services/adminApi.ts b/src/services/adminApi.ts index 50834c47..df1892b8 100644 --- a/src/services/adminApi.ts +++ b/src/services/adminApi.ts @@ -38,3 +38,105 @@ export const adminBrowserApiClient = async ( credentials: "omit", }); }; + +/** + * SSE event payload from daemon trigger endpoints. + */ +export interface DaemonSSEEvent { + event: "log" | "done" | "error"; + data: string; +} + +/** + * Error subclass carrying the HTTP status code from a failed admin API call. + */ +export class AdminAPIError extends Error { + public readonly status: number; + constructor(message: string, status: number) { + super(message); + this.name = "AdminAPIError"; + this.status = status; + } +} + +/** + * Trigger an admin daemon endpoint that returns an SSE stream. + * Calls `onEvent` for each SSE event received, then resolves when the stream ends. + * Rejects on HTTP errors (e.g. 429 cooldown) or network failures. + */ +export async function adminSSETrigger( + input: string, + hexPrivateKey: string, + onEvent: (evt: DaemonSSEEvent) => void, + body?: string, +): Promise { + const resp = await adminBrowserApiClient(input, hexPrivateKey, { + method: "POST", + body, + headers: body ? { "Content-Type": "application/json" } : undefined, + }); + + if (!resp.ok) { + let message: string | undefined; + try { + const text = await resp.text(); + // The custom Echo error handler sends he.Message directly as JSON, + // which may be a bare string (e.g. "some message") or an object + // (e.g. {"message":"..."}). Handle both. + try { + const json = JSON.parse(text); + if (typeof json === "string") { + message = json; + } else if (typeof json === "object" && json !== null) { + message = json.message ?? JSON.stringify(json); + } + } catch { + // Not valid JSON – use the raw body if it looks meaningful + if (text && text.length < 500) { + message = text; + } + } + } catch { + // body unreadable + } + throw new AdminAPIError( + message ?? resp.statusText ?? `HTTP ${resp.status}`, + resp.status, + ); + } + + if (!resp.body) { + throw new Error("No response body"); + } + + const reader = resp.body.getReader(); + const decoder = new TextDecoder(); + let buffer = ""; + + while (true) { + const { done, value } = await reader.read(); + if (done) break; + + buffer += decoder.decode(value, { stream: true }); + + // Parse SSE lines from buffer + const lines = buffer.split("\n"); + buffer = lines.pop() ?? ""; // keep incomplete line in buffer + + let currentEvent = ""; + for (const line of lines) { + if (line.startsWith("event: ")) { + currentEvent = line.slice(7); + } else if (line.startsWith("data: ")) { + const data = line.slice(6); + if (currentEvent) { + onEvent({ + event: currentEvent as DaemonSSEEvent["event"], + data, + }); + currentEvent = ""; + } + } + } + } +} From 3a4bf361c25eef7b726d450560da9ac1786986db Mon Sep 17 00:00:00 2001 From: Sebastian Kawelke Date: Sun, 22 Mar 2026 20:04:10 +0100 Subject: [PATCH 4/4] Syncs with main Signed-off-by: Sebastian Kawelke --- package-lock.json | 202 ++++++++++++++++++++++------------------------ 1 file changed, 97 insertions(+), 105 deletions(-) diff --git a/package-lock.json b/package-lock.json index c926f3a8..263e8166 100644 --- a/package-lock.json +++ b/package-lock.json @@ -180,6 +180,7 @@ "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.27.1.tgz", "integrity": "sha512-IaaGWsQqfsQWVLqMn9OB92MNN7zukfVA4s7KKAI0KfrrDsZ0yhi5uV4baBuLuN7n3vsZpwP8asPPcVwApxvjBQ==", "license": "MIT", + "peer": true, "dependencies": { "@ampproject/remapping": "^2.2.0", "@babel/code-frame": "^7.27.1", @@ -952,6 +953,7 @@ "resolved": "https://registry.npmjs.org/@codemirror/language/-/language-6.11.0.tgz", "integrity": "sha512-A7+f++LodNNc1wGgoRDTt78cOwWm9KVezApgjOMp1W4hM0898nsqBXwF+sbePE7ZRcjN7Sa1Z5m2oN27XkmEjQ==", "license": "MIT", + "peer": true, "dependencies": { "@codemirror/state": "^6.0.0", "@codemirror/view": "^6.23.0", @@ -1040,6 +1042,7 @@ "resolved": "https://registry.npmjs.org/@codemirror/state/-/state-6.5.2.tgz", "integrity": "sha512-FVqsPqtPWKVVL3dPSxy8wEF/ymIEuVzF1PK3VbUgrxXpJUSHQWWZz4JMToquRxnkw+36LTamCZG2iua2Ptq0fA==", "license": "MIT", + "peer": true, "dependencies": { "@marijn/find-cluster-break": "^1.0.0" } @@ -1049,6 +1052,7 @@ "resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.36.8.tgz", "integrity": "sha512-yoRo4f+FdnD01fFt4XpfpMCcCAo9QvZOtbrXExn4SqzH32YC6LgzqxfLZw/r6Ge65xyY03mK/UfUqrVw1gFiFg==", "license": "MIT", + "peer": true, "dependencies": { "@codemirror/state": "^6.5.0", "style-mod": "^4.1.0", @@ -2519,7 +2523,6 @@ "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.11.tgz", "integrity": "sha512-ZMp1V8ZFcPG5dIWnQLr3NSI1MiCU7UETdS/A0G8V/XWHvJv3ZsFqutJn1Y5RPmAPX6F3BiE397OqveU/9NCuIA==", "license": "MIT", - "peer": true, "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.25" @@ -2855,6 +2858,7 @@ "resolved": "https://registry.npmjs.org/@lezer/highlight/-/highlight-1.2.1.tgz", "integrity": "sha512-Z5duk4RN/3zuVO7Jq0pGLJ3qynpxUVsh7IbUbGj88+uV2ApSAn6kWg2au3iJb+0Zi7kKtqffIESgNcRXWZWmSA==", "license": "MIT", + "peer": true, "dependencies": { "@lezer/common": "^1.0.0" } @@ -3125,9 +3129,9 @@ } }, "node_modules/@next/env": { - "version": "15.5.12", - "resolved": "https://registry.npmjs.org/@next/env/-/env-15.5.12.tgz", - "integrity": "sha512-pUvdJN1on574wQHjaBfNGDt9Mz5utDSZFsIIQkMzPgNS8ZvT4H2mwOrOIClwsQOb6EGx5M76/CZr6G8i6pSpLg==", + "version": "15.5.14", + "resolved": "https://registry.npmjs.org/@next/env/-/env-15.5.14.tgz", + "integrity": "sha512-aXeirLYuASxEgi4X4WhfXsShCFxWDfNn/8ZeC5YXAS2BB4A8FJi1kwwGL6nvMVboE7fZCzmJPNdMvVHc8JpaiA==", "license": "MIT" }, "node_modules/@next/eslint-plugin-next": { @@ -3140,9 +3144,9 @@ } }, "node_modules/@next/swc-darwin-arm64": { - "version": "15.5.12", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-15.5.12.tgz", - "integrity": "sha512-RnRjBtH8S8eXCpUNkQ+543DUc7ys8y15VxmFU9HRqlo9BG3CcBUiwNtF8SNoi2xvGCVJq1vl2yYq+3oISBS0Zg==", + "version": "15.5.14", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-15.5.14.tgz", + "integrity": "sha512-Y9K6SPzobnZvrRDPO2s0grgzC+Egf0CqfbdvYmQVaztV890zicw8Z8+4Vqw8oPck8r1TjUHxVh8299Cg4TrxXg==", "cpu": [ "arm64" ], @@ -3156,9 +3160,9 @@ } }, "node_modules/@next/swc-darwin-x64": { - "version": "15.5.12", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-15.5.12.tgz", - "integrity": "sha512-nqa9/7iQlboF1EFtNhWxQA0rQstmYRSBGxSM6g3GxvxHxcoeqVXfGNr9stJOme674m2V7r4E3+jEhhGvSQhJRA==", + "version": "15.5.14", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-15.5.14.tgz", + "integrity": "sha512-aNnkSMjSFRTOmkd7qoNI2/rETQm/vKD6c/Ac9BZGa9CtoOzy3c2njgz7LvebQJ8iPxdeTuGnAjagyis8a9ifBw==", "cpu": [ "x64" ], @@ -3172,9 +3176,9 @@ } }, "node_modules/@next/swc-linux-arm64-gnu": { - "version": "15.5.12", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-15.5.12.tgz", - "integrity": "sha512-dCzAjqhDHwmoB2M4eYfVKqXs99QdQxNQVpftvP1eGVppamXh/OkDAwV737Zr0KPXEqRUMN4uCjh6mjO+XtF3Mw==", + "version": "15.5.14", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-15.5.14.tgz", + "integrity": "sha512-tjlpia+yStPRS//6sdmlVwuO1Rioern4u2onafa5n+h2hCS9MAvMXqpVbSrjgiEOoCs0nJy7oPOmWgtRRNSM5Q==", "cpu": [ "arm64" ], @@ -3188,9 +3192,9 @@ } }, "node_modules/@next/swc-linux-arm64-musl": { - "version": "15.5.12", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-15.5.12.tgz", - "integrity": "sha512-+fpGWvQiITgf7PUtbWY1H7qUSnBZsPPLyyq03QuAKpVoTy/QUx1JptEDTQMVvQhvizCEuNLEeghrQUyXQOekuw==", + "version": "15.5.14", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-15.5.14.tgz", + "integrity": "sha512-8B8cngBaLadl5lbDRdxGCP1Lef8ipD6KlxS3v0ElDAGil6lafrAM3B258p1KJOglInCVFUjk751IXMr2ixeQOQ==", "cpu": [ "arm64" ], @@ -3204,9 +3208,9 @@ } }, "node_modules/@next/swc-linux-x64-gnu": { - "version": "15.5.12", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-15.5.12.tgz", - "integrity": "sha512-jSLvgdRRL/hrFAPqEjJf1fFguC719kmcptjNVDJl26BnJIpjL3KH5h6mzR4mAweociLQaqvt4UyzfbFjgAdDcw==", + "version": "15.5.14", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-15.5.14.tgz", + "integrity": "sha512-bAS6tIAg8u4Gn3Nz7fCPpSoKAexEt2d5vn1mzokcqdqyov6ZJ6gu6GdF9l8ORFrBuRHgv3go/RfzYz5BkZ6YSQ==", "cpu": [ "x64" ], @@ -3220,9 +3224,9 @@ } }, "node_modules/@next/swc-linux-x64-musl": { - "version": "15.5.12", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-15.5.12.tgz", - "integrity": "sha512-/uaF0WfmYqQgLfPmN6BvULwxY0dufI2mlN2JbOKqqceZh1G4hjREyi7pg03zjfyS6eqNemHAZPSoP84x17vo6w==", + "version": "15.5.14", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-15.5.14.tgz", + "integrity": "sha512-mMxv/FcrT7Gfaq4tsR22l17oKWXZmH/lVqcvjX0kfp5I0lKodHYLICKPoX1KRnnE+ci6oIUdriUhuA3rBCDiSw==", "cpu": [ "x64" ], @@ -3236,9 +3240,9 @@ } }, "node_modules/@next/swc-win32-arm64-msvc": { - "version": "15.5.12", - "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-15.5.12.tgz", - "integrity": "sha512-xhsL1OvQSfGmlL5RbOmU+FV120urrgFpYLq+6U8C6KIym32gZT6XF/SDE92jKzzlPWskkbjOKCpqk5m4i8PEfg==", + "version": "15.5.14", + "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-15.5.14.tgz", + "integrity": "sha512-OTmiBlYThppnvnsqx0rBqjDRemlmIeZ8/o4zI7veaXoeO1PVHoyj2lfTfXTiiGjCyRDhA10y4h6ZvZvBiynr2g==", "cpu": [ "arm64" ], @@ -3252,9 +3256,9 @@ } }, "node_modules/@next/swc-win32-x64-msvc": { - "version": "15.5.12", - "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-15.5.12.tgz", - "integrity": "sha512-Z1Dh6lhFkxvBDH1FoW6OU/L6prYwPSlwjLiZkExIAh8fbP6iI/M7iGTQAJPYJ9YFlWobCZ1PHbchFhFYb2ADkw==", + "version": "15.5.14", + "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-15.5.14.tgz", + "integrity": "sha512-+W7eFf3RS7m4G6tppVTOSyP9Y6FsJXfOuKzav1qKniiFm3KFByQfPEcouHdjlZmysl4zJGuGLQ/M9XyVeyeNEg==", "cpu": [ "x64" ], @@ -3335,6 +3339,7 @@ "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.9.0.tgz", "integrity": "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==", "license": "Apache-2.0", + "peer": true, "engines": { "node": ">=8.0.0" } @@ -3385,6 +3390,7 @@ "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.57.2.tgz", "integrity": "sha512-BdBGhQBh8IjZ2oIIX6F2/Q3LKm/FDDKi6ccYKcBTeilh6SNdNKveDOLk73BkSJjQLJk6qe4Yh+hHw1UPhCDdrg==", "license": "Apache-2.0", + "peer": true, "dependencies": { "@opentelemetry/api-logs": "0.57.2", "@types/shimmer": "^1.2.0", @@ -4056,6 +4062,7 @@ "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.40.0.tgz", "integrity": "sha512-cifvXDhcqMwwTlTK04GBNeIe7yyo28Mfby85QXFe1Yk8nmi36Ab/5UQwptOx84SsoGNRg+EVSjwzfSZMy6pmlw==", "license": "Apache-2.0", + "peer": true, "engines": { "node": ">=14" } @@ -4861,6 +4868,7 @@ "integrity": "sha512-vSMYtL/zOcFpvJCW71Q/OEGQb7KYBPAdKh35WNSkaZA75JlAO8ED8UN6GUNTm3drWomcbcqRPFqQbLae8yBTdg==", "devOptional": true, "license": "Apache-2.0", + "peer": true, "dependencies": { "playwright": "1.56.1" }, @@ -6676,6 +6684,7 @@ "resolved": "https://registry.npmjs.org/@react-three/fiber/-/fiber-9.1.2.tgz", "integrity": "sha512-k8FR9yVHV9kIF3iuOD0ds5hVymXYXfgdKklqziBVod9ZEJ8uk05Zjw29J/omU3IKeUfLNAIHfxneN3TUYM4I2w==", "license": "MIT", + "peer": true, "dependencies": { "@babel/runtime": "^7.17.8", "@types/react-reconciler": "^0.28.9", @@ -6828,6 +6837,7 @@ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", "license": "MIT", + "peer": true, "engines": { "node": ">=12" }, @@ -8294,6 +8304,7 @@ "integrity": "sha512-o4PXJQidqJl82ckFaXUeoAW+XysPLauYI43Abki5hABd853iMhitooc6znOnczgbTYmEP6U6/y1ZyKAIsvMKGg==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/code-frame": "^7.10.4", "@babel/runtime": "^7.12.5", @@ -8596,7 +8607,6 @@ "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-9.6.1.tgz", "integrity": "sha512-FXx2pKgId/WyYo2jXw63kk7/+TY7u7AziEJxJAnSFzHlqTAS3Ync6SvgYAN/k4/PQpnnVuzoMuVnByKK2qp0ag==", "license": "MIT", - "peer": true, "dependencies": { "@types/estree": "*", "@types/json-schema": "*" @@ -8607,7 +8617,6 @@ "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.7.tgz", "integrity": "sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==", "license": "MIT", - "peer": true, "dependencies": { "@types/eslint": "*", "@types/estree": "*" @@ -8792,6 +8801,7 @@ "resolved": "https://registry.npmjs.org/@types/node/-/node-25.3.3.tgz", "integrity": "sha512-DpzbrH7wIcBaJibpKo9nnSQL0MTRdnWttGyE5haGwK86xgMOkFLp7vEyfQPGLOJh5wNYiJ3V9PmUMDhV9u8kkQ==", "license": "MIT", + "peer": true, "dependencies": { "undici-types": "~7.18.0" } @@ -8843,6 +8853,7 @@ "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.14.tgz", "integrity": "sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w==", "license": "MIT", + "peer": true, "dependencies": { "csstype": "^3.2.2" } @@ -8852,6 +8863,7 @@ "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.2.3.tgz", "integrity": "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==", "license": "MIT", + "peer": true, "peerDependencies": { "@types/react": "^19.2.0" } @@ -8908,6 +8920,7 @@ "resolved": "https://registry.npmjs.org/@types/three/-/three-0.176.0.tgz", "integrity": "sha512-FwfPXxCqOtP7EdYMagCFePNKoG1AGBDUEVKtluv2BTVRpSt7b+X27xNsirPCTCqY1pGYsPUzaM3jgWP7dXSxlw==", "license": "MIT", + "peer": true, "dependencies": { "@dimforge/rapier3d-compat": "^0.12.0", "@tweenjs/tween.js": "~23.1.3", @@ -9003,6 +9016,7 @@ "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.56.0.tgz", "integrity": "sha512-IgSWvLobTDOjnaxAfDTIHaECbkNlAlKv2j5SjpB2v7QHKv1FIfjwMy8FsDbVfDX/KjmCmYICcw7uGaXLhtsLNg==", "license": "MIT", + "peer": true, "dependencies": { "@typescript-eslint/scope-manager": "8.56.0", "@typescript-eslint/types": "8.56.0", @@ -9469,7 +9483,6 @@ "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.14.1.tgz", "integrity": "sha512-nuBEDgQfm1ccRp/8bCQrx1frohyufl4JlbMMZ4P1wpeOfDhF6FQkxZJ1b/e+PLwr6X1Nhw6OLme5usuBWYBvuQ==", "license": "MIT", - "peer": true, "dependencies": { "@webassemblyjs/helper-numbers": "1.13.2", "@webassemblyjs/helper-wasm-bytecode": "1.13.2" @@ -9479,29 +9492,25 @@ "version": "1.13.2", "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.13.2.tgz", "integrity": "sha512-6oXyTOzbKxGH4steLbLNOu71Oj+C8Lg34n6CqRvqfS2O71BxY6ByfMDRhBytzknj9yGUPVJ1qIKhRlAwO1AovA==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/@webassemblyjs/helper-api-error": { "version": "1.13.2", "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.13.2.tgz", "integrity": "sha512-U56GMYxy4ZQCbDZd6JuvvNV/WFildOjsaWD3Tzzvmw/mas3cXzRJPMjP83JqEsgSbyrmaGjBfDtV7KDXV9UzFQ==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/@webassemblyjs/helper-buffer": { "version": "1.14.1", "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.14.1.tgz", "integrity": "sha512-jyH7wtcHiKssDtFPRB+iQdxlDf96m0E39yb0k5uJVhFGleZFoNw1c4aeIcVUPPbXUVJ94wwnMOAqUHyzoEPVMA==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/@webassemblyjs/helper-numbers": { "version": "1.13.2", "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.13.2.tgz", "integrity": "sha512-FE8aCmS5Q6eQYcV3gI35O4J789wlQA+7JrqTTpJqn5emA4U2hvwJmvFRC0HODS+3Ye6WioDklgd6scJ3+PLnEA==", "license": "MIT", - "peer": true, "dependencies": { "@webassemblyjs/floating-point-hex-parser": "1.13.2", "@webassemblyjs/helper-api-error": "1.13.2", @@ -9512,15 +9521,13 @@ "version": "1.13.2", "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.13.2.tgz", "integrity": "sha512-3QbLKy93F0EAIXLh0ogEVR6rOubA9AoZ+WRYhNbFyuB70j3dRdwH9g+qXhLAO0kiYGlg3TxDV+I4rQTr/YNXkA==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/@webassemblyjs/helper-wasm-section": { "version": "1.14.1", "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.14.1.tgz", "integrity": "sha512-ds5mXEqTJ6oxRoqjhWDU83OgzAYjwsCV8Lo/N+oRsNDmx/ZDpqalmrtgOMkHwxsG0iI//3BwWAErYRHtgn0dZw==", "license": "MIT", - "peer": true, "dependencies": { "@webassemblyjs/ast": "1.14.1", "@webassemblyjs/helper-buffer": "1.14.1", @@ -9533,7 +9540,6 @@ "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.13.2.tgz", "integrity": "sha512-4LtOzh58S/5lX4ITKxnAK2USuNEvpdVV9AlgGQb8rJDHaLeHciwG4zlGr0j/SNWlr7x3vO1lDEsuePvtcDNCkw==", "license": "MIT", - "peer": true, "dependencies": { "@xtuc/ieee754": "^1.2.0" } @@ -9543,7 +9549,6 @@ "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.13.2.tgz", "integrity": "sha512-Lde1oNoIdzVzdkNEAWZ1dZ5orIbff80YPdHx20mrHwHrVNNTjNr8E3xz9BdpcGqRQbAEa+fkrCb+fRFTl/6sQw==", "license": "Apache-2.0", - "peer": true, "dependencies": { "@xtuc/long": "4.2.2" } @@ -9552,15 +9557,13 @@ "version": "1.13.2", "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.13.2.tgz", "integrity": "sha512-3NQWGjKTASY1xV5m7Hr0iPeXD9+RDobLll3T9d2AO+g3my8xy5peVyjSag4I50mR1bBSN/Ct12lo+R9tJk0NZQ==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/@webassemblyjs/wasm-edit": { "version": "1.14.1", "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.14.1.tgz", "integrity": "sha512-RNJUIQH/J8iA/1NzlE4N7KtyZNHi3w7at7hDjvRNm5rcUXa00z1vRz3glZoULfJ5mpvYhLybmVcwcjGrC1pRrQ==", "license": "MIT", - "peer": true, "dependencies": { "@webassemblyjs/ast": "1.14.1", "@webassemblyjs/helper-buffer": "1.14.1", @@ -9577,7 +9580,6 @@ "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.14.1.tgz", "integrity": "sha512-AmomSIjP8ZbfGQhumkNvgC33AY7qtMCXnN6bL2u2Js4gVCg8fp735aEiMSBbDR7UQIj90n4wKAFUSEd0QN2Ukg==", "license": "MIT", - "peer": true, "dependencies": { "@webassemblyjs/ast": "1.14.1", "@webassemblyjs/helper-wasm-bytecode": "1.13.2", @@ -9591,7 +9593,6 @@ "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.14.1.tgz", "integrity": "sha512-PTcKLUNvBqnY2U6E5bdOQcSM+oVP/PmrDY9NzowJjislEjwP/C4an2303MCVS2Mg9d3AJpIGdUFIQQWbPds0Sw==", "license": "MIT", - "peer": true, "dependencies": { "@webassemblyjs/ast": "1.14.1", "@webassemblyjs/helper-buffer": "1.14.1", @@ -9604,7 +9605,6 @@ "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.14.1.tgz", "integrity": "sha512-JLBl+KZ0R5qB7mCnud/yyX08jWFw5MsoalJ1pQ4EdFlgj9VdXKGuENGsiCIjegI1W7p91rUlcB/LB5yRJKNTcQ==", "license": "MIT", - "peer": true, "dependencies": { "@webassemblyjs/ast": "1.14.1", "@webassemblyjs/helper-api-error": "1.13.2", @@ -9619,7 +9619,6 @@ "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.14.1.tgz", "integrity": "sha512-kPSSXE6De1XOR820C90RIo2ogvZG+c3KiHzqUoO/F34Y2shGzesfqv7o57xrxovZJH/MetF5UjroJ/R/3isoiw==", "license": "MIT", - "peer": true, "dependencies": { "@webassemblyjs/ast": "1.14.1", "@xtuc/long": "4.2.2" @@ -9635,15 +9634,13 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==", - "license": "BSD-3-Clause", - "peer": true + "license": "BSD-3-Clause" }, "node_modules/@xtuc/long": { "version": "4.2.2", "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", - "license": "Apache-2.0", - "peer": true + "license": "Apache-2.0" }, "node_modules/@xyflow/react": { "version": "12.6.4", @@ -9723,6 +9720,7 @@ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz", "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==", "license": "MIT", + "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -9755,7 +9753,6 @@ "resolved": "https://registry.npmjs.org/acorn-import-phases/-/acorn-import-phases-1.0.4.tgz", "integrity": "sha512-wKmbr/DDiIXzEOiWrTTUcDm24kQ2vGfZQvM2fwg2vXqR5uW6aapr7ObPtj1th32b9u90/Pf4AItvdTh42fBmVQ==", "license": "MIT", - "peer": true, "engines": { "node": ">=10.13.0" }, @@ -9818,7 +9815,6 @@ "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", "license": "MIT", - "peer": true, "dependencies": { "ajv": "^8.0.0" }, @@ -9836,7 +9832,6 @@ "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz", "integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==", "license": "MIT", - "peer": true, "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", @@ -9852,8 +9847,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/anser": { "version": "2.3.2", @@ -10443,6 +10437,7 @@ } ], "license": "MIT", + "peer": true, "dependencies": { "baseline-browser-mapping": "^2.9.0", "caniuse-lite": "^1.0.30001759", @@ -10688,7 +10683,6 @@ "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.4.tgz", "integrity": "sha512-rNjApaLzuwaOTjCiT8lSDdGN1APCiqkChLMJxJPWLunPAt5fy8xgU9/jNOchV84wfIxrA0lRQB7oCT8jrn/wrQ==", "license": "MIT", - "peer": true, "engines": { "node": ">=6.0" } @@ -10880,8 +10874,7 @@ "version": "2.20.3", "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/commondir": { "version": "1.0.1", @@ -11139,6 +11132,7 @@ "resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-3.0.0.tgz", "integrity": "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==", "license": "ISC", + "peer": true, "engines": { "node": ">=12" } @@ -11589,7 +11583,8 @@ "version": "8.6.0", "resolved": "https://registry.npmjs.org/embla-carousel/-/embla-carousel-8.6.0.tgz", "integrity": "sha512-SjWyZBHJPbqxHOzckOfo8lHisEaJWmwd23XppYFYVh10bU66/Pn5tkVkbkCMZVdbUE5eTCI2nD8OyIP4Z+uwkA==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/embla-carousel-auto-height": { "version": "8.6.0", @@ -11800,8 +11795,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-2.0.0.tgz", "integrity": "sha512-5POEcUuZybH7IdmGsD8wlf0AI55wMecM9rVBTI/qEAy2c1kTOm3DjFYjrBdI2K3BaJjJYfYFeRtM0t9ssnRuxw==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/es-object-atoms": { "version": "1.1.1", @@ -11953,6 +11947,7 @@ "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.39.2.tgz", "integrity": "sha512-LEyamqS7W5HB3ujJyvi0HQK/dtVINZvd5mAAp9eT5S/ujByGjiZLCzPcHVzuXbpJDJF/cxwHlfceVUDZ2lnSTw==", "license": "MIT", + "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.1", @@ -12040,6 +12035,7 @@ "integrity": "sha512-82GZUjRS0p/jganf6q1rEO25VSoHH0hKPCTrgillPjdI/3bgBhAE1QzHrHTizjpRvy6pGAvKjDJtk2pF9NDq8w==", "dev": true, "license": "MIT", + "peer": true, "bin": { "eslint-config-prettier": "bin/cli.js" }, @@ -12135,6 +12131,7 @@ "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.31.0.tgz", "integrity": "sha512-ixmkI62Rbc2/w8Vfxyh1jQRTdRTF52VxwRVHl/ykPAmqG+Nb7/kNn+byLP0LxPgI7zWA16Jt82SybJInmMia3A==", "license": "MIT", + "peer": true, "dependencies": { "@rtsao/scc": "^1.1.0", "array-includes": "^3.1.8", @@ -12487,7 +12484,6 @@ "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", "license": "MIT", - "peer": true, "engines": { "node": ">=0.8.x" } @@ -12633,8 +12629,7 @@ "url": "https://opencollective.com/fastify" } ], - "license": "BSD-3-Clause", - "peer": true + "license": "BSD-3-Clause" }, "node_modules/fastq": { "version": "1.20.1", @@ -12750,9 +12745,9 @@ } }, "node_modules/flatted": { - "version": "3.4.1", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.1.tgz", - "integrity": "sha512-IxfVbRFVlV8V/yRaGzk0UVIcsKKHMSfYw66T/u4nTwlWteQePsxe//LjudR1AMX4tZW3WFCh3Zqa/sjlqpbURQ==", + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.2.tgz", + "integrity": "sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==", "license": "ISC" }, "node_modules/focus-visible": { @@ -13110,8 +13105,7 @@ "version": "0.4.1", "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==", - "license": "BSD-2-Clause", - "peer": true + "license": "BSD-2-Clause" }, "node_modules/globals": { "version": "16.5.0", @@ -14188,7 +14182,6 @@ "resolved": "https://registry.npmjs.org/isomorphic.js/-/isomorphic.js-0.2.5.tgz", "integrity": "sha512-PIeMbHqMt4DnUP3MA/Flc0HElYjMXArsw1qwJZcm9sqR8mq3l8NYizFMty0pWwE/tzIGH3EKK5+jes5mAr85yw==", "license": "MIT", - "peer": true, "funding": { "type": "GitHub Sponsors ❤", "url": "https://github.com/sponsors/dmonad" @@ -15491,7 +15484,6 @@ "resolved": "https://registry.npmjs.org/lib0/-/lib0-0.2.114.tgz", "integrity": "sha512-gcxmNFzA4hv8UYi8j43uPlQ7CGcyMJ2KQb5kZASw6SnAKAf10hK12i2fjrS3Cl/ugZa5Ui6WwIu1/6MIXiHttQ==", "license": "MIT", - "peer": true, "dependencies": { "isomorphic.js": "^0.2.4" }, @@ -15790,7 +15782,6 @@ "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.1.tgz", "integrity": "sha512-IWqP2SCPhyVFTBtRcgMHdzlf9ul25NwaFx4wCEH/KjAXuuHY4yNjvPXsBokp8jCB936PyWRaPKUNh8NvylLp2Q==", "license": "MIT", - "peer": true, "engines": { "node": ">=6.11.5" }, @@ -17325,16 +17316,16 @@ "version": "2.6.2", "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/next": { - "version": "15.5.12", - "resolved": "https://registry.npmjs.org/next/-/next-15.5.12.tgz", - "integrity": "sha512-Fi/wQ4Etlrn60rz78bebG1i1SR20QxvV8tVp6iJspjLUSHcZoeUXCt+vmWoEcza85ElZzExK/jJ/F6SvtGktjA==", + "version": "15.5.14", + "resolved": "https://registry.npmjs.org/next/-/next-15.5.14.tgz", + "integrity": "sha512-M6S+4JyRjmKic2Ssm7jHUPkE6YUJ6lv4507jprsSZLulubz0ihO2E+S4zmQK3JZ2ov81JrugukKU4Tz0ivgqqQ==", "license": "MIT", + "peer": true, "dependencies": { - "@next/env": "15.5.12", + "@next/env": "15.5.14", "@swc/helpers": "0.5.15", "caniuse-lite": "^1.0.30001579", "postcss": "8.4.31", @@ -17347,14 +17338,14 @@ "node": "^18.18.0 || ^19.8.0 || >= 20.0.0" }, "optionalDependencies": { - "@next/swc-darwin-arm64": "15.5.12", - "@next/swc-darwin-x64": "15.5.12", - "@next/swc-linux-arm64-gnu": "15.5.12", - "@next/swc-linux-arm64-musl": "15.5.12", - "@next/swc-linux-x64-gnu": "15.5.12", - "@next/swc-linux-x64-musl": "15.5.12", - "@next/swc-win32-arm64-msvc": "15.5.12", - "@next/swc-win32-x64-msvc": "15.5.12", + "@next/swc-darwin-arm64": "15.5.14", + "@next/swc-darwin-x64": "15.5.14", + "@next/swc-linux-arm64-gnu": "15.5.14", + "@next/swc-linux-arm64-musl": "15.5.14", + "@next/swc-linux-x64-gnu": "15.5.14", + "@next/swc-linux-x64-musl": "15.5.14", + "@next/swc-win32-arm64-msvc": "15.5.14", + "@next/swc-win32-x64-msvc": "15.5.14", "sharp": "^0.34.3" }, "peerDependencies": { @@ -18158,6 +18149,7 @@ } ], "license": "MIT", + "peer": true, "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", @@ -18217,6 +18209,7 @@ "resolved": "https://registry.npmjs.org/postprocessing/-/postprocessing-6.37.3.tgz", "integrity": "sha512-2FzfZg41Ml/ddvSalLdCz7H53Wmr/ZvK238fauTG1CI97+65BDgo1dNH6AKhAWc5q9ngpyG5EYiXVbNCl2t1iA==", "license": "Zlib", + "peer": true, "peerDependencies": { "three": ">= 0.157.0 < 0.177.0" } @@ -18242,6 +18235,7 @@ "integrity": "sha512-UOnG6LftzbdaHZcKoPFtOcCKztrQ57WkHDeRD9t/PTQtmT0NHSeWWepj6pS0z/N7+08BHFDQVUrfmfMRcZwbMg==", "dev": true, "license": "MIT", + "peer": true, "bin": { "prettier": "bin/prettier.cjs" }, @@ -18438,6 +18432,7 @@ "resolved": "https://registry.npmjs.org/react/-/react-19.2.4.tgz", "integrity": "sha512-9nfp2hYpCwOjAN+8TZFGhtWEwgvWHXqESH8qT89AT/lWklpLON22Lc8pEtnpsZz7VmawabSU0gCjnj8aC0euHQ==", "license": "MIT", + "peer": true, "engines": { "node": ">=0.10.0" } @@ -18456,6 +18451,7 @@ "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.4.tgz", "integrity": "sha512-AXJdLo8kgMbimY95O2aKQqsz2iWi9jMgKJhRBAxECE4IFxfcazB2LmzloIoibJI3C12IlY20+KFaLv+71bUJeQ==", "license": "MIT", + "peer": true, "dependencies": { "scheduler": "^0.27.0" }, @@ -18501,6 +18497,7 @@ "resolved": "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.56.4.tgz", "integrity": "sha512-Rob7Ftz2vyZ/ZGsQZPaRdIefkgOSrQSPXfqBdvOPwJfoGnjwRJUs7EM7Kc1mcoDv3NOtqBzPGbcMB8CGn9CKgw==", "license": "MIT", + "peer": true, "engines": { "node": ">=18.0.0" }, @@ -19059,6 +19056,7 @@ "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.59.0.tgz", "integrity": "sha512-2oMpl67a3zCH9H79LeMcbDhXW/UmWG/y2zuqnF2jQq5uq9TbM9TVyXvA4+t+ne2IIkBdrLpAaRQAvo7YI/Yyeg==", "license": "MIT", + "peer": true, "dependencies": { "@types/estree": "1.0.8" }, @@ -19197,6 +19195,7 @@ "resolved": "https://registry.npmjs.org/sass/-/sass-1.97.3.tgz", "integrity": "sha512-fDz1zJpd5GycprAbu4Q2PV/RprsRtKC/0z82z0JLgdytmcq0+ujJbJ/09bPGDxCLkKY3Np5cRAOcWiVkLXJURg==", "license": "MIT", + "peer": true, "dependencies": { "chokidar": "^4.0.0", "immutable": "^5.0.2", @@ -19236,7 +19235,6 @@ "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.3.tgz", "integrity": "sha512-eflK8wEtyOE6+hsaRVPxvUKYCpRgzLqDTb8krvAsRIwOGlHoSgYLgBXoubGgLd2fT41/OUYdb48v4k4WWHQurA==", "license": "MIT", - "peer": true, "dependencies": { "@types/json-schema": "^7.0.9", "ajv": "^8.9.0", @@ -19273,7 +19271,6 @@ "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", "license": "MIT", - "peer": true, "dependencies": { "fast-deep-equal": "^3.1.3" }, @@ -19285,8 +19282,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/semver": { "version": "7.7.3", @@ -20032,7 +20028,8 @@ "version": "4.1.18", "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.18.tgz", "integrity": "sha512-4+Z+0yiYyEtUVCScyfHCxOYP06L5Ne+JiHhY2IjR2KWMIWhJOYZKLSGZaP5HkZ8+bY0cxfzwDE5uOmzFXyIwxw==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/tailwindcss-animate": { "version": "1.0.7", @@ -20061,7 +20058,6 @@ "resolved": "https://registry.npmjs.org/terser/-/terser-5.46.0.tgz", "integrity": "sha512-jTwoImyr/QbOWFFso3YoU3ik0jBBDJ6JTOQiy/J2YxVJdZCc+5u7skhNwiOR3FQIygFqVUPHl7qbbxtjW2K3Qg==", "license": "BSD-2-Clause", - "peer": true, "dependencies": { "@jridgewell/source-map": "^0.3.3", "acorn": "^8.15.0", @@ -20080,7 +20076,6 @@ "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.4.0.tgz", "integrity": "sha512-Bn5vxm48flOIfkdl5CaD2+1CiUVbonWQ3KQPyP7/EuIl9Gbzq/gQFOzaMFUEgVjB1396tcK0SG8XcNJ/2kDH8g==", "license": "MIT", - "peer": true, "dependencies": { "@jridgewell/trace-mapping": "^0.3.25", "jest-worker": "^27.4.5", @@ -20114,7 +20109,6 @@ "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz", "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==", "license": "MIT", - "peer": true, "dependencies": { "@types/node": "*", "merge-stream": "^2.0.0", @@ -20129,7 +20123,6 @@ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", "license": "MIT", - "peer": true, "dependencies": { "has-flag": "^4.0.0" }, @@ -20145,7 +20138,6 @@ "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", "license": "MIT", - "peer": true, "dependencies": { "buffer-from": "^1.0.0", "source-map": "^0.6.0" @@ -20170,7 +20162,8 @@ "version": "0.176.0", "resolved": "https://registry.npmjs.org/three/-/three-0.176.0.tgz", "integrity": "sha512-PWRKYWQo23ojf9oZSlRGH8K09q7nRSWx6LY/HF/UUrMdYgN9i1e2OwJYHoQjwc6HF/4lvvYLC5YC1X8UJL2ZpA==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/three-mesh-bvh": { "version": "0.8.3", @@ -20254,6 +20247,7 @@ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "license": "MIT", + "peer": true, "engines": { "node": ">=12" }, @@ -20387,6 +20381,7 @@ "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@cspotcode/source-map-support": "^0.8.0", "@tsconfig/node10": "^1.0.7", @@ -20621,6 +20616,7 @@ "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "license": "Apache-2.0", + "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -21178,7 +21174,6 @@ "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.5.1.tgz", "integrity": "sha512-Zn5uXdcFNIA1+1Ei5McRd+iRzfhENPCe7LeABkJtNulSxjma+l7ltNx55BWZkRlwRnpOgHqxnjyaDgJnNXnqzg==", "license": "MIT", - "peer": true, "dependencies": { "glob-to-regexp": "^0.4.1", "graceful-fs": "^4.1.2" @@ -21213,7 +21208,6 @@ "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.105.3.tgz", "integrity": "sha512-LLBBA4oLmT7sZdHiYE/PeVuifOxYyE2uL/V+9VQP7YSYdJU7bSf7H8bZRRxW8kEPMkmVjnrXmoR3oejIdX0xbg==", "license": "MIT", - "peer": true, "dependencies": { "@types/eslint-scope": "^3.7.7", "@types/estree": "^1.0.8", @@ -21277,7 +21271,6 @@ "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", "license": "BSD-2-Clause", - "peer": true, "dependencies": { "esrecurse": "^4.3.0", "estraverse": "^4.1.1" @@ -21291,7 +21284,6 @@ "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", "license": "BSD-2-Clause", - "peer": true, "engines": { "node": ">=4.0" }