// react/AuthGuard.tsx import { useEffect, useState } from "react"; // react/chakra-compat.tsx import { Alert as ChakraAlert, Box as ChakraBox, Button as ChakraButton, Center as ChakraCenter, HStack as ChakraHStack, Icon as ChakraIcon, Input as ChakraInput, Spinner as ChakraSpinner, Stack as ChakraStack, Text as ChakraText } from "@chakra-ui/react"; import { jsx, jsxs } from "react/jsx-runtime"; function normalizeSpacingProps(props) { const next = { ...props }; if (next.spacing !== void 0 && next.gap === void 0) { next.gap = next.spacing; } delete next.spacing; return next; } function normalizeInteractiveProps(props) { const next = normalizeSpacingProps(props); if (next.isDisabled !== void 0 && next.disabled === void 0) { next.disabled = next.isDisabled; } if (next.isLoading !== void 0 && next.loading === void 0) { next.loading = next.isLoading; } delete next.isDisabled; delete next.isLoading; return next; } var Center = ChakraCenter; var Icon = ChakraIcon; var Input = ChakraInput; var Spinner = ChakraSpinner; function Stack(props) { return /* @__PURE__ */ jsx(ChakraStack, { ...normalizeSpacingProps(props) }); } function HStack(props) { return /* @__PURE__ */ jsx(ChakraHStack, { ...normalizeSpacingProps(props) }); } function Text(props) { const next = { ...props }; if (next.noOfLines !== void 0 && next.lineClamp === void 0) { next.lineClamp = next.noOfLines; } delete next.noOfLines; return /* @__PURE__ */ jsx(ChakraText, { ...next }); } function Button(props) { const { leftIcon, rightIcon, children, ...rest } = normalizeInteractiveProps(props); return /* @__PURE__ */ jsx(ChakraButton, { ...rest, children: /* @__PURE__ */ jsxs(ChakraHStack, { gap: 2, children: [ leftIcon ?? null, /* @__PURE__ */ jsx("span", { children }), rightIcon ?? null ] }) }); } function Alert({ children, status = "info", ...props }) { return /* @__PURE__ */ jsx(ChakraAlert.Root, { status, ...props, children }); } function AlertIcon() { return /* @__PURE__ */ jsx(ChakraAlert.Indicator, {}); } function AlertDescription({ children, ...props }) { return /* @__PURE__ */ jsx(ChakraAlert.Description, { ...props, children }); } function FormControl({ children, ...props }) { return /* @__PURE__ */ jsx(ChakraStack, { gap: 2, ...normalizeSpacingProps(props), children }); } function FormLabel(props) { return /* @__PURE__ */ jsx(ChakraBox, { as: "label", fontWeight: "medium", ...props }); } // react/AuthGuard.tsx import { Navigate } from "react-router"; import { Fragment, jsx as jsx2 } 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__ */ jsx2(Fragment, { children: loadingFallback ?? /* @__PURE__ */ jsx2(Center, { h: "var(--app-height)", children: /* @__PURE__ */ jsx2(Spinner, { size: "xl" }) }) }); } if (!state.authenticated) { return /* @__PURE__ */ jsx2(Navigate, { to: redirectTo, replace: true }); } return /* @__PURE__ */ jsx2(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 { FcGoogle } from "react-icons/fc"; import { jsx as jsx3, jsxs as jsxs2 } 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__ */ jsxs2(Stack, { spacing: 5, children: [ errorMessage ? /* @__PURE__ */ jsxs2(Alert, { status: "error", borderRadius: "md", children: [ /* @__PURE__ */ jsx3(AlertIcon, {}), /* @__PURE__ */ jsx3(AlertDescription, { children: errorMessage }) ] }) : null, successMessage ? /* @__PURE__ */ jsxs2(Alert, { status: "success", borderRadius: "md", children: [ /* @__PURE__ */ jsx3(AlertIcon, {}), /* @__PURE__ */ jsx3(AlertDescription, { children: successMessage }) ] }) : null, /* @__PURE__ */ jsx3("form", { onSubmit: handleSubmit, children: /* @__PURE__ */ jsxs2(Stack, { spacing: 4, children: [ registerMode ? /* @__PURE__ */ jsxs2(FormControl, { isRequired: true, children: [ /* @__PURE__ */ jsx3(FormLabel, { children: texts.nameLabel }), /* @__PURE__ */ jsx3(Input, { name: "name", placeholder: namePlaceholder }) ] }) : null, /* @__PURE__ */ jsxs2(FormControl, { isRequired: true, children: [ /* @__PURE__ */ jsx3(FormLabel, { children: texts.emailLabel }), /* @__PURE__ */ jsx3(Input, { name: "email", type: "email", placeholder: emailPlaceholder }) ] }), /* @__PURE__ */ jsxs2(FormControl, { isRequired: true, children: [ /* @__PURE__ */ jsx3(FormLabel, { children: texts.passwordLabel }), /* @__PURE__ */ jsx3(Input, { name: "password", type: "password", minLength: 8 }) ] }), !registerMode && forgotPasswordLink ? /* @__PURE__ */ jsx3(Stack, { align: "flex-end", children: forgotPasswordLink }) : null, registerMode ? /* @__PURE__ */ jsxs2(FormControl, { isRequired: true, children: [ /* @__PURE__ */ jsx3(FormLabel, { children: texts.passwordConfirmLabel }), /* @__PURE__ */ jsx3(Input, { name: "passwordConfirm", type: "password", minLength: 8 }) ] }) : null, /* @__PURE__ */ jsx3(Button, { type: "submit", isLoading: loading, children: registerMode ? texts.submitRegisterLabel : texts.submitSignInLabel }) ] }) }), registerMode || !onOAuthSignIn || !providers?.google && !providers?.slack ? null : /* @__PURE__ */ jsxs2(HStack, { children: [ providers.google ? /* @__PURE__ */ jsx3( 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__ */ jsx3(Center, { boxSize: "40px", bg: "white", borderRadius: "full", boxShadow: "sm", children: /* @__PURE__ */ jsx3(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__ */ jsx3( Button, { flex: 1, variant: "outline", isLoading: oauthLoadingProvider === "slack", onClick: () => void onOAuthSignIn("slack"), children: texts.slackLabel } ) : null ] }), /* @__PURE__ */ jsx3(Button, { variant: "ghost", onClick: onModeToggle, children: registerMode ? texts.toggleToSignInLabel : texts.toggleToRegisterLabel }), footer ?? null ] }); } // react/PasswordResetForms.tsx import { jsx as jsx4, jsxs as jsxs3 } 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__ */ jsxs3(Stack, { spacing: 5, children: [ requestSent ? /* @__PURE__ */ jsxs3(Alert, { status: "success", borderRadius: "md", children: [ /* @__PURE__ */ jsx4(AlertIcon, {}), /* @__PURE__ */ jsx4(AlertDescription, { children: texts.requestSentMessage }) ] }) : null, /* @__PURE__ */ jsx4("form", { onSubmit: handleSubmit, children: /* @__PURE__ */ jsxs3(Stack, { spacing: 4, children: [ /* @__PURE__ */ jsxs3(FormControl, { isRequired: true, children: [ /* @__PURE__ */ jsx4(FormLabel, { children: texts.emailLabel }), /* @__PURE__ */ jsx4(Input, { name: "email", type: "email", placeholder: emailPlaceholder }) ] }), /* @__PURE__ */ jsx4(Text, { fontSize: "sm", color: "gray.600", children: helperText }), /* @__PURE__ */ jsx4(Button, { 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__ */ jsxs3(Stack, { align: "center", py: 6, spacing: 3, children: [ /* @__PURE__ */ jsx4(Spinner, {}), /* @__PURE__ */ jsx4(Text, { color: "gray.600", children: texts.loadingLabel }) ] }); } if (tokenState.status === "invalid") { return /* @__PURE__ */ jsxs3(Alert, { status: "error", borderRadius: "md", children: [ /* @__PURE__ */ jsx4(AlertIcon, {}), /* @__PURE__ */ jsx4(AlertDescription, { children: tokenState.error || texts.invalidLinkLabel }) ] }); } if (completedMode !== null) { return /* @__PURE__ */ jsxs3(Alert, { status: "success", borderRadius: "md", children: [ /* @__PURE__ */ jsx4(AlertIcon, {}), /* @__PURE__ */ jsx4(AlertDescription, { children: completedMode === "create" ? texts.createSuccessLabel : texts.resetSuccessLabel }) ] }); } return /* @__PURE__ */ jsxs3(Stack, { spacing: 4, children: [ /* @__PURE__ */ jsx4(Text, { fontSize: "sm", color: "gray.600", children: tokenState.email }), /* @__PURE__ */ jsx4("form", { onSubmit: handleSubmit, children: /* @__PURE__ */ jsxs3(Stack, { spacing: 4, children: [ /* @__PURE__ */ jsxs3(FormControl, { isRequired: true, children: [ /* @__PURE__ */ jsx4(FormLabel, { children: texts.passwordLabel }), /* @__PURE__ */ jsx4(Input, { name: "password", type: "password", minLength: 8 }) ] }), /* @__PURE__ */ jsxs3(FormControl, { isRequired: true, children: [ /* @__PURE__ */ jsx4(FormLabel, { children: texts.passwordConfirmLabel }), /* @__PURE__ */ jsx4(Input, { name: "passwordConfirm", type: "password", minLength: 8 }) ] }), /* @__PURE__ */ jsx4(Button, { type: "submit", isLoading: loading, children: tokenState.mode === "create" ? texts.createSubmitLabel : texts.resetSubmitLabel }) ] }) }) ] }); } export { AuthGuard, LoginForm, PasswordResetConfirmForm, PasswordResetRequestForm, createAuthClient }; //# sourceMappingURL=index.js.map