From 9f7417d7b4400f80a546cd6f5437100915c06f1f Mon Sep 17 00:00:00 2001 From: Boaz Poolman Date: Mon, 10 Aug 2026 10:27:22 +0200 Subject: [PATCH 01/62] fix: send email verification on existing user sign up --- apps/cms/src/lib/auth.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/apps/cms/src/lib/auth.ts b/apps/cms/src/lib/auth.ts index 90a515ab..3d9082c5 100644 --- a/apps/cms/src/lib/auth.ts +++ b/apps/cms/src/lib/auth.ts @@ -33,6 +33,7 @@ export const auth = betterAuth({ ], emailVerification: { sendOnSignIn: true, + autoSignInAfterVerification: true, }, emailAndPassword: { enabled: true, @@ -40,6 +41,11 @@ export const auth = betterAuth({ sendResetPassword: async ({ user, url }) => { await sendResetPasswordEmail(user.email, url); }, + onExistingUserSignUp: async ({ user }) => { + await auth.api.sendVerificationOTP({ + body: { email: user.email, type: "email-verification" }, + }); + }, }, database: strapiAdapter({ debugLogs: From 54406a699463bb2cafa10a54488764d9d1012c0c Mon Sep 17 00:00:00 2001 From: Boaz Poolman Date: Mon, 10 Aug 2026 13:03:34 +0200 Subject: [PATCH 02/62] fix: avatar uploading --- .../better-auth/controllers/user.ts | 48 ++++++++++++++++ .../better-auth/policies/is-authenticated.ts | 28 ++++++++++ .../src/extensions/better-auth/routes/user.ts | 26 +++++++++ .../extensions/better-auth/services/user.ts | 42 ++++++++++++++ .../extensions/better-auth/strapi-server.ts | 4 ++ .../features/auth/components/providers.tsx | 27 ++++++--- apps/web/src/features/auth/lib/avatar.ts | 55 +++++++++++++++++++ 7 files changed, 221 insertions(+), 9 deletions(-) create mode 100644 apps/cms/src/extensions/better-auth/policies/is-authenticated.ts create mode 100644 apps/web/src/features/auth/lib/avatar.ts diff --git a/apps/cms/src/extensions/better-auth/controllers/user.ts b/apps/cms/src/extensions/better-auth/controllers/user.ts index b33da9ae..4bdc6b53 100644 --- a/apps/cms/src/extensions/better-auth/controllers/user.ts +++ b/apps/cms/src/extensions/better-auth/controllers/user.ts @@ -5,6 +5,8 @@ import { factories } from "@strapi/strapi"; import { getPluginService } from "../utils"; +const MAX_AVATAR_SIZE = 2 * 1024 * 1024; + export default factories.createCoreController( "plugin::better-auth.user", () => ({ @@ -28,5 +30,51 @@ export default factories.createCoreController( ctx.body = { packages, templates }; }, + + /** + * POST /users/me/avatar — used by better-auth-ui's `avatar.upload` + * config. Gated by the `is-authenticated` policy, which populates + * `ctx.state.betterAuthSession`. + */ + async uploadAvatar(ctx) { + const session = ctx.state.betterAuthSession; + + const uploaded = ctx.request.files?.file; + const file = Array.isArray(uploaded) ? uploaded[0] : uploaded; + if (!file) return ctx.badRequest("No file provided."); + if (!file.mimetype?.startsWith("image/")) { + return ctx.badRequest("Avatar must be an image."); + } + if (file.size > MAX_AVATAR_SIZE) { + return ctx.badRequest("Avatar must be smaller than 2 MB."); + } + + const uploadedFile = await getPluginService("user").uploadAvatar({ + userId: session.user.id, + file, + }); + + ctx.body = { url: uploadedFile.url }; + }, + + /** + * DELETE /users/me/avatar — used by better-auth-ui's `avatar.delete` + * config. Gated by the `is-authenticated` policy, which populates + * `ctx.state.betterAuthSession`. + */ + async deleteAvatar(ctx) { + const session = ctx.state.betterAuthSession; + + const { url } = ctx.request.body ?? {}; + if (!url) return ctx.badRequest("Missing url."); + + const deleted = await getPluginService("user").deleteAvatar({ + userId: session.user.id, + url, + }); + if (!deleted) return ctx.notFound(); + + ctx.body = { success: true }; + }, }), ); diff --git a/apps/cms/src/extensions/better-auth/policies/is-authenticated.ts b/apps/cms/src/extensions/better-auth/policies/is-authenticated.ts new file mode 100644 index 00000000..a20355dd --- /dev/null +++ b/apps/cms/src/extensions/better-auth/policies/is-authenticated.ts @@ -0,0 +1,28 @@ +/** + * is-authenticated policy + * + * Requires a valid better-auth session (via cookie) for the current + * request. This plugin's content-api routes carry no Strapi API token, so + * routes relying on this policy must also set `config.auth = false` — + * otherwise Strapi's own content-api-token check rejects the request + * before this policy ever runs. + * + * On success, stashes the resolved session on `ctx.state` so downstream + * policies (e.g. is-owner) and the controller action don't need to fetch + * it again. + */ + +import { fromNodeHeaders } from "better-auth/node"; +import { auth } from "../../../lib/auth"; + +export default async function isAuthenticated(ctx) { + const session = await auth.api.getSession({ + headers: fromNodeHeaders(ctx.request.headers), + }); + + if (!session?.user) return ctx.unauthorized(); + + ctx.state.betterAuthSession = session; + + return true; +} diff --git a/apps/cms/src/extensions/better-auth/routes/user.ts b/apps/cms/src/extensions/better-auth/routes/user.ts index 6c1b567a..cf72c3b9 100644 --- a/apps/cms/src/extensions/better-auth/routes/user.ts +++ b/apps/cms/src/extensions/better-auth/routes/user.ts @@ -26,6 +26,32 @@ export default { handler: "user.relatedContent", config: { policies: [], middlewares: [], prefix: "" }, }, + { + method: "POST", + path: "/users/me/avatar", + handler: "user.uploadAvatar", + config: { + // These are called directly from the browser with a better-auth + // session cookie, not a Strapi API token, so the default + // content-api-token check must be skipped in favor of our own + // session-based policy. + auth: false, + policies: ["plugin::better-auth.is-authenticated"], + middlewares: [], + prefix: "", + }, + }, + { + method: "DELETE", + path: "/users/me/avatar", + handler: "user.deleteAvatar", + config: { + auth: false, + policies: ["plugin::better-auth.is-authenticated"], + middlewares: [], + prefix: "", + }, + }, ], }, }; diff --git a/apps/cms/src/extensions/better-auth/services/user.ts b/apps/cms/src/extensions/better-auth/services/user.ts index 6db84168..540d36e0 100644 --- a/apps/cms/src/extensions/better-auth/services/user.ts +++ b/apps/cms/src/extensions/better-auth/services/user.ts @@ -5,7 +5,49 @@ import { factories, type Modules, type UID } from "@strapi/strapi"; import { extractContentTypeName } from "../utils"; +const avatarFileName = (userId: string | number) => `avatar-${userId}`; + export default factories.createCoreService("plugin::better-auth.user", () => ({ + /** Uploads an avatar image to the media library, scoped to a given user. */ + async uploadAvatar({ + userId, + file, + }: { + userId: string | number; + file: unknown; + }) { + const [uploaded] = await strapi + .plugin("upload") + .service("upload") + .upload({ + data: { fileInfo: { name: avatarFileName(userId) } }, + files: file, + }); + + return uploaded as { url: string }; + }, + + /** + * Removes a previously uploaded avatar from the media library, but only + * if it was tagged as this user's own avatar upload. + */ + async deleteAvatar({ + userId, + url, + }: { + userId: string | number; + url: string; + }) { + const existing = await strapi.db + .query("plugin::upload.file") + .findOne({ where: { url, name: avatarFileName(userId) } }); + + if (!existing) return false; + + await strapi.plugin("upload").service("upload").remove(existing); + return true; + }, + async getRelatedContent({ organizationId, uid, diff --git a/apps/cms/src/extensions/better-auth/strapi-server.ts b/apps/cms/src/extensions/better-auth/strapi-server.ts index 8731e90e..57ff3d21 100644 --- a/apps/cms/src/extensions/better-auth/strapi-server.ts +++ b/apps/cms/src/extensions/better-auth/strapi-server.ts @@ -1,5 +1,6 @@ import organizationController from "./controllers/organization"; import userController from "./controllers/user"; +import isAuthenticated from "./policies/is-authenticated"; import organizationRoutes from "./routes/organization"; import userRoutes from "./routes/user"; import organizationService from "./services/organization"; @@ -13,6 +14,9 @@ export default (plugin) => { plugin.services.organization = organizationService; plugin.services.user = userService; + if (!plugin.policies) plugin.policies = {}; + plugin.policies["is-authenticated"] = isAuthenticated; + plugin.routes["content-api"] = { type: "content-api", routes: [ diff --git a/apps/web/src/features/auth/components/providers.tsx b/apps/web/src/features/auth/components/providers.tsx index 24dc0f10..6ff094d1 100644 --- a/apps/web/src/features/auth/components/providers.tsx +++ b/apps/web/src/features/auth/components/providers.tsx @@ -5,6 +5,8 @@ import Image from "next/image"; import Link from "next/link"; import { useRouter } from "next/navigation"; import { cmsImageUrl } from "@/features/cms/lib/image-url"; +import { cn } from "@/lib/utils"; +import { deleteAvatar, uploadAvatar } from "../lib/avatar"; import { authClient } from "../lib/client"; export function AuthProviders({ children }: { children: React.ReactNode }) { @@ -20,15 +22,22 @@ export function AuthProviders({ children }: { children: React.ReactNode }) { baseURL={process.env.NEXT_PUBLIC_WEB_URL} account avatar={{ - Image: ({ src, alt, ...props }) => ( - {alt} - ), + upload: uploadAvatar, + delete: deleteAvatar, + Image: ({ src, alt, className, ...props }) => { + if (!src) return null; + + return ( + {alt} + ); + }, }} emailOTP passkey diff --git a/apps/web/src/features/auth/lib/avatar.ts b/apps/web/src/features/auth/lib/avatar.ts new file mode 100644 index 00000000..89dfcab3 --- /dev/null +++ b/apps/web/src/features/auth/lib/avatar.ts @@ -0,0 +1,55 @@ +import { toast } from "sonner"; + +const CMS_URL = process.env.NEXT_PUBLIC_CMS_URL; + +async function extractErrorMessage(res: Response, fallback: string) { + const body = await res.json().catch(() => null); + return (body?.error?.message as string | undefined) || fallback; +} + +export async function uploadAvatar(file: File): Promise { + try { + const form = new FormData(); + form.append("file", file, file.name); + + const res = await fetch(`${CMS_URL}/api/users/me/avatar`, { + method: "POST", + credentials: "include", + body: form, + }); + + if (!res.ok) { + throw new Error( + await extractErrorMessage(res, "Failed to upload avatar."), + ); + } + + const data = (await res.json()) as { url: string }; + return data.url; + } catch (error) { + console.error("Avatar upload failed:", error); + toast.error( + error instanceof Error ? error.message : "Failed to upload avatar.", + ); + return undefined; + } +} + +export async function deleteAvatar(url: string): Promise { + try { + const res = await fetch(`${CMS_URL}/api/users/me/avatar`, { + method: "DELETE", + credentials: "include", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ url }), + }); + + if (!res.ok) { + throw new Error( + await extractErrorMessage(res, "Failed to delete old avatar."), + ); + } + } catch (error) { + console.error("Avatar cleanup failed:", error); + } +} From c500d9bdda26b5663c2e197bbd6d73566e72f56b Mon Sep 17 00:00:00 2001 From: Boaz Poolman Date: Mon, 10 Aug 2026 15:07:28 +0200 Subject: [PATCH 03/62] feat: replace email-otp plugin with magic-link, send additional emails for several auth flows --- apps/cms/package.json | 3 +- apps/cms/src/lib/auth.ts | 103 +- apps/cms/src/lib/email.ts | 73 +- apps/web/src/app/auth/[...path]/page.tsx | 11 +- .../features/auth/components/providers.tsx | 56 +- .../auth/components/sign-up-auth-view.tsx | 23 + .../auth/components/two-factor-auth-view.tsx | 23 + apps/web/src/features/auth/lib/client.ts | 4 +- .../features/auth/lib/use-auth-ui-props.tsx | 71 ++ pnpm-lock.yaml | 902 ++++++++++++++++-- 10 files changed, 1123 insertions(+), 146 deletions(-) create mode 100644 apps/web/src/features/auth/components/sign-up-auth-view.tsx create mode 100644 apps/web/src/features/auth/components/two-factor-auth-view.tsx create mode 100644 apps/web/src/features/auth/lib/use-auth-ui-props.tsx diff --git a/apps/cms/package.json b/apps/cms/package.json index f1fafe36..e4c1bd03 100644 --- a/apps/cms/package.json +++ b/apps/cms/package.json @@ -17,10 +17,11 @@ "upgrade:dry": "npx @strapi/upgrade latest --dry" }, "dependencies": { - "@better-auth-ui/react": "^1.6.3", + "@better-auth-ui/react": "^1.6.44", "@better-auth/infra": "^0.2.5", "@piksail/strapi-provider-email-mailpit": "^1.0.0", "@react-email/render": "^1.4.0", + "react-email": "^6.6.8", "@strapi-community/plugin-better-auth": "^1.0.0-beta.6", "@strapi-community/plugin-better-auth-dashboard": "1.0.0-alpha.10", "@strapi/admin": "catalog:strapi", diff --git a/apps/cms/src/lib/auth.ts b/apps/cms/src/lib/auth.ts index 3d9082c5..a5e90cf1 100644 --- a/apps/cms/src/lib/auth.ts +++ b/apps/cms/src/lib/auth.ts @@ -1,15 +1,62 @@ import { dash } from "@better-auth/infra"; import { strapiAdapter } from "@strapi-community/plugin-better-auth"; import { betterAuth } from "better-auth"; -import { emailOTP, jwt, organization, twoFactor } from "better-auth/plugins"; -import { sendOtpEmail, sendResetPasswordEmail } from "./email"; +import { jwt, magicLink, organization, twoFactor } from "better-auth/plugins"; +import { + sendChangeEmailConfirmationEmail, + sendEmailChangedEmail, + sendMagicLinkEmail, + sendOrganizationInvitationEmail, + sendOtpEmail, + sendResetPasswordEmail, + sendVerificationEmail, +} from "./email"; + +const pendingEmailChanges = new Map(); + +function absolutizeCallbackURL(url: string): string { + const parsed = new URL(url); + const callbackURL = parsed.searchParams.get("callbackURL"); + if (callbackURL && !/^https?:\/\//i.test(callbackURL)) { + parsed.searchParams.set( + "callbackURL", + new URL(callbackURL, process.env.WEBSITE_URL).toString(), + ); + } + return parsed.toString(); +} + +const STRAPI_URL = process.env.STRAPI_URL || "http://localhost:1337"; + +function absolutizeMediaUrl(url: string): string { + return /^https?:\/\//i.test(url) ? url : `${STRAPI_URL}${url}`; +} export const auth = betterAuth({ trustedOrigins: [process.env.WEBSITE_URL], secret: process.env.BETTER_AUTH_SECRET, appName: process.env.SITE_NAME ?? "Strapi Community", plugins: [ - organization(), + organization({ + sendInvitationEmail: async (data) => { + const url = `${process.env.WEBSITE_URL}/auth/accept-invitation?invitationId=${data.id}`; + const expirationHours = Math.round( + (new Date(data.invitation.expiresAt).getTime() - Date.now()) / + (60 * 60 * 1000), + ); + + await sendOrganizationInvitationEmail(data.email, url, { + inviterName: data.inviter.user.name, + inviterEmail: data.inviter.user.email, + organizationName: data.organization.name, + organizationLogoURL: data.organization.logo + ? absolutizeMediaUrl(data.organization.logo) + : undefined, + role: data.role, + expirationHours, + }); + }, + }), twoFactor({ otpOptions: { sendOTP: async ({ user, otp }) => { @@ -18,35 +65,67 @@ export const auth = betterAuth({ }, }), dash({ - apiUrl: process.env.STRAPI_URL || "http://localhost:1337", + apiUrl: STRAPI_URL, apiKey: process.env.BETTER_AUTH_DASHBOARD_SECRET || "strapi-internal-dashboard-key", }), jwt(), - emailOTP({ - overrideDefaultEmailVerification: true, - sendVerificationOTP: async ({ email, otp }) => { - await sendOtpEmail(email, otp); + magicLink({ + sendMagicLink: async ({ email, url }) => { + await sendMagicLinkEmail(email, absolutizeCallbackURL(url)); }, }), ], emailVerification: { sendOnSignIn: true, autoSignInAfterVerification: true, + sendVerificationEmail: async ({ user, url }) => { + await sendVerificationEmail(user.email, absolutizeCallbackURL(url)); + }, }, emailAndPassword: { enabled: true, requireEmailVerification: true, sendResetPassword: async ({ user, url }) => { - await sendResetPasswordEmail(user.email, url); + await sendResetPasswordEmail(user.email, absolutizeCallbackURL(url)); }, - onExistingUserSignUp: async ({ user }) => { - await auth.api.sendVerificationOTP({ - body: { email: user.email, type: "email-verification" }, + onExistingUserSignUp: async ({ user }, request) => { + await auth.api.signInMagicLink({ + body: { email: user.email }, + headers: request?.headers, }); }, }, + user: { + changeEmail: { + enabled: true, + sendChangeEmailConfirmation: async ({ user, newEmail, url }) => { + pendingEmailChanges.set(newEmail, user.email); + await sendChangeEmailConfirmationEmail( + user.email, + user.email, + newEmail, + absolutizeCallbackURL(url), + ); + }, + }, + }, + databaseHooks: { + user: { + update: { + after: async (updated) => { + const oldEmail = pendingEmailChanges.get(updated.email); + if (!oldEmail || !updated.emailVerified) return; + pendingEmailChanges.delete(updated.email); + await Promise.all([ + sendEmailChangedEmail(oldEmail, oldEmail, updated.email), + sendEmailChangedEmail(updated.email, oldEmail, updated.email), + ]); + }, + }, + }, + }, database: strapiAdapter({ debugLogs: process.env.BETTER_AUTH_DEBUG === "true" && diff --git a/apps/cms/src/lib/email.ts b/apps/cms/src/lib/email.ts index 641003d9..c9f9a9d3 100644 --- a/apps/cms/src/lib/email.ts +++ b/apps/cms/src/lib/email.ts @@ -1,13 +1,15 @@ import type { + ChangeEmailConfirmationEmailProps, DeviceInfo, EmailChangedEmailProps, EmailVerificationEmailProps, MagicLinkEmailProps, NewDeviceEmailProps, + OrganizationInvitationEmailProps, OtpEmailProps, PasswordChangedEmailProps, ResetPasswordEmailProps, -} from "@better-auth-ui/react"; +} from "@better-auth-ui/react/dist/email"; import { render, toPlainText } from "@react-email/render"; import React from "react"; @@ -19,17 +21,26 @@ type EmailComponents = { ResetPasswordEmail: (props: ResetPasswordEmailProps) => React.ReactElement; MagicLinkEmail: (props: MagicLinkEmailProps) => React.ReactElement; EmailChangedEmail: (props: EmailChangedEmailProps) => React.ReactElement; + ChangeEmailConfirmationEmail: ( + props: ChangeEmailConfirmationEmailProps, + ) => React.ReactElement; NewDeviceEmail: (props: NewDeviceEmailProps) => React.ReactElement; PasswordChangedEmail: ( props: PasswordChangedEmailProps, ) => React.ReactElement; + OrganizationInvitationEmail: ( + props: OrganizationInvitationEmailProps, + ) => React.ReactElement; }; // Native dynamic import avoids TypeScript compiling this to require(), -// which fails for ESM-only packages like @better-auth-ui/react. +// which fails for ESM-only packages like @better-auth-ui/react. The email +// templates live under the package's dedicated `/email` subpath (as of +// 1.6.35) rather than its main entry, which now only exports client-side +// React hooks that don't belong in a server-only bundle. const importEmailComponents = () => new Function( - "return import('@better-auth-ui/react')", + "return import('@better-auth-ui/react/email')", )() as Promise; const APP_NAME = process.env.SITE_NAME ?? "Strapi Community"; @@ -91,12 +102,16 @@ export async function sendResetPasswordEmail( export async function sendOtpEmail( to: string, otp: string, - expirationMinutes = 3, + options?: { subject?: string; expirationMinutes?: number }, ): Promise { const { OtpEmail } = await importEmailComponents(); + const { + subject = "Your two-factor authentication code", + expirationMinutes = 3, + } = options ?? {}; await sendEmail( to, - "Your two-factor authentication code", + subject, React.createElement(OtpEmail, { verificationCode: otp, email: to, @@ -124,6 +139,26 @@ export async function sendMagicLinkEmail( ); } +export async function sendChangeEmailConfirmationEmail( + to: string, + currentEmail: string, + newEmail: string, + url: string, +): Promise { + const { ChangeEmailConfirmationEmail } = await importEmailComponents(); + await sendEmail( + to, + "Confirm your email address change", + React.createElement(ChangeEmailConfirmationEmail, { + url, + currentEmail, + newEmail, + appName: APP_NAME, + logoURL: LOGO_URL, + }), + ); +} + export async function sendEmailChangedEmail( to: string, oldEmail: string, @@ -161,6 +196,34 @@ export async function sendNewDeviceEmail( ); } +export async function sendOrganizationInvitationEmail( + to: string, + url: string, + options: { + inviterName?: string; + inviterEmail?: string; + organizationName?: string; + organizationLogoURL?: string; + role?: string; + expirationHours?: number; + }, +): Promise { + const { OrganizationInvitationEmail } = await importEmailComponents(); + await sendEmail( + to, + options.organizationName + ? `You've been invited to join ${options.organizationName}` + : "You've been invited to join an organization", + React.createElement(OrganizationInvitationEmail, { + url, + email: to, + appName: APP_NAME, + logoURL: LOGO_URL, + ...options, + }), + ); +} + export async function sendPasswordChangedEmail( to: string, timestamp: string, diff --git a/apps/web/src/app/auth/[...path]/page.tsx b/apps/web/src/app/auth/[...path]/page.tsx index f59ce15e..d447b9f3 100644 --- a/apps/web/src/app/auth/[...path]/page.tsx +++ b/apps/web/src/app/auth/[...path]/page.tsx @@ -1,6 +1,8 @@ import { notFound } from "next/navigation"; import { Navigation } from "@/components/layout/navigation"; import { AuthView } from "@/features/auth/components/auth-view"; +import { SignUpAuthView } from "@/features/auth/components/sign-up-auth-view"; +import { TwoFactorAuthView } from "@/features/auth/components/two-factor-auth-view"; import { isAuthEnabled } from "@/features/auth/lib/is-enabled"; type Props = { @@ -12,12 +14,19 @@ export default async function AuthPage({ params }: Props) { const { path } = await params; const pathname = `/auth/${path.join("/")}`; + const view = path.at(-1); return ( <>
- + {view === "sign-up" ? ( + + ) : view === "two-factor" ? ( + + ) : ( + + )}
); diff --git a/apps/web/src/features/auth/components/providers.tsx b/apps/web/src/features/auth/components/providers.tsx index 6ff094d1..4527044b 100644 --- a/apps/web/src/features/auth/components/providers.tsx +++ b/apps/web/src/features/auth/components/providers.tsx @@ -1,60 +1,10 @@ "use client"; import { AuthUIProvider } from "@daveyplate/better-auth-ui"; -import Image from "next/image"; -import Link from "next/link"; -import { useRouter } from "next/navigation"; -import { cmsImageUrl } from "@/features/cms/lib/image-url"; -import { cn } from "@/lib/utils"; -import { deleteAvatar, uploadAvatar } from "../lib/avatar"; -import { authClient } from "../lib/client"; +import { useAuthUIProviderProps } from "../lib/use-auth-ui-props"; export function AuthProviders({ children }: { children: React.ReactNode }) { - const router = useRouter(); + const authUIProviderProps = useAuthUIProviderProps(); - return ( - router.push(href)} - replace={(href) => router.replace(href)} - Link={({ href, ...props }) => } - basePath="/auth" - baseURL={process.env.NEXT_PUBLIC_WEB_URL} - account - avatar={{ - upload: uploadAvatar, - delete: deleteAvatar, - Image: ({ src, alt, className, ...props }) => { - if (!src) return null; - - return ( - {alt} - ); - }, - }} - emailOTP - passkey - social={{ - providers: ["google", "github"], - }} - emailVerification={{ - otp: true, - }} - twoFactor={["totp"]} - organization={{ - basePath: "/org", - pathMode: "slug", - personalPath: "/account", - }} - > - {children} - - ); + return {children}; } diff --git a/apps/web/src/features/auth/components/sign-up-auth-view.tsx b/apps/web/src/features/auth/components/sign-up-auth-view.tsx new file mode 100644 index 00000000..e88ace09 --- /dev/null +++ b/apps/web/src/features/auth/components/sign-up-auth-view.tsx @@ -0,0 +1,23 @@ +"use client"; + +import { AuthUIProvider, AuthView } from "@daveyplate/better-auth-ui"; +import { useAuthUIProviderProps } from "../lib/use-auth-ui-props"; + +/** + * AuthUIProvider's `magicLink` (like `credentials`/`emailOTP`/etc.) is a + * plain boolean with no per-view granularity — AuthView reads it straight + * from context, so there's no prop on AuthView itself to hide the magic + * link button on just the sign-up page. Nesting a second provider that + * overrides only `magicLink` for this one route shadows the context for + * this subtree without duplicating (and risking drift in) the rest of the + * shared config. + */ +export function SignUpAuthView({ pathname }: { pathname: string }) { + const authUIProviderProps = useAuthUIProviderProps(); + + return ( + + + + ); +} diff --git a/apps/web/src/features/auth/components/two-factor-auth-view.tsx b/apps/web/src/features/auth/components/two-factor-auth-view.tsx new file mode 100644 index 00000000..cc422ca2 --- /dev/null +++ b/apps/web/src/features/auth/components/two-factor-auth-view.tsx @@ -0,0 +1,23 @@ +"use client"; + +import { AuthUIProvider, AuthView } from "@daveyplate/better-auth-ui"; +import { useAuthUIProviderProps } from "../lib/use-auth-ui-props"; + +/** + * AuthView's social-buttons section only excludes the RESET_PASSWORD and + * EMAIL_VERIFICATION views — TWO_FACTOR isn't in that list, so "or + * continue with Google/GitHub" shows up on the 2FA challenge page too, + * even though picking one wouldn't actually satisfy the second factor — + * it'd just start an unrelated sign-in. There's no prop on AuthView to + * exclude a view from that section, so a nested provider with `social` + * disabled for just this route is the only way to suppress it. + */ +export function TwoFactorAuthView({ pathname }: { pathname: string }) { + const authUIProviderProps = useAuthUIProviderProps(); + + return ( + + + + ); +} diff --git a/apps/web/src/features/auth/lib/client.ts b/apps/web/src/features/auth/lib/client.ts index 701ddfe7..997ed1d7 100644 --- a/apps/web/src/features/auth/lib/client.ts +++ b/apps/web/src/features/auth/lib/client.ts @@ -1,5 +1,5 @@ import { - emailOTPClient, + magicLinkClient, organizationClient, twoFactorClient, } from "better-auth/client/plugins"; @@ -8,6 +8,6 @@ import { createAuthClient } from "better-auth/react"; export const authClient: ReturnType = createAuthClient( { baseURL: `${process.env.NEXT_PUBLIC_CMS_URL}/api/auth`, - plugins: [organizationClient(), twoFactorClient(), emailOTPClient()], + plugins: [organizationClient(), twoFactorClient(), magicLinkClient()], }, ); diff --git a/apps/web/src/features/auth/lib/use-auth-ui-props.tsx b/apps/web/src/features/auth/lib/use-auth-ui-props.tsx new file mode 100644 index 00000000..64d358b3 --- /dev/null +++ b/apps/web/src/features/auth/lib/use-auth-ui-props.tsx @@ -0,0 +1,71 @@ +"use client"; + +import type { AuthUIProvider } from "@daveyplate/better-auth-ui"; +import Image from "next/image"; +import Link from "next/link"; +import { useRouter } from "next/navigation"; +import type { ComponentProps } from "react"; +import { cmsImageUrl } from "@/features/cms/lib/image-url"; +import { cn } from "@/lib/utils"; +import { deleteAvatar, uploadAvatar } from "./avatar"; +import { authClient } from "./client"; + +/** + * The shared config, factored out so a route that needs + * to override just one setting can nest a second provider without its + * config drifting out of sync with this one. + */ +export function useAuthUIProviderProps(): Omit< + ComponentProps, + "children" +> { + const router = useRouter(); + + return { + authClient, + navigate: (href) => router.push(href), + replace: (href) => router.replace(href), + Link: ({ href, ...props }) => , + basePath: "/auth", + baseURL: process.env.NEXT_PUBLIC_WEB_URL, + account: true, + avatar: { + upload: uploadAvatar, + delete: deleteAvatar, + Image: ({ src, alt, className, ...props }) => { + // Unlike Radix's own AvatarImage (which only mounts the + // once a src successfully loads), better-auth-ui renders a + // custom `avatar.Image` unconditionally — including with an + // empty src when the user has no avatar. Bail out so no + // ends up in the DOM and the fallback initials show instead. + if (!src) return null; + + return ( + // better-auth-ui renders this in place of Radix's AvatarImage + // (which normally gets `aspect-square size-full` to fill the + // circular container) — replicate that here with `fill` + // instead of a fixed width/height, or the image only covers a + // small, badly-positioned box inside the avatar circle. + {alt} + ); + }, + }, + magicLink: true, + social: { + providers: ["google", "github"], + }, + twoFactor: ["otp", "totp"], + organization: { + basePath: "/org", + pathMode: "slug", + personalPath: "/account", + }, + }; +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 53583dca..ba242874 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -48,8 +48,8 @@ importers: apps/cms: dependencies: '@better-auth-ui/react': - specifier: ^1.6.3 - version: 1.6.3(0d1590071f28a5ba08bcd1840e03194f) + specifier: ^1.6.44 + version: 1.6.44(a201276b8466b095739dcdb4fdb7f42d) '@better-auth/infra': specifier: ^0.2.5 version: 0.2.5(f3203897ca24aa1d7482643411714120) @@ -61,10 +61,10 @@ importers: version: 1.4.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@strapi-community/plugin-better-auth': specifier: ^1.0.0-beta.6 - version: 1.0.0-beta.6(932d618de66d66b770f4bd334c84b400) + version: 1.0.0-beta.6(fd35780beaa7c8b8b6f2868050a30a3d) '@strapi-community/plugin-better-auth-dashboard': specifier: 1.0.0-alpha.10 - version: 1.0.0-alpha.10(247722706193c89f065cf703f728c040) + version: 1.0.0-alpha.10(b1927daabe7dd0388ec030704dc1c13f) '@strapi/admin': specifier: catalog:strapi version: 5.45.0(@babel/runtime@7.28.4)(@codemirror/autocomplete@6.20.0)(@codemirror/language@6.11.3)(@codemirror/lint@6.9.2)(@codemirror/search@6.5.11)(@codemirror/state@6.5.2)(@codemirror/theme-one-dark@6.1.3)(@codemirror/view@6.39.4)(@emotion/is-prop-valid@1.2.2)(@strapi/data-transfer@5.45.0(@types/node@20.19.27)(better-sqlite3@12.4.1)(pg@8.21.0)(typescript@5.9.2))(@types/hoist-non-react-statics@3.3.7(@types/react@18.3.27))(@types/node@20.19.27)(@types/react-dom@18.3.7(@types/react@18.3.27))(@types/react@18.3.27)(better-sqlite3@12.4.1)(codemirror@5.65.20)(debug@4.3.4)(pg@8.21.0)(react-dom@18.3.1(react@18.3.1))(react-router-dom@6.30.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1)(redis@4.6.14)(redux@4.2.1)(styled-components@6.1.19(react-dom@18.3.1(react@18.3.1))(react@18.3.1)) @@ -76,13 +76,13 @@ importers: version: 2.2.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(styled-components@6.1.19(react-dom@18.3.1(react@18.3.1))(react@18.3.1)) '@strapi/plugin-cloud': specifier: catalog:strapi - version: 5.45.0(770eb9469b475171bba33fb37a36eebf) + version: 5.45.0(ff33188423ab4254570ec0cdf36587d8) '@strapi/provider-email-sendgrid': specifier: catalog:strapi version: 5.45.0 '@strapi/strapi': specifier: catalog:strapi - version: 5.45.0(@babel/runtime@7.28.4)(@codemirror/autocomplete@6.20.0)(@codemirror/language@6.11.3)(@codemirror/lint@6.9.2)(@codemirror/search@6.5.11)(@codemirror/state@6.5.2)(@codemirror/theme-one-dark@6.1.3)(@codemirror/view@6.39.4)(@emotion/is-prop-valid@1.2.2)(@swc/helpers@0.5.18)(@types/hoist-non-react-statics@3.3.7(@types/react@18.3.27))(@types/node@20.19.27)(@types/react-dom@18.3.7(@types/react@18.3.27))(@types/react@18.3.27)(better-sqlite3@12.4.1)(codemirror@5.65.20)(esbuild@0.27.7)(koa@2.16.4)(lightningcss@1.31.1)(pg@8.21.0)(react-dom@18.3.1(react@18.3.1))(react-router-dom@6.30.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1)(redis@4.6.14)(redux@4.2.1)(styled-components@6.1.19(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(terser@5.44.1)(type-fest@4.41.0) + version: 5.45.0(@babel/runtime@7.28.4)(@codemirror/autocomplete@6.20.0)(@codemirror/language@6.11.3)(@codemirror/lint@6.9.2)(@codemirror/search@6.5.11)(@codemirror/state@6.5.2)(@codemirror/theme-one-dark@6.1.3)(@codemirror/view@6.39.4)(@emotion/is-prop-valid@1.2.2)(@swc/helpers@0.5.18)(@types/hoist-non-react-statics@3.3.7(@types/react@18.3.27))(@types/node@20.19.27)(@types/react-dom@18.3.7(@types/react@18.3.27))(@types/react@18.3.27)(better-sqlite3@12.4.1)(codemirror@5.65.20)(esbuild@0.28.2)(koa@2.16.4)(lightningcss@1.31.1)(pg@8.21.0)(react-dom@18.3.1(react@18.3.1))(react-router-dom@6.30.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1)(redis@4.6.14)(redux@4.2.1)(styled-components@6.1.19(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(terser@5.44.1)(type-fest@4.41.0) '@strapi/types': specifier: catalog:strapi version: 5.45.0(@types/node@20.19.27)(better-sqlite3@12.4.1)(pg@8.21.0)(typescript@5.9.2) @@ -110,6 +110,9 @@ importers: react-dom: specifier: ^18.0.0 version: 18.3.1(react@18.3.1) + react-email: + specifier: ^6.6.8 + version: 6.9.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1) react-query: specifier: ^3.39.3 version: 3.39.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1) @@ -118,19 +121,19 @@ importers: version: 6.30.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1) strapi-lucide-icons: specifier: ^1.14.0 - version: 1.14.0(9df38cc1293a58a6c42c61569eb73f43) + version: 1.14.0(b8b74674fc9c2814fa92b14e60149852) strapi-plugin-meilisearch: specifier: ^0.15.0 - version: 0.15.0(f77b4f63add7dbf7d09216c98e78916e) + version: 0.15.0(bcc84c0dccc5675451bed4707160a510) strapi-plugin-webtools: specifier: ^1.10.3 - version: 1.10.3(9d2b8bd2d0baeb82b3b54609990dbcb0) + version: 1.10.3(c8cc467db588f2291b44f319065bfcfb) styled-components: specifier: ^6.0.0 version: 6.1.19(react-dom@18.3.1(react@18.3.1))(react@18.3.1) webtools-addon-sitemap: specifier: ^1.3.1 - version: 1.3.1(2645048d954e5d7b65a3189de32cdcc5) + version: 1.3.1(794233b068737205e9cbed44c2ce006f) yaml: specifier: ^2.8.2 version: 2.8.2 @@ -541,6 +544,10 @@ packages: resolution: {integrity: sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==} engines: {node: '>=6.9.0'} + '@babel/code-frame@7.29.7': + resolution: {integrity: sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==} + engines: {node: '>=6.9.0'} + '@babel/compat-data@7.28.5': resolution: {integrity: sha512-6uFXyCayocRbqhZOB+6XcuZbkMNimwfVGFji8CTZnCzOHVGvDqzvitu1re2AU5LROliz7eQPhB8CpAMvnx9EjA==} engines: {node: '>=6.9.0'} @@ -553,6 +560,10 @@ packages: resolution: {integrity: sha512-3EwLFhZ38J4VyIP6WNtt2kUdW9dokXA9Cr4IVIFHuCpZ3H8/YFOl5JjZHisrn1fATPBmKKqXzDFvh9fUwHz6CQ==} engines: {node: '>=6.9.0'} + '@babel/generator@7.29.8': + resolution: {integrity: sha512-gZbepsdh3WDtgZKWL+vTPh71LSBrm/Y4/QDZBVCcYfmeTEEuoOYwlSy+G1StfJg+/Zy550u/3TATbm7qDbbMtg==} + engines: {node: '>=6.9.0'} + '@babel/helper-annotate-as-pure@7.27.3': resolution: {integrity: sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg==} engines: {node: '>=6.9.0'} @@ -607,10 +618,18 @@ packages: resolution: {integrity: sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==} engines: {node: '>=6.9.0'} + '@babel/helper-string-parser@7.29.7': + resolution: {integrity: sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==} + engines: {node: '>=6.9.0'} + '@babel/helper-validator-identifier@7.28.5': resolution: {integrity: sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==} engines: {node: '>=6.9.0'} + '@babel/helper-validator-identifier@7.29.7': + resolution: {integrity: sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==} + engines: {node: '>=6.9.0'} + '@babel/helper-validator-option@7.27.1': resolution: {integrity: sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==} engines: {node: '>=6.9.0'} @@ -624,6 +643,16 @@ packages: engines: {node: '>=6.0.0'} hasBin: true + '@babel/parser@7.29.2': + resolution: {integrity: sha512-4GgRzy/+fsBa72/RZVJmGKPmZu9Byn8o4MoLpmNe1m8ZfYnz5emHLQz3U4gLud6Zwl0RZIcgiLD7Uq7ySFuDLA==} + engines: {node: '>=6.0.0'} + hasBin: true + + '@babel/parser@7.29.8': + resolution: {integrity: sha512-E8lTAYNB1KW+FH+VGJuZM1ioAx2E6oVlvQFRrf5P8ZZmsiJXYAD9vTFV7yyEURNzgh1dFqMZuO6tUwcARbqFCA==} + engines: {node: '>=6.0.0'} + hasBin: true + '@babel/plugin-syntax-flow@7.27.1': resolution: {integrity: sha512-p9OkPbZ5G7UT1MofwYFigGebnrzGJacoBSQM0/6bi/PUMVE+qlWDD/OalvQKbwgQzU6dl0xAv6r4X7Jme0RYxA==} engines: {node: '>=6.9.0'} @@ -710,32 +739,51 @@ packages: resolution: {integrity: sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==} engines: {node: '>=6.9.0'} + '@babel/template@7.29.7': + resolution: {integrity: sha512-puq+Gf35oI24FeN11LkoUQFqv9uwNeWpxXZi/Ji3rRIoKAzKnxRaZ+Gkj0vKS9ZCiTESfng1N9LyOyXvo+m+Gg==} + engines: {node: '>=6.9.0'} + '@babel/traverse@7.28.5': resolution: {integrity: sha512-TCCj4t55U90khlYkVV/0TfkJkAkUg3jZFA3Neb7unZT8CPok7iiRfaX0F+WnqWqt7OxhOn0uBKXCw4lbL8W0aQ==} engines: {node: '>=6.9.0'} + '@babel/traverse@7.29.0': + resolution: {integrity: sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA==} + engines: {node: '>=6.9.0'} + '@babel/types@7.28.5': resolution: {integrity: sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA==} engines: {node: '>=6.9.0'} + '@babel/types@7.29.8': + resolution: {integrity: sha512-Vj1jF3cPfxg7OAfoI7QnVKLoILlm2JF9pnVHrX8qx7AHMiYWT+NDAA7jChlNgRS4WTLc/fD1lXLmPixluj+3Gg==} + engines: {node: '>=6.9.0'} + '@better-auth-ui/core@1.6.3': resolution: {integrity: sha512-06jDn6FV8R/blHI7sSwGFmaaLwp6Uo+y/No6FF62eP4dbI+8mdQjzu8IMYPK5dMyn31qI9IgW5dac0UCTVaGtQ==} peerDependencies: better-auth: '>=1.6.5' - '@better-auth-ui/react@1.6.3': - resolution: {integrity: sha512-q1G1EzjYWwtkP7FCkywYEw2wPQskyiwnofFqI2FlxZIFAPdNwlmC4ahcBuacLQKF32CwGzQMHUyZ7AGkzYbSqQ==} + '@better-auth-ui/react@1.6.44': + resolution: {integrity: sha512-icY/7Gzy4ntZH+rYqSphAdpyLcygx+H/YUr3f7xRZVc2YXHsxNBC7wlpsM9sTyw8niGtw+E9Kb/gYg8nhI8Lfg==} peerDependencies: - '@better-auth/passkey': '>=1.6.5' - '@react-email/components': '>=1.0.12' - '@tanstack/react-query': '>=5.99.0' - better-auth: '>=1.6.5' + '@better-auth-ui/core': '*' + '@better-auth/api-key': '>=1.6.19' + '@better-auth/oauth-provider': '>=1.6.19' + '@better-auth/passkey': '>=1.6.19' + '@tanstack/query-core': '>=5.100.14' + '@tanstack/react-query': '>=5.100.14' + better-auth: '>=1.6.19' clsx: '>=2.0.0' - react: '>=19.2.5' - react-dom: '>=19.2.5' + react: '>=19.2.6' + react-dom: '>=19.2.6' + react-email: '>=6.0.0' tailwind-merge: '>=3.5.0' - tailwindcss: '>=4.2.2' - zod: '>=4.3.0' + tailwindcss: '>=4.3.1' + zod: '>=4.4.3' + peerDependenciesMeta: + react-email: + optional: true '@better-auth/api-key@1.6.9': resolution: {integrity: sha512-MPDNmvcCwDpix911kFYRn9XCebJjaCNuj16OA//difNCJoPRn2kD6KQ/3+B3rlSWl46x098SgN7Y3e8kU8nIwg==} @@ -821,6 +869,15 @@ packages: mongodb: optional: true + '@better-auth/oauth-provider@1.6.26': + resolution: {integrity: sha512-u22d0f9PyW5j/JwK3HKsFGYip/DpZVlVCyJ3gf+KeeLDizE8BXAuVXqkd9WO8XsamJ3LxUysPcC82Ltx74JOZA==} + peerDependencies: + '@better-auth/core': ^1.6.26 + '@better-auth/utils': 0.4.2 + '@better-fetch/fetch': 1.3.1 + better-auth: ^1.6.26 + better-call: 1.3.7 + '@better-auth/passkey@1.6.9': resolution: {integrity: sha512-MFpi+2G/pG2wVcTuL/PcnWxP2ddFL4jmFByTCbgvr61tp7u96d5liBptxpTqfS5IuCi2o8bBRmjiQnDZAvxmHg==} peerDependencies: @@ -1179,6 +1236,12 @@ packages: cpu: [ppc64] os: [aix] + '@esbuild/aix-ppc64@0.28.2': + resolution: {integrity: sha512-XExcO+dvLKvVtNTibSTBej1NCAbaGhWn9Ww1ZPx80qsahhPFe/8jgWP0IchNe0F3HwkU7n8ejhH8bjonqht8mQ==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [aix] + '@esbuild/android-arm64@0.20.2': resolution: {integrity: sha512-mRzjLacRtl/tWU0SvD8lUEwb61yP9cqQo6noDZP/O8VkwafSYwZ4yWy24kan8jE/IMERpYncRt2dw438LP3Xmg==} engines: {node: '>=12'} @@ -1197,6 +1260,12 @@ packages: cpu: [arm64] os: [android] + '@esbuild/android-arm64@0.28.2': + resolution: {integrity: sha512-5YfKeeI8qWfBZIX+u2xZC3Zlb3Os/gLS2sbEKM+I4ZOcsWmHS2WLysCcQZDAFRslDUU5Oiq44gf6PYN1vGwG5A==} + engines: {node: '>=18'} + cpu: [arm64] + os: [android] + '@esbuild/android-arm@0.20.2': resolution: {integrity: sha512-t98Ra6pw2VaDhqNWO2Oph2LXbz/EJcnLmKLGBJwEwXX/JAN83Fym1rU8l0JUWK6HkIbWONCSSatf4sf2NBRx/w==} engines: {node: '>=12'} @@ -1215,6 +1284,12 @@ packages: cpu: [arm] os: [android] + '@esbuild/android-arm@0.28.2': + resolution: {integrity: sha512-kXXoiPVVGQcnIYGOeaovwOURpniDBpSq4A03qkQ+BMQqtGG6HYap3xne9C1O1yo4TR3qxlCX5IqqmX6fFo2Lqg==} + engines: {node: '>=18'} + cpu: [arm] + os: [android] + '@esbuild/android-x64@0.20.2': resolution: {integrity: sha512-btzExgV+/lMGDDa194CcUQm53ncxzeBrWJcncOBxuC6ndBkKxnHdFJn86mCIgTELsooUmwUm9FkhSp5HYu00Rg==} engines: {node: '>=12'} @@ -1233,6 +1308,12 @@ packages: cpu: [x64] os: [android] + '@esbuild/android-x64@0.28.2': + resolution: {integrity: sha512-O387ite7SzUyCcy3JQX4P4bLtEA7bLLkx+esve5JHnyYfNTxcVpXZo9jhdB0lTKN44gztELTdU7nS8Nr16Fs1Q==} + engines: {node: '>=18'} + cpu: [x64] + os: [android] + '@esbuild/darwin-arm64@0.20.2': resolution: {integrity: sha512-4J6IRT+10J3aJH3l1yzEg9y3wkTDgDk7TSDFX+wKFiWjqWp/iCfLIYzGyasx9l0SAFPT1HwSCR+0w/h1ES/MjA==} engines: {node: '>=12'} @@ -1251,6 +1332,12 @@ packages: cpu: [arm64] os: [darwin] + '@esbuild/darwin-arm64@0.28.2': + resolution: {integrity: sha512-n4KqkOQrraxHJcgjM1RvwbigfQKIKJVpM7xp+KsxiyUSrRdIXnt73VhrPAx0fV44hgfmIVKjxMN9J1t5jySVkw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [darwin] + '@esbuild/darwin-x64@0.20.2': resolution: {integrity: sha512-tBcXp9KNphnNH0dfhv8KYkZhjc+H3XBkF5DKtswJblV7KlT9EI2+jeA8DgBjp908WEuYll6pF+UStUCfEpdysA==} engines: {node: '>=12'} @@ -1269,6 +1356,12 @@ packages: cpu: [x64] os: [darwin] + '@esbuild/darwin-x64@0.28.2': + resolution: {integrity: sha512-uq6suIWYP37qzGddBKPw5QEQPi6HiLGsO7UmkpfyaYNQ3D+rN6w6WfwH+nuqcGXWvawGwxOEroO4YGnFh95azw==} + engines: {node: '>=18'} + cpu: [x64] + os: [darwin] + '@esbuild/freebsd-arm64@0.20.2': resolution: {integrity: sha512-d3qI41G4SuLiCGCFGUrKsSeTXyWG6yem1KcGZVS+3FYlYhtNoNgYrWcvkOoaqMhwXSMrZRl69ArHsGJ9mYdbbw==} engines: {node: '>=12'} @@ -1287,6 +1380,12 @@ packages: cpu: [arm64] os: [freebsd] + '@esbuild/freebsd-arm64@0.28.2': + resolution: {integrity: sha512-n+I0BTSRIoy+d6RPKnEVwql5UwBJolytvY4mAOIEJorKlqgPII8ix6slVVrfZ5Tnj7glIZvloylbB/EJPMWEXw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [freebsd] + '@esbuild/freebsd-x64@0.20.2': resolution: {integrity: sha512-d+DipyvHRuqEeM5zDivKV1KuXn9WeRX6vqSqIDgwIfPQtwMP4jaDsQsDncjTDDsExT4lR/91OLjRo8bmC1e+Cw==} engines: {node: '>=12'} @@ -1305,6 +1404,12 @@ packages: cpu: [x64] os: [freebsd] + '@esbuild/freebsd-x64@0.28.2': + resolution: {integrity: sha512-78XJTJkvPs0kz2w61301PJjXl4g7q3JqiYMZ/M/yVI73EHBrCRTgkhu9oqG7vPqq+a/yadEW8aD+agKlk5xrmg==} + engines: {node: '>=18'} + cpu: [x64] + os: [freebsd] + '@esbuild/linux-arm64@0.20.2': resolution: {integrity: sha512-9pb6rBjGvTFNira2FLIWqDk/uaf42sSyLE8j1rnUpuzsODBq7FvpwHYZxQ/It/8b+QOS1RYfqgGFNLRI+qlq2A==} engines: {node: '>=12'} @@ -1323,6 +1428,12 @@ packages: cpu: [arm64] os: [linux] + '@esbuild/linux-arm64@0.28.2': + resolution: {integrity: sha512-pW4AC0P3it8c7do9MVM4p51FzHzdM/TZrerurgRcHJ2WTa1VQ1CIq18xncfpBJw4ojkiZZrKW2yIBWBP92j6Ug==} + engines: {node: '>=18'} + cpu: [arm64] + os: [linux] + '@esbuild/linux-arm@0.20.2': resolution: {integrity: sha512-VhLPeR8HTMPccbuWWcEUD1Az68TqaTYyj6nfE4QByZIQEQVWBB8vup8PpR7y1QHL3CpcF6xd5WVBU/+SBEvGTg==} engines: {node: '>=12'} @@ -1341,6 +1452,12 @@ packages: cpu: [arm] os: [linux] + '@esbuild/linux-arm@0.28.2': + resolution: {integrity: sha512-XlDnu2q5yoqems+xay6wSAcg9DDD7K9RLKZEBOMZm3ckNpJBvOX20tSfby8KfrrhINDyv9V2YVZKY/SpoGJI8w==} + engines: {node: '>=18'} + cpu: [arm] + os: [linux] + '@esbuild/linux-ia32@0.20.2': resolution: {integrity: sha512-o10utieEkNPFDZFQm9CoP7Tvb33UutoJqg3qKf1PWVeeJhJw0Q347PxMvBgVVFgouYLGIhFYG0UGdBumROyiig==} engines: {node: '>=12'} @@ -1359,6 +1476,12 @@ packages: cpu: [ia32] os: [linux] + '@esbuild/linux-ia32@0.28.2': + resolution: {integrity: sha512-CYbnj78HsIeA+DhgUKgFCfvNsTHFhMMrinUrMZpDXJXKN8T3XViTZ/+wtHeVxEWY8ewSzTFN+nRmSwO2tZaLUQ==} + engines: {node: '>=18'} + cpu: [ia32] + os: [linux] + '@esbuild/linux-loong64@0.20.2': resolution: {integrity: sha512-PR7sp6R/UC4CFVomVINKJ80pMFlfDfMQMYynX7t1tNTeivQ6XdX5r2XovMmha/VjR1YN/HgHWsVcTRIMkymrgQ==} engines: {node: '>=12'} @@ -1377,6 +1500,12 @@ packages: cpu: [loong64] os: [linux] + '@esbuild/linux-loong64@0.28.2': + resolution: {integrity: sha512-buwkd8nsph4R+ajRvw0qM5Hja/TXQow3ptzWO2EbG/cqcIkHloRrdlBtQlshyYGTNFvfkfJ5tpPLVkY4DtsPfQ==} + engines: {node: '>=18'} + cpu: [loong64] + os: [linux] + '@esbuild/linux-mips64el@0.20.2': resolution: {integrity: sha512-4BlTqeutE/KnOiTG5Y6Sb/Hw6hsBOZapOVF6njAESHInhlQAghVVZL1ZpIctBOoTFbQyGW+LsVYZ8lSSB3wkjA==} engines: {node: '>=12'} @@ -1395,6 +1524,12 @@ packages: cpu: [mips64el] os: [linux] + '@esbuild/linux-mips64el@0.28.2': + resolution: {integrity: sha512-ZVykbDyk7519VwiNb9Lcj9m8XM6v5V9uKPvrEMkkEedVewf+0itkhahp4HDpgERXhwLRpWFypsGbG/J8s0QjJA==} + engines: {node: '>=18'} + cpu: [mips64el] + os: [linux] + '@esbuild/linux-ppc64@0.20.2': resolution: {integrity: sha512-rD3KsaDprDcfajSKdn25ooz5J5/fWBylaaXkuotBDGnMnDP1Uv5DLAN/45qfnf3JDYyJv/ytGHQaziHUdyzaAg==} engines: {node: '>=12'} @@ -1413,6 +1548,12 @@ packages: cpu: [ppc64] os: [linux] + '@esbuild/linux-ppc64@0.28.2': + resolution: {integrity: sha512-CAXl+Dtd9UUuJd8pKKdwh6MLm3MUMiqMPmhZ3tTSXPqfyQ3vDl6R5hZdZ/kYojK4ofXtdfSv1tFq8XzWx3heNQ==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [linux] + '@esbuild/linux-riscv64@0.20.2': resolution: {integrity: sha512-snwmBKacKmwTMmhLlz/3aH1Q9T8v45bKYGE3j26TsaOVtjIag4wLfWSiZykXzXuE1kbCE+zJRmwp+ZbIHinnVg==} engines: {node: '>=12'} @@ -1431,6 +1572,12 @@ packages: cpu: [riscv64] os: [linux] + '@esbuild/linux-riscv64@0.28.2': + resolution: {integrity: sha512-GeXCej4IQtU1B+QlDV8W/RRvbzI3O/Stss+/bCXv4lZls5WGRtu2a+3JkA3i4qIUlMXpcHebWpF8AkJhATowuA==} + engines: {node: '>=18'} + cpu: [riscv64] + os: [linux] + '@esbuild/linux-s390x@0.20.2': resolution: {integrity: sha512-wcWISOobRWNm3cezm5HOZcYz1sKoHLd8VL1dl309DiixxVFoFe/o8HnwuIwn6sXre88Nwj+VwZUvJf4AFxkyrQ==} engines: {node: '>=12'} @@ -1449,6 +1596,12 @@ packages: cpu: [s390x] os: [linux] + '@esbuild/linux-s390x@0.28.2': + resolution: {integrity: sha512-3H1weTYZPxt/WOhByszQZybS9w5lKzUn1FDMsgEChbHWQwHYQQRfBxgCcZvPhjHfKyJjIievvMmEUawJrdY9Dg==} + engines: {node: '>=18'} + cpu: [s390x] + os: [linux] + '@esbuild/linux-x64@0.20.2': resolution: {integrity: sha512-1MdwI6OOTsfQfek8sLwgyjOXAu+wKhLEoaOLTjbijk6E2WONYpH9ZU2mNtR+lZ2B4uwr+usqGuVfFT9tMtGvGw==} engines: {node: '>=12'} @@ -1467,12 +1620,24 @@ packages: cpu: [x64] os: [linux] + '@esbuild/linux-x64@0.28.2': + resolution: {integrity: sha512-4xTZr1FUmSoQW4XIWmit3tzQrUTZM+N3P0XV8xROKYF50XfI7xeO90+1bZvNwxIufQ9hDQVRJH5YhgPVF8A/HQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [linux] + '@esbuild/netbsd-arm64@0.27.7': resolution: {integrity: sha512-b6pqtrQdigZBwZxAn1UpazEisvwaIDvdbMbmrly7cDTMFnw/+3lVxxCTGOrkPVnsYIosJJXAsILG9XcQS+Yu6w==} engines: {node: '>=18'} cpu: [arm64] os: [netbsd] + '@esbuild/netbsd-arm64@0.28.2': + resolution: {integrity: sha512-sSATRjPeDBg3pdgHoQfoYBob11Kk1FGa9lui5RIHZCoCkJa9QKlvl3/vKz2usCmYYjs7ymJR/2Nnsqe+Hjt5nw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [netbsd] + '@esbuild/netbsd-x64@0.20.2': resolution: {integrity: sha512-K8/DhBxcVQkzYc43yJXDSyjlFeHQJBiowJ0uVL6Tor3jGQfSGHNNJcWxNbOI8v5k82prYqzPuwkzHt3J1T1iZQ==} engines: {node: '>=12'} @@ -1491,12 +1656,24 @@ packages: cpu: [x64] os: [netbsd] + '@esbuild/netbsd-x64@0.28.2': + resolution: {integrity: sha512-lqnzCV+mM0gIADaKihiCg6ifgfU2L3h5E33rNQBN1Y4MaVGnzryzmvvf7UHxprpQdE8hpqLolJ9Rl+SkIRDpyw==} + engines: {node: '>=18'} + cpu: [x64] + os: [netbsd] + '@esbuild/openbsd-arm64@0.27.7': resolution: {integrity: sha512-AFuojMQTxAz75Fo8idVcqoQWEHIXFRbOc1TrVcFSgCZtQfSdc1RXgB3tjOn/krRHENUB4j00bfGjyl2mJrU37A==} engines: {node: '>=18'} cpu: [arm64] os: [openbsd] + '@esbuild/openbsd-arm64@0.28.2': + resolution: {integrity: sha512-AL2qJILH7lNjrDmCQDvdxMfAUIv8KMNZOvrwAQ8i8//ntL9FflhOyMJ8OZSMBb8/AWXe3/5v5S20y3zCoZWKoQ==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openbsd] + '@esbuild/openbsd-x64@0.20.2': resolution: {integrity: sha512-eMpKlV0SThJmmJgiVyN9jTPJ2VBPquf6Kt/nAoo6DgHAoN57K15ZghiHaMvqjCye/uU4X5u3YSMgVBI1h3vKrQ==} engines: {node: '>=12'} @@ -1515,12 +1692,24 @@ packages: cpu: [x64] os: [openbsd] + '@esbuild/openbsd-x64@0.28.2': + resolution: {integrity: sha512-QtiuPytchRyC4rwUKhexJdQKvDuZ6hWloi3igqPQNUJCS1/v9EiO3UTOXR6A3FoMo4fnAKbWJdqaIwhOzh8qEw==} + engines: {node: '>=18'} + cpu: [x64] + os: [openbsd] + '@esbuild/openharmony-arm64@0.27.7': resolution: {integrity: sha512-+KrvYb/C8zA9CU/g0sR6w2RBw7IGc5J2BPnc3dYc5VJxHCSF1yNMxTV5LQ7GuKteQXZtspjFbiuW5/dOj7H4Yw==} engines: {node: '>=18'} cpu: [arm64] os: [openharmony] + '@esbuild/openharmony-arm64@0.28.2': + resolution: {integrity: sha512-WkhYDmpTjLvGlScA1rwjRUmhl4k8oXR3cIbtqWmELgU/dFeHHlEllxDvdWcNJV9rbzCexB5vz8gtNewWLgCT7Q==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openharmony] + '@esbuild/sunos-x64@0.20.2': resolution: {integrity: sha512-2UyFtRC6cXLyejf/YEld4Hajo7UHILetzE1vsRcGL3earZEW77JxrFjH4Ez2qaTiEfMgAXxfAZCm1fvM/G/o8w==} engines: {node: '>=12'} @@ -1539,6 +1728,12 @@ packages: cpu: [x64] os: [sunos] + '@esbuild/sunos-x64@0.28.2': + resolution: {integrity: sha512-GPMSkTOtMnv2U2F8gxe4Io6qmVs+YKyp832Etqqxr0hFngmXQ3rzwytelm3GIn7T4VviRUlf3sOgBOiTdvaf7g==} + engines: {node: '>=18'} + cpu: [x64] + os: [sunos] + '@esbuild/win32-arm64@0.20.2': resolution: {integrity: sha512-GRibxoawM9ZCnDxnP3usoUDO9vUkpAxIIZ6GQI+IlVmr5kP3zUq+l17xELTHMWTWzjxa2guPNyrpq1GWmPvcGQ==} engines: {node: '>=12'} @@ -1557,6 +1752,12 @@ packages: cpu: [arm64] os: [win32] + '@esbuild/win32-arm64@0.28.2': + resolution: {integrity: sha512-PIhhEkE9uPBleRBrQEJpUn7MBnibZzbGzYWPmY3x+YoVg/95zbjB4CxPPOQ8l5tYYM4mMaCthF8/1DIfBQQyWQ==} + engines: {node: '>=18'} + cpu: [arm64] + os: [win32] + '@esbuild/win32-ia32@0.20.2': resolution: {integrity: sha512-HfLOfn9YWmkSKRQqovpnITazdtquEW8/SoHW7pWpuEeguaZI4QnCRW6b+oZTztdBnZOS2hqJ6im/D5cPzBTTlQ==} engines: {node: '>=12'} @@ -1575,6 +1776,12 @@ packages: cpu: [ia32] os: [win32] + '@esbuild/win32-ia32@0.28.2': + resolution: {integrity: sha512-YmJbfTlvU7Sdn9BB+4PRES4oB6pxgS37MAONj+hBr/cpXS1aBPKXxNnDbu+QCWPj0o9dgyxeq79g6c5P8KeuYA==} + engines: {node: '>=18'} + cpu: [ia32] + os: [win32] + '@esbuild/win32-x64@0.20.2': resolution: {integrity: sha512-N49X4lJX27+l9jbLKSqZ6bKNjzQvHaT8IIFUy+YIqmXQdjYCToGWwOItDrfby14c78aDd5NHQl29xingXfCdLQ==} engines: {node: '>=12'} @@ -1593,6 +1800,12 @@ packages: cpu: [x64] os: [win32] + '@esbuild/win32-x64@0.28.2': + resolution: {integrity: sha512-5ebpxr3nWMzrL/rnUI755Jkuee0bHL/Gq0WTF9lvcpv73wAp5eu8MfBUgWK9bhWvZjj7yX8etf/8tI8Ney695g==} + engines: {node: '>=18'} + cpu: [x64] + os: [win32] + '@floating-ui/core@1.7.3': resolution: {integrity: sha512-sGnvb5dmrJaKEZ+LDIpguvdX3bDlEllmv4/ClQ9awcmCZrlx5jQyyMWFM5kBI+EyNOCDDiKk8il0zeuX3Zlg/w==} @@ -3746,6 +3959,13 @@ packages: react: ^18.0 || ^19.0 || ^19.0.0-rc react-dom: ^18.0 || ^19.0 || ^19.0.0-rc + '@react-email/render@2.1.0': + resolution: {integrity: sha512-F+zE3O6d6sW6Aj2UjvZAA17R7tJKM7kcq2mgV6k4HCT8jeLLFaVP2txMtH1lgqYFRMZ0Gxsd37q2PRyiXLXXxA==} + engines: {node: '>=20.0.0'} + peerDependencies: + react: ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^18.0 || ^19.0 || ^19.0.0-rc + '@react-email/row@0.0.13': resolution: {integrity: sha512-bYnOac40vIKCId7IkwuLAAsa3fKfSfqCvv6epJKmPE0JBuu5qI4FHFCl9o9dVpIIS08s/ub+Y/txoMt0dYziGw==} engines: {node: '>=20.0.0'} @@ -4026,6 +4246,9 @@ packages: '@so-ric/colorspace@1.1.6': resolution: {integrity: sha512-/KiKkpHNOBgkFJwu9sh48LkHSMYGyuTcSFK/qMBdnOAlrRJzRSXAOFB5qwzaVQuDl8wAvHVMkaASQDReTahxuw==} + '@socket.io/component-emitter@3.1.2': + resolution: {integrity: sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA==} + '@standard-schema/spec@1.1.0': resolution: {integrity: sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==} @@ -4534,6 +4757,9 @@ packages: '@types/cookies@0.9.2': resolution: {integrity: sha512-1AvkDdZM2dbyFybL4fxpuNCaWyv//0AwsuUk2DWeXyM1/5ZKm6W3z6mQi24RZ4l2ucY+bkSHzbDVpySqPGuV8A==} + '@types/cors@2.8.19': + resolution: {integrity: sha512-mFNylyeyqN93lfe/9CSxOGREz8cpzAhH+E93xJ4xWQf62V8sQ/24reV2nyzUWM6H6Xji+GGHpkbLe7pVoUEskg==} + '@types/debug@4.1.12': resolution: {integrity: sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==} @@ -4744,6 +4970,9 @@ packages: '@types/whatwg-url@13.0.0': resolution: {integrity: sha512-N8WXpbE6Wgri7KUSvrmQcqrMllKZ9uxkYWMt+mCSGwNc0Hsw9VQTW7ApqI4XNrx6/SaM2QQJCzMPDEXE058s+Q==} + '@types/ws@8.18.1': + resolution: {integrity: sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==} + '@ucast/core@1.10.2': resolution: {integrity: sha512-ons5CwXZ/51wrUPfoduC+cO7AS1/wRb0ybpQJ9RrssossDxVy4t49QxWoWgfBDvVKsz9VXzBk9z0wqTdZ+Cq8g==} @@ -5077,6 +5306,9 @@ packages: asynckit@0.4.0: resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} + atomically@2.1.1: + resolution: {integrity: sha512-P4w9o2dqARji6P7MHprklbfiArZAWvo07yW7qs3pdljb3BWr12FIB7W+p0zJiuiVsUpRO0iZn1kFFcpPegg0tQ==} + attr-accept@2.2.5: resolution: {integrity: sha512-0bDNnY/u6pPwHDMoF0FieU354oBi0a8rD9FcsLwzcGWbc8KS8KPIi7y+s13OlVY+gMWc/9xEMUgNE6Qm8ZllYQ==} engines: {node: '>=4'} @@ -5101,6 +5333,10 @@ packages: base64-js@1.5.1: resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} + base64id@2.0.0: + resolution: {integrity: sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==} + engines: {node: ^4.5.0 || >= 5.9} + base64url@3.0.1: resolution: {integrity: sha512-ir1UPr3dkwexU7FdV8qBBbNDRUhMmIekYMFZfi+C/sLNnRESKPl23nB9b2pltqfOQNnGzsDdId90AEtG5tCx4A==} engines: {node: '>=6.0.0'} @@ -5345,6 +5581,10 @@ packages: resolution: {integrity: sha512-n8enUVCED/KVRQlab1hr3MVpcVMvxtZjmEa956u+4YijlmQED223XMSYj2tLuKvr4jcCTzNNMpQDUer72MMmzA==} engines: {node: '>= 14.16.0'} + chokidar@4.0.3: + resolution: {integrity: sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==} + engines: {node: '>= 14.16.0'} + chownr@1.1.4: resolution: {integrity: sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==} @@ -5360,6 +5600,9 @@ packages: resolution: {integrity: sha512-TdHqgGf9odd8SXNuxtUBVx8Nv+qZOejE6qyqiy5NtbYYQOeFa6zmHkxlPzmaLxWWHsU6nJmB7AETdVPi+2NBUg==} engines: {node: '>=8'} + citty@0.2.2: + resolution: {integrity: sha512-+6vJA3L98yv+IdfKGZHBNiGW5KHn22e/JwID0Strsz8h4S/csAu/OuICwxrg44k5MRiZHWIo8XXuJgQTriRP4w==} + class-variance-authority@0.7.1: resolution: {integrity: sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg==} @@ -5494,6 +5737,10 @@ packages: resolution: {integrity: sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==} engines: {node: '>=18'} + commander@13.1.0: + resolution: {integrity: sha512-/rFeCpNJQbhSZjGVwO9RFV3xPqbnERS8MmIQzCtD/zl6gpJuV/bMLuN92oG3F7d8oDEHHRrujSXNUr8fpjntKw==} + engines: {node: '>=18'} + commander@14.0.3: resolution: {integrity: sha512-H+y0Jo/T1RZ9qPP4Eh1pkcQcLRglraJaSLoyOtHxu6AapkjWVCy2Sit1QQ4x3Dng8qDlSsZEet7g5Pq06MvTgw==} engines: {node: '>=20'} @@ -5527,6 +5774,10 @@ packages: engines: {node: ^14.13.0 || >=16.0.0} hasBin: true + conf@15.1.0: + resolution: {integrity: sha512-Uy5YN9KEu0WWDaZAVJ5FAmZoaJt9rdK6kH+utItPyGsCqCgaTKkrmZx3zoE0/3q6S3bcp3Ihkk+ZqPxWxFK5og==} + engines: {node: '>=20'} + config-chain@1.1.13: resolution: {integrity: sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ==} @@ -5548,6 +5799,10 @@ packages: convert-source-map@2.0.0: resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} + cookie@0.7.2: + resolution: {integrity: sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==} + engines: {node: '>= 0.6'} + cookies@0.9.1: resolution: {integrity: sha512-TG2hpqe4ELx54QER/S3HQ9SRVnQnGBtKUz5bLQWtYAQ+o6GpgMs6sYUvaiJjVxb+UXwhRhAEP3m7LbsIZ77Hmw==} engines: {node: '>= 0.8'} @@ -5568,6 +5823,10 @@ packages: core-util-is@1.0.3: resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==} + cors@2.8.6: + resolution: {integrity: sha512-tJtZBBHA6vjIAaF6EnIaq6laBBP9aq/Y3ouVJjEfoHbRBcHBAHYcMh/w8LDrk2PvIMMq8gmopa5D4V8RmbrxGw==} + engines: {node: '>= 0.10'} + cosmiconfig@7.1.0: resolution: {integrity: sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==} engines: {node: '>=10'} @@ -5581,6 +5840,7 @@ packages: cron-parser@4.9.0: resolution: {integrity: sha512-p0SaNjrHOnQeR8/VnfGbmg9te2kfyYSQ7Sc/j/6DtPL3JQvKxmjO9TSjNFpujqV3vEYYBvNNvXSxzyksBWAx1Q==} engines: {node: '>=12.0.0'} + deprecated: v4 is no longer maintained, upgrade to v5 cropperjs@1.6.1: resolution: {integrity: sha512-F4wsi+XkDHCOMrHMYjrTEE4QBOrsHHN5/2VsVAaRq8P7E5z7xQpT75S+f/9WikmBEailas3+yo+6zPIomW+NOA==} @@ -5620,6 +5880,10 @@ packages: css-to-react-native@3.2.0: resolution: {integrity: sha512-e8RKaLXMOFii+02mOlqwjbD00KSEKqblnpO9e++1aXS1fPQOpS1YoqdVHBqPjHNoxeF2mimzVqawm2KCbEdtHQ==} + css-tree@3.2.1: + resolution: {integrity: sha512-X7sjQzceUhu1u7Y/ylrRZFU2FS6LRiFVp6rKLPg23y3x3c3DOKAwuXGDp+PAGjh6CSnCjYeAul8pcT8bAl+lSA==} + engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0} + css-what@6.2.2: resolution: {integrity: sha512-u/O3vwbptzhMs3L1fQE82ZSLHQQfto5gyZzwteVIEyeaY5Fc7R4dapF/BvRoSYFeqfBk4m0V1Vafq5Pjv25wvA==} engines: {node: '>= 6'} @@ -5650,9 +5914,17 @@ packages: resolution: {integrity: sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw==} engines: {node: '>=0.11'} + debounce-fn@6.0.0: + resolution: {integrity: sha512-rBMW+F2TXryBwB54Q0d8drNEI+TfoS9JpNTAoVpukbWEhjXQq4rySFYLaqXMFXwdv61Zb2OHtj5bviSoimqxRQ==} + engines: {node: '>=18'} + debounce@1.2.1: resolution: {integrity: sha512-XRRe6Glud4rd/ZGQfiV1ruXSfbvfJedlV9Y6zOlP+2K04vBYiJEte6stfFkCP03aMnY5tsipamumUjL14fofug==} + debounce@2.2.0: + resolution: {integrity: sha512-Xks6RUDLZFdz8LIdR6q0MTH44k7FikOmnh5xkSjMig6ch45afc8sjTjRQf3P6ax8dMgcQrYO/AR2RGWURrruqw==} + engines: {node: '>=18'} + debug@3.2.7: resolution: {integrity: sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==} peerDependencies: @@ -5842,6 +6114,10 @@ packages: dot-case@3.0.4: resolution: {integrity: sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==} + dot-prop@10.2.0: + resolution: {integrity: sha512-BTJ9aZYL3vCfZlZOBLy9v8TUqWGQ0pzFnygKwFZt5udj6viBoFIBviKPUoZLDCPn1FoXffv6McQFDenrm5Krfw==} + engines: {node: '>=20'} + dot-prop@5.3.0: resolution: {integrity: sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q==} engines: {node: '>=8'} @@ -5893,6 +6169,14 @@ packages: end-of-stream@1.4.5: resolution: {integrity: sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==} + engine.io-parser@5.2.3: + resolution: {integrity: sha512-HqD3yTBfnBxIrbnM1DoD6Pcq8NECnh8d4As1Qgh0z5Gg3jRRIqijury0CL3ghu/edArpUYiYqQiDUQBIs4np3Q==} + engines: {node: '>=10.0.0'} + + engine.io@6.6.9: + resolution: {integrity: sha512-clKkw4C7nJ22mGgoVcCg6V/W/TxdNyIOTr89k2ONZu81qqkddPFDF0LXcbAwhzPD8DjkiRCjzuiO6Y+fkpD4vg==} + engines: {node: '>=10.2.0'} + enhanced-resolve@5.20.0: resolution: {integrity: sha512-/ce7+jQ1PQ6rVXwe+jKEg5hW5ciicHwIQUagZkp6IufBoY3YDgdTTY1azVs0qoRgVmvsNB+rbjLJxDAeHHtwsQ==} engines: {node: '>=10.13.0'} @@ -5908,6 +6192,10 @@ packages: resolution: {integrity: sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==} engines: {node: '>=0.12'} + env-paths@3.0.0: + resolution: {integrity: sha512-dtJUTepzMW3Lm/NPxRf3wP4642UWhjL2sQxc+ym2YMj1m/H2zDNQOlezafzkHwn6sMstjHTwG6iQQsctDW/b1A==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + environment@1.1.0: resolution: {integrity: sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q==} engines: {node: '>=18'} @@ -5967,6 +6255,11 @@ packages: engines: {node: '>=18'} hasBin: true + esbuild@0.28.2: + resolution: {integrity: sha512-HKVLS8dvII+xoKW9kmqxbRKrnWEXfJJr/FZhhJmiqIB0e053QNYFqOBouTMO/k5sID4MvCiUCvv8b9M4h32wIA==} + engines: {node: '>=18'} + hasBin: true + escalade@3.2.0: resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} engines: {node: '>=6'} @@ -6516,6 +6809,9 @@ packages: webpack: optional: true + html5parser@3.0.0: + resolution: {integrity: sha512-iNpSopa+4YHX50UOk825tBy7MghmXHo/ZpLskBYN0kAr1xhH8GlIMk5bLRXcZlfP3AnLUcSuFMu8C4MdOUxA8A==} + htmlparser2@6.1.0: resolution: {integrity: sha512-gyyPk6rgonLFEDGoeRgQNaEUvdJ4ktTmmUh/h2t7s+M8oPpIPxgNACWa+6ESR57kXstwqPiCut0V8NRpcwgU7A==} @@ -6908,6 +7204,9 @@ packages: json-schema-traverse@1.0.0: resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==} + json-schema-typed@8.0.2: + resolution: {integrity: sha512-fQhoXdcvc3V28x7C7BMs4P5+kNlgUURe2jmUT1T//oBRMDrqy1QPelJimwZGo7Hg9VPV3EQV5Bnq4hbFy2vetA==} + json-schema@0.4.0: resolution: {integrity: sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==} @@ -7214,6 +7513,10 @@ packages: resolution: {integrity: sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==} engines: {node: '>=10'} + log-symbols@7.0.1: + resolution: {integrity: sha512-ja1E3yCr9i/0hmBVaM0bfwDjnGy8I/s6PP4DFp+yP+a+mrHO4Rm7DtmnqROTUkHIkqffC84YY7AeqX6oFk0WFg==} + engines: {node: '>=18'} + log-update@6.1.0: resolution: {integrity: sha512-9ie8ItPR6tjY5uYJh8K/Zrv/RMZ5VOlOWvtZdEHYSTFKZfIBPQa9tOAEeAWhd+AnIneLJ22w5fjOYtoutpWq5w==} engines: {node: '>=18'} @@ -7405,6 +7708,9 @@ packages: mdast-util-to-string@4.0.0: resolution: {integrity: sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==} + mdn-data@2.27.1: + resolution: {integrity: sha512-9Yubnt3e8A0OKwxYSXyhLymGW4sCufcLG6VdiDdUGVkPhpqLxlvP5vl1983gQjJl3tqbrM731mjaZaP68AgosQ==} + mdurl@2.0.0: resolution: {integrity: sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==} @@ -7551,6 +7857,10 @@ packages: resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} engines: {node: '>= 0.6'} + mime-types@3.0.2: + resolution: {integrity: sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==} + engines: {node: '>=18'} + mimic-fn@2.1.0: resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==} engines: {node: '>=6'} @@ -7833,6 +8143,11 @@ packages: nth-check@2.1.1: resolution: {integrity: sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==} + nypm@0.6.6: + resolution: {integrity: sha512-vRyr0r4cbBapw07Xw8xrj9Teq3o7MUD35rSaTcanDbW+aK2XHDgJFiU6ZTj2GBw7Q12ysdsyFss+Vdz4hQ0Y6Q==} + engines: {node: '>=18'} + hasBin: true + oauth@0.10.2: resolution: {integrity: sha512-JtFnB+8nxDEXgNyniwz573xxbKSOu3R8D40xQKqcjwJ2CDkYqUDI53o6IuzDJBx60Z8VKCm271+t8iFjakrl8Q==} @@ -8057,6 +8372,9 @@ packages: resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} engines: {node: '>=8'} + pathe@2.0.3: + resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==} + pause@0.0.1: resolution: {integrity: sha512-KG8UEiEVkR3wGEb4m5yZkVCzigAD+cVEJck2CzYZO37ZGJfctvVptVO192MwrtPhzONn6go8ylnOdMhKqi4nfg==} @@ -8111,6 +8429,10 @@ packages: resolution: {integrity: sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==} engines: {node: '>=12'} + picospinner@3.1.2: + resolution: {integrity: sha512-0Z++uU8mvB0EvCPAEUq9BGiPcSravzJ+RPdXfjYlzXffqsogisiKRQqI6ctrSSJ6n985vxrgKtQ5n4sRINcJZg==} + engines: {node: '>=18.0.0'} + pify@4.0.1: resolution: {integrity: sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==} engines: {node: '>=6'} @@ -8406,6 +8728,14 @@ packages: peerDependencies: react: '>= 16.8 || 18.0.0' + react-email@6.9.2: + resolution: {integrity: sha512-A6SiRdH1U7qJcfgpaJ1BYGud8GbnPwDw61l3PP3JR1qMAmBgTlL02UuCPwPOClwc1x+saUU9OxJfPQHMNaTBMA==} + engines: {node: '>=20.0.0'} + hasBin: true + peerDependencies: + react: ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^18.0 || ^19.0 || ^19.0.0-rc + react-fast-compare@2.0.4: resolution: {integrity: sha512-suNP+J1VU1MWFKcyt7RtjiSWUjvidmQSlqu+eHslq+342xCbGTYmC0mEhPCOHxlW0CywylOC1u2DFAT+bv4dBw==} @@ -9040,6 +9370,17 @@ packages: resolution: {integrity: sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==} engines: {node: '>= 6.0.0', npm: '>= 3.0.0'} + socket.io-adapter@2.5.8: + resolution: {integrity: sha512-6Oy52pbg+kvdCVvjcN+FnY7BvxZ7cIHNScbvztT/It5d0vbwoJoVZmF2gjJmnV0/4WlXRfG15zc45ySk9Ah8bw==} + + socket.io-parser@4.2.7: + resolution: {integrity: sha512-IH/iSeO9T6gz1KkFleGDWkG9N3dl4jXVYUtMhIqH10Md0ttMer8nUNWiP1DKuNrybD2xBrixLJdCC9J6ECoYkg==} + engines: {node: '>=10.0.0'} + + socket.io@4.8.3: + resolution: {integrity: sha512-2Dd78bqzzjE6KPkD5fHZmDAKRNe3J15q+YHDrIsy9WEkqttc7GY+kT9OBLSMaPbQaEd0x1BjcmtMtXkfpc+T5A==} + engines: {node: '>=10.2.0'} + socks@2.8.8: resolution: {integrity: sha512-NlGELfPrgX2f1TAAcz0WawlLn+0r3FyhhCRpFFK2CemXenPYvzMWWZINv3eDNo9ucdwme7oCHRY0Jnbs4aIkog==} engines: {node: '>= 10.0.0', npm: '>= 3.0.0'} @@ -9228,6 +9569,12 @@ packages: resolution: {integrity: sha512-KIy5nylvC5le1OdaaoCJ07L+8iQzJHGH6pWDuzS+d07Cu7n1MZ2x26P8ZKIWfbK02+XIL8Mp4RkWeqdUCrDMfg==} engines: {node: '>=18'} + stubborn-fs@2.0.0: + resolution: {integrity: sha512-Y0AvSwDw8y+nlSNFXMm2g6L51rBGdAQT20J3YSOqxC53Lo3bjWRtr2BKcfYoAf352WYpsZSTURrA0tqhfgudPA==} + + stubborn-utils@1.0.2: + resolution: {integrity: sha512-zOh9jPYI+xrNOyisSelgym4tolKTJCQd5GBhK0+0xJvcYDcwlOoxF/rnFKQ2KRZknXSG9jWAp66fwP6AxN9STg==} + style-loader@3.3.4: resolution: {integrity: sha512-0WqXzrsMTyb8yjZJHDqwmnwRJvhALK9LfRtRc6B4UTWe8AijYLZYZ9thuJTZc2VfQWINADW/j+LiJnfy2RoC1w==} engines: {node: '>= 12.13.0'} @@ -9302,6 +9649,10 @@ packages: resolution: {integrity: sha512-7gr8p9TQP6RAHusBOSLs46F4564ZrjV8xFmw5zCmgmhGUcw2hxsShhJ6CEiHQMgPDwAQ1fWHPM0ypc4RMAig4A==} engines: {node: ^14.18.0 || >=16.0.0} + tagged-tag@1.0.0: + resolution: {integrity: sha512-yEFYrVhod+hdNyx7g5Bnkkb0G6si8HJurOoOEgC8B/O0uXLHlaey/65KRv6cuWBNhBgHKAROVpc7QyYqE5gFng==} + engines: {node: '>=20'} + tailwind-merge@3.5.0: resolution: {integrity: sha512-I8K9wewnVDkL1NTGoqWmVEIlUcB9gFriAEkXkfCjX5ib8ezGxtR3xD7iZIxrfArjEsH7F1CHD4RFUtxefdqV/A==} @@ -9382,6 +9733,10 @@ packages: resolution: {integrity: sha512-u9r3uZC0bdpGOXtlxUIdwf9pkmvhqJdrVCH9fapQtgy/OeTTMZ1nqH7agtvEfmGui6e1XxjcdrlxvxJvc3sMqw==} engines: {node: '>=18'} + tinyexec@1.3.0: + resolution: {integrity: sha512-QKAl9m8gWWGHV8jZcPeym6j+XULi6tOf1mT83WYJ4Lk2ytW/uwAWkrP0uFsdoYMdueVJ0qs26wZ+23xeB4ibNQ==} + engines: {node: '>=18'} + tinyglobby@0.2.15: resolution: {integrity: sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==} engines: {node: '>=12.0.0'} @@ -9447,6 +9802,10 @@ packages: trough@2.2.0: resolution: {integrity: sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw==} + tsconfig-paths@4.2.0: + resolution: {integrity: sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg==} + engines: {node: '>=6'} + tslib@1.14.1: resolution: {integrity: sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==} @@ -9524,6 +9883,10 @@ packages: resolution: {integrity: sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==} engines: {node: '>=16'} + type-fest@5.8.0: + resolution: {integrity: sha512-YGYEVz3Fm5iy/AybuA0oyNFq7H4CgQNfRp/qfe8nurE1kuCeNm3/vfm9X4Mtl+qLyaKJUh5xrFZwogr41SMjYA==} + engines: {node: '>=20'} + type-is@1.6.18: resolution: {integrity: sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==} engines: {node: '>= 0.6'} @@ -9857,6 +10220,9 @@ packages: whatwg-url@5.0.0: resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==} + when-exit@2.1.5: + resolution: {integrity: sha512-VGkKJ564kzt6Ms1dbgPP/yuIoQCrsFAnRbptpC5wOEsDaNsbCB2bnfnaA8i/vRs5tjUSEOtIuvl9/MyVsvQZCg==} + which-pm@2.0.0: resolution: {integrity: sha512-Lhs9Pmyph0p5n5Z3mVnN0yWcbQYUAD7rbQUiMsQxOJ3T57k7RFe35SUwWMf7dsbDZks1uOmw4AecB/JMDj3v/w==} engines: {node: '>=8.15'} @@ -9931,6 +10297,18 @@ packages: utf-8-validate: optional: true + ws@8.21.3: + resolution: {integrity: sha512-201TZ/kPWxoPr/OKWjquZR1SWKXcvxdH+e1xrx89b3YbmzLMFCLfnaG1HFIgWzJOEWZ7MvpK++odZufgYR50Rw==} + 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 + xdg-app-paths@8.3.0: resolution: {integrity: sha512-mgxlWVZw0TNWHoGmXq+NC3uhCIc55dDpAlDkMQUaIAcQzysb0kxctwv//fvuW61/nAAeUBJMQ8mnZjMmuYwOcQ==} engines: {node: '>= 4.0'} @@ -10198,6 +10576,12 @@ snapshots: js-tokens: 4.0.0 picocolors: 1.1.1 + '@babel/code-frame@7.29.7': + dependencies: + '@babel/helper-validator-identifier': 7.29.7 + js-tokens: 4.0.0 + picocolors: 1.1.1 + '@babel/compat-data@7.28.5': {} '@babel/core@7.28.5': @@ -10228,6 +10612,14 @@ snapshots: '@jridgewell/trace-mapping': 0.3.31 jsesc: 3.1.0 + '@babel/generator@7.29.8': + dependencies: + '@babel/parser': 7.29.8 + '@babel/types': 7.29.8 + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.31 + jsesc: 3.1.0 + '@babel/helper-annotate-as-pure@7.27.3': dependencies: '@babel/types': 7.28.5 @@ -10302,8 +10694,12 @@ snapshots: '@babel/helper-string-parser@7.27.1': {} + '@babel/helper-string-parser@7.29.7': {} + '@babel/helper-validator-identifier@7.28.5': {} + '@babel/helper-validator-identifier@7.29.7': {} + '@babel/helper-validator-option@7.27.1': {} '@babel/helpers@7.28.4': @@ -10315,6 +10711,14 @@ snapshots: dependencies: '@babel/types': 7.28.5 + '@babel/parser@7.29.2': + dependencies: + '@babel/types': 7.29.8 + + '@babel/parser@7.29.8': + dependencies: + '@babel/types': 7.29.8 + '@babel/plugin-syntax-flow@7.27.1(@babel/core@7.28.5)': dependencies: '@babel/core': 7.28.5 @@ -10419,6 +10823,12 @@ snapshots: '@babel/parser': 7.28.5 '@babel/types': 7.28.5 + '@babel/template@7.29.7': + dependencies: + '@babel/code-frame': 7.29.7 + '@babel/parser': 7.29.8 + '@babel/types': 7.29.8 + '@babel/traverse@7.28.5': dependencies: '@babel/code-frame': 7.27.1 @@ -10431,20 +10841,39 @@ snapshots: transitivePeerDependencies: - supports-color + '@babel/traverse@7.29.0': + dependencies: + '@babel/code-frame': 7.29.7 + '@babel/generator': 7.29.8 + '@babel/helper-globals': 7.28.0 + '@babel/parser': 7.29.2 + '@babel/template': 7.29.7 + '@babel/types': 7.29.8 + debug: 4.4.3(supports-color@5.5.0) + transitivePeerDependencies: + - supports-color + '@babel/types@7.28.5': dependencies: '@babel/helper-string-parser': 7.27.1 '@babel/helper-validator-identifier': 7.28.5 + '@babel/types@7.29.8': + dependencies: + '@babel/helper-string-parser': 7.29.7 + '@babel/helper-validator-identifier': 7.29.7 + '@better-auth-ui/core@1.6.3(better-auth@1.6.9(@opentelemetry/api@1.9.0)(better-sqlite3@12.4.1)(mongodb@7.1.0(socks@2.8.8))(next@16.0.10(@babel/core@7.28.5)(@opentelemetry/api@1.9.0)(@playwright/test@1.57.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(pg@8.21.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))': dependencies: better-auth: 1.6.9(@opentelemetry/api@1.9.0)(better-sqlite3@12.4.1)(mongodb@7.1.0(socks@2.8.8))(next@16.0.10(@babel/core@7.28.5)(@opentelemetry/api@1.9.0)(@playwright/test@1.57.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(pg@8.21.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@better-auth-ui/react@1.6.3(0d1590071f28a5ba08bcd1840e03194f)': + '@better-auth-ui/react@1.6.44(a201276b8466b095739dcdb4fdb7f42d)': dependencies: '@better-auth-ui/core': 1.6.3(better-auth@1.6.9(@opentelemetry/api@1.9.0)(better-sqlite3@12.4.1)(mongodb@7.1.0(socks@2.8.8))(next@16.0.10(@babel/core@7.28.5)(@opentelemetry/api@1.9.0)(@playwright/test@1.57.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(pg@8.21.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)) + '@better-auth/api-key': 1.6.9(@better-auth/core@1.6.9(@better-auth/utils@0.4.0)(@better-fetch/fetch@1.1.21)(@opentelemetry/api@1.9.0)(better-call@1.3.5(zod@4.3.6))(jose@6.1.3)(kysely@0.28.14)(nanostores@1.2.0))(@better-auth/utils@0.4.0)(better-auth@1.6.9(@opentelemetry/api@1.9.0)(better-sqlite3@12.4.1)(mongodb@7.1.0(socks@2.8.8))(next@16.0.10(@babel/core@7.28.5)(@opentelemetry/api@1.9.0)(@playwright/test@1.57.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(pg@8.21.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)) + '@better-auth/oauth-provider': 1.6.26(@better-auth/core@1.6.9(@better-auth/utils@0.4.0)(@better-fetch/fetch@1.1.21)(@opentelemetry/api@1.9.0)(better-call@1.3.5(zod@4.3.6))(jose@6.1.3)(kysely@0.28.14)(nanostores@1.2.0))(@better-auth/utils@0.4.0)(@better-fetch/fetch@1.1.21)(better-auth@1.6.9(@opentelemetry/api@1.9.0)(better-sqlite3@12.4.1)(mongodb@7.1.0(socks@2.8.8))(next@16.0.10(@babel/core@7.28.5)(@opentelemetry/api@1.9.0)(@playwright/test@1.57.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(pg@8.21.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(better-call@1.3.5(zod@4.3.6)) '@better-auth/passkey': 1.6.9(@better-auth/core@1.6.9(@better-auth/utils@0.4.0)(@better-fetch/fetch@1.1.21)(@opentelemetry/api@1.9.0)(better-call@1.3.5(zod@4.3.6))(jose@6.1.3)(kysely@0.28.14)(nanostores@1.2.0))(@better-auth/utils@0.4.0)(@better-fetch/fetch@1.1.21)(better-auth@1.6.9(@opentelemetry/api@1.9.0)(better-sqlite3@12.4.1)(mongodb@7.1.0(socks@2.8.8))(next@16.0.10(@babel/core@7.28.5)(@opentelemetry/api@1.9.0)(@playwright/test@1.57.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(pg@8.21.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(better-call@1.3.5(zod@4.3.6))(nanostores@1.2.0) - '@react-email/components': 1.0.12(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@tanstack/query-core': 5.100.6 '@tanstack/react-query': 5.100.6(react@18.3.1) better-auth: 1.6.9(@opentelemetry/api@1.9.0)(better-sqlite3@12.4.1)(mongodb@7.1.0(socks@2.8.8))(next@16.0.10(@babel/core@7.28.5)(@opentelemetry/api@1.9.0)(@playwright/test@1.57.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(pg@8.21.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) clsx: 2.1.1 @@ -10453,6 +10882,15 @@ snapshots: tailwind-merge: 3.5.0 tailwindcss: 4.2.1 zod: 4.3.6 + optionalDependencies: + react-email: 6.9.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + + '@better-auth/api-key@1.6.9(@better-auth/core@1.6.9(@better-auth/utils@0.4.0)(@better-fetch/fetch@1.1.21)(@opentelemetry/api@1.9.0)(better-call@1.3.5(zod@4.3.6))(jose@6.1.3)(kysely@0.28.14)(nanostores@1.2.0))(@better-auth/utils@0.4.0)(better-auth@1.6.9(@opentelemetry/api@1.9.0)(better-sqlite3@12.4.1)(mongodb@7.1.0(socks@2.8.8))(next@16.0.10(@babel/core@7.28.5)(@opentelemetry/api@1.9.0)(@playwright/test@1.57.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(pg@8.21.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))': + dependencies: + '@better-auth/core': 1.6.9(@better-auth/utils@0.4.0)(@better-fetch/fetch@1.1.21)(@opentelemetry/api@1.9.0)(better-call@1.3.5(zod@4.3.6))(jose@6.1.3)(kysely@0.28.14)(nanostores@1.2.0) + '@better-auth/utils': 0.4.0 + better-auth: 1.6.9(@opentelemetry/api@1.9.0)(better-sqlite3@12.4.1)(mongodb@7.1.0(socks@2.8.8))(next@16.0.10(@babel/core@7.28.5)(@opentelemetry/api@1.9.0)(@playwright/test@1.57.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(pg@8.21.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + zod: 4.3.6 '@better-auth/api-key@1.6.9(@better-auth/core@1.6.9(@better-auth/utils@0.4.0)(@better-fetch/fetch@1.1.21)(@opentelemetry/api@1.9.0)(better-call@2.0.2(zod@4.3.6))(jose@6.1.3)(kysely@0.28.14)(nanostores@1.2.0))(@better-auth/utils@0.4.0)(better-auth@1.6.9(@opentelemetry/api@1.9.0)(better-sqlite3@12.4.1)(mongodb@7.1.0(socks@2.8.8))(next@16.0.10(@opentelemetry/api@1.9.0)(@playwright/test@1.57.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(pg@8.21.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))': dependencies: @@ -10548,6 +10986,16 @@ snapshots: optionalDependencies: mongodb: 7.1.0(socks@2.8.8) + '@better-auth/oauth-provider@1.6.26(@better-auth/core@1.6.9(@better-auth/utils@0.4.0)(@better-fetch/fetch@1.1.21)(@opentelemetry/api@1.9.0)(better-call@1.3.5(zod@4.3.6))(jose@6.1.3)(kysely@0.28.14)(nanostores@1.2.0))(@better-auth/utils@0.4.0)(@better-fetch/fetch@1.1.21)(better-auth@1.6.9(@opentelemetry/api@1.9.0)(better-sqlite3@12.4.1)(mongodb@7.1.0(socks@2.8.8))(next@16.0.10(@babel/core@7.28.5)(@opentelemetry/api@1.9.0)(@playwright/test@1.57.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(pg@8.21.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(better-call@1.3.5(zod@4.3.6))': + dependencies: + '@better-auth/core': 1.6.9(@better-auth/utils@0.4.0)(@better-fetch/fetch@1.1.21)(@opentelemetry/api@1.9.0)(better-call@1.3.5(zod@4.3.6))(jose@6.1.3)(kysely@0.28.14)(nanostores@1.2.0) + '@better-auth/utils': 0.4.0 + '@better-fetch/fetch': 1.1.21 + better-auth: 1.6.9(@opentelemetry/api@1.9.0)(better-sqlite3@12.4.1)(mongodb@7.1.0(socks@2.8.8))(next@16.0.10(@babel/core@7.28.5)(@opentelemetry/api@1.9.0)(@playwright/test@1.57.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(pg@8.21.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + better-call: 1.3.5(zod@4.3.6) + jose: 6.1.3 + zod: 4.3.6 + '@better-auth/passkey@1.6.9(@better-auth/core@1.6.9(@better-auth/utils@0.4.0)(@better-fetch/fetch@1.1.21)(@opentelemetry/api@1.9.0)(better-call@1.3.5(zod@4.3.6))(jose@6.1.3)(kysely@0.28.14)(nanostores@1.2.0))(@better-auth/utils@0.4.0)(@better-fetch/fetch@1.1.21)(better-auth@1.6.9(@opentelemetry/api@1.9.0)(better-sqlite3@12.4.1)(mongodb@7.1.0(socks@2.8.8))(next@16.0.10(@babel/core@7.28.5)(@opentelemetry/api@1.9.0)(@playwright/test@1.57.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(pg@8.21.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(better-call@1.3.5(zod@4.3.6))(nanostores@1.2.0)': dependencies: '@better-auth/core': 1.6.9(@better-auth/utils@0.4.0)(@better-fetch/fetch@1.1.21)(@opentelemetry/api@1.9.0)(better-call@1.3.5(zod@4.3.6))(jose@6.1.3)(kysely@0.28.14)(nanostores@1.2.0) @@ -10949,6 +11397,9 @@ snapshots: '@esbuild/aix-ppc64@0.27.7': optional: true + '@esbuild/aix-ppc64@0.28.2': + optional: true + '@esbuild/android-arm64@0.20.2': optional: true @@ -10958,6 +11409,9 @@ snapshots: '@esbuild/android-arm64@0.27.7': optional: true + '@esbuild/android-arm64@0.28.2': + optional: true + '@esbuild/android-arm@0.20.2': optional: true @@ -10967,6 +11421,9 @@ snapshots: '@esbuild/android-arm@0.27.7': optional: true + '@esbuild/android-arm@0.28.2': + optional: true + '@esbuild/android-x64@0.20.2': optional: true @@ -10976,6 +11433,9 @@ snapshots: '@esbuild/android-x64@0.27.7': optional: true + '@esbuild/android-x64@0.28.2': + optional: true + '@esbuild/darwin-arm64@0.20.2': optional: true @@ -10985,6 +11445,9 @@ snapshots: '@esbuild/darwin-arm64@0.27.7': optional: true + '@esbuild/darwin-arm64@0.28.2': + optional: true + '@esbuild/darwin-x64@0.20.2': optional: true @@ -10994,6 +11457,9 @@ snapshots: '@esbuild/darwin-x64@0.27.7': optional: true + '@esbuild/darwin-x64@0.28.2': + optional: true + '@esbuild/freebsd-arm64@0.20.2': optional: true @@ -11003,6 +11469,9 @@ snapshots: '@esbuild/freebsd-arm64@0.27.7': optional: true + '@esbuild/freebsd-arm64@0.28.2': + optional: true + '@esbuild/freebsd-x64@0.20.2': optional: true @@ -11012,6 +11481,9 @@ snapshots: '@esbuild/freebsd-x64@0.27.7': optional: true + '@esbuild/freebsd-x64@0.28.2': + optional: true + '@esbuild/linux-arm64@0.20.2': optional: true @@ -11021,6 +11493,9 @@ snapshots: '@esbuild/linux-arm64@0.27.7': optional: true + '@esbuild/linux-arm64@0.28.2': + optional: true + '@esbuild/linux-arm@0.20.2': optional: true @@ -11030,6 +11505,9 @@ snapshots: '@esbuild/linux-arm@0.27.7': optional: true + '@esbuild/linux-arm@0.28.2': + optional: true + '@esbuild/linux-ia32@0.20.2': optional: true @@ -11039,6 +11517,9 @@ snapshots: '@esbuild/linux-ia32@0.27.7': optional: true + '@esbuild/linux-ia32@0.28.2': + optional: true + '@esbuild/linux-loong64@0.20.2': optional: true @@ -11048,6 +11529,9 @@ snapshots: '@esbuild/linux-loong64@0.27.7': optional: true + '@esbuild/linux-loong64@0.28.2': + optional: true + '@esbuild/linux-mips64el@0.20.2': optional: true @@ -11057,6 +11541,9 @@ snapshots: '@esbuild/linux-mips64el@0.27.7': optional: true + '@esbuild/linux-mips64el@0.28.2': + optional: true + '@esbuild/linux-ppc64@0.20.2': optional: true @@ -11066,6 +11553,9 @@ snapshots: '@esbuild/linux-ppc64@0.27.7': optional: true + '@esbuild/linux-ppc64@0.28.2': + optional: true + '@esbuild/linux-riscv64@0.20.2': optional: true @@ -11075,6 +11565,9 @@ snapshots: '@esbuild/linux-riscv64@0.27.7': optional: true + '@esbuild/linux-riscv64@0.28.2': + optional: true + '@esbuild/linux-s390x@0.20.2': optional: true @@ -11084,6 +11577,9 @@ snapshots: '@esbuild/linux-s390x@0.27.7': optional: true + '@esbuild/linux-s390x@0.28.2': + optional: true + '@esbuild/linux-x64@0.20.2': optional: true @@ -11093,9 +11589,15 @@ snapshots: '@esbuild/linux-x64@0.27.7': optional: true + '@esbuild/linux-x64@0.28.2': + optional: true + '@esbuild/netbsd-arm64@0.27.7': optional: true + '@esbuild/netbsd-arm64@0.28.2': + optional: true + '@esbuild/netbsd-x64@0.20.2': optional: true @@ -11105,9 +11607,15 @@ snapshots: '@esbuild/netbsd-x64@0.27.7': optional: true + '@esbuild/netbsd-x64@0.28.2': + optional: true + '@esbuild/openbsd-arm64@0.27.7': optional: true + '@esbuild/openbsd-arm64@0.28.2': + optional: true + '@esbuild/openbsd-x64@0.20.2': optional: true @@ -11117,9 +11625,15 @@ snapshots: '@esbuild/openbsd-x64@0.27.7': optional: true + '@esbuild/openbsd-x64@0.28.2': + optional: true + '@esbuild/openharmony-arm64@0.27.7': optional: true + '@esbuild/openharmony-arm64@0.28.2': + optional: true + '@esbuild/sunos-x64@0.20.2': optional: true @@ -11129,6 +11643,9 @@ snapshots: '@esbuild/sunos-x64@0.27.7': optional: true + '@esbuild/sunos-x64@0.28.2': + optional: true + '@esbuild/win32-arm64@0.20.2': optional: true @@ -11138,6 +11655,9 @@ snapshots: '@esbuild/win32-arm64@0.27.7': optional: true + '@esbuild/win32-arm64@0.28.2': + optional: true + '@esbuild/win32-ia32@0.20.2': optional: true @@ -11147,6 +11667,9 @@ snapshots: '@esbuild/win32-ia32@0.27.7': optional: true + '@esbuild/win32-ia32@0.28.2': + optional: true + '@esbuild/win32-x64@0.20.2': optional: true @@ -11156,6 +11679,9 @@ snapshots: '@esbuild/win32-x64@0.27.7': optional: true + '@esbuild/win32-x64@0.28.2': + optional: true + '@floating-ui/core@1.7.3': dependencies: '@floating-ui/utils': 0.2.10 @@ -11884,7 +12410,7 @@ snapshots: playwright: 1.57.0 optional: true - '@pmmmwh/react-refresh-webpack-plugin@0.5.15(react-refresh@0.14.0)(type-fest@4.41.0)(webpack-hot-middleware@2.26.1)(webpack@5.103.0(esbuild@0.27.7))': + '@pmmmwh/react-refresh-webpack-plugin@0.5.15(react-refresh@0.14.0)(type-fest@4.41.0)(webpack-hot-middleware@2.26.1)(webpack@5.103.0(esbuild@0.28.2))': dependencies: ansi-html: 0.0.9 core-js-pure: 3.47.0 @@ -11894,7 +12420,7 @@ snapshots: react-refresh: 0.14.0 schema-utils: 4.3.3 source-map: 0.7.6 - webpack: 5.103.0(esbuild@0.27.7) + webpack: 5.103.0(esbuild@0.28.2) optionalDependencies: type-fest: 4.41.0 webpack-hot-middleware: 2.26.1 @@ -13384,6 +13910,15 @@ snapshots: react: 18.3.1 react-dom: 18.3.1(react@18.3.1) + '@react-email/render@2.1.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + entities: 4.5.0 + html-to-text: 9.0.5 + html5parser: 3.0.0 + prettier: 3.8.3 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + '@react-email/row@0.0.13(react@18.3.1)': dependencies: react: 18.3.1 @@ -13640,17 +14175,19 @@ snapshots: color: 5.0.3 text-hex: 1.0.0 + '@socket.io/component-emitter@3.1.2': {} + '@standard-schema/spec@1.1.0': {} '@standard-schema/utils@0.3.0': {} - '@strapi-community/plugin-better-auth-dashboard@1.0.0-alpha.10(247722706193c89f065cf703f728c040)': + '@strapi-community/plugin-better-auth-dashboard@1.0.0-alpha.10(b1927daabe7dd0388ec030704dc1c13f)': dependencies: '@better-auth/infra': 0.2.5(f3203897ca24aa1d7482643411714120) - '@strapi-community/plugin-better-auth': 1.0.0-beta.6(932d618de66d66b770f4bd334c84b400) + '@strapi-community/plugin-better-auth': 1.0.0-beta.6(fd35780beaa7c8b8b6f2868050a30a3d) '@strapi/design-system': 2.2.0(@babel/runtime@7.28.4)(@codemirror/autocomplete@6.20.0)(@codemirror/language@6.11.3)(@codemirror/lint@6.9.2)(@codemirror/search@6.5.11)(@codemirror/state@6.5.2)(@codemirror/theme-one-dark@6.1.3)(@codemirror/view@6.39.4)(@strapi/icons@2.2.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(styled-components@6.1.19(react-dom@18.3.1(react@18.3.1))(react@18.3.1)))(@types/react-dom@18.3.7(@types/react@18.3.27))(@types/react@18.3.27)(codemirror@5.65.20)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(styled-components@6.1.19(react-dom@18.3.1(react@18.3.1))(react@18.3.1)) '@strapi/icons': 2.2.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(styled-components@6.1.19(react-dom@18.3.1(react@18.3.1))(react@18.3.1)) - '@strapi/strapi': 5.45.0(@babel/runtime@7.28.4)(@codemirror/autocomplete@6.20.0)(@codemirror/language@6.11.3)(@codemirror/lint@6.9.2)(@codemirror/search@6.5.11)(@codemirror/state@6.5.2)(@codemirror/theme-one-dark@6.1.3)(@codemirror/view@6.39.4)(@emotion/is-prop-valid@1.2.2)(@swc/helpers@0.5.18)(@types/hoist-non-react-statics@3.3.7(@types/react@18.3.27))(@types/node@20.19.27)(@types/react-dom@18.3.7(@types/react@18.3.27))(@types/react@18.3.27)(better-sqlite3@12.4.1)(codemirror@5.65.20)(esbuild@0.27.7)(koa@2.16.4)(lightningcss@1.31.1)(pg@8.21.0)(react-dom@18.3.1(react@18.3.1))(react-router-dom@6.30.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1)(redis@4.6.14)(redux@4.2.1)(styled-components@6.1.19(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(terser@5.44.1)(type-fest@4.41.0) + '@strapi/strapi': 5.45.0(@babel/runtime@7.28.4)(@codemirror/autocomplete@6.20.0)(@codemirror/language@6.11.3)(@codemirror/lint@6.9.2)(@codemirror/search@6.5.11)(@codemirror/state@6.5.2)(@codemirror/theme-one-dark@6.1.3)(@codemirror/view@6.39.4)(@emotion/is-prop-valid@1.2.2)(@swc/helpers@0.5.18)(@types/hoist-non-react-statics@3.3.7(@types/react@18.3.27))(@types/node@20.19.27)(@types/react-dom@18.3.7(@types/react@18.3.27))(@types/react@18.3.27)(better-sqlite3@12.4.1)(codemirror@5.65.20)(esbuild@0.28.2)(koa@2.16.4)(lightningcss@1.31.1)(pg@8.21.0)(react-dom@18.3.1(react@18.3.1))(react-router-dom@6.30.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1)(redis@4.6.14)(redux@4.2.1)(styled-components@6.1.19(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(terser@5.44.1)(type-fest@4.41.0) '@strapi/types': 5.45.0(@types/node@20.19.27)(better-sqlite3@12.4.1)(pg@8.21.0)(typescript@5.9.2) '@strapi/utils': 5.45.0 better-auth: 1.6.9(@opentelemetry/api@1.9.0)(better-sqlite3@12.4.1)(mongodb@7.1.0(socks@2.8.8))(next@16.0.10(@babel/core@7.28.5)(@opentelemetry/api@1.9.0)(@playwright/test@1.57.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(pg@8.21.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) @@ -13661,9 +14198,9 @@ snapshots: react-router-dom: 6.30.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1) styled-components: 6.1.19(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@strapi-community/plugin-better-auth@1.0.0-beta.6(932d618de66d66b770f4bd334c84b400)': + '@strapi-community/plugin-better-auth@1.0.0-beta.6(fd35780beaa7c8b8b6f2868050a30a3d)': dependencies: - '@strapi/strapi': 5.45.0(@babel/runtime@7.28.4)(@codemirror/autocomplete@6.20.0)(@codemirror/language@6.11.3)(@codemirror/lint@6.9.2)(@codemirror/search@6.5.11)(@codemirror/state@6.5.2)(@codemirror/theme-one-dark@6.1.3)(@codemirror/view@6.39.4)(@emotion/is-prop-valid@1.2.2)(@swc/helpers@0.5.18)(@types/hoist-non-react-statics@3.3.7(@types/react@18.3.27))(@types/node@20.19.27)(@types/react-dom@18.3.7(@types/react@18.3.27))(@types/react@18.3.27)(better-sqlite3@12.4.1)(codemirror@5.65.20)(esbuild@0.27.7)(koa@2.16.4)(lightningcss@1.31.1)(pg@8.21.0)(react-dom@18.3.1(react@18.3.1))(react-router-dom@6.30.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1)(redis@4.6.14)(redux@4.2.1)(styled-components@6.1.19(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(terser@5.44.1)(type-fest@4.41.0) + '@strapi/strapi': 5.45.0(@babel/runtime@7.28.4)(@codemirror/autocomplete@6.20.0)(@codemirror/language@6.11.3)(@codemirror/lint@6.9.2)(@codemirror/search@6.5.11)(@codemirror/state@6.5.2)(@codemirror/theme-one-dark@6.1.3)(@codemirror/view@6.39.4)(@emotion/is-prop-valid@1.2.2)(@swc/helpers@0.5.18)(@types/hoist-non-react-statics@3.3.7(@types/react@18.3.27))(@types/node@20.19.27)(@types/react-dom@18.3.7(@types/react@18.3.27))(@types/react@18.3.27)(better-sqlite3@12.4.1)(codemirror@5.65.20)(esbuild@0.28.2)(koa@2.16.4)(lightningcss@1.31.1)(pg@8.21.0)(react-dom@18.3.1(react@18.3.1))(react-router-dom@6.30.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1)(redis@4.6.14)(redux@4.2.1)(styled-components@6.1.19(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(terser@5.44.1)(type-fest@4.41.0) '@strapi/types': 5.45.0(@types/node@20.19.27)(better-sqlite3@12.4.1)(pg@8.21.0)(typescript@5.9.2) '@strapi/utils': 5.45.0 better-auth: 1.6.9(@opentelemetry/api@1.9.0)(better-sqlite3@12.4.1)(mongodb@7.1.0(socks@2.8.8))(next@16.0.10(@babel/core@7.28.5)(@opentelemetry/api@1.9.0)(@playwright/test@1.57.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(pg@8.21.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) @@ -14398,12 +14935,12 @@ snapshots: qs: 6.15.0 sift: 16.0.1 - '@strapi/plugin-cloud@5.45.0(770eb9469b475171bba33fb37a36eebf)': + '@strapi/plugin-cloud@5.45.0(ff33188423ab4254570ec0cdf36587d8)': dependencies: '@strapi/admin': 5.45.0(@babel/runtime@7.28.4)(@codemirror/autocomplete@6.20.0)(@codemirror/language@6.11.3)(@codemirror/lint@6.9.2)(@codemirror/search@6.5.11)(@codemirror/state@6.5.2)(@codemirror/theme-one-dark@6.1.3)(@codemirror/view@6.39.4)(@emotion/is-prop-valid@1.2.2)(@strapi/data-transfer@5.45.0(@types/node@20.19.27)(better-sqlite3@12.4.1)(pg@8.21.0)(typescript@5.9.2))(@types/hoist-non-react-statics@3.3.7(@types/react@18.3.27))(@types/node@20.19.27)(@types/react-dom@18.3.7(@types/react@18.3.27))(@types/react@18.3.27)(better-sqlite3@12.4.1)(codemirror@5.65.20)(debug@4.3.4)(pg@8.21.0)(react-dom@18.3.1(react@18.3.1))(react-router-dom@6.30.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1)(redis@4.6.14)(redux@4.2.1)(styled-components@6.1.19(react-dom@18.3.1(react@18.3.1))(react@18.3.1)) '@strapi/design-system': 2.2.0(@babel/runtime@7.28.4)(@codemirror/autocomplete@6.20.0)(@codemirror/language@6.11.3)(@codemirror/lint@6.9.2)(@codemirror/search@6.5.11)(@codemirror/state@6.5.2)(@codemirror/theme-one-dark@6.1.3)(@codemirror/view@6.39.4)(@strapi/icons@2.2.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(styled-components@6.1.19(react-dom@18.3.1(react@18.3.1))(react@18.3.1)))(@types/react-dom@18.3.7(@types/react@18.3.27))(@types/react@18.3.27)(codemirror@5.65.20)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(styled-components@6.1.19(react-dom@18.3.1(react@18.3.1))(react@18.3.1)) '@strapi/icons': 2.2.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(styled-components@6.1.19(react-dom@18.3.1(react@18.3.1))(react@18.3.1)) - '@strapi/strapi': 5.45.0(@babel/runtime@7.28.4)(@codemirror/autocomplete@6.20.0)(@codemirror/language@6.11.3)(@codemirror/lint@6.9.2)(@codemirror/search@6.5.11)(@codemirror/state@6.5.2)(@codemirror/theme-one-dark@6.1.3)(@codemirror/view@6.39.4)(@emotion/is-prop-valid@1.2.2)(@swc/helpers@0.5.18)(@types/hoist-non-react-statics@3.3.7(@types/react@18.3.27))(@types/node@20.19.27)(@types/react-dom@18.3.7(@types/react@18.3.27))(@types/react@18.3.27)(better-sqlite3@12.4.1)(codemirror@5.65.20)(esbuild@0.27.7)(koa@2.16.4)(lightningcss@1.31.1)(pg@8.21.0)(react-dom@18.3.1(react@18.3.1))(react-router-dom@6.30.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1)(redis@4.6.14)(redux@4.2.1)(styled-components@6.1.19(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(terser@5.44.1)(type-fest@4.41.0) + '@strapi/strapi': 5.45.0(@babel/runtime@7.28.4)(@codemirror/autocomplete@6.20.0)(@codemirror/language@6.11.3)(@codemirror/lint@6.9.2)(@codemirror/search@6.5.11)(@codemirror/state@6.5.2)(@codemirror/theme-one-dark@6.1.3)(@codemirror/view@6.39.4)(@emotion/is-prop-valid@1.2.2)(@swc/helpers@0.5.18)(@types/hoist-non-react-statics@3.3.7(@types/react@18.3.27))(@types/node@20.19.27)(@types/react-dom@18.3.7(@types/react@18.3.27))(@types/react@18.3.27)(better-sqlite3@12.4.1)(codemirror@5.65.20)(esbuild@0.28.2)(koa@2.16.4)(lightningcss@1.31.1)(pg@8.21.0)(react-dom@18.3.1(react@18.3.1))(react-router-dom@6.30.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1)(redis@4.6.14)(redux@4.2.1)(styled-components@6.1.19(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(terser@5.44.1)(type-fest@4.41.0) react: 18.3.1 react-dom: 18.3.1(react@18.3.1) react-intl: 6.6.2(react@18.3.1)(typescript@5.9.2) @@ -14508,9 +15045,9 @@ snapshots: - supports-color - terser - '@strapi/strapi@5.45.0(@babel/runtime@7.28.4)(@codemirror/autocomplete@6.20.0)(@codemirror/language@6.11.3)(@codemirror/lint@6.9.2)(@codemirror/search@6.5.11)(@codemirror/state@6.5.2)(@codemirror/theme-one-dark@6.1.3)(@codemirror/view@6.39.4)(@emotion/is-prop-valid@1.2.2)(@swc/helpers@0.5.18)(@types/hoist-non-react-statics@3.3.7(@types/react@18.3.27))(@types/node@20.19.27)(@types/react-dom@18.3.7(@types/react@18.3.27))(@types/react@18.3.27)(better-sqlite3@12.4.1)(codemirror@5.65.20)(esbuild@0.27.7)(koa@2.16.4)(lightningcss@1.31.1)(pg@8.21.0)(react-dom@18.3.1(react@18.3.1))(react-router-dom@6.30.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1)(redis@4.6.14)(redux@4.2.1)(styled-components@6.1.19(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(terser@5.44.1)(type-fest@4.41.0)': + '@strapi/strapi@5.45.0(@babel/runtime@7.28.4)(@codemirror/autocomplete@6.20.0)(@codemirror/language@6.11.3)(@codemirror/lint@6.9.2)(@codemirror/search@6.5.11)(@codemirror/state@6.5.2)(@codemirror/theme-one-dark@6.1.3)(@codemirror/view@6.39.4)(@emotion/is-prop-valid@1.2.2)(@swc/helpers@0.5.18)(@types/hoist-non-react-statics@3.3.7(@types/react@18.3.27))(@types/node@20.19.27)(@types/react-dom@18.3.7(@types/react@18.3.27))(@types/react@18.3.27)(better-sqlite3@12.4.1)(codemirror@5.65.20)(esbuild@0.28.2)(koa@2.16.4)(lightningcss@1.31.1)(pg@8.21.0)(react-dom@18.3.1(react@18.3.1))(react-router-dom@6.30.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1)(redis@4.6.14)(redux@4.2.1)(styled-components@6.1.19(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(terser@5.44.1)(type-fest@4.41.0)': dependencies: - '@pmmmwh/react-refresh-webpack-plugin': 0.5.15(react-refresh@0.14.0)(type-fest@4.41.0)(webpack-hot-middleware@2.26.1)(webpack@5.103.0(esbuild@0.27.7)) + '@pmmmwh/react-refresh-webpack-plugin': 0.5.15(react-refresh@0.14.0)(type-fest@4.41.0)(webpack-hot-middleware@2.26.1)(webpack@5.103.0(esbuild@0.28.2)) '@strapi/admin': 5.45.0(@babel/runtime@7.28.4)(@codemirror/autocomplete@6.20.0)(@codemirror/language@6.11.3)(@codemirror/lint@6.9.2)(@codemirror/search@6.5.11)(@codemirror/state@6.5.2)(@codemirror/theme-one-dark@6.1.3)(@codemirror/view@6.39.4)(@emotion/is-prop-valid@1.2.2)(@strapi/data-transfer@5.45.0(@types/node@20.19.27)(better-sqlite3@12.4.1)(pg@8.21.0)(typescript@5.9.2))(@types/hoist-non-react-statics@3.3.7(@types/react@18.3.27))(@types/node@20.19.27)(@types/react-dom@18.3.7(@types/react@18.3.27))(@types/react@18.3.27)(better-sqlite3@12.4.1)(codemirror@5.65.20)(debug@4.3.4)(pg@8.21.0)(react-dom@18.3.1(react@18.3.1))(react-router-dom@6.30.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1)(redis@4.6.14)(redux@4.2.1)(styled-components@6.1.19(react-dom@18.3.1(react@18.3.1))(react@18.3.1)) '@strapi/cloud-cli': 5.45.0(@types/node@20.19.27) '@strapi/content-manager': 5.45.0(a4fc555cb26c098a44655682e5e0152b) @@ -14542,19 +15079,19 @@ snapshots: cli-table3: 0.6.5 commander: 8.3.0 concurrently: 8.2.2 - css-loader: 6.11.0(webpack@5.103.0(esbuild@0.27.7)) + css-loader: 6.11.0(webpack@5.103.0(esbuild@0.28.2)) dotenv: 16.6.1 - esbuild-loader: 4.4.3(webpack@5.103.0(esbuild@0.27.7)) - esbuild-register: 3.6.0(esbuild@0.27.7) + esbuild-loader: 4.4.3(webpack@5.103.0(esbuild@0.28.2)) + esbuild-register: 3.6.0(esbuild@0.28.2) execa: 5.1.1 - fork-ts-checker-webpack-plugin: 8.0.0(typescript@5.4.5)(webpack@5.103.0(esbuild@0.27.7)) + fork-ts-checker-webpack-plugin: 8.0.0(typescript@5.4.5)(webpack@5.103.0(esbuild@0.28.2)) fs-extra: 11.3.4 get-latest-version: 5.1.0 git-url-parse: 14.0.0 - html-webpack-plugin: 5.6.0(webpack@5.103.0(esbuild@0.27.7)) + html-webpack-plugin: 5.6.0(webpack@5.103.0(esbuild@0.28.2)) inquirer: 9.3.8(@types/node@20.19.27) lodash: 4.18.1 - mini-css-extract-plugin: 2.7.7(webpack@5.103.0(esbuild@0.27.7)) + mini-css-extract-plugin: 2.7.7(webpack@5.103.0(esbuild@0.28.2)) nodemon: 3.0.2 ora: 5.4.1 outdent: 0.8.0 @@ -14567,13 +15104,13 @@ snapshots: read-pkg-up: 7.0.1 resolve-from: 5.0.0 semver: 7.7.4 - style-loader: 3.3.4(webpack@5.103.0(esbuild@0.27.7)) + style-loader: 3.3.4(webpack@5.103.0(esbuild@0.28.2)) styled-components: 6.1.19(react-dom@18.3.1(react@18.3.1))(react@18.3.1) typescript: 5.4.5 vite: 5.4.21(@types/node@20.19.27)(lightningcss@1.31.1)(terser@5.44.1) - webpack: 5.103.0(esbuild@0.27.7) + webpack: 5.103.0(esbuild@0.28.2) webpack-bundle-analyzer: 4.10.2 - webpack-dev-middleware: 6.1.2(webpack@5.103.0(esbuild@0.27.7)) + webpack-dev-middleware: 6.1.2(webpack@5.103.0(esbuild@0.28.2)) webpack-hot-middleware: 2.26.1 yup: 0.32.9 transitivePeerDependencies: @@ -15124,6 +15661,10 @@ snapshots: '@types/keygrip': 1.0.6 '@types/node': 22.15.3 + '@types/cors@2.8.19': + dependencies: + '@types/node': 22.15.3 + '@types/debug@4.1.12': dependencies: '@types/ms': 2.1.0 @@ -15373,6 +15914,10 @@ snapshots: '@types/webidl-conversions': 7.0.3 optional: true + '@types/ws@8.18.1': + dependencies: + '@types/node': 22.15.3 + '@ucast/core@1.10.2': {} '@ucast/js@3.0.4': @@ -15604,6 +16149,10 @@ snapshots: optionalDependencies: ajv: 8.13.0 + ajv-formats@3.0.1(ajv@8.18.0): + optionalDependencies: + ajv: 8.18.0 + ajv-keywords@3.5.2(ajv@6.12.6): dependencies: ajv: 6.12.6 @@ -15737,6 +16286,11 @@ snapshots: asynckit@0.4.0: {} + atomically@2.1.1: + dependencies: + stubborn-fs: 2.0.0 + when-exit: 2.1.5 + attr-accept@2.2.5: {} axios@1.15.1(debug@4.3.4): @@ -15761,6 +16315,8 @@ snapshots: base64-js@1.5.1: {} + base64id@2.0.0: {} + base64url@3.0.1: {} baseline-browser-mapping@2.9.7: {} @@ -16025,6 +16581,10 @@ snapshots: dependencies: readdirp: 4.1.2 + chokidar@4.0.3: + dependencies: + readdirp: 4.1.2 + chownr@1.1.4: {} chownr@3.0.0: {} @@ -16033,6 +16593,8 @@ snapshots: ci-info@4.0.0: {} + citty@0.2.2: {} + class-variance-authority@0.7.1: dependencies: clsx: 2.1.1 @@ -16156,6 +16718,8 @@ snapshots: commander@12.1.0: {} + commander@13.1.0: {} + commander@14.0.3: {} commander@2.20.3: {} @@ -16186,6 +16750,18 @@ snapshots: tree-kill: 1.2.2 yargs: 17.7.2 + conf@15.1.0: + dependencies: + ajv: 8.18.0 + ajv-formats: 3.0.1(ajv@8.18.0) + atomically: 2.1.1 + debounce-fn: 6.0.0 + dot-prop: 10.2.0 + env-paths: 3.0.0 + json-schema-typed: 8.0.2 + semver: 7.7.4 + uint8array-extras: 1.5.0 + config-chain@1.1.13: dependencies: ini: 1.3.8 @@ -16210,6 +16786,8 @@ snapshots: convert-source-map@2.0.0: {} + cookie@0.7.2: {} + cookies@0.9.1: dependencies: depd: 2.0.0 @@ -16229,6 +16807,11 @@ snapshots: core-util-is@1.0.3: {} + cors@2.8.6: + dependencies: + object-assign: 4.1.1 + vary: 1.1.2 + cosmiconfig@7.1.0: dependencies: '@types/parse-json': 4.0.2 @@ -16263,7 +16846,7 @@ snapshots: css-color-keywords@1.0.0: {} - css-loader@6.11.0(webpack@5.103.0(esbuild@0.27.7)): + css-loader@6.11.0(webpack@5.103.0(esbuild@0.28.2)): dependencies: icss-utils: 5.1.0(postcss@8.5.6) postcss: 8.5.6 @@ -16274,7 +16857,7 @@ snapshots: postcss-value-parser: 4.2.0 semver: 7.7.4 optionalDependencies: - webpack: 5.103.0(esbuild@0.27.7) + webpack: 5.103.0(esbuild@0.28.2) css-select@4.3.0: dependencies: @@ -16290,6 +16873,11 @@ snapshots: css-color-keywords: 1.0.0 postcss-value-parser: 4.2.0 + css-tree@3.2.1: + dependencies: + mdn-data: 2.27.1 + source-map-js: 1.2.1 + css-what@6.2.2: {} cssesc@3.0.0: {} @@ -16310,8 +16898,14 @@ snapshots: dependencies: '@babel/runtime': 7.28.4 + debounce-fn@6.0.0: + dependencies: + mimic-function: 5.0.1 + debounce@1.2.1: {} + debounce@2.2.0: {} + debug@3.2.7: dependencies: ms: 2.1.3 @@ -16475,6 +17069,10 @@ snapshots: no-case: 3.0.4 tslib: 2.8.1 + dot-prop@10.2.0: + dependencies: + type-fest: 5.8.0 + dot-prop@5.3.0: dependencies: is-obj: 2.0.0 @@ -16515,6 +17113,25 @@ snapshots: dependencies: once: 1.4.0 + engine.io-parser@5.2.3: {} + + engine.io@6.6.9: + dependencies: + '@types/cors': 2.8.19 + '@types/node': 22.15.3 + '@types/ws': 8.18.1 + accepts: 1.3.8 + base64id: 2.0.0 + cookie: 0.7.2 + cors: 2.8.6 + debug: 4.4.3(supports-color@5.5.0) + engine.io-parser: 5.2.3 + ws: 8.21.3 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + enhanced-resolve@5.20.0: dependencies: graceful-fs: 4.2.11 @@ -16526,6 +17143,8 @@ snapshots: entities@6.0.1: {} + env-paths@3.0.0: {} + environment@1.1.0: {} error-ex@1.3.4: @@ -16553,12 +17172,12 @@ snapshots: has-tostringtag: 1.0.2 hasown: 2.0.2 - esbuild-loader@4.4.3(webpack@5.103.0(esbuild@0.27.7)): + esbuild-loader@4.4.3(webpack@5.103.0(esbuild@0.28.2)): dependencies: esbuild: 0.27.7 get-tsconfig: 4.13.0 loader-utils: 2.0.4 - webpack: 5.103.0(esbuild@0.27.7) + webpack: 5.103.0(esbuild@0.28.2) webpack-sources: 3.4.1 esbuild-register@3.5.0(esbuild@0.20.2): @@ -16568,10 +17187,10 @@ snapshots: transitivePeerDependencies: - supports-color - esbuild-register@3.6.0(esbuild@0.27.7): + esbuild-register@3.6.0(esbuild@0.28.2): dependencies: debug: 4.4.3(supports-color@5.5.0) - esbuild: 0.27.7 + esbuild: 0.28.2 transitivePeerDependencies: - supports-color @@ -16656,6 +17275,35 @@ snapshots: '@esbuild/win32-ia32': 0.27.7 '@esbuild/win32-x64': 0.27.7 + esbuild@0.28.2: + optionalDependencies: + '@esbuild/aix-ppc64': 0.28.2 + '@esbuild/android-arm': 0.28.2 + '@esbuild/android-arm64': 0.28.2 + '@esbuild/android-x64': 0.28.2 + '@esbuild/darwin-arm64': 0.28.2 + '@esbuild/darwin-x64': 0.28.2 + '@esbuild/freebsd-arm64': 0.28.2 + '@esbuild/freebsd-x64': 0.28.2 + '@esbuild/linux-arm': 0.28.2 + '@esbuild/linux-arm64': 0.28.2 + '@esbuild/linux-ia32': 0.28.2 + '@esbuild/linux-loong64': 0.28.2 + '@esbuild/linux-mips64el': 0.28.2 + '@esbuild/linux-ppc64': 0.28.2 + '@esbuild/linux-riscv64': 0.28.2 + '@esbuild/linux-s390x': 0.28.2 + '@esbuild/linux-x64': 0.28.2 + '@esbuild/netbsd-arm64': 0.28.2 + '@esbuild/netbsd-x64': 0.28.2 + '@esbuild/openbsd-arm64': 0.28.2 + '@esbuild/openbsd-x64': 0.28.2 + '@esbuild/openharmony-arm64': 0.28.2 + '@esbuild/sunos-x64': 0.28.2 + '@esbuild/win32-arm64': 0.28.2 + '@esbuild/win32-ia32': 0.28.2 + '@esbuild/win32-x64': 0.28.2 + escalade@3.2.0: {} escape-html@1.0.3: {} @@ -16858,7 +17506,7 @@ snapshots: dependencies: for-in: 1.0.2 - fork-ts-checker-webpack-plugin@8.0.0(typescript@5.4.5)(webpack@5.103.0(esbuild@0.27.7)): + fork-ts-checker-webpack-plugin@8.0.0(typescript@5.4.5)(webpack@5.103.0(esbuild@0.28.2)): dependencies: '@babel/code-frame': 7.27.1 chalk: 4.1.2 @@ -16873,7 +17521,7 @@ snapshots: semver: 7.7.4 tapable: 2.3.0 typescript: 5.4.5 - webpack: 5.103.0(esbuild@0.27.7) + webpack: 5.103.0(esbuild@0.28.2) form-data@4.0.5: dependencies: @@ -17286,7 +17934,7 @@ snapshots: html-void-elements@3.0.0: {} - html-webpack-plugin@5.6.0(webpack@5.103.0(esbuild@0.27.7)): + html-webpack-plugin@5.6.0(webpack@5.103.0(esbuild@0.28.2)): dependencies: '@types/html-minifier-terser': 6.1.0 html-minifier-terser: 6.1.0 @@ -17294,7 +17942,9 @@ snapshots: pretty-error: 4.0.0 tapable: 2.3.0 optionalDependencies: - webpack: 5.103.0(esbuild@0.27.7) + webpack: 5.103.0(esbuild@0.28.2) + + html5parser@3.0.0: {} htmlparser2@6.1.0: dependencies: @@ -17690,6 +18340,8 @@ snapshots: json-schema-traverse@1.0.0: {} + json-schema-typed@8.0.2: {} + json-schema@0.4.0: {} json5@2.2.3: {} @@ -18035,6 +18687,11 @@ snapshots: chalk: 4.1.2 is-unicode-supported: 0.1.0 + log-symbols@7.0.1: + dependencies: + is-unicode-supported: 2.1.0 + yoctocolors: 2.1.2 + log-update@6.1.0: dependencies: ansi-escapes: 7.3.0 @@ -18321,6 +18978,8 @@ snapshots: dependencies: '@types/mdast': 4.0.4 + mdn-data@2.27.1: {} + mdurl@2.0.0: {} media-chrome@4.2.3: {} @@ -18556,6 +19215,10 @@ snapshots: dependencies: mime-db: 1.52.0 + mime-types@3.0.2: + dependencies: + mime-db: 1.54.0 + mimic-fn@2.1.0: {} mimic-function@5.0.1: {} @@ -18564,10 +19227,10 @@ snapshots: mimic-response@3.1.0: {} - mini-css-extract-plugin@2.7.7(webpack@5.103.0(esbuild@0.27.7)): + mini-css-extract-plugin@2.7.7(webpack@5.103.0(esbuild@0.28.2)): dependencies: schema-utils: 4.3.3 - webpack: 5.103.0(esbuild@0.27.7) + webpack: 5.103.0(esbuild@0.28.2) minimatch@10.2.5: dependencies: @@ -18817,6 +19480,12 @@ snapshots: dependencies: boolbase: 1.0.0 + nypm@0.6.6: + dependencies: + citty: 0.2.2 + pathe: 2.0.3 + tinyexec: 1.3.0 + oauth@0.10.2: {} object-assign@4.1.1: {} @@ -19042,6 +19711,8 @@ snapshots: path-type@4.0.0: {} + pathe@2.0.3: {} + pause@0.0.1: {} peberminta@0.9.0: {} @@ -19089,6 +19760,8 @@ snapshots: picomatch@4.0.3: {} + picospinner@3.1.2: {} + pify@4.0.1: {} pirates@4.0.7: {} @@ -19428,6 +20101,37 @@ snapshots: prop-types: 15.8.1 react: 18.3.1 + react-email@6.9.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + '@babel/parser': 7.29.2 + '@babel/traverse': 7.29.0 + '@react-email/render': 2.1.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + chokidar: 4.0.3 + commander: 13.1.0 + conf: 15.1.0 + css-tree: 3.2.1 + debounce: 2.2.0 + esbuild: 0.28.2 + glob: 13.0.6 + jiti: 2.6.1 + log-symbols: 7.0.1 + marked: 15.0.12 + mime-types: 3.0.2 + normalize-path: 3.0.0 + nypm: 0.6.6 + picospinner: 3.1.2 + prismjs: 1.30.0 + prompts: 2.4.2 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + socket.io: 4.8.3 + tailwindcss: 4.2.1 + tsconfig-paths: 4.2.0 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + react-fast-compare@2.0.4: {} react-fast-compare@3.2.2: {} @@ -20281,6 +20985,36 @@ snapshots: smart-buffer@4.2.0: optional: true + socket.io-adapter@2.5.8: + dependencies: + debug: 4.4.3(supports-color@5.5.0) + ws: 8.21.3 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + + socket.io-parser@4.2.7: + dependencies: + '@socket.io/component-emitter': 3.1.2 + debug: 4.4.3(supports-color@5.5.0) + transitivePeerDependencies: + - supports-color + + socket.io@4.8.3: + dependencies: + accepts: 1.3.8 + base64id: 2.0.0 + cors: 2.8.6 + debug: 4.4.3(supports-color@5.5.0) + engine.io: 6.6.9 + socket.io-adapter: 2.5.8 + socket.io-parser: 4.2.7 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + socks@2.8.8: dependencies: ip-address: 10.1.1 @@ -20359,12 +21093,12 @@ snapshots: statuses@2.0.2: {} - strapi-lucide-icons@1.14.0(9df38cc1293a58a6c42c61569eb73f43): + strapi-lucide-icons@1.14.0(b8b74674fc9c2814fa92b14e60149852): dependencies: '@strapi/design-system': 2.2.0(@babel/runtime@7.28.4)(@codemirror/autocomplete@6.20.0)(@codemirror/language@6.11.3)(@codemirror/lint@6.9.2)(@codemirror/search@6.5.11)(@codemirror/state@6.5.2)(@codemirror/theme-one-dark@6.1.3)(@codemirror/view@6.39.4)(@strapi/icons@2.2.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(styled-components@6.1.19(react-dom@18.3.1(react@18.3.1))(react@18.3.1)))(@types/react-dom@18.3.7(@types/react@18.3.27))(@types/react@18.3.27)(codemirror@5.65.20)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(styled-components@6.1.19(react-dom@18.3.1(react@18.3.1))(react@18.3.1)) '@strapi/icons': 2.2.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(styled-components@6.1.19(react-dom@18.3.1(react@18.3.1))(react@18.3.1)) '@strapi/sdk-plugin': 5.4.0(@swc/helpers@0.5.18)(@types/node@20.19.27)(lightningcss@1.31.1)(terser@5.44.1) - '@strapi/strapi': 5.45.0(@babel/runtime@7.28.4)(@codemirror/autocomplete@6.20.0)(@codemirror/language@6.11.3)(@codemirror/lint@6.9.2)(@codemirror/search@6.5.11)(@codemirror/state@6.5.2)(@codemirror/theme-one-dark@6.1.3)(@codemirror/view@6.39.4)(@emotion/is-prop-valid@1.2.2)(@swc/helpers@0.5.18)(@types/hoist-non-react-statics@3.3.7(@types/react@18.3.27))(@types/node@20.19.27)(@types/react-dom@18.3.7(@types/react@18.3.27))(@types/react@18.3.27)(better-sqlite3@12.4.1)(codemirror@5.65.20)(esbuild@0.27.7)(koa@2.16.4)(lightningcss@1.31.1)(pg@8.21.0)(react-dom@18.3.1(react@18.3.1))(react-router-dom@6.30.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1)(redis@4.6.14)(redux@4.2.1)(styled-components@6.1.19(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(terser@5.44.1)(type-fest@4.41.0) + '@strapi/strapi': 5.45.0(@babel/runtime@7.28.4)(@codemirror/autocomplete@6.20.0)(@codemirror/language@6.11.3)(@codemirror/lint@6.9.2)(@codemirror/search@6.5.11)(@codemirror/state@6.5.2)(@codemirror/theme-one-dark@6.1.3)(@codemirror/view@6.39.4)(@emotion/is-prop-valid@1.2.2)(@swc/helpers@0.5.18)(@types/hoist-non-react-statics@3.3.7(@types/react@18.3.27))(@types/node@20.19.27)(@types/react-dom@18.3.7(@types/react@18.3.27))(@types/react@18.3.27)(better-sqlite3@12.4.1)(codemirror@5.65.20)(esbuild@0.28.2)(koa@2.16.4)(lightningcss@1.31.1)(pg@8.21.0)(react-dom@18.3.1(react@18.3.1))(react-router-dom@6.30.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1)(redis@4.6.14)(redux@4.2.1)(styled-components@6.1.19(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(terser@5.44.1)(type-fest@4.41.0) lucide-react: 1.14.0(react@18.3.1) react: 18.3.1 react-dom: 18.3.1(react@18.3.1) @@ -20385,12 +21119,12 @@ snapshots: - codemirror - typescript - strapi-plugin-meilisearch@0.15.0(f77b4f63add7dbf7d09216c98e78916e): + strapi-plugin-meilisearch@0.15.0(bcc84c0dccc5675451bed4707160a510): dependencies: '@strapi/design-system': 2.2.0(@babel/runtime@7.28.4)(@codemirror/autocomplete@6.20.0)(@codemirror/language@6.11.3)(@codemirror/lint@6.9.2)(@codemirror/search@6.5.11)(@codemirror/state@6.5.2)(@codemirror/theme-one-dark@6.1.3)(@codemirror/view@6.39.4)(@strapi/icons@2.2.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(styled-components@6.1.19(react-dom@18.3.1(react@18.3.1))(react@18.3.1)))(@types/react-dom@18.3.7(@types/react@18.3.27))(@types/react@18.3.27)(codemirror@5.65.20)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(styled-components@6.1.19(react-dom@18.3.1(react@18.3.1))(react@18.3.1)) '@strapi/icons': 2.2.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(styled-components@6.1.19(react-dom@18.3.1(react@18.3.1))(react@18.3.1)) '@strapi/sdk-plugin': 5.4.0(@swc/helpers@0.5.18)(@types/node@20.19.27)(lightningcss@1.31.1)(terser@5.44.1) - '@strapi/strapi': 5.45.0(@babel/runtime@7.28.4)(@codemirror/autocomplete@6.20.0)(@codemirror/language@6.11.3)(@codemirror/lint@6.9.2)(@codemirror/search@6.5.11)(@codemirror/state@6.5.2)(@codemirror/theme-one-dark@6.1.3)(@codemirror/view@6.39.4)(@emotion/is-prop-valid@1.2.2)(@swc/helpers@0.5.18)(@types/hoist-non-react-statics@3.3.7(@types/react@18.3.27))(@types/node@20.19.27)(@types/react-dom@18.3.7(@types/react@18.3.27))(@types/react@18.3.27)(better-sqlite3@12.4.1)(codemirror@5.65.20)(esbuild@0.27.7)(koa@2.16.4)(lightningcss@1.31.1)(pg@8.21.0)(react-dom@18.3.1(react@18.3.1))(react-router-dom@6.30.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1)(redis@4.6.14)(redux@4.2.1)(styled-components@6.1.19(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(terser@5.44.1)(type-fest@4.41.0) + '@strapi/strapi': 5.45.0(@babel/runtime@7.28.4)(@codemirror/autocomplete@6.20.0)(@codemirror/language@6.11.3)(@codemirror/lint@6.9.2)(@codemirror/search@6.5.11)(@codemirror/state@6.5.2)(@codemirror/theme-one-dark@6.1.3)(@codemirror/view@6.39.4)(@emotion/is-prop-valid@1.2.2)(@swc/helpers@0.5.18)(@types/hoist-non-react-statics@3.3.7(@types/react@18.3.27))(@types/node@20.19.27)(@types/react-dom@18.3.7(@types/react@18.3.27))(@types/react@18.3.27)(better-sqlite3@12.4.1)(codemirror@5.65.20)(esbuild@0.28.2)(koa@2.16.4)(lightningcss@1.31.1)(pg@8.21.0)(react-dom@18.3.1(react@18.3.1))(react-router-dom@6.30.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1)(redis@4.6.14)(redux@4.2.1)(styled-components@6.1.19(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(terser@5.44.1)(type-fest@4.41.0) meilisearch: 0.48.2 react: 18.3.1 react-dom: 18.3.1(react@18.3.1) @@ -20409,12 +21143,12 @@ snapshots: - '@types/react-dom' - codemirror - strapi-plugin-webtools@1.10.3(9d2b8bd2d0baeb82b3b54609990dbcb0): + strapi-plugin-webtools@1.10.3(c8cc467db588f2291b44f319065bfcfb): dependencies: '@strapi/admin': 5.45.0(@babel/runtime@7.28.4)(@codemirror/autocomplete@6.20.0)(@codemirror/language@6.11.3)(@codemirror/lint@6.9.2)(@codemirror/search@6.5.11)(@codemirror/state@6.5.2)(@codemirror/theme-one-dark@6.1.3)(@codemirror/view@6.39.4)(@emotion/is-prop-valid@1.2.2)(@strapi/data-transfer@5.45.0(@types/node@20.19.27)(better-sqlite3@12.4.1)(pg@8.21.0)(typescript@5.9.2))(@types/hoist-non-react-statics@3.3.7(@types/react@18.3.27))(@types/node@20.19.27)(@types/react-dom@18.3.7(@types/react@18.3.27))(@types/react@18.3.27)(better-sqlite3@12.4.1)(codemirror@5.65.20)(debug@4.3.4)(pg@8.21.0)(react-dom@18.3.1(react@18.3.1))(react-router-dom@6.30.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1)(redis@4.6.14)(redux@4.2.1)(styled-components@6.1.19(react-dom@18.3.1(react@18.3.1))(react@18.3.1)) '@strapi/design-system': 2.2.0(@babel/runtime@7.28.4)(@codemirror/autocomplete@6.20.0)(@codemirror/language@6.11.3)(@codemirror/lint@6.9.2)(@codemirror/search@6.5.11)(@codemirror/state@6.5.2)(@codemirror/theme-one-dark@6.1.3)(@codemirror/view@6.39.4)(@strapi/icons@2.2.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(styled-components@6.1.19(react-dom@18.3.1(react@18.3.1))(react@18.3.1)))(@types/react-dom@18.3.7(@types/react@18.3.27))(@types/react@18.3.27)(codemirror@5.65.20)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(styled-components@6.1.19(react-dom@18.3.1(react@18.3.1))(react@18.3.1)) '@strapi/icons': 2.2.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(styled-components@6.1.19(react-dom@18.3.1(react@18.3.1))(react@18.3.1)) - '@strapi/strapi': 5.45.0(@babel/runtime@7.28.4)(@codemirror/autocomplete@6.20.0)(@codemirror/language@6.11.3)(@codemirror/lint@6.9.2)(@codemirror/search@6.5.11)(@codemirror/state@6.5.2)(@codemirror/theme-one-dark@6.1.3)(@codemirror/view@6.39.4)(@emotion/is-prop-valid@1.2.2)(@swc/helpers@0.5.18)(@types/hoist-non-react-statics@3.3.7(@types/react@18.3.27))(@types/node@20.19.27)(@types/react-dom@18.3.7(@types/react@18.3.27))(@types/react@18.3.27)(better-sqlite3@12.4.1)(codemirror@5.65.20)(esbuild@0.27.7)(koa@2.16.4)(lightningcss@1.31.1)(pg@8.21.0)(react-dom@18.3.1(react@18.3.1))(react-router-dom@6.30.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1)(redis@4.6.14)(redux@4.2.1)(styled-components@6.1.19(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(terser@5.44.1)(type-fest@4.41.0) + '@strapi/strapi': 5.45.0(@babel/runtime@7.28.4)(@codemirror/autocomplete@6.20.0)(@codemirror/language@6.11.3)(@codemirror/lint@6.9.2)(@codemirror/search@6.5.11)(@codemirror/state@6.5.2)(@codemirror/theme-one-dark@6.1.3)(@codemirror/view@6.39.4)(@emotion/is-prop-valid@1.2.2)(@swc/helpers@0.5.18)(@types/hoist-non-react-statics@3.3.7(@types/react@18.3.27))(@types/node@20.19.27)(@types/react-dom@18.3.7(@types/react@18.3.27))(@types/react@18.3.27)(better-sqlite3@12.4.1)(codemirror@5.65.20)(esbuild@0.28.2)(koa@2.16.4)(lightningcss@1.31.1)(pg@8.21.0)(react-dom@18.3.1(react@18.3.1))(react-router-dom@6.30.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1)(redis@4.6.14)(redux@4.2.1)(styled-components@6.1.19(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(terser@5.44.1)(type-fest@4.41.0) '@strapi/utils': 5.45.0 formik: 2.4.5(@types/react@18.3.27)(react@18.3.1) lodash: 4.18.1 @@ -20494,9 +21228,15 @@ snapshots: dependencies: '@tokenizer/token': 0.3.0 - style-loader@3.3.4(webpack@5.103.0(esbuild@0.27.7)): + stubborn-fs@2.0.0: + dependencies: + stubborn-utils: 1.0.2 + + stubborn-utils@1.0.2: {} + + style-loader@3.3.4(webpack@5.103.0(esbuild@0.28.2)): dependencies: - webpack: 5.103.0(esbuild@0.27.7) + webpack: 5.103.0(esbuild@0.28.2) style-mod@4.1.3: {} @@ -20564,6 +21304,8 @@ snapshots: '@pkgr/core': 0.1.2 tslib: 2.8.1 + tagged-tag@1.0.0: {} + tailwind-merge@3.5.0: {} tailwindcss@4.2.1: {} @@ -20595,16 +21337,16 @@ snapshots: tarn@3.0.2: {} - terser-webpack-plugin@5.3.16(esbuild@0.27.7)(webpack@5.103.0(esbuild@0.27.7)): + terser-webpack-plugin@5.3.16(esbuild@0.28.2)(webpack@5.103.0(esbuild@0.28.2)): dependencies: '@jridgewell/trace-mapping': 0.3.31 jest-worker: 27.5.1 schema-utils: 4.3.3 serialize-javascript: 6.0.2 terser: 5.44.1 - webpack: 5.103.0(esbuild@0.27.7) + webpack: 5.103.0(esbuild@0.28.2) optionalDependencies: - esbuild: 0.27.7 + esbuild: 0.28.2 terser@5.44.1: dependencies: @@ -20639,6 +21381,8 @@ snapshots: tinyexec@1.0.4: {} + tinyexec@1.3.0: {} + tinyglobby@0.2.15: dependencies: fdir: 6.5.0(picomatch@4.0.3) @@ -20689,6 +21433,12 @@ snapshots: trough@2.2.0: {} + tsconfig-paths@4.2.0: + dependencies: + json5: 2.2.3 + minimist: 1.2.8 + strip-bom: 3.0.0 + tslib@1.14.1: {} tslib@2.6.2: {} @@ -20744,6 +21494,10 @@ snapshots: type-fest@4.41.0: {} + type-fest@5.8.0: + dependencies: + tagged-tag: 1.0.0 + type-is@1.6.18: dependencies: media-typer: 0.3.0 @@ -21030,7 +21784,7 @@ snapshots: - bufferutil - utf-8-validate - webpack-dev-middleware@6.1.2(webpack@5.103.0(esbuild@0.27.7)): + webpack-dev-middleware@6.1.2(webpack@5.103.0(esbuild@0.28.2)): dependencies: colorette: 2.0.20 memfs: 3.5.3 @@ -21038,7 +21792,7 @@ snapshots: range-parser: 1.2.1 schema-utils: 4.3.3 optionalDependencies: - webpack: 5.103.0(esbuild@0.27.7) + webpack: 5.103.0(esbuild@0.28.2) webpack-hot-middleware@2.26.1: dependencies: @@ -21048,7 +21802,7 @@ snapshots: webpack-sources@3.4.1: {} - webpack@5.103.0(esbuild@0.27.7): + webpack@5.103.0(esbuild@0.28.2): dependencies: '@types/eslint-scope': 3.7.7 '@types/estree': 1.0.8 @@ -21072,7 +21826,7 @@ snapshots: neo-async: 2.6.2 schema-utils: 4.3.3 tapable: 2.3.0 - terser-webpack-plugin: 5.3.16(esbuild@0.27.7)(webpack@5.103.0(esbuild@0.27.7)) + terser-webpack-plugin: 5.3.16(esbuild@0.28.2)(webpack@5.103.0(esbuild@0.28.2)) watchpack: 2.4.4 webpack-sources: 3.4.1 transitivePeerDependencies: @@ -21080,11 +21834,11 @@ snapshots: - esbuild - uglify-js - webtools-addon-sitemap@1.3.1(2645048d954e5d7b65a3189de32cdcc5): + webtools-addon-sitemap@1.3.1(794233b068737205e9cbed44c2ce006f): dependencies: '@strapi/design-system': 2.2.0(@babel/runtime@7.28.4)(@codemirror/autocomplete@6.20.0)(@codemirror/language@6.11.3)(@codemirror/lint@6.9.2)(@codemirror/search@6.5.11)(@codemirror/state@6.5.2)(@codemirror/theme-one-dark@6.1.3)(@codemirror/view@6.39.4)(@strapi/icons@2.2.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(styled-components@6.1.19(react-dom@18.3.1(react@18.3.1))(react@18.3.1)))(@types/react-dom@18.3.7(@types/react@18.3.27))(@types/react@18.3.27)(codemirror@5.65.20)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(styled-components@6.1.19(react-dom@18.3.1(react@18.3.1))(react@18.3.1)) '@strapi/icons': 2.2.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(styled-components@6.1.19(react-dom@18.3.1(react@18.3.1))(react@18.3.1)) - '@strapi/strapi': 5.45.0(@babel/runtime@7.28.4)(@codemirror/autocomplete@6.20.0)(@codemirror/language@6.11.3)(@codemirror/lint@6.9.2)(@codemirror/search@6.5.11)(@codemirror/state@6.5.2)(@codemirror/theme-one-dark@6.1.3)(@codemirror/view@6.39.4)(@emotion/is-prop-valid@1.2.2)(@swc/helpers@0.5.18)(@types/hoist-non-react-statics@3.3.7(@types/react@18.3.27))(@types/node@20.19.27)(@types/react-dom@18.3.7(@types/react@18.3.27))(@types/react@18.3.27)(better-sqlite3@12.4.1)(codemirror@5.65.20)(esbuild@0.27.7)(koa@2.16.4)(lightningcss@1.31.1)(pg@8.21.0)(react-dom@18.3.1(react@18.3.1))(react-router-dom@6.30.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1)(redis@4.6.14)(redux@4.2.1)(styled-components@6.1.19(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(terser@5.44.1)(type-fest@4.41.0) + '@strapi/strapi': 5.45.0(@babel/runtime@7.28.4)(@codemirror/autocomplete@6.20.0)(@codemirror/language@6.11.3)(@codemirror/lint@6.9.2)(@codemirror/search@6.5.11)(@codemirror/state@6.5.2)(@codemirror/theme-one-dark@6.1.3)(@codemirror/view@6.39.4)(@emotion/is-prop-valid@1.2.2)(@swc/helpers@0.5.18)(@types/hoist-non-react-statics@3.3.7(@types/react@18.3.27))(@types/node@20.19.27)(@types/react-dom@18.3.7(@types/react@18.3.27))(@types/react@18.3.27)(better-sqlite3@12.4.1)(codemirror@5.65.20)(esbuild@0.28.2)(koa@2.16.4)(lightningcss@1.31.1)(pg@8.21.0)(react-dom@18.3.1(react@18.3.1))(react-router-dom@6.30.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1)(redis@4.6.14)(redux@4.2.1)(styled-components@6.1.19(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(terser@5.44.1)(type-fest@4.41.0) '@strapi/utils': 5.45.0 chalk: 4.1.2 commander: 8.3.0 @@ -21099,7 +21853,7 @@ snapshots: redux-immutable: 4.0.0(immutable@3.8.3) redux-thunk: 2.4.2(redux@4.2.1) sitemap: 7.1.3 - strapi-plugin-webtools: 1.10.3(9d2b8bd2d0baeb82b3b54609990dbcb0) + strapi-plugin-webtools: 1.10.3(c8cc467db588f2291b44f319065bfcfb) styled-components: 6.1.19(react-dom@18.3.1(react@18.3.1))(react@18.3.1) xml2js: 0.5.0 transitivePeerDependencies: @@ -21119,6 +21873,8 @@ snapshots: tr46: 0.0.3 webidl-conversions: 3.0.1 + when-exit@2.1.5: {} + which-pm@2.0.0: dependencies: load-yaml-file: 0.2.0 @@ -21194,6 +21950,8 @@ snapshots: ws@8.17.1: {} + ws@8.21.3: {} + xdg-app-paths@8.3.0: dependencies: xdg-portable: 10.6.0 From 5944eaba6a818d9e0a93caa680abda01e7ee0694 Mon Sep 17 00:00:00 2001 From: Boaz Poolman Date: Mon, 10 Aug 2026 16:05:42 +0200 Subject: [PATCH 04/62] feat: organization management --- .../better-auth/controllers/organization.ts | 77 ++++++- .../policies/is-organization-owner.ts | 32 +++ .../better-auth/routes/organization.ts | 40 ++++ .../better-auth/services/organization.ts | 90 ++++++++ .../extensions/better-auth/strapi-server.ts | 2 + .../src/extensions/better-auth/utils/index.ts | 14 ++ .../src/app/org/[slug]/[[...path]]/page.tsx | 24 +- .../components/organization-profile-card.tsx | 189 ++++++++++++++++ .../auth/components/organization-view.tsx | 205 +++++++++++++++++- .../features/auth/lib/organization-logo.ts | 55 +++++ .../features/auth/lib/organization-profile.ts | 53 +++++ .../features/auth/lib/use-auth-ui-props.tsx | 8 + 12 files changed, 786 insertions(+), 3 deletions(-) create mode 100644 apps/cms/src/extensions/better-auth/policies/is-organization-owner.ts create mode 100644 apps/web/src/features/auth/components/organization-profile-card.tsx create mode 100644 apps/web/src/features/auth/lib/organization-logo.ts create mode 100644 apps/web/src/features/auth/lib/organization-profile.ts diff --git a/apps/cms/src/extensions/better-auth/controllers/organization.ts b/apps/cms/src/extensions/better-auth/controllers/organization.ts index 8ecefd0e..17fd4e99 100644 --- a/apps/cms/src/extensions/better-auth/controllers/organization.ts +++ b/apps/cms/src/extensions/better-auth/controllers/organization.ts @@ -3,7 +3,9 @@ */ import { factories } from "@strapi/strapi"; -import { getPluginService } from "../utils"; +import { getPluginService, organizationProfileFields } from "../utils"; + +const MAX_LOGO_SIZE = 2 * 1024 * 1024; export default factories.createCoreController( "plugin::better-auth.organization", @@ -40,5 +42,78 @@ export default factories.createCoreController( ctx.body = members; }, + + /** + * PUT /organizations/:id/profile — used by the web app's organization + * "Profile" tab. Gated by the `is-authenticated` and + * `is-organization-owner` policies. + */ + async updateProfile(ctx) { + const { id } = ctx.params; + const body = ctx.request.body ?? {}; + + const data = Object.fromEntries( + organizationProfileFields + .filter((field) => field in body) + .map((field) => [field, body[field]]), + ); + + const service = getPluginService("organization"); + + const profile = await service.updateProfile({ + organizationId: id, + data, + }); + + ctx.body = profile; + }, + + /** + * POST /organizations/logo — used by better-auth-ui's + * `organization.logo.upload` config. Gated by the `is-authenticated` + * policy; not organization-scoped, since better-auth-ui doesn't tell + * us which org the logo is for until the follow-up + * `organization.update` call (which better-auth itself already + * restricts to owners/admins of that org). + */ + async uploadLogo(ctx) { + const session = ctx.state.betterAuthSession; + + const uploaded = ctx.request.files?.file; + const file = Array.isArray(uploaded) ? uploaded[0] : uploaded; + if (!file) return ctx.badRequest("No file provided."); + if (!file.mimetype?.startsWith("image/")) { + return ctx.badRequest("Logo must be an image."); + } + if (file.size > MAX_LOGO_SIZE) { + return ctx.badRequest("Logo must be smaller than 2 MB."); + } + + const uploadedFile = await getPluginService("organization").uploadLogo({ + userId: session.user.id, + file, + }); + + ctx.body = { url: uploadedFile.url }; + }, + + /** + * DELETE /organizations/logo — used by better-auth-ui's + * `organization.logo.delete` config. + */ + async deleteLogo(ctx) { + const session = ctx.state.betterAuthSession; + + const { url } = ctx.request.body ?? {}; + if (!url) return ctx.badRequest("Missing url."); + + const deleted = await getPluginService("organization").deleteLogo({ + userId: session.user.id, + url, + }); + if (!deleted) return ctx.notFound(); + + ctx.body = { success: true }; + }, }), ); diff --git a/apps/cms/src/extensions/better-auth/policies/is-organization-owner.ts b/apps/cms/src/extensions/better-auth/policies/is-organization-owner.ts new file mode 100644 index 00000000..c6d13453 --- /dev/null +++ b/apps/cms/src/extensions/better-auth/policies/is-organization-owner.ts @@ -0,0 +1,32 @@ +/** + * is-organization-owner policy + * + * Requires the caller to be an owner or admin of the organization + * identified by `ctx.params.id` — i.e. anyone better-auth's organization + * plugin grants `organization: ["update"]` to by default (owner + admin, + * not plain member). Delegates to the real `hasPermission` API instead of + * hand-rolling role checks, so this stays correct if custom roles or + * statements are ever configured in `lib/auth.ts`. + * + * Must run after `is-authenticated` so an unauthenticated request gets a + * 401 from that policy rather than a 403 from this one. + */ + +import { fromNodeHeaders } from "better-auth/node"; +import { auth } from "../../../lib/auth"; + +export default async function isOrganizationOwner(ctx) { + const { id: organizationId } = ctx.params; + + const { success } = await auth.api.hasPermission({ + headers: fromNodeHeaders(ctx.request.headers), + body: { + organizationId, + permissions: { organization: ["update"] }, + }, + }); + + if (!success) return ctx.forbidden(); + + return true; +} diff --git a/apps/cms/src/extensions/better-auth/routes/organization.ts b/apps/cms/src/extensions/better-auth/routes/organization.ts index 2b138174..0329d411 100644 --- a/apps/cms/src/extensions/better-auth/routes/organization.ts +++ b/apps/cms/src/extensions/better-auth/routes/organization.ts @@ -32,6 +32,46 @@ export default { handler: "organization.members", config: { policies: [], middlewares: [], prefix: "" }, }, + { + method: "PUT", + path: "/organizations/:id/profile", + handler: "organization.updateProfile", + config: { + // Called directly from the browser with a better-auth session + // cookie, not a Strapi API token, so the default + // content-api-token check must be skipped in favor of our own + // session- and role-based policies. + auth: false, + policies: [ + "plugin::better-auth.is-authenticated", + "plugin::better-auth.is-organization-owner", + ], + middlewares: [], + prefix: "", + }, + }, + { + method: "POST", + path: "/organizations/logo", + handler: "organization.uploadLogo", + config: { + auth: false, + policies: ["plugin::better-auth.is-authenticated"], + middlewares: [], + prefix: "", + }, + }, + { + method: "DELETE", + path: "/organizations/logo", + handler: "organization.deleteLogo", + config: { + auth: false, + policies: ["plugin::better-auth.is-authenticated"], + middlewares: [], + prefix: "", + }, + }, ], }, }; diff --git a/apps/cms/src/extensions/better-auth/services/organization.ts b/apps/cms/src/extensions/better-auth/services/organization.ts index 7ca5be0e..cb2bb10b 100644 --- a/apps/cms/src/extensions/better-auth/services/organization.ts +++ b/apps/cms/src/extensions/better-auth/services/organization.ts @@ -5,9 +5,58 @@ import { factories, type Modules, type UID } from "@strapi/strapi"; import { extractContentTypeName } from "../utils"; +const logoFileName = (userId: string | number) => `logo-${userId}`; + export default factories.createCoreService( "plugin::better-auth.organization", () => ({ + /** + * Uploads a logo image to the media library, tagged by the uploading + * user rather than a specific organization — better-auth-ui's + * `organization.logo.upload` config has no way to pass which org the + * logo is for (it's set on the org afterwards via better-auth's own + * `organization.update`, which already enforces the + * owner/admin-only `organization:update` permission for that org). + */ + async uploadLogo({ + userId, + file, + }: { + userId: string | number; + file: unknown; + }) { + const [uploaded] = await strapi + .plugin("upload") + .service("upload") + .upload({ + data: { fileInfo: { name: logoFileName(userId) } }, + files: file, + }); + + return uploaded as { url: string }; + }, + + /** + * Removes a previously uploaded logo from the media library, but only + * if it was tagged as this user's own logo upload. + */ + async deleteLogo({ + userId, + url, + }: { + userId: string | number; + url: string; + }) { + const existing = await strapi.db + .query("plugin::upload.file") + .findOne({ where: { url, name: logoFileName(userId) } }); + + if (!existing) return false; + + await strapi.plugin("upload").service("upload").remove(existing); + return true; + }, + async getRelatedContent({ organizationId, uid, @@ -70,5 +119,46 @@ export default factories.createCoreService( return users; }, + + /** + * Upserts the `api::profile.profile` linked to an organization — + * most organizations won't have one yet, since nothing creates it + * automatically when an org is created. + */ + async updateProfile({ + organizationId, + data, + }: { + organizationId: string; + data: Record; + }) { + const [organization] = await strapi + .documents("plugin::better-auth.organization") + .findMany({ + filters: { id: organizationId }, + fields: ["documentId"], + populate: { profile: { fields: ["documentId"] } }, + }); + + if (!organization) return null; + + if (organization.profile) { + return strapi.documents("api::profile.profile").update({ + documentId: organization.profile.documentId, + data, + }); + } + + const profile = await strapi.documents("api::profile.profile").create({ + data, + }); + + await strapi.documents("plugin::better-auth.organization").update({ + documentId: organization.documentId, + data: { profile: profile.documentId }, + }); + + return profile; + }, }), ); diff --git a/apps/cms/src/extensions/better-auth/strapi-server.ts b/apps/cms/src/extensions/better-auth/strapi-server.ts index 57ff3d21..589958c7 100644 --- a/apps/cms/src/extensions/better-auth/strapi-server.ts +++ b/apps/cms/src/extensions/better-auth/strapi-server.ts @@ -1,6 +1,7 @@ import organizationController from "./controllers/organization"; import userController from "./controllers/user"; import isAuthenticated from "./policies/is-authenticated"; +import isOrganizationOwner from "./policies/is-organization-owner"; import organizationRoutes from "./routes/organization"; import userRoutes from "./routes/user"; import organizationService from "./services/organization"; @@ -16,6 +17,7 @@ export default (plugin) => { if (!plugin.policies) plugin.policies = {}; plugin.policies["is-authenticated"] = isAuthenticated; + plugin.policies["is-organization-owner"] = isOrganizationOwner; plugin.routes["content-api"] = { type: "content-api", diff --git a/apps/cms/src/extensions/better-auth/utils/index.ts b/apps/cms/src/extensions/better-auth/utils/index.ts index bee25b34..02de0ab6 100644 --- a/apps/cms/src/extensions/better-auth/utils/index.ts +++ b/apps/cms/src/extensions/better-auth/utils/index.ts @@ -22,3 +22,17 @@ export const communityContentTypes: UID.ContentType[] = [ "api::package.package", "api::template.template", ]; + +/** + * `api::profile.profile` fields an org owner/admin may edit through the + * organization "Profile" tab — scoped to exactly what the public + * organization page renders (bio/subtitle/website/github/location/readme). + */ +export const organizationProfileFields = [ + "bio", + "subtitle", + "website", + "github", + "location", + "readme", +] as const; diff --git a/apps/web/src/app/org/[slug]/[[...path]]/page.tsx b/apps/web/src/app/org/[slug]/[[...path]]/page.tsx index b58708a2..c3201c4f 100644 --- a/apps/web/src/app/org/[slug]/[[...path]]/page.tsx +++ b/apps/web/src/app/org/[slug]/[[...path]]/page.tsx @@ -1,7 +1,11 @@ +import type { GetQueryParams } from "@repo/strapi-client"; import { notFound } from "next/navigation"; import { Navigation } from "@/components/layout/navigation"; import { OrganizationView } from "@/features/auth/components/organization-view"; import { isAuthEnabled } from "@/features/auth/lib/is-enabled"; +import { cmsClient } from "@/features/cms/lib/strapi"; + +const contentType = "plugin::better-auth.organization" as const; type Props = { params: Promise<{ slug: string; path?: string[] }>; @@ -13,11 +17,29 @@ export default async function OrgPage({ params }: Props) { const { slug, path } = await params; const pathname = path ? `/org/${slug}/${path.join("/")}` : `/org/${slug}`; + // Reads better-auth's organization + its linked profile via the default + // Strapi content-api route (server-side bearer token), so the "Profile" + // tab doesn't need its own read endpoint — only the write side does. + const query = { + filters: { slug: { $eq: slug } }, + populate: ["profile"], + } satisfies GetQueryParams; + + const { data } = await cmsClient.collection(contentType).find(query); + const organization = data[0]; + return ( <>
- +
); diff --git a/apps/web/src/features/auth/components/organization-profile-card.tsx b/apps/web/src/features/auth/components/organization-profile-card.tsx new file mode 100644 index 00000000..dea448c0 --- /dev/null +++ b/apps/web/src/features/auth/components/organization-profile-card.tsx @@ -0,0 +1,189 @@ +"use client"; + +import { AuthUIContext } from "@daveyplate/better-auth-ui"; +import { useContext, useState } from "react"; +import { toast } from "sonner"; +import { Input } from "@/components/ui/input"; +import { + type OrganizationProfile, + updateOrganizationProfile, +} from "../lib/organization-profile"; + +type Props = { + organizationId?: string; + initialProfile?: OrganizationProfile; +}; + +type Field = { + name: keyof OrganizationProfile; + label: string; + placeholder: string; + multiline?: boolean; + large?: boolean; +}; + +const FIELDS: Field[] = [ + { + name: "subtitle", + label: "Subtitle", + placeholder: "A short tagline for your organization", + }, + { + name: "bio", + label: "Bio", + placeholder: "Tell the community about your organization", + multiline: true, + }, + { name: "website", label: "Website", placeholder: "https://example.com" }, + { + name: "github", + label: "GitHub", + placeholder: "https://github.com/your-org", + }, + { name: "location", label: "Location", placeholder: "City, Country" }, + { + name: "readme", + label: "Readme", + placeholder: "Markdown supported", + multiline: true, + large: true, + }, +]; + +const textareaClassName = + "flex w-full rounded-md border border-(--color-neutral150) bg-white px-3 py-2 text-sm text-(--color-neutral900) shadow-[0_1px_2px_rgba(0,0,0,0.05)] outline-none placeholder:text-(--color-neutral600) focus-visible:border-(--color-primary200) disabled:cursor-not-allowed disabled:opacity-50"; + +/** + * The organization "Profile" tab — edits the `api::profile.profile` + * fields the public organization page renders (bio/subtitle/website/ + * github/location/readme). Unlike the built-in cards + * (`OrganizationNameCard`, etc.), these fields aren't known to + * better-auth's own organization object, so they're read as a server- + * fetched prop (see `apps/web/src/app/org/[slug]/[[...path]]/page.tsx`) + * rather than via `useCurrentOrganization`, and saved through our own + * `PUT /organizations/:id/profile` endpoint instead of + * `mutators.updateOrganization`. + */ +export function OrganizationProfileCard({ + organizationId, + initialProfile, +}: Props) { + if (!organizationId) { + return ( +
+
+
+
+ ); + } + + return ( + + ); +} + +function OrganizationProfileForm({ + organizationId, + initialProfile, +}: { organizationId: string } & Pick) { + const { + hooks: { useHasPermission }, + } = useContext(AuthUIContext); + + const { data: hasPermission, isPending: permissionPending } = + useHasPermission({ + organizationId, + permissions: { organization: ["update"] }, + }); + + const [values, setValues] = useState( + initialProfile ?? {}, + ); + const [isSubmitting, setIsSubmitting] = useState(false); + + const disabled = permissionPending || !hasPermission?.success; + + const onSubmit = async (event: React.FormEvent) => { + event.preventDefault(); + + setIsSubmitting(true); + const updated = await updateOrganizationProfile(organizationId, values); + setIsSubmitting(false); + + if (updated) { + setValues(updated); + toast.success("Organization profile updated successfully."); + } + }; + + return ( +
+
+

+ Organization profile +

+

+ Shown on your organization's public page. +

+
+ +
+ {FIELDS.map((field) => ( +
+ + {field.multiline ? ( +