@githat/nextjs 0.2.1 → 0.2.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -18,15 +18,16 @@ function resolveConfig(config) {
18
18
  signInUrl: config.signInUrl || "/sign-in",
19
19
  signUpUrl: config.signUpUrl || "/sign-up",
20
20
  afterSignInUrl: config.afterSignInUrl || "/dashboard",
21
- afterSignOutUrl: config.afterSignOutUrl || "/"
21
+ afterSignOutUrl: config.afterSignOutUrl || "/",
22
+ tokenStorage: config.tokenStorage || "localStorage"
22
23
  };
23
24
  }
24
25
 
25
26
  // src/client.ts
26
27
  var _refreshPromise = null;
27
- async function refreshTokens(apiUrl, appKey) {
28
- const refreshToken = typeof window !== "undefined" ? localStorage.getItem(TOKEN_KEYS.refreshToken) : null;
29
- if (!refreshToken) return false;
28
+ async function refreshTokens(apiUrl, appKey, useCookies) {
29
+ const refreshToken = typeof window !== "undefined" && !useCookies ? localStorage.getItem(TOKEN_KEYS.refreshToken) : null;
30
+ if (!useCookies && !refreshToken) return false;
30
31
  let orgId = null;
31
32
  try {
32
33
  const orgStr = localStorage.getItem(TOKEN_KEYS.org);
@@ -34,18 +35,22 @@ async function refreshTokens(apiUrl, appKey) {
34
35
  } catch {
35
36
  }
36
37
  try {
37
- const res = await fetch(`${apiUrl}/auth/refresh`, {
38
+ const refreshUrl = useCookies ? `${apiUrl}/auth/refresh?setCookie=true` : `${apiUrl}/auth/refresh`;
39
+ const res = await fetch(refreshUrl, {
38
40
  method: "POST",
39
41
  headers: {
40
42
  "Content-Type": "application/json",
41
43
  "X-GitHat-App-Key": appKey
42
44
  },
43
- body: JSON.stringify({ refreshToken, orgId })
45
+ credentials: useCookies ? "include" : "same-origin",
46
+ body: JSON.stringify(useCookies ? { orgId } : { refreshToken, orgId })
44
47
  });
45
48
  if (!res.ok) return false;
46
49
  const data = await res.json();
47
- if (data.accessToken) localStorage.setItem(TOKEN_KEYS.accessToken, data.accessToken);
48
- if (data.refreshToken) localStorage.setItem(TOKEN_KEYS.refreshToken, data.refreshToken);
50
+ if (!useCookies) {
51
+ if (data.accessToken) localStorage.setItem(TOKEN_KEYS.accessToken, data.accessToken);
52
+ if (data.refreshToken) localStorage.setItem(TOKEN_KEYS.refreshToken, data.refreshToken);
53
+ }
49
54
  if (data.org) localStorage.setItem(TOKEN_KEYS.org, JSON.stringify(data.org));
50
55
  return true;
51
56
  } catch {
@@ -55,53 +60,66 @@ async function refreshTokens(apiUrl, appKey) {
55
60
  function clearAuth() {
56
61
  if (typeof window === "undefined") return;
57
62
  Object.values(TOKEN_KEYS).forEach((key) => localStorage.removeItem(key));
58
- window.dispatchEvent(new CustomEvent("githat:auth-changed", {
59
- detail: { user: null, org: null, signedIn: false }
60
- }));
63
+ window.dispatchEvent(
64
+ new CustomEvent("githat:auth-changed", {
65
+ detail: { user: null, org: null, signedIn: false }
66
+ })
67
+ );
61
68
  }
62
- function createClient(apiUrl, appKey) {
63
- async function fetchApi(endpoint, options = {}) {
69
+ function createClient(apiUrl, appKey, options = {}) {
70
+ const { useCookies = false } = options;
71
+ async function fetchApi(endpoint, fetchOptions = {}) {
64
72
  const url = `${apiUrl}${endpoint}`;
65
- const token = typeof window !== "undefined" ? localStorage.getItem(TOKEN_KEYS.accessToken) : null;
73
+ const token = typeof window !== "undefined" && !useCookies ? localStorage.getItem(TOKEN_KEYS.accessToken) : null;
66
74
  const headers = {
67
75
  "Content-Type": "application/json",
68
76
  "X-GitHat-App-Key": appKey,
69
77
  ...token && { Authorization: `Bearer ${token}` },
70
- ...options.headers
78
+ ...fetchOptions.headers
71
79
  };
72
80
  let response;
73
81
  try {
74
- response = await fetch(url, { ...options, headers });
82
+ response = await fetch(url, {
83
+ ...fetchOptions,
84
+ headers,
85
+ credentials: useCookies ? "include" : "same-origin"
86
+ });
75
87
  } catch (networkError) {
76
88
  if (networkError instanceof TypeError) {
77
- const isMissingKey = !appKey || !appKey.startsWith("pk_live_");
89
+ const isMissingKey = !appKey || !appKey.startsWith("pk_live_") && !appKey.startsWith("pk_test_");
78
90
  const isLocalhost = typeof window !== "undefined" && (window.location.hostname === "localhost" || window.location.hostname === "127.0.0.1");
79
91
  if (isMissingKey && !isLocalhost) {
80
92
  throw new Error(
81
- "Missing GitHat API key. Add NEXT_PUBLIC_GITHAT_PUBLISHABLE_KEY to .env.local"
93
+ "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"
94
+ );
95
+ }
96
+ if (isLocalhost) {
97
+ throw new Error(
98
+ "GitHat: Cannot reach api.githat.io. Check your internet connection."
82
99
  );
83
100
  }
84
101
  throw new Error(
85
- "Unable to connect to GitHat API. Check your network connection."
102
+ "GitHat: API request failed. Verify your publishable key and app domain at https://githat.io/dashboard/apps"
86
103
  );
87
104
  }
88
105
  throw networkError;
89
106
  }
90
107
  if (response.status === 401) {
91
108
  if (!_refreshPromise) {
92
- _refreshPromise = refreshTokens(apiUrl, appKey).finally(() => {
109
+ _refreshPromise = refreshTokens(apiUrl, appKey, useCookies).finally(() => {
93
110
  _refreshPromise = null;
94
111
  });
95
112
  }
96
113
  const refreshed = await _refreshPromise;
97
114
  if (refreshed) {
98
- const newToken = localStorage.getItem(TOKEN_KEYS.accessToken);
115
+ const newToken = !useCookies && typeof window !== "undefined" ? localStorage.getItem(TOKEN_KEYS.accessToken) : null;
99
116
  const retryResponse = await fetch(url, {
100
- ...options,
117
+ ...fetchOptions,
101
118
  headers: {
102
119
  ...headers,
103
120
  ...newToken && { Authorization: `Bearer ${newToken}` }
104
- }
121
+ },
122
+ credentials: useCookies ? "include" : "same-origin"
105
123
  });
106
124
  const retryData = await retryResponse.json();
107
125
  if (!retryResponse.ok) throw new Error(retryData.error || "Request failed");
@@ -118,42 +136,110 @@ function createClient(apiUrl, appKey) {
118
136
  }
119
137
 
120
138
  // src/provider.tsx
121
- import { jsx } from "react/jsx-runtime";
139
+ import { jsx, jsxs } from "react/jsx-runtime";
122
140
  var GitHatContext = createContext(null);
141
+ function isDevMode(key) {
142
+ return !key || !key.startsWith("pk_live_") && !key.startsWith("pk_test_");
143
+ }
144
+ function DevModeBanner() {
145
+ const [dismissed, setDismissed] = useState(() => {
146
+ if (typeof window === "undefined") return true;
147
+ return localStorage.getItem("githat_dev_banner_dismissed") === "1";
148
+ });
149
+ if (dismissed || typeof window === "undefined") return null;
150
+ const hostname = window.location.hostname;
151
+ if (hostname !== "localhost" && hostname !== "127.0.0.1") return null;
152
+ return /* @__PURE__ */ jsxs("div", { className: "githat-dev-banner", role: "status", children: [
153
+ /* @__PURE__ */ jsxs("span", { children: [
154
+ /* @__PURE__ */ jsx("strong", { children: "GitHat Dev Mode" }),
155
+ " \u2014 No publishable key. Auth works on localhost.",
156
+ " ",
157
+ /* @__PURE__ */ jsx("a", { href: "https://githat.io/dashboard/apps", target: "_blank", rel: "noopener noreferrer", children: "Get your key" })
158
+ ] }),
159
+ /* @__PURE__ */ jsx(
160
+ "button",
161
+ {
162
+ onClick: () => {
163
+ setDismissed(true);
164
+ localStorage.setItem("githat_dev_banner_dismissed", "1");
165
+ },
166
+ "aria-label": "Dismiss",
167
+ children: "\xD7"
168
+ }
169
+ )
170
+ ] });
171
+ }
123
172
  function GitHatProvider({ config: rawConfig, children }) {
124
173
  const config = useMemo(() => resolveConfig(rawConfig), [rawConfig]);
125
- const clientRef = useRef(createClient(config.apiUrl, config.publishableKey));
174
+ const useCookies = config.tokenStorage === "cookie";
175
+ const devMode = isDevMode(config.publishableKey);
176
+ const clientRef = useRef(createClient(config.apiUrl, config.publishableKey, { useCookies }));
177
+ useEffect(() => {
178
+ if (!devMode || typeof window === "undefined") return;
179
+ const hostname = window.location.hostname;
180
+ if (hostname === "localhost" || hostname === "127.0.0.1") {
181
+ console.warn(
182
+ "%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",
183
+ "background: #f59e0b; color: #000; font-weight: bold; padding: 2px 6px; border-radius: 3px;",
184
+ "color: #f59e0b;"
185
+ );
186
+ } else {
187
+ console.error(
188
+ "%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",
189
+ "background: #ef4444; color: #fff; font-weight: bold; padding: 2px 6px; border-radius: 3px;",
190
+ "color: #ef4444;"
191
+ );
192
+ }
193
+ }, []);
126
194
  const [user, setUser] = useState(null);
127
195
  const [org, setOrg] = useState(null);
128
196
  const [isSignedIn, setIsSignedIn] = useState(false);
129
197
  const [isLoading, setIsLoading] = useState(true);
130
198
  const [authError, setAuthError] = useState(null);
131
199
  useEffect(() => {
132
- const token = localStorage.getItem(TOKEN_KEYS.accessToken);
133
- const storedUser = localStorage.getItem(TOKEN_KEYS.user);
134
- if (token && storedUser) {
135
- clientRef.current.fetchApi("/auth/me").then((data) => {
136
- const u = data.user || JSON.parse(storedUser);
137
- setUser(u);
138
- const storedOrg = localStorage.getItem(TOKEN_KEYS.org);
139
- setOrg(data.currentOrg || (storedOrg ? JSON.parse(storedOrg) : null));
140
- setIsSignedIn(true);
141
- setAuthError(null);
142
- }).catch((err) => {
143
- if (err.message === "Session expired") {
200
+ const validateSession = async () => {
201
+ try {
202
+ if (!useCookies) {
203
+ const token = localStorage.getItem(TOKEN_KEYS.accessToken);
204
+ const storedUser = localStorage.getItem(TOKEN_KEYS.user);
205
+ if (!token || !storedUser) {
206
+ setIsLoading(false);
207
+ return;
208
+ }
209
+ }
210
+ const data = await clientRef.current.fetchApi("/auth/me");
211
+ if (data.user) {
212
+ setUser(data.user);
213
+ setOrg(data.currentOrg || null);
214
+ setIsSignedIn(true);
215
+ setAuthError(null);
216
+ if (!useCookies) {
217
+ localStorage.setItem(TOKEN_KEYS.user, JSON.stringify(data.user));
218
+ if (data.currentOrg) {
219
+ localStorage.setItem(TOKEN_KEYS.org, JSON.stringify(data.currentOrg));
220
+ }
221
+ }
222
+ }
223
+ } catch (err) {
224
+ const error = err;
225
+ if (error.message === "Session expired") {
144
226
  clientRef.current.clearAuth();
145
- } else {
146
- try {
147
- setUser(JSON.parse(storedUser));
148
- } catch {
227
+ } else if (!useCookies) {
228
+ const storedUser = localStorage.getItem(TOKEN_KEYS.user);
229
+ if (storedUser) {
230
+ try {
231
+ setUser(JSON.parse(storedUser));
232
+ setIsSignedIn(true);
233
+ } catch {
234
+ }
149
235
  }
150
- setAuthError(err.message || "Failed to verify session");
236
+ setAuthError(error.message || "Failed to verify session");
151
237
  }
152
- }).finally(() => setIsLoading(false));
153
- } else {
238
+ }
154
239
  setIsLoading(false);
155
- }
156
- }, []);
240
+ };
241
+ validateSession();
242
+ }, [useCookies]);
157
243
  useEffect(() => {
158
244
  const handleAuthChanged = (e) => {
159
245
  const detail = e.detail;
@@ -171,30 +257,36 @@ function GitHatProvider({ config: rawConfig, children }) {
171
257
  return () => window.removeEventListener("githat:auth-changed", handleAuthChanged);
172
258
  }, []);
173
259
  const signIn = useCallback(async (email, password) => {
174
- const data = await clientRef.current.fetchApi("/auth/login", {
260
+ const loginUrl = useCookies ? "/auth/login?setCookie=true" : "/auth/login";
261
+ const data = await clientRef.current.fetchApi(loginUrl, {
175
262
  method: "POST",
176
263
  body: JSON.stringify({ email, password })
177
264
  });
178
- localStorage.setItem(TOKEN_KEYS.accessToken, data.accessToken);
179
- localStorage.setItem(TOKEN_KEYS.refreshToken, data.refreshToken);
180
- localStorage.setItem(TOKEN_KEYS.user, JSON.stringify(data.user));
181
- if (data.org) localStorage.setItem(TOKEN_KEYS.org, JSON.stringify(data.org));
265
+ if (!useCookies && data.accessToken && data.refreshToken) {
266
+ localStorage.setItem(TOKEN_KEYS.accessToken, data.accessToken);
267
+ localStorage.setItem(TOKEN_KEYS.refreshToken, data.refreshToken);
268
+ localStorage.setItem(TOKEN_KEYS.user, JSON.stringify(data.user));
269
+ if (data.org) localStorage.setItem(TOKEN_KEYS.org, JSON.stringify(data.org));
270
+ }
182
271
  setUser(data.user);
183
272
  setOrg(data.org || null);
184
273
  setIsSignedIn(true);
185
274
  window.dispatchEvent(new CustomEvent("githat:auth-changed", {
186
275
  detail: { user: data.user, org: data.org, signedIn: true }
187
276
  }));
188
- }, []);
277
+ }, [useCookies]);
189
278
  const signUp = useCallback(async (signUpData) => {
190
- const data = await clientRef.current.fetchApi("/auth/register", {
279
+ const registerUrl = useCookies ? "/auth/register?setCookie=true" : "/auth/register";
280
+ const data = await clientRef.current.fetchApi(registerUrl, {
191
281
  method: "POST",
192
282
  body: JSON.stringify(signUpData)
193
283
  });
194
- localStorage.setItem(TOKEN_KEYS.accessToken, data.accessToken);
195
- localStorage.setItem(TOKEN_KEYS.refreshToken, data.refreshToken);
196
- localStorage.setItem(TOKEN_KEYS.user, JSON.stringify(data.user));
197
- if (data.org) localStorage.setItem(TOKEN_KEYS.org, JSON.stringify(data.org));
284
+ if (!useCookies && data.accessToken && data.refreshToken) {
285
+ localStorage.setItem(TOKEN_KEYS.accessToken, data.accessToken);
286
+ localStorage.setItem(TOKEN_KEYS.refreshToken, data.refreshToken);
287
+ localStorage.setItem(TOKEN_KEYS.user, JSON.stringify(data.user));
288
+ if (data.org) localStorage.setItem(TOKEN_KEYS.org, JSON.stringify(data.org));
289
+ }
198
290
  setUser(data.user);
199
291
  setOrg(data.org || null);
200
292
  setIsSignedIn(true);
@@ -202,10 +294,11 @@ function GitHatProvider({ config: rawConfig, children }) {
202
294
  detail: { user: data.user, org: data.org, signedIn: true }
203
295
  }));
204
296
  return { requiresVerification: !data.user.emailVerified, email: signUpData.email };
205
- }, []);
297
+ }, [useCookies]);
206
298
  const signOut = useCallback(async () => {
207
299
  try {
208
- await clientRef.current.fetchApi("/auth/logout", { method: "POST" });
300
+ const logoutUrl = useCookies ? "/auth/logout?setCookie=true" : "/auth/logout";
301
+ await clientRef.current.fetchApi(logoutUrl, { method: "POST" });
209
302
  } catch {
210
303
  }
211
304
  clientRef.current.clearAuth();
@@ -215,22 +308,24 @@ function GitHatProvider({ config: rawConfig, children }) {
215
308
  if (typeof window !== "undefined" && config.afterSignOutUrl) {
216
309
  window.location.href = config.afterSignOutUrl;
217
310
  }
218
- }, [config.afterSignOutUrl]);
311
+ }, [config.afterSignOutUrl, useCookies]);
219
312
  const switchOrg = useCallback(async (orgId) => {
220
313
  try {
221
- const data = await clientRef.current.fetchApi(`/user/orgs/${orgId}/switch`, { method: "POST" });
222
- if (data.accessToken) localStorage.setItem(TOKEN_KEYS.accessToken, data.accessToken);
223
- if (data.refreshToken) localStorage.setItem(TOKEN_KEYS.refreshToken, data.refreshToken);
224
- const orgData = data.org;
225
- localStorage.setItem(TOKEN_KEYS.org, JSON.stringify(orgData));
226
- setOrg(orgData);
314
+ const switchUrl = useCookies ? `/user/orgs/${orgId}/switch?setCookie=true` : `/user/orgs/${orgId}/switch`;
315
+ const data = await clientRef.current.fetchApi(switchUrl, { method: "POST" });
316
+ if (!useCookies) {
317
+ if (data.accessToken) localStorage.setItem(TOKEN_KEYS.accessToken, data.accessToken);
318
+ if (data.refreshToken) localStorage.setItem(TOKEN_KEYS.refreshToken, data.refreshToken);
319
+ localStorage.setItem(TOKEN_KEYS.org, JSON.stringify(data.org));
320
+ }
321
+ setOrg(data.org);
227
322
  window.dispatchEvent(new CustomEvent("githat:auth-changed", {
228
- detail: { user, org: orgData, signedIn: true }
323
+ detail: { user, org: data.org, signedIn: true }
229
324
  }));
230
325
  } catch (e) {
231
326
  console.error("Org switch failed:", e);
232
327
  }
233
- }, [user]);
328
+ }, [user, useCookies]);
234
329
  const value = useMemo(() => ({
235
330
  user,
236
331
  org,
@@ -243,11 +338,14 @@ function GitHatProvider({ config: rawConfig, children }) {
243
338
  signOut,
244
339
  switchOrg
245
340
  }), [user, org, isSignedIn, isLoading, authError, config, signIn, signUp, signOut, switchOrg]);
246
- return /* @__PURE__ */ jsx(GitHatContext.Provider, { value, children });
341
+ return /* @__PURE__ */ jsxs(GitHatContext.Provider, { value, children: [
342
+ devMode && /* @__PURE__ */ jsx(DevModeBanner, {}),
343
+ children
344
+ ] });
247
345
  }
248
346
 
249
347
  // src/hooks.ts
250
- import { useContext, useMemo as useMemo2 } from "react";
348
+ import { useContext, useMemo as useMemo2, useCallback as useCallback2 } from "react";
251
349
  function useAuth() {
252
350
  const ctx = useContext(GitHatContext);
253
351
  if (!ctx) throw new Error("useAuth must be used within a <GitHatProvider>");
@@ -259,17 +357,199 @@ function useGitHat() {
259
357
  () => createClient(ctx.config.apiUrl, ctx.config.publishableKey),
260
358
  [ctx.config.apiUrl, ctx.config.publishableKey]
261
359
  );
360
+ const getOrgMetadata = useCallback2(async () => {
361
+ if (!ctx.org?.id) {
362
+ throw new Error("No active organization");
363
+ }
364
+ const response = await client.fetchApi(
365
+ `/orgs/${ctx.org.id}/metadata`
366
+ );
367
+ return response.metadata || {};
368
+ }, [client, ctx.org?.id]);
369
+ const updateOrgMetadata = useCallback2(
370
+ async (updates) => {
371
+ if (!ctx.org?.id) {
372
+ throw new Error("No active organization");
373
+ }
374
+ const response = await client.fetchApi(
375
+ `/orgs/${ctx.org.id}/metadata`,
376
+ {
377
+ method: "PATCH",
378
+ body: JSON.stringify(updates)
379
+ }
380
+ );
381
+ return response.metadata || {};
382
+ },
383
+ [client, ctx.org?.id]
384
+ );
385
+ const forgotPassword = useCallback2(
386
+ async (email) => {
387
+ const response = await fetch(`${ctx.config.apiUrl}/auth/forgot-password`, {
388
+ method: "POST",
389
+ headers: { "Content-Type": "application/json" },
390
+ body: JSON.stringify({ email })
391
+ });
392
+ if (!response.ok) {
393
+ const error = await response.json().catch(() => ({}));
394
+ throw new Error(error.message || "Failed to send reset email");
395
+ }
396
+ return { success: true };
397
+ },
398
+ [ctx.config.apiUrl]
399
+ );
400
+ const resetPassword = useCallback2(
401
+ async (token, newPassword) => {
402
+ const response = await fetch(`${ctx.config.apiUrl}/auth/reset-password`, {
403
+ method: "POST",
404
+ headers: { "Content-Type": "application/json" },
405
+ body: JSON.stringify({ token, password: newPassword })
406
+ });
407
+ if (!response.ok) {
408
+ const error = await response.json().catch(() => ({}));
409
+ throw new Error(error.message || "Failed to reset password");
410
+ }
411
+ return { success: true };
412
+ },
413
+ [ctx.config.apiUrl]
414
+ );
415
+ const changePassword = useCallback2(
416
+ async (currentPassword, newPassword) => {
417
+ await client.fetchApi("/auth/change-password", {
418
+ method: "POST",
419
+ body: JSON.stringify({ currentPassword, newPassword })
420
+ });
421
+ return { success: true };
422
+ },
423
+ [client]
424
+ );
425
+ const verifyEmail = useCallback2(
426
+ async (token) => {
427
+ const response = await fetch(`${ctx.config.apiUrl}/auth/verify-email`, {
428
+ method: "POST",
429
+ headers: { "Content-Type": "application/json" },
430
+ body: JSON.stringify({ token })
431
+ });
432
+ if (!response.ok) {
433
+ const error = await response.json().catch(() => ({}));
434
+ throw new Error(error.message || "Failed to verify email");
435
+ }
436
+ return { success: true };
437
+ },
438
+ [ctx.config.apiUrl]
439
+ );
440
+ const resendVerificationEmail = useCallback2(
441
+ async (email) => {
442
+ const response = await fetch(`${ctx.config.apiUrl}/auth/resend-verification`, {
443
+ method: "POST",
444
+ headers: { "Content-Type": "application/json" },
445
+ body: JSON.stringify({ email })
446
+ });
447
+ if (!response.ok) {
448
+ const error = await response.json().catch(() => ({}));
449
+ throw new Error(error.message || "Failed to resend verification email");
450
+ }
451
+ return { success: true };
452
+ },
453
+ [ctx.config.apiUrl]
454
+ );
262
455
  return {
263
456
  fetch: client.fetchApi,
264
457
  getUserOrgs: () => client.fetchApi("/user/orgs"),
265
458
  verifyMCP: (domain) => client.fetchApi(`/verify/mcp/${domain}`),
266
- verifyAgent: (wallet) => client.fetchApi(`/verify/agent/${wallet}`)
459
+ verifyAgent: (wallet) => client.fetchApi(`/verify/agent/${wallet}`),
460
+ getOrgMetadata,
461
+ updateOrgMetadata,
462
+ // Password management
463
+ forgotPassword,
464
+ resetPassword,
465
+ changePassword,
466
+ // Email verification
467
+ verifyEmail,
468
+ resendVerificationEmail
267
469
  };
268
470
  }
269
471
 
472
+ // src/data.ts
473
+ import { useMemo as useMemo3 } from "react";
474
+ function useData() {
475
+ const ctx = useAuth();
476
+ const client = useMemo3(
477
+ () => createClient(ctx.config.apiUrl, ctx.config.publishableKey),
478
+ [ctx.config.apiUrl, ctx.config.publishableKey]
479
+ );
480
+ return useMemo3(() => ({
481
+ /**
482
+ * Store an item in a collection. If the item exists, it will be updated.
483
+ * @param collection - Collection name (e.g., 'orders', 'users')
484
+ * @param data - Data object with required `id` field
485
+ */
486
+ put: async (collection, data) => {
487
+ if (!data.id) {
488
+ throw new Error('Data must include an "id" field');
489
+ }
490
+ return client.fetchApi(`/data/${collection}/${data.id}`, {
491
+ method: "PUT",
492
+ body: JSON.stringify(data)
493
+ });
494
+ },
495
+ /**
496
+ * Get a single item from a collection.
497
+ * @param collection - Collection name
498
+ * @param id - Item ID
499
+ */
500
+ get: async (collection, id) => {
501
+ try {
502
+ const result = await client.fetchApi(`/data/${collection}/${id}`);
503
+ return result.item;
504
+ } catch (err) {
505
+ if (err instanceof Error && err.message === "Item not found") {
506
+ return null;
507
+ }
508
+ throw err;
509
+ }
510
+ },
511
+ /**
512
+ * Query items from a collection with optional filters and pagination.
513
+ * @param collection - Collection name
514
+ * @param options - Query options (limit, cursor, filter)
515
+ */
516
+ query: async (collection, options = {}) => {
517
+ const params = new URLSearchParams();
518
+ if (options.limit) params.set("limit", options.limit.toString());
519
+ if (options.cursor) params.set("cursor", options.cursor);
520
+ if (options.filter) params.set("filter", JSON.stringify(options.filter));
521
+ const queryString = params.toString();
522
+ const url = `/data/${collection}${queryString ? `?${queryString}` : ""}`;
523
+ return client.fetchApi(url);
524
+ },
525
+ /**
526
+ * Delete an item from a collection.
527
+ * @param collection - Collection name
528
+ * @param id - Item ID
529
+ */
530
+ remove: async (collection, id) => {
531
+ return client.fetchApi(`/data/${collection}/${id}`, {
532
+ method: "DELETE"
533
+ });
534
+ },
535
+ /**
536
+ * Batch operations (put/delete) on a collection.
537
+ * Maximum 100 operations per request.
538
+ * @param collection - Collection name
539
+ * @param operations - Array of operations
540
+ */
541
+ batch: async (collection, operations) => {
542
+ return client.fetchApi(`/data/${collection}/batch`, {
543
+ method: "POST",
544
+ body: JSON.stringify({ operations })
545
+ });
546
+ }
547
+ }), [client]);
548
+ }
549
+
270
550
  // src/components/SignInForm.tsx
271
551
  import { useState as useState2 } from "react";
272
- import { jsx as jsx2, jsxs } from "react/jsx-runtime";
552
+ import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
273
553
  function SignInForm({ onSuccess, signUpUrl, forgotPasswordUrl }) {
274
554
  const { signIn, config } = useAuth();
275
555
  const [email, setEmail] = useState2("");
@@ -299,14 +579,14 @@ function SignInForm({ onSuccess, signUpUrl, forgotPasswordUrl }) {
299
579
  setLoading(false);
300
580
  }
301
581
  };
302
- return /* @__PURE__ */ jsxs("div", { className: "githat-form-container", children: [
303
- /* @__PURE__ */ jsxs("div", { className: "githat-form-header", children: [
582
+ return /* @__PURE__ */ jsxs2("div", { className: "githat-form-container", children: [
583
+ /* @__PURE__ */ jsxs2("div", { className: "githat-form-header", children: [
304
584
  /* @__PURE__ */ jsx2("h2", { className: "githat-form-title", children: "Sign in" }),
305
585
  /* @__PURE__ */ jsx2("p", { className: "githat-form-subtitle", children: "Welcome back to GitHat" })
306
586
  ] }),
307
587
  error && /* @__PURE__ */ jsx2("div", { className: "githat-alert githat-alert-error", role: "alert", "aria-live": "polite", children: error }),
308
- /* @__PURE__ */ jsxs("form", { onSubmit: handleSubmit, className: "githat-form", "aria-label": "Sign in form", children: [
309
- /* @__PURE__ */ jsxs("div", { className: "githat-field", children: [
588
+ /* @__PURE__ */ jsxs2("form", { onSubmit: handleSubmit, className: "githat-form", "aria-label": "Sign in form", children: [
589
+ /* @__PURE__ */ jsxs2("div", { className: "githat-field", children: [
310
590
  /* @__PURE__ */ jsx2("label", { className: "githat-label", htmlFor: "githat-signin-email", children: "Email" }),
311
591
  /* @__PURE__ */ jsx2(
312
592
  "input",
@@ -322,7 +602,7 @@ function SignInForm({ onSuccess, signUpUrl, forgotPasswordUrl }) {
322
602
  }
323
603
  )
324
604
  ] }),
325
- /* @__PURE__ */ jsxs("div", { className: "githat-field", children: [
605
+ /* @__PURE__ */ jsxs2("div", { className: "githat-field", children: [
326
606
  /* @__PURE__ */ jsx2("label", { className: "githat-label", htmlFor: "githat-signin-password", children: "Password" }),
327
607
  /* @__PURE__ */ jsx2(
328
608
  "input",
@@ -349,11 +629,11 @@ function SignInForm({ onSuccess, signUpUrl, forgotPasswordUrl }) {
349
629
  }
350
630
  )
351
631
  ] }),
352
- signUpUrl && /* @__PURE__ */ jsxs("p", { className: "githat-form-footer", children: [
632
+ signUpUrl && /* @__PURE__ */ jsxs2("p", { className: "githat-form-footer", children: [
353
633
  "Don't have an account? ",
354
634
  /* @__PURE__ */ jsx2("a", { href: signUpUrl, className: "githat-link", children: "Sign up" })
355
635
  ] }),
356
- /* @__PURE__ */ jsxs("p", { className: "githat-powered-by", children: [
636
+ /* @__PURE__ */ jsxs2("p", { className: "githat-powered-by", children: [
357
637
  "Secured by ",
358
638
  /* @__PURE__ */ jsx2("strong", { children: "GitHat" })
359
639
  ] })
@@ -362,7 +642,7 @@ function SignInForm({ onSuccess, signUpUrl, forgotPasswordUrl }) {
362
642
 
363
643
  // src/components/SignUpForm.tsx
364
644
  import { useState as useState3 } from "react";
365
- import { jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
645
+ import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
366
646
  function SignUpForm({ onSuccess, signInUrl }) {
367
647
  const { signUp, config } = useAuth();
368
648
  const [name, setName] = useState3("");
@@ -379,7 +659,7 @@ function SignUpForm({ onSuccess, signInUrl }) {
379
659
  return;
380
660
  }
381
661
  if (!passwordValid) {
382
- setError("Password must be 8+ characters with uppercase, lowercase, number, and special character");
662
+ setError("Password must be 8+ characters with uppercase, lowercase, and number");
383
663
  return;
384
664
  }
385
665
  setError("");
@@ -397,14 +677,14 @@ function SignUpForm({ onSuccess, signInUrl }) {
397
677
  setLoading(false);
398
678
  }
399
679
  };
400
- return /* @__PURE__ */ jsxs2("div", { className: "githat-form-container", children: [
401
- /* @__PURE__ */ jsxs2("div", { className: "githat-form-header", children: [
680
+ return /* @__PURE__ */ jsxs3("div", { className: "githat-form-container", children: [
681
+ /* @__PURE__ */ jsxs3("div", { className: "githat-form-header", children: [
402
682
  /* @__PURE__ */ jsx3("h2", { className: "githat-form-title", children: "Create an account" }),
403
683
  /* @__PURE__ */ jsx3("p", { className: "githat-form-subtitle", children: "Get started with GitHat" })
404
684
  ] }),
405
685
  error && /* @__PURE__ */ jsx3("div", { className: "githat-alert githat-alert-error", role: "alert", "aria-live": "polite", children: error }),
406
- /* @__PURE__ */ jsxs2("form", { onSubmit: handleSubmit, className: "githat-form", "aria-label": "Sign up form", children: [
407
- /* @__PURE__ */ jsxs2("div", { className: "githat-field", children: [
686
+ /* @__PURE__ */ jsxs3("form", { onSubmit: handleSubmit, className: "githat-form", "aria-label": "Sign up form", children: [
687
+ /* @__PURE__ */ jsxs3("div", { className: "githat-field", children: [
408
688
  /* @__PURE__ */ jsx3("label", { className: "githat-label", htmlFor: "githat-signup-name", children: "Full name" }),
409
689
  /* @__PURE__ */ jsx3(
410
690
  "input",
@@ -420,7 +700,7 @@ function SignUpForm({ onSuccess, signInUrl }) {
420
700
  }
421
701
  )
422
702
  ] }),
423
- /* @__PURE__ */ jsxs2("div", { className: "githat-field", children: [
703
+ /* @__PURE__ */ jsxs3("div", { className: "githat-field", children: [
424
704
  /* @__PURE__ */ jsx3("label", { className: "githat-label", htmlFor: "githat-signup-email", children: "Email" }),
425
705
  /* @__PURE__ */ jsx3(
426
706
  "input",
@@ -436,7 +716,7 @@ function SignUpForm({ onSuccess, signInUrl }) {
436
716
  }
437
717
  )
438
718
  ] }),
439
- /* @__PURE__ */ jsxs2("div", { className: "githat-field", children: [
719
+ /* @__PURE__ */ jsxs3("div", { className: "githat-field", children: [
440
720
  /* @__PURE__ */ jsx3("label", { className: "githat-label", htmlFor: "githat-signup-password", children: "Password" }),
441
721
  /* @__PURE__ */ jsx3(
442
722
  "input",
@@ -463,11 +743,11 @@ function SignUpForm({ onSuccess, signInUrl }) {
463
743
  }
464
744
  )
465
745
  ] }),
466
- signInUrl && /* @__PURE__ */ jsxs2("p", { className: "githat-form-footer", children: [
746
+ signInUrl && /* @__PURE__ */ jsxs3("p", { className: "githat-form-footer", children: [
467
747
  "Already have an account? ",
468
748
  /* @__PURE__ */ jsx3("a", { href: signInUrl, className: "githat-link", children: "Sign in" })
469
749
  ] }),
470
- /* @__PURE__ */ jsxs2("p", { className: "githat-powered-by", children: [
750
+ /* @__PURE__ */ jsxs3("p", { className: "githat-powered-by", children: [
471
751
  "Secured by ",
472
752
  /* @__PURE__ */ jsx3("strong", { children: "GitHat" })
473
753
  ] })
@@ -494,7 +774,7 @@ function SignUpButton({ className, children, href }) {
494
774
 
495
775
  // src/components/UserButton.tsx
496
776
  import { useState as useState4, useRef as useRef2, useEffect as useEffect2 } from "react";
497
- import { jsx as jsx6, jsxs as jsxs3 } from "react/jsx-runtime";
777
+ import { jsx as jsx6, jsxs as jsxs4 } from "react/jsx-runtime";
498
778
  function UserButton() {
499
779
  const { user, org, isSignedIn, signOut } = useAuth();
500
780
  const [open, setOpen] = useState4(false);
@@ -508,10 +788,10 @@ function UserButton() {
508
788
  }, []);
509
789
  if (!isSignedIn || !user) return null;
510
790
  const initials = user.name ? user.name.split(" ").map((n) => n[0]).join("").toUpperCase().slice(0, 2) : user.email[0].toUpperCase();
511
- return /* @__PURE__ */ jsxs3("div", { className: "githat-user-button", ref, children: [
791
+ return /* @__PURE__ */ jsxs4("div", { className: "githat-user-button", ref, children: [
512
792
  /* @__PURE__ */ jsx6("button", { className: "githat-avatar-trigger", onClick: () => setOpen(!open), "aria-label": "User menu", "aria-expanded": open, "aria-haspopup": "true", children: user.avatarUrl ? /* @__PURE__ */ jsx6("img", { src: user.avatarUrl, alt: user.name || "User avatar", className: "githat-avatar-img" }) : /* @__PURE__ */ jsx6("span", { className: "githat-avatar-initials", children: initials }) }),
513
- open && /* @__PURE__ */ jsxs3("div", { className: "githat-dropdown", role: "menu", children: [
514
- /* @__PURE__ */ jsxs3("div", { className: "githat-dropdown-header", children: [
793
+ open && /* @__PURE__ */ jsxs4("div", { className: "githat-dropdown", role: "menu", children: [
794
+ /* @__PURE__ */ jsxs4("div", { className: "githat-dropdown-header", children: [
515
795
  /* @__PURE__ */ jsx6("p", { className: "githat-dropdown-name", children: user.name }),
516
796
  /* @__PURE__ */ jsx6("p", { className: "githat-dropdown-email", children: user.email }),
517
797
  org && /* @__PURE__ */ jsx6("p", { className: "githat-dropdown-org", children: org.name })
@@ -527,7 +807,7 @@ function UserButton() {
527
807
 
528
808
  // src/components/OrgSwitcher.tsx
529
809
  import { useState as useState5, useEffect as useEffect3, useRef as useRef3 } from "react";
530
- import { jsx as jsx7, jsxs as jsxs4 } from "react/jsx-runtime";
810
+ import { jsx as jsx7, jsxs as jsxs5 } from "react/jsx-runtime";
531
811
  function OrgSwitcher() {
532
812
  const { org, isSignedIn, switchOrg } = useAuth();
533
813
  const githat = useGitHat();
@@ -550,12 +830,12 @@ function OrgSwitcher() {
550
830
  return () => document.removeEventListener("mousedown", handleClickOutside);
551
831
  }, []);
552
832
  if (!isSignedIn || !org || orgs.length < 2 && !orgsLoading) return null;
553
- return /* @__PURE__ */ jsxs4("div", { className: "githat-org-switcher", ref, children: [
554
- /* @__PURE__ */ jsxs4("button", { className: "githat-org-trigger", onClick: () => setOpen(!open), "aria-label": "Switch organization", "aria-expanded": open, "aria-haspopup": "true", children: [
833
+ return /* @__PURE__ */ jsxs5("div", { className: "githat-org-switcher", ref, children: [
834
+ /* @__PURE__ */ jsxs5("button", { className: "githat-org-trigger", onClick: () => setOpen(!open), "aria-label": "Switch organization", "aria-expanded": open, "aria-haspopup": "true", children: [
555
835
  /* @__PURE__ */ jsx7("span", { className: "githat-org-name", children: org.name }),
556
836
  /* @__PURE__ */ jsx7("span", { className: "githat-chevron", children: open ? "\u25B2" : "\u25BC" })
557
837
  ] }),
558
- open && /* @__PURE__ */ jsx7("div", { className: "githat-dropdown", role: "menu", children: orgsLoading ? /* @__PURE__ */ jsx7("div", { className: "githat-dropdown-item", "aria-busy": "true", children: "Loading..." }) : orgs.map((o) => /* @__PURE__ */ jsxs4(
838
+ open && /* @__PURE__ */ jsx7("div", { className: "githat-dropdown", role: "menu", children: orgsLoading ? /* @__PURE__ */ jsx7("div", { className: "githat-dropdown-item", "aria-busy": "true", children: "Loading..." }) : orgs.map((o) => /* @__PURE__ */ jsxs5(
559
839
  "button",
560
840
  {
561
841
  className: `githat-dropdown-item ${o.id === org.id ? "githat-dropdown-item-active" : ""}`,
@@ -577,7 +857,7 @@ function OrgSwitcher() {
577
857
 
578
858
  // src/components/VerifiedBadge.tsx
579
859
  import { useState as useState6, useEffect as useEffect4, useRef as useRef4 } from "react";
580
- import { jsxs as jsxs5 } from "react/jsx-runtime";
860
+ import { jsxs as jsxs6 } from "react/jsx-runtime";
581
861
  var CACHE_TTL = 5 * 60 * 1e3;
582
862
  var cache = /* @__PURE__ */ new Map();
583
863
  function VerifiedBadge({ type, identifier, label }) {
@@ -606,7 +886,7 @@ function VerifiedBadge({ type, identifier, label }) {
606
886
  };
607
887
  }, [type, identifier]);
608
888
  if (verified === null) return null;
609
- return /* @__PURE__ */ jsxs5("span", { className: `githat-badge ${verified ? "githat-badge-verified" : "githat-badge-unverified"}`, children: [
889
+ return /* @__PURE__ */ jsxs6("span", { className: `githat-badge ${verified ? "githat-badge-verified" : "githat-badge-unverified"}`, children: [
610
890
  verified ? "\u2713" : "\u2717",
611
891
  " ",
612
892
  label || (verified ? "Verified" : "Unverified")
@@ -628,17 +908,411 @@ function ProtectedRoute({ children, fallback }) {
628
908
  }
629
909
  return /* @__PURE__ */ jsx8(Fragment, { children });
630
910
  }
911
+
912
+ // src/components/ForgotPasswordForm.tsx
913
+ import { useState as useState7 } from "react";
914
+ import { jsx as jsx9, jsxs as jsxs7 } from "react/jsx-runtime";
915
+ function ForgotPasswordForm({
916
+ onSuccess,
917
+ onError,
918
+ signInUrl = "/sign-in"
919
+ }) {
920
+ const { forgotPassword } = useGitHat();
921
+ const [email, setEmail] = useState7("");
922
+ const [isLoading, setIsLoading] = useState7(false);
923
+ const [sent, setSent] = useState7(false);
924
+ const [error, setError] = useState7("");
925
+ const emailValid = /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);
926
+ const handleSubmit = async (e) => {
927
+ e.preventDefault();
928
+ if (!emailValid) {
929
+ setError("Please enter a valid email address");
930
+ return;
931
+ }
932
+ setIsLoading(true);
933
+ setError("");
934
+ try {
935
+ await forgotPassword(email);
936
+ setSent(true);
937
+ onSuccess?.(email);
938
+ } catch (err) {
939
+ const message = err instanceof Error ? err.message : "Failed to send reset email";
940
+ setError(message);
941
+ onError?.(err instanceof Error ? err : new Error(message));
942
+ } finally {
943
+ setIsLoading(false);
944
+ }
945
+ };
946
+ if (sent) {
947
+ return /* @__PURE__ */ jsxs7("div", { className: "githat-form-container", children: [
948
+ /* @__PURE__ */ jsxs7("div", { className: "githat-form-header", children: [
949
+ /* @__PURE__ */ jsx9("h2", { className: "githat-form-title", children: "Check your email" }),
950
+ /* @__PURE__ */ jsxs7("p", { className: "githat-form-subtitle", children: [
951
+ "We sent a password reset link to ",
952
+ /* @__PURE__ */ jsx9("strong", { children: email })
953
+ ] })
954
+ ] }),
955
+ /* @__PURE__ */ jsx9("a", { href: signInUrl, className: "githat-link", children: "Back to sign in" }),
956
+ /* @__PURE__ */ jsxs7("p", { className: "githat-powered-by", children: [
957
+ "Secured by ",
958
+ /* @__PURE__ */ jsx9("strong", { children: "GitHat" })
959
+ ] })
960
+ ] });
961
+ }
962
+ return /* @__PURE__ */ jsxs7("div", { className: "githat-form-container", children: [
963
+ /* @__PURE__ */ jsxs7("div", { className: "githat-form-header", children: [
964
+ /* @__PURE__ */ jsx9("h2", { className: "githat-form-title", children: "Forgot password" }),
965
+ /* @__PURE__ */ jsx9("p", { className: "githat-form-subtitle", children: "Enter your email and we'll send you a reset link" })
966
+ ] }),
967
+ error && /* @__PURE__ */ jsx9("div", { className: "githat-alert githat-alert-error", role: "alert", "aria-live": "polite", children: error }),
968
+ /* @__PURE__ */ jsxs7("form", { onSubmit: handleSubmit, className: "githat-form", "aria-label": "Forgot password form", children: [
969
+ /* @__PURE__ */ jsxs7("div", { className: "githat-field", children: [
970
+ /* @__PURE__ */ jsx9("label", { className: "githat-label", htmlFor: "githat-forgot-email", children: "Email" }),
971
+ /* @__PURE__ */ jsx9(
972
+ "input",
973
+ {
974
+ id: "githat-forgot-email",
975
+ className: "githat-input",
976
+ type: "email",
977
+ value: email,
978
+ onChange: (e) => setEmail(e.target.value),
979
+ placeholder: "you@example.com",
980
+ autoComplete: "email",
981
+ disabled: isLoading,
982
+ required: true
983
+ }
984
+ )
985
+ ] }),
986
+ /* @__PURE__ */ jsx9(
987
+ "button",
988
+ {
989
+ type: "submit",
990
+ className: "githat-button githat-button-primary",
991
+ disabled: isLoading || !email || email.length > 0 && !emailValid,
992
+ children: isLoading ? "Sending..." : "Send reset link"
993
+ }
994
+ )
995
+ ] }),
996
+ /* @__PURE__ */ jsxs7("p", { className: "githat-form-footer", children: [
997
+ "Remember your password? ",
998
+ /* @__PURE__ */ jsx9("a", { href: signInUrl, className: "githat-link", children: "Sign in" })
999
+ ] }),
1000
+ /* @__PURE__ */ jsxs7("p", { className: "githat-powered-by", children: [
1001
+ "Secured by ",
1002
+ /* @__PURE__ */ jsx9("strong", { children: "GitHat" })
1003
+ ] })
1004
+ ] });
1005
+ }
1006
+
1007
+ // src/components/ResetPasswordForm.tsx
1008
+ import { useState as useState8 } from "react";
1009
+ import { jsx as jsx10, jsxs as jsxs8 } from "react/jsx-runtime";
1010
+ function ResetPasswordForm({
1011
+ token,
1012
+ onSuccess,
1013
+ onError,
1014
+ signInUrl = "/sign-in",
1015
+ minPasswordLength = 8
1016
+ }) {
1017
+ const { resetPassword } = useGitHat();
1018
+ const [password, setPassword] = useState8("");
1019
+ const [confirm, setConfirm] = useState8("");
1020
+ const [isLoading, setIsLoading] = useState8(false);
1021
+ const [success, setSuccess] = useState8(false);
1022
+ const [error, setError] = useState8("");
1023
+ const handleSubmit = async (e) => {
1024
+ e.preventDefault();
1025
+ if (password !== confirm) {
1026
+ setError("Passwords do not match");
1027
+ return;
1028
+ }
1029
+ if (password.length < minPasswordLength) {
1030
+ setError(`Password must be at least ${minPasswordLength} characters`);
1031
+ return;
1032
+ }
1033
+ setIsLoading(true);
1034
+ setError("");
1035
+ try {
1036
+ await resetPassword(token, password);
1037
+ setSuccess(true);
1038
+ onSuccess?.();
1039
+ } catch (err) {
1040
+ const message = err instanceof Error ? err.message : "Failed to reset password";
1041
+ setError(message);
1042
+ onError?.(err instanceof Error ? err : new Error(message));
1043
+ } finally {
1044
+ setIsLoading(false);
1045
+ }
1046
+ };
1047
+ if (success) {
1048
+ return /* @__PURE__ */ jsxs8("div", { className: "githat-form-container", children: [
1049
+ /* @__PURE__ */ jsxs8("div", { className: "githat-form-header", children: [
1050
+ /* @__PURE__ */ jsx10("h2", { className: "githat-form-title", children: "Password reset!" }),
1051
+ /* @__PURE__ */ jsx10("p", { className: "githat-form-subtitle", children: "Your password has been successfully reset." })
1052
+ ] }),
1053
+ /* @__PURE__ */ jsx10("a", { href: signInUrl, className: "githat-button githat-button-primary", style: { display: "block", textAlign: "center", textDecoration: "none" }, children: "Sign in" }),
1054
+ /* @__PURE__ */ jsxs8("p", { className: "githat-powered-by", children: [
1055
+ "Secured by ",
1056
+ /* @__PURE__ */ jsx10("strong", { children: "GitHat" })
1057
+ ] })
1058
+ ] });
1059
+ }
1060
+ return /* @__PURE__ */ jsxs8("div", { className: "githat-form-container", children: [
1061
+ /* @__PURE__ */ jsxs8("div", { className: "githat-form-header", children: [
1062
+ /* @__PURE__ */ jsx10("h2", { className: "githat-form-title", children: "Reset password" }),
1063
+ /* @__PURE__ */ jsx10("p", { className: "githat-form-subtitle", children: "Enter your new password" })
1064
+ ] }),
1065
+ error && /* @__PURE__ */ jsx10("div", { className: "githat-alert githat-alert-error", role: "alert", "aria-live": "polite", children: error }),
1066
+ /* @__PURE__ */ jsxs8("form", { onSubmit: handleSubmit, className: "githat-form", "aria-label": "Reset password form", children: [
1067
+ /* @__PURE__ */ jsxs8("div", { className: "githat-field", children: [
1068
+ /* @__PURE__ */ jsx10("label", { className: "githat-label", htmlFor: "githat-reset-password", children: "New password" }),
1069
+ /* @__PURE__ */ jsx10(
1070
+ "input",
1071
+ {
1072
+ id: "githat-reset-password",
1073
+ className: "githat-input",
1074
+ type: "password",
1075
+ value: password,
1076
+ onChange: (e) => setPassword(e.target.value),
1077
+ placeholder: "Enter new password",
1078
+ autoComplete: "new-password",
1079
+ disabled: isLoading,
1080
+ required: true,
1081
+ minLength: minPasswordLength
1082
+ }
1083
+ )
1084
+ ] }),
1085
+ /* @__PURE__ */ jsxs8("div", { className: "githat-field", children: [
1086
+ /* @__PURE__ */ jsx10("label", { className: "githat-label", htmlFor: "githat-reset-confirm", children: "Confirm password" }),
1087
+ /* @__PURE__ */ jsx10(
1088
+ "input",
1089
+ {
1090
+ id: "githat-reset-confirm",
1091
+ className: "githat-input",
1092
+ type: "password",
1093
+ value: confirm,
1094
+ onChange: (e) => setConfirm(e.target.value),
1095
+ placeholder: "Confirm new password",
1096
+ autoComplete: "new-password",
1097
+ disabled: isLoading,
1098
+ required: true
1099
+ }
1100
+ )
1101
+ ] }),
1102
+ /* @__PURE__ */ jsx10(
1103
+ "button",
1104
+ {
1105
+ type: "submit",
1106
+ className: "githat-button githat-button-primary",
1107
+ disabled: isLoading || !password || !confirm,
1108
+ children: isLoading ? "Resetting..." : "Reset password"
1109
+ }
1110
+ )
1111
+ ] }),
1112
+ /* @__PURE__ */ jsxs8("p", { className: "githat-powered-by", children: [
1113
+ "Secured by ",
1114
+ /* @__PURE__ */ jsx10("strong", { children: "GitHat" })
1115
+ ] })
1116
+ ] });
1117
+ }
1118
+
1119
+ // src/components/VerifyEmailStatus.tsx
1120
+ import { useEffect as useEffect5, useState as useState9 } from "react";
1121
+ import { Fragment as Fragment2, jsx as jsx11, jsxs as jsxs9 } from "react/jsx-runtime";
1122
+ function VerifyEmailStatus({
1123
+ token,
1124
+ onSuccess,
1125
+ onError,
1126
+ signInUrl = "/sign-in",
1127
+ redirectDelay = 3e3
1128
+ }) {
1129
+ const { verifyEmail } = useGitHat();
1130
+ const [status, setStatus] = useState9("loading");
1131
+ const [error, setError] = useState9("");
1132
+ useEffect5(() => {
1133
+ if (!token) {
1134
+ setStatus("error");
1135
+ setError("Missing verification token");
1136
+ return;
1137
+ }
1138
+ verifyEmail(token).then(() => {
1139
+ setStatus("success");
1140
+ onSuccess?.();
1141
+ if (signInUrl && redirectDelay > 0) {
1142
+ setTimeout(() => {
1143
+ window.location.href = signInUrl;
1144
+ }, redirectDelay);
1145
+ }
1146
+ }).catch((err) => {
1147
+ setStatus("error");
1148
+ const message = err instanceof Error ? err.message : "Verification failed";
1149
+ setError(message);
1150
+ onError?.(err instanceof Error ? err : new Error(message));
1151
+ });
1152
+ }, [token, verifyEmail, onSuccess, onError, signInUrl, redirectDelay]);
1153
+ return /* @__PURE__ */ jsxs9("div", { className: "githat-form-container", children: [
1154
+ status === "loading" && /* @__PURE__ */ jsxs9("div", { className: "githat-form-header", children: [
1155
+ /* @__PURE__ */ jsx11("h2", { className: "githat-form-title", children: "Verifying email..." }),
1156
+ /* @__PURE__ */ jsx11("p", { className: "githat-form-subtitle", children: "Please wait while we verify your email address." })
1157
+ ] }),
1158
+ status === "success" && /* @__PURE__ */ jsxs9(Fragment2, { children: [
1159
+ /* @__PURE__ */ jsxs9("div", { className: "githat-form-header", children: [
1160
+ /* @__PURE__ */ jsx11("h2", { className: "githat-form-title", children: "Email verified!" }),
1161
+ /* @__PURE__ */ jsx11("p", { className: "githat-form-subtitle", children: "Your email has been successfully verified. Redirecting to sign in..." })
1162
+ ] }),
1163
+ /* @__PURE__ */ jsx11("a", { href: signInUrl, className: "githat-button githat-button-primary", style: { display: "block", textAlign: "center", textDecoration: "none" }, children: "Sign in now" })
1164
+ ] }),
1165
+ status === "error" && /* @__PURE__ */ jsxs9(Fragment2, { children: [
1166
+ /* @__PURE__ */ jsxs9("div", { className: "githat-form-header", children: [
1167
+ /* @__PURE__ */ jsx11("h2", { className: "githat-form-title", children: "Verification failed" }),
1168
+ /* @__PURE__ */ jsx11("p", { className: "githat-form-subtitle", children: error })
1169
+ ] }),
1170
+ /* @__PURE__ */ jsxs9("p", { className: "githat-form-footer", children: [
1171
+ "The link may have expired. ",
1172
+ /* @__PURE__ */ jsx11("a", { href: "/sign-up", className: "githat-link", children: "Try signing up again" })
1173
+ ] })
1174
+ ] }),
1175
+ /* @__PURE__ */ jsxs9("p", { className: "githat-powered-by", children: [
1176
+ "Secured by ",
1177
+ /* @__PURE__ */ jsx11("strong", { children: "GitHat" })
1178
+ ] })
1179
+ ] });
1180
+ }
1181
+
1182
+ // src/components/ChangePasswordForm.tsx
1183
+ import { useState as useState10 } from "react";
1184
+ import { jsx as jsx12, jsxs as jsxs10 } from "react/jsx-runtime";
1185
+ function ChangePasswordForm({
1186
+ onSuccess,
1187
+ onError,
1188
+ minPasswordLength = 8
1189
+ }) {
1190
+ const { changePassword } = useGitHat();
1191
+ const [current, setCurrent] = useState10("");
1192
+ const [newPass, setNewPass] = useState10("");
1193
+ const [confirm, setConfirm] = useState10("");
1194
+ const [isLoading, setIsLoading] = useState10(false);
1195
+ const [error, setError] = useState10("");
1196
+ const [success, setSuccess] = useState10(false);
1197
+ const handleSubmit = async (e) => {
1198
+ e.preventDefault();
1199
+ if (newPass !== confirm) {
1200
+ setError("Passwords do not match");
1201
+ return;
1202
+ }
1203
+ if (newPass.length < minPasswordLength) {
1204
+ setError(`Password must be at least ${minPasswordLength} characters`);
1205
+ return;
1206
+ }
1207
+ setIsLoading(true);
1208
+ setError("");
1209
+ try {
1210
+ await changePassword(current, newPass);
1211
+ setSuccess(true);
1212
+ setCurrent("");
1213
+ setNewPass("");
1214
+ setConfirm("");
1215
+ onSuccess?.();
1216
+ } catch (err) {
1217
+ const message = err instanceof Error ? err.message : "Failed to change password";
1218
+ setError(message);
1219
+ onError?.(err instanceof Error ? err : new Error(message));
1220
+ } finally {
1221
+ setIsLoading(false);
1222
+ }
1223
+ };
1224
+ return /* @__PURE__ */ jsxs10("div", { className: "githat-form-container", children: [
1225
+ /* @__PURE__ */ jsxs10("div", { className: "githat-form-header", children: [
1226
+ /* @__PURE__ */ jsx12("h2", { className: "githat-form-title", children: "Change password" }),
1227
+ /* @__PURE__ */ jsx12("p", { className: "githat-form-subtitle", children: "Update your account password" })
1228
+ ] }),
1229
+ success && /* @__PURE__ */ jsx12("div", { className: "githat-alert githat-alert-success", role: "status", "aria-live": "polite", children: "Password changed successfully!" }),
1230
+ error && /* @__PURE__ */ jsx12("div", { className: "githat-alert githat-alert-error", role: "alert", "aria-live": "polite", children: error }),
1231
+ /* @__PURE__ */ jsxs10("form", { onSubmit: handleSubmit, className: "githat-form", "aria-label": "Change password form", children: [
1232
+ /* @__PURE__ */ jsxs10("div", { className: "githat-field", children: [
1233
+ /* @__PURE__ */ jsx12("label", { className: "githat-label", htmlFor: "githat-change-current", children: "Current password" }),
1234
+ /* @__PURE__ */ jsx12(
1235
+ "input",
1236
+ {
1237
+ id: "githat-change-current",
1238
+ className: "githat-input",
1239
+ type: "password",
1240
+ value: current,
1241
+ onChange: (e) => setCurrent(e.target.value),
1242
+ placeholder: "Enter current password",
1243
+ autoComplete: "current-password",
1244
+ disabled: isLoading,
1245
+ required: true
1246
+ }
1247
+ )
1248
+ ] }),
1249
+ /* @__PURE__ */ jsxs10("div", { className: "githat-field", children: [
1250
+ /* @__PURE__ */ jsx12("label", { className: "githat-label", htmlFor: "githat-change-new", children: "New password" }),
1251
+ /* @__PURE__ */ jsx12(
1252
+ "input",
1253
+ {
1254
+ id: "githat-change-new",
1255
+ className: "githat-input",
1256
+ type: "password",
1257
+ value: newPass,
1258
+ onChange: (e) => setNewPass(e.target.value),
1259
+ placeholder: "Enter new password",
1260
+ autoComplete: "new-password",
1261
+ disabled: isLoading,
1262
+ required: true,
1263
+ minLength: minPasswordLength
1264
+ }
1265
+ )
1266
+ ] }),
1267
+ /* @__PURE__ */ jsxs10("div", { className: "githat-field", children: [
1268
+ /* @__PURE__ */ jsx12("label", { className: "githat-label", htmlFor: "githat-change-confirm", children: "Confirm new password" }),
1269
+ /* @__PURE__ */ jsx12(
1270
+ "input",
1271
+ {
1272
+ id: "githat-change-confirm",
1273
+ className: "githat-input",
1274
+ type: "password",
1275
+ value: confirm,
1276
+ onChange: (e) => setConfirm(e.target.value),
1277
+ placeholder: "Confirm new password",
1278
+ autoComplete: "new-password",
1279
+ disabled: isLoading,
1280
+ required: true
1281
+ }
1282
+ )
1283
+ ] }),
1284
+ /* @__PURE__ */ jsx12(
1285
+ "button",
1286
+ {
1287
+ type: "submit",
1288
+ className: "githat-button githat-button-primary",
1289
+ disabled: isLoading || !current || !newPass || !confirm,
1290
+ children: isLoading ? "Changing..." : "Change password"
1291
+ }
1292
+ )
1293
+ ] }),
1294
+ /* @__PURE__ */ jsxs10("p", { className: "githat-powered-by", children: [
1295
+ "Secured by ",
1296
+ /* @__PURE__ */ jsx12("strong", { children: "GitHat" })
1297
+ ] })
1298
+ ] });
1299
+ }
631
1300
  export {
1301
+ ChangePasswordForm,
1302
+ ForgotPasswordForm,
632
1303
  GitHatProvider,
633
1304
  OrgSwitcher,
634
1305
  ProtectedRoute,
1306
+ ResetPasswordForm,
635
1307
  SignInButton,
636
1308
  SignInForm,
637
1309
  SignUpButton,
638
1310
  SignUpForm,
639
1311
  UserButton,
640
1312
  VerifiedBadge,
1313
+ VerifyEmailStatus,
641
1314
  useAuth,
1315
+ useData,
642
1316
  useGitHat
643
1317
  };
644
1318
  //# sourceMappingURL=index.mjs.map