@puzzle-ui/fullstack 0.0.0-stage → 0.5.1

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.
Files changed (80) hide show
  1. package/README.md +65 -2
  2. package/dist/LICENSE +21 -0
  3. package/dist/auth/client/auth.css +206 -0
  4. package/dist/auth/client/experience.cjs +340 -0
  5. package/dist/auth/client/experience.cjs.map +1 -0
  6. package/dist/auth/client/experience.d.cts +67 -0
  7. package/dist/auth/client/experience.d.ts +67 -0
  8. package/dist/auth/client/experience.js +340 -0
  9. package/dist/auth/client/experience.js.map +1 -0
  10. package/dist/auth/client/index.cjs +234 -0
  11. package/dist/auth/client/index.cjs.map +1 -0
  12. package/dist/auth/client/index.d.cts +48 -0
  13. package/dist/auth/client/index.d.ts +48 -0
  14. package/dist/auth/client/index.js +216 -0
  15. package/dist/auth/client/index.js.map +1 -0
  16. package/dist/auth/client/messages.cjs +80 -0
  17. package/dist/auth/client/messages.cjs.map +1 -0
  18. package/dist/auth/client/messages.d.cts +92 -0
  19. package/dist/auth/client/messages.d.ts +92 -0
  20. package/dist/auth/client/messages.js +79 -0
  21. package/dist/auth/client/messages.js.map +1 -0
  22. package/dist/auth/client/passkeys.cjs +43 -0
  23. package/dist/auth/client/passkeys.cjs.map +1 -0
  24. package/dist/auth/client/passkeys.d.cts +23 -0
  25. package/dist/auth/client/passkeys.d.ts +23 -0
  26. package/dist/auth/client/passkeys.js +40 -0
  27. package/dist/auth/client/passkeys.js.map +1 -0
  28. package/dist/auth/client/react.cjs +859 -0
  29. package/dist/auth/client/react.cjs.map +1 -0
  30. package/dist/auth/client/react.d.cts +72 -0
  31. package/dist/auth/client/react.d.ts +72 -0
  32. package/dist/auth/client/react.js +850 -0
  33. package/dist/auth/client/react.js.map +1 -0
  34. package/dist/auth/next/index.cjs +40 -0
  35. package/dist/auth/next/index.cjs.map +1 -0
  36. package/dist/auth/next/index.d.cts +12 -0
  37. package/dist/auth/next/index.d.ts +12 -0
  38. package/dist/auth/next/index.js +38 -0
  39. package/dist/auth/next/index.js.map +1 -0
  40. package/dist/auth/server/health.cjs +51 -0
  41. package/dist/auth/server/health.cjs.map +1 -0
  42. package/dist/auth/server/health.d.cts +15 -0
  43. package/dist/auth/server/health.d.ts +15 -0
  44. package/dist/auth/server/health.js +51 -0
  45. package/dist/auth/server/health.js.map +1 -0
  46. package/dist/auth/server/index.cjs +108 -0
  47. package/dist/auth/server/index.cjs.map +1 -0
  48. package/dist/auth/server/index.d.cts +22 -0
  49. package/dist/auth/server/index.d.ts +22 -0
  50. package/dist/auth/server/index.js +88 -0
  51. package/dist/auth/server/index.js.map +1 -0
  52. package/dist/auth/server/lifecycle.cjs +126 -0
  53. package/dist/auth/server/lifecycle.cjs.map +1 -0
  54. package/dist/auth/server/lifecycle.d.cts +20 -0
  55. package/dist/auth/server/lifecycle.d.ts +20 -0
  56. package/dist/auth/server/lifecycle.js +126 -0
  57. package/dist/auth/server/lifecycle.js.map +1 -0
  58. package/dist/auth/server/mfa.cjs +682 -0
  59. package/dist/auth/server/mfa.cjs.map +1 -0
  60. package/dist/auth/server/mfa.d.cts +90 -0
  61. package/dist/auth/server/mfa.d.ts +90 -0
  62. package/dist/auth/server/mfa.js +673 -0
  63. package/dist/auth/server/mfa.js.map +1 -0
  64. package/dist/auth/server/session.cjs +58 -0
  65. package/dist/auth/server/session.cjs.map +1 -0
  66. package/dist/auth/server/session.d.cts +14 -0
  67. package/dist/auth/server/session.d.ts +14 -0
  68. package/dist/auth/server/session.js +53 -0
  69. package/dist/auth/server/session.js.map +1 -0
  70. package/dist/auth/shared/index.cjs +0 -0
  71. package/dist/auth/shared/index.d.cts +64 -0
  72. package/dist/auth/shared/index.d.ts +64 -0
  73. package/dist/auth/shared/index.js +1 -0
  74. package/dist/auth/tanstack/index.cjs +34 -0
  75. package/dist/auth/tanstack/index.cjs.map +1 -0
  76. package/dist/auth/tanstack/index.d.cts +7 -0
  77. package/dist/auth/tanstack/index.d.ts +7 -0
  78. package/dist/auth/tanstack/index.js +32 -0
  79. package/dist/auth/tanstack/index.js.map +1 -0
  80. package/package.json +113 -4
@@ -0,0 +1 @@
1
+ {"version":3,"file":"experience.cjs","names":["useId","FormField","TextAreaInput","Select","Checkbox","TextInput","useAuth","useState","Heading","Text","SignInForm","Alert","Button"],"sources":["../../../src/auth/client/experience.tsx"],"sourcesContent":["\"use client\";\n\nimport {\n useEffect,\n useId,\n useState,\n type CSSProperties,\n type FormEvent,\n type ReactNode,\n} from \"react\";\n\nimport {\n Alert,\n Button,\n Checkbox,\n FormField,\n Heading,\n Select,\n Text,\n TextAreaInput,\n TextInput,\n} from \"@puzzle-ui/ui\";\n\nimport type { AuthClient, AuthClientSnapshot } from \"./index\";\nimport { SignInForm, useAuth } from \"./react\";\n\nexport type AuthFieldValue = string | boolean;\n\nexport type AuthFieldValues = Readonly<Record<string, AuthFieldValue>>;\n\nexport type AuthFieldOption = {\n readonly label: string;\n readonly value: string;\n};\n\nexport type AuthCustomField = {\n readonly name: string;\n readonly label: string;\n readonly type:\n | \"text\"\n | \"email\"\n | \"tel\"\n | \"url\"\n | \"number\"\n | \"checkbox\"\n | \"select\"\n | \"textarea\";\n readonly description?: string;\n readonly placeholder?: string;\n readonly autoComplete?: string;\n readonly required?: boolean;\n readonly disabled?: boolean;\n readonly defaultValue?: AuthFieldValue;\n readonly options?: readonly AuthFieldOption[];\n readonly validate?: (\n value: AuthFieldValue,\n values: AuthFieldValues,\n ) => string | null | undefined | Promise<string | null | undefined>;\n};\n\nexport type AuthRegistrationInput = {\n readonly name: string | null;\n readonly email: string;\n readonly password: string;\n};\n\nexport type AuthExperienceSubmitInput = {\n readonly registration: AuthRegistrationInput;\n readonly fields: AuthFieldValues;\n readonly signUp: AuthClient[\"register\"];\n};\n\nexport type AuthExperienceBranding = {\n readonly logo?: ReactNode;\n readonly title?: ReactNode;\n readonly description?: ReactNode;\n};\n\nexport type AuthExperienceLayout = {\n readonly className?: string;\n readonly contentClassName?: string;\n readonly header?: ReactNode;\n readonly footer?: ReactNode;\n readonly width?: \"sm\" | \"md\" | \"lg\" | \"full\";\n readonly align?: \"left\" | \"center\";\n};\n\nexport type AuthExperienceRedirects = {\n readonly afterSignIn?: string;\n readonly afterSignUp?: string;\n};\n\nexport type AuthExperienceConfig = {\n readonly branding?: AuthExperienceBranding;\n readonly layout?: AuthExperienceLayout;\n readonly redirects?: AuthExperienceRedirects;\n readonly fields?: readonly AuthCustomField[];\n};\n\nexport type AuthExperienceProps = {\n readonly mode?: \"sign-in\" | \"sign-up\";\n readonly config?: AuthExperienceConfig;\n readonly className?: string;\n readonly onSubmit?: (\n input: AuthExperienceSubmitInput,\n ) => Promise<AuthClientSnapshot>;\n readonly onSuccess?: () => void;\n readonly onSignIn?: () => void;\n readonly onSignUp?: () => void;\n readonly onForgotPassword?: () => void;\n};\n\ntype FieldErrors = Readonly<Record<string, string>>;\n\nconst EMPTY_FIELDS: readonly AuthCustomField[] = [];\n\nconst EMAIL_PATTERN = /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/;\n\nconst isValidEmail = (value: string): boolean => {\n const email = value.trim();\n\n return email.length <= 254 && EMAIL_PATTERN.test(email);\n};\n\nconst getEmailError = (\n value: string,\n messages: ReturnType<typeof useAuth>[\"messages\"],\n): string | null => {\n const email = value.trim();\n\n if (!email) {\n return messages.emailRequired;\n }\n\n if (!isValidEmail(email)) {\n return \"Enter a valid email address.\";\n }\n\n return null;\n};\n\nconst isSafeRedirect = (value: string | undefined): value is string =>\n Boolean(\n value &&\n value.startsWith(\"/\") &&\n !value.startsWith(\"//\") &&\n !value.includes(\"\\\\\") &&\n !/[\\u0000-\\u001f\\u007f]/.test(value),\n );\n\nconst redirectAfterSuccess = (value: string | undefined) => {\n if (!isSafeRedirect(value) || typeof window === \"undefined\") {\n return;\n }\n\n window.location.assign(value);\n};\n\nconst getInitialValues = (\n fields: readonly AuthCustomField[],\n): Record<string, AuthFieldValue> =>\n Object.fromEntries(\n fields.map((field) => [\n field.name,\n field.defaultValue ?? (field.type === \"checkbox\" ? false : \"\"),\n ]),\n );\n\nconst validateFieldDefinition = (fields: readonly AuthCustomField[]): void => {\n const names = new Set<string>();\n\n for (const field of fields) {\n if (!/^[a-zA-Z][a-zA-Z0-9_-]*$/.test(field.name)) {\n throw new Error(`Invalid custom auth field name: \"${field.name}\".`);\n }\n\n if ([\"name\", \"email\", \"password\", \"confirmPassword\"].includes(field.name)) {\n throw new Error(`Custom auth field name \"${field.name}\" is reserved.`);\n }\n\n if (names.has(field.name)) {\n throw new Error(`Duplicate custom auth field name: \"${field.name}\".`);\n }\n\n if (\n field.type === \"select\" &&\n (!field.options || field.options.length === 0)\n ) {\n throw new Error(\n `Select field \"${field.name}\" requires at least one option.`,\n );\n }\n\n names.add(field.name);\n }\n};\n\nconst validateFields = async (\n fields: readonly AuthCustomField[],\n values: AuthFieldValues,\n): Promise<FieldErrors> => {\n const errors: Record<string, string> = {};\n\n for (const field of fields) {\n const value = values[field.name];\n\n if (\n field.required &&\n (value === undefined ||\n value === \"\" ||\n (typeof value === \"boolean\" && !value))\n ) {\n errors[field.name] = `${field.label} is required.`;\n continue;\n }\n\n if (!field.validate) continue;\n\n const message = await field.validate(value ?? \"\", values);\n\n if (message) {\n errors[field.name] = message;\n }\n }\n\n return errors;\n};\n\nconst AuthCustomFields = ({\n fields,\n values,\n errors,\n disabled,\n onChange,\n}: {\n readonly fields: readonly AuthCustomField[];\n readonly values: AuthFieldValues;\n readonly errors: FieldErrors;\n readonly disabled: boolean;\n readonly onChange: (name: string, value: AuthFieldValue) => void;\n}) => {\n const generatedId = useId();\n\n return (\n <>\n {fields.map((field) => {\n const id = `${generatedId}-${field.name}`;\n const value = values[field.name] ?? \"\";\n const error = errors[field.name];\n\n const commonProps = {\n name: field.name,\n disabled: disabled || field.disabled,\n required: field.required,\n };\n\n return (\n <FormField\n key={field.name}\n controlId={id}\n label={field.label}\n description={field.description}\n error={error}\n required={field.required}\n disabled={commonProps.disabled}\n className={\n field.type === \"url\" || field.type === \"number\"\n ? \"puzzle-auth__custom-field\"\n : undefined\n }\n >\n {field.type === \"textarea\" ? (\n <TextAreaInput\n {...commonProps}\n value={String(value)}\n placeholder={field.placeholder}\n resizable\n onChange={(event) =>\n onChange(field.name, event.currentTarget.value)\n }\n />\n ) : field.type === \"select\" ? (\n <Select\n {...commonProps}\n fullWidth\n value={String(value)}\n placeholder={field.placeholder ?? \"Select an option\"}\n options={field.options ?? []}\n onChange={(value) => onChange(field.name, value ?? \"\")}\n />\n ) : field.type === \"checkbox\" ? (\n <Checkbox\n {...commonProps}\n checked={Boolean(value)}\n onChange={(event) =>\n onChange(field.name, event.currentTarget.checked)\n }\n />\n ) : field.type === \"url\" || field.type === \"number\" ? (\n <input\n {...commonProps}\n type={field.type}\n value={String(value)}\n placeholder={field.placeholder}\n autoComplete={field.autoComplete}\n onChange={(event) =>\n onChange(field.name, event.currentTarget.value)\n }\n />\n ) : (\n <TextInput\n {...commonProps}\n type={field.type === \"tel\" ? \"phone\" : field.type}\n value={String(value)}\n placeholder={field.placeholder}\n autoComplete={field.autoComplete}\n onChange={(event) =>\n onChange(field.name, event.currentTarget.value)\n }\n />\n )}\n </FormField>\n );\n })}\n </>\n );\n};\n\nconst AuthExperience = ({\n mode = \"sign-in\",\n config = {},\n className,\n onSubmit,\n onSuccess,\n onSignIn,\n onSignUp,\n onForgotPassword,\n}: AuthExperienceProps) => {\n const { signUp, messages } = useAuth();\n\n const fields = config.fields ?? EMPTY_FIELDS;\n const branding = config.branding;\n const layout = config.layout;\n const redirects = config.redirects;\n\n validateFieldDefinition(fields);\n\n const [values, setValues] = useState<Record<string, AuthFieldValue>>(() =>\n getInitialValues(fields),\n );\n const [errors, setErrors] = useState<FieldErrors>({});\n const [formError, setFormError] = useState<string | null>(null);\n const [emailError, setEmailError] = useState<string | null>(null);\n const [loading, setLoading] = useState(false);\n\n const [name, setName] = useState(\"\");\n const [email, setEmail] = useState(\"\");\n const [password, setPassword] = useState(\"\");\n const [confirmation, setConfirmation] = useState(\"\");\n\n useEffect(() => {\n setValues((current) => {\n const next: Record<string, AuthFieldValue> = {};\n\n for (const field of fields) {\n const currentValue = Object.prototype.hasOwnProperty.call(\n current,\n field.name,\n )\n ? current[field.name]\n : undefined;\n\n next[field.name] =\n currentValue ??\n field.defaultValue ??\n (field.type === \"checkbox\" ? false : \"\");\n }\n\n const currentNames = Object.keys(current);\n const nextNames = Object.keys(next);\n\n const unchanged =\n currentNames.length === nextNames.length &&\n nextNames.every((fieldName) => current[fieldName] === next[fieldName]);\n\n return unchanged ? current : next;\n });\n\n setErrors((current) => {\n const activeNames = new Set(fields.map((field) => field.name));\n\n const next = Object.fromEntries(\n Object.entries(current).filter(([fieldName]) =>\n activeNames.has(fieldName),\n ),\n );\n\n return Object.keys(next).length === Object.keys(current).length\n ? current\n : next;\n });\n }, [fields]);\n\n const updateField = (fieldName: string, value: AuthFieldValue) => {\n setValues((current) => ({\n ...current,\n [fieldName]: value,\n }));\n\n setErrors((current) => {\n if (!(fieldName in current)) return current;\n\n const next = { ...current };\n delete next[fieldName];\n return next;\n });\n };\n\n const handleEmailChange = (value: string) => {\n setEmail(value);\n\n if (emailError) {\n setEmailError(getEmailError(value, messages));\n }\n };\n\n const handleSubmit = async (event: FormEvent<HTMLFormElement>) => {\n event.preventDefault();\n setFormError(null);\n setErrors({});\n\n const nextEmailError = getEmailError(email, messages);\n\n setEmailError(nextEmailError);\n\n if (nextEmailError) {\n return;\n }\n\n if (password !== confirmation) {\n setFormError(messages.passwordMismatch);\n return;\n }\n\n if (!password) {\n setFormError(messages.passwordRequired);\n return;\n }\n\n setLoading(true);\n\n try {\n const nextErrors = await validateFields(fields, values);\n\n if (Object.keys(nextErrors).length > 0) {\n setErrors(nextErrors);\n return;\n }\n\n const registration: AuthRegistrationInput = {\n name: name.trim() || null,\n email: email.trim(),\n password,\n };\n\n const result = onSubmit\n ? await onSubmit({\n registration,\n fields: { ...values },\n signUp,\n })\n : await signUp(registration);\n\n if (result.status === \"authenticated\") {\n onSuccess?.();\n redirectAfterSuccess(redirects?.afterSignUp);\n }\n } catch (cause) {\n setFormError(\n cause instanceof Error ? cause.message : messages.unexpectedError,\n );\n } finally {\n setLoading(false);\n }\n };\n\n const wrapperClassName = [\n \"puzzle-auth\",\n \"puzzle-auth-experience\",\n layout?.className,\n className,\n ]\n .filter(Boolean)\n .join(\" \");\n\n const contentClassName = [\n \"puzzle-auth-experience__content\",\n layout?.contentClassName,\n ]\n .filter(Boolean)\n .join(\" \");\n\n const style = {\n \"--puzzle-auth-experience-width\":\n layout?.width === \"sm\"\n ? \"24rem\"\n : layout?.width === \"lg\"\n ? \"48rem\"\n : layout?.width === \"full\"\n ? \"100%\"\n : \"32rem\",\n \"--puzzle-auth-experience-align\":\n layout?.align === \"left\" ? \"flex-start\" : \"center\",\n } as CSSProperties;\n\n return (\n <section className={wrapperClassName} style={style}>\n {layout?.header}\n\n <div className=\"puzzle-auth-experience__branding\">\n {branding?.logo}\n {branding?.title ? (\n <Heading as=\"h1\" level=\"headline2\">\n {branding.title}\n </Heading>\n ) : null}\n {branding?.description ? <Text as=\"p\">{branding.description}</Text> : null}\n </div>\n\n <div className={contentClassName}>\n {mode === \"sign-in\" ? (\n <SignInForm\n redirectTo={redirects?.afterSignIn}\n onSuccess={onSuccess}\n onForgotPassword={onForgotPassword}\n onSignUp={onSignUp}\n />\n ) : (\n <>\n {formError ? (\n <div role=\"alert\" aria-live=\"assertive\">\n <Alert severity=\"error\">{formError}</Alert>\n </div>\n ) : null}\n\n <form\n className=\"puzzle-auth__form\"\n onSubmit={(event) => void handleSubmit(event)}\n noValidate\n >\n <FormField\n label={messages.name}\n controlId=\"puzzle-auth-experience-name\"\n >\n <TextInput\n name=\"name\"\n type=\"text\"\n autoComplete=\"name\"\n value={name}\n disabled={loading}\n onChange={(event) => setName(event.currentTarget.value)}\n />\n </FormField>\n\n <FormField\n label={messages.email}\n controlId=\"puzzle-auth-experience-email\"\n required\n error={emailError}\n >\n <TextInput\n name=\"email\"\n type=\"email\"\n autoComplete=\"email\"\n required\n value={email}\n disabled={loading}\n onChange={(event) =>\n handleEmailChange(event.currentTarget.value)\n }\n />\n\n </FormField>\n\n <FormField\n label={messages.password}\n controlId=\"puzzle-auth-experience-password\"\n required\n >\n <TextInput\n name=\"new-password\"\n type=\"password\"\n suffix={null}\n autoComplete=\"new-password\"\n required\n value={password}\n disabled={loading}\n onChange={(event) => setPassword(event.currentTarget.value)}\n />\n </FormField>\n\n <FormField\n label={messages.confirmPassword}\n controlId=\"puzzle-auth-experience-confirm\"\n required\n >\n <TextInput\n name=\"confirm-password\"\n type=\"password\"\n suffix={null}\n autoComplete=\"new-password\"\n required\n value={confirmation}\n disabled={loading}\n onChange={(event) =>\n setConfirmation(event.currentTarget.value)\n }\n />\n </FormField>\n\n <AuthCustomFields\n fields={fields}\n values={values}\n errors={errors}\n disabled={loading}\n onChange={updateField}\n />\n\n <Button\n type=\"submit\"\n fullWidth\n loading={loading}\n disabled={\n loading || !email.trim() || !password || !confirmation\n }\n >\n {loading ? messages.signingUp : messages.createAccount}\n </Button>\n </form>\n\n {onSignIn ? (\n <footer className=\"puzzle-auth__footer\">\n <Text>{messages.existingAccount}</Text>\n <Button\n type=\"button\"\n variant=\"link\"\n onClick={onSignIn}\n >\n {messages.signIn}\n </Button>\n </footer>\n ) : null}\n </>\n )}\n </div>\n\n {layout?.footer}\n </section>\n );\n};\n\nexport { AuthExperience };\n"],"mappings":";;;;;;AAkHA,MAAM,eAA2C,CAAC;AAElD,MAAM,gBAAgB;AAEtB,MAAM,gBAAgB,UAA2B;CAC/C,MAAM,QAAQ,MAAM,KAAK;CAEzB,OAAO,MAAM,UAAU,OAAO,cAAc,KAAK,KAAK;AACxD;AAEA,MAAM,iBACJ,OACA,aACkB;CAClB,MAAM,QAAQ,MAAM,KAAK;CAEzB,IAAI,CAAC,OACH,OAAO,SAAS;CAGlB,IAAI,CAAC,aAAa,KAAK,GACrB,OAAO;CAGT,OAAO;AACT;AAEA,MAAM,kBAAkB,UACtB,QACE,SACA,MAAM,WAAW,GAAG,KACpB,CAAC,MAAM,WAAW,IAAI,KACtB,CAAC,MAAM,SAAS,IAAI,KACpB,CAAC,wBAAwB,KAAK,KAAK,CACrC;AAEF,MAAM,wBAAwB,UAA8B;CAC1D,IAAI,CAAC,eAAe,KAAK,KAAK,OAAO,WAAW,aAC9C;CAGF,OAAO,SAAS,OAAO,KAAK;AAC9B;AAEA,MAAM,oBACJ,WAEA,OAAO,YACL,OAAO,KAAK,UAAU,CACpB,MAAM,MACN,MAAM,iBAAiB,MAAM,SAAS,aAAa,QAAQ,GAC7D,CAAC,CACH;AAEF,MAAM,2BAA2B,WAA6C;CAC5E,MAAM,wBAAQ,IAAI,IAAY;CAE9B,KAAK,MAAM,SAAS,QAAQ;EAC1B,IAAI,CAAC,2BAA2B,KAAK,MAAM,IAAI,GAC7C,MAAM,IAAI,MAAM,oCAAoC,MAAM,KAAK,GAAG;EAGpE,IAAI;GAAC;GAAQ;GAAS;GAAY;EAAiB,CAAC,CAAC,SAAS,MAAM,IAAI,GACtE,MAAM,IAAI,MAAM,2BAA2B,MAAM,KAAK,eAAe;EAGvE,IAAI,MAAM,IAAI,MAAM,IAAI,GACtB,MAAM,IAAI,MAAM,sCAAsC,MAAM,KAAK,GAAG;EAGtE,IACE,MAAM,SAAS,aACd,CAAC,MAAM,WAAW,MAAM,QAAQ,WAAW,IAE5C,MAAM,IAAI,MACR,iBAAiB,MAAM,KAAK,gCAC9B;EAGF,MAAM,IAAI,MAAM,IAAI;CACtB;AACF;AAEA,MAAM,iBAAiB,OACrB,QACA,WACyB;CACzB,MAAM,SAAiC,CAAC;CAExC,KAAK,MAAM,SAAS,QAAQ;EAC1B,MAAM,QAAQ,OAAO,MAAM;EAE3B,IACE,MAAM,aACL,UAAU,KAAA,KACT,UAAU,MACT,OAAO,UAAU,aAAa,CAAC,QAClC;GACA,OAAO,MAAM,QAAQ,GAAG,MAAM,MAAM;GACpC;EACF;EAEA,IAAI,CAAC,MAAM,UAAU;EAErB,MAAM,UAAU,MAAM,MAAM,SAAS,SAAS,IAAI,MAAM;EAExD,IAAI,SACF,OAAO,MAAM,QAAQ;CAEzB;CAEA,OAAO;AACT;AAEA,MAAM,oBAAoB,EACxB,QACA,QACA,QACA,UACA,eAOI;CACJ,MAAM,eAAA,GAAcA,MAAAA,MAAAA,CAAM;CAE1B,OACE,iBAAA,GAAA,kBAAA,IAAA,CAAA,kBAAA,UAAA,EAAA,UACG,OAAO,KAAK,UAAU;EACrB,MAAM,KAAK,GAAG,YAAY,GAAG,MAAM;EACnC,MAAM,QAAQ,OAAO,MAAM,SAAS;EACpC,MAAM,QAAQ,OAAO,MAAM;EAE3B,MAAM,cAAc;GAClB,MAAM,MAAM;GACZ,UAAU,YAAY,MAAM;GAC5B,UAAU,MAAM;EAClB;EAEA,OACE,iBAAA,GAAA,kBAAA,IAAA,CAACC,cAAAA,WAAD;GAEE,WAAW;GACX,OAAO,MAAM;GACb,aAAa,MAAM;GACZ;GACP,UAAU,MAAM;GAChB,UAAU,YAAY;GACtB,WACE,MAAM,SAAS,SAAS,MAAM,SAAS,WACnC,8BACA,KAAA;GAGL,UAAA,MAAM,SAAS,aACd,iBAAA,GAAA,kBAAA,IAAA,CAACC,cAAAA,eAAD;IACE,GAAI;IACJ,OAAO,OAAO,KAAK;IACnB,aAAa,MAAM;IACnB,WAAA;IACA,WAAW,UACT,SAAS,MAAM,MAAM,MAAM,cAAc,KAAK;GAEjD,CAAA,IACC,MAAM,SAAS,WACjB,iBAAA,GAAA,kBAAA,IAAA,CAACC,cAAAA,QAAD;IACE,GAAI;IACJ,WAAA;IACA,OAAO,OAAO,KAAK;IACnB,aAAa,MAAM,eAAe;IAClC,SAAS,MAAM,WAAW,CAAC;IAC3B,WAAW,UAAU,SAAS,MAAM,MAAM,SAAS,EAAE;GACtD,CAAA,IACC,MAAM,SAAS,aACjB,iBAAA,GAAA,kBAAA,IAAA,CAACC,cAAAA,UAAD;IACE,GAAI;IACJ,SAAS,QAAQ,KAAK;IACtB,WAAW,UACT,SAAS,MAAM,MAAM,MAAM,cAAc,OAAO;GAEnD,CAAA,IACC,MAAM,SAAS,SAAS,MAAM,SAAS,WACzC,iBAAA,GAAA,kBAAA,IAAA,CAAC,SAAD;IACE,GAAI;IACJ,MAAM,MAAM;IACZ,OAAO,OAAO,KAAK;IACnB,aAAa,MAAM;IACnB,cAAc,MAAM;IACpB,WAAW,UACT,SAAS,MAAM,MAAM,MAAM,cAAc,KAAK;GAEjD,CAAA,IAED,iBAAA,GAAA,kBAAA,IAAA,CAACC,cAAAA,WAAD;IACE,GAAI;IACJ,MAAM,MAAM,SAAS,QAAQ,UAAU,MAAM;IAC7C,OAAO,OAAO,KAAK;IACnB,aAAa,MAAM;IACnB,cAAc,MAAM;IACpB,WAAW,UACT,SAAS,MAAM,MAAM,MAAM,cAAc,KAAK;GAEjD,CAAA;EAEM,GA/DJ,MAAM,IA+DF;CAEf,CAAC,EACD,CAAA;AAEN;AAEA,MAAM,kBAAkB,EACtB,OAAO,WACP,SAAS,CAAC,GACV,WACA,UACA,WACA,UACA,UACA,uBACyB;CACzB,MAAM,EAAE,QAAQ,aAAaC,cAAAA,QAAQ;CAErC,MAAM,SAAS,OAAO,UAAU;CAChC,MAAM,WAAW,OAAO;CACxB,MAAM,SAAS,OAAO;CACtB,MAAM,YAAY,OAAO;CAEzB,wBAAwB,MAAM;CAE9B,MAAM,CAAC,QAAQ,cAAA,GAAaC,MAAAA,SAAAA,OAC1B,iBAAiB,MAAM,CACzB;CACA,MAAM,CAAC,QAAQ,cAAA,GAAaA,MAAAA,SAAAA,CAAsB,CAAC,CAAC;CACpD,MAAM,CAAC,WAAW,iBAAA,GAAgBA,MAAAA,SAAAA,CAAwB,IAAI;CAC9D,MAAM,CAAC,YAAY,kBAAA,GAAiBA,MAAAA,SAAAA,CAAwB,IAAI;CAChE,MAAM,CAAC,SAAS,eAAA,GAAcA,MAAAA,SAAAA,CAAS,KAAK;CAE5C,MAAM,CAAC,MAAM,YAAA,GAAWA,MAAAA,SAAAA,CAAS,EAAE;CACnC,MAAM,CAAC,OAAO,aAAA,GAAYA,MAAAA,SAAAA,CAAS,EAAE;CACrC,MAAM,CAAC,UAAU,gBAAA,GAAeA,MAAAA,SAAAA,CAAS,EAAE;CAC3C,MAAM,CAAC,cAAc,oBAAA,GAAmBA,MAAAA,SAAAA,CAAS,EAAE;CAEnD,CAAA,GAAA,MAAA,UAAA,OAAgB;EACd,WAAW,YAAY;GACrB,MAAM,OAAuC,CAAC;GAE9C,KAAK,MAAM,SAAS,QAAQ;IAC1B,MAAM,eAAe,OAAO,UAAU,eAAe,KACnD,SACA,MAAM,IACR,IACI,QAAQ,MAAM,QACd,KAAA;IAEJ,KAAK,MAAM,QACT,gBACA,MAAM,iBACL,MAAM,SAAS,aAAa,QAAQ;GACzC;GAEA,MAAM,eAAe,OAAO,KAAK,OAAO;GACxC,MAAM,YAAY,OAAO,KAAK,IAAI;GAMlC,OAHE,aAAa,WAAW,UAAU,UAClC,UAAU,OAAO,cAAc,QAAQ,eAAe,KAAK,UAAU,IAEpD,UAAU;EAC/B,CAAC;EAED,WAAW,YAAY;GACrB,MAAM,cAAc,IAAI,IAAI,OAAO,KAAK,UAAU,MAAM,IAAI,CAAC;GAE7D,MAAM,OAAO,OAAO,YAClB,OAAO,QAAQ,OAAO,CAAC,CAAC,QAAQ,CAAC,eAC/B,YAAY,IAAI,SAAS,CAC3B,CACF;GAEA,OAAO,OAAO,KAAK,IAAI,CAAC,CAAC,WAAW,OAAO,KAAK,OAAO,CAAC,CAAC,SACrD,UACA;EACN,CAAC;CACH,GAAG,CAAC,MAAM,CAAC;CAEX,MAAM,eAAe,WAAmB,UAA0B;EAChE,WAAW,aAAa;GACtB,GAAG;IACF,YAAY;EACf,EAAE;EAEF,WAAW,YAAY;GACrB,IAAI,EAAE,aAAa,UAAU,OAAO;GAEpC,MAAM,OAAO,EAAE,GAAG,QAAQ;GAC1B,OAAO,KAAK;GACZ,OAAO;EACT,CAAC;CACH;CAEA,MAAM,qBAAqB,UAAkB;EAC3C,SAAS,KAAK;EAEd,IAAI,YACF,cAAc,cAAc,OAAO,QAAQ,CAAC;CAEhD;CAEA,MAAM,eAAe,OAAO,UAAsC;EAChE,MAAM,eAAe;EACrB,aAAa,IAAI;EACjB,UAAU,CAAC,CAAC;EAEZ,MAAM,iBAAiB,cAAc,OAAO,QAAQ;EAEpD,cAAc,cAAc;EAE5B,IAAI,gBACF;EAGF,IAAI,aAAa,cAAc;GAC7B,aAAa,SAAS,gBAAgB;GACtC;EACF;EAEA,IAAI,CAAC,UAAU;GACb,aAAa,SAAS,gBAAgB;GACtC;EACF;EAEA,WAAW,IAAI;EAEf,IAAI;GACF,MAAM,aAAa,MAAM,eAAe,QAAQ,MAAM;GAEtD,IAAI,OAAO,KAAK,UAAU,CAAC,CAAC,SAAS,GAAG;IACtC,UAAU,UAAU;IACpB;GACF;GAEA,MAAM,eAAsC;IAC1C,MAAM,KAAK,KAAK,KAAK;IACrB,OAAO,MAAM,KAAK;IAClB;GACF;GAUA,KARe,WACX,MAAM,SAAS;IACb;IACA,QAAQ,EAAE,GAAG,OAAO;IACpB;GACF,CAAC,IACD,MAAM,OAAO,YAAY,EAAA,CAElB,WAAW,iBAAiB;IACrC,YAAY;IACZ,qBAAqB,WAAW,WAAW;GAC7C;EACF,SAAS,OAAO;GACd,aACE,iBAAiB,QAAQ,MAAM,UAAU,SAAS,eACpD;EACF,UAAU;GACR,WAAW,KAAK;EAClB;CACF;CAEA,MAAM,mBAAmB;EACvB;EACA;EACA,QAAQ;EACR;CACF,CAAC,CACE,OAAO,OAAO,CAAC,CACf,KAAK,GAAG;CAEX,MAAM,mBAAmB,CACvB,mCACA,QAAQ,gBACV,CAAC,CACE,OAAO,OAAO,CAAC,CACf,KAAK,GAAG;CAEX,MAAM,QAAQ;EACZ,kCACE,QAAQ,UAAU,OACd,UACA,QAAQ,UAAU,OAChB,UACA,QAAQ,UAAU,SAChB,SACA;EACV,kCACE,QAAQ,UAAU,SAAS,eAAe;CAC9C;CAEA,OACE,iBAAA,GAAA,kBAAA,KAAA,CAAC,WAAD;EAAS,WAAW;EAAyB;EAA7C,UAAA;GACG,QAAQ;GAET,iBAAA,GAAA,kBAAA,KAAA,CAAC,OAAD;IAAK,WAAU;IAAf,UAAA;KACG,UAAU;KACV,UAAU,QACT,iBAAA,GAAA,kBAAA,IAAA,CAACC,cAAAA,SAAD;MAAS,IAAG;MAAK,OAAM;MACpB,UAAA,SAAS;KACH,CAAA,IACP;KACH,UAAU,cAAc,iBAAA,GAAA,kBAAA,IAAA,CAACC,cAAAA,MAAD;MAAM,IAAG;MAAK,UAAA,SAAS;KAAkB,CAAA,IAAI;IACnE;;GAEL,iBAAA,GAAA,kBAAA,IAAA,CAAC,OAAD;IAAK,WAAW;IACb,UAAA,SAAS,YACR,iBAAA,GAAA,kBAAA,IAAA,CAACC,cAAAA,YAAD;KACE,YAAY,WAAW;KACZ;KACO;KACR;IACX,CAAA,IAED,iBAAA,GAAA,kBAAA,KAAA,CAAA,kBAAA,UAAA,EAAA,UAAA;KACG,YACC,iBAAA,GAAA,kBAAA,IAAA,CAAC,OAAD;MAAK,MAAK;MAAQ,aAAU;MAC1B,UAAA,iBAAA,GAAA,kBAAA,IAAA,CAACC,cAAAA,OAAD;OAAO,UAAS;OAAS,UAAA;MAAiB,CAAA;KACvC,CAAA,IACH;KAEJ,iBAAA,GAAA,kBAAA,KAAA,CAAC,QAAD;MACE,WAAU;MACV,WAAW,UAAU,KAAK,aAAa,KAAK;MAC5C,YAAA;MAHF,UAAA;OAKE,iBAAA,GAAA,kBAAA,IAAA,CAACV,cAAAA,WAAD;QACE,OAAO,SAAS;QAChB,WAAU;QAEV,UAAA,iBAAA,GAAA,kBAAA,IAAA,CAACI,cAAAA,WAAD;SACE,MAAK;SACL,MAAK;SACL,cAAa;SACb,OAAO;SACP,UAAU;SACV,WAAW,UAAU,QAAQ,MAAM,cAAc,KAAK;QACvD,CAAA;OACQ,CAAA;OAEX,iBAAA,GAAA,kBAAA,IAAA,CAACJ,cAAAA,WAAD;QACE,OAAO,SAAS;QAChB,WAAU;QACV,UAAA;QACA,OAAO;QAEP,UAAA,iBAAA,GAAA,kBAAA,IAAA,CAACI,cAAAA,WAAD;SACE,MAAK;SACL,MAAK;SACL,cAAa;SACb,UAAA;SACA,OAAO;SACP,UAAU;SACV,WAAW,UACT,kBAAkB,MAAM,cAAc,KAAK;QAE9C,CAAA;OAEQ,CAAA;OAEX,iBAAA,GAAA,kBAAA,IAAA,CAACJ,cAAAA,WAAD;QACE,OAAO,SAAS;QAChB,WAAU;QACV,UAAA;QAEA,UAAA,iBAAA,GAAA,kBAAA,IAAA,CAACI,cAAAA,WAAD;SACE,MAAK;SACL,MAAK;SACL,QAAQ;SACR,cAAa;SACb,UAAA;SACA,OAAO;SACP,UAAU;SACV,WAAW,UAAU,YAAY,MAAM,cAAc,KAAK;QAC3D,CAAA;OACQ,CAAA;OAEX,iBAAA,GAAA,kBAAA,IAAA,CAACJ,cAAAA,WAAD;QACE,OAAO,SAAS;QAChB,WAAU;QACV,UAAA;QAEA,UAAA,iBAAA,GAAA,kBAAA,IAAA,CAACI,cAAAA,WAAD;SACE,MAAK;SACL,MAAK;SACL,QAAQ;SACR,cAAa;SACb,UAAA;SACA,OAAO;SACP,UAAU;SACV,WAAW,UACT,gBAAgB,MAAM,cAAc,KAAK;QAE5C,CAAA;OACQ,CAAA;OAEX,iBAAA,GAAA,kBAAA,IAAA,CAAC,kBAAD;QACU;QACA;QACA;QACR,UAAU;QACV,UAAU;OACX,CAAA;OAED,iBAAA,GAAA,kBAAA,IAAA,CAACO,cAAAA,QAAD;QACE,MAAK;QACL,WAAA;QACS;QACT,UACE,WAAW,CAAC,MAAM,KAAK,KAAK,CAAC,YAAY,CAAC;QAG3C,UAAA,UAAU,SAAS,YAAY,SAAS;OACnC,CAAA;MACJ;;KAEL,WACC,iBAAA,GAAA,kBAAA,KAAA,CAAC,UAAD;MAAQ,WAAU;MAAlB,UAAA,CACE,iBAAA,GAAA,kBAAA,IAAA,CAACH,cAAAA,MAAD,EAAA,UAAO,SAAS,gBAAsB,CAAA,GACtC,iBAAA,GAAA,kBAAA,IAAA,CAACG,cAAAA,QAAD;OACE,MAAK;OACL,SAAQ;OACR,SAAS;OAER,UAAA,SAAS;MACJ,CAAA,CACF;KACN,CAAA,IAAA;IACJ,EAAA,CAAA;GAED,CAAA;GAEJ,QAAQ;EACF;;AAEb"}
@@ -0,0 +1,67 @@
1
+ import { AuthClientSnapshot } from "../shared/index.cjs";
2
+ import { AuthClient } from "./index.cjs";
3
+ import { ReactNode } from "react";
4
+ export type AuthFieldValue = string | boolean;
5
+ export type AuthFieldValues = Readonly<Record<string, AuthFieldValue>>;
6
+ export type AuthFieldOption = {
7
+ readonly label: string;
8
+ readonly value: string;
9
+ };
10
+ export type AuthCustomField = {
11
+ readonly name: string;
12
+ readonly label: string;
13
+ readonly type: "text" | "email" | "tel" | "url" | "number" | "checkbox" | "select" | "textarea";
14
+ readonly description?: string;
15
+ readonly placeholder?: string;
16
+ readonly autoComplete?: string;
17
+ readonly required?: boolean;
18
+ readonly disabled?: boolean;
19
+ readonly defaultValue?: AuthFieldValue;
20
+ readonly options?: readonly AuthFieldOption[];
21
+ readonly validate?: (value: AuthFieldValue, values: AuthFieldValues) => string | null | undefined | Promise<string | null | undefined>;
22
+ };
23
+ export type AuthRegistrationInput = {
24
+ readonly name: string | null;
25
+ readonly email: string;
26
+ readonly password: string;
27
+ };
28
+ export type AuthExperienceSubmitInput = {
29
+ readonly registration: AuthRegistrationInput;
30
+ readonly fields: AuthFieldValues;
31
+ readonly signUp: AuthClient["register"];
32
+ };
33
+ export type AuthExperienceBranding = {
34
+ readonly logo?: ReactNode;
35
+ readonly title?: ReactNode;
36
+ readonly description?: ReactNode;
37
+ };
38
+ export type AuthExperienceLayout = {
39
+ readonly className?: string;
40
+ readonly contentClassName?: string;
41
+ readonly header?: ReactNode;
42
+ readonly footer?: ReactNode;
43
+ readonly width?: "sm" | "md" | "lg" | "full";
44
+ readonly align?: "left" | "center";
45
+ };
46
+ export type AuthExperienceRedirects = {
47
+ readonly afterSignIn?: string;
48
+ readonly afterSignUp?: string;
49
+ };
50
+ export type AuthExperienceConfig = {
51
+ readonly branding?: AuthExperienceBranding;
52
+ readonly layout?: AuthExperienceLayout;
53
+ readonly redirects?: AuthExperienceRedirects;
54
+ readonly fields?: readonly AuthCustomField[];
55
+ };
56
+ export type AuthExperienceProps = {
57
+ readonly mode?: "sign-in" | "sign-up";
58
+ readonly config?: AuthExperienceConfig;
59
+ readonly className?: string;
60
+ readonly onSubmit?: (input: AuthExperienceSubmitInput) => Promise<AuthClientSnapshot>;
61
+ readonly onSuccess?: () => void;
62
+ readonly onSignIn?: () => void;
63
+ readonly onSignUp?: () => void;
64
+ readonly onForgotPassword?: () => void;
65
+ };
66
+ declare const AuthExperience: ({ mode, config, className, onSubmit, onSuccess, onSignIn, onSignUp, onForgotPassword }: AuthExperienceProps) => import("react").JSX.Element;
67
+ export { AuthExperience };
@@ -0,0 +1,67 @@
1
+ import { AuthClientSnapshot } from "../shared/index.js";
2
+ import { AuthClient } from "./index.js";
3
+ import { ReactNode } from "react";
4
+ export type AuthFieldValue = string | boolean;
5
+ export type AuthFieldValues = Readonly<Record<string, AuthFieldValue>>;
6
+ export type AuthFieldOption = {
7
+ readonly label: string;
8
+ readonly value: string;
9
+ };
10
+ export type AuthCustomField = {
11
+ readonly name: string;
12
+ readonly label: string;
13
+ readonly type: "text" | "email" | "tel" | "url" | "number" | "checkbox" | "select" | "textarea";
14
+ readonly description?: string;
15
+ readonly placeholder?: string;
16
+ readonly autoComplete?: string;
17
+ readonly required?: boolean;
18
+ readonly disabled?: boolean;
19
+ readonly defaultValue?: AuthFieldValue;
20
+ readonly options?: readonly AuthFieldOption[];
21
+ readonly validate?: (value: AuthFieldValue, values: AuthFieldValues) => string | null | undefined | Promise<string | null | undefined>;
22
+ };
23
+ export type AuthRegistrationInput = {
24
+ readonly name: string | null;
25
+ readonly email: string;
26
+ readonly password: string;
27
+ };
28
+ export type AuthExperienceSubmitInput = {
29
+ readonly registration: AuthRegistrationInput;
30
+ readonly fields: AuthFieldValues;
31
+ readonly signUp: AuthClient["register"];
32
+ };
33
+ export type AuthExperienceBranding = {
34
+ readonly logo?: ReactNode;
35
+ readonly title?: ReactNode;
36
+ readonly description?: ReactNode;
37
+ };
38
+ export type AuthExperienceLayout = {
39
+ readonly className?: string;
40
+ readonly contentClassName?: string;
41
+ readonly header?: ReactNode;
42
+ readonly footer?: ReactNode;
43
+ readonly width?: "sm" | "md" | "lg" | "full";
44
+ readonly align?: "left" | "center";
45
+ };
46
+ export type AuthExperienceRedirects = {
47
+ readonly afterSignIn?: string;
48
+ readonly afterSignUp?: string;
49
+ };
50
+ export type AuthExperienceConfig = {
51
+ readonly branding?: AuthExperienceBranding;
52
+ readonly layout?: AuthExperienceLayout;
53
+ readonly redirects?: AuthExperienceRedirects;
54
+ readonly fields?: readonly AuthCustomField[];
55
+ };
56
+ export type AuthExperienceProps = {
57
+ readonly mode?: "sign-in" | "sign-up";
58
+ readonly config?: AuthExperienceConfig;
59
+ readonly className?: string;
60
+ readonly onSubmit?: (input: AuthExperienceSubmitInput) => Promise<AuthClientSnapshot>;
61
+ readonly onSuccess?: () => void;
62
+ readonly onSignIn?: () => void;
63
+ readonly onSignUp?: () => void;
64
+ readonly onForgotPassword?: () => void;
65
+ };
66
+ declare const AuthExperience: ({ mode, config, className, onSubmit, onSuccess, onSignIn, onSignUp, onForgotPassword }: AuthExperienceProps) => import("react").JSX.Element;
67
+ export { AuthExperience };
@@ -0,0 +1,340 @@
1
+ "use client";
2
+ import { SignInForm, useAuth } from "./react.js";
3
+ import { useEffect, useId, useState } from "react";
4
+ import { Alert, Button, Checkbox, FormField, Heading, Select, Text, TextAreaInput, TextInput } from "@puzzle-ui/ui";
5
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
6
+ // src/auth/client/experience.tsx
7
+ const EMPTY_FIELDS = [];
8
+ const EMAIL_PATTERN = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
9
+ const isValidEmail = (value) => {
10
+ const email = value.trim();
11
+ return email.length <= 254 && EMAIL_PATTERN.test(email);
12
+ };
13
+ const getEmailError = (value, messages) => {
14
+ const email = value.trim();
15
+ if (!email) return messages.emailRequired;
16
+ if (!isValidEmail(email)) return "Enter a valid email address.";
17
+ return null;
18
+ };
19
+ const isSafeRedirect = (value) => Boolean(value && value.startsWith("/") && !value.startsWith("//") && !value.includes("\\") && !/[\u0000-\u001f\u007f]/.test(value));
20
+ const redirectAfterSuccess = (value) => {
21
+ if (!isSafeRedirect(value) || typeof window === "undefined") return;
22
+ window.location.assign(value);
23
+ };
24
+ const getInitialValues = (fields) => Object.fromEntries(fields.map((field) => [field.name, field.defaultValue ?? (field.type === "checkbox" ? false : "")]));
25
+ const validateFieldDefinition = (fields) => {
26
+ const names = /* @__PURE__ */ new Set();
27
+ for (const field of fields) {
28
+ if (!/^[a-zA-Z][a-zA-Z0-9_-]*$/.test(field.name)) throw new Error(`Invalid custom auth field name: "${field.name}".`);
29
+ if ([
30
+ "name",
31
+ "email",
32
+ "password",
33
+ "confirmPassword"
34
+ ].includes(field.name)) throw new Error(`Custom auth field name "${field.name}" is reserved.`);
35
+ if (names.has(field.name)) throw new Error(`Duplicate custom auth field name: "${field.name}".`);
36
+ if (field.type === "select" && (!field.options || field.options.length === 0)) throw new Error(`Select field "${field.name}" requires at least one option.`);
37
+ names.add(field.name);
38
+ }
39
+ };
40
+ const validateFields = async (fields, values) => {
41
+ const errors = {};
42
+ for (const field of fields) {
43
+ const value = values[field.name];
44
+ if (field.required && (value === void 0 || value === "" || typeof value === "boolean" && !value)) {
45
+ errors[field.name] = `${field.label} is required.`;
46
+ continue;
47
+ }
48
+ if (!field.validate) continue;
49
+ const message = await field.validate(value ?? "", values);
50
+ if (message) errors[field.name] = message;
51
+ }
52
+ return errors;
53
+ };
54
+ const AuthCustomFields = ({ fields, values, errors, disabled, onChange }) => {
55
+ const generatedId = useId();
56
+ return /* @__PURE__ */ jsx(Fragment, { children: fields.map((field) => {
57
+ const id = `${generatedId}-${field.name}`;
58
+ const value = values[field.name] ?? "";
59
+ const error = errors[field.name];
60
+ const commonProps = {
61
+ name: field.name,
62
+ disabled: disabled || field.disabled,
63
+ required: field.required
64
+ };
65
+ return /* @__PURE__ */ jsx(FormField, {
66
+ controlId: id,
67
+ label: field.label,
68
+ description: field.description,
69
+ error,
70
+ required: field.required,
71
+ disabled: commonProps.disabled,
72
+ className: field.type === "url" || field.type === "number" ? "puzzle-auth__custom-field" : void 0,
73
+ children: field.type === "textarea" ? /* @__PURE__ */ jsx(TextAreaInput, {
74
+ ...commonProps,
75
+ value: String(value),
76
+ placeholder: field.placeholder,
77
+ resizable: true,
78
+ onChange: (event) => onChange(field.name, event.currentTarget.value)
79
+ }) : field.type === "select" ? /* @__PURE__ */ jsx(Select, {
80
+ ...commonProps,
81
+ fullWidth: true,
82
+ value: String(value),
83
+ placeholder: field.placeholder ?? "Select an option",
84
+ options: field.options ?? [],
85
+ onChange: (value) => onChange(field.name, value ?? "")
86
+ }) : field.type === "checkbox" ? /* @__PURE__ */ jsx(Checkbox, {
87
+ ...commonProps,
88
+ checked: Boolean(value),
89
+ onChange: (event) => onChange(field.name, event.currentTarget.checked)
90
+ }) : field.type === "url" || field.type === "number" ? /* @__PURE__ */ jsx("input", {
91
+ ...commonProps,
92
+ type: field.type,
93
+ value: String(value),
94
+ placeholder: field.placeholder,
95
+ autoComplete: field.autoComplete,
96
+ onChange: (event) => onChange(field.name, event.currentTarget.value)
97
+ }) : /* @__PURE__ */ jsx(TextInput, {
98
+ ...commonProps,
99
+ type: field.type === "tel" ? "phone" : field.type,
100
+ value: String(value),
101
+ placeholder: field.placeholder,
102
+ autoComplete: field.autoComplete,
103
+ onChange: (event) => onChange(field.name, event.currentTarget.value)
104
+ })
105
+ }, field.name);
106
+ }) });
107
+ };
108
+ const AuthExperience = ({ mode = "sign-in", config = {}, className, onSubmit, onSuccess, onSignIn, onSignUp, onForgotPassword }) => {
109
+ const { signUp, messages } = useAuth();
110
+ const fields = config.fields ?? EMPTY_FIELDS;
111
+ const branding = config.branding;
112
+ const layout = config.layout;
113
+ const redirects = config.redirects;
114
+ validateFieldDefinition(fields);
115
+ const [values, setValues] = useState(() => getInitialValues(fields));
116
+ const [errors, setErrors] = useState({});
117
+ const [formError, setFormError] = useState(null);
118
+ const [emailError, setEmailError] = useState(null);
119
+ const [loading, setLoading] = useState(false);
120
+ const [name, setName] = useState("");
121
+ const [email, setEmail] = useState("");
122
+ const [password, setPassword] = useState("");
123
+ const [confirmation, setConfirmation] = useState("");
124
+ useEffect(() => {
125
+ setValues((current) => {
126
+ const next = {};
127
+ for (const field of fields) {
128
+ const currentValue = Object.prototype.hasOwnProperty.call(current, field.name) ? current[field.name] : void 0;
129
+ next[field.name] = currentValue ?? field.defaultValue ?? (field.type === "checkbox" ? false : "");
130
+ }
131
+ const currentNames = Object.keys(current);
132
+ const nextNames = Object.keys(next);
133
+ return currentNames.length === nextNames.length && nextNames.every((fieldName) => current[fieldName] === next[fieldName]) ? current : next;
134
+ });
135
+ setErrors((current) => {
136
+ const activeNames = new Set(fields.map((field) => field.name));
137
+ const next = Object.fromEntries(Object.entries(current).filter(([fieldName]) => activeNames.has(fieldName)));
138
+ return Object.keys(next).length === Object.keys(current).length ? current : next;
139
+ });
140
+ }, [fields]);
141
+ const updateField = (fieldName, value) => {
142
+ setValues((current) => ({
143
+ ...current,
144
+ [fieldName]: value
145
+ }));
146
+ setErrors((current) => {
147
+ if (!(fieldName in current)) return current;
148
+ const next = { ...current };
149
+ delete next[fieldName];
150
+ return next;
151
+ });
152
+ };
153
+ const handleEmailChange = (value) => {
154
+ setEmail(value);
155
+ if (emailError) setEmailError(getEmailError(value, messages));
156
+ };
157
+ const handleSubmit = async (event) => {
158
+ event.preventDefault();
159
+ setFormError(null);
160
+ setErrors({});
161
+ const nextEmailError = getEmailError(email, messages);
162
+ setEmailError(nextEmailError);
163
+ if (nextEmailError) return;
164
+ if (password !== confirmation) {
165
+ setFormError(messages.passwordMismatch);
166
+ return;
167
+ }
168
+ if (!password) {
169
+ setFormError(messages.passwordRequired);
170
+ return;
171
+ }
172
+ setLoading(true);
173
+ try {
174
+ const nextErrors = await validateFields(fields, values);
175
+ if (Object.keys(nextErrors).length > 0) {
176
+ setErrors(nextErrors);
177
+ return;
178
+ }
179
+ const registration = {
180
+ name: name.trim() || null,
181
+ email: email.trim(),
182
+ password
183
+ };
184
+ if ((onSubmit ? await onSubmit({
185
+ registration,
186
+ fields: { ...values },
187
+ signUp
188
+ }) : await signUp(registration)).status === "authenticated") {
189
+ onSuccess?.();
190
+ redirectAfterSuccess(redirects?.afterSignUp);
191
+ }
192
+ } catch (cause) {
193
+ setFormError(cause instanceof Error ? cause.message : messages.unexpectedError);
194
+ } finally {
195
+ setLoading(false);
196
+ }
197
+ };
198
+ const wrapperClassName = [
199
+ "puzzle-auth",
200
+ "puzzle-auth-experience",
201
+ layout?.className,
202
+ className
203
+ ].filter(Boolean).join(" ");
204
+ const contentClassName = ["puzzle-auth-experience__content", layout?.contentClassName].filter(Boolean).join(" ");
205
+ const style = {
206
+ "--puzzle-auth-experience-width": layout?.width === "sm" ? "24rem" : layout?.width === "lg" ? "48rem" : layout?.width === "full" ? "100%" : "32rem",
207
+ "--puzzle-auth-experience-align": layout?.align === "left" ? "flex-start" : "center"
208
+ };
209
+ return /* @__PURE__ */ jsxs("section", {
210
+ className: wrapperClassName,
211
+ style,
212
+ children: [
213
+ layout?.header,
214
+ /* @__PURE__ */ jsxs("div", {
215
+ className: "puzzle-auth-experience__branding",
216
+ children: [
217
+ branding?.logo,
218
+ branding?.title ? /* @__PURE__ */ jsx(Heading, {
219
+ as: "h1",
220
+ level: "headline2",
221
+ children: branding.title
222
+ }) : null,
223
+ branding?.description ? /* @__PURE__ */ jsx(Text, {
224
+ as: "p",
225
+ children: branding.description
226
+ }) : null
227
+ ]
228
+ }),
229
+ /* @__PURE__ */ jsx("div", {
230
+ className: contentClassName,
231
+ children: mode === "sign-in" ? /* @__PURE__ */ jsx(SignInForm, {
232
+ redirectTo: redirects?.afterSignIn,
233
+ onSuccess,
234
+ onForgotPassword,
235
+ onSignUp
236
+ }) : /* @__PURE__ */ jsxs(Fragment, { children: [
237
+ formError ? /* @__PURE__ */ jsx("div", {
238
+ role: "alert",
239
+ "aria-live": "assertive",
240
+ children: /* @__PURE__ */ jsx(Alert, {
241
+ severity: "error",
242
+ children: formError
243
+ })
244
+ }) : null,
245
+ /* @__PURE__ */ jsxs("form", {
246
+ className: "puzzle-auth__form",
247
+ onSubmit: (event) => void handleSubmit(event),
248
+ noValidate: true,
249
+ children: [
250
+ /* @__PURE__ */ jsx(FormField, {
251
+ label: messages.name,
252
+ controlId: "puzzle-auth-experience-name",
253
+ children: /* @__PURE__ */ jsx(TextInput, {
254
+ name: "name",
255
+ type: "text",
256
+ autoComplete: "name",
257
+ value: name,
258
+ disabled: loading,
259
+ onChange: (event) => setName(event.currentTarget.value)
260
+ })
261
+ }),
262
+ /* @__PURE__ */ jsx(FormField, {
263
+ label: messages.email,
264
+ controlId: "puzzle-auth-experience-email",
265
+ required: true,
266
+ error: emailError,
267
+ children: /* @__PURE__ */ jsx(TextInput, {
268
+ name: "email",
269
+ type: "email",
270
+ autoComplete: "email",
271
+ required: true,
272
+ value: email,
273
+ disabled: loading,
274
+ onChange: (event) => handleEmailChange(event.currentTarget.value)
275
+ })
276
+ }),
277
+ /* @__PURE__ */ jsx(FormField, {
278
+ label: messages.password,
279
+ controlId: "puzzle-auth-experience-password",
280
+ required: true,
281
+ children: /* @__PURE__ */ jsx(TextInput, {
282
+ name: "new-password",
283
+ type: "password",
284
+ suffix: null,
285
+ autoComplete: "new-password",
286
+ required: true,
287
+ value: password,
288
+ disabled: loading,
289
+ onChange: (event) => setPassword(event.currentTarget.value)
290
+ })
291
+ }),
292
+ /* @__PURE__ */ jsx(FormField, {
293
+ label: messages.confirmPassword,
294
+ controlId: "puzzle-auth-experience-confirm",
295
+ required: true,
296
+ children: /* @__PURE__ */ jsx(TextInput, {
297
+ name: "confirm-password",
298
+ type: "password",
299
+ suffix: null,
300
+ autoComplete: "new-password",
301
+ required: true,
302
+ value: confirmation,
303
+ disabled: loading,
304
+ onChange: (event) => setConfirmation(event.currentTarget.value)
305
+ })
306
+ }),
307
+ /* @__PURE__ */ jsx(AuthCustomFields, {
308
+ fields,
309
+ values,
310
+ errors,
311
+ disabled: loading,
312
+ onChange: updateField
313
+ }),
314
+ /* @__PURE__ */ jsx(Button, {
315
+ type: "submit",
316
+ fullWidth: true,
317
+ loading,
318
+ disabled: loading || !email.trim() || !password || !confirmation,
319
+ children: loading ? messages.signingUp : messages.createAccount
320
+ })
321
+ ]
322
+ }),
323
+ onSignIn ? /* @__PURE__ */ jsxs("footer", {
324
+ className: "puzzle-auth__footer",
325
+ children: [/* @__PURE__ */ jsx(Text, { children: messages.existingAccount }), /* @__PURE__ */ jsx(Button, {
326
+ type: "button",
327
+ variant: "link",
328
+ onClick: onSignIn,
329
+ children: messages.signIn
330
+ })]
331
+ }) : null
332
+ ] })
333
+ }),
334
+ layout?.footer
335
+ ]
336
+ });
337
+ };
338
+ export { AuthExperience };
339
+
340
+ //# sourceMappingURL=experience.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"experience.js","names":[],"sources":["../../../src/auth/client/experience.tsx"],"sourcesContent":["\"use client\";\n\nimport {\n useEffect,\n useId,\n useState,\n type CSSProperties,\n type FormEvent,\n type ReactNode,\n} from \"react\";\n\nimport {\n Alert,\n Button,\n Checkbox,\n FormField,\n Heading,\n Select,\n Text,\n TextAreaInput,\n TextInput,\n} from \"@puzzle-ui/ui\";\n\nimport type { AuthClient, AuthClientSnapshot } from \"./index\";\nimport { SignInForm, useAuth } from \"./react\";\n\nexport type AuthFieldValue = string | boolean;\n\nexport type AuthFieldValues = Readonly<Record<string, AuthFieldValue>>;\n\nexport type AuthFieldOption = {\n readonly label: string;\n readonly value: string;\n};\n\nexport type AuthCustomField = {\n readonly name: string;\n readonly label: string;\n readonly type:\n | \"text\"\n | \"email\"\n | \"tel\"\n | \"url\"\n | \"number\"\n | \"checkbox\"\n | \"select\"\n | \"textarea\";\n readonly description?: string;\n readonly placeholder?: string;\n readonly autoComplete?: string;\n readonly required?: boolean;\n readonly disabled?: boolean;\n readonly defaultValue?: AuthFieldValue;\n readonly options?: readonly AuthFieldOption[];\n readonly validate?: (\n value: AuthFieldValue,\n values: AuthFieldValues,\n ) => string | null | undefined | Promise<string | null | undefined>;\n};\n\nexport type AuthRegistrationInput = {\n readonly name: string | null;\n readonly email: string;\n readonly password: string;\n};\n\nexport type AuthExperienceSubmitInput = {\n readonly registration: AuthRegistrationInput;\n readonly fields: AuthFieldValues;\n readonly signUp: AuthClient[\"register\"];\n};\n\nexport type AuthExperienceBranding = {\n readonly logo?: ReactNode;\n readonly title?: ReactNode;\n readonly description?: ReactNode;\n};\n\nexport type AuthExperienceLayout = {\n readonly className?: string;\n readonly contentClassName?: string;\n readonly header?: ReactNode;\n readonly footer?: ReactNode;\n readonly width?: \"sm\" | \"md\" | \"lg\" | \"full\";\n readonly align?: \"left\" | \"center\";\n};\n\nexport type AuthExperienceRedirects = {\n readonly afterSignIn?: string;\n readonly afterSignUp?: string;\n};\n\nexport type AuthExperienceConfig = {\n readonly branding?: AuthExperienceBranding;\n readonly layout?: AuthExperienceLayout;\n readonly redirects?: AuthExperienceRedirects;\n readonly fields?: readonly AuthCustomField[];\n};\n\nexport type AuthExperienceProps = {\n readonly mode?: \"sign-in\" | \"sign-up\";\n readonly config?: AuthExperienceConfig;\n readonly className?: string;\n readonly onSubmit?: (\n input: AuthExperienceSubmitInput,\n ) => Promise<AuthClientSnapshot>;\n readonly onSuccess?: () => void;\n readonly onSignIn?: () => void;\n readonly onSignUp?: () => void;\n readonly onForgotPassword?: () => void;\n};\n\ntype FieldErrors = Readonly<Record<string, string>>;\n\nconst EMPTY_FIELDS: readonly AuthCustomField[] = [];\n\nconst EMAIL_PATTERN = /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/;\n\nconst isValidEmail = (value: string): boolean => {\n const email = value.trim();\n\n return email.length <= 254 && EMAIL_PATTERN.test(email);\n};\n\nconst getEmailError = (\n value: string,\n messages: ReturnType<typeof useAuth>[\"messages\"],\n): string | null => {\n const email = value.trim();\n\n if (!email) {\n return messages.emailRequired;\n }\n\n if (!isValidEmail(email)) {\n return \"Enter a valid email address.\";\n }\n\n return null;\n};\n\nconst isSafeRedirect = (value: string | undefined): value is string =>\n Boolean(\n value &&\n value.startsWith(\"/\") &&\n !value.startsWith(\"//\") &&\n !value.includes(\"\\\\\") &&\n !/[\\u0000-\\u001f\\u007f]/.test(value),\n );\n\nconst redirectAfterSuccess = (value: string | undefined) => {\n if (!isSafeRedirect(value) || typeof window === \"undefined\") {\n return;\n }\n\n window.location.assign(value);\n};\n\nconst getInitialValues = (\n fields: readonly AuthCustomField[],\n): Record<string, AuthFieldValue> =>\n Object.fromEntries(\n fields.map((field) => [\n field.name,\n field.defaultValue ?? (field.type === \"checkbox\" ? false : \"\"),\n ]),\n );\n\nconst validateFieldDefinition = (fields: readonly AuthCustomField[]): void => {\n const names = new Set<string>();\n\n for (const field of fields) {\n if (!/^[a-zA-Z][a-zA-Z0-9_-]*$/.test(field.name)) {\n throw new Error(`Invalid custom auth field name: \"${field.name}\".`);\n }\n\n if ([\"name\", \"email\", \"password\", \"confirmPassword\"].includes(field.name)) {\n throw new Error(`Custom auth field name \"${field.name}\" is reserved.`);\n }\n\n if (names.has(field.name)) {\n throw new Error(`Duplicate custom auth field name: \"${field.name}\".`);\n }\n\n if (\n field.type === \"select\" &&\n (!field.options || field.options.length === 0)\n ) {\n throw new Error(\n `Select field \"${field.name}\" requires at least one option.`,\n );\n }\n\n names.add(field.name);\n }\n};\n\nconst validateFields = async (\n fields: readonly AuthCustomField[],\n values: AuthFieldValues,\n): Promise<FieldErrors> => {\n const errors: Record<string, string> = {};\n\n for (const field of fields) {\n const value = values[field.name];\n\n if (\n field.required &&\n (value === undefined ||\n value === \"\" ||\n (typeof value === \"boolean\" && !value))\n ) {\n errors[field.name] = `${field.label} is required.`;\n continue;\n }\n\n if (!field.validate) continue;\n\n const message = await field.validate(value ?? \"\", values);\n\n if (message) {\n errors[field.name] = message;\n }\n }\n\n return errors;\n};\n\nconst AuthCustomFields = ({\n fields,\n values,\n errors,\n disabled,\n onChange,\n}: {\n readonly fields: readonly AuthCustomField[];\n readonly values: AuthFieldValues;\n readonly errors: FieldErrors;\n readonly disabled: boolean;\n readonly onChange: (name: string, value: AuthFieldValue) => void;\n}) => {\n const generatedId = useId();\n\n return (\n <>\n {fields.map((field) => {\n const id = `${generatedId}-${field.name}`;\n const value = values[field.name] ?? \"\";\n const error = errors[field.name];\n\n const commonProps = {\n name: field.name,\n disabled: disabled || field.disabled,\n required: field.required,\n };\n\n return (\n <FormField\n key={field.name}\n controlId={id}\n label={field.label}\n description={field.description}\n error={error}\n required={field.required}\n disabled={commonProps.disabled}\n className={\n field.type === \"url\" || field.type === \"number\"\n ? \"puzzle-auth__custom-field\"\n : undefined\n }\n >\n {field.type === \"textarea\" ? (\n <TextAreaInput\n {...commonProps}\n value={String(value)}\n placeholder={field.placeholder}\n resizable\n onChange={(event) =>\n onChange(field.name, event.currentTarget.value)\n }\n />\n ) : field.type === \"select\" ? (\n <Select\n {...commonProps}\n fullWidth\n value={String(value)}\n placeholder={field.placeholder ?? \"Select an option\"}\n options={field.options ?? []}\n onChange={(value) => onChange(field.name, value ?? \"\")}\n />\n ) : field.type === \"checkbox\" ? (\n <Checkbox\n {...commonProps}\n checked={Boolean(value)}\n onChange={(event) =>\n onChange(field.name, event.currentTarget.checked)\n }\n />\n ) : field.type === \"url\" || field.type === \"number\" ? (\n <input\n {...commonProps}\n type={field.type}\n value={String(value)}\n placeholder={field.placeholder}\n autoComplete={field.autoComplete}\n onChange={(event) =>\n onChange(field.name, event.currentTarget.value)\n }\n />\n ) : (\n <TextInput\n {...commonProps}\n type={field.type === \"tel\" ? \"phone\" : field.type}\n value={String(value)}\n placeholder={field.placeholder}\n autoComplete={field.autoComplete}\n onChange={(event) =>\n onChange(field.name, event.currentTarget.value)\n }\n />\n )}\n </FormField>\n );\n })}\n </>\n );\n};\n\nconst AuthExperience = ({\n mode = \"sign-in\",\n config = {},\n className,\n onSubmit,\n onSuccess,\n onSignIn,\n onSignUp,\n onForgotPassword,\n}: AuthExperienceProps) => {\n const { signUp, messages } = useAuth();\n\n const fields = config.fields ?? EMPTY_FIELDS;\n const branding = config.branding;\n const layout = config.layout;\n const redirects = config.redirects;\n\n validateFieldDefinition(fields);\n\n const [values, setValues] = useState<Record<string, AuthFieldValue>>(() =>\n getInitialValues(fields),\n );\n const [errors, setErrors] = useState<FieldErrors>({});\n const [formError, setFormError] = useState<string | null>(null);\n const [emailError, setEmailError] = useState<string | null>(null);\n const [loading, setLoading] = useState(false);\n\n const [name, setName] = useState(\"\");\n const [email, setEmail] = useState(\"\");\n const [password, setPassword] = useState(\"\");\n const [confirmation, setConfirmation] = useState(\"\");\n\n useEffect(() => {\n setValues((current) => {\n const next: Record<string, AuthFieldValue> = {};\n\n for (const field of fields) {\n const currentValue = Object.prototype.hasOwnProperty.call(\n current,\n field.name,\n )\n ? current[field.name]\n : undefined;\n\n next[field.name] =\n currentValue ??\n field.defaultValue ??\n (field.type === \"checkbox\" ? false : \"\");\n }\n\n const currentNames = Object.keys(current);\n const nextNames = Object.keys(next);\n\n const unchanged =\n currentNames.length === nextNames.length &&\n nextNames.every((fieldName) => current[fieldName] === next[fieldName]);\n\n return unchanged ? current : next;\n });\n\n setErrors((current) => {\n const activeNames = new Set(fields.map((field) => field.name));\n\n const next = Object.fromEntries(\n Object.entries(current).filter(([fieldName]) =>\n activeNames.has(fieldName),\n ),\n );\n\n return Object.keys(next).length === Object.keys(current).length\n ? current\n : next;\n });\n }, [fields]);\n\n const updateField = (fieldName: string, value: AuthFieldValue) => {\n setValues((current) => ({\n ...current,\n [fieldName]: value,\n }));\n\n setErrors((current) => {\n if (!(fieldName in current)) return current;\n\n const next = { ...current };\n delete next[fieldName];\n return next;\n });\n };\n\n const handleEmailChange = (value: string) => {\n setEmail(value);\n\n if (emailError) {\n setEmailError(getEmailError(value, messages));\n }\n };\n\n const handleSubmit = async (event: FormEvent<HTMLFormElement>) => {\n event.preventDefault();\n setFormError(null);\n setErrors({});\n\n const nextEmailError = getEmailError(email, messages);\n\n setEmailError(nextEmailError);\n\n if (nextEmailError) {\n return;\n }\n\n if (password !== confirmation) {\n setFormError(messages.passwordMismatch);\n return;\n }\n\n if (!password) {\n setFormError(messages.passwordRequired);\n return;\n }\n\n setLoading(true);\n\n try {\n const nextErrors = await validateFields(fields, values);\n\n if (Object.keys(nextErrors).length > 0) {\n setErrors(nextErrors);\n return;\n }\n\n const registration: AuthRegistrationInput = {\n name: name.trim() || null,\n email: email.trim(),\n password,\n };\n\n const result = onSubmit\n ? await onSubmit({\n registration,\n fields: { ...values },\n signUp,\n })\n : await signUp(registration);\n\n if (result.status === \"authenticated\") {\n onSuccess?.();\n redirectAfterSuccess(redirects?.afterSignUp);\n }\n } catch (cause) {\n setFormError(\n cause instanceof Error ? cause.message : messages.unexpectedError,\n );\n } finally {\n setLoading(false);\n }\n };\n\n const wrapperClassName = [\n \"puzzle-auth\",\n \"puzzle-auth-experience\",\n layout?.className,\n className,\n ]\n .filter(Boolean)\n .join(\" \");\n\n const contentClassName = [\n \"puzzle-auth-experience__content\",\n layout?.contentClassName,\n ]\n .filter(Boolean)\n .join(\" \");\n\n const style = {\n \"--puzzle-auth-experience-width\":\n layout?.width === \"sm\"\n ? \"24rem\"\n : layout?.width === \"lg\"\n ? \"48rem\"\n : layout?.width === \"full\"\n ? \"100%\"\n : \"32rem\",\n \"--puzzle-auth-experience-align\":\n layout?.align === \"left\" ? \"flex-start\" : \"center\",\n } as CSSProperties;\n\n return (\n <section className={wrapperClassName} style={style}>\n {layout?.header}\n\n <div className=\"puzzle-auth-experience__branding\">\n {branding?.logo}\n {branding?.title ? (\n <Heading as=\"h1\" level=\"headline2\">\n {branding.title}\n </Heading>\n ) : null}\n {branding?.description ? <Text as=\"p\">{branding.description}</Text> : null}\n </div>\n\n <div className={contentClassName}>\n {mode === \"sign-in\" ? (\n <SignInForm\n redirectTo={redirects?.afterSignIn}\n onSuccess={onSuccess}\n onForgotPassword={onForgotPassword}\n onSignUp={onSignUp}\n />\n ) : (\n <>\n {formError ? (\n <div role=\"alert\" aria-live=\"assertive\">\n <Alert severity=\"error\">{formError}</Alert>\n </div>\n ) : null}\n\n <form\n className=\"puzzle-auth__form\"\n onSubmit={(event) => void handleSubmit(event)}\n noValidate\n >\n <FormField\n label={messages.name}\n controlId=\"puzzle-auth-experience-name\"\n >\n <TextInput\n name=\"name\"\n type=\"text\"\n autoComplete=\"name\"\n value={name}\n disabled={loading}\n onChange={(event) => setName(event.currentTarget.value)}\n />\n </FormField>\n\n <FormField\n label={messages.email}\n controlId=\"puzzle-auth-experience-email\"\n required\n error={emailError}\n >\n <TextInput\n name=\"email\"\n type=\"email\"\n autoComplete=\"email\"\n required\n value={email}\n disabled={loading}\n onChange={(event) =>\n handleEmailChange(event.currentTarget.value)\n }\n />\n\n </FormField>\n\n <FormField\n label={messages.password}\n controlId=\"puzzle-auth-experience-password\"\n required\n >\n <TextInput\n name=\"new-password\"\n type=\"password\"\n suffix={null}\n autoComplete=\"new-password\"\n required\n value={password}\n disabled={loading}\n onChange={(event) => setPassword(event.currentTarget.value)}\n />\n </FormField>\n\n <FormField\n label={messages.confirmPassword}\n controlId=\"puzzle-auth-experience-confirm\"\n required\n >\n <TextInput\n name=\"confirm-password\"\n type=\"password\"\n suffix={null}\n autoComplete=\"new-password\"\n required\n value={confirmation}\n disabled={loading}\n onChange={(event) =>\n setConfirmation(event.currentTarget.value)\n }\n />\n </FormField>\n\n <AuthCustomFields\n fields={fields}\n values={values}\n errors={errors}\n disabled={loading}\n onChange={updateField}\n />\n\n <Button\n type=\"submit\"\n fullWidth\n loading={loading}\n disabled={\n loading || !email.trim() || !password || !confirmation\n }\n >\n {loading ? messages.signingUp : messages.createAccount}\n </Button>\n </form>\n\n {onSignIn ? (\n <footer className=\"puzzle-auth__footer\">\n <Text>{messages.existingAccount}</Text>\n <Button\n type=\"button\"\n variant=\"link\"\n onClick={onSignIn}\n >\n {messages.signIn}\n </Button>\n </footer>\n ) : null}\n </>\n )}\n </div>\n\n {layout?.footer}\n </section>\n );\n};\n\nexport { AuthExperience };\n"],"mappings":";;;;;;AAkHA,MAAM,eAA2C,CAAC;AAElD,MAAM,gBAAgB;AAEtB,MAAM,gBAAgB,UAA2B;CAC/C,MAAM,QAAQ,MAAM,KAAK;CAEzB,OAAO,MAAM,UAAU,OAAO,cAAc,KAAK,KAAK;AACxD;AAEA,MAAM,iBACJ,OACA,aACkB;CAClB,MAAM,QAAQ,MAAM,KAAK;CAEzB,IAAI,CAAC,OACH,OAAO,SAAS;CAGlB,IAAI,CAAC,aAAa,KAAK,GACrB,OAAO;CAGT,OAAO;AACT;AAEA,MAAM,kBAAkB,UACtB,QACE,SACA,MAAM,WAAW,GAAG,KACpB,CAAC,MAAM,WAAW,IAAI,KACtB,CAAC,MAAM,SAAS,IAAI,KACpB,CAAC,wBAAwB,KAAK,KAAK,CACrC;AAEF,MAAM,wBAAwB,UAA8B;CAC1D,IAAI,CAAC,eAAe,KAAK,KAAK,OAAO,WAAW,aAC9C;CAGF,OAAO,SAAS,OAAO,KAAK;AAC9B;AAEA,MAAM,oBACJ,WAEA,OAAO,YACL,OAAO,KAAK,UAAU,CACpB,MAAM,MACN,MAAM,iBAAiB,MAAM,SAAS,aAAa,QAAQ,GAC7D,CAAC,CACH;AAEF,MAAM,2BAA2B,WAA6C;CAC5E,MAAM,wBAAQ,IAAI,IAAY;CAE9B,KAAK,MAAM,SAAS,QAAQ;EAC1B,IAAI,CAAC,2BAA2B,KAAK,MAAM,IAAI,GAC7C,MAAM,IAAI,MAAM,oCAAoC,MAAM,KAAK,GAAG;EAGpE,IAAI;GAAC;GAAQ;GAAS;GAAY;EAAiB,CAAC,CAAC,SAAS,MAAM,IAAI,GACtE,MAAM,IAAI,MAAM,2BAA2B,MAAM,KAAK,eAAe;EAGvE,IAAI,MAAM,IAAI,MAAM,IAAI,GACtB,MAAM,IAAI,MAAM,sCAAsC,MAAM,KAAK,GAAG;EAGtE,IACE,MAAM,SAAS,aACd,CAAC,MAAM,WAAW,MAAM,QAAQ,WAAW,IAE5C,MAAM,IAAI,MACR,iBAAiB,MAAM,KAAK,gCAC9B;EAGF,MAAM,IAAI,MAAM,IAAI;CACtB;AACF;AAEA,MAAM,iBAAiB,OACrB,QACA,WACyB;CACzB,MAAM,SAAiC,CAAC;CAExC,KAAK,MAAM,SAAS,QAAQ;EAC1B,MAAM,QAAQ,OAAO,MAAM;EAE3B,IACE,MAAM,aACL,UAAU,KAAA,KACT,UAAU,MACT,OAAO,UAAU,aAAa,CAAC,QAClC;GACA,OAAO,MAAM,QAAQ,GAAG,MAAM,MAAM;GACpC;EACF;EAEA,IAAI,CAAC,MAAM,UAAU;EAErB,MAAM,UAAU,MAAM,MAAM,SAAS,SAAS,IAAI,MAAM;EAExD,IAAI,SACF,OAAO,MAAM,QAAQ;CAEzB;CAEA,OAAO;AACT;AAEA,MAAM,oBAAoB,EACxB,QACA,QACA,QACA,UACA,eAOI;CACJ,MAAM,cAAc,MAAM;CAE1B,OACE,oBAAA,UAAA,EAAA,UACG,OAAO,KAAK,UAAU;EACrB,MAAM,KAAK,GAAG,YAAY,GAAG,MAAM;EACnC,MAAM,QAAQ,OAAO,MAAM,SAAS;EACpC,MAAM,QAAQ,OAAO,MAAM;EAE3B,MAAM,cAAc;GAClB,MAAM,MAAM;GACZ,UAAU,YAAY,MAAM;GAC5B,UAAU,MAAM;EAClB;EAEA,OACE,oBAAC,WAAD;GAEE,WAAW;GACX,OAAO,MAAM;GACb,aAAa,MAAM;GACZ;GACP,UAAU,MAAM;GAChB,UAAU,YAAY;GACtB,WACE,MAAM,SAAS,SAAS,MAAM,SAAS,WACnC,8BACA,KAAA;GAGL,UAAA,MAAM,SAAS,aACd,oBAAC,eAAD;IACE,GAAI;IACJ,OAAO,OAAO,KAAK;IACnB,aAAa,MAAM;IACnB,WAAA;IACA,WAAW,UACT,SAAS,MAAM,MAAM,MAAM,cAAc,KAAK;GAEjD,CAAA,IACC,MAAM,SAAS,WACjB,oBAAC,QAAD;IACE,GAAI;IACJ,WAAA;IACA,OAAO,OAAO,KAAK;IACnB,aAAa,MAAM,eAAe;IAClC,SAAS,MAAM,WAAW,CAAC;IAC3B,WAAW,UAAU,SAAS,MAAM,MAAM,SAAS,EAAE;GACtD,CAAA,IACC,MAAM,SAAS,aACjB,oBAAC,UAAD;IACE,GAAI;IACJ,SAAS,QAAQ,KAAK;IACtB,WAAW,UACT,SAAS,MAAM,MAAM,MAAM,cAAc,OAAO;GAEnD,CAAA,IACC,MAAM,SAAS,SAAS,MAAM,SAAS,WACzC,oBAAC,SAAD;IACE,GAAI;IACJ,MAAM,MAAM;IACZ,OAAO,OAAO,KAAK;IACnB,aAAa,MAAM;IACnB,cAAc,MAAM;IACpB,WAAW,UACT,SAAS,MAAM,MAAM,MAAM,cAAc,KAAK;GAEjD,CAAA,IAED,oBAAC,WAAD;IACE,GAAI;IACJ,MAAM,MAAM,SAAS,QAAQ,UAAU,MAAM;IAC7C,OAAO,OAAO,KAAK;IACnB,aAAa,MAAM;IACnB,cAAc,MAAM;IACpB,WAAW,UACT,SAAS,MAAM,MAAM,MAAM,cAAc,KAAK;GAEjD,CAAA;EAEM,GA/DJ,MAAM,IA+DF;CAEf,CAAC,EACD,CAAA;AAEN;AAEA,MAAM,kBAAkB,EACtB,OAAO,WACP,SAAS,CAAC,GACV,WACA,UACA,WACA,UACA,UACA,uBACyB;CACzB,MAAM,EAAE,QAAQ,aAAa,QAAQ;CAErC,MAAM,SAAS,OAAO,UAAU;CAChC,MAAM,WAAW,OAAO;CACxB,MAAM,SAAS,OAAO;CACtB,MAAM,YAAY,OAAO;CAEzB,wBAAwB,MAAM;CAE9B,MAAM,CAAC,QAAQ,aAAa,eAC1B,iBAAiB,MAAM,CACzB;CACA,MAAM,CAAC,QAAQ,aAAa,SAAsB,CAAC,CAAC;CACpD,MAAM,CAAC,WAAW,gBAAgB,SAAwB,IAAI;CAC9D,MAAM,CAAC,YAAY,iBAAiB,SAAwB,IAAI;CAChE,MAAM,CAAC,SAAS,cAAc,SAAS,KAAK;CAE5C,MAAM,CAAC,MAAM,WAAW,SAAS,EAAE;CACnC,MAAM,CAAC,OAAO,YAAY,SAAS,EAAE;CACrC,MAAM,CAAC,UAAU,eAAe,SAAS,EAAE;CAC3C,MAAM,CAAC,cAAc,mBAAmB,SAAS,EAAE;CAEnD,gBAAgB;EACd,WAAW,YAAY;GACrB,MAAM,OAAuC,CAAC;GAE9C,KAAK,MAAM,SAAS,QAAQ;IAC1B,MAAM,eAAe,OAAO,UAAU,eAAe,KACnD,SACA,MAAM,IACR,IACI,QAAQ,MAAM,QACd,KAAA;IAEJ,KAAK,MAAM,QACT,gBACA,MAAM,iBACL,MAAM,SAAS,aAAa,QAAQ;GACzC;GAEA,MAAM,eAAe,OAAO,KAAK,OAAO;GACxC,MAAM,YAAY,OAAO,KAAK,IAAI;GAMlC,OAHE,aAAa,WAAW,UAAU,UAClC,UAAU,OAAO,cAAc,QAAQ,eAAe,KAAK,UAAU,IAEpD,UAAU;EAC/B,CAAC;EAED,WAAW,YAAY;GACrB,MAAM,cAAc,IAAI,IAAI,OAAO,KAAK,UAAU,MAAM,IAAI,CAAC;GAE7D,MAAM,OAAO,OAAO,YAClB,OAAO,QAAQ,OAAO,CAAC,CAAC,QAAQ,CAAC,eAC/B,YAAY,IAAI,SAAS,CAC3B,CACF;GAEA,OAAO,OAAO,KAAK,IAAI,CAAC,CAAC,WAAW,OAAO,KAAK,OAAO,CAAC,CAAC,SACrD,UACA;EACN,CAAC;CACH,GAAG,CAAC,MAAM,CAAC;CAEX,MAAM,eAAe,WAAmB,UAA0B;EAChE,WAAW,aAAa;GACtB,GAAG;IACF,YAAY;EACf,EAAE;EAEF,WAAW,YAAY;GACrB,IAAI,EAAE,aAAa,UAAU,OAAO;GAEpC,MAAM,OAAO,EAAE,GAAG,QAAQ;GAC1B,OAAO,KAAK;GACZ,OAAO;EACT,CAAC;CACH;CAEA,MAAM,qBAAqB,UAAkB;EAC3C,SAAS,KAAK;EAEd,IAAI,YACF,cAAc,cAAc,OAAO,QAAQ,CAAC;CAEhD;CAEA,MAAM,eAAe,OAAO,UAAsC;EAChE,MAAM,eAAe;EACrB,aAAa,IAAI;EACjB,UAAU,CAAC,CAAC;EAEZ,MAAM,iBAAiB,cAAc,OAAO,QAAQ;EAEpD,cAAc,cAAc;EAE5B,IAAI,gBACF;EAGF,IAAI,aAAa,cAAc;GAC7B,aAAa,SAAS,gBAAgB;GACtC;EACF;EAEA,IAAI,CAAC,UAAU;GACb,aAAa,SAAS,gBAAgB;GACtC;EACF;EAEA,WAAW,IAAI;EAEf,IAAI;GACF,MAAM,aAAa,MAAM,eAAe,QAAQ,MAAM;GAEtD,IAAI,OAAO,KAAK,UAAU,CAAC,CAAC,SAAS,GAAG;IACtC,UAAU,UAAU;IACpB;GACF;GAEA,MAAM,eAAsC;IAC1C,MAAM,KAAK,KAAK,KAAK;IACrB,OAAO,MAAM,KAAK;IAClB;GACF;GAUA,KARe,WACX,MAAM,SAAS;IACb;IACA,QAAQ,EAAE,GAAG,OAAO;IACpB;GACF,CAAC,IACD,MAAM,OAAO,YAAY,EAAA,CAElB,WAAW,iBAAiB;IACrC,YAAY;IACZ,qBAAqB,WAAW,WAAW;GAC7C;EACF,SAAS,OAAO;GACd,aACE,iBAAiB,QAAQ,MAAM,UAAU,SAAS,eACpD;EACF,UAAU;GACR,WAAW,KAAK;EAClB;CACF;CAEA,MAAM,mBAAmB;EACvB;EACA;EACA,QAAQ;EACR;CACF,CAAC,CACE,OAAO,OAAO,CAAC,CACf,KAAK,GAAG;CAEX,MAAM,mBAAmB,CACvB,mCACA,QAAQ,gBACV,CAAC,CACE,OAAO,OAAO,CAAC,CACf,KAAK,GAAG;CAEX,MAAM,QAAQ;EACZ,kCACE,QAAQ,UAAU,OACd,UACA,QAAQ,UAAU,OAChB,UACA,QAAQ,UAAU,SAChB,SACA;EACV,kCACE,QAAQ,UAAU,SAAS,eAAe;CAC9C;CAEA,OACE,qBAAC,WAAD;EAAS,WAAW;EAAyB;EAA7C,UAAA;GACG,QAAQ;GAET,qBAAC,OAAD;IAAK,WAAU;IAAf,UAAA;KACG,UAAU;KACV,UAAU,QACT,oBAAC,SAAD;MAAS,IAAG;MAAK,OAAM;MACpB,UAAA,SAAS;KACH,CAAA,IACP;KACH,UAAU,cAAc,oBAAC,MAAD;MAAM,IAAG;MAAK,UAAA,SAAS;KAAkB,CAAA,IAAI;IACnE;;GAEL,oBAAC,OAAD;IAAK,WAAW;IACb,UAAA,SAAS,YACR,oBAAC,YAAD;KACE,YAAY,WAAW;KACZ;KACO;KACR;IACX,CAAA,IAED,qBAAA,UAAA,EAAA,UAAA;KACG,YACC,oBAAC,OAAD;MAAK,MAAK;MAAQ,aAAU;MAC1B,UAAA,oBAAC,OAAD;OAAO,UAAS;OAAS,UAAA;MAAiB,CAAA;KACvC,CAAA,IACH;KAEJ,qBAAC,QAAD;MACE,WAAU;MACV,WAAW,UAAU,KAAK,aAAa,KAAK;MAC5C,YAAA;MAHF,UAAA;OAKE,oBAAC,WAAD;QACE,OAAO,SAAS;QAChB,WAAU;QAEV,UAAA,oBAAC,WAAD;SACE,MAAK;SACL,MAAK;SACL,cAAa;SACb,OAAO;SACP,UAAU;SACV,WAAW,UAAU,QAAQ,MAAM,cAAc,KAAK;QACvD,CAAA;OACQ,CAAA;OAEX,oBAAC,WAAD;QACE,OAAO,SAAS;QAChB,WAAU;QACV,UAAA;QACA,OAAO;QAEP,UAAA,oBAAC,WAAD;SACE,MAAK;SACL,MAAK;SACL,cAAa;SACb,UAAA;SACA,OAAO;SACP,UAAU;SACV,WAAW,UACT,kBAAkB,MAAM,cAAc,KAAK;QAE9C,CAAA;OAEQ,CAAA;OAEX,oBAAC,WAAD;QACE,OAAO,SAAS;QAChB,WAAU;QACV,UAAA;QAEA,UAAA,oBAAC,WAAD;SACE,MAAK;SACL,MAAK;SACL,QAAQ;SACR,cAAa;SACb,UAAA;SACA,OAAO;SACP,UAAU;SACV,WAAW,UAAU,YAAY,MAAM,cAAc,KAAK;QAC3D,CAAA;OACQ,CAAA;OAEX,oBAAC,WAAD;QACE,OAAO,SAAS;QAChB,WAAU;QACV,UAAA;QAEA,UAAA,oBAAC,WAAD;SACE,MAAK;SACL,MAAK;SACL,QAAQ;SACR,cAAa;SACb,UAAA;SACA,OAAO;SACP,UAAU;SACV,WAAW,UACT,gBAAgB,MAAM,cAAc,KAAK;QAE5C,CAAA;OACQ,CAAA;OAEX,oBAAC,kBAAD;QACU;QACA;QACA;QACR,UAAU;QACV,UAAU;OACX,CAAA;OAED,oBAAC,QAAD;QACE,MAAK;QACL,WAAA;QACS;QACT,UACE,WAAW,CAAC,MAAM,KAAK,KAAK,CAAC,YAAY,CAAC;QAG3C,UAAA,UAAU,SAAS,YAAY,SAAS;OACnC,CAAA;MACJ;;KAEL,WACC,qBAAC,UAAD;MAAQ,WAAU;MAAlB,UAAA,CACE,oBAAC,MAAD,EAAA,UAAO,SAAS,gBAAsB,CAAA,GACtC,oBAAC,QAAD;OACE,MAAK;OACL,SAAQ;OACR,SAAS;OAER,UAAA,SAAS;MACJ,CAAA,CACF;KACN,CAAA,IAAA;IACJ,EAAA,CAAA;GAED,CAAA;GAEJ,QAAQ;EACF;;AAEb"}