From 5015815191368f5fb7aad3731e7fd429d34999ef Mon Sep 17 00:00:00 2001 From: Michael Silva Date: Fri, 20 Jun 2025 16:38:10 -0400 Subject: [PATCH 1/8] first bits --- .../components/control-cloud/index.tsx | 2 +- .../controls/components/logs-search/index.tsx | 2 +- .../components/control-cloud/index.tsx | 2 +- .../controls/components/logs-search/index.tsx | 2 +- .../components/control-cloud/index.tsx | 2 +- .../controls/components/logs-search/index.tsx | 2 +- .../apis/_components/control-cloud/index.tsx | 2 +- .../audit/components/control-cloud/index.tsx | 2 +- .../controls/components/logs-search/index.tsx | 2 +- .../components/control-cloud/index.tsx | 2 +- .../controls/components/logs-search/index.tsx | 2 +- .../roles/components/control-cloud/index.tsx | 2 +- .../controls/components/logs-search/index.tsx | 2 +- .../logs/components/control-cloud/index.tsx | 2 +- .../controls/components/logs-search/index.tsx | 2 +- .../components/control-cloud/index.tsx | 2 +- .../controls/components/logs-search/index.tsx | 2 +- .../logs/components/control-cloud/index.tsx | 2 +- .../controls/components/logs-search/index.tsx | 2 +- .../_components/control-cloud/index.tsx | 2 +- .../filter/control-cloud.examples.tsx | 270 ++++++++++ .../components/filter/control-cloud.mdx | 198 +++++++ internal/ui/package.json | 4 +- .../logs/control-cloud/control-pill.tsx | 14 +- .../components/logs/control-cloud/index.tsx | 13 +- .../components/logs/control-cloud/utils.ts | 0 .../src/hooks/use-keyboard-shortcut.test.tsx | 493 ++++++++++++++++++ .../ui/src/hooks/use-keyboard-shortcut.tsx | 304 +++++++++++ internal/ui/src/index.ts | 2 + internal/ui/src/lib/utils.ts | 209 ++++++++ internal/ui/src/validation/filter.types.ts | 62 +++ .../ui/src/validation/utils/nuqs-parsers.ts | 98 ++++ .../structured-output-schema-generator.ts | 60 +++ ...ansform-structured-output-filter-format.ts | 61 +++ .../ui/src/validation/utils/type-guards.ts | 9 + pnpm-lock.yaml | 6 + 36 files changed, 1811 insertions(+), 32 deletions(-) create mode 100644 apps/engineering/content/design/components/filter/control-cloud.examples.tsx create mode 100644 apps/engineering/content/design/components/filter/control-cloud.mdx rename {apps/dashboard => internal/ui/src}/components/logs/control-cloud/control-pill.tsx (84%) rename {apps/dashboard => internal/ui/src}/components/logs/control-cloud/index.tsx (89%) rename {apps/dashboard => internal/ui/src}/components/logs/control-cloud/utils.ts (100%) create mode 100644 internal/ui/src/hooks/use-keyboard-shortcut.test.tsx create mode 100644 internal/ui/src/hooks/use-keyboard-shortcut.tsx create mode 100644 internal/ui/src/validation/filter.types.ts create mode 100644 internal/ui/src/validation/utils/nuqs-parsers.ts create mode 100644 internal/ui/src/validation/utils/structured-output-schema-generator.ts create mode 100644 internal/ui/src/validation/utils/transform-structured-output-filter-format.ts create mode 100644 internal/ui/src/validation/utils/type-guards.ts diff --git a/apps/dashboard/app/(app)/apis/[apiId]/_overview/components/control-cloud/index.tsx b/apps/dashboard/app/(app)/apis/[apiId]/_overview/components/control-cloud/index.tsx index 02558c4a1d..37dd21c5ff 100644 --- a/apps/dashboard/app/(app)/apis/[apiId]/_overview/components/control-cloud/index.tsx +++ b/apps/dashboard/app/(app)/apis/[apiId]/_overview/components/control-cloud/index.tsx @@ -1,5 +1,5 @@ import { HISTORICAL_DATA_WINDOW } from "@/components/logs/constants"; -import { ControlCloud } from "@/components/logs/control-cloud"; +import { ControlCloud } from "@unkey/ui"; import { useFilters } from "../../hooks/use-filters"; const formatFieldName = (field: string): string => { diff --git a/apps/dashboard/app/(app)/apis/[apiId]/_overview/components/controls/components/logs-search/index.tsx b/apps/dashboard/app/(app)/apis/[apiId]/_overview/components/controls/components/logs-search/index.tsx index a49e8f78cb..8459e7180d 100644 --- a/apps/dashboard/app/(app)/apis/[apiId]/_overview/components/controls/components/logs-search/index.tsx +++ b/apps/dashboard/app/(app)/apis/[apiId]/_overview/components/controls/components/logs-search/index.tsx @@ -1,7 +1,7 @@ import { LogsLLMSearch } from "@/components/logs/llm-search"; -import { transformStructuredOutputToFilters } from "@/components/logs/validation/utils/transform-structured-output-filter-format"; import { toast } from "@/components/ui/toaster"; import { trpc } from "@/lib/trpc/client"; +import { transformStructuredOutputToFilters } from "@unkey/ui"; import { useFilters } from "../../../../hooks/use-filters"; export const LogsSearch = ({ apiId }: { apiId: string }) => { diff --git a/apps/dashboard/app/(app)/apis/[apiId]/keys/[keyAuthId]/[keyId]/components/control-cloud/index.tsx b/apps/dashboard/app/(app)/apis/[apiId]/keys/[keyAuthId]/[keyId]/components/control-cloud/index.tsx index 1725aba7b3..6ac6c3799a 100644 --- a/apps/dashboard/app/(app)/apis/[apiId]/keys/[keyAuthId]/[keyId]/components/control-cloud/index.tsx +++ b/apps/dashboard/app/(app)/apis/[apiId]/keys/[keyAuthId]/[keyId]/components/control-cloud/index.tsx @@ -1,5 +1,5 @@ import { HISTORICAL_DATA_WINDOW } from "@/components/logs/constants"; -import { ControlCloud } from "@/components/logs/control-cloud"; +import { ControlCloud } from "@unkey/ui"; import { useFilters } from "../../hooks/use-filters"; const formatFieldName = (field: string): string => { diff --git a/apps/dashboard/app/(app)/apis/[apiId]/keys/[keyAuthId]/[keyId]/components/controls/components/logs-search/index.tsx b/apps/dashboard/app/(app)/apis/[apiId]/keys/[keyAuthId]/[keyId]/components/controls/components/logs-search/index.tsx index ecb9fe0474..c43cf63862 100644 --- a/apps/dashboard/app/(app)/apis/[apiId]/keys/[keyAuthId]/[keyId]/components/controls/components/logs-search/index.tsx +++ b/apps/dashboard/app/(app)/apis/[apiId]/keys/[keyAuthId]/[keyId]/components/controls/components/logs-search/index.tsx @@ -1,7 +1,7 @@ import { LogsLLMSearch } from "@/components/logs/llm-search"; -import { transformStructuredOutputToFilters } from "@/components/logs/validation/utils/transform-structured-output-filter-format"; import { toast } from "@/components/ui/toaster"; import { trpc } from "@/lib/trpc/client"; +import { transformStructuredOutputToFilters } from "@unkey/ui"; import type { KeyDetailsFilterValue } from "../../../../filters.schema"; import { useFilters } from "../../../../hooks/use-filters"; diff --git a/apps/dashboard/app/(app)/apis/[apiId]/keys/[keyAuthId]/_components/components/control-cloud/index.tsx b/apps/dashboard/app/(app)/apis/[apiId]/keys/[keyAuthId]/_components/components/control-cloud/index.tsx index baaf8b3717..6448f9d713 100644 --- a/apps/dashboard/app/(app)/apis/[apiId]/keys/[keyAuthId]/_components/components/control-cloud/index.tsx +++ b/apps/dashboard/app/(app)/apis/[apiId]/keys/[keyAuthId]/_components/components/control-cloud/index.tsx @@ -1,5 +1,5 @@ import { HISTORICAL_DATA_WINDOW } from "@/components/logs/constants"; -import { ControlCloud } from "@/components/logs/control-cloud"; +import { ControlCloud } from "@unkey/ui"; import { useFilters } from "../../hooks/use-filters"; const formatFieldName = (field: string): string => { diff --git a/apps/dashboard/app/(app)/apis/[apiId]/keys/[keyAuthId]/_components/components/controls/components/logs-search/index.tsx b/apps/dashboard/app/(app)/apis/[apiId]/keys/[keyAuthId]/_components/components/controls/components/logs-search/index.tsx index 0583b98e28..6ce54b57a4 100644 --- a/apps/dashboard/app/(app)/apis/[apiId]/keys/[keyAuthId]/_components/components/controls/components/logs-search/index.tsx +++ b/apps/dashboard/app/(app)/apis/[apiId]/keys/[keyAuthId]/_components/components/controls/components/logs-search/index.tsx @@ -1,7 +1,7 @@ import { LogsLLMSearch } from "@/components/logs/llm-search"; -import { transformStructuredOutputToFilters } from "@/components/logs/validation/utils/transform-structured-output-filter-format"; import { toast } from "@/components/ui/toaster"; import { trpc } from "@/lib/trpc/client"; +import { transformStructuredOutputToFilters } from "@unkey/ui"; import { useFilters } from "../../../../hooks/use-filters"; export const LogsSearch = ({ keyspaceId }: { keyspaceId: string }) => { diff --git a/apps/dashboard/app/(app)/apis/_components/control-cloud/index.tsx b/apps/dashboard/app/(app)/apis/_components/control-cloud/index.tsx index 9a2110d5b4..b4a655e8e6 100644 --- a/apps/dashboard/app/(app)/apis/_components/control-cloud/index.tsx +++ b/apps/dashboard/app/(app)/apis/_components/control-cloud/index.tsx @@ -1,5 +1,5 @@ import { HISTORICAL_DATA_WINDOW } from "@/components/logs/constants"; -import { ControlCloud } from "@/components/logs/control-cloud"; +import { ControlCloud } from "@unkey/ui"; import { useFilters } from "../hooks/use-filters"; const formatFieldName = (field: string): string => { diff --git a/apps/dashboard/app/(app)/audit/components/control-cloud/index.tsx b/apps/dashboard/app/(app)/audit/components/control-cloud/index.tsx index 62d06777fa..833955871e 100644 --- a/apps/dashboard/app/(app)/audit/components/control-cloud/index.tsx +++ b/apps/dashboard/app/(app)/audit/components/control-cloud/index.tsx @@ -1,5 +1,5 @@ import { HISTORICAL_DATA_WINDOW } from "@/components/logs/constants"; -import { ControlCloud } from "@/components/logs/control-cloud"; +import { ControlCloud } from "@unkey/ui"; import { useFilters } from "../../hooks/use-filters"; const formatFieldName = (field: string): string => { diff --git a/apps/dashboard/app/(app)/audit/components/controls/components/logs-search/index.tsx b/apps/dashboard/app/(app)/audit/components/controls/components/logs-search/index.tsx index 42caae3d68..076f3f0ca9 100644 --- a/apps/dashboard/app/(app)/audit/components/controls/components/logs-search/index.tsx +++ b/apps/dashboard/app/(app)/audit/components/controls/components/logs-search/index.tsx @@ -1,7 +1,7 @@ import { LogsLLMSearch } from "@/components/logs/llm-search"; -import { transformStructuredOutputToFilters } from "@/components/logs/validation/utils/transform-structured-output-filter-format"; import { toast } from "@/components/ui/toaster"; import { trpc } from "@/lib/trpc/client"; +import { transformStructuredOutputToFilters } from "@unkey/ui"; import { useFilters } from "../../../../hooks/use-filters"; export const LogsSearch = () => { diff --git a/apps/dashboard/app/(app)/authorization/permissions/components/control-cloud/index.tsx b/apps/dashboard/app/(app)/authorization/permissions/components/control-cloud/index.tsx index e35d0135e3..3805dd59da 100644 --- a/apps/dashboard/app/(app)/authorization/permissions/components/control-cloud/index.tsx +++ b/apps/dashboard/app/(app)/authorization/permissions/components/control-cloud/index.tsx @@ -1,5 +1,5 @@ import { HISTORICAL_DATA_WINDOW } from "@/components/logs/constants"; -import { ControlCloud } from "@/components/logs/control-cloud"; +import { ControlCloud } from "@unkey/ui"; import type { PermissionsFilterField } from "../../filters.schema"; import { useFilters } from "../../hooks/use-filters"; diff --git a/apps/dashboard/app/(app)/authorization/permissions/components/controls/components/logs-search/index.tsx b/apps/dashboard/app/(app)/authorization/permissions/components/controls/components/logs-search/index.tsx index adde229294..27008e78af 100644 --- a/apps/dashboard/app/(app)/authorization/permissions/components/controls/components/logs-search/index.tsx +++ b/apps/dashboard/app/(app)/authorization/permissions/components/controls/components/logs-search/index.tsx @@ -1,7 +1,7 @@ import { LogsLLMSearch } from "@/components/logs/llm-search"; -import { transformStructuredOutputToFilters } from "@/components/logs/validation/utils/transform-structured-output-filter-format"; import { toast } from "@/components/ui/toaster"; import { trpc } from "@/lib/trpc/client"; +import { transformStructuredOutputToFilters } from "@unkey/ui"; import { useFilters } from "../../../../hooks/use-filters"; export const PermissionSearch = () => { diff --git a/apps/dashboard/app/(app)/authorization/roles/components/control-cloud/index.tsx b/apps/dashboard/app/(app)/authorization/roles/components/control-cloud/index.tsx index 45df55b6f4..2df86e7c4b 100644 --- a/apps/dashboard/app/(app)/authorization/roles/components/control-cloud/index.tsx +++ b/apps/dashboard/app/(app)/authorization/roles/components/control-cloud/index.tsx @@ -1,5 +1,5 @@ import { HISTORICAL_DATA_WINDOW } from "@/components/logs/constants"; -import { ControlCloud } from "@/components/logs/control-cloud"; +import { ControlCloud } from "@unkey/ui"; import type { RolesFilterField } from "../../filters.schema"; import { useFilters } from "../../hooks/use-filters"; diff --git a/apps/dashboard/app/(app)/authorization/roles/components/controls/components/logs-search/index.tsx b/apps/dashboard/app/(app)/authorization/roles/components/controls/components/logs-search/index.tsx index 03280904ae..6743f62190 100644 --- a/apps/dashboard/app/(app)/authorization/roles/components/controls/components/logs-search/index.tsx +++ b/apps/dashboard/app/(app)/authorization/roles/components/controls/components/logs-search/index.tsx @@ -1,7 +1,7 @@ import { LogsLLMSearch } from "@/components/logs/llm-search"; -import { transformStructuredOutputToFilters } from "@/components/logs/validation/utils/transform-structured-output-filter-format"; import { toast } from "@/components/ui/toaster"; import { trpc } from "@/lib/trpc/client"; +import { transformStructuredOutputToFilters } from "@unkey/ui"; import { useFilters } from "../../../../hooks/use-filters"; export const RolesSearch = () => { diff --git a/apps/dashboard/app/(app)/logs/components/control-cloud/index.tsx b/apps/dashboard/app/(app)/logs/components/control-cloud/index.tsx index 1a1efcf329..e936451c92 100644 --- a/apps/dashboard/app/(app)/logs/components/control-cloud/index.tsx +++ b/apps/dashboard/app/(app)/logs/components/control-cloud/index.tsx @@ -1,5 +1,5 @@ import { HISTORICAL_DATA_WINDOW } from "@/components/logs/constants"; -import { ControlCloud } from "@/components/logs/control-cloud"; +import { ControlCloud } from "@unkey/ui"; import { format } from "date-fns"; import { useFilters } from "../../hooks/use-filters"; diff --git a/apps/dashboard/app/(app)/logs/components/controls/components/logs-search/index.tsx b/apps/dashboard/app/(app)/logs/components/controls/components/logs-search/index.tsx index bff3c8e1b9..c454f65e11 100644 --- a/apps/dashboard/app/(app)/logs/components/controls/components/logs-search/index.tsx +++ b/apps/dashboard/app/(app)/logs/components/controls/components/logs-search/index.tsx @@ -1,8 +1,8 @@ import { useFilters } from "@/app/(app)/logs/hooks/use-filters"; import { LogsLLMSearch } from "@/components/logs/llm-search"; -import { transformStructuredOutputToFilters } from "@/components/logs/validation/utils/transform-structured-output-filter-format"; import { toast } from "@/components/ui/toaster"; import { trpc } from "@/lib/trpc/client"; +import { transformStructuredOutputToFilters } from "@unkey/ui"; export const LogsSearch = () => { const { filters, updateFilters } = useFilters(); diff --git a/apps/dashboard/app/(app)/ratelimits/[namespaceId]/_overview/components/control-cloud/index.tsx b/apps/dashboard/app/(app)/ratelimits/[namespaceId]/_overview/components/control-cloud/index.tsx index 408a8452d8..3167ddbd74 100644 --- a/apps/dashboard/app/(app)/ratelimits/[namespaceId]/_overview/components/control-cloud/index.tsx +++ b/apps/dashboard/app/(app)/ratelimits/[namespaceId]/_overview/components/control-cloud/index.tsx @@ -1,5 +1,5 @@ import { HISTORICAL_DATA_WINDOW } from "@/components/logs/constants"; -import { ControlCloud } from "@/components/logs/control-cloud"; +import { ControlCloud } from "@unkey/ui"; import { useFilters } from "../../hooks/use-filters"; const formatFieldName = (field: string): string => { diff --git a/apps/dashboard/app/(app)/ratelimits/[namespaceId]/_overview/components/controls/components/logs-search/index.tsx b/apps/dashboard/app/(app)/ratelimits/[namespaceId]/_overview/components/controls/components/logs-search/index.tsx index b165f83d15..b8b8b678b4 100644 --- a/apps/dashboard/app/(app)/ratelimits/[namespaceId]/_overview/components/controls/components/logs-search/index.tsx +++ b/apps/dashboard/app/(app)/ratelimits/[namespaceId]/_overview/components/controls/components/logs-search/index.tsx @@ -1,7 +1,7 @@ import { LogsLLMSearch } from "@/components/logs/llm-search"; -import { transformStructuredOutputToFilters } from "@/components/logs/validation/utils/transform-structured-output-filter-format"; import { toast } from "@/components/ui/toaster"; import { trpc } from "@/lib/trpc/client"; +import { transformStructuredOutputToFilters } from "@unkey/ui"; import { useFilters } from "../../../../hooks/use-filters"; export const LogsSearch = () => { diff --git a/apps/dashboard/app/(app)/ratelimits/[namespaceId]/logs/components/control-cloud/index.tsx b/apps/dashboard/app/(app)/ratelimits/[namespaceId]/logs/components/control-cloud/index.tsx index a36c80f255..18df43052c 100644 --- a/apps/dashboard/app/(app)/ratelimits/[namespaceId]/logs/components/control-cloud/index.tsx +++ b/apps/dashboard/app/(app)/ratelimits/[namespaceId]/logs/components/control-cloud/index.tsx @@ -1,5 +1,5 @@ import { HISTORICAL_DATA_WINDOW } from "@/components/logs/constants"; -import { ControlCloud } from "@/components/logs/control-cloud"; +import { ControlCloud } from "@unkey/ui"; import { useFilters } from "../../hooks/use-filters"; const formatFieldName = (field: string): string => { diff --git a/apps/dashboard/app/(app)/ratelimits/[namespaceId]/logs/components/controls/components/logs-search/index.tsx b/apps/dashboard/app/(app)/ratelimits/[namespaceId]/logs/components/controls/components/logs-search/index.tsx index 329c992d3f..076cc5654b 100644 --- a/apps/dashboard/app/(app)/ratelimits/[namespaceId]/logs/components/controls/components/logs-search/index.tsx +++ b/apps/dashboard/app/(app)/ratelimits/[namespaceId]/logs/components/controls/components/logs-search/index.tsx @@ -1,7 +1,7 @@ import { LogsLLMSearch } from "@/components/logs/llm-search"; -import { transformStructuredOutputToFilters } from "@/components/logs/validation/utils/transform-structured-output-filter-format"; import { toast } from "@/components/ui/toaster"; import { trpc } from "@/lib/trpc/client"; +import { transformStructuredOutputToFilters } from "@unkey/ui"; import { useFilters } from "../../../../hooks/use-filters"; export const LogsSearch = () => { diff --git a/apps/dashboard/app/(app)/ratelimits/_components/control-cloud/index.tsx b/apps/dashboard/app/(app)/ratelimits/_components/control-cloud/index.tsx index 62f44fda70..7af1b91048 100644 --- a/apps/dashboard/app/(app)/ratelimits/_components/control-cloud/index.tsx +++ b/apps/dashboard/app/(app)/ratelimits/_components/control-cloud/index.tsx @@ -1,5 +1,5 @@ import { HISTORICAL_DATA_WINDOW } from "@/components/logs/constants"; -import { ControlCloud } from "@/components/logs/control-cloud"; +import { ControlCloud } from "@unkey/ui"; import { useFilters } from "../hooks/use-filters"; const formatFieldName = (field: string): string => { diff --git a/apps/engineering/content/design/components/filter/control-cloud.examples.tsx b/apps/engineering/content/design/components/filter/control-cloud.examples.tsx new file mode 100644 index 0000000000..a2a6bf63d1 --- /dev/null +++ b/apps/engineering/content/design/components/filter/control-cloud.examples.tsx @@ -0,0 +1,270 @@ +"use client"; +import { RenderComponentWithSnippet } from "@/app/components/render"; +import { ControlCloud } from "@unkey/ui"; +import { useState } from "react"; + +// Define FilterValue type locally for examples +type FilterValue = { + id: string; + field: string; + operator: "is" | "contains" | "startsWith" | "endsWith"; + value: string | number; + metadata?: { + colorClass?: string; + icon?: React.ReactNode; + }; +}; + +// Mock filter data for examples +const createMockFilter = ( + field: string, + operator: string, + value: string | number, + id?: string, +): FilterValue => ({ + id: id || crypto.randomUUID(), + field, + operator: operator as FilterValue["operator"], + value, +}); + +export function BasicControlCloud() { + const [filters, setFilters] = useState([ + createMockFilter("status", "is", "200"), + createMockFilter("method", "is", "GET"), + ]); + + const removeFilter = (id: string) => { + setFilters(filters.filter((f) => f.id !== id)); + }; + + const updateFilters = (newFilters: FilterValue[]) => { + setFilters(newFilters); + }; + + const formatFieldName = (field: string): string => { + switch (field) { + case "status": + return "Status"; + case "method": + return "Method"; + case "path": + return "Path"; + default: + return field.charAt(0).toUpperCase() + field.slice(1); + } + }; + + return ( + +
+ +
Active filters: {filters.length}
+
+
+ ); +} + +export function TimeBasedFilters() { + const [filters, setFilters] = useState([ + createMockFilter("startTime", "is", Date.now() - 3600000), // 1 hour ago + createMockFilter("endTime", "is", Date.now()), + ]); + + const removeFilter = (id: string) => { + setFilters(filters.filter((f) => f.id !== id)); + }; + + const updateFilters = (newFilters: FilterValue[]) => { + setFilters(newFilters); + }; + + const formatFieldName = (field: string): string => { + switch (field) { + case "startTime": + return "Start time"; + case "endTime": + return "End time"; + default: + return field.charAt(0).toUpperCase() + field.slice(1); + } + }; + + return ( + +
+ +
Time range filters applied
+
+
+ ); +} + +export function MultipleFilterTypes() { + const [filters, setFilters] = useState([ + createMockFilter("status", "is", "404"), + createMockFilter("method", "is", "POST"), + createMockFilter("path", "contains", "/api/users"), + createMockFilter("duration", "gt", 1000), + ]); + + const removeFilter = (id: string) => { + setFilters(filters.filter((f) => f.id !== id)); + }; + + const updateFilters = (newFilters: FilterValue[]) => { + setFilters(newFilters); + }; + + const formatFieldName = (field: string): string => { + switch (field) { + case "status": + return "Status"; + case "method": + return "Method"; + case "path": + return "Path"; + case "duration": + return "Duration"; + default: + return field.charAt(0).toUpperCase() + field.slice(1); + } + }; + + const formatValue = (value: string | number, field: string): string => { + if (field === "duration") { + return `${value}ms`; + } + return String(value); + }; + + return ( + +
+ +
+ Mixed filter types: status, method, path, and duration +
+
+
+ ); +} + +export function EmptyState() { + const [filters, setFilters] = useState([]); + + const removeFilter = (id: string) => { + setFilters(filters.filter((f) => f.id !== id)); + }; + + const updateFilters = (newFilters: FilterValue[]) => { + setFilters(newFilters); + }; + + const formatFieldName = (field: string): string => { + return field.charAt(0).toUpperCase() + field.slice(1); + }; + + return ( + +
+ +
+ No filters applied (component is hidden when empty) +
+
+
+ ); +} + +export function InteractiveExample() { + const [filters, setFilters] = useState([]); + + const removeFilter = (id: string) => { + setFilters(filters.filter((f) => f.id !== id)); + }; + + const updateFilters = (newFilters: FilterValue[]) => { + setFilters(newFilters); + }; + + const addFilter = (field: string, value: string | number) => { + const newFilter = createMockFilter(field, "is", value); + setFilters([...filters, newFilter]); + }; + + const formatFieldName = (field: string): string => { + switch (field) { + case "status": + return "Status"; + case "method": + return "Method"; + case "path": + return "Path"; + default: + return field.charAt(0).toUpperCase() + field.slice(1); + } + }; + + return ( + +
+
+ + + +
+
+ +
+ Click buttons above to add filters, then use keyboard navigation (⌥+⇧+C to focus) +
+
+
+
+ ); +} diff --git a/apps/engineering/content/design/components/filter/control-cloud.mdx b/apps/engineering/content/design/components/filter/control-cloud.mdx new file mode 100644 index 0000000000..b3a08ce525 --- /dev/null +++ b/apps/engineering/content/design/components/filter/control-cloud.mdx @@ -0,0 +1,198 @@ +--- +title: ControlCloud +description: A dynamic filter display component that shows active filters as interactive pills with keyboard navigation and removal capabilities. +--- +import { + BasicControlCloud, + TimeBasedFilters, + MultipleFilterTypes, + EmptyState, + InteractiveExample +} from "./control-cloud.examples"; + +## Usage + +```tsx +import { ControlCloud } from "@unkey/ui"; + +export default function MyComponent() { + const [filters, setFilters] = useState([]); + + const removeFilter = (id: string) => { + setFilters(filters.filter(f => f.id !== id)); + }; + + const updateFilters = (newFilters: FilterValue[]) => { + setFilters(newFilters); + }; + + const formatFieldName = (field: string): string => { + return field.charAt(0).toUpperCase() + field.slice(1); + }; + + return ( + + ); +} +``` + +## Examples + +### Basic ControlCloud +A simple example showing basic filter pills for status and method. + + + +### Time-Based Filters +Example with time range filters that use the TimestampInfo component for display. + + + +### Multiple Filter Types +Demonstrates different filter types including status, method, path, and duration with custom value formatting. + + + +### Empty State +When no filters are applied, the component is hidden entirely. + + + +### Interactive Example +Add filters dynamically and test keyboard navigation. + + + +## Features + +- **Interactive Filter Pills**: Each filter is displayed as a removable pill with field name, operator, and value +- **Keyboard Navigation**: Full keyboard support with arrow keys and vim-style navigation (h,j,k,l) +- **Keyboard Shortcuts**: + - `⌥+⇧+D` to clear all filters and set default time range + - `⌥+⇧+C` to focus the first filter pill +- **Time Display**: Automatic timestamp formatting for time-based filters +- **Custom Formatting**: Configurable field name and value formatting +- **Accessibility**: Proper ARIA attributes and screen reader support +- **Responsive Design**: Adapts to different screen sizes + +## Props + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `filters` | `FilterValue[]` | `[]` | Array of active filters to display | +| `removeFilter` | `(id: string) => void` | - | Function to remove a filter by ID | +| `updateFilters` | `(filters: FilterValue[]) => void` | - | Function to update the entire filter array | +| `formatFieldName` | `(field: string) => string` | - | Function to format field names for display | +| `formatValue` | `(value: string \| number, field: string) => string` | `defaultFormatValue` | Function to format values for display | +| `historicalWindow` | `number` | `12 * 60 * 60 * 1000` | Default time window in milliseconds for time-based shortcuts | + +## FilterValue Type + +```tsx +type FilterValue = { + id: string; + field: string; + operator: "is" | "contains" | "gt" | "lt" | "gte" | "lte" | "startsWith" | "endsWith"; + value: string | number; + metadata?: { + colorClass?: string; + icon?: React.ReactNode; + }; +}; +``` + +## Keyboard Navigation + +The ControlCloud component provides comprehensive keyboard navigation: + +- **Arrow Keys**: Navigate between filter pills +- **Vim Keys**: + - `h` / `←`: Move left + - `l` / `→`: Move right + - `j` / `↓`: Move down + - `k` / `↑`: Move up +- **Delete/Backspace**: Remove focused filter pill +- **Tab**: Standard tab navigation +- **Escape**: Clear focus + +## Keyboard Shortcuts + +- **`⌥+⇧+D`**: Clear all filters and set a default time range (endTime = now, startTime = now - historicalWindow) +- **`⌥+⇧+C`**: Focus the first filter pill for keyboard navigation + +## Customization + +### Field Name Formatting + +Customize how field names are displayed: + +```tsx +const formatFieldName = (field: string): string => { + switch (field) { + case "startTime": + return "Start time"; + case "endTime": + return "End time"; + case "status": + return "Status"; + default: + return field.charAt(0).toUpperCase() + field.slice(1); + } +}; +``` + +### Value Formatting + +Customize how values are displayed: + +```tsx +const formatValue = (value: string | number, field: string): string => { + if (field === "duration") { + return `${value}ms`; + } + if (field === "status") { + return `HTTP ${value}`; + } + return String(value); +}; +``` + +## Accessibility + +The ControlCloud component is built with accessibility in mind: + +- **Keyboard Navigation**: Full keyboard support for all interactions +- **Screen Reader Support**: Proper ARIA labels and announcements +- **Focus Management**: Clear focus indicators and logical tab order +- **High Contrast**: Maintains proper contrast ratios +- **Semantic HTML**: Uses appropriate HTML elements and roles + +## Best Practices + +- **Consistent Field Names**: Use consistent field name formatting across your application +- **Clear Value Display**: Format values in a user-friendly way +- **Keyboard Shortcuts**: Document keyboard shortcuts for power users +- **Empty State**: Handle the empty state gracefully (component is hidden when no filters) +- **Filter Management**: Implement proper filter state management in your application +- **Time Windows**: Set appropriate historical windows for time-based shortcuts +- **Performance**: Consider memoizing format functions for large filter arrays + +## Integration + +The ControlCloud component is commonly used with: + +- **Data Tables**: Display active filters above table content +- **Log Viewers**: Show applied log filters +- **Analytics Dashboards**: Display metric filters +- **Search Interfaces**: Show active search criteria + +## Related Components + +- **ControlPill**: Individual filter pill component (internal) +- **TimestampInfo**: Time display component for time-based filters +- **KeyboardButton**: Keyboard shortcut display component \ No newline at end of file diff --git a/internal/ui/package.json b/internal/ui/package.json index 6ce06f7597..c2d8247911 100644 --- a/internal/ui/package.json +++ b/internal/ui/package.json @@ -26,9 +26,11 @@ "class-variance-authority": "^0.7.0", "clsx": "^2.1.1", "date-fns": "^3.6.0", + "nuqs": "^1.17.6", "react": "^18.2.0", "react-day-picker": "8.10.1", "tailwind-merge": "^2.5.4", - "tailwindcss-animate": "^1.0.7" + "tailwindcss-animate": "^1.0.7", + "zod": "^3.23.5" } } diff --git a/apps/dashboard/components/logs/control-cloud/control-pill.tsx b/internal/ui/src/components/logs/control-cloud/control-pill.tsx similarity index 84% rename from apps/dashboard/components/logs/control-cloud/control-pill.tsx rename to internal/ui/src/components/logs/control-cloud/control-pill.tsx index 4a75cda2b1..335096dc80 100644 --- a/apps/dashboard/components/logs/control-cloud/control-pill.tsx +++ b/internal/ui/src/components/logs/control-cloud/control-pill.tsx @@ -1,10 +1,12 @@ -import { cn } from "@/lib/utils"; +// biome-ignore lint: React in this context is used throughout, so biome will change to types because no APIs are used even though React is needed. +import * as React from "react"; +import { cn } from "../../../lib/utils"; import { XMark } from "@unkey/icons"; -import { TimestampInfo } from "@unkey/ui"; -import { Button } from "@unkey/ui"; -import { type KeyboardEvent, useEffect, useRef } from "react"; -import type { FilterValue } from "../validation/filter.types"; +import { TimestampInfo } from "../../timestamp-info"; +import { Button } from "../../button"; +import type { FilterValue } from "../../../validation/filter.types"; import { formatOperator } from "./utils"; +import { useEffect, useRef } from "react"; type ControlPillProps = { filter: T; @@ -35,7 +37,7 @@ export const ControlPill = ({ } }, [isFocused]); - const handleKeyDown = (e: KeyboardEvent) => { + const handleKeyDown = (e: React.KeyboardEvent) => { if (e.key === "Backspace" || e.key === "Delete") { e.preventDefault(); onRemove(filter.id); diff --git a/apps/dashboard/components/logs/control-cloud/index.tsx b/internal/ui/src/components/logs/control-cloud/index.tsx similarity index 89% rename from apps/dashboard/components/logs/control-cloud/index.tsx rename to internal/ui/src/components/logs/control-cloud/index.tsx index dfade6c90c..044715d4c5 100644 --- a/apps/dashboard/components/logs/control-cloud/index.tsx +++ b/internal/ui/src/components/logs/control-cloud/index.tsx @@ -1,7 +1,10 @@ -import { useKeyboardShortcut } from "@/hooks/use-keyboard-shortcut"; -import { KeyboardButton } from "@unkey/ui"; -import { type KeyboardEvent, useCallback, useState } from "react"; -import type { FilterValue } from "../validation/filter.types"; +"use client"; +// biome-ignore lint: React in this context is used throughout, so biome will change to types because no APIs are used even though React is needed. +import * as React from "react"; +import { useCallback, useState } from "react"; +import { useKeyboardShortcut } from "../../../hooks/use-keyboard-shortcut"; +import type { FilterValue } from "../../../validation/filter.types"; +import { KeyboardButton } from "../../keyboard-button"; import { ControlPill } from "./control-pill"; import { defaultFormatValue } from "./utils"; @@ -58,7 +61,7 @@ export const ControlCloud = ({ [removeFilter, filters.length, focusedIndex], ); - const handleKeyDown = (e: KeyboardEvent) => { + const handleKeyDown = (e: React.KeyboardEvent) => { if (filters.length === 0) { return; } diff --git a/apps/dashboard/components/logs/control-cloud/utils.ts b/internal/ui/src/components/logs/control-cloud/utils.ts similarity index 100% rename from apps/dashboard/components/logs/control-cloud/utils.ts rename to internal/ui/src/components/logs/control-cloud/utils.ts diff --git a/internal/ui/src/hooks/use-keyboard-shortcut.test.tsx b/internal/ui/src/hooks/use-keyboard-shortcut.test.tsx new file mode 100644 index 0000000000..358c4b1cbe --- /dev/null +++ b/internal/ui/src/hooks/use-keyboard-shortcut.test.tsx @@ -0,0 +1,493 @@ +import { act, renderHook } from "@testing-library/react"; +import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; +import { parseShortcutString, useKeyboardShortcut } from "./use-keyboard-shortcut"; + +function dispatchKeyEvent( + key: string, + code: string, + options: { + ctrlKey?: boolean; + metaKey?: boolean; + shiftKey?: boolean; + altKey?: boolean; + target?: Element; + } = {}, +) { + const event = new KeyboardEvent("keydown", { + key: key, + code: code, + ctrlKey: options.ctrlKey ?? false, + metaKey: options.metaKey ?? false, + shiftKey: options.shiftKey ?? false, + altKey: options.altKey ?? false, + bubbles: true, + cancelable: true, + }); + + vi.spyOn(event, "preventDefault"); + + act(() => { + (options.target ?? document).dispatchEvent(event); + }); + + return event; +} + +describe("Keyboard Shortcut Functionality", () => { + describe("useKeyboardShortcut Hook", () => { + let callback: ReturnType; + + beforeEach(() => { + callback = vi.fn(); + vi.restoreAllMocks(); + }); + + it("should call callback when the correct key is pressed", () => { + renderHook(() => useKeyboardShortcut("k", callback)); + dispatchKeyEvent("k", "KeyK"); + expect(callback).toHaveBeenCalledTimes(1); + }); + + it("should call callback when the correct key combination (Ctrl+S) is pressed", () => { + renderHook(() => useKeyboardShortcut("ctrl+s", callback)); + dispatchKeyEvent("s", "KeyS", { ctrlKey: true }); + expect(callback).toHaveBeenCalledTimes(1); + }); + + it("should call callback when the correct key combination (Shift+Alt+E) is pressed", () => { + renderHook(() => useKeyboardShortcut("shift+alt+e", callback)); + dispatchKeyEvent("e", "KeyE", { shiftKey: true, altKey: true }); + expect(callback).toHaveBeenCalledTimes(1); + }); + + it("should call callback when the correct key combination (Cmd+K) is pressed", () => { + renderHook(() => useKeyboardShortcut("meta+k", callback)); + dispatchKeyEvent("k", "KeyK", { metaKey: true }); + expect(callback).toHaveBeenCalledTimes(1); + }); + + it("should not call callback when the wrong key is pressed", () => { + renderHook(() => useKeyboardShortcut("k", callback)); + dispatchKeyEvent("j", "KeyJ"); + expect(callback).not.toHaveBeenCalled(); + }); + + it("should not call callback when modifiers are missing", () => { + renderHook(() => useKeyboardShortcut("ctrl+k", callback)); + dispatchKeyEvent("k", "KeyK"); + expect(callback).not.toHaveBeenCalled(); + }); + + it("should not call callback when extra modifiers are pressed", () => { + renderHook(() => useKeyboardShortcut("k", callback)); + dispatchKeyEvent("k", "KeyK", { ctrlKey: true }); + expect(callback).not.toHaveBeenCalled(); + }); + + it("should handle special keys like Enter", () => { + renderHook(() => useKeyboardShortcut("enter", callback)); + dispatchKeyEvent("Enter", "Enter"); + expect(callback).toHaveBeenCalledTimes(1); + }); + + it("should handle special keys like Comma", () => { + renderHook(() => useKeyboardShortcut(",", callback)); + dispatchKeyEvent(",", "Comma"); + expect(callback).toHaveBeenCalledTimes(1); + }); + + it("should handle function keys like F5", () => { + renderHook(() => useKeyboardShortcut("f5", callback)); + dispatchKeyEvent("F5", "F5"); + expect(callback).toHaveBeenCalledTimes(1); + }); + + it("should properly reject modifier-only shortcuts", () => { + const consoleSpy = vi.spyOn(console, "warn").mockImplementation(() => {}); + renderHook(() => useKeyboardShortcut("ctrl+shift", callback)); + dispatchKeyEvent("Shift", "ShiftLeft", { ctrlKey: true, shiftKey: true }); + expect(callback).not.toHaveBeenCalled(); + consoleSpy.mockRestore(); + }); + + it("should call event.preventDefault() by default", () => { + renderHook(() => useKeyboardShortcut("p", callback)); + const event = dispatchKeyEvent("p", "KeyP"); + expect(callback).toHaveBeenCalledTimes(1); + expect(event.preventDefault).toHaveBeenCalledTimes(1); + }); + + describe("ignoreInputs option", () => { + let input: HTMLInputElement; + let textarea: HTMLTextAreaElement; + let select: HTMLSelectElement; + + beforeEach(() => { + input = document.createElement("input"); + textarea = document.createElement("textarea"); + select = document.createElement("select"); + document.body.appendChild(input); + document.body.appendChild(textarea); + document.body.appendChild(select); + input.focus(); + }); + + afterEach(() => { + document.body.removeChild(input); + document.body.removeChild(textarea); + document.body.removeChild(select); + }); + + it("should ignore shortcut when focus is on an input element by default", () => { + renderHook(() => useKeyboardShortcut("i", callback)); + dispatchKeyEvent("i", "KeyI", { target: input }); + expect(callback).not.toHaveBeenCalled(); + }); + + it("should ignore shortcut when focus is on a textarea element by default", () => { + renderHook(() => useKeyboardShortcut("t", callback)); + textarea.focus(); + dispatchKeyEvent("t", "KeyT", { target: textarea }); + expect(callback).not.toHaveBeenCalled(); + }); + + it("should ignore shortcut when focus is on a select element by default", () => { + renderHook(() => useKeyboardShortcut("s", callback)); + select.focus(); + dispatchKeyEvent("s", "KeyS", { target: select }); + expect(callback).not.toHaveBeenCalled(); + }); + + it("should NOT ignore shortcut from input when ignoreInputs is false", () => { + renderHook(() => useKeyboardShortcut("i", callback, { ignoreInputs: false })); + dispatchKeyEvent("i", "KeyI", { target: input }); + expect(callback).toHaveBeenCalledTimes(1); + }); + + it("should NOT ignore shortcut from textarea when ignoreInputs is false", () => { + renderHook(() => useKeyboardShortcut("t", callback, { ignoreInputs: false })); + textarea.focus(); + dispatchKeyEvent("t", "KeyT", { target: textarea }); + expect(callback).toHaveBeenCalledTimes(1); + }); + + it("should NOT ignore shortcut from select when ignoreInputs is false", () => { + renderHook(() => useKeyboardShortcut("s", callback, { ignoreInputs: false })); + select.focus(); + dispatchKeyEvent("s", "KeyS", { target: select }); + expect(callback).toHaveBeenCalledTimes(1); + }); + }); + + describe("ignoreContentEditable option", () => { + let editableDiv: HTMLDivElement; + + beforeEach(() => { + editableDiv = document.createElement("div"); + editableDiv.setAttribute("contenteditable", "true"); + Object.defineProperty(editableDiv, "isContentEditable", { + value: true, + writable: false, + }); + document.body.appendChild(editableDiv); + editableDiv.focus(); + }); + + afterEach(() => { + document.body.removeChild(editableDiv); + }); + + it("should ignore shortcut when focus is on a contentEditable element by default", () => { + renderHook(() => useKeyboardShortcut("c", callback)); + dispatchKeyEvent("c", "KeyC", { target: editableDiv }); + expect(callback).not.toHaveBeenCalled(); + }); + + it("should NOT ignore shortcut from contentEditable when ignoreContentEditable is false", () => { + renderHook(() => useKeyboardShortcut("c", callback, { ignoreContentEditable: false })); + dispatchKeyEvent("c", "KeyC", { target: editableDiv }); + expect(callback).toHaveBeenCalledTimes(1); + }); + }); + + it("should not attach listener or call callback if disabled is true", () => { + const addSpy = vi.spyOn(document, "addEventListener"); + renderHook(() => useKeyboardShortcut("d", callback, { disabled: true })); + dispatchKeyEvent("d", "KeyD"); + expect(addSpy).not.toHaveBeenCalledWith("keydown", expect.any(Function)); + expect(callback).not.toHaveBeenCalled(); + addSpy.mockRestore(); + }); + + it("should not call callback if shortcut is null or undefined", () => { + renderHook(() => useKeyboardShortcut(null, callback)); + dispatchKeyEvent("a", "KeyA"); + expect(callback).not.toHaveBeenCalled(); + + renderHook(() => useKeyboardShortcut(undefined, callback)); + dispatchKeyEvent("b", "KeyB"); + expect(callback).not.toHaveBeenCalled(); + }); + + it("should clean up event listener on unmount", () => { + const addSpy = vi.spyOn(document, "addEventListener"); + const removeSpy = vi.spyOn(document, "removeEventListener"); + + const { unmount } = renderHook(() => useKeyboardShortcut("u", callback)); + + const listener = addSpy.mock.calls.find((call) => call[0] === "keydown")?.[1]; + expect(listener).toBeDefined(); + expect(addSpy).toHaveBeenCalledWith("keydown", listener); + + unmount(); + + expect(removeSpy).toHaveBeenCalledWith("keydown", listener); + + addSpy.mockRestore(); + removeSpy.mockRestore(); + }); + + it("should update listener if shortcut changes", () => { + const addSpy = vi.spyOn(document, "addEventListener"); + const removeSpy = vi.spyOn(document, "removeEventListener"); + let shortcut = "a"; + + const { rerender } = renderHook(() => useKeyboardShortcut(shortcut, callback)); + const listener1 = addSpy.mock.calls.find((call) => call[0] === "keydown")?.[1]; + + dispatchKeyEvent("a", "KeyA"); + expect(callback).toHaveBeenCalledTimes(1); + dispatchKeyEvent("b", "KeyB"); + expect(callback).toHaveBeenCalledTimes(1); + + shortcut = "b"; + rerender(); + + expect(removeSpy).toHaveBeenCalledWith("keydown", listener1); + const listener2 = addSpy.mock.calls.slice(-1)[0][1]; + expect(addSpy).toHaveBeenCalledWith("keydown", listener2); + expect(listener1).not.toBe(listener2); + + dispatchKeyEvent("a", "KeyA"); + expect(callback).toHaveBeenCalledTimes(1); + dispatchKeyEvent("b", "KeyB"); + expect(callback).toHaveBeenCalledTimes(2); + + addSpy.mockRestore(); + removeSpy.mockRestore(); + }); + + it("should accept a KeyCombo object as input", () => { + const keyCombo = { + key: "x", + code: "KeyX", + ctrl: true, + meta: false, + shift: true, + alt: false, + }; + renderHook(() => useKeyboardShortcut(keyCombo, callback)); + + dispatchKeyEvent("x", "KeyX", { ctrlKey: true, shiftKey: true }); + expect(callback).toHaveBeenCalledTimes(1); + + dispatchKeyEvent("x", "KeyX", { ctrlKey: true }); + expect(callback).toHaveBeenCalledTimes(1); + }); + }); + + describe("parseShortcutString Function", () => { + let warnSpy: ReturnType; + + beforeEach(() => { + //@ts-expect-error should be fine + warnSpy = vi.spyOn(console, "warn").mockImplementation(() => {}); + }); + + afterEach(() => { + warnSpy.mockRestore(); + }); + + it("should parse a single key correctly (lowercase)", () => { + expect(parseShortcutString("k")).toEqual({ + key: "k", + code: "KeyK", + ctrl: false, + meta: false, + shift: false, + alt: false, + }); + }); + + it("should parse a single key correctly (uppercase)", () => { + expect(parseShortcutString("K")).toEqual({ + key: "k", + code: "KeyK", + ctrl: false, + meta: false, + shift: false, + alt: false, + }); + }); + + it("should parse combined modifiers and key", () => { + expect(parseShortcutString("ctrl+shift+alt+meta+f")).toEqual({ + key: "f", + code: "KeyF", + ctrl: true, + meta: true, + shift: true, + alt: true, + }); + }); + + it("should parse number keys", () => { + expect(parseShortcutString("1")).toEqual({ + key: "1", + code: "Digit1", + ctrl: false, + meta: false, + shift: false, + alt: false, + }); + }); + + it("should parse special mapped keys (e.g., space, escape)", () => { + expect(parseShortcutString("space")).toEqual({ + key: "space", + code: "Space", + ctrl: false, + meta: false, + shift: false, + alt: false, + }); + expect(parseShortcutString("shift+escape")).toEqual({ + key: "escape", + code: "Escape", + ctrl: false, + meta: false, + shift: true, + alt: false, + }); + }); + + it("should parse punctuation keys (e.g., comma, bracketleft)", () => { + expect(parseShortcutString(",")).toEqual({ + key: ",", + code: "Comma", + ctrl: false, + meta: false, + shift: false, + alt: false, + }); + expect(parseShortcutString("ctrl+BracketLeft")).toEqual({ + key: "bracketleft", + code: "BracketLeft", + ctrl: true, + meta: false, + shift: false, + alt: false, + }); + }); + + it("should handle modifier aliases (cmd, option, control, win)", () => { + expect(parseShortcutString("cmd+k")).toEqual({ + key: "k", + code: "KeyK", + ctrl: false, + meta: true, + shift: false, + alt: false, + }); + expect(parseShortcutString("option+k")).toEqual({ + key: "k", + code: "KeyK", + ctrl: false, + meta: false, + shift: false, + alt: true, + }); + expect(parseShortcutString("control+k")).toEqual({ + key: "k", + code: "KeyK", + ctrl: true, + meta: false, + shift: false, + alt: false, + }); + expect(parseShortcutString("win+k")).toEqual({ + key: "k", + code: "KeyK", + ctrl: false, + meta: true, + shift: false, + alt: false, + }); + }); + + it("should handle whitespace and mixed case", () => { + expect(parseShortcutString(" Ctrl + SHIFT + a ")).toEqual({ + key: "a", + code: "KeyA", + ctrl: true, + meta: false, + shift: true, + alt: false, + }); + }); + + it("should return null for null or undefined input", () => { + expect(parseShortcutString(null as any)).toBeNull(); + expect(parseShortcutString(undefined as any)).toBeNull(); + expect(warnSpy).not.toHaveBeenCalled(); + }); + + it("should return null for empty string input", () => { + expect(parseShortcutString("")).toBeNull(); + expect(warnSpy).not.toHaveBeenCalled(); + }); + + it("should return null for non-string input", () => { + expect(parseShortcutString(123 as any)).toBeNull(); + expect(parseShortcutString({} as any)).toBeNull(); + expect(warnSpy).not.toHaveBeenCalled(); + }); + + it("should return null and warn for empty parts", () => { + expect(parseShortcutString("ctrl++k")).toBeNull(); + expect(warnSpy).toHaveBeenCalledWith(expect.stringContaining("Empty part detected")); + }); + + it("should return null and warn for multiple non-modifier keys", () => { + expect(parseShortcutString("a+b")).toBeNull(); + expect(warnSpy).toHaveBeenCalledWith(expect.stringContaining("Multiple non-modifier keys")); + warnSpy.mockClear(); + expect(parseShortcutString("ctrl+a+b")).toBeNull(); + expect(warnSpy).toHaveBeenCalledWith(expect.stringContaining("Multiple non-modifier keys")); + }); + + it("should return null and warn if only modifiers are provided", () => { + expect(parseShortcutString("ctrl+shift+")).toBeNull(); + expect(warnSpy).toHaveBeenCalledWith(expect.stringContaining("Empty part detected")); + warnSpy.mockClear(); + expect(parseShortcutString("ctrl+shift")).toBeNull(); + expect(warnSpy).toHaveBeenCalledWith(expect.stringContaining("No valid key/code identified")); + }); + + it("should return null and warn for unmappable key names", () => { + expect(parseShortcutString("ctrl+unknownkey")).toBeNull(); + expect(warnSpy).toHaveBeenCalledWith( + expect.stringContaining('Failed to map key "unknownkey"'), + ); + }); + + it("should return null and warn for unknown modifiers treated as keys", () => { + expect(parseShortcutString("super+k")).toBeNull(); + expect(warnSpy).toHaveBeenCalledWith(expect.stringContaining('Failed to map key "super"')); + warnSpy.mockClear(); + + expect(parseShortcutString("k+super")).toBeNull(); + expect(warnSpy).toHaveBeenCalledWith(expect.stringContaining("Multiple non-modifier keys")); + }); + }); +}); diff --git a/internal/ui/src/hooks/use-keyboard-shortcut.tsx b/internal/ui/src/hooks/use-keyboard-shortcut.tsx new file mode 100644 index 0000000000..a5302b7a58 --- /dev/null +++ b/internal/ui/src/hooks/use-keyboard-shortcut.tsx @@ -0,0 +1,304 @@ +import { useCallback, useEffect } from "react"; + +/** + * Represents the parsed details of a keyboard shortcut. + */ +type KeyCombo = { + key: string; // Original key name (e.g., 'e', 'enter') - useful for display + code?: string | null; // Expected KeyboardEvent.code (e.g., 'KeyE', 'Enter') - used for matching + ctrl: boolean; // True if Ctrl key is required + meta: boolean; // True if Meta key (Cmd/Win) is required + shift: boolean; // True if Shift key is required + alt: boolean; // True if Alt key (Option on Mac) is required +}; + +/** + * Configuration options for the useKeyboardShortcut hook. + */ +type KeyboardShortcutOptions = { + preventDefault?: boolean; // Call event.preventDefault() if shortcut matches (default: true) + ignoreInputs?: boolean; // Ignore shortcuts if focus is on input/textarea/select (default: true) + ignoreContentEditable?: boolean; // Ignore shortcuts if focus is on contentEditable element (default: true) + disabled?: boolean; // Disable the shortcut listener entirely (default: false) +}; + +// Default options for the hook +const defaultOptions: Required = { + preventDefault: true, + ignoreInputs: true, + ignoreContentEditable: true, + disabled: false, +}; + +/** + * Maps common key names (like 'a', 'enter', 'f1', 'comma') to their + * corresponding KeyboardEvent.code values (like 'KeyA', 'Enter', 'F1', 'Comma'). + * Expand this function to support more keys as needed. + * @param keyName The user-friendly key name from the shortcut string. + * @returns The corresponding KeyboardEvent.code string, or null if not found. + */ +export const getKeyNameToCode = (keyName: string): string | null => { + const lowerKey = keyName.toLowerCase(); + + // Basic Letters (A-Z) + if (lowerKey.length === 1 && lowerKey >= "a" && lowerKey <= "z") { + return `Key${lowerKey.toUpperCase()}`; // 'a' -> 'KeyA' + } + + // Digits (0-9) - Main number row + if (lowerKey.length === 1 && lowerKey >= "0" && lowerKey <= "9") { + return `Digit${lowerKey}`; // '1' -> 'Digit1' + } + + // Common Control/Whitespace Keys + switch (lowerKey) { + case "enter": + return "Enter"; + case "tab": + return "Tab"; + case "space": + return "Space"; // Use 'space' keyword + case "esc": + case "escape": + return "Escape"; + case "backspace": + return "Backspace"; + case "delete": + return "Delete"; + // Add more common keys as needed (e.g., Home, End, PageUp, PageDown) + } + + // Arrow Keys + switch (lowerKey) { + case "up": + case "arrowup": + return "ArrowUp"; + case "down": + case "arrowdown": + return "ArrowDown"; + case "left": + case "arrowleft": + return "ArrowLeft"; + case "right": + case "arrowright": + return "ArrowRight"; + } + + if (lowerKey.startsWith("f") && !Number.isNaN(Number.parseInt(lowerKey.substring(1), 10))) { + const fNum = Number.parseInt(lowerKey.substring(1), 10); + if (fNum >= 1 && fNum <= 12) { + return `F${fNum}`; + } + } + + switch (lowerKey) { + case ",": + case "comma": + return "Comma"; + case ".": + case "period": + return "Period"; + case "/": + case "slash": + return "Slash"; + case ";": + case "semicolon": + return "Semicolon"; + case "'": + case "quote": + return "Quote"; + case "[": + case "bracketleft": + return "BracketLeft"; + case "]": + case "bracketright": + return "BracketRight"; + case "\\": + case "backslash": + return "Backslash"; + case "`": + case "backquote": + return "Backquote"; + case "-": + case "minus": + return "Minus"; + case "=": + case "equal": + return "Equal"; + } + + console.warn( + `[useKeyboardShortcut] Could not map key name "${keyName}" to a standard KeyboardEvent.code. You might need to use the code directly in the shortcut definition or expand the mapping.`, + ); + return null; // Indicate failure to map +}; + +/** + * Parses a shortcut string (e.g., "ctrl+shift+k") into a KeyCombo object. + * It now maps the key name to its KeyboardEvent.code. + * @param shortcut The shortcut string to parse. + * @returns A KeyCombo object or null if parsing fails. + */ +export const parseShortcutString = (shortcut: string): KeyCombo | null => { + if (!shortcut || typeof shortcut !== "string") { + return null; + } + + const parts = shortcut + .toLowerCase() + .split("+") + .map((part) => part.trim()); + + const combo: Partial & { key?: string; code?: string | null } = {}; + let keyAssigned = false; + + for (const part of parts) { + switch (part) { + case "ctrl": + case "control": + combo.ctrl = true; + break; + case "shift": + combo.shift = true; + break; + case "meta": + case "cmd": + case "win": + combo.meta = true; + break; + case "alt": + case "option": + combo.alt = true; + break; + default: + if (part.length > 0) { + if (keyAssigned) { + console.warn( + `[useKeyboardShortcut] Multiple non-modifier keys detected in shortcut: "${shortcut}"`, + ); + return null; + } + combo.key = part; // Store original key name + combo.code = getKeyNameToCode(part); // Attempt to get the code + if (!combo.code) { + console.warn( + `[useKeyboardShortcut] Failed to map key "${part}" to code for shortcut: "${shortcut}". Please check spelling or expand getKeyNameToCode mapping.`, + ); + return null; // Fail parsing if code cannot be determined + } + keyAssigned = true; + } else { + console.warn( + `[useKeyboardShortcut] Empty part detected in shortcut string: "${shortcut}"`, + ); + return null; + } + } + } + + // Final validation: Ensure a key and code were actually assigned + if (!keyAssigned || !combo.key || !combo.code) { + console.warn(`[useKeyboardShortcut] No valid key/code identified for shortcut: "${shortcut}"`); + return null; + } + + // Return the full KeyCombo object with explicit boolean values for modifiers + return { + key: combo.key, + code: combo.code, + ctrl: !!combo.ctrl, + meta: !!combo.meta, + shift: !!combo.shift, + alt: !!combo.alt, + }; +}; + +/** + * A React hook to trigger a callback function when a specific keyboard shortcut is pressed globally. + * Matches shortcuts based on KeyboardEvent.code (physical key) for better reliability, especially with Alt/Option keys. + * Handles complex shortcut strings (e.g., "ctrl+shift+k") or pre-defined KeyCombo objects. + * Includes options to ignore inputs, contentEditable elements, prevent default behavior, and disable the listener. + * + * @param shortcut The keyboard shortcut definition (string like "ctrl+alt+e", "shift+enter", or KeyCombo object). Can be null/undefined to disable. Key names should be standard (e.g., 'a', 'enter', 'f1', 'comma'). + * @param callback The function to execute when the shortcut is matched. Should be memoized (e.g., with useCallback) if defined inline or stable otherwise. + * @param options Optional configuration for the shortcut listener (KeyboardShortcutOptions). Options are merged with defaults. Provide a stable reference (e.g., useMemo) if passing an object. + */ +export function useKeyboardShortcut( + shortcut: string | KeyCombo | null | undefined, + callback: () => void, + options?: KeyboardShortcutOptions, +): void { + const mergedOptions = { ...defaultOptions, ...options }; + + const { preventDefault, ignoreInputs, ignoreContentEditable, disabled } = mergedOptions; + + // Memoize callback for stability in the useEffect dependency array + // biome-ignore lint/correctness/useExhaustiveDependencies: + const memoizedCallback = useCallback(callback, [callback]); + + useEffect(() => { + // Parse the shortcut definition inside the effect. + // This ensures we work with a stable KeyCombo object within this effect run. + let parsedCombo: KeyCombo | null = null; + if (typeof shortcut === "string") { + parsedCombo = parseShortcutString(shortcut); + } else if (shortcut?.key && shortcut.code) { + // If a KeyCombo object is passed, ensure modifier flags are boolean + parsedCombo = { + ...shortcut, + ctrl: !!shortcut.ctrl, + meta: !!shortcut.meta, + shift: !!shortcut.shift, + alt: !!shortcut.alt, + }; + } else if (shortcut) { + // Handle potentially invalid KeyCombo objects passed directly + console.warn("[useKeyboardShortcut] Invalid KeyCombo object provided:", shortcut); + } + + // Exit if the hook is disabled or the shortcut definition is invalid/unparsed + if (disabled || !parsedCombo) { + return; // No listener will be attached + } + + const requiredCombo = parsedCombo; + + const handleKeyDown = (e: KeyboardEvent): void => { + const target = e.target as HTMLElement; + if ( + ignoreInputs && + (target instanceof HTMLInputElement || + target instanceof HTMLTextAreaElement || + target instanceof HTMLSelectElement) + ) { + return; // Ignore if focus is on standard input elements + } + if (ignoreContentEditable && target.isContentEditable) { + return; // Ignore if focus is on a contentEditable element + } + + // Match the physical key code (more reliable than e.key with modifiers) + const codeMatch = e.code === requiredCombo.code; + + // Match modifier keys state + const ctrlMatch = e.ctrlKey === requiredCombo.ctrl; + const metaMatch = e.metaKey === requiredCombo.meta; // Cmd on Mac, Win key on Windows + const shiftMatch = e.shiftKey === requiredCombo.shift; + const altMatch = e.altKey === requiredCombo.alt; // Option on Mac + + // Check if all conditions are met + if (codeMatch && ctrlMatch && metaMatch && shiftMatch && altMatch) { + if (preventDefault) { + e.preventDefault(); // Prevent default browser action if configured + } + memoizedCallback(); + } + }; + + document.addEventListener("keydown", handleKeyDown); + + return () => { + document.removeEventListener("keydown", handleKeyDown); + }; + }, [shortcut, memoizedCallback, preventDefault, ignoreInputs, ignoreContentEditable, disabled]); +} diff --git a/internal/ui/src/index.ts b/internal/ui/src/index.ts index 1ac2df9016..5eec39fc83 100644 --- a/internal/ui/src/index.ts +++ b/internal/ui/src/index.ts @@ -1,6 +1,8 @@ export * from "./components/button"; export * from "./components/card"; export * from "./components/copy-button"; +export * from "./components/logs/control-cloud"; +export * from "./validation/utils/transform-structured-output-filter-format"; export * from "./components/date-time/date-time"; export * from "./components/dialog/dialog-container"; export * from "./components/dialog/navigable-dialog"; diff --git a/internal/ui/src/lib/utils.ts b/internal/ui/src/lib/utils.ts index 365058cebd..0a12a07f7d 100644 --- a/internal/ui/src/lib/utils.ts +++ b/internal/ui/src/lib/utils.ts @@ -1,6 +1,215 @@ import { type ClassValue, clsx } from "clsx"; import { twMerge } from "tailwind-merge"; +import type { TimeUnit } from "../components/date-time/date-time"; export function cn(...inputs: ClassValue[]) { return twMerge(clsx(inputs)); } +export const isBrowser = typeof window !== "undefined"; + +export function debounce unknown>(func: T, delay: number) { + let timeoutId: ReturnType; + + function debounced(...args: Parameters) { + clearTimeout(timeoutId); + timeoutId = setTimeout(() => { + func(...args); + }, delay); + } + + debounced.cancel = () => { + clearTimeout(timeoutId); + }; + + return debounced; +} + +type ThrottleOptions = { + leading?: boolean; // Whether to invoke on the leading edge + trailing?: boolean; // Whether to invoke on the trailing edge +}; + +type Timer = ReturnType; + +// biome-ignore lint/suspicious/noExplicitAny: Safe to leave +export function throttle any>( + func: T, + wait: number, + options: ThrottleOptions = {}, +): { + (this: ThisParameterType, ...args: Parameters): ReturnType | undefined; + cancel: () => void; + flush: () => ReturnType | undefined; +} { + let timeout: Timer | undefined; + let result: ReturnType | undefined; + let previous = 0; + let pending = false; + + const { leading = true, trailing = true } = options; + + // Function to handle the actual invocation + function invokeFunc(time: number, args: Parameters): ReturnType { + previous = leading ? time : 0; + timeout = undefined; + result = func.apply(null, args); + pending = false; + return result as ReturnType; + } + + // Function to handle the trailing edge call + function trailingEdge(time: number, args: Parameters): ReturnType | undefined { + timeout = undefined; + + if (trailing && pending) { + return invokeFunc(time, args); + } + pending = false; + return result; + } + + // Function to schedule the delayed invocation + function remainingWait(time: number) { + const timeSinceLastCall = time - previous; + const timeWaiting = wait - timeSinceLastCall; + return timeWaiting; + } + + // The actual throttled function + function throttled( + this: ThisParameterType, + ...args: Parameters + ): ReturnType | undefined { + const time = Date.now(); + const isInvoking = shouldInvoke(time); + + pending = true; + + if (isInvoking) { + if (timeout) { + clearTimeout(timeout); + timeout = undefined; + } + return invokeFunc(time, args); + } + + if (!timeout && trailing) { + timeout = setTimeout(() => trailingEdge(Date.now(), args), remainingWait(time)); + } + + return result; + } + + // Helper to determine if we should invoke the function + function shouldInvoke(time: number): boolean { + const timeSinceLastCall = time - previous; + return ( + (previous === 0 && leading) || // First call with leading edge + timeSinceLastCall >= wait // Enough time has passed + ); + } + + // Cancel method + throttled.cancel = (): void => { + if (timeout) { + clearTimeout(timeout); + } + previous = 0; + timeout = undefined; + result = undefined; + pending = false; + }; + + // Flush method + throttled.flush = (): ReturnType | undefined => { + if (timeout) { + return trailingEdge(Date.now(), [] as unknown as Parameters); + } + return result; + }; + + return throttled; +} + +export const getTimestampFromRelative = (relativeTime: string): number => { + if (!relativeTime.match(/^(\d+[whdm])+$/)) { + throw new Error( + 'Invalid relative time format. Expected format: combination of numbers followed by w, h, d, or m (e.g., "1h", "2d", "30m", "1w", "1w2d")', + ); + } + let totalMilliseconds = 0; + for (const [, amount, unit] of relativeTime.matchAll(/(\d+)([whdm])/g)) { + const value = Number.parseInt(amount, 10); + switch (unit) { + case "w": + totalMilliseconds += value * 7 * 24 * 60 * 60 * 1000; + break; + case "h": + totalMilliseconds += value * 60 * 60 * 1000; + break; + case "d": + totalMilliseconds += value * 24 * 60 * 60 * 1000; + break; + case "m": + totalMilliseconds += value * 60 * 1000; + break; + } + } + return Date.now() - totalMilliseconds; +}; + +export const processTimeFilters = (date?: Date, newTime?: TimeUnit) => { + if (date) { + const hours = newTime?.HH ? Number.parseInt(newTime.HH) : 0; + const minutes = newTime?.mm ? Number.parseInt(newTime.mm) : 0; + const seconds = newTime?.ss ? Number.parseInt(newTime.ss) : 0; + date.setHours(hours, minutes, seconds, 0); + return date; + } + const now = new Date(); + return now; +}; + +export function getBaseUrl() { + if (typeof window !== "undefined") { + // browser should use relative path + return ""; + } + + if (process.env.VERCEL_URL) { + // reference for vercel.com + return `https://${process.env.VERCEL_URL}`; + } + + // assume localhost + return `http://localhost:${process.env.PORT ?? 3000}`; +} + +type DeepMergeInput = Record; + +export const deepMerge = (target: T, source: T): T => { + const result = { ...target } as T; + + for (const key in source) { + const sourceValue = source[key]; + const targetValue = result[key]; + + if ( + sourceValue !== null && + typeof sourceValue === "object" && + !Array.isArray(sourceValue) && + targetValue !== null && + typeof targetValue === "object" && + !Array.isArray(targetValue) + ) { + result[key] = deepMerge( + targetValue as DeepMergeInput, + sourceValue as DeepMergeInput, + ) as T[Extract]; + } else if (sourceValue !== undefined) { + result[key] = sourceValue; + } + } + + return result; +}; diff --git a/internal/ui/src/validation/filter.types.ts b/internal/ui/src/validation/filter.types.ts new file mode 100644 index 0000000000..5fbfcb8088 --- /dev/null +++ b/internal/ui/src/validation/filter.types.ts @@ -0,0 +1,62 @@ +import type { ReactNode } from "react"; +import { z } from "zod"; + +// Our default filterOperators that we use in query params e.g. "path=is:/oz/refactors" +export const filterOperatorEnum = z.enum(["is", "contains", "startsWith", "endsWith"]); + +export type FilterOperator = z.infer; + +export type BaseFieldConfig = { + type: T extends string ? "string" : "number"; + operators: ReadonlyArray; +}; + +export type NumberConfig = BaseFieldConfig & { + type: "number"; + validate?: (value: number) => boolean; + getColorClass?: (value: number) => string; +}; + +interface SortableConfig { + sortable?: boolean; + defaultSortDirection?: "asc" | "desc"; + sortWeight?: number; +} + +export interface SortableNumberConfig + extends NumberConfig, + SortableConfig {} +export interface SortableStringConfig + extends StringConfig, + SortableConfig {} + +export type StringConfig = BaseFieldConfig & { + type: "string"; + validValues?: readonly string[]; + validate?: (value: string) => boolean; + getColorClass?: (value: string) => string; +}; + +export type FieldConfig = + | StringConfig + | NumberConfig; + +export type FilterUrlValue = { + value: string | number; + operator: TOperator; +}; + +export type FilterValue< + TField extends string = string, + TOperator extends FilterOperator = FilterOperator, + TValue extends string | number = string | number, +> = { + id: string; + field: TField; + operator: TOperator; + value: TValue; + metadata?: { + colorClass?: string; + icon?: ReactNode; + }; +}; diff --git a/internal/ui/src/validation/utils/nuqs-parsers.ts b/internal/ui/src/validation/utils/nuqs-parsers.ts new file mode 100644 index 0000000000..dbb773974a --- /dev/null +++ b/internal/ui/src/validation/utils/nuqs-parsers.ts @@ -0,0 +1,98 @@ +import type { Parser } from "nuqs"; +import { getTimestampFromRelative } from "../../lib/utils"; +import type { FilterOperator, FilterUrlValue } from "../filter.types"; + +export const parseAsRelativeTime: Parser = { + parse: (str: string | null) => { + if (!str) { + return null; + } + + try { + // If that function doesn't throw it means we are safe + getTimestampFromRelative(str); + return str; + } catch { + return null; + } + }, + serialize: (value: string | null) => { + if (!value) { + return ""; + } + return value; + }, +}; + +export const VALID_OPERATORS: FilterOperator[] = ["is", "contains"]; + +export const parseAsFilterValueArray = ( + operators: TOperator[] = VALID_OPERATORS as TOperator[], +): Parser[]> => ({ + parse: (str: string | null) => { + if (!str) { + return []; + } + try { + return str.split(",").map((item) => { + const [operator, val] = item.split(/:(.+)/); + if (!operators.includes(operator as TOperator)) { + throw new Error("Invalid operator"); + } + return { + operator: operator as TOperator, + value: val, + }; + }); + } catch { + return []; + } + }, + serialize: (value: FilterUrlValue[]) => { + if (!value?.length) { + return ""; + } + return value.map((v) => `${v.operator}:${v.value}`).join(","); + }, +}); + +const VALID_DIRECTIONS = ["asc", "desc"] as const; +export type SortDirection = (typeof VALID_DIRECTIONS)[number]; + +export type SortUrlValue = { + column: TColumn; + direction: SortDirection; +}; + +export const parseAsSortArray = (): Parser< + SortUrlValue[] | null +> => ({ + parse: (str: string | null) => { + if (!str) { + return null; + } + try { + return str.split(",").map((item) => { + const [column, direction] = item.split(":"); + if (!column || !direction) { + throw new Error("Invalid sort format"); + } + if (!VALID_DIRECTIONS.includes(direction as SortDirection)) { + throw new Error("Invalid direction"); + } + return { + column: column as TColumn, + direction: direction as SortDirection, + }; + }); + } catch { + return null; + } + }, + serialize: (value: SortUrlValue[] | null) => { + if (!value?.length) { + return ""; + } + return value.map((v) => `${v.column}:${v.direction}`).join(","); + }, +}); diff --git a/internal/ui/src/validation/utils/structured-output-schema-generator.ts b/internal/ui/src/validation/utils/structured-output-schema-generator.ts new file mode 100644 index 0000000000..0a243a55bf --- /dev/null +++ b/internal/ui/src/validation/utils/structured-output-schema-generator.ts @@ -0,0 +1,60 @@ +import { z } from "zod"; +import type { FieldConfig } from "../filter.types"; +import { isNumberConfig, isStringConfig } from "./type-guards"; + +export function createFilterOutputSchema< + TFieldEnum extends z.ZodEnum<[string, ...string[]]>, + TOperatorEnum extends z.ZodEnum<[string, ...string[]]>, + TConfig extends Record, FieldConfig>>, +>(fieldEnum: TFieldEnum, operatorEnum: TOperatorEnum, filterFieldConfig: TConfig) { + return z.object({ + filters: z.array( + z + .object({ + field: fieldEnum, + filters: z.array( + z.object({ + operator: operatorEnum, + value: z.union([z.string(), z.number()]), + }), + ), + }) + .refine( + (data) => { + const config = filterFieldConfig[data.field as keyof TConfig]; + return data.filters.every((filter) => { + const isOperatorValid = config.operators.includes( + filter.operator as z.infer, + ); + return ( + isOperatorValid && + validateFieldValue(data.field as keyof TConfig, filter.value, filterFieldConfig) + ); + }); + }, + { message: "Invalid field/operator/value combination" }, + ), + ), + }); +} + +export function validateFieldValue>( + field: keyof TConfig, + value: string | number, + filterFieldConfig: TConfig, +): boolean { + const config = filterFieldConfig[field]; + + if (isStringConfig(config) && typeof value === "string") { + if (config.validValues) { + return config.validValues.includes(value); + } + return config.validate ? config.validate(value) : true; + } + + if (isNumberConfig(config) && typeof value === "number") { + return config.validate ? config.validate(value) : true; + } + + return true; +} diff --git a/internal/ui/src/validation/utils/transform-structured-output-filter-format.ts b/internal/ui/src/validation/utils/transform-structured-output-filter-format.ts new file mode 100644 index 0000000000..7ad3ae6d3f --- /dev/null +++ b/internal/ui/src/validation/utils/transform-structured-output-filter-format.ts @@ -0,0 +1,61 @@ +/** + * Transforms LLM-generated structured filter output into internal query filters that match + * the FilterFieldConfigs schema. Merges new filters with existing ones while maintaining + * uniqueness based on field-operator-value combinations. + * + * @example + * const llmOutput = { + * filters: [{ + * field: "status", + * filters: [ + * { operator: "is", value: "blocked" } + * ] + * }] + * }; + * const result = transformStructuredOutputToFilters(llmOutput); + * // Returns: [{ id: "uuid", field: "status", operator: "is", value: "blocked" }] + */ +export const transformStructuredOutputToFilters = < + TField extends string, + TOperator extends string, + TValue extends string | number, +>( + data: { + filters: Array<{ + field: TField; + filters: Array<{ operator: TOperator; value: TValue }>; + }>; + }, + existingFilters: Array<{ + id: string; + field: TField; + operator: TOperator; + value: TValue; + }> = [], +): Array<{ id: string; field: TField; operator: TOperator; value: TValue }> => { + const uniqueFilters = [...existingFilters]; + const seenFilters = new Set(existingFilters.map((f) => `${f.field}-${f.operator}-${f.value}`)); + + for (const filterGroup of data.filters) { + filterGroup.filters.forEach((filter) => { + const baseFilter = { + field: filterGroup.field, + operator: filter.operator, + value: filter.value, + }; + + const filterKey = `${baseFilter.field}-${baseFilter.operator}-${baseFilter.value}`; + if (seenFilters.has(filterKey)) { + return; + } + + uniqueFilters.push({ + id: crypto.randomUUID(), + ...baseFilter, + }); + seenFilters.add(filterKey); + }); + } + + return uniqueFilters; +}; diff --git a/internal/ui/src/validation/utils/type-guards.ts b/internal/ui/src/validation/utils/type-guards.ts new file mode 100644 index 0000000000..f7747e8054 --- /dev/null +++ b/internal/ui/src/validation/utils/type-guards.ts @@ -0,0 +1,9 @@ +import type { FieldConfig, NumberConfig, StringConfig } from "../filter.types"; + +// Type guards +export function isNumberConfig(config: FieldConfig): config is NumberConfig { + return config.type === "number"; +} +export function isStringConfig(config: FieldConfig): config is StringConfig { + return config.type === "string"; +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index d4c7773c41..621a7e7c34 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -943,6 +943,9 @@ importers: date-fns: specifier: ^3.6.0 version: 3.6.0 + nuqs: + specifier: ^1.17.6 + version: 1.17.6(next@14.2.25) react: specifier: ^18.2.0 version: 18.3.1 @@ -955,6 +958,9 @@ importers: tailwindcss-animate: specifier: ^1.0.7 version: 1.0.7(tailwindcss@3.4.15) + zod: + specifier: 3.23.8 + version: 3.23.8 devDependencies: '@types/node': specifier: ^20.14.9 From 84dc448c3410f2c5bff8b59112a43f1305ea4ba9 Mon Sep 17 00:00:00 2001 From: MichaelUnkey Date: Wed, 25 Jun 2025 14:34:00 -0400 Subject: [PATCH 2/8] Rabbit Changes --- .../filter/control-cloud.examples.tsx | 12 +- internal/ui/package.json | 2 +- .../ui/src/hooks/use-keyboard-shortcut.tsx | 209 +++--- internal/ui/src/lib/utils.ts | 5 +- .../ui/src/validation/utils/nuqs-parsers.ts | 10 +- .../structured-output-schema-generator.ts | 2 +- pnpm-lock.yaml | 643 +++++++++--------- 7 files changed, 429 insertions(+), 454 deletions(-) diff --git a/apps/engineering/content/design/components/filter/control-cloud.examples.tsx b/apps/engineering/content/design/components/filter/control-cloud.examples.tsx index a2a6bf63d1..8e619176b3 100644 --- a/apps/engineering/content/design/components/filter/control-cloud.examples.tsx +++ b/apps/engineering/content/design/components/filter/control-cloud.examples.tsx @@ -1,13 +1,14 @@ "use client"; import { RenderComponentWithSnippet } from "@/app/components/render"; import { ControlCloud } from "@unkey/ui"; +import type { FilterOperator } from "@unkey/ui/src/validation/filter.types"; import { useState } from "react"; // Define FilterValue type locally for examples type FilterValue = { id: string; field: string; - operator: "is" | "contains" | "startsWith" | "endsWith"; + operator: FilterOperator; value: string | number; metadata?: { colorClass?: string; @@ -18,13 +19,13 @@ type FilterValue = { // Mock filter data for examples const createMockFilter = ( field: string, - operator: string, + operator: FilterOperator, value: string | number, id?: string, ): FilterValue => ({ id: id || crypto.randomUUID(), field, - operator: operator as FilterValue["operator"], + operator, value, }); @@ -115,7 +116,7 @@ export function MultipleFilterTypes() { createMockFilter("status", "is", "404"), createMockFilter("method", "is", "POST"), createMockFilter("path", "contains", "/api/users"), - createMockFilter("duration", "gt", 1000), + createMockFilter("duration", "is", 1000), ]); const removeFilter = (id: string) => { @@ -261,7 +262,8 @@ export function InteractiveExample() { formatFieldName={formatFieldName} />
- Click buttons above to add filters, then use keyboard navigation (⌥+⇧+C to focus) + Click buttons above to add filters, then use keyboard navigation ( + {/(Mac|iPhone|iPod|iPad)/i.test(navigator.platform) ? "⌥+⇧+C" : "Alt+Shift+C"} to focus)
diff --git a/internal/ui/package.json b/internal/ui/package.json index c2d8247911..f09c2c0e48 100644 --- a/internal/ui/package.json +++ b/internal/ui/package.json @@ -12,6 +12,7 @@ "@types/react": "^18.3.11", "@unkey/tsconfig": "workspace:^", "tailwindcss": "^3.4.15", + "tailwindcss-animate": "^1.0.7", "typescript": "^5.5.3" }, "dependencies": { @@ -30,7 +31,6 @@ "react": "^18.2.0", "react-day-picker": "8.10.1", "tailwind-merge": "^2.5.4", - "tailwindcss-animate": "^1.0.7", "zod": "^3.23.5" } } diff --git a/internal/ui/src/hooks/use-keyboard-shortcut.tsx b/internal/ui/src/hooks/use-keyboard-shortcut.tsx index a5302b7a58..b189f10973 100644 --- a/internal/ui/src/hooks/use-keyboard-shortcut.tsx +++ b/internal/ui/src/hooks/use-keyboard-shortcut.tsx @@ -30,6 +30,64 @@ const defaultOptions: Required = { disabled: false, }; +// Map for O(1) key lookups +const KEY_NAME_TO_CODE_MAP = new Map([ + // Common Control/Whitespace Keys + ["enter", "Enter"], + ["tab", "Tab"], + ["space", "Space"], + ["esc", "Escape"], + ["escape", "Escape"], + ["backspace", "Backspace"], + ["delete", "Delete"], + + // Arrow Keys + ["up", "ArrowUp"], + ["arrowup", "ArrowUp"], + ["down", "ArrowDown"], + ["arrowdown", "ArrowDown"], + ["left", "ArrowLeft"], + ["arrowleft", "ArrowLeft"], + ["right", "ArrowRight"], + ["arrowright", "ArrowRight"], + + // Punctuation and Symbols + [",", "Comma"], + ["comma", "Comma"], + [".", "Period"], + ["period", "Period"], + ["/", "Slash"], + ["slash", "Slash"], + [";", "Semicolon"], + ["semicolon", "Semicolon"], + ["'", "Quote"], + ["quote", "Quote"], + ["[", "BracketLeft"], + ["bracketleft", "BracketLeft"], + ["]", "BracketRight"], + ["bracketright", "BracketRight"], + ["\\", "Backslash"], + ["backslash", "Backslash"], + ["`", "Backquote"], + ["backquote", "Backquote"], + ["-", "Minus"], + ["minus", "Minus"], + ["=", "Equal"], + ["equal", "Equal"], +]); + +// Map for O(1) modifier key lookups +const MODIFIER_KEY_MAP = new Map([ + ["ctrl", "ctrl"], + ["control", "ctrl"], + ["shift", "shift"], + ["meta", "meta"], + ["cmd", "meta"], + ["win", "meta"], + ["alt", "alt"], + ["option", "alt"], +]); + /** * Maps common key names (like 'a', 'enter', 'f1', 'comma') to their * corresponding KeyboardEvent.code values (like 'KeyA', 'Enter', 'F1', 'Comma'). @@ -40,6 +98,12 @@ const defaultOptions: Required = { export const getKeyNameToCode = (keyName: string): string | null => { const lowerKey = keyName.toLowerCase(); + // Check Map first for O(1) lookup + const mappedCode = KEY_NAME_TO_CODE_MAP.get(lowerKey); + if (mappedCode) { + return mappedCode; + } + // Basic Letters (A-Z) if (lowerKey.length === 1 && lowerKey >= "a" && lowerKey <= "z") { return `Key${lowerKey.toUpperCase()}`; // 'a' -> 'KeyA' @@ -50,40 +114,7 @@ export const getKeyNameToCode = (keyName: string): string | null => { return `Digit${lowerKey}`; // '1' -> 'Digit1' } - // Common Control/Whitespace Keys - switch (lowerKey) { - case "enter": - return "Enter"; - case "tab": - return "Tab"; - case "space": - return "Space"; // Use 'space' keyword - case "esc": - case "escape": - return "Escape"; - case "backspace": - return "Backspace"; - case "delete": - return "Delete"; - // Add more common keys as needed (e.g., Home, End, PageUp, PageDown) - } - - // Arrow Keys - switch (lowerKey) { - case "up": - case "arrowup": - return "ArrowUp"; - case "down": - case "arrowdown": - return "ArrowDown"; - case "left": - case "arrowleft": - return "ArrowLeft"; - case "right": - case "arrowright": - return "ArrowRight"; - } - + // Function Keys (F1-F12) if (lowerKey.startsWith("f") && !Number.isNaN(Number.parseInt(lowerKey.substring(1), 10))) { const fNum = Number.parseInt(lowerKey.substring(1), 10); if (fNum >= 1 && fNum <= 12) { @@ -91,42 +122,6 @@ export const getKeyNameToCode = (keyName: string): string | null => { } } - switch (lowerKey) { - case ",": - case "comma": - return "Comma"; - case ".": - case "period": - return "Period"; - case "/": - case "slash": - return "Slash"; - case ";": - case "semicolon": - return "Semicolon"; - case "'": - case "quote": - return "Quote"; - case "[": - case "bracketleft": - return "BracketLeft"; - case "]": - case "bracketright": - return "BracketRight"; - case "\\": - case "backslash": - return "Backslash"; - case "`": - case "backquote": - return "Backquote"; - case "-": - case "minus": - return "Minus"; - case "=": - case "equal": - return "Equal"; - } - console.warn( `[useKeyboardShortcut] Could not map key name "${keyName}" to a standard KeyboardEvent.code. You might need to use the code directly in the shortcut definition or expand the mapping.`, ); @@ -153,46 +148,46 @@ export const parseShortcutString = (shortcut: string): KeyCombo | null => { let keyAssigned = false; for (const part of parts) { - switch (part) { - case "ctrl": - case "control": - combo.ctrl = true; - break; - case "shift": - combo.shift = true; - break; - case "meta": - case "cmd": - case "win": - combo.meta = true; - break; - case "alt": - case "option": - combo.alt = true; - break; - default: - if (part.length > 0) { - if (keyAssigned) { - console.warn( - `[useKeyboardShortcut] Multiple non-modifier keys detected in shortcut: "${shortcut}"`, - ); - return null; - } - combo.key = part; // Store original key name - combo.code = getKeyNameToCode(part); // Attempt to get the code - if (!combo.code) { - console.warn( - `[useKeyboardShortcut] Failed to map key "${part}" to code for shortcut: "${shortcut}". Please check spelling or expand getKeyNameToCode mapping.`, - ); - return null; // Fail parsing if code cannot be determined - } - keyAssigned = true; - } else { + // Check if this is a modifier key using O(1) Map lookup + const modifierKey = MODIFIER_KEY_MAP.get(part); + if (modifierKey) { + // Set the corresponding modifier flag + switch (modifierKey) { + case "ctrl": + combo.ctrl = true; + break; + case "shift": + combo.shift = true; + break; + case "meta": + combo.meta = true; + break; + case "alt": + combo.alt = true; + break; + } + } else { + // This is not a modifier key, treat as the main key + if (part.length > 0) { + if (keyAssigned) { console.warn( - `[useKeyboardShortcut] Empty part detected in shortcut string: "${shortcut}"`, + `[useKeyboardShortcut] Multiple non-modifier keys detected in shortcut: "${shortcut}"`, ); return null; } + combo.key = part; // Store original key name + combo.code = getKeyNameToCode(part); // Attempt to get the code + if (!combo.code) { + console.warn( + `[useKeyboardShortcut] Failed to map key "${part}" to code for shortcut: "${shortcut}". Please check spelling or expand getKeyNameToCode mapping.`, + ); + return null; // Fail parsing if code cannot be determined + } + keyAssigned = true; + } else { + console.warn(`[useKeyboardShortcut] Empty part detected in shortcut string: "${shortcut}"`); + return null; + } } } @@ -233,7 +228,7 @@ export function useKeyboardShortcut( const { preventDefault, ignoreInputs, ignoreContentEditable, disabled } = mergedOptions; // Memoize callback for stability in the useEffect dependency array - // biome-ignore lint/correctness/useExhaustiveDependencies: + // biome-ignore lint/correctness/useExhaustiveDependencies: callback is intentionally excluded to prevent infinite re-renders const memoizedCallback = useCallback(callback, [callback]); useEffect(() => { diff --git a/internal/ui/src/lib/utils.ts b/internal/ui/src/lib/utils.ts index 0a12a07f7d..7eea8c488c 100644 --- a/internal/ui/src/lib/utils.ts +++ b/internal/ui/src/lib/utils.ts @@ -160,11 +160,12 @@ export const getTimestampFromRelative = (relativeTime: string): number => { export const processTimeFilters = (date?: Date, newTime?: TimeUnit) => { if (date) { + const newDate = new Date(date); const hours = newTime?.HH ? Number.parseInt(newTime.HH) : 0; const minutes = newTime?.mm ? Number.parseInt(newTime.mm) : 0; const seconds = newTime?.ss ? Number.parseInt(newTime.ss) : 0; - date.setHours(hours, minutes, seconds, 0); - return date; + newDate.setHours(hours, minutes, seconds, 0); + return newDate; } const now = new Date(); return now; diff --git a/internal/ui/src/validation/utils/nuqs-parsers.ts b/internal/ui/src/validation/utils/nuqs-parsers.ts index dbb773974a..1d33adbd6b 100644 --- a/internal/ui/src/validation/utils/nuqs-parsers.ts +++ b/internal/ui/src/validation/utils/nuqs-parsers.ts @@ -64,12 +64,10 @@ export type SortUrlValue = { direction: SortDirection; }; -export const parseAsSortArray = (): Parser< - SortUrlValue[] | null -> => ({ +export const parseAsSortArray = (): Parser[]> => ({ parse: (str: string | null) => { if (!str) { - return null; + return []; } try { return str.split(",").map((item) => { @@ -86,10 +84,10 @@ export const parseAsSortArray = (): Parser< }; }); } catch { - return null; + return []; } }, - serialize: (value: SortUrlValue[] | null) => { + serialize: (value: SortUrlValue[]) => { if (!value?.length) { return ""; } diff --git a/internal/ui/src/validation/utils/structured-output-schema-generator.ts b/internal/ui/src/validation/utils/structured-output-schema-generator.ts index 0a243a55bf..07bd001abe 100644 --- a/internal/ui/src/validation/utils/structured-output-schema-generator.ts +++ b/internal/ui/src/validation/utils/structured-output-schema-generator.ts @@ -56,5 +56,5 @@ export function validateFieldValue>( return config.validate ? config.validate(value) : true; } - return true; + return false; } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 621a7e7c34..a5a4955a38 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -626,7 +626,7 @@ importers: devDependencies: checkly: specifier: latest - version: 5.2.0(@types/node@20.14.9)(typescript@5.5.3) + version: 4.9.0(@types/node@20.14.9)(typescript@5.5.3) ts-node: specifier: 10.9.1 version: 10.9.1(@types/node@20.14.9)(typescript@5.5.3) @@ -955,9 +955,6 @@ importers: tailwind-merge: specifier: ^2.5.4 version: 2.5.4 - tailwindcss-animate: - specifier: ^1.0.7 - version: 1.0.7(tailwindcss@3.4.15) zod: specifier: 3.23.8 version: 3.23.8 @@ -974,6 +971,9 @@ importers: tailwindcss: specifier: ^3.4.15 version: 3.4.15 + tailwindcss-animate: + specifier: ^1.0.7 + version: 1.0.7(tailwindcss@3.4.15) typescript: specifier: ^5.5.3 version: 5.7.3 @@ -4279,23 +4279,6 @@ packages: dev: true optional: true - /@inquirer/checkbox@4.1.8(@types/node@20.14.9): - resolution: {integrity: sha512-d/QAsnwuHX2OPolxvYcgSj7A9DO9H6gVOy2DvBTx+P2LH2iRTo/RSGV3iwCzW024nP9hw98KIuDmdyhZQj1UQg==} - engines: {node: '>=18'} - peerDependencies: - '@types/node': '>=18' - peerDependenciesMeta: - '@types/node': - optional: true - dependencies: - '@inquirer/core': 10.1.13(@types/node@20.14.9) - '@inquirer/figures': 1.0.12 - '@inquirer/type': 3.0.7(@types/node@20.14.9) - '@types/node': 20.14.9 - ansi-escapes: 4.3.2 - yoctocolors-cjs: 2.1.2 - dev: true - /@inquirer/checkbox@4.1.8(@types/node@22.14.0): resolution: {integrity: sha512-d/QAsnwuHX2OPolxvYcgSj7A9DO9H6gVOy2DvBTx+P2LH2iRTo/RSGV3iwCzW024nP9hw98KIuDmdyhZQj1UQg==} engines: {node: '>=18'} @@ -4381,21 +4364,6 @@ packages: yoctocolors-cjs: 2.1.2 dev: true - /@inquirer/editor@4.2.13(@types/node@20.14.9): - resolution: {integrity: sha512-WbicD9SUQt/K8O5Vyk9iC2ojq5RHoCLK6itpp2fHsWe44VxxcA9z3GTWlvjSTGmMQpZr+lbVmrxdHcumJoLbMA==} - engines: {node: '>=18'} - peerDependencies: - '@types/node': '>=18' - peerDependenciesMeta: - '@types/node': - optional: true - dependencies: - '@inquirer/core': 10.1.13(@types/node@20.14.9) - '@inquirer/type': 3.0.7(@types/node@20.14.9) - '@types/node': 20.14.9 - external-editor: 3.1.0 - dev: true - /@inquirer/editor@4.2.13(@types/node@22.14.0): resolution: {integrity: sha512-WbicD9SUQt/K8O5Vyk9iC2ojq5RHoCLK6itpp2fHsWe44VxxcA9z3GTWlvjSTGmMQpZr+lbVmrxdHcumJoLbMA==} engines: {node: '>=18'} @@ -4411,21 +4379,6 @@ packages: external-editor: 3.1.0 dev: true - /@inquirer/expand@4.0.15(@types/node@20.14.9): - resolution: {integrity: sha512-4Y+pbr/U9Qcvf+N/goHzPEXiHH8680lM3Dr3Y9h9FFw4gHS+zVpbj8LfbKWIb/jayIB4aSO4pWiBTrBYWkvi5A==} - engines: {node: '>=18'} - peerDependencies: - '@types/node': '>=18' - peerDependenciesMeta: - '@types/node': - optional: true - dependencies: - '@inquirer/core': 10.1.13(@types/node@20.14.9) - '@inquirer/type': 3.0.7(@types/node@20.14.9) - '@types/node': 20.14.9 - yoctocolors-cjs: 2.1.2 - dev: true - /@inquirer/expand@4.0.15(@types/node@22.14.0): resolution: {integrity: sha512-4Y+pbr/U9Qcvf+N/goHzPEXiHH8680lM3Dr3Y9h9FFw4gHS+zVpbj8LfbKWIb/jayIB4aSO4pWiBTrBYWkvi5A==} engines: {node: '>=18'} @@ -4446,20 +4399,6 @@ packages: engines: {node: '>=18'} dev: true - /@inquirer/input@4.1.12(@types/node@20.14.9): - resolution: {integrity: sha512-xJ6PFZpDjC+tC1P8ImGprgcsrzQRsUh9aH3IZixm1lAZFK49UGHxM3ltFfuInN2kPYNfyoPRh+tU4ftsjPLKqQ==} - engines: {node: '>=18'} - peerDependencies: - '@types/node': '>=18' - peerDependenciesMeta: - '@types/node': - optional: true - dependencies: - '@inquirer/core': 10.1.13(@types/node@20.14.9) - '@inquirer/type': 3.0.7(@types/node@20.14.9) - '@types/node': 20.14.9 - dev: true - /@inquirer/input@4.1.12(@types/node@22.14.0): resolution: {integrity: sha512-xJ6PFZpDjC+tC1P8ImGprgcsrzQRsUh9aH3IZixm1lAZFK49UGHxM3ltFfuInN2kPYNfyoPRh+tU4ftsjPLKqQ==} engines: {node: '>=18'} @@ -4474,20 +4413,6 @@ packages: '@types/node': 22.14.0 dev: true - /@inquirer/number@3.0.15(@types/node@20.14.9): - resolution: {integrity: sha512-xWg+iYfqdhRiM55MvqiTCleHzszpoigUpN5+t1OMcRkJrUrw7va3AzXaxvS+Ak7Gny0j2mFSTv2JJj8sMtbV2g==} - engines: {node: '>=18'} - peerDependencies: - '@types/node': '>=18' - peerDependenciesMeta: - '@types/node': - optional: true - dependencies: - '@inquirer/core': 10.1.13(@types/node@20.14.9) - '@inquirer/type': 3.0.7(@types/node@20.14.9) - '@types/node': 20.14.9 - dev: true - /@inquirer/number@3.0.15(@types/node@22.14.0): resolution: {integrity: sha512-xWg+iYfqdhRiM55MvqiTCleHzszpoigUpN5+t1OMcRkJrUrw7va3AzXaxvS+Ak7Gny0j2mFSTv2JJj8sMtbV2g==} engines: {node: '>=18'} @@ -4502,21 +4427,6 @@ packages: '@types/node': 22.14.0 dev: true - /@inquirer/password@4.0.15(@types/node@20.14.9): - resolution: {integrity: sha512-75CT2p43DGEnfGTaqFpbDC2p2EEMrq0S+IRrf9iJvYreMy5mAWj087+mdKyLHapUEPLjN10mNvABpGbk8Wdraw==} - engines: {node: '>=18'} - peerDependencies: - '@types/node': '>=18' - peerDependenciesMeta: - '@types/node': - optional: true - dependencies: - '@inquirer/core': 10.1.13(@types/node@20.14.9) - '@inquirer/type': 3.0.7(@types/node@20.14.9) - '@types/node': 20.14.9 - ansi-escapes: 4.3.2 - dev: true - /@inquirer/password@4.0.15(@types/node@22.14.0): resolution: {integrity: sha512-75CT2p43DGEnfGTaqFpbDC2p2EEMrq0S+IRrf9iJvYreMy5mAWj087+mdKyLHapUEPLjN10mNvABpGbk8Wdraw==} engines: {node: '>=18'} @@ -4532,28 +4442,6 @@ packages: ansi-escapes: 4.3.2 dev: true - /@inquirer/prompts@7.5.3(@types/node@20.14.9): - resolution: {integrity: sha512-8YL0WiV7J86hVAxrh3fE5mDCzcTDe1670unmJRz6ArDgN+DBK1a0+rbnNWp4DUB5rPMwqD5ZP6YHl9KK1mbZRg==} - engines: {node: '>=18'} - peerDependencies: - '@types/node': '>=18' - peerDependenciesMeta: - '@types/node': - optional: true - dependencies: - '@inquirer/checkbox': 4.1.8(@types/node@20.14.9) - '@inquirer/confirm': 5.1.12(@types/node@20.14.9) - '@inquirer/editor': 4.2.13(@types/node@20.14.9) - '@inquirer/expand': 4.0.15(@types/node@20.14.9) - '@inquirer/input': 4.1.12(@types/node@20.14.9) - '@inquirer/number': 3.0.15(@types/node@20.14.9) - '@inquirer/password': 4.0.15(@types/node@20.14.9) - '@inquirer/rawlist': 4.1.3(@types/node@20.14.9) - '@inquirer/search': 3.0.15(@types/node@20.14.9) - '@inquirer/select': 4.2.3(@types/node@20.14.9) - '@types/node': 20.14.9 - dev: true - /@inquirer/prompts@7.5.3(@types/node@22.14.0): resolution: {integrity: sha512-8YL0WiV7J86hVAxrh3fE5mDCzcTDe1670unmJRz6ArDgN+DBK1a0+rbnNWp4DUB5rPMwqD5ZP6YHl9KK1mbZRg==} engines: {node: '>=18'} @@ -4576,21 +4464,6 @@ packages: '@types/node': 22.14.0 dev: true - /@inquirer/rawlist@4.1.3(@types/node@20.14.9): - resolution: {integrity: sha512-7XrV//6kwYumNDSsvJIPeAqa8+p7GJh7H5kRuxirct2cgOcSWwwNGoXDRgpNFbY/MG2vQ4ccIWCi8+IXXyFMZA==} - engines: {node: '>=18'} - peerDependencies: - '@types/node': '>=18' - peerDependenciesMeta: - '@types/node': - optional: true - dependencies: - '@inquirer/core': 10.1.13(@types/node@20.14.9) - '@inquirer/type': 3.0.7(@types/node@20.14.9) - '@types/node': 20.14.9 - yoctocolors-cjs: 2.1.2 - dev: true - /@inquirer/rawlist@4.1.3(@types/node@22.14.0): resolution: {integrity: sha512-7XrV//6kwYumNDSsvJIPeAqa8+p7GJh7H5kRuxirct2cgOcSWwwNGoXDRgpNFbY/MG2vQ4ccIWCi8+IXXyFMZA==} engines: {node: '>=18'} @@ -4606,22 +4479,6 @@ packages: yoctocolors-cjs: 2.1.2 dev: true - /@inquirer/search@3.0.15(@types/node@20.14.9): - resolution: {integrity: sha512-YBMwPxYBrADqyvP4nNItpwkBnGGglAvCLVW8u4pRmmvOsHUtCAUIMbUrLX5B3tFL1/WsLGdQ2HNzkqswMs5Uaw==} - engines: {node: '>=18'} - peerDependencies: - '@types/node': '>=18' - peerDependenciesMeta: - '@types/node': - optional: true - dependencies: - '@inquirer/core': 10.1.13(@types/node@20.14.9) - '@inquirer/figures': 1.0.12 - '@inquirer/type': 3.0.7(@types/node@20.14.9) - '@types/node': 20.14.9 - yoctocolors-cjs: 2.1.2 - dev: true - /@inquirer/search@3.0.15(@types/node@22.14.0): resolution: {integrity: sha512-YBMwPxYBrADqyvP4nNItpwkBnGGglAvCLVW8u4pRmmvOsHUtCAUIMbUrLX5B3tFL1/WsLGdQ2HNzkqswMs5Uaw==} engines: {node: '>=18'} @@ -4638,23 +4495,6 @@ packages: yoctocolors-cjs: 2.1.2 dev: true - /@inquirer/select@4.2.3(@types/node@20.14.9): - resolution: {integrity: sha512-OAGhXU0Cvh0PhLz9xTF/kx6g6x+sP+PcyTiLvCrewI99P3BBeexD+VbuwkNDvqGkk3y2h5ZiWLeRP7BFlhkUDg==} - engines: {node: '>=18'} - peerDependencies: - '@types/node': '>=18' - peerDependenciesMeta: - '@types/node': - optional: true - dependencies: - '@inquirer/core': 10.1.13(@types/node@20.14.9) - '@inquirer/figures': 1.0.12 - '@inquirer/type': 3.0.7(@types/node@20.14.9) - '@types/node': 20.14.9 - ansi-escapes: 4.3.2 - yoctocolors-cjs: 2.1.2 - dev: true - /@inquirer/select@4.2.3(@types/node@22.14.0): resolution: {integrity: sha512-OAGhXU0Cvh0PhLz9xTF/kx6g6x+sP+PcyTiLvCrewI99P3BBeexD+VbuwkNDvqGkk3y2h5ZiWLeRP7BFlhkUDg==} engines: {node: '>=18'} @@ -5557,28 +5397,89 @@ packages: fastq: 1.19.1 dev: true - /@oclif/core@4.2.8: - resolution: {integrity: sha512-OWv4Va6bERxIhrYcnUGzyhGRqktc64lJO6cZ3UwkzJDpfR8ZrbCxRfKRBBah1i8kzUlOAeAXnpbMBMah3skKwA==} - engines: {node: '>=18.0.0'} + /@oclif/color@1.0.13: + resolution: {integrity: sha512-/2WZxKCNjeHlQogCs1VBtJWlPXjwWke/9gMrwsVsrUt00g2V6LUBvwgwrxhrXepjOmq4IZ5QeNbpDMEOUlx/JA==} + engines: {node: '>=12.0.0'} dependencies: + ansi-styles: 4.3.0 + chalk: 4.1.2 + strip-ansi: 6.0.1 + supports-color: 8.1.1 + tslib: 2.8.1 + dev: true + + /@oclif/core@1.26.2: + resolution: {integrity: sha512-6jYuZgXvHfOIc9GIaS4T3CIKGTjPmfAxuMcbCbMRKJJl4aq/4xeRlEz0E8/hz8HxvxZBGvN2GwAUHlrGWQVrVw==} + engines: {node: '>=14.0.0'} + dependencies: + '@oclif/linewrap': 1.0.0 + '@oclif/screen': 3.0.8 ansi-escapes: 4.3.2 - ansis: 3.17.0 + ansi-styles: 4.3.0 + cardinal: 2.1.1 + chalk: 4.1.2 clean-stack: 3.0.1 - cli-spinners: 2.9.2 + cli-progress: 3.12.0 debug: 4.4.1(supports-color@8.1.1) ejs: 3.1.10 + fs-extra: 9.1.0 get-package-type: 0.1.0 globby: 11.1.0 + hyperlinker: 1.0.0 indent-string: 4.0.0 is-wsl: 2.2.0 - lilconfig: 3.1.3 - minimatch: 9.0.5 + js-yaml: 3.14.1 + natural-orderby: 2.0.3 + object-treeify: 1.1.33 + password-prompt: 1.1.3 semver: 7.7.2 string-width: 4.2.3 + strip-ansi: 6.0.1 supports-color: 8.1.1 + supports-hyperlinks: 2.3.0 + tslib: 2.8.1 + widest-line: 3.1.0 + wrap-ansi: 7.0.0 + dev: true + + /@oclif/core@2.8.11(@types/node@20.14.9)(typescript@5.5.3): + resolution: {integrity: sha512-9wYW6KRSWfB/D+tqeyl/jxmEz/xPXkFJGVWfKaptqHz6FPWNJREjAM945MuJL2Y8NRhMe+ScRlZ3WpdToX5aVQ==} + engines: {node: '>=14.0.0'} + dependencies: + '@types/cli-progress': 3.11.6 + ansi-escapes: 4.3.2 + ansi-styles: 4.3.0 + cardinal: 2.1.1 + chalk: 4.1.2 + clean-stack: 3.0.1 + cli-progress: 3.12.0 + debug: 4.4.1(supports-color@8.1.1) + ejs: 3.1.10 + fs-extra: 9.1.0 + get-package-type: 0.1.0 + globby: 11.1.0 + hyperlinker: 1.0.0 + indent-string: 4.0.0 + is-wsl: 2.2.0 + js-yaml: 3.14.1 + natural-orderby: 2.0.3 + object-treeify: 1.1.33 + password-prompt: 1.1.3 + semver: 7.7.2 + string-width: 4.2.3 + strip-ansi: 6.0.1 + supports-color: 8.1.1 + supports-hyperlinks: 2.3.0 + ts-node: 10.9.1(@types/node@20.14.9)(typescript@5.5.3) + tslib: 2.8.1 widest-line: 3.1.0 wordwrap: 1.0.0 wrap-ansi: 7.0.0 + transitivePeerDependencies: + - '@swc/core' + - '@swc/wasm' + - '@types/node' + - typescript dev: true /@oclif/core@4.3.3: @@ -5605,27 +5506,34 @@ packages: wrap-ansi: 7.0.0 dev: true - /@oclif/plugin-help@6.2.26: - resolution: {integrity: sha512-5KdldxEizbV3RsHOddN4oMxrX/HL6z79S94tbxEHVZ/dJKDWzfyCpgC9axNYqwmBF2pFZkozl/l7t3hCGOdalw==} - engines: {node: '>=18.0.0'} + /@oclif/linewrap@1.0.0: + resolution: {integrity: sha512-Ups2dShK52xXa8w6iBWLgcjPJWjais6KPJQq3gQ/88AY6BXoTX+MIGFPrWQO1KLMiQfoTpcLnUwloN4brrVUHw==} + dev: true + + /@oclif/plugin-help@5.1.20: + resolution: {integrity: sha512-N8xRxE/isFcdBDI8cobixEZA5toxIK5jbxpwALNTr4s8KNAtBA3ORQrSiY0fWGkcv0sCGMwZw7rJ0Izh18JPsw==} + engines: {node: '>=12.0.0'} dependencies: - '@oclif/core': 4.3.3 + '@oclif/core': 1.26.2 dev: true - /@oclif/plugin-not-found@3.2.44(@types/node@20.14.9): - resolution: {integrity: sha512-UF6GD/aDbElP6LJMZSSq72NvK0aQwtQ+fkjn0VLU9o1vNAA3M2K0tGL7lduZGQNw8LejOhr25eR4aXeRCgKb2A==} - engines: {node: '>=18.0.0'} + /@oclif/plugin-not-found@2.3.23(@types/node@20.14.9)(typescript@5.5.3): + resolution: {integrity: sha512-UZM8aolxXvqwH8WcmJxRNASDWgMoSQm/pgCdkc1AGCRevYc8+LBSO+U6nLWq+Dx8H/dn9RyIv5oiUIOGkKDlZA==} + engines: {node: '>=12.0.0'} dependencies: - '@inquirer/prompts': 7.5.3(@types/node@20.14.9) - '@oclif/core': 4.3.3 - ansis: 3.17.0 + '@oclif/color': 1.0.13 + '@oclif/core': 2.8.11(@types/node@20.14.9)(typescript@5.5.3) fast-levenshtein: 3.0.0 + lodash: 4.17.21 transitivePeerDependencies: + - '@swc/core' + - '@swc/wasm' - '@types/node' + - typescript dev: true - /@oclif/plugin-plugins@5.4.34: - resolution: {integrity: sha512-19sX+tHyR6M24GHbnedqqtk6w9QBgFZoa1y4zPmHf6VYaBeBmMBaq2dsLsdG0zv8LnWxaqguocICoxZTrV9f6A==} + /@oclif/plugin-plugins@5.4.4: + resolution: {integrity: sha512-p30fo3JPtbOqTJOX9A/8qKV/14XWt8xFgG/goVfIkuKBAO+cdY78ag8pYatlpzsYzJhO27X1MFn0WkkPWo36Ww==} engines: {node: '>=18.0.0'} dependencies: '@oclif/core': 4.3.3 @@ -5643,18 +5551,29 @@ packages: - supports-color dev: true - /@oclif/plugin-warn-if-update-available@3.1.35: - resolution: {integrity: sha512-gQfFW0UfT3msq/3O3idgBq4CA3cyXzFtrkoG7MK4FXVK0wxIdG0EVgJn4/o3jqjWO7t+93siCXyq56CGTGUZWQ==} - engines: {node: '>=18.0.0'} + /@oclif/plugin-warn-if-update-available@2.0.24(@types/node@20.14.9)(typescript@5.5.3): + resolution: {integrity: sha512-Rq8/EZ8wQawvPWS6W59Zhf/zSz/umLc3q75I1ybi7pul6YMNwf/E1eDVHytSUEQ6yQV+p3cCs034IItz4CVdjw==} + engines: {node: '>=12.0.0'} dependencies: - '@oclif/core': 4.3.3 - ansis: 3.17.0 + '@oclif/core': 2.8.11(@types/node@20.14.9)(typescript@5.5.3) + chalk: 4.1.2 debug: 4.4.1(supports-color@8.1.1) + fs-extra: 9.1.0 http-call: 5.3.0 lodash: 4.17.21 - registry-auth-token: 5.1.0 + semver: 7.7.2 transitivePeerDependencies: + - '@swc/core' + - '@swc/wasm' + - '@types/node' - supports-color + - typescript + dev: true + + /@oclif/screen@3.0.8: + resolution: {integrity: sha512-yx6KAqlt3TAHBduS2fMQtJDL2ufIHnDRArrJEOoTTuizxqmjLT+psGYOHpmMl3gvQpFJ11Hs76guUUktzAF9Bg==} + engines: {node: '>=12.0.0'} + deprecated: Package no longer supported. Contact Support at https://www.npmjs.com/support for more info. dev: true /@octokit/auth-token@2.5.0: @@ -6296,27 +6215,6 @@ packages: engines: {node: '>=16'} dev: false - /@pnpm/config.env-replace@1.1.0: - resolution: {integrity: sha512-htyl8TWnKL7K/ESFa1oW2UB5lVDxuF5DpM7tBi6Hu2LNL3mWkIzNLG6N4zoCUP1lCKNxWy/3iu8mS8MvToGd6w==} - engines: {node: '>=12.22.0'} - dev: true - - /@pnpm/network.ca-file@1.0.2: - resolution: {integrity: sha512-YcPQ8a0jwYU9bTdJDpXjMi7Brhkr1mXsXrUJvjqM2mQDgkRiz8jFaQGOdaLxgjtUfQgZhKy/O3cG/YwmgKaxLA==} - engines: {node: '>=12.22.0'} - dependencies: - graceful-fs: 4.2.10 - dev: true - - /@pnpm/npm-conf@2.3.1: - resolution: {integrity: sha512-c83qWb22rNRuB0UaVCI0uRPNRr8Z0FWnEIvT47jiHAmOIUHbBOg5XvV7pM5x+rKn9HRpjxquDbXYSXr3fAKFcw==} - engines: {node: '>=12'} - dependencies: - '@pnpm/config.env-replace': 1.1.0 - '@pnpm/network.ca-file': 1.0.2 - config-chain: 1.1.13 - dev: true - /@polka/url@1.0.0-next.29: resolution: {integrity: sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww==} dev: true @@ -9952,6 +9850,12 @@ packages: '@types/node': 20.14.9 dev: false + /@types/cli-progress@3.11.6: + resolution: {integrity: sha512-cE3+jb9WRlu+uOSAugewNpITJDt1VF8dHOopPO4IABFc3SXYL5WE/+PTz/FCdZRRfIujiWW3n3aMbv1eIGVRWA==} + dependencies: + '@types/node': 20.14.9 + dev: true + /@types/connect@3.4.38: resolution: {integrity: sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==} dependencies: @@ -10250,12 +10154,6 @@ packages: '@types/prop-types': 15.7.15 csstype: 3.1.3 - /@types/readable-stream@4.0.21: - resolution: {integrity: sha512-19eKVv9tugr03IgfXlA9UVUVRbW6IuqRO5B92Dl4a6pT7K8uaGrNS0GkxiZD0BOk6PLuXl5FhWl//eX/pzYdTQ==} - dependencies: - '@types/node': 20.14.9 - dev: true - /@types/send@0.17.5: resolution: {integrity: sha512-z6F2D3cOStZvuk2SaP6YrwkNO65iTZcwA2ZkSABegdkAh/lf+Aa/YQndZVfmEXT5vgAp6zv06VQ3ejSVjAny4w==} dependencies: @@ -10339,36 +10237,39 @@ packages: dev: true optional: true - /@typescript-eslint/types@8.24.1: - resolution: {integrity: sha512-9kqJ+2DkUXiuhoiYIUvIYjGcwle8pcPpdlfkemGvTObzgmYfJ5d0Qm6jwb4NBXP9W1I5tss0VIAnWFumz3mC5A==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + /@typescript-eslint/types@6.19.0: + resolution: {integrity: sha512-lFviGV/vYhOy3m8BJ/nAKoAyNhInTdXpftonhWle66XHAtT1ouBlkjL496b5H5hb8dWXHwtypTqgtb/DEa+j5A==} + engines: {node: ^16.0.0 || >=18.0.0} dev: true - /@typescript-eslint/typescript-estree@8.24.1(typescript@5.5.3): - resolution: {integrity: sha512-UPyy4MJ/0RE648DSKQe9g0VDSehPINiejjA6ElqnFaFIhI6ZEiZAkUI0D5MCk0bQcTf/LVqZStvQ6K4lPn/BRg==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + /@typescript-eslint/typescript-estree@6.19.0(typescript@5.5.3): + resolution: {integrity: sha512-o/zefXIbbLBZ8YJ51NlkSAt2BamrK6XOmuxSR3hynMIzzyMY33KuJ9vuMdFSXW+H0tVvdF9qBPTHA91HDb4BIQ==} + engines: {node: ^16.0.0 || >=18.0.0} peerDependencies: - typescript: '>=4.8.4 <5.8.0' + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true dependencies: - '@typescript-eslint/types': 8.24.1 - '@typescript-eslint/visitor-keys': 8.24.1 + '@typescript-eslint/types': 6.19.0 + '@typescript-eslint/visitor-keys': 6.19.0 debug: 4.4.1(supports-color@8.1.1) - fast-glob: 3.3.3 + globby: 11.1.0 is-glob: 4.0.3 - minimatch: 9.0.5 + minimatch: 9.0.3 semver: 7.7.2 - ts-api-utils: 2.1.0(typescript@5.5.3) + ts-api-utils: 1.4.3(typescript@5.5.3) typescript: 5.5.3 transitivePeerDependencies: - supports-color dev: true - /@typescript-eslint/visitor-keys@8.24.1: - resolution: {integrity: sha512-EwVHlp5l+2vp8CoqJm9KikPZgi3gbdZAtabKT9KPShGeOcJhsv4Zdo3oc8T8I0uKEmYoU4ItyxbptjF08enaxg==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + /@typescript-eslint/visitor-keys@6.19.0: + resolution: {integrity: sha512-hZaUCORLgubBvtGpp1JEFEazcuEdfxta9j4iUwdSAr7mEsYYAp3EAUyCZk3VEEqGj6W+AV4uWyrDGtrlawAsgQ==} + engines: {node: ^16.0.0 || >=18.0.0} dependencies: - '@typescript-eslint/types': 8.24.1 - eslint-visitor-keys: 4.2.1 + '@typescript-eslint/types': 6.19.0 + eslint-visitor-keys: 3.4.3 dev: true /@typescript/vfs@1.6.1(typescript@5.7.3): @@ -10822,6 +10723,11 @@ packages: dependencies: acorn: 8.15.0 + /acorn-walk@8.2.0: + resolution: {integrity: sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==} + engines: {node: '>=0.4.0'} + dev: true + /acorn-walk@8.3.2: resolution: {integrity: sha512-cjkyv4OtNCIeqhHrfS81QWXoCBPExR/J62oyEqepVw8WaQeSqpW2uhuLPh1m9eWhDuOo/jUXVTlifvesOWp/4A==} engines: {node: '>=0.4.0'} @@ -10845,6 +10751,12 @@ packages: engines: {node: '>=0.4.0'} hasBin: true + /acorn@8.8.1: + resolution: {integrity: sha512-7zFpHzhnqYKrkYdUjF1HI1bzd0VygEGX8lFk4k5zVMqHEoES+P+7TKI+EvLO9WVMJ8eekdO0aDEK044xTXwPPA==} + engines: {node: '>=0.4.0'} + hasBin: true + dev: true + /address@1.2.2: resolution: {integrity: sha512-4B/qKCfeE/ODUaAUpSwfzazo5x29WD4r3vXiWsB7I2mSDAihwEqKO+g8GELZUQSSAo5e1XTYh3ZVfLyxBc12nA==} engines: {node: '>= 10.0.0'} @@ -11060,6 +10972,10 @@ packages: resolution: {integrity: sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==} engines: {node: '>=12'} + /ansicolors@0.3.2: + resolution: {integrity: sha512-QXu7BPrP29VllRxH8GwB7x5iX5qWKAAMLqKQGWTeLWVlNHNOpVMJ91dsxQAIWXpjuW5wqvxu3Jd/nRjrJ+0pqg==} + dev: true + /ansis@3.17.0: resolution: {integrity: sha512-0qWUglt9JEqLFr3w1I1pbrChn1grhaiAR2ocX1PP/flRmxgtwTzPFFFnfIlD6aMOLQZgSuCRlidD70lvx8yhzg==} engines: {node: '>=14'} @@ -11232,6 +11148,16 @@ packages: resolution: {integrity: sha512-Az2ZTpuytrtqENulXwO3GGv1Bztugx6TT37NIo7imr/Qo0gsYiGtSdBa2B6fsXhTpVZDNfu1Qn3pk531e3q+nQ==} dev: true + /async-mqtt@2.6.3: + resolution: {integrity: sha512-mFGTtlEpOugOoLOf9H5AJyJaZUNtOVXLGGOnPaPZDPQex6W6iIOgtV+fAgam0GQbgnLfgX+Wn/QzS6d+PYfFAQ==} + dependencies: + mqtt: 4.3.8 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + dev: true + /async@3.2.6: resolution: {integrity: sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==} dev: true @@ -11471,15 +11397,6 @@ packages: readable-stream: 3.6.2 dev: true - /bl@6.1.0: - resolution: {integrity: sha512-ClDyJGQkc8ZtzdAAbAwBmhMSpwN/sC9HA8jxdYm6nVUbCfZbe2mgza4qh7AuEYyEPB/c4Kznf9s66bnsKMQDjw==} - dependencies: - '@types/readable-stream': 4.0.21 - buffer: 6.0.3 - inherits: 2.0.4 - readable-stream: 4.7.0 - dev: true - /blake3-wasm@2.1.5: resolution: {integrity: sha512-F1+K8EbfOZE49dtoPtmxUQrpXaBIl3ICvasLh+nJta0xkz+9kF/7uet9fLnwKqhDrmj6g+6K3Tw9yQPUg2ka5g==} dev: true @@ -11668,6 +11585,14 @@ packages: /caniuse-lite@1.0.30001722: resolution: {integrity: sha512-DCQHBBZtiK6JVkAGw7drvAMK0Q0POD/xZvEmDp6baiMMP6QXXk9HpD6mNYBZWhOPG6LvIDb82ITqtWjhDckHCA==} + /cardinal@2.1.1: + resolution: {integrity: sha512-JSr5eOgoEymtYHBjNWyjrMqet9Am2miJhlfKNdqLp6zoeAh0KN5dRAcxlecj5mAJrmQomgiOBj35xHLrFjqBpw==} + hasBin: true + dependencies: + ansicolors: 0.3.2 + redeyed: 2.1.1 + dev: true + /ccount@2.0.1: resolution: {integrity: sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==} @@ -11761,19 +11686,20 @@ packages: engines: {node: '>= 16'} dev: true - /checkly@5.2.0(@types/node@20.14.9)(typescript@5.5.3): - resolution: {integrity: sha512-y5VEdxuVfI66hEOMQlGKzcQsncCdewtG9ocKyA963MeOTW79v7a9dqHUJQLmBwLtUybaHVFXj7ha1RR57no9SA==} - engines: {node: '>=18.0.0'} + /checkly@4.9.0(@types/node@20.14.9)(typescript@5.5.3): + resolution: {integrity: sha512-LqohEntErF7dJaJPsEpjvr/O9wUfzBRac6DOXgFDMEw+dNi19oBAcspdOqVGjPjMoCZ9/s5b5tSJI1pusY4mJQ==} + engines: {node: '>=16.0.0'} hasBin: true dependencies: - '@oclif/core': 4.2.8 - '@oclif/plugin-help': 6.2.26 - '@oclif/plugin-not-found': 3.2.44(@types/node@20.14.9) - '@oclif/plugin-plugins': 5.4.34 - '@oclif/plugin-warn-if-update-available': 3.1.35 - '@typescript-eslint/typescript-estree': 8.24.1(typescript@5.5.3) - acorn: 8.14.0 - acorn-walk: 8.3.4 + '@oclif/core': 2.8.11(@types/node@20.14.9)(typescript@5.5.3) + '@oclif/plugin-help': 5.1.20 + '@oclif/plugin-not-found': 2.3.23(@types/node@20.14.9)(typescript@5.5.3) + '@oclif/plugin-plugins': 5.4.4 + '@oclif/plugin-warn-if-update-available': 2.0.24(@types/node@20.14.9)(typescript@5.5.3) + '@typescript-eslint/typescript-estree': 6.19.0(typescript@5.5.3) + acorn: 8.8.1 + acorn-walk: 8.2.0 + async-mqtt: 2.6.3 axios: 1.7.4 chalk: 4.1.2 ci-info: 3.8.0 @@ -11782,12 +11708,10 @@ packages: git-repo-info: 2.1.1 glob: 10.3.1 indent-string: 4.0.0 - json-stream-stringify: 3.1.6 json5: 2.2.3 jwt-decode: 3.1.2 log-symbols: 4.1.0 luxon: 3.3.0 - mqtt: 5.10.1 open: 8.4.0 p-queue: 6.6.2 prompts: 2.4.2 @@ -11796,6 +11720,8 @@ packages: tunnel: 0.0.6 uuid: 9.0.0 transitivePeerDependencies: + - '@swc/core' + - '@swc/wasm' - '@types/node' - bufferutil - debug @@ -11928,6 +11854,13 @@ packages: restore-cursor: 4.0.0 dev: true + /cli-progress@3.12.0: + resolution: {integrity: sha512-tRkV3HJ1ASwm19THiiLIXLO7Im7wlTuKnvkYaTkyoAPefqjNg7W7DHKUlGRxy9vxDvbyCYQkQozvptuMkGCg8A==} + engines: {node: '>=4'} + dependencies: + string-width: 4.2.3 + dev: true + /cli-spinners@2.9.2: resolution: {integrity: sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==} engines: {node: '>=6'} @@ -12098,8 +12031,11 @@ packages: engines: {node: '>= 12'} dev: true - /commist@3.2.0: - resolution: {integrity: sha512-4PIMoPniho+LqXmpS5d3NuGYncG6XWlkBSVGiWycL22dd42OYdUGil2CWuzklaJoNxyxUSpO4MKIBU94viWNAw==} + /commist@1.1.0: + resolution: {integrity: sha512-rraC8NXWOEjhADbZe9QBNzLAN5Q3fsTPQtBV+fEVj6xKIgDgNiEVE6ZNfHpZOqfQ21YUzfVNUXLOEZquYvQPPg==} + dependencies: + leven: 2.1.0 + minimist: 1.2.8 dev: true /commitizen@4.3.1(@types/node@22.14.0)(typescript@5.7.3): @@ -12179,6 +12115,7 @@ packages: dependencies: ini: 1.3.8 proto-list: 1.2.4 + dev: false /consola@3.4.2: resolution: {integrity: sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA==} @@ -13022,6 +12959,15 @@ packages: es-errors: 1.3.0 gopd: 1.2.0 + /duplexify@4.1.3: + resolution: {integrity: sha512-M3BmBhwJRZsSx38lZyhE53Csddgzl5R7xGJNk7CVddZD6CcmwMCH8J+7AprIrQKH7TonKxaCjcv27Qmf+sQ+oA==} + dependencies: + end-of-stream: 1.4.4 + inherits: 2.0.4 + readable-stream: 3.6.2 + stream-shift: 1.0.3 + dev: true + /eastasianwidth@0.2.0: resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} @@ -13621,11 +13567,11 @@ packages: /eslint-visitor-keys@3.4.3: resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - dev: false /eslint-visitor-keys@4.2.1: resolution: {integrity: sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + dev: false /eslint@9.28.0: resolution: {integrity: sha512-ocgh41VhRlf9+fVpe7QKzwLj9c92fDiqOj8Y3Sd4/ZmVA4Btx4PlUYPq4pp9JDyupkf1upbEXecxL2mwNV7jPQ==} @@ -13997,14 +13943,6 @@ packages: resolution: {integrity: sha512-n11RGP/lrWEFI/bWdygLxhI+pVeo1ZYIVwvvPkW7azl/rOy+F3HYRZ2K5zeE9mmkhQppyv9sQFx0JM9UabnpPQ==} dev: false - /fast-unique-numbers@8.0.13: - resolution: {integrity: sha512-7OnTFAVPefgw2eBJ1xj2PGGR9FwYzSUso9decayHgCDX4sJkHLdcsYTytTg+tYv+wKF3U8gJuSBz2jJpQV4u/g==} - engines: {node: '>=16.1.0'} - dependencies: - '@babel/runtime': 7.27.6 - tslib: 2.8.1 - dev: true - /fast-uri@3.0.6: resolution: {integrity: sha512-Atfo14OibSv5wAp4VWNsFYE1AchQRTv9cBGWET4pZWHzYshFSS9NQI6I57rdKn9croWVMbYFbLhJ+yJvmZIIHw==} @@ -14947,10 +14885,6 @@ packages: responselike: 3.0.0 dev: true - /graceful-fs@4.2.10: - resolution: {integrity: sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==} - dev: true - /graceful-fs@4.2.11: resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} @@ -15240,8 +15174,11 @@ packages: resolution: {integrity: sha512-IScLbePpkvO846sIwOtOTDjutRMWdXdJmXdMvk6gCBHxFO8d+QKOQedyZSxFTTFYRSmlgSTDtXqqq4pcenBXLQ==} dev: true - /help-me@5.0.0: - resolution: {integrity: sha512-7xgomUX6ADmcYzFik0HzAxh/73YlKR9bmFzf51CZwR+b6YtzU2m0u49hQCqV6SvlqIqsaxovfwdvbnsw3b/zpg==} + /help-me@3.0.0: + resolution: {integrity: sha512-hx73jClhyk910sidBB7ERlnhMlFsJJIBqSVMFDwPN8o2v9nmp5KgLq1Xz1Bf1fCMMZ6mPrX159iG0VLy/fPMtQ==} + dependencies: + glob: 7.2.3 + readable-stream: 3.6.2 dev: true /hex-rgb@4.3.0: @@ -15384,6 +15321,11 @@ packages: ms: 2.1.3 dev: false + /hyperlinker@1.0.0: + resolution: {integrity: sha512-Ty8UblRWFEcfSuIaajM34LdPXIhbs1ajEX/BBPv24J+enSVaEVY63xQ6lTO9VRYS5LAoghIG0IDJ+p+IPzKUQQ==} + engines: {node: '>=4'} + dev: true + /iconv-lite@0.4.24: resolution: {integrity: sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==} engines: {node: '>=0.10.0'} @@ -16145,11 +16087,6 @@ packages: resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} dev: false - /json-stream-stringify@3.1.6: - resolution: {integrity: sha512-x7fpwxOkbhFCaJDJ8vb1fBY3DdSa4AlITaz+HHILQJzdPMnHEFjxPwVUi1ALIbcIxDE0PNe/0i7frnY8QnBQog==} - engines: {node: '>=7.10.1'} - dev: true - /json2module@0.0.3: resolution: {integrity: sha512-qYGxqrRrt4GbB8IEOy1jJGypkNsjWoIMlZt4bAsmUScCA507Hbc2p1JOhBzqn45u3PWafUgH2OnzyNU7udO/GA==} hasBin: true @@ -16300,6 +16237,11 @@ packages: resolution: {integrity: sha512-Y3c3QZfvKWHX60BVOQPhLCvVGmDYWyJEiINE3drOog6KCyN2AOwvuQQzlS3uJg1J85kzpILXIUwRXULWavir+w==} dev: false + /leven@2.1.0: + resolution: {integrity: sha512-nvVPLpIHUxCUoRLrFqTgSxXJ614d8AgQoWl7zPe/2VadE8+1dpU3LBhowRuBAcuwruWtOdD8oYC9jDNJjXDPyA==} + engines: {node: '>=0.10.0'} + dev: true + /leven@3.1.0: resolution: {integrity: sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==} engines: {node: '>=6'} @@ -16501,6 +16443,13 @@ packages: /lru-cache@10.4.3: resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==} + /lru-cache@6.0.0: + resolution: {integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==} + engines: {node: '>=10'} + dependencies: + yallist: 4.0.0 + dev: true + /lru-cache@7.18.3: resolution: {integrity: sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==} engines: {node: '>=12'} @@ -17251,6 +17200,13 @@ packages: brace-expansion: 2.0.2 dev: false + /minimatch@9.0.3: + resolution: {integrity: sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==} + engines: {node: '>=16 || 14 >=14.17'} + dependencies: + brace-expansion: 2.0.2 + dev: true + /minimatch@9.0.5: resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==} engines: {node: '>=16 || 14 >=14.17'} @@ -17364,37 +17320,38 @@ packages: resolution: {integrity: sha512-EGWKgxALGMgzvxYF1UyGTy0HXX/2vHLkw6+NvDKW2jypWbHpjQuj4UMcqQWXHERJhVGKikolT06G3bcKe4fi7w==} dev: true - /mqtt-packet@9.0.2: - resolution: {integrity: sha512-MvIY0B8/qjq7bKxdN1eD+nrljoeaai+qjLJgfRn3TiMuz0pamsIWY2bFODPZMSNmabsLANXsLl4EMoWvlaTZWA==} + /mqtt-packet@6.10.0: + resolution: {integrity: sha512-ja8+mFKIHdB1Tpl6vac+sktqy3gA8t9Mduom1BA75cI+R9AHnZOiaBQwpGiWnaVJLDGRdNhQmFaAqd7tkKSMGA==} dependencies: - bl: 6.1.0 + bl: 4.1.0 debug: 4.4.1(supports-color@8.1.1) process-nextick-args: 2.0.1 transitivePeerDependencies: - supports-color dev: true - /mqtt@5.10.1: - resolution: {integrity: sha512-hXCOki8sANoQ7w+2OzJzg6qMBxTtrH9RlnVNV8panLZgnl+Gh0J/t4k6r8Az8+C7y3KAcyXtn0mmLixyUom8Sw==} - engines: {node: '>=16.0.0'} + /mqtt@4.3.8: + resolution: {integrity: sha512-2xT75uYa0kiPEF/PE0VPdavmEkoBzMT/UL9moid0rAvlCtV48qBwxD62m7Ld/4j8tSkIO1E/iqRl/S72SEOhOw==} + engines: {node: '>=10.0.0'} hasBin: true dependencies: - '@types/readable-stream': 4.0.21 - '@types/ws': 8.18.1 - commist: 3.2.0 + commist: 1.1.0 concat-stream: 2.0.0 debug: 4.4.1(supports-color@8.1.1) - help-me: 5.0.0 - lru-cache: 10.4.3 + duplexify: 4.1.3 + help-me: 3.0.0 + inherits: 2.0.4 + lru-cache: 6.0.0 minimist: 1.2.8 - mqtt-packet: 9.0.2 + mqtt-packet: 6.10.0 number-allocator: 1.0.14 - readable-stream: 4.7.0 + pump: 3.0.2 + readable-stream: 3.6.2 reinterval: 1.1.0 rfdc: 1.4.1 - split2: 4.2.0 - worker-timers: 7.1.8 - ws: 8.18.2 + split2: 3.2.2 + ws: 7.5.10 + xtend: 4.0.2 transitivePeerDependencies: - bufferutil - supports-color @@ -17513,6 +17470,10 @@ packages: resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} dev: false + /natural-orderby@2.0.3: + resolution: {integrity: sha512-p7KTHxU0CUrcOXe62Zfrb5Z13nLvPhSWR/so3kFulUQU0sgUll2Z0LwpsLN351eOOD+hRGu/F1g+6xDfPeD++Q==} + dev: true + /negotiator@0.6.3: resolution: {integrity: sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==} engines: {node: '>= 0.6'} @@ -17935,6 +17896,11 @@ packages: resolution: {integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==} engines: {node: '>= 0.4'} + /object-treeify@1.1.33: + resolution: {integrity: sha512-EFVjAYfzWqWsBMRHPMAXLCDIJnpMhdWAqR7xG6M6a2cs6PMFpl/+Z20w9zDW4vkxOFfddegBKq9Rehd0bxWE7A==} + engines: {node: '>= 10'} + dev: true + /object-treeify@4.0.1: resolution: {integrity: sha512-Y6tg5rHfsefSkfKujv2SwHulInROy/rCL5F4w0QOWxut8AnxYxf0YmNhTh95Zfyxpsudo66uqkux0ACFnyMSgQ==} engines: {node: '>= 16'} @@ -18360,6 +18326,13 @@ packages: engines: {node: '>= 0.8'} dev: true + /password-prompt@1.1.3: + resolution: {integrity: sha512-HkrjG2aJlvF0t2BMH0e2LB/EHf3Lcq3fNMzy4GYHcQblAvOl+QQji1Lx7WRBMqpVK8p+KR7bCg7oqAMXtdgqyw==} + dependencies: + ansi-escapes: 4.3.2 + cross-spawn: 7.0.6 + dev: true + /path-browserify@1.0.1: resolution: {integrity: sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==} dev: false @@ -18763,6 +18736,7 @@ packages: /proto-list@1.2.4: resolution: {integrity: sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==} + dev: false /protobufjs@7.5.3: resolution: {integrity: sha512-sildjKwVqOI2kmFDiXQ6aEB0fjYTafpEvIBs8tOR8qI4spuL9OPROLVu2qZqi/xgCfsHIwVqlaF8JBjWFHnKbw==} @@ -19380,6 +19354,12 @@ packages: unified: 11.0.5 vfile: 6.0.3 + /redeyed@2.1.1: + resolution: {integrity: sha512-FNpGGo1DycYAdnrKFxCMmKYgo/mILAqtRYbkdQD8Ep/Hk2PQ5+aEAEx+IU713RTDmuBaH0c8P5ZozurNu5ObRQ==} + dependencies: + esprima: 4.0.1 + dev: true + /reflect-metadata@0.2.2: resolution: {integrity: sha512-urBwgfrvVP/eAyXx4hluJivBKzuEbSQs9rKWCrCkbSxNv8mxPcUZKeuoF3Uy4mJl3Lwprp6yy5/39VWigZ4K6Q==} dev: true @@ -19436,13 +19416,6 @@ packages: gopd: 1.2.0 set-function-name: 2.0.2 - /registry-auth-token@5.1.0: - resolution: {integrity: sha512-GdekYuwLXLxMuFTwAPg5UKGLW/UXzQrZvH/Zj791BQif5T05T0RsaLfHc9q3ZOKi7n+BoprPD9mJ0O0k4xzUlw==} - engines: {node: '>=14'} - dependencies: - '@pnpm/npm-conf': 2.3.1 - dev: true - /rehype-katex@7.0.1: resolution: {integrity: sha512-OiM2wrZ/wuhKkigASodFoo8wimG3H12LWQaH8qSPVJn9apWKFSH3YOCtbKpBorTVw/eI7cuT21XBbvwEswbIOA==} dependencies: @@ -20429,9 +20402,10 @@ packages: resolution: {integrity: sha512-Q5thBSxp5t8WPTTJQS59LrGqOZqOsrhDGDVm8azCqIBjSBd7nd9o2PM+mDulQQkh8h//4U6hFZnc/mul8t5pWQ==} dev: true - /split2@4.2.0: - resolution: {integrity: sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==} - engines: {node: '>= 10.x'} + /split2@3.2.2: + resolution: {integrity: sha512-9NThjpgZnifTkJpzTZ7Eue85S49QwpNhZTq6GRJwObb6jnLFNGB7Qm73V5HewTROPyxD0C29xqmaI68bQtV+hg==} + dependencies: + readable-stream: 3.6.2 dev: true /sprintf-js@1.0.3: @@ -20524,6 +20498,10 @@ packages: engines: {node: '>=4', npm: '>=6'} dev: true + /stream-shift@1.0.3: + resolution: {integrity: sha512-76ORR0DO1o1hlKwTbi/DM3EXWGf3ZJYO8cXX5RJwnul2DEg2oyoZyjLNoQM8WsvZiFKCRfC1O0J7iCvie3RZmQ==} + dev: true + /streamsearch@1.1.0: resolution: {integrity: sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==} engines: {node: '>=10.0.0'} @@ -20760,6 +20738,14 @@ packages: engines: {node: '>=12'} dev: true + /supports-hyperlinks@2.3.0: + resolution: {integrity: sha512-RpsAZlpWcDwOPQA22aCH4J0t7L8JmAvsCxfOSEwm7cQs3LshN36QaTkwd70DnBOXDWGssw2eUoc8CaRWT0XunA==} + engines: {node: '>=8'} + dependencies: + has-flag: 4.0.0 + supports-color: 7.2.0 + dev: true + /supports-preserve-symlinks-flag@1.0.0: resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} engines: {node: '>= 0.4'} @@ -21271,11 +21257,11 @@ packages: - zod dev: false - /ts-api-utils@2.1.0(typescript@5.5.3): - resolution: {integrity: sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==} - engines: {node: '>=18.12'} + /ts-api-utils@1.4.3(typescript@5.5.3): + resolution: {integrity: sha512-i3eMG77UTMD0hZhgRS562pv83RC6ukSAC2GMNWc+9dieh/+jDM5u5YG+NHX6VNDRHQcHwmsTHctP9LhbC3WxVw==} + engines: {node: '>=16'} peerDependencies: - typescript: '>=4.8.4' + typescript: '>=4.2.0' dependencies: typescript: 5.5.3 dev: true @@ -22735,31 +22721,6 @@ packages: resolution: {integrity: sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==} dev: true - /worker-timers-broker@6.1.8: - resolution: {integrity: sha512-FUCJu9jlK3A8WqLTKXM9E6kAmI/dR1vAJ8dHYLMisLNB/n3GuaFIjJ7pn16ZcD1zCOf7P6H62lWIEBi+yz/zQQ==} - dependencies: - '@babel/runtime': 7.27.6 - fast-unique-numbers: 8.0.13 - tslib: 2.8.1 - worker-timers-worker: 7.0.71 - dev: true - - /worker-timers-worker@7.0.71: - resolution: {integrity: sha512-ks/5YKwZsto1c2vmljroppOKCivB/ma97g9y77MAAz2TBBjPPgpoOiS1qYQKIgvGTr2QYPT3XhJWIB6Rj2MVPQ==} - dependencies: - '@babel/runtime': 7.27.6 - tslib: 2.8.1 - dev: true - - /worker-timers@7.1.8: - resolution: {integrity: sha512-R54psRKYVLuzff7c1OTFcq/4Hue5Vlz4bFtNEIarpSiCYhpifHU3aIQI29S84o1j87ePCYqbmEJPqwBTf+3sfw==} - dependencies: - '@babel/runtime': 7.27.6 - tslib: 2.8.1 - worker-timers-broker: 6.1.8 - worker-timers-worker: 7.0.71 - dev: true - /workerd@1.20250405.0: resolution: {integrity: sha512-6+bOTz5ErQ8Ry91cAaRdipr/2o/EhNnRJAP69OKLii4nyU1A/EWsNhaZHGjBIPGKhla6qXS1BN41WEhFXUjI2w==} engines: {node: '>=16'} @@ -22895,6 +22856,19 @@ packages: /wrappy@1.0.2: resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} + /ws@7.5.10: + resolution: {integrity: sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==} + engines: {node: '>=8.3.0'} + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: ^5.0.2 + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + dev: true + /ws@8.17.1: resolution: {integrity: sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==} engines: {node: '>=10.0.0'} @@ -22959,6 +22933,11 @@ packages: engines: {node: '>=0.4.0'} dev: false + /xtend@4.0.2: + resolution: {integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==} + engines: {node: '>=0.4'} + dev: true + /y18n@5.0.8: resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} engines: {node: '>=10'} From a8023b146598d11ab69feeeff6fe9217e6c74e86 Mon Sep 17 00:00:00 2001 From: MichaelUnkey Date: Wed, 25 Jun 2025 15:44:36 -0400 Subject: [PATCH 3/8] errors --- .../filter/control-cloud.examples.tsx | 10 +- pnpm-lock.yaml | 902 ++++++++++-------- 2 files changed, 533 insertions(+), 379 deletions(-) diff --git a/apps/engineering/content/design/components/filter/control-cloud.examples.tsx b/apps/engineering/content/design/components/filter/control-cloud.examples.tsx index 8e619176b3..5632d9e2da 100644 --- a/apps/engineering/content/design/components/filter/control-cloud.examples.tsx +++ b/apps/engineering/content/design/components/filter/control-cloud.examples.tsx @@ -2,7 +2,7 @@ import { RenderComponentWithSnippet } from "@/app/components/render"; import { ControlCloud } from "@unkey/ui"; import type { FilterOperator } from "@unkey/ui/src/validation/filter.types"; -import { useState } from "react"; +import { useEffect, useState } from "react"; // Define FilterValue type locally for examples type FilterValue = { @@ -201,6 +201,12 @@ export function EmptyState() { export function InteractiveExample() { const [filters, setFilters] = useState([]); + const [isMac, setIsMac] = useState(false); + + // Client-side platform detection + useEffect(() => { + setIsMac(/(Mac|iPhone|iPod|iPad)/i.test(navigator.platform)); + }, []); const removeFilter = (id: string) => { setFilters(filters.filter((f) => f.id !== id)); @@ -263,7 +269,7 @@ export function InteractiveExample() { />
Click buttons above to add filters, then use keyboard navigation ( - {/(Mac|iPhone|iPod|iPad)/i.test(navigator.platform) ? "⌥+⇧+C" : "Alt+Shift+C"} to focus) + {isMac ? "⌥+⇧+C" : "Alt+Shift+C"} to focus)
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index a5a4955a38..269f32672b 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -128,13 +128,13 @@ importers: version: link:../../internal/tsconfig '@vitest/ui': specifier: ^1.6.0 - version: 1.6.1(vitest@1.6.1) + version: 1.6.0(vitest@1.6.1) typescript: specifier: ^5.5.3 version: 5.7.3 vitest: specifier: ^1.6.1 - version: 1.6.1(@types/node@22.14.0)(@vitest/ui@1.6.1) + version: 1.6.1(@types/node@22.14.0)(@vitest/ui@1.6.0) wrangler: specifier: ^4.10.0 version: 4.10.0(@cloudflare/workers-types@4.20240603.0) @@ -604,7 +604,7 @@ importers: version: link:../../packages/error ai: specifier: ^3.0.23 - version: 3.0.23(react@18.3.1)(solid-js@1.9.7)(svelte@4.2.20)(vue@3.5.16)(zod@3.23.8) + version: 3.0.23(react@18.3.1)(solid-js@1.9.7)(svelte@4.2.20)(vue@3.5.17)(zod@3.23.8) zod: specifier: 3.23.8 version: 3.23.8 @@ -660,7 +660,7 @@ importers: version: 5.7.3 vitest: specifier: ^1.6.1 - version: 1.6.1(@types/node@22.14.0)(@vitest/ui@1.6.1) + version: 1.6.1(@types/node@22.14.0)(@vitest/ui@1.6.1)(jsdom@26.0.0) internal/db: dependencies: @@ -703,7 +703,7 @@ importers: version: 5.7.3 vitest: specifier: ^1.6.1 - version: 1.6.1(@types/node@22.14.0)(@vitest/ui@1.6.1) + version: 1.6.1(@types/node@22.14.0)(@vitest/ui@1.6.1)(jsdom@26.0.0) internal/encryption: dependencies: @@ -722,7 +722,7 @@ importers: version: 5.7.3 vitest: specifier: ^1.6.1 - version: 1.6.1(@types/node@22.14.0)(@vitest/ui@1.6.1) + version: 1.6.1(@types/node@22.14.0)(@vitest/ui@1.6.1)(jsdom@26.0.0) internal/events: dependencies: @@ -747,7 +747,7 @@ importers: version: 5.7.3 vitest: specifier: ^1.6.1 - version: 1.6.1(@types/node@22.14.0)(@vitest/ui@1.6.1) + version: 1.6.1(@types/node@22.14.0)(@vitest/ui@1.6.1)(jsdom@26.0.0) internal/icons: dependencies: @@ -872,7 +872,7 @@ importers: version: 18.3.1 react-email: specifier: 2.1.1 - version: 2.1.1(eslint@9.28.0) + version: 2.1.1(eslint@9.29.0) resend: specifier: ^4.4.0 version: 4.4.0(react-dom@18.3.1)(react@18.3.1) @@ -945,7 +945,7 @@ importers: version: 3.6.0 nuqs: specifier: ^1.17.6 - version: 1.17.6(next@14.2.25) + version: 1.17.6(next@14.2.30) react: specifier: ^18.2.0 version: 18.3.1 @@ -1546,12 +1546,12 @@ packages: lru-cache: 10.4.3 dev: true - /@asteasolutions/zod-to-openapi@7.3.3(zod@3.23.8): - resolution: {integrity: sha512-ioiw+R+gBGAUwmDp+/gJA16tedBivzDaji5wOvWej0ZYDE0CXTSSfJfXbrBIuWKh6JQhuXgNDniJdeDueKUZTA==} + /@asteasolutions/zod-to-openapi@7.3.4(zod@3.23.8): + resolution: {integrity: sha512-/2rThQ5zPi9OzVwes6U7lK1+Yvug0iXu25olp7S0XsYmOqnyMfxH7gdSQjn/+DSOHRg7wnotwGJSyL+fBKdnEA==} peerDependencies: zod: ^3.20.2 dependencies: - openapi3-ts: 4.4.0 + openapi3-ts: 4.5.0 zod: 3.23.8 dev: false @@ -1774,8 +1774,8 @@ packages: semver: 7.7.2 dev: true - /@changesets/assemble-release-plan@6.0.8: - resolution: {integrity: sha512-y8+8LvZCkKJdbUlpXFuqcavpzJR80PN0OIfn8HZdwK7Sh6MgLXm4hKY5vu6/NDoKp8lAlM4ERZCqRMLxP4m+MQ==} + /@changesets/assemble-release-plan@6.0.9: + resolution: {integrity: sha512-tPgeeqCHIwNo8sypKlS3gOPmsS3wP0zHt67JDuL20P4QcXiw/O4Hl7oXiuLnP9yg+rXLQ2sScdV1Kkzde61iSQ==} dependencies: '@changesets/errors': 0.2.0 '@changesets/get-dependents-graph': 2.1.3 @@ -1796,12 +1796,12 @@ packages: hasBin: true dependencies: '@changesets/apply-release-plan': 7.0.12 - '@changesets/assemble-release-plan': 6.0.8 + '@changesets/assemble-release-plan': 6.0.9 '@changesets/changelog-git': 0.2.1 '@changesets/config': 3.1.1 '@changesets/errors': 0.2.0 '@changesets/get-dependents-graph': 2.1.3 - '@changesets/get-release-plan': 4.0.12 + '@changesets/get-release-plan': 4.0.13 '@changesets/git': 3.0.4 '@changesets/logger': 0.1.1 '@changesets/pre': 2.0.2 @@ -1852,10 +1852,10 @@ packages: semver: 7.7.2 dev: true - /@changesets/get-release-plan@4.0.12: - resolution: {integrity: sha512-KukdEgaafnyGryUwpHG2kZ7xJquOmWWWk5mmoeQaSvZTWH1DC5D/Sw6ClgGFYtQnOMSQhgoEbDxAbpIIayKH1g==} + /@changesets/get-release-plan@4.0.13: + resolution: {integrity: sha512-DWG1pus72FcNeXkM12tx+xtExyH/c9I1z+2aXlObH3i9YA7+WZEVaiHzHl03thpvAgWTRaH64MpfHxozfF7Dvg==} dependencies: - '@changesets/assemble-release-plan': 6.0.8 + '@changesets/assemble-release-plan': 6.0.9 '@changesets/config': 3.1.1 '@changesets/pre': 2.0.2 '@changesets/read': 0.6.5 @@ -2010,7 +2010,7 @@ packages: esbuild: 0.24.2 miniflare: 4.20250405.0 semver: 7.7.2 - vitest: 1.6.1(@types/node@22.14.0)(@vitest/ui@1.6.1) + vitest: 1.6.1(@types/node@22.14.0)(@vitest/ui@1.6.0) wrangler: 4.8.0(@cloudflare/workers-types@4.20240603.0) zod: 3.23.8 transitivePeerDependencies: @@ -3725,13 +3725,13 @@ packages: dev: true optional: true - /@eslint-community/eslint-utils@4.7.0(eslint@9.28.0): + /@eslint-community/eslint-utils@4.7.0(eslint@9.29.0): resolution: {integrity: sha512-dyybb3AcajC7uha6CvhdVRJqaKyn7w2YKqKyAN37NKYgZT36w+iRb0Dymmc5qEJ549c/S31cMMSFd75bteCpCw==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 dependencies: - eslint: 9.28.0 + eslint: 9.29.0 eslint-visitor-keys: 3.4.3 dev: false @@ -3740,8 +3740,8 @@ packages: engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} dev: false - /@eslint/config-array@0.20.0: - resolution: {integrity: sha512-fxlS1kkIjx8+vy2SjuCB94q3htSNrufYTXubwiBFeaQHbH6Ipi43gFJq2zCMt6PHhImH3Xmr0NksKDvchWlpQQ==} + /@eslint/config-array@0.20.1: + resolution: {integrity: sha512-OL0RJzC/CBzli0DrrR31qzj6d6i6Mm3HByuhflhl4LOBiWxN+3i6/t/ZQQNii4tjksXi8r2CRW1wMpWA2ULUEw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} dependencies: '@eslint/object-schema': 2.1.6 @@ -3751,8 +3751,8 @@ packages: - supports-color dev: false - /@eslint/config-helpers@0.2.2: - resolution: {integrity: sha512-+GPzk8PlG0sPpzdU5ZvIRMPidzAnZDl/s9L+y13iodqvb8leL53bTannOrQ/Im7UkpsmFU5Ily5U60LWixnmLg==} + /@eslint/config-helpers@0.2.3: + resolution: {integrity: sha512-u180qk2Um1le4yf0ruXH3PYFeEZeYC3p/4wCTKrr2U1CmGdzGi3KtY0nuPDH48UJxlKCC5RDzbcbh4X0XlqgHg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} dev: false @@ -3763,6 +3763,13 @@ packages: '@types/json-schema': 7.0.15 dev: false + /@eslint/core@0.15.1: + resolution: {integrity: sha512-bkOp+iumZCCbt1K1CmWf0R9pM5yKpDv+ZXtvSyQpudrI9kuFLp+bM2WOPXImuD/ceQuaa8f5pj93Y7zyECIGNA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + dependencies: + '@types/json-schema': 7.0.15 + dev: false + /@eslint/eslintrc@3.3.1: resolution: {integrity: sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -3780,8 +3787,8 @@ packages: - supports-color dev: false - /@eslint/js@9.28.0: - resolution: {integrity: sha512-fnqSjGWd/CoIp4EXIxWVK/sHA6DOHN4+8Ix2cX5ycOY7LG0UY8nHCU5pIp2eaE1Mc7Qd8kHspYNzYXT2ojPLzg==} + /@eslint/js@9.29.0: + resolution: {integrity: sha512-3PIF4cBw/y+1u2EazflInpV+lYsSG0aByVIQzAgb1m1MhHFSbqTyNqtBKHgWf/9Ykud+DhILS9EGkmekVhbKoQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} dev: false @@ -3790,11 +3797,11 @@ packages: engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} dev: false - /@eslint/plugin-kit@0.3.1: - resolution: {integrity: sha512-0J+zgWxHN+xXONWIyPWKFMgVuJoZuGiIFu8yxk7RJjxkzpGmyja5wRFqZIVtjDVOQpV+Rw0iOAjYPE2eQyjr0w==} + /@eslint/plugin-kit@0.3.3: + resolution: {integrity: sha512-1+WqvgNMhmlAambTvT3KPtCl/Ibr68VldY2XY40SL1CE0ZXiakFR/cbTspaF5HsnpDMvcYYoJHfl4980NBjGag==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} dependencies: - '@eslint/core': 0.14.0 + '@eslint/core': 0.15.1 levn: 0.4.1 dev: false @@ -3842,7 +3849,7 @@ packages: dependencies: '@apidevtools/json-schema-ref-parser': 11.9.3 js-yaml: 4.1.0 - prettier: 3.5.3 + prettier: 3.6.1 dev: false /@graphql-typed-document-node/core@3.2.0(graphql@16.11.0): @@ -3878,7 +3885,7 @@ packages: hono: '>=3.11.3' zod: 3.* dependencies: - '@asteasolutions/zod-to-openapi': 7.3.3(zod@3.23.8) + '@asteasolutions/zod-to-openapi': 7.3.4(zod@3.23.8) '@hono/zod-validator': 0.2.1(hono@4.6.3)(zod@3.23.8) hono: 4.6.3 zod: 3.23.8 @@ -4827,11 +4834,11 @@ packages: /@mendable/firecrawl-js@1.5.2(ws@8.18.2): resolution: {integrity: sha512-NksUAw2wtFO4ppUbhLiCnKrOsrxpocuwSZmonZaOhuL8ajwsu3uEBTJGuDuA1mp3De0we3BMs9+UoMs+Z5MBog==} dependencies: - axios: 1.9.0 + axios: 1.10.0 isows: 1.0.7(ws@8.18.2) typescript-event-target: 1.1.1 zod: 3.23.8 - zod-to-json-schema: 3.24.5(zod@3.23.8) + zod-to-json-schema: 3.24.6(zod@3.23.8) transitivePeerDependencies: - debug - ws @@ -4952,7 +4959,7 @@ packages: remark-gfm: 4.0.1 remark-math: 6.0.0 remark-smartypants: 3.0.2 - shiki: 3.6.0 + shiki: 3.7.0 unified: 11.0.5 unist-util-visit: 5.0.0 transitivePeerDependencies: @@ -4965,7 +4972,7 @@ packages: resolution: {integrity: sha512-SRxIWjpw0eBYchUJupg/LfV2PWWxa3p1eNJFHFHevwN3eejnd0BTdhd2zRfOOgi02EFl3HB/HSDhiJDJ6KRILQ==} engines: {node: '>=18.0.0'} dependencies: - axios: 1.9.0 + axios: 1.10.0 openapi-types: 12.1.3 transitivePeerDependencies: - debug @@ -4990,7 +4997,7 @@ packages: '@mintlify/openapi-parser': 0.0.7 '@mintlify/scraping': 4.0.195(@types/react@18.3.11)(react-dom@18.3.1)(react@18.3.1)(typescript@5.7.3) '@mintlify/validation': 0.1.343 - axios: 1.9.0 + axios: 1.10.0 chalk: 5.4.1 favicons: 7.2.0 fs-extra: 11.3.0 @@ -5092,7 +5099,7 @@ packages: lodash: 4.17.21 openapi-types: 12.1.3 zod: 3.23.8 - zod-to-json-schema: 3.24.5(zod@3.23.8) + zod-to-json-schema: 3.24.6(zod@3.23.8) transitivePeerDependencies: - debug dev: true @@ -5124,6 +5131,10 @@ packages: resolution: {integrity: sha512-JnzQ2cExDeG7FxJwqAksZ3aqVJrHjFwZQAEJ9gQZSoEhIow7SNoKZzju/AwQ+PLIR4NY8V0rhcVozx/2izDO0w==} dev: false + /@next/env@14.2.30: + resolution: {integrity: sha512-KBiBKrDY6kxTQWGzKjQB7QirL3PiiOkV7KW98leHFjtVRKtft76Ra5qSA/SL75xT44dp6hOcqiiJ6iievLOYug==} + dev: false + /@next/swc-darwin-arm64@14.1.0: resolution: {integrity: sha512-nUDn7TOGcIeyQni6lZHfzNoo9S0euXnu0jhsbMOmMJUBfgsnESdjN97kM7cBqQxZa8L/bM9om/S5/1dzCrW6wQ==} engines: {node: '>= 10'} @@ -5150,6 +5161,15 @@ packages: dev: false optional: true + /@next/swc-darwin-arm64@14.2.30: + resolution: {integrity: sha512-EAqfOTb3bTGh9+ewpO/jC59uACadRHM6TSA9DdxJB/6gxOpyV+zrbqeXiFTDy9uV6bmipFDkfpAskeaDcO+7/g==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [darwin] + requiresBuild: true + dev: false + optional: true + /@next/swc-darwin-x64@14.1.0: resolution: {integrity: sha512-1jgudN5haWxiAl3O1ljUS2GfupPmcftu2RYJqZiMJmmbBT5M1XDffjUtRUzP4W3cBHsrvkfOFdQ71hAreNQP6g==} engines: {node: '>= 10'} @@ -5176,6 +5196,15 @@ packages: dev: false optional: true + /@next/swc-darwin-x64@14.2.30: + resolution: {integrity: sha512-TyO7Wz1IKE2kGv8dwQ0bmPL3s44EKVencOqwIY69myoS3rdpO1NPg5xPM5ymKu7nfX4oYJrpMxv8G9iqLsnL4A==} + engines: {node: '>= 10'} + cpu: [x64] + os: [darwin] + requiresBuild: true + dev: false + optional: true + /@next/swc-linux-arm64-gnu@14.1.0: resolution: {integrity: sha512-RHo7Tcj+jllXUbK7xk2NyIDod3YcCPDZxj1WLIYxd709BQ7WuRYl3OWUNG+WUfqeQBds6kvZYlc42NJJTNi4tQ==} engines: {node: '>= 10'} @@ -5202,6 +5231,15 @@ packages: dev: false optional: true + /@next/swc-linux-arm64-gnu@14.2.30: + resolution: {integrity: sha512-I5lg1fgPJ7I5dk6mr3qCH1hJYKJu1FsfKSiTKoYwcuUf53HWTrEkwmMI0t5ojFKeA6Vu+SfT2zVy5NS0QLXV4Q==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + requiresBuild: true + dev: false + optional: true + /@next/swc-linux-arm64-musl@14.1.0: resolution: {integrity: sha512-v6kP8sHYxjO8RwHmWMJSq7VZP2nYCkRVQ0qolh2l6xroe9QjbgV8siTbduED4u0hlk0+tjS6/Tuy4n5XCp+l6g==} engines: {node: '>= 10'} @@ -5228,6 +5266,15 @@ packages: dev: false optional: true + /@next/swc-linux-arm64-musl@14.2.30: + resolution: {integrity: sha512-8GkNA+sLclQyxgzCDs2/2GSwBc92QLMrmYAmoP2xehe5MUKBLB2cgo34Yu242L1siSkwQkiV4YLdCnjwc/Micw==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + requiresBuild: true + dev: false + optional: true + /@next/swc-linux-x64-gnu@14.1.0: resolution: {integrity: sha512-zJ2pnoFYB1F4vmEVlb/eSe+VH679zT1VdXlZKX+pE66grOgjmKJHKacf82g/sWE4MQ4Rk2FMBCRnX+l6/TVYzQ==} engines: {node: '>= 10'} @@ -5254,6 +5301,15 @@ packages: dev: false optional: true + /@next/swc-linux-x64-gnu@14.2.30: + resolution: {integrity: sha512-8Ly7okjssLuBoe8qaRCcjGtcMsv79hwzn/63wNeIkzJVFVX06h5S737XNr7DZwlsbTBDOyI6qbL2BJB5n6TV/w==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + requiresBuild: true + dev: false + optional: true + /@next/swc-linux-x64-musl@14.1.0: resolution: {integrity: sha512-rbaIYFt2X9YZBSbH/CwGAjbBG2/MrACCVu2X0+kSykHzHnYH5FjHxwXLkcoJ10cX0aWCEynpu+rP76x0914atg==} engines: {node: '>= 10'} @@ -5280,6 +5336,15 @@ packages: dev: false optional: true + /@next/swc-linux-x64-musl@14.2.30: + resolution: {integrity: sha512-dBmV1lLNeX4mR7uI7KNVHsGQU+OgTG5RGFPi3tBJpsKPvOPtg9poyav/BYWrB3GPQL4dW5YGGgalwZ79WukbKQ==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + requiresBuild: true + dev: false + optional: true + /@next/swc-win32-arm64-msvc@14.1.0: resolution: {integrity: sha512-o1N5TsYc8f/HpGt39OUQpQ9AKIGApd3QLueu7hXk//2xq5Z9OxmV6sQfNp8C7qYmiOlHYODOGqNNa0e9jvchGQ==} engines: {node: '>= 10'} @@ -5306,6 +5371,15 @@ packages: dev: false optional: true + /@next/swc-win32-arm64-msvc@14.2.30: + resolution: {integrity: sha512-6MMHi2Qc1Gkq+4YLXAgbYslE1f9zMGBikKMdmQRHXjkGPot1JY3n5/Qrbg40Uvbi8//wYnydPnyvNhI1DMUW1g==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [win32] + requiresBuild: true + dev: false + optional: true + /@next/swc-win32-ia32-msvc@14.1.0: resolution: {integrity: sha512-XXIuB1DBRCFwNO6EEzCTMHT5pauwaSj4SWs7CYnME57eaReAKBXCnkUE80p/pAZcewm7hs+vGvNqDPacEXHVkw==} engines: {node: '>= 10'} @@ -5332,6 +5406,15 @@ packages: dev: false optional: true + /@next/swc-win32-ia32-msvc@14.2.30: + resolution: {integrity: sha512-pVZMnFok5qEX4RT59mK2hEVtJX+XFfak+/rjHpyFh7juiT52r177bfFKhnlafm0UOSldhXjj32b+LZIOdswGTg==} + engines: {node: '>= 10'} + cpu: [ia32] + os: [win32] + requiresBuild: true + dev: false + optional: true + /@next/swc-win32-x64-msvc@14.1.0: resolution: {integrity: sha512-9WEbVRRAqJ3YFVqEZIxUqkiO8l1nool1LmNxygr5HWF8AcSYsEpneUDhmjUVJEzO2A04+oPtZdombzzPPkTtgg==} engines: {node: '>= 10'} @@ -5358,6 +5441,15 @@ packages: dev: false optional: true + /@next/swc-win32-x64-msvc@14.2.30: + resolution: {integrity: sha512-4KCo8hMZXMjpTzs3HOqOGYYwAXymXIy7PEPAXNEcEOyKqkjiDlECumrWziy+JEF0Oi4ILHGxzgQ3YiMGG2t/Lg==} + engines: {node: '>= 10'} + cpu: [x64] + os: [win32] + requiresBuild: true + dev: false + optional: true + /@nodelib/fs.scandir@2.1.5: resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} engines: {node: '>= 8'} @@ -5482,8 +5574,8 @@ packages: - typescript dev: true - /@oclif/core@4.3.3: - resolution: {integrity: sha512-A0mk4nlVE+r34fl91OdglXVPwhhfzM59IhSxnOigqMkwxFgT8z3i2WlUgzmazzvzSccs2KM4N2HkTS3NEvW96g==} + /@oclif/core@4.4.0: + resolution: {integrity: sha512-wH5g3SLmbRutnr7UzQBSozRFEAZ7U9YGB/wFuBRr0ZghTgv5DE+KQaf6ZtU7iFb9pvkvoVRnT5XheNAtbjRDaQ==} engines: {node: '>=18.0.0'} dependencies: ansi-escapes: 4.3.2 @@ -5536,7 +5628,7 @@ packages: resolution: {integrity: sha512-p30fo3JPtbOqTJOX9A/8qKV/14XWt8xFgG/goVfIkuKBAO+cdY78ag8pYatlpzsYzJhO27X1MFn0WkkPWo36Ww==} engines: {node: '>=18.0.0'} dependencies: - '@oclif/core': 4.3.3 + '@oclif/core': 4.4.0 ansis: 3.17.0 debug: 4.4.1(supports-color@8.1.1) npm: 10.9.2 @@ -5903,7 +5995,7 @@ packages: '@opentelemetry/api': 1.4.1 '@opentelemetry/api-logs': 0.52.1 '@types/shimmer': 1.2.0 - import-in-the-middle: 1.14.0 + import-in-the-middle: 1.14.2 require-in-the-middle: 7.5.2 semver: 7.7.2 shimmer: 1.2.1 @@ -6173,8 +6265,8 @@ packages: engines: {node: '>=14'} dev: true - /@orama/orama@3.1.7: - resolution: {integrity: sha512-6yB0117ZjsgNevZw3LP+bkrZa9mU/POPVaXgzMPOBbBc35w2P3R+1vMMhEfC06kYCpd5bf0jodBaTkYQW5TVeQ==} + /@orama/orama@3.1.9: + resolution: {integrity: sha512-UXQYvN0DYl5EMOXX3O0Rwke+0R0Pd7PW/hOVwgpPd6KKJPb3RP74m3PEbEFjdTzZVLUW81o7herYXD2h4PVcGQ==} engines: {node: '>= 20.0.0'} dev: false @@ -6272,7 +6364,7 @@ packages: progress: 2.0.3 proxy-agent: 6.5.0 semver: 7.7.2 - tar-fs: 3.0.9 + tar-fs: 3.0.10 unbzip2-stream: 1.4.3 yargs: 17.7.2 transitivePeerDependencies: @@ -8942,160 +9034,160 @@ packages: engines: {node: '>= 10'} dev: false - /@rollup/rollup-android-arm-eabi@4.43.0: - resolution: {integrity: sha512-Krjy9awJl6rKbruhQDgivNbD1WuLb8xAclM4IR4cN5pHGAs2oIMMQJEiC3IC/9TZJ+QZkmZhlMO/6MBGxPidpw==} + /@rollup/rollup-android-arm-eabi@4.44.0: + resolution: {integrity: sha512-xEiEE5oDW6tK4jXCAyliuntGR+amEMO7HLtdSshVuhFnKTYoeYMyXQK7pLouAJJj5KHdwdn87bfHAR2nSdNAUA==} cpu: [arm] os: [android] requiresBuild: true dev: true optional: true - /@rollup/rollup-android-arm64@4.43.0: - resolution: {integrity: sha512-ss4YJwRt5I63454Rpj+mXCXicakdFmKnUNxr1dLK+5rv5FJgAxnN7s31a5VchRYxCFWdmnDWKd0wbAdTr0J5EA==} + /@rollup/rollup-android-arm64@4.44.0: + resolution: {integrity: sha512-uNSk/TgvMbskcHxXYHzqwiyBlJ/lGcv8DaUfcnNwict8ba9GTTNxfn3/FAoFZYgkaXXAdrAA+SLyKplyi349Jw==} cpu: [arm64] os: [android] requiresBuild: true dev: true optional: true - /@rollup/rollup-darwin-arm64@4.43.0: - resolution: {integrity: sha512-eKoL8ykZ7zz8MjgBenEF2OoTNFAPFz1/lyJ5UmmFSz5jW+7XbH1+MAgCVHy72aG59rbuQLcJeiMrP8qP5d/N0A==} + /@rollup/rollup-darwin-arm64@4.44.0: + resolution: {integrity: sha512-VGF3wy0Eq1gcEIkSCr8Ke03CWT+Pm2yveKLaDvq51pPpZza3JX/ClxXOCmTYYq3us5MvEuNRTaeyFThCKRQhOA==} cpu: [arm64] os: [darwin] requiresBuild: true dev: true optional: true - /@rollup/rollup-darwin-x64@4.43.0: - resolution: {integrity: sha512-SYwXJgaBYW33Wi/q4ubN+ldWC4DzQY62S4Ll2dgfr/dbPoF50dlQwEaEHSKrQdSjC6oIe1WgzosoaNoHCdNuMg==} + /@rollup/rollup-darwin-x64@4.44.0: + resolution: {integrity: sha512-fBkyrDhwquRvrTxSGH/qqt3/T0w5Rg0L7ZIDypvBPc1/gzjJle6acCpZ36blwuwcKD/u6oCE/sRWlUAcxLWQbQ==} cpu: [x64] os: [darwin] requiresBuild: true dev: true optional: true - /@rollup/rollup-freebsd-arm64@4.43.0: - resolution: {integrity: sha512-SV+U5sSo0yujrjzBF7/YidieK2iF6E7MdF6EbYxNz94lA+R0wKl3SiixGyG/9Klab6uNBIqsN7j4Y/Fya7wAjQ==} + /@rollup/rollup-freebsd-arm64@4.44.0: + resolution: {integrity: sha512-u5AZzdQJYJXByB8giQ+r4VyfZP+walV+xHWdaFx/1VxsOn6eWJhK2Vl2eElvDJFKQBo/hcYIBg/jaKS8ZmKeNQ==} cpu: [arm64] os: [freebsd] requiresBuild: true dev: true optional: true - /@rollup/rollup-freebsd-x64@4.43.0: - resolution: {integrity: sha512-J7uCsiV13L/VOeHJBo5SjasKiGxJ0g+nQTrBkAsmQBIdil3KhPnSE9GnRon4ejX1XDdsmK/l30IYLiAaQEO0Cg==} + /@rollup/rollup-freebsd-x64@4.44.0: + resolution: {integrity: sha512-qC0kS48c/s3EtdArkimctY7h3nHicQeEUdjJzYVJYR3ct3kWSafmn6jkNCA8InbUdge6PVx6keqjk5lVGJf99g==} cpu: [x64] os: [freebsd] requiresBuild: true dev: true optional: true - /@rollup/rollup-linux-arm-gnueabihf@4.43.0: - resolution: {integrity: sha512-gTJ/JnnjCMc15uwB10TTATBEhK9meBIY+gXP4s0sHD1zHOaIh4Dmy1X9wup18IiY9tTNk5gJc4yx9ctj/fjrIw==} + /@rollup/rollup-linux-arm-gnueabihf@4.44.0: + resolution: {integrity: sha512-x+e/Z9H0RAWckn4V2OZZl6EmV0L2diuX3QB0uM1r6BvhUIv6xBPL5mrAX2E3e8N8rEHVPwFfz/ETUbV4oW9+lQ==} cpu: [arm] os: [linux] requiresBuild: true dev: true optional: true - /@rollup/rollup-linux-arm-musleabihf@4.43.0: - resolution: {integrity: sha512-ZJ3gZynL1LDSIvRfz0qXtTNs56n5DI2Mq+WACWZ7yGHFUEirHBRt7fyIk0NsCKhmRhn7WAcjgSkSVVxKlPNFFw==} + /@rollup/rollup-linux-arm-musleabihf@4.44.0: + resolution: {integrity: sha512-1exwiBFf4PU/8HvI8s80icyCcnAIB86MCBdst51fwFmH5dyeoWVPVgmQPcKrMtBQ0W5pAs7jBCWuRXgEpRzSCg==} cpu: [arm] os: [linux] requiresBuild: true dev: true optional: true - /@rollup/rollup-linux-arm64-gnu@4.43.0: - resolution: {integrity: sha512-8FnkipasmOOSSlfucGYEu58U8cxEdhziKjPD2FIa0ONVMxvl/hmONtX/7y4vGjdUhjcTHlKlDhw3H9t98fPvyA==} + /@rollup/rollup-linux-arm64-gnu@4.44.0: + resolution: {integrity: sha512-ZTR2mxBHb4tK4wGf9b8SYg0Y6KQPjGpR4UWwTFdnmjB4qRtoATZ5dWn3KsDwGa5Z2ZBOE7K52L36J9LueKBdOQ==} cpu: [arm64] os: [linux] requiresBuild: true dev: true optional: true - /@rollup/rollup-linux-arm64-musl@4.43.0: - resolution: {integrity: sha512-KPPyAdlcIZ6S9C3S2cndXDkV0Bb1OSMsX0Eelr2Bay4EsF9yi9u9uzc9RniK3mcUGCLhWY9oLr6er80P5DE6XA==} + /@rollup/rollup-linux-arm64-musl@4.44.0: + resolution: {integrity: sha512-GFWfAhVhWGd4r6UxmnKRTBwP1qmModHtd5gkraeW2G490BpFOZkFtem8yuX2NyafIP/mGpRJgTJ2PwohQkUY/Q==} cpu: [arm64] os: [linux] requiresBuild: true dev: true optional: true - /@rollup/rollup-linux-loongarch64-gnu@4.43.0: - resolution: {integrity: sha512-HPGDIH0/ZzAZjvtlXj6g+KDQ9ZMHfSP553za7o2Odegb/BEfwJcR0Sw0RLNpQ9nC6Gy8s+3mSS9xjZ0n3rhcYg==} + /@rollup/rollup-linux-loongarch64-gnu@4.44.0: + resolution: {integrity: sha512-xw+FTGcov/ejdusVOqKgMGW3c4+AgqrfvzWEVXcNP6zq2ue+lsYUgJ+5Rtn/OTJf7e2CbgTFvzLW2j0YAtj0Gg==} cpu: [loong64] os: [linux] requiresBuild: true dev: true optional: true - /@rollup/rollup-linux-powerpc64le-gnu@4.43.0: - resolution: {integrity: sha512-gEmwbOws4U4GLAJDhhtSPWPXUzDfMRedT3hFMyRAvM9Mrnj+dJIFIeL7otsv2WF3D7GrV0GIewW0y28dOYWkmw==} + /@rollup/rollup-linux-powerpc64le-gnu@4.44.0: + resolution: {integrity: sha512-bKGibTr9IdF0zr21kMvkZT4K6NV+jjRnBoVMt2uNMG0BYWm3qOVmYnXKzx7UhwrviKnmK46IKMByMgvpdQlyJQ==} cpu: [ppc64] os: [linux] requiresBuild: true dev: true optional: true - /@rollup/rollup-linux-riscv64-gnu@4.43.0: - resolution: {integrity: sha512-XXKvo2e+wFtXZF/9xoWohHg+MuRnvO29TI5Hqe9xwN5uN8NKUYy7tXUG3EZAlfchufNCTHNGjEx7uN78KsBo0g==} + /@rollup/rollup-linux-riscv64-gnu@4.44.0: + resolution: {integrity: sha512-vV3cL48U5kDaKZtXrti12YRa7TyxgKAIDoYdqSIOMOFBXqFj2XbChHAtXquEn2+n78ciFgr4KIqEbydEGPxXgA==} cpu: [riscv64] os: [linux] requiresBuild: true dev: true optional: true - /@rollup/rollup-linux-riscv64-musl@4.43.0: - resolution: {integrity: sha512-ruf3hPWhjw6uDFsOAzmbNIvlXFXlBQ4nk57Sec8E8rUxs/AI4HD6xmiiasOOx/3QxS2f5eQMKTAwk7KHwpzr/Q==} + /@rollup/rollup-linux-riscv64-musl@4.44.0: + resolution: {integrity: sha512-TDKO8KlHJuvTEdfw5YYFBjhFts2TR0VpZsnLLSYmB7AaohJhM8ctDSdDnUGq77hUh4m/djRafw+9zQpkOanE2Q==} cpu: [riscv64] os: [linux] requiresBuild: true dev: true optional: true - /@rollup/rollup-linux-s390x-gnu@4.43.0: - resolution: {integrity: sha512-QmNIAqDiEMEvFV15rsSnjoSmO0+eJLoKRD9EAa9rrYNwO/XRCtOGM3A5A0X+wmG+XRrw9Fxdsw+LnyYiZWWcVw==} + /@rollup/rollup-linux-s390x-gnu@4.44.0: + resolution: {integrity: sha512-8541GEyktXaw4lvnGp9m84KENcxInhAt6vPWJ9RodsB/iGjHoMB2Pp5MVBCiKIRxrxzJhGCxmNzdu+oDQ7kwRA==} cpu: [s390x] os: [linux] requiresBuild: true dev: true optional: true - /@rollup/rollup-linux-x64-gnu@4.43.0: - resolution: {integrity: sha512-jAHr/S0iiBtFyzjhOkAics/2SrXE092qyqEg96e90L3t9Op8OTzS6+IX0Fy5wCt2+KqeHAkti+eitV0wvblEoQ==} + /@rollup/rollup-linux-x64-gnu@4.44.0: + resolution: {integrity: sha512-iUVJc3c0o8l9Sa/qlDL2Z9UP92UZZW1+EmQ4xfjTc1akr0iUFZNfxrXJ/R1T90h/ILm9iXEY6+iPrmYB3pXKjw==} cpu: [x64] os: [linux] requiresBuild: true dev: true optional: true - /@rollup/rollup-linux-x64-musl@4.43.0: - resolution: {integrity: sha512-3yATWgdeXyuHtBhrLt98w+5fKurdqvs8B53LaoKD7P7H7FKOONLsBVMNl9ghPQZQuYcceV5CDyPfyfGpMWD9mQ==} + /@rollup/rollup-linux-x64-musl@4.44.0: + resolution: {integrity: sha512-PQUobbhLTQT5yz/SPg116VJBgz+XOtXt8D1ck+sfJJhuEsMj2jSej5yTdp8CvWBSceu+WW+ibVL6dm0ptG5fcA==} cpu: [x64] os: [linux] requiresBuild: true dev: true optional: true - /@rollup/rollup-win32-arm64-msvc@4.43.0: - resolution: {integrity: sha512-wVzXp2qDSCOpcBCT5WRWLmpJRIzv23valvcTwMHEobkjippNf+C3ys/+wf07poPkeNix0paTNemB2XrHr2TnGw==} + /@rollup/rollup-win32-arm64-msvc@4.44.0: + resolution: {integrity: sha512-M0CpcHf8TWn+4oTxJfh7LQuTuaYeXGbk0eageVjQCKzYLsajWS/lFC94qlRqOlyC2KvRT90ZrfXULYmukeIy7w==} cpu: [arm64] os: [win32] requiresBuild: true dev: true optional: true - /@rollup/rollup-win32-ia32-msvc@4.43.0: - resolution: {integrity: sha512-fYCTEyzf8d+7diCw8b+asvWDCLMjsCEA8alvtAutqJOJp/wL5hs1rWSqJ1vkjgW0L2NB4bsYJrpKkiIPRR9dvw==} + /@rollup/rollup-win32-ia32-msvc@4.44.0: + resolution: {integrity: sha512-3XJ0NQtMAXTWFW8FqZKcw3gOQwBtVWP/u8TpHP3CRPXD7Pd6s8lLdH3sHWh8vqKCyyiI8xW5ltJScQmBU9j7WA==} cpu: [ia32] os: [win32] requiresBuild: true dev: true optional: true - /@rollup/rollup-win32-x64-msvc@4.43.0: - resolution: {integrity: sha512-SnGhLiE5rlK0ofq8kzuDkM0g7FN1s5VYY+YSMTibP7CqShxCQvqtNxTARS4xX4PFJfHjG0ZQYX9iGzI3FQh5Aw==} + /@rollup/rollup-win32-x64-msvc@4.44.0: + resolution: {integrity: sha512-Q2Mgwt+D8hd5FIPUuPDsvPR7Bguza6yTkJxspDGkZj7tBRn2y4KSWYuIXpftFSjBra76TbKerCV7rgFPQrn+wQ==} cpu: [x64] os: [win32] requiresBuild: true @@ -9123,10 +9215,10 @@ packages: hast-util-to-html: 9.0.5 dev: false - /@shikijs/core@3.6.0: - resolution: {integrity: sha512-9By7Xb3olEX0o6UeJyPLI1PE1scC4d3wcVepvtv2xbuN9/IThYN4Wcwh24rcFeASzPam11MCq8yQpwwzCgSBRw==} + /@shikijs/core@3.7.0: + resolution: {integrity: sha512-yilc0S9HvTPyahHpcum8eonYrQtmGTU0lbtwxhA6jHv4Bm1cAdlPFRCJX4AHebkCm75aKTjjRAW+DezqD1b/cg==} dependencies: - '@shikijs/types': 3.6.0 + '@shikijs/types': 3.7.0 '@shikijs/vscode-textmate': 10.0.2 '@types/hast': 3.0.4 hast-util-to-html: 9.0.5 @@ -9140,10 +9232,10 @@ packages: oniguruma-to-es: 2.3.0 dev: false - /@shikijs/engine-javascript@3.6.0: - resolution: {integrity: sha512-7YnLhZG/TU05IHMG14QaLvTW/9WiK8SEYafceccHUSXs2Qr5vJibUwsDfXDLmRi0zHdzsxrGKpSX6hnqe0k8nA==} + /@shikijs/engine-javascript@3.7.0: + resolution: {integrity: sha512-0t17s03Cbv+ZcUvv+y33GtX75WBLQELgNdVghnsdhTgU3hVcWcMsoP6Lb0nDTl95ZJfbP1mVMO0p3byVh3uuzA==} dependencies: - '@shikijs/types': 3.6.0 + '@shikijs/types': 3.7.0 '@shikijs/vscode-textmate': 10.0.2 oniguruma-to-es: 4.3.3 dev: true @@ -9155,10 +9247,10 @@ packages: '@shikijs/vscode-textmate': 10.0.2 dev: false - /@shikijs/engine-oniguruma@3.6.0: - resolution: {integrity: sha512-nmOhIZ9yT3Grd+2plmW/d8+vZ2pcQmo/UnVwXMUXAKTXdi+LK0S08Ancrz5tQQPkxvjBalpMW2aKvwXfelauvA==} + /@shikijs/engine-oniguruma@3.7.0: + resolution: {integrity: sha512-5BxcD6LjVWsGu4xyaBC5bu8LdNgPCVBnAkWTtOCs/CZxcB22L8rcoWfv7Hh/3WooVjBZmFtyxhgvkQFedPGnFw==} dependencies: - '@shikijs/types': 3.6.0 + '@shikijs/types': 3.7.0 '@shikijs/vscode-textmate': 10.0.2 dev: true @@ -9168,10 +9260,10 @@ packages: '@shikijs/types': 1.29.2 dev: false - /@shikijs/langs@3.6.0: - resolution: {integrity: sha512-IdZkQJaLBu1LCYCwkr30hNuSDfllOT8RWYVZK1tD2J03DkiagYKRxj/pDSl8Didml3xxuyzUjgtioInwEQM/TA==} + /@shikijs/langs@3.7.0: + resolution: {integrity: sha512-1zYtdfXLr9xDKLTGy5kb7O0zDQsxXiIsw1iIBcNOO8Yi5/Y1qDbJ+0VsFoqTlzdmneO8Ij35g7QKF8kcLyznCQ==} dependencies: - '@shikijs/types': 3.6.0 + '@shikijs/types': 3.7.0 dev: true /@shikijs/rehype@1.29.2: @@ -9191,10 +9283,10 @@ packages: '@shikijs/types': 1.29.2 dev: false - /@shikijs/themes@3.6.0: - resolution: {integrity: sha512-Fq2j4nWr1DF4drvmhqKq8x5vVQ27VncF8XZMBuHuQMZvUSS3NBgpqfwz/FoGe36+W6PvniZ1yDlg2d4kmYDU6w==} + /@shikijs/themes@3.7.0: + resolution: {integrity: sha512-VJx8497iZPy5zLiiCTSIaOChIcKQwR0FebwE9S3rcN0+J/GTWwQ1v/bqhTbpbY3zybPKeO8wdammqkpXc4NVjQ==} dependencies: - '@shikijs/types': 3.6.0 + '@shikijs/types': 3.7.0 dev: true /@shikijs/twoslash@1.29.2(typescript@5.7.3): @@ -9215,8 +9307,8 @@ packages: '@types/hast': 3.0.4 dev: false - /@shikijs/types@3.6.0: - resolution: {integrity: sha512-cLWFiToxYu0aAzJqhXTQsFiJRTFDAGl93IrMSBNaGSzs7ixkLfdG6pH11HipuWFGW5vyx4X47W8HDQ7eSrmBUg==} + /@shikijs/types@3.7.0: + resolution: {integrity: sha512-MGaLeaRlSWpnP0XSAum3kP3a8vtcTsITqoEPYdt3lQG3YCdQH4DnEhodkYcNMcU0uW0RffhoD1O3e0vG5eSBBg==} dependencies: '@shikijs/vscode-textmate': 10.0.2 '@types/hast': 3.0.4 @@ -9575,7 +9667,7 @@ packages: optional: true dependencies: '@swc/counter': 0.1.3 - '@swc/types': 0.1.22 + '@swc/types': 0.1.23 optionalDependencies: '@swc/core-darwin-arm64': 1.3.101 '@swc/core-darwin-x64': 1.3.101 @@ -9604,8 +9696,8 @@ packages: '@swc/counter': 0.1.3 tslib: 2.8.1 - /@swc/types@0.1.22: - resolution: {integrity: sha512-D13mY/ZA4PPEFSy6acki9eBT/3WgjMoRqNcdpIvjaYLQ44Xk5BdaL7UkDxAh6Z9UOe7tCCp67BVmZCojYp9owg==} + /@swc/types@0.1.23: + resolution: {integrity: sha512-u1iIVZV9Q0jxY+yM2vw/hZGDNudsN85bBpTqzAQ9rzkxW9D+e3aEM4Han+ow518gSewkXgjmEK0BD79ZcNVgPw==} dependencies: '@swc/counter': 0.1.3 dev: false @@ -9836,7 +9928,7 @@ packages: /@types/accepts@1.3.7: resolution: {integrity: sha512-Pay9fq2lM2wXPWbteBsRAGiWH2hig4ZE2asK+mm7kUzlxRTfL961rj89I6zV/E3PcIkDqyuBEcMxFT7rccugeQ==} dependencies: - '@types/node': 20.14.9 + '@types/node': 22.14.0 dev: false /@types/aria-query@5.0.4: @@ -9847,7 +9939,7 @@ packages: resolution: {integrity: sha512-HLFeCYgz89uk22N5Qg3dvGvsv46B8GLvKKo1zKG4NybA8U2DiEO3w9lqGg29t/tfLRJpJ6iQxnVw4OnB7MoM9g==} dependencies: '@types/connect': 3.4.38 - '@types/node': 20.14.9 + '@types/node': 22.14.0 dev: false /@types/cli-progress@3.11.6: @@ -9859,7 +9951,7 @@ packages: /@types/connect@3.4.38: resolution: {integrity: sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==} dependencies: - '@types/node': 20.14.9 + '@types/node': 22.14.0 dev: false /@types/content-disposition@0.5.9: @@ -9869,7 +9961,7 @@ packages: /@types/conventional-commits-parser@5.0.1: resolution: {integrity: sha512-7uz5EHdzz2TqoMfV7ee61Egf5y6NkcO4FB/1iCCQnbeiI1F3xzv3vK5dBCXUCLQgGYS+mUeigK1iKQzvED+QnQ==} dependencies: - '@types/node': 20.14.9 + '@types/node': 22.14.0 dev: true optional: true @@ -9891,7 +9983,7 @@ packages: '@types/connect': 3.4.38 '@types/express': 4.17.23 '@types/keygrip': 1.0.6 - '@types/node': 20.14.9 + '@types/node': 22.14.0 dev: false /@types/cors@2.8.19: @@ -9961,8 +10053,8 @@ packages: '@types/ssh2': 1.15.5 dev: true - /@types/dockerode@3.3.40: - resolution: {integrity: sha512-O1ckSFYbcYv/KcnAHMLCnKQYY8/5+6CRzpsOPcQIePHRX2jG4Gmz8uXPMCXIxTGN9OYkE5eox/L67l2sGY1UYg==} + /@types/dockerode@3.3.41: + resolution: {integrity: sha512-5kOi6bcnEjqfJ68ZNV/bBvSMLNIucc0XbRmBO4hg5OoFCoP99eSRcbMysjkzV7ZxQEmmc/zMnv4A7odwuKFzDA==} dependencies: '@types/docker-modem': 3.0.6 '@types/node': 20.14.9 @@ -9994,17 +10086,13 @@ packages: dependencies: '@types/estree': 1.0.8 - /@types/estree@1.0.7: - resolution: {integrity: sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ==} - dev: true - /@types/estree@1.0.8: resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} /@types/express-serve-static-core@4.19.6: resolution: {integrity: sha512-N4LZ2xG7DatVqhCZzOGb1Yi5lMbXSZcmdLDe9EzSndPV2HpWYWzRbaerl2n27irrm94EPpprqa8KpskPT085+A==} dependencies: - '@types/node': 20.14.9 + '@types/node': 22.14.0 '@types/qs': 6.14.0 '@types/range-parser': 1.2.7 '@types/send': 0.17.5 @@ -10063,7 +10151,7 @@ packages: '@types/http-errors': 2.0.5 '@types/keygrip': 1.0.6 '@types/koa-compose': 3.2.8 - '@types/node': 20.14.9 + '@types/node': 22.14.0 dev: false /@types/mdast@4.0.4: @@ -10101,8 +10189,8 @@ packages: resolution: {integrity: sha512-w+tIMs3rq2afQdsPJlODhoUEKzFP1ayaoyl1CcnwtIlsVe7K7bA1NGm4s3PraqTLlXnbIN84zuBlxBWo1u9BLw==} dev: false - /@types/node@18.19.111: - resolution: {integrity: sha512-90sGdgA+QLJr1F9X79tQuEut0gEYIfkX9pydI4XGRgvFo9g2JWswefI+WUSUHPYVBHYSEfTEqBxA5hQvAZB3Mw==} + /@types/node@18.19.112: + resolution: {integrity: sha512-i+Vukt9POdS/MBI7YrrkkI5fMfwFtOjphSmt4WXYLfwqsfr6z/HdCx7LqT9M7JktGob8WNgj8nFB4TbGNE4Cog==} dependencies: undici-types: 5.26.5 @@ -10158,14 +10246,14 @@ packages: resolution: {integrity: sha512-z6F2D3cOStZvuk2SaP6YrwkNO65iTZcwA2ZkSABegdkAh/lf+Aa/YQndZVfmEXT5vgAp6zv06VQ3ejSVjAny4w==} dependencies: '@types/mime': 1.3.5 - '@types/node': 20.14.9 + '@types/node': 22.14.0 dev: false /@types/serve-static@1.15.8: resolution: {integrity: sha512-roei0UY3LhpOJvjbIP6ZZFngyLKl5dskOtDhxY5THRSpO+ZI+nzJ+m5yUMzGrp89YRa7lvknKkMYjqQFGwA7Sg==} dependencies: '@types/http-errors': 2.0.5 - '@types/node': 20.14.9 + '@types/node': 22.14.0 '@types/send': 0.17.5 dev: false @@ -10189,7 +10277,7 @@ packages: /@types/ssh2@1.15.5: resolution: {integrity: sha512-N1ASjp/nXH3ovBHddRJpli4ozpk6UdDYIX4RJWFa9L1YKnzdhTlVmiGHm4DZnj/jLbqZpes4aeR30EFGQtvhQQ==} dependencies: - '@types/node': 18.19.111 + '@types/node': 18.19.112 dev: true /@types/statuses@2.0.6: @@ -10369,8 +10457,8 @@ packages: tinyrainbow: 2.0.0 dev: true - /@vitest/pretty-format@3.2.3: - resolution: {integrity: sha512-yFglXGkr9hW/yEXngO+IKMhP0jxyFw2/qys/CK4fFUZnSltD+MU7dVYGrH8rvPcK/O6feXQA+EU33gjaBBbAng==} + /@vitest/pretty-format@3.2.4: + resolution: {integrity: sha512-IVNZik8IVRJRTr9fxlitMKeJeXFFFN0JaB9PHPGQ8NKQbGpfjlTx9zO4RefN8gp7eqjNy8nyK3NZmBzOPeIxtA==} dependencies: tinyrainbow: 2.0.0 dev: true @@ -10433,19 +10521,19 @@ packages: tinyspy: 3.0.2 dev: true - /@vitest/ui@1.6.1(vitest@1.6.1): - resolution: {integrity: sha512-xa57bCPGuzEFqGjPs3vVLyqareG8DX0uMkr5U/v5vLv5/ZUrBrPL7gzxzTJedEyZxFMfsozwTIbbYfEQVo3kgg==} + /@vitest/ui@1.6.0(vitest@1.6.1): + resolution: {integrity: sha512-k3Lyo+ONLOgylctiGovRKy7V4+dIN2yxstX3eY5cWFXH6WP+ooVX79YSyi0GagdTQzLmT43BF27T0s6dOIPBXA==} peerDependencies: - vitest: 1.6.1 + vitest: 1.6.0 dependencies: - '@vitest/utils': 1.6.1 + '@vitest/utils': 1.6.0 fast-glob: 3.3.3 fflate: 0.8.2 flatted: 3.3.3 pathe: 1.1.2 picocolors: 1.1.1 sirv: 2.0.4 - vitest: 1.6.1(@types/node@22.14.0)(@vitest/ui@1.6.1) + vitest: 1.6.1(@types/node@22.14.0)(@vitest/ui@1.6.0) dev: true /@vitest/ui@1.6.1(vitest@3.1.4): @@ -10463,6 +10551,15 @@ packages: vitest: 3.1.4(@types/node@22.14.0)(@vitest/ui@1.6.1) dev: true + /@vitest/utils@1.6.0: + resolution: {integrity: sha512-21cPiuGMoMZwiOHa2i4LXkMkMkCGzA+MVFV70jRwHo95dL4x/ts5GZhML1QWuy7yfp3WzK3lRvZi3JnXTYqrBw==} + dependencies: + diff-sequences: 29.6.3 + estree-walker: 3.0.3 + loupe: 2.3.7 + pretty-format: 29.7.0 + dev: true + /@vitest/utils@1.6.1: resolution: {integrity: sha512-jOrrUvXM4Av9ZWiG1EajNto0u96kWAhJ1LmPmJhXXQx/32MecEKd10pOLYgS2BQx1TgkGhloPU1ArDW2vvaY6g==} dependencies: @@ -10476,7 +10573,7 @@ packages: resolution: {integrity: sha512-ilHM5fHhZ89MCp5aAaM9uhfl1c2JdxVxl3McqsdVyVNN6JffnEen8UMCdRTzOhGXNQGo5GNL9QugHrz727Wnng==} dependencies: '@vitest/pretty-format': 3.0.9 - loupe: 3.1.3 + loupe: 3.1.4 tinyrainbow: 2.0.0 dev: true @@ -10484,82 +10581,82 @@ packages: resolution: {integrity: sha512-yriMuO1cfFhmiGc8ataN51+9ooHRuURdfAZfwFd3usWynjzpLslZdYnRegTv32qdgtJTsj15FoeZe2g15fY1gg==} dependencies: '@vitest/pretty-format': 3.1.4 - loupe: 3.1.3 + loupe: 3.1.4 tinyrainbow: 2.0.0 dev: true - /@vue/compiler-core@3.5.16: - resolution: {integrity: sha512-AOQS2eaQOaaZQoL1u+2rCJIKDruNXVBZSiUD3chnUrsoX5ZTQMaCvXlWNIfxBJuU15r1o7+mpo5223KVtIhAgQ==} + /@vue/compiler-core@3.5.17: + resolution: {integrity: sha512-Xe+AittLbAyV0pabcN7cP7/BenRBNcteM4aSDCtRvGw0d9OL+HG1u/XHLY/kt1q4fyMeZYXyIYrsHuPSiDPosA==} dependencies: '@babel/parser': 7.27.5 - '@vue/shared': 3.5.16 + '@vue/shared': 3.5.17 entities: 4.5.0 estree-walker: 2.0.2 source-map-js: 1.2.1 dev: false - /@vue/compiler-dom@3.5.16: - resolution: {integrity: sha512-SSJIhBr/teipXiXjmWOVWLnxjNGo65Oj/8wTEQz0nqwQeP75jWZ0n4sF24Zxoht1cuJoWopwj0J0exYwCJ0dCQ==} + /@vue/compiler-dom@3.5.17: + resolution: {integrity: sha512-+2UgfLKoaNLhgfhV5Ihnk6wB4ljyW1/7wUIog2puUqajiC29Lp5R/IKDdkebh9jTbTogTbsgB+OY9cEWzG95JQ==} dependencies: - '@vue/compiler-core': 3.5.16 - '@vue/shared': 3.5.16 + '@vue/compiler-core': 3.5.17 + '@vue/shared': 3.5.17 dev: false - /@vue/compiler-sfc@3.5.16: - resolution: {integrity: sha512-rQR6VSFNpiinDy/DVUE0vHoIDUF++6p910cgcZoaAUm3POxgNOOdS/xgoll3rNdKYTYPnnbARDCZOyZ+QSe6Pw==} + /@vue/compiler-sfc@3.5.17: + resolution: {integrity: sha512-rQQxbRJMgTqwRugtjw0cnyQv9cP4/4BxWfTdRBkqsTfLOHWykLzbOc3C4GGzAmdMDxhzU/1Ija5bTjMVrddqww==} dependencies: '@babel/parser': 7.27.5 - '@vue/compiler-core': 3.5.16 - '@vue/compiler-dom': 3.5.16 - '@vue/compiler-ssr': 3.5.16 - '@vue/shared': 3.5.16 + '@vue/compiler-core': 3.5.17 + '@vue/compiler-dom': 3.5.17 + '@vue/compiler-ssr': 3.5.17 + '@vue/shared': 3.5.17 estree-walker: 2.0.2 magic-string: 0.30.17 - postcss: 8.5.4 + postcss: 8.5.6 source-map-js: 1.2.1 dev: false - /@vue/compiler-ssr@3.5.16: - resolution: {integrity: sha512-d2V7kfxbdsjrDSGlJE7my1ZzCXViEcqN6w14DOsDrUCHEA6vbnVCpRFfrc4ryCP/lCKzX2eS1YtnLE/BuC9f/A==} + /@vue/compiler-ssr@3.5.17: + resolution: {integrity: sha512-hkDbA0Q20ZzGgpj5uZjb9rBzQtIHLS78mMilwrlpWk2Ep37DYntUz0PonQ6kr113vfOEdM+zTBuJDaceNIW0tQ==} dependencies: - '@vue/compiler-dom': 3.5.16 - '@vue/shared': 3.5.16 + '@vue/compiler-dom': 3.5.17 + '@vue/shared': 3.5.17 dev: false - /@vue/reactivity@3.5.16: - resolution: {integrity: sha512-FG5Q5ee/kxhIm1p2bykPpPwqiUBV3kFySsHEQha5BJvjXdZTUfmya7wP7zC39dFuZAcf/PD5S4Lni55vGLMhvA==} + /@vue/reactivity@3.5.17: + resolution: {integrity: sha512-l/rmw2STIscWi7SNJp708FK4Kofs97zc/5aEPQh4bOsReD/8ICuBcEmS7KGwDj5ODQLYWVN2lNibKJL1z5b+Lw==} dependencies: - '@vue/shared': 3.5.16 + '@vue/shared': 3.5.17 dev: false - /@vue/runtime-core@3.5.16: - resolution: {integrity: sha512-bw5Ykq6+JFHYxrQa7Tjr+VSzw7Dj4ldR/udyBZbq73fCdJmyy5MPIFR9IX/M5Qs+TtTjuyUTCnmK3lWWwpAcFQ==} + /@vue/runtime-core@3.5.17: + resolution: {integrity: sha512-QQLXa20dHg1R0ri4bjKeGFKEkJA7MMBxrKo2G+gJikmumRS7PTD4BOU9FKrDQWMKowz7frJJGqBffYMgQYS96Q==} dependencies: - '@vue/reactivity': 3.5.16 - '@vue/shared': 3.5.16 + '@vue/reactivity': 3.5.17 + '@vue/shared': 3.5.17 dev: false - /@vue/runtime-dom@3.5.16: - resolution: {integrity: sha512-T1qqYJsG2xMGhImRUV9y/RseB9d0eCYZQ4CWca9ztCuiPj/XWNNN+lkNBuzVbia5z4/cgxdL28NoQCvC0Xcfww==} + /@vue/runtime-dom@3.5.17: + resolution: {integrity: sha512-8El0M60TcwZ1QMz4/os2MdlQECgGoVHPuLnQBU3m9h3gdNRW9xRmI8iLS4t/22OQlOE6aJvNNlBiCzPHur4H9g==} dependencies: - '@vue/reactivity': 3.5.16 - '@vue/runtime-core': 3.5.16 - '@vue/shared': 3.5.16 + '@vue/reactivity': 3.5.17 + '@vue/runtime-core': 3.5.17 + '@vue/shared': 3.5.17 csstype: 3.1.3 dev: false - /@vue/server-renderer@3.5.16(vue@3.5.16): - resolution: {integrity: sha512-BrX0qLiv/WugguGsnQUJiYOE0Fe5mZTwi6b7X/ybGB0vfrPH9z0gD/Y6WOR1sGCgX4gc25L1RYS5eYQKDMoNIg==} + /@vue/server-renderer@3.5.17(vue@3.5.17): + resolution: {integrity: sha512-BOHhm8HalujY6lmC3DbqF6uXN/K00uWiEeF22LfEsm9Q93XeJ/plHTepGwf6tqFcF7GA5oGSSAAUock3VvzaCA==} peerDependencies: - vue: 3.5.16 + vue: 3.5.17 dependencies: - '@vue/compiler-ssr': 3.5.16 - '@vue/shared': 3.5.16 - vue: 3.5.16(typescript@5.7.3) + '@vue/compiler-ssr': 3.5.17 + '@vue/shared': 3.5.17 + vue: 3.5.17(typescript@5.7.3) dev: false - /@vue/shared@3.5.16: - resolution: {integrity: sha512-c/0fWy3Jw6Z8L9FmTyYfkpM5zklnqqa9+a6dz3DvONRKW2NEbh46BP0FHuLFSWi2TnQEtp91Z6zOWNrU6QiyPg==} + /@vue/shared@3.5.17: + resolution: {integrity: sha512-CabR+UN630VnsJO/jHWYBC1YVXyMq94KKp6iF5MQgZJs5I8cmjw6oVMO1oDbtBkENSHSSn/UadWlW/OAgdmKrg==} dev: false /@webassemblyjs/ast@1.14.1: @@ -10795,7 +10892,7 @@ packages: indent-string: 5.0.0 dev: true - /ai@3.0.23(react@18.3.1)(solid-js@1.9.7)(svelte@4.2.20)(vue@3.5.16)(zod@3.23.8): + /ai@3.0.23(react@18.3.1)(solid-js@1.9.7)(svelte@4.2.20)(vue@3.5.17)(zod@3.23.8): resolution: {integrity: sha512-VL8Fx9euEtffzIu0BpLDZkACB+oU6zj4vHXSsSoT5VfwAzE009FJedOMPK1M4u60RpYw/DgwlD7OLN7XQfvSHw==} engines: {node: '>=18'} peerDependencies: @@ -10830,8 +10927,8 @@ packages: svelte: 4.2.20 swr: 2.2.0(react@18.3.1) swr-store: 0.10.6 - swrv: 1.0.4(vue@3.5.16) - vue: 3.5.16(typescript@5.7.3) + swrv: 1.0.4(vue@3.5.17) + vue: 3.5.17(typescript@5.7.3) zod: 3.23.8 zod-to-json-schema: 3.22.5(zod@3.23.8) dev: false @@ -11182,8 +11279,8 @@ packages: peerDependencies: postcss: ^8.1.0 dependencies: - browserslist: 4.25.0 - caniuse-lite: 1.0.30001722 + browserslist: 4.25.1 + caniuse-lite: 1.0.30001726 fraction.js: 4.3.7 normalize-range: 0.1.2 picocolors: 1.1.1 @@ -11198,8 +11295,8 @@ packages: peerDependencies: postcss: ^8.1.0 dependencies: - browserslist: 4.25.0 - caniuse-lite: 1.0.30001722 + browserslist: 4.25.1 + caniuse-lite: 1.0.30001726 fraction.js: 4.3.7 normalize-range: 0.1.2 picocolors: 1.1.1 @@ -11214,8 +11311,8 @@ packages: peerDependencies: postcss: ^8.1.0 dependencies: - browserslist: 4.25.0 - caniuse-lite: 1.0.30001722 + browserslist: 4.25.1 + caniuse-lite: 1.0.30001726 fraction.js: 4.3.7 normalize-range: 0.1.2 picocolors: 1.1.1 @@ -11238,24 +11335,24 @@ packages: resolution: {integrity: sha512-NZKeq9AfyQvEeNlN0zSYAaWrmBffJh3IELMZfRpJVWgrpEbtEpnjvzqBPf+mxoI287JohRDoa+/nsfqqiZmF6g==} engines: {node: '>= 6.0.0'} - /axios@1.7.4: - resolution: {integrity: sha512-DukmaFRnY6AzAALSH4J2M3k6PkaC+MfaAGdEERRWcC9q3/TWQwLpHR8ZRLKTdQ3aBDL64EdluRDjJqKw+BPZEw==} + /axios@1.10.0: + resolution: {integrity: sha512-/1xYAC4MP/HEG+3duIhFr4ZQXR4sQXOIe+o6sdqzeykGLx6Upp/1p8MHqhINOvGeP7xyNHe7tsiJByc4SSVUxw==} dependencies: follow-redirects: 1.15.9 form-data: 4.0.3 proxy-from-env: 1.1.0 transitivePeerDependencies: - debug - dev: true - /axios@1.9.0: - resolution: {integrity: sha512-re4CqKTJaURpzbLHtIi6XpDv20/CnpXOtjRY5/CU32L8gU8ek9UIivcfvSWvmKEngmVbrUtPpdDwWDWL7DNHvg==} + /axios@1.7.4: + resolution: {integrity: sha512-DukmaFRnY6AzAALSH4J2M3k6PkaC+MfaAGdEERRWcC9q3/TWQwLpHR8ZRLKTdQ3aBDL64EdluRDjJqKw+BPZEw==} dependencies: follow-redirects: 1.15.9 form-data: 4.0.3 proxy-from-env: 1.1.0 transitivePeerDependencies: - debug + dev: true /axobject-query@4.1.0: resolution: {integrity: sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==} @@ -11438,15 +11535,15 @@ packages: dependencies: fill-range: 7.1.1 - /browserslist@4.25.0: - resolution: {integrity: sha512-PJ8gYKeS5e/whHBh8xrwYK+dAvEj7JXtz6uTucnMRB8OiGTsKccFekoRrjajPBHV8oOY+2tI4uxeceSimKwMFA==} + /browserslist@4.25.1: + resolution: {integrity: sha512-KGj0KoOMXLpSNkkEI6Z6mShmQy0bc1I+T7K9N81k4WWMrfz+6fQ6es80B/YLAeRoKvjYE1YSHHOW1qe9xIVzHw==} engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} hasBin: true dependencies: - caniuse-lite: 1.0.30001722 - electron-to-chromium: 1.5.166 + caniuse-lite: 1.0.30001726 + electron-to-chromium: 1.5.174 node-releases: 2.0.19 - update-browserslist-db: 1.1.3(browserslist@4.25.0) + update-browserslist-db: 1.1.3(browserslist@4.25.1) /buffer-crc32@0.2.13: resolution: {integrity: sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==} @@ -11582,8 +11679,8 @@ packages: resolution: {integrity: sha512-dU+Tx2fsypxTgtLoE36npi3UqcjSSMNYfkqgmoEhtZrraP5VWq0K7FkWVTYa8eMPtnU/G2txVsfdCJTn9uzpuQ==} dev: false - /caniuse-lite@1.0.30001722: - resolution: {integrity: sha512-DCQHBBZtiK6JVkAGw7drvAMK0Q0POD/xZvEmDp6baiMMP6QXXk9HpD6mNYBZWhOPG6LvIDb82ITqtWjhDckHCA==} + /caniuse-lite@1.0.30001726: + resolution: {integrity: sha512-VQAUIUzBiZ/UnlM28fSp2CRF3ivUn1BWEvxMcVTNwpw91Py1pGbPIyIKtd+tzct9C3ouceCVdGAXxZOpZAsgdw==} /cardinal@2.1.1: resolution: {integrity: sha512-JSr5eOgoEymtYHBjNWyjrMqet9Am2miJhlfKNdqLp6zoeAh0KN5dRAcxlecj5mAJrmQomgiOBj35xHLrFjqBpw==} @@ -11624,8 +11721,8 @@ packages: assertion-error: 2.0.1 check-error: 2.1.1 deep-eql: 5.0.2 - loupe: 3.1.3 - pathval: 2.0.0 + loupe: 3.1.4 + pathval: 2.0.1 dev: true /chalk@1.1.3: @@ -12294,8 +12391,8 @@ packages: engines: {node: '>=4'} hasBin: true - /cssstyle@4.4.0: - resolution: {integrity: sha512-W0Y2HOXlPkb2yaKrCVRjinYKciu/qSLEmK0K9mcfDei3zwlnHFEHAs/Du3cIRwPqY+J4JsiBzUjoHyc8RsJ03A==} + /cssstyle@4.6.0: + resolution: {integrity: sha512-2z+rWdzbbSZv6/rhtvzvqeZQHrBaqgogqt85sqFNbabZOuFbCVFb8kPeEtZjiKkbrm395irpNKiYeFeLiQnFPg==} engines: {node: '>=18'} dependencies: '@asamuzakjp/css-color': 3.2.0 @@ -12518,8 +12615,8 @@ packages: resolution: {integrity: sha512-8vDa8Qxvr/+d94hSh5P3IJwI5t8/c0KsMp+g8bNw9cY2icONa5aPfvKeieW1WlG0WQYwwhJ7mjui2xtiePQSXw==} dev: true - /decode-named-character-reference@1.1.0: - resolution: {integrity: sha512-Wy+JTSbFThEOXQIR2L6mxJvEs+veIzpmqD7ynWxMXGpnk3smkHQOp6forLdHsKpAMW9iJpaBBIxz285t1n1C3w==} + /decode-named-character-reference@1.2.0: + resolution: {integrity: sha512-c6fcElNV6ShtZXmsgNgFFV5tVX2PaV4g+MOAkb8eXHvn6sryJBrZa9r0zV6+dtTyoCKxtDy5tyQ5ZwQuidtd+Q==} dependencies: character-entities: 2.0.2 @@ -12551,7 +12648,7 @@ packages: dependencies: is-arguments: 1.2.0 is-date-object: 1.1.0 - is-regex: 1.2.1 + is-regex: 1.1.4 object-is: 1.1.6 object-keys: 1.1.1 regexp.prototype.flags: 1.5.4 @@ -12962,7 +13059,7 @@ packages: /duplexify@4.1.3: resolution: {integrity: sha512-M3BmBhwJRZsSx38lZyhE53Csddgzl5R7xGJNk7CVddZD6CcmwMCH8J+7AprIrQKH7TonKxaCjcv27Qmf+sQ+oA==} dependencies: - end-of-stream: 1.4.4 + end-of-stream: 1.4.5 inherits: 2.0.4 readable-stream: 3.6.2 stream-shift: 1.0.3 @@ -13002,8 +13099,8 @@ packages: jake: 10.9.2 dev: true - /electron-to-chromium@1.5.166: - resolution: {integrity: sha512-QPWqHL0BglzPYyJJ1zSSmwFFL6MFXhbACOCcsCdUMCkzPdS9/OIBVxg516X/Ado2qwAq8k0nJJ7phQPCqiaFAw==} + /electron-to-chromium@1.5.174: + resolution: {integrity: sha512-HE43yYdUUiJVjewV2A9EP8o89Kb4AqMKplMQP2IxEPUws1Etu/ZkdsgUDabUZ/WmbP4ZbvJDOcunvbBUPPIfmw==} /emoji-regex-xs@1.0.0: resolution: {integrity: sha512-LRlerrMYoIDrT6jgpeZ2YYl/L8EulRTt5hQcYjy5AInh7HWXKimpqx68aknBFpGL2+/IcogTcaydJEgaTmOpDg==} @@ -13029,8 +13126,8 @@ packages: engines: {node: '>= 0.8'} dev: true - /end-of-stream@1.4.4: - resolution: {integrity: sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==} + /end-of-stream@1.4.5: + resolution: {integrity: sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==} dependencies: once: 1.4.0 dev: true @@ -13092,8 +13189,8 @@ packages: - utf-8-validate dev: true - /enhanced-resolve@5.18.1: - resolution: {integrity: sha512-ZSW3ma5GkcQBIpwZTSRAI8N71Uuwgs93IezB7mf7R60tC8ZbJideoDNKjHn2O9KIlx6rkGTTEk1xUCK2E1Y2Yg==} + /enhanced-resolve@5.18.2: + resolution: {integrity: sha512-6Jw4sE1maoRJo3q8MsSIn2onJFbLTOjY9hlx4DZXmOKvLRd1Ok2kXmAGXaafL2+ijsJZ1ClYbl/pmqr9+k4iUQ==} engines: {node: '>=10.13.0'} dependencies: graceful-fs: 4.2.11 @@ -13521,31 +13618,31 @@ packages: source-map: 0.6.1 dev: true - /eslint-config-prettier@9.0.0(eslint@9.28.0): + /eslint-config-prettier@9.0.0(eslint@9.29.0): resolution: {integrity: sha512-IcJsTkJae2S35pRsRAwoCE+925rJJStOdkKnLVgtE+tEpqU0EVVM7OqrwxqgptKdX29NUwC82I5pXsGFIgSevw==} hasBin: true peerDependencies: eslint: '>=7.0.0' dependencies: - eslint: 9.28.0 + eslint: 9.29.0 dev: false - /eslint-config-turbo@1.10.12(eslint@9.28.0): + /eslint-config-turbo@1.10.12(eslint@9.29.0): resolution: {integrity: sha512-z3jfh+D7UGYlzMWGh+Kqz++hf8LOE96q3o5R8X4HTjmxaBWlLAWG+0Ounr38h+JLR2TJno0hU9zfzoPNkR9BdA==} peerDependencies: eslint: '>6.6.0' dependencies: - eslint: 9.28.0 - eslint-plugin-turbo: 1.10.12(eslint@9.28.0) + eslint: 9.29.0 + eslint-plugin-turbo: 1.10.12(eslint@9.29.0) dev: false - /eslint-plugin-turbo@1.10.12(eslint@9.28.0): + /eslint-plugin-turbo@1.10.12(eslint@9.29.0): resolution: {integrity: sha512-uNbdj+ohZaYo4tFJ6dStRXu2FZigwulR1b3URPXe0Q8YaE7thuekKNP+54CHtZPH9Zey9dmDx5btAQl9mfzGOw==} peerDependencies: eslint: '>6.6.0' dependencies: dotenv: 16.0.3 - eslint: 9.28.0 + eslint: 9.29.0 dev: false /eslint-scope@5.1.1: @@ -13573,8 +13670,8 @@ packages: engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} dev: false - /eslint@9.28.0: - resolution: {integrity: sha512-ocgh41VhRlf9+fVpe7QKzwLj9c92fDiqOj8Y3Sd4/ZmVA4Btx4PlUYPq4pp9JDyupkf1upbEXecxL2mwNV7jPQ==} + /eslint@9.29.0: + resolution: {integrity: sha512-GsGizj2Y1rCWDu6XoEekL3RLilp0voSePurjZIkxL3wlm5o5EC9VpgaP7lrCvjnkuLvzFBQWB3vWB3K5KQTveQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} hasBin: true peerDependencies: @@ -13583,14 +13680,14 @@ packages: jiti: optional: true dependencies: - '@eslint-community/eslint-utils': 4.7.0(eslint@9.28.0) + '@eslint-community/eslint-utils': 4.7.0(eslint@9.29.0) '@eslint-community/regexpp': 4.12.1 - '@eslint/config-array': 0.20.0 - '@eslint/config-helpers': 0.2.2 + '@eslint/config-array': 0.20.1 + '@eslint/config-helpers': 0.2.3 '@eslint/core': 0.14.0 '@eslint/eslintrc': 3.3.1 - '@eslint/js': 9.28.0 - '@eslint/plugin-kit': 0.3.1 + '@eslint/js': 9.29.0 + '@eslint/plugin-kit': 0.3.3 '@humanfs/node': 0.16.6 '@humanwhocodes/module-importer': 1.0.1 '@humanwhocodes/retry': 0.4.3 @@ -13855,8 +13952,8 @@ packages: - supports-color dev: true - /exsolve@1.0.5: - resolution: {integrity: sha512-pz5dvkYYKQ1AHVrgOzBKWeP4u4FRb3a6DNK2ucr0OoNwYIU4QWsJ+NM36LLzORT+z845MzKHHhpXiUF5nvQoJg==} + /exsolve@1.0.7: + resolution: {integrity: sha512-VO5fQUzZtI6C+vx4w/4BWJpg3s/5l+6pRQEHzFRM8WFi4XffSP1Z+4qi7GbjWbvRQEbdIco5mIMq+zX4rPuLrw==} dev: true /extend-shallow@2.0.1: @@ -14335,7 +14432,7 @@ packages: optional: true dependencies: '@formatjs/intl-localematcher': 0.5.10 - '@orama/orama': 3.1.7 + '@orama/orama': 3.1.9 '@shikijs/rehype': 1.29.2 github-slugger: 2.0.0 hast-util-to-estree: 3.1.3 @@ -14377,7 +14474,7 @@ packages: optional: true dependencies: '@formatjs/intl-localematcher': 0.5.10 - '@orama/orama': 3.1.7 + '@orama/orama': 3.1.9 '@shikijs/rehype': 1.29.2 github-slugger: 2.0.0 hast-util-to-estree: 3.1.3 @@ -14443,7 +14540,7 @@ packages: openapi-sampler: 1.6.1 react: 18.3.1 react-dom: 18.3.1(react@18.3.1) - react-hook-form: 7.57.0(react@18.3.1) + react-hook-form: 7.58.1(react@18.3.1) remark: 15.0.1 remark-rehype: 11.1.2 shiki: 1.29.2 @@ -14471,7 +14568,7 @@ packages: mdast-util-to-hast: 13.2.0 react: 18.3.1 shiki: 1.29.2 - tailwind-merge: 2.6.0 + tailwind-merge: 2.5.4 transitivePeerDependencies: - '@types/react' - '@types/react-dom' @@ -14527,7 +14624,7 @@ packages: react: 18.3.1 react-dom: 18.3.1(react@18.3.1) react-medium-image-zoom: 5.2.14(react-dom@18.3.1)(react@18.3.1) - tailwind-merge: 2.6.0 + tailwind-merge: 2.5.4 tailwindcss: 3.4.15 transitivePeerDependencies: - '@oramacloud/client' @@ -14567,7 +14664,7 @@ packages: react: 18.3.1 react-dom: 18.3.1(react@18.3.1) react-medium-image-zoom: 5.2.14(react-dom@18.3.1)(react@18.3.1) - tailwind-merge: 2.6.0 + tailwind-merge: 2.5.4 tailwindcss: 3.4.15 transitivePeerDependencies: - '@oramacloud/client' @@ -14676,7 +14773,7 @@ packages: resolution: {integrity: sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==} engines: {node: '>=8'} dependencies: - pump: 3.0.2 + pump: 3.0.3 dev: true /get-stream@6.0.1: @@ -15080,7 +15177,7 @@ packages: mdast-util-mdxjs-esm: 2.0.1 property-information: 7.1.0 space-separated-tokens: 2.0.2 - style-to-js: 1.1.16 + style-to-js: 1.1.17 unist-util-position: 5.0.0 zwitch: 2.0.4 transitivePeerDependencies: @@ -15116,7 +15213,7 @@ packages: mdast-util-mdxjs-esm: 2.0.1 property-information: 7.1.0 space-separated-tokens: 2.0.2 - style-to-js: 1.1.16 + style-to-js: 1.1.17 unist-util-position: 5.0.0 vfile-message: 4.0.2 transitivePeerDependencies: @@ -15365,8 +15462,8 @@ packages: parent-module: 1.0.1 resolve-from: 4.0.0 - /import-in-the-middle@1.14.0: - resolution: {integrity: sha512-g5zLT0HaztRJWysayWYiUq/7E5H825QIiecMD2pI5QO7Wzr847l6GDvPvmZaDIdrDtS2w7qRczywxiK6SL5vRw==} + /import-in-the-middle@1.14.2: + resolution: {integrity: sha512-5tCuY9BV8ujfOpwtAGgsTx9CGUapcFMEEyByLv1B+v2+6DhAcw+Zr0nhQT7uwaZ7DiourxFEscghOR8e1aPLQw==} dependencies: acorn: 8.15.0 acorn-import-attributes: 1.9.5(acorn@8.15.0) @@ -16020,7 +16117,7 @@ packages: canvas: optional: true dependencies: - cssstyle: 4.4.0 + cssstyle: 4.6.0 data-urls: 5.0.0 decimal.js: 10.5.0 form-data: 4.0.3 @@ -16195,7 +16292,7 @@ packages: '@snyk/github-codeowners': 1.1.0 '@types/node': 22.14.0 easy-table: 1.2.0 - enhanced-resolve: 5.18.1 + enhanced-resolve: 5.18.2 fast-glob: 3.3.3 jiti: 2.4.2 js-yaml: 4.1.0 @@ -16208,7 +16305,7 @@ packages: summary: 2.1.0 typescript: 5.7.3 zod: 3.23.8 - zod-validation-error: 3.4.1(zod@3.23.8) + zod-validation-error: 3.5.2(zod@3.23.8) dev: true /lazy-cache@1.0.4: @@ -16431,8 +16528,8 @@ packages: get-func-name: 2.0.2 dev: true - /loupe@3.1.3: - resolution: {integrity: sha512-kkIp7XSkP78ZxJEsSxW3712C6teJVoeHHwgo9zJ380de7IYyJ2ISlxojcH2pC5OFLewESmnRi/+XCDIEEVyoug==} + /loupe@3.1.4: + resolution: {integrity: sha512-wJzkKwJrheKtknCOKNEtDK4iqg/MxmZheEMtSTYvnzRdEYaZzmgH976nenp8WdJRdx5Vc1X/9MO0Oszl6ezeXg==} dev: true /lowercase-keys@3.0.0: @@ -16540,7 +16637,7 @@ packages: dependencies: '@types/mdast': 4.0.4 '@types/unist': 3.0.3 - decode-named-character-reference: 1.1.0 + decode-named-character-reference: 1.2.0 devlop: 1.1.0 mdast-util-to-string: 4.0.0 micromark: 4.0.2 @@ -16770,7 +16867,7 @@ packages: /micromark-core-commonmark@2.0.3: resolution: {integrity: sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg==} dependencies: - decode-named-character-reference: 1.1.0 + decode-named-character-reference: 1.2.0 devlop: 1.1.0 micromark-factory-destination: 2.0.1 micromark-factory-label: 2.0.1 @@ -17011,7 +17108,7 @@ packages: /micromark-util-decode-string@2.0.1: resolution: {integrity: sha512-nDV/77Fj6eH1ynwscYTOsbK7rR//Uj0bZXBwJZRfaLEJ1iGBR6kIfNmlNqaqJf649EP0F3NWNdeJi03elllNUQ==} dependencies: - decode-named-character-reference: 1.1.0 + decode-named-character-reference: 1.2.0 micromark-util-character: 2.1.1 micromark-util-decode-numeric-character-reference: 2.0.2 micromark-util-symbol: 2.0.1 @@ -17069,7 +17166,7 @@ packages: dependencies: '@types/debug': 4.1.12 debug: 4.4.1(supports-color@8.1.1) - decode-named-character-reference: 1.1.0 + decode-named-character-reference: 1.2.0 devlop: 1.1.0 micromark-core-commonmark: 2.0.3 micromark-factory-space: 2.0.1 @@ -17235,7 +17332,6 @@ packages: /minipass@6.0.2: resolution: {integrity: sha512-MzWSV5nYVT7mVyWCwn2o7JH13w2TBRmmSqSRCKzTw+lmft9X4z+3wjvs06Tzijo5z4W/kahUCDpRXTF+ZrmF/w==} engines: {node: '>=16 || 14 >=14.17'} - dev: true /minipass@7.1.2: resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==} @@ -17345,7 +17441,7 @@ packages: minimist: 1.2.8 mqtt-packet: 6.10.0 number-allocator: 1.0.14 - pump: 3.0.2 + pump: 3.0.3 readable-stream: 3.6.2 reinterval: 1.1.0 rfdc: 1.4.1 @@ -17553,7 +17649,7 @@ packages: '@next/env': 14.1.0 '@swc/helpers': 0.5.2 busboy: 1.6.0 - caniuse-lite: 1.0.30001722 + caniuse-lite: 1.0.30001726 graceful-fs: 4.2.11 postcss: 8.4.31 react: 18.3.1 @@ -17595,7 +17691,7 @@ packages: '@next/env': 14.2.15 '@swc/helpers': 0.5.5 busboy: 1.6.0 - caniuse-lite: 1.0.30001722 + caniuse-lite: 1.0.30001726 graceful-fs: 4.2.11 postcss: 8.4.31 react: 18.3.1 @@ -17636,7 +17732,7 @@ packages: '@next/env': 14.2.25 '@swc/helpers': 0.5.5 busboy: 1.6.0 - caniuse-lite: 1.0.30001722 + caniuse-lite: 1.0.30001726 graceful-fs: 4.2.11 postcss: 8.4.31 react: 18.3.1 @@ -17657,6 +17753,48 @@ packages: - babel-plugin-macros dev: false + /next@14.2.30(react-dom@18.3.1)(react@18.3.1): + resolution: {integrity: sha512-+COdu6HQrHHFQ1S/8BBsCag61jZacmvbuL2avHvQFbWa2Ox7bE+d8FyNgxRLjXQ5wtPyQwEmk85js/AuaG2Sbg==} + engines: {node: '>=18.17.0'} + hasBin: true + peerDependencies: + '@opentelemetry/api': ^1.1.0 + '@playwright/test': ^1.41.2 + react: ^18.2.0 + react-dom: ^18.2.0 + sass: ^1.3.0 + peerDependenciesMeta: + '@opentelemetry/api': + optional: true + '@playwright/test': + optional: true + sass: + optional: true + dependencies: + '@next/env': 14.2.30 + '@swc/helpers': 0.5.5 + busboy: 1.6.0 + caniuse-lite: 1.0.30001726 + graceful-fs: 4.2.11 + postcss: 8.4.31 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + styled-jsx: 5.1.1(react@18.3.1) + optionalDependencies: + '@next/swc-darwin-arm64': 14.2.30 + '@next/swc-darwin-x64': 14.2.30 + '@next/swc-linux-arm64-gnu': 14.2.30 + '@next/swc-linux-arm64-musl': 14.2.30 + '@next/swc-linux-x64-gnu': 14.2.30 + '@next/swc-linux-x64-musl': 14.2.30 + '@next/swc-win32-arm64-msvc': 14.2.30 + '@next/swc-win32-ia32-msvc': 14.2.30 + '@next/swc-win32-x64-msvc': 14.2.30 + transitivePeerDependencies: + - '@babel/core' + - babel-plugin-macros + dev: false + /nimma@0.2.3: resolution: {integrity: sha512-1ZOI8J+1PKKGceo/5CT5GfQOG6H8I2BencSK06YarZ2wXwH37BSSUWldqJmMJYA5JfqDqffxDXynt6f11AyKcA==} engines: {node: ^12.20 || >=14.13} @@ -17683,7 +17821,6 @@ packages: /node-domexception@1.0.0: resolution: {integrity: sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==} engines: {node: '>=10.5.0'} - deprecated: Use your platform's native DOMException instead /node-fetch@2.6.7: resolution: {integrity: sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==} @@ -17865,6 +18002,15 @@ packages: next: 14.2.25(react-dom@18.3.1)(react@18.3.1) dev: false + /nuqs@1.17.6(next@14.2.30): + resolution: {integrity: sha512-mbQKLo+4h9ZsCg9u4WcitBqVhP2XG2PrPc0sUN0IEHL3nUKjxHT2h3tqd1Wo1gkkGxan5uVpnJ9NP51y2j7vgg==} + peerDependencies: + next: '>=13.4 <14.0.2 || ^14.0.3' + dependencies: + mitt: 3.0.1 + next: 14.2.30(react-dom@18.3.1)(react@18.3.1) + dev: false + /nwsapi@2.2.20: resolution: {integrity: sha512-/ieB+mDe4MrrKMT8z+mQL8klXydZWGR5Dowt4RAGKbJ3kIGEx3X4ljUo+6V73IXtUPWgfOlU5B9MlGxFO5T+cA==} dev: true @@ -17993,7 +18139,7 @@ packages: zod: optional: true dependencies: - '@types/node': 18.19.111 + '@types/node': 18.19.112 '@types/node-fetch': 2.6.12 abort-controller: 3.0.0 agentkeepalive: 4.6.0 @@ -18029,8 +18175,8 @@ packages: yargs-parser: 21.1.1 dev: true - /openapi3-ts@4.4.0: - resolution: {integrity: sha512-9asTNB9IkKEzWMcHmVZE7Ts3kC9G7AFHfs8i7caD8HbI76gEjdkId4z/AkP83xdZsH7PLAnnbl47qZkXuxpArw==} + /openapi3-ts@4.5.0: + resolution: {integrity: sha512-jaL+HgTq2Gj5jRcfdutgRGLosCy/hT8sQf6VOy+P+g36cZOjI1iukdPnijC+4CmeRzg/jEllJUboEic2FhxhtQ==} dependencies: yaml: 2.8.0 dev: false @@ -18265,7 +18411,7 @@ packages: '@types/unist': 2.0.11 character-entities-legacy: 3.0.0 character-reference-invalid: 2.0.1 - decode-named-character-reference: 1.1.0 + decode-named-character-reference: 1.2.0 is-alphanumerical: 2.0.1 is-decimal: 2.0.1 is-hexadecimal: 2.0.1 @@ -18366,7 +18512,7 @@ packages: engines: {node: '>=16 || 14 >=14.18'} dependencies: lru-cache: 10.4.3 - minipass: 7.1.2 + minipass: 6.0.2 /path-to-regexp@0.1.12: resolution: {integrity: sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==} @@ -18393,8 +18539,8 @@ packages: resolution: {integrity: sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==} dev: true - /pathval@2.0.0: - resolution: {integrity: sha512-vE7JKRyES09KiunauX7nd2Q9/L7lhok4smP9RZTDeD4MVs72Dp2qNFVz39Nz5a0FVEW0BJR6C0DYrq6unoziZA==} + /pathval@2.0.1: + resolution: {integrity: sha512-//nshmD55c46FuFw26xV/xFAaB5HF9Xdap7HJBBnrKdAd6/GxDBaNA1870O79+9ueg61cZLSVc+OaFlfmObYVQ==} engines: {node: '>= 14.16'} dev: true @@ -18481,27 +18627,27 @@ packages: postcss-selector-parser: 6.1.2 dev: false - /postcss-import@15.1.0(postcss@8.5.4): + /postcss-import@15.1.0(postcss@8.5.6): resolution: {integrity: sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==} engines: {node: '>=14.0.0'} peerDependencies: postcss: ^8.0.0 dependencies: - postcss: 8.5.4 + postcss: 8.5.6 postcss-value-parser: 4.2.0 read-cache: 1.0.0 resolve: 1.22.10 - /postcss-js@4.0.1(postcss@8.5.4): + /postcss-js@4.0.1(postcss@8.5.6): resolution: {integrity: sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw==} engines: {node: ^12 || ^14 || >= 16} peerDependencies: postcss: ^8.4.21 dependencies: camelcase-css: 2.0.1 - postcss: 8.5.4 + postcss: 8.5.6 - /postcss-load-config@4.0.2(postcss@8.5.4): + /postcss-load-config@4.0.2(postcss@8.5.6): resolution: {integrity: sha512-bSVhyJGL00wMVoPUzAVAnbEoWyqRxkjv64tUl427SKnPrENtq6hJwUojroMz2VB+Q1edmi4IfrAPpami5VVgMQ==} engines: {node: '>= 14'} peerDependencies: @@ -18514,16 +18660,16 @@ packages: optional: true dependencies: lilconfig: 3.1.3 - postcss: 8.5.4 + postcss: 8.5.6 yaml: 2.8.0 - /postcss-nested@6.2.0(postcss@8.5.4): + /postcss-nested@6.2.0(postcss@8.5.6): resolution: {integrity: sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ==} engines: {node: '>=12.0'} peerDependencies: postcss: ^8.2.14 dependencies: - postcss: 8.5.4 + postcss: 8.5.6 postcss-selector-parser: 6.1.2 /postcss-selector-parser@6.0.10: @@ -18565,7 +18711,7 @@ packages: dependencies: nanoid: 3.3.11 picocolors: 1.1.1 - source-map-js: 1.2.1 + source-map-js: 1.0.2 dev: false /postcss@8.4.38: @@ -18586,8 +18732,8 @@ packages: source-map-js: 1.2.1 dev: true - /postcss@8.5.4: - resolution: {integrity: sha512-QSa9EBe+uwlGTFmHsPKokv3B/oEMQZxfqW0QqNCyhpa6mB1afzulwn8hihglqAb2pOw+BJgNlmXQ8la2VeHB7w==} + /postcss@8.5.6: + resolution: {integrity: sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==} engines: {node: ^10 || ^12 || >=14} dependencies: nanoid: 3.3.11 @@ -18599,7 +18745,7 @@ packages: dependencies: core-js: 3.43.0 fflate: 0.4.8 - preact: 10.26.8 + preact: 10.26.9 web-vitals: 4.2.4 dev: false @@ -18607,14 +18753,14 @@ packages: resolution: {integrity: sha512-hgyCYMyzMvuF3qWMw6JvS8gT55v7Mtp5wKWcnDrw+nu39D0Tk9BXD7I0LOBp0lGlHEPaXCEVYUtviNKrhMALGA==} engines: {node: '>=15.0.0'} dependencies: - axios: 1.9.0 + axios: 1.10.0 rusha: 0.8.14 transitivePeerDependencies: - debug dev: false - /preact@10.26.8: - resolution: {integrity: sha512-1nMfdFjucm5hKvq0IClqZwK4FJkGXhRrQstOQ3P4vp8HxKrJEMFcY6RdBRVTdfQS/UlnX6gfbPuTvaqx/bDoeQ==} + /preact@10.26.9: + resolution: {integrity: sha512-SSjF9vcnF27mJK1XyFMNJzFd5u3pQiATFqoaDy03XuN00u4ziveVVEGt5RKJrDR8MHE/wJo9Nnad56RLzS2RMA==} dev: false /prelude-ls@1.2.1: @@ -18634,6 +18780,12 @@ packages: hasBin: true dev: false + /prettier@3.6.1: + resolution: {integrity: sha512-5xGWRa90Sp2+x1dQtNpIpeOQpTDBs9cZDmA/qs2vDNN2i18PdapqY7CmBeyLlMuGqXJRIOPaCaVZTLNQRWUH/A==} + engines: {node: '>=14'} + hasBin: true + dev: false + /pretty-format@27.5.1: resolution: {integrity: sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==} engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} @@ -18799,10 +18951,10 @@ packages: is-ip: 3.1.0 dev: true - /pump@3.0.2: - resolution: {integrity: sha512-tUPXtzlGM8FE3P0ZL6DVs/3P58k9nk8/jZeQCurTJylQA8qFYzHFfhBJkuqyE0FifOsQ0uKWekiZ5g8wtr28cw==} + /pump@3.0.3: + resolution: {integrity: sha512-todwxLMY7/heScKmntwQG8CXVkWUOdYxIvY2s0VWAAMh/nd8SoYiRaKjlr7+iCs984f2P8zvrfWcDDYVb73NfA==} dependencies: - end-of-stream: 1.4.4 + end-of-stream: 1.4.5 once: 1.4.0 dev: true @@ -18962,7 +19114,7 @@ packages: react-is: 18.1.0 dev: false - /react-email@2.1.1(eslint@9.28.0): + /react-email@2.1.1(eslint@9.29.0): resolution: {integrity: sha512-09oMVl/jN0/Re0bT0sEqYjyyFSCN/Tg0YmzjC9wfYpnMx02Apk40XXitySDfUBMR9EgTdr6T4lYknACqiLK3mg==} engines: {node: '>=18.0.0'} hasBin: true @@ -18988,8 +19140,8 @@ packages: commander: 11.1.0 debounce: 2.0.0 esbuild: 0.19.11 - eslint-config-prettier: 9.0.0(eslint@9.28.0) - eslint-config-turbo: 1.10.12(eslint@9.28.0) + eslint-config-prettier: 9.0.0(eslint@9.29.0) + eslint-config-turbo: 1.10.12(eslint@9.29.0) framer-motion: 10.17.4(react-dom@18.3.1)(react@18.3.1) glob: 10.3.4 log-symbols: 4.1.0 @@ -19044,8 +19196,8 @@ packages: react: 18.3.1 dev: false - /react-hook-form@7.57.0(react@18.3.1): - resolution: {integrity: sha512-RbEks3+cbvTP84l/VXGUZ+JMrKOS8ykQCRYdm5aYsxnDquL0vspsyNhGRO7pcH6hsZqWlPOjLye7rJqdtdAmlg==} + /react-hook-form@7.58.1(react@18.3.1): + resolution: {integrity: sha512-Lml/KZYEEFfPhUVgE0RdCVpnC4yhW+PndRhbiTtdvSlQTL8IfVR+iQkBjLIvmmc6+GGoVeM11z37ktKFPAb0FA==} engines: {node: '>=18.0.0'} peerDependencies: react: ^16.8.0 || ^17 || ^18 || ^19 @@ -19713,33 +19865,33 @@ packages: source-map-support: 0.3.3 dev: false - /rollup@4.43.0: - resolution: {integrity: sha512-wdN2Kd3Twh8MAEOEJZsuxuLKCsBEo4PVNLK6tQWAn10VhsVewQLzcucMgLolRlhFybGxfclbPeEYBaP6RvUFGg==} + /rollup@4.44.0: + resolution: {integrity: sha512-qHcdEzLCiktQIfwBq420pn2dP+30uzqYxv9ETm91wdt2R9AFcWfjNAmje4NWlnCIQ5RMTzVf0ZyisOKqHR6RwA==} engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true dependencies: - '@types/estree': 1.0.7 + '@types/estree': 1.0.8 optionalDependencies: - '@rollup/rollup-android-arm-eabi': 4.43.0 - '@rollup/rollup-android-arm64': 4.43.0 - '@rollup/rollup-darwin-arm64': 4.43.0 - '@rollup/rollup-darwin-x64': 4.43.0 - '@rollup/rollup-freebsd-arm64': 4.43.0 - '@rollup/rollup-freebsd-x64': 4.43.0 - '@rollup/rollup-linux-arm-gnueabihf': 4.43.0 - '@rollup/rollup-linux-arm-musleabihf': 4.43.0 - '@rollup/rollup-linux-arm64-gnu': 4.43.0 - '@rollup/rollup-linux-arm64-musl': 4.43.0 - '@rollup/rollup-linux-loongarch64-gnu': 4.43.0 - '@rollup/rollup-linux-powerpc64le-gnu': 4.43.0 - '@rollup/rollup-linux-riscv64-gnu': 4.43.0 - '@rollup/rollup-linux-riscv64-musl': 4.43.0 - '@rollup/rollup-linux-s390x-gnu': 4.43.0 - '@rollup/rollup-linux-x64-gnu': 4.43.0 - '@rollup/rollup-linux-x64-musl': 4.43.0 - '@rollup/rollup-win32-arm64-msvc': 4.43.0 - '@rollup/rollup-win32-ia32-msvc': 4.43.0 - '@rollup/rollup-win32-x64-msvc': 4.43.0 + '@rollup/rollup-android-arm-eabi': 4.44.0 + '@rollup/rollup-android-arm64': 4.44.0 + '@rollup/rollup-darwin-arm64': 4.44.0 + '@rollup/rollup-darwin-x64': 4.44.0 + '@rollup/rollup-freebsd-arm64': 4.44.0 + '@rollup/rollup-freebsd-x64': 4.44.0 + '@rollup/rollup-linux-arm-gnueabihf': 4.44.0 + '@rollup/rollup-linux-arm-musleabihf': 4.44.0 + '@rollup/rollup-linux-arm64-gnu': 4.44.0 + '@rollup/rollup-linux-arm64-musl': 4.44.0 + '@rollup/rollup-linux-loongarch64-gnu': 4.44.0 + '@rollup/rollup-linux-powerpc64le-gnu': 4.44.0 + '@rollup/rollup-linux-riscv64-gnu': 4.44.0 + '@rollup/rollup-linux-riscv64-musl': 4.44.0 + '@rollup/rollup-linux-s390x-gnu': 4.44.0 + '@rollup/rollup-linux-x64-gnu': 4.44.0 + '@rollup/rollup-linux-x64-musl': 4.44.0 + '@rollup/rollup-win32-arm64-msvc': 4.44.0 + '@rollup/rollup-win32-ia32-msvc': 4.44.0 + '@rollup/rollup-win32-x64-msvc': 4.44.0 fsevents: 2.3.3 dev: true @@ -20082,15 +20234,15 @@ packages: '@types/hast': 3.0.4 dev: false - /shiki@3.6.0: - resolution: {integrity: sha512-tKn/Y0MGBTffQoklaATXmTqDU02zx8NYBGQ+F6gy87/YjKbizcLd+Cybh/0ZtOBX9r1NEnAy/GTRDKtOsc1L9w==} + /shiki@3.7.0: + resolution: {integrity: sha512-ZcI4UT9n6N2pDuM2n3Jbk0sR4Swzq43nLPgS/4h0E3B/NrFn2HKElrDtceSf8Zx/OWYOo7G1SAtBLypCp+YXqg==} dependencies: - '@shikijs/core': 3.6.0 - '@shikijs/engine-javascript': 3.6.0 - '@shikijs/engine-oniguruma': 3.6.0 - '@shikijs/langs': 3.6.0 - '@shikijs/themes': 3.6.0 - '@shikijs/types': 3.6.0 + '@shikijs/core': 3.7.0 + '@shikijs/engine-javascript': 3.7.0 + '@shikijs/engine-oniguruma': 3.7.0 + '@shikijs/langs': 3.7.0 + '@shikijs/themes': 3.7.0 + '@shikijs/types': 3.7.0 '@shikijs/vscode-textmate': 10.0.2 '@types/hast': 3.0.4 dev: true @@ -20659,13 +20811,13 @@ packages: resolution: {integrity: sha512-vrN+B7DBIoTTZjnPNewwhx6cBA/H+IS7rfW68n7XxC1y7uoiGQBxaKzqucGUgavX15dJgiGztLJ8vxuEzwqBdA==} dev: false - /style-to-js@1.1.16: - resolution: {integrity: sha512-/Q6ld50hKYPH3d/r6nr117TZkHR0w0kGGIVfpG9N6D8NymRPM9RqCUv4pRpJ62E5DqOYx2AFpbZMyCPnjQCnOw==} + /style-to-js@1.1.17: + resolution: {integrity: sha512-xQcBGDxJb6jjFCTzvQtfiPn6YvvP2O8U1MDIPNfJQlWMYfktPy+iGsHE7cssjs7y84d9fQaK4UF3RIJaAHSoYA==} dependencies: - style-to-object: 1.0.8 + style-to-object: 1.0.9 - /style-to-object@1.0.8: - resolution: {integrity: sha512-xT47I/Eo0rwJmaXC4oilDGDWLohVhR6o/xAQcPQN8q6QBuZVL8qMYL85kLmST5cPjAorwvqIA4qXTRQoYHaL6g==} + /style-to-object@1.0.9: + resolution: {integrity: sha512-G4qppLgKu/k6FwRpHiGiKPaPTFcG3g4wNVX/Qsfu+RqQM30E7Tyu/TEgxcL9PNLF5pdRLwQdE3YKKf+KF2Dzlw==} dependencies: inline-style-parser: 0.2.4 @@ -20811,12 +20963,12 @@ packages: resolution: {integrity: sha512-LqVcOHSB4cPGgitD1riJ1Hh4vdmITOp+BkmfmXRh4hSF/t7EnS4iD+SOTmq7w5pPm/SiPeto4ADbKS6dHUDWFA==} dev: false - /swrv@1.0.4(vue@3.5.16): + /swrv@1.0.4(vue@3.5.17): resolution: {integrity: sha512-zjEkcP8Ywmj+xOJW3lIT65ciY/4AL4e/Or7Gj0MzU3zBJNMdJiT8geVZhINavnlHRMMCcJLHhraLTAiDOTmQ9g==} peerDependencies: vue: '>=3.2.26 < 4' dependencies: - vue: 3.5.16(typescript@5.7.3) + vue: 3.5.17(typescript@5.7.3) dev: false /symbol-tree@3.2.4: @@ -20833,10 +20985,6 @@ packages: resolution: {integrity: sha512-0q8cfZHMu9nuYP/b5Shb7Y7Sh1B7Nnl5GqNr1U+n2p6+mybvRtayrQ+0042Z5byvTA8ihjlP8Odo8/VnHbZu4Q==} dev: false - /tailwind-merge@2.6.0: - resolution: {integrity: sha512-P+Vu1qXfzediirmHOC3xKGAYeZtPcV9g76X+xg2FD4tYgR71ewMA35Y3sCz3zhiN/dwefRpJX0yBcgwi1fXNQA==} - dev: false - /tailwindcss-animate@1.0.7(tailwindcss@3.4.15): resolution: {integrity: sha512-bl6mpH3T7I3UFxuvDEXLxy/VuFxBk5bbzplh7tXI68mwMokNYd1t9qPBHlnyTwfa4JGC4zP516I1hYYtQ/vspA==} peerDependencies: @@ -20851,7 +20999,7 @@ packages: dependencies: '@alloc/quick-lru': 5.2.0 arg: 5.0.2 - chokidar: 3.6.0 + chokidar: 3.5.3 didyoumean: 1.2.2 dlv: 1.1.3 fast-glob: 3.3.3 @@ -20863,11 +21011,11 @@ packages: normalize-path: 3.0.0 object-hash: 3.0.0 picocolors: 1.1.1 - postcss: 8.5.4 - postcss-import: 15.1.0(postcss@8.5.4) - postcss-js: 4.0.1(postcss@8.5.4) - postcss-load-config: 4.0.2(postcss@8.5.4) - postcss-nested: 6.2.0(postcss@8.5.4) + postcss: 8.5.6 + postcss-import: 15.1.0(postcss@8.5.6) + postcss-js: 4.0.1(postcss@8.5.6) + postcss-load-config: 4.0.2(postcss@8.5.6) + postcss-nested: 6.2.0(postcss@8.5.6) postcss-selector-parser: 6.1.2 resolve: 1.22.10 sucrase: 3.35.0 @@ -20894,11 +21042,11 @@ packages: normalize-path: 3.0.0 object-hash: 3.0.0 picocolors: 1.1.1 - postcss: 8.5.4 - postcss-import: 15.1.0(postcss@8.5.4) - postcss-js: 4.0.1(postcss@8.5.4) - postcss-load-config: 4.0.2(postcss@8.5.4) - postcss-nested: 6.2.0(postcss@8.5.4) + postcss: 8.5.6 + postcss-import: 15.1.0(postcss@8.5.6) + postcss-js: 4.0.1(postcss@8.5.6) + postcss-load-config: 4.0.2(postcss@8.5.6) + postcss-nested: 6.2.0(postcss@8.5.6) postcss-selector-parser: 6.1.2 resolve: 1.22.10 sucrase: 3.35.0 @@ -20936,14 +21084,14 @@ packages: dependencies: chownr: 1.1.4 mkdirp-classic: 0.5.3 - pump: 3.0.2 + pump: 3.0.3 tar-stream: 2.2.0 dev: true - /tar-fs@3.0.9: - resolution: {integrity: sha512-XF4w9Xp+ZQgifKakjZYmFdkLoSWd34VGKcsTCwlNWM7QG3ZbaxnTsaBwnjFZqHRf/rROxaR8rXnbtwdvaDI+lA==} + /tar-fs@3.0.10: + resolution: {integrity: sha512-C1SwlQGNLe/jPNqapK8epDsXME7CAJR5RL3GcE6KWx1d9OUByzoHVcbu1VPI8tevg9H8Alae0AApHHFGzrD5zA==} dependencies: - pump: 3.0.2 + pump: 3.0.3 tar-stream: 3.1.7 optionalDependencies: bare-fs: 4.1.5 @@ -20957,7 +21105,7 @@ packages: engines: {node: '>=6'} dependencies: bl: 4.1.0 - end-of-stream: 1.4.4 + end-of-stream: 1.4.5 fs-constants: 1.0.0 inherits: 2.0.4 readable-stream: 3.6.2 @@ -21022,12 +21170,12 @@ packages: jest-worker: 27.5.1 schema-utils: 4.3.2 serialize-javascript: 6.0.2 - terser: 5.42.0 + terser: 5.43.1 webpack: 5.99.9(@swc/core@1.3.101)(esbuild@0.19.11) dev: false - /terser@5.42.0: - resolution: {integrity: sha512-UYCvU9YQW2f/Vwl+P0GfhxJxbUGLwd+5QrrGgLajzWAtC/23AX0vcise32kkP7Eu0Wu9VlzzHAXkLObgjQfFlQ==} + /terser@5.43.1: + resolution: {integrity: sha512-+6erLbBm0+LROX2sPXlUYx/ux5PyE9K/a92Wrt6oA+WDAoFTdpHE5tCYCI5PNzq2y8df4rA+QgHLJuR4jNymsg==} engines: {node: '>=10'} hasBin: true dependencies: @@ -21041,7 +21189,7 @@ packages: resolution: {integrity: sha512-8fReFeQ4bk17T2vHHzcFavBG8UHuHwsdVj+48TchtsCSklwmSUTkg/b57hVjxZdxN1ed/GfF63WZ39I4syV5tQ==} dependencies: '@balena/dockerignore': 1.0.2 - '@types/dockerode': 3.3.40 + '@types/dockerode': 3.3.41 archiver: 7.0.1 async-lock: 1.4.1 byline: 5.0.0 @@ -21052,7 +21200,7 @@ packages: proper-lockfile: 4.1.2 properties-reader: 2.3.0 ssh-remote-port-forward: 1.0.4 - tar-fs: 3.0.9 + tar-fs: 3.0.10 tmp: 0.2.3 undici: 5.29.0 transitivePeerDependencies: @@ -21109,8 +21257,8 @@ packages: engines: {node: '>=14.0.0'} dev: true - /tinypool@1.1.0: - resolution: {integrity: sha512-7CotroY9a8DKsKprEy/a14aCCm8jYVmR7aFy4fpkZM8sdpNJbKkixuNjgM50yCmip2ezc8z4N7k3oe2+rfRJCQ==} + /tinypool@1.1.1: + resolution: {integrity: sha512-Zba82s87IFq9A9XmjiX5uZA/ARWDrB03OHlq+Vw1fSdt0I+4/Kutwy8BP4Y/y/aORMo61FQ0vIb5j44vSo5Pkg==} engines: {node: ^18.0.0 || >=20.0.0} dev: true @@ -21345,9 +21493,9 @@ packages: execa: 5.1.1 globby: 11.1.0 joycon: 3.1.1 - postcss-load-config: 4.0.2(postcss@8.5.4) + postcss-load-config: 4.0.2(postcss@8.5.6) resolve-from: 5.0.0 - rollup: 4.43.0 + rollup: 4.44.0 source-map: 0.8.0-beta.0 sucrase: 3.35.0 tree-kill: 1.2.2 @@ -21384,9 +21532,9 @@ packages: execa: 5.1.1 globby: 11.1.0 joycon: 3.1.1 - postcss-load-config: 4.0.2(postcss@8.5.4) + postcss-load-config: 4.0.2(postcss@8.5.6) resolve-from: 5.0.0 - rollup: 4.43.0 + rollup: 4.44.0 source-map: 0.8.0-beta.0 sucrase: 3.35.0 tree-kill: 1.2.2 @@ -21672,7 +21820,7 @@ packages: resolution: {integrity: sha512-J/rEIZU8w6FOfLNz/hNKsnY+fFHWnu9MH4yRbSZF3xbbGHovcetXPs7sD+9p8L6CeNC//I9bhRYAOsBt2u7/OA==} dependencies: defu: 6.1.4 - exsolve: 1.0.5 + exsolve: 1.0.7 ohash: 2.0.11 pathe: 2.0.3 ufo: 1.6.1 @@ -21826,13 +21974,13 @@ packages: engines: {node: '>= 0.8'} dev: true - /update-browserslist-db@1.1.3(browserslist@4.25.0): + /update-browserslist-db@1.1.3(browserslist@4.25.1): resolution: {integrity: sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==} hasBin: true peerDependencies: browserslist: '>= 4.21.0' dependencies: - browserslist: 4.25.0 + browserslist: 4.25.1 escalade: 3.2.0 picocolors: 1.1.1 @@ -22112,8 +22260,8 @@ packages: dependencies: '@types/node': 20.14.9 esbuild: 0.21.5 - postcss: 8.5.4 - rollup: 4.43.0 + postcss: 8.5.6 + rollup: 4.44.0 optionalDependencies: fsevents: 2.3.3 dev: true @@ -22151,8 +22299,8 @@ packages: dependencies: '@types/node': 22.14.0 esbuild: 0.21.5 - postcss: 8.5.4 - rollup: 4.43.0 + postcss: 8.5.6 + rollup: 4.44.0 optionalDependencies: fsevents: 2.3.3 dev: true @@ -22201,8 +22349,8 @@ packages: esbuild: 0.25.5 fdir: 6.4.6(picomatch@4.0.2) picomatch: 4.0.2 - postcss: 8.5.4 - rollup: 4.43.0 + postcss: 8.5.6 + rollup: 4.44.0 tinyglobby: 0.2.14 optionalDependencies: fsevents: 2.3.3 @@ -22266,7 +22414,7 @@ packages: - terser dev: true - /vitest@1.6.1(@types/node@22.14.0)(@vitest/ui@1.6.1): + /vitest@1.6.1(@types/node@22.14.0)(@vitest/ui@1.6.0): resolution: {integrity: sha512-Ljb1cnSJSivGN0LqXd/zmDbWEM0RNNg2t1QW/XUhYl/qPqyu7CsqeWtqQXHVaJsecLPuDoak2oJcZN2QoRIOag==} engines: {node: ^18.0.0 || >=20.0.0} hasBin: true @@ -22296,7 +22444,7 @@ packages: '@vitest/runner': 1.6.1 '@vitest/snapshot': 1.6.1 '@vitest/spy': 1.6.1 - '@vitest/ui': 1.6.1(vitest@1.6.1) + '@vitest/ui': 1.6.0(vitest@1.6.1) '@vitest/utils': 1.6.1 acorn-walk: 8.3.4 chai: 4.5.0 @@ -22414,7 +22562,7 @@ packages: '@types/node': 22.14.0 '@vitest/expect': 3.1.4 '@vitest/mocker': 3.1.4(vite@6.3.5) - '@vitest/pretty-format': 3.2.3 + '@vitest/pretty-format': 3.2.4 '@vitest/runner': 3.1.4 '@vitest/snapshot': 3.1.4 '@vitest/spy': 3.1.4 @@ -22429,7 +22577,7 @@ packages: tinybench: 2.9.0 tinyexec: 0.3.2 tinyglobby: 0.2.14 - tinypool: 1.1.0 + tinypool: 1.1.1 tinyrainbow: 2.0.0 vite: 6.3.5(@types/node@22.14.0) vite-node: 3.1.4(@types/node@22.14.0) @@ -22457,19 +22605,19 @@ packages: resolution: {integrity: sha512-AFbieoL7a5LMqcnOF04ji+rpXadgOXnZsxQr//r83kLPr7biP7am3g9zbaZIaBGwBRWeSvoMD4mgPdX3e4NWBg==} dev: false - /vue@3.5.16(typescript@5.7.3): - resolution: {integrity: sha512-rjOV2ecxMd5SiAmof2xzh2WxntRcigkX/He4YFJ6WdRvVUrbt6DxC1Iujh10XLl8xCDRDtGKMeO3D+pRQ1PP9w==} + /vue@3.5.17(typescript@5.7.3): + resolution: {integrity: sha512-LbHV3xPN9BeljML+Xctq4lbz2lVHCR6DtbpTf5XIO6gugpXUN49j2QQPcMj086r9+AkJ0FfUT8xjulKKBkkr9g==} peerDependencies: typescript: '*' peerDependenciesMeta: typescript: optional: true dependencies: - '@vue/compiler-dom': 3.5.16 - '@vue/compiler-sfc': 3.5.16 - '@vue/runtime-dom': 3.5.16 - '@vue/server-renderer': 3.5.16(vue@3.5.16) - '@vue/shared': 3.5.16 + '@vue/compiler-dom': 3.5.17 + '@vue/compiler-sfc': 3.5.17 + '@vue/runtime-dom': 3.5.17 + '@vue/server-renderer': 3.5.17(vue@3.5.17) + '@vue/shared': 3.5.17 typescript: 5.7.3 dev: false @@ -22533,8 +22681,8 @@ packages: engines: {node: '>=12'} dev: true - /webpack-sources@3.3.2: - resolution: {integrity: sha512-ykKKus8lqlgXX/1WjudpIEjqsafjOTcOJqxnAbMLAu/KCsDCJ6GBtvscewvTkrn24HsnvFwrSCbenFrhtcCsAA==} + /webpack-sources@3.3.3: + resolution: {integrity: sha512-yd1RBzSGanHkitROoPFd6qsrxt+oFhg/129YzheDGqeustzX0vTZJZsSsQjVQC4yzBQ56K55XU8gaNCtIzOnTg==} engines: {node: '>=10.13.0'} dev: false @@ -22555,9 +22703,9 @@ packages: '@webassemblyjs/wasm-edit': 1.14.1 '@webassemblyjs/wasm-parser': 1.14.1 acorn: 8.15.0 - browserslist: 4.25.0 + browserslist: 4.25.1 chrome-trace-event: 1.0.4 - enhanced-resolve: 5.18.1 + enhanced-resolve: 5.18.2 es-module-lexer: 1.7.0 eslint-scope: 5.1.1 events: 3.3.0 @@ -22571,7 +22719,7 @@ packages: tapable: 2.2.2 terser-webpack-plugin: 5.3.14(@swc/core@1.3.101)(esbuild@0.19.11)(webpack@5.99.9) watchpack: 2.4.4 - webpack-sources: 3.3.2 + webpack-sources: 3.3.3 transitivePeerDependencies: - '@swc/core' - esbuild @@ -23051,18 +23199,18 @@ packages: zod: 3.23.8 dev: false - /zod-to-json-schema@3.24.5(zod@3.23.8): - resolution: {integrity: sha512-/AuWwMP+YqiPbsJx5D6TfgRTc4kTLjsh5SOcd4bLsfUg2RcEXrFMJl1DGgdHy2aCfsIA/cr/1JM0xcB2GZji8g==} + /zod-to-json-schema@3.24.6(zod@3.23.8): + resolution: {integrity: sha512-h/z3PKvcTcTetyjl1fkj79MHNEjm+HpD6NXheWjzOekY7kV+lwDYnHw+ivHkijnCSMz1yJaWBD9vu/Fcmk+vEg==} peerDependencies: zod: ^3.24.1 dependencies: zod: 3.23.8 - /zod-validation-error@3.4.1(zod@3.23.8): - resolution: {integrity: sha512-1KP64yqDPQ3rupxNv7oXhf7KdhHHgaqbKuspVoiN93TT0xrBjql+Svjkdjq/Qh/7GSMmgQs3AfvBT0heE35thw==} + /zod-validation-error@3.5.2(zod@3.23.8): + resolution: {integrity: sha512-mdi7YOLtram5dzJ5aDtm1AG9+mxRma1iaMrZdYIpFO7epdKBUwLHIxTF8CPDeCQ828zAXYtizrKlEJAtzgfgrw==} engines: {node: '>=18.0.0'} peerDependencies: - zod: ^3.24.4 + zod: ^3.25.0 dependencies: zod: 3.23.8 dev: true From 6471f18c6d79e6b575f4663b1c85f3d1faed06c7 Mon Sep 17 00:00:00 2001 From: MichaelUnkey Date: Thu, 26 Jun 2025 13:21:28 -0400 Subject: [PATCH 4/8] rabbit changes --- .../components/control-cloud/index.tsx | 2 +- .../filter/control-cloud.examples.tsx | 91 ++++++++----------- .../ui/src/hooks/use-keyboard-shortcut.tsx | 11 +-- internal/ui/src/lib/utils.ts | 5 + .../structured-output-schema-generator.ts | 45 ++++++++- 5 files changed, 89 insertions(+), 65 deletions(-) diff --git a/apps/dashboard/app/(app)/settings/root-keys/components/control-cloud/index.tsx b/apps/dashboard/app/(app)/settings/root-keys/components/control-cloud/index.tsx index b6c209b0aa..5e5b0a3439 100644 --- a/apps/dashboard/app/(app)/settings/root-keys/components/control-cloud/index.tsx +++ b/apps/dashboard/app/(app)/settings/root-keys/components/control-cloud/index.tsx @@ -1,5 +1,5 @@ import { HISTORICAL_DATA_WINDOW } from "@/components/logs/constants"; -import { ControlCloud } from "@/components/logs/control-cloud"; +import { ControlCloud } from "@unkey/ui"; import type { RootKeysFilterField } from "../../filters.schema"; import { useFilters } from "../../hooks/use-filters"; diff --git a/apps/engineering/content/design/components/filter/control-cloud.examples.tsx b/apps/engineering/content/design/components/filter/control-cloud.examples.tsx index 5632d9e2da..15e0ccb57a 100644 --- a/apps/engineering/content/design/components/filter/control-cloud.examples.tsx +++ b/apps/engineering/content/design/components/filter/control-cloud.examples.tsx @@ -4,6 +4,16 @@ import { ControlCloud } from "@unkey/ui"; import type { FilterOperator } from "@unkey/ui/src/validation/filter.types"; import { useEffect, useState } from "react"; +// Type declarations for modern User-Agent Client Hints API +declare global { + interface Navigator { + userAgentData?: { + platform: string; + getHighEntropyValues(hints: string[]): Promise<{ platform: string }>; + }; + } +} + // Define FilterValue type locally for examples type FilterValue = { id: string; @@ -16,6 +26,20 @@ type FilterValue = { }; }; +// Shared field name formatter +const defaultFormatFieldName = (field: string): string => { + const fieldMap: Record = { + status: "Status", + method: "Method", + path: "Path", + startTime: "Start time", + endTime: "End time", + duration: "Duration", + }; + + return fieldMap[field] ?? field.charAt(0).toUpperCase() + field.slice(1); +}; + // Mock filter data for examples const createMockFilter = ( field: string, @@ -23,7 +47,7 @@ const createMockFilter = ( value: string | number, id?: string, ): FilterValue => ({ - id: id || crypto.randomUUID(), + id: id || (crypto.randomUUID?.() ?? `filter-${Math.random().toString(36).substr(2, 9)}`), field, operator, value, @@ -43,18 +67,7 @@ export function BasicControlCloud() { setFilters(newFilters); }; - const formatFieldName = (field: string): string => { - switch (field) { - case "status": - return "Status"; - case "method": - return "Method"; - case "path": - return "Path"; - default: - return field.charAt(0).toUpperCase() + field.slice(1); - } - }; + const formatFieldName = defaultFormatFieldName; return ( @@ -85,16 +98,7 @@ export function TimeBasedFilters() { setFilters(newFilters); }; - const formatFieldName = (field: string): string => { - switch (field) { - case "startTime": - return "Start time"; - case "endTime": - return "End time"; - default: - return field.charAt(0).toUpperCase() + field.slice(1); - } - }; + const formatFieldName = defaultFormatFieldName; return ( @@ -127,20 +131,7 @@ export function MultipleFilterTypes() { setFilters(newFilters); }; - const formatFieldName = (field: string): string => { - switch (field) { - case "status": - return "Status"; - case "method": - return "Method"; - case "path": - return "Path"; - case "duration": - return "Duration"; - default: - return field.charAt(0).toUpperCase() + field.slice(1); - } - }; + const formatFieldName = defaultFormatFieldName; const formatValue = (value: string | number, field: string): string => { if (field === "duration") { @@ -178,9 +169,7 @@ export function EmptyState() { setFilters(newFilters); }; - const formatFieldName = (field: string): string => { - return field.charAt(0).toUpperCase() + field.slice(1); - }; + const formatFieldName = defaultFormatFieldName; return ( @@ -205,7 +194,14 @@ export function InteractiveExample() { // Client-side platform detection useEffect(() => { - setIsMac(/(Mac|iPhone|iPod|iPad)/i.test(navigator.platform)); + if (navigator.userAgentData) { + navigator.userAgentData.getHighEntropyValues(["platform"]).then((ua) => { + setIsMac(ua.platform === "macOS"); + }); + } else { + // Fallback for browsers without userAgentData support + setIsMac(/(Mac|iPhone|iPod|iPad)/i.test(navigator.platform)); + } }, []); const removeFilter = (id: string) => { @@ -221,18 +217,7 @@ export function InteractiveExample() { setFilters([...filters, newFilter]); }; - const formatFieldName = (field: string): string => { - switch (field) { - case "status": - return "Status"; - case "method": - return "Method"; - case "path": - return "Path"; - default: - return field.charAt(0).toUpperCase() + field.slice(1); - } - }; + const formatFieldName = defaultFormatFieldName; return ( diff --git a/internal/ui/src/hooks/use-keyboard-shortcut.tsx b/internal/ui/src/hooks/use-keyboard-shortcut.tsx index b189f10973..8fbe517100 100644 --- a/internal/ui/src/hooks/use-keyboard-shortcut.tsx +++ b/internal/ui/src/hooks/use-keyboard-shortcut.tsx @@ -1,4 +1,4 @@ -import { useCallback, useEffect } from "react"; +import { useEffect } from "react"; /** * Represents the parsed details of a keyboard shortcut. @@ -227,9 +227,8 @@ export function useKeyboardShortcut( const { preventDefault, ignoreInputs, ignoreContentEditable, disabled } = mergedOptions; - // Memoize callback for stability in the useEffect dependency array - // biome-ignore lint/correctness/useExhaustiveDependencies: callback is intentionally excluded to prevent infinite re-renders - const memoizedCallback = useCallback(callback, [callback]); + // Use the callback directly - memoization with [callback] dependency provides no benefit + const stableCallback = callback; useEffect(() => { // Parse the shortcut definition inside the effect. @@ -286,7 +285,7 @@ export function useKeyboardShortcut( if (preventDefault) { e.preventDefault(); // Prevent default browser action if configured } - memoizedCallback(); + stableCallback(); } }; @@ -295,5 +294,5 @@ export function useKeyboardShortcut( return () => { document.removeEventListener("keydown", handleKeyDown); }; - }, [shortcut, memoizedCallback, preventDefault, ignoreInputs, ignoreContentEditable, disabled]); + }, [shortcut, stableCallback, preventDefault, ignoreInputs, ignoreContentEditable, disabled]); } diff --git a/internal/ui/src/lib/utils.ts b/internal/ui/src/lib/utils.ts index 7eea8c488c..07b037aa72 100644 --- a/internal/ui/src/lib/utils.ts +++ b/internal/ui/src/lib/utils.ts @@ -192,6 +192,11 @@ export const deepMerge = (target: T, source: T): T => const result = { ...target } as T; for (const key in source) { + // Protect against prototype pollution + if (key === "__proto__" || key === "constructor" || key === "prototype") { + continue; + } + const sourceValue = source[key]; const targetValue = result[key]; diff --git a/internal/ui/src/validation/utils/structured-output-schema-generator.ts b/internal/ui/src/validation/utils/structured-output-schema-generator.ts index 07bd001abe..5640b2ac5b 100644 --- a/internal/ui/src/validation/utils/structured-output-schema-generator.ts +++ b/internal/ui/src/validation/utils/structured-output-schema-generator.ts @@ -22,17 +22,52 @@ export function createFilterOutputSchema< .refine( (data) => { const config = filterFieldConfig[data.field as keyof TConfig]; - return data.filters.every((filter) => { + return !data.filters.find((filter) => { const isOperatorValid = config.operators.includes( filter.operator as z.infer, ); - return ( - isOperatorValid && - validateFieldValue(data.field as keyof TConfig, filter.value, filterFieldConfig) + + if (!isOperatorValid) { + return true; + } + + return !validateFieldValue( + data.field as keyof TConfig, + filter.value, + filterFieldConfig, + ); + }); + }, + (data) => { + const config = filterFieldConfig[data.field as keyof TConfig]; + const invalidFilter = data.filters.find((filter) => { + const isOperatorValid = config.operators.includes( + filter.operator as z.infer, + ); + if (!isOperatorValid) { + return true; + } + return !validateFieldValue( + data.field as keyof TConfig, + filter.value, + filterFieldConfig, ); }); + if (invalidFilter) { + const isOperatorValid = config.operators.includes( + invalidFilter.operator as z.infer, + ); + if (!isOperatorValid) { + return { + message: `Invalid operator "${invalidFilter.operator}" for field "${data.field}"`, + }; + } + return { + message: `Invalid value "${invalidFilter.value}" for field "${data.field}"`, + }; + } + return { message: "Invalid field/operator/value combination" }; }, - { message: "Invalid field/operator/value combination" }, ), ), }); From d040a9c9b2e8d883219b96c8510295ffaa8f667b Mon Sep 17 00:00:00 2001 From: MichaelUnkey Date: Fri, 27 Jun 2025 08:46:19 -0400 Subject: [PATCH 5/8] suggested refactor --- .../structured-output-schema-generator.ts | 94 +++++++++++-------- 1 file changed, 55 insertions(+), 39 deletions(-) diff --git a/internal/ui/src/validation/utils/structured-output-schema-generator.ts b/internal/ui/src/validation/utils/structured-output-schema-generator.ts index 5640b2ac5b..e008951f8e 100644 --- a/internal/ui/src/validation/utils/structured-output-schema-generator.ts +++ b/internal/ui/src/validation/utils/structured-output-schema-generator.ts @@ -2,6 +2,37 @@ import { z } from "zod"; import type { FieldConfig } from "../filter.types"; import { isNumberConfig, isStringConfig } from "./type-guards"; +// Helper function to validate a single filter and return detailed result +function validateSingleFilter< + TFieldEnum extends z.ZodEnum<[string, ...string[]]>, + TOperatorEnum extends z.ZodEnum<[string, ...string[]]>, + TConfig extends Record, FieldConfig>>, +>( + field: keyof TConfig, + filter: { operator: z.infer; value: string | number }, + filterFieldConfig: TConfig, +) { + const config = filterFieldConfig[field]; + + const isOperatorValid = config.operators.includes(filter.operator); + if (!isOperatorValid) { + return { + valid: false, + error: `Invalid operator "${filter.operator}" for field "${String(field)}"`, + }; + } + + const isValueValid = validateFieldValue(field, filter.value, filterFieldConfig); + if (!isValueValid) { + return { + valid: false, + error: `Invalid value "${filter.value}" for field "${String(field)}"`, + }; + } + + return { valid: true }; +} + export function createFilterOutputSchema< TFieldEnum extends z.ZodEnum<[string, ...string[]]>, TOperatorEnum extends z.ZodEnum<[string, ...string[]]>, @@ -21,51 +52,36 @@ export function createFilterOutputSchema< }) .refine( (data) => { - const config = filterFieldConfig[data.field as keyof TConfig]; - return !data.filters.find((filter) => { - const isOperatorValid = config.operators.includes( - filter.operator as z.infer, - ); - - if (!isOperatorValid) { - return true; - } - - return !validateFieldValue( - data.field as keyof TConfig, - filter.value, - filterFieldConfig, - ); - }); + // Check if all filters are valid + return data.filters.every( + (filter) => + validateSingleFilter( + data.field as keyof TConfig, + filter as { operator: string; value: string | number }, + filterFieldConfig, + ).valid, + ); }, (data) => { - const config = filterFieldConfig[data.field as keyof TConfig]; - const invalidFilter = data.filters.find((filter) => { - const isOperatorValid = config.operators.includes( - filter.operator as z.infer, - ); - if (!isOperatorValid) { - return true; - } - return !validateFieldValue( + // Find the first invalid filter and return its error message + const invalidFilter = data.filters.find( + (filter) => + !validateSingleFilter( + data.field as keyof TConfig, + filter as { operator: string; value: string | number }, + filterFieldConfig, + ).valid, + ); + + if (invalidFilter) { + const validation = validateSingleFilter( data.field as keyof TConfig, - filter.value, + invalidFilter as { operator: string; value: string | number }, filterFieldConfig, ); - }); - if (invalidFilter) { - const isOperatorValid = config.operators.includes( - invalidFilter.operator as z.infer, - ); - if (!isOperatorValid) { - return { - message: `Invalid operator "${invalidFilter.operator}" for field "${data.field}"`, - }; - } - return { - message: `Invalid value "${invalidFilter.value}" for field "${data.field}"`, - }; + return { message: validation.error || "Invalid field/operator/value combination" }; } + return { message: "Invalid field/operator/value combination" }; }, ), From 9811dcca7f5b15d4adaa63d6cddef36d58883128 Mon Sep 17 00:00:00 2001 From: MichaelUnkey Date: Mon, 30 Jun 2025 12:33:35 -0400 Subject: [PATCH 6/8] changes for comments on pr --- .../structured-output-schema-generator.ts | 55 +++++++++++-------- 1 file changed, 31 insertions(+), 24 deletions(-) diff --git a/internal/ui/src/validation/utils/structured-output-schema-generator.ts b/internal/ui/src/validation/utils/structured-output-schema-generator.ts index e008951f8e..d9d22688d8 100644 --- a/internal/ui/src/validation/utils/structured-output-schema-generator.ts +++ b/internal/ui/src/validation/utils/structured-output-schema-generator.ts @@ -52,34 +52,41 @@ export function createFilterOutputSchema< }) .refine( (data) => { - // Check if all filters are valid - return data.filters.every( - (filter) => - validateSingleFilter( - data.field as keyof TConfig, - filter as { operator: string; value: string | number }, - filterFieldConfig, - ).valid, - ); - }, - (data) => { - // Find the first invalid filter and return its error message - const invalidFilter = data.filters.find( - (filter) => - !validateSingleFilter( - data.field as keyof TConfig, - filter as { operator: string; value: string | number }, - filterFieldConfig, - ).valid, - ); + // Validate all filters and cache results + const validationResults = data.filters.map((filter) => { + if ( + typeof filter.operator !== "string" || + (typeof filter.value !== "string" && typeof filter.value !== "number") + ) { + throw new Error("Invalid filter structure"); + } - if (invalidFilter) { - const validation = validateSingleFilter( + return validateSingleFilter( data.field as keyof TConfig, - invalidFilter as { operator: string; value: string | number }, + filter as { operator: string; value: string | number }, filterFieldConfig, ); - return { message: validation.error || "Invalid field/operator/value combination" }; + }); + + // Store results for error handling + (data as Record)._validationResults = validationResults; + return validationResults.every((result) => result.valid); + }, + (data) => { + // Use cached validation results + const validationResults = + ((data as Record)._validationResults as Array<{ + valid: boolean; + error?: string; + }>) || []; + const firstInvalidResult = validationResults.find( + (result: { valid: boolean; error?: string }) => !result.valid, + ); + + if (firstInvalidResult) { + return { + message: firstInvalidResult.error || "Invalid field/operator/value combination", + }; } return { message: "Invalid field/operator/value combination" }; From 802bb81abd8662484c9d679528a866265f41c711 Mon Sep 17 00:00:00 2001 From: MichaelUnkey Date: Mon, 30 Jun 2025 14:19:38 -0400 Subject: [PATCH 7/8] created type for validation store --- .../utils/structured-output-schema-generator.ts | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/internal/ui/src/validation/utils/structured-output-schema-generator.ts b/internal/ui/src/validation/utils/structured-output-schema-generator.ts index d9d22688d8..feab90071e 100644 --- a/internal/ui/src/validation/utils/structured-output-schema-generator.ts +++ b/internal/ui/src/validation/utils/structured-output-schema-generator.ts @@ -2,6 +2,11 @@ import { z } from "zod"; import type { FieldConfig } from "../filter.types"; import { isNumberConfig, isStringConfig } from "./type-guards"; +// Interface for data with validation results +type DataWithValidationResults = { + _validationResults?: Array<{ valid: boolean; error?: string }>; +} + // Helper function to validate a single filter and return detailed result function validateSingleFilter< TFieldEnum extends z.ZodEnum<[string, ...string[]]>, @@ -69,16 +74,12 @@ export function createFilterOutputSchema< }); // Store results for error handling - (data as Record)._validationResults = validationResults; + (data as DataWithValidationResults)._validationResults = validationResults; return validationResults.every((result) => result.valid); }, (data) => { // Use cached validation results - const validationResults = - ((data as Record)._validationResults as Array<{ - valid: boolean; - error?: string; - }>) || []; + const validationResults = (data as DataWithValidationResults)._validationResults || []; const firstInvalidResult = validationResults.find( (result: { valid: boolean; error?: string }) => !result.valid, ); From 173fa42ebb673169851303a43353caa69f12943c Mon Sep 17 00:00:00 2001 From: "autofix-ci[bot]" <114827586+autofix-ci[bot]@users.noreply.github.com> Date: Mon, 30 Jun 2025 18:22:11 +0000 Subject: [PATCH 8/8] [autofix.ci] apply automated fixes --- .../src/validation/utils/structured-output-schema-generator.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/ui/src/validation/utils/structured-output-schema-generator.ts b/internal/ui/src/validation/utils/structured-output-schema-generator.ts index feab90071e..49020b88a9 100644 --- a/internal/ui/src/validation/utils/structured-output-schema-generator.ts +++ b/internal/ui/src/validation/utils/structured-output-schema-generator.ts @@ -5,7 +5,7 @@ import { isNumberConfig, isStringConfig } from "./type-guards"; // Interface for data with validation results type DataWithValidationResults = { _validationResults?: Array<{ valid: boolean; error?: string }>; -} +}; // Helper function to validate a single filter and return detailed result function validateSingleFilter<