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