@githat/nextjs 0.2.0
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/README.md +510 -0
- package/dist/githat.css +334 -0
- package/dist/index.d.mts +120 -0
- package/dist/index.d.ts +120 -0
- package/dist/index.js +663 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +627 -0
- package/dist/index.mjs.map +1 -0
- package/dist/middleware.d.mts +10 -0
- package/dist/middleware.d.ts +10 -0
- package/dist/middleware.js +54 -0
- package/dist/middleware.js.map +1 -0
- package/dist/middleware.mjs +29 -0
- package/dist/middleware.mjs.map +1 -0
- package/package.json +50 -0
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,627 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
// src/provider.tsx
|
|
4
|
+
import { createContext, useState, useEffect, useCallback, useMemo, useRef } from "react";
|
|
5
|
+
|
|
6
|
+
// src/config.ts
|
|
7
|
+
var DEFAULT_API_URL = "https://api.githat.io";
|
|
8
|
+
var TOKEN_KEYS = {
|
|
9
|
+
accessToken: "githat_access_token",
|
|
10
|
+
refreshToken: "githat_refresh_token",
|
|
11
|
+
user: "githat_user",
|
|
12
|
+
org: "githat_org"
|
|
13
|
+
};
|
|
14
|
+
function resolveConfig(config) {
|
|
15
|
+
return {
|
|
16
|
+
publishableKey: config.publishableKey,
|
|
17
|
+
apiUrl: config.apiUrl || DEFAULT_API_URL,
|
|
18
|
+
signInUrl: config.signInUrl || "/sign-in",
|
|
19
|
+
signUpUrl: config.signUpUrl || "/sign-up",
|
|
20
|
+
afterSignInUrl: config.afterSignInUrl || "/dashboard",
|
|
21
|
+
afterSignOutUrl: config.afterSignOutUrl || "/"
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// src/client.ts
|
|
26
|
+
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;
|
|
30
|
+
let orgId = null;
|
|
31
|
+
try {
|
|
32
|
+
const orgStr = localStorage.getItem(TOKEN_KEYS.org);
|
|
33
|
+
if (orgStr) orgId = JSON.parse(orgStr).id;
|
|
34
|
+
} catch {
|
|
35
|
+
}
|
|
36
|
+
try {
|
|
37
|
+
const res = await fetch(`${apiUrl}/auth/refresh`, {
|
|
38
|
+
method: "POST",
|
|
39
|
+
headers: {
|
|
40
|
+
"Content-Type": "application/json",
|
|
41
|
+
"X-GitHat-App-Key": appKey
|
|
42
|
+
},
|
|
43
|
+
body: JSON.stringify({ refreshToken, orgId })
|
|
44
|
+
});
|
|
45
|
+
if (!res.ok) return false;
|
|
46
|
+
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);
|
|
49
|
+
if (data.org) localStorage.setItem(TOKEN_KEYS.org, JSON.stringify(data.org));
|
|
50
|
+
return true;
|
|
51
|
+
} catch {
|
|
52
|
+
return false;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
function clearAuth() {
|
|
56
|
+
if (typeof window === "undefined") return;
|
|
57
|
+
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
|
+
}));
|
|
61
|
+
}
|
|
62
|
+
function createClient(apiUrl, appKey) {
|
|
63
|
+
async function fetchApi(endpoint, options = {}) {
|
|
64
|
+
const url = `${apiUrl}${endpoint}`;
|
|
65
|
+
const token = typeof window !== "undefined" ? localStorage.getItem(TOKEN_KEYS.accessToken) : null;
|
|
66
|
+
const headers = {
|
|
67
|
+
"Content-Type": "application/json",
|
|
68
|
+
"X-GitHat-App-Key": appKey,
|
|
69
|
+
...token && { Authorization: `Bearer ${token}` },
|
|
70
|
+
...options.headers
|
|
71
|
+
};
|
|
72
|
+
const response = await fetch(url, { ...options, headers });
|
|
73
|
+
if (response.status === 401) {
|
|
74
|
+
if (!_refreshPromise) {
|
|
75
|
+
_refreshPromise = refreshTokens(apiUrl, appKey).finally(() => {
|
|
76
|
+
_refreshPromise = null;
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
const refreshed = await _refreshPromise;
|
|
80
|
+
if (refreshed) {
|
|
81
|
+
const newToken = localStorage.getItem(TOKEN_KEYS.accessToken);
|
|
82
|
+
const retryResponse = await fetch(url, {
|
|
83
|
+
...options,
|
|
84
|
+
headers: {
|
|
85
|
+
...headers,
|
|
86
|
+
...newToken && { Authorization: `Bearer ${newToken}` }
|
|
87
|
+
}
|
|
88
|
+
});
|
|
89
|
+
const retryData = await retryResponse.json();
|
|
90
|
+
if (!retryResponse.ok) throw new Error(retryData.error || "Request failed");
|
|
91
|
+
return retryData;
|
|
92
|
+
}
|
|
93
|
+
clearAuth();
|
|
94
|
+
throw new Error("Session expired");
|
|
95
|
+
}
|
|
96
|
+
const data = await response.json();
|
|
97
|
+
if (!response.ok) throw new Error(data.error || "Request failed");
|
|
98
|
+
return data;
|
|
99
|
+
}
|
|
100
|
+
return { fetchApi, clearAuth };
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// src/provider.tsx
|
|
104
|
+
import { jsx } from "react/jsx-runtime";
|
|
105
|
+
var GitHatContext = createContext(null);
|
|
106
|
+
function GitHatProvider({ config: rawConfig, children }) {
|
|
107
|
+
const config = useMemo(() => resolveConfig(rawConfig), [rawConfig]);
|
|
108
|
+
const clientRef = useRef(createClient(config.apiUrl, config.publishableKey));
|
|
109
|
+
const [user, setUser] = useState(null);
|
|
110
|
+
const [org, setOrg] = useState(null);
|
|
111
|
+
const [isSignedIn, setIsSignedIn] = useState(false);
|
|
112
|
+
const [isLoading, setIsLoading] = useState(true);
|
|
113
|
+
const [authError, setAuthError] = useState(null);
|
|
114
|
+
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") {
|
|
127
|
+
clientRef.current.clearAuth();
|
|
128
|
+
} else {
|
|
129
|
+
try {
|
|
130
|
+
setUser(JSON.parse(storedUser));
|
|
131
|
+
} catch {
|
|
132
|
+
}
|
|
133
|
+
setAuthError(err.message || "Failed to verify session");
|
|
134
|
+
}
|
|
135
|
+
}).finally(() => setIsLoading(false));
|
|
136
|
+
} else {
|
|
137
|
+
setIsLoading(false);
|
|
138
|
+
}
|
|
139
|
+
}, []);
|
|
140
|
+
useEffect(() => {
|
|
141
|
+
const handleAuthChanged = (e) => {
|
|
142
|
+
const detail = e.detail;
|
|
143
|
+
if (detail?.signedIn === false) {
|
|
144
|
+
setIsSignedIn(false);
|
|
145
|
+
setUser(null);
|
|
146
|
+
setOrg(null);
|
|
147
|
+
} else if (detail?.signedIn === true && detail?.user) {
|
|
148
|
+
setUser(detail.user);
|
|
149
|
+
setIsSignedIn(true);
|
|
150
|
+
if (detail.org) setOrg(detail.org);
|
|
151
|
+
}
|
|
152
|
+
};
|
|
153
|
+
window.addEventListener("githat:auth-changed", handleAuthChanged);
|
|
154
|
+
return () => window.removeEventListener("githat:auth-changed", handleAuthChanged);
|
|
155
|
+
}, []);
|
|
156
|
+
const signIn = useCallback(async (email, password) => {
|
|
157
|
+
const data = await clientRef.current.fetchApi("/auth/login", {
|
|
158
|
+
method: "POST",
|
|
159
|
+
body: JSON.stringify({ email, password })
|
|
160
|
+
});
|
|
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));
|
|
165
|
+
setUser(data.user);
|
|
166
|
+
setOrg(data.org || null);
|
|
167
|
+
setIsSignedIn(true);
|
|
168
|
+
window.dispatchEvent(new CustomEvent("githat:auth-changed", {
|
|
169
|
+
detail: { user: data.user, org: data.org, signedIn: true }
|
|
170
|
+
}));
|
|
171
|
+
}, []);
|
|
172
|
+
const signUp = useCallback(async (signUpData) => {
|
|
173
|
+
const data = await clientRef.current.fetchApi("/auth/register", {
|
|
174
|
+
method: "POST",
|
|
175
|
+
body: JSON.stringify(signUpData)
|
|
176
|
+
});
|
|
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));
|
|
181
|
+
setUser(data.user);
|
|
182
|
+
setOrg(data.org || null);
|
|
183
|
+
setIsSignedIn(true);
|
|
184
|
+
window.dispatchEvent(new CustomEvent("githat:auth-changed", {
|
|
185
|
+
detail: { user: data.user, org: data.org, signedIn: true }
|
|
186
|
+
}));
|
|
187
|
+
return { requiresVerification: !data.user.emailVerified, email: signUpData.email };
|
|
188
|
+
}, []);
|
|
189
|
+
const signOut = useCallback(async () => {
|
|
190
|
+
try {
|
|
191
|
+
await clientRef.current.fetchApi("/auth/logout", { method: "POST" });
|
|
192
|
+
} catch {
|
|
193
|
+
}
|
|
194
|
+
clientRef.current.clearAuth();
|
|
195
|
+
setIsSignedIn(false);
|
|
196
|
+
setUser(null);
|
|
197
|
+
setOrg(null);
|
|
198
|
+
if (typeof window !== "undefined" && config.afterSignOutUrl) {
|
|
199
|
+
window.location.href = config.afterSignOutUrl;
|
|
200
|
+
}
|
|
201
|
+
}, [config.afterSignOutUrl]);
|
|
202
|
+
const switchOrg = useCallback(async (orgId) => {
|
|
203
|
+
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);
|
|
210
|
+
window.dispatchEvent(new CustomEvent("githat:auth-changed", {
|
|
211
|
+
detail: { user, org: orgData, signedIn: true }
|
|
212
|
+
}));
|
|
213
|
+
} catch (e) {
|
|
214
|
+
console.error("Org switch failed:", e);
|
|
215
|
+
}
|
|
216
|
+
}, [user]);
|
|
217
|
+
const value = useMemo(() => ({
|
|
218
|
+
user,
|
|
219
|
+
org,
|
|
220
|
+
isSignedIn,
|
|
221
|
+
isLoading,
|
|
222
|
+
authError,
|
|
223
|
+
config,
|
|
224
|
+
signIn,
|
|
225
|
+
signUp,
|
|
226
|
+
signOut,
|
|
227
|
+
switchOrg
|
|
228
|
+
}), [user, org, isSignedIn, isLoading, authError, config, signIn, signUp, signOut, switchOrg]);
|
|
229
|
+
return /* @__PURE__ */ jsx(GitHatContext.Provider, { value, children });
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
// src/hooks.ts
|
|
233
|
+
import { useContext, useMemo as useMemo2 } from "react";
|
|
234
|
+
function useAuth() {
|
|
235
|
+
const ctx = useContext(GitHatContext);
|
|
236
|
+
if (!ctx) throw new Error("useAuth must be used within a <GitHatProvider>");
|
|
237
|
+
return ctx;
|
|
238
|
+
}
|
|
239
|
+
function useGitHat() {
|
|
240
|
+
const ctx = useAuth();
|
|
241
|
+
const client = useMemo2(
|
|
242
|
+
() => createClient(ctx.config.apiUrl, ctx.config.publishableKey),
|
|
243
|
+
[ctx.config.apiUrl, ctx.config.publishableKey]
|
|
244
|
+
);
|
|
245
|
+
return {
|
|
246
|
+
fetch: client.fetchApi,
|
|
247
|
+
getUserOrgs: () => client.fetchApi("/user/orgs"),
|
|
248
|
+
verifyMCP: (domain) => client.fetchApi(`/verify/mcp/${domain}`),
|
|
249
|
+
verifyAgent: (wallet) => client.fetchApi(`/verify/agent/${wallet}`)
|
|
250
|
+
};
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
// src/components/SignInForm.tsx
|
|
254
|
+
import { useState as useState2 } from "react";
|
|
255
|
+
import { jsx as jsx2, jsxs } from "react/jsx-runtime";
|
|
256
|
+
function SignInForm({ onSuccess, signUpUrl, forgotPasswordUrl }) {
|
|
257
|
+
const { signIn, config } = useAuth();
|
|
258
|
+
const [email, setEmail] = useState2("");
|
|
259
|
+
const [password, setPassword] = useState2("");
|
|
260
|
+
const [error, setError] = useState2("");
|
|
261
|
+
const [loading, setLoading] = useState2(false);
|
|
262
|
+
const emailValid = /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);
|
|
263
|
+
const handleSubmit = async (e) => {
|
|
264
|
+
e.preventDefault();
|
|
265
|
+
if (!emailValid) {
|
|
266
|
+
setError("Please enter a valid email address");
|
|
267
|
+
return;
|
|
268
|
+
}
|
|
269
|
+
setError("");
|
|
270
|
+
setLoading(true);
|
|
271
|
+
try {
|
|
272
|
+
await signIn(email, password);
|
|
273
|
+
if (onSuccess) {
|
|
274
|
+
onSuccess();
|
|
275
|
+
} else if (typeof window !== "undefined") {
|
|
276
|
+
const params = new URLSearchParams(window.location.search);
|
|
277
|
+
window.location.href = params.get("redirect_url") || config.afterSignInUrl;
|
|
278
|
+
}
|
|
279
|
+
} catch (err) {
|
|
280
|
+
setError(err.message || "Sign in failed");
|
|
281
|
+
} finally {
|
|
282
|
+
setLoading(false);
|
|
283
|
+
}
|
|
284
|
+
};
|
|
285
|
+
return /* @__PURE__ */ jsxs("div", { className: "githat-form-container", children: [
|
|
286
|
+
/* @__PURE__ */ jsxs("div", { className: "githat-form-header", children: [
|
|
287
|
+
/* @__PURE__ */ jsx2("h2", { className: "githat-form-title", children: "Sign in" }),
|
|
288
|
+
/* @__PURE__ */ jsx2("p", { className: "githat-form-subtitle", children: "Welcome back to GitHat" })
|
|
289
|
+
] }),
|
|
290
|
+
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: [
|
|
293
|
+
/* @__PURE__ */ jsx2("label", { className: "githat-label", htmlFor: "githat-signin-email", children: "Email" }),
|
|
294
|
+
/* @__PURE__ */ jsx2(
|
|
295
|
+
"input",
|
|
296
|
+
{
|
|
297
|
+
id: "githat-signin-email",
|
|
298
|
+
className: "githat-input",
|
|
299
|
+
type: "email",
|
|
300
|
+
value: email,
|
|
301
|
+
onChange: (e) => setEmail(e.target.value),
|
|
302
|
+
placeholder: "you@example.com",
|
|
303
|
+
autoComplete: "email",
|
|
304
|
+
required: true
|
|
305
|
+
}
|
|
306
|
+
)
|
|
307
|
+
] }),
|
|
308
|
+
/* @__PURE__ */ jsxs("div", { className: "githat-field", children: [
|
|
309
|
+
/* @__PURE__ */ jsx2("label", { className: "githat-label", htmlFor: "githat-signin-password", children: "Password" }),
|
|
310
|
+
/* @__PURE__ */ jsx2(
|
|
311
|
+
"input",
|
|
312
|
+
{
|
|
313
|
+
id: "githat-signin-password",
|
|
314
|
+
className: "githat-input",
|
|
315
|
+
type: "password",
|
|
316
|
+
value: password,
|
|
317
|
+
onChange: (e) => setPassword(e.target.value),
|
|
318
|
+
placeholder: "Enter your password",
|
|
319
|
+
autoComplete: "current-password",
|
|
320
|
+
required: true
|
|
321
|
+
}
|
|
322
|
+
)
|
|
323
|
+
] }),
|
|
324
|
+
forgotPasswordUrl && /* @__PURE__ */ jsx2("a", { href: forgotPasswordUrl, className: "githat-link githat-forgot-link", children: "Forgot password?" }),
|
|
325
|
+
/* @__PURE__ */ jsx2(
|
|
326
|
+
"button",
|
|
327
|
+
{
|
|
328
|
+
type: "submit",
|
|
329
|
+
className: "githat-button githat-button-primary",
|
|
330
|
+
disabled: loading || !email || !password || email.length > 0 && !emailValid,
|
|
331
|
+
children: loading ? "Signing in..." : "Sign in"
|
|
332
|
+
}
|
|
333
|
+
)
|
|
334
|
+
] }),
|
|
335
|
+
signUpUrl && /* @__PURE__ */ jsxs("p", { className: "githat-form-footer", children: [
|
|
336
|
+
"Don't have an account? ",
|
|
337
|
+
/* @__PURE__ */ jsx2("a", { href: signUpUrl, className: "githat-link", children: "Sign up" })
|
|
338
|
+
] }),
|
|
339
|
+
/* @__PURE__ */ jsxs("p", { className: "githat-powered-by", children: [
|
|
340
|
+
"Secured by ",
|
|
341
|
+
/* @__PURE__ */ jsx2("strong", { children: "GitHat" })
|
|
342
|
+
] })
|
|
343
|
+
] });
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
// src/components/SignUpForm.tsx
|
|
347
|
+
import { useState as useState3 } from "react";
|
|
348
|
+
import { jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
349
|
+
function SignUpForm({ onSuccess, signInUrl }) {
|
|
350
|
+
const { signUp, config } = useAuth();
|
|
351
|
+
const [name, setName] = useState3("");
|
|
352
|
+
const [email, setEmail] = useState3("");
|
|
353
|
+
const [password, setPassword] = useState3("");
|
|
354
|
+
const [error, setError] = useState3("");
|
|
355
|
+
const [loading, setLoading] = useState3(false);
|
|
356
|
+
const emailValid = /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);
|
|
357
|
+
const passwordValid = password.length >= 8 && /[A-Z]/.test(password) && /[a-z]/.test(password) && /\d/.test(password);
|
|
358
|
+
const handleSubmit = async (e) => {
|
|
359
|
+
e.preventDefault();
|
|
360
|
+
if (!emailValid) {
|
|
361
|
+
setError("Please enter a valid email address");
|
|
362
|
+
return;
|
|
363
|
+
}
|
|
364
|
+
if (!passwordValid) {
|
|
365
|
+
setError("Password must be 8+ characters with uppercase, lowercase, number, and special character");
|
|
366
|
+
return;
|
|
367
|
+
}
|
|
368
|
+
setError("");
|
|
369
|
+
setLoading(true);
|
|
370
|
+
try {
|
|
371
|
+
const result = await signUp({ email, password, name });
|
|
372
|
+
if (onSuccess) {
|
|
373
|
+
onSuccess(result);
|
|
374
|
+
} else if (typeof window !== "undefined") {
|
|
375
|
+
window.location.href = config.afterSignInUrl;
|
|
376
|
+
}
|
|
377
|
+
} catch (err) {
|
|
378
|
+
setError(err.message || "Sign up failed");
|
|
379
|
+
} finally {
|
|
380
|
+
setLoading(false);
|
|
381
|
+
}
|
|
382
|
+
};
|
|
383
|
+
return /* @__PURE__ */ jsxs2("div", { className: "githat-form-container", children: [
|
|
384
|
+
/* @__PURE__ */ jsxs2("div", { className: "githat-form-header", children: [
|
|
385
|
+
/* @__PURE__ */ jsx3("h2", { className: "githat-form-title", children: "Create an account" }),
|
|
386
|
+
/* @__PURE__ */ jsx3("p", { className: "githat-form-subtitle", children: "Get started with GitHat" })
|
|
387
|
+
] }),
|
|
388
|
+
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: [
|
|
391
|
+
/* @__PURE__ */ jsx3("label", { className: "githat-label", htmlFor: "githat-signup-name", children: "Full name" }),
|
|
392
|
+
/* @__PURE__ */ jsx3(
|
|
393
|
+
"input",
|
|
394
|
+
{
|
|
395
|
+
id: "githat-signup-name",
|
|
396
|
+
className: "githat-input",
|
|
397
|
+
type: "text",
|
|
398
|
+
value: name,
|
|
399
|
+
onChange: (e) => setName(e.target.value),
|
|
400
|
+
placeholder: "Your name",
|
|
401
|
+
autoComplete: "name",
|
|
402
|
+
required: true
|
|
403
|
+
}
|
|
404
|
+
)
|
|
405
|
+
] }),
|
|
406
|
+
/* @__PURE__ */ jsxs2("div", { className: "githat-field", children: [
|
|
407
|
+
/* @__PURE__ */ jsx3("label", { className: "githat-label", htmlFor: "githat-signup-email", children: "Email" }),
|
|
408
|
+
/* @__PURE__ */ jsx3(
|
|
409
|
+
"input",
|
|
410
|
+
{
|
|
411
|
+
id: "githat-signup-email",
|
|
412
|
+
className: "githat-input",
|
|
413
|
+
type: "email",
|
|
414
|
+
value: email,
|
|
415
|
+
onChange: (e) => setEmail(e.target.value),
|
|
416
|
+
placeholder: "you@example.com",
|
|
417
|
+
autoComplete: "email",
|
|
418
|
+
required: true
|
|
419
|
+
}
|
|
420
|
+
)
|
|
421
|
+
] }),
|
|
422
|
+
/* @__PURE__ */ jsxs2("div", { className: "githat-field", children: [
|
|
423
|
+
/* @__PURE__ */ jsx3("label", { className: "githat-label", htmlFor: "githat-signup-password", children: "Password" }),
|
|
424
|
+
/* @__PURE__ */ jsx3(
|
|
425
|
+
"input",
|
|
426
|
+
{
|
|
427
|
+
id: "githat-signup-password",
|
|
428
|
+
className: "githat-input",
|
|
429
|
+
type: "password",
|
|
430
|
+
value: password,
|
|
431
|
+
onChange: (e) => setPassword(e.target.value),
|
|
432
|
+
placeholder: "8+ characters",
|
|
433
|
+
autoComplete: "new-password",
|
|
434
|
+
required: true
|
|
435
|
+
}
|
|
436
|
+
),
|
|
437
|
+
password && !passwordValid && /* @__PURE__ */ jsx3("p", { className: "githat-field-error", children: "Must be 8+ characters with uppercase, lowercase, and number" })
|
|
438
|
+
] }),
|
|
439
|
+
/* @__PURE__ */ jsx3(
|
|
440
|
+
"button",
|
|
441
|
+
{
|
|
442
|
+
type: "submit",
|
|
443
|
+
className: "githat-button githat-button-primary",
|
|
444
|
+
disabled: loading || !email || !password || !name || !emailValid,
|
|
445
|
+
children: loading ? "Creating account..." : "Sign up"
|
|
446
|
+
}
|
|
447
|
+
)
|
|
448
|
+
] }),
|
|
449
|
+
signInUrl && /* @__PURE__ */ jsxs2("p", { className: "githat-form-footer", children: [
|
|
450
|
+
"Already have an account? ",
|
|
451
|
+
/* @__PURE__ */ jsx3("a", { href: signInUrl, className: "githat-link", children: "Sign in" })
|
|
452
|
+
] }),
|
|
453
|
+
/* @__PURE__ */ jsxs2("p", { className: "githat-powered-by", children: [
|
|
454
|
+
"Secured by ",
|
|
455
|
+
/* @__PURE__ */ jsx3("strong", { children: "GitHat" })
|
|
456
|
+
] })
|
|
457
|
+
] });
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
// src/components/SignInButton.tsx
|
|
461
|
+
import { useContext as useContext2 } from "react";
|
|
462
|
+
import { jsx as jsx4 } from "react/jsx-runtime";
|
|
463
|
+
function SignInButton({ className, children, href }) {
|
|
464
|
+
const ctx = useContext2(GitHatContext);
|
|
465
|
+
const url = href || ctx?.config.signInUrl || "/sign-in";
|
|
466
|
+
return /* @__PURE__ */ jsx4("a", { href: url, className: className || "githat-button githat-button-primary", "aria-label": "Sign in", children: children || "Sign in" });
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
// src/components/SignUpButton.tsx
|
|
470
|
+
import { useContext as useContext3 } from "react";
|
|
471
|
+
import { jsx as jsx5 } from "react/jsx-runtime";
|
|
472
|
+
function SignUpButton({ className, children, href }) {
|
|
473
|
+
const ctx = useContext3(GitHatContext);
|
|
474
|
+
const url = href || ctx?.config.signUpUrl || "/sign-up";
|
|
475
|
+
return /* @__PURE__ */ jsx5("a", { href: url, className: className || "githat-button githat-button-outline", "aria-label": "Sign up", children: children || "Sign up" });
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
// src/components/UserButton.tsx
|
|
479
|
+
import { useState as useState4, useRef as useRef2, useEffect as useEffect2 } from "react";
|
|
480
|
+
import { jsx as jsx6, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
481
|
+
function UserButton() {
|
|
482
|
+
const { user, org, isSignedIn, signOut } = useAuth();
|
|
483
|
+
const [open, setOpen] = useState4(false);
|
|
484
|
+
const ref = useRef2(null);
|
|
485
|
+
useEffect2(() => {
|
|
486
|
+
const handleClickOutside = (e) => {
|
|
487
|
+
if (ref.current && !ref.current.contains(e.target)) setOpen(false);
|
|
488
|
+
};
|
|
489
|
+
document.addEventListener("mousedown", handleClickOutside);
|
|
490
|
+
return () => document.removeEventListener("mousedown", handleClickOutside);
|
|
491
|
+
}, []);
|
|
492
|
+
if (!isSignedIn || !user) return null;
|
|
493
|
+
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: [
|
|
495
|
+
/* @__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: [
|
|
498
|
+
/* @__PURE__ */ jsx6("p", { className: "githat-dropdown-name", children: user.name }),
|
|
499
|
+
/* @__PURE__ */ jsx6("p", { className: "githat-dropdown-email", children: user.email }),
|
|
500
|
+
org && /* @__PURE__ */ jsx6("p", { className: "githat-dropdown-org", children: org.name })
|
|
501
|
+
] }),
|
|
502
|
+
/* @__PURE__ */ jsx6("div", { className: "githat-dropdown-divider" }),
|
|
503
|
+
/* @__PURE__ */ jsx6("button", { className: "githat-dropdown-item", role: "menuitem", onClick: () => {
|
|
504
|
+
signOut();
|
|
505
|
+
setOpen(false);
|
|
506
|
+
}, children: "Sign out" })
|
|
507
|
+
] })
|
|
508
|
+
] });
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
// src/components/OrgSwitcher.tsx
|
|
512
|
+
import { useState as useState5, useEffect as useEffect3, useRef as useRef3 } from "react";
|
|
513
|
+
import { jsx as jsx7, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
514
|
+
function OrgSwitcher() {
|
|
515
|
+
const { org, isSignedIn, switchOrg } = useAuth();
|
|
516
|
+
const githat = useGitHat();
|
|
517
|
+
const [orgs, setOrgs] = useState5([]);
|
|
518
|
+
const [orgsLoading, setOrgsLoading] = useState5(false);
|
|
519
|
+
const [open, setOpen] = useState5(false);
|
|
520
|
+
const ref = useRef3(null);
|
|
521
|
+
useEffect3(() => {
|
|
522
|
+
if (isSignedIn) {
|
|
523
|
+
setOrgsLoading(true);
|
|
524
|
+
githat.getUserOrgs().then((data) => setOrgs(data.orgs || [])).catch(() => {
|
|
525
|
+
}).finally(() => setOrgsLoading(false));
|
|
526
|
+
}
|
|
527
|
+
}, [isSignedIn]);
|
|
528
|
+
useEffect3(() => {
|
|
529
|
+
const handleClickOutside = (e) => {
|
|
530
|
+
if (ref.current && !ref.current.contains(e.target)) setOpen(false);
|
|
531
|
+
};
|
|
532
|
+
document.addEventListener("mousedown", handleClickOutside);
|
|
533
|
+
return () => document.removeEventListener("mousedown", handleClickOutside);
|
|
534
|
+
}, []);
|
|
535
|
+
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: [
|
|
538
|
+
/* @__PURE__ */ jsx7("span", { className: "githat-org-name", children: org.name }),
|
|
539
|
+
/* @__PURE__ */ jsx7("span", { className: "githat-chevron", children: open ? "\u25B2" : "\u25BC" })
|
|
540
|
+
] }),
|
|
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(
|
|
542
|
+
"button",
|
|
543
|
+
{
|
|
544
|
+
className: `githat-dropdown-item ${o.id === org.id ? "githat-dropdown-item-active" : ""}`,
|
|
545
|
+
role: "menuitem",
|
|
546
|
+
"aria-current": o.id === org.id ? "true" : void 0,
|
|
547
|
+
onClick: () => {
|
|
548
|
+
switchOrg(o.id);
|
|
549
|
+
setOpen(false);
|
|
550
|
+
},
|
|
551
|
+
children: [
|
|
552
|
+
o.name,
|
|
553
|
+
o.id === org.id && /* @__PURE__ */ jsx7("span", { className: "githat-check", children: "\u2713" })
|
|
554
|
+
]
|
|
555
|
+
},
|
|
556
|
+
o.id
|
|
557
|
+
)) })
|
|
558
|
+
] });
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
// src/components/VerifiedBadge.tsx
|
|
562
|
+
import { useState as useState6, useEffect as useEffect4, useRef as useRef4 } from "react";
|
|
563
|
+
import { jsxs as jsxs5 } from "react/jsx-runtime";
|
|
564
|
+
var CACHE_TTL = 5 * 60 * 1e3;
|
|
565
|
+
var cache = /* @__PURE__ */ new Map();
|
|
566
|
+
function VerifiedBadge({ type, identifier, label }) {
|
|
567
|
+
const githat = useGitHat();
|
|
568
|
+
const [verified, setVerified] = useState6(null);
|
|
569
|
+
const mounted = useRef4(true);
|
|
570
|
+
useEffect4(() => {
|
|
571
|
+
mounted.current = true;
|
|
572
|
+
const key = `${type}:${identifier}`;
|
|
573
|
+
const cached = cache.get(key);
|
|
574
|
+
if (cached && Date.now() - cached.ts < CACHE_TTL) {
|
|
575
|
+
setVerified(cached.verified);
|
|
576
|
+
return;
|
|
577
|
+
}
|
|
578
|
+
const verify = type === "mcp" ? githat.verifyMCP : githat.verifyAgent;
|
|
579
|
+
verify(identifier).then((data) => {
|
|
580
|
+
if (mounted.current) {
|
|
581
|
+
setVerified(data.verified);
|
|
582
|
+
cache.set(key, { verified: data.verified, ts: Date.now() });
|
|
583
|
+
}
|
|
584
|
+
}).catch(() => {
|
|
585
|
+
if (mounted.current) setVerified(false);
|
|
586
|
+
});
|
|
587
|
+
return () => {
|
|
588
|
+
mounted.current = false;
|
|
589
|
+
};
|
|
590
|
+
}, [type, identifier]);
|
|
591
|
+
if (verified === null) return null;
|
|
592
|
+
return /* @__PURE__ */ jsxs5("span", { className: `githat-badge ${verified ? "githat-badge-verified" : "githat-badge-unverified"}`, children: [
|
|
593
|
+
verified ? "\u2713" : "\u2717",
|
|
594
|
+
" ",
|
|
595
|
+
label || (verified ? "Verified" : "Unverified")
|
|
596
|
+
] });
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
// src/components/ProtectedRoute.tsx
|
|
600
|
+
import { Fragment, jsx as jsx8 } from "react/jsx-runtime";
|
|
601
|
+
function ProtectedRoute({ children, fallback }) {
|
|
602
|
+
const { isSignedIn, isLoading, config } = useAuth();
|
|
603
|
+
if (isLoading) {
|
|
604
|
+
return /* @__PURE__ */ jsx8(Fragment, { children: fallback || /* @__PURE__ */ jsx8("div", { className: "githat-loading", children: "Loading..." }) });
|
|
605
|
+
}
|
|
606
|
+
if (!isSignedIn) {
|
|
607
|
+
if (typeof window !== "undefined") {
|
|
608
|
+
window.location.href = config.signInUrl;
|
|
609
|
+
}
|
|
610
|
+
return null;
|
|
611
|
+
}
|
|
612
|
+
return /* @__PURE__ */ jsx8(Fragment, { children });
|
|
613
|
+
}
|
|
614
|
+
export {
|
|
615
|
+
GitHatProvider,
|
|
616
|
+
OrgSwitcher,
|
|
617
|
+
ProtectedRoute,
|
|
618
|
+
SignInButton,
|
|
619
|
+
SignInForm,
|
|
620
|
+
SignUpButton,
|
|
621
|
+
SignUpForm,
|
|
622
|
+
UserButton,
|
|
623
|
+
VerifiedBadge,
|
|
624
|
+
useAuth,
|
|
625
|
+
useGitHat
|
|
626
|
+
};
|
|
627
|
+
//# sourceMappingURL=index.mjs.map
|