@tern-secure/nextjs 4.0.0 → 4.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (96) hide show
  1. package/dist/cjs/app-router/client/TernSecureProvider.js +17 -2
  2. package/dist/cjs/app-router/client/TernSecureProvider.js.map +1 -1
  3. package/dist/cjs/app-router/client/actions.js +49 -49
  4. package/dist/cjs/app-router/client/actions.js.map +1 -1
  5. package/dist/cjs/app-router/route-handler/internal-route.js +17 -2
  6. package/dist/cjs/app-router/route-handler/internal-route.js.map +1 -1
  7. package/dist/cjs/boundary/TernSecureClientProvider.js +163 -40
  8. package/dist/cjs/boundary/TernSecureClientProvider.js.map +1 -1
  9. package/dist/cjs/boundary/TernSecureCtx.js.map +1 -1
  10. package/dist/cjs/boundary/hooks/useAuth.js +7 -8
  11. package/dist/cjs/boundary/hooks/useAuth.js.map +1 -1
  12. package/dist/cjs/components/sign-in.js +136 -45
  13. package/dist/cjs/components/sign-in.js.map +1 -1
  14. package/dist/cjs/components/sign-out-button.js +10 -1
  15. package/dist/cjs/components/sign-out-button.js.map +1 -1
  16. package/dist/cjs/components/sign-out.js +12 -3
  17. package/dist/cjs/components/sign-out.js.map +1 -1
  18. package/dist/cjs/components/sign-up.js +10 -5
  19. package/dist/cjs/components/sign-up.js.map +1 -1
  20. package/dist/cjs/errors.js +232 -5
  21. package/dist/cjs/errors.js.map +1 -1
  22. package/dist/cjs/index.js +0 -3
  23. package/dist/cjs/index.js.map +1 -1
  24. package/dist/cjs/types.js +14 -0
  25. package/dist/cjs/types.js.map +1 -1
  26. package/dist/cjs/utils/construct.js +50 -18
  27. package/dist/cjs/utils/construct.js.map +1 -1
  28. package/dist/cjs/utils/redirect.js +57 -0
  29. package/dist/cjs/utils/redirect.js.map +1 -0
  30. package/dist/esm/app-router/client/TernSecureProvider.js +17 -2
  31. package/dist/esm/app-router/client/TernSecureProvider.js.map +1 -1
  32. package/dist/esm/app-router/client/actions.js +59 -51
  33. package/dist/esm/app-router/client/actions.js.map +1 -1
  34. package/dist/esm/app-router/route-handler/internal-route.js +13 -1
  35. package/dist/esm/app-router/route-handler/internal-route.js.map +1 -1
  36. package/dist/esm/boundary/TernSecureClientProvider.js +164 -41
  37. package/dist/esm/boundary/TernSecureClientProvider.js.map +1 -1
  38. package/dist/esm/boundary/TernSecureCtx.js.map +1 -1
  39. package/dist/esm/boundary/hooks/useAuth.js +7 -8
  40. package/dist/esm/boundary/hooks/useAuth.js.map +1 -1
  41. package/dist/esm/components/sign-in.js +137 -46
  42. package/dist/esm/components/sign-in.js.map +1 -1
  43. package/dist/esm/components/sign-out-button.js +11 -2
  44. package/dist/esm/components/sign-out-button.js.map +1 -1
  45. package/dist/esm/components/sign-out.js +13 -4
  46. package/dist/esm/components/sign-out.js.map +1 -1
  47. package/dist/esm/components/sign-up.js +10 -5
  48. package/dist/esm/components/sign-up.js.map +1 -1
  49. package/dist/esm/errors.js +228 -4
  50. package/dist/esm/errors.js.map +1 -1
  51. package/dist/esm/index.js +0 -2
  52. package/dist/esm/index.js.map +1 -1
  53. package/dist/esm/types.js +6 -0
  54. package/dist/esm/types.js.map +1 -1
  55. package/dist/esm/utils/construct.js +46 -17
  56. package/dist/esm/utils/construct.js.map +1 -1
  57. package/dist/esm/utils/redirect.js +32 -0
  58. package/dist/esm/utils/redirect.js.map +1 -0
  59. package/dist/types/app-router/client/TernSecureProvider.d.ts +14 -3
  60. package/dist/types/app-router/client/TernSecureProvider.d.ts.map +1 -1
  61. package/dist/types/app-router/client/actions.d.ts +23 -21
  62. package/dist/types/app-router/client/actions.d.ts.map +1 -1
  63. package/dist/types/app-router/route-handler/internal-route.d.ts +3 -0
  64. package/dist/types/app-router/route-handler/internal-route.d.ts.map +1 -1
  65. package/dist/types/boundary/TernSecureClientProvider.d.ts +17 -1
  66. package/dist/types/boundary/TernSecureClientProvider.d.ts.map +1 -1
  67. package/dist/types/boundary/TernSecureCtx.d.ts +3 -1
  68. package/dist/types/boundary/TernSecureCtx.d.ts.map +1 -1
  69. package/dist/types/boundary/hooks/useAuth.d.ts +4 -1
  70. package/dist/types/boundary/hooks/useAuth.d.ts.map +1 -1
  71. package/dist/types/components/sign-in.d.ts +1 -2
  72. package/dist/types/components/sign-in.d.ts.map +1 -1
  73. package/dist/types/components/sign-out-button.d.ts +2 -1
  74. package/dist/types/components/sign-out-button.d.ts.map +1 -1
  75. package/dist/types/components/sign-out.d.ts +2 -1
  76. package/dist/types/components/sign-out.d.ts.map +1 -1
  77. package/dist/types/components/sign-up.d.ts.map +1 -1
  78. package/dist/types/components/ui/alert.d.ts +1 -1
  79. package/dist/types/components/ui/button.d.ts +1 -1
  80. package/dist/types/errors.d.ts +36 -2
  81. package/dist/types/errors.d.ts.map +1 -1
  82. package/dist/types/index.d.ts +0 -1
  83. package/dist/types/index.d.ts.map +1 -1
  84. package/dist/types/types.d.ts +35 -0
  85. package/dist/types/types.d.ts.map +1 -1
  86. package/dist/types/utils/construct.d.ts +20 -4
  87. package/dist/types/utils/construct.d.ts.map +1 -1
  88. package/dist/types/utils/redirect.d.ts +9 -0
  89. package/dist/types/utils/redirect.d.ts.map +1 -0
  90. package/package.json +6 -6
  91. package/dist/cjs/boundary/hooks/useUser.js +0 -44
  92. package/dist/cjs/boundary/hooks/useUser.js.map +0 -1
  93. package/dist/esm/boundary/hooks/useUser.js +0 -20
  94. package/dist/esm/boundary/hooks/useUser.js.map +0 -1
  95. package/dist/types/boundary/hooks/useUser.d.ts +0 -7
  96. package/dist/types/boundary/hooks/useUser.d.ts.map +0 -1
@@ -10,21 +10,18 @@ function useAuth() {
10
10
  isVerified,
11
11
  isAuthenticated,
12
12
  token,
13
+ getAuthError,
14
+ status,
15
+ requiresVerification,
13
16
  signOut
14
17
  } = useTernSecure("useAuth");
15
18
  const user = TernSecureUser();
16
- const getAuthError = () => {
17
- if (error) return error;
18
- if (isValid && !isVerified) {
19
- return new Error("Email verification required");
20
- }
21
- return null;
22
- };
19
+ const authResponse = getAuthError();
23
20
  return {
24
21
  user,
25
22
  userId,
26
23
  isLoaded,
27
- error: getAuthError(),
24
+ error: authResponse.success ? null : authResponse,
28
25
  isValid,
29
26
  // User is signed in
30
27
  isVerified,
@@ -32,6 +29,8 @@ function useAuth() {
32
29
  isAuthenticated,
33
30
  // User is both signed in and verified
34
31
  token,
32
+ status,
33
+ requiresVerification,
35
34
  signOut
36
35
  };
37
36
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../src/boundary/hooks/useAuth.ts"],"sourcesContent":["\"use client\"\n\nimport { useTernSecure } from '../TernSecureCtx'\nimport { User } from 'firebase/auth'\nimport { TernSecureUser } from '../TernSecureCtx'\n\nexport function useAuth() {\n const {\n userId,\n isLoaded,\n error,\n isValid,\n isVerified,\n isAuthenticated,\n token,\n signOut\n } = useTernSecure('useAuth')\n\n const user: User | null = TernSecureUser()\n\n const getAuthError = () => {\n if (error) return error\n if (isValid && !isVerified) {\n return new Error('Email verification required')\n }\n return null\n }\n\n return {\n user,\n userId,\n isLoaded,\n error: getAuthError(),\n isValid, // User is signed in\n isVerified, // Email is verified\n isAuthenticated, // User is both signed in and verified\n token,\n signOut\n }\n}\n"],"mappings":";AAEA,SAAS,qBAAqB;AAE9B,SAAS,sBAAsB;AAExB,SAAS,UAAU;AACxB,QAAM;AAAA,IACJ;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF,IAAI,cAAc,SAAS;AAE3B,QAAM,OAAoB,eAAe;AAEzC,QAAM,eAAe,MAAM;AACzB,QAAI,MAAO,QAAO;AAClB,QAAI,WAAW,CAAC,YAAY;AAC1B,aAAO,IAAI,MAAM,6BAA6B;AAAA,IAChD;AACA,WAAO;AAAA,EACT;AAEA,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA,OAAO,aAAa;AAAA,IACpB;AAAA;AAAA,IACA;AAAA;AAAA,IACA;AAAA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACF;","names":[]}
1
+ {"version":3,"sources":["../../../../src/boundary/hooks/useAuth.ts"],"sourcesContent":["\"use client\"\n\nimport { useTernSecure } from '../TernSecureCtx'\nimport { User } from 'firebase/auth'\nimport { TernSecureUser } from '../TernSecureCtx'\nimport type { SignInResponse } from '../../types'\n\n\nexport function useAuth() {\n const {\n userId,\n isLoaded,\n error,\n isValid,\n isVerified,\n isAuthenticated,\n token,\n getAuthError,\n status,\n requiresVerification,\n signOut\n } = useTernSecure('useAuth')\n\n const user: User | null = TernSecureUser()\n const authResponse: SignInResponse = getAuthError()\n\n\n return {\n user,\n userId,\n isLoaded,\n error: authResponse.success ? null : authResponse,\n isValid, // User is signed in\n isVerified, // Email is verified\n isAuthenticated, // User is both signed in and verified\n token,\n status,\n requiresVerification,\n signOut\n }\n}\n"],"mappings":";AAEA,SAAS,qBAAqB;AAE9B,SAAS,sBAAsB;AAIxB,SAAS,UAAU;AACxB,QAAM;AAAA,IACJ;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF,IAAI,cAAc,SAAS;AAE3B,QAAM,OAAoB,eAAe;AACzC,QAAM,eAA+B,aAAa;AAGlD,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA,OAAO,aAAa,UAAU,OAAO;AAAA,IACrC;AAAA;AAAA,IACA;AAAA;AAAA,IACA;AAAA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACF;","names":[]}
@@ -10,38 +10,85 @@ import { Button } from "./ui/button";
10
10
  import { Alert, AlertDescription } from "./ui/alert";
11
11
  import { Separator } from "./ui/separator";
12
12
  import { cn } from "../lib/utils";
13
- import { Loader2 } from "lucide-react";
13
+ import { Loader2, Eye, EyeOff } from "lucide-react";
14
14
  import { getRedirectResult } from "firebase/auth";
15
15
  import { ternSecureAuth } from "../utils/client-init";
16
16
  import { createSessionCookie } from "../app-router/server/sessionTernSecure";
17
17
  import { AuthBackground } from "./background";
18
18
  import { getValidRedirectUrl } from "../utils/construct";
19
- import { ERRORS } from "../errors";
20
19
  import { handleInternalRoute } from "../app-router/route-handler/internal-route";
20
+ import { useAuth } from "../boundary/hooks/useAuth";
21
+ import { getErrorAlertVariant } from "../errors";
21
22
  const authDomain = process.env.NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN;
22
23
  const appName = process.env.NEXT_PUBLIC_FIREBASE_APP_NAME || "TernSecure";
23
24
  function SignIn({
24
25
  redirectUrl,
25
26
  onError,
26
27
  onSuccess,
27
- requiresVerification = true,
28
28
  className,
29
29
  customStyles = {}
30
30
  }) {
31
31
  const [loading, setLoading] = useState(false);
32
32
  const [checkingRedirect, setCheckingRedirect] = useState(true);
33
+ const [formError, setFormError] = useState(null);
33
34
  const [error, setError] = useState("");
34
35
  const [email, setEmail] = useState("");
35
36
  const [password, setPassword] = useState("");
37
+ const [showPassword, setShowPassword] = useState(false);
38
+ const [passwordFocused, setPasswordFocused] = useState(false);
36
39
  const [authResponse, setAuthResponse] = useState(null);
40
+ const [authErrorMessage, setAuthErrorMessage] = useState(null);
37
41
  const searchParams = useSearchParams();
38
42
  const isRedirectSignIn = searchParams.get("signInRedirect") === "true";
39
43
  const router = useRouter();
40
44
  const pathname = usePathname();
41
- const InternalComponent = handleInternalRoute(pathname);
45
+ const InternalComponent = handleInternalRoute(pathname || "");
46
+ const { requiresVerification, error: authError, status } = useAuth();
47
+ const validRedirectUrl = getValidRedirectUrl(searchParams, redirectUrl);
42
48
  if (InternalComponent) {
43
49
  return /* @__PURE__ */ jsx(InternalComponent, {});
44
50
  }
51
+ useEffect(() => {
52
+ if (authError && status !== "loading" && status !== "unauthenticated") {
53
+ const message = authError.message || "Authentication failed";
54
+ setAuthErrorMessage(message);
55
+ if (!authResponse || authResponse.message !== message) {
56
+ setAuthResponse(authError);
57
+ }
58
+ } else {
59
+ setAuthErrorMessage(null);
60
+ }
61
+ }, [authError, status, authResponse]);
62
+ const handleSuccessfulAuth = useCallback(
63
+ async (user) => {
64
+ try {
65
+ const idToken = await user.getIdToken();
66
+ const sessionResult = await createSessionCookie(idToken);
67
+ if (!sessionResult.success) {
68
+ setFormError({
69
+ success: false,
70
+ message: sessionResult.message || "Failed to create session",
71
+ error: "INTERNAL_ERROR",
72
+ user: null
73
+ });
74
+ }
75
+ onSuccess == null ? void 0 : onSuccess();
76
+ if (process.env.NODE_ENV === "production") {
77
+ window.location.href = validRedirectUrl;
78
+ } else {
79
+ router.push(validRedirectUrl);
80
+ }
81
+ } catch (err) {
82
+ setFormError({
83
+ success: false,
84
+ message: "Failed to complete authentication",
85
+ error: "INTERNAL_ERROR",
86
+ user: null
87
+ });
88
+ }
89
+ },
90
+ [validRedirectUrl, router, onSuccess]
91
+ );
45
92
  const handleRedirectResult = useCallback(async () => {
46
93
  if (!isRedirectSignIn) return false;
47
94
  setCheckingRedirect(true);
@@ -62,15 +109,16 @@ function SignIn({
62
109
  const storedRedirectUrl = sessionStorage.getItem("auth_return_url");
63
110
  sessionStorage.removeItem("auth_redirect_url");
64
111
  onSuccess == null ? void 0 : onSuccess();
65
- window.location.href = storedRedirectUrl || getValidRedirectUrl(redirectUrl, searchParams);
112
+ window.location.href = storedRedirectUrl || getValidRedirectUrl(searchParams, redirectUrl);
66
113
  return true;
67
114
  }
68
115
  setCheckingRedirect(false);
69
116
  } catch (err) {
70
- console.error("Redirect result error:", err);
71
- const errorMessage = err instanceof Error ? err.message : "Authentication failed";
72
- setError(errorMessage);
73
- onError == null ? void 0 : onError(err instanceof Error ? err : new Error(errorMessage));
117
+ const errorMessage = err;
118
+ setFormError(errorMessage);
119
+ if (onError && err instanceof Error) {
120
+ onError(err);
121
+ }
74
122
  sessionStorage.removeItem("auth_redirect_url");
75
123
  return false;
76
124
  }
@@ -79,32 +127,42 @@ function SignIn({
79
127
  if (isRedirectSignIn) {
80
128
  handleRedirectResult();
81
129
  }
82
- ;
83
130
  }, [handleRedirectResult, isRedirectSignIn]);
84
131
  const handleSubmit = async (e) => {
85
132
  e.preventDefault();
86
133
  setLoading(true);
134
+ setFormError(null);
87
135
  setAuthResponse(null);
88
136
  try {
89
137
  const response = await signInWithEmail(email, password);
90
138
  setAuthResponse(response);
139
+ if (!response.success) {
140
+ setFormError({
141
+ success: false,
142
+ message: response.message,
143
+ error: response.error,
144
+ user: null
145
+ });
146
+ return;
147
+ }
91
148
  if (response.user) {
92
149
  if (requiresVerification && !response.user.emailVerified) {
93
- setError("Email verification required");
150
+ setFormError({
151
+ success: false,
152
+ message: "Email verification required",
153
+ error: "REQUIRES_VERIFICATION",
154
+ user: response.user
155
+ });
94
156
  return;
95
157
  }
96
- const idToken = await response.user.getIdToken();
97
- const sessionResult = await createSessionCookie(idToken);
98
- if (!sessionResult.success) {
99
- throw new Error(sessionResult.message || "Failed to create session");
100
- }
101
- onSuccess == null ? void 0 : onSuccess();
102
- window.location.href = getValidRedirectUrl(redirectUrl, searchParams);
158
+ await handleSuccessfulAuth(response.user);
103
159
  }
104
160
  } catch (err) {
105
- const errorMessage = err instanceof Error ? err.message : "Failed to sign in";
106
- setError(errorMessage);
107
- onError == null ? void 0 : onError(err instanceof Error ? err : new Error("Failed to sign in"));
161
+ const errorMessage = err;
162
+ setFormError(errorMessage);
163
+ if (onError && err instanceof Error) {
164
+ onError(err);
165
+ }
108
166
  } finally {
109
167
  setLoading(false);
110
168
  }
@@ -112,8 +170,8 @@ function SignIn({
112
170
  const handleSocialSignIn = async (provider) => {
113
171
  setLoading(true);
114
172
  try {
115
- const validRedirectUrl = getValidRedirectUrl(redirectUrl, searchParams);
116
- sessionStorage.setItem("auth_redirect_url", validRedirectUrl);
173
+ const validRedirectUrl2 = getValidRedirectUrl(searchParams, redirectUrl);
174
+ sessionStorage.setItem("auth_redirect_url", validRedirectUrl2);
117
175
  const currentUrl = new URL(window.location.href);
118
176
  currentUrl.searchParams.set("signInRedirect", "true");
119
177
  window.history.replaceState({}, "", currentUrl.toString());
@@ -122,36 +180,46 @@ function SignIn({
122
180
  throw new Error(result.error);
123
181
  }
124
182
  } catch (err) {
125
- const errorMessage = err instanceof Error ? err.message : `Failed to sign in with ${provider}`;
126
- setError(errorMessage);
127
- onError == null ? void 0 : onError(err instanceof Error ? err : new Error(`Failed to sign in with ${provider}`));
183
+ const errorMessage = err;
184
+ setFormError(errorMessage);
185
+ if (onError && err instanceof Error) {
186
+ onError(err);
187
+ }
128
188
  setLoading(false);
129
189
  sessionStorage.removeItem("auth_redirect_url");
130
190
  }
131
191
  };
192
+ const handleVerificationRedirect = (e) => {
193
+ e.preventDefault();
194
+ router.push("/sign-in/verify");
195
+ };
132
196
  if (checkingRedirect && isRedirectSignIn) {
133
197
  return /* @__PURE__ */ jsx("div", { className: "flex min-h-screen items-center justify-center", children: /* @__PURE__ */ jsx("div", { className: "text-center space-y-4", children: /* @__PURE__ */ jsx("div", { className: "animate-spin rounded-full h-12 w-12 border-b-2 border-primary mx-auto" }) }) });
134
198
  }
199
+ const activeError = formError || authResponse;
200
+ const showEmailVerificationButton = (activeError == null ? void 0 : activeError.error) === "EMAIL_NOT_VERIFIED" || (activeError == null ? void 0 : activeError.error) === "REQUIRES_VERIFICATION";
135
201
  return /* @__PURE__ */ jsxs("div", { className: "relative flex items-center justify-center", children: [
136
202
  /* @__PURE__ */ jsx(AuthBackground, {}),
137
203
  /* @__PURE__ */ jsxs(Card, { className: cn("w-full max-w-md mx-auto mt-8", className, customStyles.card), children: [
138
204
  /* @__PURE__ */ jsxs(CardHeader, { className: "space-y-1 text-center", children: [
139
205
  /* @__PURE__ */ jsxs(CardTitle, { className: cn("font-bold", customStyles.title), children: [
140
206
  "Sign in to ",
141
- `${appName}`
207
+ `${appName}`,
208
+ " "
142
209
  ] }),
143
210
  /* @__PURE__ */ jsx(CardDescription, { className: cn("text-muted-foreground", customStyles.description), children: "Please sign in to continue" })
144
211
  ] }),
145
212
  /* @__PURE__ */ jsxs(CardContent, { className: "space-y-4", children: [
146
213
  /* @__PURE__ */ jsxs("form", { onSubmit: handleSubmit, className: "space-y-4", children: [
147
- error && /* @__PURE__ */ jsx(Alert, { variant: (authResponse == null ? void 0 : authResponse.error) === ERRORS.REQUIRES_VERIFICATION ? "destructive" : "destructive", children: /* @__PURE__ */ jsxs(AlertDescription, { children: [
148
- /* @__PURE__ */ jsx("span", { children: error }),
149
- (authResponse == null ? void 0 : authResponse.error) === ERRORS.REQUIRES_VERIFICATION && /* @__PURE__ */ jsx(
214
+ activeError && /* @__PURE__ */ jsx(Alert, { variant: getErrorAlertVariant(activeError), className: "animate-in fade-in-50", children: /* @__PURE__ */ jsxs(AlertDescription, { children: [
215
+ /* @__PURE__ */ jsx("span", { children: activeError.message }),
216
+ showEmailVerificationButton && /* @__PURE__ */ jsx(
150
217
  Button,
151
218
  {
219
+ type: "button",
152
220
  variant: "link",
153
221
  className: "p-0 h-auto font-normal text-sm hover:underline",
154
- onClick: () => router.push(`/sign-in/verify`),
222
+ onClick: handleVerificationRedirect,
155
223
  children: "Request new verification email \u2192"
156
224
  }
157
225
  )
@@ -168,24 +236,47 @@ function SignIn({
168
236
  onChange: (e) => setEmail(e.target.value),
169
237
  disabled: loading,
170
238
  className: cn(customStyles.input),
171
- required: true
239
+ required: true,
240
+ "aria-invalid": (activeError == null ? void 0 : activeError.error) === "INVALID_EMAIL",
241
+ "aria-describedby": activeError ? "error-message" : void 0
172
242
  }
173
243
  )
174
244
  ] }),
175
245
  /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
176
246
  /* @__PURE__ */ jsx(Label, { htmlFor: "password", className: cn(customStyles.label), children: "Password" }),
177
- /* @__PURE__ */ jsx(
178
- Input,
179
- {
180
- id: "password",
181
- type: "password",
182
- value: password,
183
- onChange: (e) => setPassword(e.target.value),
184
- disabled: loading,
185
- className: cn(customStyles.input),
186
- required: true
187
- }
188
- )
247
+ /* @__PURE__ */ jsxs("div", { className: "relative", children: [
248
+ /* @__PURE__ */ jsx(
249
+ Input,
250
+ {
251
+ id: "password",
252
+ name: "password",
253
+ type: showPassword ? "text" : "password",
254
+ value: password,
255
+ onChange: (e) => setPassword(e.target.value),
256
+ onFocus: () => setPasswordFocused(true),
257
+ onBlur: () => setPasswordFocused(false),
258
+ disabled: loading,
259
+ className: cn(customStyles.input),
260
+ required: true,
261
+ "aria-invalid": (activeError == null ? void 0 : activeError.error) === "INVALID_CREDENTIALS",
262
+ "aria-describedby": activeError ? "error-message" : void 0
263
+ }
264
+ ),
265
+ /* @__PURE__ */ jsxs(
266
+ Button,
267
+ {
268
+ type: "button",
269
+ variant: "ghost",
270
+ size: "icon",
271
+ className: "absolute right-2 top-1/2 -translate-y-1/2 h-8 w-8 hover:bg-transparent",
272
+ onClick: () => setShowPassword(!showPassword),
273
+ children: [
274
+ showPassword ? /* @__PURE__ */ jsx(EyeOff, { className: "h-4 w-4 text-muted-foreground hover:text-foreground" }) : /* @__PURE__ */ jsx(Eye, { className: "h-4 w-4 text-muted-foreground hover:text-foreground" }),
275
+ /* @__PURE__ */ jsx("span", { className: "sr-only", children: showPassword ? "Hide password" : "Show password" })
276
+ ]
277
+ }
278
+ )
279
+ ] })
189
280
  ] }),
190
281
  /* @__PURE__ */ jsx(Button, { type: "submit", disabled: loading, className: cn("w-full", customStyles.button), children: loading ? /* @__PURE__ */ jsxs(Fragment, { children: [
191
282
  /* @__PURE__ */ jsx(Loader2, { className: "mr-2 h-4 w-4 animate-spin" }),
@@ -193,8 +284,8 @@ function SignIn({
193
284
  ] }) : "Sign in" })
194
285
  ] }),
195
286
  /* @__PURE__ */ jsxs("div", { className: "relative", children: [
196
- /* @__PURE__ */ jsx("div", { className: "absolute inset-0 flex items-center", children: /* @__PURE__ */ jsx(Separator, { className: cn(customStyles.separator) }) }),
197
- /* @__PURE__ */ jsx("div", { className: "relative flex justify-center text-xs uppercase", children: /* @__PURE__ */ jsx("span", { className: "bg-background px-2 text-muted-foreground", children: "Or continue with" }) })
287
+ /* @__PURE__ */ jsx(Separator, { className: cn(customStyles.separator) }),
288
+ /* @__PURE__ */ jsx("div", { className: "absolute inset-0 flex items-center justify-center", children: /* @__PURE__ */ jsx("span", { className: "bg-background px-2 text-muted-foreground text-sm", children: "Or continue with" }) })
198
289
  ] }),
199
290
  /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-2 gap-4", children: [
200
291
  /* @__PURE__ */ jsxs(
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/components/sign-in.tsx"],"sourcesContent":["'use client'\n\nimport React, { useState, useCallback, useEffect } from 'react'\nimport { useSearchParams, useRouter, usePathname} from 'next/navigation'\nimport { signInWithEmail, signInWithRedirectGoogle, signInWithMicrosoft, type SignInResponse } from '../app-router/client/actions'\nimport { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from \"./ui/card\"\nimport { Input } from \"./ui/input\"\nimport { Label } from \"./ui/label\"\nimport { Button } from \"./ui/button\"\nimport { Alert, AlertDescription } from \"./ui/alert\"\nimport { Separator } from \"./ui/separator\"\nimport { cn } from \"../lib/utils\"\nimport { Loader2 } from 'lucide-react'\nimport { getRedirectResult } from 'firebase/auth'\nimport { ternSecureAuth } from '../utils/client-init'\nimport { createSessionCookie } from '../app-router/server/sessionTernSecure'\nimport { AuthBackground } from './background'\nimport { getValidRedirectUrl } from '../utils/construct'\nimport { ERRORS } from '../errors'\nimport { handleInternalRoute } from '../app-router/route-handler/internal-route'\n\n\nconst authDomain = process.env.NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN;\nconst appName = process.env.NEXT_PUBLIC_FIREBASE_APP_NAME || 'TernSecure';\n\n\nexport interface SignInProps {\n redirectUrl?: string\n onError?: (error: Error) => void\n onSuccess?: () => void\n requiresVerification?: boolean\n className?: string\n customStyles?: {\n card?: string\n input?: string\n button?: string\n label?: string\n separator?: string\n title?: string\n description?: string\n socialButton?: string\n }\n}\n\nexport function SignIn({\n redirectUrl,\n onError,\n onSuccess,\n requiresVerification = true,\n className,\n customStyles = {}\n}: SignInProps) {\n const [loading, setLoading] = useState(false)\n const [checkingRedirect, setCheckingRedirect] = useState(true)\n const [error, setError] = useState('')\n const [email, setEmail] = useState('')\n const [password, setPassword] = useState('')\n const [authResponse, setAuthResponse] = useState<SignInResponse | null>(null)\n const searchParams = useSearchParams()\n const isRedirectSignIn = searchParams.get('signInRedirect') === 'true'\n const router = useRouter()\n const pathname = usePathname()\n const InternalComponent = handleInternalRoute(pathname)\n\n if (InternalComponent) {\n return <InternalComponent />\n }\n\n\n const handleRedirectResult = useCallback(async () => {\n if (!isRedirectSignIn) return false\n setCheckingRedirect(true)\n try {\n console.log('Checking redirect result...');\n console.log('Current hostname:', window.location.hostname);\n console.log('Auth domain hostname:', authDomain);\n\n const isOnAuth = authDomain && \n window.location.hostname === authDomain.replace(/https?:\\/\\//, '');\n console.log('Is on AuthDomain:', isOnAuth);\n\n\n const result = await getRedirectResult(ternSecureAuth)\n console.log('Redirect result:', result);\n if (result) {\n const idToken = await result.user.getIdToken()\n const sessionResult = await createSessionCookie(idToken)\n if (!sessionResult.success) {\n throw new Error('Failed to create session')\n }\n const storedRedirectUrl = sessionStorage.getItem('auth_return_url')\n sessionStorage.removeItem('auth_redirect_url') \n onSuccess?.()\n window.location.href = storedRedirectUrl || getValidRedirectUrl(redirectUrl, searchParams)\n return true\n }\n setCheckingRedirect(false)\n } catch (err) {\n console.error('Redirect result error:', err)\n const errorMessage = err instanceof Error ? err.message : 'Authentication failed'\n setError(errorMessage)\n onError?.(err instanceof Error ? err : new Error(errorMessage))\n sessionStorage.removeItem('auth_redirect_url')\n return false\n }\n }, [isRedirectSignIn, redirectUrl, searchParams, onSuccess, onError])\n\n ///const REDIRECT_TIMEOUT = 5000;\n\n useEffect(() => {\n //let timeoutId: NodeJS.Timeout;\n\n if (isRedirectSignIn) {\n handleRedirectResult();\n\n /*timeoutId = setTimeout(() => {\n console.warn('Redirect check timed out');\n setCheckingRedirect(false);\n setError('Sign in took too long. Please try again.');\n \n }, REDIRECT_TIMEOUT);\n }\n\n return () => {\n if (timeoutId) {\n clearTimeout(timeoutId);\n }*/\n };\n }, [handleRedirectResult, isRedirectSignIn])\n\n const handleSubmit = async (e: React.FormEvent) => {\n e.preventDefault()\n setLoading(true)\n setAuthResponse(null)\n try {\n const response = await signInWithEmail(email, password)\n setAuthResponse(response)\n\n // if (response.error === ERRORS.REQUIRES_VERIFICATION) {\n // if (requiresVerification) {\n // setError(response.message || 'Email verification required')\n // return\n // }\n // }\n\n if (response.user) {\n if (requiresVerification && !response.user.emailVerified) {\n setError('Email verification required')\n return\n }\n\n const idToken = await response.user.getIdToken()\n const sessionResult = await createSessionCookie(idToken)\n\n if(!sessionResult.success) {\n throw new Error(sessionResult.message || 'Failed to create session')\n }\n\n onSuccess?.()\n window.location.href = getValidRedirectUrl(redirectUrl, searchParams)\n }\n } catch (err) {\n const errorMessage = err instanceof Error ? err.message : 'Failed to sign in'\n setError(errorMessage)\n onError?.(err instanceof Error ? err : new Error('Failed to sign in'))\n } finally {\n setLoading(false)\n }\n }\n\n const handleSocialSignIn = async (provider: 'google' | 'microsoft') => {\n setLoading(true)\n try {\n\n const validRedirectUrl = getValidRedirectUrl(redirectUrl, searchParams)\n sessionStorage.setItem('auth_redirect_url', validRedirectUrl)\n\n const currentUrl = new URL(window.location.href)\n currentUrl.searchParams.set('signInRedirect', 'true')\n window.history.replaceState({}, '', currentUrl.toString())\n\n const result = provider === 'google' ? await signInWithRedirectGoogle() : await signInWithMicrosoft()\n if (!result.success) {\n throw new Error(result.error)\n }\n } catch (err) {\n const errorMessage = err instanceof Error ? err.message : `Failed to sign in with ${provider}`\n setError(errorMessage)\n onError?.(err instanceof Error ? err : new Error(`Failed to sign in with ${provider}`))\n setLoading(false)\n sessionStorage.removeItem('auth_redirect_url')\n }\n }\n\n if (checkingRedirect && isRedirectSignIn) {\n return (\n <div className=\"flex min-h-screen items-center justify-center\">\n <div className=\"text-center space-y-4\">\n <div className=\"animate-spin rounded-full h-12 w-12 border-b-2 border-primary mx-auto\" />\n \n </div>\n </div>\n )\n }\n\n return (\n <div className=\"relative flex items-center justify-center\">\n <AuthBackground />\n <Card className={cn(\"w-full max-w-md mx-auto mt-8\", className, customStyles.card)}>\n <CardHeader className=\"space-y-1 text-center\">\n <CardTitle className={cn(\"font-bold\", customStyles.title)}>Sign in to {`${appName}`}</CardTitle>\n <CardDescription className={cn(\"text-muted-foreground\", customStyles.description)}>\n Please sign in to continue\n </CardDescription>\n </CardHeader>\n <CardContent className=\"space-y-4\">\n <form onSubmit={handleSubmit} className=\"space-y-4\">\n {error && (\n <Alert variant={authResponse?.error === ERRORS.REQUIRES_VERIFICATION ? \"destructive\" : \"destructive\"}>\n <AlertDescription>\n <span>{error}</span>\n {authResponse?.error === ERRORS.REQUIRES_VERIFICATION && (\n <Button\n variant=\"link\"\n className=\"p-0 h-auto font-normal text-sm hover:underline\"\n onClick={() => router.push(`/sign-in/verify`)}\n >\n Request new verification email →\n </Button>\n )}\n </AlertDescription>\n </Alert>\n )}\n <div className=\"space-y-2\">\n <Label htmlFor=\"email\" className={cn(customStyles.label)}>Email</Label>\n <Input\n id=\"email\"\n type=\"email\"\n placeholder=\"m@example.com\"\n value={email}\n onChange={(e) => setEmail(e.target.value)}\n disabled={loading}\n className={cn(customStyles.input)}\n required\n />\n </div>\n <div className=\"space-y-2\">\n <Label htmlFor=\"password\" className={cn(customStyles.label)}>Password</Label>\n <Input\n id=\"password\"\n type=\"password\"\n value={password}\n onChange={(e) => setPassword(e.target.value)}\n disabled={loading}\n className={cn(customStyles.input)}\n required\n />\n </div>\n <Button type=\"submit\" disabled={loading} className={cn(\"w-full\", customStyles.button)}>\n {loading ? (\n <>\n <Loader2 className=\"mr-2 h-4 w-4 animate-spin\" />\n Signing in...\n </>\n ) : (\n 'Sign in'\n )}\n </Button>\n </form>\n <div className=\"relative\">\n <div className=\"absolute inset-0 flex items-center\">\n <Separator className={cn(customStyles.separator)} />\n </div>\n <div className=\"relative flex justify-center text-xs uppercase\">\n <span className=\"bg-background px-2 text-muted-foreground\">Or continue with</span>\n </div>\n </div>\n <div className=\"grid grid-cols-2 gap-4\">\n <Button \n variant=\"outline\" \n disabled={loading} \n onClick={() => handleSocialSignIn('google')} \n className={cn(\"flex items-center justify-center\", customStyles.socialButton)}\n >\n <svg className=\"w-5 h-5 mr-2\" viewBox=\"0 0 24 24\" xmlns=\"http://www.w3.org/2000/svg\">\n <path d=\"M22.56 12.25c0-.78-.07-1.53-.2-2.25H12v4.26h5.92c-.26 1.37-1.04 2.53-2.21 3.31v2.77h3.57c2.08-1.92 3.28-4.74 3.28-8.09z\" fill=\"#4285F4\"/>\n <path d=\"M12 23c2.97 0 5.46-.98 7.28-2.66l-3.57-2.77c-.98.66-2.23 1.06-3.71 1.06-2.86 0-5.29-1.93-6.16-4.53H2.18v2.84C3.99 20.53 7.7 23 12 23z\" fill=\"#34A853\"/>\n <path d=\"M5.84 14.09c-.22-.66-.35-1.36-.35-2.09s.13-1.43.35-2.09V7.07H2.18C1.43 8.55 1 10.22 1 12s.43 3.45 1.18 4.93l2.85-2.22.81-.62z\" fill=\"#FBBC05\"/>\n <path d=\"M12 5.38c1.62 0 3.06.56 4.21 1.64l3.15-3.15C17.45 2.09 14.97 1 12 1 7.7 1 3.99 3.47 2.18 7.07l3.66 2.84c.87-2.6 3.3-4.53 6.16-4.53z\" fill=\"#EA4335\"/>\n </svg>\n Google\n </Button>\n <Button \n variant=\"outline\" \n disabled={loading} \n onClick={() => handleSocialSignIn('microsoft')} \n className={cn(\"flex items-center justify-center\", customStyles.socialButton)}\n >\n <svg className=\"w-5 h-5 mr-2\" viewBox=\"0 0 23 23\" xmlns=\"http://www.w3.org/2000/svg\">\n <path fill=\"#f3f3f3\" d=\"M0 0h23v23H0z\"/>\n <path fill=\"#f35325\" d=\"M1 1h10v10H1z\"/>\n <path fill=\"#81bc06\" d=\"M12 1h10v10H12z\"/>\n <path fill=\"#05a6f0\" d=\"M1 12h10v10H1z\"/>\n <path fill=\"#ffba08\" d=\"M12 12h10v10H12z\"/>\n </svg>\n Microsoft\n </Button>\n </div>\n </CardContent>\n <CardFooter className=\"flex justify-center\">\n <p className=\"text-sm text-muted-foreground\">\n Don&apos;t have an account?{' '}\n <a href=\"/sign-up\" className=\"text-primary hover:underline\">\n Sign up\n </a>\n </p>\n </CardFooter>\n </Card>\n </div>\n )\n}\n\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../../src/components/sign-in.tsx"],"sourcesContent":["'use client'\n\nimport React, { useState, useCallback, useEffect } from 'react'\nimport { useSearchParams, useRouter, usePathname} from 'next/navigation'\nimport { signInWithEmail, signInWithRedirectGoogle, signInWithMicrosoft } from '../app-router/client/actions'\nimport { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from \"./ui/card\"\nimport { Input } from \"./ui/input\"\nimport { Label } from \"./ui/label\"\nimport { Button } from \"./ui/button\"\nimport { Alert, AlertDescription } from \"./ui/alert\"\nimport { Separator } from \"./ui/separator\"\nimport { cn } from \"../lib/utils\"\nimport { Loader2, Eye, EyeOff } from 'lucide-react'\nimport { getRedirectResult, User } from 'firebase/auth'\nimport { ternSecureAuth } from '../utils/client-init'\nimport { createSessionCookie } from '../app-router/server/sessionTernSecure'\nimport { AuthBackground } from './background'\nimport { getValidRedirectUrl } from '../utils/construct'\nimport { handleInternalRoute } from '../app-router/route-handler/internal-route'\nimport type { SignInResponse } from '../types'\nimport { useAuth } from '../boundary/hooks/useAuth'\nimport { getErrorAlertVariant, ErrorCode } from '../errors'\n\n\n\nconst authDomain = process.env.NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN;\nconst appName = process.env.NEXT_PUBLIC_FIREBASE_APP_NAME || 'TernSecure';\n\n\nexport interface SignInProps {\n redirectUrl?: string\n onError?: (error: Error) => void\n onSuccess?: () => void\n className?: string\n customStyles?: {\n card?: string\n input?: string\n button?: string\n label?: string\n separator?: string\n title?: string\n description?: string\n socialButton?: string\n }\n}\n\n\nexport function SignIn({\n redirectUrl,\n onError,\n onSuccess,\n className,\n customStyles = {}\n}: SignInProps) {\n const [loading, setLoading] = useState(false)\n const [checkingRedirect, setCheckingRedirect] = useState(true)\n const [formError, setFormError] = useState<SignInResponse | null>(null)\n const [error, setError] = useState('')\n const [email, setEmail] = useState('')\n const [password, setPassword] = useState('')\n const [showPassword, setShowPassword] = useState(false)\n const [passwordFocused, setPasswordFocused] = useState(false)\n const [authResponse, setAuthResponse] = useState<SignInResponse | null>(null)\n const [authErrorMessage, setAuthErrorMessage] = useState<string | null>(null)\n const searchParams = useSearchParams()\n const isRedirectSignIn = searchParams.get('signInRedirect') === 'true'\n const router = useRouter()\n const pathname = usePathname()\n const InternalComponent = handleInternalRoute(pathname || \"\")\n const { requiresVerification, error: authError, status } = useAuth()\n const validRedirectUrl = getValidRedirectUrl(searchParams, redirectUrl)\n\n\n if (InternalComponent) {\n return <InternalComponent />\n }\n\n useEffect(() => {\n if (authError && status !== \"loading\" && status !== \"unauthenticated\") {\n\n const message = authError.message || \"Authentication failed\"\n setAuthErrorMessage(message)\n\n if(!authResponse || authResponse.message !== message) {\n setAuthResponse(authError as SignInResponse)\n }\n } else {\n setAuthErrorMessage(null)\n }\n }, [authError, status, authResponse])\n\n const handleSuccessfulAuth = useCallback(\n async (user: User) => {\n try {\n const idToken = await user.getIdToken()\n const sessionResult = await createSessionCookie(idToken)\n\n if (!sessionResult.success) {\n setFormError({\n success: false, \n message: sessionResult.message || \"Failed to create session\", \n error: 'INTERNAL_ERROR', \n user: null\n })\n }\n\n onSuccess?.()\n\n // Use the finalRedirectUrl for navigation\n if (process.env.NODE_ENV === \"production\") {\n // Use window.location.href in production for a full page reload\n window.location.href = validRedirectUrl\n } else {\n // Use router.push in development\n router.push(validRedirectUrl)\n }\n } catch (err) {\n setFormError({\n success: false, \n message: \"Failed to complete authentication\", \n error: 'INTERNAL_ERROR', \n user: null\n })\n }\n },\n [validRedirectUrl, router, onSuccess],\n )\n\n\n const handleRedirectResult = useCallback(async () => {\n if (!isRedirectSignIn) return false\n setCheckingRedirect(true)\n try {\n console.log('Checking redirect result...');\n console.log('Current hostname:', window.location.hostname);\n console.log('Auth domain hostname:', authDomain);\n\n const isOnAuth = authDomain && \n window.location.hostname === authDomain.replace(/https?:\\/\\//, '');\n console.log('Is on AuthDomain:', isOnAuth);\n\n\n const result = await getRedirectResult(ternSecureAuth)\n console.log('Redirect result:', result);\n if (result) {\n const idToken = await result.user.getIdToken()\n const sessionResult = await createSessionCookie(idToken)\n if (!sessionResult.success) {\n throw new Error('Failed to create session')\n }\n const storedRedirectUrl = sessionStorage.getItem('auth_return_url')\n sessionStorage.removeItem('auth_redirect_url') \n onSuccess?.()\n window.location.href = storedRedirectUrl || getValidRedirectUrl(searchParams, redirectUrl)\n return true\n }\n setCheckingRedirect(false)\n } catch (err) { \n const errorMessage = err as SignInResponse\n setFormError(errorMessage)\n if (onError && err instanceof Error) {\n onError(err)\n }\n sessionStorage.removeItem('auth_redirect_url')\n return false\n }\n }, [isRedirectSignIn, redirectUrl, searchParams, onSuccess, onError])\n\n //const REDIRECT_TIMEOUT = 5000;\n\n useEffect(() => {\n if (isRedirectSignIn) {\n handleRedirectResult()\n }\n }, [handleRedirectResult, isRedirectSignIn])\n\n const handleSubmit = async (e: React.FormEvent) => {\n e.preventDefault()\n setLoading(true)\n setFormError(null)\n setAuthResponse(null)\n\n try {\n const response= await signInWithEmail(email, password)\n setAuthResponse(response)\n\n if (!response.success) {\n setFormError({\n success: false, \n message: response.message, \n error: response.error, \n user: null\n })\n return\n }\n\n if (response.user) {\n if(requiresVerification && !response.user.emailVerified) {\n setFormError({\n success: false, \n message: 'Email verification required', \n error: 'REQUIRES_VERIFICATION', \n user: response.user\n })\n return\n }\n\n await handleSuccessfulAuth(response.user)\n }\n } catch (err) {\n const errorMessage = err as SignInResponse\n setFormError(errorMessage)\n if (onError && err instanceof Error) {\n onError(err)\n }\n } finally {\n setLoading(false)\n }\n }\n\n const handleSocialSignIn = async (provider: 'google' | 'microsoft') => {\n setLoading(true)\n try {\n\n const validRedirectUrl = getValidRedirectUrl(searchParams, redirectUrl)\n sessionStorage.setItem('auth_redirect_url', validRedirectUrl)\n\n const currentUrl = new URL(window.location.href)\n currentUrl.searchParams.set('signInRedirect', 'true')\n window.history.replaceState({}, '', currentUrl.toString())\n\n const result = provider === 'google' ? await signInWithRedirectGoogle() : await signInWithMicrosoft()\n if (!result.success) {\n throw new Error(result.error)\n }\n } catch (err) {\n const errorMessage = err as SignInResponse\n setFormError(errorMessage)\n if (onError && err instanceof Error) {\n onError(err)\n }\n setLoading(false)\n sessionStorage.removeItem('auth_redirect_url')\n }\n }\n\n const handleVerificationRedirect = (e: React.MouseEvent) => {\n e.preventDefault()\n router.push(\"/sign-in/verify\")\n }\n\n\n if (checkingRedirect && isRedirectSignIn) {\n return (\n <div className=\"flex min-h-screen items-center justify-center\">\n <div className=\"text-center space-y-4\">\n <div className=\"animate-spin rounded-full h-12 w-12 border-b-2 border-primary mx-auto\" />\n \n </div>\n </div>\n )\n }\n\n\nconst activeError = formError || authResponse\nconst showEmailVerificationButton =\n activeError?.error === \"EMAIL_NOT_VERIFIED\" || activeError?.error === \"REQUIRES_VERIFICATION\"\n\n return (\n <div className=\"relative flex items-center justify-center\">\n <AuthBackground />\n <Card className={cn(\"w-full max-w-md mx-auto mt-8\", className, customStyles.card)}>\n <CardHeader className=\"space-y-1 text-center\">\n <CardTitle className={cn(\"font-bold\", customStyles.title)}>Sign in to {`${appName}`} </CardTitle>\n <CardDescription className={cn(\"text-muted-foreground\", customStyles.description)}>\n Please sign in to continue\n </CardDescription>\n </CardHeader>\n <CardContent className=\"space-y-4\">\n <form onSubmit={handleSubmit} className=\"space-y-4\">\n {activeError && (\n <Alert variant={getErrorAlertVariant(activeError)} className=\"animate-in fade-in-50\">\n <AlertDescription>\n <span>{activeError.message}</span>\n {showEmailVerificationButton && (\n <Button\n type='button'\n variant=\"link\"\n className=\"p-0 h-auto font-normal text-sm hover:underline\"\n onClick={handleVerificationRedirect}\n >\n Request new verification email →\n </Button>\n )}\n </AlertDescription>\n </Alert>\n )}\n <div className=\"space-y-2\">\n <Label htmlFor=\"email\" className={cn(customStyles.label)}>Email</Label>\n <Input\n id=\"email\"\n type=\"email\"\n placeholder=\"m@example.com\"\n value={email}\n onChange={(e) => setEmail(e.target.value)}\n disabled={loading}\n className={cn(customStyles.input)}\n required\n aria-invalid={activeError?.error === \"INVALID_EMAIL\"}\n aria-describedby={activeError ? \"error-message\" : undefined}\n />\n </div>\n <div className=\"space-y-2\">\n <Label htmlFor=\"password\" className={cn(customStyles.label)}>Password</Label>\n <div className=\"relative\">\n <Input\n id=\"password\"\n name=\"password\"\n type={showPassword ? \"text\" : \"password\"}\n value={password}\n onChange={(e) => setPassword(e.target.value)}\n onFocus={() => setPasswordFocused(true)}\n onBlur={() => setPasswordFocused(false)}\n disabled={loading}\n className={cn(customStyles.input)}\n required\n aria-invalid={activeError?.error === \"INVALID_CREDENTIALS\"}\n aria-describedby={activeError ? \"error-message\" : undefined}\n />\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"icon\"\n className=\"absolute right-2 top-1/2 -translate-y-1/2 h-8 w-8 hover:bg-transparent\"\n onClick={() => setShowPassword(!showPassword)}\n >\n {showPassword ? (\n <EyeOff className=\"h-4 w-4 text-muted-foreground hover:text-foreground\" />\n ) : (\n <Eye className=\"h-4 w-4 text-muted-foreground hover:text-foreground\" />\n )}\n <span className=\"sr-only\">{showPassword ? \"Hide password\" : \"Show password\"}</span>\n </Button>\n </div>\n </div>\n <Button type=\"submit\" disabled={loading} className={cn(\"w-full\", customStyles.button)}>\n {loading ? (\n <>\n <Loader2 className=\"mr-2 h-4 w-4 animate-spin\" />\n Signing in...\n </>\n ) : (\n 'Sign in'\n )}\n </Button>\n </form>\n <div className=\"relative\">\n <Separator className={cn(customStyles.separator)} />\n <div className=\"absolute inset-0 flex items-center justify-center\">\n <span className=\"bg-background px-2 text-muted-foreground text-sm\">Or continue with</span>\n </div>\n </div>\n <div className=\"grid grid-cols-2 gap-4\">\n <Button \n variant=\"outline\" \n disabled={loading} \n onClick={() => handleSocialSignIn('google')} \n className={cn(\"flex items-center justify-center\", customStyles.socialButton)}\n >\n <svg className=\"w-5 h-5 mr-2\" viewBox=\"0 0 24 24\" xmlns=\"http://www.w3.org/2000/svg\">\n <path d=\"M22.56 12.25c0-.78-.07-1.53-.2-2.25H12v4.26h5.92c-.26 1.37-1.04 2.53-2.21 3.31v2.77h3.57c2.08-1.92 3.28-4.74 3.28-8.09z\" fill=\"#4285F4\"/>\n <path d=\"M12 23c2.97 0 5.46-.98 7.28-2.66l-3.57-2.77c-.98.66-2.23 1.06-3.71 1.06-2.86 0-5.29-1.93-6.16-4.53H2.18v2.84C3.99 20.53 7.7 23 12 23z\" fill=\"#34A853\"/>\n <path d=\"M5.84 14.09c-.22-.66-.35-1.36-.35-2.09s.13-1.43.35-2.09V7.07H2.18C1.43 8.55 1 10.22 1 12s.43 3.45 1.18 4.93l2.85-2.22.81-.62z\" fill=\"#FBBC05\"/>\n <path d=\"M12 5.38c1.62 0 3.06.56 4.21 1.64l3.15-3.15C17.45 2.09 14.97 1 12 1 7.7 1 3.99 3.47 2.18 7.07l3.66 2.84c.87-2.6 3.3-4.53 6.16-4.53z\" fill=\"#EA4335\"/>\n </svg>\n Google\n </Button>\n <Button \n variant=\"outline\" \n disabled={loading} \n onClick={() => handleSocialSignIn('microsoft')} \n className={cn(\"flex items-center justify-center\", customStyles.socialButton)}\n >\n <svg className=\"w-5 h-5 mr-2\" viewBox=\"0 0 23 23\" xmlns=\"http://www.w3.org/2000/svg\">\n <path fill=\"#f3f3f3\" d=\"M0 0h23v23H0z\"/>\n <path fill=\"#f35325\" d=\"M1 1h10v10H1z\"/>\n <path fill=\"#81bc06\" d=\"M12 1h10v10H12z\"/>\n <path fill=\"#05a6f0\" d=\"M1 12h10v10H1z\"/>\n <path fill=\"#ffba08\" d=\"M12 12h10v10H12z\"/>\n </svg>\n Microsoft\n </Button>\n </div>\n </CardContent>\n <CardFooter className=\"flex justify-center\">\n <p className=\"text-sm text-muted-foreground\">\n Don&apos;t have an account?{' '}\n <a href=\"/sign-up\" className=\"text-primary hover:underline\">\n Sign up\n </a>\n </p>\n </CardFooter>\n </Card>\n </div>\n )\n}\n\n"],"mappings":"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","names":["validRedirectUrl"]}
@@ -1,30 +1,39 @@
1
1
  "use client";
2
2
  import { jsx } from "react/jsx-runtime";
3
3
  import { useState } from "react";
4
- import { useRouter } from "next/navigation";
4
+ import { usePathname, useRouter } from "next/navigation";
5
5
  import { signOut } from "firebase/auth";
6
6
  import { Button } from "./ui/button";
7
7
  import { ternSecureAuth } from "../utils/client-init";
8
8
  import { clearSessionCookie } from "../app-router/server/sessionTernSecure";
9
9
  import { cn } from "../lib/utils";
10
+ import { constructUrlWithRedirect } from "../utils/construct";
10
11
  function SignOutButton({
11
12
  children = "Sign out",
12
13
  onError,
13
14
  onSignOutSuccess,
15
+ redirectPath,
14
16
  className,
15
17
  variant = "outline",
16
18
  size = "default",
17
19
  ...buttonProps
18
20
  }) {
21
+ const pathname = usePathname();
19
22
  const router = useRouter();
20
23
  const [isLoading, setIsLoading] = useState(false);
24
+ const loginPath = process.env.NEXT_PUBLIC_LOGIN_PATH || "/sign-in";
21
25
  const handleSignOut = async () => {
22
26
  setIsLoading(true);
23
27
  try {
24
28
  await signOut(ternSecureAuth);
25
29
  await clearSessionCookie();
26
30
  onSignOutSuccess == null ? void 0 : onSignOutSuccess();
27
- router.push("/sign-in");
31
+ const loginUrl = constructUrlWithRedirect(loginPath, pathname);
32
+ if (process.env.NODE_ENV === "production") {
33
+ window.location.href = loginUrl;
34
+ } else {
35
+ router.push(loginUrl);
36
+ }
28
37
  } catch (error) {
29
38
  console.error("Sign out error:", error);
30
39
  onError == null ? void 0 : onError(error instanceof Error ? error : new Error("Failed to sign out"));
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/components/sign-out-button.tsx"],"sourcesContent":["'use client'\n\nimport { useState } from 'react'\nimport { useRouter } from 'next/navigation'\nimport { signOut } from 'firebase/auth'\nimport { Button, type ButtonProps } from './ui/button'\nimport { ternSecureAuth } from '../utils/client-init'\nimport { clearSessionCookie } from '../app-router/server/sessionTernSecure'\nimport { cn } from '../lib/utils'\n\ntype SignOutCustomProps = {\n children?: React.ReactNode\n onError?: (error: Error) => void\n onSignOutSuccess?: () => void\n className?: string\n variant?: ButtonProps['variant']\n size?: ButtonProps['size']\n}\n\ntype SignOutProps = Omit<ButtonProps, 'onClick'> & SignOutCustomProps\n\nexport function SignOutButton({ \n children = 'Sign out', \n onError,\n onSignOutSuccess,\n className,\n variant = 'outline',\n size = 'default',\n ...buttonProps \n}: SignOutProps) {\n const router = useRouter()\n const [isLoading, setIsLoading] = useState(false)\n\n const handleSignOut = async () => {\n setIsLoading(true)\n try {\n // Sign out from Firebase\n await signOut(ternSecureAuth)\n \n await clearSessionCookie()\n \n // Call success callback if provided\n onSignOutSuccess?.()\n \n // Redirect to sign-in page\n router.push('/sign-in') //todo: singout and include a redirect URl\n } catch (error) {\n console.error('Sign out error:', error)\n onError?.(error instanceof Error ? error : new Error('Failed to sign out'))\n } finally {\n setIsLoading(false)\n }\n }\n\n return (\n <Button\n variant={variant}\n size={size}\n onClick={handleSignOut}\n disabled={isLoading}\n className={cn(\"\", className)}\n {...buttonProps}\n >\n {isLoading ? 'Signing out...' : children}\n </Button>\n )\n}\n\n"],"mappings":";AAuDI;AArDJ,SAAS,gBAAgB;AACzB,SAAS,iBAAiB;AAC1B,SAAS,eAAe;AACxB,SAAS,cAAgC;AACzC,SAAS,sBAAsB;AAC/B,SAAS,0BAA0B;AACnC,SAAS,UAAU;AAaZ,SAAS,cAAc;AAAA,EAC5B,WAAW;AAAA,EACX;AAAA,EACA;AAAA,EACA;AAAA,EACA,UAAU;AAAA,EACV,OAAO;AAAA,EACP,GAAG;AACL,GAAiB;AACf,QAAM,SAAS,UAAU;AACzB,QAAM,CAAC,WAAW,YAAY,IAAI,SAAS,KAAK;AAEhD,QAAM,gBAAgB,YAAY;AAChC,iBAAa,IAAI;AACjB,QAAI;AAEF,YAAM,QAAQ,cAAc;AAE5B,YAAM,mBAAmB;AAGzB;AAGA,aAAO,KAAK,UAAU;AAAA,IACxB,SAAS,OAAO;AACd,cAAQ,MAAM,mBAAmB,KAAK;AACtC,yCAAU,iBAAiB,QAAQ,QAAQ,IAAI,MAAM,oBAAoB;AAAA,IAC3E,UAAE;AACA,mBAAa,KAAK;AAAA,IACpB;AAAA,EACF;AAEA,SACE;AAAA,IAAC;AAAA;AAAA,MACC;AAAA,MACA;AAAA,MACA,SAAS;AAAA,MACT,UAAU;AAAA,MACV,WAAW,GAAG,IAAI,SAAS;AAAA,MAC1B,GAAG;AAAA,MAEH,sBAAY,mBAAmB;AAAA;AAAA,EAClC;AAEJ;","names":[]}
1
+ {"version":3,"sources":["../../../src/components/sign-out-button.tsx"],"sourcesContent":["'use client'\n\nimport { useState } from 'react'\nimport { usePathname, useRouter } from 'next/navigation'\nimport { signOut } from 'firebase/auth'\nimport { Button, type ButtonProps } from './ui/button'\nimport { ternSecureAuth } from '../utils/client-init'\nimport { clearSessionCookie } from '../app-router/server/sessionTernSecure'\nimport { cn } from '../lib/utils'\nimport { constructUrlWithRedirect } from '../utils/construct'\n\n\ntype SignOutCustomProps = {\n children?: React.ReactNode\n onError?: (error: Error) => void\n onSignOutSuccess?: () => void\n redirectPath?: string\n className?: string\n variant?: ButtonProps['variant']\n size?: ButtonProps['size']\n}\n\ntype SignOutProps = Omit<ButtonProps, 'onClick'> & SignOutCustomProps\n\nexport function SignOutButton({ \n children = 'Sign out', \n onError,\n onSignOutSuccess,\n redirectPath,\n className,\n variant = 'outline',\n size = 'default',\n ...buttonProps \n}: SignOutProps) {\n const pathname = usePathname()\n const router = useRouter()\n const [isLoading, setIsLoading] = useState(false)\n const loginPath = process.env.NEXT_PUBLIC_LOGIN_PATH || '/sign-in'\n\n const handleSignOut = async () => {\n setIsLoading(true)\n try {\n // Sign out from Firebase\n await signOut(ternSecureAuth)\n \n await clearSessionCookie()\n \n // Call success callback if provided\n onSignOutSuccess?.()\n\n // Construct login URL with redirect parameter\n const loginUrl = constructUrlWithRedirect(loginPath, pathname)\n\n // Use router for development and window.location for production\n if (process.env.NODE_ENV === \"production\") {\n window.location.href = loginUrl\n } else {\n router.push(loginUrl)\n }\n } catch (error) {\n console.error('Sign out error:', error)\n onError?.(error instanceof Error ? error : new Error('Failed to sign out'))\n } finally {\n setIsLoading(false)\n }\n }\n\n return (\n <Button\n variant={variant}\n size={size}\n onClick={handleSignOut}\n disabled={isLoading}\n className={cn(\"\", className)}\n {...buttonProps}\n >\n {isLoading ? 'Signing out...' : children}\n </Button>\n )\n}\n\n"],"mappings":";AAoEI;AAlEJ,SAAS,gBAAgB;AACzB,SAAS,aAAa,iBAAiB;AACvC,SAAS,eAAe;AACxB,SAAS,cAAgC;AACzC,SAAS,sBAAsB;AAC/B,SAAS,0BAA0B;AACnC,SAAS,UAAU;AACnB,SAAS,gCAAgC;AAelC,SAAS,cAAc;AAAA,EAC5B,WAAW;AAAA,EACX;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,UAAU;AAAA,EACV,OAAO;AAAA,EACP,GAAG;AACL,GAAiB;AACf,QAAM,WAAW,YAAY;AAC7B,QAAM,SAAS,UAAU;AACzB,QAAM,CAAC,WAAW,YAAY,IAAI,SAAS,KAAK;AAChD,QAAM,YAAY,QAAQ,IAAI,0BAA0B;AAExD,QAAM,gBAAgB,YAAY;AAChC,iBAAa,IAAI;AACjB,QAAI;AAEF,YAAM,QAAQ,cAAc;AAE5B,YAAM,mBAAmB;AAGzB;AAGA,YAAM,WAAW,yBAAyB,WAAW,QAAQ;AAG7D,UAAI,QAAQ,IAAI,aAAa,cAAc;AACzC,eAAO,SAAS,OAAO;AAAA,MACzB,OAAO;AACL,eAAO,KAAK,QAAQ;AAAA,MACtB;AAAA,IACF,SAAS,OAAO;AACd,cAAQ,MAAM,mBAAmB,KAAK;AACtC,yCAAU,iBAAiB,QAAQ,QAAQ,IAAI,MAAM,oBAAoB;AAAA,IAC3E,UAAE;AACA,mBAAa,KAAK;AAAA,IACpB;AAAA,EACF;AAEA,SACE;AAAA,IAAC;AAAA;AAAA,MACC;AAAA,MACA;AAAA,MACA,SAAS;AAAA,MACT,UAAU;AAAA,MACV,WAAW,GAAG,IAAI,SAAS;AAAA,MAC1B,GAAG;AAAA,MAEH,sBAAY,mBAAmB;AAAA;AAAA,EAClC;AAEJ;","names":[]}
@@ -1,22 +1,26 @@
1
1
  "use client";
2
2
  import { jsx } from "react/jsx-runtime";
3
3
  import { useState } from "react";
4
- import { useRouter } from "next/navigation";
4
+ import { usePathname, useRouter } from "next/navigation";
5
5
  import { signOut } from "firebase/auth";
6
6
  import { ternSecureAuth } from "../utils/client-init";
7
7
  import { clearSessionCookie } from "../app-router/server/sessionTernSecure";
8
8
  import { cn } from "../lib/utils";
9
9
  import Link from "next/link";
10
+ import { constructUrlWithRedirect } from "../utils/construct";
10
11
  function SignOut({
11
12
  children = "Sign out",
12
13
  onError,
13
14
  onSignOutSuccess,
14
15
  className,
15
16
  activeClassName,
16
- disabled = false
17
+ disabled = false,
18
+ redirectPath
17
19
  }) {
18
- const router = useRouter();
19
20
  const [isLoading, setIsLoading] = useState(false);
21
+ const pathname = usePathname();
22
+ const router = useRouter();
23
+ const loginPath = process.env.NEXT_PUBLIC_LOGIN_PATH || "/sign-in";
20
24
  const handleSignOut = async (e) => {
21
25
  e.preventDefault();
22
26
  if (disabled || isLoading) return;
@@ -25,7 +29,12 @@ function SignOut({
25
29
  await signOut(ternSecureAuth);
26
30
  await clearSessionCookie();
27
31
  onSignOutSuccess == null ? void 0 : onSignOutSuccess();
28
- router.push("/sign-in");
32
+ const loginUrl = constructUrlWithRedirect(loginPath, pathname);
33
+ if (process.env.NODE_ENV === "production") {
34
+ window.location.href = loginUrl;
35
+ } else {
36
+ router.push(loginUrl);
37
+ }
29
38
  } catch (error) {
30
39
  console.error("Sign out error:", error);
31
40
  onError == null ? void 0 : onError(error instanceof Error ? error : new Error("Failed to sign out"));
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/components/sign-out.tsx"],"sourcesContent":["'use client'\n\nimport { useState } from 'react'\nimport { useRouter } from 'next/navigation'\nimport { signOut } from 'firebase/auth'\nimport { ternSecureAuth } from '../utils/client-init'\nimport { clearSessionCookie } from '../app-router/server/sessionTernSecure'\nimport { cn } from '../lib/utils'\nimport Link from 'next/link'\n\ninterface SignOutLinkProps {\n children?: React.ReactNode\n onError?: (error: Error) => void\n onSignOutSuccess?: () => void\n className?: string\n activeClassName?: string\n disabled?: boolean\n}\n\nexport function SignOut({\n children = 'Sign out',\n onError,\n onSignOutSuccess,\n className,\n activeClassName,\n disabled = false,\n}: SignOutLinkProps) {\n const router = useRouter()\n const [isLoading, setIsLoading] = useState(false)\n\n const handleSignOut = async (e: React.MouseEvent<HTMLAnchorElement>) => {\n e.preventDefault()\n if (disabled || isLoading) return\n\n setIsLoading(true)\n try {\n await signOut(ternSecureAuth)\n await clearSessionCookie()\n onSignOutSuccess?.()\n router.push('/sign-in')\n } catch (error) {\n console.error('Sign out error:', error)\n onError?.(error instanceof Error ? error : new Error('Failed to sign out'))\n } finally {\n setIsLoading(false)\n }\n }\n\n return (\n <Link\n href=\"#\"\n onClick={handleSignOut}\n className={cn(\n 'text-sm font-medium transition-colors hover:text-primary',\n disabled && 'pointer-events-none opacity-50',\n isLoading && 'pointer-events-none',\n className,\n isLoading && activeClassName\n )}\n aria-disabled={disabled || isLoading}\n >\n {isLoading ? 'Signing out...' : children}\n </Link>\n )\n}"],"mappings":";AAiDI;AA/CJ,SAAS,gBAAgB;AACzB,SAAS,iBAAiB;AAC1B,SAAS,eAAe;AACxB,SAAS,sBAAsB;AAC/B,SAAS,0BAA0B;AACnC,SAAS,UAAU;AACnB,OAAO,UAAU;AAWV,SAAS,QAAQ;AAAA,EACtB,WAAW;AAAA,EACX;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,WAAW;AACb,GAAqB;AACnB,QAAM,SAAS,UAAU;AACzB,QAAM,CAAC,WAAW,YAAY,IAAI,SAAS,KAAK;AAEhD,QAAM,gBAAgB,OAAO,MAA2C;AACtE,MAAE,eAAe;AACjB,QAAI,YAAY,UAAW;AAE3B,iBAAa,IAAI;AACjB,QAAI;AACF,YAAM,QAAQ,cAAc;AAC5B,YAAM,mBAAmB;AACzB;AACA,aAAO,KAAK,UAAU;AAAA,IACxB,SAAS,OAAO;AACd,cAAQ,MAAM,mBAAmB,KAAK;AACtC,yCAAU,iBAAiB,QAAQ,QAAQ,IAAI,MAAM,oBAAoB;AAAA,IAC3E,UAAE;AACA,mBAAa,KAAK;AAAA,IACpB;AAAA,EACF;AAEA,SACE;AAAA,IAAC;AAAA;AAAA,MACC,MAAK;AAAA,MACL,SAAS;AAAA,MACT,WAAW;AAAA,QACT;AAAA,QACA,YAAY;AAAA,QACZ,aAAa;AAAA,QACb;AAAA,QACA,aAAa;AAAA,MACf;AAAA,MACA,iBAAe,YAAY;AAAA,MAE1B,sBAAY,mBAAmB;AAAA;AAAA,EAClC;AAEJ;","names":[]}
1
+ {"version":3,"sources":["../../../src/components/sign-out.tsx"],"sourcesContent":["'use client'\n\nimport { useState } from 'react'\nimport { usePathname, useRouter } from 'next/navigation'\nimport { signOut } from 'firebase/auth'\nimport { ternSecureAuth } from '../utils/client-init'\nimport { clearSessionCookie } from '../app-router/server/sessionTernSecure'\nimport { cn } from '../lib/utils'\nimport Link from 'next/link'\nimport { constructUrlWithRedirect } from '../utils/construct'\n\n\ninterface SignOutLinkProps {\n children?: React.ReactNode\n onError?: (error: Error) => void\n onSignOutSuccess?: () => void\n className?: string\n activeClassName?: string\n disabled?: boolean\n redirectPath?: string\n}\n\nexport function SignOut({\n children = 'Sign out',\n onError,\n onSignOutSuccess,\n className,\n activeClassName,\n disabled = false,\n redirectPath,\n}: SignOutLinkProps) {\n const [isLoading, setIsLoading] = useState(false)\n const pathname = usePathname()\n const router = useRouter()\n const loginPath = process.env.NEXT_PUBLIC_LOGIN_PATH || \"/sign-in\"\n\n const handleSignOut = async (e: React.MouseEvent<HTMLAnchorElement>) => {\n e.preventDefault()\n if (disabled || isLoading) return\n\n setIsLoading(true)\n try {\n // Sign out from Firebase\n await signOut(ternSecureAuth)\n\n // Clear the session cookie\n await clearSessionCookie()\n\n // Call success callback if provided\n onSignOutSuccess?.()\n\n // Construct login URL with redirect parameter\n const loginUrl = constructUrlWithRedirect(loginPath, pathname)\n\n // Use router for development and window.location for production\n if (process.env.NODE_ENV === \"production\") {\n window.location.href = loginUrl\n } else {\n router.push(loginUrl)\n }\n } catch (error) {\n console.error('Sign out error:', error)\n onError?.(error instanceof Error ? error : new Error('Failed to sign out'))\n } finally {\n setIsLoading(false)\n }\n }\n\n return (\n <Link\n href=\"#\"\n onClick={handleSignOut}\n className={cn(\n 'text-sm font-medium transition-colors hover:text-primary',\n disabled && 'pointer-events-none opacity-50',\n isLoading && 'pointer-events-none',\n className,\n isLoading && activeClassName\n )}\n aria-disabled={disabled || isLoading}\n >\n {isLoading ? 'Signing out...' : children}\n </Link>\n )\n}"],"mappings":";AAqEI;AAnEJ,SAAS,gBAAgB;AACzB,SAAS,aAAa,iBAAiB;AACvC,SAAS,eAAe;AACxB,SAAS,sBAAsB;AAC/B,SAAS,0BAA0B;AACnC,SAAS,UAAU;AACnB,OAAO,UAAU;AACjB,SAAS,gCAAgC;AAalC,SAAS,QAAQ;AAAA,EACtB,WAAW;AAAA,EACX;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,WAAW;AAAA,EACX;AACF,GAAqB;AACnB,QAAM,CAAC,WAAW,YAAY,IAAI,SAAS,KAAK;AAChD,QAAM,WAAW,YAAY;AAC7B,QAAM,SAAS,UAAU;AACzB,QAAM,YAAY,QAAQ,IAAI,0BAA0B;AAExD,QAAM,gBAAgB,OAAO,MAA2C;AACtE,MAAE,eAAe;AACjB,QAAI,YAAY,UAAW;AAE3B,iBAAa,IAAI;AACjB,QAAI;AAEF,YAAM,QAAQ,cAAc;AAG5B,YAAM,mBAAmB;AAGzB;AAGA,YAAM,WAAW,yBAAyB,WAAW,QAAQ;AAG7D,UAAI,QAAQ,IAAI,aAAa,cAAc;AACzC,eAAO,SAAS,OAAO;AAAA,MACzB,OAAO;AACL,eAAO,KAAK,QAAQ;AAAA,MACtB;AAAA,IACF,SAAS,OAAO;AACd,cAAQ,MAAM,mBAAmB,KAAK;AACtC,yCAAU,iBAAiB,QAAQ,QAAQ,IAAI,MAAM,oBAAoB;AAAA,IAC3E,UAAE;AACA,mBAAa,KAAK;AAAA,IACpB;AAAA,EACF;AAEA,SACE;AAAA,IAAC;AAAA;AAAA,MACC,MAAK;AAAA,MACL,SAAS;AAAA,MACT,WAAW;AAAA,QACT;AAAA,QACA,YAAY;AAAA,QACZ,aAAa;AAAA,QACb;AAAA,QACA,aAAa;AAAA,MACf;AAAA,MACA,iBAAe,YAAY;AAAA,MAE1B,sBAAY,mBAAmB;AAAA;AAAA,EAClC;AAEJ;","names":[]}