@insforge/react 0.4.8 → 0.4.10

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
@@ -7,7 +7,7 @@
7
7
  ✅ **5-Minute Setup** - One provider + one line of router config = done
8
8
  ✅ **Built-in Auth UI** - Use deployed auth pages (like Next.js middleware)
9
9
  ✅ **Framework Agnostic** - Works with any React framework
10
- ✅ **Full TypeScript** - Complete type safety out of the box
10
+ ✅ **Full TypeScript** - Complete type safety out of the box
11
11
 
12
12
  ---
13
13
 
@@ -25,6 +25,15 @@ yarn add @insforge/react
25
25
  pnpm add @insforge/react
26
26
  ```
27
27
 
28
+ **Required Dependencies:** `bash npm install react@^19.0.0 react-dom@^19.0.0
29
+ react-router-dom@^7.9.0 `
30
+
31
+ #### Environment Variables
32
+
33
+ ```bash .env
34
+ VITE_INSFORGE_BASE_URL=https://your-project.insforge.app/
35
+ ```
36
+
28
37
  ### 2. Setup Provider
29
38
 
30
39
  Wrap your app with `InsforgeProvider`:
@@ -34,39 +43,45 @@ Wrap your app with `InsforgeProvider`:
34
43
  import { StrictMode } from 'react';
35
44
  import { createRoot } from 'react-dom/client';
36
45
  import { InsforgeProvider } from '@insforge/react';
37
- import '@insforge/react/styles.css';
38
46
  import App from './App';
39
47
 
40
48
  createRoot(document.getElementById('root')!).render(
41
49
  <StrictMode>
42
- <InsforgeProvider
43
- baseUrl={import.meta.env.VITE_INSFORGE_BASE_URL}
44
- afterSignInUrl="/dashboard"
45
- >
50
+ <InsforgeProvider baseUrl={import.meta.env.VITE_INSFORGE_BASE_URL} afterSignInUrl="/dashboard">
46
51
  <App />
47
52
  </InsforgeProvider>
48
53
  </StrictMode>
49
54
  );
50
55
  ```
51
56
 
52
- ### 3. Configure Router (Built-in Auth)
57
+ ### Step 3: Configure Router (Built-in Auth)
53
58
 
54
- ```tsx
55
- // src/App.tsx
59
+ **The fastest way** - Use deployed auth pages with Layout pattern:
60
+
61
+ ```tsx src/App.tsx
56
62
  import { createBrowserRouter, RouterProvider } from 'react-router-dom';
57
63
  import { getInsforgeRoutes } from '@insforge/react/router';
64
+ import Layout from './components/Layout';
58
65
  import Home from './pages/Home';
59
66
  import Dashboard from './pages/Dashboard';
60
67
 
61
68
  const router = createBrowserRouter([
62
- { path: '/', element: <Home /> },
63
-
69
+ {
70
+ path: '/',
71
+ element: <Layout />,
72
+ children: [
73
+ { index: true, element: <Home /> },
74
+
75
+ { path: 'dashboard', element: <Dashboard /> }
76
+ ]
77
+ },
78
+
79
+ // Add built-in auth redirect routes (sign-in, sign-up, forgot-password, etc.)
64
80
  ...getInsforgeRoutes({
65
81
  baseUrl: import.meta.env.VITE_INSFORGE_BASE_URL,
66
- builtInAuth: true
67
- }),
68
-
69
- { path: '/dashboard', element: <Dashboard /> }
82
+ afterSignInUrl="/dashboard"
83
+ builtInAuth: true // Redirects to deployed auth pages
84
+ })
70
85
  ]);
71
86
 
72
87
  export default function App() {
@@ -75,17 +90,22 @@ export default function App() {
75
90
  ```
76
91
 
77
92
  **What this does:**
93
+
78
94
  - Visiting `/sign-in` → Redirects to `your-project.insforge.app/auth/sign-in`
79
95
  - Visiting `/sign-up` → Redirects to `your-project.insforge.app/auth/sign-up`
96
+ - Visiting `/forgot-password` → Redirects to `your-project.insforge.app/auth/forgot-password`
80
97
  - After auth → Backend redirects with token in URL → SDK auto-detects and stores token
81
98
 
82
- ### 4. Add Auth UI to Your Pages
99
+ ### 4. Use Hooks & Components
83
100
 
84
101
  ```tsx
85
102
  // src/pages/Home.tsx
86
- import { SignedIn, SignedOut, UserButton } from '@insforge/react';
103
+ import { SignedIn, SignedOut, UserButton, useAuth, useUser } from '@insforge/react';
87
104
 
88
105
  export default function Home() {
106
+ const { isSignedIn } = useAuth();
107
+ const { user } = useUser();
108
+
89
109
  return (
90
110
  <div>
91
111
  <nav>
@@ -95,6 +115,7 @@ export default function Home() {
95
115
 
96
116
  <SignedIn>
97
117
  <UserButton afterSignOutUrl="/" />
118
+ <h1>Welcome, {user?.email}!</h1>
98
119
  </SignedIn>
99
120
  </nav>
100
121
 
@@ -121,9 +142,7 @@ Uses your deployed Insforge auth pages (includes all flows):
121
142
  paths: {
122
143
  signIn: '/sign-in', // Sign in page
123
144
  signUp: '/sign-up', // Sign up page
124
- verifyEmail: '/verify-email', // Email verification page
125
145
  forgotPassword: '/forgot-password', // Request password reset
126
- resetPassword: '/reset-password' // Reset password with token
127
146
  }
128
147
  })
129
148
  ```
@@ -133,23 +152,15 @@ Uses your deployed Insforge auth pages (includes all flows):
133
152
  Use package components with your own styling:
134
153
 
135
154
  ```tsx
136
- import {
137
- SignIn,
138
- SignUp,
139
- ForgotPassword,
140
- ResetPassword,
141
- VerifyEmail
142
- } from '@insforge/react';
155
+ import { SignIn, SignUp, ForgotPassword, ResetPassword, VerifyEmail } from '@insforge/react';
143
156
 
144
157
  const router = createBrowserRouter([
145
158
  { path: '/', element: <Home /> },
146
-
159
+
147
160
  // Use package components for complete auth flows
148
161
  { path: '/sign-in', element: <SignIn afterSignInUrl="/dashboard" /> },
149
162
  { path: '/sign-up', element: <SignUp afterSignUpUrl="/dashboard" /> },
150
- { path: '/forgot-password', element: <ForgotPassword backToSignInUrl="/sign-in" /> },
151
- { path: '/reset-password', element: <ResetPassword token={searchParams.get('token')} /> },
152
- { path: '/verify-email', element: <VerifyEmail token={searchParams.get('token')} /> }
163
+ { path: '/forgot-password', element: <ForgotPassword /> },
153
164
  ]);
154
165
  ```
155
166
 
@@ -162,13 +173,13 @@ import { useAuth } from '@insforge/react';
162
173
 
163
174
  function CustomSignIn() {
164
175
  const { signIn } = useAuth();
165
-
176
+
166
177
  const handleSubmit = async (e) => {
167
178
  e.preventDefault();
168
179
  await signIn(email, password);
169
180
  navigate('/dashboard');
170
181
  };
171
-
182
+
172
183
  return <form onSubmit={handleSubmit}>...</form>;
173
184
  }
174
185
  ```
@@ -180,6 +191,7 @@ function CustomSignIn() {
180
191
  ### Components
181
192
 
182
193
  **Pre-built with Business Logic:**
194
+
183
195
  - `<SignIn />` - Complete sign-in with email/password & OAuth
184
196
  - `<SignUp />` - Registration with password validation & email verification
185
197
  - `<ForgotPassword />` - Request password reset with email validation
@@ -190,6 +202,7 @@ function CustomSignIn() {
190
202
  - `<SignedIn>` / `<SignedOut>` - Conditional rendering
191
203
 
192
204
  **Form Components (Pure UI):**
205
+
193
206
  - `<SignInForm />` - Sign-in UI without logic
194
207
  - `<SignUpForm />` - Sign-up UI without logic
195
208
  - `<ForgotPasswordForm />` - Password reset request UI
@@ -197,6 +210,7 @@ function CustomSignIn() {
197
210
  - `<VerifyEmailStatus />` - Email verification status UI
198
211
 
199
212
  **Atomic Components (14 total):**
213
+
200
214
  - `<AuthContainer />`, `<AuthHeader />`, `<AuthFormField />`, `<AuthPasswordField />`, `<AuthEmailVerificationStep />`, etc.
201
215
 
202
216
  ### Hooks
@@ -239,6 +253,7 @@ All components support full text customization:
239
253
  successMessage="We've sent a reset code to your inbox"
240
254
  />
241
255
  ```
256
+
242
257
  ---
243
258
 
244
259
  ## Advanced Usage
@@ -260,7 +275,7 @@ function CustomSignIn() {
260
275
  e.preventDefault();
261
276
  setLoading(true);
262
277
  setError('');
263
-
278
+
264
279
  try {
265
280
  await signIn(email, password);
266
281
  // Custom success logic
@@ -282,7 +297,7 @@ function CustomSignIn() {
282
297
  loading={loading}
283
298
  availableProviders={['google', 'github']}
284
299
  onOAuthClick={(provider) => {
285
- // Custom OAuth logic
300
+ console.log(provider);
286
301
  }}
287
302
  />
288
303
  );
@@ -307,10 +322,7 @@ import {
307
322
  function CompletelyCustomAuth() {
308
323
  return (
309
324
  <AuthContainer>
310
- <AuthHeader
311
- title="Welcome to MyApp"
312
- subtitle="Sign in to continue"
313
- />
325
+ <AuthHeader title="Welcome to MyApp" subtitle="Sign in to continue" />
314
326
 
315
327
  <AuthErrorBanner error={error} />
316
328
 
@@ -332,9 +344,7 @@ function CompletelyCustomAuth() {
332
344
  showStrengthIndicator
333
345
  />
334
346
 
335
- <AuthSubmitButton isLoading={loading}>
336
- Sign In
337
- </AuthSubmitButton>
347
+ <AuthSubmitButton isLoading={loading}>Sign In</AuthSubmitButton>
338
348
  </form>
339
349
 
340
350
  <AuthDivider text="or" />
@@ -345,11 +355,7 @@ function CompletelyCustomAuth() {
345
355
  loading={oauthLoading}
346
356
  />
347
357
 
348
- <AuthLink
349
- text="Don't have an account?"
350
- linkText="Sign up"
351
- href="/sign-up"
352
- />
358
+ <AuthLink text="Don't have an account?" linkText="Sign up" href="/sign-up" />
353
359
  </AuthContainer>
354
360
  );
355
361
  }
@@ -364,17 +370,14 @@ function Dashboard() {
364
370
  return (
365
371
  <div>
366
372
  <h1>Dashboard</h1>
367
-
373
+
368
374
  {/* Simple protection */}
369
375
  <Protect redirectTo="/sign-in">
370
376
  <UserContent />
371
377
  </Protect>
372
378
 
373
379
  {/* Role-based protection */}
374
- <Protect
375
- redirectTo="/unauthorized"
376
- condition={(user) => user.role === 'admin'}
377
- >
380
+ <Protect redirectTo="/unauthorized" condition={(user) => user.role === 'admin'}>
378
381
  <AdminPanel />
379
382
  </Protect>
380
383
  </div>
@@ -384,39 +387,10 @@ function Dashboard() {
384
387
 
385
388
  ---
386
389
 
387
- ## TypeScript
388
-
389
- Full TypeScript support with exported types:
390
-
391
- ```tsx
392
- import type {
393
- InsforgeUser,
394
- SignInProps,
395
- SignUpProps,
396
- ForgotPasswordProps,
397
- ResetPasswordProps,
398
- VerifyEmailProps,
399
- UserButtonProps,
400
- ProtectProps,
401
- ConditionalProps,
402
- SignInFormProps,
403
- SignUpFormProps,
404
- ForgotPasswordFormProps,
405
- ResetPasswordFormProps,
406
- VerifyEmailStatusProps,
407
- AuthFormFieldProps,
408
- OAuthProvider,
409
- AuthConfig,
410
- InsforgeProviderProps,
411
- GetInsforgeRoutesConfig,
412
- } from '@insforge/react';
413
- ```
414
-
415
- ---
416
-
417
390
  ## OAuth Providers
418
391
 
419
392
  Built-in support for 10+ OAuth providers:
393
+
420
394
  - Google
421
395
  - GitHub
422
396
  - Discord
@@ -433,20 +407,6 @@ Providers are auto-detected from your backend configuration.
433
407
 
434
408
  ---
435
409
 
436
- ## Validation Utilities
437
-
438
- ```tsx
439
- import { emailSchema, cn } from '@insforge/react/lib';
440
-
441
- // Validate email with Zod
442
- const result = emailSchema.safeParse('user@example.com');
443
-
444
- // Merge Tailwind classes
445
- const className = cn('px-4 py-2', 'bg-blue-500', conditionalClass);
446
- ```
447
-
448
- ---
449
-
450
410
  ## Available Atomic Components
451
411
 
452
412
  Low-level building blocks for complete customization:
package/dist/atoms.cjs CHANGED
@@ -108,34 +108,27 @@ function AuthPasswordStrengthIndicator({
108
108
  config
109
109
  }) {
110
110
  const requirements = createRequirements(config);
111
- return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "if-passwordStrength if-internal-ps6w3k", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "if-passwordStrength-requirements", children: requirements.map((req) => /* @__PURE__ */ jsxRuntime.jsxs(
112
- "div",
113
- {
114
- className: `if-passwordStrength-requirement ${req.test(password) ? "met" : "unmet"}`,
115
- children: [
116
- /* @__PURE__ */ jsxRuntime.jsx(
117
- "div",
118
- {
119
- style: {
120
- display: "flex",
121
- alignItems: "center",
122
- justifyContent: "center",
123
- width: "1rem",
124
- height: "1rem",
125
- borderRadius: "50%",
126
- border: "2px solid",
127
- borderColor: req.test(password) ? "transparent" : "#9ca3af",
128
- backgroundColor: req.test(password) ? "#059669" : "white",
129
- transition: "all 0.2s"
130
- },
131
- children: req.test(password) && /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Check, { style: { width: "0.75rem", height: "0.75rem", color: "white" } })
132
- }
133
- ),
134
- /* @__PURE__ */ jsxRuntime.jsx("span", { children: req.label })
135
- ]
136
- },
137
- req.label
138
- )) }) });
111
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "if-passwordStrength if-internal-ps6w3k", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "if-passwordStrength-requirements", children: requirements.map((req) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "if-passwordStrength-requirement", children: [
112
+ /* @__PURE__ */ jsxRuntime.jsx(
113
+ "div",
114
+ {
115
+ style: {
116
+ display: "flex",
117
+ alignItems: "center",
118
+ justifyContent: "center",
119
+ width: "1rem",
120
+ height: "1rem",
121
+ borderRadius: "50%",
122
+ border: "2px solid",
123
+ borderColor: req.test(password) ? "transparent" : "#9ca3af",
124
+ backgroundColor: req.test(password) ? "#059669" : "white",
125
+ transition: "all 0.2s"
126
+ },
127
+ children: req.test(password) && /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Check, { style: { width: "0.75rem", height: "0.75rem", color: "white" } })
128
+ }
129
+ ),
130
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: req.label })
131
+ ] }, req.label)) }) });
139
132
  }
140
133
  function createRequirements(config) {
141
134
  const requirements = [];
@@ -203,11 +196,12 @@ function AuthPasswordField({
203
196
  onFocus,
204
197
  ...props
205
198
  }) {
199
+ const [searchParams] = reactRouterDom.useSearchParams();
206
200
  const [showPassword, setShowPassword] = react.useState(false);
207
201
  const [showStrength, setShowStrength] = react.useState(false);
208
202
  const resolvedForgotPasswordHref = react.useMemo(
209
- () => forgotPasswordLink ? resolveAuthPath(forgotPasswordLink.href) : void 0,
210
- [forgotPasswordLink]
203
+ () => forgotPasswordLink ? resolveAuthUrl(forgotPasswordLink.href, searchParams) : void 0,
204
+ [forgotPasswordLink, searchParams]
211
205
  );
212
206
  const handleFocus = (e) => {
213
207
  if (showStrengthIndicator) {
@@ -622,29 +616,20 @@ function useInsforge() {
622
616
  }
623
617
  function AuthEmailVerificationStep({
624
618
  email,
625
- method = "code",
626
- onVerifyCode
619
+ method,
620
+ onVerifyCode,
621
+ emailSent = false
627
622
  }) {
628
623
  const { sendVerificationEmail } = useInsforge();
629
624
  const [resendDisabled, setResendDisabled] = react.useState(true);
630
- const [resendCountdown, setResendCountdown] = react.useState(60);
625
+ const [resendCountdown, setResendCountdown] = react.useState(emailSent ? 60 : 0);
631
626
  const [isSending, setIsSending] = react.useState(false);
632
627
  const [verificationCode, setVerificationCode] = react.useState("");
633
628
  const [isVerifying, setIsVerifying] = react.useState(false);
634
629
  const isLinkMethod = method === "link";
635
- const displayDescription = isLinkMethod ? "We have sent an email to {email}. Please check your email to confirm your account before signing in. The confirmation link expires in 10 minutes." : "We've sent a verification code to your inbox at {email}. Enter it below to proceed.";
630
+ const displayDescription = isLinkMethod ? "We've sent an email to {email}. Please check your email to confirm your account before signing in. The confirmation link expires in 10 minutes." : "We've sent a verification code to your inbox at {email}. Enter it below to proceed.";
636
631
  react.useEffect(() => {
637
- const sendInitialEmail = async () => {
638
- try {
639
- await sendVerificationEmail(email);
640
- } catch (error) {
641
- console.error("Failed to send verification email:", error);
642
- }
643
- };
644
- void sendInitialEmail();
645
- }, [email, sendVerificationEmail]);
646
- react.useEffect(() => {
647
- if (resendCountdown > 0) {
632
+ if (emailSent && resendCountdown > 0) {
648
633
  const timer = setInterval(() => {
649
634
  setResendCountdown((prev) => {
650
635
  if (prev <= 1) {
@@ -656,7 +641,7 @@ function AuthEmailVerificationStep({
656
641
  }, 1e3);
657
642
  return () => clearInterval(timer);
658
643
  }
659
- }, [resendCountdown]);
644
+ }, [emailSent, resendCountdown]);
660
645
  const handleResend = async () => {
661
646
  setResendDisabled(true);
662
647
  setResendCountdown(60);