@githat/nextjs 0.2.0 → 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,36 +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
- const response = await fetch(url, { ...options, headers });
120
+ let response;
121
+ try {
122
+ response = await fetch(url, {
123
+ ...fetchOptions,
124
+ headers,
125
+ credentials: useCookies ? "include" : "same-origin"
126
+ });
127
+ } catch (networkError) {
128
+ if (networkError instanceof TypeError) {
129
+ const isMissingKey = !appKey || !appKey.startsWith("pk_live_") && !appKey.startsWith("pk_test_");
130
+ const isLocalhost = typeof window !== "undefined" && (window.location.hostname === "localhost" || window.location.hostname === "127.0.0.1");
131
+ if (isMissingKey && !isLocalhost) {
132
+ throw new Error(
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."
139
+ );
140
+ }
141
+ throw new Error(
142
+ "GitHat: API request failed. Verify your publishable key and app domain at https://githat.io/dashboard/apps"
143
+ );
144
+ }
145
+ throw networkError;
146
+ }
108
147
  if (response.status === 401) {
109
148
  if (!_refreshPromise) {
110
- _refreshPromise = refreshTokens(apiUrl, appKey).finally(() => {
149
+ _refreshPromise = refreshTokens(apiUrl, appKey, useCookies).finally(() => {
111
150
  _refreshPromise = null;
112
151
  });
113
152
  }
114
153
  const refreshed = await _refreshPromise;
115
154
  if (refreshed) {
116
- const newToken = localStorage.getItem(TOKEN_KEYS.accessToken);
155
+ const newToken = !useCookies && typeof window !== "undefined" ? localStorage.getItem(TOKEN_KEYS.accessToken) : null;
117
156
  const retryResponse = await fetch(url, {
118
- ...options,
157
+ ...fetchOptions,
119
158
  headers: {
120
159
  ...headers,
121
160
  ...newToken && { Authorization: `Bearer ${newToken}` }
122
- }
161
+ },
162
+ credentials: useCookies ? "include" : "same-origin"
123
163
  });
124
164
  const retryData = await retryResponse.json();
125
165
  if (!retryResponse.ok) throw new Error(retryData.error || "Request failed");
@@ -138,40 +178,108 @@ function createClient(apiUrl, appKey) {
138
178
  // src/provider.tsx
139
179
  var import_jsx_runtime = require("react/jsx-runtime");
140
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
+ }
141
212
  function GitHatProvider({ config: rawConfig, children }) {
142
213
  const config = (0, import_react.useMemo)(() => resolveConfig(rawConfig), [rawConfig]);
143
- 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
+ }, []);
144
234
  const [user, setUser] = (0, import_react.useState)(null);
145
235
  const [org, setOrg] = (0, import_react.useState)(null);
146
236
  const [isSignedIn, setIsSignedIn] = (0, import_react.useState)(false);
147
237
  const [isLoading, setIsLoading] = (0, import_react.useState)(true);
148
238
  const [authError, setAuthError] = (0, import_react.useState)(null);
149
239
  (0, import_react.useEffect)(() => {
150
- const token = localStorage.getItem(TOKEN_KEYS.accessToken);
151
- const storedUser = localStorage.getItem(TOKEN_KEYS.user);
152
- if (token && storedUser) {
153
- clientRef.current.fetchApi("/auth/me").then((data) => {
154
- const u = data.user || JSON.parse(storedUser);
155
- setUser(u);
156
- const storedOrg = localStorage.getItem(TOKEN_KEYS.org);
157
- setOrg(data.currentOrg || (storedOrg ? JSON.parse(storedOrg) : null));
158
- setIsSignedIn(true);
159
- setAuthError(null);
160
- }).catch((err) => {
161
- 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") {
162
266
  clientRef.current.clearAuth();
163
- } else {
164
- try {
165
- setUser(JSON.parse(storedUser));
166
- } 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
+ }
167
275
  }
168
- setAuthError(err.message || "Failed to verify session");
276
+ setAuthError(error.message || "Failed to verify session");
169
277
  }
170
- }).finally(() => setIsLoading(false));
171
- } else {
278
+ }
172
279
  setIsLoading(false);
173
- }
174
- }, []);
280
+ };
281
+ validateSession();
282
+ }, [useCookies]);
175
283
  (0, import_react.useEffect)(() => {
176
284
  const handleAuthChanged = (e) => {
177
285
  const detail = e.detail;
@@ -189,30 +297,36 @@ function GitHatProvider({ config: rawConfig, children }) {
189
297
  return () => window.removeEventListener("githat:auth-changed", handleAuthChanged);
190
298
  }, []);
191
299
  const signIn = (0, import_react.useCallback)(async (email, password) => {
192
- 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, {
193
302
  method: "POST",
194
303
  body: JSON.stringify({ email, password })
195
304
  });
196
- localStorage.setItem(TOKEN_KEYS.accessToken, data.accessToken);
197
- localStorage.setItem(TOKEN_KEYS.refreshToken, data.refreshToken);
198
- localStorage.setItem(TOKEN_KEYS.user, JSON.stringify(data.user));
199
- 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
+ }
200
311
  setUser(data.user);
201
312
  setOrg(data.org || null);
202
313
  setIsSignedIn(true);
203
314
  window.dispatchEvent(new CustomEvent("githat:auth-changed", {
204
315
  detail: { user: data.user, org: data.org, signedIn: true }
205
316
  }));
206
- }, []);
317
+ }, [useCookies]);
207
318
  const signUp = (0, import_react.useCallback)(async (signUpData) => {
208
- 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, {
209
321
  method: "POST",
210
322
  body: JSON.stringify(signUpData)
211
323
  });
212
- localStorage.setItem(TOKEN_KEYS.accessToken, data.accessToken);
213
- localStorage.setItem(TOKEN_KEYS.refreshToken, data.refreshToken);
214
- localStorage.setItem(TOKEN_KEYS.user, JSON.stringify(data.user));
215
- 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
+ }
216
330
  setUser(data.user);
217
331
  setOrg(data.org || null);
218
332
  setIsSignedIn(true);
@@ -220,10 +334,11 @@ function GitHatProvider({ config: rawConfig, children }) {
220
334
  detail: { user: data.user, org: data.org, signedIn: true }
221
335
  }));
222
336
  return { requiresVerification: !data.user.emailVerified, email: signUpData.email };
223
- }, []);
337
+ }, [useCookies]);
224
338
  const signOut = (0, import_react.useCallback)(async () => {
225
339
  try {
226
- 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" });
227
342
  } catch {
228
343
  }
229
344
  clientRef.current.clearAuth();
@@ -233,22 +348,24 @@ function GitHatProvider({ config: rawConfig, children }) {
233
348
  if (typeof window !== "undefined" && config.afterSignOutUrl) {
234
349
  window.location.href = config.afterSignOutUrl;
235
350
  }
236
- }, [config.afterSignOutUrl]);
351
+ }, [config.afterSignOutUrl, useCookies]);
237
352
  const switchOrg = (0, import_react.useCallback)(async (orgId) => {
238
353
  try {
239
- const data = await clientRef.current.fetchApi(`/user/orgs/${orgId}/switch`, { method: "POST" });
240
- if (data.accessToken) localStorage.setItem(TOKEN_KEYS.accessToken, data.accessToken);
241
- if (data.refreshToken) localStorage.setItem(TOKEN_KEYS.refreshToken, data.refreshToken);
242
- const orgData = data.org;
243
- localStorage.setItem(TOKEN_KEYS.org, JSON.stringify(orgData));
244
- 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);
245
362
  window.dispatchEvent(new CustomEvent("githat:auth-changed", {
246
- detail: { user, org: orgData, signedIn: true }
363
+ detail: { user, org: data.org, signedIn: true }
247
364
  }));
248
365
  } catch (e) {
249
366
  console.error("Org switch failed:", e);
250
367
  }
251
- }, [user]);
368
+ }, [user, useCookies]);
252
369
  const value = (0, import_react.useMemo)(() => ({
253
370
  user,
254
371
  org,
@@ -261,7 +378,10 @@ function GitHatProvider({ config: rawConfig, children }) {
261
378
  signOut,
262
379
  switchOrg
263
380
  }), [user, org, isSignedIn, isLoading, authError, config, signIn, signUp, signOut, switchOrg]);
264
- 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
+ ] });
265
385
  }
266
386
 
267
387
  // src/hooks.ts
@@ -277,23 +397,205 @@ function useGitHat() {
277
397
  () => createClient(ctx.config.apiUrl, ctx.config.publishableKey),
278
398
  [ctx.config.apiUrl, ctx.config.publishableKey]
279
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
+ );
280
495
  return {
281
496
  fetch: client.fetchApi,
282
497
  getUserOrgs: () => client.fetchApi("/user/orgs"),
283
498
  verifyMCP: (domain) => client.fetchApi(`/verify/mcp/${domain}`),
284
- 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
285
509
  };
286
510
  }
287
511
 
288
- // src/components/SignInForm.tsx
512
+ // src/data.ts
289
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");
290
592
  var import_jsx_runtime2 = require("react/jsx-runtime");
291
593
  function SignInForm({ onSuccess, signUpUrl, forgotPasswordUrl }) {
292
594
  const { signIn, config } = useAuth();
293
- const [email, setEmail] = (0, import_react3.useState)("");
294
- const [password, setPassword] = (0, import_react3.useState)("");
295
- const [error, setError] = (0, import_react3.useState)("");
296
- 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);
297
599
  const emailValid = /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);
298
600
  const handleSubmit = async (e) => {
299
601
  e.preventDefault();
@@ -379,15 +681,15 @@ function SignInForm({ onSuccess, signUpUrl, forgotPasswordUrl }) {
379
681
  }
380
682
 
381
683
  // src/components/SignUpForm.tsx
382
- var import_react4 = require("react");
684
+ var import_react5 = require("react");
383
685
  var import_jsx_runtime3 = require("react/jsx-runtime");
384
686
  function SignUpForm({ onSuccess, signInUrl }) {
385
687
  const { signUp, config } = useAuth();
386
- const [name, setName] = (0, import_react4.useState)("");
387
- const [email, setEmail] = (0, import_react4.useState)("");
388
- const [password, setPassword] = (0, import_react4.useState)("");
389
- const [error, setError] = (0, import_react4.useState)("");
390
- 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);
391
693
  const emailValid = /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);
392
694
  const passwordValid = password.length >= 8 && /[A-Z]/.test(password) && /[a-z]/.test(password) && /\d/.test(password);
393
695
  const handleSubmit = async (e) => {
@@ -493,31 +795,31 @@ function SignUpForm({ onSuccess, signInUrl }) {
493
795
  }
494
796
 
495
797
  // src/components/SignInButton.tsx
496
- var import_react5 = require("react");
798
+ var import_react6 = require("react");
497
799
  var import_jsx_runtime4 = require("react/jsx-runtime");
498
800
  function SignInButton({ className, children, href }) {
499
- const ctx = (0, import_react5.useContext)(GitHatContext);
801
+ const ctx = (0, import_react6.useContext)(GitHatContext);
500
802
  const url = href || ctx?.config.signInUrl || "/sign-in";
501
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" });
502
804
  }
503
805
 
504
806
  // src/components/SignUpButton.tsx
505
- var import_react6 = require("react");
807
+ var import_react7 = require("react");
506
808
  var import_jsx_runtime5 = require("react/jsx-runtime");
507
809
  function SignUpButton({ className, children, href }) {
508
- const ctx = (0, import_react6.useContext)(GitHatContext);
810
+ const ctx = (0, import_react7.useContext)(GitHatContext);
509
811
  const url = href || ctx?.config.signUpUrl || "/sign-up";
510
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" });
511
813
  }
512
814
 
513
815
  // src/components/UserButton.tsx
514
- var import_react7 = require("react");
816
+ var import_react8 = require("react");
515
817
  var import_jsx_runtime6 = require("react/jsx-runtime");
516
818
  function UserButton() {
517
819
  const { user, org, isSignedIn, signOut } = useAuth();
518
- const [open, setOpen] = (0, import_react7.useState)(false);
519
- const ref = (0, import_react7.useRef)(null);
520
- (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)(() => {
521
823
  const handleClickOutside = (e) => {
522
824
  if (ref.current && !ref.current.contains(e.target)) setOpen(false);
523
825
  };
@@ -544,23 +846,23 @@ function UserButton() {
544
846
  }
545
847
 
546
848
  // src/components/OrgSwitcher.tsx
547
- var import_react8 = require("react");
849
+ var import_react9 = require("react");
548
850
  var import_jsx_runtime7 = require("react/jsx-runtime");
549
851
  function OrgSwitcher() {
550
852
  const { org, isSignedIn, switchOrg } = useAuth();
551
853
  const githat = useGitHat();
552
- const [orgs, setOrgs] = (0, import_react8.useState)([]);
553
- const [orgsLoading, setOrgsLoading] = (0, import_react8.useState)(false);
554
- const [open, setOpen] = (0, import_react8.useState)(false);
555
- const ref = (0, import_react8.useRef)(null);
556
- (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)(() => {
557
859
  if (isSignedIn) {
558
860
  setOrgsLoading(true);
559
861
  githat.getUserOrgs().then((data) => setOrgs(data.orgs || [])).catch(() => {
560
862
  }).finally(() => setOrgsLoading(false));
561
863
  }
562
864
  }, [isSignedIn]);
563
- (0, import_react8.useEffect)(() => {
865
+ (0, import_react9.useEffect)(() => {
564
866
  const handleClickOutside = (e) => {
565
867
  if (ref.current && !ref.current.contains(e.target)) setOpen(false);
566
868
  };
@@ -594,15 +896,15 @@ function OrgSwitcher() {
594
896
  }
595
897
 
596
898
  // src/components/VerifiedBadge.tsx
597
- var import_react9 = require("react");
899
+ var import_react10 = require("react");
598
900
  var import_jsx_runtime8 = require("react/jsx-runtime");
599
901
  var CACHE_TTL = 5 * 60 * 1e3;
600
902
  var cache = /* @__PURE__ */ new Map();
601
903
  function VerifiedBadge({ type, identifier, label }) {
602
904
  const githat = useGitHat();
603
- const [verified, setVerified] = (0, import_react9.useState)(null);
604
- const mounted = (0, import_react9.useRef)(true);
605
- (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)(() => {
606
908
  mounted.current = true;
607
909
  const key = `${type}:${identifier}`;
608
910
  const cached = cache.get(key);
@@ -646,18 +948,412 @@ function ProtectedRoute({ children, fallback }) {
646
948
  }
647
949
  return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_jsx_runtime9.Fragment, { children });
648
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
+ }
649
1340
  // Annotate the CommonJS export names for ESM import in node:
650
1341
  0 && (module.exports = {
1342
+ ChangePasswordForm,
1343
+ ForgotPasswordForm,
651
1344
  GitHatProvider,
652
1345
  OrgSwitcher,
653
1346
  ProtectedRoute,
1347
+ ResetPasswordForm,
654
1348
  SignInButton,
655
1349
  SignInForm,
656
1350
  SignUpButton,
657
1351
  SignUpForm,
658
1352
  UserButton,
659
1353
  VerifiedBadge,
1354
+ VerifyEmailStatus,
660
1355
  useAuth,
1356
+ useData,
661
1357
  useGitHat
662
1358
  });
663
1359
  //# sourceMappingURL=index.js.map