(checked);
+
+ useEffect(() => {
+ setEnabled(checked);
+ }, [checked]);
+
+ return (
+
+
+
+ {
+ setEnabled(event.target?.checked);
+ onChangeVal && onChangeVal(event.target?.checked);
+ }}
+ disabled={disabled}
+ type="checkbox"
+ className={classNames(
+ "form-checkbox rounded border-gray-300 text-primary-light-500 dark:text-primary-dark-500 shadow-sm focus:border-indigo-300 focus:ring focus:ring-offset-0 focus:ring-indigo-200 focus:ring-opacity-50",
+ className,
+ )}
+ {...props}
+ />
+
+
+ {children}
+
+ );
+ },
+);
diff --git a/login/apps/login/src/components/choose-authenticator-to-login.tsx b/login/apps/login/src/components/choose-authenticator-to-login.tsx
new file mode 100644
index 0000000000..0f5dd79134
--- /dev/null
+++ b/login/apps/login/src/components/choose-authenticator-to-login.tsx
@@ -0,0 +1,38 @@
+import {
+ LoginSettings,
+ PasskeysType,
+} from "@zitadel/proto/zitadel/settings/v2/login_settings_pb";
+import { AuthenticationMethodType } from "@zitadel/proto/zitadel/user/v2/user_service_pb";
+import { PASSKEYS, PASSWORD } from "./auth-methods";
+import { Translated } from "./translated";
+
+type Props = {
+ authMethods: AuthenticationMethodType[];
+ params: URLSearchParams;
+ loginSettings: LoginSettings | undefined;
+};
+
+export function ChooseAuthenticatorToLogin({
+ authMethods,
+ params,
+ loginSettings,
+}: Props) {
+ return (
+ <>
+ {authMethods.includes(AuthenticationMethodType.PASSWORD) &&
+ loginSettings?.allowUsernamePassword && (
+
+
+
+ )}
+
+ {authMethods.includes(AuthenticationMethodType.PASSWORD) &&
+ loginSettings?.allowUsernamePassword &&
+ PASSWORD(false, "/password?" + params)}
+ {authMethods.includes(AuthenticationMethodType.PASSKEY) &&
+ loginSettings?.passkeysType == PasskeysType.ALLOWED &&
+ PASSKEYS(false, "/passkey?" + params)}
+
+ >
+ );
+}
diff --git a/login/apps/login/src/components/choose-authenticator-to-setup.tsx b/login/apps/login/src/components/choose-authenticator-to-setup.tsx
new file mode 100644
index 0000000000..4aa4de720a
--- /dev/null
+++ b/login/apps/login/src/components/choose-authenticator-to-setup.tsx
@@ -0,0 +1,51 @@
+import {
+ LoginSettings,
+ PasskeysType,
+} from "@zitadel/proto/zitadel/settings/v2/login_settings_pb";
+import { AuthenticationMethodType } from "@zitadel/proto/zitadel/user/v2/user_service_pb";
+import { Alert, AlertType } from "./alert";
+import { PASSKEYS, PASSWORD } from "./auth-methods";
+import { Translated } from "./translated";
+
+type Props = {
+ authMethods: AuthenticationMethodType[];
+ params: URLSearchParams;
+ loginSettings: LoginSettings;
+};
+
+export function ChooseAuthenticatorToSetup({
+ authMethods,
+ params,
+ loginSettings,
+}: Props) {
+ if (authMethods.length !== 0) {
+ return (
+
+
+
+ );
+ } else {
+ return (
+ <>
+ {loginSettings.passkeysType == PasskeysType.NOT_ALLOWED &&
+ !loginSettings.allowUsernamePassword && (
+
+
+
+ )}
+
+
+ {!authMethods.includes(AuthenticationMethodType.PASSWORD) &&
+ loginSettings.allowUsernamePassword &&
+ PASSWORD(false, "/password/set?" + params)}
+ {!authMethods.includes(AuthenticationMethodType.PASSKEY) &&
+ loginSettings.passkeysType == PasskeysType.ALLOWED &&
+ PASSKEYS(false, "/passkey/set?" + params)}
+
+ >
+ );
+ }
+}
diff --git a/login/apps/login/src/components/choose-second-factor-to-setup.tsx b/login/apps/login/src/components/choose-second-factor-to-setup.tsx
new file mode 100644
index 0000000000..edd0ae2b61
--- /dev/null
+++ b/login/apps/login/src/components/choose-second-factor-to-setup.tsx
@@ -0,0 +1,119 @@
+"use client";
+
+import { skipMFAAndContinueWithNextUrl } from "@/lib/server/session";
+import {
+ LoginSettings,
+ SecondFactorType,
+} from "@zitadel/proto/zitadel/settings/v2/login_settings_pb";
+import { AuthenticationMethodType } from "@zitadel/proto/zitadel/user/v2/user_service_pb";
+import { useRouter } from "next/navigation";
+import { EMAIL, SMS, TOTP, U2F } from "./auth-methods";
+import { Translated } from "./translated";
+
+type Props = {
+ userId: string;
+ loginName?: string;
+ sessionId?: string;
+ requestId?: string;
+ organization?: string;
+ loginSettings: LoginSettings;
+ userMethods: AuthenticationMethodType[];
+ checkAfter: boolean;
+ phoneVerified: boolean;
+ emailVerified: boolean;
+ force: boolean;
+};
+
+export function ChooseSecondFactorToSetup({
+ userId,
+ loginName,
+ sessionId,
+ requestId,
+ organization,
+ loginSettings,
+ userMethods,
+ checkAfter,
+ phoneVerified,
+ emailVerified,
+ force,
+}: Props) {
+ const router = useRouter();
+ const params = new URLSearchParams({});
+
+ if (loginName) {
+ params.append("loginName", loginName);
+ }
+ if (sessionId) {
+ params.append("sessionId", sessionId);
+ }
+ if (requestId) {
+ params.append("requestId", requestId);
+ }
+ if (organization) {
+ params.append("organization", organization);
+ }
+ if (checkAfter) {
+ params.append("checkAfter", "true");
+ }
+
+ return (
+ <>
+
+ {loginSettings.secondFactors.map((factor) => {
+ switch (factor) {
+ case SecondFactorType.OTP:
+ return TOTP(
+ userMethods.includes(AuthenticationMethodType.TOTP),
+ "/otp/time-based/set?" + params,
+ );
+ case SecondFactorType.U2F:
+ return U2F(
+ userMethods.includes(AuthenticationMethodType.U2F),
+ "/u2f/set?" + params,
+ );
+ case SecondFactorType.OTP_EMAIL:
+ return (
+ emailVerified &&
+ EMAIL(
+ userMethods.includes(AuthenticationMethodType.OTP_EMAIL),
+ "/otp/email/set?" + params,
+ )
+ );
+ case SecondFactorType.OTP_SMS:
+ return (
+ phoneVerified &&
+ SMS(
+ userMethods.includes(AuthenticationMethodType.OTP_SMS),
+ "/otp/sms/set?" + params,
+ )
+ );
+ default:
+ return null;
+ }
+ })}
+
+ {!force && (
+ {
+ const resp = await skipMFAAndContinueWithNextUrl({
+ userId,
+ loginName,
+ sessionId,
+ organization,
+ requestId,
+ });
+
+ if (resp?.redirect) {
+ return router.push(resp.redirect);
+ }
+ }}
+ type="button"
+ data-testid="reset-button"
+ >
+
+
+ )}
+ >
+ );
+}
diff --git a/login/apps/login/src/components/choose-second-factor.tsx b/login/apps/login/src/components/choose-second-factor.tsx
new file mode 100644
index 0000000000..6cd890f11d
--- /dev/null
+++ b/login/apps/login/src/components/choose-second-factor.tsx
@@ -0,0 +1,54 @@
+"use client";
+
+import { AuthenticationMethodType } from "@zitadel/proto/zitadel/user/v2/user_service_pb";
+import { EMAIL, SMS, TOTP, U2F } from "./auth-methods";
+
+type Props = {
+ loginName?: string;
+ sessionId?: string;
+ requestId?: string;
+ organization?: string;
+ userMethods: AuthenticationMethodType[];
+};
+
+export function ChooseSecondFactor({
+ loginName,
+ sessionId,
+ requestId,
+ organization,
+ userMethods,
+}: Props) {
+ const params = new URLSearchParams({});
+
+ if (loginName) {
+ params.append("loginName", loginName);
+ }
+ if (sessionId) {
+ params.append("sessionId", sessionId);
+ }
+ if (requestId) {
+ params.append("requestId", requestId);
+ }
+ if (organization) {
+ params.append("organization", organization);
+ }
+
+ return (
+
+ {userMethods.map((method, i) => {
+ return (
+
+ {method === AuthenticationMethodType.TOTP &&
+ TOTP(false, "/otp/time-based?" + params)}
+ {method === AuthenticationMethodType.U2F &&
+ U2F(false, "/u2f?" + params)}
+ {method === AuthenticationMethodType.OTP_EMAIL &&
+ EMAIL(false, "/otp/email?" + params)}
+ {method === AuthenticationMethodType.OTP_SMS &&
+ SMS(false, "/otp/sms?" + params)}
+
+ );
+ })}
+
+ );
+}
diff --git a/login/apps/login/src/components/consent.tsx b/login/apps/login/src/components/consent.tsx
new file mode 100644
index 0000000000..e60ed2901b
--- /dev/null
+++ b/login/apps/login/src/components/consent.tsx
@@ -0,0 +1,116 @@
+"use client";
+
+import { completeDeviceAuthorization } from "@/lib/server/device";
+import { useTranslations } from "next-intl";
+import Link from "next/link";
+import { useRouter } from "next/navigation";
+import { useState } from "react";
+import { Alert } from "./alert";
+import { Button, ButtonVariants } from "./button";
+import { Spinner } from "./spinner";
+import { Translated } from "./translated";
+
+export function ConsentScreen({
+ scope,
+ nextUrl,
+ deviceAuthorizationRequestId,
+ appName,
+}: {
+ scope?: string[];
+ nextUrl: string;
+ deviceAuthorizationRequestId: string;
+ appName?: string;
+}) {
+ const t = useTranslations();
+ const [loading, setLoading] = useState(false);
+ const [error, setError] = useState("");
+ const router = useRouter();
+
+ async function denyDeviceAuth() {
+ setLoading(true);
+ const response = await completeDeviceAuthorization(
+ deviceAuthorizationRequestId,
+ )
+ .catch(() => {
+ setError("Could not register user");
+ return;
+ })
+ .finally(() => {
+ setLoading(false);
+ });
+
+ if (response) {
+ return router.push("/device");
+ }
+ }
+
+ const scopes = scope?.filter((s) => !!s);
+
+ return (
+
+
+ {scopes?.length === 0 && (
+
+
+
+ )}
+ {scopes?.map((s) => {
+ const translationKey = `device.scope.${s}`;
+ const description = t(translationKey, null);
+
+ // Check if the key itself is returned and provide a fallback
+ const resolvedDescription =
+ description === translationKey ? "" : description;
+
+ return (
+
+ {resolvedDescription}
+
+ );
+ })}
+
+
+
+
+
+
+ {error && (
+
+ )}
+
+
+ {
+ denyDeviceAuth();
+ }}
+ variant={ButtonVariants.Secondary}
+ data-testid="deny-button"
+ >
+ {loading && }
+
+
+
+
+
+
+
+
+
+
+
+ );
+}
diff --git a/login/apps/login/src/components/copy-to-clipboard.tsx b/login/apps/login/src/components/copy-to-clipboard.tsx
new file mode 100644
index 0000000000..cf0dedc060
--- /dev/null
+++ b/login/apps/login/src/components/copy-to-clipboard.tsx
@@ -0,0 +1,41 @@
+"use client";
+
+import {
+ ClipboardDocumentCheckIcon,
+ ClipboardIcon,
+} from "@heroicons/react/20/solid";
+import copy from "copy-to-clipboard";
+import { useEffect, useState } from "react";
+
+type Props = {
+ value: string;
+};
+
+export function CopyToClipboard({ value }: Props) {
+ const [copied, setCopied] = useState(false);
+
+ useEffect(() => {
+ if (copied) {
+ copy(value);
+ const to = setTimeout(setCopied, 1000, false);
+ return () => clearTimeout(to);
+ }
+ }, [copied]);
+
+ return (
+
+ setCopied(true)}
+ >
+ {!copied ? (
+
+ ) : (
+
+ )}
+
+
+ );
+}
diff --git a/login/apps/login/src/components/default-tags.tsx b/login/apps/login/src/components/default-tags.tsx
new file mode 100644
index 0000000000..dc14f1bc1e
--- /dev/null
+++ b/login/apps/login/src/components/default-tags.tsx
@@ -0,0 +1,32 @@
+// Default tags we want shared across the app
+export function DefaultTags() {
+ return (
+ <>
+
+
+
+
+
+ {/* */}
+
+ >
+ );
+}
diff --git a/login/apps/login/src/components/device-code-form.tsx b/login/apps/login/src/components/device-code-form.tsx
new file mode 100644
index 0000000000..a1efc07207
--- /dev/null
+++ b/login/apps/login/src/components/device-code-form.tsx
@@ -0,0 +1,95 @@
+"use client";
+
+import { Alert } from "@/components/alert";
+import { getDeviceAuthorizationRequest } from "@/lib/server/oidc";
+import { useRouter } from "next/navigation";
+import { useState } from "react";
+import { useForm } from "react-hook-form";
+import { BackButton } from "./back-button";
+import { Button, ButtonVariants } from "./button";
+import { TextInput } from "./input";
+import { Spinner } from "./spinner";
+import { Translated } from "./translated";
+
+type Inputs = {
+ userCode: string;
+};
+
+export function DeviceCodeForm({ userCode }: { userCode?: string }) {
+ const router = useRouter();
+
+ const { register, handleSubmit, formState } = useForm({
+ mode: "onBlur",
+ defaultValues: {
+ userCode: userCode || "",
+ },
+ });
+
+ const [error, setError] = useState("");
+
+ const [loading, setLoading] = useState(false);
+
+ async function submitCodeAndContinue(value: Inputs): Promise {
+ setLoading(true);
+
+ const response = await getDeviceAuthorizationRequest(value.userCode)
+ .catch(() => {
+ setError("Could not continue the request");
+ return;
+ })
+ .finally(() => {
+ setLoading(false);
+ });
+
+ if (!response || !response.deviceAuthorizationRequest?.id) {
+ setError("Could not continue the request");
+ return;
+ }
+
+ return router.push(
+ `/device/consent?` +
+ new URLSearchParams({
+ requestId: `device_${response.deviceAuthorizationRequest.id}`,
+ user_code: value.userCode,
+ }).toString(),
+ );
+ }
+
+ return (
+ <>
+
+ >
+ );
+}
diff --git a/login/apps/login/src/components/dynamic-theme.tsx b/login/apps/login/src/components/dynamic-theme.tsx
new file mode 100644
index 0000000000..d50bc082ea
--- /dev/null
+++ b/login/apps/login/src/components/dynamic-theme.tsx
@@ -0,0 +1,43 @@
+"use client";
+
+import { Logo } from "@/components/logo";
+import { BrandingSettings } from "@zitadel/proto/zitadel/settings/v2/branding_settings_pb";
+import { ReactNode } from "react";
+import { AppAvatar } from "./app-avatar";
+import { ThemeWrapper } from "./theme-wrapper";
+
+export function DynamicTheme({
+ branding,
+ children,
+ appName,
+}: {
+ children: ReactNode;
+ branding?: BrandingSettings;
+ appName?: string;
+}) {
+ return (
+
+
+
+
+ {branding && (
+ <>
+
+
+ {appName &&
}
+ >
+ )}
+
+
+
{children}
+
+
+
+
+ );
+}
diff --git a/login/apps/login/src/components/external-link.tsx b/login/apps/login/src/components/external-link.tsx
new file mode 100644
index 0000000000..a52164d35d
--- /dev/null
+++ b/login/apps/login/src/components/external-link.tsx
@@ -0,0 +1,21 @@
+import { ArrowRightIcon } from "@heroicons/react/24/solid";
+import { ReactNode } from "react";
+
+export const ExternalLink = ({
+ children,
+ href,
+}: {
+ children: ReactNode;
+ href: string;
+}) => {
+ return (
+
+ {children}
+
+
+
+ );
+};
diff --git a/login/apps/login/src/components/idp-signin.tsx b/login/apps/login/src/components/idp-signin.tsx
new file mode 100644
index 0000000000..a7c938e90c
--- /dev/null
+++ b/login/apps/login/src/components/idp-signin.tsx
@@ -0,0 +1,67 @@
+"use client";
+
+import { createNewSessionFromIdpIntent } from "@/lib/server/idp";
+import { useRouter } from "next/navigation";
+import { useEffect, useState } from "react";
+import { Alert } from "./alert";
+import { Spinner } from "./spinner";
+
+type Props = {
+ userId: string;
+ // organization: string;
+ idpIntent: {
+ idpIntentId: string;
+ idpIntentToken: string;
+ };
+ requestId?: string;
+};
+
+export function IdpSignin({
+ userId,
+ idpIntent: { idpIntentId, idpIntentToken },
+ requestId,
+}: Props) {
+ const [loading, setLoading] = useState(true);
+ const [error, setError] = useState(null);
+
+ const router = useRouter();
+
+ useEffect(() => {
+ createNewSessionFromIdpIntent({
+ userId,
+ idpIntent: {
+ idpIntentId,
+ idpIntentToken,
+ },
+ requestId,
+ })
+ .then((response) => {
+ if (response && "error" in response && response?.error) {
+ setError(response?.error);
+ return;
+ }
+
+ if (response && "redirect" in response && response?.redirect) {
+ return router.push(response.redirect);
+ }
+ })
+ .catch(() => {
+ setError("An internal error occurred");
+ return;
+ })
+ .finally(() => {
+ setLoading(false);
+ });
+ }, []);
+
+ return (
+
+ {loading &&
}
+ {error && (
+
+ )}
+
+ );
+}
diff --git a/login/apps/login/src/components/idps/base-button.tsx b/login/apps/login/src/components/idps/base-button.tsx
new file mode 100644
index 0000000000..0185c57996
--- /dev/null
+++ b/login/apps/login/src/components/idps/base-button.tsx
@@ -0,0 +1,41 @@
+"use client";
+
+import { clsx } from "clsx";
+import { Loader2Icon } from "lucide-react";
+import { ButtonHTMLAttributes, DetailedHTMLProps, forwardRef } from "react";
+import { useFormStatus } from "react-dom";
+
+export type SignInWithIdentityProviderProps = DetailedHTMLProps<
+ ButtonHTMLAttributes,
+ HTMLButtonElement
+> & {
+ name?: string;
+ e2e?: string;
+};
+
+export const BaseButton = forwardRef<
+ HTMLButtonElement,
+ SignInWithIdentityProviderProps
+>(function BaseButton(props, ref) {
+ const formStatus = useFormStatus();
+
+ return (
+
+
+
+ {props.children}
+
+ {formStatus.pending &&
}
+
+
+ );
+});
diff --git a/login/apps/login/src/components/idps/pages/complete-idp.tsx b/login/apps/login/src/components/idps/pages/complete-idp.tsx
new file mode 100644
index 0000000000..2061a28e3e
--- /dev/null
+++ b/login/apps/login/src/components/idps/pages/complete-idp.tsx
@@ -0,0 +1,55 @@
+import { RegisterFormIDPIncomplete } from "@/components/register-form-idp-incomplete";
+import { BrandingSettings } from "@zitadel/proto/zitadel/settings/v2/branding_settings_pb";
+import { AddHumanUserRequest } from "@zitadel/proto/zitadel/user/v2/user_service_pb";
+import { DynamicTheme } from "../../dynamic-theme";
+import { Translated } from "../../translated";
+
+export async function completeIDP({
+ idpUserId,
+ idpId,
+ idpUserName,
+ addHumanUser,
+ requestId,
+ organization,
+ branding,
+ idpIntent,
+}: {
+ idpUserId: string;
+ idpId: string;
+ idpUserName: string;
+ addHumanUser?: AddHumanUserRequest;
+ requestId?: string;
+ organization: string;
+ branding?: BrandingSettings;
+ idpIntent: {
+ idpIntentId: string;
+ idpIntentToken: string;
+ };
+}) {
+ return (
+
+
+
+ );
+}
diff --git a/login/apps/login/src/components/idps/pages/linking-failed.tsx b/login/apps/login/src/components/idps/pages/linking-failed.tsx
new file mode 100644
index 0000000000..0c5a8264c4
--- /dev/null
+++ b/login/apps/login/src/components/idps/pages/linking-failed.tsx
@@ -0,0 +1,27 @@
+import { BrandingSettings } from "@zitadel/proto/zitadel/settings/v2/branding_settings_pb";
+import { Alert, AlertType } from "../../alert";
+import { DynamicTheme } from "../../dynamic-theme";
+import { Translated } from "../../translated";
+
+export async function linkingFailed(
+ branding?: BrandingSettings,
+ error?: string,
+) {
+ return (
+
+
+
+
+
+
+
+
+ {error && (
+
+ )}
+
+
+ );
+}
diff --git a/login/apps/login/src/components/idps/pages/linking-success.tsx b/login/apps/login/src/components/idps/pages/linking-success.tsx
new file mode 100644
index 0000000000..8d41cd8c32
--- /dev/null
+++ b/login/apps/login/src/components/idps/pages/linking-success.tsx
@@ -0,0 +1,30 @@
+import { BrandingSettings } from "@zitadel/proto/zitadel/settings/v2/branding_settings_pb";
+import { DynamicTheme } from "../../dynamic-theme";
+import { IdpSignin } from "../../idp-signin";
+import { Translated } from "../../translated";
+
+export async function linkingSuccess(
+ userId: string,
+ idpIntent: { idpIntentId: string; idpIntentToken: string },
+ requestId?: string,
+ branding?: BrandingSettings,
+) {
+ return (
+
+
+
+ );
+}
diff --git a/login/apps/login/src/components/idps/pages/login-failed.tsx b/login/apps/login/src/components/idps/pages/login-failed.tsx
new file mode 100644
index 0000000000..70c46919bf
--- /dev/null
+++ b/login/apps/login/src/components/idps/pages/login-failed.tsx
@@ -0,0 +1,24 @@
+import { BrandingSettings } from "@zitadel/proto/zitadel/settings/v2/branding_settings_pb";
+import { Alert, AlertType } from "../../alert";
+import { DynamicTheme } from "../../dynamic-theme";
+import { Translated } from "../../translated";
+
+export async function loginFailed(branding?: BrandingSettings, error?: string) {
+ return (
+
+
+
+
+
+
+
+
+ {error && (
+
+ )}
+
+
+ );
+}
diff --git a/login/apps/login/src/components/idps/pages/login-success.tsx b/login/apps/login/src/components/idps/pages/login-success.tsx
new file mode 100644
index 0000000000..6beec160a9
--- /dev/null
+++ b/login/apps/login/src/components/idps/pages/login-success.tsx
@@ -0,0 +1,30 @@
+import { BrandingSettings } from "@zitadel/proto/zitadel/settings/v2/branding_settings_pb";
+import { DynamicTheme } from "../../dynamic-theme";
+import { IdpSignin } from "../../idp-signin";
+import { Translated } from "../../translated";
+
+export async function loginSuccess(
+ userId: string,
+ idpIntent: { idpIntentId: string; idpIntentToken: string },
+ requestId?: string,
+ branding?: BrandingSettings,
+) {
+ return (
+
+
+
+ );
+}
diff --git a/login/apps/login/src/components/idps/sign-in-with-apple.tsx b/login/apps/login/src/components/idps/sign-in-with-apple.tsx
new file mode 100644
index 0000000000..17e3fc43bb
--- /dev/null
+++ b/login/apps/login/src/components/idps/sign-in-with-apple.tsx
@@ -0,0 +1,36 @@
+"use client";
+
+import { forwardRef } from "react";
+import { Translated } from "../translated";
+import { BaseButton, SignInWithIdentityProviderProps } from "./base-button";
+
+export const SignInWithApple = forwardRef<
+ HTMLButtonElement,
+ SignInWithIdentityProviderProps
+>(function SignInWithApple(props, ref) {
+ const { children, name, ...restProps } = props;
+
+ return (
+
+
+ {children ? (
+ children
+ ) : (
+
+ {name ? (
+ name
+ ) : (
+
+ )}
+
+ )}
+
+ );
+});
diff --git a/login/apps/login/src/components/idps/sign-in-with-azure-ad.tsx b/login/apps/login/src/components/idps/sign-in-with-azure-ad.tsx
new file mode 100644
index 0000000000..3cd33708b6
--- /dev/null
+++ b/login/apps/login/src/components/idps/sign-in-with-azure-ad.tsx
@@ -0,0 +1,42 @@
+"use client";
+
+import { forwardRef } from "react";
+import { Translated } from "../translated";
+import { BaseButton, SignInWithIdentityProviderProps } from "./base-button";
+
+export const SignInWithAzureAd = forwardRef<
+ HTMLButtonElement,
+ SignInWithIdentityProviderProps
+>(function SignInWithAzureAd(props, ref) {
+ const { children, name, ...restProps } = props;
+
+ return (
+
+
+ {children ? (
+ children
+ ) : (
+
+ {name ? (
+ name
+ ) : (
+
+ )}
+
+ )}
+
+ );
+});
diff --git a/login/apps/login/src/components/idps/sign-in-with-generic.tsx b/login/apps/login/src/components/idps/sign-in-with-generic.tsx
new file mode 100644
index 0000000000..ab8f2f99be
--- /dev/null
+++ b/login/apps/login/src/components/idps/sign-in-with-generic.tsx
@@ -0,0 +1,21 @@
+"use client";
+
+import { forwardRef } from "react";
+import { BaseButton, SignInWithIdentityProviderProps } from "./base-button";
+
+export const SignInWithGeneric = forwardRef<
+ HTMLButtonElement,
+ SignInWithIdentityProviderProps
+>(function SignInWithGeneric(props, ref) {
+ const {
+ children,
+ name = "",
+ className = "h-[50px] pl-20",
+ ...restProps
+ } = props;
+ return (
+
+ {children ? children : {name} }
+
+ );
+});
diff --git a/login/apps/login/src/components/idps/sign-in-with-github.tsx b/login/apps/login/src/components/idps/sign-in-with-github.tsx
new file mode 100644
index 0000000000..8800e66c3d
--- /dev/null
+++ b/login/apps/login/src/components/idps/sign-in-with-github.tsx
@@ -0,0 +1,64 @@
+"use client";
+
+import { forwardRef } from "react";
+import { Translated } from "../translated";
+import { BaseButton, SignInWithIdentityProviderProps } from "./base-button";
+
+function GitHubLogo() {
+ return (
+ <>
+
+
+
+
+
+
+ >
+ );
+}
+
+export const SignInWithGithub = forwardRef<
+ HTMLButtonElement,
+ SignInWithIdentityProviderProps
+>(function SignInWithGithub(props, ref) {
+ const { children, name, ...restProps } = props;
+
+ return (
+
+
+
+
+ {children ? (
+ children
+ ) : (
+
+ {name ? (
+ name
+ ) : (
+
+ )}
+
+ )}
+
+ );
+});
diff --git a/login/apps/login/src/components/idps/sign-in-with-gitlab.test.tsx b/login/apps/login/src/components/idps/sign-in-with-gitlab.test.tsx
new file mode 100644
index 0000000000..ab5bfda54d
--- /dev/null
+++ b/login/apps/login/src/components/idps/sign-in-with-gitlab.test.tsx
@@ -0,0 +1,45 @@
+import { afterEach, describe, expect, test } from "vitest";
+
+import { cleanup, render, screen } from "@testing-library/react";
+import { NextIntlClientProvider } from "next-intl";
+
+import { SignInWithGitlab } from "./sign-in-with-gitlab";
+
+afterEach(cleanup);
+
+describe(" ", async () => {
+ const messages = {
+ idp: {
+ signInWithGitlab: "Sign in with GitLab",
+ },
+ };
+
+ test("renders without crashing", () => {
+ const { container } = render(
+
+
+ ,
+ );
+ expect(container.firstChild).toBeDefined();
+ });
+
+ test("displays the default text", () => {
+ render(
+
+
+ ,
+ );
+ const signInText = screen.getByText(/Sign in with Gitlab/i);
+ expect(signInText).toBeInTheDocument();
+ });
+
+ test("displays the given text", () => {
+ render(
+
+
+ ,
+ );
+ const signInText = screen.getByText(/Gitlab/i);
+ expect(signInText).toBeInTheDocument();
+ });
+});
diff --git a/login/apps/login/src/components/idps/sign-in-with-gitlab.tsx b/login/apps/login/src/components/idps/sign-in-with-gitlab.tsx
new file mode 100644
index 0000000000..00f3712a90
--- /dev/null
+++ b/login/apps/login/src/components/idps/sign-in-with-gitlab.tsx
@@ -0,0 +1,53 @@
+"use client";
+
+import { forwardRef } from "react";
+import { Translated } from "../translated";
+import { BaseButton, SignInWithIdentityProviderProps } from "./base-button";
+
+export const SignInWithGitlab = forwardRef<
+ HTMLButtonElement,
+ SignInWithIdentityProviderProps
+>(function SignInWithGitlab(props, ref) {
+ const { children, name, ...restProps } = props;
+
+ return (
+
+
+ {children ? (
+ children
+ ) : (
+
+ {name ? (
+ name
+ ) : (
+
+ )}
+
+ )}
+
+ );
+});
diff --git a/login/apps/login/src/components/idps/sign-in-with-google.test.tsx b/login/apps/login/src/components/idps/sign-in-with-google.test.tsx
new file mode 100644
index 0000000000..953da21d94
--- /dev/null
+++ b/login/apps/login/src/components/idps/sign-in-with-google.test.tsx
@@ -0,0 +1,44 @@
+import { afterEach, describe, expect, test } from "vitest";
+
+import { cleanup, render, screen } from "@testing-library/react";
+import { NextIntlClientProvider } from "next-intl";
+import { SignInWithGoogle } from "./sign-in-with-google";
+
+afterEach(cleanup);
+
+describe(" ", async () => {
+ const messages = {
+ idp: {
+ signInWithGoogle: "Sign in with Google",
+ },
+ };
+
+ test("renders without crashing", () => {
+ const { container } = render(
+
+
+ ,
+ );
+ expect(container.firstChild).toBeDefined();
+ });
+
+ test("displays the default text", () => {
+ render(
+
+
+ ,
+ );
+ const signInText = screen.getByText(/Sign in with Google/i);
+ expect(signInText).toBeInTheDocument();
+ });
+
+ test("displays the given text", () => {
+ render(
+
+
+ ,
+ );
+ const signInText = screen.getByText(/Google/i);
+ expect(signInText).toBeInTheDocument();
+ });
+});
diff --git a/login/apps/login/src/components/idps/sign-in-with-google.tsx b/login/apps/login/src/components/idps/sign-in-with-google.tsx
new file mode 100644
index 0000000000..4759ad69c9
--- /dev/null
+++ b/login/apps/login/src/components/idps/sign-in-with-google.tsx
@@ -0,0 +1,66 @@
+"use client";
+
+import { forwardRef } from "react";
+import { Translated } from "../translated";
+import { BaseButton, SignInWithIdentityProviderProps } from "./base-button";
+
+export const SignInWithGoogle = forwardRef<
+ HTMLButtonElement,
+ SignInWithIdentityProviderProps
+>(function SignInWithGoogle(props, ref) {
+ const { children, name, ...restProps } = props;
+
+ return (
+
+
+ {children ? (
+ children
+ ) : (
+
+ {name ? (
+ name
+ ) : (
+
+ )}
+
+ )}
+
+ );
+});
diff --git a/login/apps/login/src/components/input.tsx b/login/apps/login/src/components/input.tsx
new file mode 100644
index 0000000000..de19156b91
--- /dev/null
+++ b/login/apps/login/src/components/input.tsx
@@ -0,0 +1,102 @@
+"use client";
+
+import { CheckCircleIcon } from "@heroicons/react/24/solid";
+import { clsx } from "clsx";
+import {
+ ChangeEvent,
+ DetailedHTMLProps,
+ forwardRef,
+ InputHTMLAttributes,
+ ReactNode,
+} from "react";
+
+export type TextInputProps = DetailedHTMLProps<
+ InputHTMLAttributes,
+ HTMLInputElement
+> & {
+ label: string;
+ suffix?: string;
+ placeholder?: string;
+ defaultValue?: string;
+ error?: string | ReactNode;
+ success?: string | ReactNode;
+ disabled?: boolean;
+ onChange?: (value: ChangeEvent) => void;
+ onBlur?: (value: ChangeEvent) => void;
+};
+
+const styles = (error: boolean, disabled: boolean) =>
+ clsx({
+ "h-[40px] mb-[2px] rounded p-[7px] bg-input-light-background dark:bg-input-dark-background transition-colors duration-300 grow":
+ true,
+ "border border-input-light-border dark:border-input-dark-border hover:border-black hover:dark:border-white focus:border-primary-light-500 focus:dark:border-primary-dark-500":
+ true,
+ "focus:outline-none focus:ring-0 text-base text-black dark:text-white placeholder:italic placeholder-gray-700 dark:placeholder-gray-700":
+ true,
+ "border border-warn-light-500 dark:border-warn-dark-500 hover:border-warn-light-500 hover:dark:border-warn-dark-500 focus:border-warn-light-500 focus:dark:border-warn-dark-500":
+ error,
+ "pointer-events-none text-gray-500 dark:text-gray-800 border border-input-light-border dark:border-input-dark-border hover:border-light-hoverborder hover:dark:border-hoverborder cursor-default":
+ disabled,
+ });
+
+// eslint-disable-next-line react/display-name
+export const TextInput = forwardRef(
+ (
+ {
+ label,
+ placeholder,
+ defaultValue,
+ suffix,
+ required = false,
+ error,
+ disabled,
+ success,
+ onChange,
+ onBlur,
+ ...props
+ },
+ ref,
+ ) => {
+ return (
+
+
+ {label} {required && "*"}
+
+ onChange && onChange(e)}
+ onBlur={(e) => onBlur && onBlur(e)}
+ {...props}
+ />
+
+ {suffix && (
+
+ @{suffix}
+
+ )}
+
+
+ {error ? error : " "}
+
+
+ {success && (
+
+
+ {success}
+
+ )}
+
+ );
+ },
+);
diff --git a/login/apps/login/src/components/language-provider.tsx b/login/apps/login/src/components/language-provider.tsx
new file mode 100644
index 0000000000..21a53093bb
--- /dev/null
+++ b/login/apps/login/src/components/language-provider.tsx
@@ -0,0 +1,13 @@
+import { NextIntlClientProvider } from "next-intl";
+import { getMessages } from "next-intl/server";
+import { ReactNode } from "react";
+
+export async function LanguageProvider({ children }: { children: ReactNode }) {
+ const messages = await getMessages();
+
+ return (
+
+ {children}
+
+ );
+}
diff --git a/login/apps/login/src/components/language-switcher.tsx b/login/apps/login/src/components/language-switcher.tsx
new file mode 100644
index 0000000000..67b54e58e3
--- /dev/null
+++ b/login/apps/login/src/components/language-switcher.tsx
@@ -0,0 +1,74 @@
+"use client";
+
+import { setLanguageCookie } from "@/lib/cookies";
+import { Lang, LANGS } from "@/lib/i18n";
+import {
+ Listbox,
+ ListboxButton,
+ ListboxOption,
+ ListboxOptions,
+} from "@headlessui/react";
+import { CheckIcon, ChevronDownIcon } from "@heroicons/react/24/outline";
+import clsx from "clsx";
+import { useLocale } from "next-intl";
+import { useRouter } from "next/navigation";
+import { useState } from "react";
+
+export function LanguageSwitcher() {
+ const currentLocale = useLocale();
+
+ const [selected, setSelected] = useState(
+ LANGS.find((l) => l.code === currentLocale) || LANGS[0],
+ );
+
+ const router = useRouter();
+
+ const handleChange = async (language: Lang) => {
+ setSelected(language);
+ const newLocale = language.code;
+
+ await setLanguageCookie(newLocale);
+
+ router.refresh();
+ };
+
+ return (
+
+
+
+ {selected.name}
+
+
+
+ {LANGS.map((lang, index) => (
+
+
+
+ {lang.name}
+
+
+ ))}
+
+
+
+ );
+}
diff --git a/login/apps/login/src/components/layout-providers.tsx b/login/apps/login/src/components/layout-providers.tsx
new file mode 100644
index 0000000000..fee93d015e
--- /dev/null
+++ b/login/apps/login/src/components/layout-providers.tsx
@@ -0,0 +1,17 @@
+"use client";
+
+import { useTheme } from "next-themes";
+import { ReactNode } from "react";
+
+type Props = {
+ children: ReactNode;
+};
+
+export function LayoutProviders({ children }: Props) {
+ const { resolvedTheme } = useTheme();
+ const isDark = resolvedTheme === "dark";
+
+ return (
+ {children}
+ );
+}
diff --git a/login/apps/login/src/components/ldap-username-password-form.tsx b/login/apps/login/src/components/ldap-username-password-form.tsx
new file mode 100644
index 0000000000..2f9824dff2
--- /dev/null
+++ b/login/apps/login/src/components/ldap-username-password-form.tsx
@@ -0,0 +1,109 @@
+"use client";
+
+import { createNewSessionForLDAP } from "@/lib/server/idp";
+import { useTranslations } from "next-intl";
+import { useRouter } from "next/navigation";
+import { useState } from "react";
+import { useForm } from "react-hook-form";
+import { Alert } from "./alert";
+import { BackButton } from "./back-button";
+import { Button, ButtonVariants } from "./button";
+import { TextInput } from "./input";
+import { Spinner } from "./spinner";
+import { Translated } from "./translated";
+
+type Inputs = {
+ loginName: string;
+ password: string;
+};
+
+type Props = {
+ idpId: string;
+ link: boolean;
+};
+
+export function LDAPUsernamePasswordForm({ idpId, link }: Props) {
+ const { register, handleSubmit, formState } = useForm({
+ mode: "onBlur",
+ });
+
+ const t = useTranslations("ldap");
+
+ const [error, setError] = useState("");
+
+ const [loading, setLoading] = useState(false);
+
+ const router = useRouter();
+
+ async function submitUsernamePassword(values: Inputs) {
+ setError("");
+ setLoading(true);
+
+ const response = await createNewSessionForLDAP({
+ idpId: idpId,
+ username: values.loginName,
+ password: values.password,
+ link: link,
+ })
+ .catch(() => {
+ setError("Could not start LDAP flow");
+ return;
+ })
+ .finally(() => {
+ setLoading(false);
+ });
+
+ if (response && "error" in response && response.error) {
+ setError(response.error);
+ return;
+ }
+
+ if (response && "redirect" in response && response.redirect) {
+ return router.push(response.redirect);
+ }
+ }
+
+ return (
+
+ );
+}
diff --git a/login/apps/login/src/components/login-otp.tsx b/login/apps/login/src/components/login-otp.tsx
new file mode 100644
index 0000000000..4ad6cced6a
--- /dev/null
+++ b/login/apps/login/src/components/login-otp.tsx
@@ -0,0 +1,284 @@
+"use client";
+
+import { getNextUrl } from "@/lib/client";
+import { updateSession } from "@/lib/server/session";
+import { create } from "@zitadel/client";
+import { RequestChallengesSchema } from "@zitadel/proto/zitadel/session/v2/challenge_pb";
+import { ChecksSchema } from "@zitadel/proto/zitadel/session/v2/session_service_pb";
+import { LoginSettings } from "@zitadel/proto/zitadel/settings/v2/login_settings_pb";
+import { useRouter } from "next/navigation";
+import { useEffect, useRef, useState } from "react";
+import { useForm } from "react-hook-form";
+import { Alert, AlertType } from "./alert";
+import { BackButton } from "./back-button";
+import { Button, ButtonVariants } from "./button";
+import { TextInput } from "./input";
+import { Spinner } from "./spinner";
+import { Translated } from "./translated";
+
+// either loginName or sessionId must be provided
+type Props = {
+ host: string | null;
+ loginName?: string;
+ sessionId?: string;
+ requestId?: string;
+ organization?: string;
+ method: string;
+ code?: string;
+ loginSettings?: LoginSettings;
+};
+
+type Inputs = {
+ code: string;
+};
+
+export function LoginOTP({
+ host,
+ loginName,
+ sessionId,
+ requestId,
+ organization,
+ method,
+ code,
+ loginSettings,
+}: Props) {
+ const [error, setError] = useState("");
+ const [loading, setLoading] = useState(false);
+
+ const router = useRouter();
+
+ const initialized = useRef(false);
+
+ const { register, handleSubmit, formState } = useForm({
+ mode: "onBlur",
+ defaultValues: {
+ code: code ? code : "",
+ },
+ });
+
+ useEffect(() => {
+ if (!initialized.current && ["email", "sms"].includes(method) && !code) {
+ initialized.current = true;
+ setLoading(true);
+ updateSessionForOTPChallenge()
+ .catch((error) => {
+ setError(error);
+ return;
+ })
+ .finally(() => {
+ setLoading(false);
+ });
+ }
+ }, []);
+
+ async function updateSessionForOTPChallenge() {
+ let challenges;
+
+ const basePath = process.env.NEXT_PUBLIC_BASE_PATH ?? "";
+
+ if (method === "email") {
+ challenges = create(RequestChallengesSchema, {
+ otpEmail: {
+ deliveryType: {
+ case: "sendCode",
+ value: host
+ ? {
+ urlTemplate:
+ `${host.includes("localhost") ? "http://" : "https://"}${host}${basePath}/otp/${method}?code={{.Code}}&userId={{.UserID}}&sessionId={{.SessionID}}` +
+ (requestId ? `&requestId=${requestId}` : ""),
+ }
+ : {},
+ },
+ },
+ });
+ }
+
+ if (method === "sms") {
+ challenges = create(RequestChallengesSchema, {
+ otpSms: {},
+ });
+ }
+
+ setLoading(true);
+ const response = await updateSession({
+ loginName,
+ sessionId,
+ organization,
+ challenges,
+ requestId,
+ })
+ .catch(() => {
+ setError("Could not request OTP challenge");
+ return;
+ })
+ .finally(() => {
+ setLoading(false);
+ });
+
+ if (response && "error" in response && response.error) {
+ setError(response.error);
+ return;
+ }
+
+ return response;
+ }
+
+ async function submitCode(values: Inputs, organization?: string) {
+ setLoading(true);
+
+ let body: any = {
+ code: values.code,
+ method,
+ };
+
+ if (organization) {
+ body.organization = organization;
+ }
+
+ if (requestId) {
+ body.requestId = requestId;
+ }
+
+ let checks;
+
+ if (method === "sms") {
+ checks = create(ChecksSchema, {
+ otpSms: { code: values.code },
+ });
+ }
+ if (method === "email") {
+ checks = create(ChecksSchema, {
+ otpEmail: { code: values.code },
+ });
+ }
+ if (method === "time-based") {
+ checks = create(ChecksSchema, {
+ totp: { code: values.code },
+ });
+ }
+
+ const response = await updateSession({
+ loginName,
+ sessionId,
+ organization,
+ checks,
+ requestId,
+ })
+ .catch(() => {
+ setError("Could not verify OTP code");
+ return;
+ })
+ .finally(() => {
+ setLoading(false);
+ });
+
+ if (response && "error" in response && response.error) {
+ setError(response.error);
+ return;
+ }
+
+ return response;
+ }
+
+ function setCodeAndContinue(values: Inputs, organization?: string) {
+ return submitCode(values, organization).then(async (response) => {
+ if (response && "sessionId" in response) {
+ setLoading(true);
+ // Wait for 2 seconds to avoid eventual consistency issues with an OTP code being verified in the /login endpoint
+ await new Promise((resolve) => setTimeout(resolve, 2000));
+
+ const url =
+ requestId && response.sessionId
+ ? await getNextUrl(
+ {
+ sessionId: response.sessionId,
+ requestId: requestId,
+ organization: response.factors?.user?.organizationId,
+ },
+ loginSettings?.defaultRedirectUri,
+ )
+ : response.factors?.user
+ ? await getNextUrl(
+ {
+ loginName: response.factors.user.loginName,
+ organization: response.factors?.user?.organizationId,
+ },
+ loginSettings?.defaultRedirectUri,
+ )
+ : null;
+
+ setLoading(false);
+ if (url) {
+ router.push(url);
+ }
+ }
+ });
+ }
+
+ return (
+
+ );
+}
diff --git a/login/apps/login/src/components/login-passkey.tsx b/login/apps/login/src/components/login-passkey.tsx
new file mode 100644
index 0000000000..5a3b0b6496
--- /dev/null
+++ b/login/apps/login/src/components/login-passkey.tsx
@@ -0,0 +1,280 @@
+"use client";
+
+import { coerceToArrayBuffer, coerceToBase64Url } from "@/helpers/base64";
+import { sendPasskey } from "@/lib/server/passkeys";
+import { updateSession } from "@/lib/server/session";
+import { create, JsonObject } from "@zitadel/client";
+import {
+ RequestChallengesSchema,
+ UserVerificationRequirement,
+} from "@zitadel/proto/zitadel/session/v2/challenge_pb";
+import { Checks } from "@zitadel/proto/zitadel/session/v2/session_service_pb";
+import { useRouter } from "next/navigation";
+import { useEffect, useRef, useState } from "react";
+import { Alert } from "./alert";
+import { BackButton } from "./back-button";
+import { Button, ButtonVariants } from "./button";
+import { Spinner } from "./spinner";
+import { Translated } from "./translated";
+
+// either loginName or sessionId must be provided
+type Props = {
+ loginName?: string;
+ sessionId?: string;
+ requestId?: string;
+ altPassword: boolean;
+ login?: boolean;
+ organization?: string;
+};
+
+export function LoginPasskey({
+ loginName,
+ sessionId,
+ requestId,
+ altPassword,
+ organization,
+ login = true,
+}: Props) {
+ const [error, setError] = useState("");
+ const [loading, setLoading] = useState(false);
+
+ const router = useRouter();
+
+ const initialized = useRef(false);
+
+ useEffect(() => {
+ if (!initialized.current) {
+ initialized.current = true;
+ setLoading(true);
+ updateSessionForChallenge()
+ .then((response) => {
+ const pK =
+ response?.challenges?.webAuthN?.publicKeyCredentialRequestOptions
+ ?.publicKey;
+
+ if (!pK) {
+ setError("Could not request passkey challenge");
+ setLoading(false);
+ return;
+ }
+
+ return submitLoginAndContinue(pK)
+ .catch((error) => {
+ setError(error);
+ return;
+ })
+ .finally(() => {
+ setLoading(false);
+ });
+ })
+ .catch((error) => {
+ setError(error);
+ return;
+ })
+ .finally(() => {
+ setLoading(false);
+ });
+ }
+ }, []);
+
+ async function updateSessionForChallenge(
+ userVerificationRequirement: number = login
+ ? UserVerificationRequirement.REQUIRED
+ : UserVerificationRequirement.DISCOURAGED,
+ ) {
+ setError("");
+ setLoading(true);
+ const session = await updateSession({
+ loginName,
+ sessionId,
+ organization,
+ challenges: create(RequestChallengesSchema, {
+ webAuthN: {
+ domain: "",
+ userVerificationRequirement,
+ },
+ }),
+ requestId,
+ })
+ .catch(() => {
+ setError("Could not request passkey challenge");
+ return;
+ })
+ .finally(() => {
+ setLoading(false);
+ });
+
+ if (session && "error" in session && session.error) {
+ setError(session.error);
+ return;
+ }
+
+ return session;
+ }
+
+ async function submitLogin(data: JsonObject) {
+ setLoading(true);
+ const response = await sendPasskey({
+ loginName,
+ sessionId,
+ organization,
+ checks: {
+ webAuthN: { credentialAssertionData: data },
+ } as Checks,
+ requestId,
+ })
+ .catch(() => {
+ setError("Could not verify passkey");
+ return;
+ })
+ .finally(() => {
+ setLoading(false);
+ });
+
+ if (response && "error" in response && response.error) {
+ setError(response.error);
+ return;
+ }
+
+ if (response && "redirect" in response && response.redirect) {
+ return router.push(response.redirect);
+ }
+ }
+
+ async function submitLoginAndContinue(
+ publicKey: any,
+ ): Promise {
+ publicKey.challenge = coerceToArrayBuffer(
+ publicKey.challenge,
+ "publicKey.challenge",
+ );
+ publicKey.allowCredentials.map((listItem: any) => {
+ listItem.id = coerceToArrayBuffer(
+ listItem.id,
+ "publicKey.allowCredentials.id",
+ );
+ });
+
+ navigator.credentials
+ .get({
+ publicKey,
+ })
+ .then((assertedCredential: any) => {
+ if (!assertedCredential) {
+ setError("An error on retrieving passkey");
+ return;
+ }
+
+ const authData = new Uint8Array(
+ assertedCredential.response.authenticatorData,
+ );
+ const clientDataJSON = new Uint8Array(
+ assertedCredential.response.clientDataJSON,
+ );
+ const rawId = new Uint8Array(assertedCredential.rawId);
+ const sig = new Uint8Array(assertedCredential.response.signature);
+ const userHandle = new Uint8Array(
+ assertedCredential.response.userHandle,
+ );
+ const data = {
+ id: assertedCredential.id,
+ rawId: coerceToBase64Url(rawId, "rawId"),
+ type: assertedCredential.type,
+ response: {
+ authenticatorData: coerceToBase64Url(authData, "authData"),
+ clientDataJSON: coerceToBase64Url(clientDataJSON, "clientDataJSON"),
+ signature: coerceToBase64Url(sig, "sig"),
+ userHandle: coerceToBase64Url(userHandle, "userHandle"),
+ },
+ };
+
+ return submitLogin(data);
+ })
+ .finally(() => {
+ setLoading(false);
+ });
+ }
+
+ return (
+
+ {error && (
+
+ )}
+
+ {altPassword ? (
+ {
+ const params = new URLSearchParams();
+
+ if (loginName) {
+ params.append("loginName", loginName);
+ }
+
+ if (sessionId) {
+ params.append("sessionId", sessionId);
+ }
+
+ if (requestId) {
+ params.append("requestId", requestId);
+ }
+
+ if (organization) {
+ params.append("organization", organization);
+ }
+
+ return router.push(
+ "/password?" + params, // alt is set because password is requested as alternative auth method, so passwordless prompt can be escaped
+ );
+ }}
+ data-testid="password-button"
+ >
+
+
+ ) : (
+
+ )}
+
+
+ {
+ const response = await updateSessionForChallenge().finally(() => {
+ setLoading(false);
+ });
+
+ const pK =
+ response?.challenges?.webAuthN?.publicKeyCredentialRequestOptions
+ ?.publicKey;
+
+ if (!pK) {
+ setError("Could not request passkey challenge");
+ return;
+ }
+
+ setLoading(true);
+
+ return submitLoginAndContinue(pK)
+ .catch((error) => {
+ setError(error);
+ return;
+ })
+ .finally(() => {
+ setLoading(false);
+ });
+ }}
+ data-testid="submit-button"
+ >
+ {loading && }{" "}
+
+
+
+
+ );
+}
diff --git a/login/apps/login/src/components/logo.tsx b/login/apps/login/src/components/logo.tsx
new file mode 100644
index 0000000000..09819f2ac3
--- /dev/null
+++ b/login/apps/login/src/components/logo.tsx
@@ -0,0 +1,37 @@
+import Image from "next/image";
+
+type Props = {
+ darkSrc?: string;
+ lightSrc?: string;
+ height?: number;
+ width?: number;
+};
+
+export function Logo({ lightSrc, darkSrc, height = 40, width = 147.5 }: Props) {
+ return (
+ <>
+ {darkSrc && (
+
+
+
+ )}
+ {lightSrc && (
+
+
+
+ )}
+ >
+ );
+}
diff --git a/login/apps/login/src/components/password-complexity.test.tsx b/login/apps/login/src/components/password-complexity.test.tsx
new file mode 100644
index 0000000000..090c95d397
--- /dev/null
+++ b/login/apps/login/src/components/password-complexity.test.tsx
@@ -0,0 +1,64 @@
+import {
+ cleanup,
+ render,
+ screen,
+ waitFor,
+ within,
+} from "@testing-library/react";
+import { afterEach, beforeEach, describe, expect, test } from "vitest";
+import { PasswordComplexity } from "./password-complexity";
+
+const matchesTitle = `Matches`;
+const doesntMatchTitle = `Doesn't match`;
+
+describe(" ", () => {
+ describe.each`
+ settingsMinLength | password | expectSVGTitle
+ ${5} | ${"Password1!"} | ${matchesTitle}
+ ${30} | ${"Password1!"} | ${doesntMatchTitle}
+ ${0} | ${"Password1!"} | ${matchesTitle}
+ ${undefined} | ${"Password1!"} | ${false}
+ `(
+ `With settingsMinLength=$settingsMinLength, password=$password, expectSVGTitle=$expectSVGTitle`,
+ ({ settingsMinLength, password, expectSVGTitle }) => {
+ const feedbackElementLabel = /password length/i;
+ beforeEach(() => {
+ render(
+ ,
+ );
+ });
+ afterEach(cleanup);
+
+ if (expectSVGTitle === false) {
+ test(`should not render the feedback element`, async () => {
+ await waitFor(() => {
+ expect(
+ screen.queryByText(feedbackElementLabel),
+ ).not.toBeInTheDocument();
+ });
+ });
+ } else {
+ test(`Should show one SVG with title ${expectSVGTitle}`, async () => {
+ await waitFor(async () => {
+ const svg = within(
+ screen.getByText(feedbackElementLabel)
+ .parentElement as HTMLElement,
+ ).findByRole("img");
+ expect(await svg).toHaveTextContent(expectSVGTitle);
+ });
+ });
+ }
+ },
+ );
+});
diff --git a/login/apps/login/src/components/password-complexity.tsx b/login/apps/login/src/components/password-complexity.tsx
new file mode 100644
index 0000000000..40988984b6
--- /dev/null
+++ b/login/apps/login/src/components/password-complexity.tsx
@@ -0,0 +1,99 @@
+import {
+ lowerCaseValidator,
+ numberValidator,
+ symbolValidator,
+ upperCaseValidator,
+} from "@/helpers/validators";
+import { PasswordComplexitySettings } from "@zitadel/proto/zitadel/settings/v2/password_settings_pb";
+
+type Props = {
+ passwordComplexitySettings: PasswordComplexitySettings;
+ password: string;
+ equals: boolean;
+};
+
+const check = (
+
+ Matches
+
+
+);
+const cross = (
+
+ Doesn't match
+
+
+);
+const desc =
+ "text-14px leading-4 text-input-light-label dark:text-input-dark-label";
+
+export function PasswordComplexity({
+ passwordComplexitySettings,
+ password,
+ equals,
+}: Props) {
+ const hasMinLength = password?.length >= passwordComplexitySettings.minLength;
+ const hasSymbol = symbolValidator(password);
+ const hasNumber = numberValidator(password);
+ const hasUppercase = upperCaseValidator(password);
+ const hasLowercase = lowerCaseValidator(password);
+
+ return (
+
+ {passwordComplexitySettings.minLength != undefined ? (
+
+ {hasMinLength ? check : cross}
+
+ Password length {passwordComplexitySettings.minLength.toString()}
+
+
+ ) : (
+
+ )}
+
+ {hasSymbol ? check : cross}
+ has Symbol
+
+
+ {hasNumber ? check : cross}
+ has Number
+
+
+ {hasUppercase ? check : cross}
+ has uppercase
+
+
+ {hasLowercase ? check : cross}
+ has lowercase
+
+
+ {equals ? check : cross}
+ equals
+
+
+ );
+}
diff --git a/login/apps/login/src/components/password-form.tsx b/login/apps/login/src/components/password-form.tsx
new file mode 100644
index 0000000000..3cd455c69c
--- /dev/null
+++ b/login/apps/login/src/components/password-form.tsx
@@ -0,0 +1,176 @@
+"use client";
+
+import { resetPassword, sendPassword } from "@/lib/server/password";
+import { create } from "@zitadel/client";
+import { ChecksSchema } from "@zitadel/proto/zitadel/session/v2/session_service_pb";
+import { LoginSettings } from "@zitadel/proto/zitadel/settings/v2/login_settings_pb";
+import { useRouter } from "next/navigation";
+import { useState } from "react";
+import { useForm } from "react-hook-form";
+import { Alert, AlertType } from "./alert";
+import { BackButton } from "./back-button";
+import { Button, ButtonVariants } from "./button";
+import { TextInput } from "./input";
+import { Spinner } from "./spinner";
+import { Translated } from "./translated";
+
+type Inputs = {
+ password: string;
+};
+
+type Props = {
+ loginSettings: LoginSettings | undefined;
+ loginName: string;
+ organization?: string;
+ requestId?: string;
+};
+
+export function PasswordForm({
+ loginSettings,
+ loginName,
+ organization,
+ requestId,
+}: Props) {
+ const { register, handleSubmit, formState } = useForm({
+ mode: "onBlur",
+ });
+
+ const [info, setInfo] = useState("");
+ const [error, setError] = useState("");
+
+ const [loading, setLoading] = useState(false);
+
+ const router = useRouter();
+
+ async function submitPassword(values: Inputs) {
+ setError("");
+ setLoading(true);
+
+ const response = await sendPassword({
+ loginName,
+ organization,
+ checks: create(ChecksSchema, {
+ password: { password: values.password },
+ }),
+ requestId,
+ })
+ .catch(() => {
+ setError("Could not verify password");
+ return;
+ })
+ .finally(() => {
+ setLoading(false);
+ });
+
+ if (response && "error" in response && response.error) {
+ setError(response.error);
+ return;
+ }
+
+ if (response && "redirect" in response && response.redirect) {
+ return router.push(response.redirect);
+ }
+ }
+
+ async function resetPasswordAndContinue() {
+ setError("");
+ setInfo("");
+ setLoading(true);
+
+ const response = await resetPassword({
+ loginName,
+ organization,
+ requestId,
+ })
+ .catch(() => {
+ setError("Could not reset password");
+ return;
+ })
+ .finally(() => {
+ setLoading(false);
+ });
+
+ if (response && "error" in response) {
+ setError(response.error);
+ return;
+ }
+
+ setInfo("Password was reset. Please check your email.");
+
+ const params = new URLSearchParams({
+ loginName: loginName,
+ });
+
+ if (organization) {
+ params.append("organization", organization);
+ }
+
+ if (requestId) {
+ params.append("requestId", requestId);
+ }
+
+ return router.push("/password/set?" + params);
+ }
+
+ return (
+
+ );
+}
diff --git a/login/apps/login/src/components/privacy-policy-checkboxes.tsx b/login/apps/login/src/components/privacy-policy-checkboxes.tsx
new file mode 100644
index 0000000000..4ab0e33222
--- /dev/null
+++ b/login/apps/login/src/components/privacy-policy-checkboxes.tsx
@@ -0,0 +1,105 @@
+"use client";
+import { LegalAndSupportSettings } from "@zitadel/proto/zitadel/settings/v2/legal_settings_pb";
+import Link from "next/link";
+import { useState } from "react";
+import { Checkbox } from "./checkbox";
+import { Translated } from "./translated";
+
+type Props = {
+ legal: LegalAndSupportSettings;
+ onChange: (allAccepted: boolean) => void;
+};
+
+type AcceptanceState = {
+ tosAccepted: boolean;
+ privacyPolicyAccepted: boolean;
+};
+
+export function PrivacyPolicyCheckboxes({ legal, onChange }: Props) {
+ const [acceptanceState, setAcceptanceState] = useState({
+ tosAccepted: false,
+ privacyPolicyAccepted: false,
+ });
+
+ return (
+ <>
+
+
+ {legal?.helpLink && (
+
+
+
+
+
+
+
+ )}
+
+ {legal?.tosLink && (
+
+
{
+ setAcceptanceState({
+ ...acceptanceState,
+ tosAccepted: checked,
+ });
+ onChange(checked && acceptanceState.privacyPolicyAccepted);
+ }}
+ data-testid="privacy-policy-checkbox"
+ />
+
+
+
+ )}
+ {legal?.privacyPolicyLink && (
+
+
{
+ setAcceptanceState({
+ ...acceptanceState,
+ privacyPolicyAccepted: checked,
+ });
+ onChange(checked && acceptanceState.tosAccepted);
+ }}
+ data-testid="tos-checkbox"
+ />
+
+
+
+ )}
+ >
+ );
+}
diff --git a/login/apps/login/src/components/register-form-idp-incomplete.tsx b/login/apps/login/src/components/register-form-idp-incomplete.tsx
new file mode 100644
index 0000000000..b8a7765c9c
--- /dev/null
+++ b/login/apps/login/src/components/register-form-idp-incomplete.tsx
@@ -0,0 +1,156 @@
+"use client";
+
+import { registerUserAndLinkToIDP } from "@/lib/server/register";
+import { useRouter } from "next/navigation";
+import { useState } from "react";
+import { FieldValues, useForm } from "react-hook-form";
+import { Alert } from "./alert";
+import { BackButton } from "./back-button";
+import { Button, ButtonVariants } from "./button";
+import { TextInput } from "./input";
+import { Spinner } from "./spinner";
+import { Translated } from "./translated";
+
+type Inputs =
+ | {
+ firstname: string;
+ lastname: string;
+ email: string;
+ }
+ | FieldValues;
+
+type Props = {
+ organization: string;
+ requestId?: string;
+ idpIntent: {
+ idpIntentId: string;
+ idpIntentToken: string;
+ };
+ defaultValues?: {
+ firstname?: string;
+ lastname?: string;
+ email?: string;
+ };
+ idpUserId: string;
+ idpId: string;
+ idpUserName: string;
+};
+
+export function RegisterFormIDPIncomplete({
+ organization,
+ requestId,
+ idpIntent,
+ defaultValues,
+ idpUserId,
+ idpId,
+ idpUserName,
+}: Props) {
+ const { register, handleSubmit, formState } = useForm({
+ mode: "onBlur",
+ defaultValues: {
+ email: defaultValues?.email ?? "",
+ firstname: defaultValues?.firstname ?? "",
+ lastname: defaultValues?.lastname ?? "",
+ },
+ });
+
+ const [loading, setLoading] = useState(false);
+ const [error, setError] = useState("");
+
+ const router = useRouter();
+
+ async function submitAndRegister(values: Inputs) {
+ setLoading(true);
+ const response = await registerUserAndLinkToIDP({
+ idpId: idpId,
+ idpUserName: idpUserName,
+ idpUserId: idpUserId,
+ email: values.email,
+ firstName: values.firstname,
+ lastName: values.lastname,
+ organization: organization,
+ requestId: requestId,
+ idpIntent: idpIntent,
+ })
+ .catch(() => {
+ setError("Could not register user");
+ return;
+ })
+ .finally(() => {
+ setLoading(false);
+ });
+
+ if (response && "error" in response && response.error) {
+ setError(response.error);
+ return;
+ }
+
+ if (response && "redirect" in response && response.redirect) {
+ return router.push(response.redirect);
+ }
+
+ return response;
+ }
+
+ const { errors } = formState;
+
+ return (
+
+ );
+}
diff --git a/login/apps/login/src/components/register-form.tsx b/login/apps/login/src/components/register-form.tsx
new file mode 100644
index 0000000000..6217bbcbb9
--- /dev/null
+++ b/login/apps/login/src/components/register-form.tsx
@@ -0,0 +1,227 @@
+"use client";
+
+import { registerUser } from "@/lib/server/register";
+import { LegalAndSupportSettings } from "@zitadel/proto/zitadel/settings/v2/legal_settings_pb";
+import {
+ LoginSettings,
+ PasskeysType,
+} from "@zitadel/proto/zitadel/settings/v2/login_settings_pb";
+import { useRouter } from "next/navigation";
+import { useState } from "react";
+import { FieldValues, useForm } from "react-hook-form";
+import { Alert, AlertType } from "./alert";
+import {
+ AuthenticationMethod,
+ AuthenticationMethodRadio,
+ methods,
+} from "./authentication-method-radio";
+import { BackButton } from "./back-button";
+import { Button, ButtonVariants } from "./button";
+import { TextInput } from "./input";
+import { PrivacyPolicyCheckboxes } from "./privacy-policy-checkboxes";
+import { Spinner } from "./spinner";
+import { Translated } from "./translated";
+
+type Inputs =
+ | {
+ firstname: string;
+ lastname: string;
+ email: string;
+ }
+ | FieldValues;
+
+type Props = {
+ legal: LegalAndSupportSettings;
+ firstname?: string;
+ lastname?: string;
+ email?: string;
+ organization: string;
+ requestId?: string;
+ loginSettings?: LoginSettings;
+ idpCount: number;
+};
+
+export function RegisterForm({
+ legal,
+ email,
+ firstname,
+ lastname,
+ organization,
+ requestId,
+ loginSettings,
+ idpCount = 0,
+}: Props) {
+ const { register, handleSubmit, formState } = useForm({
+ mode: "onBlur",
+ defaultValues: {
+ email: email ?? "",
+ firstName: firstname ?? "",
+ lastname: lastname ?? "",
+ },
+ });
+
+ const [loading, setLoading] = useState(false);
+ const [selected, setSelected] = useState(methods[0]);
+ const [error, setError] = useState("");
+
+ const router = useRouter();
+
+ async function submitAndRegister(values: Inputs) {
+ setLoading(true);
+ const response = await registerUser({
+ email: values.email,
+ firstName: values.firstname,
+ lastName: values.lastname,
+ organization: organization,
+ requestId: requestId,
+ })
+ .catch(() => {
+ setError("Could not register user");
+ return;
+ })
+ .finally(() => {
+ setLoading(false);
+ });
+
+ if (response && "error" in response && response.error) {
+ setError(response.error);
+ return;
+ }
+
+ if (response && "redirect" in response && response.redirect) {
+ return router.push(response.redirect);
+ }
+
+ return response;
+ }
+
+ async function submitAndContinue(
+ value: Inputs,
+ withPassword: boolean = false,
+ ) {
+ const registerParams: any = value;
+
+ if (organization) {
+ registerParams.organization = organization;
+ }
+
+ if (requestId) {
+ registerParams.requestId = requestId;
+ }
+
+ // redirect user to /register/password if password is chosen
+ if (withPassword) {
+ return router.push(
+ `/register/password?` + new URLSearchParams(registerParams),
+ );
+ } else {
+ return submitAndRegister(value);
+ }
+ }
+
+ const { errors } = formState;
+
+ const [tosAndPolicyAccepted, setTosAndPolicyAccepted] = useState(false);
+ return (
+
+ );
+}
diff --git a/login/apps/login/src/components/register-passkey.tsx b/login/apps/login/src/components/register-passkey.tsx
new file mode 100644
index 0000000000..e21e1acdbb
--- /dev/null
+++ b/login/apps/login/src/components/register-passkey.tsx
@@ -0,0 +1,220 @@
+"use client";
+
+import { coerceToArrayBuffer, coerceToBase64Url } from "@/helpers/base64";
+import {
+ registerPasskeyLink,
+ verifyPasskeyRegistration,
+} from "@/lib/server/passkeys";
+import { useRouter } from "next/navigation";
+import { useState } from "react";
+import { useForm } from "react-hook-form";
+import { Alert } from "./alert";
+import { BackButton } from "./back-button";
+import { Button, ButtonVariants } from "./button";
+import { Spinner } from "./spinner";
+import { Translated } from "./translated";
+
+type Inputs = {};
+
+type Props = {
+ sessionId: string;
+ isPrompt: boolean;
+ requestId?: string;
+ organization?: string;
+};
+
+export function RegisterPasskey({
+ sessionId,
+ isPrompt,
+ organization,
+ requestId,
+}: Props) {
+ const { handleSubmit, formState } = useForm({
+ mode: "onBlur",
+ });
+
+ const [error, setError] = useState("");
+
+ const [loading, setLoading] = useState(false);
+
+ const router = useRouter();
+
+ async function submitVerify(
+ passkeyId: string,
+ passkeyName: string,
+ publicKeyCredential: any,
+ sessionId: string,
+ ) {
+ setLoading(true);
+ const response = await verifyPasskeyRegistration({
+ passkeyId,
+ passkeyName,
+ publicKeyCredential,
+ sessionId,
+ })
+ .catch(() => {
+ setError("Could not verify Passkey");
+ return;
+ })
+ .finally(() => {
+ setLoading(false);
+ });
+
+ return response;
+ }
+
+ async function submitRegisterAndContinue(): Promise {
+ setLoading(true);
+ const resp = await registerPasskeyLink({
+ sessionId,
+ })
+ .catch(() => {
+ setError("Could not register passkey");
+ return;
+ })
+ .finally(() => {
+ setLoading(false);
+ });
+
+ if (!resp) {
+ setError("An error on registering passkey");
+ return;
+ }
+
+ if ("error" in resp && resp.error) {
+ setError(resp.error);
+ return;
+ }
+
+ if (!("passkeyId" in resp)) {
+ setError("An error on registering passkey");
+ return;
+ }
+
+ const passkeyId = resp.passkeyId;
+ const options: CredentialCreationOptions =
+ (resp.publicKeyCredentialCreationOptions as CredentialCreationOptions) ??
+ {};
+
+ if (!options.publicKey) {
+ setError("An error on registering passkey");
+ return;
+ }
+
+ options.publicKey.challenge = coerceToArrayBuffer(
+ options.publicKey.challenge,
+ "challenge",
+ );
+ options.publicKey.user.id = coerceToArrayBuffer(
+ options.publicKey.user.id,
+ "userid",
+ );
+ if (options.publicKey.excludeCredentials) {
+ options.publicKey.excludeCredentials.map((cred: any) => {
+ cred.id = coerceToArrayBuffer(
+ cred.id as string,
+ "excludeCredentials.id",
+ );
+ return cred;
+ });
+ }
+
+ const credentials = await navigator.credentials.create(options);
+
+ if (
+ !credentials ||
+ !(credentials as any).response?.attestationObject ||
+ !(credentials as any).response?.clientDataJSON ||
+ !(credentials as any).rawId
+ ) {
+ setError("An error on registering passkey");
+ return;
+ }
+
+ const attestationObject = (credentials as any).response.attestationObject;
+ const clientDataJSON = (credentials as any).response.clientDataJSON;
+ const rawId = (credentials as any).rawId;
+
+ const data = {
+ id: credentials.id,
+ rawId: coerceToBase64Url(rawId, "rawId"),
+ type: credentials.type,
+ response: {
+ attestationObject: coerceToBase64Url(
+ attestationObject,
+ "attestationObject",
+ ),
+ clientDataJSON: coerceToBase64Url(clientDataJSON, "clientDataJSON"),
+ },
+ };
+
+ const verificationResponse = await submitVerify(
+ passkeyId,
+ "",
+ data,
+ sessionId,
+ );
+
+ if (!verificationResponse) {
+ setError("Could not verify Passkey!");
+ return;
+ }
+
+ continueAndLogin();
+ }
+
+ function continueAndLogin() {
+ const params = new URLSearchParams();
+
+ if (organization) {
+ params.set("organization", organization);
+ }
+
+ if (requestId) {
+ params.set("requestId", requestId);
+ }
+
+ params.set("sessionId", sessionId);
+
+ router.push("/passkey?" + params);
+ }
+
+ return (
+
+ );
+}
diff --git a/login/apps/login/src/components/register-u2f.tsx b/login/apps/login/src/components/register-u2f.tsx
new file mode 100644
index 0000000000..e72bf1fc69
--- /dev/null
+++ b/login/apps/login/src/components/register-u2f.tsx
@@ -0,0 +1,225 @@
+"use client";
+
+import { coerceToArrayBuffer, coerceToBase64Url } from "@/helpers/base64";
+import { getNextUrl } from "@/lib/client";
+import { addU2F, verifyU2F } from "@/lib/server/u2f";
+import { LoginSettings } from "@zitadel/proto/zitadel/settings/v2/login_settings_pb";
+import { RegisterU2FResponse } from "@zitadel/proto/zitadel/user/v2/user_service_pb";
+import { useRouter } from "next/navigation";
+import { useState } from "react";
+import { Alert } from "./alert";
+import { BackButton } from "./back-button";
+import { Button, ButtonVariants } from "./button";
+import { Spinner } from "./spinner";
+import { Translated } from "./translated";
+
+type Props = {
+ loginName?: string;
+ sessionId: string;
+ requestId?: string;
+ organization?: string;
+ checkAfter: boolean;
+ loginSettings?: LoginSettings;
+};
+
+export function RegisterU2f({
+ loginName,
+ sessionId,
+ organization,
+ requestId,
+ checkAfter,
+ loginSettings,
+}: Props) {
+ const [error, setError] = useState("");
+
+ const [loading, setLoading] = useState(false);
+
+ const router = useRouter();
+
+ async function submitVerify(
+ u2fId: string,
+ passkeyName: string,
+ publicKeyCredential: any,
+ sessionId: string,
+ ) {
+ setError("");
+ setLoading(true);
+ const response = await verifyU2F({
+ u2fId,
+ passkeyName,
+ publicKeyCredential,
+ sessionId,
+ })
+ .catch(() => {
+ setError("An error on verifying passkey occurred");
+ return;
+ })
+ .finally(() => {
+ setLoading(false);
+ });
+
+ if (response && "error" in response && response?.error) {
+ setError(response?.error);
+ return;
+ }
+
+ return response;
+ }
+
+ async function submitRegisterAndContinue(): Promise {
+ setError("");
+ setLoading(true);
+ const response = await addU2F({
+ sessionId,
+ })
+ .catch(() => {
+ setError("An error on registering passkey");
+ return;
+ })
+ .finally(() => {
+ setLoading(false);
+ });
+
+ if (response && "error" in response && response?.error) {
+ setError(response?.error);
+ return;
+ }
+
+ if (!response || !("u2fId" in response)) {
+ setError("An error on registering passkey");
+ return;
+ }
+
+ const u2fResponse = response as unknown as RegisterU2FResponse;
+
+ const u2fId = u2fResponse.u2fId;
+ const options: CredentialCreationOptions =
+ (u2fResponse?.publicKeyCredentialCreationOptions as CredentialCreationOptions) ??
+ {};
+
+ if (options.publicKey) {
+ options.publicKey.challenge = coerceToArrayBuffer(
+ options.publicKey.challenge,
+ "challenge",
+ );
+ options.publicKey.user.id = coerceToArrayBuffer(
+ options.publicKey.user.id,
+ "userid",
+ );
+ if (options.publicKey.excludeCredentials) {
+ options.publicKey.excludeCredentials.map((cred: any) => {
+ cred.id = coerceToArrayBuffer(
+ cred.id as string,
+ "excludeCredentials.id",
+ );
+ return cred;
+ });
+ }
+
+ const resp = await navigator.credentials.create(options);
+
+ if (
+ !resp ||
+ !(resp as any).response.attestationObject ||
+ !(resp as any).response.clientDataJSON ||
+ !(resp as any).rawId
+ ) {
+ setError("An error on registering passkey");
+ return;
+ }
+
+ const attestationObject = (resp as any).response.attestationObject;
+ const clientDataJSON = (resp as any).response.clientDataJSON;
+ const rawId = (resp as any).rawId;
+
+ const data = {
+ id: resp.id,
+ rawId: coerceToBase64Url(rawId, "rawId"),
+ type: resp.type,
+ response: {
+ attestationObject: coerceToBase64Url(
+ attestationObject,
+ "attestationObject",
+ ),
+ clientDataJSON: coerceToBase64Url(clientDataJSON, "clientDataJSON"),
+ },
+ };
+
+ const submitResponse = await submitVerify(u2fId, "", data, sessionId);
+
+ if (!submitResponse) {
+ setError("An error on verifying passkey");
+ return;
+ }
+
+ if (checkAfter) {
+ const paramsToContinue = new URLSearchParams({});
+
+ if (sessionId) {
+ paramsToContinue.append("sessionId", sessionId);
+ }
+ if (loginName) {
+ paramsToContinue.append("loginName", loginName);
+ }
+ if (organization) {
+ paramsToContinue.append("organization", organization);
+ }
+ if (requestId) {
+ paramsToContinue.append("requestId", requestId);
+ }
+
+ return router.push(`/u2f?` + paramsToContinue);
+ } else {
+ const url =
+ requestId && sessionId
+ ? await getNextUrl(
+ {
+ sessionId: sessionId,
+ requestId: requestId,
+ organization: organization,
+ },
+ loginSettings?.defaultRedirectUri,
+ )
+ : loginName
+ ? await getNextUrl(
+ {
+ loginName: loginName,
+ organization: organization,
+ },
+ loginSettings?.defaultRedirectUri,
+ )
+ : null;
+ if (url) {
+ return router.push(url);
+ }
+ }
+ }
+ }
+
+ return (
+
+ );
+}
diff --git a/login/apps/login/src/components/self-service-menu.tsx b/login/apps/login/src/components/self-service-menu.tsx
new file mode 100644
index 0000000000..449c1dda1f
--- /dev/null
+++ b/login/apps/login/src/components/self-service-menu.tsx
@@ -0,0 +1,42 @@
+import Link from "next/link";
+
+export function SelfServiceMenu({ sessionId }: { sessionId: string }) {
+ const list: any[] = [];
+
+ // if (!!config.selfservice.change_password.enabled) {
+ // list.push({
+ // link:
+ // `/me/change-password?` +
+ // new URLSearchParams({
+ // sessionId: sessionId,
+ // }),
+ // name: "Change password",
+ // });
+ // }
+
+ return (
+
+ {list.map((menuitem, index) => {
+ return (
+
+ );
+ })}
+
+ );
+}
+
+const SelfServiceItem = ({ name, link }: { name: string; link: string }) => {
+ return (
+
+ {name}
+
+ );
+};
diff --git a/login/apps/login/src/components/session-clear-item.tsx b/login/apps/login/src/components/session-clear-item.tsx
new file mode 100644
index 0000000000..81930b11b3
--- /dev/null
+++ b/login/apps/login/src/components/session-clear-item.tsx
@@ -0,0 +1,105 @@
+"use client";
+
+import { clearSession } from "@/lib/server/session";
+import { timestampDate } from "@zitadel/client";
+import { Session } from "@zitadel/proto/zitadel/session/v2/session_pb";
+import moment from "moment";
+import { useLocale } from "next-intl";
+import { useRouter } from "next/navigation";
+import { useState } from "react";
+import { Avatar } from "./avatar";
+import { isSessionValid } from "./session-item";
+import { Translated } from "./translated";
+
+export function SessionClearItem({
+ session,
+ reload,
+}: {
+ session: Session;
+ reload: () => void;
+}) {
+ const currentLocale = useLocale();
+ moment.locale(currentLocale === "zh" ? "zh-cn" : currentLocale);
+
+ const [loading, setLoading] = useState(false);
+
+ async function clearSessionId(id: string) {
+ setLoading(true);
+ const response = await clearSession({
+ sessionId: id,
+ })
+ .catch((error) => {
+ setError(error.message);
+ return;
+ })
+ .finally(() => {
+ setLoading(false);
+ });
+
+ return response;
+ }
+
+ const { valid, verifiedAt } = isSessionValid(session);
+
+ const [error, setError] = useState(null);
+
+ const router = useRouter();
+
+ return (
+ {
+ clearSessionId(session.id).then(() => {
+ reload();
+ });
+ }}
+ className="group flex flex-row items-center bg-background-light-400 dark:bg-background-dark-400 border border-divider-light hover:shadow-lg dark:hover:bg-white/10 py-2 px-4 rounded-md transition-all"
+ >
+
+
+
+ {session.factors?.user?.displayName}
+
+ {session.factors?.user?.loginName}
+
+ {valid ? (
+
+ {verifiedAt && (
+
+ )}
+
+ ) : (
+ verifiedAt && (
+
+ expired{" "}
+ {session.expirationDate &&
+ moment(timestampDate(session.expirationDate)).fromNow()}
+
+ )
+ )}
+
+
+
+
+
+
+
+
+ {valid ? (
+
+ ) : (
+
+ )}
+
+
+ );
+}
diff --git a/login/apps/login/src/components/session-item.tsx b/login/apps/login/src/components/session-item.tsx
new file mode 100644
index 0000000000..94e7a19da5
--- /dev/null
+++ b/login/apps/login/src/components/session-item.tsx
@@ -0,0 +1,156 @@
+"use client";
+
+import { sendLoginname } from "@/lib/server/loginname";
+import { clearSession, continueWithSession } from "@/lib/server/session";
+import { XCircleIcon } from "@heroicons/react/24/outline";
+import { Timestamp, timestampDate } from "@zitadel/client";
+import { Session } from "@zitadel/proto/zitadel/session/v2/session_pb";
+import moment from "moment";
+import { useLocale } from "next-intl";
+import { useRouter } from "next/navigation";
+import { useState } from "react";
+import { Avatar } from "./avatar";
+
+export function isSessionValid(session: Partial): {
+ valid: boolean;
+ verifiedAt?: Timestamp;
+} {
+ const validPassword = session?.factors?.password?.verifiedAt;
+ const validPasskey = session?.factors?.webAuthN?.verifiedAt;
+ const validIDP = session?.factors?.intent?.verifiedAt;
+
+ const stillValid = session.expirationDate
+ ? timestampDate(session.expirationDate) > new Date()
+ : true;
+
+ const verifiedAt = validPassword || validPasskey || validIDP;
+ const valid = !!((validPassword || validPasskey || validIDP) && stillValid);
+
+ return { valid, verifiedAt };
+}
+
+export function SessionItem({
+ session,
+ reload,
+ requestId,
+}: {
+ session: Session;
+ reload: () => void;
+ requestId?: string;
+}) {
+ const currentLocale = useLocale();
+ moment.locale(currentLocale === "zh" ? "zh-cn" : currentLocale);
+
+ const [loading, setLoading] = useState(false);
+
+ async function clearSessionId(id: string) {
+ setLoading(true);
+ const response = await clearSession({
+ sessionId: id,
+ })
+ .catch((error) => {
+ setError(error.message);
+ return;
+ })
+ .finally(() => {
+ setLoading(false);
+ });
+
+ return response;
+ }
+
+ const { valid, verifiedAt } = isSessionValid(session);
+
+ const [error, setError] = useState(null);
+
+ const router = useRouter();
+
+ return (
+ {
+ if (valid && session?.factors?.user) {
+ const resp = await continueWithSession({
+ ...session,
+ requestId: requestId,
+ });
+
+ if (resp?.redirect) {
+ return router.push(resp.redirect);
+ }
+ } else if (session.factors?.user) {
+ setLoading(true);
+ const res = await sendLoginname({
+ loginName: session.factors?.user?.loginName,
+ organization: session.factors.user.organizationId,
+ requestId: requestId,
+ })
+ .catch(() => {
+ setError("An internal error occurred");
+ return;
+ })
+ .finally(() => {
+ setLoading(false);
+ });
+
+ if (res && "redirect" in res && res.redirect) {
+ return router.push(res.redirect);
+ }
+
+ if (res && "error" in res && res.error) {
+ setError(res.error);
+ return;
+ }
+ }
+ }}
+ className="group flex flex-row items-center bg-background-light-400 dark:bg-background-dark-400 border border-divider-light hover:shadow-lg dark:hover:bg-white/10 py-2 px-4 rounded-md transition-all"
+ >
+
+
+
+ {session.factors?.user?.displayName}
+
+ {session.factors?.user?.loginName}
+
+ {valid ? (
+
+ {verifiedAt && moment(timestampDate(verifiedAt)).fromNow()}
+
+ ) : (
+ verifiedAt && (
+
+ expired{" "}
+ {session.expirationDate &&
+ moment(timestampDate(session.expirationDate)).fromNow()}
+
+ )
+ )}
+
+
+
+
+ {valid ? (
+
+ ) : (
+
+ )}
+
+
{
+ event.preventDefault();
+ event.stopPropagation();
+ clearSessionId(session.id).then(() => {
+ reload();
+ });
+ }}
+ />
+
+
+ );
+}
diff --git a/login/apps/login/src/components/sessions-clear-list.tsx b/login/apps/login/src/components/sessions-clear-list.tsx
new file mode 100644
index 0000000000..5989948725
--- /dev/null
+++ b/login/apps/login/src/components/sessions-clear-list.tsx
@@ -0,0 +1,109 @@
+"use client";
+
+import { clearSession } from "@/lib/server/session";
+import { timestampDate } from "@zitadel/client";
+import { Session } from "@zitadel/proto/zitadel/session/v2/session_pb";
+import { redirect, useRouter } from "next/navigation";
+import { useEffect, useState } from "react";
+import { Alert, AlertType } from "./alert";
+import { SessionClearItem } from "./session-clear-item";
+import { Translated } from "./translated";
+
+type Props = {
+ sessions: Session[];
+ postLogoutRedirectUri?: string;
+ logoutHint?: string;
+ organization?: string;
+};
+
+export function SessionsClearList({
+ sessions,
+ logoutHint,
+ postLogoutRedirectUri,
+ organization,
+}: Props) {
+ const [list, setList] = useState(sessions);
+ const router = useRouter();
+
+ async function clearHintedSession() {
+ console.log("Clearing session for login hint:", logoutHint);
+ // If a login hint is provided, we logout that specific session
+ const sessionIdToBeCleared = sessions.find((session) => {
+ return session.factors?.user?.loginName === logoutHint;
+ })?.id;
+
+ if (sessionIdToBeCleared) {
+ const clearSessionResponse = await clearSession({
+ sessionId: sessionIdToBeCleared,
+ }).catch((error) => {
+ console.error("Error clearing session:", error);
+ return;
+ });
+
+ if (!clearSessionResponse) {
+ console.error("Failed to clear session for login hint:", logoutHint);
+ }
+
+ if (postLogoutRedirectUri) {
+ return redirect(postLogoutRedirectUri);
+ }
+
+ const params = new URLSearchParams();
+
+ if (organization) {
+ params.set("organization", organization);
+ }
+
+ return router.push("/logout/success?" + params);
+ } else {
+ console.warn(`No session found for login hint: ${logoutHint}`);
+ }
+ }
+
+ useEffect(() => {
+ if (logoutHint) {
+ clearHintedSession();
+ }
+ }, []);
+
+ return sessions ? (
+
+ {list
+ .filter((session) => session?.factors?.user?.loginName)
+ // sort by change date descending
+ .sort((a, b) => {
+ const dateA = a.changeDate
+ ? timestampDate(a.changeDate).getTime()
+ : 0;
+ const dateB = b.changeDate
+ ? timestampDate(b.changeDate).getTime()
+ : 0;
+ return dateB - dateA;
+ })
+ // TODO: add sorting to move invalid sessions to the bottom
+ .map((session, index) => {
+ return (
+
{
+ setList(list.filter((s) => s.id !== session.id));
+ if (postLogoutRedirectUri) {
+ router.push(postLogoutRedirectUri);
+ }
+ }}
+ key={"session-" + index}
+ />
+ );
+ })}
+ {list.length === 0 && (
+
+
+
+ )}
+
+ ) : (
+
+
+
+ );
+}
diff --git a/login/apps/login/src/components/sessions-list.tsx b/login/apps/login/src/components/sessions-list.tsx
new file mode 100644
index 0000000000..a3a1f8ed94
--- /dev/null
+++ b/login/apps/login/src/components/sessions-list.tsx
@@ -0,0 +1,50 @@
+"use client";
+
+import { timestampDate } from "@zitadel/client";
+import { Session } from "@zitadel/proto/zitadel/session/v2/session_pb";
+import { useState } from "react";
+import { Alert } from "./alert";
+import { SessionItem } from "./session-item";
+import { Translated } from "./translated";
+
+type Props = {
+ sessions: Session[];
+ requestId?: string;
+};
+
+export function SessionsList({ sessions, requestId }: Props) {
+ const [list, setList] = useState(sessions);
+ return sessions ? (
+
+ {list
+ .filter((session) => session?.factors?.user?.loginName)
+ // sort by change date descending
+ .sort((a, b) => {
+ const dateA = a.changeDate
+ ? timestampDate(a.changeDate).getTime()
+ : 0;
+ const dateB = b.changeDate
+ ? timestampDate(b.changeDate).getTime()
+ : 0;
+ return dateB - dateA;
+ })
+ // TODO: add sorting to move invalid sessions to the bottom
+ .map((session, index) => {
+ return (
+ {
+ setList(list.filter((s) => s.id !== session.id));
+ }}
+ key={"session-" + index}
+ />
+ );
+ })}
+
+ ) : (
+
+
+
+ );
+}
diff --git a/login/apps/login/src/components/set-password-form.tsx b/login/apps/login/src/components/set-password-form.tsx
new file mode 100644
index 0000000000..2c3db8dbf2
--- /dev/null
+++ b/login/apps/login/src/components/set-password-form.tsx
@@ -0,0 +1,286 @@
+"use client";
+
+import {
+ lowerCaseValidator,
+ numberValidator,
+ symbolValidator,
+ upperCaseValidator,
+} from "@/helpers/validators";
+import {
+ changePassword,
+ resetPassword,
+ sendPassword,
+} from "@/lib/server/password";
+import { create } from "@zitadel/client";
+import { ChecksSchema } from "@zitadel/proto/zitadel/session/v2/session_service_pb";
+import { PasswordComplexitySettings } from "@zitadel/proto/zitadel/settings/v2/password_settings_pb";
+import { useRouter } from "next/navigation";
+import { useState } from "react";
+import { FieldValues, useForm } from "react-hook-form";
+import { Alert, AlertType } from "./alert";
+import { BackButton } from "./back-button";
+import { Button, ButtonVariants } from "./button";
+import { TextInput } from "./input";
+import { PasswordComplexity } from "./password-complexity";
+import { Spinner } from "./spinner";
+import { Translated } from "./translated";
+
+type Inputs =
+ | {
+ code: string;
+ password: string;
+ confirmPassword: string;
+ }
+ | FieldValues;
+
+type Props = {
+ code?: string;
+ passwordComplexitySettings: PasswordComplexitySettings;
+ loginName: string;
+ userId: string;
+ organization?: string;
+ requestId?: string;
+ codeRequired: boolean;
+};
+
+export function SetPasswordForm({
+ passwordComplexitySettings,
+ organization,
+ requestId,
+ loginName,
+ userId,
+ code,
+ codeRequired,
+}: Props) {
+ const { register, handleSubmit, watch, formState } = useForm({
+ mode: "onBlur",
+ defaultValues: {
+ code: code ?? "",
+ },
+ });
+
+ const [loading, setLoading] = useState(false);
+ const [error, setError] = useState("");
+
+ const router = useRouter();
+
+ async function resendCode() {
+ setError("");
+ setLoading(true);
+
+ const response = await resetPassword({
+ loginName,
+ organization,
+ requestId,
+ })
+ .catch(() => {
+ setError("Could not reset password");
+ return;
+ })
+ .finally(() => {
+ setLoading(false);
+ });
+
+ if (response && "error" in response) {
+ setError(response.error);
+ return;
+ }
+ }
+
+ async function submitPassword(values: Inputs) {
+ setLoading(true);
+ let payload: { userId: string; password: string; code?: string } = {
+ userId: userId,
+ password: values.password,
+ };
+
+ // this is not required for initial password setup
+ if (codeRequired) {
+ payload = { ...payload, code: values.code };
+ }
+
+ const changeResponse = await changePassword(payload)
+ .catch(() => {
+ setError("Could not set password");
+ return;
+ })
+ .finally(() => {
+ setLoading(false);
+ });
+
+ if (changeResponse && "error" in changeResponse) {
+ setError(changeResponse.error);
+ return;
+ }
+
+ if (!changeResponse) {
+ setError("Could not set password");
+ return;
+ }
+
+ const params = new URLSearchParams({});
+
+ if (loginName) {
+ params.append("loginName", loginName);
+ }
+ if (organization) {
+ params.append("organization", organization);
+ }
+
+ await new Promise((resolve) => setTimeout(resolve, 2000)); // Wait for a second to avoid eventual consistency issues with an initial password being set
+
+ const passwordResponse = await sendPassword({
+ loginName,
+ organization,
+ checks: create(ChecksSchema, {
+ password: { password: values.password },
+ }),
+ requestId,
+ })
+ .catch(() => {
+ setError("Could not verify password");
+ return;
+ })
+ .finally(() => {
+ setLoading(false);
+ });
+
+ if (
+ passwordResponse &&
+ "error" in passwordResponse &&
+ passwordResponse.error
+ ) {
+ setError(passwordResponse.error);
+ return;
+ }
+
+ if (
+ passwordResponse &&
+ "redirect" in passwordResponse &&
+ passwordResponse.redirect
+ ) {
+ return router.push(passwordResponse.redirect);
+ }
+
+ return;
+ }
+
+ const { errors } = formState;
+
+ const watchPassword = watch("password", "");
+ const watchConfirmPassword = watch("confirmPassword", "");
+
+ const hasMinLength =
+ passwordComplexitySettings &&
+ watchPassword?.length >= passwordComplexitySettings.minLength;
+ const hasSymbol = symbolValidator(watchPassword);
+ const hasNumber = numberValidator(watchPassword);
+ const hasUppercase = upperCaseValidator(watchPassword);
+ const hasLowercase = lowerCaseValidator(watchPassword);
+
+ const policyIsValid =
+ passwordComplexitySettings &&
+ (passwordComplexitySettings.requiresLowercase ? hasLowercase : true) &&
+ (passwordComplexitySettings.requiresNumber ? hasNumber : true) &&
+ (passwordComplexitySettings.requiresUppercase ? hasUppercase : true) &&
+ (passwordComplexitySettings.requiresSymbol ? hasSymbol : true) &&
+ hasMinLength;
+
+ return (
+
+ );
+}
diff --git a/login/apps/login/src/components/set-register-password-form.tsx b/login/apps/login/src/components/set-register-password-form.tsx
new file mode 100644
index 0000000000..7660e60753
--- /dev/null
+++ b/login/apps/login/src/components/set-register-password-form.tsx
@@ -0,0 +1,170 @@
+"use client";
+
+import {
+ lowerCaseValidator,
+ numberValidator,
+ symbolValidator,
+ upperCaseValidator,
+} from "@/helpers/validators";
+import { registerUser } from "@/lib/server/register";
+import { PasswordComplexitySettings } from "@zitadel/proto/zitadel/settings/v2/password_settings_pb";
+import { useRouter } from "next/navigation";
+import { useState } from "react";
+import { FieldValues, useForm } from "react-hook-form";
+import { Alert } from "./alert";
+import { BackButton } from "./back-button";
+import { Button, ButtonVariants } from "./button";
+import { TextInput } from "./input";
+import { PasswordComplexity } from "./password-complexity";
+import { Spinner } from "./spinner";
+import { Translated } from "./translated";
+
+type Inputs =
+ | {
+ password: string;
+ confirmPassword: string;
+ }
+ | FieldValues;
+
+type Props = {
+ passwordComplexitySettings: PasswordComplexitySettings;
+ email: string;
+ firstname: string;
+ lastname: string;
+ organization: string;
+ requestId?: string;
+};
+
+export function SetRegisterPasswordForm({
+ passwordComplexitySettings,
+ email,
+ firstname,
+ lastname,
+ organization,
+ requestId,
+}: Props) {
+ const { register, handleSubmit, watch, formState } = useForm({
+ mode: "onBlur",
+ defaultValues: {
+ email: email ?? "",
+ firstname: firstname ?? "",
+ lastname: lastname ?? "",
+ },
+ });
+
+ const [loading, setLoading] = useState(false);
+ const [error, setError] = useState("");
+
+ const router = useRouter();
+
+ async function submitRegister(values: Inputs) {
+ setLoading(true);
+ const response = await registerUser({
+ email: email,
+ firstName: firstname,
+ lastName: lastname,
+ organization: organization,
+ requestId: requestId,
+ password: values.password,
+ })
+ .catch(() => {
+ setError("Could not register user");
+ return;
+ })
+ .finally(() => {
+ setLoading(false);
+ });
+
+ if (response && "error" in response && response.error) {
+ setError(response.error);
+ return;
+ }
+
+ if (response && "redirect" in response && response.redirect) {
+ return router.push(response.redirect);
+ }
+ }
+
+ const { errors } = formState;
+
+ const watchPassword = watch("password", "");
+ const watchConfirmPassword = watch("confirmPassword", "");
+
+ const hasMinLength =
+ passwordComplexitySettings &&
+ watchPassword?.length >= passwordComplexitySettings.minLength;
+ const hasSymbol = symbolValidator(watchPassword);
+ const hasNumber = numberValidator(watchPassword);
+ const hasUppercase = upperCaseValidator(watchPassword);
+ const hasLowercase = lowerCaseValidator(watchPassword);
+
+ const policyIsValid =
+ passwordComplexitySettings &&
+ (passwordComplexitySettings.requiresLowercase ? hasLowercase : true) &&
+ (passwordComplexitySettings.requiresNumber ? hasNumber : true) &&
+ (passwordComplexitySettings.requiresUppercase ? hasUppercase : true) &&
+ (passwordComplexitySettings.requiresSymbol ? hasSymbol : true) &&
+ hasMinLength;
+
+ return (
+
+ );
+}
diff --git a/login/apps/login/src/components/sign-in-with-idp.tsx b/login/apps/login/src/components/sign-in-with-idp.tsx
new file mode 100644
index 0000000000..ec9cfb36f8
--- /dev/null
+++ b/login/apps/login/src/components/sign-in-with-idp.tsx
@@ -0,0 +1,93 @@
+"use client";
+
+import { idpTypeToSlug } from "@/lib/idp";
+import { redirectToIdp } from "@/lib/server/idp";
+import {
+ IdentityProvider,
+ IdentityProviderType,
+} from "@zitadel/proto/zitadel/settings/v2/login_settings_pb";
+import { ReactNode, useActionState } from "react";
+import { Alert } from "./alert";
+import { SignInWithIdentityProviderProps } from "./idps/base-button";
+import { SignInWithApple } from "./idps/sign-in-with-apple";
+import { SignInWithAzureAd } from "./idps/sign-in-with-azure-ad";
+import { SignInWithGeneric } from "./idps/sign-in-with-generic";
+import { SignInWithGithub } from "./idps/sign-in-with-github";
+import { SignInWithGitlab } from "./idps/sign-in-with-gitlab";
+import { SignInWithGoogle } from "./idps/sign-in-with-google";
+import { Translated } from "./translated";
+
+export interface SignInWithIDPProps {
+ children?: ReactNode;
+ identityProviders: IdentityProvider[];
+ requestId?: string;
+ organization?: string;
+ linkOnly?: boolean;
+}
+
+export function SignInWithIdp({
+ identityProviders,
+ requestId,
+ organization,
+ linkOnly,
+}: Readonly) {
+ const [state, action, _isPending] = useActionState(redirectToIdp, {});
+
+ const renderIDPButton = (idp: IdentityProvider, index: number) => {
+ const { id, name, type } = idp;
+
+ const components: Partial<
+ Record<
+ IdentityProviderType,
+ (props: SignInWithIdentityProviderProps) => ReactNode
+ >
+ > = {
+ [IdentityProviderType.APPLE]: SignInWithApple,
+ [IdentityProviderType.OAUTH]: SignInWithGeneric,
+ [IdentityProviderType.OIDC]: SignInWithGeneric,
+ [IdentityProviderType.GITHUB]: SignInWithGithub,
+ [IdentityProviderType.GITHUB_ES]: SignInWithGithub,
+ [IdentityProviderType.AZURE_AD]: SignInWithAzureAd,
+ [IdentityProviderType.GOOGLE]: (props) => (
+
+ ),
+ [IdentityProviderType.GITLAB]: SignInWithGitlab,
+ [IdentityProviderType.GITLAB_SELF_HOSTED]: SignInWithGitlab,
+ [IdentityProviderType.SAML]: SignInWithGeneric,
+ [IdentityProviderType.LDAP]: SignInWithGeneric,
+ [IdentityProviderType.JWT]: SignInWithGeneric,
+ };
+
+ const Component = components[type];
+ return Component ? (
+
+ ) : null;
+ };
+
+ return (
+
+
+
+
+ {!!identityProviders.length && identityProviders?.map(renderIDPButton)}
+ {state?.error && (
+
+ )}
+
+ );
+}
+
+SignInWithIdp.displayName = "SignInWithIDP";
diff --git a/login/apps/login/src/components/skeleton-card.tsx b/login/apps/login/src/components/skeleton-card.tsx
new file mode 100644
index 0000000000..80b3793e8f
--- /dev/null
+++ b/login/apps/login/src/components/skeleton-card.tsx
@@ -0,0 +1,16 @@
+import { clsx } from "clsx";
+
+export const SkeletonCard = ({ isLoading }: { isLoading?: boolean }) => (
+
+);
diff --git a/login/apps/login/src/components/skeleton.tsx b/login/apps/login/src/components/skeleton.tsx
new file mode 100644
index 0000000000..548953d278
--- /dev/null
+++ b/login/apps/login/src/components/skeleton.tsx
@@ -0,0 +1,9 @@
+import { ReactNode } from "react";
+
+export function Skeleton({ children }: { children?: ReactNode }) {
+ return (
+
+ {children}
+
+ );
+}
diff --git a/login/apps/login/src/components/spinner.tsx b/login/apps/login/src/components/spinner.tsx
new file mode 100644
index 0000000000..5ed2f04c80
--- /dev/null
+++ b/login/apps/login/src/components/spinner.tsx
@@ -0,0 +1,22 @@
+import { FC } from "react";
+
+export const Spinner: FC<{ className?: string }> = ({ className = "" }) => {
+ return (
+
+
+
+
+ );
+};
diff --git a/login/apps/login/src/components/state-badge.tsx b/login/apps/login/src/components/state-badge.tsx
new file mode 100644
index 0000000000..00151390bf
--- /dev/null
+++ b/login/apps/login/src/components/state-badge.tsx
@@ -0,0 +1,40 @@
+import { clsx } from "clsx";
+import { ReactNode } from "react";
+
+export enum BadgeState {
+ Info = "info",
+ Error = "error",
+ Success = "success",
+ Alert = "alert",
+}
+
+export type StateBadgeProps = {
+ state: BadgeState;
+ children: ReactNode;
+ evenPadding?: boolean;
+};
+
+const getBadgeClasses = (state: BadgeState, evenPadding: boolean) =>
+ clsx({
+ "w-fit border-box h-18.5px flex flex-row items-center whitespace-nowrap tracking-wider leading-4 items-center justify-center px-2 py-2px text-12px rounded-full shadow-sm":
+ true,
+ "bg-state-success-light-background text-state-success-light-color dark:bg-state-success-dark-background dark:text-state-success-dark-color ":
+ state === BadgeState.Success,
+ "bg-state-neutral-light-background text-state-neutral-light-color dark:bg-state-neutral-dark-background dark:text-state-neutral-dark-color":
+ state === BadgeState.Info,
+ "bg-state-error-light-background text-state-error-light-color dark:bg-state-error-dark-background dark:text-state-error-dark-color":
+ state === BadgeState.Error,
+ "bg-state-alert-light-background text-state-alert-light-color dark:bg-state-alert-dark-background dark:text-state-alert-dark-color":
+ state === BadgeState.Alert,
+ "p-[2px]": evenPadding,
+ });
+
+export function StateBadge({
+ state = BadgeState.Success,
+ evenPadding = false,
+ children,
+}: StateBadgeProps) {
+ return (
+ {children}
+ );
+}
diff --git a/login/apps/login/src/components/tab-group.tsx b/login/apps/login/src/components/tab-group.tsx
new file mode 100644
index 0000000000..afa625d345
--- /dev/null
+++ b/login/apps/login/src/components/tab-group.tsx
@@ -0,0 +1,16 @@
+import { Tab } from "@/components/tab";
+
+export type Item = {
+ text: string;
+ slug?: string;
+};
+
+export const TabGroup = ({ path, items }: { path: string; items: Item[] }) => {
+ return (
+
+ {items.map((item) => (
+
+ ))}
+
+ );
+};
diff --git a/login/apps/login/src/components/tab.tsx b/login/apps/login/src/components/tab.tsx
new file mode 100644
index 0000000000..bd82931b5a
--- /dev/null
+++ b/login/apps/login/src/components/tab.tsx
@@ -0,0 +1,35 @@
+"use client";
+
+import type { Item } from "@/components/tab-group";
+import { clsx } from "clsx";
+import Link from "next/link";
+import { useSelectedLayoutSegment } from "next/navigation";
+
+export const Tab = ({
+ path,
+ item: { slug, text },
+}: {
+ path: string;
+ item: Item;
+}) => {
+ const segment = useSelectedLayoutSegment();
+ const href = slug ? path + "/" + slug : path;
+ const isActive =
+ // Example home pages e.g. `/layouts`
+ (!slug && segment === null) ||
+ // Nested pages e.g. `/layouts/electronics`
+ segment === slug;
+
+ return (
+
+ {text}
+
+ );
+};
diff --git a/login/apps/login/src/components/theme-provider.tsx b/login/apps/login/src/components/theme-provider.tsx
new file mode 100644
index 0000000000..a8a72f86a6
--- /dev/null
+++ b/login/apps/login/src/components/theme-provider.tsx
@@ -0,0 +1,16 @@
+"use client";
+import { ThemeProvider as ThemeP } from "next-themes";
+import { ReactNode } from "react";
+
+export function ThemeProvider({ children }: { children: ReactNode }) {
+ return (
+
+ {children}
+
+ );
+}
diff --git a/login/apps/login/src/components/theme-wrapper.tsx b/login/apps/login/src/components/theme-wrapper.tsx
new file mode 100644
index 0000000000..314c3a2ef0
--- /dev/null
+++ b/login/apps/login/src/components/theme-wrapper.tsx
@@ -0,0 +1,18 @@
+"use client";
+
+import { setTheme } from "@/helpers/colors";
+import { BrandingSettings } from "@zitadel/proto/zitadel/settings/v2/branding_settings_pb";
+import { ReactNode, useEffect } from "react";
+
+type Props = {
+ branding: BrandingSettings | undefined;
+ children: ReactNode;
+};
+
+export const ThemeWrapper = ({ children, branding }: Props) => {
+ useEffect(() => {
+ setTheme(document, branding);
+ }, [branding]);
+
+ return {children}
;
+};
diff --git a/login/apps/login/src/components/theme.tsx b/login/apps/login/src/components/theme.tsx
new file mode 100644
index 0000000000..86d39476ff
--- /dev/null
+++ b/login/apps/login/src/components/theme.tsx
@@ -0,0 +1,44 @@
+"use client";
+
+import { MoonIcon, SunIcon } from "@heroicons/react/24/outline";
+import { useTheme } from "next-themes";
+import { useEffect, useState } from "react";
+
+export function Theme() {
+ const { resolvedTheme, setTheme } = useTheme();
+ const [mounted, setMounted] = useState(false);
+
+ const isDark = resolvedTheme === "dark";
+
+ // useEffect only runs on the client, so now we can safely show the UI
+ useEffect(() => {
+ setMounted(true);
+ }, []);
+
+ if (!mounted) {
+ return null;
+ }
+
+ return (
+
+ setTheme("dark")}
+ >
+
+
+ setTheme("light")}
+ >
+
+
+
+ );
+}
diff --git a/login/apps/login/src/components/totp-register.tsx b/login/apps/login/src/components/totp-register.tsx
new file mode 100644
index 0000000000..ea40fffbf0
--- /dev/null
+++ b/login/apps/login/src/components/totp-register.tsx
@@ -0,0 +1,157 @@
+"use client";
+
+import { getNextUrl } from "@/lib/client";
+import { verifyTOTP } from "@/lib/server/verify";
+import { LoginSettings } from "@zitadel/proto/zitadel/settings/v2/login_settings_pb";
+import Link from "next/link";
+import { useRouter } from "next/navigation";
+import { QRCodeSVG } from "qrcode.react";
+import { useState } from "react";
+import { useForm } from "react-hook-form";
+import { Alert } from "./alert";
+import { Button, ButtonVariants } from "./button";
+import { CopyToClipboard } from "./copy-to-clipboard";
+import { TextInput } from "./input";
+import { Spinner } from "./spinner";
+import { Translated } from "./translated";
+
+type Inputs = {
+ code: string;
+};
+
+type Props = {
+ uri: string;
+ secret: string;
+ loginName?: string;
+ sessionId?: string;
+ requestId?: string;
+ organization?: string;
+ checkAfter?: boolean;
+ loginSettings?: LoginSettings;
+};
+export function TotpRegister({
+ uri,
+ secret,
+ loginName,
+ sessionId,
+ requestId,
+ organization,
+ checkAfter,
+ loginSettings,
+}: Props) {
+ const [error, setError] = useState("");
+ const [loading, setLoading] = useState(false);
+ const router = useRouter();
+
+ const { register, handleSubmit, formState } = useForm({
+ mode: "onBlur",
+ defaultValues: {
+ code: "",
+ },
+ });
+
+ async function continueWithCode(values: Inputs) {
+ setLoading(true);
+ return verifyTOTP(values.code, loginName, organization)
+ .then(async () => {
+ // if attribute is set, validate MFA after it is setup, otherwise proceed as usual (when mfa is enforced to login)
+ if (checkAfter) {
+ const params = new URLSearchParams({});
+
+ if (loginName) {
+ params.append("loginName", loginName);
+ }
+ if (requestId) {
+ params.append("requestId", requestId);
+ }
+ if (organization) {
+ params.append("organization", organization);
+ }
+
+ return router.push(`/otp/time-based?` + params);
+ } else {
+ const url =
+ requestId && sessionId
+ ? await getNextUrl(
+ {
+ sessionId: sessionId,
+ requestId: requestId,
+ organization: organization,
+ },
+ loginSettings?.defaultRedirectUri,
+ )
+ : loginName
+ ? await getNextUrl(
+ {
+ loginName: loginName,
+ organization: organization,
+ },
+ loginSettings?.defaultRedirectUri,
+ )
+ : null;
+
+ if (url) {
+ return router.push(url);
+ }
+ }
+ })
+ .catch((e) => {
+ setError(e.message);
+ return;
+ })
+ .finally(() => {
+ setLoading(false);
+ });
+ }
+
+ return (
+
+ {uri && (
+ <>
+
+
+
+ {uri}
+
+
+
+
+
+ >
+ )}
+
+ );
+}
diff --git a/login/apps/login/src/components/translated.tsx b/login/apps/login/src/components/translated.tsx
new file mode 100644
index 0000000000..807ea18e8f
--- /dev/null
+++ b/login/apps/login/src/components/translated.tsx
@@ -0,0 +1,23 @@
+import { useTranslations } from "next-intl";
+
+export function Translated({
+ i18nKey,
+ children,
+ namespace,
+ data,
+ ...props
+}: {
+ i18nKey: string;
+ children?: React.ReactNode;
+ namespace?: string;
+ data?: any;
+} & React.HTMLAttributes) {
+ const t = useTranslations(namespace);
+ const helperKey = `${namespace ? `${namespace}.` : ""}${i18nKey}`;
+
+ return (
+
+ {t(i18nKey, data)}
+
+ );
+}
diff --git a/login/apps/login/src/components/user-avatar.tsx b/login/apps/login/src/components/user-avatar.tsx
new file mode 100644
index 0000000000..f2aa0bfed7
--- /dev/null
+++ b/login/apps/login/src/components/user-avatar.tsx
@@ -0,0 +1,59 @@
+import { Avatar } from "@/components/avatar";
+import { ChevronDownIcon } from "@heroicons/react/24/outline";
+import Link from "next/link";
+
+type Props = {
+ loginName?: string;
+ displayName?: string;
+ showDropdown: boolean;
+ searchParams?: Record;
+};
+
+export function UserAvatar({
+ loginName,
+ displayName,
+ showDropdown,
+ searchParams,
+}: Props) {
+ const params = new URLSearchParams({});
+
+ if (searchParams?.sessionId) {
+ params.set("sessionId", searchParams.sessionId);
+ }
+
+ if (searchParams?.organization) {
+ params.set("organization", searchParams.organization);
+ }
+
+ if (searchParams?.requestId) {
+ params.set("requestId", searchParams.requestId);
+ }
+
+ if (searchParams?.loginName) {
+ params.set("loginName", searchParams.loginName);
+ }
+
+ return (
+
+
+
+ {loginName}
+
+
+ {showDropdown && (
+
+
+
+ )}
+
+ );
+}
diff --git a/login/apps/login/src/components/username-form.tsx b/login/apps/login/src/components/username-form.tsx
new file mode 100644
index 0000000000..1dffade4b5
--- /dev/null
+++ b/login/apps/login/src/components/username-form.tsx
@@ -0,0 +1,156 @@
+"use client";
+
+import { sendLoginname } from "@/lib/server/loginname";
+import { LoginSettings } from "@zitadel/proto/zitadel/settings/v2/login_settings_pb";
+import { useRouter } from "next/navigation";
+import { ReactNode, useEffect, useState } from "react";
+import { useForm } from "react-hook-form";
+import { Alert } from "./alert";
+import { BackButton } from "./back-button";
+import { Button, ButtonVariants } from "./button";
+import { TextInput } from "./input";
+import { Spinner } from "./spinner";
+import { Translated } from "./translated";
+
+type Inputs = {
+ loginName: string;
+};
+
+type Props = {
+ loginName: string | undefined;
+ requestId: string | undefined;
+ loginSettings: LoginSettings | undefined;
+ organization?: string;
+ suffix?: string;
+ submit: boolean;
+ allowRegister: boolean;
+ children?: ReactNode;
+};
+
+export function UsernameForm({
+ loginName,
+ requestId,
+ organization,
+ suffix,
+ loginSettings,
+ submit,
+ allowRegister,
+ children,
+}: Props) {
+ const { register, handleSubmit, formState } = useForm({
+ mode: "onBlur",
+ defaultValues: {
+ loginName: loginName ? loginName : "",
+ },
+ });
+
+ const router = useRouter();
+
+ const [loading, setLoading] = useState(false);
+ const [error, setError] = useState("");
+
+ async function submitLoginName(values: Inputs, organization?: string) {
+ setLoading(true);
+
+ const res = await sendLoginname({
+ loginName: values.loginName,
+ organization,
+ requestId,
+ suffix,
+ })
+ .catch(() => {
+ setError("An internal error occurred");
+ return;
+ })
+ .finally(() => {
+ setLoading(false);
+ });
+
+ if (res && "redirect" in res && res.redirect) {
+ return router.push(res.redirect);
+ }
+
+ if (res && "error" in res && res.error) {
+ setError(res.error);
+ return;
+ }
+
+ return res;
+ }
+
+ useEffect(() => {
+ if (submit && loginName) {
+ // When we navigate to this page, we always want to be redirected if submit is true and the parameters are valid.
+ submitLoginName({ loginName }, organization);
+ }
+ }, []);
+
+ let inputLabel = "Loginname";
+ if (
+ loginSettings?.disableLoginWithEmail &&
+ loginSettings?.disableLoginWithPhone
+ ) {
+ inputLabel = "Username";
+ } else if (loginSettings?.disableLoginWithEmail) {
+ inputLabel = "Username or phone number";
+ } else if (loginSettings?.disableLoginWithPhone) {
+ inputLabel = "Username or email";
+ }
+
+ return (
+
+ );
+}
diff --git a/login/apps/login/src/components/verify-form.tsx b/login/apps/login/src/components/verify-form.tsx
new file mode 100644
index 0000000000..dac4c91314
--- /dev/null
+++ b/login/apps/login/src/components/verify-form.tsx
@@ -0,0 +1,168 @@
+"use client";
+
+import { Alert, AlertType } from "@/components/alert";
+import { resendVerification, sendVerification } from "@/lib/server/verify";
+import { useRouter } from "next/navigation";
+import { useCallback, useEffect, useState } from "react";
+import { useForm } from "react-hook-form";
+import { BackButton } from "./back-button";
+import { Button, ButtonVariants } from "./button";
+import { TextInput } from "./input";
+import { Spinner } from "./spinner";
+import { Translated } from "./translated";
+
+type Inputs = {
+ code: string;
+};
+
+type Props = {
+ userId: string;
+ loginName?: string;
+ organization?: string;
+ code?: string;
+ isInvite: boolean;
+ requestId?: string;
+};
+
+export function VerifyForm({
+ userId,
+ loginName,
+ organization,
+ requestId,
+ code,
+ isInvite,
+}: Props) {
+ const router = useRouter();
+
+ const { register, handleSubmit, formState } = useForm({
+ mode: "onBlur",
+ defaultValues: {
+ code: code ?? "",
+ },
+ });
+
+ const [error, setError] = useState("");
+
+ const [loading, setLoading] = useState(false);
+
+ async function resendCode() {
+ setError("");
+ setLoading(true);
+
+ const response = await resendVerification({
+ userId,
+ isInvite: isInvite,
+ })
+ .catch(() => {
+ setError("Could not resend email");
+ return;
+ })
+ .finally(() => {
+ setLoading(false);
+ });
+
+ if (response && "error" in response && response?.error) {
+ setError(response.error);
+ return;
+ }
+
+ return response;
+ }
+
+ const fcn = useCallback(
+ async function submitCodeAndContinue(
+ value: Inputs,
+ ): Promise {
+ setLoading(true);
+
+ const response = await sendVerification({
+ code: value.code,
+ userId,
+ isInvite: isInvite,
+ loginName: loginName,
+ organization: organization,
+ requestId: requestId,
+ })
+ .catch(() => {
+ setError("Could not verify user");
+ return;
+ })
+ .finally(() => {
+ setLoading(false);
+ });
+
+ if (response && "error" in response && response?.error) {
+ setError(response.error);
+ return;
+ }
+
+ if (response && "redirect" in response && response?.redirect) {
+ return router.push(response?.redirect);
+ }
+ },
+ [isInvite, userId],
+ );
+
+ useEffect(() => {
+ if (code) {
+ fcn({ code });
+ }
+ }, [code, fcn]);
+
+ return (
+ <>
+
+ >
+ );
+}
diff --git a/login/apps/login/src/components/zitadel-logo-dark.tsx b/login/apps/login/src/components/zitadel-logo-dark.tsx
new file mode 100644
index 0000000000..0df6ae2004
--- /dev/null
+++ b/login/apps/login/src/components/zitadel-logo-dark.tsx
@@ -0,0 +1,210 @@
+import { FC } from "react";
+
+export const ZitadelLogoDark: FC = (props) => (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+);
diff --git a/login/apps/login/src/components/zitadel-logo-light.tsx b/login/apps/login/src/components/zitadel-logo-light.tsx
new file mode 100644
index 0000000000..51529aa821
--- /dev/null
+++ b/login/apps/login/src/components/zitadel-logo-light.tsx
@@ -0,0 +1,210 @@
+import { FC } from "react";
+
+export const ZitadelLogoLight: FC = (props) => (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+);
diff --git a/login/apps/login/src/components/zitadel-logo.tsx b/login/apps/login/src/components/zitadel-logo.tsx
new file mode 100644
index 0000000000..105665fbba
--- /dev/null
+++ b/login/apps/login/src/components/zitadel-logo.tsx
@@ -0,0 +1,32 @@
+import Image from "next/image";
+type Props = {
+ height?: number;
+ width?: number;
+};
+
+export function ZitadelLogo({ height = 40, width = 147.5 }: Props) {
+ return (
+ <>
+
+ {/* */}
+
+
+
+
+
+
+ >
+ );
+}
diff --git a/login/apps/login/src/helpers/base64.ts b/login/apps/login/src/helpers/base64.ts
new file mode 100644
index 0000000000..967cdc8d17
--- /dev/null
+++ b/login/apps/login/src/helpers/base64.ts
@@ -0,0 +1,63 @@
+export function coerceToBase64Url(thing: any, name: string) {
+ // Array or ArrayBuffer to Uint8Array
+ if (Array.isArray(thing)) {
+ thing = Uint8Array.from(thing);
+ }
+
+ if (thing instanceof ArrayBuffer) {
+ thing = new Uint8Array(thing);
+ }
+
+ // Uint8Array to base64
+ if (thing instanceof Uint8Array) {
+ var str = "";
+ var len = thing.byteLength;
+
+ for (var i = 0; i < len; i++) {
+ str += String.fromCharCode(thing[i]);
+ }
+ thing = window.btoa(str);
+ }
+
+ if (typeof thing !== "string") {
+ throw new Error("could not coerce '" + name + "' to string");
+ }
+
+ // base64 to base64url
+ // NOTE: "=" at the end of challenge is optional, strip it off here
+ thing = thing.replace(/\+/g, "-").replace(/\//g, "_").replace(/=*$/g, "");
+
+ return thing;
+}
+
+export function coerceToArrayBuffer(thing: any, name: string) {
+ if (typeof thing === "string") {
+ // base64url to base64
+ thing = thing.replace(/-/g, "+").replace(/_/g, "/");
+
+ // base64 to Uint8Array
+ var str = window.atob(thing);
+ var bytes = new Uint8Array(str.length);
+ for (var i = 0; i < str.length; i++) {
+ bytes[i] = str.charCodeAt(i);
+ }
+ thing = bytes;
+ }
+
+ // Array to Uint8Array
+ if (Array.isArray(thing)) {
+ thing = new Uint8Array(thing);
+ }
+
+ // Uint8Array to ArrayBuffer
+ if (thing instanceof Uint8Array) {
+ thing = thing.buffer;
+ }
+
+ // error if none of the above worked
+ if (!(thing instanceof ArrayBuffer)) {
+ throw new TypeError("could not coerce '" + name + "' to ArrayBuffer");
+ }
+
+ return thing;
+}
diff --git a/login/apps/login/src/helpers/colors.ts b/login/apps/login/src/helpers/colors.ts
new file mode 100644
index 0000000000..bdb07cecfd
--- /dev/null
+++ b/login/apps/login/src/helpers/colors.ts
@@ -0,0 +1,439 @@
+import { BrandingSettings } from "@zitadel/proto/zitadel/settings/v2/branding_settings_pb";
+import tinycolor from "tinycolor2";
+
+export interface Color {
+ name: string;
+ hex: string;
+ rgb: string;
+ contrastColor: string;
+}
+
+export type MapName = "background" | "primary" | "warn" | "text" | "link";
+
+export type ColorName =
+ | "50"
+ | "100"
+ | "200"
+ | "300"
+ | "400"
+ | "500"
+ | "600"
+ | "700"
+ | "800"
+ | "C900"
+ | "A100"
+ | "A200"
+ | "A400"
+ | "A700";
+
+export type ColorMap = {
+ [key in MapName]: Color[];
+};
+
+export const DARK_PRIMARY = "#2073c4";
+export const PRIMARY = "#5469d4";
+
+export const DARK_WARN = "#ff3b5b";
+export const WARN = "#cd3d56";
+
+export const DARK_BACKGROUND = "#111827";
+export const BACKGROUND = "#fafafa";
+
+export const DARK_TEXT = "#ffffff";
+export const TEXT = "#000000";
+
+export type LabelPolicyColors = {
+ backgroundColor: string;
+ backgroundColorDark: string;
+ fontColor: string;
+ fontColorDark: string;
+ warnColor: string;
+ warnColorDark: string;
+ primaryColor: string;
+ primaryColorDark: string;
+};
+
+type BrandingColors = {
+ lightTheme: {
+ backgroundColor: string;
+ fontColor: string;
+ primaryColor: string;
+ warnColor: string;
+ };
+ darkTheme: {
+ backgroundColor: string;
+ fontColor: string;
+ primaryColor: string;
+ warnColor: string;
+ };
+};
+
+export function setTheme(document: any, policy?: BrandingSettings) {
+ const lP: BrandingColors = {
+ lightTheme: {
+ backgroundColor: policy?.lightTheme?.backgroundColor || BACKGROUND,
+ fontColor: policy?.lightTheme?.fontColor || TEXT,
+ primaryColor: policy?.lightTheme?.primaryColor || PRIMARY,
+ warnColor: policy?.lightTheme?.warnColor || WARN,
+ },
+ darkTheme: {
+ backgroundColor: policy?.darkTheme?.backgroundColor || DARK_BACKGROUND,
+ fontColor: policy?.darkTheme?.fontColor || DARK_TEXT,
+ primaryColor: policy?.darkTheme?.primaryColor || DARK_PRIMARY,
+ warnColor: policy?.darkTheme?.warnColor || DARK_WARN,
+ },
+ };
+
+ const dark = computeMap(lP, true);
+ const light = computeMap(lP, false);
+
+ setColorShades(dark.background, "background", "dark", document);
+ setColorShades(light.background, "background", "light", document);
+
+ setColorShades(dark.primary, "primary", "dark", document);
+ setColorShades(light.primary, "primary", "light", document);
+
+ setColorShades(dark.warn, "warn", "dark", document);
+ setColorShades(light.warn, "warn", "light", document);
+
+ setColorAlpha(dark.text, "text", "dark", document);
+ setColorAlpha(light.text, "text", "light", document);
+
+ setColorAlpha(dark.link, "link", "dark", document);
+ setColorAlpha(light.link, "link", "light", document);
+}
+
+function setColorShades(
+ map: Color[],
+ type: string,
+ theme: string,
+ document: any,
+) {
+ map.forEach((color) => {
+ document.documentElement.style.setProperty(
+ `--theme-${theme}-${type}-${color.name}`,
+ color.hex,
+ );
+ document.documentElement.style.setProperty(
+ `--theme-${theme}-${type}-contrast-${color.name}`,
+ color.contrastColor,
+ );
+ });
+}
+
+function setColorAlpha(
+ map: Color[],
+ type: string,
+ theme: string,
+ document: any,
+) {
+ map.forEach((color) => {
+ document.documentElement.style.setProperty(
+ `--theme-${theme}-${type}-${color.name}`,
+ color.hex,
+ );
+ document.documentElement.style.setProperty(
+ `--theme-${theme}-${type}-contrast-${color.name}`,
+ color.contrastColor,
+ );
+ document.documentElement.style.setProperty(
+ `--theme-${theme}-${type}-secondary-${color.name}`,
+ `${color.hex}c7`,
+ );
+ });
+}
+
+function computeColors(hex: string): Color[] {
+ return [
+ getColorObject(tinycolor(hex).lighten(52), "50"),
+ getColorObject(tinycolor(hex).lighten(37), "100"),
+ getColorObject(tinycolor(hex).lighten(26), "200"),
+ getColorObject(tinycolor(hex).lighten(12), "300"),
+ getColorObject(tinycolor(hex).lighten(6), "400"),
+ getColorObject(tinycolor(hex), "500"),
+ getColorObject(tinycolor(hex).darken(6), "600"),
+ getColorObject(tinycolor(hex).darken(12), "700"),
+ getColorObject(tinycolor(hex).darken(18), "800"),
+ getColorObject(tinycolor(hex).darken(24), "900"),
+ getColorObject(tinycolor(hex).lighten(50).saturate(30), "A100"),
+ getColorObject(tinycolor(hex).lighten(30).saturate(30), "A200"),
+ getColorObject(tinycolor(hex).lighten(10).saturate(15), "A400"),
+ getColorObject(tinycolor(hex).lighten(5).saturate(5), "A700"),
+ ];
+}
+
+function getColorObject(value: any, name: string): Color {
+ const c = tinycolor(value);
+ return {
+ name: name,
+ hex: c.toHexString(),
+ rgb: c.toRgbString(),
+ contrastColor: getContrast(c.toHexString()),
+ } as Color;
+}
+
+function getContrast(color: string): string {
+ const onBlack = tinycolor.readability("#000", color);
+ const onWhite = tinycolor.readability("#fff", color);
+ if (onBlack > onWhite) {
+ return "hsla(0, 0%, 0%, 0.87)";
+ } else {
+ return "#ffffff";
+ }
+}
+
+export function computeMap(branding: BrandingColors, dark: boolean): ColorMap {
+ return {
+ background: computeColors(
+ dark
+ ? branding.darkTheme.backgroundColor
+ : branding.lightTheme.backgroundColor,
+ ),
+ primary: computeColors(
+ dark ? branding.darkTheme.primaryColor : branding.lightTheme.primaryColor,
+ ),
+ warn: computeColors(
+ dark ? branding.darkTheme.warnColor : branding.lightTheme.warnColor,
+ ),
+ text: computeColors(
+ dark ? branding.darkTheme.fontColor : branding.lightTheme.fontColor,
+ ),
+ link: computeColors(
+ dark ? branding.darkTheme.fontColor : branding.lightTheme.fontColor,
+ ),
+ };
+}
+
+export interface ColorShade {
+ 200: string;
+ 300: string;
+ 500: string;
+ 600: string;
+ 700: string;
+ 900: string;
+}
+
+export const COLORS = [
+ {
+ 500: "#ef4444",
+ 200: "#fecaca",
+ 300: "#fca5a5",
+ 600: "#dc2626",
+ 700: "#b91c1c",
+ 900: "#7f1d1d",
+ },
+ {
+ 500: "#f97316",
+ 200: "#fed7aa",
+ 300: "#fdba74",
+ 600: "#ea580c",
+ 700: "#c2410c",
+ 900: "#7c2d12",
+ },
+ {
+ 500: "#f59e0b",
+ 200: "#fde68a",
+ 300: "#fcd34d",
+ 600: "#d97706",
+ 700: "#b45309",
+ 900: "#78350f",
+ },
+ {
+ 500: "#eab308",
+ 200: "#fef08a",
+ 300: "#fde047",
+ 600: "#ca8a04",
+ 700: "#a16207",
+ 900: "#713f12",
+ },
+ {
+ 500: "#84cc16",
+ 200: "#d9f99d",
+ 300: "#bef264",
+ 600: "#65a30d",
+ 700: "#4d7c0f",
+ 900: "#365314",
+ },
+ {
+ 500: "#22c55e",
+ 200: "#bbf7d0",
+ 300: "#86efac",
+ 600: "#16a34a",
+ 700: "#15803d",
+ 900: "#14532d",
+ },
+ {
+ 500: "#10b981",
+ 200: "#a7f3d0",
+ 300: "#6ee7b7",
+ 600: "#059669",
+ 700: "#047857",
+ 900: "#064e3b",
+ },
+ {
+ 500: "#14b8a6",
+ 200: "#99f6e4",
+ 300: "#5eead4",
+ 600: "#0d9488",
+ 700: "#0f766e",
+ 900: "#134e4a",
+ },
+ {
+ 500: "#06b6d4",
+ 200: "#a5f3fc",
+ 300: "#67e8f9",
+ 600: "#0891b2",
+ 700: "#0e7490",
+ 900: "#164e63",
+ },
+ {
+ 500: "#0ea5e9",
+ 200: "#bae6fd",
+ 300: "#7dd3fc",
+ 600: "#0284c7",
+ 700: "#0369a1",
+ 900: "#0c4a6e",
+ },
+ {
+ 500: "#3b82f6",
+ 200: "#bfdbfe",
+ 300: "#93c5fd",
+ 600: "#2563eb",
+ 700: "#1d4ed8",
+ 900: "#1e3a8a",
+ },
+ {
+ 500: "#6366f1",
+ 200: "#c7d2fe",
+ 300: "#a5b4fc",
+ 600: "#4f46e5",
+ 700: "#4338ca",
+ 900: "#312e81",
+ },
+ {
+ 500: "#8b5cf6",
+ 200: "#ddd6fe",
+ 300: "#c4b5fd",
+ 600: "#7c3aed",
+ 700: "#6d28d9",
+ 900: "#4c1d95",
+ },
+ {
+ 500: "#a855f7",
+ 200: "#e9d5ff",
+ 300: "#d8b4fe",
+ 600: "#9333ea",
+ 700: "#7e22ce",
+ 900: "#581c87",
+ },
+ {
+ 500: "#d946ef",
+ 200: "#f5d0fe",
+ 300: "#f0abfc",
+ 600: "#c026d3",
+ 700: "#a21caf",
+ 900: "#701a75",
+ },
+ {
+ 500: "#ec4899",
+ 200: "#fbcfe8",
+ 300: "#f9a8d4",
+ 600: "#db2777",
+ 700: "#be185d",
+ 900: "#831843",
+ },
+ {
+ 500: "#f43f5e",
+ 200: "#fecdd3",
+ 300: "#fda4af",
+ 600: "#e11d48",
+ 700: "#be123c",
+ 900: "#881337",
+ },
+];
+
+export function getColorHash(value: string): ColorShade {
+ let hash = 0;
+
+ if (value.length === 0) {
+ return COLORS[hash];
+ }
+
+ hash = hashCode(value);
+ return COLORS[hash % COLORS.length];
+}
+
+export function hashCode(str: string, seed = 0): number {
+ let h1 = 0xdeadbeef ^ seed,
+ h2 = 0x41c6ce57 ^ seed;
+ for (let i = 0, ch; i < str.length; i++) {
+ ch = str.charCodeAt(i);
+ h1 = Math.imul(h1 ^ ch, 2654435761);
+ h2 = Math.imul(h2 ^ ch, 1597334677);
+ }
+ h1 =
+ Math.imul(h1 ^ (h1 >>> 16), 2246822507) ^
+ Math.imul(h2 ^ (h2 >>> 13), 3266489909);
+ h2 =
+ Math.imul(h2 ^ (h2 >>> 16), 2246822507) ^
+ Math.imul(h1 ^ (h1 >>> 13), 3266489909);
+ return 4294967296 * (2097151 & h2) + (h1 >>> 0);
+}
+
+export function getMembershipColor(role: string): ColorShade {
+ const hash = hashCode(role);
+ let color = COLORS[hash % COLORS.length];
+
+ switch (role) {
+ case "IAM_OWNER":
+ color = COLORS[0];
+ break;
+ case "IAM_OWNER_VIEWER":
+ color = COLORS[14];
+ break;
+ case "IAM_ORG_MANAGER":
+ color = COLORS[11];
+ break;
+ case "IAM_USER_MANAGER":
+ color = COLORS[8];
+ break;
+
+ case "ORG_OWNER":
+ color = COLORS[16];
+ break;
+ case "ORG_USER_MANAGER":
+ color = COLORS[8];
+ break;
+ case "ORG_OWNER_VIEWER":
+ color = COLORS[14];
+ break;
+ case "ORG_USER_PERMISSION_EDITOR":
+ color = COLORS[7];
+ break;
+ case "ORG_PROJECT_PERMISSION_EDITOR":
+ color = COLORS[11];
+ break;
+ case "ORG_PROJECT_CREATOR":
+ color = COLORS[12];
+ break;
+
+ case "PROJECT_OWNER":
+ color = COLORS[9];
+ break;
+ case "PROJECT_OWNER_VIEWER":
+ color = COLORS[10];
+ break;
+ case "PROJECT_OWNER_GLOBAL":
+ color = COLORS[11];
+ break;
+ case "PROJECT_OWNER_VIEWER_GLOBAL":
+ color = COLORS[12];
+ break;
+
+ default:
+ color = COLORS[hash % COLORS.length];
+ break;
+ }
+
+ return color;
+}
diff --git a/login/apps/login/src/helpers/validators.ts b/login/apps/login/src/helpers/validators.ts
new file mode 100644
index 0000000000..6a61d13ece
--- /dev/null
+++ b/login/apps/login/src/helpers/validators.ts
@@ -0,0 +1,19 @@
+export function symbolValidator(value: string): boolean {
+ const REGEXP = /[^a-zA-Z0-9]/gi;
+ return REGEXP.test(value);
+}
+
+export function numberValidator(value: string): boolean {
+ const REGEXP = /[0-9]/g;
+ return REGEXP.test(value);
+}
+
+export function upperCaseValidator(value: string): boolean {
+ const REGEXP = /[A-Z]/g;
+ return REGEXP.test(value);
+}
+
+export function lowerCaseValidator(value: string): boolean {
+ const REGEXP = /[a-z]/g;
+ return REGEXP.test(value);
+}
diff --git a/login/apps/login/src/i18n/request.ts b/login/apps/login/src/i18n/request.ts
new file mode 100644
index 0000000000..15cfe01548
--- /dev/null
+++ b/login/apps/login/src/i18n/request.ts
@@ -0,0 +1,59 @@
+import { LANGS, LANGUAGE_COOKIE_NAME, LANGUAGE_HEADER_NAME } from "@/lib/i18n";
+import { getServiceUrlFromHeaders } from "@/lib/service-url";
+import { getHostedLoginTranslation } from "@/lib/zitadel";
+import { JsonObject } from "@zitadel/client";
+import deepmerge from "deepmerge";
+import { getRequestConfig } from "next-intl/server";
+import { cookies, headers } from "next/headers";
+
+export default getRequestConfig(async () => {
+ const fallback = "en";
+ const cookiesList = await cookies();
+
+ let locale: string = fallback;
+
+ const _headers = await headers();
+ const { serviceUrl } = getServiceUrlFromHeaders(_headers);
+
+ const languageHeader = await (await headers()).get(LANGUAGE_HEADER_NAME);
+ if (languageHeader) {
+ const headerLocale = languageHeader.split(",")[0].split("-")[0]; // Extract the language code
+ if (LANGS.map((l) => l.code).includes(headerLocale)) {
+ locale = headerLocale;
+ }
+ }
+
+ const languageCookie = cookiesList?.get(LANGUAGE_COOKIE_NAME);
+ if (languageCookie && languageCookie.value) {
+ if (LANGS.map((l) => l.code).includes(languageCookie.value)) {
+ locale = languageCookie.value;
+ }
+ }
+
+ const i18nOrganization = _headers.get("x-zitadel-i18n-organization") || ""; // You may need to set this header in middleware
+
+ let translations: JsonObject | {} = {};
+ try {
+ const i18nJSON = await getHostedLoginTranslation({
+ serviceUrl,
+ locale,
+ organization: i18nOrganization,
+ });
+
+ if (i18nJSON) {
+ translations = i18nJSON;
+ }
+ } catch (error) {
+ console.warn("Error fetching custom translations:", error);
+ }
+
+ const customMessages = translations;
+ const localeMessages = (await import(`../../locales/${locale}.json`)).default;
+ const fallbackMessages = (await import(`../../locales/${fallback}.json`))
+ .default;
+
+ return {
+ locale,
+ messages: deepmerge.all([fallbackMessages, localeMessages, customMessages]),
+ };
+});
diff --git a/login/apps/login/src/lib/api.ts b/login/apps/login/src/lib/api.ts
new file mode 100644
index 0000000000..7324007307
--- /dev/null
+++ b/login/apps/login/src/lib/api.ts
@@ -0,0 +1,17 @@
+import { newSystemToken } from "@zitadel/client/node";
+
+export async function systemAPIToken() {
+ const token = {
+ audience: process.env.AUDIENCE,
+ userID: process.env.SYSTEM_USER_ID,
+ token: Buffer.from(process.env.SYSTEM_USER_PRIVATE_KEY, "base64").toString(
+ "utf-8",
+ ),
+ };
+
+ return newSystemToken({
+ audience: token.audience,
+ subject: token.userID,
+ key: token.token,
+ });
+}
diff --git a/login/apps/login/src/lib/client.ts b/login/apps/login/src/lib/client.ts
new file mode 100644
index 0000000000..a59af90b77
--- /dev/null
+++ b/login/apps/login/src/lib/client.ts
@@ -0,0 +1,80 @@
+type FinishFlowCommand =
+ | {
+ sessionId: string;
+ requestId: string;
+ }
+ | { loginName: string };
+
+function goToSignedInPage(
+ props:
+ | { sessionId: string; organization?: string; requestId?: string }
+ | { organization?: string; loginName: string; requestId?: string },
+) {
+ const params = new URLSearchParams({});
+
+ if ("loginName" in props && props.loginName) {
+ params.append("loginName", props.loginName);
+ }
+
+ if ("sessionId" in props && props.sessionId) {
+ params.append("sessionId", props.sessionId);
+ }
+
+ if (props.organization) {
+ params.append("organization", props.organization);
+ }
+
+ // required to show conditional UI for device flow
+ if (props.requestId) {
+ params.append("requestId", props.requestId);
+ }
+
+ return `/signedin?` + params;
+}
+
+/**
+ * for client: redirects user back to an OIDC or SAML application or to a success page when using requestId, check if a default redirect and redirect to it, or just redirect to a success page with the loginName
+ * @param command
+ * @returns
+ */
+export async function getNextUrl(
+ command: FinishFlowCommand & { organization?: string },
+ defaultRedirectUri?: string,
+): Promise {
+ // finish Device Authorization Flow
+ if (
+ "requestId" in command &&
+ command.requestId.startsWith("device_") &&
+ ("loginName" in command || "sessionId" in command)
+ ) {
+ return goToSignedInPage({
+ ...command,
+ organization: command.organization,
+ });
+ }
+
+ // finish SAML or OIDC flow
+ if (
+ "sessionId" in command &&
+ "requestId" in command &&
+ (command.requestId.startsWith("saml_") ||
+ command.requestId.startsWith("oidc_"))
+ ) {
+ const params = new URLSearchParams({
+ sessionId: command.sessionId,
+ requestId: command.requestId,
+ });
+
+ if (command.organization) {
+ params.append("organization", command.organization);
+ }
+
+ return `/login?` + params;
+ }
+
+ if (defaultRedirectUri) {
+ return defaultRedirectUri;
+ }
+
+ return goToSignedInPage(command);
+}
diff --git a/login/apps/login/src/lib/cookies.ts b/login/apps/login/src/lib/cookies.ts
new file mode 100644
index 0000000000..7de87a98e7
--- /dev/null
+++ b/login/apps/login/src/lib/cookies.ts
@@ -0,0 +1,341 @@
+"use server";
+
+import { timestampDate, timestampFromMs } from "@zitadel/client";
+import { cookies } from "next/headers";
+import { LANGUAGE_COOKIE_NAME } from "./i18n";
+
+// TODO: improve this to handle overflow
+const MAX_COOKIE_SIZE = 2048;
+
+export type Cookie = {
+ id: string;
+ token: string;
+ loginName: string;
+ organization?: string;
+ creationTs: string;
+ expirationTs: string;
+ changeTs: string;
+ requestId?: string; // if its linked to an OIDC flow
+};
+
+type SessionCookie = Cookie & T;
+
+async function setSessionHttpOnlyCookie(
+ sessions: SessionCookie[],
+ sameSite: boolean | "lax" | "strict" | "none" = true,
+) {
+ const cookiesList = await cookies();
+
+ return cookiesList.set({
+ name: "sessions",
+ value: JSON.stringify(sessions),
+ httpOnly: true,
+ path: "/",
+ sameSite: process.env.NODE_ENV === "production" ? sameSite : "lax",
+ secure: process.env.NODE_ENV === "production",
+ });
+}
+
+export async function setLanguageCookie(language: string) {
+ const cookiesList = await cookies();
+
+ await cookiesList.set({
+ name: LANGUAGE_COOKIE_NAME,
+ value: language,
+ httpOnly: true,
+ path: "/",
+ });
+}
+
+export async function addSessionToCookie({
+ session,
+ cleanup,
+ sameSite,
+}: {
+ session: SessionCookie;
+ cleanup?: boolean;
+ sameSite?: boolean | "lax" | "strict" | "none" | undefined;
+}): Promise {
+ const cookiesList = await cookies();
+ const stringifiedCookie = cookiesList.get("sessions");
+
+ let currentSessions: SessionCookie[] = stringifiedCookie?.value
+ ? JSON.parse(stringifiedCookie?.value)
+ : [];
+
+ const index = currentSessions.findIndex(
+ (s) => s.loginName === session.loginName,
+ );
+
+ if (index > -1) {
+ currentSessions[index] = session;
+ } else {
+ const temp = [...currentSessions, session];
+
+ if (JSON.stringify(temp).length >= MAX_COOKIE_SIZE) {
+ console.log("WARNING COOKIE OVERFLOW");
+ // TODO: improve cookie handling
+ // this replaces the first session (oldest) with the new one
+ currentSessions = [session].concat(currentSessions.slice(1));
+ } else {
+ currentSessions = [session].concat(currentSessions);
+ }
+ }
+
+ if (cleanup) {
+ const now = new Date();
+ const filteredSessions = currentSessions.filter((session) =>
+ session.expirationTs
+ ? timestampDate(timestampFromMs(Number(session.expirationTs))) > now
+ : true,
+ );
+ return setSessionHttpOnlyCookie(filteredSessions, sameSite);
+ } else {
+ return setSessionHttpOnlyCookie(currentSessions, sameSite);
+ }
+}
+
+export async function updateSessionCookie({
+ id,
+ session,
+ cleanup,
+ sameSite,
+}: {
+ id: string;
+ session: SessionCookie;
+ cleanup?: boolean;
+ sameSite?: boolean | "lax" | "strict" | "none" | undefined;
+}): Promise {
+ const cookiesList = await cookies();
+ const stringifiedCookie = cookiesList.get("sessions");
+
+ const sessions: SessionCookie[] = stringifiedCookie?.value
+ ? JSON.parse(stringifiedCookie?.value)
+ : [session];
+
+ const foundIndex = sessions.findIndex((session) => session.id === id);
+
+ if (foundIndex > -1) {
+ sessions[foundIndex] = session;
+ if (cleanup) {
+ const now = new Date();
+ const filteredSessions = sessions.filter((session) =>
+ session.expirationTs
+ ? timestampDate(timestampFromMs(Number(session.expirationTs))) > now
+ : true,
+ );
+ return setSessionHttpOnlyCookie(filteredSessions, sameSite);
+ } else {
+ return setSessionHttpOnlyCookie(sessions, sameSite);
+ }
+ } else {
+ throw "updateSessionCookie: session id now found";
+ }
+}
+
+export async function removeSessionFromCookie({
+ session,
+ cleanup,
+ sameSite,
+}: {
+ session: SessionCookie;
+ cleanup?: boolean;
+ sameSite?: boolean | "lax" | "strict" | "none" | undefined;
+}) {
+ const cookiesList = await cookies();
+ const stringifiedCookie = cookiesList.get("sessions");
+
+ const sessions: SessionCookie[] = stringifiedCookie?.value
+ ? JSON.parse(stringifiedCookie?.value)
+ : [session];
+
+ const reducedSessions = sessions.filter((s) => s.id !== session.id);
+ if (cleanup) {
+ const now = new Date();
+ const filteredSessions = reducedSessions.filter((session) =>
+ session.expirationTs
+ ? timestampDate(timestampFromMs(Number(session.expirationTs))) > now
+ : true,
+ );
+ return setSessionHttpOnlyCookie(filteredSessions, sameSite);
+ } else {
+ return setSessionHttpOnlyCookie(reducedSessions, sameSite);
+ }
+}
+
+export async function getMostRecentSessionCookie(): Promise {
+ const cookiesList = await cookies();
+ const stringifiedCookie = cookiesList.get("sessions");
+
+ if (stringifiedCookie?.value) {
+ const sessions: SessionCookie[] = JSON.parse(stringifiedCookie?.value);
+
+ const latest = sessions.reduce((prev, current) => {
+ return prev.changeTs > current.changeTs ? prev : current;
+ });
+
+ return latest;
+ } else {
+ return Promise.reject("no session cookie found");
+ }
+}
+
+export async function getSessionCookieById({
+ sessionId,
+ organization,
+}: {
+ sessionId: string;
+ organization?: string;
+}): Promise> {
+ const cookiesList = await cookies();
+ const stringifiedCookie = cookiesList.get("sessions");
+
+ if (stringifiedCookie?.value) {
+ const sessions: SessionCookie[] = JSON.parse(stringifiedCookie?.value);
+
+ const found = sessions.find((s) =>
+ organization
+ ? s.organization === organization && s.id === sessionId
+ : s.id === sessionId,
+ );
+ if (found) {
+ return found;
+ } else {
+ return Promise.reject();
+ }
+ } else {
+ return Promise.reject();
+ }
+}
+
+export async function getSessionCookieByLoginName({
+ loginName,
+ organization,
+}: {
+ loginName?: string;
+ organization?: string;
+}): Promise> {
+ const cookiesList = await cookies();
+ const stringifiedCookie = cookiesList.get("sessions");
+
+ if (stringifiedCookie?.value) {
+ const sessions: SessionCookie[] = JSON.parse(stringifiedCookie?.value);
+ const found = sessions.find((s) =>
+ organization
+ ? s.organization === organization && s.loginName === loginName
+ : s.loginName === loginName,
+ );
+ if (found) {
+ return found;
+ } else {
+ return Promise.reject("no cookie found with loginName: " + loginName);
+ }
+ } else {
+ return Promise.reject("no session cookie found");
+ }
+}
+
+/**
+ *
+ * @param cleanup when true, removes all expired sessions, default true
+ * @returns Session Cookies
+ */
+export async function getAllSessionCookieIds(
+ cleanup: boolean = false,
+): Promise {
+ const cookiesList = await cookies();
+ const stringifiedCookie = cookiesList.get("sessions");
+
+ if (stringifiedCookie?.value) {
+ const sessions: SessionCookie[] = JSON.parse(stringifiedCookie?.value);
+
+ if (cleanup) {
+ const now = new Date();
+ return sessions
+ .filter((session) =>
+ session.expirationTs
+ ? timestampDate(timestampFromMs(Number(session.expirationTs))) > now
+ : true,
+ )
+ .map((session) => session.id);
+ } else {
+ return sessions.map((session) => session.id);
+ }
+ } else {
+ return [];
+ }
+}
+
+/**
+ *
+ * @param cleanup when true, removes all expired sessions, default true
+ * @returns Session Cookies
+ */
+export async function getAllSessions(
+ cleanup: boolean = false,
+): Promise[]> {
+ const cookiesList = await cookies();
+ const stringifiedCookie = cookiesList.get("sessions");
+
+ if (stringifiedCookie?.value) {
+ const sessions: SessionCookie[] = JSON.parse(stringifiedCookie?.value);
+
+ if (cleanup) {
+ const now = new Date();
+ return sessions.filter((session) =>
+ session.expirationTs
+ ? timestampDate(timestampFromMs(Number(session.expirationTs))) > now
+ : true,
+ );
+ } else {
+ return sessions;
+ }
+ } else {
+ return [];
+ }
+}
+
+/**
+ * Returns most recent session filtered by optinal loginName
+ * @param loginName optional loginName to filter cookies, if non provided, returns most recent session
+ * @param organization optional organization to filter cookies
+ * @returns most recent session
+ */
+export async function getMostRecentCookieWithLoginname({
+ loginName,
+ organization,
+}: {
+ loginName?: string;
+ organization?: string;
+}): Promise {
+ const cookiesList = await cookies();
+ const stringifiedCookie = cookiesList.get("sessions");
+
+ if (stringifiedCookie?.value) {
+ const sessions: SessionCookie[] = JSON.parse(stringifiedCookie?.value);
+ let filtered = sessions.filter((cookie) => {
+ return !!loginName ? cookie.loginName === loginName : true;
+ });
+
+ if (organization) {
+ filtered = filtered.filter((cookie) => {
+ return cookie.organization === organization;
+ });
+ }
+
+ const latest =
+ filtered && filtered.length
+ ? filtered.reduce((prev, current) => {
+ return prev.changeTs > current.changeTs ? prev : current;
+ })
+ : undefined;
+
+ if (latest) {
+ return latest;
+ } else {
+ return Promise.reject("Could not get the context or retrieve a session");
+ }
+ } else {
+ return Promise.reject("Could not read session cookie");
+ }
+}
diff --git a/login/apps/login/src/lib/demos.ts b/login/apps/login/src/lib/demos.ts
new file mode 100644
index 0000000000..38912e50e5
--- /dev/null
+++ b/login/apps/login/src/lib/demos.ts
@@ -0,0 +1,38 @@
+export type Item = {
+ name: string;
+ slug: string;
+ description?: string;
+};
+
+export const demos: { name: string; items: Item[] }[] = [
+ {
+ name: "Login",
+ items: [
+ {
+ name: "Loginname",
+ slug: "loginname",
+ description: "Start the loginflow with loginname",
+ },
+ {
+ name: "Accounts",
+ slug: "accounts",
+ description: "List active and inactive sessions",
+ },
+ ],
+ },
+ {
+ name: "Register",
+ items: [
+ {
+ name: "Register",
+ slug: "register",
+ description: "Add a user with password or passkey",
+ },
+ {
+ name: "IDP Register",
+ slug: "idp",
+ description: "Add a user from an external identity provider",
+ },
+ ],
+ },
+];
diff --git a/login/apps/login/src/lib/fingerprint.ts b/login/apps/login/src/lib/fingerprint.ts
new file mode 100644
index 0000000000..55b59dadc8
--- /dev/null
+++ b/login/apps/login/src/lib/fingerprint.ts
@@ -0,0 +1,66 @@
+import { create } from "@zitadel/client";
+import {
+ UserAgent,
+ UserAgentSchema,
+} from "@zitadel/proto/zitadel/session/v2/session_pb";
+import { cookies, headers } from "next/headers";
+import { userAgent } from "next/server";
+import { v4 as uuidv4 } from "uuid";
+
+export async function getFingerprintId() {
+ return uuidv4();
+}
+
+export async function setFingerprintIdCookie(fingerprintId: string) {
+ const cookiesList = await cookies();
+
+ return cookiesList.set({
+ name: "fingerprintId",
+ value: fingerprintId,
+ httpOnly: true,
+ path: "/",
+ maxAge: 31536000, // 1 year
+ });
+}
+
+export async function getFingerprintIdCookie() {
+ const cookiesList = await cookies();
+ return cookiesList.get("fingerprintId");
+}
+
+export async function getOrSetFingerprintId(): Promise {
+ const cookie = await getFingerprintIdCookie();
+ if (cookie) {
+ return cookie.value;
+ }
+
+ const fingerprintId = await getFingerprintId();
+ await setFingerprintIdCookie(fingerprintId);
+ return fingerprintId;
+}
+
+export async function getUserAgent(): Promise {
+ const _headers = await headers();
+
+ const fingerprintId = await getOrSetFingerprintId();
+
+ const { device, engine, os, browser } = userAgent({ headers: _headers });
+
+ const userAgentHeader = _headers.get("user-agent");
+
+ const userAgentHeaderValues = userAgentHeader?.split(",");
+
+ const deviceDescription = `${device?.type ? `${device.type},` : ""} ${device?.vendor ? `${device.vendor},` : ""} ${device.model ? `${device.model},` : ""} `;
+ const osDescription = `${os?.name ? `${os.name},` : ""} ${os?.version ? `${os.version},` : ""} `;
+ const engineDescription = `${engine?.name ? `${engine.name},` : ""} ${engine?.version ? `${engine.version},` : ""} `;
+ const browserDescription = `${browser?.name ? `${browser.name},` : ""} ${browser.version ? `${browser.version},` : ""} `;
+
+ const userAgentData: UserAgent = create(UserAgentSchema, {
+ ip: _headers.get("x-forwarded-for") ?? _headers.get("remoteAddress") ?? "",
+ header: { "user-agent": { values: userAgentHeaderValues } },
+ description: `${browserDescription}, ${deviceDescription}, ${engineDescription}, ${osDescription}`,
+ fingerprintId: fingerprintId,
+ });
+
+ return userAgentData;
+}
diff --git a/login/apps/login/src/lib/hooks.ts b/login/apps/login/src/lib/hooks.ts
new file mode 100644
index 0000000000..2d43fe5adc
--- /dev/null
+++ b/login/apps/login/src/lib/hooks.ts
@@ -0,0 +1,14 @@
+import { useEffect, useState } from "react";
+
+// Custom hook to read auth record and user profile doc
+export function useUserData() {
+ const [clientData, setClientData] = useState(null);
+
+ useEffect(() => {
+ let unsubscribe;
+
+ return unsubscribe;
+ }, [clientData]);
+
+ return { clientData };
+}
diff --git a/login/apps/login/src/lib/i18n.ts b/login/apps/login/src/lib/i18n.ts
new file mode 100644
index 0000000000..5a101dcc8f
--- /dev/null
+++ b/login/apps/login/src/lib/i18n.ts
@@ -0,0 +1,38 @@
+export interface Lang {
+ name: string;
+ code: string;
+}
+
+export const LANGS: Lang[] = [
+ {
+ name: "English",
+ code: "en",
+ },
+ {
+ name: "Deutsch",
+ code: "de",
+ },
+ {
+ name: "Italiano",
+ code: "it",
+ },
+ {
+ name: "Español",
+ code: "es",
+ },
+ {
+ name: "Polski",
+ code: "pl",
+ },
+ {
+ name: "简体中文",
+ code: "zh",
+ },
+ {
+ name: "Русский",
+ code: "ru",
+ },
+];
+
+export const LANGUAGE_COOKIE_NAME = "NEXT_LOCALE";
+export const LANGUAGE_HEADER_NAME = "accept-language";
diff --git a/login/apps/login/src/lib/idp.ts b/login/apps/login/src/lib/idp.ts
new file mode 100644
index 0000000000..d355f9ab56
--- /dev/null
+++ b/login/apps/login/src/lib/idp.ts
@@ -0,0 +1,77 @@
+import { IDPType } from "@zitadel/proto/zitadel/idp/v2/idp_pb";
+import { IdentityProviderType } from "@zitadel/proto/zitadel/settings/v2/login_settings_pb";
+
+// This maps the IdentityProviderType to a slug which is used in the /success and /failure routes
+export function idpTypeToSlug(idpType: IdentityProviderType) {
+ switch (idpType) {
+ case IdentityProviderType.GITHUB:
+ return "github";
+ case IdentityProviderType.GITHUB_ES:
+ return "github_es";
+ case IdentityProviderType.GITLAB:
+ return "gitlab";
+ case IdentityProviderType.GITLAB_SELF_HOSTED:
+ return "gitlab_es";
+ case IdentityProviderType.APPLE:
+ return "apple";
+ case IdentityProviderType.GOOGLE:
+ return "google";
+ case IdentityProviderType.AZURE_AD:
+ return "azure";
+ case IdentityProviderType.SAML:
+ return "saml";
+ case IdentityProviderType.OAUTH:
+ return "oauth";
+ case IdentityProviderType.OIDC:
+ return "oidc";
+ case IdentityProviderType.LDAP:
+ return "ldap";
+ case IdentityProviderType.JWT:
+ return "jwt";
+ default:
+ throw new Error("Unknown identity provider type");
+ }
+}
+
+// TODO: this is ugly but needed atm as the getIDPByID returns a IDPType and not a IdentityProviderType
+export function idpTypeToIdentityProviderType(
+ idpType: IDPType,
+): IdentityProviderType {
+ switch (idpType) {
+ case IDPType.IDP_TYPE_GITHUB:
+ return IdentityProviderType.GITHUB;
+
+ case IDPType.IDP_TYPE_GITHUB_ES:
+ return IdentityProviderType.GITHUB_ES;
+
+ case IDPType.IDP_TYPE_GITLAB:
+ return IdentityProviderType.GITLAB;
+
+ case IDPType.IDP_TYPE_GITLAB_SELF_HOSTED:
+ return IdentityProviderType.GITLAB_SELF_HOSTED;
+
+ case IDPType.IDP_TYPE_APPLE:
+ return IdentityProviderType.APPLE;
+
+ case IDPType.IDP_TYPE_GOOGLE:
+ return IdentityProviderType.GOOGLE;
+
+ case IDPType.IDP_TYPE_AZURE_AD:
+ return IdentityProviderType.AZURE_AD;
+
+ case IDPType.IDP_TYPE_SAML:
+ return IdentityProviderType.SAML;
+
+ case IDPType.IDP_TYPE_OAUTH:
+ return IdentityProviderType.OAUTH;
+
+ case IDPType.IDP_TYPE_OIDC:
+ return IdentityProviderType.OIDC;
+
+ case IDPType.IDP_TYPE_JWT:
+ return IdentityProviderType.JWT;
+
+ default:
+ throw new Error("Unknown identity provider type");
+ }
+}
diff --git a/login/apps/login/src/lib/oidc.ts b/login/apps/login/src/lib/oidc.ts
new file mode 100644
index 0000000000..b692300dea
--- /dev/null
+++ b/login/apps/login/src/lib/oidc.ts
@@ -0,0 +1,132 @@
+import { Cookie } from "@/lib/cookies";
+import { sendLoginname, SendLoginnameCommand } from "@/lib/server/loginname";
+import { createCallback, getLoginSettings } from "@/lib/zitadel";
+import { create } from "@zitadel/client";
+import {
+ CreateCallbackRequestSchema,
+ SessionSchema,
+} from "@zitadel/proto/zitadel/oidc/v2/oidc_service_pb";
+import { Session } from "@zitadel/proto/zitadel/session/v2/session_pb";
+import { NextRequest, NextResponse } from "next/server";
+import { constructUrl } from "./service-url";
+import { isSessionValid } from "./session";
+
+type LoginWithOIDCAndSession = {
+ serviceUrl: string;
+ authRequest: string;
+ sessionId: string;
+ sessions: Session[];
+ sessionCookies: Cookie[];
+ request: NextRequest;
+};
+export async function loginWithOIDCAndSession({
+ serviceUrl,
+ authRequest,
+ sessionId,
+ sessions,
+ sessionCookies,
+ request,
+}: LoginWithOIDCAndSession) {
+ console.log(
+ `Login with session: ${sessionId} and authRequest: ${authRequest}`,
+ );
+
+ const selectedSession = sessions.find((s) => s.id === sessionId);
+
+ if (selectedSession && selectedSession.id) {
+ console.log(`Found session ${selectedSession.id}`);
+
+ const isValid = await isSessionValid({
+ serviceUrl,
+ session: selectedSession,
+ });
+
+ console.log("Session is valid:", isValid);
+
+ if (!isValid && selectedSession.factors?.user) {
+ // if the session is not valid anymore, we need to redirect the user to re-authenticate /
+ // TODO: handle IDP intent direcly if available
+ const command: SendLoginnameCommand = {
+ loginName: selectedSession.factors.user?.loginName,
+ organization: selectedSession.factors?.user?.organizationId,
+ requestId: `oidc_${authRequest}`,
+ };
+
+ const res = await sendLoginname(command);
+
+ if (res && "redirect" in res && res?.redirect) {
+ const absoluteUrl = constructUrl(request, res.redirect);
+ return NextResponse.redirect(absoluteUrl.toString());
+ }
+ }
+
+ const cookie = sessionCookies.find(
+ (cookie) => cookie.id === selectedSession?.id,
+ );
+
+ if (cookie && cookie.id && cookie.token) {
+ const session = {
+ sessionId: cookie?.id,
+ sessionToken: cookie?.token,
+ };
+
+ // works not with _rsc request
+ try {
+ const { callbackUrl } = await createCallback({
+ serviceUrl,
+ req: create(CreateCallbackRequestSchema, {
+ authRequestId: authRequest,
+ callbackKind: {
+ case: "session",
+ value: create(SessionSchema, session),
+ },
+ }),
+ });
+ if (callbackUrl) {
+ return NextResponse.redirect(callbackUrl);
+ } else {
+ return NextResponse.json(
+ { error: "An error occurred!" },
+ { status: 500 },
+ );
+ }
+ } catch (error: unknown) {
+ // handle already handled gracefully as these could come up if old emails with requestId are used (reset password, register emails etc.)
+ console.error(error);
+ if (
+ error &&
+ typeof error === "object" &&
+ "code" in error &&
+ error?.code === 9
+ ) {
+ const loginSettings = await getLoginSettings({
+ serviceUrl,
+ organization: selectedSession.factors?.user?.organizationId,
+ });
+
+ if (loginSettings?.defaultRedirectUri) {
+ return NextResponse.redirect(loginSettings.defaultRedirectUri);
+ }
+
+ const signedinUrl = constructUrl(request, "/signedin");
+
+ if (selectedSession.factors?.user?.loginName) {
+ signedinUrl.searchParams.set(
+ "loginName",
+ selectedSession.factors?.user?.loginName,
+ );
+ }
+ if (selectedSession.factors?.user?.organizationId) {
+ signedinUrl.searchParams.set(
+ "organization",
+ selectedSession.factors?.user?.organizationId,
+ );
+ }
+ return NextResponse.redirect(signedinUrl);
+ } else {
+ return NextResponse.json({ error }, { status: 500 });
+ }
+ }
+ }
+ }
+}
diff --git a/login/apps/login/src/lib/saml.ts b/login/apps/login/src/lib/saml.ts
new file mode 100644
index 0000000000..e85084f022
--- /dev/null
+++ b/login/apps/login/src/lib/saml.ts
@@ -0,0 +1,130 @@
+import { Cookie } from "@/lib/cookies";
+import { sendLoginname, SendLoginnameCommand } from "@/lib/server/loginname";
+import { createResponse, getLoginSettings } from "@/lib/zitadel";
+import { create } from "@zitadel/client";
+import { CreateResponseRequestSchema } from "@zitadel/proto/zitadel/saml/v2/saml_service_pb";
+import { Session } from "@zitadel/proto/zitadel/session/v2/session_pb";
+import { NextRequest, NextResponse } from "next/server";
+import { constructUrl } from "./service-url";
+import { isSessionValid } from "./session";
+
+type LoginWithSAMLAndSession = {
+ serviceUrl: string;
+ samlRequest: string;
+ sessionId: string;
+ sessions: Session[];
+ sessionCookies: Cookie[];
+ request: NextRequest;
+};
+
+export async function loginWithSAMLAndSession({
+ serviceUrl,
+ samlRequest,
+ sessionId,
+ sessions,
+ sessionCookies,
+ request,
+}: LoginWithSAMLAndSession) {
+ console.log(
+ `Login with session: ${sessionId} and samlRequest: ${samlRequest}`,
+ );
+
+ const selectedSession = sessions.find((s) => s.id === sessionId);
+
+ if (selectedSession && selectedSession.id) {
+ console.log(`Found session ${selectedSession.id}`);
+
+ const isValid = await isSessionValid({
+ serviceUrl,
+ session: selectedSession,
+ });
+
+ console.log("Session is valid:", isValid);
+
+ if (!isValid && selectedSession.factors?.user) {
+ // if the session is not valid anymore, we need to redirect the user to re-authenticate /
+ // TODO: handle IDP intent direcly if available
+ const command: SendLoginnameCommand = {
+ loginName: selectedSession.factors.user?.loginName,
+ organization: selectedSession.factors?.user?.organizationId,
+ requestId: `saml_${samlRequest}`,
+ };
+
+ const res = await sendLoginname(command);
+
+ if (res && "redirect" in res && res?.redirect) {
+ const absoluteUrl = constructUrl(request, res.redirect);
+ return NextResponse.redirect(absoluteUrl.toString());
+ }
+ }
+
+ const cookie = sessionCookies.find(
+ (cookie) => cookie.id === selectedSession?.id,
+ );
+
+ if (cookie && cookie.id && cookie.token) {
+ const session = {
+ sessionId: cookie?.id,
+ sessionToken: cookie?.token,
+ };
+
+ // works not with _rsc request
+ try {
+ const { url } = await createResponse({
+ serviceUrl,
+ req: create(CreateResponseRequestSchema, {
+ samlRequestId: samlRequest,
+ responseKind: {
+ case: "session",
+ value: session,
+ },
+ }),
+ });
+ if (url) {
+ return NextResponse.redirect(url);
+ } else {
+ return NextResponse.json(
+ { error: "An error occurred!" },
+ { status: 500 },
+ );
+ }
+ } catch (error: unknown) {
+ // handle already handled gracefully as these could come up if old emails with requestId are used (reset password, register emails etc.)
+ console.error(error);
+ if (
+ error &&
+ typeof error === "object" &&
+ "code" in error &&
+ error?.code === 9
+ ) {
+ const loginSettings = await getLoginSettings({
+ serviceUrl,
+ organization: selectedSession.factors?.user?.organizationId,
+ });
+
+ if (loginSettings?.defaultRedirectUri) {
+ return NextResponse.redirect(loginSettings.defaultRedirectUri);
+ }
+
+ const signedinUrl = constructUrl(request, "/signedin");
+
+ if (selectedSession.factors?.user?.loginName) {
+ signedinUrl.searchParams.set(
+ "loginName",
+ selectedSession.factors?.user?.loginName,
+ );
+ }
+ if (selectedSession.factors?.user?.organizationId) {
+ signedinUrl.searchParams.set(
+ "organization",
+ selectedSession.factors?.user?.organizationId,
+ );
+ }
+ return NextResponse.redirect(signedinUrl);
+ } else {
+ return NextResponse.json({ error }, { status: 500 });
+ }
+ }
+ }
+ }
+}
diff --git a/login/apps/login/src/lib/self.ts b/login/apps/login/src/lib/self.ts
new file mode 100644
index 0000000000..df8508c29e
--- /dev/null
+++ b/login/apps/login/src/lib/self.ts
@@ -0,0 +1,60 @@
+"use server";
+
+import { createUserServiceClient } from "@zitadel/client/v2";
+import { headers } from "next/headers";
+import { getSessionCookieById } from "./cookies";
+import { getServiceUrlFromHeaders } from "./service-url";
+import { createServerTransport, getSession } from "./zitadel";
+
+const myUserService = async (serviceUrl: string, sessionToken: string) => {
+ const transportPromise = await createServerTransport(
+ sessionToken,
+ serviceUrl,
+ );
+ return createUserServiceClient(transportPromise);
+};
+
+export async function setMyPassword({
+ sessionId,
+ password,
+}: {
+ sessionId: string;
+ password: string;
+}) {
+ const _headers = await headers();
+ const { serviceUrl } = getServiceUrlFromHeaders(_headers);
+
+ const sessionCookie = await getSessionCookieById({ sessionId });
+
+ const { session } = await getSession({
+ serviceUrl,
+ sessionId: sessionCookie.id,
+ sessionToken: sessionCookie.token,
+ });
+
+ if (!session) {
+ return { error: "Could not load session" };
+ }
+
+ const service = await myUserService(serviceUrl, `${sessionCookie.token}`);
+
+ if (!session?.factors?.user?.id) {
+ return { error: "No user id found in session" };
+ }
+
+ return service
+ .setPassword(
+ {
+ userId: session.factors.user.id,
+ newPassword: { password, changeRequired: false },
+ },
+ {},
+ )
+ .catch((error) => {
+ console.log(error);
+ if (error.code === 7) {
+ return { error: "Session is not valid." };
+ }
+ throw error;
+ });
+}
diff --git a/login/apps/login/src/lib/server/cookie.ts b/login/apps/login/src/lib/server/cookie.ts
new file mode 100644
index 0000000000..841fc06b3a
--- /dev/null
+++ b/login/apps/login/src/lib/server/cookie.ts
@@ -0,0 +1,278 @@
+"use server";
+
+import { addSessionToCookie, updateSessionCookie } from "@/lib/cookies";
+import {
+ createSessionForUserIdAndIdpIntent,
+ createSessionFromChecks,
+ getSecuritySettings,
+ getSession,
+ setSession,
+} from "@/lib/zitadel";
+import { ConnectError, Duration, timestampMs } from "@zitadel/client";
+import {
+ CredentialsCheckError,
+ CredentialsCheckErrorSchema,
+ ErrorDetail,
+} from "@zitadel/proto/zitadel/message_pb";
+import {
+ Challenges,
+ RequestChallenges,
+} from "@zitadel/proto/zitadel/session/v2/challenge_pb";
+import { Session } from "@zitadel/proto/zitadel/session/v2/session_pb";
+import { Checks } from "@zitadel/proto/zitadel/session/v2/session_service_pb";
+import { headers } from "next/headers";
+import { getServiceUrlFromHeaders } from "../service-url";
+
+type CustomCookieData = {
+ id: string;
+ token: string;
+ loginName: string;
+ organization?: string;
+ creationTs: string;
+ expirationTs: string;
+ changeTs: string;
+ requestId?: string; // if its linked to an OIDC flow
+};
+
+const passwordAttemptsHandler = (error: ConnectError) => {
+ const details = error.findDetails(CredentialsCheckErrorSchema);
+
+ if (details[0] && "failedAttempts" in details[0]) {
+ const failedAttempts = details[0].failedAttempts;
+ throw {
+ error: `Failed to authenticate: You had ${failedAttempts} password attempts.`,
+ failedAttempts: failedAttempts,
+ };
+ }
+ throw error;
+};
+
+export async function createSessionAndUpdateCookie(command: {
+ checks: Checks;
+ requestId: string | undefined;
+ lifetime?: Duration;
+}): Promise {
+ const _headers = await headers();
+ const { serviceUrl } = getServiceUrlFromHeaders(_headers);
+
+ const createdSession = await createSessionFromChecks({
+ serviceUrl,
+ checks: command.checks,
+ lifetime: command.lifetime,
+ });
+
+ if (createdSession) {
+ return getSession({
+ serviceUrl,
+ sessionId: createdSession.sessionId,
+ sessionToken: createdSession.sessionToken,
+ }).then(async (response) => {
+ if (response?.session && response.session?.factors?.user?.loginName) {
+ const sessionCookie: CustomCookieData = {
+ id: createdSession.sessionId,
+ token: createdSession.sessionToken,
+ creationTs: response.session.creationDate
+ ? `${timestampMs(response.session.creationDate)}`
+ : "",
+ expirationTs: response.session.expirationDate
+ ? `${timestampMs(response.session.expirationDate)}`
+ : "",
+ changeTs: response.session.changeDate
+ ? `${timestampMs(response.session.changeDate)}`
+ : "",
+ loginName: response.session.factors.user.loginName ?? "",
+ };
+
+ if (command.requestId) {
+ sessionCookie.requestId = command.requestId;
+ }
+
+ if (response.session.factors.user.organizationId) {
+ sessionCookie.organization =
+ response.session.factors.user.organizationId;
+ }
+
+ const securitySettings = await getSecuritySettings({ serviceUrl });
+ const sameSite = securitySettings?.embeddedIframe?.enabled
+ ? "none"
+ : true;
+
+ await addSessionToCookie({ session: sessionCookie, sameSite });
+
+ return response.session as Session;
+ } else {
+ throw "could not get session or session does not have loginName";
+ }
+ });
+ } else {
+ throw "Could not create session";
+ }
+}
+
+export async function createSessionForIdpAndUpdateCookie({
+ userId,
+ idpIntent,
+ requestId,
+ lifetime,
+}: {
+ userId: string;
+ idpIntent: {
+ idpIntentId?: string | undefined;
+ idpIntentToken?: string | undefined;
+ };
+ requestId: string | undefined;
+ lifetime?: Duration;
+}): Promise {
+ const _headers = await headers();
+ const { serviceUrl } = getServiceUrlFromHeaders(_headers);
+
+ const createdSession = await createSessionForUserIdAndIdpIntent({
+ serviceUrl,
+ userId,
+ idpIntent,
+ lifetime,
+ }).catch((error: ErrorDetail | CredentialsCheckError) => {
+ console.error("Could not set session", error);
+ if ("failedAttempts" in error && error.failedAttempts) {
+ throw {
+ error: `Failed to authenticate: You had ${error.failedAttempts} password attempts.`,
+ failedAttempts: error.failedAttempts,
+ };
+ }
+ throw error;
+ });
+
+ if (!createdSession) {
+ throw "Could not create session";
+ }
+
+ const { session } = await getSession({
+ serviceUrl,
+ sessionId: createdSession.sessionId,
+ sessionToken: createdSession.sessionToken,
+ });
+
+ if (!session || !session.factors?.user?.loginName) {
+ throw "Could not retrieve session";
+ }
+
+ const sessionCookie: CustomCookieData = {
+ id: createdSession.sessionId,
+ token: createdSession.sessionToken,
+ creationTs: session.creationDate
+ ? `${timestampMs(session.creationDate)}`
+ : "",
+ expirationTs: session.expirationDate
+ ? `${timestampMs(session.expirationDate)}`
+ : "",
+ changeTs: session.changeDate ? `${timestampMs(session.changeDate)}` : "",
+ loginName: session.factors.user.loginName ?? "",
+ organization: session.factors.user.organizationId ?? "",
+ };
+
+ if (requestId) {
+ sessionCookie.requestId = requestId;
+ }
+
+ if (session.factors.user.organizationId) {
+ sessionCookie.organization = session.factors.user.organizationId;
+ }
+
+ const securitySettings = await getSecuritySettings({ serviceUrl });
+ const sameSite = securitySettings?.embeddedIframe?.enabled ? "none" : true;
+
+ return addSessionToCookie({ session: sessionCookie, sameSite }).then(() => {
+ return session as Session;
+ });
+}
+
+export type SessionWithChallenges = Session & {
+ challenges: Challenges | undefined;
+};
+
+export async function setSessionAndUpdateCookie(
+ recentCookie: CustomCookieData,
+ checks?: Checks,
+ challenges?: RequestChallenges,
+ requestId?: string,
+ lifetime?: Duration,
+) {
+ const _headers = await headers();
+ const { serviceUrl } = getServiceUrlFromHeaders(_headers);
+
+ return setSession({
+ serviceUrl,
+ sessionId: recentCookie.id,
+ sessionToken: recentCookie.token,
+ challenges,
+ checks,
+ lifetime,
+ })
+ .then((updatedSession) => {
+ if (updatedSession) {
+ const sessionCookie: CustomCookieData = {
+ id: recentCookie.id,
+ token: updatedSession.sessionToken,
+ creationTs: recentCookie.creationTs,
+ expirationTs: recentCookie.expirationTs,
+ // just overwrite the changeDate with the new one
+ changeTs: updatedSession.details?.changeDate
+ ? `${timestampMs(updatedSession.details.changeDate)}`
+ : "",
+ loginName: recentCookie.loginName,
+ organization: recentCookie.organization,
+ };
+
+ if (requestId) {
+ sessionCookie.requestId = requestId;
+ }
+
+ return getSession({
+ serviceUrl,
+ sessionId: sessionCookie.id,
+ sessionToken: sessionCookie.token,
+ }).then(async (response) => {
+ if (
+ !response?.session ||
+ !response.session.factors?.user?.loginName
+ ) {
+ throw "could not get session or session does not have loginName";
+ }
+
+ const { session } = response;
+ const newCookie: CustomCookieData = {
+ id: sessionCookie.id,
+ token: updatedSession.sessionToken,
+ creationTs: sessionCookie.creationTs,
+ expirationTs: sessionCookie.expirationTs,
+ // just overwrite the changeDate with the new one
+ changeTs: updatedSession.details?.changeDate
+ ? `${timestampMs(updatedSession.details.changeDate)}`
+ : "",
+ loginName: session.factors?.user?.loginName ?? "",
+ organization: session.factors?.user?.organizationId ?? "",
+ };
+
+ if (sessionCookie.requestId) {
+ newCookie.requestId = sessionCookie.requestId;
+ }
+
+ const securitySettings = await getSecuritySettings({ serviceUrl });
+ const sameSite = securitySettings?.embeddedIframe?.enabled
+ ? "none"
+ : true;
+
+ return updateSessionCookie({
+ id: sessionCookie.id,
+ session: newCookie,
+ sameSite,
+ }).then(() => {
+ return { challenges: updatedSession.challenges, ...session };
+ });
+ });
+ } else {
+ throw "Session not be set";
+ }
+ })
+ .catch(passwordAttemptsHandler);
+}
diff --git a/login/apps/login/src/lib/server/device.ts b/login/apps/login/src/lib/server/device.ts
new file mode 100644
index 0000000000..5e36facfc8
--- /dev/null
+++ b/login/apps/login/src/lib/server/device.ts
@@ -0,0 +1,20 @@
+"use server";
+
+import { authorizeOrDenyDeviceAuthorization } from "@/lib/zitadel";
+import { headers } from "next/headers";
+import { getServiceUrlFromHeaders } from "../service-url";
+
+export async function completeDeviceAuthorization(
+ deviceAuthorizationId: string,
+ session?: { sessionId: string; sessionToken: string },
+) {
+ const _headers = await headers();
+ const { serviceUrl } = getServiceUrlFromHeaders(_headers);
+
+ // without the session, device auth request is denied
+ return authorizeOrDenyDeviceAuthorization({
+ serviceUrl,
+ deviceAuthorizationId,
+ session,
+ });
+}
diff --git a/login/apps/login/src/lib/server/idp.ts b/login/apps/login/src/lib/server/idp.ts
new file mode 100644
index 0000000000..87f88a7c32
--- /dev/null
+++ b/login/apps/login/src/lib/server/idp.ts
@@ -0,0 +1,241 @@
+"use server";
+
+import {
+ getLoginSettings,
+ getUserByID,
+ startIdentityProviderFlow,
+ startLDAPIdentityProviderFlow,
+} from "@/lib/zitadel";
+import { headers } from "next/headers";
+import { redirect } from "next/navigation";
+import { getNextUrl } from "../client";
+import { getServiceUrlFromHeaders } from "../service-url";
+import { checkEmailVerification } from "../verify-helper";
+import { createSessionForIdpAndUpdateCookie } from "./cookie";
+
+export type RedirectToIdpState = { error?: string | null } | undefined;
+
+export async function redirectToIdp(
+ prevState: RedirectToIdpState,
+ formData: FormData,
+): Promise {
+ const _headers = await headers();
+ const { serviceUrl } = getServiceUrlFromHeaders(_headers);
+ const host = _headers.get("host");
+ if (!host) {
+ return { error: "Could not get host" };
+ }
+
+ const params = new URLSearchParams();
+
+ const linkOnly = formData.get("linkOnly") === "true";
+ const requestId = formData.get("requestId") as string;
+ const organization = formData.get("organization") as string;
+ const idpId = formData.get("id") as string;
+ const provider = formData.get("provider") as string;
+
+ if (linkOnly) params.set("link", "true");
+ if (requestId) params.set("requestId", requestId);
+ if (organization) params.set("organization", organization);
+
+ // redirect to LDAP page where username and password is requested
+ if (provider === "ldap") {
+ params.set("idpId", idpId);
+ redirect(`/idp/ldap?` + params.toString());
+ }
+
+ const response = await startIDPFlow({
+ serviceUrl,
+ host,
+ idpId,
+ successUrl: `/idp/${provider}/success?` + params.toString(),
+ failureUrl: `/idp/${provider}/failure?` + params.toString(),
+ });
+
+ if (!response) {
+ return { error: "Could not start IDP flow" };
+ }
+
+ if (response && "redirect" in response && response?.redirect) {
+ redirect(response.redirect);
+ }
+
+ return { error: "Unexpected response from IDP flow" };
+}
+
+export type StartIDPFlowCommand = {
+ serviceUrl: string;
+ host: string;
+ idpId: string;
+ successUrl: string;
+ failureUrl: string;
+};
+
+async function startIDPFlow(command: StartIDPFlowCommand) {
+ const basePath = process.env.NEXT_PUBLIC_BASE_PATH ?? "";
+
+ const url = await startIdentityProviderFlow({
+ serviceUrl: command.serviceUrl,
+ idpId: command.idpId,
+ urls: {
+ successUrl: `${command.host.includes("localhost") ? "http://" : "https://"}${command.host}${basePath}${command.successUrl}`,
+ failureUrl: `${command.host.includes("localhost") ? "http://" : "https://"}${command.host}${basePath}${command.failureUrl}`,
+ },
+ });
+
+ if (!url) {
+ return { error: "Could not start IDP flow" };
+ }
+
+ return { redirect: url };
+}
+
+type CreateNewSessionCommand = {
+ userId: string;
+ idpIntent: {
+ idpIntentId: string;
+ idpIntentToken: string;
+ };
+ loginName?: string;
+ password?: string;
+ organization?: string;
+ requestId?: string;
+};
+
+export async function createNewSessionFromIdpIntent(
+ command: CreateNewSessionCommand,
+) {
+ const _headers = await headers();
+
+ const { serviceUrl } = getServiceUrlFromHeaders(_headers);
+ const host = _headers.get("host");
+
+ if (!host) {
+ return { error: "Could not get domain" };
+ }
+
+ if (!command.userId || !command.idpIntent) {
+ throw new Error("No userId or loginName provided");
+ }
+
+ const userResponse = await getUserByID({
+ serviceUrl,
+ userId: command.userId,
+ });
+
+ if (!userResponse || !userResponse.user) {
+ return { error: "User not found in the system" };
+ }
+
+ const loginSettings = await getLoginSettings({
+ serviceUrl,
+ organization: userResponse.user.details?.resourceOwner,
+ });
+
+ const session = await createSessionForIdpAndUpdateCookie({
+ userId: command.userId,
+ idpIntent: command.idpIntent,
+ requestId: command.requestId,
+ lifetime: loginSettings?.externalLoginCheckLifetime,
+ });
+
+ if (!session || !session.factors?.user) {
+ return { error: "Could not create session" };
+ }
+
+ const humanUser =
+ userResponse.user.type.case === "human"
+ ? userResponse.user.type.value
+ : undefined;
+
+ // check to see if user was verified
+ const emailVerificationCheck = checkEmailVerification(
+ session,
+ humanUser,
+ command.organization,
+ command.requestId,
+ );
+
+ if (emailVerificationCheck?.redirect) {
+ return emailVerificationCheck;
+ }
+
+ // TODO: check if user has MFA methods
+ // const mfaFactorCheck = checkMFAFactors(session, loginSettings, authMethods, organization, requestId);
+ // if (mfaFactorCheck?.redirect) {
+ // return mfaFactorCheck;
+ // }
+
+ const url = await getNextUrl(
+ command.requestId && session.id
+ ? {
+ sessionId: session.id,
+ requestId: command.requestId,
+ organization: session.factors.user.organizationId,
+ }
+ : {
+ loginName: session.factors.user.loginName,
+ organization: session.factors.user.organizationId,
+ },
+ loginSettings?.defaultRedirectUri,
+ );
+
+ if (url) {
+ return { redirect: url };
+ }
+}
+
+type createNewSessionForLDAPCommand = {
+ username: string;
+ password: string;
+ idpId: string;
+ link: boolean;
+};
+
+export async function createNewSessionForLDAP(
+ command: createNewSessionForLDAPCommand,
+) {
+ const _headers = await headers();
+
+ const { serviceUrl } = getServiceUrlFromHeaders(_headers);
+ const host = _headers.get("host");
+
+ if (!host) {
+ return { error: "Could not get domain" };
+ }
+
+ if (!command.username || !command.password) {
+ return { error: "No username or password provided" };
+ }
+
+ const response = await startLDAPIdentityProviderFlow({
+ serviceUrl,
+ idpId: command.idpId,
+ username: command.username,
+ password: command.password,
+ });
+
+ if (
+ !response ||
+ response.nextStep.case !== "idpIntent" ||
+ !response.nextStep.value
+ ) {
+ return { error: "Could not start LDAP identity provider flow" };
+ }
+
+ const { userId, idpIntentId, idpIntentToken } = response.nextStep.value;
+
+ const params = new URLSearchParams({
+ userId,
+ id: idpIntentId,
+ token: idpIntentToken,
+ });
+
+ if (command.link) {
+ params.set("link", "true");
+ }
+
+ return {
+ redirect: `/idp/ldap/success?` + params.toString(),
+ };
+}
diff --git a/login/apps/login/src/lib/server/loginname.ts b/login/apps/login/src/lib/server/loginname.ts
new file mode 100644
index 0000000000..68cb345c06
--- /dev/null
+++ b/login/apps/login/src/lib/server/loginname.ts
@@ -0,0 +1,454 @@
+"use server";
+
+import { create } from "@zitadel/client";
+import { ChecksSchema } from "@zitadel/proto/zitadel/session/v2/session_service_pb";
+import { AuthenticationMethodType } from "@zitadel/proto/zitadel/user/v2/user_service_pb";
+import { headers } from "next/headers";
+import { idpTypeToIdentityProviderType, idpTypeToSlug } from "../idp";
+
+import { PasskeysType } from "@zitadel/proto/zitadel/settings/v2/login_settings_pb";
+import { UserState } from "@zitadel/proto/zitadel/user/v2/user_pb";
+import { getServiceUrlFromHeaders } from "../service-url";
+import {
+ getActiveIdentityProviders,
+ getIDPByID,
+ getLoginSettings,
+ getOrgsByDomain,
+ listAuthenticationMethodTypes,
+ listIDPLinks,
+ searchUsers,
+ SearchUsersCommand,
+ startIdentityProviderFlow,
+} from "../zitadel";
+import { createSessionAndUpdateCookie } from "./cookie";
+
+export type SendLoginnameCommand = {
+ loginName: string;
+ requestId?: string;
+ organization?: string;
+ suffix?: string;
+};
+
+const ORG_SUFFIX_REGEX = /(?<=@)(.+)/;
+
+export async function sendLoginname(command: SendLoginnameCommand) {
+ const _headers = await headers();
+ const { serviceUrl } = getServiceUrlFromHeaders(_headers);
+ const host = _headers.get("host");
+
+ if (!host) {
+ throw new Error("Could not get domain");
+ }
+
+ const loginSettingsByContext = await getLoginSettings({
+ serviceUrl,
+ organization: command.organization,
+ });
+
+ if (!loginSettingsByContext) {
+ return { error: "Could not get login settings" };
+ }
+
+ let searchUsersRequest: SearchUsersCommand = {
+ serviceUrl,
+ searchValue: command.loginName,
+ organizationId: command.organization,
+ loginSettings: loginSettingsByContext,
+ suffix: command.suffix,
+ };
+
+ const searchResult = await searchUsers(searchUsersRequest);
+
+ if ("error" in searchResult && searchResult.error) {
+ return searchResult;
+ }
+
+ if (!("result" in searchResult)) {
+ return { error: "Could not search users" };
+ }
+
+ const { result: potentialUsers } = searchResult;
+
+ const redirectUserToSingleIDPIfAvailable = async () => {
+ const identityProviders = await getActiveIdentityProviders({
+ serviceUrl,
+ orgId: command.organization,
+ }).then((resp) => {
+ return resp.identityProviders;
+ });
+
+ if (identityProviders.length === 1) {
+ const _headers = await headers();
+ const { serviceUrl } = getServiceUrlFromHeaders(_headers);
+ const host = _headers.get("host");
+
+ if (!host) {
+ return { error: "Could not get host" };
+ }
+
+ const identityProviderType = identityProviders[0].type;
+
+ const provider = idpTypeToSlug(identityProviderType);
+
+ const params = new URLSearchParams();
+
+ if (command.requestId) {
+ params.set("requestId", command.requestId);
+ }
+
+ if (command.organization) {
+ params.set("organization", command.organization);
+ }
+
+ const basePath = process.env.NEXT_PUBLIC_BASE_PATH ?? "";
+
+ const url = await startIdentityProviderFlow({
+ serviceUrl,
+ idpId: identityProviders[0].id,
+ urls: {
+ successUrl:
+ `${host.includes("localhost") ? "http://" : "https://"}${host}${basePath}/idp/${provider}/success?` +
+ new URLSearchParams(params),
+ failureUrl:
+ `${host.includes("localhost") ? "http://" : "https://"}${host}${basePath}/idp/${provider}/failure?` +
+ new URLSearchParams(params),
+ },
+ });
+
+ if (!url) {
+ return { error: "Could not start IDP flow" };
+ }
+
+ return { redirect: url };
+ }
+ };
+
+ const redirectUserToIDP = async (userId: string) => {
+ const identityProviders = await listIDPLinks({
+ serviceUrl,
+ userId,
+ }).then((resp) => {
+ return resp.result;
+ });
+
+ if (identityProviders.length === 1) {
+ const _headers = await headers();
+ const { serviceUrl } = getServiceUrlFromHeaders(_headers);
+ const host = _headers.get("host");
+
+ if (!host) {
+ return { error: "Could not get host" };
+ }
+
+ const identityProviderId = identityProviders[0].idpId;
+
+ const idp = await getIDPByID({
+ serviceUrl,
+ id: identityProviderId,
+ });
+
+ const idpType = idp?.type;
+
+ if (!idp || !idpType) {
+ throw new Error("Could not find identity provider");
+ }
+
+ const identityProviderType = idpTypeToIdentityProviderType(idpType);
+ const provider = idpTypeToSlug(identityProviderType);
+
+ const params = new URLSearchParams({ userId });
+
+ if (command.requestId) {
+ params.set("requestId", command.requestId);
+ }
+
+ if (command.organization) {
+ params.set("organization", command.organization);
+ }
+
+ const basePath = process.env.NEXT_PUBLIC_BASE_PATH ?? "";
+
+ const url = await startIdentityProviderFlow({
+ serviceUrl,
+ idpId: idp.id,
+ urls: {
+ successUrl:
+ `${host.includes("localhost") ? "http://" : "https://"}${host}${basePath}/idp/${provider}/success?` +
+ new URLSearchParams(params),
+ failureUrl:
+ `${host.includes("localhost") ? "http://" : "https://"}${host}${basePath}/idp/${provider}/failure?` +
+ new URLSearchParams(params),
+ },
+ });
+
+ if (!url) {
+ return { error: "Could not start IDP flow" };
+ }
+
+ return { redirect: url };
+ }
+ };
+
+ if (potentialUsers.length > 1) {
+ return { error: "More than one user found. Provide a unique identifier." };
+ } else if (potentialUsers.length == 1 && potentialUsers[0].userId) {
+ const user = potentialUsers[0];
+ const userId = potentialUsers[0].userId;
+
+ const userLoginSettings = await getLoginSettings({
+ serviceUrl,
+ organization: user.details?.resourceOwner,
+ });
+
+ // compare with the concatenated suffix when set
+ const concatLoginname = command.suffix
+ ? `${command.loginName}@${command.suffix}`
+ : command.loginName;
+
+ const humanUser =
+ potentialUsers[0].type.case === "human"
+ ? potentialUsers[0].type.value
+ : undefined;
+
+ // recheck login settings after user discovery, as the search might have been done without org scope
+ if (
+ userLoginSettings?.disableLoginWithEmail &&
+ userLoginSettings?.disableLoginWithPhone
+ ) {
+ if (user.preferredLoginName !== concatLoginname) {
+ return { error: "User not found in the system!" };
+ }
+ } else if (userLoginSettings?.disableLoginWithEmail) {
+ if (
+ user.preferredLoginName !== concatLoginname ||
+ humanUser?.phone?.phone !== command.loginName
+ ) {
+ return { error: "User not found in the system!" };
+ }
+ } else if (userLoginSettings?.disableLoginWithPhone) {
+ if (
+ user.preferredLoginName !== concatLoginname ||
+ humanUser?.email?.email !== command.loginName
+ ) {
+ return { error: "User not found in the system!" };
+ }
+ }
+
+ const checks = create(ChecksSchema, {
+ user: { search: { case: "userId", value: userId } },
+ });
+
+ const session = await createSessionAndUpdateCookie({
+ checks,
+ requestId: command.requestId,
+ });
+
+ if (!session.factors?.user?.id) {
+ return { error: "Could not create session for user" };
+ }
+
+ // TODO: check if handling of userstate INITIAL is needed
+ if (user.state === UserState.INITIAL) {
+ return { error: "Initial User not supported" };
+ }
+
+ const methods = await listAuthenticationMethodTypes({
+ serviceUrl,
+ userId: session.factors?.user?.id,
+ });
+
+ // always resend invite if user has no auth method set
+ if (!methods.authMethodTypes || !methods.authMethodTypes.length) {
+ const params = new URLSearchParams({
+ loginName: session.factors?.user?.loginName as string,
+ send: "true", // set this to true to request a new code immediately
+ invite: "true",
+ });
+
+ if (command.requestId) {
+ params.append("requestId", command.requestId);
+ }
+
+ if (command.organization || session.factors?.user?.organizationId) {
+ params.append(
+ "organization",
+ command.organization ??
+ (session.factors?.user?.organizationId as string),
+ );
+ }
+
+ return { redirect: `/verify?` + params };
+ }
+
+ if (methods.authMethodTypes.length == 1) {
+ const method = methods.authMethodTypes[0];
+ switch (method) {
+ case AuthenticationMethodType.PASSWORD: // user has only password as auth method
+ if (!userLoginSettings?.allowUsernamePassword) {
+ return {
+ error:
+ "Username Password not allowed! Contact your administrator for more information.",
+ };
+ }
+
+ const paramsPassword: any = {
+ loginName: session.factors?.user?.loginName,
+ };
+
+ // TODO: does this have to be checked in loginSettings.allowDomainDiscovery
+
+ if (command.organization || session.factors?.user?.organizationId) {
+ paramsPassword.organization =
+ command.organization ?? session.factors?.user?.organizationId;
+ }
+
+ if (command.requestId) {
+ paramsPassword.requestId = command.requestId;
+ }
+
+ return {
+ redirect: "/password?" + new URLSearchParams(paramsPassword),
+ };
+
+ case AuthenticationMethodType.PASSKEY: // AuthenticationMethodType.AUTHENTICATION_METHOD_TYPE_PASSKEY
+ if (userLoginSettings?.passkeysType === PasskeysType.NOT_ALLOWED) {
+ return {
+ error:
+ "Passkeys not allowed! Contact your administrator for more information.",
+ };
+ }
+
+ const paramsPasskey: any = { loginName: command.loginName };
+ if (command.requestId) {
+ paramsPasskey.requestId = command.requestId;
+ }
+
+ if (command.organization || session.factors?.user?.organizationId) {
+ paramsPasskey.organization =
+ command.organization ?? session.factors?.user?.organizationId;
+ }
+
+ return { redirect: "/passkey?" + new URLSearchParams(paramsPasskey) };
+ }
+ } else {
+ // prefer passkey in favor of other methods
+ if (methods.authMethodTypes.includes(AuthenticationMethodType.PASSKEY)) {
+ const passkeyParams: any = {
+ loginName: command.loginName,
+ altPassword: `${methods.authMethodTypes.includes(1)}`, // show alternative password option
+ };
+
+ if (command.requestId) {
+ passkeyParams.requestId = command.requestId;
+ }
+
+ if (command.organization || session.factors?.user?.organizationId) {
+ passkeyParams.organization =
+ command.organization ?? session.factors?.user?.organizationId;
+ }
+
+ return { redirect: "/passkey?" + new URLSearchParams(passkeyParams) };
+ } else if (
+ methods.authMethodTypes.includes(AuthenticationMethodType.IDP)
+ ) {
+ return redirectUserToIDP(userId);
+ } else if (
+ methods.authMethodTypes.includes(AuthenticationMethodType.PASSWORD)
+ ) {
+ // user has no passkey setup and login settings allow passkeys
+ const paramsPasswordDefault: any = { loginName: command.loginName };
+
+ if (command.requestId) {
+ paramsPasswordDefault.requestId = command.requestId;
+ }
+
+ if (command.organization || session.factors?.user?.organizationId) {
+ paramsPasswordDefault.organization =
+ command.organization ?? session.factors?.user?.organizationId;
+ }
+
+ return {
+ redirect: "/password?" + new URLSearchParams(paramsPasswordDefault),
+ };
+ }
+ }
+ }
+
+ // user not found, check if register is enabled on instance / organization context
+ if (
+ loginSettingsByContext?.allowRegister &&
+ !loginSettingsByContext?.allowUsernamePassword
+ ) {
+ const resp = await redirectUserToSingleIDPIfAvailable();
+ if (resp) {
+ return resp;
+ }
+ return { error: "User not found in the system" };
+ } else if (
+ loginSettingsByContext?.allowRegister &&
+ loginSettingsByContext?.allowUsernamePassword
+ ) {
+ let orgToRegisterOn: string | undefined = command.organization;
+
+ if (
+ !loginSettingsByContext?.ignoreUnknownUsernames &&
+ !orgToRegisterOn &&
+ command.loginName &&
+ ORG_SUFFIX_REGEX.test(command.loginName)
+ ) {
+ const matched = ORG_SUFFIX_REGEX.exec(command.loginName);
+ const suffix = matched?.[1] ?? "";
+
+ // this just returns orgs where the suffix is set as primary domain
+ const orgs = await getOrgsByDomain({
+ serviceUrl,
+ domain: suffix,
+ });
+ const orgToCheckForDiscovery =
+ orgs.result && orgs.result.length === 1 ? orgs.result[0].id : undefined;
+
+ const orgLoginSettings = await getLoginSettings({
+ serviceUrl,
+ organization: orgToCheckForDiscovery,
+ });
+ if (orgLoginSettings?.allowDomainDiscovery) {
+ orgToRegisterOn = orgToCheckForDiscovery;
+ }
+ }
+
+ // do not register user if ignoreUnknownUsernames is set
+ if (orgToRegisterOn && !loginSettingsByContext?.ignoreUnknownUsernames) {
+ const params = new URLSearchParams({ organization: orgToRegisterOn });
+
+ if (command.requestId) {
+ params.set("requestId", command.requestId);
+ }
+
+ if (command.loginName) {
+ params.set("email", command.loginName);
+ }
+
+ return { redirect: "/register?" + params };
+ }
+ }
+
+ if (loginSettingsByContext?.ignoreUnknownUsernames) {
+ const paramsPasswordDefault = new URLSearchParams({
+ loginName: command.loginName,
+ });
+
+ if (command.requestId) {
+ paramsPasswordDefault.append("requestId", command.requestId);
+ }
+
+ if (command.organization) {
+ paramsPasswordDefault.append("organization", command.organization);
+ }
+
+ return { redirect: "/password?" + paramsPasswordDefault };
+ }
+
+ // fallbackToPassword
+
+ return { error: "User not found in the system" };
+}
diff --git a/login/apps/login/src/lib/server/oidc.ts b/login/apps/login/src/lib/server/oidc.ts
new file mode 100644
index 0000000000..36a31fe419
--- /dev/null
+++ b/login/apps/login/src/lib/server/oidc.ts
@@ -0,0 +1,15 @@
+"use server";
+
+import { getDeviceAuthorizationRequest as zitadelGetDeviceAuthorizationRequest } from "@/lib/zitadel";
+import { headers } from "next/headers";
+import { getServiceUrlFromHeaders } from "../service-url";
+
+export async function getDeviceAuthorizationRequest(userCode: string) {
+ const _headers = await headers();
+ const { serviceUrl } = getServiceUrlFromHeaders(_headers);
+
+ return zitadelGetDeviceAuthorizationRequest({
+ serviceUrl,
+ userCode,
+ });
+}
diff --git a/login/apps/login/src/lib/server/otp.ts b/login/apps/login/src/lib/server/otp.ts
new file mode 100644
index 0000000000..f3d4a1536a
--- /dev/null
+++ b/login/apps/login/src/lib/server/otp.ts
@@ -0,0 +1,83 @@
+"use server";
+
+import { setSessionAndUpdateCookie } from "@/lib/server/cookie";
+import { create } from "@zitadel/client";
+import {
+ CheckOTPSchema,
+ ChecksSchema,
+ CheckTOTPSchema,
+} from "@zitadel/proto/zitadel/session/v2/session_service_pb";
+import { headers } from "next/headers";
+import {
+ getMostRecentSessionCookie,
+ getSessionCookieById,
+ getSessionCookieByLoginName,
+} from "../cookies";
+import { getServiceUrlFromHeaders } from "../service-url";
+import { getLoginSettings } from "../zitadel";
+
+export type SetOTPCommand = {
+ loginName?: string;
+ sessionId?: string;
+ organization?: string;
+ requestId?: string;
+ code: string;
+ method: string;
+};
+
+export async function setOTP(command: SetOTPCommand) {
+ const _headers = await headers();
+ const { serviceUrl } = getServiceUrlFromHeaders(_headers);
+
+ const recentSession = command.sessionId
+ ? await getSessionCookieById({ sessionId: command.sessionId }).catch(
+ (error) => {
+ return Promise.reject(error);
+ },
+ )
+ : command.loginName
+ ? await getSessionCookieByLoginName({
+ loginName: command.loginName,
+ organization: command.organization,
+ }).catch((error) => {
+ return Promise.reject(error);
+ })
+ : await getMostRecentSessionCookie().catch((error) => {
+ return Promise.reject(error);
+ });
+
+ const checks = create(ChecksSchema, {});
+
+ if (command.method === "time-based") {
+ checks.totp = create(CheckTOTPSchema, {
+ code: command.code,
+ });
+ } else if (command.method === "sms") {
+ checks.otpSms = create(CheckOTPSchema, {
+ code: command.code,
+ });
+ } else if (command.method === "email") {
+ checks.otpEmail = create(CheckOTPSchema, {
+ code: command.code,
+ });
+ }
+
+ const loginSettings = await getLoginSettings({
+ serviceUrl,
+ organization: command.organization,
+ });
+
+ return setSessionAndUpdateCookie(
+ recentSession,
+ checks,
+ undefined,
+ command.requestId,
+ loginSettings?.secondFactorCheckLifetime,
+ ).then((session) => {
+ return {
+ sessionId: session.id,
+ factors: session.factors,
+ challenges: session.challenges,
+ };
+ });
+}
diff --git a/login/apps/login/src/lib/server/passkeys.ts b/login/apps/login/src/lib/server/passkeys.ts
new file mode 100644
index 0000000000..3470629f24
--- /dev/null
+++ b/login/apps/login/src/lib/server/passkeys.ts
@@ -0,0 +1,278 @@
+"use server";
+
+import {
+ createPasskeyRegistrationLink,
+ getLoginSettings,
+ getSession,
+ getUserByID,
+ listAuthenticationMethodTypes,
+ registerPasskey,
+ verifyPasskeyRegistration as zitadelVerifyPasskeyRegistration,
+} from "@/lib/zitadel";
+import { create, Duration, Timestamp, timestampDate } from "@zitadel/client";
+import { Session } from "@zitadel/proto/zitadel/session/v2/session_pb";
+import { Checks } from "@zitadel/proto/zitadel/session/v2/session_service_pb";
+import {
+ RegisterPasskeyResponse,
+ VerifyPasskeyRegistrationRequestSchema,
+} from "@zitadel/proto/zitadel/user/v2/user_service_pb";
+import { headers } from "next/headers";
+import { userAgent } from "next/server";
+import { getNextUrl } from "../client";
+import {
+ getMostRecentSessionCookie,
+ getSessionCookieById,
+ getSessionCookieByLoginName,
+} from "../cookies";
+import { getServiceUrlFromHeaders } from "../service-url";
+import {
+ checkEmailVerification,
+ checkUserVerification,
+} from "../verify-helper";
+import { setSessionAndUpdateCookie } from "./cookie";
+
+type VerifyPasskeyCommand = {
+ passkeyId: string;
+ passkeyName?: string;
+ publicKeyCredential: any;
+ sessionId: string;
+};
+
+type RegisterPasskeyCommand = {
+ sessionId: string;
+};
+
+function isSessionValid(session: Partial): {
+ valid: boolean;
+ verifiedAt?: Timestamp;
+} {
+ const validPassword = session?.factors?.password?.verifiedAt;
+ const validPasskey = session?.factors?.webAuthN?.verifiedAt;
+ const stillValid = session.expirationDate
+ ? timestampDate(session.expirationDate) > new Date()
+ : true;
+
+ const verifiedAt = validPassword || validPasskey;
+ const valid = !!((validPassword || validPasskey) && stillValid);
+
+ return { valid, verifiedAt };
+}
+
+export async function registerPasskeyLink(
+ command: RegisterPasskeyCommand,
+): Promise {
+ const { sessionId } = command;
+
+ const _headers = await headers();
+ const { serviceUrl } = getServiceUrlFromHeaders(_headers);
+ const host = _headers.get("host");
+
+ if (!host) {
+ throw new Error("Could not get domain");
+ }
+
+ const sessionCookie = await getSessionCookieById({ sessionId });
+ const session = await getSession({
+ serviceUrl,
+ sessionId: sessionCookie.id,
+ sessionToken: sessionCookie.token,
+ });
+
+ if (!session?.session?.factors?.user?.id) {
+ return { error: "Could not determine user from session" };
+ }
+
+ const sessionValid = isSessionValid(session.session);
+
+ if (!sessionValid) {
+ const authmethods = await listAuthenticationMethodTypes({
+ serviceUrl,
+ userId: session.session.factors.user.id,
+ });
+
+ // if the user has no authmethods set, we need to check if the user was verified
+ if (authmethods.authMethodTypes.length !== 0) {
+ return {
+ error:
+ "You have to authenticate or have a valid User Verification Check",
+ };
+ }
+
+ // check if a verification was done earlier
+ const hasValidUserVerificationCheck = await checkUserVerification(
+ session.session.factors.user.id,
+ );
+
+ if (!hasValidUserVerificationCheck) {
+ return { error: "User Verification Check has to be done" };
+ }
+ }
+
+ const [hostname, port] = host.split(":");
+
+ if (!hostname) {
+ throw new Error("Could not get hostname");
+ }
+
+ const userId = session?.session?.factors?.user?.id;
+
+ if (!userId) {
+ throw new Error("Could not get session");
+ }
+ // TODO: add org context
+
+ // use session token to add the passkey
+ const registerLink = await createPasskeyRegistrationLink({
+ serviceUrl,
+ userId,
+ });
+
+ if (!registerLink.code) {
+ throw new Error("Missing code in response");
+ }
+
+ return registerPasskey({
+ serviceUrl,
+ userId,
+ code: registerLink.code,
+ domain: hostname,
+ });
+}
+
+export async function verifyPasskeyRegistration(command: VerifyPasskeyCommand) {
+ const _headers = await headers();
+ const { serviceUrl } = getServiceUrlFromHeaders(_headers);
+
+ // if no name is provided, try to generate one from the user agent
+ let passkeyName = command.passkeyName;
+ if (!!!passkeyName) {
+ const headersList = await headers();
+ const userAgentStructure = { headers: headersList };
+ const { browser, device, os } = userAgent(userAgentStructure);
+
+ passkeyName = `${device.vendor ?? ""} ${device.model ?? ""}${
+ device.vendor || device.model ? ", " : ""
+ }${os.name}${os.name ? ", " : ""}${browser.name}`;
+ }
+
+ const sessionCookie = await getSessionCookieById({
+ sessionId: command.sessionId,
+ });
+ const session = await getSession({
+ serviceUrl,
+ sessionId: sessionCookie.id,
+ sessionToken: sessionCookie.token,
+ });
+ const userId = session?.session?.factors?.user?.id;
+
+ if (!userId) {
+ throw new Error("Could not get session");
+ }
+
+ return zitadelVerifyPasskeyRegistration({
+ serviceUrl,
+ request: create(VerifyPasskeyRegistrationRequestSchema, {
+ passkeyId: command.passkeyId,
+ publicKeyCredential: command.publicKeyCredential,
+ passkeyName,
+ userId,
+ }),
+ });
+}
+
+type SendPasskeyCommand = {
+ loginName?: string;
+ sessionId?: string;
+ organization?: string;
+ checks?: Checks;
+ requestId?: string;
+ lifetime?: Duration;
+};
+
+export async function sendPasskey(command: SendPasskeyCommand) {
+ let { loginName, sessionId, organization, checks, requestId } = command;
+ const recentSession = sessionId
+ ? await getSessionCookieById({ sessionId })
+ : loginName
+ ? await getSessionCookieByLoginName({ loginName, organization })
+ : await getMostRecentSessionCookie();
+
+ if (!recentSession) {
+ return {
+ error: "Could not find session",
+ };
+ }
+
+ const _headers = await headers();
+ const { serviceUrl } = getServiceUrlFromHeaders(_headers);
+
+ const loginSettings = await getLoginSettings({
+ serviceUrl,
+ organization,
+ });
+
+ const lifetime = checks?.webAuthN
+ ? loginSettings?.multiFactorCheckLifetime // TODO different lifetime for webauthn u2f/passkey
+ : checks?.otpEmail || checks?.otpSms
+ ? loginSettings?.secondFactorCheckLifetime
+ : undefined;
+
+ const session = await setSessionAndUpdateCookie(
+ recentSession,
+ checks,
+ undefined,
+ requestId,
+ lifetime,
+ );
+
+ if (!session || !session?.factors?.user?.id) {
+ return { error: "Could not update session" };
+ }
+
+ const userResponse = await getUserByID({
+ serviceUrl,
+ userId: session?.factors?.user?.id,
+ });
+
+ if (!userResponse.user) {
+ return { error: "User not found in the system" };
+ }
+
+ const humanUser =
+ userResponse.user.type.case === "human"
+ ? userResponse.user.type.value
+ : undefined;
+
+ const emailVerificationCheck = checkEmailVerification(
+ session,
+ humanUser,
+ organization,
+ requestId,
+ );
+
+ if (emailVerificationCheck?.redirect) {
+ return emailVerificationCheck;
+ }
+
+ const url =
+ requestId && session.id
+ ? await getNextUrl(
+ {
+ sessionId: session.id,
+ requestId: requestId,
+ organization: organization,
+ },
+ loginSettings?.defaultRedirectUri,
+ )
+ : session?.factors?.user?.loginName
+ ? await getNextUrl(
+ {
+ loginName: session.factors.user.loginName,
+ organization: organization,
+ },
+ loginSettings?.defaultRedirectUri,
+ )
+ : null;
+
+ return { redirect: url };
+}
diff --git a/login/apps/login/src/lib/server/password.ts b/login/apps/login/src/lib/server/password.ts
new file mode 100644
index 0000000000..5c6fb03aa5
--- /dev/null
+++ b/login/apps/login/src/lib/server/password.ts
@@ -0,0 +1,460 @@
+"use server";
+
+import {
+ createSessionAndUpdateCookie,
+ setSessionAndUpdateCookie,
+} from "@/lib/server/cookie";
+import {
+ getLockoutSettings,
+ getLoginSettings,
+ getPasswordExpirySettings,
+ getSession,
+ getUserByID,
+ listAuthenticationMethodTypes,
+ listUsers,
+ passwordReset,
+ setPassword,
+ setUserPassword,
+} from "@/lib/zitadel";
+import { ConnectError, create } from "@zitadel/client";
+import { createUserServiceClient } from "@zitadel/client/v2";
+import {
+ Checks,
+ ChecksSchema,
+} from "@zitadel/proto/zitadel/session/v2/session_service_pb";
+import { LoginSettings } from "@zitadel/proto/zitadel/settings/v2/login_settings_pb";
+import { User, UserState } from "@zitadel/proto/zitadel/user/v2/user_pb";
+import {
+ AuthenticationMethodType,
+ SetPasswordRequestSchema,
+} from "@zitadel/proto/zitadel/user/v2/user_service_pb";
+import { headers } from "next/headers";
+import { getNextUrl } from "../client";
+import { getSessionCookieById, getSessionCookieByLoginName } from "../cookies";
+import { getServiceUrlFromHeaders } from "../service-url";
+import {
+ checkEmailVerification,
+ checkMFAFactors,
+ checkPasswordChangeRequired,
+ checkUserVerification,
+} from "../verify-helper";
+import { createServerTransport } from "../zitadel";
+
+type ResetPasswordCommand = {
+ loginName: string;
+ organization?: string;
+ requestId?: string;
+};
+
+export async function resetPassword(command: ResetPasswordCommand) {
+ const _headers = await headers();
+ const { serviceUrl } = getServiceUrlFromHeaders(_headers);
+ const host = _headers.get("host");
+
+ if (!host || typeof host !== "string") {
+ throw new Error("No host found");
+ }
+
+ const users = await listUsers({
+ serviceUrl,
+ loginName: command.loginName,
+ organizationId: command.organization,
+ });
+
+ if (
+ !users.details ||
+ users.details.totalResult !== BigInt(1) ||
+ !users.result[0].userId
+ ) {
+ return { error: "Could not send Password Reset Link" };
+ }
+ const userId = users.result[0].userId;
+
+ const basePath = process.env.NEXT_PUBLIC_BASE_PATH ?? "";
+
+ return passwordReset({
+ serviceUrl,
+ userId,
+ urlTemplate:
+ `${host.includes("localhost") ? "http://" : "https://"}${host}${basePath}/password/set?code={{.Code}}&userId={{.UserID}}&organization={{.OrgID}}` +
+ (command.requestId ? `&requestId=${command.requestId}` : ""),
+ });
+}
+
+export type UpdateSessionCommand = {
+ loginName: string;
+ organization?: string;
+ checks: Checks;
+ requestId?: string;
+};
+
+export async function sendPassword(command: UpdateSessionCommand) {
+ const _headers = await headers();
+ const { serviceUrl } = getServiceUrlFromHeaders(_headers);
+
+ let sessionCookie = await getSessionCookieByLoginName({
+ loginName: command.loginName,
+ organization: command.organization,
+ }).catch((error) => {
+ console.warn("Ignored error:", error);
+ });
+
+ let session;
+ let user: User;
+ let loginSettings: LoginSettings | undefined;
+
+ if (!sessionCookie) {
+ const users = await listUsers({
+ serviceUrl,
+ loginName: command.loginName,
+ organizationId: command.organization,
+ });
+
+ if (users.details?.totalResult == BigInt(1) && users.result[0].userId) {
+ user = users.result[0];
+
+ const checks = create(ChecksSchema, {
+ user: { search: { case: "userId", value: users.result[0].userId } },
+ password: { password: command.checks.password?.password },
+ });
+
+ loginSettings = await getLoginSettings({
+ serviceUrl,
+ organization: command.organization,
+ });
+
+ try {
+ session = await createSessionAndUpdateCookie({
+ checks,
+ requestId: command.requestId,
+ lifetime: loginSettings?.passwordCheckLifetime,
+ });
+ } catch (error: any) {
+ if ("failedAttempts" in error && error.failedAttempts) {
+ const lockoutSettings = await getLockoutSettings({
+ serviceUrl,
+ orgId: command.organization,
+ });
+
+ return {
+ error:
+ `Failed to authenticate. You had ${error.failedAttempts} of ${lockoutSettings?.maxPasswordAttempts} password attempts.` +
+ (lockoutSettings?.maxPasswordAttempts &&
+ error.failedAttempts >= lockoutSettings?.maxPasswordAttempts
+ ? "Contact your administrator to unlock your account"
+ : ""),
+ };
+ }
+ return { error: "Could not create session for user" };
+ }
+ }
+
+ // this is a fake error message to hide that the user does not even exist
+ return { error: "Could not verify password" };
+ } else {
+ try {
+ session = await setSessionAndUpdateCookie(
+ sessionCookie,
+ command.checks,
+ undefined,
+ command.requestId,
+ loginSettings?.passwordCheckLifetime,
+ );
+ } catch (error: any) {
+ if ("failedAttempts" in error && error.failedAttempts) {
+ const lockoutSettings = await getLockoutSettings({
+ serviceUrl,
+ orgId: command.organization,
+ });
+
+ return {
+ error:
+ `Failed to authenticate. You had ${error.failedAttempts} of ${lockoutSettings?.maxPasswordAttempts} password attempts.` +
+ (lockoutSettings?.maxPasswordAttempts &&
+ error.failedAttempts >= lockoutSettings?.maxPasswordAttempts
+ ? " Contact your administrator to unlock your account"
+ : ""),
+ };
+ }
+ throw error;
+ }
+
+ if (!session?.factors?.user?.id) {
+ return { error: "Could not create session for user" };
+ }
+
+ const userResponse = await getUserByID({
+ serviceUrl,
+ userId: session?.factors?.user?.id,
+ });
+
+ if (!userResponse.user) {
+ return { error: "User not found in the system" };
+ }
+
+ user = userResponse.user;
+ }
+
+ if (!loginSettings) {
+ loginSettings = await getLoginSettings({
+ serviceUrl,
+ organization:
+ command.organization ?? session.factors?.user?.organizationId,
+ });
+ }
+
+ if (!session?.factors?.user?.id || !sessionCookie) {
+ return { error: "Could not create session for user" };
+ }
+
+ const humanUser = user.type.case === "human" ? user.type.value : undefined;
+
+ const expirySettings = await getPasswordExpirySettings({
+ serviceUrl,
+ orgId: command.organization ?? session.factors?.user?.organizationId,
+ });
+
+ // check if the user has to change password first
+ const passwordChangedCheck = checkPasswordChangeRequired(
+ expirySettings,
+ session,
+ humanUser,
+ command.organization,
+ command.requestId,
+ );
+
+ if (passwordChangedCheck?.redirect) {
+ return passwordChangedCheck;
+ }
+
+ // throw error if user is in initial state here and do not continue
+ if (user.state === UserState.INITIAL) {
+ return { error: "Initial User not supported" };
+ }
+
+ // check to see if user was verified
+ const emailVerificationCheck = checkEmailVerification(
+ session,
+ humanUser,
+ command.organization,
+ command.requestId,
+ );
+
+ if (emailVerificationCheck?.redirect) {
+ return emailVerificationCheck;
+ }
+
+ // if password, check if user has MFA methods
+ let authMethods;
+ if (command.checks && command.checks.password && session.factors?.user?.id) {
+ const response = await listAuthenticationMethodTypes({
+ serviceUrl,
+ userId: session.factors.user.id,
+ });
+ if (response.authMethodTypes && response.authMethodTypes.length) {
+ authMethods = response.authMethodTypes;
+ }
+ }
+
+ if (!authMethods) {
+ return { error: "Could not verify password!" };
+ }
+
+ const mfaFactorCheck = await checkMFAFactors(
+ serviceUrl,
+ session,
+ loginSettings,
+ authMethods,
+ command.organization,
+ command.requestId,
+ );
+
+ if (mfaFactorCheck?.redirect) {
+ return mfaFactorCheck;
+ }
+
+ if (command.requestId && session.id) {
+ const nextUrl = await getNextUrl(
+ {
+ sessionId: session.id,
+ requestId: command.requestId,
+ organization:
+ command.organization ?? session.factors?.user?.organizationId,
+ },
+ loginSettings?.defaultRedirectUri,
+ );
+
+ return { redirect: nextUrl };
+ }
+
+ const url = await getNextUrl(
+ {
+ loginName: session.factors.user.loginName,
+ organization: session.factors?.user?.organizationId,
+ },
+ loginSettings?.defaultRedirectUri,
+ );
+
+ return { redirect: url };
+}
+
+// this function lets users with code set a password or users with valid User Verification Check
+export async function changePassword(command: {
+ code?: string;
+ userId: string;
+ password: string;
+}) {
+ const _headers = await headers();
+ const { serviceUrl } = getServiceUrlFromHeaders(_headers);
+
+ // check for init state
+ const { user } = await getUserByID({
+ serviceUrl,
+ userId: command.userId,
+ });
+
+ if (!user || user.userId !== command.userId) {
+ return { error: "Could not send Password Reset Link" };
+ }
+ const userId = user.userId;
+
+ if (user.state === UserState.INITIAL) {
+ return { error: "User Initial State is not supported" };
+ }
+
+ // check if the user has no password set in order to set a password
+ if (!command.code) {
+ const authmethods = await listAuthenticationMethodTypes({
+ serviceUrl,
+ userId,
+ });
+
+ // if the user has no authmethods set, we need to check if the user was verified
+ if (authmethods.authMethodTypes.length !== 0) {
+ return {
+ error:
+ "You have to provide a code or have a valid User Verification Check",
+ };
+ }
+
+ // check if a verification was done earlier
+ const hasValidUserVerificationCheck = await checkUserVerification(
+ user.userId,
+ );
+
+ if (!hasValidUserVerificationCheck) {
+ return { error: "User Verification Check has to be done" };
+ }
+ }
+
+ return setUserPassword({
+ serviceUrl,
+ userId,
+ password: command.password,
+ code: command.code,
+ });
+}
+
+type CheckSessionAndSetPasswordCommand = {
+ sessionId: string;
+ password: string;
+};
+
+export async function checkSessionAndSetPassword({
+ sessionId,
+ password,
+}: CheckSessionAndSetPasswordCommand) {
+ const _headers = await headers();
+ const { serviceUrl } = getServiceUrlFromHeaders(_headers);
+
+ const sessionCookie = await getSessionCookieById({ sessionId });
+
+ const { session } = await getSession({
+ serviceUrl,
+ sessionId: sessionCookie.id,
+ sessionToken: sessionCookie.token,
+ });
+
+ if (!session || !session.factors?.user?.id) {
+ return { error: "Could not load session" };
+ }
+
+ const payload = create(SetPasswordRequestSchema, {
+ userId: session.factors.user.id,
+ newPassword: {
+ password,
+ },
+ });
+
+ // check if the user has no password set in order to set a password
+ const authmethods = await listAuthenticationMethodTypes({
+ serviceUrl,
+ userId: session.factors.user.id,
+ });
+
+ if (!authmethods) {
+ return { error: "Could not load auth methods" };
+ }
+
+ const requiredAuthMethodsForForceMFA = [
+ AuthenticationMethodType.OTP_EMAIL,
+ AuthenticationMethodType.OTP_SMS,
+ AuthenticationMethodType.TOTP,
+ AuthenticationMethodType.U2F,
+ ];
+
+ const hasNoMFAMethods = requiredAuthMethodsForForceMFA.every(
+ (method) => !authmethods.authMethodTypes.includes(method),
+ );
+
+ const loginSettings = await getLoginSettings({
+ serviceUrl,
+ organization: session.factors.user.organizationId,
+ });
+
+ const forceMfa = !!(
+ loginSettings?.forceMfa || loginSettings?.forceMfaLocalOnly
+ );
+
+ // if the user has no MFA but MFA is enforced, we can set a password otherwise we use the token of the user
+ if (forceMfa && hasNoMFAMethods) {
+ return setPassword({ serviceUrl, payload }).catch((error) => {
+ // throw error if failed precondition (ex. User is not yet initialized)
+ if (error.code === 9 && error.message) {
+ return { error: "Failed precondition" };
+ } else {
+ throw error;
+ }
+ });
+ } else {
+ const transport = async (serviceUrl: string, token: string) => {
+ return createServerTransport(token, serviceUrl);
+ };
+
+ const myUserService = async (serviceUrl: string, sessionToken: string) => {
+ const transportPromise = await transport(serviceUrl, sessionToken);
+ return createUserServiceClient(transportPromise);
+ };
+
+ const selfService = await myUserService(
+ serviceUrl,
+ `${sessionCookie.token}`,
+ );
+
+ return selfService
+ .setPassword(
+ {
+ userId: session.factors.user.id,
+ newPassword: { password, changeRequired: false },
+ },
+ {},
+ )
+ .catch((error: ConnectError) => {
+ console.log(error);
+ if (error.code === 7) {
+ return { error: "Session is not valid." };
+ }
+ throw error;
+ });
+ }
+}
diff --git a/login/apps/login/src/lib/server/register.ts b/login/apps/login/src/lib/server/register.ts
new file mode 100644
index 0000000000..f84b4c8d51
--- /dev/null
+++ b/login/apps/login/src/lib/server/register.ts
@@ -0,0 +1,233 @@
+"use server";
+
+import {
+ createSessionAndUpdateCookie,
+ createSessionForIdpAndUpdateCookie,
+} from "@/lib/server/cookie";
+import {
+ addHumanUser,
+ addIDPLink,
+ getLoginSettings,
+ getUserByID,
+} from "@/lib/zitadel";
+import { create } from "@zitadel/client";
+import { Factors } from "@zitadel/proto/zitadel/session/v2/session_pb";
+import {
+ ChecksJson,
+ ChecksSchema,
+} from "@zitadel/proto/zitadel/session/v2/session_service_pb";
+import { headers } from "next/headers";
+import { getNextUrl } from "../client";
+import { getServiceUrlFromHeaders } from "../service-url";
+import { checkEmailVerification } from "../verify-helper";
+
+type RegisterUserCommand = {
+ email: string;
+ firstName: string;
+ lastName: string;
+ password?: string;
+ organization: string;
+ requestId?: string;
+};
+
+export type RegisterUserResponse = {
+ userId: string;
+ sessionId: string;
+ factors: Factors | undefined;
+};
+export async function registerUser(command: RegisterUserCommand) {
+ const _headers = await headers();
+ const { serviceUrl } = getServiceUrlFromHeaders(_headers);
+ const host = _headers.get("host");
+
+ if (!host || typeof host !== "string") {
+ throw new Error("No host found");
+ }
+
+ const addResponse = await addHumanUser({
+ serviceUrl,
+ email: command.email,
+ firstName: command.firstName,
+ lastName: command.lastName,
+ password: command.password ? command.password : undefined,
+ organization: command.organization,
+ });
+
+ if (!addResponse) {
+ return { error: "Could not create user" };
+ }
+
+ const loginSettings = await getLoginSettings({
+ serviceUrl,
+ organization: command.organization,
+ });
+
+ let checkPayload: any = {
+ user: { search: { case: "userId", value: addResponse.userId } },
+ };
+
+ if (command.password) {
+ checkPayload = {
+ ...checkPayload,
+ password: { password: command.password },
+ } as ChecksJson;
+ }
+
+ const checks = create(ChecksSchema, checkPayload);
+
+ const session = await createSessionAndUpdateCookie({
+ checks,
+ requestId: command.requestId,
+ lifetime: command.password
+ ? loginSettings?.passwordCheckLifetime
+ : undefined,
+ });
+
+ if (!session || !session.factors?.user) {
+ return { error: "Could not create session" };
+ }
+
+ if (!command.password) {
+ const params = new URLSearchParams({
+ loginName: session.factors.user.loginName,
+ organization: session.factors.user.organizationId,
+ });
+
+ if (command.requestId) {
+ params.append("requestId", command.requestId);
+ }
+
+ return { redirect: "/passkey/set?" + params };
+ } else {
+ const userResponse = await getUserByID({
+ serviceUrl,
+ userId: session?.factors?.user?.id,
+ });
+
+ if (!userResponse.user) {
+ return { error: "User not found in the system" };
+ }
+
+ const humanUser =
+ userResponse.user.type.case === "human"
+ ? userResponse.user.type.value
+ : undefined;
+
+ const emailVerificationCheck = checkEmailVerification(
+ session,
+ humanUser,
+ session.factors.user.organizationId,
+ command.requestId,
+ );
+
+ if (emailVerificationCheck?.redirect) {
+ return emailVerificationCheck;
+ }
+
+ const url = await getNextUrl(
+ command.requestId && session.id
+ ? {
+ sessionId: session.id,
+ requestId: command.requestId,
+ organization: session.factors.user.organizationId,
+ }
+ : {
+ loginName: session.factors.user.loginName,
+ organization: session.factors.user.organizationId,
+ },
+ loginSettings?.defaultRedirectUri,
+ );
+
+ return { redirect: url };
+ }
+}
+
+type RegisterUserAndLinkToIDPommand = {
+ email: string;
+ firstName: string;
+ lastName: string;
+ organization: string;
+ requestId?: string;
+ idpIntent: {
+ idpIntentId: string;
+ idpIntentToken: string;
+ };
+ idpUserId: string;
+ idpId: string;
+ idpUserName: string;
+};
+
+export type registerUserAndLinkToIDPResponse = {
+ userId: string;
+ sessionId: string;
+ factors: Factors | undefined;
+};
+export async function registerUserAndLinkToIDP(
+ command: RegisterUserAndLinkToIDPommand,
+) {
+ const _headers = await headers();
+ const { serviceUrl } = getServiceUrlFromHeaders(_headers);
+ const host = _headers.get("host");
+
+ if (!host || typeof host !== "string") {
+ throw new Error("No host found");
+ }
+
+ const addResponse = await addHumanUser({
+ serviceUrl,
+ email: command.email,
+ firstName: command.firstName,
+ lastName: command.lastName,
+ organization: command.organization,
+ });
+
+ if (!addResponse) {
+ return { error: "Could not create user" };
+ }
+
+ const loginSettings = await getLoginSettings({
+ serviceUrl,
+ organization: command.organization,
+ });
+
+ const idpLink = await addIDPLink({
+ serviceUrl,
+ idp: {
+ id: command.idpId,
+ userId: command.idpUserId,
+ userName: command.idpUserName,
+ },
+ userId: addResponse.userId,
+ });
+
+ if (!idpLink) {
+ return { error: "Could not link IDP to user" };
+ }
+
+ const session = await createSessionForIdpAndUpdateCookie({
+ requestId: command.requestId,
+ userId: addResponse.userId, // the user we just created
+ idpIntent: command.idpIntent,
+ lifetime: loginSettings?.externalLoginCheckLifetime,
+ });
+
+ if (!session || !session.factors?.user) {
+ return { error: "Could not create session" };
+ }
+
+ const url = await getNextUrl(
+ command.requestId && session.id
+ ? {
+ sessionId: session.id,
+ requestId: command.requestId,
+ organization: session.factors.user.organizationId,
+ }
+ : {
+ loginName: session.factors.user.loginName,
+ organization: session.factors.user.organizationId,
+ },
+ loginSettings?.defaultRedirectUri,
+ );
+
+ return { redirect: url };
+}
diff --git a/login/apps/login/src/lib/server/session.ts b/login/apps/login/src/lib/server/session.ts
new file mode 100644
index 0000000000..2aceb3a1d0
--- /dev/null
+++ b/login/apps/login/src/lib/server/session.ts
@@ -0,0 +1,221 @@
+"use server";
+
+import { setSessionAndUpdateCookie } from "@/lib/server/cookie";
+import {
+ deleteSession,
+ getLoginSettings,
+ getSecuritySettings,
+ humanMFAInitSkipped,
+ listAuthenticationMethodTypes,
+} from "@/lib/zitadel";
+import { Duration } from "@zitadel/client";
+import { RequestChallenges } from "@zitadel/proto/zitadel/session/v2/challenge_pb";
+import { Session } from "@zitadel/proto/zitadel/session/v2/session_pb";
+import { Checks } from "@zitadel/proto/zitadel/session/v2/session_service_pb";
+import { headers } from "next/headers";
+import { getNextUrl } from "../client";
+import {
+ getMostRecentSessionCookie,
+ getSessionCookieById,
+ getSessionCookieByLoginName,
+ removeSessionFromCookie,
+} from "../cookies";
+import { getServiceUrlFromHeaders } from "../service-url";
+
+export async function skipMFAAndContinueWithNextUrl({
+ userId,
+ requestId,
+ loginName,
+ sessionId,
+ organization,
+}: {
+ userId: string;
+ loginName?: string;
+ sessionId?: string;
+ requestId?: string;
+ organization?: string;
+}) {
+ const _headers = await headers();
+ const { serviceUrl } = getServiceUrlFromHeaders(_headers);
+
+ const loginSettings = await getLoginSettings({
+ serviceUrl,
+ organization: organization,
+ });
+
+ await humanMFAInitSkipped({ serviceUrl, userId });
+
+ const url =
+ requestId && sessionId
+ ? await getNextUrl(
+ {
+ sessionId: sessionId,
+ requestId: requestId,
+ organization: organization,
+ },
+ loginSettings?.defaultRedirectUri,
+ )
+ : loginName
+ ? await getNextUrl(
+ {
+ loginName: loginName,
+ organization: organization,
+ },
+ loginSettings?.defaultRedirectUri,
+ )
+ : null;
+ if (url) {
+ return { redirect: url };
+ }
+}
+
+export async function continueWithSession({
+ requestId,
+ ...session
+}: Session & { requestId?: string }) {
+ const _headers = await headers();
+ const { serviceUrl } = getServiceUrlFromHeaders(_headers);
+
+ const loginSettings = await getLoginSettings({
+ serviceUrl,
+ organization: session.factors?.user?.organizationId,
+ });
+
+ const url =
+ requestId && session.id && session.factors?.user
+ ? await getNextUrl(
+ {
+ sessionId: session.id,
+ requestId: requestId,
+ organization: session.factors.user.organizationId,
+ },
+ loginSettings?.defaultRedirectUri,
+ )
+ : session.factors?.user
+ ? await getNextUrl(
+ {
+ loginName: session.factors.user.loginName,
+ organization: session.factors.user.organizationId,
+ },
+ loginSettings?.defaultRedirectUri,
+ )
+ : null;
+ if (url) {
+ return { redirect: url };
+ }
+}
+
+export type UpdateSessionCommand = {
+ loginName?: string;
+ sessionId?: string;
+ organization?: string;
+ checks?: Checks;
+ requestId?: string;
+ challenges?: RequestChallenges;
+ lifetime?: Duration;
+};
+
+export async function updateSession(options: UpdateSessionCommand) {
+ let { loginName, sessionId, organization, checks, requestId, challenges } =
+ options;
+ const recentSession = sessionId
+ ? await getSessionCookieById({ sessionId })
+ : loginName
+ ? await getSessionCookieByLoginName({ loginName, organization })
+ : await getMostRecentSessionCookie();
+
+ if (!recentSession) {
+ return {
+ error: "Could not find session",
+ };
+ }
+
+ const _headers = await headers();
+ const { serviceUrl } = getServiceUrlFromHeaders(_headers);
+ const host = _headers.get("host");
+
+ if (!host) {
+ return { error: "Could not get host" };
+ }
+
+ if (
+ host &&
+ challenges &&
+ challenges.webAuthN &&
+ !challenges.webAuthN.domain
+ ) {
+ const [hostname, port] = host.split(":");
+
+ challenges.webAuthN.domain = hostname;
+ }
+
+ const loginSettings = await getLoginSettings({
+ serviceUrl,
+ organization,
+ });
+
+ const lifetime = checks?.webAuthN
+ ? loginSettings?.multiFactorCheckLifetime // TODO different lifetime for webauthn u2f/passkey
+ : checks?.otpEmail || checks?.otpSms
+ ? loginSettings?.secondFactorCheckLifetime
+ : undefined;
+
+ const session = await setSessionAndUpdateCookie(
+ recentSession,
+ checks,
+ challenges,
+ requestId,
+ lifetime,
+ );
+
+ if (!session) {
+ return { error: "Could not update session" };
+ }
+
+ // if password, check if user has MFA methods
+ let authMethods;
+ if (checks && checks.password && session.factors?.user?.id) {
+ const response = await listAuthenticationMethodTypes({
+ serviceUrl,
+ userId: session.factors.user.id,
+ });
+ if (response.authMethodTypes && response.authMethodTypes.length) {
+ authMethods = response.authMethodTypes;
+ }
+ }
+
+ return {
+ sessionId: session.id,
+ factors: session.factors,
+ challenges: session.challenges,
+ authMethods,
+ };
+}
+
+type ClearSessionOptions = {
+ sessionId: string;
+};
+
+export async function clearSession(options: ClearSessionOptions) {
+ const _headers = await headers();
+ const { serviceUrl } = getServiceUrlFromHeaders(_headers);
+
+ const { sessionId } = options;
+
+ const sessionCookie = await getSessionCookieById({ sessionId });
+
+ const deleteResponse = await deleteSession({
+ serviceUrl,
+ sessionId: sessionCookie.id,
+ sessionToken: sessionCookie.token,
+ });
+
+ const securitySettings = await getSecuritySettings({ serviceUrl });
+ const sameSite = securitySettings?.embeddedIframe?.enabled ? "none" : true;
+
+ if (!deleteResponse) {
+ throw new Error("Could not delete session");
+ }
+
+ return removeSessionFromCookie({ session: sessionCookie, sameSite });
+}
diff --git a/login/apps/login/src/lib/server/u2f.ts b/login/apps/login/src/lib/server/u2f.ts
new file mode 100644
index 0000000000..3fe5194336
--- /dev/null
+++ b/login/apps/login/src/lib/server/u2f.ts
@@ -0,0 +1,103 @@
+"use server";
+
+import { getSession, registerU2F, verifyU2FRegistration } from "@/lib/zitadel";
+import { create } from "@zitadel/client";
+import { VerifyU2FRegistrationRequestSchema } from "@zitadel/proto/zitadel/user/v2/user_service_pb";
+import { headers } from "next/headers";
+import { userAgent } from "next/server";
+import { getSessionCookieById } from "../cookies";
+import { getServiceUrlFromHeaders } from "../service-url";
+
+type RegisterU2FCommand = {
+ sessionId: string;
+};
+
+type VerifyU2FCommand = {
+ u2fId: string;
+ passkeyName?: string;
+ publicKeyCredential: any;
+ sessionId: string;
+};
+
+export async function addU2F(command: RegisterU2FCommand) {
+ const _headers = await headers();
+ const { serviceUrl } = getServiceUrlFromHeaders(_headers);
+ const host = _headers.get("host");
+
+ if (!host || typeof host !== "string") {
+ throw new Error("No host found");
+ }
+
+ const sessionCookie = await getSessionCookieById({
+ sessionId: command.sessionId,
+ });
+
+ if (!sessionCookie) {
+ return { error: "Could not get session" };
+ }
+
+ const session = await getSession({
+ serviceUrl,
+ sessionId: sessionCookie.id,
+ sessionToken: sessionCookie.token,
+ });
+
+ const [hostname, port] = host.split(":");
+
+ if (!hostname) {
+ throw new Error("Could not get hostname");
+ }
+
+ const userId = session?.session?.factors?.user?.id;
+
+ if (!session || !userId) {
+ return { error: "Could not get session" };
+ }
+
+ return registerU2F({ serviceUrl, userId, domain: hostname });
+}
+
+export async function verifyU2F(command: VerifyU2FCommand) {
+ const _headers = await headers();
+ const { serviceUrl } = getServiceUrlFromHeaders(_headers);
+ const host = _headers.get("host");
+
+ if (!host || typeof host !== "string") {
+ throw new Error("No host found");
+ }
+
+ let passkeyName = command.passkeyName;
+ if (!!!passkeyName) {
+ const headersList = await headers();
+ const userAgentStructure = { headers: headersList };
+ const { browser, device, os } = userAgent(userAgentStructure);
+
+ passkeyName = `${device.vendor ?? ""} ${device.model ?? ""}${
+ device.vendor || device.model ? ", " : ""
+ }${os.name}${os.name ? ", " : ""}${browser.name}`;
+ }
+ const sessionCookie = await getSessionCookieById({
+ sessionId: command.sessionId,
+ });
+
+ const session = await getSession({
+ serviceUrl,
+ sessionId: sessionCookie.id,
+ sessionToken: sessionCookie.token,
+ });
+
+ const userId = session?.session?.factors?.user?.id;
+
+ if (!userId) {
+ return { error: "Could not get session" };
+ }
+
+ const request = create(VerifyU2FRegistrationRequestSchema, {
+ u2fId: command.u2fId,
+ publicKeyCredential: command.publicKeyCredential,
+ tokenName: passkeyName,
+ userId,
+ });
+
+ return verifyU2FRegistration({ serviceUrl, request });
+}
diff --git a/login/apps/login/src/lib/server/verify.ts b/login/apps/login/src/lib/server/verify.ts
new file mode 100644
index 0000000000..cf60f739b3
--- /dev/null
+++ b/login/apps/login/src/lib/server/verify.ts
@@ -0,0 +1,329 @@
+"use server";
+
+import {
+ createInviteCode,
+ getLoginSettings,
+ getSession,
+ getUserByID,
+ listAuthenticationMethodTypes,
+ verifyEmail,
+ verifyInviteCode,
+ verifyTOTPRegistration,
+ sendEmailCode as zitadelSendEmailCode,
+} from "@/lib/zitadel";
+import crypto from "crypto";
+
+import { create } from "@zitadel/client";
+import { Session } from "@zitadel/proto/zitadel/session/v2/session_pb";
+import { ChecksSchema } from "@zitadel/proto/zitadel/session/v2/session_service_pb";
+import { cookies, headers } from "next/headers";
+import { getNextUrl } from "../client";
+import { getSessionCookieByLoginName } from "../cookies";
+import { getOrSetFingerprintId } from "../fingerprint";
+import { getServiceUrlFromHeaders } from "../service-url";
+import { loadMostRecentSession } from "../session";
+import { checkMFAFactors } from "../verify-helper";
+import { createSessionAndUpdateCookie } from "./cookie";
+
+export async function verifyTOTP(
+ code: string,
+ loginName?: string,
+ organization?: string,
+) {
+ const _headers = await headers();
+ const { serviceUrl } = getServiceUrlFromHeaders(_headers);
+
+ return loadMostRecentSession({
+ serviceUrl,
+ sessionParams: {
+ loginName,
+ organization,
+ },
+ }).then((session) => {
+ if (session?.factors?.user?.id) {
+ return verifyTOTPRegistration({
+ serviceUrl,
+ code,
+ userId: session.factors.user.id,
+ });
+ } else {
+ throw Error("No user id found in session.");
+ }
+ });
+}
+
+type VerifyUserByEmailCommand = {
+ userId: string;
+ loginName?: string; // to determine already existing session
+ organization?: string;
+ code: string;
+ isInvite: boolean;
+ requestId?: string;
+};
+
+export async function sendVerification(command: VerifyUserByEmailCommand) {
+ const _headers = await headers();
+ const { serviceUrl } = getServiceUrlFromHeaders(_headers);
+
+ const verifyResponse = command.isInvite
+ ? await verifyInviteCode({
+ serviceUrl,
+ userId: command.userId,
+ verificationCode: command.code,
+ }).catch((error) => {
+ console.warn(error);
+ return { error: "Could not verify invite" };
+ })
+ : await verifyEmail({
+ serviceUrl,
+ userId: command.userId,
+ verificationCode: command.code,
+ }).catch((error) => {
+ console.warn(error);
+ return { error: "Could not verify email" };
+ });
+
+ if ("error" in verifyResponse) {
+ return verifyResponse;
+ }
+
+ if (!verifyResponse) {
+ return { error: "Could not verify" };
+ }
+
+ let session: Session | undefined;
+ const userResponse = await getUserByID({
+ serviceUrl,
+ userId: command.userId,
+ });
+
+ if (!userResponse || !userResponse.user) {
+ return { error: "Could not load user" };
+ }
+
+ const user = userResponse.user;
+
+ const sessionCookie = await getSessionCookieByLoginName({
+ loginName:
+ "loginName" in command ? command.loginName : user.preferredLoginName,
+ organization: command.organization,
+ }).catch((error) => {
+ console.warn("Ignored error:", error); // checked later
+ });
+
+ if (sessionCookie) {
+ session = await getSession({
+ serviceUrl,
+ sessionId: sessionCookie.id,
+ sessionToken: sessionCookie.token,
+ }).then((response) => {
+ if (response?.session) {
+ return response.session;
+ }
+ });
+ }
+
+ // load auth methods for user
+ const authMethodResponse = await listAuthenticationMethodTypes({
+ serviceUrl,
+ userId: user.userId,
+ });
+
+ if (!authMethodResponse || !authMethodResponse.authMethodTypes) {
+ return { error: "Could not load possible authenticators" };
+ }
+
+ // if no authmethods are found on the user, redirect to set one up
+ if (
+ authMethodResponse &&
+ authMethodResponse.authMethodTypes &&
+ authMethodResponse.authMethodTypes.length == 0
+ ) {
+ if (!sessionCookie) {
+ const checks = create(ChecksSchema, {
+ user: {
+ search: {
+ case: "loginName",
+ value: userResponse.user.preferredLoginName,
+ },
+ },
+ });
+
+ session = await createSessionAndUpdateCookie({
+ checks,
+ requestId: command.requestId,
+ });
+ }
+
+ if (!session) {
+ return { error: "Could not create session" };
+ }
+
+ const params = new URLSearchParams({
+ sessionId: session.id,
+ });
+
+ if (session.factors?.user?.loginName) {
+ params.set("loginName", session.factors?.user?.loginName);
+ }
+
+ // set hash of userId and userAgentId to prevent attacks, checks are done for users with invalid sessions and invalid userAgentId
+ const cookiesList = await cookies();
+ const userAgentId = await getOrSetFingerprintId();
+
+ const verificationCheck = crypto
+ .createHash("sha256")
+ .update(`${user.userId}:${userAgentId}`)
+ .digest("hex");
+
+ await cookiesList.set({
+ name: "verificationCheck",
+ value: verificationCheck,
+ httpOnly: true,
+ path: "/",
+ maxAge: 300, // 5 minutes
+ });
+
+ return { redirect: `/authenticator/set?${params}` };
+ }
+
+ // if no session found only show success page,
+ // if user is invited, recreate invite flow to not depend on session
+ if (!session?.factors?.user?.id) {
+ const verifySuccessParams = new URLSearchParams({});
+
+ if (command.userId) {
+ verifySuccessParams.set("userId", command.userId);
+ }
+
+ if (
+ ("loginName" in command && command.loginName) ||
+ user.preferredLoginName
+ ) {
+ verifySuccessParams.set(
+ "loginName",
+ "loginName" in command && command.loginName
+ ? command.loginName
+ : user.preferredLoginName,
+ );
+ }
+ if (command.requestId) {
+ verifySuccessParams.set("requestId", command.requestId);
+ }
+ if (command.organization) {
+ verifySuccessParams.set("organization", command.organization);
+ }
+
+ return { redirect: `/verify/success?${verifySuccessParams}` };
+ }
+
+ const loginSettings = await getLoginSettings({
+ serviceUrl,
+ organization: user.details?.resourceOwner,
+ });
+
+ // redirect to mfa factor if user has one, or redirect to set one up
+ const mfaFactorCheck = await checkMFAFactors(
+ serviceUrl,
+ session,
+ loginSettings,
+ authMethodResponse.authMethodTypes,
+ command.organization,
+ command.requestId,
+ );
+
+ if (mfaFactorCheck?.redirect) {
+ return mfaFactorCheck;
+ }
+
+ // login user if no additional steps are required
+ if (command.requestId && session.id) {
+ const nextUrl = await getNextUrl(
+ {
+ sessionId: session.id,
+ requestId: command.requestId,
+ organization:
+ command.organization ?? session.factors?.user?.organizationId,
+ },
+ loginSettings?.defaultRedirectUri,
+ );
+
+ return { redirect: nextUrl };
+ }
+
+ const url = await getNextUrl(
+ {
+ loginName: session.factors.user.loginName,
+ organization: session.factors?.user?.organizationId,
+ },
+ loginSettings?.defaultRedirectUri,
+ );
+
+ return { redirect: url };
+}
+
+type resendVerifyEmailCommand = {
+ userId: string;
+ isInvite: boolean;
+ requestId?: string;
+};
+
+export async function resendVerification(command: resendVerifyEmailCommand) {
+ const _headers = await headers();
+ const { serviceUrl } = getServiceUrlFromHeaders(_headers);
+ const host = _headers.get("host");
+
+ if (!host) {
+ return { error: "No host found" };
+ }
+
+ const basePath = process.env.NEXT_PUBLIC_BASE_PATH ?? "";
+
+ return command.isInvite
+ ? createInviteCode({
+ serviceUrl,
+ userId: command.userId,
+ urlTemplate:
+ `${host.includes("localhost") ? "http://" : "https://"}${host}${basePath}/verify?code={{.Code}}&userId={{.UserID}}&organization={{.OrgID}}&invite=true` +
+ (command.requestId ? `&requestId=${command.requestId}` : ""),
+ }).catch((error) => {
+ if (error.code === 9) {
+ return { error: "User is already verified!" };
+ }
+ return { error: "Could not resend invite" };
+ })
+ : zitadelSendEmailCode({
+ userId: command.userId,
+ serviceUrl,
+ urlTemplate:
+ `${host.includes("localhost") ? "http://" : "https://"}${host}${basePath}/verify?code={{.Code}}&userId={{.UserID}}&organization={{.OrgID}}` +
+ (command.requestId ? `&requestId=${command.requestId}` : ""),
+ });
+}
+
+type SendEmailCommand = {
+ userId: string;
+ urlTemplate: string;
+};
+
+export async function sendEmailCode(command: SendEmailCommand) {
+ const _headers = await headers();
+ const { serviceUrl } = getServiceUrlFromHeaders(_headers);
+
+ return zitadelSendEmailCode({
+ serviceUrl,
+ userId: command.userId,
+ urlTemplate: command.urlTemplate,
+ });
+}
+
+export async function sendInviteEmailCode(command: SendEmailCommand) {
+ const _headers = await headers();
+ const { serviceUrl } = getServiceUrlFromHeaders(_headers);
+
+ return createInviteCode({
+ serviceUrl,
+ userId: command.userId,
+ urlTemplate: command.urlTemplate,
+ });
+}
diff --git a/login/apps/login/src/lib/service-url.ts b/login/apps/login/src/lib/service-url.ts
new file mode 100644
index 0000000000..e74ee1f333
--- /dev/null
+++ b/login/apps/login/src/lib/service-url.ts
@@ -0,0 +1,58 @@
+import { ReadonlyHeaders } from "next/dist/server/web/spec-extension/adapters/headers";
+import { NextRequest } from "next/server";
+
+/**
+ * Extracts the service url and region from the headers if used in a multitenant context (host, x-zitadel-forward-host header)
+ * or falls back to the ZITADEL_API_URL for a self hosting deployment
+ * or falls back to the host header for a self hosting deployment using custom domains
+ * @param headers
+ * @returns the service url and region from the headers
+ * @throws if the service url could not be determined
+ *
+ */
+export function getServiceUrlFromHeaders(headers: ReadonlyHeaders): {
+ serviceUrl: string;
+} {
+ let instanceUrl;
+
+ const forwardedHost = headers.get("x-zitadel-forward-host");
+ // use the forwarded host if available (multitenant), otherwise fall back to the host of the deployment itself
+ if (forwardedHost) {
+ instanceUrl = forwardedHost;
+ instanceUrl = instanceUrl.startsWith("http://")
+ ? instanceUrl
+ : `https://${instanceUrl}`;
+ } else if (process.env.ZITADEL_API_URL) {
+ instanceUrl = process.env.ZITADEL_API_URL;
+ } else {
+ const host = headers.get("host");
+
+ if (host) {
+ const [hostname, port] = host.split(":");
+ if (hostname !== "localhost") {
+ instanceUrl = host.startsWith("http") ? host : `https://${host}`;
+ }
+ }
+ }
+
+ if (!instanceUrl) {
+ throw new Error("Service URL could not be determined");
+ }
+
+ return {
+ serviceUrl: instanceUrl,
+ };
+}
+
+export function constructUrl(request: NextRequest, path: string) {
+ const forwardedProto = request.headers.get("x-forwarded-proto")
+ ? `${request.headers.get("x-forwarded-proto")}:`
+ : request.nextUrl.protocol;
+
+ const forwardedHost =
+ request.headers.get("x-zitadel-forward-host") ??
+ request.headers.get("x-forwarded-host") ??
+ request.headers.get("host");
+ const basePath = process.env.NEXT_PUBLIC_BASE_PATH || "";
+ return new URL(`${basePath}${path}`, `${forwardedProto}//${forwardedHost}`);
+}
diff --git a/login/apps/login/src/lib/service.ts b/login/apps/login/src/lib/service.ts
new file mode 100644
index 0000000000..f7e81cc9d6
--- /dev/null
+++ b/login/apps/login/src/lib/service.ts
@@ -0,0 +1,49 @@
+import { createClientFor } from "@zitadel/client";
+import { IdentityProviderService } from "@zitadel/proto/zitadel/idp/v2/idp_service_pb";
+import { OIDCService } from "@zitadel/proto/zitadel/oidc/v2/oidc_service_pb";
+import { OrganizationService } from "@zitadel/proto/zitadel/org/v2/org_service_pb";
+import { SAMLService } from "@zitadel/proto/zitadel/saml/v2/saml_service_pb";
+import { SessionService } from "@zitadel/proto/zitadel/session/v2/session_service_pb";
+import { SettingsService } from "@zitadel/proto/zitadel/settings/v2/settings_service_pb";
+import { UserService } from "@zitadel/proto/zitadel/user/v2/user_service_pb";
+import { systemAPIToken } from "./api";
+import { createServerTransport } from "./zitadel";
+
+type ServiceClass =
+ | typeof IdentityProviderService
+ | typeof UserService
+ | typeof OrganizationService
+ | typeof SessionService
+ | typeof OIDCService
+ | typeof SettingsService
+ | typeof SAMLService;
+
+export async function createServiceForHost(
+ service: T,
+ serviceUrl: string,
+) {
+ let token;
+
+ // if we are running in a multitenancy context, use the system user token
+ if (
+ process.env.AUDIENCE &&
+ process.env.SYSTEM_USER_ID &&
+ process.env.SYSTEM_USER_PRIVATE_KEY
+ ) {
+ token = await systemAPIToken();
+ } else if (process.env.ZITADEL_SERVICE_USER_TOKEN) {
+ token = process.env.ZITADEL_SERVICE_USER_TOKEN;
+ }
+
+ if (!serviceUrl) {
+ throw new Error("No instance url found");
+ }
+
+ if (!token) {
+ throw new Error("No token found");
+ }
+
+ const transport = createServerTransport(token, serviceUrl);
+
+ return createClientFor(service)(transport);
+}
diff --git a/login/apps/login/src/lib/session.ts b/login/apps/login/src/lib/session.ts
new file mode 100644
index 0000000000..9698c4c4ba
--- /dev/null
+++ b/login/apps/login/src/lib/session.ts
@@ -0,0 +1,194 @@
+import { timestampDate } from "@zitadel/client";
+import { AuthRequest } from "@zitadel/proto/zitadel/oidc/v2/authorization_pb";
+import { SAMLRequest } from "@zitadel/proto/zitadel/saml/v2/authorization_pb";
+import { Session } from "@zitadel/proto/zitadel/session/v2/session_pb";
+import { GetSessionResponse } from "@zitadel/proto/zitadel/session/v2/session_service_pb";
+import { AuthenticationMethodType } from "@zitadel/proto/zitadel/user/v2/user_service_pb";
+import { getMostRecentCookieWithLoginname } from "./cookies";
+import {
+ getLoginSettings,
+ getSession,
+ listAuthenticationMethodTypes,
+} from "./zitadel";
+
+type LoadMostRecentSessionParams = {
+ serviceUrl: string;
+
+ sessionParams: {
+ loginName?: string;
+ organization?: string;
+ };
+};
+
+export async function loadMostRecentSession({
+ serviceUrl,
+ sessionParams,
+}: LoadMostRecentSessionParams): Promise {
+ const recent = await getMostRecentCookieWithLoginname({
+ loginName: sessionParams.loginName,
+ organization: sessionParams.organization,
+ });
+
+ return getSession({
+ serviceUrl,
+ sessionId: recent.id,
+ sessionToken: recent.token,
+ }).then((resp: GetSessionResponse) => resp.session);
+}
+
+/**
+ * mfa is required, session is not valid anymore (e.g. session expired, user logged out, etc.)
+ * to check for mfa for automatically selected session -> const response = await listAuthenticationMethodTypes(userId);
+ **/
+export async function isSessionValid({
+ serviceUrl,
+ session,
+}: {
+ serviceUrl: string;
+ session: Session;
+}): Promise {
+ // session can't be checked without user
+ if (!session.factors?.user) {
+ console.warn("Session has no user");
+ return false;
+ }
+
+ let mfaValid = true;
+
+ const authMethodTypes = await listAuthenticationMethodTypes({
+ serviceUrl,
+ userId: session.factors.user.id,
+ });
+
+ const authMethods = authMethodTypes.authMethodTypes;
+ if (authMethods && authMethods.includes(AuthenticationMethodType.TOTP)) {
+ mfaValid = !!session.factors.totp?.verifiedAt;
+ if (!mfaValid) {
+ console.warn(
+ "Session has no valid totpEmail factor",
+ session.factors.totp?.verifiedAt,
+ );
+ }
+ } else if (
+ authMethods &&
+ authMethods.includes(AuthenticationMethodType.OTP_EMAIL)
+ ) {
+ mfaValid = !!session.factors.otpEmail?.verifiedAt;
+ if (!mfaValid) {
+ console.warn(
+ "Session has no valid otpEmail factor",
+ session.factors.otpEmail?.verifiedAt,
+ );
+ }
+ } else if (
+ authMethods &&
+ authMethods.includes(AuthenticationMethodType.OTP_SMS)
+ ) {
+ mfaValid = !!session.factors.otpSms?.verifiedAt;
+ if (!mfaValid) {
+ console.warn(
+ "Session has no valid otpSms factor",
+ session.factors.otpSms?.verifiedAt,
+ );
+ }
+ } else if (
+ authMethods &&
+ authMethods.includes(AuthenticationMethodType.U2F)
+ ) {
+ mfaValid = !!session.factors.webAuthN?.verifiedAt;
+ if (!mfaValid) {
+ console.warn(
+ "Session has no valid u2f factor",
+ session.factors.webAuthN?.verifiedAt,
+ );
+ }
+ } else {
+ // only check settings if no auth methods are available, as this would require a setup
+ const loginSettings = await getLoginSettings({
+ serviceUrl,
+ organization: session.factors?.user?.organizationId,
+ });
+ if (loginSettings?.forceMfa || loginSettings?.forceMfaLocalOnly) {
+ const otpEmail = session.factors.otpEmail?.verifiedAt;
+ const otpSms = session.factors.otpSms?.verifiedAt;
+ const totp = session.factors.totp?.verifiedAt;
+ const webAuthN = session.factors.webAuthN?.verifiedAt;
+ const idp = session.factors.intent?.verifiedAt; // TODO: forceMFA should not consider this as valid factor
+
+ // must have one single check
+ mfaValid = !!(otpEmail || otpSms || totp || webAuthN || idp);
+ if (!mfaValid) {
+ console.warn("Session has no valid multifactor", session.factors);
+ }
+ } else {
+ mfaValid = true;
+ }
+ }
+
+ const validPassword = session?.factors?.password?.verifiedAt;
+ const validPasskey = session?.factors?.webAuthN?.verifiedAt;
+ const validIDP = session?.factors?.intent?.verifiedAt;
+
+ const stillValid = session.expirationDate
+ ? timestampDate(session.expirationDate).getTime() > new Date().getTime()
+ : true;
+
+ if (!stillValid) {
+ console.warn(
+ "Session is expired",
+ session.expirationDate
+ ? timestampDate(session.expirationDate).toDateString()
+ : "no expiration date",
+ );
+ }
+
+ const validChecks = !!(validPassword || validPasskey || validIDP);
+
+ return stillValid && validChecks && mfaValid;
+}
+
+export async function findValidSession({
+ serviceUrl,
+ sessions,
+ authRequest,
+ samlRequest,
+}: {
+ serviceUrl: string;
+ sessions: Session[];
+ authRequest?: AuthRequest;
+ samlRequest?: SAMLRequest;
+}): Promise {
+ const sessionsWithHint = sessions.filter((s) => {
+ if (authRequest && authRequest.hintUserId) {
+ return s.factors?.user?.id === authRequest.hintUserId;
+ }
+ if (authRequest && authRequest.loginHint) {
+ return s.factors?.user?.loginName === authRequest.loginHint;
+ }
+ if (samlRequest) {
+ // TODO: do whatever
+ return true;
+ }
+ return true;
+ });
+
+ if (sessionsWithHint.length === 0) {
+ return undefined;
+ }
+
+ // sort by change date descending
+ sessionsWithHint.sort((a, b) => {
+ const dateA = a.changeDate ? timestampDate(a.changeDate).getTime() : 0;
+ const dateB = b.changeDate ? timestampDate(b.changeDate).getTime() : 0;
+ return dateB - dateA;
+ });
+
+ // return the first valid session according to settings
+ for (const session of sessionsWithHint) {
+ if (await isSessionValid({ serviceUrl, session })) {
+ return session;
+ }
+ }
+
+ return undefined;
+}
diff --git a/login/apps/login/src/lib/verify-helper.ts b/login/apps/login/src/lib/verify-helper.ts
new file mode 100644
index 0000000000..dbd9b2796b
--- /dev/null
+++ b/login/apps/login/src/lib/verify-helper.ts
@@ -0,0 +1,289 @@
+import { timestampDate } from "@zitadel/client";
+import { Session } from "@zitadel/proto/zitadel/session/v2/session_pb";
+import { LoginSettings } from "@zitadel/proto/zitadel/settings/v2/login_settings_pb";
+import { PasswordExpirySettings } from "@zitadel/proto/zitadel/settings/v2/password_settings_pb";
+import { HumanUser } from "@zitadel/proto/zitadel/user/v2/user_pb";
+import { AuthenticationMethodType } from "@zitadel/proto/zitadel/user/v2/user_service_pb";
+import crypto from "crypto";
+import moment from "moment";
+import { cookies } from "next/headers";
+import { getFingerprintIdCookie } from "./fingerprint";
+import { getUserByID } from "./zitadel";
+
+export function checkPasswordChangeRequired(
+ expirySettings: PasswordExpirySettings | undefined,
+ session: Session,
+ humanUser: HumanUser | undefined,
+ organization?: string,
+ requestId?: string,
+) {
+ let isOutdated = false;
+ if (expirySettings?.maxAgeDays && humanUser?.passwordChanged) {
+ const maxAgeDays = Number(expirySettings.maxAgeDays); // Convert bigint to number
+ const passwordChangedDate = moment(
+ timestampDate(humanUser.passwordChanged),
+ );
+ const outdatedPassword = passwordChangedDate.add(maxAgeDays, "days");
+ isOutdated = moment().isAfter(outdatedPassword);
+ }
+
+ if (humanUser?.passwordChangeRequired || isOutdated) {
+ const params = new URLSearchParams({
+ loginName: session.factors?.user?.loginName as string,
+ });
+
+ if (organization || session.factors?.user?.organizationId) {
+ params.append(
+ "organization",
+ session.factors?.user?.organizationId as string,
+ );
+ }
+
+ if (requestId) {
+ params.append("requestId", requestId);
+ }
+
+ return { redirect: "/password/change?" + params };
+ }
+}
+
+export function checkEmailVerified(
+ session: Session,
+ humanUser?: HumanUser,
+ organization?: string,
+ requestId?: string,
+) {
+ if (!humanUser?.email?.isVerified) {
+ const paramsVerify = new URLSearchParams({
+ loginName: session.factors?.user?.loginName as string,
+ userId: session.factors?.user?.id as string, // verify needs user id
+ send: "true", // we request a new email code once the page is loaded
+ });
+
+ if (organization || session.factors?.user?.organizationId) {
+ paramsVerify.append(
+ "organization",
+ organization ?? (session.factors?.user?.organizationId as string),
+ );
+ }
+
+ if (requestId) {
+ paramsVerify.append("requestId", requestId);
+ }
+
+ return { redirect: "/verify?" + paramsVerify };
+ }
+}
+
+export function checkEmailVerification(
+ session: Session,
+ humanUser?: HumanUser,
+ organization?: string,
+ requestId?: string,
+) {
+ if (
+ !humanUser?.email?.isVerified &&
+ process.env.EMAIL_VERIFICATION === "true"
+ ) {
+ const params = new URLSearchParams({
+ loginName: session.factors?.user?.loginName as string,
+ send: "true", // set this to true as we dont expect old email codes to be valid anymore
+ });
+
+ if (requestId) {
+ params.append("requestId", requestId);
+ }
+
+ if (organization || session.factors?.user?.organizationId) {
+ params.append(
+ "organization",
+ organization ?? (session.factors?.user?.organizationId as string),
+ );
+ }
+
+ return { redirect: `/verify?` + params };
+ }
+}
+
+export async function checkMFAFactors(
+ serviceUrl: string,
+ session: Session,
+ loginSettings: LoginSettings | undefined,
+ authMethods: AuthenticationMethodType[],
+ organization?: string,
+ requestId?: string,
+) {
+ const availableMultiFactors = authMethods?.filter(
+ (m: AuthenticationMethodType) =>
+ m !== AuthenticationMethodType.PASSWORD &&
+ m !== AuthenticationMethodType.PASSKEY,
+ );
+
+ const hasAuthenticatedWithPasskey =
+ session.factors?.webAuthN?.verifiedAt &&
+ session.factors?.webAuthN?.userVerified;
+
+ // escape further checks if user has authenticated with passkey
+ if (hasAuthenticatedWithPasskey) {
+ return;
+ }
+
+ // if user has not authenticated with passkey and has only one additional mfa factor, redirect to that
+ if (availableMultiFactors?.length == 1) {
+ const params = new URLSearchParams({
+ loginName: session.factors?.user?.loginName as string,
+ });
+
+ if (requestId) {
+ params.append("requestId", requestId);
+ }
+
+ if (organization || session.factors?.user?.organizationId) {
+ params.append(
+ "organization",
+ organization ?? (session.factors?.user?.organizationId as string),
+ );
+ }
+
+ const factor = availableMultiFactors[0];
+ // if passwordless is other method, but user selected password as alternative, perform a login
+ if (factor === AuthenticationMethodType.TOTP) {
+ return { redirect: `/otp/time-based?` + params };
+ } else if (factor === AuthenticationMethodType.OTP_SMS) {
+ return { redirect: `/otp/sms?` + params };
+ } else if (factor === AuthenticationMethodType.OTP_EMAIL) {
+ return { redirect: `/otp/email?` + params };
+ } else if (factor === AuthenticationMethodType.U2F) {
+ return { redirect: `/u2f?` + params };
+ }
+ } else if (availableMultiFactors?.length > 1) {
+ const params = new URLSearchParams({
+ loginName: session.factors?.user?.loginName as string,
+ });
+
+ if (requestId) {
+ params.append("requestId", requestId);
+ }
+
+ if (organization || session.factors?.user?.organizationId) {
+ params.append(
+ "organization",
+ organization ?? (session.factors?.user?.organizationId as string),
+ );
+ }
+
+ return { redirect: `/mfa?` + params };
+ } else if (
+ (loginSettings?.forceMfa || loginSettings?.forceMfaLocalOnly) &&
+ !availableMultiFactors.length
+ ) {
+ const params = new URLSearchParams({
+ loginName: session.factors?.user?.loginName as string,
+ force: "true", // this defines if the mfa is forced in the settings
+ checkAfter: "true", // this defines if the check is directly made after the setup
+ });
+
+ if (requestId) {
+ params.append("requestId", requestId);
+ }
+
+ if (organization || session.factors?.user?.organizationId) {
+ params.append(
+ "organization",
+ organization ?? (session.factors?.user?.organizationId as string),
+ );
+ }
+
+ // TODO: provide a way to setup passkeys on mfa page?
+ return { redirect: `/mfa/set?` + params };
+ } else if (
+ loginSettings?.mfaInitSkipLifetime &&
+ (loginSettings.mfaInitSkipLifetime.nanos > 0 ||
+ loginSettings.mfaInitSkipLifetime.seconds > 0) &&
+ !availableMultiFactors.length &&
+ session?.factors?.user?.id
+ ) {
+ const userResponse = await getUserByID({
+ serviceUrl,
+ userId: session.factors?.user?.id,
+ });
+
+ const humanUser =
+ userResponse?.user?.type.case === "human"
+ ? userResponse?.user.type.value
+ : undefined;
+
+ if (humanUser?.mfaInitSkipped) {
+ const mfaInitSkippedTimestamp = timestampDate(humanUser.mfaInitSkipped);
+
+ const mfaInitSkipLifetimeMillis =
+ Number(loginSettings.mfaInitSkipLifetime.seconds) * 1000 +
+ loginSettings.mfaInitSkipLifetime.nanos / 1000000;
+ const currentTime = Date.now();
+ const mfaInitSkippedTime = mfaInitSkippedTimestamp.getTime();
+ const timeDifference = currentTime - mfaInitSkippedTime;
+
+ if (!(timeDifference > mfaInitSkipLifetimeMillis)) {
+ // if the time difference is smaller than the lifetime, skip the mfa setup
+ return;
+ }
+ }
+
+ // the user has never skipped the mfa init but we have a setting so we redirect
+
+ const params = new URLSearchParams({
+ loginName: session.factors?.user?.loginName as string,
+ force: "false", // this defines if the mfa is not forced in the settings and can be skipped
+ checkAfter: "true", // this defines if the check is directly made after the setup
+ });
+
+ if (requestId) {
+ params.append("requestId", requestId);
+ }
+
+ if (organization || session.factors?.user?.organizationId) {
+ params.append(
+ "organization",
+ organization ?? (session.factors?.user?.organizationId as string),
+ );
+ }
+
+ // TODO: provide a way to setup passkeys on mfa page?
+ return { redirect: `/mfa/set?` + params };
+ }
+}
+
+export async function checkUserVerification(userId: string): Promise {
+ // check if a verification was done earlier
+ const cookiesList = await cookies();
+
+ // only read cookie to prevent issues on page.tsx
+ const fingerPrintCookie = await getFingerprintIdCookie();
+
+ if (!fingerPrintCookie || !fingerPrintCookie.value) {
+ return false;
+ }
+
+ const verificationCheck = crypto
+ .createHash("sha256")
+ .update(`${userId}:${fingerPrintCookie.value}`)
+ .digest("hex");
+
+ const cookieValue = await cookiesList.get("verificationCheck")?.value;
+
+ if (!cookieValue) {
+ console.warn(
+ "User verification check cookie not found. User verification check failed.",
+ );
+ return false;
+ }
+
+ if (cookieValue !== verificationCheck) {
+ console.warn(
+ `User verification check failed. Expected ${verificationCheck} but got ${cookieValue}`,
+ );
+ return false;
+ }
+
+ return true;
+}
diff --git a/login/apps/login/src/lib/zitadel.ts b/login/apps/login/src/lib/zitadel.ts
new file mode 100644
index 0000000000..483d4e4ac9
--- /dev/null
+++ b/login/apps/login/src/lib/zitadel.ts
@@ -0,0 +1,1525 @@
+import { Client, create, Duration } from "@zitadel/client";
+import { createServerTransport as libCreateServerTransport } from "@zitadel/client/node";
+import { makeReqCtx } from "@zitadel/client/v2";
+import { IdentityProviderService } from "@zitadel/proto/zitadel/idp/v2/idp_service_pb";
+import {
+ OrganizationSchema,
+ TextQueryMethod,
+} from "@zitadel/proto/zitadel/object/v2/object_pb";
+import {
+ CreateCallbackRequest,
+ OIDCService,
+} from "@zitadel/proto/zitadel/oidc/v2/oidc_service_pb";
+import { Organization } from "@zitadel/proto/zitadel/org/v2/org_pb";
+import { OrganizationService } from "@zitadel/proto/zitadel/org/v2/org_service_pb";
+import {
+ CreateResponseRequest,
+ SAMLService,
+} from "@zitadel/proto/zitadel/saml/v2/saml_service_pb";
+import { RequestChallenges } from "@zitadel/proto/zitadel/session/v2/challenge_pb";
+import {
+ Checks,
+ SessionService,
+} from "@zitadel/proto/zitadel/session/v2/session_service_pb";
+import { LoginSettings } from "@zitadel/proto/zitadel/settings/v2/login_settings_pb";
+import { SettingsService } from "@zitadel/proto/zitadel/settings/v2/settings_service_pb";
+import { SendEmailVerificationCodeSchema } from "@zitadel/proto/zitadel/user/v2/email_pb";
+import type {
+ FormData,
+ RedirectURLsJson,
+} from "@zitadel/proto/zitadel/user/v2/idp_pb";
+import {
+ NotificationType,
+ SendPasswordResetLinkSchema,
+} from "@zitadel/proto/zitadel/user/v2/password_pb";
+import {
+ SearchQuery,
+ SearchQuerySchema,
+} from "@zitadel/proto/zitadel/user/v2/query_pb";
+import { SendInviteCodeSchema } from "@zitadel/proto/zitadel/user/v2/user_pb";
+import {
+ AddHumanUserRequest,
+ AddHumanUserRequestSchema,
+ ResendEmailCodeRequest,
+ ResendEmailCodeRequestSchema,
+ SendEmailCodeRequestSchema,
+ SetPasswordRequest,
+ SetPasswordRequestSchema,
+ UpdateHumanUserRequest,
+ UserService,
+ VerifyPasskeyRegistrationRequest,
+ VerifyU2FRegistrationRequest,
+} from "@zitadel/proto/zitadel/user/v2/user_service_pb";
+import { unstable_cacheLife as cacheLife } from "next/cache";
+import { getUserAgent } from "./fingerprint";
+import { createServiceForHost } from "./service";
+
+const useCache = process.env.DEBUG !== "true";
+
+async function cacheWrapper(callback: Promise) {
+ "use cache";
+ cacheLife("hours");
+
+ return callback;
+}
+
+export async function getHostedLoginTranslation({
+ serviceUrl,
+ organization,
+ locale,
+}: {
+ serviceUrl: string;
+ organization?: string;
+ locale?: string;
+}) {
+ const settingsService: Client =
+ await createServiceForHost(SettingsService, serviceUrl);
+
+ const callback = settingsService
+ .getHostedLoginTranslation(
+ {
+ level: organization
+ ? {
+ case: "organizationId",
+ value: organization,
+ }
+ : {
+ case: "instance",
+ value: true,
+ },
+ locale: locale,
+ },
+ {},
+ )
+ .then((resp) => {
+ return resp.translations ? resp.translations : undefined;
+ });
+
+ return useCache ? cacheWrapper(callback) : callback;
+}
+
+export async function getBrandingSettings({
+ serviceUrl,
+ organization,
+}: {
+ serviceUrl: string;
+ organization?: string;
+}) {
+ const settingsService: Client =
+ await createServiceForHost(SettingsService, serviceUrl);
+
+ const callback = settingsService
+ .getBrandingSettings({ ctx: makeReqCtx(organization) }, {})
+ .then((resp) => (resp.settings ? resp.settings : undefined));
+
+ return useCache ? cacheWrapper(callback) : callback;
+}
+
+export async function getLoginSettings({
+ serviceUrl,
+ organization,
+}: {
+ serviceUrl: string;
+ organization?: string;
+}) {
+ const settingsService: Client =
+ await createServiceForHost(SettingsService, serviceUrl);
+
+ const callback = settingsService
+ .getLoginSettings({ ctx: makeReqCtx(organization) }, {})
+ .then((resp) => (resp.settings ? resp.settings : undefined));
+
+ return useCache ? cacheWrapper(callback) : callback;
+}
+
+export async function getSecuritySettings({
+ serviceUrl,
+}: {
+ serviceUrl: string;
+}) {
+ const settingsService: Client =
+ await createServiceForHost(SettingsService, serviceUrl);
+
+ const callback = settingsService
+ .getSecuritySettings({})
+ .then((resp) => (resp.settings ? resp.settings : undefined));
+
+ return useCache ? cacheWrapper(callback) : callback;
+}
+
+export async function getLockoutSettings({
+ serviceUrl,
+ orgId,
+}: {
+ serviceUrl: string;
+ orgId?: string;
+}) {
+ const settingsService: Client =
+ await createServiceForHost(SettingsService, serviceUrl);
+
+ const callback = settingsService
+ .getLockoutSettings({ ctx: makeReqCtx(orgId) }, {})
+ .then((resp) => (resp.settings ? resp.settings : undefined));
+
+ return useCache ? cacheWrapper(callback) : callback;
+}
+
+export async function getPasswordExpirySettings({
+ serviceUrl,
+ orgId,
+}: {
+ serviceUrl: string;
+ orgId?: string;
+}) {
+ const settingsService: Client =
+ await createServiceForHost(SettingsService, serviceUrl);
+
+ const callback = settingsService
+ .getPasswordExpirySettings({ ctx: makeReqCtx(orgId) }, {})
+ .then((resp) => (resp.settings ? resp.settings : undefined));
+
+ return useCache ? cacheWrapper(callback) : callback;
+}
+
+export async function listIDPLinks({
+ serviceUrl,
+ userId,
+}: {
+ serviceUrl: string;
+ userId: string;
+}) {
+ const userService: Client = await createServiceForHost(
+ UserService,
+ serviceUrl,
+ );
+
+ return userService.listIDPLinks({ userId }, {});
+}
+
+export async function addOTPEmail({
+ serviceUrl,
+ userId,
+}: {
+ serviceUrl: string;
+ userId: string;
+}) {
+ const userService: Client = await createServiceForHost(
+ UserService,
+ serviceUrl,
+ );
+
+ return userService.addOTPEmail({ userId }, {});
+}
+
+export async function addOTPSMS({
+ serviceUrl,
+ userId,
+}: {
+ serviceUrl: string;
+ userId: string;
+}) {
+ const userService: Client = await createServiceForHost(
+ UserService,
+ serviceUrl,
+ );
+
+ return userService.addOTPSMS({ userId }, {});
+}
+
+export async function registerTOTP({
+ serviceUrl,
+ userId,
+}: {
+ serviceUrl: string;
+ userId: string;
+}) {
+ const userService: Client = await createServiceForHost(
+ UserService,
+ serviceUrl,
+ );
+
+ return userService.registerTOTP({ userId }, {});
+}
+
+export async function getGeneralSettings({
+ serviceUrl,
+}: {
+ serviceUrl: string;
+}) {
+ const settingsService: Client =
+ await createServiceForHost(SettingsService, serviceUrl);
+
+ const callback = settingsService
+ .getGeneralSettings({}, {})
+ .then((resp) => resp.supportedLanguages);
+
+ return useCache ? cacheWrapper(callback) : callback;
+}
+
+export async function getLegalAndSupportSettings({
+ serviceUrl,
+ organization,
+}: {
+ serviceUrl: string;
+ organization?: string;
+}) {
+ const settingsService: Client =
+ await createServiceForHost(SettingsService, serviceUrl);
+
+ const callback = settingsService
+ .getLegalAndSupportSettings({ ctx: makeReqCtx(organization) }, {})
+ .then((resp) => (resp.settings ? resp.settings : undefined));
+
+ return useCache ? cacheWrapper(callback) : callback;
+}
+
+export async function getPasswordComplexitySettings({
+ serviceUrl,
+ organization,
+}: {
+ serviceUrl: string;
+ organization?: string;
+}) {
+ const settingsService: Client =
+ await createServiceForHost(SettingsService, serviceUrl);
+
+ const callback = settingsService
+ .getPasswordComplexitySettings({ ctx: makeReqCtx(organization) })
+ .then((resp) => (resp.settings ? resp.settings : undefined));
+
+ return useCache ? cacheWrapper(callback) : callback;
+}
+
+export async function createSessionFromChecks({
+ serviceUrl,
+ checks,
+ lifetime,
+}: {
+ serviceUrl: string;
+ checks: Checks;
+ lifetime?: Duration;
+}) {
+ const sessionService: Client =
+ await createServiceForHost(SessionService, serviceUrl);
+
+ const userAgent = await getUserAgent();
+
+ return sessionService.createSession({ checks, lifetime, userAgent }, {});
+}
+
+export async function createSessionForUserIdAndIdpIntent({
+ serviceUrl,
+ userId,
+ idpIntent,
+ lifetime,
+}: {
+ serviceUrl: string;
+ userId: string;
+ idpIntent: {
+ idpIntentId?: string | undefined;
+ idpIntentToken?: string | undefined;
+ };
+ lifetime?: Duration;
+}) {
+ const sessionService: Client =
+ await createServiceForHost(SessionService, serviceUrl);
+
+ const userAgent = await getUserAgent();
+
+ return sessionService.createSession({
+ checks: {
+ user: {
+ search: {
+ case: "userId",
+ value: userId,
+ },
+ },
+ idpIntent,
+ },
+ lifetime,
+ userAgent,
+ });
+}
+
+export async function setSession({
+ serviceUrl,
+ sessionId,
+ sessionToken,
+ challenges,
+ checks,
+ lifetime,
+}: {
+ serviceUrl: string;
+ sessionId: string;
+ sessionToken: string;
+ challenges: RequestChallenges | undefined;
+ checks?: Checks;
+ lifetime?: Duration;
+}) {
+ const sessionService: Client =
+ await createServiceForHost(SessionService, serviceUrl);
+
+ return sessionService.setSession(
+ {
+ sessionId,
+ sessionToken,
+ challenges,
+ checks: checks ? checks : {},
+ metadata: {},
+ lifetime,
+ },
+ {},
+ );
+}
+
+export async function getSession({
+ serviceUrl,
+ sessionId,
+ sessionToken,
+}: {
+ serviceUrl: string;
+ sessionId: string;
+ sessionToken: string;
+}) {
+ const sessionService: Client =
+ await createServiceForHost(SessionService, serviceUrl);
+
+ return sessionService.getSession({ sessionId, sessionToken }, {});
+}
+
+export async function deleteSession({
+ serviceUrl,
+ sessionId,
+ sessionToken,
+}: {
+ serviceUrl: string;
+ sessionId: string;
+ sessionToken: string;
+}) {
+ const sessionService: Client =
+ await createServiceForHost(SessionService, serviceUrl);
+
+ return sessionService.deleteSession({ sessionId, sessionToken }, {});
+}
+
+type ListSessionsCommand = {
+ serviceUrl: string;
+ ids: string[];
+};
+
+export async function listSessions({ serviceUrl, ids }: ListSessionsCommand) {
+ const sessionService: Client =
+ await createServiceForHost(SessionService, serviceUrl);
+
+ return sessionService.listSessions(
+ {
+ queries: [
+ {
+ query: {
+ case: "idsQuery",
+ value: { ids },
+ },
+ },
+ ],
+ },
+ {},
+ );
+}
+
+export type AddHumanUserData = {
+ serviceUrl: string;
+ firstName: string;
+ lastName: string;
+ email: string;
+ password?: string;
+ organization: string;
+};
+
+export async function addHumanUser({
+ serviceUrl,
+ email,
+ firstName,
+ lastName,
+ password,
+ organization,
+}: AddHumanUserData) {
+ const userService: Client = await createServiceForHost(
+ UserService,
+ serviceUrl,
+ );
+
+ let addHumanUserRequest: AddHumanUserRequest = create(
+ AddHumanUserRequestSchema,
+ {
+ email: {
+ email,
+ verification: {
+ case: "isVerified",
+ value: false,
+ },
+ },
+ username: email,
+ profile: { givenName: firstName, familyName: lastName },
+ passwordType: password
+ ? { case: "password", value: { password } }
+ : undefined,
+ },
+ );
+
+ if (organization) {
+ const organizationSchema = create(OrganizationSchema, {
+ org: { case: "orgId", value: organization },
+ });
+
+ addHumanUserRequest = {
+ ...addHumanUserRequest,
+ organization: organizationSchema,
+ };
+ }
+
+ return userService.addHumanUser(addHumanUserRequest);
+}
+
+export async function addHuman({
+ serviceUrl,
+ request,
+}: {
+ serviceUrl: string;
+ request: AddHumanUserRequest;
+}) {
+ const userService: Client = await createServiceForHost(
+ UserService,
+ serviceUrl,
+ );
+
+ return userService.addHumanUser(request);
+}
+
+export async function updateHuman({
+ serviceUrl,
+ request,
+}: {
+ serviceUrl: string;
+ request: UpdateHumanUserRequest;
+}) {
+ const userService: Client = await createServiceForHost(
+ UserService,
+ serviceUrl,
+ );
+
+ return userService.updateHumanUser(request);
+}
+
+export async function verifyTOTPRegistration({
+ serviceUrl,
+ code,
+ userId,
+}: {
+ serviceUrl: string;
+ code: string;
+ userId: string;
+}) {
+ const userService: Client = await createServiceForHost(
+ UserService,
+ serviceUrl,
+ );
+
+ return userService.verifyTOTPRegistration({ code, userId }, {});
+}
+
+export async function getUserByID({
+ serviceUrl,
+ userId,
+}: {
+ serviceUrl: string;
+ userId: string;
+}) {
+ const userService: Client = await createServiceForHost(
+ UserService,
+ serviceUrl,
+ );
+
+ return userService.getUserByID({ userId }, {});
+}
+
+export async function humanMFAInitSkipped({
+ serviceUrl,
+ userId,
+}: {
+ serviceUrl: string;
+ userId: string;
+}) {
+ const userService: Client = await createServiceForHost(
+ UserService,
+ serviceUrl,
+ );
+
+ return userService.humanMFAInitSkipped({ userId }, {});
+}
+
+export async function verifyInviteCode({
+ serviceUrl,
+ userId,
+ verificationCode,
+}: {
+ serviceUrl: string;
+ userId: string;
+ verificationCode: string;
+}) {
+ const userService: Client = await createServiceForHost(
+ UserService,
+ serviceUrl,
+ );
+
+ return userService.verifyInviteCode({ userId, verificationCode }, {});
+}
+
+export async function sendEmailCode({
+ serviceUrl,
+ userId,
+ urlTemplate,
+}: {
+ serviceUrl: string;
+ userId: string;
+ urlTemplate: string;
+}) {
+ let medium = create(SendEmailCodeRequestSchema, { userId });
+
+ medium = create(SendEmailCodeRequestSchema, {
+ ...medium,
+ verification: {
+ case: "sendCode",
+ value: create(SendEmailVerificationCodeSchema, {
+ urlTemplate,
+ }),
+ },
+ });
+
+ const userService: Client = await createServiceForHost(
+ UserService,
+ serviceUrl,
+ );
+
+ return userService.sendEmailCode(medium, {});
+}
+
+export async function createInviteCode({
+ serviceUrl,
+ urlTemplate,
+ userId,
+}: {
+ serviceUrl: string;
+ urlTemplate: string;
+ userId: string;
+}) {
+ let medium = create(SendInviteCodeSchema, {
+ applicationName: "Typescript Login",
+ });
+
+ medium = {
+ ...medium,
+ urlTemplate,
+ };
+
+ const userService: Client = await createServiceForHost(
+ UserService,
+ serviceUrl,
+ );
+
+ return userService.createInviteCode(
+ {
+ userId,
+ verification: {
+ case: "sendCode",
+ value: medium,
+ },
+ },
+ {},
+ );
+}
+
+export type ListUsersCommand = {
+ serviceUrl: string;
+ loginName?: string;
+ userName?: string;
+ email?: string;
+ phone?: string;
+ organizationId?: string;
+};
+
+export async function listUsers({
+ serviceUrl,
+ loginName,
+ userName,
+ phone,
+ email,
+ organizationId,
+}: ListUsersCommand) {
+ const queries: SearchQuery[] = [];
+
+ // either use loginName or userName, email, phone
+ if (loginName) {
+ queries.push(
+ create(SearchQuerySchema, {
+ query: {
+ case: "loginNameQuery",
+ value: {
+ loginName,
+ method: TextQueryMethod.EQUALS,
+ },
+ },
+ }),
+ );
+ } else if (userName || email || phone) {
+ const orQueries: SearchQuery[] = [];
+
+ if (userName) {
+ const userNameQuery = create(SearchQuerySchema, {
+ query: {
+ case: "userNameQuery",
+ value: {
+ userName,
+ method: TextQueryMethod.EQUALS,
+ },
+ },
+ });
+ orQueries.push(userNameQuery);
+ }
+
+ if (email) {
+ const emailQuery = create(SearchQuerySchema, {
+ query: {
+ case: "emailQuery",
+ value: {
+ emailAddress: email,
+ method: TextQueryMethod.EQUALS,
+ },
+ },
+ });
+ orQueries.push(emailQuery);
+ }
+
+ if (phone) {
+ const phoneQuery = create(SearchQuerySchema, {
+ query: {
+ case: "phoneQuery",
+ value: {
+ number: phone,
+ method: TextQueryMethod.EQUALS,
+ },
+ },
+ });
+ orQueries.push(phoneQuery);
+ }
+
+ queries.push(
+ create(SearchQuerySchema, {
+ query: {
+ case: "orQuery",
+ value: {
+ queries: orQueries,
+ },
+ },
+ }),
+ );
+ }
+
+ if (organizationId) {
+ queries.push(
+ create(SearchQuerySchema, {
+ query: {
+ case: "organizationIdQuery",
+ value: {
+ organizationId,
+ },
+ },
+ }),
+ );
+ }
+
+ const userService: Client = await createServiceForHost(
+ UserService,
+ serviceUrl,
+ );
+
+ return userService.listUsers({ queries });
+}
+
+export type SearchUsersCommand = {
+ serviceUrl: string;
+ searchValue: string;
+ loginSettings: LoginSettings;
+ organizationId?: string;
+ suffix?: string;
+};
+
+const PhoneQuery = (searchValue: string) =>
+ create(SearchQuerySchema, {
+ query: {
+ case: "phoneQuery",
+ value: {
+ number: searchValue,
+ method: TextQueryMethod.EQUALS,
+ },
+ },
+ });
+
+const LoginNameQuery = (searchValue: string) =>
+ create(SearchQuerySchema, {
+ query: {
+ case: "loginNameQuery",
+ value: {
+ loginName: searchValue,
+ method: TextQueryMethod.EQUALS,
+ },
+ },
+ });
+
+const EmailQuery = (searchValue: string) =>
+ create(SearchQuerySchema, {
+ query: {
+ case: "emailQuery",
+ value: {
+ emailAddress: searchValue,
+ method: TextQueryMethod.EQUALS,
+ },
+ },
+ });
+
+/**
+ * this is a dedicated search function to search for users from the loginname page
+ * it searches users based on the loginName or userName and org suffix combination, and falls back to email and phone if no users are found
+ * */
+export async function searchUsers({
+ serviceUrl,
+ searchValue,
+ loginSettings,
+ organizationId,
+ suffix,
+}: SearchUsersCommand) {
+ const queries: SearchQuery[] = [];
+
+ // if a suffix is provided, we search for the userName concatenated with the suffix
+ if (suffix) {
+ const searchValueWithSuffix = `${searchValue}@${suffix}`;
+ const loginNameQuery = LoginNameQuery(searchValueWithSuffix);
+ queries.push(loginNameQuery);
+ } else {
+ const loginNameQuery = LoginNameQuery(searchValue);
+ queries.push(loginNameQuery);
+ }
+
+ if (organizationId) {
+ queries.push(
+ create(SearchQuerySchema, {
+ query: {
+ case: "organizationIdQuery",
+ value: {
+ organizationId,
+ },
+ },
+ }),
+ );
+ }
+
+ const userService: Client = await createServiceForHost(
+ UserService,
+ serviceUrl,
+ );
+
+ const loginNameResult = await userService.listUsers({ queries });
+
+ if (!loginNameResult || !loginNameResult.details) {
+ return { error: "An error occurred." };
+ }
+
+ if (loginNameResult.result.length > 1) {
+ return { error: "Multiple users found" };
+ }
+
+ if (loginNameResult.result.length == 1) {
+ return loginNameResult;
+ }
+
+ const emailAndPhoneQueries: SearchQuery[] = [];
+ if (
+ loginSettings.disableLoginWithEmail &&
+ loginSettings.disableLoginWithPhone
+ ) {
+ return { error: "User not found in the system" };
+ } else if (loginSettings.disableLoginWithEmail && searchValue.length <= 20) {
+ const phoneQuery = PhoneQuery(searchValue);
+ emailAndPhoneQueries.push(phoneQuery);
+ } else if (loginSettings.disableLoginWithPhone) {
+ const emailQuery = EmailQuery(searchValue);
+ emailAndPhoneQueries.push(emailQuery);
+ } else {
+ const emailAndPhoneOrQueries: SearchQuery[] = [];
+
+ const emailQuery = EmailQuery(searchValue);
+ emailAndPhoneOrQueries.push(emailQuery);
+
+ let phoneQuery;
+ if (searchValue.length <= 20) {
+ phoneQuery = PhoneQuery(searchValue);
+ emailAndPhoneOrQueries.push(phoneQuery);
+ }
+
+ emailAndPhoneQueries.push(
+ create(SearchQuerySchema, {
+ query: {
+ case: "orQuery",
+ value: {
+ queries: emailAndPhoneOrQueries,
+ },
+ },
+ }),
+ );
+ }
+
+ if (organizationId) {
+ queries.push(
+ create(SearchQuerySchema, {
+ query: {
+ case: "organizationIdQuery",
+ value: {
+ organizationId,
+ },
+ },
+ }),
+ );
+ }
+
+ const emailOrPhoneResult = await userService.listUsers({
+ queries: emailAndPhoneQueries,
+ });
+
+ if (!emailOrPhoneResult || !emailOrPhoneResult.details) {
+ return { error: "An error occurred." };
+ }
+
+ if (emailOrPhoneResult.result.length > 1) {
+ return { error: "Multiple users found." };
+ }
+
+ if (emailOrPhoneResult.result.length == 1) {
+ return loginNameResult;
+ }
+
+ return { error: "User not found in the system" };
+}
+
+export async function getDefaultOrg({
+ serviceUrl,
+}: {
+ serviceUrl: string;
+}): Promise {
+ const orgService: Client =
+ await createServiceForHost(OrganizationService, serviceUrl);
+
+ return orgService
+ .listOrganizations(
+ {
+ queries: [
+ {
+ query: {
+ case: "defaultQuery",
+ value: {},
+ },
+ },
+ ],
+ },
+ {},
+ )
+ .then((resp) => (resp?.result && resp.result[0] ? resp.result[0] : null));
+}
+
+export async function getOrgsByDomain({
+ serviceUrl,
+ domain,
+}: {
+ serviceUrl: string;
+ domain: string;
+}) {
+ const orgService: Client =
+ await createServiceForHost(OrganizationService, serviceUrl);
+
+ return orgService.listOrganizations(
+ {
+ queries: [
+ {
+ query: {
+ case: "domainQuery",
+ value: { domain, method: TextQueryMethod.EQUALS },
+ },
+ },
+ ],
+ },
+ {},
+ );
+}
+
+export async function startIdentityProviderFlow({
+ serviceUrl,
+ idpId,
+ urls,
+}: {
+ serviceUrl: string;
+ idpId: string;
+ urls: RedirectURLsJson;
+}): Promise {
+ const userService: Client = await createServiceForHost(
+ UserService,
+ serviceUrl,
+ );
+
+ return userService
+ .startIdentityProviderIntent({
+ idpId,
+ content: {
+ case: "urls",
+ value: urls,
+ },
+ })
+ .then((resp) => {
+ if (resp.nextStep.case === "authUrl" && resp.nextStep.value) {
+ return resp.nextStep.value;
+ } else if (resp.nextStep.case === "formData" && resp.nextStep.value) {
+ const formData: FormData = resp.nextStep.value;
+ const redirectUrl = "/saml-post";
+
+ const params = new URLSearchParams({ url: formData.url });
+
+ Object.entries(formData.fields).forEach(([k, v]) => {
+ params.append(k, v);
+ });
+
+ return `${redirectUrl}?${params.toString()}`;
+ } else {
+ return null;
+ }
+ });
+}
+
+export async function startLDAPIdentityProviderFlow({
+ serviceUrl,
+ idpId,
+ username,
+ password,
+}: {
+ serviceUrl: string;
+ idpId: string;
+ username: string;
+ password: string;
+}) {
+ const userService: Client = await createServiceForHost(
+ UserService,
+ serviceUrl,
+ );
+
+ return userService.startIdentityProviderIntent({
+ idpId,
+ content: {
+ case: "ldap",
+ value: {
+ username,
+ password,
+ },
+ },
+ });
+}
+
+export async function getAuthRequest({
+ serviceUrl,
+ authRequestId,
+}: {
+ serviceUrl: string;
+ authRequestId: string;
+}) {
+ const oidcService = await createServiceForHost(OIDCService, serviceUrl);
+
+ return oidcService.getAuthRequest({
+ authRequestId,
+ });
+}
+
+export async function getDeviceAuthorizationRequest({
+ serviceUrl,
+ userCode,
+}: {
+ serviceUrl: string;
+ userCode: string;
+}) {
+ const oidcService = await createServiceForHost(OIDCService, serviceUrl);
+
+ return oidcService.getDeviceAuthorizationRequest({
+ userCode,
+ });
+}
+
+export async function authorizeOrDenyDeviceAuthorization({
+ serviceUrl,
+ deviceAuthorizationId,
+ session,
+}: {
+ serviceUrl: string;
+ deviceAuthorizationId: string;
+ session?: { sessionId: string; sessionToken: string };
+}) {
+ const oidcService = await createServiceForHost(OIDCService, serviceUrl);
+
+ return oidcService.authorizeOrDenyDeviceAuthorization({
+ deviceAuthorizationId,
+ decision: session
+ ? {
+ case: "session",
+ value: session,
+ }
+ : {
+ case: "deny",
+ value: {},
+ },
+ });
+}
+
+export async function createCallback({
+ serviceUrl,
+ req,
+}: {
+ serviceUrl: string;
+ req: CreateCallbackRequest;
+}) {
+ const oidcService = await createServiceForHost(OIDCService, serviceUrl);
+
+ return oidcService.createCallback(req);
+}
+
+export async function getSAMLRequest({
+ serviceUrl,
+ samlRequestId,
+}: {
+ serviceUrl: string;
+ samlRequestId: string;
+}) {
+ const samlService = await createServiceForHost(SAMLService, serviceUrl);
+
+ return samlService.getSAMLRequest({
+ samlRequestId,
+ });
+}
+
+export async function createResponse({
+ serviceUrl,
+ req,
+}: {
+ serviceUrl: string;
+ req: CreateResponseRequest;
+}) {
+ const samlService = await createServiceForHost(SAMLService, serviceUrl);
+
+ return samlService.createResponse(req);
+}
+
+export async function verifyEmail({
+ serviceUrl,
+ userId,
+ verificationCode,
+}: {
+ serviceUrl: string;
+ userId: string;
+ verificationCode: string;
+}) {
+ const userService: Client = await createServiceForHost(
+ UserService,
+ serviceUrl,
+ );
+
+ return userService.verifyEmail(
+ {
+ userId,
+ verificationCode,
+ },
+ {},
+ );
+}
+
+export async function resendEmailCode({
+ serviceUrl,
+ userId,
+ urlTemplate,
+}: {
+ serviceUrl: string;
+ userId: string;
+ urlTemplate: string;
+}) {
+ let request: ResendEmailCodeRequest = create(ResendEmailCodeRequestSchema, {
+ userId,
+ });
+
+ const medium = create(SendEmailVerificationCodeSchema, {
+ urlTemplate,
+ });
+
+ request = { ...request, verification: { case: "sendCode", value: medium } };
+
+ const userService: Client = await createServiceForHost(
+ UserService,
+ serviceUrl,
+ );
+
+ return userService.resendEmailCode(request, {});
+}
+
+export async function retrieveIDPIntent({
+ serviceUrl,
+ id,
+ token,
+}: {
+ serviceUrl: string;
+ id: string;
+ token: string;
+}) {
+ const userService: Client = await createServiceForHost(
+ UserService,
+ serviceUrl,
+ );
+
+ return userService.retrieveIdentityProviderIntent(
+ { idpIntentId: id, idpIntentToken: token },
+ {},
+ );
+}
+
+export async function getIDPByID({
+ serviceUrl,
+ id,
+}: {
+ serviceUrl: string;
+ id: string;
+}) {
+ const idpService: Client =
+ await createServiceForHost(IdentityProviderService, serviceUrl);
+
+ return idpService.getIDPByID({ id }, {}).then((resp) => resp.idp);
+}
+
+export async function addIDPLink({
+ serviceUrl,
+ idp,
+ userId,
+}: {
+ serviceUrl: string;
+ idp: { id: string; userId: string; userName: string };
+ userId: string;
+}) {
+ const userService: Client = await createServiceForHost(
+ UserService,
+ serviceUrl,
+ );
+
+ return userService.addIDPLink(
+ {
+ idpLink: {
+ userId: idp.userId,
+ idpId: idp.id,
+ userName: idp.userName,
+ },
+ userId,
+ },
+ {},
+ );
+}
+
+export async function passwordReset({
+ serviceUrl,
+ userId,
+ urlTemplate,
+}: {
+ serviceUrl: string;
+ userId: string;
+ urlTemplate?: string;
+}) {
+ let medium = create(SendPasswordResetLinkSchema, {
+ notificationType: NotificationType.Email,
+ });
+
+ medium = {
+ ...medium,
+ urlTemplate,
+ };
+
+ const userService: Client = await createServiceForHost(
+ UserService,
+ serviceUrl,
+ );
+
+ return userService.passwordReset(
+ {
+ userId,
+ medium: {
+ case: "sendLink",
+ value: medium,
+ },
+ },
+ {},
+ );
+}
+
+export async function setUserPassword({
+ serviceUrl,
+ userId,
+ password,
+ code,
+}: {
+ serviceUrl: string;
+ userId: string;
+ password: string;
+ code?: string;
+}) {
+ let payload = create(SetPasswordRequestSchema, {
+ userId,
+ newPassword: {
+ password,
+ },
+ });
+
+ if (code) {
+ payload = {
+ ...payload,
+ verification: {
+ case: "verificationCode",
+ value: code,
+ },
+ };
+ }
+
+ const userService: Client = await createServiceForHost(
+ UserService,
+ serviceUrl,
+ );
+
+ return userService.setPassword(payload, {}).catch((error) => {
+ // throw error if failed precondition (ex. User is not yet initialized)
+ if (error.code === 9 && error.message) {
+ return { error: error.message };
+ } else {
+ throw error;
+ }
+ });
+}
+
+export async function setPassword({
+ serviceUrl,
+ payload,
+}: {
+ serviceUrl: string;
+ payload: SetPasswordRequest;
+}) {
+ const userService: Client = await createServiceForHost(
+ UserService,
+ serviceUrl,
+ );
+
+ return userService.setPassword(payload, {});
+}
+
+/**
+ *
+ * @param host
+ * @param userId the id of the user where the email should be set
+ * @returns the newly set email
+ */
+export async function createPasskeyRegistrationLink({
+ serviceUrl,
+ userId,
+}: {
+ serviceUrl: string;
+ userId: string;
+}) {
+ const userService: Client = await createServiceForHost(
+ UserService,
+ serviceUrl,
+ );
+
+ return userService.createPasskeyRegistrationLink({
+ userId,
+ medium: {
+ case: "returnCode",
+ value: {},
+ },
+ });
+}
+
+/**
+ *
+ * @param host
+ * @param userId the id of the user where the email should be set
+ * @param domain the domain on which the factor is registered
+ * @returns the newly set email
+ */
+export async function registerU2F({
+ serviceUrl,
+ userId,
+ domain,
+}: {
+ serviceUrl: string;
+ userId: string;
+ domain: string;
+}) {
+ const userService: Client = await createServiceForHost(
+ UserService,
+ serviceUrl,
+ );
+
+ return userService.registerU2F({
+ userId,
+ domain,
+ });
+}
+
+/**
+ *
+ * @param host
+ * @param request the request object for verifying U2F registration
+ * @returns the result of the verification
+ */
+export async function verifyU2FRegistration({
+ serviceUrl,
+ request,
+}: {
+ serviceUrl: string;
+ request: VerifyU2FRegistrationRequest;
+}) {
+ const userService: Client = await createServiceForHost(
+ UserService,
+ serviceUrl,
+ );
+
+ return userService.verifyU2FRegistration(request, {});
+}
+
+/**
+ *
+ * @param host
+ * @param orgId the organization ID
+ * @param linking_allowed whether linking is allowed
+ * @returns the active identity providers
+ */
+export async function getActiveIdentityProviders({
+ serviceUrl,
+ orgId,
+ linking_allowed,
+}: {
+ serviceUrl: string;
+ orgId?: string;
+ linking_allowed?: boolean;
+}) {
+ const props: any = { ctx: makeReqCtx(orgId) };
+ if (linking_allowed) {
+ props.linkingAllowed = linking_allowed;
+ }
+ const settingsService: Client =
+ await createServiceForHost(SettingsService, serviceUrl);
+
+ return settingsService.getActiveIdentityProviders(props, {});
+}
+
+/**
+ *
+ * @param host
+ * @param request the request object for verifying passkey registration
+ * @returns the result of the verification
+ */
+export async function verifyPasskeyRegistration({
+ serviceUrl,
+ request,
+}: {
+ serviceUrl: string;
+ request: VerifyPasskeyRegistrationRequest;
+}) {
+ const userService: Client = await createServiceForHost(
+ UserService,
+ serviceUrl,
+ );
+
+ return userService.verifyPasskeyRegistration(request, {});
+}
+
+/**
+ *
+ * @param host
+ * @param userId the id of the user where the email should be set
+ * @param code the code for registering the passkey
+ * @param domain the domain on which the factor is registered
+ * @returns the newly set email
+ */
+export async function registerPasskey({
+ serviceUrl,
+ userId,
+ code,
+ domain,
+}: {
+ serviceUrl: string;
+ userId: string;
+ code: { id: string; code: string };
+ domain: string;
+}) {
+ const userService: Client = await createServiceForHost(
+ UserService,
+ serviceUrl,
+ );
+
+ return userService.registerPasskey({
+ userId,
+ code,
+ domain,
+ });
+}
+
+/**
+ *
+ * @param host
+ * @param userId the id of the user where the email should be set
+ * @returns the list of authentication method types
+ */
+export async function listAuthenticationMethodTypes({
+ serviceUrl,
+ userId,
+}: {
+ serviceUrl: string;
+ userId: string;
+}) {
+ const userService: Client = await createServiceForHost(
+ UserService,
+ serviceUrl,
+ );
+
+ return userService.listAuthenticationMethodTypes({
+ userId,
+ });
+}
+
+export function createServerTransport(token: string, baseUrl: string) {
+ return libCreateServerTransport(token, {
+ baseUrl,
+ interceptors: !process.env.CUSTOM_REQUEST_HEADERS
+ ? undefined
+ : [
+ (next) => {
+ return (req) => {
+ process.env
+ .CUSTOM_REQUEST_HEADERS!.split(",")
+ .forEach((header) => {
+ const kv = header.split(":");
+ if (kv.length === 2) {
+ req.header.set(kv[0].trim(), kv[1].trim());
+ } else {
+ console.warn(`Skipping malformed header: ${header}`);
+ }
+ });
+ return next(req);
+ };
+ },
+ ],
+ });
+}
diff --git a/login/apps/login/src/middleware.ts b/login/apps/login/src/middleware.ts
new file mode 100644
index 0000000000..8eca00510e
--- /dev/null
+++ b/login/apps/login/src/middleware.ts
@@ -0,0 +1,109 @@
+import { SecuritySettings } from "@zitadel/proto/zitadel/settings/v2/security_settings_pb";
+
+import { headers } from "next/headers";
+import { NextRequest, NextResponse } from "next/server";
+import { DEFAULT_CSP } from "../constants/csp";
+import { getServiceUrlFromHeaders } from "./lib/service-url";
+export const config = {
+ matcher: [
+ "/.well-known/:path*",
+ "/oauth/:path*",
+ "/oidc/:path*",
+ "/idps/callback/:path*",
+ "/saml/:path*",
+ "/:path*",
+ ],
+};
+
+async function loadSecuritySettings(
+ request: NextRequest,
+): Promise {
+ const securityResponse = await fetch(`${request.nextUrl.origin}/security`);
+
+ if (!securityResponse.ok) {
+ console.error(
+ "Failed to fetch security settings:",
+ securityResponse.statusText,
+ );
+ return null;
+ }
+
+ const response = await securityResponse.json();
+
+ if (!response || !response.settings) {
+ console.error("No security settings found in the response.");
+ return null;
+ }
+
+ return response.settings;
+}
+
+export async function middleware(request: NextRequest) {
+ // Add the original URL as a header to all requests
+ const requestHeaders = new Headers(request.headers);
+
+ // Extract "organization" search param from the URL and set it as a header if available
+ const organization = request.nextUrl.searchParams.get("organization");
+ if (organization) {
+ requestHeaders.set("x-zitadel-i18n-organization", organization);
+ }
+
+ // Only run the rest of the logic for the original matcher paths
+ const proxyPaths = [
+ "/.well-known/",
+ "/oauth/",
+ "/oidc/",
+ "/idps/callback/",
+ "/saml/",
+ ];
+
+ const isMatched = proxyPaths.some((prefix) =>
+ request.nextUrl.pathname.startsWith(prefix),
+ );
+
+ // escape proxy if the environment is setup for multitenancy
+ if (
+ !isMatched ||
+ !process.env.ZITADEL_API_URL ||
+ !process.env.ZITADEL_SERVICE_USER_TOKEN
+ ) {
+ // For all other routes, just add the header and continue
+ return NextResponse.next({
+ request: { headers: requestHeaders },
+ });
+ }
+
+ const _headers = await headers();
+ const { serviceUrl } = getServiceUrlFromHeaders(_headers);
+
+ const instanceHost = `${serviceUrl}`
+ .replace("https://", "")
+ .replace("http://", "");
+
+ // Add additional headers as before
+ requestHeaders.set("x-zitadel-public-host", `${request.nextUrl.host}`);
+ requestHeaders.set("x-zitadel-instance-host", instanceHost);
+
+ const responseHeaders = new Headers();
+ responseHeaders.set("Access-Control-Allow-Origin", "*");
+ responseHeaders.set("Access-Control-Allow-Headers", "*");
+
+ const securitySettings = await loadSecuritySettings(request);
+
+ if (securitySettings?.embeddedIframe?.enabled) {
+ responseHeaders.set(
+ "Content-Security-Policy",
+ `${DEFAULT_CSP} frame-ancestors ${securitySettings.embeddedIframe.allowedOrigins.join(" ")};`,
+ );
+ responseHeaders.delete("X-Frame-Options");
+ }
+
+ request.nextUrl.href = `${serviceUrl}${request.nextUrl.pathname}${request.nextUrl.search}`;
+
+ return NextResponse.rewrite(request.nextUrl, {
+ request: {
+ headers: requestHeaders,
+ },
+ headers: responseHeaders,
+ });
+}
diff --git a/login/apps/login/src/styles/globals.scss b/login/apps/login/src/styles/globals.scss
new file mode 100755
index 0000000000..cfce853bc7
--- /dev/null
+++ b/login/apps/login/src/styles/globals.scss
@@ -0,0 +1,65 @@
+// include styles from the ui package
+@use "./vars.scss";
+
+@tailwind base;
+@tailwind components;
+@tailwind utilities;
+
+@layer base {
+ h1,
+ .ztdl-h1 {
+ @apply text-2xl text-center;
+ }
+
+ .ztdl-p {
+ @apply text-sm text-center text-text-light-secondary-500 dark:text-text-dark-secondary-500 text-center;
+ }
+}
+
+html {
+ --background-color: #ffffff;
+ --dark-background-color: #000000;
+}
+
+.form-checkbox:checked {
+ background-image: url("/checkbox.svg");
+}
+
+.skeleton {
+ --accents-2: var(--theme-light-background-400);
+ --accents-1: var(--theme-light-background-500);
+
+ background-image: linear-gradient(
+ 270deg,
+ var(--accents-1),
+ var(--accents-2),
+ var(--accents-2),
+ var(--accents-1)
+ );
+ background-size: 400% 100%;
+ animation: skeleton_loading 8s ease-in-out infinite;
+}
+
+.dark .skeleton {
+ --accents-2: var(--theme-dark-background-400);
+ --accents-1: var(--theme-dark-background-500);
+
+ background-image: linear-gradient(
+ 270deg,
+ var(--accents-1),
+ var(--accents-2),
+ var(--accents-2),
+ var(--accents-1)
+ );
+ background-size: 400% 100%;
+ animation: skeleton_loading 8s ease-in-out infinite;
+}
+
+@keyframes skeleton_loading {
+ 0% {
+ background-position: 200% 0;
+ }
+ 100% {
+ background-position: -200% 0;
+ }
+}
diff --git a/login/apps/login/src/styles/vars.scss b/login/apps/login/src/styles/vars.scss
new file mode 100644
index 0000000000..71c6a28782
--- /dev/null
+++ b/login/apps/login/src/styles/vars.scss
@@ -0,0 +1,174 @@
+:root {
+ --theme-dark-primary-50: #f1f7fd;
+ --theme-dark-primary-contrast-50: hsla(0, 0%, 0%, 0.87);
+ --theme-dark-primary-100: #afd1f2;
+ --theme-dark-primary-contrast-100: hsla(0, 0%, 0%, 0.87);
+ --theme-dark-primary-200: #7fb5ea;
+ --theme-dark-primary-contrast-200: hsla(0, 0%, 0%, 0.87);
+ --theme-dark-primary-300: #4192e0;
+ --theme-dark-primary-contrast-300: hsla(0, 0%, 0%, 0.87);
+ --theme-dark-primary-400: #2782dc;
+ --theme-dark-primary-contrast-400: hsla(0, 0%, 0%, 0.87);
+ --theme-dark-primary-500: #2073c4;
+ --theme-dark-primary-contrast-500: #ffffff;
+ --theme-dark-primary-600: #1c64aa;
+ --theme-dark-primary-contrast-600: #ffffff;
+ --theme-dark-primary-700: #17548f;
+ --theme-dark-primary-contrast-700: #ffffff;
+ --theme-dark-primary-800: #134575;
+ --theme-dark-primary-contrast-800: #ffffff;
+ --theme-dark-primary-900: #0f355b;
+ --theme-dark-primary-contrast-900: #ffffff;
+ --theme-dark-primary-A100: #e4f2ff;
+ --theme-dark-primary-contrast-A100: hsla(0, 0%, 0%, 0.87);
+ --theme-dark-primary-A200: #7ebfff;
+ --theme-dark-primary-contrast-A200: hsla(0, 0%, 0%, 0.87);
+ --theme-dark-primary-A400: #278df0;
+ --theme-dark-primary-contrast-A400: hsla(0, 0%, 0%, 0.87);
+ --theme-dark-primary-A700: #1d80e0;
+ --theme-dark-primary-contrast-A700: hsla(0, 0%, 0%, 0.87);
+ --theme-light-primary-50: #ffffff;
+ --theme-light-primary-contrast-50: hsla(0, 0%, 0%, 0.87);
+ --theme-light-primary-100: #ebedfa;
+ --theme-light-primary-contrast-100: hsla(0, 0%, 0%, 0.87);
+ --theme-light-primary-200: #bec6ef;
+ --theme-light-primary-contrast-200: hsla(0, 0%, 0%, 0.87);
+ --theme-light-primary-300: #8594e0;
+ --theme-light-primary-contrast-300: hsla(0, 0%, 0%, 0.87);
+ --theme-light-primary-400: #6c7eda;
+ --theme-light-primary-contrast-400: hsla(0, 0%, 0%, 0.87);
+ --theme-light-primary-500: #5469d4;
+ --theme-light-primary-contrast-500: #ffffff;
+ --theme-light-primary-600: #3c54ce;
+ --theme-light-primary-contrast-600: #ffffff;
+ --theme-light-primary-700: #2f46bc;
+ --theme-light-primary-contrast-700: #ffffff;
+ --theme-light-primary-800: #293da3;
+ --theme-light-primary-contrast-800: #ffffff;
+ --theme-light-primary-900: #23348b;
+ --theme-light-primary-contrast-900: #ffffff;
+ --theme-light-primary-A100: #ffffff;
+ --theme-light-primary-contrast-A100: hsla(0, 0%, 0%, 0.87);
+ --theme-light-primary-A200: #c5cefc;
+ --theme-light-primary-contrast-A200: hsla(0, 0%, 0%, 0.87);
+ --theme-light-primary-A400: #7085ea;
+ --theme-light-primary-contrast-A400: hsla(0, 0%, 0%, 0.87);
+ --theme-light-primary-A700: #6478de;
+ --theme-light-primary-contrast-A700: hsla(0, 0%, 0%, 0.87);
+ --theme-dark-warn-50: #ffffff;
+ --theme-dark-warn-contrast-50: hsla(0, 0%, 0%, 0.87);
+ --theme-dark-warn-100: #fff8f9;
+ --theme-dark-warn-contrast-100: hsla(0, 0%, 0%, 0.87);
+ --theme-dark-warn-200: #ffc0ca;
+ --theme-dark-warn-contrast-200: hsla(0, 0%, 0%, 0.87);
+ --theme-dark-warn-300: #ff788e;
+ --theme-dark-warn-contrast-300: hsla(0, 0%, 0%, 0.87);
+ --theme-dark-warn-400: #ff5a75;
+ --theme-dark-warn-contrast-400: hsla(0, 0%, 0%, 0.87);
+ --theme-dark-warn-500: #ff3b5b;
+ --theme-dark-warn-contrast-500: hsla(0, 0%, 0%, 0.87);
+ --theme-dark-warn-600: #ff1c41;
+ --theme-dark-warn-contrast-600: hsla(0, 0%, 0%, 0.87);
+ --theme-dark-warn-700: #fd0029;
+ --theme-dark-warn-contrast-700: hsla(0, 0%, 0%, 0.87);
+ --theme-dark-warn-800: #de0024;
+ --theme-dark-warn-contrast-800: #ffffff;
+ --theme-dark-warn-900: #c0001f;
+ --theme-dark-warn-contrast-900: #ffffff;
+ --theme-dark-warn-A100: #ffffff;
+ --theme-dark-warn-contrast-A100: hsla(0, 0%, 0%, 0.87);
+ --theme-dark-warn-A200: #ffd4db;
+ --theme-dark-warn-contrast-A200: hsla(0, 0%, 0%, 0.87);
+ --theme-dark-warn-A400: #ff6e86;
+ --theme-dark-warn-contrast-A400: hsla(0, 0%, 0%, 0.87);
+ --theme-dark-warn-A700: #ff5470;
+ --theme-dark-warn-contrast-A700: hsla(0, 0%, 0%, 0.87);
+ --theme-light-warn-50: #ffffff;
+ --theme-light-warn-contrast-50: hsla(0, 0%, 0%, 0.87);
+ --theme-light-warn-100: #f4d3d9;
+ --theme-light-warn-contrast-100: hsla(0, 0%, 0%, 0.87);
+ --theme-light-warn-200: #e8a6b2;
+ --theme-light-warn-contrast-200: hsla(0, 0%, 0%, 0.87);
+ --theme-light-warn-300: #da6e80;
+ --theme-light-warn-contrast-300: hsla(0, 0%, 0%, 0.87);
+ --theme-light-warn-400: #d3556b;
+ --theme-light-warn-contrast-400: hsla(0, 0%, 0%, 0.87);
+ --theme-light-warn-500: #cd3d56;
+ --theme-light-warn-contrast-500: #ffffff;
+ --theme-light-warn-600: #bb3048;
+ --theme-light-warn-contrast-600: #ffffff;
+ --theme-light-warn-700: #a32a3f;
+ --theme-light-warn-contrast-700: #ffffff;
+ --theme-light-warn-800: #8a2436;
+ --theme-light-warn-contrast-800: #ffffff;
+ --theme-light-warn-900: #721d2c;
+ --theme-light-warn-contrast-900: #ffffff;
+ --theme-light-warn-A100: #ffffff;
+ --theme-light-warn-contrast-A100: hsla(0, 0%, 0%, 0.87);
+ --theme-light-warn-A200: #faa9b7;
+ --theme-light-warn-contrast-A200: hsla(0, 0%, 0%, 0.87);
+ --theme-light-warn-A400: #e65770;
+ --theme-light-warn-contrast-A400: hsla(0, 0%, 0%, 0.87);
+ --theme-light-warn-A700: #d84c64;
+ --theme-light-warn-contrast-A700: hsla(0, 0%, 0%, 0.87);
+ --theme-dark-background-50: #7c93c6;
+ --theme-dark-background-contrast-50: hsla(0, 0%, 0%, 0.87);
+ --theme-dark-background-100: #4a69aa;
+ --theme-dark-background-contrast-100: #ffffff;
+ --theme-dark-background-200: #395183;
+ --theme-dark-background-contrast-200: #ffffff;
+ --theme-dark-background-300: #243252;
+ --theme-dark-background-contrast-300: #ffffff;
+ --theme-dark-background-400: #1a253c;
+ --theme-dark-background-contrast-400: #ffffff;
+ --theme-dark-background-500: #111827;
+ --theme-dark-background-contrast-500: #ffffff;
+ --theme-dark-background-600: #080b12;
+ --theme-dark-background-contrast-600: #ffffff;
+ --theme-dark-background-700: #000000;
+ --theme-dark-background-contrast-700: #ffffff;
+ --theme-dark-background-800: #000000;
+ --theme-dark-background-contrast-800: #ffffff;
+ --theme-dark-background-900: #000000;
+ --theme-dark-background-contrast-900: #ffffff;
+ --theme-dark-background-A100: #5782e0;
+ --theme-dark-background-contrast-A100: hsla(0, 0%, 0%, 0.87);
+ --theme-dark-background-A200: #204eb1;
+ --theme-dark-background-contrast-A200: #ffffff;
+ --theme-dark-background-A400: #182b53;
+ --theme-dark-background-contrast-A400: #ffffff;
+ --theme-dark-background-A700: #17223b;
+ --theme-dark-background-contrast-A700: #ffffff;
+ --theme-light-background-50: #ffffff;
+ --theme-light-background-contrast-50: hsla(0, 0%, 0%, 0.87);
+ --theme-light-background-100: #ffffff;
+ --theme-light-background-contrast-100: hsla(0, 0%, 0%, 0.87);
+ --theme-light-background-200: #ffffff;
+ --theme-light-background-contrast-200: hsla(0, 0%, 0%, 0.87);
+ --theme-light-background-300: #ffffff;
+ --theme-light-background-contrast-300: hsla(0, 0%, 0%, 0.87);
+ --theme-light-background-400: #ffffff;
+ --theme-light-background-contrast-400: hsla(0, 0%, 0%, 0.87);
+ --theme-light-background-500: #fafafa;
+ --theme-light-background-contrast-500: hsla(0, 0%, 0%, 0.87);
+ --theme-light-background-600: #ebebeb;
+ --theme-light-background-contrast-600: hsla(0, 0%, 0%, 0.87);
+ --theme-light-background-700: #dbdbdb;
+ --theme-light-background-contrast-700: hsla(0, 0%, 0%, 0.87);
+ --theme-light-background-800: #cccccc;
+ --theme-light-background-contrast-800: hsla(0, 0%, 0%, 0.87);
+ --theme-light-background-900: #bdbdbd;
+ --theme-light-background-contrast-900: hsla(0, 0%, 0%, 0.87);
+ --theme-light-background-A100: #ffffff;
+ --theme-light-background-contrast-A100: hsla(0, 0%, 0%, 0.87);
+ --theme-light-background-A200: #ffffff;
+ --theme-light-background-contrast-A200: hsla(0, 0%, 0%, 0.87);
+ --theme-light-background-A400: #ffffff;
+ --theme-light-background-contrast-A400: hsla(0, 0%, 0%, 0.87);
+ --theme-light-background-A700: #ffffff;
+ --theme-light-background-contrast-A700: hsla(0, 0%, 0%, 0.87);
+ --theme-dark-text: #ffffff;
+ --theme-dark-secondary-text: #ffffffc7;
+ --theme-light-text: #000000;
+ --theme-light-secondary-text: #000000c7;
+}
diff --git a/login/apps/login/tailwind.config.mjs b/login/apps/login/tailwind.config.mjs
new file mode 100644
index 0000000000..908068e5dd
--- /dev/null
+++ b/login/apps/login/tailwind.config.mjs
@@ -0,0 +1,117 @@
+import sharedConfig from "@zitadel/tailwind-config/tailwind.config.mjs";
+
+let colors = {
+ background: { light: { contrast: {} }, dark: { contrast: {} } },
+ primary: { light: { contrast: {} }, dark: { contrast: {} } },
+ warn: { light: { contrast: {} }, dark: { contrast: {} } },
+ text: { light: { contrast: {} }, dark: { contrast: {} } },
+ link: { light: { contrast: {} }, dark: { contrast: {} } },
+};
+
+const shades = [
+ "50",
+ "100",
+ "200",
+ "300",
+ "400",
+ "500",
+ "600",
+ "700",
+ "800",
+ "900",
+];
+const themes = ["light", "dark"];
+const types = ["background", "primary", "warn", "text", "link"];
+types.forEach((type) => {
+ themes.forEach((theme) => {
+ shades.forEach((shade) => {
+ colors[type][theme][shade] = `var(--theme-${theme}-${type}-${shade})`;
+ colors[type][theme][`contrast-${shade}`] =
+ `var(--theme-${theme}-${type}-contrast-${shade})`;
+ colors[type][theme][`secondary-${shade}`] =
+ `var(--theme-${theme}-${type}-secondary-${shade})`;
+ });
+ });
+});
+
+/** @type {import('tailwindcss').Config} */
+export default {
+ presets: [sharedConfig],
+ darkMode: "class",
+ content: ["./src/**/*.{js,ts,jsx,tsx}"],
+ future: {
+ hoverOnlyWhenSupported: true,
+ },
+ theme: {
+ extend: {
+ colors: {
+ ...colors,
+ state: {
+ success: {
+ light: {
+ background: "#cbf4c9",
+ color: "#0e6245",
+ },
+ dark: {
+ background: "#68cf8340",
+ color: "#cbf4c9",
+ },
+ },
+ error: {
+ light: {
+ background: "#ffc1c1",
+ color: "#620e0e",
+ },
+ dark: {
+ background: "#af455359",
+ color: "#ffc1c1",
+ },
+ },
+ neutral: {
+ light: {
+ background: "#e4e7e4",
+ color: "#000000",
+ },
+ dark: {
+ background: "#1a253c",
+ color: "#ffffff",
+ },
+ },
+ alert: {
+ light: {
+ background: "#fbbf24",
+ color: "#92400e",
+ },
+ dark: {
+ background: "#92400e50",
+ color: "#fbbf24",
+ },
+ },
+ },
+ },
+ animation: {
+ shake: "shake .8s cubic-bezier(.36,.07,.19,.97) both;",
+ },
+ keyframes: {
+ shake: {
+ "10%, 90%": {
+ transform: "translate3d(-1px, 0, 0)",
+ },
+
+ "20%, 80%": {
+ transform: "translate3d(2px, 0, 0)",
+ },
+
+ "30%, 50%, 70%": {
+ transform: "translate3d(-4px, 0, 0)",
+ },
+
+ "40%, 60%": {
+ transform: "translate3d(4px, 0, 0)",
+ },
+ },
+ },
+ },
+ },
+ plugins: [require("@tailwindcss/forms")],
+};
diff --git a/login/apps/login/tsconfig.json b/login/apps/login/tsconfig.json
new file mode 100755
index 0000000000..c855c43225
--- /dev/null
+++ b/login/apps/login/tsconfig.json
@@ -0,0 +1,24 @@
+{
+ "extends": "@zitadel/tsconfig/nextjs.json",
+ "compilerOptions": {
+ "jsx": "preserve",
+ "target": "es2022",
+ "baseUrl": ".",
+ "paths": {
+ "@/*": ["./src/*"]
+ },
+ "plugins": [
+ {
+ "name": "next"
+ }
+ ]
+ },
+ "include": [
+ "next-env.d.ts",
+ "**/*.ts",
+ "**/*.tsx",
+ ".next/types/**/*.ts",
+ "custom-config.js"
+ ],
+ "exclude": ["node_modules"]
+}
diff --git a/login/apps/login/turbo.json b/login/apps/login/turbo.json
new file mode 100644
index 0000000000..bc63a2dbc4
--- /dev/null
+++ b/login/apps/login/turbo.json
@@ -0,0 +1,22 @@
+{
+ "extends": ["//"],
+ "tasks": {
+ "build": {
+ "outputs": ["dist/**", ".next/**", "!.next/cache/**"],
+ "dependsOn": ["^build"]
+ },
+ "build:login:standalone": {
+ "outputs": ["dist/**", ".next/**", "!.next/cache/**"]
+ },
+ "test": {
+ "dependsOn": ["@zitadel/client#build"]
+ },
+ "test:unit": {
+ "dependsOn": ["@zitadel/client#build"]
+ },
+ "test:unit:standalone": {},
+ "test:watch": {
+ "dependsOn": ["@zitadel/client#build"]
+ }
+ }
+}
diff --git a/login/apps/login/vitest.config.mts b/login/apps/login/vitest.config.mts
new file mode 100644
index 0000000000..238c5b8b93
--- /dev/null
+++ b/login/apps/login/vitest.config.mts
@@ -0,0 +1,12 @@
+import react from "@vitejs/plugin-react";
+import tsconfigPaths from "vite-tsconfig-paths";
+import { defineConfig } from "vitest/config";
+
+export default defineConfig({
+ plugins: [tsconfigPaths(), react()],
+ test: {
+ include: ["src/**/*.test.ts", "src/**/*.test.tsx"],
+ environment: "jsdom",
+ setupFiles: ["@testing-library/jest-dom/vitest"],
+ },
+});
diff --git a/login/docker-bake.hcl b/login/docker-bake.hcl
new file mode 100644
index 0000000000..9520b752fa
--- /dev/null
+++ b/login/docker-bake.hcl
@@ -0,0 +1,145 @@
+variable "LOGIN_DIR" {
+ default = "./"
+}
+
+variable "DOCKERFILES_DIR" {
+ default = "dockerfiles/"
+}
+
+# typescript-proto-client is used to generate the client code for the login service.
+# It is not login-prefixed, so it is easily extendable.
+# To extend this bake-file.hcl, set the context of all login-prefixed targets to a different directory.
+# For example docker bake --file login/docker-bake.hcl --file docker-bake.hcl --set login-*.context=./login/
+# The zitadel repository uses this to generate the client and the mock server from local proto files.
+target "typescript-proto-client" {
+ dockerfile = "${DOCKERFILES_DIR}typescript-proto-client.Dockerfile"
+ contexts = {
+ # We directly generate and download the client server-side with buf, so we don't need the proto files
+ login-pnpm = "target:login-pnpm"
+ }
+}
+
+# We prefix the target with login- so we can reuse the writing of protos if we overwrite the typescript-proto-client target.
+target "login-typescript-proto-client-out" {
+ dockerfile = "${DOCKERFILES_DIR}login-typescript-proto-client-out.Dockerfile"
+ contexts = {
+ typescript-proto-client = "target:typescript-proto-client"
+ }
+ output = [
+ "type=local,dest=${LOGIN_DIR}packages/zitadel-proto"
+ ]
+}
+
+# proto-files is only used to build core-mock against which the integration tests run.
+# To build the proto-client, we use buf to generate and download the client code directly.
+# It is not login-prefixed, so it is easily extendable.
+# To extend this bake-file.hcl, set the context of all login-prefixed targets to a different directory.
+# For example docker bake --file login/docker-bake.hcl --file docker-bake.hcl --set login-*.context=./login/
+# The zitadel repository uses this to generate the client and the mock server from local proto files.
+target "proto-files" {
+ dockerfile = "${DOCKERFILES_DIR}proto-files.Dockerfile"
+ contexts = {
+ login-pnpm = "target:login-pnpm"
+ }
+}
+
+variable "NODE_VERSION" {
+ default = "20"
+}
+
+target "login-pnpm" {
+ dockerfile = "${DOCKERFILES_DIR}login-pnpm.Dockerfile"
+ args = {
+ NODE_VERSION = "${NODE_VERSION}"
+ }
+}
+
+target "login-dev-base" {
+ dockerfile = "${DOCKERFILES_DIR}login-dev-base.Dockerfile"
+ contexts = {
+ login-pnpm = "target:login-pnpm"
+ }
+}
+
+target "login-lint" {
+ dockerfile = "${DOCKERFILES_DIR}login-lint.Dockerfile"
+ contexts = {
+ login-dev-base = "target:login-dev-base"
+ }
+}
+
+target "login-test-unit" {
+ dockerfile = "${DOCKERFILES_DIR}login-test-unit.Dockerfile"
+ contexts = {
+ login-client = "target:login-client"
+ }
+}
+
+target "login-client" {
+ dockerfile = "${DOCKERFILES_DIR}login-client.Dockerfile"
+ contexts = {
+ login-pnpm = "target:login-pnpm"
+ typescript-proto-client = "target:typescript-proto-client"
+ }
+}
+
+variable "LOGIN_CORE_MOCK_TAG" {
+ default = "login-core-mock:local"
+}
+
+# the core-mock context must not be overwritten, so we don't prefix it with login-.
+target "core-mock" {
+ context = "${LOGIN_DIR}apps/login-test-integration/core-mock"
+ contexts = {
+ protos = "target:proto-files"
+ }
+ tags = ["${LOGIN_CORE_MOCK_TAG}"]
+}
+
+variable "LOGIN_TEST_INTEGRATION_TAG" {
+ default = "login-test-integration:local"
+}
+
+target "login-test-integration" {
+ dockerfile = "${DOCKERFILES_DIR}login-test-integration.Dockerfile"
+ contexts = {
+ login-pnpm = "target:login-pnpm"
+ }
+ tags = ["${LOGIN_TEST_INTEGRATION_TAG}"]
+}
+
+variable "LOGIN_TEST_ACCEPTANCE_TAG" {
+ default = "login-test-acceptance:local"
+}
+
+target "login-test-acceptance" {
+ dockerfile = "${DOCKERFILES_DIR}login-test-acceptance.Dockerfile"
+ contexts = {
+ login-pnpm = "target:login-pnpm"
+ }
+ tags = ["${LOGIN_TEST_ACCEPTANCE_TAG}"]
+}
+
+variable "LOGIN_TAG" {
+ default = "zitadel-login:local"
+}
+
+target "docker-metadata-action" {}
+
+# We run integration and acceptance tests against the next standalone server for docker.
+target "login-standalone" {
+ inherits = ["docker-metadata-action"]
+ dockerfile = "${DOCKERFILES_DIR}login-standalone.Dockerfile"
+ contexts = {
+ login-client = "target:login-client"
+ }
+ tags = ["${LOGIN_TAG}"]
+}
+
+target "login-standalone-out" {
+ inherits = ["login-standalone"]
+ target = "login-standalone-out"
+ output = [
+ "type=local,dest=${LOGIN_DIR}apps/login/standalone"
+ ]
+}
diff --git a/login/dockerfiles/login-client.Dockerfile b/login/dockerfiles/login-client.Dockerfile
new file mode 100644
index 0000000000..4eb01615b4
--- /dev/null
+++ b/login/dockerfiles/login-client.Dockerfile
@@ -0,0 +1,7 @@
+FROM typescript-proto-client AS login-client
+COPY packages/zitadel-tsconfig packages/zitadel-tsconfig
+COPY packages/zitadel-client/package.json ./packages/zitadel-client/
+RUN --mount=type=cache,id=pnpm,target=/pnpm/store \
+ pnpm install --frozen-lockfile --workspace-root --filter ./packages/zitadel-client
+COPY packages/zitadel-client ./packages/zitadel-client
+RUN pnpm build:client:standalone
diff --git a/login/dockerfiles/login-client.Dockerfile.dockerignore b/login/dockerfiles/login-client.Dockerfile.dockerignore
new file mode 100644
index 0000000000..c2302359f5
--- /dev/null
+++ b/login/dockerfiles/login-client.Dockerfile.dockerignore
@@ -0,0 +1,11 @@
+*
+
+!packages/zitadel-client
+packages/zitadel-client/dist
+
+!packages/zitadel-tsconfig
+
+**/*.md
+**/*.png
+**/node_modules
+**/.turbo
diff --git a/login/dockerfiles/login-dev-base.Dockerfile b/login/dockerfiles/login-dev-base.Dockerfile
new file mode 100644
index 0000000000..e102d16746
--- /dev/null
+++ b/login/dockerfiles/login-dev-base.Dockerfile
@@ -0,0 +1,3 @@
+FROM login-pnpm AS login-dev-base
+RUN pnpm install --frozen-lockfile --prefer-offline --workspace-root --filter .
+
diff --git a/login/dockerfiles/login-dev-base.Dockerfile.dockerignore b/login/dockerfiles/login-dev-base.Dockerfile.dockerignore
new file mode 100644
index 0000000000..72e8ffc0db
--- /dev/null
+++ b/login/dockerfiles/login-dev-base.Dockerfile.dockerignore
@@ -0,0 +1 @@
+*
diff --git a/login/dockerfiles/login-lint.Dockerfile b/login/dockerfiles/login-lint.Dockerfile
new file mode 100644
index 0000000000..0c466b4cfa
--- /dev/null
+++ b/login/dockerfiles/login-lint.Dockerfile
@@ -0,0 +1,7 @@
+FROM login-dev-base AS login-lint
+COPY .prettierrc .prettierignore ./
+COPY apps/login/package.json apps/login/
+RUN --mount=type=cache,id=pnpm,target=/pnpm/store \
+ pnpm install --frozen-lockfile --workspace-root --filter apps/login
+COPY . .
+RUN pnpm lint && pnpm format
diff --git a/login/dockerfiles/login-lint.Dockerfile.dockerignore b/login/dockerfiles/login-lint.Dockerfile.dockerignore
new file mode 100644
index 0000000000..1029f73c02
--- /dev/null
+++ b/login/dockerfiles/login-lint.Dockerfile.dockerignore
@@ -0,0 +1,25 @@
+*
+
+!apps/login
+apps/login/.next
+apps/login/dist
+apps/login/screenshots
+apps/login/standalone
+apps/login/.env*.local
+
+!apps/login-test-integration
+
+!apps/login-test-acceptance
+apps/login-test-acceptance/test-results
+
+!/packages/zitadel-tsconfig/*
+!/packages/zitadel-prettier-config
+!/packages/zitadel-eslint-config
+
+!/.prettierrc
+!/.prettierignore
+
+**/*.md
+**/*.png
+**/node_modules
+**/.turbo
diff --git a/login/dockerfiles/login-pnpm.Dockerfile b/login/dockerfiles/login-pnpm.Dockerfile
new file mode 100644
index 0000000000..bcef6d126c
--- /dev/null
+++ b/login/dockerfiles/login-pnpm.Dockerfile
@@ -0,0 +1,10 @@
+ARG NODE_VERSION=20
+FROM node:${NODE_VERSION}-bookworm AS login-pnpm
+ENV PNPM_HOME="/pnpm"
+ENV PATH="$PNPM_HOME:$PATH"
+RUN corepack enable && COREPACK_ENABLE_DOWNLOAD_PROMPT=0 corepack prepare pnpm@9.1.2 --activate && \
+ apt-get update && apt-get install -y --no-install-recommends && \
+ rm -rf /var/lib/apt/lists/*
+WORKDIR /build
+COPY turbo.json .npmrc package.json pnpm-lock.yaml pnpm-workspace.yaml ./
+ENTRYPOINT ["pnpm"]
diff --git a/login/dockerfiles/login-pnpm.Dockerfile.dockerignore b/login/dockerfiles/login-pnpm.Dockerfile.dockerignore
new file mode 100644
index 0000000000..067514fdd3
--- /dev/null
+++ b/login/dockerfiles/login-pnpm.Dockerfile.dockerignore
@@ -0,0 +1,6 @@
+*
+!/turbo.json
+!/.npmrc
+!/package.json
+!/pnpm-lock.yaml
+!/pnpm-workspace.yaml
diff --git a/login/dockerfiles/login-standalone.Dockerfile b/login/dockerfiles/login-standalone.Dockerfile
new file mode 100644
index 0000000000..7e97d344db
--- /dev/null
+++ b/login/dockerfiles/login-standalone.Dockerfile
@@ -0,0 +1,34 @@
+FROM login-client AS login-standalone-builder
+COPY apps/login ./apps/login
+COPY packages/zitadel-tailwind-config packages/zitadel-tailwind-config
+RUN pnpm exec turbo prune @zitadel/login --docker
+WORKDIR /build/docker
+RUN cp -r ../out/json/* .
+RUN --mount=type=cache,id=pnpm,target=/pnpm/store \
+ pnpm install --frozen-lockfile
+RUN cp -r ../out/full/* .
+RUN pnpm exec turbo run build:login:standalone
+
+FROM scratch AS login-standalone-out
+COPY --from=login-standalone-builder /build/docker/apps/login/.next/standalone /
+COPY --from=login-standalone-builder /build/docker/apps/login/.next/static /apps/login/.next/static
+COPY --from=login-standalone-builder /build/docker/apps/login/public /apps/login/public
+
+FROM node:20-alpine AS login-standalone
+WORKDIR /runtime
+RUN addgroup --system --gid 1001 nodejs && \
+ adduser --system --uid 1001 nextjs
+# If /.env-file/.env is mounted into the container, its variables are made available to the server before it starts up.
+RUN mkdir -p /.env-file && touch /.env-file/.env && chown -R nextjs:nodejs /.env-file
+COPY ./scripts/entrypoint.sh ./
+COPY ./scripts/healthcheck.js ./
+COPY --chown=nextjs:nodejs --from=login-standalone-builder /build/docker/apps/login/.next/standalone ./
+COPY --chown=nextjs:nodejs --from=login-standalone-builder /build/docker/apps/login/.next/static ./apps/login/.next/static
+COPY --chown=nextjs:nodejs --from=login-standalone-builder /build/docker/apps/login/public ./apps/login/public
+USER nextjs
+ENV HOSTNAME="0.0.0.0"
+ENV PORT=3000
+# TODO: Check healthy, not ready
+HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
+CMD ["/bin/sh", "-c", "node ./healthcheck.js http://localhost:${PORT}/ui/v2/login/healthy"]
+ENTRYPOINT ["./entrypoint.sh"]
diff --git a/login/dockerfiles/login-standalone.Dockerfile.dockerignore b/login/dockerfiles/login-standalone.Dockerfile.dockerignore
new file mode 100644
index 0000000000..f876e1e9f1
--- /dev/null
+++ b/login/dockerfiles/login-standalone.Dockerfile.dockerignore
@@ -0,0 +1,17 @@
+*
+
+!apps/login
+apps/login/.next
+apps/login/dist
+apps/login/screenshots
+apps/login/standalone
+apps/login/.env*.local
+
+!scripts/entrypoint.sh
+!scripts/healthcheck.js
+!packages/zitadel-tailwind-config
+
+**/*.md
+**/*.png
+**/node_modules
+**/.turbo
diff --git a/login/dockerfiles/login-test-acceptance.Dockerfile b/login/dockerfiles/login-test-acceptance.Dockerfile
new file mode 100644
index 0000000000..7052484779
--- /dev/null
+++ b/login/dockerfiles/login-test-acceptance.Dockerfile
@@ -0,0 +1,8 @@
+FROM login-pnpm AS login-test-acceptance-dependencies
+COPY ./apps/login-test-acceptance/package.json ./apps/login-test-acceptance/package.json
+RUN --mount=type=cache,id=pnpm,target=/pnpm/store \
+ pnpm install --frozen-lockfile --filter=login-test-acceptance && \
+ cd apps/login-test-acceptance && \
+ pnpm exec playwright install --with-deps chromium
+COPY ./apps/login-test-acceptance ./apps/login-test-acceptance
+CMD ["bash", "-c", "cd apps/login-test-acceptance && pnpm test:acceptance test"]
diff --git a/login/dockerfiles/login-test-acceptance.Dockerfile.dockerignore b/login/dockerfiles/login-test-acceptance.Dockerfile.dockerignore
new file mode 100644
index 0000000000..cba55ae91e
--- /dev/null
+++ b/login/dockerfiles/login-test-acceptance.Dockerfile.dockerignore
@@ -0,0 +1,5 @@
+*
+!/apps/login-test-acceptance/*.json
+!/apps/login-test-acceptance/*.ts
+!/apps/login-test-acceptance/zitadel.yaml
+!/apps/login-test-acceptance/tests
diff --git a/login/dockerfiles/login-test-integration.Dockerfile b/login/dockerfiles/login-test-integration.Dockerfile
new file mode 100644
index 0000000000..0b55dc2b1a
--- /dev/null
+++ b/login/dockerfiles/login-test-integration.Dockerfile
@@ -0,0 +1,11 @@
+FROM login-pnpm AS login-test-integration-dependencies
+COPY ./apps/login-test-integration/package.json ./apps/login-test-integration/package.json
+RUN --mount=type=cache,id=pnpm,target=/pnpm/store \
+ pnpm install --frozen-lockfile --filter=login-test-integration
+FROM cypress/factory:5.10.0 AS login-test-integration
+WORKDIR /opt/app
+COPY --from=login-test-integration-dependencies /build/apps/login-test-integration .
+RUN npm install cypress
+RUN npx cypress install
+COPY ./apps/login-test-integration .
+CMD ["npx", "cypress", "run"]
diff --git a/login/dockerfiles/login-test-integration.Dockerfile.dockerignore b/login/dockerfiles/login-test-integration.Dockerfile.dockerignore
new file mode 100644
index 0000000000..947a4fdb57
--- /dev/null
+++ b/login/dockerfiles/login-test-integration.Dockerfile.dockerignore
@@ -0,0 +1,9 @@
+*
+
+!/apps/login-test-integration
+/apps/login-test-integration/core-mock
+
+**/*.md
+**/*.png
+**/node_modules
+**/.turbo
diff --git a/login/dockerfiles/login-test-unit.Dockerfile b/login/dockerfiles/login-test-unit.Dockerfile
new file mode 100644
index 0000000000..d456a4fac4
--- /dev/null
+++ b/login/dockerfiles/login-test-unit.Dockerfile
@@ -0,0 +1,6 @@
+FROM login-client AS login-test-unit
+COPY apps/login/package.json ./apps/login/
+RUN --mount=type=cache,id=pnpm,target=/pnpm/store \
+ pnpm install --frozen-lockfile --workspace-root --filter ./apps/login
+COPY apps/login ./apps/login
+RUN pnpm test:unit:standalone
diff --git a/login/dockerfiles/login-test-unit.Dockerfile.dockerignore b/login/dockerfiles/login-test-unit.Dockerfile.dockerignore
new file mode 100644
index 0000000000..2263653c69
--- /dev/null
+++ b/login/dockerfiles/login-test-unit.Dockerfile.dockerignore
@@ -0,0 +1,13 @@
+*
+
+!apps/login
+apps/login/.next
+apps/login/dist
+apps/login/screenshots
+apps/login/standalone
+apps/login/.env*.local
+
+**/*.md
+**/*.png
+**/node_modules
+**/.turbo
diff --git a/login/dockerfiles/login-typescript-proto-client-out.Dockerfile b/login/dockerfiles/login-typescript-proto-client-out.Dockerfile
new file mode 100644
index 0000000000..3aa3c9d7d6
--- /dev/null
+++ b/login/dockerfiles/login-typescript-proto-client-out.Dockerfile
@@ -0,0 +1,5 @@
+FROM scratch AS typescript-proto-client-out
+COPY --from=typescript-proto-client /build/packages/zitadel-proto/zitadel /zitadel
+COPY --from=typescript-proto-client /build/packages/zitadel-proto/google /google
+COPY --from=typescript-proto-client /build/packages/zitadel-proto/protoc-gen-openapiv2 /protoc-gen-openapiv2
+COPY --from=typescript-proto-client /build/packages/zitadel-proto/validate /validate
diff --git a/login/dockerfiles/login-typescript-proto-client-out.Dockerfile.dockerignore b/login/dockerfiles/login-typescript-proto-client-out.Dockerfile.dockerignore
new file mode 100644
index 0000000000..72e8ffc0db
--- /dev/null
+++ b/login/dockerfiles/login-typescript-proto-client-out.Dockerfile.dockerignore
@@ -0,0 +1 @@
+*
diff --git a/login/dockerfiles/proto-files.Dockerfile b/login/dockerfiles/proto-files.Dockerfile
new file mode 100644
index 0000000000..f97f63a718
--- /dev/null
+++ b/login/dockerfiles/proto-files.Dockerfile
@@ -0,0 +1,8 @@
+FROM bufbuild/buf:1.54.0 AS proto-files
+RUN buf export https://github.com/envoyproxy/protoc-gen-validate.git --path validate --output /proto-files && \
+ buf export https://github.com/grpc-ecosystem/grpc-gateway.git --path protoc-gen-openapiv2 --output /proto-files && \
+ buf export https://github.com/googleapis/googleapis.git --path google/api/annotations.proto --path google/api/http.proto --path google/api/field_behavior.proto --output /proto-files && \
+ buf export https://github.com/zitadel/zitadel.git --path ./proto/zitadel --output /proto-files
+
+FROM scratch
+COPY --from=proto-files /proto-files /
diff --git a/login/dockerfiles/proto-files.Dockerfile.dockerignore b/login/dockerfiles/proto-files.Dockerfile.dockerignore
new file mode 100644
index 0000000000..72e8ffc0db
--- /dev/null
+++ b/login/dockerfiles/proto-files.Dockerfile.dockerignore
@@ -0,0 +1 @@
+*
diff --git a/login/dockerfiles/typescript-proto-client.Dockerfile b/login/dockerfiles/typescript-proto-client.Dockerfile
new file mode 100644
index 0000000000..ee0848f52d
--- /dev/null
+++ b/login/dockerfiles/typescript-proto-client.Dockerfile
@@ -0,0 +1,6 @@
+FROM login-pnpm AS typescript-proto-client
+COPY packages/zitadel-proto/package.json ./packages/zitadel-proto/
+RUN --mount=type=cache,id=pnpm,target=/pnpm/store \
+ pnpm install --frozen-lockfile --workspace-root --filter zitadel-proto
+COPY packages/zitadel-proto ./packages/zitadel-proto
+RUN pnpm generate
diff --git a/login/dockerfiles/typescript-proto-client.Dockerfile.dockerignore b/login/dockerfiles/typescript-proto-client.Dockerfile.dockerignore
new file mode 100644
index 0000000000..e67848e8c3
--- /dev/null
+++ b/login/dockerfiles/typescript-proto-client.Dockerfile.dockerignore
@@ -0,0 +1,11 @@
+*
+!/packages/zitadel-proto/
+packages/zitadel-proto/google
+packages/zitadel-proto/zitadel
+packages/zitadel-proto/protoc-gen-openapiv2
+packages/zitadel-proto/validate
+
+**/*.md
+**/*.png
+**/node_modules
+**/.turbo
\ No newline at end of file
diff --git a/login/meta.json b/login/meta.json
new file mode 100644
index 0000000000..2c443f76cf
--- /dev/null
+++ b/login/meta.json
@@ -0,0 +1,4 @@
+{
+ "name": "ZITADEL typescript Monorepo with Changesets",
+ "description": "ZITADEL typescript monorepo preconfigured to publish packages via Changesets"
+}
diff --git a/login/package.json b/login/package.json
new file mode 100644
index 0000000000..ce844c4b2c
--- /dev/null
+++ b/login/package.json
@@ -0,0 +1,55 @@
+{
+ "packageManager": "pnpm@9.1.2+sha256.19c17528f9ca20bd442e4ca42f00f1b9808a9cb419383cd04ba32ef19322aba7",
+ "private": true,
+ "name": "typescript-monorepo",
+ "scripts": {
+ "generate": "pnpm exec turbo run generate",
+ "build": "pnpm exec turbo run build",
+ "build:client:standalone": "pnpm exec turbo run build:client:standalone",
+ "build:login:standalone": "pnpm exec turbo run build:login:standalone",
+ "build:packages": "pnpm exec turbo run build --filter=./packages/*",
+ "build:apps": "pnpm exec turbo run build --filter=./apps/*",
+ "test": "pnpm exec turbo run test",
+ "start": "pnpm exec turbo run start",
+ "start:built": "pnpm exec turbo run start:built",
+ "test:unit": "pnpm exec turbo run test:unit -- --passWithNoTests",
+ "test:unit:standalone": "pnpm exec turbo run test:unit:standalone -- --passWithNoTests",
+ "test:integration": "cd apps/login-test-integration && pnpm test:integration",
+ "test:integration:setup": "NODE_ENV=test pnpm exec turbo run test:integration:setup",
+ "test:acceptance": "cd apps/login-test-acceptance && pnpm test:acceptance",
+ "test:acceptance:setup": "cd apps/login-test-acceptance && pnpm test:acceptance:setup",
+ "test:watch": "pnpm exec turbo run test:watch",
+ "dev": "pnpm exec turbo run dev --no-cache --continue",
+ "dev:local": "pnpm test:acceptance:setup",
+ "lint": "pnpm exec turbo run lint",
+ "lint:fix": "pnpm exec turbo run lint:fix",
+ "clean": "pnpm exec turbo run clean && rm -rf node_modules",
+ "format:fix": "pnpm exec prettier --write \"**/*.{ts,tsx,md}\"",
+ "format": "pnpm exec prettier --check \"**/*.{ts,tsx,md}\"",
+ "changeset": "pnpm exec changeset",
+ "version-packages": "pnpm exec changeset version",
+ "release": "pnpm exec turbo run build --filter=login^... && pnpm exec changeset publish"
+ },
+ "pnpm": {
+ "overrides": {
+ "@typescript-eslint/parser": "^7.9.0"
+ }
+ },
+ "devDependencies": {
+ "@changesets/cli": "^2.29.2",
+ "@vitejs/plugin-react": "^4.4.1",
+ "@zitadel/eslint-config": "workspace:*",
+ "@zitadel/prettier-config": "workspace:*",
+ "axios": "^1.8.4",
+ "dotenv": "^16.5.0",
+ "dotenv-cli": "^8.0.0",
+ "eslint": "8.57.1",
+ "prettier": "^3.5.3",
+ "prettier-plugin-organize-imports": "^4.1.0",
+ "tsup": "^8.4.0",
+ "turbo": "2.5.0",
+ "typescript": "^5.8.3",
+ "vite-tsconfig-paths": "^5.1.4",
+ "vitest": "^3.1.2"
+ }
+}
diff --git a/login/packages/zitadel-client/.eslintrc.cjs b/login/packages/zitadel-client/.eslintrc.cjs
new file mode 100644
index 0000000000..0eb32ca20d
--- /dev/null
+++ b/login/packages/zitadel-client/.eslintrc.cjs
@@ -0,0 +1,4 @@
+module.exports = {
+ root: true,
+ extends: ["@zitadel/eslint-config"],
+};
diff --git a/login/packages/zitadel-client/.gitignore b/login/packages/zitadel-client/.gitignore
new file mode 100644
index 0000000000..8ff894e88c
--- /dev/null
+++ b/login/packages/zitadel-client/.gitignore
@@ -0,0 +1,4 @@
+src/proto
+node_modules
+dist
+.turbo
diff --git a/login/packages/zitadel-client/CHANGELOG.md b/login/packages/zitadel-client/CHANGELOG.md
new file mode 100644
index 0000000000..f1107bcc5a
--- /dev/null
+++ b/login/packages/zitadel-client/CHANGELOG.md
@@ -0,0 +1,77 @@
+# @zitadel/client
+
+## 1.2.0
+
+### Minor Changes
+
+- 62ad388: revert CJS support
+
+## 1.1.0
+
+### Minor Changes
+
+- 9692297: add CJS and ESM support
+
+## 1.0.7
+
+### Patch Changes
+
+- Updated dependencies [97b0332]
+ - @zitadel/proto@1.0.4
+
+## 1.0.6
+
+### Patch Changes
+
+- 90fbdd1: use node16/nodenext module resolution
+- Updated dependencies [90fbdd1]
+ - @zitadel/proto@1.0.3
+
+## 1.0.5
+
+### Patch Changes
+
+- 4fa22c0: fix export for grpcweb transport
+
+## 1.0.4
+
+### Patch Changes
+
+- 28dc956: dynamic properties for system token utility
+
+## 1.0.3
+
+### Patch Changes
+
+- ef1c801: add missing client transport utility
+
+## 1.0.2
+
+### Patch Changes
+
+- Updated dependencies
+ - @zitadel/proto@1.0.2
+
+## 1.0.1
+
+### Patch Changes
+
+- README updates
+- Updated dependencies
+ - @zitadel/proto@1.0.1
+
+## 1.0.0
+
+### Major Changes
+
+- 32e1199: Initial Release
+
+### Minor Changes
+
+- f32ab7f: Initial release
+
+### Patch Changes
+
+- Updated dependencies [f32ab7f]
+- Updated dependencies [32e1199]
+ - @zitadel/proto@1.0.0
diff --git a/login/packages/zitadel-client/README.md b/login/packages/zitadel-client/README.md
new file mode 100644
index 0000000000..0a14fe32f5
--- /dev/null
+++ b/login/packages/zitadel-client/README.md
@@ -0,0 +1,53 @@
+# ZITADEL Client
+
+This package exports services and utilities to interact with ZITADEL
+
+## Installation
+
+To install the package, use npm or yarn:
+
+```sh
+npm install @zitadel/client
+```
+
+or
+
+```sh
+yarn add @zitadel/client
+```
+
+## Usage
+
+### Importing Services
+
+You can import and use the services provided by this package to interact with ZITADEL.
+
+```ts
+import { createSettingsServiceClient, makeReqCtx } from "@zitadel/client/v2";
+
+// Example usage
+const transport = createServerTransport(process.env.ZITADEL_SERVICE_USER_TOKEN!, { baseUrl: process.env.ZITADEL_API_URL! });
+
+const settingsService = createSettingsServiceClient(transport);
+
+settingsService.getBrandingSettings({ ctx: makeReqCtx("orgId") }, {});
+```
+
+### Utilities
+
+This package also provides various utilities to work with ZITADEL
+
+```ts
+import { timestampMs } from "@zitadel/client";
+
+// Example usage
+console.log(`${timestampMs(session.creationDate)}`);
+```
+
+## Documentation
+
+For detailed documentation and API references, please visit the [ZITADEL documentation](https://zitadel.com/docs).
+
+## Contributing
+
+Contributions are welcome! Please read the contributing guidelines before getting started.
diff --git a/login/packages/zitadel-client/package.json b/login/packages/zitadel-client/package.json
new file mode 100644
index 0000000000..298f54f088
--- /dev/null
+++ b/login/packages/zitadel-client/package.json
@@ -0,0 +1,71 @@
+{
+ "name": "@zitadel/client",
+ "version": "1.2.0",
+ "license": "MIT",
+ "publishConfig": {
+ "access": "public"
+ },
+ "type": "module",
+ "exports": {
+ ".": {
+ "types": "./dist/index.d.ts",
+ "import": "./dist/index.js",
+ "require": "./dist/index.cjs"
+ },
+ "./v1": {
+ "types": "./dist/v1.d.ts",
+ "import": "./dist/v1.js",
+ "require": "./dist/v1.cjs"
+ },
+ "./v2": {
+ "types": "./dist/v2.d.ts",
+ "import": "./dist/v2.js",
+ "require": "./dist/v2.cjs"
+ },
+ "./v3alpha": {
+ "types": "./dist/v3alpha.d.ts",
+ "import": "./dist/v3alpha.js",
+ "require": "./dist/v3alpha.cjs"
+ },
+ "./node": {
+ "types": "./dist/node.d.ts",
+ "import": "./dist/node.js",
+ "require": "./dist/node.cjs"
+ },
+ "./web": {
+ "types": "./dist/web.d.ts",
+ "import": "./dist/web.js",
+ "require": "./dist/web.cjs"
+ }
+ },
+ "files": [
+ "dist/**"
+ ],
+ "sideEffects": false,
+ "scripts": {
+ "build": "pnpm exec tsup",
+ "build:client:standalone": "pnpm build",
+ "test": "pnpm test:unit",
+ "test:watch": "pnpm test:unit:watch",
+ "test:unit": "pnpm exec vitest",
+ "test:unit:standalone": "pnpm test:unit",
+ "test:unit:watch": "pnpm exec vitest --watch",
+ "dev": "pnpm exec tsup --watch --dts",
+ "lint": "eslint \"src/**/*.ts*\"",
+ "clean": "rm -rf .turbo && rm -rf node_modules && rm -rf dist"
+ },
+ "dependencies": {
+ "@bufbuild/protobuf": "^2.2.2",
+ "@connectrpc/connect": "^2.0.0",
+ "@connectrpc/connect-node": "^2.0.0",
+ "@connectrpc/connect-web": "^2.0.0",
+ "jose": "^5.3.0",
+ "@zitadel/proto": "workspace:*"
+ },
+ "devDependencies": {
+ "@bufbuild/protocompile": "^0.0.1",
+ "@bufbuild/buf": "^1.53.0",
+ "@zitadel/tsconfig": "workspace:*",
+ "@zitadel/eslint-config": "workspace:*"
+ }
+}
diff --git a/login/packages/zitadel-client/src/helpers.ts b/login/packages/zitadel-client/src/helpers.ts
new file mode 100644
index 0000000000..637cadf538
--- /dev/null
+++ b/login/packages/zitadel-client/src/helpers.ts
@@ -0,0 +1,11 @@
+import type { DescService } from "@bufbuild/protobuf";
+import { Timestamp, timestampDate } from "@bufbuild/protobuf/wkt";
+import { createClient, Transport } from "@connectrpc/connect";
+
+export function createClientFor(service: TService) {
+ return (transport: Transport) => createClient(service, transport);
+}
+
+export function toDate(timestamp: Timestamp | undefined): Date | undefined {
+ return timestamp ? timestampDate(timestamp) : undefined;
+}
diff --git a/login/packages/zitadel-client/src/index.ts b/login/packages/zitadel-client/src/index.ts
new file mode 100644
index 0000000000..f3f93e593f
--- /dev/null
+++ b/login/packages/zitadel-client/src/index.ts
@@ -0,0 +1,10 @@
+export { createClientFor, toDate } from "./helpers.js";
+export { NewAuthorizationBearerInterceptor } from "./interceptors.js";
+
+// TODO: Move this to `./protobuf.ts` and export it from there
+export { create, fromJson, toJson } from "@bufbuild/protobuf";
+export type { JsonObject } from "@bufbuild/protobuf";
+export type { GenService } from "@bufbuild/protobuf/codegenv1";
+export { TimestampSchema, timestampDate, timestampFromDate, timestampFromMs, timestampMs } from "@bufbuild/protobuf/wkt";
+export type { Duration, Timestamp } from "@bufbuild/protobuf/wkt";
+export type { Client, Code, ConnectError } from "@connectrpc/connect";
diff --git a/login/packages/zitadel-client/src/interceptors.test.ts b/login/packages/zitadel-client/src/interceptors.test.ts
new file mode 100644
index 0000000000..a5100f866a
--- /dev/null
+++ b/login/packages/zitadel-client/src/interceptors.test.ts
@@ -0,0 +1,67 @@
+import { Int32Value } from "@bufbuild/protobuf/wkt";
+import { compileService } from "@bufbuild/protocompile";
+import { createRouterTransport, HandlerContext } from "@connectrpc/connect";
+import { describe, expect, test, vitest } from "vitest";
+import { NewAuthorizationBearerInterceptor } from "./interceptors.js";
+
+const TestService = compileService(`
+ syntax = "proto3";
+ package handwritten;
+ service TestService {
+ rpc Unary(Int32Value) returns (StringValue);
+ }
+ message Int32Value {
+ int32 value = 1;
+ }
+ message StringValue {
+ string value = 1;
+ }
+`);
+
+describe("NewAuthorizationBearerInterceptor", () => {
+ const transport = {
+ interceptors: [NewAuthorizationBearerInterceptor("mytoken")],
+ };
+
+ test("injects the authorization token", async () => {
+ const handler = vitest.fn((request: Int32Value, context: HandlerContext) => {
+ return { value: request.value.toString() };
+ });
+
+ const service = createRouterTransport(
+ ({ rpc }) => {
+ rpc(TestService.method.unary, handler);
+ },
+ { transport },
+ );
+
+ await service.unary(TestService.method.unary, undefined, undefined, {}, { value: 9001 });
+
+ expect(handler).toBeCalled();
+ expect(handler.mock.calls[0][1].requestHeader.get("Authorization")).toBe("Bearer mytoken");
+ });
+
+ test("do not overwrite the previous authorization token", async () => {
+ const handler = vitest.fn((request: Int32Value, context: HandlerContext) => {
+ return { value: request.value.toString() };
+ });
+
+ const service = createRouterTransport(
+ ({ rpc }) => {
+ rpc(TestService.method.unary, handler);
+ },
+ { transport },
+ );
+
+ await service.unary(
+ TestService.method.unary,
+ undefined,
+ undefined,
+ { Authorization: "Bearer somethingelse" },
+ { value: 9001 },
+ );
+
+ expect(handler).toBeCalled();
+ expect(handler.mock.calls[0][1].requestHeader.get("Authorization")).toBe("Bearer somethingelse");
+ });
+});
diff --git a/login/packages/zitadel-client/src/interceptors.ts b/login/packages/zitadel-client/src/interceptors.ts
new file mode 100644
index 0000000000..9d719c7d70
--- /dev/null
+++ b/login/packages/zitadel-client/src/interceptors.ts
@@ -0,0 +1,16 @@
+import type { Interceptor } from "@connectrpc/connect";
+
+/**
+ * Creates an interceptor that adds an Authorization header with a Bearer token.
+ * @param token
+ */
+export function NewAuthorizationBearerInterceptor(token: string): Interceptor {
+ return (next) => (req) => {
+ // TODO: I am not what is the intent of checking for the Authorization header
+ // and setting it if it is not present.
+ if (!req.header.get("Authorization")) {
+ req.header.set("Authorization", `Bearer ${token}`);
+ }
+ return next(req);
+ };
+}
diff --git a/login/packages/zitadel-client/src/node.ts b/login/packages/zitadel-client/src/node.ts
new file mode 100644
index 0000000000..0b15310d2c
--- /dev/null
+++ b/login/packages/zitadel-client/src/node.ts
@@ -0,0 +1,36 @@
+import { createGrpcTransport, GrpcTransportOptions } from "@connectrpc/connect-node";
+import { importPKCS8, SignJWT } from "jose";
+import { NewAuthorizationBearerInterceptor } from "./interceptors.js";
+
+/**
+ * Create a server transport using grpc with the given token and configuration options.
+ * @param token
+ * @param opts
+ */
+export function createServerTransport(token: string, opts: GrpcTransportOptions) {
+ return createGrpcTransport({
+ ...opts,
+ interceptors: [...(opts.interceptors || []), NewAuthorizationBearerInterceptor(token)],
+ });
+}
+
+export async function newSystemToken({
+ audience,
+ subject,
+ key,
+ expirationTime,
+}: {
+ audience: string;
+ subject: string;
+ key: string;
+ expirationTime?: number | string | Date;
+}) {
+ return await new SignJWT({})
+ .setProtectedHeader({ alg: "RS256" })
+ .setIssuedAt()
+ .setExpirationTime(expirationTime ?? "1h")
+ .setIssuer(subject)
+ .setSubject(subject)
+ .setAudience(audience)
+ .sign(await importPKCS8(key, "RS256"));
+}
diff --git a/login/packages/zitadel-client/src/v1.ts b/login/packages/zitadel-client/src/v1.ts
new file mode 100644
index 0000000000..d04180cf88
--- /dev/null
+++ b/login/packages/zitadel-client/src/v1.ts
@@ -0,0 +1,11 @@
+import { createClientFor } from "./helpers.js";
+
+import { AdminService } from "@zitadel/proto/zitadel/admin_pb.js";
+import { AuthService } from "@zitadel/proto/zitadel/auth_pb.js";
+import { ManagementService } from "@zitadel/proto/zitadel/management_pb.js";
+import { SystemService } from "@zitadel/proto/zitadel/system_pb.js";
+
+export const createAdminServiceClient = createClientFor(AdminService);
+export const createAuthServiceClient = createClientFor(AuthService);
+export const createManagementServiceClient = createClientFor(ManagementService);
+export const createSystemServiceClient = createClientFor(SystemService);
diff --git a/login/packages/zitadel-client/src/v2.ts b/login/packages/zitadel-client/src/v2.ts
new file mode 100644
index 0000000000..49cf901734
--- /dev/null
+++ b/login/packages/zitadel-client/src/v2.ts
@@ -0,0 +1,27 @@
+import { create } from "@bufbuild/protobuf";
+import { FeatureService } from "@zitadel/proto/zitadel/feature/v2/feature_service_pb.js";
+import { IdentityProviderService } from "@zitadel/proto/zitadel/idp/v2/idp_service_pb.js";
+import { RequestContextSchema } from "@zitadel/proto/zitadel/object/v2/object_pb.js";
+import { OIDCService } from "@zitadel/proto/zitadel/oidc/v2/oidc_service_pb.js";
+import { OrganizationService } from "@zitadel/proto/zitadel/org/v2/org_service_pb.js";
+import { SAMLService } from "@zitadel/proto/zitadel/saml/v2/saml_service_pb.js";
+import { SessionService } from "@zitadel/proto/zitadel/session/v2/session_service_pb.js";
+import { SettingsService } from "@zitadel/proto/zitadel/settings/v2/settings_service_pb.js";
+import { UserService } from "@zitadel/proto/zitadel/user/v2/user_service_pb.js";
+
+import { createClientFor } from "./helpers.js";
+
+export const createUserServiceClient = createClientFor(UserService);
+export const createSettingsServiceClient = createClientFor(SettingsService);
+export const createSessionServiceClient = createClientFor(SessionService);
+export const createOIDCServiceClient = createClientFor(OIDCService);
+export const createSAMLServiceClient = createClientFor(SAMLService);
+export const createOrganizationServiceClient = createClientFor(OrganizationService);
+export const createFeatureServiceClient = createClientFor(FeatureService);
+export const createIdpServiceClient = createClientFor(IdentityProviderService);
+
+export function makeReqCtx(orgId: string | undefined) {
+ return create(RequestContextSchema, {
+ resourceOwner: orgId ? { case: "orgId", value: orgId } : { case: "instance", value: true },
+ });
+}
diff --git a/login/packages/zitadel-client/src/v3alpha.ts b/login/packages/zitadel-client/src/v3alpha.ts
new file mode 100644
index 0000000000..a5cc533ade
--- /dev/null
+++ b/login/packages/zitadel-client/src/v3alpha.ts
@@ -0,0 +1,6 @@
+import { ZITADELUsers } from "@zitadel/proto/zitadel/resources/user/v3alpha/user_service_pb.js";
+import { ZITADELUserSchemas } from "@zitadel/proto/zitadel/resources/userschema/v3alpha/user_schema_service_pb.js";
+import { createClientFor } from "./helpers.js";
+
+export const createUserSchemaServiceClient = createClientFor(ZITADELUserSchemas);
+export const createUserServiceClient = createClientFor(ZITADELUsers);
diff --git a/login/packages/zitadel-client/src/web.ts b/login/packages/zitadel-client/src/web.ts
new file mode 100644
index 0000000000..26c40da0fe
--- /dev/null
+++ b/login/packages/zitadel-client/src/web.ts
@@ -0,0 +1,15 @@
+import { GrpcTransportOptions } from "@connectrpc/connect-node";
+import { createGrpcWebTransport } from "@connectrpc/connect-web";
+import { NewAuthorizationBearerInterceptor } from "./interceptors.js";
+
+/**
+ * Create a client transport using grpc web with the given token and configuration options.
+ * @param token
+ * @param opts
+ */
+export function createClientTransport(token: string, opts: GrpcTransportOptions) {
+ return createGrpcWebTransport({
+ ...opts,
+ interceptors: [...(opts.interceptors || []), NewAuthorizationBearerInterceptor(token)],
+ });
+}
diff --git a/login/packages/zitadel-client/tsconfig.json b/login/packages/zitadel-client/tsconfig.json
new file mode 100644
index 0000000000..5f0ea69110
--- /dev/null
+++ b/login/packages/zitadel-client/tsconfig.json
@@ -0,0 +1,5 @@
+{
+ "extends": "@zitadel/tsconfig/tsup.json",
+ "include": ["./src/**/*"],
+ "exclude": ["dist", "build", "node_modules"]
+}
diff --git a/login/packages/zitadel-client/tsup.config.ts b/login/packages/zitadel-client/tsup.config.ts
new file mode 100644
index 0000000000..3c9eeb8b83
--- /dev/null
+++ b/login/packages/zitadel-client/tsup.config.ts
@@ -0,0 +1,13 @@
+import { defineConfig, Options } from "tsup";
+
+export default defineConfig((options: Options) => ({
+ entry: ["src/index.ts", "src/v1.ts", "src/v2.ts", "src/v3alpha.ts", "src/node.ts", "src/web.ts"],
+ format: ["esm", "cjs"],
+ treeshake: false,
+ splitting: true,
+ dts: true,
+ minify: false,
+ clean: true,
+ sourcemap: true,
+ ...options,
+}));
diff --git a/login/packages/zitadel-client/turbo.json b/login/packages/zitadel-client/turbo.json
new file mode 100644
index 0000000000..b54d25e2ba
--- /dev/null
+++ b/login/packages/zitadel-client/turbo.json
@@ -0,0 +1,12 @@
+{
+ "extends": ["//"],
+ "tasks": {
+ "build": {
+ "outputs": ["dist/**"],
+ "dependsOn": ["@zitadel/proto#generate"]
+ },
+ "build:client:standalone": {
+ "outputs": ["dist/**"]
+ }
+ }
+}
diff --git a/login/packages/zitadel-eslint-config/CHANGELOG.md b/login/packages/zitadel-eslint-config/CHANGELOG.md
new file mode 100644
index 0000000000..6759f857ff
--- /dev/null
+++ b/login/packages/zitadel-eslint-config/CHANGELOG.md
@@ -0,0 +1,13 @@
+# @zitadel/eslint-config
+
+## 0.1.1
+
+### Patch Changes
+
+- README updates
+
+## 0.1.0
+
+### Minor Changes
+
+- f32ab7f: Initial release
diff --git a/login/packages/zitadel-eslint-config/README.md b/login/packages/zitadel-eslint-config/README.md
new file mode 100644
index 0000000000..d8d6851f91
--- /dev/null
+++ b/login/packages/zitadel-eslint-config/README.md
@@ -0,0 +1,35 @@
+# ZITADEL ESLint Config
+
+This package provides the ESLint configuration used by ZITADEL projects. It includes a set of rules and plugins to ensure consistent code quality and style across all ZITADEL codebases.
+
+## Installation
+
+To install the package, use npm or yarn:
+
+```sh
+npm install @zitadel/eslint-config
+```
+
+or
+
+```sh
+yarn add @zitadel/eslint-config
+```
+
+## Usage
+
+To use the ESLint configuration in your project, extend it in your `.eslintrc` file:
+
+```js
+{
+ "extends": "@zitadel/eslint-config"
+}
+```
+
+## Documentation
+
+For detailed documentation and configuration options, please refer to the [ESLint documentation](https://eslint.org/docs/user-guide/configuring).
+
+## Contributing
+
+Contributions are welcome! Please read the contributing guidelines before getting started.
diff --git a/login/packages/zitadel-eslint-config/index.js b/login/packages/zitadel-eslint-config/index.js
new file mode 100644
index 0000000000..6a53b2a5e6
--- /dev/null
+++ b/login/packages/zitadel-eslint-config/index.js
@@ -0,0 +1,13 @@
+module.exports = {
+ parser: "@babel/eslint-parser",
+ extends: ["next", "turbo", "prettier"],
+ rules: {
+ "@next/next/no-html-link-for-pages": "off",
+ },
+ parserOptions: {
+ requireConfigFile: false,
+ babelOptions: {
+ presets: ["next/babel"],
+ },
+ },
+};
diff --git a/login/packages/zitadel-eslint-config/package.json b/login/packages/zitadel-eslint-config/package.json
new file mode 100644
index 0000000000..84c9c76dab
--- /dev/null
+++ b/login/packages/zitadel-eslint-config/package.json
@@ -0,0 +1,17 @@
+{
+ "name": "@zitadel/eslint-config",
+ "version": "0.1.1",
+ "main": "index.js",
+ "license": "MIT",
+ "publishConfig": {
+ "access": "public"
+ },
+ "dependencies": {
+ "@typescript-eslint/parser": "^7.9.0",
+ "eslint-config-next": "^14.2.18",
+ "eslint-config-prettier": "^9.1.0",
+ "eslint-config-turbo": "^2.0.9",
+ "eslint-plugin-react": "^7.34.1",
+ "@babel/eslint-parser": "^7.25.9"
+ }
+}
diff --git a/login/packages/zitadel-prettier-config/CHANGELOG.md b/login/packages/zitadel-prettier-config/CHANGELOG.md
new file mode 100644
index 0000000000..83045c3320
--- /dev/null
+++ b/login/packages/zitadel-prettier-config/CHANGELOG.md
@@ -0,0 +1,13 @@
+# @zitadel/prettier-config
+
+## 0.1.1
+
+### Patch Changes
+
+- README updates
+
+## 0.1.0
+
+### Minor Changes
+
+- f32ab7f: Initial release
diff --git a/login/packages/zitadel-prettier-config/README.md b/login/packages/zitadel-prettier-config/README.md
new file mode 100644
index 0000000000..f33913273b
--- /dev/null
+++ b/login/packages/zitadel-prettier-config/README.md
@@ -0,0 +1,36 @@
+# ZITADEL Prettier Config
+
+This package provides the Prettier configuration used by ZITADEL projects. It includes a set of formatting rules to ensure consistent code style across all ZITADEL codebases.
+
+## Installation
+
+To install the package, use npm or yarn:
+
+```sh
+npm install @zitadel/prettier-config
+```
+
+or
+
+```sh
+yarn add @zitadel/prettier-config
+```
+
+## Usage
+
+To use the Prettier configuration in your project, extend it in your `prettier.config.js` file:
+
+```js
+module.exports = {
+ ...require("@zitadel/prettier-config"),
+ // Add your custom configurations here
+};
+```
+
+## Documentation
+
+For detailed documentation and configuration options, please refer to the [Prettier documentation](https://prettier.io/docs/en/configuration.html).
+
+## Contributing
+
+Contributions are welcome! Please read the contributing guidelines before getting started.
diff --git a/login/packages/zitadel-prettier-config/index.js b/login/packages/zitadel-prettier-config/index.js
new file mode 100644
index 0000000000..31d8c455e8
--- /dev/null
+++ b/login/packages/zitadel-prettier-config/index.js
@@ -0,0 +1,11 @@
+export default {
+ printWidth: 80,
+ tabWidth: 2,
+ useTabs: false,
+ semi: true,
+ singleQuote: false,
+ trailingComma: 'all',
+ bracketSpacing: true,
+ arrowParens: 'always',
+ plugins: ["prettier-plugin-organize-imports"]
+};
diff --git a/login/packages/zitadel-prettier-config/package.json b/login/packages/zitadel-prettier-config/package.json
new file mode 100644
index 0000000000..7b7cbf253e
--- /dev/null
+++ b/login/packages/zitadel-prettier-config/package.json
@@ -0,0 +1,12 @@
+{
+ "name": "@zitadel/prettier-config",
+ "version": "0.1.1",
+ "description": "Prettier configuration",
+ "type": "module",
+ "publishConfig": {
+ "access": "public"
+ },
+ "exports": {
+ ".": "./index.js"
+ }
+}
diff --git a/login/packages/zitadel-proto/.gitignore b/login/packages/zitadel-proto/.gitignore
new file mode 100644
index 0000000000..20bdea6767
--- /dev/null
+++ b/login/packages/zitadel-proto/.gitignore
@@ -0,0 +1,5 @@
+zitadel
+google
+protoc-gen-openapiv2
+validate
+node_modules
diff --git a/login/packages/zitadel-proto/CHANGELOG.md b/login/packages/zitadel-proto/CHANGELOG.md
new file mode 100644
index 0000000000..c3964e2b29
--- /dev/null
+++ b/login/packages/zitadel-proto/CHANGELOG.md
@@ -0,0 +1,47 @@
+# @zitadel/proto
+
+## 1.2.0
+
+### Minor Changes
+
+- 62ad388: revert CJS support
+
+## 1.1.0
+
+### Minor Changes
+
+- 9692297: add CJS and ESM support
+
+## 1.0.4
+
+### Patch Changes
+
+- 97b0332: bind @zitadel/proto version to zitadel tag
+
+## 1.0.3
+
+### Patch Changes
+
+- 90fbdd1: use node16/nodenext module resolution
+
+## 1.0.2
+
+### Patch Changes
+
+- include validate, google and protoc-gen-openapiv2
+
+## 1.0.1
+
+### Patch Changes
+
+- README updates
+
+## 1.0.0
+
+### Major Changes
+
+- 32e1199: Initial Release
+
+### Minor Changes
+
+- f32ab7f: Initial release
diff --git a/login/packages/zitadel-proto/README.md b/login/packages/zitadel-proto/README.md
new file mode 100644
index 0000000000..bf8a064c12
--- /dev/null
+++ b/login/packages/zitadel-proto/README.md
@@ -0,0 +1,35 @@
+# ZITADEL Proto
+
+This package provides the Protocol Buffers (proto) definitions used by ZITADEL projects. It includes the proto files and generated code for interacting with ZITADEL's gRPC APIs.
+
+## Installation
+
+To install the package, use npm or yarn:
+
+```sh
+npm install @zitadel/proto
+```
+
+or
+
+```sh
+yarn add @zitadel/proto
+```
+
+## Usage
+
+To use the proto definitions in your project, import the generated code:
+
+```ts
+import { Organization } from "@zitadel/proto/zitadel/org/v2/org_pb";
+
+const org: Organization | null = await getDefaultOrg();
+```
+
+## Documentation
+
+For detailed documentation and API references, please visit the [ZITADEL documentation](https://zitadel.com/docs).
+
+## Contributing
+
+Contributions are welcome! Please read the contributing guidelines before getting started.
diff --git a/login/packages/zitadel-proto/buf.gen.yaml b/login/packages/zitadel-proto/buf.gen.yaml
new file mode 100644
index 0000000000..84ecfaea9d
--- /dev/null
+++ b/login/packages/zitadel-proto/buf.gen.yaml
@@ -0,0 +1,10 @@
+version: v2
+managed:
+ enabled: true
+plugins:
+ - remote: buf.build/bufbuild/es:v2.2.0
+ out: .
+ include_imports: true
+ opt:
+ - json_types=true
+ - import_extension=js
diff --git a/login/packages/zitadel-proto/package.json b/login/packages/zitadel-proto/package.json
new file mode 100644
index 0000000000..2c60bced4b
--- /dev/null
+++ b/login/packages/zitadel-proto/package.json
@@ -0,0 +1,26 @@
+{
+ "name": "@zitadel/proto",
+ "version": "1.2.0",
+ "license": "MIT",
+ "publishConfig": {
+ "access": "public"
+ },
+ "type": "module",
+ "files": [
+ "zitadel/**",
+ "validate/**",
+ "google/**",
+ "protoc-gen-openapiv2/**"
+ ],
+ "sideEffects": false,
+ "scripts": {
+ "generate": "pnpm exec buf generate https://github.com/zitadel/zitadel.git --path ./proto/zitadel",
+ "clean": "rm -rf zitadel .turbo node_modules google protoc-gen-openapiv2 validate"
+ },
+ "dependencies": {
+ "@bufbuild/protobuf": "^2.2.2"
+ },
+ "devDependencies": {
+ "@bufbuild/buf": "^1.53.0"
+ }
+}
diff --git a/login/packages/zitadel-proto/turbo.json b/login/packages/zitadel-proto/turbo.json
new file mode 100644
index 0000000000..2d24f0349b
--- /dev/null
+++ b/login/packages/zitadel-proto/turbo.json
@@ -0,0 +1,9 @@
+{
+ "extends": ["//"],
+ "tasks": {
+ "generate": {
+ "outputs": ["zitadel/**"],
+ "cache": false
+ }
+ }
+}
diff --git a/login/packages/zitadel-tailwind-config/CHANGELOG.md b/login/packages/zitadel-tailwind-config/CHANGELOG.md
new file mode 100644
index 0000000000..e3c4d7207e
--- /dev/null
+++ b/login/packages/zitadel-tailwind-config/CHANGELOG.md
@@ -0,0 +1,13 @@
+# @zitadel/tailwind-config
+
+## 0.1.1
+
+### Patch Changes
+
+- README updates
+
+## 0.1.0
+
+### Minor Changes
+
+- f32ab7f: Initial release
diff --git a/login/packages/zitadel-tailwind-config/README.md b/login/packages/zitadel-tailwind-config/README.md
new file mode 100644
index 0000000000..f52b6c263b
--- /dev/null
+++ b/login/packages/zitadel-tailwind-config/README.md
@@ -0,0 +1,36 @@
+# ZITADEL Tailwind Config
+
+This package provides the Tailwind CSS configuration used by ZITADEL projects. It includes a set of default styles, themes, and utility classes to ensure consistent design and styling across all ZITADEL codebases.
+
+## Installation
+
+To install the package, use npm or yarn:
+
+```sh
+npm install @zitadel/tailwind-config
+```
+
+or
+
+```sh
+yarn add @zitadel/tailwind-config
+```
+
+## Usage
+
+To use the Tailwind CSS configuration in your project, extend it in your `tailwind.config.js` file:
+
+```js
+module.exports = {
+ presets: [require("@zitadel/tailwind-config")],
+ // Add your custom configurations here
+};
+```
+
+## Documentation
+
+For detailed documentation and configuration options, please refer to the [Tailwind CSS documentation](https://tailwindcss.com/docs)
+
+## Contributing
+
+Contributions are welcome! Please read the contributing guidelines before getting started.
diff --git a/login/packages/zitadel-tailwind-config/package.json b/login/packages/zitadel-tailwind-config/package.json
new file mode 100644
index 0000000000..8fba4bac95
--- /dev/null
+++ b/login/packages/zitadel-tailwind-config/package.json
@@ -0,0 +1,12 @@
+{
+ "name": "@zitadel/tailwind-config",
+ "version": "0.1.1",
+ "publishConfig": {
+ "access": "public"
+ },
+ "main": "index.js",
+ "devDependencies": {
+ "tailwindcss": "^4.1.4",
+ "@tailwindcss/forms": "0.5.3"
+ }
+}
diff --git a/login/packages/zitadel-tailwind-config/tailwind.config.mjs b/login/packages/zitadel-tailwind-config/tailwind.config.mjs
new file mode 100644
index 0000000000..4a9a437cb7
--- /dev/null
+++ b/login/packages/zitadel-tailwind-config/tailwind.config.mjs
@@ -0,0 +1,97 @@
+import colors from "tailwindcss/colors";
+
+/** @type {import('tailwindcss').Config} */
+export default {
+ content: ["./app/**/*.{js,ts,jsx,tsx}", "./page/**/*.{js,ts,jsx,tsx}", "./ui/**/*.{js,ts,jsx,tsx}"],
+ future: {
+ hoverOnlyWhenSupported: true,
+ },
+ theme: {
+ extend: {
+ // https://vercel.com/design/color
+ fontSize: {
+ "12px": "12px",
+ "14px": "14px",
+ },
+ colors: {
+ gray: colors.zinc,
+ divider: {
+ dark: "rgba(135,149,161,.2)",
+ light: "rgba(135,149,161,.2)",
+ },
+ input: {
+ light: {
+ label: "#000000c7",
+ background: "#00000004",
+ border: "#1a191954",
+ hoverborder: "1a1b1b",
+ },
+ dark: {
+ label: "#ffffffc7",
+ background: "#00000020",
+ border: "#f9f7f775",
+ hoverborder: "#e0e0e0",
+ },
+ },
+ button: {
+ light: {
+ border: "#0000001f",
+ },
+ dark: {
+ border: "#ffffff1f",
+ },
+ },
+ },
+ backgroundImage: ({ theme }) => ({
+ "dark-vc-border-gradient": `radial-gradient(at left top, ${theme(
+ "colors.gray.800",
+ )}, 50px, ${theme("colors.gray.800")} 50%)`,
+ "vc-border-gradient": `radial-gradient(at left top, ${theme(
+ "colors.gray.200",
+ )}, 50px, ${theme("colors.gray.300")} 50%)`,
+ }),
+ keyframes: ({ theme }) => ({
+ rerender: {
+ "0%": {
+ ["border-color"]: theme("colors.pink.500"),
+ },
+ "40%": {
+ ["border-color"]: theme("colors.pink.500"),
+ },
+ },
+ highlight: {
+ "0%": {
+ background: theme("colors.pink.500"),
+ color: theme("colors.white"),
+ },
+ "40%": {
+ background: theme("colors.pink.500"),
+ color: theme("colors.white"),
+ },
+ },
+ shimmer: {
+ "100%": {
+ transform: "translateX(100%)",
+ },
+ },
+ translateXReset: {
+ "100%": {
+ transform: "translateX(0)",
+ },
+ },
+ fadeToTransparent: {
+ "0%": {
+ opacity: 1,
+ },
+ "40%": {
+ opacity: 1,
+ },
+ "100%": {
+ opacity: 0,
+ },
+ },
+ }),
+ },
+ },
+ plugins: [require("@tailwindcss/forms")],
+};
diff --git a/login/packages/zitadel-tsconfig/CHANGELOG.md b/login/packages/zitadel-tsconfig/CHANGELOG.md
new file mode 100644
index 0000000000..c2d2e7e31c
--- /dev/null
+++ b/login/packages/zitadel-tsconfig/CHANGELOG.md
@@ -0,0 +1,13 @@
+# @zitadel/tsconfig
+
+## 0.1.1
+
+### Patch Changes
+
+- README updates
+
+## 0.1.0
+
+### Minor Changes
+
+- f32ab7f: Initial release
diff --git a/login/packages/zitadel-tsconfig/README.md b/login/packages/zitadel-tsconfig/README.md
new file mode 100644
index 0000000000..b93674b2b1
--- /dev/null
+++ b/login/packages/zitadel-tsconfig/README.md
@@ -0,0 +1,35 @@
+# ZITADEL TypeScript Config
+
+This package provides the TypeScript configuration used by ZITADEL projects. It includes a set of rules and settings to ensure consistent TypeScript configuration across all ZITADEL codebases.
+
+## Installation
+
+To install the package, use npm or yarn:
+
+```sh
+npm install @zitadel/tsconfig
+```
+
+or
+
+```sh
+yarn add @zitadel/tsconfig
+```
+
+## Usage
+
+To use the TypeScript configuration in your project, extend it in your `tsconfig.json` file:
+
+```json
+{
+ "extends": "@zitadel/tsconfig/tsup.json"
+}
+```
+
+## Documentation
+
+For detailed documentation and configuration options, please refer to the [TypeScript documentation](https://www.typescriptlang.org/docs/).
+
+## Contributing
+
+Contributions are welcome! Please read the contributing guidelines before getting started.
diff --git a/login/packages/zitadel-tsconfig/base.json b/login/packages/zitadel-tsconfig/base.json
new file mode 100644
index 0000000000..6d65860cce
--- /dev/null
+++ b/login/packages/zitadel-tsconfig/base.json
@@ -0,0 +1,20 @@
+{
+ "$schema": "https://json.schemastore.org/tsconfig",
+ "display": "Default",
+ "compilerOptions": {
+ "composite": false,
+ "declaration": true,
+ "declarationMap": true,
+ "esModuleInterop": true,
+ "forceConsistentCasingInFileNames": true,
+ "inlineSources": false,
+ "isolatedModules": true,
+ "moduleResolution": "node16",
+ "noUnusedLocals": false,
+ "noUnusedParameters": false,
+ "preserveWatchOutput": true,
+ "skipLibCheck": true,
+ "strict": true
+ },
+ "exclude": ["node_modules"]
+}
diff --git a/login/packages/zitadel-tsconfig/nextjs.json b/login/packages/zitadel-tsconfig/nextjs.json
new file mode 100644
index 0000000000..eaa9942e1e
--- /dev/null
+++ b/login/packages/zitadel-tsconfig/nextjs.json
@@ -0,0 +1,32 @@
+{
+ "$schema": "https://json.schemastore.org/tsconfig",
+ "display": "Next.js",
+ "extends": "./base.json",
+ "compilerOptions": {
+ "target": "es5",
+ "lib": ["dom", "dom.iterable", "esnext"],
+ "allowJs": true,
+ "skipLibCheck": true,
+ "strict": true,
+ "forceConsistentCasingInFileNames": true,
+ "noEmit": true,
+ "esModuleInterop": true,
+ "module": "esnext",
+ "moduleResolution": "bundler",
+ "resolveJsonModule": true,
+ "isolatedModules": true,
+ "jsx": "preserve",
+ "incremental": true,
+ "preserveSymlinks": true,
+ "declaration": false,
+ "declarationMap": false,
+ "baseUrl": ".",
+ "plugins": [
+ {
+ "name": "next"
+ }
+ ]
+ },
+ "include": ["src", "next-env.d.ts"],
+ "exclude": ["node_modules"]
+}
diff --git a/login/packages/zitadel-tsconfig/node20.json b/login/packages/zitadel-tsconfig/node20.json
new file mode 100644
index 0000000000..bc88cfbee4
--- /dev/null
+++ b/login/packages/zitadel-tsconfig/node20.json
@@ -0,0 +1,10 @@
+{
+ "$schema": "https://json.schemastore.org/tsconfig",
+ "display": "Node 20",
+ "extends": "./base.json",
+ "compilerOptions": {
+ "lib": ["es2023"],
+ "module": "node16",
+ "target": "es2022"
+ }
+}
diff --git a/login/packages/zitadel-tsconfig/package.json b/login/packages/zitadel-tsconfig/package.json
new file mode 100644
index 0000000000..a4d713db85
--- /dev/null
+++ b/login/packages/zitadel-tsconfig/package.json
@@ -0,0 +1,9 @@
+{
+ "name": "@zitadel/tsconfig",
+ "version": "0.1.1",
+ "publishConfig": {
+ "access": "public"
+ },
+ "type": "module",
+ "license": "MIT"
+}
diff --git a/login/packages/zitadel-tsconfig/react-library.json b/login/packages/zitadel-tsconfig/react-library.json
new file mode 100644
index 0000000000..3f6e3580f4
--- /dev/null
+++ b/login/packages/zitadel-tsconfig/react-library.json
@@ -0,0 +1,11 @@
+{
+ "$schema": "https://json.schemastore.org/tsconfig",
+ "display": "React Library",
+ "extends": "./base.json",
+ "compilerOptions": {
+ "jsx": "react-jsx",
+ "lib": ["dom", "ES2015"],
+ "module": "preserve",
+ "moduleResolution": "Bundler"
+ }
+}
diff --git a/login/packages/zitadel-tsconfig/tsup.json b/login/packages/zitadel-tsconfig/tsup.json
new file mode 100644
index 0000000000..1e5cbe42be
--- /dev/null
+++ b/login/packages/zitadel-tsconfig/tsup.json
@@ -0,0 +1,5 @@
+{
+ "$schema": "https://json.schemastore.org/tsconfig",
+ "display": "tsup",
+ "extends": "./node20.json"
+}
diff --git a/login/pnpm-lock.yaml b/login/pnpm-lock.yaml
new file mode 100644
index 0000000000..8c6424ccf6
--- /dev/null
+++ b/login/pnpm-lock.yaml
@@ -0,0 +1,9519 @@
+lockfileVersion: '9.0'
+
+settings:
+ autoInstallPeers: true
+ excludeLinksFromLockfile: false
+
+overrides:
+ '@typescript-eslint/parser': ^7.9.0
+
+importers:
+
+ .:
+ devDependencies:
+ '@changesets/cli':
+ specifier: ^2.29.2
+ version: 2.29.2
+ '@vitejs/plugin-react':
+ specifier: ^4.4.1
+ version: 4.4.1(vite@6.3.2(@types/node@22.14.1)(jiti@1.21.6)(sass@1.87.0)(yaml@2.7.1))
+ '@zitadel/eslint-config':
+ specifier: workspace:*
+ version: link:packages/zitadel-eslint-config
+ '@zitadel/prettier-config':
+ specifier: workspace:*
+ version: link:packages/zitadel-prettier-config
+ axios:
+ specifier: ^1.8.4
+ version: 1.8.4(debug@4.4.0)
+ dotenv:
+ specifier: ^16.5.0
+ version: 16.5.0
+ dotenv-cli:
+ specifier: ^8.0.0
+ version: 8.0.0
+ eslint:
+ specifier: 8.57.1
+ version: 8.57.1
+ prettier:
+ specifier: ^3.5.3
+ version: 3.5.3
+ prettier-plugin-organize-imports:
+ specifier: ^4.1.0
+ version: 4.1.0(prettier@3.5.3)(typescript@5.8.3)
+ tsup:
+ specifier: ^8.4.0
+ version: 8.4.0(jiti@1.21.6)(postcss@8.5.3)(typescript@5.8.3)(yaml@2.7.1)
+ turbo:
+ specifier: 2.5.0
+ version: 2.5.0
+ typescript:
+ specifier: ^5.8.3
+ version: 5.8.3
+ vite-tsconfig-paths:
+ specifier: ^5.1.4
+ version: 5.1.4(typescript@5.8.3)(vite@6.3.2(@types/node@22.14.1)(jiti@1.21.6)(sass@1.87.0)(yaml@2.7.1))
+ vitest:
+ specifier: ^3.1.2
+ version: 3.1.2(@types/node@22.14.1)(jiti@1.21.6)(jsdom@26.1.0)(sass@1.87.0)(yaml@2.7.1)
+
+ apps/login:
+ dependencies:
+ '@headlessui/react':
+ specifier: ^2.1.9
+ version: 2.1.9(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+ '@heroicons/react':
+ specifier: 2.1.3
+ version: 2.1.3(react@19.1.0)
+ '@tailwindcss/forms':
+ specifier: 0.5.7
+ version: 0.5.7(tailwindcss@3.4.14)
+ '@vercel/analytics':
+ specifier: ^1.2.2
+ version: 1.3.1(next@15.4.0-canary.86(@playwright/test@1.52.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(sass@1.87.0))(react@19.1.0)
+ '@zitadel/client':
+ specifier: workspace:*
+ version: link:../../packages/zitadel-client
+ '@zitadel/proto':
+ specifier: workspace:*
+ version: link:../../packages/zitadel-proto
+ clsx:
+ specifier: 1.2.1
+ version: 1.2.1
+ copy-to-clipboard:
+ specifier: ^3.3.3
+ version: 3.3.3
+ deepmerge:
+ specifier: ^4.3.1
+ version: 4.3.1
+ lucide-react:
+ specifier: 0.469.0
+ version: 0.469.0(react@19.1.0)
+ moment:
+ specifier: ^2.29.4
+ version: 2.30.1
+ next:
+ specifier: 15.4.0-canary.86
+ version: 15.4.0-canary.86(@playwright/test@1.52.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(sass@1.87.0)
+ next-intl:
+ specifier: ^3.25.1
+ version: 3.26.5(next@15.4.0-canary.86(@playwright/test@1.52.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(sass@1.87.0))(react@19.1.0)
+ next-themes:
+ specifier: ^0.2.1
+ version: 0.2.1(next@15.4.0-canary.86(@playwright/test@1.52.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(sass@1.87.0))(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+ nice-grpc:
+ specifier: 2.0.1
+ version: 2.0.1
+ qrcode.react:
+ specifier: ^3.1.0
+ version: 3.1.0(react@19.1.0)
+ react:
+ specifier: 19.1.0
+ version: 19.1.0
+ react-dom:
+ specifier: 19.1.0
+ version: 19.1.0(react@19.1.0)
+ react-hook-form:
+ specifier: 7.39.5
+ version: 7.39.5(react@19.1.0)
+ tinycolor2:
+ specifier: 1.4.2
+ version: 1.4.2
+ uuid:
+ specifier: ^11.1.0
+ version: 11.1.0
+ devDependencies:
+ '@bufbuild/buf':
+ specifier: ^1.53.0
+ version: 1.53.0
+ '@testing-library/jest-dom':
+ specifier: ^6.6.3
+ version: 6.6.3
+ '@testing-library/react':
+ specifier: ^16.3.0
+ version: 16.3.0(@testing-library/dom@10.4.0)(@types/react-dom@19.1.2(@types/react@19.1.2))(@types/react@19.1.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+ '@types/ms':
+ specifier: 2.1.0
+ version: 2.1.0
+ '@types/node':
+ specifier: ^22.14.1
+ version: 22.14.1
+ '@types/react':
+ specifier: 19.1.2
+ version: 19.1.2
+ '@types/react-dom':
+ specifier: 19.1.2
+ version: 19.1.2(@types/react@19.1.2)
+ '@types/tinycolor2':
+ specifier: 1.4.3
+ version: 1.4.3
+ '@types/uuid':
+ specifier: ^10.0.0
+ version: 10.0.0
+ '@vercel/git-hooks':
+ specifier: 1.0.0
+ version: 1.0.0
+ '@zitadel/eslint-config':
+ specifier: workspace:*
+ version: link:../../packages/zitadel-eslint-config
+ '@zitadel/prettier-config':
+ specifier: workspace:*
+ version: link:../../packages/zitadel-prettier-config
+ '@zitadel/tailwind-config':
+ specifier: workspace:*
+ version: link:../../packages/zitadel-tailwind-config
+ '@zitadel/tsconfig':
+ specifier: workspace:*
+ version: link:../../packages/zitadel-tsconfig
+ autoprefixer:
+ specifier: 10.4.21
+ version: 10.4.21(postcss@8.5.3)
+ grpc-tools:
+ specifier: 1.13.0
+ version: 1.13.0
+ jsdom:
+ specifier: ^26.1.0
+ version: 26.1.0
+ lint-staged:
+ specifier: 15.5.1
+ version: 15.5.1
+ make-dir-cli:
+ specifier: 4.0.0
+ version: 4.0.0
+ postcss:
+ specifier: 8.5.3
+ version: 8.5.3
+ prettier-plugin-tailwindcss:
+ specifier: 0.6.11
+ version: 0.6.11(prettier-plugin-organize-imports@4.1.0(prettier@3.5.3)(typescript@5.8.3))(prettier@3.5.3)
+ sass:
+ specifier: ^1.87.0
+ version: 1.87.0
+ tailwindcss:
+ specifier: 3.4.14
+ version: 3.4.14
+ ts-proto:
+ specifier: ^2.7.0
+ version: 2.7.0
+ typescript:
+ specifier: ^5.8.3
+ version: 5.8.3
+
+ apps/login-test-acceptance:
+ devDependencies:
+ '@faker-js/faker':
+ specifier: ^9.7.0
+ version: 9.7.0
+ '@otplib/core':
+ specifier: ^12.0.0
+ version: 12.0.1
+ '@otplib/plugin-crypto':
+ specifier: ^12.0.0
+ version: 12.0.1
+ '@otplib/plugin-thirty-two':
+ specifier: ^12.0.0
+ version: 12.0.1
+ '@playwright/test':
+ specifier: ^1.52.0
+ version: 1.52.0
+ gaxios:
+ specifier: ^7.1.0
+ version: 7.1.0
+ typescript:
+ specifier: ^5.8.3
+ version: 5.8.3
+
+ apps/login-test-integration:
+ devDependencies:
+ '@types/node':
+ specifier: ^22.14.1
+ version: 22.14.1
+ concurrently:
+ specifier: ^9.1.2
+ version: 9.1.2
+ cypress:
+ specifier: ^14.3.2
+ version: 14.3.2
+ env-cmd:
+ specifier: ^10.0.0
+ version: 10.1.0
+ nodemon:
+ specifier: ^3.1.9
+ version: 3.1.9
+ start-server-and-test:
+ specifier: ^2.0.11
+ version: 2.0.11
+ typescript:
+ specifier: ^5.8.3
+ version: 5.8.3
+
+ packages/zitadel-client:
+ dependencies:
+ '@bufbuild/protobuf':
+ specifier: ^2.2.2
+ version: 2.2.2
+ '@connectrpc/connect':
+ specifier: ^2.0.0
+ version: 2.0.0(@bufbuild/protobuf@2.2.2)
+ '@connectrpc/connect-node':
+ specifier: ^2.0.0
+ version: 2.0.0(@bufbuild/protobuf@2.2.2)(@connectrpc/connect@2.0.0(@bufbuild/protobuf@2.2.2))
+ '@connectrpc/connect-web':
+ specifier: ^2.0.0
+ version: 2.0.0(@bufbuild/protobuf@2.2.2)(@connectrpc/connect@2.0.0(@bufbuild/protobuf@2.2.2))
+ '@zitadel/proto':
+ specifier: workspace:*
+ version: link:../zitadel-proto
+ jose:
+ specifier: ^5.3.0
+ version: 5.8.0
+ devDependencies:
+ '@bufbuild/buf':
+ specifier: ^1.53.0
+ version: 1.53.0
+ '@bufbuild/protocompile':
+ specifier: ^0.0.1
+ version: 0.0.1(@bufbuild/buf@1.53.0)
+ '@zitadel/eslint-config':
+ specifier: workspace:*
+ version: link:../zitadel-eslint-config
+ '@zitadel/tsconfig':
+ specifier: workspace:*
+ version: link:../zitadel-tsconfig
+
+ packages/zitadel-eslint-config:
+ dependencies:
+ '@babel/eslint-parser':
+ specifier: ^7.25.9
+ version: 7.25.9(@babel/core@7.26.10)(eslint@8.57.1)
+ '@typescript-eslint/parser':
+ specifier: ^7.9.0
+ version: 7.18.0(eslint@8.57.1)(typescript@5.8.3)
+ eslint-config-next:
+ specifier: ^14.2.18
+ version: 14.2.18(eslint@8.57.1)(typescript@5.8.3)
+ eslint-config-prettier:
+ specifier: ^9.1.0
+ version: 9.1.0(eslint@8.57.1)
+ eslint-config-turbo:
+ specifier: ^2.0.9
+ version: 2.1.0(eslint@8.57.1)
+ eslint-plugin-react:
+ specifier: ^7.34.1
+ version: 7.35.0(eslint@8.57.1)
+
+ packages/zitadel-prettier-config: {}
+
+ packages/zitadel-proto:
+ dependencies:
+ '@bufbuild/protobuf':
+ specifier: ^2.2.2
+ version: 2.2.2
+ devDependencies:
+ '@bufbuild/buf':
+ specifier: ^1.53.0
+ version: 1.53.0
+
+ packages/zitadel-tailwind-config:
+ devDependencies:
+ '@tailwindcss/forms':
+ specifier: 0.5.3
+ version: 0.5.3(tailwindcss@4.1.4)
+ tailwindcss:
+ specifier: ^4.1.4
+ version: 4.1.4
+
+ packages/zitadel-tsconfig: {}
+
+packages:
+
+ '@adobe/css-tools@4.4.0':
+ resolution: {integrity: sha512-Ff9+ksdQQB3rMncgqDK78uLznstjyfIf2Arnh22pW8kBpLs6rpKDwgnZT46hin5Hl1WzazzK64DOrhSwYpS7bQ==}
+
+ '@alloc/quick-lru@5.2.0':
+ resolution: {integrity: sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==}
+ engines: {node: '>=10'}
+
+ '@ampproject/remapping@2.3.0':
+ resolution: {integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==}
+ engines: {node: '>=6.0.0'}
+
+ '@asamuzakjp/css-color@3.1.4':
+ resolution: {integrity: sha512-SeuBV4rnjpFNjI8HSgKUwteuFdkHwkboq31HWzznuqgySQir+jSTczoWVVL4jvOjKjuH80fMDG0Fvg1Sb+OJsA==}
+
+ '@babel/code-frame@7.26.2':
+ resolution: {integrity: sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/compat-data@7.26.8':
+ resolution: {integrity: sha512-oH5UPLMWR3L2wEFLnFJ1TZXqHufiTKAiLfqw5zkhS4dKXLJ10yVztfil/twG8EDTA4F/tvVNw9nOl4ZMslB8rQ==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/core@7.26.10':
+ resolution: {integrity: sha512-vMqyb7XCDMPvJFFOaT9kxtiRh42GwlZEg1/uIgtZshS5a/8OaduUfCi7kynKgc3Tw/6Uo2D+db9qBttghhmxwQ==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/eslint-parser@7.25.9':
+ resolution: {integrity: sha512-5UXfgpK0j0Xr/xIdgdLEhOFxaDZ0bRPWJJchRpqOSur/3rZoPbqqki5mm0p4NE2cs28krBEiSM2MB7//afRSQQ==}
+ engines: {node: ^10.13.0 || ^12.13.0 || >=14.0.0}
+ peerDependencies:
+ '@babel/core': ^7.11.0
+ eslint: ^7.5.0 || ^8.0.0 || ^9.0.0
+
+ '@babel/generator@7.27.0':
+ resolution: {integrity: sha512-VybsKvpiN1gU1sdMZIp7FcqphVVKEwcuj02x73uvcHE0PTihx1nlBcowYWhDwjpoAXRv43+gDzyggGnn1XZhVw==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/helper-compilation-targets@7.27.0':
+ resolution: {integrity: sha512-LVk7fbXml0H2xH34dFzKQ7TDZ2G4/rVTOrq9V+icbbadjbVxxeFeDsNHv2SrZeWoA+6ZiTyWYWtScEIW07EAcA==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/helper-module-imports@7.25.9':
+ resolution: {integrity: sha512-tnUA4RsrmflIM6W6RFTLFSXITtl0wKjgpnLgXyowocVPrbYrLUXSBXDgTs8BlbmIzIdlBySRQjINYs2BAkiLtw==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/helper-module-transforms@7.26.0':
+ resolution: {integrity: sha512-xO+xu6B5K2czEnQye6BHA7DolFFmS3LB7stHZFaOLb1pAwO1HWLS8fXA+eh0A2yIvltPVmx3eNNDBJA2SLHXFw==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0
+
+ '@babel/helper-plugin-utils@7.26.5':
+ resolution: {integrity: sha512-RS+jZcRdZdRFzMyr+wcsaqOmld1/EqTghfaBGQQd/WnRdzdlvSZ//kF7U8VQTxf1ynZ4cjUcYgjVGx13ewNPMg==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/helper-string-parser@7.25.9':
+ resolution: {integrity: sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/helper-validator-identifier@7.25.9':
+ resolution: {integrity: sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/helper-validator-option@7.25.9':
+ resolution: {integrity: sha512-e/zv1co8pp55dNdEcCynfj9X7nyUKUXoUEwfXqaZt0omVOmDe9oOTdKStH4GmAw6zxMFs50ZayuMfHDKlO7Tfw==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/helpers@7.27.0':
+ resolution: {integrity: sha512-U5eyP/CTFPuNE3qk+WZMxFkp/4zUzdceQlfzf7DdGdhp+Fezd7HD+i8Y24ZuTMKX3wQBld449jijbGq6OdGNQg==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/parser@7.27.0':
+ resolution: {integrity: sha512-iaepho73/2Pz7w2eMS0Q5f83+0RKI7i4xmiYeBmDzfRVbQtTOG7Ts0S4HzJVsTMGI9keU8rNfuZr8DKfSt7Yyg==}
+ engines: {node: '>=6.0.0'}
+ hasBin: true
+
+ '@babel/plugin-transform-react-jsx-self@7.25.9':
+ resolution: {integrity: sha512-y8quW6p0WHkEhmErnfe58r7x0A70uKphQm8Sp8cV7tjNQwK56sNVK0M73LK3WuYmsuyrftut4xAkjjgU0twaMg==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/plugin-transform-react-jsx-source@7.25.9':
+ resolution: {integrity: sha512-+iqjT8xmXhhYv4/uiYd8FNQsraMFZIfxVSqxxVSZP0WbbSAWvBXAul0m/zu+7Vv4O/3WtApy9pmaTMiumEZgfg==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/runtime@7.27.0':
+ resolution: {integrity: sha512-VtPOkrdPHZsKc/clNqyi9WUA8TINkZ4cGk63UUE3u4pmB2k+ZMQRDuIOagv8UVd6j7k0T3+RRIb7beKTebNbcw==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/template@7.27.0':
+ resolution: {integrity: sha512-2ncevenBqXI6qRMukPlXwHKHchC7RyMuu4xv5JBXRfOGVcTy1mXCD12qrp7Jsoxll1EV3+9sE4GugBVRjT2jFA==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/traverse@7.27.0':
+ resolution: {integrity: sha512-19lYZFzYVQkkHkl4Cy4WrAVcqBkgvV2YM2TU3xG6DIwO7O3ecbDPfW3yM3bjAGcqcQHi+CCtjMR3dIEHxsd6bA==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/types@7.27.0':
+ resolution: {integrity: sha512-H45s8fVLYjbhFH62dIJ3WtmJ6RSPt/3DRO0ZcT2SUiYiQyz3BLVb9ADEnLl91m74aQPS3AzzeajZHYOalWe3bg==}
+ engines: {node: '>=6.9.0'}
+
+ '@bufbuild/buf-darwin-arm64@1.53.0':
+ resolution: {integrity: sha512-UVhqDYu54ciiCMeG6RODlrX5XRvLN6PfsVDqMQG0JwmMKtUi326CbUqsqO7xsQbcEUso3FTBaURir4RixoM88w==}
+ engines: {node: '>=12'}
+ cpu: [arm64]
+ os: [darwin]
+
+ '@bufbuild/buf-darwin-x64@1.53.0':
+ resolution: {integrity: sha512-03lKaenjf08HF6DlARPU2lEL2dRxNsU6rb9GbUu+YeLayWy7SUlfeDB8drAZ/GpfSc7SL8TKF7jqRkqxT4wFGA==}
+ engines: {node: '>=12'}
+ cpu: [x64]
+ os: [darwin]
+
+ '@bufbuild/buf-linux-aarch64@1.53.0':
+ resolution: {integrity: sha512-FlxrB+rZJG5u7v2JovzXvSR/OdXjVXYHTTLnk6vN/73KPbpGPzZrW7mKxlYyn/Uar5tKDAYvmijjuItXZ6i31g==}
+ engines: {node: '>=12'}
+ cpu: [arm64]
+ os: [linux]
+
+ '@bufbuild/buf-linux-armv7@1.53.0':
+ resolution: {integrity: sha512-e9ER+5Os1DPLhr2X1BRPrQpDZWpv5Mkk2PLnmmzh5RL4kOueJKQZj/m1qQr7SQkiPPhS0yMw7EEghsr521FFzQ==}
+ engines: {node: '>=12'}
+ cpu: [arm]
+ os: [linux]
+
+ '@bufbuild/buf-linux-x64@1.53.0':
+ resolution: {integrity: sha512-LehyZPbkRgCvIM56uUnCAUD1QSno2wkBZ5HOvjrjOd0GEjfKgw/fsEu13fJR13bGBNOeOUHbHrd59iUSyY6rGA==}
+ engines: {node: '>=12'}
+ cpu: [x64]
+ os: [linux]
+
+ '@bufbuild/buf-win32-arm64@1.53.0':
+ resolution: {integrity: sha512-QRNMHYW6v4keoelIwMNZGQw2R67fsS8lEDnYxrFmiRADwZ/ri/XKJjvQfpoE2Bq0xREB0zZ++RX+1DZOkTA/Iw==}
+ engines: {node: '>=12'}
+ cpu: [arm64]
+ os: [win32]
+
+ '@bufbuild/buf-win32-x64@1.53.0':
+ resolution: {integrity: sha512-relZlT9gYrZGcEH4dcJhEWrjaHV9drG1PcgW6krqw1AzpQOPxR/loXJ7DycoCAnUhQ9TdsdTfUlVHqiJt98piQ==}
+ engines: {node: '>=12'}
+ cpu: [x64]
+ os: [win32]
+
+ '@bufbuild/buf@1.53.0':
+ resolution: {integrity: sha512-GGAztQbbKSv+HaihdDIUpejUcxIx2Fse9SqHfMisJbL/hZ7aOH7BFeSH0q8/g2kSAsLABlenVKeEWKX1uZU3LQ==}
+ engines: {node: '>=12'}
+ hasBin: true
+
+ '@bufbuild/protobuf@2.2.2':
+ resolution: {integrity: sha512-UNtPCbrwrenpmrXuRwn9jYpPoweNXj8X5sMvYgsqYyaH8jQ6LfUJSk3dJLnBK+6sfYPrF4iAIo5sd5HQ+tg75A==}
+
+ '@bufbuild/protobuf@2.2.5':
+ resolution: {integrity: sha512-/g5EzJifw5GF8aren8wZ/G5oMuPoGeS6MQD3ca8ddcvdXR5UELUfdTZITCGNhNXynY/AYl3Z4plmxdj/tRl/hQ==}
+
+ '@bufbuild/protocompile@0.0.1':
+ resolution: {integrity: sha512-cOTMtjcWLcbjF17dPYgeMtVC5jZyS0bSjz3jy8kDPjOgjgSYMD2u2It7w8aCc2z23hTPIKl/2SNdMnz0Jzu3Xg==}
+ peerDependencies:
+ '@bufbuild/buf': ^1.22.0
+
+ '@changesets/apply-release-plan@7.0.12':
+ resolution: {integrity: sha512-EaET7As5CeuhTzvXTQCRZeBUcisoYPDDcXvgTE/2jmmypKp0RC7LxKj/yzqeh/1qFTZI7oDGFcL1PHRuQuketQ==}
+
+ '@changesets/assemble-release-plan@6.0.6':
+ resolution: {integrity: sha512-Frkj8hWJ1FRZiY3kzVCKzS0N5mMwWKwmv9vpam7vt8rZjLL1JMthdh6pSDVSPumHPshTTkKZ0VtNbE0cJHZZUg==}
+
+ '@changesets/changelog-git@0.2.1':
+ resolution: {integrity: sha512-x/xEleCFLH28c3bQeQIyeZf8lFXyDFVn1SgcBiR2Tw/r4IAWlk1fzxCEZ6NxQAjF2Nwtczoen3OA2qR+UawQ8Q==}
+
+ '@changesets/cli@2.29.2':
+ resolution: {integrity: sha512-vwDemKjGYMOc0l6WUUTGqyAWH3AmueeyoJa1KmFRtCYiCoY5K3B68ErYpDB6H48T4lLI4czum4IEjh6ildxUeg==}
+ hasBin: true
+
+ '@changesets/config@3.1.1':
+ resolution: {integrity: sha512-bd+3Ap2TKXxljCggI0mKPfzCQKeV/TU4yO2h2C6vAihIo8tzseAn2e7klSuiyYYXvgu53zMN1OeYMIQkaQoWnA==}
+
+ '@changesets/errors@0.2.0':
+ resolution: {integrity: sha512-6BLOQUscTpZeGljvyQXlWOItQyU71kCdGz7Pi8H8zdw6BI0g3m43iL4xKUVPWtG+qrrL9DTjpdn8eYuCQSRpow==}
+
+ '@changesets/get-dependents-graph@2.1.3':
+ resolution: {integrity: sha512-gphr+v0mv2I3Oxt19VdWRRUxq3sseyUpX9DaHpTUmLj92Y10AGy+XOtV+kbM6L/fDcpx7/ISDFK6T8A/P3lOdQ==}
+
+ '@changesets/get-release-plan@4.0.10':
+ resolution: {integrity: sha512-CCJ/f3edYaA3MqoEnWvGGuZm0uMEMzNJ97z9hdUR34AOvajSwySwsIzC/bBu3+kuGDsB+cny4FljG8UBWAa7jg==}
+
+ '@changesets/get-version-range-type@0.4.0':
+ resolution: {integrity: sha512-hwawtob9DryoGTpixy1D3ZXbGgJu1Rhr+ySH2PvTLHvkZuQ7sRT4oQwMh0hbqZH1weAooedEjRsbrWcGLCeyVQ==}
+
+ '@changesets/git@3.0.4':
+ resolution: {integrity: sha512-BXANzRFkX+XcC1q/d27NKvlJ1yf7PSAgi8JG6dt8EfbHFHi4neau7mufcSca5zRhwOL8j9s6EqsxmT+s+/E6Sw==}
+
+ '@changesets/logger@0.1.1':
+ resolution: {integrity: sha512-OQtR36ZlnuTxKqoW4Sv6x5YIhOmClRd5pWsjZsddYxpWs517R0HkyiefQPIytCVh4ZcC5x9XaG8KTdd5iRQUfg==}
+
+ '@changesets/parse@0.4.1':
+ resolution: {integrity: sha512-iwksMs5Bf/wUItfcg+OXrEpravm5rEd9Bf4oyIPL4kVTmJQ7PNDSd6MDYkpSJR1pn7tz/k8Zf2DhTCqX08Ou+Q==}
+
+ '@changesets/pre@2.0.2':
+ resolution: {integrity: sha512-HaL/gEyFVvkf9KFg6484wR9s0qjAXlZ8qWPDkTyKF6+zqjBe/I2mygg3MbpZ++hdi0ToqNUF8cjj7fBy0dg8Ug==}
+
+ '@changesets/read@0.6.5':
+ resolution: {integrity: sha512-UPzNGhsSjHD3Veb0xO/MwvasGe8eMyNrR/sT9gR8Q3DhOQZirgKhhXv/8hVsI0QpPjR004Z9iFxoJU6in3uGMg==}
+
+ '@changesets/should-skip-package@0.1.2':
+ resolution: {integrity: sha512-qAK/WrqWLNCP22UDdBTMPH5f41elVDlsNyat180A33dWxuUDyNpg6fPi/FyTZwRriVjg0L8gnjJn2F9XAoF0qw==}
+
+ '@changesets/types@4.1.0':
+ resolution: {integrity: sha512-LDQvVDv5Kb50ny2s25Fhm3d9QSZimsoUGBsUioj6MC3qbMUCuC8GPIvk/M6IvXx3lYhAs0lwWUQLb+VIEUCECw==}
+
+ '@changesets/types@6.1.0':
+ resolution: {integrity: sha512-rKQcJ+o1nKNgeoYRHKOS07tAMNd3YSN0uHaJOZYjBAgxfV7TUE7JE+z4BzZdQwb5hKaYbayKN5KrYV7ODb2rAA==}
+
+ '@changesets/write@0.4.0':
+ resolution: {integrity: sha512-CdTLvIOPiCNuH71pyDu3rA+Q0n65cmAbXnwWH84rKGiFumFzkmHNT8KHTMEchcxN+Kl8I54xGUhJ7l3E7X396Q==}
+
+ '@colors/colors@1.5.0':
+ resolution: {integrity: sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==}
+ engines: {node: '>=0.1.90'}
+
+ '@connectrpc/connect-node@2.0.0':
+ resolution: {integrity: sha512-DoI5T+SUvlS/8QBsxt2iDoUg15dSxqhckegrgZpWOtADtmGohBIVbx1UjtWmjLBrP4RdD0FeBw+XyRUSbpKnJQ==}
+ engines: {node: '>=18.14.1'}
+ peerDependencies:
+ '@bufbuild/protobuf': ^2.2.0
+ '@connectrpc/connect': 2.0.0
+
+ '@connectrpc/connect-web@2.0.0':
+ resolution: {integrity: sha512-oeCxqHXLXlWJdmcvp9L3scgAuK+FjNSn+twyhUxc8yvDbTumnt5Io+LnBzSYxAdUdYqTw5yHfTSCJ4hj0QID0g==}
+ peerDependencies:
+ '@bufbuild/protobuf': ^2.2.0
+ '@connectrpc/connect': 2.0.0
+
+ '@connectrpc/connect@2.0.0':
+ resolution: {integrity: sha512-Usm8jgaaULANJU8vVnhWssSA6nrZ4DJEAbkNtXSoZay2YD5fDyMukCxu8NEhCvFzfHvrhxhcjttvgpyhOM7xAQ==}
+ peerDependencies:
+ '@bufbuild/protobuf': ^2.2.0
+
+ '@csstools/color-helpers@5.0.2':
+ resolution: {integrity: sha512-JqWH1vsgdGcw2RR6VliXXdA0/59LttzlU8UlRT/iUUsEeWfYq8I+K0yhihEUTTHLRm1EXvpsCx3083EU15ecsA==}
+ engines: {node: '>=18'}
+
+ '@csstools/css-calc@2.1.3':
+ resolution: {integrity: sha512-XBG3talrhid44BY1x3MHzUx/aTG8+x/Zi57M4aTKK9RFB4aLlF3TTSzfzn8nWVHWL3FgAXAxmupmDd6VWww+pw==}
+ engines: {node: '>=18'}
+ peerDependencies:
+ '@csstools/css-parser-algorithms': ^3.0.4
+ '@csstools/css-tokenizer': ^3.0.3
+
+ '@csstools/css-color-parser@3.0.9':
+ resolution: {integrity: sha512-wILs5Zk7BU86UArYBJTPy/FMPPKVKHMj1ycCEyf3VUptol0JNRLFU/BZsJ4aiIHJEbSLiizzRrw8Pc1uAEDrXw==}
+ engines: {node: '>=18'}
+ peerDependencies:
+ '@csstools/css-parser-algorithms': ^3.0.4
+ '@csstools/css-tokenizer': ^3.0.3
+
+ '@csstools/css-parser-algorithms@3.0.4':
+ resolution: {integrity: sha512-Up7rBoV77rv29d3uKHUIVubz1BTcgyUK72IvCQAbfbMv584xHcGKCKbWh7i8hPrRJ7qU4Y8IO3IY9m+iTB7P3A==}
+ engines: {node: '>=18'}
+ peerDependencies:
+ '@csstools/css-tokenizer': ^3.0.3
+
+ '@csstools/css-tokenizer@3.0.3':
+ resolution: {integrity: sha512-UJnjoFsmxfKUdNYdWgOB0mWUypuLvAfQPH1+pyvRJs6euowbFkFC6P13w1l8mJyi3vxYMxc9kld5jZEGRQs6bw==}
+ engines: {node: '>=18'}
+
+ '@cypress/request@3.0.8':
+ resolution: {integrity: sha512-h0NFgh1mJmm1nr4jCwkGHwKneVYKghUyWe6TMNrk0B9zsjAJxpg8C4/+BAcmLgCPa1vj1V8rNUaILl+zYRUWBQ==}
+ engines: {node: '>= 6'}
+
+ '@cypress/xvfb@1.2.4':
+ resolution: {integrity: sha512-skbBzPggOVYCbnGgV+0dmBdW/s77ZkAOXIC1knS8NagwDjBrNC1LuXtQJeiN6l+m7lzmHtaoUw/ctJKdqkG57Q==}
+
+ '@emnapi/runtime@1.4.3':
+ resolution: {integrity: sha512-pBPWdu6MLKROBX05wSNKcNb++m5Er+KQ9QkB+WVM+pW2Kx9hoSrVTnu3BdkI5eBLZoKu/J6mW/B6i6bJB2ytXQ==}
+
+ '@esbuild/aix-ppc64@0.25.2':
+ resolution: {integrity: sha512-wCIboOL2yXZym2cgm6mlA742s9QeJ8DjGVaL39dLN4rRwrOgOyYSnOaFPhKZGLb2ngj4EyfAFjsNJwPXZvseag==}
+ engines: {node: '>=18'}
+ cpu: [ppc64]
+ os: [aix]
+
+ '@esbuild/android-arm64@0.25.2':
+ resolution: {integrity: sha512-5ZAX5xOmTligeBaeNEPnPaeEuah53Id2tX4c2CVP3JaROTH+j4fnfHCkr1PjXMd78hMst+TlkfKcW/DlTq0i4w==}
+ engines: {node: '>=18'}
+ cpu: [arm64]
+ os: [android]
+
+ '@esbuild/android-arm@0.25.2':
+ resolution: {integrity: sha512-NQhH7jFstVY5x8CKbcfa166GoV0EFkaPkCKBQkdPJFvo5u+nGXLEH/ooniLb3QI8Fk58YAx7nsPLozUWfCBOJA==}
+ engines: {node: '>=18'}
+ cpu: [arm]
+ os: [android]
+
+ '@esbuild/android-x64@0.25.2':
+ resolution: {integrity: sha512-Ffcx+nnma8Sge4jzddPHCZVRvIfQ0kMsUsCMcJRHkGJ1cDmhe4SsrYIjLUKn1xpHZybmOqCWwB0zQvsjdEHtkg==}
+ engines: {node: '>=18'}
+ cpu: [x64]
+ os: [android]
+
+ '@esbuild/darwin-arm64@0.25.2':
+ resolution: {integrity: sha512-MpM6LUVTXAzOvN4KbjzU/q5smzryuoNjlriAIx+06RpecwCkL9JpenNzpKd2YMzLJFOdPqBpuub6eVRP5IgiSA==}
+ engines: {node: '>=18'}
+ cpu: [arm64]
+ os: [darwin]
+
+ '@esbuild/darwin-x64@0.25.2':
+ resolution: {integrity: sha512-5eRPrTX7wFyuWe8FqEFPG2cU0+butQQVNcT4sVipqjLYQjjh8a8+vUTfgBKM88ObB85ahsnTwF7PSIt6PG+QkA==}
+ engines: {node: '>=18'}
+ cpu: [x64]
+ os: [darwin]
+
+ '@esbuild/freebsd-arm64@0.25.2':
+ resolution: {integrity: sha512-mLwm4vXKiQ2UTSX4+ImyiPdiHjiZhIaE9QvC7sw0tZ6HoNMjYAqQpGyui5VRIi5sGd+uWq940gdCbY3VLvsO1w==}
+ engines: {node: '>=18'}
+ cpu: [arm64]
+ os: [freebsd]
+
+ '@esbuild/freebsd-x64@0.25.2':
+ resolution: {integrity: sha512-6qyyn6TjayJSwGpm8J9QYYGQcRgc90nmfdUb0O7pp1s4lTY+9D0H9O02v5JqGApUyiHOtkz6+1hZNvNtEhbwRQ==}
+ engines: {node: '>=18'}
+ cpu: [x64]
+ os: [freebsd]
+
+ '@esbuild/linux-arm64@0.25.2':
+ resolution: {integrity: sha512-gq/sjLsOyMT19I8obBISvhoYiZIAaGF8JpeXu1u8yPv8BE5HlWYobmlsfijFIZ9hIVGYkbdFhEqC0NvM4kNO0g==}
+ engines: {node: '>=18'}
+ cpu: [arm64]
+ os: [linux]
+
+ '@esbuild/linux-arm@0.25.2':
+ resolution: {integrity: sha512-UHBRgJcmjJv5oeQF8EpTRZs/1knq6loLxTsjc3nxO9eXAPDLcWW55flrMVc97qFPbmZP31ta1AZVUKQzKTzb0g==}
+ engines: {node: '>=18'}
+ cpu: [arm]
+ os: [linux]
+
+ '@esbuild/linux-ia32@0.25.2':
+ resolution: {integrity: sha512-bBYCv9obgW2cBP+2ZWfjYTU+f5cxRoGGQ5SeDbYdFCAZpYWrfjjfYwvUpP8MlKbP0nwZ5gyOU/0aUzZ5HWPuvQ==}
+ engines: {node: '>=18'}
+ cpu: [ia32]
+ os: [linux]
+
+ '@esbuild/linux-loong64@0.25.2':
+ resolution: {integrity: sha512-SHNGiKtvnU2dBlM5D8CXRFdd+6etgZ9dXfaPCeJtz+37PIUlixvlIhI23L5khKXs3DIzAn9V8v+qb1TRKrgT5w==}
+ engines: {node: '>=18'}
+ cpu: [loong64]
+ os: [linux]
+
+ '@esbuild/linux-mips64el@0.25.2':
+ resolution: {integrity: sha512-hDDRlzE6rPeoj+5fsADqdUZl1OzqDYow4TB4Y/3PlKBD0ph1e6uPHzIQcv2Z65u2K0kpeByIyAjCmjn1hJgG0Q==}
+ engines: {node: '>=18'}
+ cpu: [mips64el]
+ os: [linux]
+
+ '@esbuild/linux-ppc64@0.25.2':
+ resolution: {integrity: sha512-tsHu2RRSWzipmUi9UBDEzc0nLc4HtpZEI5Ba+Omms5456x5WaNuiG3u7xh5AO6sipnJ9r4cRWQB2tUjPyIkc6g==}
+ engines: {node: '>=18'}
+ cpu: [ppc64]
+ os: [linux]
+
+ '@esbuild/linux-riscv64@0.25.2':
+ resolution: {integrity: sha512-k4LtpgV7NJQOml/10uPU0s4SAXGnowi5qBSjaLWMojNCUICNu7TshqHLAEbkBdAszL5TabfvQ48kK84hyFzjnw==}
+ engines: {node: '>=18'}
+ cpu: [riscv64]
+ os: [linux]
+
+ '@esbuild/linux-s390x@0.25.2':
+ resolution: {integrity: sha512-GRa4IshOdvKY7M/rDpRR3gkiTNp34M0eLTaC1a08gNrh4u488aPhuZOCpkF6+2wl3zAN7L7XIpOFBhnaE3/Q8Q==}
+ engines: {node: '>=18'}
+ cpu: [s390x]
+ os: [linux]
+
+ '@esbuild/linux-x64@0.25.2':
+ resolution: {integrity: sha512-QInHERlqpTTZ4FRB0fROQWXcYRD64lAoiegezDunLpalZMjcUcld3YzZmVJ2H/Cp0wJRZ8Xtjtj0cEHhYc/uUg==}
+ engines: {node: '>=18'}
+ cpu: [x64]
+ os: [linux]
+
+ '@esbuild/netbsd-arm64@0.25.2':
+ resolution: {integrity: sha512-talAIBoY5M8vHc6EeI2WW9d/CkiO9MQJ0IOWX8hrLhxGbro/vBXJvaQXefW2cP0z0nQVTdQ/eNyGFV1GSKrxfw==}
+ engines: {node: '>=18'}
+ cpu: [arm64]
+ os: [netbsd]
+
+ '@esbuild/netbsd-x64@0.25.2':
+ resolution: {integrity: sha512-voZT9Z+tpOxrvfKFyfDYPc4DO4rk06qamv1a/fkuzHpiVBMOhpjK+vBmWM8J1eiB3OLSMFYNaOaBNLXGChf5tg==}
+ engines: {node: '>=18'}
+ cpu: [x64]
+ os: [netbsd]
+
+ '@esbuild/openbsd-arm64@0.25.2':
+ resolution: {integrity: sha512-dcXYOC6NXOqcykeDlwId9kB6OkPUxOEqU+rkrYVqJbK2hagWOMrsTGsMr8+rW02M+d5Op5NNlgMmjzecaRf7Tg==}
+ engines: {node: '>=18'}
+ cpu: [arm64]
+ os: [openbsd]
+
+ '@esbuild/openbsd-x64@0.25.2':
+ resolution: {integrity: sha512-t/TkWwahkH0Tsgoq1Ju7QfgGhArkGLkF1uYz8nQS/PPFlXbP5YgRpqQR3ARRiC2iXoLTWFxc6DJMSK10dVXluw==}
+ engines: {node: '>=18'}
+ cpu: [x64]
+ os: [openbsd]
+
+ '@esbuild/sunos-x64@0.25.2':
+ resolution: {integrity: sha512-cfZH1co2+imVdWCjd+D1gf9NjkchVhhdpgb1q5y6Hcv9TP6Zi9ZG/beI3ig8TvwT9lH9dlxLq5MQBBgwuj4xvA==}
+ engines: {node: '>=18'}
+ cpu: [x64]
+ os: [sunos]
+
+ '@esbuild/win32-arm64@0.25.2':
+ resolution: {integrity: sha512-7Loyjh+D/Nx/sOTzV8vfbB3GJuHdOQyrOryFdZvPHLf42Tk9ivBU5Aedi7iyX+x6rbn2Mh68T4qq1SDqJBQO5Q==}
+ engines: {node: '>=18'}
+ cpu: [arm64]
+ os: [win32]
+
+ '@esbuild/win32-ia32@0.25.2':
+ resolution: {integrity: sha512-WRJgsz9un0nqZJ4MfhabxaD9Ft8KioqU3JMinOTvobbX6MOSUigSBlogP8QB3uxpJDsFS6yN+3FDBdqE5lg9kg==}
+ engines: {node: '>=18'}
+ cpu: [ia32]
+ os: [win32]
+
+ '@esbuild/win32-x64@0.25.2':
+ resolution: {integrity: sha512-kM3HKb16VIXZyIeVrM1ygYmZBKybX8N4p754bw390wGO3Tf2j4L2/WYL+4suWujpgf6GBYs3jv7TyUivdd05JA==}
+ engines: {node: '>=18'}
+ cpu: [x64]
+ os: [win32]
+
+ '@eslint-community/eslint-utils@4.4.0':
+ resolution: {integrity: sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==}
+ engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
+ peerDependencies:
+ eslint: ^6.0.0 || ^7.0.0 || >=8.0.0
+
+ '@eslint-community/eslint-utils@4.4.1':
+ resolution: {integrity: sha512-s3O3waFUrMV8P/XaF/+ZTp1X9XBZW1a4B97ZnjQF2KYWaFD2A8KyFBsrsfSjEmjn3RGWAIuvlneuZm3CUK3jbA==}
+ engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
+ peerDependencies:
+ eslint: ^6.0.0 || ^7.0.0 || >=8.0.0
+
+ '@eslint-community/regexpp@4.11.1':
+ resolution: {integrity: sha512-m4DVN9ZqskZoLU5GlWZadwDnYo3vAEydiUayB9widCl9ffWx2IvPnp6n3on5rJmziJSw9Bv+Z3ChDVdMwXCY8Q==}
+ engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0}
+
+ '@eslint-community/regexpp@4.12.1':
+ resolution: {integrity: sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==}
+ engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0}
+
+ '@eslint/eslintrc@2.1.4':
+ resolution: {integrity: sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==}
+ engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
+
+ '@eslint/js@8.57.1':
+ resolution: {integrity: sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q==}
+ engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
+
+ '@faker-js/faker@9.7.0':
+ resolution: {integrity: sha512-aozo5vqjCmDoXLNUJarFZx2IN/GgGaogY4TMJ6so/WLZOWpSV7fvj2dmrV6sEAnUm1O7aCrhTibjpzeDFgNqbg==}
+ engines: {node: '>=18.0.0', npm: '>=9.0.0'}
+
+ '@floating-ui/core@1.6.8':
+ resolution: {integrity: sha512-7XJ9cPU+yI2QeLS+FCSlqNFZJq8arvswefkZrYI1yQBbftw6FyrZOxYSh+9S7z7TpeWlRt9zJ5IhM1WIL334jA==}
+
+ '@floating-ui/dom@1.6.11':
+ resolution: {integrity: sha512-qkMCxSR24v2vGkhYDo/UzxfJN3D4syqSjyuTFz6C7XcpU1pASPRieNI0Kj5VP3/503mOfYiGY891ugBX1GlABQ==}
+
+ '@floating-ui/react-dom@2.1.2':
+ resolution: {integrity: sha512-06okr5cgPzMNBy+Ycse2A6udMi4bqwW/zgBF/rwjcNqWkyr82Mcg8b0vjX8OJpZFy/FKjJmw6wV7t44kK6kW7A==}
+ peerDependencies:
+ react: '>=16.8.0'
+ react-dom: '>=16.8.0'
+
+ '@floating-ui/react@0.26.24':
+ resolution: {integrity: sha512-2ly0pCkZIGEQUq5H8bBK0XJmc1xIK/RM3tvVzY3GBER7IOD1UgmC2Y2tjj4AuS+TC+vTE1KJv2053290jua0Sw==}
+ peerDependencies:
+ react: '>=16.8.0'
+ react-dom: '>=16.8.0'
+
+ '@floating-ui/utils@0.2.8':
+ resolution: {integrity: sha512-kym7SodPp8/wloecOpcmSnWJsK7M0E5Wg8UcFA+uO4B9s5d0ywXOEro/8HM9x0rW+TljRzul/14UYz3TleT3ig==}
+
+ '@formatjs/ecma402-abstract@2.2.4':
+ resolution: {integrity: sha512-lFyiQDVvSbQOpU+WFd//ILolGj4UgA/qXrKeZxdV14uKiAUiPAtX6XAn7WBCRi7Mx6I7EybM9E5yYn4BIpZWYg==}
+
+ '@formatjs/fast-memoize@2.2.3':
+ resolution: {integrity: sha512-3jeJ+HyOfu8osl3GNSL4vVHUuWFXR03Iz9jjgI7RwjG6ysu/Ymdr0JRCPHfF5yGbTE6JCrd63EpvX1/WybYRbA==}
+
+ '@formatjs/icu-messageformat-parser@2.9.4':
+ resolution: {integrity: sha512-Tbvp5a9IWuxUcpWNIW6GlMQYEc4rwNHR259uUFoKWNN1jM9obf9Ul0e+7r7MvFOBNcN+13K7NuKCKqQiAn1QEg==}
+
+ '@formatjs/icu-skeleton-parser@1.8.8':
+ resolution: {integrity: sha512-vHwK3piXwamFcx5YQdCdJxUQ1WdTl6ANclt5xba5zLGDv5Bsur7qz8AD7BevaKxITwpgDeU0u8My3AIibW9ywA==}
+
+ '@formatjs/intl-localematcher@0.5.8':
+ resolution: {integrity: sha512-I+WDNWWJFZie+jkfkiK5Mp4hEDyRSEvmyfYadflOno/mmKJKcB17fEpEH0oJu/OWhhCJ8kJBDz2YMd/6cDl7Mg==}
+
+ '@grpc/grpc-js@1.11.1':
+ resolution: {integrity: sha512-gyt/WayZrVPH2w/UTLansS7F9Nwld472JxxaETamrM8HNlsa+jSLNyKAZmhxI2Me4c3mQHFiS1wWHDY1g1Kthw==}
+ engines: {node: '>=12.10.0'}
+
+ '@grpc/proto-loader@0.7.13':
+ resolution: {integrity: sha512-AiXO/bfe9bmxBjxxtYxFAXGZvMaN5s8kO+jBHAJCON8rJoB5YS/D6X7ZNc6XQkuHNmyl4CYaMI1fJ/Gn27RGGw==}
+ engines: {node: '>=6'}
+ hasBin: true
+
+ '@hapi/hoek@9.3.0':
+ resolution: {integrity: sha512-/c6rf4UJlmHlC9b5BaNvzAcFv7HZ2QHaV0D4/HNlBdvFnvQq8RI4kYdhyPCl7Xj+oWvTWQ8ujhqS53LIgAe6KQ==}
+
+ '@hapi/topo@5.1.0':
+ resolution: {integrity: sha512-foQZKJig7Ob0BMAYBfcJk8d77QtOe7Wo4ox7ff1lQYoNNAb6jwcY1ncdoy2e9wQZzvNy7ODZCYJkK8kzmcAnAg==}
+
+ '@headlessui/react@2.1.9':
+ resolution: {integrity: sha512-ckWw7vlKtnoa1fL2X0fx1a3t/Li9MIKDVXn3SgG65YlxvDAsNrY39PPCxVM7sQRA7go2fJsuHSSauKFNaJHH7A==}
+ engines: {node: '>=10'}
+ peerDependencies:
+ react: ^18
+ react-dom: ^18
+
+ '@heroicons/react@2.1.3':
+ resolution: {integrity: sha512-fEcPfo4oN345SoqdlCDdSa4ivjaKbk0jTd+oubcgNxnNgAfzysfwWfQUr+51wigiWHQQRiZNd1Ao0M5Y3M2EGg==}
+ peerDependencies:
+ react: '>= 16'
+
+ '@humanwhocodes/config-array@0.13.0':
+ resolution: {integrity: sha512-DZLEEqFWQFiyK6h5YIeynKx7JlvCYWL0cImfSRXZ9l4Sg2efkFGTuFf6vzXjK1cq6IYkU+Eg/JizXw+TD2vRNw==}
+ engines: {node: '>=10.10.0'}
+ deprecated: Use @eslint/config-array instead
+
+ '@humanwhocodes/module-importer@1.0.1':
+ resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==}
+ engines: {node: '>=12.22'}
+
+ '@humanwhocodes/object-schema@2.0.3':
+ resolution: {integrity: sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==}
+ deprecated: Use @eslint/object-schema instead
+
+ '@img/sharp-darwin-arm64@0.34.1':
+ resolution: {integrity: sha512-pn44xgBtgpEbZsu+lWf2KNb6OAf70X68k+yk69Ic2Xz11zHR/w24/U49XT7AeRwJ0Px+mhALhU5LPci1Aymk7A==}
+ engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
+ cpu: [arm64]
+ os: [darwin]
+
+ '@img/sharp-darwin-x64@0.34.1':
+ resolution: {integrity: sha512-VfuYgG2r8BpYiOUN+BfYeFo69nP/MIwAtSJ7/Zpxc5QF3KS22z8Pvg3FkrSFJBPNQ7mmcUcYQFBmEQp7eu1F8Q==}
+ engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
+ cpu: [x64]
+ os: [darwin]
+
+ '@img/sharp-libvips-darwin-arm64@1.1.0':
+ resolution: {integrity: sha512-HZ/JUmPwrJSoM4DIQPv/BfNh9yrOA8tlBbqbLz4JZ5uew2+o22Ik+tHQJcih7QJuSa0zo5coHTfD5J8inqj9DA==}
+ cpu: [arm64]
+ os: [darwin]
+
+ '@img/sharp-libvips-darwin-x64@1.1.0':
+ resolution: {integrity: sha512-Xzc2ToEmHN+hfvsl9wja0RlnXEgpKNmftriQp6XzY/RaSfwD9th+MSh0WQKzUreLKKINb3afirxW7A0fz2YWuQ==}
+ cpu: [x64]
+ os: [darwin]
+
+ '@img/sharp-libvips-linux-arm64@1.1.0':
+ resolution: {integrity: sha512-IVfGJa7gjChDET1dK9SekxFFdflarnUB8PwW8aGwEoF3oAsSDuNUTYS+SKDOyOJxQyDC1aPFMuRYLoDInyV9Ew==}
+ cpu: [arm64]
+ os: [linux]
+
+ '@img/sharp-libvips-linux-arm@1.1.0':
+ resolution: {integrity: sha512-s8BAd0lwUIvYCJyRdFqvsj+BJIpDBSxs6ivrOPm/R7piTs5UIwY5OjXrP2bqXC9/moGsyRa37eYWYCOGVXxVrA==}
+ cpu: [arm]
+ os: [linux]
+
+ '@img/sharp-libvips-linux-ppc64@1.1.0':
+ resolution: {integrity: sha512-tiXxFZFbhnkWE2LA8oQj7KYR+bWBkiV2nilRldT7bqoEZ4HiDOcePr9wVDAZPi/Id5fT1oY9iGnDq20cwUz8lQ==}
+ cpu: [ppc64]
+ os: [linux]
+
+ '@img/sharp-libvips-linux-s390x@1.1.0':
+ resolution: {integrity: sha512-xukSwvhguw7COyzvmjydRb3x/09+21HykyapcZchiCUkTThEQEOMtBj9UhkaBRLuBrgLFzQ2wbxdeCCJW/jgJA==}
+ cpu: [s390x]
+ os: [linux]
+
+ '@img/sharp-libvips-linux-x64@1.1.0':
+ resolution: {integrity: sha512-yRj2+reB8iMg9W5sULM3S74jVS7zqSzHG3Ol/twnAAkAhnGQnpjj6e4ayUz7V+FpKypwgs82xbRdYtchTTUB+Q==}
+ cpu: [x64]
+ os: [linux]
+
+ '@img/sharp-libvips-linuxmusl-arm64@1.1.0':
+ resolution: {integrity: sha512-jYZdG+whg0MDK+q2COKbYidaqW/WTz0cc1E+tMAusiDygrM4ypmSCjOJPmFTvHHJ8j/6cAGyeDWZOsK06tP33w==}
+ cpu: [arm64]
+ os: [linux]
+
+ '@img/sharp-libvips-linuxmusl-x64@1.1.0':
+ resolution: {integrity: sha512-wK7SBdwrAiycjXdkPnGCPLjYb9lD4l6Ze2gSdAGVZrEL05AOUJESWU2lhlC+Ffn5/G+VKuSm6zzbQSzFX/P65A==}
+ cpu: [x64]
+ os: [linux]
+
+ '@img/sharp-linux-arm64@0.34.1':
+ resolution: {integrity: sha512-kX2c+vbvaXC6vly1RDf/IWNXxrlxLNpBVWkdpRq5Ka7OOKj6nr66etKy2IENf6FtOgklkg9ZdGpEu9kwdlcwOQ==}
+ engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
+ cpu: [arm64]
+ os: [linux]
+
+ '@img/sharp-linux-arm@0.34.1':
+ resolution: {integrity: sha512-anKiszvACti2sGy9CirTlNyk7BjjZPiML1jt2ZkTdcvpLU1YH6CXwRAZCA2UmRXnhiIftXQ7+Oh62Ji25W72jA==}
+ engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
+ cpu: [arm]
+ os: [linux]
+
+ '@img/sharp-linux-s390x@0.34.1':
+ resolution: {integrity: sha512-7s0KX2tI9mZI2buRipKIw2X1ufdTeaRgwmRabt5bi9chYfhur+/C1OXg3TKg/eag1W+6CCWLVmSauV1owmRPxA==}
+ engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
+ cpu: [s390x]
+ os: [linux]
+
+ '@img/sharp-linux-x64@0.34.1':
+ resolution: {integrity: sha512-wExv7SH9nmoBW3Wr2gvQopX1k8q2g5V5Iag8Zk6AVENsjwd+3adjwxtp3Dcu2QhOXr8W9NusBU6XcQUohBZ5MA==}
+ engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
+ cpu: [x64]
+ os: [linux]
+
+ '@img/sharp-linuxmusl-arm64@0.34.1':
+ resolution: {integrity: sha512-DfvyxzHxw4WGdPiTF0SOHnm11Xv4aQexvqhRDAoD00MzHekAj9a/jADXeXYCDFH/DzYruwHbXU7uz+H+nWmSOQ==}
+ engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
+ cpu: [arm64]
+ os: [linux]
+
+ '@img/sharp-linuxmusl-x64@0.34.1':
+ resolution: {integrity: sha512-pax/kTR407vNb9qaSIiWVnQplPcGU8LRIJpDT5o8PdAx5aAA7AS3X9PS8Isw1/WfqgQorPotjrZL3Pqh6C5EBg==}
+ engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
+ cpu: [x64]
+ os: [linux]
+
+ '@img/sharp-wasm32@0.34.1':
+ resolution: {integrity: sha512-YDybQnYrLQfEpzGOQe7OKcyLUCML4YOXl428gOOzBgN6Gw0rv8dpsJ7PqTHxBnXnwXr8S1mYFSLSa727tpz0xg==}
+ engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
+ cpu: [wasm32]
+
+ '@img/sharp-win32-ia32@0.34.1':
+ resolution: {integrity: sha512-WKf/NAZITnonBf3U1LfdjoMgNO5JYRSlhovhRhMxXVdvWYveM4kM3L8m35onYIdh75cOMCo1BexgVQcCDzyoWw==}
+ engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
+ cpu: [ia32]
+ os: [win32]
+
+ '@img/sharp-win32-x64@0.34.1':
+ resolution: {integrity: sha512-hw1iIAHpNE8q3uMIRCgGOeDoz9KtFNarFLQclLxr/LK1VBkj8nby18RjFvr6aP7USRYAjTZW6yisnBWMX571Tw==}
+ engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
+ cpu: [x64]
+ os: [win32]
+
+ '@isaacs/cliui@8.0.2':
+ resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==}
+ engines: {node: '>=12'}
+
+ '@jridgewell/gen-mapping@0.3.8':
+ resolution: {integrity: sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==}
+ engines: {node: '>=6.0.0'}
+
+ '@jridgewell/resolve-uri@3.1.2':
+ resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==}
+ engines: {node: '>=6.0.0'}
+
+ '@jridgewell/set-array@1.2.1':
+ resolution: {integrity: sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==}
+ engines: {node: '>=6.0.0'}
+
+ '@jridgewell/sourcemap-codec@1.5.0':
+ resolution: {integrity: sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==}
+
+ '@jridgewell/trace-mapping@0.3.25':
+ resolution: {integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==}
+
+ '@js-sdsl/ordered-map@4.4.2':
+ resolution: {integrity: sha512-iUKgm52T8HOE/makSxjqoWhe95ZJA1/G1sYsGev2JDKUSS14KAgg1LHb+Ba+IPow0xflbnSkOsZcO08C7w1gYw==}
+
+ '@manypkg/find-root@1.1.0':
+ resolution: {integrity: sha512-mki5uBvhHzO8kYYix/WRy2WX8S3B5wdVSc9D6KcU5lQNglP2yt58/VfLuAK49glRXChosY8ap2oJ1qgma3GUVA==}
+
+ '@manypkg/get-packages@1.1.3':
+ resolution: {integrity: sha512-fo+QhuU3qE/2TQMQmbVMqaQ6EWbMhi4ABWP+O4AM1NqPBuy0OrApV5LO6BrrgnhtAHS2NH6RrVk9OL181tTi8A==}
+
+ '@mapbox/node-pre-gyp@1.0.11':
+ resolution: {integrity: sha512-Yhlar6v9WQgUp/He7BdgzOz8lqMQ8sU+jkCq7Wx8Myc5YFJLbEe7lgui/V7G1qB1DJykHSGwreceSaD60Y0PUQ==}
+ hasBin: true
+
+ '@next/env@15.4.0-canary.86':
+ resolution: {integrity: sha512-WPrEvwqHnjeLx05ncJvqizbBJJFlQGRbxzOnL/pZWKzo19auM9x5Se87P27+E/D/d6jJS801l+thF85lfobAZQ==}
+
+ '@next/eslint-plugin-next@14.2.18':
+ resolution: {integrity: sha512-KyYTbZ3GQwWOjX3Vi1YcQbekyGP0gdammb7pbmmi25HBUCINzDReyrzCMOJIeZisK1Q3U6DT5Rlc4nm2/pQeXA==}
+
+ '@next/swc-darwin-arm64@15.4.0-canary.86':
+ resolution: {integrity: sha512-1ofBmzjPkmoMdM+dXvybZ/Roq8HRo0sFzcwXk7/FJNOufuwyK+QKdSpLE7pHlPR7ZREqfEMj61ONO+gAK+zOJw==}
+ engines: {node: '>= 10'}
+ cpu: [arm64]
+ os: [darwin]
+
+ '@next/swc-darwin-x64@15.4.0-canary.86':
+ resolution: {integrity: sha512-WCKSrllvwzYi4TgrSdgxKSOF2nhieeaWWOeGucn0OXy50uOAamr0HwP5OaIBCx3oRar4w66gvs4IrdTdMedeJA==}
+ engines: {node: '>= 10'}
+ cpu: [x64]
+ os: [darwin]
+
+ '@next/swc-linux-arm64-gnu@15.4.0-canary.86':
+ resolution: {integrity: sha512-8qn7DJVNFjhEIDo2ts0YCsO7g+vJjPWh8Ur8lBK3XspeX0BPsF4s+YmgidrpzRXeIfoo2uYLkkXcy/57CVDblw==}
+ engines: {node: '>= 10'}
+ cpu: [arm64]
+ os: [linux]
+
+ '@next/swc-linux-arm64-musl@15.4.0-canary.86':
+ resolution: {integrity: sha512-8MTn6N4Ja25neMLu2Bra1lqW9AWPqsYg0BVs5M/cxL0QkcN3mak/8LLX1vbzz7GigMGSA+NLwg+ol8lglfgIGA==}
+ engines: {node: '>= 10'}
+ cpu: [arm64]
+ os: [linux]
+
+ '@next/swc-linux-x64-gnu@15.4.0-canary.86':
+ resolution: {integrity: sha512-hIhzDwWDQHnH0M0Pzaqs1c5fa4+LHiLLEBuPJQvhBxQfH+Eh86DWiWHDCaoNiURvdRPg6uCuF2MjwptrMplEkg==}
+ engines: {node: '>= 10'}
+ cpu: [x64]
+ os: [linux]
+
+ '@next/swc-linux-x64-musl@15.4.0-canary.86':
+ resolution: {integrity: sha512-FG6SBuSeRWYMNu6tsfaZ4iDzv3BLxlpRncO2xvKKQPeUdDSQ0cehuHYnx8fRte8IOAJ3rlbRd6NXvrDarqu92Q==}
+ engines: {node: '>= 10'}
+ cpu: [x64]
+ os: [linux]
+
+ '@next/swc-win32-arm64-msvc@15.4.0-canary.86':
+ resolution: {integrity: sha512-3HvZo4VuyINrNYplRhvC8ILdKwi/vFDHOcTN/I4ru039TFpu2eO6VtXsLBdOdJjGslSSSBYkX+6yRrghihAZDA==}
+ engines: {node: '>= 10'}
+ cpu: [arm64]
+ os: [win32]
+
+ '@next/swc-win32-x64-msvc@15.4.0-canary.86':
+ resolution: {integrity: sha512-UO9JzGGj7GhtSJFdI0Bl0dkIIBfgbhXLsgNVmq9Z/CsUsQB6J9RS/BMhsxfVwhO+RETk13nFpNutMAhAwcuD8w==}
+ engines: {node: '>= 10'}
+ cpu: [x64]
+ os: [win32]
+
+ '@nicolo-ribaudo/eslint-scope-5-internals@5.1.1-v1':
+ resolution: {integrity: sha512-54/JRvkLIzzDWshCWfuhadfrfZVPiElY8Fcgmg1HroEly/EDSszzhBAsarCux+D/kOslTRquNzuyGSmUSTTHGg==}
+
+ '@nodelib/fs.scandir@2.1.5':
+ resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==}
+ engines: {node: '>= 8'}
+
+ '@nodelib/fs.stat@2.0.5':
+ resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==}
+ engines: {node: '>= 8'}
+
+ '@nodelib/fs.walk@1.2.8':
+ resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==}
+ engines: {node: '>= 8'}
+
+ '@nolyfill/is-core-module@1.0.39':
+ resolution: {integrity: sha512-nn5ozdjYQpUCZlWGuxcJY/KpxkWQs4DcbMCmKojjyrYDEAGy4Ce19NN4v5MduafTwJlbKc99UA8YhSVqq9yPZA==}
+ engines: {node: '>=12.4.0'}
+
+ '@otplib/core@12.0.1':
+ resolution: {integrity: sha512-4sGntwbA/AC+SbPhbsziRiD+jNDdIzsZ3JUyfZwjtKyc/wufl1pnSIaG4Uqx8ymPagujub0o92kgBnB89cuAMA==}
+
+ '@otplib/plugin-crypto@12.0.1':
+ resolution: {integrity: sha512-qPuhN3QrT7ZZLcLCyKOSNhuijUi9G5guMRVrxq63r9YNOxxQjPm59gVxLM+7xGnHnM6cimY57tuKsjK7y9LM1g==}
+
+ '@otplib/plugin-thirty-two@12.0.1':
+ resolution: {integrity: sha512-MtT+uqRso909UkbrrYpJ6XFjj9D+x2Py7KjTO9JDPhL0bJUYVu5kFP4TFZW4NFAywrAtFRxOVY261u0qwb93gA==}
+
+ '@parcel/watcher-android-arm64@2.5.1':
+ resolution: {integrity: sha512-KF8+j9nNbUN8vzOFDpRMsaKBHZ/mcjEjMToVMJOhTozkDonQFFrRcfdLWn6yWKCmJKmdVxSgHiYvTCef4/qcBA==}
+ engines: {node: '>= 10.0.0'}
+ cpu: [arm64]
+ os: [android]
+
+ '@parcel/watcher-darwin-arm64@2.5.1':
+ resolution: {integrity: sha512-eAzPv5osDmZyBhou8PoF4i6RQXAfeKL9tjb3QzYuccXFMQU0ruIc/POh30ePnaOyD1UXdlKguHBmsTs53tVoPw==}
+ engines: {node: '>= 10.0.0'}
+ cpu: [arm64]
+ os: [darwin]
+
+ '@parcel/watcher-darwin-x64@2.5.1':
+ resolution: {integrity: sha512-1ZXDthrnNmwv10A0/3AJNZ9JGlzrF82i3gNQcWOzd7nJ8aj+ILyW1MTxVk35Db0u91oD5Nlk9MBiujMlwmeXZg==}
+ engines: {node: '>= 10.0.0'}
+ cpu: [x64]
+ os: [darwin]
+
+ '@parcel/watcher-freebsd-x64@2.5.1':
+ resolution: {integrity: sha512-SI4eljM7Flp9yPuKi8W0ird8TI/JK6CSxju3NojVI6BjHsTyK7zxA9urjVjEKJ5MBYC+bLmMcbAWlZ+rFkLpJQ==}
+ engines: {node: '>= 10.0.0'}
+ cpu: [x64]
+ os: [freebsd]
+
+ '@parcel/watcher-linux-arm-glibc@2.5.1':
+ resolution: {integrity: sha512-RCdZlEyTs8geyBkkcnPWvtXLY44BCeZKmGYRtSgtwwnHR4dxfHRG3gR99XdMEdQ7KeiDdasJwwvNSF5jKtDwdA==}
+ engines: {node: '>= 10.0.0'}
+ cpu: [arm]
+ os: [linux]
+
+ '@parcel/watcher-linux-arm-musl@2.5.1':
+ resolution: {integrity: sha512-6E+m/Mm1t1yhB8X412stiKFG3XykmgdIOqhjWj+VL8oHkKABfu/gjFj8DvLrYVHSBNC+/u5PeNrujiSQ1zwd1Q==}
+ engines: {node: '>= 10.0.0'}
+ cpu: [arm]
+ os: [linux]
+
+ '@parcel/watcher-linux-arm64-glibc@2.5.1':
+ resolution: {integrity: sha512-LrGp+f02yU3BN9A+DGuY3v3bmnFUggAITBGriZHUREfNEzZh/GO06FF5u2kx8x+GBEUYfyTGamol4j3m9ANe8w==}
+ engines: {node: '>= 10.0.0'}
+ cpu: [arm64]
+ os: [linux]
+
+ '@parcel/watcher-linux-arm64-musl@2.5.1':
+ resolution: {integrity: sha512-cFOjABi92pMYRXS7AcQv9/M1YuKRw8SZniCDw0ssQb/noPkRzA+HBDkwmyOJYp5wXcsTrhxO0zq1U11cK9jsFg==}
+ engines: {node: '>= 10.0.0'}
+ cpu: [arm64]
+ os: [linux]
+
+ '@parcel/watcher-linux-x64-glibc@2.5.1':
+ resolution: {integrity: sha512-GcESn8NZySmfwlTsIur+49yDqSny2IhPeZfXunQi48DMugKeZ7uy1FX83pO0X22sHntJ4Ub+9k34XQCX+oHt2A==}
+ engines: {node: '>= 10.0.0'}
+ cpu: [x64]
+ os: [linux]
+
+ '@parcel/watcher-linux-x64-musl@2.5.1':
+ resolution: {integrity: sha512-n0E2EQbatQ3bXhcH2D1XIAANAcTZkQICBPVaxMeaCVBtOpBZpWJuf7LwyWPSBDITb7In8mqQgJ7gH8CILCURXg==}
+ engines: {node: '>= 10.0.0'}
+ cpu: [x64]
+ os: [linux]
+
+ '@parcel/watcher-win32-arm64@2.5.1':
+ resolution: {integrity: sha512-RFzklRvmc3PkjKjry3hLF9wD7ppR4AKcWNzH7kXR7GUe0Igb3Nz8fyPwtZCSquGrhU5HhUNDr/mKBqj7tqA2Vw==}
+ engines: {node: '>= 10.0.0'}
+ cpu: [arm64]
+ os: [win32]
+
+ '@parcel/watcher-win32-ia32@2.5.1':
+ resolution: {integrity: sha512-c2KkcVN+NJmuA7CGlaGD1qJh1cLfDnQsHjE89E60vUEMlqduHGCdCLJCID5geFVM0dOtA3ZiIO8BoEQmzQVfpQ==}
+ engines: {node: '>= 10.0.0'}
+ cpu: [ia32]
+ os: [win32]
+
+ '@parcel/watcher-win32-x64@2.5.1':
+ resolution: {integrity: sha512-9lHBdJITeNR++EvSQVUcaZoWupyHfXe1jZvGZ06O/5MflPcuPLtEphScIBL+AiCWBO46tDSHzWyD0uDmmZqsgA==}
+ engines: {node: '>= 10.0.0'}
+ cpu: [x64]
+ os: [win32]
+
+ '@parcel/watcher@2.5.1':
+ resolution: {integrity: sha512-dfUnCxiN9H4ap84DvD2ubjw+3vUNpstxa0TneY/Paat8a3R4uQZDLSvWjmznAY/DoahqTHl9V46HF/Zs3F29pg==}
+ engines: {node: '>= 10.0.0'}
+
+ '@pkgjs/parseargs@0.11.0':
+ resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==}
+ engines: {node: '>=14'}
+
+ '@playwright/test@1.52.0':
+ resolution: {integrity: sha512-uh6W7sb55hl7D6vsAeA+V2p5JnlAqzhqFyF0VcJkKZXkgnFcVG9PziERRHQfPLfNGx1C292a4JqbWzhR8L4R1g==}
+ engines: {node: '>=18'}
+ hasBin: true
+
+ '@protobufjs/aspromise@1.1.2':
+ resolution: {integrity: sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==}
+
+ '@protobufjs/base64@1.1.2':
+ resolution: {integrity: sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==}
+
+ '@protobufjs/codegen@2.0.4':
+ resolution: {integrity: sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==}
+
+ '@protobufjs/eventemitter@1.1.0':
+ resolution: {integrity: sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==}
+
+ '@protobufjs/fetch@1.1.0':
+ resolution: {integrity: sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==}
+
+ '@protobufjs/float@1.0.2':
+ resolution: {integrity: sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==}
+
+ '@protobufjs/inquire@1.1.0':
+ resolution: {integrity: sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==}
+
+ '@protobufjs/path@1.1.2':
+ resolution: {integrity: sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==}
+
+ '@protobufjs/pool@1.1.0':
+ resolution: {integrity: sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==}
+
+ '@protobufjs/utf8@1.1.0':
+ resolution: {integrity: sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==}
+
+ '@react-aria/focus@3.18.3':
+ resolution: {integrity: sha512-WKUElg+5zS0D3xlVn8MntNnkzJql2J6MuzAMP8Sv5WTgFDse/XGR842dsxPTIyKKdrWVCRegCuwa4m3n/GzgJw==}
+ peerDependencies:
+ react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0
+
+ '@react-aria/interactions@3.22.3':
+ resolution: {integrity: sha512-RRUb/aG+P0IKTIWikY/SylB6bIbLZeztnZY2vbe7RAG5MgVaCgn5HQ45SI15GlTmhsFG8CnF6slJsUFJiNHpbQ==}
+ peerDependencies:
+ react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0
+
+ '@react-aria/ssr@3.9.6':
+ resolution: {integrity: sha512-iLo82l82ilMiVGy342SELjshuWottlb5+VefO3jOQqQRNYnJBFpUSadswDPbRimSgJUZuFwIEYs6AabkP038fA==}
+ engines: {node: '>= 12'}
+ peerDependencies:
+ react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0
+
+ '@react-aria/utils@3.25.3':
+ resolution: {integrity: sha512-PR5H/2vaD8fSq0H/UB9inNbc8KDcVmW6fYAfSWkkn+OAdhTTMVKqXXrZuZBWyFfSD5Ze7VN6acr4hrOQm2bmrA==}
+ peerDependencies:
+ react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0
+
+ '@react-stately/utils@3.10.4':
+ resolution: {integrity: sha512-gBEQEIMRh5f60KCm7QKQ2WfvhB2gLUr9b72sqUdIZ2EG+xuPgaIlCBeSicvjmjBvYZwOjoOEnmIkcx2GHp/HWw==}
+ peerDependencies:
+ react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0
+
+ '@react-types/shared@3.25.0':
+ resolution: {integrity: sha512-OZSyhzU6vTdW3eV/mz5i6hQwQUhkRs7xwY2d1aqPvTdMe0+2cY7Fwp45PAiwYLEj73i9ro2FxF9qC4DvHGSCgQ==}
+ peerDependencies:
+ react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0
+
+ '@rollup/rollup-android-arm-eabi@4.40.0':
+ resolution: {integrity: sha512-+Fbls/diZ0RDerhE8kyC6hjADCXA1K4yVNlH0EYfd2XjyH0UGgzaQ8MlT0pCXAThfxv3QUAczHaL+qSv1E4/Cg==}
+ cpu: [arm]
+ os: [android]
+
+ '@rollup/rollup-android-arm64@4.40.0':
+ resolution: {integrity: sha512-PPA6aEEsTPRz+/4xxAmaoWDqh67N7wFbgFUJGMnanCFs0TV99M0M8QhhaSCks+n6EbQoFvLQgYOGXxlMGQe/6w==}
+ cpu: [arm64]
+ os: [android]
+
+ '@rollup/rollup-darwin-arm64@4.40.0':
+ resolution: {integrity: sha512-GwYOcOakYHdfnjjKwqpTGgn5a6cUX7+Ra2HeNj/GdXvO2VJOOXCiYYlRFU4CubFM67EhbmzLOmACKEfvp3J1kQ==}
+ cpu: [arm64]
+ os: [darwin]
+
+ '@rollup/rollup-darwin-x64@4.40.0':
+ resolution: {integrity: sha512-CoLEGJ+2eheqD9KBSxmma6ld01czS52Iw0e2qMZNpPDlf7Z9mj8xmMemxEucinev4LgHalDPczMyxzbq+Q+EtA==}
+ cpu: [x64]
+ os: [darwin]
+
+ '@rollup/rollup-freebsd-arm64@4.40.0':
+ resolution: {integrity: sha512-r7yGiS4HN/kibvESzmrOB/PxKMhPTlz+FcGvoUIKYoTyGd5toHp48g1uZy1o1xQvybwwpqpe010JrcGG2s5nkg==}
+ cpu: [arm64]
+ os: [freebsd]
+
+ '@rollup/rollup-freebsd-x64@4.40.0':
+ resolution: {integrity: sha512-mVDxzlf0oLzV3oZOr0SMJ0lSDd3xC4CmnWJ8Val8isp9jRGl5Dq//LLDSPFrasS7pSm6m5xAcKaw3sHXhBjoRw==}
+ cpu: [x64]
+ os: [freebsd]
+
+ '@rollup/rollup-linux-arm-gnueabihf@4.40.0':
+ resolution: {integrity: sha512-y/qUMOpJxBMy8xCXD++jeu8t7kzjlOCkoxxajL58G62PJGBZVl/Gwpm7JK9+YvlB701rcQTzjUZ1JgUoPTnoQA==}
+ cpu: [arm]
+ os: [linux]
+
+ '@rollup/rollup-linux-arm-musleabihf@4.40.0':
+ resolution: {integrity: sha512-GoCsPibtVdJFPv/BOIvBKO/XmwZLwaNWdyD8TKlXuqp0veo2sHE+A/vpMQ5iSArRUz/uaoj4h5S6Pn0+PdhRjg==}
+ cpu: [arm]
+ os: [linux]
+
+ '@rollup/rollup-linux-arm64-gnu@4.40.0':
+ resolution: {integrity: sha512-L5ZLphTjjAD9leJzSLI7rr8fNqJMlGDKlazW2tX4IUF9P7R5TMQPElpH82Q7eNIDQnQlAyiNVfRPfP2vM5Avvg==}
+ cpu: [arm64]
+ os: [linux]
+
+ '@rollup/rollup-linux-arm64-musl@4.40.0':
+ resolution: {integrity: sha512-ATZvCRGCDtv1Y4gpDIXsS+wfFeFuLwVxyUBSLawjgXK2tRE6fnsQEkE4csQQYWlBlsFztRzCnBvWVfcae/1qxQ==}
+ cpu: [arm64]
+ os: [linux]
+
+ '@rollup/rollup-linux-loongarch64-gnu@4.40.0':
+ resolution: {integrity: sha512-wG9e2XtIhd++QugU5MD9i7OnpaVb08ji3P1y/hNbxrQ3sYEelKJOq1UJ5dXczeo6Hj2rfDEL5GdtkMSVLa/AOg==}
+ cpu: [loong64]
+ os: [linux]
+
+ '@rollup/rollup-linux-powerpc64le-gnu@4.40.0':
+ resolution: {integrity: sha512-vgXfWmj0f3jAUvC7TZSU/m/cOE558ILWDzS7jBhiCAFpY2WEBn5jqgbqvmzlMjtp8KlLcBlXVD2mkTSEQE6Ixw==}
+ cpu: [ppc64]
+ os: [linux]
+
+ '@rollup/rollup-linux-riscv64-gnu@4.40.0':
+ resolution: {integrity: sha512-uJkYTugqtPZBS3Z136arevt/FsKTF/J9dEMTX/cwR7lsAW4bShzI2R0pJVw+hcBTWF4dxVckYh72Hk3/hWNKvA==}
+ cpu: [riscv64]
+ os: [linux]
+
+ '@rollup/rollup-linux-riscv64-musl@4.40.0':
+ resolution: {integrity: sha512-rKmSj6EXQRnhSkE22+WvrqOqRtk733x3p5sWpZilhmjnkHkpeCgWsFFo0dGnUGeA+OZjRl3+VYq+HyCOEuwcxQ==}
+ cpu: [riscv64]
+ os: [linux]
+
+ '@rollup/rollup-linux-s390x-gnu@4.40.0':
+ resolution: {integrity: sha512-SpnYlAfKPOoVsQqmTFJ0usx0z84bzGOS9anAC0AZ3rdSo3snecihbhFTlJZ8XMwzqAcodjFU4+/SM311dqE5Sw==}
+ cpu: [s390x]
+ os: [linux]
+
+ '@rollup/rollup-linux-x64-gnu@4.40.0':
+ resolution: {integrity: sha512-RcDGMtqF9EFN8i2RYN2W+64CdHruJ5rPqrlYw+cgM3uOVPSsnAQps7cpjXe9be/yDp8UC7VLoCoKC8J3Kn2FkQ==}
+ cpu: [x64]
+ os: [linux]
+
+ '@rollup/rollup-linux-x64-musl@4.40.0':
+ resolution: {integrity: sha512-HZvjpiUmSNx5zFgwtQAV1GaGazT2RWvqeDi0hV+AtC8unqqDSsaFjPxfsO6qPtKRRg25SisACWnJ37Yio8ttaw==}
+ cpu: [x64]
+ os: [linux]
+
+ '@rollup/rollup-win32-arm64-msvc@4.40.0':
+ resolution: {integrity: sha512-UtZQQI5k/b8d7d3i9AZmA/t+Q4tk3hOC0tMOMSq2GlMYOfxbesxG4mJSeDp0EHs30N9bsfwUvs3zF4v/RzOeTQ==}
+ cpu: [arm64]
+ os: [win32]
+
+ '@rollup/rollup-win32-ia32-msvc@4.40.0':
+ resolution: {integrity: sha512-+m03kvI2f5syIqHXCZLPVYplP8pQch9JHyXKZ3AGMKlg8dCyr2PKHjwRLiW53LTrN/Nc3EqHOKxUxzoSPdKddA==}
+ cpu: [ia32]
+ os: [win32]
+
+ '@rollup/rollup-win32-x64-msvc@4.40.0':
+ resolution: {integrity: sha512-lpPE1cLfP5oPzVjKMx10pgBmKELQnFJXHgvtHCtuJWOv8MxqdEIMNtgHgBFf7Ea2/7EuVwa9fodWUfXAlXZLZQ==}
+ cpu: [x64]
+ os: [win32]
+
+ '@rushstack/eslint-patch@1.10.4':
+ resolution: {integrity: sha512-WJgX9nzTqknM393q1QJDJmoW28kUfEnybeTfVNcNAPnIx210RXm2DiXiHzfNPJNIUUb1tJnz/l4QGtJ30PgWmA==}
+
+ '@sideway/address@4.1.5':
+ resolution: {integrity: sha512-IqO/DUQHUkPeixNQ8n0JA6102hT9CmaljNTPmQ1u8MEhBo/R4Q8eKLN/vGZxuebwOroDB4cbpjheD4+/sKFK4Q==}
+
+ '@sideway/formula@3.0.1':
+ resolution: {integrity: sha512-/poHZJJVjx3L+zVD6g9KgHfYnb443oi7wLu/XKojDviHy6HOEOA6z1Trk5aR1dGcmPenJEgb2sK2I80LeS3MIg==}
+
+ '@sideway/pinpoint@2.0.0':
+ resolution: {integrity: sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ==}
+
+ '@swc/counter@0.1.3':
+ resolution: {integrity: sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==}
+
+ '@swc/helpers@0.5.15':
+ resolution: {integrity: sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==}
+
+ '@swc/helpers@0.5.5':
+ resolution: {integrity: sha512-KGYxvIOXcceOAbEk4bi/dVLEK9z8sZ0uBB3Il5b1rhfClSpcX0yfRO0KmTkqR2cnQDymwLB+25ZyMzICg/cm/A==}
+
+ '@tailwindcss/forms@0.5.3':
+ resolution: {integrity: sha512-y5mb86JUoiUgBjY/o6FJSFZSEttfb3Q5gllE4xoKjAAD+vBrnIhE4dViwUuow3va8mpH4s9jyUbUbrRGoRdc2Q==}
+ peerDependencies:
+ tailwindcss: '>=3.0.0 || >= 3.0.0-alpha.1'
+
+ '@tailwindcss/forms@0.5.7':
+ resolution: {integrity: sha512-QE7X69iQI+ZXwldE+rzasvbJiyV/ju1FGHH0Qn2W3FKbuYtqp8LKcy6iSw79fVUT5/Vvf+0XgLCeYVG+UV6hOw==}
+ peerDependencies:
+ tailwindcss: '>=3.0.0 || >= 3.0.0-alpha.1'
+
+ '@tanstack/react-virtual@3.10.6':
+ resolution: {integrity: sha512-xaSy6uUxB92O8mngHZ6CvbhGuqxQ5lIZWCBy+FjhrbHmOwc6BnOnKkYm2FsB1/BpKw/+FVctlMbEtI+F6I1aJg==}
+ peerDependencies:
+ react: ^16.8.0 || ^17.0.0 || ^18.0.0
+ react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0
+
+ '@tanstack/virtual-core@3.10.6':
+ resolution: {integrity: sha512-1giLc4dzgEKLMx5pgKjL6HlG5fjZMgCjzlKAlpr7yoUtetVPELgER1NtephAI910nMwfPTHNyWKSFmJdHkz2Cw==}
+
+ '@testing-library/dom@10.4.0':
+ resolution: {integrity: sha512-pemlzrSESWbdAloYml3bAJMEfNh1Z7EduzqPKprCH5S341frlpYnUEW0H72dLxa6IsYr+mPno20GiSm+h9dEdQ==}
+ engines: {node: '>=18'}
+
+ '@testing-library/jest-dom@6.6.3':
+ resolution: {integrity: sha512-IteBhl4XqYNkM54f4ejhLRJiZNqcSCoXUOG2CPK7qbD322KjQozM4kHQOfkG2oln9b9HTYqs+Sae8vBATubxxA==}
+ engines: {node: '>=14', npm: '>=6', yarn: '>=1'}
+
+ '@testing-library/react@16.3.0':
+ resolution: {integrity: sha512-kFSyxiEDwv1WLl2fgsq6pPBbw5aWKrsY2/noi1Id0TK0UParSF62oFQFGHXIyaG4pp2tEub/Zlel+fjjZILDsw==}
+ engines: {node: '>=18'}
+ peerDependencies:
+ '@testing-library/dom': ^10.0.0
+ '@types/react': ^18.0.0 || ^19.0.0
+ '@types/react-dom': ^18.0.0 || ^19.0.0
+ react: ^18.0.0 || ^19.0.0
+ react-dom: ^18.0.0 || ^19.0.0
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+
+ '@types/aria-query@5.0.4':
+ resolution: {integrity: sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==}
+
+ '@types/babel__core@7.20.5':
+ resolution: {integrity: sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==}
+
+ '@types/babel__generator@7.27.0':
+ resolution: {integrity: sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==}
+
+ '@types/babel__template@7.4.4':
+ resolution: {integrity: sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==}
+
+ '@types/babel__traverse@7.20.7':
+ resolution: {integrity: sha512-dkO5fhS7+/oos4ciWxyEyjWe48zmG6wbCheo/G2ZnHx4fs3EU6YC6UM8rk56gAjNJ9P3MTH2jo5jb92/K6wbng==}
+
+ '@types/estree@1.0.7':
+ resolution: {integrity: sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ==}
+
+ '@types/json5@0.0.29':
+ resolution: {integrity: sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==}
+
+ '@types/ms@2.1.0':
+ resolution: {integrity: sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==}
+
+ '@types/node@12.20.55':
+ resolution: {integrity: sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ==}
+
+ '@types/node@22.14.1':
+ resolution: {integrity: sha512-u0HuPQwe/dHrItgHHpmw3N2fYCR6x4ivMNbPHRkBVP4CvN+kiRrKHWk3i8tXiO/joPwXLMYvF9TTF0eqgHIuOw==}
+
+ '@types/react-dom@19.1.2':
+ resolution: {integrity: sha512-XGJkWF41Qq305SKWEILa1O8vzhb3aOo3ogBlSmiqNko/WmRb6QIaweuZCXjKygVDXpzXb5wyxKTSOsmkuqj+Qw==}
+ peerDependencies:
+ '@types/react': ^19.0.0
+
+ '@types/react@19.1.2':
+ resolution: {integrity: sha512-oxLPMytKchWGbnQM9O7D67uPa9paTNxO7jVoNMXgkkErULBPhPARCfkKL9ytcIJJRGjbsVwW4ugJzyFFvm/Tiw==}
+
+ '@types/sinonjs__fake-timers@8.1.1':
+ resolution: {integrity: sha512-0kSuKjAS0TrGLJ0M/+8MaFkGsQhZpB6pxOmvS3K8FYI72K//YmdfoW9X2qPsAKh1mkwxGD5zib9s1FIFed6E8g==}
+
+ '@types/sizzle@2.3.9':
+ resolution: {integrity: sha512-xzLEyKB50yqCUPUJkIsrVvoWNfFUbIZI+RspLWt8u+tIW/BetMBZtgV2LY/2o+tYH8dRvQ+eoPf3NdhQCcLE2w==}
+
+ '@types/tinycolor2@1.4.3':
+ resolution: {integrity: sha512-Kf1w9NE5HEgGxCRyIcRXR/ZYtDv0V8FVPtYHwLxl0O+maGX0erE77pQlD0gpP+/KByMZ87mOA79SjifhSB3PjQ==}
+
+ '@types/uuid@10.0.0':
+ resolution: {integrity: sha512-7gqG38EyHgyP1S+7+xomFtL+ZNHcKv6DwNaCZmJmo1vgMugyF3TCnXVg4t1uk89mLNwnLtnY3TpOpCOyp1/xHQ==}
+
+ '@types/yauzl@2.10.3':
+ resolution: {integrity: sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q==}
+
+ '@typescript-eslint/eslint-plugin@8.15.0':
+ resolution: {integrity: sha512-+zkm9AR1Ds9uLWN3fkoeXgFppaQ+uEVtfOV62dDmsy9QCNqlRHWNEck4yarvRNrvRcHQLGfqBNui3cimoz8XAg==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+ peerDependencies:
+ '@typescript-eslint/parser': ^7.9.0
+ eslint: ^8.57.0 || ^9.0.0
+ typescript: '*'
+ peerDependenciesMeta:
+ typescript:
+ optional: true
+
+ '@typescript-eslint/parser@7.18.0':
+ resolution: {integrity: sha512-4Z+L8I2OqhZV8qA132M4wNL30ypZGYOQVBfMgxDH/K5UX0PNqTu1c6za9ST5r9+tavvHiTWmBnKzpCJ/GlVFtg==}
+ engines: {node: ^18.18.0 || >=20.0.0}
+ peerDependencies:
+ eslint: ^8.56.0
+ typescript: '*'
+ peerDependenciesMeta:
+ typescript:
+ optional: true
+
+ '@typescript-eslint/scope-manager@7.18.0':
+ resolution: {integrity: sha512-jjhdIE/FPF2B7Z1uzc6i3oWKbGcHb87Qw7AWj6jmEqNOfDFbJWtjt/XfwCpvNkpGWlcJaog5vTR+VV8+w9JflA==}
+ engines: {node: ^18.18.0 || >=20.0.0}
+
+ '@typescript-eslint/scope-manager@8.15.0':
+ resolution: {integrity: sha512-QRGy8ADi4J7ii95xz4UoiymmmMd/zuy9azCaamnZ3FM8T5fZcex8UfJcjkiEZjJSztKfEBe3dZ5T/5RHAmw2mA==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
+ '@typescript-eslint/type-utils@8.15.0':
+ resolution: {integrity: sha512-UU6uwXDoI3JGSXmcdnP5d8Fffa2KayOhUUqr/AiBnG1Gl7+7ut/oyagVeSkh7bxQ0zSXV9ptRh/4N15nkCqnpw==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+ peerDependencies:
+ eslint: ^8.57.0 || ^9.0.0
+ typescript: '*'
+ peerDependenciesMeta:
+ typescript:
+ optional: true
+
+ '@typescript-eslint/types@7.18.0':
+ resolution: {integrity: sha512-iZqi+Ds1y4EDYUtlOOC+aUmxnE9xS/yCigkjA7XpTKV6nCBd3Hp/PRGGmdwnfkV2ThMyYldP1wRpm/id99spTQ==}
+ engines: {node: ^18.18.0 || >=20.0.0}
+
+ '@typescript-eslint/types@8.15.0':
+ resolution: {integrity: sha512-n3Gt8Y/KyJNe0S3yDCD2RVKrHBC4gTUcLTebVBXacPy091E6tNspFLKRXlk3hwT4G55nfr1n2AdFqi/XMxzmPQ==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
+ '@typescript-eslint/typescript-estree@7.18.0':
+ resolution: {integrity: sha512-aP1v/BSPnnyhMHts8cf1qQ6Q1IFwwRvAQGRvBFkWlo3/lH29OXA3Pts+c10nxRxIBrDnoMqzhgdwVe5f2D6OzA==}
+ engines: {node: ^18.18.0 || >=20.0.0}
+ peerDependencies:
+ typescript: '*'
+ peerDependenciesMeta:
+ typescript:
+ optional: true
+
+ '@typescript-eslint/typescript-estree@8.15.0':
+ resolution: {integrity: sha512-1eMp2JgNec/niZsR7ioFBlsh/Fk0oJbhaqO0jRyQBMgkz7RrFfkqF9lYYmBoGBaSiLnu8TAPQTwoTUiSTUW9dg==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+ peerDependencies:
+ typescript: '*'
+ peerDependenciesMeta:
+ typescript:
+ optional: true
+
+ '@typescript-eslint/utils@8.15.0':
+ resolution: {integrity: sha512-k82RI9yGhr0QM3Dnq+egEpz9qB6Un+WLYhmoNcvl8ltMEededhh7otBVVIDDsEEttauwdY/hQoSsOv13lxrFzQ==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+ peerDependencies:
+ eslint: ^8.57.0 || ^9.0.0
+ typescript: '*'
+ peerDependenciesMeta:
+ typescript:
+ optional: true
+
+ '@typescript-eslint/visitor-keys@7.18.0':
+ resolution: {integrity: sha512-cDF0/Gf81QpY3xYyJKDV14Zwdmid5+uuENhjH2EqFaF0ni+yAyq/LzMaIJdhNJXZI7uLzwIlA+V7oWoyn6Curg==}
+ engines: {node: ^18.18.0 || >=20.0.0}
+
+ '@typescript-eslint/visitor-keys@8.15.0':
+ resolution: {integrity: sha512-h8vYOulWec9LhpwfAdZf2bjr8xIp0KNKnpgqSz0qqYYKAW/QZKw3ktRndbiAtUz4acH4QLQavwZBYCc0wulA/Q==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
+ '@ungap/structured-clone@1.2.0':
+ resolution: {integrity: sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==}
+
+ '@vercel/analytics@1.3.1':
+ resolution: {integrity: sha512-xhSlYgAuJ6Q4WQGkzYTLmXwhYl39sWjoMA3nHxfkvG+WdBT25c563a7QhwwKivEOZtPJXifYHR1m2ihoisbWyA==}
+ peerDependencies:
+ next: '>= 13'
+ react: ^18 || ^19
+ peerDependenciesMeta:
+ next:
+ optional: true
+ react:
+ optional: true
+
+ '@vercel/git-hooks@1.0.0':
+ resolution: {integrity: sha512-OxDFAAdyiJ/H0b8zR9rFCu3BIb78LekBXOphOYG3snV4ULhKFX387pBPpqZ9HLiRTejBWBxYEahkw79tuIgdAA==}
+
+ '@vitejs/plugin-react@4.4.1':
+ resolution: {integrity: sha512-IpEm5ZmeXAP/osiBXVVP5KjFMzbWOonMs0NaQQl+xYnUAcq4oHUBsF2+p4MgKWG4YMmFYJU8A6sxRPuowllm6w==}
+ engines: {node: ^14.18.0 || >=16.0.0}
+ peerDependencies:
+ vite: ^4.2.0 || ^5.0.0 || ^6.0.0
+
+ '@vitest/expect@3.1.2':
+ resolution: {integrity: sha512-O8hJgr+zREopCAqWl3uCVaOdqJwZ9qaDwUP7vy3Xigad0phZe9APxKhPcDNqYYi0rX5oMvwJMSCAXY2afqeTSA==}
+
+ '@vitest/mocker@3.1.2':
+ resolution: {integrity: sha512-kOtd6K2lc7SQ0mBqYv/wdGedlqPdM/B38paPY+OwJ1XiNi44w3Fpog82UfOibmHaV9Wod18A09I9SCKLyDMqgw==}
+ peerDependencies:
+ msw: ^2.4.9
+ vite: ^5.0.0 || ^6.0.0
+ peerDependenciesMeta:
+ msw:
+ optional: true
+ vite:
+ optional: true
+
+ '@vitest/pretty-format@3.1.2':
+ resolution: {integrity: sha512-R0xAiHuWeDjTSB3kQ3OQpT8Rx3yhdOAIm/JM4axXxnG7Q/fS8XUwggv/A4xzbQA+drYRjzkMnpYnOGAc4oeq8w==}
+
+ '@vitest/runner@3.1.2':
+ resolution: {integrity: sha512-bhLib9l4xb4sUMPXnThbnhX2Yi8OutBMA8Yahxa7yavQsFDtwY/jrUZwpKp2XH9DhRFJIeytlyGpXCqZ65nR+g==}
+
+ '@vitest/snapshot@3.1.2':
+ resolution: {integrity: sha512-Q1qkpazSF/p4ApZg1vfZSQ5Yw6OCQxVMVrLjslbLFA1hMDrT2uxtqMaw8Tc/jy5DLka1sNs1Y7rBcftMiaSH/Q==}
+
+ '@vitest/spy@3.1.2':
+ resolution: {integrity: sha512-OEc5fSXMws6sHVe4kOFyDSj/+4MSwst0ib4un0DlcYgQvRuYQ0+M2HyqGaauUMnjq87tmUaMNDxKQx7wNfVqPA==}
+
+ '@vitest/utils@3.1.2':
+ resolution: {integrity: sha512-5GGd0ytZ7BH3H6JTj9Kw7Prn1Nbg0wZVrIvou+UWxm54d+WoXXgAgjFJ8wn3LdagWLFSEfpPeyYrByZaGEZHLg==}
+
+ abbrev@1.1.1:
+ resolution: {integrity: sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==}
+
+ abort-controller-x@0.4.3:
+ resolution: {integrity: sha512-VtUwTNU8fpMwvWGn4xE93ywbogTYsuT+AUxAXOeelbXuQVIwNmC5YLeho9sH4vZ4ITW8414TTAOG1nW6uIVHCA==}
+
+ acorn-jsx@5.3.2:
+ resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==}
+ peerDependencies:
+ acorn: ^6.0.0 || ^7.0.0 || ^8.0.0
+
+ acorn@8.12.1:
+ resolution: {integrity: sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==}
+ engines: {node: '>=0.4.0'}
+ hasBin: true
+
+ agent-base@6.0.2:
+ resolution: {integrity: sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==}
+ engines: {node: '>= 6.0.0'}
+
+ agent-base@7.1.3:
+ resolution: {integrity: sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw==}
+ engines: {node: '>= 14'}
+
+ aggregate-error@3.1.0:
+ resolution: {integrity: sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==}
+ engines: {node: '>=8'}
+
+ ajv@6.12.6:
+ resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==}
+
+ ansi-colors@4.1.3:
+ resolution: {integrity: sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==}
+ engines: {node: '>=6'}
+
+ ansi-escapes@4.3.2:
+ resolution: {integrity: sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==}
+ engines: {node: '>=8'}
+
+ ansi-escapes@7.0.0:
+ resolution: {integrity: sha512-GdYO7a61mR0fOlAsvC9/rIHf7L96sBc6dEWzeOu+KAea5bZyQRPIpojrVoI4AXGJS/ycu/fBTdLrUkA4ODrvjw==}
+ engines: {node: '>=18'}
+
+ ansi-regex@5.0.1:
+ resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==}
+ engines: {node: '>=8'}
+
+ ansi-regex@6.1.0:
+ resolution: {integrity: sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==}
+ engines: {node: '>=12'}
+
+ ansi-styles@4.3.0:
+ resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==}
+ engines: {node: '>=8'}
+
+ ansi-styles@5.2.0:
+ resolution: {integrity: sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==}
+ engines: {node: '>=10'}
+
+ ansi-styles@6.2.1:
+ resolution: {integrity: sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==}
+ engines: {node: '>=12'}
+
+ any-promise@1.3.0:
+ resolution: {integrity: sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==}
+
+ anymatch@3.1.3:
+ resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==}
+ engines: {node: '>= 8'}
+
+ aproba@2.0.0:
+ resolution: {integrity: sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==}
+
+ arch@2.2.0:
+ resolution: {integrity: sha512-Of/R0wqp83cgHozfIYLbBMnej79U/SVGOOyuB3VVFv1NRM/PSFMK12x9KVtiYzJqmnU5WR2qp0Z5rHb7sWGnFQ==}
+
+ are-we-there-yet@2.0.0:
+ resolution: {integrity: sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw==}
+ engines: {node: '>=10'}
+ deprecated: This package is no longer supported.
+
+ arg@5.0.2:
+ resolution: {integrity: sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==}
+
+ argparse@1.0.10:
+ resolution: {integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==}
+
+ argparse@2.0.1:
+ resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==}
+
+ aria-query@5.1.3:
+ resolution: {integrity: sha512-R5iJ5lkuHybztUfuOAznmboyjWq8O6sqNqtK7CLOqdydi54VNbORp49mb14KbWgG1QD3JFO9hJdZ+y4KutfdOQ==}
+
+ aria-query@5.3.0:
+ resolution: {integrity: sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==}
+
+ array-buffer-byte-length@1.0.1:
+ resolution: {integrity: sha512-ahC5W1xgou+KTXix4sAO8Ki12Q+jf4i0+tmk3sC+zgcynshkHxzpXdImBehiUYKKKDwvfFiJl1tZt6ewscS1Mg==}
+ engines: {node: '>= 0.4'}
+
+ array-includes@3.1.8:
+ resolution: {integrity: sha512-itaWrbYbqpGXkGhZPGUulwnhVf5Hpy1xiCFsGqyIGglbBxmG5vSjxQen3/WGOjPpNEv1RtBLKxbmVXm8HpJStQ==}
+ engines: {node: '>= 0.4'}
+
+ array-union@2.1.0:
+ resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==}
+ engines: {node: '>=8'}
+
+ array.prototype.findlast@1.2.5:
+ resolution: {integrity: sha512-CVvd6FHg1Z3POpBLxO6E6zr+rSKEQ9L6rZHAaY7lLfhKsWYUBBOuMs0e9o24oopj6H+geRCX0YJ+TJLBK2eHyQ==}
+ engines: {node: '>= 0.4'}
+
+ array.prototype.findlastindex@1.2.5:
+ resolution: {integrity: sha512-zfETvRFA8o7EiNn++N5f/kaCw221hrpGsDmcpndVupkPzEc1Wuf3VgC0qby1BbHs7f5DVYjgtEU2LLh5bqeGfQ==}
+ engines: {node: '>= 0.4'}
+
+ array.prototype.flat@1.3.2:
+ resolution: {integrity: sha512-djYB+Zx2vLewY8RWlNCUdHjDXs2XOgm602S9E7P/UpHgfeHL00cRiIF+IN/G/aUJ7kGPb6yO/ErDI5V2s8iycA==}
+ engines: {node: '>= 0.4'}
+
+ array.prototype.flatmap@1.3.2:
+ resolution: {integrity: sha512-Ewyx0c9PmpcsByhSW4r+9zDU7sGjFc86qf/kKtuSCRdhfbk0SNLLkaT5qvcHnRGgc5NP/ly/y+qkXkqONX54CQ==}
+ engines: {node: '>= 0.4'}
+
+ array.prototype.tosorted@1.1.4:
+ resolution: {integrity: sha512-p6Fx8B7b7ZhL/gmUsAy0D15WhvDccw3mnGNbZpi3pmeJdxtWsj2jEaI4Y6oo3XiHfzuSgPwKc04MYt6KgvC/wA==}
+ engines: {node: '>= 0.4'}
+
+ arraybuffer.prototype.slice@1.0.3:
+ resolution: {integrity: sha512-bMxMKAjg13EBSVscxTaYA4mRc5t1UAXa2kXiGTNfZ079HIWXEkKmkgFrh/nJqamaLSrXO5H4WFFkPEaLJWbs3A==}
+ engines: {node: '>= 0.4'}
+
+ asn1@0.2.6:
+ resolution: {integrity: sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==}
+
+ assert-plus@1.0.0:
+ resolution: {integrity: sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==}
+ engines: {node: '>=0.8'}
+
+ assertion-error@2.0.1:
+ resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==}
+ engines: {node: '>=12'}
+
+ ast-types-flow@0.0.8:
+ resolution: {integrity: sha512-OH/2E5Fg20h2aPrbe+QL8JZQFko0YZaF+j4mnQ7BGhfavO7OpSLa8a0y9sBwomHdSbkhTS8TQNayBfnW5DwbvQ==}
+
+ astral-regex@2.0.0:
+ resolution: {integrity: sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==}
+ engines: {node: '>=8'}
+
+ async@3.2.6:
+ resolution: {integrity: sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==}
+
+ asynckit@0.4.0:
+ resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==}
+
+ at-least-node@1.0.0:
+ resolution: {integrity: sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==}
+ engines: {node: '>= 4.0.0'}
+
+ autoprefixer@10.4.21:
+ resolution: {integrity: sha512-O+A6LWV5LDHSJD3LjHYoNi4VLsj/Whi7k6zG12xTYaU4cQ8oxQGckXNX8cRHK5yOZ/ppVHe0ZBXGzSV9jXdVbQ==}
+ engines: {node: ^10 || ^12 || >=14}
+ hasBin: true
+ peerDependencies:
+ postcss: ^8.1.0
+
+ available-typed-arrays@1.0.7:
+ resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==}
+ engines: {node: '>= 0.4'}
+
+ aws-sign2@0.7.0:
+ resolution: {integrity: sha512-08kcGqnYf/YmjoRhfxyu+CLxBjUtHLXLXX/vUfx9l2LYzG3c1m61nrpyFUZI6zeS+Li/wWMMidD9KgrqtGq3mA==}
+
+ aws4@1.13.2:
+ resolution: {integrity: sha512-lHe62zvbTB5eEABUVi/AwVh0ZKY9rMMDhmm+eeyuuUQbQ3+J+fONVQOZyj+DdrvD4BY33uYniyRJ4UJIaSKAfw==}
+
+ axe-core@4.10.0:
+ resolution: {integrity: sha512-Mr2ZakwQ7XUAjp7pAwQWRhhK8mQQ6JAaNWSjmjxil0R8BPioMtQsTLOolGYkji1rcL++3dCqZA3zWqpT+9Ew6g==}
+ engines: {node: '>=4'}
+
+ axios@1.8.4:
+ resolution: {integrity: sha512-eBSYY4Y68NNlHbHBMdeDmKNtDgXWhQsJcGqzO3iLUM0GraQFSS9cVgPX5I9b3lbdFKyYoAEGAZF1DwhTaljNAw==}
+
+ axobject-query@3.1.1:
+ resolution: {integrity: sha512-goKlv8DZrK9hUh975fnHzhNIO4jUnFCfv/dszV5VwUGDFjI6vQ2VwoyjYjYNEbBE8AH87TduWP5uyDR1D+Iteg==}
+
+ balanced-match@1.0.2:
+ resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==}
+
+ base64-js@1.5.1:
+ resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==}
+
+ bcrypt-pbkdf@1.0.2:
+ resolution: {integrity: sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==}
+
+ better-path-resolve@1.0.0:
+ resolution: {integrity: sha512-pbnl5XzGBdrFU/wT4jqmJVPn2B6UHPBOhzMQkY/SPUPB6QtUXtmBHBIwCbXJol93mOpGMnQyP/+BB19q04xj7g==}
+ engines: {node: '>=4'}
+
+ binary-extensions@2.3.0:
+ resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==}
+ engines: {node: '>=8'}
+
+ blob-util@2.0.2:
+ resolution: {integrity: sha512-T7JQa+zsXXEa6/8ZhHcQEW1UFfVM49Ts65uBkFL6fz2QmrElqmbajIDJvuA0tEhRe5eIjpV9ZF+0RfZR9voJFQ==}
+
+ bluebird@3.7.2:
+ resolution: {integrity: sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==}
+
+ brace-expansion@1.1.11:
+ resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==}
+
+ brace-expansion@2.0.1:
+ resolution: {integrity: sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==}
+
+ braces@3.0.3:
+ resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==}
+ engines: {node: '>=8'}
+
+ browserslist@4.24.4:
+ resolution: {integrity: sha512-KDi1Ny1gSePi1vm0q4oxSF8b4DR44GF4BbmS2YdhPLOEqd8pDviZOGH/GsmRwoWJ2+5Lr085X7naowMwKHDG1A==}
+ engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7}
+ hasBin: true
+
+ buffer-crc32@0.2.13:
+ resolution: {integrity: sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==}
+
+ buffer@5.7.1:
+ resolution: {integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==}
+
+ bundle-require@5.1.0:
+ resolution: {integrity: sha512-3WrrOuZiyaaZPWiEt4G3+IffISVC9HYlWueJEBWED4ZH4aIAC2PnkdnuRrR94M+w6yGWn4AglWtJtBI8YqvgoA==}
+ engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
+ peerDependencies:
+ esbuild: '>=0.18'
+
+ cac@6.7.14:
+ resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==}
+ engines: {node: '>=8'}
+
+ cachedir@2.4.0:
+ resolution: {integrity: sha512-9EtFOZR8g22CL7BWjJ9BUx1+A/djkofnyW3aOXZORNW2kxoUpx2h+uN2cOqwPmFhnpVmxg+KW2OjOSgChTEvsQ==}
+ engines: {node: '>=6'}
+
+ call-bind-apply-helpers@1.0.2:
+ resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==}
+ engines: {node: '>= 0.4'}
+
+ call-bind@1.0.7:
+ resolution: {integrity: sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==}
+ engines: {node: '>= 0.4'}
+
+ call-bound@1.0.4:
+ resolution: {integrity: sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==}
+ engines: {node: '>= 0.4'}
+
+ callsites@3.1.0:
+ resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==}
+ engines: {node: '>=6'}
+
+ camelcase-css@2.0.1:
+ resolution: {integrity: sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==}
+ engines: {node: '>= 6'}
+
+ caniuse-lite@1.0.30001715:
+ resolution: {integrity: sha512-7ptkFGMm2OAOgvZpwgA4yjQ5SQbrNVGdRjzH0pBdy1Fasvcr+KAeECmbCAECzTuDuoX0FCY8KzUxjf9+9kfZEw==}
+
+ case-anything@2.1.13:
+ resolution: {integrity: sha512-zlOQ80VrQ2Ue+ymH5OuM/DlDq64mEm+B9UTdHULv5osUMD6HalNTblf2b1u/m6QecjsnOkBpqVZ+XPwIVsy7Ng==}
+ engines: {node: '>=12.13'}
+
+ caseless@0.12.0:
+ resolution: {integrity: sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==}
+
+ chai@5.2.0:
+ resolution: {integrity: sha512-mCuXncKXk5iCLhfhwTc0izo0gtEmpz5CtG2y8GiOINBlMVS6v8TMRc5TaLWKS6692m9+dVVfzgeVxR5UxWHTYw==}
+ engines: {node: '>=12'}
+
+ chalk@3.0.0:
+ resolution: {integrity: sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==}
+ engines: {node: '>=8'}
+
+ chalk@4.1.2:
+ resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==}
+ engines: {node: '>=10'}
+
+ chalk@5.4.1:
+ resolution: {integrity: sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w==}
+ engines: {node: ^12.17.0 || ^14.13 || >=16.0.0}
+
+ chardet@0.7.0:
+ resolution: {integrity: sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==}
+
+ check-error@2.1.1:
+ resolution: {integrity: sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw==}
+ engines: {node: '>= 16'}
+
+ check-more-types@2.24.0:
+ resolution: {integrity: sha512-Pj779qHxV2tuapviy1bSZNEL1maXr13bPYpsvSDB68HlYcYuhlDrmGd63i0JHMCLKzc7rUSNIrpdJlhVlNwrxA==}
+ engines: {node: '>= 0.8.0'}
+
+ chokidar@3.6.0:
+ resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==}
+ engines: {node: '>= 8.10.0'}
+
+ chokidar@4.0.3:
+ resolution: {integrity: sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==}
+ engines: {node: '>= 14.16.0'}
+
+ chownr@2.0.0:
+ resolution: {integrity: sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==}
+ engines: {node: '>=10'}
+
+ ci-info@3.9.0:
+ resolution: {integrity: sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==}
+ engines: {node: '>=8'}
+
+ ci-info@4.2.0:
+ resolution: {integrity: sha512-cYY9mypksY8NRqgDB1XD1RiJL338v/551niynFTGkZOO2LHuB2OmOYxDIe/ttN9AHwrqdum1360G3ald0W9kCg==}
+ engines: {node: '>=8'}
+
+ clean-stack@2.2.0:
+ resolution: {integrity: sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==}
+ engines: {node: '>=6'}
+
+ cli-cursor@3.1.0:
+ resolution: {integrity: sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==}
+ engines: {node: '>=8'}
+
+ cli-cursor@5.0.0:
+ resolution: {integrity: sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==}
+ engines: {node: '>=18'}
+
+ cli-table3@0.6.5:
+ resolution: {integrity: sha512-+W/5efTR7y5HRD7gACw9yQjqMVvEMLBHmboM/kPWam+H+Hmyrgjh6YncVKK122YZkXrLudzTuAukUw9FnMf7IQ==}
+ engines: {node: 10.* || >= 12.*}
+
+ cli-truncate@2.1.0:
+ resolution: {integrity: sha512-n8fOixwDD6b/ObinzTrp1ZKFzbgvKZvuz/TvejnLn1aQfC6r52XEx85FmuC+3HI+JM7coBRXUvNqEU2PHVrHpg==}
+ engines: {node: '>=8'}
+
+ cli-truncate@4.0.0:
+ resolution: {integrity: sha512-nPdaFdQ0h/GEigbPClz11D0v/ZJEwxmeVZGeMo3Z5StPtUTkA9o1lD6QwoirYiSDzbcwn2XcjwmCp68W1IS4TA==}
+ engines: {node: '>=18'}
+
+ client-only@0.0.1:
+ resolution: {integrity: sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==}
+
+ cliui@8.0.1:
+ resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==}
+ engines: {node: '>=12'}
+
+ clsx@1.2.1:
+ resolution: {integrity: sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg==}
+ engines: {node: '>=6'}
+
+ clsx@2.1.1:
+ resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==}
+ engines: {node: '>=6'}
+
+ color-convert@2.0.1:
+ resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==}
+ engines: {node: '>=7.0.0'}
+
+ color-name@1.1.4:
+ resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==}
+
+ color-string@1.9.1:
+ resolution: {integrity: sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==}
+
+ color-support@1.1.3:
+ resolution: {integrity: sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==}
+ hasBin: true
+
+ color@4.2.3:
+ resolution: {integrity: sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==}
+ engines: {node: '>=12.5.0'}
+
+ colorette@2.0.20:
+ resolution: {integrity: sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==}
+
+ combined-stream@1.0.8:
+ resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==}
+ engines: {node: '>= 0.8'}
+
+ commander@13.1.0:
+ resolution: {integrity: sha512-/rFeCpNJQbhSZjGVwO9RFV3xPqbnERS8MmIQzCtD/zl6gpJuV/bMLuN92oG3F7d8oDEHHRrujSXNUr8fpjntKw==}
+ engines: {node: '>=18'}
+
+ commander@4.1.1:
+ resolution: {integrity: sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==}
+ engines: {node: '>= 6'}
+
+ commander@6.2.1:
+ resolution: {integrity: sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA==}
+ engines: {node: '>= 6'}
+
+ common-tags@1.8.2:
+ resolution: {integrity: sha512-gk/Z852D2Wtb//0I+kRFNKKE9dIIVirjoqPoA1wJU+XePVXZfGeBpk45+A1rKO4Q43prqWBNY/MiIeRLbPWUaA==}
+ engines: {node: '>=4.0.0'}
+
+ concat-map@0.0.1:
+ resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==}
+
+ concurrently@9.1.2:
+ resolution: {integrity: sha512-H9MWcoPsYddwbOGM6difjVwVZHl63nwMEwDJG/L7VGtuaJhb12h2caPG2tVPWs7emuYix252iGfqOyrz1GczTQ==}
+ engines: {node: '>=18'}
+ hasBin: true
+
+ consola@3.4.2:
+ resolution: {integrity: sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA==}
+ engines: {node: ^14.18.0 || >=16.10.0}
+
+ console-control-strings@1.1.0:
+ resolution: {integrity: sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==}
+
+ convert-source-map@2.0.0:
+ resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==}
+
+ copy-to-clipboard@3.3.3:
+ resolution: {integrity: sha512-2KV8NhB5JqC3ky0r9PMCAZKbUHSwtEo4CwCs0KXgruG43gX5PMqDEBbVU4OUzw2MuAWUfsuFmWvEKG5QRfSnJA==}
+
+ core-util-is@1.0.2:
+ resolution: {integrity: sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==}
+
+ cross-spawn@7.0.3:
+ resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==}
+ engines: {node: '>= 8'}
+
+ cross-spawn@7.0.6:
+ resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==}
+ engines: {node: '>= 8'}
+
+ css.escape@1.5.1:
+ resolution: {integrity: sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg==}
+
+ cssesc@3.0.0:
+ resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==}
+ engines: {node: '>=4'}
+ hasBin: true
+
+ cssstyle@4.3.1:
+ resolution: {integrity: sha512-ZgW+Jgdd7i52AaLYCriF8Mxqft0gD/R9i9wi6RWBhs1pqdPEzPjym7rvRKi397WmQFf3SlyUsszhw+VVCbx79Q==}
+ engines: {node: '>=18'}
+
+ csstype@3.1.3:
+ resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==}
+
+ cypress@14.3.2:
+ resolution: {integrity: sha512-n+yGD2ZFFKgy7I3YtVpZ7BcFYrrDMcKj713eOZdtxPttpBjCyw/R8dLlFSsJPouneGN7A/HOSRyPJ5+3/gKDoA==}
+ engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0}
+ hasBin: true
+
+ damerau-levenshtein@1.0.8:
+ resolution: {integrity: sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==}
+
+ dashdash@1.14.1:
+ resolution: {integrity: sha512-jRFi8UDGo6j+odZiEpjazZaWqEal3w/basFjQHQEwVtZJGDpxbH1MeYluwCS8Xq5wmLJooDlMgvVarmWfGM44g==}
+ engines: {node: '>=0.10'}
+
+ data-uri-to-buffer@4.0.1:
+ resolution: {integrity: sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==}
+ engines: {node: '>= 12'}
+
+ data-urls@5.0.0:
+ resolution: {integrity: sha512-ZYP5VBHshaDAiVZxjbRVcFJpc+4xGgT0bK3vzy1HLN8jTO975HEbuYzZJcHoQEY5K1a0z8YayJkyVETa08eNTg==}
+ engines: {node: '>=18'}
+
+ data-view-buffer@1.0.1:
+ resolution: {integrity: sha512-0lht7OugA5x3iJLOWFhWK/5ehONdprk0ISXqVFn/NFrDu+cuc8iADFrGQz5BnRK7LLU3JmkbXSxaqX+/mXYtUA==}
+ engines: {node: '>= 0.4'}
+
+ data-view-byte-length@1.0.1:
+ resolution: {integrity: sha512-4J7wRJD3ABAzr8wP+OcIcqq2dlUKp4DVflx++hs5h5ZKydWMI6/D/fAot+yh6g2tHh8fLFTvNOaVN357NvSrOQ==}
+ engines: {node: '>= 0.4'}
+
+ data-view-byte-offset@1.0.0:
+ resolution: {integrity: sha512-t/Ygsytq+R995EJ5PZlD4Cu56sWa8InXySaViRzw9apusqsOO2bQP+SbYzAhR0pFKoB+43lYy8rWban9JSuXnA==}
+ engines: {node: '>= 0.4'}
+
+ dayjs@1.11.13:
+ resolution: {integrity: sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg==}
+
+ debug@3.2.7:
+ resolution: {integrity: sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==}
+ peerDependencies:
+ supports-color: '*'
+ peerDependenciesMeta:
+ supports-color:
+ optional: true
+
+ debug@4.3.7:
+ resolution: {integrity: sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==}
+ engines: {node: '>=6.0'}
+ peerDependencies:
+ supports-color: '*'
+ peerDependenciesMeta:
+ supports-color:
+ optional: true
+
+ debug@4.4.0:
+ resolution: {integrity: sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==}
+ engines: {node: '>=6.0'}
+ peerDependencies:
+ supports-color: '*'
+ peerDependenciesMeta:
+ supports-color:
+ optional: true
+
+ decimal.js@10.5.0:
+ resolution: {integrity: sha512-8vDa8Qxvr/+d94hSh5P3IJwI5t8/c0KsMp+g8bNw9cY2icONa5aPfvKeieW1WlG0WQYwwhJ7mjui2xtiePQSXw==}
+
+ deep-eql@5.0.2:
+ resolution: {integrity: sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==}
+ engines: {node: '>=6'}
+
+ deep-equal@2.2.3:
+ resolution: {integrity: sha512-ZIwpnevOurS8bpT4192sqAowWM76JDKSHYzMLty3BZGSswgq6pBaH3DhCSW5xVAZICZyKdOBPjwww5wfgT/6PA==}
+ engines: {node: '>= 0.4'}
+
+ deep-is@0.1.4:
+ resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==}
+
+ deepmerge@4.3.1:
+ resolution: {integrity: sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==}
+ engines: {node: '>=0.10.0'}
+
+ define-data-property@1.1.4:
+ resolution: {integrity: sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==}
+ engines: {node: '>= 0.4'}
+
+ define-properties@1.2.1:
+ resolution: {integrity: sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==}
+ engines: {node: '>= 0.4'}
+
+ delayed-stream@1.0.0:
+ resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==}
+ engines: {node: '>=0.4.0'}
+
+ delegates@1.0.0:
+ resolution: {integrity: sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==}
+
+ dequal@2.0.3:
+ resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==}
+ engines: {node: '>=6'}
+
+ detect-indent@6.1.0:
+ resolution: {integrity: sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA==}
+ engines: {node: '>=8'}
+
+ detect-libc@1.0.3:
+ resolution: {integrity: sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==}
+ engines: {node: '>=0.10'}
+ hasBin: true
+
+ detect-libc@2.0.4:
+ resolution: {integrity: sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA==}
+ engines: {node: '>=8'}
+
+ didyoumean@1.2.2:
+ resolution: {integrity: sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==}
+
+ dir-glob@3.0.1:
+ resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==}
+ engines: {node: '>=8'}
+
+ dlv@1.1.3:
+ resolution: {integrity: sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==}
+
+ doctrine@2.1.0:
+ resolution: {integrity: sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==}
+ engines: {node: '>=0.10.0'}
+
+ doctrine@3.0.0:
+ resolution: {integrity: sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==}
+ engines: {node: '>=6.0.0'}
+
+ dom-accessibility-api@0.5.16:
+ resolution: {integrity: sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==}
+
+ dom-accessibility-api@0.6.3:
+ resolution: {integrity: sha512-7ZgogeTnjuHbo+ct10G9Ffp0mif17idi0IyWNVA/wcwcm7NPOD/WEHVP3n7n3MhXqxoIYm8d6MuZohYWIZ4T3w==}
+
+ dotenv-cli@8.0.0:
+ resolution: {integrity: sha512-aLqYbK7xKOiTMIRf1lDPbI+Y+Ip/wo5k3eyp6ePysVaSqbyxjyK3dK35BTxG+rmd7djf5q2UPs4noPNH+cj0Qw==}
+ hasBin: true
+
+ dotenv-expand@10.0.0:
+ resolution: {integrity: sha512-GopVGCpVS1UKH75VKHGuQFqS1Gusej0z4FyQkPdwjil2gNIv+LNsqBlboOzpJFZKVT95GkCyWJbBSdFEFUWI2A==}
+ engines: {node: '>=12'}
+
+ dotenv@16.0.3:
+ resolution: {integrity: sha512-7GO6HghkA5fYG9TYnNxi14/7K9f5occMlp3zXAuSxn7CKCxt9xbNWG7yF8hTCSUchlfWSe3uLmlPfigevRItzQ==}
+ engines: {node: '>=12'}
+
+ dotenv@16.5.0:
+ resolution: {integrity: sha512-m/C+AwOAr9/W1UOIZUo232ejMNnJAJtYQjUbHoNTBNTJSvqzzDh7vnrei3o3r3m9blf6ZoDkvcw0VmozNRFJxg==}
+ engines: {node: '>=12'}
+
+ dprint-node@1.0.8:
+ resolution: {integrity: sha512-iVKnUtYfGrYcW1ZAlfR/F59cUVL8QIhWoBJoSjkkdua/dkWIgjZfiLMeTjiB06X0ZLkQ0M2C1VbUj/CxkIf1zg==}
+
+ dunder-proto@1.0.1:
+ resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==}
+ engines: {node: '>= 0.4'}
+
+ duplexer@0.1.2:
+ resolution: {integrity: sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==}
+
+ eastasianwidth@0.2.0:
+ resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==}
+
+ ecc-jsbn@0.1.2:
+ resolution: {integrity: sha512-eh9O+hwRHNbG4BLTjEl3nw044CkGm5X6LoaCf7LPp7UU8Qrt47JYNi6nPX8xjW97TKGKm1ouctg0QSpZe9qrnw==}
+
+ electron-to-chromium@1.5.140:
+ resolution: {integrity: sha512-o82Rj+ONp4Ip7Cl1r7lrqx/pXhbp/lh9DpKcMNscFJdh8ebyRofnc7Sh01B4jx403RI0oqTBvlZ7OBIZLMr2+Q==}
+
+ emoji-regex@10.4.0:
+ resolution: {integrity: sha512-EC+0oUMY1Rqm4O6LLrgjtYDvcVYTy7chDnM4Q7030tP4Kwj3u/pR6gP9ygnp2CJMK5Gq+9Q2oqmrFJAz01DXjw==}
+
+ emoji-regex@8.0.0:
+ resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==}
+
+ emoji-regex@9.2.2:
+ resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==}
+
+ end-of-stream@1.4.4:
+ resolution: {integrity: sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==}
+
+ enhanced-resolve@5.17.1:
+ resolution: {integrity: sha512-LMHl3dXhTcfv8gM4kEzIUeTQ+7fpdA0l2tUf34BddXPkz2A5xJ5L/Pchd5BL6rdccM9QGvu0sWZzK1Z1t4wwyg==}
+ engines: {node: '>=10.13.0'}
+
+ enquirer@2.4.1:
+ resolution: {integrity: sha512-rRqJg/6gd538VHvR3PSrdRBb/1Vy2YfzHqzvbhGIQpDRKIa4FgV/54b5Q1xYSxOOwKvjXweS26E0Q+nAMwp2pQ==}
+ engines: {node: '>=8.6'}
+
+ entities@6.0.0:
+ resolution: {integrity: sha512-aKstq2TDOndCn4diEyp9Uq/Flu2i1GlLkc6XIDQSDMuaFE3OPW5OphLCyQ5SpSJZTb4reN+kTcYru5yIfXoRPw==}
+ engines: {node: '>=0.12'}
+
+ env-cmd@10.1.0:
+ resolution: {integrity: sha512-mMdWTT9XKN7yNth/6N6g2GuKuJTsKMDHlQFUDacb/heQRRWOTIZ42t1rMHnQu4jYxU1ajdTeJM+9eEETlqToMA==}
+ engines: {node: '>=8.0.0'}
+ hasBin: true
+
+ environment@1.1.0:
+ resolution: {integrity: sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q==}
+ engines: {node: '>=18'}
+
+ es-abstract@1.23.3:
+ resolution: {integrity: sha512-e+HfNH61Bj1X9/jLc5v1owaLYuHdeHHSQlkhCBiTK8rBvKaULl/beGMxwrMXjpYrv4pz22BlY570vVePA2ho4A==}
+ engines: {node: '>= 0.4'}
+
+ es-define-property@1.0.0:
+ resolution: {integrity: sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==}
+ engines: {node: '>= 0.4'}
+
+ es-define-property@1.0.1:
+ resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==}
+ engines: {node: '>= 0.4'}
+
+ es-errors@1.3.0:
+ resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==}
+ engines: {node: '>= 0.4'}
+
+ es-get-iterator@1.1.3:
+ resolution: {integrity: sha512-sPZmqHBe6JIiTfN5q2pEi//TwxmAFHwj/XEuYjTuse78i8KxaqMTTzxPoFKuzRpDpTJ+0NAbpfenkmH2rePtuw==}
+
+ es-iterator-helpers@1.0.19:
+ resolution: {integrity: sha512-zoMwbCcH5hwUkKJkT8kDIBZSz9I6mVG//+lDCinLCGov4+r7NIy0ld8o03M0cJxl2spVf6ESYVS6/gpIfq1FFw==}
+ engines: {node: '>= 0.4'}
+
+ es-module-lexer@1.7.0:
+ resolution: {integrity: sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==}
+
+ es-object-atoms@1.0.0:
+ resolution: {integrity: sha512-MZ4iQ6JwHOBQjahnjwaC1ZtIBH+2ohjamzAO3oaHcXYup7qxjF2fixyH+Q71voWHeOkI2q/TnJao/KfXYIZWbw==}
+ engines: {node: '>= 0.4'}
+
+ es-object-atoms@1.1.1:
+ resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==}
+ engines: {node: '>= 0.4'}
+
+ es-set-tostringtag@2.0.3:
+ resolution: {integrity: sha512-3T8uNMC3OQTHkFUsFq8r/BwAXLHvU/9O9mE0fBc/MY5iq/8H7ncvO947LmYA6ldWw9Uh8Yhf25zu6n7nML5QWQ==}
+ engines: {node: '>= 0.4'}
+
+ es-set-tostringtag@2.1.0:
+ resolution: {integrity: sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==}
+ engines: {node: '>= 0.4'}
+
+ es-shim-unscopables@1.0.2:
+ resolution: {integrity: sha512-J3yBRXCzDu4ULnQwxyToo/OjdMx6akgVC7K6few0a7F/0wLtmKKN7I73AH5T2836UuXRqN7Qg+IIUw/+YJksRw==}
+
+ es-to-primitive@1.2.1:
+ resolution: {integrity: sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==}
+ engines: {node: '>= 0.4'}
+
+ esbuild@0.25.2:
+ resolution: {integrity: sha512-16854zccKPnC+toMywC+uKNeYSv+/eXkevRAfwRD/G9Cleq66m8XFIrigkbvauLLlCfDL45Q2cWegSg53gGBnQ==}
+ engines: {node: '>=18'}
+ hasBin: true
+
+ escalade@3.2.0:
+ resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==}
+ engines: {node: '>=6'}
+
+ escape-string-regexp@1.0.5:
+ resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==}
+ engines: {node: '>=0.8.0'}
+
+ escape-string-regexp@4.0.0:
+ resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==}
+ engines: {node: '>=10'}
+
+ eslint-config-next@14.2.18:
+ resolution: {integrity: sha512-SuDRcpJY5VHBkhz5DijJ4iA4bVnBA0n48Rb+YSJSCDr+h7kKAcb1mZHusLbW+WA8LDB6edSolomXA55eG3eOVA==}
+ peerDependencies:
+ eslint: ^7.23.0 || ^8.0.0
+ typescript: '>=3.3.1'
+ peerDependenciesMeta:
+ typescript:
+ optional: true
+
+ eslint-config-prettier@9.1.0:
+ resolution: {integrity: sha512-NSWl5BFQWEPi1j4TjVNItzYV7dZXZ+wP6I6ZhrBGpChQhZRUaElihE9uRRkcbRnNb76UMKDF3r+WTmNcGPKsqw==}
+ hasBin: true
+ peerDependencies:
+ eslint: '>=7.0.0'
+
+ eslint-config-turbo@2.1.0:
+ resolution: {integrity: sha512-3SeE2OCWnkA/84adGJXABm++966LNGxRdXtXKBcplJdIe4PmERkov1z6Kzp2PrPKT13wGu/bwoLV5h1rm7v9ug==}
+ peerDependencies:
+ eslint: '>6.6.0'
+
+ eslint-import-resolver-node@0.3.9:
+ resolution: {integrity: sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==}
+
+ eslint-import-resolver-typescript@3.6.3:
+ resolution: {integrity: sha512-ud9aw4szY9cCT1EWWdGv1L1XR6hh2PaRWif0j2QjQ0pgTY/69iw+W0Z4qZv5wHahOl8isEr+k/JnyAqNQkLkIA==}
+ engines: {node: ^14.18.0 || >=16.0.0}
+ peerDependencies:
+ eslint: '*'
+ eslint-plugin-import: '*'
+ eslint-plugin-import-x: '*'
+ peerDependenciesMeta:
+ eslint-plugin-import:
+ optional: true
+ eslint-plugin-import-x:
+ optional: true
+
+ eslint-module-utils@2.8.2:
+ resolution: {integrity: sha512-3XnC5fDyc8M4J2E8pt8pmSVRX2M+5yWMCfI/kDZwauQeFgzQOuhcRBFKjTeJagqgk4sFKxe1mvNVnaWwImx/Tg==}
+ engines: {node: '>=4'}
+ peerDependencies:
+ '@typescript-eslint/parser': '*'
+ eslint: '*'
+ eslint-import-resolver-node: '*'
+ eslint-import-resolver-typescript: '*'
+ eslint-import-resolver-webpack: '*'
+ peerDependenciesMeta:
+ '@typescript-eslint/parser':
+ optional: true
+ eslint:
+ optional: true
+ eslint-import-resolver-node:
+ optional: true
+ eslint-import-resolver-typescript:
+ optional: true
+ eslint-import-resolver-webpack:
+ optional: true
+
+ eslint-plugin-import@2.29.1:
+ resolution: {integrity: sha512-BbPC0cuExzhiMo4Ff1BTVwHpjjv28C5R+btTOGaCRC7UEz801up0JadwkeSk5Ued6TG34uaczuVuH6qyy5YUxw==}
+ engines: {node: '>=4'}
+ peerDependencies:
+ '@typescript-eslint/parser': '*'
+ eslint: ^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8
+ peerDependenciesMeta:
+ '@typescript-eslint/parser':
+ optional: true
+
+ eslint-plugin-jsx-a11y@6.9.0:
+ resolution: {integrity: sha512-nOFOCaJG2pYqORjK19lqPqxMO/JpvdCZdPtNdxY3kvom3jTvkAbOvQvD8wuD0G8BYR0IGAGYDlzqWJOh/ybn2g==}
+ engines: {node: '>=4.0'}
+ peerDependencies:
+ eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8
+
+ eslint-plugin-react-hooks@4.6.2:
+ resolution: {integrity: sha512-QzliNJq4GinDBcD8gPB5v0wh6g8q3SUi6EFF0x8N/BL9PoVs0atuGc47ozMRyOWAKdwaZ5OnbOEa3WR+dSGKuQ==}
+ engines: {node: '>=10'}
+ peerDependencies:
+ eslint: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0
+
+ eslint-plugin-react@7.35.0:
+ resolution: {integrity: sha512-v501SSMOWv8gerHkk+IIQBkcGRGrO2nfybfj5pLxuJNFTPxxA3PSryhXTK+9pNbtkggheDdsC0E9Q8CuPk6JKA==}
+ engines: {node: '>=4'}
+ peerDependencies:
+ eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9.7
+
+ eslint-plugin-turbo@2.1.0:
+ resolution: {integrity: sha512-+CWVY29y7Qa+gvrKSzP+TOYrHAlNLCh/97K5VtDdnpH54h/JFmnd3U0aSG6WANe0HgAK8NHQfeWFDdRzfDqbKA==}
+ peerDependencies:
+ eslint: '>6.6.0'
+
+ eslint-scope@5.1.1:
+ resolution: {integrity: sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==}
+ engines: {node: '>=8.0.0'}
+
+ eslint-scope@7.2.2:
+ resolution: {integrity: sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==}
+ engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
+
+ eslint-visitor-keys@2.1.0:
+ resolution: {integrity: sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==}
+ engines: {node: '>=10'}
+
+ eslint-visitor-keys@3.4.3:
+ resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==}
+ engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
+
+ eslint-visitor-keys@4.2.0:
+ resolution: {integrity: sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
+ eslint@8.57.1:
+ resolution: {integrity: sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA==}
+ engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
+ deprecated: This version is no longer supported. Please see https://eslint.org/version-support for other options.
+ hasBin: true
+
+ espree@9.6.1:
+ resolution: {integrity: sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==}
+ engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
+
+ esprima@4.0.1:
+ resolution: {integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==}
+ engines: {node: '>=4'}
+ hasBin: true
+
+ esquery@1.6.0:
+ resolution: {integrity: sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==}
+ engines: {node: '>=0.10'}
+
+ esrecurse@4.3.0:
+ resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==}
+ engines: {node: '>=4.0'}
+
+ estraverse@4.3.0:
+ resolution: {integrity: sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==}
+ engines: {node: '>=4.0'}
+
+ estraverse@5.3.0:
+ resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==}
+ engines: {node: '>=4.0'}
+
+ estree-walker@3.0.3:
+ resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==}
+
+ esutils@2.0.3:
+ resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==}
+ engines: {node: '>=0.10.0'}
+
+ event-stream@3.3.4:
+ resolution: {integrity: sha512-QHpkERcGsR0T7Qm3HNJSyXKEEj8AHNxkY3PK8TS2KJvQ7NiSHe3DDpwVKKtoYprL/AreyzFBeIkBIWChAqn60g==}
+
+ eventemitter2@6.4.7:
+ resolution: {integrity: sha512-tYUSVOGeQPKt/eC1ABfhHy5Xd96N3oIijJvN3O9+TsC28T5V9yX9oEfEK5faP0EFSNVOG97qtAS68GBrQB2hDg==}
+
+ eventemitter3@5.0.1:
+ resolution: {integrity: sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==}
+
+ execa@4.1.0:
+ resolution: {integrity: sha512-j5W0//W7f8UxAn8hXVnwG8tLwdiUy4FJLcSupCg6maBYZDpyBvTApK7KyuI4bKj8KOh1r2YH+6ucuYtJv1bTZA==}
+ engines: {node: '>=10'}
+
+ execa@5.1.1:
+ resolution: {integrity: sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==}
+ engines: {node: '>=10'}
+
+ execa@8.0.1:
+ resolution: {integrity: sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==}
+ engines: {node: '>=16.17'}
+
+ executable@4.1.1:
+ resolution: {integrity: sha512-8iA79xD3uAch729dUG8xaaBBFGaEa0wdD2VkYLFHwlqosEj/jT66AzcreRDSgV7ehnNLBW2WR5jIXwGKjVdTLg==}
+ engines: {node: '>=4'}
+
+ expect-type@1.2.1:
+ resolution: {integrity: sha512-/kP8CAwxzLVEeFrMm4kMmy4CCDlpipyA7MYLVrdJIkV0fYF0UaigQHRsxHiuY/GEea+bh4KSv3TIlgr+2UL6bw==}
+ engines: {node: '>=12.0.0'}
+
+ extend@3.0.2:
+ resolution: {integrity: sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==}
+
+ extendable-error@0.1.7:
+ resolution: {integrity: sha512-UOiS2in6/Q0FK0R0q6UY9vYpQ21mr/Qn1KOnte7vsACuNJf514WvCCUHSRCPcgjPT2bAhNIJdlE6bVap1GKmeg==}
+
+ external-editor@3.1.0:
+ resolution: {integrity: sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==}
+ engines: {node: '>=4'}
+
+ extract-zip@2.0.1:
+ resolution: {integrity: sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==}
+ engines: {node: '>= 10.17.0'}
+ hasBin: true
+
+ extsprintf@1.3.0:
+ resolution: {integrity: sha512-11Ndz7Nv+mvAC1j0ktTa7fAb0vLyGGX+rMHNBYQviQDGU0Hw7lhctJANqbPhu9nV9/izT/IntTgZ7Im/9LJs9g==}
+ engines: {'0': node >=0.6.0}
+
+ fast-deep-equal@3.1.3:
+ resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==}
+
+ fast-glob@3.3.2:
+ resolution: {integrity: sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==}
+ engines: {node: '>=8.6.0'}
+
+ fast-glob@3.3.3:
+ resolution: {integrity: sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==}
+ engines: {node: '>=8.6.0'}
+
+ fast-json-stable-stringify@2.1.0:
+ resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==}
+
+ fast-levenshtein@2.0.6:
+ resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==}
+
+ fastq@1.17.1:
+ resolution: {integrity: sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==}
+
+ fd-slicer@1.1.0:
+ resolution: {integrity: sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==}
+
+ fdir@6.4.4:
+ resolution: {integrity: sha512-1NZP+GK4GfuAv3PqKvxQRDMjdSRZjnkq7KfhlNrCNNlZ0ygQFpebfrnfnq/W7fpUnAv9aGWmY1zKx7FYL3gwhg==}
+ peerDependencies:
+ picomatch: ^3 || ^4
+ peerDependenciesMeta:
+ picomatch:
+ optional: true
+
+ fetch-blob@3.2.0:
+ resolution: {integrity: sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==}
+ engines: {node: ^12.20 || >= 14.13}
+
+ fflate@0.8.2:
+ resolution: {integrity: sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==}
+
+ figures@3.2.0:
+ resolution: {integrity: sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==}
+ engines: {node: '>=8'}
+
+ file-entry-cache@6.0.1:
+ resolution: {integrity: sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==}
+ engines: {node: ^10.12.0 || >=12.0.0}
+
+ fill-range@7.1.1:
+ resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==}
+ engines: {node: '>=8'}
+
+ find-up@4.1.0:
+ resolution: {integrity: sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==}
+ engines: {node: '>=8'}
+
+ find-up@5.0.0:
+ resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==}
+ engines: {node: '>=10'}
+
+ flat-cache@3.2.0:
+ resolution: {integrity: sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==}
+ engines: {node: ^10.12.0 || >=12.0.0}
+
+ flatted@3.3.1:
+ resolution: {integrity: sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==}
+
+ follow-redirects@1.15.9:
+ resolution: {integrity: sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==}
+ engines: {node: '>=4.0'}
+ peerDependencies:
+ debug: '*'
+ peerDependenciesMeta:
+ debug:
+ optional: true
+
+ for-each@0.3.3:
+ resolution: {integrity: sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==}
+
+ foreground-child@3.3.0:
+ resolution: {integrity: sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==}
+ engines: {node: '>=14'}
+
+ foreground-child@3.3.1:
+ resolution: {integrity: sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==}
+ engines: {node: '>=14'}
+
+ forever-agent@0.6.1:
+ resolution: {integrity: sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw==}
+
+ form-data@4.0.2:
+ resolution: {integrity: sha512-hGfm/slu0ZabnNt4oaRZ6uREyfCj6P4fT/n6A1rGV+Z0VdGXjfOhVUpkn6qVQONHGIFwmveGXyDs75+nr6FM8w==}
+ engines: {node: '>= 6'}
+
+ formdata-polyfill@4.0.10:
+ resolution: {integrity: sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==}
+ engines: {node: '>=12.20.0'}
+
+ fraction.js@4.3.7:
+ resolution: {integrity: sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==}
+
+ from@0.1.7:
+ resolution: {integrity: sha512-twe20eF1OxVxp/ML/kq2p1uc6KvFK/+vs8WjEbeKmV2He22MKm7YF2ANIt+EOqhJ5L3K/SuuPhk0hWQDjOM23g==}
+
+ fs-extra@7.0.1:
+ resolution: {integrity: sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==}
+ engines: {node: '>=6 <7 || >=8'}
+
+ fs-extra@8.1.0:
+ resolution: {integrity: sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==}
+ engines: {node: '>=6 <7 || >=8'}
+
+ fs-extra@9.1.0:
+ resolution: {integrity: sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==}
+ engines: {node: '>=10'}
+
+ fs-minipass@2.1.0:
+ resolution: {integrity: sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==}
+ engines: {node: '>= 8'}
+
+ fs.realpath@1.0.0:
+ resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==}
+
+ fsevents@2.3.2:
+ resolution: {integrity: sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==}
+ engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
+ os: [darwin]
+
+ fsevents@2.3.3:
+ resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==}
+ engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
+ os: [darwin]
+
+ function-bind@1.1.2:
+ resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==}
+
+ function.prototype.name@1.1.6:
+ resolution: {integrity: sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg==}
+ engines: {node: '>= 0.4'}
+
+ functions-have-names@1.2.3:
+ resolution: {integrity: sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==}
+
+ gauge@3.0.2:
+ resolution: {integrity: sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q==}
+ engines: {node: '>=10'}
+ deprecated: This package is no longer supported.
+
+ gaxios@7.1.0:
+ resolution: {integrity: sha512-y1Q0MX1Ba6eg67Zz92kW0MHHhdtWksYckQy1KJsI6P4UlDQ8cvdvpLEPslD/k7vFkdPppMESFGTvk7XpSiKj8g==}
+ engines: {node: '>=18'}
+
+ gensync@1.0.0-beta.2:
+ resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==}
+ engines: {node: '>=6.9.0'}
+
+ get-caller-file@2.0.5:
+ resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==}
+ engines: {node: 6.* || 8.* || >= 10.*}
+
+ get-east-asian-width@1.3.0:
+ resolution: {integrity: sha512-vpeMIQKxczTD/0s2CdEWHcb0eeJe6TFjxb+J5xgX7hScxqrGuyjmv4c1D4A/gelKfyox0gJJwIHF+fLjeaM8kQ==}
+ engines: {node: '>=18'}
+
+ get-intrinsic@1.2.4:
+ resolution: {integrity: sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==}
+ engines: {node: '>= 0.4'}
+
+ get-intrinsic@1.3.0:
+ resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==}
+ engines: {node: '>= 0.4'}
+
+ get-proto@1.0.1:
+ resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==}
+ engines: {node: '>= 0.4'}
+
+ get-stream@5.2.0:
+ resolution: {integrity: sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==}
+ engines: {node: '>=8'}
+
+ get-stream@6.0.1:
+ resolution: {integrity: sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==}
+ engines: {node: '>=10'}
+
+ get-stream@8.0.1:
+ resolution: {integrity: sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==}
+ engines: {node: '>=16'}
+
+ get-symbol-description@1.0.2:
+ resolution: {integrity: sha512-g0QYk1dZBxGwk+Ngc+ltRH2IBp2f7zBkBMBJZCDerh6EhlhSR6+9irMCuT/09zD6qkarHUSn529sK/yL4S27mg==}
+ engines: {node: '>= 0.4'}
+
+ get-tsconfig@4.8.0:
+ resolution: {integrity: sha512-Pgba6TExTZ0FJAn1qkJAjIeKoDJ3CsI2ChuLohJnZl/tTU8MVrq3b+2t5UOPfRa4RMsorClBjJALkJUMjG1PAw==}
+
+ getos@3.2.1:
+ resolution: {integrity: sha512-U56CfOK17OKgTVqozZjUKNdkfEv6jk5WISBJ8SHoagjE6L69zOwl3Z+O8myjY9MEW3i2HPWQBt/LTbCgcC973Q==}
+
+ getpass@0.1.7:
+ resolution: {integrity: sha512-0fzj9JxOLfJ+XGLhR8ze3unN0KZCgZwiSSDz168VERjK8Wl8kVSdcu2kspd4s4wtAa1y/qrVRiAA0WclVsu0ng==}
+
+ glob-parent@5.1.2:
+ resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==}
+ engines: {node: '>= 6'}
+
+ glob-parent@6.0.2:
+ resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==}
+ engines: {node: '>=10.13.0'}
+
+ glob@10.3.10:
+ resolution: {integrity: sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g==}
+ engines: {node: '>=16 || 14 >=14.17'}
+ hasBin: true
+
+ glob@10.4.5:
+ resolution: {integrity: sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==}
+ hasBin: true
+
+ glob@7.2.3:
+ resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==}
+ deprecated: Glob versions prior to v9 are no longer supported
+
+ global-dirs@3.0.1:
+ resolution: {integrity: sha512-NBcGGFbBA9s1VzD41QXDG+3++t9Mn5t1FpLdhESY6oKY4gYTFpX4wO3sqGUa0Srjtbfj3szX0RnemmrVRUdULA==}
+ engines: {node: '>=10'}
+
+ globals@11.12.0:
+ resolution: {integrity: sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==}
+ engines: {node: '>=4'}
+
+ globals@13.24.0:
+ resolution: {integrity: sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==}
+ engines: {node: '>=8'}
+
+ globalthis@1.0.4:
+ resolution: {integrity: sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==}
+ engines: {node: '>= 0.4'}
+
+ globby@11.1.0:
+ resolution: {integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==}
+ engines: {node: '>=10'}
+
+ globrex@0.1.2:
+ resolution: {integrity: sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg==}
+
+ gopd@1.0.1:
+ resolution: {integrity: sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==}
+
+ gopd@1.2.0:
+ resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==}
+ engines: {node: '>= 0.4'}
+
+ graceful-fs@4.2.11:
+ resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==}
+
+ graphemer@1.4.0:
+ resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==}
+
+ grpc-tools@1.13.0:
+ resolution: {integrity: sha512-7CbkJ1yWPfX0nHjbYG58BQThNhbICXBZynzCUxCb3LzX5X9B3hQbRY2STiRgIEiLILlK9fgl0z0QVGwPCdXf5g==}
+ hasBin: true
+
+ has-bigints@1.0.2:
+ resolution: {integrity: sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==}
+
+ has-flag@3.0.0:
+ resolution: {integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==}
+ engines: {node: '>=4'}
+
+ has-flag@4.0.0:
+ resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==}
+ engines: {node: '>=8'}
+
+ has-property-descriptors@1.0.2:
+ resolution: {integrity: sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==}
+
+ has-proto@1.0.3:
+ resolution: {integrity: sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==}
+ engines: {node: '>= 0.4'}
+
+ has-symbols@1.0.3:
+ resolution: {integrity: sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==}
+ engines: {node: '>= 0.4'}
+
+ has-symbols@1.1.0:
+ resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==}
+ engines: {node: '>= 0.4'}
+
+ has-tostringtag@1.0.2:
+ resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==}
+ engines: {node: '>= 0.4'}
+
+ has-unicode@2.0.1:
+ resolution: {integrity: sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==}
+
+ hasown@2.0.2:
+ resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==}
+ engines: {node: '>= 0.4'}
+
+ html-encoding-sniffer@4.0.0:
+ resolution: {integrity: sha512-Y22oTqIU4uuPgEemfz7NDJz6OeKf12Lsu+QC+s3BVpda64lTiMYCyGwg5ki4vFxkMwQdeZDl2adZoqUgdFuTgQ==}
+ engines: {node: '>=18'}
+
+ http-proxy-agent@7.0.2:
+ resolution: {integrity: sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==}
+ engines: {node: '>= 14'}
+
+ http-signature@1.4.0:
+ resolution: {integrity: sha512-G5akfn7eKbpDN+8nPS/cb57YeA1jLTVxjpCj7tmm3QKPdyDy7T+qSC40e9ptydSWvkwjSXw1VbkpyEm39ukeAg==}
+ engines: {node: '>=0.10'}
+
+ https-proxy-agent@5.0.1:
+ resolution: {integrity: sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==}
+ engines: {node: '>= 6'}
+
+ https-proxy-agent@7.0.6:
+ resolution: {integrity: sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==}
+ engines: {node: '>= 14'}
+
+ human-id@4.1.1:
+ resolution: {integrity: sha512-3gKm/gCSUipeLsRYZbbdA1BD83lBoWUkZ7G9VFrhWPAU76KwYo5KR8V28bpoPm/ygy0x5/GCbpRQdY7VLYCoIg==}
+ hasBin: true
+
+ human-signals@1.1.1:
+ resolution: {integrity: sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw==}
+ engines: {node: '>=8.12.0'}
+
+ human-signals@2.1.0:
+ resolution: {integrity: sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==}
+ engines: {node: '>=10.17.0'}
+
+ human-signals@5.0.0:
+ resolution: {integrity: sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==}
+ engines: {node: '>=16.17.0'}
+
+ iconv-lite@0.4.24:
+ resolution: {integrity: sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==}
+ engines: {node: '>=0.10.0'}
+
+ iconv-lite@0.6.3:
+ resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==}
+ engines: {node: '>=0.10.0'}
+
+ ieee754@1.2.1:
+ resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==}
+
+ ignore-by-default@1.0.1:
+ resolution: {integrity: sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==}
+
+ ignore@5.3.2:
+ resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==}
+ engines: {node: '>= 4'}
+
+ immutable@5.1.1:
+ resolution: {integrity: sha512-3jatXi9ObIsPGr3N5hGw/vWWcTkq6hUYhpQz4k0wLC+owqWi/LiugIw9x0EdNZ2yGedKN/HzePiBvaJRXa0Ujg==}
+
+ import-fresh@3.3.0:
+ resolution: {integrity: sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==}
+ engines: {node: '>=6'}
+
+ imurmurhash@0.1.4:
+ resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==}
+ engines: {node: '>=0.8.19'}
+
+ indent-string@4.0.0:
+ resolution: {integrity: sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==}
+ engines: {node: '>=8'}
+
+ inflight@1.0.6:
+ resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==}
+ deprecated: This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.
+
+ inherits@2.0.4:
+ resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==}
+
+ ini@2.0.0:
+ resolution: {integrity: sha512-7PnF4oN3CvZF23ADhA5wRaYEQpJ8qygSkbtTXWBeXWXmEVRXK+1ITciHWwHhsjv1TmW0MgacIv6hEi5pX5NQdA==}
+ engines: {node: '>=10'}
+
+ internal-slot@1.0.7:
+ resolution: {integrity: sha512-NGnrKwXzSms2qUUih/ILZ5JBqNTSa1+ZmP6flaIp6KmSElgE9qdndzS3cqjrDovwFdmwsGsLdeFgB6suw+1e9g==}
+ engines: {node: '>= 0.4'}
+
+ intl-messageformat@10.7.7:
+ resolution: {integrity: sha512-F134jIoeYMro/3I0h08D0Yt4N9o9pjddU/4IIxMMURqbAtI2wu70X8hvG1V48W49zXHXv3RKSF/po+0fDfsGjA==}
+
+ is-arguments@1.1.1:
+ resolution: {integrity: sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==}
+ engines: {node: '>= 0.4'}
+
+ is-array-buffer@3.0.4:
+ resolution: {integrity: sha512-wcjaerHw0ydZwfhiKbXJWLDY8A7yV7KhjQOpb83hGgGfId/aQa4TOvwyzn2PuswW2gPCYEL/nEAiSVpdOj1lXw==}
+ engines: {node: '>= 0.4'}
+
+ is-arrayish@0.3.2:
+ resolution: {integrity: sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==}
+
+ is-async-function@2.0.0:
+ resolution: {integrity: sha512-Y1JXKrfykRJGdlDwdKlLpLyMIiWqWvuSd17TvZk68PLAOGOoF4Xyav1z0Xhoi+gCYjZVeC5SI+hYFOfvXmGRCA==}
+ engines: {node: '>= 0.4'}
+
+ is-bigint@1.0.4:
+ resolution: {integrity: sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==}
+
+ is-binary-path@2.1.0:
+ resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==}
+ engines: {node: '>=8'}
+
+ is-boolean-object@1.1.2:
+ resolution: {integrity: sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==}
+ engines: {node: '>= 0.4'}
+
+ is-bun-module@1.1.0:
+ resolution: {integrity: sha512-4mTAVPlrXpaN3jtF0lsnPCMGnq4+qZjVIKq0HCpfcqf8OC1SM5oATCIAPM5V5FN05qp2NNnFndphmdZS9CV3hA==}
+
+ is-callable@1.2.7:
+ resolution: {integrity: sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==}
+ engines: {node: '>= 0.4'}
+
+ is-core-module@2.15.1:
+ resolution: {integrity: sha512-z0vtXSwucUJtANQWldhbtbt7BnL0vxiFjIdDLAatwhDYty2bad6s+rijD6Ri4YuYJubLzIJLUidCh09e1djEVQ==}
+ engines: {node: '>= 0.4'}
+
+ is-data-view@1.0.1:
+ resolution: {integrity: sha512-AHkaJrsUVW6wq6JS8y3JnM/GJF/9cf+k20+iDzlSaJrinEo5+7vRiteOSwBhHRiAyQATN1AmY4hwzxJKPmYf+w==}
+ engines: {node: '>= 0.4'}
+
+ is-date-object@1.0.5:
+ resolution: {integrity: sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==}
+ engines: {node: '>= 0.4'}
+
+ is-extglob@2.1.1:
+ resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==}
+ engines: {node: '>=0.10.0'}
+
+ is-finalizationregistry@1.0.2:
+ resolution: {integrity: sha512-0by5vtUJs8iFQb5TYUHHPudOR+qXYIMKtiUzvLIZITZUjknFmziyBJuLhVRc+Ds0dREFlskDNJKYIdIzu/9pfw==}
+
+ is-fullwidth-code-point@3.0.0:
+ resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==}
+ engines: {node: '>=8'}
+
+ is-fullwidth-code-point@4.0.0:
+ resolution: {integrity: sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ==}
+ engines: {node: '>=12'}
+
+ is-fullwidth-code-point@5.0.0:
+ resolution: {integrity: sha512-OVa3u9kkBbw7b8Xw5F9P+D/T9X+Z4+JruYVNapTjPYZYUznQ5YfWeFkOj606XYYW8yugTfC8Pj0hYqvi4ryAhA==}
+ engines: {node: '>=18'}
+
+ is-generator-function@1.0.10:
+ resolution: {integrity: sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==}
+ engines: {node: '>= 0.4'}
+
+ is-glob@4.0.3:
+ resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==}
+ engines: {node: '>=0.10.0'}
+
+ is-installed-globally@0.4.0:
+ resolution: {integrity: sha512-iwGqO3J21aaSkC7jWnHP/difazwS7SFeIqxv6wEtLU8Y5KlzFTjyqcSIT0d8s4+dDhKytsk9PJZ2BkS5eZwQRQ==}
+ engines: {node: '>=10'}
+
+ is-map@2.0.3:
+ resolution: {integrity: sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==}
+ engines: {node: '>= 0.4'}
+
+ is-negative-zero@2.0.3:
+ resolution: {integrity: sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==}
+ engines: {node: '>= 0.4'}
+
+ is-number-object@1.0.7:
+ resolution: {integrity: sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==}
+ engines: {node: '>= 0.4'}
+
+ is-number@7.0.0:
+ resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==}
+ engines: {node: '>=0.12.0'}
+
+ is-path-inside@3.0.3:
+ resolution: {integrity: sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==}
+ engines: {node: '>=8'}
+
+ is-potential-custom-element-name@1.0.1:
+ resolution: {integrity: sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==}
+
+ is-regex@1.1.4:
+ resolution: {integrity: sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==}
+ engines: {node: '>= 0.4'}
+
+ is-set@2.0.3:
+ resolution: {integrity: sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==}
+ engines: {node: '>= 0.4'}
+
+ is-shared-array-buffer@1.0.3:
+ resolution: {integrity: sha512-nA2hv5XIhLR3uVzDDfCIknerhx8XUKnstuOERPNNIinXG7v9u+ohXF67vxm4TPTEPU6lm61ZkwP3c9PCB97rhg==}
+ engines: {node: '>= 0.4'}
+
+ is-stream@2.0.1:
+ resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==}
+ engines: {node: '>=8'}
+
+ is-stream@3.0.0:
+ resolution: {integrity: sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==}
+ engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
+
+ is-string@1.0.7:
+ resolution: {integrity: sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==}
+ engines: {node: '>= 0.4'}
+
+ is-subdir@1.2.0:
+ resolution: {integrity: sha512-2AT6j+gXe/1ueqbW6fLZJiIw3F8iXGJtt0yDrZaBhAZEG1raiTxKWU+IPqMCzQAXOUCKdA4UDMgacKH25XG2Cw==}
+ engines: {node: '>=4'}
+
+ is-symbol@1.0.4:
+ resolution: {integrity: sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==}
+ engines: {node: '>= 0.4'}
+
+ is-typed-array@1.1.13:
+ resolution: {integrity: sha512-uZ25/bUAlUY5fR4OKT4rZQEBrzQWYV9ZJYGGsUmEJ6thodVJ1HX64ePQ6Z0qPWP+m+Uq6e9UugrE38jeYsDSMw==}
+ engines: {node: '>= 0.4'}
+
+ is-typedarray@1.0.0:
+ resolution: {integrity: sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==}
+
+ is-unicode-supported@0.1.0:
+ resolution: {integrity: sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==}
+ engines: {node: '>=10'}
+
+ is-weakmap@2.0.2:
+ resolution: {integrity: sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==}
+ engines: {node: '>= 0.4'}
+
+ is-weakref@1.0.2:
+ resolution: {integrity: sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==}
+
+ is-weakset@2.0.3:
+ resolution: {integrity: sha512-LvIm3/KWzS9oRFHugab7d+M/GcBXuXX5xZkzPmN+NxihdQlZUQ4dWuSV1xR/sq6upL1TJEDrfBgRepHFdBtSNQ==}
+ engines: {node: '>= 0.4'}
+
+ is-windows@1.0.2:
+ resolution: {integrity: sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==}
+ engines: {node: '>=0.10.0'}
+
+ isarray@2.0.5:
+ resolution: {integrity: sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==}
+
+ isexe@2.0.0:
+ resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==}
+
+ isstream@0.1.2:
+ resolution: {integrity: sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g==}
+
+ iterator.prototype@1.1.2:
+ resolution: {integrity: sha512-DR33HMMr8EzwuRL8Y9D3u2BMj8+RqSE850jfGu59kS7tbmPLzGkZmVSfyCFSDxuZiEY6Rzt3T2NA/qU+NwVj1w==}
+
+ jackspeak@2.3.6:
+ resolution: {integrity: sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ==}
+ engines: {node: '>=14'}
+
+ jackspeak@3.4.3:
+ resolution: {integrity: sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==}
+
+ jiti@1.21.6:
+ resolution: {integrity: sha512-2yTgeWTWzMWkHu6Jp9NKgePDaYHbntiwvYuuJLbbN9vl7DC9DvXKOB2BC3ZZ92D3cvV/aflH0osDfwpHepQ53w==}
+ hasBin: true
+
+ joi@17.13.3:
+ resolution: {integrity: sha512-otDA4ldcIx+ZXsKHWmp0YizCweVRZG96J10b0FevjfuncLO1oX59THoAmHkNubYJ+9gWsYsp5k8v4ib6oDv1fA==}
+
+ jose@5.8.0:
+ resolution: {integrity: sha512-E7CqYpL/t7MMnfGnK/eg416OsFCVUrU/Y3Vwe7QjKhu/BkS1Ms455+2xsqZQVN57/U2MHMBvEb5SrmAZWAIntA==}
+
+ joycon@3.1.1:
+ resolution: {integrity: sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw==}
+ engines: {node: '>=10'}
+
+ js-tokens@4.0.0:
+ resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==}
+
+ js-yaml@3.14.1:
+ resolution: {integrity: sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==}
+ hasBin: true
+
+ js-yaml@4.1.0:
+ resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==}
+ hasBin: true
+
+ jsbn@0.1.1:
+ resolution: {integrity: sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==}
+
+ jsdom@26.1.0:
+ resolution: {integrity: sha512-Cvc9WUhxSMEo4McES3P7oK3QaXldCfNWp7pl2NNeiIFlCoLr3kfq9kb1fxftiwk1FLV7CvpvDfonxtzUDeSOPg==}
+ engines: {node: '>=18'}
+ peerDependencies:
+ canvas: ^3.0.0
+ peerDependenciesMeta:
+ canvas:
+ optional: true
+
+ jsesc@3.1.0:
+ resolution: {integrity: sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==}
+ engines: {node: '>=6'}
+ hasBin: true
+
+ json-buffer@3.0.1:
+ resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==}
+
+ json-schema-traverse@0.4.1:
+ resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==}
+
+ json-schema@0.4.0:
+ resolution: {integrity: sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==}
+
+ json-stable-stringify-without-jsonify@1.0.1:
+ resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==}
+
+ json-stringify-safe@5.0.1:
+ resolution: {integrity: sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==}
+
+ json5@1.0.2:
+ resolution: {integrity: sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==}
+ hasBin: true
+
+ json5@2.2.3:
+ resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==}
+ engines: {node: '>=6'}
+ hasBin: true
+
+ jsonfile@4.0.0:
+ resolution: {integrity: sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==}
+
+ jsonfile@6.1.0:
+ resolution: {integrity: sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==}
+
+ jsprim@2.0.2:
+ resolution: {integrity: sha512-gqXddjPqQ6G40VdnI6T6yObEC+pDNvyP95wdQhkWkg7crHH3km5qP1FsOXEkzEQwnz6gz5qGTn1c2Y52wP3OyQ==}
+ engines: {'0': node >=0.6.0}
+
+ jsx-ast-utils@3.3.5:
+ resolution: {integrity: sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==}
+ engines: {node: '>=4.0'}
+
+ keyv@4.5.4:
+ resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==}
+
+ language-subtag-registry@0.3.23:
+ resolution: {integrity: sha512-0K65Lea881pHotoGEa5gDlMxt3pctLi2RplBb7Ezh4rRdLEOtgi7n4EwK9lamnUCkKBqaeKRVebTq6BAxSkpXQ==}
+
+ language-tags@1.0.9:
+ resolution: {integrity: sha512-MbjN408fEndfiQXbFQ1vnd+1NoLDsnQW41410oQBXiyXDMYH5z505juWa4KUE1LqxRC7DgOgZDbKLxHIwm27hA==}
+ engines: {node: '>=0.10'}
+
+ lazy-ass@1.6.0:
+ resolution: {integrity: sha512-cc8oEVoctTvsFZ/Oje/kGnHbpWHYBe8IAJe4C0QNc3t8uM/0Y8+erSz/7Y1ALuXTEZTMvxXwO6YbX1ey3ujiZw==}
+ engines: {node: '> 0.8'}
+
+ levn@0.4.1:
+ resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==}
+ engines: {node: '>= 0.8.0'}
+
+ lilconfig@2.1.0:
+ resolution: {integrity: sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==}
+ engines: {node: '>=10'}
+
+ lilconfig@3.1.3:
+ resolution: {integrity: sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==}
+ engines: {node: '>=14'}
+
+ lines-and-columns@1.2.4:
+ resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==}
+
+ lint-staged@15.5.1:
+ resolution: {integrity: sha512-6m7u8mue4Xn6wK6gZvSCQwBvMBR36xfY24nF5bMTf2MHDYG6S3yhJuOgdYVw99hsjyDt2d4z168b3naI8+NWtQ==}
+ engines: {node: '>=18.12.0'}
+ hasBin: true
+
+ listr2@3.14.0:
+ resolution: {integrity: sha512-TyWI8G99GX9GjE54cJ+RrNMcIFBfwMPxc3XTFiAYGN4s10hWROGtOg7+O6u6LE3mNkyld7RSLE6nrKBvTfcs3g==}
+ engines: {node: '>=10.0.0'}
+ peerDependencies:
+ enquirer: '>= 2.3.0 < 3'
+ peerDependenciesMeta:
+ enquirer:
+ optional: true
+
+ listr2@8.3.2:
+ resolution: {integrity: sha512-vsBzcU4oE+v0lj4FhVLzr9dBTv4/fHIa57l+GCwovP8MoFNZJTOhGU8PXd4v2VJCbECAaijBiHntiekFMLvo0g==}
+ engines: {node: '>=18.0.0'}
+
+ load-tsconfig@0.2.5:
+ resolution: {integrity: sha512-IXO6OCs9yg8tMKzfPZ1YmheJbZCiEsnBdcB03l0OcfK9prKnJb96siuHCr5Fl37/yo9DnKU+TLpxzTUspw9shg==}
+ engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
+
+ locate-path@5.0.0:
+ resolution: {integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==}
+ engines: {node: '>=8'}
+
+ locate-path@6.0.0:
+ resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==}
+ engines: {node: '>=10'}
+
+ lodash.camelcase@4.3.0:
+ resolution: {integrity: sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==}
+
+ lodash.merge@4.6.2:
+ resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==}
+
+ lodash.once@4.1.1:
+ resolution: {integrity: sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==}
+
+ lodash.sortby@4.7.0:
+ resolution: {integrity: sha512-HDWXG8isMntAyRF5vZ7xKuEvOhT4AhlRt/3czTSjvGUxjYCBVRQY48ViDHyfYz9VIoBkW4TMGQNapx+l3RUwdA==}
+
+ lodash.startcase@4.4.0:
+ resolution: {integrity: sha512-+WKqsK294HMSc2jEbNgpHpd0JfIBhp7rEV4aqXWqFr6AlXov+SlcgB1Fv01y2kGe3Gc8nMW7VA0SrGuSkRfIEg==}
+
+ lodash@4.17.21:
+ resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==}
+
+ log-symbols@4.1.0:
+ resolution: {integrity: sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==}
+ engines: {node: '>=10'}
+
+ log-update@4.0.0:
+ resolution: {integrity: sha512-9fkkDevMefjg0mmzWFBW8YkFP91OrizzkW3diF7CpG+S2EYdy4+TVfGwz1zeF8x7hCx1ovSPTOE9Ngib74qqUg==}
+ engines: {node: '>=10'}
+
+ log-update@6.1.0:
+ resolution: {integrity: sha512-9ie8ItPR6tjY5uYJh8K/Zrv/RMZ5VOlOWvtZdEHYSTFKZfIBPQa9tOAEeAWhd+AnIneLJ22w5fjOYtoutpWq5w==}
+ engines: {node: '>=18'}
+
+ long@5.2.3:
+ resolution: {integrity: sha512-lcHwpNoggQTObv5apGNCTdJrO69eHOZMi4BNC+rTLER8iHAqGrUVeLh/irVIM7zTw2bOXA8T6uNPeujwOLg/2Q==}
+
+ loose-envify@1.4.0:
+ resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==}
+ hasBin: true
+
+ loupe@3.1.3:
+ resolution: {integrity: sha512-kkIp7XSkP78ZxJEsSxW3712C6teJVoeHHwgo9zJ380de7IYyJ2ISlxojcH2pC5OFLewESmnRi/+XCDIEEVyoug==}
+
+ lru-cache@10.4.3:
+ resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==}
+
+ lru-cache@5.1.1:
+ resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==}
+
+ lucide-react@0.469.0:
+ resolution: {integrity: sha512-28vvUnnKQ/dBwiCQtwJw7QauYnE7yd2Cyp4tTTJpvglX4EMpbflcdBgrgToX2j71B3YvugK/NH3BGUk+E/p/Fw==}
+ peerDependencies:
+ react: ^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0
+
+ lz-string@1.5.0:
+ resolution: {integrity: sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==}
+ hasBin: true
+
+ magic-string@0.30.17:
+ resolution: {integrity: sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==}
+
+ make-dir-cli@4.0.0:
+ resolution: {integrity: sha512-9BBC2CaGH0hUAx+tQthgxqYypwkTs+7oXmPdiWyDpHGo4mGB3kdudUKQGivK59C1aJroo4QLlXF7Chu/kdhYiw==}
+ engines: {node: '>=18'}
+ hasBin: true
+
+ make-dir@3.1.0:
+ resolution: {integrity: sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==}
+ engines: {node: '>=8'}
+
+ make-dir@5.0.0:
+ resolution: {integrity: sha512-G0yBotnlWVonPClw+tq+xi4K7DZC9n96HjGTBDdHkstAVsDkfZhi1sTvZypXLpyQTbISBkDtK0E5XlUqDsShQg==}
+ engines: {node: '>=18'}
+
+ map-stream@0.1.0:
+ resolution: {integrity: sha512-CkYQrPYZfWnu/DAmVCpTSX/xHpKZ80eKh2lAkyA6AJTef6bW+6JpbQZN5rofum7da+SyN1bi5ctTm+lTfcCW3g==}
+
+ math-intrinsics@1.1.0:
+ resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==}
+ engines: {node: '>= 0.4'}
+
+ meow@13.2.0:
+ resolution: {integrity: sha512-pxQJQzB6djGPXh08dacEloMFopsOqGVRKFPYvPOt9XDZ1HasbgDZA74CJGreSU4G3Ak7EFJGoiH2auq+yXISgA==}
+ engines: {node: '>=18'}
+
+ merge-stream@2.0.0:
+ resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==}
+
+ merge2@1.4.1:
+ resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==}
+ engines: {node: '>= 8'}
+
+ micromatch@4.0.8:
+ resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==}
+ engines: {node: '>=8.6'}
+
+ mime-db@1.52.0:
+ resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==}
+ engines: {node: '>= 0.6'}
+
+ mime-types@2.1.35:
+ resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==}
+ engines: {node: '>= 0.6'}
+
+ mimic-fn@2.1.0:
+ resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==}
+ engines: {node: '>=6'}
+
+ mimic-fn@4.0.0:
+ resolution: {integrity: sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==}
+ engines: {node: '>=12'}
+
+ mimic-function@5.0.1:
+ resolution: {integrity: sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==}
+ engines: {node: '>=18'}
+
+ min-indent@1.0.1:
+ resolution: {integrity: sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==}
+ engines: {node: '>=4'}
+
+ mini-svg-data-uri@1.4.4:
+ resolution: {integrity: sha512-r9deDe9p5FJUPZAk3A59wGH7Ii9YrjjWw0jmw/liSbHl2CHiyXj6FcDXDu2K3TjVAXqiJdaw3xxwlZZr9E6nHg==}
+ hasBin: true
+
+ minimatch@3.1.2:
+ resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==}
+
+ minimatch@9.0.5:
+ resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==}
+ engines: {node: '>=16 || 14 >=14.17'}
+
+ minimist@1.2.8:
+ resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==}
+
+ minipass@3.3.6:
+ resolution: {integrity: sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==}
+ engines: {node: '>=8'}
+
+ minipass@5.0.0:
+ resolution: {integrity: sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==}
+ engines: {node: '>=8'}
+
+ minipass@7.1.2:
+ resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==}
+ engines: {node: '>=16 || 14 >=14.17'}
+
+ minizlib@2.1.2:
+ resolution: {integrity: sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==}
+ engines: {node: '>= 8'}
+
+ mkdirp@1.0.4:
+ resolution: {integrity: sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==}
+ engines: {node: '>=10'}
+ hasBin: true
+
+ moment@2.30.1:
+ resolution: {integrity: sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==}
+
+ mri@1.2.0:
+ resolution: {integrity: sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==}
+ engines: {node: '>=4'}
+
+ ms@2.1.3:
+ resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==}
+
+ mz@2.7.0:
+ resolution: {integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==}
+
+ nanoid@3.3.11:
+ resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==}
+ engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
+ hasBin: true
+
+ natural-compare@1.4.0:
+ resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==}
+
+ negotiator@1.0.0:
+ resolution: {integrity: sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==}
+ engines: {node: '>= 0.6'}
+
+ next-intl@3.26.5:
+ resolution: {integrity: sha512-EQlCIfY0jOhRldiFxwSXG+ImwkQtDEfQeSOEQp6ieAGSLWGlgjdb/Ck/O7wMfC430ZHGeUKVKax8KGusTPKCgg==}
+ peerDependencies:
+ next: ^10.0.0 || ^11.0.0 || ^12.0.0 || ^13.0.0 || ^14.0.0 || ^15.0.0
+ react: ^16.8.0 || ^17.0.0 || ^18.0.0 || >=19.0.0-rc <19.0.0 || ^19.0.0
+
+ next-themes@0.2.1:
+ resolution: {integrity: sha512-B+AKNfYNIzh0vqQQKqQItTS8evEouKD7H5Hj3kmuPERwddR2TxvDSFZuTj6T7Jfn1oyeUyJMydPl1Bkxkh0W7A==}
+ peerDependencies:
+ next: '*'
+ react: '*'
+ react-dom: '*'
+
+ next@15.4.0-canary.86:
+ resolution: {integrity: sha512-lGeO0sOvPZ7oFIklqRA863YzRL1bW+kT/OqU3N6RBquHldiucZwnZKQceZdn6WcHEFmWIHzZV+SMG1JEK7hZLg==}
+ engines: {node: ^18.18.0 || ^19.8.0 || >= 20.0.0}
+ hasBin: true
+ peerDependencies:
+ '@opentelemetry/api': ^1.1.0
+ '@playwright/test': ^1.51.1
+ babel-plugin-react-compiler: '*'
+ react: ^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0
+ react-dom: ^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0
+ sass: ^1.3.0
+ peerDependenciesMeta:
+ '@opentelemetry/api':
+ optional: true
+ '@playwright/test':
+ optional: true
+ babel-plugin-react-compiler:
+ optional: true
+ sass:
+ optional: true
+
+ nice-grpc-common@2.0.2:
+ resolution: {integrity: sha512-7RNWbls5kAL1QVUOXvBsv1uO0wPQK3lHv+cY1gwkTzirnG1Nop4cBJZubpgziNbaVc/bl9QJcyvsf/NQxa3rjQ==}
+
+ nice-grpc@2.0.1:
+ resolution: {integrity: sha512-Q5CGXO08STsv+HAkXeFgRayANT62X1LnIDhNXdCf+LP0XaP7EiHM0Cr3QefnoFjDZAx/Kxq+qiQfY66BrtKcNQ==}
+
+ node-addon-api@7.1.1:
+ resolution: {integrity: sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==}
+
+ node-domexception@1.0.0:
+ resolution: {integrity: sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==}
+ engines: {node: '>=10.5.0'}
+ deprecated: Use your platform's native DOMException instead
+
+ node-fetch@2.7.0:
+ resolution: {integrity: sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==}
+ engines: {node: 4.x || >=6.0.0}
+ peerDependencies:
+ encoding: ^0.1.0
+ peerDependenciesMeta:
+ encoding:
+ optional: true
+
+ node-fetch@3.3.2:
+ resolution: {integrity: sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==}
+ engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
+
+ node-releases@2.0.19:
+ resolution: {integrity: sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==}
+
+ nodemon@3.1.9:
+ resolution: {integrity: sha512-hdr1oIb2p6ZSxu3PB2JWWYS7ZQ0qvaZsc3hK8DR8f02kRzc8rjYmxAIvdz+aYC+8F2IjNaB7HMcSDg8nQpJxyg==}
+ engines: {node: '>=10'}
+ hasBin: true
+
+ nopt@5.0.0:
+ resolution: {integrity: sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==}
+ engines: {node: '>=6'}
+ hasBin: true
+
+ normalize-path@3.0.0:
+ resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==}
+ engines: {node: '>=0.10.0'}
+
+ normalize-range@0.1.2:
+ resolution: {integrity: sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==}
+ engines: {node: '>=0.10.0'}
+
+ npm-run-path@4.0.1:
+ resolution: {integrity: sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==}
+ engines: {node: '>=8'}
+
+ npm-run-path@5.3.0:
+ resolution: {integrity: sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==}
+ engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
+
+ npmlog@5.0.1:
+ resolution: {integrity: sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw==}
+ deprecated: This package is no longer supported.
+
+ nwsapi@2.2.20:
+ resolution: {integrity: sha512-/ieB+mDe4MrrKMT8z+mQL8klXydZWGR5Dowt4RAGKbJ3kIGEx3X4ljUo+6V73IXtUPWgfOlU5B9MlGxFO5T+cA==}
+
+ object-assign@4.1.1:
+ resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==}
+ engines: {node: '>=0.10.0'}
+
+ object-hash@3.0.0:
+ resolution: {integrity: sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==}
+ engines: {node: '>= 6'}
+
+ object-inspect@1.13.2:
+ resolution: {integrity: sha512-IRZSRuzJiynemAXPYtPe5BoI/RESNYR7TYm50MC5Mqbd3Jmw5y790sErYw3V6SryFJD64b74qQQs9wn5Bg/k3g==}
+ engines: {node: '>= 0.4'}
+
+ object-inspect@1.13.4:
+ resolution: {integrity: sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==}
+ engines: {node: '>= 0.4'}
+
+ object-is@1.1.6:
+ resolution: {integrity: sha512-F8cZ+KfGlSGi09lJT7/Nd6KJZ9ygtvYC0/UYYLI9nmQKLMnydpB9yvbv9K1uSkEu7FU9vYPmVwLg328tX+ot3Q==}
+ engines: {node: '>= 0.4'}
+
+ object-keys@1.1.1:
+ resolution: {integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==}
+ engines: {node: '>= 0.4'}
+
+ object.assign@4.1.5:
+ resolution: {integrity: sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ==}
+ engines: {node: '>= 0.4'}
+
+ object.entries@1.1.8:
+ resolution: {integrity: sha512-cmopxi8VwRIAw/fkijJohSfpef5PdN0pMQJN6VC/ZKvn0LIknWD8KtgY6KlQdEc4tIjcQ3HxSMmnvtzIscdaYQ==}
+ engines: {node: '>= 0.4'}
+
+ object.fromentries@2.0.8:
+ resolution: {integrity: sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==}
+ engines: {node: '>= 0.4'}
+
+ object.groupby@1.0.3:
+ resolution: {integrity: sha512-+Lhy3TQTuzXI5hevh8sBGqbmurHbbIjAi0Z4S63nthVLmLxfbj4T54a4CfZrXIrt9iP4mVAPYMo/v99taj3wjQ==}
+ engines: {node: '>= 0.4'}
+
+ object.values@1.2.0:
+ resolution: {integrity: sha512-yBYjY9QX2hnRmZHAjG/f13MzmBzxzYgQhFrke06TTyKY5zSTEqkOeukBzIdVA3j3ulu8Qa3MbVFShV7T2RmGtQ==}
+ engines: {node: '>= 0.4'}
+
+ once@1.4.0:
+ resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==}
+
+ onetime@5.1.2:
+ resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==}
+ engines: {node: '>=6'}
+
+ onetime@6.0.0:
+ resolution: {integrity: sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==}
+ engines: {node: '>=12'}
+
+ onetime@7.0.0:
+ resolution: {integrity: sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==}
+ engines: {node: '>=18'}
+
+ optionator@0.9.4:
+ resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==}
+ engines: {node: '>= 0.8.0'}
+
+ os-tmpdir@1.0.2:
+ resolution: {integrity: sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==}
+ engines: {node: '>=0.10.0'}
+
+ ospath@1.2.2:
+ resolution: {integrity: sha512-o6E5qJV5zkAbIDNhGSIlyOhScKXgQrSRMilfph0clDfM0nEnBOlKlH4sWDmG95BW/CvwNz0vmm7dJVtU2KlMiA==}
+
+ outdent@0.5.0:
+ resolution: {integrity: sha512-/jHxFIzoMXdqPzTaCpFzAAWhpkSjZPF4Vsn6jAfNpmbH/ymsmd7Qc6VE9BGn0L6YMj6uwpQLxCECpus4ukKS9Q==}
+
+ p-filter@2.1.0:
+ resolution: {integrity: sha512-ZBxxZ5sL2HghephhpGAQdoskxplTwr7ICaehZwLIlfL6acuVgZPm8yBNuRAFBGEqtD/hmUeq9eqLg2ys9Xr/yw==}
+ engines: {node: '>=8'}
+
+ p-limit@2.3.0:
+ resolution: {integrity: sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==}
+ engines: {node: '>=6'}
+
+ p-limit@3.1.0:
+ resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==}
+ engines: {node: '>=10'}
+
+ p-locate@4.1.0:
+ resolution: {integrity: sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==}
+ engines: {node: '>=8'}
+
+ p-locate@5.0.0:
+ resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==}
+ engines: {node: '>=10'}
+
+ p-map@2.1.0:
+ resolution: {integrity: sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw==}
+ engines: {node: '>=6'}
+
+ p-map@4.0.0:
+ resolution: {integrity: sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==}
+ engines: {node: '>=10'}
+
+ p-try@2.2.0:
+ resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==}
+ engines: {node: '>=6'}
+
+ package-json-from-dist@1.0.1:
+ resolution: {integrity: sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==}
+
+ package-manager-detector@0.2.11:
+ resolution: {integrity: sha512-BEnLolu+yuz22S56CU1SUKq3XC3PkwD5wv4ikR4MfGvnRVcmzXR9DwSlW2fEamyTPyXHomBJRzgapeuBvRNzJQ==}
+
+ parent-module@1.0.1:
+ resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==}
+ engines: {node: '>=6'}
+
+ parse5@7.3.0:
+ resolution: {integrity: sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==}
+
+ path-exists@4.0.0:
+ resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==}
+ engines: {node: '>=8'}
+
+ path-is-absolute@1.0.1:
+ resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==}
+ engines: {node: '>=0.10.0'}
+
+ path-key@3.1.1:
+ resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==}
+ engines: {node: '>=8'}
+
+ path-key@4.0.0:
+ resolution: {integrity: sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==}
+ engines: {node: '>=12'}
+
+ path-parse@1.0.7:
+ resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==}
+
+ path-scurry@1.11.1:
+ resolution: {integrity: sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==}
+ engines: {node: '>=16 || 14 >=14.18'}
+
+ path-type@4.0.0:
+ resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==}
+ engines: {node: '>=8'}
+
+ pathe@2.0.3:
+ resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==}
+
+ pathval@2.0.0:
+ resolution: {integrity: sha512-vE7JKRyES09KiunauX7nd2Q9/L7lhok4smP9RZTDeD4MVs72Dp2qNFVz39Nz5a0FVEW0BJR6C0DYrq6unoziZA==}
+ engines: {node: '>= 14.16'}
+
+ pause-stream@0.0.11:
+ resolution: {integrity: sha512-e3FBlXLmN/D1S+zHzanP4E/4Z60oFAa3O051qt1pxa7DEJWKAyil6upYVXCWadEnuoqa4Pkc9oUx9zsxYeRv8A==}
+
+ pend@1.2.0:
+ resolution: {integrity: sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==}
+
+ performance-now@2.1.0:
+ resolution: {integrity: sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==}
+
+ picocolors@1.1.1:
+ resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==}
+
+ picomatch@2.3.1:
+ resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==}
+ engines: {node: '>=8.6'}
+
+ picomatch@4.0.2:
+ resolution: {integrity: sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==}
+ engines: {node: '>=12'}
+
+ pidtree@0.6.0:
+ resolution: {integrity: sha512-eG2dWTVw5bzqGRztnHExczNxt5VGsE6OwTeCG3fdUf9KBsZzO3R5OIIIzWR+iZA0NtZ+RDVdaoE2dK1cn6jH4g==}
+ engines: {node: '>=0.10'}
+ hasBin: true
+
+ pify@2.3.0:
+ resolution: {integrity: sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==}
+ engines: {node: '>=0.10.0'}
+
+ pify@4.0.1:
+ resolution: {integrity: sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==}
+ engines: {node: '>=6'}
+
+ pirates@4.0.7:
+ resolution: {integrity: sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==}
+ engines: {node: '>= 6'}
+
+ playwright-core@1.52.0:
+ resolution: {integrity: sha512-l2osTgLXSMeuLZOML9qYODUQoPPnUsKsb5/P6LJ2e6uPKXUdPK5WYhN4z03G+YNbWmGDY4YENauNu4ZKczreHg==}
+ engines: {node: '>=18'}
+ hasBin: true
+
+ playwright@1.52.0:
+ resolution: {integrity: sha512-JAwMNMBlxJ2oD1kce4KPtMkDeKGHQstdpFPcPH3maElAXon/QZeTvtsfXmTMRyO9TslfoYOXkSsvao2nE1ilTw==}
+ engines: {node: '>=18'}
+ hasBin: true
+
+ possible-typed-array-names@1.0.0:
+ resolution: {integrity: sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==}
+ engines: {node: '>= 0.4'}
+
+ postcss-import@15.1.0:
+ resolution: {integrity: sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==}
+ engines: {node: '>=14.0.0'}
+ peerDependencies:
+ postcss: ^8.0.0
+
+ postcss-js@4.0.1:
+ resolution: {integrity: sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw==}
+ engines: {node: ^12 || ^14 || >= 16}
+ peerDependencies:
+ postcss: ^8.4.21
+
+ postcss-load-config@4.0.2:
+ resolution: {integrity: sha512-bSVhyJGL00wMVoPUzAVAnbEoWyqRxkjv64tUl427SKnPrENtq6hJwUojroMz2VB+Q1edmi4IfrAPpami5VVgMQ==}
+ engines: {node: '>= 14'}
+ peerDependencies:
+ postcss: '>=8.0.9'
+ ts-node: '>=9.0.0'
+ peerDependenciesMeta:
+ postcss:
+ optional: true
+ ts-node:
+ optional: true
+
+ postcss-load-config@6.0.1:
+ resolution: {integrity: sha512-oPtTM4oerL+UXmx+93ytZVN82RrlY/wPUV8IeDxFrzIjXOLF1pN+EmKPLbubvKHT2HC20xXsCAH2Z+CKV6Oz/g==}
+ engines: {node: '>= 18'}
+ peerDependencies:
+ jiti: '>=1.21.0'
+ postcss: '>=8.0.9'
+ tsx: ^4.8.1
+ yaml: ^2.4.2
+ peerDependenciesMeta:
+ jiti:
+ optional: true
+ postcss:
+ optional: true
+ tsx:
+ optional: true
+ yaml:
+ optional: true
+
+ postcss-nested@6.2.0:
+ resolution: {integrity: sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ==}
+ engines: {node: '>=12.0'}
+ peerDependencies:
+ postcss: ^8.2.14
+
+ postcss-selector-parser@6.1.2:
+ resolution: {integrity: sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==}
+ engines: {node: '>=4'}
+
+ postcss-value-parser@4.2.0:
+ resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==}
+
+ postcss@8.4.31:
+ resolution: {integrity: sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==}
+ engines: {node: ^10 || ^12 || >=14}
+
+ postcss@8.5.3:
+ resolution: {integrity: sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A==}
+ engines: {node: ^10 || ^12 || >=14}
+
+ prelude-ls@1.2.1:
+ resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==}
+ engines: {node: '>= 0.8.0'}
+
+ prettier-plugin-organize-imports@4.1.0:
+ resolution: {integrity: sha512-5aWRdCgv645xaa58X8lOxzZoiHAldAPChljr/MT0crXVOWTZ+Svl4hIWlz+niYSlO6ikE5UXkN1JrRvIP2ut0A==}
+ peerDependencies:
+ prettier: '>=2.0'
+ typescript: '>=2.9'
+ vue-tsc: ^2.1.0
+ peerDependenciesMeta:
+ vue-tsc:
+ optional: true
+
+ prettier-plugin-tailwindcss@0.6.11:
+ resolution: {integrity: sha512-YxaYSIvZPAqhrrEpRtonnrXdghZg1irNg4qrjboCXrpybLWVs55cW2N3juhspVJiO0JBvYJT8SYsJpc8OQSnsA==}
+ engines: {node: '>=14.21.3'}
+ peerDependencies:
+ '@ianvs/prettier-plugin-sort-imports': '*'
+ '@prettier/plugin-pug': '*'
+ '@shopify/prettier-plugin-liquid': '*'
+ '@trivago/prettier-plugin-sort-imports': '*'
+ '@zackad/prettier-plugin-twig': '*'
+ prettier: ^3.0
+ prettier-plugin-astro: '*'
+ prettier-plugin-css-order: '*'
+ prettier-plugin-import-sort: '*'
+ prettier-plugin-jsdoc: '*'
+ prettier-plugin-marko: '*'
+ prettier-plugin-multiline-arrays: '*'
+ prettier-plugin-organize-attributes: '*'
+ prettier-plugin-organize-imports: '*'
+ prettier-plugin-sort-imports: '*'
+ prettier-plugin-style-order: '*'
+ prettier-plugin-svelte: '*'
+ peerDependenciesMeta:
+ '@ianvs/prettier-plugin-sort-imports':
+ optional: true
+ '@prettier/plugin-pug':
+ optional: true
+ '@shopify/prettier-plugin-liquid':
+ optional: true
+ '@trivago/prettier-plugin-sort-imports':
+ optional: true
+ '@zackad/prettier-plugin-twig':
+ optional: true
+ prettier-plugin-astro:
+ optional: true
+ prettier-plugin-css-order:
+ optional: true
+ prettier-plugin-import-sort:
+ optional: true
+ prettier-plugin-jsdoc:
+ optional: true
+ prettier-plugin-marko:
+ optional: true
+ prettier-plugin-multiline-arrays:
+ optional: true
+ prettier-plugin-organize-attributes:
+ optional: true
+ prettier-plugin-organize-imports:
+ optional: true
+ prettier-plugin-sort-imports:
+ optional: true
+ prettier-plugin-style-order:
+ optional: true
+ prettier-plugin-svelte:
+ optional: true
+
+ prettier@2.8.8:
+ resolution: {integrity: sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==}
+ engines: {node: '>=10.13.0'}
+ hasBin: true
+
+ prettier@3.5.3:
+ resolution: {integrity: sha512-QQtaxnoDJeAkDvDKWCLiwIXkTgRhwYDEQCghU9Z6q03iyek/rxRh/2lC3HB7P8sWT2xC/y5JDctPLBIGzHKbhw==}
+ engines: {node: '>=14'}
+ hasBin: true
+
+ pretty-bytes@5.6.0:
+ resolution: {integrity: sha512-FFw039TmrBqFK8ma/7OL3sDz/VytdtJr044/QUJtH0wK9lb9jLq9tJyIxUwtQJHwar2BqtiA4iCWSwo9JLkzFg==}
+ engines: {node: '>=6'}
+
+ pretty-format@27.5.1:
+ resolution: {integrity: sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==}
+ engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0}
+
+ process@0.11.10:
+ resolution: {integrity: sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==}
+ engines: {node: '>= 0.6.0'}
+
+ prop-types@15.8.1:
+ resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==}
+
+ protobufjs@7.4.0:
+ resolution: {integrity: sha512-mRUWCc3KUU4w1jU8sGxICXH/gNS94DvI1gxqDvBzhj1JpcsimQkYiOJfwsPUykUI5ZaspFbSgmBLER8IrQ3tqw==}
+ engines: {node: '>=12.0.0'}
+
+ proxy-from-env@1.0.0:
+ resolution: {integrity: sha512-F2JHgJQ1iqwnHDcQjVBsq3n/uoaFL+iPW/eAeL7kVxy/2RrWaN4WroKjjvbsoRtv0ftelNyC01bjRhn/bhcf4A==}
+
+ proxy-from-env@1.1.0:
+ resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==}
+
+ ps-tree@1.2.0:
+ resolution: {integrity: sha512-0VnamPPYHl4uaU/nSFeZZpR21QAWRz+sRv4iW9+v/GS/J5U5iZB5BNN6J0RMoOvdx2gWM2+ZFMIm58q24e4UYA==}
+ engines: {node: '>= 0.10'}
+ hasBin: true
+
+ pstree.remy@1.1.8:
+ resolution: {integrity: sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==}
+
+ pump@3.0.2:
+ resolution: {integrity: sha512-tUPXtzlGM8FE3P0ZL6DVs/3P58k9nk8/jZeQCurTJylQA8qFYzHFfhBJkuqyE0FifOsQ0uKWekiZ5g8wtr28cw==}
+
+ punycode@2.3.1:
+ resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==}
+ engines: {node: '>=6'}
+
+ qrcode.react@3.1.0:
+ resolution: {integrity: sha512-oyF+Urr3oAMUG/OiOuONL3HXM+53wvuH3mtIWQrYmsXoAq0DkvZp2RYUWFSMFtbdOpuS++9v+WAkzNVkMlNW6Q==}
+ peerDependencies:
+ react: ^16.8.0 || ^17.0.0 || ^18.0.0
+
+ qs@6.14.0:
+ resolution: {integrity: sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==}
+ engines: {node: '>=0.6'}
+
+ quansync@0.2.10:
+ resolution: {integrity: sha512-t41VRkMYbkHyCYmOvx/6URnN80H7k4X0lLdBMGsz+maAwrJQYB1djpV6vHrQIBE0WBSGqhtEHrK9U3DWWH8v7A==}
+
+ queue-microtask@1.2.3:
+ resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==}
+
+ react-dom@19.1.0:
+ resolution: {integrity: sha512-Xs1hdnE+DyKgeHJeJznQmYMIBG3TKIHJJT95Q58nHLSrElKlGQqDTR2HQ9fx5CN/Gk6Vh/kupBTDLU11/nDk/g==}
+ peerDependencies:
+ react: ^19.1.0
+
+ react-hook-form@7.39.5:
+ resolution: {integrity: sha512-OE0HKyz5IPc6svN2wd+e+evidZrw4O4WZWAWYzQVZuHi+hYnHFSLnxOq0ddjbdmaLIsLHut/ab7j72y2QT3+KA==}
+ engines: {node: '>=12.22.0'}
+ peerDependencies:
+ react: ^16.8.0 || ^17 || ^18
+
+ react-is@16.13.1:
+ resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==}
+
+ react-is@17.0.2:
+ resolution: {integrity: sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==}
+
+ react-refresh@0.17.0:
+ resolution: {integrity: sha512-z6F7K9bV85EfseRCp2bzrpyQ0Gkw1uLoCel9XBVWPg/TjRj94SkJzUTGfOa4bs7iJvBWtQG0Wq7wnI0syw3EBQ==}
+ engines: {node: '>=0.10.0'}
+
+ react@19.1.0:
+ resolution: {integrity: sha512-FS+XFBNvn3GTAWq26joslQgWNoFu08F4kl0J4CgdNKADkdSGXQyTCnKteIAJy96Br6YbpEU1LSzV5dYtjMkMDg==}
+ engines: {node: '>=0.10.0'}
+
+ read-cache@1.0.0:
+ resolution: {integrity: sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==}
+
+ read-yaml-file@1.1.0:
+ resolution: {integrity: sha512-VIMnQi/Z4HT2Fxuwg5KrY174U1VdUIASQVWXXyqtNRtxSr9IYkn1rsI6Tb6HsrHCmB7gVpNwX6JxPTHcH6IoTA==}
+ engines: {node: '>=6'}
+
+ readable-stream@3.6.2:
+ resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==}
+ engines: {node: '>= 6'}
+
+ readdirp@3.6.0:
+ resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==}
+ engines: {node: '>=8.10.0'}
+
+ readdirp@4.1.2:
+ resolution: {integrity: sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==}
+ engines: {node: '>= 14.18.0'}
+
+ redent@3.0.0:
+ resolution: {integrity: sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==}
+ engines: {node: '>=8'}
+
+ reflect.getprototypeof@1.0.6:
+ resolution: {integrity: sha512-fmfw4XgoDke3kdI6h4xcUz1dG8uaiv5q9gcEwLS4Pnth2kxT+GZ7YehS1JTMGBQmtV7Y4GFGbs2re2NqhdozUg==}
+ engines: {node: '>= 0.4'}
+
+ regenerator-runtime@0.14.1:
+ resolution: {integrity: sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==}
+
+ regexp.prototype.flags@1.5.2:
+ resolution: {integrity: sha512-NcDiDkTLuPR+++OCKB0nWafEmhg/Da8aUPLPMQbK+bxKKCm1/S5he+AqYa4PlMCVBalb4/yxIRub6qkEx5yJbw==}
+ engines: {node: '>= 0.4'}
+
+ request-progress@3.0.0:
+ resolution: {integrity: sha512-MnWzEHHaxHO2iWiQuHrUPBi/1WeBf5PkxQqNyNvLl9VAYSdXkP8tQ3pBSeCPD+yw0v0Aq1zosWLz0BdeXpWwZg==}
+
+ require-directory@2.1.1:
+ resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==}
+ engines: {node: '>=0.10.0'}
+
+ resolve-from@4.0.0:
+ resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==}
+ engines: {node: '>=4'}
+
+ resolve-from@5.0.0:
+ resolution: {integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==}
+ engines: {node: '>=8'}
+
+ resolve-pkg-maps@1.0.0:
+ resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==}
+
+ resolve@1.22.8:
+ resolution: {integrity: sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==}
+ hasBin: true
+
+ resolve@2.0.0-next.5:
+ resolution: {integrity: sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA==}
+ hasBin: true
+
+ restore-cursor@3.1.0:
+ resolution: {integrity: sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==}
+ engines: {node: '>=8'}
+
+ restore-cursor@5.1.0:
+ resolution: {integrity: sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==}
+ engines: {node: '>=18'}
+
+ reusify@1.0.4:
+ resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==}
+ engines: {iojs: '>=1.0.0', node: '>=0.10.0'}
+
+ rfdc@1.4.1:
+ resolution: {integrity: sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==}
+
+ rimraf@3.0.2:
+ resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==}
+ deprecated: Rimraf versions prior to v4 are no longer supported
+ hasBin: true
+
+ rollup@4.40.0:
+ resolution: {integrity: sha512-Noe455xmA96nnqH5piFtLobsGbCij7Tu+tb3c1vYjNbTkfzGqXqQXG3wJaYXkRZuQ0vEYN4bhwg7QnIrqB5B+w==}
+ engines: {node: '>=18.0.0', npm: '>=8.0.0'}
+ hasBin: true
+
+ rrweb-cssom@0.8.0:
+ resolution: {integrity: sha512-guoltQEx+9aMf2gDZ0s62EcV8lsXR+0w8915TC3ITdn2YueuNjdAYh/levpU9nFaoChh9RUS5ZdQMrKfVEN9tw==}
+
+ run-parallel@1.2.0:
+ resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==}
+
+ rxjs@7.8.2:
+ resolution: {integrity: sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==}
+
+ safe-array-concat@1.1.2:
+ resolution: {integrity: sha512-vj6RsCsWBCf19jIeHEfkRMw8DPiBb+DMXklQ/1SGDHOMlHdPUkZXFQ2YdplS23zESTijAcurb1aSgJA3AgMu1Q==}
+ engines: {node: '>=0.4'}
+
+ safe-buffer@5.2.1:
+ resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==}
+
+ safe-regex-test@1.0.3:
+ resolution: {integrity: sha512-CdASjNJPvRa7roO6Ra/gLYBTzYzzPyyBXxIMdGW3USQLyjWEls2RgW5UBTXaQVp+OrpeCK3bLem8smtmheoRuw==}
+ engines: {node: '>= 0.4'}
+
+ safer-buffer@2.1.2:
+ resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==}
+
+ sass@1.87.0:
+ resolution: {integrity: sha512-d0NoFH4v6SjEK7BoX810Jsrhj7IQSYHAHLi/iSpgqKc7LaIDshFRlSg5LOymf9FqQhxEHs2W5ZQXlvy0KD45Uw==}
+ engines: {node: '>=14.0.0'}
+ hasBin: true
+
+ saxes@6.0.0:
+ resolution: {integrity: sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==}
+ engines: {node: '>=v12.22.7'}
+
+ scheduler@0.26.0:
+ resolution: {integrity: sha512-NlHwttCI/l5gCPR3D1nNXtWABUmBwvZpEQiD4IXSbIDq8BzLIK/7Ir5gTFSGZDUu37K5cMNp0hFtzO38sC7gWA==}
+
+ semver@6.3.1:
+ resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==}
+ hasBin: true
+
+ semver@7.7.1:
+ resolution: {integrity: sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==}
+ engines: {node: '>=10'}
+ hasBin: true
+
+ server-only@0.0.1:
+ resolution: {integrity: sha512-qepMx2JxAa5jjfzxG79yPPq+8BuFToHd1hm7kI+Z4zAq1ftQiP7HcxMhDDItrbtwVeLg/cY2JnKnrcFkmiswNA==}
+
+ set-blocking@2.0.0:
+ resolution: {integrity: sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==}
+
+ set-function-length@1.2.2:
+ resolution: {integrity: sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==}
+ engines: {node: '>= 0.4'}
+
+ set-function-name@2.0.2:
+ resolution: {integrity: sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==}
+ engines: {node: '>= 0.4'}
+
+ sharp@0.34.1:
+ resolution: {integrity: sha512-1j0w61+eVxu7DawFJtnfYcvSv6qPFvfTaqzTQ2BLknVhHTwGS8sc63ZBF4rzkWMBVKybo4S5OBtDdZahh2A1xg==}
+ engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
+
+ shebang-command@2.0.0:
+ resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==}
+ engines: {node: '>=8'}
+
+ shebang-regex@3.0.0:
+ resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==}
+ engines: {node: '>=8'}
+
+ shell-quote@1.8.2:
+ resolution: {integrity: sha512-AzqKpGKjrj7EM6rKVQEPpB288oCfnrEIuyoT9cyF4nmGa7V8Zk6f7RRqYisX8X9m+Q7bd632aZW4ky7EhbQztA==}
+ engines: {node: '>= 0.4'}
+
+ side-channel-list@1.0.0:
+ resolution: {integrity: sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==}
+ engines: {node: '>= 0.4'}
+
+ side-channel-map@1.0.1:
+ resolution: {integrity: sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==}
+ engines: {node: '>= 0.4'}
+
+ side-channel-weakmap@1.0.2:
+ resolution: {integrity: sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==}
+ engines: {node: '>= 0.4'}
+
+ side-channel@1.0.6:
+ resolution: {integrity: sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==}
+ engines: {node: '>= 0.4'}
+
+ side-channel@1.1.0:
+ resolution: {integrity: sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==}
+ engines: {node: '>= 0.4'}
+
+ siginfo@2.0.0:
+ resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==}
+
+ signal-exit@3.0.7:
+ resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==}
+
+ signal-exit@4.1.0:
+ resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==}
+ engines: {node: '>=14'}
+
+ simple-swizzle@0.2.2:
+ resolution: {integrity: sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==}
+
+ simple-update-notifier@2.0.0:
+ resolution: {integrity: sha512-a2B9Y0KlNXl9u/vsW6sTIu9vGEpfKu2wRV6l1H3XEas/0gUIzGzBoP/IouTcUQbm9JWZLH3COxyn03TYlFax6w==}
+ engines: {node: '>=10'}
+
+ slash@3.0.0:
+ resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==}
+ engines: {node: '>=8'}
+
+ slice-ansi@3.0.0:
+ resolution: {integrity: sha512-pSyv7bSTC7ig9Dcgbw9AuRNUb5k5V6oDudjZoMBSr13qpLBG7tB+zgCkARjq7xIUgdz5P1Qe8u+rSGdouOOIyQ==}
+ engines: {node: '>=8'}
+
+ slice-ansi@4.0.0:
+ resolution: {integrity: sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==}
+ engines: {node: '>=10'}
+
+ slice-ansi@5.0.0:
+ resolution: {integrity: sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ==}
+ engines: {node: '>=12'}
+
+ slice-ansi@7.1.0:
+ resolution: {integrity: sha512-bSiSngZ/jWeX93BqeIAbImyTbEihizcwNjFoRUIY/T1wWQsfsm2Vw1agPKylXvQTU7iASGdHhyqRlqQzfz+Htg==}
+ engines: {node: '>=18'}
+
+ source-map-js@1.2.1:
+ resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==}
+ engines: {node: '>=0.10.0'}
+
+ source-map@0.8.0-beta.0:
+ resolution: {integrity: sha512-2ymg6oRBpebeZi9UUNsgQ89bhx01TcTkmNTGnNO88imTmbSgy4nfujrgVEFKWpMTEGA11EDkTt7mqObTPdigIA==}
+ engines: {node: '>= 8'}
+
+ spawndamnit@3.0.1:
+ resolution: {integrity: sha512-MmnduQUuHCoFckZoWnXsTg7JaiLBJrKFj9UI2MbRPGaJeVpsLcVBu6P/IGZovziM/YBsellCmsprgNA+w0CzVg==}
+
+ split@0.3.3:
+ resolution: {integrity: sha512-wD2AeVmxXRBoX44wAycgjVpMhvbwdI2aZjCkvfNcH1YqHQvJVa1duWc73OyVGJUc05fhFaTZeQ/PYsrmyH0JVA==}
+
+ sprintf-js@1.0.3:
+ resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==}
+
+ sshpk@1.18.0:
+ resolution: {integrity: sha512-2p2KJZTSqQ/I3+HX42EpYOa2l3f8Erv8MWKsy2I9uf4wA7yFIkXRffYdsx86y6z4vHtV8u7g+pPlr8/4ouAxsQ==}
+ engines: {node: '>=0.10.0'}
+ hasBin: true
+
+ stackback@0.0.2:
+ resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==}
+
+ start-server-and-test@2.0.11:
+ resolution: {integrity: sha512-TN39gLzPhHAflxyOkE/oMfQGj+pj3JgF6qVicFH/JrXt7xXktidKXwqfRga+ve7lVA8+RgPZVc25VrEPRScaDw==}
+ engines: {node: '>=16'}
+ hasBin: true
+
+ std-env@3.9.0:
+ resolution: {integrity: sha512-UGvjygr6F6tpH7o2qyqR6QYpwraIjKSdtzyBdyytFOHmPZY917kwdwLG0RbOjWOnKmnm3PeHjaoLLMie7kPLQw==}
+
+ stop-iteration-iterator@1.0.0:
+ resolution: {integrity: sha512-iCGQj+0l0HOdZ2AEeBADlsRC+vsnDsZsbdSiH1yNSjcfKM7fdpCMfqAL/dwF5BLiw/XhRft/Wax6zQbhq2BcjQ==}
+ engines: {node: '>= 0.4'}
+
+ stream-combiner@0.0.4:
+ resolution: {integrity: sha512-rT00SPnTVyRsaSz5zgSPma/aHSOic5U1prhYdRy5HS2kTZviFpmDgzilbtsJsxiroqACmayynDN/9VzIbX5DOw==}
+
+ string-argv@0.3.2:
+ resolution: {integrity: sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==}
+ engines: {node: '>=0.6.19'}
+
+ string-width@4.2.3:
+ resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==}
+ engines: {node: '>=8'}
+
+ string-width@5.1.2:
+ resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==}
+ engines: {node: '>=12'}
+
+ string-width@7.2.0:
+ resolution: {integrity: sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==}
+ engines: {node: '>=18'}
+
+ string.prototype.includes@2.0.0:
+ resolution: {integrity: sha512-E34CkBgyeqNDcrbU76cDjL5JLcVrtSdYq0MEh/B10r17pRP4ciHLwTgnuLV8Ay6cgEMLkcBkFCKyFZ43YldYzg==}
+
+ string.prototype.matchall@4.0.11:
+ resolution: {integrity: sha512-NUdh0aDavY2og7IbBPenWqR9exH+E26Sv8e0/eTe1tltDGZL+GtBkDAnnyBtmekfK6/Dq3MkcGtzXFEd1LQrtg==}
+ engines: {node: '>= 0.4'}
+
+ string.prototype.repeat@1.0.0:
+ resolution: {integrity: sha512-0u/TldDbKD8bFCQ/4f5+mNRrXwZ8hg2w7ZR8wa16e8z9XpePWl3eGEcUD0OXpEH/VJH/2G3gjUtR3ZOiBe2S/w==}
+
+ string.prototype.trim@1.2.9:
+ resolution: {integrity: sha512-klHuCNxiMZ8MlsOihJhJEBJAiMVqU3Z2nEXWfWnIqjN0gEFS9J9+IxKozWWtQGcgoa1WUZzLjKPTr4ZHNFTFxw==}
+ engines: {node: '>= 0.4'}
+
+ string.prototype.trimend@1.0.8:
+ resolution: {integrity: sha512-p73uL5VCHCO2BZZ6krwwQE3kCzM7NKmis8S//xEC6fQonchbum4eP6kR4DLEjQFO3Wnj3Fuo8NM0kOSjVdHjZQ==}
+
+ string.prototype.trimstart@1.0.8:
+ resolution: {integrity: sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==}
+ engines: {node: '>= 0.4'}
+
+ string_decoder@1.3.0:
+ resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==}
+
+ strip-ansi@6.0.1:
+ resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==}
+ engines: {node: '>=8'}
+
+ strip-ansi@7.1.0:
+ resolution: {integrity: sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==}
+ engines: {node: '>=12'}
+
+ strip-bom@3.0.0:
+ resolution: {integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==}
+ engines: {node: '>=4'}
+
+ strip-final-newline@2.0.0:
+ resolution: {integrity: sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==}
+ engines: {node: '>=6'}
+
+ strip-final-newline@3.0.0:
+ resolution: {integrity: sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==}
+ engines: {node: '>=12'}
+
+ strip-indent@3.0.0:
+ resolution: {integrity: sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==}
+ engines: {node: '>=8'}
+
+ strip-json-comments@3.1.1:
+ resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==}
+ engines: {node: '>=8'}
+
+ styled-jsx@5.1.6:
+ resolution: {integrity: sha512-qSVyDTeMotdvQYoHWLNGwRFJHC+i+ZvdBRYosOFgC+Wg1vx4frN2/RG/NA7SYqqvKNLf39P2LSRA2pu6n0XYZA==}
+ engines: {node: '>= 12.0.0'}
+ peerDependencies:
+ '@babel/core': '*'
+ babel-plugin-macros: '*'
+ react: '>= 16.8.0 || 17.x.x || ^18.0.0-0 || ^19.0.0-0'
+ peerDependenciesMeta:
+ '@babel/core':
+ optional: true
+ babel-plugin-macros:
+ optional: true
+
+ sucrase@3.35.0:
+ resolution: {integrity: sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA==}
+ engines: {node: '>=16 || 14 >=14.17'}
+ hasBin: true
+
+ supports-color@5.5.0:
+ resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==}
+ engines: {node: '>=4'}
+
+ supports-color@7.2.0:
+ resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==}
+ engines: {node: '>=8'}
+
+ supports-color@8.1.1:
+ resolution: {integrity: sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==}
+ engines: {node: '>=10'}
+
+ supports-preserve-symlinks-flag@1.0.0:
+ resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==}
+ engines: {node: '>= 0.4'}
+
+ symbol-tree@3.2.4:
+ resolution: {integrity: sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==}
+
+ tabbable@6.2.0:
+ resolution: {integrity: sha512-Cat63mxsVJlzYvN51JmVXIgNoUokrIaT2zLclCXjRd8boZ0004U4KCs/sToJ75C6sdlByWxpYnb5Boif1VSFew==}
+
+ tailwindcss@3.4.14:
+ resolution: {integrity: sha512-IcSvOcTRcUtQQ7ILQL5quRDg7Xs93PdJEk1ZLbhhvJc7uj/OAhYOnruEiwnGgBvUtaUAJ8/mhSw1o8L2jCiENA==}
+ engines: {node: '>=14.0.0'}
+ hasBin: true
+
+ tailwindcss@4.1.4:
+ resolution: {integrity: sha512-1ZIUqtPITFbv/DxRmDr5/agPqJwF69d24m9qmM1939TJehgY539CtzeZRjbLt5G6fSy/7YqqYsfvoTEw9xUI2A==}
+
+ tapable@2.2.1:
+ resolution: {integrity: sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==}
+ engines: {node: '>=6'}
+
+ tar@6.2.1:
+ resolution: {integrity: sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==}
+ engines: {node: '>=10'}
+
+ term-size@2.2.1:
+ resolution: {integrity: sha512-wK0Ri4fOGjv/XPy8SBHZChl8CM7uMc5VML7SqiQ0zG7+J5Vr+RMQDoHa2CNT6KHUnTGIXH34UDMkPzAUyapBZg==}
+ engines: {node: '>=8'}
+
+ text-table@0.2.0:
+ resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==}
+
+ thenify-all@1.6.0:
+ resolution: {integrity: sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==}
+ engines: {node: '>=0.8'}
+
+ thenify@3.3.1:
+ resolution: {integrity: sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==}
+
+ thirty-two@1.0.2:
+ resolution: {integrity: sha512-OEI0IWCe+Dw46019YLl6V10Us5bi574EvlJEOcAkB29IzQ/mYD1A6RyNHLjZPiHCmuodxvgF6U+vZO1L15lxVA==}
+ engines: {node: '>=0.2.6'}
+
+ throttleit@1.0.1:
+ resolution: {integrity: sha512-vDZpf9Chs9mAdfY046mcPt8fg5QSZr37hEH4TXYBnDF+izxgrbRGUAAaBvIk/fJm9aOFCGFd1EsNg5AZCbnQCQ==}
+
+ through@2.3.8:
+ resolution: {integrity: sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==}
+
+ tinybench@2.9.0:
+ resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==}
+
+ tinycolor2@1.4.2:
+ resolution: {integrity: sha512-vJhccZPs965sV/L2sU4oRQVAos0pQXwsvTLkWYdqJ+a8Q5kPFzJTuOFwy7UniPli44NKQGAglksjvOcpo95aZA==}
+
+ tinyexec@0.3.2:
+ resolution: {integrity: sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==}
+
+ tinyglobby@0.2.13:
+ resolution: {integrity: sha512-mEwzpUgrLySlveBwEVDMKk5B57bhLPYovRfPAXD5gA/98Opn0rCDj3GtLwFvCvH5RK9uPCExUROW5NjDwvqkxw==}
+ engines: {node: '>=12.0.0'}
+
+ tinypool@1.0.2:
+ resolution: {integrity: sha512-al6n+QEANGFOMf/dmUMsuS5/r9B06uwlyNjZZql/zv8J7ybHCgoihBNORZCY2mzUuAnomQa2JdhyHKzZxPCrFA==}
+ engines: {node: ^18.0.0 || >=20.0.0}
+
+ tinyrainbow@2.0.0:
+ resolution: {integrity: sha512-op4nsTR47R6p0vMUUoYl/a+ljLFVtlfaXkLQmqfLR1qHma1h/ysYk4hEXZ880bf2CYgTskvTa/e196Vd5dDQXw==}
+ engines: {node: '>=14.0.0'}
+
+ tinyspy@3.0.2:
+ resolution: {integrity: sha512-n1cw8k1k0x4pgA2+9XrOkFydTerNcJ1zWCO5Nn9scWHTD+5tp8dghT2x1uduQePZTZgd3Tupf+x9BxJjeJi77Q==}
+ engines: {node: '>=14.0.0'}
+
+ tldts-core@6.1.86:
+ resolution: {integrity: sha512-Je6p7pkk+KMzMv2XXKmAE3McmolOQFdxkKw0R8EYNr7sELW46JqnNeTX8ybPiQgvg1ymCoF8LXs5fzFaZvJPTA==}
+
+ tldts@6.1.86:
+ resolution: {integrity: sha512-WMi/OQ2axVTf/ykqCQgXiIct+mSQDFdH2fkwhPwgEwvJ1kSzZRiinb0zF2Xb8u4+OqPChmyI6MEu4EezNJz+FQ==}
+ hasBin: true
+
+ tmp@0.0.33:
+ resolution: {integrity: sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==}
+ engines: {node: '>=0.6.0'}
+
+ tmp@0.2.3:
+ resolution: {integrity: sha512-nZD7m9iCPC5g0pYmcaxogYKggSfLsdxl8of3Q/oIbqCqLLIO9IAF0GWjX1z9NZRHPiXv8Wex4yDCaZsgEw0Y8w==}
+ engines: {node: '>=14.14'}
+
+ to-regex-range@5.0.1:
+ resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==}
+ engines: {node: '>=8.0'}
+
+ toggle-selection@1.0.6:
+ resolution: {integrity: sha512-BiZS+C1OS8g/q2RRbJmy59xpyghNBqrr6k5L/uKBGRsTfxmu3ffiRnd8mlGPUVayg8pvfi5urfnu8TU7DVOkLQ==}
+
+ touch@3.1.1:
+ resolution: {integrity: sha512-r0eojU4bI8MnHr8c5bNo7lJDdI2qXlWWJk6a9EAFG7vbhTjElYhBVS3/miuE0uOuoLdb8Mc/rVfsmm6eo5o9GA==}
+ hasBin: true
+
+ tough-cookie@5.1.2:
+ resolution: {integrity: sha512-FVDYdxtnj0G6Qm/DhNPSb8Ju59ULcup3tuJxkFb5K8Bv2pUXILbf0xZWU8PX8Ov19OXljbUyveOFwRMwkXzO+A==}
+ engines: {node: '>=16'}
+
+ tr46@0.0.3:
+ resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==}
+
+ tr46@1.0.1:
+ resolution: {integrity: sha512-dTpowEjclQ7Kgx5SdBkqRzVhERQXov8/l9Ft9dVM9fmg0W0KQSVaXX9T4i6twCPNtYiZM53lpSSUAwJbFPOHxA==}
+
+ tr46@5.1.1:
+ resolution: {integrity: sha512-hdF5ZgjTqgAntKkklYw0R03MG2x/bSzTtkxmIRw/sTNV8YXsCJ1tfLAX23lhxhHJlEf3CRCOCGGWw3vI3GaSPw==}
+ engines: {node: '>=18'}
+
+ tree-kill@1.2.2:
+ resolution: {integrity: sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==}
+ hasBin: true
+
+ ts-api-utils@1.4.1:
+ resolution: {integrity: sha512-5RU2/lxTA3YUZxju61HO2U6EoZLvBLtmV2mbTvqyu4a/7s7RmJPT+1YekhMVsQhznRWk/czIwDUg+V8Q9ZuG4w==}
+ engines: {node: '>=16'}
+ peerDependencies:
+ typescript: '>=4.2.0'
+
+ ts-error@1.0.6:
+ resolution: {integrity: sha512-tLJxacIQUM82IR7JO1UUkKlYuUTmoY9HBJAmNWFzheSlDS5SPMcNIepejHJa4BpPQLAcbRhRf3GDJzyj6rbKvA==}
+
+ ts-interface-checker@0.1.13:
+ resolution: {integrity: sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==}
+
+ ts-poet@6.11.0:
+ resolution: {integrity: sha512-r5AGF8vvb+GjBsnqiTqbLhN1/U2FJt6BI+k0dfCrkKzWvUhNlwMmq9nDHuucHs45LomgHjZPvYj96dD3JawjJA==}
+
+ ts-proto-descriptors@2.0.0:
+ resolution: {integrity: sha512-wHcTH3xIv11jxgkX5OyCSFfw27agpInAd6yh89hKG6zqIXnjW9SYqSER2CVQxdPj4czeOhGagNvZBEbJPy7qkw==}
+
+ ts-proto@2.7.0:
+ resolution: {integrity: sha512-BGHjse2wTOeswOqnnPKinpxmbaRd882so/e1En6ww59YMG7AO9Kg4vPpJcbVfrpBixPRDqHafXD/RDyd2T99GA==}
+ hasBin: true
+
+ tsconfck@3.1.5:
+ resolution: {integrity: sha512-CLDfGgUp7XPswWnezWwsCRxNmgQjhYq3VXHM0/XIRxhVrKw0M1if9agzryh1QS3nxjCROvV+xWxoJO1YctzzWg==}
+ engines: {node: ^18 || >=20}
+ hasBin: true
+ peerDependencies:
+ typescript: ^5.0.0
+ peerDependenciesMeta:
+ typescript:
+ optional: true
+
+ tsconfig-paths@3.15.0:
+ resolution: {integrity: sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==}
+
+ tslib@2.8.1:
+ resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==}
+
+ tsup@8.4.0:
+ resolution: {integrity: sha512-b+eZbPCjz10fRryaAA7C8xlIHnf8VnsaRqydheLIqwG/Mcpfk8Z5zp3HayX7GaTygkigHl5cBUs+IhcySiIexQ==}
+ engines: {node: '>=18'}
+ hasBin: true
+ peerDependencies:
+ '@microsoft/api-extractor': ^7.36.0
+ '@swc/core': ^1
+ postcss: ^8.4.12
+ typescript: '>=4.5.0'
+ peerDependenciesMeta:
+ '@microsoft/api-extractor':
+ optional: true
+ '@swc/core':
+ optional: true
+ postcss:
+ optional: true
+ typescript:
+ optional: true
+
+ tunnel-agent@0.6.0:
+ resolution: {integrity: sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==}
+
+ turbo-darwin-64@2.5.0:
+ resolution: {integrity: sha512-fP1hhI9zY8hv0idym3hAaXdPi80TLovmGmgZFocVAykFtOxF+GlfIgM/l4iLAV9ObIO4SUXPVWHeBZQQ+Hpjag==}
+ cpu: [x64]
+ os: [darwin]
+
+ turbo-darwin-arm64@2.5.0:
+ resolution: {integrity: sha512-p9sYq7kXH7qeJwIQE86cOWv/xNqvow846l6c/qWc26Ib1ci5W7V0sI5thsrP3eH+VA0d+SHalTKg5SQXgNQBWA==}
+ cpu: [arm64]
+ os: [darwin]
+
+ turbo-linux-64@2.5.0:
+ resolution: {integrity: sha512-1iEln2GWiF3iPPPS1HQJT6ZCFXynJPd89gs9SkggH2EJsj3eRUSVMmMC8y6d7bBbhBFsiGGazwFIYrI12zs6uQ==}
+ cpu: [x64]
+ os: [linux]
+
+ turbo-linux-arm64@2.5.0:
+ resolution: {integrity: sha512-bKBcbvuQHmsX116KcxHJuAcppiiBOfivOObh2O5aXNER6mce7YDDQJy00xQQNp1DhEfcSV2uOsvb3O3nN2cbcA==}
+ cpu: [arm64]
+ os: [linux]
+
+ turbo-windows-64@2.5.0:
+ resolution: {integrity: sha512-9BCo8oQ7BO7J0K913Czbc3tw8QwLqn2nTe4E47k6aVYkM12ASTScweXPTuaPFP5iYXAT6z5Dsniw704Ixa5eGg==}
+ cpu: [x64]
+ os: [win32]
+
+ turbo-windows-arm64@2.5.0:
+ resolution: {integrity: sha512-OUHCV+ueXa3UzfZ4co/ueIHgeq9B2K48pZwIxKSm5VaLVuv8M13MhM7unukW09g++dpdrrE1w4IOVgxKZ0/exg==}
+ cpu: [arm64]
+ os: [win32]
+
+ turbo@2.5.0:
+ resolution: {integrity: sha512-PvSRruOsitjy6qdqwIIyolv99+fEn57gP6gn4zhsHTEcCYgXPhv6BAxzAjleS8XKpo+Y582vTTA9nuqYDmbRuA==}
+ hasBin: true
+
+ tweetnacl@0.14.5:
+ resolution: {integrity: sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==}
+
+ type-check@0.4.0:
+ resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==}
+ engines: {node: '>= 0.8.0'}
+
+ type-fest@0.20.2:
+ resolution: {integrity: sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==}
+ engines: {node: '>=10'}
+
+ type-fest@0.21.3:
+ resolution: {integrity: sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==}
+ engines: {node: '>=10'}
+
+ typed-array-buffer@1.0.2:
+ resolution: {integrity: sha512-gEymJYKZtKXzzBzM4jqa9w6Q1Jjm7x2d+sh19AdsD4wqnMPDYyvwpsIc2Q/835kHuo3BEQ7CjelGhfTsoBb2MQ==}
+ engines: {node: '>= 0.4'}
+
+ typed-array-byte-length@1.0.1:
+ resolution: {integrity: sha512-3iMJ9q0ao7WE9tWcaYKIptkNBuOIcZCCT0d4MRvuuH88fEoEH62IuQe0OtraD3ebQEoTRk8XCBoknUNc1Y67pw==}
+ engines: {node: '>= 0.4'}
+
+ typed-array-byte-offset@1.0.2:
+ resolution: {integrity: sha512-Ous0vodHa56FviZucS2E63zkgtgrACj7omjwd/8lTEMEPFFyjfixMZ1ZXenpgCFBBt4EC1J2XsyVS2gkG0eTFA==}
+ engines: {node: '>= 0.4'}
+
+ typed-array-length@1.0.6:
+ resolution: {integrity: sha512-/OxDN6OtAk5KBpGb28T+HZc2M+ADtvRxXrKKbUwtsLgdoxgX13hyy7ek6bFRl5+aBs2yZzB0c4CnQfAtVypW/g==}
+ engines: {node: '>= 0.4'}
+
+ typescript@5.8.3:
+ resolution: {integrity: sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==}
+ engines: {node: '>=14.17'}
+ hasBin: true
+
+ unbox-primitive@1.0.2:
+ resolution: {integrity: sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==}
+
+ undefsafe@2.0.5:
+ resolution: {integrity: sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==}
+
+ undici-types@6.21.0:
+ resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==}
+
+ universalify@0.1.2:
+ resolution: {integrity: sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==}
+ engines: {node: '>= 4.0.0'}
+
+ universalify@2.0.1:
+ resolution: {integrity: sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==}
+ engines: {node: '>= 10.0.0'}
+
+ untildify@4.0.0:
+ resolution: {integrity: sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw==}
+ engines: {node: '>=8'}
+
+ update-browserslist-db@1.1.3:
+ resolution: {integrity: sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==}
+ hasBin: true
+ peerDependencies:
+ browserslist: '>= 4.21.0'
+
+ uri-js@4.4.1:
+ resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==}
+
+ use-intl@3.26.5:
+ resolution: {integrity: sha512-OdsJnC/znPvHCHLQH/duvQNXnP1w0hPfS+tkSi3mAbfjYBGh4JnyfdwkQBfIVf7t8gs9eSX/CntxUMvtKdG2MQ==}
+ peerDependencies:
+ react: ^16.8.0 || ^17.0.0 || ^18.0.0 || >=19.0.0-rc <19.0.0 || ^19.0.0
+
+ util-deprecate@1.0.2:
+ resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==}
+
+ uuid@11.1.0:
+ resolution: {integrity: sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A==}
+ hasBin: true
+
+ uuid@8.3.2:
+ resolution: {integrity: sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==}
+ hasBin: true
+
+ verror@1.10.0:
+ resolution: {integrity: sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw==}
+ engines: {'0': node >=0.6.0}
+
+ vite-node@3.1.2:
+ resolution: {integrity: sha512-/8iMryv46J3aK13iUXsei5G/A3CUlW4665THCPS+K8xAaqrVWiGB4RfXMQXCLjpK9P2eK//BczrVkn5JLAk6DA==}
+ engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0}
+ hasBin: true
+
+ vite-tsconfig-paths@5.1.4:
+ resolution: {integrity: sha512-cYj0LRuLV2c2sMqhqhGpaO3LretdtMn/BVX4cPLanIZuwwrkVl+lK84E/miEXkCHWXuq65rhNN4rXsBcOB3S4w==}
+ peerDependencies:
+ vite: '*'
+ peerDependenciesMeta:
+ vite:
+ optional: true
+
+ vite@6.3.2:
+ resolution: {integrity: sha512-ZSvGOXKGceizRQIZSz7TGJ0pS3QLlVY/9hwxVh17W3re67je1RKYzFHivZ/t0tubU78Vkyb9WnHPENSBCzbckg==}
+ engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0}
+ hasBin: true
+ 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
+
+ vitest@3.1.2:
+ resolution: {integrity: sha512-WaxpJe092ID1C0mr+LH9MmNrhfzi8I65EX/NRU/Ld016KqQNRgxSOlGNP1hHN+a/F8L15Mh8klwaF77zR3GeDQ==}
+ engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0}
+ hasBin: true
+ peerDependencies:
+ '@edge-runtime/vm': '*'
+ '@types/debug': ^4.1.12
+ '@types/node': ^18.0.0 || ^20.0.0 || >=22.0.0
+ '@vitest/browser': 3.1.2
+ '@vitest/ui': 3.1.2
+ happy-dom: '*'
+ jsdom: '*'
+ peerDependenciesMeta:
+ '@edge-runtime/vm':
+ optional: true
+ '@types/debug':
+ optional: true
+ '@types/node':
+ optional: true
+ '@vitest/browser':
+ optional: true
+ '@vitest/ui':
+ optional: true
+ happy-dom:
+ optional: true
+ jsdom:
+ optional: true
+
+ w3c-xmlserializer@5.0.0:
+ resolution: {integrity: sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==}
+ engines: {node: '>=18'}
+
+ wait-on@8.0.3:
+ resolution: {integrity: sha512-nQFqAFzZDeRxsu7S3C7LbuxslHhk+gnJZHyethuGKAn2IVleIbTB9I3vJSQiSR+DifUqmdzfPMoMPJfLqMF2vw==}
+ engines: {node: '>=12.0.0'}
+ hasBin: true
+
+ web-streams-polyfill@3.3.3:
+ resolution: {integrity: sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==}
+ engines: {node: '>= 8'}
+
+ webidl-conversions@3.0.1:
+ resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==}
+
+ webidl-conversions@4.0.2:
+ resolution: {integrity: sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==}
+
+ webidl-conversions@7.0.0:
+ resolution: {integrity: sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==}
+ engines: {node: '>=12'}
+
+ whatwg-encoding@3.1.1:
+ resolution: {integrity: sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==}
+ engines: {node: '>=18'}
+
+ whatwg-mimetype@4.0.0:
+ resolution: {integrity: sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==}
+ engines: {node: '>=18'}
+
+ whatwg-url@14.2.0:
+ resolution: {integrity: sha512-De72GdQZzNTUBBChsXueQUnPKDkg/5A5zp7pFDuQAj5UFoENpiACU0wlCvzpAGnTkj++ihpKwKyYewn/XNUbKw==}
+ engines: {node: '>=18'}
+
+ whatwg-url@5.0.0:
+ resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==}
+
+ whatwg-url@7.1.0:
+ resolution: {integrity: sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg==}
+
+ which-boxed-primitive@1.0.2:
+ resolution: {integrity: sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==}
+
+ which-builtin-type@1.1.4:
+ resolution: {integrity: sha512-bppkmBSsHFmIMSl8BO9TbsyzsvGjVoppt8xUiGzwiu/bhDCGxnpOKCxgqj6GuyHE0mINMDecBFPlOm2hzY084w==}
+ engines: {node: '>= 0.4'}
+
+ which-collection@1.0.2:
+ resolution: {integrity: sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==}
+ engines: {node: '>= 0.4'}
+
+ which-typed-array@1.1.15:
+ resolution: {integrity: sha512-oV0jmFtUky6CXfkqehVvBP/LSWJ2sy4vWMioiENyJLePrBO/yKyV9OyJySfAKosh+RYkIl5zJCNZ8/4JncrpdA==}
+ engines: {node: '>= 0.4'}
+
+ which@2.0.2:
+ resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==}
+ engines: {node: '>= 8'}
+ hasBin: true
+
+ why-is-node-running@2.3.0:
+ resolution: {integrity: sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==}
+ engines: {node: '>=8'}
+ hasBin: true
+
+ wide-align@1.1.5:
+ resolution: {integrity: sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==}
+
+ word-wrap@1.2.5:
+ resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==}
+ engines: {node: '>=0.10.0'}
+
+ wrap-ansi@6.2.0:
+ resolution: {integrity: sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==}
+ engines: {node: '>=8'}
+
+ wrap-ansi@7.0.0:
+ resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==}
+ engines: {node: '>=10'}
+
+ wrap-ansi@8.1.0:
+ resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==}
+ engines: {node: '>=12'}
+
+ wrap-ansi@9.0.0:
+ resolution: {integrity: sha512-G8ura3S+3Z2G+mkgNRq8dqaFZAuxfsxpBB8OCTGRTCtp+l/v9nbFNmCUP1BZMts3G1142MsZfn6eeUKrr4PD1Q==}
+ engines: {node: '>=18'}
+
+ wrappy@1.0.2:
+ resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==}
+
+ ws@8.18.1:
+ resolution: {integrity: sha512-RKW2aJZMXeMxVpnZ6bck+RswznaxmzdULiBr6KY7XkTnW8uvt0iT9H5DkHUChXrc+uurzwa0rVI16n/Xzjdz1w==}
+ engines: {node: '>=10.0.0'}
+ peerDependencies:
+ bufferutil: ^4.0.1
+ utf-8-validate: '>=5.0.2'
+ peerDependenciesMeta:
+ bufferutil:
+ optional: true
+ utf-8-validate:
+ optional: true
+
+ xml-name-validator@5.0.0:
+ resolution: {integrity: sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==}
+ engines: {node: '>=18'}
+
+ xmlchars@2.2.0:
+ resolution: {integrity: sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==}
+
+ y18n@5.0.8:
+ resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==}
+ engines: {node: '>=10'}
+
+ yallist@3.1.1:
+ resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==}
+
+ yallist@4.0.0:
+ resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==}
+
+ yaml@2.7.1:
+ resolution: {integrity: sha512-10ULxpnOCQXxJvBgxsn9ptjq6uviG/htZKk9veJGhlqn3w/DxQ631zFF+nlQXLwmImeS5amR2dl2U8sg6U9jsQ==}
+ engines: {node: '>= 14'}
+ hasBin: true
+
+ yargs-parser@21.1.1:
+ resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==}
+ engines: {node: '>=12'}
+
+ yargs@17.7.2:
+ resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==}
+ engines: {node: '>=12'}
+
+ yauzl@2.10.0:
+ resolution: {integrity: sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==}
+
+ yocto-queue@0.1.0:
+ resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==}
+ engines: {node: '>=10'}
+
+snapshots:
+
+ '@adobe/css-tools@4.4.0': {}
+
+ '@alloc/quick-lru@5.2.0': {}
+
+ '@ampproject/remapping@2.3.0':
+ dependencies:
+ '@jridgewell/gen-mapping': 0.3.8
+ '@jridgewell/trace-mapping': 0.3.25
+
+ '@asamuzakjp/css-color@3.1.4':
+ dependencies:
+ '@csstools/css-calc': 2.1.3(@csstools/css-parser-algorithms@3.0.4(@csstools/css-tokenizer@3.0.3))(@csstools/css-tokenizer@3.0.3)
+ '@csstools/css-color-parser': 3.0.9(@csstools/css-parser-algorithms@3.0.4(@csstools/css-tokenizer@3.0.3))(@csstools/css-tokenizer@3.0.3)
+ '@csstools/css-parser-algorithms': 3.0.4(@csstools/css-tokenizer@3.0.3)
+ '@csstools/css-tokenizer': 3.0.3
+ lru-cache: 10.4.3
+
+ '@babel/code-frame@7.26.2':
+ dependencies:
+ '@babel/helper-validator-identifier': 7.25.9
+ js-tokens: 4.0.0
+ picocolors: 1.1.1
+
+ '@babel/compat-data@7.26.8': {}
+
+ '@babel/core@7.26.10':
+ dependencies:
+ '@ampproject/remapping': 2.3.0
+ '@babel/code-frame': 7.26.2
+ '@babel/generator': 7.27.0
+ '@babel/helper-compilation-targets': 7.27.0
+ '@babel/helper-module-transforms': 7.26.0(@babel/core@7.26.10)
+ '@babel/helpers': 7.27.0
+ '@babel/parser': 7.27.0
+ '@babel/template': 7.27.0
+ '@babel/traverse': 7.27.0
+ '@babel/types': 7.27.0
+ convert-source-map: 2.0.0
+ debug: 4.4.0(supports-color@5.5.0)
+ gensync: 1.0.0-beta.2
+ json5: 2.2.3
+ semver: 6.3.1
+ transitivePeerDependencies:
+ - supports-color
+
+ '@babel/eslint-parser@7.25.9(@babel/core@7.26.10)(eslint@8.57.1)':
+ dependencies:
+ '@babel/core': 7.26.10
+ '@nicolo-ribaudo/eslint-scope-5-internals': 5.1.1-v1
+ eslint: 8.57.1
+ eslint-visitor-keys: 2.1.0
+ semver: 6.3.1
+
+ '@babel/generator@7.27.0':
+ dependencies:
+ '@babel/parser': 7.27.0
+ '@babel/types': 7.27.0
+ '@jridgewell/gen-mapping': 0.3.8
+ '@jridgewell/trace-mapping': 0.3.25
+ jsesc: 3.1.0
+
+ '@babel/helper-compilation-targets@7.27.0':
+ dependencies:
+ '@babel/compat-data': 7.26.8
+ '@babel/helper-validator-option': 7.25.9
+ browserslist: 4.24.4
+ lru-cache: 5.1.1
+ semver: 6.3.1
+
+ '@babel/helper-module-imports@7.25.9':
+ dependencies:
+ '@babel/traverse': 7.27.0
+ '@babel/types': 7.27.0
+ transitivePeerDependencies:
+ - supports-color
+
+ '@babel/helper-module-transforms@7.26.0(@babel/core@7.26.10)':
+ dependencies:
+ '@babel/core': 7.26.10
+ '@babel/helper-module-imports': 7.25.9
+ '@babel/helper-validator-identifier': 7.25.9
+ '@babel/traverse': 7.27.0
+ transitivePeerDependencies:
+ - supports-color
+
+ '@babel/helper-plugin-utils@7.26.5': {}
+
+ '@babel/helper-string-parser@7.25.9': {}
+
+ '@babel/helper-validator-identifier@7.25.9': {}
+
+ '@babel/helper-validator-option@7.25.9': {}
+
+ '@babel/helpers@7.27.0':
+ dependencies:
+ '@babel/template': 7.27.0
+ '@babel/types': 7.27.0
+
+ '@babel/parser@7.27.0':
+ dependencies:
+ '@babel/types': 7.27.0
+
+ '@babel/plugin-transform-react-jsx-self@7.25.9(@babel/core@7.26.10)':
+ dependencies:
+ '@babel/core': 7.26.10
+ '@babel/helper-plugin-utils': 7.26.5
+
+ '@babel/plugin-transform-react-jsx-source@7.25.9(@babel/core@7.26.10)':
+ dependencies:
+ '@babel/core': 7.26.10
+ '@babel/helper-plugin-utils': 7.26.5
+
+ '@babel/runtime@7.27.0':
+ dependencies:
+ regenerator-runtime: 0.14.1
+
+ '@babel/template@7.27.0':
+ dependencies:
+ '@babel/code-frame': 7.26.2
+ '@babel/parser': 7.27.0
+ '@babel/types': 7.27.0
+
+ '@babel/traverse@7.27.0':
+ dependencies:
+ '@babel/code-frame': 7.26.2
+ '@babel/generator': 7.27.0
+ '@babel/parser': 7.27.0
+ '@babel/template': 7.27.0
+ '@babel/types': 7.27.0
+ debug: 4.4.0(supports-color@5.5.0)
+ globals: 11.12.0
+ transitivePeerDependencies:
+ - supports-color
+
+ '@babel/types@7.27.0':
+ dependencies:
+ '@babel/helper-string-parser': 7.25.9
+ '@babel/helper-validator-identifier': 7.25.9
+
+ '@bufbuild/buf-darwin-arm64@1.53.0':
+ optional: true
+
+ '@bufbuild/buf-darwin-x64@1.53.0':
+ optional: true
+
+ '@bufbuild/buf-linux-aarch64@1.53.0':
+ optional: true
+
+ '@bufbuild/buf-linux-armv7@1.53.0':
+ optional: true
+
+ '@bufbuild/buf-linux-x64@1.53.0':
+ optional: true
+
+ '@bufbuild/buf-win32-arm64@1.53.0':
+ optional: true
+
+ '@bufbuild/buf-win32-x64@1.53.0':
+ optional: true
+
+ '@bufbuild/buf@1.53.0':
+ optionalDependencies:
+ '@bufbuild/buf-darwin-arm64': 1.53.0
+ '@bufbuild/buf-darwin-x64': 1.53.0
+ '@bufbuild/buf-linux-aarch64': 1.53.0
+ '@bufbuild/buf-linux-armv7': 1.53.0
+ '@bufbuild/buf-linux-x64': 1.53.0
+ '@bufbuild/buf-win32-arm64': 1.53.0
+ '@bufbuild/buf-win32-x64': 1.53.0
+
+ '@bufbuild/protobuf@2.2.2': {}
+
+ '@bufbuild/protobuf@2.2.5': {}
+
+ '@bufbuild/protocompile@0.0.1(@bufbuild/buf@1.53.0)':
+ dependencies:
+ '@bufbuild/buf': 1.53.0
+ '@bufbuild/protobuf': 2.2.2
+ fflate: 0.8.2
+
+ '@changesets/apply-release-plan@7.0.12':
+ dependencies:
+ '@changesets/config': 3.1.1
+ '@changesets/get-version-range-type': 0.4.0
+ '@changesets/git': 3.0.4
+ '@changesets/should-skip-package': 0.1.2
+ '@changesets/types': 6.1.0
+ '@manypkg/get-packages': 1.1.3
+ detect-indent: 6.1.0
+ fs-extra: 7.0.1
+ lodash.startcase: 4.4.0
+ outdent: 0.5.0
+ prettier: 2.8.8
+ resolve-from: 5.0.0
+ semver: 7.7.1
+
+ '@changesets/assemble-release-plan@6.0.6':
+ dependencies:
+ '@changesets/errors': 0.2.0
+ '@changesets/get-dependents-graph': 2.1.3
+ '@changesets/should-skip-package': 0.1.2
+ '@changesets/types': 6.1.0
+ '@manypkg/get-packages': 1.1.3
+ semver: 7.7.1
+
+ '@changesets/changelog-git@0.2.1':
+ dependencies:
+ '@changesets/types': 6.1.0
+
+ '@changesets/cli@2.29.2':
+ dependencies:
+ '@changesets/apply-release-plan': 7.0.12
+ '@changesets/assemble-release-plan': 6.0.6
+ '@changesets/changelog-git': 0.2.1
+ '@changesets/config': 3.1.1
+ '@changesets/errors': 0.2.0
+ '@changesets/get-dependents-graph': 2.1.3
+ '@changesets/get-release-plan': 4.0.10
+ '@changesets/git': 3.0.4
+ '@changesets/logger': 0.1.1
+ '@changesets/pre': 2.0.2
+ '@changesets/read': 0.6.5
+ '@changesets/should-skip-package': 0.1.2
+ '@changesets/types': 6.1.0
+ '@changesets/write': 0.4.0
+ '@manypkg/get-packages': 1.1.3
+ ansi-colors: 4.1.3
+ ci-info: 3.9.0
+ enquirer: 2.4.1
+ external-editor: 3.1.0
+ fs-extra: 7.0.1
+ mri: 1.2.0
+ p-limit: 2.3.0
+ package-manager-detector: 0.2.11
+ picocolors: 1.1.1
+ resolve-from: 5.0.0
+ semver: 7.7.1
+ spawndamnit: 3.0.1
+ term-size: 2.2.1
+
+ '@changesets/config@3.1.1':
+ dependencies:
+ '@changesets/errors': 0.2.0
+ '@changesets/get-dependents-graph': 2.1.3
+ '@changesets/logger': 0.1.1
+ '@changesets/types': 6.1.0
+ '@manypkg/get-packages': 1.1.3
+ fs-extra: 7.0.1
+ micromatch: 4.0.8
+
+ '@changesets/errors@0.2.0':
+ dependencies:
+ extendable-error: 0.1.7
+
+ '@changesets/get-dependents-graph@2.1.3':
+ dependencies:
+ '@changesets/types': 6.1.0
+ '@manypkg/get-packages': 1.1.3
+ picocolors: 1.1.1
+ semver: 7.7.1
+
+ '@changesets/get-release-plan@4.0.10':
+ dependencies:
+ '@changesets/assemble-release-plan': 6.0.6
+ '@changesets/config': 3.1.1
+ '@changesets/pre': 2.0.2
+ '@changesets/read': 0.6.5
+ '@changesets/types': 6.1.0
+ '@manypkg/get-packages': 1.1.3
+
+ '@changesets/get-version-range-type@0.4.0': {}
+
+ '@changesets/git@3.0.4':
+ dependencies:
+ '@changesets/errors': 0.2.0
+ '@manypkg/get-packages': 1.1.3
+ is-subdir: 1.2.0
+ micromatch: 4.0.8
+ spawndamnit: 3.0.1
+
+ '@changesets/logger@0.1.1':
+ dependencies:
+ picocolors: 1.1.1
+
+ '@changesets/parse@0.4.1':
+ dependencies:
+ '@changesets/types': 6.1.0
+ js-yaml: 3.14.1
+
+ '@changesets/pre@2.0.2':
+ dependencies:
+ '@changesets/errors': 0.2.0
+ '@changesets/types': 6.1.0
+ '@manypkg/get-packages': 1.1.3
+ fs-extra: 7.0.1
+
+ '@changesets/read@0.6.5':
+ dependencies:
+ '@changesets/git': 3.0.4
+ '@changesets/logger': 0.1.1
+ '@changesets/parse': 0.4.1
+ '@changesets/types': 6.1.0
+ fs-extra: 7.0.1
+ p-filter: 2.1.0
+ picocolors: 1.1.1
+
+ '@changesets/should-skip-package@0.1.2':
+ dependencies:
+ '@changesets/types': 6.1.0
+ '@manypkg/get-packages': 1.1.3
+
+ '@changesets/types@4.1.0': {}
+
+ '@changesets/types@6.1.0': {}
+
+ '@changesets/write@0.4.0':
+ dependencies:
+ '@changesets/types': 6.1.0
+ fs-extra: 7.0.1
+ human-id: 4.1.1
+ prettier: 2.8.8
+
+ '@colors/colors@1.5.0':
+ optional: true
+
+ '@connectrpc/connect-node@2.0.0(@bufbuild/protobuf@2.2.2)(@connectrpc/connect@2.0.0(@bufbuild/protobuf@2.2.2))':
+ dependencies:
+ '@bufbuild/protobuf': 2.2.2
+ '@connectrpc/connect': 2.0.0(@bufbuild/protobuf@2.2.2)
+
+ '@connectrpc/connect-web@2.0.0(@bufbuild/protobuf@2.2.2)(@connectrpc/connect@2.0.0(@bufbuild/protobuf@2.2.2))':
+ dependencies:
+ '@bufbuild/protobuf': 2.2.2
+ '@connectrpc/connect': 2.0.0(@bufbuild/protobuf@2.2.2)
+
+ '@connectrpc/connect@2.0.0(@bufbuild/protobuf@2.2.2)':
+ dependencies:
+ '@bufbuild/protobuf': 2.2.2
+
+ '@csstools/color-helpers@5.0.2': {}
+
+ '@csstools/css-calc@2.1.3(@csstools/css-parser-algorithms@3.0.4(@csstools/css-tokenizer@3.0.3))(@csstools/css-tokenizer@3.0.3)':
+ dependencies:
+ '@csstools/css-parser-algorithms': 3.0.4(@csstools/css-tokenizer@3.0.3)
+ '@csstools/css-tokenizer': 3.0.3
+
+ '@csstools/css-color-parser@3.0.9(@csstools/css-parser-algorithms@3.0.4(@csstools/css-tokenizer@3.0.3))(@csstools/css-tokenizer@3.0.3)':
+ dependencies:
+ '@csstools/color-helpers': 5.0.2
+ '@csstools/css-calc': 2.1.3(@csstools/css-parser-algorithms@3.0.4(@csstools/css-tokenizer@3.0.3))(@csstools/css-tokenizer@3.0.3)
+ '@csstools/css-parser-algorithms': 3.0.4(@csstools/css-tokenizer@3.0.3)
+ '@csstools/css-tokenizer': 3.0.3
+
+ '@csstools/css-parser-algorithms@3.0.4(@csstools/css-tokenizer@3.0.3)':
+ dependencies:
+ '@csstools/css-tokenizer': 3.0.3
+
+ '@csstools/css-tokenizer@3.0.3': {}
+
+ '@cypress/request@3.0.8':
+ dependencies:
+ aws-sign2: 0.7.0
+ aws4: 1.13.2
+ caseless: 0.12.0
+ combined-stream: 1.0.8
+ extend: 3.0.2
+ forever-agent: 0.6.1
+ form-data: 4.0.2
+ http-signature: 1.4.0
+ is-typedarray: 1.0.0
+ isstream: 0.1.2
+ json-stringify-safe: 5.0.1
+ mime-types: 2.1.35
+ performance-now: 2.1.0
+ qs: 6.14.0
+ safe-buffer: 5.2.1
+ tough-cookie: 5.1.2
+ tunnel-agent: 0.6.0
+ uuid: 8.3.2
+
+ '@cypress/xvfb@1.2.4(supports-color@8.1.1)':
+ dependencies:
+ debug: 3.2.7(supports-color@8.1.1)
+ lodash.once: 4.1.1
+ transitivePeerDependencies:
+ - supports-color
+
+ '@emnapi/runtime@1.4.3':
+ dependencies:
+ tslib: 2.8.1
+ optional: true
+
+ '@esbuild/aix-ppc64@0.25.2':
+ optional: true
+
+ '@esbuild/android-arm64@0.25.2':
+ optional: true
+
+ '@esbuild/android-arm@0.25.2':
+ optional: true
+
+ '@esbuild/android-x64@0.25.2':
+ optional: true
+
+ '@esbuild/darwin-arm64@0.25.2':
+ optional: true
+
+ '@esbuild/darwin-x64@0.25.2':
+ optional: true
+
+ '@esbuild/freebsd-arm64@0.25.2':
+ optional: true
+
+ '@esbuild/freebsd-x64@0.25.2':
+ optional: true
+
+ '@esbuild/linux-arm64@0.25.2':
+ optional: true
+
+ '@esbuild/linux-arm@0.25.2':
+ optional: true
+
+ '@esbuild/linux-ia32@0.25.2':
+ optional: true
+
+ '@esbuild/linux-loong64@0.25.2':
+ optional: true
+
+ '@esbuild/linux-mips64el@0.25.2':
+ optional: true
+
+ '@esbuild/linux-ppc64@0.25.2':
+ optional: true
+
+ '@esbuild/linux-riscv64@0.25.2':
+ optional: true
+
+ '@esbuild/linux-s390x@0.25.2':
+ optional: true
+
+ '@esbuild/linux-x64@0.25.2':
+ optional: true
+
+ '@esbuild/netbsd-arm64@0.25.2':
+ optional: true
+
+ '@esbuild/netbsd-x64@0.25.2':
+ optional: true
+
+ '@esbuild/openbsd-arm64@0.25.2':
+ optional: true
+
+ '@esbuild/openbsd-x64@0.25.2':
+ optional: true
+
+ '@esbuild/sunos-x64@0.25.2':
+ optional: true
+
+ '@esbuild/win32-arm64@0.25.2':
+ optional: true
+
+ '@esbuild/win32-ia32@0.25.2':
+ optional: true
+
+ '@esbuild/win32-x64@0.25.2':
+ optional: true
+
+ '@eslint-community/eslint-utils@4.4.0(eslint@8.57.1)':
+ dependencies:
+ eslint: 8.57.1
+ eslint-visitor-keys: 3.4.3
+
+ '@eslint-community/eslint-utils@4.4.1(eslint@8.57.1)':
+ dependencies:
+ eslint: 8.57.1
+ eslint-visitor-keys: 3.4.3
+
+ '@eslint-community/regexpp@4.11.1': {}
+
+ '@eslint-community/regexpp@4.12.1': {}
+
+ '@eslint/eslintrc@2.1.4':
+ dependencies:
+ ajv: 6.12.6
+ debug: 4.4.0(supports-color@5.5.0)
+ espree: 9.6.1
+ globals: 13.24.0
+ ignore: 5.3.2
+ import-fresh: 3.3.0
+ js-yaml: 4.1.0
+ minimatch: 3.1.2
+ strip-json-comments: 3.1.1
+ transitivePeerDependencies:
+ - supports-color
+
+ '@eslint/js@8.57.1': {}
+
+ '@faker-js/faker@9.7.0': {}
+
+ '@floating-ui/core@1.6.8':
+ dependencies:
+ '@floating-ui/utils': 0.2.8
+
+ '@floating-ui/dom@1.6.11':
+ dependencies:
+ '@floating-ui/core': 1.6.8
+ '@floating-ui/utils': 0.2.8
+
+ '@floating-ui/react-dom@2.1.2(react-dom@19.1.0(react@19.1.0))(react@19.1.0)':
+ dependencies:
+ '@floating-ui/dom': 1.6.11
+ react: 19.1.0
+ react-dom: 19.1.0(react@19.1.0)
+
+ '@floating-ui/react@0.26.24(react-dom@19.1.0(react@19.1.0))(react@19.1.0)':
+ dependencies:
+ '@floating-ui/react-dom': 2.1.2(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+ '@floating-ui/utils': 0.2.8
+ react: 19.1.0
+ react-dom: 19.1.0(react@19.1.0)
+ tabbable: 6.2.0
+
+ '@floating-ui/utils@0.2.8': {}
+
+ '@formatjs/ecma402-abstract@2.2.4':
+ dependencies:
+ '@formatjs/fast-memoize': 2.2.3
+ '@formatjs/intl-localematcher': 0.5.8
+ tslib: 2.8.1
+
+ '@formatjs/fast-memoize@2.2.3':
+ dependencies:
+ tslib: 2.8.1
+
+ '@formatjs/icu-messageformat-parser@2.9.4':
+ dependencies:
+ '@formatjs/ecma402-abstract': 2.2.4
+ '@formatjs/icu-skeleton-parser': 1.8.8
+ tslib: 2.8.1
+
+ '@formatjs/icu-skeleton-parser@1.8.8':
+ dependencies:
+ '@formatjs/ecma402-abstract': 2.2.4
+ tslib: 2.8.1
+
+ '@formatjs/intl-localematcher@0.5.8':
+ dependencies:
+ tslib: 2.8.1
+
+ '@grpc/grpc-js@1.11.1':
+ dependencies:
+ '@grpc/proto-loader': 0.7.13
+ '@js-sdsl/ordered-map': 4.4.2
+
+ '@grpc/proto-loader@0.7.13':
+ dependencies:
+ lodash.camelcase: 4.3.0
+ long: 5.2.3
+ protobufjs: 7.4.0
+ yargs: 17.7.2
+
+ '@hapi/hoek@9.3.0': {}
+
+ '@hapi/topo@5.1.0':
+ dependencies:
+ '@hapi/hoek': 9.3.0
+
+ '@headlessui/react@2.1.9(react-dom@19.1.0(react@19.1.0))(react@19.1.0)':
+ dependencies:
+ '@floating-ui/react': 0.26.24(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+ '@react-aria/focus': 3.18.3(react@19.1.0)
+ '@react-aria/interactions': 3.22.3(react@19.1.0)
+ '@tanstack/react-virtual': 3.10.6(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+ react: 19.1.0
+ react-dom: 19.1.0(react@19.1.0)
+
+ '@heroicons/react@2.1.3(react@19.1.0)':
+ dependencies:
+ react: 19.1.0
+
+ '@humanwhocodes/config-array@0.13.0':
+ dependencies:
+ '@humanwhocodes/object-schema': 2.0.3
+ debug: 4.4.0(supports-color@5.5.0)
+ minimatch: 3.1.2
+ transitivePeerDependencies:
+ - supports-color
+
+ '@humanwhocodes/module-importer@1.0.1': {}
+
+ '@humanwhocodes/object-schema@2.0.3': {}
+
+ '@img/sharp-darwin-arm64@0.34.1':
+ optionalDependencies:
+ '@img/sharp-libvips-darwin-arm64': 1.1.0
+ optional: true
+
+ '@img/sharp-darwin-x64@0.34.1':
+ optionalDependencies:
+ '@img/sharp-libvips-darwin-x64': 1.1.0
+ optional: true
+
+ '@img/sharp-libvips-darwin-arm64@1.1.0':
+ optional: true
+
+ '@img/sharp-libvips-darwin-x64@1.1.0':
+ optional: true
+
+ '@img/sharp-libvips-linux-arm64@1.1.0':
+ optional: true
+
+ '@img/sharp-libvips-linux-arm@1.1.0':
+ optional: true
+
+ '@img/sharp-libvips-linux-ppc64@1.1.0':
+ optional: true
+
+ '@img/sharp-libvips-linux-s390x@1.1.0':
+ optional: true
+
+ '@img/sharp-libvips-linux-x64@1.1.0':
+ optional: true
+
+ '@img/sharp-libvips-linuxmusl-arm64@1.1.0':
+ optional: true
+
+ '@img/sharp-libvips-linuxmusl-x64@1.1.0':
+ optional: true
+
+ '@img/sharp-linux-arm64@0.34.1':
+ optionalDependencies:
+ '@img/sharp-libvips-linux-arm64': 1.1.0
+ optional: true
+
+ '@img/sharp-linux-arm@0.34.1':
+ optionalDependencies:
+ '@img/sharp-libvips-linux-arm': 1.1.0
+ optional: true
+
+ '@img/sharp-linux-s390x@0.34.1':
+ optionalDependencies:
+ '@img/sharp-libvips-linux-s390x': 1.1.0
+ optional: true
+
+ '@img/sharp-linux-x64@0.34.1':
+ optionalDependencies:
+ '@img/sharp-libvips-linux-x64': 1.1.0
+ optional: true
+
+ '@img/sharp-linuxmusl-arm64@0.34.1':
+ optionalDependencies:
+ '@img/sharp-libvips-linuxmusl-arm64': 1.1.0
+ optional: true
+
+ '@img/sharp-linuxmusl-x64@0.34.1':
+ optionalDependencies:
+ '@img/sharp-libvips-linuxmusl-x64': 1.1.0
+ optional: true
+
+ '@img/sharp-wasm32@0.34.1':
+ dependencies:
+ '@emnapi/runtime': 1.4.3
+ optional: true
+
+ '@img/sharp-win32-ia32@0.34.1':
+ optional: true
+
+ '@img/sharp-win32-x64@0.34.1':
+ optional: true
+
+ '@isaacs/cliui@8.0.2':
+ dependencies:
+ string-width: 5.1.2
+ string-width-cjs: string-width@4.2.3
+ strip-ansi: 7.1.0
+ strip-ansi-cjs: strip-ansi@6.0.1
+ wrap-ansi: 8.1.0
+ wrap-ansi-cjs: wrap-ansi@7.0.0
+
+ '@jridgewell/gen-mapping@0.3.8':
+ dependencies:
+ '@jridgewell/set-array': 1.2.1
+ '@jridgewell/sourcemap-codec': 1.5.0
+ '@jridgewell/trace-mapping': 0.3.25
+
+ '@jridgewell/resolve-uri@3.1.2': {}
+
+ '@jridgewell/set-array@1.2.1': {}
+
+ '@jridgewell/sourcemap-codec@1.5.0': {}
+
+ '@jridgewell/trace-mapping@0.3.25':
+ dependencies:
+ '@jridgewell/resolve-uri': 3.1.2
+ '@jridgewell/sourcemap-codec': 1.5.0
+
+ '@js-sdsl/ordered-map@4.4.2': {}
+
+ '@manypkg/find-root@1.1.0':
+ dependencies:
+ '@babel/runtime': 7.27.0
+ '@types/node': 12.20.55
+ find-up: 4.1.0
+ fs-extra: 8.1.0
+
+ '@manypkg/get-packages@1.1.3':
+ dependencies:
+ '@babel/runtime': 7.27.0
+ '@changesets/types': 4.1.0
+ '@manypkg/find-root': 1.1.0
+ fs-extra: 8.1.0
+ globby: 11.1.0
+ read-yaml-file: 1.1.0
+
+ '@mapbox/node-pre-gyp@1.0.11':
+ dependencies:
+ detect-libc: 2.0.4
+ https-proxy-agent: 5.0.1
+ make-dir: 3.1.0
+ node-fetch: 2.7.0
+ nopt: 5.0.0
+ npmlog: 5.0.1
+ rimraf: 3.0.2
+ semver: 7.7.1
+ tar: 6.2.1
+ transitivePeerDependencies:
+ - encoding
+ - supports-color
+
+ '@next/env@15.4.0-canary.86': {}
+
+ '@next/eslint-plugin-next@14.2.18':
+ dependencies:
+ glob: 10.3.10
+
+ '@next/swc-darwin-arm64@15.4.0-canary.86':
+ optional: true
+
+ '@next/swc-darwin-x64@15.4.0-canary.86':
+ optional: true
+
+ '@next/swc-linux-arm64-gnu@15.4.0-canary.86':
+ optional: true
+
+ '@next/swc-linux-arm64-musl@15.4.0-canary.86':
+ optional: true
+
+ '@next/swc-linux-x64-gnu@15.4.0-canary.86':
+ optional: true
+
+ '@next/swc-linux-x64-musl@15.4.0-canary.86':
+ optional: true
+
+ '@next/swc-win32-arm64-msvc@15.4.0-canary.86':
+ optional: true
+
+ '@next/swc-win32-x64-msvc@15.4.0-canary.86':
+ optional: true
+
+ '@nicolo-ribaudo/eslint-scope-5-internals@5.1.1-v1':
+ dependencies:
+ eslint-scope: 5.1.1
+
+ '@nodelib/fs.scandir@2.1.5':
+ dependencies:
+ '@nodelib/fs.stat': 2.0.5
+ run-parallel: 1.2.0
+
+ '@nodelib/fs.stat@2.0.5': {}
+
+ '@nodelib/fs.walk@1.2.8':
+ dependencies:
+ '@nodelib/fs.scandir': 2.1.5
+ fastq: 1.17.1
+
+ '@nolyfill/is-core-module@1.0.39': {}
+
+ '@otplib/core@12.0.1': {}
+
+ '@otplib/plugin-crypto@12.0.1':
+ dependencies:
+ '@otplib/core': 12.0.1
+
+ '@otplib/plugin-thirty-two@12.0.1':
+ dependencies:
+ '@otplib/core': 12.0.1
+ thirty-two: 1.0.2
+
+ '@parcel/watcher-android-arm64@2.5.1':
+ optional: true
+
+ '@parcel/watcher-darwin-arm64@2.5.1':
+ optional: true
+
+ '@parcel/watcher-darwin-x64@2.5.1':
+ optional: true
+
+ '@parcel/watcher-freebsd-x64@2.5.1':
+ optional: true
+
+ '@parcel/watcher-linux-arm-glibc@2.5.1':
+ optional: true
+
+ '@parcel/watcher-linux-arm-musl@2.5.1':
+ optional: true
+
+ '@parcel/watcher-linux-arm64-glibc@2.5.1':
+ optional: true
+
+ '@parcel/watcher-linux-arm64-musl@2.5.1':
+ optional: true
+
+ '@parcel/watcher-linux-x64-glibc@2.5.1':
+ optional: true
+
+ '@parcel/watcher-linux-x64-musl@2.5.1':
+ optional: true
+
+ '@parcel/watcher-win32-arm64@2.5.1':
+ optional: true
+
+ '@parcel/watcher-win32-ia32@2.5.1':
+ optional: true
+
+ '@parcel/watcher-win32-x64@2.5.1':
+ optional: true
+
+ '@parcel/watcher@2.5.1':
+ dependencies:
+ detect-libc: 1.0.3
+ is-glob: 4.0.3
+ micromatch: 4.0.8
+ node-addon-api: 7.1.1
+ optionalDependencies:
+ '@parcel/watcher-android-arm64': 2.5.1
+ '@parcel/watcher-darwin-arm64': 2.5.1
+ '@parcel/watcher-darwin-x64': 2.5.1
+ '@parcel/watcher-freebsd-x64': 2.5.1
+ '@parcel/watcher-linux-arm-glibc': 2.5.1
+ '@parcel/watcher-linux-arm-musl': 2.5.1
+ '@parcel/watcher-linux-arm64-glibc': 2.5.1
+ '@parcel/watcher-linux-arm64-musl': 2.5.1
+ '@parcel/watcher-linux-x64-glibc': 2.5.1
+ '@parcel/watcher-linux-x64-musl': 2.5.1
+ '@parcel/watcher-win32-arm64': 2.5.1
+ '@parcel/watcher-win32-ia32': 2.5.1
+ '@parcel/watcher-win32-x64': 2.5.1
+ optional: true
+
+ '@pkgjs/parseargs@0.11.0':
+ optional: true
+
+ '@playwright/test@1.52.0':
+ dependencies:
+ playwright: 1.52.0
+
+ '@protobufjs/aspromise@1.1.2': {}
+
+ '@protobufjs/base64@1.1.2': {}
+
+ '@protobufjs/codegen@2.0.4': {}
+
+ '@protobufjs/eventemitter@1.1.0': {}
+
+ '@protobufjs/fetch@1.1.0':
+ dependencies:
+ '@protobufjs/aspromise': 1.1.2
+ '@protobufjs/inquire': 1.1.0
+
+ '@protobufjs/float@1.0.2': {}
+
+ '@protobufjs/inquire@1.1.0': {}
+
+ '@protobufjs/path@1.1.2': {}
+
+ '@protobufjs/pool@1.1.0': {}
+
+ '@protobufjs/utf8@1.1.0': {}
+
+ '@react-aria/focus@3.18.3(react@19.1.0)':
+ dependencies:
+ '@react-aria/interactions': 3.22.3(react@19.1.0)
+ '@react-aria/utils': 3.25.3(react@19.1.0)
+ '@react-types/shared': 3.25.0(react@19.1.0)
+ '@swc/helpers': 0.5.5
+ clsx: 2.1.1
+ react: 19.1.0
+
+ '@react-aria/interactions@3.22.3(react@19.1.0)':
+ dependencies:
+ '@react-aria/ssr': 3.9.6(react@19.1.0)
+ '@react-aria/utils': 3.25.3(react@19.1.0)
+ '@react-types/shared': 3.25.0(react@19.1.0)
+ '@swc/helpers': 0.5.5
+ react: 19.1.0
+
+ '@react-aria/ssr@3.9.6(react@19.1.0)':
+ dependencies:
+ '@swc/helpers': 0.5.15
+ react: 19.1.0
+
+ '@react-aria/utils@3.25.3(react@19.1.0)':
+ dependencies:
+ '@react-aria/ssr': 3.9.6(react@19.1.0)
+ '@react-stately/utils': 3.10.4(react@19.1.0)
+ '@react-types/shared': 3.25.0(react@19.1.0)
+ '@swc/helpers': 0.5.15
+ clsx: 2.1.1
+ react: 19.1.0
+
+ '@react-stately/utils@3.10.4(react@19.1.0)':
+ dependencies:
+ '@swc/helpers': 0.5.15
+ react: 19.1.0
+
+ '@react-types/shared@3.25.0(react@19.1.0)':
+ dependencies:
+ react: 19.1.0
+
+ '@rollup/rollup-android-arm-eabi@4.40.0':
+ optional: true
+
+ '@rollup/rollup-android-arm64@4.40.0':
+ optional: true
+
+ '@rollup/rollup-darwin-arm64@4.40.0':
+ optional: true
+
+ '@rollup/rollup-darwin-x64@4.40.0':
+ optional: true
+
+ '@rollup/rollup-freebsd-arm64@4.40.0':
+ optional: true
+
+ '@rollup/rollup-freebsd-x64@4.40.0':
+ optional: true
+
+ '@rollup/rollup-linux-arm-gnueabihf@4.40.0':
+ optional: true
+
+ '@rollup/rollup-linux-arm-musleabihf@4.40.0':
+ optional: true
+
+ '@rollup/rollup-linux-arm64-gnu@4.40.0':
+ optional: true
+
+ '@rollup/rollup-linux-arm64-musl@4.40.0':
+ optional: true
+
+ '@rollup/rollup-linux-loongarch64-gnu@4.40.0':
+ optional: true
+
+ '@rollup/rollup-linux-powerpc64le-gnu@4.40.0':
+ optional: true
+
+ '@rollup/rollup-linux-riscv64-gnu@4.40.0':
+ optional: true
+
+ '@rollup/rollup-linux-riscv64-musl@4.40.0':
+ optional: true
+
+ '@rollup/rollup-linux-s390x-gnu@4.40.0':
+ optional: true
+
+ '@rollup/rollup-linux-x64-gnu@4.40.0':
+ optional: true
+
+ '@rollup/rollup-linux-x64-musl@4.40.0':
+ optional: true
+
+ '@rollup/rollup-win32-arm64-msvc@4.40.0':
+ optional: true
+
+ '@rollup/rollup-win32-ia32-msvc@4.40.0':
+ optional: true
+
+ '@rollup/rollup-win32-x64-msvc@4.40.0':
+ optional: true
+
+ '@rushstack/eslint-patch@1.10.4': {}
+
+ '@sideway/address@4.1.5':
+ dependencies:
+ '@hapi/hoek': 9.3.0
+
+ '@sideway/formula@3.0.1': {}
+
+ '@sideway/pinpoint@2.0.0': {}
+
+ '@swc/counter@0.1.3': {}
+
+ '@swc/helpers@0.5.15':
+ dependencies:
+ tslib: 2.8.1
+
+ '@swc/helpers@0.5.5':
+ dependencies:
+ '@swc/counter': 0.1.3
+ tslib: 2.8.1
+
+ '@tailwindcss/forms@0.5.3(tailwindcss@4.1.4)':
+ dependencies:
+ mini-svg-data-uri: 1.4.4
+ tailwindcss: 4.1.4
+
+ '@tailwindcss/forms@0.5.7(tailwindcss@3.4.14)':
+ dependencies:
+ mini-svg-data-uri: 1.4.4
+ tailwindcss: 3.4.14
+
+ '@tanstack/react-virtual@3.10.6(react-dom@19.1.0(react@19.1.0))(react@19.1.0)':
+ dependencies:
+ '@tanstack/virtual-core': 3.10.6
+ react: 19.1.0
+ react-dom: 19.1.0(react@19.1.0)
+
+ '@tanstack/virtual-core@3.10.6': {}
+
+ '@testing-library/dom@10.4.0':
+ dependencies:
+ '@babel/code-frame': 7.26.2
+ '@babel/runtime': 7.27.0
+ '@types/aria-query': 5.0.4
+ aria-query: 5.3.0
+ chalk: 4.1.2
+ dom-accessibility-api: 0.5.16
+ lz-string: 1.5.0
+ pretty-format: 27.5.1
+
+ '@testing-library/jest-dom@6.6.3':
+ dependencies:
+ '@adobe/css-tools': 4.4.0
+ aria-query: 5.3.0
+ chalk: 3.0.0
+ css.escape: 1.5.1
+ dom-accessibility-api: 0.6.3
+ lodash: 4.17.21
+ redent: 3.0.0
+
+ '@testing-library/react@16.3.0(@testing-library/dom@10.4.0)(@types/react-dom@19.1.2(@types/react@19.1.2))(@types/react@19.1.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)':
+ dependencies:
+ '@babel/runtime': 7.27.0
+ '@testing-library/dom': 10.4.0
+ react: 19.1.0
+ react-dom: 19.1.0(react@19.1.0)
+ optionalDependencies:
+ '@types/react': 19.1.2
+ '@types/react-dom': 19.1.2(@types/react@19.1.2)
+
+ '@types/aria-query@5.0.4': {}
+
+ '@types/babel__core@7.20.5':
+ dependencies:
+ '@babel/parser': 7.27.0
+ '@babel/types': 7.27.0
+ '@types/babel__generator': 7.27.0
+ '@types/babel__template': 7.4.4
+ '@types/babel__traverse': 7.20.7
+
+ '@types/babel__generator@7.27.0':
+ dependencies:
+ '@babel/types': 7.27.0
+
+ '@types/babel__template@7.4.4':
+ dependencies:
+ '@babel/parser': 7.27.0
+ '@babel/types': 7.27.0
+
+ '@types/babel__traverse@7.20.7':
+ dependencies:
+ '@babel/types': 7.27.0
+
+ '@types/estree@1.0.7': {}
+
+ '@types/json5@0.0.29': {}
+
+ '@types/ms@2.1.0': {}
+
+ '@types/node@12.20.55': {}
+
+ '@types/node@22.14.1':
+ dependencies:
+ undici-types: 6.21.0
+
+ '@types/react-dom@19.1.2(@types/react@19.1.2)':
+ dependencies:
+ '@types/react': 19.1.2
+
+ '@types/react@19.1.2':
+ dependencies:
+ csstype: 3.1.3
+
+ '@types/sinonjs__fake-timers@8.1.1': {}
+
+ '@types/sizzle@2.3.9': {}
+
+ '@types/tinycolor2@1.4.3': {}
+
+ '@types/uuid@10.0.0': {}
+
+ '@types/yauzl@2.10.3':
+ dependencies:
+ '@types/node': 22.14.1
+ optional: true
+
+ '@typescript-eslint/eslint-plugin@8.15.0(@typescript-eslint/parser@7.18.0(eslint@8.57.1)(typescript@5.8.3))(eslint@8.57.1)(typescript@5.8.3)':
+ dependencies:
+ '@eslint-community/regexpp': 4.12.1
+ '@typescript-eslint/parser': 7.18.0(eslint@8.57.1)(typescript@5.8.3)
+ '@typescript-eslint/scope-manager': 8.15.0
+ '@typescript-eslint/type-utils': 8.15.0(eslint@8.57.1)(typescript@5.8.3)
+ '@typescript-eslint/utils': 8.15.0(eslint@8.57.1)(typescript@5.8.3)
+ '@typescript-eslint/visitor-keys': 8.15.0
+ eslint: 8.57.1
+ graphemer: 1.4.0
+ ignore: 5.3.2
+ natural-compare: 1.4.0
+ ts-api-utils: 1.4.1(typescript@5.8.3)
+ optionalDependencies:
+ typescript: 5.8.3
+ transitivePeerDependencies:
+ - supports-color
+
+ '@typescript-eslint/parser@7.18.0(eslint@8.57.1)(typescript@5.8.3)':
+ dependencies:
+ '@typescript-eslint/scope-manager': 7.18.0
+ '@typescript-eslint/types': 7.18.0
+ '@typescript-eslint/typescript-estree': 7.18.0(typescript@5.8.3)
+ '@typescript-eslint/visitor-keys': 7.18.0
+ debug: 4.3.7
+ eslint: 8.57.1
+ optionalDependencies:
+ typescript: 5.8.3
+ transitivePeerDependencies:
+ - supports-color
+
+ '@typescript-eslint/scope-manager@7.18.0':
+ dependencies:
+ '@typescript-eslint/types': 7.18.0
+ '@typescript-eslint/visitor-keys': 7.18.0
+
+ '@typescript-eslint/scope-manager@8.15.0':
+ dependencies:
+ '@typescript-eslint/types': 8.15.0
+ '@typescript-eslint/visitor-keys': 8.15.0
+
+ '@typescript-eslint/type-utils@8.15.0(eslint@8.57.1)(typescript@5.8.3)':
+ dependencies:
+ '@typescript-eslint/typescript-estree': 8.15.0(typescript@5.8.3)
+ '@typescript-eslint/utils': 8.15.0(eslint@8.57.1)(typescript@5.8.3)
+ debug: 4.4.0(supports-color@5.5.0)
+ eslint: 8.57.1
+ ts-api-utils: 1.4.1(typescript@5.8.3)
+ optionalDependencies:
+ typescript: 5.8.3
+ transitivePeerDependencies:
+ - supports-color
+
+ '@typescript-eslint/types@7.18.0': {}
+
+ '@typescript-eslint/types@8.15.0': {}
+
+ '@typescript-eslint/typescript-estree@7.18.0(typescript@5.8.3)':
+ dependencies:
+ '@typescript-eslint/types': 7.18.0
+ '@typescript-eslint/visitor-keys': 7.18.0
+ debug: 4.4.0(supports-color@5.5.0)
+ globby: 11.1.0
+ is-glob: 4.0.3
+ minimatch: 9.0.5
+ semver: 7.7.1
+ ts-api-utils: 1.4.1(typescript@5.8.3)
+ optionalDependencies:
+ typescript: 5.8.3
+ transitivePeerDependencies:
+ - supports-color
+
+ '@typescript-eslint/typescript-estree@8.15.0(typescript@5.8.3)':
+ dependencies:
+ '@typescript-eslint/types': 8.15.0
+ '@typescript-eslint/visitor-keys': 8.15.0
+ debug: 4.4.0(supports-color@5.5.0)
+ fast-glob: 3.3.3
+ is-glob: 4.0.3
+ minimatch: 9.0.5
+ semver: 7.7.1
+ ts-api-utils: 1.4.1(typescript@5.8.3)
+ optionalDependencies:
+ typescript: 5.8.3
+ transitivePeerDependencies:
+ - supports-color
+
+ '@typescript-eslint/utils@8.15.0(eslint@8.57.1)(typescript@5.8.3)':
+ dependencies:
+ '@eslint-community/eslint-utils': 4.4.1(eslint@8.57.1)
+ '@typescript-eslint/scope-manager': 8.15.0
+ '@typescript-eslint/types': 8.15.0
+ '@typescript-eslint/typescript-estree': 8.15.0(typescript@5.8.3)
+ eslint: 8.57.1
+ optionalDependencies:
+ typescript: 5.8.3
+ transitivePeerDependencies:
+ - supports-color
+
+ '@typescript-eslint/visitor-keys@7.18.0':
+ dependencies:
+ '@typescript-eslint/types': 7.18.0
+ eslint-visitor-keys: 3.4.3
+
+ '@typescript-eslint/visitor-keys@8.15.0':
+ dependencies:
+ '@typescript-eslint/types': 8.15.0
+ eslint-visitor-keys: 4.2.0
+
+ '@ungap/structured-clone@1.2.0': {}
+
+ '@vercel/analytics@1.3.1(next@15.4.0-canary.86(@playwright/test@1.52.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(sass@1.87.0))(react@19.1.0)':
+ dependencies:
+ server-only: 0.0.1
+ optionalDependencies:
+ next: 15.4.0-canary.86(@playwright/test@1.52.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(sass@1.87.0)
+ react: 19.1.0
+
+ '@vercel/git-hooks@1.0.0': {}
+
+ '@vitejs/plugin-react@4.4.1(vite@6.3.2(@types/node@22.14.1)(jiti@1.21.6)(sass@1.87.0)(yaml@2.7.1))':
+ dependencies:
+ '@babel/core': 7.26.10
+ '@babel/plugin-transform-react-jsx-self': 7.25.9(@babel/core@7.26.10)
+ '@babel/plugin-transform-react-jsx-source': 7.25.9(@babel/core@7.26.10)
+ '@types/babel__core': 7.20.5
+ react-refresh: 0.17.0
+ vite: 6.3.2(@types/node@22.14.1)(jiti@1.21.6)(sass@1.87.0)(yaml@2.7.1)
+ transitivePeerDependencies:
+ - supports-color
+
+ '@vitest/expect@3.1.2':
+ dependencies:
+ '@vitest/spy': 3.1.2
+ '@vitest/utils': 3.1.2
+ chai: 5.2.0
+ tinyrainbow: 2.0.0
+
+ '@vitest/mocker@3.1.2(vite@6.3.2(@types/node@22.14.1)(jiti@1.21.6)(sass@1.87.0)(yaml@2.7.1))':
+ dependencies:
+ '@vitest/spy': 3.1.2
+ estree-walker: 3.0.3
+ magic-string: 0.30.17
+ optionalDependencies:
+ vite: 6.3.2(@types/node@22.14.1)(jiti@1.21.6)(sass@1.87.0)(yaml@2.7.1)
+
+ '@vitest/pretty-format@3.1.2':
+ dependencies:
+ tinyrainbow: 2.0.0
+
+ '@vitest/runner@3.1.2':
+ dependencies:
+ '@vitest/utils': 3.1.2
+ pathe: 2.0.3
+
+ '@vitest/snapshot@3.1.2':
+ dependencies:
+ '@vitest/pretty-format': 3.1.2
+ magic-string: 0.30.17
+ pathe: 2.0.3
+
+ '@vitest/spy@3.1.2':
+ dependencies:
+ tinyspy: 3.0.2
+
+ '@vitest/utils@3.1.2':
+ dependencies:
+ '@vitest/pretty-format': 3.1.2
+ loupe: 3.1.3
+ tinyrainbow: 2.0.0
+
+ abbrev@1.1.1: {}
+
+ abort-controller-x@0.4.3: {}
+
+ acorn-jsx@5.3.2(acorn@8.12.1):
+ dependencies:
+ acorn: 8.12.1
+
+ acorn@8.12.1: {}
+
+ agent-base@6.0.2:
+ dependencies:
+ debug: 4.4.0
+ transitivePeerDependencies:
+ - supports-color
+
+ agent-base@7.1.3: {}
+
+ aggregate-error@3.1.0:
+ dependencies:
+ clean-stack: 2.2.0
+ indent-string: 4.0.0
+
+ ajv@6.12.6:
+ dependencies:
+ fast-deep-equal: 3.1.3
+ fast-json-stable-stringify: 2.1.0
+ json-schema-traverse: 0.4.1
+ uri-js: 4.4.1
+
+ ansi-colors@4.1.3: {}
+
+ ansi-escapes@4.3.2:
+ dependencies:
+ type-fest: 0.21.3
+
+ ansi-escapes@7.0.0:
+ dependencies:
+ environment: 1.1.0
+
+ ansi-regex@5.0.1: {}
+
+ ansi-regex@6.1.0: {}
+
+ ansi-styles@4.3.0:
+ dependencies:
+ color-convert: 2.0.1
+
+ ansi-styles@5.2.0: {}
+
+ ansi-styles@6.2.1: {}
+
+ any-promise@1.3.0: {}
+
+ anymatch@3.1.3:
+ dependencies:
+ normalize-path: 3.0.0
+ picomatch: 2.3.1
+
+ aproba@2.0.0: {}
+
+ arch@2.2.0: {}
+
+ are-we-there-yet@2.0.0:
+ dependencies:
+ delegates: 1.0.0
+ readable-stream: 3.6.2
+
+ arg@5.0.2: {}
+
+ argparse@1.0.10:
+ dependencies:
+ sprintf-js: 1.0.3
+
+ argparse@2.0.1: {}
+
+ aria-query@5.1.3:
+ dependencies:
+ deep-equal: 2.2.3
+
+ aria-query@5.3.0:
+ dependencies:
+ dequal: 2.0.3
+
+ array-buffer-byte-length@1.0.1:
+ dependencies:
+ call-bind: 1.0.7
+ is-array-buffer: 3.0.4
+
+ array-includes@3.1.8:
+ dependencies:
+ call-bind: 1.0.7
+ define-properties: 1.2.1
+ es-abstract: 1.23.3
+ es-object-atoms: 1.0.0
+ get-intrinsic: 1.2.4
+ is-string: 1.0.7
+
+ array-union@2.1.0: {}
+
+ array.prototype.findlast@1.2.5:
+ dependencies:
+ call-bind: 1.0.7
+ define-properties: 1.2.1
+ es-abstract: 1.23.3
+ es-errors: 1.3.0
+ es-object-atoms: 1.0.0
+ es-shim-unscopables: 1.0.2
+
+ array.prototype.findlastindex@1.2.5:
+ dependencies:
+ call-bind: 1.0.7
+ define-properties: 1.2.1
+ es-abstract: 1.23.3
+ es-errors: 1.3.0
+ es-object-atoms: 1.0.0
+ es-shim-unscopables: 1.0.2
+
+ array.prototype.flat@1.3.2:
+ dependencies:
+ call-bind: 1.0.7
+ define-properties: 1.2.1
+ es-abstract: 1.23.3
+ es-shim-unscopables: 1.0.2
+
+ array.prototype.flatmap@1.3.2:
+ dependencies:
+ call-bind: 1.0.7
+ define-properties: 1.2.1
+ es-abstract: 1.23.3
+ es-shim-unscopables: 1.0.2
+
+ array.prototype.tosorted@1.1.4:
+ dependencies:
+ call-bind: 1.0.7
+ define-properties: 1.2.1
+ es-abstract: 1.23.3
+ es-errors: 1.3.0
+ es-shim-unscopables: 1.0.2
+
+ arraybuffer.prototype.slice@1.0.3:
+ dependencies:
+ array-buffer-byte-length: 1.0.1
+ call-bind: 1.0.7
+ define-properties: 1.2.1
+ es-abstract: 1.23.3
+ es-errors: 1.3.0
+ get-intrinsic: 1.2.4
+ is-array-buffer: 3.0.4
+ is-shared-array-buffer: 1.0.3
+
+ asn1@0.2.6:
+ dependencies:
+ safer-buffer: 2.1.2
+
+ assert-plus@1.0.0: {}
+
+ assertion-error@2.0.1: {}
+
+ ast-types-flow@0.0.8: {}
+
+ astral-regex@2.0.0: {}
+
+ async@3.2.6: {}
+
+ asynckit@0.4.0: {}
+
+ at-least-node@1.0.0: {}
+
+ autoprefixer@10.4.21(postcss@8.5.3):
+ dependencies:
+ browserslist: 4.24.4
+ caniuse-lite: 1.0.30001715
+ fraction.js: 4.3.7
+ normalize-range: 0.1.2
+ picocolors: 1.1.1
+ postcss: 8.5.3
+ postcss-value-parser: 4.2.0
+
+ available-typed-arrays@1.0.7:
+ dependencies:
+ possible-typed-array-names: 1.0.0
+
+ aws-sign2@0.7.0: {}
+
+ aws4@1.13.2: {}
+
+ axe-core@4.10.0: {}
+
+ axios@1.8.4(debug@4.4.0):
+ dependencies:
+ follow-redirects: 1.15.9(debug@4.4.0)
+ form-data: 4.0.2
+ proxy-from-env: 1.1.0
+ transitivePeerDependencies:
+ - debug
+
+ axobject-query@3.1.1:
+ dependencies:
+ deep-equal: 2.2.3
+
+ balanced-match@1.0.2: {}
+
+ base64-js@1.5.1: {}
+
+ bcrypt-pbkdf@1.0.2:
+ dependencies:
+ tweetnacl: 0.14.5
+
+ better-path-resolve@1.0.0:
+ dependencies:
+ is-windows: 1.0.2
+
+ binary-extensions@2.3.0: {}
+
+ blob-util@2.0.2: {}
+
+ bluebird@3.7.2: {}
+
+ brace-expansion@1.1.11:
+ dependencies:
+ balanced-match: 1.0.2
+ concat-map: 0.0.1
+
+ brace-expansion@2.0.1:
+ dependencies:
+ balanced-match: 1.0.2
+
+ braces@3.0.3:
+ dependencies:
+ fill-range: 7.1.1
+
+ browserslist@4.24.4:
+ dependencies:
+ caniuse-lite: 1.0.30001715
+ electron-to-chromium: 1.5.140
+ node-releases: 2.0.19
+ update-browserslist-db: 1.1.3(browserslist@4.24.4)
+
+ buffer-crc32@0.2.13: {}
+
+ buffer@5.7.1:
+ dependencies:
+ base64-js: 1.5.1
+ ieee754: 1.2.1
+
+ bundle-require@5.1.0(esbuild@0.25.2):
+ dependencies:
+ esbuild: 0.25.2
+ load-tsconfig: 0.2.5
+
+ cac@6.7.14: {}
+
+ cachedir@2.4.0: {}
+
+ call-bind-apply-helpers@1.0.2:
+ dependencies:
+ es-errors: 1.3.0
+ function-bind: 1.1.2
+
+ call-bind@1.0.7:
+ dependencies:
+ es-define-property: 1.0.1
+ es-errors: 1.3.0
+ function-bind: 1.1.2
+ get-intrinsic: 1.3.0
+ set-function-length: 1.2.2
+
+ call-bound@1.0.4:
+ dependencies:
+ call-bind-apply-helpers: 1.0.2
+ get-intrinsic: 1.3.0
+
+ callsites@3.1.0: {}
+
+ camelcase-css@2.0.1: {}
+
+ caniuse-lite@1.0.30001715: {}
+
+ case-anything@2.1.13: {}
+
+ caseless@0.12.0: {}
+
+ chai@5.2.0:
+ dependencies:
+ assertion-error: 2.0.1
+ check-error: 2.1.1
+ deep-eql: 5.0.2
+ loupe: 3.1.3
+ pathval: 2.0.0
+
+ chalk@3.0.0:
+ dependencies:
+ ansi-styles: 4.3.0
+ supports-color: 7.2.0
+
+ chalk@4.1.2:
+ dependencies:
+ ansi-styles: 4.3.0
+ supports-color: 7.2.0
+
+ chalk@5.4.1: {}
+
+ chardet@0.7.0: {}
+
+ check-error@2.1.1: {}
+
+ check-more-types@2.24.0: {}
+
+ chokidar@3.6.0:
+ dependencies:
+ anymatch: 3.1.3
+ braces: 3.0.3
+ glob-parent: 5.1.2
+ is-binary-path: 2.1.0
+ is-glob: 4.0.3
+ normalize-path: 3.0.0
+ readdirp: 3.6.0
+ optionalDependencies:
+ fsevents: 2.3.3
+
+ chokidar@4.0.3:
+ dependencies:
+ readdirp: 4.1.2
+
+ chownr@2.0.0: {}
+
+ ci-info@3.9.0: {}
+
+ ci-info@4.2.0: {}
+
+ clean-stack@2.2.0: {}
+
+ cli-cursor@3.1.0:
+ dependencies:
+ restore-cursor: 3.1.0
+
+ cli-cursor@5.0.0:
+ dependencies:
+ restore-cursor: 5.1.0
+
+ cli-table3@0.6.5:
+ dependencies:
+ string-width: 4.2.3
+ optionalDependencies:
+ '@colors/colors': 1.5.0
+
+ cli-truncate@2.1.0:
+ dependencies:
+ slice-ansi: 3.0.0
+ string-width: 4.2.3
+
+ cli-truncate@4.0.0:
+ dependencies:
+ slice-ansi: 5.0.0
+ string-width: 7.2.0
+
+ client-only@0.0.1: {}
+
+ cliui@8.0.1:
+ dependencies:
+ string-width: 4.2.3
+ strip-ansi: 6.0.1
+ wrap-ansi: 7.0.0
+
+ clsx@1.2.1: {}
+
+ clsx@2.1.1: {}
+
+ color-convert@2.0.1:
+ dependencies:
+ color-name: 1.1.4
+
+ color-name@1.1.4: {}
+
+ color-string@1.9.1:
+ dependencies:
+ color-name: 1.1.4
+ simple-swizzle: 0.2.2
+ optional: true
+
+ color-support@1.1.3: {}
+
+ color@4.2.3:
+ dependencies:
+ color-convert: 2.0.1
+ color-string: 1.9.1
+ optional: true
+
+ colorette@2.0.20: {}
+
+ combined-stream@1.0.8:
+ dependencies:
+ delayed-stream: 1.0.0
+
+ commander@13.1.0: {}
+
+ commander@4.1.1: {}
+
+ commander@6.2.1: {}
+
+ common-tags@1.8.2: {}
+
+ concat-map@0.0.1: {}
+
+ concurrently@9.1.2:
+ dependencies:
+ chalk: 4.1.2
+ lodash: 4.17.21
+ rxjs: 7.8.2
+ shell-quote: 1.8.2
+ supports-color: 8.1.1
+ tree-kill: 1.2.2
+ yargs: 17.7.2
+
+ consola@3.4.2: {}
+
+ console-control-strings@1.1.0: {}
+
+ convert-source-map@2.0.0: {}
+
+ copy-to-clipboard@3.3.3:
+ dependencies:
+ toggle-selection: 1.0.6
+
+ core-util-is@1.0.2: {}
+
+ cross-spawn@7.0.3:
+ dependencies:
+ path-key: 3.1.1
+ shebang-command: 2.0.0
+ which: 2.0.2
+
+ cross-spawn@7.0.6:
+ dependencies:
+ path-key: 3.1.1
+ shebang-command: 2.0.0
+ which: 2.0.2
+
+ css.escape@1.5.1: {}
+
+ cssesc@3.0.0: {}
+
+ cssstyle@4.3.1:
+ dependencies:
+ '@asamuzakjp/css-color': 3.1.4
+ rrweb-cssom: 0.8.0
+
+ csstype@3.1.3: {}
+
+ cypress@14.3.2:
+ dependencies:
+ '@cypress/request': 3.0.8
+ '@cypress/xvfb': 1.2.4(supports-color@8.1.1)
+ '@types/sinonjs__fake-timers': 8.1.1
+ '@types/sizzle': 2.3.9
+ arch: 2.2.0
+ blob-util: 2.0.2
+ bluebird: 3.7.2
+ buffer: 5.7.1
+ cachedir: 2.4.0
+ chalk: 4.1.2
+ check-more-types: 2.24.0
+ ci-info: 4.2.0
+ cli-cursor: 3.1.0
+ cli-table3: 0.6.5
+ commander: 6.2.1
+ common-tags: 1.8.2
+ dayjs: 1.11.13
+ debug: 4.4.0(supports-color@8.1.1)
+ enquirer: 2.4.1
+ eventemitter2: 6.4.7
+ execa: 4.1.0
+ executable: 4.1.1
+ extract-zip: 2.0.1(supports-color@8.1.1)
+ figures: 3.2.0
+ fs-extra: 9.1.0
+ getos: 3.2.1
+ is-installed-globally: 0.4.0
+ lazy-ass: 1.6.0
+ listr2: 3.14.0(enquirer@2.4.1)
+ lodash: 4.17.21
+ log-symbols: 4.1.0
+ minimist: 1.2.8
+ ospath: 1.2.2
+ pretty-bytes: 5.6.0
+ process: 0.11.10
+ proxy-from-env: 1.0.0
+ request-progress: 3.0.0
+ semver: 7.7.1
+ supports-color: 8.1.1
+ tmp: 0.2.3
+ tree-kill: 1.2.2
+ untildify: 4.0.0
+ yauzl: 2.10.0
+
+ damerau-levenshtein@1.0.8: {}
+
+ dashdash@1.14.1:
+ dependencies:
+ assert-plus: 1.0.0
+
+ data-uri-to-buffer@4.0.1: {}
+
+ data-urls@5.0.0:
+ dependencies:
+ whatwg-mimetype: 4.0.0
+ whatwg-url: 14.2.0
+
+ data-view-buffer@1.0.1:
+ dependencies:
+ call-bind: 1.0.7
+ es-errors: 1.3.0
+ is-data-view: 1.0.1
+
+ data-view-byte-length@1.0.1:
+ dependencies:
+ call-bind: 1.0.7
+ es-errors: 1.3.0
+ is-data-view: 1.0.1
+
+ data-view-byte-offset@1.0.0:
+ dependencies:
+ call-bind: 1.0.7
+ es-errors: 1.3.0
+ is-data-view: 1.0.1
+
+ dayjs@1.11.13: {}
+
+ debug@3.2.7(supports-color@8.1.1):
+ dependencies:
+ ms: 2.1.3
+ optionalDependencies:
+ supports-color: 8.1.1
+
+ debug@4.3.7:
+ dependencies:
+ ms: 2.1.3
+
+ debug@4.4.0:
+ dependencies:
+ ms: 2.1.3
+
+ debug@4.4.0(supports-color@5.5.0):
+ dependencies:
+ ms: 2.1.3
+ optionalDependencies:
+ supports-color: 5.5.0
+
+ debug@4.4.0(supports-color@8.1.1):
+ dependencies:
+ ms: 2.1.3
+ optionalDependencies:
+ supports-color: 8.1.1
+
+ decimal.js@10.5.0: {}
+
+ deep-eql@5.0.2: {}
+
+ deep-equal@2.2.3:
+ dependencies:
+ array-buffer-byte-length: 1.0.1
+ call-bind: 1.0.7
+ es-get-iterator: 1.1.3
+ get-intrinsic: 1.3.0
+ is-arguments: 1.1.1
+ is-array-buffer: 3.0.4
+ is-date-object: 1.0.5
+ is-regex: 1.1.4
+ is-shared-array-buffer: 1.0.3
+ isarray: 2.0.5
+ object-is: 1.1.6
+ object-keys: 1.1.1
+ object.assign: 4.1.5
+ regexp.prototype.flags: 1.5.2
+ side-channel: 1.1.0
+ which-boxed-primitive: 1.0.2
+ which-collection: 1.0.2
+ which-typed-array: 1.1.15
+
+ deep-is@0.1.4: {}
+
+ deepmerge@4.3.1: {}
+
+ define-data-property@1.1.4:
+ dependencies:
+ es-define-property: 1.0.0
+ es-errors: 1.3.0
+ gopd: 1.0.1
+
+ define-properties@1.2.1:
+ dependencies:
+ define-data-property: 1.1.4
+ has-property-descriptors: 1.0.2
+ object-keys: 1.1.1
+
+ delayed-stream@1.0.0: {}
+
+ delegates@1.0.0: {}
+
+ dequal@2.0.3: {}
+
+ detect-indent@6.1.0: {}
+
+ detect-libc@1.0.3: {}
+
+ detect-libc@2.0.4: {}
+
+ didyoumean@1.2.2: {}
+
+ dir-glob@3.0.1:
+ dependencies:
+ path-type: 4.0.0
+
+ dlv@1.1.3: {}
+
+ doctrine@2.1.0:
+ dependencies:
+ esutils: 2.0.3
+
+ doctrine@3.0.0:
+ dependencies:
+ esutils: 2.0.3
+
+ dom-accessibility-api@0.5.16: {}
+
+ dom-accessibility-api@0.6.3: {}
+
+ dotenv-cli@8.0.0:
+ dependencies:
+ cross-spawn: 7.0.6
+ dotenv: 16.5.0
+ dotenv-expand: 10.0.0
+ minimist: 1.2.8
+
+ dotenv-expand@10.0.0: {}
+
+ dotenv@16.0.3: {}
+
+ dotenv@16.5.0: {}
+
+ dprint-node@1.0.8:
+ dependencies:
+ detect-libc: 1.0.3
+
+ dunder-proto@1.0.1:
+ dependencies:
+ call-bind-apply-helpers: 1.0.2
+ es-errors: 1.3.0
+ gopd: 1.2.0
+
+ duplexer@0.1.2: {}
+
+ eastasianwidth@0.2.0: {}
+
+ ecc-jsbn@0.1.2:
+ dependencies:
+ jsbn: 0.1.1
+ safer-buffer: 2.1.2
+
+ electron-to-chromium@1.5.140: {}
+
+ emoji-regex@10.4.0: {}
+
+ emoji-regex@8.0.0: {}
+
+ emoji-regex@9.2.2: {}
+
+ end-of-stream@1.4.4:
+ dependencies:
+ once: 1.4.0
+
+ enhanced-resolve@5.17.1:
+ dependencies:
+ graceful-fs: 4.2.11
+ tapable: 2.2.1
+
+ enquirer@2.4.1:
+ dependencies:
+ ansi-colors: 4.1.3
+ strip-ansi: 6.0.1
+
+ entities@6.0.0: {}
+
+ env-cmd@10.1.0:
+ dependencies:
+ commander: 4.1.1
+ cross-spawn: 7.0.6
+
+ environment@1.1.0: {}
+
+ es-abstract@1.23.3:
+ dependencies:
+ array-buffer-byte-length: 1.0.1
+ arraybuffer.prototype.slice: 1.0.3
+ available-typed-arrays: 1.0.7
+ call-bind: 1.0.7
+ data-view-buffer: 1.0.1
+ data-view-byte-length: 1.0.1
+ data-view-byte-offset: 1.0.0
+ es-define-property: 1.0.0
+ es-errors: 1.3.0
+ es-object-atoms: 1.0.0
+ es-set-tostringtag: 2.1.0
+ es-to-primitive: 1.2.1
+ function.prototype.name: 1.1.6
+ get-intrinsic: 1.2.4
+ get-symbol-description: 1.0.2
+ globalthis: 1.0.4
+ gopd: 1.0.1
+ has-property-descriptors: 1.0.2
+ has-proto: 1.0.3
+ has-symbols: 1.0.3
+ hasown: 2.0.2
+ internal-slot: 1.0.7
+ is-array-buffer: 3.0.4
+ is-callable: 1.2.7
+ is-data-view: 1.0.1
+ is-negative-zero: 2.0.3
+ is-regex: 1.1.4
+ is-shared-array-buffer: 1.0.3
+ is-string: 1.0.7
+ is-typed-array: 1.1.13
+ is-weakref: 1.0.2
+ object-inspect: 1.13.2
+ object-keys: 1.1.1
+ object.assign: 4.1.5
+ regexp.prototype.flags: 1.5.2
+ safe-array-concat: 1.1.2
+ safe-regex-test: 1.0.3
+ string.prototype.trim: 1.2.9
+ string.prototype.trimend: 1.0.8
+ string.prototype.trimstart: 1.0.8
+ typed-array-buffer: 1.0.2
+ typed-array-byte-length: 1.0.1
+ typed-array-byte-offset: 1.0.2
+ typed-array-length: 1.0.6
+ unbox-primitive: 1.0.2
+ which-typed-array: 1.1.15
+
+ es-define-property@1.0.0:
+ dependencies:
+ get-intrinsic: 1.3.0
+
+ es-define-property@1.0.1: {}
+
+ es-errors@1.3.0: {}
+
+ es-get-iterator@1.1.3:
+ dependencies:
+ call-bind: 1.0.7
+ get-intrinsic: 1.3.0
+ has-symbols: 1.1.0
+ is-arguments: 1.1.1
+ is-map: 2.0.3
+ is-set: 2.0.3
+ is-string: 1.0.7
+ isarray: 2.0.5
+ stop-iteration-iterator: 1.0.0
+
+ es-iterator-helpers@1.0.19:
+ dependencies:
+ call-bind: 1.0.7
+ define-properties: 1.2.1
+ es-abstract: 1.23.3
+ es-errors: 1.3.0
+ es-set-tostringtag: 2.0.3
+ function-bind: 1.1.2
+ get-intrinsic: 1.2.4
+ globalthis: 1.0.4
+ has-property-descriptors: 1.0.2
+ has-proto: 1.0.3
+ has-symbols: 1.0.3
+ internal-slot: 1.0.7
+ iterator.prototype: 1.1.2
+ safe-array-concat: 1.1.2
+
+ es-module-lexer@1.7.0: {}
+
+ es-object-atoms@1.0.0:
+ dependencies:
+ es-errors: 1.3.0
+
+ es-object-atoms@1.1.1:
+ dependencies:
+ es-errors: 1.3.0
+
+ es-set-tostringtag@2.0.3:
+ dependencies:
+ get-intrinsic: 1.2.4
+ has-tostringtag: 1.0.2
+ hasown: 2.0.2
+
+ es-set-tostringtag@2.1.0:
+ dependencies:
+ es-errors: 1.3.0
+ get-intrinsic: 1.3.0
+ has-tostringtag: 1.0.2
+ hasown: 2.0.2
+
+ es-shim-unscopables@1.0.2:
+ dependencies:
+ hasown: 2.0.2
+
+ es-to-primitive@1.2.1:
+ dependencies:
+ is-callable: 1.2.7
+ is-date-object: 1.0.5
+ is-symbol: 1.0.4
+
+ esbuild@0.25.2:
+ optionalDependencies:
+ '@esbuild/aix-ppc64': 0.25.2
+ '@esbuild/android-arm': 0.25.2
+ '@esbuild/android-arm64': 0.25.2
+ '@esbuild/android-x64': 0.25.2
+ '@esbuild/darwin-arm64': 0.25.2
+ '@esbuild/darwin-x64': 0.25.2
+ '@esbuild/freebsd-arm64': 0.25.2
+ '@esbuild/freebsd-x64': 0.25.2
+ '@esbuild/linux-arm': 0.25.2
+ '@esbuild/linux-arm64': 0.25.2
+ '@esbuild/linux-ia32': 0.25.2
+ '@esbuild/linux-loong64': 0.25.2
+ '@esbuild/linux-mips64el': 0.25.2
+ '@esbuild/linux-ppc64': 0.25.2
+ '@esbuild/linux-riscv64': 0.25.2
+ '@esbuild/linux-s390x': 0.25.2
+ '@esbuild/linux-x64': 0.25.2
+ '@esbuild/netbsd-arm64': 0.25.2
+ '@esbuild/netbsd-x64': 0.25.2
+ '@esbuild/openbsd-arm64': 0.25.2
+ '@esbuild/openbsd-x64': 0.25.2
+ '@esbuild/sunos-x64': 0.25.2
+ '@esbuild/win32-arm64': 0.25.2
+ '@esbuild/win32-ia32': 0.25.2
+ '@esbuild/win32-x64': 0.25.2
+
+ escalade@3.2.0: {}
+
+ escape-string-regexp@1.0.5: {}
+
+ escape-string-regexp@4.0.0: {}
+
+ eslint-config-next@14.2.18(eslint@8.57.1)(typescript@5.8.3):
+ dependencies:
+ '@next/eslint-plugin-next': 14.2.18
+ '@rushstack/eslint-patch': 1.10.4
+ '@typescript-eslint/eslint-plugin': 8.15.0(@typescript-eslint/parser@7.18.0(eslint@8.57.1)(typescript@5.8.3))(eslint@8.57.1)(typescript@5.8.3)
+ '@typescript-eslint/parser': 7.18.0(eslint@8.57.1)(typescript@5.8.3)
+ eslint: 8.57.1
+ eslint-import-resolver-node: 0.3.9
+ eslint-import-resolver-typescript: 3.6.3(@typescript-eslint/parser@7.18.0(eslint@8.57.1)(typescript@5.8.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1)(eslint@8.57.1)
+ eslint-plugin-import: 2.29.1(@typescript-eslint/parser@7.18.0(eslint@8.57.1)(typescript@5.8.3))(eslint-import-resolver-typescript@3.6.3)(eslint@8.57.1)
+ eslint-plugin-jsx-a11y: 6.9.0(eslint@8.57.1)
+ eslint-plugin-react: 7.35.0(eslint@8.57.1)
+ eslint-plugin-react-hooks: 4.6.2(eslint@8.57.1)
+ optionalDependencies:
+ typescript: 5.8.3
+ transitivePeerDependencies:
+ - eslint-import-resolver-webpack
+ - eslint-plugin-import-x
+ - supports-color
+
+ eslint-config-prettier@9.1.0(eslint@8.57.1):
+ dependencies:
+ eslint: 8.57.1
+
+ eslint-config-turbo@2.1.0(eslint@8.57.1):
+ dependencies:
+ eslint: 8.57.1
+ eslint-plugin-turbo: 2.1.0(eslint@8.57.1)
+
+ eslint-import-resolver-node@0.3.9:
+ dependencies:
+ debug: 3.2.7(supports-color@8.1.1)
+ is-core-module: 2.15.1
+ resolve: 1.22.8
+ transitivePeerDependencies:
+ - supports-color
+
+ eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@7.18.0(eslint@8.57.1)(typescript@5.8.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1)(eslint@8.57.1):
+ dependencies:
+ '@nolyfill/is-core-module': 1.0.39
+ debug: 4.4.0(supports-color@5.5.0)
+ enhanced-resolve: 5.17.1
+ eslint: 8.57.1
+ eslint-module-utils: 2.8.2(@typescript-eslint/parser@7.18.0(eslint@8.57.1)(typescript@5.8.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@7.18.0(eslint@8.57.1)(typescript@5.8.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1)(eslint@8.57.1))(eslint@8.57.1)
+ fast-glob: 3.3.2
+ get-tsconfig: 4.8.0
+ is-bun-module: 1.1.0
+ is-glob: 4.0.3
+ optionalDependencies:
+ eslint-plugin-import: 2.29.1(@typescript-eslint/parser@7.18.0(eslint@8.57.1)(typescript@5.8.3))(eslint-import-resolver-typescript@3.6.3)(eslint@8.57.1)
+ transitivePeerDependencies:
+ - '@typescript-eslint/parser'
+ - eslint-import-resolver-node
+ - eslint-import-resolver-webpack
+ - supports-color
+
+ eslint-module-utils@2.8.2(@typescript-eslint/parser@7.18.0(eslint@8.57.1)(typescript@5.8.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@7.18.0(eslint@8.57.1)(typescript@5.8.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1)(eslint@8.57.1))(eslint@8.57.1):
+ dependencies:
+ debug: 3.2.7(supports-color@8.1.1)
+ optionalDependencies:
+ '@typescript-eslint/parser': 7.18.0(eslint@8.57.1)(typescript@5.8.3)
+ eslint: 8.57.1
+ eslint-import-resolver-node: 0.3.9
+ eslint-import-resolver-typescript: 3.6.3(@typescript-eslint/parser@7.18.0(eslint@8.57.1)(typescript@5.8.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1)(eslint@8.57.1)
+ transitivePeerDependencies:
+ - supports-color
+
+ eslint-plugin-import@2.29.1(@typescript-eslint/parser@7.18.0(eslint@8.57.1)(typescript@5.8.3))(eslint-import-resolver-typescript@3.6.3)(eslint@8.57.1):
+ dependencies:
+ array-includes: 3.1.8
+ array.prototype.findlastindex: 1.2.5
+ array.prototype.flat: 1.3.2
+ array.prototype.flatmap: 1.3.2
+ debug: 3.2.7(supports-color@8.1.1)
+ doctrine: 2.1.0
+ eslint: 8.57.1
+ eslint-import-resolver-node: 0.3.9
+ eslint-module-utils: 2.8.2(@typescript-eslint/parser@7.18.0(eslint@8.57.1)(typescript@5.8.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@7.18.0(eslint@8.57.1)(typescript@5.8.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1)(eslint@8.57.1))(eslint@8.57.1)
+ hasown: 2.0.2
+ is-core-module: 2.15.1
+ is-glob: 4.0.3
+ minimatch: 3.1.2
+ object.fromentries: 2.0.8
+ object.groupby: 1.0.3
+ object.values: 1.2.0
+ semver: 6.3.1
+ tsconfig-paths: 3.15.0
+ optionalDependencies:
+ '@typescript-eslint/parser': 7.18.0(eslint@8.57.1)(typescript@5.8.3)
+ transitivePeerDependencies:
+ - eslint-import-resolver-typescript
+ - eslint-import-resolver-webpack
+ - supports-color
+
+ eslint-plugin-jsx-a11y@6.9.0(eslint@8.57.1):
+ dependencies:
+ aria-query: 5.1.3
+ array-includes: 3.1.8
+ array.prototype.flatmap: 1.3.2
+ ast-types-flow: 0.0.8
+ axe-core: 4.10.0
+ axobject-query: 3.1.1
+ damerau-levenshtein: 1.0.8
+ emoji-regex: 9.2.2
+ es-iterator-helpers: 1.0.19
+ eslint: 8.57.1
+ hasown: 2.0.2
+ jsx-ast-utils: 3.3.5
+ language-tags: 1.0.9
+ minimatch: 3.1.2
+ object.fromentries: 2.0.8
+ safe-regex-test: 1.0.3
+ string.prototype.includes: 2.0.0
+
+ eslint-plugin-react-hooks@4.6.2(eslint@8.57.1):
+ dependencies:
+ eslint: 8.57.1
+
+ eslint-plugin-react@7.35.0(eslint@8.57.1):
+ dependencies:
+ array-includes: 3.1.8
+ array.prototype.findlast: 1.2.5
+ array.prototype.flatmap: 1.3.2
+ array.prototype.tosorted: 1.1.4
+ doctrine: 2.1.0
+ es-iterator-helpers: 1.0.19
+ eslint: 8.57.1
+ estraverse: 5.3.0
+ hasown: 2.0.2
+ jsx-ast-utils: 3.3.5
+ minimatch: 3.1.2
+ object.entries: 1.1.8
+ object.fromentries: 2.0.8
+ object.values: 1.2.0
+ prop-types: 15.8.1
+ resolve: 2.0.0-next.5
+ semver: 6.3.1
+ string.prototype.matchall: 4.0.11
+ string.prototype.repeat: 1.0.0
+
+ eslint-plugin-turbo@2.1.0(eslint@8.57.1):
+ dependencies:
+ dotenv: 16.0.3
+ eslint: 8.57.1
+
+ eslint-scope@5.1.1:
+ dependencies:
+ esrecurse: 4.3.0
+ estraverse: 4.3.0
+
+ eslint-scope@7.2.2:
+ dependencies:
+ esrecurse: 4.3.0
+ estraverse: 5.3.0
+
+ eslint-visitor-keys@2.1.0: {}
+
+ eslint-visitor-keys@3.4.3: {}
+
+ eslint-visitor-keys@4.2.0: {}
+
+ eslint@8.57.1:
+ dependencies:
+ '@eslint-community/eslint-utils': 4.4.0(eslint@8.57.1)
+ '@eslint-community/regexpp': 4.11.1
+ '@eslint/eslintrc': 2.1.4
+ '@eslint/js': 8.57.1
+ '@humanwhocodes/config-array': 0.13.0
+ '@humanwhocodes/module-importer': 1.0.1
+ '@nodelib/fs.walk': 1.2.8
+ '@ungap/structured-clone': 1.2.0
+ ajv: 6.12.6
+ chalk: 4.1.2
+ cross-spawn: 7.0.3
+ debug: 4.3.7
+ doctrine: 3.0.0
+ escape-string-regexp: 4.0.0
+ eslint-scope: 7.2.2
+ eslint-visitor-keys: 3.4.3
+ espree: 9.6.1
+ esquery: 1.6.0
+ esutils: 2.0.3
+ fast-deep-equal: 3.1.3
+ file-entry-cache: 6.0.1
+ find-up: 5.0.0
+ glob-parent: 6.0.2
+ globals: 13.24.0
+ graphemer: 1.4.0
+ ignore: 5.3.2
+ imurmurhash: 0.1.4
+ is-glob: 4.0.3
+ is-path-inside: 3.0.3
+ js-yaml: 4.1.0
+ json-stable-stringify-without-jsonify: 1.0.1
+ levn: 0.4.1
+ lodash.merge: 4.6.2
+ minimatch: 3.1.2
+ natural-compare: 1.4.0
+ optionator: 0.9.4
+ strip-ansi: 6.0.1
+ text-table: 0.2.0
+ transitivePeerDependencies:
+ - supports-color
+
+ espree@9.6.1:
+ dependencies:
+ acorn: 8.12.1
+ acorn-jsx: 5.3.2(acorn@8.12.1)
+ eslint-visitor-keys: 3.4.3
+
+ esprima@4.0.1: {}
+
+ esquery@1.6.0:
+ dependencies:
+ estraverse: 5.3.0
+
+ esrecurse@4.3.0:
+ dependencies:
+ estraverse: 5.3.0
+
+ estraverse@4.3.0: {}
+
+ estraverse@5.3.0: {}
+
+ estree-walker@3.0.3:
+ dependencies:
+ '@types/estree': 1.0.7
+
+ esutils@2.0.3: {}
+
+ event-stream@3.3.4:
+ dependencies:
+ duplexer: 0.1.2
+ from: 0.1.7
+ map-stream: 0.1.0
+ pause-stream: 0.0.11
+ split: 0.3.3
+ stream-combiner: 0.0.4
+ through: 2.3.8
+
+ eventemitter2@6.4.7: {}
+
+ eventemitter3@5.0.1: {}
+
+ execa@4.1.0:
+ dependencies:
+ cross-spawn: 7.0.6
+ get-stream: 5.2.0
+ human-signals: 1.1.1
+ is-stream: 2.0.1
+ merge-stream: 2.0.0
+ npm-run-path: 4.0.1
+ onetime: 5.1.2
+ signal-exit: 3.0.7
+ strip-final-newline: 2.0.0
+
+ execa@5.1.1:
+ dependencies:
+ cross-spawn: 7.0.6
+ get-stream: 6.0.1
+ human-signals: 2.1.0
+ is-stream: 2.0.1
+ merge-stream: 2.0.0
+ npm-run-path: 4.0.1
+ onetime: 5.1.2
+ signal-exit: 3.0.7
+ strip-final-newline: 2.0.0
+
+ execa@8.0.1:
+ dependencies:
+ cross-spawn: 7.0.6
+ get-stream: 8.0.1
+ human-signals: 5.0.0
+ is-stream: 3.0.0
+ merge-stream: 2.0.0
+ npm-run-path: 5.3.0
+ onetime: 6.0.0
+ signal-exit: 4.1.0
+ strip-final-newline: 3.0.0
+
+ executable@4.1.1:
+ dependencies:
+ pify: 2.3.0
+
+ expect-type@1.2.1: {}
+
+ extend@3.0.2: {}
+
+ extendable-error@0.1.7: {}
+
+ external-editor@3.1.0:
+ dependencies:
+ chardet: 0.7.0
+ iconv-lite: 0.4.24
+ tmp: 0.0.33
+
+ extract-zip@2.0.1(supports-color@8.1.1):
+ dependencies:
+ debug: 4.4.0(supports-color@8.1.1)
+ get-stream: 5.2.0
+ yauzl: 2.10.0
+ optionalDependencies:
+ '@types/yauzl': 2.10.3
+ transitivePeerDependencies:
+ - supports-color
+
+ extsprintf@1.3.0: {}
+
+ fast-deep-equal@3.1.3: {}
+
+ fast-glob@3.3.2:
+ dependencies:
+ '@nodelib/fs.stat': 2.0.5
+ '@nodelib/fs.walk': 1.2.8
+ glob-parent: 5.1.2
+ merge2: 1.4.1
+ micromatch: 4.0.8
+
+ fast-glob@3.3.3:
+ dependencies:
+ '@nodelib/fs.stat': 2.0.5
+ '@nodelib/fs.walk': 1.2.8
+ glob-parent: 5.1.2
+ merge2: 1.4.1
+ micromatch: 4.0.8
+
+ fast-json-stable-stringify@2.1.0: {}
+
+ fast-levenshtein@2.0.6: {}
+
+ fastq@1.17.1:
+ dependencies:
+ reusify: 1.0.4
+
+ fd-slicer@1.1.0:
+ dependencies:
+ pend: 1.2.0
+
+ fdir@6.4.4(picomatch@4.0.2):
+ optionalDependencies:
+ picomatch: 4.0.2
+
+ fetch-blob@3.2.0:
+ dependencies:
+ node-domexception: 1.0.0
+ web-streams-polyfill: 3.3.3
+
+ fflate@0.8.2: {}
+
+ figures@3.2.0:
+ dependencies:
+ escape-string-regexp: 1.0.5
+
+ file-entry-cache@6.0.1:
+ dependencies:
+ flat-cache: 3.2.0
+
+ fill-range@7.1.1:
+ dependencies:
+ to-regex-range: 5.0.1
+
+ find-up@4.1.0:
+ dependencies:
+ locate-path: 5.0.0
+ path-exists: 4.0.0
+
+ find-up@5.0.0:
+ dependencies:
+ locate-path: 6.0.0
+ path-exists: 4.0.0
+
+ flat-cache@3.2.0:
+ dependencies:
+ flatted: 3.3.1
+ keyv: 4.5.4
+ rimraf: 3.0.2
+
+ flatted@3.3.1: {}
+
+ follow-redirects@1.15.9(debug@4.4.0):
+ optionalDependencies:
+ debug: 4.4.0
+
+ for-each@0.3.3:
+ dependencies:
+ is-callable: 1.2.7
+
+ foreground-child@3.3.0:
+ dependencies:
+ cross-spawn: 7.0.6
+ signal-exit: 4.1.0
+
+ foreground-child@3.3.1:
+ dependencies:
+ cross-spawn: 7.0.6
+ signal-exit: 4.1.0
+
+ forever-agent@0.6.1: {}
+
+ form-data@4.0.2:
+ dependencies:
+ asynckit: 0.4.0
+ combined-stream: 1.0.8
+ es-set-tostringtag: 2.1.0
+ mime-types: 2.1.35
+
+ formdata-polyfill@4.0.10:
+ dependencies:
+ fetch-blob: 3.2.0
+
+ fraction.js@4.3.7: {}
+
+ from@0.1.7: {}
+
+ fs-extra@7.0.1:
+ dependencies:
+ graceful-fs: 4.2.11
+ jsonfile: 4.0.0
+ universalify: 0.1.2
+
+ fs-extra@8.1.0:
+ dependencies:
+ graceful-fs: 4.2.11
+ jsonfile: 4.0.0
+ universalify: 0.1.2
+
+ fs-extra@9.1.0:
+ dependencies:
+ at-least-node: 1.0.0
+ graceful-fs: 4.2.11
+ jsonfile: 6.1.0
+ universalify: 2.0.1
+
+ fs-minipass@2.1.0:
+ dependencies:
+ minipass: 3.3.6
+
+ fs.realpath@1.0.0: {}
+
+ fsevents@2.3.2:
+ optional: true
+
+ fsevents@2.3.3:
+ optional: true
+
+ function-bind@1.1.2: {}
+
+ function.prototype.name@1.1.6:
+ dependencies:
+ call-bind: 1.0.7
+ define-properties: 1.2.1
+ es-abstract: 1.23.3
+ functions-have-names: 1.2.3
+
+ functions-have-names@1.2.3: {}
+
+ gauge@3.0.2:
+ dependencies:
+ aproba: 2.0.0
+ color-support: 1.1.3
+ console-control-strings: 1.1.0
+ has-unicode: 2.0.1
+ object-assign: 4.1.1
+ signal-exit: 3.0.7
+ string-width: 4.2.3
+ strip-ansi: 6.0.1
+ wide-align: 1.1.5
+
+ gaxios@7.1.0:
+ dependencies:
+ extend: 3.0.2
+ https-proxy-agent: 7.0.6
+ node-fetch: 3.3.2
+ transitivePeerDependencies:
+ - supports-color
+
+ gensync@1.0.0-beta.2: {}
+
+ get-caller-file@2.0.5: {}
+
+ get-east-asian-width@1.3.0: {}
+
+ get-intrinsic@1.2.4:
+ dependencies:
+ es-errors: 1.3.0
+ function-bind: 1.1.2
+ has-proto: 1.0.3
+ has-symbols: 1.0.3
+ hasown: 2.0.2
+
+ get-intrinsic@1.3.0:
+ dependencies:
+ call-bind-apply-helpers: 1.0.2
+ es-define-property: 1.0.1
+ es-errors: 1.3.0
+ es-object-atoms: 1.1.1
+ function-bind: 1.1.2
+ get-proto: 1.0.1
+ gopd: 1.2.0
+ has-symbols: 1.1.0
+ hasown: 2.0.2
+ math-intrinsics: 1.1.0
+
+ get-proto@1.0.1:
+ dependencies:
+ dunder-proto: 1.0.1
+ es-object-atoms: 1.1.1
+
+ get-stream@5.2.0:
+ dependencies:
+ pump: 3.0.2
+
+ get-stream@6.0.1: {}
+
+ get-stream@8.0.1: {}
+
+ get-symbol-description@1.0.2:
+ dependencies:
+ call-bind: 1.0.7
+ es-errors: 1.3.0
+ get-intrinsic: 1.2.4
+
+ get-tsconfig@4.8.0:
+ dependencies:
+ resolve-pkg-maps: 1.0.0
+
+ getos@3.2.1:
+ dependencies:
+ async: 3.2.6
+
+ getpass@0.1.7:
+ dependencies:
+ assert-plus: 1.0.0
+
+ glob-parent@5.1.2:
+ dependencies:
+ is-glob: 4.0.3
+
+ glob-parent@6.0.2:
+ dependencies:
+ is-glob: 4.0.3
+
+ glob@10.3.10:
+ dependencies:
+ foreground-child: 3.3.0
+ jackspeak: 2.3.6
+ minimatch: 9.0.5
+ minipass: 7.1.2
+ path-scurry: 1.11.1
+
+ glob@10.4.5:
+ dependencies:
+ foreground-child: 3.3.1
+ jackspeak: 3.4.3
+ minimatch: 9.0.5
+ minipass: 7.1.2
+ package-json-from-dist: 1.0.1
+ path-scurry: 1.11.1
+
+ glob@7.2.3:
+ dependencies:
+ fs.realpath: 1.0.0
+ inflight: 1.0.6
+ inherits: 2.0.4
+ minimatch: 3.1.2
+ once: 1.4.0
+ path-is-absolute: 1.0.1
+
+ global-dirs@3.0.1:
+ dependencies:
+ ini: 2.0.0
+
+ globals@11.12.0: {}
+
+ globals@13.24.0:
+ dependencies:
+ type-fest: 0.20.2
+
+ globalthis@1.0.4:
+ dependencies:
+ define-properties: 1.2.1
+ gopd: 1.0.1
+
+ globby@11.1.0:
+ dependencies:
+ array-union: 2.1.0
+ dir-glob: 3.0.1
+ fast-glob: 3.3.3
+ ignore: 5.3.2
+ merge2: 1.4.1
+ slash: 3.0.0
+
+ globrex@0.1.2: {}
+
+ gopd@1.0.1:
+ dependencies:
+ get-intrinsic: 1.2.4
+
+ gopd@1.2.0: {}
+
+ graceful-fs@4.2.11: {}
+
+ graphemer@1.4.0: {}
+
+ grpc-tools@1.13.0:
+ dependencies:
+ '@mapbox/node-pre-gyp': 1.0.11
+ transitivePeerDependencies:
+ - encoding
+ - supports-color
+
+ has-bigints@1.0.2: {}
+
+ has-flag@3.0.0: {}
+
+ has-flag@4.0.0: {}
+
+ has-property-descriptors@1.0.2:
+ dependencies:
+ es-define-property: 1.0.0
+
+ has-proto@1.0.3: {}
+
+ has-symbols@1.0.3: {}
+
+ has-symbols@1.1.0: {}
+
+ has-tostringtag@1.0.2:
+ dependencies:
+ has-symbols: 1.1.0
+
+ has-unicode@2.0.1: {}
+
+ hasown@2.0.2:
+ dependencies:
+ function-bind: 1.1.2
+
+ html-encoding-sniffer@4.0.0:
+ dependencies:
+ whatwg-encoding: 3.1.1
+
+ http-proxy-agent@7.0.2:
+ dependencies:
+ agent-base: 7.1.3
+ debug: 4.4.0
+ transitivePeerDependencies:
+ - supports-color
+
+ http-signature@1.4.0:
+ dependencies:
+ assert-plus: 1.0.0
+ jsprim: 2.0.2
+ sshpk: 1.18.0
+
+ https-proxy-agent@5.0.1:
+ dependencies:
+ agent-base: 6.0.2
+ debug: 4.4.0
+ transitivePeerDependencies:
+ - supports-color
+
+ https-proxy-agent@7.0.6:
+ dependencies:
+ agent-base: 7.1.3
+ debug: 4.4.0
+ transitivePeerDependencies:
+ - supports-color
+
+ human-id@4.1.1: {}
+
+ human-signals@1.1.1: {}
+
+ human-signals@2.1.0: {}
+
+ human-signals@5.0.0: {}
+
+ iconv-lite@0.4.24:
+ dependencies:
+ safer-buffer: 2.1.2
+
+ iconv-lite@0.6.3:
+ dependencies:
+ safer-buffer: 2.1.2
+
+ ieee754@1.2.1: {}
+
+ ignore-by-default@1.0.1: {}
+
+ ignore@5.3.2: {}
+
+ immutable@5.1.1: {}
+
+ import-fresh@3.3.0:
+ dependencies:
+ parent-module: 1.0.1
+ resolve-from: 4.0.0
+
+ imurmurhash@0.1.4: {}
+
+ indent-string@4.0.0: {}
+
+ inflight@1.0.6:
+ dependencies:
+ once: 1.4.0
+ wrappy: 1.0.2
+
+ inherits@2.0.4: {}
+
+ ini@2.0.0: {}
+
+ internal-slot@1.0.7:
+ dependencies:
+ es-errors: 1.3.0
+ hasown: 2.0.2
+ side-channel: 1.0.6
+
+ intl-messageformat@10.7.7:
+ dependencies:
+ '@formatjs/ecma402-abstract': 2.2.4
+ '@formatjs/fast-memoize': 2.2.3
+ '@formatjs/icu-messageformat-parser': 2.9.4
+ tslib: 2.8.1
+
+ is-arguments@1.1.1:
+ dependencies:
+ call-bind: 1.0.7
+ has-tostringtag: 1.0.2
+
+ is-array-buffer@3.0.4:
+ dependencies:
+ call-bind: 1.0.7
+ get-intrinsic: 1.2.4
+
+ is-arrayish@0.3.2:
+ optional: true
+
+ is-async-function@2.0.0:
+ dependencies:
+ has-tostringtag: 1.0.2
+
+ is-bigint@1.0.4:
+ dependencies:
+ has-bigints: 1.0.2
+
+ is-binary-path@2.1.0:
+ dependencies:
+ binary-extensions: 2.3.0
+
+ is-boolean-object@1.1.2:
+ dependencies:
+ call-bind: 1.0.7
+ has-tostringtag: 1.0.2
+
+ is-bun-module@1.1.0:
+ dependencies:
+ semver: 7.7.1
+
+ is-callable@1.2.7: {}
+
+ is-core-module@2.15.1:
+ dependencies:
+ hasown: 2.0.2
+
+ is-data-view@1.0.1:
+ dependencies:
+ is-typed-array: 1.1.13
+
+ is-date-object@1.0.5:
+ dependencies:
+ has-tostringtag: 1.0.2
+
+ is-extglob@2.1.1: {}
+
+ is-finalizationregistry@1.0.2:
+ dependencies:
+ call-bind: 1.0.7
+
+ is-fullwidth-code-point@3.0.0: {}
+
+ is-fullwidth-code-point@4.0.0: {}
+
+ is-fullwidth-code-point@5.0.0:
+ dependencies:
+ get-east-asian-width: 1.3.0
+
+ is-generator-function@1.0.10:
+ dependencies:
+ has-tostringtag: 1.0.2
+
+ is-glob@4.0.3:
+ dependencies:
+ is-extglob: 2.1.1
+
+ is-installed-globally@0.4.0:
+ dependencies:
+ global-dirs: 3.0.1
+ is-path-inside: 3.0.3
+
+ is-map@2.0.3: {}
+
+ is-negative-zero@2.0.3: {}
+
+ is-number-object@1.0.7:
+ dependencies:
+ has-tostringtag: 1.0.2
+
+ is-number@7.0.0: {}
+
+ is-path-inside@3.0.3: {}
+
+ is-potential-custom-element-name@1.0.1: {}
+
+ is-regex@1.1.4:
+ dependencies:
+ call-bind: 1.0.7
+ has-tostringtag: 1.0.2
+
+ is-set@2.0.3: {}
+
+ is-shared-array-buffer@1.0.3:
+ dependencies:
+ call-bind: 1.0.7
+
+ is-stream@2.0.1: {}
+
+ is-stream@3.0.0: {}
+
+ is-string@1.0.7:
+ dependencies:
+ has-tostringtag: 1.0.2
+
+ is-subdir@1.2.0:
+ dependencies:
+ better-path-resolve: 1.0.0
+
+ is-symbol@1.0.4:
+ dependencies:
+ has-symbols: 1.0.3
+
+ is-typed-array@1.1.13:
+ dependencies:
+ which-typed-array: 1.1.15
+
+ is-typedarray@1.0.0: {}
+
+ is-unicode-supported@0.1.0: {}
+
+ is-weakmap@2.0.2: {}
+
+ is-weakref@1.0.2:
+ dependencies:
+ call-bind: 1.0.7
+
+ is-weakset@2.0.3:
+ dependencies:
+ call-bind: 1.0.7
+ get-intrinsic: 1.3.0
+
+ is-windows@1.0.2: {}
+
+ isarray@2.0.5: {}
+
+ isexe@2.0.0: {}
+
+ isstream@0.1.2: {}
+
+ iterator.prototype@1.1.2:
+ dependencies:
+ define-properties: 1.2.1
+ get-intrinsic: 1.2.4
+ has-symbols: 1.0.3
+ reflect.getprototypeof: 1.0.6
+ set-function-name: 2.0.2
+
+ jackspeak@2.3.6:
+ dependencies:
+ '@isaacs/cliui': 8.0.2
+ optionalDependencies:
+ '@pkgjs/parseargs': 0.11.0
+
+ jackspeak@3.4.3:
+ dependencies:
+ '@isaacs/cliui': 8.0.2
+ optionalDependencies:
+ '@pkgjs/parseargs': 0.11.0
+
+ jiti@1.21.6: {}
+
+ joi@17.13.3:
+ dependencies:
+ '@hapi/hoek': 9.3.0
+ '@hapi/topo': 5.1.0
+ '@sideway/address': 4.1.5
+ '@sideway/formula': 3.0.1
+ '@sideway/pinpoint': 2.0.0
+
+ jose@5.8.0: {}
+
+ joycon@3.1.1: {}
+
+ js-tokens@4.0.0: {}
+
+ js-yaml@3.14.1:
+ dependencies:
+ argparse: 1.0.10
+ esprima: 4.0.1
+
+ js-yaml@4.1.0:
+ dependencies:
+ argparse: 2.0.1
+
+ jsbn@0.1.1: {}
+
+ jsdom@26.1.0:
+ dependencies:
+ cssstyle: 4.3.1
+ data-urls: 5.0.0
+ decimal.js: 10.5.0
+ html-encoding-sniffer: 4.0.0
+ http-proxy-agent: 7.0.2
+ https-proxy-agent: 7.0.6
+ is-potential-custom-element-name: 1.0.1
+ nwsapi: 2.2.20
+ parse5: 7.3.0
+ rrweb-cssom: 0.8.0
+ saxes: 6.0.0
+ symbol-tree: 3.2.4
+ tough-cookie: 5.1.2
+ w3c-xmlserializer: 5.0.0
+ webidl-conversions: 7.0.0
+ whatwg-encoding: 3.1.1
+ whatwg-mimetype: 4.0.0
+ whatwg-url: 14.2.0
+ ws: 8.18.1
+ xml-name-validator: 5.0.0
+ transitivePeerDependencies:
+ - bufferutil
+ - supports-color
+ - utf-8-validate
+
+ jsesc@3.1.0: {}
+
+ json-buffer@3.0.1: {}
+
+ json-schema-traverse@0.4.1: {}
+
+ json-schema@0.4.0: {}
+
+ json-stable-stringify-without-jsonify@1.0.1: {}
+
+ json-stringify-safe@5.0.1: {}
+
+ json5@1.0.2:
+ dependencies:
+ minimist: 1.2.8
+
+ json5@2.2.3: {}
+
+ jsonfile@4.0.0:
+ optionalDependencies:
+ graceful-fs: 4.2.11
+
+ jsonfile@6.1.0:
+ dependencies:
+ universalify: 2.0.1
+ optionalDependencies:
+ graceful-fs: 4.2.11
+
+ jsprim@2.0.2:
+ dependencies:
+ assert-plus: 1.0.0
+ extsprintf: 1.3.0
+ json-schema: 0.4.0
+ verror: 1.10.0
+
+ jsx-ast-utils@3.3.5:
+ dependencies:
+ array-includes: 3.1.8
+ array.prototype.flat: 1.3.2
+ object.assign: 4.1.5
+ object.values: 1.2.0
+
+ keyv@4.5.4:
+ dependencies:
+ json-buffer: 3.0.1
+
+ language-subtag-registry@0.3.23: {}
+
+ language-tags@1.0.9:
+ dependencies:
+ language-subtag-registry: 0.3.23
+
+ lazy-ass@1.6.0: {}
+
+ levn@0.4.1:
+ dependencies:
+ prelude-ls: 1.2.1
+ type-check: 0.4.0
+
+ lilconfig@2.1.0: {}
+
+ lilconfig@3.1.3: {}
+
+ lines-and-columns@1.2.4: {}
+
+ lint-staged@15.5.1:
+ dependencies:
+ chalk: 5.4.1
+ commander: 13.1.0
+ debug: 4.4.0
+ execa: 8.0.1
+ lilconfig: 3.1.3
+ listr2: 8.3.2
+ micromatch: 4.0.8
+ pidtree: 0.6.0
+ string-argv: 0.3.2
+ yaml: 2.7.1
+ transitivePeerDependencies:
+ - supports-color
+
+ listr2@3.14.0(enquirer@2.4.1):
+ dependencies:
+ cli-truncate: 2.1.0
+ colorette: 2.0.20
+ log-update: 4.0.0
+ p-map: 4.0.0
+ rfdc: 1.4.1
+ rxjs: 7.8.2
+ through: 2.3.8
+ wrap-ansi: 7.0.0
+ optionalDependencies:
+ enquirer: 2.4.1
+
+ listr2@8.3.2:
+ dependencies:
+ cli-truncate: 4.0.0
+ colorette: 2.0.20
+ eventemitter3: 5.0.1
+ log-update: 6.1.0
+ rfdc: 1.4.1
+ wrap-ansi: 9.0.0
+
+ load-tsconfig@0.2.5: {}
+
+ locate-path@5.0.0:
+ dependencies:
+ p-locate: 4.1.0
+
+ locate-path@6.0.0:
+ dependencies:
+ p-locate: 5.0.0
+
+ lodash.camelcase@4.3.0: {}
+
+ lodash.merge@4.6.2: {}
+
+ lodash.once@4.1.1: {}
+
+ lodash.sortby@4.7.0: {}
+
+ lodash.startcase@4.4.0: {}
+
+ lodash@4.17.21: {}
+
+ log-symbols@4.1.0:
+ dependencies:
+ chalk: 4.1.2
+ is-unicode-supported: 0.1.0
+
+ log-update@4.0.0:
+ dependencies:
+ ansi-escapes: 4.3.2
+ cli-cursor: 3.1.0
+ slice-ansi: 4.0.0
+ wrap-ansi: 6.2.0
+
+ log-update@6.1.0:
+ dependencies:
+ ansi-escapes: 7.0.0
+ cli-cursor: 5.0.0
+ slice-ansi: 7.1.0
+ strip-ansi: 7.1.0
+ wrap-ansi: 9.0.0
+
+ long@5.2.3: {}
+
+ loose-envify@1.4.0:
+ dependencies:
+ js-tokens: 4.0.0
+
+ loupe@3.1.3: {}
+
+ lru-cache@10.4.3: {}
+
+ lru-cache@5.1.1:
+ dependencies:
+ yallist: 3.1.1
+
+ lucide-react@0.469.0(react@19.1.0):
+ dependencies:
+ react: 19.1.0
+
+ lz-string@1.5.0: {}
+
+ magic-string@0.30.17:
+ dependencies:
+ '@jridgewell/sourcemap-codec': 1.5.0
+
+ make-dir-cli@4.0.0:
+ dependencies:
+ make-dir: 5.0.0
+ meow: 13.2.0
+
+ make-dir@3.1.0:
+ dependencies:
+ semver: 6.3.1
+
+ make-dir@5.0.0: {}
+
+ map-stream@0.1.0: {}
+
+ math-intrinsics@1.1.0: {}
+
+ meow@13.2.0: {}
+
+ merge-stream@2.0.0: {}
+
+ merge2@1.4.1: {}
+
+ micromatch@4.0.8:
+ dependencies:
+ braces: 3.0.3
+ picomatch: 2.3.1
+
+ mime-db@1.52.0: {}
+
+ mime-types@2.1.35:
+ dependencies:
+ mime-db: 1.52.0
+
+ mimic-fn@2.1.0: {}
+
+ mimic-fn@4.0.0: {}
+
+ mimic-function@5.0.1: {}
+
+ min-indent@1.0.1: {}
+
+ mini-svg-data-uri@1.4.4: {}
+
+ minimatch@3.1.2:
+ dependencies:
+ brace-expansion: 1.1.11
+
+ minimatch@9.0.5:
+ dependencies:
+ brace-expansion: 2.0.1
+
+ minimist@1.2.8: {}
+
+ minipass@3.3.6:
+ dependencies:
+ yallist: 4.0.0
+
+ minipass@5.0.0: {}
+
+ minipass@7.1.2: {}
+
+ minizlib@2.1.2:
+ dependencies:
+ minipass: 3.3.6
+ yallist: 4.0.0
+
+ mkdirp@1.0.4: {}
+
+ moment@2.30.1: {}
+
+ mri@1.2.0: {}
+
+ ms@2.1.3: {}
+
+ mz@2.7.0:
+ dependencies:
+ any-promise: 1.3.0
+ object-assign: 4.1.1
+ thenify-all: 1.6.0
+
+ nanoid@3.3.11: {}
+
+ natural-compare@1.4.0: {}
+
+ negotiator@1.0.0: {}
+
+ next-intl@3.26.5(next@15.4.0-canary.86(@playwright/test@1.52.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(sass@1.87.0))(react@19.1.0):
+ dependencies:
+ '@formatjs/intl-localematcher': 0.5.8
+ negotiator: 1.0.0
+ next: 15.4.0-canary.86(@playwright/test@1.52.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(sass@1.87.0)
+ react: 19.1.0
+ use-intl: 3.26.5(react@19.1.0)
+
+ next-themes@0.2.1(next@15.4.0-canary.86(@playwright/test@1.52.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(sass@1.87.0))(react-dom@19.1.0(react@19.1.0))(react@19.1.0):
+ dependencies:
+ next: 15.4.0-canary.86(@playwright/test@1.52.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(sass@1.87.0)
+ react: 19.1.0
+ react-dom: 19.1.0(react@19.1.0)
+
+ next@15.4.0-canary.86(@playwright/test@1.52.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(sass@1.87.0):
+ dependencies:
+ '@next/env': 15.4.0-canary.86
+ '@swc/helpers': 0.5.15
+ caniuse-lite: 1.0.30001715
+ postcss: 8.4.31
+ react: 19.1.0
+ react-dom: 19.1.0(react@19.1.0)
+ styled-jsx: 5.1.6(react@19.1.0)
+ optionalDependencies:
+ '@next/swc-darwin-arm64': 15.4.0-canary.86
+ '@next/swc-darwin-x64': 15.4.0-canary.86
+ '@next/swc-linux-arm64-gnu': 15.4.0-canary.86
+ '@next/swc-linux-arm64-musl': 15.4.0-canary.86
+ '@next/swc-linux-x64-gnu': 15.4.0-canary.86
+ '@next/swc-linux-x64-musl': 15.4.0-canary.86
+ '@next/swc-win32-arm64-msvc': 15.4.0-canary.86
+ '@next/swc-win32-x64-msvc': 15.4.0-canary.86
+ '@playwright/test': 1.52.0
+ sass: 1.87.0
+ sharp: 0.34.1
+ transitivePeerDependencies:
+ - '@babel/core'
+ - babel-plugin-macros
+
+ nice-grpc-common@2.0.2:
+ dependencies:
+ ts-error: 1.0.6
+
+ nice-grpc@2.0.1:
+ dependencies:
+ '@grpc/grpc-js': 1.11.1
+ abort-controller-x: 0.4.3
+ nice-grpc-common: 2.0.2
+
+ node-addon-api@7.1.1:
+ optional: true
+
+ node-domexception@1.0.0: {}
+
+ node-fetch@2.7.0:
+ dependencies:
+ whatwg-url: 5.0.0
+
+ node-fetch@3.3.2:
+ dependencies:
+ data-uri-to-buffer: 4.0.1
+ fetch-blob: 3.2.0
+ formdata-polyfill: 4.0.10
+
+ node-releases@2.0.19: {}
+
+ nodemon@3.1.9:
+ dependencies:
+ chokidar: 3.6.0
+ debug: 4.4.0(supports-color@5.5.0)
+ ignore-by-default: 1.0.1
+ minimatch: 3.1.2
+ pstree.remy: 1.1.8
+ semver: 7.7.1
+ simple-update-notifier: 2.0.0
+ supports-color: 5.5.0
+ touch: 3.1.1
+ undefsafe: 2.0.5
+
+ nopt@5.0.0:
+ dependencies:
+ abbrev: 1.1.1
+
+ normalize-path@3.0.0: {}
+
+ normalize-range@0.1.2: {}
+
+ npm-run-path@4.0.1:
+ dependencies:
+ path-key: 3.1.1
+
+ npm-run-path@5.3.0:
+ dependencies:
+ path-key: 4.0.0
+
+ npmlog@5.0.1:
+ dependencies:
+ are-we-there-yet: 2.0.0
+ console-control-strings: 1.1.0
+ gauge: 3.0.2
+ set-blocking: 2.0.0
+
+ nwsapi@2.2.20: {}
+
+ object-assign@4.1.1: {}
+
+ object-hash@3.0.0: {}
+
+ object-inspect@1.13.2: {}
+
+ object-inspect@1.13.4: {}
+
+ object-is@1.1.6:
+ dependencies:
+ call-bind: 1.0.7
+ define-properties: 1.2.1
+
+ object-keys@1.1.1: {}
+
+ object.assign@4.1.5:
+ dependencies:
+ call-bind: 1.0.7
+ define-properties: 1.2.1
+ has-symbols: 1.0.3
+ object-keys: 1.1.1
+
+ object.entries@1.1.8:
+ dependencies:
+ call-bind: 1.0.7
+ define-properties: 1.2.1
+ es-object-atoms: 1.0.0
+
+ object.fromentries@2.0.8:
+ dependencies:
+ call-bind: 1.0.7
+ define-properties: 1.2.1
+ es-abstract: 1.23.3
+ es-object-atoms: 1.0.0
+
+ object.groupby@1.0.3:
+ dependencies:
+ call-bind: 1.0.7
+ define-properties: 1.2.1
+ es-abstract: 1.23.3
+
+ object.values@1.2.0:
+ dependencies:
+ call-bind: 1.0.7
+ define-properties: 1.2.1
+ es-object-atoms: 1.0.0
+
+ once@1.4.0:
+ dependencies:
+ wrappy: 1.0.2
+
+ onetime@5.1.2:
+ dependencies:
+ mimic-fn: 2.1.0
+
+ onetime@6.0.0:
+ dependencies:
+ mimic-fn: 4.0.0
+
+ onetime@7.0.0:
+ dependencies:
+ mimic-function: 5.0.1
+
+ optionator@0.9.4:
+ dependencies:
+ deep-is: 0.1.4
+ fast-levenshtein: 2.0.6
+ levn: 0.4.1
+ prelude-ls: 1.2.1
+ type-check: 0.4.0
+ word-wrap: 1.2.5
+
+ os-tmpdir@1.0.2: {}
+
+ ospath@1.2.2: {}
+
+ outdent@0.5.0: {}
+
+ p-filter@2.1.0:
+ dependencies:
+ p-map: 2.1.0
+
+ p-limit@2.3.0:
+ dependencies:
+ p-try: 2.2.0
+
+ p-limit@3.1.0:
+ dependencies:
+ yocto-queue: 0.1.0
+
+ p-locate@4.1.0:
+ dependencies:
+ p-limit: 2.3.0
+
+ p-locate@5.0.0:
+ dependencies:
+ p-limit: 3.1.0
+
+ p-map@2.1.0: {}
+
+ p-map@4.0.0:
+ dependencies:
+ aggregate-error: 3.1.0
+
+ p-try@2.2.0: {}
+
+ package-json-from-dist@1.0.1: {}
+
+ package-manager-detector@0.2.11:
+ dependencies:
+ quansync: 0.2.10
+
+ parent-module@1.0.1:
+ dependencies:
+ callsites: 3.1.0
+
+ parse5@7.3.0:
+ dependencies:
+ entities: 6.0.0
+
+ path-exists@4.0.0: {}
+
+ path-is-absolute@1.0.1: {}
+
+ path-key@3.1.1: {}
+
+ path-key@4.0.0: {}
+
+ path-parse@1.0.7: {}
+
+ path-scurry@1.11.1:
+ dependencies:
+ lru-cache: 10.4.3
+ minipass: 7.1.2
+
+ path-type@4.0.0: {}
+
+ pathe@2.0.3: {}
+
+ pathval@2.0.0: {}
+
+ pause-stream@0.0.11:
+ dependencies:
+ through: 2.3.8
+
+ pend@1.2.0: {}
+
+ performance-now@2.1.0: {}
+
+ picocolors@1.1.1: {}
+
+ picomatch@2.3.1: {}
+
+ picomatch@4.0.2: {}
+
+ pidtree@0.6.0: {}
+
+ pify@2.3.0: {}
+
+ pify@4.0.1: {}
+
+ pirates@4.0.7: {}
+
+ playwright-core@1.52.0: {}
+
+ playwright@1.52.0:
+ dependencies:
+ playwright-core: 1.52.0
+ optionalDependencies:
+ fsevents: 2.3.2
+
+ possible-typed-array-names@1.0.0: {}
+
+ postcss-import@15.1.0(postcss@8.5.3):
+ dependencies:
+ postcss: 8.5.3
+ postcss-value-parser: 4.2.0
+ read-cache: 1.0.0
+ resolve: 1.22.8
+
+ postcss-js@4.0.1(postcss@8.5.3):
+ dependencies:
+ camelcase-css: 2.0.1
+ postcss: 8.5.3
+
+ postcss-load-config@4.0.2(postcss@8.5.3):
+ dependencies:
+ lilconfig: 3.1.3
+ yaml: 2.7.1
+ optionalDependencies:
+ postcss: 8.5.3
+
+ postcss-load-config@6.0.1(jiti@1.21.6)(postcss@8.5.3)(yaml@2.7.1):
+ dependencies:
+ lilconfig: 3.1.3
+ optionalDependencies:
+ jiti: 1.21.6
+ postcss: 8.5.3
+ yaml: 2.7.1
+
+ postcss-nested@6.2.0(postcss@8.5.3):
+ dependencies:
+ postcss: 8.5.3
+ postcss-selector-parser: 6.1.2
+
+ postcss-selector-parser@6.1.2:
+ dependencies:
+ cssesc: 3.0.0
+ util-deprecate: 1.0.2
+
+ postcss-value-parser@4.2.0: {}
+
+ postcss@8.4.31:
+ dependencies:
+ nanoid: 3.3.11
+ picocolors: 1.1.1
+ source-map-js: 1.2.1
+
+ postcss@8.5.3:
+ dependencies:
+ nanoid: 3.3.11
+ picocolors: 1.1.1
+ source-map-js: 1.2.1
+
+ prelude-ls@1.2.1: {}
+
+ prettier-plugin-organize-imports@4.1.0(prettier@3.5.3)(typescript@5.8.3):
+ dependencies:
+ prettier: 3.5.3
+ typescript: 5.8.3
+
+ prettier-plugin-tailwindcss@0.6.11(prettier-plugin-organize-imports@4.1.0(prettier@3.5.3)(typescript@5.8.3))(prettier@3.5.3):
+ dependencies:
+ prettier: 3.5.3
+ optionalDependencies:
+ prettier-plugin-organize-imports: 4.1.0(prettier@3.5.3)(typescript@5.8.3)
+
+ prettier@2.8.8: {}
+
+ prettier@3.5.3: {}
+
+ pretty-bytes@5.6.0: {}
+
+ pretty-format@27.5.1:
+ dependencies:
+ ansi-regex: 5.0.1
+ ansi-styles: 5.2.0
+ react-is: 17.0.2
+
+ process@0.11.10: {}
+
+ prop-types@15.8.1:
+ dependencies:
+ loose-envify: 1.4.0
+ object-assign: 4.1.1
+ react-is: 16.13.1
+
+ protobufjs@7.4.0:
+ dependencies:
+ '@protobufjs/aspromise': 1.1.2
+ '@protobufjs/base64': 1.1.2
+ '@protobufjs/codegen': 2.0.4
+ '@protobufjs/eventemitter': 1.1.0
+ '@protobufjs/fetch': 1.1.0
+ '@protobufjs/float': 1.0.2
+ '@protobufjs/inquire': 1.1.0
+ '@protobufjs/path': 1.1.2
+ '@protobufjs/pool': 1.1.0
+ '@protobufjs/utf8': 1.1.0
+ '@types/node': 22.14.1
+ long: 5.2.3
+
+ proxy-from-env@1.0.0: {}
+
+ proxy-from-env@1.1.0: {}
+
+ ps-tree@1.2.0:
+ dependencies:
+ event-stream: 3.3.4
+
+ pstree.remy@1.1.8: {}
+
+ pump@3.0.2:
+ dependencies:
+ end-of-stream: 1.4.4
+ once: 1.4.0
+
+ punycode@2.3.1: {}
+
+ qrcode.react@3.1.0(react@19.1.0):
+ dependencies:
+ react: 19.1.0
+
+ qs@6.14.0:
+ dependencies:
+ side-channel: 1.1.0
+
+ quansync@0.2.10: {}
+
+ queue-microtask@1.2.3: {}
+
+ react-dom@19.1.0(react@19.1.0):
+ dependencies:
+ react: 19.1.0
+ scheduler: 0.26.0
+
+ react-hook-form@7.39.5(react@19.1.0):
+ dependencies:
+ react: 19.1.0
+
+ react-is@16.13.1: {}
+
+ react-is@17.0.2: {}
+
+ react-refresh@0.17.0: {}
+
+ react@19.1.0: {}
+
+ read-cache@1.0.0:
+ dependencies:
+ pify: 2.3.0
+
+ read-yaml-file@1.1.0:
+ dependencies:
+ graceful-fs: 4.2.11
+ js-yaml: 3.14.1
+ pify: 4.0.1
+ strip-bom: 3.0.0
+
+ readable-stream@3.6.2:
+ dependencies:
+ inherits: 2.0.4
+ string_decoder: 1.3.0
+ util-deprecate: 1.0.2
+
+ readdirp@3.6.0:
+ dependencies:
+ picomatch: 2.3.1
+
+ readdirp@4.1.2: {}
+
+ redent@3.0.0:
+ dependencies:
+ indent-string: 4.0.0
+ strip-indent: 3.0.0
+
+ reflect.getprototypeof@1.0.6:
+ dependencies:
+ call-bind: 1.0.7
+ define-properties: 1.2.1
+ es-abstract: 1.23.3
+ es-errors: 1.3.0
+ get-intrinsic: 1.2.4
+ globalthis: 1.0.4
+ which-builtin-type: 1.1.4
+
+ regenerator-runtime@0.14.1: {}
+
+ regexp.prototype.flags@1.5.2:
+ dependencies:
+ call-bind: 1.0.7
+ define-properties: 1.2.1
+ es-errors: 1.3.0
+ set-function-name: 2.0.2
+
+ request-progress@3.0.0:
+ dependencies:
+ throttleit: 1.0.1
+
+ require-directory@2.1.1: {}
+
+ resolve-from@4.0.0: {}
+
+ resolve-from@5.0.0: {}
+
+ resolve-pkg-maps@1.0.0: {}
+
+ resolve@1.22.8:
+ dependencies:
+ is-core-module: 2.15.1
+ path-parse: 1.0.7
+ supports-preserve-symlinks-flag: 1.0.0
+
+ resolve@2.0.0-next.5:
+ dependencies:
+ is-core-module: 2.15.1
+ path-parse: 1.0.7
+ supports-preserve-symlinks-flag: 1.0.0
+
+ restore-cursor@3.1.0:
+ dependencies:
+ onetime: 5.1.2
+ signal-exit: 3.0.7
+
+ restore-cursor@5.1.0:
+ dependencies:
+ onetime: 7.0.0
+ signal-exit: 4.1.0
+
+ reusify@1.0.4: {}
+
+ rfdc@1.4.1: {}
+
+ rimraf@3.0.2:
+ dependencies:
+ glob: 7.2.3
+
+ rollup@4.40.0:
+ dependencies:
+ '@types/estree': 1.0.7
+ optionalDependencies:
+ '@rollup/rollup-android-arm-eabi': 4.40.0
+ '@rollup/rollup-android-arm64': 4.40.0
+ '@rollup/rollup-darwin-arm64': 4.40.0
+ '@rollup/rollup-darwin-x64': 4.40.0
+ '@rollup/rollup-freebsd-arm64': 4.40.0
+ '@rollup/rollup-freebsd-x64': 4.40.0
+ '@rollup/rollup-linux-arm-gnueabihf': 4.40.0
+ '@rollup/rollup-linux-arm-musleabihf': 4.40.0
+ '@rollup/rollup-linux-arm64-gnu': 4.40.0
+ '@rollup/rollup-linux-arm64-musl': 4.40.0
+ '@rollup/rollup-linux-loongarch64-gnu': 4.40.0
+ '@rollup/rollup-linux-powerpc64le-gnu': 4.40.0
+ '@rollup/rollup-linux-riscv64-gnu': 4.40.0
+ '@rollup/rollup-linux-riscv64-musl': 4.40.0
+ '@rollup/rollup-linux-s390x-gnu': 4.40.0
+ '@rollup/rollup-linux-x64-gnu': 4.40.0
+ '@rollup/rollup-linux-x64-musl': 4.40.0
+ '@rollup/rollup-win32-arm64-msvc': 4.40.0
+ '@rollup/rollup-win32-ia32-msvc': 4.40.0
+ '@rollup/rollup-win32-x64-msvc': 4.40.0
+ fsevents: 2.3.3
+
+ rrweb-cssom@0.8.0: {}
+
+ run-parallel@1.2.0:
+ dependencies:
+ queue-microtask: 1.2.3
+
+ rxjs@7.8.2:
+ dependencies:
+ tslib: 2.8.1
+
+ safe-array-concat@1.1.2:
+ dependencies:
+ call-bind: 1.0.7
+ get-intrinsic: 1.2.4
+ has-symbols: 1.0.3
+ isarray: 2.0.5
+
+ safe-buffer@5.2.1: {}
+
+ safe-regex-test@1.0.3:
+ dependencies:
+ call-bind: 1.0.7
+ es-errors: 1.3.0
+ is-regex: 1.1.4
+
+ safer-buffer@2.1.2: {}
+
+ sass@1.87.0:
+ dependencies:
+ chokidar: 4.0.3
+ immutable: 5.1.1
+ source-map-js: 1.2.1
+ optionalDependencies:
+ '@parcel/watcher': 2.5.1
+
+ saxes@6.0.0:
+ dependencies:
+ xmlchars: 2.2.0
+
+ scheduler@0.26.0: {}
+
+ semver@6.3.1: {}
+
+ semver@7.7.1: {}
+
+ server-only@0.0.1: {}
+
+ set-blocking@2.0.0: {}
+
+ set-function-length@1.2.2:
+ dependencies:
+ define-data-property: 1.1.4
+ es-errors: 1.3.0
+ function-bind: 1.1.2
+ get-intrinsic: 1.3.0
+ gopd: 1.2.0
+ has-property-descriptors: 1.0.2
+
+ set-function-name@2.0.2:
+ dependencies:
+ define-data-property: 1.1.4
+ es-errors: 1.3.0
+ functions-have-names: 1.2.3
+ has-property-descriptors: 1.0.2
+
+ sharp@0.34.1:
+ dependencies:
+ color: 4.2.3
+ detect-libc: 2.0.4
+ semver: 7.7.1
+ optionalDependencies:
+ '@img/sharp-darwin-arm64': 0.34.1
+ '@img/sharp-darwin-x64': 0.34.1
+ '@img/sharp-libvips-darwin-arm64': 1.1.0
+ '@img/sharp-libvips-darwin-x64': 1.1.0
+ '@img/sharp-libvips-linux-arm': 1.1.0
+ '@img/sharp-libvips-linux-arm64': 1.1.0
+ '@img/sharp-libvips-linux-ppc64': 1.1.0
+ '@img/sharp-libvips-linux-s390x': 1.1.0
+ '@img/sharp-libvips-linux-x64': 1.1.0
+ '@img/sharp-libvips-linuxmusl-arm64': 1.1.0
+ '@img/sharp-libvips-linuxmusl-x64': 1.1.0
+ '@img/sharp-linux-arm': 0.34.1
+ '@img/sharp-linux-arm64': 0.34.1
+ '@img/sharp-linux-s390x': 0.34.1
+ '@img/sharp-linux-x64': 0.34.1
+ '@img/sharp-linuxmusl-arm64': 0.34.1
+ '@img/sharp-linuxmusl-x64': 0.34.1
+ '@img/sharp-wasm32': 0.34.1
+ '@img/sharp-win32-ia32': 0.34.1
+ '@img/sharp-win32-x64': 0.34.1
+ optional: true
+
+ shebang-command@2.0.0:
+ dependencies:
+ shebang-regex: 3.0.0
+
+ shebang-regex@3.0.0: {}
+
+ shell-quote@1.8.2: {}
+
+ side-channel-list@1.0.0:
+ dependencies:
+ es-errors: 1.3.0
+ object-inspect: 1.13.4
+
+ side-channel-map@1.0.1:
+ dependencies:
+ call-bound: 1.0.4
+ es-errors: 1.3.0
+ get-intrinsic: 1.3.0
+ object-inspect: 1.13.4
+
+ side-channel-weakmap@1.0.2:
+ dependencies:
+ call-bound: 1.0.4
+ es-errors: 1.3.0
+ get-intrinsic: 1.3.0
+ object-inspect: 1.13.4
+ side-channel-map: 1.0.1
+
+ side-channel@1.0.6:
+ dependencies:
+ call-bind: 1.0.7
+ es-errors: 1.3.0
+ get-intrinsic: 1.3.0
+ object-inspect: 1.13.2
+
+ side-channel@1.1.0:
+ dependencies:
+ es-errors: 1.3.0
+ object-inspect: 1.13.4
+ side-channel-list: 1.0.0
+ side-channel-map: 1.0.1
+ side-channel-weakmap: 1.0.2
+
+ siginfo@2.0.0: {}
+
+ signal-exit@3.0.7: {}
+
+ signal-exit@4.1.0: {}
+
+ simple-swizzle@0.2.2:
+ dependencies:
+ is-arrayish: 0.3.2
+ optional: true
+
+ simple-update-notifier@2.0.0:
+ dependencies:
+ semver: 7.7.1
+
+ slash@3.0.0: {}
+
+ slice-ansi@3.0.0:
+ dependencies:
+ ansi-styles: 4.3.0
+ astral-regex: 2.0.0
+ is-fullwidth-code-point: 3.0.0
+
+ slice-ansi@4.0.0:
+ dependencies:
+ ansi-styles: 4.3.0
+ astral-regex: 2.0.0
+ is-fullwidth-code-point: 3.0.0
+
+ slice-ansi@5.0.0:
+ dependencies:
+ ansi-styles: 6.2.1
+ is-fullwidth-code-point: 4.0.0
+
+ slice-ansi@7.1.0:
+ dependencies:
+ ansi-styles: 6.2.1
+ is-fullwidth-code-point: 5.0.0
+
+ source-map-js@1.2.1: {}
+
+ source-map@0.8.0-beta.0:
+ dependencies:
+ whatwg-url: 7.1.0
+
+ spawndamnit@3.0.1:
+ dependencies:
+ cross-spawn: 7.0.6
+ signal-exit: 4.1.0
+
+ split@0.3.3:
+ dependencies:
+ through: 2.3.8
+
+ sprintf-js@1.0.3: {}
+
+ sshpk@1.18.0:
+ dependencies:
+ asn1: 0.2.6
+ assert-plus: 1.0.0
+ bcrypt-pbkdf: 1.0.2
+ dashdash: 1.14.1
+ ecc-jsbn: 0.1.2
+ getpass: 0.1.7
+ jsbn: 0.1.1
+ safer-buffer: 2.1.2
+ tweetnacl: 0.14.5
+
+ stackback@0.0.2: {}
+
+ start-server-and-test@2.0.11:
+ dependencies:
+ arg: 5.0.2
+ bluebird: 3.7.2
+ check-more-types: 2.24.0
+ debug: 4.4.0
+ execa: 5.1.1
+ lazy-ass: 1.6.0
+ ps-tree: 1.2.0
+ wait-on: 8.0.3(debug@4.4.0)
+ transitivePeerDependencies:
+ - supports-color
+
+ std-env@3.9.0: {}
+
+ stop-iteration-iterator@1.0.0:
+ dependencies:
+ internal-slot: 1.0.7
+
+ stream-combiner@0.0.4:
+ dependencies:
+ duplexer: 0.1.2
+
+ string-argv@0.3.2: {}
+
+ string-width@4.2.3:
+ dependencies:
+ emoji-regex: 8.0.0
+ is-fullwidth-code-point: 3.0.0
+ strip-ansi: 6.0.1
+
+ string-width@5.1.2:
+ dependencies:
+ eastasianwidth: 0.2.0
+ emoji-regex: 9.2.2
+ strip-ansi: 7.1.0
+
+ string-width@7.2.0:
+ dependencies:
+ emoji-regex: 10.4.0
+ get-east-asian-width: 1.3.0
+ strip-ansi: 7.1.0
+
+ string.prototype.includes@2.0.0:
+ dependencies:
+ define-properties: 1.2.1
+ es-abstract: 1.23.3
+
+ string.prototype.matchall@4.0.11:
+ dependencies:
+ call-bind: 1.0.7
+ define-properties: 1.2.1
+ es-abstract: 1.23.3
+ es-errors: 1.3.0
+ es-object-atoms: 1.0.0
+ get-intrinsic: 1.2.4
+ gopd: 1.0.1
+ has-symbols: 1.0.3
+ internal-slot: 1.0.7
+ regexp.prototype.flags: 1.5.2
+ set-function-name: 2.0.2
+ side-channel: 1.0.6
+
+ string.prototype.repeat@1.0.0:
+ dependencies:
+ define-properties: 1.2.1
+ es-abstract: 1.23.3
+
+ string.prototype.trim@1.2.9:
+ dependencies:
+ call-bind: 1.0.7
+ define-properties: 1.2.1
+ es-abstract: 1.23.3
+ es-object-atoms: 1.0.0
+
+ string.prototype.trimend@1.0.8:
+ dependencies:
+ call-bind: 1.0.7
+ define-properties: 1.2.1
+ es-object-atoms: 1.0.0
+
+ string.prototype.trimstart@1.0.8:
+ dependencies:
+ call-bind: 1.0.7
+ define-properties: 1.2.1
+ es-object-atoms: 1.0.0
+
+ string_decoder@1.3.0:
+ dependencies:
+ safe-buffer: 5.2.1
+
+ strip-ansi@6.0.1:
+ dependencies:
+ ansi-regex: 5.0.1
+
+ strip-ansi@7.1.0:
+ dependencies:
+ ansi-regex: 6.1.0
+
+ strip-bom@3.0.0: {}
+
+ strip-final-newline@2.0.0: {}
+
+ strip-final-newline@3.0.0: {}
+
+ strip-indent@3.0.0:
+ dependencies:
+ min-indent: 1.0.1
+
+ strip-json-comments@3.1.1: {}
+
+ styled-jsx@5.1.6(react@19.1.0):
+ dependencies:
+ client-only: 0.0.1
+ react: 19.1.0
+
+ sucrase@3.35.0:
+ dependencies:
+ '@jridgewell/gen-mapping': 0.3.8
+ commander: 4.1.1
+ glob: 10.4.5
+ lines-and-columns: 1.2.4
+ mz: 2.7.0
+ pirates: 4.0.7
+ ts-interface-checker: 0.1.13
+
+ supports-color@5.5.0:
+ dependencies:
+ has-flag: 3.0.0
+
+ supports-color@7.2.0:
+ dependencies:
+ has-flag: 4.0.0
+
+ supports-color@8.1.1:
+ dependencies:
+ has-flag: 4.0.0
+
+ supports-preserve-symlinks-flag@1.0.0: {}
+
+ symbol-tree@3.2.4: {}
+
+ tabbable@6.2.0: {}
+
+ tailwindcss@3.4.14:
+ dependencies:
+ '@alloc/quick-lru': 5.2.0
+ arg: 5.0.2
+ chokidar: 3.6.0
+ didyoumean: 1.2.2
+ dlv: 1.1.3
+ fast-glob: 3.3.3
+ glob-parent: 6.0.2
+ is-glob: 4.0.3
+ jiti: 1.21.6
+ lilconfig: 2.1.0
+ micromatch: 4.0.8
+ normalize-path: 3.0.0
+ object-hash: 3.0.0
+ picocolors: 1.1.1
+ postcss: 8.5.3
+ postcss-import: 15.1.0(postcss@8.5.3)
+ postcss-js: 4.0.1(postcss@8.5.3)
+ postcss-load-config: 4.0.2(postcss@8.5.3)
+ postcss-nested: 6.2.0(postcss@8.5.3)
+ postcss-selector-parser: 6.1.2
+ resolve: 1.22.8
+ sucrase: 3.35.0
+ transitivePeerDependencies:
+ - ts-node
+
+ tailwindcss@4.1.4: {}
+
+ tapable@2.2.1: {}
+
+ tar@6.2.1:
+ dependencies:
+ chownr: 2.0.0
+ fs-minipass: 2.1.0
+ minipass: 5.0.0
+ minizlib: 2.1.2
+ mkdirp: 1.0.4
+ yallist: 4.0.0
+
+ term-size@2.2.1: {}
+
+ text-table@0.2.0: {}
+
+ thenify-all@1.6.0:
+ dependencies:
+ thenify: 3.3.1
+
+ thenify@3.3.1:
+ dependencies:
+ any-promise: 1.3.0
+
+ thirty-two@1.0.2: {}
+
+ throttleit@1.0.1: {}
+
+ through@2.3.8: {}
+
+ tinybench@2.9.0: {}
+
+ tinycolor2@1.4.2: {}
+
+ tinyexec@0.3.2: {}
+
+ tinyglobby@0.2.13:
+ dependencies:
+ fdir: 6.4.4(picomatch@4.0.2)
+ picomatch: 4.0.2
+
+ tinypool@1.0.2: {}
+
+ tinyrainbow@2.0.0: {}
+
+ tinyspy@3.0.2: {}
+
+ tldts-core@6.1.86: {}
+
+ tldts@6.1.86:
+ dependencies:
+ tldts-core: 6.1.86
+
+ tmp@0.0.33:
+ dependencies:
+ os-tmpdir: 1.0.2
+
+ tmp@0.2.3: {}
+
+ to-regex-range@5.0.1:
+ dependencies:
+ is-number: 7.0.0
+
+ toggle-selection@1.0.6: {}
+
+ touch@3.1.1: {}
+
+ tough-cookie@5.1.2:
+ dependencies:
+ tldts: 6.1.86
+
+ tr46@0.0.3: {}
+
+ tr46@1.0.1:
+ dependencies:
+ punycode: 2.3.1
+
+ tr46@5.1.1:
+ dependencies:
+ punycode: 2.3.1
+
+ tree-kill@1.2.2: {}
+
+ ts-api-utils@1.4.1(typescript@5.8.3):
+ dependencies:
+ typescript: 5.8.3
+
+ ts-error@1.0.6: {}
+
+ ts-interface-checker@0.1.13: {}
+
+ ts-poet@6.11.0:
+ dependencies:
+ dprint-node: 1.0.8
+
+ ts-proto-descriptors@2.0.0:
+ dependencies:
+ '@bufbuild/protobuf': 2.2.5
+
+ ts-proto@2.7.0:
+ dependencies:
+ '@bufbuild/protobuf': 2.2.5
+ case-anything: 2.1.13
+ ts-poet: 6.11.0
+ ts-proto-descriptors: 2.0.0
+
+ tsconfck@3.1.5(typescript@5.8.3):
+ optionalDependencies:
+ typescript: 5.8.3
+
+ tsconfig-paths@3.15.0:
+ dependencies:
+ '@types/json5': 0.0.29
+ json5: 1.0.2
+ minimist: 1.2.8
+ strip-bom: 3.0.0
+
+ tslib@2.8.1: {}
+
+ tsup@8.4.0(jiti@1.21.6)(postcss@8.5.3)(typescript@5.8.3)(yaml@2.7.1):
+ dependencies:
+ bundle-require: 5.1.0(esbuild@0.25.2)
+ cac: 6.7.14
+ chokidar: 4.0.3
+ consola: 3.4.2
+ debug: 4.4.0(supports-color@5.5.0)
+ esbuild: 0.25.2
+ joycon: 3.1.1
+ picocolors: 1.1.1
+ postcss-load-config: 6.0.1(jiti@1.21.6)(postcss@8.5.3)(yaml@2.7.1)
+ resolve-from: 5.0.0
+ rollup: 4.40.0
+ source-map: 0.8.0-beta.0
+ sucrase: 3.35.0
+ tinyexec: 0.3.2
+ tinyglobby: 0.2.13
+ tree-kill: 1.2.2
+ optionalDependencies:
+ postcss: 8.5.3
+ typescript: 5.8.3
+ transitivePeerDependencies:
+ - jiti
+ - supports-color
+ - tsx
+ - yaml
+
+ tunnel-agent@0.6.0:
+ dependencies:
+ safe-buffer: 5.2.1
+
+ turbo-darwin-64@2.5.0:
+ optional: true
+
+ turbo-darwin-arm64@2.5.0:
+ optional: true
+
+ turbo-linux-64@2.5.0:
+ optional: true
+
+ turbo-linux-arm64@2.5.0:
+ optional: true
+
+ turbo-windows-64@2.5.0:
+ optional: true
+
+ turbo-windows-arm64@2.5.0:
+ optional: true
+
+ turbo@2.5.0:
+ optionalDependencies:
+ turbo-darwin-64: 2.5.0
+ turbo-darwin-arm64: 2.5.0
+ turbo-linux-64: 2.5.0
+ turbo-linux-arm64: 2.5.0
+ turbo-windows-64: 2.5.0
+ turbo-windows-arm64: 2.5.0
+
+ tweetnacl@0.14.5: {}
+
+ type-check@0.4.0:
+ dependencies:
+ prelude-ls: 1.2.1
+
+ type-fest@0.20.2: {}
+
+ type-fest@0.21.3: {}
+
+ typed-array-buffer@1.0.2:
+ dependencies:
+ call-bind: 1.0.7
+ es-errors: 1.3.0
+ is-typed-array: 1.1.13
+
+ typed-array-byte-length@1.0.1:
+ dependencies:
+ call-bind: 1.0.7
+ for-each: 0.3.3
+ gopd: 1.0.1
+ has-proto: 1.0.3
+ is-typed-array: 1.1.13
+
+ typed-array-byte-offset@1.0.2:
+ dependencies:
+ available-typed-arrays: 1.0.7
+ call-bind: 1.0.7
+ for-each: 0.3.3
+ gopd: 1.0.1
+ has-proto: 1.0.3
+ is-typed-array: 1.1.13
+
+ typed-array-length@1.0.6:
+ dependencies:
+ call-bind: 1.0.7
+ for-each: 0.3.3
+ gopd: 1.0.1
+ has-proto: 1.0.3
+ is-typed-array: 1.1.13
+ possible-typed-array-names: 1.0.0
+
+ typescript@5.8.3: {}
+
+ unbox-primitive@1.0.2:
+ dependencies:
+ call-bind: 1.0.7
+ has-bigints: 1.0.2
+ has-symbols: 1.0.3
+ which-boxed-primitive: 1.0.2
+
+ undefsafe@2.0.5: {}
+
+ undici-types@6.21.0: {}
+
+ universalify@0.1.2: {}
+
+ universalify@2.0.1: {}
+
+ untildify@4.0.0: {}
+
+ update-browserslist-db@1.1.3(browserslist@4.24.4):
+ dependencies:
+ browserslist: 4.24.4
+ escalade: 3.2.0
+ picocolors: 1.1.1
+
+ uri-js@4.4.1:
+ dependencies:
+ punycode: 2.3.1
+
+ use-intl@3.26.5(react@19.1.0):
+ dependencies:
+ '@formatjs/fast-memoize': 2.2.3
+ intl-messageformat: 10.7.7
+ react: 19.1.0
+
+ util-deprecate@1.0.2: {}
+
+ uuid@11.1.0: {}
+
+ uuid@8.3.2: {}
+
+ verror@1.10.0:
+ dependencies:
+ assert-plus: 1.0.0
+ core-util-is: 1.0.2
+ extsprintf: 1.3.0
+
+ vite-node@3.1.2(@types/node@22.14.1)(jiti@1.21.6)(sass@1.87.0)(yaml@2.7.1):
+ dependencies:
+ cac: 6.7.14
+ debug: 4.4.0(supports-color@5.5.0)
+ es-module-lexer: 1.7.0
+ pathe: 2.0.3
+ vite: 6.3.2(@types/node@22.14.1)(jiti@1.21.6)(sass@1.87.0)(yaml@2.7.1)
+ transitivePeerDependencies:
+ - '@types/node'
+ - jiti
+ - less
+ - lightningcss
+ - sass
+ - sass-embedded
+ - stylus
+ - sugarss
+ - supports-color
+ - terser
+ - tsx
+ - yaml
+
+ vite-tsconfig-paths@5.1.4(typescript@5.8.3)(vite@6.3.2(@types/node@22.14.1)(jiti@1.21.6)(sass@1.87.0)(yaml@2.7.1)):
+ dependencies:
+ debug: 4.4.0(supports-color@5.5.0)
+ globrex: 0.1.2
+ tsconfck: 3.1.5(typescript@5.8.3)
+ optionalDependencies:
+ vite: 6.3.2(@types/node@22.14.1)(jiti@1.21.6)(sass@1.87.0)(yaml@2.7.1)
+ transitivePeerDependencies:
+ - supports-color
+ - typescript
+
+ vite@6.3.2(@types/node@22.14.1)(jiti@1.21.6)(sass@1.87.0)(yaml@2.7.1):
+ dependencies:
+ esbuild: 0.25.2
+ fdir: 6.4.4(picomatch@4.0.2)
+ picomatch: 4.0.2
+ postcss: 8.5.3
+ rollup: 4.40.0
+ tinyglobby: 0.2.13
+ optionalDependencies:
+ '@types/node': 22.14.1
+ fsevents: 2.3.3
+ jiti: 1.21.6
+ sass: 1.87.0
+ yaml: 2.7.1
+
+ vitest@3.1.2(@types/node@22.14.1)(jiti@1.21.6)(jsdom@26.1.0)(sass@1.87.0)(yaml@2.7.1):
+ dependencies:
+ '@vitest/expect': 3.1.2
+ '@vitest/mocker': 3.1.2(vite@6.3.2(@types/node@22.14.1)(jiti@1.21.6)(sass@1.87.0)(yaml@2.7.1))
+ '@vitest/pretty-format': 3.1.2
+ '@vitest/runner': 3.1.2
+ '@vitest/snapshot': 3.1.2
+ '@vitest/spy': 3.1.2
+ '@vitest/utils': 3.1.2
+ chai: 5.2.0
+ debug: 4.4.0(supports-color@5.5.0)
+ expect-type: 1.2.1
+ magic-string: 0.30.17
+ pathe: 2.0.3
+ std-env: 3.9.0
+ tinybench: 2.9.0
+ tinyexec: 0.3.2
+ tinyglobby: 0.2.13
+ tinypool: 1.0.2
+ tinyrainbow: 2.0.0
+ vite: 6.3.2(@types/node@22.14.1)(jiti@1.21.6)(sass@1.87.0)(yaml@2.7.1)
+ vite-node: 3.1.2(@types/node@22.14.1)(jiti@1.21.6)(sass@1.87.0)(yaml@2.7.1)
+ why-is-node-running: 2.3.0
+ optionalDependencies:
+ '@types/node': 22.14.1
+ jsdom: 26.1.0
+ transitivePeerDependencies:
+ - jiti
+ - less
+ - lightningcss
+ - msw
+ - sass
+ - sass-embedded
+ - stylus
+ - sugarss
+ - supports-color
+ - terser
+ - tsx
+ - yaml
+
+ w3c-xmlserializer@5.0.0:
+ dependencies:
+ xml-name-validator: 5.0.0
+
+ wait-on@8.0.3(debug@4.4.0):
+ dependencies:
+ axios: 1.8.4(debug@4.4.0)
+ joi: 17.13.3
+ lodash: 4.17.21
+ minimist: 1.2.8
+ rxjs: 7.8.2
+ transitivePeerDependencies:
+ - debug
+
+ web-streams-polyfill@3.3.3: {}
+
+ webidl-conversions@3.0.1: {}
+
+ webidl-conversions@4.0.2: {}
+
+ webidl-conversions@7.0.0: {}
+
+ whatwg-encoding@3.1.1:
+ dependencies:
+ iconv-lite: 0.6.3
+
+ whatwg-mimetype@4.0.0: {}
+
+ whatwg-url@14.2.0:
+ dependencies:
+ tr46: 5.1.1
+ webidl-conversions: 7.0.0
+
+ whatwg-url@5.0.0:
+ dependencies:
+ tr46: 0.0.3
+ webidl-conversions: 3.0.1
+
+ whatwg-url@7.1.0:
+ dependencies:
+ lodash.sortby: 4.7.0
+ tr46: 1.0.1
+ webidl-conversions: 4.0.2
+
+ which-boxed-primitive@1.0.2:
+ dependencies:
+ is-bigint: 1.0.4
+ is-boolean-object: 1.1.2
+ is-number-object: 1.0.7
+ is-string: 1.0.7
+ is-symbol: 1.0.4
+
+ which-builtin-type@1.1.4:
+ dependencies:
+ function.prototype.name: 1.1.6
+ has-tostringtag: 1.0.2
+ is-async-function: 2.0.0
+ is-date-object: 1.0.5
+ is-finalizationregistry: 1.0.2
+ is-generator-function: 1.0.10
+ is-regex: 1.1.4
+ is-weakref: 1.0.2
+ isarray: 2.0.5
+ which-boxed-primitive: 1.0.2
+ which-collection: 1.0.2
+ which-typed-array: 1.1.15
+
+ which-collection@1.0.2:
+ dependencies:
+ is-map: 2.0.3
+ is-set: 2.0.3
+ is-weakmap: 2.0.2
+ is-weakset: 2.0.3
+
+ which-typed-array@1.1.15:
+ dependencies:
+ available-typed-arrays: 1.0.7
+ call-bind: 1.0.7
+ for-each: 0.3.3
+ gopd: 1.0.1
+ has-tostringtag: 1.0.2
+
+ which@2.0.2:
+ dependencies:
+ isexe: 2.0.0
+
+ why-is-node-running@2.3.0:
+ dependencies:
+ siginfo: 2.0.0
+ stackback: 0.0.2
+
+ wide-align@1.1.5:
+ dependencies:
+ string-width: 4.2.3
+
+ word-wrap@1.2.5: {}
+
+ wrap-ansi@6.2.0:
+ dependencies:
+ ansi-styles: 4.3.0
+ string-width: 4.2.3
+ strip-ansi: 6.0.1
+
+ wrap-ansi@7.0.0:
+ dependencies:
+ ansi-styles: 4.3.0
+ string-width: 4.2.3
+ strip-ansi: 6.0.1
+
+ wrap-ansi@8.1.0:
+ dependencies:
+ ansi-styles: 6.2.1
+ string-width: 5.1.2
+ strip-ansi: 7.1.0
+
+ wrap-ansi@9.0.0:
+ dependencies:
+ ansi-styles: 6.2.1
+ string-width: 7.2.0
+ strip-ansi: 7.1.0
+
+ wrappy@1.0.2: {}
+
+ ws@8.18.1: {}
+
+ xml-name-validator@5.0.0: {}
+
+ xmlchars@2.2.0: {}
+
+ y18n@5.0.8: {}
+
+ yallist@3.1.1: {}
+
+ yallist@4.0.0: {}
+
+ yaml@2.7.1: {}
+
+ yargs-parser@21.1.1: {}
+
+ yargs@17.7.2:
+ dependencies:
+ cliui: 8.0.1
+ escalade: 3.2.0
+ get-caller-file: 2.0.5
+ require-directory: 2.1.1
+ string-width: 4.2.3
+ y18n: 5.0.8
+ yargs-parser: 21.1.1
+
+ yauzl@2.10.0:
+ dependencies:
+ buffer-crc32: 0.2.13
+ fd-slicer: 1.1.0
+
+ yocto-queue@0.1.0: {}
diff --git a/login/pnpm-workspace.yaml b/login/pnpm-workspace.yaml
new file mode 100644
index 0000000000..3ff5faaaf5
--- /dev/null
+++ b/login/pnpm-workspace.yaml
@@ -0,0 +1,3 @@
+packages:
+ - "apps/*"
+ - "packages/*"
diff --git a/login/scripts/entrypoint.sh b/login/scripts/entrypoint.sh
new file mode 100755
index 0000000000..c537e8b8fb
--- /dev/null
+++ b/login/scripts/entrypoint.sh
@@ -0,0 +1,11 @@
+#!/bin/sh
+set -o allexport
+. /.env-file/.env
+set +o allexport
+
+if [ -n "${ZITADEL_SERVICE_USER_TOKEN_FILE}" ] && [ -f "${ZITADEL_SERVICE_USER_TOKEN_FILE}" ]; then
+ echo "ZITADEL_SERVICE_USER_TOKEN_FILE=${ZITADEL_SERVICE_USER_TOKEN_FILE} is set and file exists, setting ZITADEL_SERVICE_USER_TOKEN to the files content"
+ export ZITADEL_SERVICE_USER_TOKEN=$(cat "${ZITADEL_SERVICE_USER_TOKEN_FILE}")
+fi
+
+exec node apps/login/server.js
diff --git a/login/scripts/healthcheck.js b/login/scripts/healthcheck.js
new file mode 100644
index 0000000000..c1a64c6e75
--- /dev/null
+++ b/login/scripts/healthcheck.js
@@ -0,0 +1,14 @@
+const url = process.argv[2];
+
+if (!url) {
+ console.error("❌ No URL provided as command line argument.");
+ process.exit(1);
+}
+
+try {
+ const res = await fetch(url);
+ if (!res.ok) process.exit(1);
+ process.exit(0);
+} catch (e) {
+ process.exit(1);
+}
diff --git a/login/scripts/run_or_skip.sh b/login/scripts/run_or_skip.sh
new file mode 100755
index 0000000000..4516eb01b1
--- /dev/null
+++ b/login/scripts/run_or_skip.sh
@@ -0,0 +1,67 @@
+#!/usr/bin/env bash
+
+# Usage: ./run_or_skip.sh
+# Example: ./run_or_skip.sh lint-force "img1;img2"
+
+set -euo pipefail
+
+if [ -z "$CACHE_DIR" ]; then
+ echo "CACHE_DIR is not set. Please set it to a valid directory."
+ exit 1
+fi
+
+MAKE_TARGET=$1
+IMAGES=$2
+IGNORE_RUN_CACHE=${IGNORE_RUN_CACHE:-false}
+
+CACHE_FILE="$CACHE_DIR/$MAKE_TARGET.digests"
+mkdir -p "$CACHE_DIR"
+
+get_image_creation_dates() {
+ local values=""
+ for img in $(echo "$IMAGES"); do
+ local value=$(docker image inspect "$img" --format='{{.Created}}' 2>/dev/null || true)
+ if [[ -z $value ]]; then
+ docker pull "$img" >/dev/null 2>&1 || true
+ value=$(docker image inspect "$img" --format='{{.Created}}' 2>/dev/null || true)
+ fi
+ if [[ -z $value ]]; then
+ value=$(docker image inspect "$img" --format='{{.Created}}' 2>/dev/null || true)
+ fi
+ value=${value:-new-and-not-pullable-or-failed-to-build}
+ value="${img}@${value}"
+ values="${values}${value};"
+ done
+ values=${values%;} # Remove trailing semicolon
+ echo "$values"
+}
+
+CACHE_FILE_CONTENT=$(cat "$CACHE_FILE" 2>/dev/null || echo "")
+CACHED_STATUS=$(echo "$CACHE_FILE_CONTENT" | cut -d ';' -f1)
+CACHED_IMAGE_CREATED_VALUES=$(echo "$CACHE_FILE_CONTENT" | cut -d ';' -f2-99)
+CURRENT_IMAGE_CREATED_VALUES="$(get_image_creation_dates)"
+ if [[ "$CACHED_IMAGE_CREATED_VALUES" == "$CURRENT_IMAGE_CREATED_VALUES" ]]; then
+ if [[ "$IGNORE_RUN_CACHE" == "true" ]]; then
+ echo "\$IGNORE_RUN_CACHE=$IGNORE_RUN_CACHE - Running $MAKE_TARGET despite unchanged images."
+ else
+ echo "Skipping $MAKE_TARGET – all images unchanged, returning cached status $CACHED_STATUS"
+ exit $CACHED_STATUS
+ fi
+fi
+echo "Images have changed"
+echo
+echo "CACHED_IMAGE_CREATED_VALUES does not match CURRENT_IMAGE_CREATED_VALUES"
+echo
+echo "$CACHED_IMAGE_CREATED_VALUES"
+echo
+echo "$CURRENT_IMAGE_CREATED_VALUES"
+echo
+docker images
+echo
+echo "Running $MAKE_TARGET..."
+set +e
+make -j $MAKE_TARGET
+STATUS=$?
+set -e
+echo "${STATUS};$(get_image_creation_dates)" > $CACHE_FILE
+exit $STATUS
diff --git a/login/turbo.json b/login/turbo.json
new file mode 100644
index 0000000000..dabae8fa97
--- /dev/null
+++ b/login/turbo.json
@@ -0,0 +1,51 @@
+{
+ "$schema": "https://turbo.build/schema.json",
+ "ui": "tui",
+ "globalDependencies": ["**/.env.*local"],
+ "globalEnv": [
+ "DEBUG",
+ "VERCEL_URL",
+ "EMAIL_VERIFICATION",
+ "AUDIENCE",
+ "SYSTEM_USER_ID",
+ "SYSTEM_USER_PRIVATE_KEY",
+ "ZITADEL_API_URL",
+ "ZITADEL_SERVICE_USER_TOKEN",
+ "NEXT_PUBLIC_BASE_PATH",
+ "CUSTOM_REQUEST_HEADERS",
+ "NODE_ENV"
+ ],
+ "tasks": {
+ "generate": {
+ "cache": true
+ },
+ "build": {},
+ "build:login:standalone": {},
+ "build:client:standalone": {},
+ "test": {},
+ "start": {},
+ "start:built": {},
+ "test:unit": {},
+ "test:unit:standalone": {},
+ "test:integration": {},
+ "test:integration:setup": {
+ "with": ["dev"]
+ },
+ "test:acceptance:setup": {},
+ "test:acceptance:setup:dev": {
+ "with": ["dev"]
+ },
+ "test:watch": {
+ "persistent": true
+ },
+ "lint": {},
+ "lint:fix": {},
+ "dev": {
+ "cache": false,
+ "persistent": true
+ },
+ "clean": {
+ "cache": false
+ }
+ }
+}