@kendevelops/auth-flow-kit 1.3.2 → 1.3.3

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.
package/dist/index.js CHANGED
@@ -9,7 +9,9 @@ import {
9
9
 
10
10
  // src/http.ts
11
11
  function makeURL(baseURL, path) {
12
- return `${baseURL.replace(/\/$/, "")}${path.startsWith("/") ? path : `/${path}`}`;
12
+ const base = baseURL.replace(/\/$/, "");
13
+ const suffix = path.startsWith("/") ? path : `/${path}`;
14
+ return `${base}${suffix}`;
13
15
  }
14
16
  function getStoredAccessToken() {
15
17
  try {
@@ -20,8 +22,11 @@ function getStoredAccessToken() {
20
22
  }
21
23
  function setStoredAccessToken(token) {
22
24
  try {
23
- if (token) localStorage.setItem("afk_access_token", token);
24
- else localStorage.removeItem("afk_access_token");
25
+ if (token) {
26
+ localStorage.setItem("afk_access_token", token);
27
+ return;
28
+ }
29
+ localStorage.removeItem("afk_access_token");
25
30
  } catch {
26
31
  }
27
32
  }
@@ -30,20 +35,27 @@ async function httpJSON(url, opts = {}, withAuth = false) {
30
35
  "Content-Type": "application/json"
31
36
  };
32
37
  if (withAuth) {
33
- const tok = getStoredAccessToken();
34
- if (tok) headers["Authorization"] = `Bearer ${tok}`;
38
+ const storedToken = getStoredAccessToken();
39
+ if (storedToken) {
40
+ headers["Authorization"] = `Bearer ${storedToken}`;
41
+ }
35
42
  }
36
43
  const res = await fetch(url, {
37
44
  ...opts,
38
- headers: { ...headers, ...opts.headers || {} }
45
+ headers: {
46
+ ...headers,
47
+ ...opts.headers || {}
48
+ }
39
49
  });
40
50
  if (!res.ok) {
41
51
  let message = `Request failed (${res.status})`;
42
52
  const contentType = res.headers.get("content-type") || "";
43
53
  if (contentType.includes("application/json")) {
44
54
  try {
45
- const data = await res.json();
46
- if (data?.message) message = data.message;
55
+ const body = await res.json();
56
+ if (body?.message) {
57
+ message = body.message;
58
+ }
47
59
  } catch {
48
60
  }
49
61
  }
@@ -163,18 +175,29 @@ function PasswordResetScreen() {
163
175
  const [email, setEmail] = useState2("");
164
176
  const [sent, setSent] = useState2(false);
165
177
  const [error, setError] = useState2(null);
178
+ const isValidEmail = (value) => /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value);
166
179
  const requestReset = async (e) => {
167
180
  e.preventDefault();
168
181
  setError(null);
182
+ const trimmedEmail = email.trim();
183
+ if (!trimmedEmail) {
184
+ setError("Email is required.");
185
+ return;
186
+ }
187
+ if (!isValidEmail(trimmedEmail)) {
188
+ setError("Enter a valid email address.");
189
+ return;
190
+ }
169
191
  try {
170
192
  const url = makeURL(config.baseURL, config.endpoints.forgot);
171
193
  await httpJSON(url, {
172
194
  method: "POST",
173
- body: JSON.stringify({ email })
195
+ body: JSON.stringify({ email: trimmedEmail })
174
196
  });
175
197
  setSent(true);
176
198
  } catch (err) {
177
- setError(err?.message || "Failed to request reset");
199
+ const message = err instanceof Error ? err.message : "Failed to request reset";
200
+ setError(message);
178
201
  }
179
202
  };
180
203
  return /* @__PURE__ */ jsxs(
@@ -214,6 +237,7 @@ function PasswordResetScreen() {
214
237
  /* @__PURE__ */ jsx3(
215
238
  "label",
216
239
  {
240
+ htmlFor: "afk-reset-email",
217
241
  style: {
218
242
  position: "absolute",
219
243
  top: sent ? "-18px" : "-10px",
@@ -230,6 +254,7 @@ function PasswordResetScreen() {
230
254
  /* @__PURE__ */ jsx3(
231
255
  "input",
232
256
  {
257
+ id: "afk-reset-email",
233
258
  value: email,
234
259
  onChange: (e) => setEmail(e.target.value),
235
260
  type: "email",
@@ -292,6 +317,8 @@ function PasswordResetScreen() {
292
317
  error && /* @__PURE__ */ jsx3(
293
318
  "p",
294
319
  {
320
+ role: "alert",
321
+ "aria-live": "polite",
295
322
  style: {
296
323
  marginTop: 20,
297
324
  color: "crimson",
@@ -342,15 +369,26 @@ function LoginScreen() {
342
369
  setError(null);
343
370
  const trimmedEmail = email.trim();
344
371
  const trimmedPassword = password.trim();
345
- if (!trimmedEmail || !trimmedPassword) {
346
- setError("Email and password cannot be empty.");
372
+ if (!trimmedEmail) {
373
+ setError("Email is required.");
374
+ setSubmitting(false);
375
+ return;
376
+ }
377
+ if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(trimmedEmail)) {
378
+ setError("Enter a valid email address.");
379
+ setSubmitting(false);
380
+ return;
381
+ }
382
+ if (!trimmedPassword) {
383
+ setError("Password is required.");
347
384
  setSubmitting(false);
348
385
  return;
349
386
  }
350
387
  try {
351
388
  await login(trimmedEmail, trimmedPassword);
352
389
  } catch (err) {
353
- setError(err?.message || "Login failed");
390
+ const message = err instanceof Error ? err.message : "Login failed";
391
+ setError(message);
354
392
  } finally {
355
393
  setSubmitting(false);
356
394
  }
@@ -391,6 +429,7 @@ function LoginScreen() {
391
429
  /* @__PURE__ */ jsx4(
392
430
  "label",
393
431
  {
432
+ htmlFor: "afk-login-email",
394
433
  style: {
395
434
  position: "absolute",
396
435
  top: "-10px",
@@ -407,6 +446,7 @@ function LoginScreen() {
407
446
  /* @__PURE__ */ jsx4(
408
447
  "input",
409
448
  {
449
+ id: "afk-login-email",
410
450
  value: email,
411
451
  onChange: (e) => setEmail(e.target.value),
412
452
  type: "email",
@@ -436,6 +476,7 @@ function LoginScreen() {
436
476
  /* @__PURE__ */ jsx4(
437
477
  "label",
438
478
  {
479
+ htmlFor: "afk-login-password",
439
480
  style: {
440
481
  position: "absolute",
441
482
  top: "-10px",
@@ -452,6 +493,7 @@ function LoginScreen() {
452
493
  /* @__PURE__ */ jsx4(
453
494
  "input",
454
495
  {
496
+ id: "afk-login-password",
455
497
  value: password,
456
498
  onChange: (e) => setPassword(e.target.value),
457
499
  type: "password",
@@ -478,17 +520,28 @@ function LoginScreen() {
478
520
  )
479
521
  ] }),
480
522
  /* @__PURE__ */ jsx4(
481
- "p",
523
+ "div",
482
524
  {
483
- onClick: () => setShowReset(true),
484
525
  style: {
485
- textAlign: "right",
486
- fontSize: 13,
487
- color: "#4b4bff",
488
- cursor: "pointer",
489
- marginBottom: 24
526
+ textAlign: "right"
490
527
  },
491
- children: "Forgot password?"
528
+ children: /* @__PURE__ */ jsx4(
529
+ "button",
530
+ {
531
+ onClick: () => setShowReset(true),
532
+ style: {
533
+ textAlign: "right",
534
+ fontSize: 13,
535
+ color: "#4b4bff",
536
+ cursor: "pointer",
537
+ marginBottom: 24,
538
+ width: "fit-content",
539
+ border: "none",
540
+ background: "none"
541
+ },
542
+ children: "Forgot password?"
543
+ }
544
+ )
492
545
  }
493
546
  ),
494
547
  /* @__PURE__ */ jsx4(
@@ -516,6 +569,8 @@ function LoginScreen() {
516
569
  error && /* @__PURE__ */ jsx4(
517
570
  "p",
518
571
  {
572
+ role: "alert",
573
+ "aria-live": "polite",
519
574
  style: {
520
575
  marginTop: 18,
521
576
  color: "crimson",
@@ -539,8 +594,21 @@ function SignupScreen() {
539
594
  const [name, setName] = useState4("");
540
595
  const [email, setEmail] = useState4("");
541
596
  const [password, setPassword] = useState4("");
597
+ const [confirmPassword, setConfirmPassword] = useState4("");
542
598
  const [submitting, setSubmitting] = useState4(false);
543
599
  const [error, setError] = useState4(null);
600
+ const isValidEmail = (value) => /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value);
601
+ const getPasswordStrength = (value) => {
602
+ let score = 0;
603
+ if (value.length >= 8) score++;
604
+ if (/[A-Z]/.test(value)) score++;
605
+ if (/[0-9]/.test(value)) score++;
606
+ if (/[^A-Za-z0-9]/.test(value)) score++;
607
+ if (!value) return { label: "", color: "" };
608
+ if (score <= 1) return { label: "Weak", color: "crimson" };
609
+ if (score === 2) return { label: "Medium", color: "#f39c12" };
610
+ return { label: "Strong", color: "#2ecc71" };
611
+ };
544
612
  const onSubmit = async (e) => {
545
613
  e.preventDefault();
546
614
  setSubmitting(true);
@@ -548,15 +616,47 @@ function SignupScreen() {
548
616
  const trimmedEmail = email.trim();
549
617
  const trimmedPassword = password.trim();
550
618
  const trimmedName = name.trim();
551
- if (!trimmedEmail || !trimmedPassword || !trimmedName) {
552
- setError("Email and password cannot be empty.");
619
+ const trimmedConfirm = confirmPassword.trim();
620
+ if (!trimmedName) {
621
+ setError("Name is required.");
622
+ setSubmitting(false);
623
+ return;
624
+ }
625
+ if (!trimmedEmail) {
626
+ setError("Email is required.");
627
+ setSubmitting(false);
628
+ return;
629
+ }
630
+ if (!isValidEmail(trimmedEmail)) {
631
+ setError("Enter a valid email address.");
632
+ setSubmitting(false);
633
+ return;
634
+ }
635
+ if (!trimmedPassword) {
636
+ setError("Password is required.");
637
+ setSubmitting(false);
638
+ return;
639
+ }
640
+ if (trimmedPassword.length < 8) {
641
+ setError("Password must be at least 8 characters long.");
642
+ setSubmitting(false);
643
+ return;
644
+ }
645
+ if (!trimmedConfirm) {
646
+ setError("Please confirm your password.");
647
+ setSubmitting(false);
648
+ return;
649
+ }
650
+ if (trimmedPassword !== trimmedConfirm) {
651
+ setError("Passwords do not match.");
553
652
  setSubmitting(false);
554
653
  return;
555
654
  }
556
655
  try {
557
- await signup({ name, email, password });
656
+ await signup({ name: trimmedName, email: trimmedEmail, password: trimmedPassword });
558
657
  } catch (err) {
559
- setError(err?.message || "Signup failed");
658
+ const message = err instanceof Error ? err.message : "Signup failed";
659
+ setError(message);
560
660
  } finally {
561
661
  setSubmitting(false);
562
662
  }
@@ -598,6 +698,7 @@ function SignupScreen() {
598
698
  /* @__PURE__ */ jsx5(
599
699
  "label",
600
700
  {
701
+ htmlFor: "afk-signup-name",
601
702
  style: {
602
703
  position: "absolute",
603
704
  top: "-10px",
@@ -614,6 +715,7 @@ function SignupScreen() {
614
715
  /* @__PURE__ */ jsx5(
615
716
  "input",
616
717
  {
718
+ id: "afk-signup-name",
617
719
  value: name,
618
720
  onChange: (e) => setName(e.target.value),
619
721
  placeholder: "Your name",
@@ -642,6 +744,7 @@ function SignupScreen() {
642
744
  /* @__PURE__ */ jsx5(
643
745
  "label",
644
746
  {
747
+ htmlFor: "afk-signup-email",
645
748
  style: {
646
749
  position: "absolute",
647
750
  top: "-10px",
@@ -658,6 +761,7 @@ function SignupScreen() {
658
761
  /* @__PURE__ */ jsx5(
659
762
  "input",
660
763
  {
764
+ id: "afk-signup-email",
661
765
  value: email,
662
766
  onChange: (e) => setEmail(e.target.value),
663
767
  type: "email",
@@ -687,6 +791,7 @@ function SignupScreen() {
687
791
  /* @__PURE__ */ jsx5(
688
792
  "label",
689
793
  {
794
+ htmlFor: "afk-signup-password",
690
795
  style: {
691
796
  position: "absolute",
692
797
  top: "-10px",
@@ -703,6 +808,7 @@ function SignupScreen() {
703
808
  /* @__PURE__ */ jsx5(
704
809
  "input",
705
810
  {
811
+ id: "afk-signup-password",
706
812
  value: password,
707
813
  onChange: (e) => setPassword(e.target.value),
708
814
  type: "password",
@@ -728,6 +834,95 @@ function SignupScreen() {
728
834
  }
729
835
  )
730
836
  ] }),
837
+ /* @__PURE__ */ jsxs3("div", { style: { position: "relative", marginBottom: 18 }, children: [
838
+ /* @__PURE__ */ jsx5(
839
+ "label",
840
+ {
841
+ htmlFor: "afk-signup-confirm-password",
842
+ style: {
843
+ position: "absolute",
844
+ top: "-10px",
845
+ left: "14px",
846
+ background: "rgba(255,255,255,0.85)",
847
+ padding: "0 6px",
848
+ fontSize: 13,
849
+ color: "#444",
850
+ borderRadius: 6
851
+ },
852
+ children: "Confirm password"
853
+ }
854
+ ),
855
+ /* @__PURE__ */ jsx5(
856
+ "input",
857
+ {
858
+ id: "afk-signup-confirm-password",
859
+ value: confirmPassword,
860
+ onChange: (e) => setConfirmPassword(e.target.value),
861
+ type: "password",
862
+ placeholder: "Repeat your password",
863
+ style: {
864
+ width: "80%",
865
+ padding: "14px 16px",
866
+ borderRadius: 12,
867
+ border: "1px solid #d2d2d2",
868
+ fontSize: 15,
869
+ outline: "none",
870
+ transition: "0.25s",
871
+ background: "rgba(255,255,255,0.85)"
872
+ },
873
+ onFocus: (e) => {
874
+ e.currentTarget.style.border = "1px solid #4b4bff";
875
+ e.currentTarget.style.boxShadow = "0 0 0 3px rgba(75,75,255,0.25)";
876
+ },
877
+ onBlur: (e) => {
878
+ e.currentTarget.style.border = "1px solid #d2d2d2";
879
+ e.currentTarget.style.boxShadow = "0 0 0 transparent";
880
+ }
881
+ }
882
+ )
883
+ ] }),
884
+ /* @__PURE__ */ jsxs3(
885
+ "div",
886
+ {
887
+ style: {
888
+ marginBottom: 20,
889
+ fontSize: 12,
890
+ color: "#555"
891
+ },
892
+ children: [
893
+ /* @__PURE__ */ jsx5("p", { style: { marginBottom: 6 }, children: "Use at least 8 characters, including letters, numbers, and symbols." }),
894
+ password && /* @__PURE__ */ jsxs3(
895
+ "p",
896
+ {
897
+ style: {
898
+ fontWeight: 600,
899
+ color: getPasswordStrength(password).color
900
+ },
901
+ children: [
902
+ "Password strength: ",
903
+ getPasswordStrength(password).label
904
+ ]
905
+ }
906
+ )
907
+ ]
908
+ }
909
+ ),
910
+ /* @__PURE__ */ jsx5(
911
+ "p",
912
+ {
913
+ style: {
914
+ textAlign: "center",
915
+ fontSize: 13,
916
+ color: "#4b4bff",
917
+ cursor: "pointer",
918
+ marginBottom: 24
919
+ },
920
+ onClick: () => {
921
+ window.dispatchEvent(new CustomEvent("auth-flow-kit:navigate-to-login"));
922
+ },
923
+ children: "Already have an account? Sign in"
924
+ }
925
+ ),
731
926
  /* @__PURE__ */ jsx5(
732
927
  "button",
733
928
  {
@@ -753,6 +948,8 @@ function SignupScreen() {
753
948
  error && /* @__PURE__ */ jsx5(
754
949
  "p",
755
950
  {
951
+ role: "alert",
952
+ "aria-live": "polite",
756
953
  style: {
757
954
  marginTop: 18,
758
955
  color: "crimson",