@insforge/react 0.4.0 → 0.4.6

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/README.md CHANGED
@@ -39,7 +39,10 @@ import App from './App';
39
39
 
40
40
  createRoot(document.getElementById('root')!).render(
41
41
  <StrictMode>
42
- <InsforgeProvider baseUrl={import.meta.env.VITE_INSFORGE_BASE_URL}>
42
+ <InsforgeProvider
43
+ baseUrl={import.meta.env.VITE_INSFORGE_BASE_URL}
44
+ afterSignInUrl="/dashboard"
45
+ >
43
46
  <App />
44
47
  </InsforgeProvider>
45
48
  </StrictMode>
@@ -74,7 +77,7 @@ export default function App() {
74
77
  **What this does:**
75
78
  - Visiting `/sign-in` → Redirects to `your-project.insforge.app/auth/sign-in`
76
79
  - Visiting `/sign-up` → Redirects to `your-project.insforge.app/auth/sign-up`
77
- - After auth → Redirects back to `/auth/callback`Goes to dashboard
80
+ - After auth → Backend redirects with token in URL SDK auto-detects and stores token
78
81
 
79
82
  ### 4. Add Auth UI to Your Pages
80
83
 
@@ -120,8 +123,7 @@ Uses your deployed Insforge auth pages (includes all flows):
120
123
  signUp: '/sign-up', // Sign up page
121
124
  verifyEmail: '/verify-email', // Email verification page
122
125
  forgotPassword: '/forgot-password', // Request password reset
123
- resetPassword: '/reset-password', // Reset password with token
124
- callback: '/auth/callback' // OAuth callback handler
126
+ resetPassword: '/reset-password' // Reset password with token
125
127
  }
126
128
  })
127
129
  ```
@@ -142,12 +144,6 @@ import {
142
144
  const router = createBrowserRouter([
143
145
  { path: '/', element: <Home /> },
144
146
 
145
- // Still need callback route for OAuth
146
- ...getInsforgeRoutes({
147
- baseUrl: import.meta.env.VITE_INSFORGE_BASE_URL,
148
- builtInAuth: false // Don't redirect to deployed UI
149
- }),
150
-
151
147
  // Use package components for complete auth flows
152
148
  { path: '/sign-in', element: <SignIn afterSignInUrl="/dashboard" /> },
153
149
  { path: '/sign-up', element: <SignUp afterSignUpUrl="/dashboard" /> },
@@ -192,7 +188,6 @@ function CustomSignIn() {
192
188
  - `<UserButton />` - User dropdown with sign-out
193
189
  - `<Protect />` - Route protection wrapper
194
190
  - `<SignedIn>` / `<SignedOut>` - Conditional rendering
195
- - `<InsforgeCallback />` - OAuth callback handler
196
191
 
197
192
  **Form Components (Pure UI):**
198
193
  - `<SignInForm />` - Sign-in UI without logic
@@ -404,7 +399,6 @@ import type {
404
399
  UserButtonProps,
405
400
  ProtectProps,
406
401
  ConditionalProps,
407
- InsforgeCallbackProps,
408
402
  SignInFormProps,
409
403
  SignUpFormProps,
410
404
  ForgotPasswordFormProps,
package/dist/atoms.cjs CHANGED
@@ -3,7 +3,8 @@
3
3
  var jsxRuntime = require('react/jsx-runtime');
4
4
  var lucideReact = require('lucide-react');
5
5
  var react = require('react');
6
- var sdk = require('@insforge/sdk');
6
+ var reactRouterDom = require('react-router-dom');
7
+ require('@insforge/sdk');
7
8
 
8
9
  function AuthBranding() {
9
10
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "if-authBranding if-internal-ab4k9w", children: [
@@ -91,21 +92,12 @@ function AuthErrorBanner({ error }) {
91
92
  if (!error) {
92
93
  return null;
93
94
  }
94
- return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "if-errorBanner if-internal-eb2m7k", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", alignItems: "center", gap: "0.5rem" }, children: [
95
- /* @__PURE__ */ jsxRuntime.jsx(
96
- lucideReact.AlertTriangle,
97
- {
98
- style: { width: "1.5rem", height: "1.5rem", flexShrink: 0, color: "#dc2626" }
99
- }
100
- ),
95
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "if-errorBanner if-internal-eb2m7k", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "if-errorBanner-content", children: [
96
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.AlertTriangle, { className: "if-errorBanner-icon" }),
101
97
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "if-errorBanner-text", children: error })
102
98
  ] }) });
103
99
  }
104
- function AuthFormField({
105
- label,
106
- id,
107
- ...props
108
- }) {
100
+ function AuthFormField({ label, id, ...props }) {
109
101
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "if-formField if-internal-f9n6p2", children: [
110
102
  /* @__PURE__ */ jsxRuntime.jsx("label", { htmlFor: id, className: "if-formField-label if-internal-l3k8m1", children: label }),
111
103
  /* @__PURE__ */ jsxRuntime.jsx("input", { id, className: "if-formField-input if-internal-i2v8k4", ...props })
@@ -232,14 +224,17 @@ function AuthSubmitButton({
232
224
  children,
233
225
  isLoading = false,
234
226
  confirmed = false,
235
- disabled = false
227
+ disabled = false,
228
+ type = "submit",
229
+ onClick
236
230
  }) {
237
231
  return /* @__PURE__ */ jsxRuntime.jsxs(
238
232
  "button",
239
233
  {
240
- type: "submit",
234
+ type,
241
235
  className: "if-submitButton if-internal-b8p3m4",
242
236
  disabled: disabled || isLoading || confirmed,
237
+ onClick,
243
238
  children: [
244
239
  isLoading && /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Loader2, { className: "if-submitButton-icon if-submitButton-spinner", size: 20 }),
245
240
  confirmed && /* @__PURE__ */ jsxRuntime.jsx(lucideReact.CircleCheck, { className: "if-submitButton-icon", size: 20 }),
@@ -248,24 +243,16 @@ function AuthSubmitButton({
248
243
  }
249
244
  );
250
245
  }
251
- function AuthLink({
252
- text,
253
- linkText,
254
- href
255
- }) {
256
- const currentSearch = typeof window !== "undefined" ? window.location.search : "";
246
+ function AuthLink({ text, linkText, href }) {
247
+ const [searchParams] = reactRouterDom.useSearchParams();
248
+ const currentSearch = searchParams.toString();
257
249
  const finalHref = (() => {
258
250
  if (!currentSearch) {
259
251
  return href;
260
252
  }
261
253
  try {
262
254
  const url = new URL(href, window.location.origin);
263
- const currentParams = new URLSearchParams(currentSearch);
264
- currentParams.forEach((value, key) => {
265
- if (!url.searchParams.has(key)) {
266
- url.searchParams.set(key, value);
267
- }
268
- });
255
+ url.search = currentSearch;
269
256
  return url.pathname + url.search;
270
257
  } catch {
271
258
  return href;
@@ -630,28 +617,26 @@ function useInsforge() {
630
617
  }
631
618
  function AuthEmailVerificationStep({
632
619
  email,
633
- description,
634
620
  method = "code",
635
621
  onVerifyCode
636
622
  }) {
637
- const { baseUrl } = useInsforge();
638
- const [insforge] = react.useState(() => sdk.createClient({ baseUrl }));
623
+ const { sendVerificationEmail } = useInsforge();
639
624
  const [resendDisabled, setResendDisabled] = react.useState(true);
640
625
  const [resendCountdown, setResendCountdown] = react.useState(60);
641
626
  const [isSending, setIsSending] = react.useState(false);
642
627
  const [verificationCode, setVerificationCode] = react.useState("");
643
628
  const [isVerifying, setIsVerifying] = react.useState(false);
644
- const [verificationError, setVerificationError] = react.useState("");
645
629
  const defaultDescription = method === "code" ? "We've sent a 6-digit verification code to {email}. Please enter it below to verify your account. The code will expire in 10 minutes." : "We've sent a verification link to {email}. Please check your email and click the link to verify your account. The link will expire in 10 minutes.";
646
630
  react.useEffect(() => {
647
631
  const sendInitialEmail = async () => {
648
632
  try {
649
- await insforge.auth.sendVerificationEmail({ email });
650
- } catch {
633
+ await sendVerificationEmail(email);
634
+ } catch (error) {
635
+ console.error("Failed to send verification email:", error);
651
636
  }
652
637
  };
653
638
  void sendInitialEmail();
654
- }, [email, method, insforge.auth]);
639
+ }, [email, sendVerificationEmail]);
655
640
  react.useEffect(() => {
656
641
  if (resendCountdown > 0) {
657
642
  const timer = setInterval(() => {
@@ -670,9 +655,8 @@ function AuthEmailVerificationStep({
670
655
  setResendDisabled(true);
671
656
  setResendCountdown(60);
672
657
  setIsSending(true);
673
- setVerificationError("");
674
658
  try {
675
- await insforge.auth.sendVerificationEmail({ email });
659
+ await sendVerificationEmail(email);
676
660
  } catch {
677
661
  setResendDisabled(false);
678
662
  setResendCountdown(0);
@@ -680,123 +664,157 @@ function AuthEmailVerificationStep({
680
664
  setIsSending(false);
681
665
  }
682
666
  };
683
- const handleVerifyCode = async (code) => {
684
- if (!onVerifyCode) {
667
+ const handleSubmit = async () => {
668
+ if (!onVerifyCode || verificationCode.length !== 6) {
685
669
  return;
686
670
  }
687
671
  setIsVerifying(true);
688
- setVerificationError("");
689
672
  try {
690
- await onVerifyCode(code);
691
- } catch (error) {
692
- setVerificationError(
693
- error instanceof Error ? error.message : "Invalid verification code. Please try again."
694
- );
695
- setVerificationCode("");
673
+ await onVerifyCode(verificationCode);
696
674
  } finally {
697
675
  setIsVerifying(false);
676
+ setVerificationCode("");
698
677
  }
699
678
  };
700
- const displayDescription = description || defaultDescription;
701
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", flexDirection: "column", gap: "1.5rem", alignItems: "stretch" }, children: [
702
- /* @__PURE__ */ jsxRuntime.jsx("p", { className: "if-verificationCode-description", children: displayDescription.split("{email}").map((part, index, array) => /* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
679
+ const displayDescription = defaultDescription;
680
+ const isLinkMethod = method === "link";
681
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "if-verificationStep", children: [
682
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "if-verificationStep-description", children: displayDescription.split("{email}").map((part, index, array) => /* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
703
683
  part,
704
684
  index < array.length - 1 && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "if-verificationCode-email", children: email })
705
685
  ] }, index)) }),
706
- verificationError && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "if-errorBanner if-internal-eb2m7k", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", alignItems: "center", gap: "0.5rem" }, children: [
686
+ !isLinkMethod && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "if-verificationStep-codeContainer", children: [
687
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "if-verificationStep-codeInputWrapper", children: /* @__PURE__ */ jsxRuntime.jsx(
688
+ AuthVerificationCodeInput,
689
+ {
690
+ value: verificationCode,
691
+ onChange: setVerificationCode,
692
+ email,
693
+ disabled: isVerifying
694
+ }
695
+ ) }),
707
696
  /* @__PURE__ */ jsxRuntime.jsx(
708
- "svg",
697
+ AuthSubmitButton,
709
698
  {
710
- style: { width: "1.5rem", height: "1.5rem", flexShrink: 0, color: "#DC2626" },
711
- fill: "none",
712
- strokeLinecap: "round",
713
- strokeLinejoin: "round",
714
- strokeWidth: "2",
715
- viewBox: "0 0 24 24",
716
- stroke: "currentColor",
717
- children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z" })
699
+ type: "button",
700
+ isLoading: isVerifying,
701
+ disabled: isVerifying || verificationCode.length !== 6,
702
+ onClick: () => {
703
+ void handleSubmit();
704
+ },
705
+ children: isVerifying ? "Verifying..." : "Verify Code"
718
706
  }
719
- ),
720
- /* @__PURE__ */ jsxRuntime.jsx("p", { className: "if-errorBanner-text", children: verificationError })
721
- ] }) }),
722
- method === "code" && /* @__PURE__ */ jsxRuntime.jsxs(
723
- "div",
724
- {
725
- style: {
726
- width: "100%",
727
- backgroundColor: "#F5F5F5",
728
- borderRadius: "0.5rem",
729
- padding: "1rem 1rem 1.5rem",
730
- display: "flex",
731
- flexDirection: "column",
732
- gap: "1rem"
733
- },
734
- children: [
735
- /* @__PURE__ */ jsxRuntime.jsx(
736
- AuthVerificationCodeInput,
737
- {
738
- value: verificationCode,
739
- onChange: setVerificationCode,
740
- email,
741
- disabled: isVerifying,
742
- onComplete: (code) => {
743
- void handleVerifyCode(code);
744
- }
745
- }
746
- ),
747
- isVerifying && /* @__PURE__ */ jsxRuntime.jsx(
748
- "p",
749
- {
750
- style: {
751
- fontSize: "0.875rem",
752
- color: "#828282",
753
- textAlign: "center",
754
- fontFamily: "var(--if-font-family)"
755
- },
756
- children: "Verifying..."
757
- }
758
- )
759
- ]
760
- }
761
- ),
762
- /* @__PURE__ */ jsxRuntime.jsxs(
763
- "div",
764
- {
765
- style: {
766
- width: "100%",
767
- fontSize: "0.875rem",
768
- textAlign: "center",
769
- color: "#828282",
770
- fontFamily: "var(--if-font-family)"
771
- },
772
- children: [
773
- "Didn't receive the email?",
774
- " ",
775
- /* @__PURE__ */ jsxRuntime.jsx(
776
- "button",
777
- {
778
- onClick: () => {
779
- void handleResend();
780
- },
781
- disabled: resendDisabled || isSending,
782
- style: {
783
- color: "#000",
784
- fontWeight: 500,
785
- transition: "all 0.2s",
786
- cursor: resendDisabled || isSending ? "not-allowed" : "pointer",
787
- background: "none",
788
- border: "none",
789
- padding: 0,
790
- textDecoration: resendDisabled || isSending ? "none" : "underline",
791
- opacity: resendDisabled || isSending ? 0.5 : 1,
792
- fontFamily: "var(--if-font-family)"
793
- },
794
- children: isSending ? "Sending..." : resendDisabled ? `Retry in (${resendCountdown}s)` : "Click to resend"
795
- }
796
- )
797
- ]
798
- }
799
- )
707
+ )
708
+ ] }),
709
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "if-verificationStep-resendContainer", children: [
710
+ "Didn't receive the email?",
711
+ " ",
712
+ /* @__PURE__ */ jsxRuntime.jsx(
713
+ "button",
714
+ {
715
+ onClick: () => {
716
+ void handleResend();
717
+ },
718
+ disabled: resendDisabled || isSending,
719
+ className: "if-verificationStep-resendButton",
720
+ children: isSending ? "Sending..." : resendDisabled ? `Retry in (${resendCountdown}s)` : "Click to resend"
721
+ }
722
+ )
723
+ ] })
724
+ ] });
725
+ }
726
+ function AuthResetPasswordVerificationStep({
727
+ email,
728
+ method,
729
+ onVerifyCode,
730
+ onResendEmail
731
+ }) {
732
+ const [resendDisabled, setResendDisabled] = react.useState(true);
733
+ const [resendCountdown, setResendCountdown] = react.useState(60);
734
+ const [isSending, setIsSending] = react.useState(false);
735
+ const [verificationCode, setVerificationCode] = react.useState("");
736
+ const [isVerifying, setIsVerifying] = react.useState(false);
737
+ react.useEffect(() => {
738
+ if (resendCountdown > 0) {
739
+ const timer = setInterval(() => {
740
+ setResendCountdown((prev) => {
741
+ if (prev <= 1) {
742
+ setResendDisabled(false);
743
+ return 0;
744
+ }
745
+ return prev - 1;
746
+ });
747
+ }, 1e3);
748
+ return () => clearInterval(timer);
749
+ }
750
+ }, [resendCountdown]);
751
+ const handleResend = react.useCallback(async () => {
752
+ setResendDisabled(true);
753
+ setResendCountdown(60);
754
+ setIsSending(true);
755
+ try {
756
+ await onResendEmail();
757
+ } catch {
758
+ setResendDisabled(false);
759
+ setResendCountdown(0);
760
+ } finally {
761
+ setIsSending(false);
762
+ }
763
+ }, [onResendEmail]);
764
+ const handleSubmit = async () => {
765
+ if (!onVerifyCode || verificationCode.length !== 6) {
766
+ return;
767
+ }
768
+ setIsVerifying(true);
769
+ try {
770
+ await onVerifyCode(verificationCode);
771
+ } finally {
772
+ setIsVerifying(false);
773
+ setVerificationCode("");
774
+ }
775
+ };
776
+ const isLinkMethod = method === "link";
777
+ const description = isLinkMethod ? `We've sent a password reset link to ${email}. Please check your email and click the link to reset your password. The link will expire in 10 minutes.` : `We've sent a 6-digit verification code to ${email}. Please enter it below to reset your password. The code will expire in 10 minutes.`;
778
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "if-verificationStep", children: [
779
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "if-verificationStep-description", children: description }),
780
+ !isLinkMethod && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "if-verificationStep-codeContainer", children: [
781
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "if-verificationStep-codeInputWrapper", children: /* @__PURE__ */ jsxRuntime.jsx(
782
+ AuthVerificationCodeInput,
783
+ {
784
+ value: verificationCode,
785
+ onChange: setVerificationCode,
786
+ email,
787
+ disabled: isVerifying
788
+ }
789
+ ) }),
790
+ /* @__PURE__ */ jsxRuntime.jsx(
791
+ AuthSubmitButton,
792
+ {
793
+ type: "button",
794
+ isLoading: isVerifying,
795
+ disabled: isVerifying || verificationCode.length !== 6,
796
+ onClick: () => {
797
+ void handleSubmit();
798
+ },
799
+ children: isVerifying ? "Verifying..." : "Continue"
800
+ }
801
+ )
802
+ ] }),
803
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "if-verificationStep-resendContainer", children: [
804
+ "Didn't receive the email?",
805
+ " ",
806
+ /* @__PURE__ */ jsxRuntime.jsx(
807
+ "button",
808
+ {
809
+ onClick: () => {
810
+ void handleResend();
811
+ },
812
+ disabled: resendDisabled || isSending,
813
+ className: "if-verificationStep-resendButton",
814
+ children: isSending ? "Sending..." : resendDisabled ? `Retry in (${resendCountdown}s)` : "Click to resend"
815
+ }
816
+ )
817
+ ] })
800
818
  ] });
801
819
  }
802
820
 
@@ -812,6 +830,7 @@ exports.AuthOAuthButton = AuthOAuthButton;
812
830
  exports.AuthOAuthProviders = AuthOAuthProviders;
813
831
  exports.AuthPasswordField = AuthPasswordField;
814
832
  exports.AuthPasswordStrengthIndicator = AuthPasswordStrengthIndicator;
833
+ exports.AuthResetPasswordVerificationStep = AuthResetPasswordVerificationStep;
815
834
  exports.AuthSubmitButton = AuthSubmitButton;
816
835
  exports.AuthVerificationCodeInput = AuthVerificationCodeInput;
817
836
  //# sourceMappingURL=atoms.cjs.map