@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.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,36 +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
- const response = await fetch(url, { ...options, headers });
80
+ let response;
81
+ try {
82
+ response = await fetch(url, {
83
+ ...fetchOptions,
84
+ headers,
85
+ credentials: useCookies ? "include" : "same-origin"
86
+ });
87
+ } catch (networkError) {
88
+ if (networkError instanceof TypeError) {
89
+ const isMissingKey = !appKey || !appKey.startsWith("pk_live_") && !appKey.startsWith("pk_test_");
90
+ const isLocalhost = typeof window !== "undefined" && (window.location.hostname === "localhost" || window.location.hostname === "127.0.0.1");
91
+ if (isMissingKey && !isLocalhost) {
92
+ throw new Error(
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."
99
+ );
100
+ }
101
+ throw new Error(
102
+ "GitHat: API request failed. Verify your publishable key and app domain at https://githat.io/dashboard/apps"
103
+ );
104
+ }
105
+ throw networkError;
106
+ }
73
107
  if (response.status === 401) {
74
108
  if (!_refreshPromise) {
75
- _refreshPromise = refreshTokens(apiUrl, appKey).finally(() => {
109
+ _refreshPromise = refreshTokens(apiUrl, appKey, useCookies).finally(() => {
76
110
  _refreshPromise = null;
77
111
  });
78
112
  }
79
113
  const refreshed = await _refreshPromise;
80
114
  if (refreshed) {
81
- const newToken = localStorage.getItem(TOKEN_KEYS.accessToken);
115
+ const newToken = !useCookies && typeof window !== "undefined" ? localStorage.getItem(TOKEN_KEYS.accessToken) : null;
82
116
  const retryResponse = await fetch(url, {
83
- ...options,
117
+ ...fetchOptions,
84
118
  headers: {
85
119
  ...headers,
86
120
  ...newToken && { Authorization: `Bearer ${newToken}` }
87
- }
121
+ },
122
+ credentials: useCookies ? "include" : "same-origin"
88
123
  });
89
124
  const retryData = await retryResponse.json();
90
125
  if (!retryResponse.ok) throw new Error(retryData.error || "Request failed");
@@ -101,42 +136,110 @@ function createClient(apiUrl, appKey) {
101
136
  }
102
137
 
103
138
  // src/provider.tsx
104
- import { jsx } from "react/jsx-runtime";
139
+ import { jsx, jsxs } from "react/jsx-runtime";
105
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
+ }
106
172
  function GitHatProvider({ config: rawConfig, children }) {
107
173
  const config = useMemo(() => resolveConfig(rawConfig), [rawConfig]);
108
- 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
+ }, []);
109
194
  const [user, setUser] = useState(null);
110
195
  const [org, setOrg] = useState(null);
111
196
  const [isSignedIn, setIsSignedIn] = useState(false);
112
197
  const [isLoading, setIsLoading] = useState(true);
113
198
  const [authError, setAuthError] = useState(null);
114
199
  useEffect(() => {
115
- const token = localStorage.getItem(TOKEN_KEYS.accessToken);
116
- const storedUser = localStorage.getItem(TOKEN_KEYS.user);
117
- if (token && storedUser) {
118
- clientRef.current.fetchApi("/auth/me").then((data) => {
119
- const u = data.user || JSON.parse(storedUser);
120
- setUser(u);
121
- const storedOrg = localStorage.getItem(TOKEN_KEYS.org);
122
- setOrg(data.currentOrg || (storedOrg ? JSON.parse(storedOrg) : null));
123
- setIsSignedIn(true);
124
- setAuthError(null);
125
- }).catch((err) => {
126
- 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") {
127
226
  clientRef.current.clearAuth();
128
- } else {
129
- try {
130
- setUser(JSON.parse(storedUser));
131
- } 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
+ }
132
235
  }
133
- setAuthError(err.message || "Failed to verify session");
236
+ setAuthError(error.message || "Failed to verify session");
134
237
  }
135
- }).finally(() => setIsLoading(false));
136
- } else {
238
+ }
137
239
  setIsLoading(false);
138
- }
139
- }, []);
240
+ };
241
+ validateSession();
242
+ }, [useCookies]);
140
243
  useEffect(() => {
141
244
  const handleAuthChanged = (e) => {
142
245
  const detail = e.detail;
@@ -154,30 +257,36 @@ function GitHatProvider({ config: rawConfig, children }) {
154
257
  return () => window.removeEventListener("githat:auth-changed", handleAuthChanged);
155
258
  }, []);
156
259
  const signIn = useCallback(async (email, password) => {
157
- 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, {
158
262
  method: "POST",
159
263
  body: JSON.stringify({ email, password })
160
264
  });
161
- localStorage.setItem(TOKEN_KEYS.accessToken, data.accessToken);
162
- localStorage.setItem(TOKEN_KEYS.refreshToken, data.refreshToken);
163
- localStorage.setItem(TOKEN_KEYS.user, JSON.stringify(data.user));
164
- 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
+ }
165
271
  setUser(data.user);
166
272
  setOrg(data.org || null);
167
273
  setIsSignedIn(true);
168
274
  window.dispatchEvent(new CustomEvent("githat:auth-changed", {
169
275
  detail: { user: data.user, org: data.org, signedIn: true }
170
276
  }));
171
- }, []);
277
+ }, [useCookies]);
172
278
  const signUp = useCallback(async (signUpData) => {
173
- 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, {
174
281
  method: "POST",
175
282
  body: JSON.stringify(signUpData)
176
283
  });
177
- localStorage.setItem(TOKEN_KEYS.accessToken, data.accessToken);
178
- localStorage.setItem(TOKEN_KEYS.refreshToken, data.refreshToken);
179
- localStorage.setItem(TOKEN_KEYS.user, JSON.stringify(data.user));
180
- 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
+ }
181
290
  setUser(data.user);
182
291
  setOrg(data.org || null);
183
292
  setIsSignedIn(true);
@@ -185,10 +294,11 @@ function GitHatProvider({ config: rawConfig, children }) {
185
294
  detail: { user: data.user, org: data.org, signedIn: true }
186
295
  }));
187
296
  return { requiresVerification: !data.user.emailVerified, email: signUpData.email };
188
- }, []);
297
+ }, [useCookies]);
189
298
  const signOut = useCallback(async () => {
190
299
  try {
191
- 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" });
192
302
  } catch {
193
303
  }
194
304
  clientRef.current.clearAuth();
@@ -198,22 +308,24 @@ function GitHatProvider({ config: rawConfig, children }) {
198
308
  if (typeof window !== "undefined" && config.afterSignOutUrl) {
199
309
  window.location.href = config.afterSignOutUrl;
200
310
  }
201
- }, [config.afterSignOutUrl]);
311
+ }, [config.afterSignOutUrl, useCookies]);
202
312
  const switchOrg = useCallback(async (orgId) => {
203
313
  try {
204
- const data = await clientRef.current.fetchApi(`/user/orgs/${orgId}/switch`, { method: "POST" });
205
- if (data.accessToken) localStorage.setItem(TOKEN_KEYS.accessToken, data.accessToken);
206
- if (data.refreshToken) localStorage.setItem(TOKEN_KEYS.refreshToken, data.refreshToken);
207
- const orgData = data.org;
208
- localStorage.setItem(TOKEN_KEYS.org, JSON.stringify(orgData));
209
- 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);
210
322
  window.dispatchEvent(new CustomEvent("githat:auth-changed", {
211
- detail: { user, org: orgData, signedIn: true }
323
+ detail: { user, org: data.org, signedIn: true }
212
324
  }));
213
325
  } catch (e) {
214
326
  console.error("Org switch failed:", e);
215
327
  }
216
- }, [user]);
328
+ }, [user, useCookies]);
217
329
  const value = useMemo(() => ({
218
330
  user,
219
331
  org,
@@ -226,11 +338,14 @@ function GitHatProvider({ config: rawConfig, children }) {
226
338
  signOut,
227
339
  switchOrg
228
340
  }), [user, org, isSignedIn, isLoading, authError, config, signIn, signUp, signOut, switchOrg]);
229
- return /* @__PURE__ */ jsx(GitHatContext.Provider, { value, children });
341
+ return /* @__PURE__ */ jsxs(GitHatContext.Provider, { value, children: [
342
+ devMode && /* @__PURE__ */ jsx(DevModeBanner, {}),
343
+ children
344
+ ] });
230
345
  }
231
346
 
232
347
  // src/hooks.ts
233
- import { useContext, useMemo as useMemo2 } from "react";
348
+ import { useContext, useMemo as useMemo2, useCallback as useCallback2 } from "react";
234
349
  function useAuth() {
235
350
  const ctx = useContext(GitHatContext);
236
351
  if (!ctx) throw new Error("useAuth must be used within a <GitHatProvider>");
@@ -242,17 +357,199 @@ function useGitHat() {
242
357
  () => createClient(ctx.config.apiUrl, ctx.config.publishableKey),
243
358
  [ctx.config.apiUrl, ctx.config.publishableKey]
244
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
+ );
245
455
  return {
246
456
  fetch: client.fetchApi,
247
457
  getUserOrgs: () => client.fetchApi("/user/orgs"),
248
458
  verifyMCP: (domain) => client.fetchApi(`/verify/mcp/${domain}`),
249
- 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
250
469
  };
251
470
  }
252
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
+
253
550
  // src/components/SignInForm.tsx
254
551
  import { useState as useState2 } from "react";
255
- import { jsx as jsx2, jsxs } from "react/jsx-runtime";
552
+ import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
256
553
  function SignInForm({ onSuccess, signUpUrl, forgotPasswordUrl }) {
257
554
  const { signIn, config } = useAuth();
258
555
  const [email, setEmail] = useState2("");
@@ -282,14 +579,14 @@ function SignInForm({ onSuccess, signUpUrl, forgotPasswordUrl }) {
282
579
  setLoading(false);
283
580
  }
284
581
  };
285
- return /* @__PURE__ */ jsxs("div", { className: "githat-form-container", children: [
286
- /* @__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: [
287
584
  /* @__PURE__ */ jsx2("h2", { className: "githat-form-title", children: "Sign in" }),
288
585
  /* @__PURE__ */ jsx2("p", { className: "githat-form-subtitle", children: "Welcome back to GitHat" })
289
586
  ] }),
290
587
  error && /* @__PURE__ */ jsx2("div", { className: "githat-alert githat-alert-error", role: "alert", "aria-live": "polite", children: error }),
291
- /* @__PURE__ */ jsxs("form", { onSubmit: handleSubmit, className: "githat-form", "aria-label": "Sign in form", children: [
292
- /* @__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: [
293
590
  /* @__PURE__ */ jsx2("label", { className: "githat-label", htmlFor: "githat-signin-email", children: "Email" }),
294
591
  /* @__PURE__ */ jsx2(
295
592
  "input",
@@ -305,7 +602,7 @@ function SignInForm({ onSuccess, signUpUrl, forgotPasswordUrl }) {
305
602
  }
306
603
  )
307
604
  ] }),
308
- /* @__PURE__ */ jsxs("div", { className: "githat-field", children: [
605
+ /* @__PURE__ */ jsxs2("div", { className: "githat-field", children: [
309
606
  /* @__PURE__ */ jsx2("label", { className: "githat-label", htmlFor: "githat-signin-password", children: "Password" }),
310
607
  /* @__PURE__ */ jsx2(
311
608
  "input",
@@ -332,11 +629,11 @@ function SignInForm({ onSuccess, signUpUrl, forgotPasswordUrl }) {
332
629
  }
333
630
  )
334
631
  ] }),
335
- signUpUrl && /* @__PURE__ */ jsxs("p", { className: "githat-form-footer", children: [
632
+ signUpUrl && /* @__PURE__ */ jsxs2("p", { className: "githat-form-footer", children: [
336
633
  "Don't have an account? ",
337
634
  /* @__PURE__ */ jsx2("a", { href: signUpUrl, className: "githat-link", children: "Sign up" })
338
635
  ] }),
339
- /* @__PURE__ */ jsxs("p", { className: "githat-powered-by", children: [
636
+ /* @__PURE__ */ jsxs2("p", { className: "githat-powered-by", children: [
340
637
  "Secured by ",
341
638
  /* @__PURE__ */ jsx2("strong", { children: "GitHat" })
342
639
  ] })
@@ -345,7 +642,7 @@ function SignInForm({ onSuccess, signUpUrl, forgotPasswordUrl }) {
345
642
 
346
643
  // src/components/SignUpForm.tsx
347
644
  import { useState as useState3 } from "react";
348
- import { jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
645
+ import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
349
646
  function SignUpForm({ onSuccess, signInUrl }) {
350
647
  const { signUp, config } = useAuth();
351
648
  const [name, setName] = useState3("");
@@ -380,14 +677,14 @@ function SignUpForm({ onSuccess, signInUrl }) {
380
677
  setLoading(false);
381
678
  }
382
679
  };
383
- return /* @__PURE__ */ jsxs2("div", { className: "githat-form-container", children: [
384
- /* @__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: [
385
682
  /* @__PURE__ */ jsx3("h2", { className: "githat-form-title", children: "Create an account" }),
386
683
  /* @__PURE__ */ jsx3("p", { className: "githat-form-subtitle", children: "Get started with GitHat" })
387
684
  ] }),
388
685
  error && /* @__PURE__ */ jsx3("div", { className: "githat-alert githat-alert-error", role: "alert", "aria-live": "polite", children: error }),
389
- /* @__PURE__ */ jsxs2("form", { onSubmit: handleSubmit, className: "githat-form", "aria-label": "Sign up form", children: [
390
- /* @__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: [
391
688
  /* @__PURE__ */ jsx3("label", { className: "githat-label", htmlFor: "githat-signup-name", children: "Full name" }),
392
689
  /* @__PURE__ */ jsx3(
393
690
  "input",
@@ -403,7 +700,7 @@ function SignUpForm({ onSuccess, signInUrl }) {
403
700
  }
404
701
  )
405
702
  ] }),
406
- /* @__PURE__ */ jsxs2("div", { className: "githat-field", children: [
703
+ /* @__PURE__ */ jsxs3("div", { className: "githat-field", children: [
407
704
  /* @__PURE__ */ jsx3("label", { className: "githat-label", htmlFor: "githat-signup-email", children: "Email" }),
408
705
  /* @__PURE__ */ jsx3(
409
706
  "input",
@@ -419,7 +716,7 @@ function SignUpForm({ onSuccess, signInUrl }) {
419
716
  }
420
717
  )
421
718
  ] }),
422
- /* @__PURE__ */ jsxs2("div", { className: "githat-field", children: [
719
+ /* @__PURE__ */ jsxs3("div", { className: "githat-field", children: [
423
720
  /* @__PURE__ */ jsx3("label", { className: "githat-label", htmlFor: "githat-signup-password", children: "Password" }),
424
721
  /* @__PURE__ */ jsx3(
425
722
  "input",
@@ -446,11 +743,11 @@ function SignUpForm({ onSuccess, signInUrl }) {
446
743
  }
447
744
  )
448
745
  ] }),
449
- signInUrl && /* @__PURE__ */ jsxs2("p", { className: "githat-form-footer", children: [
746
+ signInUrl && /* @__PURE__ */ jsxs3("p", { className: "githat-form-footer", children: [
450
747
  "Already have an account? ",
451
748
  /* @__PURE__ */ jsx3("a", { href: signInUrl, className: "githat-link", children: "Sign in" })
452
749
  ] }),
453
- /* @__PURE__ */ jsxs2("p", { className: "githat-powered-by", children: [
750
+ /* @__PURE__ */ jsxs3("p", { className: "githat-powered-by", children: [
454
751
  "Secured by ",
455
752
  /* @__PURE__ */ jsx3("strong", { children: "GitHat" })
456
753
  ] })
@@ -477,7 +774,7 @@ function SignUpButton({ className, children, href }) {
477
774
 
478
775
  // src/components/UserButton.tsx
479
776
  import { useState as useState4, useRef as useRef2, useEffect as useEffect2 } from "react";
480
- import { jsx as jsx6, jsxs as jsxs3 } from "react/jsx-runtime";
777
+ import { jsx as jsx6, jsxs as jsxs4 } from "react/jsx-runtime";
481
778
  function UserButton() {
482
779
  const { user, org, isSignedIn, signOut } = useAuth();
483
780
  const [open, setOpen] = useState4(false);
@@ -491,10 +788,10 @@ function UserButton() {
491
788
  }, []);
492
789
  if (!isSignedIn || !user) return null;
493
790
  const initials = user.name ? user.name.split(" ").map((n) => n[0]).join("").toUpperCase().slice(0, 2) : user.email[0].toUpperCase();
494
- return /* @__PURE__ */ jsxs3("div", { className: "githat-user-button", ref, children: [
791
+ return /* @__PURE__ */ jsxs4("div", { className: "githat-user-button", ref, children: [
495
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 }) }),
496
- open && /* @__PURE__ */ jsxs3("div", { className: "githat-dropdown", role: "menu", children: [
497
- /* @__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: [
498
795
  /* @__PURE__ */ jsx6("p", { className: "githat-dropdown-name", children: user.name }),
499
796
  /* @__PURE__ */ jsx6("p", { className: "githat-dropdown-email", children: user.email }),
500
797
  org && /* @__PURE__ */ jsx6("p", { className: "githat-dropdown-org", children: org.name })
@@ -510,7 +807,7 @@ function UserButton() {
510
807
 
511
808
  // src/components/OrgSwitcher.tsx
512
809
  import { useState as useState5, useEffect as useEffect3, useRef as useRef3 } from "react";
513
- import { jsx as jsx7, jsxs as jsxs4 } from "react/jsx-runtime";
810
+ import { jsx as jsx7, jsxs as jsxs5 } from "react/jsx-runtime";
514
811
  function OrgSwitcher() {
515
812
  const { org, isSignedIn, switchOrg } = useAuth();
516
813
  const githat = useGitHat();
@@ -533,12 +830,12 @@ function OrgSwitcher() {
533
830
  return () => document.removeEventListener("mousedown", handleClickOutside);
534
831
  }, []);
535
832
  if (!isSignedIn || !org || orgs.length < 2 && !orgsLoading) return null;
536
- return /* @__PURE__ */ jsxs4("div", { className: "githat-org-switcher", ref, children: [
537
- /* @__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: [
538
835
  /* @__PURE__ */ jsx7("span", { className: "githat-org-name", children: org.name }),
539
836
  /* @__PURE__ */ jsx7("span", { className: "githat-chevron", children: open ? "\u25B2" : "\u25BC" })
540
837
  ] }),
541
- 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(
542
839
  "button",
543
840
  {
544
841
  className: `githat-dropdown-item ${o.id === org.id ? "githat-dropdown-item-active" : ""}`,
@@ -560,7 +857,7 @@ function OrgSwitcher() {
560
857
 
561
858
  // src/components/VerifiedBadge.tsx
562
859
  import { useState as useState6, useEffect as useEffect4, useRef as useRef4 } from "react";
563
- import { jsxs as jsxs5 } from "react/jsx-runtime";
860
+ import { jsxs as jsxs6 } from "react/jsx-runtime";
564
861
  var CACHE_TTL = 5 * 60 * 1e3;
565
862
  var cache = /* @__PURE__ */ new Map();
566
863
  function VerifiedBadge({ type, identifier, label }) {
@@ -589,7 +886,7 @@ function VerifiedBadge({ type, identifier, label }) {
589
886
  };
590
887
  }, [type, identifier]);
591
888
  if (verified === null) return null;
592
- 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: [
593
890
  verified ? "\u2713" : "\u2717",
594
891
  " ",
595
892
  label || (verified ? "Verified" : "Unverified")
@@ -611,17 +908,411 @@ function ProtectedRoute({ children, fallback }) {
611
908
  }
612
909
  return /* @__PURE__ */ jsx8(Fragment, { children });
613
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
+ }
614
1300
  export {
1301
+ ChangePasswordForm,
1302
+ ForgotPasswordForm,
615
1303
  GitHatProvider,
616
1304
  OrgSwitcher,
617
1305
  ProtectedRoute,
1306
+ ResetPasswordForm,
618
1307
  SignInButton,
619
1308
  SignInForm,
620
1309
  SignUpButton,
621
1310
  SignUpForm,
622
1311
  UserButton,
623
1312
  VerifiedBadge,
1313
+ VerifyEmailStatus,
624
1314
  useAuth,
1315
+ useData,
625
1316
  useGitHat
626
1317
  };
627
1318
  //# sourceMappingURL=index.mjs.map