From b0f66903f1b7fc81d0b51b20b57f1c9aa170b955 Mon Sep 17 00:00:00 2001 From: Bryan Frimin Date: Wed, 8 Apr 2026 14:46:55 +0200 Subject: [PATCH] WIP Signed-off-by: Bryan Frimin --- .gitignore | 1 + GNUmakefile | 8 +- apps/console/package.json | 1 + .../iam/organizations/_components/Sidebar.tsx | 8 + .../cookie-banners/CookieBannerDetailPage.tsx | 280 + .../cookie-banners/CookieBannersPage.tsx | 268 + .../_components/CookieBannerPreview.tsx | 83 + .../_components/CookieBannerStateBadge.tsx | 21 + .../dialogs/CreateCookieBannerDialog.tsx | 136 + .../tabs/CookieBannerAppearanceTab.tsx | 543 ++ .../tabs/CookieBannerCategoriesTab.tsx | 549 ++ .../tabs/CookieBannerConsentRecordsTab.tsx | 156 + .../tabs/CookieBannerEmbedTab.tsx | 43 + .../tabs/CookieBannerOverviewTab.tsx | 283 + apps/console/src/routes.tsx | 2 + apps/console/src/routes/cookieBannerRoutes.ts | 111 + e2e/console/cookie_banner_test.go | 620 ++ e2e/internal/factory/factory.go | 102 + package-lock.json | 7378 +++++------------ packages/cookie-banner/cookie_banner.go | 41 + packages/cookie-banner/package.json | 22 + packages/cookie-banner/src/headless/api.ts | 123 + packages/cookie-banner/src/headless/apply.ts | 127 + .../src/headless/consent-manager.ts | 336 + .../src/headless/cookie-interceptor.ts | 128 + .../cookie-banner/src/headless/cookies.ts | 70 + packages/cookie-banner/src/headless/events.ts | 37 + packages/cookie-banner/src/headless/gcm.ts | 122 + packages/cookie-banner/src/headless/gpc.ts | 22 + packages/cookie-banner/src/headless/i18n.ts | 81 + packages/cookie-banner/src/headless/index.ts | 26 + .../cookie-banner/src/headless/observer.ts | 74 + .../cookie-banner/src/headless/storage.ts | 154 + packages/cookie-banner/src/headless/types.ts | 91 + .../src/styled/banner-renderer.ts | 235 + packages/cookie-banner/src/styled/index.ts | 24 + .../src/styled/placeholder-renderer.ts | 196 + .../src/styled/preferences-renderer.ts | 224 + .../src/styled/revisit-renderer.ts | 51 + .../cookie-banner/src/styled/styled-banner.ts | 135 + packages/cookie-banner/src/styled/theme.ts | 30 + .../cookie-banner/src/styled/widget-entry.ts | 109 + packages/cookie-banner/tsconfig.json | 13 + packages/cookie-banner/vite.config.ts | 33 + pkg/cmd/cookiebanner/category/category.go | 38 + .../cookiebanner/category/create/create.go | 141 + .../cookiebanner/category/delete/delete.go | 102 + pkg/cmd/cookiebanner/category/list/list.go | 179 + .../cookiebanner/category/update/update.go | 125 + pkg/cmd/cookiebanner/consent/consent.go | 32 + pkg/cmd/cookiebanner/consent/list/list.go | 176 + pkg/cmd/cookiebanner/cookiebanner.go | 50 + pkg/cmd/cookiebanner/create/create.go | 157 + pkg/cmd/cookiebanner/delete/delete.go | 102 + pkg/cmd/cookiebanner/disable/disable.go | 101 + pkg/cmd/cookiebanner/list/list.go | 195 + pkg/cmd/cookiebanner/provider/add/add.go | 118 + pkg/cmd/cookiebanner/provider/list/list.go | 134 + pkg/cmd/cookiebanner/provider/provider.go | 36 + pkg/cmd/cookiebanner/provider/view/view.go | 146 + pkg/cmd/cookiebanner/publish/publish.go | 101 + pkg/cmd/cookiebanner/update/update.go | 162 + pkg/cmd/cookiebanner/view/view.go | 170 + pkg/cmd/root/root.go | 2 + pkg/consent/consent_record.go | 107 + pkg/consent/cookie_banner.go | 393 + pkg/consent/cookie_category.go | 391 + pkg/consent/service.go | 158 + pkg/consent/service_test.go | 79 + pkg/cookieprovider/cookieprovider.go | 323 + pkg/cookieprovider/cookieprovider_test.go | 114 + pkg/coredata/consent_action.go | 86 + pkg/coredata/consent_mode.go | 71 + pkg/coredata/consent_record.go | 261 + pkg/coredata/consent_record_filter.go | 50 + pkg/coredata/consent_record_order_field.go | 55 + pkg/coredata/cookie_banner.go | 430 + pkg/coredata/cookie_banner_order_field.go | 55 + pkg/coredata/cookie_banner_state.go | 75 + pkg/coredata/cookie_category.go | 396 + pkg/coredata/cookie_category_order_field.go | 55 + pkg/coredata/entity_type_reg.go | 9 + pkg/coredata/migrations/20260408T143300Z.sql | 64 + pkg/probo/actions.go | 17 + pkg/probo/policies.go | 3 + pkg/probo/service.go | 1 - pkg/probod/probod.go | 4 + pkg/server/api/api.go | 15 + pkg/server/api/console/v1/graphql_handler.go | 17 +- pkg/server/api/console/v1/resolver.go | 17 +- pkg/server/api/console/v1/schema.graphql | 440 + .../api/console/v1/types/consent_record.go | 79 + .../api/console/v1/types/cookie_banner.go | 337 + .../api/console/v1/types/cookie_category.go | 81 + pkg/server/api/console/v1/v1_resolver.go | 511 +- pkg/server/api/cookiebanner/v1/handler.go | 157 + pkg/server/api/cookiebanner/v1/types/types.go | 188 + pkg/server/api/mcp/v1/resolver.go | 2 + pkg/server/api/mcp/v1/schema.resolvers.go | 307 + pkg/server/api/mcp/v1/specification.yaml | 989 ++- pkg/server/api/mcp/v1/types/cookie_banner.go | 199 + pkg/server/api/mcp/v1/v1_handler.go | 4 +- pkg/server/server.go | 3 + 103 files changed, 16744 insertions(+), 5163 deletions(-) create mode 100644 apps/console/src/pages/organizations/cookie-banners/CookieBannerDetailPage.tsx create mode 100644 apps/console/src/pages/organizations/cookie-banners/CookieBannersPage.tsx create mode 100644 apps/console/src/pages/organizations/cookie-banners/_components/CookieBannerPreview.tsx create mode 100644 apps/console/src/pages/organizations/cookie-banners/_components/CookieBannerStateBadge.tsx create mode 100644 apps/console/src/pages/organizations/cookie-banners/dialogs/CreateCookieBannerDialog.tsx create mode 100644 apps/console/src/pages/organizations/cookie-banners/tabs/CookieBannerAppearanceTab.tsx create mode 100644 apps/console/src/pages/organizations/cookie-banners/tabs/CookieBannerCategoriesTab.tsx create mode 100644 apps/console/src/pages/organizations/cookie-banners/tabs/CookieBannerConsentRecordsTab.tsx create mode 100644 apps/console/src/pages/organizations/cookie-banners/tabs/CookieBannerEmbedTab.tsx create mode 100644 apps/console/src/pages/organizations/cookie-banners/tabs/CookieBannerOverviewTab.tsx create mode 100644 apps/console/src/routes/cookieBannerRoutes.ts create mode 100644 e2e/console/cookie_banner_test.go create mode 100644 packages/cookie-banner/cookie_banner.go create mode 100644 packages/cookie-banner/package.json create mode 100644 packages/cookie-banner/src/headless/api.ts create mode 100644 packages/cookie-banner/src/headless/apply.ts create mode 100644 packages/cookie-banner/src/headless/consent-manager.ts create mode 100644 packages/cookie-banner/src/headless/cookie-interceptor.ts create mode 100644 packages/cookie-banner/src/headless/cookies.ts create mode 100644 packages/cookie-banner/src/headless/events.ts create mode 100644 packages/cookie-banner/src/headless/gcm.ts create mode 100644 packages/cookie-banner/src/headless/gpc.ts create mode 100644 packages/cookie-banner/src/headless/i18n.ts create mode 100644 packages/cookie-banner/src/headless/index.ts create mode 100644 packages/cookie-banner/src/headless/observer.ts create mode 100644 packages/cookie-banner/src/headless/storage.ts create mode 100644 packages/cookie-banner/src/headless/types.ts create mode 100644 packages/cookie-banner/src/styled/banner-renderer.ts create mode 100644 packages/cookie-banner/src/styled/index.ts create mode 100644 packages/cookie-banner/src/styled/placeholder-renderer.ts create mode 100644 packages/cookie-banner/src/styled/preferences-renderer.ts create mode 100644 packages/cookie-banner/src/styled/revisit-renderer.ts create mode 100644 packages/cookie-banner/src/styled/styled-banner.ts create mode 100644 packages/cookie-banner/src/styled/theme.ts create mode 100644 packages/cookie-banner/src/styled/widget-entry.ts create mode 100644 packages/cookie-banner/tsconfig.json create mode 100644 packages/cookie-banner/vite.config.ts create mode 100644 pkg/cmd/cookiebanner/category/category.go create mode 100644 pkg/cmd/cookiebanner/category/create/create.go create mode 100644 pkg/cmd/cookiebanner/category/delete/delete.go create mode 100644 pkg/cmd/cookiebanner/category/list/list.go create mode 100644 pkg/cmd/cookiebanner/category/update/update.go create mode 100644 pkg/cmd/cookiebanner/consent/consent.go create mode 100644 pkg/cmd/cookiebanner/consent/list/list.go create mode 100644 pkg/cmd/cookiebanner/cookiebanner.go create mode 100644 pkg/cmd/cookiebanner/create/create.go create mode 100644 pkg/cmd/cookiebanner/delete/delete.go create mode 100644 pkg/cmd/cookiebanner/disable/disable.go create mode 100644 pkg/cmd/cookiebanner/list/list.go create mode 100644 pkg/cmd/cookiebanner/provider/add/add.go create mode 100644 pkg/cmd/cookiebanner/provider/list/list.go create mode 100644 pkg/cmd/cookiebanner/provider/provider.go create mode 100644 pkg/cmd/cookiebanner/provider/view/view.go create mode 100644 pkg/cmd/cookiebanner/publish/publish.go create mode 100644 pkg/cmd/cookiebanner/update/update.go create mode 100644 pkg/cmd/cookiebanner/view/view.go create mode 100644 pkg/consent/consent_record.go create mode 100644 pkg/consent/cookie_banner.go create mode 100644 pkg/consent/cookie_category.go create mode 100644 pkg/consent/service.go create mode 100644 pkg/consent/service_test.go create mode 100644 pkg/cookieprovider/cookieprovider.go create mode 100644 pkg/cookieprovider/cookieprovider_test.go create mode 100644 pkg/coredata/consent_action.go create mode 100644 pkg/coredata/consent_mode.go create mode 100644 pkg/coredata/consent_record.go create mode 100644 pkg/coredata/consent_record_filter.go create mode 100644 pkg/coredata/consent_record_order_field.go create mode 100644 pkg/coredata/cookie_banner.go create mode 100644 pkg/coredata/cookie_banner_order_field.go create mode 100644 pkg/coredata/cookie_banner_state.go create mode 100644 pkg/coredata/cookie_category.go create mode 100644 pkg/coredata/cookie_category_order_field.go create mode 100644 pkg/coredata/migrations/20260408T143300Z.sql create mode 100644 pkg/server/api/console/v1/types/consent_record.go create mode 100644 pkg/server/api/console/v1/types/cookie_banner.go create mode 100644 pkg/server/api/console/v1/types/cookie_category.go create mode 100644 pkg/server/api/cookiebanner/v1/handler.go create mode 100644 pkg/server/api/cookiebanner/v1/types/types.go create mode 100644 pkg/server/api/mcp/v1/types/cookie_banner.go diff --git a/.gitignore b/.gitignore index e65d0c421..d06525d5d 100644 --- a/.gitignore +++ b/.gitignore @@ -18,4 +18,5 @@ compose/keycloak/probo-realm.json # Generated files (codegen) __generated__/ pkg/server/api/*/v1/types/types.go +!pkg/server/api/cookiebanner/v1/types/types.go cfg/dev_local.yaml diff --git a/GNUmakefile b/GNUmakefile index 87e6a13a4..1ceb702b5 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -57,7 +57,8 @@ PROBOD_BOOTSTRAP_SRC= cmd/probod-bootstrap/main.go ifndef SKIP_APPS PROBOD_BIN_EXTRA_DEPS += \ @probo/console \ - @probo/trust + @probo/trust \ + @probo/cookie-banner endif .PHONY: all @@ -235,6 +236,11 @@ bin/probod-bootstrap: $(NPM) --workspace $@ run check $(NPM) --workspace $@ run build +.PHONY: @probo/cookie-banner +@probo/cookie-banner: NODE_ENV=production +@probo/cookie-banner: + $(NPM) --workspace $@ run build + .PHONY: generate generate: pkg/server/api/connect/v1/schema/schema.go \ pkg/server/api/connect/v1/types/types.go \ diff --git a/apps/console/package.json b/apps/console/package.json index 104e4cf3e..091b6e07a 100644 --- a/apps/console/package.json +++ b/apps/console/package.json @@ -15,6 +15,7 @@ }, "dependencies": { "@hookform/resolvers": "^5.0.1", + "@probo/cookie-banner": "^0.0.0", "@probo/coredata": "^1.0.0", "@probo/helpers": "^1.0.0", "@probo/hooks": "1.0.0", diff --git a/apps/console/src/pages/iam/organizations/_components/Sidebar.tsx b/apps/console/src/pages/iam/organizations/_components/Sidebar.tsx index 367ca0cd6..00ea2356d 100644 --- a/apps/console/src/pages/iam/organizations/_components/Sidebar.tsx +++ b/apps/console/src/pages/iam/organizations/_components/Sidebar.tsx @@ -62,6 +62,7 @@ const fragment = graphql` ) canListRightsRequests: permission(action: "core:rights-request:list") canListSnapshots: permission(action: "core:snapshot:list") + canListCookieBanners: permission(action: "core:cookie-banner:list") canGetTrustCenter: permission(action: "core:trust-center:get") canUpdateOrganization: permission(action: "iam:organization:update") canListStatesOfApplicability: permission( @@ -211,6 +212,13 @@ export function Sidebar(props: { fKey: SidebarFragment$key }) { to={`${prefix}/access-reviews`} /> )} + {organization.canListCookieBanners && ( + + )} {organization.canGetTrustCenter && ( ; +}; + +export default function CookieBannerDetailPage(props: Props) { + const { node: banner } = usePreloadedQuery( + cookieBannerNodeQuery, + props.queryRef, + ); + const { __ } = useTranslate(); + const { toast } = useToast(); + const confirm = useConfirm(); + const organizationId = useOrganizationId(); + + const connectionId = ConnectionHandler.getConnectionID( + organizationId, + "CookieBannersPage_cookieBanners", + ); + + const [deleteCookieBanner] = useMutation(deleteCookieBannerMutation); + const [publishBanner] = useMutation(publishCookieBannerMutation); + const [disableBanner] = useMutation(disableCookieBannerMutation); + + const bannersUrl = `/organizations/${organizationId}/cookie-banners`; + const baseBannerUrl = `/organizations/${organizationId}/cookie-banners/${banner.id}`; + + const handlePublish = () => { + publishBanner({ + variables: { + input: { id: banner.id }, + }, + onCompleted() { + toast({ + title: __("Success"), + description: __("Cookie banner published successfully."), + variant: "success", + }); + }, + onError(error) { + toast({ + title: __("Error"), + description: formatError(__("Failed to publish cookie banner"), error as GraphQLError), + variant: "error", + }); + }, + }); + }; + + const handleDisable = () => { + disableBanner({ + variables: { + input: { id: banner.id }, + }, + onCompleted() { + toast({ + title: __("Success"), + description: __("Cookie banner disabled successfully."), + variant: "success", + }); + }, + onError(error) { + toast({ + title: __("Error"), + description: formatError(__("Failed to disable cookie banner"), error as GraphQLError), + variant: "error", + }); + }, + }); + }; + + const handleDelete = () => { + if (!banner.id || !banner.name) { + return alert(__("Failed to delete cookie banner: missing id or name")); + } + confirm( + () => + new Promise((resolve) => { + deleteCookieBanner({ + variables: { + input: { id: banner.id }, + connections: [connectionId], + }, + onCompleted() { + toast({ + title: __("Success"), + description: __("Cookie banner deleted successfully."), + variant: "success", + }); + resolve(); + }, + onError(error) { + toast({ + title: __("Error"), + description: formatError(__("Failed to delete cookie banner"), error as GraphQLError), + variant: "error", + }); + resolve(); + }, + }); + }), + { + message: sprintf( + __( + "This will permanently delete cookie banner \"%s\". This action cannot be undone.", + ), + banner.name, + ), + }, + ); + }; + + return ( +
+ + +
+
+
{banner.name}
+ +
+
+ {banner.canPublish && banner.state !== "PUBLISHED" && ( + + )} + {banner.canPublish && banner.state === "PUBLISHED" && ( + + )} + {banner.canDelete && ( + + + {__("Delete")} + + + )} +
+
+ + + + {__("Overview")} + + + {__("Appearance")} + + + {__("Categories")} + + + {__("Consent Records")} + + + {__("Embed")} + + + + +
+ ); +} diff --git a/apps/console/src/pages/organizations/cookie-banners/CookieBannersPage.tsx b/apps/console/src/pages/organizations/cookie-banners/CookieBannersPage.tsx new file mode 100644 index 000000000..f78088689 --- /dev/null +++ b/apps/console/src/pages/organizations/cookie-banners/CookieBannersPage.tsx @@ -0,0 +1,268 @@ +import { formatError, type GraphQLError, sprintf } from "@probo/helpers"; +import { usePageTitle } from "@probo/hooks"; +import { useTranslate } from "@probo/i18n"; +import { + ActionDropdown, + Button, + DropdownItem, + IconPlusLarge, + IconTrashCan, + PageHeader, + Tbody, + Td, + Th, + Thead, + Tr, + useConfirm, + useToast, +} from "@probo/ui"; +import { + graphql, + type PreloadedQuery, + useMutation, + usePaginationFragment, + usePreloadedQuery, +} from "react-relay"; + +import type { CookieBannersPageDeleteMutation } from "#/__generated__/core/CookieBannersPageDeleteMutation.graphql"; +import type { CookieBannersPageFragment$key } from "#/__generated__/core/CookieBannersPageFragment.graphql"; +import type { CookieBannersPageQuery } from "#/__generated__/core/CookieBannersPageQuery.graphql"; +import { SortableTable, SortableTh } from "#/components/SortableTable"; +import { useOrganizationId } from "#/hooks/useOrganizationId"; + +import { CookieBannerStateBadge } from "./_components/CookieBannerStateBadge"; +import { CreateCookieBannerDialog } from "./dialogs/CreateCookieBannerDialog"; + +/* eslint-disable relay/unused-fields, relay/must-colocate-fragment-spreads */ + +export const cookieBannersQuery = graphql` + query CookieBannersPageQuery($organizationId: ID!) { + node(id: $organizationId) { + ... on Organization { + canCreateCookieBanner: permission( + action: "core:cookie-banner:create" + ) + ...CookieBannersPageFragment + } + } + } +`; + +const deleteCookieBannerMutation = graphql` + mutation CookieBannersPageDeleteMutation( + $input: DeleteCookieBannerInput! + $connections: [ID!]! + ) { + deleteCookieBanner(input: $input) { + deletedCookieBannerId @deleteEdge(connections: $connections) + } + } +`; + +const paginatedCookieBannersFragment = graphql` + fragment CookieBannersPageFragment on Organization + @refetchable(queryName: "CookieBannersListQuery") + @argumentDefinitions( + first: { type: "Int", defaultValue: 50 } + order: { type: "CookieBannerOrder", defaultValue: null } + after: { type: "CursorKey", defaultValue: null } + before: { type: "CursorKey", defaultValue: null } + last: { type: "Int", defaultValue: null } + ) { + cookieBanners( + first: $first + after: $after + last: $last + before: $before + orderBy: $order + ) @connection(key: "CookieBannersPage_cookieBanners") { + __id + edges { + node { + # eslint-disable-next-line relay/unused-fields + id + # eslint-disable-next-line relay/unused-fields + name + # eslint-disable-next-line relay/unused-fields + domain + # eslint-disable-next-line relay/unused-fields + state + # eslint-disable-next-line relay/unused-fields + version + # eslint-disable-next-line relay/unused-fields + createdAt + canUpdate: permission(action: "core:cookie-banner:update") + canDelete: permission(action: "core:cookie-banner:delete") + } + } + } + } +`; + +type Props = { + queryRef: PreloadedQuery; +}; + +export default function CookieBannersPage(props: Props) { + const { __ } = useTranslate(); + const organizationId = useOrganizationId(); + + const data = usePreloadedQuery( + cookieBannersQuery, + props.queryRef, + ); + const pagination = usePaginationFragment( + paginatedCookieBannersFragment, + data.node as CookieBannersPageFragment$key, + ); + + const banners = pagination.data?.cookieBanners?.edges.map(edge => edge.node) ?? []; + const connectionId = pagination.data?.cookieBanners?.__id ?? ""; + + usePageTitle(__("Cookie Banners")); + + const hasAnyAction = banners.some( + ({ canUpdate, canDelete }) => canUpdate || canDelete, + ); + + return ( +
+ + {data.node.canCreateCookieBanner && ( + + + + )} + + {/* eslint-disable-next-line @typescript-eslint/no-explicit-any */} + + + + {__("Name")} + {__("Domain")} + {__("State")} + {__("Version")} + {hasAnyAction && } + + + + {banners?.map(banner => ( + + ))} + + +
+ ); +} + +type Banner = { + id: string; + name: string; + domain: string; + state: string; + version: number; + createdAt: string; + canUpdate: boolean; + canDelete: boolean; +}; + +function CookieBannerRow({ + banner, + organizationId, + connectionId, + hasAnyAction, +}: { + banner: Banner; + organizationId: string; + connectionId: string; + hasAnyAction: boolean; +}) { + const { __ } = useTranslate(); + const { toast } = useToast(); + const confirm = useConfirm(); + const [deleteCookieBanner] = useMutation(deleteCookieBannerMutation); + const bannerUrl = `/organizations/${organizationId}/cookie-banners/${banner.id}/overview`; + + const handleDelete = () => { + if (!banner.id || !banner.name) { + return alert(__("Failed to delete cookie banner: missing id or name")); + } + confirm( + () => + new Promise((resolve) => { + deleteCookieBanner({ + variables: { + input: { id: banner.id }, + connections: [connectionId], + }, + onCompleted() { + toast({ + title: __("Success"), + description: __("Cookie banner deleted successfully."), + variant: "success", + }); + resolve(); + }, + onError(error) { + toast({ + title: __("Error"), + description: formatError(__("Failed to delete cookie banner"), error as GraphQLError), + variant: "error", + }); + resolve(); + }, + }); + }), + { + message: sprintf( + __( + "This will permanently delete cookie banner \"%s\". This action cannot be undone.", + ), + banner.name, + ), + }, + ); + }; + + return ( + + {banner.name} + {banner.domain} + + + + v{banner.version} + {hasAnyAction && ( + + + {banner.canDelete && ( + + {__("Delete")} + + )} + + + )} + + ); +} diff --git a/apps/console/src/pages/organizations/cookie-banners/_components/CookieBannerPreview.tsx b/apps/console/src/pages/organizations/cookie-banners/_components/CookieBannerPreview.tsx new file mode 100644 index 000000000..f9f4176d1 --- /dev/null +++ b/apps/console/src/pages/organizations/cookie-banners/_components/CookieBannerPreview.tsx @@ -0,0 +1,83 @@ +import type { BannerCategory, ThemeConfig } from "@probo/cookie-banner/api"; +import { defaultTheme } from "@probo/cookie-banner/api"; +import { renderBanner } from "@probo/cookie-banner/banner"; +import { getStrings } from "@probo/cookie-banner/i18n"; +import { useEffect, useRef } from "react"; + +interface CookieBannerPreviewProps { + title: string; + description: string; + acceptAllLabel: string; + rejectAllLabel: string; + savePreferencesLabel: string; + categories: BannerCategory[]; + theme?: Partial; +} + +const noop = () => {}; + +export function CookieBannerPreview({ + title, + description, + acceptAllLabel, + rejectAllLabel, + savePreferencesLabel, + categories, + theme, +}: CookieBannerPreviewProps) { + const containerRef = useRef(null); + const shadowRef = useRef(null); + + useEffect(() => { + if (containerRef.current && !shadowRef.current) { + shadowRef.current = containerRef.current.attachShadow({ mode: "open" }); + } + }, []); + + useEffect(() => { + const shadow = shadowRef.current; + if (!shadow) return; + + const resolvedTheme = { ...defaultTheme, ...theme }; + + renderBanner( + shadow, + { + id: "preview", + title, + description, + accept_all_label: acceptAllLabel, + reject_all_label: rejectAllLabel, + save_preferences_label: savePreferencesLabel, + privacy_policy_url: "", + consent_expiry_days: 365, + version: 1, + categories, + theme: resolvedTheme, + }, + {}, + { onAcceptAll: noop, onRejectAll: noop, onCustomize: noop }, + getStrings("en"), + { preview: true, theme: resolvedTheme }, + ); + + // Override fixed positioning so the banner renders inline in the preview + const overrideStyle = document.createElement("style"); + overrideStyle.textContent = ` + .probo-banner-overlay { + position: relative !important; + } + `; + shadow.appendChild(overrideStyle); + }, [ + title, + description, + acceptAllLabel, + rejectAllLabel, + savePreferencesLabel, + categories, + theme, + ]); + + return
; +} diff --git a/apps/console/src/pages/organizations/cookie-banners/_components/CookieBannerStateBadge.tsx b/apps/console/src/pages/organizations/cookie-banners/_components/CookieBannerStateBadge.tsx new file mode 100644 index 000000000..84f8e8369 --- /dev/null +++ b/apps/console/src/pages/organizations/cookie-banners/_components/CookieBannerStateBadge.tsx @@ -0,0 +1,21 @@ +import { useTranslate } from "@probo/i18n"; +import { Badge } from "@probo/ui"; + +export function CookieBannerStateBadge({ + state, +}: { + state: string; +}) { + const { __ } = useTranslate(); + + switch (state) { + case "PUBLISHED": + return {__("Published")}; + case "DRAFT": + return {__("Draft")}; + case "DISABLED": + return {__("Disabled")}; + default: + return {state}; + } +} diff --git a/apps/console/src/pages/organizations/cookie-banners/dialogs/CreateCookieBannerDialog.tsx b/apps/console/src/pages/organizations/cookie-banners/dialogs/CreateCookieBannerDialog.tsx new file mode 100644 index 000000000..af6f52bce --- /dev/null +++ b/apps/console/src/pages/organizations/cookie-banners/dialogs/CreateCookieBannerDialog.tsx @@ -0,0 +1,136 @@ +import { formatError, type GraphQLError } from "@probo/helpers"; +import { useTranslate } from "@probo/i18n"; +import { + Button, + Dialog, + DialogContent, + DialogFooter, + Field, + useDialogRef, + useToast, +} from "@probo/ui"; +import type { ReactNode } from "react"; +import { useMutation } from "react-relay"; +import { graphql } from "relay-runtime"; +import { z } from "zod"; + +import type { CreateCookieBannerDialogMutation } from "#/__generated__/core/CreateCookieBannerDialogMutation.graphql"; +import { useFormWithSchema } from "#/hooks/useFormWithSchema"; + +const createCookieBannerMutation = graphql` + mutation CreateCookieBannerDialogMutation( + $input: CreateCookieBannerInput! + $connections: [ID!]! + ) { + createCookieBanner(input: $input) { + cookieBannerEdge @appendEdge(connections: $connections) { + node { + id + name + domain + state + version + createdAt + updatedAt + canUpdate: permission(action: "core:cookie-banner:update") + canDelete: permission(action: "core:cookie-banner:delete") + } + } + } + } +`; + +const schema = z.object({ + name: z.string().min(1, "Name is required"), + domain: z.string().optional(), +}); + +type Props = { + children: ReactNode; + organizationId: string; + connection: string; +}; + +export function CreateCookieBannerDialog({ + children, + organizationId, + connection, +}: Props) { + const { __ } = useTranslate(); + const { toast } = useToast(); + const dialogRef = useDialogRef(); + const [createCookieBanner] = useMutation(createCookieBannerMutation); + + const { + register, + handleSubmit, + reset, + formState: { errors, isSubmitting }, + } = useFormWithSchema(schema, { + defaultValues: { + name: "", + domain: "", + }, + }); + + const onSubmit = handleSubmit((formData) => { + createCookieBanner({ + variables: { + input: { + organizationId, + name: formData.name, + domain: formData.domain || null, + }, + connections: [connection], + }, + onCompleted() { + toast({ + title: __("Success"), + description: __("Cookie banner created successfully."), + variant: "success", + }); + reset(); + dialogRef.current?.close(); + }, + onError(error) { + toast({ + title: __("Error"), + description: formatError(__("Failed to create cookie banner"), error as GraphQLError), + variant: "error", + }); + }, + }); + }); + + return ( + +
void onSubmit(e)}> + + + + + + + +
+
+ ); +} diff --git a/apps/console/src/pages/organizations/cookie-banners/tabs/CookieBannerAppearanceTab.tsx b/apps/console/src/pages/organizations/cookie-banners/tabs/CookieBannerAppearanceTab.tsx new file mode 100644 index 000000000..bca81db19 --- /dev/null +++ b/apps/console/src/pages/organizations/cookie-banners/tabs/CookieBannerAppearanceTab.tsx @@ -0,0 +1,543 @@ +import { formatError, type GraphQLError } from "@probo/helpers"; +import { useTranslate } from "@probo/i18n"; +import { Button, Card, Field, useToast } from "@probo/ui"; +import { Controller, useWatch } from "react-hook-form"; +import { graphql, useFragment, useMutation } from "react-relay"; +import { useOutletContext } from "react-router"; +import { z } from "zod"; + +import type { CookieBannerAppearanceTabFragment$key } from "#/__generated__/core/CookieBannerAppearanceTabFragment.graphql"; +import type { CookieBannerAppearanceTabUpdateMutation } from "#/__generated__/core/CookieBannerAppearanceTabUpdateMutation.graphql"; +import type { CookieBannerDetailPageQuery$data } from "#/__generated__/core/CookieBannerDetailPageQuery.graphql"; +import { useFormWithSchema } from "#/hooks/useFormWithSchema"; +import { CookieBannerPreview } from "../_components/CookieBannerPreview"; + +const fragment = graphql` + fragment CookieBannerAppearanceTabFragment on CookieBanner { + id + title + description + acceptAllLabel + rejectAllLabel + savePreferencesLabel + canUpdate: permission(action: "core:cookie-banner:update") + theme { + primaryColor + primaryTextColor + secondaryColor + secondaryTextColor + backgroundColor + textColor + secondaryTextBodyColor + borderColor + fontFamily + borderRadius + position + revisitPosition + } + categories(first: 50, orderBy: { field: RANK, direction: ASC }) { + edges { + node { + id + name + description + required + rank + cookies { + name + duration + description + } + } + } + } + } +`; + +const updateCookieBannerMutation = graphql` + mutation CookieBannerAppearanceTabUpdateMutation( + $input: UpdateCookieBannerInput! + ) { + updateCookieBanner(input: $input) { + cookieBanner { + id + name + domain + state + title + description + acceptAllLabel + rejectAllLabel + savePreferencesLabel + privacyPolicyUrl + consentExpiryDays + version + updatedAt + theme { + primaryColor + primaryTextColor + secondaryColor + secondaryTextColor + backgroundColor + textColor + secondaryTextBodyColor + borderColor + fontFamily + borderRadius + position + revisitPosition + } + } + } + } +`; + +const hexColorRegex = /^#[0-9a-fA-F]{6}$/; + +const schema = z.object({ + title: z.string().min(1, "Title is required"), + description: z.string().min(1, "Description is required"), + acceptAllLabel: z.string().min(1, "Label is required"), + rejectAllLabel: z.string().min(1, "Label is required"), + savePreferencesLabel: z.string().min(1, "Label is required"), + primaryColor: z.string().regex(hexColorRegex, "Must be a valid hex color"), + primaryTextColor: z.string().regex(hexColorRegex, "Must be a valid hex color"), + backgroundColor: z.string().regex(hexColorRegex, "Must be a valid hex color"), + textColor: z.string().regex(hexColorRegex, "Must be a valid hex color"), + secondaryTextBodyColor: z.string().regex(hexColorRegex, "Must be a valid hex color"), + borderColor: z.string().regex(hexColorRegex, "Must be a valid hex color"), + borderRadius: z.coerce.number().min(0).max(24), + fontFamily: z.string().min(1, "Font family is required"), + position: z.enum(["bottom", "bottom-left", "bottom-right", "center"]), + revisitPosition: z.enum(["bottom-left", "bottom-right"]), +}); + +const lightPreset = { + primaryColor: "#2563eb", + primaryTextColor: "#ffffff", + backgroundColor: "#ffffff", + textColor: "#1a1a1a", + secondaryTextBodyColor: "#4b5563", + borderColor: "#e5e7eb", +}; + +const darkPreset = { + primaryColor: "#3b82f6", + primaryTextColor: "#ffffff", + backgroundColor: "#1f2937", + textColor: "#f9fafb", + secondaryTextBodyColor: "#9ca3af", + borderColor: "#374151", +}; + +function ColorField({ + label, + value, + onChange, + disabled, + error, +}: { + label: string; + value: string; + onChange: (value: string) => void; + disabled?: boolean; + error?: string; +}) { + return ( +
+ +
+ onChange(e.target.value)} + disabled={disabled} + className="h-9 w-9 shrink-0 cursor-pointer rounded-lg border border-border-mid p-0.5 disabled:cursor-not-allowed disabled:opacity-60" + /> + onChange(e.target.value)} + disabled={disabled} + className="w-full rounded-[10px] border border-border-mid bg-secondary px-3 py-[6px] text-sm text-txt-primary hover:border-border-strong disabled:bg-transparent disabled:opacity-60" + /> +
+ {error && {error}} +
+ ); +} + +export default function CookieBannerAppearanceTab() { + const { banner } = useOutletContext<{ + banner: CookieBannerDetailPageQuery$data["node"]; + }>(); + + const { __ } = useTranslate(); + const { toast } = useToast(); + const data = useFragment( + fragment, + banner, + ); + + const [mutate] = useMutation(updateCookieBannerMutation); + + const { + control, + register, + handleSubmit, + formState: { errors, isDirty, isSubmitting }, + reset, + setValue, + } = useFormWithSchema(schema, { + defaultValues: { + title: data.title, + description: data.description, + acceptAllLabel: data.acceptAllLabel, + rejectAllLabel: data.rejectAllLabel, + savePreferencesLabel: data.savePreferencesLabel, + primaryColor: data.theme.primaryColor, + primaryTextColor: data.theme.primaryTextColor, + backgroundColor: data.theme.backgroundColor, + textColor: data.theme.textColor, + secondaryTextBodyColor: data.theme.secondaryTextBodyColor, + borderColor: data.theme.borderColor, + borderRadius: data.theme.borderRadius, + fontFamily: data.theme.fontFamily, + position: data.theme.position as "bottom" | "bottom-left" | "bottom-right" | "center", + revisitPosition: data.theme.revisitPosition as "bottom-left" | "bottom-right", + }, + }); + + const watchedValues = useWatch({ control }); + + const categories = (data.categories?.edges ?? []).map((edge) => ({ + id: edge.node.id, + name: edge.node.name, + description: edge.node.description, + required: edge.node.required, + rank: edge.node.rank, + cookies: edge.node.cookies.map((c) => ({ + name: c.name, + duration: c.duration, + description: c.description, + })), + })); + + const isFormDisabled = isSubmitting || !data.canUpdate; + + const applyPreset = (preset: typeof lightPreset) => { + for (const [key, value] of Object.entries(preset)) { + setValue(key as keyof typeof preset, value, { shouldDirty: true }); + } + }; + + const themeForPreview = { + primary_color: watchedValues.primaryColor ?? data.theme.primaryColor, + primary_text_color: watchedValues.primaryTextColor ?? data.theme.primaryTextColor, + background_color: watchedValues.backgroundColor ?? data.theme.backgroundColor, + text_color: watchedValues.textColor ?? data.theme.textColor, + secondary_text_body_color: watchedValues.secondaryTextBodyColor ?? data.theme.secondaryTextBodyColor, + border_color: watchedValues.borderColor ?? data.theme.borderColor, + border_radius: watchedValues.borderRadius ?? data.theme.borderRadius, + font_family: watchedValues.fontFamily ?? data.theme.fontFamily, + position: watchedValues.position ?? data.theme.position, + revisit_position: watchedValues.revisitPosition ?? data.theme.revisitPosition, + }; + + const onSubmit = handleSubmit((formData) => { + const { + primaryColor, + primaryTextColor, + backgroundColor, + textColor, + secondaryTextBodyColor, + borderColor, + borderRadius, + fontFamily, + position, + revisitPosition, + ...contentData + } = formData; + + mutate({ + variables: { + input: { + id: data.id, + ...contentData, + theme: { + primaryColor, + primaryTextColor, + backgroundColor, + textColor, + secondaryTextBodyColor, + borderColor, + borderRadius, + fontFamily, + position, + revisitPosition, + }, + }, + }, + onCompleted() { + toast({ + title: __("Success"), + description: __("Cookie banner updated successfully."), + variant: "success", + }); + reset(formData); + }, + onError(error) { + toast({ + title: __("Error"), + description: formatError(__("Failed to update cookie banner"), error as GraphQLError), + variant: "error", + }); + }, + }); + }); + + return ( +
+
void onSubmit(e)} className="space-y-12"> +
+

{__("Banner Content")}

+ + + + +
+ +
+

{__("Button Labels")}

+ + + + + +
+ +
+
+

{__("Theme")}

+
+ + +
+
+ +
+ ( + + )} + /> + ( + + )} + /> + ( + + )} + /> + ( + + )} + /> + ( + + )} + /> + ( + + )} + /> +
+ + + +
+
+ +
+

{__("Layout")}

+ + ( +
+ + +
+ )} + /> + ( +
+ + +
+ )} + /> +
+
+ + {isDirty && data.canUpdate && ( +
+ +
+ )} +
+ +
+

{__("Preview")}

+ + + +
+
+ ); +} diff --git a/apps/console/src/pages/organizations/cookie-banners/tabs/CookieBannerCategoriesTab.tsx b/apps/console/src/pages/organizations/cookie-banners/tabs/CookieBannerCategoriesTab.tsx new file mode 100644 index 000000000..9ab4b0ba4 --- /dev/null +++ b/apps/console/src/pages/organizations/cookie-banners/tabs/CookieBannerCategoriesTab.tsx @@ -0,0 +1,549 @@ +import { formatError, type GraphQLError, sprintf } from "@probo/helpers"; +import { useTranslate } from "@probo/i18n"; +import { + ActionDropdown, + Badge, + Button, + Card, + Dialog, + DialogContent, + DialogFooter, + DropdownItem, + Field, + IconPencil, + IconPlusLarge, + IconTrashCan, + useConfirm, + useDialogRef, + useToast, +} from "@probo/ui"; +import { graphql, useFragment, useMutation } from "react-relay"; +import { useOutletContext } from "react-router"; +import { z } from "zod"; + +import type { CookieBannerCategoriesTabCreateMutation } from "#/__generated__/core/CookieBannerCategoriesTabCreateMutation.graphql"; +import type { CookieBannerCategoriesTabDeleteMutation } from "#/__generated__/core/CookieBannerCategoriesTabDeleteMutation.graphql"; +import type { CookieBannerCategoriesTabFragment$key } from "#/__generated__/core/CookieBannerCategoriesTabFragment.graphql"; +import type { CookieBannerCategoriesTabUpdateMutation } from "#/__generated__/core/CookieBannerCategoriesTabUpdateMutation.graphql"; +import type { CookieBannerDetailPageQuery$data } from "#/__generated__/core/CookieBannerDetailPageQuery.graphql"; +import { useFormWithSchema } from "#/hooks/useFormWithSchema"; + +const fragment = graphql` + fragment CookieBannerCategoriesTabFragment on CookieBanner { + id + canUpdate: permission(action: "core:cookie-banner:update") + categories(first: 50, orderBy: { field: RANK, direction: ASC }) { + edges { + node { + id + name + description + required + rank + cookies { + name + duration + description + } + } + } + } + } +`; + +const createCategoryMutation = graphql` + mutation CookieBannerCategoriesTabCreateMutation( + $input: CreateCookieCategoryInput! + ) { + createCookieCategory(input: $input) { + cookieCategory { + id + name + description + required + rank + cookies { + name + duration + description + } + } + } + } +`; + +const updateCategoryMutation = graphql` + mutation CookieBannerCategoriesTabUpdateMutation( + $input: UpdateCookieCategoryInput! + ) { + updateCookieCategory(input: $input) { + cookieCategory { + id + name + description + required + rank + cookies { + name + duration + description + } + } + } + } +`; + +const deleteCategoryMutation = graphql` + mutation CookieBannerCategoriesTabDeleteMutation( + $input: DeleteCookieCategoryInput! + ) { + deleteCookieCategory(input: $input) { + deletedCookieCategoryId + } + } +`; + +type CategoryNode = { + id: string; + name: string; + description: string; + required: boolean; + rank: number; + cookies: readonly { + readonly name: string; + readonly duration: string; + readonly description: string; + }[]; +}; + +export default function CookieBannerCategoriesTab() { + const { banner } = useOutletContext<{ + banner: CookieBannerDetailPageQuery$data["node"]; + }>(); + + const { __ } = useTranslate(); + const data = useFragment( + fragment, + banner, + ); + + const categories = data.categories?.edges.map((edge) => edge.node) ?? []; + + return ( +
+
+

{__("Cookie Categories")}

+ {data.canUpdate && ( + + + + )} +
+ +
+ {categories.map((category) => ( + + ))} +
+
+ ); +} + +function CategoryCard({ + category, + canUpdate, +}: { + category: CategoryNode; + canUpdate: boolean; +}) { + const { __ } = useTranslate(); + const { toast } = useToast(); + const confirm = useConfirm(); + const [deleteCategory] = useMutation(deleteCategoryMutation); + + const handleDelete = () => { + confirm( + () => + new Promise((resolve) => { + deleteCategory({ + variables: { + input: { id: category.id }, + }, + onCompleted() { + toast({ + title: __("Success"), + description: __("Category deleted successfully."), + variant: "success", + }); + resolve(); + }, + onError(error) { + toast({ + title: __("Error"), + description: formatError(__("Failed to delete category"), error as GraphQLError), + variant: "error", + }); + resolve(); + }, + }); + }), + { + message: sprintf( + __( + 'This will permanently delete category "%s". This action cannot be undone.', + ), + category.name, + ), + }, + ); + }; + + return ( + +
+
+
+ {category.name} + {category.required && ( + {__("Required")} + )} +
+

{category.description}

+ {category.cookies.length > 0 && ( +
+ + + + + + + + + + {category.cookies.map((cookie) => ( + + + + + + ))} + +
{__("Cookie")}{__("Duration")}{__("Description")}
+ {cookie.name} + + {cookie.duration} + + {cookie.description} +
+
+ )} +
+ {canUpdate && ( + + + {__("Edit")} + + {!category.required && ( + + {__("Delete")} + + )} + + )} +
+
+ ); +} + +const categorySchema = z.object({ + name: z.string().min(1, "Name is required"), + description: z.string().optional(), + rank: z.number().min(0), +}); + +function CreateCategoryDialog({ + children, + cookieBannerId, +}: { + children: React.ReactNode; + cookieBannerId: string; +}) { + const { __ } = useTranslate(); + const { toast } = useToast(); + const dialogRef = useDialogRef(); + + const [createCategory] = useMutation(createCategoryMutation); + + const { + register, + handleSubmit, + reset, + formState: { errors, isSubmitting }, + } = useFormWithSchema(categorySchema, { + defaultValues: { + name: "", + description: "", + rank: 0, + }, + }); + + const onSubmit = handleSubmit((formData) => { + createCategory({ + variables: { + input: { + cookieBannerId, + name: formData.name, + description: formData.description || null, + rank: formData.rank, + }, + }, + onCompleted() { + toast({ + title: __("Success"), + description: __("Category created successfully."), + variant: "success", + }); + reset(); + dialogRef.current?.close(); + }, + onError(error) { + toast({ + title: __("Error"), + description: formatError(__("Failed to create category"), error as GraphQLError), + variant: "error", + }); + }, + }); + }); + + return ( + +
void onSubmit(e)}> + + + + + + + + +
+
+ ); +} + +const editCategorySchema = z.object({ + name: z.string().min(1, "Name is required"), + description: z.string().optional(), + rank: z.number().min(0), + cookies: z.array( + z.object({ + name: z.string().min(1, "Cookie name is required"), + duration: z.string().min(1, "Duration is required"), + description: z.string(), + }), + ), +}); + +function EditCategoryDialog({ + children, + category, +}: { + children: React.ReactNode; + category: CategoryNode; +}) { + const { __ } = useTranslate(); + const { toast } = useToast(); + const dialogRef = useDialogRef(); + + const [updateCategory] = useMutation(updateCategoryMutation); + + const { + register, + handleSubmit, + reset, + watch, + setValue, + formState: { errors, isSubmitting }, + } = useFormWithSchema(editCategorySchema, { + defaultValues: { + name: category.name, + description: category.description, + rank: category.rank, + cookies: category.cookies.map((c) => ({ + name: c.name, + duration: c.duration, + description: c.description, + })), + }, + }); + + const cookies = watch("cookies"); + + const addCookie = () => { + setValue( + "cookies", + [...cookies, { name: "", duration: "", description: "" }], + { shouldDirty: true }, + ); + }; + + const removeCookie = (index: number) => { + setValue( + "cookies", + cookies.filter((_, i) => i !== index), + { shouldDirty: true }, + ); + }; + + const onSubmit = handleSubmit((formData) => { + updateCategory({ + variables: { + input: { + id: category.id, + name: formData.name, + description: formData.description || null, + rank: formData.rank, + cookies: formData.cookies, + }, + }, + onCompleted() { + toast({ + title: __("Success"), + description: __("Category updated successfully."), + variant: "success", + }); + reset(formData); + dialogRef.current?.close(); + }, + onError(error) { + toast({ + title: __("Error"), + description: formatError(__("Failed to update category"), error as GraphQLError), + variant: "error", + }); + }, + }); + }); + + return ( + +
void onSubmit(e)}> + + + + + +
+
+ + +
+ + {cookies.map((_, index) => ( +
+
+ + {sprintf(__("Cookie %d"), index + 1)} + + +
+ + + +
+ ))} +
+
+ + + +
+
+ ); +} diff --git a/apps/console/src/pages/organizations/cookie-banners/tabs/CookieBannerConsentRecordsTab.tsx b/apps/console/src/pages/organizations/cookie-banners/tabs/CookieBannerConsentRecordsTab.tsx new file mode 100644 index 000000000..9bda577a2 --- /dev/null +++ b/apps/console/src/pages/organizations/cookie-banners/tabs/CookieBannerConsentRecordsTab.tsx @@ -0,0 +1,156 @@ +import { formatDate } from "@probo/helpers"; +import { useTranslate } from "@probo/i18n"; +import { Badge, Tbody, Td, Th, Thead, Tr } from "@probo/ui"; +import { useState } from "react"; +import { graphql, usePaginationFragment } from "react-relay"; +import { useOutletContext } from "react-router"; + +import type { CookieBannerConsentRecordsTabFragment$key } from "#/__generated__/core/CookieBannerConsentRecordsTabFragment.graphql"; +import type { CookieBannerDetailPageQuery$data } from "#/__generated__/core/CookieBannerDetailPageQuery.graphql"; +import { SortableTable, SortableTh } from "#/components/SortableTable"; + +const fragment = graphql` + fragment CookieBannerConsentRecordsTabFragment on CookieBanner + @refetchable(queryName: "CookieBannerConsentRecordsListQuery") + @argumentDefinitions( + first: { type: "Int", defaultValue: 50 } + order: { type: "ConsentRecordOrder", defaultValue: null } + after: { type: "CursorKey", defaultValue: null } + before: { type: "CursorKey", defaultValue: null } + last: { type: "Int", defaultValue: null } + filter: { type: "ConsentRecordFilter", defaultValue: null } + ) { + consentRecords( + first: $first + after: $after + last: $last + before: $before + orderBy: $order + filter: $filter + ) + @connection( + key: "CookieBannerConsentRecordsTab_consentRecords" + ) { + edges { + node { + # eslint-disable-next-line relay/unused-fields + id + # eslint-disable-next-line relay/unused-fields + visitorId + # eslint-disable-next-line relay/unused-fields + action + # eslint-disable-next-line relay/unused-fields + bannerVersion + # eslint-disable-next-line relay/unused-fields + ipAddress + # eslint-disable-next-line relay/unused-fields + createdAt + } + } + } + } +`; + +function ConsentActionBadge({ action }: { action: string }) { + const { __ } = useTranslate(); + + switch (action) { + case "ACCEPT_ALL": + return {__("Accept All")}; + case "REJECT_ALL": + return {__("Reject All")}; + case "CUSTOMIZE": + return {__("Customize")}; + case "ACCEPT_CATEGORY": + return {__("Accept Category")}; + case "GPC": + return {__("GPC")}; + default: + return {action}; + } +} + +const ACTION_OPTIONS = [ + { value: "", label: "All Actions" }, + { value: "ACCEPT_ALL", label: "Accept All" }, + { value: "REJECT_ALL", label: "Reject All" }, + { value: "CUSTOMIZE", label: "Customize" }, + { value: "ACCEPT_CATEGORY", label: "Accept Category" }, + { value: "GPC", label: "GPC" }, +] as const; + +export default function CookieBannerConsentRecordsTab() { + const { banner } = useOutletContext<{ + banner: CookieBannerDetailPageQuery$data["node"]; + }>(); + + const { __ } = useTranslate(); + const [actionFilter, setActionFilter] = useState(""); + + const pagination = usePaginationFragment( + fragment, + banner as CookieBannerConsentRecordsTabFragment$key, + ); + + const handleFilterChange = (value: string) => { + setActionFilter(value); + pagination.refetch({ + filter: value ? { action: value } : null, + first: 50, + after: null, + last: null, + before: null, + }); + }; + + const records = + pagination.data.consentRecords?.edges.map((edge) => edge.node) ?? []; + + return ( +
+
+ +
+ + {/* eslint-disable-next-line @typescript-eslint/no-explicit-any */} + + + + {__("Visitor ID")} + {__("Action")} + {__("Banner Version")} + {__("IP Address")} + {__("Date")} + + + + {records.map((record) => ( + + + + {record.visitorId.slice(0, 12)}... + + + + + + v{record.bannerVersion} + {record.ipAddress ?? "-"} + {formatDate(record.createdAt)} + + ))} + + +
+ ); +} diff --git a/apps/console/src/pages/organizations/cookie-banners/tabs/CookieBannerEmbedTab.tsx b/apps/console/src/pages/organizations/cookie-banners/tabs/CookieBannerEmbedTab.tsx new file mode 100644 index 000000000..1b75c777e --- /dev/null +++ b/apps/console/src/pages/organizations/cookie-banners/tabs/CookieBannerEmbedTab.tsx @@ -0,0 +1,43 @@ +import { useTranslate } from "@probo/i18n"; +import { Button, Card } from "@probo/ui"; +import { useCallback } from "react"; +import { useOutletContext } from "react-router"; + +import type { CookieBannerDetailPageQuery$data } from "#/__generated__/core/CookieBannerDetailPageQuery.graphql"; + +export default function CookieBannerEmbedTab() { + const { banner } = useOutletContext<{ + banner: CookieBannerDetailPageQuery$data["node"]; + }>(); + + const { __ } = useTranslate(); + + const snippet = banner.embedSnippet ?? ""; + + const handleCopy = useCallback(() => { + void navigator.clipboard.writeText(snippet); + }, [snippet]); + + return ( +
+
+

{__("Embed Snippet")}

+

+ {__( + "Add this script tag to your website's HTML to display the cookie consent banner. Place it in the section of your page.", + )} +

+ +
+            {snippet}
+          
+
+ +
+
+
+
+ ); +} diff --git a/apps/console/src/pages/organizations/cookie-banners/tabs/CookieBannerOverviewTab.tsx b/apps/console/src/pages/organizations/cookie-banners/tabs/CookieBannerOverviewTab.tsx new file mode 100644 index 000000000..8b08086ed --- /dev/null +++ b/apps/console/src/pages/organizations/cookie-banners/tabs/CookieBannerOverviewTab.tsx @@ -0,0 +1,283 @@ +import { formatError, type GraphQLError } from "@probo/helpers"; +import { useTranslate } from "@probo/i18n"; +import { Button, Card, Field, useToast } from "@probo/ui"; +import { Controller } from "react-hook-form"; +import { graphql, useFragment, useMutation } from "react-relay"; +import { useOutletContext } from "react-router"; +import { z } from "zod"; + +import type { CookieBannerDetailPageQuery$data } from "#/__generated__/core/CookieBannerDetailPageQuery.graphql"; +import type { CookieBannerOverviewTabFragment$key } from "#/__generated__/core/CookieBannerOverviewTabFragment.graphql"; +import type { CookieBannerOverviewTabUpdateMutation } from "#/__generated__/core/CookieBannerOverviewTabUpdateMutation.graphql"; +import { useFormWithSchema } from "#/hooks/useFormWithSchema"; + +const fragment = graphql` + fragment CookieBannerOverviewTabFragment on CookieBanner { + id + name + domain + privacyPolicyUrl + consentExpiryDays + consentMode + canUpdate: permission(action: "core:cookie-banner:update") + analytics { + totalRecords + acceptAllCount + rejectAllCount + customizeCount + acceptCategoryCount + gpcCount + } + } +`; + +const updateCookieBannerMutation = graphql` + mutation CookieBannerOverviewTabUpdateMutation( + $input: UpdateCookieBannerInput! + ) { + updateCookieBanner(input: $input) { + cookieBanner { + id + name + domain + privacyPolicyUrl + consentExpiryDays + consentMode + version + updatedAt + } + } + } +`; + +const schema = z.object({ + name: z.string().min(1, "Name is required"), + domain: z.string().min(1, "Domain is required"), + privacyPolicyUrl: z.string().optional().nullable(), + consentExpiryDays: z.number().min(1), + consentMode: z.enum(["OPT_IN", "OPT_OUT"]), +}); + +function AnalyticsCard({ + analytics, +}: { + analytics: { + totalRecords: number; + acceptAllCount: number; + rejectAllCount: number; + customizeCount: number; + acceptCategoryCount: number; + gpcCount: number; + }; +}) { + const { __ } = useTranslate(); + + const rate = + analytics.totalRecords > 0 + ? Math.round( + (analytics.acceptAllCount / analytics.totalRecords) * 100, + ) + : 0; + + return ( + +
+
+
{analytics.totalRecords}
+
{__("Total Records")}
+
+
+
+ {analytics.acceptAllCount} +
+
{__("Accept All")}
+
+
+
+ {analytics.rejectAllCount} +
+
{__("Reject All")}
+
+
+
+ {analytics.customizeCount} +
+
{__("Customize")}
+
+
+
+ {analytics.acceptCategoryCount} +
+
{__("Accept Category")}
+
+
+
{rate}%
+
{__("Acceptance Rate")}
+
+
+
+ ); +} + +export default function CookieBannerOverviewTab() { + const { banner } = useOutletContext<{ + banner: CookieBannerDetailPageQuery$data["node"]; + }>(); + + const { __ } = useTranslate(); + const { toast } = useToast(); + const data = useFragment( + fragment, + banner, + ); + + const [mutate] = useMutation(updateCookieBannerMutation); + + const { + control, + register, + handleSubmit, + formState: { errors, isDirty, isSubmitting }, + reset, + } = useFormWithSchema(schema, { + defaultValues: { + name: data.name, + domain: data.domain, + privacyPolicyUrl: data.privacyPolicyUrl || null, + consentExpiryDays: data.consentExpiryDays, + consentMode: data.consentMode, + }, + }); + + const isFormDisabled = isSubmitting || !data.canUpdate; + + const onSubmit = handleSubmit((formData) => { + mutate({ + variables: { + input: { + id: data.id, + ...formData, + privacyPolicyUrl: formData.privacyPolicyUrl || null, + }, + }, + onCompleted() { + toast({ + title: __("Success"), + description: __("Cookie banner updated successfully."), + variant: "success", + }); + reset(formData); + }, + onError(error) { + toast({ + title: __("Error"), + description: formatError(__("Failed to update cookie banner"), error as GraphQLError), + variant: "error", + }); + }, + }); + }); + + return ( +
+
+

{__("Consent Analytics")}

+ +
+ +
void onSubmit(e)} className="space-y-12"> +
+

{__("General")}

+ + + + + + +
+ +
+

{__("Consent Mode")}

+ + ( +
+ + +
+ )} + /> +
+
+ + {isDirty && data.canUpdate && ( +
+ +
+ )} +
+
+ ); +} diff --git a/apps/console/src/routes.tsx b/apps/console/src/routes.tsx index eae747e4d..640695d89 100644 --- a/apps/console/src/routes.tsx +++ b/apps/console/src/routes.tsx @@ -47,6 +47,7 @@ import { riskRoutes } from "./routes/riskRoutes"; import { snapshotsRoutes } from "./routes/snapshotsRoutes"; import { statesOfApplicabilityRoutes } from "./routes/statesOfApplicabilityRoutes"; import { taskRoutes } from "./routes/taskRoutes"; +import { cookieBannerRoutes } from "./routes/cookieBannerRoutes"; import { vendorRoutes } from "./routes/vendorRoutes"; const routes = [ @@ -290,6 +291,7 @@ const routes = [ ...processingActivityRoutes, ...statesOfApplicabilityRoutes, ...accessReviewRoutes, + ...cookieBannerRoutes, ...compliancePageRoutes, ...snapshotsRoutes, { diff --git a/apps/console/src/routes/cookieBannerRoutes.ts b/apps/console/src/routes/cookieBannerRoutes.ts new file mode 100644 index 000000000..a79c016ed --- /dev/null +++ b/apps/console/src/routes/cookieBannerRoutes.ts @@ -0,0 +1,111 @@ +import { lazy } from "@probo/react-lazy"; +import { + type AppRoute, + loaderFromQueryLoader, + withQueryRef, +} from "@probo/routes"; +import { loadQuery } from "react-relay"; + +import type { CookieBannerDetailPageQuery } from "#/__generated__/core/CookieBannerDetailPageQuery.graphql"; +import type { CookieBannersPageQuery } from "#/__generated__/core/CookieBannersPageQuery.graphql"; +import { PageSkeleton } from "#/components/skeletons/PageSkeleton"; +import { coreEnvironment } from "#/environments"; + +import { + cookieBannerNodeQuery, +} from "../pages/organizations/cookie-banners/CookieBannerDetailPage"; +import { + cookieBannersQuery, +} from "../pages/organizations/cookie-banners/CookieBannersPage"; + +export const cookieBannerRoutes = [ + { + path: "cookie-banners", + Fallback: PageSkeleton, + loader: loaderFromQueryLoader(({ organizationId }) => + loadQuery( + coreEnvironment, + cookieBannersQuery, + { + organizationId: organizationId, + }, + ), + ), + Component: withQueryRef( + lazy( + () => + import( + "#/pages/organizations/cookie-banners/CookieBannersPage" + ), + ), + ), + }, + { + path: "cookie-banners/:cookieBannerId", + Fallback: PageSkeleton, + loader: loaderFromQueryLoader(({ cookieBannerId }) => + loadQuery( + coreEnvironment, + cookieBannerNodeQuery, + { + cookieBannerId, + }, + ), + ), + Component: withQueryRef( + lazy( + () => + import( + "#/pages/organizations/cookie-banners/CookieBannerDetailPage" + ), + ), + ), + children: [ + { + path: "overview", + Component: lazy( + () => + import( + "#/pages/organizations/cookie-banners/tabs/CookieBannerOverviewTab" + ), + ), + }, + { + path: "appearance", + Component: lazy( + () => + import( + "#/pages/organizations/cookie-banners/tabs/CookieBannerAppearanceTab" + ), + ), + }, + { + path: "categories", + Component: lazy( + () => + import( + "#/pages/organizations/cookie-banners/tabs/CookieBannerCategoriesTab" + ), + ), + }, + { + path: "consent-records", + Component: lazy( + () => + import( + "#/pages/organizations/cookie-banners/tabs/CookieBannerConsentRecordsTab" + ), + ), + }, + { + path: "embed", + Component: lazy( + () => + import( + "#/pages/organizations/cookie-banners/tabs/CookieBannerEmbedTab" + ), + ), + }, + ], + }, +] satisfies AppRoute[]; diff --git a/e2e/console/cookie_banner_test.go b/e2e/console/cookie_banner_test.go new file mode 100644 index 000000000..a022ee6f7 --- /dev/null +++ b/e2e/console/cookie_banner_test.go @@ -0,0 +1,620 @@ +// Copyright (c) 2026 Probo Inc . +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR +// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +// PERFORMANCE OF THIS SOFTWARE. + +package console_test + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "go.probo.inc/probo/e2e/internal/factory" + "go.probo.inc/probo/e2e/internal/testutil" +) + +func TestCookieBanner_Create(t *testing.T) { + t.Parallel() + owner := testutil.NewClient(t, testutil.RoleOwner) + + t.Run("with required fields", func(t *testing.T) { + t.Parallel() + + const query = ` + mutation($input: CreateCookieBannerInput!) { + createCookieBanner(input: $input) { + cookieBanner { + id + name + domain + state + version + } + } + } + ` + + var result struct { + CreateCookieBanner struct { + CookieBanner struct { + ID string `json:"id"` + Name string `json:"name"` + Domain string `json:"domain"` + State string `json:"state"` + Version int `json:"version"` + } `json:"cookieBanner"` + } `json:"createCookieBanner"` + } + + err := owner.Execute(query, map[string]any{ + "input": map[string]any{ + "organizationId": owner.GetOrganizationID().String(), + "name": "Main Site Banner", + "domain": "example.com", + }, + }, &result) + require.NoError(t, err) + + b := result.CreateCookieBanner.CookieBanner + assert.NotEmpty(t, b.ID) + assert.Equal(t, "Main Site Banner", b.Name) + assert.Equal(t, "example.com", b.Domain) + assert.Equal(t, "draft", b.State) + assert.Equal(t, 1, b.Version) + }) + + t.Run("creates default categories", func(t *testing.T) { + t.Parallel() + + bannerID := factory.NewCookieBanner(owner).WithName("Banner With Categories").Create() + + const query = ` + query($id: ID!) { + node(id: $id) { + ... on CookieBanner { + categories(first: 10) { + totalCount + edges { + node { + id + name + required + } + } + } + } + } + } + ` + + var result struct { + Node struct { + Categories struct { + TotalCount int `json:"totalCount"` + Edges []struct { + Node struct { + ID string `json:"id"` + Name string `json:"name"` + Required bool `json:"required"` + } `json:"node"` + } `json:"edges"` + } `json:"categories"` + } `json:"node"` + } + + err := owner.Execute(query, map[string]any{"id": bannerID}, &result) + require.NoError(t, err) + assert.Equal(t, 4, result.Node.Categories.TotalCount) + + hasRequired := false + for _, edge := range result.Node.Categories.Edges { + if edge.Node.Required { + hasRequired = true + break + } + } + assert.True(t, hasRequired, "should have at least one required category") + }) +} + +func TestCookieBanner_Update(t *testing.T) { + t.Parallel() + owner := testutil.NewClient(t, testutil.RoleOwner) + bannerID := factory.NewCookieBanner(owner).WithName("Banner to Update").Create() + + const query = ` + mutation($input: UpdateCookieBannerInput!) { + updateCookieBanner(input: $input) { + cookieBanner { + id + name + title + description + } + } + } + ` + + var result struct { + UpdateCookieBanner struct { + CookieBanner struct { + ID string `json:"id"` + Name string `json:"name"` + Title string `json:"title"` + Description string `json:"description"` + } `json:"cookieBanner"` + } `json:"updateCookieBanner"` + } + + err := owner.Execute(query, map[string]any{ + "input": map[string]any{ + "id": bannerID, + "name": "Updated Banner", + "title": "We use cookies", + "description": "This site uses cookies to improve your experience.", + }, + }, &result) + require.NoError(t, err) + + assert.Equal(t, bannerID, result.UpdateCookieBanner.CookieBanner.ID) + assert.Equal(t, "Updated Banner", result.UpdateCookieBanner.CookieBanner.Name) + assert.Equal(t, "We use cookies", result.UpdateCookieBanner.CookieBanner.Title) +} + +func TestCookieBanner_Delete(t *testing.T) { + t.Parallel() + owner := testutil.NewClient(t, testutil.RoleOwner) + bannerID := factory.NewCookieBanner(owner).WithName("Banner to Delete").Create() + + const query = ` + mutation($input: DeleteCookieBannerInput!) { + deleteCookieBanner(input: $input) { + deletedCookieBannerId + } + } + ` + + var result struct { + DeleteCookieBanner struct { + DeletedCookieBannerID string `json:"deletedCookieBannerId"` + } `json:"deleteCookieBanner"` + } + + err := owner.Execute(query, map[string]any{ + "input": map[string]any{ + "id": bannerID, + }, + }, &result) + require.NoError(t, err) + assert.Equal(t, bannerID, result.DeleteCookieBanner.DeletedCookieBannerID) +} + +func TestCookieBanner_List(t *testing.T) { + t.Parallel() + owner := testutil.NewClient(t, testutil.RoleOwner) + + for _, name := range []string{"Banner A", "Banner B", "Banner C"} { + factory.NewCookieBanner(owner).WithName(name).Create() + } + + const query = ` + query($orgId: ID!) { + node(id: $orgId) { + ... on Organization { + cookieBanners(first: 10) { + edges { + node { + id + name + } + } + totalCount + } + } + } + } + ` + + var result struct { + Node struct { + CookieBanners struct { + Edges []struct { + Node struct { + ID string `json:"id"` + Name string `json:"name"` + } `json:"node"` + } `json:"edges"` + TotalCount int `json:"totalCount"` + } `json:"cookieBanners"` + } `json:"node"` + } + + err := owner.Execute(query, map[string]any{ + "orgId": owner.GetOrganizationID().String(), + }, &result) + require.NoError(t, err) + assert.GreaterOrEqual(t, result.Node.CookieBanners.TotalCount, 3) +} + +func TestCookieBanner_Publish(t *testing.T) { + t.Parallel() + owner := testutil.NewClient(t, testutil.RoleOwner) + bannerID := factory.NewCookieBanner(owner).WithName("Banner to Publish").Create() + + const query = ` + mutation($input: PublishCookieBannerInput!) { + publishCookieBanner(input: $input) { + cookieBanner { + id + state + } + } + } + ` + + var result struct { + PublishCookieBanner struct { + CookieBanner struct { + ID string `json:"id"` + State string `json:"state"` + } `json:"cookieBanner"` + } `json:"publishCookieBanner"` + } + + err := owner.Execute(query, map[string]any{ + "input": map[string]any{"id": bannerID}, + }, &result) + require.NoError(t, err) + assert.Equal(t, "published", result.PublishCookieBanner.CookieBanner.State) +} + +func TestCookieBanner_Disable(t *testing.T) { + t.Parallel() + owner := testutil.NewClient(t, testutil.RoleOwner) + bannerID := factory.NewCookieBanner(owner).WithName("Banner to Disable").Create() + + // Publish first so we can disable. + const publishQuery = ` + mutation($input: PublishCookieBannerInput!) { + publishCookieBanner(input: $input) { + cookieBanner { id } + } + } + ` + + var publishResult struct { + PublishCookieBanner struct { + CookieBanner struct { + ID string `json:"id"` + } `json:"cookieBanner"` + } `json:"publishCookieBanner"` + } + + err := owner.Execute(publishQuery, map[string]any{ + "input": map[string]any{"id": bannerID}, + }, &publishResult) + require.NoError(t, err) + + const query = ` + mutation($input: DisableCookieBannerInput!) { + disableCookieBanner(input: $input) { + cookieBanner { + id + state + } + } + } + ` + + var result struct { + DisableCookieBanner struct { + CookieBanner struct { + ID string `json:"id"` + State string `json:"state"` + } `json:"cookieBanner"` + } `json:"disableCookieBanner"` + } + + err = owner.Execute(query, map[string]any{ + "input": map[string]any{"id": bannerID}, + }, &result) + require.NoError(t, err) + assert.Equal(t, "disabled", result.DisableCookieBanner.CookieBanner.State) +} + +func TestCookieCategory_Create(t *testing.T) { + t.Parallel() + owner := testutil.NewClient(t, testutil.RoleOwner) + bannerID := factory.NewCookieBanner(owner).WithName("Banner for Categories").Create() + + const query = ` + mutation($input: CreateCookieCategoryInput!) { + createCookieCategory(input: $input) { + cookieCategory { + id + name + description + required + rank + } + } + } + ` + + var result struct { + CreateCookieCategory struct { + CookieCategory struct { + ID string `json:"id"` + Name string `json:"name"` + Description string `json:"description"` + Required bool `json:"required"` + Rank int `json:"rank"` + } `json:"cookieCategory"` + } `json:"createCookieCategory"` + } + + err := owner.Execute(query, map[string]any{ + "input": map[string]any{ + "cookieBannerId": bannerID, + "name": "Marketing", + "description": "Marketing cookies", + "rank": 5, + }, + }, &result) + require.NoError(t, err) + + c := result.CreateCookieCategory.CookieCategory + assert.NotEmpty(t, c.ID) + assert.Equal(t, "Marketing", c.Name) + assert.Equal(t, "Marketing cookies", c.Description) + assert.False(t, c.Required) + assert.Equal(t, 5, c.Rank) +} + +func TestCookieCategory_Update(t *testing.T) { + t.Parallel() + owner := testutil.NewClient(t, testutil.RoleOwner) + bannerID := factory.NewCookieBanner(owner).WithName("Banner for Category Update").Create() + categoryID := factory.CreateCookieCategory(owner, bannerID, factory.Attrs{ + "name": "Old Name", + }) + + const query = ` + mutation($input: UpdateCookieCategoryInput!) { + updateCookieCategory(input: $input) { + cookieCategory { + id + name + } + } + } + ` + + var result struct { + UpdateCookieCategory struct { + CookieCategory struct { + ID string `json:"id"` + Name string `json:"name"` + } `json:"cookieCategory"` + } `json:"updateCookieCategory"` + } + + err := owner.Execute(query, map[string]any{ + "input": map[string]any{ + "id": categoryID, + "name": "New Name", + }, + }, &result) + require.NoError(t, err) + assert.Equal(t, categoryID, result.UpdateCookieCategory.CookieCategory.ID) + assert.Equal(t, "New Name", result.UpdateCookieCategory.CookieCategory.Name) +} + +func TestCookieCategory_Delete(t *testing.T) { + t.Parallel() + owner := testutil.NewClient(t, testutil.RoleOwner) + bannerID := factory.NewCookieBanner(owner).WithName("Banner for Category Delete").Create() + categoryID := factory.CreateCookieCategory(owner, bannerID, factory.Attrs{ + "name": "Category to Delete", + }) + + const query = ` + mutation($input: DeleteCookieCategoryInput!) { + deleteCookieCategory(input: $input) { + deletedCookieCategoryId + } + } + ` + + var result struct { + DeleteCookieCategory struct { + DeletedCookieCategoryID string `json:"deletedCookieCategoryId"` + } `json:"deleteCookieCategory"` + } + + err := owner.Execute(query, map[string]any{ + "input": map[string]any{ + "id": categoryID, + }, + }, &result) + require.NoError(t, err) + assert.Equal(t, categoryID, result.DeleteCookieCategory.DeletedCookieCategoryID) +} + +func TestCookieBanner_RBAC(t *testing.T) { + t.Parallel() + + owner := testutil.NewClient(t, testutil.RoleOwner) + viewer := testutil.NewClientInOrg(t, testutil.RoleViewer, owner) + + bannerID := factory.NewCookieBanner(owner).WithName("RBAC Banner").Create() + + t.Run("viewer can read cookie banner", func(t *testing.T) { + t.Parallel() + + const query = ` + query($id: ID!) { + node(id: $id) { + ... on CookieBanner { + id + name + } + } + } + ` + + var result struct { + Node *struct { + ID string `json:"id"` + Name string `json:"name"` + } `json:"node"` + } + + err := viewer.Execute(query, map[string]any{"id": bannerID}, &result) + require.NoError(t, err) + require.NotNil(t, result.Node) + assert.Equal(t, bannerID, result.Node.ID) + }) + + t.Run("viewer cannot create cookie banner", func(t *testing.T) { + t.Parallel() + + const query = ` + mutation($input: CreateCookieBannerInput!) { + createCookieBanner(input: $input) { + cookieBanner { id } + } + } + ` + + _, err := viewer.Do(query, map[string]any{ + "input": map[string]any{ + "organizationId": owner.GetOrganizationID().String(), + "name": factory.SafeName("Viewer Banner"), + }, + }) + testutil.RequireForbiddenError(t, err) + }) + + t.Run("viewer cannot update cookie banner", func(t *testing.T) { + t.Parallel() + + const query = ` + mutation($input: UpdateCookieBannerInput!) { + updateCookieBanner(input: $input) { + cookieBanner { id } + } + } + ` + + _, err := viewer.Do(query, map[string]any{ + "input": map[string]any{ + "id": bannerID, + "name": "Viewer Updated", + }, + }) + testutil.RequireForbiddenError(t, err) + }) + + t.Run("viewer cannot delete cookie banner", func(t *testing.T) { + t.Parallel() + + const query = ` + mutation($input: DeleteCookieBannerInput!) { + deleteCookieBanner(input: $input) { + deletedCookieBannerId + } + } + ` + + _, err := viewer.Do(query, map[string]any{ + "input": map[string]any{ + "id": bannerID, + }, + }) + testutil.RequireForbiddenError(t, err) + }) +} + +func TestCookieBanner_TenantIsolation(t *testing.T) { + t.Parallel() + + org1Owner := testutil.NewClient(t, testutil.RoleOwner) + org2Owner := testutil.NewClient(t, testutil.RoleOwner) + + bannerID := factory.NewCookieBanner(org1Owner).WithName("Org1 Banner").Create() + + t.Run("cannot read banner from another organization", func(t *testing.T) { + t.Parallel() + + const query = ` + query($id: ID!) { + node(id: $id) { + ... on CookieBanner { + id + name + } + } + } + ` + + var result struct { + Node *struct { + ID string `json:"id"` + Name string `json:"name"` + } `json:"node"` + } + + err := org2Owner.Execute(query, map[string]any{"id": bannerID}, &result) + testutil.AssertNodeNotAccessible(t, err, result.Node == nil, "cookie banner") + }) + + t.Run("cannot update banner from another organization", func(t *testing.T) { + t.Parallel() + + const query = ` + mutation($input: UpdateCookieBannerInput!) { + updateCookieBanner(input: $input) { + cookieBanner { id } + } + } + ` + + _, err := org2Owner.Do(query, map[string]any{ + "input": map[string]any{ + "id": bannerID, + "name": "Hijacked Banner", + }, + }) + require.Error(t, err, "Should not be able to update banner from another org") + }) + + t.Run("cannot delete banner from another organization", func(t *testing.T) { + t.Parallel() + + const query = ` + mutation($input: DeleteCookieBannerInput!) { + deleteCookieBanner(input: $input) { + deletedCookieBannerId + } + } + ` + + _, err := org2Owner.Do(query, map[string]any{ + "input": map[string]any{ + "id": bannerID, + }, + }) + require.Error(t, err, "Should not be able to delete banner from another org") + }) +} diff --git a/e2e/internal/factory/factory.go b/e2e/internal/factory/factory.go index 1e268e4ae..2e06837dd 100644 --- a/e2e/internal/factory/factory.go +++ b/e2e/internal/factory/factory.go @@ -1054,6 +1054,67 @@ func (b *AccessSourceBuilder) Create() string { return CreateAccessSource(b.client, b.organizationID, b.attrs) } +func CreateCookieBanner(c *testutil.Client, attrs ...Attrs) string { + c.T.Helper() + + var a Attrs + if len(attrs) > 0 { + a = attrs[0] + } + + const query = ` + mutation($input: CreateCookieBannerInput!) { + createCookieBanner(input: $input) { + cookieBanner { id } + } + } + ` + + input := map[string]any{ + "organizationId": c.GetOrganizationID().String(), + "name": a.getString("name", SafeName("CookieBanner")), + } + if domain := a.getStringPtr("domain"); domain != nil { + input["domain"] = *domain + } + + var result struct { + CreateCookieBanner struct { + CookieBanner struct { + ID string `json:"id"` + } `json:"cookieBanner"` + } `json:"createCookieBanner"` + } + + err := c.Execute(query, map[string]any{"input": input}, &result) + require.NoError(c.T, err, "CreateCookieBanner mutation failed") + + return result.CreateCookieBanner.CookieBanner.ID +} + +type CookieBannerBuilder struct { + client *testutil.Client + attrs Attrs +} + +func NewCookieBanner(c *testutil.Client) *CookieBannerBuilder { + return &CookieBannerBuilder{client: c, attrs: Attrs{}} +} + +func (b *CookieBannerBuilder) WithName(name string) *CookieBannerBuilder { + b.attrs["name"] = name + return b +} + +func (b *CookieBannerBuilder) WithDomain(domain string) *CookieBannerBuilder { + b.attrs["domain"] = domain + return b +} + +func (b *CookieBannerBuilder) Create() string { + return CreateCookieBanner(b.client, b.attrs) +} + func CreateAccessReviewCampaign(c *testutil.Client, organizationID string, attrs ...Attrs) string { c.T.Helper() @@ -1120,3 +1181,44 @@ func (b *AccessReviewCampaignBuilder) WithAccessSourceIDs(ids []string) *AccessR func (b *AccessReviewCampaignBuilder) Create() string { return CreateAccessReviewCampaign(b.client, b.organizationID, b.attrs) } + +func CreateCookieCategory(c *testutil.Client, bannerID string, attrs ...Attrs) string { + c.T.Helper() + + var a Attrs + if len(attrs) > 0 { + a = attrs[0] + } + + const query = ` + mutation($input: CreateCookieCategoryInput!) { + createCookieCategory(input: $input) { + cookieCategory { id } + } + } + ` + + input := map[string]any{ + "cookieBannerId": bannerID, + "name": a.getString("name", SafeName("Category")), + } + if desc := a.getStringPtr("description"); desc != nil { + input["description"] = *desc + } + if _, ok := a["rank"]; ok { + input["rank"] = a.getInt("rank", 0) + } + + var result struct { + CreateCookieCategory struct { + CookieCategory struct { + ID string `json:"id"` + } `json:"cookieCategory"` + } `json:"createCookieCategory"` + } + + err := c.Execute(query, map[string]any{"input": input}, &result) + require.NoError(c.T, err, "CreateCookieCategory mutation failed") + + return result.CreateCookieCategory.CookieCategory.ID +} diff --git a/package-lock.json b/package-lock.json index 6e67b0c0b..a5531084d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -26,6 +26,7 @@ "version": "0.0.0", "dependencies": { "@hookform/resolvers": "^5.0.1", + "@probo/cookie-banner": "^0.0.0", "@probo/coredata": "^1.0.0", "@probo/helpers": "^1.0.0", "@probo/hooks": "1.0.0", @@ -70,6 +71,96 @@ "vite": "^7.3.2" } }, + "apps/console/node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "apps/console/node_modules/vite": { + "version": "7.3.2", + "resolved": "https://registry.npmjs.org/vite/-/vite-7.3.2.tgz", + "integrity": "sha512-Bby3NOsna2jsjfLVOHKes8sGwgl4TT0E6vvpYgnAYDIF/tie7MRaFthmKuHx1NSXjiTueXH3do80FMQgvEktRg==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "^0.27.0", + "fdir": "^6.5.0", + "picomatch": "^4.0.3", + "postcss": "^8.5.6", + "rollup": "^4.43.0", + "tinyglobby": "^0.2.15" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^20.19.0 || >=22.12.0", + "jiti": ">=1.21.0", + "less": "^4.0.0", + "lightningcss": "^1.21.0", + "sass": "^1.70.0", + "sass-embedded": "^1.70.0", + "stylus": ">=0.54.8", + "sugarss": "^5.0.0", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, "apps/trust": { "name": "@probo/trust", "version": "0.0.0", @@ -111,17 +202,103 @@ "vite": "^7.3.2" } }, + "apps/trust/node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "apps/trust/node_modules/vite": { + "version": "7.3.2", + "resolved": "https://registry.npmjs.org/vite/-/vite-7.3.2.tgz", + "integrity": "sha512-Bby3NOsna2jsjfLVOHKes8sGwgl4TT0E6vvpYgnAYDIF/tie7MRaFthmKuHx1NSXjiTueXH3do80FMQgvEktRg==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "^0.27.0", + "fdir": "^6.5.0", + "picomatch": "^4.0.3", + "postcss": "^8.5.6", + "rollup": "^4.43.0", + "tinyglobby": "^0.2.15" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^20.19.0 || >=22.12.0", + "jiti": ">=1.21.0", + "less": "^4.0.0", + "lightningcss": "^1.21.0", + "sass": "^1.70.0", + "sass-embedded": "^1.70.0", + "stylus": ">=0.54.8", + "sugarss": "^5.0.0", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, "node_modules/@adobe/css-tools": { "version": "4.4.4", - "resolved": "https://registry.npmjs.org/@adobe/css-tools/-/css-tools-4.4.4.tgz", - "integrity": "sha512-Elp+iwUx5rN5+Y8xLt5/GRoG20WGoDCQ/1Fb+1LiGtvwbDavuSk0jhD/eZdckHAuzcDzccnkv+rEjyWfRx18gg==", "dev": true, "license": "MIT" }, "node_modules/@antfu/install-pkg": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@antfu/install-pkg/-/install-pkg-1.1.0.tgz", - "integrity": "sha512-MGQsmw10ZyI+EJo45CdSER4zEb+p31LpDAFp2Z3gkSd1yqVZGi0Ebx++YTEMonJy4oChEMLsxZ64j8FH6sSqtQ==", "license": "MIT", "dependencies": { "package-manager-detector": "^1.3.0", @@ -131,6 +308,13 @@ "url": "https://github.com/sponsors/antfu" } }, + "node_modules/@antfu/install-pkg/node_modules/tinyexec": { + "version": "1.0.2", + "license": "MIT", + "engines": { + "node": ">=18" + } + }, "node_modules/@anthropic-ai/sdk": { "version": "0.27.3", "resolved": "https://registry.npmjs.org/@anthropic-ai/sdk/-/sdk-0.27.3.tgz", @@ -169,17 +353,13 @@ }, "node_modules/@ariakit/core": { "version": "0.4.18", - "resolved": "https://registry.npmjs.org/@ariakit/core/-/core-0.4.18.tgz", - "integrity": "sha512-9urEa+GbZTSyredq3B/3thQjTcSZSUC68XctwCkJNH/xNfKN5O+VThiem2rcJxpsGw8sRUQenhagZi0yB4foyg==", "license": "MIT" }, "node_modules/@ariakit/react": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/@ariakit/react/-/react-0.4.24.tgz", - "integrity": "sha512-kL0+7ZdPXM8uJ2/cCudm94QKh2DAcE8kNcPnFgnyXaMhStpvkEIumSEu0dIHAGkv7s6NWWANrGZK7ADwcXjoXw==", + "version": "0.4.21", "license": "MIT", "dependencies": { - "@ariakit/react-core": "0.4.24" + "@ariakit/react-core": "0.4.21" }, "funding": { "type": "opencollective", @@ -191,9 +371,7 @@ } }, "node_modules/@ariakit/react-core": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/@ariakit/react-core/-/react-core-0.4.24.tgz", - "integrity": "sha512-MuqDooqkeaYCeMpvj+ygcONb2bS3CGniD3mW99l7P8Fioa+/kPvQCQfJjC6pR9mWFPCRiOpDjfXGREaYgm5olQ==", + "version": "0.4.21", "license": "MIT", "dependencies": { "@ariakit/core": "0.4.18", @@ -207,8 +385,6 @@ }, "node_modules/@babel/code-frame": { "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.0.tgz", - "integrity": "sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==", "dev": true, "license": "MIT", "dependencies": { @@ -222,15 +398,11 @@ }, "node_modules/@babel/code-frame/node_modules/picocolors": { "version": "1.1.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", - "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", "dev": true, "license": "ISC" }, "node_modules/@babel/compat-data": { "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.0.tgz", - "integrity": "sha512-T1NCJqT/j9+cn8fvkt7jtwbLBfLC/1y1c7NtCeXFRgzGTsafi68MRv8yzkYSapBnFA6L3U2VSc02ciDzoAJhJg==", "dev": true, "license": "MIT", "engines": { @@ -239,8 +411,6 @@ }, "node_modules/@babel/core": { "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.0.tgz", - "integrity": "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==", "dev": true, "license": "MIT", "dependencies": { @@ -270,8 +440,6 @@ }, "node_modules/@babel/core/node_modules/semver": { "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true, "license": "ISC", "bin": { @@ -280,8 +448,6 @@ }, "node_modules/@babel/generator": { "version": "7.29.1", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.1.tgz", - "integrity": "sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw==", "dev": true, "license": "MIT", "dependencies": { @@ -297,8 +463,6 @@ }, "node_modules/@babel/helper-compilation-targets": { "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.28.6.tgz", - "integrity": "sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA==", "dev": true, "license": "MIT", "dependencies": { @@ -314,8 +478,6 @@ }, "node_modules/@babel/helper-compilation-targets/node_modules/semver": { "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true, "license": "ISC", "bin": { @@ -324,8 +486,6 @@ }, "node_modules/@babel/helper-globals": { "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz", - "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==", "dev": true, "license": "MIT", "engines": { @@ -334,8 +494,6 @@ }, "node_modules/@babel/helper-module-imports": { "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.28.6.tgz", - "integrity": "sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw==", "dev": true, "license": "MIT", "dependencies": { @@ -348,8 +506,6 @@ }, "node_modules/@babel/helper-module-transforms": { "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.6.tgz", - "integrity": "sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA==", "dev": true, "license": "MIT", "dependencies": { @@ -366,8 +522,6 @@ }, "node_modules/@babel/helper-plugin-utils": { "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.28.6.tgz", - "integrity": "sha512-S9gzZ/bz83GRysI7gAD4wPT/AI3uCnY+9xn+Mx/KPs2JwHJIz1W8PZkg2cqyt3RNOBM8ejcXhV6y8Og7ly/Dug==", "dev": true, "license": "MIT", "engines": { @@ -376,8 +530,6 @@ }, "node_modules/@babel/helper-string-parser": { "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", - "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", "dev": true, "license": "MIT", "engines": { @@ -386,8 +538,6 @@ }, "node_modules/@babel/helper-validator-identifier": { "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", - "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", "dev": true, "license": "MIT", "engines": { @@ -396,8 +546,6 @@ }, "node_modules/@babel/helper-validator-option": { "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", - "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", "dev": true, "license": "MIT", "engines": { @@ -405,23 +553,19 @@ } }, "node_modules/@babel/helpers": { - "version": "7.29.2", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.29.2.tgz", - "integrity": "sha512-HoGuUs4sCZNezVEKdVcwqmZN8GoHirLUcLaYVNBK2J0DadGtdcqgr3BCbvH8+XUo4NGjNl3VOtSjEKNzqfFgKw==", + "version": "7.28.6", "dev": true, "license": "MIT", "dependencies": { "@babel/template": "^7.28.6", - "@babel/types": "^7.29.0" + "@babel/types": "^7.28.6" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/parser": { - "version": "7.29.2", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.2.tgz", - "integrity": "sha512-4GgRzy/+fsBa72/RZVJmGKPmZu9Byn8o4MoLpmNe1m8ZfYnz5emHLQz3U4gLud6Zwl0RZIcgiLD7Uq7ySFuDLA==", + "version": "7.29.0", "dev": true, "license": "MIT", "dependencies": { @@ -436,8 +580,6 @@ }, "node_modules/@babel/plugin-transform-react-jsx-self": { "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.27.1.tgz", - "integrity": "sha512-6UzkCs+ejGdZ5mFFC/OCUrv028ab2fp1znZmCZjAOBKiBK2jXD1O+BPSfX8X2qjJ75fZBMSnQn3Rq2mrBJK2mw==", "dev": true, "license": "MIT", "dependencies": { @@ -452,8 +594,6 @@ }, "node_modules/@babel/plugin-transform-react-jsx-source": { "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.27.1.tgz", - "integrity": "sha512-zbwoTsBruTeKB9hSq73ha66iFeJHuaFkUbwvqElnygoNbj/jHRsSeokowZFN3CZ64IvEqcmmkVe89OPXc7ldAw==", "dev": true, "license": "MIT", "dependencies": { @@ -467,9 +607,7 @@ } }, "node_modules/@babel/runtime": { - "version": "7.29.2", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.29.2.tgz", - "integrity": "sha512-JiDShH45zKHWyGe4ZNVRrCjBz8Nh9TMmZG1kh4QTK8hCBTWBi8Da+i7s1fJw7/lYpM4ccepSNfqzZ/QvABBi5g==", + "version": "7.28.6", "license": "MIT", "engines": { "node": ">=6.9.0" @@ -477,8 +615,6 @@ }, "node_modules/@babel/template": { "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.28.6.tgz", - "integrity": "sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==", "dev": true, "license": "MIT", "dependencies": { @@ -492,8 +628,6 @@ }, "node_modules/@babel/traverse": { "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.0.tgz", - "integrity": "sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA==", "dev": true, "license": "MIT", "dependencies": { @@ -511,8 +645,6 @@ }, "node_modules/@babel/types": { "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.0.tgz", - "integrity": "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==", "dev": true, "license": "MIT", "dependencies": { @@ -523,6 +655,14 @@ "node": ">=6.9.0" } }, + "node_modules/@bcoe/v8-coverage": { + "version": "1.0.2", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, "node_modules/@borewit/text-codec": { "version": "0.2.2", "resolved": "https://registry.npmjs.org/@borewit/text-codec/-/text-codec-0.2.2.tgz", @@ -537,14 +677,12 @@ }, "node_modules/@braintree/sanitize-url": { "version": "7.1.2", - "resolved": "https://registry.npmjs.org/@braintree/sanitize-url/-/sanitize-url-7.1.2.tgz", - "integrity": "sha512-jigsZK+sMF/cuiB7sERuo9V7N9jx+dhmHHnQyDSVdpZwVutaBu7WvNYqMDLSgFgfB30n452TP3vjDAvFC973mA==", "license": "MIT" }, "node_modules/@browserbasehq/sdk": { - "version": "2.9.0", - "resolved": "https://registry.npmjs.org/@browserbasehq/sdk/-/sdk-2.9.0.tgz", - "integrity": "sha512-Xzm1+6suzQypXjley4Phqer++pjnYyST6S7CArUn3kWyGA8aruXjAV5wkmqE21lgXo9K3/OQJvCu48bKEZFNDQ==", + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/@browserbasehq/sdk/-/sdk-2.10.0.tgz", + "integrity": "sha512-pOL4yW8P8AI2+N5y6zEP6XXKqIXtYyKunr1JXppqQDOyKLxxvZEDqQCHJXWUzqgx3R1tGWpn7m9AjXN7MeYInA==", "dev": true, "peer": true, "dependencies": { @@ -616,48 +754,44 @@ "license": "MIT" }, "node_modules/@chevrotain/cst-dts-gen": { - "version": "11.1.2", - "resolved": "https://registry.npmjs.org/@chevrotain/cst-dts-gen/-/cst-dts-gen-11.1.2.tgz", - "integrity": "sha512-XTsjvDVB5nDZBQB8o0o/0ozNelQtn2KrUVteIHSlPd2VAV2utEb6JzyCJaJ8tGxACR4RiBNWy5uYUHX2eji88Q==", + "version": "12.0.0", + "resolved": "https://registry.npmjs.org/@chevrotain/cst-dts-gen/-/cst-dts-gen-12.0.0.tgz", + "integrity": "sha512-fSL4KXjTl7cDgf0B5Rip9Q05BOrYvkJV/RrBTE/bKDN096E4hN/ySpcBK5B24T76dlQ2i32Zc3PAE27jFnFrKg==", "license": "Apache-2.0", "dependencies": { - "@chevrotain/gast": "11.1.2", - "@chevrotain/types": "11.1.2", - "lodash-es": "4.17.23" + "@chevrotain/gast": "12.0.0", + "@chevrotain/types": "12.0.0" } }, "node_modules/@chevrotain/gast": { - "version": "11.1.2", - "resolved": "https://registry.npmjs.org/@chevrotain/gast/-/gast-11.1.2.tgz", - "integrity": "sha512-Z9zfXR5jNZb1Hlsd/p+4XWeUFugrHirq36bKzPWDSIacV+GPSVXdk+ahVWZTwjhNwofAWg/sZg58fyucKSQx5g==", + "version": "12.0.0", + "resolved": "https://registry.npmjs.org/@chevrotain/gast/-/gast-12.0.0.tgz", + "integrity": "sha512-1ne/m3XsIT8aEdrvT33so0GUC+wkctpUPK6zU9IlOyJLUbR0rg4G7ZiApiJbggpgPir9ERy3FRjT6T7lpgetnQ==", "license": "Apache-2.0", "dependencies": { - "@chevrotain/types": "11.1.2", - "lodash-es": "4.17.23" + "@chevrotain/types": "12.0.0" } }, "node_modules/@chevrotain/regexp-to-ast": { - "version": "11.1.2", - "resolved": "https://registry.npmjs.org/@chevrotain/regexp-to-ast/-/regexp-to-ast-11.1.2.tgz", - "integrity": "sha512-nMU3Uj8naWer7xpZTYJdxbAs6RIv/dxYzkYU8GSwgUtcAAlzjcPfX1w+RKRcYG8POlzMeayOQ/znfwxEGo5ulw==", + "version": "12.0.0", + "resolved": "https://registry.npmjs.org/@chevrotain/regexp-to-ast/-/regexp-to-ast-12.0.0.tgz", + "integrity": "sha512-p+EW9MaJwgaHguhoqwOtx/FwuGr+DnNn857sXWOi/mClXIkPGl3rn7hGNWvo31HA3vyeQxjqe+H36yZJwYU8cA==", "license": "Apache-2.0" }, "node_modules/@chevrotain/types": { - "version": "11.1.2", - "resolved": "https://registry.npmjs.org/@chevrotain/types/-/types-11.1.2.tgz", - "integrity": "sha512-U+HFai5+zmJCkK86QsaJtoITlboZHBqrVketcO2ROv865xfCMSFpELQoz1GkX5GzME8pTa+3kbKrZHQtI0gdbw==", + "version": "12.0.0", + "resolved": "https://registry.npmjs.org/@chevrotain/types/-/types-12.0.0.tgz", + "integrity": "sha512-S+04vjFQKeuYw0/eW3U52LkAHQsB1ASxsPGsLPUyQgrZ2iNNibQrsidruDzjEX2JYfespXMG0eZmXlhA6z7nWA==", "license": "Apache-2.0" }, "node_modules/@chevrotain/utils": { - "version": "11.1.2", - "resolved": "https://registry.npmjs.org/@chevrotain/utils/-/utils-11.1.2.tgz", - "integrity": "sha512-4mudFAQ6H+MqBTfqLmU7G1ZwRzCLfJEooL/fsF6rCX5eePMbGhoy5n4g+G4vlh2muDcsCTJtL+uKbOzWxs5LHA==", + "version": "12.0.0", + "resolved": "https://registry.npmjs.org/@chevrotain/utils/-/utils-12.0.0.tgz", + "integrity": "sha512-lB59uJoaGIfOOL9knQqQRfhl9g7x8/wqFkp13zTdkRu1huG9kg6IJs1O8hqj9rs6h7orGxHJUKb+mX3rPbWGhA==", "license": "Apache-2.0" }, "node_modules/@chromatic-com/storybook": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/@chromatic-com/storybook/-/storybook-5.1.1.tgz", - "integrity": "sha512-BPoAXHM71XgeCK2u0jKr9i8apeQMm/Z9IWGyndA2FMijfQG9m8ox45DdWh/pxFkK5ClhGgirv5QwMhFIeHmThg==", + "version": "5.0.1", "dev": true, "license": "MIT", "dependencies": { @@ -672,13 +806,11 @@ "yarn": ">=1.22.18" }, "peerDependencies": { - "storybook": "^0.0.0-0 || ^10.1.0 || ^10.1.0-0 || ^10.2.0-0 || ^10.3.0-0 || ^10.4.0-0" + "storybook": "^0.0.0-0 || ^10.1.0 || ^10.1.0-0 || ^10.2.0-0 || ^10.3.0-0" } }, "node_modules/@clack/core": { "version": "0.5.0", - "resolved": "https://registry.npmjs.org/@clack/core/-/core-0.5.0.tgz", - "integrity": "sha512-p3y0FIOwaYRUPRcMO7+dlmLh8PSRcrjuTndsiA0WAFbWES0mLZlrjVoBRZ9DzkPFJZG6KGkJmoEAY0ZcVWTkow==", "dev": true, "license": "MIT", "dependencies": { @@ -688,8 +820,6 @@ }, "node_modules/@clack/prompts": { "version": "0.11.0", - "resolved": "https://registry.npmjs.org/@clack/prompts/-/prompts-0.11.0.tgz", - "integrity": "sha512-pMN5FcrEw9hUkZA4f+zLlzivQSeQf5dRGJjSUbvVYDLvpKCdQx5OaknvKzgbtXOizhP+SJJJjqEbOe55uKKfAw==", "dev": true, "license": "MIT", "dependencies": { @@ -724,563 +854,314 @@ "@jridgewell/sourcemap-codec": "^1.4.10" } }, - "node_modules/@emnapi/core": { - "version": "1.9.2", - "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.9.2.tgz", - "integrity": "sha512-UC+ZhH3XtczQYfOlu3lNEkdW/p4dsJ1r/bP7H8+rhao3TTTMO1ATq/4DdIi23XuGoFY+Cz0JmCbdVl0hz9jZcA==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "@emnapi/wasi-threads": "1.2.1", - "tslib": "^2.4.0" - } - }, - "node_modules/@emnapi/runtime": { - "version": "1.9.2", - "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.9.2.tgz", - "integrity": "sha512-3U4+MIWHImeyu1wnmVygh5WlgfYDtyf0k8AbLhMFxOipihf6nrWC4syIm/SwEeec0mNSafiiNnMJwbza/Is6Lw==", + "node_modules/@esbuild/darwin-arm64": { + "version": "0.27.3", + "cpu": [ + "arm64" + ], "dev": true, "license": "MIT", "optional": true, - "dependencies": { - "tslib": "^2.4.0" + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" } }, - "node_modules/@emnapi/wasi-threads": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.1.tgz", - "integrity": "sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==", + "node_modules/@eslint-community/eslint-utils": { + "version": "4.9.1", "dev": true, "license": "MIT", - "optional": true, "dependencies": { - "tslib": "^2.4.0" + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" } }, - "node_modules/@esbuild/aix-ppc64": { - "version": "0.27.5", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.5.tgz", - "integrity": "sha512-nGsF/4C7uzUj+Nj/4J+Zt0bYQ6bz33Phz8Lb2N80Mti1HjGclTJdXZ+9APC4kLvONbjxN1zfvYNd8FEcbBK/MQ==", - "cpu": [ - "ppc64" - ], + "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "3.4.3", "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "aix" - ], + "license": "Apache-2.0", "engines": { - "node": ">=18" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" } }, - "node_modules/@esbuild/android-arm": { - "version": "0.27.5", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.5.tgz", - "integrity": "sha512-Cv781jd0Rfj/paoNrul1/r4G0HLvuFKYh7C9uHZ2Pl8YXstzvCyyeWENTFR9qFnRzNMCjXmsulZuvosDg10Mog==", - "cpu": [ - "arm" - ], + "node_modules/@eslint-community/regexpp": { + "version": "4.12.2", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "android" - ], "engines": { - "node": ">=18" + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" } }, - "node_modules/@esbuild/android-arm64": { - "version": "0.27.5", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.5.tgz", - "integrity": "sha512-Oeghq+XFgh1pUGd1YKs4DDoxzxkoUkvko+T/IVKwlghKLvvjbGFB3ek8VEDBmNvqhwuL0CQS3cExdzpmUyIrgA==", - "cpu": [ - "arm64" - ], + "node_modules/@eslint/config-array": { + "version": "0.21.1", "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], + "license": "Apache-2.0", + "dependencies": { + "@eslint/object-schema": "^2.1.7", + "debug": "^4.3.1", + "minimatch": "^3.1.2" + }, "engines": { - "node": ">=18" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, - "node_modules/@esbuild/android-x64": { - "version": "0.27.5", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.5.tgz", - "integrity": "sha512-nQD7lspbzerlmtNOxYMFAGmhxgzn8Z7m9jgFkh6kpkjsAhZee1w8tJW3ZlW+N9iRePz0oPUDrYrXidCPSImD0Q==", - "cpu": [ - "x64" - ], + "node_modules/@eslint/config-helpers": { + "version": "0.4.2", "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^0.17.0" + }, "engines": { - "node": ">=18" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, - "node_modules/@esbuild/darwin-arm64": { - "version": "0.27.5", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.5.tgz", - "integrity": "sha512-I+Ya/MgC6rr8oRWGRDF3BXDfP8K1BVUggHqN6VI2lUZLdDi1IM1v2cy0e3lCPbP+pVcK3Tv8cgUhHse1kaNZZw==", - "cpu": [ - "arm64" - ], + "node_modules/@eslint/core": { + "version": "0.17.0", "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], + "license": "Apache-2.0", + "dependencies": { + "@types/json-schema": "^7.0.15" + }, "engines": { - "node": ">=18" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, - "node_modules/@esbuild/darwin-x64": { - "version": "0.27.5", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.5.tgz", - "integrity": "sha512-MCjQUtC8wWJn/pIPM7vQaO69BFgwPD1jriEdqwTCKzWjGgkMbcg+M5HzrOhPhuYe1AJjXlHmD142KQf+jnYj8A==", - "cpu": [ - "x64" - ], + "node_modules/@eslint/eslintrc": { + "version": "3.3.4", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], + "dependencies": { + "ajv": "^6.14.0", + "debug": "^4.3.2", + "espree": "^10.0.1", + "globals": "^14.0.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.1", + "minimatch": "^3.1.3", + "strip-json-comments": "^3.1.1" + }, "engines": { - "node": ">=18" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" } }, - "node_modules/@esbuild/freebsd-arm64": { - "version": "0.27.5", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.5.tgz", - "integrity": "sha512-X6xVS+goSH0UelYXnuf4GHLwpOdc8rgK/zai+dKzBMnncw7BTQIwquOodE7EKvY2UVUetSqyAfyZC1D+oqLQtg==", - "cpu": [ - "arm64" - ], + "node_modules/@eslint/eslintrc/node_modules/argparse": { + "version": "2.0.1", "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=18" - } + "license": "Python-2.0" }, - "node_modules/@esbuild/freebsd-x64": { - "version": "0.27.5", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.5.tgz", - "integrity": "sha512-233X1FGo3a8x1ekLB6XT69LfZ83vqz+9z3TSEQCTYfMNY880A97nr81KbPcAMl9rmOFp11wO0dP+eB18KU/Ucg==", - "cpu": [ - "x64" - ], + "node_modules/@eslint/eslintrc/node_modules/globals": { + "version": "14.0.0", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], "engines": { "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@esbuild/linux-arm": { - "version": "0.27.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.5.tgz", - "integrity": "sha512-0wkVrYHG4sdCCN/bcwQ7yYMXACkaHc3UFeaEOwSVW6e5RycMageYAFv+JS2bKLwHyeKVUvtoVH+5/RHq0fgeFw==", - "cpu": [ - "arm" - ], + "node_modules/@eslint/eslintrc/node_modules/import-fresh": { + "version": "3.3.1", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "linux" - ], + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, "engines": { - "node": ">=18" + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@esbuild/linux-arm64": { - "version": "0.27.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.5.tgz", - "integrity": "sha512-euKkilsNOv7x/M1NKsx5znyprbpsRFIzTV6lWziqJch7yWYayfLtZzDxDTl+LSQDJYAjd9TVb/Kt5UKIrj2e4A==", - "cpu": [ - "arm64" - ], + "node_modules/@eslint/eslintrc/node_modules/js-yaml": { + "version": "4.1.1", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" } }, - "node_modules/@esbuild/linux-ia32": { - "version": "0.27.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.5.tgz", - "integrity": "sha512-hVRQX4+P3MS36NxOy24v/Cdsimy/5HYePw+tmPqnNN1fxV0bPrFWR6TMqwXPwoTM2VzbkA+4lbHWUKDd5ZDA/w==", - "cpu": [ - "ia32" - ], + "node_modules/@eslint/eslintrc/node_modules/resolve-from": { + "version": "4.0.0", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "linux" - ], "engines": { - "node": ">=18" + "node": ">=4" } }, - "node_modules/@esbuild/linux-loong64": { - "version": "0.27.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.5.tgz", - "integrity": "sha512-mKqqRuOPALI8nDzhOBmIS0INvZOOFGGg5n1osGIXAx8oersceEbKd4t1ACNTHM3sJBXGFAlEgqM+svzjPot+ZQ==", - "cpu": [ - "loong64" - ], + "node_modules/@eslint/js": { + "version": "9.39.3", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "linux" - ], "engines": { - "node": ">=18" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" } }, - "node_modules/@esbuild/linux-mips64el": { - "version": "0.27.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.5.tgz", - "integrity": "sha512-EE/QXH9IyaAj1qeuIV5+/GZkBTipgGO782Ff7Um3vPS9cvLhJJeATy4Ggxikz2inZ46KByamMn6GqtqyVjhenA==", - "cpu": [ - "mips64el" - ], + "node_modules/@eslint/object-schema": { + "version": "2.1.7", "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], + "license": "Apache-2.0", "engines": { - "node": ">=18" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, - "node_modules/@esbuild/linux-ppc64": { - "version": "0.27.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.5.tgz", - "integrity": "sha512-0V2iF1RGxBf1b7/BjurA5jfkl7PtySjom1r6xOK2q9KWw/XCpAdtB6KNMO+9xx69yYfSCRR9FE0TyKfHA2eQMw==", - "cpu": [ - "ppc64" - ], + "node_modules/@eslint/plugin-kit": { + "version": "0.4.1", "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^0.17.0", + "levn": "^0.4.1" + }, "engines": { - "node": ">=18" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, - "node_modules/@esbuild/linux-riscv64": { - "version": "0.27.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.5.tgz", - "integrity": "sha512-rYxThBx6G9HN6tFNuvB/vykeLi4VDsm5hE5pVwzqbAjZEARQrWu3noZSfbEnPZ/CRXP3271GyFk/49up2W190g==", - "cpu": [ - "riscv64" - ], - "dev": true, + "node_modules/@floating-ui/core": { + "version": "1.7.5", + "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.7.5.tgz", + "integrity": "sha512-1Ih4WTWyw0+lKyFMcBHGbb5U5FtuHJuujoyyr5zTaWS5EYMeT6Jb2AuDeftsCsEuchO+mM2ij5+q9crhydzLhQ==", "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" + "dependencies": { + "@floating-ui/utils": "^0.2.11" } }, - "node_modules/@esbuild/linux-s390x": { - "version": "0.27.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.5.tgz", - "integrity": "sha512-uEP2q/4qgd8goEUc4QIdU/1P2NmEtZ/zX5u3OpLlCGhJIuBIv0s0wr7TB2nBrd3/A5XIdEkkS5ZLF0ULuvaaYQ==", - "cpu": [ - "s390x" - ], - "dev": true, + "node_modules/@floating-ui/dom": { + "version": "1.7.6", + "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.7.6.tgz", + "integrity": "sha512-9gZSAI5XM36880PPMm//9dfiEngYoC6Am2izES1FF406YFsjvyBMmeJ2g4SAju3xWwtuynNRFL2s9hgxpLI5SQ==", "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" + "dependencies": { + "@floating-ui/core": "^1.7.5", + "@floating-ui/utils": "^0.2.11" } }, - "node_modules/@esbuild/linux-x64": { - "version": "0.27.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.5.tgz", - "integrity": "sha512-+Gq47Wqq6PLOOZuBzVSII2//9yyHNKZLuwfzCemqexqOQCSz0zy0O26kIzyp9EMNMK+nZ0tFHBZrCeVUuMs/ew==", - "cpu": [ - "x64" - ], - "dev": true, + "node_modules/@floating-ui/react": { + "version": "0.27.19", + "resolved": "https://registry.npmjs.org/@floating-ui/react/-/react-0.27.19.tgz", + "integrity": "sha512-31B8h5mm8YxotlE7/AU/PhNAl8eWxAmjL/v2QOxroDNkTFLk3Uu82u63N3b6TXa4EGJeeZLVcd/9AlNlVqzeog==", "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" + "dependencies": { + "@floating-ui/react-dom": "^2.1.8", + "@floating-ui/utils": "^0.2.11", + "tabbable": "^6.0.0" + }, + "peerDependencies": { + "react": ">=17.0.0", + "react-dom": ">=17.0.0" } }, - "node_modules/@esbuild/netbsd-arm64": { - "version": "0.27.5", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.5.tgz", - "integrity": "sha512-3F/5EG8VHfN/I+W5cO1/SV2H9Q/5r7vcHabMnBqhHK2lTWOh3F8vixNzo8lqxrlmBtZVFpW8pmITHnq54+Tq4g==", - "cpu": [ - "arm64" - ], - "dev": true, + "node_modules/@floating-ui/react-dom": { + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.1.8.tgz", + "integrity": "sha512-cC52bHwM/n/CxS87FH0yWdngEZrjdtLW/qVruo68qg+prK7ZQ4YGdut2GyDVpoGeAYe/h899rVeOVm6Oi40k2A==", "license": "MIT", - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=18" + "dependencies": { + "@floating-ui/dom": "^1.7.6" + }, + "peerDependencies": { + "react": ">=16.8.0", + "react-dom": ">=16.8.0" } }, - "node_modules/@esbuild/netbsd-x64": { - "version": "0.27.5", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.5.tgz", - "integrity": "sha512-28t+Sj3CPN8vkMOlZotOmDgilQwVvxWZl7b8rxpn73Tt/gCnvrHxQUMng4uu3itdFvrtba/1nHejvxqz8xgEMA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/openbsd-arm64": { - "version": "0.27.5", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.5.tgz", - "integrity": "sha512-Doz/hKtiuVAi9hMsBMpwBANhIZc8l238U2Onko3t2xUp8xtM0ZKdDYHMnm/qPFVthY8KtxkXaocwmMh6VolzMA==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/openbsd-x64": { - "version": "0.27.5", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.5.tgz", - "integrity": "sha512-WfGVaa1oz5A7+ZFPkERIbIhKT4olvGl1tyzTRaB5yoZRLqC0KwaO95FeZtOdQj/oKkjW57KcVF944m62/0GYtA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/openharmony-arm64": { - "version": "0.27.5", - "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.5.tgz", - "integrity": "sha512-Xh+VRuh6OMh3uJ0JkCjI57l+DVe7VRGBYymen8rFPnTVgATBwA6nmToxM2OwTlSvrnWpPKkrQUj93+K9huYC6A==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openharmony" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/sunos-x64": { - "version": "0.27.5", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.5.tgz", - "integrity": "sha512-aC1gpJkkaUADHuAdQfuVTnqVUTLqqUNhAvEwHwVWcnVVZvNlDPGA0UveZsfXJJ9T6k9Po4eHi3c02gbdwO3g6w==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "sunos" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/win32-arm64": { - "version": "0.27.5", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.5.tgz", - "integrity": "sha512-0UNx2aavV0fk6UpZcwXFLztA2r/k9jTUa7OW7SAea1VYUhkug99MW1uZeXEnPn5+cHOd0n8myQay6TlFnBR07w==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/win32-ia32": { - "version": "0.27.5", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.5.tgz", - "integrity": "sha512-5nlJ3AeJWCTSzR7AEqVjT/faWyqKU86kCi1lLmxVqmNR+j4HrYdns+eTGjS/vmrzCIe8inGQckUadvS0+JkKdQ==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/win32-x64": { - "version": "0.27.5", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.5.tgz", - "integrity": "sha512-PWypQR+d4FLfkhBIV+/kHsUELAnMpx1bRvvsn3p+/sAERbnCzFrtDRG2Xw5n+2zPxBK2+iaP+vetsRl4Ti7WgA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } + "node_modules/@floating-ui/utils": { + "version": "0.2.11", + "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.11.tgz", + "integrity": "sha512-RiB/yIh78pcIxl6lLMG0CgBXAZ2Y0eVHqMPYugu+9U0AeT6YBeiJpf7lbdJNIugFP5SIjwNRgo4DhR1Qxi26Gg==", + "license": "MIT" }, - "node_modules/@eslint-community/eslint-utils": { - "version": "4.9.1", - "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz", - "integrity": "sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==", - "dev": true, + "node_modules/@hookform/resolvers": { + "version": "5.2.2", "license": "MIT", "dependencies": { - "eslint-visitor-keys": "^3.4.3" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" + "@standard-schema/utils": "^0.3.0" }, "peerDependencies": { - "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + "react-hook-form": "^7.55.0" } }, - "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", - "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "node_modules/@humanfs/core": { + "version": "0.19.1", "dev": true, "license": "Apache-2.0", "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" + "node": ">=18.18.0" } }, - "node_modules/@eslint-community/regexpp": { - "version": "4.12.2", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz", - "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==", + "node_modules/@humanfs/node": { + "version": "0.16.7", "dev": true, - "license": "MIT", + "license": "Apache-2.0", + "dependencies": { + "@humanfs/core": "^0.19.1", + "@humanwhocodes/retry": "^0.4.0" + }, "engines": { - "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + "node": ">=18.18.0" } }, - "node_modules/@eslint/config-array": { - "version": "0.21.2", - "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.2.tgz", - "integrity": "sha512-nJl2KGTlrf9GjLimgIru+V/mzgSK0ABCDQRvxw5BjURL7WfH5uoWmizbH7QB6MmnMBd8cIC9uceWnezL1VZWWw==", + "node_modules/@humanwhocodes/config-array": { + "version": "0.13.0", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.13.0.tgz", + "integrity": "sha512-DZLEEqFWQFiyK6h5YIeynKx7JlvCYWL0cImfSRXZ9l4Sg2efkFGTuFf6vzXjK1cq6IYkU+Eg/JizXw+TD2vRNw==", + "deprecated": "Use @eslint/config-array instead", "dev": true, "license": "Apache-2.0", + "peer": true, "dependencies": { - "@eslint/object-schema": "^2.1.7", + "@humanwhocodes/object-schema": "^2.0.3", "debug": "^4.3.1", - "minimatch": "^3.1.5" + "minimatch": "^3.0.5" }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": ">=10.10.0" } }, - "node_modules/@eslint/config-array/node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true, - "license": "MIT" - }, - "node_modules/@eslint/config-array/node_modules/brace-expansion": { + "node_modules/@humanwhocodes/config-array/node_modules/brace-expansion": { "version": "1.1.13", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.13.tgz", "integrity": "sha512-9ZLprWS6EENmhEOpjCYW2c8VkmOvckIJZfkr7rBW6dObmfgJ/L1GpSYW5Hpo9lDz4D1+n0Ckz8rU7FwHDQiG/w==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, - "node_modules/@eslint/config-array/node_modules/minimatch": { + "node_modules/@humanwhocodes/config-array/node_modules/minimatch": { "version": "3.1.5", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", "dev": true, "license": "ISC", + "peer": true, "dependencies": { "brace-expansion": "^1.1.7" }, @@ -1288,530 +1169,637 @@ "node": "*" } }, - "node_modules/@eslint/config-helpers": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.4.2.tgz", - "integrity": "sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==", + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", "dev": true, "license": "Apache-2.0", - "dependencies": { - "@eslint/core": "^0.17.0" + "engines": { + "node": ">=12.22" }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/object-schema": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz", + "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==", + "deprecated": "Use @eslint/object-schema instead", + "dev": true, + "license": "BSD-3-Clause", + "peer": true + }, + "node_modules/@humanwhocodes/retry": { + "version": "0.4.3", + "dev": true, + "license": "Apache-2.0", "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" } }, - "node_modules/@eslint/core": { - "version": "0.17.0", - "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.17.0.tgz", - "integrity": "sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==", + "node_modules/@ibm-cloud/watsonx-ai": { + "version": "1.7.11", + "resolved": "https://registry.npmjs.org/@ibm-cloud/watsonx-ai/-/watsonx-ai-1.7.11.tgz", + "integrity": "sha512-sBMj/YhV5qvJdBpvgutmX6vLUUnSwvhFaJk7r3hiMh5aB7BQWQ+5zyzvSPLywOwTWZbgy4v8jxTUhR6jb+kguw==", "dev": true, "license": "Apache-2.0", + "peer": true, "dependencies": { - "@types/json-schema": "^7.0.15" + "@types/node": "^18.0.0", + "extend": "3.0.2", + "form-data": "^4.0.4", + "ibm-cloud-sdk-core": "^5.4.9", + "ts-node": "^10.9.2" }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": ">=20.0.0" } }, - "node_modules/@eslint/eslintrc": { - "version": "3.3.5", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.5.tgz", - "integrity": "sha512-4IlJx0X0qftVsN5E+/vGujTRIFtwuLbNsVUe7TO6zYPDR1O6nFwvwhIKEKSrl6dZchmYBITazxKoUYOjdtjlRg==", + "node_modules/@ibm-cloud/watsonx-ai/node_modules/@types/node": { + "version": "18.19.130", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.130.tgz", + "integrity": "sha512-GRaXQx6jGfL8sKfaIDD6OupbIHBr9jv7Jnaml9tB7l4v068PAOXqfcujMMo5PhbIs6ggR1XODELqahT2R8v0fg==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { - "ajv": "^6.14.0", - "debug": "^4.3.2", - "espree": "^10.0.1", - "globals": "^14.0.0", - "ignore": "^5.2.0", - "import-fresh": "^3.2.1", - "js-yaml": "^4.1.1", - "minimatch": "^3.1.5", - "strip-json-comments": "^3.1.1" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" + "undici-types": "~5.26.4" } }, - "node_modules/@eslint/eslintrc/node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "node_modules/@ibm-cloud/watsonx-ai/node_modules/undici-types": { + "version": "5.26.5", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/@iconify/types": { + "version": "2.0.0", "license": "MIT" }, - "node_modules/@eslint/eslintrc/node_modules/brace-expansion": { - "version": "1.1.13", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.13.tgz", - "integrity": "sha512-9ZLprWS6EENmhEOpjCYW2c8VkmOvckIJZfkr7rBW6dObmfgJ/L1GpSYW5Hpo9lDz4D1+n0Ckz8rU7FwHDQiG/w==", - "dev": true, + "node_modules/@iconify/utils": { + "version": "3.1.0", "license": "MIT", "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" + "@antfu/install-pkg": "^1.1.0", + "@iconify/types": "^2.0.0", + "mlly": "^1.8.0" } }, - "node_modules/@eslint/eslintrc/node_modules/globals": { - "version": "14.0.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", - "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", + "node_modules/@isaacs/cliui": { + "version": "9.0.0", "dev": true, - "license": "MIT", + "license": "BlueOak-1.0.0", "engines": { "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@eslint/eslintrc/node_modules/import-fresh": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", - "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", + "node_modules/@joshwooding/vite-plugin-react-docgen-typescript": { + "version": "0.6.4", "dev": true, "license": "MIT", "dependencies": { - "parent-module": "^1.0.0", - "resolve-from": "^4.0.0" + "glob": "^13.0.1", + "react-docgen-typescript": "^2.2.2" }, - "engines": { - "node": ">=6" + "peerDependencies": { + "typescript": ">= 4.3.x", + "vite": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "peerDependenciesMeta": { + "typescript": { + "optional": true + } } }, - "node_modules/@eslint/eslintrc/node_modules/minimatch": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", - "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.13", "dev": true, - "license": "ISC", + "license": "MIT", "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" } }, - "node_modules/@eslint/eslintrc/node_modules/resolve-from": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "node_modules/@jridgewell/remapping": { + "version": "2.3.5", "dev": true, "license": "MIT", - "engines": { - "node": ">=4" + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" } }, - "node_modules/@eslint/js": { - "version": "9.39.4", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.4.tgz", - "integrity": "sha512-nE7DEIchvtiFTwBw4Lfbu59PG+kCofhjsKaCWzxTpt4lfRjRMqG6uMBzKXuEcyXhOHoUp9riAm7/aWYGhXZ9cw==", + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", "dev": true, "license": "MIT", "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://eslint.org/donate" - } - }, - "node_modules/@eslint/object-schema": { - "version": "2.1.7", - "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.7.tgz", - "integrity": "sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": ">=6.0.0" } }, - "node_modules/@eslint/plugin-kit": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.4.1.tgz", - "integrity": "sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==", + "node_modules/@jridgewell/source-map": { + "version": "0.3.11", "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@eslint/core": "^0.17.0", - "levn": "^0.4.1" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "node_modules/@floating-ui/core": { - "version": "1.7.5", - "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.7.5.tgz", - "integrity": "sha512-1Ih4WTWyw0+lKyFMcBHGbb5U5FtuHJuujoyyr5zTaWS5EYMeT6Jb2AuDeftsCsEuchO+mM2ij5+q9crhydzLhQ==", "license": "MIT", "dependencies": { - "@floating-ui/utils": "^0.2.11" + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25" } }, - "node_modules/@floating-ui/dom": { - "version": "1.7.6", - "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.7.6.tgz", - "integrity": "sha512-9gZSAI5XM36880PPMm//9dfiEngYoC6Am2izES1FF406YFsjvyBMmeJ2g4SAju3xWwtuynNRFL2s9hgxpLI5SQ==", - "license": "MIT", - "dependencies": { - "@floating-ui/core": "^1.7.5", - "@floating-ui/utils": "^0.2.11" - } + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "dev": true, + "license": "MIT" }, - "node_modules/@floating-ui/react": { - "version": "0.27.19", - "resolved": "https://registry.npmjs.org/@floating-ui/react/-/react-0.27.19.tgz", - "integrity": "sha512-31B8h5mm8YxotlE7/AU/PhNAl8eWxAmjL/v2QOxroDNkTFLk3Uu82u63N3b6TXa4EGJeeZLVcd/9AlNlVqzeog==", + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "dev": true, "license": "MIT", "dependencies": { - "@floating-ui/react-dom": "^2.1.8", - "@floating-ui/utils": "^0.2.11", - "tabbable": "^6.0.0" - }, - "peerDependencies": { - "react": ">=17.0.0", - "react-dom": ">=17.0.0" + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" } }, - "node_modules/@floating-ui/react-dom": { - "version": "2.1.8", - "resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.1.8.tgz", - "integrity": "sha512-cC52bHwM/n/CxS87FH0yWdngEZrjdtLW/qVruo68qg+prK7ZQ4YGdut2GyDVpoGeAYe/h899rVeOVm6Oi40k2A==", + "node_modules/@langchain/classic": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@langchain/classic/-/classic-1.0.5.tgz", + "integrity": "sha512-yMlcuQ80iG0SAEzgym95oLS+bJZJlmsFrMb+qkwg5mzHfL9DzAIFyvaMPiDnwKM0iv52u7iwD/aucLljZul9mQ==", + "dev": true, "license": "MIT", "dependencies": { - "@floating-ui/dom": "^1.7.6" + "@langchain/openai": "1.1.3", + "@langchain/textsplitters": "1.0.1", + "handlebars": "^4.7.8", + "js-yaml": "^4.1.1", + "jsonpointer": "^5.0.1", + "openapi-types": "^12.1.3", + "p-retry": "^7.0.0", + "uuid": "^10.0.0", + "yaml": "^2.2.1", + "zod": "^3.25.76 || ^4" + }, + "engines": { + "node": ">=20" + }, + "optionalDependencies": { + "langsmith": "^0.3.64" }, "peerDependencies": { - "react": ">=16.8.0", - "react-dom": ">=16.8.0" + "@langchain/core": "^1.0.0", + "cheerio": "*", + "peggy": "^3.0.2", + "typeorm": "*" + }, + "peerDependenciesMeta": { + "cheerio": { + "optional": true + }, + "peggy": { + "optional": true + }, + "typeorm": { + "optional": true + } } }, - "node_modules/@floating-ui/utils": { - "version": "0.2.11", - "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.11.tgz", - "integrity": "sha512-RiB/yIh78pcIxl6lLMG0CgBXAZ2Y0eVHqMPYugu+9U0AeT6YBeiJpf7lbdJNIugFP5SIjwNRgo4DhR1Qxi26Gg==", - "license": "MIT" + "node_modules/@langchain/classic/node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true, + "license": "Python-2.0" }, - "node_modules/@hookform/resolvers": { - "version": "5.2.2", - "resolved": "https://registry.npmjs.org/@hookform/resolvers/-/resolvers-5.2.2.tgz", - "integrity": "sha512-A/IxlMLShx3KjV/HeTcTfaMxdwy690+L/ZADoeaTltLx+CVuzkeVIPuybK3jrRfw7YZnmdKsVVHAlEPIAEUNlA==", + "node_modules/@langchain/classic/node_modules/js-yaml": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", + "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", + "dev": true, "license": "MIT", "dependencies": { - "@standard-schema/utils": "^0.3.0" + "argparse": "^2.0.1" }, - "peerDependencies": { - "react-hook-form": "^7.55.0" + "bin": { + "js-yaml": "bin/js-yaml.js" } }, - "node_modules/@humanfs/core": { - "version": "0.19.1", - "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", - "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==", + "node_modules/@langchain/classic/node_modules/uuid": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-10.0.0.tgz", + "integrity": "sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==", "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=18.18.0" + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" } }, - "node_modules/@humanfs/node": { - "version": "0.16.7", - "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.7.tgz", - "integrity": "sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==", + "node_modules/@langchain/classic/node_modules/yaml": { + "version": "2.8.3", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.3.tgz", + "integrity": "sha512-AvbaCLOO2Otw/lW5bmh9d/WEdcDFdQp2Z2ZUH3pX9U2ihyUY0nvLv7J6TrWowklRGPYbB/IuIMfYgxaCPg5Bpg==", "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@humanfs/core": "^0.19.1", - "@humanwhocodes/retry": "^0.4.0" + "license": "ISC", + "bin": { + "yaml": "bin.mjs" }, "engines": { - "node": ">=18.18.0" + "node": ">= 14.6" + }, + "funding": { + "url": "https://github.com/sponsors/eemeli" } }, - "node_modules/@humanwhocodes/config-array": { - "version": "0.13.0", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.13.0.tgz", - "integrity": "sha512-DZLEEqFWQFiyK6h5YIeynKx7JlvCYWL0cImfSRXZ9l4Sg2efkFGTuFf6vzXjK1cq6IYkU+Eg/JizXw+TD2vRNw==", - "deprecated": "Use @eslint/config-array instead", + "node_modules/@langchain/core": { + "version": "1.1.34", + "resolved": "https://registry.npmjs.org/@langchain/core/-/core-1.1.34.tgz", + "integrity": "sha512-IDlZES5Vexo5meLQRCGkAU7NM0tPGPfPP5wcUzBd7Ot+JoFBmSXutC4gGzvZod5AKRVn3I0Qy5k8vkTraY21jA==", "dev": true, - "license": "Apache-2.0", - "peer": true, + "license": "MIT", "dependencies": { - "@humanwhocodes/object-schema": "^2.0.3", - "debug": "^4.3.1", - "minimatch": "^3.0.5" + "@cfworker/json-schema": "^4.0.2", + "@standard-schema/spec": "^1.1.0", + "ansi-styles": "^5.0.0", + "camelcase": "6", + "decamelize": "1.2.0", + "js-tiktoken": "^1.0.12", + "langsmith": ">=0.5.0 <1.0.0", + "mustache": "^4.2.0", + "p-queue": "^6.6.2", + "uuid": "^11.1.0", + "zod": "^3.25.76 || ^4" }, "engines": { - "node": ">=10.10.0" + "node": ">=20" } }, - "node_modules/@humanwhocodes/config-array/node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "node_modules/@langchain/core/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", "dev": true, "license": "MIT", - "peer": true + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } }, - "node_modules/@humanwhocodes/config-array/node_modules/brace-expansion": { - "version": "1.1.13", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.13.tgz", - "integrity": "sha512-9ZLprWS6EENmhEOpjCYW2c8VkmOvckIJZfkr7rBW6dObmfgJ/L1GpSYW5Hpo9lDz4D1+n0Ckz8rU7FwHDQiG/w==", + "node_modules/@langchain/core/node_modules/chalk": { + "version": "5.6.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz", + "integrity": "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==", "dev": true, "license": "MIT", - "peer": true, - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/@humanwhocodes/config-array/node_modules/minimatch": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", - "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", - "dev": true, - "license": "ISC", - "peer": true, + "node_modules/@langchain/core/node_modules/langsmith": { + "version": "0.5.16", + "resolved": "https://registry.npmjs.org/langsmith/-/langsmith-0.5.16.tgz", + "integrity": "sha512-nSsSnTo3gjg1dnb48vb8i582zyjvtPbn+EpR6P1pNELb+4Hb4R3nt7LDy+Tl1ltw73vPGfJQtUWOl28irI1b5w==", + "dev": true, + "license": "MIT", "dependencies": { - "brace-expansion": "^1.1.7" + "chalk": "^5.6.2", + "console-table-printer": "^2.12.1", + "p-queue": "^6.6.2", + "semver": "^7.6.3", + "uuid": "^10.0.0" }, - "engines": { - "node": "*" - } - }, - "node_modules/@humanwhocodes/module-importer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", - "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=12.22" + "peerDependencies": { + "@opentelemetry/api": "*", + "@opentelemetry/exporter-trace-otlp-proto": "*", + "@opentelemetry/sdk-trace-base": "*", + "openai": "*", + "ws": ">=7" }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/nzakas" + "peerDependenciesMeta": { + "@opentelemetry/api": { + "optional": true + }, + "@opentelemetry/exporter-trace-otlp-proto": { + "optional": true + }, + "@opentelemetry/sdk-trace-base": { + "optional": true + }, + "openai": { + "optional": true + }, + "ws": { + "optional": true + } } }, - "node_modules/@humanwhocodes/object-schema": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz", - "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==", - "deprecated": "Use @eslint/object-schema instead", + "node_modules/@langchain/core/node_modules/langsmith/node_modules/uuid": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-10.0.0.tgz", + "integrity": "sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==", "dev": true, - "license": "BSD-3-Clause", - "peer": true + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } }, - "node_modules/@humanwhocodes/retry": { - "version": "0.4.3", - "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz", - "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==", + "node_modules/@langchain/langgraph": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@langchain/langgraph/-/langgraph-1.2.8.tgz", + "integrity": "sha512-kKkRpC5xFz1e6vPivE7lwRJa5oahLAMaVQvVGZdTa6uJIchIYJDIuM1n93FqGvg8aYVcgYU4FENtKKC5Eh1JYw==", "dev": true, - "license": "Apache-2.0", + "license": "MIT", + "dependencies": { + "@langchain/langgraph-checkpoint": "^1.0.1", + "@langchain/langgraph-sdk": "~1.8.8", + "@standard-schema/spec": "1.1.0", + "uuid": "^10.0.0" + }, "engines": { - "node": ">=18.18" + "node": ">=18" }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/nzakas" + "peerDependencies": { + "@langchain/core": "^1.1.16", + "zod": "^3.25.32 || ^4.2.0", + "zod-to-json-schema": "^3.x" + }, + "peerDependenciesMeta": { + "zod-to-json-schema": { + "optional": true + } } }, - "node_modules/@ibm-cloud/watsonx-ai": { - "version": "1.7.11", - "resolved": "https://registry.npmjs.org/@ibm-cloud/watsonx-ai/-/watsonx-ai-1.7.11.tgz", - "integrity": "sha512-sBMj/YhV5qvJdBpvgutmX6vLUUnSwvhFaJk7r3hiMh5aB7BQWQ+5zyzvSPLywOwTWZbgy4v8jxTUhR6jb+kguw==", + "node_modules/@langchain/langgraph-checkpoint": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@langchain/langgraph-checkpoint/-/langgraph-checkpoint-1.0.1.tgz", + "integrity": "sha512-HM0cJLRpIsSlWBQ/xuDC67l52SqZ62Bh2Y61DX+Xorqwoh5e1KxYvfCD7GnSTbWWhjBOutvnR0vPhu4orFkZfw==", "dev": true, - "license": "Apache-2.0", - "peer": true, + "license": "MIT", "dependencies": { - "@types/node": "^18.0.0", - "extend": "3.0.2", - "form-data": "^4.0.4", - "ibm-cloud-sdk-core": "^5.4.9", - "ts-node": "^10.9.2" + "uuid": "^10.0.0" }, "engines": { - "node": ">=20.0.0" + "node": ">=18" + }, + "peerDependencies": { + "@langchain/core": "^1.0.1" } }, - "node_modules/@ibm-cloud/watsonx-ai/node_modules/@types/node": { - "version": "18.19.130", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.130.tgz", - "integrity": "sha512-GRaXQx6jGfL8sKfaIDD6OupbIHBr9jv7Jnaml9tB7l4v068PAOXqfcujMMo5PhbIs6ggR1XODELqahT2R8v0fg==", + "node_modules/@langchain/langgraph-checkpoint/node_modules/uuid": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-10.0.0.tgz", + "integrity": "sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==", "dev": true, + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], "license": "MIT", - "peer": true, - "dependencies": { - "undici-types": "~5.26.4" + "bin": { + "uuid": "dist/bin/uuid" } }, - "node_modules/@ibm-cloud/watsonx-ai/node_modules/undici-types": { - "version": "5.26.5", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", - "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", + "node_modules/@langchain/langgraph-sdk": { + "version": "1.8.8", + "resolved": "https://registry.npmjs.org/@langchain/langgraph-sdk/-/langgraph-sdk-1.8.8.tgz", + "integrity": "sha512-4OoqFAvPloOTZ6oPxXbJngz4FLJO8QSXb+BQV3qvNTvmfu1LQA7cCEqSNLYX9MoC340PbnDkHNgUtjajwkDHRg==", "dev": true, "license": "MIT", - "peer": true + "dependencies": { + "@types/json-schema": "^7.0.15", + "p-queue": "^9.0.1", + "p-retry": "^7.1.1", + "uuid": "^13.0.0" + }, + "peerDependencies": { + "@langchain/core": "^1.1.16", + "react": "^18 || ^19", + "react-dom": "^18 || ^19", + "svelte": "^4.0.0 || ^5.0.0", + "vue": "^3.0.0" + }, + "peerDependenciesMeta": { + "@langchain/core": { + "optional": true + }, + "react": { + "optional": true + }, + "react-dom": { + "optional": true + }, + "svelte": { + "optional": true + }, + "vue": { + "optional": true + } + } }, - "node_modules/@iconify/types": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@iconify/types/-/types-2.0.0.tgz", - "integrity": "sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg==", + "node_modules/@langchain/langgraph-sdk/node_modules/eventemitter3": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.4.tgz", + "integrity": "sha512-mlsTRyGaPBjPedk6Bvw+aqbsXDtoAyAzm5MO7JgU+yVRyMQ5O8bD4Kcci7BS85f93veegeCPkL8R4GLClnjLFw==", + "dev": true, "license": "MIT" }, - "node_modules/@iconify/utils": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@iconify/utils/-/utils-3.1.0.tgz", - "integrity": "sha512-Zlzem1ZXhI1iHeeERabLNzBHdOa4VhQbqAcOQaMKuTuyZCpwKbC2R4Dd0Zo3g9EAc+Y4fiarO8HIHRAth7+skw==", + "node_modules/@langchain/langgraph-sdk/node_modules/p-queue": { + "version": "9.1.2", + "resolved": "https://registry.npmjs.org/p-queue/-/p-queue-9.1.2.tgz", + "integrity": "sha512-ktsDOALzTYTWWF1PbkNVg2rOt+HaOaMWJMUnt7T3qf5tvZ1L8dBW3tObzprBcXNMKkwj+yFSLqHso0x+UFcJXw==", + "dev": true, "license": "MIT", "dependencies": { - "@antfu/install-pkg": "^1.1.0", - "@iconify/types": "^2.0.0", - "mlly": "^1.8.0" + "eventemitter3": "^5.0.1", + "p-timeout": "^7.0.0" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@isaacs/cliui": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-9.0.0.tgz", - "integrity": "sha512-AokJm4tuBHillT+FpMtxQ60n8ObyXBatq7jD2/JA9dxbDDokKQm8KMht5ibGzLVU9IJDIKK4TPKgMHEYMn3lMg==", + "node_modules/@langchain/langgraph-sdk/node_modules/p-timeout": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-7.0.1.tgz", + "integrity": "sha512-AxTM2wDGORHGEkPCt8yqxOTMgpfbEHqF51f/5fJCmwFC3C/zNcGT63SymH2ttOAaiIws2zVg4+izQCjrakcwHg==", "dev": true, - "license": "BlueOak-1.0.0", + "license": "MIT", "engines": { - "node": ">=18" + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@joshwooding/vite-plugin-react-docgen-typescript": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/@joshwooding/vite-plugin-react-docgen-typescript/-/vite-plugin-react-docgen-typescript-0.7.0.tgz", - "integrity": "sha512-qvsTEwEFefhdirGOPnu9Wp6ChfIwy2dBCRuETU3uE+4cC+PFoxMSiiEhxk4lOluA34eARHA0OxqsEUYDqRMgeQ==", + "node_modules/@langchain/langgraph-sdk/node_modules/uuid": { + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-13.0.0.tgz", + "integrity": "sha512-XQegIaBTVUjSHliKqcnFqYypAd4S+WCYt5NIeRs6w/UAry7z8Y9j5ZwRRL4kzq9U3sD6v+85er9FvkEaBpji2w==", + "dev": true, + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", + "bin": { + "uuid": "dist-node/bin/uuid" + } + }, + "node_modules/@langchain/langgraph/node_modules/uuid": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-10.0.0.tgz", + "integrity": "sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==", + "dev": true, + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/@langchain/openai": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@langchain/openai/-/openai-1.1.3.tgz", + "integrity": "sha512-p+xR+4HRms5Ozjf5miC6U2AYRyNVSTdO7AMBkMYs1Tp6DWHBd+mQ72H8Ogd2dKrPuS5UDJ5dbpI1fS+OrTbgQQ==", "dev": true, "license": "MIT", "dependencies": { - "glob": "^13.0.1", - "react-docgen-typescript": "^2.2.2" + "js-tiktoken": "^1.0.12", + "openai": "^6.9.0", + "zod": "^3.25.76 || ^4" + }, + "engines": { + "node": ">=20" }, "peerDependencies": { - "typescript": ">= 4.3.x", - "vite": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0" + "@langchain/core": "^1.0.0" + } + }, + "node_modules/@langchain/openai/node_modules/openai": { + "version": "6.33.0", + "resolved": "https://registry.npmjs.org/openai/-/openai-6.33.0.tgz", + "integrity": "sha512-xAYN1W3YsDXJWA5F277135YfkEk6H7D3D6vWwRhJ3OEkzRgcyK8z/P5P9Gyi/wB4N8kK9kM5ZjprfvyHagKmpw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "openai": "bin/cli" + }, + "peerDependencies": { + "ws": "^8.18.0", + "zod": "^3.25 || ^4.0" }, "peerDependenciesMeta": { - "typescript": { + "ws": { + "optional": true + }, + "zod": { "optional": true } } }, - "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.13", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", - "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "node_modules/@langchain/textsplitters": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@langchain/textsplitters/-/textsplitters-1.0.1.tgz", + "integrity": "sha512-rheJlB01iVtrOUzttscutRgLybPH9qR79EyzBEbf1u97ljWyuxQfCwIWK+SjoQTM9O8M7GGLLRBSYE26Jmcoww==", "dev": true, "license": "MIT", "dependencies": { - "@jridgewell/sourcemap-codec": "^1.5.0", - "@jridgewell/trace-mapping": "^0.3.24" + "js-tiktoken": "^1.0.12" + }, + "engines": { + "node": ">=20" + }, + "peerDependencies": { + "@langchain/core": "^1.0.0" } - }, - "node_modules/@jridgewell/remapping": { - "version": "2.3.5", - "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", - "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", - "dev": true, + }, + "node_modules/@mermaid-js/parser": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@mermaid-js/parser/-/parser-1.1.0.tgz", + "integrity": "sha512-gxK9ZX2+Fex5zu8LhRQoMeMPEHbc73UKZ0FQ54YrQtUxE1VVhMwzeNtKRPAu5aXks4FasbMe4xB4bWrmq6Jlxw==", "license": "MIT", "dependencies": { - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.24" + "langium": "^4.0.0" } }, - "node_modules/@jridgewell/resolve-uri": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", - "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", - "dev": true, + "node_modules/@n8n_io/riot-tmpl": { + "version": "4.0.1", "license": "MIT", - "engines": { - "node": ">=6.0.0" + "peer": true, + "dependencies": { + "eslint-config-riot": "^1.0.0" } }, - "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.5.5", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", - "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", - "dev": true, - "license": "MIT" - }, - "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.31", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", - "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "node_modules/@n8n/ai-node-sdk": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/@n8n/ai-node-sdk/-/ai-node-sdk-0.5.1.tgz", + "integrity": "sha512-7YH6SVVP3OsCz7dcha3uULJ+2qSXdBvBP6a6HM2bVTRnL2D1wwz/tti2uuR+DW3luKpeqmE3p9w4mf6mYH0AZA==", "dev": true, - "license": "MIT", + "license": "SEE LICENSE IN LICENSE.md", "dependencies": { - "@jridgewell/resolve-uri": "^3.1.0", - "@jridgewell/sourcemap-codec": "^1.4.14" + "@n8n/ai-utilities": "0.8.1" } }, - "node_modules/@langchain/classic": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/@langchain/classic/-/classic-1.0.5.tgz", - "integrity": "sha512-yMlcuQ80iG0SAEzgym95oLS+bJZJlmsFrMb+qkwg5mzHfL9DzAIFyvaMPiDnwKM0iv52u7iwD/aucLljZul9mQ==", + "node_modules/@n8n/ai-utilities": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@n8n/ai-utilities/-/ai-utilities-0.8.1.tgz", + "integrity": "sha512-2fGq0zh8CaC6ZmzlNKO7jfUHrepALBcntHMlMikN8nz2ablF/4NqetXrtPrx26hq2FzePIrMM+NBFOTCZlqKoA==", "dev": true, - "license": "MIT", + "license": "SEE LICENSE IN LICENSE.md", "dependencies": { + "@langchain/classic": "1.0.5", + "@langchain/community": "1.1.14", + "@langchain/core": "1.1.34", "@langchain/openai": "1.1.3", "@langchain/textsplitters": "1.0.1", - "handlebars": "^4.7.8", - "js-yaml": "^4.1.1", - "jsonpointer": "^5.0.1", - "openapi-types": "^12.1.3", - "p-retry": "^7.0.0", - "uuid": "^10.0.0", - "yaml": "^2.2.1", - "zod": "^3.25.76 || ^4" - }, - "engines": { - "node": ">=20" - }, - "optionalDependencies": { - "langsmith": "^0.3.64" + "@n8n/config": "2.13.1", + "@n8n/typescript-config": "1.3.0", + "https-proxy-agent": "7.0.6", + "js-tiktoken": "1.0.12", + "langchain": "1.2.30", + "proxy-from-env": "^1.1.0", + "tmp-promise": "3.0.3", + "undici": "^6.21.0", + "zod": "3.25.67", + "zod-to-json-schema": "3.23.3" }, "peerDependencies": { - "@langchain/core": "^1.0.0", - "cheerio": "*", - "peggy": "^3.0.2", - "typeorm": "*" - }, - "peerDependenciesMeta": { - "cheerio": { - "optional": true - }, - "peggy": { - "optional": true - }, - "typeorm": { - "optional": true - } + "n8n-workflow": "*" } }, - "node_modules/@langchain/community": { - "version": "1.1.27", - "resolved": "https://registry.npmjs.org/@langchain/community/-/community-1.1.27.tgz", - "integrity": "sha512-s2U3w7QV7QpkFtY1eZMni4poz+nKLFclpDi3a7hUbZ67ttsGaU9WkZ2BiLuzLIs+IFaUvON/KcGkE8EqAl9aPA==", + "node_modules/@n8n/ai-utilities/node_modules/@langchain/community": { + "version": "1.1.14", + "resolved": "https://registry.npmjs.org/@langchain/community/-/community-1.1.14.tgz", + "integrity": "sha512-Jb64jqkjwocfK04RYW8oP9Z0VO3E1LfSlOx5QK3g7LJI7zQN8o1QzVvfXWigKQRuvMYue+X7DkunLaiEx/mohA==", "dev": true, "license": "MIT", "dependencies": { - "@langchain/classic": "1.0.27", - "@langchain/openai": "1.4.1", + "@langchain/classic": "1.0.17", + "@langchain/openai": "1.2.7", "binary-extensions": "^2.2.0", "flat": "^5.0.2", "js-yaml": "^4.1.1", - "langsmith": ">=0.4.0 <1.0.0", "math-expression-evaluator": "^2.0.0", "uuid": "^10.0.0", "zod": "^3.25.76 || ^4" @@ -1820,16 +1808,16 @@ "node": ">=20" }, "peerDependencies": { - "@arcjet/redact": "^v1.2.0", + "@arcjet/redact": "^v1.1.0", "@aws-crypto/sha256-js": "^5.0.0", - "@aws-sdk/client-dynamodb": "^3.1001.0", - "@aws-sdk/client-lambda": "^3.1001.0", - "@aws-sdk/client-s3": "^3.1001.0", - "@aws-sdk/client-sagemaker-runtime": "^3.1001.0", - "@aws-sdk/client-sfn": "^3.1001.0", + "@aws-sdk/client-dynamodb": "^3.985.0", + "@aws-sdk/client-lambda": "^3.985.0", + "@aws-sdk/client-s3": "^3.985.0", + "@aws-sdk/client-sagemaker-runtime": "^3.985.0", + "@aws-sdk/client-sfn": "^3.985.0", "@aws-sdk/credential-provider-node": "^3.388.0", "@azure/search-documents": "^12.2.0", - "@azure/storage-blob": "^12.31.0", + "@azure/storage-blob": "^12.30.0", "@browserbasehq/sdk": "*", "@browserbasehq/stagehand": "^1.0.0", "@clickhouse/client": "^0.2.5", @@ -1837,32 +1825,32 @@ "@elastic/elasticsearch": "^8.4.0", "@getmetal/metal-sdk": "*", "@getzep/zep-cloud": "^1.0.6", - "@getzep/zep-js": "^2.0.2", + "@getzep/zep-js": "^0.9.0", "@gomomento/sdk-core": "^1.117.2", "@google-cloud/storage": "^6.10.1 || ^7.7.0", "@gradientai/nodejs-sdk": "^1.2.0", - "@huggingface/inference": "^4.13.14", + "@huggingface/inference": "^4.13.11", "@huggingface/transformers": "^3.8.1", "@ibm-cloud/watsonx-ai": "*", "@lancedb/lancedb": "^0.19.1", - "@langchain/core": "^1.1.38", + "@langchain/core": "^1.1.21", "@layerup/layerup-security": "^1.5.12", "@libsql/client": "^0.17.0", - "@mendable/firecrawl-js": "^4.15.2", + "@mendable/firecrawl-js": "^1.4.3", "@mlc-ai/web-llm": "*", "@mozilla/readability": "*", "@neondatabase/serverless": "*", - "@notionhq/client": "^5.11.1", + "@notionhq/client": "^2.2.10", "@opensearch-project/opensearch": "*", "@planetscale/database": "^1.8.0", "@premai/prem-sdk": "^0.3.25", "@raycast/api": "^1.55.2", "@rockset/client": "^0.9.1", - "@smithy/eventstream-codec": "^4.2.10", - "@smithy/protocol-http": "^5.3.10", - "@smithy/signature-v4": "^5.3.10", - "@smithy/util-utf8": "^4.2.2", - "@spider-cloud/spider-client": "^0.2.0", + "@smithy/eventstream-codec": "^2.0.5", + "@smithy/protocol-http": "^3.0.6", + "@smithy/signature-v4": "^2.0.10", + "@smithy/util-utf8": "^2.0.0", + "@spider-cloud/spider-client": "^0.1.85", "@supabase/supabase-js": "^2.45.0", "@tensorflow-models/universal-sentence-encoder": "*", "@tensorflow/tfjs-core": "*", @@ -1871,24 +1859,24 @@ "@upstash/vector": "^1.1.1", "@vercel/kv": "*", "@vercel/postgres": "*", - "@writerai/writer-sdk": "^3.6.0", - "@xata.io/client": "^0.30.1", + "@writerai/writer-sdk": "^0.40.2", + "@xata.io/client": "^0.28.0", "@zilliz/milvus2-sdk-node": ">=2.3.5", - "apify-client": "^2.22.2", - "assemblyai": "^4.25.1", - "azion": "^3.1.2", - "better-sqlite3": ">=9.4.0 <13.0.0", + "apify-client": "^2.22.0", + "assemblyai": "^4.23.0", + "azion": "^3.0.0", + "better-sqlite3": ">=9.4.0 <12.0.0", "cassandra-driver": "^4.7.2", "cborg": "^4.5.8", - "cheerio": "^1.2.0", + "cheerio": "^1.0.0-rc.12", "chromadb": "*", "closevector-common": "0.1.3", "closevector-node": "0.1.6", "closevector-web": "0.1.6", - "convex": "^1.32.0", - "couchbase": "^4.6.1", + "convex": "^1.3.1", + "couchbase": "^4.6.0", "crypto-js": "^4.2.0", - "d3-dsv": "^3.0.1", + "d3-dsv": "^2.0.0", "discord.js": "^14.25.1", "duck-duck-scrape": "^2.2.5", "epub2": "^3.0.1", @@ -1900,8 +1888,8 @@ "hnswlib-node": "^3.0.0", "html-to-text": "^9.0.5", "ibm-cloud-sdk-core": "*", - "ignore": "^7.0.5", - "interface-datastore": "^9.0.2", + "ignore": "^5.2.0", + "interface-datastore": "^8.2.11", "ioredis": "^5.3.2", "it-all": "^3.0.4", "jsdom": "*", @@ -1909,20 +1897,20 @@ "lodash": "^4.17.23", "lunary": "^0.7.10", "mammoth": "^1.11.0", - "mariadb": "^3.5.1", - "mem0ai": "^2.2.4", - "mysql2": "^3.19.1", + "mariadb": "^3.4.0", + "mem0ai": "^2.1.8", + "mysql2": "^3.16.3", "neo4j-driver": "*", "node-llama-cpp": ">=3.0.0", "notion-to-md": "^3.1.0", - "officeparser": "^6.0.4", + "officeparser": "^4.0.4", "openai": "*", - "pdf-parse": "2.4.5", + "pdf-parse": "1.1.1", "pg": "^8.11.0", - "pg-copy-streams": "^7.0.0", + "pg-copy-streams": "^6.0.5", "pickleparser": "^0.2.1", "playwright": "^1.58.2", - "portkey-ai": "^3.0.3", + "portkey-ai": "^0.1.11", "puppeteer": "*", "pyodide": ">=0.24.1 <0.27.0", "replicate": "*", @@ -1931,7 +1919,7 @@ "typeorm": "^0.3.28", "typesense": "^3.0.1", "usearch": "^1.1.1", - "voy-search": "0.6.3", + "voy-search": "0.6.2", "word-extractor": "*", "ws": "^8.14.2", "youtubei.js": "*" @@ -2245,352 +2233,105 @@ }, "playwright": { "optional": true - }, - "portkey-ai": { - "optional": true - }, - "puppeteer": { - "optional": true - }, - "pyodide": { - "optional": true - }, - "redis": { - "optional": true - }, - "replicate": { - "optional": true - }, - "sonix-speech-recognition": { - "optional": true - }, - "srt-parser-2": { - "optional": true - }, - "typeorm": { - "optional": true - }, - "typesense": { - "optional": true - }, - "usearch": { - "optional": true - }, - "voy-search": { - "optional": true - }, - "weaviate-client": { - "optional": true - }, - "word-extractor": { - "optional": true - }, - "ws": { - "optional": true - }, - "youtubei.js": { - "optional": true - } - } - }, - "node_modules/@langchain/community/node_modules/@langchain/classic": { - "version": "1.0.27", - "resolved": "https://registry.npmjs.org/@langchain/classic/-/classic-1.0.27.tgz", - "integrity": "sha512-dMq8rgt3cy/QoHrB6HIKobievyW00DfuZcASG0aMy47Pf5TQq3vZM2tLT1kkCBe2yJnncQwqmAzxoEgFLJZrcw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@langchain/openai": "1.4.1", - "@langchain/textsplitters": "1.0.1", - "handlebars": "^4.7.9", - "js-yaml": "^4.1.1", - "jsonpointer": "^5.0.1", - "openapi-types": "^12.1.3", - "uuid": "^10.0.0", - "yaml": "^2.8.3", - "zod": "^3.25.76 || ^4" - }, - "engines": { - "node": ">=20" - }, - "optionalDependencies": { - "langsmith": ">=0.4.0 <1.0.0" - }, - "peerDependencies": { - "@langchain/core": "^1.0.0", - "cheerio": "*", - "peggy": "^5.1.0", - "typeorm": "*" - }, - "peerDependenciesMeta": { - "cheerio": { - "optional": true - }, - "peggy": { - "optional": true - }, - "typeorm": { - "optional": true - } - } - }, - "node_modules/@langchain/community/node_modules/@langchain/openai": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/@langchain/openai/-/openai-1.4.1.tgz", - "integrity": "sha512-jaHk4TnLqWrQ1KYmavvwCImW6x8pBy6LLTK73tzSMg7HBLbq0g/l7EkpMcxZWDOvyufuCXUqO2bj47apcOhw6Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "js-tiktoken": "^1.0.12", - "openai": "^6.32.0", - "zod": "^3.25.76 || ^4" - }, - "engines": { - "node": ">=20" - }, - "peerDependencies": { - "@langchain/core": "^1.1.38" - } - }, - "node_modules/@langchain/community/node_modules/@langchain/openai/node_modules/openai": { - "version": "6.33.0", - "resolved": "https://registry.npmjs.org/openai/-/openai-6.33.0.tgz", - "integrity": "sha512-xAYN1W3YsDXJWA5F277135YfkEk6H7D3D6vWwRhJ3OEkzRgcyK8z/P5P9Gyi/wB4N8kK9kM5ZjprfvyHagKmpw==", - "dev": true, - "license": "Apache-2.0", - "bin": { - "openai": "bin/cli" - }, - "peerDependencies": { - "ws": "^8.18.0", - "zod": "^3.25 || ^4.0" - }, - "peerDependenciesMeta": { - "ws": { - "optional": true - }, - "zod": { - "optional": true - } - } - }, - "node_modules/@langchain/core": { - "version": "1.1.38", - "resolved": "https://registry.npmjs.org/@langchain/core/-/core-1.1.38.tgz", - "integrity": "sha512-C340wH1YL10CiVOFlEpQMp0zQE85/eBLKX/gi1Lv7shAyUmR3CQ0t/mXlCd5RsZa6ntAN1kDJnp64ArWey9XAA==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "@cfworker/json-schema": "^4.0.2", - "@standard-schema/spec": "^1.1.0", - "ansi-styles": "^5.0.0", - "camelcase": "6", - "decamelize": "1.2.0", - "js-tiktoken": "^1.0.12", - "langsmith": ">=0.5.0 <1.0.0", - "mustache": "^4.2.0", - "p-queue": "^6.6.2", - "uuid": "^11.1.0", - "zod": "^3.25.76 || ^4" - }, - "engines": { - "node": ">=20" - } - }, - "node_modules/@langchain/core/node_modules/uuid": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-11.1.0.tgz", - "integrity": "sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A==", - "dev": true, - "funding": [ - "https://github.com/sponsors/broofa", - "https://github.com/sponsors/ctavan" - ], - "license": "MIT", - "peer": true, - "bin": { - "uuid": "dist/esm/bin/uuid" - } - }, - "node_modules/@langchain/langgraph": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/@langchain/langgraph/-/langgraph-1.2.6.tgz", - "integrity": "sha512-5cX402dNGN6w9+0mlMU2dgUiKx6Y1tlENp7x05e9ByDbQCHSDc0kyqRWNFLGc7vatQ92S4ylxQrcCJvi8Fr4SQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@langchain/langgraph-checkpoint": "^1.0.1", - "@langchain/langgraph-sdk": "~1.8.1", - "@standard-schema/spec": "1.1.0", - "uuid": "^10.0.0" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@langchain/core": "^1.1.16", - "zod": "^3.25.32 || ^4.2.0", - "zod-to-json-schema": "^3.x" - }, - "peerDependenciesMeta": { - "zod-to-json-schema": { + }, + "portkey-ai": { "optional": true - } - } - }, - "node_modules/@langchain/langgraph-checkpoint": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@langchain/langgraph-checkpoint/-/langgraph-checkpoint-1.0.1.tgz", - "integrity": "sha512-HM0cJLRpIsSlWBQ/xuDC67l52SqZ62Bh2Y61DX+Xorqwoh5e1KxYvfCD7GnSTbWWhjBOutvnR0vPhu4orFkZfw==", - "dev": true, - "license": "MIT", - "dependencies": { - "uuid": "^10.0.0" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@langchain/core": "^1.0.1" - } - }, - "node_modules/@langchain/langgraph-sdk": { - "version": "1.8.4", - "resolved": "https://registry.npmjs.org/@langchain/langgraph-sdk/-/langgraph-sdk-1.8.4.tgz", - "integrity": "sha512-dvkJihuYXHBdPEvLsXZAWNBPLQr5WTUcEsQK7K9xqq4nwwHGlmQKFrgL1BSSRCTRyuw+6UzVRI7vGb11VWViJQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/json-schema": "^7.0.15", - "p-queue": "^9.0.1", - "p-retry": "^7.1.1", - "uuid": "^13.0.0" - }, - "peerDependencies": { - "@langchain/core": "^1.1.16", - "react": "^18 || ^19", - "react-dom": "^18 || ^19", - "svelte": "^4.0.0 || ^5.0.0", - "vue": "^3.0.0" - }, - "peerDependenciesMeta": { - "@langchain/core": { + }, + "puppeteer": { "optional": true }, - "react": { + "pyodide": { "optional": true }, - "react-dom": { + "redis": { "optional": true }, - "svelte": { + "replicate": { "optional": true }, - "vue": { + "sonix-speech-recognition": { + "optional": true + }, + "srt-parser-2": { + "optional": true + }, + "typeorm": { + "optional": true + }, + "typesense": { + "optional": true + }, + "usearch": { + "optional": true + }, + "voy-search": { + "optional": true + }, + "weaviate-client": { + "optional": true + }, + "word-extractor": { + "optional": true + }, + "ws": { + "optional": true + }, + "youtubei.js": { "optional": true } } }, - "node_modules/@langchain/langgraph-sdk/node_modules/eventemitter3": { - "version": "5.0.4", - "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.4.tgz", - "integrity": "sha512-mlsTRyGaPBjPedk6Bvw+aqbsXDtoAyAzm5MO7JgU+yVRyMQ5O8bD4Kcci7BS85f93veegeCPkL8R4GLClnjLFw==", - "dev": true, - "license": "MIT" - }, - "node_modules/@langchain/langgraph-sdk/node_modules/p-queue": { - "version": "9.1.1", - "resolved": "https://registry.npmjs.org/p-queue/-/p-queue-9.1.1.tgz", - "integrity": "sha512-yQS1vV2V7Q14MQrgD8jMNY5owPuGgVHVdSK8NqmKpOVajnjbaeMa6uLOzTALPtvJ7Vo4bw0BGsw7qfUT8z24Ig==", - "dev": true, - "license": "MIT", - "dependencies": { - "eventemitter3": "^5.0.1", - "p-timeout": "^7.0.0" - }, - "engines": { - "node": ">=20" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@langchain/langgraph-sdk/node_modules/p-timeout": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-7.0.1.tgz", - "integrity": "sha512-AxTM2wDGORHGEkPCt8yqxOTMgpfbEHqF51f/5fJCmwFC3C/zNcGT63SymH2ttOAaiIws2zVg4+izQCjrakcwHg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=20" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@langchain/langgraph-sdk/node_modules/uuid": { - "version": "13.0.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-13.0.0.tgz", - "integrity": "sha512-XQegIaBTVUjSHliKqcnFqYypAd4S+WCYt5NIeRs6w/UAry7z8Y9j5ZwRRL4kzq9U3sD6v+85er9FvkEaBpji2w==", - "dev": true, - "funding": [ - "https://github.com/sponsors/broofa", - "https://github.com/sponsors/ctavan" - ], - "license": "MIT", - "bin": { - "uuid": "dist-node/bin/uuid" - } - }, - "node_modules/@langchain/openai": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@langchain/openai/-/openai-1.1.3.tgz", - "integrity": "sha512-p+xR+4HRms5Ozjf5miC6U2AYRyNVSTdO7AMBkMYs1Tp6DWHBd+mQ72H8Ogd2dKrPuS5UDJ5dbpI1fS+OrTbgQQ==", + "node_modules/@n8n/ai-utilities/node_modules/@langchain/community/node_modules/@langchain/classic": { + "version": "1.0.17", + "resolved": "https://registry.npmjs.org/@langchain/classic/-/classic-1.0.17.tgz", + "integrity": "sha512-GgcmDILxl26E0Oo09Q/fotJB3EZrTnU4MuJGR2zQXPMZnZ1CCQqyecXjKDRdI6sZkfc8Kxg+ezT+0kIMtKV10A==", "dev": true, "license": "MIT", "dependencies": { - "js-tiktoken": "^1.0.12", - "openai": "^6.9.0", + "@langchain/openai": "1.2.7", + "@langchain/textsplitters": "1.0.1", + "handlebars": "^4.7.8", + "js-yaml": "^4.1.1", + "jsonpointer": "^5.0.1", + "openapi-types": "^12.1.3", + "uuid": "^10.0.0", + "yaml": "^2.2.1", "zod": "^3.25.76 || ^4" }, "engines": { "node": ">=20" }, - "peerDependencies": { - "@langchain/core": "^1.0.0" - } - }, - "node_modules/@langchain/openai/node_modules/openai": { - "version": "6.33.0", - "resolved": "https://registry.npmjs.org/openai/-/openai-6.33.0.tgz", - "integrity": "sha512-xAYN1W3YsDXJWA5F277135YfkEk6H7D3D6vWwRhJ3OEkzRgcyK8z/P5P9Gyi/wB4N8kK9kM5ZjprfvyHagKmpw==", - "dev": true, - "license": "Apache-2.0", - "bin": { - "openai": "bin/cli" + "optionalDependencies": { + "langsmith": ">=0.4.0 <1.0.0" }, "peerDependencies": { - "ws": "^8.18.0", - "zod": "^3.25 || ^4.0" + "@langchain/core": "^1.0.0", + "cheerio": "*", + "peggy": "^3.0.2", + "typeorm": "*" }, "peerDependenciesMeta": { - "ws": { + "cheerio": { "optional": true }, - "zod": { + "peggy": { + "optional": true + }, + "typeorm": { "optional": true } } }, - "node_modules/@langchain/textsplitters": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@langchain/textsplitters/-/textsplitters-1.0.1.tgz", - "integrity": "sha512-rheJlB01iVtrOUzttscutRgLybPH9qR79EyzBEbf1u97ljWyuxQfCwIWK+SjoQTM9O8M7GGLLRBSYE26Jmcoww==", + "node_modules/@n8n/ai-utilities/node_modules/@langchain/community/node_modules/@langchain/openai": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/@langchain/openai/-/openai-1.2.7.tgz", + "integrity": "sha512-vR9zoF0/EZ03X0Tc6woIEWRDSDSr2l64n+MQCW8NduScJtBJs5r/Ng3Lrp2bjtJQywEMQoOhcrV2DMmAIPWgnw==", "dev": true, "license": "MIT", "dependencies": { - "js-tiktoken": "^1.0.12" + "js-tiktoken": "^1.0.12", + "openai": "^6.18.0", + "zod": "^3.25.76 || ^4" }, "engines": { "node": ">=20" @@ -2599,99 +2340,115 @@ "@langchain/core": "^1.0.0" } }, - "node_modules/@mermaid-js/parser": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@mermaid-js/parser/-/parser-1.1.0.tgz", - "integrity": "sha512-gxK9ZX2+Fex5zu8LhRQoMeMPEHbc73UKZ0FQ54YrQtUxE1VVhMwzeNtKRPAu5aXks4FasbMe4xB4bWrmq6Jlxw==", - "license": "MIT", - "dependencies": { - "langium": "^4.0.0" - } - }, - "node_modules/@n8n_io/riot-tmpl": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@n8n_io/riot-tmpl/-/riot-tmpl-4.0.1.tgz", - "integrity": "sha512-/zdRbEfTFjsm1NqnpPQHgZTkTdbp5v3VUxGeMA9098sps8jRCTraQkc3AQstJgHUm7ylBXJcIVhnVeLUMWAfwQ==", + "node_modules/@n8n/ai-utilities/node_modules/@langchain/community/node_modules/zod": { + "version": "4.3.6", + "resolved": "https://registry.npmjs.org/zod/-/zod-4.3.6.tgz", + "integrity": "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg==", + "dev": true, "license": "MIT", - "peer": true, - "dependencies": { - "eslint-config-riot": "^1.0.0" + "funding": { + "url": "https://github.com/sponsors/colinhacks" } }, - "node_modules/@n8n/ai-node-sdk": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/@n8n/ai-node-sdk/-/ai-node-sdk-0.5.1.tgz", - "integrity": "sha512-7YH6SVVP3OsCz7dcha3uULJ+2qSXdBvBP6a6HM2bVTRnL2D1wwz/tti2uuR+DW3luKpeqmE3p9w4mf6mYH0AZA==", + "node_modules/@n8n/ai-utilities/node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", "dev": true, - "license": "SEE LICENSE IN LICENSE.md", - "dependencies": { - "@n8n/ai-utilities": "0.8.1" - } + "license": "Python-2.0" }, - "node_modules/@n8n/ai-utilities": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/@n8n/ai-utilities/-/ai-utilities-0.8.1.tgz", - "integrity": "sha512-2fGq0zh8CaC6ZmzlNKO7jfUHrepALBcntHMlMikN8nz2ablF/4NqetXrtPrx26hq2FzePIrMM+NBFOTCZlqKoA==", + "node_modules/@n8n/ai-utilities/node_modules/chalk": { + "version": "5.6.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz", + "integrity": "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==", "dev": true, - "license": "SEE LICENSE IN LICENSE.md", - "dependencies": { - "@langchain/classic": "1.0.5", - "@langchain/community": "1.1.14", - "@langchain/core": "1.1.34", - "@langchain/openai": "1.1.3", - "@langchain/textsplitters": "1.0.1", - "@n8n/config": "2.13.1", - "@n8n/typescript-config": "1.3.0", - "https-proxy-agent": "7.0.6", - "js-tiktoken": "1.0.12", - "langchain": "1.2.30", - "proxy-from-env": "^1.1.0", - "tmp-promise": "3.0.3", - "undici": "^6.21.0", - "zod": "3.25.67", - "zod-to-json-schema": "3.23.3" + "license": "MIT", + "optional": true, + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" }, - "peerDependencies": { - "n8n-workflow": "*" + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/@n8n/ai-utilities/node_modules/@langchain/core": { - "version": "1.1.34", - "resolved": "https://registry.npmjs.org/@langchain/core/-/core-1.1.34.tgz", - "integrity": "sha512-IDlZES5Vexo5meLQRCGkAU7NM0tPGPfPP5wcUzBd7Ot+JoFBmSXutC4gGzvZod5AKRVn3I0Qy5k8vkTraY21jA==", + "node_modules/@n8n/ai-utilities/node_modules/js-yaml": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", + "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", "dev": true, "license": "MIT", "dependencies": { - "@cfworker/json-schema": "^4.0.2", - "@standard-schema/spec": "^1.1.0", - "ansi-styles": "^5.0.0", - "camelcase": "6", - "decamelize": "1.2.0", - "js-tiktoken": "^1.0.12", - "langsmith": ">=0.5.0 <1.0.0", - "mustache": "^4.2.0", + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/@n8n/ai-utilities/node_modules/langsmith": { + "version": "0.5.16", + "resolved": "https://registry.npmjs.org/langsmith/-/langsmith-0.5.16.tgz", + "integrity": "sha512-nSsSnTo3gjg1dnb48vb8i582zyjvtPbn+EpR6P1pNELb+4Hb4R3nt7LDy+Tl1ltw73vPGfJQtUWOl28irI1b5w==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "chalk": "^5.6.2", + "console-table-printer": "^2.12.1", "p-queue": "^6.6.2", - "uuid": "^11.1.0", - "zod": "^3.25.76 || ^4" + "semver": "^7.6.3", + "uuid": "^10.0.0" }, - "engines": { - "node": ">=20" + "peerDependencies": { + "@opentelemetry/api": "*", + "@opentelemetry/exporter-trace-otlp-proto": "*", + "@opentelemetry/sdk-trace-base": "*", + "openai": "*", + "ws": ">=7" + }, + "peerDependenciesMeta": { + "@opentelemetry/api": { + "optional": true + }, + "@opentelemetry/exporter-trace-otlp-proto": { + "optional": true + }, + "@opentelemetry/sdk-trace-base": { + "optional": true + }, + "openai": { + "optional": true + }, + "ws": { + "optional": true + } } }, - "node_modules/@n8n/ai-utilities/node_modules/@langchain/core/node_modules/zod": { - "version": "4.3.6", - "resolved": "https://registry.npmjs.org/zod/-/zod-4.3.6.tgz", - "integrity": "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg==", + "node_modules/@n8n/ai-utilities/node_modules/openai": { + "version": "6.33.0", + "resolved": "https://registry.npmjs.org/openai/-/openai-6.33.0.tgz", + "integrity": "sha512-xAYN1W3YsDXJWA5F277135YfkEk6H7D3D6vWwRhJ3OEkzRgcyK8z/P5P9Gyi/wB4N8kK9kM5ZjprfvyHagKmpw==", "dev": true, - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/colinhacks" + "license": "Apache-2.0", + "bin": { + "openai": "bin/cli" + }, + "peerDependencies": { + "ws": "^8.18.0", + "zod": "^3.25 || ^4.0" + }, + "peerDependenciesMeta": { + "ws": { + "optional": true + }, + "zod": { + "optional": true + } } }, "node_modules/@n8n/ai-utilities/node_modules/uuid": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-11.1.0.tgz", - "integrity": "sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A==", + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-10.0.0.tgz", + "integrity": "sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==", "dev": true, "funding": [ "https://github.com/sponsors/broofa", @@ -2699,7 +2456,23 @@ ], "license": "MIT", "bin": { - "uuid": "dist/esm/bin/uuid" + "uuid": "dist/bin/uuid" + } + }, + "node_modules/@n8n/ai-utilities/node_modules/yaml": { + "version": "2.8.3", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.3.tgz", + "integrity": "sha512-AvbaCLOO2Otw/lW5bmh9d/WEdcDFdQp2Z2ZUH3pX9U2ihyUY0nvLv7J6TrWowklRGPYbB/IuIMfYgxaCPg5Bpg==", + "dev": true, + "license": "ISC", + "bin": { + "yaml": "bin.mjs" + }, + "engines": { + "node": ">= 14.6" + }, + "funding": { + "url": "https://github.com/sponsors/eemeli" } }, "node_modules/@n8n/ai-utilities/node_modules/zod": { @@ -2745,9 +2518,7 @@ } }, "node_modules/@n8n/errors": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/@n8n/errors/-/errors-0.6.0.tgz", - "integrity": "sha512-oVJ0lgRYJY6/aPOW2h37ea5T+nX7/wULRn5FymwYeaiYlsLdqwIQEtGwZrajpzxJB0Os74u4lSH3WWQgZCkgxQ==", + "version": "0.5.0", "license": "SEE LICENSE IN LICENSE.md", "peer": true, "dependencies": { @@ -2756,8 +2527,6 @@ }, "node_modules/@n8n/errors/node_modules/callsites": { "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", "license": "MIT", "peer": true, "engines": { @@ -2778,24 +2547,6 @@ "eslint": ">= 9" } }, - "node_modules/@n8n/expression-runtime": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/@n8n/expression-runtime/-/expression-runtime-0.5.0.tgz", - "integrity": "sha512-DqkF79FyuljdjlSz1i6PfbrQ47w5U/fIqjoyAbRVxr+YatiM3RAe0zhfQ1ur1lV5QTq0BHnY8zsfFQ+DIItPhA==", - "license": "SEE LICENSE IN LICENSE.md", - "peer": true, - "dependencies": { - "@n8n/tournament": "1.0.6", - "isolated-vm": "^6.0.2", - "js-base64": "3.7.2", - "jssha": "3.3.1", - "lodash": "4.17.23", - "luxon": "3.7.2", - "md5": "2.3.0", - "title-case": "3.0.3", - "transliteration": "2.3.5" - } - }, "node_modules/@n8n/node-cli": { "version": "0.24.1", "resolved": "https://registry.npmjs.org/@n8n/node-cli/-/node-cli-0.24.1.tgz", @@ -2829,8 +2580,6 @@ }, "node_modules/@n8n/tournament": { "version": "1.0.6", - "resolved": "https://registry.npmjs.org/@n8n/tournament/-/tournament-1.0.6.tgz", - "integrity": "sha512-UGSxYXXVuOX0yL6HTLBStKYwLIa0+JmRKiSZSCMcM2s2Wax984KWT6XIA1TR/27i7yYpDk1MY14KsTPnuEp27A==", "license": "Apache-2.0", "peer": true, "dependencies": { @@ -2851,227 +2600,12 @@ "dev": true, "license": "SEE LICENSE IN LICENSE.md" }, - "node_modules/@napi-rs/canvas": { - "version": "0.1.97", - "resolved": "https://registry.npmjs.org/@napi-rs/canvas/-/canvas-0.1.97.tgz", - "integrity": "sha512-8cFniXvrIEnVwuNSRCW9wirRZbHvrD3JVujdS2P5n5xiJZNZMOZcfOvJ1pb66c7jXMKHHglJEDVJGbm8XWFcXQ==", - "license": "MIT", - "optional": true, - "workspaces": [ - "e2e/*" - ], - "engines": { - "node": ">= 10" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/Brooooooklyn" - }, - "optionalDependencies": { - "@napi-rs/canvas-android-arm64": "0.1.97", - "@napi-rs/canvas-darwin-arm64": "0.1.97", - "@napi-rs/canvas-darwin-x64": "0.1.97", - "@napi-rs/canvas-linux-arm-gnueabihf": "0.1.97", - "@napi-rs/canvas-linux-arm64-gnu": "0.1.97", - "@napi-rs/canvas-linux-arm64-musl": "0.1.97", - "@napi-rs/canvas-linux-riscv64-gnu": "0.1.97", - "@napi-rs/canvas-linux-x64-gnu": "0.1.97", - "@napi-rs/canvas-linux-x64-musl": "0.1.97", - "@napi-rs/canvas-win32-arm64-msvc": "0.1.97", - "@napi-rs/canvas-win32-x64-msvc": "0.1.97" - } - }, - "node_modules/@napi-rs/canvas-android-arm64": { - "version": "0.1.97", - "resolved": "https://registry.npmjs.org/@napi-rs/canvas-android-arm64/-/canvas-android-arm64-0.1.97.tgz", - "integrity": "sha512-V1c/WVw+NzH8vk7ZK/O8/nyBSCQimU8sfMsB/9qeSvdkGKNU7+mxy/bIF0gTgeBFmHpj30S4E9WHMSrxXGQuVQ==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">= 10" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/Brooooooklyn" - } - }, - "node_modules/@napi-rs/canvas-darwin-arm64": { - "version": "0.1.97", - "resolved": "https://registry.npmjs.org/@napi-rs/canvas-darwin-arm64/-/canvas-darwin-arm64-0.1.97.tgz", - "integrity": "sha512-ok+SCEF4YejcxuJ9Rm+WWunHHpf2HmiPxfz6z1a/NFQECGXtsY7A4B8XocK1LmT1D7P174MzwPF9Wy3AUAwEPw==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 10" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/Brooooooklyn" - } - }, - "node_modules/@napi-rs/canvas-darwin-x64": { - "version": "0.1.97", - "resolved": "https://registry.npmjs.org/@napi-rs/canvas-darwin-x64/-/canvas-darwin-x64-0.1.97.tgz", - "integrity": "sha512-PUP6e6/UGlclUvAQNnuXCcnkpdUou6VYZfQOQxExLp86epOylmiwLkqXIvpFmjoTEDmPmXrI+coL/9EFU1gKPA==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 10" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/Brooooooklyn" - } - }, - "node_modules/@napi-rs/canvas-linux-arm-gnueabihf": { - "version": "0.1.97", - "resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-arm-gnueabihf/-/canvas-linux-arm-gnueabihf-0.1.97.tgz", - "integrity": "sha512-XyXH2L/cic8eTNtbrXCcvqHtMX/nEOxN18+7rMrAM2XtLYC/EB5s0wnO1FsLMWmK+04ZSLN9FBGipo7kpIkcOw==", - "cpu": [ - "arm" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/Brooooooklyn" - } - }, - "node_modules/@napi-rs/canvas-linux-arm64-gnu": { - "version": "0.1.97", - "resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-arm64-gnu/-/canvas-linux-arm64-gnu-0.1.97.tgz", - "integrity": "sha512-Kuq/M3djq0K8ktgz6nPlK7Ne5d4uWeDxPpyKWOjWDK2RIOhHVtLtyLiJw2fuldw7Vn4mhw05EZXCEr4Q76rs9w==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/Brooooooklyn" - } - }, - "node_modules/@napi-rs/canvas-linux-arm64-musl": { - "version": "0.1.97", - "resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-arm64-musl/-/canvas-linux-arm64-musl-0.1.97.tgz", - "integrity": "sha512-kKmSkQVnWeqg7qdsiXvYxKhAFuHz3tkBjW/zyQv5YKUPhotpaVhpBGv5LqCngzyuRV85SXoe+OFj+Tv0a0QXkQ==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/Brooooooklyn" - } - }, - "node_modules/@napi-rs/canvas-linux-riscv64-gnu": { - "version": "0.1.97", - "resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-riscv64-gnu/-/canvas-linux-riscv64-gnu-0.1.97.tgz", - "integrity": "sha512-Jc7I3A51jnEOIAXeLsN/M/+Z28LUeakcsXs07FLq9prXc0eYOtVwsDEv913Gr+06IRo34gJJVgT0TXvmz+N2VA==", - "cpu": [ - "riscv64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/Brooooooklyn" - } - }, - "node_modules/@napi-rs/canvas-linux-x64-gnu": { - "version": "0.1.97", - "resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-x64-gnu/-/canvas-linux-x64-gnu-0.1.97.tgz", - "integrity": "sha512-iDUBe7AilfuBSRbSa8/IGX38Mf+iCSBqoVKLSQ5XaY2JLOaqz1TVyPFEyIck7wT6mRQhQt5sN6ogfjIDfi74tg==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/Brooooooklyn" - } - }, - "node_modules/@napi-rs/canvas-linux-x64-musl": { - "version": "0.1.97", - "resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-x64-musl/-/canvas-linux-x64-musl-0.1.97.tgz", - "integrity": "sha512-AKLFd/v0Z5fvgqBDqhvqtAdx+fHMJ5t9JcUNKq4FIZ5WH+iegGm8HPdj00NFlCSnm83Fp3Ln8I2f7uq1aIiWaA==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/Brooooooklyn" - } - }, - "node_modules/@napi-rs/canvas-win32-arm64-msvc": { - "version": "0.1.97", - "resolved": "https://registry.npmjs.org/@napi-rs/canvas-win32-arm64-msvc/-/canvas-win32-arm64-msvc-0.1.97.tgz", - "integrity": "sha512-u883Yr6A6fO7Vpsy9YE4FVCIxzzo5sO+7pIUjjoDLjS3vQaNMkVzx5bdIpEL+ob+gU88WDK4VcxYMZ6nmnoX9A==", - "cpu": [ - "arm64" - ], + "node_modules/@napi-rs/canvas": { + "version": "0.1.95", "license": "MIT", "optional": true, - "os": [ - "win32" + "workspaces": [ + "e2e/*" ], "engines": { "node": ">= 10" @@ -3079,19 +2613,30 @@ "funding": { "type": "github", "url": "https://github.com/sponsors/Brooooooklyn" + }, + "optionalDependencies": { + "@napi-rs/canvas-android-arm64": "0.1.95", + "@napi-rs/canvas-darwin-arm64": "0.1.95", + "@napi-rs/canvas-darwin-x64": "0.1.95", + "@napi-rs/canvas-linux-arm-gnueabihf": "0.1.95", + "@napi-rs/canvas-linux-arm64-gnu": "0.1.95", + "@napi-rs/canvas-linux-arm64-musl": "0.1.95", + "@napi-rs/canvas-linux-riscv64-gnu": "0.1.95", + "@napi-rs/canvas-linux-x64-gnu": "0.1.95", + "@napi-rs/canvas-linux-x64-musl": "0.1.95", + "@napi-rs/canvas-win32-arm64-msvc": "0.1.95", + "@napi-rs/canvas-win32-x64-msvc": "0.1.95" } }, - "node_modules/@napi-rs/canvas-win32-x64-msvc": { - "version": "0.1.97", - "resolved": "https://registry.npmjs.org/@napi-rs/canvas-win32-x64-msvc/-/canvas-win32-x64-msvc-0.1.97.tgz", - "integrity": "sha512-sWtD2EE3fV0IzN+iiQUqr/Q1SwqWhs2O1FKItFlxtdDkikpEj5g7DKQpY3x55H/MAOnL8iomnlk3mcEeGiUMoQ==", + "node_modules/@napi-rs/canvas-darwin-arm64": { + "version": "0.1.95", "cpu": [ - "x64" + "arm64" ], "license": "MIT", "optional": true, "os": [ - "win32" + "darwin" ], "engines": { "node": ">= 10" @@ -3101,23 +2646,8 @@ "url": "https://github.com/sponsors/Brooooooklyn" } }, - "node_modules/@napi-rs/wasm-runtime": { - "version": "0.2.12", - "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-0.2.12.tgz", - "integrity": "sha512-ZVWUcfwY4E/yPitQJl481FjFo3K22D6qF0DuFH6Y/nbnE11GY5uguDxZMGXPQ8WQ0128MXQD7TnfHyK4oWoIJQ==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "@emnapi/core": "^1.4.3", - "@emnapi/runtime": "^1.4.3", - "@tybys/wasm-util": "^0.10.0" - } - }, "node_modules/@neoconfetti/react": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@neoconfetti/react/-/react-1.0.0.tgz", - "integrity": "sha512-klcSooChXXOzIm+SE5IISIAn3bYzYfPjbX7D7HoqZL84oAfgREeSg5vSIaSFH+DaGzzvImTyWe1OyrJ67vik4A==", "dev": true, "license": "MIT" }, @@ -3160,9 +2690,7 @@ } }, "node_modules/@oclif/core": { - "version": "4.10.3", - "resolved": "https://registry.npmjs.org/@oclif/core/-/core-4.10.3.tgz", - "integrity": "sha512-0mD8vcrrX5uRsxzvI8tbWmSVGngvZA/Qo6O0ZGvLPAWEauSf5GFniwgirhY0SkszuHwu0S1J1ivj/jHmqtIDuA==", + "version": "4.8.2", "dev": true, "license": "MIT", "dependencies": { @@ -3189,13 +2717,6 @@ "node": ">=18.0.0" } }, - "node_modules/@package-json/types": { - "version": "0.0.12", - "resolved": "https://registry.npmjs.org/@package-json/types/-/types-0.0.12.tgz", - "integrity": "sha512-uu43FGU34B5VM9mCNjXCwLaGHYjXdNincqKLaraaCW+7S2+SmiBg1Nv8bPnmschrIfZmfKNY9f3fC376MRrObw==", - "dev": true, - "license": "MIT" - }, "node_modules/@phosphor-icons/react": { "version": "2.1.10", "resolved": "https://registry.npmjs.org/@phosphor-icons/react/-/react-2.1.10.tgz", @@ -3226,10 +2747,19 @@ "node": ">=18" } }, + "node_modules/@polka/url": { + "version": "1.0.0-next.29", + "dev": true, + "license": "MIT" + }, "node_modules/@probo/console": { "resolved": "apps/console", "link": true }, + "node_modules/@probo/cookie-banner": { + "resolved": "packages/cookie-banner", + "link": true + }, "node_modules/@probo/coredata": { "resolved": "packages/coredata", "link": true @@ -3296,20 +2826,14 @@ }, "node_modules/@radix-ui/number": { "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@radix-ui/number/-/number-1.1.1.tgz", - "integrity": "sha512-MkKCwxlXTgz6CFoJx3pCwn07GKp36+aZyu/u2Ln2VrA5DcdyCZkASEDBTd8x5whTQQL5CiYf4prXKLcgQdv29g==", "license": "MIT" }, "node_modules/@radix-ui/primitive": { "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.3.tgz", - "integrity": "sha512-JTF99U/6XIjCBo0wqkU5sK10glYe27MRRsfwoiq5zzOEZLHU3A3KCMa5X/azekYRCJ0HlwI0crAXS/5dEHTzDg==", "license": "MIT" }, "node_modules/@radix-ui/react-alert-dialog": { "version": "1.1.15", - "resolved": "https://registry.npmjs.org/@radix-ui/react-alert-dialog/-/react-alert-dialog-1.1.15.tgz", - "integrity": "sha512-oTVLkEw5GpdRe29BqJ0LSDFWI3qu0vR1M0mUkOQWDIUnY/QIkLpgDMWuKxP94c2NAC2LGcgVhG1ImF3jkZ5wXw==", "license": "MIT", "dependencies": { "@radix-ui/primitive": "1.1.3", @@ -3336,8 +2860,6 @@ }, "node_modules/@radix-ui/react-arrow": { "version": "1.1.7", - "resolved": "https://registry.npmjs.org/@radix-ui/react-arrow/-/react-arrow-1.1.7.tgz", - "integrity": "sha512-F+M1tLhO+mlQaOWspE8Wstg+z6PwxwRd8oQ8IXceWz92kfAmalTRf0EjrouQeo7QssEPfCn05B4Ihs1K9WQ/7w==", "license": "MIT", "dependencies": { "@radix-ui/react-primitive": "2.1.3" @@ -3359,8 +2881,6 @@ }, "node_modules/@radix-ui/react-collection": { "version": "1.1.7", - "resolved": "https://registry.npmjs.org/@radix-ui/react-collection/-/react-collection-1.1.7.tgz", - "integrity": "sha512-Fh9rGN0MoI4ZFUNyfFVNU4y9LUz93u9/0K+yLgA2bwRojxM8JU1DyvvMBabnZPBgMWREAJvU2jjVzq+LrFUglw==", "license": "MIT", "dependencies": { "@radix-ui/react-compose-refs": "1.1.2", @@ -3385,8 +2905,6 @@ }, "node_modules/@radix-ui/react-compose-refs": { "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.2.tgz", - "integrity": "sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg==", "license": "MIT", "peerDependencies": { "@types/react": "*", @@ -3400,8 +2918,6 @@ }, "node_modules/@radix-ui/react-context": { "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.1.2.tgz", - "integrity": "sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA==", "license": "MIT", "peerDependencies": { "@types/react": "*", @@ -3415,8 +2931,6 @@ }, "node_modules/@radix-ui/react-dialog": { "version": "1.1.15", - "resolved": "https://registry.npmjs.org/@radix-ui/react-dialog/-/react-dialog-1.1.15.tgz", - "integrity": "sha512-TCglVRtzlffRNxRMEyR36DGBLJpeusFcgMVD9PZEzAKnUs1lKCgX5u9BmC2Yg+LL9MgZDugFFs1Vl+Jp4t/PGw==", "license": "MIT", "dependencies": { "@radix-ui/primitive": "1.1.3", @@ -3451,8 +2965,6 @@ }, "node_modules/@radix-ui/react-dialog/node_modules/@radix-ui/react-portal": { "version": "1.1.9", - "resolved": "https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.1.9.tgz", - "integrity": "sha512-bpIxvq03if6UNwXZ+HTK71JLh4APvnXntDc6XOX8UVq4XQOVl7lwok0AvIl+b8zgCw3fSaVTZMpAPPagXbKmHQ==", "license": "MIT", "dependencies": { "@radix-ui/react-primitive": "2.1.3", @@ -3475,8 +2987,6 @@ }, "node_modules/@radix-ui/react-direction": { "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-direction/-/react-direction-1.1.1.tgz", - "integrity": "sha512-1UEWRX6jnOA2y4H5WczZ44gOOjTEmlqv1uNW4GAJEO5+bauCBhv8snY65Iw5/VOS/ghKN9gr2KjnLKxrsvoMVw==", "license": "MIT", "peerDependencies": { "@types/react": "*", @@ -3490,8 +3000,6 @@ }, "node_modules/@radix-ui/react-dismissable-layer": { "version": "1.1.11", - "resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.1.11.tgz", - "integrity": "sha512-Nqcp+t5cTB8BinFkZgXiMJniQH0PsUt2k51FUhbdfeKvc4ACcG2uQniY/8+h1Yv6Kza4Q7lD7PQV0z0oicE0Mg==", "license": "MIT", "dependencies": { "@radix-ui/primitive": "1.1.3", @@ -3517,8 +3025,6 @@ }, "node_modules/@radix-ui/react-dropdown-menu": { "version": "2.1.16", - "resolved": "https://registry.npmjs.org/@radix-ui/react-dropdown-menu/-/react-dropdown-menu-2.1.16.tgz", - "integrity": "sha512-1PLGQEynI/3OX/ftV54COn+3Sud/Mn8vALg2rWnBLnRaGtJDduNW/22XjlGgPdpcIbiQxjKtb7BkcjP00nqfJw==", "license": "MIT", "dependencies": { "@radix-ui/primitive": "1.1.3", @@ -3546,8 +3052,6 @@ }, "node_modules/@radix-ui/react-focus-guards": { "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-guards/-/react-focus-guards-1.1.3.tgz", - "integrity": "sha512-0rFg/Rj2Q62NCm62jZw0QX7a3sz6QCQU0LpZdNrJX8byRGaGVTqbrW9jAoIAHyMQqsNpeZ81YgSizOt5WXq0Pw==", "license": "MIT", "peerDependencies": { "@types/react": "*", @@ -3561,8 +3065,6 @@ }, "node_modules/@radix-ui/react-focus-scope": { "version": "1.1.7", - "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-scope/-/react-focus-scope-1.1.7.tgz", - "integrity": "sha512-t2ODlkXBQyn7jkl6TNaw/MtVEVvIGelJDCG41Okq/KwUsJBwQ4XVZsHAVUkK4mBv3ewiAS3PGuUWuY2BoK4ZUw==", "license": "MIT", "dependencies": { "@radix-ui/react-compose-refs": "1.1.2", @@ -3586,8 +3088,6 @@ }, "node_modules/@radix-ui/react-id": { "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-id/-/react-id-1.1.1.tgz", - "integrity": "sha512-kGkGegYIdQsOb4XjsfM97rXsiHaBwco+hFI66oO4s9LU+PLAC5oJ7khdOVFxkhsmlbpUqDAvXw11CluXP+jkHg==", "license": "MIT", "dependencies": { "@radix-ui/react-use-layout-effect": "1.1.1" @@ -3604,8 +3104,6 @@ }, "node_modules/@radix-ui/react-label": { "version": "2.1.8", - "resolved": "https://registry.npmjs.org/@radix-ui/react-label/-/react-label-2.1.8.tgz", - "integrity": "sha512-FmXs37I6hSBVDlO4y764TNz1rLgKwjJMQ0EGte6F3Cb3f4bIuHB/iLa/8I9VKkmOy+gNHq8rql3j686ACVV21A==", "license": "MIT", "dependencies": { "@radix-ui/react-primitive": "2.1.4" @@ -3627,8 +3125,6 @@ }, "node_modules/@radix-ui/react-label/node_modules/@radix-ui/react-primitive": { "version": "2.1.4", - "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.4.tgz", - "integrity": "sha512-9hQc4+GNVtJAIEPEqlYqW5RiYdrr8ea5XQ0ZOnD6fgru+83kqT15mq2OCcbe8KnjRZl5vF3ks69AKz3kh1jrhg==", "license": "MIT", "dependencies": { "@radix-ui/react-slot": "1.2.4" @@ -3650,8 +3146,6 @@ }, "node_modules/@radix-ui/react-label/node_modules/@radix-ui/react-slot": { "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.4.tgz", - "integrity": "sha512-Jl+bCv8HxKnlTLVrcDE8zTMJ09R9/ukw4qBs/oZClOfoQk/cOTbDn+NceXfV7j09YPVQUryJPHurafcSg6EVKA==", "license": "MIT", "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" @@ -3668,8 +3162,6 @@ }, "node_modules/@radix-ui/react-menu": { "version": "2.1.16", - "resolved": "https://registry.npmjs.org/@radix-ui/react-menu/-/react-menu-2.1.16.tgz", - "integrity": "sha512-72F2T+PLlphrqLcAotYPp0uJMr5SjP5SL01wfEspJbru5Zs5vQaSHb4VB3ZMJPimgHHCHG7gMOeOB9H3Hdmtxg==", "license": "MIT", "dependencies": { "@radix-ui/primitive": "1.1.3", @@ -3708,8 +3200,6 @@ }, "node_modules/@radix-ui/react-menu/node_modules/@radix-ui/react-portal": { "version": "1.1.9", - "resolved": "https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.1.9.tgz", - "integrity": "sha512-bpIxvq03if6UNwXZ+HTK71JLh4APvnXntDc6XOX8UVq4XQOVl7lwok0AvIl+b8zgCw3fSaVTZMpAPPagXbKmHQ==", "license": "MIT", "dependencies": { "@radix-ui/react-primitive": "2.1.3", @@ -3732,8 +3222,6 @@ }, "node_modules/@radix-ui/react-popover": { "version": "1.1.15", - "resolved": "https://registry.npmjs.org/@radix-ui/react-popover/-/react-popover-1.1.15.tgz", - "integrity": "sha512-kr0X2+6Yy/vJzLYJUPCZEc8SfQcf+1COFoAqauJm74umQhta9M7lNJHP7QQS3vkvcGLQUbWpMzwrXYwrYztHKA==", "license": "MIT", "dependencies": { "@radix-ui/primitive": "1.1.3", @@ -3769,8 +3257,6 @@ }, "node_modules/@radix-ui/react-popover/node_modules/@radix-ui/react-portal": { "version": "1.1.9", - "resolved": "https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.1.9.tgz", - "integrity": "sha512-bpIxvq03if6UNwXZ+HTK71JLh4APvnXntDc6XOX8UVq4XQOVl7lwok0AvIl+b8zgCw3fSaVTZMpAPPagXbKmHQ==", "license": "MIT", "dependencies": { "@radix-ui/react-primitive": "2.1.3", @@ -3793,8 +3279,6 @@ }, "node_modules/@radix-ui/react-popper": { "version": "1.2.8", - "resolved": "https://registry.npmjs.org/@radix-ui/react-popper/-/react-popper-1.2.8.tgz", - "integrity": "sha512-0NJQ4LFFUuWkE7Oxf0htBKS6zLkkjBH+hM1uk7Ng705ReR8m/uelduy1DBo0PyBXPKVnBA6YBlU94MBGXrSBCw==", "license": "MIT", "dependencies": { "@floating-ui/react-dom": "^2.0.0", @@ -3825,8 +3309,6 @@ }, "node_modules/@radix-ui/react-portal": { "version": "1.1.10", - "resolved": "https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.1.10.tgz", - "integrity": "sha512-4kY9IVa6+9nJPsYmngK5Uk2kUmZnv7ChhHAFeQ5oaj8jrR1bIi3xww8nH71pz1/Ve4d/cXO3YxT8eikt1B0a8w==", "license": "MIT", "dependencies": { "@radix-ui/react-primitive": "2.1.4", @@ -3849,8 +3331,6 @@ }, "node_modules/@radix-ui/react-portal/node_modules/@radix-ui/react-primitive": { "version": "2.1.4", - "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.4.tgz", - "integrity": "sha512-9hQc4+GNVtJAIEPEqlYqW5RiYdrr8ea5XQ0ZOnD6fgru+83kqT15mq2OCcbe8KnjRZl5vF3ks69AKz3kh1jrhg==", "license": "MIT", "dependencies": { "@radix-ui/react-slot": "1.2.4" @@ -3872,8 +3352,6 @@ }, "node_modules/@radix-ui/react-portal/node_modules/@radix-ui/react-slot": { "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.4.tgz", - "integrity": "sha512-Jl+bCv8HxKnlTLVrcDE8zTMJ09R9/ukw4qBs/oZClOfoQk/cOTbDn+NceXfV7j09YPVQUryJPHurafcSg6EVKA==", "license": "MIT", "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" @@ -3890,8 +3368,6 @@ }, "node_modules/@radix-ui/react-presence": { "version": "1.1.5", - "resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.1.5.tgz", - "integrity": "sha512-/jfEwNDdQVBCNvjkGit4h6pMOzq8bHkopq458dPt2lMjx+eBQUohZNG9A7DtO/O5ukSbxuaNGXMjHicgwy6rQQ==", "license": "MIT", "dependencies": { "@radix-ui/react-compose-refs": "1.1.2", @@ -3914,8 +3390,6 @@ }, "node_modules/@radix-ui/react-primitive": { "version": "2.1.3", - "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.3.tgz", - "integrity": "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ==", "license": "MIT", "dependencies": { "@radix-ui/react-slot": "1.2.3" @@ -3937,8 +3411,6 @@ }, "node_modules/@radix-ui/react-roving-focus": { "version": "1.1.11", - "resolved": "https://registry.npmjs.org/@radix-ui/react-roving-focus/-/react-roving-focus-1.1.11.tgz", - "integrity": "sha512-7A6S9jSgm/S+7MdtNDSb+IU859vQqJ/QAtcYQcfFC6W8RS4IxIZDldLR0xqCFZ6DCyrQLjLPsxtTNch5jVA4lA==", "license": "MIT", "dependencies": { "@radix-ui/primitive": "1.1.3", @@ -3968,8 +3440,6 @@ }, "node_modules/@radix-ui/react-scroll-area": { "version": "1.2.10", - "resolved": "https://registry.npmjs.org/@radix-ui/react-scroll-area/-/react-scroll-area-1.2.10.tgz", - "integrity": "sha512-tAXIa1g3sM5CGpVT0uIbUx/U3Gs5N8T52IICuCtObaos1S8fzsrPXG5WObkQN3S6NVl6wKgPhAIiBGbWnvc97A==", "license": "MIT", "dependencies": { "@radix-ui/number": "1.1.1", @@ -3999,8 +3469,6 @@ }, "node_modules/@radix-ui/react-select": { "version": "2.2.6", - "resolved": "https://registry.npmjs.org/@radix-ui/react-select/-/react-select-2.2.6.tgz", - "integrity": "sha512-I30RydO+bnn2PQztvo25tswPH+wFBjehVGtmagkU78yMdwTwVf12wnAOF+AeP8S2N8xD+5UPbGhkUfPyvT+mwQ==", "license": "MIT", "dependencies": { "@radix-ui/number": "1.1.1", @@ -4042,8 +3510,6 @@ }, "node_modules/@radix-ui/react-select/node_modules/@radix-ui/react-portal": { "version": "1.1.9", - "resolved": "https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.1.9.tgz", - "integrity": "sha512-bpIxvq03if6UNwXZ+HTK71JLh4APvnXntDc6XOX8UVq4XQOVl7lwok0AvIl+b8zgCw3fSaVTZMpAPPagXbKmHQ==", "license": "MIT", "dependencies": { "@radix-ui/react-primitive": "2.1.3", @@ -4066,8 +3532,6 @@ }, "node_modules/@radix-ui/react-slot": { "version": "1.2.3", - "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz", - "integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==", "license": "MIT", "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" @@ -4084,8 +3548,6 @@ }, "node_modules/@radix-ui/react-tabs": { "version": "1.1.13", - "resolved": "https://registry.npmjs.org/@radix-ui/react-tabs/-/react-tabs-1.1.13.tgz", - "integrity": "sha512-7xdcatg7/U+7+Udyoj2zodtI9H/IIopqo+YOIcZOq1nJwXWBZ9p8xiu5llXlekDbZkca79a/fozEYQXIA4sW6A==", "license": "MIT", "dependencies": { "@radix-ui/primitive": "1.1.3", @@ -4114,8 +3576,6 @@ }, "node_modules/@radix-ui/react-use-callback-ref": { "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.1.1.tgz", - "integrity": "sha512-FkBMwD+qbGQeMu1cOHnuGB6x4yzPjho8ap5WtbEJ26umhgqVXbhekKUQO+hZEL1vU92a3wHwdp0HAcqAUF5iDg==", "license": "MIT", "peerDependencies": { "@types/react": "*", @@ -4129,8 +3589,6 @@ }, "node_modules/@radix-ui/react-use-controllable-state": { "version": "1.2.2", - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.2.2.tgz", - "integrity": "sha512-BjasUjixPFdS+NKkypcyyN5Pmg83Olst0+c6vGov0diwTEo6mgdqVR6hxcEgFuh4QrAs7Rc+9KuGJ9TVCj0Zzg==", "license": "MIT", "dependencies": { "@radix-ui/react-use-effect-event": "0.0.2", @@ -4148,8 +3606,6 @@ }, "node_modules/@radix-ui/react-use-effect-event": { "version": "0.0.2", - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-effect-event/-/react-use-effect-event-0.0.2.tgz", - "integrity": "sha512-Qp8WbZOBe+blgpuUT+lw2xheLP8q0oatc9UpmiemEICxGvFLYmHm9QowVZGHtJlGbS6A6yJ3iViad/2cVjnOiA==", "license": "MIT", "dependencies": { "@radix-ui/react-use-layout-effect": "1.1.1" @@ -4166,8 +3622,6 @@ }, "node_modules/@radix-ui/react-use-escape-keydown": { "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-escape-keydown/-/react-use-escape-keydown-1.1.1.tgz", - "integrity": "sha512-Il0+boE7w/XebUHyBjroE+DbByORGR9KKmITzbR7MyQ4akpORYP/ZmbhAr0DG7RmmBqoOnZdy2QlvajJ2QA59g==", "license": "MIT", "dependencies": { "@radix-ui/react-use-callback-ref": "1.1.1" @@ -4184,8 +3638,6 @@ }, "node_modules/@radix-ui/react-use-layout-effect": { "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.1.1.tgz", - "integrity": "sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ==", "license": "MIT", "peerDependencies": { "@types/react": "*", @@ -4199,8 +3651,6 @@ }, "node_modules/@radix-ui/react-use-previous": { "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-previous/-/react-use-previous-1.1.1.tgz", - "integrity": "sha512-2dHfToCj/pzca2Ck724OZ5L0EVrr3eHRNsG/b3xQJLA2hZpVCS99bLAX+hm1IHXDEnzU6by5z/5MIY794/a8NQ==", "license": "MIT", "peerDependencies": { "@types/react": "*", @@ -4214,8 +3664,6 @@ }, "node_modules/@radix-ui/react-use-rect": { "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-rect/-/react-use-rect-1.1.1.tgz", - "integrity": "sha512-QTYuDesS0VtuHNNvMh+CjlKJ4LJickCMUAqjlE3+j8w+RlRpwyX3apEQKGFzbZGdo7XNG1tXa+bQqIE7HIXT2w==", "license": "MIT", "dependencies": { "@radix-ui/rect": "1.1.1" @@ -4232,8 +3680,6 @@ }, "node_modules/@radix-ui/react-use-size": { "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-size/-/react-use-size-1.1.1.tgz", - "integrity": "sha512-ewrXRDTAqAXlkl6t/fkXWNAhFX9I+CkKlw6zjEwk86RSPKwZr3xpBRso655aqYafwtnbpHLj6toFzmd6xdVptQ==", "license": "MIT", "dependencies": { "@radix-ui/react-use-layout-effect": "1.1.1" @@ -4250,8 +3696,6 @@ }, "node_modules/@radix-ui/react-visually-hidden": { "version": "1.2.3", - "resolved": "https://registry.npmjs.org/@radix-ui/react-visually-hidden/-/react-visually-hidden-1.2.3.tgz", - "integrity": "sha512-pzJq12tEaaIhqjbzpCuv/OypJY/BPavOofm+dbab+MHLajy277+1lLm6JFcGgF5eskJ6mquGirhXY2GD/8u8Ug==", "license": "MIT", "dependencies": { "@radix-ui/react-primitive": "2.1.3" @@ -4273,14 +3717,10 @@ }, "node_modules/@radix-ui/rect": { "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@radix-ui/rect/-/rect-1.1.1.tgz", - "integrity": "sha512-HPwpGIzkl28mWyZqG52jiqDJ12waP11Pa1lGoiyUkIEuMLBP0oeK/C89esbXrxsky5we7dfd8U58nm0SgAWpVw==", "license": "MIT" }, "node_modules/@react-email/body": { "version": "0.1.0", - "resolved": "https://registry.npmjs.org/@react-email/body/-/body-0.1.0.tgz", - "integrity": "sha512-o1bcSAmDYNNHECbkeyceCVPGmVsYvT+O3sSO/Ct7apKUu3JphTi31hu+0Nwqr/pgV5QFqdoT5vdS3SW5DJFHgQ==", "license": "MIT", "peerDependencies": { "react": "^18.0 || ^19.0 || ^19.0.0-rc" @@ -4288,8 +3728,6 @@ }, "node_modules/@react-email/button": { "version": "0.2.0", - "resolved": "https://registry.npmjs.org/@react-email/button/-/button-0.2.0.tgz", - "integrity": "sha512-8i+v6cMxr2emz4ihCrRiYJPp2/sdYsNNsBzXStlcA+/B9Umpm5Jj3WJKYpgTPM+aeyiqlG/MMI1AucnBm4f1oQ==", "license": "MIT", "engines": { "node": ">=18.0.0" @@ -4300,8 +3738,6 @@ }, "node_modules/@react-email/code-block": { "version": "0.1.0", - "resolved": "https://registry.npmjs.org/@react-email/code-block/-/code-block-0.1.0.tgz", - "integrity": "sha512-jSpHFsgqnQXxDIssE4gvmdtFncaFQz5D6e22BnVjcCPk/udK+0A9jRwGFEG8JD2si9ZXBmU4WsuqQEczuZn4ww==", "license": "MIT", "dependencies": { "prismjs": "^1.30.0" @@ -4315,8 +3751,6 @@ }, "node_modules/@react-email/code-inline": { "version": "0.0.5", - "resolved": "https://registry.npmjs.org/@react-email/code-inline/-/code-inline-0.0.5.tgz", - "integrity": "sha512-MmAsOzdJpzsnY2cZoPHFPk6uDO/Ncpb4Kh1hAt9UZc1xOW3fIzpe1Pi9y9p6wwUmpaeeDalJxAxH6/fnTquinA==", "license": "MIT", "engines": { "node": ">=18.0.0" @@ -4327,8 +3761,6 @@ }, "node_modules/@react-email/column": { "version": "0.0.13", - "resolved": "https://registry.npmjs.org/@react-email/column/-/column-0.0.13.tgz", - "integrity": "sha512-Lqq17l7ShzJG/d3b1w/+lVO+gp2FM05ZUo/nW0rjxB8xBICXOVv6PqjDnn3FXKssvhO5qAV20lHM6S+spRhEwQ==", "license": "MIT", "engines": { "node": ">=18.0.0" @@ -4339,8 +3771,6 @@ }, "node_modules/@react-email/components": { "version": "0.5.7", - "resolved": "https://registry.npmjs.org/@react-email/components/-/components-0.5.7.tgz", - "integrity": "sha512-ECyVoyDcev2FSQ7C0buXaIJ0+6MRDXNUbCOZwBRrlLdCCRjap2b4+MHrYSTXFzo5kqfjjRoyo/2PbJXFQni67g==", "license": "MIT", "dependencies": { "@react-email/body": "0.1.0", @@ -4373,8 +3803,6 @@ }, "node_modules/@react-email/container": { "version": "0.0.15", - "resolved": "https://registry.npmjs.org/@react-email/container/-/container-0.0.15.tgz", - "integrity": "sha512-Qo2IQo0ru2kZq47REmHW3iXjAQaKu4tpeq/M8m1zHIVwKduL2vYOBQWbC2oDnMtWPmkBjej6XxgtZByxM6cCFg==", "license": "MIT", "engines": { "node": ">=18.0.0" @@ -4385,8 +3813,6 @@ }, "node_modules/@react-email/font": { "version": "0.0.9", - "resolved": "https://registry.npmjs.org/@react-email/font/-/font-0.0.9.tgz", - "integrity": "sha512-4zjq23oT9APXkerqeslPH3OZWuh5X4crHK6nx82mVHV2SrLba8+8dPEnWbaACWTNjOCbcLIzaC9unk7Wq2MIXw==", "license": "MIT", "peerDependencies": { "react": "^18.0 || ^19.0 || ^19.0.0-rc" @@ -4394,8 +3820,6 @@ }, "node_modules/@react-email/head": { "version": "0.0.12", - "resolved": "https://registry.npmjs.org/@react-email/head/-/head-0.0.12.tgz", - "integrity": "sha512-X2Ii6dDFMF+D4niNwMAHbTkeCjlYYnMsd7edXOsi0JByxt9wNyZ9EnhFiBoQdqkE+SMDcu8TlNNttMrf5sJeMA==", "license": "MIT", "engines": { "node": ">=18.0.0" @@ -4406,8 +3830,6 @@ }, "node_modules/@react-email/heading": { "version": "0.0.15", - "resolved": "https://registry.npmjs.org/@react-email/heading/-/heading-0.0.15.tgz", - "integrity": "sha512-xF2GqsvBrp/HbRHWEfOgSfRFX+Q8I5KBEIG5+Lv3Vb2R/NYr0s8A5JhHHGf2pWBMJdbP4B2WHgj/VUrhy8dkIg==", "license": "MIT", "engines": { "node": ">=18.0.0" @@ -4418,8 +3840,6 @@ }, "node_modules/@react-email/hr": { "version": "0.0.11", - "resolved": "https://registry.npmjs.org/@react-email/hr/-/hr-0.0.11.tgz", - "integrity": "sha512-S1gZHVhwOsd1Iad5IFhpfICwNPMGPJidG/Uysy1AwmspyoAP5a4Iw3OWEpINFdgh9MHladbxcLKO2AJO+cA9Lw==", "license": "MIT", "engines": { "node": ">=18.0.0" @@ -4430,8 +3850,6 @@ }, "node_modules/@react-email/html": { "version": "0.0.11", - "resolved": "https://registry.npmjs.org/@react-email/html/-/html-0.0.11.tgz", - "integrity": "sha512-qJhbOQy5VW5qzU74AimjAR9FRFQfrMa7dn4gkEXKMB/S9xZN8e1yC1uA9C15jkXI/PzmJ0muDIWmFwatm5/+VA==", "license": "MIT", "engines": { "node": ">=18.0.0" @@ -4442,8 +3860,6 @@ }, "node_modules/@react-email/img": { "version": "0.0.11", - "resolved": "https://registry.npmjs.org/@react-email/img/-/img-0.0.11.tgz", - "integrity": "sha512-aGc8Y6U5C3igoMaqAJKsCpkbm1XjguQ09Acd+YcTKwjnC2+0w3yGUJkjWB2vTx4tN8dCqQCXO8FmdJpMfOA9EQ==", "license": "MIT", "engines": { "node": ">=18.0.0" @@ -4454,8 +3870,6 @@ }, "node_modules/@react-email/link": { "version": "0.0.12", - "resolved": "https://registry.npmjs.org/@react-email/link/-/link-0.0.12.tgz", - "integrity": "sha512-vF+xxQk2fGS1CN7UPQDbzvcBGfffr+GjTPNiWM38fhBfsLv6A/YUfaqxWlmL7zLzVmo0K2cvvV9wxlSyNba1aQ==", "license": "MIT", "engines": { "node": ">=18.0.0" @@ -4466,8 +3880,6 @@ }, "node_modules/@react-email/markdown": { "version": "0.0.16", - "resolved": "https://registry.npmjs.org/@react-email/markdown/-/markdown-0.0.16.tgz", - "integrity": "sha512-KSUHmoBMYhvc6iGwlIDkm0DRGbGQ824iNjLMCJsBVUoKHGQYs7F/N3b1tnS1YzRUX+GwHIexSsHuIUEi1m+8OQ==", "license": "MIT", "dependencies": { "marked": "^15.0.12" @@ -4481,8 +3893,6 @@ }, "node_modules/@react-email/preview": { "version": "0.0.13", - "resolved": "https://registry.npmjs.org/@react-email/preview/-/preview-0.0.13.tgz", - "integrity": "sha512-F7j9FJ0JN/A4d7yr+aw28p4uX7VLWs7hTHtLo7WRyw4G+Lit6Zucq4UWKRxJC8lpsUdzVmG7aBJnKOT+urqs/w==", "license": "MIT", "engines": { "node": ">=18.0.0" @@ -4493,8 +3903,6 @@ }, "node_modules/@react-email/render": { "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@react-email/render/-/render-1.4.0.tgz", - "integrity": "sha512-ZtJ3noggIvW1ZAryoui95KJENKdCzLmN5F7hyZY1F/17B1vwzuxHB7YkuCg0QqHjDivc5axqYEYdIOw4JIQdUw==", "license": "MIT", "dependencies": { "html-to-text": "^9.0.5", @@ -4511,8 +3919,6 @@ }, "node_modules/@react-email/row": { "version": "0.0.12", - "resolved": "https://registry.npmjs.org/@react-email/row/-/row-0.0.12.tgz", - "integrity": "sha512-HkCdnEjvK3o+n0y0tZKXYhIXUNPDx+2vq1dJTmqappVHXS5tXS6W5JOPZr5j+eoZ8gY3PShI2LWj5rWF7ZEtIQ==", "license": "MIT", "engines": { "node": ">=18.0.0" @@ -4523,8 +3929,6 @@ }, "node_modules/@react-email/section": { "version": "0.0.16", - "resolved": "https://registry.npmjs.org/@react-email/section/-/section-0.0.16.tgz", - "integrity": "sha512-FjqF9xQ8FoeUZYKSdt8sMIKvoT9XF8BrzhT3xiFKdEMwYNbsDflcjfErJe3jb7Wj/es/lKTbV5QR1dnLzGpL3w==", "license": "MIT", "engines": { "node": ">=18.0.0" @@ -4535,8 +3939,6 @@ }, "node_modules/@react-email/tailwind": { "version": "1.2.2", - "resolved": "https://registry.npmjs.org/@react-email/tailwind/-/tailwind-1.2.2.tgz", - "integrity": "sha512-heO9Khaqxm6Ulm6p7HQ9h01oiiLRrZuuEQuYds/O7Iyp3c58sMVHZGIxiRXO/kSs857NZQycpjewEVKF3jhNTw==", "license": "MIT", "engines": { "node": ">=18.0.0" @@ -4547,8 +3949,6 @@ }, "node_modules/@react-email/text": { "version": "0.1.5", - "resolved": "https://registry.npmjs.org/@react-email/text/-/text-0.1.5.tgz", - "integrity": "sha512-o5PNHFSE085VMXayxH+SJ1LSOtGsTv+RpNKnTiJDrJUwoBu77G3PlKOsZZQHCNyD28WsQpl9v2WcJLbQudqwPg==", "license": "MIT", "engines": { "node": ">=18.0.0" @@ -4565,15 +3965,11 @@ }, "node_modules/@rolldown/pluginutils": { "version": "1.0.0-rc.3", - "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-rc.3.tgz", - "integrity": "sha512-eybk3TjzzzV97Dlj5c+XrBFW57eTNhzod66y9HrBlzJ6NsCrWCp/2kaPS3K9wJmurBC0Tdw4yPjXKZqlznim3Q==", "dev": true, "license": "MIT" }, "node_modules/@rollup/pluginutils": { "version": "5.3.0", - "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.3.0.tgz", - "integrity": "sha512-5EdhGZtnu3V88ces7s53hhfK5KSASnJZv8Lulpc04cWO3REESroJXg73DFsOmgbU2BhwV0E20bu2IDZb3VKW4Q==", "dev": true, "license": "MIT", "dependencies": { @@ -4587,366 +3983,26 @@ "peerDependencies": { "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" }, - "peerDependenciesMeta": { - "rollup": { - "optional": true - } - } - }, - "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.60.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.60.1.tgz", - "integrity": "sha512-d6FinEBLdIiK+1uACUttJKfgZREXrF0Qc2SmLII7W2AD8FfiZ9Wjd+rD/iRuf5s5dWrr1GgwXCvPqOuDquOowA==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ] - }, - "node_modules/@rollup/rollup-android-arm64": { - "version": "4.60.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.60.1.tgz", - "integrity": "sha512-YjG/EwIDvvYI1YvYbHvDz/BYHtkY4ygUIXHnTdLhG+hKIQFBiosfWiACWortsKPKU/+dUwQQCKQM3qrDe8c9BA==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ] - }, - "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.60.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.60.1.tgz", - "integrity": "sha512-mjCpF7GmkRtSJwon+Rq1N8+pI+8l7w5g9Z3vWj4T7abguC4Czwi3Yu/pFaLvA3TTeMVjnu3ctigusqWUfjZzvw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.60.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.60.1.tgz", - "integrity": "sha512-haZ7hJ1JT4e9hqkoT9R/19XW2QKqjfJVv+i5AGg57S+nLk9lQnJ1F/eZloRO3o9Scy9CM3wQ9l+dkXtcBgN5Ew==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@rollup/rollup-freebsd-arm64": { - "version": "4.60.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.60.1.tgz", - "integrity": "sha512-czw90wpQq3ZsAVBlinZjAYTKduOjTywlG7fEeWKUA7oCmpA8xdTkxZZlwNJKWqILlq0wehoZcJYfBvOyhPTQ6w==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ] - }, - "node_modules/@rollup/rollup-freebsd-x64": { - "version": "4.60.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.60.1.tgz", - "integrity": "sha512-KVB2rqsxTHuBtfOeySEyzEOB7ltlB/ux38iu2rBQzkjbwRVlkhAGIEDiiYnO2kFOkJp+Z7pUXKyrRRFuFUKt+g==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ] - }, - "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.60.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.60.1.tgz", - "integrity": "sha512-L+34Qqil+v5uC0zEubW7uByo78WOCIrBvci69E7sFASRl0X7b/MB6Cqd1lky/CtcSVTydWa2WZwFuWexjS5o6g==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.60.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.60.1.tgz", - "integrity": "sha512-n83O8rt4v34hgFzlkb1ycniJh7IR5RCIqt6mz1VRJD6pmhRi0CXdmfnLu9dIUS6buzh60IvACM842Ffb3xd6Gg==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.60.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.60.1.tgz", - "integrity": "sha512-Nql7sTeAzhTAja3QXeAI48+/+GjBJ+QmAH13snn0AJSNL50JsDqotyudHyMbO2RbJkskbMbFJfIJKWA6R1LCJQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.60.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.60.1.tgz", - "integrity": "sha512-+pUymDhd0ys9GcKZPPWlFiZ67sTWV5UU6zOJat02M1+PiuSGDziyRuI/pPue3hoUwm2uGfxdL+trT6Z9rxnlMA==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-loong64-gnu": { - "version": "4.60.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.60.1.tgz", - "integrity": "sha512-VSvgvQeIcsEvY4bKDHEDWcpW4Yw7BtlKG1GUT4FzBUlEKQK0rWHYBqQt6Fm2taXS+1bXvJT6kICu5ZwqKCnvlQ==", - "cpu": [ - "loong64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-loong64-musl": { - "version": "4.60.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.60.1.tgz", - "integrity": "sha512-4LqhUomJqwe641gsPp6xLfhqWMbQV04KtPp7/dIp0nzPxAkNY1AbwL5W0MQpcalLYk07vaW9Kp1PBhdpZYYcEw==", - "cpu": [ - "loong64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-ppc64-gnu": { - "version": "4.60.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.60.1.tgz", - "integrity": "sha512-tLQQ9aPvkBxOc/EUT6j3pyeMD6Hb8QF2BTBnCQWP/uu1lhc9AIrIjKnLYMEroIz/JvtGYgI9dF3AxHZNaEH0rw==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-ppc64-musl": { - "version": "4.60.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.60.1.tgz", - "integrity": "sha512-RMxFhJwc9fSXP6PqmAz4cbv3kAyvD1etJFjTx4ONqFP9DkTkXsAMU4v3Vyc5BgzC+anz7nS/9tp4obsKfqkDHg==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.60.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.60.1.tgz", - "integrity": "sha512-QKgFl+Yc1eEk6MmOBfRHYF6lTxiiiV3/z/BRrbSiW2I7AFTXoBFvdMEyglohPj//2mZS4hDOqeB0H1ACh3sBbg==", - "cpu": [ - "riscv64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-riscv64-musl": { - "version": "4.60.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.60.1.tgz", - "integrity": "sha512-RAjXjP/8c6ZtzatZcA1RaQr6O1TRhzC+adn8YZDnChliZHviqIjmvFwHcxi4JKPSDAt6Uhf/7vqcBzQJy0PDJg==", - "cpu": [ - "riscv64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.60.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.60.1.tgz", - "integrity": "sha512-wcuocpaOlaL1COBYiA89O6yfjlp3RwKDeTIA0hM7OpmhR1Bjo9j31G1uQVpDlTvwxGn2nQs65fBFL5UFd76FcQ==", - "cpu": [ - "s390x" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.60.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.60.1.tgz", - "integrity": "sha512-77PpsFQUCOiZR9+LQEFg9GClyfkNXj1MP6wRnzYs0EeWbPcHs02AXu4xuUbM1zhwn3wqaizle3AEYg5aeoohhg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.60.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.60.1.tgz", - "integrity": "sha512-5cIATbk5vynAjqqmyBjlciMJl1+R/CwX9oLk/EyiFXDWd95KpHdrOJT//rnUl4cUcskrd0jCCw3wpZnhIHdD9w==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-openbsd-x64": { - "version": "4.60.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.60.1.tgz", - "integrity": "sha512-cl0w09WsCi17mcmWqqglez9Gk8isgeWvoUZ3WiJFYSR3zjBQc2J5/ihSjpl+VLjPqjQ/1hJRcqBfLjssREQILw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ] - }, - "node_modules/@rollup/rollup-openharmony-arm64": { - "version": "4.60.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.60.1.tgz", - "integrity": "sha512-4Cv23ZrONRbNtbZa37mLSueXUCtN7MXccChtKpUnQNgF010rjrjfHx3QxkS2PI7LqGT5xXyYs1a7LbzAwT0iCA==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openharmony" - ] - }, - "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.60.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.60.1.tgz", - "integrity": "sha512-i1okWYkA4FJICtr7KpYzFpRTHgy5jdDbZiWfvny21iIKky5YExiDXP+zbXzm3dUcFpkEeYNHgQ5fuG236JPq0g==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.60.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.60.1.tgz", - "integrity": "sha512-u09m3CuwLzShA0EYKMNiFgcjjzwqtUMLmuCJLeZWjjOYA3IT2Di09KaxGBTP9xVztWyIWjVdsB2E9goMjZvTQg==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@rollup/rollup-win32-x64-gnu": { - "version": "4.60.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.60.1.tgz", - "integrity": "sha512-k+600V9Zl1CM7eZxJgMyTUzmrmhB/0XZnF4pRypKAlAgxmedUA+1v9R+XOFv56W4SlHEzfeMtzujLJD22Uz5zg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] + "peerDependenciesMeta": { + "rollup": { + "optional": true + } + } }, - "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.60.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.60.1.tgz", - "integrity": "sha512-lWMnixq/QzxyhTV6NjQJ4SFo1J6PvOX8vUx5Wb4bBPsEb+8xZ89Bz6kOXpfXj9ak9AHTQVQzlgzBEc1SyM27xQ==", + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.59.0", "cpu": [ - "x64" + "arm64" ], "dev": true, "license": "MIT", "optional": true, "os": [ - "win32" + "darwin" ] }, "node_modules/@selderee/plugin-htmlparser2": { "version": "0.11.0", - "resolved": "https://registry.npmjs.org/@selderee/plugin-htmlparser2/-/plugin-htmlparser2-0.11.0.tgz", - "integrity": "sha512-P33hHGdldxGabLFjPPpaTxVolMrzrcegejx+0GxjrIb9Zv48D8yAIA/QTDR2dFl7Uz7urX8aX6+5bCZslr+gWQ==", "license": "MIT", "dependencies": { "domhandler": "^5.0.3", @@ -4958,25 +4014,19 @@ }, "node_modules/@standard-schema/spec": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz", - "integrity": "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==", "dev": true, "license": "MIT" }, "node_modules/@standard-schema/utils": { "version": "0.3.0", - "resolved": "https://registry.npmjs.org/@standard-schema/utils/-/utils-0.3.0.tgz", - "integrity": "sha512-e7Mew686owMaPJVNNLs55PUvgz371nKgwsc4vxE49zsODpJEnxgxRo2y/OKrqueavXgZNMDVj3DdHFlaSAeU8g==", "license": "MIT" }, "node_modules/@storybook/builder-vite": { - "version": "10.3.4", - "resolved": "https://registry.npmjs.org/@storybook/builder-vite/-/builder-vite-10.3.4.tgz", - "integrity": "sha512-dNQyBZpBKvwmhSTpjrsuxxY8FqFCh0hgu5+46h2WbgQ2Te3pO458heWkGb+QO7mC6FmkXO6j6zgYzXticD6F2A==", + "version": "10.2.14", "dev": true, "license": "MIT", "dependencies": { - "@storybook/csf-plugin": "10.3.4", + "@storybook/csf-plugin": "10.2.14", "ts-dedent": "^2.0.0" }, "funding": { @@ -4984,14 +4034,12 @@ "url": "https://opencollective.com/storybook" }, "peerDependencies": { - "storybook": "^10.3.4", - "vite": "^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0" + "storybook": "^10.2.14", + "vite": "^5.0.0 || ^6.0.0 || ^7.0.0" } }, "node_modules/@storybook/csf-plugin": { - "version": "10.3.4", - "resolved": "https://registry.npmjs.org/@storybook/csf-plugin/-/csf-plugin-10.3.4.tgz", - "integrity": "sha512-WPP0Z39o82WiohPkhPOs6z+9yJ+bVvqPz4d+QUPfE6FMvOOBLojlwOcGx6Xmclyn5H/CKwywFrjuz4mBO/nHhA==", + "version": "10.2.14", "dev": true, "license": "MIT", "dependencies": { @@ -5004,7 +4052,7 @@ "peerDependencies": { "esbuild": "*", "rollup": "*", - "storybook": "^10.3.4", + "storybook": "^10.2.14", "vite": "*", "webpack": "*" }, @@ -5025,15 +4073,11 @@ }, "node_modules/@storybook/global": { "version": "5.0.0", - "resolved": "https://registry.npmjs.org/@storybook/global/-/global-5.0.0.tgz", - "integrity": "sha512-FcOqPAXACP0I3oJ/ws6/rrPT9WGhu915Cg8D02a9YxLo0DE9zI+a9A5gRGvmQ09fiWPukqI8ZAEoQEdWUKMQdQ==", "dev": true, "license": "MIT" }, "node_modules/@storybook/icons": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@storybook/icons/-/icons-2.0.1.tgz", - "integrity": "sha512-/smVjw88yK3CKsiuR71vNgWQ9+NuY2L+e8X7IMrFjexjm6ZR8ULrV2DRkTA61aV6ryefslzHEGDInGpnNeIocg==", "dev": true, "license": "MIT", "peerDependencies": { @@ -5042,16 +4086,13 @@ } }, "node_modules/@storybook/react": { - "version": "10.3.4", - "resolved": "https://registry.npmjs.org/@storybook/react/-/react-10.3.4.tgz", - "integrity": "sha512-I5ifYqjrqyuhOFjalpy47kMKMXX7QU/qmHj0h/547s9Bg6sEU7xRhJnneXx1RJsEJTySjC4SmGfEU+FJz4Foiw==", + "version": "10.2.14", "dev": true, "license": "MIT", "dependencies": { "@storybook/global": "^5.0.0", - "@storybook/react-dom-shim": "10.3.4", - "react-docgen": "^8.0.2", - "react-docgen-typescript": "^2.2.2" + "@storybook/react-dom-shim": "10.2.14", + "react-docgen": "^8.0.2" }, "funding": { "type": "opencollective", @@ -5060,7 +4101,7 @@ "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", - "storybook": "^10.3.4", + "storybook": "^10.2.14", "typescript": ">= 4.9.x" }, "peerDependenciesMeta": { @@ -5070,9 +4111,7 @@ } }, "node_modules/@storybook/react-dom-shim": { - "version": "10.3.4", - "resolved": "https://registry.npmjs.org/@storybook/react-dom-shim/-/react-dom-shim-10.3.4.tgz", - "integrity": "sha512-VIm9YzreGubnOtQOZ6iqEfj6KncHvAkrCR/IilqnJq7DidPWuykrFszyajTASRMiY+p+TElOW+O1PGpv55qNGw==", + "version": "10.2.14", "dev": true, "license": "MIT", "funding": { @@ -5082,20 +4121,18 @@ "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", - "storybook": "^10.3.4" + "storybook": "^10.2.14" } }, "node_modules/@storybook/react-vite": { - "version": "10.3.4", - "resolved": "https://registry.npmjs.org/@storybook/react-vite/-/react-vite-10.3.4.tgz", - "integrity": "sha512-xaMt7NdvlAb+CwXn5TOiluQ+0WkkMN3mZhCThocpblWGoyfmHH7bgQ5ZwzT+IIp8DGOsAi/HkNmSyS7Z8HRLJg==", + "version": "10.2.14", "dev": true, "license": "MIT", "dependencies": { - "@joshwooding/vite-plugin-react-docgen-typescript": "^0.7.0", + "@joshwooding/vite-plugin-react-docgen-typescript": "^0.6.4", "@rollup/pluginutils": "^5.0.2", - "@storybook/builder-vite": "10.3.4", - "@storybook/react": "10.3.4", + "@storybook/builder-vite": "10.2.14", + "@storybook/react": "10.2.14", "empathic": "^2.0.0", "magic-string": "^0.30.0", "react-docgen": "^8.0.0", @@ -5109,14 +4146,12 @@ "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", - "storybook": "^10.3.4", - "vite": "^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0" + "storybook": "^10.2.14", + "vite": "^5.0.0 || ^6.0.0 || ^7.0.0" } }, "node_modules/@storybook/react-vite/node_modules/doctrine": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", - "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -5127,9 +4162,7 @@ } }, "node_modules/@storybook/react-vite/node_modules/react-docgen": { - "version": "8.0.3", - "resolved": "https://registry.npmjs.org/react-docgen/-/react-docgen-8.0.3.tgz", - "integrity": "sha512-aEZ9qP+/M+58x2qgfSFEWH1BxLyHe5+qkLNJOZQb5iGS017jpbRnoKhNRrXPeA6RfBrZO5wZrT9DMC1UqE1f1w==", + "version": "8.0.2", "dev": true, "license": "MIT", "dependencies": { @@ -5150,8 +4183,6 @@ }, "node_modules/@storybook/react/node_modules/doctrine": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", - "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -5162,9 +4193,7 @@ } }, "node_modules/@storybook/react/node_modules/react-docgen": { - "version": "8.0.3", - "resolved": "https://registry.npmjs.org/react-docgen/-/react-docgen-8.0.3.tgz", - "integrity": "sha512-aEZ9qP+/M+58x2qgfSFEWH1BxLyHe5+qkLNJOZQb5iGS017jpbRnoKhNRrXPeA6RfBrZO5wZrT9DMC1UqE1f1w==", + "version": "8.0.2", "dev": true, "license": "MIT", "dependencies": { @@ -5184,9 +4213,7 @@ } }, "node_modules/@stylistic/eslint-plugin": { - "version": "5.10.0", - "resolved": "https://registry.npmjs.org/@stylistic/eslint-plugin/-/eslint-plugin-5.10.0.tgz", - "integrity": "sha512-nPK52ZHvot8Ju/0A4ucSX1dcPV2/1clx0kLcH5wDmrE4naKso7TUC/voUyU1O9OTKTrR6MYip6LP0ogEMQ9jPQ==", + "version": "5.9.0", "dev": true, "license": "MIT", "dependencies": { @@ -5205,66 +4232,43 @@ } }, "node_modules/@tailwindcss/node": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.2.2.tgz", - "integrity": "sha512-pXS+wJ2gZpVXqFaUEjojq7jzMpTGf8rU6ipJz5ovJV6PUGmlJ+jvIwGrzdHdQ80Sg+wmQxUFuoW1UAAwHNEdFA==", + "version": "4.2.1", "dev": true, "license": "MIT", "dependencies": { "@jridgewell/remapping": "^2.3.5", "enhanced-resolve": "^5.19.0", "jiti": "^2.6.1", - "lightningcss": "1.32.0", + "lightningcss": "1.31.1", "magic-string": "^0.30.21", "source-map-js": "^1.2.1", - "tailwindcss": "4.2.2" + "tailwindcss": "4.2.1" } }, "node_modules/@tailwindcss/oxide": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.2.2.tgz", - "integrity": "sha512-qEUA07+E5kehxYp9BVMpq9E8vnJuBHfJEC0vPC5e7iL/hw7HR61aDKoVoKzrG+QKp56vhNZe4qwkRmMC0zDLvg==", + "version": "4.2.1", "dev": true, "license": "MIT", "engines": { "node": ">= 20" }, "optionalDependencies": { - "@tailwindcss/oxide-android-arm64": "4.2.2", - "@tailwindcss/oxide-darwin-arm64": "4.2.2", - "@tailwindcss/oxide-darwin-x64": "4.2.2", - "@tailwindcss/oxide-freebsd-x64": "4.2.2", - "@tailwindcss/oxide-linux-arm-gnueabihf": "4.2.2", - "@tailwindcss/oxide-linux-arm64-gnu": "4.2.2", - "@tailwindcss/oxide-linux-arm64-musl": "4.2.2", - "@tailwindcss/oxide-linux-x64-gnu": "4.2.2", - "@tailwindcss/oxide-linux-x64-musl": "4.2.2", - "@tailwindcss/oxide-wasm32-wasi": "4.2.2", - "@tailwindcss/oxide-win32-arm64-msvc": "4.2.2", - "@tailwindcss/oxide-win32-x64-msvc": "4.2.2" - } - }, - "node_modules/@tailwindcss/oxide-android-arm64": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.2.2.tgz", - "integrity": "sha512-dXGR1n+P3B6748jZO/SvHZq7qBOqqzQ+yFrXpoOWWALWndF9MoSKAT3Q0fYgAzYzGhxNYOoysRvYlpixRBBoDg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">= 20" + "@tailwindcss/oxide-android-arm64": "4.2.1", + "@tailwindcss/oxide-darwin-arm64": "4.2.1", + "@tailwindcss/oxide-darwin-x64": "4.2.1", + "@tailwindcss/oxide-freebsd-x64": "4.2.1", + "@tailwindcss/oxide-linux-arm-gnueabihf": "4.2.1", + "@tailwindcss/oxide-linux-arm64-gnu": "4.2.1", + "@tailwindcss/oxide-linux-arm64-musl": "4.2.1", + "@tailwindcss/oxide-linux-x64-gnu": "4.2.1", + "@tailwindcss/oxide-linux-x64-musl": "4.2.1", + "@tailwindcss/oxide-wasm32-wasi": "4.2.1", + "@tailwindcss/oxide-win32-arm64-msvc": "4.2.1", + "@tailwindcss/oxide-win32-x64-msvc": "4.2.1" } }, "node_modules/@tailwindcss/oxide-darwin-arm64": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.2.2.tgz", - "integrity": "sha512-iq9Qjr6knfMpZHj55/37ouZeykwbDqF21gPFtfnhCCKGDcPI/21FKC9XdMO/XyBM7qKORx6UIhGgg6jLl7BZlg==", + "version": "4.2.1", "cpu": [ "arm64" ], @@ -5278,193 +4282,8 @@ "node": ">= 20" } }, - "node_modules/@tailwindcss/oxide-darwin-x64": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.2.2.tgz", - "integrity": "sha512-BlR+2c3nzc8f2G639LpL89YY4bdcIdUmiOOkv2GQv4/4M0vJlpXEa0JXNHhCHU7VWOKWT/CjqHdTP8aUuDJkuw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 20" - } - }, - "node_modules/@tailwindcss/oxide-freebsd-x64": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.2.2.tgz", - "integrity": "sha512-YUqUgrGMSu2CDO82hzlQ5qSb5xmx3RUrke/QgnoEx7KvmRJHQuZHZmZTLSuuHwFf0DJPybFMXMYf+WJdxHy/nQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">= 20" - } - }, - "node_modules/@tailwindcss/oxide-linux-arm-gnueabihf": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.2.2.tgz", - "integrity": "sha512-FPdhvsW6g06T9BWT0qTwiVZYE2WIFo2dY5aCSpjG/S/u1tby+wXoslXS0kl3/KXnULlLr1E3NPRRw0g7t2kgaQ==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 20" - } - }, - "node_modules/@tailwindcss/oxide-linux-arm64-gnu": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.2.2.tgz", - "integrity": "sha512-4og1V+ftEPXGttOO7eCmW7VICmzzJWgMx+QXAJRAhjrSjumCwWqMfkDrNu1LXEQzNAwz28NCUpucgQPrR4S2yw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 20" - } - }, - "node_modules/@tailwindcss/oxide-linux-arm64-musl": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.2.2.tgz", - "integrity": "sha512-oCfG/mS+/+XRlwNjnsNLVwnMWYH7tn/kYPsNPh+JSOMlnt93mYNCKHYzylRhI51X+TbR+ufNhhKKzm6QkqX8ag==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 20" - } - }, - "node_modules/@tailwindcss/oxide-linux-x64-gnu": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.2.2.tgz", - "integrity": "sha512-rTAGAkDgqbXHNp/xW0iugLVmX62wOp2PoE39BTCGKjv3Iocf6AFbRP/wZT/kuCxC9QBh9Pu8XPkv/zCZB2mcMg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 20" - } - }, - "node_modules/@tailwindcss/oxide-linux-x64-musl": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.2.2.tgz", - "integrity": "sha512-XW3t3qwbIwiSyRCggeO2zxe3KWaEbM0/kW9e8+0XpBgyKU4ATYzcVSMKteZJ1iukJ3HgHBjbg9P5YPRCVUxlnQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 20" - } - }, - "node_modules/@tailwindcss/oxide-wasm32-wasi": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.2.2.tgz", - "integrity": "sha512-eKSztKsmEsn1O5lJ4ZAfyn41NfG7vzCg496YiGtMDV86jz1q/irhms5O0VrY6ZwTUkFy/EKG3RfWgxSI3VbZ8Q==", - "bundleDependencies": [ - "@napi-rs/wasm-runtime", - "@emnapi/core", - "@emnapi/runtime", - "@tybys/wasm-util", - "@emnapi/wasi-threads", - "tslib" - ], - "cpu": [ - "wasm32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "@emnapi/core": "^1.8.1", - "@emnapi/runtime": "^1.8.1", - "@emnapi/wasi-threads": "^1.1.0", - "@napi-rs/wasm-runtime": "^1.1.1", - "@tybys/wasm-util": "^0.10.1", - "tslib": "^2.8.1" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@tailwindcss/oxide-win32-arm64-msvc": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.2.2.tgz", - "integrity": "sha512-qPmaQM4iKu5mxpsrWZMOZRgZv1tOZpUm+zdhhQP0VhJfyGGO3aUKdbh3gDZc/dPLQwW4eSqWGrrcWNBZWUWaXQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 20" - } - }, - "node_modules/@tailwindcss/oxide-win32-x64-msvc": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.2.2.tgz", - "integrity": "sha512-1T/37VvI7WyH66b+vqHj/cLwnCxt7Qt3WFu5Q8hk65aOvlwAhs7rAp1VkulBJw/N4tMirXjVnylTR72uI0HGcA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 20" - } - }, "node_modules/@tailwindcss/typography": { "version": "0.5.19", - "resolved": "https://registry.npmjs.org/@tailwindcss/typography/-/typography-0.5.19.tgz", - "integrity": "sha512-w31dd8HOx3k9vPtcQh5QHP9GwKcgbMp87j58qi6xgiBnFFtKEAgCWnDw4qUT8aHwkCp8bKvb/KGKWWHedP0AAg==", "dev": true, "license": "MIT", "dependencies": { @@ -5475,24 +4294,20 @@ } }, "node_modules/@tailwindcss/vite": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/@tailwindcss/vite/-/vite-4.2.2.tgz", - "integrity": "sha512-mEiF5HO1QqCLXoNEfXVA1Tzo+cYsrqV7w9Juj2wdUFyW07JRenqMG225MvPwr3ZD9N1bFQj46X7r33iHxLUW0w==", + "version": "4.2.1", "dev": true, "license": "MIT", "dependencies": { - "@tailwindcss/node": "4.2.2", - "@tailwindcss/oxide": "4.2.2", - "tailwindcss": "4.2.2" + "@tailwindcss/node": "4.2.1", + "@tailwindcss/oxide": "4.2.1", + "tailwindcss": "4.2.1" }, "peerDependencies": { - "vite": "^5.2.0 || ^6 || ^7 || ^8" + "vite": "^5.2.0 || ^6 || ^7" } }, "node_modules/@tanstack/query-core": { - "version": "5.96.1", - "resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.96.1.tgz", - "integrity": "sha512-u1yBgtavSy+N8wgtW3PiER6UpxcplMje65yXnnVgiHTqiMwLlxiw4WvQDrXyn+UD6lnn8kHaxmerJUzQcV/MMg==", + "version": "5.90.20", "license": "MIT", "funding": { "type": "github", @@ -5500,12 +4315,10 @@ } }, "node_modules/@tanstack/react-query": { - "version": "5.96.1", - "resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.96.1.tgz", - "integrity": "sha512-2X7KYK5KKWUKGeWCVcqxXAkYefJtrKB7tSKWgeG++b0H6BRHxQaLSSi8AxcgjmUnnosHuh9WsFZqvE16P1WCzA==", + "version": "5.90.21", "license": "MIT", "dependencies": { - "@tanstack/query-core": "5.96.1" + "@tanstack/query-core": "5.90.20" }, "funding": { "type": "github", @@ -5517,8 +4330,6 @@ }, "node_modules/@testing-library/dom": { "version": "10.4.1", - "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-10.4.1.tgz", - "integrity": "sha512-o4PXJQidqJl82ckFaXUeoAW+XysPLauYI43Abki5hABd853iMhitooc6znOnczgbTYmEP6U6/y1ZyKAIsvMKGg==", "dev": true, "license": "MIT", "peer": true, @@ -5538,16 +4349,12 @@ }, "node_modules/@testing-library/dom/node_modules/picocolors": { "version": "1.1.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", - "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", "dev": true, "license": "ISC", "peer": true }, "node_modules/@testing-library/jest-dom": { "version": "6.9.1", - "resolved": "https://registry.npmjs.org/@testing-library/jest-dom/-/jest-dom-6.9.1.tgz", - "integrity": "sha512-zIcONa+hVtVSSep9UT3jZ5rizo2BsxgyDYU7WFD5eICBE7no3881HGeb/QkGfsJs6JTkY1aQhT7rIPC7e+0nnA==", "dev": true, "license": "MIT", "dependencies": { @@ -5566,22 +4373,16 @@ }, "node_modules/@testing-library/jest-dom/node_modules/dom-accessibility-api": { "version": "0.6.3", - "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.6.3.tgz", - "integrity": "sha512-7ZgogeTnjuHbo+ct10G9Ffp0mif17idi0IyWNVA/wcwcm7NPOD/WEHVP3n7n3MhXqxoIYm8d6MuZohYWIZ4T3w==", "dev": true, "license": "MIT" }, "node_modules/@testing-library/jest-dom/node_modules/picocolors": { "version": "1.1.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", - "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", "dev": true, "license": "ISC" }, "node_modules/@testing-library/user-event": { "version": "14.6.1", - "resolved": "https://registry.npmjs.org/@testing-library/user-event/-/user-event-14.6.1.tgz", - "integrity": "sha512-vq7fv0rnt+QTXgPxr5Hjc210p6YKq2kmdziLgnsZGgLJ9e6VAShx1pACLuRjd/AS/sr7phAR58OIIpf0LlmQNw==", "dev": true, "license": "MIT", "engines": { @@ -5593,48 +4394,48 @@ } }, "node_modules/@tiptap/core": { - "version": "3.22.1", - "resolved": "https://registry.npmjs.org/@tiptap/core/-/core-3.22.1.tgz", - "integrity": "sha512-6wPNhkdLIGYiKAGqepDCRtR0TYGJxV40SwOEN2vlPhsXqAgzmyG37UyREj5pGH5xTekugqMCgCnyRg7m5nYoYQ==", + "version": "3.22.3", + "resolved": "https://registry.npmjs.org/@tiptap/core/-/core-3.22.3.tgz", + "integrity": "sha512-Dv9MKK5BDWCF0N2l6/Pxv3JNCce2kwuWf2cKMBc2bEetx0Pn6o7zlFmSxMvYK4UtG1Tw9Yg/ZHi6QOFWK0Zm9Q==", "license": "MIT", "funding": { "type": "github", "url": "https://github.com/sponsors/ueberdosis" }, "peerDependencies": { - "@tiptap/pm": "^3.22.1" + "@tiptap/pm": "^3.22.3" } }, "node_modules/@tiptap/extension-blockquote": { - "version": "3.22.1", - "resolved": "https://registry.npmjs.org/@tiptap/extension-blockquote/-/extension-blockquote-3.22.1.tgz", - "integrity": "sha512-omPsJ/IMAZYhXqOaEenYE+HA9U2zju5rQbAn6Xktynvr4A5P95jqkgAwncXB82pCkNYU/uYxi51vyTweTeEUHA==", + "version": "3.22.3", + "resolved": "https://registry.npmjs.org/@tiptap/extension-blockquote/-/extension-blockquote-3.22.3.tgz", + "integrity": "sha512-IaUx3zh7yLHXzIXKL+fw/jzFhsIImdhJyw0lMhe8FfYrefFqXJFYW/sey6+L/e8B3AWvTksPA6VBwefzbH77JA==", "license": "MIT", "funding": { "type": "github", "url": "https://github.com/sponsors/ueberdosis" }, "peerDependencies": { - "@tiptap/core": "^3.22.1" + "@tiptap/core": "^3.22.3" } }, "node_modules/@tiptap/extension-bold": { - "version": "3.22.1", - "resolved": "https://registry.npmjs.org/@tiptap/extension-bold/-/extension-bold-3.22.1.tgz", - "integrity": "sha512-0+q6Apu1Vx2+ReB2ktTpBrQ5/dCvGzTkJCy+MZ/t8WBcybqFXOKYRCr/i/VGPDpXZttxpk0EPl0+ao+NVcUTAA==", + "version": "3.22.3", + "resolved": "https://registry.npmjs.org/@tiptap/extension-bold/-/extension-bold-3.22.3.tgz", + "integrity": "sha512-tysipHla2zCWr8XNIWRaW9O+7i7/SoEqnRqSRUUi2ailcJjlia+RBy3RykhkgyThrQDStu5KGBS/UvrXwA+O1A==", "license": "MIT", "funding": { "type": "github", "url": "https://github.com/sponsors/ueberdosis" }, "peerDependencies": { - "@tiptap/core": "^3.22.1" + "@tiptap/core": "^3.22.3" } }, "node_modules/@tiptap/extension-bubble-menu": { - "version": "3.22.1", - "resolved": "https://registry.npmjs.org/@tiptap/extension-bubble-menu/-/extension-bubble-menu-3.22.1.tgz", - "integrity": "sha512-JJI63N55hLPjfqHgBnbG1ORZTXJiswnfBkfNd8YKytCC8D++g5qX3UMObxmJKLMBRGyqjEi6krzOyYtOix5ALA==", + "version": "3.22.3", + "resolved": "https://registry.npmjs.org/@tiptap/extension-bubble-menu/-/extension-bubble-menu-3.22.3.tgz", + "integrity": "sha512-Y6zQjh0ypDg32HWgICEvmPSKjGLr39k3aDxxt/H0uQEZSfw4smT0hxUyyyjVjx68C6t6MTnwdfz0hPI5lL68vQ==", "license": "MIT", "optional": true, "dependencies": { @@ -5645,80 +4446,80 @@ "url": "https://github.com/sponsors/ueberdosis" }, "peerDependencies": { - "@tiptap/core": "^3.22.1", - "@tiptap/pm": "^3.22.1" + "@tiptap/core": "^3.22.3", + "@tiptap/pm": "^3.22.3" } }, "node_modules/@tiptap/extension-bullet-list": { - "version": "3.22.1", - "resolved": "https://registry.npmjs.org/@tiptap/extension-bullet-list/-/extension-bullet-list-3.22.1.tgz", - "integrity": "sha512-83L+4N2JziWORbWtlsM0xBm3LOKIw4YtIm+Kh4amV5kGvIgIL5I1KYzoxv20qjgFX2k08LtLMwPdvPSPSh4e7g==", + "version": "3.22.3", + "resolved": "https://registry.npmjs.org/@tiptap/extension-bullet-list/-/extension-bullet-list-3.22.3.tgz", + "integrity": "sha512-xOmW/b1hgECIE6r3IeZvKn4VVlG3+dfTjCWE6lnnyLaqdNkNhKS1CwUmDZdYNLUS2ryIUtgz5ID1W/8A3PhbiA==", "license": "MIT", "funding": { "type": "github", "url": "https://github.com/sponsors/ueberdosis" }, "peerDependencies": { - "@tiptap/extension-list": "^3.22.1" + "@tiptap/extension-list": "^3.22.3" } }, "node_modules/@tiptap/extension-code": { - "version": "3.22.1", - "resolved": "https://registry.npmjs.org/@tiptap/extension-code/-/extension-code-3.22.1.tgz", - "integrity": "sha512-Ze+hjSLLCn+5gVpuE/Uv7mQ83AlG5A9OPsuDoyzTpJ2XNvZP2iZdwQMGqwXKC8eH7fIOJN6XQ3IDv/EhltQx/Q==", + "version": "3.22.3", + "resolved": "https://registry.npmjs.org/@tiptap/extension-code/-/extension-code-3.22.3.tgz", + "integrity": "sha512-wafWTDQOuMKtXpZEuk1PFQmzopabBciNLryL90MB9S03MNLaQQZYLnmYkDBlzAaLAbgF5QiC+2XZQEBQuTVjFQ==", "license": "MIT", "funding": { "type": "github", "url": "https://github.com/sponsors/ueberdosis" }, "peerDependencies": { - "@tiptap/core": "^3.22.1" + "@tiptap/core": "^3.22.3" } }, "node_modules/@tiptap/extension-code-block": { - "version": "3.22.1", - "resolved": "https://registry.npmjs.org/@tiptap/extension-code-block/-/extension-code-block-3.22.1.tgz", - "integrity": "sha512-fr3b1seFsAeYHtPAb9fbATkGcgyfStD05GHsZXFLh7yCpf2ejWLNxdWJT/g+FggSEHYFKCXT06aixk0WbtRcWw==", + "version": "3.22.3", + "resolved": "https://registry.npmjs.org/@tiptap/extension-code-block/-/extension-code-block-3.22.3.tgz", + "integrity": "sha512-RiQtEjDAPrHpdo6sw6b7fOw/PijqgFIsozKKkGcSeBgWHQuFg7q9OxJTj+l0e60rVwSu/5gmKEEobzM9bX+t2Q==", "license": "MIT", "funding": { "type": "github", "url": "https://github.com/sponsors/ueberdosis" }, "peerDependencies": { - "@tiptap/core": "^3.22.1", - "@tiptap/pm": "^3.22.1" + "@tiptap/core": "^3.22.3", + "@tiptap/pm": "^3.22.3" } }, "node_modules/@tiptap/extension-document": { - "version": "3.22.1", - "resolved": "https://registry.npmjs.org/@tiptap/extension-document/-/extension-document-3.22.1.tgz", - "integrity": "sha512-fBI/+PGtK6pzitqjSSSYL2+uZglX6T53zb5nLEmN/q8q7FzUuUpglp8toHVhBG05WDk4vx6Z7bC95uyxkYdoAA==", + "version": "3.22.3", + "resolved": "https://registry.npmjs.org/@tiptap/extension-document/-/extension-document-3.22.3.tgz", + "integrity": "sha512-MCSr1PFPtTd++lA3H1RNgqAczAE59XXJ5wUFIQf2F+/0DPY5q2SU4g5QsNJVxPPft5mrNT4C6ty8xBPrALFEdA==", "license": "MIT", "funding": { "type": "github", "url": "https://github.com/sponsors/ueberdosis" }, "peerDependencies": { - "@tiptap/core": "^3.22.1" + "@tiptap/core": "^3.22.3" } }, "node_modules/@tiptap/extension-dropcursor": { - "version": "3.22.1", - "resolved": "https://registry.npmjs.org/@tiptap/extension-dropcursor/-/extension-dropcursor-3.22.1.tgz", - "integrity": "sha512-PuSNoTROZB564KpTG9ExVB3CsfRa0ridHx+1sWZajOBVZJiXSn4QlS/ShS509SOx8z17DyxEw06IH//OHY9XyQ==", + "version": "3.22.3", + "resolved": "https://registry.npmjs.org/@tiptap/extension-dropcursor/-/extension-dropcursor-3.22.3.tgz", + "integrity": "sha512-taXq9Tl5aybdFbptJtFRHX9LFJzbXphAbPp4/vutFyTrBu5meXDxuS+B9pEmE+Or0XcolTlW2nDZB0Tqnr18JQ==", "license": "MIT", "funding": { "type": "github", "url": "https://github.com/sponsors/ueberdosis" }, "peerDependencies": { - "@tiptap/extensions": "^3.22.1" + "@tiptap/extensions": "^3.22.3" } }, "node_modules/@tiptap/extension-floating-menu": { - "version": "3.22.1", - "resolved": "https://registry.npmjs.org/@tiptap/extension-floating-menu/-/extension-floating-menu-3.22.1.tgz", - "integrity": "sha512-TaZqmaoKv36FzbKTrBkkv74o0t8dYTftNZ7NotBqfSki0BB2PupTCJHafdu1YI0zmJ3xEzjB/XKcKPz2+10sDA==", + "version": "3.22.3", + "resolved": "https://registry.npmjs.org/@tiptap/extension-floating-menu/-/extension-floating-menu-3.22.3.tgz", + "integrity": "sha512-0f8b4KZ3XKai8GXWseIYJGdOfQr3evtFbBo3U08zy2aYzMMXWG0zEF7qe5/oiYp2aZ95edjjITnEceviTsZkIg==", "license": "MIT", "optional": true, "funding": { @@ -5727,80 +4528,80 @@ }, "peerDependencies": { "@floating-ui/dom": "^1.0.0", - "@tiptap/core": "^3.22.1", - "@tiptap/pm": "^3.22.1" + "@tiptap/core": "^3.22.3", + "@tiptap/pm": "^3.22.3" } }, "node_modules/@tiptap/extension-gapcursor": { - "version": "3.22.1", - "resolved": "https://registry.npmjs.org/@tiptap/extension-gapcursor/-/extension-gapcursor-3.22.1.tgz", - "integrity": "sha512-qqsyy7unWM3elv+7ru+6paKAnw1PZTvjNVQu3UzB6d556Gx2uE4isXJNdBaslBZdp2EoaYdIkhhEccW9B/Nwqg==", + "version": "3.22.3", + "resolved": "https://registry.npmjs.org/@tiptap/extension-gapcursor/-/extension-gapcursor-3.22.3.tgz", + "integrity": "sha512-L/Px4UeQEVG/D9WIlcAOIej+4wyIBCMUSYicSR+hW68UsObe4rxVbUas1QgidQKm6DOhoT7U7D4KQHA/Gdg/7A==", "license": "MIT", "funding": { "type": "github", "url": "https://github.com/sponsors/ueberdosis" }, "peerDependencies": { - "@tiptap/extensions": "^3.22.1" + "@tiptap/extensions": "^3.22.3" } }, "node_modules/@tiptap/extension-hard-break": { - "version": "3.22.1", - "resolved": "https://registry.npmjs.org/@tiptap/extension-hard-break/-/extension-hard-break-3.22.1.tgz", - "integrity": "sha512-hzLwLEZVbZODa9q5UiCQpOUmDnyxN19FA4LhlqLP0/JSHewP/aol5igFZwuw0XVFp425BuzPjrB7tmr0GRTDWw==", + "version": "3.22.3", + "resolved": "https://registry.npmjs.org/@tiptap/extension-hard-break/-/extension-hard-break-3.22.3.tgz", + "integrity": "sha512-J0v8I99y9tbvVmgKYKzKP/JYNsWaZYS7avn4rzLft2OhnyTfwt3OoY8DtpHmmi6apSUaCtoWHWta/TmoEfK1nQ==", "license": "MIT", "funding": { "type": "github", "url": "https://github.com/sponsors/ueberdosis" }, "peerDependencies": { - "@tiptap/core": "^3.22.1" + "@tiptap/core": "^3.22.3" } }, "node_modules/@tiptap/extension-heading": { - "version": "3.22.1", - "resolved": "https://registry.npmjs.org/@tiptap/extension-heading/-/extension-heading-3.22.1.tgz", - "integrity": "sha512-EaIihzrOfXUHQlL6fFyJCkDrjgg0e/eD4jpkjhKpeuJDcqf7eJ1c0E2zcNRAiZkeXdN/hTQFaXKsSyNUE7T7Sg==", + "version": "3.22.3", + "resolved": "https://registry.npmjs.org/@tiptap/extension-heading/-/extension-heading-3.22.3.tgz", + "integrity": "sha512-XBHuhiEV2EEhZHpOLcplLqAmBIhJciU3I6AtwmqeEqDC0P114uMEfAO7JGlbBZdCYotNer26PKnu44TBTeNtkw==", "license": "MIT", "funding": { "type": "github", "url": "https://github.com/sponsors/ueberdosis" }, "peerDependencies": { - "@tiptap/core": "^3.22.1" + "@tiptap/core": "^3.22.3" } }, "node_modules/@tiptap/extension-horizontal-rule": { - "version": "3.22.1", - "resolved": "https://registry.npmjs.org/@tiptap/extension-horizontal-rule/-/extension-horizontal-rule-3.22.1.tgz", - "integrity": "sha512-Q18A8IN+gnfptIksPeVAI6oOBGYKAGf+QN0FEJ5OXO4BEAmA3hflflA1rWNfPC4aQNry/N7sAl8Gpd6HuIbz2w==", + "version": "3.22.3", + "resolved": "https://registry.npmjs.org/@tiptap/extension-horizontal-rule/-/extension-horizontal-rule-3.22.3.tgz", + "integrity": "sha512-wI2bFzScs+KgWeBH/BtypcVKeYelCyqV0RG8nxsZMWtPrBhqixzNd0Oi3gEKtjSjKUqMQ/kjJAIRuESr5UzlHA==", "license": "MIT", "funding": { "type": "github", "url": "https://github.com/sponsors/ueberdosis" }, "peerDependencies": { - "@tiptap/core": "^3.22.1", - "@tiptap/pm": "^3.22.1" + "@tiptap/core": "^3.22.3", + "@tiptap/pm": "^3.22.3" } }, "node_modules/@tiptap/extension-italic": { - "version": "3.22.1", - "resolved": "https://registry.npmjs.org/@tiptap/extension-italic/-/extension-italic-3.22.1.tgz", - "integrity": "sha512-EXPZWEsWJK9tUMypddOBvayaBeu8wFV2uH5PNrtDKrfRZ1Bf8GQ3lfcO0blHssaQ9nWqa9HwBC1mdfWcmfpxig==", + "version": "3.22.3", + "resolved": "https://registry.npmjs.org/@tiptap/extension-italic/-/extension-italic-3.22.3.tgz", + "integrity": "sha512-LteA4cb4EGCiUtrK2JHvDF/Zg0/YqV4DUyHhAAho+oGEQDupZlsS6m0ia5wQcclkiTLzsoPrwcSNu6RDGQ16wQ==", "license": "MIT", "funding": { "type": "github", "url": "https://github.com/sponsors/ueberdosis" }, "peerDependencies": { - "@tiptap/core": "^3.22.1" + "@tiptap/core": "^3.22.3" } }, "node_modules/@tiptap/extension-link": { - "version": "3.22.1", - "resolved": "https://registry.npmjs.org/@tiptap/extension-link/-/extension-link-3.22.1.tgz", - "integrity": "sha512-RHch/Bqv+QDvW3J1CXmiTB54pyrQYNQq8Vfa7is/O209dNPA8tdbkRP44rDjqn8NeDCriC/oJ4avWeXL4qNDVw==", + "version": "3.22.3", + "resolved": "https://registry.npmjs.org/@tiptap/extension-link/-/extension-link-3.22.3.tgz", + "integrity": "sha512-S8/P2o9pv6B3kqLjH2TRWwSAximGbciNc6R8/QcN6HWLYxp0N0JoqN3rZHl9VWIBAGRWc4zkt80dhqrl2xmgfQ==", "license": "MIT", "dependencies": { "linkifyjs": "^4.3.2" @@ -5810,147 +4611,147 @@ "url": "https://github.com/sponsors/ueberdosis" }, "peerDependencies": { - "@tiptap/core": "^3.22.1", - "@tiptap/pm": "^3.22.1" + "@tiptap/core": "^3.22.3", + "@tiptap/pm": "^3.22.3" } }, "node_modules/@tiptap/extension-list": { - "version": "3.22.1", - "resolved": "https://registry.npmjs.org/@tiptap/extension-list/-/extension-list-3.22.1.tgz", - "integrity": "sha512-6bVI5A12sFeyb0EngABV8/qCtC2IgiDbWC8mtNNLh5dAVGaUKo1KucL6vRYDhzXhyO/eHuGYepXZDLOOdS9LIQ==", + "version": "3.22.3", + "resolved": "https://registry.npmjs.org/@tiptap/extension-list/-/extension-list-3.22.3.tgz", + "integrity": "sha512-rqvv/dtqwbX+8KnPv0eMYp6PnBcuhPMol5cv1GlS8Nq/Cxt68EWGUHBuTFesw+hdnRQLmKwzoO1DlRn7PhxYRQ==", "license": "MIT", "funding": { "type": "github", "url": "https://github.com/sponsors/ueberdosis" }, "peerDependencies": { - "@tiptap/core": "^3.22.1", - "@tiptap/pm": "^3.22.1" + "@tiptap/core": "^3.22.3", + "@tiptap/pm": "^3.22.3" } }, "node_modules/@tiptap/extension-list-item": { - "version": "3.22.1", - "resolved": "https://registry.npmjs.org/@tiptap/extension-list-item/-/extension-list-item-3.22.1.tgz", - "integrity": "sha512-v0FgSX3cqLY3L1hIe2PFRTR3/+wlFOdFjv0p3fSJ5Tl7cgU7DR1OcljFqpw0exePcmt6dXqXVQua3PxSVV15eA==", + "version": "3.22.3", + "resolved": "https://registry.npmjs.org/@tiptap/extension-list-item/-/extension-list-item-3.22.3.tgz", + "integrity": "sha512-80CNf4oO5y8+LdckT4CyMe1t01EyhpRrQC9H45JW20P7559Nrchp5my3vvMtIAJbpTPPZtcB7LwdzWGKsG5drg==", "license": "MIT", "funding": { "type": "github", "url": "https://github.com/sponsors/ueberdosis" }, "peerDependencies": { - "@tiptap/extension-list": "^3.22.1" + "@tiptap/extension-list": "^3.22.3" } }, "node_modules/@tiptap/extension-list-keymap": { - "version": "3.22.1", - "resolved": "https://registry.npmjs.org/@tiptap/extension-list-keymap/-/extension-list-keymap-3.22.1.tgz", - "integrity": "sha512-00Nz4jJygYGJg6N1mdbQUslFG9QaGZq5P9MFwqoduWku7gYHWkZoZvrkxZrYtxGTHVIlLnF8LIfblAlOwNd76g==", + "version": "3.22.3", + "resolved": "https://registry.npmjs.org/@tiptap/extension-list-keymap/-/extension-list-keymap-3.22.3.tgz", + "integrity": "sha512-pKuyj5llu35zd/s2u/H9aydKZjmPRAIK5P1q/YXULhhCNln2RnmuRfQ5NklAqTD3yGciQ2lxDwwf7J6iw3ergA==", "license": "MIT", "funding": { "type": "github", "url": "https://github.com/sponsors/ueberdosis" }, "peerDependencies": { - "@tiptap/extension-list": "^3.22.1" + "@tiptap/extension-list": "^3.22.3" } }, "node_modules/@tiptap/extension-ordered-list": { - "version": "3.22.1", - "resolved": "https://registry.npmjs.org/@tiptap/extension-ordered-list/-/extension-ordered-list-3.22.1.tgz", - "integrity": "sha512-sbd99ZUa1lIemH7N6dLB+9aYxUgduwW2216VM3dLJBS9hmTA4iDRxWx0a1ApnAVv+sZasRSbb/wpYLtXviA1XQ==", + "version": "3.22.3", + "resolved": "https://registry.npmjs.org/@tiptap/extension-ordered-list/-/extension-ordered-list-3.22.3.tgz", + "integrity": "sha512-orAghtmd+K4Euu4BgI1hG+iZDXBYOyl5YTwiLBc2mQn+pqtZ9LqaH2us4ETwEwNP3/IWXGSAimUZ19nuL+eM2w==", "license": "MIT", "funding": { "type": "github", "url": "https://github.com/sponsors/ueberdosis" }, "peerDependencies": { - "@tiptap/extension-list": "^3.22.1" + "@tiptap/extension-list": "^3.22.3" } }, "node_modules/@tiptap/extension-paragraph": { - "version": "3.22.1", - "resolved": "https://registry.npmjs.org/@tiptap/extension-paragraph/-/extension-paragraph-3.22.1.tgz", - "integrity": "sha512-mnvGEZfZFysHGvmEqrSLjeddaNPB3UmomTInv9gsImw8hlB4/gQedvB6Qf2tFfIjl4ISKC5AbFxraSnJfjaL5g==", + "version": "3.22.3", + "resolved": "https://registry.npmjs.org/@tiptap/extension-paragraph/-/extension-paragraph-3.22.3.tgz", + "integrity": "sha512-oO7rhfyhEuwm+50s9K3GZPjYyEEEvFAvm1wXopvZnhbkBLydIWImBfrZoC5IQh4/sRDlTIjosV2C+ji5y0tUSg==", "license": "MIT", "funding": { "type": "github", "url": "https://github.com/sponsors/ueberdosis" }, "peerDependencies": { - "@tiptap/core": "^3.22.1" + "@tiptap/core": "^3.22.3" } }, "node_modules/@tiptap/extension-strike": { - "version": "3.22.1", - "resolved": "https://registry.npmjs.org/@tiptap/extension-strike/-/extension-strike-3.22.1.tgz", - "integrity": "sha512-LTdnGmglK1f/AW//36k+Km8URA1wrTLENi3R5N+/ipv+yP2rZ2Ki1R1m6yJx3KSFzR55c91xE6659/vz1uZ6iA==", + "version": "3.22.3", + "resolved": "https://registry.npmjs.org/@tiptap/extension-strike/-/extension-strike-3.22.3.tgz", + "integrity": "sha512-jY2InoUlKkuk5KHoIDGdML1OCA2n6PRHAtxwHNkAmiYh0Khf0zaVPGFpx4dgQrN7W5Q1WE6oBZnjrvy6qb7w0g==", "license": "MIT", "funding": { "type": "github", "url": "https://github.com/sponsors/ueberdosis" }, "peerDependencies": { - "@tiptap/core": "^3.22.1" + "@tiptap/core": "^3.22.3" } }, "node_modules/@tiptap/extension-table": { - "version": "3.22.1", - "resolved": "https://registry.npmjs.org/@tiptap/extension-table/-/extension-table-3.22.1.tgz", - "integrity": "sha512-wGioCPgrAhqQ9NNQitVM4sm8WVsu6MBs+4hdgTCtBTA7oEv7EqKWAujY6DA/aPE8uV236pUmosZX3iloHmvpOA==", + "version": "3.22.3", + "resolved": "https://registry.npmjs.org/@tiptap/extension-table/-/extension-table-3.22.3.tgz", + "integrity": "sha512-inbQSusJad7H0T++L1APg/anfL5d15cNGp2YG3vwo6TQr71nn2c9pepvmz3xuAIt8eygZDRba+4GT/COP1f9QA==", "license": "MIT", "funding": { "type": "github", "url": "https://github.com/sponsors/ueberdosis" }, "peerDependencies": { - "@tiptap/core": "^3.22.1", - "@tiptap/pm": "^3.22.1" + "@tiptap/core": "^3.22.3", + "@tiptap/pm": "^3.22.3" } }, "node_modules/@tiptap/extension-text": { - "version": "3.22.1", - "resolved": "https://registry.npmjs.org/@tiptap/extension-text/-/extension-text-3.22.1.tgz", - "integrity": "sha512-wFCNCATSTTFhvA9wOPkAgzPVyG3RM6+jOlDeRhHUCHsFWFWj0w9ZPwA/nP+Qi5hEW7kGG9V8o62RjBdHNvK2PQ==", + "version": "3.22.3", + "resolved": "https://registry.npmjs.org/@tiptap/extension-text/-/extension-text-3.22.3.tgz", + "integrity": "sha512-Q9R7JsTdomP5uUjtPjNKxHT1xoh/i9OJZnmgJLe7FcgZEaPOQ3bWxmKZoLZQfDfZjyB8BtH+Hc7nUvhCMOePxw==", "license": "MIT", "funding": { "type": "github", "url": "https://github.com/sponsors/ueberdosis" }, "peerDependencies": { - "@tiptap/core": "^3.22.1" + "@tiptap/core": "^3.22.3" } }, "node_modules/@tiptap/extension-underline": { - "version": "3.22.1", - "resolved": "https://registry.npmjs.org/@tiptap/extension-underline/-/extension-underline-3.22.1.tgz", - "integrity": "sha512-p8/ErqQInWJbpncBycIggmtCjdrMwHmA3GNhOugo6F4fYfeVxgy7pVb7ZF+ss62d0mpQvEd81pyrzhkBtb0nBg==", + "version": "3.22.3", + "resolved": "https://registry.npmjs.org/@tiptap/extension-underline/-/extension-underline-3.22.3.tgz", + "integrity": "sha512-Ch6CBWRa5w90yYSPUW6x9Py9JdrXMqk3pZ9OIlMYD8A7BqyZGfiHerX7XDMYDS09KjyK3U9XH60/zxYOzXdDLA==", "license": "MIT", "funding": { "type": "github", "url": "https://github.com/sponsors/ueberdosis" }, "peerDependencies": { - "@tiptap/core": "^3.22.1" + "@tiptap/core": "^3.22.3" } }, "node_modules/@tiptap/extensions": { - "version": "3.22.1", - "resolved": "https://registry.npmjs.org/@tiptap/extensions/-/extensions-3.22.1.tgz", - "integrity": "sha512-BKpp371Pl1CVcLRLrWH7PC1I+IsXOhet80+pILqCMlwkJnsVtOOVRr5uCF6rbPP4xK5H/ehkQWmxA8rqpv42aA==", + "version": "3.22.3", + "resolved": "https://registry.npmjs.org/@tiptap/extensions/-/extensions-3.22.3.tgz", + "integrity": "sha512-s5eiMq0m5N6N+W7dU6rd60KgZyyCD7FvtPNNswISfPr12EQwJBfbjWwTqd0UKNzA4fNrhQEERXnzORkykttPeA==", "license": "MIT", "funding": { "type": "github", "url": "https://github.com/sponsors/ueberdosis" }, "peerDependencies": { - "@tiptap/core": "^3.22.1", - "@tiptap/pm": "^3.22.1" + "@tiptap/core": "^3.22.3", + "@tiptap/pm": "^3.22.3" } }, "node_modules/@tiptap/pm": { - "version": "3.22.1", - "resolved": "https://registry.npmjs.org/@tiptap/pm/-/pm-3.22.1.tgz", - "integrity": "sha512-OSqSg2974eLJT5PNKFLM7156lBXCUf/dsKTQXWSzsLTf6HOP4dYP6c0YbAk6lgbNI+BdszsHNClmLVLA8H/L9A==", + "version": "3.22.3", + "resolved": "https://registry.npmjs.org/@tiptap/pm/-/pm-3.22.3.tgz", + "integrity": "sha512-NjfWjZuvrqmpICT+GZWNIjtOdhPyqFKDMtQy7tsQ5rErM9L2ZQdy/+T/BKSO1JdTeBhdg9OP+0yfsqoYp2aT6A==", "license": "MIT", "dependencies": { "prosemirror-changeset": "^2.3.0", @@ -5978,9 +4779,9 @@ } }, "node_modules/@tiptap/react": { - "version": "3.22.1", - "resolved": "https://registry.npmjs.org/@tiptap/react/-/react-3.22.1.tgz", - "integrity": "sha512-1pIRfgK9wape4nDXVJRfgUcYVZdPPkuECbGtz8bo0rgtdsVN7B8PBVCDyuitZ7acdLbMuuX5+TxeUOvME8np7Q==", + "version": "3.22.3", + "resolved": "https://registry.npmjs.org/@tiptap/react/-/react-3.22.3.tgz", + "integrity": "sha512-6MNr6z0PxwfJFs+BKhHcvPNvY+UV1PXgqzTiTM4Z9guml84iVZxv7ZOCSj1dFYTr3Bf1MiOs4hT1yvBFlTfIaQ==", "license": "MIT", "dependencies": { "@types/use-sync-external-store": "^0.0.6", @@ -5992,12 +4793,12 @@ "url": "https://github.com/sponsors/ueberdosis" }, "optionalDependencies": { - "@tiptap/extension-bubble-menu": "^3.22.1", - "@tiptap/extension-floating-menu": "^3.22.1" + "@tiptap/extension-bubble-menu": "^3.22.3", + "@tiptap/extension-floating-menu": "^3.22.3" }, "peerDependencies": { - "@tiptap/core": "^3.22.1", - "@tiptap/pm": "^3.22.1", + "@tiptap/core": "^3.22.3", + "@tiptap/pm": "^3.22.3", "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", "@types/react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0", "react": "^17.0.0 || ^18.0.0 || ^19.0.0", @@ -6005,35 +4806,35 @@ } }, "node_modules/@tiptap/starter-kit": { - "version": "3.22.1", - "resolved": "https://registry.npmjs.org/@tiptap/starter-kit/-/starter-kit-3.22.1.tgz", - "integrity": "sha512-1fFmURkgofxgP9GW993bSpxf2rIJzQbWZ9rPw17qbAVuGouIArG+Fd/A1WUD95Vdbx6JIrc1QxbNlLs7bhcoPA==", - "license": "MIT", - "dependencies": { - "@tiptap/core": "^3.22.1", - "@tiptap/extension-blockquote": "^3.22.1", - "@tiptap/extension-bold": "^3.22.1", - "@tiptap/extension-bullet-list": "^3.22.1", - "@tiptap/extension-code": "^3.22.1", - "@tiptap/extension-code-block": "^3.22.1", - "@tiptap/extension-document": "^3.22.1", - "@tiptap/extension-dropcursor": "^3.22.1", - "@tiptap/extension-gapcursor": "^3.22.1", - "@tiptap/extension-hard-break": "^3.22.1", - "@tiptap/extension-heading": "^3.22.1", - "@tiptap/extension-horizontal-rule": "^3.22.1", - "@tiptap/extension-italic": "^3.22.1", - "@tiptap/extension-link": "^3.22.1", - "@tiptap/extension-list": "^3.22.1", - "@tiptap/extension-list-item": "^3.22.1", - "@tiptap/extension-list-keymap": "^3.22.1", - "@tiptap/extension-ordered-list": "^3.22.1", - "@tiptap/extension-paragraph": "^3.22.1", - "@tiptap/extension-strike": "^3.22.1", - "@tiptap/extension-text": "^3.22.1", - "@tiptap/extension-underline": "^3.22.1", - "@tiptap/extensions": "^3.22.1", - "@tiptap/pm": "^3.22.1" + "version": "3.22.3", + "resolved": "https://registry.npmjs.org/@tiptap/starter-kit/-/starter-kit-3.22.3.tgz", + "integrity": "sha512-vdW/Oo1fdwTL1VOQ5YYbTov00ANeHLquBVEZyL/EkV7Xv5io9rXQsCysJfTSHhiQlyr2MtWFB4+CPGuwXjQWOQ==", + "license": "MIT", + "dependencies": { + "@tiptap/core": "^3.22.3", + "@tiptap/extension-blockquote": "^3.22.3", + "@tiptap/extension-bold": "^3.22.3", + "@tiptap/extension-bullet-list": "^3.22.3", + "@tiptap/extension-code": "^3.22.3", + "@tiptap/extension-code-block": "^3.22.3", + "@tiptap/extension-document": "^3.22.3", + "@tiptap/extension-dropcursor": "^3.22.3", + "@tiptap/extension-gapcursor": "^3.22.3", + "@tiptap/extension-hard-break": "^3.22.3", + "@tiptap/extension-heading": "^3.22.3", + "@tiptap/extension-horizontal-rule": "^3.22.3", + "@tiptap/extension-italic": "^3.22.3", + "@tiptap/extension-link": "^3.22.3", + "@tiptap/extension-list": "^3.22.3", + "@tiptap/extension-list-item": "^3.22.3", + "@tiptap/extension-list-keymap": "^3.22.3", + "@tiptap/extension-ordered-list": "^3.22.3", + "@tiptap/extension-paragraph": "^3.22.3", + "@tiptap/extension-strike": "^3.22.3", + "@tiptap/extension-text": "^3.22.3", + "@tiptap/extension-underline": "^3.22.3", + "@tiptap/extensions": "^3.22.3", + "@tiptap/pm": "^3.22.3" }, "funding": { "type": "github", @@ -6079,145 +4880,85 @@ "tinyglobby": "^0.2.14" } }, - "node_modules/@tsconfig/node10": { - "version": "1.0.12", - "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.12.tgz", - "integrity": "sha512-UCYBaeFvM11aU2y3YPZ//O5Rhj+xKyzy7mvcIoAjASbigy8mHMryP5cK7dgjlz2hWxh1g5pLw084E0a/wlUSFQ==", - "dev": true, - "license": "MIT", - "peer": true - }, - "node_modules/@tsconfig/node12": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", - "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", - "dev": true, - "license": "MIT", - "peer": true - }, - "node_modules/@tsconfig/node14": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", - "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", - "dev": true, - "license": "MIT", - "peer": true - }, - "node_modules/@tsconfig/node16": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", - "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", - "dev": true, - "license": "MIT", - "peer": true - }, - "node_modules/@turbo/darwin-64": { - "version": "2.9.3", - "resolved": "https://registry.npmjs.org/@turbo/darwin-64/-/darwin-64-2.9.3.tgz", - "integrity": "sha512-P8foouaP+y/p+hhEGBoZpzMbpVvUMwPjDpcy6wN7EYfvvyISD1USuV27qWkczecihwuPJzQ1lDBuL8ERcavTyg==", - "cpu": [ - "x64" - ], + "node_modules/@ts-morph/common/node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "darwin" - ] + "engines": { + "node": "18 || 20 || >=22" + } }, - "node_modules/@turbo/darwin-arm64": { - "version": "2.9.3", - "resolved": "https://registry.npmjs.org/@turbo/darwin-arm64/-/darwin-arm64-2.9.3.tgz", - "integrity": "sha512-SIzEkvtNdzdI50FJDaIQ6kQGqgSSdFPcdn0wqmmONN6iGKjy6hsT+EH99GP65FsfV7DLZTh2NmtTIRl2kdoz5Q==", - "cpu": [ - "arm64" - ], + "node_modules/@ts-morph/common/node_modules/brace-expansion": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.5.tgz", + "integrity": "sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "darwin" - ] + "dependencies": { + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + } }, - "node_modules/@turbo/linux-64": { - "version": "2.9.3", - "resolved": "https://registry.npmjs.org/@turbo/linux-64/-/linux-64-2.9.3.tgz", - "integrity": "sha512-pLRwFmcHHNBvsCySLS6OFabr/07kDT2pxEt/k6eBf/3asiVQZKJ7Rk88AafQx2aYA641qek4RsXvYO3JYpiBug==", - "cpu": [ - "x64" - ], + "node_modules/@ts-morph/common/node_modules/minimatch": { + "version": "10.2.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", + "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==", "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] + "license": "BlueOak-1.0.0", + "dependencies": { + "brace-expansion": "^5.0.5" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } }, - "node_modules/@turbo/linux-arm64": { - "version": "2.9.3", - "resolved": "https://registry.npmjs.org/@turbo/linux-arm64/-/linux-arm64-2.9.3.tgz", - "integrity": "sha512-gy6ApUroC2Nzv+qjGtE/uPNkhHAFU4c8God+zd5Aiv9L9uBgHlxVJpHT3XWl5xwlJZ2KWuMrlHTaS5kmNB+q1Q==", - "cpu": [ - "arm64" - ], + "node_modules/@tsconfig/node10": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.12.tgz", + "integrity": "sha512-UCYBaeFvM11aU2y3YPZ//O5Rhj+xKyzy7mvcIoAjASbigy8mHMryP5cK7dgjlz2hWxh1g5pLw084E0a/wlUSFQ==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "linux" - ] + "peer": true }, - "node_modules/@turbo/windows-64": { - "version": "2.9.3", - "resolved": "https://registry.npmjs.org/@turbo/windows-64/-/windows-64-2.9.3.tgz", - "integrity": "sha512-d0YelTX6hAsB7kIEtGB3PzIzSfAg3yDoUlHwuwJc3adBXUsyUIs0YLG+1NNtuhcDOUGnWQeKUoJ2pGWvbpRj7w==", - "cpu": [ - "x64" - ], + "node_modules/@tsconfig/node12": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", + "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "win32" - ] + "peer": true }, - "node_modules/@turbo/windows-arm64": { - "version": "2.9.3", - "resolved": "https://registry.npmjs.org/@turbo/windows-arm64/-/windows-arm64-2.9.3.tgz", - "integrity": "sha512-/08CwpKJl3oRY8nOlh2YgilZVJDHsr60XTNxRhuDeuFXONpUZ5X+Nv65izbG/xBew9qxcJFbDX9/sAmAX+ITcQ==", - "cpu": [ - "arm64" - ], + "node_modules/@tsconfig/node14": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", + "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "win32" - ] + "peer": true }, - "node_modules/@tybys/wasm-util": { - "version": "0.10.1", - "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.1.tgz", - "integrity": "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==", + "node_modules/@tsconfig/node16": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", + "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", "dev": true, "license": "MIT", - "optional": true, - "dependencies": { - "tslib": "^2.4.0" - } + "peer": true }, "node_modules/@types/aria-query": { "version": "5.0.4", - "resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-5.0.4.tgz", - "integrity": "sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==", "dev": true, "license": "MIT", "peer": true }, "node_modules/@types/babel__core": { "version": "7.20.5", - "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", - "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", "dev": true, "license": "MIT", "dependencies": { @@ -6230,8 +4971,6 @@ }, "node_modules/@types/babel__generator": { "version": "7.27.0", - "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.27.0.tgz", - "integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==", "dev": true, "license": "MIT", "dependencies": { @@ -6240,8 +4979,6 @@ }, "node_modules/@types/babel__template": { "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", - "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", "dev": true, "license": "MIT", "dependencies": { @@ -6251,8 +4988,6 @@ }, "node_modules/@types/babel__traverse": { "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.28.0.tgz", - "integrity": "sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==", "dev": true, "license": "MIT", "dependencies": { @@ -6261,8 +4996,6 @@ }, "node_modules/@types/chai": { "version": "5.2.3", - "resolved": "https://registry.npmjs.org/@types/chai/-/chai-5.2.3.tgz", - "integrity": "sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==", "dev": true, "license": "MIT", "dependencies": { @@ -6272,8 +5005,6 @@ }, "node_modules/@types/d3": { "version": "7.4.3", - "resolved": "https://registry.npmjs.org/@types/d3/-/d3-7.4.3.tgz", - "integrity": "sha512-lZXZ9ckh5R8uiFVt8ogUNf+pIrK4EsWrx2Np75WvF/eTpJ0FMHNhjXk8CKEx/+gpHbNQyJWehbFaTvqmHWB3ww==", "license": "MIT", "dependencies": { "@types/d3-array": "*", @@ -6310,14 +5041,10 @@ }, "node_modules/@types/d3-array": { "version": "3.2.2", - "resolved": "https://registry.npmjs.org/@types/d3-array/-/d3-array-3.2.2.tgz", - "integrity": "sha512-hOLWVbm7uRza0BYXpIIW5pxfrKe0W+D5lrFiAEYR+pb6w3N2SwSMaJbXdUfSEv+dT4MfHBLtn5js0LAWaO6otw==", "license": "MIT" }, "node_modules/@types/d3-axis": { "version": "3.0.6", - "resolved": "https://registry.npmjs.org/@types/d3-axis/-/d3-axis-3.0.6.tgz", - "integrity": "sha512-pYeijfZuBd87T0hGn0FO1vQ/cgLk6E1ALJjfkC0oJ8cbwkZl3TpgS8bVBLZN+2jjGgg38epgxb2zmoGtSfvgMw==", "license": "MIT", "dependencies": { "@types/d3-selection": "*" @@ -6325,8 +5052,6 @@ }, "node_modules/@types/d3-brush": { "version": "3.0.6", - "resolved": "https://registry.npmjs.org/@types/d3-brush/-/d3-brush-3.0.6.tgz", - "integrity": "sha512-nH60IZNNxEcrh6L1ZSMNA28rj27ut/2ZmI3r96Zd+1jrZD++zD3LsMIjWlvg4AYrHn/Pqz4CF3veCxGjtbqt7A==", "license": "MIT", "dependencies": { "@types/d3-selection": "*" @@ -6334,20 +5059,14 @@ }, "node_modules/@types/d3-chord": { "version": "3.0.6", - "resolved": "https://registry.npmjs.org/@types/d3-chord/-/d3-chord-3.0.6.tgz", - "integrity": "sha512-LFYWWd8nwfwEmTZG9PfQxd17HbNPksHBiJHaKuY1XeqscXacsS2tyoo6OdRsjf+NQYeB6XrNL3a25E3gH69lcg==", "license": "MIT" }, "node_modules/@types/d3-color": { "version": "3.1.3", - "resolved": "https://registry.npmjs.org/@types/d3-color/-/d3-color-3.1.3.tgz", - "integrity": "sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A==", "license": "MIT" }, "node_modules/@types/d3-contour": { "version": "3.0.6", - "resolved": "https://registry.npmjs.org/@types/d3-contour/-/d3-contour-3.0.6.tgz", - "integrity": "sha512-BjzLgXGnCWjUSYGfH1cpdo41/hgdWETu4YxpezoztawmqsvCeep+8QGfiY6YbDvfgHz/DkjeIkkZVJavB4a3rg==", "license": "MIT", "dependencies": { "@types/d3-array": "*", @@ -6356,20 +5075,14 @@ }, "node_modules/@types/d3-delaunay": { "version": "6.0.4", - "resolved": "https://registry.npmjs.org/@types/d3-delaunay/-/d3-delaunay-6.0.4.tgz", - "integrity": "sha512-ZMaSKu4THYCU6sV64Lhg6qjf1orxBthaC161plr5KuPHo3CNm8DTHiLw/5Eq2b6TsNP0W0iJrUOFscY6Q450Hw==", "license": "MIT" }, "node_modules/@types/d3-dispatch": { "version": "3.0.7", - "resolved": "https://registry.npmjs.org/@types/d3-dispatch/-/d3-dispatch-3.0.7.tgz", - "integrity": "sha512-5o9OIAdKkhN1QItV2oqaE5KMIiXAvDWBDPrD85e58Qlz1c1kI/J0NcqbEG88CoTwJrYe7ntUCVfeUl2UJKbWgA==", "license": "MIT" }, "node_modules/@types/d3-drag": { "version": "3.0.7", - "resolved": "https://registry.npmjs.org/@types/d3-drag/-/d3-drag-3.0.7.tgz", - "integrity": "sha512-HE3jVKlzU9AaMazNufooRJ5ZpWmLIoc90A37WU2JMmeq28w1FQqCZswHZ3xR+SuxYftzHq6WU6KJHvqxKzTxxQ==", "license": "MIT", "dependencies": { "@types/d3-selection": "*" @@ -6377,20 +5090,14 @@ }, "node_modules/@types/d3-dsv": { "version": "3.0.7", - "resolved": "https://registry.npmjs.org/@types/d3-dsv/-/d3-dsv-3.0.7.tgz", - "integrity": "sha512-n6QBF9/+XASqcKK6waudgL0pf/S5XHPPI8APyMLLUHd8NqouBGLsU8MgtO7NINGtPBtk9Kko/W4ea0oAspwh9g==", "license": "MIT" }, "node_modules/@types/d3-ease": { "version": "3.0.2", - "resolved": "https://registry.npmjs.org/@types/d3-ease/-/d3-ease-3.0.2.tgz", - "integrity": "sha512-NcV1JjO5oDzoK26oMzbILE6HW7uVXOHLQvHshBUW4UMdZGfiY6v5BeQwh9a9tCzv+CeefZQHJt5SRgK154RtiA==", "license": "MIT" }, "node_modules/@types/d3-fetch": { "version": "3.0.7", - "resolved": "https://registry.npmjs.org/@types/d3-fetch/-/d3-fetch-3.0.7.tgz", - "integrity": "sha512-fTAfNmxSb9SOWNB9IoG5c8Hg6R+AzUHDRlsXsDZsNp6sxAEOP0tkP3gKkNSO/qmHPoBFTxNrjDprVHDQDvo5aA==", "license": "MIT", "dependencies": { "@types/d3-dsv": "*" @@ -6398,20 +5105,14 @@ }, "node_modules/@types/d3-force": { "version": "3.0.10", - "resolved": "https://registry.npmjs.org/@types/d3-force/-/d3-force-3.0.10.tgz", - "integrity": "sha512-ZYeSaCF3p73RdOKcjj+swRlZfnYpK1EbaDiYICEEp5Q6sUiqFaFQ9qgoshp5CzIyyb/yD09kD9o2zEltCexlgw==", "license": "MIT" }, "node_modules/@types/d3-format": { "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@types/d3-format/-/d3-format-3.0.4.tgz", - "integrity": "sha512-fALi2aI6shfg7vM5KiR1wNJnZ7r6UuggVqtDA+xiEdPZQwy/trcQaHnwShLuLdta2rTymCNpxYTiMZX/e09F4g==", "license": "MIT" }, "node_modules/@types/d3-geo": { "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@types/d3-geo/-/d3-geo-3.1.0.tgz", - "integrity": "sha512-856sckF0oP/diXtS4jNsiQw/UuK5fQG8l/a9VVLeSouf1/PPbBE1i1W852zVwKwYCBkFJJB7nCFTbk6UMEXBOQ==", "license": "MIT", "dependencies": { "@types/geojson": "*" @@ -6419,14 +5120,10 @@ }, "node_modules/@types/d3-hierarchy": { "version": "3.1.7", - "resolved": "https://registry.npmjs.org/@types/d3-hierarchy/-/d3-hierarchy-3.1.7.tgz", - "integrity": "sha512-tJFtNoYBtRtkNysX1Xq4sxtjK8YgoWUNpIiUee0/jHGRwqvzYxkq0hGVbbOGSz+JgFxxRu4K8nb3YpG3CMARtg==", "license": "MIT" }, "node_modules/@types/d3-interpolate": { "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@types/d3-interpolate/-/d3-interpolate-3.0.4.tgz", - "integrity": "sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA==", "license": "MIT", "dependencies": { "@types/d3-color": "*" @@ -6434,32 +5131,22 @@ }, "node_modules/@types/d3-path": { "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@types/d3-path/-/d3-path-3.1.1.tgz", - "integrity": "sha512-VMZBYyQvbGmWyWVea0EHs/BwLgxc+MKi1zLDCONksozI4YJMcTt8ZEuIR4Sb1MMTE8MMW49v0IwI5+b7RmfWlg==", "license": "MIT" }, "node_modules/@types/d3-polygon": { "version": "3.0.2", - "resolved": "https://registry.npmjs.org/@types/d3-polygon/-/d3-polygon-3.0.2.tgz", - "integrity": "sha512-ZuWOtMaHCkN9xoeEMr1ubW2nGWsp4nIql+OPQRstu4ypeZ+zk3YKqQT0CXVe/PYqrKpZAi+J9mTs05TKwjXSRA==", "license": "MIT" }, "node_modules/@types/d3-quadtree": { "version": "3.0.6", - "resolved": "https://registry.npmjs.org/@types/d3-quadtree/-/d3-quadtree-3.0.6.tgz", - "integrity": "sha512-oUzyO1/Zm6rsxKRHA1vH0NEDG58HrT5icx/azi9MF1TWdtttWl0UIUsjEQBBh+SIkrpd21ZjEv7ptxWys1ncsg==", "license": "MIT" }, "node_modules/@types/d3-random": { "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@types/d3-random/-/d3-random-3.0.3.tgz", - "integrity": "sha512-Imagg1vJ3y76Y2ea0871wpabqp613+8/r0mCLEBfdtqC7xMSfj9idOnmBYyMoULfHePJyxMAw3nWhJxzc+LFwQ==", "license": "MIT" }, "node_modules/@types/d3-scale": { "version": "4.0.9", - "resolved": "https://registry.npmjs.org/@types/d3-scale/-/d3-scale-4.0.9.tgz", - "integrity": "sha512-dLmtwB8zkAeO/juAMfnV+sItKjlsw2lKdZVVy6LRr0cBmegxSABiLEpGVmSJJ8O08i4+sGR6qQtb6WtuwJdvVw==", "license": "MIT", "dependencies": { "@types/d3-time": "*" @@ -6467,20 +5154,14 @@ }, "node_modules/@types/d3-scale-chromatic": { "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@types/d3-scale-chromatic/-/d3-scale-chromatic-3.1.0.tgz", - "integrity": "sha512-iWMJgwkK7yTRmWqRB5plb1kadXyQ5Sj8V/zYlFGMUBbIPKQScw+Dku9cAAMgJG+z5GYDoMjWGLVOvjghDEFnKQ==", "license": "MIT" }, "node_modules/@types/d3-selection": { "version": "3.0.11", - "resolved": "https://registry.npmjs.org/@types/d3-selection/-/d3-selection-3.0.11.tgz", - "integrity": "sha512-bhAXu23DJWsrI45xafYpkQ4NtcKMwWnAC/vKrd2l+nxMFuvOT3XMYTIj2opv8vq8AO5Yh7Qac/nSeP/3zjTK0w==", "license": "MIT" }, "node_modules/@types/d3-shape": { "version": "3.1.8", - "resolved": "https://registry.npmjs.org/@types/d3-shape/-/d3-shape-3.1.8.tgz", - "integrity": "sha512-lae0iWfcDeR7qt7rA88BNiqdvPS5pFVPpo5OfjElwNaT2yyekbM0C9vK+yqBqEmHr6lDkRnYNoTBYlAgJa7a4w==", "license": "MIT", "dependencies": { "@types/d3-path": "*" @@ -6488,26 +5169,18 @@ }, "node_modules/@types/d3-time": { "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@types/d3-time/-/d3-time-3.0.4.tgz", - "integrity": "sha512-yuzZug1nkAAaBlBBikKZTgzCeA+k1uy4ZFwWANOfKw5z5LRhV0gNA7gNkKm7HoK+HRN0wX3EkxGk0fpbWhmB7g==", "license": "MIT" }, "node_modules/@types/d3-time-format": { "version": "4.0.3", - "resolved": "https://registry.npmjs.org/@types/d3-time-format/-/d3-time-format-4.0.3.tgz", - "integrity": "sha512-5xg9rC+wWL8kdDj153qZcsJ0FWiFt0J5RB6LYUNZjwSnesfblqrI/bJ1wBdJ8OQfncgbJG5+2F+qfqnqyzYxyg==", "license": "MIT" }, "node_modules/@types/d3-timer": { "version": "3.0.2", - "resolved": "https://registry.npmjs.org/@types/d3-timer/-/d3-timer-3.0.2.tgz", - "integrity": "sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw==", "license": "MIT" }, "node_modules/@types/d3-transition": { "version": "3.0.9", - "resolved": "https://registry.npmjs.org/@types/d3-transition/-/d3-transition-3.0.9.tgz", - "integrity": "sha512-uZS5shfxzO3rGlu0cC3bjmMFKsXv+SmZZcgp0KD22ts4uGXp5EVYGzu/0YdwZeKmddhcAccYtREJKkPfXkZuCg==", "license": "MIT", "dependencies": { "@types/d3-selection": "*" @@ -6515,8 +5188,6 @@ }, "node_modules/@types/d3-zoom": { "version": "3.0.8", - "resolved": "https://registry.npmjs.org/@types/d3-zoom/-/d3-zoom-3.0.8.tgz", - "integrity": "sha512-iqMC4/YlFCSlO8+2Ii1GGGliCAY4XdeG748w5vQUbevlbDu0zSjH/+jojorQVBK/se0j6DUFNPBGSqD3YWYnDw==", "license": "MIT", "dependencies": { "@types/d3-interpolate": "*", @@ -6524,9 +5195,7 @@ } }, "node_modules/@types/debug": { - "version": "4.1.13", - "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.13.tgz", - "integrity": "sha512-KSVgmQmzMwPlmtljOomayoR89W4FynCAi3E8PPs7vmDVPe84hT+vGPKkJfThkmXs0x0jAaa9U8uW8bbfyS2fWw==", + "version": "4.1.12", "license": "MIT", "dependencies": { "@types/ms": "*" @@ -6534,28 +5203,20 @@ }, "node_modules/@types/deep-eql": { "version": "4.0.2", - "resolved": "https://registry.npmjs.org/@types/deep-eql/-/deep-eql-4.0.2.tgz", - "integrity": "sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==", "dev": true, "license": "MIT" }, "node_modules/@types/doctrine": { "version": "0.0.9", - "resolved": "https://registry.npmjs.org/@types/doctrine/-/doctrine-0.0.9.tgz", - "integrity": "sha512-eOIHzCUSH7SMfonMG1LsC2f8vxBFtho6NGBznK41R84YzPuvSBzrhEps33IsQiOW9+VL6NQ9DbjQJznk/S4uRA==", "dev": true, "license": "MIT" }, "node_modules/@types/estree": { "version": "1.0.8", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", - "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", "license": "MIT" }, "node_modules/@types/estree-jsx": { "version": "1.0.5", - "resolved": "https://registry.npmjs.org/@types/estree-jsx/-/estree-jsx-1.0.5.tgz", - "integrity": "sha512-52CcUVNFyfb1A2ALocQw/Dd1BQFNmSdkuC3BkZ6iqhdMfQz7JWOFRuJFloOzjk+6WijU56m9oKXFAXc7o3Towg==", "license": "MIT", "dependencies": { "@types/estree": "*" @@ -6563,14 +5224,10 @@ }, "node_modules/@types/geojson": { "version": "7946.0.16", - "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.16.tgz", - "integrity": "sha512-6C8nqWur3j98U6+lXDfTUWIfgvZU+EumvpHKcYjujKH7woYyLj2sUmff0tRhrqM7BohUw7Pz3ZB1jj2gW9Fvmg==", "license": "MIT" }, "node_modules/@types/hast": { "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz", - "integrity": "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==", "license": "MIT", "dependencies": { "@types/unist": "*" @@ -6578,8 +5235,6 @@ }, "node_modules/@types/json-schema": { "version": "7.0.15", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", - "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", "dev": true, "license": "MIT" }, @@ -6601,8 +5256,6 @@ }, "node_modules/@types/mdast": { "version": "4.0.4", - "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-4.0.4.tgz", - "integrity": "sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==", "license": "MIT", "dependencies": { "@types/unist": "*" @@ -6616,14 +5269,10 @@ }, "node_modules/@types/ms": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@types/ms/-/ms-2.1.0.tgz", - "integrity": "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==", "license": "MIT" }, "node_modules/@types/node": { - "version": "22.19.15", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.19.15.tgz", - "integrity": "sha512-F0R/h2+dsy5wJAUe3tAU6oqa2qbWY5TpNfL/RGmo1y38hiyO1w3x2jPtt76wmuaJI4DQnOBu21cNXQ2STIUUWg==", + "version": "22.19.13", "dev": true, "license": "MIT", "dependencies": { @@ -6644,15 +5293,11 @@ }, "node_modules/@types/parse-json": { "version": "4.0.2", - "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.2.tgz", - "integrity": "sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==", "dev": true, "license": "MIT" }, "node_modules/@types/react": { "version": "19.2.14", - "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.14.tgz", - "integrity": "sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w==", "license": "MIT", "dependencies": { "csstype": "^3.2.2" @@ -6660,8 +5305,6 @@ }, "node_modules/@types/react-dom": { "version": "19.2.3", - "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.2.3.tgz", - "integrity": "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==", "license": "MIT", "peerDependencies": { "@types/react": "^19.2.0" @@ -6669,8 +5312,6 @@ }, "node_modules/@types/react-relay": { "version": "18.2.1", - "resolved": "https://registry.npmjs.org/@types/react-relay/-/react-relay-18.2.1.tgz", - "integrity": "sha512-KgmFapsxAylhxcFfaAv5GZZJhTHnDvV8IDZVsUm5afpJUvgZC1Y68ssfOGsFfiFY/2EhxHM/YPfpdKbfmF3Ecg==", "dev": true, "license": "MIT", "dependencies": { @@ -6680,15 +5321,11 @@ }, "node_modules/@types/relay-runtime": { "version": "19.0.3", - "resolved": "https://registry.npmjs.org/@types/relay-runtime/-/relay-runtime-19.0.3.tgz", - "integrity": "sha512-pvpWWQq5e9KeESF8klQaP2igLLhr2bRd3XxVCxNpGElsPQiP6Mejr59RT9/OGY3O3i8jAGGQsshVe0QCQDbxNg==", "dev": true, "license": "MIT" }, "node_modules/@types/resolve": { "version": "1.20.6", - "resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.20.6.tgz", - "integrity": "sha512-A4STmOXPhMUtHH+S6ymgE2GiBSMqf4oTvcQZMcHzokuTLVYzXTB8ttjcgxOVaAp2lGwEdzZ0J+cRbbeevQj1UQ==", "dev": true, "license": "MIT" }, @@ -6709,15 +5346,11 @@ }, "node_modules/@types/trusted-types": { "version": "2.0.7", - "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz", - "integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==", "license": "MIT", "optional": true }, "node_modules/@types/unist": { "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", - "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==", "license": "MIT" }, "node_modules/@types/use-sync-external-store": { @@ -6726,21 +5359,27 @@ "integrity": "sha512-zFDAD+tlpf2r4asuHEj0XH6pY6i0g5NeAHPn+15wk3BV6JA69eERFXC1gyGThDkVa1zCyKr5jox1+2LbV/AMLg==", "license": "MIT" }, + "node_modules/@types/uuid": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-10.0.0.tgz", + "integrity": "sha512-7gqG38EyHgyP1S+7+xomFtL+ZNHcKv6DwNaCZmJmo1vgMugyF3TCnXVg4t1uk89mLNwnLtnY3TpOpCOyp1/xHQ==", + "dev": true, + "license": "MIT", + "optional": true + }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.58.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.58.0.tgz", - "integrity": "sha512-RLkVSiNuUP1C2ROIWfqX+YcUfLaSnxGE/8M+Y57lopVwg9VTYYfhuz15Yf1IzCKgZj6/rIbYTmJCUSqr76r0Wg==", + "version": "8.56.1", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/regexpp": "^4.12.2", - "@typescript-eslint/scope-manager": "8.58.0", - "@typescript-eslint/type-utils": "8.58.0", - "@typescript-eslint/utils": "8.58.0", - "@typescript-eslint/visitor-keys": "8.58.0", + "@typescript-eslint/scope-manager": "8.56.1", + "@typescript-eslint/type-utils": "8.56.1", + "@typescript-eslint/utils": "8.56.1", + "@typescript-eslint/visitor-keys": "8.56.1", "ignore": "^7.0.5", "natural-compare": "^1.4.0", - "ts-api-utils": "^2.5.0" + "ts-api-utils": "^2.4.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -6750,15 +5389,13 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "@typescript-eslint/parser": "^8.58.0", + "@typescript-eslint/parser": "^8.56.1", "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", - "typescript": ">=4.8.4 <6.1.0" + "typescript": ">=4.8.4 <6.0.0" } }, "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": { "version": "7.0.5", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", - "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==", "dev": true, "license": "MIT", "engines": { @@ -6766,16 +5403,14 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "8.58.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.58.0.tgz", - "integrity": "sha512-rLoGZIf9afaRBYsPUMtvkDWykwXwUPL60HebR4JgTI8mxfFe2cQTu3AGitANp4b9B2QlVru6WzjgB2IzJKiCSA==", + "version": "8.56.1", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/scope-manager": "8.58.0", - "@typescript-eslint/types": "8.58.0", - "@typescript-eslint/typescript-estree": "8.58.0", - "@typescript-eslint/visitor-keys": "8.58.0", + "@typescript-eslint/scope-manager": "8.56.1", + "@typescript-eslint/types": "8.56.1", + "@typescript-eslint/typescript-estree": "8.56.1", + "@typescript-eslint/visitor-keys": "8.56.1", "debug": "^4.4.3" }, "engines": { @@ -6787,18 +5422,16 @@ }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", - "typescript": ">=4.8.4 <6.1.0" + "typescript": ">=4.8.4 <6.0.0" } }, "node_modules/@typescript-eslint/project-service": { - "version": "8.58.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.58.0.tgz", - "integrity": "sha512-8Q/wBPWLQP1j16NxoPNIKpDZFMaxl7yWIoqXWYeWO+Bbd2mjgvoF0dxP2jKZg5+x49rgKdf7Ck473M8PC3V9lg==", + "version": "8.56.1", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/tsconfig-utils": "^8.58.0", - "@typescript-eslint/types": "^8.58.0", + "@typescript-eslint/tsconfig-utils": "^8.56.1", + "@typescript-eslint/types": "^8.56.1", "debug": "^4.4.3" }, "engines": { @@ -6809,18 +5442,16 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "typescript": ">=4.8.4 <6.1.0" + "typescript": ">=4.8.4 <6.0.0" } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.58.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.58.0.tgz", - "integrity": "sha512-W1Lur1oF50FxSnNdGp3Vs6P+yBRSmZiw4IIjEeYxd8UQJwhUF0gDgDD/W/Tgmh73mxgEU3qX0Bzdl/NGuSPEpQ==", + "version": "8.56.1", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.58.0", - "@typescript-eslint/visitor-keys": "8.58.0" + "@typescript-eslint/types": "8.56.1", + "@typescript-eslint/visitor-keys": "8.56.1" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -6831,9 +5462,7 @@ } }, "node_modules/@typescript-eslint/tsconfig-utils": { - "version": "8.58.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.58.0.tgz", - "integrity": "sha512-doNSZEVJsWEu4htiVC+PR6NpM+pa+a4ClH9INRWOWCUzMst/VA9c4gXq92F8GUD1rwhNvRLkgjfYtFXegXQF7A==", + "version": "8.56.1", "dev": true, "license": "MIT", "engines": { @@ -6844,21 +5473,19 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "typescript": ">=4.8.4 <6.1.0" + "typescript": ">=4.8.4 <6.0.0" } }, "node_modules/@typescript-eslint/type-utils": { - "version": "8.58.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.58.0.tgz", - "integrity": "sha512-aGsCQImkDIqMyx1u4PrVlbi/krmDsQUs4zAcCV6M7yPcPev+RqVlndsJy9kJ8TLihW9TZ0kbDAzctpLn5o+lOg==", + "version": "8.56.1", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.58.0", - "@typescript-eslint/typescript-estree": "8.58.0", - "@typescript-eslint/utils": "8.58.0", + "@typescript-eslint/types": "8.56.1", + "@typescript-eslint/typescript-estree": "8.56.1", + "@typescript-eslint/utils": "8.56.1", "debug": "^4.4.3", - "ts-api-utils": "^2.5.0" + "ts-api-utils": "^2.4.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -6869,13 +5496,11 @@ }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", - "typescript": ">=4.8.4 <6.1.0" + "typescript": ">=4.8.4 <6.0.0" } }, "node_modules/@typescript-eslint/types": { - "version": "8.58.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.58.0.tgz", - "integrity": "sha512-O9CjxypDT89fbHxRfETNoAnHj/i6IpRK0CvbVN3qibxlLdo5p5hcLmUuCCrHMpxiWSwKyI8mCP7qRNYuOJ0Uww==", + "version": "8.56.1", "dev": true, "license": "MIT", "engines": { @@ -6887,21 +5512,19 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.58.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.58.0.tgz", - "integrity": "sha512-7vv5UWbHqew/dvs+D3e1RvLv1v2eeZ9txRHPnEEBUgSNLx5ghdzjHa0sgLWYVKssH+lYmV0JaWdoubo0ncGYLA==", + "version": "8.56.1", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/project-service": "8.58.0", - "@typescript-eslint/tsconfig-utils": "8.58.0", - "@typescript-eslint/types": "8.58.0", - "@typescript-eslint/visitor-keys": "8.58.0", + "@typescript-eslint/project-service": "8.56.1", + "@typescript-eslint/tsconfig-utils": "8.56.1", + "@typescript-eslint/types": "8.56.1", + "@typescript-eslint/visitor-keys": "8.56.1", "debug": "^4.4.3", "minimatch": "^10.2.2", "semver": "^7.7.3", "tinyglobby": "^0.2.15", - "ts-api-utils": "^2.5.0" + "ts-api-utils": "^2.4.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -6911,20 +5534,18 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "typescript": ">=4.8.4 <6.1.0" + "typescript": ">=4.8.4 <6.0.0" } }, "node_modules/@typescript-eslint/utils": { - "version": "8.58.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.58.0.tgz", - "integrity": "sha512-RfeSqcFeHMHlAWzt4TBjWOAtoW9lnsAGiP3GbaX9uVgTYYrMbVnGONEfUCiSss+xMHFl+eHZiipmA8WkQ7FuNA==", + "version": "8.56.1", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.9.1", - "@typescript-eslint/scope-manager": "8.58.0", - "@typescript-eslint/types": "8.58.0", - "@typescript-eslint/typescript-estree": "8.58.0" + "@typescript-eslint/scope-manager": "8.56.1", + "@typescript-eslint/types": "8.56.1", + "@typescript-eslint/typescript-estree": "8.56.1" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -6935,17 +5556,15 @@ }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", - "typescript": ">=4.8.4 <6.1.0" + "typescript": ">=4.8.4 <6.0.0" } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.58.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.58.0.tgz", - "integrity": "sha512-XJ9UD9+bbDo4a4epraTwG3TsNPeiB9aShrUneAVXy8q4LuwowN+qu89/6ByLMINqvIMeI9H9hOHQtg/ijrYXzQ==", + "version": "8.56.1", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.58.0", + "@typescript-eslint/types": "8.56.1", "eslint-visitor-keys": "^5.0.0" }, "engines": { @@ -6958,8 +5577,6 @@ }, "node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": { "version": "5.0.1", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz", - "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==", "dev": true, "license": "Apache-2.0", "engines": { @@ -6971,42 +5588,10 @@ }, "node_modules/@ungap/structured-clone": { "version": "1.3.0", - "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.0.tgz", - "integrity": "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==", "license": "ISC" }, - "node_modules/@unrs/resolver-binding-android-arm-eabi": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-android-arm-eabi/-/resolver-binding-android-arm-eabi-1.11.1.tgz", - "integrity": "sha512-ppLRUgHVaGRWUx0R0Ut06Mjo9gBaBkg3v/8AxusGLhsIotbBLuRk51rAzqLC8gq6NyyAojEXglNjzf6R948DNw==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ] - }, - "node_modules/@unrs/resolver-binding-android-arm64": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-android-arm64/-/resolver-binding-android-arm64-1.11.1.tgz", - "integrity": "sha512-lCxkVtb4wp1v+EoN+HjIG9cIIzPkX5OtM03pQYkG+U5O/wL53LC4QbIeazgiKqluGeVEeBlZahHalCaBvU1a2g==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ] - }, "node_modules/@unrs/resolver-binding-darwin-arm64": { "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-darwin-arm64/-/resolver-binding-darwin-arm64-1.11.1.tgz", - "integrity": "sha512-gPVA1UjRu1Y/IsB/dQEsp2V1pm44Of6+LWvbLc9SDk1c2KhhDRDBUkQCYVWe6f26uJb3fOK8saWMgtX8IrMk3g==", "cpu": [ "arm64" ], @@ -7017,237 +5602,8 @@ "darwin" ] }, - "node_modules/@unrs/resolver-binding-darwin-x64": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-darwin-x64/-/resolver-binding-darwin-x64-1.11.1.tgz", - "integrity": "sha512-cFzP7rWKd3lZaCsDze07QX1SC24lO8mPty9vdP+YVa3MGdVgPmFc59317b2ioXtgCMKGiCLxJ4HQs62oz6GfRQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@unrs/resolver-binding-freebsd-x64": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-freebsd-x64/-/resolver-binding-freebsd-x64-1.11.1.tgz", - "integrity": "sha512-fqtGgak3zX4DCB6PFpsH5+Kmt/8CIi4Bry4rb1ho6Av2QHTREM+47y282Uqiu3ZRF5IQioJQ5qWRV6jduA+iGw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ] - }, - "node_modules/@unrs/resolver-binding-linux-arm-gnueabihf": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm-gnueabihf/-/resolver-binding-linux-arm-gnueabihf-1.11.1.tgz", - "integrity": "sha512-u92mvlcYtp9MRKmP+ZvMmtPN34+/3lMHlyMj7wXJDeXxuM0Vgzz0+PPJNsro1m3IZPYChIkn944wW8TYgGKFHw==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@unrs/resolver-binding-linux-arm-musleabihf": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm-musleabihf/-/resolver-binding-linux-arm-musleabihf-1.11.1.tgz", - "integrity": "sha512-cINaoY2z7LVCrfHkIcmvj7osTOtm6VVT16b5oQdS4beibX2SYBwgYLmqhBjA1t51CarSaBuX5YNsWLjsqfW5Cw==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@unrs/resolver-binding-linux-arm64-gnu": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm64-gnu/-/resolver-binding-linux-arm64-gnu-1.11.1.tgz", - "integrity": "sha512-34gw7PjDGB9JgePJEmhEqBhWvCiiWCuXsL9hYphDF7crW7UgI05gyBAi6MF58uGcMOiOqSJ2ybEeCvHcq0BCmQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@unrs/resolver-binding-linux-arm64-musl": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm64-musl/-/resolver-binding-linux-arm64-musl-1.11.1.tgz", - "integrity": "sha512-RyMIx6Uf53hhOtJDIamSbTskA99sPHS96wxVE/bJtePJJtpdKGXO1wY90oRdXuYOGOTuqjT8ACccMc4K6QmT3w==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@unrs/resolver-binding-linux-ppc64-gnu": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-ppc64-gnu/-/resolver-binding-linux-ppc64-gnu-1.11.1.tgz", - "integrity": "sha512-D8Vae74A4/a+mZH0FbOkFJL9DSK2R6TFPC9M+jCWYia/q2einCubX10pecpDiTmkJVUH+y8K3BZClycD8nCShA==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@unrs/resolver-binding-linux-riscv64-gnu": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-riscv64-gnu/-/resolver-binding-linux-riscv64-gnu-1.11.1.tgz", - "integrity": "sha512-frxL4OrzOWVVsOc96+V3aqTIQl1O2TjgExV4EKgRY09AJ9leZpEg8Ak9phadbuX0BA4k8U5qtvMSQQGGmaJqcQ==", - "cpu": [ - "riscv64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@unrs/resolver-binding-linux-riscv64-musl": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-riscv64-musl/-/resolver-binding-linux-riscv64-musl-1.11.1.tgz", - "integrity": "sha512-mJ5vuDaIZ+l/acv01sHoXfpnyrNKOk/3aDoEdLO/Xtn9HuZlDD6jKxHlkN8ZhWyLJsRBxfv9GYM2utQ1SChKew==", - "cpu": [ - "riscv64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@unrs/resolver-binding-linux-s390x-gnu": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-s390x-gnu/-/resolver-binding-linux-s390x-gnu-1.11.1.tgz", - "integrity": "sha512-kELo8ebBVtb9sA7rMe1Cph4QHreByhaZ2QEADd9NzIQsYNQpt9UkM9iqr2lhGr5afh885d/cB5QeTXSbZHTYPg==", - "cpu": [ - "s390x" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@unrs/resolver-binding-linux-x64-gnu": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-x64-gnu/-/resolver-binding-linux-x64-gnu-1.11.1.tgz", - "integrity": "sha512-C3ZAHugKgovV5YvAMsxhq0gtXuwESUKc5MhEtjBpLoHPLYM+iuwSj3lflFwK3DPm68660rZ7G8BMcwSro7hD5w==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@unrs/resolver-binding-linux-x64-musl": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-x64-musl/-/resolver-binding-linux-x64-musl-1.11.1.tgz", - "integrity": "sha512-rV0YSoyhK2nZ4vEswT/QwqzqQXw5I6CjoaYMOX0TqBlWhojUf8P94mvI7nuJTeaCkkds3QE4+zS8Ko+GdXuZtA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@unrs/resolver-binding-wasm32-wasi": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-wasm32-wasi/-/resolver-binding-wasm32-wasi-1.11.1.tgz", - "integrity": "sha512-5u4RkfxJm+Ng7IWgkzi3qrFOvLvQYnPBmjmZQ8+szTK/b31fQCnleNl1GgEt7nIsZRIf5PLhPwT0WM+q45x/UQ==", - "cpu": [ - "wasm32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "@napi-rs/wasm-runtime": "^0.2.11" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@unrs/resolver-binding-win32-arm64-msvc": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-arm64-msvc/-/resolver-binding-win32-arm64-msvc-1.11.1.tgz", - "integrity": "sha512-nRcz5Il4ln0kMhfL8S3hLkxI85BXs3o8EYoattsJNdsX4YUU89iOkVn7g0VHSRxFuVMdM4Q1jEpIId1Ihim/Uw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@unrs/resolver-binding-win32-ia32-msvc": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-ia32-msvc/-/resolver-binding-win32-ia32-msvc-1.11.1.tgz", - "integrity": "sha512-DCEI6t5i1NmAZp6pFonpD5m7i6aFrpofcp4LA2i8IIq60Jyo28hamKBxNrZcyOwVOZkgsRp9O2sXWBWP8MnvIQ==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@unrs/resolver-binding-win32-x64-msvc": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-x64-msvc/-/resolver-binding-win32-x64-msvc-1.11.1.tgz", - "integrity": "sha512-lrW200hZdbfRtztbygyaq/6jP6AKE8qQN2KvPcJ+x7wiD038YtnYtZ82IMNJ69GJibV7bwL3y9FgK+5w/pYt6g==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, "node_modules/@upsetjs/venn.js": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@upsetjs/venn.js/-/venn.js-2.0.0.tgz", - "integrity": "sha512-WbBhLrooyePuQ1VZxrJjtLvTc4NVfpOyKx0sKqioq9bX1C1m7Jgykkn8gLrtwumBioXIqam8DLxp88Adbue6Hw==", "license": "MIT", "optionalDependencies": { "d3-selection": "^3.0.0", @@ -7255,9 +5611,7 @@ } }, "node_modules/@vitejs/plugin-react": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-5.2.0.tgz", - "integrity": "sha512-YmKkfhOAi3wsB1PhJq5Scj3GXMn3WvtQ/JC0xoopuHoXSdmtdStOpFrYaT1kie2YgFBcIe64ROzMYRjCrYOdYw==", + "version": "5.1.4", "dev": true, "license": "MIT", "dependencies": { @@ -7272,13 +5626,11 @@ "node": "^20.19.0 || >=22.12.0" }, "peerDependencies": { - "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0" + "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0" } }, "node_modules/@vitest/expect": { "version": "3.2.4", - "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-3.2.4.tgz", - "integrity": "sha512-Io0yyORnB6sikFlt8QW5K7slY4OjqNX9jmJQ02QDda8lyM6B5oNgVWoSoKPac8/kgnCUzuHQKrSLtu/uOqqrig==", "dev": true, "license": "MIT", "dependencies": { @@ -7294,8 +5646,6 @@ }, "node_modules/@vitest/mocker": { "version": "3.2.4", - "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-3.2.4.tgz", - "integrity": "sha512-46ryTE9RZO/rfDd7pEqFl7etuyzekzEhUbTW3BvmeO/BcCMEgq59BKhek3dXDWgAj4oMK6OZi+vRr1wPW6qjEQ==", "dev": true, "license": "MIT", "dependencies": { @@ -7321,8 +5671,6 @@ }, "node_modules/@vitest/mocker/node_modules/estree-walker": { "version": "3.0.3", - "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", - "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", "dev": true, "license": "MIT", "dependencies": { @@ -7331,8 +5679,6 @@ }, "node_modules/@vitest/pretty-format": { "version": "3.2.4", - "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-3.2.4.tgz", - "integrity": "sha512-IVNZik8IVRJRTr9fxlitMKeJeXFFFN0JaB9PHPGQ8NKQbGpfjlTx9zO4RefN8gp7eqjNy8nyK3NZmBzOPeIxtA==", "dev": true, "license": "MIT", "dependencies": { @@ -7344,8 +5690,6 @@ }, "node_modules/@vitest/runner": { "version": "3.2.4", - "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-3.2.4.tgz", - "integrity": "sha512-oukfKT9Mk41LreEW09vt45f8wx7DordoWUZMYdY/cyAk7w5TWkTRCNZYF7sX7n2wB7jyGAl74OxgwhPgKaqDMQ==", "dev": true, "license": "MIT", "dependencies": { @@ -7359,8 +5703,6 @@ }, "node_modules/@vitest/snapshot": { "version": "3.2.4", - "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-3.2.4.tgz", - "integrity": "sha512-dEYtS7qQP2CjU27QBC5oUOxLE/v5eLkGqPE0ZKEIDGMs4vKWe7IjgLOeauHsR0D5YuuycGRO5oSRXnwnmA78fQ==", "dev": true, "license": "MIT", "dependencies": { @@ -7374,8 +5716,6 @@ }, "node_modules/@vitest/spy": { "version": "3.2.4", - "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-3.2.4.tgz", - "integrity": "sha512-vAfasCOe6AIK70iP5UD11Ac4siNUNJ9i/9PZ3NKx07sG6sUxeag1LWdNrMWeKKYBLlzuK+Gn65Yd5nyL6ds+nw==", "dev": true, "license": "MIT", "dependencies": { @@ -7387,8 +5727,6 @@ }, "node_modules/@vitest/utils": { "version": "3.2.4", - "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-3.2.4.tgz", - "integrity": "sha512-fB2V0JFrQSMsCo9HiSq3Ezpdv4iYaXRG1Sx8edX3MwxfyNn83mKiGzOcH+Fkxt4MHxr3y42fQi1oeAInqgX2QA==", "dev": true, "license": "MIT", "dependencies": { @@ -7400,13 +5738,6 @@ "url": "https://opencollective.com/vitest" } }, - "node_modules/@webcontainer/env": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@webcontainer/env/-/env-1.1.1.tgz", - "integrity": "sha512-6aN99yL695Hi9SuIk1oC88l9o0gmxL1nGWWQ/kNy81HigJ0FoaoTXpytCj6ItzgyCEwA9kF1wixsTuv5cjsgng==", - "dev": true, - "license": "MIT" - }, "node_modules/abort-controller": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", @@ -7423,8 +5754,6 @@ }, "node_modules/acorn": { "version": "8.16.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz", - "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==", "license": "MIT", "bin": { "acorn": "bin/acorn" @@ -7435,8 +5764,6 @@ }, "node_modules/acorn-jsx": { "version": "5.3.2", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", - "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", "dev": true, "license": "MIT", "peerDependencies": { @@ -7483,8 +5810,6 @@ }, "node_modules/ajv": { "version": "6.14.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.14.0.tgz", - "integrity": "sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw==", "dev": true, "license": "MIT", "dependencies": { @@ -7500,8 +5825,6 @@ }, "node_modules/ansi-escapes": { "version": "4.3.2", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", - "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", "dev": true, "license": "MIT", "dependencies": { @@ -7516,21 +5839,19 @@ }, "node_modules/ansi-regex": { "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "license": "MIT", "engines": { "node": ">=8" } }, "node_modules/ansi-styles": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", - "dev": true, + "version": "4.3.0", "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, "engines": { - "node": ">=10" + "node": ">=8" }, "funding": { "url": "https://github.com/chalk/ansi-styles?sponsor=1" @@ -7538,8 +5859,6 @@ }, "node_modules/ansis": { "version": "3.17.0", - "resolved": "https://registry.npmjs.org/ansis/-/ansis-3.17.0.tgz", - "integrity": "sha512-0qWUglt9JEqLFr3w1I1pbrChn1grhaiAR2ocX1PP/flRmxgtwTzPFFFnfIlD6aMOLQZgSuCRlidD70lvx8yhzg==", "dev": true, "license": "ISC", "engines": { @@ -7555,15 +5874,15 @@ "peer": true }, "node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "license": "Python-2.0" + "version": "1.0.10", + "dev": true, + "license": "MIT", + "dependencies": { + "sprintf-js": "~1.0.2" + } }, "node_modules/aria-hidden": { "version": "1.2.6", - "resolved": "https://registry.npmjs.org/aria-hidden/-/aria-hidden-1.2.6.tgz", - "integrity": "sha512-ik3ZgC9dY/lYVVM++OISsaYDeg1tb0VtP5uL3ouh1koGOaUMDPpbFIei4JkFimWUFPn90sbMNMXQAIVOlnYKJA==", "license": "MIT", "dependencies": { "tslib": "^2.0.0" @@ -7574,8 +5893,6 @@ }, "node_modules/aria-query": { "version": "5.3.0", - "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.0.tgz", - "integrity": "sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -7584,8 +5901,6 @@ }, "node_modules/array-buffer-byte-length": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.2.tgz", - "integrity": "sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==", "dev": true, "license": "MIT", "dependencies": { @@ -7601,8 +5916,6 @@ }, "node_modules/array-includes": { "version": "3.1.9", - "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.9.tgz", - "integrity": "sha512-FmeCCAenzH0KH381SPT5FZmiA/TmpndpcaShhfgEN9eCVjnFBqq3l1xrI42y8+PPLI6hypzou4GXw00WHmPBLQ==", "dev": true, "license": "MIT", "dependencies": { @@ -7634,8 +5947,6 @@ }, "node_modules/array.prototype.findlast": { "version": "1.2.5", - "resolved": "https://registry.npmjs.org/array.prototype.findlast/-/array.prototype.findlast-1.2.5.tgz", - "integrity": "sha512-CVvd6FHg1Z3POpBLxO6E6zr+rSKEQ9L6rZHAaY7lLfhKsWYUBBOuMs0e9o24oopj6H+geRCX0YJ+TJLBK2eHyQ==", "dev": true, "license": "MIT", "dependencies": { @@ -7655,8 +5966,6 @@ }, "node_modules/array.prototype.flat": { "version": "1.3.3", - "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.3.tgz", - "integrity": "sha512-rwG/ja1neyLqCuGZ5YYrznA62D4mZXg0i1cIskIUKSiqF3Cje9/wXAls9B9s1Wa2fomMsIv8czB8jZcPmxCXFg==", "dev": true, "license": "MIT", "dependencies": { @@ -7674,8 +5983,6 @@ }, "node_modules/array.prototype.flatmap": { "version": "1.3.3", - "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.3.tgz", - "integrity": "sha512-Y7Wt51eKJSyi80hFrJCePGGNo5ktJCslFuboqJsbf57CCPcm5zztluPlc4/aD8sWsKvlwatezpV4U1efk8kpjg==", "dev": true, "license": "MIT", "dependencies": { @@ -7693,8 +6000,6 @@ }, "node_modules/array.prototype.tosorted": { "version": "1.1.4", - "resolved": "https://registry.npmjs.org/array.prototype.tosorted/-/array.prototype.tosorted-1.1.4.tgz", - "integrity": "sha512-p6Fx8B7b7ZhL/gmUsAy0D15WhvDccw3mnGNbZpi3pmeJdxtWsj2jEaI4Y6oo3XiHfzuSgPwKc04MYt6KgvC/wA==", "dev": true, "license": "MIT", "dependencies": { @@ -7710,8 +6015,6 @@ }, "node_modules/arraybuffer.prototype.slice": { "version": "1.0.4", - "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.4.tgz", - "integrity": "sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==", "dev": true, "license": "MIT", "dependencies": { @@ -7732,14 +6035,10 @@ }, "node_modules/asap": { "version": "2.0.6", - "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", - "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==", "license": "MIT" }, "node_modules/assert": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/assert/-/assert-2.1.0.tgz", - "integrity": "sha512-eLHpSK/Y4nhMJ07gDaAzoX/XAKS8PSaojml3M0DM4JpV1LAi5JOJ/p6H/XWrl8L+DzVEvVCW1z3vWAaB9oTsQw==", "license": "MIT", "peer": true, "dependencies": { @@ -7752,8 +6051,6 @@ }, "node_modules/assertion-error": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz", - "integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==", "dev": true, "license": "MIT", "engines": { @@ -7762,8 +6059,6 @@ }, "node_modules/ast-types": { "version": "0.16.1", - "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.16.1.tgz", - "integrity": "sha512-6t10qk83GOG8p0vKmaCr8eiilZwO171AvbROMtvvNiwrTly62t+7XkA8RdIIVbpMhCASAsxgAzdRSwh6nw/5Dg==", "license": "MIT", "dependencies": { "tslib": "^2.0.1" @@ -7772,17 +6067,36 @@ "node": ">=4" } }, + "node_modules/ast-v8-to-istanbul": { + "version": "0.3.12", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.31", + "estree-walker": "^3.0.3", + "js-tokens": "^10.0.0" + } + }, + "node_modules/ast-v8-to-istanbul/node_modules/estree-walker": { + "version": "3.0.3", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0" + } + }, + "node_modules/ast-v8-to-istanbul/node_modules/js-tokens": { + "version": "10.0.0", + "dev": true, + "license": "MIT" + }, "node_modules/async": { "version": "3.2.6", - "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", - "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==", "dev": true, "license": "MIT" }, "node_modules/async-function": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/async-function/-/async-function-1.0.0.tgz", - "integrity": "sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==", "dev": true, "license": "MIT", "engines": { @@ -7791,15 +6105,11 @@ }, "node_modules/asynckit": { "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", "license": "MIT", "peer": true }, "node_modules/attr-accept": { "version": "2.2.5", - "resolved": "https://registry.npmjs.org/attr-accept/-/attr-accept-2.2.5.tgz", - "integrity": "sha512-0bDNnY/u6pPwHDMoF0FieU354oBi0a8rD9FcsLwzcGWbc8KS8KPIi7y+s13OlVY+gMWc/9xEMUgNE6Qm8ZllYQ==", "license": "MIT", "engines": { "node": ">=4" @@ -7807,8 +6117,6 @@ }, "node_modules/available-typed-arrays": { "version": "1.0.7", - "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", - "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", "license": "MIT", "dependencies": { "possible-typed-array-names": "^1.0.0" @@ -7846,8 +6154,6 @@ }, "node_modules/babel-plugin-macros": { "version": "2.8.0", - "resolved": "https://registry.npmjs.org/babel-plugin-macros/-/babel-plugin-macros-2.8.0.tgz", - "integrity": "sha512-SEP5kJpfGYqYKpBrj5XU3ahw5p5GOHJ0U5ssOSQ/WBVdwkD2Dzlce95exQTs3jOVWPPKLBN2rlEWkCK7dSmLvg==", "dev": true, "license": "MIT", "dependencies": { @@ -7858,8 +6164,6 @@ }, "node_modules/babel-plugin-macros/node_modules/cosmiconfig": { "version": "6.0.0", - "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-6.0.0.tgz", - "integrity": "sha512-xb3ZL6+L8b9JLLCx3ZdoZy4+2ECphCMo2PwqgP1tlfVq6M6YReyzBJtvWWtbDSpNr9hn96pkCiZqUcFEc+54Qg==", "dev": true, "license": "MIT", "dependencies": { @@ -7875,8 +6179,6 @@ }, "node_modules/babel-plugin-macros/node_modules/import-fresh": { "version": "3.3.1", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", - "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", "dev": true, "license": "MIT", "dependencies": { @@ -7892,8 +6194,6 @@ }, "node_modules/babel-plugin-macros/node_modules/parse-json": { "version": "5.2.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", - "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", "dev": true, "license": "MIT", "dependencies": { @@ -7911,28 +6211,14 @@ }, "node_modules/babel-plugin-macros/node_modules/resolve-from": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", "dev": true, "license": "MIT", "engines": { "node": ">=4" } }, - "node_modules/babel-plugin-macros/node_modules/yaml": { - "version": "1.10.3", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.3.tgz", - "integrity": "sha512-vIYeF1u3CjlhAFekPPAk2h/Kv4T3mAkMox5OymRiJQB0spDP10LHvt+K7G9Ny6NuuMAb25/6n1qyUjAcGNf/AA==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">= 6" - } - }, "node_modules/babel-plugin-relay": { "version": "19.0.0", - "resolved": "https://registry.npmjs.org/babel-plugin-relay/-/babel-plugin-relay-19.0.0.tgz", - "integrity": "sha512-7K0UnVyagSgzexy28KcEGu18/ms8wgJd2AufZshiKEaZXH/5XwtK2sh+6wKj/8U7xTGSudBuze39Se+CxSlxsw==", "dev": true, "license": "MIT", "dependencies": { @@ -7943,8 +6229,6 @@ }, "node_modules/babel-plugin-relay/node_modules/graphql": { "version": "15.3.0", - "resolved": "https://registry.npmjs.org/graphql/-/graphql-15.3.0.tgz", - "integrity": "sha512-GTCJtzJmkFLWRfFJuoo9RWWa/FfamUHgiFosxi/X1Ani4AVWbeyBenZTNX6dM+7WSbbFfTo/25eh0LLkwHMw2w==", "dev": true, "license": "MIT", "engines": { @@ -7953,8 +6237,6 @@ }, "node_modules/bail": { "version": "2.0.2", - "resolved": "https://registry.npmjs.org/bail/-/bail-2.0.2.tgz", - "integrity": "sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==", "license": "MIT", "funding": { "type": "github", @@ -7962,14 +6244,9 @@ } }, "node_modules/balanced-match": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", - "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "version": "1.0.2", "dev": true, - "license": "MIT", - "engines": { - "node": "18 || 20 || >=22" - } + "license": "MIT" }, "node_modules/base64-js": { "version": "1.5.1", @@ -7993,9 +6270,7 @@ "license": "MIT" }, "node_modules/baseline-browser-mapping": { - "version": "2.10.13", - "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.13.tgz", - "integrity": "sha512-BL2sTuHOdy0YT1lYieUxTw/QMtPBC3pmlJC6xk8BBYVv6vcw3SGdKemQ+Xsx9ik2F/lYDO9tqsFQH1r9PFuHKw==", + "version": "2.10.0", "dev": true, "license": "Apache-2.0", "bin": { @@ -8019,16 +6294,11 @@ } }, "node_modules/brace-expansion": { - "version": "5.0.5", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.5.tgz", - "integrity": "sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ==", + "version": "2.0.2", "dev": true, "license": "MIT", "dependencies": { - "balanced-match": "^4.0.2" - }, - "engines": { - "node": "18 || 20 || >=22" + "balanced-match": "^1.0.0" } }, "node_modules/braces": { @@ -8045,9 +6315,7 @@ } }, "node_modules/browserslist": { - "version": "4.28.2", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.2.tgz", - "integrity": "sha512-48xSriZYYg+8qXna9kwqjIVzuQxi+KYWp2+5nCYnYKPTr0LvD89Jqk2Or5ogxz0NUMfIjhh2lIUX/LyX9B4oIg==", + "version": "4.28.1", "dev": true, "funding": [ { @@ -8065,11 +6333,11 @@ ], "license": "MIT", "dependencies": { - "baseline-browser-mapping": "^2.10.12", - "caniuse-lite": "^1.0.30001782", - "electron-to-chromium": "^1.5.328", - "node-releases": "^2.0.36", - "update-browserslist-db": "^1.2.3" + "baseline-browser-mapping": "^2.9.0", + "caniuse-lite": "^1.0.30001759", + "electron-to-chromium": "^1.5.263", + "node-releases": "^2.0.27", + "update-browserslist-db": "^1.2.0" }, "bin": { "browserslist": "cli.js" @@ -8086,10 +6354,13 @@ "license": "BSD-3-Clause", "peer": true }, + "node_modules/buffer-from": { + "version": "1.1.2", + "dev": true, + "license": "MIT" + }, "node_modules/bundle-name": { "version": "4.1.0", - "resolved": "https://registry.npmjs.org/bundle-name/-/bundle-name-4.1.0.tgz", - "integrity": "sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q==", "dev": true, "license": "MIT", "dependencies": { @@ -8104,8 +6375,6 @@ }, "node_modules/cac": { "version": "6.7.14", - "resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz", - "integrity": "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==", "dev": true, "license": "MIT", "engines": { @@ -8114,8 +6383,6 @@ }, "node_modules/call-bind": { "version": "1.0.8", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz", - "integrity": "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==", "license": "MIT", "dependencies": { "call-bind-apply-helpers": "^1.0.0", @@ -8132,8 +6399,6 @@ }, "node_modules/call-bind-apply-helpers": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", - "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", "license": "MIT", "dependencies": { "es-errors": "^1.3.0", @@ -8145,8 +6410,6 @@ }, "node_modules/call-bound": { "version": "1.0.4", - "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", - "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", "license": "MIT", "dependencies": { "call-bind-apply-helpers": "^1.0.2", @@ -8161,8 +6424,6 @@ }, "node_modules/caller-callsite": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/caller-callsite/-/caller-callsite-2.0.0.tgz", - "integrity": "sha512-JuG3qI4QOftFsZyOn1qq87fq5grLIyk1JYd5lJmdA+fG7aQ9pA/i3JIJGcO3q0MrRcHlOt1U+ZeHW8Dq9axALQ==", "dev": true, "license": "MIT", "dependencies": { @@ -8174,8 +6435,6 @@ }, "node_modules/caller-path": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/caller-path/-/caller-path-2.0.0.tgz", - "integrity": "sha512-MCL3sf6nCSXOwCTzvPKhN18TU7AHTvdtam8DAogxcrJ8Rjfbbg7Lgng64H9Iy+vUV6VGFClN/TyxBkAebLRR4A==", "dev": true, "license": "MIT", "dependencies": { @@ -8187,8 +6446,6 @@ }, "node_modules/callsites": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-2.0.0.tgz", - "integrity": "sha512-ksWePWBloaWPxJYQ8TL0JHvtci6G5QTKwQ95RcWAa/lzoAKuAOflGdAK92hpHXjkwb8zLxoLNUoNYZgVsaJzvQ==", "dev": true, "license": "MIT", "engines": { @@ -8220,9 +6477,7 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001784", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001784.tgz", - "integrity": "sha512-WU346nBTklUV9YfUl60fqRbU5ZqyXlqvo1SgigE1OAXK5bFL8LL9q1K7aap3N739l4BvNqnkm3YrGHiY9sfUQw==", + "version": "1.0.30001775", "dev": true, "funding": [ { @@ -8242,8 +6497,6 @@ }, "node_modules/ccount": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/ccount/-/ccount-2.0.1.tgz", - "integrity": "sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==", "license": "MIT", "funding": { "type": "github", @@ -8252,8 +6505,6 @@ }, "node_modules/chai": { "version": "5.3.3", - "resolved": "https://registry.npmjs.org/chai/-/chai-5.3.3.tgz", - "integrity": "sha512-4zNhdJD/iOjSH0A05ea+Ke6MU5mmpQcbQsSOkgdaUMJ9zTlDTD/GYlwohmIE2u0gaxHYiVHEn1Fw9mZ/ktJWgw==", "dev": true, "license": "MIT", "dependencies": { @@ -8269,8 +6520,6 @@ }, "node_modules/chalk": { "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dev": true, "license": "MIT", "dependencies": { @@ -8284,26 +6533,8 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/chalk/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, "node_modules/chalk/node_modules/supports-color": { "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, "license": "MIT", "dependencies": { @@ -8315,15 +6546,11 @@ }, "node_modules/change-case": { "version": "5.4.4", - "resolved": "https://registry.npmjs.org/change-case/-/change-case-5.4.4.tgz", - "integrity": "sha512-HRQyTk2/YPEkt9TnUPbOpr64Uw3KOicFWPVBb+xiHvd6eBx/qPr9xqfBFDT8P2vWsvvz4jbEkfDe71W3VyNu2w==", "dev": true, "license": "MIT" }, "node_modules/character-entities": { "version": "2.0.2", - "resolved": "https://registry.npmjs.org/character-entities/-/character-entities-2.0.2.tgz", - "integrity": "sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==", "license": "MIT", "funding": { "type": "github", @@ -8332,8 +6559,6 @@ }, "node_modules/character-entities-html4": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/character-entities-html4/-/character-entities-html4-2.1.0.tgz", - "integrity": "sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==", "license": "MIT", "funding": { "type": "github", @@ -8342,8 +6567,6 @@ }, "node_modules/character-entities-legacy": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-3.0.0.tgz", - "integrity": "sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==", "license": "MIT", "funding": { "type": "github", @@ -8352,8 +6575,6 @@ }, "node_modules/character-reference-invalid": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/character-reference-invalid/-/character-reference-invalid-2.0.1.tgz", - "integrity": "sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw==", "license": "MIT", "funding": { "type": "github", @@ -8362,8 +6583,6 @@ }, "node_modules/charenc": { "version": "0.0.2", - "resolved": "https://registry.npmjs.org/charenc/-/charenc-0.0.2.tgz", - "integrity": "sha512-yrLQ/yVUFXkzg7EDQsPieE/53+0RlaWTs+wBrvW36cyilJ2SaDWfl4Yj7MtLTXleV9uEKefbAGUPv2/iWSooRA==", "license": "BSD-3-Clause", "peer": true, "engines": { @@ -8372,8 +6591,6 @@ }, "node_modules/check-error": { "version": "2.1.3", - "resolved": "https://registry.npmjs.org/check-error/-/check-error-2.1.3.tgz", - "integrity": "sha512-PAJdDJusoxnwm1VwW07VWwUN1sl7smmC3OKggvndJFadxxDRyFJBX/ggnu/KE4kQAB7a3Dp8f/YXC1FlUprWmA==", "dev": true, "license": "MIT", "engines": { @@ -8381,35 +6598,35 @@ } }, "node_modules/chevrotain": { - "version": "11.1.2", - "resolved": "https://registry.npmjs.org/chevrotain/-/chevrotain-11.1.2.tgz", - "integrity": "sha512-opLQzEVriiH1uUQ4Kctsd49bRoFDXGGSC4GUqj7pGyxM3RehRhvTlZJc1FL/Flew2p5uwxa1tUDWKzI4wNM8pg==", + "version": "12.0.0", + "resolved": "https://registry.npmjs.org/chevrotain/-/chevrotain-12.0.0.tgz", + "integrity": "sha512-csJvb+6kEiQaqo1woTdSAuOWdN0WTLIydkKrBnS+V5gZz0oqBrp4kQ35519QgK6TpBThiG3V1vNSHlIkv4AglQ==", "license": "Apache-2.0", "dependencies": { - "@chevrotain/cst-dts-gen": "11.1.2", - "@chevrotain/gast": "11.1.2", - "@chevrotain/regexp-to-ast": "11.1.2", - "@chevrotain/types": "11.1.2", - "@chevrotain/utils": "11.1.2", - "lodash-es": "4.17.23" + "@chevrotain/cst-dts-gen": "12.0.0", + "@chevrotain/gast": "12.0.0", + "@chevrotain/regexp-to-ast": "12.0.0", + "@chevrotain/types": "12.0.0", + "@chevrotain/utils": "12.0.0" + }, + "engines": { + "node": ">=22.0.0" } }, "node_modules/chevrotain-allstar": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/chevrotain-allstar/-/chevrotain-allstar-0.3.1.tgz", - "integrity": "sha512-b7g+y9A0v4mxCW1qUhf3BSVPg+/NvGErk/dOkrDaHA0nQIQGAtrOjlX//9OQtRlSCy+x9rfB5N8yC71lH1nvMw==", + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/chevrotain-allstar/-/chevrotain-allstar-0.4.1.tgz", + "integrity": "sha512-PvVJm3oGqrveUVW2Vt/eZGeiAIsJszYweUcYwcskg9e+IubNYKKD+rHHem7A6XVO22eDAL+inxNIGAzZ/VIWlA==", "license": "MIT", "dependencies": { "lodash-es": "^4.17.21" }, "peerDependencies": { - "chevrotain": "^11.0.0" + "chevrotain": "^12.0.0" } }, "node_modules/chromatic": { "version": "13.3.5", - "resolved": "https://registry.npmjs.org/chromatic/-/chromatic-13.3.5.tgz", - "integrity": "sha512-MzPhxpl838qJUo0A55osCF2ifwPbjcIPeElr1d4SHcjnHoIcg7l1syJDrAYK/a+PcCBrOGi06jPNpQAln5hWgw==", "dev": true, "license": "MIT", "bin": { @@ -8432,8 +6649,6 @@ }, "node_modules/clean-stack": { "version": "3.0.1", - "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-3.0.1.tgz", - "integrity": "sha512-lR9wNiMRcVQjSB3a7xXGLuz4cr4wJuuXlaAEbRutGowQTmlp7R72/DOgN21e8jdwblMWl9UOJMJXarX94pzKdg==", "dev": true, "license": "MIT", "dependencies": { @@ -8448,8 +6663,6 @@ }, "node_modules/cli-spinners": { "version": "2.9.2", - "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.2.tgz", - "integrity": "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==", "dev": true, "license": "MIT", "engines": { @@ -8461,8 +6674,6 @@ }, "node_modules/cliui": { "version": "8.0.1", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", - "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", "license": "ISC", "peer": true, "dependencies": { @@ -8476,8 +6687,6 @@ }, "node_modules/cliui/node_modules/strip-ansi": { "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "license": "MIT", "peer": true, "dependencies": { @@ -8489,8 +6698,6 @@ }, "node_modules/clsx": { "version": "2.1.1", - "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", - "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", "license": "MIT", "engines": { "node": ">=6" @@ -8498,8 +6705,6 @@ }, "node_modules/cmdk": { "version": "1.1.1", - "resolved": "https://registry.npmjs.org/cmdk/-/cmdk-1.1.1.tgz", - "integrity": "sha512-Vsv7kFaXm+ptHDMZ7izaRsP70GgrW9NBNGswt9OZaVBLlE0SNpDq8eu/VGXyF9r7M0azK3Wy7OlYXsuyYLFzHg==", "license": "MIT", "dependencies": { "@radix-ui/react-compose-refs": "^1.1.1", @@ -8521,8 +6726,6 @@ }, "node_modules/color-convert": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "license": "MIT", "dependencies": { "color-name": "~1.1.4" @@ -8533,14 +6736,10 @@ }, "node_modules/color-name": { "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "license": "MIT" }, "node_modules/combined-stream": { "version": "1.0.8", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", "license": "MIT", "peer": true, "dependencies": { @@ -8552,8 +6751,6 @@ }, "node_modules/comma-separated-tokens": { "version": "2.0.3", - "resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz", - "integrity": "sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==", "license": "MIT", "funding": { "type": "github", @@ -8562,17 +6759,13 @@ }, "node_modules/commander": { "version": "7.2.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", - "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", "license": "MIT", "engines": { "node": ">= 10" } }, "node_modules/comment-parser": { - "version": "1.4.6", - "resolved": "https://registry.npmjs.org/comment-parser/-/comment-parser-1.4.6.tgz", - "integrity": "sha512-ObxuY6vnbWTN6Od72xfwN9DbzC7Y2vv8u1Soi9ahRKL37gb6y1qk6/dgjs+3JWuXJHWvsg3BXIwzd/rkmAwavg==", + "version": "1.4.5", "dev": true, "license": "MIT", "engines": { @@ -8584,12 +6777,11 @@ "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", "dev": true, - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/confbox": { "version": "0.1.8", - "resolved": "https://registry.npmjs.org/confbox/-/confbox-0.1.8.tgz", - "integrity": "sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==", "license": "MIT" }, "node_modules/console-table-printer": { @@ -8604,15 +6796,11 @@ }, "node_modules/convert-source-map": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", - "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", "dev": true, "license": "MIT" }, "node_modules/cookie": { "version": "1.1.1", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-1.1.1.tgz", - "integrity": "sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ==", "license": "MIT", "engines": { "node": ">=18" @@ -8624,8 +6812,6 @@ }, "node_modules/cose-base": { "version": "1.0.3", - "resolved": "https://registry.npmjs.org/cose-base/-/cose-base-1.0.3.tgz", - "integrity": "sha512-s9whTXInMSgAp/NVXVNuVxVKzGH2qck3aQlVHxDCdAEPgtMKwc4Wq6/QKhgdEdgbLSi9rBTAcPoRa6JpiG4ksg==", "license": "MIT", "dependencies": { "layout-base": "^1.0.0" @@ -8633,8 +6819,6 @@ }, "node_modules/cosmiconfig": { "version": "5.2.1", - "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-5.2.1.tgz", - "integrity": "sha512-H65gsXo1SKjf8zmrJ67eJk8aIRKV5ff2D4uKZIBZShbhGSpEmsQOPW/SKMKYhSTrqR7ufy6RP69rPogdaPh/kA==", "dev": true, "license": "MIT", "dependencies": { @@ -8647,30 +6831,6 @@ "node": ">=4" } }, - "node_modules/cosmiconfig/node_modules/argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dev": true, - "license": "MIT", - "dependencies": { - "sprintf-js": "~1.0.2" - } - }, - "node_modules/cosmiconfig/node_modules/js-yaml": { - "version": "3.14.2", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.2.tgz", - "integrity": "sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg==", - "dev": true, - "license": "MIT", - "dependencies": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, "node_modules/create-require": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", @@ -8687,8 +6847,6 @@ }, "node_modules/cross-fetch": { "version": "3.2.0", - "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.2.0.tgz", - "integrity": "sha512-Q+xVJLoGOeIMXZmbUK4HYk+69cQH6LudR0Vu/pRm2YlU/hDV9CiS0gKUMaWY5f2NeUH9C1nV3bsTlCo0FsTV1Q==", "license": "MIT", "dependencies": { "node-fetch": "^2.7.0" @@ -8696,8 +6854,6 @@ }, "node_modules/cross-spawn": { "version": "7.0.6", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", - "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", "dev": true, "license": "MIT", "dependencies": { @@ -8711,8 +6867,6 @@ }, "node_modules/crypt": { "version": "0.0.2", - "resolved": "https://registry.npmjs.org/crypt/-/crypt-0.0.2.tgz", - "integrity": "sha512-mCxBlsHFYh9C+HVpiEacem8FEBnMXgU9gy4zmNC+SXAZNB/1idgp/aulFJ4FgCi7GPEVbfyng092GqL2k2rmow==", "license": "BSD-3-Clause", "peer": true, "engines": { @@ -8721,15 +6875,11 @@ }, "node_modules/css.escape": { "version": "1.5.1", - "resolved": "https://registry.npmjs.org/css.escape/-/css.escape-1.5.1.tgz", - "integrity": "sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg==", "dev": true, "license": "MIT" }, "node_modules/cssesc": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", - "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", "dev": true, "license": "MIT", "bin": { @@ -8741,14 +6891,10 @@ }, "node_modules/csstype": { "version": "3.2.3", - "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", - "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", "license": "MIT" }, "node_modules/cytoscape": { "version": "3.33.1", - "resolved": "https://registry.npmjs.org/cytoscape/-/cytoscape-3.33.1.tgz", - "integrity": "sha512-iJc4TwyANnOGR1OmWhsS9ayRS3s+XQ185FmuHObThD+5AeJCakAAbWv8KimMTt08xCCLNgneQwFp+JRJOr9qGQ==", "license": "MIT", "engines": { "node": ">=0.10" @@ -8756,8 +6902,6 @@ }, "node_modules/cytoscape-cose-bilkent": { "version": "4.1.0", - "resolved": "https://registry.npmjs.org/cytoscape-cose-bilkent/-/cytoscape-cose-bilkent-4.1.0.tgz", - "integrity": "sha512-wgQlVIUJF13Quxiv5e1gstZ08rnZj2XaLHGoFMYXz7SkNfCDOOteKBE6SYRfA9WxxI/iBc3ajfDoc6hb/MRAHQ==", "license": "MIT", "dependencies": { "cose-base": "^1.0.0" @@ -8768,8 +6912,6 @@ }, "node_modules/cytoscape-fcose": { "version": "2.2.0", - "resolved": "https://registry.npmjs.org/cytoscape-fcose/-/cytoscape-fcose-2.2.0.tgz", - "integrity": "sha512-ki1/VuRIHFCzxWNrsshHYPs6L7TvLu3DL+TyIGEsRcvVERmxokbf5Gdk7mFxZnTdiGtnA4cfSmjZJMviqSuZrQ==", "license": "MIT", "dependencies": { "cose-base": "^2.2.0" @@ -8780,8 +6922,6 @@ }, "node_modules/cytoscape-fcose/node_modules/cose-base": { "version": "2.2.0", - "resolved": "https://registry.npmjs.org/cose-base/-/cose-base-2.2.0.tgz", - "integrity": "sha512-AzlgcsCbUMymkADOJtQm3wO9S3ltPfYOFD5033keQn9NJzIbtnZj+UdBJe7DYml/8TdbtHJW3j58SOnKhWY/5g==", "license": "MIT", "dependencies": { "layout-base": "^2.0.0" @@ -8789,14 +6929,10 @@ }, "node_modules/cytoscape-fcose/node_modules/layout-base": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/layout-base/-/layout-base-2.0.1.tgz", - "integrity": "sha512-dp3s92+uNI1hWIpPGH3jK2kxE2lMjdXdr+DH8ynZHpd6PUlH6x6cbuXnoMmiNumznqaNO31xu9e79F0uuZ0JFg==", "license": "MIT" }, "node_modules/d3": { "version": "7.9.0", - "resolved": "https://registry.npmjs.org/d3/-/d3-7.9.0.tgz", - "integrity": "sha512-e1U46jVP+w7Iut8Jt8ri1YsPOvFpg46k+K8TpCb0P+zjCkjkPnV7WzfDJzMHy1LnA+wj5pLT1wjO901gLXeEhA==", "license": "ISC", "dependencies": { "d3-array": "3", @@ -8836,8 +6972,6 @@ }, "node_modules/d3-array": { "version": "3.2.4", - "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-3.2.4.tgz", - "integrity": "sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==", "license": "ISC", "dependencies": { "internmap": "1 - 2" @@ -8848,8 +6982,6 @@ }, "node_modules/d3-axis": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/d3-axis/-/d3-axis-3.0.0.tgz", - "integrity": "sha512-IH5tgjV4jE/GhHkRV0HiVYPDtvfjHQlQfJHs0usq7M30XcSBvOotpmH1IgkcXsO/5gEQZD43B//fc7SRT5S+xw==", "license": "ISC", "engines": { "node": ">=12" @@ -8857,8 +6989,6 @@ }, "node_modules/d3-brush": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/d3-brush/-/d3-brush-3.0.0.tgz", - "integrity": "sha512-ALnjWlVYkXsVIGlOsuWH1+3udkYFI48Ljihfnh8FZPF2QS9o+PzGLBslO0PjzVoHLZ2KCVgAM8NVkXPJB2aNnQ==", "license": "ISC", "dependencies": { "d3-dispatch": "1 - 3", @@ -8873,8 +7003,6 @@ }, "node_modules/d3-chord": { "version": "3.0.1", - "resolved": "https://registry.npmjs.org/d3-chord/-/d3-chord-3.0.1.tgz", - "integrity": "sha512-VE5S6TNa+j8msksl7HwjxMHDM2yNK3XCkusIlpX5kwauBfXuyLAtNg9jCp/iHH61tgI4sb6R/EIMWCqEIdjT/g==", "license": "ISC", "dependencies": { "d3-path": "1 - 3" @@ -8885,8 +7013,6 @@ }, "node_modules/d3-color": { "version": "3.1.0", - "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-3.1.0.tgz", - "integrity": "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==", "license": "ISC", "engines": { "node": ">=12" @@ -8894,8 +7020,6 @@ }, "node_modules/d3-contour": { "version": "4.0.2", - "resolved": "https://registry.npmjs.org/d3-contour/-/d3-contour-4.0.2.tgz", - "integrity": "sha512-4EzFTRIikzs47RGmdxbeUvLWtGedDUNkTcmzoeyg4sP/dvCexO47AaQL7VKy/gul85TOxw+IBgA8US2xwbToNA==", "license": "ISC", "dependencies": { "d3-array": "^3.2.0" @@ -8906,8 +7030,6 @@ }, "node_modules/d3-delaunay": { "version": "6.0.4", - "resolved": "https://registry.npmjs.org/d3-delaunay/-/d3-delaunay-6.0.4.tgz", - "integrity": "sha512-mdjtIZ1XLAM8bm/hx3WwjfHt6Sggek7qH043O8KEjDXN40xi3vx/6pYSVTwLjEgiXQTbvaouWKynLBiUZ6SK6A==", "license": "ISC", "dependencies": { "delaunator": "5" @@ -8918,8 +7040,6 @@ }, "node_modules/d3-dispatch": { "version": "3.0.1", - "resolved": "https://registry.npmjs.org/d3-dispatch/-/d3-dispatch-3.0.1.tgz", - "integrity": "sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg==", "license": "ISC", "engines": { "node": ">=12" @@ -8927,8 +7047,6 @@ }, "node_modules/d3-drag": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/d3-drag/-/d3-drag-3.0.0.tgz", - "integrity": "sha512-pWbUJLdETVA8lQNJecMxoXfH6x+mO2UQo8rSmZ+QqxcbyA3hfeprFgIT//HW2nlHChWeIIMwS2Fq+gEARkhTkg==", "license": "ISC", "dependencies": { "d3-dispatch": "1 - 3", @@ -8940,8 +7058,6 @@ }, "node_modules/d3-dsv": { "version": "3.0.1", - "resolved": "https://registry.npmjs.org/d3-dsv/-/d3-dsv-3.0.1.tgz", - "integrity": "sha512-UG6OvdI5afDIFP9w4G0mNq50dSOsXHJaRE8arAS5o9ApWnIElp8GZw1Dun8vP8OyHOZ/QJUKUJwxiiCCnUwm+Q==", "license": "ISC", "dependencies": { "commander": "7", @@ -8965,8 +7081,6 @@ }, "node_modules/d3-ease": { "version": "3.0.1", - "resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-3.0.1.tgz", - "integrity": "sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==", "license": "BSD-3-Clause", "engines": { "node": ">=12" @@ -8974,8 +7088,6 @@ }, "node_modules/d3-fetch": { "version": "3.0.1", - "resolved": "https://registry.npmjs.org/d3-fetch/-/d3-fetch-3.0.1.tgz", - "integrity": "sha512-kpkQIM20n3oLVBKGg6oHrUchHM3xODkTzjMoj7aWQFq5QEM+R6E4WkzT5+tojDY7yjez8KgCBRoj4aEr99Fdqw==", "license": "ISC", "dependencies": { "d3-dsv": "1 - 3" @@ -8986,8 +7098,6 @@ }, "node_modules/d3-force": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/d3-force/-/d3-force-3.0.0.tgz", - "integrity": "sha512-zxV/SsA+U4yte8051P4ECydjD/S+qeYtnaIyAs9tgHCqfguma/aAQDjo85A9Z6EKhBirHRJHXIgJUlffT4wdLg==", "license": "ISC", "dependencies": { "d3-dispatch": "1 - 3", @@ -9000,8 +7110,6 @@ }, "node_modules/d3-format": { "version": "3.1.2", - "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-3.1.2.tgz", - "integrity": "sha512-AJDdYOdnyRDV5b6ArilzCPPwc1ejkHcoyFarqlPqT7zRYjhavcT3uSrqcMvsgh2CgoPbK3RCwyHaVyxYcP2Arg==", "license": "ISC", "engines": { "node": ">=12" @@ -9009,8 +7117,6 @@ }, "node_modules/d3-geo": { "version": "3.1.1", - "resolved": "https://registry.npmjs.org/d3-geo/-/d3-geo-3.1.1.tgz", - "integrity": "sha512-637ln3gXKXOwhalDzinUgY83KzNWZRKbYubaG+fGVuc/dxO64RRljtCTnf5ecMyE1RIdtqpkVcq0IbtU2S8j2Q==", "license": "ISC", "dependencies": { "d3-array": "2.5.0 - 3" @@ -9021,8 +7127,6 @@ }, "node_modules/d3-hierarchy": { "version": "3.1.2", - "resolved": "https://registry.npmjs.org/d3-hierarchy/-/d3-hierarchy-3.1.2.tgz", - "integrity": "sha512-FX/9frcub54beBdugHjDCdikxThEqjnR93Qt7PvQTOHxyiNCAlvMrHhclk3cD5VeAaq9fxmfRp+CnWw9rEMBuA==", "license": "ISC", "engines": { "node": ">=12" @@ -9030,8 +7134,6 @@ }, "node_modules/d3-interpolate": { "version": "3.0.1", - "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-3.0.1.tgz", - "integrity": "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==", "license": "ISC", "dependencies": { "d3-color": "1 - 3" @@ -9042,8 +7144,6 @@ }, "node_modules/d3-path": { "version": "3.1.0", - "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-3.1.0.tgz", - "integrity": "sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==", "license": "ISC", "engines": { "node": ">=12" @@ -9051,8 +7151,6 @@ }, "node_modules/d3-polygon": { "version": "3.0.1", - "resolved": "https://registry.npmjs.org/d3-polygon/-/d3-polygon-3.0.1.tgz", - "integrity": "sha512-3vbA7vXYwfe1SYhED++fPUQlWSYTTGmFmQiany/gdbiWgU/iEyQzyymwL9SkJjFFuCS4902BSzewVGsHHmHtXg==", "license": "ISC", "engines": { "node": ">=12" @@ -9060,8 +7158,6 @@ }, "node_modules/d3-quadtree": { "version": "3.0.1", - "resolved": "https://registry.npmjs.org/d3-quadtree/-/d3-quadtree-3.0.1.tgz", - "integrity": "sha512-04xDrxQTDTCFwP5H6hRhsRcb9xxv2RzkcsygFzmkSIOJy3PeRJP7sNk3VRIbKXcog561P9oU0/rVH6vDROAgUw==", "license": "ISC", "engines": { "node": ">=12" @@ -9069,8 +7165,6 @@ }, "node_modules/d3-random": { "version": "3.0.1", - "resolved": "https://registry.npmjs.org/d3-random/-/d3-random-3.0.1.tgz", - "integrity": "sha512-FXMe9GfxTxqd5D6jFsQ+DJ8BJS4E/fT5mqqdjovykEB2oFbTMDVdg1MGFxfQW+FBOGoB++k8swBrgwSHT1cUXQ==", "license": "ISC", "engines": { "node": ">=12" @@ -9078,8 +7172,6 @@ }, "node_modules/d3-sankey": { "version": "0.12.3", - "resolved": "https://registry.npmjs.org/d3-sankey/-/d3-sankey-0.12.3.tgz", - "integrity": "sha512-nQhsBRmM19Ax5xEIPLMY9ZmJ/cDvd1BG3UVvt5h3WRxKg5zGRbvnteTyWAbzeSvlh3tW7ZEmq4VwR5mB3tutmQ==", "license": "BSD-3-Clause", "dependencies": { "d3-array": "1 - 2", @@ -9088,8 +7180,6 @@ }, "node_modules/d3-sankey/node_modules/d3-array": { "version": "2.12.1", - "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-2.12.1.tgz", - "integrity": "sha512-B0ErZK/66mHtEsR1TkPEEkwdy+WDesimkM5gpZr5Dsg54BiTA5RXtYW5qTLIAcekaS9xfZrzBLF/OAkB3Qn1YQ==", "license": "BSD-3-Clause", "dependencies": { "internmap": "^1.0.0" @@ -9097,14 +7187,10 @@ }, "node_modules/d3-sankey/node_modules/d3-path": { "version": "1.0.9", - "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-1.0.9.tgz", - "integrity": "sha512-VLaYcn81dtHVTjEHd8B+pbe9yHWpXKZUC87PzoFmsFrJqgFwDe/qxfp5MlfsfM1V5E/iVt0MmEbWQ7FVIXh/bg==", "license": "BSD-3-Clause" }, "node_modules/d3-sankey/node_modules/d3-shape": { "version": "1.3.7", - "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-1.3.7.tgz", - "integrity": "sha512-EUkvKjqPFUAZyOlhY5gzCxCeI0Aep04LwIRpsZ/mLFelJiUfnK56jo5JMDSE7yyP2kLSb6LtF+S5chMk7uqPqw==", "license": "BSD-3-Clause", "dependencies": { "d3-path": "1" @@ -9112,14 +7198,10 @@ }, "node_modules/d3-sankey/node_modules/internmap": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/internmap/-/internmap-1.0.1.tgz", - "integrity": "sha512-lDB5YccMydFBtasVtxnZ3MRBHuaoE8GKsppq+EchKL2U4nK/DmEpPHNH8MZe5HkMtpSiTSOZwfN0tzYjO/lJEw==", "license": "ISC" }, "node_modules/d3-scale": { "version": "4.0.2", - "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-4.0.2.tgz", - "integrity": "sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==", "license": "ISC", "dependencies": { "d3-array": "2.10.0 - 3", @@ -9134,8 +7216,6 @@ }, "node_modules/d3-scale-chromatic": { "version": "3.1.0", - "resolved": "https://registry.npmjs.org/d3-scale-chromatic/-/d3-scale-chromatic-3.1.0.tgz", - "integrity": "sha512-A3s5PWiZ9YCXFye1o246KoscMWqf8BsD9eRiJ3He7C9OBaxKhAd5TFCdEx/7VbKtxxTsu//1mMJFrEt572cEyQ==", "license": "ISC", "dependencies": { "d3-color": "1 - 3", @@ -9147,8 +7227,6 @@ }, "node_modules/d3-selection": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-3.0.0.tgz", - "integrity": "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==", "license": "ISC", "engines": { "node": ">=12" @@ -9156,8 +7234,6 @@ }, "node_modules/d3-shape": { "version": "3.2.0", - "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-3.2.0.tgz", - "integrity": "sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==", "license": "ISC", "dependencies": { "d3-path": "^3.1.0" @@ -9168,8 +7244,6 @@ }, "node_modules/d3-time": { "version": "3.1.0", - "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-3.1.0.tgz", - "integrity": "sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==", "license": "ISC", "dependencies": { "d3-array": "2 - 3" @@ -9180,8 +7254,6 @@ }, "node_modules/d3-time-format": { "version": "4.1.0", - "resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-4.1.0.tgz", - "integrity": "sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==", "license": "ISC", "dependencies": { "d3-time": "1 - 3" @@ -9192,8 +7264,6 @@ }, "node_modules/d3-timer": { "version": "3.0.1", - "resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-3.0.1.tgz", - "integrity": "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==", "license": "ISC", "engines": { "node": ">=12" @@ -9201,8 +7271,6 @@ }, "node_modules/d3-transition": { "version": "3.0.1", - "resolved": "https://registry.npmjs.org/d3-transition/-/d3-transition-3.0.1.tgz", - "integrity": "sha512-ApKvfjsSR6tg06xrL434C0WydLr7JewBB3V+/39RMHsaXTOG0zmt/OAXeng5M5LBm0ojmxJrpomQVZ1aPvBL4w==", "license": "ISC", "dependencies": { "d3-color": "1 - 3", @@ -9220,8 +7288,6 @@ }, "node_modules/d3-zoom": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/d3-zoom/-/d3-zoom-3.0.0.tgz", - "integrity": "sha512-b8AmV3kfQaqWAuacbPuNbL6vahnOJflOhexLzMMNLga62+/nh0JzvJ0aO/5a5MVgUFGS7Hu1P9P03o3fJkDCyw==", "license": "ISC", "dependencies": { "d3-dispatch": "1 - 3", @@ -9236,8 +7302,6 @@ }, "node_modules/dagre-d3-es": { "version": "7.0.14", - "resolved": "https://registry.npmjs.org/dagre-d3-es/-/dagre-d3-es-7.0.14.tgz", - "integrity": "sha512-P4rFMVq9ESWqmOgK+dlXvOtLwYg0i7u0HBGJER0LZDJT2VHIPAMZ/riPxqJceWMStH5+E61QxFra9kIS3AqdMg==", "license": "MIT", "dependencies": { "d3": "^7.9.0", @@ -9246,8 +7310,6 @@ }, "node_modules/data-view-buffer": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.2.tgz", - "integrity": "sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ==", "dev": true, "license": "MIT", "dependencies": { @@ -9264,8 +7326,6 @@ }, "node_modules/data-view-byte-length": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/data-view-byte-length/-/data-view-byte-length-1.0.2.tgz", - "integrity": "sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ==", "dev": true, "license": "MIT", "dependencies": { @@ -9282,8 +7342,6 @@ }, "node_modules/data-view-byte-offset": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/data-view-byte-offset/-/data-view-byte-offset-1.0.1.tgz", - "integrity": "sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ==", "dev": true, "license": "MIT", "dependencies": { @@ -9299,15 +7357,11 @@ } }, "node_modules/dayjs": { - "version": "1.11.20", - "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.20.tgz", - "integrity": "sha512-YbwwqR/uYpeoP4pu043q+LTDLFBLApUP6VxRihdfNTqu4ubqMlGDLd6ErXhEgsyvY0K6nCs7nggYumAN+9uEuQ==", + "version": "1.11.19", "license": "MIT" }, "node_modules/debug": { "version": "4.4.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", - "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", "license": "MIT", "dependencies": { "ms": "^2.1.3" @@ -9333,8 +7387,6 @@ }, "node_modules/decode-named-character-reference": { "version": "1.3.0", - "resolved": "https://registry.npmjs.org/decode-named-character-reference/-/decode-named-character-reference-1.3.0.tgz", - "integrity": "sha512-GtpQYB283KrPp6nRw50q3U9/VfOutZOe103qlN7BPP6Ad27xYnOIWv4lPzo8HCAL+mMZofJ9KEy30fq6MfaK6Q==", "license": "MIT", "dependencies": { "character-entities": "^2.0.0" @@ -9346,8 +7398,6 @@ }, "node_modules/deep-eql": { "version": "5.0.2", - "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-5.0.2.tgz", - "integrity": "sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==", "dev": true, "license": "MIT", "engines": { @@ -9356,15 +7406,11 @@ }, "node_modules/deep-is": { "version": "0.1.4", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", - "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", "dev": true, "license": "MIT" }, "node_modules/deepmerge": { "version": "4.3.1", - "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", - "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", "license": "MIT", "engines": { "node": ">=0.10.0" @@ -9372,8 +7418,6 @@ }, "node_modules/default-browser": { "version": "5.5.0", - "resolved": "https://registry.npmjs.org/default-browser/-/default-browser-5.5.0.tgz", - "integrity": "sha512-H9LMLr5zwIbSxrmvikGuI/5KGhZ8E2zH3stkMgM5LpOWDutGM2JZaj460Udnf1a+946zc7YBgrqEWwbk7zHvGw==", "dev": true, "license": "MIT", "dependencies": { @@ -9389,8 +7433,6 @@ }, "node_modules/default-browser-id": { "version": "5.0.1", - "resolved": "https://registry.npmjs.org/default-browser-id/-/default-browser-id-5.0.1.tgz", - "integrity": "sha512-x1VCxdX4t+8wVfd1so/9w+vQ4vx7lKd2Qp5tDRutErwmR85OgmfX7RlLRMWafRMY7hbEiXIbudNrjOAPa/hL8Q==", "dev": true, "license": "MIT", "engines": { @@ -9402,8 +7444,6 @@ }, "node_modules/define-data-property": { "version": "1.1.4", - "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", - "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", "license": "MIT", "dependencies": { "es-define-property": "^1.0.0", @@ -9419,8 +7459,6 @@ }, "node_modules/define-lazy-prop": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-3.0.0.tgz", - "integrity": "sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==", "dev": true, "license": "MIT", "engines": { @@ -9432,8 +7470,6 @@ }, "node_modules/define-properties": { "version": "1.2.1", - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", - "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", "license": "MIT", "dependencies": { "define-data-property": "^1.0.1", @@ -9448,9 +7484,7 @@ } }, "node_modules/delaunator": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/delaunator/-/delaunator-5.1.0.tgz", - "integrity": "sha512-AGrQ4QSgssa1NGmWmLPqN5NY2KajF5MqxetNEO+o0n3ZwZZeTmt7bBnvzHWrmkZFxGgr4HdyFgelzgi06otLuQ==", + "version": "5.0.1", "license": "ISC", "dependencies": { "robust-predicates": "^3.0.2" @@ -9458,8 +7492,6 @@ }, "node_modules/delayed-stream": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", "license": "MIT", "peer": true, "engines": { @@ -9468,8 +7500,6 @@ }, "node_modules/dequal": { "version": "2.0.3", - "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", - "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", "license": "MIT", "engines": { "node": ">=6" @@ -9477,8 +7507,6 @@ }, "node_modules/detect-libc": { "version": "2.1.2", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", - "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", "dev": true, "license": "Apache-2.0", "engines": { @@ -9487,14 +7515,10 @@ }, "node_modules/detect-node-es": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/detect-node-es/-/detect-node-es-1.1.0.tgz", - "integrity": "sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==", "license": "MIT" }, "node_modules/devlop": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/devlop/-/devlop-1.1.0.tgz", - "integrity": "sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==", "license": "MIT", "dependencies": { "dequal": "^2.0.0" @@ -9530,8 +7554,6 @@ }, "node_modules/doctrine": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", - "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -9543,16 +7565,12 @@ }, "node_modules/dom-accessibility-api": { "version": "0.5.16", - "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.5.16.tgz", - "integrity": "sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==", "dev": true, "license": "MIT", "peer": true }, "node_modules/dom-serializer": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", - "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", "license": "MIT", "dependencies": { "domelementtype": "^2.3.0", @@ -9565,8 +7583,6 @@ }, "node_modules/domelementtype": { "version": "2.3.0", - "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", - "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", "funding": [ { "type": "github", @@ -9577,8 +7593,6 @@ }, "node_modules/domhandler": { "version": "5.0.3", - "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", - "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", "license": "BSD-2-Clause", "dependencies": { "domelementtype": "^2.3.0" @@ -9601,8 +7615,6 @@ }, "node_modules/domutils": { "version": "3.2.2", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.2.2.tgz", - "integrity": "sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==", "license": "BSD-2-Clause", "dependencies": { "dom-serializer": "^2.0.0", @@ -9629,8 +7641,6 @@ }, "node_modules/dunder-proto": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", - "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", "license": "MIT", "dependencies": { "call-bind-apply-helpers": "^1.0.1", @@ -9654,8 +7664,6 @@ }, "node_modules/ejs": { "version": "3.1.10", - "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.10.tgz", - "integrity": "sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -9669,22 +7677,16 @@ } }, "node_modules/electron-to-chromium": { - "version": "1.5.331", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.331.tgz", - "integrity": "sha512-IbxXrsTlD3hRodkLnbxAPP4OuJYdWCeM3IOdT+CpcMoIwIoDfCmRpEtSPfwBXxVkg9xmBeY7Lz2Eo2TDn/HC3Q==", + "version": "1.5.302", "dev": true, "license": "ISC" }, "node_modules/emoji-regex": { "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", "license": "MIT" }, "node_modules/empathic": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/empathic/-/empathic-2.0.0.tgz", - "integrity": "sha512-i6UzDscO/XfAcNYD75CfICkmfLedpyPDdozrLMmQc5ORaQcdMoc21OnlEylMIqI7U8eniKrPMxxtj8k0vhmJhA==", "dev": true, "license": "MIT", "engines": { @@ -9692,9 +7694,7 @@ } }, "node_modules/enhanced-resolve": { - "version": "5.20.1", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.20.1.tgz", - "integrity": "sha512-Qohcme7V1inbAfvjItgw0EaxVX5q2rdVEZHRBrEQdRZTssLDGsL8Lwrznl8oQ/6kuTJONLaDcGjkNP247XEhcA==", + "version": "5.20.0", "dev": true, "license": "MIT", "dependencies": { @@ -9707,8 +7707,6 @@ }, "node_modules/entities": { "version": "4.5.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", - "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", "license": "BSD-2-Clause", "engines": { "node": ">=0.12" @@ -9719,8 +7717,6 @@ }, "node_modules/error-ex": { "version": "1.3.4", - "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.4.tgz", - "integrity": "sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==", "dev": true, "license": "MIT", "dependencies": { @@ -9729,8 +7725,6 @@ }, "node_modules/es-abstract": { "version": "1.24.1", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.24.1.tgz", - "integrity": "sha512-zHXBLhP+QehSSbsS9Pt23Gg964240DPd6QCf8WpkqEXxQ7fhdZzYsocOr5u7apWonsS5EjZDmTF+/slGMyasvw==", "dev": true, "license": "MIT", "dependencies": { @@ -9798,8 +7792,6 @@ }, "node_modules/es-define-property": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", - "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", "license": "MIT", "engines": { "node": ">= 0.4" @@ -9807,17 +7799,13 @@ }, "node_modules/es-errors": { "version": "1.3.0", - "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", - "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", "license": "MIT", "engines": { "node": ">= 0.4" } }, "node_modules/es-iterator-helpers": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/es-iterator-helpers/-/es-iterator-helpers-1.3.1.tgz", - "integrity": "sha512-zWwRvqWiuBPr0muUG/78cW3aHROFCNIQ3zpmYDpwdbnt2m+xlNyRWpHBpa2lJjSBit7BQ+RXA1iwbSmu5yJ/EQ==", + "version": "1.2.2", "dev": true, "license": "MIT", "dependencies": { @@ -9836,7 +7824,6 @@ "has-symbols": "^1.1.0", "internal-slot": "^1.1.0", "iterator.prototype": "^1.1.5", - "math-intrinsics": "^1.1.0", "safe-array-concat": "^1.1.3" }, "engines": { @@ -9845,15 +7832,11 @@ }, "node_modules/es-module-lexer": { "version": "1.7.0", - "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.7.0.tgz", - "integrity": "sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==", "dev": true, "license": "MIT" }, "node_modules/es-object-atoms": { "version": "1.1.1", - "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", - "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", "license": "MIT", "dependencies": { "es-errors": "^1.3.0" @@ -9864,8 +7847,6 @@ }, "node_modules/es-set-tostringtag": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", - "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", "license": "MIT", "dependencies": { "es-errors": "^1.3.0", @@ -9879,8 +7860,6 @@ }, "node_modules/es-shim-unscopables": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.1.0.tgz", - "integrity": "sha512-d9T8ucsEhh8Bi1woXCf+TIKDIROLG5WCkxg8geBCbvk22kzwC5G2OnXVMO6FUsvQlgUUXQ2itephWDLqDzbeCw==", "dev": true, "license": "MIT", "dependencies": { @@ -9892,8 +7871,6 @@ }, "node_modules/es-to-primitive": { "version": "1.3.0", - "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.3.0.tgz", - "integrity": "sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==", "dev": true, "license": "MIT", "dependencies": { @@ -9909,9 +7886,7 @@ } }, "node_modules/esbuild": { - "version": "0.27.5", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.5.tgz", - "integrity": "sha512-zdQoHBjuDqKsvV5OPaWansOwfSQ0Js+Uj9J85TBvj3bFW1JjWTSULMRwdQAc8qMeIScbClxeMK0jlrtB9linhA==", + "version": "0.27.3", "dev": true, "hasInstallScript": true, "license": "MIT", @@ -9922,38 +7897,36 @@ "node": ">=18" }, "optionalDependencies": { - "@esbuild/aix-ppc64": "0.27.5", - "@esbuild/android-arm": "0.27.5", - "@esbuild/android-arm64": "0.27.5", - "@esbuild/android-x64": "0.27.5", - "@esbuild/darwin-arm64": "0.27.5", - "@esbuild/darwin-x64": "0.27.5", - "@esbuild/freebsd-arm64": "0.27.5", - "@esbuild/freebsd-x64": "0.27.5", - "@esbuild/linux-arm": "0.27.5", - "@esbuild/linux-arm64": "0.27.5", - "@esbuild/linux-ia32": "0.27.5", - "@esbuild/linux-loong64": "0.27.5", - "@esbuild/linux-mips64el": "0.27.5", - "@esbuild/linux-ppc64": "0.27.5", - "@esbuild/linux-riscv64": "0.27.5", - "@esbuild/linux-s390x": "0.27.5", - "@esbuild/linux-x64": "0.27.5", - "@esbuild/netbsd-arm64": "0.27.5", - "@esbuild/netbsd-x64": "0.27.5", - "@esbuild/openbsd-arm64": "0.27.5", - "@esbuild/openbsd-x64": "0.27.5", - "@esbuild/openharmony-arm64": "0.27.5", - "@esbuild/sunos-x64": "0.27.5", - "@esbuild/win32-arm64": "0.27.5", - "@esbuild/win32-ia32": "0.27.5", - "@esbuild/win32-x64": "0.27.5" + "@esbuild/aix-ppc64": "0.27.3", + "@esbuild/android-arm": "0.27.3", + "@esbuild/android-arm64": "0.27.3", + "@esbuild/android-x64": "0.27.3", + "@esbuild/darwin-arm64": "0.27.3", + "@esbuild/darwin-x64": "0.27.3", + "@esbuild/freebsd-arm64": "0.27.3", + "@esbuild/freebsd-x64": "0.27.3", + "@esbuild/linux-arm": "0.27.3", + "@esbuild/linux-arm64": "0.27.3", + "@esbuild/linux-ia32": "0.27.3", + "@esbuild/linux-loong64": "0.27.3", + "@esbuild/linux-mips64el": "0.27.3", + "@esbuild/linux-ppc64": "0.27.3", + "@esbuild/linux-riscv64": "0.27.3", + "@esbuild/linux-s390x": "0.27.3", + "@esbuild/linux-x64": "0.27.3", + "@esbuild/netbsd-arm64": "0.27.3", + "@esbuild/netbsd-x64": "0.27.3", + "@esbuild/openbsd-arm64": "0.27.3", + "@esbuild/openbsd-x64": "0.27.3", + "@esbuild/openharmony-arm64": "0.27.3", + "@esbuild/sunos-x64": "0.27.3", + "@esbuild/win32-arm64": "0.27.3", + "@esbuild/win32-ia32": "0.27.3", + "@esbuild/win32-x64": "0.27.3" } }, "node_modules/escalade": { "version": "3.2.0", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", - "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", "license": "MIT", "engines": { "node": ">=6" @@ -9961,8 +7934,6 @@ }, "node_modules/escape-string-regexp": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", "license": "MIT", "engines": { "node": ">=10" @@ -9972,25 +7943,23 @@ } }, "node_modules/eslint": { - "version": "9.39.4", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.39.4.tgz", - "integrity": "sha512-XoMjdBOwe/esVgEvLmNsD3IRHkm7fbKIUGvrleloJXUZgDHig2IPWNniv+GwjyJXzuNqVjlr5+4yVUZjycJwfQ==", + "version": "9.39.3", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.1", - "@eslint/config-array": "^0.21.2", + "@eslint/config-array": "^0.21.1", "@eslint/config-helpers": "^0.4.2", "@eslint/core": "^0.17.0", - "@eslint/eslintrc": "^3.3.5", - "@eslint/js": "9.39.4", + "@eslint/eslintrc": "^3.3.1", + "@eslint/js": "9.39.3", "@eslint/plugin-kit": "^0.4.1", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", "@humanwhocodes/retry": "^0.4.2", "@types/estree": "^1.0.6", - "ajv": "^6.14.0", + "ajv": "^6.12.4", "chalk": "^4.0.0", "cross-spawn": "^7.0.6", "debug": "^4.3.2", @@ -10009,7 +7978,7 @@ "is-glob": "^4.0.0", "json-stable-stringify-without-jsonify": "^1.0.1", "lodash.merge": "^4.6.2", - "minimatch": "^3.1.5", + "minimatch": "^3.1.2", "natural-compare": "^1.4.0", "optionator": "^0.9.3" }, @@ -10033,15 +8002,11 @@ }, "node_modules/eslint-config-riot": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/eslint-config-riot/-/eslint-config-riot-1.0.0.tgz", - "integrity": "sha512-NB/L/1Y30qyJcG5xZxCJKW/+bqyj+llbcCwo9DEz8bESIP0SLTOQ8T1DWCCFc+wJ61AMEstj4511PSScqMMfCw==", "license": "MIT", "peer": true }, "node_modules/eslint-import-context": { "version": "0.1.9", - "resolved": "https://registry.npmjs.org/eslint-import-context/-/eslint-import-context-0.1.9.tgz", - "integrity": "sha512-K9Hb+yRaGAGUbwjhFNHvSmmkZs9+zbuoe3kFQ4V1wYjrepUFYM2dZAfNtjbbj3qsPfUfsA68Bx/ICWQMi+C8Eg==", "dev": true, "license": "MIT", "dependencies": { @@ -10065,8 +8030,6 @@ }, "node_modules/eslint-import-resolver-typescript": { "version": "4.4.4", - "resolved": "https://registry.npmjs.org/eslint-import-resolver-typescript/-/eslint-import-resolver-typescript-4.4.4.tgz", - "integrity": "sha512-1iM2zeBvrYmUNTj2vSC/90JTHDth+dfOfiNKkxApWRsTJYNrc8rOdxxIf5vazX+BiAXTeOT0UvWpGI/7qIWQOw==", "dev": true, "license": "ISC", "dependencies": { @@ -10099,19 +8062,16 @@ } }, "node_modules/eslint-plugin-import-x": { - "version": "4.16.2", - "resolved": "https://registry.npmjs.org/eslint-plugin-import-x/-/eslint-plugin-import-x-4.16.2.tgz", - "integrity": "sha512-rM9K8UBHcWKpzQzStn1YRN2T5NvdeIfSVoKu/lKF41znQXHAUcBbYXe5wd6GNjZjTrP7viQ49n1D83x/2gYgIw==", + "version": "4.16.1", "dev": true, "license": "MIT", "dependencies": { - "@package-json/types": "^0.0.12", - "@typescript-eslint/types": "^8.56.0", + "@typescript-eslint/types": "^8.35.0", "comment-parser": "^1.4.1", "debug": "^4.4.1", "eslint-import-context": "^0.1.9", "is-glob": "^4.0.3", - "minimatch": "^9.0.3 || ^10.1.2", + "minimatch": "^9.0.3 || ^10.0.1", "semver": "^7.7.2", "stable-hash-x": "^0.2.0", "unrs-resolver": "^1.9.2" @@ -10123,8 +8083,8 @@ "url": "https://opencollective.com/eslint-plugin-import-x" }, "peerDependencies": { - "@typescript-eslint/utils": "^8.56.0", - "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "@typescript-eslint/utils": "^8.0.0", + "eslint": "^8.57.0 || ^9.0.0", "eslint-import-resolver-node": "*" }, "peerDependenciesMeta": { @@ -10324,12 +8284,12 @@ "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/eslint-plugin-n8n-nodes-base/node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "node_modules/eslint-plugin-n8n-nodes-base/node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", "dev": true, - "license": "MIT", + "license": "Python-2.0", "peer": true }, "node_modules/eslint-plugin-n8n-nodes-base/node_modules/doctrine": { @@ -10594,14 +8554,28 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/eslint-plugin-n8n-nodes-base/node_modules/js-yaml": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", + "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, "node_modules/eslint-plugin-n8n-nodes-base/node_modules/minimatch": { - "version": "9.0.9", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz", - "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==", + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", + "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", "dev": true, "license": "ISC", "dependencies": { - "brace-expansion": "^2.0.2" + "brace-expansion": "^2.0.1" }, "engines": { "node": ">=16 || 14 >=14.17" @@ -10682,8 +8656,6 @@ }, "node_modules/eslint-plugin-react": { "version": "7.37.5", - "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.37.5.tgz", - "integrity": "sha512-Qteup0SqU15kdocexFNAJMvCJEfa2xUKNV4CC1xsVMrIIqEy3SQ/rqyxCWNzfrd3/ldy6HMlD2e0JDVpDg2qIA==", "dev": true, "license": "MIT", "dependencies": { @@ -10715,8 +8687,6 @@ }, "node_modules/eslint-plugin-react-hooks": { "version": "7.0.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-7.0.1.tgz", - "integrity": "sha512-O0d0m04evaNzEPoSW+59Mezf8Qt0InfgGIBJnpC0h3NH/WjUAR7BIKUfysC6todmtiZ/A0oUVS8Gce0WhBrHsA==", "dev": true, "license": "MIT", "dependencies": { @@ -10733,41 +8703,8 @@ "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0" } }, - "node_modules/eslint-plugin-react/node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true, - "license": "MIT" - }, - "node_modules/eslint-plugin-react/node_modules/brace-expansion": { - "version": "1.1.13", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.13.tgz", - "integrity": "sha512-9ZLprWS6EENmhEOpjCYW2c8VkmOvckIJZfkr7rBW6dObmfgJ/L1GpSYW5Hpo9lDz4D1+n0Ckz8rU7FwHDQiG/w==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/eslint-plugin-react/node_modules/minimatch": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", - "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, "node_modules/eslint-plugin-react/node_modules/resolve": { "version": "2.0.0-next.6", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.6.tgz", - "integrity": "sha512-3JmVl5hMGtJ3kMmB3zi3DL25KfkCEyy3Tw7Gmw7z5w8M9WlwoPFnIvwChzu1+cF3iaK3sp18hhPz8ANeimdJfA==", "dev": true, "license": "MIT", "dependencies": { @@ -10790,8 +8727,6 @@ }, "node_modules/eslint-plugin-react/node_modules/semver": { "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true, "license": "ISC", "bin": { @@ -10800,8 +8735,6 @@ }, "node_modules/eslint-plugin-relay": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-relay/-/eslint-plugin-relay-2.0.0.tgz", - "integrity": "sha512-H52u+YM72VhBwhKrGSSdsjbeAWbLIgmph3z4Hgfyqm8Jt5pP5g9PbFmBp8kk10D4vZldrbNwa0esHxDRoczprg==", "dev": true, "license": "MIT", "dependencies": { @@ -10810,8 +8743,6 @@ }, "node_modules/eslint-scope": { "version": "8.4.0", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz", - "integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==", "dev": true, "license": "BSD-2-Clause", "dependencies": { @@ -10827,8 +8758,6 @@ }, "node_modules/eslint-visitor-keys": { "version": "4.2.1", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", - "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", "dev": true, "license": "Apache-2.0", "engines": { @@ -10838,41 +8767,8 @@ "url": "https://opencollective.com/eslint" } }, - "node_modules/eslint/node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true, - "license": "MIT" - }, - "node_modules/eslint/node_modules/brace-expansion": { - "version": "1.1.13", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.13.tgz", - "integrity": "sha512-9ZLprWS6EENmhEOpjCYW2c8VkmOvckIJZfkr7rBW6dObmfgJ/L1GpSYW5Hpo9lDz4D1+n0Ckz8rU7FwHDQiG/w==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/eslint/node_modules/minimatch": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", - "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, "node_modules/espree": { "version": "10.4.0", - "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz", - "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==", "dev": true, "license": "BSD-2-Clause", "dependencies": { @@ -10889,8 +8785,6 @@ }, "node_modules/esprima": { "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", "license": "BSD-2-Clause", "bin": { "esparse": "bin/esparse.js", @@ -10902,8 +8796,6 @@ }, "node_modules/esprima-next": { "version": "5.8.4", - "resolved": "https://registry.npmjs.org/esprima-next/-/esprima-next-5.8.4.tgz", - "integrity": "sha512-8nYVZ4ioIH4Msjb/XmhnBdz5WRRBaYqevKa1cv9nGJdCehMbzZCPNEEnqfLCZVetUVrUPEcb5IYyu1GG4hFqgg==", "license": "BSD-2-Clause", "peer": true, "bin": { @@ -10916,8 +8808,6 @@ }, "node_modules/esquery": { "version": "1.7.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.7.0.tgz", - "integrity": "sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==", "dev": true, "license": "BSD-3-Clause", "dependencies": { @@ -10929,8 +8819,6 @@ }, "node_modules/esrecurse": { "version": "4.3.0", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", - "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", "dev": true, "license": "BSD-2-Clause", "dependencies": { @@ -10942,8 +8830,6 @@ }, "node_modules/estraverse": { "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", "dev": true, "license": "BSD-2-Clause", "engines": { @@ -10952,8 +8838,6 @@ }, "node_modules/estree-util-is-identifier-name": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/estree-util-is-identifier-name/-/estree-util-is-identifier-name-3.0.0.tgz", - "integrity": "sha512-hFtqIDZTIUZ9BXLb8y4pYGyk6+wekIivNVTcmvk8NoOh+VeRn5y6cEHzbURrWbfp1fIqdVipilzj+lfaadNZmg==", "license": "MIT", "funding": { "type": "opencollective", @@ -10962,15 +8846,11 @@ }, "node_modules/estree-walker": { "version": "2.0.2", - "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", - "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", "dev": true, "license": "MIT" }, "node_modules/esutils": { "version": "2.0.3", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", "dev": true, "license": "BSD-2-Clause", "engines": { @@ -10997,8 +8877,6 @@ }, "node_modules/expect-type": { "version": "1.3.0", - "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.3.0.tgz", - "integrity": "sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA==", "dev": true, "license": "Apache-2.0", "engines": { @@ -11007,14 +8885,10 @@ }, "node_modules/extend": { "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", - "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", "license": "MIT" }, "node_modules/fast-deep-equal": { "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", "dev": true, "license": "MIT" }, @@ -11059,15 +8933,11 @@ }, "node_modules/fast-json-stable-stringify": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", "dev": true, "license": "MIT" }, "node_modules/fast-levenshtein": { "version": "2.0.6", - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", "dev": true, "license": "MIT" }, @@ -11093,8 +8963,6 @@ }, "node_modules/fbjs": { "version": "3.0.5", - "resolved": "https://registry.npmjs.org/fbjs/-/fbjs-3.0.5.tgz", - "integrity": "sha512-ztsSx77JBtkuMrEypfhgc3cI0+0h+svqeie7xHbh1k/IKdcydnvadp/mUaGgjAOXQmQSxsqgaRhS3q9fy+1kxg==", "license": "MIT", "dependencies": { "cross-fetch": "^3.1.5", @@ -11108,14 +8976,10 @@ }, "node_modules/fbjs-css-vars": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/fbjs-css-vars/-/fbjs-css-vars-1.0.2.tgz", - "integrity": "sha512-b2XGFAFdWZWg0phtAWLHCk836A1Xann+I+Dgd3Gk64MHKZO44FfoD1KxyvbSh0qZsIoXQGGlVztIY+oitJPpRQ==", "license": "MIT" }, "node_modules/fdir": { "version": "6.5.0", - "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", - "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", "dev": true, "license": "MIT", "engines": { @@ -11132,8 +8996,6 @@ }, "node_modules/file-entry-cache": { "version": "8.0.0", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", - "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", "dev": true, "license": "MIT", "dependencies": { @@ -11145,8 +9007,6 @@ }, "node_modules/file-selector": { "version": "2.1.2", - "resolved": "https://registry.npmjs.org/file-selector/-/file-selector-2.1.2.tgz", - "integrity": "sha512-QgXo+mXTe8ljeqUFaX3QVHc5osSItJ/Km+xpocx0aSqWGMSCf6qYs/VnzZgS864Pjn5iceMRFigeAV7AfTlaig==", "license": "MIT", "dependencies": { "tslib": "^2.7.0" @@ -11177,31 +9037,14 @@ }, "node_modules/filelist": { "version": "1.0.6", - "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.6.tgz", - "integrity": "sha512-5giy2PkLYY1cP39p17Ech+2xlpTRL9HLspOfEgm0L6CwBXBTgsK5ou0JtzYuepxkaQ/tvhCFIJ5uXo0OrM2DxA==", "dev": true, "license": "Apache-2.0", "dependencies": { "minimatch": "^5.0.1" } }, - "node_modules/filelist/node_modules/minimatch": { - "version": "5.1.9", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.9.tgz", - "integrity": "sha512-7o1wEA2RyMP7Iu7GNba9vc0RWWGACJOCZBJX2GJWip0ikV+wcOsgVuY9uE8CPiyQhkGFSlhuSkZPavN7u1c2Fw==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/filesize": { "version": "10.1.6", - "resolved": "https://registry.npmjs.org/filesize/-/filesize-10.1.6.tgz", - "integrity": "sha512-sJslQKU2uM33qH5nqewAwVB2QgR6w1aMNsYUp3aN5rMRyXEwJGmZvaWzeJFNTOXWlHQyBFCWrdj3fV/fsTOX8w==", "dev": true, "license": "BSD-3-Clause", "engines": { @@ -11223,8 +9066,6 @@ }, "node_modules/find-up": { "version": "5.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", - "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", "dev": true, "license": "MIT", "dependencies": { @@ -11250,8 +9091,6 @@ }, "node_modules/flat-cache": { "version": "4.0.1", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", - "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", "dev": true, "license": "MIT", "dependencies": { @@ -11264,8 +9103,6 @@ }, "node_modules/flatted": { "version": "3.4.2", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.2.tgz", - "integrity": "sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==", "dev": true, "license": "ISC" }, @@ -11293,8 +9130,6 @@ }, "node_modules/for-each": { "version": "0.3.5", - "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz", - "integrity": "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==", "license": "MIT", "dependencies": { "is-callable": "^1.2.7" @@ -11308,8 +9143,6 @@ }, "node_modules/foreground-child": { "version": "3.3.1", - "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", - "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", "dev": true, "license": "ISC", "dependencies": { @@ -11325,9 +9158,6 @@ }, "node_modules/form-data": { "version": "4.0.5", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz", - "integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==", - "dev": true, "license": "MIT", "peer": true, "dependencies": { @@ -11374,10 +9204,7 @@ }, "node_modules/fsevents": { "version": "2.3.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", - "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", "dev": true, - "hasInstallScript": true, "license": "MIT", "optional": true, "os": [ @@ -11389,8 +9216,6 @@ }, "node_modules/function-bind": { "version": "1.1.2", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", - "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", "license": "MIT", "funding": { "url": "https://github.com/sponsors/ljharb" @@ -11398,8 +9223,6 @@ }, "node_modules/function.prototype.name": { "version": "1.1.8", - "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.8.tgz", - "integrity": "sha512-e5iwyodOHhbMr/yNrc7fDYG4qlbIvI5gajyzPnb5TCwyhjApznQh1BMFou9b30SevY43gCJKXycoCBjMbsuW0Q==", "dev": true, "license": "MIT", "dependencies": { @@ -11419,8 +9242,6 @@ }, "node_modules/functions-have-names": { "version": "1.2.3", - "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", - "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", "dev": true, "license": "MIT", "funding": { @@ -11429,8 +9250,6 @@ }, "node_modules/generator-function": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/generator-function/-/generator-function-2.0.1.tgz", - "integrity": "sha512-SFdFmIJi+ybC0vjlHN0ZGVGHc3lgE0DxPAT0djjVg+kjOnSqclqmj0KQ7ykTOLP6YxoqOvuAODGdcHJn+43q3g==", "license": "MIT", "engines": { "node": ">= 0.4" @@ -11438,8 +9257,6 @@ }, "node_modules/gensync": { "version": "1.0.0-beta.2", - "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", - "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", "dev": true, "license": "MIT", "engines": { @@ -11448,8 +9265,6 @@ }, "node_modules/get-caller-file": { "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", "license": "ISC", "peer": true, "engines": { @@ -11458,8 +9273,6 @@ }, "node_modules/get-intrinsic": { "version": "1.3.0", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", - "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", "license": "MIT", "dependencies": { "call-bind-apply-helpers": "^1.0.2", @@ -11482,8 +9295,6 @@ }, "node_modules/get-nonce": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/get-nonce/-/get-nonce-1.0.1.tgz", - "integrity": "sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q==", "license": "MIT", "engines": { "node": ">=6" @@ -11491,8 +9302,6 @@ }, "node_modules/get-package-type": { "version": "0.1.0", - "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", - "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", "dev": true, "license": "MIT", "engines": { @@ -11501,8 +9310,6 @@ }, "node_modules/get-proto": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", - "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", "license": "MIT", "dependencies": { "dunder-proto": "^1.0.1", @@ -11514,8 +9321,6 @@ }, "node_modules/get-symbol-description": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.1.0.tgz", - "integrity": "sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==", "dev": true, "license": "MIT", "dependencies": { @@ -11531,9 +9336,7 @@ } }, "node_modules/get-tsconfig": { - "version": "4.13.7", - "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.13.7.tgz", - "integrity": "sha512-7tN6rFgBlMgpBML5j8typ92BKFi2sFQvIdpAqLA2beia5avZDrMs0FLZiM5etShWq5irVyGcGMEA1jcDaK7A/Q==", + "version": "4.13.6", "dev": true, "license": "MIT", "dependencies": { @@ -11545,8 +9348,6 @@ }, "node_modules/glob": { "version": "13.0.6", - "resolved": "https://registry.npmjs.org/glob/-/glob-13.0.6.tgz", - "integrity": "sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw==", "dev": true, "license": "BlueOak-1.0.0", "dependencies": { @@ -11563,8 +9364,6 @@ }, "node_modules/glob-parent": { "version": "6.0.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", - "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", "dev": true, "license": "ISC", "dependencies": { @@ -11576,8 +9375,6 @@ }, "node_modules/globals": { "version": "17.4.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-17.4.0.tgz", - "integrity": "sha512-hjrNztw/VajQwOLsMNT1cbJiH2muO3OROCHnbehc8eY5JyD2gqz4AcMHPqgaOR59DjgUjYAYLeH699g/eWi2jw==", "dev": true, "license": "MIT", "engines": { @@ -11589,8 +9386,6 @@ }, "node_modules/globalthis": { "version": "1.0.4", - "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz", - "integrity": "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==", "dev": true, "license": "MIT", "dependencies": { @@ -11627,8 +9422,6 @@ }, "node_modules/gopd": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", - "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", "license": "MIT", "engines": { "node": ">= 0.4" @@ -11639,8 +9432,6 @@ }, "node_modules/graceful-fs": { "version": "4.2.11", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", - "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", "dev": true, "license": "ISC" }, @@ -11653,9 +9444,7 @@ "peer": true }, "node_modules/graphql": { - "version": "16.13.2", - "resolved": "https://registry.npmjs.org/graphql/-/graphql-16.13.2.tgz", - "integrity": "sha512-5bJ+nf/UCpAjHM8i06fl7eLyVC9iuNAjm9qzkiu2ZGhM0VscSvS6WDPfAwkdkBuoXGM9FJSbKl6wylMwP9Ktig==", + "version": "16.13.0", "license": "MIT", "engines": { "node": "^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0" @@ -11663,14 +9452,10 @@ }, "node_modules/hachure-fill": { "version": "0.5.2", - "resolved": "https://registry.npmjs.org/hachure-fill/-/hachure-fill-0.5.2.tgz", - "integrity": "sha512-3GKBOn+m2LX9iq+JC1064cSFprJY4jL1jCXTcpnfER5HYE2l/4EfWSGzkPa/ZDBmYI0ZOEj5VHV/eKnPGkHuOg==", "license": "MIT" }, "node_modules/handlebars": { - "version": "4.7.9", - "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.9.tgz", - "integrity": "sha512-4E71E0rpOaQuJR2A3xDZ+GM1HyWYv1clR58tC8emQNeQe3RH7MAzSbat+V0wG78LQBo6m6bzSG/L4pBuCsgnUQ==", + "version": "4.7.8", "dev": true, "license": "MIT", "dependencies": { @@ -11691,8 +9476,6 @@ }, "node_modules/has-bigints": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.1.0.tgz", - "integrity": "sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==", "dev": true, "license": "MIT", "engines": { @@ -11704,8 +9487,6 @@ }, "node_modules/has-flag": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true, "license": "MIT", "engines": { @@ -11714,8 +9495,6 @@ }, "node_modules/has-property-descriptors": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", - "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", "license": "MIT", "dependencies": { "es-define-property": "^1.0.0" @@ -11726,8 +9505,6 @@ }, "node_modules/has-proto": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.2.0.tgz", - "integrity": "sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ==", "dev": true, "license": "MIT", "dependencies": { @@ -11742,8 +9519,6 @@ }, "node_modules/has-symbols": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", - "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", "license": "MIT", "engines": { "node": ">= 0.4" @@ -11754,8 +9529,6 @@ }, "node_modules/has-tostringtag": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", - "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", "license": "MIT", "dependencies": { "has-symbols": "^1.0.3" @@ -11769,8 +9542,6 @@ }, "node_modules/hasown": { "version": "2.0.2", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", - "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", "license": "MIT", "dependencies": { "function-bind": "^1.1.2" @@ -11781,8 +9552,6 @@ }, "node_modules/hast-util-from-parse5": { "version": "8.0.3", - "resolved": "https://registry.npmjs.org/hast-util-from-parse5/-/hast-util-from-parse5-8.0.3.tgz", - "integrity": "sha512-3kxEVkEKt0zvcZ3hCRYI8rqrgwtlIOFMWkbclACvjlDw8Li9S2hk/d51OI0nr/gIpdMHNepwgOKqZ/sy0Clpyg==", "license": "MIT", "dependencies": { "@types/hast": "^3.0.0", @@ -11801,8 +9570,6 @@ }, "node_modules/hast-util-parse-selector": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/hast-util-parse-selector/-/hast-util-parse-selector-4.0.0.tgz", - "integrity": "sha512-wkQCkSYoOGCRKERFWcxMVMOcYE2K1AaNLU8DXS9arxnLOUEWbOXKXiJUNzEpqZ3JOKpnha3jkFrumEjVliDe7A==", "license": "MIT", "dependencies": { "@types/hast": "^3.0.0" @@ -11814,8 +9581,6 @@ }, "node_modules/hast-util-raw": { "version": "9.1.0", - "resolved": "https://registry.npmjs.org/hast-util-raw/-/hast-util-raw-9.1.0.tgz", - "integrity": "sha512-Y8/SBAHkZGoNkpzqqfCldijcuUKh7/su31kEBp67cFY09Wy0mTRgtsLYsiIxMJxlu0f6AA5SUTbDR8K0rxnbUw==", "license": "MIT", "dependencies": { "@types/hast": "^3.0.0", @@ -11839,8 +9604,6 @@ }, "node_modules/hast-util-to-jsx-runtime": { "version": "2.3.6", - "resolved": "https://registry.npmjs.org/hast-util-to-jsx-runtime/-/hast-util-to-jsx-runtime-2.3.6.tgz", - "integrity": "sha512-zl6s8LwNyo1P9uw+XJGvZtdFF1GdAkOg8ujOw+4Pyb76874fLps4ueHXDhXWdk6YHQ6OgUtinliG7RsYvCbbBg==", "license": "MIT", "dependencies": { "@types/estree": "^1.0.0", @@ -11866,8 +9629,6 @@ }, "node_modules/hast-util-to-parse5": { "version": "8.0.1", - "resolved": "https://registry.npmjs.org/hast-util-to-parse5/-/hast-util-to-parse5-8.0.1.tgz", - "integrity": "sha512-MlWT6Pjt4CG9lFCjiz4BH7l9wmrMkfkJYCxFwKQic8+RTZgWPuWxwAfjJElsXkex7DJjfSJsQIt931ilUgmwdA==", "license": "MIT", "dependencies": { "@types/hast": "^3.0.0", @@ -11885,8 +9646,6 @@ }, "node_modules/hast-util-whitespace": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/hast-util-whitespace/-/hast-util-whitespace-3.0.0.tgz", - "integrity": "sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==", "license": "MIT", "dependencies": { "@types/hast": "^3.0.0" @@ -11898,8 +9657,6 @@ }, "node_modules/hastscript": { "version": "9.0.1", - "resolved": "https://registry.npmjs.org/hastscript/-/hastscript-9.0.1.tgz", - "integrity": "sha512-g7df9rMFX/SPi34tyGCyUBREQoKkapwdY/T04Qn9TDWfHhAYt4/I0gMVirzK5wEzeUqIjEB+LXC/ypb7Aqno5w==", "license": "MIT", "dependencies": { "@types/hast": "^3.0.0", @@ -11915,25 +9672,24 @@ }, "node_modules/hermes-estree": { "version": "0.25.1", - "resolved": "https://registry.npmjs.org/hermes-estree/-/hermes-estree-0.25.1.tgz", - "integrity": "sha512-0wUoCcLp+5Ev5pDW2OriHC2MJCbwLwuRx+gAqMTOkGKJJiBCLjtrvy4PWUGn6MIVefecRpzoOZ/UV6iGdOr+Cw==", "dev": true, "license": "MIT" }, "node_modules/hermes-parser": { "version": "0.25.1", - "resolved": "https://registry.npmjs.org/hermes-parser/-/hermes-parser-0.25.1.tgz", - "integrity": "sha512-6pEjquH3rqaI6cYAXYPcz9MS4rY6R4ngRgrgfDshRptUZIc3lw0MCIJIGDj9++mfySOuPTHB4nrSW99BCvOPIA==", "dev": true, "license": "MIT", "dependencies": { "hermes-estree": "0.25.1" } }, + "node_modules/html-escaper": { + "version": "2.0.2", + "dev": true, + "license": "MIT" + }, "node_modules/html-to-text": { "version": "9.0.5", - "resolved": "https://registry.npmjs.org/html-to-text/-/html-to-text-9.0.5.tgz", - "integrity": "sha512-qY60FjREgVZL03vJU6IfMV4GDjGBIoOyvuFdpBDIX9yTlDw0TjxVBQp+P8NvpdIXNJvfWBTNul7fsAQJq2FNpg==", "license": "MIT", "dependencies": { "@selderee/plugin-htmlparser2": "^0.11.0", @@ -11948,8 +9704,6 @@ }, "node_modules/html-url-attributes": { "version": "3.0.1", - "resolved": "https://registry.npmjs.org/html-url-attributes/-/html-url-attributes-3.0.1.tgz", - "integrity": "sha512-ol6UPyBWqsrO6EJySPz2O7ZSr856WDrEzM5zMqp+FJJLGMW35cLYmmZnl0vztAZxRUoNZJFTCohfjuIJ8I4QBQ==", "license": "MIT", "funding": { "type": "opencollective", @@ -11958,8 +9712,6 @@ }, "node_modules/html-void-elements": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/html-void-elements/-/html-void-elements-3.0.0.tgz", - "integrity": "sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg==", "license": "MIT", "funding": { "type": "github", @@ -11968,8 +9720,6 @@ }, "node_modules/htmlparser2": { "version": "8.0.2", - "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-8.0.2.tgz", - "integrity": "sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA==", "funding": [ "https://github.com/fb55/htmlparser2?sponsor=1", { @@ -12011,9 +9761,9 @@ } }, "node_modules/ibm-cloud-sdk-core": { - "version": "5.4.9", - "resolved": "https://registry.npmjs.org/ibm-cloud-sdk-core/-/ibm-cloud-sdk-core-5.4.9.tgz", - "integrity": "sha512-340fGcZEwUBdxBOPmn8V8fIiFRWF92yFqSFRNLwPQz4h+PS4jcAyd3JGqU6CpFqzUTt+PatVX/jHFwzUTVdmxQ==", + "version": "5.4.10", + "resolved": "https://registry.npmjs.org/ibm-cloud-sdk-core/-/ibm-cloud-sdk-core-5.4.10.tgz", + "integrity": "sha512-A9CL/XQTNoyS2fkp1uKKcYC03CJwp7hIl4DQeyG9s9QLsuMwffOM2fIkORsYHhn5wmWeFUkjAmt2iTlb7Hv1Iw==", "dev": true, "license": "Apache-2.0", "peer": true, @@ -12021,7 +9771,7 @@ "@types/debug": "^4.1.12", "@types/node": "^18.19.80", "@types/tough-cookie": "^4.0.0", - "axios": "^1.13.5", + "axios": "1.14.0", "camelcase": "^6.3.0", "debug": "^4.3.4", "dotenv": "^16.4.5", @@ -12060,8 +9810,6 @@ }, "node_modules/iconv-lite": { "version": "0.6.3", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", - "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", "license": "MIT", "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" @@ -12094,8 +9842,6 @@ }, "node_modules/ignore": { "version": "5.3.2", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", - "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", "dev": true, "license": "MIT", "engines": { @@ -12104,8 +9850,6 @@ }, "node_modules/import-fresh": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-2.0.0.tgz", - "integrity": "sha512-eZ5H8rcgYazHbKC3PG4ClHNykCSxtAhxSSEM+2mb+7evD2CKF5V7c0dNum7AdpDh0ZdICwZY9sRSn8f+KH96sg==", "dev": true, "license": "MIT", "dependencies": { @@ -12118,8 +9862,6 @@ }, "node_modules/imurmurhash": { "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", "dev": true, "license": "MIT", "engines": { @@ -12138,8 +9880,6 @@ }, "node_modules/indent-string": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", - "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", "dev": true, "license": "MIT", "engines": { @@ -12161,21 +9901,15 @@ }, "node_modules/inherits": { "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", "license": "ISC", "peer": true }, "node_modules/inline-style-parser": { "version": "0.2.7", - "resolved": "https://registry.npmjs.org/inline-style-parser/-/inline-style-parser-0.2.7.tgz", - "integrity": "sha512-Nb2ctOyNR8DqQoR0OwRG95uNWIC0C1lCgf5Naz5H6Ji72KZ8OcFZLz2P5sNgwlyoJ8Yif11oMuYs5pBQa86csA==", "license": "MIT" }, "node_modules/internal-slot": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.1.0.tgz", - "integrity": "sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==", "dev": true, "license": "MIT", "dependencies": { @@ -12189,8 +9923,6 @@ }, "node_modules/internmap": { "version": "2.0.3", - "resolved": "https://registry.npmjs.org/internmap/-/internmap-2.0.3.tgz", - "integrity": "sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==", "license": "ISC", "engines": { "node": ">=12" @@ -12198,8 +9930,6 @@ }, "node_modules/invariant": { "version": "2.2.4", - "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", - "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==", "license": "MIT", "dependencies": { "loose-envify": "^1.0.0" @@ -12207,8 +9937,6 @@ }, "node_modules/is-alphabetical": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-2.0.1.tgz", - "integrity": "sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ==", "license": "MIT", "funding": { "type": "github", @@ -12217,8 +9945,6 @@ }, "node_modules/is-alphanumerical": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-alphanumerical/-/is-alphanumerical-2.0.1.tgz", - "integrity": "sha512-hmbYhX/9MUMF5uh7tOXyK/n0ZvWpad5caBA17GsC6vyuCqaWliRG5K1qS9inmUhEMaOBIW7/whAnSwveW/LtZw==", "license": "MIT", "dependencies": { "is-alphabetical": "^2.0.0", @@ -12231,8 +9957,6 @@ }, "node_modules/is-arguments": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.2.0.tgz", - "integrity": "sha512-7bVbi0huj/wrIAOzb8U1aszg9kdi3KN/CyU19CTI7tAoZYEZoL9yCDXpbXN+uPsuWnP02cyug1gleqq+TU+YCA==", "license": "MIT", "peer": true, "dependencies": { @@ -12248,8 +9972,6 @@ }, "node_modules/is-array-buffer": { "version": "3.0.5", - "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.5.tgz", - "integrity": "sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==", "dev": true, "license": "MIT", "dependencies": { @@ -12266,15 +9988,11 @@ }, "node_modules/is-arrayish": { "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", "dev": true, "license": "MIT" }, "node_modules/is-async-function": { "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.1.1.tgz", - "integrity": "sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ==", "dev": true, "license": "MIT", "dependencies": { @@ -12293,8 +10011,6 @@ }, "node_modules/is-bigint": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.1.0.tgz", - "integrity": "sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==", "dev": true, "license": "MIT", "dependencies": { @@ -12309,8 +10025,6 @@ }, "node_modules/is-boolean-object": { "version": "1.2.2", - "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.2.2.tgz", - "integrity": "sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A==", "dev": true, "license": "MIT", "dependencies": { @@ -12326,15 +10040,11 @@ }, "node_modules/is-buffer": { "version": "1.1.6", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", - "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", "license": "MIT", "peer": true }, "node_modules/is-bun-module": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-bun-module/-/is-bun-module-2.0.0.tgz", - "integrity": "sha512-gNCGbnnnnFAUGKeZ9PdbyeGYJqewpmc2aKHUEMO5nQPWU9lOmv7jcmQIv+qHD8fXW6W7qfuCwX4rY9LNRjXrkQ==", "dev": true, "license": "MIT", "dependencies": { @@ -12343,8 +10053,6 @@ }, "node_modules/is-callable": { "version": "1.2.7", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", - "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", "license": "MIT", "engines": { "node": ">= 0.4" @@ -12355,8 +10063,6 @@ }, "node_modules/is-core-module": { "version": "2.16.1", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", - "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", "dev": true, "license": "MIT", "dependencies": { @@ -12371,8 +10077,6 @@ }, "node_modules/is-data-view": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-data-view/-/is-data-view-1.0.2.tgz", - "integrity": "sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw==", "dev": true, "license": "MIT", "dependencies": { @@ -12389,8 +10093,6 @@ }, "node_modules/is-date-object": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.1.0.tgz", - "integrity": "sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==", "dev": true, "license": "MIT", "dependencies": { @@ -12406,8 +10108,6 @@ }, "node_modules/is-decimal": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-decimal/-/is-decimal-2.0.1.tgz", - "integrity": "sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A==", "license": "MIT", "funding": { "type": "github", @@ -12416,8 +10116,6 @@ }, "node_modules/is-directory": { "version": "0.3.1", - "resolved": "https://registry.npmjs.org/is-directory/-/is-directory-0.3.1.tgz", - "integrity": "sha512-yVChGzahRFvbkscn2MlwGismPO12i9+znNruC5gVEntG3qu0xQMzsGg/JFbrsqDOHtHFPci+V5aP5T9I+yeKqw==", "dev": true, "license": "MIT", "engines": { @@ -12426,8 +10124,6 @@ }, "node_modules/is-docker": { "version": "2.2.1", - "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", - "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", "dev": true, "license": "MIT", "bin": { @@ -12442,8 +10138,6 @@ }, "node_modules/is-extglob": { "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", "dev": true, "license": "MIT", "engines": { @@ -12452,8 +10146,6 @@ }, "node_modules/is-finalizationregistry": { "version": "1.1.1", - "resolved": "https://registry.npmjs.org/is-finalizationregistry/-/is-finalizationregistry-1.1.1.tgz", - "integrity": "sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg==", "dev": true, "license": "MIT", "dependencies": { @@ -12468,8 +10160,6 @@ }, "node_modules/is-fullwidth-code-point": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", "license": "MIT", "engines": { "node": ">=8" @@ -12477,8 +10167,6 @@ }, "node_modules/is-generator-function": { "version": "1.1.2", - "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.1.2.tgz", - "integrity": "sha512-upqt1SkGkODW9tsGNG5mtXTXtECizwtS2kA161M+gJPc1xdb/Ax629af6YrTwcOeQHbewrPNlE5Dx7kzvXTizA==", "license": "MIT", "dependencies": { "call-bound": "^1.0.4", @@ -12496,8 +10184,6 @@ }, "node_modules/is-glob": { "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", "dev": true, "license": "MIT", "dependencies": { @@ -12509,8 +10195,6 @@ }, "node_modules/is-hexadecimal": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-hexadecimal/-/is-hexadecimal-2.0.1.tgz", - "integrity": "sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg==", "license": "MIT", "funding": { "type": "github", @@ -12519,8 +10203,6 @@ }, "node_modules/is-inside-container": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-inside-container/-/is-inside-container-1.0.0.tgz", - "integrity": "sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==", "dev": true, "license": "MIT", "dependencies": { @@ -12538,8 +10220,6 @@ }, "node_modules/is-inside-container/node_modules/is-docker": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-3.0.0.tgz", - "integrity": "sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==", "dev": true, "license": "MIT", "bin": { @@ -12554,8 +10234,6 @@ }, "node_modules/is-map": { "version": "2.0.3", - "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.3.tgz", - "integrity": "sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==", "dev": true, "license": "MIT", "engines": { @@ -12567,8 +10245,6 @@ }, "node_modules/is-nan": { "version": "1.3.2", - "resolved": "https://registry.npmjs.org/is-nan/-/is-nan-1.3.2.tgz", - "integrity": "sha512-E+zBKpQ2t6MEo1VsonYmluk9NxGrbzpeeLC2xIViuO2EjU2xsXsBPwTr3Ykv9l08UYEVEdWeRZNouaZqF6RN0w==", "license": "MIT", "peer": true, "dependencies": { @@ -12584,8 +10260,6 @@ }, "node_modules/is-negative-zero": { "version": "2.0.3", - "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.3.tgz", - "integrity": "sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==", "dev": true, "license": "MIT", "engines": { @@ -12620,8 +10294,6 @@ }, "node_modules/is-number-object": { "version": "1.1.1", - "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.1.1.tgz", - "integrity": "sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==", "dev": true, "license": "MIT", "dependencies": { @@ -12648,8 +10320,6 @@ }, "node_modules/is-plain-obj": { "version": "4.1.0", - "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-4.1.0.tgz", - "integrity": "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==", "license": "MIT", "engines": { "node": ">=12" @@ -12660,8 +10330,6 @@ }, "node_modules/is-regex": { "version": "1.2.1", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.2.1.tgz", - "integrity": "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==", "license": "MIT", "dependencies": { "call-bound": "^1.0.2", @@ -12678,8 +10346,6 @@ }, "node_modules/is-set": { "version": "2.0.3", - "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.3.tgz", - "integrity": "sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==", "dev": true, "license": "MIT", "engines": { @@ -12691,8 +10357,6 @@ }, "node_modules/is-shared-array-buffer": { "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.4.tgz", - "integrity": "sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A==", "dev": true, "license": "MIT", "dependencies": { @@ -12707,8 +10371,6 @@ }, "node_modules/is-string": { "version": "1.1.1", - "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.1.1.tgz", - "integrity": "sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA==", "dev": true, "license": "MIT", "dependencies": { @@ -12724,8 +10386,6 @@ }, "node_modules/is-symbol": { "version": "1.1.1", - "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.1.1.tgz", - "integrity": "sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w==", "dev": true, "license": "MIT", "dependencies": { @@ -12742,8 +10402,6 @@ }, "node_modules/is-typed-array": { "version": "1.1.15", - "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.15.tgz", - "integrity": "sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==", "license": "MIT", "dependencies": { "which-typed-array": "^1.1.16" @@ -12757,8 +10415,6 @@ }, "node_modules/is-weakmap": { "version": "2.0.2", - "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.2.tgz", - "integrity": "sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==", "dev": true, "license": "MIT", "engines": { @@ -12770,8 +10426,6 @@ }, "node_modules/is-weakref": { "version": "1.1.1", - "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.1.1.tgz", - "integrity": "sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew==", "dev": true, "license": "MIT", "dependencies": { @@ -12786,8 +10440,6 @@ }, "node_modules/is-weakset": { "version": "2.0.4", - "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.4.tgz", - "integrity": "sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==", "dev": true, "license": "MIT", "dependencies": { @@ -12803,8 +10455,6 @@ }, "node_modules/is-wsl": { "version": "2.2.0", - "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", - "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", "dev": true, "license": "MIT", "dependencies": { @@ -12816,32 +10466,14 @@ }, "node_modules/isarray": { "version": "2.0.5", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", - "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", "dev": true, "license": "MIT" }, "node_modules/isexe": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", "dev": true, "license": "ISC" }, - "node_modules/isolated-vm": { - "version": "6.1.2", - "resolved": "https://registry.npmjs.org/isolated-vm/-/isolated-vm-6.1.2.tgz", - "integrity": "sha512-GGfsHqtlZiiurZaxB/3kY7LLAXR3sgzDul0fom4cSyBjx6ZbjpTrFWiH3z/nUfLJGJ8PIq9LQmQFiAxu24+I7A==", - "hasInstallScript": true, - "license": "ISC", - "peer": true, - "dependencies": { - "node-gyp-build": "^4.8.4" - }, - "engines": { - "node": ">=22.0.0" - } - }, "node_modules/isstream": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", @@ -12850,10 +10482,52 @@ "license": "MIT", "peer": true }, + "node_modules/istanbul-lib-coverage": { + "version": "3.2.2", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-report": { + "version": "3.0.1", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^4.0.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-lib-report/node_modules/supports-color": { + "version": "7.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-reports": { + "version": "3.2.0", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/iterator.prototype": { "version": "1.1.5", - "resolved": "https://registry.npmjs.org/iterator.prototype/-/iterator.prototype-1.1.5.tgz", - "integrity": "sha512-H0dkQoCa3b2VEeKQBOxFph+JAbcrQdE7KC0UkqwpLmv2EC4P41QXP+rqo9wYodACiG5/WM5s9oDApTU8utwj9g==", "dev": true, "license": "MIT", "dependencies": { @@ -12870,8 +10544,6 @@ }, "node_modules/jackspeak": { "version": "4.2.3", - "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-4.2.3.tgz", - "integrity": "sha512-ykkVRwrYvFm1nb2AJfKKYPr0emF6IiXDYUaFx4Zn9ZuIH7MrzEZ3sD5RlqGXNRpHtvUHJyOnCEFxOlNDtGo7wg==", "dev": true, "license": "BlueOak-1.0.0", "dependencies": { @@ -12886,8 +10558,6 @@ }, "node_modules/jake": { "version": "10.9.4", - "resolved": "https://registry.npmjs.org/jake/-/jake-10.9.4.tgz", - "integrity": "sha512-wpHYzhxiVQL+IV05BLE2Xn34zW1S223hvjtqk0+gsPrwd/8JNLXJgZZM/iPFsYc1xyphF+6M6EvdE5E9MBGkDA==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -12904,15 +10574,11 @@ }, "node_modules/jake/node_modules/picocolors": { "version": "1.1.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", - "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", "dev": true, "license": "ISC" }, "node_modules/jiti": { "version": "2.6.1", - "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.6.1.tgz", - "integrity": "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==", "dev": true, "license": "MIT", "bin": { @@ -12921,8 +10587,6 @@ }, "node_modules/jmespath": { "version": "0.16.0", - "resolved": "https://registry.npmjs.org/jmespath/-/jmespath-0.16.0.tgz", - "integrity": "sha512-9FzQjJ7MATs1tSpnco1K6ayiYE3figslrXA72G2HQ/n76RzvYlofyi5QM+iX4YRs/pu3yzxlVQSST23+dMDknw==", "license": "Apache-2.0", "peer": true, "engines": { @@ -12931,8 +10595,6 @@ }, "node_modules/js-base64": { "version": "3.7.2", - "resolved": "https://registry.npmjs.org/js-base64/-/js-base64-3.7.2.tgz", - "integrity": "sha512-NnRs6dsyqUXejqk/yv2aiXlAvOs56sLkX6nUdeaNezI5LFFLlsZjOThmwnrcwh5ZZRwZlCMnVAY3CvhIhoVEKQ==", "license": "BSD-3-Clause", "peer": true }, @@ -12948,18 +10610,15 @@ }, "node_modules/js-tokens": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", "license": "MIT" }, - "node_modules/js-yaml": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", - "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", + "node_modules/js-yaml": { + "version": "3.14.2", "dev": true, "license": "MIT", "dependencies": { - "argparse": "^2.0.1" + "argparse": "^1.0.7", + "esprima": "^4.0.0" }, "bin": { "js-yaml": "bin/js-yaml.js" @@ -12967,8 +10626,6 @@ }, "node_modules/jsesc": { "version": "3.1.0", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", - "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", "dev": true, "license": "MIT", "bin": { @@ -12980,43 +10637,31 @@ }, "node_modules/json-buffer": { "version": "3.0.1", - "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", - "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", "dev": true, "license": "MIT" }, "node_modules/json-parse-better-errors": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", - "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", "dev": true, "license": "MIT" }, "node_modules/json-parse-even-better-errors": { "version": "2.3.1", - "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", - "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", "dev": true, "license": "MIT" }, "node_modules/json-schema-traverse": { "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", "dev": true, "license": "MIT" }, "node_modules/json-stable-stringify-without-jsonify": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", - "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", "dev": true, "license": "MIT" }, "node_modules/json5": { "version": "2.2.3", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", - "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", "dev": true, "license": "MIT", "bin": { @@ -13028,8 +10673,6 @@ }, "node_modules/jsonfile": { "version": "6.2.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.0.tgz", - "integrity": "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==", "dev": true, "license": "MIT", "dependencies": { @@ -13050,9 +10693,7 @@ } }, "node_modules/jsonrepair": { - "version": "3.13.2", - "resolved": "https://registry.npmjs.org/jsonrepair/-/jsonrepair-3.13.2.tgz", - "integrity": "sha512-Leuly0nbM4R+S5SVJk3VHfw1oxnlEK9KygdZvfUtEtTawNDyzB4qa1xWTmFt1aeoA7sXZkVTRuIixJ8bAvqVUg==", + "version": "3.13.1", "license": "ISC", "peer": true, "bin": { @@ -13085,8 +10726,6 @@ }, "node_modules/jssha": { "version": "3.3.1", - "resolved": "https://registry.npmjs.org/jssha/-/jssha-3.3.1.tgz", - "integrity": "sha512-VCMZj12FCFMQYcFLPRm/0lOBbLi8uM2BhXPTqw3U4YAfs4AZfiApOoBLoN8cQE60Z50m1MYMTQVCfgF/KaCVhQ==", "license": "BSD-3-Clause", "peer": true, "engines": { @@ -13095,8 +10734,6 @@ }, "node_modules/jsx-ast-utils": { "version": "3.3.5", - "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz", - "integrity": "sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==", "dev": true, "license": "MIT", "dependencies": { @@ -13135,9 +10772,7 @@ } }, "node_modules/katex": { - "version": "0.16.44", - "resolved": "https://registry.npmjs.org/katex/-/katex-0.16.44.tgz", - "integrity": "sha512-EkxoDTk8ufHqHlf9QxGwcxeLkWRR3iOuYfRpfORgYfqc8s13bgb+YtRY59NK5ZpRaCwq1kqA6a5lpX8C/eLphQ==", + "version": "0.16.38", "funding": [ "https://opencollective.com/katex", "https://github.com/sponsors/katex" @@ -13152,8 +10787,6 @@ }, "node_modules/katex/node_modules/commander": { "version": "8.3.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz", - "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==", "license": "MIT", "engines": { "node": ">= 12" @@ -13161,8 +10794,6 @@ }, "node_modules/keyv": { "version": "4.5.4", - "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", - "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", "dev": true, "license": "MIT", "dependencies": { @@ -13170,14 +10801,10 @@ } }, "node_modules/khroma": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/khroma/-/khroma-2.1.0.tgz", - "integrity": "sha512-Ls993zuzfayK269Svk9hzpeGUKob/sIgZzyHYdjQoAdQetRKpOLj+k/QQQ/6Qi0Yz65mlROrfd+Ev+1+7dz9Kw==" + "version": "2.1.0" }, "node_modules/kleur": { "version": "3.0.3", - "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", - "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", "dev": true, "license": "MIT", "engines": { @@ -13204,10 +10831,61 @@ "@langchain/core": "^1.1.31" } }, - "node_modules/langchain/node_modules/uuid": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-11.1.0.tgz", - "integrity": "sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A==", + "node_modules/langchain/node_modules/chalk": { + "version": "5.6.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz", + "integrity": "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/langchain/node_modules/langsmith": { + "version": "0.5.16", + "resolved": "https://registry.npmjs.org/langsmith/-/langsmith-0.5.16.tgz", + "integrity": "sha512-nSsSnTo3gjg1dnb48vb8i582zyjvtPbn+EpR6P1pNELb+4Hb4R3nt7LDy+Tl1ltw73vPGfJQtUWOl28irI1b5w==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^5.6.2", + "console-table-printer": "^2.12.1", + "p-queue": "^6.6.2", + "semver": "^7.6.3", + "uuid": "^10.0.0" + }, + "peerDependencies": { + "@opentelemetry/api": "*", + "@opentelemetry/exporter-trace-otlp-proto": "*", + "@opentelemetry/sdk-trace-base": "*", + "openai": "*", + "ws": ">=7" + }, + "peerDependenciesMeta": { + "@opentelemetry/api": { + "optional": true + }, + "@opentelemetry/exporter-trace-otlp-proto": { + "optional": true + }, + "@opentelemetry/sdk-trace-base": { + "optional": true + }, + "openai": { + "optional": true + }, + "ws": { + "optional": true + } + } + }, + "node_modules/langchain/node_modules/langsmith/node_modules/uuid": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-10.0.0.tgz", + "integrity": "sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==", "dev": true, "funding": [ "https://github.com/sponsors/broofa", @@ -13215,17 +10893,18 @@ ], "license": "MIT", "bin": { - "uuid": "dist/esm/bin/uuid" + "uuid": "dist/bin/uuid" } }, "node_modules/langium": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/langium/-/langium-4.2.1.tgz", - "integrity": "sha512-zu9QWmjpzJcomzdJQAHgDVhLGq5bLosVak1KVa40NzQHXfqr4eAHupvnPOVXEoLkg6Ocefvf/93d//SB7du4YQ==", + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/langium/-/langium-4.2.2.tgz", + "integrity": "sha512-JUshTRAfHI4/MF9dH2WupvjSXyn8JBuUEWazB8ZVJUtXutT0doDlAv1XKbZ1Pb5sMexa8FF4CFBc0iiul7gbUQ==", "license": "MIT", "dependencies": { - "chevrotain": "~11.1.1", - "chevrotain-allstar": "~0.3.1", + "@chevrotain/regexp-to-ast": "~12.0.0", + "chevrotain": "~12.0.0", + "chevrotain-allstar": "~0.4.1", "vscode-languageserver": "~9.0.1", "vscode-languageserver-textdocument": "~1.0.11", "vscode-uri": "~3.1.0" @@ -13236,13 +10915,15 @@ } }, "node_modules/langsmith": { - "version": "0.5.16", - "resolved": "https://registry.npmjs.org/langsmith/-/langsmith-0.5.16.tgz", - "integrity": "sha512-nSsSnTo3gjg1dnb48vb8i582zyjvtPbn+EpR6P1pNELb+4Hb4R3nt7LDy+Tl1ltw73vPGfJQtUWOl28irI1b5w==", + "version": "0.3.87", + "resolved": "https://registry.npmjs.org/langsmith/-/langsmith-0.3.87.tgz", + "integrity": "sha512-XXR1+9INH8YX96FKWc5tie0QixWz6tOqAsAKfcJyPkE0xPep+NDz0IQLR32q4bn10QK3LqD2HN6T3n6z1YLW7Q==", "dev": true, "license": "MIT", + "optional": true, "dependencies": { - "chalk": "^5.6.2", + "@types/uuid": "^10.0.0", + "chalk": "^4.1.2", "console-table-printer": "^2.12.1", "p-queue": "^6.6.2", "semver": "^7.6.3", @@ -13252,8 +10933,7 @@ "@opentelemetry/api": "*", "@opentelemetry/exporter-trace-otlp-proto": "*", "@opentelemetry/sdk-trace-base": "*", - "openai": "*", - "ws": ">=7" + "openai": "*" }, "peerDependenciesMeta": { "@opentelemetry/api": { @@ -13267,35 +10947,30 @@ }, "openai": { "optional": true - }, - "ws": { - "optional": true } } }, - "node_modules/langsmith/node_modules/chalk": { - "version": "5.6.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz", - "integrity": "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==", + "node_modules/langsmith/node_modules/uuid": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-10.0.0.tgz", + "integrity": "sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==", "dev": true, + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], "license": "MIT", - "engines": { - "node": "^12.17.0 || ^14.13 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "optional": true, + "bin": { + "uuid": "dist/bin/uuid" } }, "node_modules/layout-base": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/layout-base/-/layout-base-1.0.2.tgz", - "integrity": "sha512-8h2oVEZNktL4BH2JCOI90iD1yXwL6iNW7KcCKT2QZgQJR2vbqDsldCTPRU9NifTCqHZci57XvQQ15YTu+sTYPg==", "license": "MIT" }, "node_modules/leac": { "version": "0.6.0", - "resolved": "https://registry.npmjs.org/leac/-/leac-0.6.0.tgz", - "integrity": "sha512-y+SqErxb8h7nE/fiEX07jsbuhrpO9lL8eca7/Y1nuWV2moNlXhyd59iDGcRf6moVyDMbmTNzL40SUyrFU/yDpg==", "license": "MIT", "funding": { "url": "https://ko-fi.com/killymxi" @@ -13303,8 +10978,6 @@ }, "node_modules/levn": { "version": "0.4.1", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", - "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", "dev": true, "license": "MIT", "dependencies": { @@ -13315,258 +10988,44 @@ "node": ">= 0.8.0" } }, - "node_modules/lightningcss": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.32.0.tgz", - "integrity": "sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==", - "dev": true, - "license": "MPL-2.0", - "dependencies": { - "detect-libc": "^2.0.3" - }, - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - }, - "optionalDependencies": { - "lightningcss-android-arm64": "1.32.0", - "lightningcss-darwin-arm64": "1.32.0", - "lightningcss-darwin-x64": "1.32.0", - "lightningcss-freebsd-x64": "1.32.0", - "lightningcss-linux-arm-gnueabihf": "1.32.0", - "lightningcss-linux-arm64-gnu": "1.32.0", - "lightningcss-linux-arm64-musl": "1.32.0", - "lightningcss-linux-x64-gnu": "1.32.0", - "lightningcss-linux-x64-musl": "1.32.0", - "lightningcss-win32-arm64-msvc": "1.32.0", - "lightningcss-win32-x64-msvc": "1.32.0" - } - }, - "node_modules/lightningcss-android-arm64": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.32.0.tgz", - "integrity": "sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-darwin-arm64": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.32.0.tgz", - "integrity": "sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-darwin-x64": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.32.0.tgz", - "integrity": "sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-freebsd-x64": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.32.0.tgz", - "integrity": "sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-linux-arm-gnueabihf": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.32.0.tgz", - "integrity": "sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-linux-arm64-gnu": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.32.0.tgz", - "integrity": "sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-linux-arm64-musl": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.32.0.tgz", - "integrity": "sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-linux-x64-gnu": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.32.0.tgz", - "integrity": "sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-linux-x64-musl": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.32.0.tgz", - "integrity": "sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-win32-arm64-msvc": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.32.0.tgz", - "integrity": "sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==", - "cpu": [ - "arm64" - ], + "node_modules/lightningcss": { + "version": "1.31.1", "dev": true, "license": "MPL-2.0", - "optional": true, - "os": [ - "win32" - ], + "dependencies": { + "detect-libc": "^2.0.3" + }, "engines": { "node": ">= 12.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "lightningcss-android-arm64": "1.31.1", + "lightningcss-darwin-arm64": "1.31.1", + "lightningcss-darwin-x64": "1.31.1", + "lightningcss-freebsd-x64": "1.31.1", + "lightningcss-linux-arm-gnueabihf": "1.31.1", + "lightningcss-linux-arm64-gnu": "1.31.1", + "lightningcss-linux-arm64-musl": "1.31.1", + "lightningcss-linux-x64-gnu": "1.31.1", + "lightningcss-linux-x64-musl": "1.31.1", + "lightningcss-win32-arm64-msvc": "1.31.1", + "lightningcss-win32-x64-msvc": "1.31.1" } }, - "node_modules/lightningcss-win32-x64-msvc": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.32.0.tgz", - "integrity": "sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==", + "node_modules/lightningcss-darwin-arm64": { + "version": "1.31.1", "cpu": [ - "x64" + "arm64" ], "dev": true, "license": "MPL-2.0", "optional": true, "os": [ - "win32" + "darwin" ], "engines": { "node": ">= 12.0.0" @@ -13578,8 +11037,6 @@ }, "node_modules/lilconfig": { "version": "3.1.3", - "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz", - "integrity": "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==", "dev": true, "license": "MIT", "engines": { @@ -13591,8 +11048,6 @@ }, "node_modules/lines-and-columns": { "version": "1.2.4", - "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", - "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", "dev": true, "license": "MIT" }, @@ -13634,8 +11089,6 @@ }, "node_modules/locate-path": { "version": "6.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", - "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", "dev": true, "license": "MIT", "dependencies": { @@ -13649,22 +11102,16 @@ } }, "node_modules/lodash": { - "version": "4.18.1", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.18.1.tgz", - "integrity": "sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==", + "version": "4.17.23", "license": "MIT", "peer": true }, "node_modules/lodash-es": { - "version": "4.18.1", - "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.18.1.tgz", - "integrity": "sha512-J8xewKD/Gk22OZbhpOVSwcs60zhd95ESDwezOFuA3/099925PdHJ7OFHNTGtajL3AlZkykD32HykiMo+BIBI8A==", + "version": "4.17.23", "license": "MIT" }, "node_modules/lodash.debounce": { "version": "4.0.8", - "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", - "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==", "license": "MIT" }, "node_modules/lodash.includes": { @@ -13717,8 +11164,6 @@ }, "node_modules/lodash.merge": { "version": "4.6.2", - "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", - "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", "dev": true, "license": "MIT" }, @@ -13732,8 +11177,6 @@ }, "node_modules/longest-streak": { "version": "3.1.0", - "resolved": "https://registry.npmjs.org/longest-streak/-/longest-streak-3.1.0.tgz", - "integrity": "sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==", "license": "MIT", "funding": { "type": "github", @@ -13742,8 +11185,6 @@ }, "node_modules/loose-envify": { "version": "1.4.0", - "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", - "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", "license": "MIT", "dependencies": { "js-tokens": "^3.0.0 || ^4.0.0" @@ -13754,8 +11195,6 @@ }, "node_modules/loupe": { "version": "3.2.1", - "resolved": "https://registry.npmjs.org/loupe/-/loupe-3.2.1.tgz", - "integrity": "sha512-CdzqowRJCeLU72bHvWqwRBBlLcMEtIvGrlvef74kMnV2AolS9Y8xUv1I0U/MNAWMhBlKIoyuEgoJ0t/bbwHbLQ==", "dev": true, "license": "MIT" }, @@ -13771,8 +11210,6 @@ }, "node_modules/lru-cache": { "version": "5.1.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", - "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", "dev": true, "license": "ISC", "dependencies": { @@ -13780,9 +11217,7 @@ } }, "node_modules/luxon": { - "version": "3.7.2", - "resolved": "https://registry.npmjs.org/luxon/-/luxon-3.7.2.tgz", - "integrity": "sha512-vtEhXh/gNjI9Yg1u4jX/0YVPMvxzHuGgCm6tC5kZyb08yjGWGnqAjGJvcXbqQR2P3MyMEFnRbpcdFS6PBcLqew==", + "version": "3.4.4", "license": "MIT", "peer": true, "engines": { @@ -13791,8 +11226,6 @@ }, "node_modules/lz-string": { "version": "1.5.0", - "resolved": "https://registry.npmjs.org/lz-string/-/lz-string-1.5.0.tgz", - "integrity": "sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==", "dev": true, "license": "MIT", "peer": true, @@ -13802,23 +11235,43 @@ }, "node_modules/magic-string": { "version": "0.30.21", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", - "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", "dev": true, "license": "MIT", "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.5" } }, + "node_modules/magicast": { + "version": "0.5.2", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.29.0", + "@babel/types": "^7.29.0", + "source-map-js": "^1.2.1" + } + }, "node_modules/make-cancellable-promise": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/make-cancellable-promise/-/make-cancellable-promise-2.0.0.tgz", - "integrity": "sha512-3SEQqTpV9oqVsIWqAcmDuaNeo7yBO3tqPtqGRcKkEo0lrzD3wqbKG9mkxO65KoOgXqj+zH2phJ2LiAsdzlogSw==", "license": "MIT", "funding": { "url": "https://github.com/wojtekmaj/make-cancellable-promise?sponsor=1" } }, + "node_modules/make-dir": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "semver": "^7.5.3" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/make-error": { "version": "1.3.6", "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", @@ -13829,8 +11282,6 @@ }, "node_modules/make-event-props": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/make-event-props/-/make-event-props-2.0.0.tgz", - "integrity": "sha512-G/hncXrl4Qt7mauJEXSg3AcdYzmpkIITTNl5I+rH9sog5Yw0kK6vseJjCaPfOXqOqQuPUP89Rkhfz5kPS8ijtw==", "license": "MIT", "funding": { "url": "https://github.com/wojtekmaj/make-event-props?sponsor=1" @@ -13853,10 +11304,14 @@ "markdown-it": "bin/markdown-it.mjs" } }, + "node_modules/markdown-it/node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "license": "Python-2.0" + }, "node_modules/markdown-table": { "version": "3.0.4", - "resolved": "https://registry.npmjs.org/markdown-table/-/markdown-table-3.0.4.tgz", - "integrity": "sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw==", "license": "MIT", "funding": { "type": "github", @@ -13865,8 +11320,6 @@ }, "node_modules/marked": { "version": "15.0.12", - "resolved": "https://registry.npmjs.org/marked/-/marked-15.0.12.tgz", - "integrity": "sha512-8dD6FusOQSrpv9Z1rdNMdlSgQOIP880DHqnohobOmYLElGEqAL/JvxvuxZO16r4HtjTlfPRDC1hbvxC9dPN2nA==", "license": "MIT", "bin": { "marked": "bin/marked.js" @@ -13884,8 +11337,6 @@ }, "node_modules/math-intrinsics": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", - "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", "license": "MIT", "engines": { "node": ">= 0.4" @@ -13893,8 +11344,6 @@ }, "node_modules/md5": { "version": "2.3.0", - "resolved": "https://registry.npmjs.org/md5/-/md5-2.3.0.tgz", - "integrity": "sha512-T1GITYmFaKuO91vxyoQMFETst+O71VUPEU3ze5GNzDm0OWdP8v1ziTaAEPUr/3kLsY3Sftgz242A1SetQiDL7g==", "license": "BSD-3-Clause", "peer": true, "dependencies": { @@ -13905,8 +11354,6 @@ }, "node_modules/mdast-util-find-and-replace": { "version": "3.0.2", - "resolved": "https://registry.npmjs.org/mdast-util-find-and-replace/-/mdast-util-find-and-replace-3.0.2.tgz", - "integrity": "sha512-Tmd1Vg/m3Xz43afeNxDIhWRtFZgM2VLyaf4vSTYwudTyeuTneoL3qtWMA5jeLyz/O1vDJmmV4QuScFCA2tBPwg==", "license": "MIT", "dependencies": { "@types/mdast": "^4.0.0", @@ -13921,8 +11368,6 @@ }, "node_modules/mdast-util-find-and-replace/node_modules/escape-string-regexp": { "version": "5.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz", - "integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==", "license": "MIT", "engines": { "node": ">=12" @@ -13933,8 +11378,6 @@ }, "node_modules/mdast-util-from-markdown": { "version": "2.0.3", - "resolved": "https://registry.npmjs.org/mdast-util-from-markdown/-/mdast-util-from-markdown-2.0.3.tgz", - "integrity": "sha512-W4mAWTvSlKvf8L6J+VN9yLSqQ9AOAAvHuoDAmPkz4dHf553m5gVj2ejadHJhoJmcmxEnOv6Pa8XJhpxE93kb8Q==", "license": "MIT", "dependencies": { "@types/mdast": "^4.0.0", @@ -13957,8 +11400,6 @@ }, "node_modules/mdast-util-gfm": { "version": "3.1.0", - "resolved": "https://registry.npmjs.org/mdast-util-gfm/-/mdast-util-gfm-3.1.0.tgz", - "integrity": "sha512-0ulfdQOM3ysHhCJ1p06l0b0VKlhU0wuQs3thxZQagjcjPrlFRqY215uZGHHJan9GEAXd9MbfPjFJz+qMkVR6zQ==", "license": "MIT", "dependencies": { "mdast-util-from-markdown": "^2.0.0", @@ -13976,8 +11417,6 @@ }, "node_modules/mdast-util-gfm-autolink-literal": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/mdast-util-gfm-autolink-literal/-/mdast-util-gfm-autolink-literal-2.0.1.tgz", - "integrity": "sha512-5HVP2MKaP6L+G6YaxPNjuL0BPrq9orG3TsrZ9YXbA3vDw/ACI4MEsnoDpn6ZNm7GnZgtAcONJyPhOP8tNJQavQ==", "license": "MIT", "dependencies": { "@types/mdast": "^4.0.0", @@ -13993,8 +11432,6 @@ }, "node_modules/mdast-util-gfm-footnote": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mdast-util-gfm-footnote/-/mdast-util-gfm-footnote-2.1.0.tgz", - "integrity": "sha512-sqpDWlsHn7Ac9GNZQMeUzPQSMzR6Wv0WKRNvQRg0KqHh02fpTz69Qc1QSseNX29bhz1ROIyNyxExfawVKTm1GQ==", "license": "MIT", "dependencies": { "@types/mdast": "^4.0.0", @@ -14010,8 +11447,6 @@ }, "node_modules/mdast-util-gfm-strikethrough": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/mdast-util-gfm-strikethrough/-/mdast-util-gfm-strikethrough-2.0.0.tgz", - "integrity": "sha512-mKKb915TF+OC5ptj5bJ7WFRPdYtuHv0yTRxK2tJvi+BDqbkiG7h7u/9SI89nRAYcmap2xHQL9D+QG/6wSrTtXg==", "license": "MIT", "dependencies": { "@types/mdast": "^4.0.0", @@ -14025,8 +11460,6 @@ }, "node_modules/mdast-util-gfm-table": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/mdast-util-gfm-table/-/mdast-util-gfm-table-2.0.0.tgz", - "integrity": "sha512-78UEvebzz/rJIxLvE7ZtDd/vIQ0RHv+3Mh5DR96p7cS7HsBhYIICDBCu8csTNWNO6tBWfqXPWekRuj2FNOGOZg==", "license": "MIT", "dependencies": { "@types/mdast": "^4.0.0", @@ -14042,8 +11475,6 @@ }, "node_modules/mdast-util-gfm-task-list-item": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/mdast-util-gfm-task-list-item/-/mdast-util-gfm-task-list-item-2.0.0.tgz", - "integrity": "sha512-IrtvNvjxC1o06taBAVJznEnkiHxLFTzgonUdy8hzFVeDun0uTjxxrRGVaNFqkU1wJR3RBPEfsxmU6jDWPofrTQ==", "license": "MIT", "dependencies": { "@types/mdast": "^4.0.0", @@ -14058,8 +11489,6 @@ }, "node_modules/mdast-util-mdx-expression": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/mdast-util-mdx-expression/-/mdast-util-mdx-expression-2.0.1.tgz", - "integrity": "sha512-J6f+9hUp+ldTZqKRSg7Vw5V6MqjATc+3E4gf3CFNcuZNWD8XdyI6zQ8GqH7f8169MM6P7hMBRDVGnn7oHB9kXQ==", "license": "MIT", "dependencies": { "@types/estree-jsx": "^1.0.0", @@ -14076,8 +11505,6 @@ }, "node_modules/mdast-util-mdx-jsx": { "version": "3.2.0", - "resolved": "https://registry.npmjs.org/mdast-util-mdx-jsx/-/mdast-util-mdx-jsx-3.2.0.tgz", - "integrity": "sha512-lj/z8v0r6ZtsN/cGNNtemmmfoLAFZnjMbNyLzBafjzikOM+glrjNHPlf6lQDOTccj9n5b0PPihEBbhneMyGs1Q==", "license": "MIT", "dependencies": { "@types/estree-jsx": "^1.0.0", @@ -14100,8 +11527,6 @@ }, "node_modules/mdast-util-mdxjs-esm": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/mdast-util-mdxjs-esm/-/mdast-util-mdxjs-esm-2.0.1.tgz", - "integrity": "sha512-EcmOpxsZ96CvlP03NghtH1EsLtr0n9Tm4lPUJUBccV9RwUOneqSycg19n5HGzCf+10LozMRSObtVr3ee1WoHtg==", "license": "MIT", "dependencies": { "@types/estree-jsx": "^1.0.0", @@ -14118,8 +11543,6 @@ }, "node_modules/mdast-util-phrasing": { "version": "4.1.0", - "resolved": "https://registry.npmjs.org/mdast-util-phrasing/-/mdast-util-phrasing-4.1.0.tgz", - "integrity": "sha512-TqICwyvJJpBwvGAMZjj4J2n0X8QWp21b9l0o7eXyVJ25YNWYbJDVIyD1bZXE6WtV6RmKJVYmQAKWa0zWOABz2w==", "license": "MIT", "dependencies": { "@types/mdast": "^4.0.0", @@ -14132,8 +11555,6 @@ }, "node_modules/mdast-util-to-hast": { "version": "13.2.1", - "resolved": "https://registry.npmjs.org/mdast-util-to-hast/-/mdast-util-to-hast-13.2.1.tgz", - "integrity": "sha512-cctsq2wp5vTsLIcaymblUriiTcZd0CwWtCbLvrOzYCDZoWyMNV8sZ7krj09FSnsiJi3WVsHLM4k6Dq/yaPyCXA==", "license": "MIT", "dependencies": { "@types/hast": "^3.0.0", @@ -14153,8 +11574,6 @@ }, "node_modules/mdast-util-to-markdown": { "version": "2.1.2", - "resolved": "https://registry.npmjs.org/mdast-util-to-markdown/-/mdast-util-to-markdown-2.1.2.tgz", - "integrity": "sha512-xj68wMTvGXVOKonmog6LwyJKrYXZPvlwabaryTjLh9LuvovB/KAH+kvi8Gjj+7rJjsFi23nkUxRQv1KqSroMqA==", "license": "MIT", "dependencies": { "@types/mdast": "^4.0.0", @@ -14174,8 +11593,6 @@ }, "node_modules/mdast-util-to-string": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/mdast-util-to-string/-/mdast-util-to-string-4.0.0.tgz", - "integrity": "sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==", "license": "MIT", "dependencies": { "@types/mdast": "^4.0.0" @@ -14193,8 +11610,6 @@ }, "node_modules/merge-refs": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/merge-refs/-/merge-refs-2.0.0.tgz", - "integrity": "sha512-3+B21mYK2IqUWnd2EivABLT7ueDhb0b8/dGK8LoFQPrU61YITeCMn14F7y7qZafWNZhUEKb24cJdiT5Wxs3prg==", "license": "MIT", "funding": { "url": "https://github.com/wojtekmaj/merge-refs?sponsor=1" @@ -14249,8 +11664,6 @@ }, "node_modules/mermaid/node_modules/marked": { "version": "16.4.2", - "resolved": "https://registry.npmjs.org/marked/-/marked-16.4.2.tgz", - "integrity": "sha512-TI3V8YYWvkVf3KJe1dRkpnjs68JUPyEa5vjKrp1XEEJUAOaQc+Qj+L1qWbPd0SJuAdQkFU0h73sXXqwDYxsiDA==", "license": "MIT", "bin": { "marked": "bin/marked.js" @@ -14259,23 +11672,8 @@ "node": ">= 20" } }, - "node_modules/mermaid/node_modules/uuid": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-11.1.0.tgz", - "integrity": "sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A==", - "funding": [ - "https://github.com/sponsors/broofa", - "https://github.com/sponsors/ctavan" - ], - "license": "MIT", - "bin": { - "uuid": "dist/esm/bin/uuid" - } - }, "node_modules/micromark": { "version": "4.0.2", - "resolved": "https://registry.npmjs.org/micromark/-/micromark-4.0.2.tgz", - "integrity": "sha512-zpe98Q6kvavpCr1NPVSCMebCKfD7CA2NqZ+rykeNhONIJBpc1tFKt9hucLGwha3jNTNI8lHpctWJWoimVF4PfA==", "funding": [ { "type": "GitHub Sponsors", @@ -14309,8 +11707,6 @@ }, "node_modules/micromark-core-commonmark": { "version": "2.0.3", - "resolved": "https://registry.npmjs.org/micromark-core-commonmark/-/micromark-core-commonmark-2.0.3.tgz", - "integrity": "sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg==", "funding": [ { "type": "GitHub Sponsors", @@ -14343,8 +11739,6 @@ }, "node_modules/micromark-extension-gfm": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/micromark-extension-gfm/-/micromark-extension-gfm-3.0.0.tgz", - "integrity": "sha512-vsKArQsicm7t0z2GugkCKtZehqUm31oeGBV/KVSorWSy8ZlNAv7ytjFhvaryUiCUJYqs+NoE6AFhpQvBTM6Q4w==", "license": "MIT", "dependencies": { "micromark-extension-gfm-autolink-literal": "^2.0.0", @@ -14363,8 +11757,6 @@ }, "node_modules/micromark-extension-gfm-autolink-literal": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/micromark-extension-gfm-autolink-literal/-/micromark-extension-gfm-autolink-literal-2.1.0.tgz", - "integrity": "sha512-oOg7knzhicgQ3t4QCjCWgTmfNhvQbDDnJeVu9v81r7NltNCVmhPy1fJRX27pISafdjL+SVc4d3l48Gb6pbRypw==", "license": "MIT", "dependencies": { "micromark-util-character": "^2.0.0", @@ -14379,8 +11771,6 @@ }, "node_modules/micromark-extension-gfm-footnote": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/micromark-extension-gfm-footnote/-/micromark-extension-gfm-footnote-2.1.0.tgz", - "integrity": "sha512-/yPhxI1ntnDNsiHtzLKYnE3vf9JZ6cAisqVDauhp4CEHxlb4uoOTxOCJ+9s51bIB8U1N1FJ1RXOKTIlD5B/gqw==", "license": "MIT", "dependencies": { "devlop": "^1.0.0", @@ -14399,8 +11789,6 @@ }, "node_modules/micromark-extension-gfm-strikethrough": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/micromark-extension-gfm-strikethrough/-/micromark-extension-gfm-strikethrough-2.1.0.tgz", - "integrity": "sha512-ADVjpOOkjz1hhkZLlBiYA9cR2Anf8F4HqZUO6e5eDcPQd0Txw5fxLzzxnEkSkfnD0wziSGiv7sYhk/ktvbf1uw==", "license": "MIT", "dependencies": { "devlop": "^1.0.0", @@ -14417,8 +11805,6 @@ }, "node_modules/micromark-extension-gfm-table": { "version": "2.1.1", - "resolved": "https://registry.npmjs.org/micromark-extension-gfm-table/-/micromark-extension-gfm-table-2.1.1.tgz", - "integrity": "sha512-t2OU/dXXioARrC6yWfJ4hqB7rct14e8f7m0cbI5hUmDyyIlwv5vEtooptH8INkbLzOatzKuVbQmAYcbWoyz6Dg==", "license": "MIT", "dependencies": { "devlop": "^1.0.0", @@ -14434,8 +11820,6 @@ }, "node_modules/micromark-extension-gfm-tagfilter": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-extension-gfm-tagfilter/-/micromark-extension-gfm-tagfilter-2.0.0.tgz", - "integrity": "sha512-xHlTOmuCSotIA8TW1mDIM6X2O1SiX5P9IuDtqGonFhEK0qgRI4yeC6vMxEV2dgyr2TiD+2PQ10o+cOhdVAcwfg==", "license": "MIT", "dependencies": { "micromark-util-types": "^2.0.0" @@ -14447,8 +11831,6 @@ }, "node_modules/micromark-extension-gfm-task-list-item": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/micromark-extension-gfm-task-list-item/-/micromark-extension-gfm-task-list-item-2.1.0.tgz", - "integrity": "sha512-qIBZhqxqI6fjLDYFTBIa4eivDMnP+OZqsNwmQ3xNLE4Cxwc+zfQEfbs6tzAo2Hjq+bh6q5F+Z8/cksrLFYWQQw==", "license": "MIT", "dependencies": { "devlop": "^1.0.0", @@ -14464,8 +11846,6 @@ }, "node_modules/micromark-factory-destination": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-factory-destination/-/micromark-factory-destination-2.0.1.tgz", - "integrity": "sha512-Xe6rDdJlkmbFRExpTOmRj9N3MaWmbAgdpSrBQvCFqhezUn4AHqJHbaEnfbVYYiexVSs//tqOdY/DxhjdCiJnIA==", "funding": [ { "type": "GitHub Sponsors", @@ -14485,8 +11865,6 @@ }, "node_modules/micromark-factory-label": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-factory-label/-/micromark-factory-label-2.0.1.tgz", - "integrity": "sha512-VFMekyQExqIW7xIChcXn4ok29YE3rnuyveW3wZQWWqF4Nv9Wk5rgJ99KzPvHjkmPXF93FXIbBp6YdW3t71/7Vg==", "funding": [ { "type": "GitHub Sponsors", @@ -14507,8 +11885,6 @@ }, "node_modules/micromark-factory-space": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-2.0.1.tgz", - "integrity": "sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg==", "funding": [ { "type": "GitHub Sponsors", @@ -14527,8 +11903,6 @@ }, "node_modules/micromark-factory-title": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-factory-title/-/micromark-factory-title-2.0.1.tgz", - "integrity": "sha512-5bZ+3CjhAd9eChYTHsjy6TGxpOFSKgKKJPJxr293jTbfry2KDoWkhBb6TcPVB4NmzaPhMs1Frm9AZH7OD4Cjzw==", "funding": [ { "type": "GitHub Sponsors", @@ -14549,8 +11923,6 @@ }, "node_modules/micromark-factory-whitespace": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-factory-whitespace/-/micromark-factory-whitespace-2.0.1.tgz", - "integrity": "sha512-Ob0nuZ3PKt/n0hORHyvoD9uZhr+Za8sFoP+OnMcnWK5lngSzALgQYKMr9RJVOWLqQYuyn6ulqGWSXdwf6F80lQ==", "funding": [ { "type": "GitHub Sponsors", @@ -14571,8 +11943,6 @@ }, "node_modules/micromark-util-character": { "version": "2.1.1", - "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.1.tgz", - "integrity": "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==", "funding": [ { "type": "GitHub Sponsors", @@ -14591,8 +11961,6 @@ }, "node_modules/micromark-util-chunked": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-chunked/-/micromark-util-chunked-2.0.1.tgz", - "integrity": "sha512-QUNFEOPELfmvv+4xiNg2sRYeS/P84pTW0TCgP5zc9FpXetHY0ab7SxKyAQCNCc1eK0459uoLI1y5oO5Vc1dbhA==", "funding": [ { "type": "GitHub Sponsors", @@ -14610,8 +11978,6 @@ }, "node_modules/micromark-util-classify-character": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-classify-character/-/micromark-util-classify-character-2.0.1.tgz", - "integrity": "sha512-K0kHzM6afW/MbeWYWLjoHQv1sgg2Q9EccHEDzSkxiP/EaagNzCm7T/WMKZ3rjMbvIpvBiZgwR3dKMygtA4mG1Q==", "funding": [ { "type": "GitHub Sponsors", @@ -14631,8 +11997,6 @@ }, "node_modules/micromark-util-combine-extensions": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-combine-extensions/-/micromark-util-combine-extensions-2.0.1.tgz", - "integrity": "sha512-OnAnH8Ujmy59JcyZw8JSbK9cGpdVY44NKgSM7E9Eh7DiLS2E9RNQf0dONaGDzEG9yjEl5hcqeIsj4hfRkLH/Bg==", "funding": [ { "type": "GitHub Sponsors", @@ -14651,8 +12015,6 @@ }, "node_modules/micromark-util-decode-numeric-character-reference": { "version": "2.0.2", - "resolved": "https://registry.npmjs.org/micromark-util-decode-numeric-character-reference/-/micromark-util-decode-numeric-character-reference-2.0.2.tgz", - "integrity": "sha512-ccUbYk6CwVdkmCQMyr64dXz42EfHGkPQlBj5p7YVGzq8I7CtjXZJrubAYezf7Rp+bjPseiROqe7G6foFd+lEuw==", "funding": [ { "type": "GitHub Sponsors", @@ -14670,8 +12032,6 @@ }, "node_modules/micromark-util-decode-string": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-decode-string/-/micromark-util-decode-string-2.0.1.tgz", - "integrity": "sha512-nDV/77Fj6eH1ynwscYTOsbK7rR//Uj0bZXBwJZRfaLEJ1iGBR6kIfNmlNqaqJf649EP0F3NWNdeJi03elllNUQ==", "funding": [ { "type": "GitHub Sponsors", @@ -14692,8 +12052,6 @@ }, "node_modules/micromark-util-encode": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-encode/-/micromark-util-encode-2.0.1.tgz", - "integrity": "sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==", "funding": [ { "type": "GitHub Sponsors", @@ -14708,8 +12066,6 @@ }, "node_modules/micromark-util-html-tag-name": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-html-tag-name/-/micromark-util-html-tag-name-2.0.1.tgz", - "integrity": "sha512-2cNEiYDhCWKI+Gs9T0Tiysk136SnR13hhO8yW6BGNyhOC4qYFnwF1nKfD3HFAIXA5c45RrIG1ub11GiXeYd1xA==", "funding": [ { "type": "GitHub Sponsors", @@ -14724,8 +12080,6 @@ }, "node_modules/micromark-util-normalize-identifier": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-normalize-identifier/-/micromark-util-normalize-identifier-2.0.1.tgz", - "integrity": "sha512-sxPqmo70LyARJs0w2UclACPUUEqltCkJ6PhKdMIDuJ3gSf/Q+/GIe3WKl0Ijb/GyH9lOpUkRAO2wp0GVkLvS9Q==", "funding": [ { "type": "GitHub Sponsors", @@ -14743,8 +12097,6 @@ }, "node_modules/micromark-util-resolve-all": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-resolve-all/-/micromark-util-resolve-all-2.0.1.tgz", - "integrity": "sha512-VdQyxFWFT2/FGJgwQnJYbe1jjQoNTS4RjglmSjTUlpUMa95Htx9NHeYW4rGDJzbjvCsl9eLjMQwGeElsqmzcHg==", "funding": [ { "type": "GitHub Sponsors", @@ -14762,8 +12114,6 @@ }, "node_modules/micromark-util-sanitize-uri": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-2.0.1.tgz", - "integrity": "sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==", "funding": [ { "type": "GitHub Sponsors", @@ -14783,8 +12133,6 @@ }, "node_modules/micromark-util-subtokenize": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/micromark-util-subtokenize/-/micromark-util-subtokenize-2.1.0.tgz", - "integrity": "sha512-XQLu552iSctvnEcgXw6+Sx75GflAPNED1qx7eBJ+wydBb2KCbRZe+NwvIEEMM83uml1+2WSXpBAcp9IUCgCYWA==", "funding": [ { "type": "GitHub Sponsors", @@ -14805,8 +12153,6 @@ }, "node_modules/micromark-util-symbol": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz", - "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==", "funding": [ { "type": "GitHub Sponsors", @@ -14821,8 +12167,6 @@ }, "node_modules/micromark-util-types": { "version": "2.0.2", - "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-2.0.2.tgz", - "integrity": "sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA==", "funding": [ { "type": "GitHub Sponsors", @@ -14864,8 +12208,6 @@ }, "node_modules/mime-db": { "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", "license": "MIT", "peer": true, "engines": { @@ -14874,8 +12216,6 @@ }, "node_modules/mime-types": { "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", "license": "MIT", "peer": true, "dependencies": { @@ -14887,8 +12227,6 @@ }, "node_modules/min-indent": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz", - "integrity": "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==", "dev": true, "license": "MIT", "engines": { @@ -14896,16 +12234,14 @@ } }, "node_modules/minimatch": { - "version": "10.2.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", - "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==", + "version": "9.0.9", "dev": true, - "license": "BlueOak-1.0.0", + "license": "ISC", "dependencies": { - "brace-expansion": "^5.0.5" + "brace-expansion": "^2.0.2" }, "engines": { - "node": "18 || 20 || >=22" + "node": ">=16 || 14 >=14.17" }, "funding": { "url": "https://github.com/sponsors/isaacs" @@ -14913,8 +12249,6 @@ }, "node_modules/minimist": { "version": "1.2.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", - "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", "dev": true, "license": "MIT", "funding": { @@ -14923,8 +12257,6 @@ }, "node_modules/minipass": { "version": "7.1.3", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.3.tgz", - "integrity": "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==", "dev": true, "license": "BlueOak-1.0.0", "engines": { @@ -14933,14 +12265,10 @@ }, "node_modules/minisearch": { "version": "7.2.0", - "resolved": "https://registry.npmjs.org/minisearch/-/minisearch-7.2.0.tgz", - "integrity": "sha512-dqT2XBYUOZOiC5t2HRnwADjhNS2cecp9u+TJRiJ1Qp/f5qjkeT5APcGPjHw+bz89Ms8Jp+cG4AlE+QZ/QnDglg==", "license": "MIT" }, "node_modules/mlly": { - "version": "1.8.2", - "resolved": "https://registry.npmjs.org/mlly/-/mlly-1.8.2.tgz", - "integrity": "sha512-d+ObxMQFmbt10sretNDytwt85VrbkhhUA/JBGm1MPaWJ65Cl4wOgLaB1NYvJSZ0Ef03MMEU/0xpPMXUIQ29UfA==", + "version": "1.8.1", "license": "MIT", "dependencies": { "acorn": "^8.16.0", @@ -14949,10 +12277,16 @@ "ufo": "^1.6.3" } }, + "node_modules/mrmime": { + "version": "2.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + } + }, "node_modules/ms": { "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "license": "MIT" }, "node_modules/mustache": { @@ -14966,65 +12300,40 @@ } }, "node_modules/n8n-workflow": { - "version": "2.13.1", - "resolved": "https://registry.npmjs.org/n8n-workflow/-/n8n-workflow-2.13.1.tgz", - "integrity": "sha512-79qJU7aIwtzZY1bhYhFwjLs83yu4Cb+ZFC7+hjN9N+9+mRV0CObHTR9tg5/EwD1HURfztwdCK+N/dlfjO8xDlw==", + "version": "1.120.9", "license": "SEE LICENSE IN LICENSE.md", "peer": true, "dependencies": { - "@n8n/errors": "0.6.0", - "@n8n/expression-runtime": "0.5.0", + "@n8n/errors": "0.5.0", "@n8n/tournament": "1.0.6", "ast-types": "0.16.1", "callsites": "3.1.0", "esprima-next": "5.8.4", - "form-data": "4.0.4", + "form-data": "4.0.0", "jmespath": "0.16.0", "js-base64": "3.7.2", - "jsonrepair": "3.13.2", + "jsonrepair": "3.13.1", "jssha": "3.3.1", "lodash": "4.17.23", - "luxon": "3.7.2", + "luxon": "3.4.4", "md5": "2.3.0", "recast": "0.22.0", "title-case": "3.0.3", "transliteration": "2.3.5", - "uuid": "10.0.0", "xml2js": "0.6.2", "zod": "3.25.67" } }, "node_modules/n8n-workflow/node_modules/callsites": { "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", "license": "MIT", "peer": true, "engines": { "node": ">=6" } }, - "node_modules/n8n-workflow/node_modules/form-data": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.4.tgz", - "integrity": "sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==", - "license": "MIT", - "peer": true, - "dependencies": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "es-set-tostringtag": "^2.1.0", - "hasown": "^2.0.2", - "mime-types": "^2.1.12" - }, - "engines": { - "node": ">= 6" - } - }, "node_modules/n8n-workflow/node_modules/zod": { "version": "3.25.67", - "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.67.tgz", - "integrity": "sha512-idA2YXwpCdqUSKRCACDE6ItZD9TZzy3OZMtpfLoh6oPR47lipysRrJfjzMqFxQ3uJuUPyUeWe1r9vLH33xO/Qw==", "license": "MIT", "peer": true, "funding": { @@ -15033,8 +12342,6 @@ }, "node_modules/nanoid": { "version": "3.3.11", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", - "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", "dev": true, "funding": [ { @@ -15052,8 +12359,6 @@ }, "node_modules/napi-postinstall": { "version": "0.3.4", - "resolved": "https://registry.npmjs.org/napi-postinstall/-/napi-postinstall-0.3.4.tgz", - "integrity": "sha512-PHI5f1O0EP5xJ9gQmFGMS6IZcrVvTjpXjz7Na41gTE7eE2hK11lg04CECCYEEjdc17EV4DO+fkGEtt7TpTaTiQ==", "dev": true, "license": "MIT", "bin": { @@ -15068,15 +12373,11 @@ }, "node_modules/natural-compare": { "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", "dev": true, "license": "MIT" }, "node_modules/neo-async": { "version": "2.6.2", - "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", - "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", "dev": true, "license": "MIT" }, @@ -15115,8 +12416,6 @@ }, "node_modules/node-exports-info": { "version": "1.6.0", - "resolved": "https://registry.npmjs.org/node-exports-info/-/node-exports-info-1.6.0.tgz", - "integrity": "sha512-pyFS63ptit/P5WqUkt+UUfe+4oevH+bFeIiPPdfb0pFeYEu/1ELnJu5l+5EcTKYL5M7zaAa7S8ddywgXypqKCw==", "dev": true, "license": "MIT", "dependencies": { @@ -15134,8 +12433,6 @@ }, "node_modules/node-exports-info/node_modules/semver": { "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true, "license": "ISC", "bin": { @@ -15144,8 +12441,6 @@ }, "node_modules/node-fetch": { "version": "2.7.0", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", - "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", "license": "MIT", "dependencies": { "whatwg-url": "^5.0.0" @@ -15162,35 +12457,17 @@ } } }, - "node_modules/node-gyp-build": { - "version": "4.8.4", - "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.8.4.tgz", - "integrity": "sha512-LA4ZjwlnUblHVgq0oBF3Jl/6h/Nvs5fzBLwdEF4nuxnFdsfajde4WfxtJr3CaiH+F6ewcIB/q4jQ4UzPyid+CQ==", - "license": "MIT", - "peer": true, - "bin": { - "node-gyp-build": "bin.js", - "node-gyp-build-optional": "optional.js", - "node-gyp-build-test": "build-test.js" - } - }, "node_modules/node-releases": { - "version": "2.0.37", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.37.tgz", - "integrity": "sha512-1h5gKZCF+pO/o3Iqt5Jp7wc9rH3eJJ0+nh/CIoiRwjRxde/hAHyLPXYN4V3CqKAbiZPSeJFSWHmJsbkicta0Eg==", + "version": "2.0.27", "dev": true, "license": "MIT" }, "node_modules/nullthrows": { "version": "1.1.1", - "resolved": "https://registry.npmjs.org/nullthrows/-/nullthrows-1.1.1.tgz", - "integrity": "sha512-2vPPEi+Z7WqML2jZYddDIfy5Dqb0r2fze2zTxNNknZaFpVHU3mFB3R+DWeJWGVx0ecvttSGlJTI+WG+8Z4cDWw==", "license": "MIT" }, "node_modules/object-assign": { "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", "license": "MIT", "engines": { "node": ">=0.10.0" @@ -15198,8 +12475,6 @@ }, "node_modules/object-inspect": { "version": "1.13.4", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", - "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", "dev": true, "license": "MIT", "engines": { @@ -15211,8 +12486,6 @@ }, "node_modules/object-is": { "version": "1.1.6", - "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.6.tgz", - "integrity": "sha512-F8cZ+KfGlSGi09lJT7/Nd6KJZ9ygtvYC0/UYYLI9nmQKLMnydpB9yvbv9K1uSkEu7FU9vYPmVwLg328tX+ot3Q==", "license": "MIT", "peer": true, "dependencies": { @@ -15228,8 +12501,6 @@ }, "node_modules/object-keys": { "version": "1.1.1", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", - "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", "license": "MIT", "engines": { "node": ">= 0.4" @@ -15237,8 +12508,6 @@ }, "node_modules/object.assign": { "version": "4.1.7", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.7.tgz", - "integrity": "sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==", "license": "MIT", "dependencies": { "call-bind": "^1.0.8", @@ -15257,8 +12526,6 @@ }, "node_modules/object.entries": { "version": "1.1.9", - "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.9.tgz", - "integrity": "sha512-8u/hfXFRBD1O0hPUjioLhoWFHRmt6tKA4/vZPyckBr18l1KE9uHrFaFaUi8MDRTpi4uak2goyPTSNJLXX2k2Hw==", "dev": true, "license": "MIT", "dependencies": { @@ -15273,8 +12540,6 @@ }, "node_modules/object.fromentries": { "version": "2.0.8", - "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.8.tgz", - "integrity": "sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==", "dev": true, "license": "MIT", "dependencies": { @@ -15292,8 +12557,6 @@ }, "node_modules/object.values": { "version": "1.2.1", - "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.2.1.tgz", - "integrity": "sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA==", "dev": true, "license": "MIT", "dependencies": { @@ -15309,6 +12572,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/obug": { + "version": "2.1.1", + "dev": true, + "funding": [ + "https://github.com/sponsors/sxzz", + "https://opencollective.com/debug" + ], + "license": "MIT" + }, "node_modules/once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", @@ -15322,8 +12594,6 @@ }, "node_modules/open": { "version": "10.2.0", - "resolved": "https://registry.npmjs.org/open/-/open-10.2.0.tgz", - "integrity": "sha512-YgBpdJHPyQ2UE5x+hlSXcnejzAvD0b22U2OuAP+8OnlJT+PjWPxtgmGqKKc+RgTM63U9gN0YzrYc71R2WT/hTA==", "dev": true, "license": "MIT", "dependencies": { @@ -15399,8 +12669,6 @@ }, "node_modules/optionator": { "version": "0.9.4", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", - "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", "dev": true, "license": "MIT", "dependencies": { @@ -15423,8 +12691,6 @@ }, "node_modules/own-keys": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/own-keys/-/own-keys-1.0.1.tgz", - "integrity": "sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==", "dev": true, "license": "MIT", "dependencies": { @@ -15451,8 +12717,6 @@ }, "node_modules/p-limit": { "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", "dev": true, "license": "MIT", "dependencies": { @@ -15467,8 +12731,6 @@ }, "node_modules/p-locate": { "version": "5.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", - "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", "dev": true, "license": "MIT", "dependencies": { @@ -15529,21 +12791,15 @@ }, "node_modules/package-json-from-dist": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", - "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", "dev": true, "license": "BlueOak-1.0.0" }, "node_modules/package-manager-detector": { "version": "1.6.0", - "resolved": "https://registry.npmjs.org/package-manager-detector/-/package-manager-detector-1.6.0.tgz", - "integrity": "sha512-61A5ThoTiDG/C8s8UMZwSorAGwMJ0ERVGj2OjoW5pAalsNOg15+iQiPzrLJ4jhZ1HJzmC2PIHT2oEiH3R5fzNA==", "license": "MIT" }, "node_modules/parent-module": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", - "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", "dev": true, "license": "MIT", "dependencies": { @@ -15555,8 +12811,6 @@ }, "node_modules/parent-module/node_modules/callsites": { "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", "dev": true, "license": "MIT", "engines": { @@ -15565,8 +12819,6 @@ }, "node_modules/parse-entities": { "version": "4.0.2", - "resolved": "https://registry.npmjs.org/parse-entities/-/parse-entities-4.0.2.tgz", - "integrity": "sha512-GG2AQYWoLgL877gQIKeRPGO1xF9+eG1ujIb5soS5gPvLQ1y2o8FL90w2QWNdf9I361Mpp7726c+lj3U0qK1uGw==", "license": "MIT", "dependencies": { "@types/unist": "^2.0.0", @@ -15584,14 +12836,10 @@ }, "node_modules/parse-entities/node_modules/@types/unist": { "version": "2.0.11", - "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.11.tgz", - "integrity": "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==", "license": "MIT" }, "node_modules/parse-json": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", - "integrity": "sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw==", "dev": true, "license": "MIT", "dependencies": { @@ -15604,8 +12852,6 @@ }, "node_modules/parse5": { "version": "7.3.0", - "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.3.0.tgz", - "integrity": "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==", "license": "MIT", "dependencies": { "entities": "^6.0.0" @@ -15616,8 +12862,6 @@ }, "node_modules/parse5/node_modules/entities": { "version": "6.0.1", - "resolved": "https://registry.npmjs.org/entities/-/entities-6.0.1.tgz", - "integrity": "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==", "license": "BSD-2-Clause", "engines": { "node": ">=0.12" @@ -15628,8 +12872,6 @@ }, "node_modules/parseley": { "version": "0.12.1", - "resolved": "https://registry.npmjs.org/parseley/-/parseley-0.12.1.tgz", - "integrity": "sha512-e6qHKe3a9HWr0oMRVDTRhKce+bRO8VGQR3NyVwcjwrbhMmFCX9KszEV35+rn4AdilFAq9VPxP/Fe1wC9Qjd2lw==", "license": "MIT", "dependencies": { "leac": "^0.6.0", @@ -15659,14 +12901,10 @@ }, "node_modules/path-data-parser": { "version": "0.1.0", - "resolved": "https://registry.npmjs.org/path-data-parser/-/path-data-parser-0.1.0.tgz", - "integrity": "sha512-NOnmBpt5Y2RWbuv0LMzsayp3lVylAHLPUTut412ZA3l+C4uw4ZVkQbjShYCQ8TCpUMdPapr4YjUqLYD6v68j+w==", "license": "MIT" }, "node_modules/path-exists": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", "dev": true, "license": "MIT", "engines": { @@ -15686,8 +12924,6 @@ }, "node_modules/path-key": { "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", "dev": true, "license": "MIT", "engines": { @@ -15696,15 +12932,11 @@ }, "node_modules/path-parse": { "version": "1.0.7", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", "dev": true, "license": "MIT" }, "node_modules/path-scurry": { "version": "2.0.2", - "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.2.tgz", - "integrity": "sha512-3O/iVVsJAPsOnpwWIeD+d6z/7PmqApyQePUtCndjatj/9I5LylHvt5qluFaBT3I5h3r1ejfR056c+FCv+NnNXg==", "dev": true, "license": "BlueOak-1.0.0", "dependencies": { @@ -15719,9 +12951,7 @@ } }, "node_modules/path-scurry/node_modules/lru-cache": { - "version": "11.2.7", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.7.tgz", - "integrity": "sha512-aY/R+aEsRelme17KGQa/1ZSIpLpNYYrhcrepKTZgE+W3WM16YMCaPwOHLHsmopZHELU0Ojin1lPVxKR0MihncA==", + "version": "11.2.6", "dev": true, "license": "BlueOak-1.0.0", "engines": { @@ -15730,8 +12960,6 @@ }, "node_modules/path-type": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", - "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", "dev": true, "license": "MIT", "engines": { @@ -15740,14 +12968,10 @@ }, "node_modules/pathe": { "version": "2.0.3", - "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", - "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", "license": "MIT" }, "node_modules/pathval": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/pathval/-/pathval-2.0.1.tgz", - "integrity": "sha512-//nshmD55c46FuFw26xV/xFAaB5HF9Xdap7HJBBnrKdAd6/GxDBaNA1870O79+9ueg61cZLSVc+OaFlfmObYVQ==", "dev": true, "license": "MIT", "engines": { @@ -15756,8 +12980,6 @@ }, "node_modules/pdfjs-dist": { "version": "5.4.296", - "resolved": "https://registry.npmjs.org/pdfjs-dist/-/pdfjs-dist-5.4.296.tgz", - "integrity": "sha512-DlOzet0HO7OEnmUmB6wWGJrrdvbyJKftI1bhMitK7O2N8W2gc757yyYBbINy9IDafXAV9wmKr9t7xsTaNKRG5Q==", "license": "Apache-2.0", "engines": { "node": ">=20.16.0 || >=22.3.0" @@ -15768,8 +12990,6 @@ }, "node_modules/peberminta": { "version": "0.9.0", - "resolved": "https://registry.npmjs.org/peberminta/-/peberminta-0.9.0.tgz", - "integrity": "sha512-XIxfHpEuSJbITd1H3EeQwpcZbTLHc+VVr8ANI9t5sit565tsI4/xK3KWTUFE2e6QiangUkh3B0jihzmGnNrRsQ==", "license": "MIT", "funding": { "url": "https://ko-fi.com/killymxi" @@ -15777,15 +12997,11 @@ }, "node_modules/picocolors": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.1.tgz", - "integrity": "sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==", "dev": true, "license": "ISC" }, "node_modules/picomatch": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", - "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", + "version": "4.0.3", "dev": true, "license": "MIT", "engines": { @@ -15795,10 +13011,19 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/pixelmatch": { + "version": "7.1.0", + "dev": true, + "license": "ISC", + "dependencies": { + "pngjs": "^7.0.0" + }, + "bin": { + "pixelmatch": "bin/pixelmatch" + } + }, "node_modules/pkg-types": { "version": "1.3.1", - "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-1.3.1.tgz", - "integrity": "sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==", "license": "MIT", "dependencies": { "confbox": "^0.1.8", @@ -15848,16 +13073,20 @@ "node": ">=4" } }, + "node_modules/pngjs": { + "version": "7.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.19.0" + } + }, "node_modules/points-on-curve": { "version": "0.2.0", - "resolved": "https://registry.npmjs.org/points-on-curve/-/points-on-curve-0.2.0.tgz", - "integrity": "sha512-0mYKnYYe9ZcqMCWhUjItv/oHjvgEsfKvnUTg8sAtnHr3GVy7rGkXCb6d5cSyqrWqL4k81b9CPg3urd+T7aop3A==", "license": "MIT" }, "node_modules/points-on-path": { "version": "0.2.1", - "resolved": "https://registry.npmjs.org/points-on-path/-/points-on-path-0.2.1.tgz", - "integrity": "sha512-25ClnWWuw7JbWZcgqY/gJ4FQWadKxGWk+3kR/7kD0tCaDtPPMj7oHu2ToLaVhfpnHrZzYby2w6tUA0eOIuUg8g==", "license": "MIT", "dependencies": { "path-data-parser": "0.1.0", @@ -15866,17 +13095,13 @@ }, "node_modules/possible-typed-array-names": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz", - "integrity": "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==", "license": "MIT", "engines": { "node": ">= 0.4" } }, "node_modules/postcss": { - "version": "8.5.8", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.8.tgz", - "integrity": "sha512-OW/rX8O/jXnm82Ey1k44pObPtdblfiuWnrd8X7GJ7emImCOstunGbXUpp7HdBrFQX6rJzn3sPT397Wp5aCwCHg==", + "version": "8.5.6", "dev": true, "funding": [ { @@ -15904,8 +13129,6 @@ }, "node_modules/postcss-selector-parser": { "version": "6.0.10", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.10.tgz", - "integrity": "sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w==", "dev": true, "license": "MIT", "dependencies": { @@ -15918,15 +13141,11 @@ }, "node_modules/postcss/node_modules/picocolors": { "version": "1.1.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", - "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", "dev": true, "license": "ISC" }, "node_modules/prelude-ls": { "version": "1.2.1", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", - "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", "dev": true, "license": "MIT", "engines": { @@ -15935,8 +13154,6 @@ }, "node_modules/prettier": { "version": "3.6.2", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.6.2.tgz", - "integrity": "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==", "license": "MIT", "bin": { "prettier": "bin/prettier.cjs" @@ -15950,8 +13167,6 @@ }, "node_modules/pretty-format": { "version": "27.5.1", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.5.1.tgz", - "integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==", "dev": true, "license": "MIT", "peer": true, @@ -15964,18 +13179,26 @@ "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" } }, + "node_modules/pretty-format/node_modules/ansi-styles": { + "version": "5.2.0", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, "node_modules/pretty-format/node_modules/react-is": { "version": "17.0.2", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", - "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", "dev": true, "license": "MIT", "peer": true }, "node_modules/prismjs": { "version": "1.30.0", - "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.30.0.tgz", - "integrity": "sha512-DEvV2ZF2r2/63V+tK8hQvrR2ZGn10srHbXviTlcv7Kpzw8jWiNTqbVgjO3IY8RxrrOUF8VPMQQFysYYYv0YZxw==", "license": "MIT", "engines": { "node": ">=6" @@ -15983,8 +13206,6 @@ }, "node_modules/promise": { "version": "7.3.1", - "resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz", - "integrity": "sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==", "license": "MIT", "dependencies": { "asap": "~2.0.3" @@ -15992,8 +13213,6 @@ }, "node_modules/prompts": { "version": "2.4.2", - "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", - "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", "dev": true, "license": "MIT", "dependencies": { @@ -16006,8 +13225,6 @@ }, "node_modules/prop-types": { "version": "15.8.1", - "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", - "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", "license": "MIT", "dependencies": { "loose-envify": "^1.4.0", @@ -16017,8 +13234,6 @@ }, "node_modules/property-information": { "version": "7.1.0", - "resolved": "https://registry.npmjs.org/property-information/-/property-information-7.1.0.tgz", - "integrity": "sha512-TwEZ+X+yCJmYfL7TPUOcvBZ4QfoT5YenQiJuX//0th53DE6w0xxLEtfK3iyryQFddXuvkIk51EEgrJQ0WJkOmQ==", "license": "MIT", "funding": { "type": "github", @@ -16243,8 +13458,6 @@ }, "node_modules/punycode": { "version": "2.3.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", - "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", "dev": true, "license": "MIT", "engines": { @@ -16291,8 +13504,6 @@ }, "node_modules/react": { "version": "19.2.4", - "resolved": "https://registry.npmjs.org/react/-/react-19.2.4.tgz", - "integrity": "sha512-9nfp2hYpCwOjAN+8TZFGhtWEwgvWHXqESH8qT89AT/lWklpLON22Lc8pEtnpsZz7VmawabSU0gCjnj8aC0euHQ==", "license": "MIT", "engines": { "node": ">=0.10.0" @@ -16300,8 +13511,6 @@ }, "node_modules/react-docgen": { "version": "7.1.1", - "resolved": "https://registry.npmjs.org/react-docgen/-/react-docgen-7.1.1.tgz", - "integrity": "sha512-hlSJDQ2synMPKFZOsKo9Hi8WWZTC7POR8EmWvTSjow+VDgKzkmjQvFm2fk0tmRw+f0vTOIYKlarR0iL4996pdg==", "dev": true, "license": "MIT", "dependencies": { @@ -16322,8 +13531,6 @@ }, "node_modules/react-docgen-typescript": { "version": "2.4.0", - "resolved": "https://registry.npmjs.org/react-docgen-typescript/-/react-docgen-typescript-2.4.0.tgz", - "integrity": "sha512-ZtAp5XTO5HRzQctjPU0ybY0RRCQO19X/8fxn3w7y2VVTUbGHDKULPTL4ky3vB05euSgG5NpALhEhDPvQ56wvXg==", "dev": true, "license": "MIT", "peerDependencies": { @@ -16332,8 +13539,6 @@ }, "node_modules/react-docgen/node_modules/doctrine": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", - "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -16345,8 +13550,6 @@ }, "node_modules/react-dom": { "version": "19.2.4", - "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.4.tgz", - "integrity": "sha512-AXJdLo8kgMbimY95O2aKQqsz2iWi9jMgKJhRBAxECE4IFxfcazB2LmzloIoibJI3C12IlY20+KFaLv+71bUJeQ==", "license": "MIT", "dependencies": { "scheduler": "^0.27.0" @@ -16357,8 +13560,6 @@ }, "node_modules/react-dropzone": { "version": "14.4.1", - "resolved": "https://registry.npmjs.org/react-dropzone/-/react-dropzone-14.4.1.tgz", - "integrity": "sha512-QDuV76v3uKbHiH34SpwifZ+gOLi1+RdsCO1kl5vxMT4wW8R82+sthjvBw4th3NHF/XX6FBsqDYZVNN+pnhaw0g==", "license": "MIT", "dependencies": { "attr-accept": "^2.2.4", @@ -16374,17 +13575,13 @@ }, "node_modules/react-error-boundary": { "version": "6.1.1", - "resolved": "https://registry.npmjs.org/react-error-boundary/-/react-error-boundary-6.1.1.tgz", - "integrity": "sha512-BrYwPOdXi5mqkk5lw+Uvt0ThHx32rCt3BkukS4X23A2AIWDPSGX6iaWTc0y9TU/mHDA/6qOSGel+B2ERkOvD1w==", "license": "MIT", "peerDependencies": { "react": "^18.0.0 || ^19.0.0" } }, "node_modules/react-hook-form": { - "version": "7.72.0", - "resolved": "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.72.0.tgz", - "integrity": "sha512-V4v6jubaf6JAurEaVnT9aUPKFbNtDgohj5CIgVGyPHvT9wRx5OZHVjz31GsxnPNI278XMu+ruFz+wGOscHaLKw==", + "version": "7.71.2", "license": "MIT", "engines": { "node": ">=18.0.0" @@ -16399,8 +13596,6 @@ }, "node_modules/react-intersection-observer": { "version": "9.16.0", - "resolved": "https://registry.npmjs.org/react-intersection-observer/-/react-intersection-observer-9.16.0.tgz", - "integrity": "sha512-w9nJSEp+DrW9KmQmeWHQyfaP6b03v+TdXynaoA964Wxt7mdR3An11z4NNCQgL4gKSK7y1ver2Fq+JKH6CWEzUA==", "license": "MIT", "peerDependencies": { "react": "^17.0.0 || ^18.0.0 || ^19.0.0", @@ -16414,14 +13609,10 @@ }, "node_modules/react-is": { "version": "16.13.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", - "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", "license": "MIT" }, "node_modules/react-markdown": { "version": "10.1.0", - "resolved": "https://registry.npmjs.org/react-markdown/-/react-markdown-10.1.0.tgz", - "integrity": "sha512-qKxVopLT/TyA6BX3Ue5NwabOsAzm0Q7kAPwq6L+wWDwisYs7R8vZ0nRXqq6rkueboxpkjvLGU9fWifiX/ZZFxQ==", "license": "MIT", "dependencies": { "@types/hast": "^3.0.0", @@ -16447,8 +13638,6 @@ }, "node_modules/react-pdf": { "version": "10.4.1", - "resolved": "https://registry.npmjs.org/react-pdf/-/react-pdf-10.4.1.tgz", - "integrity": "sha512-kS/35staVCBqS29verTQJQZXw7RfsRCPO3fdJoW1KXylcv7A9dw6DZ3vJXC2w+bIBgLw5FN4pOFvKSQtkQhPfA==", "license": "MIT", "dependencies": { "clsx": "^2.0.0", @@ -16476,8 +13665,6 @@ }, "node_modules/react-promise-suspense": { "version": "0.3.4", - "resolved": "https://registry.npmjs.org/react-promise-suspense/-/react-promise-suspense-0.3.4.tgz", - "integrity": "sha512-I42jl7L3Ze6kZaq+7zXWSunBa3b1on5yfvUW6Eo/3fFOj6dZ5Bqmcd264nJbTK/gn1HjjILAjSwnZbV4RpSaNQ==", "license": "MIT", "dependencies": { "fast-deep-equal": "^2.0.1" @@ -16485,14 +13672,10 @@ }, "node_modules/react-promise-suspense/node_modules/fast-deep-equal": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz", - "integrity": "sha512-bCK/2Z4zLidyB4ReuIsvALH6w31YfAQDmXMqMx6FyfHqvBxtjC0eRumeSu4Bs3XtXwpyIywtSTrVT99BxY1f9w==", "license": "MIT" }, "node_modules/react-refresh": { "version": "0.18.0", - "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.18.0.tgz", - "integrity": "sha512-QgT5//D3jfjJb6Gsjxv0Slpj23ip+HtOpnNgnb2S5zU3CB26G/IDPGoy4RJB42wzFE46DRsstbW6tKHoKbhAxw==", "dev": true, "license": "MIT", "engines": { @@ -16501,8 +13684,6 @@ }, "node_modules/react-relay": { "version": "19.0.0", - "resolved": "https://registry.npmjs.org/react-relay/-/react-relay-19.0.0.tgz", - "integrity": "sha512-OaUISVLUTjfBL1tGBtmt3eDZC+KFnP4QIHq0HDacm7ntiZPgAt+udYs3zapuc8hN65wlRruaLKrWc6P/ZQRlSg==", "license": "MIT", "dependencies": { "@babel/runtime": "^7.25.0", @@ -16517,8 +13698,6 @@ }, "node_modules/react-remove-scroll": { "version": "2.7.2", - "resolved": "https://registry.npmjs.org/react-remove-scroll/-/react-remove-scroll-2.7.2.tgz", - "integrity": "sha512-Iqb9NjCCTt6Hf+vOdNIZGdTiH1QSqr27H/Ek9sv/a97gfueI/5h1s3yRi1nngzMUaOOToin5dI1dXKdXiF+u0Q==", "license": "MIT", "dependencies": { "react-remove-scroll-bar": "^2.3.7", @@ -16542,8 +13721,6 @@ }, "node_modules/react-remove-scroll-bar": { "version": "2.3.8", - "resolved": "https://registry.npmjs.org/react-remove-scroll-bar/-/react-remove-scroll-bar-2.3.8.tgz", - "integrity": "sha512-9r+yi9+mgU33AKcj6IbT9oRCO78WriSj6t/cF8DWBZJ9aOGPOTEDvdUDz1FwKim7QXWwmHqtdHnRJfhAxEG46Q==", "license": "MIT", "dependencies": { "react-style-singleton": "^2.2.2", @@ -16563,9 +13740,7 @@ } }, "node_modules/react-router": { - "version": "7.13.2", - "resolved": "https://registry.npmjs.org/react-router/-/react-router-7.13.2.tgz", - "integrity": "sha512-tX1Aee+ArlKQP+NIUd7SE6Li+CiGKwQtbS+FfRxPX6Pe4vHOo6nr9d++u5cwg+Z8K/x8tP+7qLmujDtfrAoUJA==", + "version": "7.13.1", "license": "MIT", "dependencies": { "cookie": "^1.0.1", @@ -16586,8 +13761,6 @@ }, "node_modules/react-style-singleton": { "version": "2.2.3", - "resolved": "https://registry.npmjs.org/react-style-singleton/-/react-style-singleton-2.2.3.tgz", - "integrity": "sha512-b6jSvxvVnyptAiLjbkWLE/lOnR4lfTtDAl+eUC7RZy+QQWc6wRzIV2CE6xBuMmDxc2qIihtDCZD5NPOFl7fRBQ==", "license": "MIT", "dependencies": { "get-nonce": "^1.0.0", @@ -16608,8 +13781,6 @@ }, "node_modules/recast": { "version": "0.22.0", - "resolved": "https://registry.npmjs.org/recast/-/recast-0.22.0.tgz", - "integrity": "sha512-5AAx+mujtXijsEavc5lWXBPQqrM4+Dl5qNH96N2aNeuJFUzpiiToKPsxQD/zAIJHspz7zz0maX0PCtCTFVlixQ==", "license": "MIT", "peer": true, "dependencies": { @@ -16625,8 +13796,6 @@ }, "node_modules/recast/node_modules/ast-types": { "version": "0.15.2", - "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.15.2.tgz", - "integrity": "sha512-c27loCv9QkZinsa5ProX751khO9DJl/AcB5c2KNtA6NRvHKS0PgLfcftz72KVq504vB0Gku5s2kUZzDBvQWvHg==", "license": "MIT", "peer": true, "dependencies": { @@ -16638,8 +13807,6 @@ }, "node_modules/redent": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/redent/-/redent-3.0.0.tgz", - "integrity": "sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==", "dev": true, "license": "MIT", "dependencies": { @@ -16652,8 +13819,6 @@ }, "node_modules/redent/node_modules/strip-indent": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz", - "integrity": "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==", "dev": true, "license": "MIT", "dependencies": { @@ -16672,8 +13837,6 @@ }, "node_modules/reflect.getprototypeof": { "version": "1.0.10", - "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz", - "integrity": "sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==", "dev": true, "license": "MIT", "dependencies": { @@ -16695,8 +13858,6 @@ }, "node_modules/regexp.prototype.flags": { "version": "1.5.4", - "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.4.tgz", - "integrity": "sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==", "dev": true, "license": "MIT", "dependencies": { @@ -16716,8 +13877,6 @@ }, "node_modules/rehype-raw": { "version": "7.0.0", - "resolved": "https://registry.npmjs.org/rehype-raw/-/rehype-raw-7.0.0.tgz", - "integrity": "sha512-/aE8hCfKlQeA8LmyeyQvQF3eBiLRGNlfBJEvWH7ivp9sBqs7TNqBL5X3v157rM4IFETqDnIOO+z5M/biZbo9Ww==", "license": "MIT", "dependencies": { "@types/hast": "^3.0.0", @@ -16731,8 +13890,6 @@ }, "node_modules/relay-compiler": { "version": "19.0.0", - "resolved": "https://registry.npmjs.org/relay-compiler/-/relay-compiler-19.0.0.tgz", - "integrity": "sha512-DwyNWPUkzdggT3oBuNTS18bL6KCU3Sg/oSW/q+pqOQNHLo+RUZOWAx44ALy5XoBrsXjQVBr0EiNqJ4A8G1skOw==", "dev": true, "license": "MIT", "bin": { @@ -16741,8 +13898,6 @@ }, "node_modules/relay-runtime": { "version": "19.0.0", - "resolved": "https://registry.npmjs.org/relay-runtime/-/relay-runtime-19.0.0.tgz", - "integrity": "sha512-uQqizN6d/7ZkTm2C0sTUAFfHk65BQEFLkfp0IBsXPTsXLL3fAJ2iHkemA621vuphtiKsF06C5ZKxypmC5lduFg==", "license": "MIT", "dependencies": { "@babel/runtime": "^7.25.0", @@ -16752,8 +13907,6 @@ }, "node_modules/remark-gfm": { "version": "4.0.1", - "resolved": "https://registry.npmjs.org/remark-gfm/-/remark-gfm-4.0.1.tgz", - "integrity": "sha512-1quofZ2RQ9EWdeN34S79+KExV1764+wCUGop5CPL1WGdD0ocPpu91lzPGbwWMECpEpd42kJGQwzRfyov9j4yNg==", "license": "MIT", "dependencies": { "@types/mdast": "^4.0.0", @@ -16770,8 +13923,6 @@ }, "node_modules/remark-parse": { "version": "11.0.0", - "resolved": "https://registry.npmjs.org/remark-parse/-/remark-parse-11.0.0.tgz", - "integrity": "sha512-FCxlKLNGknS5ba/1lmpYijMUzX2esxW5xQqjWxw2eHFfS2MSdaHVINFmhjo+qN1WhZhNimq0dZATN9pH0IDrpA==", "license": "MIT", "dependencies": { "@types/mdast": "^4.0.0", @@ -16786,8 +13937,6 @@ }, "node_modules/remark-rehype": { "version": "11.1.2", - "resolved": "https://registry.npmjs.org/remark-rehype/-/remark-rehype-11.1.2.tgz", - "integrity": "sha512-Dh7l57ianaEoIpzbp0PC9UKAdCSVklD8E5Rpw7ETfbTl3FqcOOgq5q2LVDhgGCkaBv7p24JXikPdvhhmHvKMsw==", "license": "MIT", "dependencies": { "@types/hast": "^3.0.0", @@ -16803,8 +13952,6 @@ }, "node_modules/remark-stringify": { "version": "11.0.0", - "resolved": "https://registry.npmjs.org/remark-stringify/-/remark-stringify-11.0.0.tgz", - "integrity": "sha512-1OSmLd3awB/t8qdoEOMazZkNsfVTeY4fTsgzcQFdXNq8ToTN4ZGwrMnlda4K6smTFKD+GRV6O48i6Z4iKgPPpw==", "license": "MIT", "dependencies": { "@types/mdast": "^4.0.0", @@ -16818,8 +13965,6 @@ }, "node_modules/require-directory": { "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", "license": "MIT", "peer": true, "engines": { @@ -16836,8 +13981,6 @@ }, "node_modules/resolve": { "version": "1.22.11", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.11.tgz", - "integrity": "sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ==", "dev": true, "license": "MIT", "dependencies": { @@ -16857,8 +14000,6 @@ }, "node_modules/resolve-from": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz", - "integrity": "sha512-GnlH6vxLymXJNMBo7XP1fJIzBFbdYt49CuTwmB/6N53t+kMPRMFKz783LlQ4tv28XoQfMWinAJX6WCGf2IlaIw==", "dev": true, "license": "MIT", "engines": { @@ -16867,8 +14008,6 @@ }, "node_modules/resolve-pkg-maps": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", - "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", "dev": true, "license": "MIT", "funding": { @@ -16902,8 +14041,6 @@ }, "node_modules/rimraf": { "version": "6.0.1", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-6.0.1.tgz", - "integrity": "sha512-9dkvaxAsk/xNXSJzMgFqqMCuFgt2+KsOFek3TMLfo8NCPfWpBmqwyNn5Y+NX56QUYfCtsyhF3ayiboEoUmJk/A==", "dev": true, "license": "ISC", "dependencies": { @@ -16922,9 +14059,6 @@ }, "node_modules/rimraf/node_modules/glob": { "version": "11.1.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-11.1.0.tgz", - "integrity": "sha512-vuNwKSaKiqm7g0THUBu2x7ckSs3XJLXE+2ssL7/MfTGPLLcrJQ/4Uq1CjPTtO5cCIiRxqvN6Twy1qOwhL0Xjcw==", - "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", "dev": true, "license": "BlueOak-1.0.0", "dependencies": { @@ -16946,15 +14080,11 @@ } }, "node_modules/robust-predicates": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/robust-predicates/-/robust-predicates-3.0.3.tgz", - "integrity": "sha512-NS3levdsRIUOmiJ8FZWCP7LG3QpJyrs/TE0Zpf1yvZu8cAJJ6QMW92H1c7kWpdIHo8RvmLxN/o2JXTKHp74lUA==", + "version": "3.0.2", "license": "Unlicense" }, "node_modules/rollup": { - "version": "4.60.1", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.60.1.tgz", - "integrity": "sha512-VmtB2rFU/GroZ4oL8+ZqXgSA38O6GR8KSIvWmEFv63pQ0G6KaBH9s07PO8XTXP4vI+3UJUEypOfjkGfmSBBR0w==", + "version": "4.59.0", "dev": true, "license": "MIT", "dependencies": { @@ -16968,31 +14098,31 @@ "npm": ">=8.0.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.60.1", - "@rollup/rollup-android-arm64": "4.60.1", - "@rollup/rollup-darwin-arm64": "4.60.1", - "@rollup/rollup-darwin-x64": "4.60.1", - "@rollup/rollup-freebsd-arm64": "4.60.1", - "@rollup/rollup-freebsd-x64": "4.60.1", - "@rollup/rollup-linux-arm-gnueabihf": "4.60.1", - "@rollup/rollup-linux-arm-musleabihf": "4.60.1", - "@rollup/rollup-linux-arm64-gnu": "4.60.1", - "@rollup/rollup-linux-arm64-musl": "4.60.1", - "@rollup/rollup-linux-loong64-gnu": "4.60.1", - "@rollup/rollup-linux-loong64-musl": "4.60.1", - "@rollup/rollup-linux-ppc64-gnu": "4.60.1", - "@rollup/rollup-linux-ppc64-musl": "4.60.1", - "@rollup/rollup-linux-riscv64-gnu": "4.60.1", - "@rollup/rollup-linux-riscv64-musl": "4.60.1", - "@rollup/rollup-linux-s390x-gnu": "4.60.1", - "@rollup/rollup-linux-x64-gnu": "4.60.1", - "@rollup/rollup-linux-x64-musl": "4.60.1", - "@rollup/rollup-openbsd-x64": "4.60.1", - "@rollup/rollup-openharmony-arm64": "4.60.1", - "@rollup/rollup-win32-arm64-msvc": "4.60.1", - "@rollup/rollup-win32-ia32-msvc": "4.60.1", - "@rollup/rollup-win32-x64-gnu": "4.60.1", - "@rollup/rollup-win32-x64-msvc": "4.60.1", + "@rollup/rollup-android-arm-eabi": "4.59.0", + "@rollup/rollup-android-arm64": "4.59.0", + "@rollup/rollup-darwin-arm64": "4.59.0", + "@rollup/rollup-darwin-x64": "4.59.0", + "@rollup/rollup-freebsd-arm64": "4.59.0", + "@rollup/rollup-freebsd-x64": "4.59.0", + "@rollup/rollup-linux-arm-gnueabihf": "4.59.0", + "@rollup/rollup-linux-arm-musleabihf": "4.59.0", + "@rollup/rollup-linux-arm64-gnu": "4.59.0", + "@rollup/rollup-linux-arm64-musl": "4.59.0", + "@rollup/rollup-linux-loong64-gnu": "4.59.0", + "@rollup/rollup-linux-loong64-musl": "4.59.0", + "@rollup/rollup-linux-ppc64-gnu": "4.59.0", + "@rollup/rollup-linux-ppc64-musl": "4.59.0", + "@rollup/rollup-linux-riscv64-gnu": "4.59.0", + "@rollup/rollup-linux-riscv64-musl": "4.59.0", + "@rollup/rollup-linux-s390x-gnu": "4.59.0", + "@rollup/rollup-linux-x64-gnu": "4.59.0", + "@rollup/rollup-linux-x64-musl": "4.59.0", + "@rollup/rollup-openbsd-x64": "4.59.0", + "@rollup/rollup-openharmony-arm64": "4.59.0", + "@rollup/rollup-win32-arm64-msvc": "4.59.0", + "@rollup/rollup-win32-ia32-msvc": "4.59.0", + "@rollup/rollup-win32-x64-gnu": "4.59.0", + "@rollup/rollup-win32-x64-msvc": "4.59.0", "fsevents": "~2.3.2" } }, @@ -17004,8 +14134,6 @@ }, "node_modules/roughjs": { "version": "4.6.6", - "resolved": "https://registry.npmjs.org/roughjs/-/roughjs-4.6.6.tgz", - "integrity": "sha512-ZUz/69+SYpFN/g/lUlo2FXcIjRkSu3nDarreVdGGndHEBJ6cXPdKguS8JGxwj5HA5xIbVKSmLgr5b3AWxtRfvQ==", "license": "MIT", "dependencies": { "hachure-fill": "^0.5.2", @@ -17016,8 +14144,6 @@ }, "node_modules/run-applescript": { "version": "7.1.0", - "resolved": "https://registry.npmjs.org/run-applescript/-/run-applescript-7.1.0.tgz", - "integrity": "sha512-DPe5pVFaAsinSaV6QjQ6gdiedWDcRCbUuiQfQa2wmWV7+xC9bGulGI8+TdRmoFkAPaBXk8CrAbnlY2ISniJ47Q==", "dev": true, "license": "MIT", "engines": { @@ -17053,14 +14179,10 @@ }, "node_modules/rw": { "version": "1.3.3", - "resolved": "https://registry.npmjs.org/rw/-/rw-1.3.3.tgz", - "integrity": "sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ==", "license": "BSD-3-Clause" }, "node_modules/safe-array-concat": { "version": "1.1.3", - "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.3.tgz", - "integrity": "sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q==", "dev": true, "license": "MIT", "dependencies": { @@ -17101,8 +14223,6 @@ }, "node_modules/safe-push-apply": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/safe-push-apply/-/safe-push-apply-1.0.0.tgz", - "integrity": "sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA==", "dev": true, "license": "MIT", "dependencies": { @@ -17118,8 +14238,6 @@ }, "node_modules/safe-regex-test": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.1.0.tgz", - "integrity": "sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==", "license": "MIT", "dependencies": { "call-bound": "^1.0.2", @@ -17135,14 +14253,10 @@ }, "node_modules/safer-buffer": { "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", "license": "MIT" }, "node_modules/sax": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/sax/-/sax-1.6.0.tgz", - "integrity": "sha512-6R3J5M4AcbtLUdZmRv2SygeVaM7IhrLXu9BmnOGmmACak8fiUtOsYNWUS4uK7upbmHIBbLBeFeI//477BKLBzA==", + "version": "1.5.0", "license": "BlueOak-1.0.0", "peer": true, "engines": { @@ -17151,14 +14265,10 @@ }, "node_modules/scheduler": { "version": "0.27.0", - "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz", - "integrity": "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==", "license": "MIT" }, "node_modules/selderee": { "version": "0.11.0", - "resolved": "https://registry.npmjs.org/selderee/-/selderee-0.11.0.tgz", - "integrity": "sha512-5TF+l7p4+OsnP8BCCvSyZiSPc4x4//p5uPwK8TCnVPJYRmU2aYKMpOXvw8zM5a5JvuuCGN1jmsMwuU2W02ukfA==", "license": "MIT", "dependencies": { "parseley": "^0.12.0" @@ -17169,8 +14279,6 @@ }, "node_modules/semver": { "version": "7.7.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", - "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", "dev": true, "license": "ISC", "bin": { @@ -17194,14 +14302,10 @@ }, "node_modules/set-cookie-parser": { "version": "2.7.2", - "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.7.2.tgz", - "integrity": "sha512-oeM1lpU/UvhTxw+g3cIfxXHyJRc/uidd3yK1P242gzHds0udQBYzs3y8j4gCCW+ZJ7ad0yctld8RYO+bdurlvw==", "license": "MIT" }, "node_modules/set-function-length": { "version": "1.2.2", - "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", - "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", "license": "MIT", "dependencies": { "define-data-property": "^1.1.4", @@ -17217,8 +14321,6 @@ }, "node_modules/set-function-name": { "version": "2.0.2", - "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.2.tgz", - "integrity": "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==", "dev": true, "license": "MIT", "dependencies": { @@ -17233,8 +14335,6 @@ }, "node_modules/set-proto": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/set-proto/-/set-proto-1.0.0.tgz", - "integrity": "sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw==", "dev": true, "license": "MIT", "dependencies": { @@ -17248,14 +14348,10 @@ }, "node_modules/setimmediate": { "version": "1.0.5", - "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", - "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==", "license": "MIT" }, "node_modules/shebang-command": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", "dev": true, "license": "MIT", "dependencies": { @@ -17267,8 +14363,6 @@ }, "node_modules/shebang-regex": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", "dev": true, "license": "MIT", "engines": { @@ -17277,8 +14371,6 @@ }, "node_modules/side-channel": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", - "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", "dev": true, "license": "MIT", "dependencies": { @@ -17297,8 +14389,6 @@ }, "node_modules/side-channel-list": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", - "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", "dev": true, "license": "MIT", "dependencies": { @@ -17314,8 +14404,6 @@ }, "node_modules/side-channel-map": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", - "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", "dev": true, "license": "MIT", "dependencies": { @@ -17333,8 +14421,6 @@ }, "node_modules/side-channel-weakmap": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", - "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", "dev": true, "license": "MIT", "dependencies": { @@ -17353,15 +14439,11 @@ }, "node_modules/siginfo": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz", - "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==", "dev": true, "license": "ISC" }, "node_modules/signal-exit": { "version": "4.1.0", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", - "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", "dev": true, "license": "ISC", "engines": { @@ -17378,10 +14460,21 @@ "dev": true, "license": "MIT" }, + "node_modules/sirv": { + "version": "3.0.2", + "dev": true, + "license": "MIT", + "dependencies": { + "@polka/url": "^1.0.0-next.24", + "mrmime": "^2.0.0", + "totalist": "^3.0.0" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/sisteransi": { "version": "1.0.5", - "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", - "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", "dev": true, "license": "MIT" }, @@ -17397,8 +14490,6 @@ }, "node_modules/source-map": { "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", "license": "BSD-3-Clause", "engines": { "node": ">=0.10.0" @@ -17406,18 +14497,23 @@ }, "node_modules/source-map-js": { "version": "1.2.1", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", - "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", "dev": true, "license": "BSD-3-Clause", "engines": { "node": ">=0.10.0" } }, + "node_modules/source-map-support": { + "version": "0.5.21", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, "node_modules/space-separated-tokens": { "version": "2.0.2", - "resolved": "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-2.0.2.tgz", - "integrity": "sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==", "license": "MIT", "funding": { "type": "github", @@ -17426,15 +14522,11 @@ }, "node_modules/sprintf-js": { "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", "dev": true, "license": "BSD-3-Clause" }, "node_modules/stable-hash-x": { "version": "0.2.0", - "resolved": "https://registry.npmjs.org/stable-hash-x/-/stable-hash-x-0.2.0.tgz", - "integrity": "sha512-o3yWv49B/o4QZk5ZcsALc6t0+eCelPc44zZsLtCQnZPDwFpDYSWcDnrv2TtMmMbQ7uKo3J0HTURCqckw23czNQ==", "dev": true, "license": "MIT", "engines": { @@ -17443,22 +14535,16 @@ }, "node_modules/stackback": { "version": "0.0.2", - "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz", - "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==", "dev": true, "license": "MIT" }, "node_modules/std-env": { "version": "3.10.0", - "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.10.0.tgz", - "integrity": "sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==", "dev": true, "license": "MIT" }, "node_modules/stop-iteration-iterator": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.1.0.tgz", - "integrity": "sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==", "dev": true, "license": "MIT", "dependencies": { @@ -17470,19 +14556,16 @@ } }, "node_modules/storybook": { - "version": "10.3.4", - "resolved": "https://registry.npmjs.org/storybook/-/storybook-10.3.4.tgz", - "integrity": "sha512-866YXZy9k59tLPl9SN3KZZOFeBC/swxkuBVtW8iQjJIzfCrvk7zXQd8RSQ4ignmCdArVvY4lGMCAT4yNaZSt1g==", + "version": "10.2.14", "dev": true, "license": "MIT", "dependencies": { "@storybook/global": "^5.0.0", "@storybook/icons": "^2.0.1", - "@testing-library/jest-dom": "^6.9.1", + "@testing-library/jest-dom": "^6.6.3", "@testing-library/user-event": "^14.6.1", "@vitest/expect": "3.2.4", "@vitest/spy": "3.2.4", - "@webcontainer/env": "^1.1.1", "esbuild": "^0.18.0 || ^0.19.0 || ^0.20.0 || ^0.21.0 || ^0.22.0 || ^0.23.0 || ^0.24.0 || ^0.25.0 || ^0.26.0 || ^0.27.0", "open": "^10.2.0", "recast": "^0.23.5", @@ -17508,8 +14591,6 @@ }, "node_modules/storybook/node_modules/recast": { "version": "0.23.11", - "resolved": "https://registry.npmjs.org/recast/-/recast-0.23.11.tgz", - "integrity": "sha512-YTUo+Flmw4ZXiWfQKGcwwc11KnoRAYgzAE2E7mXKCjSviTKShtxBsN6YUUBB2gtaBzKzeKunxhUwNHQuRryhWA==", "dev": true, "license": "MIT", "dependencies": { @@ -17525,8 +14606,6 @@ }, "node_modules/string-width": { "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", "license": "MIT", "dependencies": { "emoji-regex": "^8.0.0", @@ -17539,8 +14618,6 @@ }, "node_modules/string-width/node_modules/strip-ansi": { "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "license": "MIT", "dependencies": { "ansi-regex": "^5.0.1" @@ -17551,8 +14628,6 @@ }, "node_modules/string.prototype.matchall": { "version": "4.0.12", - "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.12.tgz", - "integrity": "sha512-6CC9uyBL+/48dYizRf7H7VAYCMCNTBeM78x/VTUe9bFEaxBepPJDa1Ow99LqI/1yF7kuy7Q3cQsYMrcjGUcskA==", "dev": true, "license": "MIT", "dependencies": { @@ -17579,8 +14654,6 @@ }, "node_modules/string.prototype.repeat": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/string.prototype.repeat/-/string.prototype.repeat-1.0.0.tgz", - "integrity": "sha512-0u/TldDbKD8bFCQ/4f5+mNRrXwZ8hg2w7ZR8wa16e8z9XpePWl3eGEcUD0OXpEH/VJH/2G3gjUtR3ZOiBe2S/w==", "dev": true, "license": "MIT", "dependencies": { @@ -17590,8 +14663,6 @@ }, "node_modules/string.prototype.trim": { "version": "1.2.10", - "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.10.tgz", - "integrity": "sha512-Rs66F0P/1kedk5lyYyH9uBzuiI/kNRmwJAR9quK6VOtIpZ2G+hMZd+HQbbv25MgCA6gEffoMZYxlTod4WcdrKA==", "dev": true, "license": "MIT", "dependencies": { @@ -17612,8 +14683,6 @@ }, "node_modules/string.prototype.trimend": { "version": "1.0.9", - "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.9.tgz", - "integrity": "sha512-G7Ok5C6E/j4SGfyLCloXTrngQIQU3PWtXGst3yM7Bea9FRURf1S42ZHlZZtsNque2FN2PoUhfZXYLNWwEr4dLQ==", "dev": true, "license": "MIT", "dependencies": { @@ -17631,8 +14700,6 @@ }, "node_modules/string.prototype.trimstart": { "version": "1.0.8", - "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz", - "integrity": "sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==", "dev": true, "license": "MIT", "dependencies": { @@ -17649,8 +14716,6 @@ }, "node_modules/stringify-entities": { "version": "4.0.4", - "resolved": "https://registry.npmjs.org/stringify-entities/-/stringify-entities-4.0.4.tgz", - "integrity": "sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==", "license": "MIT", "dependencies": { "character-entities-html4": "^2.0.0", @@ -17663,8 +14728,6 @@ }, "node_modules/strip-ansi": { "version": "7.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz", - "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==", "dev": true, "license": "MIT", "dependencies": { @@ -17679,8 +14742,6 @@ }, "node_modules/strip-ansi/node_modules/ansi-regex": { "version": "6.2.2", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", - "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", "dev": true, "license": "MIT", "engines": { @@ -17692,8 +14753,6 @@ }, "node_modules/strip-bom": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", "dev": true, "license": "MIT", "engines": { @@ -17702,8 +14761,6 @@ }, "node_modules/strip-indent": { "version": "4.1.1", - "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-4.1.1.tgz", - "integrity": "sha512-SlyRoSkdh1dYP0PzclLE7r0M9sgbFKKMFXpFRUMNuKhQSbC6VQIGzq3E0qsfvGJaUFJPGv6Ws1NZ/haTAjfbMA==", "dev": true, "license": "MIT", "engines": { @@ -17715,8 +14772,6 @@ }, "node_modules/strip-json-comments": { "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", "dev": true, "license": "MIT", "engines": { @@ -17728,8 +14783,6 @@ }, "node_modules/strip-literal": { "version": "3.1.0", - "resolved": "https://registry.npmjs.org/strip-literal/-/strip-literal-3.1.0.tgz", - "integrity": "sha512-8r3mkIM/2+PpjHoOtiAW8Rg3jJLHaV7xPwG+YRGrv6FP0wwk/toTpATxWYOW0BKdWwl82VT2tFYi5DlROa0Mxg==", "dev": true, "license": "MIT", "dependencies": { @@ -17741,8 +14794,6 @@ }, "node_modules/strip-literal/node_modules/js-tokens": { "version": "9.0.1", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-9.0.1.tgz", - "integrity": "sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ==", "dev": true, "license": "MIT" }, @@ -17766,8 +14817,6 @@ }, "node_modules/style-to-js": { "version": "1.1.21", - "resolved": "https://registry.npmjs.org/style-to-js/-/style-to-js-1.1.21.tgz", - "integrity": "sha512-RjQetxJrrUJLQPHbLku6U/ocGtzyjbJMP9lCNK7Ag0CNh690nSH8woqWH9u16nMjYBAok+i7JO1NP2pOy8IsPQ==", "license": "MIT", "dependencies": { "style-to-object": "1.0.14" @@ -17775,8 +14824,6 @@ }, "node_modules/style-to-object": { "version": "1.0.14", - "resolved": "https://registry.npmjs.org/style-to-object/-/style-to-object-1.0.14.tgz", - "integrity": "sha512-LIN7rULI0jBscWQYaSswptyderlarFkjQ+t79nzty8tcIAceVomEVlLzH5VP4Cmsv6MtKhs7qaAiwlcp+Mgaxw==", "license": "MIT", "dependencies": { "inline-style-parser": "0.2.7" @@ -17784,14 +14831,10 @@ }, "node_modules/stylis": { "version": "4.3.6", - "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.3.6.tgz", - "integrity": "sha512-yQ3rwFWRfwNUY7H5vpU0wfdkNSnvnJinhF9830Swlaxl03zsOjCfmX0ugac+3LtK0lYSgwL/KXc8oYL3mG4YFQ==", "license": "MIT" }, "node_modules/supports-color": { "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", "dev": true, "license": "MIT", "dependencies": { @@ -17806,8 +14849,6 @@ }, "node_modules/supports-preserve-symlinks-flag": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", - "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", "dev": true, "license": "MIT", "engines": { @@ -17825,8 +14866,6 @@ }, "node_modules/tailwind-merge": { "version": "3.5.0", - "resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-3.5.0.tgz", - "integrity": "sha512-I8K9wewnVDkL1NTGoqWmVEIlUcB9gFriAEkXkfCjX5ib8ezGxtR3xD7iZIxrfArjEsH7F1CHD4RFUtxefdqV/A==", "license": "MIT", "funding": { "type": "github", @@ -17835,8 +14874,6 @@ }, "node_modules/tailwind-variants": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/tailwind-variants/-/tailwind-variants-1.0.0.tgz", - "integrity": "sha512-2WSbv4ulEEyuBKomOunut65D8UZwxrHoRfYnxGcQNnHqlSCp2+B7Yz2W+yrNDrxRodOXtGD/1oCcKGNBnUqMqA==", "license": "MIT", "dependencies": { "tailwind-merge": "3.0.2" @@ -17851,8 +14888,6 @@ }, "node_modules/tailwind-variants/node_modules/tailwind-merge": { "version": "3.0.2", - "resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-3.0.2.tgz", - "integrity": "sha512-l7z+OYZ7mu3DTqrL88RiKrKIqO3NcpEO8V/Od04bNpvk0kiIFndGEoqfuzvj4yuhRkHKjRkII2z+KS2HfPcSxw==", "license": "MIT", "funding": { "type": "github", @@ -17860,15 +14895,11 @@ } }, "node_modules/tailwindcss": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.2.2.tgz", - "integrity": "sha512-KWBIxs1Xb6NoLdMVqhbhgwZf2PGBpPEiwOqgI4pFIYbNTfBXiKYyWoTsXgBQ9WFg/OlhnvHaY+AEpW7wSmFo2Q==", + "version": "4.2.1", "license": "MIT" }, "node_modules/tapable": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.2.tgz", - "integrity": "sha512-1MOpMXuhGzGL5TTCZFItxCc0AARf1EZFQkGqMm7ERKj8+Hgr5oLvJOVFcC+lRmR8hCe2S3jC4T5D7Vg/d7/fhA==", + "version": "2.3.0", "dev": true, "license": "MIT", "engines": { @@ -17879,6 +14910,28 @@ "url": "https://opencollective.com/webpack" } }, + "node_modules/terser": { + "version": "5.46.1", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "@jridgewell/source-map": "^0.3.3", + "acorn": "^8.15.0", + "commander": "^2.20.0", + "source-map-support": "~0.5.20" + }, + "bin": { + "terser": "bin/terser" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/terser/node_modules/commander": { + "version": "2.20.3", + "dev": true, + "license": "MIT" + }, "node_modules/text-table": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", @@ -17889,30 +14942,20 @@ }, "node_modules/tiny-invariant": { "version": "1.3.3", - "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.3.tgz", - "integrity": "sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==", "license": "MIT" }, "node_modules/tinybench": { "version": "2.9.0", - "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz", - "integrity": "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==", "dev": true, "license": "MIT" }, "node_modules/tinyexec": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.0.4.tgz", - "integrity": "sha512-u9r3uZC0bdpGOXtlxUIdwf9pkmvhqJdrVCH9fapQtgy/OeTTMZ1nqH7agtvEfmGui6e1XxjcdrlxvxJvc3sMqw==", - "license": "MIT", - "engines": { - "node": ">=18" - } + "version": "0.3.2", + "dev": true, + "license": "MIT" }, "node_modules/tinyglobby": { "version": "0.2.15", - "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", - "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", "dev": true, "license": "MIT", "dependencies": { @@ -17928,8 +14971,6 @@ }, "node_modules/tinypool": { "version": "1.1.1", - "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-1.1.1.tgz", - "integrity": "sha512-Zba82s87IFq9A9XmjiX5uZA/ARWDrB03OHlq+Vw1fSdt0I+4/Kutwy8BP4Y/y/aORMo61FQ0vIb5j44vSo5Pkg==", "dev": true, "license": "MIT", "engines": { @@ -17938,8 +14979,6 @@ }, "node_modules/tinyrainbow": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-2.0.0.tgz", - "integrity": "sha512-op4nsTR47R6p0vMUUoYl/a+ljLFVtlfaXkLQmqfLR1qHma1h/ysYk4hEXZ880bf2CYgTskvTa/e196Vd5dDQXw==", "dev": true, "license": "MIT", "engines": { @@ -17948,8 +14987,6 @@ }, "node_modules/tinyspy": { "version": "4.0.4", - "resolved": "https://registry.npmjs.org/tinyspy/-/tinyspy-4.0.4.tgz", - "integrity": "sha512-azl+t0z7pw/z958Gy9svOTuzqIk6xq+NSheJzn5MMWtWTFywIacg2wUlzKFGtt3cthx0r2SxMK0yzJOR0IES7Q==", "dev": true, "license": "MIT", "engines": { @@ -17958,8 +14995,6 @@ }, "node_modules/title-case": { "version": "3.0.3", - "resolved": "https://registry.npmjs.org/title-case/-/title-case-3.0.3.tgz", - "integrity": "sha512-e1zGYRvbffpcHIrnuqT0Dh+gEJtDaxDSoG4JAIpq4oDFyooziLBIiYQv0GBT4FUAnUop5uZ1hiIAj7oAF6sOCA==", "license": "MIT", "dependencies": { "tslib": "^2.0.3" @@ -18018,6 +15053,14 @@ "url": "https://github.com/sponsors/Borewit" } }, + "node_modules/totalist": { + "version": "3.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/tough-cookie": { "version": "4.1.4", "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.4.tgz", @@ -18048,14 +15091,10 @@ }, "node_modules/tr46": { "version": "0.0.3", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", - "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", "license": "MIT" }, "node_modules/transliteration": { "version": "2.3.5", - "resolved": "https://registry.npmjs.org/transliteration/-/transliteration-2.3.5.tgz", - "integrity": "sha512-HAGI4Lq4Q9dZ3Utu2phaWgtm3vB6PkLUFqWAScg/UW+1eZ/Tg6Exo4oC0/3VUol/w4BlefLhUUSVBr/9/ZGQOw==", "license": "MIT", "peer": true, "dependencies": { @@ -18071,8 +15110,6 @@ }, "node_modules/trim-lines": { "version": "3.0.1", - "resolved": "https://registry.npmjs.org/trim-lines/-/trim-lines-3.0.1.tgz", - "integrity": "sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==", "license": "MIT", "funding": { "type": "github", @@ -18081,8 +15118,6 @@ }, "node_modules/trough": { "version": "2.2.0", - "resolved": "https://registry.npmjs.org/trough/-/trough-2.2.0.tgz", - "integrity": "sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw==", "license": "MIT", "funding": { "type": "github", @@ -18090,9 +15125,7 @@ } }, "node_modules/ts-api-utils": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.5.0.tgz", - "integrity": "sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA==", + "version": "2.4.0", "dev": true, "license": "MIT", "engines": { @@ -18104,8 +15137,6 @@ }, "node_modules/ts-dedent": { "version": "2.2.0", - "resolved": "https://registry.npmjs.org/ts-dedent/-/ts-dedent-2.2.0.tgz", - "integrity": "sha512-q5W7tVM71e2xjHZTlgfTDoPF/SmqKG5hddq9SzR49CH2hayqRKJtQ4mtRlSxKaJlR/+9rEM+mnBHf7I2/BQcpQ==", "license": "MIT", "engines": { "node": ">=6.10" @@ -18169,8 +15200,6 @@ }, "node_modules/tsconfig-paths": { "version": "4.2.0", - "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-4.2.0.tgz", - "integrity": "sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg==", "dev": true, "license": "MIT", "dependencies": { @@ -18184,14 +15213,10 @@ }, "node_modules/tslib": { "version": "2.8.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", - "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", "license": "0BSD" }, "node_modules/tsx": { "version": "4.21.0", - "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.21.0.tgz", - "integrity": "sha512-5C1sg4USs1lfG0GFb2RLXsdpXqBSEhAaA/0kPL01wxzpMqLILNxIxIOKiILz+cdg/pLnOUxFYOR5yhHU666wbw==", "dev": true, "license": "MIT", "dependencies": { @@ -18210,41 +15235,116 @@ }, "node_modules/tsx/node_modules/fsevents": { "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", "dev": true, - "hasInstallScript": true, "license": "MIT", "optional": true, "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, - "node_modules/turbo": { - "version": "2.9.3", - "resolved": "https://registry.npmjs.org/turbo/-/turbo-2.9.3.tgz", - "integrity": "sha512-J/VUvsGRykPb9R8Kh8dHVBOqioDexLk9BhLCU/ZybRR+HN9UR3cURdazFvNgMDt9zPP8TF6K73Z+tplfmi0PqQ==", - "dev": true, - "license": "MIT", - "bin": { - "turbo": "bin/turbo" - }, - "optionalDependencies": { - "@turbo/darwin-64": "2.9.3", - "@turbo/darwin-arm64": "2.9.3", - "@turbo/linux-64": "2.9.3", - "@turbo/linux-arm64": "2.9.3", - "@turbo/windows-64": "2.9.3", - "@turbo/windows-arm64": "2.9.3" - } + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/turbo": { + "version": "2.8.12", + "dev": true, + "license": "MIT", + "bin": { + "turbo": "bin/turbo" + }, + "optionalDependencies": { + "turbo-darwin-64": "2.8.12", + "turbo-darwin-arm64": "2.8.12", + "turbo-linux-64": "2.8.12", + "turbo-linux-arm64": "2.8.12", + "turbo-windows-64": "2.8.12", + "turbo-windows-arm64": "2.8.12" + } + }, + "node_modules/turbo-darwin-64": { + "version": "2.8.12", + "resolved": "https://registry.npmjs.org/turbo-darwin-64/-/turbo-darwin-64-2.8.12.tgz", + "integrity": "sha512-EiHJmW2MeQQx+21x8hjMHw/uPhXt9PIxvDrxzOtyVwrXzL0tQmsxtO4qHf2l7uA+K6PUJ4+TjY1MHZDuCvWXrw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/turbo-darwin-arm64": { + "version": "2.8.12", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/turbo-linux-64": { + "version": "2.8.12", + "resolved": "https://registry.npmjs.org/turbo-linux-64/-/turbo-linux-64-2.8.12.tgz", + "integrity": "sha512-jXKw9j4r4q6s0goSXuKI3aKbQK2qiNeP25lGGEnq018TM6SWRW1CCpPMxyG91aCKrub7wDm/K45sGNT4ZFBcFQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/turbo-linux-arm64": { + "version": "2.8.12", + "resolved": "https://registry.npmjs.org/turbo-linux-arm64/-/turbo-linux-arm64-2.8.12.tgz", + "integrity": "sha512-BRJCMdyXjyBoL0GYpvj9d2WNfMHwc3tKmJG5ATn2Efvil9LsiOsd/93/NxDqW0jACtHFNVOPnd/CBwXRPiRbwA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/turbo-windows-64": { + "version": "2.8.12", + "resolved": "https://registry.npmjs.org/turbo-windows-64/-/turbo-windows-64-2.8.12.tgz", + "integrity": "sha512-vyFOlpFFzQFkikvSVhVkESEfzIopgs2J7J1rYvtSwSHQ4zmHxkC95Q8Kjkus8gg+8X2mZyP1GS5jirmaypGiPw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/turbo-windows-arm64": { + "version": "2.8.12", + "resolved": "https://registry.npmjs.org/turbo-windows-arm64/-/turbo-windows-arm64-2.8.12.tgz", + "integrity": "sha512-9nRnlw5DF0LkJClkIws1evaIF36dmmMEO84J5Uj4oQ8C0QTHwlH7DNe5Kq2Jdmu8GXESCNDNuUYG8Cx6W/vm3g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] }, "node_modules/tw-animate-css": { "version": "1.4.0", - "resolved": "https://registry.npmjs.org/tw-animate-css/-/tw-animate-css-1.4.0.tgz", - "integrity": "sha512-7bziOlRqH0hJx80h/3mbicLW7o8qLsH5+RaLR2t+OHM3D0JlWGODQKQ4cxbK7WlvmUxpcj6Kgu6EKqjrGFe3QQ==", "license": "MIT", "funding": { "url": "https://github.com/sponsors/Wombosvideo" @@ -18252,8 +15352,6 @@ }, "node_modules/type-check": { "version": "0.4.0", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", - "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", "dev": true, "license": "MIT", "dependencies": { @@ -18265,8 +15363,6 @@ }, "node_modules/type-fest": { "version": "0.21.3", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", - "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", "dev": true, "license": "(MIT OR CC0-1.0)", "engines": { @@ -18278,8 +15374,6 @@ }, "node_modules/typed-array-buffer": { "version": "1.0.3", - "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.3.tgz", - "integrity": "sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==", "dev": true, "license": "MIT", "dependencies": { @@ -18293,8 +15387,6 @@ }, "node_modules/typed-array-byte-length": { "version": "1.0.3", - "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.3.tgz", - "integrity": "sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg==", "dev": true, "license": "MIT", "dependencies": { @@ -18313,8 +15405,6 @@ }, "node_modules/typed-array-byte-offset": { "version": "1.0.4", - "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.4.tgz", - "integrity": "sha512-bTlAFB/FBYMcuX81gbL4OcpH5PmlFHqlCCpAl8AlEzMz5k53oNDvN8p1PNOWLEmI2x4orp3raOFB51tv9X+MFQ==", "dev": true, "license": "MIT", "dependencies": { @@ -18335,8 +15425,6 @@ }, "node_modules/typed-array-length": { "version": "1.0.7", - "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.7.tgz", - "integrity": "sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg==", "dev": true, "license": "MIT", "dependencies": { @@ -18356,8 +15444,6 @@ }, "node_modules/typescript": { "version": "5.8.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz", - "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==", "dev": true, "license": "Apache-2.0", "bin": { @@ -18369,16 +15455,14 @@ } }, "node_modules/typescript-eslint": { - "version": "8.58.0", - "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.58.0.tgz", - "integrity": "sha512-e2TQzKfaI85fO+F3QywtX+tCTsu/D3WW5LVU6nz8hTFKFZ8yBJ6mSYRpXqdR3mFjPWmO0eWsTa5f+UpAOe/FMA==", + "version": "8.56.1", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/eslint-plugin": "8.58.0", - "@typescript-eslint/parser": "8.58.0", - "@typescript-eslint/typescript-estree": "8.58.0", - "@typescript-eslint/utils": "8.58.0" + "@typescript-eslint/eslint-plugin": "8.56.1", + "@typescript-eslint/parser": "8.56.1", + "@typescript-eslint/typescript-estree": "8.56.1", + "@typescript-eslint/utils": "8.56.1" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -18389,13 +15473,11 @@ }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", - "typescript": ">=4.8.4 <6.1.0" + "typescript": ">=4.8.4 <6.0.0" } }, "node_modules/ua-parser-js": { "version": "1.0.41", - "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-1.0.41.tgz", - "integrity": "sha512-LbBDqdIC5s8iROCUjMbW1f5dJQTEFB1+KO9ogbvlb3nm9n4YHa5p4KTvFPWvh2Hs8gZMBuiB1/8+pdfe/tDPug==", "funding": [ { "type": "opencollective", @@ -18426,14 +15508,10 @@ }, "node_modules/ufo": { "version": "1.6.3", - "resolved": "https://registry.npmjs.org/ufo/-/ufo-1.6.3.tgz", - "integrity": "sha512-yDJTmhydvl5lJzBmy/hyOAA0d+aqCBuwl818haVdYCRrWV84o7YyeVm4QlVHStqNrrJSTb6jKuFAVqAFsr+K3Q==", "license": "MIT" }, "node_modules/uglify-js": { "version": "3.19.3", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.19.3.tgz", - "integrity": "sha512-v3Xu+yuwBXisp6QYTcH4UbH+xYJXqnq2m/LtQVWKWzYc1iehYnLixoQDN9FH6/j9/oybfd6W9Ghwkl8+UMKTKQ==", "dev": true, "license": "BSD-2-Clause", "optional": true, @@ -18460,8 +15538,6 @@ }, "node_modules/unbox-primitive": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.1.0.tgz", - "integrity": "sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==", "dev": true, "license": "MIT", "dependencies": { @@ -18489,15 +15565,11 @@ }, "node_modules/undici-types": { "version": "6.21.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", - "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", "dev": true, "license": "MIT" }, "node_modules/unified": { "version": "11.0.5", - "resolved": "https://registry.npmjs.org/unified/-/unified-11.0.5.tgz", - "integrity": "sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA==", "license": "MIT", "dependencies": { "@types/unist": "^3.0.0", @@ -18515,8 +15587,6 @@ }, "node_modules/unist-util-is": { "version": "6.0.1", - "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-6.0.1.tgz", - "integrity": "sha512-LsiILbtBETkDz8I9p1dQ0uyRUWuaQzd/cuEeS1hoRSyW5E5XGmTzlwY1OrNzzakGowI9Dr/I8HVaw4hTtnxy8g==", "license": "MIT", "dependencies": { "@types/unist": "^3.0.0" @@ -18528,8 +15598,6 @@ }, "node_modules/unist-util-position": { "version": "5.0.0", - "resolved": "https://registry.npmjs.org/unist-util-position/-/unist-util-position-5.0.0.tgz", - "integrity": "sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==", "license": "MIT", "dependencies": { "@types/unist": "^3.0.0" @@ -18541,8 +15609,6 @@ }, "node_modules/unist-util-stringify-position": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-4.0.0.tgz", - "integrity": "sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==", "license": "MIT", "dependencies": { "@types/unist": "^3.0.0" @@ -18554,8 +15620,6 @@ }, "node_modules/unist-util-visit": { "version": "5.1.0", - "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-5.1.0.tgz", - "integrity": "sha512-m+vIdyeCOpdr/QeQCu2EzxX/ohgS8KbnPDgFni4dQsfSCtpz8UqDyY5GjRru8PDKuYn7Fq19j1CQ+nJSsGKOzg==", "license": "MIT", "dependencies": { "@types/unist": "^3.0.0", @@ -18569,8 +15633,6 @@ }, "node_modules/unist-util-visit-parents": { "version": "6.0.2", - "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-6.0.2.tgz", - "integrity": "sha512-goh1s1TBrqSqukSc8wrjwWhL0hiJxgA8m4kFxGlQ+8FYQ3C/m11FcTs4YYem7V664AhHVvgoQLk890Ssdsr2IQ==", "license": "MIT", "dependencies": { "@types/unist": "^3.0.0", @@ -18583,8 +15645,6 @@ }, "node_modules/universalify": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", - "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", "dev": true, "license": "MIT", "engines": { @@ -18593,8 +15653,6 @@ }, "node_modules/unplugin": { "version": "2.3.11", - "resolved": "https://registry.npmjs.org/unplugin/-/unplugin-2.3.11.tgz", - "integrity": "sha512-5uKD0nqiYVzlmCRs01Fhs2BdkEgBS3SAVP6ndrBsuK42iC2+JHyxM05Rm9G8+5mkmRtzMZGY8Ct5+mliZxU/Ww==", "dev": true, "license": "MIT", "dependencies": { @@ -18609,8 +15667,6 @@ }, "node_modules/unrs-resolver": { "version": "1.11.1", - "resolved": "https://registry.npmjs.org/unrs-resolver/-/unrs-resolver-1.11.1.tgz", - "integrity": "sha512-bSjt9pjaEBnNiGgc9rUiHGKv5l4/TGzDmYw3RhnkJGtLhbnnA/5qJj7x3dNDCRx/PJxu774LlH8lCOlB4hEfKg==", "dev": true, "hasInstallScript": true, "license": "MIT", @@ -18644,8 +15700,6 @@ }, "node_modules/update-browserslist-db": { "version": "1.2.3", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz", - "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==", "dev": true, "funding": [ { @@ -18675,8 +15729,6 @@ }, "node_modules/update-browserslist-db/node_modules/picocolors": { "version": "1.1.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", - "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", "dev": true, "license": "ISC" }, @@ -18692,8 +15744,6 @@ }, "node_modules/uri-js": { "version": "4.4.1", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", - "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", "dev": true, "license": "BSD-2-Clause", "dependencies": { @@ -18714,8 +15764,6 @@ }, "node_modules/use-callback-ref": { "version": "1.3.3", - "resolved": "https://registry.npmjs.org/use-callback-ref/-/use-callback-ref-1.3.3.tgz", - "integrity": "sha512-jQL3lRnocaFtu3V00JToYz/4QkNWswxijDaCVNZRiRTO3HQDLsdu1ZtmIUvV4yPp+rvWm5j0y0TG/S61cuijTg==", "license": "MIT", "dependencies": { "tslib": "^2.0.0" @@ -18734,9 +15782,7 @@ } }, "node_modules/use-debounce": { - "version": "10.1.1", - "resolved": "https://registry.npmjs.org/use-debounce/-/use-debounce-10.1.1.tgz", - "integrity": "sha512-kvds8BHR2k28cFsxW8k3nc/tRga2rs1RHYCqmmGqb90MEeE++oALwzh2COiuBLO1/QXiOuShXoSN2ZpWnMmvuQ==", + "version": "10.1.0", "license": "MIT", "engines": { "node": ">= 16.0.0" @@ -18747,8 +15793,6 @@ }, "node_modules/use-sidecar": { "version": "1.1.3", - "resolved": "https://registry.npmjs.org/use-sidecar/-/use-sidecar-1.1.3.tgz", - "integrity": "sha512-Fedw0aZvkhynoPYlA5WXrMCAMm+nSWdZt6lzJQ7Ok8S6Q+VsHmHpRWndVRJ8Be0ZbkfPc5LRYH+5XrzXcEeLRQ==", "license": "MIT", "dependencies": { "detect-node-es": "^1.1.0", @@ -18769,8 +15813,6 @@ }, "node_modules/use-sync-external-store": { "version": "1.6.0", - "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.6.0.tgz", - "integrity": "sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w==", "license": "MIT", "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" @@ -18778,8 +15820,6 @@ }, "node_modules/usehooks-ts": { "version": "3.1.1", - "resolved": "https://registry.npmjs.org/usehooks-ts/-/usehooks-ts-3.1.1.tgz", - "integrity": "sha512-I4diPp9Cq6ieSUH2wu+fDAVQO43xwtulo+fKEidHUwZPnYImbtkTjzIJYcDcJqxgmX31GVqNFURodvcgHcW0pA==", "license": "MIT", "dependencies": { "lodash.debounce": "^4.0.8" @@ -18793,8 +15833,6 @@ }, "node_modules/util": { "version": "0.12.5", - "resolved": "https://registry.npmjs.org/util/-/util-0.12.5.tgz", - "integrity": "sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA==", "license": "MIT", "peer": true, "dependencies": { @@ -18807,22 +15845,18 @@ }, "node_modules/util-deprecate": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", "dev": true, "license": "MIT" }, "node_modules/uuid": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-10.0.0.tgz", - "integrity": "sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==", + "version": "11.1.0", "funding": [ "https://github.com/sponsors/broofa", "https://github.com/sponsors/ctavan" ], "license": "MIT", "bin": { - "uuid": "dist/bin/uuid" + "uuid": "dist/esm/bin/uuid" } }, "node_modules/v8-compile-cache-lib": { @@ -18835,8 +15869,6 @@ }, "node_modules/vfile": { "version": "6.0.3", - "resolved": "https://registry.npmjs.org/vfile/-/vfile-6.0.3.tgz", - "integrity": "sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==", "license": "MIT", "dependencies": { "@types/unist": "^3.0.0", @@ -18849,8 +15881,6 @@ }, "node_modules/vfile-location": { "version": "5.0.3", - "resolved": "https://registry.npmjs.org/vfile-location/-/vfile-location-5.0.3.tgz", - "integrity": "sha512-5yXvWDEgqeiYiBe1lbxYF7UMAIm/IcopxMHrMQDq3nvKcjPKIhZklUKL+AE7J7uApI4kwe2snsK+eI6UTj9EHg==", "license": "MIT", "dependencies": { "@types/unist": "^3.0.0", @@ -18863,8 +15893,6 @@ }, "node_modules/vfile-message": { "version": "4.0.3", - "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-4.0.3.tgz", - "integrity": "sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw==", "license": "MIT", "dependencies": { "@types/unist": "^3.0.0", @@ -18876,9 +15904,7 @@ } }, "node_modules/vite": { - "version": "7.3.2", - "resolved": "https://registry.npmjs.org/vite/-/vite-7.3.2.tgz", - "integrity": "sha512-Bby3NOsna2jsjfLVOHKes8sGwgl4TT0E6vvpYgnAYDIF/tie7MRaFthmKuHx1NSXjiTueXH3do80FMQgvEktRg==", + "version": "7.3.1", "dev": true, "license": "MIT", "dependencies": { @@ -18952,8 +15978,6 @@ }, "node_modules/vite-node": { "version": "3.2.4", - "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-3.2.4.tgz", - "integrity": "sha512-EbKSKh+bh1E1IFxeO0pg1n4dvoOTt0UDiXMd/qn++r98+jPO1xtJilvXldeuQ8giIB5IkpjCgMleHMNEsGH6pg==", "dev": true, "license": "MIT", "dependencies": { @@ -18975,10 +15999,7 @@ }, "node_modules/vite/node_modules/fsevents": { "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", "dev": true, - "hasInstallScript": true, "license": "MIT", "optional": true, "os": [ @@ -18990,8 +16011,6 @@ }, "node_modules/vitest": { "version": "3.2.4", - "resolved": "https://registry.npmjs.org/vitest/-/vitest-3.2.4.tgz", - "integrity": "sha512-LUCP5ev3GURDysTWiP47wRRUpLKMOfPh+yKTx3kVIEiu5KOMeqzpnYNsKyOoVrULivR8tLcks4+lga33Whn90A==", "dev": true, "license": "MIT", "dependencies": { @@ -19061,13 +16080,6 @@ } } }, - "node_modules/vitest/node_modules/tinyexec": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-0.3.2.tgz", - "integrity": "sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==", - "dev": true, - "license": "MIT" - }, "node_modules/vscode-jsonrpc": { "version": "8.2.0", "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-8.2.0.tgz", @@ -19125,8 +16137,6 @@ }, "node_modules/warning": { "version": "4.0.3", - "resolved": "https://registry.npmjs.org/warning/-/warning-4.0.3.tgz", - "integrity": "sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==", "license": "MIT", "dependencies": { "loose-envify": "^1.0.0" @@ -19134,8 +16144,6 @@ }, "node_modules/web-namespaces": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/web-namespaces/-/web-namespaces-2.0.1.tgz", - "integrity": "sha512-bKr1DkiNa2krS7qxNtdrtHAmzuYGFQLiQ13TsorsdT6ULTkPLKuu5+GsFpDlg6JFjUTwX2DyhMPG2be8uPrqsQ==", "license": "MIT", "funding": { "type": "github", @@ -19155,21 +16163,15 @@ }, "node_modules/webidl-conversions": { "version": "3.0.1", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", - "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", "license": "BSD-2-Clause" }, "node_modules/webpack-virtual-modules": { "version": "0.6.2", - "resolved": "https://registry.npmjs.org/webpack-virtual-modules/-/webpack-virtual-modules-0.6.2.tgz", - "integrity": "sha512-66/V2i5hQanC51vBQKPH4aI8NMAcBW59FVBs+rC7eGHupMyfn34q7rZIE+ETlJ+XTevqfUhVVBgSUNSW2flEUQ==", "dev": true, "license": "MIT" }, "node_modules/whatwg-url": { "version": "5.0.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", - "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", "license": "MIT", "dependencies": { "tr46": "~0.0.3", @@ -19178,8 +16180,6 @@ }, "node_modules/which": { "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", "dev": true, "license": "ISC", "dependencies": { @@ -19194,8 +16194,6 @@ }, "node_modules/which-boxed-primitive": { "version": "1.1.1", - "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.1.1.tgz", - "integrity": "sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==", "dev": true, "license": "MIT", "dependencies": { @@ -19214,8 +16212,6 @@ }, "node_modules/which-builtin-type": { "version": "1.2.1", - "resolved": "https://registry.npmjs.org/which-builtin-type/-/which-builtin-type-1.2.1.tgz", - "integrity": "sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q==", "dev": true, "license": "MIT", "dependencies": { @@ -19242,8 +16238,6 @@ }, "node_modules/which-collection": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.2.tgz", - "integrity": "sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==", "dev": true, "license": "MIT", "dependencies": { @@ -19261,8 +16255,6 @@ }, "node_modules/which-typed-array": { "version": "1.1.20", - "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.20.tgz", - "integrity": "sha512-LYfpUkmqwl0h9A2HL09Mms427Q1RZWuOHsukfVcKRq9q95iQxdw0ix1JQrqbcDR9PH1QDwf5Qo8OZb5lksZ8Xg==", "license": "MIT", "dependencies": { "available-typed-arrays": "^1.0.7", @@ -19282,8 +16274,6 @@ }, "node_modules/why-is-node-running": { "version": "2.3.0", - "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.3.0.tgz", - "integrity": "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==", "dev": true, "license": "MIT", "dependencies": { @@ -19299,8 +16289,6 @@ }, "node_modules/widest-line": { "version": "3.1.0", - "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-3.1.0.tgz", - "integrity": "sha512-NsmoXalsWVDMGupxZ5R08ka9flZjjiLvHVAWYOKtiKM8ujtZWr9cRffak+uSE48+Ob8ObalXpwyeUiyDD6QFgg==", "dev": true, "license": "MIT", "dependencies": { @@ -19312,8 +16300,6 @@ }, "node_modules/word-wrap": { "version": "1.2.5", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", - "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", "dev": true, "license": "MIT", "engines": { @@ -19322,15 +16308,11 @@ }, "node_modules/wordwrap": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", - "integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==", "dev": true, "license": "MIT" }, "node_modules/wrap-ansi": { "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", "license": "MIT", "dependencies": { "ansi-styles": "^4.0.0", @@ -19344,25 +16326,8 @@ "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, - "node_modules/wrap-ansi/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, "node_modules/wrap-ansi/node_modules/strip-ansi": { "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "license": "MIT", "dependencies": { "ansi-regex": "^5.0.1" @@ -19380,9 +16345,7 @@ "peer": true }, "node_modules/ws": { - "version": "8.20.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.20.0.tgz", - "integrity": "sha512-sAt8BhgNbzCtgGbt2OxmpuryO63ZoDk/sqaB/znQm94T4fCEsy/yV+7CdC1kJhOU9lboAEU7R3kquuycDoibVA==", + "version": "8.19.0", "dev": true, "license": "MIT", "engines": { @@ -19403,8 +16366,6 @@ }, "node_modules/wsl-utils": { "version": "0.1.0", - "resolved": "https://registry.npmjs.org/wsl-utils/-/wsl-utils-0.1.0.tgz", - "integrity": "sha512-h3Fbisa2nKGPxCpm89Hk33lBLsnaGBvctQopaBSOW/uIs6FTe1ATyAnKFJrzVs9vpGdsTe73WF3V4lIsk4Gacw==", "dev": true, "license": "MIT", "dependencies": { @@ -19419,8 +16380,6 @@ }, "node_modules/wsl-utils/node_modules/is-wsl": { "version": "3.1.1", - "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-3.1.1.tgz", - "integrity": "sha512-e6rvdUCiQCAuumZslxRJWR/Doq4VpPR82kqclvcS0efgt430SlGIk05vdCN58+VrzgtIcfNODjozVielycD4Sw==", "dev": true, "license": "MIT", "dependencies": { @@ -19435,8 +16394,6 @@ }, "node_modules/xml2js": { "version": "0.6.2", - "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.6.2.tgz", - "integrity": "sha512-T4rieHaC1EXcES0Kxxj4JWgaUQHDk+qwHcYOCFHfiwKz7tOVPLq7Hjq9dM1WCMhylqMEfP7hMcOIChvotiZegA==", "license": "MIT", "peer": true, "dependencies": { @@ -19449,8 +16406,6 @@ }, "node_modules/xmlbuilder": { "version": "11.0.1", - "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz", - "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==", "license": "MIT", "peer": true, "engines": { @@ -19459,8 +16414,6 @@ }, "node_modules/y18n": { "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", "license": "ISC", "peer": true, "engines": { @@ -19469,31 +16422,19 @@ }, "node_modules/yallist": { "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", "dev": true, "license": "ISC" }, "node_modules/yaml": { - "version": "2.8.3", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.3.tgz", - "integrity": "sha512-AvbaCLOO2Otw/lW5bmh9d/WEdcDFdQp2Z2ZUH3pX9U2ihyUY0nvLv7J6TrWowklRGPYbB/IuIMfYgxaCPg5Bpg==", + "version": "1.10.2", "dev": true, "license": "ISC", - "bin": { - "yaml": "bin.mjs" - }, "engines": { - "node": ">= 14.6" - }, - "funding": { - "url": "https://github.com/sponsors/eemeli" + "node": ">= 6" } }, "node_modules/yargs": { "version": "17.7.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", - "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", "license": "MIT", "peer": true, "dependencies": { @@ -19511,8 +16452,6 @@ }, "node_modules/yargs-parser": { "version": "21.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", - "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", "license": "ISC", "peer": true, "engines": { @@ -19532,8 +16471,6 @@ }, "node_modules/yocto-queue": { "version": "0.1.0", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", - "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", "dev": true, "license": "MIT", "engines": { @@ -19545,8 +16482,6 @@ }, "node_modules/zod": { "version": "3.25.76", - "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", - "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", "license": "MIT", "funding": { "url": "https://github.com/sponsors/colinhacks" @@ -19564,8 +16499,6 @@ }, "node_modules/zod-validation-error": { "version": "4.0.2", - "resolved": "https://registry.npmjs.org/zod-validation-error/-/zod-validation-error-4.0.2.tgz", - "integrity": "sha512-Q6/nZLe6jxuU80qb/4uJ4t5v2VEZ44lzQjPDhYJNztRQ4wyWc6VF3D3Kb/fAuPetZQnhS3hnajCf9CsWesghLQ==", "dev": true, "license": "MIT", "engines": { @@ -19576,9 +16509,7 @@ } }, "node_modules/zustand": { - "version": "5.0.12", - "resolved": "https://registry.npmjs.org/zustand/-/zustand-5.0.12.tgz", - "integrity": "sha512-i77ae3aZq4dhMlRhJVCYgMLKuSiZAaUPAct2AksxQ+gOtimhGMdXljRT21P5BNpeT4kXlLIckvkPM029OljD7g==", + "version": "5.0.11", "license": "MIT", "engines": { "node": ">=12.20.0" @@ -19606,14 +16537,161 @@ }, "node_modules/zwitch": { "version": "2.0.4", - "resolved": "https://registry.npmjs.org/zwitch/-/zwitch-2.0.4.tgz", - "integrity": "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==", "license": "MIT", "funding": { "type": "github", "url": "https://github.com/sponsors/wooorm" } }, + "packages/cookie-banner": { + "name": "@probo/cookie-banner", + "version": "0.0.0", + "devDependencies": { + "terser": "^5.46.1", + "typescript": "~5.8.0", + "vite": "^6.3.0" + } + }, + "packages/cookie-banner/node_modules/@esbuild/darwin-arm64": { + "version": "0.25.12", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "packages/cookie-banner/node_modules/esbuild": { + "version": "0.25.12", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.25.12", + "@esbuild/android-arm": "0.25.12", + "@esbuild/android-arm64": "0.25.12", + "@esbuild/android-x64": "0.25.12", + "@esbuild/darwin-arm64": "0.25.12", + "@esbuild/darwin-x64": "0.25.12", + "@esbuild/freebsd-arm64": "0.25.12", + "@esbuild/freebsd-x64": "0.25.12", + "@esbuild/linux-arm": "0.25.12", + "@esbuild/linux-arm64": "0.25.12", + "@esbuild/linux-ia32": "0.25.12", + "@esbuild/linux-loong64": "0.25.12", + "@esbuild/linux-mips64el": "0.25.12", + "@esbuild/linux-ppc64": "0.25.12", + "@esbuild/linux-riscv64": "0.25.12", + "@esbuild/linux-s390x": "0.25.12", + "@esbuild/linux-x64": "0.25.12", + "@esbuild/netbsd-arm64": "0.25.12", + "@esbuild/netbsd-x64": "0.25.12", + "@esbuild/openbsd-arm64": "0.25.12", + "@esbuild/openbsd-x64": "0.25.12", + "@esbuild/openharmony-arm64": "0.25.12", + "@esbuild/sunos-x64": "0.25.12", + "@esbuild/win32-arm64": "0.25.12", + "@esbuild/win32-ia32": "0.25.12", + "@esbuild/win32-x64": "0.25.12" + } + }, + "packages/cookie-banner/node_modules/fsevents": { + "version": "2.3.3", + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "packages/cookie-banner/node_modules/vite": { + "version": "6.4.1", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "^0.25.0", + "fdir": "^6.4.4", + "picomatch": "^4.0.2", + "postcss": "^8.5.3", + "rollup": "^4.34.9", + "tinyglobby": "^0.2.13" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^18.0.0 || ^20.0.0 || >=22.0.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", + "jiti": ">=1.21.0", + "less": "*", + "lightningcss": "^1.21.0", + "sass": "*", + "sass-embedded": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, "packages/coredata": { "name": "@probo/coredata", "version": "1.0.0", @@ -20026,6 +17104,29 @@ "@types/estree": "^1.0.0" } }, + "packages/ui/node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "packages/ui/node_modules/tinyexec": { + "version": "1.0.2", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, "packages/ui/node_modules/tinyrainbow": { "version": "3.0.3", "dev": true, @@ -20034,6 +17135,81 @@ "node": ">=14.0.0" } }, + "packages/ui/node_modules/vite": { + "version": "7.3.2", + "resolved": "https://registry.npmjs.org/vite/-/vite-7.3.2.tgz", + "integrity": "sha512-Bby3NOsna2jsjfLVOHKes8sGwgl4TT0E6vvpYgnAYDIF/tie7MRaFthmKuHx1NSXjiTueXH3do80FMQgvEktRg==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "^0.27.0", + "fdir": "^6.5.0", + "picomatch": "^4.0.3", + "postcss": "^8.5.6", + "rollup": "^4.43.0", + "tinyglobby": "^0.2.15" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^20.19.0 || >=22.12.0", + "jiti": ">=1.21.0", + "less": "^4.0.0", + "lightningcss": "^1.21.0", + "sass": "^1.70.0", + "sass-embedded": "^1.70.0", + "stylus": ">=0.54.8", + "sugarss": "^5.0.0", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, "packages/ui/node_modules/vitest": { "version": "4.0.18", "dev": true, diff --git a/packages/cookie-banner/cookie_banner.go b/packages/cookie-banner/cookie_banner.go new file mode 100644 index 000000000..842f6e4ed --- /dev/null +++ b/packages/cookie-banner/cookie_banner.go @@ -0,0 +1,41 @@ +// Copyright (c) 2025-2026 Probo Inc . +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR +// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +// PERFORMANCE OF THIS SOFTWARE. + +package cookiebanner + +import ( + "crypto/sha256" + "embed" + "encoding/hex" +) + +//go:embed dist +var StaticFiles embed.FS + +var ( + WidgetBundle []byte + WidgetETag string +) + +func init() { + var err error + + WidgetBundle, err = StaticFiles.ReadFile("dist/cookie-banner.js") + if err != nil { + panic("cannot read embedded widget bundle: " + err.Error()) + } + + hash := sha256.Sum256(WidgetBundle) + WidgetETag = `"` + hex.EncodeToString(hash[:16]) + `"` +} diff --git a/packages/cookie-banner/package.json b/packages/cookie-banner/package.json new file mode 100644 index 000000000..bd8db4908 --- /dev/null +++ b/packages/cookie-banner/package.json @@ -0,0 +1,22 @@ +{ + "name": "@probo/cookie-banner", + "version": "0.0.0", + "type": "module", + "exports": { + "./headless": "./src/headless/index.ts", + "./styled": "./src/styled/index.ts", + "./banner": "./src/styled/banner-renderer.ts", + "./api": "./src/headless/api.ts", + "./i18n": "./src/headless/i18n.ts" + }, + "scripts": { + "dev": "vite build --watch", + "build": "tsc --noEmit && vite build", + "check": "tsc --noEmit" + }, + "devDependencies": { + "terser": "^5.46.1", + "typescript": "~5.8.0", + "vite": "^6.3.0" + } +} diff --git a/packages/cookie-banner/src/headless/api.ts b/packages/cookie-banner/src/headless/api.ts new file mode 100644 index 000000000..907294848 --- /dev/null +++ b/packages/cookie-banner/src/headless/api.ts @@ -0,0 +1,123 @@ +// Copyright (c) 2025-2026 Probo Inc . +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR +// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +// PERFORMANCE OF THIS SOFTWARE. + +import type { BannerConfig, ThemeConfig } from "./types"; +import { enqueueConsent, dequeueAllConsents } from "./storage"; + +export type { BannerCategory, BannerConfig, ThemeConfig } from "./types"; + +export const defaultTheme: ThemeConfig = { + primary_color: "#2563eb", + primary_text_color: "#ffffff", + secondary_color: "#1a1a1a", + secondary_text_color: "#ffffff", + background_color: "#ffffff", + text_color: "#1a1a1a", + secondary_text_body_color: "#4b5563", + border_color: "#e5e7eb", + font_family: + "-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif", + border_radius: 8, + position: "bottom", + revisit_position: "bottom-left", +}; + +export async function fetchConfig( + baseUrl: string, + bannerId: string, +): Promise { + try { + const resp = await fetch(`${baseUrl}/${bannerId}/config`); + if (!resp.ok) return null; + const data = await resp.json(); + return { + ...data, + theme: { ...defaultTheme, ...data.theme }, + } as BannerConfig; + } catch { + return null; + } +} + +async function sendConsent( + baseUrl: string, + bannerId: string, + visitorId: string, + consentData: Record, + action: string, +): Promise { + try { + const resp = await fetch(`${baseUrl}/${bannerId}/consents`, { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ + visitor_id: visitorId, + consent_data: consentData, + action, + }), + }); + return resp.ok; + } catch { + return false; + } +} + +export async function postConsent( + baseUrl: string, + bannerId: string, + visitorId: string, + consentData: Record, + action: string, +): Promise { + const ok = await sendConsent( + baseUrl, + bannerId, + visitorId, + consentData, + action, + ); + + if (!ok) { + enqueueConsent({ + baseUrl, + bannerId, + visitorId, + consentData, + action, + timestamp: Date.now(), + }); + } +} + +export async function flushConsentQueue(): Promise { + const queued = dequeueAllConsents(); + for (let i = 0; i < queued.length; i++) { + const entry = queued[i]; + const ok = await sendConsent( + entry.baseUrl, + entry.bannerId, + entry.visitorId, + entry.consentData, + entry.action, + ); + + if (!ok) { + // Re-queue the failed entry and all remaining entries. + for (let j = i; j < queued.length; j++) { + enqueueConsent(queued[j]); + } + break; + } + } +} diff --git a/packages/cookie-banner/src/headless/apply.ts b/packages/cookie-banner/src/headless/apply.ts new file mode 100644 index 000000000..3b93e55b0 --- /dev/null +++ b/packages/cookie-banner/src/headless/apply.ts @@ -0,0 +1,127 @@ +// Copyright (c) 2025-2026 Probo Inc . +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR +// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +// PERFORMANCE OF THIS SOFTWARE. + +export function applyConsent( + categories: Record, + previousCategories?: Record, +): void { + // Scripts: activate blocked scripts (cannot un-execute) + try { + const blocked = document.querySelectorAll( + 'script[type="text/plain"][data-cookie-category]', + ); + blocked.forEach((el) => { + const script = el as HTMLScriptElement; + const category = script.getAttribute("data-cookie-category"); + if (category && categories[category]) { + const newScript = document.createElement("script"); + for (const attr of Array.from(script.attributes)) { + if (attr.name === "type" || attr.name === "data-cookie-category") + continue; + newScript.setAttribute(attr.name, attr.value); + } + if (script.textContent) { + newScript.textContent = script.textContent; + } + script.parentNode?.replaceChild(newScript, script); + } + }); + } catch { + // Never break the host site. + } + + // Iframes: activate or deactivate + try { + const iframes = document.querySelectorAll( + "iframe[data-cookie-category]", + ); + iframes.forEach((el) => { + const iframe = el as HTMLIFrameElement; + const category = iframe.getAttribute("data-cookie-category"); + if (!category) return; + + if (categories[category]) { + const dataSrc = iframe.getAttribute("data-src"); + if (dataSrc) { + iframe.setAttribute("src", dataSrc); + iframe.removeAttribute("data-src"); + } + } else if (previousCategories && previousCategories[category]) { + const src = iframe.getAttribute("src"); + if (src && src !== "about:blank") { + iframe.setAttribute("data-src", src); + } + iframe.setAttribute("src", "about:blank"); + } + }); + } catch { + // Never break the host site. + } + + // Images: activate or deactivate + try { + const images = document.querySelectorAll( + "img[data-cookie-category]", + ); + images.forEach((el) => { + const img = el as HTMLImageElement; + const category = img.getAttribute("data-cookie-category"); + if (!category) return; + + if (categories[category]) { + const dataSrc = img.getAttribute("data-src"); + if (dataSrc) { + img.setAttribute("src", dataSrc); + img.removeAttribute("data-src"); + } + } else if (previousCategories && previousCategories[category]) { + const src = img.getAttribute("src"); + if (src) { + img.setAttribute("data-src", src); + } + img.removeAttribute("src"); + } + }); + } catch { + // Never break the host site. + } + + // Links (stylesheets): activate or deactivate + try { + const links = document.querySelectorAll( + "link[data-cookie-category]", + ); + links.forEach((el) => { + const link = el as HTMLLinkElement; + const category = link.getAttribute("data-cookie-category"); + if (!category) return; + + if (categories[category]) { + const dataHref = link.getAttribute("data-href"); + if (dataHref) { + link.setAttribute("href", dataHref); + link.removeAttribute("data-href"); + } + } else if (previousCategories && previousCategories[category]) { + const href = link.getAttribute("href"); + if (href) { + link.setAttribute("data-href", href); + } + link.removeAttribute("href"); + } + }); + } catch { + // Never break the host site. + } +} diff --git a/packages/cookie-banner/src/headless/consent-manager.ts b/packages/cookie-banner/src/headless/consent-manager.ts new file mode 100644 index 000000000..f8b22b0ec --- /dev/null +++ b/packages/cookie-banner/src/headless/consent-manager.ts @@ -0,0 +1,336 @@ +// Copyright (c) 2025-2026 Probo Inc . +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR +// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +// PERFORMANCE OF THIS SOFTWARE. + +import { fetchConfig, postConsent, flushConsentQueue } from "./api"; +import { applyConsent } from "./apply"; +import { + installCookieInterceptor, + updateCookieInterceptor, + uninstallCookieInterceptor, +} from "./cookie-interceptor"; +import { cleanupCookies } from "./cookies"; +import { + addConsentChangeListener, + removeConsentChangeListener, + notifyConsentChange, +} from "./events"; +import { setGCMDefault, updateGCM } from "./gcm"; +import { isGPCEnabled } from "./gpc"; +import { getStrings } from "./i18n"; +import { + startObserver, + updateObserverConsent, + stopObserver, +} from "./observer"; +import { + getStoredConsent, + setStoredConsent, + clearStoredConsent, + generateVisitorId, +} from "./storage"; +import type { + BannerConfig, + ConsentChangeCallback, + ConsentManagerConfig, + ConsentMode, + WidgetStrings, +} from "./types"; + +export class ConsentManager { + private config: BannerConfig | null = null; + private currentConsent: Record = {}; + private visitorId: string; + private strings: WidgetStrings; + private readyCbs: Array<(config: BannerConfig) => void> = []; + private consentRequiredCbs: Array<(config: BannerConfig) => void> = []; + private gpcHandledCbs: Array<(config: BannerConfig) => void> = []; + + readonly bannerId: string; + readonly baseUrl: string; + private consentMode: ConsentMode; + readonly gpcEnabled: boolean; + + constructor(private readonly managerConfig: ConsentManagerConfig) { + this.bannerId = managerConfig.bannerId; + this.baseUrl = managerConfig.baseUrl; + this.consentMode = managerConfig.consentMode ?? "opt-in"; // Overridden by server config + this.strings = getStrings(managerConfig.lang ?? "en"); + this.visitorId = + getStoredConsent()?.visitorId ?? generateVisitorId(); + this.gpcEnabled = isGPCEnabled(); + } + + async init(): Promise { + const config = await fetchConfig(this.baseUrl, this.bannerId); + if (!config) return; + + this.config = config; + + // Use server-side consent mode if provided. + const serverMode = config.consent_mode; + if (serverMode === "opt-out") { + this.consentMode = "opt-out"; + } else if (serverMode === "opt-in") { + this.consentMode = "opt-in"; + } + + // Install cookie interceptor before anything else to block + // writes from scripts that may run before consent is given. + const defaultConsent = this.buildDefaultConsent(); + installCookieInterceptor(config.categories, defaultConsent); + + // Set Google Consent Mode defaults. + setGCMDefault(config.categories, defaultConsent); + + // Flush any queued consent API calls from previous sessions. + flushConsentQueue(); + + // Notify ready listeners. + for (const cb of this.readyCbs) { + try { + cb(config); + } catch { + // Never break the host site. + } + } + + // GPC signal: auto-reject non-required categories and skip the banner. + if (this.gpcEnabled) { + const gpcData = this.buildConsentData(false); + this.currentConsent = gpcData; + updateCookieInterceptor(gpcData); + applyConsent(gpcData); + updateGCM(config.categories, gpcData); + startObserver(gpcData); + for (const cb of this.gpcHandledCbs) { + try { + cb(config); + } catch { + // Never break the host site. + } + } + return; + } + + const stored = getStoredConsent(); + + // If consent exists, is valid, version matches, and not expired — apply silently. + if ( + stored && + stored.version === config.version && + !this.isConsentExpired(stored) + ) { + this.currentConsent = stored.categories; + this.visitorId = stored.visitorId; + updateCookieInterceptor(stored.categories); + applyConsent(stored.categories); + updateGCM(config.categories, stored.categories); + startObserver(stored.categories); + return; + } + + // No stored consent: apply defaults based on consent mode. + if (this.consentMode === "opt-out") { + const defaults = this.buildConsentData(true); + this.currentConsent = defaults; + updateCookieInterceptor(defaults); + applyConsent(defaults); + updateGCM(config.categories, defaults); + startObserver(defaults); + } else { + // opt-in: only required categories are active — interceptor + // already installed with defaultConsent above. + applyConsent(defaultConsent); + startObserver(defaultConsent); + } + + // Notify consent required listeners. + for (const cb of this.consentRequiredCbs) { + try { + cb(config); + } catch { + // Never break the host site. + } + } + } + + destroy(): void { + this.readyCbs = []; + this.consentRequiredCbs = []; + this.gpcHandledCbs = []; + stopObserver(); + uninstallCookieInterceptor(); + } + + getConfig(): BannerConfig | null { + return this.config; + } + + getStrings(): WidgetStrings { + return this.strings; + } + + getConsent(categoryId: string): boolean | null { + if (categoryId in this.currentConsent) { + return this.currentConsent[categoryId]; + } + return null; + } + + getConsents(): Record { + return { ...this.currentConsent }; + } + + needsConsent(): boolean { + if (!this.config) return false; + if (this.gpcEnabled) return false; + const stored = getStoredConsent(); + if (!stored) return true; + if (stored.version !== this.config.version) return true; + return this.isConsentExpired(stored); + } + + acceptAll(): void { + if (!this.config) return; + const previousConsent = { ...this.currentConsent }; + const data = this.buildConsentData(true); + this.applyAndStore(data, previousConsent, "ACCEPT_ALL"); + } + + rejectAll(): void { + if (!this.config) return; + const previousConsent = { ...this.currentConsent }; + const data = this.buildConsentData(false); + this.applyAndStore(data, previousConsent, "REJECT_ALL"); + } + + acceptCategory(categoryId: string): void { + if (!this.config) return; + const previousConsent = { ...this.currentConsent }; + const data = { ...this.currentConsent }; + for (const cat of this.config.categories) { + if (cat.required) data[cat.id] = true; + } + data[categoryId] = true; + this.applyAndStore(data, previousConsent, "ACCEPT_CATEGORY"); + } + + savePreferences(choices: Record): void { + if (!this.config) return; + const previousConsent = { ...this.currentConsent }; + // Ensure required categories are always true. + for (const cat of this.config.categories) { + if (cat.required) choices[cat.id] = true; + } + this.applyAndStore(choices, previousConsent, "CUSTOMIZE"); + } + + reset(): void { + clearStoredConsent(); + this.currentConsent = {}; + this.visitorId = generateVisitorId(); + stopObserver(); + + if (this.config) { + for (const cb of this.consentRequiredCbs) { + try { + cb(this.config); + } catch { + // Never break the host site. + } + } + } + } + + onReady(cb: (config: BannerConfig) => void): void { + this.readyCbs.push(cb); + // If already initialized, fire immediately. + if (this.config) { + try { + cb(this.config); + } catch { + // Never break the host site. + } + } + } + + onConsentRequired(cb: (config: BannerConfig) => void): void { + this.consentRequiredCbs.push(cb); + } + + onGPCHandled(cb: (config: BannerConfig) => void): void { + this.gpcHandledCbs.push(cb); + } + + onConsentChange(cb: ConsentChangeCallback): void { + addConsentChangeListener(cb); + } + + removeConsentChangeListener(cb: ConsentChangeCallback): void { + removeConsentChangeListener(cb); + } + + private isConsentExpired(stored: import("./types").StoredConsent): boolean { + if (!this.config || !stored.timestamp) return false; + const expiryMs = this.config.consent_expiry_days * 24 * 60 * 60 * 1000; + return Date.now() - stored.timestamp > expiryMs; + } + + private buildDefaultConsent(): Record { + const data: Record = {}; + if (!this.config) return data; + for (const cat of this.config.categories) { + data[cat.id] = cat.required; + } + return data; + } + + private buildConsentData(accepted: boolean): Record { + const data: Record = {}; + for (const cat of this.config!.categories) { + data[cat.id] = cat.required || accepted; + } + return data; + } + + private applyAndStore( + data: Record, + previousConsent: Record, + action: string, + ): void { + this.currentConsent = data; + setStoredConsent( + { + visitorId: this.visitorId, + version: this.config!.version, + categories: data, + timestamp: Date.now(), + }, + this.config!.consent_expiry_days, + ); + updateCookieInterceptor(data); + applyConsent(data, previousConsent); + updateObserverConsent(data); + updateGCM(this.config!.categories, data); + cleanupCookies(this.config!.categories, previousConsent, data); + notifyConsentChange(data); + postConsent( + this.baseUrl, + this.bannerId, + this.visitorId, + data, + action, + ); + } +} diff --git a/packages/cookie-banner/src/headless/cookie-interceptor.ts b/packages/cookie-banner/src/headless/cookie-interceptor.ts new file mode 100644 index 000000000..60ca093d0 --- /dev/null +++ b/packages/cookie-banner/src/headless/cookie-interceptor.ts @@ -0,0 +1,128 @@ +// Copyright (c) 2025-2026 Probo Inc . +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR +// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +// PERFORMANCE OF THIS SOFTWARE. + +import type { BannerCategory } from "./types"; + +// Save the native descriptor before anyone else can tamper with it. +const nativeDescriptor = Object.getOwnPropertyDescriptor( + Document.prototype, + "cookie", +); + +let installed = false; +let categories: BannerCategory[] = []; +let currentConsent: Record = {}; + +// Our own cookie name — must always be allowed through. +const PROBO_COOKIE = "probo_consent"; + +function parseCookieName(cookieStr: string): string { + const eqIdx = cookieStr.indexOf("="); + if (eqIdx === -1) return cookieStr.trim(); + return cookieStr.substring(0, eqIdx).trim(); +} + +function matchesPattern(cookieName: string, pattern: string): boolean { + if (pattern.startsWith("^") || pattern.endsWith("$")) { + try { + return new RegExp(pattern).test(cookieName); + } catch { + return false; + } + } + return cookieName === pattern; +} + +function isBlocked(cookieName: string): boolean { + for (const cat of categories) { + // Required categories are always allowed. + if (cat.required) continue; + + // If this category is consented, its cookies are allowed. + if (currentConsent[cat.id] === true) continue; + + // Check if this cookie name belongs to a non-consented category. + for (const cookie of cat.cookies) { + if (matchesPattern(cookieName, cookie.name)) { + return true; + } + } + } + + return false; +} + +export function installCookieInterceptor( + bannerCategories: BannerCategory[], + consent: Record, +): void { + categories = bannerCategories; + currentConsent = { ...consent }; + + if (installed) return; + if (!nativeDescriptor?.set || !nativeDescriptor?.get) return; + + try { + const nativeSet = nativeDescriptor.set; + const nativeGet = nativeDescriptor.get; + + Object.defineProperty(document, "cookie", { + configurable: true, + enumerable: true, + get() { + return nativeGet.call(this); + }, + set(value: string) { + const name = parseCookieName(value); + + // Always allow our own consent cookie. + if (name === PROBO_COOKIE) { + nativeSet.call(this, value); + return; + } + + // Block cookies that belong to non-consented categories. + if (isBlocked(name)) { + return; + } + + // Unknown cookies (not in any category) are allowed through. + nativeSet.call(this, value); + }, + }); + + installed = true; + } catch { + // If we can't override (e.g. frozen prototype), silently give up. + // Never break the host site. + } +} + +export function updateCookieInterceptor( + consent: Record, +): void { + currentConsent = { ...consent }; +} + +export function uninstallCookieInterceptor(): void { + if (!installed) return; + if (!nativeDescriptor) return; + + try { + Object.defineProperty(document, "cookie", nativeDescriptor); + installed = false; + } catch { + // Never break the host site. + } +} diff --git a/packages/cookie-banner/src/headless/cookies.ts b/packages/cookie-banner/src/headless/cookies.ts new file mode 100644 index 000000000..d3e4772ec --- /dev/null +++ b/packages/cookie-banner/src/headless/cookies.ts @@ -0,0 +1,70 @@ +// Copyright (c) 2025-2026 Probo Inc . +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR +// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +// PERFORMANCE OF THIS SOFTWARE. + +import type { BannerCategory } from "./types"; + +function deleteCookie(name: string): void { + const hostname = window.location.hostname; + const variations = [ + `${name}=; max-age=-1; path=/`, + `${name}=; max-age=-1; path=/; domain=${hostname}`, + `${name}=; max-age=-1; path=/; domain=.${hostname}`, + ]; + for (const cookie of variations) { + document.cookie = cookie; + } +} + +function matchesPattern(cookieName: string, pattern: string): boolean { + if (pattern.startsWith("^") || pattern.endsWith("$")) { + try { + return new RegExp(pattern).test(cookieName); + } catch { + return false; + } + } + return cookieName === pattern; +} + +function getAllCookieNames(): string[] { + if (!document.cookie) return []; + return document.cookie + .split(";") + .map((c) => c.trim().split("=")[0]) + .filter((n) => n.length > 0); +} + +export function cleanupCookies( + categories: BannerCategory[], + previousConsent: Record, + newConsent: Record, +): void { + try { + const cookieNames = getAllCookieNames(); + for (const category of categories) { + if (!previousConsent[category.id] || newConsent[category.id]) continue; + if (!category.cookies || category.cookies.length === 0) continue; + + for (const cookie of category.cookies) { + for (const name of cookieNames) { + if (matchesPattern(name, cookie.name)) { + deleteCookie(name); + } + } + } + } + } catch { + // Never break the host site due to cookie cleanup errors. + } +} diff --git a/packages/cookie-banner/src/headless/events.ts b/packages/cookie-banner/src/headless/events.ts new file mode 100644 index 000000000..882096afd --- /dev/null +++ b/packages/cookie-banner/src/headless/events.ts @@ -0,0 +1,37 @@ +// Copyright (c) 2025-2026 Probo Inc . +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR +// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +// PERFORMANCE OF THIS SOFTWARE. + +import type { ConsentChangeCallback } from "./types"; + +const listeners = new Set(); + +export function addConsentChangeListener(cb: ConsentChangeCallback): void { + listeners.add(cb); +} + +export function removeConsentChangeListener(cb: ConsentChangeCallback): void { + listeners.delete(cb); +} + +export function notifyConsentChange( + consents: Record, +): void { + for (const cb of listeners) { + try { + cb(consents); + } catch { + // Never break the host site due to a listener error. + } + } +} diff --git a/packages/cookie-banner/src/headless/gcm.ts b/packages/cookie-banner/src/headless/gcm.ts new file mode 100644 index 000000000..71a30bd1b --- /dev/null +++ b/packages/cookie-banner/src/headless/gcm.ts @@ -0,0 +1,122 @@ +// Copyright (c) 2025-2026 Probo Inc . +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR +// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +// PERFORMANCE OF THIS SOFTWARE. + +import type { BannerCategory } from "./types"; + +type ConsentSignal = "granted" | "denied"; + +interface GCMState { + ad_storage: ConsentSignal; + ad_user_data: ConsentSignal; + ad_personalization: ConsentSignal; + analytics_storage: ConsentSignal; + functionality_storage: ConsentSignal; + personalization_storage: ConsentSignal; + security_storage: ConsentSignal; +} + +// Maps category names (lowercased) to the GCM signals they control. +const categorySignalMap: Record = { + necessary: ["functionality_storage", "security_storage"], + analytics: ["analytics_storage"], + marketing: [ + "ad_storage", + "ad_user_data", + "ad_personalization", + ], + preferences: ["personalization_storage"], +}; + +function getGtag(): ((...args: unknown[]) => void) | null { + const w = window as unknown as Record; + + if (typeof w.gtag === "function") { + return w.gtag as (...args: unknown[]) => void; + } + + // Ensure dataLayer exists so gtag calls are queued for when GTM loads. + if (!Array.isArray(w.dataLayer)) { + return null; + } + + // Define a minimal gtag function that pushes to dataLayer. + const gtag = function (...args: unknown[]) { + (w.dataLayer as unknown[]).push(args); + }; + w.gtag = gtag; + return gtag; +} + +function buildGCMState( + categories: BannerCategory[], + consent: Record, +): GCMState { + const state: GCMState = { + ad_storage: "denied", + ad_user_data: "denied", + ad_personalization: "denied", + analytics_storage: "denied", + functionality_storage: "denied", + personalization_storage: "denied", + security_storage: "denied", + }; + + for (const cat of categories) { + const granted = consent[cat.id] === true; + if (!granted) continue; + + const signals = + categorySignalMap[cat.name.toLowerCase()]; + if (!signals) continue; + + for (const signal of signals) { + state[signal] = "granted"; + } + } + + return state; +} + +export function setGCMDefault( + categories: BannerCategory[], + consent: Record, +): void { + try { + const gtag = getGtag(); + if (!gtag) return; + + const state = buildGCMState(categories, consent); + gtag("consent", "default", { + ...state, + wait_for_update: 500, + }); + } catch { + // Never break the host site. + } +} + +export function updateGCM( + categories: BannerCategory[], + consent: Record, +): void { + try { + const gtag = getGtag(); + if (!gtag) return; + + const state = buildGCMState(categories, consent); + gtag("consent", "update", state); + } catch { + // Never break the host site. + } +} diff --git a/packages/cookie-banner/src/headless/gpc.ts b/packages/cookie-banner/src/headless/gpc.ts new file mode 100644 index 000000000..498b3e26e --- /dev/null +++ b/packages/cookie-banner/src/headless/gpc.ts @@ -0,0 +1,22 @@ +// Copyright (c) 2025-2026 Probo Inc . +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR +// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +// PERFORMANCE OF THIS SOFTWARE. + +export function isGPCEnabled(): boolean { + try { + const nav = navigator as unknown as Record; + return nav.globalPrivacyControl === true; + } catch { + return false; + } +} diff --git a/packages/cookie-banner/src/headless/i18n.ts b/packages/cookie-banner/src/headless/i18n.ts new file mode 100644 index 000000000..79b82e7d2 --- /dev/null +++ b/packages/cookie-banner/src/headless/i18n.ts @@ -0,0 +1,81 @@ +// Copyright (c) 2025-2026 Probo Inc . +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR +// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +// PERFORMANCE OF THIS SOFTWARE. + +import type { WidgetStrings } from "./types"; + +const translations: Record = { + en: { + customize: "Customize", + cookiePreferences: "Cookie Preferences", + back: "Back", + rejectAll: "Reject all", + acceptAll: "Accept all", + savePreferences: "Save my preferences", + privacyPolicy: "Privacy Policy", + cookiePreferencesTooltip: "Cookie preferences", + contextualBlockedMessage: + 'This content requires "{categoryName}" cookies to be displayed.', + contextualAllowButton: "Allow and show content", + }, + fr: { + customize: "Personnaliser", + cookiePreferences: "Préférences de cookies", + back: "Retour", + rejectAll: "Tout refuser", + acceptAll: "Tout accepter", + savePreferences: "Enregistrer mes préférences", + privacyPolicy: "Politique de confidentialité", + cookiePreferencesTooltip: "Préférences de cookies", + contextualBlockedMessage: + 'Ce contenu nécessite les cookies « {categoryName} » pour être affiché.', + contextualAllowButton: "Autoriser et afficher le contenu", + }, + de: { + customize: "Anpassen", + cookiePreferences: "Cookie-Einstellungen", + back: "Zurück", + rejectAll: "Alle ablehnen", + acceptAll: "Alle akzeptieren", + savePreferences: "Meine Einstellungen speichern", + privacyPolicy: "Datenschutzerklärung", + cookiePreferencesTooltip: "Cookie-Einstellungen", + contextualBlockedMessage: + 'Dieser Inhalt erfordert „{categoryName}"-Cookies, um angezeigt zu werden.', + contextualAllowButton: "Zulassen und Inhalt anzeigen", + }, + es: { + customize: "Personalizar", + cookiePreferences: "Preferencias de cookies", + back: "Volver", + rejectAll: "Rechazar todo", + acceptAll: "Aceptar todo", + savePreferences: "Guardar mis preferencias", + privacyPolicy: "Política de privacidad", + cookiePreferencesTooltip: "Preferencias de cookies", + contextualBlockedMessage: + 'Este contenido requiere cookies de "{categoryName}" para mostrarse.', + contextualAllowButton: "Permitir y mostrar contenido", + }, +}; + +export function getStrings(lang: string): WidgetStrings { + const exact = translations[lang]; + if (exact) return exact; + + const prefix = lang.split("-")[0]; + const prefixMatch = translations[prefix]; + if (prefixMatch) return prefixMatch; + + return translations["en"]; +} diff --git a/packages/cookie-banner/src/headless/index.ts b/packages/cookie-banner/src/headless/index.ts new file mode 100644 index 000000000..2f74fe492 --- /dev/null +++ b/packages/cookie-banner/src/headless/index.ts @@ -0,0 +1,26 @@ +// Copyright (c) 2025-2026 Probo Inc . +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR +// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +// PERFORMANCE OF THIS SOFTWARE. + +export { ConsentManager } from "./consent-manager"; +export { defaultTheme } from "./api"; +export type { + BannerCategory, + BannerConfig, + ConsentChangeCallback, + ConsentManagerConfig, + ConsentMode, + StoredConsent, + ThemeConfig, + WidgetStrings, +} from "./types"; diff --git a/packages/cookie-banner/src/headless/observer.ts b/packages/cookie-banner/src/headless/observer.ts new file mode 100644 index 000000000..74ea8069a --- /dev/null +++ b/packages/cookie-banner/src/headless/observer.ts @@ -0,0 +1,74 @@ +// Copyright (c) 2025-2026 Probo Inc . +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR +// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +// PERFORMANCE OF THIS SOFTWARE. + +import { applyConsent } from "./apply"; + +let observer: MutationObserver | null = null; +let currentConsent: Record = {}; + +function handleMutations(mutations: MutationRecord[]): void { + let hasNewElements = false; + + for (const mutation of mutations) { + for (const node of Array.from(mutation.addedNodes)) { + if (node.nodeType !== Node.ELEMENT_NODE) continue; + const el = node as HTMLElement; + + if (el.hasAttribute("data-cookie-category")) { + hasNewElements = true; + break; + } + + if (el.querySelector("[data-cookie-category]")) { + hasNewElements = true; + break; + } + } + + if (hasNewElements) break; + } + + if (hasNewElements) { + applyConsent(currentConsent); + } +} + +export function startObserver(consent: Record): void { + currentConsent = { ...consent }; + + if (observer) return; + + try { + observer = new MutationObserver(handleMutations); + observer.observe(document.body, { + childList: true, + subtree: true, + }); + } catch { + // Never break the host site. + } +} + +export function updateObserverConsent( + consent: Record, +): void { + currentConsent = { ...consent }; +} + +export function stopObserver(): void { + if (observer) { + observer.disconnect(); + observer = null; + } +} diff --git a/packages/cookie-banner/src/headless/storage.ts b/packages/cookie-banner/src/headless/storage.ts new file mode 100644 index 000000000..52829a2f0 --- /dev/null +++ b/packages/cookie-banner/src/headless/storage.ts @@ -0,0 +1,154 @@ +// Copyright (c) 2025-2026 Probo Inc . +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR +// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +// PERFORMANCE OF THIS SOFTWARE. + +import type { StoredConsent } from "./types"; + +const COOKIE_NAME = "probo_consent"; +const LS_CONSENT_KEY = "probo_consent"; +const LS_QUEUE_KEY = "probo_consent_queue"; + +// --- Cookie helpers --- + +function getCookieConsent(): StoredConsent | null { + try { + const match = document.cookie + .split("; ") + .find((c) => c.startsWith(`${COOKIE_NAME}=`)); + if (!match) return null; + return JSON.parse( + decodeURIComponent(match.split("=").slice(1).join("=")), + ); + } catch { + return null; + } +} + +function setCookieConsent( + consent: StoredConsent, + maxAgeDays: number, +): boolean { + try { + const value = encodeURIComponent(JSON.stringify(consent)); + const maxAge = maxAgeDays * 24 * 60 * 60; + document.cookie = `${COOKIE_NAME}=${value}; path=/; max-age=${maxAge}; SameSite=Lax`; + // Verify the cookie was actually set. + return document.cookie.includes(`${COOKIE_NAME}=`); + } catch { + return false; + } +} + +function clearCookieConsent(): void { + try { + document.cookie = `${COOKIE_NAME}=; max-age=-1; path=/; SameSite=Lax`; + } catch { + // Ignore. + } +} + +// --- localStorage helpers --- + +function getLocalStorageConsent(): StoredConsent | null { + try { + const raw = localStorage.getItem(LS_CONSENT_KEY); + if (!raw) return null; + return JSON.parse(raw); + } catch { + return null; + } +} + +function setLocalStorageConsent(consent: StoredConsent): void { + try { + localStorage.setItem(LS_CONSENT_KEY, JSON.stringify(consent)); + } catch { + // localStorage may be full or disabled. + } +} + +function clearLocalStorageConsent(): void { + try { + localStorage.removeItem(LS_CONSENT_KEY); + } catch { + // Ignore. + } +} + +// --- Public API: cookie-first with localStorage fallback --- + +export function getStoredConsent(): StoredConsent | null { + return getCookieConsent() ?? getLocalStorageConsent(); +} + +export function setStoredConsent( + consent: StoredConsent, + maxAgeDays: number, +): void { + const cookieOk = setCookieConsent(consent, maxAgeDays); + if (!cookieOk) { + // Cookie blocked (e.g. Safari ITP, private browsing) — fall back. + setLocalStorageConsent(consent); + } else { + // Keep localStorage in sync as a backup. + setLocalStorageConsent(consent); + } +} + +export function clearStoredConsent(): void { + clearCookieConsent(); + clearLocalStorageConsent(); +} + +export function generateVisitorId(): string { + const array = new Uint8Array(16); + crypto.getRandomValues(array); + return Array.from(array, (b) => b.toString(16).padStart(2, "0")).join(""); +} + +// --- Consent API retry queue --- + +export interface QueuedConsent { + baseUrl: string; + bannerId: string; + visitorId: string; + consentData: Record; + action: string; + timestamp: number; +} + +export function enqueueConsent(entry: QueuedConsent): void { + try { + const raw = localStorage.getItem(LS_QUEUE_KEY); + const queue: QueuedConsent[] = raw ? JSON.parse(raw) : []; + queue.push(entry); + // Keep at most 20 entries to avoid filling localStorage. + if (queue.length > 20) { + queue.splice(0, queue.length - 20); + } + localStorage.setItem(LS_QUEUE_KEY, JSON.stringify(queue)); + } catch { + // localStorage may be full or disabled. + } +} + +export function dequeueAllConsents(): QueuedConsent[] { + try { + const raw = localStorage.getItem(LS_QUEUE_KEY); + if (!raw) return []; + localStorage.removeItem(LS_QUEUE_KEY); + return JSON.parse(raw); + } catch { + return []; + } +} diff --git a/packages/cookie-banner/src/headless/types.ts b/packages/cookie-banner/src/headless/types.ts new file mode 100644 index 000000000..399574795 --- /dev/null +++ b/packages/cookie-banner/src/headless/types.ts @@ -0,0 +1,91 @@ +// Copyright (c) 2025-2026 Probo Inc . +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR +// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +// PERFORMANCE OF THIS SOFTWARE. + +export interface BannerCookieItem { + name: string; + duration: string; + description: string; +} + +export interface BannerCategory { + id: string; + name: string; + description: string; + required: boolean; + rank: number; + cookies: BannerCookieItem[]; +} + +export interface ThemeConfig { + primary_color: string; + primary_text_color: string; + secondary_color: string; + secondary_text_color: string; + background_color: string; + text_color: string; + secondary_text_body_color: string; + border_color: string; + font_family: string; + border_radius: number; + position: "bottom" | "bottom-left" | "bottom-right" | "center"; + revisit_position: "bottom-left" | "bottom-right"; +} + +export interface BannerConfig { + id: string; + title: string; + description: string; + accept_all_label: string; + reject_all_label: string; + save_preferences_label: string; + privacy_policy_url: string; + consent_expiry_days: number; + consent_mode: ConsentMode; + version: number; + categories: BannerCategory[]; + theme: ThemeConfig; +} + +export interface StoredConsent { + visitorId: string; + version: number; + categories: Record; + timestamp: number; +} + +export type ConsentChangeCallback = ( + consents: Record, +) => void; + +export type ConsentMode = "opt-in" | "opt-out"; + +export interface ConsentManagerConfig { + bannerId: string; + baseUrl: string; + lang?: string; + consentMode?: ConsentMode; +} + +export interface WidgetStrings { + customize: string; + cookiePreferences: string; + back: string; + rejectAll: string; + acceptAll: string; + savePreferences: string; + privacyPolicy: string; + cookiePreferencesTooltip: string; + contextualBlockedMessage: string; + contextualAllowButton: string; +} diff --git a/packages/cookie-banner/src/styled/banner-renderer.ts b/packages/cookie-banner/src/styled/banner-renderer.ts new file mode 100644 index 000000000..d0ff4ba16 --- /dev/null +++ b/packages/cookie-banner/src/styled/banner-renderer.ts @@ -0,0 +1,235 @@ +// Copyright (c) 2025-2026 Probo Inc . +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR +// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +// PERFORMANCE OF THIS SOFTWARE. + +import type { BannerConfig, ThemeConfig, WidgetStrings } from "../headless/types"; +import { defaultTheme } from "../headless/api"; +import { renderPreferences } from "./preferences-renderer"; +import { themeToCSSVars } from "./theme"; + +export interface BannerCallbacks { + onAcceptAll: () => void; + onRejectAll: () => void; + onCustomize: (choices: Record) => void; +} + +export interface BannerOptions { + preview?: boolean; + theme?: ThemeConfig; +} + +export function renderBanner( + root: ShadowRoot, + config: BannerConfig, + currentConsent: Record, + callbacks: BannerCallbacks, + strings: WidgetStrings, + options?: BannerOptions, +): void { + root.innerHTML = ""; + + const theme = options?.theme ?? config.theme ?? defaultTheme; + + const style = document.createElement("style"); + style.textContent = ` + :host { ${themeToCSSVars(theme)} } + * { box-sizing: border-box; margin: 0; padding: 0; } + .probo-banner-overlay { + position: fixed; bottom: 0; left: 0; right: 0; z-index: 2147483647; + font-family: var(--probo-font); + } + .probo-banner-container { + background: var(--probo-bg); border-top: 1px solid var(--probo-border); + box-shadow: 0 -4px 20px rgba(0,0,0,0.1); + padding: 0 24px; max-width: 100%; + overflow: hidden; + transition: max-height 0.3s ease-out, padding 0.3s ease-out; + max-height: 0; + } + .probo-banner-container.open { + padding: 24px; + max-height: 800px; + } + .probo-banner-content { + max-width: 1200px; margin: 0 auto; + } + .probo-banner-title { + font-size: 16px; font-weight: 600; color: var(--probo-text); margin-bottom: 8px; + } + .probo-banner-desc { + font-size: 14px; color: var(--probo-text-secondary); line-height: 1.5; margin-bottom: 16px; + } + .probo-banner-actions { + display: flex; gap: 12px; flex-wrap: wrap; align-items: center; + } + .probo-btn { + padding: 10px 24px; border-radius: var(--probo-radius); font-size: 14px; + font-weight: 500; cursor: pointer; border: none; transition: opacity 0.2s; + font-family: var(--probo-font); + } + .probo-btn:hover { opacity: 0.9; } + .probo-btn-primary { + background: var(--probo-primary); color: var(--probo-primary-text); + } + .probo-btn-secondary { + background: var(--probo-secondary); color: var(--probo-secondary-text); + } + .probo-btn-outline { + background: transparent; color: var(--probo-text); + border: 1px solid var(--probo-border); + } + .probo-privacy-link { + font-size: 13px; color: var(--probo-text-secondary); text-decoration: underline; cursor: pointer; + margin-left: auto; + } + .probo-privacy-link:hover { color: var(--probo-text); } + `; + root.appendChild(style); + + const overlay = document.createElement("div"); + overlay.className = "probo-banner-overlay"; + overlay.setAttribute("role", "dialog"); + overlay.setAttribute("aria-label", config.title); + overlay.setAttribute("aria-modal", "false"); + + const container = document.createElement("div"); + container.className = "probo-banner-container"; + + const content = document.createElement("div"); + content.className = "probo-banner-content"; + + const transitionTo = (render: () => void) => { + content.innerHTML = ""; + render(); + }; + + // Main banner view + const renderMainContent = () => { + const title = document.createElement("div"); + title.className = "probo-banner-title"; + title.textContent = config.title; + content.appendChild(title); + + const desc = document.createElement("div"); + desc.className = "probo-banner-desc"; + desc.textContent = config.description; + content.appendChild(desc); + + const actions = document.createElement("div"); + actions.className = "probo-banner-actions"; + + const rejectBtn = document.createElement("button"); + rejectBtn.className = "probo-btn probo-btn-secondary"; + rejectBtn.textContent = config.reject_all_label; + rejectBtn.setAttribute("aria-label", config.reject_all_label); + rejectBtn.addEventListener("click", () => { + if (!options?.preview) { + callbacks.onRejectAll(); + overlay.remove(); + } + }); + actions.appendChild(rejectBtn); + + const acceptBtn = document.createElement("button"); + acceptBtn.className = "probo-btn probo-btn-primary"; + acceptBtn.textContent = config.accept_all_label; + acceptBtn.setAttribute("aria-label", config.accept_all_label); + acceptBtn.addEventListener("click", () => { + if (!options?.preview) { + callbacks.onAcceptAll(); + overlay.remove(); + } + }); + actions.appendChild(acceptBtn); + + const customizeBtn = document.createElement("button"); + customizeBtn.className = "probo-btn probo-btn-outline"; + customizeBtn.textContent = strings.customize; + customizeBtn.setAttribute("aria-label", strings.customize); + customizeBtn.addEventListener("click", () => { + transitionTo(() => { + renderPreferences( + content, + config.categories, + currentConsent, + options?.preview + ? () => { transitionTo(renderMainContent); } + : (choices) => { + callbacks.onCustomize(choices); + overlay.remove(); + }, + options?.preview + ? () => { transitionTo(renderMainContent); } + : () => { + callbacks.onRejectAll(); + overlay.remove(); + }, + options?.preview + ? () => { transitionTo(renderMainContent); } + : () => { + callbacks.onAcceptAll(); + overlay.remove(); + }, + strings, + theme, + ); + }); + }); + actions.appendChild(customizeBtn); + + if (config.privacy_policy_url) { + const link = document.createElement("a"); + link.className = "probo-privacy-link"; + link.textContent = strings.privacyPolicy; + link.href = config.privacy_policy_url; + link.target = "_blank"; + link.rel = "noopener noreferrer"; + actions.appendChild(link); + } + + content.appendChild(actions); + }; + + renderMainContent(); + container.appendChild(content); + overlay.appendChild(container); + root.appendChild(overlay); + + // Focus trap: cycle Tab within the banner. + overlay.addEventListener("keydown", (e: KeyboardEvent) => { + if (e.key !== "Tab") return; + const focusable = Array.from( + overlay.querySelectorAll("button, a, input, [tabindex]"), + ); + if (focusable.length === 0) return; + const first = focusable[0]; + const last = focusable[focusable.length - 1]; + if (e.shiftKey && root.activeElement === first) { + e.preventDefault(); + last.focus(); + } else if (!e.shiftKey && root.activeElement === last) { + e.preventDefault(); + first.focus(); + } + }); + + // Trigger grow animation on next frame + requestAnimationFrame(() => { + requestAnimationFrame(() => { + container.classList.add("open"); + // Auto-focus the first button after animation. + const firstBtn = overlay.querySelector("button"); + firstBtn?.focus(); + }); + }); +} diff --git a/packages/cookie-banner/src/styled/index.ts b/packages/cookie-banner/src/styled/index.ts new file mode 100644 index 000000000..fb894838b --- /dev/null +++ b/packages/cookie-banner/src/styled/index.ts @@ -0,0 +1,24 @@ +// Copyright (c) 2025-2026 Probo Inc . +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR +// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +// PERFORMANCE OF THIS SOFTWARE. + +export { StyledBanner } from "./styled-banner"; +export { renderBanner } from "./banner-renderer"; +export type { BannerCallbacks, BannerOptions } from "./banner-renderer"; +export { + applyContextualPlaceholders, + removeAllPlaceholders, +} from "./placeholder-renderer"; +export { renderPreferences } from "./preferences-renderer"; +export { renderRevisitIcon } from "./revisit-renderer"; +export { themeToCSSVars } from "./theme"; diff --git a/packages/cookie-banner/src/styled/placeholder-renderer.ts b/packages/cookie-banner/src/styled/placeholder-renderer.ts new file mode 100644 index 000000000..db0104c32 --- /dev/null +++ b/packages/cookie-banner/src/styled/placeholder-renderer.ts @@ -0,0 +1,196 @@ +// Copyright (c) 2025-2026 Probo Inc . +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR +// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +// PERFORMANCE OF THIS SOFTWARE. + +import type { ThemeConfig, WidgetStrings } from "../headless/types"; + +const placeholderMap = new WeakMap(); +const originalDisplayMap = new WeakMap(); + +const LOCK_ICON_SVG = ``; + +function createPlaceholder( + el: HTMLElement, + categoryName: string, + categoryId: string, + onAcceptCategory: (categoryId: string) => void, + strings: WidgetStrings, + theme: ThemeConfig, +): HTMLElement { + const placeholder = document.createElement("div"); + placeholder.setAttribute("data-probo-placeholder", "true"); + + // Copy dimensions from the original element. + const width = el.getAttribute("width"); + const height = el.getAttribute("height"); + const computedStyle = window.getComputedStyle(el); + + const placeholderWidth = width + ? `${width}${width.includes("%") ? "" : "px"}` + : computedStyle.width; + const placeholderHeight = height + ? `${height}${height.includes("%") ? "" : "px"}` + : computedStyle.height; + + placeholder.style.cssText = [ + `display:flex`, + `flex-direction:column`, + `align-items:center`, + `justify-content:center`, + `gap:12px`, + `width:${placeholderWidth}`, + `height:${placeholderHeight}`, + `min-height:120px`, + `box-sizing:border-box`, + `padding:24px`, + `background-color:${theme.background_color}`, + `border:1px solid ${theme.border_color}`, + `border-radius:${theme.border_radius}px`, + `font-family:${theme.font_family}`, + `color:${theme.text_color}`, + `text-align:center`, + `overflow:hidden`, + ].join(";"); + + // Icon + const icon = document.createElement("div"); + icon.style.cssText = [ + `color:${theme.secondary_text_body_color}`, + `opacity:0.6`, + ].join(";"); + icon.innerHTML = LOCK_ICON_SVG; + placeholder.appendChild(icon); + + // Message + const message = document.createElement("p"); + message.style.cssText = [ + `margin:0`, + `font-size:14px`, + `line-height:1.4`, + `color:${theme.secondary_text_body_color}`, + `max-width:360px`, + ].join(";"); + message.textContent = strings.contextualBlockedMessage.replace( + "{categoryName}", + categoryName, + ); + placeholder.appendChild(message); + + // Button + const button = document.createElement("button"); + button.style.cssText = [ + `display:inline-flex`, + `align-items:center`, + `gap:6px`, + `padding:8px 16px`, + `border:none`, + `border-radius:${theme.border_radius}px`, + `background-color:${theme.primary_color}`, + `color:${theme.primary_text_color}`, + `font-family:${theme.font_family}`, + `font-size:14px`, + `font-weight:500`, + `cursor:pointer`, + `transition:opacity 0.15s`, + ].join(";"); + button.textContent = strings.contextualAllowButton; + button.addEventListener("mouseenter", () => { + button.style.opacity = "0.85"; + }); + button.addEventListener("mouseleave", () => { + button.style.opacity = "1"; + }); + button.addEventListener("click", (e) => { + e.preventDefault(); + e.stopPropagation(); + onAcceptCategory(categoryId); + }); + placeholder.appendChild(button); + + return placeholder; +} + +export function applyContextualPlaceholders( + categories: Record, + getCategoryName: (id: string) => string, + onAcceptCategory: (categoryId: string) => void, + strings: WidgetStrings, + theme: ThemeConfig, +): void { + try { + const elements = document.querySelectorAll( + "iframe[data-cookie-category], img[data-cookie-category]", + ); + + elements.forEach((node) => { + const el = node as HTMLElement; + const categoryId = el.getAttribute("data-cookie-category"); + if (!categoryId) return; + + const consented = categories[categoryId] === true; + + if (!consented) { + // Already has a placeholder — skip. + if (placeholderMap.has(el)) return; + + const categoryName = getCategoryName(categoryId); + const placeholder = createPlaceholder( + el, + categoryName, + categoryId, + onAcceptCategory, + strings, + theme, + ); + + // Save original display value and hide the element. + originalDisplayMap.set(el, el.style.display); + el.style.display = "none"; + + // Insert placeholder right before the hidden element. + el.parentNode?.insertBefore(placeholder, el); + placeholderMap.set(el, placeholder); + } else { + // Consented — remove placeholder if present. + const placeholder = placeholderMap.get(el); + if (placeholder) { + placeholder.remove(); + placeholderMap.delete(el); + el.style.display = originalDisplayMap.get(el) ?? ""; + originalDisplayMap.delete(el); + } + } + }); + } catch { + // Never break the host site. + } +} + +export function removeAllPlaceholders(): void { + try { + const placeholders = document.querySelectorAll( + "[data-probo-placeholder]", + ); + placeholders.forEach((placeholder) => { + const el = placeholder.nextElementSibling as HTMLElement | null; + if (el) { + el.style.display = originalDisplayMap.get(el) ?? ""; + originalDisplayMap.delete(el); + placeholderMap.delete(el); + } + placeholder.remove(); + }); + } catch { + // Never break the host site. + } +} diff --git a/packages/cookie-banner/src/styled/preferences-renderer.ts b/packages/cookie-banner/src/styled/preferences-renderer.ts new file mode 100644 index 000000000..403884b10 --- /dev/null +++ b/packages/cookie-banner/src/styled/preferences-renderer.ts @@ -0,0 +1,224 @@ +// Copyright (c) 2025-2026 Probo Inc . +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR +// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +// PERFORMANCE OF THIS SOFTWARE. + +import type { BannerCategory, ThemeConfig, WidgetStrings } from "../headless/types"; + +export function renderPreferences( + container: HTMLElement, + categories: BannerCategory[], + currentConsent: Record, + onSave: (choices: Record) => void, + onRejectAll: () => void, + onAcceptAll: () => void, + strings: WidgetStrings, + theme: ThemeConfig, +): void { + const panel = document.createElement("div"); + panel.style.cssText = + "padding:24px;max-height:80vh;overflow-y:auto;"; + panel.setAttribute("role", "region"); + panel.setAttribute("aria-label", strings.cookiePreferences); + + const title = document.createElement("h2"); + title.textContent = strings.cookiePreferences; + title.style.cssText = + `margin:0 0 16px;font-size:18px;font-weight:600;color:${theme.text_color};`; + panel.appendChild(title); + + const checkboxes: { id: string; checkbox: HTMLInputElement }[] = []; + + for (const cat of categories) { + const section = document.createElement("div"); + section.style.cssText = + `padding:12px 0;border-bottom:1px solid ${theme.border_color};`; + + const row = document.createElement("div"); + row.style.cssText = + "display:flex;justify-content:space-between;align-items:flex-start;"; + + const info = document.createElement("div"); + info.style.cssText = "flex:1;margin-right:16px;"; + + const nameRow = document.createElement("div"); + nameRow.style.cssText = "display:flex;align-items:center;gap:6px;"; + + if (cat.cookies.length > 0) { + const arrow = document.createElement("span"); + arrow.textContent = "\u25B6"; + arrow.style.cssText = + `font-size:10px;color:${theme.secondary_text_body_color};cursor:pointer;transition:transform .2s;user-select:none;`; + nameRow.appendChild(arrow); + + const cookieDetails = document.createElement("div"); + cookieDetails.style.cssText = "display:none;margin-top:8px;"; + + const toggle = () => { + const isOpen = cookieDetails.style.display !== "none"; + cookieDetails.style.display = isOpen ? "none" : "block"; + arrow.style.transform = isOpen ? "rotate(0deg)" : "rotate(90deg)"; + }; + + nameRow.style.cursor = "pointer"; + nameRow.addEventListener("click", toggle); + + renderCookieDetails(cookieDetails, cat, theme); + info.appendChild(nameRow); + + if (cat.description) { + const desc = document.createElement("div"); + desc.textContent = cat.description; + desc.style.cssText = + `font-size:12px;color:${theme.secondary_text_body_color};margin-top:4px;line-height:1.4;`; + info.appendChild(desc); + } + + info.appendChild(cookieDetails); + } else { + info.appendChild(nameRow); + + if (cat.description) { + const desc = document.createElement("div"); + desc.textContent = cat.description; + desc.style.cssText = + `font-size:12px;color:${theme.secondary_text_body_color};margin-top:4px;line-height:1.4;`; + info.appendChild(desc); + } + } + + const name = document.createElement("span"); + name.textContent = cat.name; + name.style.cssText = `font-weight:500;font-size:14px;color:${theme.text_color};`; + nameRow.appendChild(name); + + row.appendChild(info); + + const toggleLabel = document.createElement("label"); + toggleLabel.style.cssText = + `position:relative;display:inline-block;width:44px;height:24px;flex-shrink:0;${cat.required ? "opacity:0.5;" : ""}`; + + const checkbox = document.createElement("input"); + checkbox.type = "checkbox"; + checkbox.checked = cat.required || (currentConsent[cat.id] ?? false); + checkbox.disabled = cat.required; + checkbox.style.cssText = "opacity:0;width:0;height:0;"; + checkbox.setAttribute("aria-label", cat.name); + checkbox.setAttribute("role", "switch"); + checkbox.setAttribute("aria-checked", String(checkbox.checked)); + + const slider = document.createElement("span"); + const updateSlider = () => { + slider.style.cssText = `position:absolute;cursor:${cat.required ? "not-allowed" : "pointer"};top:0;left:0;right:0;bottom:0;background:${checkbox.checked ? theme.primary_color : "#d1d5db"};border-radius:12px;transition:background .2s;`; + const knob = slider.querySelector("span") as HTMLSpanElement; + if (knob) { + knob.style.transform = checkbox.checked + ? "translateX(20px)" + : "translateX(0)"; + } + }; + + const knob = document.createElement("span"); + knob.style.cssText = + "position:absolute;height:18px;width:18px;left:3px;bottom:3px;background:white;border-radius:50%;transition:transform .2s;"; + slider.appendChild(knob); + + toggleLabel.appendChild(checkbox); + toggleLabel.appendChild(slider); + row.appendChild(toggleLabel); + section.appendChild(row); + panel.appendChild(section); + + updateSlider(); + checkbox.addEventListener("change", () => { + updateSlider(); + checkbox.setAttribute("aria-checked", String(checkbox.checked)); + }); + + checkboxes.push({ id: cat.id, checkbox }); + } + + const actions = document.createElement("div"); + actions.style.cssText = + "display:flex;gap:12px;margin-top:20px;"; + + const rejectBtn = document.createElement("button"); + rejectBtn.textContent = strings.rejectAll; + rejectBtn.style.cssText = + `flex:1;padding:12px;border:1px solid ${theme.border_color};border-radius:${theme.border_radius}px;background:${theme.background_color};color:${theme.text_color};font-size:14px;font-weight:500;cursor:pointer;font-family:${theme.font_family};`; + rejectBtn.addEventListener("click", onRejectAll); + actions.appendChild(rejectBtn); + + const saveBtn = document.createElement("button"); + saveBtn.textContent = strings.savePreferences; + saveBtn.style.cssText = + `flex:1;padding:12px;border:1px solid ${theme.border_color};border-radius:${theme.border_radius}px;background:${theme.background_color};color:${theme.text_color};font-size:14px;font-weight:500;cursor:pointer;font-family:${theme.font_family};`; + saveBtn.addEventListener("click", () => { + const choices: Record = {}; + for (const { id, checkbox } of checkboxes) { + choices[id] = checkbox.checked; + } + onSave(choices); + }); + actions.appendChild(saveBtn); + + const acceptBtn = document.createElement("button"); + acceptBtn.textContent = strings.acceptAll; + acceptBtn.style.cssText = + `flex:1;padding:12px;border:none;border-radius:${theme.border_radius}px;background:${theme.primary_color};color:${theme.primary_text_color};font-size:14px;font-weight:500;cursor:pointer;font-family:${theme.font_family};`; + acceptBtn.addEventListener("click", onAcceptAll); + actions.appendChild(acceptBtn); + + panel.appendChild(actions); + container.appendChild(panel); +} + +function renderCookieDetails( + container: HTMLElement, + cat: BannerCategory, + theme: ThemeConfig, +): void { + for (const cookie of cat.cookies) { + const item = document.createElement("div"); + item.style.cssText = + `padding:8px 0;border-bottom:1px solid ${theme.border_color};`; + + const fields: [string, string][] = [ + ["Cookie", cookie.name], + ["Duration", cookie.duration], + ["Description", cookie.description], + ]; + + for (const [label, value] of fields) { + if (!value) continue; + const fieldRow = document.createElement("div"); + fieldRow.style.cssText = + "display:flex;gap:12px;padding:2px 0;font-size:12px;line-height:1.4;"; + + const labelEl = document.createElement("span"); + labelEl.textContent = label; + labelEl.style.cssText = + `font-weight:600;min-width:80px;flex-shrink:0;color:${theme.text_color};`; + fieldRow.appendChild(labelEl); + + const valueEl = document.createElement("span"); + valueEl.textContent = value; + valueEl.style.cssText = + `color:${theme.secondary_text_body_color};`; + fieldRow.appendChild(valueEl); + + item.appendChild(fieldRow); + } + + container.appendChild(item); + } +} diff --git a/packages/cookie-banner/src/styled/revisit-renderer.ts b/packages/cookie-banner/src/styled/revisit-renderer.ts new file mode 100644 index 000000000..9d69352b2 --- /dev/null +++ b/packages/cookie-banner/src/styled/revisit-renderer.ts @@ -0,0 +1,51 @@ +// Copyright (c) 2025-2026 Probo Inc . +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR +// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +// PERFORMANCE OF THIS SOFTWARE. + +import type { ThemeConfig, WidgetStrings } from "../headless/types"; +import { defaultTheme } from "../headless/api"; + +export function renderRevisitIcon( + root: ShadowRoot, + onClick: () => void, + strings: WidgetStrings, + theme?: ThemeConfig, +): void { + const t = theme ?? defaultTheme; + + const positionCSS = t.revisit_position === "bottom-right" + ? "right: 20px;" + : "left: 20px;"; + + const style = document.createElement("style"); + style.textContent = ` + .probo-revisit { + position: fixed; bottom: 20px; ${positionCSS} z-index: 2147483646; + width: 44px; height: 44px; border-radius: 50%; + background: ${t.primary_color}; color: ${t.primary_text_color}; border: none; + cursor: pointer; display: flex; align-items: center; justify-content: center; + box-shadow: 0 2px 8px rgba(0,0,0,0.15); transition: transform 0.2s; + font-family: ${t.font_family}; + } + .probo-revisit:hover { transform: scale(1.1); } + .probo-revisit svg { width: 20px; height: 20px; } + `; + root.appendChild(style); + + const btn = document.createElement("button"); + btn.className = "probo-revisit"; + btn.title = strings.cookiePreferencesTooltip; + btn.innerHTML = ``; + btn.addEventListener("click", onClick); + root.appendChild(btn); +} diff --git a/packages/cookie-banner/src/styled/styled-banner.ts b/packages/cookie-banner/src/styled/styled-banner.ts new file mode 100644 index 000000000..34b83cc7c --- /dev/null +++ b/packages/cookie-banner/src/styled/styled-banner.ts @@ -0,0 +1,135 @@ +// Copyright (c) 2025-2026 Probo Inc . +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR +// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +// PERFORMANCE OF THIS SOFTWARE. + +import { ConsentManager } from "../headless/consent-manager"; +import type { BannerConfig, ConsentManagerConfig } from "../headless/types"; +import { renderBanner } from "./banner-renderer"; +import { + applyContextualPlaceholders, + removeAllPlaceholders, +} from "./placeholder-renderer"; +import { renderRevisitIcon } from "./revisit-renderer"; + +export class StyledBanner { + readonly manager: ConsentManager; + + constructor(config: ConsentManagerConfig) { + this.manager = new ConsentManager(config); + } + + async init(): Promise { + this.manager.onConsentRequired((config) => { + this.applyPlaceholders(config); + this.showBanner(config); + }); + + this.manager.onReady((config) => { + this.applyPlaceholders(config); + if (!this.manager.needsConsent()) { + this.showRevisitIcon(config); + } + }); + + this.manager.onGPCHandled((config) => { + this.applyPlaceholders(config); + // GPC auto-rejected: show revisit icon so user can still change. + this.showRevisitIcon(config); + }); + + this.manager.onConsentChange(() => { + const config = this.manager.getConfig(); + if (config) this.applyPlaceholders(config); + }); + + await this.manager.init(); + } + + show(): void { + const config = this.manager.getConfig(); + if (!config) return; + document.getElementById("probo-cookie-revisit")?.remove(); + this.showBanner(config); + } + + destroy(): void { + document.getElementById("probo-cookie-banner")?.remove(); + document.getElementById("probo-cookie-revisit")?.remove(); + removeAllPlaceholders(); + this.manager.destroy(); + } + + private applyPlaceholders(config: BannerConfig): void { + applyContextualPlaceholders( + this.manager.getConsents(), + (id) => config.categories.find((c) => c.id === id)?.name ?? id, + (categoryId) => this.manager.acceptCategory(categoryId), + this.manager.getStrings(), + config.theme, + ); + } + + private showBanner(config: BannerConfig): void { + document.getElementById("probo-cookie-banner")?.remove(); + + const host = document.createElement("div"); + host.id = "probo-cookie-banner"; + document.body.appendChild(host); + const shadow = host.attachShadow({ mode: "closed" }); + + const strings = this.manager.getStrings(); + + renderBanner( + shadow, + config, + this.manager.getConsents(), + { + onAcceptAll: () => { + this.manager.acceptAll(); + host.remove(); + this.showRevisitIcon(config); + }, + onRejectAll: () => { + this.manager.rejectAll(); + host.remove(); + this.showRevisitIcon(config); + }, + onCustomize: (choices) => { + this.manager.savePreferences(choices); + host.remove(); + this.showRevisitIcon(config); + }, + }, + strings, + ); + } + + private showRevisitIcon(config: BannerConfig): void { + document.getElementById("probo-cookie-revisit")?.remove(); + + const host = document.createElement("div"); + host.id = "probo-cookie-revisit"; + document.body.appendChild(host); + const shadow = host.attachShadow({ mode: "closed" }); + + renderRevisitIcon( + shadow, + () => { + host.remove(); + this.showBanner(config); + }, + this.manager.getStrings(), + config.theme, + ); + } +} diff --git a/packages/cookie-banner/src/styled/theme.ts b/packages/cookie-banner/src/styled/theme.ts new file mode 100644 index 000000000..d53c74516 --- /dev/null +++ b/packages/cookie-banner/src/styled/theme.ts @@ -0,0 +1,30 @@ +// Copyright (c) 2025-2026 Probo Inc . +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR +// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +// PERFORMANCE OF THIS SOFTWARE. + +import type { ThemeConfig } from "../headless/types"; + +export function themeToCSSVars(theme: ThemeConfig): string { + return ` + --probo-primary: ${theme.primary_color}; + --probo-primary-text: ${theme.primary_text_color}; + --probo-secondary: ${theme.secondary_color}; + --probo-secondary-text: ${theme.secondary_text_color}; + --probo-bg: ${theme.background_color}; + --probo-text: ${theme.text_color}; + --probo-text-secondary: ${theme.secondary_text_body_color}; + --probo-border: ${theme.border_color}; + --probo-font: ${theme.font_family}; + --probo-radius: ${theme.border_radius}px; + `; +} diff --git a/packages/cookie-banner/src/styled/widget-entry.ts b/packages/cookie-banner/src/styled/widget-entry.ts new file mode 100644 index 000000000..f670b973f --- /dev/null +++ b/packages/cookie-banner/src/styled/widget-entry.ts @@ -0,0 +1,109 @@ +// Copyright (c) 2025-2026 Probo Inc . +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR +// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +// PERFORMANCE OF THIS SOFTWARE. + +import type { + ConsentChangeCallback, + ConsentMode, +} from "../headless/types"; +import { StyledBanner } from "./styled-banner"; + +let _banner: StyledBanner | null = null; + +function getScriptInfo(): { + bannerId: string; + baseUrl: string; + lang: string; + consentMode: ConsentMode; +} | null { + const scripts = document.querySelectorAll( + 'script[data-banner-id]', + ); + const script = scripts[scripts.length - 1] as HTMLScriptElement | undefined; + if (!script) return null; + + const bannerId = script.getAttribute("data-banner-id"); + if (!bannerId) return null; + + const src = script.getAttribute("src"); + if (!src) return null; + + const lang = + script.getAttribute("data-lang") || + document.documentElement.lang || + "en"; + + const rawMode = script.getAttribute("data-consent-mode"); + const consentMode: ConsentMode = + rawMode === "opt-out" ? "opt-out" : "opt-in"; + + // Derive base URL: remove /widget.js from the script src + const baseUrl = src.replace(/\/widget\.js(\?.*)?$/, ""); + return { bannerId, baseUrl, lang, consentMode }; +} + +async function init(): Promise { + const info = getScriptInfo(); + if (!info) return; + + _banner = new StyledBanner({ + bannerId: info.bannerId, + baseUrl: info.baseUrl, + lang: info.lang, + consentMode: info.consentMode, + }); + + await _banner.init(); +} + +// --- Public API (exposed as window.ProboCookieBanner.*) --- + +export function show(): void { + _banner?.show(); +} + +export function getConsent(categoryId: string): boolean | null { + return _banner?.manager.getConsent(categoryId) ?? null; +} + +export function getConsents(): Record { + return _banner?.manager.getConsents() ?? {}; +} + +export function onConsentChange(cb: ConsentChangeCallback): void { + _banner?.manager.onConsentChange(cb); +} + +export function removeConsentChangeListener(cb: ConsentChangeCallback): void { + _banner?.manager.removeConsentChangeListener(cb); +} + +export function isGPCEnabled(): boolean { + return _banner?.manager.gpcEnabled ?? false; +} + +export function reset(): void { + if (_banner) { + document.getElementById("probo-cookie-revisit")?.remove(); + _banner.manager.reset(); + } +} + +// Initialize when DOM is ready +if (document.readyState === "loading") { + document.addEventListener("DOMContentLoaded", () => { + init(); + }); +} else { + init(); +} diff --git a/packages/cookie-banner/tsconfig.json b/packages/cookie-banner/tsconfig.json new file mode 100644 index 000000000..6b9029181 --- /dev/null +++ b/packages/cookie-banner/tsconfig.json @@ -0,0 +1,13 @@ +{ + "compilerOptions": { + "target": "ES2020", + "module": "ESNext", + "moduleResolution": "bundler", + "strict": true, + "noEmit": true, + "lib": ["ES2020", "DOM", "DOM.Iterable"], + "outDir": "./dist", + "rootDir": "./src" + }, + "include": ["src"] +} diff --git a/packages/cookie-banner/vite.config.ts b/packages/cookie-banner/vite.config.ts new file mode 100644 index 000000000..678eb785e --- /dev/null +++ b/packages/cookie-banner/vite.config.ts @@ -0,0 +1,33 @@ +// Copyright (c) 2025-2026 Probo Inc . +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR +// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +// PERFORMANCE OF THIS SOFTWARE. + +import { defineConfig } from "vite"; + +export default defineConfig({ + build: { + lib: { + entry: "src/styled/widget-entry.ts", + name: "ProboCookieBanner", + formats: ["iife"], + fileName: () => "cookie-banner.js", + }, + outDir: "dist", + minify: "terser", + rollupOptions: { + output: { + inlineDynamicImports: true, + }, + }, + }, +}); diff --git a/pkg/cmd/cookiebanner/category/category.go b/pkg/cmd/cookiebanner/category/category.go new file mode 100644 index 000000000..8c301ff47 --- /dev/null +++ b/pkg/cmd/cookiebanner/category/category.go @@ -0,0 +1,38 @@ +// Copyright (c) 2026 Probo Inc . +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR +// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +// PERFORMANCE OF THIS SOFTWARE. + +package category + +import ( + "github.com/spf13/cobra" + "go.probo.inc/probo/pkg/cmd/cmdutil" + "go.probo.inc/probo/pkg/cmd/cookiebanner/category/create" + "go.probo.inc/probo/pkg/cmd/cookiebanner/category/delete" + "go.probo.inc/probo/pkg/cmd/cookiebanner/category/list" + "go.probo.inc/probo/pkg/cmd/cookiebanner/category/update" +) + +func NewCmdCategory(f *cmdutil.Factory) *cobra.Command { + cmd := &cobra.Command{ + Use: "category ", + Short: "Manage cookie categories", + } + + cmd.AddCommand(list.NewCmdList(f)) + cmd.AddCommand(create.NewCmdCreate(f)) + cmd.AddCommand(update.NewCmdUpdate(f)) + cmd.AddCommand(delete.NewCmdDelete(f)) + + return cmd +} diff --git a/pkg/cmd/cookiebanner/category/create/create.go b/pkg/cmd/cookiebanner/category/create/create.go new file mode 100644 index 000000000..82cc924b1 --- /dev/null +++ b/pkg/cmd/cookiebanner/category/create/create.go @@ -0,0 +1,141 @@ +// Copyright (c) 2026 Probo Inc . +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR +// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +// PERFORMANCE OF THIS SOFTWARE. + +package create + +import ( + "encoding/json" + "fmt" + + "github.com/charmbracelet/huh" + "github.com/spf13/cobra" + "go.probo.inc/probo/pkg/cli/api" + "go.probo.inc/probo/pkg/cmd/cmdutil" +) + +const createMutation = ` +mutation($input: CreateCookieCategoryInput!) { + createCookieCategory(input: $input) { + cookieCategory { + id + name + rank + } + } +} +` + +type createResponse struct { + CreateCookieCategory struct { + CookieCategory struct { + ID string `json:"id"` + Name string `json:"name"` + Rank int `json:"rank"` + } `json:"cookieCategory"` + } `json:"createCookieCategory"` +} + +func NewCmdCreate(f *cmdutil.Factory) *cobra.Command { + var ( + flagBanner string + flagName string + flagDescription string + flagRank int + ) + + cmd := &cobra.Command{ + Use: "create", + Short: "Create a new cookie category", + Example: ` # Create a cookie category + prb cookie-banner category create --banner --name "Analytics" --description "Cookies used for analytics"`, + RunE: func(cmd *cobra.Command, args []string) error { + cfg, err := f.Config() + if err != nil { + return err + } + + host, hc, err := cfg.DefaultHost() + if err != nil { + return err + } + + client := api.NewClient( + host, + hc.Token, + "/api/console/v1/graphql", + cfg.HTTPTimeoutDuration(), + ) + + if f.IOStreams.IsInteractive() { + if flagName == "" { + err := huh.NewInput(). + Title("Category name"). + Value(&flagName). + Run() + if err != nil { + return err + } + } + } + + if flagName == "" { + return fmt.Errorf("name is required; pass --name or run interactively") + } + + input := map[string]any{ + "cookieBannerId": flagBanner, + "name": flagName, + } + + if flagDescription != "" { + input["description"] = flagDescription + } + if cmd.Flags().Changed("rank") { + input["rank"] = flagRank + } + + data, err := client.Do( + createMutation, + map[string]any{"input": input}, + ) + if err != nil { + return err + } + + var resp createResponse + if err := json.Unmarshal(data, &resp); err != nil { + return fmt.Errorf("cannot parse response: %w", err) + } + + c := resp.CreateCookieCategory.CookieCategory + _, _ = fmt.Fprintf( + f.IOStreams.Out, + "Created cookie category %s (%s)\n", + c.ID, + c.Name, + ) + + return nil + }, + } + + cmd.Flags().StringVar(&flagBanner, "banner", "", "Cookie banner ID (required)") + cmd.Flags().StringVar(&flagName, "name", "", "Category name (required)") + cmd.Flags().StringVar(&flagDescription, "description", "", "Category description") + cmd.Flags().IntVar(&flagRank, "rank", 0, "Display order rank") + + _ = cmd.MarkFlagRequired("banner") + + return cmd +} diff --git a/pkg/cmd/cookiebanner/category/delete/delete.go b/pkg/cmd/cookiebanner/category/delete/delete.go new file mode 100644 index 000000000..c5daa9cc0 --- /dev/null +++ b/pkg/cmd/cookiebanner/category/delete/delete.go @@ -0,0 +1,102 @@ +// Copyright (c) 2026 Probo Inc . +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR +// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +// PERFORMANCE OF THIS SOFTWARE. + +package delete + +import ( + "fmt" + + "github.com/charmbracelet/huh" + "github.com/spf13/cobra" + "go.probo.inc/probo/pkg/cli/api" + "go.probo.inc/probo/pkg/cmd/cmdutil" +) + +const deleteMutation = ` +mutation($input: DeleteCookieCategoryInput!) { + deleteCookieCategory(input: $input) { + deletedCookieCategoryId + } +} +` + +func NewCmdDelete(f *cmdutil.Factory) *cobra.Command { + var flagYes bool + + cmd := &cobra.Command{ + Use: "delete ", + Short: "Delete a cookie category", + Args: cobra.ExactArgs(1), + RunE: func(cmd *cobra.Command, args []string) error { + if !flagYes { + if !f.IOStreams.IsInteractive() { + return fmt.Errorf("cannot delete cookie category: confirmation required, use --yes to confirm") + } + + var confirmed bool + err := huh.NewConfirm(). + Title(fmt.Sprintf("Delete cookie category %s?", args[0])). + Value(&confirmed). + Run() + if err != nil { + return err + } + if !confirmed { + return nil + } + } + + cfg, err := f.Config() + if err != nil { + return err + } + + host, hc, err := cfg.DefaultHost() + if err != nil { + return err + } + + client := api.NewClient( + host, + hc.Token, + "/api/console/v1/graphql", + cfg.HTTPTimeoutDuration(), + ) + + _, err = client.Do( + deleteMutation, + map[string]any{ + "input": map[string]any{ + "id": args[0], + }, + }, + ) + if err != nil { + return err + } + + _, _ = fmt.Fprintf( + f.IOStreams.Out, + "Deleted cookie category %s\n", + args[0], + ) + + return nil + }, + } + + cmd.Flags().BoolVarP(&flagYes, "yes", "y", false, "Skip confirmation prompt") + + return cmd +} diff --git a/pkg/cmd/cookiebanner/category/list/list.go b/pkg/cmd/cookiebanner/category/list/list.go new file mode 100644 index 000000000..ed2ce771e --- /dev/null +++ b/pkg/cmd/cookiebanner/category/list/list.go @@ -0,0 +1,179 @@ +// Copyright (c) 2026 Probo Inc . +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR +// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +// PERFORMANCE OF THIS SOFTWARE. + +package list + +import ( + "encoding/json" + "fmt" + + "github.com/spf13/cobra" + "go.probo.inc/probo/pkg/cli/api" + "go.probo.inc/probo/pkg/cmd/cmdutil" +) + +const listQuery = ` +query($id: ID!, $first: Int, $after: CursorKey) { + node(id: $id) { + __typename + ... on CookieBanner { + categories(first: $first, after: $after) { + totalCount + edges { + node { + id + name + description + required + rank + } + } + pageInfo { + hasNextPage + endCursor + } + } + } + } +} +` + +type cookieCategory struct { + ID string `json:"id"` + Name string `json:"name"` + Description string `json:"description"` + Required bool `json:"required"` + Rank int `json:"rank"` +} + +func NewCmdList(f *cmdutil.Factory) *cobra.Command { + var ( + flagBanner string + flagLimit int + flagOutput *string + ) + + cmd := &cobra.Command{ + Use: "list", + Short: "List cookie categories for a banner", + Aliases: []string{"ls"}, + Example: ` # List categories for a cookie banner + prb cookie-banner category list --banner `, + Args: cobra.NoArgs, + RunE: func(cmd *cobra.Command, args []string) error { + if err := cmdutil.ValidateOutputFlag(flagOutput); err != nil { + return err + } + + cfg, err := f.Config() + if err != nil { + return err + } + + host, hc, err := cfg.DefaultHost() + if err != nil { + return err + } + + client := api.NewClient( + host, + hc.Token, + "/api/console/v1/graphql", + cfg.HTTPTimeoutDuration(), + ) + + if flagBanner == "" { + return fmt.Errorf("banner is required; pass --banner") + } + + variables := map[string]any{ + "id": flagBanner, + } + + categories, totalCount, err := api.Paginate( + client, + listQuery, + variables, + flagLimit, + func(data json.RawMessage) (*api.Connection[cookieCategory], error) { + var resp struct { + Node *struct { + Typename string `json:"__typename"` + Categories api.Connection[cookieCategory] `json:"categories"` + } `json:"node"` + } + if err := json.Unmarshal(data, &resp); err != nil { + return nil, err + } + if resp.Node == nil { + return nil, fmt.Errorf("cookie banner %s not found", flagBanner) + } + if resp.Node.Typename != "CookieBanner" { + return nil, fmt.Errorf("expected CookieBanner node, got %s", resp.Node.Typename) + } + return &resp.Node.Categories, nil + }, + ) + if err != nil { + return err + } + + if *flagOutput == cmdutil.OutputJSON { + return cmdutil.PrintJSON(f.IOStreams.Out, categories) + } + + if len(categories) == 0 { + _, _ = fmt.Fprintln(f.IOStreams.Out, "No cookie categories found.") + return nil + } + + rows := make([][]string, 0, len(categories)) + for _, c := range categories { + required := "no" + if c.Required { + required = "yes" + } + rows = append(rows, []string{ + c.ID, + c.Name, + required, + fmt.Sprintf("%d", c.Rank), + }) + } + + t := cmdutil.NewTable("ID", "NAME", "REQUIRED", "RANK").Rows(rows...) + + _, _ = fmt.Fprintln(f.IOStreams.Out, t) + + if totalCount > len(categories) { + _, _ = fmt.Fprintf( + f.IOStreams.ErrOut, + "\nShowing %d of %d categories\n", + len(categories), + totalCount, + ) + } + + return nil + }, + } + + cmd.Flags().StringVar(&flagBanner, "banner", "", "Cookie banner ID (required)") + cmd.Flags().IntVarP(&flagLimit, "limit", "L", 30, "Maximum number of categories to list") + flagOutput = cmdutil.AddOutputFlag(cmd) + + _ = cmd.MarkFlagRequired("banner") + + return cmd +} diff --git a/pkg/cmd/cookiebanner/category/update/update.go b/pkg/cmd/cookiebanner/category/update/update.go new file mode 100644 index 000000000..f38bb6853 --- /dev/null +++ b/pkg/cmd/cookiebanner/category/update/update.go @@ -0,0 +1,125 @@ +// Copyright (c) 2026 Probo Inc . +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR +// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +// PERFORMANCE OF THIS SOFTWARE. + +package update + +import ( + "encoding/json" + "fmt" + + "github.com/spf13/cobra" + "go.probo.inc/probo/pkg/cli/api" + "go.probo.inc/probo/pkg/cmd/cmdutil" +) + +const updateMutation = ` +mutation($input: UpdateCookieCategoryInput!) { + updateCookieCategory(input: $input) { + cookieCategory { + id + name + rank + } + } +} +` + +type updateResponse struct { + UpdateCookieCategory struct { + CookieCategory struct { + ID string `json:"id"` + Name string `json:"name"` + Rank int `json:"rank"` + } `json:"cookieCategory"` + } `json:"updateCookieCategory"` +} + +func NewCmdUpdate(f *cmdutil.Factory) *cobra.Command { + var ( + flagName string + flagDescription string + flagRank int + ) + + cmd := &cobra.Command{ + Use: "update ", + Short: "Update a cookie category", + Args: cobra.ExactArgs(1), + RunE: func(cmd *cobra.Command, args []string) error { + cfg, err := f.Config() + if err != nil { + return err + } + + host, hc, err := cfg.DefaultHost() + if err != nil { + return err + } + + client := api.NewClient( + host, + hc.Token, + "/api/console/v1/graphql", + cfg.HTTPTimeoutDuration(), + ) + + input := map[string]any{ + "id": args[0], + } + + if cmd.Flags().Changed("name") { + input["name"] = flagName + } + if cmd.Flags().Changed("description") { + input["description"] = flagDescription + } + if cmd.Flags().Changed("rank") { + input["rank"] = flagRank + } + + if len(input) == 1 { + return fmt.Errorf("at least one field must be specified for update") + } + + data, err := client.Do( + updateMutation, + map[string]any{"input": input}, + ) + if err != nil { + return err + } + + var resp updateResponse + if err := json.Unmarshal(data, &resp); err != nil { + return fmt.Errorf("cannot parse response: %w", err) + } + + c := resp.UpdateCookieCategory.CookieCategory + _, _ = fmt.Fprintf( + f.IOStreams.Out, + "Updated cookie category %s (%s)\n", + c.ID, + c.Name, + ) + + return nil + }, + } + + cmd.Flags().StringVar(&flagName, "name", "", "Category name") + cmd.Flags().StringVar(&flagDescription, "description", "", "Category description") + cmd.Flags().IntVar(&flagRank, "rank", 0, "Display order rank") + + return cmd +} diff --git a/pkg/cmd/cookiebanner/consent/consent.go b/pkg/cmd/cookiebanner/consent/consent.go new file mode 100644 index 000000000..c9ad5907f --- /dev/null +++ b/pkg/cmd/cookiebanner/consent/consent.go @@ -0,0 +1,32 @@ +// Copyright (c) 2026 Probo Inc . +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR +// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +// PERFORMANCE OF THIS SOFTWARE. + +package consent + +import ( + "github.com/spf13/cobra" + "go.probo.inc/probo/pkg/cmd/cmdutil" + "go.probo.inc/probo/pkg/cmd/cookiebanner/consent/list" +) + +func NewCmdConsent(f *cmdutil.Factory) *cobra.Command { + cmd := &cobra.Command{ + Use: "consent ", + Short: "Manage consent records", + } + + cmd.AddCommand(list.NewCmdList(f)) + + return cmd +} diff --git a/pkg/cmd/cookiebanner/consent/list/list.go b/pkg/cmd/cookiebanner/consent/list/list.go new file mode 100644 index 000000000..019f7a5cc --- /dev/null +++ b/pkg/cmd/cookiebanner/consent/list/list.go @@ -0,0 +1,176 @@ +// Copyright (c) 2026 Probo Inc . +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR +// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +// PERFORMANCE OF THIS SOFTWARE. + +package list + +import ( + "encoding/json" + "fmt" + + "github.com/spf13/cobra" + "go.probo.inc/probo/pkg/cli/api" + "go.probo.inc/probo/pkg/cmd/cmdutil" +) + +const listQuery = ` +query($id: ID!, $first: Int, $after: CursorKey) { + node(id: $id) { + __typename + ... on CookieBanner { + consentRecords(first: $first, after: $after) { + totalCount + edges { + node { + id + visitorId + action + bannerVersion + createdAt + } + } + pageInfo { + hasNextPage + endCursor + } + } + } + } +} +` + +type consentRecord struct { + ID string `json:"id"` + VisitorID string `json:"visitorId"` + Action string `json:"action"` + BannerVersion int `json:"bannerVersion"` + CreatedAt string `json:"createdAt"` +} + +func NewCmdList(f *cmdutil.Factory) *cobra.Command { + var ( + flagBanner string + flagLimit int + flagOutput *string + ) + + cmd := &cobra.Command{ + Use: "list", + Short: "List consent records for a banner", + Aliases: []string{"ls"}, + Example: ` # List consent records for a cookie banner + prb cookie-banner consent list --banner `, + Args: cobra.NoArgs, + RunE: func(cmd *cobra.Command, args []string) error { + if err := cmdutil.ValidateOutputFlag(flagOutput); err != nil { + return err + } + + cfg, err := f.Config() + if err != nil { + return err + } + + host, hc, err := cfg.DefaultHost() + if err != nil { + return err + } + + client := api.NewClient( + host, + hc.Token, + "/api/console/v1/graphql", + cfg.HTTPTimeoutDuration(), + ) + + if flagBanner == "" { + return fmt.Errorf("banner is required; pass --banner") + } + + variables := map[string]any{ + "id": flagBanner, + } + + records, totalCount, err := api.Paginate( + client, + listQuery, + variables, + flagLimit, + func(data json.RawMessage) (*api.Connection[consentRecord], error) { + var resp struct { + Node *struct { + Typename string `json:"__typename"` + ConsentRecords api.Connection[consentRecord] `json:"consentRecords"` + } `json:"node"` + } + if err := json.Unmarshal(data, &resp); err != nil { + return nil, err + } + if resp.Node == nil { + return nil, fmt.Errorf("cookie banner %s not found", flagBanner) + } + if resp.Node.Typename != "CookieBanner" { + return nil, fmt.Errorf("expected CookieBanner node, got %s", resp.Node.Typename) + } + return &resp.Node.ConsentRecords, nil + }, + ) + if err != nil { + return err + } + + if *flagOutput == cmdutil.OutputJSON { + return cmdutil.PrintJSON(f.IOStreams.Out, records) + } + + if len(records) == 0 { + _, _ = fmt.Fprintln(f.IOStreams.Out, "No consent records found.") + return nil + } + + rows := make([][]string, 0, len(records)) + for _, r := range records { + rows = append(rows, []string{ + r.ID, + r.VisitorID, + r.Action, + fmt.Sprintf("%d", r.BannerVersion), + cmdutil.FormatTime(r.CreatedAt), + }) + } + + t := cmdutil.NewTable("ID", "VISITOR", "ACTION", "VERSION", "CREATED").Rows(rows...) + + _, _ = fmt.Fprintln(f.IOStreams.Out, t) + + if totalCount > len(records) { + _, _ = fmt.Fprintf( + f.IOStreams.ErrOut, + "\nShowing %d of %d consent records\n", + len(records), + totalCount, + ) + } + + return nil + }, + } + + cmd.Flags().StringVar(&flagBanner, "banner", "", "Cookie banner ID (required)") + cmd.Flags().IntVarP(&flagLimit, "limit", "L", 30, "Maximum number of records to list") + flagOutput = cmdutil.AddOutputFlag(cmd) + + _ = cmd.MarkFlagRequired("banner") + + return cmd +} diff --git a/pkg/cmd/cookiebanner/cookiebanner.go b/pkg/cmd/cookiebanner/cookiebanner.go new file mode 100644 index 000000000..fda607c34 --- /dev/null +++ b/pkg/cmd/cookiebanner/cookiebanner.go @@ -0,0 +1,50 @@ +// Copyright (c) 2026 Probo Inc . +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR +// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +// PERFORMANCE OF THIS SOFTWARE. + +package cookiebanner + +import ( + "github.com/spf13/cobra" + "go.probo.inc/probo/pkg/cmd/cmdutil" + "go.probo.inc/probo/pkg/cmd/cookiebanner/category" + "go.probo.inc/probo/pkg/cmd/cookiebanner/consent" + "go.probo.inc/probo/pkg/cmd/cookiebanner/create" + "go.probo.inc/probo/pkg/cmd/cookiebanner/delete" + "go.probo.inc/probo/pkg/cmd/cookiebanner/disable" + "go.probo.inc/probo/pkg/cmd/cookiebanner/list" + "go.probo.inc/probo/pkg/cmd/cookiebanner/provider" + "go.probo.inc/probo/pkg/cmd/cookiebanner/publish" + "go.probo.inc/probo/pkg/cmd/cookiebanner/update" + "go.probo.inc/probo/pkg/cmd/cookiebanner/view" +) + +func NewCmdCookieBanner(f *cmdutil.Factory) *cobra.Command { + cmd := &cobra.Command{ + Use: "cookie-banner ", + Short: "Manage cookie banners", + } + + cmd.AddCommand(list.NewCmdList(f)) + cmd.AddCommand(create.NewCmdCreate(f)) + cmd.AddCommand(view.NewCmdView(f)) + cmd.AddCommand(update.NewCmdUpdate(f)) + cmd.AddCommand(delete.NewCmdDelete(f)) + cmd.AddCommand(publish.NewCmdPublish(f)) + cmd.AddCommand(disable.NewCmdDisable(f)) + cmd.AddCommand(category.NewCmdCategory(f)) + cmd.AddCommand(consent.NewCmdConsent(f)) + cmd.AddCommand(provider.NewCmdProvider(f)) + + return cmd +} diff --git a/pkg/cmd/cookiebanner/create/create.go b/pkg/cmd/cookiebanner/create/create.go new file mode 100644 index 000000000..516bd9144 --- /dev/null +++ b/pkg/cmd/cookiebanner/create/create.go @@ -0,0 +1,157 @@ +// Copyright (c) 2026 Probo Inc . +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR +// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +// PERFORMANCE OF THIS SOFTWARE. + +package create + +import ( + "encoding/json" + "fmt" + + "github.com/charmbracelet/huh" + "github.com/spf13/cobra" + "go.probo.inc/probo/pkg/cli/api" + "go.probo.inc/probo/pkg/cmd/cmdutil" +) + +const createMutation = ` +mutation($input: CreateCookieBannerInput!) { + createCookieBanner(input: $input) { + cookieBanner { + id + name + domain + state + } + } +} +` + +type createResponse struct { + CreateCookieBanner struct { + CookieBanner struct { + ID string `json:"id"` + Name string `json:"name"` + Domain string `json:"domain"` + State string `json:"state"` + } `json:"cookieBanner"` + } `json:"createCookieBanner"` +} + +func NewCmdCreate(f *cmdutil.Factory) *cobra.Command { + var ( + flagOrg string + flagName string + flagDomain string + ) + + cmd := &cobra.Command{ + Use: "create", + Short: "Create a new cookie banner", + Example: ` # Create a cookie banner interactively + prb cookie-banner create + + # Create a cookie banner non-interactively + prb cookie-banner create --name "Main Site" --domain "example.com"`, + RunE: func(cmd *cobra.Command, args []string) error { + cfg, err := f.Config() + if err != nil { + return err + } + + host, hc, err := cfg.DefaultHost() + if err != nil { + return err + } + + client := api.NewClient( + host, + hc.Token, + "/api/console/v1/graphql", + cfg.HTTPTimeoutDuration(), + ) + + if flagOrg == "" { + flagOrg = hc.Organization + } + + if flagOrg == "" { + return fmt.Errorf("organization is required; pass --org or set a default with 'prb auth login'") + } + + if f.IOStreams.IsInteractive() { + if flagName == "" { + err := huh.NewInput(). + Title("Banner name"). + Value(&flagName). + Run() + if err != nil { + return err + } + } + + if flagDomain == "" { + err := huh.NewInput(). + Title("Domain"). + Value(&flagDomain). + Run() + if err != nil { + return err + } + } + } + + if flagName == "" { + return fmt.Errorf("name is required; pass --name or run interactively") + } + + input := map[string]any{ + "organizationId": flagOrg, + "name": flagName, + } + + if flagDomain != "" { + input["domain"] = flagDomain + } + + data, err := client.Do( + createMutation, + map[string]any{"input": input}, + ) + if err != nil { + return err + } + + var resp createResponse + if err := json.Unmarshal(data, &resp); err != nil { + return fmt.Errorf("cannot parse response: %w", err) + } + + b := resp.CreateCookieBanner.CookieBanner + _, _ = fmt.Fprintf( + f.IOStreams.Out, + "Created cookie banner %s (%s)\n", + b.ID, + b.Name, + ) + + return nil + }, + } + + cmd.Flags().StringVar(&flagOrg, "org", "", "Organization ID") + cmd.Flags().StringVar(&flagName, "name", "", "Banner name (required)") + cmd.Flags().StringVar(&flagDomain, "domain", "", "Domain the banner is deployed on") + + return cmd +} diff --git a/pkg/cmd/cookiebanner/delete/delete.go b/pkg/cmd/cookiebanner/delete/delete.go new file mode 100644 index 000000000..09fbe874b --- /dev/null +++ b/pkg/cmd/cookiebanner/delete/delete.go @@ -0,0 +1,102 @@ +// Copyright (c) 2026 Probo Inc . +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR +// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +// PERFORMANCE OF THIS SOFTWARE. + +package delete + +import ( + "fmt" + + "github.com/charmbracelet/huh" + "github.com/spf13/cobra" + "go.probo.inc/probo/pkg/cli/api" + "go.probo.inc/probo/pkg/cmd/cmdutil" +) + +const deleteMutation = ` +mutation($input: DeleteCookieBannerInput!) { + deleteCookieBanner(input: $input) { + deletedCookieBannerId + } +} +` + +func NewCmdDelete(f *cmdutil.Factory) *cobra.Command { + var flagYes bool + + cmd := &cobra.Command{ + Use: "delete ", + Short: "Delete a cookie banner", + Args: cobra.ExactArgs(1), + RunE: func(cmd *cobra.Command, args []string) error { + if !flagYes { + if !f.IOStreams.IsInteractive() { + return fmt.Errorf("cannot delete cookie banner: confirmation required, use --yes to confirm") + } + + var confirmed bool + err := huh.NewConfirm(). + Title(fmt.Sprintf("Delete cookie banner %s?", args[0])). + Value(&confirmed). + Run() + if err != nil { + return err + } + if !confirmed { + return nil + } + } + + cfg, err := f.Config() + if err != nil { + return err + } + + host, hc, err := cfg.DefaultHost() + if err != nil { + return err + } + + client := api.NewClient( + host, + hc.Token, + "/api/console/v1/graphql", + cfg.HTTPTimeoutDuration(), + ) + + _, err = client.Do( + deleteMutation, + map[string]any{ + "input": map[string]any{ + "id": args[0], + }, + }, + ) + if err != nil { + return err + } + + _, _ = fmt.Fprintf( + f.IOStreams.Out, + "Deleted cookie banner %s\n", + args[0], + ) + + return nil + }, + } + + cmd.Flags().BoolVarP(&flagYes, "yes", "y", false, "Skip confirmation prompt") + + return cmd +} diff --git a/pkg/cmd/cookiebanner/disable/disable.go b/pkg/cmd/cookiebanner/disable/disable.go new file mode 100644 index 000000000..605d83f21 --- /dev/null +++ b/pkg/cmd/cookiebanner/disable/disable.go @@ -0,0 +1,101 @@ +// Copyright (c) 2026 Probo Inc . +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR +// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +// PERFORMANCE OF THIS SOFTWARE. + +package disable + +import ( + "encoding/json" + "fmt" + + "github.com/spf13/cobra" + "go.probo.inc/probo/pkg/cli/api" + "go.probo.inc/probo/pkg/cmd/cmdutil" +) + +const disableMutation = ` +mutation($input: DisableCookieBannerInput!) { + disableCookieBanner(input: $input) { + cookieBanner { + id + name + state + } + } +} +` + +type disableResponse struct { + DisableCookieBanner struct { + CookieBanner struct { + ID string `json:"id"` + Name string `json:"name"` + State string `json:"state"` + } `json:"cookieBanner"` + } `json:"disableCookieBanner"` +} + +func NewCmdDisable(f *cmdutil.Factory) *cobra.Command { + cmd := &cobra.Command{ + Use: "disable ", + Short: "Disable a cookie banner", + Args: cobra.ExactArgs(1), + RunE: func(cmd *cobra.Command, args []string) error { + cfg, err := f.Config() + if err != nil { + return err + } + + host, hc, err := cfg.DefaultHost() + if err != nil { + return err + } + + client := api.NewClient( + host, + hc.Token, + "/api/console/v1/graphql", + cfg.HTTPTimeoutDuration(), + ) + + data, err := client.Do( + disableMutation, + map[string]any{ + "input": map[string]any{ + "id": args[0], + }, + }, + ) + if err != nil { + return err + } + + var resp disableResponse + if err := json.Unmarshal(data, &resp); err != nil { + return fmt.Errorf("cannot parse response: %w", err) + } + + b := resp.DisableCookieBanner.CookieBanner + _, _ = fmt.Fprintf( + f.IOStreams.Out, + "Disabled cookie banner %s (%s)\n", + b.ID, + b.Name, + ) + + return nil + }, + } + + return cmd +} diff --git a/pkg/cmd/cookiebanner/list/list.go b/pkg/cmd/cookiebanner/list/list.go new file mode 100644 index 000000000..e0df18b61 --- /dev/null +++ b/pkg/cmd/cookiebanner/list/list.go @@ -0,0 +1,195 @@ +// Copyright (c) 2026 Probo Inc . +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR +// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +// PERFORMANCE OF THIS SOFTWARE. + +package list + +import ( + "encoding/json" + "fmt" + + "github.com/spf13/cobra" + "go.probo.inc/probo/pkg/cli/api" + "go.probo.inc/probo/pkg/cmd/cmdutil" +) + +const listQuery = ` +query($id: ID!, $first: Int, $after: CursorKey, $orderBy: CookieBannerOrder) { + node(id: $id) { + __typename + ... on Organization { + cookieBanners(first: $first, after: $after, orderBy: $orderBy) { + totalCount + edges { + node { + id + name + domain + state + version + } + } + pageInfo { + hasNextPage + endCursor + } + } + } + } +} +` + +type cookieBanner struct { + ID string `json:"id"` + Name string `json:"name"` + Domain string `json:"domain"` + State string `json:"state"` + Version int `json:"version"` +} + +func NewCmdList(f *cmdutil.Factory) *cobra.Command { + var ( + flagOrg string + flagLimit int + flagOrderBy string + flagOrderDir string + flagOutput *string + ) + + cmd := &cobra.Command{ + Use: "list", + Short: "List cookie banners in an organization", + Aliases: []string{"ls"}, + Example: ` # List cookie banners in the default organization + prb cookie-banner list + + # List as JSON + prb cookie-banner ls --output json`, + Args: cobra.NoArgs, + RunE: func(cmd *cobra.Command, args []string) error { + if err := cmdutil.ValidateOutputFlag(flagOutput); err != nil { + return err + } + + cfg, err := f.Config() + if err != nil { + return err + } + + host, hc, err := cfg.DefaultHost() + if err != nil { + return err + } + + client := api.NewClient( + host, + hc.Token, + "/api/console/v1/graphql", + cfg.HTTPTimeoutDuration(), + ) + + if flagOrg == "" { + flagOrg = hc.Organization + } + + if flagOrg == "" { + return fmt.Errorf("organization is required; pass --org or set a default with 'prb auth login'") + } + + variables := map[string]any{ + "id": flagOrg, + } + + if flagOrderBy != "" { + if err := cmdutil.ValidateEnum("order-by", flagOrderBy, []string{"CREATED_AT"}); err != nil { + return err + } + variables["orderBy"] = map[string]any{ + "field": flagOrderBy, + "direction": flagOrderDir, + } + } + + banners, totalCount, err := api.Paginate( + client, + listQuery, + variables, + flagLimit, + func(data json.RawMessage) (*api.Connection[cookieBanner], error) { + var resp struct { + Node *struct { + Typename string `json:"__typename"` + CookieBanners api.Connection[cookieBanner] `json:"cookieBanners"` + } `json:"node"` + } + if err := json.Unmarshal(data, &resp); err != nil { + return nil, err + } + if resp.Node == nil { + return nil, fmt.Errorf("organization %s not found", flagOrg) + } + if resp.Node.Typename != "Organization" { + return nil, fmt.Errorf("expected Organization node, got %s", resp.Node.Typename) + } + return &resp.Node.CookieBanners, nil + }, + ) + if err != nil { + return err + } + + if *flagOutput == cmdutil.OutputJSON { + return cmdutil.PrintJSON(f.IOStreams.Out, banners) + } + + if len(banners) == 0 { + _, _ = fmt.Fprintln(f.IOStreams.Out, "No cookie banners found.") + return nil + } + + rows := make([][]string, 0, len(banners)) + for _, b := range banners { + rows = append(rows, []string{ + b.ID, + b.Name, + b.Domain, + b.State, + fmt.Sprintf("%d", b.Version), + }) + } + + t := cmdutil.NewTable("ID", "NAME", "DOMAIN", "STATE", "VERSION").Rows(rows...) + + _, _ = fmt.Fprintln(f.IOStreams.Out, t) + + if totalCount > len(banners) { + _, _ = fmt.Fprintf( + f.IOStreams.ErrOut, + "\nShowing %d of %d cookie banners\n", + len(banners), + totalCount, + ) + } + + return nil + }, + } + + cmd.Flags().StringVar(&flagOrg, "org", "", "Organization ID") + cmd.Flags().IntVarP(&flagLimit, "limit", "L", 30, "Maximum number of cookie banners to list") + cmd.Flags().StringVar(&flagOrderBy, "order-by", "", "Order by field (CREATED_AT)") + cmd.Flags().StringVar(&flagOrderDir, "order-direction", "DESC", "Sort direction (ASC, DESC)") + flagOutput = cmdutil.AddOutputFlag(cmd) + + return cmd +} diff --git a/pkg/cmd/cookiebanner/provider/add/add.go b/pkg/cmd/cookiebanner/provider/add/add.go new file mode 100644 index 000000000..66dc6e0d9 --- /dev/null +++ b/pkg/cmd/cookiebanner/provider/add/add.go @@ -0,0 +1,118 @@ +// Copyright (c) 2026 Probo Inc . +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR +// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +// PERFORMANCE OF THIS SOFTWARE. + +package add + +import ( + "encoding/json" + "fmt" + + "github.com/spf13/cobra" + "go.probo.inc/probo/pkg/cli/api" + "go.probo.inc/probo/pkg/cmd/cmdutil" +) + +const addMutation = ` +mutation($input: AddCookiesFromProviderInput!) { + addCookiesFromProvider(input: $input) { + cookieCategory { + id + name + cookies { + name + } + } + } +} +` + +func NewCmdAdd(f *cmdutil.Factory) *cobra.Command { + var ( + flagCategoryID string + flagProvider string + ) + + cmd := &cobra.Command{ + Use: "add", + Short: "Add cookies from a known provider to a category", + Example: ` # Add Google Analytics cookies to a category + prb cookie-banner provider add --category-id --provider google-analytics`, + Args: cobra.NoArgs, + RunE: func(cmd *cobra.Command, args []string) error { + cfg, err := f.Config() + if err != nil { + return err + } + + host, hc, err := cfg.DefaultHost() + if err != nil { + return err + } + + client := api.NewClient( + host, + hc.Token, + "/api/console/v1/graphql", + cfg.HTTPTimeoutDuration(), + ) + + variables := map[string]any{ + "input": map[string]any{ + "cookieCategoryId": flagCategoryID, + "providerKey": flagProvider, + }, + } + + data, err := client.Do(addMutation, variables) + if err != nil { + return err + } + + var resp struct { + AddCookiesFromProvider struct { + CookieCategory struct { + ID string `json:"id"` + Name string `json:"name"` + Cookies []struct { + Name string `json:"name"` + } `json:"cookies"` + } `json:"cookieCategory"` + } `json:"addCookiesFromProvider"` + } + if err := json.Unmarshal(data, &resp); err != nil { + return fmt.Errorf("cannot parse response: %w", err) + } + + cat := resp.AddCookiesFromProvider.CookieCategory + _, _ = fmt.Fprintf( + f.IOStreams.Out, + "Added cookies from %s to category %s (%s), now has %d cookies\n", + flagProvider, + cat.Name, + cat.ID, + len(cat.Cookies), + ) + + return nil + }, + } + + cmd.Flags().StringVar(&flagCategoryID, "category-id", "", "Cookie category ID (required)") + cmd.Flags().StringVar(&flagProvider, "provider", "", "Provider key (required)") + + _ = cmd.MarkFlagRequired("category-id") + _ = cmd.MarkFlagRequired("provider") + + return cmd +} diff --git a/pkg/cmd/cookiebanner/provider/list/list.go b/pkg/cmd/cookiebanner/provider/list/list.go new file mode 100644 index 000000000..bd53a8e67 --- /dev/null +++ b/pkg/cmd/cookiebanner/provider/list/list.go @@ -0,0 +1,134 @@ +// Copyright (c) 2026 Probo Inc . +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR +// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +// PERFORMANCE OF THIS SOFTWARE. + +package list + +import ( + "encoding/json" + "fmt" + + "github.com/spf13/cobra" + "go.probo.inc/probo/pkg/cli/api" + "go.probo.inc/probo/pkg/cmd/cmdutil" +) + +const listQuery = ` +query($category: String) { + cookieProviders(category: $category) { + key + name + category + cookies { + name + } + } +} +` + +type cookieProvider struct { + Key string `json:"key"` + Name string `json:"name"` + Category string `json:"category"` + Cookies []struct { + Name string `json:"name"` + } `json:"cookies"` +} + +func NewCmdList(f *cmdutil.Factory) *cobra.Command { + var ( + flagCategory string + flagOutput *string + ) + + cmd := &cobra.Command{ + Use: "list", + Short: "List known cookie providers", + Aliases: []string{"ls"}, + Example: ` # List all cookie providers + prb cookie-banner provider list + + # List only analytics providers + prb cookie-banner provider list --category Analytics`, + Args: cobra.NoArgs, + RunE: func(cmd *cobra.Command, args []string) error { + if err := cmdutil.ValidateOutputFlag(flagOutput); err != nil { + return err + } + + cfg, err := f.Config() + if err != nil { + return err + } + + host, hc, err := cfg.DefaultHost() + if err != nil { + return err + } + + client := api.NewClient( + host, + hc.Token, + "/api/console/v1/graphql", + cfg.HTTPTimeoutDuration(), + ) + + variables := map[string]any{} + if flagCategory != "" { + variables["category"] = flagCategory + } + + data, err := client.Do(listQuery, variables) + if err != nil { + return err + } + + var resp struct { + CookieProviders []cookieProvider `json:"cookieProviders"` + } + if err := json.Unmarshal(data, &resp); err != nil { + return fmt.Errorf("cannot parse response: %w", err) + } + + if *flagOutput == cmdutil.OutputJSON { + return cmdutil.PrintJSON(f.IOStreams.Out, resp.CookieProviders) + } + + if len(resp.CookieProviders) == 0 { + _, _ = fmt.Fprintln(f.IOStreams.Out, "No cookie providers found.") + return nil + } + + rows := make([][]string, 0, len(resp.CookieProviders)) + for _, p := range resp.CookieProviders { + rows = append(rows, []string{ + p.Key, + p.Name, + p.Category, + fmt.Sprintf("%d", len(p.Cookies)), + }) + } + + t := cmdutil.NewTable("KEY", "NAME", "CATEGORY", "COOKIES").Rows(rows...) + + _, _ = fmt.Fprintln(f.IOStreams.Out, t) + + return nil + }, + } + + cmd.Flags().StringVar(&flagCategory, "category", "", "Filter by category (Necessary, Analytics, Marketing, Preferences)") + flagOutput = cmdutil.AddOutputFlag(cmd) + + return cmd +} diff --git a/pkg/cmd/cookiebanner/provider/provider.go b/pkg/cmd/cookiebanner/provider/provider.go new file mode 100644 index 000000000..efb5186fc --- /dev/null +++ b/pkg/cmd/cookiebanner/provider/provider.go @@ -0,0 +1,36 @@ +// Copyright (c) 2026 Probo Inc . +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR +// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +// PERFORMANCE OF THIS SOFTWARE. + +package provider + +import ( + "github.com/spf13/cobra" + "go.probo.inc/probo/pkg/cmd/cmdutil" + "go.probo.inc/probo/pkg/cmd/cookiebanner/provider/add" + "go.probo.inc/probo/pkg/cmd/cookiebanner/provider/list" + "go.probo.inc/probo/pkg/cmd/cookiebanner/provider/view" +) + +func NewCmdProvider(f *cmdutil.Factory) *cobra.Command { + cmd := &cobra.Command{ + Use: "provider ", + Short: "Browse the built-in cookie provider library", + } + + cmd.AddCommand(list.NewCmdList(f)) + cmd.AddCommand(view.NewCmdView(f)) + cmd.AddCommand(add.NewCmdAdd(f)) + + return cmd +} diff --git a/pkg/cmd/cookiebanner/provider/view/view.go b/pkg/cmd/cookiebanner/provider/view/view.go new file mode 100644 index 000000000..a08f07adf --- /dev/null +++ b/pkg/cmd/cookiebanner/provider/view/view.go @@ -0,0 +1,146 @@ +// Copyright (c) 2026 Probo Inc . +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR +// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +// PERFORMANCE OF THIS SOFTWARE. + +package view + +import ( + "encoding/json" + "fmt" + + "github.com/charmbracelet/lipgloss" + "github.com/spf13/cobra" + "go.probo.inc/probo/pkg/cli/api" + "go.probo.inc/probo/pkg/cmd/cmdutil" +) + +const viewQuery = ` +query($key: String!) { + cookieProvider(key: $key) { + key + name + description + category + websiteUrl + cookies { + name + duration + description + } + } +} +` + +type cookieProvider struct { + Key string `json:"key"` + Name string `json:"name"` + Description string `json:"description"` + Category string `json:"category"` + WebsiteURL string `json:"websiteUrl"` + Cookies []struct { + Name string `json:"name"` + Duration string `json:"duration"` + Description string `json:"description"` + } `json:"cookies"` +} + +func NewCmdView(f *cmdutil.Factory) *cobra.Command { + var flagOutput *string + + cmd := &cobra.Command{ + Use: "view ", + Short: "View a cookie provider's details", + Example: ` # View Google Analytics provider + prb cookie-banner provider view google-analytics`, + Args: cobra.ExactArgs(1), + RunE: func(cmd *cobra.Command, args []string) error { + if err := cmdutil.ValidateOutputFlag(flagOutput); err != nil { + return err + } + + cfg, err := f.Config() + if err != nil { + return err + } + + host, hc, err := cfg.DefaultHost() + if err != nil { + return err + } + + client := api.NewClient( + host, + hc.Token, + "/api/console/v1/graphql", + cfg.HTTPTimeoutDuration(), + ) + + data, err := client.Do( + viewQuery, + map[string]any{"key": args[0]}, + ) + if err != nil { + return err + } + + var resp struct { + CookieProvider *cookieProvider `json:"cookieProvider"` + } + if err := json.Unmarshal(data, &resp); err != nil { + return fmt.Errorf("cannot parse response: %w", err) + } + + if resp.CookieProvider == nil { + return fmt.Errorf("cookie provider %q not found", args[0]) + } + + if *flagOutput == cmdutil.OutputJSON { + return cmdutil.PrintJSON(f.IOStreams.Out, resp.CookieProvider) + } + + p := resp.CookieProvider + out := f.IOStreams.Out + + bold := lipgloss.NewStyle().Bold(true) + label := lipgloss.NewStyle().Foreground(lipgloss.Color("242")).Width(16) + + _, _ = fmt.Fprintf(out, "%s\n\n", bold.Render(p.Name)) + + _, _ = fmt.Fprintf(out, "%s%s\n", label.Render("Key:"), p.Key) + _, _ = fmt.Fprintf(out, "%s%s\n", label.Render("Category:"), p.Category) + _, _ = fmt.Fprintf(out, "%s%s\n", label.Render("Website:"), p.WebsiteURL) + _, _ = fmt.Fprintf(out, "%s%s\n", label.Render("Description:"), p.Description) + + _, _ = fmt.Fprintln(out) + _, _ = fmt.Fprintf(out, "%s\n\n", bold.Render("Cookies")) + + rows := make([][]string, 0, len(p.Cookies)) + for _, c := range p.Cookies { + rows = append(rows, []string{ + c.Name, + c.Duration, + c.Description, + }) + } + + t := cmdutil.NewTable("NAME", "DURATION", "DESCRIPTION").Rows(rows...) + _, _ = fmt.Fprintln(out, t) + + return nil + }, + } + + flagOutput = cmdutil.AddOutputFlag(cmd) + + return cmd +} diff --git a/pkg/cmd/cookiebanner/publish/publish.go b/pkg/cmd/cookiebanner/publish/publish.go new file mode 100644 index 000000000..32e827a31 --- /dev/null +++ b/pkg/cmd/cookiebanner/publish/publish.go @@ -0,0 +1,101 @@ +// Copyright (c) 2026 Probo Inc . +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR +// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +// PERFORMANCE OF THIS SOFTWARE. + +package publish + +import ( + "encoding/json" + "fmt" + + "github.com/spf13/cobra" + "go.probo.inc/probo/pkg/cli/api" + "go.probo.inc/probo/pkg/cmd/cmdutil" +) + +const publishMutation = ` +mutation($input: PublishCookieBannerInput!) { + publishCookieBanner(input: $input) { + cookieBanner { + id + name + state + } + } +} +` + +type publishResponse struct { + PublishCookieBanner struct { + CookieBanner struct { + ID string `json:"id"` + Name string `json:"name"` + State string `json:"state"` + } `json:"cookieBanner"` + } `json:"publishCookieBanner"` +} + +func NewCmdPublish(f *cmdutil.Factory) *cobra.Command { + cmd := &cobra.Command{ + Use: "publish ", + Short: "Publish a cookie banner", + Args: cobra.ExactArgs(1), + RunE: func(cmd *cobra.Command, args []string) error { + cfg, err := f.Config() + if err != nil { + return err + } + + host, hc, err := cfg.DefaultHost() + if err != nil { + return err + } + + client := api.NewClient( + host, + hc.Token, + "/api/console/v1/graphql", + cfg.HTTPTimeoutDuration(), + ) + + data, err := client.Do( + publishMutation, + map[string]any{ + "input": map[string]any{ + "id": args[0], + }, + }, + ) + if err != nil { + return err + } + + var resp publishResponse + if err := json.Unmarshal(data, &resp); err != nil { + return fmt.Errorf("cannot parse response: %w", err) + } + + b := resp.PublishCookieBanner.CookieBanner + _, _ = fmt.Fprintf( + f.IOStreams.Out, + "Published cookie banner %s (%s)\n", + b.ID, + b.Name, + ) + + return nil + }, + } + + return cmd +} diff --git a/pkg/cmd/cookiebanner/update/update.go b/pkg/cmd/cookiebanner/update/update.go new file mode 100644 index 000000000..460060399 --- /dev/null +++ b/pkg/cmd/cookiebanner/update/update.go @@ -0,0 +1,162 @@ +// Copyright (c) 2026 Probo Inc . +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR +// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +// PERFORMANCE OF THIS SOFTWARE. + +package update + +import ( + "encoding/json" + "fmt" + + "github.com/spf13/cobra" + "go.probo.inc/probo/pkg/cli/api" + "go.probo.inc/probo/pkg/cmd/cmdutil" +) + +const updateMutation = ` +mutation($input: UpdateCookieBannerInput!) { + updateCookieBanner(input: $input) { + cookieBanner { + id + name + domain + state + } + } +} +` + +type updateResponse struct { + UpdateCookieBanner struct { + CookieBanner struct { + ID string `json:"id"` + Name string `json:"name"` + Domain string `json:"domain"` + State string `json:"state"` + } `json:"cookieBanner"` + } `json:"updateCookieBanner"` +} + +func NewCmdUpdate(f *cmdutil.Factory) *cobra.Command { + var ( + flagName string + flagDomain string + flagTitle string + flagDescription string + flagAcceptAllLabel string + flagRejectAllLabel string + flagSavePrefsLabel string + flagPrivacyPolicyURL string + flagConsentExpiryDays int + flagConsentMode string + ) + + cmd := &cobra.Command{ + Use: "update ", + Short: "Update a cookie banner", + Args: cobra.ExactArgs(1), + RunE: func(cmd *cobra.Command, args []string) error { + cfg, err := f.Config() + if err != nil { + return err + } + + host, hc, err := cfg.DefaultHost() + if err != nil { + return err + } + + client := api.NewClient( + host, + hc.Token, + "/api/console/v1/graphql", + cfg.HTTPTimeoutDuration(), + ) + + input := map[string]any{ + "id": args[0], + } + + if cmd.Flags().Changed("name") { + input["name"] = flagName + } + if cmd.Flags().Changed("domain") { + input["domain"] = flagDomain + } + if cmd.Flags().Changed("title") { + input["title"] = flagTitle + } + if cmd.Flags().Changed("description") { + input["description"] = flagDescription + } + if cmd.Flags().Changed("accept-all-label") { + input["acceptAllLabel"] = flagAcceptAllLabel + } + if cmd.Flags().Changed("reject-all-label") { + input["rejectAllLabel"] = flagRejectAllLabel + } + if cmd.Flags().Changed("save-preferences-label") { + input["savePreferencesLabel"] = flagSavePrefsLabel + } + if cmd.Flags().Changed("privacy-policy-url") { + input["privacyPolicyUrl"] = flagPrivacyPolicyURL + } + if cmd.Flags().Changed("consent-expiry-days") { + input["consentExpiryDays"] = flagConsentExpiryDays + } + if cmd.Flags().Changed("consent-mode") { + input["consentMode"] = flagConsentMode + } + + if len(input) == 1 { + return fmt.Errorf("at least one field must be specified for update") + } + + data, err := client.Do( + updateMutation, + map[string]any{"input": input}, + ) + if err != nil { + return err + } + + var resp updateResponse + if err := json.Unmarshal(data, &resp); err != nil { + return fmt.Errorf("cannot parse response: %w", err) + } + + b := resp.UpdateCookieBanner.CookieBanner + _, _ = fmt.Fprintf( + f.IOStreams.Out, + "Updated cookie banner %s (%s)\n", + b.ID, + b.Name, + ) + + return nil + }, + } + + cmd.Flags().StringVar(&flagName, "name", "", "Banner name") + cmd.Flags().StringVar(&flagDomain, "domain", "", "Domain") + cmd.Flags().StringVar(&flagTitle, "title", "", "Banner title") + cmd.Flags().StringVar(&flagDescription, "description", "", "Banner description") + cmd.Flags().StringVar(&flagAcceptAllLabel, "accept-all-label", "", "Accept all button label") + cmd.Flags().StringVar(&flagRejectAllLabel, "reject-all-label", "", "Reject all button label") + cmd.Flags().StringVar(&flagSavePrefsLabel, "save-preferences-label", "", "Save preferences button label") + cmd.Flags().StringVar(&flagPrivacyPolicyURL, "privacy-policy-url", "", "Privacy policy URL") + cmd.Flags().IntVar(&flagConsentExpiryDays, "consent-expiry-days", 0, "Days until consent expires") + cmd.Flags().StringVar(&flagConsentMode, "consent-mode", "", "Consent mode (OPT_IN or OPT_OUT)") + + return cmd +} diff --git a/pkg/cmd/cookiebanner/view/view.go b/pkg/cmd/cookiebanner/view/view.go new file mode 100644 index 000000000..2ca53a543 --- /dev/null +++ b/pkg/cmd/cookiebanner/view/view.go @@ -0,0 +1,170 @@ +// Copyright (c) 2026 Probo Inc . +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR +// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +// PERFORMANCE OF THIS SOFTWARE. + +package view + +import ( + "encoding/json" + "fmt" + + "github.com/charmbracelet/lipgloss" + "github.com/spf13/cobra" + "go.probo.inc/probo/pkg/cli/api" + "go.probo.inc/probo/pkg/cmd/cmdutil" +) + +const viewQuery = ` +query($id: ID!) { + node(id: $id) { + __typename + ... on CookieBanner { + id + name + domain + state + title + description + acceptAllLabel + rejectAllLabel + savePreferencesLabel + privacyPolicyUrl + consentExpiryDays + consentMode + version + embedSnippet + createdAt + updatedAt + } + } +} +` + +type viewResponse struct { + Node *struct { + Typename string `json:"__typename"` + ID string `json:"id"` + Name string `json:"name"` + Domain string `json:"domain"` + State string `json:"state"` + Title string `json:"title"` + Description string `json:"description"` + AcceptAllLabel string `json:"acceptAllLabel"` + RejectAllLabel string `json:"rejectAllLabel"` + SavePreferencesLabel string `json:"savePreferencesLabel"` + PrivacyPolicyURL string `json:"privacyPolicyUrl"` + ConsentExpiryDays int `json:"consentExpiryDays"` + ConsentMode string `json:"consentMode"` + Version int `json:"version"` + EmbedSnippet string `json:"embedSnippet"` + CreatedAt string `json:"createdAt"` + UpdatedAt string `json:"updatedAt"` + } `json:"node"` +} + +func NewCmdView(f *cmdutil.Factory) *cobra.Command { + var flagOutput *string + + cmd := &cobra.Command{ + Use: "view ", + Short: "View a cookie banner", + Args: cobra.ExactArgs(1), + RunE: func(cmd *cobra.Command, args []string) error { + if err := cmdutil.ValidateOutputFlag(flagOutput); err != nil { + return err + } + + cfg, err := f.Config() + if err != nil { + return err + } + + host, hc, err := cfg.DefaultHost() + if err != nil { + return err + } + + client := api.NewClient( + host, + hc.Token, + "/api/console/v1/graphql", + cfg.HTTPTimeoutDuration(), + ) + + data, err := client.Do( + viewQuery, + map[string]any{"id": args[0]}, + ) + if err != nil { + return err + } + + var resp viewResponse + if err := json.Unmarshal(data, &resp); err != nil { + return fmt.Errorf("cannot parse response: %w", err) + } + + if resp.Node == nil { + return fmt.Errorf("cookie banner %s not found", args[0]) + } + + if resp.Node.Typename != "CookieBanner" { + return fmt.Errorf("expected CookieBanner node, got %s", resp.Node.Typename) + } + + if *flagOutput == cmdutil.OutputJSON { + return cmdutil.PrintJSON(f.IOStreams.Out, resp.Node) + } + + b := resp.Node + out := f.IOStreams.Out + + bold := lipgloss.NewStyle().Bold(true) + label := lipgloss.NewStyle().Foreground(lipgloss.Color("242")).Width(22) + + _, _ = fmt.Fprintf(out, "%s\n\n", bold.Render(b.Name)) + + _, _ = fmt.Fprintf(out, "%s%s\n", label.Render("ID:"), b.ID) + _, _ = fmt.Fprintf(out, "%s%s\n", label.Render("Domain:"), b.Domain) + _, _ = fmt.Fprintf(out, "%s%s\n", label.Render("State:"), b.State) + _, _ = fmt.Fprintf(out, "%s%d\n", label.Render("Version:"), b.Version) + + _, _ = fmt.Fprintln(out) + _, _ = fmt.Fprintf(out, "%s%s\n", label.Render("Title:"), b.Title) + if b.Description != "" { + _, _ = fmt.Fprintf(out, "%s%s\n", label.Render("Description:"), b.Description) + } + _, _ = fmt.Fprintf(out, "%s%s\n", label.Render("Privacy Policy URL:"), b.PrivacyPolicyURL) + _, _ = fmt.Fprintf(out, "%s%d days\n", label.Render("Consent Expiry:"), b.ConsentExpiryDays) + _, _ = fmt.Fprintf(out, "%s%s\n", label.Render("Consent Mode:"), b.ConsentMode) + + _, _ = fmt.Fprintln(out) + _, _ = fmt.Fprintf(out, "%s%s\n", label.Render("Accept All Label:"), b.AcceptAllLabel) + _, _ = fmt.Fprintf(out, "%s%s\n", label.Render("Reject All Label:"), b.RejectAllLabel) + _, _ = fmt.Fprintf(out, "%s%s\n", label.Render("Save Prefs Label:"), b.SavePreferencesLabel) + + _, _ = fmt.Fprintln(out) + _, _ = fmt.Fprintf(out, "%s\n%s\n", label.Render("Embed Snippet:"), b.EmbedSnippet) + + _, _ = fmt.Fprintln(out) + _, _ = fmt.Fprintf(out, "%s%s\n", label.Render("Created:"), cmdutil.FormatTime(b.CreatedAt)) + _, _ = fmt.Fprintf(out, "%s%s\n", label.Render("Updated:"), cmdutil.FormatTime(b.UpdatedAt)) + + return nil + }, + } + + flagOutput = cmdutil.AddOutputFlag(cmd) + + return cmd +} diff --git a/pkg/cmd/root/root.go b/pkg/cmd/root/root.go index 05f646acc..38a12f11a 100644 --- a/pkg/cmd/root/root.go +++ b/pkg/cmd/root/root.go @@ -26,6 +26,7 @@ import ( cmdconfig "go.probo.inc/probo/pkg/cmd/config" cmdcontext "go.probo.inc/probo/pkg/cmd/context" "go.probo.inc/probo/pkg/cmd/control" + "go.probo.inc/probo/pkg/cmd/cookiebanner" "go.probo.inc/probo/pkg/cmd/evidence" "go.probo.inc/probo/pkg/cmd/finding" "go.probo.inc/probo/pkg/cmd/framework" @@ -76,6 +77,7 @@ func NewCmdRoot(f *cmdutil.Factory) *cobra.Command { cmd.AddCommand(cmdconfig.NewCmdConfig(f)) cmd.AddCommand(cmdcontext.NewCmdContext(f)) cmd.AddCommand(control.NewCmdControl(f)) + cmd.AddCommand(cookiebanner.NewCmdCookieBanner(f)) cmd.AddCommand(evidence.NewCmdEvidence(f)) cmd.AddCommand(finding.NewCmdFinding(f)) cmd.AddCommand(framework.NewCmdFramework(f)) diff --git a/pkg/consent/consent_record.go b/pkg/consent/consent_record.go new file mode 100644 index 000000000..1940c2e57 --- /dev/null +++ b/pkg/consent/consent_record.go @@ -0,0 +1,107 @@ +// Copyright (c) 2025-2026 Probo Inc . +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR +// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +// PERFORMANCE OF THIS SOFTWARE. + +package consent + +import ( + "context" + "fmt" + + "go.gearno.de/kit/pg" + "go.probo.inc/probo/pkg/coredata" + "go.probo.inc/probo/pkg/gid" + "go.probo.inc/probo/pkg/page" +) + +func (s *Service) ListConsentRecordsForCookieBannerID( + ctx context.Context, + cookieBannerID gid.GID, + cursor *page.Cursor[coredata.ConsentRecordOrderField], + filter *coredata.ConsentRecordFilter, +) (*page.Page[*coredata.ConsentRecord, coredata.ConsentRecordOrderField], error) { + scope := coredata.NewScopeFromObjectID(cookieBannerID) + var records coredata.ConsentRecords + + err := s.pg.WithConn( + ctx, + func(ctx context.Context, conn pg.Querier) error { + if err := records.LoadByCookieBannerID( + ctx, + conn, + scope, + cookieBannerID, + cursor, + filter, + ); err != nil { + return fmt.Errorf("cannot load consent records: %w", err) + } + + return nil + }, + ) + if err != nil { + return nil, fmt.Errorf("cannot list consent records: %w", err) + } + + return page.NewPage(records, cursor), nil +} + +func (s *Service) GetConsentAnalyticsForCookieBannerID( + ctx context.Context, + cookieBannerID gid.GID, +) (*coredata.ConsentAnalytics, error) { + scope := coredata.NewScopeFromObjectID(cookieBannerID) + analytics := &coredata.ConsentAnalytics{} + + err := s.pg.WithConn( + ctx, + func(ctx context.Context, conn pg.Querier) error { + return analytics.LoadByCookieBannerID(ctx, conn, scope, cookieBannerID) + }, + ) + if err != nil { + return nil, fmt.Errorf("cannot get consent analytics: %w", err) + } + + return analytics, nil +} + +func (s *Service) CountConsentRecordsForCookieBannerID( + ctx context.Context, + cookieBannerID gid.GID, + filter *coredata.ConsentRecordFilter, +) (int, error) { + var ( + scope = coredata.NewScopeFromObjectID(cookieBannerID) + count int + ) + + err := s.pg.WithConn( + ctx, + func(ctx context.Context, conn pg.Querier) (err error) { + records := coredata.ConsentRecords{} + count, err = records.CountByCookieBannerID(ctx, conn, scope, cookieBannerID, filter) + if err != nil { + return fmt.Errorf("cannot count consent records: %w", err) + } + + return nil + }, + ) + if err != nil { + return 0, err + } + + return count, nil +} diff --git a/pkg/consent/cookie_banner.go b/pkg/consent/cookie_banner.go new file mode 100644 index 000000000..b061cd70c --- /dev/null +++ b/pkg/consent/cookie_banner.go @@ -0,0 +1,393 @@ +// Copyright (c) 2025-2026 Probo Inc . +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR +// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +// PERFORMANCE OF THIS SOFTWARE. + +package consent + +import ( + "context" + "encoding/json" + "fmt" + "time" + + "go.gearno.de/kit/pg" + "go.probo.inc/probo/pkg/coredata" + "go.probo.inc/probo/pkg/gid" + "go.probo.inc/probo/pkg/page" + "go.probo.inc/probo/pkg/validator" +) + +type ( + CreateCookieBannerRequest struct { + OrganizationID gid.GID + Name string + Domain string + } + + UpdateCookieBannerRequest struct { + ID gid.GID + Name *string + Domain *string + State *coredata.CookieBannerState + Title *string + Description *string + AcceptAllLabel *string + RejectAllLabel *string + SavePreferencesLabel *string + PrivacyPolicyURL *string + ConsentExpiryDays *int + ConsentMode *coredata.ConsentMode + Theme json.RawMessage + } +) + +func (r *CreateCookieBannerRequest) Validate() error { + v := validator.New() + + v.Check(r.OrganizationID, "organization_id", validator.Required(), validator.GID(coredata.OrganizationEntityType)) + v.Check(r.Name, "name", validator.Required(), validator.SafeTextNoNewLine(NameMaxLength)) + + return v.Error() +} + +func (r *UpdateCookieBannerRequest) Validate() error { + v := validator.New() + + v.Check(r.ID, "id", validator.Required(), validator.GID(coredata.CookieBannerEntityType)) + v.Check(r.Name, "name", validator.SafeTextNoNewLine(NameMaxLength)) + v.Check(r.Title, "title", validator.SafeTextNoNewLine(TitleMaxLength)) + v.Check(r.Description, "description", validator.SafeText(ContentMaxLength)) + v.Check(r.AcceptAllLabel, "accept_all_label", validator.SafeTextNoNewLine(NameMaxLength)) + v.Check(r.RejectAllLabel, "reject_all_label", validator.SafeTextNoNewLine(NameMaxLength)) + v.Check(r.SavePreferencesLabel, "save_preferences_label", validator.SafeTextNoNewLine(NameMaxLength)) + v.Check(r.State, "state", validator.OneOfSlice(coredata.CookieBannerStates())) + v.Check(r.ConsentExpiryDays, "consent_expiry_days", validator.Min(ConsentExpiryDaysMin), validator.Max(ConsentExpiryDaysMax)) + v.Check(r.ConsentMode, "consent_mode", validator.OneOfSlice(coredata.ConsentModes())) + + return v.Error() +} + +func (s *Service) GetCookieBanner( + ctx context.Context, + bannerID gid.GID, +) (*coredata.CookieBanner, error) { + scope := coredata.NewScopeFromObjectID(bannerID) + banner := &coredata.CookieBanner{} + + err := s.pg.WithConn( + ctx, + func(ctx context.Context, conn pg.Querier) error { + return banner.LoadByID(ctx, conn, scope, bannerID) + }, + ) + if err != nil { + return nil, fmt.Errorf("cannot get cookie banner: %w", err) + } + + return banner, nil +} + +func (s *Service) ListCookieBannersForOrganizationID( + ctx context.Context, + organizationID gid.GID, + cursor *page.Cursor[coredata.CookieBannerOrderField], +) (*page.Page[*coredata.CookieBanner, coredata.CookieBannerOrderField], error) { + scope := coredata.NewScopeFromObjectID(organizationID) + var banners coredata.CookieBanners + + err := s.pg.WithConn( + ctx, + func(ctx context.Context, conn pg.Querier) error { + if err := banners.LoadByOrganizationID( + ctx, + conn, + scope, + organizationID, + cursor, + ); err != nil { + return fmt.Errorf("cannot load cookie banners: %w", err) + } + + return nil + }, + ) + if err != nil { + return nil, fmt.Errorf("cannot list cookie banners: %w", err) + } + + return page.NewPage(banners, cursor), nil +} + +func (s *Service) CountCookieBannersForOrganizationID( + ctx context.Context, + organizationID gid.GID, +) (int, error) { + scope := coredata.NewScopeFromObjectID(organizationID) + var count int + + err := s.pg.WithConn( + ctx, + func(ctx context.Context, conn pg.Querier) (err error) { + banners := coredata.CookieBanners{} + count, err = banners.CountByOrganizationID(ctx, conn, scope, organizationID) + if err != nil { + return fmt.Errorf("cannot count cookie banners: %w", err) + } + + return nil + }, + ) + if err != nil { + return 0, err + } + + return count, nil +} + +func (s *Service) CreateCookieBanner( + ctx context.Context, + req CreateCookieBannerRequest, +) (*coredata.CookieBanner, error) { + if err := req.Validate(); err != nil { + return nil, fmt.Errorf("invalid request: %w", err) + } + + var ( + scope = coredata.NewScopeFromObjectID(req.OrganizationID) + now = time.Now() + bannerID = gid.New(req.OrganizationID.TenantID(), coredata.CookieBannerEntityType) + tenantID = req.OrganizationID.TenantID() + ) + + banner := &coredata.CookieBanner{ + ID: bannerID, + OrganizationID: req.OrganizationID, + Name: req.Name, + Domain: req.Domain, + State: coredata.CookieBannerStateDraft, + Title: "We value your privacy", + Description: "We use cookies to enhance your browsing experience, serve personalized content, and analyze our traffic. By clicking \"Accept All\", you consent to our use of cookies.", + AcceptAllLabel: "Accept all", + RejectAllLabel: "Reject all", + SavePreferencesLabel: "Save preferences", + ConsentExpiryDays: 365, + ConsentMode: coredata.ConsentModeOptIn, + Version: 1, + CreatedAt: now, + UpdatedAt: now, + } + + defaultCategories := []*coredata.CookieCategory{ + { + ID: gid.New(tenantID, coredata.CookieCategoryEntityType), + CookieBannerID: bannerID, + Name: "Necessary", + Description: "Essential cookies required for the website to function properly. These cannot be disabled.", + Required: true, + Rank: 0, + Cookies: make(coredata.CookieItems, 0), + CreatedAt: now, + UpdatedAt: now, + }, + { + ID: gid.New(tenantID, coredata.CookieCategoryEntityType), + CookieBannerID: bannerID, + Name: "Analytics", + Description: "Cookies that help us understand how visitors interact with our website.", + Required: false, + Rank: 1, + Cookies: make(coredata.CookieItems, 0), + CreatedAt: now, + UpdatedAt: now, + }, + { + ID: gid.New(tenantID, coredata.CookieCategoryEntityType), + CookieBannerID: bannerID, + Name: "Marketing", + Description: "Cookies used to deliver personalized advertisements.", + Required: false, + Rank: 2, + Cookies: make(coredata.CookieItems, 0), + CreatedAt: now, + UpdatedAt: now, + }, + { + ID: gid.New(tenantID, coredata.CookieCategoryEntityType), + CookieBannerID: bannerID, + Name: "Preferences", + Description: "Cookies that remember your settings and preferences.", + Required: false, + Rank: 3, + Cookies: make(coredata.CookieItems, 0), + CreatedAt: now, + UpdatedAt: now, + }, + } + + err := s.pg.WithTx( + ctx, + func(ctx context.Context, conn pg.Tx) error { + if err := banner.Insert(ctx, conn, scope); err != nil { + return fmt.Errorf("cannot insert cookie banner: %w", err) + } + + for _, category := range defaultCategories { + if err := category.Insert(ctx, conn, scope); err != nil { + return fmt.Errorf("cannot insert default cookie category %q: %w", category.Name, err) + } + } + + return nil + }, + ) + if err != nil { + return nil, fmt.Errorf("cannot create cookie banner: %w", err) + } + + return banner, nil +} + +func (s *Service) UpdateCookieBanner( + ctx context.Context, + req UpdateCookieBannerRequest, +) (*coredata.CookieBanner, error) { + if err := req.Validate(); err != nil { + return nil, fmt.Errorf("invalid request: %w", err) + } + + scope := coredata.NewScopeFromObjectID(req.ID) + banner := &coredata.CookieBanner{ID: req.ID} + + err := s.pg.WithTx( + ctx, + func(ctx context.Context, conn pg.Tx) error { + if err := banner.LoadByID(ctx, conn, scope, req.ID); err != nil { + return fmt.Errorf("cannot load cookie banner: %w", err) + } + + bumpVersion := false + now := time.Now() + banner.UpdatedAt = now + + if req.Name != nil { + banner.Name = *req.Name + } + if req.Domain != nil { + banner.Domain = *req.Domain + } + if req.State != nil { + banner.State = *req.State + } + if req.Title != nil { + if *req.Title != banner.Title { + bumpVersion = true + } + banner.Title = *req.Title + } + if req.Description != nil { + if *req.Description != banner.Description { + bumpVersion = true + } + banner.Description = *req.Description + } + if req.AcceptAllLabel != nil { + banner.AcceptAllLabel = *req.AcceptAllLabel + } + if req.RejectAllLabel != nil { + banner.RejectAllLabel = *req.RejectAllLabel + } + if req.SavePreferencesLabel != nil { + banner.SavePreferencesLabel = *req.SavePreferencesLabel + } + if req.PrivacyPolicyURL != nil { + if *req.PrivacyPolicyURL != banner.PrivacyPolicyURL { + bumpVersion = true + } + banner.PrivacyPolicyURL = *req.PrivacyPolicyURL + } + if req.ConsentExpiryDays != nil { + banner.ConsentExpiryDays = *req.ConsentExpiryDays + } + if req.ConsentMode != nil { + if *req.ConsentMode != banner.ConsentMode { + bumpVersion = true + } + banner.ConsentMode = *req.ConsentMode + } + if req.Theme != nil { + banner.Theme = req.Theme + } + + if bumpVersion { + banner.Version++ + } + + if err := banner.Update(ctx, conn, scope); err != nil { + return fmt.Errorf("cannot update cookie banner: %w", err) + } + + return nil + }, + ) + if err != nil { + return nil, fmt.Errorf("cannot update cookie banner: %w", err) + } + + return banner, nil +} + +func (s *Service) DeleteCookieBanner( + ctx context.Context, + bannerID gid.GID, +) error { + scope := coredata.NewScopeFromObjectID(bannerID) + banner := &coredata.CookieBanner{ID: bannerID} + + err := s.pg.WithConn( + ctx, + func(ctx context.Context, conn pg.Querier) error { + return banner.Delete(ctx, conn, scope) + }, + ) + if err != nil { + return fmt.Errorf("cannot delete cookie banner: %w", err) + } + + return nil +} + +func (s *Service) PublishCookieBanner( + ctx context.Context, + bannerID gid.GID, +) (*coredata.CookieBanner, error) { + return s.UpdateCookieBanner( + ctx, + UpdateCookieBannerRequest{ + ID: bannerID, + State: new(coredata.CookieBannerStatePublished), + }, + ) +} + +func (s *Service) DisableCookieBanner( + ctx context.Context, + bannerID gid.GID, +) (*coredata.CookieBanner, error) { + return s.UpdateCookieBanner( + ctx, + UpdateCookieBannerRequest{ + ID: bannerID, + State: new(coredata.CookieBannerStateDisabled), + }, + ) +} diff --git a/pkg/consent/cookie_category.go b/pkg/consent/cookie_category.go new file mode 100644 index 000000000..b18facd83 --- /dev/null +++ b/pkg/consent/cookie_category.go @@ -0,0 +1,391 @@ +// Copyright (c) 2025-2026 Probo Inc . +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR +// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +// PERFORMANCE OF THIS SOFTWARE. + +package consent + +import ( + "context" + "errors" + "fmt" + "time" + + "go.gearno.de/kit/pg" + "go.probo.inc/probo/pkg/cookieprovider" + "go.probo.inc/probo/pkg/coredata" + "go.probo.inc/probo/pkg/gid" + "go.probo.inc/probo/pkg/page" + "go.probo.inc/probo/pkg/validator" +) + +type ( + CreateCookieCategoryRequest struct { + CookieBannerID gid.GID + Name string + Description string + Rank int + Cookies coredata.CookieItems + } + + UpdateCookieCategoryRequest struct { + ID gid.GID + Name *string + Description *string + Rank *int + Cookies *coredata.CookieItems + } + + AddCookiesFromProviderRequest struct { + CookieCategoryID gid.GID + ProviderKey string + } +) + +var ( + ErrCannotDeleteRequiredCategory = errors.New("cannot delete a required cookie category") +) + +func (r *CreateCookieCategoryRequest) Validate() error { + v := validator.New() + + v.Check(r.CookieBannerID, "cookie_banner_id", validator.Required(), validator.GID(coredata.CookieBannerEntityType)) + v.Check(r.Name, "name", validator.Required(), validator.SafeTextNoNewLine(NameMaxLength)) + v.Check(r.Description, "description", validator.SafeText(ContentMaxLength)) + + return v.Error() +} + +func (r *UpdateCookieCategoryRequest) Validate() error { + v := validator.New() + + v.Check(r.ID, "id", validator.Required(), validator.GID(coredata.CookieCategoryEntityType)) + v.Check(r.Name, "name", validator.SafeTextNoNewLine(NameMaxLength)) + v.Check(r.Description, "description", validator.SafeText(ContentMaxLength)) + + return v.Error() +} + +func (s *Service) GetCookieCategory( + ctx context.Context, + categoryID gid.GID, +) (*coredata.CookieCategory, error) { + scope := coredata.NewScopeFromObjectID(categoryID) + category := &coredata.CookieCategory{} + + err := s.pg.WithConn( + ctx, + func(ctx context.Context, conn pg.Querier) error { + if err := category.LoadByID(ctx, conn, scope, categoryID); err != nil { + return fmt.Errorf("cannot load cookie category: %w", err) + } + + return nil + }, + ) + if err != nil { + return nil, fmt.Errorf("cannot get cookie category: %w", err) + } + + return category, nil +} + +func (s *Service) ListCookieCategoriesForCookieBannerID( + ctx context.Context, + cookieBannerID gid.GID, + cursor *page.Cursor[coredata.CookieCategoryOrderField], +) (*page.Page[*coredata.CookieCategory, coredata.CookieCategoryOrderField], error) { + scope := coredata.NewScopeFromObjectID(cookieBannerID) + var categories coredata.CookieCategories + + err := s.pg.WithConn( + ctx, + func(ctx context.Context, conn pg.Querier) error { + if err := categories.LoadByCookieBannerID( + ctx, + conn, + scope, + cookieBannerID, + cursor, + ); err != nil { + return fmt.Errorf("cannot load cookie categories: %w", err) + } + + return nil + }, + ) + if err != nil { + return nil, fmt.Errorf("cannot list cookie categories: %w", err) + } + + return page.NewPage(categories, cursor), nil +} + +func (s *Service) CountCookieCategoriesForCookieBannerID( + ctx context.Context, + cookieBannerID gid.GID, +) (int, error) { + var ( + scope = coredata.NewScopeFromObjectID(cookieBannerID) + count int + ) + + err := s.pg.WithConn( + ctx, + func(ctx context.Context, conn pg.Querier) (err error) { + categories := coredata.CookieCategories{} + count, err = categories.CountByCookieBannerID(ctx, conn, scope, cookieBannerID) + if err != nil { + return fmt.Errorf("cannot count cookie categories: %w", err) + } + + return nil + }, + ) + if err != nil { + return 0, err + } + + return count, nil +} + +func (s *Service) CreateCookieCategory( + ctx context.Context, + req CreateCookieCategoryRequest, +) (*coredata.CookieCategory, error) { + if err := req.Validate(); err != nil { + return nil, fmt.Errorf("invalid request: %w", err) + } + + var ( + scope = coredata.NewScopeFromObjectID(req.CookieBannerID) + now = time.Now() + categoryID = gid.New(req.CookieBannerID.TenantID(), coredata.CookieCategoryEntityType) + cookies = req.Cookies + ) + + if cookies == nil { + cookies = make(coredata.CookieItems, 0) + } + + category := &coredata.CookieCategory{ + ID: categoryID, + CookieBannerID: req.CookieBannerID, + Name: req.Name, + Description: req.Description, + Required: false, + Rank: req.Rank, + Cookies: cookies, + CreatedAt: now, + UpdatedAt: now, + } + + err := s.pg.WithTx( + ctx, + func(ctx context.Context, conn pg.Tx) error { + if err := category.Insert(ctx, conn, scope); err != nil { + return fmt.Errorf("cannot insert cookie category: %w", err) + } + + banner := &coredata.CookieBanner{} + if err := banner.LoadByID(ctx, conn, scope, req.CookieBannerID); err != nil { + return fmt.Errorf("cannot load cookie banner: %w", err) + } + + banner.Version++ + banner.UpdatedAt = now + + if err := banner.Update(ctx, conn, scope); err != nil { + return fmt.Errorf("cannot update cookie banner version: %w", err) + } + + return nil + }, + ) + if err != nil { + return nil, fmt.Errorf("cannot create cookie category: %w", err) + } + + return category, nil +} + +func (s *Service) UpdateCookieCategory( + ctx context.Context, + req UpdateCookieCategoryRequest, +) (*coredata.CookieCategory, error) { + if err := req.Validate(); err != nil { + return nil, fmt.Errorf("invalid request: %w", err) + } + + scope := coredata.NewScopeFromObjectID(req.ID) + category := &coredata.CookieCategory{ID: req.ID} + + err := s.pg.WithTx( + ctx, + func(ctx context.Context, conn pg.Tx) error { + if err := category.LoadByID(ctx, conn, scope, req.ID); err != nil { + return fmt.Errorf("cannot load cookie category: %w", err) + } + + bumpVersion := false + now := time.Now() + category.UpdatedAt = now + + if req.Name != nil { + if *req.Name != category.Name { + bumpVersion = true + } + category.Name = *req.Name + } + if req.Description != nil { + category.Description = *req.Description + } + if req.Rank != nil { + category.Rank = *req.Rank + } + if req.Cookies != nil { + category.Cookies = *req.Cookies + } + + if err := category.Update(ctx, conn, scope); err != nil { + return fmt.Errorf("cannot update cookie category: %w", err) + } + + if bumpVersion { + banner := &coredata.CookieBanner{} + if err := banner.LoadByID(ctx, conn, scope, category.CookieBannerID); err != nil { + return fmt.Errorf("cannot load cookie banner: %w", err) + } + + banner.Version++ + banner.UpdatedAt = now + + if err := banner.Update(ctx, conn, scope); err != nil { + return fmt.Errorf("cannot update cookie banner version: %w", err) + } + } + + return nil + }, + ) + if err != nil { + return nil, fmt.Errorf("cannot update cookie category: %w", err) + } + + return category, nil +} + +func (s *Service) DeleteCookieCategory( + ctx context.Context, + categoryID gid.GID, +) error { + scope := coredata.NewScopeFromObjectID(categoryID) + + err := s.pg.WithTx( + ctx, + func(ctx context.Context, conn pg.Tx) error { + category := &coredata.CookieCategory{} + if err := category.LoadByID(ctx, conn, scope, categoryID); err != nil { + return fmt.Errorf("cannot load cookie category: %w", err) + } + + if category.Required { + return ErrCannotDeleteRequiredCategory + } + + if err := category.Delete(ctx, conn, scope); err != nil { + return fmt.Errorf("cannot delete cookie category: %w", err) + } + + banner := &coredata.CookieBanner{} + if err := banner.LoadByID(ctx, conn, scope, category.CookieBannerID); err != nil { + return fmt.Errorf("cannot load cookie banner: %w", err) + } + + banner.Version++ + banner.UpdatedAt = time.Now() + + if err := banner.Update(ctx, conn, scope); err != nil { + return fmt.Errorf("cannot update cookie banner version: %w", err) + } + + return nil + }, + ) + if err != nil { + return fmt.Errorf("cannot delete cookie category: %w", err) + } + + return nil +} + +func (r *AddCookiesFromProviderRequest) Validate() error { + v := validator.New() + + v.Check(r.CookieCategoryID, "cookie_category_id", validator.Required(), validator.GID(coredata.CookieCategoryEntityType)) + v.Check(r.ProviderKey, "provider_key", validator.Required()) + + return v.Error() +} + +func (s *Service) AddCookiesFromProvider( + ctx context.Context, + req AddCookiesFromProviderRequest, +) (*coredata.CookieCategory, error) { + if err := req.Validate(); err != nil { + return nil, fmt.Errorf("invalid request: %w", err) + } + + provider, ok := cookieprovider.ByKey(req.ProviderKey) + if !ok { + return nil, fmt.Errorf("invalid request: %w", validator.ValidationErrors{ + {Field: "provider_key", Code: validator.ErrorCodeInvalidFormat, Message: "unknown cookie provider"}, + }) + } + + scope := coredata.NewScopeFromObjectID(req.CookieCategoryID) + category := &coredata.CookieCategory{} + + err := s.pg.WithTx( + ctx, + func(ctx context.Context, conn pg.Tx) error { + if err := category.LoadByID(ctx, conn, scope, req.CookieCategoryID); err != nil { + return fmt.Errorf("cannot load cookie category: %w", err) + } + + existing := make(map[string]bool, len(category.Cookies)) + for _, c := range category.Cookies { + existing[c.Name] = true + } + + for _, item := range provider.CookieItems() { + if !existing[item.Name] { + category.Cookies = append(category.Cookies, item) + } + } + + now := time.Now() + category.UpdatedAt = now + + if err := category.Update(ctx, conn, scope); err != nil { + return fmt.Errorf("cannot update cookie category: %w", err) + } + + return nil + }, + ) + if err != nil { + return nil, fmt.Errorf("cannot add cookies from provider: %w", err) + } + + return category, nil +} diff --git a/pkg/consent/service.go b/pkg/consent/service.go new file mode 100644 index 000000000..88335eafe --- /dev/null +++ b/pkg/consent/service.go @@ -0,0 +1,158 @@ +// Copyright (c) 2025-2026 Probo Inc . +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR +// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +// PERFORMANCE OF THIS SOFTWARE. + +package consent + +import ( + "context" + "encoding/json" + "fmt" + "net" + "time" + + "go.gearno.de/kit/pg" + "go.probo.inc/probo/pkg/coredata" + "go.probo.inc/probo/pkg/gid" +) + +type ( + Service struct { + pg *pg.Client + } + + RecordConsentRequest struct { + VisitorID string + ConsentData json.RawMessage + Action coredata.ConsentAction + } + + PublicBannerConfig struct { + Banner *coredata.CookieBanner + Categories coredata.CookieCategories + } +) + +const ( + NameMaxLength = 100 + TitleMaxLength = 1000 + ContentMaxLength = 5000 + ConsentExpiryDaysMin = 1 + ConsentExpiryDaysMax = 3650 +) + +func NewService(pgClient *pg.Client) *Service { + return &Service{pg: pgClient} +} + +func (s *Service) GetPublishedBannerConfig( + ctx context.Context, + bannerID gid.GID, +) (*PublicBannerConfig, error) { + var ( + banner = &coredata.CookieBanner{} + categories coredata.CookieCategories + ) + + err := s.pg.WithConn( + ctx, + func(ctx context.Context, conn pg.Querier) error { + if err := banner.LoadPublishedByID(ctx, conn, bannerID); err != nil { + return fmt.Errorf("cannot load published cookie banner: %w", err) + } + + if err := categories.LoadAllPublicByCookieBannerID(ctx, conn, bannerID); err != nil { + return fmt.Errorf("cannot load cookie categories: %w", err) + } + + return nil + }, + ) + + if err != nil { + return nil, err + } + + return &PublicBannerConfig{ + Banner: banner, + Categories: categories, + }, nil +} + +func (s *Service) RecordConsent( + ctx context.Context, + bannerID gid.GID, + req RecordConsentRequest, + ipAddress string, + userAgent string, +) error { + return s.pg.WithConn( + ctx, + func(ctx context.Context, conn pg.Querier) error { + banner := &coredata.CookieBanner{} + if err := banner.LoadPublishedByID(ctx, conn, bannerID); err != nil { + return fmt.Errorf("cannot load published cookie banner: %w", err) + } + + var ( + tenantID = banner.ID.TenantID() + scope = coredata.NewScope(tenantID) + anonymizedIP = anonymizeIP(ipAddress) + ) + + record := &coredata.ConsentRecord{ + ID: gid.New(tenantID, coredata.ConsentRecordEntityType), + CookieBannerID: bannerID, + VisitorID: req.VisitorID, + IPAddress: &anonymizedIP, + UserAgent: &userAgent, + ConsentData: req.ConsentData, + Action: req.Action, + BannerVersion: banner.Version, + CreatedAt: time.Now(), + } + + if err := record.Insert(ctx, conn, scope); err != nil { + return fmt.Errorf("cannot insert consent record: %w", err) + } + + return nil + }, + ) +} + +func anonymizeIP(ip string) string { + // r.RemoteAddr includes a port (e.g. "192.168.1.42:54321"), + // strip it before parsing. + host, _, err := net.SplitHostPort(ip) + if err != nil { + // No port — use as-is (e.g. Unix socket or plain IP). + host = ip + } + + parsed := net.ParseIP(host) + if parsed == nil { + return "" + } + + if v4 := parsed.To4(); v4 != nil { + v4[3] = 0 + return v4.String() + } + + // IPv6: zero last 80 bits (10 bytes) + for i := 6; i < 16; i++ { + parsed[i] = 0 + } + return parsed.String() +} diff --git a/pkg/consent/service_test.go b/pkg/consent/service_test.go new file mode 100644 index 000000000..3bbe56827 --- /dev/null +++ b/pkg/consent/service_test.go @@ -0,0 +1,79 @@ +// Copyright (c) 2025-2026 Probo Inc . +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR +// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +// PERFORMANCE OF THIS SOFTWARE. + +package consent + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestAnonymizeIP(t *testing.T) { + t.Parallel() + + tests := []struct { + name string + input string + expected string + }{ + { + name: "ipv4 zeroes last octet", + input: "192.168.1.42", + expected: "192.168.1.0", + }, + { + name: "ipv4 already anonymized", + input: "10.0.0.0", + expected: "10.0.0.0", + }, + { + name: "ipv4 loopback", + input: "127.0.0.1", + expected: "127.0.0.0", + }, + { + name: "ipv6 zeroes last 80 bits", + input: "2001:0db8:85a3:0000:0000:8a2e:0370:7334", + expected: "2001:db8:85a3::", + }, + { + name: "ipv6 loopback", + input: "::1", + expected: "::", + }, + { + name: "invalid ip returns empty string", + input: "not-an-ip", + expected: "", + }, + { + name: "empty string returns empty string", + input: "", + expected: "", + }, + } + + for _, tt := range tests { + t.Run( + tt.name, + func(t *testing.T) { + t.Parallel() + + result := anonymizeIP(tt.input) + assert.Equal(t, tt.expected, result) + }, + ) + } +} diff --git a/pkg/cookieprovider/cookieprovider.go b/pkg/cookieprovider/cookieprovider.go new file mode 100644 index 000000000..a28510e72 --- /dev/null +++ b/pkg/cookieprovider/cookieprovider.go @@ -0,0 +1,323 @@ +// Copyright (c) 2026 Probo Inc . +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR +// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +// PERFORMANCE OF THIS SOFTWARE. + +package cookieprovider + +import ( + "go.probo.inc/probo/pkg/coredata" +) + +type ( + Category string + + Cookie struct { + Name string + Duration string + Description string + } + + Provider struct { + Key string + Name string + Description string + Category Category + WebsiteURL string + Cookies []Cookie + } +) + +const ( + CategoryNecessary Category = "Necessary" + CategoryAnalytics Category = "Analytics" + CategoryMarketing Category = "Marketing" + CategoryPreferences Category = "Preferences" +) + +var providersByKey map[string]*Provider + +func init() { + providersByKey = make(map[string]*Provider, len(providers)) + for i := range providers { + providersByKey[providers[i].Key] = &providers[i] + } +} + +func All() []Provider { + result := make([]Provider, len(providers)) + copy(result, providers) + return result +} + +func ByCategory(category Category) []Provider { + var result []Provider + for _, p := range providers { + if p.Category == category { + result = append(result, p) + } + } + return result +} + +func ByKey(key string) (Provider, bool) { + p, ok := providersByKey[key] + if !ok { + return Provider{}, false + } + return *p, true +} + +func (p Provider) CookieItems() coredata.CookieItems { + items := make(coredata.CookieItems, len(p.Cookies)) + for i, c := range p.Cookies { + items[i] = coredata.CookieItem{ + Name: c.Name, + Duration: c.Duration, + Description: c.Description, + } + } + return items +} + +var providers = []Provider{ + { + Key: "google-analytics", + Name: "Google Analytics", + Description: "Web analytics service that tracks and reports website traffic.", + Category: CategoryAnalytics, + WebsiteURL: "https://analytics.google.com", + Cookies: []Cookie{ + {Name: "_ga", Duration: "2 years", Description: "Distinguishes unique users by assigning a randomly generated number as a client identifier."}, + {Name: "_ga_*", Duration: "2 years", Description: "Used by Google Analytics to persist session state."}, + {Name: "_gid", Duration: "24 hours", Description: "Distinguishes unique users."}, + {Name: "_gat", Duration: "1 minute", Description: "Used to throttle the request rate to Google Analytics."}, + }, + }, + { + Key: "google-tag-manager", + Name: "Google Tag Manager", + Description: "Tag management system that allows managing JavaScript and HTML tags for tracking and analytics.", + Category: CategoryNecessary, + WebsiteURL: "https://tagmanager.google.com", + Cookies: []Cookie{ + {Name: "_gcl_au", Duration: "90 days", Description: "Used by Google AdSense to store and track conversions."}, + }, + }, + { + Key: "google-ads", + Name: "Google Ads", + Description: "Online advertising platform for displaying ads on Google search results and partner websites.", + Category: CategoryMarketing, + WebsiteURL: "https://ads.google.com", + Cookies: []Cookie{ + {Name: "_gcl_aw", Duration: "90 days", Description: "Stores click information from Google Ads for conversion tracking."}, + {Name: "_gcl_dc", Duration: "90 days", Description: "Stores click information from DoubleClick for conversion tracking."}, + {Name: "IDE", Duration: "1 year", Description: "Used by DoubleClick to register and report ad clicks."}, + {Name: "test_cookie", Duration: "15 minutes", Description: "Used to check if the browser supports cookies."}, + }, + }, + { + Key: "facebook-pixel", + Name: "Facebook Pixel", + Description: "Analytics tool for measuring the effectiveness of advertising on Facebook.", + Category: CategoryMarketing, + WebsiteURL: "https://www.facebook.com/business/tools/meta-pixel", + Cookies: []Cookie{ + {Name: "_fbp", Duration: "90 days", Description: "Used by Facebook to deliver advertising products."}, + {Name: "_fbc", Duration: "90 days", Description: "Stores click identifier from Facebook ad campaigns."}, + {Name: "fr", Duration: "90 days", Description: "Used by Facebook for advertising and tracking."}, + }, + }, + { + Key: "hubspot", + Name: "HubSpot", + Description: "Marketing, sales, and service software platform with tracking and analytics capabilities.", + Category: CategoryMarketing, + WebsiteURL: "https://www.hubspot.com", + Cookies: []Cookie{ + {Name: "__hssc", Duration: "30 minutes", Description: "Tracks sessions and determines if a new session should be created."}, + {Name: "__hssrc", Duration: "Session", Description: "Indicates if the visitor has restarted the browser."}, + {Name: "__hstc", Duration: "180 days", Description: "Tracks visitors across sessions for HubSpot analytics."}, + {Name: "hubspotutk", Duration: "180 days", Description: "Keeps track of a visitor's identity and is passed to HubSpot on form submission."}, + }, + }, + { + Key: "hotjar", + Name: "Hotjar", + Description: "Behavior analytics and user feedback service that helps understand user interactions.", + Category: CategoryAnalytics, + WebsiteURL: "https://www.hotjar.com", + Cookies: []Cookie{ + {Name: "_hj*", Duration: "Varies", Description: "Used by Hotjar to collect usage statistics and behavior data."}, + {Name: "_hjSession_*", Duration: "30 minutes", Description: "Ensures subsequent requests in a session window are attributed to the same session."}, + {Name: "_hjSessionUser_*", Duration: "1 year", Description: "Ensures data from subsequent visits is attributed to the same user."}, + }, + }, + { + Key: "intercom", + Name: "Intercom", + Description: "Customer messaging platform for sales, marketing, and support.", + Category: CategoryPreferences, + WebsiteURL: "https://www.intercom.com", + Cookies: []Cookie{ + {Name: "intercom-id-*", Duration: "9 months", Description: "Anonymous visitor identifier for Intercom messenger."}, + {Name: "intercom-session-*", Duration: "1 week", Description: "Identifies sessions so that users can access their conversations."}, + }, + }, + { + Key: "linkedin-insight", + Name: "LinkedIn Insight", + Description: "Analytics tool for tracking conversions and retargeting from LinkedIn campaigns.", + Category: CategoryMarketing, + WebsiteURL: "https://business.linkedin.com", + Cookies: []Cookie{ + {Name: "li_sugr", Duration: "90 days", Description: "Used by LinkedIn for browser identification."}, + {Name: "bcookie", Duration: "1 year", Description: "LinkedIn browser identifier."}, + {Name: "lidc", Duration: "24 hours", Description: "Used for routing and data center selection."}, + {Name: "UserMatchHistory", Duration: "30 days", Description: "Used for LinkedIn Ads ID syncing."}, + }, + }, + { + Key: "stripe", + Name: "Stripe", + Description: "Payment processing platform for internet businesses.", + Category: CategoryNecessary, + WebsiteURL: "https://stripe.com", + Cookies: []Cookie{ + {Name: "__stripe_mid", Duration: "1 year", Description: "Used for fraud prevention and detection."}, + {Name: "__stripe_sid", Duration: "30 minutes", Description: "Used for fraud prevention and detection during a session."}, + }, + }, + { + Key: "cloudflare", + Name: "Cloudflare", + Description: "Web infrastructure and security company providing CDN, DDoS protection, and bot management.", + Category: CategoryNecessary, + WebsiteURL: "https://www.cloudflare.com", + Cookies: []Cookie{ + {Name: "__cf_bm", Duration: "30 minutes", Description: "Used by Cloudflare Bot Management to identify and distinguish bots from humans."}, + {Name: "cf_clearance", Duration: "30 minutes", Description: "Stores proof that a visitor has successfully completed a challenge."}, + }, + }, + { + Key: "cookiebot", + Name: "Cookiebot", + Description: "Cookie consent management platform for GDPR and ePrivacy compliance.", + Category: CategoryNecessary, + WebsiteURL: "https://www.cookiebot.com", + Cookies: []Cookie{ + {Name: "CookieConsent", Duration: "1 year", Description: "Stores the user's cookie consent state."}, + {Name: "CookieConsentBulkTicket", Duration: "1 year", Description: "Enables consent across multiple websites."}, + }, + }, + { + Key: "onetrust", + Name: "OneTrust", + Description: "Privacy management and cookie compliance platform.", + Category: CategoryNecessary, + WebsiteURL: "https://www.onetrust.com", + Cookies: []Cookie{ + {Name: "OptanonConsent", Duration: "1 year", Description: "Stores the user's cookie consent preferences."}, + {Name: "OptanonAlertBoxClosed", Duration: "1 year", Description: "Records that the cookie consent banner has been dismissed."}, + }, + }, + { + Key: "twitter-x", + Name: "Twitter / X", + Description: "Social media platform advertising and tracking pixels.", + Category: CategoryMarketing, + WebsiteURL: "https://ads.x.com", + Cookies: []Cookie{ + {Name: "muc_ads", Duration: "2 years", Description: "Used by Twitter/X for advertising purposes."}, + {Name: "guest_id", Duration: "2 years", Description: "Unique identifier for the visitor assigned by Twitter/X."}, + {Name: "personalization_id", Duration: "2 years", Description: "Used by Twitter/X to deliver personalized ads."}, + }, + }, + { + Key: "tiktok", + Name: "TikTok", + Description: "Social media platform advertising and tracking pixel for measuring ad performance.", + Category: CategoryMarketing, + WebsiteURL: "https://ads.tiktok.com", + Cookies: []Cookie{ + {Name: "_ttp", Duration: "13 months", Description: "Used by TikTok to track and attribute conversions."}, + {Name: "tt_webid", Duration: "13 months", Description: "Used by TikTok to identify the visitor."}, + {Name: "tt_webid_v2", Duration: "13 months", Description: "Used by TikTok to identify the visitor (updated version)."}, + }, + }, + { + Key: "zendesk", + Name: "Zendesk", + Description: "Customer service and engagement platform with live chat and support widgets.", + Category: CategoryPreferences, + WebsiteURL: "https://www.zendesk.com", + Cookies: []Cookie{ + {Name: "__zlcmid", Duration: "1 year", Description: "Used by Zendesk live chat to store a unique user identifier."}, + }, + }, + { + Key: "drift", + Name: "Drift", + Description: "Conversational marketing and sales platform for live chat and chatbots.", + Category: CategoryMarketing, + WebsiteURL: "https://www.drift.com", + Cookies: []Cookie{ + {Name: "drift_aid", Duration: "2 years", Description: "Anonymous identifier for the visitor."}, + {Name: "drift_campaign_refresh", Duration: "30 minutes", Description: "Controls campaign display frequency."}, + {Name: "driftt_aid", Duration: "2 years", Description: "Anonymous identifier used for tracking across sessions."}, + }, + }, + { + Key: "segment", + Name: "Segment", + Description: "Customer data platform for collecting, cleaning, and controlling customer data.", + Category: CategoryAnalytics, + WebsiteURL: "https://segment.com", + Cookies: []Cookie{ + {Name: "ajs_anonymous_id", Duration: "1 year", Description: "Anonymous user identifier for tracking before a user is identified."}, + {Name: "ajs_user_id", Duration: "1 year", Description: "Stores the identified user ID after identification."}, + }, + }, + { + Key: "mixpanel", + Name: "Mixpanel", + Description: "Product analytics platform for tracking user interactions and engagement.", + Category: CategoryAnalytics, + WebsiteURL: "https://mixpanel.com", + Cookies: []Cookie{ + {Name: "mp_*_mixpanel", Duration: "1 year", Description: "Stores tracking information for Mixpanel analytics."}, + }, + }, + { + Key: "amplitude", + Name: "Amplitude", + Description: "Product analytics platform for understanding user behavior and engagement.", + Category: CategoryAnalytics, + WebsiteURL: "https://amplitude.com", + Cookies: []Cookie{ + {Name: "AMP_*", Duration: "1 year", Description: "Stores user and session information for Amplitude analytics."}, + }, + }, + { + Key: "microsoft-clarity", + Name: "Microsoft Clarity", + Description: "Free analytics tool that captures how users interact with your site via session recordings and heatmaps.", + Category: CategoryAnalytics, + WebsiteURL: "https://clarity.microsoft.com", + Cookies: []Cookie{ + {Name: "_clck", Duration: "1 year", Description: "Persists the Clarity user ID and preferences."}, + {Name: "_clsk", Duration: "1 day", Description: "Connects multiple page views by a user into a single session recording."}, + {Name: "CLID", Duration: "1 year", Description: "Identifies the first-time Clarity saw this user on any site."}, + }, + }, +} diff --git a/pkg/cookieprovider/cookieprovider_test.go b/pkg/cookieprovider/cookieprovider_test.go new file mode 100644 index 000000000..c9a47e439 --- /dev/null +++ b/pkg/cookieprovider/cookieprovider_test.go @@ -0,0 +1,114 @@ +// Copyright (c) 2026 Probo Inc . +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR +// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +// PERFORMANCE OF THIS SOFTWARE. + +package cookieprovider + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestAll(t *testing.T) { + t.Parallel() + + providers := All() + require.NotEmpty(t, providers) + assert.Len(t, providers, 20) + + for _, p := range providers { + assert.NotEmpty(t, p.Key, "provider key should not be empty") + assert.NotEmpty(t, p.Name, "provider name should not be empty") + assert.NotEmpty(t, p.Description, "provider description should not be empty") + assert.NotEmpty(t, p.Category, "provider category should not be empty") + assert.NotEmpty(t, p.WebsiteURL, "provider website URL should not be empty") + assert.NotEmpty(t, p.Cookies, "provider %s should have at least one cookie", p.Key) + } +} + +func TestByKey(t *testing.T) { + t.Parallel() + + t.Run("existing provider", func(t *testing.T) { + t.Parallel() + + p, ok := ByKey("google-analytics") + require.True(t, ok) + assert.Equal(t, "Google Analytics", p.Name) + assert.Equal(t, CategoryAnalytics, p.Category) + assert.NotEmpty(t, p.Cookies) + }) + + t.Run("non-existing provider", func(t *testing.T) { + t.Parallel() + + _, ok := ByKey("nonexistent") + assert.False(t, ok) + }) +} + +func TestByCategory(t *testing.T) { + t.Parallel() + + analytics := ByCategory(CategoryAnalytics) + require.NotEmpty(t, analytics) + + for _, p := range analytics { + assert.Equal(t, CategoryAnalytics, p.Category) + } + + marketing := ByCategory(CategoryMarketing) + require.NotEmpty(t, marketing) + + for _, p := range marketing { + assert.Equal(t, CategoryMarketing, p.Category) + } +} + +func TestCookieItems(t *testing.T) { + t.Parallel() + + p, ok := ByKey("google-analytics") + require.True(t, ok) + + items := p.CookieItems() + require.Len(t, items, len(p.Cookies)) + + for i, item := range items { + assert.Equal(t, p.Cookies[i].Name, item.Name) + assert.Equal(t, p.Cookies[i].Duration, item.Duration) + assert.Equal(t, p.Cookies[i].Description, item.Description) + } +} + +func TestAllReturnsACopy(t *testing.T) { + t.Parallel() + + p1 := All() + p2 := All() + + p1[0].Name = "modified" + assert.NotEqual(t, p1[0].Name, p2[0].Name) +} + +func TestUniqueKeys(t *testing.T) { + t.Parallel() + + seen := make(map[string]bool) + for _, p := range All() { + assert.False(t, seen[p.Key], "duplicate provider key: %s", p.Key) + seen[p.Key] = true + } +} diff --git a/pkg/coredata/consent_action.go b/pkg/coredata/consent_action.go new file mode 100644 index 000000000..682ad5fb0 --- /dev/null +++ b/pkg/coredata/consent_action.go @@ -0,0 +1,86 @@ +// Copyright (c) 2025-2026 Probo Inc . +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR +// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +// PERFORMANCE OF THIS SOFTWARE. + +package coredata + +import ( + "database/sql/driver" + "fmt" +) + +type ConsentAction string + +const ( + ConsentActionAcceptAll ConsentAction = "ACCEPT_ALL" + ConsentActionRejectAll ConsentAction = "REJECT_ALL" + ConsentActionCustomize ConsentAction = "CUSTOMIZE" + ConsentActionAcceptCategory ConsentAction = "ACCEPT_CATEGORY" + ConsentActionGPC ConsentAction = "GPC" +) + +func ConsentActions() []ConsentAction { + return []ConsentAction{ + ConsentActionAcceptAll, + ConsentActionRejectAll, + ConsentActionCustomize, + ConsentActionAcceptCategory, + ConsentActionGPC, + } +} + +func (a ConsentAction) String() string { + return string(a) +} + +func (a *ConsentAction) Scan(value any) error { + var v string + switch val := value.(type) { + case string: + v = val + case []byte: + v = string(val) + default: + return fmt.Errorf("unsupported type for ConsentAction: %T", value) + } + + switch ConsentAction(v) { + case ConsentActionAcceptAll: + *a = ConsentActionAcceptAll + case ConsentActionRejectAll: + *a = ConsentActionRejectAll + case ConsentActionCustomize: + *a = ConsentActionCustomize + case ConsentActionAcceptCategory: + *a = ConsentActionAcceptCategory + case ConsentActionGPC: + *a = ConsentActionGPC + default: + return fmt.Errorf("invalid ConsentAction value: %q", v) + } + + return nil +} + +func (a ConsentAction) Value() (driver.Value, error) { + switch a { + case ConsentActionAcceptAll, + ConsentActionRejectAll, + ConsentActionCustomize, + ConsentActionAcceptCategory, + ConsentActionGPC: + return string(a), nil + default: + return nil, fmt.Errorf("invalid ConsentAction: %s", a) + } +} diff --git a/pkg/coredata/consent_mode.go b/pkg/coredata/consent_mode.go new file mode 100644 index 000000000..db3457478 --- /dev/null +++ b/pkg/coredata/consent_mode.go @@ -0,0 +1,71 @@ +// Copyright (c) 2025-2026 Probo Inc . +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR +// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +// PERFORMANCE OF THIS SOFTWARE. + +package coredata + +import ( + "database/sql/driver" + "fmt" +) + +type ConsentMode string + +const ( + ConsentModeOptIn ConsentMode = "OPT_IN" + ConsentModeOptOut ConsentMode = "OPT_OUT" +) + +func ConsentModes() []ConsentMode { + return []ConsentMode{ + ConsentModeOptIn, + ConsentModeOptOut, + } +} + +func (m ConsentMode) String() string { + return string(m) +} + +func (m *ConsentMode) Scan(value any) error { + var v string + switch val := value.(type) { + case string: + v = val + case []byte: + v = string(val) + default: + return fmt.Errorf("unsupported type for ConsentMode: %T", value) + } + + switch ConsentMode(v) { + case ConsentModeOptIn: + *m = ConsentModeOptIn + case ConsentModeOptOut: + *m = ConsentModeOptOut + default: + return fmt.Errorf("invalid ConsentMode value: %q", v) + } + + return nil +} + +func (m ConsentMode) Value() (driver.Value, error) { + switch m { + case ConsentModeOptIn, + ConsentModeOptOut: + return string(m), nil + default: + return nil, fmt.Errorf("invalid ConsentMode: %s", m) + } +} diff --git a/pkg/coredata/consent_record.go b/pkg/coredata/consent_record.go new file mode 100644 index 000000000..0f98f1aa0 --- /dev/null +++ b/pkg/coredata/consent_record.go @@ -0,0 +1,261 @@ +// Copyright (c) 2025-2026 Probo Inc . +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR +// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +// PERFORMANCE OF THIS SOFTWARE. + +package coredata + +import ( + "context" + "encoding/json" + "errors" + "fmt" + "maps" + "time" + + "github.com/jackc/pgx/v5" + "go.gearno.de/kit/pg" + "go.probo.inc/probo/pkg/gid" + "go.probo.inc/probo/pkg/page" +) + +type ( + ConsentRecord struct { + ID gid.GID `db:"id"` + CookieBannerID gid.GID `db:"cookie_banner_id"` + VisitorID string `db:"visitor_id"` + IPAddress *string `db:"ip_address"` + UserAgent *string `db:"user_agent"` + ConsentData json.RawMessage `db:"consent_data"` + Action ConsentAction `db:"action"` + BannerVersion int `db:"banner_version"` + CreatedAt time.Time `db:"created_at"` + } + + ConsentRecords []*ConsentRecord +) + +func (r *ConsentRecord) CursorKey(field ConsentRecordOrderField) page.CursorKey { + switch field { + case ConsentRecordOrderFieldCreatedAt: + return page.NewCursorKey(r.ID, r.CreatedAt) + } + + panic(fmt.Sprintf("unsupported order by: %s", field)) +} + +func (r *ConsentRecord) AuthorizationAttributes(ctx context.Context, conn pg.Querier) (map[string]string, error) { + q := ` +SELECT cb.organization_id +FROM consent_records cr +JOIN cookie_banners cb ON cr.cookie_banner_id = cb.id +WHERE cr.id = $1 +LIMIT 1; +` + + var organizationID gid.GID + if err := conn.QueryRow(ctx, q, r.ID).Scan(&organizationID); err != nil { + if errors.Is(err, pgx.ErrNoRows) { + return nil, ErrResourceNotFound + } + return nil, fmt.Errorf("cannot query consent record authorization attributes: %w", err) + } + + return map[string]string{"organization_id": organizationID.String()}, nil +} + +func (r *ConsentRecords) LoadByCookieBannerID( + ctx context.Context, + conn pg.Querier, + scope Scoper, + cookieBannerID gid.GID, + cursor *page.Cursor[ConsentRecordOrderField], + filter *ConsentRecordFilter, +) error { + q := ` +SELECT + id, + cookie_banner_id, + visitor_id, + ip_address, + user_agent, + consent_data, + action, + banner_version, + created_at +FROM + consent_records +WHERE + %s + AND cookie_banner_id = @cookie_banner_id + AND %s + AND %s +` + + q = fmt.Sprintf(q, scope.SQLFragment(), filter.SQLFragment(), cursor.SQLFragment()) + + args := pgx.StrictNamedArgs{"cookie_banner_id": cookieBannerID} + maps.Copy(args, scope.SQLArguments()) + maps.Copy(args, filter.SQLArguments()) + maps.Copy(args, cursor.SQLArguments()) + + rows, err := conn.Query(ctx, q, args) + if err != nil { + return fmt.Errorf("cannot query consent records: %w", err) + } + + records, err := pgx.CollectRows(rows, pgx.RowToAddrOfStructByName[ConsentRecord]) + if err != nil { + return fmt.Errorf("cannot collect consent records: %w", err) + } + + *r = records + + return nil +} + +func (r *ConsentRecords) CountByCookieBannerID( + ctx context.Context, + conn pg.Querier, + scope Scoper, + cookieBannerID gid.GID, + filter *ConsentRecordFilter, +) (int, error) { + q := ` +SELECT + COUNT(id) +FROM + consent_records +WHERE + %s + AND cookie_banner_id = @cookie_banner_id + AND %s +` + + q = fmt.Sprintf(q, scope.SQLFragment(), filter.SQLFragment()) + + args := pgx.StrictNamedArgs{"cookie_banner_id": cookieBannerID} + maps.Copy(args, scope.SQLArguments()) + maps.Copy(args, filter.SQLArguments()) + + row := conn.QueryRow(ctx, q, args) + + var count int + if err := row.Scan(&count); err != nil { + return 0, fmt.Errorf("cannot scan count: %w", err) + } + + return count, nil +} + +type ConsentAnalytics struct { + TotalRecords int + AcceptAllCount int + RejectAllCount int + CustomizeCount int + AcceptCategoryCount int + GPCCount int +} + +func (a *ConsentAnalytics) LoadByCookieBannerID( + ctx context.Context, + conn pg.Querier, + scope Scoper, + cookieBannerID gid.GID, +) error { + q := ` +SELECT + COUNT(*) AS total_records, + COUNT(*) FILTER (WHERE action = 'ACCEPT_ALL') AS accept_all_count, + COUNT(*) FILTER (WHERE action = 'REJECT_ALL') AS reject_all_count, + COUNT(*) FILTER (WHERE action = 'CUSTOMIZE') AS customize_count, + COUNT(*) FILTER (WHERE action = 'ACCEPT_CATEGORY') AS accept_category_count, + COUNT(*) FILTER (WHERE action = 'GPC') AS gpc_count +FROM + consent_records +WHERE + %s + AND cookie_banner_id = @cookie_banner_id +` + + q = fmt.Sprintf(q, scope.SQLFragment()) + + args := pgx.StrictNamedArgs{"cookie_banner_id": cookieBannerID} + maps.Copy(args, scope.SQLArguments()) + + row := conn.QueryRow(ctx, q, args) + + if err := row.Scan( + &a.TotalRecords, + &a.AcceptAllCount, + &a.RejectAllCount, + &a.CustomizeCount, + &a.AcceptCategoryCount, + &a.GPCCount, + ); err != nil { + return fmt.Errorf("cannot scan consent analytics: %w", err) + } + + return nil +} + +func (r *ConsentRecord) Insert( + ctx context.Context, + conn pg.Querier, + scope Scoper, +) error { + q := ` +INSERT INTO consent_records ( + id, + tenant_id, + cookie_banner_id, + visitor_id, + ip_address, + user_agent, + consent_data, + action, + banner_version, + created_at +) VALUES ( + @id, + @tenant_id, + @cookie_banner_id, + @visitor_id, + @ip_address, + @user_agent, + @consent_data, + @action, + @banner_version, + @created_at +) +` + + args := pgx.StrictNamedArgs{ + "id": r.ID, + "tenant_id": scope.GetTenantID(), + "cookie_banner_id": r.CookieBannerID, + "visitor_id": r.VisitorID, + "ip_address": r.IPAddress, + "user_agent": r.UserAgent, + "consent_data": r.ConsentData, + "action": r.Action, + "banner_version": r.BannerVersion, + "created_at": r.CreatedAt, + } + + _, err := conn.Exec(ctx, q, args) + if err != nil { + return fmt.Errorf("cannot insert consent record: %w", err) + } + + return nil +} diff --git a/pkg/coredata/consent_record_filter.go b/pkg/coredata/consent_record_filter.go new file mode 100644 index 000000000..a1f120cd0 --- /dev/null +++ b/pkg/coredata/consent_record_filter.go @@ -0,0 +1,50 @@ +// Copyright (c) 2025-2026 Probo Inc . +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR +// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +// PERFORMANCE OF THIS SOFTWARE. + +package coredata + +import "github.com/jackc/pgx/v5" + +type ConsentRecordFilter struct { + action *ConsentAction +} + +func NewConsentRecordFilter(action *ConsentAction) *ConsentRecordFilter { + return &ConsentRecordFilter{ + action: action, + } +} + +func (f *ConsentRecordFilter) SQLFragment() string { + return ` +( + CASE + WHEN @filter_action::text IS NOT NULL THEN + action = @filter_action::consent_action + ELSE TRUE + END +)` +} + +func (f *ConsentRecordFilter) SQLArguments() pgx.StrictNamedArgs { + args := pgx.StrictNamedArgs{ + "filter_action": nil, + } + + if f.action != nil { + args["filter_action"] = string(*f.action) + } + + return args +} diff --git a/pkg/coredata/consent_record_order_field.go b/pkg/coredata/consent_record_order_field.go new file mode 100644 index 000000000..712010338 --- /dev/null +++ b/pkg/coredata/consent_record_order_field.go @@ -0,0 +1,55 @@ +// Copyright (c) 2025-2026 Probo Inc . +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR +// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +// PERFORMANCE OF THIS SOFTWARE. + +package coredata + +import "fmt" + +type ConsentRecordOrderField string + +const ( + ConsentRecordOrderFieldCreatedAt ConsentRecordOrderField = "CREATED_AT" +) + +func (p ConsentRecordOrderField) Column() string { + switch p { + case ConsentRecordOrderFieldCreatedAt: + return "created_at" + } + panic(fmt.Sprintf("unsupported order by: %s", p)) +} + +func (p ConsentRecordOrderField) IsValid() bool { + switch p { + case ConsentRecordOrderFieldCreatedAt: + return true + } + return false +} + +func (p ConsentRecordOrderField) String() string { + return string(p) +} + +func (p *ConsentRecordOrderField) UnmarshalText(text []byte) error { + *p = ConsentRecordOrderField(text) + if !p.IsValid() { + return fmt.Errorf("%s is not a valid ConsentRecordOrderField", string(text)) + } + return nil +} + +func (p ConsentRecordOrderField) MarshalText() ([]byte, error) { + return []byte(p.String()), nil +} diff --git a/pkg/coredata/cookie_banner.go b/pkg/coredata/cookie_banner.go new file mode 100644 index 000000000..65ff3a23a --- /dev/null +++ b/pkg/coredata/cookie_banner.go @@ -0,0 +1,430 @@ +// Copyright (c) 2025-2026 Probo Inc . +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR +// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +// PERFORMANCE OF THIS SOFTWARE. + +package coredata + +import ( + "context" + "encoding/json" + "errors" + "fmt" + "maps" + "time" + + "github.com/jackc/pgx/v5" + "go.gearno.de/kit/pg" + "go.probo.inc/probo/pkg/gid" + "go.probo.inc/probo/pkg/page" +) + +type ( + CookieBanner struct { + ID gid.GID `db:"id"` + OrganizationID gid.GID `db:"organization_id"` + Name string `db:"name"` + Domain string `db:"domain"` + State CookieBannerState `db:"state"` + Title string `db:"title"` + Description string `db:"description"` + AcceptAllLabel string `db:"accept_all_label"` + RejectAllLabel string `db:"reject_all_label"` + SavePreferencesLabel string `db:"save_preferences_label"` + PrivacyPolicyURL string `db:"privacy_policy_url"` + ConsentExpiryDays int `db:"consent_expiry_days"` + ConsentMode ConsentMode `db:"consent_mode"` + Version int `db:"version"` + Theme json.RawMessage `db:"theme"` + CreatedAt time.Time `db:"created_at"` + UpdatedAt time.Time `db:"updated_at"` + } + + CookieBanners []*CookieBanner +) + +func (b *CookieBanner) CursorKey(field CookieBannerOrderField) page.CursorKey { + switch field { + case CookieBannerOrderFieldCreatedAt: + return page.NewCursorKey(b.ID, b.CreatedAt) + } + + panic(fmt.Sprintf("unsupported order by: %s", field)) +} + +func (b *CookieBanner) AuthorizationAttributes(ctx context.Context, conn pg.Querier) (map[string]string, error) { + q := `SELECT organization_id FROM cookie_banners WHERE id = $1 LIMIT 1;` + + var organizationID gid.GID + if err := conn.QueryRow(ctx, q, b.ID).Scan(&organizationID); err != nil { + if errors.Is(err, pgx.ErrNoRows) { + return nil, ErrResourceNotFound + } + + return nil, fmt.Errorf("cannot query cookie banner authorization attributes: %w", err) + } + + return map[string]string{"organization_id": organizationID.String()}, nil +} + +func (b *CookieBanner) LoadByID( + ctx context.Context, + conn pg.Querier, + scope Scoper, + bannerID gid.GID, +) error { + q := ` +SELECT + id, + organization_id, + name, + domain, + state, + title, + description, + accept_all_label, + reject_all_label, + save_preferences_label, + privacy_policy_url, + consent_expiry_days, + consent_mode, + version, + theme, + created_at, + updated_at +FROM + cookie_banners +WHERE + %s + AND id = @banner_id +LIMIT 1; +` + + q = fmt.Sprintf(q, scope.SQLFragment()) + + args := pgx.StrictNamedArgs{"banner_id": bannerID} + maps.Copy(args, scope.SQLArguments()) + + rows, err := conn.Query(ctx, q, args) + if err != nil { + return fmt.Errorf("cannot query cookie banners: %w", err) + } + + banner, err := pgx.CollectExactlyOneRow(rows, pgx.RowToStructByName[CookieBanner]) + if err != nil { + if errors.Is(err, pgx.ErrNoRows) { + return ErrResourceNotFound + } + + return fmt.Errorf("cannot collect cookie banner: %w", err) + } + + *b = banner + + return nil +} + +func (b *CookieBanner) LoadPublishedByID( + ctx context.Context, + conn pg.Querier, + bannerID gid.GID, +) error { + q := ` +SELECT + id, + organization_id, + name, + domain, + state, + title, + description, + accept_all_label, + reject_all_label, + save_preferences_label, + privacy_policy_url, + consent_expiry_days, + consent_mode, + version, + theme, + created_at, + updated_at +FROM + cookie_banners +WHERE + id = @banner_id + AND state = 'PUBLISHED' +LIMIT 1; +` + + args := pgx.StrictNamedArgs{"banner_id": bannerID} + + rows, err := conn.Query(ctx, q, args) + if err != nil { + return fmt.Errorf("cannot query cookie banners: %w", err) + } + + banner, err := pgx.CollectExactlyOneRow(rows, pgx.RowToStructByName[CookieBanner]) + if err != nil { + if errors.Is(err, pgx.ErrNoRows) { + return ErrResourceNotFound + } + + return fmt.Errorf("cannot collect cookie banner: %w", err) + } + + *b = banner + + return nil +} + +func (b *CookieBanners) LoadByOrganizationID( + ctx context.Context, + conn pg.Querier, + scope Scoper, + organizationID gid.GID, + cursor *page.Cursor[CookieBannerOrderField], +) error { + q := ` +SELECT + id, + organization_id, + name, + domain, + state, + title, + description, + accept_all_label, + reject_all_label, + save_preferences_label, + privacy_policy_url, + consent_expiry_days, + consent_mode, + version, + theme, + created_at, + updated_at +FROM + cookie_banners +WHERE + %s + AND organization_id = @organization_id + AND %s +` + + q = fmt.Sprintf(q, scope.SQLFragment(), cursor.SQLFragment()) + + args := pgx.StrictNamedArgs{"organization_id": organizationID} + maps.Copy(args, scope.SQLArguments()) + maps.Copy(args, cursor.SQLArguments()) + + rows, err := conn.Query(ctx, q, args) + if err != nil { + return fmt.Errorf("cannot query cookie banners: %w", err) + } + + banners, err := pgx.CollectRows(rows, pgx.RowToAddrOfStructByName[CookieBanner]) + if err != nil { + return fmt.Errorf("cannot collect cookie banners: %w", err) + } + + *b = banners + + return nil +} + +func (b *CookieBanners) CountByOrganizationID( + ctx context.Context, + conn pg.Querier, + scope Scoper, + organizationID gid.GID, +) (int, error) { + q := ` +SELECT + COUNT(id) +FROM + cookie_banners +WHERE + %s + AND organization_id = @organization_id +` + + q = fmt.Sprintf(q, scope.SQLFragment()) + + args := pgx.StrictNamedArgs{"organization_id": organizationID} + maps.Copy(args, scope.SQLArguments()) + + row := conn.QueryRow(ctx, q, args) + + var count int + if err := row.Scan(&count); err != nil { + return 0, fmt.Errorf("cannot scan count: %w", err) + } + + return count, nil +} + +func (b *CookieBanner) Insert( + ctx context.Context, + conn pg.Querier, + scope Scoper, +) error { + q := ` +INSERT INTO cookie_banners ( + id, + tenant_id, + organization_id, + name, + domain, + state, + title, + description, + accept_all_label, + reject_all_label, + save_preferences_label, + privacy_policy_url, + consent_expiry_days, + consent_mode, + version, + theme, + created_at, + updated_at +) VALUES ( + @id, + @tenant_id, + @organization_id, + @name, + @domain, + @state, + @title, + @description, + @accept_all_label, + @reject_all_label, + @save_preferences_label, + @privacy_policy_url, + @consent_expiry_days, + @consent_mode, + @version, + @theme, + @created_at, + @updated_at +) +` + + args := pgx.StrictNamedArgs{ + "id": b.ID, + "tenant_id": scope.GetTenantID(), + "organization_id": b.OrganizationID, + "name": b.Name, + "domain": b.Domain, + "state": b.State, + "title": b.Title, + "description": b.Description, + "accept_all_label": b.AcceptAllLabel, + "reject_all_label": b.RejectAllLabel, + "save_preferences_label": b.SavePreferencesLabel, + "privacy_policy_url": b.PrivacyPolicyURL, + "consent_expiry_days": b.ConsentExpiryDays, + "consent_mode": b.ConsentMode, + "version": b.Version, + "theme": b.Theme, + "created_at": b.CreatedAt, + "updated_at": b.UpdatedAt, + } + + _, err := conn.Exec(ctx, q, args) + if err != nil { + return fmt.Errorf("cannot insert cookie banner: %w", err) + } + + return nil +} + +func (b *CookieBanner) Update( + ctx context.Context, + conn pg.Querier, + scope Scoper, +) error { + q := ` +UPDATE cookie_banners +SET + name = @name, + domain = @domain, + state = @state, + title = @title, + description = @description, + accept_all_label = @accept_all_label, + reject_all_label = @reject_all_label, + save_preferences_label = @save_preferences_label, + privacy_policy_url = @privacy_policy_url, + consent_expiry_days = @consent_expiry_days, + consent_mode = @consent_mode, + version = @version, + theme = @theme, + updated_at = @updated_at +WHERE + %s + AND id = @id +` + + q = fmt.Sprintf(q, scope.SQLFragment()) + + args := pgx.StrictNamedArgs{ + "id": b.ID, + "name": b.Name, + "domain": b.Domain, + "state": b.State, + "title": b.Title, + "description": b.Description, + "accept_all_label": b.AcceptAllLabel, + "reject_all_label": b.RejectAllLabel, + "save_preferences_label": b.SavePreferencesLabel, + "privacy_policy_url": b.PrivacyPolicyURL, + "consent_expiry_days": b.ConsentExpiryDays, + "consent_mode": b.ConsentMode, + "version": b.Version, + "theme": b.Theme, + "updated_at": b.UpdatedAt, + } + maps.Copy(args, scope.SQLArguments()) + + _, err := conn.Exec(ctx, q, args) + if err != nil { + return fmt.Errorf("cannot update cookie banner: %w", err) + } + + return nil +} + +func (b *CookieBanner) Delete( + ctx context.Context, + conn pg.Querier, + scope Scoper, +) error { + q := ` +DELETE FROM cookie_banners +WHERE + %s + AND id = @id +` + + q = fmt.Sprintf(q, scope.SQLFragment()) + + args := pgx.StrictNamedArgs{"id": b.ID} + maps.Copy(args, scope.SQLArguments()) + + _, err := conn.Exec(ctx, q, args) + if err != nil { + return fmt.Errorf("cannot delete cookie banner: %w", err) + } + + return nil +} diff --git a/pkg/coredata/cookie_banner_order_field.go b/pkg/coredata/cookie_banner_order_field.go new file mode 100644 index 000000000..52dd47393 --- /dev/null +++ b/pkg/coredata/cookie_banner_order_field.go @@ -0,0 +1,55 @@ +// Copyright (c) 2025-2026 Probo Inc . +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR +// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +// PERFORMANCE OF THIS SOFTWARE. + +package coredata + +import "fmt" + +type CookieBannerOrderField string + +const ( + CookieBannerOrderFieldCreatedAt CookieBannerOrderField = "CREATED_AT" +) + +func (p CookieBannerOrderField) Column() string { + switch p { + case CookieBannerOrderFieldCreatedAt: + return "created_at" + } + panic(fmt.Sprintf("unsupported order by: %s", p)) +} + +func (p CookieBannerOrderField) IsValid() bool { + switch p { + case CookieBannerOrderFieldCreatedAt: + return true + } + return false +} + +func (p CookieBannerOrderField) String() string { + return string(p) +} + +func (p *CookieBannerOrderField) UnmarshalText(text []byte) error { + *p = CookieBannerOrderField(text) + if !p.IsValid() { + return fmt.Errorf("%s is not a valid CookieBannerOrderField", string(text)) + } + return nil +} + +func (p CookieBannerOrderField) MarshalText() ([]byte, error) { + return []byte(p.String()), nil +} diff --git a/pkg/coredata/cookie_banner_state.go b/pkg/coredata/cookie_banner_state.go new file mode 100644 index 000000000..5ac6efc75 --- /dev/null +++ b/pkg/coredata/cookie_banner_state.go @@ -0,0 +1,75 @@ +// Copyright (c) 2025-2026 Probo Inc . +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR +// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +// PERFORMANCE OF THIS SOFTWARE. + +package coredata + +import ( + "database/sql/driver" + "fmt" +) + +type CookieBannerState string + +const ( + CookieBannerStateDraft CookieBannerState = "DRAFT" + CookieBannerStatePublished CookieBannerState = "PUBLISHED" + CookieBannerStateDisabled CookieBannerState = "DISABLED" +) + +func CookieBannerStates() []CookieBannerState { + return []CookieBannerState{ + CookieBannerStateDraft, + CookieBannerStatePublished, + CookieBannerStateDisabled, + } +} + +func (s CookieBannerState) String() string { + return string(s) +} + +func (s *CookieBannerState) Scan(value any) error { + var v string + switch val := value.(type) { + case string: + v = val + case []byte: + v = string(val) + default: + return fmt.Errorf("unsupported type for CookieBannerState: %T", value) + } + + switch CookieBannerState(v) { + case CookieBannerStateDraft: + *s = CookieBannerStateDraft + case CookieBannerStatePublished: + *s = CookieBannerStatePublished + case CookieBannerStateDisabled: + *s = CookieBannerStateDisabled + default: + return fmt.Errorf("invalid CookieBannerState value: %q", v) + } + return nil +} + +func (s CookieBannerState) Value() (driver.Value, error) { + switch s { + case CookieBannerStateDraft, + CookieBannerStatePublished, + CookieBannerStateDisabled: + return string(s), nil + default: + return nil, fmt.Errorf("invalid CookieBannerState: %s", s) + } +} diff --git a/pkg/coredata/cookie_category.go b/pkg/coredata/cookie_category.go new file mode 100644 index 000000000..bbb500c11 --- /dev/null +++ b/pkg/coredata/cookie_category.go @@ -0,0 +1,396 @@ +// Copyright (c) 2025-2026 Probo Inc . +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR +// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +// PERFORMANCE OF THIS SOFTWARE. + +package coredata + +import ( + "context" + "encoding/json" + "errors" + "fmt" + "maps" + "time" + + "github.com/jackc/pgx/v5" + "go.gearno.de/kit/pg" + "go.probo.inc/probo/pkg/gid" + "go.probo.inc/probo/pkg/page" +) + +type ( + CookieItem struct { + Name string `json:"name"` + Duration string `json:"duration"` + Description string `json:"description"` + } + + CookieItems []CookieItem + + CookieCategory struct { + ID gid.GID `db:"id"` + CookieBannerID gid.GID `db:"cookie_banner_id"` + Name string `db:"name"` + Description string `db:"description"` + Required bool `db:"required"` + Rank int `db:"rank"` + Cookies CookieItems `db:"cookies"` + CreatedAt time.Time `db:"created_at"` + UpdatedAt time.Time `db:"updated_at"` + } + + CookieCategories []*CookieCategory +) + +func (c CookieItems) MarshalJSON() ([]byte, error) { + if c == nil { + return []byte("[]"), nil + } + return json.Marshal([]CookieItem(c)) +} + +func (c *CookieItems) UnmarshalJSON(data []byte) error { + if len(data) == 0 || string(data) == "null" { + *c = CookieItems{} + return nil + } + return json.Unmarshal(data, (*[]CookieItem)(c)) +} + +func (c *CookieCategory) CursorKey(field CookieCategoryOrderField) page.CursorKey { + switch field { + case CookieCategoryOrderFieldRank: + return page.NewCursorKey(c.ID, c.Rank) + } + + panic(fmt.Sprintf("unsupported order by: %s", field)) +} + +func (c *CookieCategory) AuthorizationAttributes(ctx context.Context, conn pg.Querier) (map[string]string, error) { + q := ` +SELECT cb.organization_id +FROM cookie_categories cc +JOIN cookie_banners cb ON cc.cookie_banner_id = cb.id +WHERE cc.id = $1 +LIMIT 1; +` + + var organizationID gid.GID + if err := conn.QueryRow(ctx, q, c.ID).Scan(&organizationID); err != nil { + if errors.Is(err, pgx.ErrNoRows) { + return nil, ErrResourceNotFound + } + return nil, fmt.Errorf("cannot query cookie category authorization attributes: %w", err) + } + + return map[string]string{"organization_id": organizationID.String()}, nil +} + +func (c *CookieCategory) LoadByID( + ctx context.Context, + conn pg.Querier, + scope Scoper, + categoryID gid.GID, +) error { + q := ` +SELECT + id, + cookie_banner_id, + name, + description, + required, + rank, + cookies, + created_at, + updated_at +FROM + cookie_categories +WHERE + %s + AND id = @category_id +LIMIT 1; +` + + q = fmt.Sprintf(q, scope.SQLFragment()) + + args := pgx.StrictNamedArgs{"category_id": categoryID} + maps.Copy(args, scope.SQLArguments()) + + rows, err := conn.Query(ctx, q, args) + if err != nil { + return fmt.Errorf("cannot query cookie categories: %w", err) + } + + category, err := pgx.CollectExactlyOneRow(rows, pgx.RowToStructByName[CookieCategory]) + if err != nil { + if errors.Is(err, pgx.ErrNoRows) { + return ErrResourceNotFound + } + return fmt.Errorf("cannot collect cookie category: %w", err) + } + + *c = category + + return nil +} + +func (c *CookieCategories) LoadByCookieBannerID( + ctx context.Context, + conn pg.Querier, + scope Scoper, + cookieBannerID gid.GID, + cursor *page.Cursor[CookieCategoryOrderField], +) error { + q := ` +SELECT + id, + cookie_banner_id, + name, + description, + required, + rank, + cookies, + created_at, + updated_at +FROM + cookie_categories +WHERE + %s + AND cookie_banner_id = @cookie_banner_id + AND %s +` + + q = fmt.Sprintf(q, scope.SQLFragment(), cursor.SQLFragment()) + + args := pgx.StrictNamedArgs{"cookie_banner_id": cookieBannerID} + maps.Copy(args, scope.SQLArguments()) + maps.Copy(args, cursor.SQLArguments()) + + rows, err := conn.Query(ctx, q, args) + if err != nil { + return fmt.Errorf("cannot query cookie categories: %w", err) + } + + categories, err := pgx.CollectRows(rows, pgx.RowToAddrOfStructByName[CookieCategory]) + if err != nil { + return fmt.Errorf("cannot collect cookie categories: %w", err) + } + + *c = categories + + return nil +} + +func (c *CookieCategories) CountByCookieBannerID( + ctx context.Context, + conn pg.Querier, + scope Scoper, + cookieBannerID gid.GID, +) (int, error) { + q := ` +SELECT + COUNT(id) +FROM + cookie_categories +WHERE + %s + AND cookie_banner_id = @cookie_banner_id +` + + q = fmt.Sprintf(q, scope.SQLFragment()) + + args := pgx.StrictNamedArgs{"cookie_banner_id": cookieBannerID} + maps.Copy(args, scope.SQLArguments()) + + row := conn.QueryRow(ctx, q, args) + + var count int + if err := row.Scan(&count); err != nil { + return 0, fmt.Errorf("cannot scan count: %w", err) + } + + return count, nil +} + +func (c *CookieCategories) LoadAllPublicByCookieBannerID( + ctx context.Context, + conn pg.Querier, + cookieBannerID gid.GID, +) error { + q := ` +SELECT + id, + cookie_banner_id, + name, + description, + required, + rank, + cookies, + created_at, + updated_at +FROM + cookie_categories +WHERE + cookie_banner_id = @cookie_banner_id +ORDER BY + rank ASC, id ASC; +` + + args := pgx.StrictNamedArgs{"cookie_banner_id": cookieBannerID} + + rows, err := conn.Query(ctx, q, args) + if err != nil { + return fmt.Errorf("cannot query cookie categories: %w", err) + } + + categories, err := pgx.CollectRows(rows, pgx.RowToAddrOfStructByName[CookieCategory]) + if err != nil { + return fmt.Errorf("cannot collect cookie categories: %w", err) + } + + *c = categories + + return nil +} + +func (c *CookieCategory) Insert( + ctx context.Context, + conn pg.Querier, + scope Scoper, +) error { + q := ` +INSERT INTO cookie_categories ( + id, + tenant_id, + cookie_banner_id, + name, + description, + required, + rank, + cookies, + created_at, + updated_at +) VALUES ( + @id, + @tenant_id, + @cookie_banner_id, + @name, + @description, + @required, + @rank, + @cookies, + @created_at, + @updated_at +) +` + + args := pgx.StrictNamedArgs{ + "id": c.ID, + "tenant_id": scope.GetTenantID(), + "cookie_banner_id": c.CookieBannerID, + "name": c.Name, + "description": c.Description, + "required": c.Required, + "rank": c.Rank, + "cookies": c.Cookies, + "created_at": c.CreatedAt, + "updated_at": c.UpdatedAt, + } + + _, err := conn.Exec(ctx, q, args) + if err != nil { + return fmt.Errorf("cannot insert cookie category: %w", err) + } + + return nil +} + +func (c *CookieCategory) Update( + ctx context.Context, + conn pg.Querier, + scope Scoper, +) error { + q := ` +UPDATE cookie_categories +SET + name = @name, + description = @description, + rank = @rank, + cookies = @cookies, + updated_at = @updated_at +WHERE + %s + AND id = @id +RETURNING + id, + cookie_banner_id, + name, + description, + required, + rank, + cookies, + created_at, + updated_at +` + + q = fmt.Sprintf(q, scope.SQLFragment()) + + args := pgx.StrictNamedArgs{ + "id": c.ID, + "name": c.Name, + "description": c.Description, + "rank": c.Rank, + "cookies": c.Cookies, + "updated_at": c.UpdatedAt, + } + maps.Copy(args, scope.SQLArguments()) + + rows, err := conn.Query(ctx, q, args) + if err != nil { + return fmt.Errorf("cannot update cookie category: %w", err) + } + + category, err := pgx.CollectExactlyOneRow(rows, pgx.RowToStructByName[CookieCategory]) + if err != nil { + return fmt.Errorf("cannot collect updated cookie category: %w", err) + } + + *c = category + + return nil +} + +func (c *CookieCategory) Delete( + ctx context.Context, + conn pg.Querier, + scope Scoper, +) error { + q := ` +DELETE FROM cookie_categories +WHERE + %s + AND id = @id +` + + q = fmt.Sprintf(q, scope.SQLFragment()) + + args := pgx.StrictNamedArgs{"id": c.ID} + maps.Copy(args, scope.SQLArguments()) + + _, err := conn.Exec(ctx, q, args) + if err != nil { + return fmt.Errorf("cannot delete cookie category: %w", err) + } + + return nil +} diff --git a/pkg/coredata/cookie_category_order_field.go b/pkg/coredata/cookie_category_order_field.go new file mode 100644 index 000000000..eef02dad0 --- /dev/null +++ b/pkg/coredata/cookie_category_order_field.go @@ -0,0 +1,55 @@ +// Copyright (c) 2025-2026 Probo Inc . +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR +// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +// PERFORMANCE OF THIS SOFTWARE. + +package coredata + +import "fmt" + +type CookieCategoryOrderField string + +const ( + CookieCategoryOrderFieldRank CookieCategoryOrderField = "RANK" +) + +func (p CookieCategoryOrderField) Column() string { + switch p { + case CookieCategoryOrderFieldRank: + return "rank" + } + panic(fmt.Sprintf("unsupported order by: %s", p)) +} + +func (p CookieCategoryOrderField) IsValid() bool { + switch p { + case CookieCategoryOrderFieldRank: + return true + } + return false +} + +func (p CookieCategoryOrderField) String() string { + return string(p) +} + +func (p *CookieCategoryOrderField) UnmarshalText(text []byte) error { + *p = CookieCategoryOrderField(text) + if !p.IsValid() { + return fmt.Errorf("%s is not a valid CookieCategoryOrderField", string(text)) + } + return nil +} + +func (p CookieCategoryOrderField) MarshalText() ([]byte, error) { + return []byte(p.String()), nil +} diff --git a/pkg/coredata/entity_type_reg.go b/pkg/coredata/entity_type_reg.go index e83c98428..55cd3fed4 100644 --- a/pkg/coredata/entity_type_reg.go +++ b/pkg/coredata/entity_type_reg.go @@ -98,6 +98,9 @@ const ( AccessReviewCampaignEntityType uint16 = 72 AccessEntryEntityType uint16 = 73 AccessEntryDecisionHistoryEntityType uint16 = 74 + CookieBannerEntityType uint16 = 75 + CookieCategoryEntityType uint16 = 76 + ConsentRecordEntityType uint16 = 77 ) func NewEntityFromID(id gid.GID) (any, bool) { @@ -244,6 +247,12 @@ func NewEntityFromID(id gid.GID) (any, bool) { return &AccessEntry{ID: id}, true case AccessEntryDecisionHistoryEntityType: return &AccessEntryDecisionHistory{ID: id}, true + case CookieBannerEntityType: + return &CookieBanner{ID: id}, true + case CookieCategoryEntityType: + return &CookieCategory{ID: id}, true + case ConsentRecordEntityType: + return &ConsentRecord{ID: id}, true default: return nil, false } diff --git a/pkg/coredata/migrations/20260408T143300Z.sql b/pkg/coredata/migrations/20260408T143300Z.sql new file mode 100644 index 000000000..1788640e1 --- /dev/null +++ b/pkg/coredata/migrations/20260408T143300Z.sql @@ -0,0 +1,64 @@ +-- Copyright (c) 2025-2026 Probo Inc . +-- +-- Permission to use, copy, modify, and/or distribute this software for any +-- purpose with or without fee is hereby granted, provided that the above +-- copyright notice and this permission notice appear in all copies. +-- +-- THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +-- REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +-- AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +-- INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +-- LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR +-- OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +-- PERFORMANCE OF THIS SOFTWARE. + +CREATE TYPE cookie_banner_state AS ENUM ('DRAFT', 'PUBLISHED', 'DISABLED'); +CREATE TYPE consent_action AS ENUM ('ACCEPT_ALL', 'REJECT_ALL', 'CUSTOMIZE', 'ACCEPT_CATEGORY', 'GPC'); +CREATE TYPE consent_mode AS ENUM ('OPT_IN', 'OPT_OUT'); + +CREATE TABLE cookie_banners ( + id TEXT PRIMARY KEY, + tenant_id TEXT NOT NULL, + organization_id TEXT NOT NULL REFERENCES organizations(id) ON UPDATE CASCADE ON DELETE CASCADE, + name TEXT NOT NULL, + domain TEXT NOT NULL, + state cookie_banner_state NOT NULL, + title TEXT NOT NULL, + description TEXT NOT NULL, + accept_all_label TEXT NOT NULL, + reject_all_label TEXT NOT NULL, + save_preferences_label TEXT NOT NULL, + privacy_policy_url TEXT NOT NULL, + consent_expiry_days INTEGER NOT NULL, + version INTEGER NOT NULL, + theme JSONB, + consent_mode consent_mode NOT NULL, + created_at TIMESTAMPTZ NOT NULL, + updated_at TIMESTAMPTZ NOT NULL +); + +CREATE TABLE cookie_categories ( + id TEXT PRIMARY KEY, + tenant_id TEXT NOT NULL, + cookie_banner_id TEXT NOT NULL REFERENCES cookie_banners(id) ON UPDATE CASCADE ON DELETE CASCADE, + name TEXT NOT NULL, + description TEXT NOT NULL, + required BOOLEAN NOT NULL, + rank INTEGER NOT NULL, + cookies JSONB NOT NULL, + created_at TIMESTAMPTZ NOT NULL, + updated_at TIMESTAMPTZ NOT NULL +); + +CREATE TABLE consent_records ( + id TEXT PRIMARY KEY, + tenant_id TEXT NOT NULL, + cookie_banner_id TEXT NOT NULL REFERENCES cookie_banners(id) ON UPDATE CASCADE ON DELETE CASCADE, + visitor_id TEXT NOT NULL, + ip_address TEXT, + user_agent TEXT, + consent_data JSONB NOT NULL, + action consent_action NOT NULL, + banner_version INTEGER NOT NULL, + created_at TIMESTAMPTZ NOT NULL +); diff --git a/pkg/probo/actions.go b/pkg/probo/actions.go index 03cdc9670..b93bb979d 100644 --- a/pkg/probo/actions.go +++ b/pkg/probo/actions.go @@ -386,4 +386,21 @@ const ( ActionAccessSourceUpdate = "core:access-source:update" ActionAccessSourceDelete = "core:access-source:delete" ActionAccessSourceSync = "core:access-source:sync" + + // CookieBanner actions + ActionCookieBannerGet = "core:cookie-banner:get" + ActionCookieBannerList = "core:cookie-banner:list" + ActionCookieBannerCreate = "core:cookie-banner:create" + ActionCookieBannerUpdate = "core:cookie-banner:update" + ActionCookieBannerDelete = "core:cookie-banner:delete" + + // CookieCategory actions + ActionCookieCategoryGet = "core:cookie-category:get" + ActionCookieCategoryList = "core:cookie-category:list" + ActionCookieCategoryCreate = "core:cookie-category:create" + ActionCookieCategoryUpdate = "core:cookie-category:update" + ActionCookieCategoryDelete = "core:cookie-category:delete" + + // ConsentRecord actions + ActionConsentRecordList = "core:consent-record:list" ) diff --git a/pkg/probo/policies.go b/pkg/probo/policies.go index cf2f84853..0ba34dd88 100644 --- a/pkg/probo/policies.go +++ b/pkg/probo/policies.go @@ -151,6 +151,9 @@ var ViewerPolicy = policy.NewPolicy( ActionAccessReviewCampaignGet, ActionAccessReviewCampaignList, ActionAccessEntryGet, ActionAccessEntryList, ActionAccessSourceGet, ActionAccessSourceList, + ActionCookieBannerGet, ActionCookieBannerList, + ActionCookieCategoryGet, ActionCookieCategoryList, + ActionConsentRecordList, ).WithSID("entity-read-access").When(organizationCondition), policy.Allow( diff --git a/pkg/probo/service.go b/pkg/probo/service.go index 2286f958b..b5abfcf43 100644 --- a/pkg/probo/service.go +++ b/pkg/probo/service.go @@ -295,7 +295,6 @@ func (s *Service) WithTenant(tenantID gid.TenantID) *TenantService { logger: s.logger.Named("custom_domains"), } tenantService.SlackMessages = s.slack.WithTenant(tenantID).SlackMessages - return tenantService } diff --git a/pkg/probod/probod.go b/pkg/probod/probod.go index 2ea17ae7f..735369826 100644 --- a/pkg/probod/probod.go +++ b/pkg/probod/probod.go @@ -47,6 +47,7 @@ import ( "go.probo.inc/probo/pkg/baseurl" "go.probo.inc/probo/pkg/certmanager" "go.probo.inc/probo/pkg/connector" + "go.probo.inc/probo/pkg/consent" "go.probo.inc/probo/pkg/coredata" "go.probo.inc/probo/pkg/crypto/cipher" "go.probo.inc/probo/pkg/crypto/keys" @@ -500,6 +501,8 @@ func (impl *Implm) Run( return fmt.Errorf("cannot create probo service: %w", err) } + consentService := consent.NewService(pgClient) + trustService := trust.NewService( pgClient, s3Client, @@ -528,6 +531,7 @@ func (impl *Implm) Run( AllowedOrigins: impl.cfg.Api.Cors.AllowedOrigins, ExtraHeaderFields: impl.cfg.Api.ExtraHeaderFields, Probo: proboService, + Consent: consentService, File: fileService, IAM: iamService, Trust: trustService, diff --git a/pkg/server/api/api.go b/pkg/server/api/api.go index aa62a8f46..978db4f65 100644 --- a/pkg/server/api/api.go +++ b/pkg/server/api/api.go @@ -28,6 +28,7 @@ import ( "go.probo.inc/probo/pkg/accessreview" "go.probo.inc/probo/pkg/baseurl" "go.probo.inc/probo/pkg/connector" + "go.probo.inc/probo/pkg/consent" "go.probo.inc/probo/pkg/esign" "go.probo.inc/probo/pkg/file" "go.probo.inc/probo/pkg/iam" @@ -36,6 +37,7 @@ import ( "go.probo.inc/probo/pkg/securecookie" connect_v1 "go.probo.inc/probo/pkg/server/api/connect/v1" console_v1 "go.probo.inc/probo/pkg/server/api/console/v1" + cookiebanner_v1 "go.probo.inc/probo/pkg/server/api/cookiebanner/v1" files_v1 "go.probo.inc/probo/pkg/server/api/files/v1" mcp_v1 "go.probo.inc/probo/pkg/server/api/mcp/v1" slack_v1 "go.probo.inc/probo/pkg/server/api/slack/v1" @@ -49,6 +51,7 @@ type ( BaseURL *baseurl.BaseURL AllowedOrigins []string Probo *probo.Service + Consent *consent.Service File *file.Service IAM *iam.Service Trust *trust.Service @@ -74,6 +77,7 @@ type ( csrf *http.CrossOriginProtection compliancePageHandler http.Handler consoleHandler http.Handler + cookieBannerHandler http.Handler filesHandler http.Handler mcpHandler http.Handler slackHandler http.Handler @@ -135,6 +139,10 @@ func NewServer(cfg Config) (*Server, error) { // POSTs from external identity providers by design. csrf.AddInsecureBypassPattern("POST /connect/v1/saml/2.0/consume") + // Cookie banner endpoints are cross-origin by design (embedded widget). + csrf.AddInsecureBypassPattern("GET /cookie-banner/v1/*") + csrf.AddInsecureBypassPattern("POST /cookie-banner/v1/*") + csrf.SetDenyHandler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { httpserver.RenderJSON( w, @@ -148,6 +156,10 @@ func NewServer(cfg Config) (*Server, error) { return &Server{ cfg: cfg, csrf: csrf, + cookieBannerHandler: cookiebanner_v1.NewMux( + cfg.Logger.Named("cookiebanner.v1"), + cfg.Consent, + ), compliancePageHandler: trust_v1.NewMux( cfg.Logger.Named("trust.v1"), cfg.IAM, @@ -161,6 +173,7 @@ func NewServer(cfg Config) (*Server, error) { consoleHandler: console_v1.NewMux( cfg.Logger.Named("console.v1"), cfg.Probo, + cfg.Consent, cfg.IAM, cfg.ESign, cfg.AccessReview, @@ -178,6 +191,7 @@ func NewServer(cfg Config) (*Server, error) { mcpHandler: mcp_v1.NewMux( cfg.Logger.Named("mcp.v1"), cfg.Probo, + cfg.Consent, cfg.IAM, cfg.AccessReview, cfg.TokenSecret, @@ -238,6 +252,7 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { router.Use(cors.Handler(corsOpts)) + router.Mount("/cookie-banner/v1", http.StripPrefix("/cookie-banner/v1", s.cookieBannerHandler)) router.Mount("/console/v1", http.StripPrefix("/console/v1", s.consoleHandler)) router.Mount("/connect/v1", http.StripPrefix("/connect/v1", s.connectHandler)) router.Mount("/files/v1", http.StripPrefix("/files/v1", s.filesHandler)) diff --git a/pkg/server/api/console/v1/graphql_handler.go b/pkg/server/api/console/v1/graphql_handler.go index c63b6c3e9..1a6981101 100644 --- a/pkg/server/api/console/v1/graphql_handler.go +++ b/pkg/server/api/console/v1/graphql_handler.go @@ -20,6 +20,8 @@ import ( "go.gearno.de/kit/log" "go.probo.inc/probo/pkg/accessreview" "go.probo.inc/probo/pkg/connector" + "go.probo.inc/probo/pkg/baseurl" + "go.probo.inc/probo/pkg/consent" "go.probo.inc/probo/pkg/esign" "go.probo.inc/probo/pkg/iam" "go.probo.inc/probo/pkg/mailman" @@ -29,17 +31,30 @@ import ( "go.probo.inc/probo/pkg/server/gqlutils" ) -func NewGraphQLHandler(iamSvc *iam.Service, proboSvc *probo.Service, esignSvc *esign.Service, accessReviewSvc *accessreview.Service, mailmanSvc *mailman.Service, connectorRegistry *connector.ConnectorRegistry, customDomainCname string, logger *log.Logger) http.Handler { +func NewGraphQLHandler( + iamSvc *iam.Service, + proboSvc *probo.Service, + consentSvc *consent.Service, + esignSvc *esign.Service, + accessReviewSvc *accessreview.Service, + mailmanSvc *mailman.Service, + connectorRegistry *connector.ConnectorRegistry, + customDomainCname string, + baseURL *baseurl.BaseURL, + logger *log.Logger, +) http.Handler { config := schema.Config{ Resolvers: &Resolver{ authorize: authz.NewAuthorizeFunc(iamSvc, logger), probo: proboSvc, + consent: consentSvc, iam: iamSvc, esign: esignSvc, accessReview: accessReviewSvc, mailman: mailmanSvc, connectorRegistry: connectorRegistry, customDomainCname: customDomainCname, + baseURL: baseURL, logger: logger, }, } diff --git a/pkg/server/api/console/v1/resolver.go b/pkg/server/api/console/v1/resolver.go index 44e4c264f..bf9c71c8f 100644 --- a/pkg/server/api/console/v1/resolver.go +++ b/pkg/server/api/console/v1/resolver.go @@ -42,6 +42,7 @@ import ( "go.probo.inc/probo/pkg/accessreview" "go.probo.inc/probo/pkg/baseurl" "go.probo.inc/probo/pkg/connector" + "go.probo.inc/probo/pkg/consent" "go.probo.inc/probo/pkg/coredata" "go.probo.inc/probo/pkg/esign" "go.probo.inc/probo/pkg/gid" @@ -60,6 +61,7 @@ type ( Resolver struct { authorize authz.AuthorizeFunc probo *probo.Service + consent *consent.Service iam *iam.Service esign *esign.Service accessReview *accessreview.Service @@ -67,12 +69,14 @@ type ( connectorRegistry *connector.ConnectorRegistry logger *log.Logger customDomainCname string + baseURL *baseurl.BaseURL } ) func NewMux( logger *log.Logger, proboSvc *probo.Service, + consentSvc *consent.Service, iamSvc *iam.Service, esignSvc *esign.Service, accessReviewSvc *accessreview.Service, @@ -87,7 +91,18 @@ func NewMux( safeRedirect := saferedirect.New(saferedirect.StaticHosts(baseURL.Host())) - graphqlHandler := NewGraphQLHandler(iamSvc, proboSvc, esignSvc, accessReviewSvc, mailmanSvc, connectorRegistry, customDomainCname, logger) + graphqlHandler := NewGraphQLHandler( + iamSvc, + proboSvc, + consentSvc, + esignSvc, + accessReviewSvc, + mailmanSvc, + connectorRegistry, + customDomainCname, + baseURL, + logger, + ) r.Group(func(r chi.Router) { r.Use(authn.NewSessionMiddleware(iamSvc, cookieConfig)) diff --git a/pkg/server/api/console/v1/schema.graphql b/pkg/server/api/console/v1/schema.graphql index 1c3d42d43..7989d1d46 100644 --- a/pkg/server/api/console/v1/schema.graphql +++ b/pkg/server/api/console/v1/schema.graphql @@ -1727,6 +1727,10 @@ input VendorFilter { snapshotId: ID } +input ConsentRecordFilter { + action: ConsentAction +} + # Core Types type TrustCenter implements Node @goModel( @@ -2099,6 +2103,7 @@ type Organization implements Node { orderBy: WebhookSubscriptionOrder ): WebhookSubscriptionConnection! @goField(forceResolver: true) + auditLogEntries( first: Int after: CursorKey @@ -2107,6 +2112,13 @@ type Organization implements Node { orderBy: AuditLogEntryOrder filter: AuditLogEntryFilter ): AuditLogEntryConnection! @goField(forceResolver: true) + cookieBanners( + first: Int + after: CursorKey + last: Int + before: CursorKey + orderBy: CookieBannerOrder + ): CookieBannerConnection! @goField(forceResolver: true) accessSources( first: Int @@ -3639,6 +3651,48 @@ type SnapshotEdge { node: Snapshot! } +type CookieBannerConnection + @goModel( + model: "go.probo.inc/probo/pkg/server/api/console/v1/types.CookieBannerConnection" + ) { + totalCount: Int! @goField(forceResolver: true) + edges: [CookieBannerEdge!]! + pageInfo: PageInfo! +} + +type CookieBannerEdge { + cursor: CursorKey! + node: CookieBanner! +} + +type CookieCategoryConnection + @goModel( + model: "go.probo.inc/probo/pkg/server/api/console/v1/types.CookieCategoryConnection" + ) { + totalCount: Int! @goField(forceResolver: true) + edges: [CookieCategoryEdge!]! + pageInfo: PageInfo! +} + +type CookieCategoryEdge { + cursor: CursorKey! + node: CookieCategory! +} + +type ConsentRecordConnection + @goModel( + model: "go.probo.inc/probo/pkg/server/api/console/v1/types.ConsentRecordConnection" + ) { + totalCount: Int! @goField(forceResolver: true) + edges: [ConsentRecordEdge!]! + pageInfo: PageInfo! +} + +type ConsentRecordEdge { + cursor: CursorKey! + node: ConsentRecord! +} + type File { id: ID! mimeType: String! @@ -3653,6 +3707,8 @@ type File { type Query { node(id: ID!): Node! viewer: Viewer! + cookieProviders(category: String): [CookieProvider!]! + cookieProvider(key: String!): CookieProvider } type Mutation { @@ -4079,6 +4135,7 @@ type Mutation { deleteCustomDomain( input: DeleteCustomDomainInput! ): DeleteCustomDomainPayload! + # Access Source mutations createAccessSource( input: CreateAccessSourceInput! @@ -4139,6 +4196,35 @@ type Mutation { deleteSlackConnection( input: DeleteSlackConnectionInput! ): DeleteSlackConnectionPayload! + + # Cookie Banner mutations + createCookieBanner( + input: CreateCookieBannerInput! + ): CreateCookieBannerPayload! + updateCookieBanner( + input: UpdateCookieBannerInput! + ): UpdateCookieBannerPayload! + deleteCookieBanner( + input: DeleteCookieBannerInput! + ): DeleteCookieBannerPayload! + publishCookieBanner( + input: PublishCookieBannerInput! + ): PublishCookieBannerPayload! + disableCookieBanner( + input: DisableCookieBannerInput! + ): DisableCookieBannerPayload! + createCookieCategory( + input: CreateCookieCategoryInput! + ): CreateCookieCategoryPayload! + updateCookieCategory( + input: UpdateCookieCategoryInput! + ): UpdateCookieCategoryPayload! + deleteCookieCategory( + input: DeleteCookieCategoryInput! + ): DeleteCookieCategoryPayload! + addCookiesFromProvider( + input: AddCookiesFromProviderInput! + ): AddCookiesFromProviderPayload! } # Input Types @@ -6450,6 +6536,76 @@ enum ElectronicSignatureEventSource ) } +enum CookieBannerState + @goModel( + model: "go.probo.inc/probo/pkg/coredata.CookieBannerState" + ) { + DRAFT + @goEnum( + value: "go.probo.inc/probo/pkg/coredata.CookieBannerStateDraft" + ) + PUBLISHED + @goEnum( + value: "go.probo.inc/probo/pkg/coredata.CookieBannerStatePublished" + ) + DISABLED + @goEnum( + value: "go.probo.inc/probo/pkg/coredata.CookieBannerStateDisabled" + ) +} + +enum ConsentAction + @goModel( + model: "go.probo.inc/probo/pkg/coredata.ConsentAction" + ) { + ACCEPT_ALL + @goEnum( + value: "go.probo.inc/probo/pkg/coredata.ConsentActionAcceptAll" + ) + REJECT_ALL + @goEnum( + value: "go.probo.inc/probo/pkg/coredata.ConsentActionRejectAll" + ) + CUSTOMIZE + @goEnum( + value: "go.probo.inc/probo/pkg/coredata.ConsentActionCustomize" + ) + ACCEPT_CATEGORY + @goEnum( + value: "go.probo.inc/probo/pkg/coredata.ConsentActionAcceptCategory" + ) + GPC + @goEnum( + value: "go.probo.inc/probo/pkg/coredata.ConsentActionGPC" + ) +} + +enum ConsentMode + @goModel( + model: "go.probo.inc/probo/pkg/coredata.ConsentMode" + ) { + OPT_IN + @goEnum( + value: "go.probo.inc/probo/pkg/coredata.ConsentModeOptIn" + ) + OPT_OUT + @goEnum( + value: "go.probo.inc/probo/pkg/coredata.ConsentModeOptOut" + ) +} + +type ConsentAnalytics + @goModel( + model: "go.probo.inc/probo/pkg/server/api/console/v1/types.ConsentAnalytics" + ) { + totalRecords: Int! + acceptAllCount: Int! + rejectAllCount: Int! + customizeCount: Int! + acceptCategoryCount: Int! + gpcCount: Int! +} + type ElectronicSignature implements Node { id: ID! status: ElectronicSignatureStatus! @@ -6811,6 +6967,62 @@ type AccessSource implements Node { permission(action: String!): Boolean! @goField(forceResolver: true) } +type CookieBannerTheme + @goModel( + model: "go.probo.inc/probo/pkg/server/api/console/v1/types.CookieBannerTheme" + ) { + primaryColor: String! + primaryTextColor: String! + secondaryColor: String! + secondaryTextColor: String! + backgroundColor: String! + textColor: String! + secondaryTextBodyColor: String! + borderColor: String! + fontFamily: String! + borderRadius: Int! + position: String! + revisitPosition: String! +} + +type CookieBanner implements Node { + id: ID! + name: String! + domain: String! + state: CookieBannerState! + title: String! + description: String! + acceptAllLabel: String! + rejectAllLabel: String! + savePreferencesLabel: String! + privacyPolicyUrl: String! + consentExpiryDays: Int! + consentMode: ConsentMode! + version: Int! + theme: CookieBannerTheme! @goField(forceResolver: true) + analytics: ConsentAnalytics! @goField(forceResolver: true) + categories( + first: Int + after: CursorKey + last: Int + before: CursorKey + orderBy: CookieCategoryOrder + ): CookieCategoryConnection! @goField(forceResolver: true) + consentRecords( + first: Int + after: CursorKey + last: Int + before: CursorKey + orderBy: ConsentRecordOrder + filter: ConsentRecordFilter + ): ConsentRecordConnection! @goField(forceResolver: true) + embedSnippet: String! + createdAt: Datetime! + updatedAt: Datetime! + + permission(action: String!): Boolean! @goField(forceResolver: true) +} + type AccessReviewCampaignScopeSource @goModel( model: "go.probo.inc/probo/pkg/server/api/console/v1/types.AccessReviewCampaignScopeSource" @@ -7204,3 +7416,231 @@ input DeleteConnectorInput { type DeleteConnectorPayload { deletedConnectorId: ID! } + +type CookieCategoryItem + @goModel( + model: "go.probo.inc/probo/pkg/coredata.CookieItem" + ) { + name: String! + duration: String! + description: String! +} + +type CookieCategory implements Node { + id: ID! + name: String! + description: String! + required: Boolean! + rank: Int! + cookies: [CookieCategoryItem!]! + createdAt: Datetime! + updatedAt: Datetime! +} + +type ConsentRecord implements Node { + id: ID! + visitorId: String! + consentData: String! + ipAddress: String + userAgent: String + action: ConsentAction! + bannerVersion: Int! + createdAt: Datetime! +} + +input CreateCookieBannerInput { + organizationId: ID! + name: String! + domain: String +} + +type CreateCookieBannerPayload { + cookieBanner: CookieBanner + cookieBannerEdge: CookieBannerEdge +} + +input UpdateCookieBannerThemeInput + @goModel( + model: "go.probo.inc/probo/pkg/server/api/console/v1/types.UpdateCookieBannerThemeInput" + ) { + primaryColor: String + primaryTextColor: String + secondaryColor: String + secondaryTextColor: String + backgroundColor: String + textColor: String + secondaryTextBodyColor: String + borderColor: String + fontFamily: String + borderRadius: Int + position: String + revisitPosition: String +} + +input UpdateCookieBannerInput { + id: ID! + name: String + domain: String + title: String + description: String + acceptAllLabel: String + rejectAllLabel: String + savePreferencesLabel: String + privacyPolicyUrl: String + consentExpiryDays: Int + consentMode: ConsentMode + theme: UpdateCookieBannerThemeInput +} + +type UpdateCookieBannerPayload { + cookieBanner: CookieBanner +} + +input DeleteCookieBannerInput { + id: ID! +} + +type DeleteCookieBannerPayload { + deletedCookieBannerId: ID! +} + +input PublishCookieBannerInput { + id: ID! +} + +type PublishCookieBannerPayload { + cookieBanner: CookieBanner +} + +input DisableCookieBannerInput { + id: ID! +} + +type DisableCookieBannerPayload { + cookieBanner: CookieBanner +} + +input CookieCategoryItemInput + @goModel( + model: "go.probo.inc/probo/pkg/coredata.CookieItem" + ) { + name: String! + duration: String! + description: String! +} + +input CreateCookieCategoryInput { + cookieBannerId: ID! + name: String! + description: String + rank: Int + cookies: [CookieCategoryItemInput!] +} + +type CreateCookieCategoryPayload { + cookieCategory: CookieCategory +} + +input UpdateCookieCategoryInput { + id: ID! + name: String + description: String + rank: Int + cookies: [CookieCategoryItemInput!] +} + +type UpdateCookieCategoryPayload { + cookieCategory: CookieCategory +} + +input DeleteCookieCategoryInput { + id: ID! +} + +type DeleteCookieCategoryPayload { + deletedCookieCategoryId: ID! +} + +type CookieProvider + @goModel( + model: "go.probo.inc/probo/pkg/cookieprovider.Provider" + ) { + key: String! + name: String! + description: String! + category: String! + websiteUrl: String! @goField(name: "WebsiteURL") + cookies: [CookieProviderCookie!]! +} + +type CookieProviderCookie + @goModel( + model: "go.probo.inc/probo/pkg/cookieprovider.Cookie" + ) { + name: String! + duration: String! + description: String! +} + +input AddCookiesFromProviderInput { + cookieCategoryId: ID! + providerKey: String! +} + +type AddCookiesFromProviderPayload { + cookieCategory: CookieCategory +} + +input CookieBannerOrder + @goModel( + model: "go.probo.inc/probo/pkg/server/api/console/v1/types.CookieBannerOrderBy" + ) { + direction: OrderDirection! + field: CookieBannerOrderField! +} + +enum CookieBannerOrderField + @goModel( + model: "go.probo.inc/probo/pkg/coredata.CookieBannerOrderField" + ) { + CREATED_AT + @goEnum( + value: "go.probo.inc/probo/pkg/coredata.CookieBannerOrderFieldCreatedAt" + ) +} + +input CookieCategoryOrder + @goModel( + model: "go.probo.inc/probo/pkg/server/api/console/v1/types.CookieCategoryOrderBy" + ) { + direction: OrderDirection! + field: CookieCategoryOrderField! +} + +enum CookieCategoryOrderField + @goModel( + model: "go.probo.inc/probo/pkg/coredata.CookieCategoryOrderField" + ) { + RANK + @goEnum( + value: "go.probo.inc/probo/pkg/coredata.CookieCategoryOrderFieldRank" + ) +} + +input ConsentRecordOrder + @goModel( + model: "go.probo.inc/probo/pkg/server/api/console/v1/types.ConsentRecordOrderBy" + ) { + direction: OrderDirection! + field: ConsentRecordOrderField! +} + +enum ConsentRecordOrderField + @goModel( + model: "go.probo.inc/probo/pkg/coredata.ConsentRecordOrderField" + ) { + CREATED_AT + @goEnum( + value: "go.probo.inc/probo/pkg/coredata.ConsentRecordOrderFieldCreatedAt" + ) +} diff --git a/pkg/server/api/console/v1/types/consent_record.go b/pkg/server/api/console/v1/types/consent_record.go new file mode 100644 index 000000000..75b738421 --- /dev/null +++ b/pkg/server/api/console/v1/types/consent_record.go @@ -0,0 +1,79 @@ +// Copyright (c) 2025-2026 Probo Inc . +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR +// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +// PERFORMANCE OF THIS SOFTWARE. + +package types + +import ( + "go.probo.inc/probo/pkg/coredata" + "go.probo.inc/probo/pkg/gid" + "go.probo.inc/probo/pkg/page" +) + +type ( + ConsentRecordOrderBy OrderBy[coredata.ConsentRecordOrderField] + + ConsentRecordConnection struct { + TotalCount int + Edges []*ConsentRecordEdge + PageInfo PageInfo + + Resolver any + ParentID gid.GID + Filter *coredata.ConsentRecordFilter + } +) + +func NewConsentRecordConnection( + p *page.Page[*coredata.ConsentRecord, coredata.ConsentRecordOrderField], + resolver any, + parentID gid.GID, + filter *coredata.ConsentRecordFilter, +) *ConsentRecordConnection { + edges := make([]*ConsentRecordEdge, len(p.Data)) + for i, record := range p.Data { + edges[i] = NewConsentRecordEdge(record, p.Cursor.OrderBy.Field) + } + + return &ConsentRecordConnection{ + Edges: edges, + PageInfo: *NewPageInfo(p), + + Resolver: resolver, + ParentID: parentID, + Filter: filter, + } +} + +func NewConsentRecordEdge( + record *coredata.ConsentRecord, + orderField coredata.ConsentRecordOrderField, +) *ConsentRecordEdge { + return &ConsentRecordEdge{ + Node: NewConsentRecord(record), + Cursor: record.CursorKey(orderField), + } +} + +func NewConsentRecord(record *coredata.ConsentRecord) *ConsentRecord { + return &ConsentRecord{ + ID: record.ID, + VisitorID: record.VisitorID, + ConsentData: string(record.ConsentData), + IPAddress: record.IPAddress, + UserAgent: record.UserAgent, + Action: record.Action, + BannerVersion: record.BannerVersion, + CreatedAt: record.CreatedAt, + } +} diff --git a/pkg/server/api/console/v1/types/cookie_banner.go b/pkg/server/api/console/v1/types/cookie_banner.go new file mode 100644 index 000000000..b83f3e3e1 --- /dev/null +++ b/pkg/server/api/console/v1/types/cookie_banner.go @@ -0,0 +1,337 @@ +// Copyright (c) 2025-2026 Probo Inc . +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR +// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +// PERFORMANCE OF THIS SOFTWARE. + +package types + +import ( + "encoding/json" + "fmt" + "regexp" + "slices" + + "go.probo.inc/probo/pkg/baseurl" + "go.probo.inc/probo/pkg/coredata" + "go.probo.inc/probo/pkg/gid" + "go.probo.inc/probo/pkg/page" + "go.probo.inc/probo/pkg/validator" +) + +type ( + CookieBannerOrderBy OrderBy[coredata.CookieBannerOrderField] + + CookieBannerConnection struct { + TotalCount int + Edges []*CookieBannerEdge + PageInfo PageInfo + + Resolver any + ParentID gid.GID + } + + CookieBannerTheme struct { + PrimaryColor string `json:"primaryColor"` + PrimaryTextColor string `json:"primaryTextColor"` + SecondaryColor string `json:"secondaryColor"` + SecondaryTextColor string `json:"secondaryTextColor"` + BackgroundColor string `json:"backgroundColor"` + TextColor string `json:"textColor"` + SecondaryTextBodyColor string `json:"secondaryTextBodyColor"` + BorderColor string `json:"borderColor"` + FontFamily string `json:"fontFamily"` + BorderRadius int `json:"borderRadius"` + Position string `json:"position"` + RevisitPosition string `json:"revisitPosition"` + } + + ConsentAnalytics struct { + TotalRecords int `json:"totalRecords"` + AcceptAllCount int `json:"acceptAllCount"` + RejectAllCount int `json:"rejectAllCount"` + CustomizeCount int `json:"customizeCount"` + AcceptCategoryCount int `json:"acceptCategoryCount"` + GPCCount int `json:"gpcCount"` + } + + UpdateCookieBannerThemeInput struct { + PrimaryColor *string `json:"primaryColor,omitempty"` + PrimaryTextColor *string `json:"primaryTextColor,omitempty"` + SecondaryColor *string `json:"secondaryColor,omitempty"` + SecondaryTextColor *string `json:"secondaryTextColor,omitempty"` + BackgroundColor *string `json:"backgroundColor,omitempty"` + TextColor *string `json:"textColor,omitempty"` + SecondaryTextBodyColor *string `json:"secondaryTextBodyColor,omitempty"` + BorderColor *string `json:"borderColor,omitempty"` + FontFamily *string `json:"fontFamily,omitempty"` + BorderRadius *int `json:"borderRadius,omitempty"` + Position *string `json:"position,omitempty"` + RevisitPosition *string `json:"revisitPosition,omitempty"` + } +) + +var ( + hexColorRegex = regexp.MustCompile(`^#[0-9a-fA-F]{6}$`) + validPositions = []string{"bottom", "bottom-left", "bottom-right", "center"} + validRevisitPositions = []string{"bottom-left", "bottom-right"} +) + +func (t *UpdateCookieBannerThemeInput) Validate() error { + var errs validator.ValidationErrors + + colorFields := []struct { + field string + value *string + }{ + {"theme.primaryColor", t.PrimaryColor}, + {"theme.primaryTextColor", t.PrimaryTextColor}, + {"theme.secondaryColor", t.SecondaryColor}, + {"theme.secondaryTextColor", t.SecondaryTextColor}, + {"theme.backgroundColor", t.BackgroundColor}, + {"theme.textColor", t.TextColor}, + {"theme.secondaryTextBodyColor", t.SecondaryTextBodyColor}, + {"theme.borderColor", t.BorderColor}, + } + + for _, cf := range colorFields { + if cf.value != nil && !hexColorRegex.MatchString(*cf.value) { + errs = append(errs, &validator.ValidationError{ + Field: cf.field, + Code: validator.ErrorCodeInvalidFormat, + Message: "must be a valid hex color (e.g. #ff0000)", + }) + } + } + + if t.Position != nil && !slices.Contains(validPositions, *t.Position) { + errs = append(errs, &validator.ValidationError{ + Field: "theme.position", + Code: validator.ErrorCodeInvalidFormat, + Message: "must be one of: bottom, bottom-left, bottom-right, center", + }) + } + + if t.RevisitPosition != nil && !slices.Contains(validRevisitPositions, *t.RevisitPosition) { + errs = append(errs, &validator.ValidationError{ + Field: "theme.revisitPosition", + Code: validator.ErrorCodeInvalidFormat, + Message: "must be one of: bottom-left, bottom-right", + }) + } + + if t.BorderRadius != nil && (*t.BorderRadius < 0 || *t.BorderRadius > 24) { + errs = append(errs, &validator.ValidationError{ + Field: "theme.borderRadius", + Code: validator.ErrorCodeInvalidFormat, + Message: "must be between 0 and 24", + }) + } + + if t.FontFamily != nil && len(*t.FontFamily) > 200 { + errs = append(errs, &validator.ValidationError{ + Field: "theme.fontFamily", + Code: validator.ErrorCodeInvalidFormat, + Message: "must be 200 characters or fewer", + }) + } + + if len(errs) > 0 { + return errs + } + + return nil +} + +func NewCookieBannerConnection( + p *page.Page[*coredata.CookieBanner, coredata.CookieBannerOrderField], + resolver any, + parentID gid.GID, + baseURL *baseurl.BaseURL, +) *CookieBannerConnection { + edges := make([]*CookieBannerEdge, len(p.Data)) + for i, banner := range p.Data { + edges[i] = NewCookieBannerEdge(banner, p.Cursor.OrderBy.Field, baseURL) + } + + return &CookieBannerConnection{ + Edges: edges, + PageInfo: *NewPageInfo(p), + + Resolver: resolver, + ParentID: parentID, + } +} + +func NewCookieBannerEdge( + banner *coredata.CookieBanner, + orderField coredata.CookieBannerOrderField, + baseURL *baseurl.BaseURL, +) *CookieBannerEdge { + return &CookieBannerEdge{ + Node: NewCookieBanner(banner, baseURL), + Cursor: banner.CursorKey(orderField), + } +} + +func NewCookieBanner(banner *coredata.CookieBanner, baseURL *baseurl.BaseURL) *CookieBanner { + widgetURL := baseURL.WithPath("/api/cookie-banner/v1/widget.js").MustString() + + return &CookieBanner{ + ID: banner.ID, + Name: banner.Name, + Domain: banner.Domain, + State: banner.State, + Title: banner.Title, + Description: banner.Description, + AcceptAllLabel: banner.AcceptAllLabel, + RejectAllLabel: banner.RejectAllLabel, + SavePreferencesLabel: banner.SavePreferencesLabel, + PrivacyPolicyURL: banner.PrivacyPolicyURL, + ConsentExpiryDays: banner.ConsentExpiryDays, + ConsentMode: banner.ConsentMode, + Version: banner.Version, + EmbedSnippet: fmt.Sprintf(``, widgetURL, banner.ID), + CreatedAt: banner.CreatedAt, + UpdatedAt: banner.UpdatedAt, + } +} + +var defaultCookieBannerTheme = CookieBannerTheme{ + PrimaryColor: "#2563eb", + PrimaryTextColor: "#ffffff", + SecondaryColor: "#1a1a1a", + SecondaryTextColor: "#ffffff", + BackgroundColor: "#ffffff", + TextColor: "#1a1a1a", + SecondaryTextBodyColor: "#4b5563", + BorderColor: "#e5e7eb", + FontFamily: "-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif", + BorderRadius: 8, + Position: "bottom", + RevisitPosition: "bottom-left", +} + +func NewCookieBannerTheme(raw json.RawMessage) *CookieBannerTheme { + theme := defaultCookieBannerTheme + + if len(raw) > 0 { + var override struct { + PrimaryColor *string `json:"primaryColor"` + PrimaryTextColor *string `json:"primaryTextColor"` + SecondaryColor *string `json:"secondaryColor"` + SecondaryTextColor *string `json:"secondaryTextColor"` + BackgroundColor *string `json:"backgroundColor"` + TextColor *string `json:"textColor"` + SecondaryTextBodyColor *string `json:"secondaryTextBodyColor"` + BorderColor *string `json:"borderColor"` + FontFamily *string `json:"fontFamily"` + BorderRadius *int `json:"borderRadius"` + Position *string `json:"position"` + RevisitPosition *string `json:"revisitPosition"` + } + if err := json.Unmarshal(raw, &override); err == nil { + if override.PrimaryColor != nil { + theme.PrimaryColor = *override.PrimaryColor + } + if override.PrimaryTextColor != nil { + theme.PrimaryTextColor = *override.PrimaryTextColor + } + if override.SecondaryColor != nil { + theme.SecondaryColor = *override.SecondaryColor + } + if override.SecondaryTextColor != nil { + theme.SecondaryTextColor = *override.SecondaryTextColor + } + if override.BackgroundColor != nil { + theme.BackgroundColor = *override.BackgroundColor + } + if override.TextColor != nil { + theme.TextColor = *override.TextColor + } + if override.SecondaryTextBodyColor != nil { + theme.SecondaryTextBodyColor = *override.SecondaryTextBodyColor + } + if override.BorderColor != nil { + theme.BorderColor = *override.BorderColor + } + if override.FontFamily != nil { + theme.FontFamily = *override.FontFamily + } + if override.BorderRadius != nil { + theme.BorderRadius = *override.BorderRadius + } + if override.Position != nil { + theme.Position = *override.Position + } + if override.RevisitPosition != nil { + theme.RevisitPosition = *override.RevisitPosition + } + } + } + + return &theme +} + +func MergeThemeInput(existing json.RawMessage, input *UpdateCookieBannerThemeInput) json.RawMessage { + if input == nil { + return existing + } + + current := make(map[string]any) + if len(existing) > 0 { + _ = json.Unmarshal(existing, ¤t) + } + + if input.PrimaryColor != nil { + current["primaryColor"] = *input.PrimaryColor + } + if input.PrimaryTextColor != nil { + current["primaryTextColor"] = *input.PrimaryTextColor + } + if input.SecondaryColor != nil { + current["secondaryColor"] = *input.SecondaryColor + } + if input.SecondaryTextColor != nil { + current["secondaryTextColor"] = *input.SecondaryTextColor + } + if input.BackgroundColor != nil { + current["backgroundColor"] = *input.BackgroundColor + } + if input.TextColor != nil { + current["textColor"] = *input.TextColor + } + if input.SecondaryTextBodyColor != nil { + current["secondaryTextBodyColor"] = *input.SecondaryTextBodyColor + } + if input.BorderColor != nil { + current["borderColor"] = *input.BorderColor + } + if input.FontFamily != nil { + current["fontFamily"] = *input.FontFamily + } + if input.BorderRadius != nil { + current["borderRadius"] = *input.BorderRadius + } + if input.Position != nil { + current["position"] = *input.Position + } + if input.RevisitPosition != nil { + current["revisitPosition"] = *input.RevisitPosition + } + + data, err := json.Marshal(current) + if err != nil { + return existing + } + + return data +} diff --git a/pkg/server/api/console/v1/types/cookie_category.go b/pkg/server/api/console/v1/types/cookie_category.go new file mode 100644 index 000000000..45ec4e21e --- /dev/null +++ b/pkg/server/api/console/v1/types/cookie_category.go @@ -0,0 +1,81 @@ +// Copyright (c) 2025-2026 Probo Inc . +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR +// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +// PERFORMANCE OF THIS SOFTWARE. + +package types + +import ( + "go.probo.inc/probo/pkg/coredata" + "go.probo.inc/probo/pkg/gid" + "go.probo.inc/probo/pkg/page" +) + +type ( + CookieCategoryOrderBy OrderBy[coredata.CookieCategoryOrderField] + + CookieCategoryConnection struct { + TotalCount int + Edges []*CookieCategoryEdge + PageInfo PageInfo + + Resolver any + ParentID gid.GID + } +) + +func NewCookieCategoryConnection( + p *page.Page[*coredata.CookieCategory, coredata.CookieCategoryOrderField], + resolver any, + parentID gid.GID, +) *CookieCategoryConnection { + edges := make([]*CookieCategoryEdge, len(p.Data)) + for i, category := range p.Data { + edges[i] = NewCookieCategoryEdge(category, p.Cursor.OrderBy.Field) + } + + return &CookieCategoryConnection{ + Edges: edges, + PageInfo: *NewPageInfo(p), + + Resolver: resolver, + ParentID: parentID, + } +} + +func NewCookieCategoryEdge( + category *coredata.CookieCategory, + orderField coredata.CookieCategoryOrderField, +) *CookieCategoryEdge { + return &CookieCategoryEdge{ + Node: NewCookieCategory(category), + Cursor: category.CursorKey(orderField), + } +} + +func NewCookieCategory(category *coredata.CookieCategory) *CookieCategory { + cookies := make([]*coredata.CookieItem, 0, len(category.Cookies)) + for i := range category.Cookies { + cookies = append(cookies, &category.Cookies[i]) + } + + return &CookieCategory{ + ID: category.ID, + Name: category.Name, + Description: category.Description, + Required: category.Required, + Rank: category.Rank, + Cookies: cookies, + CreatedAt: category.CreatedAt, + UpdatedAt: category.UpdatedAt, + } +} diff --git a/pkg/server/api/console/v1/v1_resolver.go b/pkg/server/api/console/v1/v1_resolver.go index c2ecb9898..da161a265 100644 --- a/pkg/server/api/console/v1/v1_resolver.go +++ b/pkg/server/api/console/v1/v1_resolver.go @@ -20,6 +20,8 @@ import ( "go.probo.inc/probo/pkg/accessreview" "go.probo.inc/probo/pkg/accessreview/drivers" "go.probo.inc/probo/pkg/connector" + "go.probo.inc/probo/pkg/consent" + "go.probo.inc/probo/pkg/cookieprovider" "go.probo.inc/probo/pkg/coredata" "go.probo.inc/probo/pkg/gid" "go.probo.inc/probo/pkg/iam" @@ -992,6 +994,26 @@ func (r *connectorResolver) Oauth2Scopes(ctx context.Context, obj *types.Connect return scopes, nil } +// TotalCount is the resolver for the totalCount field. +func (r *consentRecordConnectionResolver) TotalCount(ctx context.Context, obj *types.ConsentRecordConnection) (int, error) { + if err := r.authorize(ctx, obj.ParentID, probo.ActionConsentRecordList); err != nil { + return 0, err + } + + switch obj.Resolver.(type) { + case *cookieBannerResolver: + count, err := r.consent.CountConsentRecordsForCookieBannerID(ctx, obj.ParentID, obj.Filter) + if err != nil { + r.logger.ErrorCtx(ctx, "cannot count consent records", log.Error(err)) + return 0, gqlutils.Internal(ctx) + } + return count, nil + } + + r.logger.ErrorCtx(ctx, "unsupported resolver") + return 0, gqlutils.Internal(ctx) +} + // Organization is the resolver for the organization field. func (r *controlResolver) Organization(ctx context.Context, obj *types.Control) (*types.Organization, error) { if err := r.authorize(ctx, obj.ID, probo.ActionOrganizationGet); err != nil { @@ -1302,6 +1324,143 @@ func (r *controlConnectionResolver) TotalCount(ctx context.Context, obj *types.C return 0, gqlutils.Internal(ctx) } +// Theme is the resolver for the theme field. +func (r *cookieBannerResolver) Theme(ctx context.Context, obj *types.CookieBanner) (*types.CookieBannerTheme, error) { + if err := r.authorize(ctx, obj.ID, probo.ActionCookieBannerGet); err != nil { + return nil, err + } + + banner, err := r.consent.GetCookieBanner(ctx, obj.ID) + if err != nil { + r.logger.ErrorCtx(ctx, "cannot get cookie banner for theme", log.Error(err)) + return nil, gqlutils.Internal(ctx) + } + + return types.NewCookieBannerTheme(banner.Theme), nil +} + +// Analytics is the resolver for the analytics field. +func (r *cookieBannerResolver) Analytics(ctx context.Context, obj *types.CookieBanner) (*types.ConsentAnalytics, error) { + if err := r.authorize(ctx, obj.ID, probo.ActionCookieBannerGet); err != nil { + return nil, err + } + + analytics, err := r.consent.GetConsentAnalyticsForCookieBannerID(ctx, obj.ID) + if err != nil { + r.logger.ErrorCtx(ctx, "cannot get consent analytics", log.Error(err)) + return nil, gqlutils.Internal(ctx) + } + + return &types.ConsentAnalytics{ + TotalRecords: analytics.TotalRecords, + AcceptAllCount: analytics.AcceptAllCount, + RejectAllCount: analytics.RejectAllCount, + CustomizeCount: analytics.CustomizeCount, + AcceptCategoryCount: analytics.AcceptCategoryCount, + GPCCount: analytics.GPCCount, + }, nil +} + +// Categories is the resolver for the categories field. +func (r *cookieBannerResolver) Categories(ctx context.Context, obj *types.CookieBanner, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.CookieCategoryOrderBy) (*types.CookieCategoryConnection, error) { + if err := r.authorize(ctx, obj.ID, probo.ActionCookieCategoryList); err != nil { + return nil, err + } + + pageOrderBy := page.OrderBy[coredata.CookieCategoryOrderField]{ + Field: coredata.CookieCategoryOrderFieldRank, + Direction: page.OrderDirectionAsc, + } + + cursor := types.NewCursor(first, after, last, before, pageOrderBy) + + p, err := r.consent.ListCookieCategoriesForCookieBannerID(ctx, obj.ID, cursor) + if err != nil { + r.logger.ErrorCtx(ctx, "cannot list cookie categories", log.Error(err)) + return nil, gqlutils.Internal(ctx) + } + + return types.NewCookieCategoryConnection(p, r, obj.ID), nil +} + +// ConsentRecords is the resolver for the consentRecords field. +func (r *cookieBannerResolver) ConsentRecords(ctx context.Context, obj *types.CookieBanner, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.ConsentRecordOrderBy, filter *types.ConsentRecordFilter) (*types.ConsentRecordConnection, error) { + if err := r.authorize(ctx, obj.ID, probo.ActionConsentRecordList); err != nil { + return nil, err + } + + pageOrderBy := page.OrderBy[coredata.ConsentRecordOrderField]{ + Field: coredata.ConsentRecordOrderFieldCreatedAt, + Direction: page.OrderDirectionDesc, + } + + cursor := types.NewCursor(first, after, last, before, pageOrderBy) + + var actionFilter *coredata.ConsentAction + if filter != nil { + actionFilter = filter.Action + } + consentFilter := coredata.NewConsentRecordFilter(actionFilter) + + p, err := r.consent.ListConsentRecordsForCookieBannerID(ctx, obj.ID, cursor, consentFilter) + if err != nil { + r.logger.ErrorCtx(ctx, "cannot list consent records", log.Error(err)) + return nil, gqlutils.Internal(ctx) + } + + return types.NewConsentRecordConnection(p, r, obj.ID, consentFilter), nil +} + +// Permission is the resolver for the permission field. +func (r *cookieBannerResolver) Permission(ctx context.Context, obj *types.CookieBanner, action string) (bool, error) { + return r.Resolver.Permission(ctx, obj, action) +} + +// TotalCount is the resolver for the totalCount field. +func (r *cookieBannerConnectionResolver) TotalCount(ctx context.Context, obj *types.CookieBannerConnection) (int, error) { + if err := r.authorize(ctx, obj.ParentID, probo.ActionCookieBannerList); err != nil { + return 0, err + } + + switch obj.Resolver.(type) { + case *organizationResolver: + count, err := r.consent.CountCookieBannersForOrganizationID(ctx, obj.ParentID) + if err != nil { + r.logger.ErrorCtx(ctx, "cannot count cookie banners", log.Error(err)) + return 0, gqlutils.Internal(ctx) + } + return count, nil + } + + r.logger.ErrorCtx(ctx, "unsupported resolver") + return 0, gqlutils.Internal(ctx) +} + +// TotalCount is the resolver for the totalCount field. +func (r *cookieCategoryConnectionResolver) TotalCount(ctx context.Context, obj *types.CookieCategoryConnection) (int, error) { + if err := r.authorize(ctx, obj.ParentID, probo.ActionCookieCategoryList); err != nil { + return 0, err + } + + switch obj.Resolver.(type) { + case *cookieBannerResolver: + count, err := r.consent.CountCookieCategoriesForCookieBannerID(ctx, obj.ParentID) + if err != nil { + r.logger.ErrorCtx(ctx, "cannot count cookie categories", log.Error(err)) + return 0, gqlutils.Internal(ctx) + } + return count, nil + } + + r.logger.ErrorCtx(ctx, "unsupported resolver") + return 0, gqlutils.Internal(ctx) +} + +// Category is the resolver for the category field. +func (r *cookieProviderResolver) Category(ctx context.Context, obj *cookieprovider.Provider) (string, error) { + return string(obj.Category), nil +} + // Permission is the resolver for the permission field. func (r *customDomainResolver) Permission(ctx context.Context, obj *types.CustomDomain, action string) (bool, error) { return r.Resolver.Permission(ctx, obj, action) @@ -7792,9 +7951,6 @@ func (r *mutationResolver) RecordAccessEntryDecision(ctx context.Context, input scope := coredata.NewScopeFromObjectID(input.AccessEntryID) - // Resolve the profile ID from the session's identity. - // The profile may not exist for every identity, in which - // case decided_by will be left nil. identity := authn.IdentityFromContext(ctx) if identity == nil { return nil, fmt.Errorf("no identity in context") @@ -7840,7 +7996,6 @@ func (r *mutationResolver) RecordAccessEntryDecisions(ctx context.Context, input return nil, fmt.Errorf("cannot record decisions: batch size %d exceeds maximum of %d", len(input.Decisions), maxBatchSize) } - // Authorize each entry individually to prevent cross-org bypass. for _, d := range input.Decisions { if err := r.authorize(ctx, d.AccessEntryID, probo.ActionAccessEntryDecide); err != nil { return nil, err @@ -7855,8 +8010,6 @@ func (r *mutationResolver) RecordAccessEntryDecisions(ctx context.Context, input tenantID := input.Decisions[0].AccessEntryID.TenantID() scope := coredata.NewScope(tenantID) - // Cache profile lookups per organization so we resolve the correct - // decidedByID for each entry even when a batch spans multiple orgs. profileCache := make(map[gid.GID]*gid.GID) decisions := make([]accessreview.RecordAccessEntryDecisionRequest, len(input.Decisions)) @@ -8089,6 +8242,268 @@ func (r *mutationResolver) DeleteSlackConnection(ctx context.Context, input type }, nil } +// CreateCookieBanner is the resolver for the createCookieBanner field. +func (r *mutationResolver) CreateCookieBanner(ctx context.Context, input types.CreateCookieBannerInput) (*types.CreateCookieBannerPayload, error) { + if err := r.authorize(ctx, input.OrganizationID, probo.ActionCookieBannerCreate); err != nil { + return nil, err + } + + var domain string + if input.Domain != nil { + domain = *input.Domain + } + + banner, err := r.consent.CreateCookieBanner( + ctx, + consent.CreateCookieBannerRequest{ + OrganizationID: input.OrganizationID, + Name: input.Name, + Domain: domain, + }, + ) + if err != nil { + if validationErrors, ok := errors.AsType[validator.ValidationErrors](err); ok { + return nil, gqlutils.InvalidValidationErrors(ctx, validationErrors) + } + r.logger.ErrorCtx(ctx, "cannot create cookie banner", log.Error(err)) + return nil, gqlutils.Internal(ctx) + } + + return &types.CreateCookieBannerPayload{ + CookieBanner: types.NewCookieBanner(banner, r.baseURL), + CookieBannerEdge: types.NewCookieBannerEdge(banner, coredata.CookieBannerOrderFieldCreatedAt, r.baseURL), + }, nil +} + +// UpdateCookieBanner is the resolver for the updateCookieBanner field. +func (r *mutationResolver) UpdateCookieBanner(ctx context.Context, input types.UpdateCookieBannerInput) (*types.UpdateCookieBannerPayload, error) { + if err := r.authorize(ctx, input.ID, probo.ActionCookieBannerUpdate); err != nil { + return nil, err + } + + req := consent.UpdateCookieBannerRequest{ + ID: input.ID, + Name: input.Name, + Domain: input.Domain, + Title: input.Title, + Description: input.Description, + AcceptAllLabel: input.AcceptAllLabel, + RejectAllLabel: input.RejectAllLabel, + SavePreferencesLabel: input.SavePreferencesLabel, + PrivacyPolicyURL: input.PrivacyPolicyURL, + ConsentExpiryDays: input.ConsentExpiryDays, + ConsentMode: input.ConsentMode, + } + + if input.Theme != nil { + if err := input.Theme.Validate(); err != nil { + if validationErrors, ok := errors.AsType[validator.ValidationErrors](err); ok { + return nil, gqlutils.InvalidValidationErrors(ctx, validationErrors) + } + return nil, gqlutils.Internal(ctx) + } + + existing, err := r.consent.GetCookieBanner(ctx, input.ID) + if err != nil { + r.logger.ErrorCtx(ctx, "cannot get cookie banner for theme update", log.Error(err)) + return nil, gqlutils.Internal(ctx) + } + req.Theme = types.MergeThemeInput(existing.Theme, input.Theme) + } + + banner, err := r.consent.UpdateCookieBanner(ctx, req) + if err != nil { + if validationErrors, ok := errors.AsType[validator.ValidationErrors](err); ok { + return nil, gqlutils.InvalidValidationErrors(ctx, validationErrors) + } + r.logger.ErrorCtx(ctx, "cannot update cookie banner", log.Error(err)) + return nil, gqlutils.Internal(ctx) + } + + return &types.UpdateCookieBannerPayload{ + CookieBanner: types.NewCookieBanner(banner, r.baseURL), + }, nil +} + +// DeleteCookieBanner is the resolver for the deleteCookieBanner field. +func (r *mutationResolver) DeleteCookieBanner(ctx context.Context, input types.DeleteCookieBannerInput) (*types.DeleteCookieBannerPayload, error) { + if err := r.authorize(ctx, input.ID, probo.ActionCookieBannerDelete); err != nil { + return nil, err + } + + err := r.consent.DeleteCookieBanner(ctx, input.ID) + if err != nil { + r.logger.ErrorCtx(ctx, "cannot delete cookie banner", log.Error(err)) + return nil, gqlutils.Internal(ctx) + } + + return &types.DeleteCookieBannerPayload{ + DeletedCookieBannerID: input.ID, + }, nil +} + +// PublishCookieBanner is the resolver for the publishCookieBanner field. +func (r *mutationResolver) PublishCookieBanner(ctx context.Context, input types.PublishCookieBannerInput) (*types.PublishCookieBannerPayload, error) { + if err := r.authorize(ctx, input.ID, probo.ActionCookieBannerUpdate); err != nil { + return nil, err + } + + banner, err := r.consent.PublishCookieBanner(ctx, input.ID) + if err != nil { + r.logger.ErrorCtx(ctx, "cannot publish cookie banner", log.Error(err)) + return nil, gqlutils.Internal(ctx) + } + + return &types.PublishCookieBannerPayload{ + CookieBanner: types.NewCookieBanner(banner, r.baseURL), + }, nil +} + +// DisableCookieBanner is the resolver for the disableCookieBanner field. +func (r *mutationResolver) DisableCookieBanner(ctx context.Context, input types.DisableCookieBannerInput) (*types.DisableCookieBannerPayload, error) { + if err := r.authorize(ctx, input.ID, probo.ActionCookieBannerUpdate); err != nil { + return nil, err + } + + banner, err := r.consent.DisableCookieBanner(ctx, input.ID) + if err != nil { + r.logger.ErrorCtx(ctx, "cannot disable cookie banner", log.Error(err)) + return nil, gqlutils.Internal(ctx) + } + + return &types.DisableCookieBannerPayload{ + CookieBanner: types.NewCookieBanner(banner, r.baseURL), + }, nil +} + +// CreateCookieCategory is the resolver for the createCookieCategory field. +func (r *mutationResolver) CreateCookieCategory(ctx context.Context, input types.CreateCookieCategoryInput) (*types.CreateCookieCategoryPayload, error) { + if err := r.authorize(ctx, input.CookieBannerID, probo.ActionCookieCategoryCreate); err != nil { + return nil, err + } + + var description string + if input.Description != nil { + description = *input.Description + } + + var rank int + if input.Rank != nil { + rank = *input.Rank + } + + var cookies coredata.CookieItems + for _, c := range input.Cookies { + cookies = append(cookies, *c) + } + + category, err := r.consent.CreateCookieCategory( + ctx, + consent.CreateCookieCategoryRequest{ + CookieBannerID: input.CookieBannerID, + Name: input.Name, + Description: description, + Rank: rank, + Cookies: cookies, + }, + ) + if err != nil { + if validationErrors, ok := errors.AsType[validator.ValidationErrors](err); ok { + return nil, gqlutils.InvalidValidationErrors(ctx, validationErrors) + } + r.logger.ErrorCtx(ctx, "cannot create cookie category", log.Error(err)) + return nil, gqlutils.Internal(ctx) + } + + return &types.CreateCookieCategoryPayload{ + CookieCategory: types.NewCookieCategory(category), + }, nil +} + +// UpdateCookieCategory is the resolver for the updateCookieCategory field. +func (r *mutationResolver) UpdateCookieCategory(ctx context.Context, input types.UpdateCookieCategoryInput) (*types.UpdateCookieCategoryPayload, error) { + if err := r.authorize(ctx, input.ID, probo.ActionCookieCategoryUpdate); err != nil { + return nil, err + } + + var cookies *coredata.CookieItems + if input.Cookies != nil { + items := make(coredata.CookieItems, 0, len(input.Cookies)) + for _, c := range input.Cookies { + items = append(items, *c) + } + cookies = &items + } + + category, err := r.consent.UpdateCookieCategory( + ctx, + consent.UpdateCookieCategoryRequest{ + ID: input.ID, + Name: input.Name, + Description: input.Description, + Rank: input.Rank, + Cookies: cookies, + }, + ) + if err != nil { + if validationErrors, ok := errors.AsType[validator.ValidationErrors](err); ok { + return nil, gqlutils.InvalidValidationErrors(ctx, validationErrors) + } + r.logger.ErrorCtx(ctx, "cannot update cookie category", log.Error(err)) + return nil, gqlutils.Internal(ctx) + } + + return &types.UpdateCookieCategoryPayload{ + CookieCategory: types.NewCookieCategory(category), + }, nil +} + +// DeleteCookieCategory is the resolver for the deleteCookieCategory field. +func (r *mutationResolver) DeleteCookieCategory(ctx context.Context, input types.DeleteCookieCategoryInput) (*types.DeleteCookieCategoryPayload, error) { + if err := r.authorize(ctx, input.ID, probo.ActionCookieCategoryDelete); err != nil { + return nil, err + } + + err := r.consent.DeleteCookieCategory(ctx, input.ID) + if err != nil { + if errors.Is(err, consent.ErrCannotDeleteRequiredCategory) { + return nil, gqlutils.Invalidf(ctx, "cannot delete a required cookie category") + } + r.logger.ErrorCtx(ctx, "cannot delete cookie category", log.Error(err)) + return nil, gqlutils.Internal(ctx) + } + + return &types.DeleteCookieCategoryPayload{ + DeletedCookieCategoryID: input.ID, + }, nil +} + +// AddCookiesFromProvider is the resolver for the addCookiesFromProvider field. +func (r *mutationResolver) AddCookiesFromProvider(ctx context.Context, input types.AddCookiesFromProviderInput) (*types.AddCookiesFromProviderPayload, error) { + if err := r.authorize(ctx, input.CookieCategoryID, probo.ActionCookieCategoryUpdate); err != nil { + return nil, err + } + + category, err := r.consent.AddCookiesFromProvider( + ctx, + consent.AddCookiesFromProviderRequest{ + CookieCategoryID: input.CookieCategoryID, + ProviderKey: input.ProviderKey, + }, + ) + if err != nil { + if validationErrors, ok := errors.AsType[validator.ValidationErrors](err); ok { + return nil, gqlutils.InvalidValidationErrors(ctx, validationErrors) + } + r.logger.ErrorCtx(ctx, "cannot add cookies from provider", log.Error(err)) + return nil, gqlutils.Internal(ctx) + } + + return &types.AddCookiesFromProviderPayload{ + CookieCategory: types.NewCookieCategory(category), + }, nil +} + // Organization is the resolver for the organization field. func (r *obligationResolver) Organization(ctx context.Context, obj *types.Obligation) (*types.Organization, error) { if err := r.authorize(ctx, obj.ID, probo.ActionOrganizationGet); err != nil { @@ -9179,6 +9594,28 @@ func (r *organizationResolver) AuditLogEntries(ctx context.Context, obj *types.O return types.NewAuditLogEntryConnection(p, r, obj.ID, coredataFilter), nil } +// CookieBanners is the resolver for the cookieBanners field. +func (r *organizationResolver) CookieBanners(ctx context.Context, obj *types.Organization, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.CookieBannerOrderBy) (*types.CookieBannerConnection, error) { + if err := r.authorize(ctx, obj.ID, probo.ActionCookieBannerList); err != nil { + return nil, err + } + + pageOrderBy := page.OrderBy[coredata.CookieBannerOrderField]{ + Field: coredata.CookieBannerOrderFieldCreatedAt, + Direction: page.OrderDirectionDesc, + } + + cursor := types.NewCursor(first, after, last, before, pageOrderBy) + + p, err := r.consent.ListCookieBannersForOrganizationID(ctx, obj.ID, cursor) + if err != nil { + r.logger.ErrorCtx(ctx, "cannot list cookie banners", log.Error(err)) + return nil, gqlutils.Internal(ctx) + } + + return types.NewCookieBannerConnection(p, r, obj.ID, r.baseURL), nil +} + // AccessSources is the resolver for the accessSources field. func (r *organizationResolver) AccessSources(ctx context.Context, obj *types.Organization, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.AccessSourceOrder) (*types.AccessSourceConnection, error) { if err := r.authorize(ctx, obj.ID, probo.ActionAccessSourceList); err != nil { @@ -9743,6 +10180,15 @@ func (r *queryResolver) Node(ctx context.Context, id gid.GID) (types.Node, error } return types.NewAccessEntry(entry), nil } + case coredata.CookieBannerEntityType: + action = probo.ActionCookieBannerGet + loadNode = func(ctx context.Context, id gid.GID) (types.Node, error) { + banner, err := r.consent.GetCookieBanner(ctx, id) + if err != nil { + return nil, err + } + return types.NewCookieBanner(banner, r.baseURL), nil + } default: } @@ -9782,6 +10228,33 @@ func (r *queryResolver) Viewer(ctx context.Context) (*types.Viewer, error) { return &types.Viewer{ID: viewerID}, nil } +// CookieProviders is the resolver for the cookieProviders field. +func (r *queryResolver) CookieProviders(ctx context.Context, category *string) ([]*cookieprovider.Provider, error) { + var providers []cookieprovider.Provider + if category != nil { + providers = cookieprovider.ByCategory(cookieprovider.Category(*category)) + } else { + providers = cookieprovider.All() + } + + result := make([]*cookieprovider.Provider, len(providers)) + for i := range providers { + result[i] = &providers[i] + } + + return result, nil +} + +// CookieProvider is the resolver for the cookieProvider field. +func (r *queryResolver) CookieProvider(ctx context.Context, key string) (*cookieprovider.Provider, error) { + p, ok := cookieprovider.ByKey(key) + if !ok { + return nil, nil + } + + return &p, nil +} + // DownloadURL is the resolver for the downloadUrl field. func (r *reportResolver) DownloadURL(ctx context.Context, obj *types.Report) (*string, error) { if err := r.authorize(ctx, obj.ID, probo.ActionReportDownloadUrlGet); err != nil { @@ -11868,6 +12341,11 @@ func (r *Resolver) ComplianceFramework() schema.ComplianceFrameworkResolver { // Connector returns schema.ConnectorResolver implementation. func (r *Resolver) Connector() schema.ConnectorResolver { return &connectorResolver{r} } +// ConsentRecordConnection returns schema.ConsentRecordConnectionResolver implementation. +func (r *Resolver) ConsentRecordConnection() schema.ConsentRecordConnectionResolver { + return &consentRecordConnectionResolver{r} +} + // Control returns schema.ControlResolver implementation. func (r *Resolver) Control() schema.ControlResolver { return &controlResolver{r} } @@ -11876,6 +12354,22 @@ func (r *Resolver) ControlConnection() schema.ControlConnectionResolver { return &controlConnectionResolver{r} } +// CookieBanner returns schema.CookieBannerResolver implementation. +func (r *Resolver) CookieBanner() schema.CookieBannerResolver { return &cookieBannerResolver{r} } + +// CookieBannerConnection returns schema.CookieBannerConnectionResolver implementation. +func (r *Resolver) CookieBannerConnection() schema.CookieBannerConnectionResolver { + return &cookieBannerConnectionResolver{r} +} + +// CookieCategoryConnection returns schema.CookieCategoryConnectionResolver implementation. +func (r *Resolver) CookieCategoryConnection() schema.CookieCategoryConnectionResolver { + return &cookieCategoryConnectionResolver{r} +} + +// CookieProvider returns schema.CookieProviderResolver implementation. +func (r *Resolver) CookieProvider() schema.CookieProviderResolver { return &cookieProviderResolver{r} } + // CustomDomain returns schema.CustomDomainResolver implementation. func (r *Resolver) CustomDomain() schema.CustomDomainResolver { return &customDomainResolver{r} } @@ -12216,8 +12710,13 @@ type auditLogEntryConnectionResolver struct{ *Resolver } type complianceExternalURLResolver struct{ *Resolver } type complianceFrameworkResolver struct{ *Resolver } type connectorResolver struct{ *Resolver } +type consentRecordConnectionResolver struct{ *Resolver } type controlResolver struct{ *Resolver } type controlConnectionResolver struct{ *Resolver } +type cookieBannerResolver struct{ *Resolver } +type cookieBannerConnectionResolver struct{ *Resolver } +type cookieCategoryConnectionResolver struct{ *Resolver } +type cookieProviderResolver struct{ *Resolver } type customDomainResolver struct{ *Resolver } type dataProtectionImpactAssessmentResolver struct{ *Resolver } type dataProtectionImpactAssessmentConnectionResolver struct{ *Resolver } diff --git a/pkg/server/api/cookiebanner/v1/handler.go b/pkg/server/api/cookiebanner/v1/handler.go new file mode 100644 index 000000000..c17ee6738 --- /dev/null +++ b/pkg/server/api/cookiebanner/v1/handler.go @@ -0,0 +1,157 @@ +// Copyright (c) 2025-2026 Probo Inc . +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR +// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +// PERFORMANCE OF THIS SOFTWARE. + +package v1 + +import ( + "encoding/json" + "io" + "net/http" + + "github.com/go-chi/chi/v5" + "github.com/go-chi/cors" + "go.gearno.de/kit/httpserver" + "go.gearno.de/kit/log" + cookiebanner "go.probo.inc/probo/packages/cookie-banner" + "go.probo.inc/probo/pkg/consent" + "go.probo.inc/probo/pkg/coredata" + "go.probo.inc/probo/pkg/gid" + "go.probo.inc/probo/pkg/server/api/cookiebanner/v1/types" +) + +type handler struct { + consentService *consent.Service + logger *log.Logger +} + +func NewMux(logger *log.Logger, consentService *consent.Service) http.Handler { + h := &handler{ + consentService: consentService, + logger: logger, + } + + r := chi.NewRouter() + + r.Use( + cors.Handler( + cors.Options{ + AllowedOrigins: []string{"*"}, + AllowedMethods: []string{"GET", "POST", "OPTIONS"}, + AllowedHeaders: []string{"content-type"}, + AllowCredentials: false, + MaxAge: 600, + }, + ), + ) + + r.Get("/widget.js", h.serveWidget) + r.Get("/{bannerID}/config", h.getConfig) + r.Post("/{bannerID}/consents", h.recordConsent) + + return r +} + +func (h *handler) serveWidget(w http.ResponseWriter, r *http.Request) { + if r.Header.Get("If-None-Match") == cookiebanner.WidgetETag { + w.WriteHeader(http.StatusNotModified) + return + } + + w.Header().Set("Content-Type", "application/javascript") + w.Header().Set("Cache-Control", "public, no-cache") + w.Header().Set("ETag", cookiebanner.WidgetETag) + w.WriteHeader(http.StatusOK) + _, _ = w.Write(cookiebanner.WidgetBundle) +} + +func (h *handler) getConfig(w http.ResponseWriter, r *http.Request) { + bannerIDStr := chi.URLParam(r, "bannerID") + bannerID, err := gid.ParseGID(bannerIDStr) + if err != nil { + httpserver.RenderJSON(w, http.StatusBadRequest, map[string]string{"error": "invalid banner ID"}) + return + } + + config, err := h.consentService.GetPublishedBannerConfig(r.Context(), bannerID) + if err != nil { + h.logger.ErrorCtx(r.Context(), "cannot get banner config", log.Error(err)) + httpserver.RenderJSON(w, http.StatusNotFound, map[string]string{"error": "banner not found"}) + return + } + + resp := types.NewConfigResponse(config.Banner, config.Categories) + + w.Header().Set("Cache-Control", "public, max-age=300") + httpserver.RenderJSON(w, http.StatusOK, resp) +} + +func (h *handler) recordConsent(w http.ResponseWriter, r *http.Request) { + bannerIDStr := chi.URLParam(r, "bannerID") + bannerID, err := gid.ParseGID(bannerIDStr) + if err != nil { + httpserver.RenderJSON(w, http.StatusBadRequest, map[string]string{"error": "invalid banner ID"}) + return + } + + body, err := io.ReadAll(io.LimitReader(r.Body, 1<<20)) // 1MB limit + if err != nil { + httpserver.RenderJSON(w, http.StatusBadRequest, map[string]string{"error": "cannot read request body"}) + return + } + + var req struct { + VisitorID string `json:"visitor_id"` + ConsentData json.RawMessage `json:"consent_data"` + Action string `json:"action"` + } + + if err := json.Unmarshal(body, &req); err != nil { + httpserver.RenderJSON(w, http.StatusBadRequest, map[string]string{"error": "invalid JSON"}) + return + } + + if req.VisitorID == "" { + httpserver.RenderJSON(w, http.StatusBadRequest, map[string]string{"error": "visitor_id is required"}) + return + } + + var action coredata.ConsentAction + if err := action.Scan(req.Action); err != nil { + httpserver.RenderJSON(w, http.StatusBadRequest, map[string]string{"error": "invalid action"}) + return + } + + ipAddress := r.RemoteAddr + + userAgent := r.UserAgent() + + err = h.consentService.RecordConsent( + r.Context(), + bannerID, + consent.RecordConsentRequest{ + VisitorID: req.VisitorID, + ConsentData: req.ConsentData, + Action: action, + }, + ipAddress, + userAgent, + ) + if err != nil { + h.logger.ErrorCtx(r.Context(), "cannot record consent", log.Error(err)) + httpserver.RenderJSON(w, http.StatusInternalServerError, map[string]string{"error": "cannot record consent"}) + return + } + + httpserver.RenderJSON(w, http.StatusCreated, map[string]string{"status": "ok"}) +} diff --git a/pkg/server/api/cookiebanner/v1/types/types.go b/pkg/server/api/cookiebanner/v1/types/types.go new file mode 100644 index 000000000..d9021ffbc --- /dev/null +++ b/pkg/server/api/cookiebanner/v1/types/types.go @@ -0,0 +1,188 @@ +// Copyright (c) 2025-2026 Probo Inc . +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR +// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +// PERFORMANCE OF THIS SOFTWARE. + +package types + +import ( + "encoding/json" + + "strings" + + "go.probo.inc/probo/pkg/coredata" +) + +type ( + ThemeResponse struct { + PrimaryColor string `json:"primary_color"` + PrimaryTextColor string `json:"primary_text_color"` + SecondaryColor string `json:"secondary_color"` + SecondaryTextColor string `json:"secondary_text_color"` + BackgroundColor string `json:"background_color"` + TextColor string `json:"text_color"` + SecondaryTextBodyColor string `json:"secondary_text_body_color"` + BorderColor string `json:"border_color"` + FontFamily string `json:"font_family"` + BorderRadius int `json:"border_radius"` + Position string `json:"position"` + RevisitPosition string `json:"revisit_position"` + } + + CookieItemResponse struct { + Name string `json:"name"` + Duration string `json:"duration"` + Description string `json:"description"` + } + + CategoryResponse struct { + ID string `json:"id"` + Name string `json:"name"` + Description string `json:"description"` + Required bool `json:"required"` + Rank int `json:"rank"` + Cookies []CookieItemResponse `json:"cookies"` + } + + ConfigResponse struct { + ID string `json:"id"` + Title string `json:"title"` + Description string `json:"description"` + AcceptAllLabel string `json:"accept_all_label"` + RejectAllLabel string `json:"reject_all_label"` + SavePreferencesLabel string `json:"save_preferences_label"` + PrivacyPolicyURL string `json:"privacy_policy_url"` + ConsentExpiryDays int `json:"consent_expiry_days"` + ConsentMode string `json:"consent_mode"` + Version int `json:"version"` + Categories []CategoryResponse `json:"categories"` + Theme *ThemeResponse `json:"theme,omitempty"` + } +) + +var defaultTheme = ThemeResponse{ + PrimaryColor: "#2563eb", + PrimaryTextColor: "#ffffff", + SecondaryColor: "#1a1a1a", + SecondaryTextColor: "#ffffff", + BackgroundColor: "#ffffff", + TextColor: "#1a1a1a", + SecondaryTextBodyColor: "#4b5563", + BorderColor: "#e5e7eb", + FontFamily: "-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif", + BorderRadius: 8, + Position: "bottom", + RevisitPosition: "bottom-left", +} + +func NewCategoryResponse(cat *coredata.CookieCategory) CategoryResponse { + cookies := make([]CookieItemResponse, 0, len(cat.Cookies)) + for _, c := range cat.Cookies { + cookies = append(cookies, CookieItemResponse{ + Name: c.Name, + Duration: c.Duration, + Description: c.Description, + }) + } + + return CategoryResponse{ + ID: cat.ID.String(), + Name: cat.Name, + Description: cat.Description, + Required: cat.Required, + Rank: cat.Rank, + Cookies: cookies, + } +} + +func newThemeResponse(raw json.RawMessage) *ThemeResponse { + theme := defaultTheme + + if len(raw) > 0 { + var override struct { + PrimaryColor *string `json:"primary_color"` + PrimaryTextColor *string `json:"primary_text_color"` + SecondaryColor *string `json:"secondary_color"` + SecondaryTextColor *string `json:"secondary_text_color"` + BackgroundColor *string `json:"background_color"` + TextColor *string `json:"text_color"` + SecondaryTextBodyColor *string `json:"secondary_text_body_color"` + BorderColor *string `json:"border_color"` + FontFamily *string `json:"font_family"` + BorderRadius *int `json:"border_radius"` + Position *string `json:"position"` + RevisitPosition *string `json:"revisit_position"` + } + if err := json.Unmarshal(raw, &override); err == nil { + if override.PrimaryColor != nil { + theme.PrimaryColor = *override.PrimaryColor + } + if override.PrimaryTextColor != nil { + theme.PrimaryTextColor = *override.PrimaryTextColor + } + if override.SecondaryColor != nil { + theme.SecondaryColor = *override.SecondaryColor + } + if override.SecondaryTextColor != nil { + theme.SecondaryTextColor = *override.SecondaryTextColor + } + if override.BackgroundColor != nil { + theme.BackgroundColor = *override.BackgroundColor + } + if override.TextColor != nil { + theme.TextColor = *override.TextColor + } + if override.SecondaryTextBodyColor != nil { + theme.SecondaryTextBodyColor = *override.SecondaryTextBodyColor + } + if override.BorderColor != nil { + theme.BorderColor = *override.BorderColor + } + if override.FontFamily != nil { + theme.FontFamily = *override.FontFamily + } + if override.BorderRadius != nil { + theme.BorderRadius = *override.BorderRadius + } + if override.Position != nil { + theme.Position = *override.Position + } + if override.RevisitPosition != nil { + theme.RevisitPosition = *override.RevisitPosition + } + } + } + + return &theme +} + +func NewConfigResponse(banner *coredata.CookieBanner, categories coredata.CookieCategories) ConfigResponse { + cats := make([]CategoryResponse, 0, len(categories)) + for _, cat := range categories { + cats = append(cats, NewCategoryResponse(cat)) + } + + return ConfigResponse{ + ID: banner.ID.String(), + Title: banner.Title, + Description: banner.Description, + AcceptAllLabel: banner.AcceptAllLabel, + RejectAllLabel: banner.RejectAllLabel, + SavePreferencesLabel: banner.SavePreferencesLabel, + PrivacyPolicyURL: banner.PrivacyPolicyURL, + ConsentExpiryDays: banner.ConsentExpiryDays, + ConsentMode: strings.ReplaceAll(strings.ToLower(string(banner.ConsentMode)), "_", "-"), + Version: banner.Version, + Categories: cats, + Theme: newThemeResponse(banner.Theme), + } +} diff --git a/pkg/server/api/mcp/v1/resolver.go b/pkg/server/api/mcp/v1/resolver.go index aae168ef4..8c43894b8 100644 --- a/pkg/server/api/mcp/v1/resolver.go +++ b/pkg/server/api/mcp/v1/resolver.go @@ -23,6 +23,7 @@ import ( "go.gearno.de/kit/log" "go.probo.inc/probo/pkg/accessreview" + "go.probo.inc/probo/pkg/consent" "go.probo.inc/probo/pkg/gid" "go.probo.inc/probo/pkg/iam" "go.probo.inc/probo/pkg/probo" @@ -32,6 +33,7 @@ import ( type Resolver struct { proboSvc *probo.Service + consentSvc *consent.Service iamSvc *iam.Service accessReview *accessreview.Service logger *log.Logger diff --git a/pkg/server/api/mcp/v1/schema.resolvers.go b/pkg/server/api/mcp/v1/schema.resolvers.go index dac4ee06b..48c329ad0 100644 --- a/pkg/server/api/mcp/v1/schema.resolvers.go +++ b/pkg/server/api/mcp/v1/schema.resolvers.go @@ -13,6 +13,8 @@ import ( "github.com/modelcontextprotocol/go-sdk/mcp" "go.probo.inc/probo/pkg/accessreview" + "go.probo.inc/probo/pkg/consent" + "go.probo.inc/probo/pkg/cookieprovider" "go.probo.inc/probo/pkg/coredata" "go.probo.inc/probo/pkg/gid" "go.probo.inc/probo/pkg/iam" @@ -3920,3 +3922,308 @@ func (r *Resolver) ListMeasureDocumentsTool(ctx context.Context, req *mcp.CallTo return nil, types.NewListMeasureDocumentsOutput(docPage), nil } + +func (r *Resolver) ListCookieBannersTool(ctx context.Context, req *mcp.CallToolRequest, input *types.ListCookieBannersInput) (*mcp.CallToolResult, types.ListCookieBannersOutput, error) { + r.MustAuthorize(ctx, input.OrganizationID, probo.ActionCookieBannerList) + + pageOrderBy := page.OrderBy[coredata.CookieBannerOrderField]{ + Field: coredata.CookieBannerOrderFieldCreatedAt, + Direction: page.OrderDirectionDesc, + } + if input.OrderBy != nil { + pageOrderBy = page.OrderBy[coredata.CookieBannerOrderField]{ + Field: input.OrderBy.Field, + Direction: input.OrderBy.Direction, + } + } + + cursor := types.NewCursor(input.Size, input.Cursor, pageOrderBy) + + p, err := r.consentSvc.ListCookieBannersForOrganizationID(ctx, input.OrganizationID, cursor) + if err != nil { + return nil, types.ListCookieBannersOutput{}, fmt.Errorf("cannot list cookie banners: %w", err) + } + + return nil, types.NewListCookieBannersOutput(p), nil +} + +func (r *Resolver) GetCookieBannerTool(ctx context.Context, req *mcp.CallToolRequest, input *types.GetCookieBannerInput) (*mcp.CallToolResult, types.GetCookieBannerOutput, error) { + r.MustAuthorize(ctx, input.ID, probo.ActionCookieBannerGet) + + banner, err := r.consentSvc.GetCookieBanner(ctx, input.ID) + if err != nil { + return nil, types.GetCookieBannerOutput{}, fmt.Errorf("cannot get cookie banner: %w", err) + } + + return nil, types.GetCookieBannerOutput{ + CookieBanner: types.NewCookieBanner(banner), + }, nil +} + +func (r *Resolver) CreateCookieBannerTool(ctx context.Context, req *mcp.CallToolRequest, input *types.CreateCookieBannerInput) (*mcp.CallToolResult, types.CreateCookieBannerOutput, error) { + r.MustAuthorize(ctx, input.OrganizationID, probo.ActionCookieBannerCreate) + + banner, err := r.consentSvc.CreateCookieBanner( + ctx, + consent.CreateCookieBannerRequest{ + OrganizationID: input.OrganizationID, + Name: input.Name, + Domain: input.Domain, + }, + ) + if err != nil { + return nil, types.CreateCookieBannerOutput{}, fmt.Errorf("cannot create cookie banner: %w", err) + } + + return nil, types.CreateCookieBannerOutput{ + CookieBanner: types.NewCookieBanner(banner), + }, nil +} + +func (r *Resolver) UpdateCookieBannerTool(ctx context.Context, req *mcp.CallToolRequest, input *types.UpdateCookieBannerInput) (*mcp.CallToolResult, types.UpdateCookieBannerOutput, error) { + r.MustAuthorize(ctx, input.ID, probo.ActionCookieBannerUpdate) + + updateReq := consent.UpdateCookieBannerRequest{ + ID: input.ID, + Name: input.Name, + Domain: input.Domain, + Title: input.Title, + Description: input.Description, + AcceptAllLabel: input.AcceptAllLabel, + RejectAllLabel: input.RejectAllLabel, + SavePreferencesLabel: input.SavePreferencesLabel, + PrivacyPolicyURL: input.PrivacyPolicyURL, + ConsentExpiryDays: input.ConsentExpiryDays, + ConsentMode: input.ConsentMode, + } + + if input.Theme != nil { + existing, err := r.consentSvc.GetCookieBanner(ctx, input.ID) + if err != nil { + return nil, types.UpdateCookieBannerOutput{}, fmt.Errorf("cannot get cookie banner for theme update: %w", err) + } + updateReq.Theme = types.MergeThemeInput(existing.Theme, input.Theme) + } + + banner, err := r.consentSvc.UpdateCookieBanner(ctx, updateReq) + if err != nil { + return nil, types.UpdateCookieBannerOutput{}, fmt.Errorf("cannot update cookie banner: %w", err) + } + + return nil, types.UpdateCookieBannerOutput{ + CookieBanner: types.NewCookieBanner(banner), + }, nil +} + +func (r *Resolver) DeleteCookieBannerTool(ctx context.Context, req *mcp.CallToolRequest, input *types.DeleteCookieBannerInput) (*mcp.CallToolResult, types.DeleteCookieBannerOutput, error) { + r.MustAuthorize(ctx, input.ID, probo.ActionCookieBannerDelete) + + err := r.consentSvc.DeleteCookieBanner(ctx, input.ID) + if err != nil { + return nil, types.DeleteCookieBannerOutput{}, fmt.Errorf("cannot delete cookie banner: %w", err) + } + + return nil, types.DeleteCookieBannerOutput{ + DeletedCookieBannerID: input.ID, + }, nil +} + +func (r *Resolver) PublishCookieBannerTool(ctx context.Context, req *mcp.CallToolRequest, input *types.PublishCookieBannerInput) (*mcp.CallToolResult, types.PublishCookieBannerOutput, error) { + r.MustAuthorize(ctx, input.ID, probo.ActionCookieBannerUpdate) + + banner, err := r.consentSvc.PublishCookieBanner(ctx, input.ID) + if err != nil { + return nil, types.PublishCookieBannerOutput{}, fmt.Errorf("cannot publish cookie banner: %w", err) + } + + return nil, types.PublishCookieBannerOutput{ + CookieBanner: types.NewCookieBanner(banner), + }, nil +} + +func (r *Resolver) DisableCookieBannerTool(ctx context.Context, req *mcp.CallToolRequest, input *types.DisableCookieBannerInput) (*mcp.CallToolResult, types.DisableCookieBannerOutput, error) { + r.MustAuthorize(ctx, input.ID, probo.ActionCookieBannerUpdate) + + banner, err := r.consentSvc.DisableCookieBanner(ctx, input.ID) + if err != nil { + return nil, types.DisableCookieBannerOutput{}, fmt.Errorf("cannot disable cookie banner: %w", err) + } + + return nil, types.DisableCookieBannerOutput{ + CookieBanner: types.NewCookieBanner(banner), + }, nil +} + +func (r *Resolver) ListCookieCategoriesTool(ctx context.Context, req *mcp.CallToolRequest, input *types.ListCookieCategoriesInput) (*mcp.CallToolResult, types.ListCookieCategoriesOutput, error) { + r.MustAuthorize(ctx, input.CookieBannerID, probo.ActionCookieCategoryList) + + pageOrderBy := page.OrderBy[coredata.CookieCategoryOrderField]{ + Field: coredata.CookieCategoryOrderFieldRank, + Direction: page.OrderDirectionAsc, + } + if input.OrderBy != nil { + pageOrderBy = page.OrderBy[coredata.CookieCategoryOrderField]{ + Field: input.OrderBy.Field, + Direction: input.OrderBy.Direction, + } + } + + cursor := types.NewCursor(input.Size, input.Cursor, pageOrderBy) + + p, err := r.consentSvc.ListCookieCategoriesForCookieBannerID(ctx, input.CookieBannerID, cursor) + if err != nil { + return nil, types.ListCookieCategoriesOutput{}, fmt.Errorf("cannot list cookie categories: %w", err) + } + + return nil, types.NewListCookieCategoriesOutput(p), nil +} + +func (r *Resolver) GetCookieCategoryTool(ctx context.Context, req *mcp.CallToolRequest, input *types.GetCookieCategoryInput) (*mcp.CallToolResult, types.GetCookieCategoryOutput, error) { + r.MustAuthorize(ctx, input.ID, probo.ActionCookieCategoryGet) + + category, err := r.consentSvc.GetCookieCategory(ctx, input.ID) + if err != nil { + return nil, types.GetCookieCategoryOutput{}, fmt.Errorf("cannot get cookie category: %w", err) + } + + return nil, types.GetCookieCategoryOutput{ + CookieCategory: types.NewCookieCategory(category), + }, nil +} + +func (r *Resolver) CreateCookieCategoryTool(ctx context.Context, req *mcp.CallToolRequest, input *types.CreateCookieCategoryInput) (*mcp.CallToolResult, types.CreateCookieCategoryOutput, error) { + r.MustAuthorize(ctx, input.CookieBannerID, probo.ActionCookieCategoryCreate) + + category, err := r.consentSvc.CreateCookieCategory( + ctx, + consent.CreateCookieCategoryRequest{ + CookieBannerID: input.CookieBannerID, + Name: input.Name, + Description: input.Description, + Rank: input.Rank, + Cookies: input.Cookies, + }, + ) + if err != nil { + return nil, types.CreateCookieCategoryOutput{}, fmt.Errorf("cannot create cookie category: %w", err) + } + + return nil, types.CreateCookieCategoryOutput{ + CookieCategory: types.NewCookieCategory(category), + }, nil +} + +func (r *Resolver) UpdateCookieCategoryTool(ctx context.Context, req *mcp.CallToolRequest, input *types.UpdateCookieCategoryInput) (*mcp.CallToolResult, types.UpdateCookieCategoryOutput, error) { + r.MustAuthorize(ctx, input.ID, probo.ActionCookieCategoryUpdate) + + var cookies *coredata.CookieItems + if input.Cookies != nil { + items := coredata.CookieItems(input.Cookies) + cookies = &items + } + + category, err := r.consentSvc.UpdateCookieCategory( + ctx, + consent.UpdateCookieCategoryRequest{ + ID: input.ID, + Name: input.Name, + Description: input.Description, + Rank: input.Rank, + Cookies: cookies, + }, + ) + if err != nil { + return nil, types.UpdateCookieCategoryOutput{}, fmt.Errorf("cannot update cookie category: %w", err) + } + + return nil, types.UpdateCookieCategoryOutput{ + CookieCategory: types.NewCookieCategory(category), + }, nil +} + +func (r *Resolver) DeleteCookieCategoryTool(ctx context.Context, req *mcp.CallToolRequest, input *types.DeleteCookieCategoryInput) (*mcp.CallToolResult, types.DeleteCookieCategoryOutput, error) { + r.MustAuthorize(ctx, input.ID, probo.ActionCookieCategoryDelete) + + err := r.consentSvc.DeleteCookieCategory(ctx, input.ID) + if err != nil { + return nil, types.DeleteCookieCategoryOutput{}, fmt.Errorf("cannot delete cookie category: %w", err) + } + + return nil, types.DeleteCookieCategoryOutput{ + DeletedCookieCategoryID: input.ID, + }, nil +} + +func (r *Resolver) ListConsentRecordsTool(ctx context.Context, req *mcp.CallToolRequest, input *types.ListConsentRecordsInput) (*mcp.CallToolResult, types.ListConsentRecordsOutput, error) { + r.MustAuthorize(ctx, input.CookieBannerID, probo.ActionConsentRecordList) + + pageOrderBy := page.OrderBy[coredata.ConsentRecordOrderField]{ + Field: coredata.ConsentRecordOrderFieldCreatedAt, + Direction: page.OrderDirectionDesc, + } + if input.OrderBy != nil { + pageOrderBy = page.OrderBy[coredata.ConsentRecordOrderField]{ + Field: input.OrderBy.Field, + Direction: input.OrderBy.Direction, + } + } + + cursor := types.NewCursor(input.Size, input.Cursor, pageOrderBy) + + var filter *coredata.ConsentRecordFilter + if input.Filter != nil && input.Filter.Action != nil { + filter = coredata.NewConsentRecordFilter(input.Filter.Action) + } else { + filter = coredata.NewConsentRecordFilter(nil) + } + + p, err := r.consentSvc.ListConsentRecordsForCookieBannerID(ctx, input.CookieBannerID, cursor, filter) + if err != nil { + return nil, types.ListConsentRecordsOutput{}, fmt.Errorf("cannot list consent records: %w", err) + } + + return nil, types.NewListConsentRecordsOutput(p), nil +} + +func (r *Resolver) ListCookieProvidersTool(ctx context.Context, req *mcp.CallToolRequest, input *types.ListCookieProvidersInput) (*mcp.CallToolResult, types.ListCookieProvidersOutput, error) { + var providers []cookieprovider.Provider + if input.Category != nil { + providers = cookieprovider.ByCategory(cookieprovider.Category(*input.Category)) + } else { + providers = cookieprovider.All() + } + + return nil, types.ListCookieProvidersOutput{ + CookieProviders: providers, + }, nil +} + +func (r *Resolver) GetCookieProviderTool(ctx context.Context, req *mcp.CallToolRequest, input *types.GetCookieProviderInput) (*mcp.CallToolResult, types.GetCookieProviderOutput, error) { + provider, ok := cookieprovider.ByKey(input.Key) + if !ok { + return nil, types.GetCookieProviderOutput{}, fmt.Errorf("cannot get cookie provider: unknown provider key %q", input.Key) + } + + return nil, types.GetCookieProviderOutput{ + CookieProvider: provider, + }, nil +} + +func (r *Resolver) AddCookiesFromProviderTool(ctx context.Context, req *mcp.CallToolRequest, input *types.AddCookiesFromProviderInput) (*mcp.CallToolResult, types.AddCookiesFromProviderOutput, error) { + r.MustAuthorize(ctx, input.CookieCategoryID, probo.ActionCookieCategoryUpdate) + + category, err := r.consentSvc.AddCookiesFromProvider( + ctx, + consent.AddCookiesFromProviderRequest{ + CookieCategoryID: input.CookieCategoryID, + ProviderKey: input.ProviderKey, + }, + ) + if err != nil { + return nil, types.AddCookiesFromProviderOutput{}, fmt.Errorf("cannot add cookies from provider: %w", err) + } + + return nil, types.AddCookiesFromProviderOutput{ + CookieCategory: types.NewCookieCategory(category), + }, nil +} diff --git a/pkg/server/api/mcp/v1/specification.yaml b/pkg/server/api/mcp/v1/specification.yaml index 06135e2b8..20feb6365 100644 --- a/pkg/server/api/mcp/v1/specification.yaml +++ b/pkg/server/api/mcp/v1/specification.yaml @@ -7382,6 +7382,308 @@ components: audit_log_entry: $ref: "#/components/schemas/AuditLogEntry" + CookieBannerState: + type: string + enum: + - draft + - published + - disabled + go.probo.inc/mcpgen/type: go.probo.inc/probo/pkg/coredata.CookieBannerState + + ConsentAction: + type: string + enum: + - ACCEPT_ALL + - REJECT_ALL + - CUSTOMIZE + - ACCEPT_CATEGORY + - GPC + go.probo.inc/mcpgen/type: go.probo.inc/probo/pkg/coredata.ConsentAction + + ConsentMode: + type: string + enum: + - OPT_IN + - OPT_OUT + go.probo.inc/mcpgen/type: go.probo.inc/probo/pkg/coredata.ConsentMode + + CookieBannerOrderField: + type: string + enum: + - CREATED_AT + go.probo.inc/mcpgen/type: go.probo.inc/probo/pkg/coredata.CookieBannerOrderField + + CookieBannerOrderBy: + type: object + required: + - field + - direction + properties: + field: + $ref: "#/components/schemas/CookieBannerOrderField" + description: Cookie banner order field + direction: + $ref: "#/components/schemas/OrderDirection" + description: Cookie banner order direction + + CookieCategoryOrderField: + type: string + enum: + - RANK + go.probo.inc/mcpgen/type: go.probo.inc/probo/pkg/coredata.CookieCategoryOrderField + + CookieCategoryOrderBy: + type: object + required: + - field + - direction + properties: + field: + $ref: "#/components/schemas/CookieCategoryOrderField" + description: Cookie category order field + direction: + $ref: "#/components/schemas/OrderDirection" + description: Cookie category order direction + + ConsentRecordOrderField: + type: string + enum: + - CREATED_AT + go.probo.inc/mcpgen/type: go.probo.inc/probo/pkg/coredata.ConsentRecordOrderField + + ConsentRecordOrderBy: + type: object + required: + - field + - direction + properties: + field: + $ref: "#/components/schemas/ConsentRecordOrderField" + description: Consent record order field + direction: + $ref: "#/components/schemas/OrderDirection" + description: Consent record order direction + + CookieBannerTheme: + type: object + properties: + primary_color: + type: string + description: Primary button color + primary_text_color: + type: string + description: Text color on primary buttons + secondary_color: + type: string + description: Secondary button color + secondary_text_color: + type: string + description: Text color on secondary buttons + background_color: + type: string + description: Banner background color + text_color: + type: string + description: Title text color + secondary_text_body_color: + type: string + description: Description text color + border_color: + type: string + description: Border color + font_family: + type: string + description: Font family + border_radius: + type: integer + description: Border radius in pixels + position: + type: string + description: Banner position (bottom, bottom-left, bottom-right, center) + revisit_position: + type: string + description: Revisit icon position (bottom-left, bottom-right) + + CookieBanner: + type: object + required: + - id + - organization_id + - name + - domain + - state + - title + - description + - accept_all_label + - reject_all_label + - save_preferences_label + - privacy_policy_url + - consent_expiry_days + - consent_mode + - version + - created_at + - updated_at + properties: + id: + $ref: "#/components/schemas/GID" + description: Cookie banner ID + organization_id: + $ref: "#/components/schemas/GID" + description: Organization ID + name: + type: string + description: Cookie banner name + domain: + type: string + description: Domain the banner is deployed on + state: + $ref: "#/components/schemas/CookieBannerState" + description: Cookie banner state + title: + type: string + description: Banner title displayed to visitors + description: + type: string + description: Banner description displayed to visitors + accept_all_label: + type: string + description: Accept all button label + reject_all_label: + type: string + description: Reject all button label + save_preferences_label: + type: string + description: Save preferences button label + privacy_policy_url: + type: string + description: Privacy policy URL + consent_expiry_days: + type: integer + description: Days until consent expires + consent_mode: + $ref: "#/components/schemas/ConsentMode" + description: Consent mode (opt-in requires explicit consent, opt-out assumes consent) + version: + type: integer + description: Banner configuration version + theme: + $ref: "#/components/schemas/CookieBannerTheme" + description: Visual theme configuration + created_at: + type: string + format: date-time + description: Creation timestamp + updated_at: + type: string + format: date-time + description: Update timestamp + + CookieCategory: + type: object + required: + - id + - cookie_banner_id + - name + - description + - required + - rank + - created_at + - updated_at + properties: + id: + $ref: "#/components/schemas/GID" + description: Cookie category ID + cookie_banner_id: + $ref: "#/components/schemas/GID" + description: Cookie banner ID + name: + type: string + description: Category name + description: + type: string + description: Category description + required: + type: boolean + description: Whether this category is required + rank: + type: integer + description: Display order rank + cookies: + type: array + items: + $ref: "#/components/schemas/CookieCategoryItem" + description: Individual cookies managed by this category + created_at: + type: string + format: date-time + description: Creation timestamp + updated_at: + type: string + format: date-time + description: Update timestamp + + CookieCategoryItem: + type: object + go.probo.inc/mcpgen/type: go.probo.inc/probo/pkg/coredata.CookieItem + required: + - name + - duration + - description + properties: + name: + type: string + description: Cookie name + duration: + type: string + description: Cookie duration (e.g. "1 year", "session", "Never Expires") + description: + type: string + description: Cookie description + + ConsentRecord: + type: object + required: + - id + - cookie_banner_id + - visitor_id + - consent_data + - action + - banner_version + - created_at + properties: + id: + $ref: "#/components/schemas/GID" + description: Consent record ID + cookie_banner_id: + $ref: "#/components/schemas/GID" + description: Cookie banner ID + visitor_id: + type: string + description: Visitor identifier + ip_address: + type: + - string + - "null" + description: Anonymized IP address + user_agent: + type: + - string + - "null" + description: User agent string + consent_data: + type: string + description: Consent data as JSON string + action: + $ref: "#/components/schemas/ConsentAction" + description: Consent action + banner_version: + type: integer + description: Banner version at time of consent + created_at: + type: string + format: date-time + description: Creation timestamp + ListAuditLogEntriesInput: type: object required: @@ -7389,85 +7691,533 @@ components: properties: organization_id: $ref: "#/components/schemas/GID" - description: Organization ID + description: Organization ID + size: + type: integer + description: Page size + cursor: + $ref: "#/components/schemas/CursorKey" + description: Page cursor + filter: + type: object + properties: + action: + type: string + description: Filter by action (e.g. "core:vendor:create") + actor_id: + $ref: "#/components/schemas/GID" + description: Filter by actor ID + resource_type: + type: string + description: Filter by resource type (e.g. "Vendor") + resource_id: + $ref: "#/components/schemas/GID" + description: Filter by resource ID + + ListAuditLogEntriesOutput: + type: object + required: + - audit_log_entries + properties: + next_cursor: + $ref: "#/components/schemas/CursorKey" + description: Next cursor + audit_log_entries: + type: array + items: + $ref: "#/components/schemas/AuditLogEntry" + + AuditLogEntry: + type: object + required: + - id + - organization_id + - actor_id + - actor_type + - action + - resource_type + - resource_id + - created_at + properties: + id: + $ref: "#/components/schemas/GID" + description: Audit log entry ID + organization_id: + $ref: "#/components/schemas/GID" + description: Organization ID + actor_id: + $ref: "#/components/schemas/GID" + description: ID of the actor who performed the action + actor_type: + type: string + enum: [USER, API_KEY, SYSTEM] + go.probo.inc/mcpgen/type: go.probo.inc/probo/pkg/coredata.AuditLogActorType + description: Type of actor + action: + type: string + description: Action performed (e.g. "core:vendor:create") + resource_type: + type: string + description: Type of resource affected (e.g. "Vendor") + resource_id: + $ref: "#/components/schemas/GID" + description: ID of the affected resource + metadata: + type: object + description: Additional metadata about the action + created_at: + type: string + format: date-time + go.probo.inc/mcpgen/type: time.Time + description: When the action was performed + + ListCookieBannersInput: + type: object + required: + - organization_id + properties: + organization_id: + $ref: "#/components/schemas/GID" + description: Organization ID + order_by: + $ref: "#/components/schemas/CookieBannerOrderBy" + description: Cookie banner order by + size: + type: integer + description: Page size + cursor: + $ref: "#/components/schemas/CursorKey" + description: Pagination cursor + + ListCookieBannersOutput: + type: object + required: + - cookie_banners + properties: + next_cursor: + $ref: "#/components/schemas/CursorKey" + description: Next cursor + cookie_banners: + type: array + items: + $ref: "#/components/schemas/CookieBanner" + + GetCookieBannerInput: + type: object + required: + - id + properties: + id: + $ref: "#/components/schemas/GID" + description: Cookie banner ID + + GetCookieBannerOutput: + type: object + required: + - cookie_banner + properties: + cookie_banner: + $ref: "#/components/schemas/CookieBanner" + + CreateCookieBannerInput: + type: object + required: + - organization_id + - name + - domain + properties: + organization_id: + $ref: "#/components/schemas/GID" + description: Organization ID + name: + type: string + description: Cookie banner name + domain: + type: string + description: Domain the banner is deployed on + + CreateCookieBannerOutput: + type: object + required: + - cookie_banner + properties: + cookie_banner: + $ref: "#/components/schemas/CookieBanner" + + UpdateCookieBannerInput: + type: object + required: + - id + properties: + id: + $ref: "#/components/schemas/GID" + description: Cookie banner ID + name: + type: string + description: Cookie banner name + domain: + type: string + description: Domain + title: + type: string + description: Banner title + description: + type: string + description: Banner description + accept_all_label: + type: string + description: Accept all button label + reject_all_label: + type: string + description: Reject all button label + save_preferences_label: + type: string + description: Save preferences button label + privacy_policy_url: + type: string + description: Privacy policy URL + consent_expiry_days: + type: integer + description: Days until consent expires + consent_mode: + $ref: "#/components/schemas/ConsentMode" + description: Consent mode (OPT_IN or OPT_OUT) + theme: + $ref: "#/components/schemas/CookieBannerTheme" + description: Visual theme configuration (partial update) + + UpdateCookieBannerOutput: + type: object + required: + - cookie_banner + properties: + cookie_banner: + $ref: "#/components/schemas/CookieBanner" + + DeleteCookieBannerInput: + type: object + required: + - id + properties: + id: + $ref: "#/components/schemas/GID" + description: Cookie banner ID + + DeleteCookieBannerOutput: + type: object + required: + - deleted_cookie_banner_id + properties: + deleted_cookie_banner_id: + $ref: "#/components/schemas/GID" + description: Deleted cookie banner ID + + PublishCookieBannerInput: + type: object + required: + - id + properties: + id: + $ref: "#/components/schemas/GID" + description: Cookie banner ID + + PublishCookieBannerOutput: + type: object + required: + - cookie_banner + properties: + cookie_banner: + $ref: "#/components/schemas/CookieBanner" + + DisableCookieBannerInput: + type: object + required: + - id + properties: + id: + $ref: "#/components/schemas/GID" + description: Cookie banner ID + + DisableCookieBannerOutput: + type: object + required: + - cookie_banner + properties: + cookie_banner: + $ref: "#/components/schemas/CookieBanner" + + ListCookieCategoriesInput: + type: object + required: + - cookie_banner_id + properties: + cookie_banner_id: + $ref: "#/components/schemas/GID" + description: Cookie banner ID + order_by: + $ref: "#/components/schemas/CookieCategoryOrderBy" + description: Cookie category order by size: type: integer description: Page size cursor: $ref: "#/components/schemas/CursorKey" - description: Page cursor - filter: - type: object - properties: - action: - type: string - description: Filter by action (e.g. "core:vendor:create") - actor_id: - $ref: "#/components/schemas/GID" - description: Filter by actor ID - resource_type: - type: string - description: Filter by resource type (e.g. "Vendor") - resource_id: - $ref: "#/components/schemas/GID" - description: Filter by resource ID + description: Pagination cursor - ListAuditLogEntriesOutput: + ListCookieCategoriesOutput: type: object required: - - audit_log_entries + - cookie_categories properties: next_cursor: $ref: "#/components/schemas/CursorKey" description: Next cursor - audit_log_entries: + cookie_categories: type: array items: - $ref: "#/components/schemas/AuditLogEntry" + $ref: "#/components/schemas/CookieCategory" - AuditLogEntry: + GetCookieCategoryInput: type: object required: - id - - organization_id - - actor_id - - actor_type - - action - - resource_type - - resource_id - - created_at properties: id: $ref: "#/components/schemas/GID" - description: Audit log entry ID - organization_id: + description: Cookie category ID + + GetCookieCategoryOutput: + type: object + required: + - cookie_category + properties: + cookie_category: + $ref: "#/components/schemas/CookieCategory" + + CreateCookieCategoryInput: + type: object + required: + - cookie_banner_id + - name + - description + - rank + properties: + cookie_banner_id: $ref: "#/components/schemas/GID" - description: Organization ID - actor_id: + description: Cookie banner ID + name: + type: string + description: Category name + description: + type: string + description: Category description + rank: + type: integer + description: Display order rank + cookies: + type: array + items: + $ref: "#/components/schemas/CookieCategoryItem" + description: Individual cookies managed by this category + + CreateCookieCategoryOutput: + type: object + required: + - cookie_category + properties: + cookie_category: + $ref: "#/components/schemas/CookieCategory" + + UpdateCookieCategoryInput: + type: object + required: + - id + properties: + id: $ref: "#/components/schemas/GID" - description: ID of the actor who performed the action - actor_type: + description: Cookie category ID + name: type: string - enum: [USER, API_KEY, SYSTEM] - go.probo.inc/mcpgen/type: go.probo.inc/probo/pkg/coredata.AuditLogActorType - description: Type of actor - action: + description: Category name + description: type: string - description: Action performed (e.g. "core:vendor:create") - resource_type: + description: Category description + rank: + type: integer + description: Display order rank + cookies: + type: array + items: + $ref: "#/components/schemas/CookieCategoryItem" + description: Individual cookies managed by this category + + UpdateCookieCategoryOutput: + type: object + required: + - cookie_category + properties: + cookie_category: + $ref: "#/components/schemas/CookieCategory" + + DeleteCookieCategoryInput: + type: object + required: + - id + properties: + id: + $ref: "#/components/schemas/GID" + description: Cookie category ID + + DeleteCookieCategoryOutput: + type: object + required: + - deleted_cookie_category_id + properties: + deleted_cookie_category_id: + $ref: "#/components/schemas/GID" + description: Deleted cookie category ID + + CookieProviderCookie: + type: object + go.probo.inc/mcpgen/type: go.probo.inc/probo/pkg/cookieprovider.Cookie + required: + - name + - duration + - description + properties: + name: type: string - description: Type of resource affected (e.g. "Vendor") - resource_id: + description: Cookie name or pattern + duration: + type: string + description: Cookie duration + description: + type: string + description: Cookie description + + CookieProvider: + type: object + go.probo.inc/mcpgen/type: go.probo.inc/probo/pkg/cookieprovider.Provider + required: + - key + - name + - description + - category + - website_url + - cookies + properties: + key: + type: string + description: Unique provider key + name: + type: string + description: Provider name + description: + type: string + description: Provider description + category: + type: string + description: Default cookie category (Necessary, Analytics, Marketing, Preferences) + website_url: + type: string + description: Provider website URL + cookies: + type: array + items: + $ref: "#/components/schemas/CookieProviderCookie" + description: Cookies set by this provider + + ListCookieProvidersInput: + type: object + properties: + category: + type: string + description: Filter by cookie category (Necessary, Analytics, Marketing, Preferences) + + ListCookieProvidersOutput: + type: object + required: + - cookie_providers + properties: + cookie_providers: + type: array + items: + $ref: "#/components/schemas/CookieProvider" + + GetCookieProviderInput: + type: object + required: + - key + properties: + key: + type: string + description: Provider key + + GetCookieProviderOutput: + type: object + required: + - cookie_provider + properties: + cookie_provider: + $ref: "#/components/schemas/CookieProvider" + + AddCookiesFromProviderInput: + type: object + required: + - cookie_category_id + - provider_key + properties: + cookie_category_id: $ref: "#/components/schemas/GID" - description: ID of the affected resource - metadata: - type: object - description: Additional metadata about the action - created_at: + description: Cookie category ID to add cookies to + provider_key: type: string - format: date-time - go.probo.inc/mcpgen/type: time.Time - description: When the action was performed + description: Provider key from the cookie provider library + + AddCookiesFromProviderOutput: + type: object + required: + - cookie_category + properties: + cookie_category: + $ref: "#/components/schemas/CookieCategory" + + ListConsentRecordsInput: + type: object + required: + - cookie_banner_id + properties: + cookie_banner_id: + $ref: "#/components/schemas/GID" + description: Cookie banner ID + order_by: + $ref: "#/components/schemas/ConsentRecordOrderBy" + description: Consent record order by + size: + type: integer + description: Page size + cursor: + $ref: "#/components/schemas/CursorKey" + description: Pagination cursor + filter: + type: object + properties: + action: + $ref: "#/components/schemas/ConsentAction" + description: Filter by consent action + + ListConsentRecordsOutput: + type: object + required: + - consent_records + properties: + next_cursor: + $ref: "#/components/schemas/CursorKey" + description: Next cursor + consent_records: + type: array + items: + $ref: "#/components/schemas/ConsentRecord" tools: - name: listOrganizations @@ -8806,3 +9556,140 @@ tools: $ref: "#/components/schemas/ListAuditLogEntriesInput" outputSchema: $ref: "#/components/schemas/ListAuditLogEntriesOutput" + - name: listCookieBanners + description: List all cookie banners for the organization + hints: + readonly: true + idempotent: true + inputSchema: + $ref: "#/components/schemas/ListCookieBannersInput" + outputSchema: + $ref: "#/components/schemas/ListCookieBannersOutput" + - name: getCookieBanner + description: Get a cookie banner by ID + hints: + readonly: true + idempotent: true + inputSchema: + $ref: "#/components/schemas/GetCookieBannerInput" + outputSchema: + $ref: "#/components/schemas/GetCookieBannerOutput" + - name: createCookieBanner + description: Create a new cookie banner + hints: + readonly: false + inputSchema: + $ref: "#/components/schemas/CreateCookieBannerInput" + outputSchema: + $ref: "#/components/schemas/CreateCookieBannerOutput" + - name: updateCookieBanner + description: Update a cookie banner + hints: + readonly: false + inputSchema: + $ref: "#/components/schemas/UpdateCookieBannerInput" + outputSchema: + $ref: "#/components/schemas/UpdateCookieBannerOutput" + - name: deleteCookieBanner + description: Delete a cookie banner + hints: + readonly: false + destructive: true + inputSchema: + $ref: "#/components/schemas/DeleteCookieBannerInput" + outputSchema: + $ref: "#/components/schemas/DeleteCookieBannerOutput" + - name: publishCookieBanner + description: Publish a cookie banner to make it active + hints: + readonly: false + inputSchema: + $ref: "#/components/schemas/PublishCookieBannerInput" + outputSchema: + $ref: "#/components/schemas/PublishCookieBannerOutput" + - name: disableCookieBanner + description: Disable a published cookie banner + hints: + readonly: false + inputSchema: + $ref: "#/components/schemas/DisableCookieBannerInput" + outputSchema: + $ref: "#/components/schemas/DisableCookieBannerOutput" + - name: listCookieCategories + description: List all cookie categories for a cookie banner + hints: + readonly: true + idempotent: true + inputSchema: + $ref: "#/components/schemas/ListCookieCategoriesInput" + outputSchema: + $ref: "#/components/schemas/ListCookieCategoriesOutput" + - name: getCookieCategory + description: Get a cookie category by ID + hints: + readonly: true + idempotent: true + inputSchema: + $ref: "#/components/schemas/GetCookieCategoryInput" + outputSchema: + $ref: "#/components/schemas/GetCookieCategoryOutput" + - name: createCookieCategory + description: Create a new cookie category for a banner + hints: + readonly: false + inputSchema: + $ref: "#/components/schemas/CreateCookieCategoryInput" + outputSchema: + $ref: "#/components/schemas/CreateCookieCategoryOutput" + - name: updateCookieCategory + description: Update a cookie category + hints: + readonly: false + inputSchema: + $ref: "#/components/schemas/UpdateCookieCategoryInput" + outputSchema: + $ref: "#/components/schemas/UpdateCookieCategoryOutput" + - name: deleteCookieCategory + description: Delete a cookie category + hints: + readonly: false + destructive: true + inputSchema: + $ref: "#/components/schemas/DeleteCookieCategoryInput" + outputSchema: + $ref: "#/components/schemas/DeleteCookieCategoryOutput" + - name: listCookieProviders + description: List known cookie providers from the built-in library, optionally filtered by category + hints: + readonly: true + idempotent: true + inputSchema: + $ref: "#/components/schemas/ListCookieProvidersInput" + outputSchema: + $ref: "#/components/schemas/ListCookieProvidersOutput" + - name: getCookieProvider + description: Get details of a known cookie provider by key + hints: + readonly: true + idempotent: true + inputSchema: + $ref: "#/components/schemas/GetCookieProviderInput" + outputSchema: + $ref: "#/components/schemas/GetCookieProviderOutput" + - name: addCookiesFromProvider + description: Add cookies from a known provider to an existing cookie category + hints: + readonly: false + inputSchema: + $ref: "#/components/schemas/AddCookiesFromProviderInput" + outputSchema: + $ref: "#/components/schemas/AddCookiesFromProviderOutput" + - name: listConsentRecords + description: List consent records for a cookie banner + hints: + readonly: true + idempotent: true + inputSchema: + $ref: "#/components/schemas/ListConsentRecordsInput" + outputSchema: + $ref: "#/components/schemas/ListConsentRecordsOutput" diff --git a/pkg/server/api/mcp/v1/types/cookie_banner.go b/pkg/server/api/mcp/v1/types/cookie_banner.go new file mode 100644 index 000000000..9aef1b94b --- /dev/null +++ b/pkg/server/api/mcp/v1/types/cookie_banner.go @@ -0,0 +1,199 @@ +// Copyright (c) 2025-2026 Probo Inc . +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR +// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +// PERFORMANCE OF THIS SOFTWARE. + +package types + +import ( + "encoding/json" + + "go.probo.inc/probo/pkg/coredata" + "go.probo.inc/probo/pkg/page" +) + +func newCookieBannerTheme(raw json.RawMessage) *CookieBannerTheme { + if len(raw) == 0 { + return nil + } + + var theme CookieBannerTheme + if err := json.Unmarshal(raw, &theme); err != nil { + return nil + } + + return &theme +} + +func MergeThemeInput(existing json.RawMessage, input *CookieBannerTheme) json.RawMessage { + if input == nil { + return existing + } + + current := make(map[string]any) + if len(existing) > 0 { + _ = json.Unmarshal(existing, ¤t) + } + + if input.PrimaryColor != nil { + current["primaryColor"] = *input.PrimaryColor + } + if input.PrimaryTextColor != nil { + current["primaryTextColor"] = *input.PrimaryTextColor + } + if input.SecondaryColor != nil { + current["secondaryColor"] = *input.SecondaryColor + } + if input.SecondaryTextColor != nil { + current["secondaryTextColor"] = *input.SecondaryTextColor + } + if input.BackgroundColor != nil { + current["backgroundColor"] = *input.BackgroundColor + } + if input.TextColor != nil { + current["textColor"] = *input.TextColor + } + if input.SecondaryTextBodyColor != nil { + current["secondaryTextBodyColor"] = *input.SecondaryTextBodyColor + } + if input.BorderColor != nil { + current["borderColor"] = *input.BorderColor + } + if input.FontFamily != nil { + current["fontFamily"] = *input.FontFamily + } + if input.BorderRadius != nil { + current["borderRadius"] = *input.BorderRadius + } + if input.Position != nil { + current["position"] = *input.Position + } + if input.RevisitPosition != nil { + current["revisitPosition"] = *input.RevisitPosition + } + + data, err := json.Marshal(current) + if err != nil { + return existing + } + + return data +} + +func NewCookieBanner(b *coredata.CookieBanner) *CookieBanner { + return &CookieBanner{ + ID: b.ID, + OrganizationID: b.OrganizationID, + Name: b.Name, + Domain: b.Domain, + State: b.State, + Title: b.Title, + Description: b.Description, + AcceptAllLabel: b.AcceptAllLabel, + RejectAllLabel: b.RejectAllLabel, + SavePreferencesLabel: b.SavePreferencesLabel, + PrivacyPolicyURL: b.PrivacyPolicyURL, + ConsentExpiryDays: b.ConsentExpiryDays, + ConsentMode: b.ConsentMode, + Version: b.Version, + Theme: newCookieBannerTheme(b.Theme), + CreatedAt: b.CreatedAt, + UpdatedAt: b.UpdatedAt, + } +} + +func NewListCookieBannersOutput(p *page.Page[*coredata.CookieBanner, coredata.CookieBannerOrderField]) ListCookieBannersOutput { + banners := make([]*CookieBanner, 0, len(p.Data)) + for _, b := range p.Data { + banners = append(banners, NewCookieBanner(b)) + } + + var nextCursor *page.CursorKey + if len(p.Data) > 0 { + cursorKey := p.Data[len(p.Data)-1].CursorKey(p.Cursor.OrderBy.Field) + nextCursor = &cursorKey + } + + return ListCookieBannersOutput{ + NextCursor: nextCursor, + CookieBanners: banners, + } +} + +func NewCookieCategory(c *coredata.CookieCategory) *CookieCategory { + cookies := c.Cookies + if cookies == nil { + cookies = make(coredata.CookieItems, 0) + } + + return &CookieCategory{ + ID: c.ID, + CookieBannerID: c.CookieBannerID, + Name: c.Name, + Description: c.Description, + Required: c.Required, + Rank: c.Rank, + Cookies: cookies, + CreatedAt: c.CreatedAt, + UpdatedAt: c.UpdatedAt, + } +} + +func NewListCookieCategoriesOutput(p *page.Page[*coredata.CookieCategory, coredata.CookieCategoryOrderField]) ListCookieCategoriesOutput { + categories := make([]*CookieCategory, 0, len(p.Data)) + for _, c := range p.Data { + categories = append(categories, NewCookieCategory(c)) + } + + var nextCursor *page.CursorKey + if len(p.Data) > 0 { + cursorKey := p.Data[len(p.Data)-1].CursorKey(p.Cursor.OrderBy.Field) + nextCursor = &cursorKey + } + + return ListCookieCategoriesOutput{ + NextCursor: nextCursor, + CookieCategories: categories, + } +} + +func NewConsentRecord(r *coredata.ConsentRecord) *ConsentRecord { + return &ConsentRecord{ + ID: r.ID, + CookieBannerID: r.CookieBannerID, + VisitorID: r.VisitorID, + IPAddress: r.IPAddress, + UserAgent: r.UserAgent, + ConsentData: string(r.ConsentData), + Action: r.Action, + BannerVersion: r.BannerVersion, + CreatedAt: r.CreatedAt, + } +} + +func NewListConsentRecordsOutput(p *page.Page[*coredata.ConsentRecord, coredata.ConsentRecordOrderField]) ListConsentRecordsOutput { + records := make([]*ConsentRecord, 0, len(p.Data)) + for _, r := range p.Data { + records = append(records, NewConsentRecord(r)) + } + + var nextCursor *page.CursorKey + if len(p.Data) > 0 { + cursorKey := p.Data[len(p.Data)-1].CursorKey(p.Cursor.OrderBy.Field) + nextCursor = &cursorKey + } + + return ListConsentRecordsOutput{ + NextCursor: nextCursor, + ConsentRecords: records, + } +} diff --git a/pkg/server/api/mcp/v1/v1_handler.go b/pkg/server/api/mcp/v1/v1_handler.go index 2d2678d08..5c14c1015 100644 --- a/pkg/server/api/mcp/v1/v1_handler.go +++ b/pkg/server/api/mcp/v1/v1_handler.go @@ -23,6 +23,7 @@ import ( "go.gearno.de/kit/log" mcpgenmcp "go.probo.inc/mcpgen/mcp" "go.probo.inc/probo/pkg/accessreview" + "go.probo.inc/probo/pkg/consent" "go.probo.inc/probo/pkg/gid" "go.probo.inc/probo/pkg/iam" "go.probo.inc/probo/pkg/probo" @@ -35,7 +36,7 @@ func (r *Resolver) ProboService(ctx context.Context, objectID gid.GID) *probo.Te return r.proboSvc.WithTenant(objectID.TenantID()) } -func NewMux(logger *log.Logger, proboSvc *probo.Service, iamSvc *iam.Service, accessReviewSvc *accessreview.Service, tokenSecret string) *chi.Mux { +func NewMux(logger *log.Logger, proboSvc *probo.Service, consentSvc *consent.Service, iamSvc *iam.Service, accessReviewSvc *accessreview.Service, tokenSecret string) *chi.Mux { logger = logger.Named("mcp.v1") logger.Info("initializing MCP server") @@ -43,6 +44,7 @@ func NewMux(logger *log.Logger, proboSvc *probo.Service, iamSvc *iam.Service, ac resolver := &Resolver{ proboSvc: proboSvc, + consentSvc: consentSvc, iamSvc: iamSvc, accessReview: accessReviewSvc, logger: logger, diff --git a/pkg/server/server.go b/pkg/server/server.go index 23cd8e0eb..e2c6060da 100644 --- a/pkg/server/server.go +++ b/pkg/server/server.go @@ -27,6 +27,7 @@ import ( "go.probo.inc/probo/pkg/accessreview" "go.probo.inc/probo/pkg/baseurl" "go.probo.inc/probo/pkg/connector" + "go.probo.inc/probo/pkg/consent" "go.probo.inc/probo/pkg/esign" "go.probo.inc/probo/pkg/file" "go.probo.inc/probo/pkg/iam" @@ -47,6 +48,7 @@ type Config struct { AllowedOrigins []string ExtraHeaderFields map[string]string Probo *probo.Service + Consent *consent.Service File *file.Service IAM *iam.Service Trust *trust.Service @@ -78,6 +80,7 @@ func NewServer(cfg Config) (*Server, error) { BaseURL: cfg.BaseURL, AllowedOrigins: cfg.AllowedOrigins, Probo: cfg.Probo, + Consent: cfg.Consent, File: cfg.File, IAM: cfg.IAM, Trust: cfg.Trust,