@githat/nextjs 0.2.1 → 0.2.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.js CHANGED
@@ -21,16 +21,21 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
21
21
  // src/index.ts
22
22
  var src_exports = {};
23
23
  __export(src_exports, {
24
+ ChangePasswordForm: () => ChangePasswordForm,
25
+ ForgotPasswordForm: () => ForgotPasswordForm,
24
26
  GitHatProvider: () => GitHatProvider,
25
27
  OrgSwitcher: () => OrgSwitcher,
26
28
  ProtectedRoute: () => ProtectedRoute,
29
+ ResetPasswordForm: () => ResetPasswordForm,
27
30
  SignInButton: () => SignInButton,
28
31
  SignInForm: () => SignInForm,
29
32
  SignUpButton: () => SignUpButton,
30
33
  SignUpForm: () => SignUpForm,
31
34
  UserButton: () => UserButton,
32
35
  VerifiedBadge: () => VerifiedBadge,
36
+ VerifyEmailStatus: () => VerifyEmailStatus,
33
37
  useAuth: () => useAuth,
38
+ useData: () => useData,
34
39
  useGitHat: () => useGitHat
35
40
  });
36
41
  module.exports = __toCommonJS(src_exports);
@@ -53,15 +58,16 @@ function resolveConfig(config) {
53
58
  signInUrl: config.signInUrl || "/sign-in",
54
59
  signUpUrl: config.signUpUrl || "/sign-up",
55
60
  afterSignInUrl: config.afterSignInUrl || "/dashboard",
56
- afterSignOutUrl: config.afterSignOutUrl || "/"
61
+ afterSignOutUrl: config.afterSignOutUrl || "/",
62
+ tokenStorage: config.tokenStorage || "localStorage"
57
63
  };
58
64
  }
59
65
 
60
66
  // src/client.ts
61
67
  var _refreshPromise = null;
62
- async function refreshTokens(apiUrl, appKey) {
63
- const refreshToken = typeof window !== "undefined" ? localStorage.getItem(TOKEN_KEYS.refreshToken) : null;
64
- if (!refreshToken) return false;
68
+ async function refreshTokens(apiUrl, appKey, useCookies) {
69
+ const refreshToken = typeof window !== "undefined" && !useCookies ? localStorage.getItem(TOKEN_KEYS.refreshToken) : null;
70
+ if (!useCookies && !refreshToken) return false;
65
71
  let orgId = null;
66
72
  try {
67
73
  const orgStr = localStorage.getItem(TOKEN_KEYS.org);
@@ -69,18 +75,22 @@ async function refreshTokens(apiUrl, appKey) {
69
75
  } catch {
70
76
  }
71
77
  try {
72
- const res = await fetch(`${apiUrl}/auth/refresh`, {
78
+ const refreshUrl = useCookies ? `${apiUrl}/auth/refresh?setCookie=true` : `${apiUrl}/auth/refresh`;
79
+ const res = await fetch(refreshUrl, {
73
80
  method: "POST",
74
81
  headers: {
75
82
  "Content-Type": "application/json",
76
83
  "X-GitHat-App-Key": appKey
77
84
  },
78
- body: JSON.stringify({ refreshToken, orgId })
85
+ credentials: useCookies ? "include" : "same-origin",
86
+ body: JSON.stringify(useCookies ? { orgId } : { refreshToken, orgId })
79
87
  });
80
88
  if (!res.ok) return false;
81
89
  const data = await res.json();
82
- if (data.accessToken) localStorage.setItem(TOKEN_KEYS.accessToken, data.accessToken);
83
- if (data.refreshToken) localStorage.setItem(TOKEN_KEYS.refreshToken, data.refreshToken);
90
+ if (!useCookies) {
91
+ if (data.accessToken) localStorage.setItem(TOKEN_KEYS.accessToken, data.accessToken);
92
+ if (data.refreshToken) localStorage.setItem(TOKEN_KEYS.refreshToken, data.refreshToken);
93
+ }
84
94
  if (data.org) localStorage.setItem(TOKEN_KEYS.org, JSON.stringify(data.org));
85
95
  return true;
86
96
  } catch {
@@ -90,53 +100,66 @@ async function refreshTokens(apiUrl, appKey) {
90
100
  function clearAuth() {
91
101
  if (typeof window === "undefined") return;
92
102
  Object.values(TOKEN_KEYS).forEach((key) => localStorage.removeItem(key));
93
- window.dispatchEvent(new CustomEvent("githat:auth-changed", {
94
- detail: { user: null, org: null, signedIn: false }
95
- }));
103
+ window.dispatchEvent(
104
+ new CustomEvent("githat:auth-changed", {
105
+ detail: { user: null, org: null, signedIn: false }
106
+ })
107
+ );
96
108
  }
97
- function createClient(apiUrl, appKey) {
98
- async function fetchApi(endpoint, options = {}) {
109
+ function createClient(apiUrl, appKey, options = {}) {
110
+ const { useCookies = false } = options;
111
+ async function fetchApi(endpoint, fetchOptions = {}) {
99
112
  const url = `${apiUrl}${endpoint}`;
100
- const token = typeof window !== "undefined" ? localStorage.getItem(TOKEN_KEYS.accessToken) : null;
113
+ const token = typeof window !== "undefined" && !useCookies ? localStorage.getItem(TOKEN_KEYS.accessToken) : null;
101
114
  const headers = {
102
115
  "Content-Type": "application/json",
103
116
  "X-GitHat-App-Key": appKey,
104
117
  ...token && { Authorization: `Bearer ${token}` },
105
- ...options.headers
118
+ ...fetchOptions.headers
106
119
  };
107
120
  let response;
108
121
  try {
109
- response = await fetch(url, { ...options, headers });
122
+ response = await fetch(url, {
123
+ ...fetchOptions,
124
+ headers,
125
+ credentials: useCookies ? "include" : "same-origin"
126
+ });
110
127
  } catch (networkError) {
111
128
  if (networkError instanceof TypeError) {
112
- const isMissingKey = !appKey || !appKey.startsWith("pk_live_");
129
+ const isMissingKey = !appKey || !appKey.startsWith("pk_live_") && !appKey.startsWith("pk_test_");
113
130
  const isLocalhost = typeof window !== "undefined" && (window.location.hostname === "localhost" || window.location.hostname === "127.0.0.1");
114
131
  if (isMissingKey && !isLocalhost) {
115
132
  throw new Error(
116
- "Missing GitHat API key. Add NEXT_PUBLIC_GITHAT_PUBLISHABLE_KEY to .env.local"
133
+ "GitHat: Missing or invalid publishable key. Add NEXT_PUBLIC_GITHAT_PUBLISHABLE_KEY to your environment variables. Get your key at https://githat.io/dashboard/apps"
134
+ );
135
+ }
136
+ if (isLocalhost) {
137
+ throw new Error(
138
+ "GitHat: Cannot reach api.githat.io. Check your internet connection."
117
139
  );
118
140
  }
119
141
  throw new Error(
120
- "Unable to connect to GitHat API. Check your network connection."
142
+ "GitHat: API request failed. Verify your publishable key and app domain at https://githat.io/dashboard/apps"
121
143
  );
122
144
  }
123
145
  throw networkError;
124
146
  }
125
147
  if (response.status === 401) {
126
148
  if (!_refreshPromise) {
127
- _refreshPromise = refreshTokens(apiUrl, appKey).finally(() => {
149
+ _refreshPromise = refreshTokens(apiUrl, appKey, useCookies).finally(() => {
128
150
  _refreshPromise = null;
129
151
  });
130
152
  }
131
153
  const refreshed = await _refreshPromise;
132
154
  if (refreshed) {
133
- const newToken = localStorage.getItem(TOKEN_KEYS.accessToken);
155
+ const newToken = !useCookies && typeof window !== "undefined" ? localStorage.getItem(TOKEN_KEYS.accessToken) : null;
134
156
  const retryResponse = await fetch(url, {
135
- ...options,
157
+ ...fetchOptions,
136
158
  headers: {
137
159
  ...headers,
138
160
  ...newToken && { Authorization: `Bearer ${newToken}` }
139
- }
161
+ },
162
+ credentials: useCookies ? "include" : "same-origin"
140
163
  });
141
164
  const retryData = await retryResponse.json();
142
165
  if (!retryResponse.ok) throw new Error(retryData.error || "Request failed");
@@ -155,40 +178,108 @@ function createClient(apiUrl, appKey) {
155
178
  // src/provider.tsx
156
179
  var import_jsx_runtime = require("react/jsx-runtime");
157
180
  var GitHatContext = (0, import_react.createContext)(null);
181
+ function isDevMode(key) {
182
+ return !key || !key.startsWith("pk_live_") && !key.startsWith("pk_test_");
183
+ }
184
+ function DevModeBanner() {
185
+ const [dismissed, setDismissed] = (0, import_react.useState)(() => {
186
+ if (typeof window === "undefined") return true;
187
+ return localStorage.getItem("githat_dev_banner_dismissed") === "1";
188
+ });
189
+ if (dismissed || typeof window === "undefined") return null;
190
+ const hostname = window.location.hostname;
191
+ if (hostname !== "localhost" && hostname !== "127.0.0.1") return null;
192
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "githat-dev-banner", role: "status", children: [
193
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { children: [
194
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("strong", { children: "GitHat Dev Mode" }),
195
+ " \u2014 No publishable key. Auth works on localhost.",
196
+ " ",
197
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("a", { href: "https://githat.io/dashboard/apps", target: "_blank", rel: "noopener noreferrer", children: "Get your key" })
198
+ ] }),
199
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
200
+ "button",
201
+ {
202
+ onClick: () => {
203
+ setDismissed(true);
204
+ localStorage.setItem("githat_dev_banner_dismissed", "1");
205
+ },
206
+ "aria-label": "Dismiss",
207
+ children: "\xD7"
208
+ }
209
+ )
210
+ ] });
211
+ }
158
212
  function GitHatProvider({ config: rawConfig, children }) {
159
213
  const config = (0, import_react.useMemo)(() => resolveConfig(rawConfig), [rawConfig]);
160
- const clientRef = (0, import_react.useRef)(createClient(config.apiUrl, config.publishableKey));
214
+ const useCookies = config.tokenStorage === "cookie";
215
+ const devMode = isDevMode(config.publishableKey);
216
+ const clientRef = (0, import_react.useRef)(createClient(config.apiUrl, config.publishableKey, { useCookies }));
217
+ (0, import_react.useEffect)(() => {
218
+ if (!devMode || typeof window === "undefined") return;
219
+ const hostname = window.location.hostname;
220
+ if (hostname === "localhost" || hostname === "127.0.0.1") {
221
+ console.warn(
222
+ "%c GitHat Dev Mode %c No publishable key configured. Auth works on localhost but will fail in production. Get your key at https://githat.io/dashboard/apps",
223
+ "background: #f59e0b; color: #000; font-weight: bold; padding: 2px 6px; border-radius: 3px;",
224
+ "color: #f59e0b;"
225
+ );
226
+ } else {
227
+ console.error(
228
+ "%c GitHat %c Missing publishable key. Auth requests will fail. Add NEXT_PUBLIC_GITHAT_PUBLISHABLE_KEY to your environment. Get your key at https://githat.io/dashboard/apps",
229
+ "background: #ef4444; color: #fff; font-weight: bold; padding: 2px 6px; border-radius: 3px;",
230
+ "color: #ef4444;"
231
+ );
232
+ }
233
+ }, []);
161
234
  const [user, setUser] = (0, import_react.useState)(null);
162
235
  const [org, setOrg] = (0, import_react.useState)(null);
163
236
  const [isSignedIn, setIsSignedIn] = (0, import_react.useState)(false);
164
237
  const [isLoading, setIsLoading] = (0, import_react.useState)(true);
165
238
  const [authError, setAuthError] = (0, import_react.useState)(null);
166
239
  (0, import_react.useEffect)(() => {
167
- const token = localStorage.getItem(TOKEN_KEYS.accessToken);
168
- const storedUser = localStorage.getItem(TOKEN_KEYS.user);
169
- if (token && storedUser) {
170
- clientRef.current.fetchApi("/auth/me").then((data) => {
171
- const u = data.user || JSON.parse(storedUser);
172
- setUser(u);
173
- const storedOrg = localStorage.getItem(TOKEN_KEYS.org);
174
- setOrg(data.currentOrg || (storedOrg ? JSON.parse(storedOrg) : null));
175
- setIsSignedIn(true);
176
- setAuthError(null);
177
- }).catch((err) => {
178
- if (err.message === "Session expired") {
240
+ const validateSession = async () => {
241
+ try {
242
+ if (!useCookies) {
243
+ const token = localStorage.getItem(TOKEN_KEYS.accessToken);
244
+ const storedUser = localStorage.getItem(TOKEN_KEYS.user);
245
+ if (!token || !storedUser) {
246
+ setIsLoading(false);
247
+ return;
248
+ }
249
+ }
250
+ const data = await clientRef.current.fetchApi("/auth/me");
251
+ if (data.user) {
252
+ setUser(data.user);
253
+ setOrg(data.currentOrg || null);
254
+ setIsSignedIn(true);
255
+ setAuthError(null);
256
+ if (!useCookies) {
257
+ localStorage.setItem(TOKEN_KEYS.user, JSON.stringify(data.user));
258
+ if (data.currentOrg) {
259
+ localStorage.setItem(TOKEN_KEYS.org, JSON.stringify(data.currentOrg));
260
+ }
261
+ }
262
+ }
263
+ } catch (err) {
264
+ const error = err;
265
+ if (error.message === "Session expired") {
179
266
  clientRef.current.clearAuth();
180
- } else {
181
- try {
182
- setUser(JSON.parse(storedUser));
183
- } catch {
267
+ } else if (!useCookies) {
268
+ const storedUser = localStorage.getItem(TOKEN_KEYS.user);
269
+ if (storedUser) {
270
+ try {
271
+ setUser(JSON.parse(storedUser));
272
+ setIsSignedIn(true);
273
+ } catch {
274
+ }
184
275
  }
185
- setAuthError(err.message || "Failed to verify session");
276
+ setAuthError(error.message || "Failed to verify session");
186
277
  }
187
- }).finally(() => setIsLoading(false));
188
- } else {
278
+ }
189
279
  setIsLoading(false);
190
- }
191
- }, []);
280
+ };
281
+ validateSession();
282
+ }, [useCookies]);
192
283
  (0, import_react.useEffect)(() => {
193
284
  const handleAuthChanged = (e) => {
194
285
  const detail = e.detail;
@@ -206,30 +297,36 @@ function GitHatProvider({ config: rawConfig, children }) {
206
297
  return () => window.removeEventListener("githat:auth-changed", handleAuthChanged);
207
298
  }, []);
208
299
  const signIn = (0, import_react.useCallback)(async (email, password) => {
209
- const data = await clientRef.current.fetchApi("/auth/login", {
300
+ const loginUrl = useCookies ? "/auth/login?setCookie=true" : "/auth/login";
301
+ const data = await clientRef.current.fetchApi(loginUrl, {
210
302
  method: "POST",
211
303
  body: JSON.stringify({ email, password })
212
304
  });
213
- localStorage.setItem(TOKEN_KEYS.accessToken, data.accessToken);
214
- localStorage.setItem(TOKEN_KEYS.refreshToken, data.refreshToken);
215
- localStorage.setItem(TOKEN_KEYS.user, JSON.stringify(data.user));
216
- if (data.org) localStorage.setItem(TOKEN_KEYS.org, JSON.stringify(data.org));
305
+ if (!useCookies && data.accessToken && data.refreshToken) {
306
+ localStorage.setItem(TOKEN_KEYS.accessToken, data.accessToken);
307
+ localStorage.setItem(TOKEN_KEYS.refreshToken, data.refreshToken);
308
+ localStorage.setItem(TOKEN_KEYS.user, JSON.stringify(data.user));
309
+ if (data.org) localStorage.setItem(TOKEN_KEYS.org, JSON.stringify(data.org));
310
+ }
217
311
  setUser(data.user);
218
312
  setOrg(data.org || null);
219
313
  setIsSignedIn(true);
220
314
  window.dispatchEvent(new CustomEvent("githat:auth-changed", {
221
315
  detail: { user: data.user, org: data.org, signedIn: true }
222
316
  }));
223
- }, []);
317
+ }, [useCookies]);
224
318
  const signUp = (0, import_react.useCallback)(async (signUpData) => {
225
- const data = await clientRef.current.fetchApi("/auth/register", {
319
+ const registerUrl = useCookies ? "/auth/register?setCookie=true" : "/auth/register";
320
+ const data = await clientRef.current.fetchApi(registerUrl, {
226
321
  method: "POST",
227
322
  body: JSON.stringify(signUpData)
228
323
  });
229
- localStorage.setItem(TOKEN_KEYS.accessToken, data.accessToken);
230
- localStorage.setItem(TOKEN_KEYS.refreshToken, data.refreshToken);
231
- localStorage.setItem(TOKEN_KEYS.user, JSON.stringify(data.user));
232
- if (data.org) localStorage.setItem(TOKEN_KEYS.org, JSON.stringify(data.org));
324
+ if (!useCookies && data.accessToken && data.refreshToken) {
325
+ localStorage.setItem(TOKEN_KEYS.accessToken, data.accessToken);
326
+ localStorage.setItem(TOKEN_KEYS.refreshToken, data.refreshToken);
327
+ localStorage.setItem(TOKEN_KEYS.user, JSON.stringify(data.user));
328
+ if (data.org) localStorage.setItem(TOKEN_KEYS.org, JSON.stringify(data.org));
329
+ }
233
330
  setUser(data.user);
234
331
  setOrg(data.org || null);
235
332
  setIsSignedIn(true);
@@ -237,10 +334,11 @@ function GitHatProvider({ config: rawConfig, children }) {
237
334
  detail: { user: data.user, org: data.org, signedIn: true }
238
335
  }));
239
336
  return { requiresVerification: !data.user.emailVerified, email: signUpData.email };
240
- }, []);
337
+ }, [useCookies]);
241
338
  const signOut = (0, import_react.useCallback)(async () => {
242
339
  try {
243
- await clientRef.current.fetchApi("/auth/logout", { method: "POST" });
340
+ const logoutUrl = useCookies ? "/auth/logout?setCookie=true" : "/auth/logout";
341
+ await clientRef.current.fetchApi(logoutUrl, { method: "POST" });
244
342
  } catch {
245
343
  }
246
344
  clientRef.current.clearAuth();
@@ -250,22 +348,24 @@ function GitHatProvider({ config: rawConfig, children }) {
250
348
  if (typeof window !== "undefined" && config.afterSignOutUrl) {
251
349
  window.location.href = config.afterSignOutUrl;
252
350
  }
253
- }, [config.afterSignOutUrl]);
351
+ }, [config.afterSignOutUrl, useCookies]);
254
352
  const switchOrg = (0, import_react.useCallback)(async (orgId) => {
255
353
  try {
256
- const data = await clientRef.current.fetchApi(`/user/orgs/${orgId}/switch`, { method: "POST" });
257
- if (data.accessToken) localStorage.setItem(TOKEN_KEYS.accessToken, data.accessToken);
258
- if (data.refreshToken) localStorage.setItem(TOKEN_KEYS.refreshToken, data.refreshToken);
259
- const orgData = data.org;
260
- localStorage.setItem(TOKEN_KEYS.org, JSON.stringify(orgData));
261
- setOrg(orgData);
354
+ const switchUrl = useCookies ? `/user/orgs/${orgId}/switch?setCookie=true` : `/user/orgs/${orgId}/switch`;
355
+ const data = await clientRef.current.fetchApi(switchUrl, { method: "POST" });
356
+ if (!useCookies) {
357
+ if (data.accessToken) localStorage.setItem(TOKEN_KEYS.accessToken, data.accessToken);
358
+ if (data.refreshToken) localStorage.setItem(TOKEN_KEYS.refreshToken, data.refreshToken);
359
+ localStorage.setItem(TOKEN_KEYS.org, JSON.stringify(data.org));
360
+ }
361
+ setOrg(data.org);
262
362
  window.dispatchEvent(new CustomEvent("githat:auth-changed", {
263
- detail: { user, org: orgData, signedIn: true }
363
+ detail: { user, org: data.org, signedIn: true }
264
364
  }));
265
365
  } catch (e) {
266
366
  console.error("Org switch failed:", e);
267
367
  }
268
- }, [user]);
368
+ }, [user, useCookies]);
269
369
  const value = (0, import_react.useMemo)(() => ({
270
370
  user,
271
371
  org,
@@ -278,7 +378,10 @@ function GitHatProvider({ config: rawConfig, children }) {
278
378
  signOut,
279
379
  switchOrg
280
380
  }), [user, org, isSignedIn, isLoading, authError, config, signIn, signUp, signOut, switchOrg]);
281
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(GitHatContext.Provider, { value, children });
381
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(GitHatContext.Provider, { value, children: [
382
+ devMode && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(DevModeBanner, {}),
383
+ children
384
+ ] });
282
385
  }
283
386
 
284
387
  // src/hooks.ts
@@ -294,23 +397,205 @@ function useGitHat() {
294
397
  () => createClient(ctx.config.apiUrl, ctx.config.publishableKey),
295
398
  [ctx.config.apiUrl, ctx.config.publishableKey]
296
399
  );
400
+ const getOrgMetadata = (0, import_react2.useCallback)(async () => {
401
+ if (!ctx.org?.id) {
402
+ throw new Error("No active organization");
403
+ }
404
+ const response = await client.fetchApi(
405
+ `/orgs/${ctx.org.id}/metadata`
406
+ );
407
+ return response.metadata || {};
408
+ }, [client, ctx.org?.id]);
409
+ const updateOrgMetadata = (0, import_react2.useCallback)(
410
+ async (updates) => {
411
+ if (!ctx.org?.id) {
412
+ throw new Error("No active organization");
413
+ }
414
+ const response = await client.fetchApi(
415
+ `/orgs/${ctx.org.id}/metadata`,
416
+ {
417
+ method: "PATCH",
418
+ body: JSON.stringify(updates)
419
+ }
420
+ );
421
+ return response.metadata || {};
422
+ },
423
+ [client, ctx.org?.id]
424
+ );
425
+ const forgotPassword = (0, import_react2.useCallback)(
426
+ async (email) => {
427
+ const response = await fetch(`${ctx.config.apiUrl}/auth/forgot-password`, {
428
+ method: "POST",
429
+ headers: { "Content-Type": "application/json" },
430
+ body: JSON.stringify({ email })
431
+ });
432
+ if (!response.ok) {
433
+ const error = await response.json().catch(() => ({}));
434
+ throw new Error(error.message || "Failed to send reset email");
435
+ }
436
+ return { success: true };
437
+ },
438
+ [ctx.config.apiUrl]
439
+ );
440
+ const resetPassword = (0, import_react2.useCallback)(
441
+ async (token, newPassword) => {
442
+ const response = await fetch(`${ctx.config.apiUrl}/auth/reset-password`, {
443
+ method: "POST",
444
+ headers: { "Content-Type": "application/json" },
445
+ body: JSON.stringify({ token, password: newPassword })
446
+ });
447
+ if (!response.ok) {
448
+ const error = await response.json().catch(() => ({}));
449
+ throw new Error(error.message || "Failed to reset password");
450
+ }
451
+ return { success: true };
452
+ },
453
+ [ctx.config.apiUrl]
454
+ );
455
+ const changePassword = (0, import_react2.useCallback)(
456
+ async (currentPassword, newPassword) => {
457
+ await client.fetchApi("/auth/change-password", {
458
+ method: "POST",
459
+ body: JSON.stringify({ currentPassword, newPassword })
460
+ });
461
+ return { success: true };
462
+ },
463
+ [client]
464
+ );
465
+ const verifyEmail = (0, import_react2.useCallback)(
466
+ async (token) => {
467
+ const response = await fetch(`${ctx.config.apiUrl}/auth/verify-email`, {
468
+ method: "POST",
469
+ headers: { "Content-Type": "application/json" },
470
+ body: JSON.stringify({ token })
471
+ });
472
+ if (!response.ok) {
473
+ const error = await response.json().catch(() => ({}));
474
+ throw new Error(error.message || "Failed to verify email");
475
+ }
476
+ return { success: true };
477
+ },
478
+ [ctx.config.apiUrl]
479
+ );
480
+ const resendVerificationEmail = (0, import_react2.useCallback)(
481
+ async (email) => {
482
+ const response = await fetch(`${ctx.config.apiUrl}/auth/resend-verification`, {
483
+ method: "POST",
484
+ headers: { "Content-Type": "application/json" },
485
+ body: JSON.stringify({ email })
486
+ });
487
+ if (!response.ok) {
488
+ const error = await response.json().catch(() => ({}));
489
+ throw new Error(error.message || "Failed to resend verification email");
490
+ }
491
+ return { success: true };
492
+ },
493
+ [ctx.config.apiUrl]
494
+ );
297
495
  return {
298
496
  fetch: client.fetchApi,
299
497
  getUserOrgs: () => client.fetchApi("/user/orgs"),
300
498
  verifyMCP: (domain) => client.fetchApi(`/verify/mcp/${domain}`),
301
- verifyAgent: (wallet) => client.fetchApi(`/verify/agent/${wallet}`)
499
+ verifyAgent: (wallet) => client.fetchApi(`/verify/agent/${wallet}`),
500
+ getOrgMetadata,
501
+ updateOrgMetadata,
502
+ // Password management
503
+ forgotPassword,
504
+ resetPassword,
505
+ changePassword,
506
+ // Email verification
507
+ verifyEmail,
508
+ resendVerificationEmail
302
509
  };
303
510
  }
304
511
 
305
- // src/components/SignInForm.tsx
512
+ // src/data.ts
306
513
  var import_react3 = require("react");
514
+ function useData() {
515
+ const ctx = useAuth();
516
+ const client = (0, import_react3.useMemo)(
517
+ () => createClient(ctx.config.apiUrl, ctx.config.publishableKey),
518
+ [ctx.config.apiUrl, ctx.config.publishableKey]
519
+ );
520
+ return (0, import_react3.useMemo)(() => ({
521
+ /**
522
+ * Store an item in a collection. If the item exists, it will be updated.
523
+ * @param collection - Collection name (e.g., 'orders', 'users')
524
+ * @param data - Data object with required `id` field
525
+ */
526
+ put: async (collection, data) => {
527
+ if (!data.id) {
528
+ throw new Error('Data must include an "id" field');
529
+ }
530
+ return client.fetchApi(`/data/${collection}/${data.id}`, {
531
+ method: "PUT",
532
+ body: JSON.stringify(data)
533
+ });
534
+ },
535
+ /**
536
+ * Get a single item from a collection.
537
+ * @param collection - Collection name
538
+ * @param id - Item ID
539
+ */
540
+ get: async (collection, id) => {
541
+ try {
542
+ const result = await client.fetchApi(`/data/${collection}/${id}`);
543
+ return result.item;
544
+ } catch (err) {
545
+ if (err instanceof Error && err.message === "Item not found") {
546
+ return null;
547
+ }
548
+ throw err;
549
+ }
550
+ },
551
+ /**
552
+ * Query items from a collection with optional filters and pagination.
553
+ * @param collection - Collection name
554
+ * @param options - Query options (limit, cursor, filter)
555
+ */
556
+ query: async (collection, options = {}) => {
557
+ const params = new URLSearchParams();
558
+ if (options.limit) params.set("limit", options.limit.toString());
559
+ if (options.cursor) params.set("cursor", options.cursor);
560
+ if (options.filter) params.set("filter", JSON.stringify(options.filter));
561
+ const queryString = params.toString();
562
+ const url = `/data/${collection}${queryString ? `?${queryString}` : ""}`;
563
+ return client.fetchApi(url);
564
+ },
565
+ /**
566
+ * Delete an item from a collection.
567
+ * @param collection - Collection name
568
+ * @param id - Item ID
569
+ */
570
+ remove: async (collection, id) => {
571
+ return client.fetchApi(`/data/${collection}/${id}`, {
572
+ method: "DELETE"
573
+ });
574
+ },
575
+ /**
576
+ * Batch operations (put/delete) on a collection.
577
+ * Maximum 100 operations per request.
578
+ * @param collection - Collection name
579
+ * @param operations - Array of operations
580
+ */
581
+ batch: async (collection, operations) => {
582
+ return client.fetchApi(`/data/${collection}/batch`, {
583
+ method: "POST",
584
+ body: JSON.stringify({ operations })
585
+ });
586
+ }
587
+ }), [client]);
588
+ }
589
+
590
+ // src/components/SignInForm.tsx
591
+ var import_react4 = require("react");
307
592
  var import_jsx_runtime2 = require("react/jsx-runtime");
308
593
  function SignInForm({ onSuccess, signUpUrl, forgotPasswordUrl }) {
309
594
  const { signIn, config } = useAuth();
310
- const [email, setEmail] = (0, import_react3.useState)("");
311
- const [password, setPassword] = (0, import_react3.useState)("");
312
- const [error, setError] = (0, import_react3.useState)("");
313
- const [loading, setLoading] = (0, import_react3.useState)(false);
595
+ const [email, setEmail] = (0, import_react4.useState)("");
596
+ const [password, setPassword] = (0, import_react4.useState)("");
597
+ const [error, setError] = (0, import_react4.useState)("");
598
+ const [loading, setLoading] = (0, import_react4.useState)(false);
314
599
  const emailValid = /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);
315
600
  const handleSubmit = async (e) => {
316
601
  e.preventDefault();
@@ -396,15 +681,15 @@ function SignInForm({ onSuccess, signUpUrl, forgotPasswordUrl }) {
396
681
  }
397
682
 
398
683
  // src/components/SignUpForm.tsx
399
- var import_react4 = require("react");
684
+ var import_react5 = require("react");
400
685
  var import_jsx_runtime3 = require("react/jsx-runtime");
401
686
  function SignUpForm({ onSuccess, signInUrl }) {
402
687
  const { signUp, config } = useAuth();
403
- const [name, setName] = (0, import_react4.useState)("");
404
- const [email, setEmail] = (0, import_react4.useState)("");
405
- const [password, setPassword] = (0, import_react4.useState)("");
406
- const [error, setError] = (0, import_react4.useState)("");
407
- const [loading, setLoading] = (0, import_react4.useState)(false);
688
+ const [name, setName] = (0, import_react5.useState)("");
689
+ const [email, setEmail] = (0, import_react5.useState)("");
690
+ const [password, setPassword] = (0, import_react5.useState)("");
691
+ const [error, setError] = (0, import_react5.useState)("");
692
+ const [loading, setLoading] = (0, import_react5.useState)(false);
408
693
  const emailValid = /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);
409
694
  const passwordValid = password.length >= 8 && /[A-Z]/.test(password) && /[a-z]/.test(password) && /\d/.test(password);
410
695
  const handleSubmit = async (e) => {
@@ -510,31 +795,31 @@ function SignUpForm({ onSuccess, signInUrl }) {
510
795
  }
511
796
 
512
797
  // src/components/SignInButton.tsx
513
- var import_react5 = require("react");
798
+ var import_react6 = require("react");
514
799
  var import_jsx_runtime4 = require("react/jsx-runtime");
515
800
  function SignInButton({ className, children, href }) {
516
- const ctx = (0, import_react5.useContext)(GitHatContext);
801
+ const ctx = (0, import_react6.useContext)(GitHatContext);
517
802
  const url = href || ctx?.config.signInUrl || "/sign-in";
518
803
  return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("a", { href: url, className: className || "githat-button githat-button-primary", "aria-label": "Sign in", children: children || "Sign in" });
519
804
  }
520
805
 
521
806
  // src/components/SignUpButton.tsx
522
- var import_react6 = require("react");
807
+ var import_react7 = require("react");
523
808
  var import_jsx_runtime5 = require("react/jsx-runtime");
524
809
  function SignUpButton({ className, children, href }) {
525
- const ctx = (0, import_react6.useContext)(GitHatContext);
810
+ const ctx = (0, import_react7.useContext)(GitHatContext);
526
811
  const url = href || ctx?.config.signUpUrl || "/sign-up";
527
812
  return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("a", { href: url, className: className || "githat-button githat-button-outline", "aria-label": "Sign up", children: children || "Sign up" });
528
813
  }
529
814
 
530
815
  // src/components/UserButton.tsx
531
- var import_react7 = require("react");
816
+ var import_react8 = require("react");
532
817
  var import_jsx_runtime6 = require("react/jsx-runtime");
533
818
  function UserButton() {
534
819
  const { user, org, isSignedIn, signOut } = useAuth();
535
- const [open, setOpen] = (0, import_react7.useState)(false);
536
- const ref = (0, import_react7.useRef)(null);
537
- (0, import_react7.useEffect)(() => {
820
+ const [open, setOpen] = (0, import_react8.useState)(false);
821
+ const ref = (0, import_react8.useRef)(null);
822
+ (0, import_react8.useEffect)(() => {
538
823
  const handleClickOutside = (e) => {
539
824
  if (ref.current && !ref.current.contains(e.target)) setOpen(false);
540
825
  };
@@ -561,23 +846,23 @@ function UserButton() {
561
846
  }
562
847
 
563
848
  // src/components/OrgSwitcher.tsx
564
- var import_react8 = require("react");
849
+ var import_react9 = require("react");
565
850
  var import_jsx_runtime7 = require("react/jsx-runtime");
566
851
  function OrgSwitcher() {
567
852
  const { org, isSignedIn, switchOrg } = useAuth();
568
853
  const githat = useGitHat();
569
- const [orgs, setOrgs] = (0, import_react8.useState)([]);
570
- const [orgsLoading, setOrgsLoading] = (0, import_react8.useState)(false);
571
- const [open, setOpen] = (0, import_react8.useState)(false);
572
- const ref = (0, import_react8.useRef)(null);
573
- (0, import_react8.useEffect)(() => {
854
+ const [orgs, setOrgs] = (0, import_react9.useState)([]);
855
+ const [orgsLoading, setOrgsLoading] = (0, import_react9.useState)(false);
856
+ const [open, setOpen] = (0, import_react9.useState)(false);
857
+ const ref = (0, import_react9.useRef)(null);
858
+ (0, import_react9.useEffect)(() => {
574
859
  if (isSignedIn) {
575
860
  setOrgsLoading(true);
576
861
  githat.getUserOrgs().then((data) => setOrgs(data.orgs || [])).catch(() => {
577
862
  }).finally(() => setOrgsLoading(false));
578
863
  }
579
864
  }, [isSignedIn]);
580
- (0, import_react8.useEffect)(() => {
865
+ (0, import_react9.useEffect)(() => {
581
866
  const handleClickOutside = (e) => {
582
867
  if (ref.current && !ref.current.contains(e.target)) setOpen(false);
583
868
  };
@@ -611,15 +896,15 @@ function OrgSwitcher() {
611
896
  }
612
897
 
613
898
  // src/components/VerifiedBadge.tsx
614
- var import_react9 = require("react");
899
+ var import_react10 = require("react");
615
900
  var import_jsx_runtime8 = require("react/jsx-runtime");
616
901
  var CACHE_TTL = 5 * 60 * 1e3;
617
902
  var cache = /* @__PURE__ */ new Map();
618
903
  function VerifiedBadge({ type, identifier, label }) {
619
904
  const githat = useGitHat();
620
- const [verified, setVerified] = (0, import_react9.useState)(null);
621
- const mounted = (0, import_react9.useRef)(true);
622
- (0, import_react9.useEffect)(() => {
905
+ const [verified, setVerified] = (0, import_react10.useState)(null);
906
+ const mounted = (0, import_react10.useRef)(true);
907
+ (0, import_react10.useEffect)(() => {
623
908
  mounted.current = true;
624
909
  const key = `${type}:${identifier}`;
625
910
  const cached = cache.get(key);
@@ -663,18 +948,412 @@ function ProtectedRoute({ children, fallback }) {
663
948
  }
664
949
  return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_jsx_runtime9.Fragment, { children });
665
950
  }
951
+
952
+ // src/components/ForgotPasswordForm.tsx
953
+ var import_react11 = require("react");
954
+ var import_jsx_runtime10 = require("react/jsx-runtime");
955
+ function ForgotPasswordForm({
956
+ onSuccess,
957
+ onError,
958
+ signInUrl = "/sign-in"
959
+ }) {
960
+ const { forgotPassword } = useGitHat();
961
+ const [email, setEmail] = (0, import_react11.useState)("");
962
+ const [isLoading, setIsLoading] = (0, import_react11.useState)(false);
963
+ const [sent, setSent] = (0, import_react11.useState)(false);
964
+ const [error, setError] = (0, import_react11.useState)("");
965
+ const emailValid = /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);
966
+ const handleSubmit = async (e) => {
967
+ e.preventDefault();
968
+ if (!emailValid) {
969
+ setError("Please enter a valid email address");
970
+ return;
971
+ }
972
+ setIsLoading(true);
973
+ setError("");
974
+ try {
975
+ await forgotPassword(email);
976
+ setSent(true);
977
+ onSuccess?.(email);
978
+ } catch (err) {
979
+ const message = err instanceof Error ? err.message : "Failed to send reset email";
980
+ setError(message);
981
+ onError?.(err instanceof Error ? err : new Error(message));
982
+ } finally {
983
+ setIsLoading(false);
984
+ }
985
+ };
986
+ if (sent) {
987
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "githat-form-container", children: [
988
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "githat-form-header", children: [
989
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("h2", { className: "githat-form-title", children: "Check your email" }),
990
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("p", { className: "githat-form-subtitle", children: [
991
+ "We sent a password reset link to ",
992
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("strong", { children: email })
993
+ ] })
994
+ ] }),
995
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("a", { href: signInUrl, className: "githat-link", children: "Back to sign in" }),
996
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("p", { className: "githat-powered-by", children: [
997
+ "Secured by ",
998
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("strong", { children: "GitHat" })
999
+ ] })
1000
+ ] });
1001
+ }
1002
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "githat-form-container", children: [
1003
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "githat-form-header", children: [
1004
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("h2", { className: "githat-form-title", children: "Forgot password" }),
1005
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("p", { className: "githat-form-subtitle", children: "Enter your email and we'll send you a reset link" })
1006
+ ] }),
1007
+ error && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "githat-alert githat-alert-error", role: "alert", "aria-live": "polite", children: error }),
1008
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("form", { onSubmit: handleSubmit, className: "githat-form", "aria-label": "Forgot password form", children: [
1009
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "githat-field", children: [
1010
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("label", { className: "githat-label", htmlFor: "githat-forgot-email", children: "Email" }),
1011
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
1012
+ "input",
1013
+ {
1014
+ id: "githat-forgot-email",
1015
+ className: "githat-input",
1016
+ type: "email",
1017
+ value: email,
1018
+ onChange: (e) => setEmail(e.target.value),
1019
+ placeholder: "you@example.com",
1020
+ autoComplete: "email",
1021
+ disabled: isLoading,
1022
+ required: true
1023
+ }
1024
+ )
1025
+ ] }),
1026
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
1027
+ "button",
1028
+ {
1029
+ type: "submit",
1030
+ className: "githat-button githat-button-primary",
1031
+ disabled: isLoading || !email || email.length > 0 && !emailValid,
1032
+ children: isLoading ? "Sending..." : "Send reset link"
1033
+ }
1034
+ )
1035
+ ] }),
1036
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("p", { className: "githat-form-footer", children: [
1037
+ "Remember your password? ",
1038
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("a", { href: signInUrl, className: "githat-link", children: "Sign in" })
1039
+ ] }),
1040
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("p", { className: "githat-powered-by", children: [
1041
+ "Secured by ",
1042
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("strong", { children: "GitHat" })
1043
+ ] })
1044
+ ] });
1045
+ }
1046
+
1047
+ // src/components/ResetPasswordForm.tsx
1048
+ var import_react12 = require("react");
1049
+ var import_jsx_runtime11 = require("react/jsx-runtime");
1050
+ function ResetPasswordForm({
1051
+ token,
1052
+ onSuccess,
1053
+ onError,
1054
+ signInUrl = "/sign-in",
1055
+ minPasswordLength = 8
1056
+ }) {
1057
+ const { resetPassword } = useGitHat();
1058
+ const [password, setPassword] = (0, import_react12.useState)("");
1059
+ const [confirm, setConfirm] = (0, import_react12.useState)("");
1060
+ const [isLoading, setIsLoading] = (0, import_react12.useState)(false);
1061
+ const [success, setSuccess] = (0, import_react12.useState)(false);
1062
+ const [error, setError] = (0, import_react12.useState)("");
1063
+ const handleSubmit = async (e) => {
1064
+ e.preventDefault();
1065
+ if (password !== confirm) {
1066
+ setError("Passwords do not match");
1067
+ return;
1068
+ }
1069
+ if (password.length < minPasswordLength) {
1070
+ setError(`Password must be at least ${minPasswordLength} characters`);
1071
+ return;
1072
+ }
1073
+ setIsLoading(true);
1074
+ setError("");
1075
+ try {
1076
+ await resetPassword(token, password);
1077
+ setSuccess(true);
1078
+ onSuccess?.();
1079
+ } catch (err) {
1080
+ const message = err instanceof Error ? err.message : "Failed to reset password";
1081
+ setError(message);
1082
+ onError?.(err instanceof Error ? err : new Error(message));
1083
+ } finally {
1084
+ setIsLoading(false);
1085
+ }
1086
+ };
1087
+ if (success) {
1088
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "githat-form-container", children: [
1089
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "githat-form-header", children: [
1090
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("h2", { className: "githat-form-title", children: "Password reset!" }),
1091
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("p", { className: "githat-form-subtitle", children: "Your password has been successfully reset." })
1092
+ ] }),
1093
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("a", { href: signInUrl, className: "githat-button githat-button-primary", style: { display: "block", textAlign: "center", textDecoration: "none" }, children: "Sign in" }),
1094
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("p", { className: "githat-powered-by", children: [
1095
+ "Secured by ",
1096
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("strong", { children: "GitHat" })
1097
+ ] })
1098
+ ] });
1099
+ }
1100
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "githat-form-container", children: [
1101
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "githat-form-header", children: [
1102
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("h2", { className: "githat-form-title", children: "Reset password" }),
1103
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("p", { className: "githat-form-subtitle", children: "Enter your new password" })
1104
+ ] }),
1105
+ error && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: "githat-alert githat-alert-error", role: "alert", "aria-live": "polite", children: error }),
1106
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("form", { onSubmit: handleSubmit, className: "githat-form", "aria-label": "Reset password form", children: [
1107
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "githat-field", children: [
1108
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("label", { className: "githat-label", htmlFor: "githat-reset-password", children: "New password" }),
1109
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
1110
+ "input",
1111
+ {
1112
+ id: "githat-reset-password",
1113
+ className: "githat-input",
1114
+ type: "password",
1115
+ value: password,
1116
+ onChange: (e) => setPassword(e.target.value),
1117
+ placeholder: "Enter new password",
1118
+ autoComplete: "new-password",
1119
+ disabled: isLoading,
1120
+ required: true,
1121
+ minLength: minPasswordLength
1122
+ }
1123
+ )
1124
+ ] }),
1125
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "githat-field", children: [
1126
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("label", { className: "githat-label", htmlFor: "githat-reset-confirm", children: "Confirm password" }),
1127
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
1128
+ "input",
1129
+ {
1130
+ id: "githat-reset-confirm",
1131
+ className: "githat-input",
1132
+ type: "password",
1133
+ value: confirm,
1134
+ onChange: (e) => setConfirm(e.target.value),
1135
+ placeholder: "Confirm new password",
1136
+ autoComplete: "new-password",
1137
+ disabled: isLoading,
1138
+ required: true
1139
+ }
1140
+ )
1141
+ ] }),
1142
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
1143
+ "button",
1144
+ {
1145
+ type: "submit",
1146
+ className: "githat-button githat-button-primary",
1147
+ disabled: isLoading || !password || !confirm,
1148
+ children: isLoading ? "Resetting..." : "Reset password"
1149
+ }
1150
+ )
1151
+ ] }),
1152
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("p", { className: "githat-powered-by", children: [
1153
+ "Secured by ",
1154
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("strong", { children: "GitHat" })
1155
+ ] })
1156
+ ] });
1157
+ }
1158
+
1159
+ // src/components/VerifyEmailStatus.tsx
1160
+ var import_react13 = require("react");
1161
+ var import_jsx_runtime12 = require("react/jsx-runtime");
1162
+ function VerifyEmailStatus({
1163
+ token,
1164
+ onSuccess,
1165
+ onError,
1166
+ signInUrl = "/sign-in",
1167
+ redirectDelay = 3e3
1168
+ }) {
1169
+ const { verifyEmail } = useGitHat();
1170
+ const [status, setStatus] = (0, import_react13.useState)("loading");
1171
+ const [error, setError] = (0, import_react13.useState)("");
1172
+ (0, import_react13.useEffect)(() => {
1173
+ if (!token) {
1174
+ setStatus("error");
1175
+ setError("Missing verification token");
1176
+ return;
1177
+ }
1178
+ verifyEmail(token).then(() => {
1179
+ setStatus("success");
1180
+ onSuccess?.();
1181
+ if (signInUrl && redirectDelay > 0) {
1182
+ setTimeout(() => {
1183
+ window.location.href = signInUrl;
1184
+ }, redirectDelay);
1185
+ }
1186
+ }).catch((err) => {
1187
+ setStatus("error");
1188
+ const message = err instanceof Error ? err.message : "Verification failed";
1189
+ setError(message);
1190
+ onError?.(err instanceof Error ? err : new Error(message));
1191
+ });
1192
+ }, [token, verifyEmail, onSuccess, onError, signInUrl, redirectDelay]);
1193
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "githat-form-container", children: [
1194
+ status === "loading" && /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "githat-form-header", children: [
1195
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("h2", { className: "githat-form-title", children: "Verifying email..." }),
1196
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("p", { className: "githat-form-subtitle", children: "Please wait while we verify your email address." })
1197
+ ] }),
1198
+ status === "success" && /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(import_jsx_runtime12.Fragment, { children: [
1199
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "githat-form-header", children: [
1200
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("h2", { className: "githat-form-title", children: "Email verified!" }),
1201
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("p", { className: "githat-form-subtitle", children: "Your email has been successfully verified. Redirecting to sign in..." })
1202
+ ] }),
1203
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("a", { href: signInUrl, className: "githat-button githat-button-primary", style: { display: "block", textAlign: "center", textDecoration: "none" }, children: "Sign in now" })
1204
+ ] }),
1205
+ status === "error" && /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(import_jsx_runtime12.Fragment, { children: [
1206
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "githat-form-header", children: [
1207
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("h2", { className: "githat-form-title", children: "Verification failed" }),
1208
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("p", { className: "githat-form-subtitle", children: error })
1209
+ ] }),
1210
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("p", { className: "githat-form-footer", children: [
1211
+ "The link may have expired. ",
1212
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("a", { href: "/sign-up", className: "githat-link", children: "Try signing up again" })
1213
+ ] })
1214
+ ] }),
1215
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("p", { className: "githat-powered-by", children: [
1216
+ "Secured by ",
1217
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("strong", { children: "GitHat" })
1218
+ ] })
1219
+ ] });
1220
+ }
1221
+
1222
+ // src/components/ChangePasswordForm.tsx
1223
+ var import_react14 = require("react");
1224
+ var import_jsx_runtime13 = require("react/jsx-runtime");
1225
+ function ChangePasswordForm({
1226
+ onSuccess,
1227
+ onError,
1228
+ minPasswordLength = 8
1229
+ }) {
1230
+ const { changePassword } = useGitHat();
1231
+ const [current, setCurrent] = (0, import_react14.useState)("");
1232
+ const [newPass, setNewPass] = (0, import_react14.useState)("");
1233
+ const [confirm, setConfirm] = (0, import_react14.useState)("");
1234
+ const [isLoading, setIsLoading] = (0, import_react14.useState)(false);
1235
+ const [error, setError] = (0, import_react14.useState)("");
1236
+ const [success, setSuccess] = (0, import_react14.useState)(false);
1237
+ const handleSubmit = async (e) => {
1238
+ e.preventDefault();
1239
+ if (newPass !== confirm) {
1240
+ setError("Passwords do not match");
1241
+ return;
1242
+ }
1243
+ if (newPass.length < minPasswordLength) {
1244
+ setError(`Password must be at least ${minPasswordLength} characters`);
1245
+ return;
1246
+ }
1247
+ setIsLoading(true);
1248
+ setError("");
1249
+ try {
1250
+ await changePassword(current, newPass);
1251
+ setSuccess(true);
1252
+ setCurrent("");
1253
+ setNewPass("");
1254
+ setConfirm("");
1255
+ onSuccess?.();
1256
+ } catch (err) {
1257
+ const message = err instanceof Error ? err.message : "Failed to change password";
1258
+ setError(message);
1259
+ onError?.(err instanceof Error ? err : new Error(message));
1260
+ } finally {
1261
+ setIsLoading(false);
1262
+ }
1263
+ };
1264
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "githat-form-container", children: [
1265
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "githat-form-header", children: [
1266
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("h2", { className: "githat-form-title", children: "Change password" }),
1267
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("p", { className: "githat-form-subtitle", children: "Update your account password" })
1268
+ ] }),
1269
+ success && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "githat-alert githat-alert-success", role: "status", "aria-live": "polite", children: "Password changed successfully!" }),
1270
+ error && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "githat-alert githat-alert-error", role: "alert", "aria-live": "polite", children: error }),
1271
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("form", { onSubmit: handleSubmit, className: "githat-form", "aria-label": "Change password form", children: [
1272
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "githat-field", children: [
1273
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("label", { className: "githat-label", htmlFor: "githat-change-current", children: "Current password" }),
1274
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
1275
+ "input",
1276
+ {
1277
+ id: "githat-change-current",
1278
+ className: "githat-input",
1279
+ type: "password",
1280
+ value: current,
1281
+ onChange: (e) => setCurrent(e.target.value),
1282
+ placeholder: "Enter current password",
1283
+ autoComplete: "current-password",
1284
+ disabled: isLoading,
1285
+ required: true
1286
+ }
1287
+ )
1288
+ ] }),
1289
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "githat-field", children: [
1290
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("label", { className: "githat-label", htmlFor: "githat-change-new", children: "New password" }),
1291
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
1292
+ "input",
1293
+ {
1294
+ id: "githat-change-new",
1295
+ className: "githat-input",
1296
+ type: "password",
1297
+ value: newPass,
1298
+ onChange: (e) => setNewPass(e.target.value),
1299
+ placeholder: "Enter new password",
1300
+ autoComplete: "new-password",
1301
+ disabled: isLoading,
1302
+ required: true,
1303
+ minLength: minPasswordLength
1304
+ }
1305
+ )
1306
+ ] }),
1307
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "githat-field", children: [
1308
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("label", { className: "githat-label", htmlFor: "githat-change-confirm", children: "Confirm new password" }),
1309
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
1310
+ "input",
1311
+ {
1312
+ id: "githat-change-confirm",
1313
+ className: "githat-input",
1314
+ type: "password",
1315
+ value: confirm,
1316
+ onChange: (e) => setConfirm(e.target.value),
1317
+ placeholder: "Confirm new password",
1318
+ autoComplete: "new-password",
1319
+ disabled: isLoading,
1320
+ required: true
1321
+ }
1322
+ )
1323
+ ] }),
1324
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
1325
+ "button",
1326
+ {
1327
+ type: "submit",
1328
+ className: "githat-button githat-button-primary",
1329
+ disabled: isLoading || !current || !newPass || !confirm,
1330
+ children: isLoading ? "Changing..." : "Change password"
1331
+ }
1332
+ )
1333
+ ] }),
1334
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("p", { className: "githat-powered-by", children: [
1335
+ "Secured by ",
1336
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("strong", { children: "GitHat" })
1337
+ ] })
1338
+ ] });
1339
+ }
666
1340
  // Annotate the CommonJS export names for ESM import in node:
667
1341
  0 && (module.exports = {
1342
+ ChangePasswordForm,
1343
+ ForgotPasswordForm,
668
1344
  GitHatProvider,
669
1345
  OrgSwitcher,
670
1346
  ProtectedRoute,
1347
+ ResetPasswordForm,
671
1348
  SignInButton,
672
1349
  SignInForm,
673
1350
  SignUpButton,
674
1351
  SignUpForm,
675
1352
  UserButton,
676
1353
  VerifiedBadge,
1354
+ VerifyEmailStatus,
677
1355
  useAuth,
1356
+ useData,
678
1357
  useGitHat
679
1358
  });
680
1359
  //# sourceMappingURL=index.js.map