From 23f2e5dc1f15516061520ee174225eb4eda6fda7 Mon Sep 17 00:00:00 2001 From: timonwa Date: Tue, 8 Sep 2026 15:15:08 +0100 Subject: [PATCH 01/23] feat(auth): add sendEmail to the password-reset and verification hooks Mirrors useEmailLinkSignIn's sendLink, so both sends can go through your own rate-limited API instead of the browser. success, error and resetState are unchanged; the verification sender receives the signed-in user's address. --- .changeset/delegate-email-sends.md | 13 +++++ .../auth/use-send-email-verification.test.tsx | 32 +++++++++++++ .../src/auth/use-send-email-verification.ts | 25 +++++++++- .../use-send-password-reset-email.test.tsx | 47 +++++++++++++++++++ .../src/auth/use-send-password-reset-email.ts | 21 ++++++++- 5 files changed, 134 insertions(+), 4 deletions(-) create mode 100644 .changeset/delegate-email-sends.md diff --git a/.changeset/delegate-email-sends.md b/.changeset/delegate-email-sends.md new file mode 100644 index 0000000..2a43980 --- /dev/null +++ b/.changeset/delegate-email-sends.md @@ -0,0 +1,13 @@ +--- +"@timonwa/firebase-hooks": minor +--- + +`useSendPasswordResetEmail` and `useSendEmailVerification` gain a `sendEmail` option, matching `useEmailLinkSignIn`'s `sendLink`. Both hooks previously sent from the browser with no way to delegate, which put an email-sending path outside an app's own rate limiter — awkward when the same app already rate-limits the sign-in link server-side. + +```tsx +const { send } = useSendPasswordResetEmail({ + sendEmail: (email) => requestPasswordReset(email), // your rate-limited endpoint +}); +``` + +The hook keeps its own bookkeeping either way: `loading`, `error`, `success` and `resetState` behave identically, and a throwing sender surfaces as an ordinary failure result. On `useSendEmailVerification` the sender receives the signed-in user's address, since `send()` takes no arguments; an account without one fails clearly rather than calling your sender with nothing. diff --git a/packages/firebase-hooks/src/auth/use-send-email-verification.test.tsx b/packages/firebase-hooks/src/auth/use-send-email-verification.test.tsx index 265f97b..452961f 100644 --- a/packages/firebase-hooks/src/auth/use-send-email-verification.test.tsx +++ b/packages/firebase-hooks/src/auth/use-send-email-verification.test.tsx @@ -51,3 +51,35 @@ describe("useSendEmailVerification", () => { expect(result.current.success).toBe(false); }); }); + +describe("sendEmail", () => { + it("delegates the send, passing the signed-in user's address", async () => { + const sendEmail = vi.fn(async () => {}); + const { result } = renderHook(() => + useSendEmailVerification(makeAuth(makeUser({ email: "who@b.c" })), { sendEmail }), + ); + + await act(async () => { + await result.current.send(); + }); + + expect(sendEmail).toHaveBeenCalledWith("who@b.c"); + expect(sendEmailVerification).not.toHaveBeenCalled(); + expect(result.current.success).toBe(true); + }); + + it("fails clearly on an account with no email, rather than sending to undefined", async () => { + const sendEmail = vi.fn(async () => {}); + const { result } = renderHook(() => + useSendEmailVerification(makeAuth(makeUser({ email: null })), { sendEmail }), + ); + + let outcome: unknown; + await act(async () => { + outcome = await result.current.send(); + }); + + expect(sendEmail).not.toHaveBeenCalled(); + expect(outcome).toMatchObject({ success: false }); + }); +}); diff --git a/packages/firebase-hooks/src/auth/use-send-email-verification.ts b/packages/firebase-hooks/src/auth/use-send-email-verification.ts index 8c463aa..6caef9a 100644 --- a/packages/firebase-hooks/src/auth/use-send-email-verification.ts +++ b/packages/firebase-hooks/src/auth/use-send-email-verification.ts @@ -5,11 +5,18 @@ * * @param auth - Firebase `Auth` instance, or null while it initialises * @param options.actionCodeSettings - Where the emailed verification link lands + * @param options.sendEmail - Replace the client-side sender, called with the user's address * @returns `{ send, loading, error, success }` * * @example - * const { send, loading, success } = useSendEmailVerification(auth); + * const { send, loading, success } = useSendEmailVerification(); * + * + * @example + * // Sent by your own API, so the flow goes through your rate limiter + * const { send } = useSendEmailVerification({ + * sendEmail: (email) => requestVerification(email), + * }); */ "use client"; @@ -28,6 +35,12 @@ import { export interface UseSendEmailVerificationOptionsProps extends HookErrorOptions { /** Where the emailed link points back to. Overrides the provider; `null` opts out. */ actionCodeSettings?: ActionCodeSettings | null; + /** + * Replace the sender — e.g. your own API emails the verification link + * instead of Firebase, so the send goes through your rate limiter. Receives + * the signed-in user's address; `success` and `error` behave the same way. + */ + sendEmail?: (email: string) => Promise; } export function useSendEmailVerification( @@ -61,7 +74,15 @@ function useSendEmailVerificationBase( "send-email-verification", "Failed to send verification email", async () => { - await sendEmailVerification(requireCurrentUser(auth), actionCodeSettings); + const user = requireCurrentUser(auth); + if (options.sendEmail) { + // Your sender needs an address, which `send()` doesn't take — it + // comes off the signed-in user, so a phone-only account can't use it. + if (!user.email) throw new Error("This account has no email address"); + await options.sendEmail(user.email); + } else { + await sendEmailVerification(user, actionCodeSettings); + } return {}; }, ); diff --git a/packages/firebase-hooks/src/auth/use-send-password-reset-email.test.tsx b/packages/firebase-hooks/src/auth/use-send-password-reset-email.test.tsx index da9627d..d82ebf5 100644 --- a/packages/firebase-hooks/src/auth/use-send-password-reset-email.test.tsx +++ b/packages/firebase-hooks/src/auth/use-send-password-reset-email.test.tsx @@ -77,3 +77,50 @@ describe("useSendPasswordResetEmail", () => { expect(result.current.error).toBe("Firebase: Error (auth/invalid-email)."); }); }); + +describe("sendEmail", () => { + it("delegates the send and never touches Firebase", async () => { + const sendEmail = vi.fn(async () => {}); + const { result } = renderHook(() => + useSendPasswordResetEmail(makeAuth(), { sendEmail }), + ); + + await act(async () => { + await result.current.send("a@b.c"); + }); + + expect(sendEmail).toHaveBeenCalledWith("a@b.c"); + expect(sendPasswordResetEmail).not.toHaveBeenCalled(); + expect(result.current.success).toBe(true); + }); + + it("a throwing sender fails like any other error, leaving success false", async () => { + const sendEmail = vi.fn(async () => { + throw new Error("rate limited"); + }); + const { result } = renderHook(() => + useSendPasswordResetEmail(makeAuth(), { sendEmail }), + ); + + let outcome: unknown; + await act(async () => { + outcome = await result.current.send("a@b.c"); + }); + + expect(outcome).toMatchObject({ success: false, error: "rate limited" }); + expect(result.current.success).toBe(false); + }); + + it("needs no actionCodeSettings, since Firebase is not the sender", async () => { + const sendEmail = vi.fn(async () => {}); + const { result } = renderHook(() => + useSendPasswordResetEmail(makeAuth(), { sendEmail, actionCodeSettings: null }), + ); + + await act(async () => { + await result.current.send("a@b.c"); + }); + + expect(result.current.success).toBe(true); + }); +}); diff --git a/packages/firebase-hooks/src/auth/use-send-password-reset-email.ts b/packages/firebase-hooks/src/auth/use-send-password-reset-email.ts index 69f294c..2f6a8fe 100644 --- a/packages/firebase-hooks/src/auth/use-send-password-reset-email.ts +++ b/packages/firebase-hooks/src/auth/use-send-password-reset-email.ts @@ -5,12 +5,19 @@ * * @param auth - Firebase `Auth` instance, or null while it initialises * @param options.actionCodeSettings - Where the emailed reset link lands + * @param options.sendEmail - Replace the client-side sender (e.g. your API emails the link instead) * @returns `{ send, loading, error, success, resetState }` * * @example - * const { send, loading, success } = useSendPasswordResetEmail(auth); + * const { send, loading, success } = useSendPasswordResetEmail(); * await send(email); * {success &&

If an account exists for {email}, a reset link is on its way.

} + * + * @example + * // Sent by your own API, so the flow goes through your rate limiter + * const { send } = useSendPasswordResetEmail({ + * sendEmail: (email) => requestPasswordReset(email), + * }); */ "use client"; @@ -33,6 +40,12 @@ import { export interface UseSendPasswordResetEmailOptionsProps extends HookErrorOptions { /** Where the emailed link points back to. Overrides the provider; `null` opts out. */ actionCodeSettings?: ActionCodeSettings | null; + /** + * Replace the sender — e.g. your own API emails the reset link instead of + * Firebase, so the send goes through your rate limiter. `success`, `error` + * and `resetState` still behave the same way. + */ + sendEmail?: (email: string) => Promise; } export function useSendPasswordResetEmail( @@ -66,7 +79,11 @@ function useSendPasswordResetEmailBase( "send-password-reset-email", "Failed to send reset email", async () => { - await sendPasswordResetEmail(requireAuth(auth), email, actionCodeSettings); + if (options.sendEmail) { + await options.sendEmail(email); + } else { + await sendPasswordResetEmail(requireAuth(auth), email, actionCodeSettings); + } return {}; }, ); From c6f7543e7450d08b05da773805d80f0095ef5821 Mon Sep 17 00:00:00 2001 From: timonwa Date: Tue, 8 Sep 2026 15:16:44 +0100 Subject: [PATCH 02/23] docs(auth): explain delegating the emailed-link sends The generated options table lists sendEmail but not why you would reach for it, which is keeping the send inside your own rate limiter rather than the browser. --- .../docs/auth/use-send-email-verification.mdx | 12 ++++++++++++ .../docs/auth/use-send-password-reset-email.mdx | 14 ++++++++++++++ 2 files changed, 26 insertions(+) diff --git a/apps/docs/content/docs/auth/use-send-email-verification.mdx b/apps/docs/content/docs/auth/use-send-email-verification.mdx index 96e46ac..9aa7e18 100644 --- a/apps/docs/content/docs/auth/use-send-email-verification.mdx +++ b/apps/docs/content/docs/auth/use-send-email-verification.mdx @@ -21,6 +21,18 @@ const { send, loading, success } = useSendEmailVerification(); Pair with a cooldown to stop rapid re-sends — Firebase rate-limits these, and hitting the limit is a worse experience than a disabled button. +### Sending it yourself + +`sendEmail` replaces the browser send, so the request goes through your own API and your own rate limiter: + +```tsx +const { send } = useSendEmailVerification({ + sendEmail: (email) => requestVerification(email), // your rate-limited endpoint +}); +``` + +It receives the signed-in user's address, because `send()` takes no arguments. An account with no email — phone-only, for instance — fails with a clear result rather than calling your sender with nothing. + ## Options requestPasswordReset(email), // your rate-limited endpoint +}); +``` + +The hook keeps everything else: `loading`, `error`, `success` and `resetState` behave identically, and a throwing sender surfaces as an ordinary failure result. `actionCodeSettings` is unused on this path — your server decides where the link lands. + +Same option, same semantics, on [`useSendEmailVerification`](/docs/auth/use-send-email-verification) and [`useEmailLinkSignIn`](/docs/auth/use-email-link-sign-in). + ## Options Date: Tue, 8 Sep 2026 15:31:34 +0100 Subject: [PATCH 03/23] feat(auth): export the types needed to wrap a hook All 13 Use*OptionsProps, plus AuthProviderProps and what useAuth returns, so an app can accept and forward a hook's options and layer its own provider without restating shapes the package already has. --- .changeset/export-wrapper-types.md | 18 ++++++ apps/playground/lib/wrapper-types.ts | 58 +++++++++++++++++++ .../firebase-hooks/src/auth/auth-provider.tsx | 4 +- packages/firebase-hooks/src/auth/index.ts | 57 +++++++++++++----- 4 files changed, 120 insertions(+), 17 deletions(-) create mode 100644 .changeset/export-wrapper-types.md create mode 100644 apps/playground/lib/wrapper-types.ts diff --git a/.changeset/export-wrapper-types.md b/.changeset/export-wrapper-types.md new file mode 100644 index 0000000..534ce2e --- /dev/null +++ b/.changeset/export-wrapper-types.md @@ -0,0 +1,18 @@ +--- +"@timonwa/firebase-hooks": minor +--- + +Every type needed to write a wrapper around a hook is now reachable from `@timonwa/firebase-hooks/auth`: + +- all 13 `Use*OptionsProps` interfaces, so a wrapper can accept and forward a hook's options without restating them +- `AuthProviderProps` and `AuthContextValueProps` (what `useAuth` returns), for an app provider layered over this one + +```tsx +import { useLogin, type UseLoginOptionsProps } from "@timonwa/firebase-hooks/auth"; + +export function useAppLogin(options?: UseLoginOptionsProps) { + return useLogin(options); +} +``` + +`HookResult`, `HookErrorOptions` and `HookErrorContext` are unchanged and still ship from the root entry — every service returns them, so they keep one home rather than being re-exported per service. diff --git a/apps/playground/lib/wrapper-types.ts b/apps/playground/lib/wrapper-types.ts new file mode 100644 index 0000000..b59d7c3 --- /dev/null +++ b/apps/playground/lib/wrapper-types.ts @@ -0,0 +1,58 @@ +/** + * Proof that a consumer can type a wrapper without restating the package's + * shapes — it resolves through the exports map exactly as an installed app + * does, so `pnpm --filter playground typecheck` fails if any of these stop + * being reachable from `@timonwa/firebase-hooks/auth`. + * + * Type-only, so nothing here ships in the bundle. + */ + +import type { HookResult } from '@timonwa/firebase-hooks'; +import type { + AuthContextValueProps, + AuthProviderProps, + UseAnonymousSignInOptionsProps, + UseCustomTokenSignInOptionsProps, + UseDeleteAccountOptionsProps, + UseEmailLinkSignInOptionsProps, + UseLoginOptionsProps, + UseLogoutOptionsProps, + UseOAuthSignInOptionsProps, + UsePhoneSignInOptionsProps, + UseSendEmailVerificationOptionsProps, + UseSendPasswordResetEmailOptionsProps, + UseSignupOptionsProps, + UseUpdateEmailOptionsProps, + UseVerifyEmailOptionsProps, +} from '@timonwa/firebase-hooks/auth'; + +/** The wrapper shape the docs recommend for server-fetched user records. */ +export type AppAuth = AuthContextValueProps & { record: { plan: string } | null }; + +/** An app provider layered on top, reusing the package's own props. */ +export type AppAuthProviderProps = Omit & { + auth: AuthProviderProps['auth']; +}; + +/** A wrapper stating what it resolves to, rather than redeclaring the shape. */ +export type WrappedLogin = ( + email: string, + password: string, +) => Promise>; + +/** Every option interface, reachable by name from the auth entry. */ +export type AuthOptions = { + anonymousSignIn: UseAnonymousSignInOptionsProps; + customTokenSignIn: UseCustomTokenSignInOptionsProps; + deleteAccount: UseDeleteAccountOptionsProps; + emailLinkSignIn: UseEmailLinkSignInOptionsProps; + login: UseLoginOptionsProps; + logout: UseLogoutOptionsProps; + oauthSignIn: UseOAuthSignInOptionsProps; + phoneSignIn: UsePhoneSignInOptionsProps; + sendEmailVerification: UseSendEmailVerificationOptionsProps; + sendPasswordResetEmail: UseSendPasswordResetEmailOptionsProps; + signup: UseSignupOptionsProps; + updateEmail: UseUpdateEmailOptionsProps; + verifyEmail: UseVerifyEmailOptionsProps; +}; diff --git a/packages/firebase-hooks/src/auth/auth-provider.tsx b/packages/firebase-hooks/src/auth/auth-provider.tsx index 737eca7..a386042 100644 --- a/packages/firebase-hooks/src/auth/auth-provider.tsx +++ b/packages/firebase-hooks/src/auth/auth-provider.tsx @@ -44,7 +44,7 @@ import { } from "react"; import { AuthConfigContext, type HookErrorContext, type OnIdToken } from "./_shared"; -interface AuthContextValueProps { +export interface AuthContextValueProps { firebaseUser: User | null; /** Custom claims from the current ID token; null while signed out or loading. */ claims: Record | null; @@ -54,7 +54,7 @@ interface AuthContextValueProps { const AuthContext = createContext(undefined); -interface AuthProviderProps { +export interface AuthProviderProps { /** The Firebase `Auth` instance, or null while it initialises. */ auth: Auth | null; /** Package-wide default error wording; each hook's own option overrides it. */ diff --git a/packages/firebase-hooks/src/auth/index.ts b/packages/firebase-hooks/src/auth/index.ts index 8bd04c8..eaae018 100644 --- a/packages/firebase-hooks/src/auth/index.ts +++ b/packages/firebase-hooks/src/auth/index.ts @@ -1,25 +1,52 @@ // The ./auth entry — everything Firebase Auth, including its error catalogue. -// Shared shapes (HookResult, …) live in the core entry, not here. +// Shared shapes (HookResult, HookErrorOptions, HookErrorContext) live in the +// core entry, not here: every service returns them, so they have one home. +// +// Each hook's options interface is re-exported so an app can type a wrapper +// around it without restating the shape. export type { OnIdToken } from "./_shared.js"; export { AUTH_ERROR_MESSAGES } from "./auth-error-messages.js"; -export { AuthProvider, useAuth } from "./auth-provider.js"; -export { useAnonymousSignIn } from "./use-anonymous-sign-in.js"; +export { + type AuthContextValueProps, + AuthProvider, + type AuthProviderProps, + useAuth, +} from "./auth-provider.js"; +export { + type UseAnonymousSignInOptionsProps, + useAnonymousSignIn, +} from "./use-anonymous-sign-in.js"; export { useConfirmPasswordReset } from "./use-confirm-password-reset.js"; -export { useCustomTokenSignIn } from "./use-custom-token-sign-in.js"; -export { useDeleteAccount } from "./use-delete-account.js"; -export { useEmailLinkSignIn } from "./use-email-link-sign-in.js"; +export { + type UseCustomTokenSignInOptionsProps, + useCustomTokenSignIn, +} from "./use-custom-token-sign-in.js"; +export { + type UseDeleteAccountOptionsProps, + useDeleteAccount, +} from "./use-delete-account.js"; +export { + type UseEmailLinkSignInOptionsProps, + useEmailLinkSignIn, +} from "./use-email-link-sign-in.js"; export { useLinkProvider } from "./use-link-provider.js"; -export { useLogin } from "./use-login.js"; -export { useLogout } from "./use-logout.js"; -export { useOAuthSignIn } from "./use-oauth-sign-in.js"; -export { usePhoneSignIn } from "./use-phone-sign-in.js"; +export { type UseLoginOptionsProps, useLogin } from "./use-login.js"; +export { type UseLogoutOptionsProps, useLogout } from "./use-logout.js"; +export { type UseOAuthSignInOptionsProps, useOAuthSignIn } from "./use-oauth-sign-in.js"; +export { type UsePhoneSignInOptionsProps, usePhoneSignIn } from "./use-phone-sign-in.js"; export { useReauthenticate } from "./use-reauthenticate.js"; -export { useSendEmailVerification } from "./use-send-email-verification.js"; -export { useSendPasswordResetEmail } from "./use-send-password-reset-email.js"; -export { useSignup } from "./use-signup.js"; +export { + type UseSendEmailVerificationOptionsProps, + useSendEmailVerification, +} from "./use-send-email-verification.js"; +export { + type UseSendPasswordResetEmailOptionsProps, + useSendPasswordResetEmail, +} from "./use-send-password-reset-email.js"; +export { type UseSignupOptionsProps, useSignup } from "./use-signup.js"; export { useUnlinkProvider } from "./use-unlink-provider.js"; -export { useUpdateEmail } from "./use-update-email.js"; +export { type UseUpdateEmailOptionsProps, useUpdateEmail } from "./use-update-email.js"; export { useUpdatePassword } from "./use-update-password.js"; export { useUpdateProfile } from "./use-update-profile.js"; -export { useVerifyEmail } from "./use-verify-email.js"; +export { type UseVerifyEmailOptionsProps, useVerifyEmail } from "./use-verify-email.js"; From ce266c5ee7ed6e6f21b8ee10d9bc0fbd4abf371a Mon Sep 17 00:00:00 2001 From: timonwa Date: Tue, 8 Sep 2026 15:48:14 +0100 Subject: [PATCH 04/23] docs: show how to type a wrapper around a hook The contract page never named the core entry, so the shared result and error types read as private even though they are documented under Core. --- apps/docs/content/docs/how-hooks-work.mdx | 24 +++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/apps/docs/content/docs/how-hooks-work.mdx b/apps/docs/content/docs/how-hooks-work.mdx index 4509e52..cea3db2 100644 --- a/apps/docs/content/docs/how-hooks-work.mdx +++ b/apps/docs/content/docs/how-hooks-work.mdx @@ -49,6 +49,30 @@ else if (result.code === 'auth/too-many-requests') startCooldown(); See [Error handling](/docs/guides/error-handling) for the full model. +## Writing a wrapper around a hook + +Everything a wrapper needs to name is exported. Each hook's options interface comes from the auth entry, so you can accept and forward them without restating the shape: + +```tsx +import { useLogin, type UseLoginOptionsProps } from '@timonwa/firebase-hooks/auth'; + +export function useAppLogin(options?: UseLoginOptionsProps) { + const { login, loading, error } = useLogin(options); + // …your own state on top + return { login, loading, error }; +} +``` + +`AuthProviderProps` and the type `useAuth` returns are exported too, for an app provider layered over this one — see [Server sessions](/docs/guides/server-sessions). + +The result and error shapes — `HookResult`, `HookErrorOptions`, `HookErrorContext` — come from the **root** entry instead, because every service shares them: + +```ts +import type { HookResult } from '@timonwa/firebase-hooks'; +``` + +Hooks whose only option is `formatErrorMessage` take `HookErrorOptions` directly rather than declaring an interface of their own. See [Core](/docs/core). + ## `onIdToken` runs after a successful sign-in With a freshly minted token, so you can trade it for a server session. Throwing inside it aborts the flow, and the error surfaces like any other failure. From ff0b268ef3e76672e6f92f6cfbc3bdcbf30be9d6 Mon Sep 17 00:00:00 2001 From: timonwa Date: Tue, 8 Sep 2026 16:13:07 +0100 Subject: [PATCH 05/23] docs(auth): trim the sendEmail comments to match sendLink MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit They explained the change rather than the code — what still works, why it was added. The neighbouring sendLink option says it in one line. --- .../src/auth/use-send-email-verification.ts | 8 +++----- .../src/auth/use-send-password-reset-email.ts | 6 +----- 2 files changed, 4 insertions(+), 10 deletions(-) diff --git a/packages/firebase-hooks/src/auth/use-send-email-verification.ts b/packages/firebase-hooks/src/auth/use-send-email-verification.ts index 6caef9a..ed5373a 100644 --- a/packages/firebase-hooks/src/auth/use-send-email-verification.ts +++ b/packages/firebase-hooks/src/auth/use-send-email-verification.ts @@ -36,9 +36,8 @@ export interface UseSendEmailVerificationOptionsProps extends HookErrorOptions { /** Where the emailed link points back to. Overrides the provider; `null` opts out. */ actionCodeSettings?: ActionCodeSettings | null; /** - * Replace the sender — e.g. your own API emails the verification link - * instead of Firebase, so the send goes through your rate limiter. Receives - * the signed-in user's address; `success` and `error` behave the same way. + * Replace the sender — e.g. your own API emails the link instead of Firebase. + * Called with the signed-in user's address. */ sendEmail?: (email: string) => Promise; } @@ -76,8 +75,7 @@ function useSendEmailVerificationBase( async () => { const user = requireCurrentUser(auth); if (options.sendEmail) { - // Your sender needs an address, which `send()` doesn't take — it - // comes off the signed-in user, so a phone-only account can't use it. + // `send()` takes no arguments, so the address comes off the user. if (!user.email) throw new Error("This account has no email address"); await options.sendEmail(user.email); } else { diff --git a/packages/firebase-hooks/src/auth/use-send-password-reset-email.ts b/packages/firebase-hooks/src/auth/use-send-password-reset-email.ts index 2f6a8fe..e3ae96d 100644 --- a/packages/firebase-hooks/src/auth/use-send-password-reset-email.ts +++ b/packages/firebase-hooks/src/auth/use-send-password-reset-email.ts @@ -40,11 +40,7 @@ import { export interface UseSendPasswordResetEmailOptionsProps extends HookErrorOptions { /** Where the emailed link points back to. Overrides the provider; `null` opts out. */ actionCodeSettings?: ActionCodeSettings | null; - /** - * Replace the sender — e.g. your own API emails the reset link instead of - * Firebase, so the send goes through your rate limiter. `success`, `error` - * and `resetState` still behave the same way. - */ + /** Replace the sender — e.g. your own API emails the reset link instead of Firebase. */ sendEmail?: (email: string) => Promise; } From b45ec5c4c8c8741e56640b6eee0d7fb28c5d1f53 Mon Sep 17 00:00:00 2001 From: timonwa Date: Tue, 8 Sep 2026 16:28:43 +0100 Subject: [PATCH 06/23] feat: export CompleteSignInResult, VerifyEmailStatusType and AsyncStatus An app could not annotate what completeSignIn resolves to or what useVerifyEmail reports. AsyncStatus puts the three states in core, so on-mount hooks share one vocabulary instead of each app inventing its own spelling. --- .changeset/export-result-types.md | 12 +++++++++++ apps/playground/lib/wrapper-types.ts | 20 ++++++++++++------- packages/firebase-hooks/src/auth/index.ts | 16 +++++++++------ .../src/auth/use-email-link-sign-in.ts | 3 ++- .../src/auth/use-verify-email.ts | 3 ++- packages/firebase-hooks/src/core/index.ts | 1 + packages/firebase-hooks/src/core/types.ts | 6 ++++++ 7 files changed, 46 insertions(+), 15 deletions(-) create mode 100644 .changeset/export-result-types.md diff --git a/.changeset/export-result-types.md b/.changeset/export-result-types.md new file mode 100644 index 0000000..b6bfe6a --- /dev/null +++ b/.changeset/export-result-types.md @@ -0,0 +1,12 @@ +--- +"@timonwa/firebase-hooks": minor +--- + +Two result types are no longer module-private: + +- `CompleteSignInResult` — what `useEmailLinkSignIn`'s `completeSignIn` resolves to, so a callback page can annotate the function that handles it +- `VerifyEmailStatusType` — `useVerifyEmail`'s status, instead of re-declaring the three states in app code + +Both ship from `@timonwa/firebase-hooks/auth`. + +The status vocabulary is also now shared. `AsyncStatus` (`"processing" | "success" | "failed"`) ships from the root entry, and `VerifyEmailStatusType` is an alias of it, so hooks that act on mount report the same three states rather than each spelling them differently. There is no idle state: the work starts before a render, which is why `processing` is the initial value. diff --git a/apps/playground/lib/wrapper-types.ts b/apps/playground/lib/wrapper-types.ts index b59d7c3..6e934d1 100644 --- a/apps/playground/lib/wrapper-types.ts +++ b/apps/playground/lib/wrapper-types.ts @@ -1,16 +1,14 @@ /** - * Proof that a consumer can type a wrapper without restating the package's - * shapes — it resolves through the exports map exactly as an installed app - * does, so `pnpm --filter playground typecheck` fails if any of these stop - * being reachable from `@timonwa/firebase-hooks/auth`. - * - * Type-only, so nothing here ships in the bundle. + * Type-only. Resolves the package's public types through its exports map the + * way an installed app does, so `typecheck` fails if one stops being reachable. */ -import type { HookResult } from '@timonwa/firebase-hooks'; +import type { AsyncStatus, HookResult } from '@timonwa/firebase-hooks'; import type { AuthContextValueProps, AuthProviderProps, + CompleteSignInResult, + VerifyEmailStatusType, UseAnonymousSignInOptionsProps, UseCustomTokenSignInOptionsProps, UseDeleteAccountOptionsProps, @@ -40,6 +38,14 @@ export type WrappedLogin = ( password: string, ) => Promise>; +/** A page rendering the link-completion result without redeclaring its shape. */ +export type CallbackState = + | { phase: Exclude } + | { phase: 'failed'; result: Extract }; + +/** The status vocabulary, shared rather than respelled per hook. */ +export const VERIFY_STATES: VerifyEmailStatusType[] = ['processing', 'success', 'failed']; + /** Every option interface, reachable by name from the auth entry. */ export type AuthOptions = { anonymousSignIn: UseAnonymousSignInOptionsProps; diff --git a/packages/firebase-hooks/src/auth/index.ts b/packages/firebase-hooks/src/auth/index.ts index eaae018..cbf8fc6 100644 --- a/packages/firebase-hooks/src/auth/index.ts +++ b/packages/firebase-hooks/src/auth/index.ts @@ -1,9 +1,8 @@ -// The ./auth entry — everything Firebase Auth, including its error catalogue. -// Shared shapes (HookResult, HookErrorOptions, HookErrorContext) live in the -// core entry, not here: every service returns them, so they have one home. +// The ./auth entry — everything Firebase Auth, including its error catalogue +// and each hook's options interface. // -// Each hook's options interface is re-exported so an app can type a wrapper -// around it without restating the shape. +// Shared shapes (HookResult, HookErrorOptions, HookErrorContext, AsyncStatus) +// ship from the core entry instead: every service returns them. export type { OnIdToken } from "./_shared.js"; export { AUTH_ERROR_MESSAGES } from "./auth-error-messages.js"; @@ -27,6 +26,7 @@ export { useDeleteAccount, } from "./use-delete-account.js"; export { + type CompleteSignInResult, type UseEmailLinkSignInOptionsProps, useEmailLinkSignIn, } from "./use-email-link-sign-in.js"; @@ -49,4 +49,8 @@ export { useUnlinkProvider } from "./use-unlink-provider.js"; export { type UseUpdateEmailOptionsProps, useUpdateEmail } from "./use-update-email.js"; export { useUpdatePassword } from "./use-update-password.js"; export { useUpdateProfile } from "./use-update-profile.js"; -export { type UseVerifyEmailOptionsProps, useVerifyEmail } from "./use-verify-email.js"; +export { + type UseVerifyEmailOptionsProps, + useVerifyEmail, + type VerifyEmailStatusType, +} from "./use-verify-email.js"; diff --git a/packages/firebase-hooks/src/auth/use-email-link-sign-in.ts b/packages/firebase-hooks/src/auth/use-email-link-sign-in.ts index 4880791..e9361eb 100644 --- a/packages/firebase-hooks/src/auth/use-email-link-sign-in.ts +++ b/packages/firebase-hooks/src/auth/use-email-link-sign-in.ts @@ -66,7 +66,8 @@ export interface UseEmailLinkSignInOptionsProps extends HookErrorOptions { onIdToken?: OnIdToken | null; } -type CompleteSignInResult = +/** `needsEmail` is set when the link opened where the address was never stored. */ +export type CompleteSignInResult = | { success: true; user: User; credential: UserCredential } | { success: false; diff --git a/packages/firebase-hooks/src/auth/use-verify-email.ts b/packages/firebase-hooks/src/auth/use-verify-email.ts index 157b528..898f827 100644 --- a/packages/firebase-hooks/src/auth/use-verify-email.ts +++ b/packages/firebase-hooks/src/auth/use-verify-email.ts @@ -23,6 +23,7 @@ import { type Auth, applyActionCode, type User } from "firebase/auth"; import { useEffect, useRef, useState } from "react"; +import type { AsyncStatus } from "../core/types"; import { getFirebaseErrorCode, type HookErrorOptions, @@ -31,7 +32,7 @@ import { useErrorMessageResolver, } from "./_shared"; -type VerifyEmailStatusType = "processing" | "success" | "failed"; +export type VerifyEmailStatusType = AsyncStatus; export interface UseVerifyEmailOptionsProps extends HookErrorOptions { /** diff --git a/packages/firebase-hooks/src/core/index.ts b/packages/firebase-hooks/src/core/index.ts index 47a29b9..b4bde7e 100644 --- a/packages/firebase-hooks/src/core/index.ts +++ b/packages/firebase-hooks/src/core/index.ts @@ -7,6 +7,7 @@ export { } from "./format-firebase-error.js"; export { getFirebaseErrorCode } from "./get-firebase-error-code.js"; export type { + AsyncStatus, HookErrorContext, HookErrorOptions, HookResult, diff --git a/packages/firebase-hooks/src/core/types.ts b/packages/firebase-hooks/src/core/types.ts index 7894150..56494f1 100644 --- a/packages/firebase-hooks/src/core/types.ts +++ b/packages/firebase-hooks/src/core/types.ts @@ -22,6 +22,12 @@ export interface HookErrorOptions { formatErrorMessage?: (error: unknown) => string; } +/** + * Where a hook that acts on mount has got to. Starts at `processing` — there is + * no idle state, because the work begins before you can render. + */ +export type AsyncStatus = "processing" | "success" | "failed"; + /** What failed, for the global `onError` observer. */ export interface HookErrorContext { /** Stable id of the operation: "login", "oauth-sign-in", "update-password", … */ From 87c77927ca2b59c25c23db9ed0f4b5fa30338fef Mon Sep 17 00:00:00 2001 From: timonwa Date: Tue, 8 Sep 2026 16:30:03 +0100 Subject: [PATCH 07/23] docs: document AsyncStatus and name the exported result types Both hook pages described their return shape in prose without naming it, so there was no way to tell the type was importable. --- .../docs/auth/use-email-link-sign-in.mdx | 2 +- .../content/docs/auth/use-verify-email.mdx | 2 +- apps/docs/content/docs/core/hook-result.mdx | 18 ++++++++++++++++++ 3 files changed, 20 insertions(+), 2 deletions(-) diff --git a/apps/docs/content/docs/auth/use-email-link-sign-in.mdx b/apps/docs/content/docs/auth/use-email-link-sign-in.mdx index 9f02dfd..6d46d93 100644 --- a/apps/docs/content/docs/auth/use-email-link-sign-in.mdx +++ b/apps/docs/content/docs/auth/use-email-link-sign-in.mdx @@ -22,7 +22,7 @@ if (!result.success && result.needsEmail) showEmailConfirmField(); `sendLink(email)` emails the link and remembers the address in `localStorage`. -`completeSignIn(url, email?)` resolves to `{ success: true, user, credential }` or a failure result. +`completeSignIn(url, email?)` resolves to `{ success: true, user, credential }` or a failure result, exported as `CompleteSignInResult` — a failure there also carries `needsEmail`. Also exposes `loading` and `error`. diff --git a/apps/docs/content/docs/auth/use-verify-email.mdx b/apps/docs/content/docs/auth/use-verify-email.mdx index 02a0502..e9f6740 100644 --- a/apps/docs/content/docs/auth/use-verify-email.mdx +++ b/apps/docs/content/docs/auth/use-verify-email.mdx @@ -17,7 +17,7 @@ return ; ## Returns -`status` is `"processing"`, `"success"`, or `"failed"`. `error` carries the message on failure. +`status` is `"processing"`, `"success"`, or `"failed"` — exported as `VerifyEmailStatusType`, and the same [`AsyncStatus`](/docs/core/hook-result#asyncstatus) vocabulary every status in the package uses. `error` carries the message on failure. ## Notes diff --git a/apps/docs/content/docs/core/hook-result.mdx b/apps/docs/content/docs/core/hook-result.mdx index 1166b8b..5d3a156 100644 --- a/apps/docs/content/docs/core/hook-result.mdx +++ b/apps/docs/content/docs/core/hook-result.mdx @@ -52,6 +52,24 @@ if (!result.success) { result.user; // User — narrowed, no optional chaining needed ``` +## AsyncStatus + +`HookResult` covers actions you call. A hook that acts on mount reports a status instead, and they all use one vocabulary: + +```ts +type AsyncStatus = 'processing' | 'success' | 'failed'; +``` + +There is no idle state — the work starts before you can render, so `processing` is where it begins. [`useVerifyEmail`](/docs/auth/use-verify-email) reports it as `VerifyEmailStatusType`, an alias of this. + +Render the three branches and you've handled every case: + +```tsx +if (status === 'processing') return ; +if (status === 'failed') return ; +return ; +``` + ## Related types `HookErrorOptions` — the `formatErrorMessage` option every hook accepts. From d3a9345afb1aaaafc86d2f8dee9324afd202a75f Mon Sep 17 00:00:00 2001 From: timonwa Date: Tue, 8 Sep 2026 16:45:25 +0100 Subject: [PATCH 08/23] feat(auth): let the provider set an email sender per flow An app sending from its own API had to pass sendLink or sendEmail at every call site. One key per flow, not one sender: the three email different things, so a shared one could mail a reset to someone verifying an address. --- .changeset/provider-senders.md | 20 +++++++ packages/firebase-hooks/src/auth/_shared.ts | 22 ++++++++ .../src/auth/auth-provider.test.tsx | 54 +++++++++++++++++++ .../firebase-hooks/src/auth/auth-provider.tsx | 32 ++++++++++- packages/firebase-hooks/src/auth/index.ts | 2 +- .../src/auth/use-email-link-sign-in.ts | 9 ++-- .../src/auth/use-send-email-verification.ts | 8 +-- .../src/auth/use-send-password-reset-email.ts | 8 +-- 8 files changed, 143 insertions(+), 12 deletions(-) create mode 100644 .changeset/provider-senders.md diff --git a/.changeset/provider-senders.md b/.changeset/provider-senders.md new file mode 100644 index 0000000..8009a29 --- /dev/null +++ b/.changeset/provider-senders.md @@ -0,0 +1,20 @@ +--- +"@timonwa/firebase-hooks": minor +--- + +`AuthProvider` gains `senders`, so an app that emails links from its own API configures that once instead of at every call site: + +```tsx + api.sendSignInLink(email), + passwordReset: (email) => api.sendPasswordReset(email), + emailVerification: (email) => api.sendVerification(email), + }} +> +``` + +One key per flow rather than a single sender: the three send different emails, so one sender for all of them could mail a password reset to someone asking to verify an address. The shape follows the same convention as TanStack Query's `defaultOptions`, which namespaces defaults by operation kind so the per-call signature stays identical to the global one. + +Each key follows the existing rule — a hook's own `sendLink`/`sendEmail` overrides it, and `null` opts that one flow back to Firebase's client-side send. diff --git a/packages/firebase-hooks/src/auth/_shared.ts b/packages/firebase-hooks/src/auth/_shared.ts index cc2a90c..6ff43e6 100644 --- a/packages/firebase-hooks/src/auth/_shared.ts +++ b/packages/firebase-hooks/src/auth/_shared.ts @@ -34,6 +34,23 @@ function rawErrorMessage(error: unknown, fallback: string): string { return fallback; } +/** Emails a link on the app's behalf, in place of Firebase's client SDK. */ +export type SendEmail = (email: string) => Promise; + +/** + * Your own sender per emailed flow. Each one replaces the client-side send for + * that flow only — the three send different emails, so one sender for all of + * them could mail a password reset to someone asking to verify an address. + */ +export interface AuthSendersProps { + /** `useEmailLinkSignIn`'s `sendLink`. */ + signInLink?: SendEmail; + /** `useSendPasswordResetEmail`'s `sendEmail`. */ + passwordReset?: SendEmail; + /** `useSendEmailVerification`'s `sendEmail`. */ + emailVerification?: SendEmail; +} + /** Provider-level configuration shared with every hook below the provider. */ export interface AuthConfigContextValueProps { /** The provider's own `Auth`, for hooks called without one. */ @@ -43,6 +60,11 @@ export interface AuthConfigContextValueProps { onBeforeSignOut?: () => void | Promise; actionCodeSettings?: ActionCodeSettings; onError?: (error: unknown, context: HookErrorContext) => void; + // Flattened from the provider's `senders` prop, so each resolves through + // useResolvedConfig like every other inherited option. + sendSignInLink?: SendEmail; + sendPasswordReset?: SendEmail; + sendEmailVerification?: SendEmail; } export const AuthConfigContext = createContext( diff --git a/packages/firebase-hooks/src/auth/auth-provider.test.tsx b/packages/firebase-hooks/src/auth/auth-provider.test.tsx index 180f652..6dc9f42 100644 --- a/packages/firebase-hooks/src/auth/auth-provider.test.tsx +++ b/packages/firebase-hooks/src/auth/auth-provider.test.tsx @@ -351,3 +351,57 @@ describe("auth argument resolution", () => { expect(outcome).toMatchObject({ success: false }); }); }); + +describe("provider-level senders", () => { + it("each hook inherits its own sender, and only its own", async () => { + const signInLink = vi.fn(async () => {}); + const passwordReset = vi.fn(async () => {}); + const wrapper = withAuthProvider({ + auth: makeAuth(), + senders: { signInLink, passwordReset }, + }); + + const { result } = renderHook(() => useSendPasswordResetEmail(), { wrapper }); + await act(async () => { + await result.current.send("a@b.c"); + }); + + // The three senders email different things, so a reset must never reach + // the sign-in-link sender. + expect(passwordReset).toHaveBeenCalledWith("a@b.c"); + expect(signInLink).not.toHaveBeenCalled(); + expect(sendPasswordResetEmail).not.toHaveBeenCalled(); + }); + + it("a hook's own sender overrides the provider's", async () => { + const passwordReset = vi.fn(async () => {}); + const own = vi.fn(async () => {}); + const wrapper = withAuthProvider({ auth: makeAuth(), senders: { passwordReset } }); + + const { result } = renderHook(() => useSendPasswordResetEmail({ sendEmail: own }), { + wrapper, + }); + await act(async () => { + await result.current.send("a@b.c"); + }); + + expect(own).toHaveBeenCalledWith("a@b.c"); + expect(passwordReset).not.toHaveBeenCalled(); + }); + + it("null opts one flow back to Firebase's own send", async () => { + const passwordReset = vi.fn(async () => {}); + const wrapper = withAuthProvider({ auth: makeAuth(), senders: { passwordReset } }); + + const { result } = renderHook( + () => useSendPasswordResetEmail({ sendEmail: null, actionCodeSettings: null }), + { wrapper }, + ); + await act(async () => { + await result.current.send("a@b.c"); + }); + + expect(passwordReset).not.toHaveBeenCalled(); + expect(sendPasswordResetEmail).toHaveBeenCalled(); + }); +}); diff --git a/packages/firebase-hooks/src/auth/auth-provider.tsx b/packages/firebase-hooks/src/auth/auth-provider.tsx index a386042..25c5899 100644 --- a/packages/firebase-hooks/src/auth/auth-provider.tsx +++ b/packages/firebase-hooks/src/auth/auth-provider.tsx @@ -42,7 +42,12 @@ import { useMemo, useState, } from "react"; -import { AuthConfigContext, type HookErrorContext, type OnIdToken } from "./_shared"; +import { + AuthConfigContext, + type AuthSendersProps, + type HookErrorContext, + type OnIdToken, +} from "./_shared"; export interface AuthContextValueProps { firebaseUser: User | null; @@ -67,6 +72,11 @@ export interface AuthProviderProps { actionCodeSettings?: ActionCodeSettings; /** Fire-and-forget observer every hook failure flows through (logging/analytics). */ onError?: (error: unknown, context: HookErrorContext) => void; + /** + * Your own sender per emailed flow, so the sends go through your API rather + * than the browser. A hook's own option overrides its entry here. + */ + senders?: AuthSendersProps; children: ReactNode; } @@ -77,6 +87,7 @@ export function AuthProvider({ onBeforeSignOut, actionCodeSettings, onError, + senders, children, }: AuthProviderProps) { const [firebaseUser, setFirebaseUser] = useState(null); @@ -106,6 +117,10 @@ export function AuthProvider({ return () => unsubscribe(); }, [auth]); + // Destructured so an inline `senders={{ … }}` doesn't rebuild the config on + // every render. + const { signInLink, passwordReset, emailVerification } = senders ?? {}; + const config = useMemo( () => ({ auth, @@ -114,8 +129,21 @@ export function AuthProvider({ onBeforeSignOut, actionCodeSettings, onError, + sendSignInLink: signInLink, + sendPasswordReset: passwordReset, + sendEmailVerification: emailVerification, }), - [auth, formatErrorMessage, onIdToken, onBeforeSignOut, actionCodeSettings, onError], + [ + auth, + formatErrorMessage, + onIdToken, + onBeforeSignOut, + actionCodeSettings, + onError, + signInLink, + passwordReset, + emailVerification, + ], ); return ( diff --git a/packages/firebase-hooks/src/auth/index.ts b/packages/firebase-hooks/src/auth/index.ts index cbf8fc6..a04dd10 100644 --- a/packages/firebase-hooks/src/auth/index.ts +++ b/packages/firebase-hooks/src/auth/index.ts @@ -4,7 +4,7 @@ // Shared shapes (HookResult, HookErrorOptions, HookErrorContext, AsyncStatus) // ship from the core entry instead: every service returns them. -export type { OnIdToken } from "./_shared.js"; +export type { AuthSendersProps, OnIdToken, SendEmail } from "./_shared.js"; export { AUTH_ERROR_MESSAGES } from "./auth-error-messages.js"; export { type AuthContextValueProps, diff --git a/packages/firebase-hooks/src/auth/use-email-link-sign-in.ts b/packages/firebase-hooks/src/auth/use-email-link-sign-in.ts index e9361eb..734759e 100644 --- a/packages/firebase-hooks/src/auth/use-email-link-sign-in.ts +++ b/packages/firebase-hooks/src/auth/use-email-link-sign-in.ts @@ -44,6 +44,7 @@ import { type OnIdToken, requireAuth, runOnIdToken, + type SendEmail, useAuthArgs, useAuthTask, useResolvedConfig, @@ -58,7 +59,7 @@ export interface UseEmailLinkSignInOptionsProps extends HookErrorOptions { */ storageKey?: string; /** Replace the sender — e.g. your own API emails the link instead of Firebase. */ - sendLink?: (email: string) => Promise; + sendLink?: SendEmail | null; /** * Called with a freshly minted ID token after sign-in — mint your server * session here. Throwing aborts the flow. Overrides the provider; `null` opts out. @@ -103,10 +104,12 @@ function useEmailLinkSignInBase( options.actionCodeSettings, ); + const send = useResolvedConfig("sendSignInLink", options.sendLink); + const sendLink = (email: string): Promise => run("send-sign-in-link", "Failed to send sign-in link", async () => { - if (options.sendLink) { - await options.sendLink(email); + if (send) { + await send(email); } else { if (!actionCodeSettings) { throw new Error( diff --git a/packages/firebase-hooks/src/auth/use-send-email-verification.ts b/packages/firebase-hooks/src/auth/use-send-email-verification.ts index ed5373a..97cc12e 100644 --- a/packages/firebase-hooks/src/auth/use-send-email-verification.ts +++ b/packages/firebase-hooks/src/auth/use-send-email-verification.ts @@ -27,6 +27,7 @@ import { type HookErrorOptions, type HookResult, requireCurrentUser, + type SendEmail, useAuthArgs, useAuthTask, useResolvedConfig, @@ -39,7 +40,7 @@ export interface UseSendEmailVerificationOptionsProps extends HookErrorOptions { * Replace the sender — e.g. your own API emails the link instead of Firebase. * Called with the signed-in user's address. */ - sendEmail?: (email: string) => Promise; + sendEmail?: SendEmail | null; } export function useSendEmailVerification( @@ -65,6 +66,7 @@ function useSendEmailVerificationBase( "actionCodeSettings", options.actionCodeSettings, ); + const sendEmail = useResolvedConfig("sendEmailVerification", options.sendEmail); const [success, setSuccess] = useState(false); const send = async (): Promise => { @@ -74,10 +76,10 @@ function useSendEmailVerificationBase( "Failed to send verification email", async () => { const user = requireCurrentUser(auth); - if (options.sendEmail) { + if (sendEmail) { // `send()` takes no arguments, so the address comes off the user. if (!user.email) throw new Error("This account has no email address"); - await options.sendEmail(user.email); + await sendEmail(user.email); } else { await sendEmailVerification(user, actionCodeSettings); } diff --git a/packages/firebase-hooks/src/auth/use-send-password-reset-email.ts b/packages/firebase-hooks/src/auth/use-send-password-reset-email.ts index e3ae96d..9da205f 100644 --- a/packages/firebase-hooks/src/auth/use-send-password-reset-email.ts +++ b/packages/firebase-hooks/src/auth/use-send-password-reset-email.ts @@ -32,6 +32,7 @@ import { type HookErrorOptions, type HookResult, requireAuth, + type SendEmail, useAuthArgs, useAuthTask, useResolvedConfig, @@ -41,7 +42,7 @@ export interface UseSendPasswordResetEmailOptionsProps extends HookErrorOptions /** Where the emailed link points back to. Overrides the provider; `null` opts out. */ actionCodeSettings?: ActionCodeSettings | null; /** Replace the sender — e.g. your own API emails the reset link instead of Firebase. */ - sendEmail?: (email: string) => Promise; + sendEmail?: SendEmail | null; } export function useSendPasswordResetEmail( @@ -67,6 +68,7 @@ function useSendPasswordResetEmailBase( "actionCodeSettings", options.actionCodeSettings, ); + const sendEmail = useResolvedConfig("sendPasswordReset", options.sendEmail); const [success, setSuccess] = useState(false); const send = async (email: string): Promise => { @@ -75,8 +77,8 @@ function useSendPasswordResetEmailBase( "send-password-reset-email", "Failed to send reset email", async () => { - if (options.sendEmail) { - await options.sendEmail(email); + if (sendEmail) { + await sendEmail(email); } else { await sendPasswordResetEmail(requireAuth(auth), email, actionCodeSettings); } From 1d42005054a8cfa498120d4c8b2c118a69677686 Mon Sep 17 00:00:00 2001 From: timonwa Date: Tue, 8 Sep 2026 16:58:47 +0100 Subject: [PATCH 09/23] docs(auth): document the provider senders Includes why it is a key per flow rather than one sender, so the shape does not get flattened later into something that can mail the wrong email. --- apps/docs/content/docs/auth/auth-provider.mdx | 31 +++++++++++++++++++ .../auth/use-send-password-reset-email.mdx | 2 +- 2 files changed, 32 insertions(+), 1 deletion(-) diff --git a/apps/docs/content/docs/auth/auth-provider.mdx b/apps/docs/content/docs/auth/auth-provider.mdx index 617a591..d1d783d 100644 --- a/apps/docs/content/docs/auth/auth-provider.mdx +++ b/apps/docs/content/docs/auth/auth-provider.mdx @@ -58,6 +58,37 @@ A hook's own option overrides the provider, and an explicit `null` opts that flo | `actionCodeSettings` | Inherited by every emailed link | | `formatErrorMessage` | Inherited by every hook — see [Error handling](/docs/guides/error-handling) | | `onError` | Fire-and-forget observer for every failure | +| `senders` | Your own email sender per flow — see below | + +## Sending the emails yourself + +Three hooks email a link, and by default each sends from the browser. `senders` moves them behind your own API, once, instead of per call site: + +```tsx + api.sendSignInLink(email), + passwordReset: (email) => api.sendPasswordReset(email), + emailVerification: (email) => api.sendVerification(email), + }} +> +``` + +| Key | Replaces | +| --- | --- | +| `signInLink` | [`useEmailLinkSignIn`](/docs/auth/use-email-link-sign-in)'s `sendLink` | +| `passwordReset` | [`useSendPasswordResetEmail`](/docs/auth/use-send-password-reset-email)'s `sendEmail` | +| `emailVerification` | [`useSendEmailVerification`](/docs/auth/use-send-email-verification)'s `sendEmail` | + +One key per flow rather than a single sender, because the three send different emails — one sender for all of them could mail a password reset to someone who asked to verify an address. + +Each follows the usual rule: the hook's own option wins, and `null` opts that one flow back to Firebase's client-side send. + +```tsx +// Everything through your API, except this one call +const { send } = useSendPasswordResetEmail({ sendEmail: null }); +``` ## Your own user record diff --git a/apps/docs/content/docs/auth/use-send-password-reset-email.mdx b/apps/docs/content/docs/auth/use-send-password-reset-email.mdx index 8bfbdd2..1dbb60b 100644 --- a/apps/docs/content/docs/auth/use-send-password-reset-email.mdx +++ b/apps/docs/content/docs/auth/use-send-password-reset-email.mdx @@ -35,7 +35,7 @@ const { send, success } = useSendPasswordResetEmail({ The hook keeps everything else: `loading`, `error`, `success` and `resetState` behave identically, and a throwing sender surfaces as an ordinary failure result. `actionCodeSettings` is unused on this path — your server decides where the link lands. -Same option, same semantics, on [`useSendEmailVerification`](/docs/auth/use-send-email-verification) and [`useEmailLinkSignIn`](/docs/auth/use-email-link-sign-in). +Same option, same semantics, on [`useSendEmailVerification`](/docs/auth/use-send-email-verification) and [`useEmailLinkSignIn`](/docs/auth/use-email-link-sign-in). To set all three at once, use the provider's [`senders`](/docs/auth/auth-provider#sending-the-emails-yourself) — a hook's own option still overrides it, and `null` opts that one flow back to Firebase. ## Options From 846cebee216edc10184cb004b1ecf0a5136f28e3 Mon Sep 17 00:00:00 2001 From: timonwa Date: Tue, 8 Sep 2026 17:20:40 +0100 Subject: [PATCH 10/23] docs(ssr): note when a null auth is required, not just allowed MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Next 16 prerendering rejects the clock read inside getAuth, and the error names the app's own file — so the fix reads as an app bug rather than a known rule. --- apps/docs/content/docs/guides/ssr.mdx | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/apps/docs/content/docs/guides/ssr.mdx b/apps/docs/content/docs/guides/ssr.mdx index f5e4c0b..2a020fb 100644 --- a/apps/docs/content/docs/guides/ssr.mdx +++ b/apps/docs/content/docs/guides/ssr.mdx @@ -20,6 +20,18 @@ Nothing throws, so you don't need to guard every call site. const { login } = useLogin(); // auth may be null — calling login just fails cleanly ``` +### When you have to pass null + +One case turns that affordance into a requirement. Next 16 with Cache Components prerenders your provider, and `getAuth()` reads the clock — which Next rejects as an unstable prerender value, failing the build. The error names your own file, so it reads as a bug in your code: + +```tsx + + {children} + +``` + +Server-side there is no `Auth` instance, which is correct: Firebase Auth is a browser API, and the prerendered HTML is the signed-out shell either way. The client creates it on hydration and `isLoading` covers the gap. + ## Distinguishing "signed out" from "not yet known" [`useAuth`](/docs/auth/auth-provider) exposes `isLoading`, true only until Firebase's first callback. Without it, a signed-in user flashes a signed-out UI on every load. From 0ffc93d00844beafe88954ac8690f2f029d01845 Mon Sep 17 00:00:00 2001 From: timonwa Date: Tue, 8 Sep 2026 17:25:04 +0100 Subject: [PATCH 11/23] docs(guides): add the one-page recipe for every emailed link Three hooks, one action URL, and no page showing them together. The action URL is set per template, so configuring one flow leaves the others silently hosted. --- .../docs/auth/use-confirm-password-reset.mdx | 2 + .../docs/auth/use-email-link-sign-in.mdx | 2 + .../content/docs/auth/use-verify-email.mdx | 2 + .../content/docs/guides/email-action-page.mdx | 94 +++++++++++++++++++ apps/docs/content/docs/guides/index.mdx | 4 + apps/docs/content/docs/guides/meta.json | 8 +- 6 files changed, 111 insertions(+), 1 deletion(-) create mode 100644 apps/docs/content/docs/guides/email-action-page.mdx diff --git a/apps/docs/content/docs/auth/use-confirm-password-reset.mdx b/apps/docs/content/docs/auth/use-confirm-password-reset.mdx index 3fd4f36..c48a4e5 100644 --- a/apps/docs/content/docs/auth/use-confirm-password-reset.mdx +++ b/apps/docs/content/docs/auth/use-confirm-password-reset.mdx @@ -19,3 +19,5 @@ await confirm(oobCode, newPassword); `verifyCode(oobCode)` optionally checks the code first and resolves to `{ success: true, email }`, so the page can show whose password is being reset before asking for a new one. Also exposes `loading`, `error`, `success`, and `resetState`. + +All three emailed links land on one URL — see [One page for every emailed link](/docs/guides/email-action-page) for the page that dispatches on `mode`. diff --git a/apps/docs/content/docs/auth/use-email-link-sign-in.mdx b/apps/docs/content/docs/auth/use-email-link-sign-in.mdx index 6d46d93..f838f3d 100644 --- a/apps/docs/content/docs/auth/use-email-link-sign-in.mdx +++ b/apps/docs/content/docs/auth/use-email-link-sign-in.mdx @@ -40,3 +40,5 @@ const result = await completeSignIn(url, confirmedEmail); path="../../packages/firebase-hooks/src/auth/use-email-link-sign-in.ts" name="UseEmailLinkSignInOptionsProps" /> + +All three emailed links land on one URL — see [One page for every emailed link](/docs/guides/email-action-page) for the page that dispatches on `mode`. diff --git a/apps/docs/content/docs/auth/use-verify-email.mdx b/apps/docs/content/docs/auth/use-verify-email.mdx index e9f6740..c3bfabe 100644 --- a/apps/docs/content/docs/auth/use-verify-email.mdx +++ b/apps/docs/content/docs/auth/use-verify-email.mdx @@ -31,3 +31,5 @@ After verifying, the current user is reloaded and the token refreshed so `emailV path="../../packages/firebase-hooks/src/auth/use-verify-email.ts" name="UseVerifyEmailOptionsProps" /> + +All three emailed links land on one URL — see [One page for every emailed link](/docs/guides/email-action-page) for the page that dispatches on `mode`. diff --git a/apps/docs/content/docs/guides/email-action-page.mdx b/apps/docs/content/docs/guides/email-action-page.mdx new file mode 100644 index 0000000..ebcc602 --- /dev/null +++ b/apps/docs/content/docs/guides/email-action-page.mdx @@ -0,0 +1,94 @@ +--- +title: One page for every emailed link +description: Firebase sends sign-in, password-reset and verification links to a single URL. Dispatch on mode and hand each one to its hook. +--- + +Firebase emails three kinds of link, and by default they all land on Firebase's own hosted pages. To handle them in your app instead, you point Firebase at **one** URL — every link goes there, with a `mode` parameter saying which flow it is. + +So you write one page that dispatches on `mode`, and a hook handles each branch. + +| `mode` | Hook | Also in the URL | +| --- | --- | --- | +| `signIn` | [`useEmailLinkSignIn`](/docs/auth/use-email-link-sign-in) | the whole URL is the credential | +| `resetPassword` | [`useConfirmPasswordReset`](/docs/auth/use-confirm-password-reset) | `oobCode` | +| `verifyEmail` | [`useVerifyEmail`](/docs/auth/use-verify-email) | `oobCode` | + +## The page + +```tsx title="app/auth/action/page.tsx" +'use client'; + +import { + useConfirmPasswordReset, + useEmailLinkSignIn, + useVerifyEmail, +} from '@timonwa/firebase-hooks/auth'; +import { useSearchParams } from 'next/navigation'; +import { Suspense } from 'react'; + +export default function AuthActionPage() { + return ( + }> + + + ); +} + +function AuthAction() { + const params = useSearchParams(); + const mode = params.get('mode'); + const oobCode = params.get('oobCode'); + + if (mode === 'signIn') return ; + if (mode === 'resetPassword') return ; + if (mode === 'verifyEmail') return ; + return

This link isn't one we recognise.

; +} +``` + +Each branch is then the ordinary hook usage: + +```tsx +function VerifyEmail({ oobCode }: { oobCode: string | null }) { + const { status, error } = useVerifyEmail(oobCode); + + if (status === 'processing') return ; + if (status === 'failed') return ; + return

Email verified.

; +} + +function ResetPassword({ oobCode }: { oobCode: string | null }) { + const { verifyCode, confirm, loading, error } = useConfirmPasswordReset(); + // verifyCode(oobCode) first — it returns the account email, so the form can + // say whose password is being reset before asking for a new one. +} + +function CompleteSignIn() { + const { completeSignIn } = useEmailLinkSignIn(); + // Pass the full URL, not the oobCode: the link itself is the credential. + // A failure with needsEmail means the link opened on another device. +} +``` + +## Set the action URL per template + +This is the step that silently half-works. The action URL is configured **per email template**, not once for the project — **Authentication → Templates**, then edit each one. + +Set it for the sign-in template only and password resets keep going to Firebase's hosted page, with no error anywhere to tell you. If two of your three flows work and one doesn't, this is why. + +Both `resetPassword` and `verifyEmail` need it. `signIn` is the exception: [`useEmailLinkSignIn`](/docs/auth/use-email-link-sign-in) passes its return URL in `actionCodeSettings` at send time, so it doesn't depend on the console setting. + +Add your domain under **Authentication → Settings → Authorized domains**. `localhost` is there by default. + +## In Next.js, wrap it in Suspense + +`useSearchParams` opts the route out of prerendering unless it sits under a `` boundary — without one the build fails. That's the only reason the page above splits into two components: the outer one exists to hold the boundary. + +## Testing it + +The codes are single-use and expire, so you need a real email each time. Two things make that less painful: + +- Verification links can be re-sent from the app with [`useSendEmailVerification`](/docs/auth/use-send-email-verification). +- A reset code can be checked without spending it — `verifyCode(oobCode)` validates and returns the email; only `confirm` consumes it. + +`useVerifyEmail` applies its code on mount and is guarded against React Strict Mode's double effect, which would otherwise spend the code on the first run and report failure on the second. diff --git a/apps/docs/content/docs/guides/index.mdx b/apps/docs/content/docs/guides/index.mdx index 8e1b3f1..eea9071 100644 --- a/apps/docs/content/docs/guides/index.mdx +++ b/apps/docs/content/docs/guides/index.mdx @@ -16,6 +16,10 @@ Read them when you hit the problem, not upfront. Why actions return failures instead of throwing, how to branch on Firebase codes, and how to opt into message formatting or replace the wording entirely. + + Firebase sends sign-in, reset and verification links to a single URL. Dispatch on + `mode`, and set the action URL per template or only one of them arrives. + Where the client boundary is, why passing a null `auth` is always safe, and how to avoid flashing a signed-out UI on every load. diff --git a/apps/docs/content/docs/guides/meta.json b/apps/docs/content/docs/guides/meta.json index 80d8093..2e4edc9 100644 --- a/apps/docs/content/docs/guides/meta.json +++ b/apps/docs/content/docs/guides/meta.json @@ -1,5 +1,11 @@ { "title": "Guides", - "pages": ["server-sessions", "error-handling", "ssr", "compatibility"], + "pages": [ + "server-sessions", + "error-handling", + "email-action-page", + "ssr", + "compatibility" + ], "defaultOpen": true } From fc59c2e6ccd64fffe63ecd37b8f54cc61bc4937c Mon Sep 17 00:00:00 2001 From: timonwa Date: Tue, 8 Sep 2026 22:47:15 +0100 Subject: [PATCH 12/23] feat(auth)!: align the status vocabulary and unify the sender option MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit status was 'processing'/'failed' where TanStack Query uses 'pending'/'error' — the same synonym problem the shared type was meant to end. sendLink's option is now sendEmail, so all three emailed-link hooks name it identically. --- .changeset/align-status-and-sender-naming.md | 19 +++++++++++++++++++ apps/docs/content/docs/auth/auth-provider.mdx | 2 +- .../docs/auth/use-email-link-sign-in.mdx | 2 ++ .../content/docs/auth/use-verify-email.mdx | 6 +++--- apps/docs/content/docs/core/hook-result.mdx | 8 ++++---- .../content/docs/guides/email-action-page.mdx | 4 ++-- apps/playground/app/auth/action/page.tsx | 4 ++-- .../components/auth/use-verify-email.tsx | 2 +- apps/playground/lib/wrapper-types.ts | 6 +++--- packages/firebase-hooks/src/auth/_shared.ts | 2 +- .../src/auth/use-email-link-sign-in.test.tsx | 7 +++++-- .../src/auth/use-email-link-sign-in.ts | 6 +++--- .../src/auth/use-verify-email.test.tsx | 14 +++++++------- .../src/auth/use-verify-email.ts | 12 ++++++------ packages/firebase-hooks/src/core/types.ts | 6 +++--- 15 files changed, 62 insertions(+), 38 deletions(-) create mode 100644 .changeset/align-status-and-sender-naming.md diff --git a/.changeset/align-status-and-sender-naming.md b/.changeset/align-status-and-sender-naming.md new file mode 100644 index 0000000..751583e --- /dev/null +++ b/.changeset/align-status-and-sender-naming.md @@ -0,0 +1,19 @@ +--- +"@timonwa/firebase-hooks": minor +--- + +**Breaking: `useVerifyEmail`'s `status` uses the standard async vocabulary.** `"processing"` is now `"pending"` and `"failed"` is now `"error"`, matching TanStack Query's `QueryStatus` rather than spelling the same three states differently. + +```tsx +- if (status === 'processing') return ; +- if (status === 'failed') return ; ++ if (status === 'pending') return ; ++ if (status === 'error') return ; +``` + +**Breaking: `useEmailLinkSignIn`'s `sendLink` option is now `sendEmail`.** All three emailed-link hooks take the same option name. The returned `sendLink` function is unchanged. + +```tsx +-useEmailLinkSignIn({ sendLink: (email) => api.send(email) }); ++useEmailLinkSignIn({ sendEmail: (email) => api.send(email) }); +``` diff --git a/apps/docs/content/docs/auth/auth-provider.mdx b/apps/docs/content/docs/auth/auth-provider.mdx index d1d783d..e46b1fe 100644 --- a/apps/docs/content/docs/auth/auth-provider.mdx +++ b/apps/docs/content/docs/auth/auth-provider.mdx @@ -77,7 +77,7 @@ Three hooks email a link, and by default each sends from the browser. `senders` | Key | Replaces | | --- | --- | -| `signInLink` | [`useEmailLinkSignIn`](/docs/auth/use-email-link-sign-in)'s `sendLink` | +| `signInLink` | [`useEmailLinkSignIn`](/docs/auth/use-email-link-sign-in)'s `sendEmail` | | `passwordReset` | [`useSendPasswordResetEmail`](/docs/auth/use-send-password-reset-email)'s `sendEmail` | | `emailVerification` | [`useSendEmailVerification`](/docs/auth/use-send-email-verification)'s `sendEmail` | diff --git a/apps/docs/content/docs/auth/use-email-link-sign-in.mdx b/apps/docs/content/docs/auth/use-email-link-sign-in.mdx index f838f3d..7941196 100644 --- a/apps/docs/content/docs/auth/use-email-link-sign-in.mdx +++ b/apps/docs/content/docs/auth/use-email-link-sign-in.mdx @@ -22,6 +22,8 @@ if (!result.success && result.needsEmail) showEmailConfirmField(); `sendLink(email)` emails the link and remembers the address in `localStorage`. +Set the `sendEmail` option to send it from your own API instead — the same option name the other two emailed-link hooks take, or set all three at once with the provider's [`senders`](/docs/auth/auth-provider#sending-the-emails-yourself). + `completeSignIn(url, email?)` resolves to `{ success: true, user, credential }` or a failure result, exported as `CompleteSignInResult` — a failure there also carries `needsEmail`. Also exposes `loading` and `error`. diff --git a/apps/docs/content/docs/auth/use-verify-email.mdx b/apps/docs/content/docs/auth/use-verify-email.mdx index c3bfabe..e241b19 100644 --- a/apps/docs/content/docs/auth/use-verify-email.mdx +++ b/apps/docs/content/docs/auth/use-verify-email.mdx @@ -10,14 +10,14 @@ const { status, error } = useVerifyEmail(searchParams.get('oobCode'), { onVerified: refreshSession, }); -if (status === 'processing') return ; -if (status === 'failed') return ; +if (status === 'pending') return ; +if (status === 'error') return ; return ; ``` ## Returns -`status` is `"processing"`, `"success"`, or `"failed"` — exported as `VerifyEmailStatusType`, and the same [`AsyncStatus`](/docs/core/hook-result#asyncstatus) vocabulary every status in the package uses. `error` carries the message on failure. +`status` is `"pending"`, `"success"`, or `"error"` — exported as `VerifyEmailStatusType`, and the same [`AsyncStatus`](/docs/core/hook-result#asyncstatus) vocabulary every status in the package uses. `error` carries the message on failure. ## Notes diff --git a/apps/docs/content/docs/core/hook-result.mdx b/apps/docs/content/docs/core/hook-result.mdx index 5d3a156..b2912db 100644 --- a/apps/docs/content/docs/core/hook-result.mdx +++ b/apps/docs/content/docs/core/hook-result.mdx @@ -57,16 +57,16 @@ result.user; // User — narrowed, no optional chaining needed `HookResult` covers actions you call. A hook that acts on mount reports a status instead, and they all use one vocabulary: ```ts -type AsyncStatus = 'processing' | 'success' | 'failed'; +type AsyncStatus = 'pending' | 'error' | 'success'; ``` -There is no idle state — the work starts before you can render, so `processing` is where it begins. [`useVerifyEmail`](/docs/auth/use-verify-email) reports it as `VerifyEmailStatusType`, an alias of this. +There is no idle state — the work starts before you can render, so `pending` is where it begins. [`useVerifyEmail`](/docs/auth/use-verify-email) reports it as `VerifyEmailStatusType`, an alias of this. Render the three branches and you've handled every case: ```tsx -if (status === 'processing') return ; -if (status === 'failed') return ; +if (status === 'pending') return ; +if (status === 'error') return ; return ; ``` diff --git a/apps/docs/content/docs/guides/email-action-page.mdx b/apps/docs/content/docs/guides/email-action-page.mdx index ebcc602..1ed1c3f 100644 --- a/apps/docs/content/docs/guides/email-action-page.mdx +++ b/apps/docs/content/docs/guides/email-action-page.mdx @@ -52,8 +52,8 @@ Each branch is then the ordinary hook usage: function VerifyEmail({ oobCode }: { oobCode: string | null }) { const { status, error } = useVerifyEmail(oobCode); - if (status === 'processing') return ; - if (status === 'failed') return ; + if (status === 'pending') return ; + if (status === 'error') return ; return

Email verified.

; } diff --git a/apps/playground/app/auth/action/page.tsx b/apps/playground/app/auth/action/page.tsx index 2b02da4..7fb1b9b 100644 --- a/apps/playground/app/auth/action/page.tsx +++ b/apps/playground/app/auth/action/page.tsx @@ -73,7 +73,7 @@ function VerifyEmail({ oobCode }: Props) { onVerified: refreshSession, }); -if (status === "processing") return ;`} +if (status === "pending") return ;`} form={

Status: {status} @@ -81,7 +81,7 @@ if (status === "processing") return ;`} } result={{ status }} error={error} - loading={status === 'processing'} + loading={status === 'pending'} /> ); } diff --git a/apps/playground/components/auth/use-verify-email.tsx b/apps/playground/components/auth/use-verify-email.tsx index a9bf8c6..7e07bba 100644 --- a/apps/playground/components/auth/use-verify-email.tsx +++ b/apps/playground/components/auth/use-verify-email.tsx @@ -19,7 +19,7 @@ export function UseVerifyEmailSection() { onVerified: refreshSession, }); -if (status === "processing") return ;`} +if (status === "pending") return ;`} form={

diff --git a/apps/playground/lib/wrapper-types.ts b/apps/playground/lib/wrapper-types.ts index 6e934d1..d71019d 100644 --- a/apps/playground/lib/wrapper-types.ts +++ b/apps/playground/lib/wrapper-types.ts @@ -40,11 +40,11 @@ export type WrappedLogin = ( /** A page rendering the link-completion result without redeclaring its shape. */ export type CallbackState = - | { phase: Exclude } - | { phase: 'failed'; result: Extract }; + | { phase: Exclude } + | { phase: 'error'; result: Extract }; /** The status vocabulary, shared rather than respelled per hook. */ -export const VERIFY_STATES: VerifyEmailStatusType[] = ['processing', 'success', 'failed']; +export const VERIFY_STATES: VerifyEmailStatusType[] = ['pending', 'error', 'success']; /** Every option interface, reachable by name from the auth entry. */ export type AuthOptions = { diff --git a/packages/firebase-hooks/src/auth/_shared.ts b/packages/firebase-hooks/src/auth/_shared.ts index 6ff43e6..7cd66c8 100644 --- a/packages/firebase-hooks/src/auth/_shared.ts +++ b/packages/firebase-hooks/src/auth/_shared.ts @@ -43,7 +43,7 @@ export type SendEmail = (email: string) => Promise; * them could mail a password reset to someone asking to verify an address. */ export interface AuthSendersProps { - /** `useEmailLinkSignIn`'s `sendLink`. */ + /** `useEmailLinkSignIn`'s `sendEmail`. */ signInLink?: SendEmail; /** `useSendPasswordResetEmail`'s `sendEmail`. */ passwordReset?: SendEmail; diff --git a/packages/firebase-hooks/src/auth/use-email-link-sign-in.test.tsx b/packages/firebase-hooks/src/auth/use-email-link-sign-in.test.tsx index 25c1c9d..b191894 100644 --- a/packages/firebase-hooks/src/auth/use-email-link-sign-in.test.tsx +++ b/packages/firebase-hooks/src/auth/use-email-link-sign-in.test.tsx @@ -58,10 +58,13 @@ describe("useEmailLinkSignIn", () => { expect(result.current.error).toMatch(/actionCodeSettings/); }); - it("a custom sendLink replaces the client-side sender and stores under the custom key", async () => { + it("a custom sendEmail replaces the client-side sender and stores under the custom key", async () => { const sendViaApi = vi.fn(async () => {}); const { result } = renderHook(() => - useEmailLinkSignIn(makeAuth(), { sendLink: sendViaApi, storageKey: "magic-email" }), + useEmailLinkSignIn(makeAuth(), { + sendEmail: sendViaApi, + storageKey: "magic-email", + }), ); await act(async () => { await result.current.sendLink("a@b.c"); diff --git a/packages/firebase-hooks/src/auth/use-email-link-sign-in.ts b/packages/firebase-hooks/src/auth/use-email-link-sign-in.ts index 734759e..d63d24b 100644 --- a/packages/firebase-hooks/src/auth/use-email-link-sign-in.ts +++ b/packages/firebase-hooks/src/auth/use-email-link-sign-in.ts @@ -9,7 +9,7 @@ * @param auth - Firebase `Auth` instance, or null while it initialises * @param options.actionCodeSettings - Where the emailed link lands (`url`, `handleCodeInApp: true`) * @param options.storageKey - localStorage key the address persists under (default: "emailForSignIn") - * @param options.sendLink - Replace the client-side sender (e.g. your API emails the link instead) + * @param options.sendEmail - Replace the client-side sender (e.g. your API emails the link instead) * @param options.onIdToken - Called with the ID token + user after sign-in * @returns `{ sendLink, completeSignIn, loading, error }` * @@ -59,7 +59,7 @@ export interface UseEmailLinkSignInOptionsProps extends HookErrorOptions { */ storageKey?: string; /** Replace the sender — e.g. your own API emails the link instead of Firebase. */ - sendLink?: SendEmail | null; + sendEmail?: SendEmail | null; /** * Called with a freshly minted ID token after sign-in — mint your server * session here. Throwing aborts the flow. Overrides the provider; `null` opts out. @@ -104,7 +104,7 @@ function useEmailLinkSignInBase( options.actionCodeSettings, ); - const send = useResolvedConfig("sendSignInLink", options.sendLink); + const send = useResolvedConfig("sendSignInLink", options.sendEmail); const sendLink = (email: string): Promise => run("send-sign-in-link", "Failed to send sign-in link", async () => { diff --git a/packages/firebase-hooks/src/auth/use-verify-email.test.tsx b/packages/firebase-hooks/src/auth/use-verify-email.test.tsx index 1dccc36..60e9434 100644 --- a/packages/firebase-hooks/src/auth/use-verify-email.test.tsx +++ b/packages/firebase-hooks/src/auth/use-verify-email.test.tsx @@ -30,7 +30,7 @@ describe("useVerifyEmail", () => { it("fails fast on a missing code", () => { const { result } = renderHook(() => useVerifyEmail(makeAuth(), null)); - expect(result.current.status).toBe("failed"); + expect(result.current.status).toBe("error"); expect(result.current.error).toMatch(/missing/i); }); @@ -59,7 +59,7 @@ describe("useVerifyEmail", () => { ); vi.mocked(applyActionCode).mockRejectedValue(firebaseError); const { result } = renderHook(() => useVerifyEmail(makeAuth(), "bad-oob")); - await waitFor(() => expect(result.current.status).toBe("failed")); + await waitFor(() => expect(result.current.status).toBe("error")); expect(result.current.error).toBe("Firebase: Error (auth/invalid-action-code)."); expect(result.current.code).toBe("auth/invalid-action-code"); expect(result.current.cause).toBe(firebaseError); @@ -76,7 +76,7 @@ describe("useVerifyEmail", () => { const { result } = renderHook(() => useVerifyEmail(makeAuth(), "stale-oob"), { wrapper, }); - await waitFor(() => expect(result.current.status).toBe("failed")); + await waitFor(() => expect(result.current.status).toBe("error")); expect(onError).toHaveBeenCalledWith(firebaseError, { action: "verify-email", code: "auth/expired-action-code", @@ -108,11 +108,11 @@ describe("useVerifyEmail argument forms", () => { it("reads a single null as a missing code, not as a missing auth", () => { // searchParams.get("oobCode") returns null when the parameter is absent, // and that is the far more common reason to pass one. Treating it as auth - // would leave the page stuck on "processing" instead of reporting failure. + // would leave the page stuck on "pending" instead of reporting failure. const wrapper = withAuthProvider({ auth: makeAuth() }); const { result } = renderHook(() => useVerifyEmail(null), { wrapper }); - expect(result.current.status).toBe("failed"); + expect(result.current.status).toBe("error"); expect(applyActionCode).not.toHaveBeenCalled(); }); @@ -120,8 +120,8 @@ describe("useVerifyEmail argument forms", () => { const wrapper = withAuthProvider({ auth: makeAuth() }); const { result } = renderHook(() => useVerifyEmail(null, "oob-1"), { wrapper }); - // Still processing: nothing ran, and no failure was reported either. - expect(result.current.status).toBe("processing"); + // Still pending: nothing ran, and no failure was reported either. + expect(result.current.status).toBe("pending"); expect(applyActionCode).not.toHaveBeenCalled(); }); }); diff --git a/packages/firebase-hooks/src/auth/use-verify-email.ts b/packages/firebase-hooks/src/auth/use-verify-email.ts index 898f827..d0a71b3 100644 --- a/packages/firebase-hooks/src/auth/use-verify-email.ts +++ b/packages/firebase-hooks/src/auth/use-verify-email.ts @@ -8,14 +8,14 @@ * @param auth - Firebase `Auth` instance, or null while it initialises * @param oobCode - The code from the verification link, or null while parsing the URL * @param options.onVerified - Runs after a successful verification (e.g. refresh the session) - * @returns `{ status, error, code, cause }` — status is "processing" | "success" | "failed"; + * @returns `{ status, error, code, cause }` — status is "pending" | "error" | "success"; * `code`/`cause` carry the raw failure like every other hook * * @example * const oobCode = searchParams.get("oobCode"); * const { status, error } = useVerifyEmail(auth, oobCode, { onVerified: refreshSession }); - * if (status === "processing") return ; - * if (status === "failed") return ; + * if (status === "pending") return ; + * if (status === "error") return ; * return ; */ @@ -80,7 +80,7 @@ function useVerifyEmailBase( oobCode: string | null, options: UseVerifyEmailOptionsProps, ) { - const [status, setStatus] = useState("processing"); + const [status, setStatus] = useState("pending"); const [error, setError] = useState(null); const [code, setCode] = useState(null); const [cause, setCause] = useState(null); @@ -93,7 +93,7 @@ function useVerifyEmailBase( useEffect(() => { if (!auth) return; if (!oobCode) { - setStatus("failed"); + setStatus("error"); setError("Verification code is missing"); return; } @@ -116,7 +116,7 @@ function useVerifyEmailBase( }) .catch((err: unknown) => { const message = resolveMessage(err, "Failed to verify email"); - setStatus("failed"); + setStatus("error"); setError(message); setCode(getFirebaseErrorCode(err)); setCause(err); diff --git a/packages/firebase-hooks/src/core/types.ts b/packages/firebase-hooks/src/core/types.ts index 56494f1..ba9097b 100644 --- a/packages/firebase-hooks/src/core/types.ts +++ b/packages/firebase-hooks/src/core/types.ts @@ -23,10 +23,10 @@ export interface HookErrorOptions { } /** - * Where a hook that acts on mount has got to. Starts at `processing` — there is - * no idle state, because the work begins before you can render. + * Where a hook that acts on mount has got to. Starts at `pending` — there is no + * idle state, because the work begins before you can render. */ -export type AsyncStatus = "processing" | "success" | "failed"; +export type AsyncStatus = "pending" | "error" | "success"; /** What failed, for the global `onError` observer. */ export interface HookErrorContext { From 999284b38e1f5a4f9559b4dfafa349b3a7f088a9 Mon Sep 17 00:00:00 2001 From: timonwa Date: Tue, 8 Sep 2026 22:48:10 +0100 Subject: [PATCH 13/23] docs(guides): cover every mode the action URL receives MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit verifyAndChangeEmail is what useUpdateEmail's own link arrives as, and recoverEmail arrives unprompted after any address change. Both apply an action code, so both route to useVerifyEmail — the guide sent them to a dead end. --- .../content/docs/guides/email-action-page.mdx | 32 +++++++++++++------ 1 file changed, 22 insertions(+), 10 deletions(-) diff --git a/apps/docs/content/docs/guides/email-action-page.mdx b/apps/docs/content/docs/guides/email-action-page.mdx index 1ed1c3f..1477cdf 100644 --- a/apps/docs/content/docs/guides/email-action-page.mdx +++ b/apps/docs/content/docs/guides/email-action-page.mdx @@ -3,15 +3,19 @@ title: One page for every emailed link description: Firebase sends sign-in, password-reset and verification links to a single URL. Dispatch on mode and hand each one to its hook. --- -Firebase emails three kinds of link, and by default they all land on Firebase's own hosted pages. To handle them in your app instead, you point Firebase at **one** URL — every link goes there, with a `mode` parameter saying which flow it is. +Every link Firebase emails lands on Firebase's own hosted pages by default. To handle them in your app instead, you point Firebase at **one** URL — every link goes there, with a `mode` parameter saying which flow it is. This is Firebase's [custom email action handler](https://firebase.google.com/docs/auth/custom-email-handler) pattern; the page below is that handler, built from the hooks. So you write one page that dispatches on `mode`, and a hook handles each branch. -| `mode` | Hook | Also in the URL | +| `mode` | Sent by | Hook | | --- | --- | --- | -| `signIn` | [`useEmailLinkSignIn`](/docs/auth/use-email-link-sign-in) | the whole URL is the credential | -| `resetPassword` | [`useConfirmPasswordReset`](/docs/auth/use-confirm-password-reset) | `oobCode` | -| `verifyEmail` | [`useVerifyEmail`](/docs/auth/use-verify-email) | `oobCode` | +| `signIn` | [`useEmailLinkSignIn`](/docs/auth/use-email-link-sign-in) `sendLink` | `useEmailLinkSignIn` `completeSignIn` | +| `resetPassword` | [`useSendPasswordResetEmail`](/docs/auth/use-send-password-reset-email) | [`useConfirmPasswordReset`](/docs/auth/use-confirm-password-reset) | +| `verifyEmail` | [`useSendEmailVerification`](/docs/auth/use-send-email-verification), [`useSignup`](/docs/auth/use-signup) | [`useVerifyEmail`](/docs/auth/use-verify-email) | +| `verifyAndChangeEmail` | [`useUpdateEmail`](/docs/auth/use-update-email) | `useVerifyEmail` | +| `recoverEmail` | Firebase, unprompted, after an address change | `useVerifyEmail` | + +The last two are easy to miss. `useUpdateEmail` uses `verifyBeforeUpdateEmail`, so its link arrives as `verifyAndChangeEmail`, not `verifyEmail` — a handler that only knows the first three sends your own users to a dead end. And `recoverEmail` goes to the *old* address whenever an email changes, so the owner can undo it; you never send it, but it arrives. Both apply an action code, which is what `useVerifyEmail` does. ## The page @@ -39,10 +43,18 @@ function AuthAction() { const mode = params.get('mode'); const oobCode = params.get('oobCode'); - if (mode === 'signIn') return ; - if (mode === 'resetPassword') return ; - if (mode === 'verifyEmail') return ; - return

This link isn't one we recognise.

; + switch (mode) { + case 'signIn': + return ; + case 'resetPassword': + return ; + case 'verifyEmail': + case 'verifyAndChangeEmail': + case 'recoverEmail': + return ; + default: + return

This link isn't one we recognise.

; + } } ``` @@ -76,7 +88,7 @@ This is the step that silently half-works. The action URL is configured **per em Set it for the sign-in template only and password resets keep going to Firebase's hosted page, with no error anywhere to tell you. If two of your three flows work and one doesn't, this is why. -Both `resetPassword` and `verifyEmail` need it. `signIn` is the exception: [`useEmailLinkSignIn`](/docs/auth/use-email-link-sign-in) passes its return URL in `actionCodeSettings` at send time, so it doesn't depend on the console setting. +Every mode except `signIn` needs it — password reset, email verification, and email change each have their own template. `signIn` is the exception: [`useEmailLinkSignIn`](/docs/auth/use-email-link-sign-in) passes its return URL in `actionCodeSettings` at send time, so it doesn't depend on the console setting. Add your domain under **Authentication → Settings → Authorized domains**. `localhost` is there by default. From 07ee8f52816f65a2e9c73b2646bb0e419dbec3de Mon Sep 17 00:00:00 2001 From: timonwa Date: Tue, 8 Sep 2026 22:52:11 +0100 Subject: [PATCH 14/23] refactor(auth)!: name types by the house and industry convention MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Props is for component props and unions take no Type suffix — per the repo's own naming rule and TanStack's UseQueryOptions / QueryStatus / UseQueryResult. So UseXOptions, VerifyEmailStatus, UseAuthResult, AuthSenders, EmailSender. --- .changeset/export-result-types.md | 4 +- .changeset/export-wrapper-types.md | 8 +-- CONTRIBUTING.md | 2 +- .../docs/auth/use-anonymous-sign-in.mdx | 2 +- .../docs/auth/use-custom-token-sign-in.mdx | 2 +- .../content/docs/auth/use-delete-account.mdx | 2 +- .../docs/auth/use-email-link-sign-in.mdx | 2 +- apps/docs/content/docs/auth/use-login.mdx | 2 +- apps/docs/content/docs/auth/use-logout.mdx | 2 +- .../content/docs/auth/use-oauth-sign-in.mdx | 2 +- .../content/docs/auth/use-phone-sign-in.mdx | 2 +- .../docs/auth/use-send-email-verification.mdx | 2 +- .../auth/use-send-password-reset-email.mdx | 2 +- apps/docs/content/docs/auth/use-signup.mdx | 2 +- .../content/docs/auth/use-update-email.mdx | 2 +- .../content/docs/auth/use-verify-email.mdx | 4 +- apps/docs/content/docs/core/hook-result.mdx | 2 +- .../content/docs/guides/email-action-page.mdx | 2 +- apps/docs/content/docs/how-hooks-work.mdx | 4 +- apps/playground/lib/wrapper-types.ts | 60 +++++++++---------- packages/firebase-hooks/src/auth/_shared.ts | 16 ++--- .../firebase-hooks/src/auth/auth-provider.tsx | 10 ++-- packages/firebase-hooks/src/auth/index.ts | 32 +++++----- .../src/auth/use-anonymous-sign-in.ts | 15 ++--- .../src/auth/use-custom-token-sign-in.ts | 12 ++-- .../src/auth/use-delete-account.ts | 12 ++-- .../src/auth/use-email-link-sign-in.ts | 19 +++--- packages/firebase-hooks/src/auth/use-login.ts | 12 ++-- .../firebase-hooks/src/auth/use-logout.ts | 14 ++--- .../src/auth/use-oauth-sign-in.ts | 12 ++-- .../src/auth/use-phone-sign-in.ts | 12 ++-- .../src/auth/use-send-email-verification.ts | 16 ++--- .../src/auth/use-send-password-reset-email.ts | 16 ++--- .../firebase-hooks/src/auth/use-signup.ts | 14 ++--- .../src/auth/use-update-email.ts | 12 ++-- .../src/auth/use-verify-email.ts | 20 +++---- 36 files changed, 172 insertions(+), 182 deletions(-) diff --git a/.changeset/export-result-types.md b/.changeset/export-result-types.md index b6bfe6a..d443c19 100644 --- a/.changeset/export-result-types.md +++ b/.changeset/export-result-types.md @@ -5,8 +5,8 @@ Two result types are no longer module-private: - `CompleteSignInResult` — what `useEmailLinkSignIn`'s `completeSignIn` resolves to, so a callback page can annotate the function that handles it -- `VerifyEmailStatusType` — `useVerifyEmail`'s status, instead of re-declaring the three states in app code +- `VerifyEmailStatus` — `useVerifyEmail`'s status, instead of re-declaring the three states in app code Both ship from `@timonwa/firebase-hooks/auth`. -The status vocabulary is also now shared. `AsyncStatus` (`"processing" | "success" | "failed"`) ships from the root entry, and `VerifyEmailStatusType` is an alias of it, so hooks that act on mount report the same three states rather than each spelling them differently. There is no idle state: the work starts before a render, which is why `processing` is the initial value. +The status vocabulary is also now shared. `AsyncStatus` (`"processing" | "success" | "failed"`) ships from the root entry, and `VerifyEmailStatus` is an alias of it, so hooks that act on mount report the same three states rather than each spelling them differently. There is no idle state: the work starts before a render, which is why `processing` is the initial value. diff --git a/.changeset/export-wrapper-types.md b/.changeset/export-wrapper-types.md index 534ce2e..f4944f6 100644 --- a/.changeset/export-wrapper-types.md +++ b/.changeset/export-wrapper-types.md @@ -4,13 +4,13 @@ Every type needed to write a wrapper around a hook is now reachable from `@timonwa/firebase-hooks/auth`: -- all 13 `Use*OptionsProps` interfaces, so a wrapper can accept and forward a hook's options without restating them -- `AuthProviderProps` and `AuthContextValueProps` (what `useAuth` returns), for an app provider layered over this one +- all 13 `Use*Options` interfaces, so a wrapper can accept and forward a hook's options without restating them +- `AuthProviderProps` and `UseAuthResult` (what `useAuth` returns), for an app provider layered over this one ```tsx -import { useLogin, type UseLoginOptionsProps } from "@timonwa/firebase-hooks/auth"; +import { useLogin, type UseLoginOptions } from "@timonwa/firebase-hooks/auth"; -export function useAppLogin(options?: UseLoginOptionsProps) { +export function useAppLogin(options?: UseLoginOptions) { return useLogin(options); } ``` diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 03758f3..1a5c10b 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -24,7 +24,7 @@ This is a pnpm workspace. The published package lives in `packages/firebase-hook - **One folder per service** (`src/core/`, `src/auth/`, later `src/firestore/`, `src/storage/`), each with its own `index.ts` entry barrel. **One file per hook**, kebab-cased after it — `src/auth/use-login.ts`. Start the file with a `"use client"` directive and a JSDoc block; the JSDoc is what editors show, so keep it agreeing with the README. Internals a service shares live in that folder's `_shared.ts` and never reach the barrel. - **Follow the shared contract.** `auth: Auth | null` first argument; actions resolve to `HookResult` and never throw (`useAuthTask` gives you the skeleton); sensitive operations reauthenticate first. -- **Options go in an exported `UseOptionsProps` interface** extending `HookErrorOptions`, with a TSDoc line on every field it declares itself (and `@defaultValue` where there is one). The docs site generates its options table from that interface, so an undocumented field ships an empty cell. Exported for the generator's sake — keep it out of the barrel, so the published types don't change. +- **Options go in an exported `UseOptions` interface** extending `HookErrorOptions`, with a TSDoc line on every field it declares itself (and `@defaultValue` where there is one). The docs site generates its options table from that interface, so an undocumented field ships an empty cell. Re-export it from the barrel too, so an app can type a wrapper around the hook without restating the shape. - **Export it explicitly** from its service's entry barrel (`src/auth/index.ts` for auth), one line per file, alphabetical. The root entry (`src/core/index.ts`) carries only the service-agnostic core — nothing service-specific is ever added to it; a new service gets a new folder + subpath entry. - **Document it in the same change** — add a page under `apps/docs/content/docs/auth/` and list it in that folder's `meta.json`. Follow the shape of the existing pages: prose intro, example, `## Returns`, then `` for the options. The table generates from the option interface's TSDoc, so document each field there rather than hand-writing a table. - **Add it to the playground in the same change** — a section component under `apps/playground/components//`, rendered from that service's page, and its name in the right group in `apps/playground/lib/hooks-map.ts` so it appears in the sidebar. See below. diff --git a/apps/docs/content/docs/auth/use-anonymous-sign-in.mdx b/apps/docs/content/docs/auth/use-anonymous-sign-in.mdx index d3c8fa6..945eeb0 100644 --- a/apps/docs/content/docs/auth/use-anonymous-sign-in.mdx +++ b/apps/docs/content/docs/auth/use-anonymous-sign-in.mdx @@ -21,5 +21,5 @@ The resulting user can be upgraded to a real account **without losing their data diff --git a/apps/docs/content/docs/auth/use-custom-token-sign-in.mdx b/apps/docs/content/docs/auth/use-custom-token-sign-in.mdx index 08465f8..f3ebe04 100644 --- a/apps/docs/content/docs/auth/use-custom-token-sign-in.mdx +++ b/apps/docs/content/docs/auth/use-custom-token-sign-in.mdx @@ -20,5 +20,5 @@ await signIn(token); diff --git a/apps/docs/content/docs/auth/use-delete-account.mdx b/apps/docs/content/docs/auth/use-delete-account.mdx index c0b564b..7da2f5d 100644 --- a/apps/docs/content/docs/auth/use-delete-account.mdx +++ b/apps/docs/content/docs/auth/use-delete-account.mdx @@ -28,5 +28,5 @@ Deletion requires a recent sign-in. Pass `currentPassword` for automatic reauthe diff --git a/apps/docs/content/docs/auth/use-email-link-sign-in.mdx b/apps/docs/content/docs/auth/use-email-link-sign-in.mdx index 7941196..fc0436e 100644 --- a/apps/docs/content/docs/auth/use-email-link-sign-in.mdx +++ b/apps/docs/content/docs/auth/use-email-link-sign-in.mdx @@ -40,7 +40,7 @@ const result = await completeSignIn(url, confirmedEmail); All three emailed links land on one URL — see [One page for every emailed link](/docs/guides/email-action-page) for the page that dispatches on `mode`. diff --git a/apps/docs/content/docs/auth/use-login.mdx b/apps/docs/content/docs/auth/use-login.mdx index 020d0cb..27d312a 100644 --- a/apps/docs/content/docs/auth/use-login.mdx +++ b/apps/docs/content/docs/auth/use-login.mdx @@ -27,7 +27,7 @@ function LoginForm() { ## Returns diff --git a/apps/docs/content/docs/auth/use-logout.mdx b/apps/docs/content/docs/auth/use-logout.mdx index 6da4cb6..2ac410a 100644 --- a/apps/docs/content/docs/auth/use-logout.mdx +++ b/apps/docs/content/docs/auth/use-logout.mdx @@ -21,5 +21,5 @@ const { logout, loading } = useLogout({ onBeforeSignOut: clearSession }); diff --git a/apps/docs/content/docs/auth/use-oauth-sign-in.mdx b/apps/docs/content/docs/auth/use-oauth-sign-in.mdx index 4fadad2..f1ecb31 100644 --- a/apps/docs/content/docs/auth/use-oauth-sign-in.mdx +++ b/apps/docs/content/docs/auth/use-oauth-sign-in.mdx @@ -33,5 +33,5 @@ The hook also completes a pending redirect on mount via `getRedirectResult`, run diff --git a/apps/docs/content/docs/auth/use-phone-sign-in.mdx b/apps/docs/content/docs/auth/use-phone-sign-in.mdx index 08a042d..115145a 100644 --- a/apps/docs/content/docs/auth/use-phone-sign-in.mdx +++ b/apps/docs/content/docs/auth/use-phone-sign-in.mdx @@ -33,5 +33,5 @@ Created and cleaned up for you — you never construct a `RecaptchaVerifier`. Ch diff --git a/apps/docs/content/docs/auth/use-send-email-verification.mdx b/apps/docs/content/docs/auth/use-send-email-verification.mdx index 9aa7e18..caf2f0e 100644 --- a/apps/docs/content/docs/auth/use-send-email-verification.mdx +++ b/apps/docs/content/docs/auth/use-send-email-verification.mdx @@ -37,5 +37,5 @@ It receives the signed-in user's address, because `send()` takes no arguments. A diff --git a/apps/docs/content/docs/auth/use-send-password-reset-email.mdx b/apps/docs/content/docs/auth/use-send-password-reset-email.mdx index 1dbb60b..e38735a 100644 --- a/apps/docs/content/docs/auth/use-send-password-reset-email.mdx +++ b/apps/docs/content/docs/auth/use-send-password-reset-email.mdx @@ -41,5 +41,5 @@ Same option, same semantics, on [`useSendEmailVerification`](/docs/auth/use-send diff --git a/apps/docs/content/docs/auth/use-signup.mdx b/apps/docs/content/docs/auth/use-signup.mdx index a4d0292..a2156f0 100644 --- a/apps/docs/content/docs/auth/use-signup.mdx +++ b/apps/docs/content/docs/auth/use-signup.mdx @@ -26,5 +26,5 @@ Server-first signups — where your API creates the record and the client signs diff --git a/apps/docs/content/docs/auth/use-update-email.mdx b/apps/docs/content/docs/auth/use-update-email.mdx index bf5c906..1827950 100644 --- a/apps/docs/content/docs/auth/use-update-email.mdx +++ b/apps/docs/content/docs/auth/use-update-email.mdx @@ -32,5 +32,5 @@ Pass `currentPassword` for automatic reauthentication. Omit it for OAuth-only ac diff --git a/apps/docs/content/docs/auth/use-verify-email.mdx b/apps/docs/content/docs/auth/use-verify-email.mdx index e241b19..6d6fbc6 100644 --- a/apps/docs/content/docs/auth/use-verify-email.mdx +++ b/apps/docs/content/docs/auth/use-verify-email.mdx @@ -17,7 +17,7 @@ return ; ## Returns -`status` is `"pending"`, `"success"`, or `"error"` — exported as `VerifyEmailStatusType`, and the same [`AsyncStatus`](/docs/core/hook-result#asyncstatus) vocabulary every status in the package uses. `error` carries the message on failure. +`status` is `"pending"`, `"success"`, or `"error"` — exported as `VerifyEmailStatus`, and the same [`AsyncStatus`](/docs/core/hook-result#asyncstatus) vocabulary every status in the package uses. `error` carries the message on failure. ## Notes @@ -29,7 +29,7 @@ After verifying, the current user is reloaded and the token refreshed so `emailV All three emailed links land on one URL — see [One page for every emailed link](/docs/guides/email-action-page) for the page that dispatches on `mode`. diff --git a/apps/docs/content/docs/core/hook-result.mdx b/apps/docs/content/docs/core/hook-result.mdx index b2912db..22735df 100644 --- a/apps/docs/content/docs/core/hook-result.mdx +++ b/apps/docs/content/docs/core/hook-result.mdx @@ -60,7 +60,7 @@ result.user; // User — narrowed, no optional chaining needed type AsyncStatus = 'pending' | 'error' | 'success'; ``` -There is no idle state — the work starts before you can render, so `pending` is where it begins. [`useVerifyEmail`](/docs/auth/use-verify-email) reports it as `VerifyEmailStatusType`, an alias of this. +There is no idle state — the work starts before you can render, so `pending` is where it begins. [`useVerifyEmail`](/docs/auth/use-verify-email) reports it as `VerifyEmailStatus`, an alias of this. Render the three branches and you've handled every case: diff --git a/apps/docs/content/docs/guides/email-action-page.mdx b/apps/docs/content/docs/guides/email-action-page.mdx index 1477cdf..f21ce05 100644 --- a/apps/docs/content/docs/guides/email-action-page.mdx +++ b/apps/docs/content/docs/guides/email-action-page.mdx @@ -15,7 +15,7 @@ So you write one page that dispatches on `mode`, and a hook handles each branch. | `verifyAndChangeEmail` | [`useUpdateEmail`](/docs/auth/use-update-email) | `useVerifyEmail` | | `recoverEmail` | Firebase, unprompted, after an address change | `useVerifyEmail` | -The last two are easy to miss. `useUpdateEmail` uses `verifyBeforeUpdateEmail`, so its link arrives as `verifyAndChangeEmail`, not `verifyEmail` — a handler that only knows the first three sends your own users to a dead end. And `recoverEmail` goes to the *old* address whenever an email changes, so the owner can undo it; you never send it, but it arrives. Both apply an action code, which is what `useVerifyEmail` does. +The last two are easy to miss. `useUpdateEmail` uses `verifyBeforeUpdateEmail`, so its link arrives as `verifyAndChangeEmail`, not `verifyEmail` — a handler that only knows the first three sends your own users to a dead end. And `recoverEmail` goes to the _old_ address whenever an email changes, so the owner can undo it; you never send it, but it arrives. Both apply an action code, which is what `useVerifyEmail` does. ## The page diff --git a/apps/docs/content/docs/how-hooks-work.mdx b/apps/docs/content/docs/how-hooks-work.mdx index cea3db2..5885df9 100644 --- a/apps/docs/content/docs/how-hooks-work.mdx +++ b/apps/docs/content/docs/how-hooks-work.mdx @@ -54,9 +54,9 @@ See [Error handling](/docs/guides/error-handling) for the full model. Everything a wrapper needs to name is exported. Each hook's options interface comes from the auth entry, so you can accept and forward them without restating the shape: ```tsx -import { useLogin, type UseLoginOptionsProps } from '@timonwa/firebase-hooks/auth'; +import { useLogin, type UseLoginOptions } from '@timonwa/firebase-hooks/auth'; -export function useAppLogin(options?: UseLoginOptionsProps) { +export function useAppLogin(options?: UseLoginOptions) { const { login, loading, error } = useLogin(options); // …your own state on top return { login, loading, error }; diff --git a/apps/playground/lib/wrapper-types.ts b/apps/playground/lib/wrapper-types.ts index d71019d..3f14ef6 100644 --- a/apps/playground/lib/wrapper-types.ts +++ b/apps/playground/lib/wrapper-types.ts @@ -5,27 +5,27 @@ import type { AsyncStatus, HookResult } from '@timonwa/firebase-hooks'; import type { - AuthContextValueProps, + UseAuthResult, AuthProviderProps, CompleteSignInResult, - VerifyEmailStatusType, - UseAnonymousSignInOptionsProps, - UseCustomTokenSignInOptionsProps, - UseDeleteAccountOptionsProps, - UseEmailLinkSignInOptionsProps, - UseLoginOptionsProps, - UseLogoutOptionsProps, - UseOAuthSignInOptionsProps, - UsePhoneSignInOptionsProps, - UseSendEmailVerificationOptionsProps, - UseSendPasswordResetEmailOptionsProps, - UseSignupOptionsProps, - UseUpdateEmailOptionsProps, - UseVerifyEmailOptionsProps, + VerifyEmailStatus, + UseAnonymousSignInOptions, + UseCustomTokenSignInOptions, + UseDeleteAccountOptions, + UseEmailLinkSignInOptions, + UseLoginOptions, + UseLogoutOptions, + UseOAuthSignInOptions, + UsePhoneSignInOptions, + UseSendEmailVerificationOptions, + UseSendPasswordResetEmailOptions, + UseSignupOptions, + UseUpdateEmailOptions, + UseVerifyEmailOptions, } from '@timonwa/firebase-hooks/auth'; /** The wrapper shape the docs recommend for server-fetched user records. */ -export type AppAuth = AuthContextValueProps & { record: { plan: string } | null }; +export type AppAuth = UseAuthResult & { record: { plan: string } | null }; /** An app provider layered on top, reusing the package's own props. */ export type AppAuthProviderProps = Omit & { @@ -44,21 +44,21 @@ export type CallbackState = | { phase: 'error'; result: Extract }; /** The status vocabulary, shared rather than respelled per hook. */ -export const VERIFY_STATES: VerifyEmailStatusType[] = ['pending', 'error', 'success']; +export const VERIFY_STATES: VerifyEmailStatus[] = ['pending', 'error', 'success']; /** Every option interface, reachable by name from the auth entry. */ export type AuthOptions = { - anonymousSignIn: UseAnonymousSignInOptionsProps; - customTokenSignIn: UseCustomTokenSignInOptionsProps; - deleteAccount: UseDeleteAccountOptionsProps; - emailLinkSignIn: UseEmailLinkSignInOptionsProps; - login: UseLoginOptionsProps; - logout: UseLogoutOptionsProps; - oauthSignIn: UseOAuthSignInOptionsProps; - phoneSignIn: UsePhoneSignInOptionsProps; - sendEmailVerification: UseSendEmailVerificationOptionsProps; - sendPasswordResetEmail: UseSendPasswordResetEmailOptionsProps; - signup: UseSignupOptionsProps; - updateEmail: UseUpdateEmailOptionsProps; - verifyEmail: UseVerifyEmailOptionsProps; + anonymousSignIn: UseAnonymousSignInOptions; + customTokenSignIn: UseCustomTokenSignInOptions; + deleteAccount: UseDeleteAccountOptions; + emailLinkSignIn: UseEmailLinkSignInOptions; + login: UseLoginOptions; + logout: UseLogoutOptions; + oauthSignIn: UseOAuthSignInOptions; + phoneSignIn: UsePhoneSignInOptions; + sendEmailVerification: UseSendEmailVerificationOptions; + sendPasswordResetEmail: UseSendPasswordResetEmailOptions; + signup: UseSignupOptions; + updateEmail: UseUpdateEmailOptions; + verifyEmail: UseVerifyEmailOptions; }; diff --git a/packages/firebase-hooks/src/auth/_shared.ts b/packages/firebase-hooks/src/auth/_shared.ts index 7cd66c8..75a3617 100644 --- a/packages/firebase-hooks/src/auth/_shared.ts +++ b/packages/firebase-hooks/src/auth/_shared.ts @@ -35,20 +35,20 @@ function rawErrorMessage(error: unknown, fallback: string): string { } /** Emails a link on the app's behalf, in place of Firebase's client SDK. */ -export type SendEmail = (email: string) => Promise; +export type EmailSender = (email: string) => Promise; /** * Your own sender per emailed flow. Each one replaces the client-side send for * that flow only — the three send different emails, so one sender for all of * them could mail a password reset to someone asking to verify an address. */ -export interface AuthSendersProps { +export interface AuthSenders { /** `useEmailLinkSignIn`'s `sendEmail`. */ - signInLink?: SendEmail; + signInLink?: EmailSender; /** `useSendPasswordResetEmail`'s `sendEmail`. */ - passwordReset?: SendEmail; + passwordReset?: EmailSender; /** `useSendEmailVerification`'s `sendEmail`. */ - emailVerification?: SendEmail; + emailVerification?: EmailSender; } /** Provider-level configuration shared with every hook below the provider. */ @@ -62,9 +62,9 @@ export interface AuthConfigContextValueProps { onError?: (error: unknown, context: HookErrorContext) => void; // Flattened from the provider's `senders` prop, so each resolves through // useResolvedConfig like every other inherited option. - sendSignInLink?: SendEmail; - sendPasswordReset?: SendEmail; - sendEmailVerification?: SendEmail; + sendSignInLink?: EmailSender; + sendPasswordReset?: EmailSender; + sendEmailVerification?: EmailSender; } export const AuthConfigContext = createContext( diff --git a/packages/firebase-hooks/src/auth/auth-provider.tsx b/packages/firebase-hooks/src/auth/auth-provider.tsx index 25c5899..687f31d 100644 --- a/packages/firebase-hooks/src/auth/auth-provider.tsx +++ b/packages/firebase-hooks/src/auth/auth-provider.tsx @@ -44,12 +44,12 @@ import { } from "react"; import { AuthConfigContext, - type AuthSendersProps, + type AuthSenders, type HookErrorContext, type OnIdToken, } from "./_shared"; -export interface AuthContextValueProps { +export interface UseAuthResult { firebaseUser: User | null; /** Custom claims from the current ID token; null while signed out or loading. */ claims: Record | null; @@ -57,7 +57,7 @@ export interface AuthContextValueProps { isLoading: boolean; } -const AuthContext = createContext(undefined); +const AuthContext = createContext(undefined); export interface AuthProviderProps { /** The Firebase `Auth` instance, or null while it initialises. */ @@ -76,7 +76,7 @@ export interface AuthProviderProps { * Your own sender per emailed flow, so the sends go through your API rather * than the browser. A hook's own option overrides its entry here. */ - senders?: AuthSendersProps; + senders?: AuthSenders; children: ReactNode; } @@ -162,7 +162,7 @@ export function AuthProvider({ ); } -export function useAuth(): AuthContextValueProps { +export function useAuth(): UseAuthResult { const ctx = useContext(AuthContext); if (!ctx) throw new Error("useAuth must be used inside "); return ctx; diff --git a/packages/firebase-hooks/src/auth/index.ts b/packages/firebase-hooks/src/auth/index.ts index a04dd10..da89b4d 100644 --- a/packages/firebase-hooks/src/auth/index.ts +++ b/packages/firebase-hooks/src/auth/index.ts @@ -4,53 +4,53 @@ // Shared shapes (HookResult, HookErrorOptions, HookErrorContext, AsyncStatus) // ship from the core entry instead: every service returns them. -export type { AuthSendersProps, OnIdToken, SendEmail } from "./_shared.js"; +export type { AuthSenders, EmailSender, OnIdToken } from "./_shared.js"; export { AUTH_ERROR_MESSAGES } from "./auth-error-messages.js"; export { - type AuthContextValueProps, AuthProvider, type AuthProviderProps, + type UseAuthResult, useAuth, } from "./auth-provider.js"; export { - type UseAnonymousSignInOptionsProps, + type UseAnonymousSignInOptions, useAnonymousSignIn, } from "./use-anonymous-sign-in.js"; export { useConfirmPasswordReset } from "./use-confirm-password-reset.js"; export { - type UseCustomTokenSignInOptionsProps, + type UseCustomTokenSignInOptions, useCustomTokenSignIn, } from "./use-custom-token-sign-in.js"; export { - type UseDeleteAccountOptionsProps, + type UseDeleteAccountOptions, useDeleteAccount, } from "./use-delete-account.js"; export { type CompleteSignInResult, - type UseEmailLinkSignInOptionsProps, + type UseEmailLinkSignInOptions, useEmailLinkSignIn, } from "./use-email-link-sign-in.js"; export { useLinkProvider } from "./use-link-provider.js"; -export { type UseLoginOptionsProps, useLogin } from "./use-login.js"; -export { type UseLogoutOptionsProps, useLogout } from "./use-logout.js"; -export { type UseOAuthSignInOptionsProps, useOAuthSignIn } from "./use-oauth-sign-in.js"; -export { type UsePhoneSignInOptionsProps, usePhoneSignIn } from "./use-phone-sign-in.js"; +export { type UseLoginOptions, useLogin } from "./use-login.js"; +export { type UseLogoutOptions, useLogout } from "./use-logout.js"; +export { type UseOAuthSignInOptions, useOAuthSignIn } from "./use-oauth-sign-in.js"; +export { type UsePhoneSignInOptions, usePhoneSignIn } from "./use-phone-sign-in.js"; export { useReauthenticate } from "./use-reauthenticate.js"; export { - type UseSendEmailVerificationOptionsProps, + type UseSendEmailVerificationOptions, useSendEmailVerification, } from "./use-send-email-verification.js"; export { - type UseSendPasswordResetEmailOptionsProps, + type UseSendPasswordResetEmailOptions, useSendPasswordResetEmail, } from "./use-send-password-reset-email.js"; -export { type UseSignupOptionsProps, useSignup } from "./use-signup.js"; +export { type UseSignupOptions, useSignup } from "./use-signup.js"; export { useUnlinkProvider } from "./use-unlink-provider.js"; -export { type UseUpdateEmailOptionsProps, useUpdateEmail } from "./use-update-email.js"; +export { type UseUpdateEmailOptions, useUpdateEmail } from "./use-update-email.js"; export { useUpdatePassword } from "./use-update-password.js"; export { useUpdateProfile } from "./use-update-profile.js"; export { - type UseVerifyEmailOptionsProps, + type UseVerifyEmailOptions, useVerifyEmail, - type VerifyEmailStatusType, + type VerifyEmailStatus, } from "./use-verify-email.js"; diff --git a/packages/firebase-hooks/src/auth/use-anonymous-sign-in.ts b/packages/firebase-hooks/src/auth/use-anonymous-sign-in.ts index 27e5b92..158895d 100644 --- a/packages/firebase-hooks/src/auth/use-anonymous-sign-in.ts +++ b/packages/firebase-hooks/src/auth/use-anonymous-sign-in.ts @@ -30,7 +30,7 @@ import { useResolvedConfig, } from "./_shared"; -export interface UseAnonymousSignInOptionsProps extends HookErrorOptions { +export interface UseAnonymousSignInOptions extends HookErrorOptions { /** * Called with a freshly minted ID token after sign-in — mint your server * session here. Throwing aborts the flow. Overrides the provider; `null` opts out. @@ -39,23 +39,20 @@ export interface UseAnonymousSignInOptionsProps extends HookErrorOptions { } export function useAnonymousSignIn( - options?: UseAnonymousSignInOptionsProps, + options?: UseAnonymousSignInOptions, ): ReturnType; export function useAnonymousSignIn( auth: Auth | null, - options?: UseAnonymousSignInOptionsProps, + options?: UseAnonymousSignInOptions, ): ReturnType; export function useAnonymousSignIn( - authOrOptions?: Auth | null | UseAnonymousSignInOptionsProps, - maybeOptions?: UseAnonymousSignInOptionsProps, + authOrOptions?: Auth | null | UseAnonymousSignInOptions, + maybeOptions?: UseAnonymousSignInOptions, ) { return useAnonymousSignInBase(...useAuthArgs(authOrOptions, maybeOptions)); } -function useAnonymousSignInBase( - auth: Auth | null, - options: UseAnonymousSignInOptionsProps, -) { +function useAnonymousSignInBase(auth: Auth | null, options: UseAnonymousSignInOptions) { const { loading, error, run } = useAuthTask(options); const onIdToken = useResolvedConfig("onIdToken", options.onIdToken); diff --git a/packages/firebase-hooks/src/auth/use-custom-token-sign-in.ts b/packages/firebase-hooks/src/auth/use-custom-token-sign-in.ts index 7e8e7ca..eb90eea 100644 --- a/packages/firebase-hooks/src/auth/use-custom-token-sign-in.ts +++ b/packages/firebase-hooks/src/auth/use-custom-token-sign-in.ts @@ -31,7 +31,7 @@ import { useResolvedConfig, } from "./_shared"; -export interface UseCustomTokenSignInOptionsProps extends HookErrorOptions { +export interface UseCustomTokenSignInOptions extends HookErrorOptions { /** * Called with a freshly minted ID token after sign-in — mint your server * session here. Throwing aborts the flow. Overrides the provider; `null` opts out. @@ -40,22 +40,22 @@ export interface UseCustomTokenSignInOptionsProps extends HookErrorOptions { } export function useCustomTokenSignIn( - options?: UseCustomTokenSignInOptionsProps, + options?: UseCustomTokenSignInOptions, ): ReturnType; export function useCustomTokenSignIn( auth: Auth | null, - options?: UseCustomTokenSignInOptionsProps, + options?: UseCustomTokenSignInOptions, ): ReturnType; export function useCustomTokenSignIn( - authOrOptions?: Auth | null | UseCustomTokenSignInOptionsProps, - maybeOptions?: UseCustomTokenSignInOptionsProps, + authOrOptions?: Auth | null | UseCustomTokenSignInOptions, + maybeOptions?: UseCustomTokenSignInOptions, ) { return useCustomTokenSignInBase(...useAuthArgs(authOrOptions, maybeOptions)); } function useCustomTokenSignInBase( auth: Auth | null, - options: UseCustomTokenSignInOptionsProps, + options: UseCustomTokenSignInOptions, ) { const { loading, error, run } = useAuthTask(options); const onIdToken = useResolvedConfig("onIdToken", options.onIdToken); diff --git a/packages/firebase-hooks/src/auth/use-delete-account.ts b/packages/firebase-hooks/src/auth/use-delete-account.ts index 52144d1..096bd49 100644 --- a/packages/firebase-hooks/src/auth/use-delete-account.ts +++ b/packages/firebase-hooks/src/auth/use-delete-account.ts @@ -31,7 +31,7 @@ import { useAuthTask, } from "./_shared"; -export interface UseDeleteAccountOptionsProps extends HookErrorOptions { +export interface UseDeleteAccountOptions extends HookErrorOptions { /** * Runs while the user is still authenticated — clean up server-side data * here. Throwing aborts the deletion. @@ -40,20 +40,20 @@ export interface UseDeleteAccountOptionsProps extends HookErrorOptions { } export function useDeleteAccount( - options?: UseDeleteAccountOptionsProps, + options?: UseDeleteAccountOptions, ): ReturnType; export function useDeleteAccount( auth: Auth | null, - options?: UseDeleteAccountOptionsProps, + options?: UseDeleteAccountOptions, ): ReturnType; export function useDeleteAccount( - authOrOptions?: Auth | null | UseDeleteAccountOptionsProps, - maybeOptions?: UseDeleteAccountOptionsProps, + authOrOptions?: Auth | null | UseDeleteAccountOptions, + maybeOptions?: UseDeleteAccountOptions, ) { return useDeleteAccountBase(...useAuthArgs(authOrOptions, maybeOptions)); } -function useDeleteAccountBase(auth: Auth | null, options: UseDeleteAccountOptionsProps) { +function useDeleteAccountBase(auth: Auth | null, options: UseDeleteAccountOptions) { const { loading, error, run } = useAuthTask(options); const deleteAccount = ({ diff --git a/packages/firebase-hooks/src/auth/use-email-link-sign-in.ts b/packages/firebase-hooks/src/auth/use-email-link-sign-in.ts index d63d24b..fa0c023 100644 --- a/packages/firebase-hooks/src/auth/use-email-link-sign-in.ts +++ b/packages/firebase-hooks/src/auth/use-email-link-sign-in.ts @@ -39,18 +39,18 @@ import { type UserCredential, } from "firebase/auth"; import { + type EmailSender, type HookErrorOptions, type HookResult, type OnIdToken, requireAuth, runOnIdToken, - type SendEmail, useAuthArgs, useAuthTask, useResolvedConfig, } from "./_shared"; -export interface UseEmailLinkSignInOptionsProps extends HookErrorOptions { +export interface UseEmailLinkSignInOptions extends HookErrorOptions { /** Where the emailed link points back to. Overrides the provider; `null` opts out. */ actionCodeSettings?: ActionCodeSettings | null; /** @@ -59,7 +59,7 @@ export interface UseEmailLinkSignInOptionsProps extends HookErrorOptions { */ storageKey?: string; /** Replace the sender — e.g. your own API emails the link instead of Firebase. */ - sendEmail?: SendEmail | null; + sendEmail?: EmailSender | null; /** * Called with a freshly minted ID token after sign-in — mint your server * session here. Throwing aborts the flow. Overrides the provider; `null` opts out. @@ -79,23 +79,20 @@ export type CompleteSignInResult = }; export function useEmailLinkSignIn( - options?: UseEmailLinkSignInOptionsProps, + options?: UseEmailLinkSignInOptions, ): ReturnType; export function useEmailLinkSignIn( auth: Auth | null, - options?: UseEmailLinkSignInOptionsProps, + options?: UseEmailLinkSignInOptions, ): ReturnType; export function useEmailLinkSignIn( - authOrOptions?: Auth | null | UseEmailLinkSignInOptionsProps, - maybeOptions?: UseEmailLinkSignInOptionsProps, + authOrOptions?: Auth | null | UseEmailLinkSignInOptions, + maybeOptions?: UseEmailLinkSignInOptions, ) { return useEmailLinkSignInBase(...useAuthArgs(authOrOptions, maybeOptions)); } -function useEmailLinkSignInBase( - auth: Auth | null, - options: UseEmailLinkSignInOptionsProps, -) { +function useEmailLinkSignInBase(auth: Auth | null, options: UseEmailLinkSignInOptions) { const { storageKey = "emailForSignIn" } = options; const { loading, error, setError, run } = useAuthTask(options); const onIdToken = useResolvedConfig("onIdToken", options.onIdToken); diff --git a/packages/firebase-hooks/src/auth/use-login.ts b/packages/firebase-hooks/src/auth/use-login.ts index 62696c8..0aaff2a 100644 --- a/packages/firebase-hooks/src/auth/use-login.ts +++ b/packages/firebase-hooks/src/auth/use-login.ts @@ -41,7 +41,7 @@ import { useResolvedConfig, } from "./_shared"; -export interface UseLoginOptionsProps extends HookErrorOptions { +export interface UseLoginOptions extends HookErrorOptions { /** * Called with a freshly minted ID token after sign-in — mint your server * session here. Throwing aborts the flow. Overrides the provider; `null` opts out. @@ -51,19 +51,19 @@ export interface UseLoginOptionsProps extends HookErrorOptions { // Overloads give the two call styles; the return type is inferred from the // implementation below rather than restated, so it cannot drift from it. -export function useLogin(options?: UseLoginOptionsProps): ReturnType; +export function useLogin(options?: UseLoginOptions): ReturnType; export function useLogin( auth: Auth | null, - options?: UseLoginOptionsProps, + options?: UseLoginOptions, ): ReturnType; export function useLogin( - authOrOptions?: Auth | null | UseLoginOptionsProps, - maybeOptions?: UseLoginOptionsProps, + authOrOptions?: Auth | null | UseLoginOptions, + maybeOptions?: UseLoginOptions, ) { return useLoginBase(...useAuthArgs(authOrOptions, maybeOptions)); } -function useLoginBase(auth: Auth | null, options: UseLoginOptionsProps) { +function useLoginBase(auth: Auth | null, options: UseLoginOptions) { const { loading, error, run } = useAuthTask(options); const onIdToken = useResolvedConfig("onIdToken", options.onIdToken); diff --git a/packages/firebase-hooks/src/auth/use-logout.ts b/packages/firebase-hooks/src/auth/use-logout.ts index 66d0d0e..3f89452 100644 --- a/packages/firebase-hooks/src/auth/use-logout.ts +++ b/packages/firebase-hooks/src/auth/use-logout.ts @@ -24,7 +24,7 @@ import { useResolvedConfig, } from "./_shared"; -export interface UseLogoutOptionsProps extends HookErrorOptions { +export interface UseLogoutOptions extends HookErrorOptions { /** * Runs before Firebase clears the session — clear your server session here. * Throwing leaves the user signed in. Overrides the provider; `null` opts out. @@ -32,21 +32,19 @@ export interface UseLogoutOptionsProps extends HookErrorOptions { onBeforeSignOut?: (() => void | Promise) | null; } -export function useLogout( - options?: UseLogoutOptionsProps, -): ReturnType; +export function useLogout(options?: UseLogoutOptions): ReturnType; export function useLogout( auth: Auth | null, - options?: UseLogoutOptionsProps, + options?: UseLogoutOptions, ): ReturnType; export function useLogout( - authOrOptions?: Auth | null | UseLogoutOptionsProps, - maybeOptions?: UseLogoutOptionsProps, + authOrOptions?: Auth | null | UseLogoutOptions, + maybeOptions?: UseLogoutOptions, ) { return useLogoutBase(...useAuthArgs(authOrOptions, maybeOptions)); } -function useLogoutBase(auth: Auth | null, options: UseLogoutOptionsProps) { +function useLogoutBase(auth: Auth | null, options: UseLogoutOptions) { const { loading, error, run } = useAuthTask(options); const onBeforeSignOut = useResolvedConfig("onBeforeSignOut", options.onBeforeSignOut); diff --git a/packages/firebase-hooks/src/auth/use-oauth-sign-in.ts b/packages/firebase-hooks/src/auth/use-oauth-sign-in.ts index e7efc9e..411dbc2 100644 --- a/packages/firebase-hooks/src/auth/use-oauth-sign-in.ts +++ b/packages/firebase-hooks/src/auth/use-oauth-sign-in.ts @@ -43,7 +43,7 @@ import { useResolvedConfig, } from "./_shared"; -export interface UseOAuthSignInOptionsProps extends HookErrorOptions { +export interface UseOAuthSignInOptions extends HookErrorOptions { /** * Called with a freshly minted ID token after sign-in — mint your server * session here. Throwing aborts the flow. Overrides the provider; `null` opts out. @@ -52,20 +52,20 @@ export interface UseOAuthSignInOptionsProps extends HookErrorOptions { } export function useOAuthSignIn( - options?: UseOAuthSignInOptionsProps, + options?: UseOAuthSignInOptions, ): ReturnType; export function useOAuthSignIn( auth: Auth | null, - options?: UseOAuthSignInOptionsProps, + options?: UseOAuthSignInOptions, ): ReturnType; export function useOAuthSignIn( - authOrOptions?: Auth | null | UseOAuthSignInOptionsProps, - maybeOptions?: UseOAuthSignInOptionsProps, + authOrOptions?: Auth | null | UseOAuthSignInOptions, + maybeOptions?: UseOAuthSignInOptions, ) { return useOAuthSignInBase(...useAuthArgs(authOrOptions, maybeOptions)); } -function useOAuthSignInBase(auth: Auth | null, options: UseOAuthSignInOptionsProps) { +function useOAuthSignInBase(auth: Auth | null, options: UseOAuthSignInOptions) { const { loading, error, run } = useAuthTask(options); // getRedirectResult consumes the pending result — guard Strict Mode's double effect. const redirectHandledRef = useRef(false); diff --git a/packages/firebase-hooks/src/auth/use-phone-sign-in.ts b/packages/firebase-hooks/src/auth/use-phone-sign-in.ts index 540d41c..a620bfc 100644 --- a/packages/firebase-hooks/src/auth/use-phone-sign-in.ts +++ b/packages/firebase-hooks/src/auth/use-phone-sign-in.ts @@ -41,7 +41,7 @@ import { useResolvedConfig, } from "./_shared"; -export interface UsePhoneSignInOptionsProps extends HookErrorOptions { +export interface UsePhoneSignInOptions extends HookErrorOptions { /** * Size of the managed reCAPTCHA widget. * @defaultValue "invisible" @@ -55,20 +55,20 @@ export interface UsePhoneSignInOptionsProps extends HookErrorOptions { } export function usePhoneSignIn( - options?: UsePhoneSignInOptionsProps, + options?: UsePhoneSignInOptions, ): ReturnType; export function usePhoneSignIn( auth: Auth | null, - options?: UsePhoneSignInOptionsProps, + options?: UsePhoneSignInOptions, ): ReturnType; export function usePhoneSignIn( - authOrOptions?: Auth | null | UsePhoneSignInOptionsProps, - maybeOptions?: UsePhoneSignInOptionsProps, + authOrOptions?: Auth | null | UsePhoneSignInOptions, + maybeOptions?: UsePhoneSignInOptions, ) { return usePhoneSignInBase(...useAuthArgs(authOrOptions, maybeOptions)); } -function usePhoneSignInBase(auth: Auth | null, options: UsePhoneSignInOptionsProps) { +function usePhoneSignInBase(auth: Auth | null, options: UsePhoneSignInOptions) { const { loading, error, run } = useAuthTask(options); const onIdToken = useResolvedConfig("onIdToken", options.onIdToken); const [codeSent, setCodeSent] = useState(false); diff --git a/packages/firebase-hooks/src/auth/use-send-email-verification.ts b/packages/firebase-hooks/src/auth/use-send-email-verification.ts index 97cc12e..f6b4eef 100644 --- a/packages/firebase-hooks/src/auth/use-send-email-verification.ts +++ b/packages/firebase-hooks/src/auth/use-send-email-verification.ts @@ -24,42 +24,42 @@ import { type ActionCodeSettings, type Auth, sendEmailVerification } from "firebase/auth"; import { useState } from "react"; import { + type EmailSender, type HookErrorOptions, type HookResult, requireCurrentUser, - type SendEmail, useAuthArgs, useAuthTask, useResolvedConfig, } from "./_shared"; -export interface UseSendEmailVerificationOptionsProps extends HookErrorOptions { +export interface UseSendEmailVerificationOptions extends HookErrorOptions { /** Where the emailed link points back to. Overrides the provider; `null` opts out. */ actionCodeSettings?: ActionCodeSettings | null; /** * Replace the sender — e.g. your own API emails the link instead of Firebase. * Called with the signed-in user's address. */ - sendEmail?: SendEmail | null; + sendEmail?: EmailSender | null; } export function useSendEmailVerification( - options?: UseSendEmailVerificationOptionsProps, + options?: UseSendEmailVerificationOptions, ): ReturnType; export function useSendEmailVerification( auth: Auth | null, - options?: UseSendEmailVerificationOptionsProps, + options?: UseSendEmailVerificationOptions, ): ReturnType; export function useSendEmailVerification( - authOrOptions?: Auth | null | UseSendEmailVerificationOptionsProps, - maybeOptions?: UseSendEmailVerificationOptionsProps, + authOrOptions?: Auth | null | UseSendEmailVerificationOptions, + maybeOptions?: UseSendEmailVerificationOptions, ) { return useSendEmailVerificationBase(...useAuthArgs(authOrOptions, maybeOptions)); } function useSendEmailVerificationBase( auth: Auth | null, - options: UseSendEmailVerificationOptionsProps, + options: UseSendEmailVerificationOptions, ) { const { loading, error, run } = useAuthTask(options); const actionCodeSettings = useResolvedConfig( diff --git a/packages/firebase-hooks/src/auth/use-send-password-reset-email.ts b/packages/firebase-hooks/src/auth/use-send-password-reset-email.ts index 9da205f..4875689 100644 --- a/packages/firebase-hooks/src/auth/use-send-password-reset-email.ts +++ b/packages/firebase-hooks/src/auth/use-send-password-reset-email.ts @@ -29,39 +29,39 @@ import { } from "firebase/auth"; import { useState } from "react"; import { + type EmailSender, type HookErrorOptions, type HookResult, requireAuth, - type SendEmail, useAuthArgs, useAuthTask, useResolvedConfig, } from "./_shared"; -export interface UseSendPasswordResetEmailOptionsProps extends HookErrorOptions { +export interface UseSendPasswordResetEmailOptions extends HookErrorOptions { /** Where the emailed link points back to. Overrides the provider; `null` opts out. */ actionCodeSettings?: ActionCodeSettings | null; /** Replace the sender — e.g. your own API emails the reset link instead of Firebase. */ - sendEmail?: SendEmail | null; + sendEmail?: EmailSender | null; } export function useSendPasswordResetEmail( - options?: UseSendPasswordResetEmailOptionsProps, + options?: UseSendPasswordResetEmailOptions, ): ReturnType; export function useSendPasswordResetEmail( auth: Auth | null, - options?: UseSendPasswordResetEmailOptionsProps, + options?: UseSendPasswordResetEmailOptions, ): ReturnType; export function useSendPasswordResetEmail( - authOrOptions?: Auth | null | UseSendPasswordResetEmailOptionsProps, - maybeOptions?: UseSendPasswordResetEmailOptionsProps, + authOrOptions?: Auth | null | UseSendPasswordResetEmailOptions, + maybeOptions?: UseSendPasswordResetEmailOptions, ) { return useSendPasswordResetEmailBase(...useAuthArgs(authOrOptions, maybeOptions)); } function useSendPasswordResetEmailBase( auth: Auth | null, - options: UseSendPasswordResetEmailOptionsProps, + options: UseSendPasswordResetEmailOptions, ) { const { loading, error, setError, run } = useAuthTask(options); const actionCodeSettings = useResolvedConfig( diff --git a/packages/firebase-hooks/src/auth/use-signup.ts b/packages/firebase-hooks/src/auth/use-signup.ts index 800284d..08bec64 100644 --- a/packages/firebase-hooks/src/auth/use-signup.ts +++ b/packages/firebase-hooks/src/auth/use-signup.ts @@ -39,7 +39,7 @@ import { useResolvedConfig, } from "./_shared"; -export interface UseSignupOptionsProps extends HookErrorOptions { +export interface UseSignupOptions extends HookErrorOptions { /** * Send the verification email once the account is created. * @defaultValue true @@ -52,21 +52,19 @@ export interface UseSignupOptionsProps extends HookErrorOptions { onIdToken?: OnIdToken | null; } -export function useSignup( - options?: UseSignupOptionsProps, -): ReturnType; +export function useSignup(options?: UseSignupOptions): ReturnType; export function useSignup( auth: Auth | null, - options?: UseSignupOptionsProps, + options?: UseSignupOptions, ): ReturnType; export function useSignup( - authOrOptions?: Auth | null | UseSignupOptionsProps, - maybeOptions?: UseSignupOptionsProps, + authOrOptions?: Auth | null | UseSignupOptions, + maybeOptions?: UseSignupOptions, ) { return useSignupBase(...useAuthArgs(authOrOptions, maybeOptions)); } -function useSignupBase(auth: Auth | null, options: UseSignupOptionsProps) { +function useSignupBase(auth: Auth | null, options: UseSignupOptions) { const { sendVerificationEmail = true } = options; const { loading, error, run } = useAuthTask(options); const onIdToken = useResolvedConfig("onIdToken", options.onIdToken); diff --git a/packages/firebase-hooks/src/auth/use-update-email.ts b/packages/firebase-hooks/src/auth/use-update-email.ts index b1f1bee..b34da94 100644 --- a/packages/firebase-hooks/src/auth/use-update-email.ts +++ b/packages/firebase-hooks/src/auth/use-update-email.ts @@ -36,26 +36,26 @@ import { useResolvedConfig, } from "./_shared"; -export interface UseUpdateEmailOptionsProps extends HookErrorOptions { +export interface UseUpdateEmailOptions extends HookErrorOptions { /** Where the emailed link points back to. Overrides the provider; `null` opts out. */ actionCodeSettings?: ActionCodeSettings | null; } export function useUpdateEmail( - options?: UseUpdateEmailOptionsProps, + options?: UseUpdateEmailOptions, ): ReturnType; export function useUpdateEmail( auth: Auth | null, - options?: UseUpdateEmailOptionsProps, + options?: UseUpdateEmailOptions, ): ReturnType; export function useUpdateEmail( - authOrOptions?: Auth | null | UseUpdateEmailOptionsProps, - maybeOptions?: UseUpdateEmailOptionsProps, + authOrOptions?: Auth | null | UseUpdateEmailOptions, + maybeOptions?: UseUpdateEmailOptions, ) { return useUpdateEmailBase(...useAuthArgs(authOrOptions, maybeOptions)); } -function useUpdateEmailBase(auth: Auth | null, options: UseUpdateEmailOptionsProps) { +function useUpdateEmailBase(auth: Auth | null, options: UseUpdateEmailOptions) { const { loading, error, run } = useAuthTask(options); const actionCodeSettings = useResolvedConfig( "actionCodeSettings", diff --git a/packages/firebase-hooks/src/auth/use-verify-email.ts b/packages/firebase-hooks/src/auth/use-verify-email.ts index d0a71b3..7265b7e 100644 --- a/packages/firebase-hooks/src/auth/use-verify-email.ts +++ b/packages/firebase-hooks/src/auth/use-verify-email.ts @@ -32,9 +32,9 @@ import { useErrorMessageResolver, } from "./_shared"; -export type VerifyEmailStatusType = AsyncStatus; +export type VerifyEmailStatus = AsyncStatus; -export interface UseVerifyEmailOptionsProps extends HookErrorOptions { +export interface UseVerifyEmailOptions extends HookErrorOptions { /** * Runs after the code is applied and the token refreshed — refresh your * server session here. @@ -44,17 +44,17 @@ export interface UseVerifyEmailOptionsProps extends HookErrorOptions { export function useVerifyEmail( oobCode: string | null, - options?: UseVerifyEmailOptionsProps, + options?: UseVerifyEmailOptions, ): ReturnType; export function useVerifyEmail( auth: Auth | null, oobCode: string | null, - options?: UseVerifyEmailOptionsProps, + options?: UseVerifyEmailOptions, ): ReturnType; export function useVerifyEmail( ...args: - | [oobCode: string | null, options?: UseVerifyEmailOptionsProps] - | [auth: Auth | null, oobCode: string | null, options?: UseVerifyEmailOptionsProps] + | [oobCode: string | null, options?: UseVerifyEmailOptions] + | [auth: Auth | null, oobCode: string | null, options?: UseVerifyEmailOptions] ) { // Arity, not just type: `useVerifyEmail(null)` has to mean "no code in the // URL" — the common case, since `searchParams.get()` returns null — while @@ -66,9 +66,9 @@ export function useVerifyEmail( args.length > 2 || (args.length === 2 && (typeof second === "string" || second === null)); - const [auth, options] = useAuthArgs( + const [auth, options] = useAuthArgs( withAuth ? (args[0] as Auth | null) : undefined, - (withAuth ? args[2] : args[1]) as UseVerifyEmailOptionsProps | undefined, + (withAuth ? args[2] : args[1]) as UseVerifyEmailOptions | undefined, ); const oobCode = ((withAuth ? args[1] : args[0]) as string | null) ?? null; @@ -78,9 +78,9 @@ export function useVerifyEmail( function useVerifyEmailBase( auth: Auth | null, oobCode: string | null, - options: UseVerifyEmailOptionsProps, + options: UseVerifyEmailOptions, ) { - const [status, setStatus] = useState("pending"); + const [status, setStatus] = useState("pending"); const [error, setError] = useState(null); const [code, setCode] = useState(null); const [cause, setCause] = useState(null); From 779a36e9966cbed799d44daa0ade5ede6c949536 Mon Sep 17 00:00:00 2001 From: timonwa Date: Tue, 8 Sep 2026 23:00:09 +0100 Subject: [PATCH 15/23] feat(auth)!: senders receive { email, actionCodeSettings } MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Mirrors Firebase on both ends — the client send takes them, and the Admin SDK's generate*Link wants them back — so a provider-level actionCodeSettings reaches your server instead of being dropped. An object also grows without a break. --- .changeset/align-status-and-sender-naming.md | 2 +- .changeset/delegate-email-sends.md | 5 +++-- .changeset/provider-senders.md | 6 +++--- apps/docs/content/docs/auth/auth-provider.mdx | 6 +++--- .../content/docs/auth/use-send-email-verification.mdx | 5 +++-- .../docs/auth/use-send-password-reset-email.mdx | 7 +++++-- packages/firebase-hooks/src/auth/_shared.ts | 11 +++++++++-- .../firebase-hooks/src/auth/auth-provider.test.tsx | 6 ++++-- .../src/auth/use-email-link-sign-in.test.tsx | 2 +- .../firebase-hooks/src/auth/use-email-link-sign-in.ts | 2 +- .../src/auth/use-send-email-verification.test.tsx | 2 +- .../src/auth/use-send-email-verification.ts | 8 ++++---- .../src/auth/use-send-password-reset-email.test.tsx | 2 +- .../src/auth/use-send-password-reset-email.ts | 4 ++-- 14 files changed, 41 insertions(+), 27 deletions(-) diff --git a/.changeset/align-status-and-sender-naming.md b/.changeset/align-status-and-sender-naming.md index 751583e..c6d3caa 100644 --- a/.changeset/align-status-and-sender-naming.md +++ b/.changeset/align-status-and-sender-naming.md @@ -15,5 +15,5 @@ ```tsx -useEmailLinkSignIn({ sendLink: (email) => api.send(email) }); -+useEmailLinkSignIn({ sendEmail: (email) => api.send(email) }); ++useEmailLinkSignIn({ sendEmail: ({ email }) => api.send(email) }); ``` diff --git a/.changeset/delegate-email-sends.md b/.changeset/delegate-email-sends.md index 2a43980..be144f7 100644 --- a/.changeset/delegate-email-sends.md +++ b/.changeset/delegate-email-sends.md @@ -6,8 +6,9 @@ ```tsx const { send } = useSendPasswordResetEmail({ - sendEmail: (email) => requestPasswordReset(email), // your rate-limited endpoint + sendEmail: ({ email, actionCodeSettings }) => + requestPasswordReset(email, actionCodeSettings), }); ``` -The hook keeps its own bookkeeping either way: `loading`, `error`, `success` and `resetState` behave identically, and a throwing sender surfaces as an ordinary failure result. On `useSendEmailVerification` the sender receives the signed-in user's address, since `send()` takes no arguments; an account without one fails clearly rather than calling your sender with nothing. +The hook keeps its own bookkeeping either way: `loading`, `error`, `success` and `resetState` behave identically, and a throwing sender surfaces as an ordinary failure result. The sender receives `{ email, actionCodeSettings }` — the same inputs Firebase's client send takes and the Admin SDK's `generate*Link` wants, so a provider-level `actionCodeSettings` still reaches your server. On `useSendEmailVerification`, `email` is the signed-in user's address, since `send()` takes no arguments; an account without one fails clearly rather than calling your sender with nothing. diff --git a/.changeset/provider-senders.md b/.changeset/provider-senders.md index 8009a29..3f00cf5 100644 --- a/.changeset/provider-senders.md +++ b/.changeset/provider-senders.md @@ -8,9 +8,9 @@ api.sendSignInLink(email), - passwordReset: (email) => api.sendPasswordReset(email), - emailVerification: (email) => api.sendVerification(email), + signInLink: ({ email, actionCodeSettings }) => api.sendSignInLink(email, actionCodeSettings), + passwordReset: ({ email, actionCodeSettings }) => api.sendPasswordReset(email, actionCodeSettings), + emailVerification: ({ email, actionCodeSettings }) => api.sendVerification(email, actionCodeSettings), }} > ``` diff --git a/apps/docs/content/docs/auth/auth-provider.mdx b/apps/docs/content/docs/auth/auth-provider.mdx index e46b1fe..16081a8 100644 --- a/apps/docs/content/docs/auth/auth-provider.mdx +++ b/apps/docs/content/docs/auth/auth-provider.mdx @@ -68,9 +68,9 @@ Three hooks email a link, and by default each sends from the browser. `senders` api.sendSignInLink(email), - passwordReset: (email) => api.sendPasswordReset(email), - emailVerification: (email) => api.sendVerification(email), + signInLink: ({ email, actionCodeSettings }) => api.sendSignInLink(email, actionCodeSettings), + passwordReset: ({ email, actionCodeSettings }) => api.sendPasswordReset(email, actionCodeSettings), + emailVerification: ({ email, actionCodeSettings }) => api.sendVerification(email, actionCodeSettings), }} > ``` diff --git a/apps/docs/content/docs/auth/use-send-email-verification.mdx b/apps/docs/content/docs/auth/use-send-email-verification.mdx index caf2f0e..104d6f8 100644 --- a/apps/docs/content/docs/auth/use-send-email-verification.mdx +++ b/apps/docs/content/docs/auth/use-send-email-verification.mdx @@ -27,11 +27,12 @@ Pair with a cooldown to stop rapid re-sends — Firebase rate-limits these, and ```tsx const { send } = useSendEmailVerification({ - sendEmail: (email) => requestVerification(email), // your rate-limited endpoint + sendEmail: ({ email, actionCodeSettings }) => + requestVerification(email, actionCodeSettings), }); ``` -It receives the signed-in user's address, because `send()` takes no arguments. An account with no email — phone-only, for instance — fails with a clear result rather than calling your sender with nothing. +It receives `{ email, actionCodeSettings }` — `email` is the signed-in user's address, since `send()` takes no arguments, and `actionCodeSettings` is whatever the hook or provider resolved, ready for the Admin SDK's `generateEmailVerificationLink`. An account with no email — phone-only, for instance — fails with a clear result rather than calling your sender with nothing. ## Options diff --git a/apps/docs/content/docs/auth/use-send-password-reset-email.mdx b/apps/docs/content/docs/auth/use-send-password-reset-email.mdx index e38735a..76140cd 100644 --- a/apps/docs/content/docs/auth/use-send-password-reset-email.mdx +++ b/apps/docs/content/docs/auth/use-send-password-reset-email.mdx @@ -29,11 +29,14 @@ By default the link is sent from the browser, which puts an email-sending path o ```tsx const { send, success } = useSendPasswordResetEmail({ - sendEmail: (email) => requestPasswordReset(email), // your rate-limited endpoint + sendEmail: ({ email, actionCodeSettings }) => + requestPasswordReset(email, actionCodeSettings), }); ``` -The hook keeps everything else: `loading`, `error`, `success` and `resetState` behave identically, and a throwing sender surfaces as an ordinary failure result. `actionCodeSettings` is unused on this path — your server decides where the link lands. +The hook keeps everything else: `loading`, `error`, `success` and `resetState` behave identically, and a throwing sender surfaces as an ordinary failure result. + +The sender receives `{ email, actionCodeSettings }` — the same two things Firebase's own `sendPasswordResetEmail` takes, and the same two the Admin SDK's `generatePasswordResetLink(email, actionCodeSettings)` wants on your server. So the `actionCodeSettings` you set once on the provider still decides where the link lands, even when your API sends it. Same option, same semantics, on [`useSendEmailVerification`](/docs/auth/use-send-email-verification) and [`useEmailLinkSignIn`](/docs/auth/use-email-link-sign-in). To set all three at once, use the provider's [`senders`](/docs/auth/auth-provider#sending-the-emails-yourself) — a hook's own option still overrides it, and `null` opts that one flow back to Firebase. diff --git a/packages/firebase-hooks/src/auth/_shared.ts b/packages/firebase-hooks/src/auth/_shared.ts index 75a3617..7e3ba15 100644 --- a/packages/firebase-hooks/src/auth/_shared.ts +++ b/packages/firebase-hooks/src/auth/_shared.ts @@ -34,8 +34,15 @@ function rawErrorMessage(error: unknown, fallback: string): string { return fallback; } -/** Emails a link on the app's behalf, in place of Firebase's client SDK. */ -export type EmailSender = (email: string) => Promise; +/** + * Emails a link on the app's behalf, in place of Firebase's client SDK. Receives + * the same two things Firebase's own send functions take — and the same two the + * Admin SDK's `generate*Link(email, actionCodeSettings)` wants on the server. + */ +export type EmailSender = (params: { + email: string; + actionCodeSettings?: ActionCodeSettings; +}) => Promise; /** * Your own sender per emailed flow. Each one replaces the client-side send for diff --git a/packages/firebase-hooks/src/auth/auth-provider.test.tsx b/packages/firebase-hooks/src/auth/auth-provider.test.tsx index 6dc9f42..07017bb 100644 --- a/packages/firebase-hooks/src/auth/auth-provider.test.tsx +++ b/packages/firebase-hooks/src/auth/auth-provider.test.tsx @@ -368,7 +368,9 @@ describe("provider-level senders", () => { // The three senders email different things, so a reset must never reach // the sign-in-link sender. - expect(passwordReset).toHaveBeenCalledWith("a@b.c"); + expect(passwordReset).toHaveBeenCalledWith( + expect.objectContaining({ email: "a@b.c" }), + ); expect(signInLink).not.toHaveBeenCalled(); expect(sendPasswordResetEmail).not.toHaveBeenCalled(); }); @@ -385,7 +387,7 @@ describe("provider-level senders", () => { await result.current.send("a@b.c"); }); - expect(own).toHaveBeenCalledWith("a@b.c"); + expect(own).toHaveBeenCalledWith(expect.objectContaining({ email: "a@b.c" })); expect(passwordReset).not.toHaveBeenCalled(); }); diff --git a/packages/firebase-hooks/src/auth/use-email-link-sign-in.test.tsx b/packages/firebase-hooks/src/auth/use-email-link-sign-in.test.tsx index b191894..40003ef 100644 --- a/packages/firebase-hooks/src/auth/use-email-link-sign-in.test.tsx +++ b/packages/firebase-hooks/src/auth/use-email-link-sign-in.test.tsx @@ -69,7 +69,7 @@ describe("useEmailLinkSignIn", () => { await act(async () => { await result.current.sendLink("a@b.c"); }); - expect(sendViaApi).toHaveBeenCalledWith("a@b.c"); + expect(sendViaApi).toHaveBeenCalledWith(expect.objectContaining({ email: "a@b.c" })); expect(sendSignInLinkToEmail).not.toHaveBeenCalled(); expect(window.localStorage.getItem("magic-email")).toBe("a@b.c"); }); diff --git a/packages/firebase-hooks/src/auth/use-email-link-sign-in.ts b/packages/firebase-hooks/src/auth/use-email-link-sign-in.ts index fa0c023..508e2dc 100644 --- a/packages/firebase-hooks/src/auth/use-email-link-sign-in.ts +++ b/packages/firebase-hooks/src/auth/use-email-link-sign-in.ts @@ -106,7 +106,7 @@ function useEmailLinkSignInBase(auth: Auth | null, options: UseEmailLinkSignInOp const sendLink = (email: string): Promise => run("send-sign-in-link", "Failed to send sign-in link", async () => { if (send) { - await send(email); + await send({ email, actionCodeSettings: actionCodeSettings ?? undefined }); } else { if (!actionCodeSettings) { throw new Error( diff --git a/packages/firebase-hooks/src/auth/use-send-email-verification.test.tsx b/packages/firebase-hooks/src/auth/use-send-email-verification.test.tsx index 452961f..eb9400d 100644 --- a/packages/firebase-hooks/src/auth/use-send-email-verification.test.tsx +++ b/packages/firebase-hooks/src/auth/use-send-email-verification.test.tsx @@ -63,7 +63,7 @@ describe("sendEmail", () => { await result.current.send(); }); - expect(sendEmail).toHaveBeenCalledWith("who@b.c"); + expect(sendEmail).toHaveBeenCalledWith(expect.objectContaining({ email: "who@b.c" })); expect(sendEmailVerification).not.toHaveBeenCalled(); expect(result.current.success).toBe(true); }); diff --git a/packages/firebase-hooks/src/auth/use-send-email-verification.ts b/packages/firebase-hooks/src/auth/use-send-email-verification.ts index f6b4eef..81091ce 100644 --- a/packages/firebase-hooks/src/auth/use-send-email-verification.ts +++ b/packages/firebase-hooks/src/auth/use-send-email-verification.ts @@ -5,7 +5,7 @@ * * @param auth - Firebase `Auth` instance, or null while it initialises * @param options.actionCodeSettings - Where the emailed verification link lands - * @param options.sendEmail - Replace the client-side sender, called with the user's address + * @param options.sendEmail - Replace the client-side sender; gets the signed-in user's address * @returns `{ send, loading, error, success }` * * @example @@ -15,7 +15,7 @@ * @example * // Sent by your own API, so the flow goes through your rate limiter * const { send } = useSendEmailVerification({ - * sendEmail: (email) => requestVerification(email), + * sendEmail: ({ email }) => requestVerification(email), * }); */ @@ -38,7 +38,7 @@ export interface UseSendEmailVerificationOptions extends HookErrorOptions { actionCodeSettings?: ActionCodeSettings | null; /** * Replace the sender — e.g. your own API emails the link instead of Firebase. - * Called with the signed-in user's address. + * `email` is the signed-in user's address. */ sendEmail?: EmailSender | null; } @@ -79,7 +79,7 @@ function useSendEmailVerificationBase( if (sendEmail) { // `send()` takes no arguments, so the address comes off the user. if (!user.email) throw new Error("This account has no email address"); - await sendEmail(user.email); + await sendEmail({ email: user.email, actionCodeSettings }); } else { await sendEmailVerification(user, actionCodeSettings); } diff --git a/packages/firebase-hooks/src/auth/use-send-password-reset-email.test.tsx b/packages/firebase-hooks/src/auth/use-send-password-reset-email.test.tsx index d82ebf5..0f421ed 100644 --- a/packages/firebase-hooks/src/auth/use-send-password-reset-email.test.tsx +++ b/packages/firebase-hooks/src/auth/use-send-password-reset-email.test.tsx @@ -89,7 +89,7 @@ describe("sendEmail", () => { await result.current.send("a@b.c"); }); - expect(sendEmail).toHaveBeenCalledWith("a@b.c"); + expect(sendEmail).toHaveBeenCalledWith(expect.objectContaining({ email: "a@b.c" })); expect(sendPasswordResetEmail).not.toHaveBeenCalled(); expect(result.current.success).toBe(true); }); diff --git a/packages/firebase-hooks/src/auth/use-send-password-reset-email.ts b/packages/firebase-hooks/src/auth/use-send-password-reset-email.ts index 4875689..6d11a7e 100644 --- a/packages/firebase-hooks/src/auth/use-send-password-reset-email.ts +++ b/packages/firebase-hooks/src/auth/use-send-password-reset-email.ts @@ -16,7 +16,7 @@ * @example * // Sent by your own API, so the flow goes through your rate limiter * const { send } = useSendPasswordResetEmail({ - * sendEmail: (email) => requestPasswordReset(email), + * sendEmail: ({ email }) => requestPasswordReset(email), * }); */ @@ -78,7 +78,7 @@ function useSendPasswordResetEmailBase( "Failed to send reset email", async () => { if (sendEmail) { - await sendEmail(email); + await sendEmail({ email, actionCodeSettings }); } else { await sendPasswordResetEmail(requireAuth(auth), email, actionCodeSettings); } From bac2d4dfa0f254d18a9285a5033f354b99f130ea Mon Sep 17 00:00:00 2001 From: timonwa Date: Tue, 8 Sep 2026 23:10:02 +0100 Subject: [PATCH 16/23] feat(auth): export what every hook returns as UseResult MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A wrapper could state a hook's options but not its return without re-deriving it via ReturnType. Each is the inferred type the overloads already used, named — so it cannot drift. Matches TanStack's UseQueryResult / UseMutationResult. --- .changeset/named-result-types.md | 19 +++++ apps/docs/content/docs/how-hooks-work.mdx | 10 ++- apps/playground/lib/wrapper-types.ts | 42 ++++++++++ packages/firebase-hooks/src/auth/index.ts | 77 +++++++++++++++---- .../src/auth/use-anonymous-sign-in.ts | 7 +- .../src/auth/use-confirm-password-reset.ts | 9 ++- .../src/auth/use-custom-token-sign-in.ts | 7 +- .../src/auth/use-delete-account.ts | 7 +- .../src/auth/use-email-link-sign-in.ts | 7 +- .../src/auth/use-link-provider.ts | 9 ++- packages/firebase-hooks/src/auth/use-login.ts | 10 +-- .../firebase-hooks/src/auth/use-logout.ts | 10 +-- .../src/auth/use-oauth-sign-in.ts | 9 ++- .../src/auth/use-phone-sign-in.ts | 9 ++- .../src/auth/use-reauthenticate.ts | 9 ++- .../src/auth/use-send-email-verification.ts | 9 ++- .../src/auth/use-send-password-reset-email.ts | 9 ++- .../firebase-hooks/src/auth/use-signup.ts | 10 +-- .../src/auth/use-unlink-provider.ts | 9 ++- .../src/auth/use-update-email.ts | 9 ++- .../src/auth/use-update-password.ts | 9 ++- .../src/auth/use-update-profile.ts | 9 ++- .../src/auth/use-verify-email.ts | 7 +- 23 files changed, 232 insertions(+), 80 deletions(-) create mode 100644 .changeset/named-result-types.md diff --git a/.changeset/named-result-types.md b/.changeset/named-result-types.md new file mode 100644 index 0000000..83d9947 --- /dev/null +++ b/.changeset/named-result-types.md @@ -0,0 +1,19 @@ +--- +"@timonwa/firebase-hooks": minor +--- + +Every hook now exports the type it returns as `UseResult` — `UseLoginResult`, `UseSignupResult`, `UseVerifyEmailResult`, and so on, from `@timonwa/firebase-hooks/auth`. A wrapper can state its return type instead of re-deriving it with `ReturnType`. + +```tsx +import { + useLogin, + type UseLoginOptions, + type UseLoginResult, +} from "@timonwa/firebase-hooks/auth"; + +export function useAppLogin(options?: UseLoginOptions): UseLoginResult { + return useLogin(options); +} +``` + +Same shape as TanStack Query's `UseQueryResult` / `UseMutationResult`. `useAuth` already returned the named `UseAuthResult`. diff --git a/apps/docs/content/docs/how-hooks-work.mdx b/apps/docs/content/docs/how-hooks-work.mdx index 5885df9..9f4de93 100644 --- a/apps/docs/content/docs/how-hooks-work.mdx +++ b/apps/docs/content/docs/how-hooks-work.mdx @@ -54,16 +54,20 @@ See [Error handling](/docs/guides/error-handling) for the full model. Everything a wrapper needs to name is exported. Each hook's options interface comes from the auth entry, so you can accept and forward them without restating the shape: ```tsx -import { useLogin, type UseLoginOptions } from '@timonwa/firebase-hooks/auth'; +import { + useLogin, + type UseLoginOptions, + type UseLoginResult, +} from '@timonwa/firebase-hooks/auth'; -export function useAppLogin(options?: UseLoginOptions) { +export function useAppLogin(options?: UseLoginOptions): UseLoginResult { const { login, loading, error } = useLogin(options); // …your own state on top return { login, loading, error }; } ``` -`AuthProviderProps` and the type `useAuth` returns are exported too, for an app provider layered over this one — see [Server sessions](/docs/guides/server-sessions). +Every hook exports what it returns as `UseResult` — `UseLoginResult`, `UseSignupResult`, and so on — so a wrapper can state its return type rather than re-deriving it. `AuthProviderProps` and `UseAuthResult` are exported too, for an app provider layered over this one — see [Server sessions](/docs/guides/server-sessions). The result and error shapes — `HookResult`, `HookErrorOptions`, `HookErrorContext` — come from the **root** entry instead, because every service shares them: diff --git a/apps/playground/lib/wrapper-types.ts b/apps/playground/lib/wrapper-types.ts index 3f14ef6..190a2b0 100644 --- a/apps/playground/lib/wrapper-types.ts +++ b/apps/playground/lib/wrapper-types.ts @@ -10,6 +10,25 @@ import type { CompleteSignInResult, VerifyEmailStatus, UseAnonymousSignInOptions, + UseAnonymousSignInResult, + UseConfirmPasswordResetResult, + UseCustomTokenSignInResult, + UseDeleteAccountResult, + UseEmailLinkSignInResult, + UseLinkProviderResult, + UseLoginResult, + UseLogoutResult, + UseOAuthSignInResult, + UsePhoneSignInResult, + UseReauthenticateResult, + UseSendEmailVerificationResult, + UseSendPasswordResetEmailResult, + UseSignupResult, + UseUnlinkProviderResult, + UseUpdateEmailResult, + UseUpdatePasswordResult, + UseUpdateProfileResult, + UseVerifyEmailResult, UseCustomTokenSignInOptions, UseDeleteAccountOptions, UseEmailLinkSignInOptions, @@ -62,3 +81,26 @@ export type AuthOptions = { updateEmail: UseUpdateEmailOptions; verifyEmail: UseVerifyEmailOptions; }; + +/** Every hook's return type, reachable by name from the auth entry. */ +export type AuthResults = { + anonymousSignIn: UseAnonymousSignInResult; + confirmPasswordReset: UseConfirmPasswordResetResult; + customTokenSignIn: UseCustomTokenSignInResult; + deleteAccount: UseDeleteAccountResult; + emailLinkSignIn: UseEmailLinkSignInResult; + linkProvider: UseLinkProviderResult; + login: UseLoginResult; + logout: UseLogoutResult; + oAuthSignIn: UseOAuthSignInResult; + phoneSignIn: UsePhoneSignInResult; + reauthenticate: UseReauthenticateResult; + sendEmailVerification: UseSendEmailVerificationResult; + sendPasswordResetEmail: UseSendPasswordResetEmailResult; + signup: UseSignupResult; + unlinkProvider: UseUnlinkProviderResult; + updateEmail: UseUpdateEmailResult; + updatePassword: UseUpdatePasswordResult; + updateProfile: UseUpdateProfileResult; + verifyEmail: UseVerifyEmailResult; +}; diff --git a/packages/firebase-hooks/src/auth/index.ts b/packages/firebase-hooks/src/auth/index.ts index da89b4d..f8c7e89 100644 --- a/packages/firebase-hooks/src/auth/index.ts +++ b/packages/firebase-hooks/src/auth/index.ts @@ -1,5 +1,5 @@ -// The ./auth entry — everything Firebase Auth, including its error catalogue -// and each hook's options interface. +// The ./auth entry — everything Firebase Auth, including its error catalogue, +// each hook's options interface, and the type each hook returns. // // Shared shapes (HookResult, HookErrorOptions, HookErrorContext, AsyncStatus) // ship from the core entry instead: every service returns them. @@ -14,43 +14,92 @@ export { } from "./auth-provider.js"; export { type UseAnonymousSignInOptions, + type UseAnonymousSignInResult, useAnonymousSignIn, } from "./use-anonymous-sign-in.js"; -export { useConfirmPasswordReset } from "./use-confirm-password-reset.js"; +export { + type UseConfirmPasswordResetResult, + useConfirmPasswordReset, +} from "./use-confirm-password-reset.js"; export { type UseCustomTokenSignInOptions, + type UseCustomTokenSignInResult, useCustomTokenSignIn, } from "./use-custom-token-sign-in.js"; export { type UseDeleteAccountOptions, + type UseDeleteAccountResult, useDeleteAccount, } from "./use-delete-account.js"; export { type CompleteSignInResult, type UseEmailLinkSignInOptions, + type UseEmailLinkSignInResult, useEmailLinkSignIn, } from "./use-email-link-sign-in.js"; -export { useLinkProvider } from "./use-link-provider.js"; -export { type UseLoginOptions, useLogin } from "./use-login.js"; -export { type UseLogoutOptions, useLogout } from "./use-logout.js"; -export { type UseOAuthSignInOptions, useOAuthSignIn } from "./use-oauth-sign-in.js"; -export { type UsePhoneSignInOptions, usePhoneSignIn } from "./use-phone-sign-in.js"; -export { useReauthenticate } from "./use-reauthenticate.js"; +export { + type UseLinkProviderResult, + useLinkProvider, +} from "./use-link-provider.js"; +export { + type UseLoginOptions, + type UseLoginResult, + useLogin, +} from "./use-login.js"; +export { + type UseLogoutOptions, + type UseLogoutResult, + useLogout, +} from "./use-logout.js"; +export { + type UseOAuthSignInOptions, + type UseOAuthSignInResult, + useOAuthSignIn, +} from "./use-oauth-sign-in.js"; +export { + type UsePhoneSignInOptions, + type UsePhoneSignInResult, + usePhoneSignIn, +} from "./use-phone-sign-in.js"; +export { + type UseReauthenticateResult, + useReauthenticate, +} from "./use-reauthenticate.js"; export { type UseSendEmailVerificationOptions, + type UseSendEmailVerificationResult, useSendEmailVerification, } from "./use-send-email-verification.js"; export { type UseSendPasswordResetEmailOptions, + type UseSendPasswordResetEmailResult, useSendPasswordResetEmail, } from "./use-send-password-reset-email.js"; -export { type UseSignupOptions, useSignup } from "./use-signup.js"; -export { useUnlinkProvider } from "./use-unlink-provider.js"; -export { type UseUpdateEmailOptions, useUpdateEmail } from "./use-update-email.js"; -export { useUpdatePassword } from "./use-update-password.js"; -export { useUpdateProfile } from "./use-update-profile.js"; +export { + type UseSignupOptions, + type UseSignupResult, + useSignup, +} from "./use-signup.js"; +export { + type UseUnlinkProviderResult, + useUnlinkProvider, +} from "./use-unlink-provider.js"; +export { + type UseUpdateEmailOptions, + type UseUpdateEmailResult, + useUpdateEmail, +} from "./use-update-email.js"; +export { + type UseUpdatePasswordResult, + useUpdatePassword, +} from "./use-update-password.js"; +export { + type UseUpdateProfileResult, + useUpdateProfile, +} from "./use-update-profile.js"; export { type UseVerifyEmailOptions, + type UseVerifyEmailResult, useVerifyEmail, type VerifyEmailStatus, } from "./use-verify-email.js"; diff --git a/packages/firebase-hooks/src/auth/use-anonymous-sign-in.ts b/packages/firebase-hooks/src/auth/use-anonymous-sign-in.ts index 158895d..c426f8a 100644 --- a/packages/firebase-hooks/src/auth/use-anonymous-sign-in.ts +++ b/packages/firebase-hooks/src/auth/use-anonymous-sign-in.ts @@ -38,13 +38,16 @@ export interface UseAnonymousSignInOptions extends HookErrorOptions { onIdToken?: OnIdToken | null; } +/** What `useAnonymousSignIn` returns. */ +export type UseAnonymousSignInResult = ReturnType; + export function useAnonymousSignIn( options?: UseAnonymousSignInOptions, -): ReturnType; +): UseAnonymousSignInResult; export function useAnonymousSignIn( auth: Auth | null, options?: UseAnonymousSignInOptions, -): ReturnType; +): UseAnonymousSignInResult; export function useAnonymousSignIn( authOrOptions?: Auth | null | UseAnonymousSignInOptions, maybeOptions?: UseAnonymousSignInOptions, diff --git a/packages/firebase-hooks/src/auth/use-confirm-password-reset.ts b/packages/firebase-hooks/src/auth/use-confirm-password-reset.ts index 96deccf..3a2d0cb 100644 --- a/packages/firebase-hooks/src/auth/use-confirm-password-reset.ts +++ b/packages/firebase-hooks/src/auth/use-confirm-password-reset.ts @@ -26,13 +26,18 @@ import { useAuthTask, } from "./_shared"; +/** What `useConfirmPasswordReset` returns. */ +export type UseConfirmPasswordResetResult = ReturnType< + typeof useConfirmPasswordResetBase +>; + export function useConfirmPasswordReset( options?: HookErrorOptions, -): ReturnType; +): UseConfirmPasswordResetResult; export function useConfirmPasswordReset( auth: Auth | null, options?: HookErrorOptions, -): ReturnType; +): UseConfirmPasswordResetResult; export function useConfirmPasswordReset( authOrOptions?: Auth | null | HookErrorOptions, maybeOptions?: HookErrorOptions, diff --git a/packages/firebase-hooks/src/auth/use-custom-token-sign-in.ts b/packages/firebase-hooks/src/auth/use-custom-token-sign-in.ts index eb90eea..919c9ff 100644 --- a/packages/firebase-hooks/src/auth/use-custom-token-sign-in.ts +++ b/packages/firebase-hooks/src/auth/use-custom-token-sign-in.ts @@ -39,13 +39,16 @@ export interface UseCustomTokenSignInOptions extends HookErrorOptions { onIdToken?: OnIdToken | null; } +/** What `useCustomTokenSignIn` returns. */ +export type UseCustomTokenSignInResult = ReturnType; + export function useCustomTokenSignIn( options?: UseCustomTokenSignInOptions, -): ReturnType; +): UseCustomTokenSignInResult; export function useCustomTokenSignIn( auth: Auth | null, options?: UseCustomTokenSignInOptions, -): ReturnType; +): UseCustomTokenSignInResult; export function useCustomTokenSignIn( authOrOptions?: Auth | null | UseCustomTokenSignInOptions, maybeOptions?: UseCustomTokenSignInOptions, diff --git a/packages/firebase-hooks/src/auth/use-delete-account.ts b/packages/firebase-hooks/src/auth/use-delete-account.ts index 096bd49..795175d 100644 --- a/packages/firebase-hooks/src/auth/use-delete-account.ts +++ b/packages/firebase-hooks/src/auth/use-delete-account.ts @@ -39,13 +39,16 @@ export interface UseDeleteAccountOptions extends HookErrorOptions { onBeforeDelete?: (user: User) => void | Promise; } +/** What `useDeleteAccount` returns. */ +export type UseDeleteAccountResult = ReturnType; + export function useDeleteAccount( options?: UseDeleteAccountOptions, -): ReturnType; +): UseDeleteAccountResult; export function useDeleteAccount( auth: Auth | null, options?: UseDeleteAccountOptions, -): ReturnType; +): UseDeleteAccountResult; export function useDeleteAccount( authOrOptions?: Auth | null | UseDeleteAccountOptions, maybeOptions?: UseDeleteAccountOptions, diff --git a/packages/firebase-hooks/src/auth/use-email-link-sign-in.ts b/packages/firebase-hooks/src/auth/use-email-link-sign-in.ts index 508e2dc..581f9a7 100644 --- a/packages/firebase-hooks/src/auth/use-email-link-sign-in.ts +++ b/packages/firebase-hooks/src/auth/use-email-link-sign-in.ts @@ -78,13 +78,16 @@ export type CompleteSignInResult = needsEmail?: boolean; }; +/** What `useEmailLinkSignIn` returns. */ +export type UseEmailLinkSignInResult = ReturnType; + export function useEmailLinkSignIn( options?: UseEmailLinkSignInOptions, -): ReturnType; +): UseEmailLinkSignInResult; export function useEmailLinkSignIn( auth: Auth | null, options?: UseEmailLinkSignInOptions, -): ReturnType; +): UseEmailLinkSignInResult; export function useEmailLinkSignIn( authOrOptions?: Auth | null | UseEmailLinkSignInOptions, maybeOptions?: UseEmailLinkSignInOptions, diff --git a/packages/firebase-hooks/src/auth/use-link-provider.ts b/packages/firebase-hooks/src/auth/use-link-provider.ts index 3d3a190..d054f84 100644 --- a/packages/firebase-hooks/src/auth/use-link-provider.ts +++ b/packages/firebase-hooks/src/auth/use-link-provider.ts @@ -31,13 +31,14 @@ import { useAuthTask, } from "./_shared"; -export function useLinkProvider( - options?: HookErrorOptions, -): ReturnType; +/** What `useLinkProvider` returns. */ +export type UseLinkProviderResult = ReturnType; + +export function useLinkProvider(options?: HookErrorOptions): UseLinkProviderResult; export function useLinkProvider( auth: Auth | null, options?: HookErrorOptions, -): ReturnType; +): UseLinkProviderResult; export function useLinkProvider( authOrOptions?: Auth | null | HookErrorOptions, maybeOptions?: HookErrorOptions, diff --git a/packages/firebase-hooks/src/auth/use-login.ts b/packages/firebase-hooks/src/auth/use-login.ts index 0aaff2a..302285e 100644 --- a/packages/firebase-hooks/src/auth/use-login.ts +++ b/packages/firebase-hooks/src/auth/use-login.ts @@ -51,11 +51,11 @@ export interface UseLoginOptions extends HookErrorOptions { // Overloads give the two call styles; the return type is inferred from the // implementation below rather than restated, so it cannot drift from it. -export function useLogin(options?: UseLoginOptions): ReturnType; -export function useLogin( - auth: Auth | null, - options?: UseLoginOptions, -): ReturnType; +/** What `useLogin` returns. */ +export type UseLoginResult = ReturnType; + +export function useLogin(options?: UseLoginOptions): UseLoginResult; +export function useLogin(auth: Auth | null, options?: UseLoginOptions): UseLoginResult; export function useLogin( authOrOptions?: Auth | null | UseLoginOptions, maybeOptions?: UseLoginOptions, diff --git a/packages/firebase-hooks/src/auth/use-logout.ts b/packages/firebase-hooks/src/auth/use-logout.ts index 3f89452..33ae73e 100644 --- a/packages/firebase-hooks/src/auth/use-logout.ts +++ b/packages/firebase-hooks/src/auth/use-logout.ts @@ -32,11 +32,11 @@ export interface UseLogoutOptions extends HookErrorOptions { onBeforeSignOut?: (() => void | Promise) | null; } -export function useLogout(options?: UseLogoutOptions): ReturnType; -export function useLogout( - auth: Auth | null, - options?: UseLogoutOptions, -): ReturnType; +/** What `useLogout` returns. */ +export type UseLogoutResult = ReturnType; + +export function useLogout(options?: UseLogoutOptions): UseLogoutResult; +export function useLogout(auth: Auth | null, options?: UseLogoutOptions): UseLogoutResult; export function useLogout( authOrOptions?: Auth | null | UseLogoutOptions, maybeOptions?: UseLogoutOptions, diff --git a/packages/firebase-hooks/src/auth/use-oauth-sign-in.ts b/packages/firebase-hooks/src/auth/use-oauth-sign-in.ts index 411dbc2..b95c6de 100644 --- a/packages/firebase-hooks/src/auth/use-oauth-sign-in.ts +++ b/packages/firebase-hooks/src/auth/use-oauth-sign-in.ts @@ -51,13 +51,14 @@ export interface UseOAuthSignInOptions extends HookErrorOptions { onIdToken?: OnIdToken | null; } -export function useOAuthSignIn( - options?: UseOAuthSignInOptions, -): ReturnType; +/** What `useOAuthSignIn` returns. */ +export type UseOAuthSignInResult = ReturnType; + +export function useOAuthSignIn(options?: UseOAuthSignInOptions): UseOAuthSignInResult; export function useOAuthSignIn( auth: Auth | null, options?: UseOAuthSignInOptions, -): ReturnType; +): UseOAuthSignInResult; export function useOAuthSignIn( authOrOptions?: Auth | null | UseOAuthSignInOptions, maybeOptions?: UseOAuthSignInOptions, diff --git a/packages/firebase-hooks/src/auth/use-phone-sign-in.ts b/packages/firebase-hooks/src/auth/use-phone-sign-in.ts index a620bfc..6b708e2 100644 --- a/packages/firebase-hooks/src/auth/use-phone-sign-in.ts +++ b/packages/firebase-hooks/src/auth/use-phone-sign-in.ts @@ -54,13 +54,14 @@ export interface UsePhoneSignInOptions extends HookErrorOptions { onIdToken?: OnIdToken | null; } -export function usePhoneSignIn( - options?: UsePhoneSignInOptions, -): ReturnType; +/** What `usePhoneSignIn` returns. */ +export type UsePhoneSignInResult = ReturnType; + +export function usePhoneSignIn(options?: UsePhoneSignInOptions): UsePhoneSignInResult; export function usePhoneSignIn( auth: Auth | null, options?: UsePhoneSignInOptions, -): ReturnType; +): UsePhoneSignInResult; export function usePhoneSignIn( authOrOptions?: Auth | null | UsePhoneSignInOptions, maybeOptions?: UsePhoneSignInOptions, diff --git a/packages/firebase-hooks/src/auth/use-reauthenticate.ts b/packages/firebase-hooks/src/auth/use-reauthenticate.ts index 500b389..f5ac933 100644 --- a/packages/firebase-hooks/src/auth/use-reauthenticate.ts +++ b/packages/firebase-hooks/src/auth/use-reauthenticate.ts @@ -33,13 +33,14 @@ import { useAuthTask, } from "./_shared"; -export function useReauthenticate( - options?: HookErrorOptions, -): ReturnType; +/** What `useReauthenticate` returns. */ +export type UseReauthenticateResult = ReturnType; + +export function useReauthenticate(options?: HookErrorOptions): UseReauthenticateResult; export function useReauthenticate( auth: Auth | null, options?: HookErrorOptions, -): ReturnType; +): UseReauthenticateResult; export function useReauthenticate( authOrOptions?: Auth | null | HookErrorOptions, maybeOptions?: HookErrorOptions, diff --git a/packages/firebase-hooks/src/auth/use-send-email-verification.ts b/packages/firebase-hooks/src/auth/use-send-email-verification.ts index 81091ce..327c376 100644 --- a/packages/firebase-hooks/src/auth/use-send-email-verification.ts +++ b/packages/firebase-hooks/src/auth/use-send-email-verification.ts @@ -43,13 +43,18 @@ export interface UseSendEmailVerificationOptions extends HookErrorOptions { sendEmail?: EmailSender | null; } +/** What `useSendEmailVerification` returns. */ +export type UseSendEmailVerificationResult = ReturnType< + typeof useSendEmailVerificationBase +>; + export function useSendEmailVerification( options?: UseSendEmailVerificationOptions, -): ReturnType; +): UseSendEmailVerificationResult; export function useSendEmailVerification( auth: Auth | null, options?: UseSendEmailVerificationOptions, -): ReturnType; +): UseSendEmailVerificationResult; export function useSendEmailVerification( authOrOptions?: Auth | null | UseSendEmailVerificationOptions, maybeOptions?: UseSendEmailVerificationOptions, diff --git a/packages/firebase-hooks/src/auth/use-send-password-reset-email.ts b/packages/firebase-hooks/src/auth/use-send-password-reset-email.ts index 6d11a7e..110307b 100644 --- a/packages/firebase-hooks/src/auth/use-send-password-reset-email.ts +++ b/packages/firebase-hooks/src/auth/use-send-password-reset-email.ts @@ -45,13 +45,18 @@ export interface UseSendPasswordResetEmailOptions extends HookErrorOptions { sendEmail?: EmailSender | null; } +/** What `useSendPasswordResetEmail` returns. */ +export type UseSendPasswordResetEmailResult = ReturnType< + typeof useSendPasswordResetEmailBase +>; + export function useSendPasswordResetEmail( options?: UseSendPasswordResetEmailOptions, -): ReturnType; +): UseSendPasswordResetEmailResult; export function useSendPasswordResetEmail( auth: Auth | null, options?: UseSendPasswordResetEmailOptions, -): ReturnType; +): UseSendPasswordResetEmailResult; export function useSendPasswordResetEmail( authOrOptions?: Auth | null | UseSendPasswordResetEmailOptions, maybeOptions?: UseSendPasswordResetEmailOptions, diff --git a/packages/firebase-hooks/src/auth/use-signup.ts b/packages/firebase-hooks/src/auth/use-signup.ts index 08bec64..88a8522 100644 --- a/packages/firebase-hooks/src/auth/use-signup.ts +++ b/packages/firebase-hooks/src/auth/use-signup.ts @@ -52,11 +52,11 @@ export interface UseSignupOptions extends HookErrorOptions { onIdToken?: OnIdToken | null; } -export function useSignup(options?: UseSignupOptions): ReturnType; -export function useSignup( - auth: Auth | null, - options?: UseSignupOptions, -): ReturnType; +/** What `useSignup` returns. */ +export type UseSignupResult = ReturnType; + +export function useSignup(options?: UseSignupOptions): UseSignupResult; +export function useSignup(auth: Auth | null, options?: UseSignupOptions): UseSignupResult; export function useSignup( authOrOptions?: Auth | null | UseSignupOptions, maybeOptions?: UseSignupOptions, diff --git a/packages/firebase-hooks/src/auth/use-unlink-provider.ts b/packages/firebase-hooks/src/auth/use-unlink-provider.ts index eeb0db7..46a2c47 100644 --- a/packages/firebase-hooks/src/auth/use-unlink-provider.ts +++ b/packages/firebase-hooks/src/auth/use-unlink-provider.ts @@ -22,13 +22,14 @@ import { useAuthTask, } from "./_shared"; -export function useUnlinkProvider( - options?: HookErrorOptions, -): ReturnType; +/** What `useUnlinkProvider` returns. */ +export type UseUnlinkProviderResult = ReturnType; + +export function useUnlinkProvider(options?: HookErrorOptions): UseUnlinkProviderResult; export function useUnlinkProvider( auth: Auth | null, options?: HookErrorOptions, -): ReturnType; +): UseUnlinkProviderResult; export function useUnlinkProvider( authOrOptions?: Auth | null | HookErrorOptions, maybeOptions?: HookErrorOptions, diff --git a/packages/firebase-hooks/src/auth/use-update-email.ts b/packages/firebase-hooks/src/auth/use-update-email.ts index b34da94..49914c6 100644 --- a/packages/firebase-hooks/src/auth/use-update-email.ts +++ b/packages/firebase-hooks/src/auth/use-update-email.ts @@ -41,13 +41,14 @@ export interface UseUpdateEmailOptions extends HookErrorOptions { actionCodeSettings?: ActionCodeSettings | null; } -export function useUpdateEmail( - options?: UseUpdateEmailOptions, -): ReturnType; +/** What `useUpdateEmail` returns. */ +export type UseUpdateEmailResult = ReturnType; + +export function useUpdateEmail(options?: UseUpdateEmailOptions): UseUpdateEmailResult; export function useUpdateEmail( auth: Auth | null, options?: UseUpdateEmailOptions, -): ReturnType; +): UseUpdateEmailResult; export function useUpdateEmail( authOrOptions?: Auth | null | UseUpdateEmailOptions, maybeOptions?: UseUpdateEmailOptions, diff --git a/packages/firebase-hooks/src/auth/use-update-password.ts b/packages/firebase-hooks/src/auth/use-update-password.ts index 1d9bd58..c06c0d3 100644 --- a/packages/firebase-hooks/src/auth/use-update-password.ts +++ b/packages/firebase-hooks/src/auth/use-update-password.ts @@ -27,13 +27,14 @@ import { useAuthTask, } from "./_shared"; -export function useUpdatePassword( - options?: HookErrorOptions, -): ReturnType; +/** What `useUpdatePassword` returns. */ +export type UseUpdatePasswordResult = ReturnType; + +export function useUpdatePassword(options?: HookErrorOptions): UseUpdatePasswordResult; export function useUpdatePassword( auth: Auth | null, options?: HookErrorOptions, -): ReturnType; +): UseUpdatePasswordResult; export function useUpdatePassword( authOrOptions?: Auth | null | HookErrorOptions, maybeOptions?: HookErrorOptions, diff --git a/packages/firebase-hooks/src/auth/use-update-profile.ts b/packages/firebase-hooks/src/auth/use-update-profile.ts index 55d4803..673446a 100644 --- a/packages/firebase-hooks/src/auth/use-update-profile.ts +++ b/packages/firebase-hooks/src/auth/use-update-profile.ts @@ -22,13 +22,14 @@ import { useAuthTask, } from "./_shared"; -export function useUpdateProfile( - options?: HookErrorOptions, -): ReturnType; +/** What `useUpdateProfile` returns. */ +export type UseUpdateProfileResult = ReturnType; + +export function useUpdateProfile(options?: HookErrorOptions): UseUpdateProfileResult; export function useUpdateProfile( auth: Auth | null, options?: HookErrorOptions, -): ReturnType; +): UseUpdateProfileResult; export function useUpdateProfile( authOrOptions?: Auth | null | HookErrorOptions, maybeOptions?: HookErrorOptions, diff --git a/packages/firebase-hooks/src/auth/use-verify-email.ts b/packages/firebase-hooks/src/auth/use-verify-email.ts index 7265b7e..c50c5e2 100644 --- a/packages/firebase-hooks/src/auth/use-verify-email.ts +++ b/packages/firebase-hooks/src/auth/use-verify-email.ts @@ -42,15 +42,18 @@ export interface UseVerifyEmailOptions extends HookErrorOptions { onVerified?: (user: User | null) => void | Promise; } +/** What `useVerifyEmail` returns. */ +export type UseVerifyEmailResult = ReturnType; + export function useVerifyEmail( oobCode: string | null, options?: UseVerifyEmailOptions, -): ReturnType; +): UseVerifyEmailResult; export function useVerifyEmail( auth: Auth | null, oobCode: string | null, options?: UseVerifyEmailOptions, -): ReturnType; +): UseVerifyEmailResult; export function useVerifyEmail( ...args: | [oobCode: string | null, options?: UseVerifyEmailOptions] From e0cf88f176976e18a0a432e827cdb616461a2884 Mon Sep 17 00:00:00 2001 From: timonwa Date: Tue, 8 Sep 2026 23:31:54 +0100 Subject: [PATCH 17/23] feat(auth)!: report status with derived booleans instead of loading/success MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit loading + a hand-rolled success flag on six hooks becomes one status field and isIdle/isPending/isSuccess/isError derived from it, plus reset() — TanStack's mutation result, field for field. useVerifyEmail keeps AsyncStatus (no idle). --- packages/firebase-hooks/src/auth/_shared.ts | 38 ++++++++--- .../src/auth/auth-provider.test.tsx | 67 ++++++++++++++++++- .../src/auth/use-anonymous-sign-in.test.tsx | 2 +- .../src/auth/use-anonymous-sign-in.ts | 5 +- .../auth/use-confirm-password-reset.test.tsx | 8 +-- .../src/auth/use-confirm-password-reset.ts | 22 +++--- .../src/auth/use-custom-token-sign-in.ts | 5 +- .../src/auth/use-delete-account.ts | 5 +- .../src/auth/use-email-link-sign-in.ts | 15 ++++- .../src/auth/use-link-provider.ts | 15 ++++- packages/firebase-hooks/src/auth/use-login.ts | 5 +- .../firebase-hooks/src/auth/use-logout.ts | 5 +- .../src/auth/use-oauth-sign-in.ts | 5 +- .../src/auth/use-phone-sign-in.ts | 16 ++++- .../src/auth/use-reauthenticate.ts | 15 ++++- .../auth/use-send-email-verification.test.tsx | 6 +- .../src/auth/use-send-email-verification.ts | 9 +-- .../use-send-password-reset-email.test.tsx | 14 ++-- .../src/auth/use-send-password-reset-email.ts | 14 +--- .../firebase-hooks/src/auth/use-signup.ts | 5 +- .../src/auth/use-unlink-provider.ts | 5 +- .../src/auth/use-update-email.test.tsx | 4 +- .../src/auth/use-update-email.ts | 9 +-- .../src/auth/use-update-password.test.tsx | 4 +- .../src/auth/use-update-password.ts | 9 +-- .../src/auth/use-update-profile.test.tsx | 4 +- .../src/auth/use-update-profile.ts | 9 +-- .../src/auth/use-verify-email.ts | 10 ++- packages/firebase-hooks/src/core/index.ts | 1 + packages/firebase-hooks/src/core/types.ts | 6 ++ 30 files changed, 236 insertions(+), 101 deletions(-) diff --git a/packages/firebase-hooks/src/auth/_shared.ts b/packages/firebase-hooks/src/auth/_shared.ts index 7e3ba15..766deaa 100644 --- a/packages/firebase-hooks/src/auth/_shared.ts +++ b/packages/firebase-hooks/src/auth/_shared.ts @@ -13,7 +13,12 @@ import { } from "firebase/auth"; import { createContext, useCallback, useContext, useRef, useState } from "react"; import { getFirebaseErrorCode } from "../core/get-firebase-error-code"; -import type { HookErrorContext, HookErrorOptions, HookResult } from "../core/types"; +import type { + ActionStatus, + HookErrorContext, + HookErrorOptions, + HookResult, +} from "../core/types"; export type { HookErrorContext, HookErrorOptions, HookResult }; // Re-exported so every hook in this module imports its shared shapes from one @@ -201,12 +206,12 @@ export function useAuthErrorObserver() { } /** - * The loading/error/try-catch skeleton every action hook repeats. `run` never + * The status/error/try-catch skeleton every action hook repeats. `run` never * throws: failures come back as `{ success: false, error, code, cause }` with - * the `error` state set to the same message. + * `status` set to `"error"` and `error` carrying the same message. */ export function useAuthTask(options?: HookErrorOptions) { - const [loading, setLoading] = useState(false); + const [status, setStatus] = useState("idle"); const [error, setError] = useState(null); const resolveMessage = useErrorMessageResolver(options); const notifyError = useAuthErrorObserver(); @@ -217,23 +222,40 @@ export function useAuthTask(options?: HookErrorOptions) { fallback: string, task: () => Promise, ): Promise> => { - setLoading(true); + setStatus("pending"); setError(null); try { const value = await task(); + setStatus("success"); return { success: true, ...value }; } catch (cause) { const message = resolveMessage(cause, fallback); const code = getFirebaseErrorCode(cause); + setStatus("error"); setError(message); notifyError(cause, { action, code, message }); return { success: false, error: message, code, cause }; - } finally { - setLoading(false); } }, [resolveMessage, notifyError], ); - return { loading, error, setError, run }; + // Back to idle — for a form the user retries with different input, so a stale + // success or error message doesn't sit under the new attempt. + const reset = useCallback(() => { + setStatus("idle"); + setError(null); + }, []); + + return { + status, + isIdle: status === "idle", + isPending: status === "pending", + isSuccess: status === "success", + isError: status === "error", + error, + setError, + reset, + run, + }; } diff --git a/packages/firebase-hooks/src/auth/auth-provider.test.tsx b/packages/firebase-hooks/src/auth/auth-provider.test.tsx index 07017bb..400f96c 100644 --- a/packages/firebase-hooks/src/auth/auth-provider.test.tsx +++ b/packages/firebase-hooks/src/auth/auth-provider.test.tsx @@ -107,13 +107,13 @@ describe("error model", () => { act(() => { pending = result.current.login("a@b.c", "pw"); }); - expect(result.current.loading).toBe(true); + expect(result.current.isPending).toBe(true); await act(async () => { rejectSignIn(new Error("boom")); await pending; }); - expect(result.current.loading).toBe(false); + expect(result.current.isPending).toBe(false); expect(result.current.error).toBe("boom"); }); }); @@ -407,3 +407,66 @@ describe("provider-level senders", () => { expect(sendPasswordResetEmail).toHaveBeenCalled(); }); }); + +describe("status", () => { + it("starts idle, is pending while running, and settles on success", async () => { + let finish!: (value: unknown) => void; + vi.mocked(signInWithEmailAndPassword).mockImplementation( + () => new Promise((resolve) => (finish = resolve)) as never, + ); + const { result } = renderHook(() => useLogin(makeAuth())); + + expect(result.current.status).toBe("idle"); + expect(result.current.isIdle).toBe(true); + + let pending!: Promise; + await act(async () => { + pending = result.current.login("a@b.c", "pw"); + }); + expect(result.current.status).toBe("pending"); + expect(result.current.isPending).toBe(true); + + await act(async () => { + finish({ user: makeUser() }); + await pending; + }); + expect(result.current.status).toBe("success"); + expect(result.current.isSuccess).toBe(true); + expect(result.current.isPending).toBe(false); + }); + + it("settles on error with the message, and reset() returns to idle", async () => { + vi.mocked(signInWithEmailAndPassword).mockRejectedValue( + new FakeFirebaseError( + "auth/invalid-credential", + "Firebase: Error (auth/invalid-credential).", + ), + ); + const { result } = renderHook(() => useLogin(makeAuth())); + + await act(async () => { + await result.current.login("a@b.c", "pw"); + }); + expect(result.current.status).toBe("error"); + expect(result.current.isError).toBe(true); + expect(result.current.error).toBe("Firebase: Error (auth/invalid-credential)."); + + act(() => result.current.reset()); + expect(result.current.status).toBe("idle"); + expect(result.current.error).toBeNull(); + expect(result.current.isError).toBe(false); + }); + + it("the booleans are derived from status, never independently set", async () => { + vi.mocked(signInWithEmailAndPassword).mockResolvedValue({ + user: makeUser(), + } as never); + const { result } = renderHook(() => useLogin(makeAuth())); + await act(async () => { + await result.current.login("a@b.c", "pw"); + }); + const { status, isIdle, isPending, isSuccess, isError } = result.current; + expect([isIdle, isPending, isSuccess, isError].filter(Boolean)).toHaveLength(1); + expect(isSuccess).toBe(status === "success"); + }); +}); diff --git a/packages/firebase-hooks/src/auth/use-anonymous-sign-in.test.tsx b/packages/firebase-hooks/src/auth/use-anonymous-sign-in.test.tsx index 1316b86..230f2bf 100644 --- a/packages/firebase-hooks/src/auth/use-anonymous-sign-in.test.tsx +++ b/packages/firebase-hooks/src/auth/use-anonymous-sign-in.test.tsx @@ -52,6 +52,6 @@ describe("useAnonymousSignIn", () => { expect(result.current.error).toBe( "Firebase: Error (auth/admin-restricted-operation).", ); - expect(result.current.loading).toBe(false); + expect(result.current.isPending).toBe(false); }); }); diff --git a/packages/firebase-hooks/src/auth/use-anonymous-sign-in.ts b/packages/firebase-hooks/src/auth/use-anonymous-sign-in.ts index c426f8a..e19d56c 100644 --- a/packages/firebase-hooks/src/auth/use-anonymous-sign-in.ts +++ b/packages/firebase-hooks/src/auth/use-anonymous-sign-in.ts @@ -56,7 +56,8 @@ export function useAnonymousSignIn( } function useAnonymousSignInBase(auth: Auth | null, options: UseAnonymousSignInOptions) { - const { loading, error, run } = useAuthTask(options); + const { status, isIdle, isPending, isSuccess, isError, error, reset, run } = + useAuthTask(options); const onIdToken = useResolvedConfig("onIdToken", options.onIdToken); const signIn = (): Promise> => @@ -66,5 +67,5 @@ function useAnonymousSignInBase(auth: Auth | null, options: UseAnonymousSignInOp return { user: credential.user, credential }; }); - return { signIn, loading, error }; + return { signIn, status, isIdle, isPending, isSuccess, isError, error, reset }; } diff --git a/packages/firebase-hooks/src/auth/use-confirm-password-reset.test.tsx b/packages/firebase-hooks/src/auth/use-confirm-password-reset.test.tsx index 79e0999..fd83157 100644 --- a/packages/firebase-hooks/src/auth/use-confirm-password-reset.test.tsx +++ b/packages/firebase-hooks/src/auth/use-confirm-password-reset.test.tsx @@ -27,7 +27,7 @@ describe("useConfirmPasswordReset", () => { "oob-1", "new-pw", ); - expect(result.current.success).toBe(true); + expect(result.current.isSuccess).toBe(true); }); it("an expired link fails verifyCode with error, code, and cause", async () => { @@ -62,7 +62,7 @@ describe("useConfirmPasswordReset", () => { outcome = await result.current.confirm("oob-1", "123"); }); expect(outcome?.success).toBe(false); - expect(result.current.success).toBe(false); + expect(result.current.isSuccess).toBe(false); expect(result.current.error).toBe("Firebase: Error (auth/weak-password)."); }); @@ -78,9 +78,9 @@ describe("useConfirmPasswordReset", () => { await result.current.confirm("oob-1", "123"); }); expect(result.current.error).not.toBe(null); - act(() => result.current.resetState()); + act(() => result.current.reset()); expect(result.current.error).toBe(null); - expect(result.current.success).toBe(false); + expect(result.current.isSuccess).toBe(false); }); it("notifies onError with the flow-specific action ids — verify vs confirm", async () => { diff --git a/packages/firebase-hooks/src/auth/use-confirm-password-reset.ts b/packages/firebase-hooks/src/auth/use-confirm-password-reset.ts index 3a2d0cb..c5fb91d 100644 --- a/packages/firebase-hooks/src/auth/use-confirm-password-reset.ts +++ b/packages/firebase-hooks/src/auth/use-confirm-password-reset.ts @@ -17,7 +17,6 @@ "use client"; import { type Auth, confirmPasswordReset, verifyPasswordResetCode } from "firebase/auth"; -import { useState } from "react"; import { type HookErrorOptions, type HookResult, @@ -46,8 +45,8 @@ export function useConfirmPasswordReset( } function useConfirmPasswordResetBase(auth: Auth | null, options: HookErrorOptions) { - const { loading, error, setError, run } = useAuthTask(options); - const [success, setSuccess] = useState(false); + const { status, isIdle, isPending, isSuccess, isError, error, reset, setError, run } = + useAuthTask(options); const verifyCode = (oobCode: string): Promise> => run( @@ -60,7 +59,6 @@ function useConfirmPasswordResetBase(auth: Auth | null, options: HookErrorOption ); const confirm = async (oobCode: string, newPassword: string): Promise => { - setSuccess(false); const result = await run( "confirm-password-reset", "Failed to reset password", @@ -69,14 +67,18 @@ function useConfirmPasswordResetBase(auth: Auth | null, options: HookErrorOption return {}; }, ); - if (result.success) setSuccess(true); return result; }; - const resetState = () => { - setError(null); - setSuccess(false); + return { + confirm, + verifyCode, + status, + isIdle, + isPending, + isSuccess, + isError, + error, + reset, }; - - return { confirm, verifyCode, loading, error, success, resetState }; } diff --git a/packages/firebase-hooks/src/auth/use-custom-token-sign-in.ts b/packages/firebase-hooks/src/auth/use-custom-token-sign-in.ts index 919c9ff..840eb98 100644 --- a/packages/firebase-hooks/src/auth/use-custom-token-sign-in.ts +++ b/packages/firebase-hooks/src/auth/use-custom-token-sign-in.ts @@ -60,7 +60,8 @@ function useCustomTokenSignInBase( auth: Auth | null, options: UseCustomTokenSignInOptions, ) { - const { loading, error, run } = useAuthTask(options); + const { status, isIdle, isPending, isSuccess, isError, error, reset, run } = + useAuthTask(options); const onIdToken = useResolvedConfig("onIdToken", options.onIdToken); const signIn = ( @@ -72,5 +73,5 @@ function useCustomTokenSignInBase( return { user: credential.user, credential }; }); - return { signIn, loading, error }; + return { signIn, status, isIdle, isPending, isSuccess, isError, error, reset }; } diff --git a/packages/firebase-hooks/src/auth/use-delete-account.ts b/packages/firebase-hooks/src/auth/use-delete-account.ts index 795175d..2251da2 100644 --- a/packages/firebase-hooks/src/auth/use-delete-account.ts +++ b/packages/firebase-hooks/src/auth/use-delete-account.ts @@ -57,7 +57,8 @@ export function useDeleteAccount( } function useDeleteAccountBase(auth: Auth | null, options: UseDeleteAccountOptions) { - const { loading, error, run } = useAuthTask(options); + const { status, isIdle, isPending, isSuccess, isError, error, reset, run } = + useAuthTask(options); const deleteAccount = ({ currentPassword, @@ -72,5 +73,5 @@ function useDeleteAccountBase(auth: Auth | null, options: UseDeleteAccountOption return {}; }); - return { deleteAccount, loading, error }; + return { deleteAccount, status, isIdle, isPending, isSuccess, isError, error, reset }; } diff --git a/packages/firebase-hooks/src/auth/use-email-link-sign-in.ts b/packages/firebase-hooks/src/auth/use-email-link-sign-in.ts index 581f9a7..cb34743 100644 --- a/packages/firebase-hooks/src/auth/use-email-link-sign-in.ts +++ b/packages/firebase-hooks/src/auth/use-email-link-sign-in.ts @@ -97,7 +97,8 @@ export function useEmailLinkSignIn( function useEmailLinkSignInBase(auth: Auth | null, options: UseEmailLinkSignInOptions) { const { storageKey = "emailForSignIn" } = options; - const { loading, error, setError, run } = useAuthTask(options); + const { status, isIdle, isPending, isSuccess, isError, error, reset, setError, run } = + useAuthTask(options); const onIdToken = useResolvedConfig("onIdToken", options.onIdToken); const actionCodeSettings = useResolvedConfig( "actionCodeSettings", @@ -168,5 +169,15 @@ function useEmailLinkSignInBase(auth: Auth | null, options: UseEmailLinkSignInOp return result; }; - return { sendLink, completeSignIn, loading, error, setError }; + return { + sendLink, + completeSignIn, + status, + isIdle, + isPending, + isSuccess, + isError, + error, + reset, + }; } diff --git a/packages/firebase-hooks/src/auth/use-link-provider.ts b/packages/firebase-hooks/src/auth/use-link-provider.ts index d054f84..3d79dc3 100644 --- a/packages/firebase-hooks/src/auth/use-link-provider.ts +++ b/packages/firebase-hooks/src/auth/use-link-provider.ts @@ -47,7 +47,8 @@ export function useLinkProvider( } function useLinkProviderBase(auth: Auth | null, options: HookErrorOptions) { - const { loading, error, run } = useAuthTask(options); + const { status, isIdle, isPending, isSuccess, isError, error, reset, run } = + useAuthTask(options); const linkWithProvider = ( provider: FirebaseAuthProvider, @@ -69,5 +70,15 @@ function useLinkProviderBase(auth: Auth | null, options: HookErrorOptions) { return { user: credential.user, credential }; }); - return { linkWithProvider, linkWithPassword, loading, error }; + return { + linkWithProvider, + linkWithPassword, + status, + isIdle, + isPending, + isSuccess, + isError, + error, + reset, + }; } diff --git a/packages/firebase-hooks/src/auth/use-login.ts b/packages/firebase-hooks/src/auth/use-login.ts index 302285e..2c3189b 100644 --- a/packages/firebase-hooks/src/auth/use-login.ts +++ b/packages/firebase-hooks/src/auth/use-login.ts @@ -64,7 +64,8 @@ export function useLogin( } function useLoginBase(auth: Auth | null, options: UseLoginOptions) { - const { loading, error, run } = useAuthTask(options); + const { status, isIdle, isPending, isSuccess, isError, error, reset, run } = + useAuthTask(options); const onIdToken = useResolvedConfig("onIdToken", options.onIdToken); const login = ( @@ -81,5 +82,5 @@ function useLoginBase(auth: Auth | null, options: UseLoginOptions) { return { user: credential.user, credential }; }); - return { login, loading, error }; + return { login, status, isIdle, isPending, isSuccess, isError, error, reset }; } diff --git a/packages/firebase-hooks/src/auth/use-logout.ts b/packages/firebase-hooks/src/auth/use-logout.ts index 33ae73e..f934f51 100644 --- a/packages/firebase-hooks/src/auth/use-logout.ts +++ b/packages/firebase-hooks/src/auth/use-logout.ts @@ -45,7 +45,8 @@ export function useLogout( } function useLogoutBase(auth: Auth | null, options: UseLogoutOptions) { - const { loading, error, run } = useAuthTask(options); + const { status, isIdle, isPending, isSuccess, isError, error, reset, run } = + useAuthTask(options); const onBeforeSignOut = useResolvedConfig("onBeforeSignOut", options.onBeforeSignOut); const logout = (): Promise => @@ -55,5 +56,5 @@ function useLogoutBase(auth: Auth | null, options: UseLogoutOptions) { return {}; }); - return { logout, loading, error }; + return { logout, status, isIdle, isPending, isSuccess, isError, error, reset }; } diff --git a/packages/firebase-hooks/src/auth/use-oauth-sign-in.ts b/packages/firebase-hooks/src/auth/use-oauth-sign-in.ts index b95c6de..c7ccc7f 100644 --- a/packages/firebase-hooks/src/auth/use-oauth-sign-in.ts +++ b/packages/firebase-hooks/src/auth/use-oauth-sign-in.ts @@ -67,7 +67,8 @@ export function useOAuthSignIn( } function useOAuthSignInBase(auth: Auth | null, options: UseOAuthSignInOptions) { - const { loading, error, run } = useAuthTask(options); + const { status, isIdle, isPending, isSuccess, isError, error, reset, run } = + useAuthTask(options); // getRedirectResult consumes the pending result — guard Strict Mode's double effect. const redirectHandledRef = useRef(false); // Read the callback through a ref so an inline option object can't re-trigger the effect. @@ -101,5 +102,5 @@ function useOAuthSignInBase(auth: Auth | null, options: UseOAuthSignInOptions) { return { user: credential.user, credential }; }); - return { signIn, loading, error }; + return { signIn, status, isIdle, isPending, isSuccess, isError, error, reset }; } diff --git a/packages/firebase-hooks/src/auth/use-phone-sign-in.ts b/packages/firebase-hooks/src/auth/use-phone-sign-in.ts index 6b708e2..490850e 100644 --- a/packages/firebase-hooks/src/auth/use-phone-sign-in.ts +++ b/packages/firebase-hooks/src/auth/use-phone-sign-in.ts @@ -70,7 +70,8 @@ export function usePhoneSignIn( } function usePhoneSignInBase(auth: Auth | null, options: UsePhoneSignInOptions) { - const { loading, error, run } = useAuthTask(options); + const { status, isIdle, isPending, isSuccess, isError, error, reset, run } = + useAuthTask(options); const onIdToken = useResolvedConfig("onIdToken", options.onIdToken); const [codeSent, setCodeSent] = useState(false); const confirmationRef = useRef(null); @@ -111,5 +112,16 @@ function usePhoneSignInBase(auth: Auth | null, options: UsePhoneSignInOptions) { return { user: credential.user, credential }; }); - return { sendCode, confirmCode, codeSent, loading, error }; + return { + sendCode, + confirmCode, + codeSent, + status, + isIdle, + isPending, + isSuccess, + isError, + error, + reset, + }; } diff --git a/packages/firebase-hooks/src/auth/use-reauthenticate.ts b/packages/firebase-hooks/src/auth/use-reauthenticate.ts index f5ac933..48345db 100644 --- a/packages/firebase-hooks/src/auth/use-reauthenticate.ts +++ b/packages/firebase-hooks/src/auth/use-reauthenticate.ts @@ -49,7 +49,8 @@ export function useReauthenticate( } function useReauthenticateBase(auth: Auth | null, options: HookErrorOptions) { - const { loading, error, run } = useAuthTask(options); + const { status, isIdle, isPending, isSuccess, isError, error, reset, run } = + useAuthTask(options); const reauthenticateWithPassword = (currentPassword: string): Promise => run("reauthenticate", "Reauthentication failed", async () => { @@ -65,5 +66,15 @@ function useReauthenticateBase(auth: Auth | null, options: HookErrorOptions) { return {}; }); - return { reauthenticateWithPassword, reauthenticateWithProvider, loading, error }; + return { + reauthenticateWithPassword, + reauthenticateWithProvider, + status, + isIdle, + isPending, + isSuccess, + isError, + error, + reset, + }; } diff --git a/packages/firebase-hooks/src/auth/use-send-email-verification.test.tsx b/packages/firebase-hooks/src/auth/use-send-email-verification.test.tsx index eb9400d..ea02896 100644 --- a/packages/firebase-hooks/src/auth/use-send-email-verification.test.tsx +++ b/packages/firebase-hooks/src/auth/use-send-email-verification.test.tsx @@ -29,7 +29,7 @@ describe("useSendEmailVerification", () => { await result.current.send(); }); expect(sendEmailVerification).toHaveBeenCalledWith(user, settings); - expect(result.current.success).toBe(true); + expect(result.current.isSuccess).toBe(true); }); it("a rate-limited resend keeps success false and carries code and cause", async () => { @@ -48,7 +48,7 @@ describe("useSendEmailVerification", () => { code: "auth/too-many-requests", cause: firebaseError, }); - expect(result.current.success).toBe(false); + expect(result.current.isSuccess).toBe(false); }); }); @@ -65,7 +65,7 @@ describe("sendEmail", () => { expect(sendEmail).toHaveBeenCalledWith(expect.objectContaining({ email: "who@b.c" })); expect(sendEmailVerification).not.toHaveBeenCalled(); - expect(result.current.success).toBe(true); + expect(result.current.isSuccess).toBe(true); }); it("fails clearly on an account with no email, rather than sending to undefined", async () => { diff --git a/packages/firebase-hooks/src/auth/use-send-email-verification.ts b/packages/firebase-hooks/src/auth/use-send-email-verification.ts index 327c376..8836fdd 100644 --- a/packages/firebase-hooks/src/auth/use-send-email-verification.ts +++ b/packages/firebase-hooks/src/auth/use-send-email-verification.ts @@ -22,7 +22,6 @@ "use client"; import { type ActionCodeSettings, type Auth, sendEmailVerification } from "firebase/auth"; -import { useState } from "react"; import { type EmailSender, type HookErrorOptions, @@ -66,16 +65,15 @@ function useSendEmailVerificationBase( auth: Auth | null, options: UseSendEmailVerificationOptions, ) { - const { loading, error, run } = useAuthTask(options); + const { status, isIdle, isPending, isSuccess, isError, error, reset, run } = + useAuthTask(options); const actionCodeSettings = useResolvedConfig( "actionCodeSettings", options.actionCodeSettings, ); const sendEmail = useResolvedConfig("sendEmailVerification", options.sendEmail); - const [success, setSuccess] = useState(false); const send = async (): Promise => { - setSuccess(false); const result = await run( "send-email-verification", "Failed to send verification email", @@ -91,9 +89,8 @@ function useSendEmailVerificationBase( return {}; }, ); - if (result.success) setSuccess(true); return result; }; - return { send, loading, error, success }; + return { send, status, isIdle, isPending, isSuccess, isError, error, reset }; } diff --git a/packages/firebase-hooks/src/auth/use-send-password-reset-email.test.tsx b/packages/firebase-hooks/src/auth/use-send-password-reset-email.test.tsx index 0f421ed..5921fb2 100644 --- a/packages/firebase-hooks/src/auth/use-send-password-reset-email.test.tsx +++ b/packages/firebase-hooks/src/auth/use-send-password-reset-email.test.tsx @@ -17,9 +17,9 @@ describe("useSendPasswordResetEmail", () => { await result.current.send("a@b.c"); }); expect(sendPasswordResetEmail).toHaveBeenCalled(); - expect(result.current.success).toBe(true); - act(() => result.current.resetState()); - expect(result.current.success).toBe(false); + expect(result.current.isSuccess).toBe(true); + act(() => result.current.reset()); + expect(result.current.isSuccess).toBe(false); }); it("hook-level actionCodeSettings wins over the provider default; null opts out", async () => { @@ -73,7 +73,7 @@ describe("useSendPasswordResetEmail", () => { code: "auth/invalid-email", cause: firebaseError, }); - expect(result.current.success).toBe(false); + expect(result.current.isSuccess).toBe(false); expect(result.current.error).toBe("Firebase: Error (auth/invalid-email)."); }); }); @@ -91,7 +91,7 @@ describe("sendEmail", () => { expect(sendEmail).toHaveBeenCalledWith(expect.objectContaining({ email: "a@b.c" })); expect(sendPasswordResetEmail).not.toHaveBeenCalled(); - expect(result.current.success).toBe(true); + expect(result.current.isSuccess).toBe(true); }); it("a throwing sender fails like any other error, leaving success false", async () => { @@ -108,7 +108,7 @@ describe("sendEmail", () => { }); expect(outcome).toMatchObject({ success: false, error: "rate limited" }); - expect(result.current.success).toBe(false); + expect(result.current.isSuccess).toBe(false); }); it("needs no actionCodeSettings, since Firebase is not the sender", async () => { @@ -121,6 +121,6 @@ describe("sendEmail", () => { await result.current.send("a@b.c"); }); - expect(result.current.success).toBe(true); + expect(result.current.isSuccess).toBe(true); }); }); diff --git a/packages/firebase-hooks/src/auth/use-send-password-reset-email.ts b/packages/firebase-hooks/src/auth/use-send-password-reset-email.ts index 110307b..24abaed 100644 --- a/packages/firebase-hooks/src/auth/use-send-password-reset-email.ts +++ b/packages/firebase-hooks/src/auth/use-send-password-reset-email.ts @@ -27,7 +27,6 @@ import { type Auth, sendPasswordResetEmail, } from "firebase/auth"; -import { useState } from "react"; import { type EmailSender, type HookErrorOptions, @@ -68,16 +67,15 @@ function useSendPasswordResetEmailBase( auth: Auth | null, options: UseSendPasswordResetEmailOptions, ) { - const { loading, error, setError, run } = useAuthTask(options); + const { status, isIdle, isPending, isSuccess, isError, error, reset, setError, run } = + useAuthTask(options); const actionCodeSettings = useResolvedConfig( "actionCodeSettings", options.actionCodeSettings, ); const sendEmail = useResolvedConfig("sendPasswordReset", options.sendEmail); - const [success, setSuccess] = useState(false); const send = async (email: string): Promise => { - setSuccess(false); const result = await run( "send-password-reset-email", "Failed to send reset email", @@ -90,14 +88,8 @@ function useSendPasswordResetEmailBase( return {}; }, ); - if (result.success) setSuccess(true); return result; }; - const resetState = () => { - setError(null); - setSuccess(false); - }; - - return { send, loading, error, success, resetState }; + return { send, status, isIdle, isPending, isSuccess, isError, error, reset }; } diff --git a/packages/firebase-hooks/src/auth/use-signup.ts b/packages/firebase-hooks/src/auth/use-signup.ts index 88a8522..04df885 100644 --- a/packages/firebase-hooks/src/auth/use-signup.ts +++ b/packages/firebase-hooks/src/auth/use-signup.ts @@ -66,7 +66,8 @@ export function useSignup( function useSignupBase(auth: Auth | null, options: UseSignupOptions) { const { sendVerificationEmail = true } = options; - const { loading, error, run } = useAuthTask(options); + const { status, isIdle, isPending, isSuccess, isError, error, reset, run } = + useAuthTask(options); const onIdToken = useResolvedConfig("onIdToken", options.onIdToken); const signup = ( @@ -90,5 +91,5 @@ function useSignupBase(auth: Auth | null, options: UseSignupOptions) { return { user: credential.user, credential }; }); - return { signup, loading, error }; + return { signup, status, isIdle, isPending, isSuccess, isError, error, reset }; } diff --git a/packages/firebase-hooks/src/auth/use-unlink-provider.ts b/packages/firebase-hooks/src/auth/use-unlink-provider.ts index 46a2c47..8dc42c6 100644 --- a/packages/firebase-hooks/src/auth/use-unlink-provider.ts +++ b/packages/firebase-hooks/src/auth/use-unlink-provider.ts @@ -38,7 +38,8 @@ export function useUnlinkProvider( } function useUnlinkProviderBase(auth: Auth | null, options: HookErrorOptions) { - const { loading, error, run } = useAuthTask(options); + const { status, isIdle, isPending, isSuccess, isError, error, reset, run } = + useAuthTask(options); const unlinkProvider = (providerId: string): Promise> => run("unlink-provider", "Failed to unlink provider", async () => { @@ -46,5 +47,5 @@ function useUnlinkProviderBase(auth: Auth | null, options: HookErrorOptions) { return { user }; }); - return { unlinkProvider, loading, error }; + return { unlinkProvider, status, isIdle, isPending, isSuccess, isError, error, reset }; } diff --git a/packages/firebase-hooks/src/auth/use-update-email.test.tsx b/packages/firebase-hooks/src/auth/use-update-email.test.tsx index d955830..2c9a1f7 100644 --- a/packages/firebase-hooks/src/auth/use-update-email.test.tsx +++ b/packages/firebase-hooks/src/auth/use-update-email.test.tsx @@ -27,7 +27,7 @@ describe("useUpdateEmail", () => { }); expect(order).toEqual(["reauth", "verify"]); expect(verifyBeforeUpdateEmail).toHaveBeenCalledWith(user, "new@b.c", undefined); - expect(result.current.success).toBe(true); + expect(result.current.isSuccess).toBe(true); }); it("without currentPassword goes straight to verifyBeforeUpdateEmail", async () => { @@ -68,7 +68,7 @@ describe("useUpdateEmail", () => { code: "auth/requires-recent-login", cause: firebaseError, }); - expect(result.current.success).toBe(false); + expect(result.current.isSuccess).toBe(false); }); it("a passwordless account can't take the currentPassword path", async () => { diff --git a/packages/firebase-hooks/src/auth/use-update-email.ts b/packages/firebase-hooks/src/auth/use-update-email.ts index 49914c6..17b62ab 100644 --- a/packages/firebase-hooks/src/auth/use-update-email.ts +++ b/packages/firebase-hooks/src/auth/use-update-email.ts @@ -25,7 +25,6 @@ import { type Auth, verifyBeforeUpdateEmail, } from "firebase/auth"; -import { useState } from "react"; import { type HookErrorOptions, type HookResult, @@ -57,12 +56,12 @@ export function useUpdateEmail( } function useUpdateEmailBase(auth: Auth | null, options: UseUpdateEmailOptions) { - const { loading, error, run } = useAuthTask(options); + const { status, isIdle, isPending, isSuccess, isError, error, reset, run } = + useAuthTask(options); const actionCodeSettings = useResolvedConfig( "actionCodeSettings", options.actionCodeSettings, ); - const [success, setSuccess] = useState(false); // Shaped like the SDK's `verifyBeforeUpdateEmail(user, newEmail, …)` — the // required value leads, and the reauthentication this hook adds goes after. @@ -70,16 +69,14 @@ function useUpdateEmailBase(auth: Auth | null, options: UseUpdateEmailOptions) { newEmail: string, { currentPassword }: { currentPassword?: string } = {}, ): Promise => { - setSuccess(false); const result = await run("update-email", "Failed to update email", async () => { const user = requireCurrentUser(auth); if (currentPassword) await reauthenticateUserWithPassword(user, currentPassword); await verifyBeforeUpdateEmail(user, newEmail, actionCodeSettings); return {}; }); - if (result.success) setSuccess(true); return result; }; - return { update, loading, error, success }; + return { update, status, isIdle, isPending, isSuccess, isError, error, reset }; } diff --git a/packages/firebase-hooks/src/auth/use-update-password.test.tsx b/packages/firebase-hooks/src/auth/use-update-password.test.tsx index f62da06..b87ee98 100644 --- a/packages/firebase-hooks/src/auth/use-update-password.test.tsx +++ b/packages/firebase-hooks/src/auth/use-update-password.test.tsx @@ -23,7 +23,7 @@ describe("useUpdatePassword", () => { await result.current.update("new-pw", { currentPassword: "current-pw" }); }); expect(order).toEqual(["reauth", "update"]); - expect(result.current.success).toBe(true); + expect(result.current.isSuccess).toBe(true); }); it("skips reauthentication when currentPassword is omitted", async () => { @@ -52,7 +52,7 @@ describe("useUpdatePassword", () => { code: "auth/requires-recent-login", cause: firebaseError, }); - expect(result.current.success).toBe(false); + expect(result.current.isSuccess).toBe(false); }); it("a passwordless account can't take the currentPassword path", async () => { diff --git a/packages/firebase-hooks/src/auth/use-update-password.ts b/packages/firebase-hooks/src/auth/use-update-password.ts index c06c0d3..0cc822a 100644 --- a/packages/firebase-hooks/src/auth/use-update-password.ts +++ b/packages/firebase-hooks/src/auth/use-update-password.ts @@ -17,7 +17,6 @@ "use client"; import { type Auth, updatePassword } from "firebase/auth"; -import { useState } from "react"; import { type HookErrorOptions, type HookResult, @@ -43,8 +42,8 @@ export function useUpdatePassword( } function useUpdatePasswordBase(auth: Auth | null, options: HookErrorOptions) { - const { loading, error, run } = useAuthTask(options); - const [success, setSuccess] = useState(false); + const { status, isIdle, isPending, isSuccess, isError, error, reset, run } = + useAuthTask(options); // Shaped like the SDK's `updatePassword(user, newPassword)` — the required // value leads, and the reauthentication this hook adds on top goes in the @@ -53,16 +52,14 @@ function useUpdatePasswordBase(auth: Auth | null, options: HookErrorOptions) { newPassword: string, { currentPassword }: { currentPassword?: string } = {}, ): Promise => { - setSuccess(false); const result = await run("update-password", "Failed to update password", async () => { const user = requireCurrentUser(auth); if (currentPassword) await reauthenticateUserWithPassword(user, currentPassword); await updatePassword(user, newPassword); return {}; }); - if (result.success) setSuccess(true); return result; }; - return { update, loading, error, success }; + return { update, status, isIdle, isPending, isSuccess, isError, error, reset }; } diff --git a/packages/firebase-hooks/src/auth/use-update-profile.test.tsx b/packages/firebase-hooks/src/auth/use-update-profile.test.tsx index 75581ae..3d63513 100644 --- a/packages/firebase-hooks/src/auth/use-update-profile.test.tsx +++ b/packages/firebase-hooks/src/auth/use-update-profile.test.tsx @@ -18,7 +18,7 @@ describe("useUpdateProfile", () => { await result.current.update({ displayName: "Ada" }); }); expect(updateProfile).toHaveBeenCalledWith(user, { displayName: "Ada" }); - expect(result.current.success).toBe(true); + expect(result.current.isSuccess).toBe(true); }); it("a failed update keeps success false and carries code and cause", async () => { @@ -37,7 +37,7 @@ describe("useUpdateProfile", () => { code: "auth/network-request-failed", cause: firebaseError, }); - expect(result.current.success).toBe(false); + expect(result.current.isSuccess).toBe(false); expect(result.current.error).toBe("Firebase: Error (auth/network-request-failed)."); }); diff --git a/packages/firebase-hooks/src/auth/use-update-profile.ts b/packages/firebase-hooks/src/auth/use-update-profile.ts index 673446a..56e7e07 100644 --- a/packages/firebase-hooks/src/auth/use-update-profile.ts +++ b/packages/firebase-hooks/src/auth/use-update-profile.ts @@ -13,7 +13,6 @@ "use client"; import { type Auth, updateProfile } from "firebase/auth"; -import { useState } from "react"; import { type HookErrorOptions, type HookResult, @@ -38,21 +37,19 @@ export function useUpdateProfile( } function useUpdateProfileBase(auth: Auth | null, options: HookErrorOptions) { - const { loading, error, run } = useAuthTask(options); - const [success, setSuccess] = useState(false); + const { status, isIdle, isPending, isSuccess, isError, error, reset, run } = + useAuthTask(options); const update = async (profile: { displayName?: string | null; photoURL?: string | null; }): Promise => { - setSuccess(false); const result = await run("update-profile", "Failed to update profile", async () => { await updateProfile(requireCurrentUser(auth), profile); return {}; }); - if (result.success) setSuccess(true); return result; }; - return { update, loading, error, success }; + return { update, status, isIdle, isPending, isSuccess, isError, error, reset }; } diff --git a/packages/firebase-hooks/src/auth/use-verify-email.ts b/packages/firebase-hooks/src/auth/use-verify-email.ts index c50c5e2..568f496 100644 --- a/packages/firebase-hooks/src/auth/use-verify-email.ts +++ b/packages/firebase-hooks/src/auth/use-verify-email.ts @@ -131,5 +131,13 @@ function useVerifyEmailBase( }); }, [auth, oobCode, resolveMessage, notifyError]); - return { status, error, code, cause }; + return { + status, + isPending: status === "pending", + isSuccess: status === "success", + isError: status === "error", + error, + code, + cause, + }; } diff --git a/packages/firebase-hooks/src/core/index.ts b/packages/firebase-hooks/src/core/index.ts index b4bde7e..1cf3d16 100644 --- a/packages/firebase-hooks/src/core/index.ts +++ b/packages/firebase-hooks/src/core/index.ts @@ -7,6 +7,7 @@ export { } from "./format-firebase-error.js"; export { getFirebaseErrorCode } from "./get-firebase-error-code.js"; export type { + ActionStatus, AsyncStatus, HookErrorContext, HookErrorOptions, diff --git a/packages/firebase-hooks/src/core/types.ts b/packages/firebase-hooks/src/core/types.ts index ba9097b..c51ce84 100644 --- a/packages/firebase-hooks/src/core/types.ts +++ b/packages/firebase-hooks/src/core/types.ts @@ -28,6 +28,12 @@ export interface HookErrorOptions { */ export type AsyncStatus = "pending" | "error" | "success"; +/** + * Where an action you call has got to. Unlike `AsyncStatus` it has an idle + * state, because nothing runs until you call the action. + */ +export type ActionStatus = "idle" | AsyncStatus; + /** What failed, for the global `onError` observer. */ export interface HookErrorContext { /** Stable id of the operation: "login", "oauth-sign-in", "update-password", … */ From 8e5930dc535e572ea06adc8a5284b322f89235dc Mon Sep 17 00:00:00 2001 From: timonwa Date: Tue, 8 Sep 2026 23:40:08 +0100 Subject: [PATCH 18/23] feat(playground): show each hook's status instead of a loading flag MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Follows the package: isPending, isSuccess and reset replace loading, success and resetState in every section, and the console readout prints status itself, so idle → pending → success/error is visible rather than a boolean flipping. --- apps/playground/app/auth/action/page.tsx | 15 +++++----- apps/playground/app/auth/callback/page.tsx | 6 ++-- .../components/auth/use-anonymous-sign-in.tsx | 10 +++---- .../auth/use-custom-token-sign-in.tsx | 13 +++++---- .../components/auth/use-delete-account.tsx | 10 +++---- .../auth/use-email-link-sign-in.tsx | 10 +++---- .../components/auth/use-link-provider.tsx | 13 +++++---- apps/playground/components/auth/use-login.tsx | 10 +++---- .../playground/components/auth/use-logout.tsx | 10 +++---- .../components/auth/use-oauth-sign-in.tsx | 28 +++++++++++++------ .../components/auth/use-phone-sign-in.tsx | 10 +++---- .../components/auth/use-reauthenticate.tsx | 15 ++++++---- .../auth/use-send-email-verification.tsx | 16 +++++------ .../auth/use-send-password-reset-email.tsx | 21 ++++++++------ .../playground/components/auth/use-signup.tsx | 10 +++---- .../components/auth/use-unlink-provider.tsx | 8 +++--- .../components/auth/use-update-email.tsx | 12 ++++---- .../components/auth/use-update-password.tsx | 12 ++++---- .../components/auth/use-update-profile.tsx | 12 ++++---- apps/playground/components/hook-section.tsx | 20 +++++++------ 20 files changed, 144 insertions(+), 117 deletions(-) diff --git a/apps/playground/app/auth/action/page.tsx b/apps/playground/app/auth/action/page.tsx index 7fb1b9b..0bdd07c 100644 --- a/apps/playground/app/auth/action/page.tsx +++ b/apps/playground/app/auth/action/page.tsx @@ -81,13 +81,14 @@ if (status === "pending") return ;`} } result={{ status }} error={error} - loading={status === 'pending'} + status={status} /> ); } function ConfirmReset({ oobCode }: Props) { - const { confirm, verifyCode, loading, error, success } = useConfirmPasswordReset(); + const { confirm, verifyCode, status, isPending, isSuccess, error } = + useConfirmPasswordReset(); const [password, setPassword] = useState(''); const [result, setResult] = useState(); @@ -109,7 +110,7 @@ await confirm(oobCode, newPassword);`} <> - {success ?

Password updated.

: null} + {isSuccess ?

Password updated.

: null} } result={result} error={error} - loading={loading} + status={status} /> ); } diff --git a/apps/playground/app/auth/callback/page.tsx b/apps/playground/app/auth/callback/page.tsx index f62b1b5..87814ac 100644 --- a/apps/playground/app/auth/callback/page.tsx +++ b/apps/playground/app/auth/callback/page.tsx @@ -10,7 +10,7 @@ import { PageIntro } from '@/components/page-intro'; export default function EmailLinkCallbackPage() { const { auth, config } = useFirebase(); - const { completeSignIn, loading, error } = useEmailLinkSignIn(); + const { completeSignIn, status, isPending, error } = useEmailLinkSignIn(); const [result, setResult] = useState< (Awaited> & { needsEmail?: boolean }) | undefined >(); @@ -53,7 +53,7 @@ if (!result.success && result.needsEmail) { await completeSignIn(window.location.href, email); }`} form={ - loading ? ( + isPending ? (

Completing sign-in…

) : needsEmail ? ( <> @@ -81,7 +81,7 @@ if (!result.success && result.needsEmail) { } result={result} error={error} - loading={loading} + status={status} /> ); diff --git a/apps/playground/components/auth/use-anonymous-sign-in.tsx b/apps/playground/components/auth/use-anonymous-sign-in.tsx index a8a6d09..78d51af 100644 --- a/apps/playground/components/auth/use-anonymous-sign-in.tsx +++ b/apps/playground/components/auth/use-anonymous-sign-in.tsx @@ -14,7 +14,7 @@ export function UseAnonymousSignInSection() { body: 'createSession(idToken)', throwsHint: 'The guest session is not created.', }); - const { signIn, loading, error } = useAnonymousSignIn({ + const { signIn, status, isPending, error } = useAnonymousSignIn({ formatErrorMessage: errorFormat.value, onIdToken: onIdToken.value, }); @@ -31,7 +31,7 @@ export function UseAnonymousSignInSection() { } snippet={hookSnippet({ hook: 'useAnonymousSignIn', - returns: 'signIn, loading, error', + returns: 'signIn, isPending, error', lines: [onIdToken.line, errorFormat.line], body: 'await signIn();', })} @@ -42,13 +42,13 @@ export function UseAnonymousSignInSection() { } form={ - } result={result} error={error} - loading={loading} + status={status} /> ); } diff --git a/apps/playground/components/auth/use-custom-token-sign-in.tsx b/apps/playground/components/auth/use-custom-token-sign-in.tsx index 2da3d83..9a4ce90 100644 --- a/apps/playground/components/auth/use-custom-token-sign-in.tsx +++ b/apps/playground/components/auth/use-custom-token-sign-in.tsx @@ -14,7 +14,7 @@ export function UseCustomTokenSignInSection() { body: 'createSession(idToken)', throwsHint: 'The exchange is rolled back into a failed result.', }); - const { signIn, loading, error } = useCustomTokenSignIn({ + const { signIn, status, isPending, error } = useCustomTokenSignIn({ formatErrorMessage: errorFormat.value, onIdToken: onIdToken.value, }); @@ -27,7 +27,7 @@ export function UseCustomTokenSignInSection() { why="Bridges an existing auth system into Firebase — your server mints a token with the Admin SDK, this exchanges it for a Firebase session." snippet={hookSnippet({ hook: 'useCustomTokenSignIn', - returns: 'signIn, loading, error', + returns: 'signIn, isPending, error', lines: [onIdToken.line, errorFormat.line], body: `const { token } = await fetch("/api/firebase-token").then((r) => r.json()); await signIn(token);`, @@ -45,14 +45,17 @@ await signIn(token);`, value={token} onChange={(e) => setToken(e.target.value)} /> - } result={result} error={error} - loading={loading} + status={status} /> ); } diff --git a/apps/playground/components/auth/use-delete-account.tsx b/apps/playground/components/auth/use-delete-account.tsx index 865ea9e..e4554eb 100644 --- a/apps/playground/components/auth/use-delete-account.tsx +++ b/apps/playground/components/auth/use-delete-account.tsx @@ -14,7 +14,7 @@ export function UseDeleteAccountSection() { body: 'deleteUserRecord(user.uid)', throwsHint: 'The account survives — cleanup failing cannot orphan its records.', }); - const { deleteAccount, loading, error } = useDeleteAccount({ + const { deleteAccount, status, isPending, error } = useDeleteAccount({ formatErrorMessage: errorFormat.value, onBeforeDelete: onBeforeDelete.value, }); @@ -33,7 +33,7 @@ export function UseDeleteAccountSection() { } snippet={hookSnippet({ hook: 'useDeleteAccount', - returns: 'deleteAccount, loading, error', + returns: 'deleteAccount, isPending, error', lines: [onBeforeDelete.line, errorFormat.line], body: 'await deleteAccount({ currentPassword });', })} @@ -53,20 +53,20 @@ export function UseDeleteAccountSection() { /> } result={result} error={error} - loading={loading} + status={status} /> ); } diff --git a/apps/playground/components/auth/use-email-link-sign-in.tsx b/apps/playground/components/auth/use-email-link-sign-in.tsx index 2e86ad6..868f1ad 100644 --- a/apps/playground/components/auth/use-email-link-sign-in.tsx +++ b/apps/playground/components/auth/use-email-link-sign-in.tsx @@ -28,7 +28,7 @@ export function UseEmailLinkSignInSection() { typeof window !== 'undefined' ? `${window.location.origin}/auth/callback` : 'http://localhost:3000/auth/callback'; - const { sendLink, loading, error } = useEmailLinkSignIn({ + const { sendLink, status, isPending, error } = useEmailLinkSignIn({ actionCodeSettings: { url: returnUrl, handleCodeInApp: true }, storageKey: storageKey.value, formatErrorMessage: errorFormat.value, @@ -53,7 +53,7 @@ export function UseEmailLinkSignInSection() { } snippet={hookSnippet({ hook: 'useEmailLinkSignIn', - returns: 'sendLink, loading, error', + returns: 'sendLink, isPending, error', lines: [ `actionCodeSettings: { url: "${returnUrl}", handleCodeInApp: true },`, storageKey.line, @@ -77,16 +77,16 @@ if (!result.success && result.needsEmail) showEmailField();`, <> setEmail(e.target.value)} /> } result={result} error={error} - loading={loading} + status={status} /> ); } diff --git a/apps/playground/components/auth/use-link-provider.tsx b/apps/playground/components/auth/use-link-provider.tsx index ef7a1fe..aa15cac 100644 --- a/apps/playground/components/auth/use-link-provider.tsx +++ b/apps/playground/components/auth/use-link-provider.tsx @@ -9,9 +9,10 @@ import { HookSection } from '@/components/hook-section'; export function UseLinkProviderSection() { const errorFormat = useErrorFormat(); - const { linkWithProvider, linkWithPassword, loading, error } = useLinkProvider({ - formatErrorMessage: errorFormat.value, - }); + const { linkWithProvider, linkWithPassword, status, isPending, error } = + useLinkProvider({ + formatErrorMessage: errorFormat.value, + }); const [email, setEmail] = useState(''); const [password, setPassword] = useState(''); const [result, setResult] = useState(); @@ -37,7 +38,7 @@ await linkWithPassword(email, password); // guest → password`, form={ <> } result={result} error={error} - loading={loading} + status={status} /> ); } diff --git a/apps/playground/components/auth/use-logout.tsx b/apps/playground/components/auth/use-logout.tsx index 63ce7b7..d97b087 100644 --- a/apps/playground/components/auth/use-logout.tsx +++ b/apps/playground/components/auth/use-logout.tsx @@ -13,7 +13,7 @@ export function UseLogoutSection() { body: 'clearSession()', throwsHint: 'You stay signed in — check the header, the session is still there.', }); - const { logout, loading, error } = useLogout({ + const { logout, status, isPending, error } = useLogout({ formatErrorMessage: errorFormat.value, onBeforeSignOut: onBeforeSignOut.value, }); @@ -31,7 +31,7 @@ export function UseLogoutSection() { } snippet={hookSnippet({ hook: 'useLogout', - returns: 'logout, loading, error', + returns: 'logout, isPending, error', lines: [onBeforeSignOut.line, errorFormat.line], body: 'await logout();', })} @@ -44,15 +44,15 @@ export function UseLogoutSection() { form={ } result={result} error={error} - loading={loading} + status={status} /> ); } diff --git a/apps/playground/components/auth/use-oauth-sign-in.tsx b/apps/playground/components/auth/use-oauth-sign-in.tsx index 658f3ce..1dcc78a 100644 --- a/apps/playground/components/auth/use-oauth-sign-in.tsx +++ b/apps/playground/components/auth/use-oauth-sign-in.tsx @@ -20,14 +20,26 @@ import { HookSection } from '@/components/hook-section'; * the id passed in. */ const PROVIDERS = { - google: { label: 'Google', source: 'new GoogleAuthProvider()', make: () => new GoogleAuthProvider() }, - github: { label: 'GitHub', source: 'new GithubAuthProvider()', make: () => new GithubAuthProvider() }, + google: { + label: 'Google', + source: 'new GoogleAuthProvider()', + make: () => new GoogleAuthProvider(), + }, + github: { + label: 'GitHub', + source: 'new GithubAuthProvider()', + make: () => new GithubAuthProvider(), + }, facebook: { label: 'Facebook', source: 'new FacebookAuthProvider()', make: () => new FacebookAuthProvider(), }, - twitter: { label: 'X (Twitter)', source: 'new TwitterAuthProvider()', make: () => new TwitterAuthProvider() }, + twitter: { + label: 'X (Twitter)', + source: 'new TwitterAuthProvider()', + make: () => new TwitterAuthProvider(), + }, apple: { label: 'Apple', source: 'new OAuthProvider("apple.com")', @@ -50,7 +62,7 @@ export function UseOAuthSignInSection() { body: 'createSession(idToken)', throwsHint: 'Aborts on the popup path and on the redirect path alike.', }); - const { signIn, loading, error } = useOAuthSignIn({ + const { signIn, status, isPending, error } = useOAuthSignIn({ formatErrorMessage: errorFormat.value, onIdToken: onIdToken.value, }); @@ -72,7 +84,7 @@ export function UseOAuthSignInSection() { } snippet={hookSnippet({ hook: 'useOAuthSignIn', - returns: 'signIn, loading, error', + returns: 'signIn, isPending, error', lines: [onIdToken.line, errorFormat.line], body: method === 'popup' @@ -111,15 +123,15 @@ export function UseOAuthSignInSection() { } form={ } result={result} error={error} - loading={loading} + status={status} /> ); } diff --git a/apps/playground/components/auth/use-phone-sign-in.tsx b/apps/playground/components/auth/use-phone-sign-in.tsx index 64672d6..00012f9 100644 --- a/apps/playground/components/auth/use-phone-sign-in.tsx +++ b/apps/playground/components/auth/use-phone-sign-in.tsx @@ -15,7 +15,7 @@ export function UsePhoneSignInSection() { body: 'createSession(idToken)', throwsHint: 'Aborts once the code is confirmed.', }); - const { sendCode, confirmCode, codeSent, loading, error } = usePhoneSignIn({ + const { sendCode, confirmCode, codeSent, status, isPending, error } = usePhoneSignIn({ recaptchaSize, formatErrorMessage: errorFormat.value, onIdToken: onIdToken.value, @@ -74,10 +74,10 @@ await confirmCode(smsCode);`, />
{codeSent ? ( <> @@ -87,7 +87,7 @@ await confirmCode(smsCode);`, onChange={(e) => setCode(e.target.value)} /> ;', + body: ';', })} options={ <> @@ -37,17 +37,17 @@ export function UseSendEmailVerificationSection() { } form={ <> - - {success ? ( + {isSuccess ? (

Verification email sent.

) : null} } result={result} error={error} - loading={loading} + status={status} /> ); } diff --git a/apps/playground/components/auth/use-send-password-reset-email.tsx b/apps/playground/components/auth/use-send-password-reset-email.tsx index cda66c4..befcd0b 100644 --- a/apps/playground/components/auth/use-send-password-reset-email.tsx +++ b/apps/playground/components/auth/use-send-password-reset-email.tsx @@ -13,7 +13,7 @@ import { HookSection } from '@/components/hook-section'; export function UseSendPasswordResetEmailSection() { const errorFormat = useErrorFormat(); const actionCodeSettings = useActionCodeSettings(); - const { send, loading, error, success, resetState } = useSendPasswordResetEmail({ + const { send, status, isPending, isSuccess, error, reset } = useSendPasswordResetEmail({ actionCodeSettings: actionCodeSettings.value, formatErrorMessage: errorFormat.value, }); @@ -25,14 +25,14 @@ export function UseSendPasswordResetEmailSection() { hook="useSendPasswordResetEmail" why={ <> - success and resetState exist for the form that has to + isSuccess and reset() exist for the form that has to say “check your inbox”, then let someone try a different address without a stale success message sitting underneath. } snippet={hookSnippet({ hook: 'useSendPasswordResetEmail', - returns: 'send, success, resetState', + returns: 'send, isSuccess, reset', lines: [actionCodeSettings.line, errorFormat.line], body: `await send(email); // success === true → "if an account exists, a link is on its way"`, @@ -47,20 +47,23 @@ export function UseSendPasswordResetEmailSection() { <> setEmail(e.target.value)} />
-
- {success ? ( + {isSuccess ? (

If an account exists for {email}, a reset link is on its way.

@@ -69,7 +72,7 @@ export function UseSendPasswordResetEmailSection() { } result={result} error={error} - loading={loading} + status={status} /> ); } diff --git a/apps/playground/components/auth/use-signup.tsx b/apps/playground/components/auth/use-signup.tsx index 8410481..18370a7 100644 --- a/apps/playground/components/auth/use-signup.tsx +++ b/apps/playground/components/auth/use-signup.tsx @@ -24,7 +24,7 @@ export function UseSignupSection() { body: 'createSession(idToken)', throwsHint: 'Signup aborts after the account exists — try signing in with it.', }); - const { signup, loading, error } = useSignup({ + const { signup, status, isPending, error } = useSignup({ sendVerificationEmail: sendVerificationEmail.value, formatErrorMessage: errorFormat.value, onIdToken: onIdToken.value, @@ -46,7 +46,7 @@ export function UseSignupSection() { } snippet={hookSnippet({ hook: 'useSignup', - returns: 'signup, loading, error', + returns: 'signup, isPending, error', lines: [sendVerificationEmail.line, onIdToken.line, errorFormat.line], body: 'await signup(email, password, { displayName });', })} @@ -72,20 +72,20 @@ export function UseSignupSection() { onChange={(e) => setDisplayName(e.target.value)} /> } result={result} error={error} - loading={loading} + status={status} /> ); } diff --git a/apps/playground/components/auth/use-unlink-provider.tsx b/apps/playground/components/auth/use-unlink-provider.tsx index 7af69a8..7c31f0b 100644 --- a/apps/playground/components/auth/use-unlink-provider.tsx +++ b/apps/playground/components/auth/use-unlink-provider.tsx @@ -8,7 +8,7 @@ import { HookSection } from '@/components/hook-section'; export function UseUnlinkProviderSection() { const errorFormat = useErrorFormat(); - const { unlinkProvider, loading, error } = useUnlinkProvider({ + const { unlinkProvider, status, isPending, error } = useUnlinkProvider({ formatErrorMessage: errorFormat.value, }); const [providerId, setProviderId] = useState('google.com'); @@ -20,7 +20,7 @@ export function UseUnlinkProviderSection() { why="Firebase refuses to unlink the last remaining method, so an account can't be locked out this way — the refusal arrives as an ordinary failure result you can show." snippet={hookSnippet({ hook: 'useUnlinkProvider', - returns: 'unlinkProvider, loading, error', + returns: 'unlinkProvider, isPending, error', lines: [errorFormat.line], body: 'await unlinkProvider("google.com");', })} @@ -34,7 +34,7 @@ export function UseUnlinkProviderSection() { /> - {success ? ( + {isSuccess ? (

Check {newEmail} to confirm the change.

@@ -77,7 +77,7 @@ export function UseUpdateEmailSection() { } result={result} error={error} - loading={loading} + status={status} /> ); } diff --git a/apps/playground/components/auth/use-update-password.tsx b/apps/playground/components/auth/use-update-password.tsx index 71b9c7e..f829d42 100644 --- a/apps/playground/components/auth/use-update-password.tsx +++ b/apps/playground/components/auth/use-update-password.tsx @@ -8,7 +8,7 @@ import { HookSection } from '@/components/hook-section'; export function UseUpdatePasswordSection() { const errorFormat = useErrorFormat(); - const { update, loading, error, success } = useUpdatePassword({ + const { update, status, isPending, isSuccess, error } = useUpdatePassword({ formatErrorMessage: errorFormat.value, }); const [newPassword, setNewPassword] = useState(''); @@ -29,7 +29,7 @@ export function UseUpdatePasswordSection() { } snippet={hookSnippet({ hook: 'useUpdatePassword', - returns: 'update, loading, error, success', + returns: 'update, isPending, isSuccess, error', lines: [errorFormat.line], body: `await update(newPassword, { currentPassword }); // reauthenticates first await update(newPassword); // your own policy`, @@ -50,7 +50,7 @@ await update(newPassword); // your own policy`, onChange={(e) => setCurrentPassword(e.target.value)} /> - {success ?

Password updated.

: null} + {isSuccess ?

Password updated.

: null} } result={result} error={error} - loading={loading} + status={status} /> ); } diff --git a/apps/playground/components/auth/use-update-profile.tsx b/apps/playground/components/auth/use-update-profile.tsx index b33962a..2412aea 100644 --- a/apps/playground/components/auth/use-update-profile.tsx +++ b/apps/playground/components/auth/use-update-profile.tsx @@ -8,7 +8,7 @@ import { HookSection } from '@/components/hook-section'; export function UseUpdateProfileSection() { const errorFormat = useErrorFormat(); - const { update, loading, error, success } = useUpdateProfile({ + const { update, status, isPending, isSuccess, error } = useUpdateProfile({ formatErrorMessage: errorFormat.value, }); const [displayName, setDisplayName] = useState(''); @@ -21,7 +21,7 @@ export function UseUpdateProfileSection() { why="Firebase treats profile fields as non-sensitive, so this is the one account operation that needs no reauthentication." snippet={hookSnippet({ hook: 'useUpdateProfile', - returns: 'update, loading, error, success', + returns: 'update, isPending, isSuccess, error', lines: [errorFormat.line], body: 'await update({ displayName, photoURL });', })} @@ -39,7 +39,7 @@ export function UseUpdateProfileSection() { onChange={(e) => setPhotoURL(e.target.value)} /> - {success ?

Profile updated.

: null} + {isSuccess ?

Profile updated.

: null} } result={result} error={error} - loading={loading} + status={status} /> ); } diff --git a/apps/playground/components/hook-section.tsx b/apps/playground/components/hook-section.tsx index 2784ec6..9318dac 100644 --- a/apps/playground/components/hook-section.tsx +++ b/apps/playground/components/hook-section.tsx @@ -20,7 +20,7 @@ export function HookSection({ form, result, error, - loading, + status, }: { hook: string; why: ReactNode; @@ -31,8 +31,8 @@ export function HookSection({ form: ReactNode; result?: unknown; error?: string | null; - /** The hook's own flag, shown live — it's a third of what every hook returns. */ - loading?: boolean; + /** The hook's own status, shown live in the console. */ + status?: string; }) { const failure = result && typeof result === 'object' && 'success' in result && !result.success @@ -93,17 +93,19 @@ export function HookSection({

{form}
- {/* The hook's own state, live. Watching `loading` flip is the clearest + {/* The hook's own state, live. Watching `status` move is the clearest demonstration that the hook owns this rather than you. */}
-
loading
-
- {String(Boolean(loading))} +
status
+
+ {status ?? 'idle'}
{/* Its own row, wrapping: a Firebase message is long enough that - sharing a line with `loading` truncated it to nothing useful. */} + sharing a line with `status` truncated it to nothing useful. */}
error
@@ -116,7 +118,7 @@ export function HookSection({ Response

- {loading ? ( + {status === 'pending' ? (

Running…

) : result === undefined && !error ? (

Run it to see the result.

From 14c4f6ad5478972e625181d21594725c2372a010 Mon Sep 17 00:00:00 2001 From: timonwa Date: Tue, 8 Sep 2026 23:49:01 +0100 Subject: [PATCH 19/23] docs: describe the status contract in place of loading/success Every Returns section, the quickstarts, and the contract page now name status, isPending/isSuccess/isError and reset(); the core page documents ActionStatus beside AsyncStatus and why there are two, mirroring TanStack's query/mutation split. --- .changeset/status-instead-of-loading.md | 19 +++++++++++++++++++ README.md | 6 +++--- .../docs/auth/use-anonymous-sign-in.mdx | 2 +- .../docs/auth/use-confirm-password-reset.mdx | 4 ++-- .../docs/auth/use-custom-token-sign-in.mdx | 2 +- .../content/docs/auth/use-delete-account.mdx | 4 ++-- .../docs/auth/use-email-link-sign-in.mdx | 2 +- .../content/docs/auth/use-link-provider.mdx | 2 +- apps/docs/content/docs/auth/use-login.mdx | 4 ++-- apps/docs/content/docs/auth/use-logout.mdx | 6 +++--- .../content/docs/auth/use-oauth-sign-in.mdx | 2 +- .../content/docs/auth/use-phone-sign-in.mdx | 2 +- .../content/docs/auth/use-reauthenticate.mdx | 2 +- .../docs/auth/use-send-email-verification.mdx | 6 +++--- .../auth/use-send-password-reset-email.mdx | 10 +++++----- apps/docs/content/docs/auth/use-signup.mdx | 4 ++-- .../content/docs/auth/use-unlink-provider.mdx | 2 +- .../content/docs/auth/use-update-email.mdx | 10 +++++----- .../content/docs/auth/use-update-password.mdx | 4 ++-- .../content/docs/auth/use-update-profile.mdx | 4 ++-- apps/docs/content/docs/core/hook-result.mdx | 8 ++++++++ apps/docs/content/docs/getting-started.mdx | 2 +- .../content/docs/guides/email-action-page.mdx | 2 +- apps/docs/content/docs/how-hooks-work.mdx | 6 +++--- apps/docs/content/docs/index.mdx | 2 +- 25 files changed, 72 insertions(+), 45 deletions(-) create mode 100644 .changeset/status-instead-of-loading.md diff --git a/.changeset/status-instead-of-loading.md b/.changeset/status-instead-of-loading.md new file mode 100644 index 0000000..e870780 --- /dev/null +++ b/.changeset/status-instead-of-loading.md @@ -0,0 +1,19 @@ +--- +"@timonwa/firebase-hooks": minor +--- + +**Breaking: every action hook reports `status` with derived booleans, replacing `loading`, `success` and `resetState`.** + +```tsx +const { login, status, isIdle, isPending, isSuccess, isError, error, reset } = useLogin(); +// ^ 'idle' | 'pending' | 'success' | 'error' +``` + +| Before | After | +| ------------------------------------------ | ------------------------------- | +| `loading` | `isPending` | +| `success` (six hooks, hand-rolled) | `isSuccess` — now on every hook | +| `resetState()` (two hooks) | `reset()` — now on every hook | +| `useEmailLinkSignIn`'s returned `setError` | removed; `reset()` covers it | + +The booleans are derived from `status`, so exactly one is ever true. This is TanStack Query's mutation result, field for field, and the new `ActionStatus` type ships from the root entry beside `AsyncStatus`. Hooks that act on mount — `useVerifyEmail` — keep `AsyncStatus` (no `idle`, since the work starts before you can render) and gain the same `isPending`/`isSuccess`/`isError`. diff --git a/README.md b/README.md index 9c9aff5..a28b410 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ --- -Each hook runs one flow end to end. It holds its own loading, error, and success state, and sets up the browser pieces and callbacks the flow needs. Every action returns a result you can branch on: `{ success: true, … }` when it works, `{ success: false, error, code, cause }` when it doesn't. Firebase's own response is on both paths. +Each hook runs one flow end to end. It holds its own status and error, and sets up the browser pieces and callbacks the flow needs. Every action returns a result you can branch on: `{ success: true, … }` when it works, `{ success: false, error, code, cause }` when it doesn't. Firebase's own response is on both paths. Zero dependencies — `firebase` and `react` are peers. @@ -48,7 +48,7 @@ Hooks below it need nothing passed: import { useLogin } from "@timonwa/firebase-hooks/auth"; function LoginForm() { - const { login, loading, error } = useLogin(); + const { login, isPending, error } = useLogin(); async function onSubmit(email: string, password: string) { const result = await login(email, password); @@ -89,7 +89,7 @@ Every hook has its own page — signature, options, and a worked example — in One contract, so learning one hook is learning them all: - **Every hook needs an `Auth` instance** — taken from `AuthProvider`, or passed in as the first argument to override it. No global, no hidden singleton. Passing `null` means "not ready yet", never "use the provider's", so an action called before `auth` exists fails cleanly with `{ success: false, error }`. -- **Every action resolves to `HookResult`** — `{ success: true, ...data }` or `{ success: false, error, code, cause }`. The hook's `error` state carries the same message for rendering, and `loading` and `success` track the action. See [Error handling](#error-handling). +- **Every action resolves to `HookResult`** — `{ success: true, ...data }` or `{ success: false, error, code, cause }`. The hook's `status` — `idle`, `pending`, `success`, `error` — tracks the action, with `isPending`/`isSuccess`/`isError` derived from it and `reset()` to go back to idle; `error` carries the message for rendering. See [Error handling](#error-handling). - **`onIdToken(idToken, user)` runs after a successful sign-in**, with a freshly minted token. Throw inside it to abort the flow; the error surfaces like any other. - **`currentPassword` triggers reauthentication** in `useUpdatePassword`, `useUpdateEmail`, and `useDeleteAccount`. `useReauthenticate` exposes the same step for custom flows. - **Provider options are defaults, hook options win.** `onIdToken`, `onBeforeSignOut`, `actionCodeSettings`, `formatErrorMessage`, and the `onError` observer can all be set once on `AuthProvider`; a hook's own option overrides the provider, and an explicit `null` opts that flow out entirely: diff --git a/apps/docs/content/docs/auth/use-anonymous-sign-in.mdx b/apps/docs/content/docs/auth/use-anonymous-sign-in.mdx index 945eeb0..66b0514 100644 --- a/apps/docs/content/docs/auth/use-anonymous-sign-in.mdx +++ b/apps/docs/content/docs/auth/use-anonymous-sign-in.mdx @@ -11,7 +11,7 @@ const { signIn } = useAnonymousSignIn(); ## Returns -`signIn()` resolves to `{ success: true, user, credential }` or a failure result. Also exposes `loading` and `error`. +`signIn()` resolves to `{ success: true, user, credential }` or a failure result. Also exposes `status`, `isPending`, `error`, and `reset`. ## Upgrading a guest diff --git a/apps/docs/content/docs/auth/use-confirm-password-reset.mdx b/apps/docs/content/docs/auth/use-confirm-password-reset.mdx index c48a4e5..241438c 100644 --- a/apps/docs/content/docs/auth/use-confirm-password-reset.mdx +++ b/apps/docs/content/docs/auth/use-confirm-password-reset.mdx @@ -6,7 +6,7 @@ description: Complete a reset from the emailed oobCode. Runs on the page your reset email links to, using the `oobCode` from the URL. ```tsx -const { confirm, verifyCode, success, error } = useConfirmPasswordReset(); +const { confirm, verifyCode, isSuccess, error } = useConfirmPasswordReset(); const check = await verifyCode(oobCode); // { success: true, email: "a@b.c" } await confirm(oobCode, newPassword); @@ -18,6 +18,6 @@ await confirm(oobCode, newPassword); `verifyCode(oobCode)` optionally checks the code first and resolves to `{ success: true, email }`, so the page can show whose password is being reset before asking for a new one. -Also exposes `loading`, `error`, `success`, and `resetState`. +Also exposes `status`, `isPending`, `isSuccess`, `error`, and `reset`. All three emailed links land on one URL — see [One page for every emailed link](/docs/guides/email-action-page) for the page that dispatches on `mode`. diff --git a/apps/docs/content/docs/auth/use-custom-token-sign-in.mdx b/apps/docs/content/docs/auth/use-custom-token-sign-in.mdx index f3ebe04..1ce24b6 100644 --- a/apps/docs/content/docs/auth/use-custom-token-sign-in.mdx +++ b/apps/docs/content/docs/auth/use-custom-token-sign-in.mdx @@ -14,7 +14,7 @@ await signIn(token); ## Returns -`signIn(customToken)` resolves to `{ success: true, user, credential }` or a failure result. Also exposes `loading` and `error`. +`signIn(customToken)` resolves to `{ success: true, user, credential }` or a failure result. Also exposes `status`, `isPending`, `error`, and `reset`. ## Options diff --git a/apps/docs/content/docs/auth/use-delete-account.mdx b/apps/docs/content/docs/auth/use-delete-account.mdx index 7da2f5d..414ac65 100644 --- a/apps/docs/content/docs/auth/use-delete-account.mdx +++ b/apps/docs/content/docs/auth/use-delete-account.mdx @@ -4,7 +4,7 @@ description: Delete the account, with reauth and server cleanup. --- ```tsx -const { deleteAccount, loading, error } = useDeleteAccount({ +const { deleteAccount, isPending, error } = useDeleteAccount({ onBeforeDelete: (user) => deleteUserRecord(user.uid), }); @@ -14,7 +14,7 @@ if (result.success) router.push('/goodbye'); ## Returns -`deleteAccount({ currentPassword? })` resolves to `{ success: true }` or a failure result. Also exposes `loading` and `error`. +`deleteAccount({ currentPassword? })` resolves to `{ success: true }` or a failure result. Also exposes `status`, `isPending`, `error`, and `reset`. ## Cleaning up first diff --git a/apps/docs/content/docs/auth/use-email-link-sign-in.mdx b/apps/docs/content/docs/auth/use-email-link-sign-in.mdx index fc0436e..b527997 100644 --- a/apps/docs/content/docs/auth/use-email-link-sign-in.mdx +++ b/apps/docs/content/docs/auth/use-email-link-sign-in.mdx @@ -26,7 +26,7 @@ Set the `sendEmail` option to send it from your own API instead — the same opt `completeSignIn(url, email?)` resolves to `{ success: true, user, credential }` or a failure result, exported as `CompleteSignInResult` — a failure there also carries `needsEmail`. -Also exposes `loading` and `error`. +Also exposes `status`, `isPending`, `error`, and `reset`. ## When the link opens on another device diff --git a/apps/docs/content/docs/auth/use-link-provider.mdx b/apps/docs/content/docs/auth/use-link-provider.mdx index e5693ea..1069717 100644 --- a/apps/docs/content/docs/auth/use-link-provider.mdx +++ b/apps/docs/content/docs/auth/use-link-provider.mdx @@ -14,7 +14,7 @@ await linkWithPassword(email, password); // guest -> email/password account `linkWithProvider(provider)` links an OAuth provider via popup. `linkWithPassword(email, password)` links an email/password credential. -Both resolve to `{ success: true, user, credential }` or a failure result. Also exposes `loading` and `error`. +Both resolve to `{ success: true, user, credential }` or a failure result. Also exposes `status`, `isPending`, `error`, and `reset`. ## Upgrading a guest diff --git a/apps/docs/content/docs/auth/use-login.mdx b/apps/docs/content/docs/auth/use-login.mdx index 27d312a..ef7628a 100644 --- a/apps/docs/content/docs/auth/use-login.mdx +++ b/apps/docs/content/docs/auth/use-login.mdx @@ -12,7 +12,7 @@ import { useLogin } from '@timonwa/firebase-hooks/auth'; import { auth } from '@/lib/firebase'; function LoginForm() { - const { login, loading, error } = useLogin({ + const { login, isPending, error } = useLogin({ onIdToken: (idToken) => createSession(idToken), }); @@ -34,7 +34,7 @@ function LoginForm() { `login(email, password)` resolves to a [`HookResult`](/docs/core/hook-result) — `{ success: true, user, credential }` on success, or `{ success: false, error, code, cause }` on failure. It never throws. -The hook also exposes `loading` and `error` for rendering. +The hook also exposes `status`, `isPending`, `isSuccess`, `isError`, `error`, and `reset` for rendering. ## Notes diff --git a/apps/docs/content/docs/auth/use-logout.mdx b/apps/docs/content/docs/auth/use-logout.mdx index 2ac410a..8290990 100644 --- a/apps/docs/content/docs/auth/use-logout.mdx +++ b/apps/docs/content/docs/auth/use-logout.mdx @@ -6,16 +6,16 @@ description: Sign-out, with your server session cleared first. `onBeforeSignOut` runs **before** Firebase clears anything. If it throws, the Firebase session is left intact and the user can retry. ```tsx -const { logout, loading } = useLogout({ onBeforeSignOut: clearSession }); +const { logout, isPending } = useLogout({ onBeforeSignOut: clearSession }); -; ``` ## Returns -`logout()` resolves to `{ success: true }` or a failure result. Also exposes `loading` and `error`. +`logout()` resolves to `{ success: true }` or a failure result. Also exposes `status`, `isPending`, `error`, and `reset`. ## Options diff --git a/apps/docs/content/docs/auth/use-oauth-sign-in.mdx b/apps/docs/content/docs/auth/use-oauth-sign-in.mdx index f1ecb31..b3f18c8 100644 --- a/apps/docs/content/docs/auth/use-oauth-sign-in.mdx +++ b/apps/docs/content/docs/auth/use-oauth-sign-in.mdx @@ -8,7 +8,7 @@ Works with any Firebase provider — Google, Apple, GitHub, Facebook, Microsoft, ```tsx import { GoogleAuthProvider } from 'firebase/auth'; -const { signIn, loading, error } = useOAuthSignIn({ onIdToken: createSession }); +const { signIn, isPending, error } = useOAuthSignIn({ onIdToken: createSession }); ; ``` diff --git a/apps/docs/content/docs/auth/use-phone-sign-in.mdx b/apps/docs/content/docs/auth/use-phone-sign-in.mdx index 115145a..bdf5209 100644 --- a/apps/docs/content/docs/auth/use-phone-sign-in.mdx +++ b/apps/docs/content/docs/auth/use-phone-sign-in.mdx @@ -23,7 +23,7 @@ const result = await confirmCode(smsCode); `confirmCode(code)` resolves to `{ success: true, user, credential }` or a failure result. -`codeSent` flips true after a successful send. Also exposes `loading` and `error`. +`codeSent` flips true after a successful send. Also exposes `status`, `isPending`, `error`, and `reset`. ## The reCAPTCHA verifier diff --git a/apps/docs/content/docs/auth/use-reauthenticate.mdx b/apps/docs/content/docs/auth/use-reauthenticate.mdx index 59e7c9e..9fb7542 100644 --- a/apps/docs/content/docs/auth/use-reauthenticate.mdx +++ b/apps/docs/content/docs/auth/use-reauthenticate.mdx @@ -14,7 +14,7 @@ if (check.success) await performSensitiveOperation(); ## Returns -`reauthenticateWithPassword(currentPassword)` and `reauthenticateWithProvider(provider)` each resolve to `{ success: true }` or a failure result. Also exposes `loading` and `error`. +`reauthenticateWithPassword(currentPassword)` and `reauthenticateWithProvider(provider)` each resolve to `{ success: true }` or a failure result. Also exposes `status`, `isPending`, `error`, and `reset`. ## When you need it diff --git a/apps/docs/content/docs/auth/use-send-email-verification.mdx b/apps/docs/content/docs/auth/use-send-email-verification.mdx index 104d6f8..e049fcf 100644 --- a/apps/docs/content/docs/auth/use-send-email-verification.mdx +++ b/apps/docs/content/docs/auth/use-send-email-verification.mdx @@ -6,16 +6,16 @@ description: The resend-verification-email button. Sends, or re-sends, the verification email to the signed-in user. ```tsx -const { send, loading, success } = useSendEmailVerification(); +const { send, isPending, isSuccess } = useSendEmailVerification(); -; ``` ## Returns -`send()` resolves to `{ success: true }` or a failure result. Also exposes `loading`, `error`, and `success`. +`send()` resolves to `{ success: true }` or a failure result. Also exposes `status`, `isPending`, `isSuccess`, `error`, and `reset`. ## Notes diff --git a/apps/docs/content/docs/auth/use-send-password-reset-email.mdx b/apps/docs/content/docs/auth/use-send-password-reset-email.mdx index 76140cd..2fb0a73 100644 --- a/apps/docs/content/docs/auth/use-send-password-reset-email.mdx +++ b/apps/docs/content/docs/auth/use-send-password-reset-email.mdx @@ -4,12 +4,12 @@ description: The forgot-password email. --- ```tsx -const { send, loading, success } = useSendPasswordResetEmail(); +const { send, isPending, isSuccess } = useSendPasswordResetEmail(); await send(email); { - success &&

If an account exists for {email}, a reset link is on its way.

; + isSuccess &&

If an account exists for {email}, a reset link is on its way.

; } ``` @@ -17,7 +17,7 @@ await send(email); `send(email)` resolves to `{ success: true }` or a failure result. -`success` flips true after a send. `resetState` clears both flags — useful for a retry-with-a-different-address form. Also exposes `loading` and `error`. +`isSuccess` flips true after a send. `reset()` returns to idle and clears the error — useful for a retry-with-a-different-address form. Also exposes `status`, `isPending`, and `error`. ## Notes @@ -28,13 +28,13 @@ Phrase the confirmation so it doesn't reveal whether an account exists, as above By default the link is sent from the browser, which puts an email-sending path outside your own rate limiter. `sendEmail` replaces the send, so the request goes through your API instead: ```tsx -const { send, success } = useSendPasswordResetEmail({ +const { send, isSuccess } = useSendPasswordResetEmail({ sendEmail: ({ email, actionCodeSettings }) => requestPasswordReset(email, actionCodeSettings), }); ``` -The hook keeps everything else: `loading`, `error`, `success` and `resetState` behave identically, and a throwing sender surfaces as an ordinary failure result. +The hook keeps everything else: `status`, `isPending`, `isSuccess`, `error` and `reset` behave identically, and a throwing sender surfaces as an ordinary failure result. The sender receives `{ email, actionCodeSettings }` — the same two things Firebase's own `sendPasswordResetEmail` takes, and the same two the Admin SDK's `generatePasswordResetLink(email, actionCodeSettings)` wants on your server. So the `actionCodeSettings` you set once on the provider still decides where the link lands, even when your API sends it. diff --git a/apps/docs/content/docs/auth/use-signup.mdx b/apps/docs/content/docs/auth/use-signup.mdx index a2156f0..bc0442d 100644 --- a/apps/docs/content/docs/auth/use-signup.mdx +++ b/apps/docs/content/docs/auth/use-signup.mdx @@ -6,7 +6,7 @@ description: Email/password signup with optional profile and a verification emai The standard client-side signup: create the account, set the optional profile, send the verification email (on by default), then run `onIdToken`. ```tsx -const { signup, loading, error } = useSignup({ onIdToken: createSession }); +const { signup, isPending, error } = useSignup({ onIdToken: createSession }); const result = await signup(email, password, { displayName: fullName }); if (result.success) router.push('/verify-email'); @@ -14,7 +14,7 @@ if (result.success) router.push('/verify-email'); ## Returns -`signup(email, password, profile?)` resolves to `{ success: true, user, credential }` or a failure result. Also exposes `loading` and `error`. +`signup(email, password, profile?)` resolves to `{ success: true, user, credential }` or a failure result. Also exposes `status`, `isPending`, `error`, and `reset`. `profile` takes `displayName` and `photoURL`, both optional. diff --git a/apps/docs/content/docs/auth/use-unlink-provider.mdx b/apps/docs/content/docs/auth/use-unlink-provider.mdx index 278ccf2..cf64b1f 100644 --- a/apps/docs/content/docs/auth/use-unlink-provider.mdx +++ b/apps/docs/content/docs/auth/use-unlink-provider.mdx @@ -11,7 +11,7 @@ await unlinkProvider('google.com'); ## Returns -`unlinkProvider(providerId)` resolves to `{ success: true, user, credential }` or a failure result. Also exposes `loading` and `error`. +`unlinkProvider(providerId)` resolves to `{ success: true, user, credential }` or a failure result. Also exposes `status`, `isPending`, `error`, and `reset`. Provider ids are Firebase's own: `"google.com"`, `"password"`, `"github.com"`, and so on. diff --git a/apps/docs/content/docs/auth/use-update-email.mdx b/apps/docs/content/docs/auth/use-update-email.mdx index 1827950..7c99a3c 100644 --- a/apps/docs/content/docs/auth/use-update-email.mdx +++ b/apps/docs/content/docs/auth/use-update-email.mdx @@ -4,25 +4,25 @@ description: Change the email via verify-before-update. --- ```tsx -const { update, success } = useUpdateEmail(); +const { update, isSuccess } = useUpdateEmail(); await update(newEmail, { currentPassword }); // password account await update(newEmail); // OAuth account { - success &&

Check {newEmail} to confirm the change.

; + isSuccess &&

Check {newEmail} to confirm the change.

; } ``` ## Returns -`update(newEmail, { currentPassword? })` resolves to `{ success: true }` or a failure result. Also exposes `loading`, `error`, and `success`. +`update(newEmail, { currentPassword? })` resolves to `{ success: true }` or a failure result. Also exposes `status`, `isPending`, `isSuccess`, `error`, and `reset`. -## `success` means "email sent" +## `isSuccess` means "email sent" This uses Firebase's `verifyBeforeUpdateEmail`. Firebase mails a verification link to the **new** address, and the change lands only when it's clicked. -So `success` means the verification email went out — not that the email changed. Word your UI accordingly. +So `isSuccess` means the verification email went out — not that the email changed. Word your UI accordingly. ## Reauthentication diff --git a/apps/docs/content/docs/auth/use-update-password.mdx b/apps/docs/content/docs/auth/use-update-password.mdx index 3a8f8b9..a6495cc 100644 --- a/apps/docs/content/docs/auth/use-update-password.mdx +++ b/apps/docs/content/docs/auth/use-update-password.mdx @@ -4,7 +4,7 @@ description: Change the password, with reauthentication built in. --- ```tsx -const { update, loading, error, success } = useUpdatePassword(); +const { update, isPending, isSuccess, error } = useUpdatePassword(); await update(newPassword, { currentPassword }); // reauthenticates first await update(newPassword); // no reauth — your call @@ -12,7 +12,7 @@ await update(newPassword); // no reauth — your call ## Returns -`update(newPassword, { currentPassword? })` resolves to `{ success: true }` or a failure result. Also exposes `loading`, `error`, and `success`. +`update(newPassword, { currentPassword? })` resolves to `{ success: true }` or a failure result. Also exposes `status`, `isPending`, `isSuccess`, `error`, and `reset`. ## Reauthentication diff --git a/apps/docs/content/docs/auth/use-update-profile.mdx b/apps/docs/content/docs/auth/use-update-profile.mdx index af2ad7c..879965d 100644 --- a/apps/docs/content/docs/auth/use-update-profile.mdx +++ b/apps/docs/content/docs/auth/use-update-profile.mdx @@ -4,14 +4,14 @@ description: Display name and photo URL. --- ```tsx -const { update, loading } = useUpdateProfile(); +const { update, isPending } = useUpdateProfile(); await update({ displayName: fullName, photoURL: avatarUrl }); ``` ## Returns -`update({ displayName?, photoURL? })` resolves to `{ success: true }` or a failure result. Both fields accept `null` to clear them. Also exposes `loading`, `error`, and `success`. +`update({ displayName?, photoURL? })` resolves to `{ success: true }` or a failure result. Both fields accept `null` to clear them. Also exposes `status`, `isPending`, `isSuccess`, `error`, and `reset`. ## Notes diff --git a/apps/docs/content/docs/core/hook-result.mdx b/apps/docs/content/docs/core/hook-result.mdx index 22735df..649aad6 100644 --- a/apps/docs/content/docs/core/hook-result.mdx +++ b/apps/docs/content/docs/core/hook-result.mdx @@ -62,6 +62,14 @@ type AsyncStatus = 'pending' | 'error' | 'success'; There is no idle state — the work starts before you can render, so `pending` is where it begins. [`useVerifyEmail`](/docs/auth/use-verify-email) reports it as `VerifyEmailStatus`, an alias of this. +Actions you call get one more state, because nothing runs until you call them: + +```ts +type ActionStatus = 'idle' | AsyncStatus; +``` + +Every action hook returns `status: ActionStatus` with `isIdle`, `isPending`, `isSuccess` and `isError` derived from it, plus `reset()`. Two vocabularies rather than one is deliberate — the same split TanStack Query makes between a query's status and a mutation's. + Render the three branches and you've handled every case: ```tsx diff --git a/apps/docs/content/docs/getting-started.mdx b/apps/docs/content/docs/getting-started.mdx index b424a9b..518ad61 100644 --- a/apps/docs/content/docs/getting-started.mdx +++ b/apps/docs/content/docs/getting-started.mdx @@ -81,7 +81,7 @@ Nothing to pass: the provider supplies both the instance and the `onIdToken` you import { useLogin } from '@timonwa/firebase-hooks/auth'; function LoginForm() { - const { login, loading, error } = useLogin(); + const { login, isPending, error } = useLogin(); async function onSubmit(email: string, password: string) { const result = await login(email, password); diff --git a/apps/docs/content/docs/guides/email-action-page.mdx b/apps/docs/content/docs/guides/email-action-page.mdx index f21ce05..3e69483 100644 --- a/apps/docs/content/docs/guides/email-action-page.mdx +++ b/apps/docs/content/docs/guides/email-action-page.mdx @@ -70,7 +70,7 @@ function VerifyEmail({ oobCode }: { oobCode: string | null }) { } function ResetPassword({ oobCode }: { oobCode: string | null }) { - const { verifyCode, confirm, loading, error } = useConfirmPasswordReset(); + const { verifyCode, confirm, isPending, error } = useConfirmPasswordReset(); // verifyCode(oobCode) first — it returns the account email, so the form can // say whose password is being reset before asking for a new one. } diff --git a/apps/docs/content/docs/how-hooks-work.mdx b/apps/docs/content/docs/how-hooks-work.mdx index 9f4de93..eeaf601 100644 --- a/apps/docs/content/docs/how-hooks-work.mdx +++ b/apps/docs/content/docs/how-hooks-work.mdx @@ -39,7 +39,7 @@ Actions never throw. Each resolves to a `HookResult`: { success: false, error, code, cause } ``` -The hook's own `error` state carries the same message for rendering, and `loading` tracks the action. Some hooks also expose `success` and `resetState`. +Alongside the result, every action hook reports where it is: `status` is `"idle" | "pending" | "success" | "error"`, with `isIdle`, `isPending`, `isSuccess` and `isError` derived from it, `error` carrying the message for rendering, and `reset()` returning to idle — for a form the user retries, so a stale message doesn't sit under the new attempt. It's the same shape as TanStack Query's mutation result. Hooks that act on mount, like `useVerifyEmail`, report [`AsyncStatus`](/docs/core/hook-result#asyncstatus) instead: the same words without `idle`. ```tsx const result = await login(email, password); @@ -61,9 +61,9 @@ import { } from '@timonwa/firebase-hooks/auth'; export function useAppLogin(options?: UseLoginOptions): UseLoginResult { - const { login, loading, error } = useLogin(options); + const { login, isPending, error } = useLogin(options); // …your own state on top - return { login, loading, error }; + return { login, isPending, error }; } ``` diff --git a/apps/docs/content/docs/index.mdx b/apps/docs/content/docs/index.mdx index 99e777b..764c673 100644 --- a/apps/docs/content/docs/index.mdx +++ b/apps/docs/content/docs/index.mdx @@ -3,7 +3,7 @@ title: Introduction description: Typed React hooks for Firebase — one hook per flow, with its state, errors, and callbacks handled. --- -Each hook runs one flow end to end. It holds its own loading, error, and success state, and sets up the browser pieces and callbacks the flow needs. +Each hook runs one flow end to end. It holds its own status and error, and sets up the browser pieces and callbacks the flow needs. Every action returns a result you can branch on — `{ success: true, … }` when it works, `{ success: false, error, code, cause }` when it doesn't. Firebase's own response is on both paths, unmodified. From 7ed1070ca01b4f1abb793edeea5d99a139bac074 Mon Sep 17 00:00:00 2001 From: timonwa Date: Wed, 9 Sep 2026 00:29:10 +0100 Subject: [PATCH 20/23] build: one Prettier config for the repo MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Root .prettierignore listed apps, so lint-staged's Prettier skipped every app file — the pre-commit format was a no-op. Both apps also ran the Tailwind plugin without tailwindStylesheet, so custom utilities sorted as unknowns, at two versions. --- .prettierignore | 13 +- .prettierrc.json | 16 +- apps/docs/.prettierignore | 4 - apps/docs/.prettierrc.json | 16 - apps/docs/package.json | 1 - apps/playground/.prettierignore | 4 - apps/playground/.prettierrc.json | 16 - apps/playground/package.json | 1 - package.json | 7 +- pnpm-lock.yaml | 4189 ++++++++++++++++++++++-------- 10 files changed, 3136 insertions(+), 1131 deletions(-) delete mode 100644 apps/docs/.prettierignore delete mode 100644 apps/docs/.prettierrc.json delete mode 100644 apps/playground/.prettierignore delete mode 100644 apps/playground/.prettierrc.json diff --git a/.prettierignore b/.prettierignore index 8922ae3..4313575 100644 --- a/.prettierignore +++ b/.prettierignore @@ -1,6 +1,17 @@ node_modules dist -apps + +# Biome's territory — two formatters on one file fight forever. +packages/**/*.ts +packages/**/*.tsx +packages/**/*.json +/*.ts +/*.json + +# Next build output +**/.next +**/.source +**/next-env.d.ts # Generated by changesets — reformatting it would churn on every release. **/CHANGELOG.md diff --git a/.prettierrc.json b/.prettierrc.json index d25ee21..4ab4905 100644 --- a/.prettierrc.json +++ b/.prettierrc.json @@ -1,5 +1,19 @@ { "$schema": "https://json.schemastore.org/prettierrc", "printWidth": 90, - "proseWrap": "never" + "singleQuote": true, + "semi": true, + "trailingComma": "all", + "proseWrap": "never", + "plugins": ["prettier-plugin-tailwindcss"], + "overrides": [ + { + "files": "apps/docs/**", + "options": { "tailwindStylesheet": "./apps/docs/app/global.css" } + }, + { + "files": "apps/playground/**", + "options": { "tailwindStylesheet": "./apps/playground/app/globals.css" } + } + ] } diff --git a/apps/docs/.prettierignore b/apps/docs/.prettierignore deleted file mode 100644 index 324551b..0000000 --- a/apps/docs/.prettierignore +++ /dev/null @@ -1,4 +0,0 @@ -.next -.source -node_modules -next-env.d.ts diff --git a/apps/docs/.prettierrc.json b/apps/docs/.prettierrc.json deleted file mode 100644 index 7659439..0000000 --- a/apps/docs/.prettierrc.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "$schema": "https://json.schemastore.org/prettierrc", - "singleQuote": true, - "semi": true, - "trailingComma": "all", - "printWidth": 90, - "plugins": ["prettier-plugin-tailwindcss"], - "overrides": [ - { - "files": "*.mdx", - "options": { - "proseWrap": "never" - } - } - ] -} diff --git a/apps/docs/package.json b/apps/docs/package.json index 6f0bb0a..91e1215 100644 --- a/apps/docs/package.json +++ b/apps/docs/package.json @@ -32,7 +32,6 @@ "@types/react-dom": "^19.2.5", "postcss": "^8.5.26", "prettier": "^3.9.6", - "prettier-plugin-tailwindcss": "^0.8.1", "tailwindcss": "^4.3.3", "typescript": "^6.0.3" } diff --git a/apps/playground/.prettierignore b/apps/playground/.prettierignore deleted file mode 100644 index 324551b..0000000 --- a/apps/playground/.prettierignore +++ /dev/null @@ -1,4 +0,0 @@ -.next -.source -node_modules -next-env.d.ts diff --git a/apps/playground/.prettierrc.json b/apps/playground/.prettierrc.json deleted file mode 100644 index 7659439..0000000 --- a/apps/playground/.prettierrc.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "$schema": "https://json.schemastore.org/prettierrc", - "singleQuote": true, - "semi": true, - "trailingComma": "all", - "printWidth": 90, - "plugins": ["prettier-plugin-tailwindcss"], - "overrides": [ - { - "files": "*.mdx", - "options": { - "proseWrap": "never" - } - } - ] -} diff --git a/apps/playground/package.json b/apps/playground/package.json index a8a2fc3..3ef692f 100644 --- a/apps/playground/package.json +++ b/apps/playground/package.json @@ -26,7 +26,6 @@ "@types/react-dom": "^19.2.5", "postcss": "^8.5.26", "prettier": "^3.9.6", - "prettier-plugin-tailwindcss": "^0.7.1", "tailwindcss": "^4.3.3", "typescript": "^6.0.3" } diff --git a/package.json b/package.json index feea1af..bcbdb82 100644 --- a/package.json +++ b/package.json @@ -18,8 +18,8 @@ "test": "pnpm --filter @timonwa/firebase-hooks test", "test:watch": "pnpm --filter @timonwa/firebase-hooks test:watch", "typecheck": "pnpm -r typecheck", - "lint": "biome check . && prettier --check \"**/*.md\" && pnpm --filter docs lint", - "lint:fix": "biome check --write . && prettier --write \"**/*.md\" && pnpm --filter docs lint:fix", + "lint": "biome check . && prettier --check .", + "lint:fix": "biome check --write . && prettier --write .", "check:exports": "pnpm --filter @timonwa/firebase-hooks check:exports", "check:publish": "pnpm --filter @timonwa/firebase-hooks check:publish", "verify": "pnpm build && pnpm typecheck && pnpm lint && pnpm test && pnpm check:publish && pnpm check:exports", @@ -33,7 +33,8 @@ "@changesets/cli": "^3.0.0", "husky": "^9.1.7", "lint-staged": "^17.3.0", - "prettier": "^3.9.6" + "prettier": "^3.9.6", + "prettier-plugin-tailwindcss": "^0.8.1" }, "lint-staged": { "apps/**/*.{ts,tsx,mdx,json,css}": "prettier --write --ignore-unknown", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 17d486b..3f1c730 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -5,7 +5,6 @@ settings: excludeLinksFromLockfile: false importers: - .: devDependencies: '@biomejs/biome': @@ -26,6 +25,9 @@ importers: prettier: specifier: ^3.9.6 version: 3.9.6 + prettier-plugin-tailwindcss: + specifier: ^0.8.1 + version: 0.8.1(prettier@3.9.6) apps/docs: dependencies: @@ -87,9 +89,6 @@ importers: prettier: specifier: ^3.9.6 version: 3.9.6 - prettier-plugin-tailwindcss: - specifier: ^0.8.1 - version: 0.8.1(prettier@3.9.6) tailwindcss: specifier: ^4.3.3 version: 4.3.3 @@ -139,9 +138,6 @@ importers: prettier: specifier: ^3.9.6 version: 3.9.6 - prettier-plugin-tailwindcss: - specifier: ^0.7.1 - version: 0.7.4(prettier@3.9.6) tailwindcss: specifier: ^4.3.3 version: 4.3.3 @@ -189,55 +185,93 @@ importers: version: 4.1.11(@types/node@26.4.0)(jsdom@28.1.0(supports-color@7.2.0))(vite@8.2.2(@types/node@26.4.0)(esbuild@0.28.2)(jiti@2.7.0)(yaml@2.9.0)) packages: - '@acemir/cssom@0.9.31': - resolution: {integrity: sha512-ZnR3GSaH+/vJ0YlHau21FjfLYjMpYVIzTD8M8vIEQvIGxeOXyXdzCI140rrCY862p/C/BbzWsjc1dgnM9mkoTA==} + resolution: + { + integrity: sha512-ZnR3GSaH+/vJ0YlHau21FjfLYjMpYVIzTD8M8vIEQvIGxeOXyXdzCI140rrCY862p/C/BbzWsjc1dgnM9mkoTA==, + } '@alloc/quick-lru@5.2.0': - resolution: {integrity: sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==} - engines: {node: '>=10'} + resolution: + { + integrity: sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==, + } + engines: { node: '>=10' } '@andrewbranch/untar.js@1.0.4': - resolution: {integrity: sha512-pVXSwPsLuw8IGLo2Di0EaOfsk+ntVvpkk942J/sHYIkwvtKUakEcPh7HBgZ6tuimgzKSEHgCvO4XgQ05DEbwDw==} + resolution: + { + integrity: sha512-pVXSwPsLuw8IGLo2Di0EaOfsk+ntVvpkk942J/sHYIkwvtKUakEcPh7HBgZ6tuimgzKSEHgCvO4XgQ05DEbwDw==, + } '@arethetypeswrong/cli@0.18.5': - resolution: {integrity: sha512-gM+8vRsQOD/Uc7EnBedUhkG5OCsDWE4uoak5QvomGpMpaky0Eh41p04nIMgrWb8EOmqZUJGc6zz9hsP6E56R7g==} - engines: {node: '>=20'} + resolution: + { + integrity: sha512-gM+8vRsQOD/Uc7EnBedUhkG5OCsDWE4uoak5QvomGpMpaky0Eh41p04nIMgrWb8EOmqZUJGc6zz9hsP6E56R7g==, + } + engines: { node: '>=20' } hasBin: true '@arethetypeswrong/core@0.18.5': - resolution: {integrity: sha512-9ytjzGwxjm9Uz7I9avfbt5vlQt6uk9uRRESzJjqrznl6WKvI6dwYTo+vJ3U02Wrq/mR3iql/PzhvHhKdJIAjDQ==} - engines: {node: '>=20'} + resolution: + { + integrity: sha512-9ytjzGwxjm9Uz7I9avfbt5vlQt6uk9uRRESzJjqrznl6WKvI6dwYTo+vJ3U02Wrq/mR3iql/PzhvHhKdJIAjDQ==, + } + engines: { node: '>=20' } '@asamuzakjp/css-color@5.1.11': - resolution: {integrity: sha512-KVw6qIiCTUQhByfTd78h2yD1/00waTmm9uy/R7Ck/ctUyAPj+AEDLkQIdJW0T8+qGgj3j5bpNKK7Q3G+LedJWg==} - engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} + resolution: + { + integrity: sha512-KVw6qIiCTUQhByfTd78h2yD1/00waTmm9uy/R7Ck/ctUyAPj+AEDLkQIdJW0T8+qGgj3j5bpNKK7Q3G+LedJWg==, + } + engines: { node: ^20.19.0 || ^22.12.0 || >=24.0.0 } '@asamuzakjp/dom-selector@6.8.1': - resolution: {integrity: sha512-MvRz1nCqW0fsy8Qz4dnLIvhOlMzqDVBabZx6lH+YywFDdjXhMY37SmpV1XFX3JzG5GWHn63j6HX6QPr3lZXHvQ==} + resolution: + { + integrity: sha512-MvRz1nCqW0fsy8Qz4dnLIvhOlMzqDVBabZx6lH+YywFDdjXhMY37SmpV1XFX3JzG5GWHn63j6HX6QPr3lZXHvQ==, + } '@asamuzakjp/generational-cache@1.0.1': - resolution: {integrity: sha512-wajfB8KqzMCN2KGNFdLkReeHncd0AslUSrvHVvvYWuU8ghncRJoA50kT3zP9MVL0+9g4/67H+cdvBskj9THPzg==} - engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} + resolution: + { + integrity: sha512-wajfB8KqzMCN2KGNFdLkReeHncd0AslUSrvHVvvYWuU8ghncRJoA50kT3zP9MVL0+9g4/67H+cdvBskj9THPzg==, + } + engines: { node: ^20.19.0 || ^22.12.0 || >=24.0.0 } '@asamuzakjp/nwsapi@2.3.9': - resolution: {integrity: sha512-n8GuYSrI9bF7FFZ/SjhwevlHc8xaVlb/7HmHelnc/PZXBD2ZR49NnN9sMMuDdEGPeeRQ5d0hqlSlEpgCX3Wl0Q==} + resolution: + { + integrity: sha512-n8GuYSrI9bF7FFZ/SjhwevlHc8xaVlb/7HmHelnc/PZXBD2ZR49NnN9sMMuDdEGPeeRQ5d0hqlSlEpgCX3Wl0Q==, + } '@babel/code-frame@7.29.7': - resolution: {integrity: sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==} - engines: {node: '>=6.9.0'} + resolution: + { + integrity: sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==, + } + engines: { node: '>=6.9.0' } '@babel/helper-validator-identifier@7.29.7': - resolution: {integrity: sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==} - engines: {node: '>=6.9.0'} + resolution: + { + integrity: sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==, + } + engines: { node: '>=6.9.0' } '@babel/runtime@7.29.7': - resolution: {integrity: sha512-Nq8OhGWiZIZGV6hLHoyAKLLcJihP/xFeBMGJoUrxTX2psI8dCifzLhZISFb+VWS3wFMRDmCGw5R+dOySCqPLhw==} - engines: {node: '>=6.9.0'} + resolution: + { + integrity: sha512-Nq8OhGWiZIZGV6hLHoyAKLLcJihP/xFeBMGJoUrxTX2psI8dCifzLhZISFb+VWS3wFMRDmCGw5R+dOySCqPLhw==, + } + engines: { node: '>=6.9.0' } '@base-ui/react@1.7.0': - resolution: {integrity: sha512-j+8QjX44C32jrXD/qyEAGpFr70FRpGL2CY61mQd9nBPWN737CK0xxD1ceJ055rW4RtdvFDT1e7otzdlfxvsYug==} - engines: {node: '>=14.0.0'} + resolution: + { + integrity: sha512-j+8QjX44C32jrXD/qyEAGpFr70FRpGL2CY61mQd9nBPWN737CK0xxD1ceJ055rW4RtdvFDT1e7otzdlfxvsYug==, + } + engines: { node: '>=14.0.0' } peerDependencies: '@date-fns/tz': ^1.2.0 '@types/react': ^17 || ^18 || ^19 @@ -253,7 +287,10 @@ packages: optional: true '@base-ui/utils@0.3.2': - resolution: {integrity: sha512-oWy1aq/I2GmYjpl4PhEAhzflF8VPGKgZeq0xAWTbfD5KBWyxcN0ZP2+WHSUm/5Z6lVMBDLReLcoXwSYoRc/zNQ==} + resolution: + { + integrity: sha512-oWy1aq/I2GmYjpl4PhEAhzflF8VPGKgZeq0xAWTbfD5KBWyxcN0ZP2+WHSUm/5Z6lVMBDLReLcoXwSYoRc/zNQ==, + } peerDependencies: '@types/react': ^17 || ^18 || ^19 react: ^17 || ^18 || ^19 @@ -263,177 +300,289 @@ packages: optional: true '@biomejs/biome@2.5.11': - resolution: {integrity: sha512-Tj0dnkLPdW0ASjHfj2D/ZkkvPU2wrFmnE1jWTD2xzV1ycapV1DutbYXk4NDnR3rYTi1ZCbNFD4G2gRMEY65WaA==} - engines: {node: '>=14.21.3'} + resolution: + { + integrity: sha512-Tj0dnkLPdW0ASjHfj2D/ZkkvPU2wrFmnE1jWTD2xzV1ycapV1DutbYXk4NDnR3rYTi1ZCbNFD4G2gRMEY65WaA==, + } + engines: { node: '>=14.21.3' } hasBin: true '@biomejs/cli-darwin-arm64@2.5.11': - resolution: {integrity: sha512-6SGZxoKbXvUjMn1t6A98HqWISPnGNbYs0R/Rt2JarmXBSev+lva4QxUMWEBX9lX1Wo1XTJ78uk5xVDtG58SRZg==} - engines: {node: '>=14.21.3'} + resolution: + { + integrity: sha512-6SGZxoKbXvUjMn1t6A98HqWISPnGNbYs0R/Rt2JarmXBSev+lva4QxUMWEBX9lX1Wo1XTJ78uk5xVDtG58SRZg==, + } + engines: { node: '>=14.21.3' } cpu: [arm64] os: [darwin] '@biomejs/cli-darwin-x64@2.5.11': - resolution: {integrity: sha512-nYkXY7tLBEgnGbYapDKAyKzgt44ZEyG+AKalvTXtCWKYgepI9dw327q+cVgedxm+Udi1ZzHKUyZrIusHi/KQbw==} - engines: {node: '>=14.21.3'} + resolution: + { + integrity: sha512-nYkXY7tLBEgnGbYapDKAyKzgt44ZEyG+AKalvTXtCWKYgepI9dw327q+cVgedxm+Udi1ZzHKUyZrIusHi/KQbw==, + } + engines: { node: '>=14.21.3' } cpu: [x64] os: [darwin] '@biomejs/cli-linux-arm64-musl@2.5.11': - resolution: {integrity: sha512-qhyZUMyCbWYFV2bAwRNVvfMVZ+hv7WYl6mossGrxC+uiQQXhvsuWWU8zz6jYX0mChZd9MgQZbm4vozTmG/5iGw==} - engines: {node: '>=14.21.3'} + resolution: + { + integrity: sha512-qhyZUMyCbWYFV2bAwRNVvfMVZ+hv7WYl6mossGrxC+uiQQXhvsuWWU8zz6jYX0mChZd9MgQZbm4vozTmG/5iGw==, + } + engines: { node: '>=14.21.3' } cpu: [arm64] os: [linux] libc: [musl] '@biomejs/cli-linux-arm64@2.5.11': - resolution: {integrity: sha512-3PVLSTD9RR73rvVPt5G3T1gc+ycggWEGfTD7RvzzbtcDPD27NxgxBbAFfpm7DXJKW6VLHWE1lLMGvFt2Qxjcow==} - engines: {node: '>=14.21.3'} + resolution: + { + integrity: sha512-3PVLSTD9RR73rvVPt5G3T1gc+ycggWEGfTD7RvzzbtcDPD27NxgxBbAFfpm7DXJKW6VLHWE1lLMGvFt2Qxjcow==, + } + engines: { node: '>=14.21.3' } cpu: [arm64] os: [linux] libc: [glibc] '@biomejs/cli-linux-x64-musl@2.5.11': - resolution: {integrity: sha512-oRRlrchG5EfrEL/EmtT1qUjSNHk3/5LGeZhQqADBBAJF1b1ET6964xEKe7aGlGARzDfza8H/seEsFJl7S6Ql9w==} - engines: {node: '>=14.21.3'} + resolution: + { + integrity: sha512-oRRlrchG5EfrEL/EmtT1qUjSNHk3/5LGeZhQqADBBAJF1b1ET6964xEKe7aGlGARzDfza8H/seEsFJl7S6Ql9w==, + } + engines: { node: '>=14.21.3' } cpu: [x64] os: [linux] libc: [musl] '@biomejs/cli-linux-x64@2.5.11': - resolution: {integrity: sha512-JOytptlsgM33B2MMFUg8iBrb4IKpbD5JnJrSeYiaFEeAj4vuXx0iQSQZ4qK7sqyMtfjZxxPdNdMZZVL4y/mFyA==} - engines: {node: '>=14.21.3'} + resolution: + { + integrity: sha512-JOytptlsgM33B2MMFUg8iBrb4IKpbD5JnJrSeYiaFEeAj4vuXx0iQSQZ4qK7sqyMtfjZxxPdNdMZZVL4y/mFyA==, + } + engines: { node: '>=14.21.3' } cpu: [x64] os: [linux] libc: [glibc] '@biomejs/cli-win32-arm64@2.5.11': - resolution: {integrity: sha512-e49E6K9hzH/ohJNx8Y26mY8HaV4I4ZViIeoqhKsmoXLKHhQnMeBAVqCgsGf2Wa3lXlS7RkporDXMHHWkzvZzFw==} - engines: {node: '>=14.21.3'} + resolution: + { + integrity: sha512-e49E6K9hzH/ohJNx8Y26mY8HaV4I4ZViIeoqhKsmoXLKHhQnMeBAVqCgsGf2Wa3lXlS7RkporDXMHHWkzvZzFw==, + } + engines: { node: '>=14.21.3' } cpu: [arm64] os: [win32] '@biomejs/cli-win32-x64@2.5.11': - resolution: {integrity: sha512-QSQr/KjOgXA7OzXJUWS+oguKyAZ3Q0l/lnlDGbu397eKo83atuWUjBPJrsqbKNF6CARGw8XXJLGzpHC8Ryhd4Q==} - engines: {node: '>=14.21.3'} + resolution: + { + integrity: sha512-QSQr/KjOgXA7OzXJUWS+oguKyAZ3Q0l/lnlDGbu397eKo83atuWUjBPJrsqbKNF6CARGw8XXJLGzpHC8Ryhd4Q==, + } + engines: { node: '>=14.21.3' } cpu: [x64] os: [win32] '@braidai/lang@1.1.2': - resolution: {integrity: sha512-qBcknbBufNHlui137Hft8xauQMTZDKdophmLFv05r2eNmdIv/MlPuP4TdUknHG68UdWLgVZwgxVe735HzJNIwA==} + resolution: + { + integrity: sha512-qBcknbBufNHlui137Hft8xauQMTZDKdophmLFv05r2eNmdIv/MlPuP4TdUknHG68UdWLgVZwgxVe735HzJNIwA==, + } '@bramus/specificity@2.4.2': - resolution: {integrity: sha512-ctxtJ/eA+t+6q2++vj5j7FYX3nRu311q1wfYH3xjlLOsczhlhxAg2FWNUXhpGvAw3BWo1xBcvOV6/YLc2r5FJw==} + resolution: + { + integrity: sha512-ctxtJ/eA+t+6q2++vj5j7FYX3nRu311q1wfYH3xjlLOsczhlhxAg2FWNUXhpGvAw3BWo1xBcvOV6/YLc2r5FJw==, + } hasBin: true '@changesets/apply-release-plan@8.0.0': - resolution: {integrity: sha512-kUd2pbf1w5/AYmBMb0Tt+rkIPCjFJdT0SZMrkOjJT/WV/QbtmvkyB5jkV0oaNPheavprZk+SfUiozUti7TIL2w==} - engines: {node: ^22.11 || ^24 || >=26} + resolution: + { + integrity: sha512-kUd2pbf1w5/AYmBMb0Tt+rkIPCjFJdT0SZMrkOjJT/WV/QbtmvkyB5jkV0oaNPheavprZk+SfUiozUti7TIL2w==, + } + engines: { node: ^22.11 || ^24 || >=26 } '@changesets/assemble-release-plan@7.0.0': - resolution: {integrity: sha512-oEW8BxdA604kGGtDSCiHr5w9Tv4UWe9I2k61IBNZzCOE1kbYaJj4v+lFQNgcEZFkUc2pV/+hASErGDvpJOZCTg==} - engines: {node: ^22.11 || ^24 || >=26} + resolution: + { + integrity: sha512-oEW8BxdA604kGGtDSCiHr5w9Tv4UWe9I2k61IBNZzCOE1kbYaJj4v+lFQNgcEZFkUc2pV/+hASErGDvpJOZCTg==, + } + engines: { node: ^22.11 || ^24 || >=26 } '@changesets/changelog-git@1.0.0': - resolution: {integrity: sha512-3Dst2Ime2Op5nd4XmWJLPIgp11ZFqJqSkVug9izK6TDcIV4YlhPS4ECbEVR+eGI0bk0r1ItogD4j2Oli87bJrA==} - engines: {node: ^22.11 || ^24 || >=26} + resolution: + { + integrity: sha512-3Dst2Ime2Op5nd4XmWJLPIgp11ZFqJqSkVug9izK6TDcIV4YlhPS4ECbEVR+eGI0bk0r1ItogD4j2Oli87bJrA==, + } + engines: { node: ^22.11 || ^24 || >=26 } '@changesets/changelog-github@0.5.2': - resolution: {integrity: sha512-HeGeDl8HaIGj9fQHo/tv5XKQ2SNEi9+9yl1Bss1jttPqeiASRXhfi0A2wv8yFKCp07kR1gpOI5ge6+CWNm1jPw==} + resolution: + { + integrity: sha512-HeGeDl8HaIGj9fQHo/tv5XKQ2SNEi9+9yl1Bss1jttPqeiASRXhfi0A2wv8yFKCp07kR1gpOI5ge6+CWNm1jPw==, + } '@changesets/cli@3.0.1': - resolution: {integrity: sha512-3IVpRSgiKDj2aRbBHKtWTXSRH2/iR3bWxau9iQUoEsZjDhRIj9nhl90k7FWMxZAJvLgJBbgMq8+qS0xQsenYsQ==} - engines: {node: ^22.11 || ^24 || >=26, npm: '>=10.9.0', pnpm: '>=10.0.0', yarn: '>=4.5.2'} + resolution: + { + integrity: sha512-3IVpRSgiKDj2aRbBHKtWTXSRH2/iR3bWxau9iQUoEsZjDhRIj9nhl90k7FWMxZAJvLgJBbgMq8+qS0xQsenYsQ==, + } + engines: + { node: ^22.11 || ^24 || >=26, npm: '>=10.9.0', pnpm: '>=10.0.0', yarn: '>=4.5.2' } hasBin: true '@changesets/config@4.0.0': - resolution: {integrity: sha512-mw95/YrkOuhZZxfnVAA4bSXOFUi+KlhzOBTM8C4x777NhUU6HWIl9Z+K+nME+E4PVsv5NQVQwTfiHihAS1A/ow==} - engines: {node: ^22.11 || ^24 || >=26} + resolution: + { + integrity: sha512-mw95/YrkOuhZZxfnVAA4bSXOFUi+KlhzOBTM8C4x777NhUU6HWIl9Z+K+nME+E4PVsv5NQVQwTfiHihAS1A/ow==, + } + engines: { node: ^22.11 || ^24 || >=26 } '@changesets/errors@1.0.0': - resolution: {integrity: sha512-ElN/mEzn6zmETgjwf5MclCMa9ef59sAR0lfO8VSYIsiRvbC2FbLB/92EoYw10Sl0kGixxHFiJZUSv7dA+YpR8g==} - engines: {node: ^22.11 || ^24 || >=26} + resolution: + { + integrity: sha512-ElN/mEzn6zmETgjwf5MclCMa9ef59sAR0lfO8VSYIsiRvbC2FbLB/92EoYw10Sl0kGixxHFiJZUSv7dA+YpR8g==, + } + engines: { node: ^22.11 || ^24 || >=26 } '@changesets/format@0.1.2': - resolution: {integrity: sha512-Caez5XtNXCFS/G5bwyav3wuXL0tMxVd2ZGbaumWbzN08tyzO21asCw7JZhNtVsAZDCvDRUzZN+Iit9SyRITSYA==} - engines: {node: ^22.11 || ^24 || >=26} + resolution: + { + integrity: sha512-Caez5XtNXCFS/G5bwyav3wuXL0tMxVd2ZGbaumWbzN08tyzO21asCw7JZhNtVsAZDCvDRUzZN+Iit9SyRITSYA==, + } + engines: { node: ^22.11 || ^24 || >=26 } '@changesets/get-dependents-graph@3.0.0': - resolution: {integrity: sha512-ji/t5wFA1zREKXRUePE6Qi+Qu2UgxCeSSGQrphezwvQZrp49B7sJ+8+wvM0tA7zPeSxYKCojDy3WWgrl+s+awg==} - engines: {node: ^22.11 || ^24 || >=26} + resolution: + { + integrity: sha512-ji/t5wFA1zREKXRUePE6Qi+Qu2UgxCeSSGQrphezwvQZrp49B7sJ+8+wvM0tA7zPeSxYKCojDy3WWgrl+s+awg==, + } + engines: { node: ^22.11 || ^24 || >=26 } '@changesets/get-github-info@0.7.0': - resolution: {integrity: sha512-+i67Bmhfj9V4KfDeS1+Tz3iF32btKZB2AAx+cYMqDSRFP7r3/ZdGbjCo+c6qkyViN9ygDuBjzageuPGJtKGe5A==} + resolution: + { + integrity: sha512-+i67Bmhfj9V4KfDeS1+Tz3iF32btKZB2AAx+cYMqDSRFP7r3/ZdGbjCo+c6qkyViN9ygDuBjzageuPGJtKGe5A==, + } '@changesets/git@4.0.0': - resolution: {integrity: sha512-uIEswpPUgzBBqrC0qg13byNaPorzhf05LE2T+gRizEKCXvMsJC6NPJp5iNDSV/gYj4Viqh09UiOF5E7XWeH5lQ==} - engines: {node: ^22.11 || ^24 || >=26} + resolution: + { + integrity: sha512-uIEswpPUgzBBqrC0qg13byNaPorzhf05LE2T+gRizEKCXvMsJC6NPJp5iNDSV/gYj4Viqh09UiOF5E7XWeH5lQ==, + } + engines: { node: ^22.11 || ^24 || >=26 } '@changesets/parse@1.0.0': - resolution: {integrity: sha512-P0iaMb9p9CRYZiTgAllEIF9AUMQHIy1G72tKlcIqJp61icZSsKQNiOPdxAMZG8m/DvZwt/oz5xEbTpike//dWg==} - engines: {node: ^22.11 || ^24 || >=26} + resolution: + { + integrity: sha512-P0iaMb9p9CRYZiTgAllEIF9AUMQHIy1G72tKlcIqJp61icZSsKQNiOPdxAMZG8m/DvZwt/oz5xEbTpike//dWg==, + } + engines: { node: ^22.11 || ^24 || >=26 } '@changesets/pre@3.0.0': - resolution: {integrity: sha512-Zm/6YliV/a2oeWTqHJf6KxLrQwgcK1i/BRDl2m0EKZvbnxV5fG9QRhwJJGshjsZTUTS6dkfURQ2K6aAvgNw/3Q==} - engines: {node: ^22.11 || ^24 || >=26} + resolution: + { + integrity: sha512-Zm/6YliV/a2oeWTqHJf6KxLrQwgcK1i/BRDl2m0EKZvbnxV5fG9QRhwJJGshjsZTUTS6dkfURQ2K6aAvgNw/3Q==, + } + engines: { node: ^22.11 || ^24 || >=26 } '@changesets/read@1.0.0': - resolution: {integrity: sha512-8TdE2PwG6yArPt5Ozej83Z6iHz1G8BDBKvIEKZ455MccR4K3GNbLR2XqjBxa+5yLFnEd3xAOPXYboBg1pt8stQ==} - engines: {node: ^22.11 || ^24 || >=26} + resolution: + { + integrity: sha512-8TdE2PwG6yArPt5Ozej83Z6iHz1G8BDBKvIEKZ455MccR4K3GNbLR2XqjBxa+5yLFnEd3xAOPXYboBg1pt8stQ==, + } + engines: { node: ^22.11 || ^24 || >=26 } '@changesets/should-skip-package@1.0.0': - resolution: {integrity: sha512-pwqoJmbONn1XgXmZXPEExgAaT+HdZLjALFTDgIm+PnS5KeO2nLtzA2/Q+4aMFY14kFMuXKG30MObhvDzWgzDgg==} - engines: {node: ^22.11 || ^24 || >=26} + resolution: + { + integrity: sha512-pwqoJmbONn1XgXmZXPEExgAaT+HdZLjALFTDgIm+PnS5KeO2nLtzA2/Q+4aMFY14kFMuXKG30MObhvDzWgzDgg==, + } + engines: { node: ^22.11 || ^24 || >=26 } '@changesets/types@6.1.0': - resolution: {integrity: sha512-rKQcJ+o1nKNgeoYRHKOS07tAMNd3YSN0uHaJOZYjBAgxfV7TUE7JE+z4BzZdQwb5hKaYbayKN5KrYV7ODb2rAA==} + resolution: + { + integrity: sha512-rKQcJ+o1nKNgeoYRHKOS07tAMNd3YSN0uHaJOZYjBAgxfV7TUE7JE+z4BzZdQwb5hKaYbayKN5KrYV7ODb2rAA==, + } '@changesets/types@7.0.0': - resolution: {integrity: sha512-c5GoiQyt3pxiXjrWSNoP8/GRf4kG+VnKzovx1OQM8dYYALlSwgedmkPmJ+ZqGxqwg9D3Bkj85Uo4KLd5BN3A0w==} - engines: {node: ^22.11 || ^24 || >=26} + resolution: + { + integrity: sha512-c5GoiQyt3pxiXjrWSNoP8/GRf4kG+VnKzovx1OQM8dYYALlSwgedmkPmJ+ZqGxqwg9D3Bkj85Uo4KLd5BN3A0w==, + } + engines: { node: ^22.11 || ^24 || >=26 } '@changesets/write@1.0.1': - resolution: {integrity: sha512-q/ThtP9gcnEP6xlv7LrY26C2mHqdGBti/MmOVngt/M/oIGYkssmQGxPK9WzBNt2juVcH/vml2WQ+ra8LXYOTaA==} - engines: {node: ^22.11 || ^24 || >=26} + resolution: + { + integrity: sha512-q/ThtP9gcnEP6xlv7LrY26C2mHqdGBti/MmOVngt/M/oIGYkssmQGxPK9WzBNt2juVcH/vml2WQ+ra8LXYOTaA==, + } + engines: { node: ^22.11 || ^24 || >=26 } '@clack/core@1.4.3': - resolution: {integrity: sha512-/kr3UWNtdJfxZtPgDqUOmG2pvwlmcLGheex5yiZKdwbzZJxhV+HMNR9QNmyY5cGwTNV6LrR7Jtp+KjhUAP1qBQ==} - engines: {node: '>= 20.12.0'} + resolution: + { + integrity: sha512-/kr3UWNtdJfxZtPgDqUOmG2pvwlmcLGheex5yiZKdwbzZJxhV+HMNR9QNmyY5cGwTNV6LrR7Jtp+KjhUAP1qBQ==, + } + engines: { node: '>= 20.12.0' } '@clack/prompts@1.7.0': - resolution: {integrity: sha512-y7/yvZ2TPAnR9+jnc00klvNNLkJiXFFrQA/hlLCcxA9a2A4zQIOimyFQ9XfwYKiGD1fb5GY8vbKIIgO8d5Tb2A==} - engines: {node: '>= 20.12.0'} + resolution: + { + integrity: sha512-y7/yvZ2TPAnR9+jnc00klvNNLkJiXFFrQA/hlLCcxA9a2A4zQIOimyFQ9XfwYKiGD1fb5GY8vbKIIgO8d5Tb2A==, + } + engines: { node: '>= 20.12.0' } '@colors/colors@1.5.0': - resolution: {integrity: sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==} - engines: {node: '>=0.1.90'} + resolution: + { + integrity: sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==, + } + engines: { node: '>=0.1.90' } '@csstools/color-helpers@6.1.1': - resolution: {integrity: sha512-gLNsunvwf3mCi5u5o46/Z/JcJMnhbHSaZ69rkgPzNM3J4s8hWwpPUQB6/tt0EDFyCiWzxANlx+2LJwpYj4zS1w==} - engines: {node: '>=20.19.0'} + resolution: + { + integrity: sha512-gLNsunvwf3mCi5u5o46/Z/JcJMnhbHSaZ69rkgPzNM3J4s8hWwpPUQB6/tt0EDFyCiWzxANlx+2LJwpYj4zS1w==, + } + engines: { node: '>=20.19.0' } '@csstools/css-calc@3.3.0': - resolution: {integrity: sha512-c5ihYsPkdG6JCkU2zTMm4+k6r7RXuGxtWYhu5DHMIiF1FHzrfmHL5so11AoFpUv/tu61xfcmT4AmKoFfMPoqdQ==} - engines: {node: '>=20.19.0'} + resolution: + { + integrity: sha512-c5ihYsPkdG6JCkU2zTMm4+k6r7RXuGxtWYhu5DHMIiF1FHzrfmHL5so11AoFpUv/tu61xfcmT4AmKoFfMPoqdQ==, + } + engines: { node: '>=20.19.0' } peerDependencies: '@csstools/css-parser-algorithms': ^4.0.0 '@csstools/css-tokenizer': ^4.0.0 '@csstools/css-color-parser@4.2.1': - resolution: {integrity: sha512-YpAJZhaHplYQkG8ib+/Fx5Y0eF2lVWi3tIvMJA6i39TLyUNp2439cifzW8VMjhlqrBjHzK5hVGugRRm2zTKI/A==} - engines: {node: '>=20.19.0'} + resolution: + { + integrity: sha512-YpAJZhaHplYQkG8ib+/Fx5Y0eF2lVWi3tIvMJA6i39TLyUNp2439cifzW8VMjhlqrBjHzK5hVGugRRm2zTKI/A==, + } + engines: { node: '>=20.19.0' } peerDependencies: '@csstools/css-parser-algorithms': ^4.0.0 '@csstools/css-tokenizer': ^4.0.0 '@csstools/css-parser-algorithms@4.0.0': - resolution: {integrity: sha512-+B87qS7fIG3L5h3qwJ/IFbjoVoOe/bpOdh9hAjXbvx0o8ImEmUsGXN0inFOnk2ChCFgqkkGFQ+TpM5rbhkKe4w==} - engines: {node: '>=20.19.0'} + resolution: + { + integrity: sha512-+B87qS7fIG3L5h3qwJ/IFbjoVoOe/bpOdh9hAjXbvx0o8ImEmUsGXN0inFOnk2ChCFgqkkGFQ+TpM5rbhkKe4w==, + } + engines: { node: '>=20.19.0' } peerDependencies: '@csstools/css-tokenizer': ^4.0.0 '@csstools/css-syntax-patches-for-csstree@1.1.9': - resolution: {integrity: sha512-iGGw4OsAYsS6pD29MdJ2bX/nJx65a04ZZiw6x+VwWlP2DdXf6f++Zmuv/OzALpdyfVhjbduIIF2cXM7HWBIe9A==} + resolution: + { + integrity: sha512-iGGw4OsAYsS6pD29MdJ2bX/nJx65a04ZZiw6x+VwWlP2DdXf6f++Zmuv/OzALpdyfVhjbduIIF2cXM7HWBIe9A==, + } peerDependencies: css-tree: ^3.2.1 peerDependenciesMeta: @@ -441,171 +590,258 @@ packages: optional: true '@csstools/css-tokenizer@4.0.0': - resolution: {integrity: sha512-QxULHAm7cNu72w97JUNCBFODFaXpbDg+dP8b/oWFAZ2MTRppA3U00Y2L1HqaS4J6yBqxwa/Y3nMBaxVKbB/NsA==} - engines: {node: '>=20.19.0'} + resolution: + { + integrity: sha512-QxULHAm7cNu72w97JUNCBFODFaXpbDg+dP8b/oWFAZ2MTRppA3U00Y2L1HqaS4J6yBqxwa/Y3nMBaxVKbB/NsA==, + } + engines: { node: '>=20.19.0' } '@emnapi/runtime@1.11.3': - resolution: {integrity: sha512-Xz4Tpyki7XyrpbUK1jR1AhdAdaXyhhY4lZ3neLodmhpuWfy2PAQN5B46sAiU4liOXGLkHypn/qU+jvfWSCYYLA==} + resolution: + { + integrity: sha512-Xz4Tpyki7XyrpbUK1jR1AhdAdaXyhhY4lZ3neLodmhpuWfy2PAQN5B46sAiU4liOXGLkHypn/qU+jvfWSCYYLA==, + } '@esbuild/aix-ppc64@0.28.2': - resolution: {integrity: sha512-XExcO+dvLKvVtNTibSTBej1NCAbaGhWn9Ww1ZPx80qsahhPFe/8jgWP0IchNe0F3HwkU7n8ejhH8bjonqht8mQ==} - engines: {node: '>=18'} + resolution: + { + integrity: sha512-XExcO+dvLKvVtNTibSTBej1NCAbaGhWn9Ww1ZPx80qsahhPFe/8jgWP0IchNe0F3HwkU7n8ejhH8bjonqht8mQ==, + } + engines: { node: '>=18' } cpu: [ppc64] os: [aix] '@esbuild/android-arm64@0.28.2': - resolution: {integrity: sha512-5YfKeeI8qWfBZIX+u2xZC3Zlb3Os/gLS2sbEKM+I4ZOcsWmHS2WLysCcQZDAFRslDUU5Oiq44gf6PYN1vGwG5A==} - engines: {node: '>=18'} + resolution: + { + integrity: sha512-5YfKeeI8qWfBZIX+u2xZC3Zlb3Os/gLS2sbEKM+I4ZOcsWmHS2WLysCcQZDAFRslDUU5Oiq44gf6PYN1vGwG5A==, + } + engines: { node: '>=18' } cpu: [arm64] os: [android] '@esbuild/android-arm@0.28.2': - resolution: {integrity: sha512-kXXoiPVVGQcnIYGOeaovwOURpniDBpSq4A03qkQ+BMQqtGG6HYap3xne9C1O1yo4TR3qxlCX5IqqmX6fFo2Lqg==} - engines: {node: '>=18'} + resolution: + { + integrity: sha512-kXXoiPVVGQcnIYGOeaovwOURpniDBpSq4A03qkQ+BMQqtGG6HYap3xne9C1O1yo4TR3qxlCX5IqqmX6fFo2Lqg==, + } + engines: { node: '>=18' } cpu: [arm] os: [android] '@esbuild/android-x64@0.28.2': - resolution: {integrity: sha512-O387ite7SzUyCcy3JQX4P4bLtEA7bLLkx+esve5JHnyYfNTxcVpXZo9jhdB0lTKN44gztELTdU7nS8Nr16Fs1Q==} - engines: {node: '>=18'} + resolution: + { + integrity: sha512-O387ite7SzUyCcy3JQX4P4bLtEA7bLLkx+esve5JHnyYfNTxcVpXZo9jhdB0lTKN44gztELTdU7nS8Nr16Fs1Q==, + } + engines: { node: '>=18' } cpu: [x64] os: [android] '@esbuild/darwin-arm64@0.28.2': - resolution: {integrity: sha512-n4KqkOQrraxHJcgjM1RvwbigfQKIKJVpM7xp+KsxiyUSrRdIXnt73VhrPAx0fV44hgfmIVKjxMN9J1t5jySVkw==} - engines: {node: '>=18'} + resolution: + { + integrity: sha512-n4KqkOQrraxHJcgjM1RvwbigfQKIKJVpM7xp+KsxiyUSrRdIXnt73VhrPAx0fV44hgfmIVKjxMN9J1t5jySVkw==, + } + engines: { node: '>=18' } cpu: [arm64] os: [darwin] '@esbuild/darwin-x64@0.28.2': - resolution: {integrity: sha512-uq6suIWYP37qzGddBKPw5QEQPi6HiLGsO7UmkpfyaYNQ3D+rN6w6WfwH+nuqcGXWvawGwxOEroO4YGnFh95azw==} - engines: {node: '>=18'} + resolution: + { + integrity: sha512-uq6suIWYP37qzGddBKPw5QEQPi6HiLGsO7UmkpfyaYNQ3D+rN6w6WfwH+nuqcGXWvawGwxOEroO4YGnFh95azw==, + } + engines: { node: '>=18' } cpu: [x64] os: [darwin] '@esbuild/freebsd-arm64@0.28.2': - resolution: {integrity: sha512-n+I0BTSRIoy+d6RPKnEVwql5UwBJolytvY4mAOIEJorKlqgPII8ix6slVVrfZ5Tnj7glIZvloylbB/EJPMWEXw==} - engines: {node: '>=18'} + resolution: + { + integrity: sha512-n+I0BTSRIoy+d6RPKnEVwql5UwBJolytvY4mAOIEJorKlqgPII8ix6slVVrfZ5Tnj7glIZvloylbB/EJPMWEXw==, + } + engines: { node: '>=18' } cpu: [arm64] os: [freebsd] '@esbuild/freebsd-x64@0.28.2': - resolution: {integrity: sha512-78XJTJkvPs0kz2w61301PJjXl4g7q3JqiYMZ/M/yVI73EHBrCRTgkhu9oqG7vPqq+a/yadEW8aD+agKlk5xrmg==} - engines: {node: '>=18'} + resolution: + { + integrity: sha512-78XJTJkvPs0kz2w61301PJjXl4g7q3JqiYMZ/M/yVI73EHBrCRTgkhu9oqG7vPqq+a/yadEW8aD+agKlk5xrmg==, + } + engines: { node: '>=18' } cpu: [x64] os: [freebsd] '@esbuild/linux-arm64@0.28.2': - resolution: {integrity: sha512-pW4AC0P3it8c7do9MVM4p51FzHzdM/TZrerurgRcHJ2WTa1VQ1CIq18xncfpBJw4ojkiZZrKW2yIBWBP92j6Ug==} - engines: {node: '>=18'} + resolution: + { + integrity: sha512-pW4AC0P3it8c7do9MVM4p51FzHzdM/TZrerurgRcHJ2WTa1VQ1CIq18xncfpBJw4ojkiZZrKW2yIBWBP92j6Ug==, + } + engines: { node: '>=18' } cpu: [arm64] os: [linux] '@esbuild/linux-arm@0.28.2': - resolution: {integrity: sha512-XlDnu2q5yoqems+xay6wSAcg9DDD7K9RLKZEBOMZm3ckNpJBvOX20tSfby8KfrrhINDyv9V2YVZKY/SpoGJI8w==} - engines: {node: '>=18'} + resolution: + { + integrity: sha512-XlDnu2q5yoqems+xay6wSAcg9DDD7K9RLKZEBOMZm3ckNpJBvOX20tSfby8KfrrhINDyv9V2YVZKY/SpoGJI8w==, + } + engines: { node: '>=18' } cpu: [arm] os: [linux] '@esbuild/linux-ia32@0.28.2': - resolution: {integrity: sha512-CYbnj78HsIeA+DhgUKgFCfvNsTHFhMMrinUrMZpDXJXKN8T3XViTZ/+wtHeVxEWY8ewSzTFN+nRmSwO2tZaLUQ==} - engines: {node: '>=18'} + resolution: + { + integrity: sha512-CYbnj78HsIeA+DhgUKgFCfvNsTHFhMMrinUrMZpDXJXKN8T3XViTZ/+wtHeVxEWY8ewSzTFN+nRmSwO2tZaLUQ==, + } + engines: { node: '>=18' } cpu: [ia32] os: [linux] '@esbuild/linux-loong64@0.28.2': - resolution: {integrity: sha512-buwkd8nsph4R+ajRvw0qM5Hja/TXQow3ptzWO2EbG/cqcIkHloRrdlBtQlshyYGTNFvfkfJ5tpPLVkY4DtsPfQ==} - engines: {node: '>=18'} + resolution: + { + integrity: sha512-buwkd8nsph4R+ajRvw0qM5Hja/TXQow3ptzWO2EbG/cqcIkHloRrdlBtQlshyYGTNFvfkfJ5tpPLVkY4DtsPfQ==, + } + engines: { node: '>=18' } cpu: [loong64] os: [linux] '@esbuild/linux-mips64el@0.28.2': - resolution: {integrity: sha512-ZVykbDyk7519VwiNb9Lcj9m8XM6v5V9uKPvrEMkkEedVewf+0itkhahp4HDpgERXhwLRpWFypsGbG/J8s0QjJA==} - engines: {node: '>=18'} + resolution: + { + integrity: sha512-ZVykbDyk7519VwiNb9Lcj9m8XM6v5V9uKPvrEMkkEedVewf+0itkhahp4HDpgERXhwLRpWFypsGbG/J8s0QjJA==, + } + engines: { node: '>=18' } cpu: [mips64el] os: [linux] '@esbuild/linux-ppc64@0.28.2': - resolution: {integrity: sha512-CAXl+Dtd9UUuJd8pKKdwh6MLm3MUMiqMPmhZ3tTSXPqfyQ3vDl6R5hZdZ/kYojK4ofXtdfSv1tFq8XzWx3heNQ==} - engines: {node: '>=18'} + resolution: + { + integrity: sha512-CAXl+Dtd9UUuJd8pKKdwh6MLm3MUMiqMPmhZ3tTSXPqfyQ3vDl6R5hZdZ/kYojK4ofXtdfSv1tFq8XzWx3heNQ==, + } + engines: { node: '>=18' } cpu: [ppc64] os: [linux] '@esbuild/linux-riscv64@0.28.2': - resolution: {integrity: sha512-GeXCej4IQtU1B+QlDV8W/RRvbzI3O/Stss+/bCXv4lZls5WGRtu2a+3JkA3i4qIUlMXpcHebWpF8AkJhATowuA==} - engines: {node: '>=18'} + resolution: + { + integrity: sha512-GeXCej4IQtU1B+QlDV8W/RRvbzI3O/Stss+/bCXv4lZls5WGRtu2a+3JkA3i4qIUlMXpcHebWpF8AkJhATowuA==, + } + engines: { node: '>=18' } cpu: [riscv64] os: [linux] '@esbuild/linux-s390x@0.28.2': - resolution: {integrity: sha512-3H1weTYZPxt/WOhByszQZybS9w5lKzUn1FDMsgEChbHWQwHYQQRfBxgCcZvPhjHfKyJjIievvMmEUawJrdY9Dg==} - engines: {node: '>=18'} + resolution: + { + integrity: sha512-3H1weTYZPxt/WOhByszQZybS9w5lKzUn1FDMsgEChbHWQwHYQQRfBxgCcZvPhjHfKyJjIievvMmEUawJrdY9Dg==, + } + engines: { node: '>=18' } cpu: [s390x] os: [linux] '@esbuild/linux-x64@0.28.2': - resolution: {integrity: sha512-4xTZr1FUmSoQW4XIWmit3tzQrUTZM+N3P0XV8xROKYF50XfI7xeO90+1bZvNwxIufQ9hDQVRJH5YhgPVF8A/HQ==} - engines: {node: '>=18'} + resolution: + { + integrity: sha512-4xTZr1FUmSoQW4XIWmit3tzQrUTZM+N3P0XV8xROKYF50XfI7xeO90+1bZvNwxIufQ9hDQVRJH5YhgPVF8A/HQ==, + } + engines: { node: '>=18' } cpu: [x64] os: [linux] '@esbuild/netbsd-arm64@0.28.2': - resolution: {integrity: sha512-sSATRjPeDBg3pdgHoQfoYBob11Kk1FGa9lui5RIHZCoCkJa9QKlvl3/vKz2usCmYYjs7ymJR/2Nnsqe+Hjt5nw==} - engines: {node: '>=18'} + resolution: + { + integrity: sha512-sSATRjPeDBg3pdgHoQfoYBob11Kk1FGa9lui5RIHZCoCkJa9QKlvl3/vKz2usCmYYjs7ymJR/2Nnsqe+Hjt5nw==, + } + engines: { node: '>=18' } cpu: [arm64] os: [netbsd] '@esbuild/netbsd-x64@0.28.2': - resolution: {integrity: sha512-lqnzCV+mM0gIADaKihiCg6ifgfU2L3h5E33rNQBN1Y4MaVGnzryzmvvf7UHxprpQdE8hpqLolJ9Rl+SkIRDpyw==} - engines: {node: '>=18'} + resolution: + { + integrity: sha512-lqnzCV+mM0gIADaKihiCg6ifgfU2L3h5E33rNQBN1Y4MaVGnzryzmvvf7UHxprpQdE8hpqLolJ9Rl+SkIRDpyw==, + } + engines: { node: '>=18' } cpu: [x64] os: [netbsd] '@esbuild/openbsd-arm64@0.28.2': - resolution: {integrity: sha512-AL2qJILH7lNjrDmCQDvdxMfAUIv8KMNZOvrwAQ8i8//ntL9FflhOyMJ8OZSMBb8/AWXe3/5v5S20y3zCoZWKoQ==} - engines: {node: '>=18'} + resolution: + { + integrity: sha512-AL2qJILH7lNjrDmCQDvdxMfAUIv8KMNZOvrwAQ8i8//ntL9FflhOyMJ8OZSMBb8/AWXe3/5v5S20y3zCoZWKoQ==, + } + engines: { node: '>=18' } cpu: [arm64] os: [openbsd] '@esbuild/openbsd-x64@0.28.2': - resolution: {integrity: sha512-QtiuPytchRyC4rwUKhexJdQKvDuZ6hWloi3igqPQNUJCS1/v9EiO3UTOXR6A3FoMo4fnAKbWJdqaIwhOzh8qEw==} - engines: {node: '>=18'} + resolution: + { + integrity: sha512-QtiuPytchRyC4rwUKhexJdQKvDuZ6hWloi3igqPQNUJCS1/v9EiO3UTOXR6A3FoMo4fnAKbWJdqaIwhOzh8qEw==, + } + engines: { node: '>=18' } cpu: [x64] os: [openbsd] '@esbuild/openharmony-arm64@0.28.2': - resolution: {integrity: sha512-WkhYDmpTjLvGlScA1rwjRUmhl4k8oXR3cIbtqWmELgU/dFeHHlEllxDvdWcNJV9rbzCexB5vz8gtNewWLgCT7Q==} - engines: {node: '>=18'} + resolution: + { + integrity: sha512-WkhYDmpTjLvGlScA1rwjRUmhl4k8oXR3cIbtqWmELgU/dFeHHlEllxDvdWcNJV9rbzCexB5vz8gtNewWLgCT7Q==, + } + engines: { node: '>=18' } cpu: [arm64] os: [openharmony] '@esbuild/sunos-x64@0.28.2': - resolution: {integrity: sha512-GPMSkTOtMnv2U2F8gxe4Io6qmVs+YKyp832Etqqxr0hFngmXQ3rzwytelm3GIn7T4VviRUlf3sOgBOiTdvaf7g==} - engines: {node: '>=18'} + resolution: + { + integrity: sha512-GPMSkTOtMnv2U2F8gxe4Io6qmVs+YKyp832Etqqxr0hFngmXQ3rzwytelm3GIn7T4VviRUlf3sOgBOiTdvaf7g==, + } + engines: { node: '>=18' } cpu: [x64] os: [sunos] '@esbuild/win32-arm64@0.28.2': - resolution: {integrity: sha512-PIhhEkE9uPBleRBrQEJpUn7MBnibZzbGzYWPmY3x+YoVg/95zbjB4CxPPOQ8l5tYYM4mMaCthF8/1DIfBQQyWQ==} - engines: {node: '>=18'} + resolution: + { + integrity: sha512-PIhhEkE9uPBleRBrQEJpUn7MBnibZzbGzYWPmY3x+YoVg/95zbjB4CxPPOQ8l5tYYM4mMaCthF8/1DIfBQQyWQ==, + } + engines: { node: '>=18' } cpu: [arm64] os: [win32] '@esbuild/win32-ia32@0.28.2': - resolution: {integrity: sha512-YmJbfTlvU7Sdn9BB+4PRES4oB6pxgS37MAONj+hBr/cpXS1aBPKXxNnDbu+QCWPj0o9dgyxeq79g6c5P8KeuYA==} - engines: {node: '>=18'} + resolution: + { + integrity: sha512-YmJbfTlvU7Sdn9BB+4PRES4oB6pxgS37MAONj+hBr/cpXS1aBPKXxNnDbu+QCWPj0o9dgyxeq79g6c5P8KeuYA==, + } + engines: { node: '>=18' } cpu: [ia32] os: [win32] '@esbuild/win32-x64@0.28.2': - resolution: {integrity: sha512-5ebpxr3nWMzrL/rnUI755Jkuee0bHL/Gq0WTF9lvcpv73wAp5eu8MfBUgWK9bhWvZjj7yX8etf/8tI8Ney695g==} - engines: {node: '>=18'} + resolution: + { + integrity: sha512-5ebpxr3nWMzrL/rnUI755Jkuee0bHL/Gq0WTF9lvcpv73wAp5eu8MfBUgWK9bhWvZjj7yX8etf/8tI8Ney695g==, + } + engines: { node: '>=18' } cpu: [x64] os: [win32] '@exodus/bytes@1.15.1': - resolution: {integrity: sha512-S6mL0yNB/Abt9Ei4tq8gDhcczc4S3+vQ4ra7vxnAf+YHC02srtqxKKZghx2Dq6p0e66THKwR6r8N6P95wEty7Q==} - engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} + resolution: + { + integrity: sha512-S6mL0yNB/Abt9Ei4tq8gDhcczc4S3+vQ4ra7vxnAf+YHC02srtqxKKZghx2Dq6p0e66THKwR6r8N6P95wEty7Q==, + } + engines: { node: ^20.19.0 || ^22.12.0 || >=24.0.0 } peerDependencies: '@noble/hashes': ^1.8.0 || ^2.0.0 peerDependenciesMeta: @@ -613,75 +849,120 @@ packages: optional: true '@firebase/ai@2.15.0': - resolution: {integrity: sha512-Aj7TbFdAIWZdkX8JfdDStERpR35g6WNs+7XhNPtFLFOizUotj6k4N/D8HJkR45165HhnMJBe4hjOeeaxnnn56Q==} - engines: {node: '>=20.0.0'} + resolution: + { + integrity: sha512-Aj7TbFdAIWZdkX8JfdDStERpR35g6WNs+7XhNPtFLFOizUotj6k4N/D8HJkR45165HhnMJBe4hjOeeaxnnn56Q==, + } + engines: { node: '>=20.0.0' } peerDependencies: '@firebase/app': 0.x '@firebase/app-types': 0.x '@firebase/analytics-compat@0.2.30': - resolution: {integrity: sha512-uVZEKlLaW4AHAhv8zoN9cTmveX6v86AVqcJ4LCaRCMTChTH8/NsjbisTq1lpBfWCLWS1spqwSHB4vol/YSCdMA==} + resolution: + { + integrity: sha512-uVZEKlLaW4AHAhv8zoN9cTmveX6v86AVqcJ4LCaRCMTChTH8/NsjbisTq1lpBfWCLWS1spqwSHB4vol/YSCdMA==, + } peerDependencies: '@firebase/app': 0.x '@firebase/app-compat': 0.x '@firebase/analytics-types@0.8.5': - resolution: {integrity: sha512-kdnooE7Bis2jEnsqcerRwn/UQpH5D3uvHpku7OdUM9TJN3omlu6iYtbyAQ6XkAJRgJtO0aNf9OOkW8J6D59oCA==} + resolution: + { + integrity: sha512-kdnooE7Bis2jEnsqcerRwn/UQpH5D3uvHpku7OdUM9TJN3omlu6iYtbyAQ6XkAJRgJtO0aNf9OOkW8J6D59oCA==, + } '@firebase/analytics@0.10.24': - resolution: {integrity: sha512-OfIAcIIwoqXjBzS+DnUQpOZ7i3ePaNFg83KPpDWjBZUKJmqMwzzAtLJqmKZOkQnW8im6vFR6f2I3M/9hxVd+QA==} + resolution: + { + integrity: sha512-OfIAcIIwoqXjBzS+DnUQpOZ7i3ePaNFg83KPpDWjBZUKJmqMwzzAtLJqmKZOkQnW8im6vFR6f2I3M/9hxVd+QA==, + } peerDependencies: '@firebase/app': 0.x '@firebase/app-check-compat@0.4.7': - resolution: {integrity: sha512-fBb/xSMyIKv1nDmccfzz3IAGBzx/cQOxmZai66rJzifb1hMMkztf824Xx7LhpTUe7HNUbXwY90IvJ66fi8q6yg==} - engines: {node: '>=20.0.0'} + resolution: + { + integrity: sha512-fBb/xSMyIKv1nDmccfzz3IAGBzx/cQOxmZai66rJzifb1hMMkztf824Xx7LhpTUe7HNUbXwY90IvJ66fi8q6yg==, + } + engines: { node: '>=20.0.0' } peerDependencies: '@firebase/app': 0.x '@firebase/app-compat': 0.x '@firebase/app-check-interop-types@0.3.5': - resolution: {integrity: sha512-qId34pVZ2CXTmtu4ofW5leiI93DvnOvIcr/5GT7MZPw5WXAi6nZoU+g9cNRgl7K90UJSbK0cXxten4meYMPyZg==} + resolution: + { + integrity: sha512-qId34pVZ2CXTmtu4ofW5leiI93DvnOvIcr/5GT7MZPw5WXAi6nZoU+g9cNRgl7K90UJSbK0cXxten4meYMPyZg==, + } '@firebase/app-check-types@0.5.5': - resolution: {integrity: sha512-+DF4gzFrlwGFyky38o4T/YN/r51l70yCtJ2HTkwmRj8FCMwPjm4hLH4fQbj6BUvzkiFqliBkitFsRpf1/YZkWA==} + resolution: + { + integrity: sha512-+DF4gzFrlwGFyky38o4T/YN/r51l70yCtJ2HTkwmRj8FCMwPjm4hLH4fQbj6BUvzkiFqliBkitFsRpf1/YZkWA==, + } '@firebase/app-check@0.13.1': - resolution: {integrity: sha512-l8y3dmnhodXks/APAwx4tWqRl3tk8b9874KF1FJyKg1DIU+kMD0l52iz7S9/MW+eK8k6cjJg0G0iJ5KQAsQpow==} - engines: {node: '>=20.0.0'} + resolution: + { + integrity: sha512-l8y3dmnhodXks/APAwx4tWqRl3tk8b9874KF1FJyKg1DIU+kMD0l52iz7S9/MW+eK8k6cjJg0G0iJ5KQAsQpow==, + } + engines: { node: '>=20.0.0' } peerDependencies: '@firebase/app': 0.x '@firebase/app-compat@0.5.17': - resolution: {integrity: sha512-5GdJWobqs6jbNYOnaQiSa4Ng8gnFerhqr7nY+v5jlnT7o9QbEeIZRLPQpnLe0TxYSWfRV2lAMbyR0kbXeIos9Q==} - engines: {node: '>=20.0.0'} + resolution: + { + integrity: sha512-5GdJWobqs6jbNYOnaQiSa4Ng8gnFerhqr7nY+v5jlnT7o9QbEeIZRLPQpnLe0TxYSWfRV2lAMbyR0kbXeIos9Q==, + } + engines: { node: '>=20.0.0' } '@firebase/app-types@0.9.6': - resolution: {integrity: sha512-yPLahy7Esfu2w/yme3msVK4xTkDXQqq6szfQn8yVOQpCKiT5GVFjqNpLbuz6NkX0WuTwUidkmPewW3r4xkvpeg==} + resolution: + { + integrity: sha512-yPLahy7Esfu2w/yme3msVK4xTkDXQqq6szfQn8yVOQpCKiT5GVFjqNpLbuz6NkX0WuTwUidkmPewW3r4xkvpeg==, + } '@firebase/app@0.16.1': - resolution: {integrity: sha512-tjUEorFyKrurH7PbLWv9zDHRuU4mLefgD/yY38D584ziorIRlQz/PVjx4c/SCGyCuUlmyzt72mK+Lh+COAywNQ==} - engines: {node: '>=20.0.0'} + resolution: + { + integrity: sha512-tjUEorFyKrurH7PbLWv9zDHRuU4mLefgD/yY38D584ziorIRlQz/PVjx4c/SCGyCuUlmyzt72mK+Lh+COAywNQ==, + } + engines: { node: '>=20.0.0' } '@firebase/auth-compat@0.6.10': - resolution: {integrity: sha512-Bvklg2nL7BrBFCAqdsleW8fse9Jh0fARqJPlJmA40uermfWx1bJiO7dnKyZN3J39d2TFVd4VJXtg3i6Wg92/YQ==} - engines: {node: '>=20.0.0'} + resolution: + { + integrity: sha512-Bvklg2nL7BrBFCAqdsleW8fse9Jh0fARqJPlJmA40uermfWx1bJiO7dnKyZN3J39d2TFVd4VJXtg3i6Wg92/YQ==, + } + engines: { node: '>=20.0.0' } peerDependencies: '@firebase/app': 0.x '@firebase/app-compat': 0.x '@firebase/auth-interop-types@0.2.6': - resolution: {integrity: sha512-FgwZqDrBqgK0BHI70QTv1v/5wmuDF9f8fsJFvKSxoRlK2PPfSQtZAk2jhXu4pe9BZzFi/e4UYusU/bEQHdf6Qg==} + resolution: + { + integrity: sha512-FgwZqDrBqgK0BHI70QTv1v/5wmuDF9f8fsJFvKSxoRlK2PPfSQtZAk2jhXu4pe9BZzFi/e4UYusU/bEQHdf6Qg==, + } '@firebase/auth-types@0.13.2': - resolution: {integrity: sha512-OU+miuoSxIWYN7GT291d2ylVJBL3k/OePo7/JDSoQtkFQoEiGBc4AHuMjj/seqFIsHrqnjrAfRCk4pfufoJolQ==} + resolution: + { + integrity: sha512-OU+miuoSxIWYN7GT291d2ylVJBL3k/OePo7/JDSoQtkFQoEiGBc4AHuMjj/seqFIsHrqnjrAfRCk4pfufoJolQ==, + } peerDependencies: '@firebase/app-types': 0.x '@firebase/util': 1.x '@firebase/auth@1.13.5': - resolution: {integrity: sha512-1AXoBJqBVD8WL8FZYo3S2GmJF9YUoom6Y6ngMxOSkzzhW5sT83pLchb6TGFgxes91dfXx8s/VYc5VrLDNqpLog==} - engines: {node: '>=20.0.0'} + resolution: + { + integrity: sha512-1AXoBJqBVD8WL8FZYo3S2GmJF9YUoom6Y6ngMxOSkzzhW5sT83pLchb6TGFgxes91dfXx8s/VYc5VrLDNqpLog==, + } + engines: { node: '>=20.0.0' } peerDependencies: '@firebase/app': 0.x '@react-native-async-storage/async-storage': ^2.2.0 || ^3.0.0 @@ -690,17 +971,26 @@ packages: optional: true '@firebase/component@0.7.5': - resolution: {integrity: sha512-vuFDcL91Q+2ZuBJkyOh86T4q0B4ffNTDjc/A38tybO56odQABxRTFLTIowCWqAKeIcgo37GowWMVgFF73gD8Qw==} - engines: {node: '>=20.0.0'} + resolution: + { + integrity: sha512-vuFDcL91Q+2ZuBJkyOh86T4q0B4ffNTDjc/A38tybO56odQABxRTFLTIowCWqAKeIcgo37GowWMVgFF73gD8Qw==, + } + engines: { node: '>=20.0.0' } '@firebase/data-connect@0.7.4': - resolution: {integrity: sha512-su1aGWlzhxb+xtggCUSsufJn1FDa06SBDK71y+fpQ+g2zMhMExik6FUv/odkKzOF/xfWVjabCbWBcjJY3MceDA==} + resolution: + { + integrity: sha512-su1aGWlzhxb+xtggCUSsufJn1FDa06SBDK71y+fpQ+g2zMhMExik6FUv/odkKzOF/xfWVjabCbWBcjJY3MceDA==, + } peerDependencies: '@firebase/app': 0.x '@firebase/database-compat@2.1.7': - resolution: {integrity: sha512-lBq9sJm8MnJINKJnkAKSOj2MbC66xGoSVCcGkPtstl+lkoKEBtD46qHLbuDgW/WbLPoKVm17RIN8BxHj+Z2F2w==} - engines: {node: '>=20.0.0'} + resolution: + { + integrity: sha512-lBq9sJm8MnJINKJnkAKSOj2MbC66xGoSVCcGkPtstl+lkoKEBtD46qHLbuDgW/WbLPoKVm17RIN8BxHj+Z2F2w==, + } + engines: { node: '>=20.0.0' } peerDependencies: '@firebase/app': 0.x '@firebase/app-compat': 0.x @@ -711,152 +1001,245 @@ packages: optional: true '@firebase/database-types@1.0.22': - resolution: {integrity: sha512-YAZNXsjY9EQQ+pKw/3ax8n5FgolHC7Qew7EY5RceYdl0R2ZP+kCv1O0DkKlcceue8uQCOreHCNN7PWVFpY1Nug==} + resolution: + { + integrity: sha512-YAZNXsjY9EQQ+pKw/3ax8n5FgolHC7Qew7EY5RceYdl0R2ZP+kCv1O0DkKlcceue8uQCOreHCNN7PWVFpY1Nug==, + } '@firebase/database@1.1.5': - resolution: {integrity: sha512-/JGpvszLoNXNgzilRXocigGfFF4hbcPA9wN1i1kjJx6oKkXgkHteZYl3lQs1lJX3ETDf6bv7zI15lPMVUp4sAQ==} - engines: {node: '>=20.0.0'} + resolution: + { + integrity: sha512-/JGpvszLoNXNgzilRXocigGfFF4hbcPA9wN1i1kjJx6oKkXgkHteZYl3lQs1lJX3ETDf6bv7zI15lPMVUp4sAQ==, + } + engines: { node: '>=20.0.0' } '@firebase/firestore-compat@0.4.13': - resolution: {integrity: sha512-l9dCewxMzzLOIhcwTjERCKxrWOn1kZ9JvwOQq9zZNq4I/Nbvb/B9njT230V61uvQ9qZ3/qpUG758D8DDTEFXnw==} - engines: {node: '>=20.0.0'} + resolution: + { + integrity: sha512-l9dCewxMzzLOIhcwTjERCKxrWOn1kZ9JvwOQq9zZNq4I/Nbvb/B9njT230V61uvQ9qZ3/qpUG758D8DDTEFXnw==, + } + engines: { node: '>=20.0.0' } peerDependencies: '@firebase/app': 0.x '@firebase/app-compat': 0.x '@firebase/firestore-types@3.0.5': - resolution: {integrity: sha512-dbdMAQkMd5dwWc48eupz/Y6/E9ruat3+gY5lhVKscvvT/HnDBEEMzJW38zdKhgdnglZHGk2vUsJMOHAphMHGMA==} + resolution: + { + integrity: sha512-dbdMAQkMd5dwWc48eupz/Y6/E9ruat3+gY5lhVKscvvT/HnDBEEMzJW38zdKhgdnglZHGk2vUsJMOHAphMHGMA==, + } peerDependencies: '@firebase/app-types': 0.x '@firebase/util': 1.x '@firebase/firestore@4.17.1': - resolution: {integrity: sha512-8lqPNf2w10CtYG+tayVjZO1pSyQpnhztQRudeD109VtXDzNbASTaYdO43sj5PMsDcWq0aOYY3RmJOUlXu9++jw==} - engines: {node: '>=20.0.0'} + resolution: + { + integrity: sha512-8lqPNf2w10CtYG+tayVjZO1pSyQpnhztQRudeD109VtXDzNbASTaYdO43sj5PMsDcWq0aOYY3RmJOUlXu9++jw==, + } + engines: { node: '>=20.0.0' } peerDependencies: '@firebase/app': 0.x '@firebase/functions-compat@0.5.0': - resolution: {integrity: sha512-T3BDIToESZUHt7438wyKYMkRjKg3m1xAIux5fVpNvTRQlDOFLukWniQWBDhJ2znpU9kxMTR6+e31TVo8P15PZw==} - engines: {node: '>=20.0.0'} + resolution: + { + integrity: sha512-T3BDIToESZUHt7438wyKYMkRjKg3m1xAIux5fVpNvTRQlDOFLukWniQWBDhJ2znpU9kxMTR6+e31TVo8P15PZw==, + } + engines: { node: '>=20.0.0' } peerDependencies: '@firebase/app': 0.x '@firebase/app-compat': 0.x '@firebase/functions-types@0.6.5': - resolution: {integrity: sha512-Zc0pURjthHXzSj54ZivCkzKDSV1r/wIpnmHdhq82q2yFFPVoncG/ZJjnVMiANvQfeww/QnElhzODpfwUucVwdA==} + resolution: + { + integrity: sha512-Zc0pURjthHXzSj54ZivCkzKDSV1r/wIpnmHdhq82q2yFFPVoncG/ZJjnVMiANvQfeww/QnElhzODpfwUucVwdA==, + } '@firebase/functions@0.14.0': - resolution: {integrity: sha512-DhuYFr0eMhp+s/PNEk6SiMsYkc00+XVOUeKbrl8MOzQNZI3SKQpqoDR1d+keNDAutwmHRWTUAdXBoVPD+xxVUw==} - engines: {node: '>=20.0.0'} + resolution: + { + integrity: sha512-DhuYFr0eMhp+s/PNEk6SiMsYkc00+XVOUeKbrl8MOzQNZI3SKQpqoDR1d+keNDAutwmHRWTUAdXBoVPD+xxVUw==, + } + engines: { node: '>=20.0.0' } peerDependencies: '@firebase/app': 0.x '@firebase/installations-compat@0.2.24': - resolution: {integrity: sha512-8M5nlcWwYt881x83COP2odq5vgf+NgwJh+RMd4LRSv8JI1pxwDfgrDJOERrjTgfC9J5Z0vnQX4b1pdN914e0Zw==} + resolution: + { + integrity: sha512-8M5nlcWwYt881x83COP2odq5vgf+NgwJh+RMd4LRSv8JI1pxwDfgrDJOERrjTgfC9J5Z0vnQX4b1pdN914e0Zw==, + } peerDependencies: '@firebase/app': 0.x '@firebase/app-compat': 0.x '@firebase/installations-types@0.5.5': - resolution: {integrity: sha512-e9UYcju3puDl1vdrcKIi5dExzHLameOT/Tc61Q48PYwxtsM1NzZh/ikGbdBQTsbRgg0EMZqdPr0/m5ODUBobrg==} + resolution: + { + integrity: sha512-e9UYcju3puDl1vdrcKIi5dExzHLameOT/Tc61Q48PYwxtsM1NzZh/ikGbdBQTsbRgg0EMZqdPr0/m5ODUBobrg==, + } peerDependencies: '@firebase/app-types': 0.x '@firebase/installations@0.6.24': - resolution: {integrity: sha512-Ui52ey8wHoWqkBbXRKJEKYWylI0JZogZmoLS+o8Anh1bxWtK27ZYjLla1UJAAdC5DCKLJ2qAp0VpJOjg8VOX1g==} + resolution: + { + integrity: sha512-Ui52ey8wHoWqkBbXRKJEKYWylI0JZogZmoLS+o8Anh1bxWtK27ZYjLla1UJAAdC5DCKLJ2qAp0VpJOjg8VOX1g==, + } peerDependencies: '@firebase/app': 0.x '@firebase/logger@0.5.2': - resolution: {integrity: sha512-J2VO4NFTc0xQFrxV1B/lm5balicm9cwuX2acR9Yn41fN8KgUeQFo+VJV222IqW2FPSXKDu9uo5WdQFWf9TPbYg==} - engines: {node: '>=20.0.0'} + resolution: + { + integrity: sha512-J2VO4NFTc0xQFrxV1B/lm5balicm9cwuX2acR9Yn41fN8KgUeQFo+VJV222IqW2FPSXKDu9uo5WdQFWf9TPbYg==, + } + engines: { node: '>=20.0.0' } '@firebase/messaging-compat@0.2.29': - resolution: {integrity: sha512-8Twe4CeYvAx8AzjBxyyQFKzinaMGGt13hPQBqaARQ2QZjagrEaqSVBe+Zy6F4L/vT8DV168yRgRu7W/RK7p+4w==} + resolution: + { + integrity: sha512-8Twe4CeYvAx8AzjBxyyQFKzinaMGGt13hPQBqaARQ2QZjagrEaqSVBe+Zy6F4L/vT8DV168yRgRu7W/RK7p+4w==, + } peerDependencies: '@firebase/app': 0.x '@firebase/app-compat': 0.x '@firebase/messaging-interop-types@0.2.6': - resolution: {integrity: sha512-MVzvkKe2V4H2dHu5oOxRfeKQcfTwWmCgnzsC4V1q3ixun5iiL8riCZ2qI35rDhCL4glPGiu0jHxDbpVNuTKfow==} + resolution: + { + integrity: sha512-MVzvkKe2V4H2dHu5oOxRfeKQcfTwWmCgnzsC4V1q3ixun5iiL8riCZ2qI35rDhCL4glPGiu0jHxDbpVNuTKfow==, + } '@firebase/messaging@0.13.2': - resolution: {integrity: sha512-KcZoqUu2ih4sLH91dW9tmyHjCR0IQyNzSLZunX5uq7cLeImXb4uO2I0wq5FACduO2I+FoTeWa2BtUv7Jpowqdw==} + resolution: + { + integrity: sha512-KcZoqUu2ih4sLH91dW9tmyHjCR0IQyNzSLZunX5uq7cLeImXb4uO2I0wq5FACduO2I+FoTeWa2BtUv7Jpowqdw==, + } peerDependencies: '@firebase/app': 0.x '@firebase/performance-compat@0.2.27': - resolution: {integrity: sha512-O/ozTf/EbChN94Pk7bd1eUC0PAC36726AwsaiJyC0bZzSBWfLGSWASGPH5pebUWXQPJtGxIJYRkkbX5l0Qa+Hw==} + resolution: + { + integrity: sha512-O/ozTf/EbChN94Pk7bd1eUC0PAC36726AwsaiJyC0bZzSBWfLGSWASGPH5pebUWXQPJtGxIJYRkkbX5l0Qa+Hw==, + } peerDependencies: '@firebase/app': 0.x '@firebase/app-compat': 0.x '@firebase/performance-types@0.2.5': - resolution: {integrity: sha512-PRzOgB+/M+6AKlEkY8a9xy5Ff5SfZPIh4iShXhn2WAcL/euSbK7bdLqWysHduunNJhGFsXa22Ag3ixqL6rQtYg==} + resolution: + { + integrity: sha512-PRzOgB+/M+6AKlEkY8a9xy5Ff5SfZPIh4iShXhn2WAcL/euSbK7bdLqWysHduunNJhGFsXa22Ag3ixqL6rQtYg==, + } '@firebase/performance@0.7.14': - resolution: {integrity: sha512-9PH1XEZVHErxGdbXluvGz4Uyjw4W955H8v7Mv9rHIybRrg5F2Ac2tvf5+mSf5EtnxWNmxrD2WLnvMFaZP4sMrQ==} + resolution: + { + integrity: sha512-9PH1XEZVHErxGdbXluvGz4Uyjw4W955H8v7Mv9rHIybRrg5F2Ac2tvf5+mSf5EtnxWNmxrD2WLnvMFaZP4sMrQ==, + } peerDependencies: '@firebase/app': 0.x '@firebase/remote-config-compat@0.2.29': - resolution: {integrity: sha512-mY7JtTISK6F4g4T0x3kVepr5cp0NXL7f5yjIUXXGaTrg4qUlFBWRbENaHaqZ78dwmNcLm24h/yPsOVRlDXEXhA==} + resolution: + { + integrity: sha512-mY7JtTISK6F4g4T0x3kVepr5cp0NXL7f5yjIUXXGaTrg4qUlFBWRbENaHaqZ78dwmNcLm24h/yPsOVRlDXEXhA==, + } peerDependencies: '@firebase/app': 0.x '@firebase/app-compat': 0.x '@firebase/remote-config-types@0.5.2': - resolution: {integrity: sha512-i8k1omVfoAnaT1ZPv2FFjxMZrATYsO/GnFgHEj5+7fAJLzi8wLnGGv1WK06oEAD6ltrWXSO1HzeNyajn/NjMWw==} + resolution: + { + integrity: sha512-i8k1omVfoAnaT1ZPv2FFjxMZrATYsO/GnFgHEj5+7fAJLzi8wLnGGv1WK06oEAD6ltrWXSO1HzeNyajn/NjMWw==, + } '@firebase/remote-config@0.9.2': - resolution: {integrity: sha512-Rii93DkXjM+RE/ytHdYa7EIiQLJbdBr+W8EEiqd/vbLCOC+1FdkDuRiumJBp6t3yewHGbdqbpjB3fk3z0cZ+8g==} + resolution: + { + integrity: sha512-Rii93DkXjM+RE/ytHdYa7EIiQLJbdBr+W8EEiqd/vbLCOC+1FdkDuRiumJBp6t3yewHGbdqbpjB3fk3z0cZ+8g==, + } peerDependencies: '@firebase/app': 0.x '@firebase/storage-compat@0.4.5': - resolution: {integrity: sha512-vO0tFPxXbKDKdlTu8tYT08S9t9ezUTJYEdLCULpWxWq2aq6zojwN1QmAB+1a50AI/g47GlWUJn1XPncm/PDZsw==} - engines: {node: '>=20.0.0'} + resolution: + { + integrity: sha512-vO0tFPxXbKDKdlTu8tYT08S9t9ezUTJYEdLCULpWxWq2aq6zojwN1QmAB+1a50AI/g47GlWUJn1XPncm/PDZsw==, + } + engines: { node: '>=20.0.0' } peerDependencies: '@firebase/app': 0.x '@firebase/app-compat': 0.x '@firebase/storage-types@0.8.5': - resolution: {integrity: sha512-GEDs5P+rNUfNcS+wxIdOLAHficife2YXtvTJnyi3ssrX11AAtBg+nDUdbYh8vuBzWehVQZPmztGUUnud4L+4yg==} + resolution: + { + integrity: sha512-GEDs5P+rNUfNcS+wxIdOLAHficife2YXtvTJnyi3ssrX11AAtBg+nDUdbYh8vuBzWehVQZPmztGUUnud4L+4yg==, + } peerDependencies: '@firebase/app-types': 0.x '@firebase/util': 1.x '@firebase/storage@0.14.5': - resolution: {integrity: sha512-r2tozN/BlEewLi70tJNUQPzWwbex9GM7NgXZsamHKQrjKEfcR0i4jGgHBKAKA4hbwiPE9eNMb3hcxWnDpeJZwg==} - engines: {node: '>=20.0.0'} + resolution: + { + integrity: sha512-r2tozN/BlEewLi70tJNUQPzWwbex9GM7NgXZsamHKQrjKEfcR0i4jGgHBKAKA4hbwiPE9eNMb3hcxWnDpeJZwg==, + } + engines: { node: '>=20.0.0' } peerDependencies: '@firebase/app': 0.x '@firebase/util@1.15.3': - resolution: {integrity: sha512-c/z/gaIlaaLZEuGbE6sLUuJ61tskg1JghvhcNQzW948ASBinbVBBRnZTC4b4yt4LaEtJQkYlyzqLHcutFwEIvA==} - engines: {node: '>=20.0.0'} + resolution: + { + integrity: sha512-c/z/gaIlaaLZEuGbE6sLUuJ61tskg1JghvhcNQzW948ASBinbVBBRnZTC4b4yt4LaEtJQkYlyzqLHcutFwEIvA==, + } + engines: { node: '>=20.0.0' } '@firebase/webchannel-wrapper@1.0.7': - resolution: {integrity: sha512-phBFwieDLvkZGYN9CE9ZFNEIoBVksprzsnCzQejCmCHtgwCXReeuRpoEGN9C4EbhONztv8NRV1tau6Rb9pONwQ==} + resolution: + { + integrity: sha512-phBFwieDLvkZGYN9CE9ZFNEIoBVksprzsnCzQejCmCHtgwCXReeuRpoEGN9C4EbhONztv8NRV1tau6Rb9pONwQ==, + } '@floating-ui/core@1.8.0': - resolution: {integrity: sha512-0CIZ5itps/8x7BG8dEIhs53BvCUH2PCoogtakwRTut+Arm58sJooJ0AuZhLw2HJYIR5cMLNPBSS728sPho2khQ==} + resolution: + { + integrity: sha512-0CIZ5itps/8x7BG8dEIhs53BvCUH2PCoogtakwRTut+Arm58sJooJ0AuZhLw2HJYIR5cMLNPBSS728sPho2khQ==, + } '@floating-ui/dom@1.8.0': - resolution: {integrity: sha512-yXSrzeHZBTZadLOlfyhCkJHNeLJnHRnRInwdZ40L7ZiaAtrBwoYlsDrX3v5zB1Utk7CLfzcOVnVVWoXEky7Ceg==} + resolution: + { + integrity: sha512-yXSrzeHZBTZadLOlfyhCkJHNeLJnHRnRInwdZ40L7ZiaAtrBwoYlsDrX3v5zB1Utk7CLfzcOVnVVWoXEky7Ceg==, + } '@floating-ui/react-dom@2.1.9': - resolution: {integrity: sha512-JDjEFGCpImxDCA7JJKviA0M9+RtmJdj0m/NVU5IMgBK+AmZouAQQ7/+2GLH0GXXY0YMw9oXPB8hKdbPYg5QLYg==} + resolution: + { + integrity: sha512-JDjEFGCpImxDCA7JJKviA0M9+RtmJdj0m/NVU5IMgBK+AmZouAQQ7/+2GLH0GXXY0YMw9oXPB8hKdbPYg5QLYg==, + } peerDependencies: react: '>=16.8.0' react-dom: '>=16.8.0' '@floating-ui/utils@0.2.12': - resolution: {integrity: sha512-HpCo8tmWzLVad5s2d19EhAz5zqrrQ6s69qd6moPMQvkOuSwDT1YgRfWSVuc4ennqrgv3OHppiOGMQ7oC13yIww==} + resolution: + { + integrity: sha512-HpCo8tmWzLVad5s2d19EhAz5zqrrQ6s69qd6moPMQvkOuSwDT1YgRfWSVuc4ennqrgv3OHppiOGMQ7oC13yIww==, + } '@fuma-translate/react@1.0.2': - resolution: {integrity: sha512-uOiOtBx3nRXR8Nu1GzBf1tApgF1FErDBTHxRIAQeyQdyOoZbrNRN6H4kDCWObY4qyGeGbHydG0DHzgeUgFDMIw==} + resolution: + { + integrity: sha512-uOiOtBx3nRXR8Nu1GzBf1tApgF1FErDBTHxRIAQeyQdyOoZbrNRN6H4kDCWObY4qyGeGbHydG0DHzgeUgFDMIw==, + } peerDependencies: '@types/react': '*' react: ^19.2.0 @@ -866,7 +1249,10 @@ packages: optional: true '@fumadocs/base-ui@16.15.4': - resolution: {integrity: sha512-BcoUPoSbfX/uaUl2NgPn0IKOjqJCkwsWPP6z5uRlKSEdh4zbImwXf3k6+qJ3ULlVaFEY8hqfR2wn+Ya5vPKKEQ==} + resolution: + { + integrity: sha512-BcoUPoSbfX/uaUl2NgPn0IKOjqJCkwsWPP6z5uRlKSEdh4zbImwXf3k6+qJ3ULlVaFEY8hqfR2wn+Ya5vPKKEQ==, + } peerDependencies: '@types/mdx': '*' '@types/react': '*' @@ -886,7 +1272,10 @@ packages: optional: true '@fumadocs/tailwind@0.1.1': - resolution: {integrity: sha512-BnPe52UxSaG8yKlHMKBxXw8h6GpK5qO55ci6+Qd5JnquTvIw6SpfbC1P+qAi82PuPWv1KZAWY8bxRk4+x9ctXw==} + resolution: + { + integrity: sha512-BnPe52UxSaG8yKlHMKBxXw8h6GpK5qO55ci6+Qd5JnquTvIw6SpfbC1P+qAi82PuPWv1KZAWY8bxRk4+x9ctXw==, + } peerDependencies: tailwindcss: ^4.0.0 peerDependenciesMeta: @@ -894,513 +1283,813 @@ packages: optional: true '@fumari/image-size@0.1.0': - resolution: {integrity: sha512-x2o9u6P8uKUK15B8XgEoRhR3PgLoLSbQKK6FUCd14JzumEw+e8FXZPelij/dZ4VQVMp4r61VD3DcvSo3aEhLAA==} + resolution: + { + integrity: sha512-x2o9u6P8uKUK15B8XgEoRhR3PgLoLSbQKK6FUCd14JzumEw+e8FXZPelij/dZ4VQVMp4r61VD3DcvSo3aEhLAA==, + } '@grpc/grpc-js@1.9.16': - resolution: {integrity: sha512-wE4Ut/olIzfKqp631XrG+wbF0v1vWFN4YL9FyXC2LJiG33DsV7PLzURjrCvY/6je2ntdRkeLpPDluzSRGaVltQ==} - engines: {node: ^8.13.0 || >=10.10.0} + resolution: + { + integrity: sha512-wE4Ut/olIzfKqp631XrG+wbF0v1vWFN4YL9FyXC2LJiG33DsV7PLzURjrCvY/6je2ntdRkeLpPDluzSRGaVltQ==, + } + engines: { node: ^8.13.0 || >=10.10.0 } '@grpc/proto-loader@0.7.15': - resolution: {integrity: sha512-tMXdRCfYVixjuFK+Hk0Q1s38gV9zDiDJfWL3h1rv4Qc39oILCu1TRTDt7+fGUI8K4G1Fj125Hx/ru3azECWTyQ==} - engines: {node: '>=6'} + resolution: + { + integrity: sha512-tMXdRCfYVixjuFK+Hk0Q1s38gV9zDiDJfWL3h1rv4Qc39oILCu1TRTDt7+fGUI8K4G1Fj125Hx/ru3azECWTyQ==, + } + engines: { node: '>=6' } hasBin: true '@img/colour@1.1.0': - resolution: {integrity: sha512-Td76q7j57o/tLVdgS746cYARfSyxk8iEfRxewL9h4OMzYhbW4TAcppl0mT4eyqXddh6L/jwoM75mo7ixa/pCeQ==} - engines: {node: '>=18'} + resolution: + { + integrity: sha512-Td76q7j57o/tLVdgS746cYARfSyxk8iEfRxewL9h4OMzYhbW4TAcppl0mT4eyqXddh6L/jwoM75mo7ixa/pCeQ==, + } + engines: { node: '>=18' } '@img/sharp-darwin-arm64@0.35.4': - resolution: {integrity: sha512-Uhfl4V4lhP2nbUVF9+hyH1+luj86f1gUFeo8ALYxFoULoU+G87D43BfeMP8XHsk9boxAnCY/bf2EHwhA7MuGsA==} - engines: {node: '>=20.9.0'} + resolution: + { + integrity: sha512-Uhfl4V4lhP2nbUVF9+hyH1+luj86f1gUFeo8ALYxFoULoU+G87D43BfeMP8XHsk9boxAnCY/bf2EHwhA7MuGsA==, + } + engines: { node: '>=20.9.0' } cpu: [arm64] os: [darwin] '@img/sharp-darwin-x64@0.35.4': - resolution: {integrity: sha512-hWniXY3bG5qKpkKrAwPe4y+VTPmf086YQAnkxWh7uA1YrlRouWGa0M0Mxj3ZjnXFkv7/TD1bTy9lGUK26vRvWw==} - engines: {node: '>=20.9.0'} + resolution: + { + integrity: sha512-hWniXY3bG5qKpkKrAwPe4y+VTPmf086YQAnkxWh7uA1YrlRouWGa0M0Mxj3ZjnXFkv7/TD1bTy9lGUK26vRvWw==, + } + engines: { node: '>=20.9.0' } cpu: [x64] os: [darwin] '@img/sharp-freebsd-wasm32@0.35.4': - resolution: {integrity: sha512-lIsKw/BU+kjB4eZjxrYrZmwOJYi3Ajrv66iAlBmUPyKc3HpnloevB1g3wxGD9P/5BbQ1brBGl65VRRrCvQDEqA==} - engines: {node: '>=20.9.0'} + resolution: + { + integrity: sha512-lIsKw/BU+kjB4eZjxrYrZmwOJYi3Ajrv66iAlBmUPyKc3HpnloevB1g3wxGD9P/5BbQ1brBGl65VRRrCvQDEqA==, + } + engines: { node: '>=20.9.0' } os: [freebsd] '@img/sharp-libvips-darwin-arm64@1.3.3': - resolution: {integrity: sha512-suTBPTDGrI9WodccaDdwZItTSaBYASlBk1NSfElSHrUfzu3szG6lvIF58+WiFvnfzuK8ZBFS5zE00PxqxnRiPg==} + resolution: + { + integrity: sha512-suTBPTDGrI9WodccaDdwZItTSaBYASlBk1NSfElSHrUfzu3szG6lvIF58+WiFvnfzuK8ZBFS5zE00PxqxnRiPg==, + } cpu: [arm64] os: [darwin] '@img/sharp-libvips-darwin-x64@1.3.3': - resolution: {integrity: sha512-FVJZ5mITMobmXIz/hPDTw0EintTW5H3WfrxwLqEqjiIihlu+hVRyGrFQ60xl0Lxn7Bt3zdpevPaQi0HEzqz9fw==} + resolution: + { + integrity: sha512-FVJZ5mITMobmXIz/hPDTw0EintTW5H3WfrxwLqEqjiIihlu+hVRyGrFQ60xl0Lxn7Bt3zdpevPaQi0HEzqz9fw==, + } cpu: [x64] os: [darwin] '@img/sharp-libvips-linux-arm64@1.3.3': - resolution: {integrity: sha512-0DaL0A6Xu6sQSQFwe4iVCrKWU2cCTItnRsYsCdxAMm9NF6twAA9BKnoqy4hqz4+azQ0JHuA26qiUKsf1XJ/v5A==} + resolution: + { + integrity: sha512-0DaL0A6Xu6sQSQFwe4iVCrKWU2cCTItnRsYsCdxAMm9NF6twAA9BKnoqy4hqz4+azQ0JHuA26qiUKsf1XJ/v5A==, + } cpu: [arm64] os: [linux] libc: [glibc] '@img/sharp-libvips-linux-arm@1.3.3': - resolution: {integrity: sha512-3rbU4vqXXc3hY/OiXdl52xZvT0F1yEngWfvqudtPJg/KkyiaQw2DRsFrNzpmLvfavbwOq3qXn36GP8obHRULQA==} + resolution: + { + integrity: sha512-3rbU4vqXXc3hY/OiXdl52xZvT0F1yEngWfvqudtPJg/KkyiaQw2DRsFrNzpmLvfavbwOq3qXn36GP8obHRULQA==, + } cpu: [arm] os: [linux] libc: [glibc] '@img/sharp-libvips-linux-ppc64@1.3.3': - resolution: {integrity: sha512-cdn1OvUBwsXhbC0zSzJnNzf5MZ/mTrobawDvNXBTxe8VtqKAm0sRuEY2Evzovb/w9JMk4TvRxqt1mekSuJz64w==} + resolution: + { + integrity: sha512-cdn1OvUBwsXhbC0zSzJnNzf5MZ/mTrobawDvNXBTxe8VtqKAm0sRuEY2Evzovb/w9JMk4TvRxqt1mekSuJz64w==, + } cpu: [ppc64] os: [linux] libc: [glibc] '@img/sharp-libvips-linux-riscv64@1.3.3': - resolution: {integrity: sha512-HjPVx7yKz+0lqdhDlTw1tt90wamBoxhiXpvl1XZpJLiHH4RCJ5yDTqH+VlYPv2fwFs89JFw4c1IexYOcQUi4IQ==} + resolution: + { + integrity: sha512-HjPVx7yKz+0lqdhDlTw1tt90wamBoxhiXpvl1XZpJLiHH4RCJ5yDTqH+VlYPv2fwFs89JFw4c1IexYOcQUi4IQ==, + } cpu: [riscv64] os: [linux] libc: [glibc] '@img/sharp-libvips-linux-s390x@1.3.3': - resolution: {integrity: sha512-neWLh+3yCNThxnfy3c4BbVBeGgt9aftno+XbT56iK28RgeDs3UOFWviLWlUu0bArYVYJaFDK+RRohbicUNCm8Q==} + resolution: + { + integrity: sha512-neWLh+3yCNThxnfy3c4BbVBeGgt9aftno+XbT56iK28RgeDs3UOFWviLWlUu0bArYVYJaFDK+RRohbicUNCm8Q==, + } cpu: [s390x] os: [linux] libc: [glibc] '@img/sharp-libvips-linux-x64@1.3.3': - resolution: {integrity: sha512-4vKmvAst9nrowcqquKFAyZJUDolUaIp8uRiN0mWFguJ1IplC9/pitXtlnnlU4aa/eJw3J7i67V+pwUL+wZGdsA==} + resolution: + { + integrity: sha512-4vKmvAst9nrowcqquKFAyZJUDolUaIp8uRiN0mWFguJ1IplC9/pitXtlnnlU4aa/eJw3J7i67V+pwUL+wZGdsA==, + } cpu: [x64] os: [linux] libc: [glibc] '@img/sharp-libvips-linuxmusl-arm64@1.3.3': - resolution: {integrity: sha512-Y9kQaLMuNoB0bPYOOdcZMaseNrFpPodIWWMrx+CZyydf2xn68j9WYc6sWWRrDwNkzCQjKYfc68L7jKjGlHMibw==} + resolution: + { + integrity: sha512-Y9kQaLMuNoB0bPYOOdcZMaseNrFpPodIWWMrx+CZyydf2xn68j9WYc6sWWRrDwNkzCQjKYfc68L7jKjGlHMibw==, + } cpu: [arm64] os: [linux] libc: [musl] '@img/sharp-libvips-linuxmusl-x64@1.3.3': - resolution: {integrity: sha512-fj8Mv0HHfD1Rr+4I68+3agJynxDWtBFgicTbSOb9Bke6pIwzGcJ+RX/yHjmiEGFMCavY/dxvem7MyNaJF+wDiw==} + resolution: + { + integrity: sha512-fj8Mv0HHfD1Rr+4I68+3agJynxDWtBFgicTbSOb9Bke6pIwzGcJ+RX/yHjmiEGFMCavY/dxvem7MyNaJF+wDiw==, + } cpu: [x64] os: [linux] libc: [musl] '@img/sharp-linux-arm64@0.35.4': - resolution: {integrity: sha512-De4jpEnAU8Hd5oT0j1G3uL4ZvTuipVMn7YC6vPaJhy6/7EwEae0SVAoBrUMYQbkLGDm85taVWwuPc1a44LTzCQ==} - engines: {node: '>=20.9.0'} + resolution: + { + integrity: sha512-De4jpEnAU8Hd5oT0j1G3uL4ZvTuipVMn7YC6vPaJhy6/7EwEae0SVAoBrUMYQbkLGDm85taVWwuPc1a44LTzCQ==, + } + engines: { node: '>=20.9.0' } cpu: [arm64] os: [linux] libc: [glibc] '@img/sharp-linux-arm@0.35.4': - resolution: {integrity: sha512-7OAS8gI0EReKGVN2HssHlM6umJgxF5VI3xN0p9FA91p/YO+ou5hiNghLdZ5BEHztwaaK5+bLKRf8x/o2L2nk9A==} - engines: {node: '>=20.9.0'} + resolution: + { + integrity: sha512-7OAS8gI0EReKGVN2HssHlM6umJgxF5VI3xN0p9FA91p/YO+ou5hiNghLdZ5BEHztwaaK5+bLKRf8x/o2L2nk9A==, + } + engines: { node: '>=20.9.0' } cpu: [arm] os: [linux] libc: [glibc] '@img/sharp-linux-ppc64@0.35.4': - resolution: {integrity: sha512-2oYZJeIl4kCcMGk4ouZVjnkCtFrpQFlNEtJ6GbxzhHQchwH0NH/qEb9ykmOl29dqwMq+JhFdZn+1ak2FKhI9fQ==} - engines: {node: '>=20.9.0'} + resolution: + { + integrity: sha512-2oYZJeIl4kCcMGk4ouZVjnkCtFrpQFlNEtJ6GbxzhHQchwH0NH/qEb9ykmOl29dqwMq+JhFdZn+1ak2FKhI9fQ==, + } + engines: { node: '>=20.9.0' } cpu: [ppc64] os: [linux] libc: [glibc] '@img/sharp-linux-riscv64@0.35.4': - resolution: {integrity: sha512-cPbNChoRURAWdebDIHSenxRpgEdy7JkPydSnUxRm9VvKD7m0/xVaR/8Fzlu81pk5nHEvHH87UZUA7cTtwnbJSA==} - engines: {node: '>=20.9.0'} + resolution: + { + integrity: sha512-cPbNChoRURAWdebDIHSenxRpgEdy7JkPydSnUxRm9VvKD7m0/xVaR/8Fzlu81pk5nHEvHH87UZUA7cTtwnbJSA==, + } + engines: { node: '>=20.9.0' } cpu: [riscv64] os: [linux] libc: [glibc] '@img/sharp-linux-s390x@0.35.4': - resolution: {integrity: sha512-RY0JFY8Fd6RonCBtHz+DvadaPkXDSI1AUn6yWL9TipqkZ1vY8w8evqdgyDFnkm4/K1ve1TvZiaePP5oSd4+WVQ==} - engines: {node: '>=20.9.0'} + resolution: + { + integrity: sha512-RY0JFY8Fd6RonCBtHz+DvadaPkXDSI1AUn6yWL9TipqkZ1vY8w8evqdgyDFnkm4/K1ve1TvZiaePP5oSd4+WVQ==, + } + engines: { node: '>=20.9.0' } cpu: [s390x] os: [linux] libc: [glibc] '@img/sharp-linux-x64@0.35.4': - resolution: {integrity: sha512-9qvvEAuk8k89TfWUoX2htWjbAMX8p+NxCppjpcg5k6xMsjhBQPTsoIh36h9Qde4WRuGpJeYnOjdosDn/cnv+OA==} - engines: {node: '>=20.9.0'} + resolution: + { + integrity: sha512-9qvvEAuk8k89TfWUoX2htWjbAMX8p+NxCppjpcg5k6xMsjhBQPTsoIh36h9Qde4WRuGpJeYnOjdosDn/cnv+OA==, + } + engines: { node: '>=20.9.0' } cpu: [x64] os: [linux] libc: [glibc] '@img/sharp-linuxmusl-arm64@0.35.4': - resolution: {integrity: sha512-KB5jxpfWQTr0nc3xdHtWChdbifHrBGsd2SM62Eyxrl8afikm+f5qGBU75SJIZBT/S1MC8XyacdlXBMSWq6OURA==} - engines: {node: '>=20.9.0'} + resolution: + { + integrity: sha512-KB5jxpfWQTr0nc3xdHtWChdbifHrBGsd2SM62Eyxrl8afikm+f5qGBU75SJIZBT/S1MC8XyacdlXBMSWq6OURA==, + } + engines: { node: '>=20.9.0' } cpu: [arm64] os: [linux] libc: [musl] '@img/sharp-linuxmusl-x64@0.35.4': - resolution: {integrity: sha512-f+eZJZIQNEEd26RPSW+76chwOf1XtA2Y/O+5ocVyLliHkeih3e+jhLVBdNTd2rS3IbNXK8+ug93Vf5ZXtF5Lxg==} - engines: {node: '>=20.9.0'} + resolution: + { + integrity: sha512-f+eZJZIQNEEd26RPSW+76chwOf1XtA2Y/O+5ocVyLliHkeih3e+jhLVBdNTd2rS3IbNXK8+ug93Vf5ZXtF5Lxg==, + } + engines: { node: '>=20.9.0' } cpu: [x64] os: [linux] libc: [musl] '@img/sharp-wasm32@0.35.4': - resolution: {integrity: sha512-zQnl4Kwp7Q6NHsENtU2T/00Zi+w3AQNwz3+UaTyVBy2FpXrzXzGjndpK61onhZjRtRpQXxCTeqw19bVyXOh7jA==} - engines: {node: '>=20.9.0'} + resolution: + { + integrity: sha512-zQnl4Kwp7Q6NHsENtU2T/00Zi+w3AQNwz3+UaTyVBy2FpXrzXzGjndpK61onhZjRtRpQXxCTeqw19bVyXOh7jA==, + } + engines: { node: '>=20.9.0' } '@img/sharp-webcontainers-wasm32@0.35.4': - resolution: {integrity: sha512-ESfNkywmCfPNyaZjxooddJQiQ+l/nTpGEOGthxiLnIHXC/CmcBixnfwUleX9mCz9ovrUUvKMap/pm8RYbzfwaA==} - engines: {node: '>=20.9.0'} + resolution: + { + integrity: sha512-ESfNkywmCfPNyaZjxooddJQiQ+l/nTpGEOGthxiLnIHXC/CmcBixnfwUleX9mCz9ovrUUvKMap/pm8RYbzfwaA==, + } + engines: { node: '>=20.9.0' } cpu: [wasm32] '@img/sharp-win32-arm64@0.35.4': - resolution: {integrity: sha512-iNdlBX9gLVvqe2I3uIJSIKTq6wckP/DYxZtcqxm09x5Gi24DnFBmPAWZmr60ZyYMG0xlzo6goG3670ar+RXvRw==} - engines: {node: '>=20.9.0'} + resolution: + { + integrity: sha512-iNdlBX9gLVvqe2I3uIJSIKTq6wckP/DYxZtcqxm09x5Gi24DnFBmPAWZmr60ZyYMG0xlzo6goG3670ar+RXvRw==, + } + engines: { node: '>=20.9.0' } cpu: [arm64] os: [win32] '@img/sharp-win32-ia32@0.35.4': - resolution: {integrity: sha512-kqRsbaa5CS6KHlpxnN7WhE6vAAugXyZButpRdvDWetlv6Qv4N9WTcrWzF7tXfB9T7MsoadqdI8hmwLq6UlLvtw==} - engines: {node: ^20.9.0} + resolution: + { + integrity: sha512-kqRsbaa5CS6KHlpxnN7WhE6vAAugXyZButpRdvDWetlv6Qv4N9WTcrWzF7tXfB9T7MsoadqdI8hmwLq6UlLvtw==, + } + engines: { node: ^20.9.0 } cpu: [ia32] os: [win32] '@img/sharp-win32-x64@0.35.4': - resolution: {integrity: sha512-XtmnYhBcrORsJ4XJngyzr/EWP0hRZLAZRFaApdKuviyqF78+ylxh2y06ZmtULAMOnObJ3ucpN0AcwSWnMowTRg==} - engines: {node: '>=20.9.0'} + resolution: + { + integrity: sha512-XtmnYhBcrORsJ4XJngyzr/EWP0hRZLAZRFaApdKuviyqF78+ylxh2y06ZmtULAMOnObJ3ucpN0AcwSWnMowTRg==, + } + engines: { node: '>=20.9.0' } cpu: [x64] os: [win32] '@jridgewell/gen-mapping@0.3.13': - resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==} + resolution: + { + integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==, + } '@jridgewell/remapping@2.3.5': - resolution: {integrity: sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==} + resolution: + { + integrity: sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==, + } '@jridgewell/resolve-uri@3.1.2': - resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} - engines: {node: '>=6.0.0'} + resolution: + { + integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==, + } + engines: { node: '>=6.0.0' } '@jridgewell/sourcemap-codec@1.6.0': - resolution: {integrity: sha512-T7jf+5zgsZHwNJ4lvQ7/aezbyk0nNX+zJVWpmHA7VYsEx7a7qr5Rg5IbtJFqkgze5Y2sruq1RUY8Q837Od7iFw==} + resolution: + { + integrity: sha512-T7jf+5zgsZHwNJ4lvQ7/aezbyk0nNX+zJVWpmHA7VYsEx7a7qr5Rg5IbtJFqkgze5Y2sruq1RUY8Q837Od7iFw==, + } '@jridgewell/trace-mapping@0.3.31': - resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==} + resolution: + { + integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==, + } '@loaderkit/resolve@1.0.6': - resolution: {integrity: sha512-G8FdIoF5CypfwmD9rl8BXod5HDn8JqB0CCNBXDTaRZ+yRYhARrrSToX1zg1zy9jX3zLqigsELwhT4gNtkdQAUg==} + resolution: + { + integrity: sha512-G8FdIoF5CypfwmD9rl8BXod5HDn8JqB0CCNBXDTaRZ+yRYhARrrSToX1zg1zy9jX3zLqigsELwhT4gNtkdQAUg==, + } '@manypkg/find-root@3.1.0': - resolution: {integrity: sha512-BcSqCyKhBVZ5YkSzOiheMCV41kqAFptW6xGqYSTjkVTl9XQpr+pqHhwgGCOHQtjDCv7Is6EFyA14Sm5GVbVABA==} - engines: {node: '>=20.0.0'} + resolution: + { + integrity: sha512-BcSqCyKhBVZ5YkSzOiheMCV41kqAFptW6xGqYSTjkVTl9XQpr+pqHhwgGCOHQtjDCv7Is6EFyA14Sm5GVbVABA==, + } + engines: { node: '>=20.0.0' } '@manypkg/get-packages@3.1.0': - resolution: {integrity: sha512-0TbBVyvPrP7xGYBI/cP8UP+yl/z+HtbTttAD7FMAJgn/kXOTwh5/60TsqP9ZYY710forNfyV0N8P/IE/ujGZJg==} - engines: {node: '>=20.0.0'} + resolution: + { + integrity: sha512-0TbBVyvPrP7xGYBI/cP8UP+yl/z+HtbTttAD7FMAJgn/kXOTwh5/60TsqP9ZYY710forNfyV0N8P/IE/ujGZJg==, + } + engines: { node: '>=20.0.0' } '@manypkg/tools@2.1.2': - resolution: {integrity: sha512-6QEf6yqFbETdwGITKq57aYoPfX/3K8XFNwsAlx0C1M7o8cb79sv1M3w+tWuWvIcSbNqrLF7OD7YpZMVVz335hQ==} - engines: {node: '>=20.0.0'} + resolution: + { + integrity: sha512-6QEf6yqFbETdwGITKq57aYoPfX/3K8XFNwsAlx0C1M7o8cb79sv1M3w+tWuWvIcSbNqrLF7OD7YpZMVVz335hQ==, + } + engines: { node: '>=20.0.0' } '@mdx-js/mdx@3.1.1': - resolution: {integrity: sha512-f6ZO2ifpwAQIpzGWaBQT2TXxPv6z3RBzQKpVftEWN78Vl/YweF1uwussDx8ECAXVtr3Rs89fKyG9YlzUs9DyGQ==} + resolution: + { + integrity: sha512-f6ZO2ifpwAQIpzGWaBQT2TXxPv6z3RBzQKpVftEWN78Vl/YweF1uwussDx8ECAXVtr3Rs89fKyG9YlzUs9DyGQ==, + } '@next/env@16.3.3': - resolution: {integrity: sha512-U2eYQRwXj+dsqxV79zFqExDdatnNY/ZWc2nsJU1p/OgT7fd3dXwlF6OjYaFQCfMoeTA19PWq+wVmYgimVA+V+g==} + resolution: + { + integrity: sha512-U2eYQRwXj+dsqxV79zFqExDdatnNY/ZWc2nsJU1p/OgT7fd3dXwlF6OjYaFQCfMoeTA19PWq+wVmYgimVA+V+g==, + } '@next/swc-darwin-arm64@16.3.3': - resolution: {integrity: sha512-8Hiv32QJPwdV6KYJ8meR9SBA061tQqnIKTJDocvOXlEQqib0xMFpzArosuffFUUc0sslbh7QQ8a3Yey1QV8EIw==} - engines: {node: '>= 10'} + resolution: + { + integrity: sha512-8Hiv32QJPwdV6KYJ8meR9SBA061tQqnIKTJDocvOXlEQqib0xMFpzArosuffFUUc0sslbh7QQ8a3Yey1QV8EIw==, + } + engines: { node: '>= 10' } cpu: [arm64] os: [darwin] '@next/swc-darwin-x64@16.3.3': - resolution: {integrity: sha512-A1lgKgwVchRYmSe467zdwhxT9040dd8lH+o65sL5Jet8fjB4kegw/rDyPIpYVRb6jAqwXFOJpjIXJLxQKLiE3A==} - engines: {node: '>= 10'} + resolution: + { + integrity: sha512-A1lgKgwVchRYmSe467zdwhxT9040dd8lH+o65sL5Jet8fjB4kegw/rDyPIpYVRb6jAqwXFOJpjIXJLxQKLiE3A==, + } + engines: { node: '>= 10' } cpu: [x64] os: [darwin] '@next/swc-linux-arm64-gnu@16.3.3': - resolution: {integrity: sha512-bf0FIssMFueU2dm7vQEWWxk0c8UjKTdW0yzuh0sQsD8pf1+KCLDdaqhYZNMYGmXwEOiHAUzgBKudovIlcvvBjg==} - engines: {node: '>= 10'} + resolution: + { + integrity: sha512-bf0FIssMFueU2dm7vQEWWxk0c8UjKTdW0yzuh0sQsD8pf1+KCLDdaqhYZNMYGmXwEOiHAUzgBKudovIlcvvBjg==, + } + engines: { node: '>= 10' } cpu: [arm64] os: [linux] libc: [glibc] '@next/swc-linux-arm64-musl@16.3.3': - resolution: {integrity: sha512-W7viwCk9JY/cAkdz/A273rd5bb3RgT/IHwR7Upv90tunjBWNtAAhGhoecHh+teRNRSinuAFmE+l7fwZ4YKkrXg==} - engines: {node: '>= 10'} + resolution: + { + integrity: sha512-W7viwCk9JY/cAkdz/A273rd5bb3RgT/IHwR7Upv90tunjBWNtAAhGhoecHh+teRNRSinuAFmE+l7fwZ4YKkrXg==, + } + engines: { node: '>= 10' } cpu: [arm64] os: [linux] libc: [musl] '@next/swc-linux-x64-gnu@16.3.3': - resolution: {integrity: sha512-0W46zw1N3ODpI6n0GeivHvvob1pooozgZVqy65k0mh4/7vr+FbY9+WpHzNVXjHipJf/A3FDheBG19H1s5A25rA==} - engines: {node: '>= 10'} + resolution: + { + integrity: sha512-0W46zw1N3ODpI6n0GeivHvvob1pooozgZVqy65k0mh4/7vr+FbY9+WpHzNVXjHipJf/A3FDheBG19H1s5A25rA==, + } + engines: { node: '>= 10' } cpu: [x64] os: [linux] libc: [glibc] '@next/swc-linux-x64-musl@16.3.3': - resolution: {integrity: sha512-H4mBso8ZTMBPtdT0PN0pBx2ayTvQuTuvS6qT13d77yVFJXAPCxkyIhLTmdMaGTJs0krQYI/qpzdHijCeihXhbg==} - engines: {node: '>= 10'} + resolution: + { + integrity: sha512-H4mBso8ZTMBPtdT0PN0pBx2ayTvQuTuvS6qT13d77yVFJXAPCxkyIhLTmdMaGTJs0krQYI/qpzdHijCeihXhbg==, + } + engines: { node: '>= 10' } cpu: [x64] os: [linux] libc: [musl] '@next/swc-win32-arm64-msvc@16.3.3': - resolution: {integrity: sha512-cTMUJpcEGmeywofCUfhR+rSsoE33+rVPnPEYNTNdLNlsOeEg/vktOsKUSTb28vUGqD2jkm4Zaskcwn7OCI6FQg==} - engines: {node: '>= 10'} + resolution: + { + integrity: sha512-cTMUJpcEGmeywofCUfhR+rSsoE33+rVPnPEYNTNdLNlsOeEg/vktOsKUSTb28vUGqD2jkm4Zaskcwn7OCI6FQg==, + } + engines: { node: '>= 10' } cpu: [arm64] os: [win32] '@next/swc-win32-x64-msvc@16.3.3': - resolution: {integrity: sha512-2VR4cTBzHXaBjnGsuH6GyJjENzQOmHeAh11uY1iUhjm3j5dEUrVJuUj+VL78jaGi/Dik8xS76zEj18BsFhlVZQ==} - engines: {node: '>= 10'} + resolution: + { + integrity: sha512-2VR4cTBzHXaBjnGsuH6GyJjENzQOmHeAh11uY1iUhjm3j5dEUrVJuUj+VL78jaGi/Dik8xS76zEj18BsFhlVZQ==, + } + engines: { node: '>= 10' } cpu: [x64] os: [win32] '@oxc-project/types@0.147.0': - resolution: {integrity: sha512-IJ3s6ltHLp45S0bh7phkX+gJO7A1Wuz2EaqpAhb8WjqDwbzMiWKHhyyT42tskaWjEYXtHtVCPpnBJVT9+dcRLg==} + resolution: + { + integrity: sha512-IJ3s6ltHLp45S0bh7phkX+gJO7A1Wuz2EaqpAhb8WjqDwbzMiWKHhyyT42tskaWjEYXtHtVCPpnBJVT9+dcRLg==, + } '@pnpm/deps.graph-sequencer@1100.0.1': - resolution: {integrity: sha512-pOr5+q1fLYKwFN3LAJuGZEnfXDcQ73zqgDHMtGy+K+uIoUqyY+6MeDCWFwfu+4EFuq76I5EPFofoNAI+Bmmq4A==} - engines: {node: '>=22.13'} + resolution: + { + integrity: sha512-pOr5+q1fLYKwFN3LAJuGZEnfXDcQ73zqgDHMtGy+K+uIoUqyY+6MeDCWFwfu+4EFuq76I5EPFofoNAI+Bmmq4A==, + } + engines: { node: '>=22.13' } '@protobufjs/aspromise@1.1.2': - resolution: {integrity: sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==} + resolution: + { + integrity: sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==, + } '@protobufjs/base64@1.1.2': - resolution: {integrity: sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==} + resolution: + { + integrity: sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==, + } '@protobufjs/codegen@2.0.5': - resolution: {integrity: sha512-zgXFLzW3Ap33e6d0Wlj4MGIm6Ce8O89n/apUaGNB/jx+hw+ruWEp7EwGUshdLKVRCxZW12fp9r40E1mQrf/34g==} + resolution: + { + integrity: sha512-zgXFLzW3Ap33e6d0Wlj4MGIm6Ce8O89n/apUaGNB/jx+hw+ruWEp7EwGUshdLKVRCxZW12fp9r40E1mQrf/34g==, + } '@protobufjs/eventemitter@1.1.1': - resolution: {integrity: sha512-vW1GmwMZNnL+gMRaovlh9yZX74kc+TTU3FObkkurpMaRtBfLP3ldjS9KQWlwZgraRE0+dheEEoAxdzcJQ8eXZg==} + resolution: + { + integrity: sha512-vW1GmwMZNnL+gMRaovlh9yZX74kc+TTU3FObkkurpMaRtBfLP3ldjS9KQWlwZgraRE0+dheEEoAxdzcJQ8eXZg==, + } '@protobufjs/fetch@1.1.1': - resolution: {integrity: sha512-GpptLrs57adMSuHi3VNj0mAF8dwh36LMaYF6XyJ6JMWlVsc+t42tm1HSEDmOs3A8fC9yyeisgLhsTVQokOZ0zw==} + resolution: + { + integrity: sha512-GpptLrs57adMSuHi3VNj0mAF8dwh36LMaYF6XyJ6JMWlVsc+t42tm1HSEDmOs3A8fC9yyeisgLhsTVQokOZ0zw==, + } '@protobufjs/float@1.0.2': - resolution: {integrity: sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==} + resolution: + { + integrity: sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==, + } '@protobufjs/path@1.1.2': - resolution: {integrity: sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==} + resolution: + { + integrity: sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==, + } '@protobufjs/pool@1.1.0': - resolution: {integrity: sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==} + resolution: + { + integrity: sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==, + } '@protobufjs/utf8@1.1.2': - resolution: {integrity: sha512-b1UQwcEZ4yCnMCD8DAL1VlbvBJE9/IX4FTIp7BG1xYpf29SLazLSrqUkj4w7Y5y7cCVP6E5tcqqcI0xemPkHug==} + resolution: + { + integrity: sha512-b1UQwcEZ4yCnMCD8DAL1VlbvBJE9/IX4FTIp7BG1xYpf29SLazLSrqUkj4w7Y5y7cCVP6E5tcqqcI0xemPkHug==, + } '@publint/pack@0.1.7': - resolution: {integrity: sha512-4EDEmvxWtgsCnnVeBvtFIFZtUhPPt1+bA9JrSwU4Sa//6oKtzCSlGGXYJr44OD9aGISymbieJ4mCKHUygUDU+g==} - engines: {node: '>=18'} + resolution: + { + integrity: sha512-4EDEmvxWtgsCnnVeBvtFIFZtUhPPt1+bA9JrSwU4Sa//6oKtzCSlGGXYJr44OD9aGISymbieJ4mCKHUygUDU+g==, + } + engines: { node: '>=18' } '@quansync/fs@1.0.0': - resolution: {integrity: sha512-4TJ3DFtlf1L5LDMaM6CanJ/0lckGNtJcMjQ1NAV6zDmA0tEHKZtxNKin8EgPaVX1YzljbxckyT2tJrpQKAtngQ==} + resolution: + { + integrity: sha512-4TJ3DFtlf1L5LDMaM6CanJ/0lckGNtJcMjQ1NAV6zDmA0tEHKZtxNKin8EgPaVX1YzljbxckyT2tJrpQKAtngQ==, + } '@rolldown/binding-android-arm-eabi@1.2.6': - resolution: {integrity: sha512-b+jTcARdTiFLI6jB4a5XjTm0RWd6KcRfQj/I2356fxUZemiho9zQLxo0RtCuMDAyKcLo6cEltkgbQp6d1+sjjQ==} - engines: {node: ^20.19.0 || >=22.12.0} + resolution: + { + integrity: sha512-b+jTcARdTiFLI6jB4a5XjTm0RWd6KcRfQj/I2356fxUZemiho9zQLxo0RtCuMDAyKcLo6cEltkgbQp6d1+sjjQ==, + } + engines: { node: ^20.19.0 || >=22.12.0 } cpu: [arm] os: [android] '@rolldown/binding-android-arm64@1.2.6': - resolution: {integrity: sha512-lkWU8ZJaRk9q3CIEY1Tc7vIFALp3Xw5NfGJo2hQg5oIqNgxWi1zI+IiDEK3r70BF5Dzol1tcXsnzsRc8NLhG+Q==} - engines: {node: ^20.19.0 || >=22.12.0} + resolution: + { + integrity: sha512-lkWU8ZJaRk9q3CIEY1Tc7vIFALp3Xw5NfGJo2hQg5oIqNgxWi1zI+IiDEK3r70BF5Dzol1tcXsnzsRc8NLhG+Q==, + } + engines: { node: ^20.19.0 || >=22.12.0 } cpu: [arm64] os: [android] '@rolldown/binding-darwin-arm64@1.2.6': - resolution: {integrity: sha512-dgR56NYnvAszm7Ob1B2/Vn0e8bUQYZH2UjVaMMtMVOCKFSfjhfLmuA/9+O+F+ajUdG6B/bSssrKW6JJYASa8jA==} - engines: {node: ^20.19.0 || >=22.12.0} + resolution: + { + integrity: sha512-dgR56NYnvAszm7Ob1B2/Vn0e8bUQYZH2UjVaMMtMVOCKFSfjhfLmuA/9+O+F+ajUdG6B/bSssrKW6JJYASa8jA==, + } + engines: { node: ^20.19.0 || >=22.12.0 } cpu: [arm64] os: [darwin] '@rolldown/binding-darwin-x64@1.2.6': - resolution: {integrity: sha512-vpVxFvUCFioJqug7OTvqptkc4yb8UX0AwfDmJpaR/0sWz+BUmqSVAf7c8JkUgnN8YLspb4a/N6NhTyMAmdyQ7Q==} - engines: {node: ^20.19.0 || >=22.12.0} + resolution: + { + integrity: sha512-vpVxFvUCFioJqug7OTvqptkc4yb8UX0AwfDmJpaR/0sWz+BUmqSVAf7c8JkUgnN8YLspb4a/N6NhTyMAmdyQ7Q==, + } + engines: { node: ^20.19.0 || >=22.12.0 } cpu: [x64] os: [darwin] '@rolldown/binding-freebsd-x64@1.2.6': - resolution: {integrity: sha512-h1wG6Y6K3JlRswxsI64qQJqBAy4vrLuHgRbc8CZMGSWTOFRY6ghMApM1NKzB2I0n5xV1fjkE18SuVl2QpLeNpA==} - engines: {node: ^20.19.0 || >=22.12.0} + resolution: + { + integrity: sha512-h1wG6Y6K3JlRswxsI64qQJqBAy4vrLuHgRbc8CZMGSWTOFRY6ghMApM1NKzB2I0n5xV1fjkE18SuVl2QpLeNpA==, + } + engines: { node: ^20.19.0 || >=22.12.0 } cpu: [x64] os: [freebsd] '@rolldown/binding-linux-arm-gnueabihf@1.2.6': - resolution: {integrity: sha512-tbCiqub0q2MVWJKgF5PoAlNWCtQydiOYSLIkd8sByqK/6MMYLJRcSXSYodqYtd0O+Fw7QaVmKKlS4oL94YRZ0w==} - engines: {node: ^20.19.0 || >=22.12.0} + resolution: + { + integrity: sha512-tbCiqub0q2MVWJKgF5PoAlNWCtQydiOYSLIkd8sByqK/6MMYLJRcSXSYodqYtd0O+Fw7QaVmKKlS4oL94YRZ0w==, + } + engines: { node: ^20.19.0 || >=22.12.0 } cpu: [arm] os: [linux] '@rolldown/binding-linux-arm64-gnu@1.2.6': - resolution: {integrity: sha512-oxK9+baEBPhZG5HB4URY+uU04zJWeZlH6Tb9rB5DK4DF9XR1uXNLXt5Q5ZsugTKayNCNLhkcwz/ye74hRI98dg==} - engines: {node: ^20.19.0 || >=22.12.0} + resolution: + { + integrity: sha512-oxK9+baEBPhZG5HB4URY+uU04zJWeZlH6Tb9rB5DK4DF9XR1uXNLXt5Q5ZsugTKayNCNLhkcwz/ye74hRI98dg==, + } + engines: { node: ^20.19.0 || >=22.12.0 } cpu: [arm64] os: [linux] libc: [glibc] '@rolldown/binding-linux-arm64-musl@1.2.6': - resolution: {integrity: sha512-muWCk27FVBEZtv0MsK8gnfSmgczA8KQ0uRVJbTABKhkRfQc38aUrcb7fhi3BNiyseFmgcRsoMfQsSNJ+DbZdSw==} - engines: {node: ^20.19.0 || >=22.12.0} + resolution: + { + integrity: sha512-muWCk27FVBEZtv0MsK8gnfSmgczA8KQ0uRVJbTABKhkRfQc38aUrcb7fhi3BNiyseFmgcRsoMfQsSNJ+DbZdSw==, + } + engines: { node: ^20.19.0 || >=22.12.0 } cpu: [arm64] os: [linux] libc: [musl] '@rolldown/binding-linux-ppc64-gnu@1.2.6': - resolution: {integrity: sha512-eWDoSfU7Co2qj3vgB3Dt4lj1mG6CoWbcJQkRMP3XJplyCMtuaq3LHvPFjS9QIPvMGWVadJC04Xiy0IdcVPtnwQ==} - engines: {node: ^20.19.0 || >=22.12.0} + resolution: + { + integrity: sha512-eWDoSfU7Co2qj3vgB3Dt4lj1mG6CoWbcJQkRMP3XJplyCMtuaq3LHvPFjS9QIPvMGWVadJC04Xiy0IdcVPtnwQ==, + } + engines: { node: ^20.19.0 || >=22.12.0 } cpu: [ppc64] os: [linux] libc: [glibc] '@rolldown/binding-linux-s390x-gnu@1.2.6': - resolution: {integrity: sha512-2bWNjRSIayvupRKxXUY2tWG9fYdoUlTqWywHRvE8Eq3GvuQ+f2HeIkve697fIt+IQs/PV8yFsdWuhp1aJ1PdnA==} - engines: {node: ^20.19.0 || >=22.12.0} + resolution: + { + integrity: sha512-2bWNjRSIayvupRKxXUY2tWG9fYdoUlTqWywHRvE8Eq3GvuQ+f2HeIkve697fIt+IQs/PV8yFsdWuhp1aJ1PdnA==, + } + engines: { node: ^20.19.0 || >=22.12.0 } cpu: [s390x] os: [linux] libc: [glibc] '@rolldown/binding-linux-x64-gnu@1.2.6': - resolution: {integrity: sha512-KekI0gS0wLxe1UBSQSjenBVwou/JkcQPDzBPICGZjxUv9k3RteHDPBQaiOicZUFKRIH2wKEimGwVpnJsbPzu7w==} - engines: {node: ^20.19.0 || >=22.12.0} + resolution: + { + integrity: sha512-KekI0gS0wLxe1UBSQSjenBVwou/JkcQPDzBPICGZjxUv9k3RteHDPBQaiOicZUFKRIH2wKEimGwVpnJsbPzu7w==, + } + engines: { node: ^20.19.0 || >=22.12.0 } cpu: [x64] os: [linux] libc: [glibc] '@rolldown/binding-linux-x64-musl@1.2.6': - resolution: {integrity: sha512-TvtPnfVr+HtyGiDmPK4VWmlNm7QhNNAcK5Q9A7aOXsI8545yCyaoMaicXrFZ72JzeYjaUVk7yT243zT0jzjFKQ==} - engines: {node: ^20.19.0 || >=22.12.0} + resolution: + { + integrity: sha512-TvtPnfVr+HtyGiDmPK4VWmlNm7QhNNAcK5Q9A7aOXsI8545yCyaoMaicXrFZ72JzeYjaUVk7yT243zT0jzjFKQ==, + } + engines: { node: ^20.19.0 || >=22.12.0 } cpu: [x64] os: [linux] libc: [musl] '@rolldown/binding-openharmony-arm64@1.2.6': - resolution: {integrity: sha512-iOo0VEay2XFhaCcH0sps5XIimkSuOnNaZrf6+ZkoSOQBJPKNU48RkmJv0/lSpipexu5P+ouFgafe5IGr/DiQfg==} - engines: {node: ^20.19.0 || >=22.12.0} + resolution: + { + integrity: sha512-iOo0VEay2XFhaCcH0sps5XIimkSuOnNaZrf6+ZkoSOQBJPKNU48RkmJv0/lSpipexu5P+ouFgafe5IGr/DiQfg==, + } + engines: { node: ^20.19.0 || >=22.12.0 } cpu: [arm64] os: [openharmony] '@rolldown/binding-win32-arm64-msvc@1.2.6': - resolution: {integrity: sha512-y5NTmmasMS455JlOCO4ZM9krIchv3Mvm1crL1iUPGOPgEzSkves9n0SdC5Sjz6+qWDFhd8/JpfWMH8NSWNHe+A==} - engines: {node: ^20.19.0 || >=22.12.0} + resolution: + { + integrity: sha512-y5NTmmasMS455JlOCO4ZM9krIchv3Mvm1crL1iUPGOPgEzSkves9n0SdC5Sjz6+qWDFhd8/JpfWMH8NSWNHe+A==, + } + engines: { node: ^20.19.0 || >=22.12.0 } cpu: [arm64] os: [win32] '@rolldown/binding-win32-x64-msvc@1.2.6': - resolution: {integrity: sha512-np8iZSLfXlAD4kWhiyq/u0Yt8oZDtRQ8lGhQaCXo2rl37KNjeU0GjJuwr4P3oeZ++ROfofsKNBqR5LTO8aXyWQ==} - engines: {node: ^20.19.0 || >=22.12.0} + resolution: + { + integrity: sha512-np8iZSLfXlAD4kWhiyq/u0Yt8oZDtRQ8lGhQaCXo2rl37KNjeU0GjJuwr4P3oeZ++ROfofsKNBqR5LTO8aXyWQ==, + } + engines: { node: ^20.19.0 || >=22.12.0 } cpu: [x64] os: [win32] '@rolldown/pluginutils@1.0.1': - resolution: {integrity: sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==} + resolution: + { + integrity: sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==, + } '@shikijs/core@4.4.3': - resolution: {integrity: sha512-QCR4q2ZO/ILJEuwiBMel4wdcTDb1JGwfjKTxPDF6x8ixOaluPrVqIn06C99AcRPhmYlBR56d/Fb+GN58GzExpg==} - engines: {node: '>=20'} + resolution: + { + integrity: sha512-QCR4q2ZO/ILJEuwiBMel4wdcTDb1JGwfjKTxPDF6x8ixOaluPrVqIn06C99AcRPhmYlBR56d/Fb+GN58GzExpg==, + } + engines: { node: '>=20' } '@shikijs/engine-javascript@4.4.3': - resolution: {integrity: sha512-FbOjFJp9VLdo1Wevs10BBtVxiTWwNLqZh5Gkhjgda/ioL15YOgeSl9n+6XMa3qRlPQzfhFNe641SrynFHYG0nQ==} - engines: {node: '>=20'} + resolution: + { + integrity: sha512-FbOjFJp9VLdo1Wevs10BBtVxiTWwNLqZh5Gkhjgda/ioL15YOgeSl9n+6XMa3qRlPQzfhFNe641SrynFHYG0nQ==, + } + engines: { node: '>=20' } '@shikijs/engine-oniguruma@4.4.3': - resolution: {integrity: sha512-EcOQkxdxGQrc1Row/cC2c96/v1dbZqGnEVu1qTuT/MJmp6+cXCvQussowVmCv5Tqr3KuY3c7IbM6HTW3LJ1k9w==} - engines: {node: '>=20'} + resolution: + { + integrity: sha512-EcOQkxdxGQrc1Row/cC2c96/v1dbZqGnEVu1qTuT/MJmp6+cXCvQussowVmCv5Tqr3KuY3c7IbM6HTW3LJ1k9w==, + } + engines: { node: '>=20' } '@shikijs/langs@4.4.3': - resolution: {integrity: sha512-ePic0yfAJGOF83D5wBHK/00EjK65oahBYxFk5epgq33WRv7X9UuxLEV8PtR0szC0z8dl7INIpIodB99JRFlR+A==} - engines: {node: '>=20'} + resolution: + { + integrity: sha512-ePic0yfAJGOF83D5wBHK/00EjK65oahBYxFk5epgq33WRv7X9UuxLEV8PtR0szC0z8dl7INIpIodB99JRFlR+A==, + } + engines: { node: '>=20' } '@shikijs/primitive@4.4.3': - resolution: {integrity: sha512-m0wBeLDQDeIxRdUmrCPdQqfuUamDwRL5isCfYbguKD6NiaKpVbsv+3J81DyIKgNW5h4WAIIr8T4EkgQrBBxvaQ==} - engines: {node: '>=20'} + resolution: + { + integrity: sha512-m0wBeLDQDeIxRdUmrCPdQqfuUamDwRL5isCfYbguKD6NiaKpVbsv+3J81DyIKgNW5h4WAIIr8T4EkgQrBBxvaQ==, + } + engines: { node: '>=20' } '@shikijs/themes@4.4.3': - resolution: {integrity: sha512-w8UHjeUnIR965KMWJHUPXOc2mNJUnK3vpVLYLvw5IYU2mnTTJ89E24OrJDBNiJDQ0qzb0tc4l7mrIXx5cFeIyw==} - engines: {node: '>=20'} + resolution: + { + integrity: sha512-w8UHjeUnIR965KMWJHUPXOc2mNJUnK3vpVLYLvw5IYU2mnTTJ89E24OrJDBNiJDQ0qzb0tc4l7mrIXx5cFeIyw==, + } + engines: { node: '>=20' } '@shikijs/types@4.4.3': - resolution: {integrity: sha512-UEJxmRR++MAGR6hugn0vgVS2W/6lWAts84FFSrnlH9sP0LNol7E5+NQ792pH8liWUhyMyjhTgSUH3k7iD7tc5g==} - engines: {node: '>=20'} + resolution: + { + integrity: sha512-UEJxmRR++MAGR6hugn0vgVS2W/6lWAts84FFSrnlH9sP0LNol7E5+NQ792pH8liWUhyMyjhTgSUH3k7iD7tc5g==, + } + engines: { node: '>=20' } '@shikijs/vscode-textmate@10.0.2': - resolution: {integrity: sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg==} + resolution: + { + integrity: sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg==, + } '@sindresorhus/is@4.6.0': - resolution: {integrity: sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==} - engines: {node: '>=10'} + resolution: + { + integrity: sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==, + } + engines: { node: '>=10' } '@standard-schema/spec@1.1.0': - resolution: {integrity: sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==} + resolution: + { + integrity: sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==, + } '@swc/helpers@0.5.23': - resolution: {integrity: sha512-5lSsMOTXURePglDfvuAQUqkGek9Hg2kksOYay2m0+XR++b2NWYL/4sWyuvVBIs8oKnJaxkdi9whaL/sqN13afw==} + resolution: + { + integrity: sha512-5lSsMOTXURePglDfvuAQUqkGek9Hg2kksOYay2m0+XR++b2NWYL/4sWyuvVBIs8oKnJaxkdi9whaL/sqN13afw==, + } '@tailwindcss/node@4.3.3': - resolution: {integrity: sha512-/T8IKEsf9VTU6tLjgC7+sv2mOPtQxzE2jMw7u4Tt40Tx+QSZxpzh95/H6cMKoja9XuW7iMdLJYBB0o9G1CaAgg==} + resolution: + { + integrity: sha512-/T8IKEsf9VTU6tLjgC7+sv2mOPtQxzE2jMw7u4Tt40Tx+QSZxpzh95/H6cMKoja9XuW7iMdLJYBB0o9G1CaAgg==, + } '@tailwindcss/oxide-android-arm64@4.3.3': - resolution: {integrity: sha512-Y85A2gmPSkl5Ve5qR86GL4HT509cFqQh1aes9p3sSkyTPwt0Pppf3GkwGe4JPACcRYjgJIEhQgM6dBClnr0NYw==} - engines: {node: '>= 20'} + resolution: + { + integrity: sha512-Y85A2gmPSkl5Ve5qR86GL4HT509cFqQh1aes9p3sSkyTPwt0Pppf3GkwGe4JPACcRYjgJIEhQgM6dBClnr0NYw==, + } + engines: { node: '>= 20' } cpu: [arm64] os: [android] '@tailwindcss/oxide-darwin-arm64@4.3.3': - resolution: {integrity: sha512-BiaWatpBcERQFDlOjRDpIVXuFK5PJez5SA4JMg6VYZdBYU+qKfV/vqjcIs+IYmtitf1xYQZTwXvU/8y4lfZUGw==} - engines: {node: '>= 20'} + resolution: + { + integrity: sha512-BiaWatpBcERQFDlOjRDpIVXuFK5PJez5SA4JMg6VYZdBYU+qKfV/vqjcIs+IYmtitf1xYQZTwXvU/8y4lfZUGw==, + } + engines: { node: '>= 20' } cpu: [arm64] os: [darwin] '@tailwindcss/oxide-darwin-x64@4.3.3': - resolution: {integrity: sha512-fAeUqfV5ndhxRwai8cXGzdLvul9utWOmeTkv69unv4ZXixjn61Z+p9lCWdwOwA3TYboG3BwdVuN/RDjhBRl0mw==} - engines: {node: '>= 20'} + resolution: + { + integrity: sha512-fAeUqfV5ndhxRwai8cXGzdLvul9utWOmeTkv69unv4ZXixjn61Z+p9lCWdwOwA3TYboG3BwdVuN/RDjhBRl0mw==, + } + engines: { node: '>= 20' } cpu: [x64] os: [darwin] '@tailwindcss/oxide-freebsd-x64@4.3.3': - resolution: {integrity: sha512-iyf5bV6+wnAlflVeEy7R25dupxTNECZN5QMI0qNT6eT+EgaGdZcKhGkr5SdoaWiLJ3spLqIY9VCeSGrwmtg4kw==} - engines: {node: '>= 20'} + resolution: + { + integrity: sha512-iyf5bV6+wnAlflVeEy7R25dupxTNECZN5QMI0qNT6eT+EgaGdZcKhGkr5SdoaWiLJ3spLqIY9VCeSGrwmtg4kw==, + } + engines: { node: '>= 20' } cpu: [x64] os: [freebsd] '@tailwindcss/oxide-linux-arm-gnueabihf@4.3.3': - resolution: {integrity: sha512-aAYUprJAJQWWbRrPvtjdroZ56Md+JM8pMiopS6xGEwDfLhqj+2ver2p4nU4Mb3CRqcMmNBjo8KkUgcxhkzVQGQ==} - engines: {node: '>= 20'} + resolution: + { + integrity: sha512-aAYUprJAJQWWbRrPvtjdroZ56Md+JM8pMiopS6xGEwDfLhqj+2ver2p4nU4Mb3CRqcMmNBjo8KkUgcxhkzVQGQ==, + } + engines: { node: '>= 20' } cpu: [arm] os: [linux] '@tailwindcss/oxide-linux-arm64-gnu@4.3.3': - resolution: {integrity: sha512-nDxldcEENOxZRzC2uu9jrutZdAAQtb+8WWDCSnWL1zvBk1+FN+x6MtDViPB5AJMfttVCUhehGWus3XBPgatM/w==} - engines: {node: '>= 20'} + resolution: + { + integrity: sha512-nDxldcEENOxZRzC2uu9jrutZdAAQtb+8WWDCSnWL1zvBk1+FN+x6MtDViPB5AJMfttVCUhehGWus3XBPgatM/w==, + } + engines: { node: '>= 20' } cpu: [arm64] os: [linux] libc: [glibc] '@tailwindcss/oxide-linux-arm64-musl@4.3.3': - resolution: {integrity: sha512-Md44bD6veX/PC5iyF8cDVnw4HBIANZepRZZ7a8DQOvkfo5WUBwcp6iAuCUz23u+4SUkhJlD3eL7hNdW8ezd/kA==} - engines: {node: '>= 20'} + resolution: + { + integrity: sha512-Md44bD6veX/PC5iyF8cDVnw4HBIANZepRZZ7a8DQOvkfo5WUBwcp6iAuCUz23u+4SUkhJlD3eL7hNdW8ezd/kA==, + } + engines: { node: '>= 20' } cpu: [arm64] os: [linux] libc: [musl] '@tailwindcss/oxide-linux-x64-gnu@4.3.3': - resolution: {integrity: sha512-tx7us1muwOKAKWao2v/GaafFeQboE6aj88vC6ziN2NCGcRm8gWUhwjzg+YdVB1e4boAtdtma4L43onunI6NS4w==} - engines: {node: '>= 20'} + resolution: + { + integrity: sha512-tx7us1muwOKAKWao2v/GaafFeQboE6aj88vC6ziN2NCGcRm8gWUhwjzg+YdVB1e4boAtdtma4L43onunI6NS4w==, + } + engines: { node: '>= 20' } cpu: [x64] os: [linux] libc: [glibc] '@tailwindcss/oxide-linux-x64-musl@4.3.3': - resolution: {integrity: sha512-SJxX60smvHgasZoBy11dX6YRjXJFovwWBoedhbQPOBzgFWBHGB+TVPWB9BxzR7TTxU8FQZAI2AyiNCMzFm8Img==} - engines: {node: '>= 20'} + resolution: + { + integrity: sha512-SJxX60smvHgasZoBy11dX6YRjXJFovwWBoedhbQPOBzgFWBHGB+TVPWB9BxzR7TTxU8FQZAI2AyiNCMzFm8Img==, + } + engines: { node: '>= 20' } cpu: [x64] os: [linux] libc: [musl] '@tailwindcss/oxide-wasm32-wasi@4.3.3': - resolution: {integrity: sha512-jx1+rPhY/5Ympkktd656HBWEBLxP7dH06losBLjjf5vgCODXvi9KhtftWcMIwTFIDqBr7cRnQkdLnAG+IOlGvQ==} - engines: {node: '>=14.0.0'} + resolution: + { + integrity: sha512-jx1+rPhY/5Ympkktd656HBWEBLxP7dH06losBLjjf5vgCODXvi9KhtftWcMIwTFIDqBr7cRnQkdLnAG+IOlGvQ==, + } + engines: { node: '>=14.0.0' } cpu: [wasm32] bundledDependencies: - '@napi-rs/wasm-runtime' @@ -1411,31 +2100,49 @@ packages: - tslib '@tailwindcss/oxide-win32-arm64-msvc@4.3.3': - resolution: {integrity: sha512-3rc292Ca2ceK6Ulcc/bAVnTs/3nDtoPhyEKlgPv+yQJQi/JS/AMJlqzxvlDacL1nekbrcf6bTqp/jV4qgnPxNQ==} - engines: {node: '>= 20'} + resolution: + { + integrity: sha512-3rc292Ca2ceK6Ulcc/bAVnTs/3nDtoPhyEKlgPv+yQJQi/JS/AMJlqzxvlDacL1nekbrcf6bTqp/jV4qgnPxNQ==, + } + engines: { node: '>= 20' } cpu: [arm64] os: [win32] '@tailwindcss/oxide-win32-x64-msvc@4.3.3': - resolution: {integrity: sha512-yJ0pwIVc/nYeGoV02WtsN8KYyLQv7kyI2wDnkezyJlGGjkd4QLwDGAwl47YpPJeuI0M0ObaXGSPjvWDPeTPggw==} - engines: {node: '>= 20'} + resolution: + { + integrity: sha512-yJ0pwIVc/nYeGoV02WtsN8KYyLQv7kyI2wDnkezyJlGGjkd4QLwDGAwl47YpPJeuI0M0ObaXGSPjvWDPeTPggw==, + } + engines: { node: '>= 20' } cpu: [x64] os: [win32] '@tailwindcss/oxide@4.3.3': - resolution: {integrity: sha512-krXjAikiaFSPaK/FkAQT5UTx3VormQaiZ5hBFlJZ9UFQGB/rwg1MZIhHAG9smMQRTdyJxP6Qt5MwMtdyU5FWrA==} - engines: {node: '>= 20'} + resolution: + { + integrity: sha512-krXjAikiaFSPaK/FkAQT5UTx3VormQaiZ5hBFlJZ9UFQGB/rwg1MZIhHAG9smMQRTdyJxP6Qt5MwMtdyU5FWrA==, + } + engines: { node: '>= 20' } '@tailwindcss/postcss@4.3.3': - resolution: {integrity: sha512-JTSZZGQi1AyKirbLN3azmjVzef92tcX7h+iSqPdaeStyFpGpDlKvvpxeOE8njhbUanbRwr3z8DyzhICWnMtQeg==} + resolution: + { + integrity: sha512-JTSZZGQi1AyKirbLN3azmjVzef92tcX7h+iSqPdaeStyFpGpDlKvvpxeOE8njhbUanbRwr3z8DyzhICWnMtQeg==, + } '@testing-library/dom@10.4.1': - resolution: {integrity: sha512-o4PXJQidqJl82ckFaXUeoAW+XysPLauYI43Abki5hABd853iMhitooc6znOnczgbTYmEP6U6/y1ZyKAIsvMKGg==} - engines: {node: '>=18'} + resolution: + { + integrity: sha512-o4PXJQidqJl82ckFaXUeoAW+XysPLauYI43Abki5hABd853iMhitooc6znOnczgbTYmEP6U6/y1ZyKAIsvMKGg==, + } + engines: { node: '>=18' } '@testing-library/react@16.3.3': - resolution: {integrity: sha512-Uo193NgQbPMz6lrrhtRQQFcMC6Re/ELLFbbuVL30WDlZxlpZf9/lMHTAVxPRLw1q1iu9OJmR1c2BLiENRstdBg==} - engines: {node: '>=18'} + resolution: + { + integrity: sha512-Uo193NgQbPMz6lrrhtRQQFcMC6Re/ELLFbbuVL30WDlZxlpZf9/lMHTAVxPRLw1q1iu9OJmR1c2BLiENRstdBg==, + } + engines: { node: '>=18' } peerDependencies: '@testing-library/dom': ^10.0.0 '@types/react': ^18.0.0 || ^19.0.0 @@ -1449,60 +2156,114 @@ packages: optional: true '@ts-morph/common@0.28.1': - resolution: {integrity: sha512-W74iWf7ILp1ZKNYXY5qbddNaml7e9Sedv5lvU1V8lftlitkc9Pq1A+jlH23ltDgWYeZFFEqGCD1Ies9hqu3O+g==} + resolution: + { + integrity: sha512-W74iWf7ILp1ZKNYXY5qbddNaml7e9Sedv5lvU1V8lftlitkc9Pq1A+jlH23ltDgWYeZFFEqGCD1Ies9hqu3O+g==, + } '@types/aria-query@5.0.4': - resolution: {integrity: sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==} + resolution: + { + integrity: sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==, + } '@types/chai@5.2.3': - resolution: {integrity: sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==} + resolution: + { + integrity: sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==, + } '@types/debug@4.1.13': - resolution: {integrity: sha512-KSVgmQmzMwPlmtljOomayoR89W4FynCAi3E8PPs7vmDVPe84hT+vGPKkJfThkmXs0x0jAaa9U8uW8bbfyS2fWw==} + resolution: + { + integrity: sha512-KSVgmQmzMwPlmtljOomayoR89W4FynCAi3E8PPs7vmDVPe84hT+vGPKkJfThkmXs0x0jAaa9U8uW8bbfyS2fWw==, + } '@types/deep-eql@4.0.2': - resolution: {integrity: sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==} + resolution: + { + integrity: sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==, + } '@types/estree-jsx@1.0.5': - resolution: {integrity: sha512-52CcUVNFyfb1A2ALocQw/Dd1BQFNmSdkuC3BkZ6iqhdMfQz7JWOFRuJFloOzjk+6WijU56m9oKXFAXc7o3Towg==} + resolution: + { + integrity: sha512-52CcUVNFyfb1A2ALocQw/Dd1BQFNmSdkuC3BkZ6iqhdMfQz7JWOFRuJFloOzjk+6WijU56m9oKXFAXc7o3Towg==, + } '@types/estree@1.0.9': - resolution: {integrity: sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==} + resolution: + { + integrity: sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==, + } '@types/hast@3.0.5': - resolution: {integrity: sha512-rp/ezSWaD1m44dPKICGhiskI13nVr7qTloFwDa/IYkhhf5nzwP+zIQcIJh3WIFSBOy/H1PzB40jPjMDksN4F+g==} + resolution: + { + integrity: sha512-rp/ezSWaD1m44dPKICGhiskI13nVr7qTloFwDa/IYkhhf5nzwP+zIQcIJh3WIFSBOy/H1PzB40jPjMDksN4F+g==, + } '@types/mdast@4.0.4': - resolution: {integrity: sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==} + resolution: + { + integrity: sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==, + } '@types/mdx@2.0.14': - resolution: {integrity: sha512-T48PeuJtvLosNTPVhfnIp3i/n3a4g4Bad7YCq5k64D4u7NwDrAotikQ+5+sjtUvBmxCMlbo3dVL+C2dP0rWHzg==} + resolution: + { + integrity: sha512-T48PeuJtvLosNTPVhfnIp3i/n3a4g4Bad7YCq5k64D4u7NwDrAotikQ+5+sjtUvBmxCMlbo3dVL+C2dP0rWHzg==, + } '@types/ms@2.1.0': - resolution: {integrity: sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==} + resolution: + { + integrity: sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==, + } '@types/node@26.4.0': - resolution: {integrity: sha512-faiGnoIrLH/V8cibOMEAZ8pMw6oXqSukl29ra4mN8GdaB2ZewzeaLj+INpV5N+Z1eKWzY+IzaIZH2EIR6YZRNQ==} + resolution: + { + integrity: sha512-faiGnoIrLH/V8cibOMEAZ8pMw6oXqSukl29ra4mN8GdaB2ZewzeaLj+INpV5N+Z1eKWzY+IzaIZH2EIR6YZRNQ==, + } '@types/react-dom@19.2.5': - resolution: {integrity: sha512-fMPwH9v7r/pp43yUd2/Mbiex5KouJwwR3dzHkhLREUC6764VyDsqxhAxv6OFEYR1RhjOyD1naqba8ECDBe7ZQg==} + resolution: + { + integrity: sha512-fMPwH9v7r/pp43yUd2/Mbiex5KouJwwR3dzHkhLREUC6764VyDsqxhAxv6OFEYR1RhjOyD1naqba8ECDBe7ZQg==, + } peerDependencies: '@types/react': ^19.2.0 '@types/react@19.2.18': - resolution: {integrity: sha512-AnzbBERsrLKtk2XSfTbYRLjQPdy116Sty4q+T+Bp3IC4l6jNBvreVPAHmpq9qhXQM7CXZPjLVmGMw9sy+hxQ3w==} + resolution: + { + integrity: sha512-AnzbBERsrLKtk2XSfTbYRLjQPdy116Sty4q+T+Bp3IC4l6jNBvreVPAHmpq9qhXQM7CXZPjLVmGMw9sy+hxQ3w==, + } '@types/unist@2.0.11': - resolution: {integrity: sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==} + resolution: + { + integrity: sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==, + } '@types/unist@3.0.3': - resolution: {integrity: sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==} + resolution: + { + integrity: sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==, + } '@ungap/structured-clone@1.4.0': - resolution: {integrity: sha512-1mEZtMKPM09vDmQt5y7YvmN2+DFTP7Tg0EWXdic8/C6VRnpb33e4ghisCIE3WZjsE2N8mf+QV1Zqh7ZFYLWInQ==} + resolution: + { + integrity: sha512-1mEZtMKPM09vDmQt5y7YvmN2+DFTP7Tg0EWXdic8/C6VRnpb33e4ghisCIE3WZjsE2N8mf+QV1Zqh7ZFYLWInQ==, + } '@vercel/analytics@2.0.1': - resolution: {integrity: sha512-MTQG6V9qQrt1tsDeF+2Uoo5aPjqbVPys1xvnIftXSJYG2SrwXRHnqEvVoYID7BTruDz4lCd2Z7rM1BdkUehk2g==} + resolution: + { + integrity: sha512-MTQG6V9qQrt1tsDeF+2Uoo5aPjqbVPys1xvnIftXSJYG2SrwXRHnqEvVoYID7BTruDz4lCd2Z7rM1BdkUehk2g==, + } peerDependencies: '@remix-run/react': ^2 '@sveltejs/kit': ^1 || ^2 @@ -1531,10 +2292,16 @@ packages: optional: true '@vitest/expect@4.1.11': - resolution: {integrity: sha512-VX2x5vNJXET47KAFzwERI+KRMtTTCSWTfSMKsW7JsUsXV4psq++e3DvZpuTDOpHcxytiDs6p2nhVb2tVDiiUYw==} + resolution: + { + integrity: sha512-VX2x5vNJXET47KAFzwERI+KRMtTTCSWTfSMKsW7JsUsXV4psq++e3DvZpuTDOpHcxytiDs6p2nhVb2tVDiiUYw==, + } '@vitest/mocker@4.1.11': - resolution: {integrity: sha512-2XJVD55d1o5AZous5CCGKS74g/riOj9odEt2bQpCVZeblHyHdnMeFl4jl0XjU21stf4mbjUkew2eXQZt65g5CQ==} + resolution: + { + integrity: sha512-2XJVD55d1o5AZous5CCGKS74g/riOj9odEt2bQpCVZeblHyHdnMeFl4jl0XjU21stf4mbjUkew2eXQZt65g5CQ==, + } peerDependencies: msw: ^2.4.9 vite: ^6.0.0 || ^7.0.0 || ^8.0.0 @@ -1545,417 +2312,705 @@ packages: optional: true '@vitest/pretty-format@4.1.11': - resolution: {integrity: sha512-yiZzPbGTS9Sr/JpFl8zHrcIkAofNbFV6k21vIgQN/cY/oxZeXhJv5sc/MBJ5jFKWmWs+oJHw0UXLZjmf931+Vw==} + resolution: + { + integrity: sha512-yiZzPbGTS9Sr/JpFl8zHrcIkAofNbFV6k21vIgQN/cY/oxZeXhJv5sc/MBJ5jFKWmWs+oJHw0UXLZjmf931+Vw==, + } '@vitest/runner@4.1.11': - resolution: {integrity: sha512-LztvUgdwMNJMIkj3hQnnxiC2Xy1zNxq928W/xhjCLaNCzqTZOudjwbQf6v9IntZGPw132i2Lq2rgTRZHD3JHNw==} + resolution: + { + integrity: sha512-LztvUgdwMNJMIkj3hQnnxiC2Xy1zNxq928W/xhjCLaNCzqTZOudjwbQf6v9IntZGPw132i2Lq2rgTRZHD3JHNw==, + } '@vitest/snapshot@4.1.11': - resolution: {integrity: sha512-pN7ikn1ON7h8ee4gIAp4AzyK+zBtJPzVbqOgu5LCEh4VaJVbPQcgYQYJIMGQPXVeJJq1fnfazis7a5pFNPahog==} + resolution: + { + integrity: sha512-pN7ikn1ON7h8ee4gIAp4AzyK+zBtJPzVbqOgu5LCEh4VaJVbPQcgYQYJIMGQPXVeJJq1fnfazis7a5pFNPahog==, + } '@vitest/spy@4.1.11': - resolution: {integrity: sha512-apNa/prQy2qCeywhnixOHPRCgGNhvg7T4Dapfl1GahLp/R+uhBm5cPyFoNVyqsNd2h1nJxL6BqqdIjiABL60YA==} + resolution: + { + integrity: sha512-apNa/prQy2qCeywhnixOHPRCgGNhvg7T4Dapfl1GahLp/R+uhBm5cPyFoNVyqsNd2h1nJxL6BqqdIjiABL60YA==, + } '@vitest/utils@4.1.11': - resolution: {integrity: sha512-zTCVGpyFsGWBhllOyKlTw/vnr6D9qxsfSDyfbyZmTyjHw5N/VuvzHpHoQjm2ZJzn4RJgx5w4r7V0er69CmLgPQ==} + resolution: + { + integrity: sha512-zTCVGpyFsGWBhllOyKlTw/vnr6D9qxsfSDyfbyZmTyjHw5N/VuvzHpHoQjm2ZJzn4RJgx5w4r7V0er69CmLgPQ==, + } '@yuku-analyzer/binding-android-arm64@0.9.3': - resolution: {integrity: sha512-6dOwkawiJYtUUBchfjrFo7poz460Yg+aQNgr4lHUPZ2fNW+llpMEZkbznTq25BEmmiBGPJr+L15dzTvR8zP4vQ==} + resolution: + { + integrity: sha512-6dOwkawiJYtUUBchfjrFo7poz460Yg+aQNgr4lHUPZ2fNW+llpMEZkbznTq25BEmmiBGPJr+L15dzTvR8zP4vQ==, + } cpu: [arm64] os: [android] '@yuku-analyzer/binding-darwin-arm64@0.9.3': - resolution: {integrity: sha512-DTRoWK7AqNfshN+DcCS/s4n86br0ZusISeXLZWWNuvnZ3b9LCVXdWRVPlnMpEwsmSH3c3QSwL7MAOPyEHMe+FA==} + resolution: + { + integrity: sha512-DTRoWK7AqNfshN+DcCS/s4n86br0ZusISeXLZWWNuvnZ3b9LCVXdWRVPlnMpEwsmSH3c3QSwL7MAOPyEHMe+FA==, + } cpu: [arm64] os: [darwin] '@yuku-analyzer/binding-darwin-x64@0.9.3': - resolution: {integrity: sha512-EWzaR0/AL3ikZfUiADG1SmbB+wF7GGhXFKhMWm3RbOTf+ATyEW7Fa9nsjo1K70pWEeVZHRm9MU4iq7LIUEEgfg==} + resolution: + { + integrity: sha512-EWzaR0/AL3ikZfUiADG1SmbB+wF7GGhXFKhMWm3RbOTf+ATyEW7Fa9nsjo1K70pWEeVZHRm9MU4iq7LIUEEgfg==, + } cpu: [x64] os: [darwin] '@yuku-analyzer/binding-freebsd-x64@0.9.3': - resolution: {integrity: sha512-Ao4/v+ppzIFVs2SldvBM2hc9NsijXNsKc1xUY+d+Nv20HrjRdm96HtrkIViw7zI5i6Ca77jkd8VbhWE8E4kgyg==} + resolution: + { + integrity: sha512-Ao4/v+ppzIFVs2SldvBM2hc9NsijXNsKc1xUY+d+Nv20HrjRdm96HtrkIViw7zI5i6Ca77jkd8VbhWE8E4kgyg==, + } cpu: [x64] os: [freebsd] '@yuku-analyzer/binding-linux-arm-gnu@0.9.3': - resolution: {integrity: sha512-EZc2H6bAyl4u3z48Jtqf4Un1657TpOkBWEmSdPCY1tHCO4YUFaNAz4dPA93yWgo5t38oRQT9UoXlwse/4aLtxw==} + resolution: + { + integrity: sha512-EZc2H6bAyl4u3z48Jtqf4Un1657TpOkBWEmSdPCY1tHCO4YUFaNAz4dPA93yWgo5t38oRQT9UoXlwse/4aLtxw==, + } cpu: [arm] os: [linux] libc: [glibc] '@yuku-analyzer/binding-linux-arm-musl@0.9.3': - resolution: {integrity: sha512-pKny3wEa2Xl4kPoqNU8fO5NCzcp9VgMAkLpN3GZ5TOkdz23ppuzUqddlhad43/9puxfckX5aZqfxML/1vGoxBw==} + resolution: + { + integrity: sha512-pKny3wEa2Xl4kPoqNU8fO5NCzcp9VgMAkLpN3GZ5TOkdz23ppuzUqddlhad43/9puxfckX5aZqfxML/1vGoxBw==, + } cpu: [arm] os: [linux] libc: [musl] '@yuku-analyzer/binding-linux-arm64-gnu@0.9.3': - resolution: {integrity: sha512-bfpsIupfbX7N4ueSjo3Zm+Mob0q7MjrZX+INMGcPjNnXBcyKjkzrTotkqbEbwgfiNKhqyDVjynwsh64xsv5IPg==} + resolution: + { + integrity: sha512-bfpsIupfbX7N4ueSjo3Zm+Mob0q7MjrZX+INMGcPjNnXBcyKjkzrTotkqbEbwgfiNKhqyDVjynwsh64xsv5IPg==, + } cpu: [arm64] os: [linux] libc: [glibc] '@yuku-analyzer/binding-linux-arm64-musl@0.9.3': - resolution: {integrity: sha512-Q+AAUrrsgYgHb0/Wrm+HnSKN7xOpkC+6Y2812dZw5/rrSX7+qjRjJ+3uNnCZlkHpH3Hy7WXbLIa/IncP4bcHQA==} + resolution: + { + integrity: sha512-Q+AAUrrsgYgHb0/Wrm+HnSKN7xOpkC+6Y2812dZw5/rrSX7+qjRjJ+3uNnCZlkHpH3Hy7WXbLIa/IncP4bcHQA==, + } cpu: [arm64] os: [linux] libc: [musl] '@yuku-analyzer/binding-linux-x64-gnu@0.9.3': - resolution: {integrity: sha512-ZQyjmSDRHTkDlddrzmIG1/nMUYDZC21XUguzQ4qvSWtfq6CsfJvIDFBPfJ03YDNQ+wR0px9Ly/xAa8VC0p9rHQ==} + resolution: + { + integrity: sha512-ZQyjmSDRHTkDlddrzmIG1/nMUYDZC21XUguzQ4qvSWtfq6CsfJvIDFBPfJ03YDNQ+wR0px9Ly/xAa8VC0p9rHQ==, + } cpu: [x64] os: [linux] libc: [glibc] '@yuku-analyzer/binding-linux-x64-musl@0.9.3': - resolution: {integrity: sha512-EIYzThB5pI3BiZHFNYyY8nMQ38z9l8/kT8uYvfYVpZ9TNEC0YgqX95MH61l9RILCYFDx+DbLoGajGY55JFvb6Q==} + resolution: + { + integrity: sha512-EIYzThB5pI3BiZHFNYyY8nMQ38z9l8/kT8uYvfYVpZ9TNEC0YgqX95MH61l9RILCYFDx+DbLoGajGY55JFvb6Q==, + } cpu: [x64] os: [linux] libc: [musl] '@yuku-analyzer/binding-win32-arm64@0.9.3': - resolution: {integrity: sha512-KF+Ho3jtjikfPYJ76NL1KiPYwF+0UjnsASX0k461BZ1Er4pRQG39o8WvsngLT+fDg6JxB3v5J+ScDrto29RVnA==} + resolution: + { + integrity: sha512-KF+Ho3jtjikfPYJ76NL1KiPYwF+0UjnsASX0k461BZ1Er4pRQG39o8WvsngLT+fDg6JxB3v5J+ScDrto29RVnA==, + } cpu: [arm64] os: [win32] '@yuku-analyzer/binding-win32-x64@0.9.3': - resolution: {integrity: sha512-4pdUfYVYPf05vyygvWiXsiI/tdPqn5bBiHs7c1TBIm3Kx7/w5pq++myNQ/8CUH/Proz+1kHtCr/lDJF9k67fqg==} + resolution: + { + integrity: sha512-4pdUfYVYPf05vyygvWiXsiI/tdPqn5bBiHs7c1TBIm3Kx7/w5pq++myNQ/8CUH/Proz+1kHtCr/lDJF9k67fqg==, + } cpu: [x64] os: [win32] '@yuku-codegen/binding-android-arm64@0.8.7': - resolution: {integrity: sha512-C/0zV5IhgVdYhGJTwrY0v8dknxlhiKwtVJkMUaexu9/QvRmzlV4vfU3hZlUSgqc2BxQHntL1mCVbDq8j0FRFDw==} + resolution: + { + integrity: sha512-C/0zV5IhgVdYhGJTwrY0v8dknxlhiKwtVJkMUaexu9/QvRmzlV4vfU3hZlUSgqc2BxQHntL1mCVbDq8j0FRFDw==, + } cpu: [arm64] os: [android] '@yuku-codegen/binding-darwin-arm64@0.8.7': - resolution: {integrity: sha512-/u+REDMI4a0/lsJXTM4c53/w31OGZLOReZIyg62uhgLs0kc8NHsj/nOcxTdlQjq5gi0zhdkccD9LaLTXcdzPvw==} + resolution: + { + integrity: sha512-/u+REDMI4a0/lsJXTM4c53/w31OGZLOReZIyg62uhgLs0kc8NHsj/nOcxTdlQjq5gi0zhdkccD9LaLTXcdzPvw==, + } cpu: [arm64] os: [darwin] '@yuku-codegen/binding-darwin-x64@0.8.7': - resolution: {integrity: sha512-sMMzFOwCo4WXR+/6zIBThOocSC50iIIZZdfiIDbaLvj0Ax/rWt/iavyfEAqajyvzydLyCqR/ZItdLWSRlu1umw==} + resolution: + { + integrity: sha512-sMMzFOwCo4WXR+/6zIBThOocSC50iIIZZdfiIDbaLvj0Ax/rWt/iavyfEAqajyvzydLyCqR/ZItdLWSRlu1umw==, + } cpu: [x64] os: [darwin] '@yuku-codegen/binding-freebsd-x64@0.8.7': - resolution: {integrity: sha512-MpdpKXix9P+Y1rKgjvcNeNtGjXeL1CmttNhYINrWls8kRpm4xM/oBGTmn6w7to8lAlwj5jm8q03dQPl5mRv4Qw==} + resolution: + { + integrity: sha512-MpdpKXix9P+Y1rKgjvcNeNtGjXeL1CmttNhYINrWls8kRpm4xM/oBGTmn6w7to8lAlwj5jm8q03dQPl5mRv4Qw==, + } cpu: [x64] os: [freebsd] '@yuku-codegen/binding-linux-arm-gnu@0.8.7': - resolution: {integrity: sha512-rr1srFLlPAmC1vtxfc9C1YLDe3iH09YjfSeeIidBqKhzx1MATjOAq4mjlRUOnhr/L27MotWIYOFKwVsd5JZFOg==} + resolution: + { + integrity: sha512-rr1srFLlPAmC1vtxfc9C1YLDe3iH09YjfSeeIidBqKhzx1MATjOAq4mjlRUOnhr/L27MotWIYOFKwVsd5JZFOg==, + } cpu: [arm] os: [linux] libc: [glibc] '@yuku-codegen/binding-linux-arm-musl@0.8.7': - resolution: {integrity: sha512-eAufXh8qBRpiSO6ueaMDL+yyoXIGLhpUce72YbcACtZU2qhExwBIJyEtQf5kH2Ki0X2aqkSjSfog4OPtKXan3Q==} + resolution: + { + integrity: sha512-eAufXh8qBRpiSO6ueaMDL+yyoXIGLhpUce72YbcACtZU2qhExwBIJyEtQf5kH2Ki0X2aqkSjSfog4OPtKXan3Q==, + } cpu: [arm] os: [linux] libc: [musl] '@yuku-codegen/binding-linux-arm64-gnu@0.8.7': - resolution: {integrity: sha512-gw4w6wPoHObBrdIC4duVWLmJOvpdE25j5D7yrM5mACNlK4klRz/lv8hK+ssQk9EJHBgZjSaqZIJVFgqNYbfv7A==} + resolution: + { + integrity: sha512-gw4w6wPoHObBrdIC4duVWLmJOvpdE25j5D7yrM5mACNlK4klRz/lv8hK+ssQk9EJHBgZjSaqZIJVFgqNYbfv7A==, + } cpu: [arm64] os: [linux] libc: [glibc] '@yuku-codegen/binding-linux-arm64-musl@0.8.7': - resolution: {integrity: sha512-L68N6Y4XkqcIaKo3Ra88JEvBEH4AHff44A4INcrxeVWZ8CZtu2tCpfVxe3hR8qQQMcvBSQlDn24KpkCKEhVvfA==} + resolution: + { + integrity: sha512-L68N6Y4XkqcIaKo3Ra88JEvBEH4AHff44A4INcrxeVWZ8CZtu2tCpfVxe3hR8qQQMcvBSQlDn24KpkCKEhVvfA==, + } cpu: [arm64] os: [linux] libc: [musl] '@yuku-codegen/binding-linux-x64-gnu@0.8.7': - resolution: {integrity: sha512-dzyAbltJmf3Cqlb8HcFuYIf5Yn0fl1vTr3XJ9HiVNNvOlhqPSArOqtw9vI1p6/VTXTjrMLXlU+s+/kNHiIy/Cw==} + resolution: + { + integrity: sha512-dzyAbltJmf3Cqlb8HcFuYIf5Yn0fl1vTr3XJ9HiVNNvOlhqPSArOqtw9vI1p6/VTXTjrMLXlU+s+/kNHiIy/Cw==, + } cpu: [x64] os: [linux] libc: [glibc] '@yuku-codegen/binding-linux-x64-musl@0.8.7': - resolution: {integrity: sha512-Otw4MH3404q0Bbvl+YTdW9aoUV5vXmUw8260bWvt1XlaoIX/ceSgI4ygheRDMfBPoHt6FDayQaO9OLVUZAgkFA==} + resolution: + { + integrity: sha512-Otw4MH3404q0Bbvl+YTdW9aoUV5vXmUw8260bWvt1XlaoIX/ceSgI4ygheRDMfBPoHt6FDayQaO9OLVUZAgkFA==, + } cpu: [x64] os: [linux] libc: [musl] '@yuku-codegen/binding-win32-arm64@0.8.7': - resolution: {integrity: sha512-qo/jyrzryiBuKEsFiuWaBCBe3tRMynQ0qFWFgOEjcCMQeZfBm+wKiVEUEFXXLc7bh8YezguAWp0Mtnhq4ARNyA==} + resolution: + { + integrity: sha512-qo/jyrzryiBuKEsFiuWaBCBe3tRMynQ0qFWFgOEjcCMQeZfBm+wKiVEUEFXXLc7bh8YezguAWp0Mtnhq4ARNyA==, + } cpu: [arm64] os: [win32] '@yuku-codegen/binding-win32-x64@0.8.7': - resolution: {integrity: sha512-D5lDsVDx6m00E6bWySlWdH72Ca4TPSaphDqB6QjU6MpuNLIJqoGoatYyq2rOmBE8Zv/kunot/o58KGL03P3eiA==} + resolution: + { + integrity: sha512-D5lDsVDx6m00E6bWySlWdH72Ca4TPSaphDqB6QjU6MpuNLIJqoGoatYyq2rOmBE8Zv/kunot/o58KGL03P3eiA==, + } cpu: [x64] os: [win32] '@yuku-parser/binding-android-arm64@0.8.7': - resolution: {integrity: sha512-eGKYiUDX7Y0V7tDTmg+JTVnXnjMqfXXsorZ+EDf5kxwchQ3Or1HS14MzI2fw+jFhHR85fCWt+mtX33Yao73hIQ==} + resolution: + { + integrity: sha512-eGKYiUDX7Y0V7tDTmg+JTVnXnjMqfXXsorZ+EDf5kxwchQ3Or1HS14MzI2fw+jFhHR85fCWt+mtX33Yao73hIQ==, + } cpu: [arm64] os: [android] '@yuku-parser/binding-darwin-arm64@0.8.7': - resolution: {integrity: sha512-Re0RHelKLnjEURulY2/KxW+Ngb8zuNA4BRZuMwgGQNzVumT6u4U2N2hc01oeYVNVof0i7GrXE4UCNBgbpRRnjQ==} + resolution: + { + integrity: sha512-Re0RHelKLnjEURulY2/KxW+Ngb8zuNA4BRZuMwgGQNzVumT6u4U2N2hc01oeYVNVof0i7GrXE4UCNBgbpRRnjQ==, + } cpu: [arm64] os: [darwin] '@yuku-parser/binding-darwin-x64@0.8.7': - resolution: {integrity: sha512-Hn8DROtQkjlA1ACbPgj4a7eP9IuVOI504oiTwpkWPbpaDWD9KdmnVYCqW+1LfenNK/g7O9NhWGpXEdaCNX7lIA==} + resolution: + { + integrity: sha512-Hn8DROtQkjlA1ACbPgj4a7eP9IuVOI504oiTwpkWPbpaDWD9KdmnVYCqW+1LfenNK/g7O9NhWGpXEdaCNX7lIA==, + } cpu: [x64] os: [darwin] '@yuku-parser/binding-freebsd-x64@0.8.7': - resolution: {integrity: sha512-bAP2OV8wRuzplX/jYxv9+vvqQT8JxyNphI8fLfXGL054Xs+4/J5u33cIm3y4rxY8rdoLmmdiJs2Tq7r7lrDRfA==} + resolution: + { + integrity: sha512-bAP2OV8wRuzplX/jYxv9+vvqQT8JxyNphI8fLfXGL054Xs+4/J5u33cIm3y4rxY8rdoLmmdiJs2Tq7r7lrDRfA==, + } cpu: [x64] os: [freebsd] '@yuku-parser/binding-linux-arm-gnu@0.8.7': - resolution: {integrity: sha512-kTYwJQQgmZeAWdDIWabiReIZMpmfLueIj1tCmjStUtFGhR1Z0qwxonKVfUC4N7h/VhGGzLZ//7O1kgt1QKqgCg==} + resolution: + { + integrity: sha512-kTYwJQQgmZeAWdDIWabiReIZMpmfLueIj1tCmjStUtFGhR1Z0qwxonKVfUC4N7h/VhGGzLZ//7O1kgt1QKqgCg==, + } cpu: [arm] os: [linux] libc: [glibc] '@yuku-parser/binding-linux-arm-musl@0.8.7': - resolution: {integrity: sha512-uL4jE8HPT2BLlxAXyD10LqgPuXa9eDa0BKpCdSANmzIJghq/2eZo3/gQNtaxPZMupWoxjYzSad9IXrwu7aYPXQ==} + resolution: + { + integrity: sha512-uL4jE8HPT2BLlxAXyD10LqgPuXa9eDa0BKpCdSANmzIJghq/2eZo3/gQNtaxPZMupWoxjYzSad9IXrwu7aYPXQ==, + } cpu: [arm] os: [linux] libc: [musl] '@yuku-parser/binding-linux-arm64-gnu@0.8.7': - resolution: {integrity: sha512-3gVN4pWSKZmXiNX7cU164dR9MPvesCHnlH6nPfpK+yQsCuYphjKKplcb4SnZBrhiqmXbgb2HR0c2TS0IZUPhgA==} + resolution: + { + integrity: sha512-3gVN4pWSKZmXiNX7cU164dR9MPvesCHnlH6nPfpK+yQsCuYphjKKplcb4SnZBrhiqmXbgb2HR0c2TS0IZUPhgA==, + } cpu: [arm64] os: [linux] libc: [glibc] '@yuku-parser/binding-linux-arm64-musl@0.8.7': - resolution: {integrity: sha512-S0mwfEjoLpxzXeZw802Wa4RaELsQiPtWqG6INcy8j4GtvNFtl4LCX3eGO1XLn9pyLAISLzTRyU3zUCBUPin8lg==} + resolution: + { + integrity: sha512-S0mwfEjoLpxzXeZw802Wa4RaELsQiPtWqG6INcy8j4GtvNFtl4LCX3eGO1XLn9pyLAISLzTRyU3zUCBUPin8lg==, + } cpu: [arm64] os: [linux] libc: [musl] '@yuku-parser/binding-linux-x64-gnu@0.8.7': - resolution: {integrity: sha512-lnbWdPmerE5D1uH1G4IEZKnPzCrWCStRGrtgpSIe1RibAo5bZIjDbbbPYXmMHCEh4F+x/JaJpElh26a3r+BPbg==} + resolution: + { + integrity: sha512-lnbWdPmerE5D1uH1G4IEZKnPzCrWCStRGrtgpSIe1RibAo5bZIjDbbbPYXmMHCEh4F+x/JaJpElh26a3r+BPbg==, + } cpu: [x64] os: [linux] libc: [glibc] '@yuku-parser/binding-linux-x64-musl@0.8.7': - resolution: {integrity: sha512-769uwndMvMzUvATWbAcEvyLHKA+DzhHSCl/obBUrRdYfRo26yxui6S8y3z7uJ+Naup7UKrDxrpK7OnQkxkl9KQ==} + resolution: + { + integrity: sha512-769uwndMvMzUvATWbAcEvyLHKA+DzhHSCl/obBUrRdYfRo26yxui6S8y3z7uJ+Naup7UKrDxrpK7OnQkxkl9KQ==, + } cpu: [x64] os: [linux] libc: [musl] '@yuku-parser/binding-win32-arm64@0.8.7': - resolution: {integrity: sha512-mEB/9PlaAkisJ6KWGz0zvywXoU6+80dTlR2LwS7s/jcXXoU6fm2+sitBZXtqu3+Q4DcDgPxM45uWMCzPs0TSRw==} + resolution: + { + integrity: sha512-mEB/9PlaAkisJ6KWGz0zvywXoU6+80dTlR2LwS7s/jcXXoU6fm2+sitBZXtqu3+Q4DcDgPxM45uWMCzPs0TSRw==, + } cpu: [arm64] os: [win32] '@yuku-parser/binding-win32-x64@0.8.7': - resolution: {integrity: sha512-8vNB2DP0ou61nGb8tc/qfi41gfyDXz1MHr2zqL3nR+cJ6CEbiuWV/l/a/vv151gCgiZLLAyGkQGENpozdg716w==} + resolution: + { + integrity: sha512-8vNB2DP0ou61nGb8tc/qfi41gfyDXz1MHr2zqL3nR+cJ6CEbiuWV/l/a/vv151gCgiZLLAyGkQGENpozdg716w==, + } cpu: [x64] os: [win32] '@yuku-toolchain/types@0.8.7': - resolution: {integrity: sha512-2Z53dNxAJL6UvFoIrDZvYf3zlO8s4VJK4O2hhaB4mXVwwpX/7ajtss3cmfqKvamlNLWyt9FSWs4eoYdlbxpnHA==} + resolution: + { + integrity: sha512-2Z53dNxAJL6UvFoIrDZvYf3zlO8s4VJK4O2hhaB4mXVwwpX/7ajtss3cmfqKvamlNLWyt9FSWs4eoYdlbxpnHA==, + } '@yuku-toolchain/types@0.9.3': - resolution: {integrity: sha512-rFE+5P4g2wxko5C85MugJOlVjBHEQq87dIkhLkniLXLp63PEtgaFjD954i5HXlfnyzLxPcZHsSOVDVgmo1HToA==} + resolution: + { + integrity: sha512-rFE+5P4g2wxko5C85MugJOlVjBHEQq87dIkhLkniLXLp63PEtgaFjD954i5HXlfnyzLxPcZHsSOVDVgmo1HToA==, + } acorn-jsx@5.3.2: - resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} + resolution: + { + integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==, + } peerDependencies: acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 acorn@8.18.0: - resolution: {integrity: sha512-lGq+9yr1/GuAWaVYIHRjvvySG5/4VfKIvC8EWxStPdcDh/Ka7FG3twP6v4d5BkravUilhIAsG4Qj83t02LWUPQ==} - engines: {node: '>=0.4.0'} + resolution: + { + integrity: sha512-lGq+9yr1/GuAWaVYIHRjvvySG5/4VfKIvC8EWxStPdcDh/Ka7FG3twP6v4d5BkravUilhIAsG4Qj83t02LWUPQ==, + } + engines: { node: '>=0.4.0' } hasBin: true agent-base@7.1.4: - resolution: {integrity: sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==} - engines: {node: '>= 14'} + resolution: + { + integrity: sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==, + } + engines: { node: '>= 14' } ansi-escapes@7.3.0: - resolution: {integrity: sha512-BvU8nYgGQBxcmMuEeUEmNTvrMVjJNSH7RgW24vXexN4Ven6qCvy4TntnvlnwnMLTVlcRQQdbRY8NKnaIoeWDNg==} - engines: {node: '>=18'} + resolution: + { + integrity: sha512-BvU8nYgGQBxcmMuEeUEmNTvrMVjJNSH7RgW24vXexN4Ven6qCvy4TntnvlnwnMLTVlcRQQdbRY8NKnaIoeWDNg==, + } + engines: { node: '>=18' } ansi-regex@5.0.1: - resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} - engines: {node: '>=8'} + resolution: + { + integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==, + } + engines: { node: '>=8' } ansi-regex@6.3.0: - resolution: {integrity: sha512-WpDfL7NO6j7tH88IDBNVdUJxDh9nmCteAVW9dsep846XdwF4naCBK+/tGLX3KJgcpgMRXCFlTM2hKGoK9FsdrQ==} - engines: {node: '>=12'} + resolution: + { + integrity: sha512-WpDfL7NO6j7tH88IDBNVdUJxDh9nmCteAVW9dsep846XdwF4naCBK+/tGLX3KJgcpgMRXCFlTM2hKGoK9FsdrQ==, + } + engines: { node: '>=12' } ansi-styles@4.3.0: - resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} - engines: {node: '>=8'} + resolution: + { + integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==, + } + engines: { node: '>=8' } ansi-styles@5.2.0: - resolution: {integrity: sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==} - engines: {node: '>=10'} + resolution: + { + integrity: sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==, + } + engines: { node: '>=10' } ansis@4.3.1: - resolution: {integrity: sha512-BJ8/l4R5LRE7hW9WdSuGYrLSHi2ynxeFpDFbH0K/CgNeY/tyhk+vO6TYxXC5r5CpUhNVX310xzPsN/H9lCdfOA==} - engines: {node: '>=14'} + resolution: + { + integrity: sha512-BJ8/l4R5LRE7hW9WdSuGYrLSHi2ynxeFpDFbH0K/CgNeY/tyhk+vO6TYxXC5r5CpUhNVX310xzPsN/H9lCdfOA==, + } + engines: { node: '>=14' } any-promise@1.3.0: - resolution: {integrity: sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==} + resolution: + { + integrity: sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==, + } aria-query@5.3.0: - resolution: {integrity: sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==} + resolution: + { + integrity: sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==, + } assertion-error@2.0.1: - resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==} - engines: {node: '>=12'} + resolution: + { + integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==, + } + engines: { node: '>=12' } astring@1.9.0: - resolution: {integrity: sha512-LElXdjswlqjWrPpJFg1Fx4wpkOCxj1TDHlSV4PlaRxHGWko024xICaa97ZkMfs6DRKlCguiAI+rbXv5GWwXIkg==} + resolution: + { + integrity: sha512-LElXdjswlqjWrPpJFg1Fx4wpkOCxj1TDHlSV4PlaRxHGWko024xICaa97ZkMfs6DRKlCguiAI+rbXv5GWwXIkg==, + } hasBin: true bail@2.0.2: - resolution: {integrity: sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==} + resolution: + { + integrity: sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==, + } balanced-match@4.0.4: - resolution: {integrity: sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==} - engines: {node: 18 || 20 || >=22} + resolution: + { + integrity: sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==, + } + engines: { node: 18 || 20 || >=22 } baseline-browser-mapping@2.11.20: - resolution: {integrity: sha512-H0ulySigv6icDJ1F7SjtdCD6PrhTpdYCmP0CactWy1+ekh0AFd0o1Wn5T8b+hnTmdBx19u9yhL6wvCylXMY7zw==} - engines: {node: '>=6.0.0'} + resolution: + { + integrity: sha512-H0ulySigv6icDJ1F7SjtdCD6PrhTpdYCmP0CactWy1+ekh0AFd0o1Wn5T8b+hnTmdBx19u9yhL6wvCylXMY7zw==, + } + engines: { node: '>=6.0.0' } hasBin: true bidi-js@1.0.3: - resolution: {integrity: sha512-RKshQI1R3YQ+n9YJz2QQ147P66ELpa1FQEg20Dk8oW9t2KgLbpDLLp9aGZ7y8WHSshDknG0bknqGw5/tyCs5tw==} + resolution: + { + integrity: sha512-RKshQI1R3YQ+n9YJz2QQ147P66ELpa1FQEg20Dk8oW9t2KgLbpDLLp9aGZ7y8WHSshDknG0bknqGw5/tyCs5tw==, + } brace-expansion@5.0.9: - resolution: {integrity: sha512-ScQ4IuvIEF1TMlP7Zt+vjJ//9zlPb2SDcxWxM3bk8s6t6GGdJ7KO1dCcTidOPJKePW30LE/2cT7wCyPho9/Wxg==} - engines: {node: 20 || >=22} + resolution: + { + integrity: sha512-ScQ4IuvIEF1TMlP7Zt+vjJ//9zlPb2SDcxWxM3bk8s6t6GGdJ7KO1dCcTidOPJKePW30LE/2cT7wCyPho9/Wxg==, + } + engines: { node: 20 || >=22 } cac@7.0.0: - resolution: {integrity: sha512-tixWYgm5ZoOD+3g6UTea91eow5z6AAHaho3g0V9CNSNb45gM8SmflpAc+GRd1InC4AqN/07Unrgp56Y94N9hJQ==} - engines: {node: '>=20.19.0'} + resolution: + { + integrity: sha512-tixWYgm5ZoOD+3g6UTea91eow5z6AAHaho3g0V9CNSNb45gM8SmflpAc+GRd1InC4AqN/07Unrgp56Y94N9hJQ==, + } + engines: { node: '>=20.19.0' } caniuse-lite@1.0.30001810: - resolution: {integrity: sha512-TITQPUkaz+aVk5GL6NhOdwk1aEaNTSDPsGFWrTuhKGtjTF70jL/Oht2W4c6rXUe5fu7Ie19VIahAXHIIiWWNeg==} + resolution: + { + integrity: sha512-TITQPUkaz+aVk5GL6NhOdwk1aEaNTSDPsGFWrTuhKGtjTF70jL/Oht2W4c6rXUe5fu7Ie19VIahAXHIIiWWNeg==, + } ccount@2.0.1: - resolution: {integrity: sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==} + resolution: + { + integrity: sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==, + } chai@6.2.2: - resolution: {integrity: sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==} - engines: {node: '>=18'} + resolution: + { + integrity: sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==, + } + engines: { node: '>=18' } chalk@4.1.2: - resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} - engines: {node: '>=10'} + resolution: + { + integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==, + } + engines: { node: '>=10' } chalk@5.6.2: - resolution: {integrity: sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==} - engines: {node: ^12.17.0 || ^14.13 || >=16.0.0} + resolution: + { + integrity: sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==, + } + engines: { node: ^12.17.0 || ^14.13 || >=16.0.0 } char-regex@1.0.2: - resolution: {integrity: sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==} - engines: {node: '>=10'} + resolution: + { + integrity: sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==, + } + engines: { node: '>=10' } character-entities-html4@2.1.0: - resolution: {integrity: sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==} + resolution: + { + integrity: sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==, + } character-entities-legacy@3.0.0: - resolution: {integrity: sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==} + resolution: + { + integrity: sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==, + } character-entities@2.0.2: - resolution: {integrity: sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==} + resolution: + { + integrity: sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==, + } character-reference-invalid@2.0.1: - resolution: {integrity: sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw==} + resolution: + { + integrity: sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw==, + } chokidar@5.0.0: - resolution: {integrity: sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw==} - engines: {node: '>= 20.19.0'} + resolution: + { + integrity: sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw==, + } + engines: { node: '>= 20.19.0' } cjs-module-lexer@1.4.3: - resolution: {integrity: sha512-9z8TZaGM1pfswYeXrUpzPrkx8UnWYdhJclsiYMm6x/w5+nN+8Tf/LnAgfLGQCm59qAOxU8WwHEq2vNwF6i4j+Q==} + resolution: + { + integrity: sha512-9z8TZaGM1pfswYeXrUpzPrkx8UnWYdhJclsiYMm6x/w5+nN+8Tf/LnAgfLGQCm59qAOxU8WwHEq2vNwF6i4j+Q==, + } class-variance-authority@0.7.1: - resolution: {integrity: sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg==} + resolution: + { + integrity: sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg==, + } cli-highlight@2.1.11: - resolution: {integrity: sha512-9KDcoEVwyUXrjcJNvHD0NFc/hiwe/WPVYIleQh2O1N2Zro5gWJZ/K+3DGn8w8P/F6FxOgzyC5bxDyHIgCSPhGg==} - engines: {node: '>=8.0.0', npm: '>=5.0.0'} + resolution: + { + integrity: sha512-9KDcoEVwyUXrjcJNvHD0NFc/hiwe/WPVYIleQh2O1N2Zro5gWJZ/K+3DGn8w8P/F6FxOgzyC5bxDyHIgCSPhGg==, + } + engines: { node: '>=8.0.0', npm: '>=5.0.0' } hasBin: true cli-table3@0.6.5: - resolution: {integrity: sha512-+W/5efTR7y5HRD7gACw9yQjqMVvEMLBHmboM/kPWam+H+Hmyrgjh6YncVKK122YZkXrLudzTuAukUw9FnMf7IQ==} - engines: {node: 10.* || >= 12.*} + resolution: + { + integrity: sha512-+W/5efTR7y5HRD7gACw9yQjqMVvEMLBHmboM/kPWam+H+Hmyrgjh6YncVKK122YZkXrLudzTuAukUw9FnMf7IQ==, + } + engines: { node: 10.* || >= 12.* } client-only@0.0.1: - resolution: {integrity: sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==} + resolution: + { + integrity: sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==, + } cliui@7.0.4: - resolution: {integrity: sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==} + resolution: + { + integrity: sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==, + } cliui@8.0.1: - resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==} - engines: {node: '>=12'} + resolution: + { + integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==, + } + engines: { node: '>=12' } clsx@2.1.1: - resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==} - engines: {node: '>=6'} + resolution: + { + integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==, + } + engines: { node: '>=6' } cnfast@0.1.0: - resolution: {integrity: sha512-rH0jBKeLkVrK7NsZ5Ba2l7WdMBmm1k0FMpABeXUU1PgTUxbz3261gEuCSsrYuXo4BwAe3yCcIbQ93YyS52lOGQ==} + resolution: + { + integrity: sha512-rH0jBKeLkVrK7NsZ5Ba2l7WdMBmm1k0FMpABeXUU1PgTUxbz3261gEuCSsrYuXo4BwAe3yCcIbQ93YyS52lOGQ==, + } hasBin: true code-block-writer@13.0.3: - resolution: {integrity: sha512-Oofo0pq3IKnsFtuHqSF7TqBfr71aeyZDVJ0HpmqB7FBM2qEigL0iPONSCZSO9pE9dZTAxANe5XHG9Uy0YMv8cg==} + resolution: + { + integrity: sha512-Oofo0pq3IKnsFtuHqSF7TqBfr71aeyZDVJ0HpmqB7FBM2qEigL0iPONSCZSO9pE9dZTAxANe5XHG9Uy0YMv8cg==, + } collapse-white-space@2.1.0: - resolution: {integrity: sha512-loKTxY1zCOuG4j9f6EPnuyyYkf58RnhhWTvRoZEokgB+WbdXehfjFviyOVYkqzEWz1Q5kRiZdBYS5SwxbQYwzw==} + resolution: + { + integrity: sha512-loKTxY1zCOuG4j9f6EPnuyyYkf58RnhhWTvRoZEokgB+WbdXehfjFviyOVYkqzEWz1Q5kRiZdBYS5SwxbQYwzw==, + } color-convert@2.0.1: - resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} - engines: {node: '>=7.0.0'} + resolution: + { + integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==, + } + engines: { node: '>=7.0.0' } color-name@1.1.4: - resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + resolution: + { + integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==, + } comma-separated-tokens@2.0.3: - resolution: {integrity: sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==} + resolution: + { + integrity: sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==, + } commander@10.0.1: - resolution: {integrity: sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==} - engines: {node: '>=14'} + resolution: + { + integrity: sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==, + } + engines: { node: '>=14' } compute-scroll-into-view@3.1.1: - resolution: {integrity: sha512-VRhuHOLoKYOy4UbilLbUzbYg93XLjv2PncJC50EuTWPA3gaja1UjBsUP/D/9/juV3vQFr6XBEzn9KCAHdUvOHw==} + resolution: + { + integrity: sha512-VRhuHOLoKYOy4UbilLbUzbYg93XLjv2PncJC50EuTWPA3gaja1UjBsUP/D/9/juV3vQFr6XBEzn9KCAHdUvOHw==, + } convert-source-map@2.0.0: - resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} + resolution: + { + integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==, + } css-tree@3.2.1: - resolution: {integrity: sha512-X7sjQzceUhu1u7Y/ylrRZFU2FS6LRiFVp6rKLPg23y3x3c3DOKAwuXGDp+PAGjh6CSnCjYeAul8pcT8bAl+lSA==} - engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0} + resolution: + { + integrity: sha512-X7sjQzceUhu1u7Y/ylrRZFU2FS6LRiFVp6rKLPg23y3x3c3DOKAwuXGDp+PAGjh6CSnCjYeAul8pcT8bAl+lSA==, + } + engines: { node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0 } cssstyle@6.2.0: - resolution: {integrity: sha512-Fm5NvhYathRnXNVndkUsCCuR63DCLVVwGOOwQw782coXFi5HhkXdu289l59HlXZBawsyNccXfWRYvLzcDCdDig==} - engines: {node: '>=20'} + resolution: + { + integrity: sha512-Fm5NvhYathRnXNVndkUsCCuR63DCLVVwGOOwQw782coXFi5HhkXdu289l59HlXZBawsyNccXfWRYvLzcDCdDig==, + } + engines: { node: '>=20' } csstype@3.2.3: - resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==} + resolution: + { + integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==, + } data-urls@7.0.0: - resolution: {integrity: sha512-23XHcCF+coGYevirZceTVD7NdJOqVn+49IHyxgszm+JIiHLoB2TkmPtsYkNWT1pvRSGkc35L6NHs0yHkN2SumA==} - engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} + resolution: + { + integrity: sha512-23XHcCF+coGYevirZceTVD7NdJOqVn+49IHyxgszm+JIiHLoB2TkmPtsYkNWT1pvRSGkc35L6NHs0yHkN2SumA==, + } + engines: { node: ^20.19.0 || ^22.12.0 || >=24.0.0 } dataloader@1.4.0: - resolution: {integrity: sha512-68s5jYdlvasItOJnCuI2Q9s4q98g0pCyL3HrcKJu8KNugUl8ahgmZYg38ysLTgQjjXX3H8CJLkAvWrclWfcalw==} + resolution: + { + integrity: sha512-68s5jYdlvasItOJnCuI2Q9s4q98g0pCyL3HrcKJu8KNugUl8ahgmZYg38ysLTgQjjXX3H8CJLkAvWrclWfcalw==, + } debug@4.4.3: - resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==} - engines: {node: '>=6.0'} + resolution: + { + integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==, + } + engines: { node: '>=6.0' } peerDependencies: supports-color: '*' peerDependenciesMeta: @@ -1963,38 +3018,68 @@ packages: optional: true decimal.js@10.6.0: - resolution: {integrity: sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==} + resolution: + { + integrity: sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==, + } decode-named-character-reference@1.3.0: - resolution: {integrity: sha512-GtpQYB283KrPp6nRw50q3U9/VfOutZOe103qlN7BPP6Ad27xYnOIWv4lPzo8HCAL+mMZofJ9KEy30fq6MfaK6Q==} + resolution: + { + integrity: sha512-GtpQYB283KrPp6nRw50q3U9/VfOutZOe103qlN7BPP6Ad27xYnOIWv4lPzo8HCAL+mMZofJ9KEy30fq6MfaK6Q==, + } defu@6.1.7: - resolution: {integrity: sha512-7z22QmUWiQ/2d0KkdYmANbRUVABpZ9SNYyH5vx6PZ+nE5bcC0l7uFvEfHlyld/HcGBFTL536ClDt3DEcSlEJAQ==} + resolution: + { + integrity: sha512-7z22QmUWiQ/2d0KkdYmANbRUVABpZ9SNYyH5vx6PZ+nE5bcC0l7uFvEfHlyld/HcGBFTL536ClDt3DEcSlEJAQ==, + } dequal@2.0.3: - resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==} - engines: {node: '>=6'} + resolution: + { + integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==, + } + engines: { node: '>=6' } detect-libc@2.1.2: - resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==} - engines: {node: '>=8'} + resolution: + { + integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==, + } + engines: { node: '>=8' } detect-node-es@1.1.0: - resolution: {integrity: sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==} + resolution: + { + integrity: sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==, + } devlop@1.1.0: - resolution: {integrity: sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==} + resolution: + { + integrity: sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==, + } dom-accessibility-api@0.5.16: - resolution: {integrity: sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==} + resolution: + { + integrity: sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==, + } dotenv@8.6.0: - resolution: {integrity: sha512-IrPdXQsk2BbzvCBGBOTmmSH5SodmqZNt4ERAZDmW4CT+tL8VtvinqywuANaFu4bOMWki16nqf0e4oC0QIaDr/g==} - engines: {node: '>=10'} + resolution: + { + integrity: sha512-IrPdXQsk2BbzvCBGBOTmmSH5SodmqZNt4ERAZDmW4CT+tL8VtvinqywuANaFu4bOMWki16nqf0e4oC0QIaDr/g==, + } + engines: { node: '>=10' } dts-resolver@3.0.0: - resolution: {integrity: sha512-1T1f+z+4tl9XD+m+0HBgWoL/nm0bOIffyWaUuUSBlFg/86IWvfx+wjNaO/ybU0AJzG9/Mi5hBUgGV6zCmWEN7Q==} - engines: {node: ^22.18.0 || >=24.0.0} + resolution: + { + integrity: sha512-1T1f+z+4tl9XD+m+0HBgWoL/nm0bOIffyWaUuUSBlFg/86IWvfx+wjNaO/ybU0AJzG9/Mi5hBUgGV6zCmWEN7Q==, + } + engines: { node: ^22.18.0 || >=24.0.0 } peerDependencies: oxc-resolver: '>=11.0.0' peerDependenciesMeta: @@ -2002,100 +3087,184 @@ packages: optional: true emoji-regex@8.0.0: - resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} + resolution: + { + integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==, + } emojilib@2.4.0: - resolution: {integrity: sha512-5U0rVMU5Y2n2+ykNLQqMoqklN9ICBT/KsvC1Gz6vqHbz2AXXGkG+Pm5rMWk/8Vjrr/mY9985Hi8DYzn1F09Nyw==} + resolution: + { + integrity: sha512-5U0rVMU5Y2n2+ykNLQqMoqklN9ICBT/KsvC1Gz6vqHbz2AXXGkG+Pm5rMWk/8Vjrr/mY9985Hi8DYzn1F09Nyw==, + } empathic@2.0.1: - resolution: {integrity: sha512-YGRs8knHhKHVShLkFET/rWAU8kmHbOV5LwN938RHI0pljAJ1Gf6SzXsSmRaEzcXTtOOmVqJ5+WtQPL5uigY50Q==} - engines: {node: '>=14'} + resolution: + { + integrity: sha512-YGRs8knHhKHVShLkFET/rWAU8kmHbOV5LwN938RHI0pljAJ1Gf6SzXsSmRaEzcXTtOOmVqJ5+WtQPL5uigY50Q==, + } + engines: { node: '>=14' } enhanced-resolve@5.24.5: - resolution: {integrity: sha512-L1l8TNvomm6UVW5B253AGxQagSQr+vGwhMlrrfRS2qmhx46AMpMVJKQYLvWYbysTMY8VoicOvzHzoHMbyzB+4A==} - engines: {node: '>=10.13.0'} + resolution: + { + integrity: sha512-L1l8TNvomm6UVW5B253AGxQagSQr+vGwhMlrrfRS2qmhx46AMpMVJKQYLvWYbysTMY8VoicOvzHzoHMbyzB+4A==, + } + engines: { node: '>=10.13.0' } entities@6.0.1: - resolution: {integrity: sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==} - engines: {node: '>=0.12'} + resolution: + { + integrity: sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==, + } + engines: { node: '>=0.12' } entities@8.0.0: - resolution: {integrity: sha512-zwfzJecQ/Uej6tusMqwAqU/6KL2XaB2VZ2Jg54Je6ahNBGNH6Ek6g3jjNCF0fG9EWQKGZNddNjU5F1ZQn/sBnA==} - engines: {node: '>=20.19.0'} + resolution: + { + integrity: sha512-zwfzJecQ/Uej6tusMqwAqU/6KL2XaB2VZ2Jg54Je6ahNBGNH6Ek6g3jjNCF0fG9EWQKGZNddNjU5F1ZQn/sBnA==, + } + engines: { node: '>=20.19.0' } environment@1.1.0: - resolution: {integrity: sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q==} - engines: {node: '>=18'} + resolution: + { + integrity: sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q==, + } + engines: { node: '>=18' } es-module-lexer@2.3.2: - resolution: {integrity: sha512-poHGpORABojJJucnV9KbOavETW8lBVnphkW77ER5/BQ5Fz7oXSoCNek7IH3vR5nRjdsEz926ibFYX8KtLQmdyw==} + resolution: + { + integrity: sha512-poHGpORABojJJucnV9KbOavETW8lBVnphkW77ER5/BQ5Fz7oXSoCNek7IH3vR5nRjdsEz926ibFYX8KtLQmdyw==, + } esast-util-from-estree@2.0.0: - resolution: {integrity: sha512-4CyanoAudUSBAn5K13H4JhsMH6L9ZP7XbLVe/dKybkxMO7eDyLsT8UHl9TRNrU2Gr9nz+FovfSIjuXWJ81uVwQ==} + resolution: + { + integrity: sha512-4CyanoAudUSBAn5K13H4JhsMH6L9ZP7XbLVe/dKybkxMO7eDyLsT8UHl9TRNrU2Gr9nz+FovfSIjuXWJ81uVwQ==, + } esast-util-from-js@2.0.1: - resolution: {integrity: sha512-8Ja+rNJ0Lt56Pcf3TAmpBZjmx8ZcK5Ts4cAzIOjsjevg9oSXJnl6SUQ2EevU8tv3h6ZLWmoKL5H4fgWvdvfETw==} + resolution: + { + integrity: sha512-8Ja+rNJ0Lt56Pcf3TAmpBZjmx8ZcK5Ts4cAzIOjsjevg9oSXJnl6SUQ2EevU8tv3h6ZLWmoKL5H4fgWvdvfETw==, + } esbuild@0.28.2: - resolution: {integrity: sha512-HKVLS8dvII+xoKW9kmqxbRKrnWEXfJJr/FZhhJmiqIB0e053QNYFqOBouTMO/k5sID4MvCiUCvv8b9M4h32wIA==} - engines: {node: '>=18'} + 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'} + resolution: + { + integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==, + } + engines: { node: '>=6' } escape-string-regexp@5.0.0: - resolution: {integrity: sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==} - engines: {node: '>=12'} + resolution: + { + integrity: sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==, + } + engines: { node: '>=12' } estree-util-attach-comments@3.0.0: - resolution: {integrity: sha512-cKUwm/HUcTDsYh/9FgnuFqpfquUbwIqwKM26BVCGDPVgvaCl/nDCCjUfiLlx6lsEZ3Z4RFxNbOQ60pkaEwFxGw==} + resolution: + { + integrity: sha512-cKUwm/HUcTDsYh/9FgnuFqpfquUbwIqwKM26BVCGDPVgvaCl/nDCCjUfiLlx6lsEZ3Z4RFxNbOQ60pkaEwFxGw==, + } estree-util-build-jsx@3.0.1: - resolution: {integrity: sha512-8U5eiL6BTrPxp/CHbs2yMgP8ftMhR5ww1eIKoWRMlqvltHF8fZn5LRDvTKuxD3DUn+shRbLGqXemcP51oFCsGQ==} + resolution: + { + integrity: sha512-8U5eiL6BTrPxp/CHbs2yMgP8ftMhR5ww1eIKoWRMlqvltHF8fZn5LRDvTKuxD3DUn+shRbLGqXemcP51oFCsGQ==, + } estree-util-is-identifier-name@3.0.0: - resolution: {integrity: sha512-hFtqIDZTIUZ9BXLb8y4pYGyk6+wekIivNVTcmvk8NoOh+VeRn5y6cEHzbURrWbfp1fIqdVipilzj+lfaadNZmg==} + resolution: + { + integrity: sha512-hFtqIDZTIUZ9BXLb8y4pYGyk6+wekIivNVTcmvk8NoOh+VeRn5y6cEHzbURrWbfp1fIqdVipilzj+lfaadNZmg==, + } estree-util-scope@1.0.1: - resolution: {integrity: sha512-B0np3dcdxqILX5e9nEi5/Fr4K7gL4oYFVPV1zRa2e9wRCbQoZZNWOZFYyoInvXUPJXBXjss+QXlWLJChDEHDkA==} + resolution: + { + integrity: sha512-B0np3dcdxqILX5e9nEi5/Fr4K7gL4oYFVPV1zRa2e9wRCbQoZZNWOZFYyoInvXUPJXBXjss+QXlWLJChDEHDkA==, + } estree-util-to-js@2.0.0: - resolution: {integrity: sha512-WDF+xj5rRWmD5tj6bIqRi6CkLIXbbNQUcxQHzGysQzvHmdYG2G7p/Tf0J0gpxGgkeMZNTIjT/AoSvC9Xehcgdg==} + resolution: + { + integrity: sha512-WDF+xj5rRWmD5tj6bIqRi6CkLIXbbNQUcxQHzGysQzvHmdYG2G7p/Tf0J0gpxGgkeMZNTIjT/AoSvC9Xehcgdg==, + } estree-util-value-to-estree@3.5.0: - resolution: {integrity: sha512-aMV56R27Gv3QmfmF1MY12GWkGzzeAezAX+UplqHVASfjc9wNzI/X6hC0S9oxq61WT4aQesLGslWP9tKk6ghRZQ==} + resolution: + { + integrity: sha512-aMV56R27Gv3QmfmF1MY12GWkGzzeAezAX+UplqHVASfjc9wNzI/X6hC0S9oxq61WT4aQesLGslWP9tKk6ghRZQ==, + } estree-util-visit@2.0.0: - resolution: {integrity: sha512-m5KgiH85xAhhW8Wta0vShLcUvOsh3LLPI2YVwcbio1l7E09NTLL1EyMZFM1OyWowoH0skScNbhOPl4kcBgzTww==} + resolution: + { + integrity: sha512-m5KgiH85xAhhW8Wta0vShLcUvOsh3LLPI2YVwcbio1l7E09NTLL1EyMZFM1OyWowoH0skScNbhOPl4kcBgzTww==, + } estree-walker@3.0.3: - resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==} + resolution: + { + integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==, + } expect-type@1.4.0: - resolution: {integrity: sha512-KfYbmpRm0VbLjEvVa9yGwCi9GI34xvi7A/HXYWQO65CSD2u3MczUJSuwXKFIxlGsgBQizV9q5J9NHj4VG0n+pA==} - engines: {node: '>=12.0.0'} + resolution: + { + integrity: sha512-KfYbmpRm0VbLjEvVa9yGwCi9GI34xvi7A/HXYWQO65CSD2u3MczUJSuwXKFIxlGsgBQizV9q5J9NHj4VG0n+pA==, + } + engines: { node: '>=12.0.0' } extend@3.0.2: - resolution: {integrity: sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==} + resolution: + { + integrity: sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==, + } fast-string-truncated-width@3.0.3: - resolution: {integrity: sha512-0jjjIEL6+0jag3l2XWWizO64/aZVtpiGE3t0Zgqxv0DPuxiMjvB3M24fCyhZUO4KomJQPj3LTSUnDP3GpdwC0g==} + resolution: + { + integrity: sha512-0jjjIEL6+0jag3l2XWWizO64/aZVtpiGE3t0Zgqxv0DPuxiMjvB3M24fCyhZUO4KomJQPj3LTSUnDP3GpdwC0g==, + } fast-string-width@3.0.2: - resolution: {integrity: sha512-gX8LrtNEI5hq8DVUfRQMbr5lpaS4nMIWV+7XEbXk2b8kiQIizgnlr12B4dA3ZEx3308ze0O4Q1R+cHts8kyUJg==} + resolution: + { + integrity: sha512-gX8LrtNEI5hq8DVUfRQMbr5lpaS4nMIWV+7XEbXk2b8kiQIizgnlr12B4dA3ZEx3308ze0O4Q1R+cHts8kyUJg==, + } fast-wrap-ansi@0.2.2: - resolution: {integrity: sha512-7F2Fl+TjRSenLqlU3UjSH0iyqopqoZIu7eZVpEirP2g1GtWa2G/ecEmBdgz31+Mxr+ELclgg6sokpSFIQiZ02Q==} + resolution: + { + integrity: sha512-7F2Fl+TjRSenLqlU3UjSH0iyqopqoZIu7eZVpEirP2g1GtWa2G/ecEmBdgz31+Mxr+ELclgg6sokpSFIQiZ02Q==, + } faye-websocket@0.11.4: - resolution: {integrity: sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g==} - engines: {node: '>=0.8.0'} + resolution: + { + integrity: sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g==, + } + engines: { node: '>=0.8.0' } fdir@6.5.0: - resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} - engines: {node: '>=12.0.0'} + resolution: + { + integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==, + } + engines: { node: '>=12.0.0' } peerDependencies: picomatch: ^3 || ^4 peerDependenciesMeta: @@ -2103,13 +3272,22 @@ packages: optional: true fflate@0.8.3: - resolution: {integrity: sha512-tbZNuJrLwGUp3zshBtdy4W+ORxZuIh8a5ilyIEQDC5rY1f3U20JMry0Ll3WBzU58EZKsEuJFXhb5gwv8CsPvgA==} + resolution: + { + integrity: sha512-tbZNuJrLwGUp3zshBtdy4W+ORxZuIh8a5ilyIEQDC5rY1f3U20JMry0Ll3WBzU58EZKsEuJFXhb5gwv8CsPvgA==, + } firebase@12.18.0: - resolution: {integrity: sha512-XaL6tlE5Xd20ZDhckqOMIw+JJTET+wTdeZPxQ7ihc42oxRb7kWUyn/j1LO5V9dH1xq8Rv5R71Pv1fBCdIkt9Rw==} + resolution: + { + integrity: sha512-XaL6tlE5Xd20ZDhckqOMIw+JJTET+wTdeZPxQ7ihc42oxRb7kWUyn/j1LO5V9dH1xq8Rv5R71Pv1fBCdIkt9Rw==, + } framer-motion@13.1.1: - resolution: {integrity: sha512-B/xn2TPS4f61cEBLFjiYlQFnBZUW1YVj/LM+C+N4OP8Rs95VLEI2ot/RlfBg111la/EiyECFaJJi/A3FWA8MUA==} + resolution: + { + integrity: sha512-B/xn2TPS4f61cEBLFjiYlQFnBZUW1YVj/LM+C+N4OP8Rs95VLEI2ot/RlfBg111la/EiyECFaJJi/A3FWA8MUA==, + } peerDependencies: react: ^18.0.0 || ^19.0.0 react-dom: ^18.0.0 || ^19.0.0 @@ -2120,12 +3298,18 @@ packages: optional: true fsevents@2.3.3: - resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} - engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + resolution: + { + integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==, + } + engines: { node: ^8.16.0 || ^10.6.0 || >=11.0.0 } os: [darwin] fumadocs-core@16.15.4: - resolution: {integrity: sha512-kdOuM0tvHkLWajnDu73BmtryPuUq4xsu610ssl1YMAm9fYF7BRmvYxx2apHbyf2Lt+7w8OEiWDTmB8Ja3zqp4Q==} + resolution: + { + integrity: sha512-kdOuM0tvHkLWajnDu73BmtryPuUq4xsu610ssl1YMAm9fYF7BRmvYxx2apHbyf2Lt+7w8OEiWDTmB8Ja3zqp4Q==, + } peerDependencies: '@mdx-js/mdx': '*' '@mixedbread/sdk': 0.x.x @@ -2184,7 +3368,10 @@ packages: optional: true fumadocs-mdx@15.4.0: - resolution: {integrity: sha512-bJCQfsckKUQ4x2pyz8OdASEAARVMB49kOej7njaJ3t+tYsP1HnwkmqiO8e/jdQHcv6JH3XkXWidTho3ZC/WBcA==} + resolution: + { + integrity: sha512-bJCQfsckKUQ4x2pyz8OdASEAARVMB49kOej7njaJ3t+tYsP1HnwkmqiO8e/jdQHcv6JH3XkXWidTho3ZC/WBcA==, + } hasBin: true peerDependencies: '@fumadocs/satteri': 0.x.x @@ -2221,7 +3408,10 @@ packages: optional: true fumadocs-typescript@4.0.14: - resolution: {integrity: sha512-Jx2ldrFP2jEKUeczHuj1OCaCXNxJbVX/bseYaGA3+DY5BK0otaozfs2bJK75TfbGPF3grAZdSe+0KGP1DOTYqQ==} + resolution: + { + integrity: sha512-Jx2ldrFP2jEKUeczHuj1OCaCXNxJbVX/bseYaGA3+DY5BK0otaozfs2bJK75TfbGPF3grAZdSe+0KGP1DOTYqQ==, + } peerDependencies: '@types/react': '*' fumadocs-core: ^15.7.0 || ^16.0.0 @@ -2234,136 +3424,253 @@ packages: optional: true get-caller-file@2.0.5: - resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} - engines: {node: 6.* || 8.* || >= 10.*} + resolution: + { + integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==, + } + engines: { node: 6.* || 8.* || >= 10.* } get-nonce@1.0.1: - resolution: {integrity: sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q==} - engines: {node: '>=6'} + resolution: + { + integrity: sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q==, + } + engines: { node: '>=6' } get-tsconfig@5.0.0-beta.5: - resolution: {integrity: sha512-/6gFNr0N04nob252sTQxyFLi3eKFRqIg1I87YcqAMT1i6SQrSF6KujUEQrtrjMV0H/eejTCltLdDSTEMzHbnsQ==} - engines: {node: '>=20.20.0'} + resolution: + { + integrity: sha512-/6gFNr0N04nob252sTQxyFLi3eKFRqIg1I87YcqAMT1i6SQrSF6KujUEQrtrjMV0H/eejTCltLdDSTEMzHbnsQ==, + } + engines: { node: '>=20.20.0' } github-slugger@2.0.0: - resolution: {integrity: sha512-IaOQ9puYtjrkq7Y0Ygl9KDZnrf/aiUJYUpVf89y8kyaxbRG7Y1SrX/jaumrv81vc61+kiMempujsM3Yw7w5qcw==} + resolution: + { + integrity: sha512-IaOQ9puYtjrkq7Y0Ygl9KDZnrf/aiUJYUpVf89y8kyaxbRG7Y1SrX/jaumrv81vc61+kiMempujsM3Yw7w5qcw==, + } graceful-fs@4.2.11: - resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} + resolution: + { + integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==, + } has-flag@4.0.0: - resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} - engines: {node: '>=8'} + resolution: + { + integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==, + } + engines: { node: '>=8' } hast-util-from-parse5@8.0.3: - resolution: {integrity: sha512-3kxEVkEKt0zvcZ3hCRYI8rqrgwtlIOFMWkbclACvjlDw8Li9S2hk/d51OI0nr/gIpdMHNepwgOKqZ/sy0Clpyg==} + resolution: + { + integrity: sha512-3kxEVkEKt0zvcZ3hCRYI8rqrgwtlIOFMWkbclACvjlDw8Li9S2hk/d51OI0nr/gIpdMHNepwgOKqZ/sy0Clpyg==, + } hast-util-parse-selector@4.0.0: - resolution: {integrity: sha512-wkQCkSYoOGCRKERFWcxMVMOcYE2K1AaNLU8DXS9arxnLOUEWbOXKXiJUNzEpqZ3JOKpnha3jkFrumEjVliDe7A==} + resolution: + { + integrity: sha512-wkQCkSYoOGCRKERFWcxMVMOcYE2K1AaNLU8DXS9arxnLOUEWbOXKXiJUNzEpqZ3JOKpnha3jkFrumEjVliDe7A==, + } hast-util-raw@9.1.0: - resolution: {integrity: sha512-Y8/SBAHkZGoNkpzqqfCldijcuUKh7/su31kEBp67cFY09Wy0mTRgtsLYsiIxMJxlu0f6AA5SUTbDR8K0rxnbUw==} + resolution: + { + integrity: sha512-Y8/SBAHkZGoNkpzqqfCldijcuUKh7/su31kEBp67cFY09Wy0mTRgtsLYsiIxMJxlu0f6AA5SUTbDR8K0rxnbUw==, + } hast-util-to-estree@3.1.3: - resolution: {integrity: sha512-48+B/rJWAp0jamNbAAf9M7Uf//UVqAoMmgXhBdxTDJLGKY+LRnZ99qcG+Qjl5HfMpYNzS5v4EAwVEF34LeAj7w==} + resolution: + { + integrity: sha512-48+B/rJWAp0jamNbAAf9M7Uf//UVqAoMmgXhBdxTDJLGKY+LRnZ99qcG+Qjl5HfMpYNzS5v4EAwVEF34LeAj7w==, + } hast-util-to-html@9.0.5: - resolution: {integrity: sha512-OguPdidb+fbHQSU4Q4ZiLKnzWo8Wwsf5bZfbvu7//a9oTYoqD/fWpe96NuHkoS9h0ccGOTe0C4NGXdtS0iObOw==} + resolution: + { + integrity: sha512-OguPdidb+fbHQSU4Q4ZiLKnzWo8Wwsf5bZfbvu7//a9oTYoqD/fWpe96NuHkoS9h0ccGOTe0C4NGXdtS0iObOw==, + } hast-util-to-jsx-runtime@2.3.6: - resolution: {integrity: sha512-zl6s8LwNyo1P9uw+XJGvZtdFF1GdAkOg8ujOw+4Pyb76874fLps4ueHXDhXWdk6YHQ6OgUtinliG7RsYvCbbBg==} + resolution: + { + integrity: sha512-zl6s8LwNyo1P9uw+XJGvZtdFF1GdAkOg8ujOw+4Pyb76874fLps4ueHXDhXWdk6YHQ6OgUtinliG7RsYvCbbBg==, + } hast-util-to-parse5@8.0.1: - resolution: {integrity: sha512-MlWT6Pjt4CG9lFCjiz4BH7l9wmrMkfkJYCxFwKQic8+RTZgWPuWxwAfjJElsXkex7DJjfSJsQIt931ilUgmwdA==} + resolution: + { + integrity: sha512-MlWT6Pjt4CG9lFCjiz4BH7l9wmrMkfkJYCxFwKQic8+RTZgWPuWxwAfjJElsXkex7DJjfSJsQIt931ilUgmwdA==, + } hast-util-whitespace@3.0.0: - resolution: {integrity: sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==} + resolution: + { + integrity: sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==, + } hastscript@9.0.1: - resolution: {integrity: sha512-g7df9rMFX/SPi34tyGCyUBREQoKkapwdY/T04Qn9TDWfHhAYt4/I0gMVirzK5wEzeUqIjEB+LXC/ypb7Aqno5w==} + resolution: + { + integrity: sha512-g7df9rMFX/SPi34tyGCyUBREQoKkapwdY/T04Qn9TDWfHhAYt4/I0gMVirzK5wEzeUqIjEB+LXC/ypb7Aqno5w==, + } highlight.js@10.7.3: - resolution: {integrity: sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A==} + resolution: + { + integrity: sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A==, + } hookable@6.1.1: - resolution: {integrity: sha512-U9LYDy1CwhMCnprUfeAZWZGByVbhd54hwepegYTK7Pi5NvqEj63ifz5z+xukznehT7i6NIZRu89Ay1AZmRsLEQ==} + resolution: + { + integrity: sha512-U9LYDy1CwhMCnprUfeAZWZGByVbhd54hwepegYTK7Pi5NvqEj63ifz5z+xukznehT7i6NIZRu89Ay1AZmRsLEQ==, + } html-encoding-sniffer@6.0.0: - resolution: {integrity: sha512-CV9TW3Y3f8/wT0BRFc1/KAVQ3TUHiXmaAb6VW9vtiMFf7SLoMd1PdAc4W3KFOFETBJUb90KatHqlsZMWV+R9Gg==} - engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} + resolution: + { + integrity: sha512-CV9TW3Y3f8/wT0BRFc1/KAVQ3TUHiXmaAb6VW9vtiMFf7SLoMd1PdAc4W3KFOFETBJUb90KatHqlsZMWV+R9Gg==, + } + engines: { node: ^20.19.0 || ^22.12.0 || >=24.0.0 } html-void-elements@3.0.0: - resolution: {integrity: sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg==} + resolution: + { + integrity: sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg==, + } http-parser-js@0.5.10: - resolution: {integrity: sha512-Pysuw9XpUq5dVc/2SMHpuTY01RFl8fttgcyunjL7eEMhGM3cI4eOmiCycJDVCo/7O7ClfQD3SaI6ftDzqOXYMA==} + resolution: + { + integrity: sha512-Pysuw9XpUq5dVc/2SMHpuTY01RFl8fttgcyunjL7eEMhGM3cI4eOmiCycJDVCo/7O7ClfQD3SaI6ftDzqOXYMA==, + } http-proxy-agent@7.0.2: - resolution: {integrity: sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==} - engines: {node: '>= 14'} + resolution: + { + integrity: sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==, + } + engines: { node: '>= 14' } https-proxy-agent@7.0.6: - resolution: {integrity: sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==} - engines: {node: '>= 14'} + resolution: + { + integrity: sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==, + } + engines: { node: '>= 14' } human-id@4.2.1: - resolution: {integrity: sha512-zPGsiS+dWoTZtZ4AtpA9Y+BdSFSNWvnouNlWNoUFyAM6xHOHmdCvqO3k8AIbdamCOv4gUFUVNPf6rJFfc4UiJw==} + resolution: + { + integrity: sha512-zPGsiS+dWoTZtZ4AtpA9Y+BdSFSNWvnouNlWNoUFyAM6xHOHmdCvqO3k8AIbdamCOv4gUFUVNPf6rJFfc4UiJw==, + } hasBin: true husky@9.1.7: - resolution: {integrity: sha512-5gs5ytaNjBrh5Ow3zrvdUUY+0VxIuWVL4i9irt6friV+BqdCfmV11CQTWMiBYWHbXhco+J1kHfTOUkePhCDvMA==} - engines: {node: '>=18'} + resolution: + { + integrity: sha512-5gs5ytaNjBrh5Ow3zrvdUUY+0VxIuWVL4i9irt6friV+BqdCfmV11CQTWMiBYWHbXhco+J1kHfTOUkePhCDvMA==, + } + engines: { node: '>=18' } hasBin: true idb@7.1.1: - resolution: {integrity: sha512-gchesWBzyvGHRO9W8tzUWFDycow5gwjvFKfyV9FF32Y7F50yZMp7mP+T2mJIWFx49zicqyC4uefHM17o6xKIVQ==} + resolution: + { + integrity: sha512-gchesWBzyvGHRO9W8tzUWFDycow5gwjvFKfyV9FF32Y7F50yZMp7mP+T2mJIWFx49zicqyC4uefHM17o6xKIVQ==, + } import-meta-resolve@4.2.0: - resolution: {integrity: sha512-Iqv2fzaTQN28s/FwZAoFq0ZSs/7hMAHJVX+w8PZl3cY19Pxk6jFFalxQoIfW2826i/fDLXv8IiEZRIT0lDuWcg==} + resolution: + { + integrity: sha512-Iqv2fzaTQN28s/FwZAoFq0ZSs/7hMAHJVX+w8PZl3cY19Pxk6jFFalxQoIfW2826i/fDLXv8IiEZRIT0lDuWcg==, + } import-without-cache@0.4.0: - resolution: {integrity: sha512-NkJQA7oZ4YHQhd2+H3BoRFKF3d/XNsiKpHZCQEMH9pDX27hQQLsTyOocyRgaIVtf8gHX3Nt3LPkR4e5EdtPAGQ==} - engines: {node: ^22.18.0 || >=24.0.0} + resolution: + { + integrity: sha512-NkJQA7oZ4YHQhd2+H3BoRFKF3d/XNsiKpHZCQEMH9pDX27hQQLsTyOocyRgaIVtf8gHX3Nt3LPkR4e5EdtPAGQ==, + } + engines: { node: ^22.18.0 || >=24.0.0 } inline-style-parser@0.2.7: - resolution: {integrity: sha512-Nb2ctOyNR8DqQoR0OwRG95uNWIC0C1lCgf5Naz5H6Ji72KZ8OcFZLz2P5sNgwlyoJ8Yif11oMuYs5pBQa86csA==} + resolution: + { + integrity: sha512-Nb2ctOyNR8DqQoR0OwRG95uNWIC0C1lCgf5Naz5H6Ji72KZ8OcFZLz2P5sNgwlyoJ8Yif11oMuYs5pBQa86csA==, + } is-alphabetical@2.0.1: - resolution: {integrity: sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ==} + resolution: + { + integrity: sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ==, + } is-alphanumerical@2.0.1: - resolution: {integrity: sha512-hmbYhX/9MUMF5uh7tOXyK/n0ZvWpad5caBA17GsC6vyuCqaWliRG5K1qS9inmUhEMaOBIW7/whAnSwveW/LtZw==} + resolution: + { + integrity: sha512-hmbYhX/9MUMF5uh7tOXyK/n0ZvWpad5caBA17GsC6vyuCqaWliRG5K1qS9inmUhEMaOBIW7/whAnSwveW/LtZw==, + } is-decimal@2.0.1: - resolution: {integrity: sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A==} + resolution: + { + integrity: sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A==, + } is-fullwidth-code-point@3.0.0: - resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} - engines: {node: '>=8'} + resolution: + { + integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==, + } + engines: { node: '>=8' } is-hexadecimal@2.0.1: - resolution: {integrity: sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg==} + resolution: + { + integrity: sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg==, + } is-plain-obj@4.1.0: - resolution: {integrity: sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==} - engines: {node: '>=12'} + resolution: + { + integrity: sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==, + } + engines: { node: '>=12' } is-potential-custom-element-name@1.0.1: - resolution: {integrity: sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==} + resolution: + { + integrity: sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==, + } jiti@2.7.0: - resolution: {integrity: sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ==} + resolution: + { + integrity: sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ==, + } hasBin: true jju@1.4.0: - resolution: {integrity: sha512-8wb9Yw966OSxApiCt0K3yNJL8pnNeIv+OEq2YMidz4FKP6nonSRoOXc80iXY4JaN2FC11B9qsNmDsm+ZOfMROA==} + resolution: + { + integrity: sha512-8wb9Yw966OSxApiCt0K3yNJL8pnNeIv+OEq2YMidz4FKP6nonSRoOXc80iXY4JaN2FC11B9qsNmDsm+ZOfMROA==, + } js-tokens@4.0.0: - resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} + resolution: + { + integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==, + } jsdom@28.1.0: - resolution: {integrity: sha512-0+MoQNYyr2rBHqO1xilltfDjV9G7ymYGlAUazgcDLQaUf8JDHbuGwsxN6U9qWaElZ4w1B2r7yEGIL3GdeW3Rug==} - engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} + resolution: + { + integrity: sha512-0+MoQNYyr2rBHqO1xilltfDjV9G7ymYGlAUazgcDLQaUf8JDHbuGwsxN6U9qWaElZ4w1B2r7yEGIL3GdeW3Rug==, + } + engines: { node: ^20.19.0 || ^22.12.0 || >=24.0.0 } peerDependencies: canvas: ^3.0.0 peerDependenciesMeta: @@ -2371,378 +3678,663 @@ packages: optional: true jsonc-parser@3.3.1: - resolution: {integrity: sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ==} + resolution: + { + integrity: sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ==, + } launch-editor@2.14.1: - resolution: {integrity: sha512-QWBrQsMpH7gPr965dsKD/3cKWiNoTjpATQf++Xq63N6sKRGMwlVXz41O1IZTMfZQgBctD/K5Zt06+/I6pP6+HA==} + resolution: + { + integrity: sha512-QWBrQsMpH7gPr965dsKD/3cKWiNoTjpATQf++Xq63N6sKRGMwlVXz41O1IZTMfZQgBctD/K5Zt06+/I6pP6+HA==, + } lightningcss-android-arm64@1.32.0: - resolution: {integrity: sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==} - engines: {node: '>= 12.0.0'} + resolution: + { + integrity: sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==, + } + engines: { node: '>= 12.0.0' } cpu: [arm64] os: [android] lightningcss-android-arm64@1.33.0: - resolution: {integrity: sha512-gEpRTalKdosp4Bb8qWtc2iOgE5SeIHlpS1up9bFq2wAyYhl1UdTObYiHe98zEM9SQvSoqQZ1IQD0JNpg3Ml5pg==} - engines: {node: '>= 12.0.0'} + resolution: + { + integrity: sha512-gEpRTalKdosp4Bb8qWtc2iOgE5SeIHlpS1up9bFq2wAyYhl1UdTObYiHe98zEM9SQvSoqQZ1IQD0JNpg3Ml5pg==, + } + engines: { node: '>= 12.0.0' } cpu: [arm64] os: [android] lightningcss-darwin-arm64@1.32.0: - resolution: {integrity: sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==} - engines: {node: '>= 12.0.0'} + resolution: + { + integrity: sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==, + } + engines: { node: '>= 12.0.0' } cpu: [arm64] os: [darwin] lightningcss-darwin-arm64@1.33.0: - resolution: {integrity: sha512-Sciaz8eenNTKn9b3t7+xr0ipTp9YxKQY4npwQ3mrRuL0BAVHBLyZxofhaKBAVtzmtRZ/zTyo0/to4B1uWG/Djg==} - engines: {node: '>= 12.0.0'} + resolution: + { + integrity: sha512-Sciaz8eenNTKn9b3t7+xr0ipTp9YxKQY4npwQ3mrRuL0BAVHBLyZxofhaKBAVtzmtRZ/zTyo0/to4B1uWG/Djg==, + } + engines: { node: '>= 12.0.0' } cpu: [arm64] os: [darwin] lightningcss-darwin-x64@1.32.0: - resolution: {integrity: sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==} - engines: {node: '>= 12.0.0'} + resolution: + { + integrity: sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==, + } + engines: { node: '>= 12.0.0' } cpu: [x64] os: [darwin] lightningcss-darwin-x64@1.33.0: - resolution: {integrity: sha512-Z5UPAxzrjlWNNyGy6i65cJzzvgJ5D3T6wMvs+gWpY9d7qRhANrxqAp6LhxIgZhWEw18RfJTGcRxjuLIBr+m8XQ==} - engines: {node: '>= 12.0.0'} + resolution: + { + integrity: sha512-Z5UPAxzrjlWNNyGy6i65cJzzvgJ5D3T6wMvs+gWpY9d7qRhANrxqAp6LhxIgZhWEw18RfJTGcRxjuLIBr+m8XQ==, + } + engines: { node: '>= 12.0.0' } cpu: [x64] os: [darwin] lightningcss-freebsd-x64@1.32.0: - resolution: {integrity: sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==} - engines: {node: '>= 12.0.0'} + resolution: + { + integrity: sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==, + } + engines: { node: '>= 12.0.0' } cpu: [x64] os: [freebsd] lightningcss-freebsd-x64@1.33.0: - resolution: {integrity: sha512-QQM/Ti/hQajJwCY+RiWuCZ9sdtI/XQk7nDK5vC8kkdwixezOlDgvDx7+RT+QjK6FcFT4MpsuoBnHIo/O3StRRg==} - engines: {node: '>= 12.0.0'} + resolution: + { + integrity: sha512-QQM/Ti/hQajJwCY+RiWuCZ9sdtI/XQk7nDK5vC8kkdwixezOlDgvDx7+RT+QjK6FcFT4MpsuoBnHIo/O3StRRg==, + } + engines: { node: '>= 12.0.0' } cpu: [x64] os: [freebsd] lightningcss-linux-arm-gnueabihf@1.32.0: - resolution: {integrity: sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==} - engines: {node: '>= 12.0.0'} + resolution: + { + integrity: sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==, + } + engines: { node: '>= 12.0.0' } cpu: [arm] os: [linux] lightningcss-linux-arm-gnueabihf@1.33.0: - resolution: {integrity: sha512-N7FVBe6iS24MlM6R/4RBTxGhQheZGs7tiQ9U32UtF75NzP5Q7xWPRqLBCKxlRQRk3rY1jCIPLzx7WzOhuUIRLQ==} - engines: {node: '>= 12.0.0'} + resolution: + { + integrity: sha512-N7FVBe6iS24MlM6R/4RBTxGhQheZGs7tiQ9U32UtF75NzP5Q7xWPRqLBCKxlRQRk3rY1jCIPLzx7WzOhuUIRLQ==, + } + engines: { node: '>= 12.0.0' } cpu: [arm] os: [linux] lightningcss-linux-arm64-gnu@1.32.0: - resolution: {integrity: sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==} - engines: {node: '>= 12.0.0'} + resolution: + { + integrity: sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==, + } + engines: { node: '>= 12.0.0' } cpu: [arm64] os: [linux] libc: [glibc] lightningcss-linux-arm64-gnu@1.33.0: - resolution: {integrity: sha512-j2v/itmy4HlNxlc6voKXYgBqNi0Ng2LShg4z7GufpEgs05P+2suBVyi9I6YHq5uoVFx9ETin3eCEhLVyXGQnKg==} - engines: {node: '>= 12.0.0'} + resolution: + { + integrity: sha512-j2v/itmy4HlNxlc6voKXYgBqNi0Ng2LShg4z7GufpEgs05P+2suBVyi9I6YHq5uoVFx9ETin3eCEhLVyXGQnKg==, + } + engines: { node: '>= 12.0.0' } cpu: [arm64] os: [linux] libc: [glibc] lightningcss-linux-arm64-musl@1.32.0: - resolution: {integrity: sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==} - engines: {node: '>= 12.0.0'} + resolution: + { + integrity: sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==, + } + engines: { node: '>= 12.0.0' } cpu: [arm64] os: [linux] libc: [musl] lightningcss-linux-arm64-musl@1.33.0: - resolution: {integrity: sha512-yiO5ROMuYQgXbC60yjZU5CYSFZGKXL0HFATXt9mHJn1+zW55oCtMI9NfcVhYLMFDL7gV7oBPon/EmMMGg2OvtQ==} - engines: {node: '>= 12.0.0'} + resolution: + { + integrity: sha512-yiO5ROMuYQgXbC60yjZU5CYSFZGKXL0HFATXt9mHJn1+zW55oCtMI9NfcVhYLMFDL7gV7oBPon/EmMMGg2OvtQ==, + } + engines: { node: '>= 12.0.0' } cpu: [arm64] os: [linux] libc: [musl] lightningcss-linux-x64-gnu@1.32.0: - resolution: {integrity: sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==} - engines: {node: '>= 12.0.0'} + resolution: + { + integrity: sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==, + } + engines: { node: '>= 12.0.0' } cpu: [x64] os: [linux] libc: [glibc] lightningcss-linux-x64-gnu@1.33.0: - resolution: {integrity: sha512-ar+Ju7LmcN0Jo4FpL4hpFybwNG9/3A/Br5KW2n2jyODg3MEZXaDYADdemoNS+BDNfMgKvylJLj4S5tyRActuAg==} - engines: {node: '>= 12.0.0'} + resolution: + { + integrity: sha512-ar+Ju7LmcN0Jo4FpL4hpFybwNG9/3A/Br5KW2n2jyODg3MEZXaDYADdemoNS+BDNfMgKvylJLj4S5tyRActuAg==, + } + engines: { node: '>= 12.0.0' } cpu: [x64] os: [linux] libc: [glibc] lightningcss-linux-x64-musl@1.32.0: - resolution: {integrity: sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==} - engines: {node: '>= 12.0.0'} + resolution: + { + integrity: sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==, + } + engines: { node: '>= 12.0.0' } cpu: [x64] os: [linux] libc: [musl] lightningcss-linux-x64-musl@1.33.0: - resolution: {integrity: sha512-RYiYbkokw0trfKqqzfF55lginwEPrD3OJDfTuJzFs1MK6iFnDenaz1fqLLtX4ITG3OktJQXOeTaw1awrBAlZPw==} - engines: {node: '>= 12.0.0'} + resolution: + { + integrity: sha512-RYiYbkokw0trfKqqzfF55lginwEPrD3OJDfTuJzFs1MK6iFnDenaz1fqLLtX4ITG3OktJQXOeTaw1awrBAlZPw==, + } + engines: { node: '>= 12.0.0' } cpu: [x64] os: [linux] libc: [musl] lightningcss-win32-arm64-msvc@1.32.0: - resolution: {integrity: sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==} - engines: {node: '>= 12.0.0'} + resolution: + { + integrity: sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==, + } + engines: { node: '>= 12.0.0' } cpu: [arm64] os: [win32] lightningcss-win32-arm64-msvc@1.33.0: - resolution: {integrity: sha512-1K+MPfLSFVpphzpdbfkhlWk6wBrTObBzS2T6db10PNOZgR9GoVsAWzwNyuhUYYbTp23j+4RrncfujZ4uAzXvwA==} - engines: {node: '>= 12.0.0'} + resolution: + { + integrity: sha512-1K+MPfLSFVpphzpdbfkhlWk6wBrTObBzS2T6db10PNOZgR9GoVsAWzwNyuhUYYbTp23j+4RrncfujZ4uAzXvwA==, + } + engines: { node: '>= 12.0.0' } cpu: [arm64] os: [win32] lightningcss-win32-x64-msvc@1.32.0: - resolution: {integrity: sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==} - engines: {node: '>= 12.0.0'} + resolution: + { + integrity: sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==, + } + engines: { node: '>= 12.0.0' } cpu: [x64] os: [win32] lightningcss-win32-x64-msvc@1.33.0: - resolution: {integrity: sha512-OlEICDx/Xl0FqSp4bry8zFnCvGpig3Gl4gCquvYwHuqJKEC1+n9NgDniFvqHGmMv1ZkqDJrDqKKSykTDX+ehuA==} - engines: {node: '>= 12.0.0'} + resolution: + { + integrity: sha512-OlEICDx/Xl0FqSp4bry8zFnCvGpig3Gl4gCquvYwHuqJKEC1+n9NgDniFvqHGmMv1ZkqDJrDqKKSykTDX+ehuA==, + } + engines: { node: '>= 12.0.0' } cpu: [x64] os: [win32] lightningcss@1.32.0: - resolution: {integrity: sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==} - engines: {node: '>= 12.0.0'} + resolution: + { + integrity: sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==, + } + engines: { node: '>= 12.0.0' } lightningcss@1.33.0: - resolution: {integrity: sha512-WkUDrojuJs0xkgGf2udWxa3yGBRxPtxUkB79i6aCZLRgc7PM8fZe9TosfPDcvEpQZbuFASnHYmRLBLUbmLOIIA==} - engines: {node: '>= 12.0.0'} + resolution: + { + integrity: sha512-WkUDrojuJs0xkgGf2udWxa3yGBRxPtxUkB79i6aCZLRgc7PM8fZe9TosfPDcvEpQZbuFASnHYmRLBLUbmLOIIA==, + } + engines: { node: '>= 12.0.0' } lint-staged@17.4.1: - resolution: {integrity: sha512-FmJeudcalbSfg1du+JCfvi5vS6Qt08KgbfLWiHinbef+2JJwUZwAWVoaO1AcJVUTWPfk0t30PMQNwPAeCzYQ+Q==} - engines: {node: '>=22.22.1'} + resolution: + { + integrity: sha512-FmJeudcalbSfg1du+JCfvi5vS6Qt08KgbfLWiHinbef+2JJwUZwAWVoaO1AcJVUTWPfk0t30PMQNwPAeCzYQ+Q==, + } + engines: { node: '>=22.22.1' } hasBin: true lodash.camelcase@4.3.0: - resolution: {integrity: sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==} + resolution: + { + integrity: sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==, + } long@5.3.2: - resolution: {integrity: sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==} + resolution: + { + integrity: sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==, + } longest-streak@3.1.0: - resolution: {integrity: sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==} + resolution: + { + integrity: sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==, + } lru-cache@11.5.2: - resolution: {integrity: sha512-4pfM1Ff0x50o0tQwb5ucw/RzNyD0/YJME6IVcStalZuMWxdt3sR3huStTtxz4PUmvZfRguvDejasvQ2kifR11g==} - engines: {node: 20 || >=22} + resolution: + { + integrity: sha512-4pfM1Ff0x50o0tQwb5ucw/RzNyD0/YJME6IVcStalZuMWxdt3sR3huStTtxz4PUmvZfRguvDejasvQ2kifR11g==, + } + engines: { node: 20 || >=22 } lucide-react@1.37.0: - resolution: {integrity: sha512-LPsB4rD1TD6wZu1djKOf9vUnS1jTNaHbolXebXDgiTdb6jeA1agIJhJsIybCmjKmQClcOaal1o1OaiYahEftyQ==} + resolution: + { + integrity: sha512-LPsB4rD1TD6wZu1djKOf9vUnS1jTNaHbolXebXDgiTdb6jeA1agIJhJsIybCmjKmQClcOaal1o1OaiYahEftyQ==, + } peerDependencies: react: ^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0 lz-string@1.5.0: - resolution: {integrity: sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==} + resolution: + { + integrity: sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==, + } hasBin: true magic-string@0.30.21: - resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==} + resolution: + { + integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==, + } magic-string@1.2.3: - resolution: {integrity: sha512-Bpb0W2TbLKOZ7vJnOUnVRGq3WL2p+ISV29M6hYPL1AFCpyKZpdr5ytiXoTSSxRVhg8YW7f65+6gbG8WG6PCa/g==} + resolution: + { + integrity: sha512-Bpb0W2TbLKOZ7vJnOUnVRGq3WL2p+ISV29M6hYPL1AFCpyKZpdr5ytiXoTSSxRVhg8YW7f65+6gbG8WG6PCa/g==, + } markdown-extensions@2.0.0: - resolution: {integrity: sha512-o5vL7aDWatOTX8LzaS1WMoaoxIiLRQJuIKKe2wAw6IeULDHaqbiqiggmx+pKvZDb1Sj+pE46Sn1T7lCqfFtg1Q==} - engines: {node: '>=16'} + resolution: + { + integrity: sha512-o5vL7aDWatOTX8LzaS1WMoaoxIiLRQJuIKKe2wAw6IeULDHaqbiqiggmx+pKvZDb1Sj+pE46Sn1T7lCqfFtg1Q==, + } + engines: { node: '>=16' } markdown-table@3.0.4: - resolution: {integrity: sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw==} + resolution: + { + integrity: sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw==, + } marked-terminal@7.3.0: - resolution: {integrity: sha512-t4rBvPsHc57uE/2nJOLmMbZCQ4tgAccAED3ngXQqW6g+TxA488JzJ+FK3lQkzBQOI1mRV/r/Kq+1ZlJ4D0owQw==} - engines: {node: '>=16.0.0'} + resolution: + { + integrity: sha512-t4rBvPsHc57uE/2nJOLmMbZCQ4tgAccAED3ngXQqW6g+TxA488JzJ+FK3lQkzBQOI1mRV/r/Kq+1ZlJ4D0owQw==, + } + engines: { node: '>=16.0.0' } peerDependencies: marked: '>=1 <16' marked@9.1.6: - resolution: {integrity: sha512-jcByLnIFkd5gSXZmjNvS1TlmRhCXZjIzHYlaGkPlLIekG55JDR2Z4va9tZwCiP+/RDERiNhMOFu01xd6O5ct1Q==} - engines: {node: '>= 16'} + resolution: + { + integrity: sha512-jcByLnIFkd5gSXZmjNvS1TlmRhCXZjIzHYlaGkPlLIekG55JDR2Z4va9tZwCiP+/RDERiNhMOFu01xd6O5ct1Q==, + } + engines: { node: '>= 16' } hasBin: true mdast-util-find-and-replace@3.0.2: - resolution: {integrity: sha512-Tmd1Vg/m3Xz43afeNxDIhWRtFZgM2VLyaf4vSTYwudTyeuTneoL3qtWMA5jeLyz/O1vDJmmV4QuScFCA2tBPwg==} + resolution: + { + integrity: sha512-Tmd1Vg/m3Xz43afeNxDIhWRtFZgM2VLyaf4vSTYwudTyeuTneoL3qtWMA5jeLyz/O1vDJmmV4QuScFCA2tBPwg==, + } mdast-util-from-markdown@2.0.3: - resolution: {integrity: sha512-W4mAWTvSlKvf8L6J+VN9yLSqQ9AOAAvHuoDAmPkz4dHf553m5gVj2ejadHJhoJmcmxEnOv6Pa8XJhpxE93kb8Q==} + resolution: + { + integrity: sha512-W4mAWTvSlKvf8L6J+VN9yLSqQ9AOAAvHuoDAmPkz4dHf553m5gVj2ejadHJhoJmcmxEnOv6Pa8XJhpxE93kb8Q==, + } mdast-util-gfm-autolink-literal@2.0.1: - resolution: {integrity: sha512-5HVP2MKaP6L+G6YaxPNjuL0BPrq9orG3TsrZ9YXbA3vDw/ACI4MEsnoDpn6ZNm7GnZgtAcONJyPhOP8tNJQavQ==} + resolution: + { + integrity: sha512-5HVP2MKaP6L+G6YaxPNjuL0BPrq9orG3TsrZ9YXbA3vDw/ACI4MEsnoDpn6ZNm7GnZgtAcONJyPhOP8tNJQavQ==, + } mdast-util-gfm-footnote@2.1.0: - resolution: {integrity: sha512-sqpDWlsHn7Ac9GNZQMeUzPQSMzR6Wv0WKRNvQRg0KqHh02fpTz69Qc1QSseNX29bhz1ROIyNyxExfawVKTm1GQ==} + resolution: + { + integrity: sha512-sqpDWlsHn7Ac9GNZQMeUzPQSMzR6Wv0WKRNvQRg0KqHh02fpTz69Qc1QSseNX29bhz1ROIyNyxExfawVKTm1GQ==, + } mdast-util-gfm-strikethrough@2.0.0: - resolution: {integrity: sha512-mKKb915TF+OC5ptj5bJ7WFRPdYtuHv0yTRxK2tJvi+BDqbkiG7h7u/9SI89nRAYcmap2xHQL9D+QG/6wSrTtXg==} + resolution: + { + integrity: sha512-mKKb915TF+OC5ptj5bJ7WFRPdYtuHv0yTRxK2tJvi+BDqbkiG7h7u/9SI89nRAYcmap2xHQL9D+QG/6wSrTtXg==, + } mdast-util-gfm-table@2.0.0: - resolution: {integrity: sha512-78UEvebzz/rJIxLvE7ZtDd/vIQ0RHv+3Mh5DR96p7cS7HsBhYIICDBCu8csTNWNO6tBWfqXPWekRuj2FNOGOZg==} + resolution: + { + integrity: sha512-78UEvebzz/rJIxLvE7ZtDd/vIQ0RHv+3Mh5DR96p7cS7HsBhYIICDBCu8csTNWNO6tBWfqXPWekRuj2FNOGOZg==, + } mdast-util-gfm-task-list-item@2.0.0: - resolution: {integrity: sha512-IrtvNvjxC1o06taBAVJznEnkiHxLFTzgonUdy8hzFVeDun0uTjxxrRGVaNFqkU1wJR3RBPEfsxmU6jDWPofrTQ==} + resolution: + { + integrity: sha512-IrtvNvjxC1o06taBAVJznEnkiHxLFTzgonUdy8hzFVeDun0uTjxxrRGVaNFqkU1wJR3RBPEfsxmU6jDWPofrTQ==, + } mdast-util-gfm@3.1.0: - resolution: {integrity: sha512-0ulfdQOM3ysHhCJ1p06l0b0VKlhU0wuQs3thxZQagjcjPrlFRqY215uZGHHJan9GEAXd9MbfPjFJz+qMkVR6zQ==} + resolution: + { + integrity: sha512-0ulfdQOM3ysHhCJ1p06l0b0VKlhU0wuQs3thxZQagjcjPrlFRqY215uZGHHJan9GEAXd9MbfPjFJz+qMkVR6zQ==, + } mdast-util-mdx-expression@2.0.1: - resolution: {integrity: sha512-J6f+9hUp+ldTZqKRSg7Vw5V6MqjATc+3E4gf3CFNcuZNWD8XdyI6zQ8GqH7f8169MM6P7hMBRDVGnn7oHB9kXQ==} + resolution: + { + integrity: sha512-J6f+9hUp+ldTZqKRSg7Vw5V6MqjATc+3E4gf3CFNcuZNWD8XdyI6zQ8GqH7f8169MM6P7hMBRDVGnn7oHB9kXQ==, + } mdast-util-mdx-jsx@3.2.0: - resolution: {integrity: sha512-lj/z8v0r6ZtsN/cGNNtemmmfoLAFZnjMbNyLzBafjzikOM+glrjNHPlf6lQDOTccj9n5b0PPihEBbhneMyGs1Q==} + resolution: + { + integrity: sha512-lj/z8v0r6ZtsN/cGNNtemmmfoLAFZnjMbNyLzBafjzikOM+glrjNHPlf6lQDOTccj9n5b0PPihEBbhneMyGs1Q==, + } mdast-util-mdx@3.0.0: - resolution: {integrity: sha512-JfbYLAW7XnYTTbUsmpu0kdBUVe+yKVJZBItEjwyYJiDJuZ9w4eeaqks4HQO+R7objWgS2ymV60GYpI14Ug554w==} + resolution: + { + integrity: sha512-JfbYLAW7XnYTTbUsmpu0kdBUVe+yKVJZBItEjwyYJiDJuZ9w4eeaqks4HQO+R7objWgS2ymV60GYpI14Ug554w==, + } mdast-util-mdxjs-esm@2.0.1: - resolution: {integrity: sha512-EcmOpxsZ96CvlP03NghtH1EsLtr0n9Tm4lPUJUBccV9RwUOneqSycg19n5HGzCf+10LozMRSObtVr3ee1WoHtg==} + resolution: + { + integrity: sha512-EcmOpxsZ96CvlP03NghtH1EsLtr0n9Tm4lPUJUBccV9RwUOneqSycg19n5HGzCf+10LozMRSObtVr3ee1WoHtg==, + } mdast-util-phrasing@4.1.0: - resolution: {integrity: sha512-TqICwyvJJpBwvGAMZjj4J2n0X8QWp21b9l0o7eXyVJ25YNWYbJDVIyD1bZXE6WtV6RmKJVYmQAKWa0zWOABz2w==} + resolution: + { + integrity: sha512-TqICwyvJJpBwvGAMZjj4J2n0X8QWp21b9l0o7eXyVJ25YNWYbJDVIyD1bZXE6WtV6RmKJVYmQAKWa0zWOABz2w==, + } mdast-util-to-hast@13.2.1: - resolution: {integrity: sha512-cctsq2wp5vTsLIcaymblUriiTcZd0CwWtCbLvrOzYCDZoWyMNV8sZ7krj09FSnsiJi3WVsHLM4k6Dq/yaPyCXA==} + resolution: + { + integrity: sha512-cctsq2wp5vTsLIcaymblUriiTcZd0CwWtCbLvrOzYCDZoWyMNV8sZ7krj09FSnsiJi3WVsHLM4k6Dq/yaPyCXA==, + } mdast-util-to-markdown@2.1.2: - resolution: {integrity: sha512-xj68wMTvGXVOKonmog6LwyJKrYXZPvlwabaryTjLh9LuvovB/KAH+kvi8Gjj+7rJjsFi23nkUxRQv1KqSroMqA==} + resolution: + { + integrity: sha512-xj68wMTvGXVOKonmog6LwyJKrYXZPvlwabaryTjLh9LuvovB/KAH+kvi8Gjj+7rJjsFi23nkUxRQv1KqSroMqA==, + } mdast-util-to-string@4.0.0: - resolution: {integrity: sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==} + resolution: + { + integrity: sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==, + } mdn-data@2.27.1: - resolution: {integrity: sha512-9Yubnt3e8A0OKwxYSXyhLymGW4sCufcLG6VdiDdUGVkPhpqLxlvP5vl1983gQjJl3tqbrM731mjaZaP68AgosQ==} + resolution: + { + integrity: sha512-9Yubnt3e8A0OKwxYSXyhLymGW4sCufcLG6VdiDdUGVkPhpqLxlvP5vl1983gQjJl3tqbrM731mjaZaP68AgosQ==, + } micromark-core-commonmark@2.0.3: - resolution: {integrity: sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg==} + resolution: + { + integrity: sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg==, + } micromark-extension-gfm-autolink-literal@2.1.0: - resolution: {integrity: sha512-oOg7knzhicgQ3t4QCjCWgTmfNhvQbDDnJeVu9v81r7NltNCVmhPy1fJRX27pISafdjL+SVc4d3l48Gb6pbRypw==} + resolution: + { + integrity: sha512-oOg7knzhicgQ3t4QCjCWgTmfNhvQbDDnJeVu9v81r7NltNCVmhPy1fJRX27pISafdjL+SVc4d3l48Gb6pbRypw==, + } micromark-extension-gfm-footnote@2.1.0: - resolution: {integrity: sha512-/yPhxI1ntnDNsiHtzLKYnE3vf9JZ6cAisqVDauhp4CEHxlb4uoOTxOCJ+9s51bIB8U1N1FJ1RXOKTIlD5B/gqw==} + resolution: + { + integrity: sha512-/yPhxI1ntnDNsiHtzLKYnE3vf9JZ6cAisqVDauhp4CEHxlb4uoOTxOCJ+9s51bIB8U1N1FJ1RXOKTIlD5B/gqw==, + } micromark-extension-gfm-strikethrough@2.1.0: - resolution: {integrity: sha512-ADVjpOOkjz1hhkZLlBiYA9cR2Anf8F4HqZUO6e5eDcPQd0Txw5fxLzzxnEkSkfnD0wziSGiv7sYhk/ktvbf1uw==} + resolution: + { + integrity: sha512-ADVjpOOkjz1hhkZLlBiYA9cR2Anf8F4HqZUO6e5eDcPQd0Txw5fxLzzxnEkSkfnD0wziSGiv7sYhk/ktvbf1uw==, + } micromark-extension-gfm-table@2.1.1: - resolution: {integrity: sha512-t2OU/dXXioARrC6yWfJ4hqB7rct14e8f7m0cbI5hUmDyyIlwv5vEtooptH8INkbLzOatzKuVbQmAYcbWoyz6Dg==} + resolution: + { + integrity: sha512-t2OU/dXXioARrC6yWfJ4hqB7rct14e8f7m0cbI5hUmDyyIlwv5vEtooptH8INkbLzOatzKuVbQmAYcbWoyz6Dg==, + } micromark-extension-gfm-tagfilter@2.0.0: - resolution: {integrity: sha512-xHlTOmuCSotIA8TW1mDIM6X2O1SiX5P9IuDtqGonFhEK0qgRI4yeC6vMxEV2dgyr2TiD+2PQ10o+cOhdVAcwfg==} + resolution: + { + integrity: sha512-xHlTOmuCSotIA8TW1mDIM6X2O1SiX5P9IuDtqGonFhEK0qgRI4yeC6vMxEV2dgyr2TiD+2PQ10o+cOhdVAcwfg==, + } micromark-extension-gfm-task-list-item@2.1.0: - resolution: {integrity: sha512-qIBZhqxqI6fjLDYFTBIa4eivDMnP+OZqsNwmQ3xNLE4Cxwc+zfQEfbs6tzAo2Hjq+bh6q5F+Z8/cksrLFYWQQw==} + resolution: + { + integrity: sha512-qIBZhqxqI6fjLDYFTBIa4eivDMnP+OZqsNwmQ3xNLE4Cxwc+zfQEfbs6tzAo2Hjq+bh6q5F+Z8/cksrLFYWQQw==, + } micromark-extension-gfm@3.0.0: - resolution: {integrity: sha512-vsKArQsicm7t0z2GugkCKtZehqUm31oeGBV/KVSorWSy8ZlNAv7ytjFhvaryUiCUJYqs+NoE6AFhpQvBTM6Q4w==} + resolution: + { + integrity: sha512-vsKArQsicm7t0z2GugkCKtZehqUm31oeGBV/KVSorWSy8ZlNAv7ytjFhvaryUiCUJYqs+NoE6AFhpQvBTM6Q4w==, + } micromark-extension-mdx-expression@3.0.1: - resolution: {integrity: sha512-dD/ADLJ1AeMvSAKBwO22zG22N4ybhe7kFIZ3LsDI0GlsNr2A3KYxb0LdC1u5rj4Nw+CHKY0RVdnHX8vj8ejm4Q==} + resolution: + { + integrity: sha512-dD/ADLJ1AeMvSAKBwO22zG22N4ybhe7kFIZ3LsDI0GlsNr2A3KYxb0LdC1u5rj4Nw+CHKY0RVdnHX8vj8ejm4Q==, + } micromark-extension-mdx-jsx@3.0.2: - resolution: {integrity: sha512-e5+q1DjMh62LZAJOnDraSSbDMvGJ8x3cbjygy2qFEi7HCeUT4BDKCvMozPozcD6WmOt6sVvYDNBKhFSz3kjOVQ==} + resolution: + { + integrity: sha512-e5+q1DjMh62LZAJOnDraSSbDMvGJ8x3cbjygy2qFEi7HCeUT4BDKCvMozPozcD6WmOt6sVvYDNBKhFSz3kjOVQ==, + } micromark-extension-mdx-md@2.0.0: - resolution: {integrity: sha512-EpAiszsB3blw4Rpba7xTOUptcFeBFi+6PY8VnJ2hhimH+vCQDirWgsMpz7w1XcZE7LVrSAUGb9VJpG9ghlYvYQ==} + resolution: + { + integrity: sha512-EpAiszsB3blw4Rpba7xTOUptcFeBFi+6PY8VnJ2hhimH+vCQDirWgsMpz7w1XcZE7LVrSAUGb9VJpG9ghlYvYQ==, + } micromark-extension-mdxjs-esm@3.0.0: - resolution: {integrity: sha512-DJFl4ZqkErRpq/dAPyeWp15tGrcrrJho1hKK5uBS70BCtfrIFg81sqcTVu3Ta+KD1Tk5vAtBNElWxtAa+m8K9A==} + resolution: + { + integrity: sha512-DJFl4ZqkErRpq/dAPyeWp15tGrcrrJho1hKK5uBS70BCtfrIFg81sqcTVu3Ta+KD1Tk5vAtBNElWxtAa+m8K9A==, + } micromark-extension-mdxjs@3.0.0: - resolution: {integrity: sha512-A873fJfhnJ2siZyUrJ31l34Uqwy4xIFmvPY1oj+Ean5PHcPBYzEsvqvWGaWcfEIr11O5Dlw3p2y0tZWpKHDejQ==} + resolution: + { + integrity: sha512-A873fJfhnJ2siZyUrJ31l34Uqwy4xIFmvPY1oj+Ean5PHcPBYzEsvqvWGaWcfEIr11O5Dlw3p2y0tZWpKHDejQ==, + } micromark-factory-destination@2.0.1: - resolution: {integrity: sha512-Xe6rDdJlkmbFRExpTOmRj9N3MaWmbAgdpSrBQvCFqhezUn4AHqJHbaEnfbVYYiexVSs//tqOdY/DxhjdCiJnIA==} + resolution: + { + integrity: sha512-Xe6rDdJlkmbFRExpTOmRj9N3MaWmbAgdpSrBQvCFqhezUn4AHqJHbaEnfbVYYiexVSs//tqOdY/DxhjdCiJnIA==, + } micromark-factory-label@2.0.1: - resolution: {integrity: sha512-VFMekyQExqIW7xIChcXn4ok29YE3rnuyveW3wZQWWqF4Nv9Wk5rgJ99KzPvHjkmPXF93FXIbBp6YdW3t71/7Vg==} + resolution: + { + integrity: sha512-VFMekyQExqIW7xIChcXn4ok29YE3rnuyveW3wZQWWqF4Nv9Wk5rgJ99KzPvHjkmPXF93FXIbBp6YdW3t71/7Vg==, + } micromark-factory-mdx-expression@2.0.3: - resolution: {integrity: sha512-kQnEtA3vzucU2BkrIa8/VaSAsP+EJ3CKOvhMuJgOEGg9KDC6OAY6nSnNDVRiVNRqj7Y4SlSzcStaH/5jge8JdQ==} + resolution: + { + integrity: sha512-kQnEtA3vzucU2BkrIa8/VaSAsP+EJ3CKOvhMuJgOEGg9KDC6OAY6nSnNDVRiVNRqj7Y4SlSzcStaH/5jge8JdQ==, + } micromark-factory-space@2.0.1: - resolution: {integrity: sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg==} + resolution: + { + integrity: sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg==, + } micromark-factory-title@2.0.1: - resolution: {integrity: sha512-5bZ+3CjhAd9eChYTHsjy6TGxpOFSKgKKJPJxr293jTbfry2KDoWkhBb6TcPVB4NmzaPhMs1Frm9AZH7OD4Cjzw==} + resolution: + { + integrity: sha512-5bZ+3CjhAd9eChYTHsjy6TGxpOFSKgKKJPJxr293jTbfry2KDoWkhBb6TcPVB4NmzaPhMs1Frm9AZH7OD4Cjzw==, + } micromark-factory-whitespace@2.0.1: - resolution: {integrity: sha512-Ob0nuZ3PKt/n0hORHyvoD9uZhr+Za8sFoP+OnMcnWK5lngSzALgQYKMr9RJVOWLqQYuyn6ulqGWSXdwf6F80lQ==} + resolution: + { + integrity: sha512-Ob0nuZ3PKt/n0hORHyvoD9uZhr+Za8sFoP+OnMcnWK5lngSzALgQYKMr9RJVOWLqQYuyn6ulqGWSXdwf6F80lQ==, + } micromark-util-character@2.1.1: - resolution: {integrity: sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==} + resolution: + { + integrity: sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==, + } micromark-util-chunked@2.0.1: - resolution: {integrity: sha512-QUNFEOPELfmvv+4xiNg2sRYeS/P84pTW0TCgP5zc9FpXetHY0ab7SxKyAQCNCc1eK0459uoLI1y5oO5Vc1dbhA==} + resolution: + { + integrity: sha512-QUNFEOPELfmvv+4xiNg2sRYeS/P84pTW0TCgP5zc9FpXetHY0ab7SxKyAQCNCc1eK0459uoLI1y5oO5Vc1dbhA==, + } micromark-util-classify-character@2.0.1: - resolution: {integrity: sha512-K0kHzM6afW/MbeWYWLjoHQv1sgg2Q9EccHEDzSkxiP/EaagNzCm7T/WMKZ3rjMbvIpvBiZgwR3dKMygtA4mG1Q==} + resolution: + { + integrity: sha512-K0kHzM6afW/MbeWYWLjoHQv1sgg2Q9EccHEDzSkxiP/EaagNzCm7T/WMKZ3rjMbvIpvBiZgwR3dKMygtA4mG1Q==, + } micromark-util-combine-extensions@2.0.1: - resolution: {integrity: sha512-OnAnH8Ujmy59JcyZw8JSbK9cGpdVY44NKgSM7E9Eh7DiLS2E9RNQf0dONaGDzEG9yjEl5hcqeIsj4hfRkLH/Bg==} + resolution: + { + integrity: sha512-OnAnH8Ujmy59JcyZw8JSbK9cGpdVY44NKgSM7E9Eh7DiLS2E9RNQf0dONaGDzEG9yjEl5hcqeIsj4hfRkLH/Bg==, + } micromark-util-decode-numeric-character-reference@2.0.2: - resolution: {integrity: sha512-ccUbYk6CwVdkmCQMyr64dXz42EfHGkPQlBj5p7YVGzq8I7CtjXZJrubAYezf7Rp+bjPseiROqe7G6foFd+lEuw==} + resolution: + { + integrity: sha512-ccUbYk6CwVdkmCQMyr64dXz42EfHGkPQlBj5p7YVGzq8I7CtjXZJrubAYezf7Rp+bjPseiROqe7G6foFd+lEuw==, + } micromark-util-decode-string@2.0.1: - resolution: {integrity: sha512-nDV/77Fj6eH1ynwscYTOsbK7rR//Uj0bZXBwJZRfaLEJ1iGBR6kIfNmlNqaqJf649EP0F3NWNdeJi03elllNUQ==} + resolution: + { + integrity: sha512-nDV/77Fj6eH1ynwscYTOsbK7rR//Uj0bZXBwJZRfaLEJ1iGBR6kIfNmlNqaqJf649EP0F3NWNdeJi03elllNUQ==, + } micromark-util-encode@2.0.1: - resolution: {integrity: sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==} + resolution: + { + integrity: sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==, + } micromark-util-events-to-acorn@2.0.3: - resolution: {integrity: sha512-jmsiEIiZ1n7X1Rr5k8wVExBQCg5jy4UXVADItHmNk1zkwEVhBuIUKRu3fqv+hs4nxLISi2DQGlqIOGiFxgbfHg==} + resolution: + { + integrity: sha512-jmsiEIiZ1n7X1Rr5k8wVExBQCg5jy4UXVADItHmNk1zkwEVhBuIUKRu3fqv+hs4nxLISi2DQGlqIOGiFxgbfHg==, + } micromark-util-html-tag-name@2.0.1: - resolution: {integrity: sha512-2cNEiYDhCWKI+Gs9T0Tiysk136SnR13hhO8yW6BGNyhOC4qYFnwF1nKfD3HFAIXA5c45RrIG1ub11GiXeYd1xA==} + resolution: + { + integrity: sha512-2cNEiYDhCWKI+Gs9T0Tiysk136SnR13hhO8yW6BGNyhOC4qYFnwF1nKfD3HFAIXA5c45RrIG1ub11GiXeYd1xA==, + } micromark-util-normalize-identifier@2.0.1: - resolution: {integrity: sha512-sxPqmo70LyARJs0w2UclACPUUEqltCkJ6PhKdMIDuJ3gSf/Q+/GIe3WKl0Ijb/GyH9lOpUkRAO2wp0GVkLvS9Q==} + resolution: + { + integrity: sha512-sxPqmo70LyARJs0w2UclACPUUEqltCkJ6PhKdMIDuJ3gSf/Q+/GIe3WKl0Ijb/GyH9lOpUkRAO2wp0GVkLvS9Q==, + } micromark-util-resolve-all@2.0.1: - resolution: {integrity: sha512-VdQyxFWFT2/FGJgwQnJYbe1jjQoNTS4RjglmSjTUlpUMa95Htx9NHeYW4rGDJzbjvCsl9eLjMQwGeElsqmzcHg==} + resolution: + { + integrity: sha512-VdQyxFWFT2/FGJgwQnJYbe1jjQoNTS4RjglmSjTUlpUMa95Htx9NHeYW4rGDJzbjvCsl9eLjMQwGeElsqmzcHg==, + } micromark-util-sanitize-uri@2.0.1: - resolution: {integrity: sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==} + resolution: + { + integrity: sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==, + } micromark-util-subtokenize@2.1.0: - resolution: {integrity: sha512-XQLu552iSctvnEcgXw6+Sx75GflAPNED1qx7eBJ+wydBb2KCbRZe+NwvIEEMM83uml1+2WSXpBAcp9IUCgCYWA==} + resolution: + { + integrity: sha512-XQLu552iSctvnEcgXw6+Sx75GflAPNED1qx7eBJ+wydBb2KCbRZe+NwvIEEMM83uml1+2WSXpBAcp9IUCgCYWA==, + } micromark-util-symbol@2.0.1: - resolution: {integrity: sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==} + resolution: + { + integrity: sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==, + } micromark-util-types@2.0.2: - resolution: {integrity: sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA==} + resolution: + { + integrity: sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA==, + } micromark@4.0.2: - resolution: {integrity: sha512-zpe98Q6kvavpCr1NPVSCMebCKfD7CA2NqZ+rykeNhONIJBpc1tFKt9hucLGwha3jNTNI8lHpctWJWoimVF4PfA==} + resolution: + { + integrity: sha512-zpe98Q6kvavpCr1NPVSCMebCKfD7CA2NqZ+rykeNhONIJBpc1tFKt9hucLGwha3jNTNI8lHpctWJWoimVF4PfA==, + } minimatch@10.2.6: - resolution: {integrity: sha512-vpLQEs+VLCr1nU0BXS07maYoFwlDAH0gngQuuttxIwutDFEMHq2blX+8vpgxDdK3J1PwjCJiep77OitTZ4Ll1A==} - engines: {node: 18 || 20 || >=22} + resolution: + { + integrity: sha512-vpLQEs+VLCr1nU0BXS07maYoFwlDAH0gngQuuttxIwutDFEMHq2blX+8vpgxDdK3J1PwjCJiep77OitTZ4Ll1A==, + } + engines: { node: 18 || 20 || >=22 } motion-dom@13.1.1: - resolution: {integrity: sha512-XSf8VYWSB6G/0IY3rWVbyLcxWXtAVHkN1PQE2agTaCv3u8RGvbwu56TyyR/MNzBqqNavEBTZzErcxI1TxBrjcA==} + resolution: + { + integrity: sha512-XSf8VYWSB6G/0IY3rWVbyLcxWXtAVHkN1PQE2agTaCv3u8RGvbwu56TyyR/MNzBqqNavEBTZzErcxI1TxBrjcA==, + } motion-utils@13.0.0: - resolution: {integrity: sha512-7DnN7TmbLcYXcG4RVadXIihWlyuM9afoUww8Y5Agg431kGKiuL2/OMyP4mJ5wLz+pvN3t5ySClLOaVXJ+wekRQ==} + resolution: + { + integrity: sha512-7DnN7TmbLcYXcG4RVadXIihWlyuM9afoUww8Y5Agg431kGKiuL2/OMyP4mJ5wLz+pvN3t5ySClLOaVXJ+wekRQ==, + } motion@13.1.1: - resolution: {integrity: sha512-WNZoK6xiF+kkTqkZ5K7FDDh6A8BG4i5Hc7KXtW8gtTxkpJFds+hIOrDaQGKjQj/AE/i4hJqAaUHEqp/Qo02y6Q==} + resolution: + { + integrity: sha512-WNZoK6xiF+kkTqkZ5K7FDDh6A8BG4i5Hc7KXtW8gtTxkpJFds+hIOrDaQGKjQj/AE/i4hJqAaUHEqp/Qo02y6Q==, + } peerDependencies: react: ^18.0.0 || ^19.0.0 react-dom: ^18.0.0 || ^19.0.0 @@ -2753,29 +4345,47 @@ packages: optional: true mri@1.2.0: - resolution: {integrity: sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==} - engines: {node: '>=4'} + resolution: + { + integrity: sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==, + } + engines: { node: '>=4' } ms@2.1.3: - resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + resolution: + { + integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==, + } mz@2.7.0: - resolution: {integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==} + resolution: + { + integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==, + } nanoid@3.3.18: - resolution: {integrity: sha512-DTg4MJbGMWkfi6VZFdNt2/caMbQy4Ou+Op/hJQvGEWcnVfoA1QA+xzRKAzw9jD6+GVOOeYr/mIcuDSdug6F6+w==} - engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + resolution: + { + integrity: sha512-DTg4MJbGMWkfi6VZFdNt2/caMbQy4Ou+Op/hJQvGEWcnVfoA1QA+xzRKAzw9jD6+GVOOeYr/mIcuDSdug6F6+w==, + } + engines: { node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1 } hasBin: true next-themes@0.4.6: - resolution: {integrity: sha512-pZvgD5L0IEvX5/9GWyHMf3m8BKiVQwsCMHfoFosXtXBMnaS0ZnIJ9ST4b4NqLVKDEm8QBxoNNGNaBv2JNF6XNA==} + resolution: + { + integrity: sha512-pZvgD5L0IEvX5/9GWyHMf3m8BKiVQwsCMHfoFosXtXBMnaS0ZnIJ9ST4b4NqLVKDEm8QBxoNNGNaBv2JNF6XNA==, + } peerDependencies: react: ^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc react-dom: ^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc next@16.3.3: - resolution: {integrity: sha512-tuRTx1nQ/yVw83cwJBo9F+njGUgMn3UHQycreWHB8XsStvvAh1AthbI8/4IpKnFaF58F+iSiHejYOlMQ/eq83g==} - engines: {node: '>=20.9.0'} + resolution: + { + integrity: sha512-tuRTx1nQ/yVw83cwJBo9F+njGUgMn3UHQycreWHB8XsStvvAh1AthbI8/4IpKnFaF58F+iSiHejYOlMQ/eq83g==, + } + engines: { node: '>=20.9.0' } hasBin: true peerDependencies: '@opentelemetry/api': ^1.1.0 @@ -2795,12 +4405,18 @@ packages: optional: true node-emoji@2.2.0: - resolution: {integrity: sha512-Z3lTE9pLaJF47NyMhd4ww1yFTAP8YhYI8SleJiHzM46Fgpm5cnNzSl9XfzFNqbaz+VlJrIj3fXQ4DeN1Rjm6cw==} - engines: {node: '>=18'} + resolution: + { + integrity: sha512-Z3lTE9pLaJF47NyMhd4ww1yFTAP8YhYI8SleJiHzM46Fgpm5cnNzSl9XfzFNqbaz+VlJrIj3fXQ4DeN1Rjm6cw==, + } + engines: { node: '>=18' } node-fetch@2.7.0: - resolution: {integrity: sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==} - engines: {node: 4.x || >=6.0.0} + resolution: + { + integrity: sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==, + } + engines: { node: 4.x || >=6.0.0 } peerDependencies: encoding: ^0.1.0 peerDependenciesMeta: @@ -2808,123 +4424,125 @@ packages: optional: true npm-to-yarn@3.2.0: - resolution: {integrity: sha512-K1HmQeZT2HrjpsR6KgqbN2FAXL2NrJJmNUSD9ck7HGTVu1JKXox8n9SB+tjbU8m8JGLF4OscrroPepew/L7/Xw==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + resolution: + { + integrity: sha512-K1HmQeZT2HrjpsR6KgqbN2FAXL2NrJJmNUSD9ck7HGTVu1JKXox8n9SB+tjbU8m8JGLF4OscrroPepew/L7/Xw==, + } + engines: { node: ^12.22.0 || ^14.17.0 || >=16.0.0 } object-assign@4.1.1: - resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} - engines: {node: '>=0.10.0'} + resolution: + { + integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==, + } + engines: { node: '>=0.10.0' } obug@2.1.4: - resolution: {integrity: sha512-4a+OsYv9UktOJKE+l1A4OufDgdRF9PifWj+tJnHURo/P+WOxpG4GzUFL9qCalmWauao6ogiG+QvnCovwPoyAWA==} - engines: {node: '>=12.20.0'} + resolution: + { + integrity: sha512-4a+OsYv9UktOJKE+l1A4OufDgdRF9PifWj+tJnHURo/P+WOxpG4GzUFL9qCalmWauao6ogiG+QvnCovwPoyAWA==, + } + engines: { node: '>=12.20.0' } oniguruma-parser@0.12.2: - resolution: {integrity: sha512-6HVa5oIrgMC6aA6WF6XyyqbhRPJrKR02L20+2+zpDtO5QAzGHAUGw5TKQvwi5vctNnRHkJYmjAhRVQF2EKdTQw==} + resolution: + { + integrity: sha512-6HVa5oIrgMC6aA6WF6XyyqbhRPJrKR02L20+2+zpDtO5QAzGHAUGw5TKQvwi5vctNnRHkJYmjAhRVQF2EKdTQw==, + } oniguruma-to-es@4.3.6: - resolution: {integrity: sha512-csuQ9x3Yr0cEIs/Zgx/OEt9iBw9vqIunAPQkx19R/fiMq2oGVTgcMqO/V3Ybqefr1TBvosI6jU539ksaBULJyA==} + resolution: + { + integrity: sha512-csuQ9x3Yr0cEIs/Zgx/OEt9iBw9vqIunAPQkx19R/fiMq2oGVTgcMqO/V3Ybqefr1TBvosI6jU539ksaBULJyA==, + } package-manager-detector@1.8.0: - resolution: {integrity: sha512-yQA4H19AmPEoMUeavPMDIe1higySl/gH/yaQrkT/s07Qp+7pp2hYz30N3z2l5BkjVkF9Ow6o0wjJamm2y7Sn0A==} + resolution: + { + integrity: sha512-yQA4H19AmPEoMUeavPMDIe1higySl/gH/yaQrkT/s07Qp+7pp2hYz30N3z2l5BkjVkF9Ow6o0wjJamm2y7Sn0A==, + } parse-entities@4.0.2: - resolution: {integrity: sha512-GG2AQYWoLgL877gQIKeRPGO1xF9+eG1ujIb5soS5gPvLQ1y2o8FL90w2QWNdf9I361Mpp7726c+lj3U0qK1uGw==} + resolution: + { + integrity: sha512-GG2AQYWoLgL877gQIKeRPGO1xF9+eG1ujIb5soS5gPvLQ1y2o8FL90w2QWNdf9I361Mpp7726c+lj3U0qK1uGw==, + } parse5-htmlparser2-tree-adapter@6.0.1: - resolution: {integrity: sha512-qPuWvbLgvDGilKc5BoicRovlT4MtYT6JfJyBOMDsKoiT+GiuP5qyrPCnR9HcPECIJJmZh5jRndyNThnhhb/vlA==} + resolution: + { + integrity: sha512-qPuWvbLgvDGilKc5BoicRovlT4MtYT6JfJyBOMDsKoiT+GiuP5qyrPCnR9HcPECIJJmZh5jRndyNThnhhb/vlA==, + } parse5@5.1.1: - resolution: {integrity: sha512-ugq4DFI0Ptb+WWjAdOK16+u/nHfiIrcE+sh8kZMaM0WllQKLI9rOUq6c2b7cwPkXdzfQESqvoqK6ug7U/Yyzug==} + resolution: + { + integrity: sha512-ugq4DFI0Ptb+WWjAdOK16+u/nHfiIrcE+sh8kZMaM0WllQKLI9rOUq6c2b7cwPkXdzfQESqvoqK6ug7U/Yyzug==, + } parse5@6.0.1: - resolution: {integrity: sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==} + resolution: + { + integrity: sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==, + } parse5@7.3.0: - resolution: {integrity: sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==} + resolution: + { + integrity: sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==, + } parse5@8.0.1: - resolution: {integrity: sha512-z1e/HMG90obSGeidlli3hj7cbocou0/wa5HacvI3ASx34PecNjNQeaHNo5WIZpWofN9kgkqV1q5YvXe3F0FoPw==} + resolution: + { + integrity: sha512-z1e/HMG90obSGeidlli3hj7cbocou0/wa5HacvI3ASx34PecNjNQeaHNo5WIZpWofN9kgkqV1q5YvXe3F0FoPw==, + } path-browserify@1.0.1: - resolution: {integrity: sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==} + resolution: + { + integrity: sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==, + } pathe@2.0.3: - resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==} + resolution: + { + integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==, + } picocolors@1.1.1: - resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} + resolution: + { + integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==, + } picomatch@4.0.7: - resolution: {integrity: sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA==} - engines: {node: '>=12'} + resolution: + { + integrity: sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA==, + } + engines: { node: '>=12' } postcss@8.5.23: - resolution: {integrity: sha512-g50586zr4bZmwFiTlflMu8E0bDTb5I5gertgwAKmsdUlTQIhZtunzUlD1WSzwcVWPoAVpsrA6vlfCD7oXvRwgg==} - engines: {node: ^10 || ^12 || >=14} + resolution: + { + integrity: sha512-g50586zr4bZmwFiTlflMu8E0bDTb5I5gertgwAKmsdUlTQIhZtunzUlD1WSzwcVWPoAVpsrA6vlfCD7oXvRwgg==, + } + engines: { node: ^10 || ^12 || >=14 } postcss@8.5.26: - resolution: {integrity: sha512-u82N74LFzG8ca+dD8puPnplTXoGH4fTPpVGuIbt36G3qvNlkvfD0lEAZSxaly3KX8TS/L1A1gsCEmvKmBcVbkQ==} - engines: {node: ^10 || ^12 || >=14} - - prettier-plugin-tailwindcss@0.7.4: - resolution: {integrity: sha512-UKii4RjY05SNt/WQi6/NcOn/LsT0/ILLXsxygjbRg5/YZelsSu5jTqorYHPDGq4nZy5q5hpCu+XdGZ1xaJEQgw==} - engines: {node: '>=20.19'} - peerDependencies: - '@ianvs/prettier-plugin-sort-imports': '*' - '@prettier/plugin-hermes': '*' - '@prettier/plugin-oxc': '*' - '@prettier/plugin-pug': '*' - '@shopify/prettier-plugin-liquid': '*' - '@trivago/prettier-plugin-sort-imports': '*' - '@zackad/prettier-plugin-twig': '*' - prettier: ^3.0 - prettier-plugin-astro: '*' - prettier-plugin-css-order: '*' - prettier-plugin-jsdoc: '*' - prettier-plugin-marko: '*' - prettier-plugin-multiline-arrays: '*' - prettier-plugin-organize-attributes: '*' - prettier-plugin-organize-imports: '*' - prettier-plugin-sort-imports: '*' - prettier-plugin-svelte: '*' - peerDependenciesMeta: - '@ianvs/prettier-plugin-sort-imports': - optional: true - '@prettier/plugin-hermes': - optional: true - '@prettier/plugin-oxc': - optional: true - '@prettier/plugin-pug': - optional: true - '@shopify/prettier-plugin-liquid': - optional: true - '@trivago/prettier-plugin-sort-imports': - optional: true - '@zackad/prettier-plugin-twig': - optional: true - prettier-plugin-astro: - optional: true - prettier-plugin-css-order: - optional: true - prettier-plugin-jsdoc: - optional: true - prettier-plugin-marko: - optional: true - prettier-plugin-multiline-arrays: - optional: true - prettier-plugin-organize-attributes: - optional: true - prettier-plugin-organize-imports: - optional: true - prettier-plugin-sort-imports: - optional: true - prettier-plugin-svelte: - optional: true + resolution: + { + integrity: sha512-u82N74LFzG8ca+dD8puPnplTXoGH4fTPpVGuIbt36G3qvNlkvfD0lEAZSxaly3KX8TS/L1A1gsCEmvKmBcVbkQ==, + } + engines: { node: ^10 || ^12 || >=14 } prettier-plugin-tailwindcss@0.8.1: - resolution: {integrity: sha512-iaFMYqDsE4ffdDkn5qup0j5f2aCEBFZrdrZnvu9QKTlWx/iGPeQ4HHu7b7fCPMxeo9nwQBiOAh2nSypdFYWJkw==} - engines: {node: '>=20.19'} + resolution: + { + integrity: sha512-iaFMYqDsE4ffdDkn5qup0j5f2aCEBFZrdrZnvu9QKTlWx/iGPeQ4HHu7b7fCPMxeo9nwQBiOAh2nSypdFYWJkw==, + } + engines: { node: '>=20.19' } peerDependencies: '@ianvs/prettier-plugin-sort-imports': '*' '@prettier/plugin-hermes': '*' @@ -2978,48 +4596,81 @@ packages: optional: true prettier@3.9.6: - resolution: {integrity: sha512-OpN0zzVdiaiAhxpuuj5efpIS4sY9j7bY6uR5mnj5yPzGkdkjNKSJeUThPb60Jw29QuAZgA4o+/iB49kFiaBX6g==} - engines: {node: '>=14'} + resolution: + { + integrity: sha512-OpN0zzVdiaiAhxpuuj5efpIS4sY9j7bY6uR5mnj5yPzGkdkjNKSJeUThPb60Jw29QuAZgA4o+/iB49kFiaBX6g==, + } + engines: { node: '>=14' } hasBin: true pretty-format@27.5.1: - resolution: {integrity: sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==} - engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} + resolution: + { + integrity: sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==, + } + engines: { node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0 } property-information@7.2.0: - resolution: {integrity: sha512-IAtzIB6sUiWaJYrX9smp3V46pBGbBeLFRGdh25kg1334VcBlD8HzhPeNIWQH9zhGmo2itIe25EHt9dQP7G5hmg==} + resolution: + { + integrity: sha512-IAtzIB6sUiWaJYrX9smp3V46pBGbBeLFRGdh25kg1334VcBlD8HzhPeNIWQH9zhGmo2itIe25EHt9dQP7G5hmg==, + } protobufjs@7.6.6: - resolution: {integrity: sha512-dYDWdjSl5RNb7SgPxGQcRU+GtvP7s2fpkrY0r432PcOIaZ0/rBcxEZnQN67iJhFuQiVw754JDoPruPCNdGsbjg==} - engines: {node: '>=12.0.0'} + resolution: + { + integrity: sha512-dYDWdjSl5RNb7SgPxGQcRU+GtvP7s2fpkrY0r432PcOIaZ0/rBcxEZnQN67iJhFuQiVw754JDoPruPCNdGsbjg==, + } + engines: { node: '>=12.0.0' } publint@0.3.24: - resolution: {integrity: sha512-9zS56KrKBoqi5Qt8h92uMP8TTM9AYZSgnmCo4u2priMqkOZvQnTsziZ2p5LJ2ywbYkAjoCDp2jda9u4cgFefIw==} - engines: {node: '>=18'} + resolution: + { + integrity: sha512-9zS56KrKBoqi5Qt8h92uMP8TTM9AYZSgnmCo4u2priMqkOZvQnTsziZ2p5LJ2ywbYkAjoCDp2jda9u4cgFefIw==, + } + engines: { node: '>=18' } hasBin: true punycode@2.3.1: - resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} - engines: {node: '>=6'} + resolution: + { + integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==, + } + engines: { node: '>=6' } quansync@1.0.0: - resolution: {integrity: sha512-5xZacEEufv3HSTPQuchrvV6soaiACMFnq1H8wkVioctoH3TRha9Sz66lOxRwPK/qZj7HPiSveih9yAyh98gvqA==} + resolution: + { + integrity: sha512-5xZacEEufv3HSTPQuchrvV6soaiACMFnq1H8wkVioctoH3TRha9Sz66lOxRwPK/qZj7HPiSveih9yAyh98gvqA==, + } re2js@2.8.6: - resolution: {integrity: sha512-xLgQil4kIUCrAzVk9fRSkxkFNwmygLFjVxXrLc65aE1F0+Zsb8rxumFBy4XKyvgMCTL6kilDq3EZ0piE2dP/Dg==} - engines: {node: '>=18.0.0'} + resolution: + { + integrity: sha512-xLgQil4kIUCrAzVk9fRSkxkFNwmygLFjVxXrLc65aE1F0+Zsb8rxumFBy4XKyvgMCTL6kilDq3EZ0piE2dP/Dg==, + } + engines: { node: '>=18.0.0' } react-dom@19.2.8: - resolution: {integrity: sha512-rVprimfGBG3DR+Tq0IQG2DT5PxKth1WIGDmj5yPmlzr4YBe7uyE+Du4oVqTDXZSHGGGXRtTJEGSSePyQCMBglQ==} + resolution: + { + integrity: sha512-rVprimfGBG3DR+Tq0IQG2DT5PxKth1WIGDmj5yPmlzr4YBe7uyE+Du4oVqTDXZSHGGGXRtTJEGSSePyQCMBglQ==, + } peerDependencies: react: ^19.2.8 react-is@17.0.2: - resolution: {integrity: sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==} + resolution: + { + integrity: sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==, + } react-remove-scroll-bar@2.3.8: - resolution: {integrity: sha512-9r+yi9+mgU33AKcj6IbT9oRCO78WriSj6t/cF8DWBZJ9aOGPOTEDvdUDz1FwKim7QXWwmHqtdHnRJfhAxEG46Q==} - engines: {node: '>=10'} + resolution: + { + integrity: sha512-9r+yi9+mgU33AKcj6IbT9oRCO78WriSj6t/cF8DWBZJ9aOGPOTEDvdUDz1FwKim7QXWwmHqtdHnRJfhAxEG46Q==, + } + engines: { node: '>=10' } peerDependencies: '@types/react': '*' react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 @@ -3028,8 +4679,11 @@ packages: optional: true react-remove-scroll@2.7.2: - resolution: {integrity: sha512-Iqb9NjCCTt6Hf+vOdNIZGdTiH1QSqr27H/Ek9sv/a97gfueI/5h1s3yRi1nngzMUaOOToin5dI1dXKdXiF+u0Q==} - engines: {node: '>=10'} + resolution: + { + integrity: sha512-Iqb9NjCCTt6Hf+vOdNIZGdTiH1QSqr27H/Ek9sv/a97gfueI/5h1s3yRi1nngzMUaOOToin5dI1dXKdXiF+u0Q==, + } + engines: { node: '>=10' } peerDependencies: '@types/react': '*' react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc @@ -3038,8 +4692,11 @@ packages: optional: true react-style-singleton@2.2.3: - resolution: {integrity: sha512-b6jSvxvVnyptAiLjbkWLE/lOnR4lfTtDAl+eUC7RZy+QQWc6wRzIV2CE6xBuMmDxc2qIihtDCZD5NPOFl7fRBQ==} - engines: {node: '>=10'} + resolution: + { + integrity: sha512-b6jSvxvVnyptAiLjbkWLE/lOnR4lfTtDAl+eUC7RZy+QQWc6wRzIV2CE6xBuMmDxc2qIihtDCZD5NPOFl7fRBQ==, + } + engines: { node: '>=10' } peerDependencies: '@types/react': '*' react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc @@ -3048,77 +4705,143 @@ packages: optional: true react@19.2.8: - resolution: {integrity: sha512-PWaYA1L/q9u2u7xYQi+Y3L3Yfnie7XyLeaJICV1MGD6LprsBxcAqGjYyr0eY3p+QdsA+x/Irkt4Qif8D63+Sbw==} - engines: {node: '>=0.10.0'} + resolution: + { + integrity: sha512-PWaYA1L/q9u2u7xYQi+Y3L3Yfnie7XyLeaJICV1MGD6LprsBxcAqGjYyr0eY3p+QdsA+x/Irkt4Qif8D63+Sbw==, + } + engines: { node: '>=0.10.0' } readdirp@5.1.1: - resolution: {integrity: sha512-Kko+Y5XQ6fM+Ce3dq3m9YGxnacYZYl9cA1wZjaF3Vbry2L3i1qVg8+CAgNPsXRArPMUMCaOR7oa9Nqntc43JKA==} - engines: {node: '>= 20.19.0'} + resolution: + { + integrity: sha512-Kko+Y5XQ6fM+Ce3dq3m9YGxnacYZYl9cA1wZjaF3Vbry2L3i1qVg8+CAgNPsXRArPMUMCaOR7oa9Nqntc43JKA==, + } + engines: { node: '>= 20.19.0' } recma-build-jsx@1.0.0: - resolution: {integrity: sha512-8GtdyqaBcDfva+GUKDr3nev3VpKAhup1+RvkMvUxURHpW7QyIvk9F5wz7Vzo06CEMSilw6uArgRqhpiUcWp8ew==} + resolution: + { + integrity: sha512-8GtdyqaBcDfva+GUKDr3nev3VpKAhup1+RvkMvUxURHpW7QyIvk9F5wz7Vzo06CEMSilw6uArgRqhpiUcWp8ew==, + } recma-jsx@1.0.1: - resolution: {integrity: sha512-huSIy7VU2Z5OLv6oFLosQGGDqPqdO1iq6bWNAdhzMxSJP7RAso4fCZ1cKu8j9YHCZf3TPrq4dw3okhrylgcd7w==} + resolution: + { + integrity: sha512-huSIy7VU2Z5OLv6oFLosQGGDqPqdO1iq6bWNAdhzMxSJP7RAso4fCZ1cKu8j9YHCZf3TPrq4dw3okhrylgcd7w==, + } peerDependencies: acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 recma-parse@1.0.0: - resolution: {integrity: sha512-OYLsIGBB5Y5wjnSnQW6t3Xg7q3fQ7FWbw/vcXtORTnyaSFscOtABg+7Pnz6YZ6c27fG1/aN8CjfwoUEUIdwqWQ==} + resolution: + { + integrity: sha512-OYLsIGBB5Y5wjnSnQW6t3Xg7q3fQ7FWbw/vcXtORTnyaSFscOtABg+7Pnz6YZ6c27fG1/aN8CjfwoUEUIdwqWQ==, + } recma-stringify@1.0.0: - resolution: {integrity: sha512-cjwII1MdIIVloKvC9ErQ+OgAtwHBmcZ0Bg4ciz78FtbT8In39aAYbaA7zvxQ61xVMSPE8WxhLwLbhif4Js2C+g==} + resolution: + { + integrity: sha512-cjwII1MdIIVloKvC9ErQ+OgAtwHBmcZ0Bg4ciz78FtbT8In39aAYbaA7zvxQ61xVMSPE8WxhLwLbhif4Js2C+g==, + } regex-recursion@6.0.2: - resolution: {integrity: sha512-0YCaSCq2VRIebiaUviZNs0cBz1kg5kVS2UKUfNIx8YVs1cN3AV7NTctO5FOKBA+UT2BPJIWZauYHPqJODG50cg==} + resolution: + { + integrity: sha512-0YCaSCq2VRIebiaUviZNs0cBz1kg5kVS2UKUfNIx8YVs1cN3AV7NTctO5FOKBA+UT2BPJIWZauYHPqJODG50cg==, + } regex-utilities@2.3.0: - resolution: {integrity: sha512-8VhliFJAWRaUiVvREIiW2NXXTmHs4vMNnSzuJVhscgmGav3g9VDxLrQndI3dZZVVdp0ZO/5v0xmX516/7M9cng==} + resolution: + { + integrity: sha512-8VhliFJAWRaUiVvREIiW2NXXTmHs4vMNnSzuJVhscgmGav3g9VDxLrQndI3dZZVVdp0ZO/5v0xmX516/7M9cng==, + } regex@6.1.0: - resolution: {integrity: sha512-6VwtthbV4o/7+OaAF9I5L5V3llLEsoPyq9P1JVXkedTP33c7MfCG0/5NOPcSJn0TzXcG9YUrR0gQSWioew3LDg==} + resolution: + { + integrity: sha512-6VwtthbV4o/7+OaAF9I5L5V3llLEsoPyq9P1JVXkedTP33c7MfCG0/5NOPcSJn0TzXcG9YUrR0gQSWioew3LDg==, + } rehype-raw@7.0.0: - resolution: {integrity: sha512-/aE8hCfKlQeA8LmyeyQvQF3eBiLRGNlfBJEvWH7ivp9sBqs7TNqBL5X3v157rM4IFETqDnIOO+z5M/biZbo9Ww==} + resolution: + { + integrity: sha512-/aE8hCfKlQeA8LmyeyQvQF3eBiLRGNlfBJEvWH7ivp9sBqs7TNqBL5X3v157rM4IFETqDnIOO+z5M/biZbo9Ww==, + } rehype-recma@1.0.0: - resolution: {integrity: sha512-lqA4rGUf1JmacCNWWZx0Wv1dHqMwxzsDWYMTowuplHF3xH0N/MmrZ/G3BDZnzAkRmxDadujCjaKM2hqYdCBOGw==} + resolution: + { + integrity: sha512-lqA4rGUf1JmacCNWWZx0Wv1dHqMwxzsDWYMTowuplHF3xH0N/MmrZ/G3BDZnzAkRmxDadujCjaKM2hqYdCBOGw==, + } remark-gfm@4.0.1: - resolution: {integrity: sha512-1quofZ2RQ9EWdeN34S79+KExV1764+wCUGop5CPL1WGdD0ocPpu91lzPGbwWMECpEpd42kJGQwzRfyov9j4yNg==} + resolution: + { + integrity: sha512-1quofZ2RQ9EWdeN34S79+KExV1764+wCUGop5CPL1WGdD0ocPpu91lzPGbwWMECpEpd42kJGQwzRfyov9j4yNg==, + } remark-mdx@3.1.1: - resolution: {integrity: sha512-Pjj2IYlUY3+D8x00UJsIOg5BEvfMyeI+2uLPn9VO9Wg4MEtN/VTIq2NEJQfde9PnX15KgtHyl9S0BcTnWrIuWg==} + resolution: + { + integrity: sha512-Pjj2IYlUY3+D8x00UJsIOg5BEvfMyeI+2uLPn9VO9Wg4MEtN/VTIq2NEJQfde9PnX15KgtHyl9S0BcTnWrIuWg==, + } remark-parse@11.0.0: - resolution: {integrity: sha512-FCxlKLNGknS5ba/1lmpYijMUzX2esxW5xQqjWxw2eHFfS2MSdaHVINFmhjo+qN1WhZhNimq0dZATN9pH0IDrpA==} + resolution: + { + integrity: sha512-FCxlKLNGknS5ba/1lmpYijMUzX2esxW5xQqjWxw2eHFfS2MSdaHVINFmhjo+qN1WhZhNimq0dZATN9pH0IDrpA==, + } remark-rehype@11.1.2: - resolution: {integrity: sha512-Dh7l57ianaEoIpzbp0PC9UKAdCSVklD8E5Rpw7ETfbTl3FqcOOgq5q2LVDhgGCkaBv7p24JXikPdvhhmHvKMsw==} + resolution: + { + integrity: sha512-Dh7l57ianaEoIpzbp0PC9UKAdCSVklD8E5Rpw7ETfbTl3FqcOOgq5q2LVDhgGCkaBv7p24JXikPdvhhmHvKMsw==, + } remark-stringify@11.0.0: - resolution: {integrity: sha512-1OSmLd3awB/t8qdoEOMazZkNsfVTeY4fTsgzcQFdXNq8ToTN4ZGwrMnlda4K6smTFKD+GRV6O48i6Z4iKgPPpw==} + resolution: + { + integrity: sha512-1OSmLd3awB/t8qdoEOMazZkNsfVTeY4fTsgzcQFdXNq8ToTN4ZGwrMnlda4K6smTFKD+GRV6O48i6Z4iKgPPpw==, + } remark@15.0.1: - resolution: {integrity: sha512-Eht5w30ruCXgFmxVUSlNWQ9iiimq07URKeFS3hNc8cUWy1llX4KDWfyEDZRycMc+znsN9Ux5/tJ/BFdgdOwA3A==} + resolution: + { + integrity: sha512-Eht5w30ruCXgFmxVUSlNWQ9iiimq07URKeFS3hNc8cUWy1llX4KDWfyEDZRycMc+znsN9Ux5/tJ/BFdgdOwA3A==, + } require-directory@2.1.1: - resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} - engines: {node: '>=0.10.0'} + resolution: + { + integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==, + } + engines: { node: '>=0.10.0' } require-from-string@2.0.2: - resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} - engines: {node: '>=0.10.0'} + resolution: + { + integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==, + } + engines: { node: '>=0.10.0' } reselect@5.3.0: - resolution: {integrity: sha512-XGoLeRAVzUTcJ1qkxPQhDJyIZ5d6zzZD9nT7AEZOaaU9UbWclhycElmhO+VD5bFeLuzhPBaOV2oXC8uG35ZSpg==} + resolution: + { + integrity: sha512-XGoLeRAVzUTcJ1qkxPQhDJyIZ5d6zzZD9nT7AEZOaaU9UbWclhycElmhO+VD5bFeLuzhPBaOV2oXC8uG35ZSpg==, + } resolve-pkg-maps@1.0.0: - resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==} + resolution: + { + integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==, + } rolldown-plugin-dts@0.27.14: - resolution: {integrity: sha512-ZvuDDwoIpRK9RPxDXratCpklFO9QZZWndf/sd0VBFb4LEj0jj07UcHK9OCh7V4XiFz2Z89ziyBC2K6tJiDjrbw==} - engines: {node: ^22.18.0 || >=24.11.0} + resolution: + { + integrity: sha512-ZvuDDwoIpRK9RPxDXratCpklFO9QZZWndf/sd0VBFb4LEj0jj07UcHK9OCh7V4XiFz2Z89ziyBC2K6tJiDjrbw==, + } + engines: { node: ^22.18.0 || >=24.11.0 } peerDependencies: '@typescript/native-preview': '*' '@volar/typescript': ~2.4.0 @@ -3136,35 +4859,59 @@ packages: optional: true rolldown@1.2.6: - resolution: {integrity: sha512-vMM4q3aixf46GiF1Kok8jDPFsEpXgFWGjUHXNkNHNm+Y2adXAG2dbX91jkti3i0ZRsOlcmbuzAz1poObSHCmUA==} - engines: {node: ^20.19.0 || >=22.12.0} + resolution: + { + integrity: sha512-vMM4q3aixf46GiF1Kok8jDPFsEpXgFWGjUHXNkNHNm+Y2adXAG2dbX91jkti3i0ZRsOlcmbuzAz1poObSHCmUA==, + } + engines: { node: ^20.19.0 || >=22.12.0 } hasBin: true sade@1.8.1: - resolution: {integrity: sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A==} - engines: {node: '>=6'} + resolution: + { + integrity: sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A==, + } + engines: { node: '>=6' } safe-buffer@5.2.1: - resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} + resolution: + { + integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==, + } saxes@6.0.0: - resolution: {integrity: sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==} - engines: {node: '>=v12.22.7'} + resolution: + { + integrity: sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==, + } + engines: { node: '>=v12.22.7' } scheduler@0.27.0: - resolution: {integrity: sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==} + resolution: + { + integrity: sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==, + } scroll-into-view-if-needed@3.1.0: - resolution: {integrity: sha512-49oNpRjWRvnU8NyGVmUaYG4jtTkNonFZI86MmGRDqBphEK2EXT9gdEUoQPZhuBM8yWHxCWbobltqYO5M4XrUvQ==} + resolution: + { + integrity: sha512-49oNpRjWRvnU8NyGVmUaYG4jtTkNonFZI86MmGRDqBphEK2EXT9gdEUoQPZhuBM8yWHxCWbobltqYO5M4XrUvQ==, + } semver@7.8.5: - resolution: {integrity: sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==} - engines: {node: '>=10'} + resolution: + { + integrity: sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==, + } + engines: { node: '>=10' } hasBin: true sharp@0.35.4: - resolution: {integrity: sha512-n++8XWcj+jCOr2IOl7h8LbKnGBDY4aPbmprMONBNFdn0ImXqpGVv5zliDs0V9HbmbCQLpbuo2ej9rAoOQTvMDA==} - engines: {node: '>=20.9.0'} + resolution: + { + integrity: sha512-n++8XWcj+jCOr2IOl7h8LbKnGBDY4aPbmprMONBNFdn0ImXqpGVv5zliDs0V9HbmbCQLpbuo2ej9rAoOQTvMDA==, + } + engines: { node: '>=20.9.0' } peerDependencies: '@types/node': '*' peerDependenciesMeta: @@ -3172,64 +4919,115 @@ packages: optional: true shell-quote@1.10.0: - resolution: {integrity: sha512-w1aiOKwKuRgtwAReIIj89puqg+I7GvX4IbLrvmhXbzQsj1+Zwi4VO3+fa6ZF91TWSjIxoEkKnMeHcLEODK5ZXA==} - engines: {node: '>= 0.4'} + resolution: + { + integrity: sha512-w1aiOKwKuRgtwAReIIj89puqg+I7GvX4IbLrvmhXbzQsj1+Zwi4VO3+fa6ZF91TWSjIxoEkKnMeHcLEODK5ZXA==, + } + engines: { node: '>= 0.4' } shiki@4.4.3: - resolution: {integrity: sha512-Mb/GvXPHBAXdgGIcnfU5L3ldpn1XcxrGkPHwqgRx17/I2XRfqlFKk2vGkHWINn1kdXvzJZeuO3is6I9KLPFm0g==} - engines: {node: '>=20'} + resolution: + { + integrity: sha512-Mb/GvXPHBAXdgGIcnfU5L3ldpn1XcxrGkPHwqgRx17/I2XRfqlFKk2vGkHWINn1kdXvzJZeuO3is6I9KLPFm0g==, + } + engines: { node: '>=20' } siginfo@2.0.0: - resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==} + resolution: + { + integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==, + } sisteransi@1.0.5: - resolution: {integrity: sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==} + resolution: + { + integrity: sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==, + } skin-tone@2.0.0: - resolution: {integrity: sha512-kUMbT1oBJCpgrnKoSr0o6wPtvRWT9W9UKvGLwfJYO2WuahZRHOpEyL1ckyMGgMWh0UdpmaoFqKKD29WTomNEGA==} - engines: {node: '>=8'} + resolution: + { + integrity: sha512-kUMbT1oBJCpgrnKoSr0o6wPtvRWT9W9UKvGLwfJYO2WuahZRHOpEyL1ckyMGgMWh0UdpmaoFqKKD29WTomNEGA==, + } + engines: { node: '>=8' } source-map-js@1.2.1: - resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} - engines: {node: '>=0.10.0'} + resolution: + { + integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==, + } + engines: { node: '>=0.10.0' } source-map@0.7.6: - resolution: {integrity: sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ==} - engines: {node: '>= 12'} + resolution: + { + integrity: sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ==, + } + engines: { node: '>= 12' } space-separated-tokens@2.0.2: - resolution: {integrity: sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==} + resolution: + { + integrity: sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==, + } stackback@0.0.2: - resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} + resolution: + { + integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==, + } std-env@4.2.0: - resolution: {integrity: sha512-oCUKSupKTHX53EyjDtuZQ64pjLJ6yYCtpmEw0goYxtjG9KpbRe8KAsl2tBUGU9DyMcJ0RwJ8GqJAFzMXcXW1Rw==} + resolution: + { + integrity: sha512-oCUKSupKTHX53EyjDtuZQ64pjLJ6yYCtpmEw0goYxtjG9KpbRe8KAsl2tBUGU9DyMcJ0RwJ8GqJAFzMXcXW1Rw==, + } string-argv@0.3.2: - resolution: {integrity: sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==} - engines: {node: '>=0.6.19'} + resolution: + { + integrity: sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==, + } + engines: { node: '>=0.6.19' } string-width@4.2.3: - resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} - engines: {node: '>=8'} + resolution: + { + integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==, + } + engines: { node: '>=8' } stringify-entities@4.0.4: - resolution: {integrity: sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==} + resolution: + { + integrity: sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==, + } strip-ansi@6.0.1: - resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} - engines: {node: '>=8'} + resolution: + { + integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==, + } + engines: { node: '>=8' } style-to-js@1.1.21: - resolution: {integrity: sha512-RjQetxJrrUJLQPHbLku6U/ocGtzyjbJMP9lCNK7Ag0CNh690nSH8woqWH9u16nMjYBAok+i7JO1NP2pOy8IsPQ==} + resolution: + { + integrity: sha512-RjQetxJrrUJLQPHbLku6U/ocGtzyjbJMP9lCNK7Ag0CNh690nSH8woqWH9u16nMjYBAok+i7JO1NP2pOy8IsPQ==, + } style-to-object@1.0.14: - resolution: {integrity: sha512-LIN7rULI0jBscWQYaSswptyderlarFkjQ+t79nzty8tcIAceVomEVlLzH5VP4Cmsv6MtKhs7qaAiwlcp+Mgaxw==} + resolution: + { + integrity: sha512-LIN7rULI0jBscWQYaSswptyderlarFkjQ+t79nzty8tcIAceVomEVlLzH5VP4Cmsv6MtKhs7qaAiwlcp+Mgaxw==, + } styled-jsx@5.1.6: - resolution: {integrity: sha512-qSVyDTeMotdvQYoHWLNGwRFJHC+i+ZvdBRYosOFgC+Wg1vx4frN2/RG/NA7SYqqvKNLf39P2LSRA2pu6n0XYZA==} - engines: {node: '>= 12.0.0'} + resolution: + { + integrity: sha512-qSVyDTeMotdvQYoHWLNGwRFJHC+i+ZvdBRYosOFgC+Wg1vx4frN2/RG/NA7SYqqvKNLf39P2LSRA2pu6n0XYZA==, + } + engines: { node: '>= 12.0.0' } peerDependencies: '@babel/core': '*' babel-plugin-macros: '*' @@ -3241,79 +5039,142 @@ packages: optional: true supports-color@7.2.0: - resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} - engines: {node: '>=8'} + resolution: + { + integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==, + } + engines: { node: '>=8' } supports-hyperlinks@3.2.0: - resolution: {integrity: sha512-zFObLMyZeEwzAoKCyu1B91U79K2t7ApXuQfo8OuxwXLDgcKxuwM+YvcbIhm6QWqz7mHUH1TVytR1PwVVjEuMig==} - engines: {node: '>=14.18'} + resolution: + { + integrity: sha512-zFObLMyZeEwzAoKCyu1B91U79K2t7ApXuQfo8OuxwXLDgcKxuwM+YvcbIhm6QWqz7mHUH1TVytR1PwVVjEuMig==, + } + engines: { node: '>=14.18' } symbol-tree@3.2.4: - resolution: {integrity: sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==} + resolution: + { + integrity: sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==, + } tailwindcss@4.3.3: - resolution: {integrity: sha512-gOhV3P7ufE62QDGg1zVaTgCR+EtPv92k2nIhVcVKcLmxT1sUBsQGhnZj175j+MqRt4zLF7ic+sCYjfhxMxj7YQ==} + resolution: + { + integrity: sha512-gOhV3P7ufE62QDGg1zVaTgCR+EtPv92k2nIhVcVKcLmxT1sUBsQGhnZj175j+MqRt4zLF7ic+sCYjfhxMxj7YQ==, + } tapable@2.3.3: - resolution: {integrity: sha512-uxc/zpqFg6x7C8vOE7lh6Lbda8eEL9zmVm/PLeTPBRhh1xCgdWaQ+J1CUieGpIfm2HdtsUpRv+HshiasBMcc6A==} - engines: {node: '>=6'} + resolution: + { + integrity: sha512-uxc/zpqFg6x7C8vOE7lh6Lbda8eEL9zmVm/PLeTPBRhh1xCgdWaQ+J1CUieGpIfm2HdtsUpRv+HshiasBMcc6A==, + } + engines: { node: '>=6' } thenify-all@1.6.0: - resolution: {integrity: sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==} - engines: {node: '>=0.8'} + resolution: + { + integrity: sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==, + } + engines: { node: '>=0.8' } thenify@3.3.1: - resolution: {integrity: sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==} + resolution: + { + integrity: sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==, + } tinybench@2.9.0: - resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==} + resolution: + { + integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==, + } tinyexec@1.3.0: - resolution: {integrity: sha512-QKAl9m8gWWGHV8jZcPeym6j+XULi6tOf1mT83WYJ4Lk2ytW/uwAWkrP0uFsdoYMdueVJ0qs26wZ+23xeB4ibNQ==} - engines: {node: '>=18'} + resolution: + { + integrity: sha512-QKAl9m8gWWGHV8jZcPeym6j+XULi6tOf1mT83WYJ4Lk2ytW/uwAWkrP0uFsdoYMdueVJ0qs26wZ+23xeB4ibNQ==, + } + engines: { node: '>=18' } tinyglobby@0.2.17: - resolution: {integrity: sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==} - engines: {node: '>=12.0.0'} + resolution: + { + integrity: sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==, + } + engines: { node: '>=12.0.0' } tinyrainbow@3.1.1: - resolution: {integrity: sha512-yau8yJdTt989Mm0Bd/236QnzEiPf2xLLTqUZRUJOo/3CB078LSwzei343DgtJVmfJKJE3TMINY1u42SQsP6mXw==} - engines: {node: '>=14.0.0'} + resolution: + { + integrity: sha512-yau8yJdTt989Mm0Bd/236QnzEiPf2xLLTqUZRUJOo/3CB078LSwzei343DgtJVmfJKJE3TMINY1u42SQsP6mXw==, + } + engines: { node: '>=14.0.0' } tldts-core@7.4.11: - resolution: {integrity: sha512-CW3WN2rIIE/Of21mulhgnGOwoDyEFNygyIBOONSdyAuSATgMMUCpLeUlB+E8sAwA5xRV9hYPl+kyZ9citHCaKg==} + resolution: + { + integrity: sha512-CW3WN2rIIE/Of21mulhgnGOwoDyEFNygyIBOONSdyAuSATgMMUCpLeUlB+E8sAwA5xRV9hYPl+kyZ9citHCaKg==, + } tldts@7.4.11: - resolution: {integrity: sha512-aBiNayCfTQxuIJBm06M+xR14cYaYlDlSXZbgsnKzKNxDKUVq7KFwTjwBSsb7m9Y5xO8WfPnBc63WaYFMTGlvqw==} + resolution: + { + integrity: sha512-aBiNayCfTQxuIJBm06M+xR14cYaYlDlSXZbgsnKzKNxDKUVq7KFwTjwBSsb7m9Y5xO8WfPnBc63WaYFMTGlvqw==, + } hasBin: true tough-cookie@6.0.2: - resolution: {integrity: sha512-exgYmnmL/sJpR3upZfXG5PoatXQii55xAiXGXzY+sROLZ/Y+SLcp9PgJNI9Vz37HpQ74WvDcLT8eqm+kV3FzrA==} - engines: {node: '>=16'} + resolution: + { + integrity: sha512-exgYmnmL/sJpR3upZfXG5PoatXQii55xAiXGXzY+sROLZ/Y+SLcp9PgJNI9Vz37HpQ74WvDcLT8eqm+kV3FzrA==, + } + engines: { node: '>=16' } tr46@0.0.3: - resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==} + resolution: + { + integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==, + } tr46@6.0.0: - resolution: {integrity: sha512-bLVMLPtstlZ4iMQHpFHTR7GAGj2jxi8Dg0s2h2MafAE4uSWF98FC/3MomU51iQAMf8/qDUbKWf5GxuvvVcXEhw==} - engines: {node: '>=20'} + resolution: + { + integrity: sha512-bLVMLPtstlZ4iMQHpFHTR7GAGj2jxi8Dg0s2h2MafAE4uSWF98FC/3MomU51iQAMf8/qDUbKWf5GxuvvVcXEhw==, + } + engines: { node: '>=20' } tree-kill@1.2.2: - resolution: {integrity: sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==} + resolution: + { + integrity: sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==, + } hasBin: true trim-lines@3.0.1: - resolution: {integrity: sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==} + resolution: + { + integrity: sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==, + } trough@2.2.0: - resolution: {integrity: sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw==} + resolution: + { + integrity: sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw==, + } ts-morph@27.0.2: - resolution: {integrity: sha512-fhUhgeljcrdZ+9DZND1De1029PrE+cMkIP7ooqkLRTrRLTqcki2AstsyJm0vRNbTbVCNJ0idGlbBrfqc7/nA8w==} + resolution: + { + integrity: sha512-fhUhgeljcrdZ+9DZND1De1029PrE+cMkIP7ooqkLRTrRLTqcki2AstsyJm0vRNbTbVCNJ0idGlbBrfqc7/nA8w==, + } tsdown@0.22.14: - resolution: {integrity: sha512-ule7Y+fsAN2iZbLDoo7C4KYljFJNJJ+fLshyn+9gozeTspVersWHxwdGB+Dm2hzA38s6muFnUTl0jK3vJm9ifQ==} - engines: {node: ^22.18.0 || >=24.11.0} + resolution: + { + integrity: sha512-ule7Y+fsAN2iZbLDoo7C4KYljFJNJJ+fLshyn+9gozeTspVersWHxwdGB+Dm2hzA38s6muFnUTl0jK3vJm9ifQ==, + } + engines: { node: ^22.18.0 || >=24.11.0 } hasBin: true peerDependencies: '@arethetypeswrong/core': ^0.18.1 @@ -3346,64 +5207,115 @@ packages: optional: true tslib@2.8.1: - resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} + resolution: + { + integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==, + } typescript@5.6.1-rc: - resolution: {integrity: sha512-E3b2+1zEFu84jB0YQi9BORDjz9+jGbwwy1Zi3G0LUNw7a7cePUrHMRNy8aPh53nXpkFGVHSxIZo5vKTfYaFiBQ==} - engines: {node: '>=14.17'} + resolution: + { + integrity: sha512-E3b2+1zEFu84jB0YQi9BORDjz9+jGbwwy1Zi3G0LUNw7a7cePUrHMRNy8aPh53nXpkFGVHSxIZo5vKTfYaFiBQ==, + } + engines: { node: '>=14.17' } hasBin: true typescript@5.9.3: - resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==} - engines: {node: '>=14.17'} + resolution: + { + integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==, + } + engines: { node: '>=14.17' } hasBin: true typescript@6.0.3: - resolution: {integrity: sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==} - engines: {node: '>=14.17'} + resolution: + { + integrity: sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==, + } + engines: { node: '>=14.17' } hasBin: true unconfig-core@7.5.0: - resolution: {integrity: sha512-Su3FauozOGP44ZmKdHy2oE6LPjk51M/TRRjHv2HNCWiDvfvCoxC2lno6jevMA91MYAdCdwP05QnWdWpSbncX/w==} + resolution: + { + integrity: sha512-Su3FauozOGP44ZmKdHy2oE6LPjk51M/TRRjHv2HNCWiDvfvCoxC2lno6jevMA91MYAdCdwP05QnWdWpSbncX/w==, + } undici-types@8.3.0: - resolution: {integrity: sha512-j375ScV60dom+YkPFIfTLcOiPxkN/buHz5GobjLhixFuANaNs3C9l4GmrWqejgXWJ7BbJcFYpTEUkS1Ge8bpZQ==} + resolution: + { + integrity: sha512-j375ScV60dom+YkPFIfTLcOiPxkN/buHz5GobjLhixFuANaNs3C9l4GmrWqejgXWJ7BbJcFYpTEUkS1Ge8bpZQ==, + } undici@7.29.0: - resolution: {integrity: sha512-IDxfleLmmbSskfWSUATiN1nfn2rDuvnMOqb5CWR92iIfojA0Ud+ulOAAEQ57LPr9rWmsreUyf5lwyao+7GNNVw==} - engines: {node: '>=20.18.1'} + resolution: + { + integrity: sha512-IDxfleLmmbSskfWSUATiN1nfn2rDuvnMOqb5CWR92iIfojA0Ud+ulOAAEQ57LPr9rWmsreUyf5lwyao+7GNNVw==, + } + engines: { node: '>=20.18.1' } unicode-emoji-modifier-base@1.0.0: - resolution: {integrity: sha512-yLSH4py7oFH3oG/9K+XWrz1pSi3dfUrWEnInbxMfArOfc1+33BlGPQtLsOYwvdMy11AwUBetYuaRxSPqgkq+8g==} - engines: {node: '>=4'} + resolution: + { + integrity: sha512-yLSH4py7oFH3oG/9K+XWrz1pSi3dfUrWEnInbxMfArOfc1+33BlGPQtLsOYwvdMy11AwUBetYuaRxSPqgkq+8g==, + } + engines: { node: '>=4' } unified@11.0.5: - resolution: {integrity: sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA==} + resolution: + { + integrity: sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA==, + } unist-util-is@6.0.1: - resolution: {integrity: sha512-LsiILbtBETkDz8I9p1dQ0uyRUWuaQzd/cuEeS1hoRSyW5E5XGmTzlwY1OrNzzakGowI9Dr/I8HVaw4hTtnxy8g==} + resolution: + { + integrity: sha512-LsiILbtBETkDz8I9p1dQ0uyRUWuaQzd/cuEeS1hoRSyW5E5XGmTzlwY1OrNzzakGowI9Dr/I8HVaw4hTtnxy8g==, + } unist-util-position-from-estree@2.0.0: - resolution: {integrity: sha512-KaFVRjoqLyF6YXCbVLNad/eS4+OfPQQn2yOd7zF/h5T/CSL2v8NpN6a5TPvtbXthAGw5nG+PuTtq+DdIZr+cRQ==} + resolution: + { + integrity: sha512-KaFVRjoqLyF6YXCbVLNad/eS4+OfPQQn2yOd7zF/h5T/CSL2v8NpN6a5TPvtbXthAGw5nG+PuTtq+DdIZr+cRQ==, + } unist-util-position@5.0.0: - resolution: {integrity: sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==} + resolution: + { + integrity: sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==, + } unist-util-remove-position@5.0.0: - resolution: {integrity: sha512-Hp5Kh3wLxv0PHj9m2yZhhLt58KzPtEYKQQ4yxfYFEO7EvHwzyDYnduhHnY1mDxoqr7VUwVuHXk9RXKIiYS1N8Q==} + resolution: + { + integrity: sha512-Hp5Kh3wLxv0PHj9m2yZhhLt58KzPtEYKQQ4yxfYFEO7EvHwzyDYnduhHnY1mDxoqr7VUwVuHXk9RXKIiYS1N8Q==, + } unist-util-stringify-position@4.0.0: - resolution: {integrity: sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==} + resolution: + { + integrity: sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==, + } unist-util-visit-parents@6.0.2: - resolution: {integrity: sha512-goh1s1TBrqSqukSc8wrjwWhL0hiJxgA8m4kFxGlQ+8FYQ3C/m11FcTs4YYem7V664AhHVvgoQLk890Ssdsr2IQ==} + resolution: + { + integrity: sha512-goh1s1TBrqSqukSc8wrjwWhL0hiJxgA8m4kFxGlQ+8FYQ3C/m11FcTs4YYem7V664AhHVvgoQLk890Ssdsr2IQ==, + } unist-util-visit@5.1.0: - resolution: {integrity: sha512-m+vIdyeCOpdr/QeQCu2EzxX/ohgS8KbnPDgFni4dQsfSCtpz8UqDyY5GjRru8PDKuYn7Fq19j1CQ+nJSsGKOzg==} + resolution: + { + integrity: sha512-m+vIdyeCOpdr/QeQCu2EzxX/ohgS8KbnPDgFni4dQsfSCtpz8UqDyY5GjRru8PDKuYn7Fq19j1CQ+nJSsGKOzg==, + } unrun@0.3.1: - resolution: {integrity: sha512-onIck/oNnCaytwths1ZVp1LK2Gq2hPoyFhiHebObuUXqR3S0uHuLLaBK8K6mRRgV7Ptip8AnNvaUsgzwWwBZuA==} - engines: {node: ^22.13.0 || >=24.0.0} + resolution: + { + integrity: sha512-onIck/oNnCaytwths1ZVp1LK2Gq2hPoyFhiHebObuUXqR3S0uHuLLaBK8K6mRRgV7Ptip8AnNvaUsgzwWwBZuA==, + } + engines: { node: ^22.13.0 || >=24.0.0 } hasBin: true peerDependencies: synckit: ^0.11.11 @@ -3412,8 +5324,11 @@ packages: optional: true use-callback-ref@1.3.3: - resolution: {integrity: sha512-jQL3lRnocaFtu3V00JToYz/4QkNWswxijDaCVNZRiRTO3HQDLsdu1ZtmIUvV4yPp+rvWm5j0y0TG/S61cuijTg==} - engines: {node: '>=10'} + resolution: + { + integrity: sha512-jQL3lRnocaFtu3V00JToYz/4QkNWswxijDaCVNZRiRTO3HQDLsdu1ZtmIUvV4yPp+rvWm5j0y0TG/S61cuijTg==, + } + engines: { node: '>=10' } peerDependencies: '@types/react': '*' react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc @@ -3422,8 +5337,11 @@ packages: optional: true use-sidecar@1.1.3: - resolution: {integrity: sha512-Fedw0aZvkhynoPYlA5WXrMCAMm+nSWdZt6lzJQ7Ok8S6Q+VsHmHpRWndVRJ8Be0ZbkfPc5LRYH+5XrzXcEeLRQ==} - engines: {node: '>=10'} + resolution: + { + integrity: sha512-Fedw0aZvkhynoPYlA5WXrMCAMm+nSWdZt6lzJQ7Ok8S6Q+VsHmHpRWndVRJ8Be0ZbkfPc5LRYH+5XrzXcEeLRQ==, + } + engines: { node: '>=10' } peerDependencies: '@types/react': '*' react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc @@ -3432,30 +5350,51 @@ packages: optional: true use-sync-external-store@1.6.0: - resolution: {integrity: sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w==} + resolution: + { + integrity: sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w==, + } peerDependencies: react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 validate-npm-package-name@5.0.1: - resolution: {integrity: sha512-OljLrQ9SQdOUqTaQxqL5dEfZWrXExyyWsozYlAWFawPVNuD83igl7uJD2RTkNMbniIYgt8l81eCJGIdQF7avLQ==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + resolution: + { + integrity: sha512-OljLrQ9SQdOUqTaQxqL5dEfZWrXExyyWsozYlAWFawPVNuD83igl7uJD2RTkNMbniIYgt8l81eCJGIdQF7avLQ==, + } + engines: { node: ^14.17.0 || ^16.13.0 || >=18.0.0 } verkit@0.3.2: - resolution: {integrity: sha512-zj/ob3UsvJGN0whEAKFp53REA5X66hvffVqoCtVQAakJKnKlH+/PcOfMoFwIG/o4rElqLv/ycAFlx8ZlXUorCg==} - engines: {node: '>=18.12.0'} + resolution: + { + integrity: sha512-zj/ob3UsvJGN0whEAKFp53REA5X66hvffVqoCtVQAakJKnKlH+/PcOfMoFwIG/o4rElqLv/ycAFlx8ZlXUorCg==, + } + engines: { node: '>=18.12.0' } vfile-location@5.0.3: - resolution: {integrity: sha512-5yXvWDEgqeiYiBe1lbxYF7UMAIm/IcopxMHrMQDq3nvKcjPKIhZklUKL+AE7J7uApI4kwe2snsK+eI6UTj9EHg==} + resolution: + { + integrity: sha512-5yXvWDEgqeiYiBe1lbxYF7UMAIm/IcopxMHrMQDq3nvKcjPKIhZklUKL+AE7J7uApI4kwe2snsK+eI6UTj9EHg==, + } vfile-message@4.0.3: - resolution: {integrity: sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw==} + resolution: + { + integrity: sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw==, + } vfile@6.0.3: - resolution: {integrity: sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==} + resolution: + { + integrity: sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==, + } vite@8.2.2: - resolution: {integrity: sha512-cFKLV/PRgAUlIRm5WjMjJ86jrftzpqcgH+Us+DS8mI3CDNiH30Whrz8uHL3+MOLPAgqbMBAqWdAHAphOAM+z/Q==} - engines: {node: ^20.19.0 || >=22.12.0} + resolution: + { + integrity: sha512-cFKLV/PRgAUlIRm5WjMjJ86jrftzpqcgH+Us+DS8mI3CDNiH30Whrz8uHL3+MOLPAgqbMBAqWdAHAphOAM+z/Q==, + } + engines: { node: ^20.19.0 || >=22.12.0 } hasBin: true peerDependencies: '@types/node': ^20.19.0 || >=22.12.0 @@ -3497,8 +5436,11 @@ packages: optional: true vitest@4.1.11: - resolution: {integrity: sha512-fhACrNXUidIbGSBr5FlbuBkO7VWC1ZyLl0DO4CU2DrQoAPxX84Ysxs+HeGQpii5lZWV1Q4gBZTTu49mF+A6Edw==} - engines: {node: ^20.0.0 || ^22.0.0 || >=24.0.0} + resolution: + { + integrity: sha512-fhACrNXUidIbGSBr5FlbuBkO7VWC1ZyLl0DO4CU2DrQoAPxX84Ysxs+HeGQpii5lZWV1Q4gBZTTu49mF+A6Edw==, + } + engines: { node: ^20.0.0 || ^22.0.0 || >=24.0.0 } hasBin: true peerDependencies: '@edge-runtime/vm': '*' @@ -3538,109 +5480,192 @@ packages: optional: true w3c-xmlserializer@5.0.0: - resolution: {integrity: sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==} - engines: {node: '>=18'} + resolution: + { + integrity: sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==, + } + engines: { node: '>=18' } web-namespaces@2.0.1: - resolution: {integrity: sha512-bKr1DkiNa2krS7qxNtdrtHAmzuYGFQLiQ13TsorsdT6ULTkPLKuu5+GsFpDlg6JFjUTwX2DyhMPG2be8uPrqsQ==} + resolution: + { + integrity: sha512-bKr1DkiNa2krS7qxNtdrtHAmzuYGFQLiQ13TsorsdT6ULTkPLKuu5+GsFpDlg6JFjUTwX2DyhMPG2be8uPrqsQ==, + } web-vitals@4.2.4: - resolution: {integrity: sha512-r4DIlprAGwJ7YM11VZp4R884m0Vmgr6EAKe3P+kO0PPj3Unqyvv59rczf6UiGcb9Z8QxZVcqKNwv/g0WNdWwsw==} + resolution: + { + integrity: sha512-r4DIlprAGwJ7YM11VZp4R884m0Vmgr6EAKe3P+kO0PPj3Unqyvv59rczf6UiGcb9Z8QxZVcqKNwv/g0WNdWwsw==, + } webidl-conversions@3.0.1: - resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==} + resolution: + { + integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==, + } webidl-conversions@8.0.1: - resolution: {integrity: sha512-BMhLD/Sw+GbJC21C/UgyaZX41nPt8bUTg+jWyDeg7e7YN4xOM05YPSIXceACnXVtqyEw/LMClUQMtMZ+PGGpqQ==} - engines: {node: '>=20'} + resolution: + { + integrity: sha512-BMhLD/Sw+GbJC21C/UgyaZX41nPt8bUTg+jWyDeg7e7YN4xOM05YPSIXceACnXVtqyEw/LMClUQMtMZ+PGGpqQ==, + } + engines: { node: '>=20' } websocket-driver@0.7.5: - resolution: {integrity: sha512-ZL2+3c7kMBdIRCMz6l8jQMHyGVxj+UL+xVk74Ombiciboca8rHa15L86B19E5oh1pL9Ii/uj54gtsIrZGMo6zA==} - engines: {node: '>=0.8.0'} + resolution: + { + integrity: sha512-ZL2+3c7kMBdIRCMz6l8jQMHyGVxj+UL+xVk74Ombiciboca8rHa15L86B19E5oh1pL9Ii/uj54gtsIrZGMo6zA==, + } + engines: { node: '>=0.8.0' } websocket-extensions@0.1.4: - resolution: {integrity: sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==} - engines: {node: '>=0.8.0'} + resolution: + { + integrity: sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==, + } + engines: { node: '>=0.8.0' } whatwg-mimetype@5.0.0: - resolution: {integrity: sha512-sXcNcHOC51uPGF0P/D4NVtrkjSU2fNsm9iog4ZvZJsL3rjoDAzXZhkm2MWt1y+PUdggKAYVoMAIYcs78wJ51Cw==} - engines: {node: '>=20'} + resolution: + { + integrity: sha512-sXcNcHOC51uPGF0P/D4NVtrkjSU2fNsm9iog4ZvZJsL3rjoDAzXZhkm2MWt1y+PUdggKAYVoMAIYcs78wJ51Cw==, + } + engines: { node: '>=20' } whatwg-url@16.0.1: - resolution: {integrity: sha512-1to4zXBxmXHV3IiSSEInrreIlu02vUOvrhxJJH5vcxYTBDAx51cqZiKdyTxlecdKNSjj8EcxGBxNf6Vg+945gw==} - engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} + resolution: + { + integrity: sha512-1to4zXBxmXHV3IiSSEInrreIlu02vUOvrhxJJH5vcxYTBDAx51cqZiKdyTxlecdKNSjj8EcxGBxNf6Vg+945gw==, + } + engines: { node: ^20.19.0 || ^22.12.0 || >=24.0.0 } whatwg-url@5.0.0: - resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==} + resolution: + { + integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==, + } why-is-node-running@2.3.0: - resolution: {integrity: sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==} - engines: {node: '>=8'} + resolution: + { + integrity: sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==, + } + engines: { node: '>=8' } hasBin: true wrap-ansi@7.0.0: - resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} - engines: {node: '>=10'} + resolution: + { + integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==, + } + engines: { node: '>=10' } xml-name-validator@5.0.0: - resolution: {integrity: sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==} - engines: {node: '>=18'} + resolution: + { + integrity: sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==, + } + engines: { node: '>=18' } xmlchars@2.2.0: - resolution: {integrity: sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==} + resolution: + { + integrity: sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==, + } y18n@5.0.8: - resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} - engines: {node: '>=10'} + resolution: + { + integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==, + } + engines: { node: '>=10' } yaml@2.9.0: - resolution: {integrity: sha512-2AvhNX3mb8zd6Zy7INTtSpl1F15HW6Wnqj0srWlkKLcpYl/gMIMJiyuGq2KeI2YFxUPjdlB+3Lc10seMLtL4cA==} - engines: {node: '>= 14.6'} + resolution: + { + integrity: sha512-2AvhNX3mb8zd6Zy7INTtSpl1F15HW6Wnqj0srWlkKLcpYl/gMIMJiyuGq2KeI2YFxUPjdlB+3Lc10seMLtL4cA==, + } + engines: { node: '>= 14.6' } hasBin: true yargs-parser@20.2.9: - resolution: {integrity: sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==} - engines: {node: '>=10'} + resolution: + { + integrity: sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==, + } + engines: { node: '>=10' } yargs-parser@21.1.1: - resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==} - engines: {node: '>=12'} + resolution: + { + integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==, + } + engines: { node: '>=12' } yargs@16.2.2: - resolution: {integrity: sha512-Nt9ZJjXTv5R8MHbqby/wXQ6Gi0Bb3TcYZkR1bzuL4yB2OxWPkXknz513gEF0GoA6tn00UpbPvERW8rzCuWCA6w==} - engines: {node: '>=10'} + resolution: + { + integrity: sha512-Nt9ZJjXTv5R8MHbqby/wXQ6Gi0Bb3TcYZkR1bzuL4yB2OxWPkXknz513gEF0GoA6tn00UpbPvERW8rzCuWCA6w==, + } + engines: { node: '>=10' } yargs@17.7.3: - resolution: {integrity: sha512-GZtjxm/J/4TSxuL3FNYjCmLktBTnIw/rVmKSIyKeYAZpmJB2ig9VauCC5xsa82GNKVKDAqpOn3KVzNt0zmrU0g==} - engines: {node: '>=12'} + resolution: + { + integrity: sha512-GZtjxm/J/4TSxuL3FNYjCmLktBTnIw/rVmKSIyKeYAZpmJB2ig9VauCC5xsa82GNKVKDAqpOn3KVzNt0zmrU0g==, + } + engines: { node: '>=12' } yuku-analyzer@0.9.3: - resolution: {integrity: sha512-2xSREErroEF8boH7XyKfVO5hbjP6PCIYYnLR2Abg6PGJaFVbETLucmbzIebBOHeyY4GO6VXzloTuhUldR/zyew==} + resolution: + { + integrity: sha512-2xSREErroEF8boH7XyKfVO5hbjP6PCIYYnLR2Abg6PGJaFVbETLucmbzIebBOHeyY4GO6VXzloTuhUldR/zyew==, + } yuku-ast@0.8.7: - resolution: {integrity: sha512-h6+4bDfyootiMB9vckk5uKo5r5j0GHrkr17FQTDNfEsFT3DWlN9uu1HJwQwc64pgmLCI945fWM3lbTIqxjT3GQ==} + resolution: + { + integrity: sha512-h6+4bDfyootiMB9vckk5uKo5r5j0GHrkr17FQTDNfEsFT3DWlN9uu1HJwQwc64pgmLCI945fWM3lbTIqxjT3GQ==, + } yuku-ast@0.9.3: - resolution: {integrity: sha512-Kt0PXlPCXdKF1fWFTl7N3DaxsVk6DHF8VAXHJMkwPtJnWYgbx20pYgGSweuC/86mIM7k1NUITQ4JffgOLUWTCw==} + resolution: + { + integrity: sha512-Kt0PXlPCXdKF1fWFTl7N3DaxsVk6DHF8VAXHJMkwPtJnWYgbx20pYgGSweuC/86mIM7k1NUITQ4JffgOLUWTCw==, + } yuku-codegen@0.8.7: - resolution: {integrity: sha512-adwDZSh8oVDzhE6Du9PwVWxcOxeV0e2EVhUuMKWfhSY4wkrDq9eqixlxFF3l/XGUV1E7UFzhpz9393MUumkyNw==} + resolution: + { + integrity: sha512-adwDZSh8oVDzhE6Du9PwVWxcOxeV0e2EVhUuMKWfhSY4wkrDq9eqixlxFF3l/XGUV1E7UFzhpz9393MUumkyNw==, + } yuku-parser@0.8.7: - resolution: {integrity: sha512-vRD9nwt4L3aYpxNqeSC4WqLv58xrXef0Ong1Mc45CTXTIpvLafx7JO05sczmQZwdLEZvywrLOGdNC5+Rp5N1BQ==} + resolution: + { + integrity: sha512-vRD9nwt4L3aYpxNqeSC4WqLv58xrXef0Ong1Mc45CTXTIpvLafx7JO05sczmQZwdLEZvywrLOGdNC5+Rp5N1BQ==, + } zbsearch@4.0.0: - resolution: {integrity: sha512-gm4zfO31n2ZdruTTRQoWVWO4Q2+zrDt2GlrvIc+5JulRQNAm4IanCxER82vQ7Ug96FYkGqWUJBXFe1kGAcDWaQ==} - engines: {node: '>= 20.0.0'} + resolution: + { + integrity: sha512-gm4zfO31n2ZdruTTRQoWVWO4Q2+zrDt2GlrvIc+5JulRQNAm4IanCxER82vQ7Ug96FYkGqWUJBXFe1kGAcDWaQ==, + } + engines: { node: '>= 20.0.0' } zod@4.5.2: - resolution: {integrity: sha512-XkYXCol10+ba/6F/cueWV+TezUeOqXW0hdeJt5CdXjTYeAgAQg5N03RQdJ80mhfFE72+pblvYMW4wy2Qp4Qbrg==} + resolution: + { + integrity: sha512-XkYXCol10+ba/6F/cueWV+TezUeOqXW0hdeJt5CdXjTYeAgAQg5N03RQdJ80mhfFE72+pblvYMW4wy2Qp4Qbrg==, + } zwitch@2.0.4: - resolution: {integrity: sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==} + resolution: + { + integrity: sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==, + } snapshots: - '@acemir/cssom@0.9.31': {} '@alloc/quick-lru@5.2.0': {} @@ -6396,10 +8421,6 @@ snapshots: picocolors: 1.1.1 source-map-js: 1.2.1 - prettier-plugin-tailwindcss@0.7.4(prettier@3.9.6): - dependencies: - prettier: 3.9.6 - prettier-plugin-tailwindcss@0.8.1(prettier@3.9.6): dependencies: prettier: 3.9.6 From 73207806cbd9b001e45f49aef13396a133b03f5a Mon Sep 17 00:00:00 2001 From: timonwa Date: Wed, 9 Sep 2026 00:49:45 +0100 Subject: [PATCH 21/23] build(prettier): mirror the editor config MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Prettier's defaults (80 columns, double quotes) plus the two the editor overrides, arrowParens and bracketSameLine — so the CLI, pre-commit, and VS Code agree, and docs code fences match what Biome emits for the library. Lockfile ignored. --- .prettierignore | 3 +++ .prettierrc.json | 5 +++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/.prettierignore b/.prettierignore index 4313575..1f80343 100644 --- a/.prettierignore +++ b/.prettierignore @@ -15,3 +15,6 @@ packages/**/*.json # Generated by changesets — reformatting it would churn on every release. **/CHANGELOG.md + +# Machine-owned; pnpm writes it. +pnpm-lock.yaml diff --git a/.prettierrc.json b/.prettierrc.json index 4ab4905..0b08905 100644 --- a/.prettierrc.json +++ b/.prettierrc.json @@ -1,9 +1,10 @@ { "$schema": "https://json.schemastore.org/prettierrc", - "printWidth": 90, - "singleQuote": true, + "printWidth": 80, "semi": true, "trailingComma": "all", + "arrowParens": "avoid", + "bracketSameLine": true, "proseWrap": "never", "plugins": ["prettier-plugin-tailwindcss"], "overrides": [ From f4d564a98a2a42c1097d1ed546a8e4fd1ff9396c Mon Sep 17 00:00:00 2001 From: timonwa Date: Wed, 9 Sep 2026 00:56:12 +0100 Subject: [PATCH 22/23] refactor(auth): drop the setError destructure reset() made dead Three hooks kept pulling setError out of useAuthTask after the resetState bodies that used it were replaced by reset(). Biome flagged all three; now none. --- packages/firebase-hooks/src/auth/use-confirm-password-reset.ts | 2 +- packages/firebase-hooks/src/auth/use-email-link-sign-in.ts | 2 +- .../firebase-hooks/src/auth/use-send-password-reset-email.ts | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/firebase-hooks/src/auth/use-confirm-password-reset.ts b/packages/firebase-hooks/src/auth/use-confirm-password-reset.ts index c5fb91d..d8e56ac 100644 --- a/packages/firebase-hooks/src/auth/use-confirm-password-reset.ts +++ b/packages/firebase-hooks/src/auth/use-confirm-password-reset.ts @@ -45,7 +45,7 @@ export function useConfirmPasswordReset( } function useConfirmPasswordResetBase(auth: Auth | null, options: HookErrorOptions) { - const { status, isIdle, isPending, isSuccess, isError, error, reset, setError, run } = + const { status, isIdle, isPending, isSuccess, isError, error, reset, run } = useAuthTask(options); const verifyCode = (oobCode: string): Promise> => diff --git a/packages/firebase-hooks/src/auth/use-email-link-sign-in.ts b/packages/firebase-hooks/src/auth/use-email-link-sign-in.ts index cb34743..8b622fc 100644 --- a/packages/firebase-hooks/src/auth/use-email-link-sign-in.ts +++ b/packages/firebase-hooks/src/auth/use-email-link-sign-in.ts @@ -97,7 +97,7 @@ export function useEmailLinkSignIn( function useEmailLinkSignInBase(auth: Auth | null, options: UseEmailLinkSignInOptions) { const { storageKey = "emailForSignIn" } = options; - const { status, isIdle, isPending, isSuccess, isError, error, reset, setError, run } = + const { status, isIdle, isPending, isSuccess, isError, error, reset, run } = useAuthTask(options); const onIdToken = useResolvedConfig("onIdToken", options.onIdToken); const actionCodeSettings = useResolvedConfig( diff --git a/packages/firebase-hooks/src/auth/use-send-password-reset-email.ts b/packages/firebase-hooks/src/auth/use-send-password-reset-email.ts index 24abaed..f23a8fe 100644 --- a/packages/firebase-hooks/src/auth/use-send-password-reset-email.ts +++ b/packages/firebase-hooks/src/auth/use-send-password-reset-email.ts @@ -67,7 +67,7 @@ function useSendPasswordResetEmailBase( auth: Auth | null, options: UseSendPasswordResetEmailOptions, ) { - const { status, isIdle, isPending, isSuccess, isError, error, reset, setError, run } = + const { status, isIdle, isPending, isSuccess, isError, error, reset, run } = useAuthTask(options); const actionCodeSettings = useResolvedConfig( "actionCodeSettings", From 0d8dfef54c7b7ee5234d37f2a22fa404bcffdfea Mon Sep 17 00:00:00 2001 From: timonwa Date: Wed, 9 Sep 2026 01:02:23 +0100 Subject: [PATCH 23/23] style: reformat under the mirrored Prettier config Double quotes, 80 columns, arrowParens avoid, bracketSameLine, and class order that reflects the apps' own utilities. No code changes. --- .changeset/align-status-and-sender-naming.md | 2 +- .changeset/config.json | 5 +- .changeset/status-instead-of-loading.md | 3 +- README.md | 17 +- apps/docs/README.md | 28 +-- apps/docs/app/(home)/layout.tsx | 8 +- apps/docs/app/(home)/page.tsx | 216 ++++++++++-------- apps/docs/app/api/search/route.ts | 4 +- apps/docs/app/docs/[[...slug]]/page.tsx | 37 +-- apps/docs/app/docs/layout.tsx | 8 +- apps/docs/app/global.css | 33 ++- apps/docs/app/layout.tsx | 48 ++-- apps/docs/app/llms-full.txt/route.ts | 4 +- .../app/llms.mdx/docs/[[...slug]]/route.ts | 10 +- apps/docs/app/llms.txt/route.ts | 4 +- apps/docs/app/not-found.tsx | 52 ++--- apps/docs/app/og/docs/[...slug]/route.tsx | 15 +- apps/docs/app/og/route.tsx | 10 +- apps/docs/app/robots.ts | 12 +- apps/docs/app/sitemap.ts | 15 +- apps/docs/components/code-sample.tsx | 19 +- apps/docs/components/copy-button.tsx | 11 +- apps/docs/components/json-ld.tsx | 13 +- apps/docs/components/mdx.tsx | 8 +- apps/docs/components/og-image.tsx | 62 ++--- apps/docs/components/site-footer.tsx | 56 +++-- apps/docs/content/docs/auth/index.mdx | 8 +- apps/docs/content/docs/core/hook-result.mdx | 10 +- apps/docs/content/docs/guides/index.mdx | 25 +- apps/docs/content/docs/index.mdx | 19 +- apps/docs/lib/cn.ts | 2 +- apps/docs/lib/layout.shared.tsx | 28 ++- apps/docs/lib/schema.ts | 54 ++--- apps/docs/lib/seo.ts | 24 +- apps/docs/lib/shared.ts | 26 +-- apps/docs/lib/site.ts | 14 +- apps/docs/lib/source.ts | 36 +-- apps/docs/next.config.mjs | 2 +- apps/docs/postcss.config.mjs | 2 +- apps/docs/proxy.ts | 8 +- apps/playground/app/auth/action/page.tsx | 75 +++--- apps/playground/app/auth/callback/page.tsx | 43 ++-- apps/playground/app/auth/page.tsx | 50 ++-- apps/playground/app/globals.css | 6 +- apps/playground/app/layout.tsx | 28 +-- apps/playground/app/page.tsx | 78 ++++--- apps/playground/components/app-sidebar.tsx | 64 +++--- .../components/auth/use-anonymous-sign-in.tsx | 41 ++-- apps/playground/components/auth/use-auth.tsx | 22 +- .../auth/use-confirm-password-reset.tsx | 24 +- .../auth/use-custom-token-sign-in.tsx | 37 +-- .../components/auth/use-delete-account.tsx | 51 +++-- .../auth/use-email-link-sign-in.tsx | 54 +++-- .../components/auth/use-link-provider.tsx | 44 ++-- apps/playground/components/auth/use-login.tsx | 55 +++-- .../playground/components/auth/use-logout.tsx | 41 ++-- .../components/auth/use-oauth-sign-in.tsx | 88 +++---- .../components/auth/use-phone-sign-in.tsx | 82 ++++--- .../components/auth/use-reauthenticate.tsx | 41 ++-- .../auth/use-send-email-verification.tsx | 33 +-- .../auth/use-send-password-reset-email.tsx | 47 ++-- .../playground/components/auth/use-signup.tsx | 62 ++--- .../components/auth/use-unlink-provider.tsx | 23 +- .../components/auth/use-update-email.tsx | 42 ++-- .../components/auth/use-update-password.tsx | 43 ++-- .../components/auth/use-update-profile.tsx | 35 +-- .../components/auth/use-verify-email.tsx | 23 +- apps/playground/components/code-block.tsx | 42 ++-- apps/playground/components/controls.tsx | 27 ++- .../components/error-comparison.tsx | 38 +-- .../components/firebase-provider.tsx | 50 ++-- apps/playground/components/group-heading.tsx | 8 +- apps/playground/components/hook-options.tsx | 99 ++++---- apps/playground/components/hook-section.tsx | 85 ++++--- apps/playground/components/needs-config.tsx | 23 +- apps/playground/components/page-intro.tsx | 4 +- apps/playground/components/popover.tsx | 24 +- apps/playground/components/shell.tsx | 24 +- apps/playground/components/theme.tsx | 53 +++-- apps/playground/components/top-bar.tsx | 125 +++++----- apps/playground/lib/firebase-config.ts | 4 +- apps/playground/lib/hooks-map.ts | 54 ++--- apps/playground/lib/use-active-anchor.ts | 20 +- apps/playground/lib/use-stored-state.ts | 4 +- apps/playground/lib/wrapper-types.ts | 21 +- apps/playground/postcss.config.mjs | 2 +- pnpm-workspace.yaml | 2 +- 87 files changed, 1559 insertions(+), 1340 deletions(-) diff --git a/.changeset/align-status-and-sender-naming.md b/.changeset/align-status-and-sender-naming.md index c6d3caa..8fe1bf0 100644 --- a/.changeset/align-status-and-sender-naming.md +++ b/.changeset/align-status-and-sender-naming.md @@ -14,6 +14,6 @@ **Breaking: `useEmailLinkSignIn`'s `sendLink` option is now `sendEmail`.** All three emailed-link hooks take the same option name. The returned `sendLink` function is unchanged. ```tsx --useEmailLinkSignIn({ sendLink: (email) => api.send(email) }); +-useEmailLinkSignIn({ sendLink: email => api.send(email) }); +useEmailLinkSignIn({ sendEmail: ({ email }) => api.send(email) }); ``` diff --git a/.changeset/config.json b/.changeset/config.json index 7fb5a92..7f95297 100644 --- a/.changeset/config.json +++ b/.changeset/config.json @@ -1,6 +1,9 @@ { "$schema": "https://unpkg.com/@changesets/config@3.0.0/schema.json", - "changelog": ["@changesets/changelog-github", { "repo": "Timonwa/firebase-hooks" }], + "changelog": [ + "@changesets/changelog-github", + { "repo": "Timonwa/firebase-hooks" } + ], "commit": false, "access": "public", "baseBranch": "main", diff --git a/.changeset/status-instead-of-loading.md b/.changeset/status-instead-of-loading.md index e870780..e3007d0 100644 --- a/.changeset/status-instead-of-loading.md +++ b/.changeset/status-instead-of-loading.md @@ -5,7 +5,8 @@ **Breaking: every action hook reports `status` with derived booleans, replacing `loading`, `success` and `resetState`.** ```tsx -const { login, status, isIdle, isPending, isSuccess, isError, error, reset } = useLogin(); +const { login, status, isIdle, isPending, isSuccess, isError, error, reset } = + useLogin(); // ^ 'idle' | 'pending' | 'success' | 'error' ``` diff --git a/README.md b/README.md index a28b410..d76a20b 100644 --- a/README.md +++ b/README.md @@ -37,7 +37,7 @@ Create your `Auth` instance once with the Firebase SDK, then wrap your app: import { AuthProvider } from "@timonwa/firebase-hooks/auth"; import { auth } from "@/lib/firebase"; // getAuth(initializeApp(config)) - createSession(idToken)}> + createSession(idToken)}> {children} ; ``` @@ -74,7 +74,10 @@ The most-used services ship first; more (Realtime Database, Remote Config, Cloud Each service is its own import, so an app only carries the services it uses. The root holds what every service shares — `formatFirebaseError`, `getFirebaseErrorCode`, and the `HookResult` types. ```ts -import { formatFirebaseError, getFirebaseErrorCode } from "@timonwa/firebase-hooks"; +import { + formatFirebaseError, + getFirebaseErrorCode, +} from "@timonwa/firebase-hooks"; import { AuthProvider, useLogin, @@ -97,11 +100,12 @@ One contract, so learning one hook is learning them all: ```tsx createSession(idToken)} + onIdToken={idToken => createSession(idToken)} onBeforeSignOut={() => clearSession()} actionCodeSettings={{ url: `${origin}/auth/action`, handleCodeInApp: true }} - formatErrorMessage={(e) => formatFirebaseError(e, { messages: AUTH_ERROR_MESSAGES })} -> + formatErrorMessage={e => + formatFirebaseError(e, { messages: AUTH_ERROR_MESSAGES }) + }> {children} ; @@ -149,8 +153,7 @@ For logging and analytics, the provider's **`onError` observer** sees every fail ```tsx track("auth_error", { action, code })} -> + onError={(error, { action, code }) => track("auth_error", { action, code })}> {children} ``` diff --git a/apps/docs/README.md b/apps/docs/README.md index cac2c1f..261aaaa 100644 --- a/apps/docs/README.md +++ b/apps/docs/README.md @@ -12,9 +12,9 @@ Open . ## Environment -| Variable | Required | What it's for | -| ---------------------- | -------- | ------------------------------------------------------------- | -| `NEXT_PUBLIC_SITE_URL` | No | Absolute origin for canonicals, the sitemap and OG image URLs | +| Variable | Required | What it's for | +| --- | --- | --- | +| `NEXT_PUBLIC_SITE_URL` | No | Absolute origin for canonicals, the sitemap and OG image URLs | **Nothing to configure on Vercel.** The origin resolves from `VERCEL_PROJECT_PRODUCTION_URL`, which Vercel sets automatically. Set `NEXT_PUBLIC_SITE_URL` only once a custom domain points at the site. @@ -22,17 +22,17 @@ Locally the origin falls back to `http://localhost:3000`. If your dev server pic ## Layout -| Path | What it is | -| ----------------------- | ---------------------------------------------------------------------------- | -| `content/docs/` | The MDX pages; `meta.json` per folder controls nav order | -| `lib/source.ts` | Content source adapter — the nav, sitemap and OG routes all read from it | -| `lib/site.ts` | Site config: origin, description, author, whether the env is indexable | -| `lib/seo.ts` | `buildMetadata()` — every page's canonical, OG, Twitter and robots | -| `lib/schema.ts` | JSON-LD graph, anchored by stable `@id` | -| `lib/layout.shared.tsx` | Nav title, version pill, and the nav links | -| `app/og/` | OG image routes — `/og` for the home page, `/og/docs/*` prerendered per page | -| `app/(home)/` | Landing page and its footer | -| `app/docs/` | Documentation layout and pages | +| Path | What it is | +| --- | --- | +| `content/docs/` | The MDX pages; `meta.json` per folder controls nav order | +| `lib/source.ts` | Content source adapter — the nav, sitemap and OG routes all read from it | +| `lib/site.ts` | Site config: origin, description, author, whether the env is indexable | +| `lib/seo.ts` | `buildMetadata()` — every page's canonical, OG, Twitter and robots | +| `lib/schema.ts` | JSON-LD graph, anchored by stable `@id` | +| `lib/layout.shared.tsx` | Nav title, version pill, and the nav links | +| `app/og/` | OG image routes — `/og` for the home page, `/og/docs/*` prerendered per page | +| `app/(home)/` | Landing page and its footer | +| `app/docs/` | Documentation layout and pages | ## Writing a page diff --git a/apps/docs/app/(home)/layout.tsx b/apps/docs/app/(home)/layout.tsx index 0ac81cb..96c68a6 100644 --- a/apps/docs/app/(home)/layout.tsx +++ b/apps/docs/app/(home)/layout.tsx @@ -1,8 +1,8 @@ -import { HomeLayout } from 'fumadocs-ui/layouts/home'; -import { SiteFooter } from '@/components/site-footer'; -import { baseOptions } from '@/lib/layout.shared'; +import { HomeLayout } from "fumadocs-ui/layouts/home"; +import { SiteFooter } from "@/components/site-footer"; +import { baseOptions } from "@/lib/layout.shared"; -export default function Layout({ children }: LayoutProps<'/'>) { +export default function Layout({ children }: LayoutProps<"/">) { return ( {children} diff --git a/apps/docs/app/(home)/page.tsx b/apps/docs/app/(home)/page.tsx index 4488cfe..abae1c7 100644 --- a/apps/docs/app/(home)/page.tsx +++ b/apps/docs/app/(home)/page.tsx @@ -10,21 +10,21 @@ import { SlidersHorizontal, UserCog, Workflow, -} from 'lucide-react'; -import Link from 'next/link'; -import type { ReactNode } from 'react'; -import { CodeSample } from '@/components/code-sample'; -import { CopyButton } from '@/components/copy-button'; -import type { Metadata } from 'next'; -import { buildMetadata } from '@/lib/seo'; -import { npmUrl, packageName, packageVersion } from '@/lib/shared'; +} from "lucide-react"; +import Link from "next/link"; +import type { ReactNode } from "react"; +import { CodeSample } from "@/components/code-sample"; +import { CopyButton } from "@/components/copy-button"; +import type { Metadata } from "next"; +import { buildMetadata } from "@/lib/seo"; +import { npmUrl, packageName, packageVersion } from "@/lib/shared"; export const metadata: Metadata = buildMetadata({ // No `title` key: the home page keeps the root layout's default title rather // than having the template append the package name a second time. description: - 'Typed React hooks for every Firebase Auth flow — email/password, OAuth, magic link, phone and anonymous sign-in, plus password, email, profile and provider linking. Zero dependencies.', - path: '/', + "Typed React hooks for every Firebase Auth flow — email/password, OAuth, magic link, phone and anonymous sign-in, plus password, email, profile and provider linking. Zero dependencies.", + path: "/", }); const INSTALL_COMMAND = `pnpm add ${packageName} firebase`; @@ -32,80 +32,92 @@ const INSTALL_COMMAND = `pnpm add ${packageName} firebase`; const FEATURES = [ { icon: Workflow, - title: 'Whole flows, not single calls', - body: 'usePhoneSignIn builds and tears down the reCAPTCHA verifier. useOAuthSignIn finishes a redirect when the page returns. useEmailLinkSignIn asks for the address instead of calling window.prompt.', + title: "Whole flows, not single calls", + body: "usePhoneSignIn builds and tears down the reCAPTCHA verifier. useOAuthSignIn finishes a redirect when the page returns. useEmailLinkSignIn asks for the address instead of calling window.prompt.", }, { icon: CircleAlert, - title: 'Failures are values', - body: 'Actions never throw. A failure carries Firebase’s own code and the untouched original error, so you branch on a result instead of wrapping every call in try/catch.', + title: "Failures are values", + body: "Actions never throw. A failure carries Firebase’s own code and the untouched original error, so you branch on a result instead of wrapping every call in try/catch.", }, { icon: KeyRound, - title: 'Server sessions built in', - body: 'onIdToken hands you a fresh ID token as part of the sign-in, not after it. Throw inside it and the sign-in aborts, so a user can’t land on a protected page without a server session.', + title: "Server sessions built in", + body: "onIdToken hands you a fresh ID token as part of the sign-in, not after it. Throw inside it and the sign-in aborts, so a user can’t land on a protected page without a server session.", }, { icon: PackageOpen, - title: 'Nothing withheld', - body: 'Sign-ins hand back Firebase’s raw UserCredential. Error messages stay exactly as Firebase wrote them unless you opt into formatting.', + title: "Nothing withheld", + body: "Sign-ins hand back Firebase’s raw UserCredential. Error messages stay exactly as Firebase wrote them unless you opt into formatting.", }, { icon: SlidersHorizontal, - title: 'Configure once, override anywhere', - body: 'Session callbacks, action-code settings and error wording live on the provider. Any hook can override them, or opt out entirely with null.', + title: "Configure once, override anywhere", + body: "Session callbacks, action-code settings and error wording live on the provider. Any hook can override them, or opt out entirely with null.", }, { icon: Fingerprint, - title: 'Reauthentication handled', - body: 'Pass currentPassword to a sensitive operation and the recent-sign-in check happens first. Omit it, and auth/requires-recent-login reaches you to handle your own way.', + title: "Reauthentication handled", + body: "Pass currentPassword to a sensitive operation and the recent-sign-in check happens first. Omit it, and auth/requires-recent-login reaches you to handle your own way.", }, ]; const GROUPS = [ { - label: 'Signing in and out', + label: "Signing in and out", icon: LogIn, hooks: [ - 'useLogin', - 'useSignup', - 'useLogout', - 'useOAuthSignIn', - 'useEmailLinkSignIn', - 'usePhoneSignIn', - 'useAnonymousSignIn', - 'useCustomTokenSignIn', + "useLogin", + "useSignup", + "useLogout", + "useOAuthSignIn", + "useEmailLinkSignIn", + "usePhoneSignIn", + "useAnonymousSignIn", + "useCustomTokenSignIn", ], }, { - label: 'Passwords', + label: "Passwords", icon: Lock, - hooks: ['useSendPasswordResetEmail', 'useConfirmPasswordReset', 'useUpdatePassword'], + hooks: [ + "useSendPasswordResetEmail", + "useConfirmPasswordReset", + "useUpdatePassword", + ], }, { - label: 'Email', + label: "Email", icon: Mail, - hooks: ['useSendEmailVerification', 'useVerifyEmail', 'useUpdateEmail'], + hooks: ["useSendEmailVerification", "useVerifyEmail", "useUpdateEmail"], }, { - label: 'Account and linking', + label: "Account and linking", icon: UserCog, hooks: [ - 'useUpdateProfile', - 'useDeleteAccount', - 'useReauthenticate', - 'useLinkProvider', - 'useUnlinkProvider', + "useUpdateProfile", + "useDeleteAccount", + "useReauthenticate", + "useLinkProvider", + "useUnlinkProvider", ], }, ]; const SERVICES = [ - { name: 'Core', entry: '@timonwa/firebase-hooks', ready: true }, - { name: 'Auth', entry: '@timonwa/firebase-hooks/auth', ready: true }, - { name: 'Firestore', entry: '@timonwa/firebase-hooks/firestore', ready: false }, - { name: 'Storage', entry: '@timonwa/firebase-hooks/storage', ready: false }, - { name: 'Cloud Functions', entry: '@timonwa/firebase-hooks/functions', ready: false }, + { name: "Core", entry: "@timonwa/firebase-hooks", ready: true }, + { name: "Auth", entry: "@timonwa/firebase-hooks/auth", ready: true }, + { + name: "Firestore", + entry: "@timonwa/firebase-hooks/firestore", + ready: false, + }, + { name: "Storage", entry: "@timonwa/firebase-hooks/storage", ready: false }, + { + name: "Cloud Functions", + entry: "@timonwa/firebase-hooks/functions", + ready: false, + }, ]; // Kept under ~56 columns so it fits a half-width column without scrolling. @@ -139,26 +151,30 @@ const result = await login(email, password); if (result.success) router.push("/dashboard");`; function toSlug(hook: string) { - return hook.replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase(); + return hook.replace(/([a-z])([A-Z])/g, "$1-$2").toLowerCase(); } function Section({ children, - className = '', + className = "", }: { children: ReactNode; className?: string; }) { return ( -
{children}
+
+ {children} +
); } function SectionHeading({ title, lead }: { title: string; lead: string }) { return (
-

{title}

-

{lead}

+

+ {title} +

+

{lead}

); } @@ -168,50 +184,51 @@ export default function HomePage() {
{/* Hero */}
-
+
- - v{packageVersion} · - Auth available · Firestore next + + v + {packageVersion} · Auth available · Firestore next

- Typed React hooks for Firebase,{' '} + Typed React hooks for Firebase,{" "} one hook per flow

-

- Each hook runs a whole flow end to end and holds its own loading, error and - success state. Zero dependencies — firebase and{' '} - react stay peers. +

+ Each hook runs a whole flow end to end and holds its own loading, + error and success state. Zero dependencies — firebase{" "} + and react stay peers.

+ className="group inline-flex items-center gap-2 rounded-lg bg-fd-primary px-5 py-2.5 text-sm font-medium text-fd-primary-foreground transition-opacity hover:opacity-90"> Get started + className="surface px-5 py-2.5 text-sm font-medium transition-colors hover:bg-fd-accent"> Browse the hooks -
+
$ {INSTALL_COMMAND} - +
@@ -229,7 +246,7 @@ export default function HomePage() { only the label above and the note below — no second border. */}
-
+
Firebase directly 21 lines
@@ -237,18 +254,19 @@ export default function HomePage() {
-
+
With useLogin 6 lines
-

- And it does more: if createSession throws, the sign-in aborts - rather than leaving a signed-in user with no server session. +

+ And it does more: if createSession throws, the + sign-in aborts rather than leaving a signed-in user with no server + session.

@@ -263,7 +281,7 @@ export default function HomePage() { {/* One hairline grid rather than six outlined cards, so the icons stay the only accent in the section. */} -
+
{/* The grid is pulled 1px past the container so the last column's and last row's borders land under the container's own border and get clipped — otherwise the rounded corners sit on straight cell borders @@ -272,17 +290,18 @@ export default function HomePage() { {FEATURES.map(({ icon: Icon, title, body }) => (
+ className="group relative flex flex-col border-r border-b border-fd-border p-6"> - +

{title}

-

{body}

+

+ {body} +

))}
@@ -306,22 +325,23 @@ export default function HomePage() { {label} - + {hooks.length}
    - {hooks.map((hook) => ( + {hooks.map(hook => (
  • + className="inline-block rounded-md border border-fd-border px-2.5 py-1 font-mono text-xs transition-colors hover:border-fd-primary/40 hover:bg-fd-primary/5"> {/* Colouring the shared prefix carries the accent through twenty otherwise-grey chips, and shows the naming pattern at a glance. */} use - {hook.slice(3)} + + {hook.slice(3)} +
  • ))} @@ -339,29 +359,27 @@ export default function HomePage() { />
      - {SERVICES.map((service) => ( + {SERVICES.map(service => (
    • + className="flex flex-wrap items-center gap-x-4 gap-y-1 border-b py-3.5 last:border-b-0"> {service.name} - + {service.entry} - {service.ready ? 'Available' : 'Coming soon'} + ? "text-xs font-medium text-fd-primary" + : "text-xs text-fd-muted-foreground" + }> + {service.ready ? "Available" : "Coming soon"}
    • ))} @@ -370,10 +388,10 @@ export default function HomePage() { {/* Close */}
      -
      +

      Sign a user in, in about five lines @@ -381,16 +399,14 @@ export default function HomePage() {
      + className="group inline-flex items-center gap-2 rounded-lg bg-fd-primary px-5 py-2.5 text-sm font-medium text-fd-primary-foreground transition-opacity hover:opacity-90"> Get started + className="rounded-lg border px-5 py-2.5 text-sm font-medium transition-colors hover:bg-fd-accent"> View on npm ↗
      diff --git a/apps/docs/app/api/search/route.ts b/apps/docs/app/api/search/route.ts index df88962..d86bfc5 100644 --- a/apps/docs/app/api/search/route.ts +++ b/apps/docs/app/api/search/route.ts @@ -1,4 +1,4 @@ -import { source } from '@/lib/source'; -import { createFromSource } from 'fumadocs-core/search/server'; +import { source } from "@/lib/source"; +import { createFromSource } from "fumadocs-core/search/server"; export const { GET } = createFromSource(source); diff --git a/apps/docs/app/docs/[[...slug]]/page.tsx b/apps/docs/app/docs/[[...slug]]/page.tsx index 7bf8db8..9937fa6 100644 --- a/apps/docs/app/docs/[[...slug]]/page.tsx +++ b/apps/docs/app/docs/[[...slug]]/page.tsx @@ -1,4 +1,4 @@ -import { getPageImageUrl, getPageMarkdownUrl, source } from '@/lib/source'; +import { getPageImageUrl, getPageMarkdownUrl, source } from "@/lib/source"; import { DocsBody, DocsDescription, @@ -6,17 +6,17 @@ import { DocsTitle, MarkdownCopyButton, ViewOptionsPopover, -} from 'fumadocs-ui/layouts/docs/page'; -import { notFound } from 'next/navigation'; -import { getMDXComponents } from '@/components/mdx'; -import type { Metadata } from 'next'; -import { createRelativeLink } from 'fumadocs-ui/mdx'; -import { JsonLd } from '@/components/json-ld'; -import { breadcrumbSchema, techArticleSchema } from '@/lib/schema'; -import { buildMetadata } from '@/lib/seo'; -import { gitConfig } from '@/lib/shared'; +} from "fumadocs-ui/layouts/docs/page"; +import { notFound } from "next/navigation"; +import { getMDXComponents } from "@/components/mdx"; +import type { Metadata } from "next"; +import { createRelativeLink } from "fumadocs-ui/mdx"; +import { JsonLd } from "@/components/json-ld"; +import { breadcrumbSchema, techArticleSchema } from "@/lib/schema"; +import { buildMetadata } from "@/lib/seo"; +import { gitConfig } from "@/lib/shared"; -export default async function Page(props: PageProps<'/docs/[[...slug]]'>) { +export default async function Page(props: PageProps<"/docs/[[...slug]]">) { const params = await props.params; const page = source.getPage(params.slug); if (!page) notFound(); @@ -26,11 +26,12 @@ export default async function Page(props: PageProps<'/docs/[[...slug]]'>) { // Breadcrumbs mirror the URL, which is what Google expects them to. const trail = [ - { name: 'Docs', path: '/docs' }, + { name: "Docs", path: "/docs" }, ...page.slugs.map((_, index) => ({ name: - source.getPage(page.slugs.slice(0, index + 1))?.data.title ?? page.slugs[index], - path: `/docs/${page.slugs.slice(0, index + 1).join('/')}`, + source.getPage(page.slugs.slice(0, index + 1))?.data.title ?? + page.slugs[index], + path: `/docs/${page.slugs.slice(0, index + 1).join("/")}`, })), ]; @@ -47,7 +48,9 @@ export default async function Page(props: PageProps<'/docs/[[...slug]]'>) { ]} /> {page.data.title} - {page.data.description} + + {page.data.description} +
      , + props: PageProps<"/docs/[[...slug]]">, ): Promise { const params = await props.params; const page = source.getPage(params.slug); @@ -86,6 +89,6 @@ export async function generateMetadata( path: page.url, imageUrl: getPageImageUrl(page).url, imageAlt: page.data.title, - type: 'article', + type: "article", }); } diff --git a/apps/docs/app/docs/layout.tsx b/apps/docs/app/docs/layout.tsx index a373143..1281dfd 100644 --- a/apps/docs/app/docs/layout.tsx +++ b/apps/docs/app/docs/layout.tsx @@ -1,8 +1,8 @@ -import { source } from '@/lib/source'; -import { DocsLayout } from 'fumadocs-ui/layouts/docs'; -import { baseOptions } from '@/lib/layout.shared'; +import { source } from "@/lib/source"; +import { DocsLayout } from "fumadocs-ui/layouts/docs"; +import { baseOptions } from "@/lib/layout.shared"; -export default function Layout({ children }: LayoutProps<'/docs'>) { +export default function Layout({ children }: LayoutProps<"/docs">) { return ( {children} diff --git a/apps/docs/app/global.css b/apps/docs/app/global.css index 344f013..b860ff2 100644 --- a/apps/docs/app/global.css +++ b/apps/docs/app/global.css @@ -1,5 +1,5 @@ -@import 'tailwindcss'; -@import 'fumadocs-ui/css/preset.css'; +@import "tailwindcss"; +@import "fumadocs-ui/css/preset.css"; /* Colours are defined here rather than by importing a Fumadocs preset, so the whole palette is one violet-tinted ramp instead of a grey one with an accent @@ -107,7 +107,11 @@ html > body[data-scroll-locked] { height: 2.25rem; border-radius: 0.625rem; color: var(--color-fd-primary); - background-color: color-mix(in oklch, var(--color-fd-primary) 12%, transparent); + background-color: color-mix( + in oklch, + var(--color-fd-primary) 12%, + transparent + ); } /* The hero's dot grid — the one decorative signature on the page. Masked so it @@ -117,17 +121,32 @@ html > body[data-scroll-locked] { fainter than a light dot on a dark one at the same mix, so a single value leaves the grid invisible in light mode. */ :root { - --dot-grid-color: color-mix(in oklch, var(--color-fd-foreground) 30%, transparent); + --dot-grid-color: color-mix( + in oklch, + var(--color-fd-foreground) 30%, + transparent + ); } .dark { - --dot-grid-color: color-mix(in oklch, var(--color-fd-foreground) 20%, transparent); + --dot-grid-color: color-mix( + in oklch, + var(--color-fd-foreground) 20%, + transparent + ); } @utility dot-grid { - background-image: radial-gradient(var(--dot-grid-color) 1.2px, transparent 1.2px); + background-image: radial-gradient( + var(--dot-grid-color) 1.2px, + transparent 1.2px + ); background-size: 22px 22px; - mask-image: radial-gradient(ellipse 85% 70% at 50% 0%, black, transparent 80%); + mask-image: radial-gradient( + ellipse 85% 70% at 50% 0%, + black, + transparent 80% + ); } /* Geist Mono renders wide at the inherited size; the small step down keeps an diff --git a/apps/docs/app/layout.tsx b/apps/docs/app/layout.tsx index 1bb8c47..880b000 100644 --- a/apps/docs/app/layout.tsx +++ b/apps/docs/app/layout.tsx @@ -1,16 +1,19 @@ -import { Analytics } from '@vercel/analytics/next'; -import { RootProvider } from 'fumadocs-ui/provider/next'; -import './global.css'; -import type { Metadata } from 'next'; -import { Geist, Geist_Mono } from 'next/font/google'; -import { JsonLd } from '@/components/json-ld'; -import { siteGraph } from '@/lib/schema'; -import { buildMetadata } from '@/lib/seo'; -import { packageName } from '@/lib/shared'; -import { siteConfig } from '@/lib/site'; +import { Analytics } from "@vercel/analytics/next"; +import { RootProvider } from "fumadocs-ui/provider/next"; +import "./global.css"; +import type { Metadata } from "next"; +import { Geist, Geist_Mono } from "next/font/google"; +import { JsonLd } from "@/components/json-ld"; +import { siteGraph } from "@/lib/schema"; +import { buildMetadata } from "@/lib/seo"; +import { packageName } from "@/lib/shared"; +import { siteConfig } from "@/lib/site"; -const geistSans = Geist({ subsets: ['latin'], variable: '--font-geist-sans' }); -const geistMono = Geist_Mono({ subsets: ['latin'], variable: '--font-geist-mono' }); +const geistSans = Geist({ subsets: ["latin"], variable: "--font-geist-sans" }); +const geistMono = Geist_Mono({ + subsets: ["latin"], + variable: "--font-geist-mono", +}); // Spread first, explicit keys after, so the title template can't be wiped by // the spread. metadataBase resolves every relative canonical and OG path. @@ -26,26 +29,25 @@ export const metadata: Metadata = { // Google Search Console ownership check. Public by design — it proves control of the // site, it grants nothing. verification: { - google: 'O-sqozPAg0xCaOeVyHDEaf0hcHrCrMEkOK0E_0TGBCo', + google: "O-sqozPAg0xCaOeVyHDEaf0hcHrCrMEkOK0E_0TGBCo", }, keywords: [ - 'react', - 'firebase', - 'firebase auth', - 'react hooks', - 'typescript', - 'authentication', - 'nextjs', + "react", + "firebase", + "firebase auth", + "react hooks", + "typescript", + "authentication", + "nextjs", ], }; -export default function Layout({ children }: LayoutProps<'/'>) { +export default function Layout({ children }: LayoutProps<"/">) { return ( + suppressHydrationWarning> {children} diff --git a/apps/docs/app/llms-full.txt/route.ts b/apps/docs/app/llms-full.txt/route.ts index d494d2c..fcccaee 100644 --- a/apps/docs/app/llms-full.txt/route.ts +++ b/apps/docs/app/llms-full.txt/route.ts @@ -1,4 +1,4 @@ -import { getLLMText, source } from '@/lib/source'; +import { getLLMText, source } from "@/lib/source"; export const revalidate = false; @@ -6,5 +6,5 @@ export async function GET() { const scan = source.getPages().map(getLLMText); const scanned = await Promise.all(scan); - return new Response(scanned.join('\n\n')); + return new Response(scanned.join("\n\n")); } diff --git a/apps/docs/app/llms.mdx/docs/[[...slug]]/route.ts b/apps/docs/app/llms.mdx/docs/[[...slug]]/route.ts index 395e6b6..726ff58 100644 --- a/apps/docs/app/llms.mdx/docs/[[...slug]]/route.ts +++ b/apps/docs/app/llms.mdx/docs/[[...slug]]/route.ts @@ -1,11 +1,11 @@ -import { getLLMText, getPageMarkdownUrl, source } from '@/lib/source'; -import { notFound } from 'next/navigation'; +import { getLLMText, getPageMarkdownUrl, source } from "@/lib/source"; +import { notFound } from "next/navigation"; export const revalidate = false; export async function GET( _req: Request, - { params }: RouteContext<'/llms.mdx/docs/[[...slug]]'>, + { params }: RouteContext<"/llms.mdx/docs/[[...slug]]">, ) { const { slug } = await params; const page = source.getPage(slug?.slice(0, -1)); @@ -13,13 +13,13 @@ export async function GET( return new Response(await getLLMText(page), { headers: { - 'Content-Type': 'text/markdown', + "Content-Type": "text/markdown", }, }); } export function generateStaticParams() { - return source.getPages().map((page) => ({ + return source.getPages().map(page => ({ lang: page.locale, slug: getPageMarkdownUrl(page).segments, })); diff --git a/apps/docs/app/llms.txt/route.ts b/apps/docs/app/llms.txt/route.ts index fc80cb6..f18f409 100644 --- a/apps/docs/app/llms.txt/route.ts +++ b/apps/docs/app/llms.txt/route.ts @@ -1,5 +1,5 @@ -import { source } from '@/lib/source'; -import { llms } from 'fumadocs-core/source'; +import { source } from "@/lib/source"; +import { llms } from "fumadocs-core/source"; export const revalidate = false; diff --git a/apps/docs/app/not-found.tsx b/apps/docs/app/not-found.tsx index ed8b21d..634ccac 100644 --- a/apps/docs/app/not-found.tsx +++ b/apps/docs/app/not-found.tsx @@ -1,27 +1,28 @@ -import { ArrowRight, BookOpen, Home } from 'lucide-react'; -import type { Metadata } from 'next'; -import Link from 'next/link'; -import { buildMetadata } from '@/lib/seo'; -import { appName, packageName } from '@/lib/shared'; +import { ArrowRight, BookOpen, Home } from "lucide-react"; +import type { Metadata } from "next"; +import Link from "next/link"; +import { buildMetadata } from "@/lib/seo"; +import { appName, packageName } from "@/lib/shared"; export const metadata: Metadata = buildMetadata({ - title: 'Page not found', - description: 'That page does not exist. The hook reference and guides are still here.', + title: "Page not found", + description: + "That page does not exist. The hook reference and guides are still here.", noIndex: true, }); const SUGGESTIONS = [ { icon: BookOpen, - title: 'Getting started', - body: 'Install the package and sign a user in.', - href: '/docs/getting-started', + title: "Getting started", + body: "Install the package and sign a user in.", + href: "/docs/getting-started", }, { icon: ArrowRight, - title: 'Auth reference', - body: 'All twenty hooks, grouped by flow.', - href: '/docs/auth', + title: "Auth reference", + body: "All twenty hooks, grouped by flow.", + href: "/docs/auth", }, ]; @@ -33,19 +34,20 @@ export default function NotFound() { site, arriving from a stale link with no idea where they landed. */} - + className="inline-flex items-center gap-2 text-sm text-fd-muted-foreground transition-colors hover:text-fd-foreground"> + {packageName} -

      404

      +

      + 404 +

      That page doesn’t exist

      -

      - The link may be out of date, or the page moved when the docs were reorganised. - You’re on the {appName} documentation — try one of these. +

      + The link may be out of date, or the page moved when the docs were + reorganised. You’re on the {appName} documentation — try one of these.

      @@ -53,24 +55,22 @@ export default function NotFound() { + className="group flex items-center gap-4 surface px-4 py-3 transition-colors hover:border-fd-primary/40"> {title} - {body} + {body} - + ))}
      + className="mt-8 inline-flex items-center gap-2 text-sm text-fd-muted-foreground transition-colors hover:text-fd-foreground"> Back to the home page diff --git a/apps/docs/app/og/docs/[...slug]/route.tsx b/apps/docs/app/og/docs/[...slug]/route.tsx index 8e0fa60..706ad05 100644 --- a/apps/docs/app/og/docs/[...slug]/route.tsx +++ b/apps/docs/app/og/docs/[...slug]/route.tsx @@ -1,11 +1,14 @@ -import { notFound } from 'next/navigation'; -import { ImageResponse } from 'next/og'; -import { OgImage } from '@/components/og-image'; -import { getPageImageUrl, source } from '@/lib/source'; +import { notFound } from "next/navigation"; +import { ImageResponse } from "next/og"; +import { OgImage } from "@/components/og-image"; +import { getPageImageUrl, source } from "@/lib/source"; export const revalidate = false; -export async function GET(_req: Request, { params }: RouteContext<'/og/docs/[...slug]'>) { +export async function GET( + _req: Request, + { params }: RouteContext<"/og/docs/[...slug]">, +) { const { slug } = await params; const page = source.getPage(slug.slice(0, -1)); if (!page) notFound(); @@ -17,7 +20,7 @@ export async function GET(_req: Request, { params }: RouteContext<'/og/docs/[... } export function generateStaticParams() { - return source.getPages().map((page) => ({ + return source.getPages().map(page => ({ lang: page.locale, slug: getPageImageUrl(page).segments, })); diff --git a/apps/docs/app/og/route.tsx b/apps/docs/app/og/route.tsx index cf81fc9..d4114d2 100644 --- a/apps/docs/app/og/route.tsx +++ b/apps/docs/app/og/route.tsx @@ -1,6 +1,6 @@ -import { ImageResponse } from 'next/og'; -import { OgImage } from '@/components/og-image'; -import { siteConfig } from '@/lib/site'; +import { ImageResponse } from "next/og"; +import { OgImage } from "@/components/og-image"; +import { siteConfig } from "@/lib/site"; /** * The default OG card, for the home page, 404 and anything without its own. @@ -11,8 +11,8 @@ export function GET(request: Request) { return new ImageResponse( , { width: 1200, height: 630 }, ); diff --git a/apps/docs/app/robots.ts b/apps/docs/app/robots.ts index 790aaba..8487c38 100644 --- a/apps/docs/app/robots.ts +++ b/apps/docs/app/robots.ts @@ -1,12 +1,12 @@ -import type { MetadataRoute } from 'next'; -import { isIndexableEnv, siteConfig } from '@/lib/site'; +import type { MetadataRoute } from "next"; +import { isIndexableEnv, siteConfig } from "@/lib/site"; export default function robots(): MetadataRoute.Robots { // Preview deployments get a blanket disallow so Vercel's per-commit URLs // never compete with the production site for the same content. if (!isIndexableEnv) { return { - rules: { userAgent: '*', disallow: '/' }, + rules: { userAgent: "*", disallow: "/" }, }; } @@ -14,9 +14,9 @@ export default function robots(): MetadataRoute.Robots { // AI crawlers are deliberately allowed: being cited by an answer engine is // how a library like this gets found, and the docs are public anyway. rules: { - userAgent: '*', - allow: '/', - disallow: ['/api/', '/og/'], + userAgent: "*", + allow: "/", + disallow: ["/api/", "/og/"], }, sitemap: `${siteConfig.url}/sitemap.xml`, host: siteConfig.url, diff --git a/apps/docs/app/sitemap.ts b/apps/docs/app/sitemap.ts index 673ff30..efbd259 100644 --- a/apps/docs/app/sitemap.ts +++ b/apps/docs/app/sitemap.ts @@ -1,6 +1,6 @@ -import type { MetadataRoute } from 'next'; -import { source } from '@/lib/source'; -import { siteConfig } from '@/lib/site'; +import type { MetadataRoute } from "next"; +import { source } from "@/lib/source"; +import { siteConfig } from "@/lib/site"; export const revalidate = false; @@ -8,15 +8,16 @@ export default function sitemap(): MetadataRoute.Sitemap { const url = (path: string) => new URL(path, siteConfig.url).toString(); return [ - { url: url('/'), changeFrequency: 'monthly', priority: 1 }, + { url: url("/"), changeFrequency: "monthly", priority: 1 }, // Every docs page, straight from the same source the navigation uses, so a // new page is listed the moment it exists — no second list to maintain. - ...source.getPages().map((page) => ({ + ...source.getPages().map(page => ({ url: url(page.url), - changeFrequency: 'monthly' as const, + changeFrequency: "monthly" as const, // The reference is the reason people arrive, but getting started should // outrank an individual hook page. - priority: page.url === '/docs' || page.url.split('/').length <= 3 ? 0.8 : 0.6, + priority: + page.url === "/docs" || page.url.split("/").length <= 3 ? 0.8 : 0.6, })), ]; } diff --git a/apps/docs/components/code-sample.tsx b/apps/docs/components/code-sample.tsx index 4281776..9ee986d 100644 --- a/apps/docs/components/code-sample.tsx +++ b/apps/docs/components/code-sample.tsx @@ -1,6 +1,6 @@ -import { highlight } from 'fumadocs-core/highlight'; -import { CodeBlock, Pre } from 'fumadocs-ui/components/codeblock'; -import type { ReactNode } from 'react'; +import { highlight } from "fumadocs-core/highlight"; +import { CodeBlock, Pre } from "fumadocs-ui/components/codeblock"; +import type { ReactNode } from "react"; /** * Shiki-highlighted code for hand-written pages. MDX code fences get this from @@ -16,11 +16,11 @@ import type { ReactNode } from 'react'; * lines scroll; overriding to `w-full` is what gives `pre-wrap` something to * wrap against. Without the `pre` override the wrap silently does nothing. */ -const WRAP = '[&_pre]:w-full [&_code]:whitespace-pre-wrap [&_code]:break-words'; +const WRAP = "[&_pre]:w-full [&_code]:whitespace-pre-wrap [&_code]:break-words"; export async function CodeSample({ code, - lang = 'tsx', + lang = "tsx", title, className, wrap = false, @@ -33,18 +33,17 @@ export async function CodeSample({ }): Promise { return highlight(code, { lang, - themes: { light: 'github-light', dark: 'github-dark' }, + themes: { light: "github-light", dark: "github-dark" }, // Emit --shiki-light/--shiki-dark variables instead of a baked-in colour. // Fumadocs' stylesheet switches on those; without this the light theme is // hardcoded and the blocks stay light in dark mode. defaultColor: false, components: { - pre: (props) => ( + pre: props => ( + className={[wrap && WRAP, className].filter(Boolean).join(" ")} + keepBackground={false}>
               
             ),
      diff --git a/apps/docs/components/copy-button.tsx b/apps/docs/components/copy-button.tsx
      index ddbe37a..e2d47e4 100644
      --- a/apps/docs/components/copy-button.tsx
      +++ b/apps/docs/components/copy-button.tsx
      @@ -1,7 +1,7 @@
      -'use client';
      +"use client";
       
      -import { Check, Copy } from 'lucide-react';
      -import { useEffect, useState } from 'react';
      +import { Check, Copy } from "lucide-react";
      +import { useEffect, useState } from "react";
       
       /**
        * Copy-to-clipboard for the hero's install command, which is plain text rather
      @@ -30,10 +30,9 @@ export function CopyButton({ value, label }: { value: string; label: string }) {
                 () => {},
               );
             }}
      -      className="text-fd-muted-foreground hover:text-fd-foreground -mr-1 rounded p-1 transition-colors"
      -    >
      +      className="-mr-1 rounded p-1 text-fd-muted-foreground transition-colors hover:text-fd-foreground">
             {copied ? (
      -        
      +        
             ) : (
               
             )}
      diff --git a/apps/docs/components/json-ld.tsx b/apps/docs/components/json-ld.tsx
      index 466b10f..93bbdf4 100644
      --- a/apps/docs/components/json-ld.tsx
      +++ b/apps/docs/components/json-ld.tsx
      @@ -4,14 +4,17 @@
        */
       export function JsonLd({ data }: { data: object | object[] }) {
         const json = JSON.stringify(data)
      -    .replace(/&/g, '\\u0026')
      -    .replace(/
      +