@usethrottle/auth 0.1.1 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,3 +1,4 @@
1
+ "use client";
1
2
  "use strict";
2
3
  var __defProp = Object.defineProperty;
3
4
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
@@ -93,6 +94,8 @@ function messageForError(err) {
93
94
  return "That cart belongs to a different account.";
94
95
  case "not_found":
95
96
  return "We could not find that.";
97
+ case "payment_method_in_use":
98
+ return e.message || "That payment method is in use and cannot be removed.";
96
99
  default:
97
100
  return "Something went wrong. Please try again.";
98
101
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/react/forms/index.tsx","../../../src/react/forms/SignIn.tsx","../../../src/react/context.tsx","../../../src/react/forms/useFormAction.ts","../../../src/react/forms/messages.ts","../../../src/react/forms/SignUp.tsx","../../../src/react/forms/ForgotPassword.tsx","../../../src/react/forms/ResetPassword.tsx","../../../src/react/forms/VerifyEmail.tsx"],"sourcesContent":["export { SignInForm } from './SignIn';\nexport type { SignInFormProps } from './SignIn';\nexport { SignUpForm } from './SignUp';\nexport type { SignUpFormProps } from './SignUp';\nexport { ForgotPasswordForm } from './ForgotPassword';\nexport type { ForgotPasswordFormProps } from './ForgotPassword';\nexport { ResetPasswordForm } from './ResetPassword';\nexport type { ResetPasswordFormProps } from './ResetPassword';\nexport { VerifyEmailPanel } from './VerifyEmail';\nexport type { VerifyEmailPanelProps } from './VerifyEmail';\nexport { messageForError } from './messages';\n","import { useId, useState } from 'react';\nimport { useAuth } from '../context';\nimport { useFormAction } from './useFormAction';\n\nexport interface SignInFormProps {\n onSuccess?: () => void;\n onForgotPassword?: () => void;\n className?: string;\n labels?: Partial<{ email: string; password: string; submit: string; forgot: string }>;\n}\n\nexport function SignInForm(props: SignInFormProps) {\n const { auth } = useAuth();\n const { submitting, error, run } = useFormAction();\n const [email, setEmail] = useState('');\n const [password, setPassword] = useState('');\n const ids = { email: `${useId()}-email`, password: `${useId()}-password` };\n const l = { email: 'Email', password: 'Password', submit: 'Sign in', forgot: 'Forgot your password?', ...props.labels };\n\n return (\n <form\n data-testid=\"throttle-auth-signin\"\n className={props.className ?? 'throttle-auth-form'}\n onSubmit={(e) => {\n e.preventDefault();\n void run(async () => { await auth.signIn(email, password); props.onSuccess?.(); });\n }}\n >\n <label className=\"throttle-auth-label\" htmlFor={ids.email}>{l.email}</label>\n <input className=\"throttle-auth-input\" id={ids.email} type=\"email\" autoComplete=\"email\"\n value={email} onChange={(e) => setEmail(e.target.value)} required />\n\n <label className=\"throttle-auth-label\" htmlFor={ids.password}>{l.password}</label>\n <input className=\"throttle-auth-input\" id={ids.password} type=\"password\" autoComplete=\"current-password\"\n value={password} onChange={(e) => setPassword(e.target.value)} required />\n\n {error && <p className=\"throttle-auth-error\" role=\"alert\">{error}</p>}\n\n <button className=\"throttle-auth-submit\" type=\"submit\" disabled={submitting}>\n {submitting ? '…' : l.submit}\n </button>\n\n {props.onForgotPassword && (\n <button className=\"throttle-auth-link\" type=\"button\" onClick={props.onForgotPassword}>{l.forgot}</button>\n )}\n </form>\n );\n}\n","import { createContext, useContext, useEffect, useMemo, useRef, useSyncExternalStore } from 'react';\nimport type { ReactNode } from 'react';\nimport { createThrottleAuth, type ThrottleAuth } from '../client';\nimport { createAccount, type ThrottleAccount } from '../account';\nimport type { AuthSnapshot } from '../session';\nimport type { ThrottleAuthConfig } from '../types';\n\nexport interface ThrottleAuthProviderProps extends Partial<ThrottleAuthConfig> {\n publishableKey: string;\n /** Inject a pre-built client. Tests use it; merchants sharing one client across roots may too. */\n auth?: ThrottleAuth;\n children: ReactNode;\n}\n\ninterface ContextValue { auth: ThrottleAuth; account: ThrottleAccount }\n\nconst Ctx = createContext<ContextValue | null>(null);\n\nexport function ThrottleAuthProvider(props: ThrottleAuthProviderProps) {\n const { auth: injected, publishableKey, baseUrl, storage, onStepUpRequired, children } = props;\n\n // `storage` and `onStepUpRequired` are deliberately NOT dependencies: they are\n // read once at mount. A merchant passing `storage={memoryStorage()}` inline —\n // which the README suggests — would otherwise get a new client on every parent\n // render, and with it a fresh empty SessionState that reads as signed out.\n const value = useMemo<ContextValue>(() => {\n const auth = injected ?? createThrottleAuth({ publishableKey, baseUrl, storage, onStepUpRequired });\n return { auth, account: createAccount(auth) };\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [injected, publishableKey, baseUrl]);\n\n const restored = useRef<ThrottleAuth | null>(null);\n useEffect(() => {\n // Once per client. StrictMode runs effects twice in development, and a\n // second restore() would present the refresh token that the first one\n // just rotated away.\n if (restored.current === value.auth) return;\n restored.current = value.auth;\n void value.auth.restore();\n }, [value]);\n\n return <Ctx.Provider value={value}>{children}</Ctx.Provider>;\n}\n\nfunction useCtx(): ContextValue {\n const ctx = useContext(Ctx);\n if (!ctx) throw new Error('useAuth must be used inside a <ThrottleAuthProvider>');\n return ctx;\n}\n\nexport function useAuthContext(): ContextValue { return useCtx(); }\n\nexport type UseAuthResult = AuthSnapshot & {\n isLoading: boolean;\n isAuthenticated: boolean;\n auth: ThrottleAuth;\n account: ThrottleAccount;\n};\n\nexport function useAuth(): UseAuthResult {\n const { auth, account } = useCtx();\n const snapshot = useSyncExternalStore(auth.subscribe, auth.getSnapshot, auth.getSnapshot);\n return {\n ...snapshot,\n isLoading: snapshot.status === 'loading',\n isAuthenticated: snapshot.status === 'authenticated',\n auth,\n account,\n };\n}\n","import { useCallback, useRef, useState } from 'react';\nimport { messageForError } from './messages';\n\nexport interface FormAction {\n submitting: boolean;\n error: string | null;\n notice: string | null;\n run(fn: () => Promise<string | void>): Promise<void>;\n}\n\n/** Submit-once semantics plus mapped error copy, shared by all five forms. */\nexport function useFormAction(): FormAction {\n const [submitting, setSubmitting] = useState(false);\n const [error, setError] = useState<string | null>(null);\n const [notice, setNotice] = useState<string | null>(null);\n const inFlight = useRef(false);\n\n const run = useCallback(async (fn: () => Promise<string | void>) => {\n if (inFlight.current) return; // a double-click is one attempt, not two\n inFlight.current = true;\n setSubmitting(true);\n setError(null);\n setNotice(null);\n try {\n const message = await fn();\n if (typeof message === 'string') setNotice(message);\n } catch (err) {\n setError(messageForError(err));\n } finally {\n inFlight.current = false;\n setSubmitting(false);\n }\n }, []);\n\n return { submitting, error, notice, run };\n}\n","import { ThrottleAuthError } from '../../errors';\n\nfunction waitCopy(seconds?: number): string {\n if (!seconds || !Number.isFinite(seconds)) return 'Please wait a moment and try again.';\n if (seconds < 60) return `Please wait ${Math.ceil(seconds)} seconds and try again.`;\n const minutes = Math.ceil(seconds / 60);\n return `Please wait ${minutes} ${minutes === 1 ? 'minute' : 'minutes'} and try again.`;\n}\n\n/**\n * Buyer-facing copy for every code the storefront API returns. Two codes are\n * deliberately NOT passed through from the server: `invalid_credentials` (the\n * server is vague on purpose — enumerating accounts through our own UI would\n * undo that) and anything unrecognised.\n */\nexport function messageForError(err: unknown): string {\n const e = err as Partial<ThrottleAuthError> & { code?: string };\n switch (e?.code) {\n case 'invalid_credentials':\n return 'That email or password is not correct.';\n case 'weak_password':\n // The server's message IS the policy reason (\"Use at least 12 characters\").\n return e.message || 'Please choose a stronger password.';\n case 'too_many_attempts':\n return `Too many attempts. ${waitCopy(e.retryAfter)}`;\n case 'verification_required':\n return 'Please verify your email address to continue.';\n case 'step_up_required':\n return 'Please re-enter your password to continue.';\n case 'session_revoked':\n return 'Your session has ended. Please sign in again.';\n case 'token_expired':\n case 'token_invalid':\n case 'token_already_used':\n return 'That link is no longer valid. Please request a new link.';\n case 'auth_unavailable':\n return 'Accounts are briefly unavailable. Please try again shortly.';\n case 'origin_not_allowed':\n case 'auth_not_enabled':\n case 'invalid_api_key':\n case 'environment_mismatch':\n // Merchant misconfiguration, not something the buyer can fix.\n return 'Accounts are not available right now. Please contact support.';\n case 'cart_already_claimed':\n return 'That cart belongs to a different account.';\n case 'not_found':\n return 'We could not find that.';\n default:\n return 'Something went wrong. Please try again.';\n }\n}\n","import { useId, useState } from 'react';\nimport { useAuth } from '../context';\nimport { useFormAction } from './useFormAction';\n\nexport interface SignUpFormProps {\n onSuccess?: () => void;\n /** Called for the 202 shape: the address already has credentials, so we sent an email instead. */\n onPending?: () => void;\n className?: string;\n labels?: Partial<{ email: string; password: string; submit: string; pending: string }>;\n}\n\nexport function SignUpForm(props: SignUpFormProps) {\n const { auth } = useAuth();\n const { submitting, error, notice, run } = useFormAction();\n const [email, setEmail] = useState('');\n const [password, setPassword] = useState('');\n const ids = { email: `${useId()}-email`, password: `${useId()}-password` };\n const l = {\n email: 'Email', password: 'Password', submit: 'Create account',\n pending: 'Check your email — we sent you a link to finish signing in.',\n ...props.labels,\n };\n\n return (\n <form\n data-testid=\"throttle-auth-signup\"\n className={props.className ?? 'throttle-auth-form'}\n onSubmit={(e) => {\n e.preventDefault();\n void run(async () => {\n const result = await auth.register(email, password);\n // 202: an account already exists for this address. There is no\n // session — say so rather than routing to a signed-in page.\n if (result.status === 'pending') { props.onPending?.(); return l.pending; }\n props.onSuccess?.();\n });\n }}\n >\n <label className=\"throttle-auth-label\" htmlFor={ids.email}>{l.email}</label>\n <input className=\"throttle-auth-input\" id={ids.email} type=\"email\" autoComplete=\"email\"\n value={email} onChange={(e) => setEmail(e.target.value)} required />\n\n <label className=\"throttle-auth-label\" htmlFor={ids.password}>{l.password}</label>\n <input className=\"throttle-auth-input\" id={ids.password} type=\"password\" autoComplete=\"new-password\"\n value={password} onChange={(e) => setPassword(e.target.value)} required />\n\n {error && <p className=\"throttle-auth-error\" role=\"alert\">{error}</p>}\n {notice && <p className=\"throttle-auth-notice\" role=\"status\">{notice}</p>}\n\n <button className=\"throttle-auth-submit\" type=\"submit\" disabled={submitting}>\n {submitting ? '…' : l.submit}\n </button>\n </form>\n );\n}\n","import { useId, useState } from 'react';\nimport { useAuth } from '../context';\nimport { useFormAction } from './useFormAction';\n\nexport interface ForgotPasswordFormProps {\n onSent?: () => void;\n className?: string;\n labels?: Partial<{ email: string; submit: string; sent: string }>;\n}\n\nexport function ForgotPasswordForm(props: ForgotPasswordFormProps) {\n const { auth } = useAuth();\n const { submitting, error, notice, run } = useFormAction();\n const [email, setEmail] = useState('');\n const id = `${useId()}-email`;\n const l = {\n email: 'Email', submit: 'Send reset link',\n // Identical copy whether or not the address has an account — the API is\n // deliberately silent on that and this form must be too.\n sent: 'If an account exists for that address, we have sent a reset link.',\n ...props.labels,\n };\n\n return (\n <form\n data-testid=\"throttle-auth-forgot\"\n className={props.className ?? 'throttle-auth-form'}\n onSubmit={(e) => {\n e.preventDefault();\n void run(async () => { await auth.forgotPassword(email); props.onSent?.(); return l.sent; });\n }}\n >\n <label className=\"throttle-auth-label\" htmlFor={id}>{l.email}</label>\n <input className=\"throttle-auth-input\" id={id} type=\"email\" autoComplete=\"email\"\n value={email} onChange={(e) => setEmail(e.target.value)} required />\n\n {error && <p className=\"throttle-auth-error\" role=\"alert\">{error}</p>}\n {notice && <p className=\"throttle-auth-notice\" role=\"status\">{notice}</p>}\n\n <button className=\"throttle-auth-submit\" type=\"submit\" disabled={submitting}>\n {submitting ? '…' : l.submit}\n </button>\n </form>\n );\n}\n","import { useId, useState } from 'react';\nimport { useAuth } from '../context';\nimport { useFormAction } from './useFormAction';\n\nexport interface ResetPasswordFormProps {\n /** The token from the emailed link, e.g. `new URLSearchParams(location.search).get('token')`. */\n token: string;\n onSuccess?: () => void;\n className?: string;\n labels?: Partial<{ password: string; submit: string; done: string }>;\n}\n\nexport function ResetPasswordForm(props: ResetPasswordFormProps) {\n const { auth } = useAuth();\n const { submitting, error, notice, run } = useFormAction();\n const [password, setPassword] = useState('');\n const id = `${useId()}-new-password`;\n const l = {\n password: 'New password', submit: 'Set new password',\n done: 'Your password has been reset. Please sign in with your new password.',\n ...props.labels,\n };\n\n return (\n <form\n data-testid=\"throttle-auth-reset\"\n className={props.className ?? 'throttle-auth-form'}\n onSubmit={(e) => {\n e.preventDefault();\n void run(async () => {\n await auth.resetPassword(props.token, password);\n props.onSuccess?.();\n // Every session died with the reset, this one included — the buyer\n // signs in again rather than landing on an account page.\n return l.done;\n });\n }}\n >\n <label className=\"throttle-auth-label\" htmlFor={id}>{l.password}</label>\n <input className=\"throttle-auth-input\" id={id} type=\"password\" autoComplete=\"new-password\"\n value={password} onChange={(e) => setPassword(e.target.value)} required />\n\n {error && <p className=\"throttle-auth-error\" role=\"alert\">{error}</p>}\n {notice && <p className=\"throttle-auth-notice\" role=\"status\">{notice}</p>}\n\n <button className=\"throttle-auth-submit\" type=\"submit\" disabled={submitting}>\n {submitting ? '…' : l.submit}\n </button>\n </form>\n );\n}\n","import { useEffect, useRef, useState } from 'react';\nimport { useAuth } from '../context';\nimport { useFormAction } from './useFormAction';\n\nexport interface VerifyEmailPanelProps {\n /** Token from the emailed link. Omit to render the \"resend\" state instead. */\n token?: string;\n onVerified?: () => void;\n className?: string;\n labels?: Partial<{ verifying: string; verified: string; prompt: string; resend: string; resent: string }>;\n}\n\nexport function VerifyEmailPanel(props: VerifyEmailPanelProps) {\n const { auth, emailVerified } = useAuth();\n const { submitting, error, notice, run } = useFormAction();\n const attempted = useRef<string | null>(null);\n const [verifiedLocally, setVerifiedLocally] = useState(false);\n const l = {\n verifying: 'Verifying your email…',\n verified: 'Your email address is verified.',\n prompt: 'Please verify your email address to unlock your full account.',\n resend: 'Resend verification email',\n resent: 'Sent. Check your inbox.',\n ...props.labels,\n };\n\n useEffect(() => {\n if (!props.token || attempted.current === props.token) return;\n // Once per token. Re-running would burn the single-use token and report\n // `token_already_used` to a buyer who did nothing wrong.\n //\n // useFormAction's `inFlight` flag happens to block the StrictMode\n // double-invoke too, but that is a submit-debounce concern that belongs to\n // form actions generally. Burning a single-use token is this panel's own\n // invariant, and it should not depend on another layer's debounce\n // semantics staying exactly as they are today.\n attempted.current = props.token;\n void run(async () => {\n await auth.verifyEmail(props.token!);\n setVerifiedLocally(true);\n props.onVerified?.();\n });\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [props.token]);\n\n return (\n <div data-testid=\"throttle-auth-verify\" className={props.className ?? 'throttle-auth-panel'}>\n {props.token && submitting && <p className=\"throttle-auth-notice\" role=\"status\">{l.verifying}</p>}\n {(emailVerified || verifiedLocally) && (\n <p className=\"throttle-auth-notice\" role=\"status\">{l.verified}</p>\n )}\n {!emailVerified && !props.token && <p className=\"throttle-auth-prompt\">{l.prompt}</p>}\n\n {error && <p className=\"throttle-auth-error\" role=\"alert\">{error}</p>}\n {notice && <p className=\"throttle-auth-notice\" role=\"status\">{notice}</p>}\n\n {!emailVerified && !props.token && (\n <button\n className=\"throttle-auth-submit\" type=\"button\" disabled={submitting}\n onClick={() => void run(async () => { await auth.resendVerification(); return l.resent; })}\n >\n {l.resend}\n </button>\n )}\n </div>\n );\n}\n"],"mappings":"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","names":["import_react","import_react","import_jsx_runtime","import_react","import_jsx_runtime","import_react","import_jsx_runtime","import_react","import_jsx_runtime","import_react","import_jsx_runtime"]}
1
+ {"version":3,"sources":["../../../src/react/forms/index.tsx","../../../src/react/forms/SignIn.tsx","../../../src/react/context.tsx","../../../src/react/forms/useFormAction.ts","../../../src/react/forms/messages.ts","../../../src/react/forms/SignUp.tsx","../../../src/react/forms/ForgotPassword.tsx","../../../src/react/forms/ResetPassword.tsx","../../../src/react/forms/VerifyEmail.tsx"],"sourcesContent":["export { SignInForm } from './SignIn';\nexport type { SignInFormProps } from './SignIn';\nexport { SignUpForm } from './SignUp';\nexport type { SignUpFormProps } from './SignUp';\nexport { ForgotPasswordForm } from './ForgotPassword';\nexport type { ForgotPasswordFormProps } from './ForgotPassword';\nexport { ResetPasswordForm } from './ResetPassword';\nexport type { ResetPasswordFormProps } from './ResetPassword';\nexport { VerifyEmailPanel } from './VerifyEmail';\nexport type { VerifyEmailPanelProps } from './VerifyEmail';\nexport { messageForError } from './messages';\n","import { useId, useState } from 'react';\nimport { useAuth } from '../context';\nimport { useFormAction } from './useFormAction';\n\nexport interface SignInFormProps {\n onSuccess?: () => void;\n onForgotPassword?: () => void;\n className?: string;\n labels?: Partial<{ email: string; password: string; submit: string; forgot: string }>;\n}\n\nexport function SignInForm(props: SignInFormProps) {\n const { auth } = useAuth();\n const { submitting, error, run } = useFormAction();\n const [email, setEmail] = useState('');\n const [password, setPassword] = useState('');\n const ids = { email: `${useId()}-email`, password: `${useId()}-password` };\n const l = { email: 'Email', password: 'Password', submit: 'Sign in', forgot: 'Forgot your password?', ...props.labels };\n\n return (\n <form\n data-testid=\"throttle-auth-signin\"\n className={props.className ?? 'throttle-auth-form'}\n onSubmit={(e) => {\n e.preventDefault();\n void run(async () => { await auth.signIn(email, password); props.onSuccess?.(); });\n }}\n >\n <label className=\"throttle-auth-label\" htmlFor={ids.email}>{l.email}</label>\n <input className=\"throttle-auth-input\" id={ids.email} type=\"email\" autoComplete=\"email\"\n value={email} onChange={(e) => setEmail(e.target.value)} required />\n\n <label className=\"throttle-auth-label\" htmlFor={ids.password}>{l.password}</label>\n <input className=\"throttle-auth-input\" id={ids.password} type=\"password\" autoComplete=\"current-password\"\n value={password} onChange={(e) => setPassword(e.target.value)} required />\n\n {error && <p className=\"throttle-auth-error\" role=\"alert\">{error}</p>}\n\n <button className=\"throttle-auth-submit\" type=\"submit\" disabled={submitting}>\n {submitting ? '…' : l.submit}\n </button>\n\n {props.onForgotPassword && (\n <button className=\"throttle-auth-link\" type=\"button\" onClick={props.onForgotPassword}>{l.forgot}</button>\n )}\n </form>\n );\n}\n","import { createContext, useContext, useEffect, useMemo, useRef, useSyncExternalStore } from 'react';\nimport type { ReactNode } from 'react';\nimport { createThrottleAuth, type ThrottleAuth } from '../client';\nimport { createAccount, type ThrottleAccount } from '../account';\nimport type { AuthSnapshot } from '../session';\nimport type { ThrottleAuthConfig } from '../types';\n\nexport interface ThrottleAuthProviderProps extends Partial<ThrottleAuthConfig> {\n publishableKey: string;\n /** Inject a pre-built client. Tests use it; merchants sharing one client across roots may too. */\n auth?: ThrottleAuth;\n children: ReactNode;\n}\n\ninterface ContextValue { auth: ThrottleAuth; account: ThrottleAccount }\n\nconst Ctx = createContext<ContextValue | null>(null);\n\nexport function ThrottleAuthProvider(props: ThrottleAuthProviderProps) {\n const { auth: injected, publishableKey, baseUrl, storage, onStepUpRequired, children } = props;\n\n // `storage` and `onStepUpRequired` are deliberately NOT dependencies: they are\n // read once at mount. A merchant passing `storage={memoryStorage()}` inline —\n // which the README suggests — would otherwise get a new client on every parent\n // render, and with it a fresh empty SessionState that reads as signed out.\n const value = useMemo<ContextValue>(() => {\n const auth = injected ?? createThrottleAuth({ publishableKey, baseUrl, storage, onStepUpRequired });\n return { auth, account: createAccount(auth) };\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [injected, publishableKey, baseUrl]);\n\n const restored = useRef<ThrottleAuth | null>(null);\n useEffect(() => {\n // Once per client. StrictMode runs effects twice in development, and a\n // second restore() would present the refresh token that the first one\n // just rotated away.\n if (restored.current === value.auth) return;\n restored.current = value.auth;\n void value.auth.restore();\n }, [value]);\n\n return <Ctx.Provider value={value}>{children}</Ctx.Provider>;\n}\n\nfunction useCtx(): ContextValue {\n const ctx = useContext(Ctx);\n if (!ctx) throw new Error('useAuth must be used inside a <ThrottleAuthProvider>');\n return ctx;\n}\n\nexport function useAuthContext(): ContextValue { return useCtx(); }\n\nexport type UseAuthResult = AuthSnapshot & {\n isLoading: boolean;\n isAuthenticated: boolean;\n auth: ThrottleAuth;\n account: ThrottleAccount;\n};\n\nexport function useAuth(): UseAuthResult {\n const { auth, account } = useCtx();\n const snapshot = useSyncExternalStore(auth.subscribe, auth.getSnapshot, auth.getSnapshot);\n return {\n ...snapshot,\n isLoading: snapshot.status === 'loading',\n isAuthenticated: snapshot.status === 'authenticated',\n auth,\n account,\n };\n}\n","import { useCallback, useRef, useState } from 'react';\nimport { messageForError } from './messages';\n\nexport interface FormAction {\n submitting: boolean;\n error: string | null;\n notice: string | null;\n run(fn: () => Promise<string | void>): Promise<void>;\n}\n\n/** Submit-once semantics plus mapped error copy, shared by all five forms. */\nexport function useFormAction(): FormAction {\n const [submitting, setSubmitting] = useState(false);\n const [error, setError] = useState<string | null>(null);\n const [notice, setNotice] = useState<string | null>(null);\n const inFlight = useRef(false);\n\n const run = useCallback(async (fn: () => Promise<string | void>) => {\n if (inFlight.current) return; // a double-click is one attempt, not two\n inFlight.current = true;\n setSubmitting(true);\n setError(null);\n setNotice(null);\n try {\n const message = await fn();\n if (typeof message === 'string') setNotice(message);\n } catch (err) {\n setError(messageForError(err));\n } finally {\n inFlight.current = false;\n setSubmitting(false);\n }\n }, []);\n\n return { submitting, error, notice, run };\n}\n","import { ThrottleAuthError } from '../../errors';\n\nfunction waitCopy(seconds?: number): string {\n if (!seconds || !Number.isFinite(seconds)) return 'Please wait a moment and try again.';\n if (seconds < 60) return `Please wait ${Math.ceil(seconds)} seconds and try again.`;\n const minutes = Math.ceil(seconds / 60);\n return `Please wait ${minutes} ${minutes === 1 ? 'minute' : 'minutes'} and try again.`;\n}\n\n/**\n * Buyer-facing copy for every code the storefront API returns. Two codes are\n * deliberately NOT passed through from the server: `invalid_credentials` (the\n * server is vague on purpose — enumerating accounts through our own UI would\n * undo that) and anything unrecognised.\n */\nexport function messageForError(err: unknown): string {\n const e = err as Partial<ThrottleAuthError> & { code?: string };\n switch (e?.code) {\n case 'invalid_credentials':\n return 'That email or password is not correct.';\n case 'weak_password':\n // The server's message IS the policy reason (\"Use at least 12 characters\").\n return e.message || 'Please choose a stronger password.';\n case 'too_many_attempts':\n return `Too many attempts. ${waitCopy(e.retryAfter)}`;\n case 'verification_required':\n return 'Please verify your email address to continue.';\n case 'step_up_required':\n return 'Please re-enter your password to continue.';\n case 'session_revoked':\n return 'Your session has ended. Please sign in again.';\n case 'token_expired':\n case 'token_invalid':\n case 'token_already_used':\n return 'That link is no longer valid. Please request a new link.';\n case 'auth_unavailable':\n return 'Accounts are briefly unavailable. Please try again shortly.';\n case 'origin_not_allowed':\n case 'auth_not_enabled':\n case 'invalid_api_key':\n case 'environment_mismatch':\n // Merchant misconfiguration, not something the buyer can fix.\n return 'Accounts are not available right now. Please contact support.';\n case 'cart_already_claimed':\n return 'That cart belongs to a different account.';\n case 'not_found':\n return 'We could not find that.';\n case 'payment_method_in_use':\n // The server's message IS the fix (\"set another card as default\n // first\") — same rationale as weak_password below.\n return e.message || 'That payment method is in use and cannot be removed.';\n default:\n return 'Something went wrong. Please try again.';\n }\n}\n","import { useId, useState } from 'react';\nimport { useAuth } from '../context';\nimport { useFormAction } from './useFormAction';\n\nexport interface SignUpFormProps {\n onSuccess?: () => void;\n /** Called for the 202 shape: the address already has credentials, so we sent an email instead. */\n onPending?: () => void;\n className?: string;\n labels?: Partial<{ email: string; password: string; submit: string; pending: string }>;\n}\n\nexport function SignUpForm(props: SignUpFormProps) {\n const { auth } = useAuth();\n const { submitting, error, notice, run } = useFormAction();\n const [email, setEmail] = useState('');\n const [password, setPassword] = useState('');\n const ids = { email: `${useId()}-email`, password: `${useId()}-password` };\n const l = {\n email: 'Email', password: 'Password', submit: 'Create account',\n pending: 'Check your email — we sent you a link to finish signing in.',\n ...props.labels,\n };\n\n return (\n <form\n data-testid=\"throttle-auth-signup\"\n className={props.className ?? 'throttle-auth-form'}\n onSubmit={(e) => {\n e.preventDefault();\n void run(async () => {\n const result = await auth.register(email, password);\n // 202: an account already exists for this address. There is no\n // session — say so rather than routing to a signed-in page.\n if (result.status === 'pending') { props.onPending?.(); return l.pending; }\n props.onSuccess?.();\n });\n }}\n >\n <label className=\"throttle-auth-label\" htmlFor={ids.email}>{l.email}</label>\n <input className=\"throttle-auth-input\" id={ids.email} type=\"email\" autoComplete=\"email\"\n value={email} onChange={(e) => setEmail(e.target.value)} required />\n\n <label className=\"throttle-auth-label\" htmlFor={ids.password}>{l.password}</label>\n <input className=\"throttle-auth-input\" id={ids.password} type=\"password\" autoComplete=\"new-password\"\n value={password} onChange={(e) => setPassword(e.target.value)} required />\n\n {error && <p className=\"throttle-auth-error\" role=\"alert\">{error}</p>}\n {notice && <p className=\"throttle-auth-notice\" role=\"status\">{notice}</p>}\n\n <button className=\"throttle-auth-submit\" type=\"submit\" disabled={submitting}>\n {submitting ? '…' : l.submit}\n </button>\n </form>\n );\n}\n","import { useId, useState } from 'react';\nimport { useAuth } from '../context';\nimport { useFormAction } from './useFormAction';\n\nexport interface ForgotPasswordFormProps {\n onSent?: () => void;\n className?: string;\n labels?: Partial<{ email: string; submit: string; sent: string }>;\n}\n\nexport function ForgotPasswordForm(props: ForgotPasswordFormProps) {\n const { auth } = useAuth();\n const { submitting, error, notice, run } = useFormAction();\n const [email, setEmail] = useState('');\n const id = `${useId()}-email`;\n const l = {\n email: 'Email', submit: 'Send reset link',\n // Identical copy whether or not the address has an account — the API is\n // deliberately silent on that and this form must be too.\n sent: 'If an account exists for that address, we have sent a reset link.',\n ...props.labels,\n };\n\n return (\n <form\n data-testid=\"throttle-auth-forgot\"\n className={props.className ?? 'throttle-auth-form'}\n onSubmit={(e) => {\n e.preventDefault();\n void run(async () => { await auth.forgotPassword(email); props.onSent?.(); return l.sent; });\n }}\n >\n <label className=\"throttle-auth-label\" htmlFor={id}>{l.email}</label>\n <input className=\"throttle-auth-input\" id={id} type=\"email\" autoComplete=\"email\"\n value={email} onChange={(e) => setEmail(e.target.value)} required />\n\n {error && <p className=\"throttle-auth-error\" role=\"alert\">{error}</p>}\n {notice && <p className=\"throttle-auth-notice\" role=\"status\">{notice}</p>}\n\n <button className=\"throttle-auth-submit\" type=\"submit\" disabled={submitting}>\n {submitting ? '…' : l.submit}\n </button>\n </form>\n );\n}\n","import { useId, useState } from 'react';\nimport { useAuth } from '../context';\nimport { useFormAction } from './useFormAction';\n\nexport interface ResetPasswordFormProps {\n /** The token from the emailed link, e.g. `new URLSearchParams(location.search).get('token')`. */\n token: string;\n onSuccess?: () => void;\n className?: string;\n labels?: Partial<{ password: string; submit: string; done: string }>;\n}\n\nexport function ResetPasswordForm(props: ResetPasswordFormProps) {\n const { auth } = useAuth();\n const { submitting, error, notice, run } = useFormAction();\n const [password, setPassword] = useState('');\n const id = `${useId()}-new-password`;\n const l = {\n password: 'New password', submit: 'Set new password',\n done: 'Your password has been reset. Please sign in with your new password.',\n ...props.labels,\n };\n\n return (\n <form\n data-testid=\"throttle-auth-reset\"\n className={props.className ?? 'throttle-auth-form'}\n onSubmit={(e) => {\n e.preventDefault();\n void run(async () => {\n await auth.resetPassword(props.token, password);\n props.onSuccess?.();\n // Every session died with the reset, this one included — the buyer\n // signs in again rather than landing on an account page.\n return l.done;\n });\n }}\n >\n <label className=\"throttle-auth-label\" htmlFor={id}>{l.password}</label>\n <input className=\"throttle-auth-input\" id={id} type=\"password\" autoComplete=\"new-password\"\n value={password} onChange={(e) => setPassword(e.target.value)} required />\n\n {error && <p className=\"throttle-auth-error\" role=\"alert\">{error}</p>}\n {notice && <p className=\"throttle-auth-notice\" role=\"status\">{notice}</p>}\n\n <button className=\"throttle-auth-submit\" type=\"submit\" disabled={submitting}>\n {submitting ? '…' : l.submit}\n </button>\n </form>\n );\n}\n","import { useEffect, useRef, useState } from 'react';\nimport { useAuth } from '../context';\nimport { useFormAction } from './useFormAction';\n\nexport interface VerifyEmailPanelProps {\n /** Token from the emailed link. Omit to render the \"resend\" state instead. */\n token?: string;\n onVerified?: () => void;\n className?: string;\n labels?: Partial<{ verifying: string; verified: string; prompt: string; resend: string; resent: string }>;\n}\n\nexport function VerifyEmailPanel(props: VerifyEmailPanelProps) {\n const { auth, emailVerified } = useAuth();\n const { submitting, error, notice, run } = useFormAction();\n const attempted = useRef<string | null>(null);\n const [verifiedLocally, setVerifiedLocally] = useState(false);\n const l = {\n verifying: 'Verifying your email…',\n verified: 'Your email address is verified.',\n prompt: 'Please verify your email address to unlock your full account.',\n resend: 'Resend verification email',\n resent: 'Sent. Check your inbox.',\n ...props.labels,\n };\n\n useEffect(() => {\n if (!props.token || attempted.current === props.token) return;\n // Once per token. Re-running would burn the single-use token and report\n // `token_already_used` to a buyer who did nothing wrong.\n //\n // useFormAction's `inFlight` flag happens to block the StrictMode\n // double-invoke too, but that is a submit-debounce concern that belongs to\n // form actions generally. Burning a single-use token is this panel's own\n // invariant, and it should not depend on another layer's debounce\n // semantics staying exactly as they are today.\n attempted.current = props.token;\n void run(async () => {\n await auth.verifyEmail(props.token!);\n setVerifiedLocally(true);\n props.onVerified?.();\n });\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [props.token]);\n\n return (\n <div data-testid=\"throttle-auth-verify\" className={props.className ?? 'throttle-auth-panel'}>\n {props.token && submitting && <p className=\"throttle-auth-notice\" role=\"status\">{l.verifying}</p>}\n {(emailVerified || verifiedLocally) && (\n <p className=\"throttle-auth-notice\" role=\"status\">{l.verified}</p>\n )}\n {!emailVerified && !props.token && <p className=\"throttle-auth-prompt\">{l.prompt}</p>}\n\n {error && <p className=\"throttle-auth-error\" role=\"alert\">{error}</p>}\n {notice && <p className=\"throttle-auth-notice\" role=\"status\">{notice}</p>}\n\n {!emailVerified && !props.token && (\n <button\n className=\"throttle-auth-submit\" type=\"button\" disabled={submitting}\n onClick={() => void run(async () => { await auth.resendVerification(); return l.resent; })}\n >\n {l.resend}\n </button>\n )}\n </div>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACAA,IAAAA,gBAAgC;;;ACAhC,mBAA4F;AAyCnF;AAzBT,IAAM,UAAM,4BAAmC,IAAI;AA4BnD,SAAS,SAAuB;AAC9B,QAAM,UAAM,yBAAW,GAAG;AAC1B,MAAI,CAAC,IAAK,OAAM,IAAI,MAAM,sDAAsD;AAChF,SAAO;AACT;AAWO,SAAS,UAAyB;AACvC,QAAM,EAAE,MAAM,QAAQ,IAAI,OAAO;AACjC,QAAM,eAAW,mCAAqB,KAAK,WAAW,KAAK,aAAa,KAAK,WAAW;AACxF,SAAO;AAAA,IACL,GAAG;AAAA,IACH,WAAW,SAAS,WAAW;AAAA,IAC/B,iBAAiB,SAAS,WAAW;AAAA,IACrC;AAAA,IACA;AAAA,EACF;AACF;;;ACrEA,IAAAC,gBAA8C;;;ACE9C,SAAS,SAAS,SAA0B;AAC1C,MAAI,CAAC,WAAW,CAAC,OAAO,SAAS,OAAO,EAAG,QAAO;AAClD,MAAI,UAAU,GAAI,QAAO,eAAe,KAAK,KAAK,OAAO,CAAC;AAC1D,QAAM,UAAU,KAAK,KAAK,UAAU,EAAE;AACtC,SAAO,eAAe,OAAO,IAAI,YAAY,IAAI,WAAW,SAAS;AACvE;AAQO,SAAS,gBAAgB,KAAsB;AACpD,QAAM,IAAI;AACV,UAAQ,GAAG,MAAM;AAAA,IACf,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AAEH,aAAO,EAAE,WAAW;AAAA,IACtB,KAAK;AACH,aAAO,sBAAsB,SAAS,EAAE,UAAU,CAAC;AAAA,IACrD,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AAAA,IACL,KAAK;AAAA,IACL,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AAAA,IACL,KAAK;AAAA,IACL,KAAK;AAAA,IACL,KAAK;AAEH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AAGH,aAAO,EAAE,WAAW;AAAA,IACtB;AACE,aAAO;AAAA,EACX;AACF;;;AD3CO,SAAS,gBAA4B;AAC1C,QAAM,CAAC,YAAY,aAAa,QAAI,wBAAS,KAAK;AAClD,QAAM,CAAC,OAAO,QAAQ,QAAI,wBAAwB,IAAI;AACtD,QAAM,CAAC,QAAQ,SAAS,QAAI,wBAAwB,IAAI;AACxD,QAAM,eAAW,sBAAO,KAAK;AAE7B,QAAM,UAAM,2BAAY,OAAO,OAAqC;AAClE,QAAI,SAAS,QAAS;AACtB,aAAS,UAAU;AACnB,kBAAc,IAAI;AAClB,aAAS,IAAI;AACb,cAAU,IAAI;AACd,QAAI;AACF,YAAM,UAAU,MAAM,GAAG;AACzB,UAAI,OAAO,YAAY,SAAU,WAAU,OAAO;AAAA,IACpD,SAAS,KAAK;AACZ,eAAS,gBAAgB,GAAG,CAAC;AAAA,IAC/B,UAAE;AACA,eAAS,UAAU;AACnB,oBAAc,KAAK;AAAA,IACrB;AAAA,EACF,GAAG,CAAC,CAAC;AAEL,SAAO,EAAE,YAAY,OAAO,QAAQ,IAAI;AAC1C;;;AFfI,IAAAC,sBAAA;AATG,SAAS,WAAW,OAAwB;AACjD,QAAM,EAAE,KAAK,IAAI,QAAQ;AACzB,QAAM,EAAE,YAAY,OAAO,IAAI,IAAI,cAAc;AACjD,QAAM,CAAC,OAAO,QAAQ,QAAI,wBAAS,EAAE;AACrC,QAAM,CAAC,UAAU,WAAW,QAAI,wBAAS,EAAE;AAC3C,QAAM,MAAM,EAAE,OAAO,OAAG,qBAAM,CAAC,UAAU,UAAU,OAAG,qBAAM,CAAC,YAAY;AACzE,QAAM,IAAI,EAAE,OAAO,SAAS,UAAU,YAAY,QAAQ,WAAW,QAAQ,yBAAyB,GAAG,MAAM,OAAO;AAEtH,SACE;AAAA,IAAC;AAAA;AAAA,MACC,eAAY;AAAA,MACZ,WAAW,MAAM,aAAa;AAAA,MAC9B,UAAU,CAAC,MAAM;AACf,UAAE,eAAe;AACjB,aAAK,IAAI,YAAY;AAAE,gBAAM,KAAK,OAAO,OAAO,QAAQ;AAAG,gBAAM,YAAY;AAAA,QAAG,CAAC;AAAA,MACnF;AAAA,MAEA;AAAA,qDAAC,WAAM,WAAU,uBAAsB,SAAS,IAAI,OAAQ,YAAE,OAAM;AAAA,QACpE;AAAA,UAAC;AAAA;AAAA,YAAM,WAAU;AAAA,YAAsB,IAAI,IAAI;AAAA,YAAO,MAAK;AAAA,YAAQ,cAAa;AAAA,YACzE,OAAO;AAAA,YAAO,UAAU,CAAC,MAAM,SAAS,EAAE,OAAO,KAAK;AAAA,YAAG,UAAQ;AAAA;AAAA,QAAC;AAAA,QAEzE,6CAAC,WAAM,WAAU,uBAAsB,SAAS,IAAI,UAAW,YAAE,UAAS;AAAA,QAC1E;AAAA,UAAC;AAAA;AAAA,YAAM,WAAU;AAAA,YAAsB,IAAI,IAAI;AAAA,YAAU,MAAK;AAAA,YAAW,cAAa;AAAA,YAC/E,OAAO;AAAA,YAAU,UAAU,CAAC,MAAM,YAAY,EAAE,OAAO,KAAK;AAAA,YAAG,UAAQ;AAAA;AAAA,QAAC;AAAA,QAE9E,SAAS,6CAAC,OAAE,WAAU,uBAAsB,MAAK,SAAS,iBAAM;AAAA,QAEjE,6CAAC,YAAO,WAAU,wBAAuB,MAAK,UAAS,UAAU,YAC9D,uBAAa,WAAM,EAAE,QACxB;AAAA,QAEC,MAAM,oBACL,6CAAC,YAAO,WAAU,sBAAqB,MAAK,UAAS,SAAS,MAAM,kBAAmB,YAAE,QAAO;AAAA;AAAA;AAAA,EAEpG;AAEJ;;;AI/CA,IAAAC,gBAAgC;AAyB5B,IAAAC,sBAAA;AAbG,SAAS,WAAW,OAAwB;AACjD,QAAM,EAAE,KAAK,IAAI,QAAQ;AACzB,QAAM,EAAE,YAAY,OAAO,QAAQ,IAAI,IAAI,cAAc;AACzD,QAAM,CAAC,OAAO,QAAQ,QAAI,wBAAS,EAAE;AACrC,QAAM,CAAC,UAAU,WAAW,QAAI,wBAAS,EAAE;AAC3C,QAAM,MAAM,EAAE,OAAO,OAAG,qBAAM,CAAC,UAAU,UAAU,OAAG,qBAAM,CAAC,YAAY;AACzE,QAAM,IAAI;AAAA,IACR,OAAO;AAAA,IAAS,UAAU;AAAA,IAAY,QAAQ;AAAA,IAC9C,SAAS;AAAA,IACT,GAAG,MAAM;AAAA,EACX;AAEA,SACE;AAAA,IAAC;AAAA;AAAA,MACC,eAAY;AAAA,MACZ,WAAW,MAAM,aAAa;AAAA,MAC9B,UAAU,CAAC,MAAM;AACf,UAAE,eAAe;AACjB,aAAK,IAAI,YAAY;AACnB,gBAAM,SAAS,MAAM,KAAK,SAAS,OAAO,QAAQ;AAGlD,cAAI,OAAO,WAAW,WAAW;AAAE,kBAAM,YAAY;AAAG,mBAAO,EAAE;AAAA,UAAS;AAC1E,gBAAM,YAAY;AAAA,QACpB,CAAC;AAAA,MACH;AAAA,MAEA;AAAA,qDAAC,WAAM,WAAU,uBAAsB,SAAS,IAAI,OAAQ,YAAE,OAAM;AAAA,QACpE;AAAA,UAAC;AAAA;AAAA,YAAM,WAAU;AAAA,YAAsB,IAAI,IAAI;AAAA,YAAO,MAAK;AAAA,YAAQ,cAAa;AAAA,YACzE,OAAO;AAAA,YAAO,UAAU,CAAC,MAAM,SAAS,EAAE,OAAO,KAAK;AAAA,YAAG,UAAQ;AAAA;AAAA,QAAC;AAAA,QAEzE,6CAAC,WAAM,WAAU,uBAAsB,SAAS,IAAI,UAAW,YAAE,UAAS;AAAA,QAC1E;AAAA,UAAC;AAAA;AAAA,YAAM,WAAU;AAAA,YAAsB,IAAI,IAAI;AAAA,YAAU,MAAK;AAAA,YAAW,cAAa;AAAA,YAC/E,OAAO;AAAA,YAAU,UAAU,CAAC,MAAM,YAAY,EAAE,OAAO,KAAK;AAAA,YAAG,UAAQ;AAAA;AAAA,QAAC;AAAA,QAE9E,SAAS,6CAAC,OAAE,WAAU,uBAAsB,MAAK,SAAS,iBAAM;AAAA,QAChE,UAAU,6CAAC,OAAE,WAAU,wBAAuB,MAAK,UAAU,kBAAO;AAAA,QAErE,6CAAC,YAAO,WAAU,wBAAuB,MAAK,UAAS,UAAU,YAC9D,uBAAa,WAAM,EAAE,QACxB;AAAA;AAAA;AAAA,EACF;AAEJ;;;ACvDA,IAAAC,gBAAgC;AAwB5B,IAAAC,sBAAA;AAdG,SAAS,mBAAmB,OAAgC;AACjE,QAAM,EAAE,KAAK,IAAI,QAAQ;AACzB,QAAM,EAAE,YAAY,OAAO,QAAQ,IAAI,IAAI,cAAc;AACzD,QAAM,CAAC,OAAO,QAAQ,QAAI,wBAAS,EAAE;AACrC,QAAM,KAAK,OAAG,qBAAM,CAAC;AACrB,QAAM,IAAI;AAAA,IACR,OAAO;AAAA,IAAS,QAAQ;AAAA;AAAA;AAAA,IAGxB,MAAM;AAAA,IACN,GAAG,MAAM;AAAA,EACX;AAEA,SACE;AAAA,IAAC;AAAA;AAAA,MACC,eAAY;AAAA,MACZ,WAAW,MAAM,aAAa;AAAA,MAC9B,UAAU,CAAC,MAAM;AACf,UAAE,eAAe;AACjB,aAAK,IAAI,YAAY;AAAE,gBAAM,KAAK,eAAe,KAAK;AAAG,gBAAM,SAAS;AAAG,iBAAO,EAAE;AAAA,QAAM,CAAC;AAAA,MAC7F;AAAA,MAEA;AAAA,qDAAC,WAAM,WAAU,uBAAsB,SAAS,IAAK,YAAE,OAAM;AAAA,QAC7D;AAAA,UAAC;AAAA;AAAA,YAAM,WAAU;AAAA,YAAsB;AAAA,YAAQ,MAAK;AAAA,YAAQ,cAAa;AAAA,YAClE,OAAO;AAAA,YAAO,UAAU,CAAC,MAAM,SAAS,EAAE,OAAO,KAAK;AAAA,YAAG,UAAQ;AAAA;AAAA,QAAC;AAAA,QAExE,SAAS,6CAAC,OAAE,WAAU,uBAAsB,MAAK,SAAS,iBAAM;AAAA,QAChE,UAAU,6CAAC,OAAE,WAAU,wBAAuB,MAAK,UAAU,kBAAO;AAAA,QAErE,6CAAC,YAAO,WAAU,wBAAuB,MAAK,UAAS,UAAU,YAC9D,uBAAa,WAAM,EAAE,QACxB;AAAA;AAAA;AAAA,EACF;AAEJ;;;AC5CA,IAAAC,gBAAgC;AAwB5B,IAAAC,sBAAA;AAZG,SAAS,kBAAkB,OAA+B;AAC/D,QAAM,EAAE,KAAK,IAAI,QAAQ;AACzB,QAAM,EAAE,YAAY,OAAO,QAAQ,IAAI,IAAI,cAAc;AACzD,QAAM,CAAC,UAAU,WAAW,QAAI,wBAAS,EAAE;AAC3C,QAAM,KAAK,OAAG,qBAAM,CAAC;AACrB,QAAM,IAAI;AAAA,IACR,UAAU;AAAA,IAAgB,QAAQ;AAAA,IAClC,MAAM;AAAA,IACN,GAAG,MAAM;AAAA,EACX;AAEA,SACE;AAAA,IAAC;AAAA;AAAA,MACC,eAAY;AAAA,MACZ,WAAW,MAAM,aAAa;AAAA,MAC9B,UAAU,CAAC,MAAM;AACf,UAAE,eAAe;AACjB,aAAK,IAAI,YAAY;AACnB,gBAAM,KAAK,cAAc,MAAM,OAAO,QAAQ;AAC9C,gBAAM,YAAY;AAGlB,iBAAO,EAAE;AAAA,QACX,CAAC;AAAA,MACH;AAAA,MAEA;AAAA,qDAAC,WAAM,WAAU,uBAAsB,SAAS,IAAK,YAAE,UAAS;AAAA,QAChE;AAAA,UAAC;AAAA;AAAA,YAAM,WAAU;AAAA,YAAsB;AAAA,YAAQ,MAAK;AAAA,YAAW,cAAa;AAAA,YACrE,OAAO;AAAA,YAAU,UAAU,CAAC,MAAM,YAAY,EAAE,OAAO,KAAK;AAAA,YAAG,UAAQ;AAAA;AAAA,QAAC;AAAA,QAE9E,SAAS,6CAAC,OAAE,WAAU,uBAAsB,MAAK,SAAS,iBAAM;AAAA,QAChE,UAAU,6CAAC,OAAE,WAAU,wBAAuB,MAAK,UAAU,kBAAO;AAAA,QAErE,6CAAC,YAAO,WAAU,wBAAuB,MAAK,UAAS,UAAU,YAC9D,uBAAa,WAAM,EAAE,QACxB;AAAA;AAAA;AAAA,EACF;AAEJ;;;AClDA,IAAAC,gBAA4C;AA8CxC,IAAAC,sBAAA;AAlCG,SAAS,iBAAiB,OAA8B;AAC7D,QAAM,EAAE,MAAM,cAAc,IAAI,QAAQ;AACxC,QAAM,EAAE,YAAY,OAAO,QAAQ,IAAI,IAAI,cAAc;AACzD,QAAM,gBAAY,sBAAsB,IAAI;AAC5C,QAAM,CAAC,iBAAiB,kBAAkB,QAAI,wBAAS,KAAK;AAC5D,QAAM,IAAI;AAAA,IACR,WAAW;AAAA,IACX,UAAU;AAAA,IACV,QAAQ;AAAA,IACR,QAAQ;AAAA,IACR,QAAQ;AAAA,IACR,GAAG,MAAM;AAAA,EACX;AAEA,+BAAU,MAAM;AACd,QAAI,CAAC,MAAM,SAAS,UAAU,YAAY,MAAM,MAAO;AASvD,cAAU,UAAU,MAAM;AAC1B,SAAK,IAAI,YAAY;AACnB,YAAM,KAAK,YAAY,MAAM,KAAM;AACnC,yBAAmB,IAAI;AACvB,YAAM,aAAa;AAAA,IACrB,CAAC;AAAA,EAEH,GAAG,CAAC,MAAM,KAAK,CAAC;AAEhB,SACE,8CAAC,SAAI,eAAY,wBAAuB,WAAW,MAAM,aAAa,uBACnE;AAAA,UAAM,SAAS,cAAc,6CAAC,OAAE,WAAU,wBAAuB,MAAK,UAAU,YAAE,WAAU;AAAA,KAC3F,iBAAiB,oBACjB,6CAAC,OAAE,WAAU,wBAAuB,MAAK,UAAU,YAAE,UAAS;AAAA,IAE/D,CAAC,iBAAiB,CAAC,MAAM,SAAS,6CAAC,OAAE,WAAU,wBAAwB,YAAE,QAAO;AAAA,IAEhF,SAAS,6CAAC,OAAE,WAAU,uBAAsB,MAAK,SAAS,iBAAM;AAAA,IAChE,UAAU,6CAAC,OAAE,WAAU,wBAAuB,MAAK,UAAU,kBAAO;AAAA,IAEpE,CAAC,iBAAiB,CAAC,MAAM,SACxB;AAAA,MAAC;AAAA;AAAA,QACC,WAAU;AAAA,QAAuB,MAAK;AAAA,QAAS,UAAU;AAAA,QACzD,SAAS,MAAM,KAAK,IAAI,YAAY;AAAE,gBAAM,KAAK,mBAAmB;AAAG,iBAAO,EAAE;AAAA,QAAQ,CAAC;AAAA,QAExF,YAAE;AAAA;AAAA,IACL;AAAA,KAEJ;AAEJ;","names":["import_react","import_react","import_jsx_runtime","import_react","import_jsx_runtime","import_react","import_jsx_runtime","import_react","import_jsx_runtime","import_react","import_jsx_runtime"]}
@@ -1,57 +1,16 @@
1
+ "use client";
2
+ import {
3
+ messageForError
4
+ } from "../../chunk-AIXBUDOT.js";
1
5
  import {
2
6
  useAuth
3
- } from "../../chunk-IJ3DNMNG.js";
4
- import "../../chunk-OYNGTNOS.js";
7
+ } from "../../chunk-SZ427BQ3.js";
5
8
 
6
9
  // src/react/forms/SignIn.tsx
7
10
  import { useId, useState as useState2 } from "react";
8
11
 
9
12
  // src/react/forms/useFormAction.ts
10
13
  import { useCallback, useRef, useState } from "react";
11
-
12
- // src/react/forms/messages.ts
13
- function waitCopy(seconds) {
14
- if (!seconds || !Number.isFinite(seconds)) return "Please wait a moment and try again.";
15
- if (seconds < 60) return `Please wait ${Math.ceil(seconds)} seconds and try again.`;
16
- const minutes = Math.ceil(seconds / 60);
17
- return `Please wait ${minutes} ${minutes === 1 ? "minute" : "minutes"} and try again.`;
18
- }
19
- function messageForError(err) {
20
- const e = err;
21
- switch (e?.code) {
22
- case "invalid_credentials":
23
- return "That email or password is not correct.";
24
- case "weak_password":
25
- return e.message || "Please choose a stronger password.";
26
- case "too_many_attempts":
27
- return `Too many attempts. ${waitCopy(e.retryAfter)}`;
28
- case "verification_required":
29
- return "Please verify your email address to continue.";
30
- case "step_up_required":
31
- return "Please re-enter your password to continue.";
32
- case "session_revoked":
33
- return "Your session has ended. Please sign in again.";
34
- case "token_expired":
35
- case "token_invalid":
36
- case "token_already_used":
37
- return "That link is no longer valid. Please request a new link.";
38
- case "auth_unavailable":
39
- return "Accounts are briefly unavailable. Please try again shortly.";
40
- case "origin_not_allowed":
41
- case "auth_not_enabled":
42
- case "invalid_api_key":
43
- case "environment_mismatch":
44
- return "Accounts are not available right now. Please contact support.";
45
- case "cart_already_claimed":
46
- return "That cart belongs to a different account.";
47
- case "not_found":
48
- return "We could not find that.";
49
- default:
50
- return "Something went wrong. Please try again.";
51
- }
52
- }
53
-
54
- // src/react/forms/useFormAction.ts
55
14
  function useFormAction() {
56
15
  const [submitting, setSubmitting] = useState(false);
57
16
  const [error, setError] = useState(null);
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/react/forms/SignIn.tsx","../../../src/react/forms/useFormAction.ts","../../../src/react/forms/messages.ts","../../../src/react/forms/SignUp.tsx","../../../src/react/forms/ForgotPassword.tsx","../../../src/react/forms/ResetPassword.tsx","../../../src/react/forms/VerifyEmail.tsx"],"sourcesContent":["import { useId, useState } from 'react';\nimport { useAuth } from '../context';\nimport { useFormAction } from './useFormAction';\n\nexport interface SignInFormProps {\n onSuccess?: () => void;\n onForgotPassword?: () => void;\n className?: string;\n labels?: Partial<{ email: string; password: string; submit: string; forgot: string }>;\n}\n\nexport function SignInForm(props: SignInFormProps) {\n const { auth } = useAuth();\n const { submitting, error, run } = useFormAction();\n const [email, setEmail] = useState('');\n const [password, setPassword] = useState('');\n const ids = { email: `${useId()}-email`, password: `${useId()}-password` };\n const l = { email: 'Email', password: 'Password', submit: 'Sign in', forgot: 'Forgot your password?', ...props.labels };\n\n return (\n <form\n data-testid=\"throttle-auth-signin\"\n className={props.className ?? 'throttle-auth-form'}\n onSubmit={(e) => {\n e.preventDefault();\n void run(async () => { await auth.signIn(email, password); props.onSuccess?.(); });\n }}\n >\n <label className=\"throttle-auth-label\" htmlFor={ids.email}>{l.email}</label>\n <input className=\"throttle-auth-input\" id={ids.email} type=\"email\" autoComplete=\"email\"\n value={email} onChange={(e) => setEmail(e.target.value)} required />\n\n <label className=\"throttle-auth-label\" htmlFor={ids.password}>{l.password}</label>\n <input className=\"throttle-auth-input\" id={ids.password} type=\"password\" autoComplete=\"current-password\"\n value={password} onChange={(e) => setPassword(e.target.value)} required />\n\n {error && <p className=\"throttle-auth-error\" role=\"alert\">{error}</p>}\n\n <button className=\"throttle-auth-submit\" type=\"submit\" disabled={submitting}>\n {submitting ? '…' : l.submit}\n </button>\n\n {props.onForgotPassword && (\n <button className=\"throttle-auth-link\" type=\"button\" onClick={props.onForgotPassword}>{l.forgot}</button>\n )}\n </form>\n );\n}\n","import { useCallback, useRef, useState } from 'react';\nimport { messageForError } from './messages';\n\nexport interface FormAction {\n submitting: boolean;\n error: string | null;\n notice: string | null;\n run(fn: () => Promise<string | void>): Promise<void>;\n}\n\n/** Submit-once semantics plus mapped error copy, shared by all five forms. */\nexport function useFormAction(): FormAction {\n const [submitting, setSubmitting] = useState(false);\n const [error, setError] = useState<string | null>(null);\n const [notice, setNotice] = useState<string | null>(null);\n const inFlight = useRef(false);\n\n const run = useCallback(async (fn: () => Promise<string | void>) => {\n if (inFlight.current) return; // a double-click is one attempt, not two\n inFlight.current = true;\n setSubmitting(true);\n setError(null);\n setNotice(null);\n try {\n const message = await fn();\n if (typeof message === 'string') setNotice(message);\n } catch (err) {\n setError(messageForError(err));\n } finally {\n inFlight.current = false;\n setSubmitting(false);\n }\n }, []);\n\n return { submitting, error, notice, run };\n}\n","import { ThrottleAuthError } from '../../errors';\n\nfunction waitCopy(seconds?: number): string {\n if (!seconds || !Number.isFinite(seconds)) return 'Please wait a moment and try again.';\n if (seconds < 60) return `Please wait ${Math.ceil(seconds)} seconds and try again.`;\n const minutes = Math.ceil(seconds / 60);\n return `Please wait ${minutes} ${minutes === 1 ? 'minute' : 'minutes'} and try again.`;\n}\n\n/**\n * Buyer-facing copy for every code the storefront API returns. Two codes are\n * deliberately NOT passed through from the server: `invalid_credentials` (the\n * server is vague on purpose — enumerating accounts through our own UI would\n * undo that) and anything unrecognised.\n */\nexport function messageForError(err: unknown): string {\n const e = err as Partial<ThrottleAuthError> & { code?: string };\n switch (e?.code) {\n case 'invalid_credentials':\n return 'That email or password is not correct.';\n case 'weak_password':\n // The server's message IS the policy reason (\"Use at least 12 characters\").\n return e.message || 'Please choose a stronger password.';\n case 'too_many_attempts':\n return `Too many attempts. ${waitCopy(e.retryAfter)}`;\n case 'verification_required':\n return 'Please verify your email address to continue.';\n case 'step_up_required':\n return 'Please re-enter your password to continue.';\n case 'session_revoked':\n return 'Your session has ended. Please sign in again.';\n case 'token_expired':\n case 'token_invalid':\n case 'token_already_used':\n return 'That link is no longer valid. Please request a new link.';\n case 'auth_unavailable':\n return 'Accounts are briefly unavailable. Please try again shortly.';\n case 'origin_not_allowed':\n case 'auth_not_enabled':\n case 'invalid_api_key':\n case 'environment_mismatch':\n // Merchant misconfiguration, not something the buyer can fix.\n return 'Accounts are not available right now. Please contact support.';\n case 'cart_already_claimed':\n return 'That cart belongs to a different account.';\n case 'not_found':\n return 'We could not find that.';\n default:\n return 'Something went wrong. Please try again.';\n }\n}\n","import { useId, useState } from 'react';\nimport { useAuth } from '../context';\nimport { useFormAction } from './useFormAction';\n\nexport interface SignUpFormProps {\n onSuccess?: () => void;\n /** Called for the 202 shape: the address already has credentials, so we sent an email instead. */\n onPending?: () => void;\n className?: string;\n labels?: Partial<{ email: string; password: string; submit: string; pending: string }>;\n}\n\nexport function SignUpForm(props: SignUpFormProps) {\n const { auth } = useAuth();\n const { submitting, error, notice, run } = useFormAction();\n const [email, setEmail] = useState('');\n const [password, setPassword] = useState('');\n const ids = { email: `${useId()}-email`, password: `${useId()}-password` };\n const l = {\n email: 'Email', password: 'Password', submit: 'Create account',\n pending: 'Check your email — we sent you a link to finish signing in.',\n ...props.labels,\n };\n\n return (\n <form\n data-testid=\"throttle-auth-signup\"\n className={props.className ?? 'throttle-auth-form'}\n onSubmit={(e) => {\n e.preventDefault();\n void run(async () => {\n const result = await auth.register(email, password);\n // 202: an account already exists for this address. There is no\n // session — say so rather than routing to a signed-in page.\n if (result.status === 'pending') { props.onPending?.(); return l.pending; }\n props.onSuccess?.();\n });\n }}\n >\n <label className=\"throttle-auth-label\" htmlFor={ids.email}>{l.email}</label>\n <input className=\"throttle-auth-input\" id={ids.email} type=\"email\" autoComplete=\"email\"\n value={email} onChange={(e) => setEmail(e.target.value)} required />\n\n <label className=\"throttle-auth-label\" htmlFor={ids.password}>{l.password}</label>\n <input className=\"throttle-auth-input\" id={ids.password} type=\"password\" autoComplete=\"new-password\"\n value={password} onChange={(e) => setPassword(e.target.value)} required />\n\n {error && <p className=\"throttle-auth-error\" role=\"alert\">{error}</p>}\n {notice && <p className=\"throttle-auth-notice\" role=\"status\">{notice}</p>}\n\n <button className=\"throttle-auth-submit\" type=\"submit\" disabled={submitting}>\n {submitting ? '…' : l.submit}\n </button>\n </form>\n );\n}\n","import { useId, useState } from 'react';\nimport { useAuth } from '../context';\nimport { useFormAction } from './useFormAction';\n\nexport interface ForgotPasswordFormProps {\n onSent?: () => void;\n className?: string;\n labels?: Partial<{ email: string; submit: string; sent: string }>;\n}\n\nexport function ForgotPasswordForm(props: ForgotPasswordFormProps) {\n const { auth } = useAuth();\n const { submitting, error, notice, run } = useFormAction();\n const [email, setEmail] = useState('');\n const id = `${useId()}-email`;\n const l = {\n email: 'Email', submit: 'Send reset link',\n // Identical copy whether or not the address has an account — the API is\n // deliberately silent on that and this form must be too.\n sent: 'If an account exists for that address, we have sent a reset link.',\n ...props.labels,\n };\n\n return (\n <form\n data-testid=\"throttle-auth-forgot\"\n className={props.className ?? 'throttle-auth-form'}\n onSubmit={(e) => {\n e.preventDefault();\n void run(async () => { await auth.forgotPassword(email); props.onSent?.(); return l.sent; });\n }}\n >\n <label className=\"throttle-auth-label\" htmlFor={id}>{l.email}</label>\n <input className=\"throttle-auth-input\" id={id} type=\"email\" autoComplete=\"email\"\n value={email} onChange={(e) => setEmail(e.target.value)} required />\n\n {error && <p className=\"throttle-auth-error\" role=\"alert\">{error}</p>}\n {notice && <p className=\"throttle-auth-notice\" role=\"status\">{notice}</p>}\n\n <button className=\"throttle-auth-submit\" type=\"submit\" disabled={submitting}>\n {submitting ? '…' : l.submit}\n </button>\n </form>\n );\n}\n","import { useId, useState } from 'react';\nimport { useAuth } from '../context';\nimport { useFormAction } from './useFormAction';\n\nexport interface ResetPasswordFormProps {\n /** The token from the emailed link, e.g. `new URLSearchParams(location.search).get('token')`. */\n token: string;\n onSuccess?: () => void;\n className?: string;\n labels?: Partial<{ password: string; submit: string; done: string }>;\n}\n\nexport function ResetPasswordForm(props: ResetPasswordFormProps) {\n const { auth } = useAuth();\n const { submitting, error, notice, run } = useFormAction();\n const [password, setPassword] = useState('');\n const id = `${useId()}-new-password`;\n const l = {\n password: 'New password', submit: 'Set new password',\n done: 'Your password has been reset. Please sign in with your new password.',\n ...props.labels,\n };\n\n return (\n <form\n data-testid=\"throttle-auth-reset\"\n className={props.className ?? 'throttle-auth-form'}\n onSubmit={(e) => {\n e.preventDefault();\n void run(async () => {\n await auth.resetPassword(props.token, password);\n props.onSuccess?.();\n // Every session died with the reset, this one included — the buyer\n // signs in again rather than landing on an account page.\n return l.done;\n });\n }}\n >\n <label className=\"throttle-auth-label\" htmlFor={id}>{l.password}</label>\n <input className=\"throttle-auth-input\" id={id} type=\"password\" autoComplete=\"new-password\"\n value={password} onChange={(e) => setPassword(e.target.value)} required />\n\n {error && <p className=\"throttle-auth-error\" role=\"alert\">{error}</p>}\n {notice && <p className=\"throttle-auth-notice\" role=\"status\">{notice}</p>}\n\n <button className=\"throttle-auth-submit\" type=\"submit\" disabled={submitting}>\n {submitting ? '…' : l.submit}\n </button>\n </form>\n );\n}\n","import { useEffect, useRef, useState } from 'react';\nimport { useAuth } from '../context';\nimport { useFormAction } from './useFormAction';\n\nexport interface VerifyEmailPanelProps {\n /** Token from the emailed link. Omit to render the \"resend\" state instead. */\n token?: string;\n onVerified?: () => void;\n className?: string;\n labels?: Partial<{ verifying: string; verified: string; prompt: string; resend: string; resent: string }>;\n}\n\nexport function VerifyEmailPanel(props: VerifyEmailPanelProps) {\n const { auth, emailVerified } = useAuth();\n const { submitting, error, notice, run } = useFormAction();\n const attempted = useRef<string | null>(null);\n const [verifiedLocally, setVerifiedLocally] = useState(false);\n const l = {\n verifying: 'Verifying your email…',\n verified: 'Your email address is verified.',\n prompt: 'Please verify your email address to unlock your full account.',\n resend: 'Resend verification email',\n resent: 'Sent. Check your inbox.',\n ...props.labels,\n };\n\n useEffect(() => {\n if (!props.token || attempted.current === props.token) return;\n // Once per token. Re-running would burn the single-use token and report\n // `token_already_used` to a buyer who did nothing wrong.\n //\n // useFormAction's `inFlight` flag happens to block the StrictMode\n // double-invoke too, but that is a submit-debounce concern that belongs to\n // form actions generally. Burning a single-use token is this panel's own\n // invariant, and it should not depend on another layer's debounce\n // semantics staying exactly as they are today.\n attempted.current = props.token;\n void run(async () => {\n await auth.verifyEmail(props.token!);\n setVerifiedLocally(true);\n props.onVerified?.();\n });\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [props.token]);\n\n return (\n <div data-testid=\"throttle-auth-verify\" className={props.className ?? 'throttle-auth-panel'}>\n {props.token && submitting && <p className=\"throttle-auth-notice\" role=\"status\">{l.verifying}</p>}\n {(emailVerified || verifiedLocally) && (\n <p className=\"throttle-auth-notice\" role=\"status\">{l.verified}</p>\n )}\n {!emailVerified && !props.token && <p className=\"throttle-auth-prompt\">{l.prompt}</p>}\n\n {error && <p className=\"throttle-auth-error\" role=\"alert\">{error}</p>}\n {notice && <p className=\"throttle-auth-notice\" role=\"status\">{notice}</p>}\n\n {!emailVerified && !props.token && (\n <button\n className=\"throttle-auth-submit\" type=\"button\" disabled={submitting}\n onClick={() => void run(async () => { await auth.resendVerification(); return l.resent; })}\n >\n {l.resend}\n </button>\n )}\n </div>\n );\n}\n"],"mappings":"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","names":["useState","useState","useId","useState","jsx","jsxs","useState","useId","useId","useState","jsx","jsxs","useState","useId","useId","useState","jsx","jsxs","useState","useId","useRef","useState","jsx","jsxs","useRef","useState"]}
1
+ {"version":3,"sources":["../../../src/react/forms/SignIn.tsx","../../../src/react/forms/useFormAction.ts","../../../src/react/forms/SignUp.tsx","../../../src/react/forms/ForgotPassword.tsx","../../../src/react/forms/ResetPassword.tsx","../../../src/react/forms/VerifyEmail.tsx"],"sourcesContent":["import { useId, useState } from 'react';\nimport { useAuth } from '../context';\nimport { useFormAction } from './useFormAction';\n\nexport interface SignInFormProps {\n onSuccess?: () => void;\n onForgotPassword?: () => void;\n className?: string;\n labels?: Partial<{ email: string; password: string; submit: string; forgot: string }>;\n}\n\nexport function SignInForm(props: SignInFormProps) {\n const { auth } = useAuth();\n const { submitting, error, run } = useFormAction();\n const [email, setEmail] = useState('');\n const [password, setPassword] = useState('');\n const ids = { email: `${useId()}-email`, password: `${useId()}-password` };\n const l = { email: 'Email', password: 'Password', submit: 'Sign in', forgot: 'Forgot your password?', ...props.labels };\n\n return (\n <form\n data-testid=\"throttle-auth-signin\"\n className={props.className ?? 'throttle-auth-form'}\n onSubmit={(e) => {\n e.preventDefault();\n void run(async () => { await auth.signIn(email, password); props.onSuccess?.(); });\n }}\n >\n <label className=\"throttle-auth-label\" htmlFor={ids.email}>{l.email}</label>\n <input className=\"throttle-auth-input\" id={ids.email} type=\"email\" autoComplete=\"email\"\n value={email} onChange={(e) => setEmail(e.target.value)} required />\n\n <label className=\"throttle-auth-label\" htmlFor={ids.password}>{l.password}</label>\n <input className=\"throttle-auth-input\" id={ids.password} type=\"password\" autoComplete=\"current-password\"\n value={password} onChange={(e) => setPassword(e.target.value)} required />\n\n {error && <p className=\"throttle-auth-error\" role=\"alert\">{error}</p>}\n\n <button className=\"throttle-auth-submit\" type=\"submit\" disabled={submitting}>\n {submitting ? '…' : l.submit}\n </button>\n\n {props.onForgotPassword && (\n <button className=\"throttle-auth-link\" type=\"button\" onClick={props.onForgotPassword}>{l.forgot}</button>\n )}\n </form>\n );\n}\n","import { useCallback, useRef, useState } from 'react';\nimport { messageForError } from './messages';\n\nexport interface FormAction {\n submitting: boolean;\n error: string | null;\n notice: string | null;\n run(fn: () => Promise<string | void>): Promise<void>;\n}\n\n/** Submit-once semantics plus mapped error copy, shared by all five forms. */\nexport function useFormAction(): FormAction {\n const [submitting, setSubmitting] = useState(false);\n const [error, setError] = useState<string | null>(null);\n const [notice, setNotice] = useState<string | null>(null);\n const inFlight = useRef(false);\n\n const run = useCallback(async (fn: () => Promise<string | void>) => {\n if (inFlight.current) return; // a double-click is one attempt, not two\n inFlight.current = true;\n setSubmitting(true);\n setError(null);\n setNotice(null);\n try {\n const message = await fn();\n if (typeof message === 'string') setNotice(message);\n } catch (err) {\n setError(messageForError(err));\n } finally {\n inFlight.current = false;\n setSubmitting(false);\n }\n }, []);\n\n return { submitting, error, notice, run };\n}\n","import { useId, useState } from 'react';\nimport { useAuth } from '../context';\nimport { useFormAction } from './useFormAction';\n\nexport interface SignUpFormProps {\n onSuccess?: () => void;\n /** Called for the 202 shape: the address already has credentials, so we sent an email instead. */\n onPending?: () => void;\n className?: string;\n labels?: Partial<{ email: string; password: string; submit: string; pending: string }>;\n}\n\nexport function SignUpForm(props: SignUpFormProps) {\n const { auth } = useAuth();\n const { submitting, error, notice, run } = useFormAction();\n const [email, setEmail] = useState('');\n const [password, setPassword] = useState('');\n const ids = { email: `${useId()}-email`, password: `${useId()}-password` };\n const l = {\n email: 'Email', password: 'Password', submit: 'Create account',\n pending: 'Check your email — we sent you a link to finish signing in.',\n ...props.labels,\n };\n\n return (\n <form\n data-testid=\"throttle-auth-signup\"\n className={props.className ?? 'throttle-auth-form'}\n onSubmit={(e) => {\n e.preventDefault();\n void run(async () => {\n const result = await auth.register(email, password);\n // 202: an account already exists for this address. There is no\n // session — say so rather than routing to a signed-in page.\n if (result.status === 'pending') { props.onPending?.(); return l.pending; }\n props.onSuccess?.();\n });\n }}\n >\n <label className=\"throttle-auth-label\" htmlFor={ids.email}>{l.email}</label>\n <input className=\"throttle-auth-input\" id={ids.email} type=\"email\" autoComplete=\"email\"\n value={email} onChange={(e) => setEmail(e.target.value)} required />\n\n <label className=\"throttle-auth-label\" htmlFor={ids.password}>{l.password}</label>\n <input className=\"throttle-auth-input\" id={ids.password} type=\"password\" autoComplete=\"new-password\"\n value={password} onChange={(e) => setPassword(e.target.value)} required />\n\n {error && <p className=\"throttle-auth-error\" role=\"alert\">{error}</p>}\n {notice && <p className=\"throttle-auth-notice\" role=\"status\">{notice}</p>}\n\n <button className=\"throttle-auth-submit\" type=\"submit\" disabled={submitting}>\n {submitting ? '…' : l.submit}\n </button>\n </form>\n );\n}\n","import { useId, useState } from 'react';\nimport { useAuth } from '../context';\nimport { useFormAction } from './useFormAction';\n\nexport interface ForgotPasswordFormProps {\n onSent?: () => void;\n className?: string;\n labels?: Partial<{ email: string; submit: string; sent: string }>;\n}\n\nexport function ForgotPasswordForm(props: ForgotPasswordFormProps) {\n const { auth } = useAuth();\n const { submitting, error, notice, run } = useFormAction();\n const [email, setEmail] = useState('');\n const id = `${useId()}-email`;\n const l = {\n email: 'Email', submit: 'Send reset link',\n // Identical copy whether or not the address has an account — the API is\n // deliberately silent on that and this form must be too.\n sent: 'If an account exists for that address, we have sent a reset link.',\n ...props.labels,\n };\n\n return (\n <form\n data-testid=\"throttle-auth-forgot\"\n className={props.className ?? 'throttle-auth-form'}\n onSubmit={(e) => {\n e.preventDefault();\n void run(async () => { await auth.forgotPassword(email); props.onSent?.(); return l.sent; });\n }}\n >\n <label className=\"throttle-auth-label\" htmlFor={id}>{l.email}</label>\n <input className=\"throttle-auth-input\" id={id} type=\"email\" autoComplete=\"email\"\n value={email} onChange={(e) => setEmail(e.target.value)} required />\n\n {error && <p className=\"throttle-auth-error\" role=\"alert\">{error}</p>}\n {notice && <p className=\"throttle-auth-notice\" role=\"status\">{notice}</p>}\n\n <button className=\"throttle-auth-submit\" type=\"submit\" disabled={submitting}>\n {submitting ? '…' : l.submit}\n </button>\n </form>\n );\n}\n","import { useId, useState } from 'react';\nimport { useAuth } from '../context';\nimport { useFormAction } from './useFormAction';\n\nexport interface ResetPasswordFormProps {\n /** The token from the emailed link, e.g. `new URLSearchParams(location.search).get('token')`. */\n token: string;\n onSuccess?: () => void;\n className?: string;\n labels?: Partial<{ password: string; submit: string; done: string }>;\n}\n\nexport function ResetPasswordForm(props: ResetPasswordFormProps) {\n const { auth } = useAuth();\n const { submitting, error, notice, run } = useFormAction();\n const [password, setPassword] = useState('');\n const id = `${useId()}-new-password`;\n const l = {\n password: 'New password', submit: 'Set new password',\n done: 'Your password has been reset. Please sign in with your new password.',\n ...props.labels,\n };\n\n return (\n <form\n data-testid=\"throttle-auth-reset\"\n className={props.className ?? 'throttle-auth-form'}\n onSubmit={(e) => {\n e.preventDefault();\n void run(async () => {\n await auth.resetPassword(props.token, password);\n props.onSuccess?.();\n // Every session died with the reset, this one included — the buyer\n // signs in again rather than landing on an account page.\n return l.done;\n });\n }}\n >\n <label className=\"throttle-auth-label\" htmlFor={id}>{l.password}</label>\n <input className=\"throttle-auth-input\" id={id} type=\"password\" autoComplete=\"new-password\"\n value={password} onChange={(e) => setPassword(e.target.value)} required />\n\n {error && <p className=\"throttle-auth-error\" role=\"alert\">{error}</p>}\n {notice && <p className=\"throttle-auth-notice\" role=\"status\">{notice}</p>}\n\n <button className=\"throttle-auth-submit\" type=\"submit\" disabled={submitting}>\n {submitting ? '…' : l.submit}\n </button>\n </form>\n );\n}\n","import { useEffect, useRef, useState } from 'react';\nimport { useAuth } from '../context';\nimport { useFormAction } from './useFormAction';\n\nexport interface VerifyEmailPanelProps {\n /** Token from the emailed link. Omit to render the \"resend\" state instead. */\n token?: string;\n onVerified?: () => void;\n className?: string;\n labels?: Partial<{ verifying: string; verified: string; prompt: string; resend: string; resent: string }>;\n}\n\nexport function VerifyEmailPanel(props: VerifyEmailPanelProps) {\n const { auth, emailVerified } = useAuth();\n const { submitting, error, notice, run } = useFormAction();\n const attempted = useRef<string | null>(null);\n const [verifiedLocally, setVerifiedLocally] = useState(false);\n const l = {\n verifying: 'Verifying your email…',\n verified: 'Your email address is verified.',\n prompt: 'Please verify your email address to unlock your full account.',\n resend: 'Resend verification email',\n resent: 'Sent. Check your inbox.',\n ...props.labels,\n };\n\n useEffect(() => {\n if (!props.token || attempted.current === props.token) return;\n // Once per token. Re-running would burn the single-use token and report\n // `token_already_used` to a buyer who did nothing wrong.\n //\n // useFormAction's `inFlight` flag happens to block the StrictMode\n // double-invoke too, but that is a submit-debounce concern that belongs to\n // form actions generally. Burning a single-use token is this panel's own\n // invariant, and it should not depend on another layer's debounce\n // semantics staying exactly as they are today.\n attempted.current = props.token;\n void run(async () => {\n await auth.verifyEmail(props.token!);\n setVerifiedLocally(true);\n props.onVerified?.();\n });\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [props.token]);\n\n return (\n <div data-testid=\"throttle-auth-verify\" className={props.className ?? 'throttle-auth-panel'}>\n {props.token && submitting && <p className=\"throttle-auth-notice\" role=\"status\">{l.verifying}</p>}\n {(emailVerified || verifiedLocally) && (\n <p className=\"throttle-auth-notice\" role=\"status\">{l.verified}</p>\n )}\n {!emailVerified && !props.token && <p className=\"throttle-auth-prompt\">{l.prompt}</p>}\n\n {error && <p className=\"throttle-auth-error\" role=\"alert\">{error}</p>}\n {notice && <p className=\"throttle-auth-notice\" role=\"status\">{notice}</p>}\n\n {!emailVerified && !props.token && (\n <button\n className=\"throttle-auth-submit\" type=\"button\" disabled={submitting}\n onClick={() => void run(async () => { await auth.resendVerification(); return l.resent; })}\n >\n {l.resend}\n </button>\n )}\n </div>\n );\n}\n"],"mappings":"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","names":["useState","useState","useId","useState","jsx","jsxs","useState","useId","useId","useState","jsx","jsxs","useState","useId","useId","useState","jsx","jsxs","useState","useId","useRef","useState","jsx","jsxs","useRef","useState"]}
@@ -1,3 +1,4 @@
1
+ "use client";
1
2
  "use strict";
2
3
  var __defProp = Object.defineProperty;
3
4
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
@@ -30,7 +31,9 @@ __export(react_exports, {
30
31
  useAuth: () => useAuth,
31
32
  useAuthContext: () => useAuthContext,
32
33
  useCustomer: () => useCustomer,
34
+ useInvoice: () => useInvoice,
33
35
  useInvoices: () => useInvoices,
36
+ useOrder: () => useOrder,
34
37
  useOrders: () => useOrders,
35
38
  useSavedCards: () => useSavedCards,
36
39
  useSubscriptions: () => useSubscriptions
@@ -226,7 +229,8 @@ function createThrottleAuth(config) {
226
229
  const data = await request({ ...base, method: "POST", path: "/auth/refresh", body: { refreshToken } });
227
230
  await persist(data);
228
231
  } catch (err) {
229
- await forget();
232
+ const refused = err instanceof ThrottleAuthError && (err.statusCode === 401 || err.statusCode === 403);
233
+ if (refused) await forget();
230
234
  throw err;
231
235
  }
232
236
  });
@@ -272,6 +276,7 @@ function createThrottleAuth(config) {
272
276
  async function call(method, path, body) {
273
277
  let retriedAuth = false;
274
278
  let retriedStepUp = false;
279
+ let sendStepUp = false;
275
280
  for (; ; ) {
276
281
  const token = await accessToken();
277
282
  try {
@@ -281,7 +286,7 @@ function createThrottleAuth(config) {
281
286
  path,
282
287
  body,
283
288
  accessToken: token ?? void 0,
284
- stepUpToken: stepUpToken ?? void 0
289
+ stepUpToken: sendStepUp ? stepUpToken ?? void 0 : void 0
285
290
  });
286
291
  } catch (err) {
287
292
  const e = err;
@@ -293,8 +298,10 @@ function createThrottleAuth(config) {
293
298
  }
294
299
  if (e.code === "step_up_required" && !retriedStepUp) {
295
300
  retriedStepUp = true;
296
- stepUpToken = null;
297
- if (await ensureStepUp()) continue;
301
+ if (await ensureStepUp()) {
302
+ sendStepUp = true;
303
+ continue;
304
+ }
298
305
  }
299
306
  throw err;
300
307
  }
@@ -404,6 +411,7 @@ function createAccount(auth) {
404
411
  getOrder: (id) => call("GET", `/me/orders/${seg(id)}`),
405
412
  listInvoices: (page) => call("GET", `/me/invoices${query(page)}`),
406
413
  getInvoice: (id) => call("GET", `/me/invoices/${seg(id)}`),
414
+ getInvoicePdfUrl: (id) => call("GET", `/me/invoices/${seg(id)}/pdf`),
407
415
  listSubscriptions: (page) => call("GET", `/me/subscriptions${query(page)}`),
408
416
  getSubscription: (id) => call("GET", `/me/subscriptions/${seg(id)}`),
409
417
  cancelSubscription: (id, opts) => call("POST", `/me/subscriptions/${seg(id)}/cancel`, opts),
@@ -564,6 +572,14 @@ function useSubscriptions(page) {
564
572
  resume: (id) => after(account.resumeSubscription(id))
565
573
  };
566
574
  }
575
+ function useOrder(id) {
576
+ const { account } = useAuth();
577
+ return useResource(null, async () => id ? account.getOrder(id) : null, [account, id]);
578
+ }
579
+ function useInvoice(id) {
580
+ const { account } = useAuth();
581
+ return useResource(null, async () => id ? account.getInvoice(id) : null, [account, id]);
582
+ }
567
583
  // Annotate the CommonJS export names for ESM import in node:
568
584
  0 && (module.exports = {
569
585
  ThrottleAuthError,
@@ -576,7 +592,9 @@ function useSubscriptions(page) {
576
592
  useAuth,
577
593
  useAuthContext,
578
594
  useCustomer,
595
+ useInvoice,
579
596
  useInvoices,
597
+ useOrder,
580
598
  useOrders,
581
599
  useSavedCards,
582
600
  useSubscriptions