@mesob/auth-react 0.3.5 → 0.4.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 (124) hide show
  1. package/dist/components/auth/forgot-password.js +5 -1
  2. package/dist/components/auth/forgot-password.js.map +1 -1
  3. package/dist/components/auth/reset-password-form.js +5 -1
  4. package/dist/components/auth/reset-password-form.js.map +1 -1
  5. package/dist/components/auth/set-password.d.ts +9 -0
  6. package/dist/components/auth/set-password.js +527 -0
  7. package/dist/components/auth/set-password.js.map +1 -0
  8. package/dist/components/auth/sign-in.js +22 -1
  9. package/dist/components/auth/sign-in.js.map +1 -1
  10. package/dist/components/auth/sign-up.js +7 -5
  11. package/dist/components/auth/sign-up.js.map +1 -1
  12. package/dist/components/auth/verify-email.js +5 -1
  13. package/dist/components/auth/verify-email.js.map +1 -1
  14. package/dist/components/auth/verify-phone.js +5 -1
  15. package/dist/components/auth/verify-phone.js.map +1 -1
  16. package/dist/components/authorization/deny.d.ts +11 -0
  17. package/dist/components/authorization/deny.js +52 -0
  18. package/dist/components/authorization/deny.js.map +1 -0
  19. package/dist/components/authorization/grant.d.ts +12 -0
  20. package/dist/components/authorization/grant.js +57 -0
  21. package/dist/components/authorization/grant.js.map +1 -0
  22. package/dist/components/error-boundary.d.ts +2 -2
  23. package/dist/components/iam/permission-selector.d.ts +19 -0
  24. package/dist/components/iam/permission-selector.js +122 -0
  25. package/dist/components/iam/permission-selector.js.map +1 -0
  26. package/dist/components/iam/permissions.js +12 -31
  27. package/dist/components/iam/permissions.js.map +1 -1
  28. package/dist/components/iam/role-detail-layout.d.ts +11 -0
  29. package/dist/components/iam/role-detail-layout.js +137 -0
  30. package/dist/components/iam/role-detail-layout.js.map +1 -0
  31. package/dist/components/iam/role-detail-page.d.ts +9 -0
  32. package/dist/components/iam/role-detail-page.js +229 -0
  33. package/dist/components/iam/role-detail-page.js.map +1 -0
  34. package/dist/components/iam/role-permissions-page.d.ts +8 -0
  35. package/dist/components/iam/role-permissions-page.js +397 -0
  36. package/dist/components/iam/role-permissions-page.js.map +1 -0
  37. package/dist/components/iam/roles.js +11 -8
  38. package/dist/components/iam/roles.js.map +1 -1
  39. package/dist/components/iam/users.js +1 -7
  40. package/dist/components/iam/users.js.map +1 -1
  41. package/dist/components/profile/account.js +110 -19
  42. package/dist/components/profile/account.js.map +1 -1
  43. package/dist/components/profile/change-profile.d.ts +2 -1
  44. package/dist/components/profile/change-profile.js +16 -8
  45. package/dist/components/profile/change-profile.js.map +1 -1
  46. package/dist/components/profile/security.js +51 -17
  47. package/dist/components/profile/security.js.map +1 -1
  48. package/dist/index.d.ts +9 -1
  49. package/dist/index.js +1813 -725
  50. package/dist/index.js.map +1 -1
  51. package/dist/pages/auth/forgot-password.d.ts +7 -0
  52. package/dist/pages/auth/forgot-password.js +784 -0
  53. package/dist/pages/auth/forgot-password.js.map +1 -0
  54. package/dist/pages/auth/layout.d.ts +8 -0
  55. package/dist/pages/auth/layout.js +562 -0
  56. package/dist/pages/auth/layout.js.map +1 -0
  57. package/dist/pages/auth/reset-password.d.ts +10 -0
  58. package/dist/pages/auth/reset-password.js +913 -0
  59. package/dist/pages/auth/reset-password.js.map +1 -0
  60. package/dist/pages/auth/set-password.d.ts +10 -0
  61. package/dist/pages/auth/set-password.js +946 -0
  62. package/dist/pages/auth/set-password.js.map +1 -0
  63. package/dist/pages/auth/sign-in.d.ts +10 -0
  64. package/dist/pages/auth/sign-in.js +984 -0
  65. package/dist/pages/auth/sign-in.js.map +1 -0
  66. package/dist/pages/auth/sign-up.d.ts +10 -0
  67. package/dist/pages/auth/sign-up.js +940 -0
  68. package/dist/pages/auth/sign-up.js.map +1 -0
  69. package/dist/pages/auth/verify-email.d.ts +10 -0
  70. package/dist/pages/auth/verify-email.js +950 -0
  71. package/dist/pages/auth/verify-email.js.map +1 -0
  72. package/dist/pages/auth/verify-phone.d.ts +10 -0
  73. package/dist/pages/auth/verify-phone.js +964 -0
  74. package/dist/pages/auth/verify-phone.js.map +1 -0
  75. package/dist/pages/iam/permissions.d.ts +5 -0
  76. package/dist/pages/iam/permissions.js +308 -0
  77. package/dist/pages/iam/permissions.js.map +1 -0
  78. package/dist/pages/iam/role-detail-layout.d.ts +12 -0
  79. package/dist/pages/iam/role-detail-layout.js +145 -0
  80. package/dist/pages/iam/role-detail-layout.js.map +1 -0
  81. package/dist/pages/iam/role-detail.d.ts +12 -0
  82. package/dist/pages/iam/role-detail.js +241 -0
  83. package/dist/pages/iam/role-detail.js.map +1 -0
  84. package/dist/pages/iam/role-permissions.d.ts +12 -0
  85. package/dist/pages/iam/role-permissions.js +409 -0
  86. package/dist/pages/iam/role-permissions.js.map +1 -0
  87. package/dist/pages/iam/role-users.d.ts +12 -0
  88. package/dist/pages/iam/role-users.js +825 -0
  89. package/dist/pages/iam/role-users.js.map +1 -0
  90. package/dist/pages/iam/roles.d.ts +5 -0
  91. package/dist/pages/iam/roles.js +684 -0
  92. package/dist/pages/iam/roles.js.map +1 -0
  93. package/dist/pages/iam/sessions.d.ts +5 -0
  94. package/dist/pages/iam/sessions.js +315 -0
  95. package/dist/pages/iam/sessions.js.map +1 -0
  96. package/dist/pages/iam/tenant-detail.d.ts +10 -0
  97. package/dist/pages/iam/tenant-detail.js +186 -0
  98. package/dist/pages/iam/tenant-detail.js.map +1 -0
  99. package/dist/pages/iam/tenants.d.ts +5 -0
  100. package/dist/pages/iam/tenants.js +610 -0
  101. package/dist/pages/iam/tenants.js.map +1 -0
  102. package/dist/pages/iam/user-activity.d.ts +10 -0
  103. package/dist/pages/iam/user-activity.js +850 -0
  104. package/dist/pages/iam/user-activity.js.map +1 -0
  105. package/dist/pages/iam/user-detail-layout.d.ts +12 -0
  106. package/dist/pages/iam/user-detail-layout.js +106 -0
  107. package/dist/pages/iam/user-detail-layout.js.map +1 -0
  108. package/dist/pages/iam/user-detail.d.ts +10 -0
  109. package/dist/pages/iam/user-detail.js +102 -0
  110. package/dist/pages/iam/user-detail.js.map +1 -0
  111. package/dist/pages/iam/users.d.ts +5 -0
  112. package/dist/pages/iam/users.js +1275 -0
  113. package/dist/pages/iam/users.js.map +1 -0
  114. package/dist/pages/profile/account.d.ts +5 -0
  115. package/dist/pages/profile/account.js +182 -0
  116. package/dist/pages/profile/account.js.map +1 -0
  117. package/dist/pages/profile/layout.d.ts +8 -0
  118. package/dist/pages/profile/layout.js +133 -0
  119. package/dist/pages/profile/layout.js.map +1 -0
  120. package/dist/pages/profile/security.d.ts +5 -0
  121. package/dist/pages/profile/security.js +1539 -0
  122. package/dist/pages/profile/security.js.map +1 -0
  123. package/dist/{types-vcfvnAzQ.d.ts → types-g9QcNRxT.d.ts} +13 -7
  124. package/package.json +102 -3
@@ -0,0 +1,527 @@
1
+ "use client";
2
+
3
+ // src/components/auth/set-password.tsx
4
+ import { zodResolver } from "@hookform/resolvers/zod";
5
+ import {
6
+ Alert,
7
+ AlertDescription,
8
+ AlertTitle,
9
+ Button,
10
+ Form,
11
+ FormControl,
12
+ FormField,
13
+ FormItem,
14
+ FormLabel,
15
+ FormMessage,
16
+ Input,
17
+ useFormField
18
+ } from "@mesob/ui/components";
19
+ import { useMesob as useMesob2 } from "@mesob/ui/providers";
20
+ import { IconAlertCircle, IconEye, IconEyeOff } from "@tabler/icons-react";
21
+ import { useEffect, useEffectEvent, useState as useState2 } from "react";
22
+ import { useForm } from "react-hook-form";
23
+ import { toast } from "sonner";
24
+ import { z } from "zod";
25
+
26
+ // src/hooks/use-translator.ts
27
+ import { useMesob } from "@mesob/ui/providers";
28
+
29
+ // src/lib/translations.ts
30
+ function createTranslator(messages, namespace) {
31
+ return (key, params) => {
32
+ const fullKey = namespace ? `${namespace}.${key}` : key;
33
+ const keys = fullKey.split(".");
34
+ let value = messages;
35
+ for (const k of keys) {
36
+ if (value && typeof value === "object" && value !== null) {
37
+ value = value[k];
38
+ } else {
39
+ return fullKey;
40
+ }
41
+ }
42
+ if (typeof value !== "string") {
43
+ return fullKey;
44
+ }
45
+ if (params) {
46
+ return value.replace(
47
+ /\{(\w+)\}/g,
48
+ (_, param) => String(params[param] ?? `{${param}}`)
49
+ );
50
+ }
51
+ return value;
52
+ };
53
+ }
54
+
55
+ // src/provider.tsx
56
+ import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
57
+ import { deepmerge } from "deepmerge-ts";
58
+ import createFetchClient from "openapi-fetch";
59
+ import createClient from "openapi-react-query";
60
+ import { createContext, useContext, useMemo, useState } from "react";
61
+
62
+ // src/utils/cookie.ts
63
+ var isProduction = typeof process !== "undefined" && process.env.NODE_ENV === "production";
64
+
65
+ // src/provider.tsx
66
+ import { jsx } from "react/jsx-runtime";
67
+ var SessionContext = createContext(null);
68
+ var ApiContext = createContext(null);
69
+ var ConfigContext = createContext(null);
70
+ var queryClient = new QueryClient({
71
+ defaultOptions: {
72
+ queries: {
73
+ refetchOnWindowFocus: false
74
+ }
75
+ }
76
+ });
77
+ function useApi() {
78
+ const context = useContext(ApiContext);
79
+ if (!context) {
80
+ throw new Error("useApi must be used within MesobAuthProvider");
81
+ }
82
+ return context;
83
+ }
84
+ function useConfig() {
85
+ const context = useContext(ConfigContext);
86
+ if (!context) {
87
+ throw new Error("useConfig must be used within MesobAuthProvider");
88
+ }
89
+ return context;
90
+ }
91
+
92
+ // src/hooks/use-translator.ts
93
+ function useTranslator(namespace) {
94
+ const mesob = useMesob();
95
+ const { config } = useConfig();
96
+ if (mesob?.t) {
97
+ return (key, params) => {
98
+ const fullKey = namespace ? `${namespace}.${key}` : key;
99
+ return mesob.t?.(fullKey, params) ?? fullKey;
100
+ };
101
+ }
102
+ return createTranslator(config.messages || {}, namespace);
103
+ }
104
+
105
+ // src/constants/auth.error.codes.ts
106
+ var AUTH_ERROR_MAPPING = {
107
+ USER_NOT_FOUND: {
108
+ title: "Account Not Found",
109
+ description: "We could not find an account with that identifier. Please check your spelling or sign up."
110
+ },
111
+ INVALID_PASSWORD: {
112
+ title: "Invalid Password",
113
+ description: "The password you entered is incorrect. Please try again."
114
+ },
115
+ USER_EXISTS: {
116
+ title: "Account Already Exists",
117
+ description: "An account with this identifier already exists. Please sign in instead."
118
+ },
119
+ VERIFICATION_EXPIRED: {
120
+ title: "Verification Expired",
121
+ description: "The verification code or link has expired. Please request a new one."
122
+ },
123
+ VERIFICATION_MISMATCH: {
124
+ title: "Invalid Code",
125
+ description: "The verification code you entered is invalid. Please double-check and try again."
126
+ },
127
+ VERIFICATION_NOT_FOUND: {
128
+ title: "Verification Not Found",
129
+ description: "We could not find a pending verification request. Please restart the process."
130
+ },
131
+ TOO_MANY_ATTEMPTS: {
132
+ title: "Too Many Attempts",
133
+ description: "You have made too many requests recently. Please wait a moment before trying again."
134
+ },
135
+ REQUIRES_VERIFICATION: {
136
+ title: "Verification Required",
137
+ description: "You need to verify your account before you can continue. Please check your email or phone."
138
+ },
139
+ UNAUTHORIZED: {
140
+ title: "Unauthorized",
141
+ description: "You are not authorized to perform this action. Please sign in again."
142
+ },
143
+ ACCESS_DENIED: {
144
+ title: "Access Denied",
145
+ description: "You do not have permission to access this resource. Please contact support if you believe this is an error."
146
+ },
147
+ HAS_NO_PASSWORD: {
148
+ title: "No Password Set",
149
+ description: "Your account does not have a password yet. Continue to set a password before signing in."
150
+ },
151
+ PASSWORD_ALREADY_SET: {
152
+ title: "Password Already Set",
153
+ description: "This account already has a password. Use the normal sign-in form instead."
154
+ }
155
+ };
156
+ var validCodes = Object.keys(AUTH_ERROR_MAPPING);
157
+
158
+ // src/utils/handle-error.ts
159
+ function isAuthError(err) {
160
+ return typeof err === "object" && err !== null && "message" in err && typeof err.message === "string";
161
+ }
162
+ function extractErrorCode(err) {
163
+ if (err.code && validCodes.includes(err.code)) {
164
+ return err.code;
165
+ }
166
+ if (err.message) {
167
+ const messageUpper = err.message.toUpperCase().trim();
168
+ if (validCodes.includes(messageUpper)) {
169
+ return messageUpper;
170
+ }
171
+ }
172
+ return "";
173
+ }
174
+ function sanitizeErrorMessage(message) {
175
+ const lowerMessage = message.toLowerCase();
176
+ const isDatabaseError = lowerMessage.includes("failed query") || lowerMessage.includes("select") || lowerMessage.includes("insert") || lowerMessage.includes("update") || lowerMessage.includes("delete") || lowerMessage.includes("from") || lowerMessage.includes("where") || lowerMessage.includes("limit") || lowerMessage.includes("params:") || lowerMessage.includes("query") || message.includes('"iam".') || message.includes('"tenants"') || message.includes('"users"') || message.includes('"sessions"') || message.includes('"accounts"') || lowerMessage.includes("relation") || lowerMessage.includes("column") || lowerMessage.includes("syntax error") || lowerMessage.includes("database") || lowerMessage.includes("postgres") || lowerMessage.includes("sql");
177
+ if (isDatabaseError) {
178
+ return "An error occurred while processing your request";
179
+ }
180
+ return message;
181
+ }
182
+ function handleAuthError(err, setError, t) {
183
+ const errorCode = extractErrorCode(err);
184
+ if (errorCode && AUTH_ERROR_MAPPING[errorCode]) {
185
+ const mapping = AUTH_ERROR_MAPPING[errorCode];
186
+ setError({
187
+ title: mapping.title,
188
+ description: mapping.description
189
+ });
190
+ return;
191
+ }
192
+ const sanitizedMessage = sanitizeErrorMessage(
193
+ err.message || t("errors.fallback")
194
+ );
195
+ setError({
196
+ title: t("errors.fallback"),
197
+ description: sanitizedMessage
198
+ });
199
+ }
200
+ function handleGenericError(err, setError, t) {
201
+ const rawMessage = err instanceof Error ? err.message : t("errors.fallback");
202
+ const sanitizedMessage = sanitizeErrorMessage(rawMessage);
203
+ setError({
204
+ title: "Error",
205
+ description: sanitizedMessage
206
+ });
207
+ }
208
+ var handleError = (err, setError, t) => {
209
+ if (isAuthError(err)) {
210
+ handleAuthError(err, setError, t);
211
+ } else {
212
+ handleGenericError(err, setError, t);
213
+ }
214
+ };
215
+
216
+ // src/utils/normalize-phone.ts
217
+ function normalizePhone(phone) {
218
+ const cleaned = phone.trim().replace(/\s/g, "");
219
+ if (cleaned.startsWith("+2519") || cleaned.startsWith("+2517")) {
220
+ return cleaned;
221
+ }
222
+ if (cleaned.startsWith("2519") || cleaned.startsWith("2517")) {
223
+ return `+${cleaned}`;
224
+ }
225
+ if (cleaned.startsWith("09") || cleaned.startsWith("07")) {
226
+ return `+251${cleaned.slice(1)}`;
227
+ }
228
+ if ((cleaned.startsWith("9") || cleaned.startsWith("7")) && cleaned.length === 9) {
229
+ return `+251${cleaned}`;
230
+ }
231
+ return cleaned;
232
+ }
233
+
234
+ // src/components/auth/auth-layout.tsx
235
+ import { jsx as jsx2, jsxs } from "react/jsx-runtime";
236
+ var AuthLayout = ({
237
+ title,
238
+ description,
239
+ children,
240
+ footer,
241
+ logoImage
242
+ }) => {
243
+ return /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
244
+ /* @__PURE__ */ jsx2("div", { className: "flex size-8 mb-6 w-full items-center justify-center rounded-md", children: /* @__PURE__ */ jsx2(
245
+ "img",
246
+ {
247
+ src: logoImage || "",
248
+ alt: title,
249
+ width: 42,
250
+ height: 42
251
+ }
252
+ ) }),
253
+ /* @__PURE__ */ jsxs("div", { className: "text-center", children: [
254
+ /* @__PURE__ */ jsx2("h1", { className: "text-2xl font-bold tracking-tight", children: title }),
255
+ description && /* @__PURE__ */ jsx2("p", { className: "mt-2 text-sm text-muted-foreground", children: description })
256
+ ] }),
257
+ children,
258
+ footer && /* @__PURE__ */ jsx2("div", { className: "mt-2 w-full", children: /* @__PURE__ */ jsx2("div", { className: "w-full text-center text-sm text-muted-foreground", children: footer }) })
259
+ ] });
260
+ };
261
+
262
+ // src/components/auth/set-password.tsx
263
+ import { jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
264
+ function PasswordInput({ field, show, onToggle }) {
265
+ const { formItemId, error } = useFormField();
266
+ return /* @__PURE__ */ jsxs2("div", { className: "relative", children: [
267
+ /* @__PURE__ */ jsx3(
268
+ Input,
269
+ {
270
+ ...field,
271
+ id: formItemId,
272
+ type: show ? "text" : "password",
273
+ "aria-invalid": !!error,
274
+ className: "pr-10"
275
+ }
276
+ ),
277
+ /* @__PURE__ */ jsx3(
278
+ Button,
279
+ {
280
+ type: "button",
281
+ variant: "ghost",
282
+ size: "icon",
283
+ className: "absolute right-0 top-0 h-full px-3 text-muted-foreground hover:text-foreground",
284
+ onClick: onToggle,
285
+ "aria-label": show ? "Hide password" : "Show password",
286
+ children: show ? /* @__PURE__ */ jsx3(IconEyeOff, { className: "h-4 w-4" }) : /* @__PURE__ */ jsx3(IconEye, { className: "h-4 w-4" })
287
+ }
288
+ )
289
+ ] });
290
+ }
291
+ var setPasswordSchema = (t) => z.object({
292
+ password: z.string().min(8, t("errors.passwordLength")).max(128, t("errors.longPasswordError")),
293
+ confirmPassword: z.string()
294
+ }).refine((data) => data.password === data.confirmPassword, {
295
+ message: t("errors.passwordsMismatch"),
296
+ path: ["confirmPassword"]
297
+ });
298
+ var getPhoneRegex = (phoneRegex) => {
299
+ if (typeof phoneRegex === "string") {
300
+ return new RegExp(phoneRegex);
301
+ }
302
+ return phoneRegex || /^(\+2519|\+2517|2519|2517|09|07)\d{8}$/;
303
+ };
304
+ var normalizeIdentifier = (identifier, phoneRegex) => {
305
+ const resolvedPhoneRegex = getPhoneRegex(phoneRegex);
306
+ return resolvedPhoneRegex.test(identifier) ? normalizePhone(identifier) : identifier;
307
+ };
308
+ var SetPassword = function SetPassword2({
309
+ identifier,
310
+ redirectUrl
311
+ } = {}) {
312
+ const { hooks, setAuth } = useApi();
313
+ const { config } = useConfig();
314
+ const mesob = useMesob2();
315
+ const t = useTranslator("Auth.setPassword");
316
+ const Link = mesob?.navigation?.Link;
317
+ const [error, setError] = useState2(null);
318
+ const [isLoading, setIsLoading] = useState2(false);
319
+ const [isChecking, setIsChecking] = useState2(true);
320
+ const [showPassword, setShowPassword] = useState2(false);
321
+ const [showConfirmPassword, setShowConfirmPassword] = useState2(false);
322
+ const [account, setAccount] = useState2(null);
323
+ const checkAccountMutation = hooks.useMutation("post", "/check-account");
324
+ const setPasswordMutation = hooks.useMutation("post", "/password/set");
325
+ const onNavigate = config.navigation?.onNavigate || ((path) => {
326
+ if (typeof window !== "undefined") {
327
+ window.location.href = path;
328
+ }
329
+ });
330
+ const signInLink = config.navigation?.links?.signIn || "/auth/sign-in";
331
+ const logoImage = config.ui.logoImage;
332
+ const defaultRedirect = redirectUrl || config.navigation?.defaultRedirectUrl || "/";
333
+ const form = useForm({
334
+ resolver: zodResolver(setPasswordSchema(t)),
335
+ defaultValues: {
336
+ password: "",
337
+ confirmPassword: ""
338
+ }
339
+ });
340
+ useEffect(() => {
341
+ if (error) {
342
+ toast.error(error.title || "Error", {
343
+ description: error.description
344
+ });
345
+ }
346
+ }, [error]);
347
+ const loadAccount = useEffectEvent(async () => {
348
+ if (!identifier) {
349
+ setError({
350
+ title: t("errors.fallback"),
351
+ description: t("errors.missingIdentifier")
352
+ });
353
+ setIsChecking(false);
354
+ return;
355
+ }
356
+ try {
357
+ const normalizedIdentifier = normalizeIdentifier(
358
+ identifier,
359
+ config.phoneRegex
360
+ );
361
+ const res = await checkAccountMutation.mutateAsync({
362
+ body: { username: normalizedIdentifier }
363
+ });
364
+ if (!(res.exists && res.account && res.requiresPasswordSetup)) {
365
+ setError({
366
+ title: t("errors.fallback"),
367
+ description: t("errors.accountNotEligible")
368
+ });
369
+ setIsChecking(false);
370
+ return;
371
+ }
372
+ setAccount({
373
+ fullName: res.account.fullName,
374
+ email: res.account.email,
375
+ phone: res.account.phone
376
+ });
377
+ } catch {
378
+ setError({
379
+ title: t("errors.fallback"),
380
+ description: t("errors.accountLookupFailed")
381
+ });
382
+ } finally {
383
+ setIsChecking(false);
384
+ }
385
+ });
386
+ useEffect(() => {
387
+ loadAccount().catch(() => {
388
+ setIsChecking(false);
389
+ });
390
+ }, [identifier]);
391
+ const handleSubmit = form.handleSubmit(async (values) => {
392
+ if (!identifier) {
393
+ return;
394
+ }
395
+ setIsLoading(true);
396
+ setError(null);
397
+ try {
398
+ const normalizedIdentifier = normalizeIdentifier(
399
+ identifier,
400
+ config.phoneRegex
401
+ );
402
+ const res = await setPasswordMutation.mutateAsync({
403
+ body: {
404
+ identifier: normalizedIdentifier,
405
+ password: values.password
406
+ }
407
+ });
408
+ if ("user" in res && "session" in res) {
409
+ setAuth(res);
410
+ }
411
+ onNavigate(defaultRedirect);
412
+ } catch (err) {
413
+ handleError(err, setError, t);
414
+ } finally {
415
+ setIsLoading(false);
416
+ }
417
+ });
418
+ const accountLabel = account?.email ? t("form.emailLabel") : t("form.phoneLabel");
419
+ const accountValue = account?.email || account?.phone || identifier || "";
420
+ let errorContent = null;
421
+ if (error) {
422
+ errorContent = typeof error === "string" ? { title: "Error", description: error } : error;
423
+ }
424
+ const showError = !isChecking && !!errorContent;
425
+ const errorAlert = showError && errorContent && /* @__PURE__ */ jsxs2(Alert, { variant: "destructive", className: "mt-4", children: [
426
+ /* @__PURE__ */ jsx3(IconAlertCircle, { className: "h-4 w-4" }),
427
+ /* @__PURE__ */ jsx3(AlertTitle, { children: errorContent.title }),
428
+ /* @__PURE__ */ jsx3(AlertDescription, { children: errorContent.description })
429
+ ] });
430
+ return /* @__PURE__ */ jsxs2(
431
+ AuthLayout,
432
+ {
433
+ title: config.ui.name,
434
+ description: t("description"),
435
+ logoImage,
436
+ footer: /* @__PURE__ */ jsxs2("p", { children: [
437
+ t("footer.hasPassword"),
438
+ " ",
439
+ Link ? /* @__PURE__ */ jsx3(Link, { href: signInLink, className: "text-primary hover:underline", children: t("footer.signInCta") }) : /* @__PURE__ */ jsx3(
440
+ "a",
441
+ {
442
+ href: signInLink,
443
+ onClick: (e) => {
444
+ e.preventDefault();
445
+ onNavigate(signInLink);
446
+ },
447
+ className: "text-primary hover:underline",
448
+ children: t("footer.signInCta")
449
+ }
450
+ )
451
+ ] }),
452
+ children: [
453
+ /* @__PURE__ */ jsx3(Form, { ...form, children: /* @__PURE__ */ jsxs2(
454
+ "form",
455
+ {
456
+ id: "set-password-form",
457
+ onSubmit: handleSubmit,
458
+ className: "space-y-4",
459
+ children: [
460
+ /* @__PURE__ */ jsxs2(FormItem, { children: [
461
+ /* @__PURE__ */ jsx3(FormLabel, { children: t("form.fullNameLabel") }),
462
+ /* @__PURE__ */ jsx3(FormControl, { children: /* @__PURE__ */ jsx3(Input, { value: account?.fullName || "", disabled: true }) })
463
+ ] }),
464
+ /* @__PURE__ */ jsxs2(FormItem, { children: [
465
+ /* @__PURE__ */ jsx3(FormLabel, { children: accountLabel }),
466
+ /* @__PURE__ */ jsx3(FormControl, { children: /* @__PURE__ */ jsx3(Input, { value: accountValue, disabled: true }) })
467
+ ] }),
468
+ /* @__PURE__ */ jsx3(
469
+ FormField,
470
+ {
471
+ control: form.control,
472
+ name: "password",
473
+ render: ({ field }) => /* @__PURE__ */ jsxs2(FormItem, { children: [
474
+ /* @__PURE__ */ jsx3(FormLabel, { children: t("form.passwordLabel") }),
475
+ /* @__PURE__ */ jsx3(
476
+ PasswordInput,
477
+ {
478
+ field,
479
+ show: showPassword,
480
+ onToggle: () => setShowPassword(!showPassword)
481
+ }
482
+ ),
483
+ /* @__PURE__ */ jsx3(FormMessage, {})
484
+ ] })
485
+ }
486
+ ),
487
+ /* @__PURE__ */ jsx3(
488
+ FormField,
489
+ {
490
+ control: form.control,
491
+ name: "confirmPassword",
492
+ render: ({ field }) => /* @__PURE__ */ jsxs2(FormItem, { children: [
493
+ /* @__PURE__ */ jsx3(FormLabel, { children: t("form.confirmPasswordLabel") }),
494
+ /* @__PURE__ */ jsx3(
495
+ PasswordInput,
496
+ {
497
+ field,
498
+ show: showConfirmPassword,
499
+ onToggle: () => setShowConfirmPassword(!showConfirmPassword)
500
+ }
501
+ ),
502
+ /* @__PURE__ */ jsx3(FormMessage, {})
503
+ ] })
504
+ }
505
+ ),
506
+ /* @__PURE__ */ jsx3(
507
+ Button,
508
+ {
509
+ type: "submit",
510
+ form: "set-password-form",
511
+ className: "w-full",
512
+ disabled: isChecking || !account || isLoading || setPasswordMutation.isPending,
513
+ children: isLoading || setPasswordMutation.isPending ? t("form.submitting") : t("form.submit")
514
+ }
515
+ )
516
+ ]
517
+ }
518
+ ) }),
519
+ errorAlert
520
+ ]
521
+ }
522
+ );
523
+ };
524
+ export {
525
+ SetPassword
526
+ };
527
+ //# sourceMappingURL=set-password.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/components/auth/set-password.tsx","../../../src/hooks/use-translator.ts","../../../src/lib/translations.ts","../../../src/provider.tsx","../../../src/utils/cookie.ts","../../../src/constants/auth.error.codes.ts","../../../src/utils/handle-error.ts","../../../src/utils/normalize-phone.ts","../../../src/components/auth/auth-layout.tsx"],"sourcesContent":["'use client';\n\nimport { zodResolver } from '@hookform/resolvers/zod';\nimport {\n Alert,\n AlertDescription,\n AlertTitle,\n Button,\n Form,\n FormControl,\n FormField,\n FormItem,\n FormLabel,\n FormMessage,\n Input,\n useFormField,\n} from '@mesob/ui/components';\nimport { useMesob } from '@mesob/ui/providers';\nimport { IconAlertCircle, IconEye, IconEyeOff } from '@tabler/icons-react';\nimport type { ChangeEvent, ComponentProps } from 'react';\nimport { useEffect, useEffectEvent, useState } from 'react';\nimport { useForm } from 'react-hook-form';\nimport { toast } from 'sonner';\nimport { z } from 'zod';\nimport { useTranslator } from '../../hooks/use-translator';\nimport { useApi, useConfig } from '../../provider';\nimport type { AuthErrorContent } from '../../utils/handle-error';\nimport { handleError } from '../../utils/handle-error';\nimport { normalizePhone } from '../../utils/normalize-phone';\nimport { AuthLayout } from './auth-layout';\n\ntype PasswordInputProps = {\n field: ComponentProps<'input'> & {\n value: string;\n onChange: (e: ChangeEvent<HTMLInputElement>) => void;\n onBlur: () => void;\n };\n show: boolean;\n onToggle: () => void;\n};\n\nfunction PasswordInput({ field, show, onToggle }: PasswordInputProps) {\n const { formItemId, error } = useFormField();\n return (\n <div className=\"relative\">\n <Input\n {...field}\n id={formItemId}\n type={show ? 'text' : 'password'}\n aria-invalid={!!error}\n className=\"pr-10\"\n />\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"icon\"\n className=\"absolute right-0 top-0 h-full px-3 text-muted-foreground hover:text-foreground\"\n onClick={onToggle}\n aria-label={show ? 'Hide password' : 'Show password'}\n >\n {show ? (\n <IconEyeOff className=\"h-4 w-4\" />\n ) : (\n <IconEye className=\"h-4 w-4\" />\n )}\n </Button>\n </div>\n );\n}\n\nconst setPasswordSchema = (t: (key: string) => string) =>\n z\n .object({\n password: z\n .string()\n .min(8, t('errors.passwordLength'))\n .max(128, t('errors.longPasswordError')),\n confirmPassword: z.string(),\n })\n .refine((data) => data.password === data.confirmPassword, {\n message: t('errors.passwordsMismatch'),\n path: ['confirmPassword'],\n });\n\ntype SetPasswordFormValues = z.infer<ReturnType<typeof setPasswordSchema>>;\n\ntype SetPasswordProps = {\n identifier?: string;\n redirectUrl?: string;\n};\n\ntype AccountState = {\n fullName: string;\n email: string | null;\n phone: string | null;\n};\n\nconst getPhoneRegex = (phoneRegex: RegExp | string | undefined) => {\n if (typeof phoneRegex === 'string') {\n return new RegExp(phoneRegex);\n }\n\n return phoneRegex || /^(\\+2519|\\+2517|2519|2517|09|07)\\d{8}$/;\n};\n\nconst normalizeIdentifier = (\n identifier: string,\n phoneRegex: RegExp | string | undefined,\n) => {\n const resolvedPhoneRegex = getPhoneRegex(phoneRegex);\n return resolvedPhoneRegex.test(identifier)\n ? normalizePhone(identifier)\n : identifier;\n};\n\nexport const SetPassword = function SetPassword({\n identifier,\n redirectUrl,\n}: SetPasswordProps = {}) {\n const { hooks, setAuth } = useApi();\n const { config } = useConfig();\n const mesob = useMesob();\n const t = useTranslator('Auth.setPassword');\n const Link = mesob?.navigation?.Link;\n const [error, setError] = useState<AuthErrorContent | null>(null);\n const [isLoading, setIsLoading] = useState(false);\n const [isChecking, setIsChecking] = useState(true);\n const [showPassword, setShowPassword] = useState(false);\n const [showConfirmPassword, setShowConfirmPassword] = useState(false);\n const [account, setAccount] = useState<AccountState | null>(null);\n\n const checkAccountMutation = hooks.useMutation('post', '/check-account');\n const setPasswordMutation = hooks.useMutation('post', '/password/set');\n\n const onNavigate =\n config.navigation?.onNavigate ||\n ((path: string) => {\n if (typeof window !== 'undefined') {\n window.location.href = path;\n }\n });\n const signInLink = config.navigation?.links?.signIn || '/auth/sign-in';\n const logoImage = config.ui.logoImage;\n const defaultRedirect =\n redirectUrl || config.navigation?.defaultRedirectUrl || '/';\n const form = useForm<SetPasswordFormValues>({\n resolver: zodResolver(setPasswordSchema(t)),\n defaultValues: {\n password: '',\n confirmPassword: '',\n },\n });\n\n useEffect(() => {\n if (error) {\n toast.error(error.title || 'Error', {\n description: error.description,\n });\n }\n }, [error]);\n\n const loadAccount = useEffectEvent(async () => {\n if (!identifier) {\n setError({\n title: t('errors.fallback'),\n description: t('errors.missingIdentifier'),\n });\n setIsChecking(false);\n return;\n }\n\n try {\n const normalizedIdentifier = normalizeIdentifier(\n identifier,\n config.phoneRegex,\n );\n const res = await checkAccountMutation.mutateAsync({\n body: { username: normalizedIdentifier },\n });\n\n if (!(res.exists && res.account && res.requiresPasswordSetup)) {\n setError({\n title: t('errors.fallback'),\n description: t('errors.accountNotEligible'),\n });\n setIsChecking(false);\n return;\n }\n\n setAccount({\n fullName: res.account.fullName,\n email: res.account.email,\n phone: res.account.phone,\n });\n } catch {\n setError({\n title: t('errors.fallback'),\n description: t('errors.accountLookupFailed'),\n });\n } finally {\n setIsChecking(false);\n }\n });\n\n // biome-ignore lint/correctness/useExhaustiveDependencies: rerun only when route identifier changes\n useEffect(() => {\n loadAccount().catch(() => {\n setIsChecking(false);\n });\n }, [identifier]);\n\n const handleSubmit = form.handleSubmit(async (values) => {\n if (!identifier) {\n return;\n }\n\n setIsLoading(true);\n setError(null);\n\n try {\n const normalizedIdentifier = normalizeIdentifier(\n identifier,\n config.phoneRegex,\n );\n const res = await setPasswordMutation.mutateAsync({\n body: {\n identifier: normalizedIdentifier,\n password: values.password,\n },\n });\n\n if ('user' in res && 'session' in res) {\n setAuth(res);\n }\n onNavigate(defaultRedirect);\n } catch (err) {\n handleError(err, setError, t);\n } finally {\n setIsLoading(false);\n }\n });\n\n const accountLabel = account?.email\n ? t('form.emailLabel')\n : t('form.phoneLabel');\n const accountValue = account?.email || account?.phone || identifier || '';\n\n let errorContent: AuthErrorContent | null = null;\n if (error) {\n errorContent =\n typeof error === 'string'\n ? { title: 'Error', description: error }\n : error;\n }\n const showError = !isChecking && !!errorContent;\n const errorAlert = showError && errorContent && (\n <Alert variant=\"destructive\" className=\"mt-4\">\n <IconAlertCircle className=\"h-4 w-4\" />\n <AlertTitle>{errorContent.title}</AlertTitle>\n <AlertDescription>{errorContent.description}</AlertDescription>\n </Alert>\n );\n\n return (\n <AuthLayout\n title={config.ui.name}\n description={t('description')}\n logoImage={logoImage}\n footer={\n <p>\n {t('footer.hasPassword')}{' '}\n {Link ? (\n <Link href={signInLink} className=\"text-primary hover:underline\">\n {t('footer.signInCta')}\n </Link>\n ) : (\n <a\n href={signInLink}\n onClick={(e) => {\n e.preventDefault();\n onNavigate(signInLink);\n }}\n className=\"text-primary hover:underline\"\n >\n {t('footer.signInCta')}\n </a>\n )}\n </p>\n }\n >\n <Form {...form}>\n <form\n id=\"set-password-form\"\n onSubmit={handleSubmit}\n className=\"space-y-4\"\n >\n <FormItem>\n <FormLabel>{t('form.fullNameLabel')}</FormLabel>\n <FormControl>\n <Input value={account?.fullName || ''} disabled />\n </FormControl>\n </FormItem>\n <FormItem>\n <FormLabel>{accountLabel}</FormLabel>\n <FormControl>\n <Input value={accountValue} disabled />\n </FormControl>\n </FormItem>\n <FormField\n control={form.control}\n name=\"password\"\n render={({ field }) => (\n <FormItem>\n <FormLabel>{t('form.passwordLabel')}</FormLabel>\n <PasswordInput\n field={field}\n show={showPassword}\n onToggle={() => setShowPassword(!showPassword)}\n />\n <FormMessage />\n </FormItem>\n )}\n />\n <FormField\n control={form.control}\n name=\"confirmPassword\"\n render={({ field }) => (\n <FormItem>\n <FormLabel>{t('form.confirmPasswordLabel')}</FormLabel>\n <PasswordInput\n field={field}\n show={showConfirmPassword}\n onToggle={() => setShowConfirmPassword(!showConfirmPassword)}\n />\n <FormMessage />\n </FormItem>\n )}\n />\n <Button\n type=\"submit\"\n form=\"set-password-form\"\n className=\"w-full\"\n disabled={\n isChecking ||\n !account ||\n isLoading ||\n setPasswordMutation.isPending\n }\n >\n {isLoading || setPasswordMutation.isPending\n ? t('form.submitting')\n : t('form.submit')}\n </Button>\n </form>\n </Form>\n {errorAlert}\n </AuthLayout>\n );\n};\n","import { useMesob } from '@mesob/ui/providers';\nimport { createTranslator } from '../lib/translations';\nimport { useConfig } from '../provider';\n\nexport function useTranslator(namespace?: string) {\n const mesob = useMesob();\n const { config } = useConfig();\n\n if (mesob?.t) {\n return (key: string, params?: Record<string, string | number>): string => {\n const fullKey = namespace ? `${namespace}.${key}` : key;\n return mesob.t?.(fullKey, params) ?? fullKey;\n };\n }\n\n return createTranslator(config.messages || {}, namespace);\n}\n","type Messages = Record<string, unknown>;\n\nexport function createTranslator(messages: Messages, namespace?: string) {\n return (key: string, params?: Record<string, string | number>): string => {\n const fullKey = namespace ? `${namespace}.${key}` : key;\n const keys = fullKey.split('.');\n\n let value: unknown = messages;\n for (const k of keys) {\n if (value && typeof value === 'object' && value !== null) {\n value = (value as Record<string, unknown>)[k];\n } else {\n return fullKey;\n }\n }\n\n if (typeof value !== 'string') {\n return fullKey;\n }\n\n // Simple parameter replacement\n if (params) {\n return value.replace(/\\{(\\w+)\\}/g, (_, param) =>\n String(params[param] ?? `{${param}}`),\n );\n }\n\n return value;\n };\n}\n","'use client';\n\nimport { QueryClient, QueryClientProvider } from '@tanstack/react-query';\nimport { deepmerge } from 'deepmerge-ts';\nimport createFetchClient from 'openapi-fetch';\nimport createClient from 'openapi-react-query';\nimport type { ReactNode } from 'react';\nimport { createContext, useContext, useMemo, useState } from 'react';\nimport type { paths } from './data/openapi';\nimport { createTranslator } from './lib/translations';\nimport {\n type AuthClientConfig,\n type AuthResponse,\n defaultAuthClientConfig,\n type Session,\n type User,\n} from './types';\nimport { getSessionCookieName } from './utils/cookie';\nimport { createCustomFetch } from './utils/custom-fetch';\n\n// biome-ignore lint/suspicious/noExplicitAny: OpenAPI hooks type\ntype OpenApiHooks = any;\n\n// --- Utility: Check if running on server ---\nfunction isServer(): boolean {\n return typeof document === 'undefined';\n}\n\n/**\n * @deprecated Cookie is httpOnly and cannot be read client-side.\n * Use `useSession().isAuthenticated` instead.\n * This function always returns false on client.\n */\nexport function hasAuthCookie(_cookieName: string): boolean {\n // Cookie is httpOnly, can't check client-side\n // Always return false - use useSession() for auth status\n return false;\n}\n\n// --- Types ---\nexport type AuthStatus = 'loading' | 'authenticated' | 'unauthenticated';\n\ntype AuthState = {\n user: User | null;\n session: Session | null;\n status: AuthStatus;\n error: Error | null;\n};\n\ntype SessionContextValue = AuthState & {\n isLoading: boolean;\n isAuthenticated: boolean;\n refresh: () => Promise<void>;\n signOut: () => Promise<void>;\n};\n\ntype ApiContextValue = {\n hooks: OpenApiHooks;\n setAuth: (auth: AuthResponse) => void;\n clearAuth: () => void;\n refresh: () => Promise<void>;\n};\n\ntype ConfigContextValue = {\n config: AuthClientConfig;\n cookieName: string;\n t: (key: string, params?: Record<string, string | number>) => string;\n};\n\nconst SessionContext = createContext<SessionContextValue | null>(null);\nconst ApiContext = createContext<ApiContextValue | null>(null);\nconst ConfigContext = createContext<ConfigContextValue | null>(null);\n\nconst queryClient = new QueryClient({\n defaultOptions: {\n queries: {\n refetchOnWindowFocus: false,\n },\n },\n});\n\n// --- Hooks ---\n\n/**\n * Get session state including user, session, and auth status.\n * - `status`: 'loading' | 'authenticated' | 'unauthenticated'\n * - `isLoading`: true while fetching session\n * - `isAuthenticated`: true if user and session exist\n */\nexport function useSession(): SessionContextValue {\n const context = useContext(SessionContext);\n if (!context) {\n throw new Error('useSession must be used within MesobAuthProvider');\n }\n return context;\n}\n\nexport function useApi(): ApiContextValue {\n const context = useContext(ApiContext);\n if (!context) {\n throw new Error('useApi must be used within MesobAuthProvider');\n }\n return context;\n}\n\nexport function useConfig(): ConfigContextValue {\n const context = useContext(ConfigContext);\n if (!context) {\n throw new Error('useConfig must be used within MesobAuthProvider');\n }\n return context;\n}\n\n/**\n * @deprecated Cookie is httpOnly, can't be checked client-side.\n * Use `useSession().isAuthenticated` instead.\n */\nexport function useHasAuthCookie(): boolean {\n const { status } = useSession();\n return status === 'authenticated' || status === 'loading';\n}\n\n// --- Provider ---\n\ntype MesobAuthProviderProps = {\n config: AuthClientConfig;\n children: ReactNode;\n};\n\nexport function MesobAuthProvider({\n config,\n children,\n}: MesobAuthProviderProps) {\n const mergedConfig = useMemo(\n () =>\n deepmerge(\n { ...defaultAuthClientConfig } as Partial<AuthClientConfig>,\n config,\n ) as AuthClientConfig,\n [config],\n );\n\n const api = useMemo(\n () =>\n createFetchClient<paths>({\n baseUrl: mergedConfig.baseURL,\n fetch: createCustomFetch(mergedConfig),\n }),\n [mergedConfig],\n );\n\n const hooks = useMemo(() => createClient(api), [api]);\n const cookieName = useMemo(\n () => getSessionCookieName(mergedConfig),\n [mergedConfig],\n );\n\n return (\n <QueryClientProvider client={queryClient}>\n <AuthStateProvider\n config={mergedConfig}\n hooks={hooks}\n cookieName={cookieName}\n >\n {children}\n </AuthStateProvider>\n </QueryClientProvider>\n );\n}\n\ntype AuthStateProviderProps = {\n config: AuthClientConfig;\n hooks: OpenApiHooks;\n cookieName: string;\n children: ReactNode;\n};\n\nfunction AuthStateProvider({\n config,\n hooks,\n cookieName,\n children,\n}: AuthStateProviderProps) {\n // Manual override for sign-out / sign-in\n const [override, setOverride] = useState<AuthState | null>(null);\n\n // Always fetch session - cookie is httpOnly, can't check client-side\n // Server will read the cookie and return user/session if valid\n const {\n data: sessionData,\n isLoading,\n isFetched,\n error: sessionError,\n refetch,\n } = hooks.useQuery(\n 'get',\n '/session',\n {},\n {\n enabled: !(override || isServer()),\n refetchOnMount: false,\n refetchOnWindowFocus: false,\n refetchOnReconnect: false,\n retry: false,\n gcTime: 0,\n staleTime: 0,\n },\n );\n\n // Derive state directly - no useEffect\n const user = override?.user ?? sessionData?.user ?? null;\n const session = override?.session ?? sessionData?.session ?? null;\n const error = override?.error ?? (sessionError as Error | null);\n\n // Check error status code\n const errorStatus = (() => {\n if (!sessionError) {\n return null;\n }\n const err = sessionError as { status?: number };\n return err.status ?? null;\n })();\n\n // Check if error is a network/connection error\n const isNetworkError = (() => {\n if (!sessionError) {\n return false;\n }\n const error = sessionError as Error & { cause?: unknown; data?: unknown };\n const errorMessage =\n error.message || String(error) || JSON.stringify(error);\n // Network errors: TypeError, DOMException, or fetch failures\n if (\n error instanceof TypeError ||\n error instanceof DOMException ||\n error.name === 'TypeError' ||\n errorMessage.includes('Failed to fetch') ||\n errorMessage.includes('ERR_CONNECTION_REFUSED') ||\n errorMessage.includes('NetworkError') ||\n errorMessage.includes('Network request failed') ||\n errorMessage.includes('fetch failed')\n ) {\n return true;\n }\n // Check error cause\n if (error.cause) {\n const causeStr = String(error.cause);\n if (\n causeStr.includes('Failed to fetch') ||\n causeStr.includes('ERR_CONNECTION_REFUSED') ||\n causeStr.includes('NetworkError')\n ) {\n return true;\n }\n }\n return false;\n })();\n\n // Compute status\n // biome-ignore lint: Status determination requires multiple checks\n const status: AuthStatus = (() => {\n if (override) {\n return override.status;\n }\n if (isServer()) {\n return 'loading';\n }\n if (user && session) {\n return 'authenticated';\n }\n // Check for network errors or auth errors first - allow auth page to show\n if (isNetworkError || errorStatus === 401) {\n return 'unauthenticated';\n }\n // If we have an error but it's not a network error, still check loading state\n if (sessionError && !isNetworkError && errorStatus !== 401) {\n if (errorStatus && errorStatus >= 500) {\n return 'authenticated';\n }\n // Other errors mean unauthenticated\n if (isFetched) {\n return 'unauthenticated';\n }\n }\n if (isLoading || !isFetched) {\n return 'loading';\n }\n if (isFetched && !user && !session) {\n return 'unauthenticated';\n }\n return 'unauthenticated';\n })();\n\n const signOutMutation = hooks.useMutation('post', '/sign-out');\n const t = createTranslator(config.messages || {});\n\n const setAuth = (auth: AuthResponse) => {\n setOverride({\n user: auth.user,\n session: auth.session,\n status: 'authenticated',\n error: null,\n });\n };\n\n const clearAuth = () => {\n setOverride({\n user: null,\n session: null,\n status: 'unauthenticated',\n error: null,\n });\n };\n\n const refresh = async () => {\n setOverride(null);\n await refetch();\n };\n\n const signOut = async () => {\n try {\n await signOutMutation.mutateAsync({});\n } finally {\n clearAuth();\n }\n };\n\n return (\n <ConfigContext.Provider value={{ config, cookieName, t }}>\n <ApiContext.Provider value={{ hooks, setAuth, clearAuth, refresh }}>\n <SessionContext.Provider\n value={{\n user,\n session,\n status,\n error,\n isLoading: status === 'loading',\n isAuthenticated: status === 'authenticated',\n refresh,\n signOut,\n }}\n >\n {children}\n </SessionContext.Provider>\n </ApiContext.Provider>\n </ConfigContext.Provider>\n );\n}\n","import type { AuthClientConfig } from '../types';\n\nconst isProduction =\n typeof process !== 'undefined' && process.env.NODE_ENV === 'production';\n\nexport const getSessionCookieName = (config: AuthClientConfig): string => {\n const prefix = config.cookiePrefix || '';\n const baseName = 'session_token';\n if (prefix) {\n return `${prefix}_${baseName}`;\n }\n return isProduction ? '__Host-session_token' : baseName;\n};\n","export const AUTH_ERROR_MAPPING: Record<\n string,\n { title: string; description: string }\n> = {\n USER_NOT_FOUND: {\n title: 'Account Not Found',\n description:\n 'We could not find an account with that identifier. Please check your spelling or sign up.',\n },\n INVALID_PASSWORD: {\n title: 'Invalid Password',\n description: 'The password you entered is incorrect. Please try again.',\n },\n USER_EXISTS: {\n title: 'Account Already Exists',\n description:\n 'An account with this identifier already exists. Please sign in instead.',\n },\n VERIFICATION_EXPIRED: {\n title: 'Verification Expired',\n description:\n 'The verification code or link has expired. Please request a new one.',\n },\n VERIFICATION_MISMATCH: {\n title: 'Invalid Code',\n description:\n 'The verification code you entered is invalid. Please double-check and try again.',\n },\n VERIFICATION_NOT_FOUND: {\n title: 'Verification Not Found',\n description:\n 'We could not find a pending verification request. Please restart the process.',\n },\n TOO_MANY_ATTEMPTS: {\n title: 'Too Many Attempts',\n description:\n 'You have made too many requests recently. Please wait a moment before trying again.',\n },\n REQUIRES_VERIFICATION: {\n title: 'Verification Required',\n description:\n 'You need to verify your account before you can continue. Please check your email or phone.',\n },\n UNAUTHORIZED: {\n title: 'Unauthorized',\n description:\n 'You are not authorized to perform this action. Please sign in again.',\n },\n ACCESS_DENIED: {\n title: 'Access Denied',\n description:\n 'You do not have permission to access this resource. Please contact support if you believe this is an error.',\n },\n HAS_NO_PASSWORD: {\n title: 'No Password Set',\n description:\n 'Your account does not have a password yet. Continue to set a password before signing in.',\n },\n PASSWORD_ALREADY_SET: {\n title: 'Password Already Set',\n description:\n 'This account already has a password. Use the normal sign-in form instead.',\n },\n};\n\nexport const validCodes = Object.keys(AUTH_ERROR_MAPPING);\n","import { AUTH_ERROR_MAPPING, validCodes } from '../constants/auth.error.codes';\nimport type { AuthError } from '../types';\n\nexport type AuthErrorContent = {\n title: string;\n description: string;\n};\n\ntype TranslatorFunction = (\n key: string,\n params?: Record<string, string | number>,\n) => string;\n\n// Type guard to check if error is an AuthError\nfunction isAuthError(err: unknown): err is AuthError {\n return (\n typeof err === 'object' &&\n err !== null &&\n 'message' in err &&\n typeof (err as { message: unknown }).message === 'string'\n );\n}\n\nfunction extractErrorCode(err: AuthError): string {\n if (err.code && validCodes.includes(err.code)) {\n return err.code;\n }\n if (err.message) {\n const messageUpper = err.message.toUpperCase().trim();\n if (validCodes.includes(messageUpper)) {\n return messageUpper;\n }\n }\n return '';\n}\n\nfunction sanitizeErrorMessage(message: string): string {\n const lowerMessage = message.toLowerCase();\n const isDatabaseError =\n lowerMessage.includes('failed query') ||\n lowerMessage.includes('select') ||\n lowerMessage.includes('insert') ||\n lowerMessage.includes('update') ||\n lowerMessage.includes('delete') ||\n lowerMessage.includes('from') ||\n lowerMessage.includes('where') ||\n lowerMessage.includes('limit') ||\n lowerMessage.includes('params:') ||\n lowerMessage.includes('query') ||\n message.includes('\"iam\".') ||\n message.includes('\"tenants\"') ||\n message.includes('\"users\"') ||\n message.includes('\"sessions\"') ||\n message.includes('\"accounts\"') ||\n lowerMessage.includes('relation') ||\n lowerMessage.includes('column') ||\n lowerMessage.includes('syntax error') ||\n lowerMessage.includes('database') ||\n lowerMessage.includes('postgres') ||\n lowerMessage.includes('sql');\n\n if (isDatabaseError) {\n return 'An error occurred while processing your request';\n }\n\n return message;\n}\n\nfunction handleAuthError(\n err: AuthError,\n setError: (error: AuthErrorContent | null) => void,\n t: TranslatorFunction,\n) {\n const errorCode = extractErrorCode(err);\n\n if (errorCode && AUTH_ERROR_MAPPING[errorCode]) {\n const mapping = AUTH_ERROR_MAPPING[errorCode];\n setError({\n title: mapping.title,\n description: mapping.description,\n });\n return;\n }\n\n const sanitizedMessage = sanitizeErrorMessage(\n err.message || t('errors.fallback'),\n );\n setError({\n title: t('errors.fallback'),\n description: sanitizedMessage,\n });\n}\n\nfunction handleGenericError(\n err: unknown,\n setError: (error: AuthErrorContent | null) => void,\n t: TranslatorFunction,\n) {\n const rawMessage = err instanceof Error ? err.message : t('errors.fallback');\n const sanitizedMessage = sanitizeErrorMessage(rawMessage);\n setError({\n title: 'Error',\n description: sanitizedMessage,\n });\n}\n\nexport const handleError = (\n err: unknown,\n setError: (error: AuthErrorContent | null) => void,\n t: TranslatorFunction,\n) => {\n if (isAuthError(err)) {\n handleAuthError(err, setError, t);\n } else {\n handleGenericError(err, setError, t);\n }\n};\n","export function normalizePhone(phone: string): string {\n const cleaned = phone.trim().replace(/\\s/g, '');\n if (cleaned.startsWith('+2519') || cleaned.startsWith('+2517')) {\n return cleaned;\n }\n if (cleaned.startsWith('2519') || cleaned.startsWith('2517')) {\n return `+${cleaned}`;\n }\n if (cleaned.startsWith('09') || cleaned.startsWith('07')) {\n return `+251${cleaned.slice(1)}`;\n }\n if (\n (cleaned.startsWith('9') || cleaned.startsWith('7')) &&\n cleaned.length === 9\n ) {\n return `+251${cleaned}`;\n }\n return cleaned;\n}\n","'use client';\n\nimport type { ReactNode } from 'react';\n\ntype AuthLayoutProps = {\n title: ReactNode;\n description?: string;\n children: ReactNode;\n footer?: ReactNode;\n logoImage?: string;\n};\n\nexport const AuthLayout = ({\n title,\n description,\n children,\n footer,\n logoImage,\n}: AuthLayoutProps) => {\n return (\n <div className=\"space-y-4\">\n <div className=\"flex size-8 mb-6 w-full items-center justify-center rounded-md\">\n {/** biome-ignore lint/performance/noImgElement: logo image */}\n <img\n src={logoImage || ''}\n alt={title as string}\n width={42}\n height={42}\n />\n </div>\n <div className=\"text-center\">\n <h1 className=\"text-2xl font-bold tracking-tight\">{title}</h1>\n {description && (\n <p className=\"mt-2 text-sm text-muted-foreground\">{description}</p>\n )}\n </div>\n\n {children}\n\n {footer && (\n <div className=\"mt-2 w-full\">\n <div className=\"w-full text-center text-sm text-muted-foreground\">\n {footer}\n </div>\n </div>\n )}\n </div>\n );\n};\n"],"mappings":"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","names":["useMesob","useState","jsx","jsx","jsxs","SetPassword","useMesob","useState"]}
@@ -146,7 +146,11 @@ var AUTH_ERROR_MAPPING = {
146
146
  },
147
147
  HAS_NO_PASSWORD: {
148
148
  title: "No Password Set",
149
- description: "Your account does not have a password set (e.g. social login). Please sign in with your provider or reset your password."
149
+ description: "Your account does not have a password yet. Continue to set a password before signing in."
150
+ },
151
+ PASSWORD_ALREADY_SET: {
152
+ title: "Password Already Set",
153
+ description: "This account already has a password. Use the normal sign-in form instead."
150
154
  }
151
155
  };
152
156
  var validCodes = Object.keys(AUTH_ERROR_MAPPING);
@@ -318,6 +322,7 @@ var SignIn = ({ redirectUrl } = {}) => {
318
322
  const defaultRedirect = redirectUrl || config.navigation?.defaultRedirectUrl || "/dashboard";
319
323
  const forgotPasswordLink = config.navigation?.links?.forgotPassword || "/auth/forgot-password";
320
324
  const signUpLink = config.navigation?.links?.signUp || "/auth/sign-up";
325
+ const setPasswordLink = config.navigation?.links?.setPassword || "/auth/set-password";
321
326
  const onNavigate = config.navigation?.onNavigate || ((path) => {
322
327
  if (typeof window !== "undefined") {
323
328
  window.location.href = path;
@@ -346,6 +351,13 @@ var SignIn = ({ redirectUrl } = {}) => {
346
351
  }
347
352
  });
348
353
  if (result.exists) {
354
+ if (result.requiresPasswordSetup) {
355
+ const redirectParam = defaultRedirect ? `&redirect=${encodeURIComponent(defaultRedirect)}` : "";
356
+ onNavigate(
357
+ `${setPasswordLink}?identifier=${encodeURIComponent(normalizedUsername)}${redirectParam}`
358
+ );
359
+ return;
360
+ }
349
361
  setUsername(normalizedUsername);
350
362
  form.setValue("username", normalizedUsername);
351
363
  setShowPasswordField(true);
@@ -401,6 +413,15 @@ var SignIn = ({ redirectUrl } = {}) => {
401
413
  }
402
414
  onNavigate(defaultRedirect);
403
415
  } catch (err) {
416
+ const authError = err;
417
+ const errorCode = authError.code || authError.message;
418
+ if (errorCode === "HAS_NO_PASSWORD") {
419
+ const redirectParam = defaultRedirect ? `&redirect=${encodeURIComponent(defaultRedirect)}` : "";
420
+ onNavigate(
421
+ `${setPasswordLink}?identifier=${encodeURIComponent(username)}${redirectParam}`
422
+ );
423
+ return;
424
+ }
404
425
  handleError(err, setError, t);
405
426
  } finally {
406
427
  setIsLoading(false);