@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.
- package/CLAUDE.md +25 -0
- package/README.md +307 -1
- package/dist/chunk-AIXBUDOT.js +50 -0
- package/dist/chunk-AIXBUDOT.js.map +1 -0
- package/dist/chunk-HGBAIM6B.js +140 -0
- package/dist/chunk-HGBAIM6B.js.map +1 -0
- package/dist/{chunk-OYNGTNOS.js → chunk-SZ427BQ3.js} +54 -6
- package/dist/chunk-SZ427BQ3.js.map +1 -0
- package/dist/index.cjs +9 -4
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +26 -1
- package/dist/index.d.ts +26 -1
- package/dist/index.js +377 -8
- package/dist/index.js.map +1 -1
- package/dist/react/components/index.cjs +4558 -0
- package/dist/react/components/index.cjs.map +1 -0
- package/dist/react/components/index.d.cts +735 -0
- package/dist/react/components/index.d.ts +735 -0
- package/dist/react/components/index.js +3918 -0
- package/dist/react/components/index.js.map +1 -0
- package/dist/react/forms/index.cjs +3 -0
- package/dist/react/forms/index.cjs.map +1 -1
- package/dist/react/forms/index.js +5 -46
- package/dist/react/forms/index.js.map +1 -1
- package/dist/react/index.cjs +22 -4
- package/dist/react/index.cjs.map +1 -1
- package/dist/react/index.d.cts +278 -3
- package/dist/react/index.d.ts +278 -3
- package/dist/react/index.js +17 -122
- package/dist/react/index.js.map +1 -1
- package/dist/styles.css +422 -0
- package/package.json +5 -2
- package/dist/chunk-IJ3DNMNG.js +0 -49
- package/dist/chunk-IJ3DNMNG.js.map +0 -1
- package/dist/chunk-OYNGTNOS.js.map +0 -1
- package/dist/chunk-ZMNCURDE.js +0 -1
- package/dist/chunk-ZMNCURDE.js.map +0 -1
|
@@ -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":"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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,57 +1,16 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import {
|
|
3
|
+
messageForError
|
|
4
|
+
} from "../../chunk-AIXBUDOT.js";
|
|
1
5
|
import {
|
|
2
6
|
useAuth
|
|
3
|
-
} from "../../chunk-
|
|
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"]}
|
package/dist/react/index.cjs
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
297
|
-
|
|
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
|