@insforge/nextjs 0.7.0 → 0.7.2

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.mjs CHANGED
@@ -1,5 +1,19 @@
1
1
  "use client";
2
2
 
3
+ // Auto-inject InsForge styles
4
+ if (typeof document !== 'undefined' && typeof window !== 'undefined') {
5
+ const styleId = 'insforge-nextjs-styles';
6
+ if (!document.getElementById(styleId)) {
7
+ const style = document.createElement('style');
8
+ style.id = styleId;
9
+ style.textContent = "/**\n * InsForge Next.js Component Library Styles\n * A standalone CSS file for auth components - no Tailwind required!\n */\n\n/* Font Face Declaration */\n@font-face {\n font-family: 'Manrope';\n src: url('./fonts/Manrope-VariableFont_wght.ttf') format('truetype');\n font-weight: 100 900;\n font-style: normal;\n font-display: swap;\n}\n\n/* CSS Variables */\n:root {\n --font-manrope: 'Manrope', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;\n}\n\n/* Reset and Base Styles */\n.insforge-auth-container * {\n box-sizing: border-box;\n}\n\n/* Main Container - App handles layout, we just provide the card */\n.insforge-auth-container {\n width: 100%;\n max-width: 400px;\n background: white;\n}\n\n.insforge-branding {\n background: #FAFAFA;\n padding: 8px 8px 16px 8px;\n display: flex;\n flex-direction: row;\n justify-content: center;\n align-items: center;\n gap: 4px;\n}\n\n.insforge-branding-text {\n color: #000;\n font-family: var(--font-manrope);\n font-size: 12px;\n font-style: normal;\n font-weight: 400;\n line-height: normal;\n}\n\n/* Form Card */\n.insforge-auth-card {\n width: 100%;\n border-radius: 12px;\n overflow: hidden;\n box-shadow: 0 4px 12px 0 rgba(0, 0, 0, 0.25);\n}\n\n.insforge-auth-content {\n padding: 24px;\n display: flex;\n flex-direction: column;\n justify-content: center;\n align-items: stretch;\n gap: 24px;\n}\n\n/* Header */\n.insforge-auth-header {\n display: flex;\n flex-direction: column;\n justify-content: start;\n align-items: start;\n gap: 8px;\n}\n\n.insforge-auth-title {\n color: #000;\n font-family: Inter;\n font-size: 24px;\n font-style: normal;\n font-weight: 600;\n line-height: 32px; /* 133.333% */\n}\n\n.insforge-auth-subtitle {\n color: #828282;\n font-family: Inter;\n font-size: 14px;\n font-style: normal;\n font-weight: 400;\n line-height: 24px; /* 171.429% */\n}\n\n/* Error Banner */\n.insforge-error-banner {\n display: flex;\n padding: 8px 8px 8px 12px;\n margin-bottom: 16px;\n align-items: center;\n gap: 8px;\n align-self: stretch;\n border-radius: 4px;\n border: 2px solid #DC2626;\n background: #FEF2F2;\n color: #DC2626;\n font-family: Inter;\n font-size: 14px;\n font-style: normal;\n font-weight: 400;\n line-height: 20px; /* 142.857% */\n}\n\n.insforge-error-icon {\n color: #EF4444;\n flex-shrink: 0;\n width: 24px;\n height: 24px;\n}\n\n/* Form Elements */\n.insforge-form {\n display: flex;\n flex-direction: column;\n justify-content: center;\n align-items: stretch;\n gap: 24px;\n}\n\n.insforge-form-group {\n display: flex;\n flex-direction: column;\n justify-content: center;\n align-items: stretch;\n gap: 4px;\n}\n\n.insforge-form-label-row {\n display: flex;\n justify-content: space-between;\n align-items: center;\n}\n\n.insforge-form-label {\n color: #000;\n font-family: Inter;\n font-size: 14px;\n font-style: normal;\n font-weight: 400;\n line-height: 24px;\n}\n\n.insforge-form-link {\n color: #828282;\n text-align: right;\n font-family: Inter;\n font-size: 14px;\n font-style: normal;\n font-weight: 400;\n line-height: 24px;\n}\n\n.insforge-form-link:hover {\n color: #828282;\n text-decoration: underline;\n}\n\n/* Input Container for Password (with icon) */\n.insforge-input-wrapper {\n position: relative;\n}\n\n.insforge-input {\n width: 100%;\n display: flex;\n padding: 8px 8px 10px 12px;\n align-items: center;\n gap: 8px;\n align-self: stretch;\n border-radius: 4px;\n border: 1px solid #BCBCBC;\n background: #FFF;\n font-family: Inter;\n font-size: 16px;\n font-style: normal;\n font-weight: 400;\n line-height: 20px;\n}\n\n.insforge-input::placeholder {\n color: #A6A6A6;\n}\n\n.insforge-input:focus {\n outline: none;\n}\n\n.insforge-input-with-icon {\n padding-right: 3rem;\n}\n\n.insforge-input-icon-btn {\n position: absolute;\n right: 8px;\n top: 50%;\n transform: translateY(-50%);\n background: transparent;\n border: none;\n color: #A6A6A6;\n cursor: pointer;\n transition: color 0.2s;\n display: flex;\n align-items: center;\n justify-content: center;\n}\n\n.insforge-input-icon-btn:hover {\n color: #6b7280;\n}\n\n/* Primary Button */\n.insforge-btn-primary {\n border-radius: 4px;\n background: #000;\n width: 100%;\n display: flex;\n margin: 16px 0 0 0;\n padding: 8px 16px;\n justify-content: center;\n align-items: center;\n gap: 10px;\n align-self: stretch;\n color: #FFF;\n font-family: Manrope;\n font-size: 16px;\n font-style: normal;\n font-weight: 600;\n line-height: normal;\n border: none;\n cursor: pointer;\n}\n\n.insforge-btn-primary:hover {\n background: #303030;\n}\n\n.insforge-btn-primary:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n}\n\n.insforge-btn-primary .insforge-btn-loader {\n display: none;\n animation: insforge-spin 1s linear infinite;\n}\n\n.insforge-btn-primary[data-loading] .insforge-btn-loader {\n display: block;\n}\n\n.insforge-btn-primary .insforge-btn-check {\n display: none;\n}\n\n.insforge-btn-primary[data-confirmed] .insforge-btn-check {\n display: block;\n}\n\n/* Text Link Section */\n.insforge-text-center {\n text-align: center;\n color: #828282;\n font-family: Inter;\n font-size: 14px;\n font-style: normal;\n font-weight: 400;\n line-height: 24px;\n}\n\n.insforge-link-primary {\n color: #000;\n font-family: Inter;\n font-size: 14px;\n font-style: normal;\n font-weight: 500;\n line-height: 24px;\n}\n\n/* Divider */\n.insforge-divider {\n display: flex;\n justify-content: center;\n align-items: center;\n gap: 24px;\n align-self: stretch;\n}\n\n.insforge-divider::before,\n.insforge-divider::after {\n content: '';\n flex: 1;\n height: 1px;\n background: #C6C6C6;\n}\n\n.insforge-divider-text {\n color: #C6C6C6;\n font-family: Manrope;\n font-size: 14px;\n font-style: normal;\n font-weight: 600;\n line-height: normal;\n}\n\n/* OAuth Section */\n.insforge-oauth-container {\n display: grid;\n gap: 12px;\n width: 100%;\n}\n\n/* ============================================================================\n SMART OAUTH GRID LAYOUT SYSTEM\n Pattern: 1→1x1, 2→1x2, 3→1x3, 4→2x2, 5+→auto (3 per row, last row centered)\n ============================================================================ */\n\n/* 1 provider: single column, full width - displays \"Continue with Provider\" */\n.insforge-oauth-container[data-provider-count=\"1\"] {\n grid-template-columns: 1fr;\n}\n\n/* 2 providers: two columns - displays \"Provider\" */\n.insforge-oauth-container[data-provider-count=\"2\"] {\n grid-template-columns: repeat(2, 1fr);\n}\n\n/* 3 providers: three columns - icon only */\n.insforge-oauth-container[data-provider-count=\"3\"] {\n grid-template-columns: repeat(3, 1fr);\n}\n\n/* 4 providers: 2x2 grid - displays \"Provider\" */\n.insforge-oauth-container[data-provider-count=\"4\"] {\n grid-template-columns: repeat(2, 1fr);\n}\n\n/* 5+ providers: Universal 6-column grid system\n - Grid columns managed by OAuthProviderList component via inline styles\n - This provides precise control over button positioning */\n.insforge-oauth-container:not([data-provider-count=\"1\"]):not([data-provider-count=\"2\"]):not([data-provider-count=\"3\"]):not([data-provider-count=\"4\"]) {\n grid-template-columns: repeat(6, 1fr);\n}\n\n/* OAuth Button */\n.insforge-oauth-btn {\n display: flex;\n width: 100%;\n height: 36px;\n padding: 8px 12px;\n flex-direction: row;\n justify-content: center;\n align-items: center;\n gap: 12px;\n border-radius: 6px;\n border: 1px solid #E4E4E7;\n background: #FFF;\n box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.10);\n color: #09090B;\n text-align: center;\n font-family: Inter;\n font-size: 14px;\n font-style: normal;\n font-weight: 500;\n line-height: 20px;\n cursor: pointer;\n transition: all 0.2s ease;\n}\n\n/* Full mode: show icon + \"Continue with Provider\" */\n.insforge-oauth-btn[data-display-mode=\"full\"] {\n justify-content: center;\n}\n\n/* Short mode: show icon + \"Provider\" */\n.insforge-oauth-btn[data-display-mode=\"short\"] {\n justify-content: center;\n padding: 8px;\n gap: 8px;\n}\n\n/* Icon only mode: show only icon */\n.insforge-oauth-btn[data-display-mode=\"icon\"] {\n justify-content: center;\n gap: 0;\n}\n\n.insforge-oauth-btn[data-display-mode=\"icon\"] .insforge-oauth-icon {\n display: flex;\n align-items: center;\n justify-content: center;\n}\n\n.insforge-oauth-btn:hover {\n background: #f9fafb;\n border-color: #9ca3af;\n}\n\n.insforge-oauth-btn:disabled {\n opacity: 0.6;\n cursor: not-allowed;\n}\n\n.insforge-oauth-icon {\n display: flex;\n align-items: center;\n justify-content: center;\n flex-shrink: 0;\n}\n\n.insforge-oauth-loader {\n display: none;\n animation: insforge-spin 1s linear infinite;\n}\n\n.insforge-oauth-btn[data-loading] .insforge-oauth-icon {\n display: none;\n}\n\n.insforge-oauth-btn[data-loading] .insforge-oauth-loader {\n display: block;\n}\n\n/* Spin Animation */\n@keyframes insforge-spin {\n from {\n transform: rotate(0deg);\n }\n to {\n transform: rotate(360deg);\n }\n}\n\n/* UserButton Styles */\n.insforge-user-button-container {\n position: relative;\n display: inline-block;\n}\n\n.insforge-user-button {\n padding: 0.25rem;\n background: transparent;\n border: none;\n border-radius: 9999px;\n cursor: pointer;\n transition: all 0.2s;\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 0.5rem;\n}\n\n.insforge-user-button:hover {\n background: rgba(0, 0, 0, 0.05);\n}\n\n.insforge-user-button-detailed {\n border-radius: 0.5rem;\n padding: 0.5rem;\n}\n\n.insforge-user-button-info {\n display: flex;\n flex-direction: column;\n align-items: flex-start;\n gap: 0.125rem;\n}\n\n.insforge-user-button-name {\n font-size: 0.875rem;\n font-weight: 600;\n color: #111827;\n line-height: 1.25rem;\n text-align: left;\n}\n\n.insforge-user-button-email {\n font-size: 0.75rem;\n color: #6b7280;\n line-height: 1rem;\n text-align: left;\n}\n\n.insforge-user-avatar {\n width: 2.5rem;\n height: 2.5rem;\n border-radius: 9999px;\n object-fit: cover;\n}\n\n.insforge-user-avatar-placeholder {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 2.5rem;\n height: 2.5rem;\n background: #3b82f6;\n color: white;\n font-weight: 600;\n font-size: 0.875rem;\n border-radius: 9999px;\n}\n\n.insforge-user-dropdown {\n position: absolute;\n top: 100%;\n right: 0;\n margin-top: 0.5rem;\n min-width: 10rem;\n background: white;\n border: 1px solid #e5e7eb;\n border-radius: 0.5rem;\n box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);\n z-index: 50;\n overflow: hidden;\n padding: 0.25rem;\n}\n\n.insforge-sign-out-button {\n display: flex;\n align-items: center;\n justify-content: flex-start;\n gap: 0.5rem;\n width: 100%;\n padding: 0.5rem 0.75rem;\n font-size: 0.875rem;\n font-family: inherit;\n color: #dc2626;\n background: transparent;\n border: none;\n border-radius: 0.375rem;\n cursor: pointer;\n transition: background 0.2s;\n text-align: left;\n}\n\n.insforge-sign-out-button:hover {\n background: #fef2f2;\n}\n\n/* Loading State */\n.insforge-loading {\n display: flex;\n justify-content: center;\n align-items: center;\n padding: 2rem;\n color: #6b7280;\n font-size: 0.875rem;\n}\n\n/* Password Strength Indicator */\n.insforge-password-strength {\n display: flex;\n flex-direction: column;\n gap: 0.25rem;\n margin-top: 0.5rem;\n}\n\n.insforge-password-requirement {\n height: 1.5rem;\n display: flex;\n align-items: center;\n gap: 0.25rem;\n}\n\n.insforge-password-check {\n width: 1.25rem;\n height: 1.25rem;\n border-radius: 9999px;\n display: flex;\n border-width: 2px;\n border-style: solid;\n align-items: center;\n justify-content: center;\n transition: all 0.2s ease-in-out;\n background-color: transparent;\n border-color: #a3a3a3;\n flex-shrink: 0;\n}\n\n.insforge-password-check-valid {\n background-color: #22c55e;\n border-color: transparent;\n}\n\n.insforge-password-check-icon {\n color: #fff;\n stroke-width: 3;\n}\n\n.insforge-password-requirement-label {\n color:#000;\n font-family: Inter;\n font-size: 14px;\n font-style: normal;\n font-weight: 400;\n line-height: 24px;\n}\n\n/* Verification Code Input */\n.insforge-verification-code-container {\n display: flex;\n flex-direction: column;\n justify-content: center;\n align-items: center;\n gap: 24px;\n}\n\n.insforge-verification-instructions {\n color: #525252;\n font-family: Inter;\n font-size: 14px;\n font-style: normal;\n font-weight: 400;\n line-height: 20px;\n}\n\n.insforge-verification-instructions > span {\n color: #000;\n font-family: Inter;\n font-size: 14px;\n font-style: normal;\n font-weight: 600;\n line-height: 20px;\n}\n\n.insforge-verification-code-inputs {\n display: flex;\n flex-direction: row;\n gap: 12px;\n justify-content: center;\n align-items: center;\n}\n\n.insforge-verification-code-input {\n width: 100%;\n height: 48px;\n padding: 8px 12px;\n border-radius: 4px;\n border: 1px solid #E0E0E0;\n background: #FFF;\n text-align: center;\n font-size: 16px;\n font-style: normal;\n line-height: 20px;\n font-weight: 600;\n font-family: var(--font-manrope);\n color: #000;\n transition: all 0.2s ease-in-out;\n outline: none;\n}\n\n.insforge-verification-code-input:focus {\n border-color: #000;\n box-shadow: 0 0 0 2px rgba(0, 0, 0, 0.1);\n}\n\n.insforge-verification-code-input:disabled {\n background-color: #F5F5F5;\n cursor: not-allowed;\n opacity: 0.6;\n}\n\n/* Verification Instructions */\n.insforge-verification-instructions {\n color: #4F4F4F;\n font-family: Inter;\n font-size: 14px;\n font-style: normal;\n font-weight: 400;\n line-height: 24px;\n margin-bottom: 8px;\n}\n\n.insforge-verification-email {\n color: #000;\n font-weight: 600;\n}\n\n.insforge-resend-code {\n color: #525252;\n font-family: Inter;\n font-size: 14px;\n font-style: normal;\n font-weight: 400;\n line-height: 24px;\n text-align: center;\n}\n\n.insforge-resend-link {\n color: #000;\n font-family: Inter;\n font-size: 14px;\n font-style: normal;\n font-weight: 500;\n line-height: 24px;\n}\n\n.insforge-resend-link:hover {\n text-decoration: underline;\n}\n\n.insforge-resend-link:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n}\n\n/* Responsive adjustments */\n@media (max-width: 640px) {\n .insforge-auth-card {\n padding: 2rem 1.5rem;\n }\n\n .insforge-auth-title {\n font-size: 1.75rem;\n }\n\n .insforge-verification-code-container {\n gap: 8px;\n }\n\n .insforge-verification-code-input {\n width: 40px;\n height: 48px;\n font-size: 20px;\n }\n}\n\n";
10
+ if (document.head) {
11
+ document.head.appendChild(style);
12
+ }
13
+ }
14
+ }
15
+
16
+
3
17
  // src/provider/InsforgeProvider.tsx
4
18
  import { createContext, useContext, useEffect, useState, useCallback, useRef } from "react";
5
19
  import { createClient } from "@insforge/sdk";
@@ -59,9 +73,8 @@ function InsforgeProvider({
59
73
  const userData = {
60
74
  id: cachedData.user.id,
61
75
  email: cachedData.user.email,
62
- createdAt: cachedData.user.createdAt || (/* @__PURE__ */ new Date()).toISOString(),
63
- updatedAt: cachedData.user.updatedAt || (/* @__PURE__ */ new Date()).toISOString(),
64
- ...cachedData.profile
76
+ name: cachedData.profile?.nickname || "",
77
+ avatarUrl: cachedData.profile?.avatar_url || ""
65
78
  };
66
79
  setUser(userData);
67
80
  setSession({
@@ -88,9 +101,8 @@ function InsforgeProvider({
88
101
  const userData = {
89
102
  id: userResult.data.user.id,
90
103
  email: userResult.data.user.email,
91
- createdAt: userResult.data.user.createdAt || (/* @__PURE__ */ new Date()).toISOString(),
92
- updatedAt: userResult.data.user.updatedAt || (/* @__PURE__ */ new Date()).toISOString(),
93
- ...userResult.data.profile
104
+ name: userResult.data.profile?.nickname || "",
105
+ avatarUrl: userResult.data.profile?.avatar_url || ""
94
106
  };
95
107
  setUser(userData);
96
108
  setSession({
@@ -141,120 +153,90 @@ function InsforgeProvider({
141
153
  }
142
154
  };
143
155
  }, []);
156
+ const handleAuthSuccess = useCallback(
157
+ async (authToken, fallbackUser) => {
158
+ const userResult = await insforge.auth.getCurrentUser();
159
+ if (userResult.data) {
160
+ const userData = {
161
+ id: userResult.data.user.id,
162
+ email: userResult.data.user.email,
163
+ name: userResult.data.profile?.nickname || "",
164
+ avatarUrl: userResult.data.profile?.avatar_url || ""
165
+ };
166
+ const sessionData = {
167
+ userId: userResult.data.user.id,
168
+ token: authToken,
169
+ expiresAt: "",
170
+ createdAt: (/* @__PURE__ */ new Date()).toISOString()
171
+ };
172
+ setUser(userData);
173
+ setSession(sessionData);
174
+ localStorage.setItem("insforge-user-profile", JSON.stringify(userResult.data));
175
+ if (onAuthChange) {
176
+ onAuthChange(userData);
177
+ }
178
+ try {
179
+ await syncTokenToCookie(authToken);
180
+ } catch (error) {
181
+ }
182
+ } else if (fallbackUser) {
183
+ const userData = {
184
+ id: fallbackUser.id || "",
185
+ email: fallbackUser.email || "",
186
+ name: fallbackUser.name || "",
187
+ avatarUrl: ""
188
+ };
189
+ setUser(userData);
190
+ setSession({
191
+ userId: fallbackUser.id || "",
192
+ token: authToken,
193
+ expiresAt: "",
194
+ createdAt: (/* @__PURE__ */ new Date()).toISOString()
195
+ });
196
+ if (onAuthChange) {
197
+ onAuthChange(userData);
198
+ }
199
+ }
200
+ },
201
+ [insforge, onAuthChange]
202
+ );
144
203
  const signIn = useCallback(
145
204
  async (email, password) => {
146
205
  const sdkResult = await insforge.auth.signInWithPassword({ email, password });
147
206
  if (sdkResult.data) {
148
- const userResult = await insforge.auth.getCurrentUser();
149
- if (userResult.data) {
150
- const userData = {
151
- id: userResult.data.user.id,
152
- email: userResult.data.user.email,
153
- name: userResult.data.user.name || void 0,
154
- createdAt: userResult.data.user.createdAt,
155
- updatedAt: userResult.data.user.updatedAt,
156
- ...userResult.data.profile
157
- // Include profile fields (nickname, avatar_url, etc.)
158
- };
159
- const sessionData = {
160
- userId: userResult.data.user.id,
161
- token: sdkResult.data.accessToken || "",
162
- expiresAt: "",
163
- createdAt: (/* @__PURE__ */ new Date()).toISOString()
164
- };
165
- setUser(userData);
166
- setSession(sessionData);
167
- localStorage.setItem("insforge-user-profile", JSON.stringify(userResult.data));
168
- if (onAuthChange) {
169
- onAuthChange(userData);
170
- }
171
- try {
172
- await syncTokenToCookie(sdkResult.data.accessToken || "");
173
- } catch (error) {
174
- console.error("Please add /api/auth route to your server to sync token to cookie:", error);
175
- }
176
- } else {
177
- const userData = {
207
+ await handleAuthSuccess(
208
+ sdkResult.data.accessToken || "",
209
+ sdkResult.data.user ? {
178
210
  id: sdkResult.data.user.id,
179
211
  email: sdkResult.data.user.email,
180
- name: sdkResult.data.user.name || void 0,
181
- createdAt: sdkResult.data.user.createdAt,
182
- updatedAt: sdkResult.data.user.updatedAt
183
- };
184
- setUser(userData);
185
- setSession({
186
- userId: sdkResult.data.user.id,
187
- token: sdkResult.data.accessToken || "",
188
- expiresAt: "",
189
- createdAt: (/* @__PURE__ */ new Date()).toISOString()
190
- });
191
- if (onAuthChange) {
192
- onAuthChange(userData);
193
- }
194
- }
212
+ name: sdkResult.data.user.name
213
+ } : void 0
214
+ );
195
215
  } else {
196
216
  const errorMessage = sdkResult.error?.message || "Invalid email or password";
197
217
  throw new Error(errorMessage);
198
218
  }
199
219
  },
200
- [insforge, onAuthChange]
220
+ [insforge, handleAuthSuccess]
201
221
  );
202
222
  const signUp = useCallback(
203
223
  async (email, password) => {
204
224
  const sdkResult = await insforge.auth.signUp({ email, password });
205
225
  if (sdkResult.data) {
206
- const userResult = await insforge.auth.getCurrentUser();
207
- if (userResult.data) {
208
- const userData = {
209
- id: userResult.data.user.id,
210
- email: userResult.data.user.email,
211
- name: userResult.data.user.name || void 0,
212
- createdAt: userResult.data.user.createdAt,
213
- updatedAt: userResult.data.user.updatedAt,
214
- ...userResult.data.profile
215
- // Include profile fields (nickname, avatar_url, etc.)
216
- };
217
- const sessionData = {
218
- userId: userResult.data.user.id,
219
- token: sdkResult.data.accessToken || "",
220
- expiresAt: "",
221
- createdAt: (/* @__PURE__ */ new Date()).toISOString()
222
- };
223
- setUser(userData);
224
- setSession(sessionData);
225
- localStorage.setItem("insforge-user-profile", JSON.stringify(userResult.data));
226
- if (onAuthChange) {
227
- onAuthChange(userData);
228
- }
229
- try {
230
- await syncTokenToCookie(sdkResult.data.accessToken || "");
231
- } catch (error) {
232
- }
233
- } else {
234
- const userData = {
226
+ await handleAuthSuccess(
227
+ sdkResult.data.accessToken || "",
228
+ sdkResult.data.user ? {
235
229
  id: sdkResult.data.user.id,
236
230
  email: sdkResult.data.user.email,
237
- name: sdkResult.data.user.name || void 0,
238
- createdAt: sdkResult.data.user.createdAt,
239
- updatedAt: sdkResult.data.user.updatedAt
240
- };
241
- setUser(userData);
242
- setSession({
243
- userId: sdkResult.data.user.id,
244
- token: sdkResult.data.accessToken || "",
245
- expiresAt: "",
246
- createdAt: (/* @__PURE__ */ new Date()).toISOString()
247
- });
248
- if (onAuthChange) {
249
- onAuthChange(userData);
250
- }
251
- }
231
+ name: sdkResult.data.user.name
232
+ } : void 0
233
+ );
252
234
  } else {
253
235
  const errorMessage = sdkResult.error?.message || "Sign up failed";
254
236
  throw new Error(errorMessage);
255
237
  }
256
238
  },
257
- [insforge, onAuthChange]
239
+ [insforge, handleAuthSuccess]
258
240
  );
259
241
  const signOut = useCallback(async () => {
260
242
  await insforge.auth.signOut();
@@ -273,7 +255,10 @@ function InsforgeProvider({
273
255
  const updateUser = useCallback(
274
256
  async (data) => {
275
257
  if (!user) throw new Error("No user signed in");
276
- const result = await insforge.auth.setProfile(data);
258
+ const result = await insforge.auth.setProfile({
259
+ nickname: data.name || "",
260
+ avatar_url: data.avatarUrl || ""
261
+ });
277
262
  if (result.data) {
278
263
  const updatedUser = { ...user, ...result.data };
279
264
  setUser(updatedUser);
@@ -336,8 +321,8 @@ function useSession() {
336
321
  }
337
322
 
338
323
  // src/components/SignIn.tsx
339
- import { useState as useState4 } from "react";
340
- import { createClient as createClient3 } from "@insforge/sdk";
324
+ import { useState as useState5 } from "react";
325
+ import { createClient as createClient4 } from "@insforge/sdk";
341
326
 
342
327
  // src/hooks/useOAuthProviders.ts
343
328
  import { useState as useState2, useEffect as useEffect2 } from "react";
@@ -357,8 +342,8 @@ function useOAuthProviders() {
357
342
  console.warn("[useOAuthProviders] Failed to fetch OAuth providers:", error);
358
343
  setProviders([]);
359
344
  } else if (data) {
360
- const configuredProviders = data.filter((p) => p.isConfigured).map((p) => p.provider);
361
- setProviders(configuredProviders);
345
+ const providerNames = data.map((p) => p.provider);
346
+ setProviders(providerNames);
362
347
  } else {
363
348
  setProviders([]);
364
349
  }
@@ -379,6 +364,43 @@ function useOAuthProviders() {
379
364
  return { providers, isLoaded };
380
365
  }
381
366
 
367
+ // src/hooks/useEmailAuthConfig.ts
368
+ import { useState as useState3, useEffect as useEffect3 } from "react";
369
+ import { createClient as createClient3 } from "@insforge/sdk";
370
+ function useEmailAuthConfig() {
371
+ const { baseUrl } = useInsforge();
372
+ const [config, setConfig] = useState3(null);
373
+ const [isLoaded, setIsLoaded] = useState3(false);
374
+ useEffect3(() => {
375
+ let mounted = true;
376
+ async function fetchConfig() {
377
+ try {
378
+ const insforge = createClient3({ baseUrl });
379
+ const { data, error } = await insforge.auth.getEmailAuthConfig();
380
+ if (!mounted) return;
381
+ if (error) {
382
+ console.warn("[useEmailAuthConfig] Failed to fetch email auth config:", error);
383
+ setConfig(null);
384
+ } else {
385
+ setConfig(data);
386
+ }
387
+ setIsLoaded(true);
388
+ } catch (error) {
389
+ console.warn("[useEmailAuthConfig] Unexpected error:", error);
390
+ if (mounted) {
391
+ setConfig(null);
392
+ setIsLoaded(true);
393
+ }
394
+ }
395
+ }
396
+ fetchConfig();
397
+ return () => {
398
+ mounted = false;
399
+ };
400
+ }, [baseUrl]);
401
+ return { config, isLoaded };
402
+ }
403
+
382
404
  // src/components/auth/AuthBranding.tsx
383
405
  import Link from "next/link";
384
406
  import { jsx as jsx2, jsxs } from "react/jsx-runtime";
@@ -489,35 +511,59 @@ function AuthFormField({ label, id, className = "", ...props }) {
489
511
  }
490
512
 
491
513
  // src/components/auth/AuthPasswordField.tsx
492
- import { useState as useState3 } from "react";
514
+ import { useState as useState4 } from "react";
493
515
  import { Eye, EyeOff } from "lucide-react";
494
516
 
495
517
  // src/components/auth/AuthPasswordStrengthIndicator.tsx
496
518
  import { Check } from "lucide-react";
497
519
  import { jsx as jsx7, jsxs as jsxs6 } from "react/jsx-runtime";
498
- var requirements = [
499
- {
500
- label: "At least 1 Uppercase letter",
501
- test: (pwd) => /[A-Z]/.test(pwd)
502
- },
503
- {
504
- label: "At least 1 Number",
505
- test: (pwd) => /\d/.test(pwd)
506
- },
507
- {
508
- label: "Special character (e.g. !?<>@#$%)",
509
- test: (pwd) => /[!@#$%^&*()_+\-=[\]{};\\|,.<>/?]/.test(pwd)
510
- },
511
- {
512
- label: "8 characters or more",
513
- test: (pwd) => pwd.length >= 8
520
+ function createRequirements(config) {
521
+ const requirements = [];
522
+ const minLength = config.passwordMinLength;
523
+ const requireUppercase = config.requireUppercase;
524
+ const requireLowercase = config.requireLowercase;
525
+ const requireNumber = config.requireNumber;
526
+ const requireSpecialChar = config.requireSpecialChar;
527
+ if (requireUppercase) {
528
+ requirements.push({
529
+ label: "At least 1 Uppercase letter",
530
+ test: (pwd) => /[A-Z]/.test(pwd)
531
+ });
532
+ }
533
+ if (requireLowercase) {
534
+ requirements.push({
535
+ label: "At least 1 Lowercase letter",
536
+ test: (pwd) => /[a-z]/.test(pwd)
537
+ });
514
538
  }
515
- ];
516
- function validatePasswordStrength(password) {
539
+ if (requireNumber) {
540
+ requirements.push({
541
+ label: "At least 1 Number",
542
+ test: (pwd) => /\d/.test(pwd)
543
+ });
544
+ }
545
+ if (requireSpecialChar) {
546
+ requirements.push({
547
+ label: "Special character (e.g. !?<>@#$%)",
548
+ test: (pwd) => /[!@#$%^&*()_+\-=[\]{};\\|,.<>/?]/.test(pwd)
549
+ });
550
+ }
551
+ requirements.push({
552
+ label: `${minLength} characters or more`,
553
+ test: (pwd) => pwd.length >= minLength
554
+ });
555
+ return requirements;
556
+ }
557
+ function validatePasswordStrength(password, config) {
517
558
  if (!password) return false;
559
+ const requirements = createRequirements(config);
518
560
  return requirements.every((req) => req.test(password));
519
561
  }
520
- function AuthPasswordStrengthIndicator({ password }) {
562
+ function AuthPasswordStrengthIndicator({
563
+ password,
564
+ config
565
+ }) {
566
+ const requirements = createRequirements(config);
521
567
  return /* @__PURE__ */ jsx7("div", { className: "insforge-password-strength", children: requirements.map((requirement, index) => {
522
568
  const isValid = requirement.test(password);
523
569
  return /* @__PURE__ */ jsxs6("div", { className: "insforge-password-requirement", children: [
@@ -539,14 +585,15 @@ function AuthPasswordField({
539
585
  label,
540
586
  id,
541
587
  showStrengthIndicator = false,
588
+ emailAuthConfig,
542
589
  forgotPasswordLink,
543
590
  value,
544
591
  className = "",
545
592
  onFocus,
546
593
  ...props
547
594
  }) {
548
- const [showPassword, setShowPassword] = useState3(false);
549
- const [showStrength, setShowStrength] = useState3(false);
595
+ const [showPassword, setShowPassword] = useState4(false);
596
+ const [showStrength, setShowStrength] = useState4(false);
550
597
  const handleFocus = (e) => {
551
598
  if (showStrengthIndicator) {
552
599
  setShowStrength(true);
@@ -581,7 +628,13 @@ function AuthPasswordField({
581
628
  }
582
629
  )
583
630
  ] }),
584
- showStrengthIndicator && showStrength && /* @__PURE__ */ jsx8(AuthPasswordStrengthIndicator, { password: String(value || "") })
631
+ showStrengthIndicator && showStrength && /* @__PURE__ */ jsx8(
632
+ AuthPasswordStrengthIndicator,
633
+ {
634
+ password: String(value || ""),
635
+ config: emailAuthConfig
636
+ }
637
+ )
585
638
  ] });
586
639
  }
587
640
 
@@ -968,12 +1021,13 @@ function SignIn({
968
1021
  }) {
969
1022
  const { signIn, baseUrl } = useInsforge();
970
1023
  const { providers: oauthProviders } = useOAuthProviders();
971
- const [email, setEmail] = useState4("");
972
- const [password, setPassword] = useState4("");
973
- const [error, setError] = useState4("");
974
- const [loading, setLoading] = useState4(false);
975
- const [oauthLoading, setOauthLoading] = useState4(null);
976
- const insforge = useState4(() => createClient3({ baseUrl }))[0];
1024
+ const { config: emailAuthConfig } = useEmailAuthConfig();
1025
+ const [email, setEmail] = useState5("");
1026
+ const [password, setPassword] = useState5("");
1027
+ const [error, setError] = useState5("");
1028
+ const [loading, setLoading] = useState5(false);
1029
+ const [oauthLoading, setOauthLoading] = useState5(null);
1030
+ const insforge = useState5(() => createClient4({ baseUrl }))[0];
977
1031
  async function handleSubmit(e) {
978
1032
  e.preventDefault();
979
1033
  setLoading(true);
@@ -1035,7 +1089,15 @@ function SignIn({
1035
1089
  value: password,
1036
1090
  onChange: (e) => setPassword(e.target.value),
1037
1091
  required: true,
1038
- autoComplete: "current-password"
1092
+ autoComplete: "current-password",
1093
+ emailAuthConfig: emailAuthConfig || {
1094
+ requireEmailVerification: false,
1095
+ passwordMinLength: 6,
1096
+ requireNumber: false,
1097
+ requireLowercase: false,
1098
+ requireUppercase: false,
1099
+ requireSpecialChar: false
1100
+ }
1039
1101
  }
1040
1102
  ),
1041
1103
  /* @__PURE__ */ jsx16(
@@ -1065,8 +1127,8 @@ function SignIn({
1065
1127
  }
1066
1128
 
1067
1129
  // src/components/SignUp.tsx
1068
- import { useState as useState5 } from "react";
1069
- import { createClient as createClient4 } from "@insforge/sdk";
1130
+ import { useState as useState6 } from "react";
1131
+ import { createClient as createClient5 } from "@insforge/sdk";
1070
1132
  import { Fragment as Fragment2, jsx as jsx17, jsxs as jsxs14 } from "react/jsx-runtime";
1071
1133
  function SignUp({
1072
1134
  afterSignUpUrl = "/",
@@ -1091,17 +1153,18 @@ function SignUp({
1091
1153
  }) {
1092
1154
  const { signUp, baseUrl } = useInsforge();
1093
1155
  const { providers: oauthProviders } = useOAuthProviders();
1094
- const [email, setEmail] = useState5("");
1095
- const [password, setPassword] = useState5("");
1096
- const [error, setError] = useState5("");
1097
- const [loading, setLoading] = useState5(false);
1098
- const [oauthLoading, setOauthLoading] = useState5(null);
1099
- const insforge = useState5(() => createClient4({ baseUrl }))[0];
1156
+ const { config: emailAuthConfig } = useEmailAuthConfig();
1157
+ const [email, setEmail] = useState6("");
1158
+ const [password, setPassword] = useState6("");
1159
+ const [error, setError] = useState6("");
1160
+ const [loading, setLoading] = useState6(false);
1161
+ const [oauthLoading, setOauthLoading] = useState6(null);
1162
+ const insforge = useState6(() => createClient5({ baseUrl }))[0];
1100
1163
  async function handleCredentialsSubmit(e) {
1101
1164
  e.preventDefault();
1102
1165
  setLoading(true);
1103
1166
  setError("");
1104
- if (!validatePasswordStrength(password)) {
1167
+ if (emailAuthConfig && !validatePasswordStrength(password, emailAuthConfig)) {
1105
1168
  setError("Password does not meet all requirements");
1106
1169
  setLoading(false);
1107
1170
  return;
@@ -1166,9 +1229,17 @@ function SignUp({
1166
1229
  value: password,
1167
1230
  onChange: (e) => setPassword(e.target.value),
1168
1231
  required: true,
1169
- minLength: 8,
1232
+ minLength: emailAuthConfig?.passwordMinLength ?? 8,
1170
1233
  autoComplete: "new-password",
1171
- showStrengthIndicator: true
1234
+ showStrengthIndicator: true,
1235
+ emailAuthConfig: emailAuthConfig || {
1236
+ requireEmailVerification: false,
1237
+ passwordMinLength: 6,
1238
+ requireNumber: false,
1239
+ requireLowercase: false,
1240
+ requireUppercase: false,
1241
+ requireSpecialChar: false
1242
+ }
1172
1243
  }
1173
1244
  ),
1174
1245
  /* @__PURE__ */ jsx17(
@@ -1205,7 +1276,7 @@ function SignUp({
1205
1276
  }
1206
1277
 
1207
1278
  // src/components/UserButton.tsx
1208
- import { useState as useState6, useRef as useRef3, useEffect as useEffect3 } from "react";
1279
+ import { useState as useState7, useRef as useRef3, useEffect as useEffect4 } from "react";
1209
1280
  import { LogOut } from "lucide-react";
1210
1281
  import { jsx as jsx18, jsxs as jsxs15 } from "react/jsx-runtime";
1211
1282
  function UserButton({
@@ -1214,9 +1285,9 @@ function UserButton({
1214
1285
  appearance = {}
1215
1286
  }) {
1216
1287
  const { user, signOut } = useInsforge();
1217
- const [isOpen, setIsOpen] = useState6(false);
1288
+ const [isOpen, setIsOpen] = useState7(false);
1218
1289
  const dropdownRef = useRef3(null);
1219
- useEffect3(() => {
1290
+ useEffect4(() => {
1220
1291
  function handleClickOutside(event) {
1221
1292
  if (dropdownRef.current && !dropdownRef.current.contains(event.target)) {
1222
1293
  setIsOpen(false);
@@ -1281,7 +1352,7 @@ function SignedOut({ children }) {
1281
1352
  }
1282
1353
 
1283
1354
  // src/components/Protect.tsx
1284
- import { useEffect as useEffect4 } from "react";
1355
+ import { useEffect as useEffect5 } from "react";
1285
1356
  import { useRouter } from "next/navigation";
1286
1357
  import { Fragment as Fragment5, jsx as jsx21 } from "react/jsx-runtime";
1287
1358
  function Protect({
@@ -1292,7 +1363,7 @@ function Protect({
1292
1363
  }) {
1293
1364
  const { isSignedIn, isLoaded, user } = useInsforge();
1294
1365
  const router = useRouter();
1295
- useEffect4(() => {
1366
+ useEffect5(() => {
1296
1367
  if (isLoaded && !isSignedIn) {
1297
1368
  router.push(redirectTo);
1298
1369
  } else if (isLoaded && isSignedIn && condition && user) {