// react/AuthGuard.tsx import { useEffect, useState } from "react"; import { Center, Spinner } from "@chakra-ui/react"; import { Navigate } from "react-router-dom"; import { Fragment, jsx } from "react/jsx-runtime"; function AuthGuard({ children, fetchCurrentUser, redirectTo = "/login", loadingFallback, authenticatedWrapper }) { const [state, setState] = useState({ loading: true, authenticated: false }); useEffect(() => { let cancelled = false; fetchCurrentUser().then(() => { if (!cancelled) { setState({ loading: false, authenticated: true }); } }).catch(() => { if (!cancelled) { setState({ loading: false, authenticated: false }); } }); return () => { cancelled = true; }; }, [fetchCurrentUser]); if (state.loading) { return /* @__PURE__ */ jsx(Fragment, { children: loadingFallback ?? /* @__PURE__ */ jsx(Center, { h: "var(--app-height)", children: /* @__PURE__ */ jsx(Spinner, { size: "xl" }) }) }); } if (!state.authenticated) { return /* @__PURE__ */ jsx(Navigate, { to: redirectTo, replace: true }); } return /* @__PURE__ */ jsx(Fragment, { children: authenticatedWrapper ? authenticatedWrapper(children) : children }); } // react/client.ts async function readJsonError(response, fallback) { const payload = await response.json().catch(() => null); return new Error(payload?.error ?? fallback); } function createAuthClient(options) { const fetchImpl = options.fetchImpl ?? fetch; const authUrl = options.authUrl ?? options.apiUrl; const credentials = options.credentials ?? "include"; function resolveDefaultOAuthCallbackUrl() { const configured = options.defaultOAuthCallbackUrl; if (typeof configured === "function") { return configured(); } if (typeof configured === "string" && configured.trim().length > 0) { return configured; } return `${window.location.origin}/chat`; } async function request(path, init) { return fetchImpl(options.apiUrl(path), { ...init, credentials, headers: { "Content-Type": "application/json", ...init?.headers ?? {} } }); } return { async getProviders() { const response = await request("/api/auth/providers"); if (!response.ok) { throw await readJsonError(response, "providers_unavailable"); } return await response.json(); }, async getCurrentUser() { const response = await request("/api/me"); if (!response.ok) { throw await readJsonError(response, "Unauthorized"); } const payload = await response.json(); return payload.user; }, async register(input) { const response = await request("/api/auth/register", { method: "POST", body: JSON.stringify(input) }); if (!response.ok) { throw await readJsonError(response, "Registration failed"); } }, async login(input) { const response = await request("/api/auth/login", { method: "POST", body: JSON.stringify(input) }); if (!response.ok) { throw await readJsonError(response, "Sign in failed"); } }, async requestPasswordReset(email) { const response = await request("/api/auth/password-reset/request", { method: "POST", body: JSON.stringify({ email }) }); if (!response.ok) { throw await readJsonError(response, "Password reset request failed"); } }, async validatePasswordResetToken(token) { const response = await request(`/api/auth/password-reset/validate?token=${encodeURIComponent(token)}`, { headers: {} }); const payload = await response.json().catch(() => null); if (!response.ok || !payload?.email || payload.mode !== "reset" && payload.mode !== "create") { throw new Error(payload?.error ?? "Invalid reset link"); } return { email: payload.email, mode: payload.mode }; }, async confirmPasswordReset(input) { const response = await request("/api/auth/password-reset/confirm", { method: "POST", body: JSON.stringify(input) }); if (!response.ok) { throw await readJsonError(response, "Invalid reset link"); } }, async logout() { const response = await request("/api/auth/logout", { method: "POST" }); if (!response.ok) { throw await readJsonError(response, "Logout failed"); } }, async startOAuthSignIn(provider, callbackUrl = resolveDefaultOAuthCallbackUrl()) { const response = await fetchImpl(authUrl("/auth/csrf"), { credentials }); if (!response.ok) { throw await readJsonError(response, "Sign in failed"); } const payload = await response.json(); if (!payload.csrfToken) { throw new Error("Sign in failed"); } const form = document.createElement("form"); form.method = "POST"; form.action = authUrl(`/auth/signin/${provider}`); form.style.display = "none"; const csrfInput = document.createElement("input"); csrfInput.type = "hidden"; csrfInput.name = "csrfToken"; csrfInput.value = payload.csrfToken; form.appendChild(csrfInput); const callbackInput = document.createElement("input"); callbackInput.type = "hidden"; callbackInput.name = "callbackUrl"; callbackInput.value = callbackUrl; form.appendChild(callbackInput); document.body.appendChild(form); form.submit(); } }; } // react/LoginForm.tsx import { Alert, AlertDescription, AlertIcon, Button, Center as Center2, FormControl, FormLabel, HStack, Icon, Input, Stack } from "@chakra-ui/react"; import { FcGoogle } from "react-icons/fc"; import { jsx as jsx2, jsxs } from "react/jsx-runtime"; function LoginForm({ mode, texts, onSubmit, onModeToggle, loading = false, oauthLoadingProvider = null, providers, onOAuthSignIn, errorMessage, successMessage, footer, forgotPasswordLink, emailPlaceholder = "you@example.com", namePlaceholder = "Jane Doe" }) { const registerMode = mode === "register"; function handleSubmit(event) { event.preventDefault(); const form = new FormData(event.currentTarget); void onSubmit({ name: String(form.get("name") ?? ""), email: String(form.get("email") ?? ""), password: String(form.get("password") ?? ""), passwordConfirm: String(form.get("passwordConfirm") ?? "") }); } return /* @__PURE__ */ jsxs(Stack, { spacing: 5, children: [ errorMessage ? /* @__PURE__ */ jsxs(Alert, { status: "error", borderRadius: "md", children: [ /* @__PURE__ */ jsx2(AlertIcon, {}), /* @__PURE__ */ jsx2(AlertDescription, { children: errorMessage }) ] }) : null, successMessage ? /* @__PURE__ */ jsxs(Alert, { status: "success", borderRadius: "md", children: [ /* @__PURE__ */ jsx2(AlertIcon, {}), /* @__PURE__ */ jsx2(AlertDescription, { children: successMessage }) ] }) : null, /* @__PURE__ */ jsx2("form", { onSubmit: handleSubmit, children: /* @__PURE__ */ jsxs(Stack, { spacing: 4, children: [ registerMode ? /* @__PURE__ */ jsxs(FormControl, { isRequired: true, children: [ /* @__PURE__ */ jsx2(FormLabel, { children: texts.nameLabel }), /* @__PURE__ */ jsx2(Input, { name: "name", placeholder: namePlaceholder }) ] }) : null, /* @__PURE__ */ jsxs(FormControl, { isRequired: true, children: [ /* @__PURE__ */ jsx2(FormLabel, { children: texts.emailLabel }), /* @__PURE__ */ jsx2(Input, { name: "email", type: "email", placeholder: emailPlaceholder }) ] }), /* @__PURE__ */ jsxs(FormControl, { isRequired: true, children: [ /* @__PURE__ */ jsx2(FormLabel, { children: texts.passwordLabel }), /* @__PURE__ */ jsx2(Input, { name: "password", type: "password", minLength: 8 }) ] }), !registerMode && forgotPasswordLink ? /* @__PURE__ */ jsx2(Stack, { align: "flex-end", children: forgotPasswordLink }) : null, registerMode ? /* @__PURE__ */ jsxs(FormControl, { isRequired: true, children: [ /* @__PURE__ */ jsx2(FormLabel, { children: texts.passwordConfirmLabel }), /* @__PURE__ */ jsx2(Input, { name: "passwordConfirm", type: "password", minLength: 8 }) ] }) : null, /* @__PURE__ */ jsx2(Button, { type: "submit", isLoading: loading, children: registerMode ? texts.submitRegisterLabel : texts.submitSignInLabel }) ] }) }), registerMode || !onOAuthSignIn || !providers?.google && !providers?.slack ? null : /* @__PURE__ */ jsxs(HStack, { children: [ providers.google ? /* @__PURE__ */ jsx2( Button, { flex: 1, bg: "gray.200", color: "gray.900", borderRadius: "full", justifyContent: "flex-start", h: "56px", px: 3, fontSize: { base: "md", md: "lg" }, fontWeight: "semibold", iconSpacing: 4, leftIcon: /* @__PURE__ */ jsx2(Center2, { boxSize: "40px", bg: "white", borderRadius: "full", boxShadow: "sm", children: /* @__PURE__ */ jsx2(Icon, { as: FcGoogle, boxSize: 6 }) }), _hover: { bg: "gray.300" }, _active: { bg: "gray.300" }, isLoading: oauthLoadingProvider === "google", onClick: () => void onOAuthSignIn("google"), children: texts.googleLabel } ) : null, providers.slack ? /* @__PURE__ */ jsx2( Button, { flex: 1, variant: "outline", isLoading: oauthLoadingProvider === "slack", onClick: () => void onOAuthSignIn("slack"), children: texts.slackLabel } ) : null ] }), /* @__PURE__ */ jsx2(Button, { variant: "ghost", onClick: onModeToggle, children: registerMode ? texts.toggleToSignInLabel : texts.toggleToRegisterLabel }), footer ?? null ] }); } // react/PasswordResetForms.tsx import { Alert as Alert2, AlertDescription as AlertDescription2, AlertIcon as AlertIcon2, Button as Button2, FormControl as FormControl2, FormLabel as FormLabel2, Input as Input2, Spinner as Spinner2, Stack as Stack2, Text } from "@chakra-ui/react"; import { jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime"; function PasswordResetRequestForm({ texts, helperText, loading = false, requestSent = false, onSubmit, emailPlaceholder = "you@example.com" }) { function handleSubmit(event) { event.preventDefault(); const form = new FormData(event.currentTarget); void onSubmit({ email: String(form.get("email") ?? "") }); } return /* @__PURE__ */ jsxs2(Stack2, { spacing: 5, children: [ requestSent ? /* @__PURE__ */ jsxs2(Alert2, { status: "success", borderRadius: "md", children: [ /* @__PURE__ */ jsx3(AlertIcon2, {}), /* @__PURE__ */ jsx3(AlertDescription2, { children: texts.requestSentMessage }) ] }) : null, /* @__PURE__ */ jsx3("form", { onSubmit: handleSubmit, children: /* @__PURE__ */ jsxs2(Stack2, { spacing: 4, children: [ /* @__PURE__ */ jsxs2(FormControl2, { isRequired: true, children: [ /* @__PURE__ */ jsx3(FormLabel2, { children: texts.emailLabel }), /* @__PURE__ */ jsx3(Input2, { name: "email", type: "email", placeholder: emailPlaceholder }) ] }), /* @__PURE__ */ jsx3(Text, { fontSize: "sm", color: "gray.600", children: helperText }), /* @__PURE__ */ jsx3(Button2, { type: "submit", isLoading: loading, children: texts.submitLabel }) ] }) }) ] }); } function PasswordResetConfirmForm({ texts, tokenState, loading = false, completedMode = null, onSubmit }) { function handleSubmit(event) { event.preventDefault(); const form = new FormData(event.currentTarget); void onSubmit({ password: String(form.get("password") ?? ""), passwordConfirm: String(form.get("passwordConfirm") ?? "") }); } if (tokenState.status === "loading") { return /* @__PURE__ */ jsxs2(Stack2, { align: "center", py: 6, spacing: 3, children: [ /* @__PURE__ */ jsx3(Spinner2, {}), /* @__PURE__ */ jsx3(Text, { color: "gray.600", children: texts.loadingLabel }) ] }); } if (tokenState.status === "invalid") { return /* @__PURE__ */ jsxs2(Alert2, { status: "error", borderRadius: "md", children: [ /* @__PURE__ */ jsx3(AlertIcon2, {}), /* @__PURE__ */ jsx3(AlertDescription2, { children: tokenState.error || texts.invalidLinkLabel }) ] }); } if (completedMode !== null) { return /* @__PURE__ */ jsxs2(Alert2, { status: "success", borderRadius: "md", children: [ /* @__PURE__ */ jsx3(AlertIcon2, {}), /* @__PURE__ */ jsx3(AlertDescription2, { children: completedMode === "create" ? texts.createSuccessLabel : texts.resetSuccessLabel }) ] }); } return /* @__PURE__ */ jsxs2(Stack2, { spacing: 4, children: [ /* @__PURE__ */ jsx3(Text, { fontSize: "sm", color: "gray.600", children: tokenState.email }), /* @__PURE__ */ jsx3("form", { onSubmit: handleSubmit, children: /* @__PURE__ */ jsxs2(Stack2, { spacing: 4, children: [ /* @__PURE__ */ jsxs2(FormControl2, { isRequired: true, children: [ /* @__PURE__ */ jsx3(FormLabel2, { children: texts.passwordLabel }), /* @__PURE__ */ jsx3(Input2, { name: "password", type: "password", minLength: 8 }) ] }), /* @__PURE__ */ jsxs2(FormControl2, { isRequired: true, children: [ /* @__PURE__ */ jsx3(FormLabel2, { children: texts.passwordConfirmLabel }), /* @__PURE__ */ jsx3(Input2, { name: "passwordConfirm", type: "password", minLength: 8 }) ] }), /* @__PURE__ */ jsx3(Button2, { type: "submit", isLoading: loading, children: tokenState.mode === "create" ? texts.createSubmitLabel : texts.resetSubmitLabel }) ] }) }) ] }); } export { AuthGuard, LoginForm, PasswordResetConfirmForm, PasswordResetRequestForm, createAuthClient }; //# sourceMappingURL=index.js.map