@brokr/sdk 1.0.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.
@@ -0,0 +1,66 @@
1
+ // src/management.ts
2
+ async function trpcRequest(config, procedure, input) {
3
+ const url = new URL(`${config.apiUrl}/api/trpc/${procedure}`);
4
+ const response = await fetch(url.toString(), {
5
+ method: "POST",
6
+ headers: {
7
+ "Content-Type": "application/json",
8
+ "Authorization": `Bearer ${config.accessToken}`,
9
+ ...config.headers
10
+ },
11
+ body: JSON.stringify(input),
12
+ signal: config.timeout ? AbortSignal.timeout(config.timeout) : void 0
13
+ });
14
+ if (!response.ok) {
15
+ const error = await response.json().catch(() => ({ message: "Request failed" }));
16
+ throw new Error(error.message || `HTTP ${response.status}`);
17
+ }
18
+ const result = await response.json();
19
+ return result.data;
20
+ }
21
+ var BrokrClient = class {
22
+ constructor(config) {
23
+ this.config = config;
24
+ }
25
+ /** Create a new stack with default providers. */
26
+ async create(input) {
27
+ return trpcRequest(this.config, "v1.stack.create", input);
28
+ }
29
+ /** List all stacks for the current user. */
30
+ async listStacks() {
31
+ return trpcRequest(this.config, "v1.stack.list", {});
32
+ }
33
+ /** Get a specific stack by ID. */
34
+ async getStack(id) {
35
+ return trpcRequest(this.config, "v1.stack.get", { id });
36
+ }
37
+ /** Update a stack. */
38
+ async updateStack(id, input) {
39
+ return trpcRequest(this.config, "v1.stack.update", { id, ...input });
40
+ }
41
+ /** Delete a stack. */
42
+ async deleteStack(id) {
43
+ return trpcRequest(this.config, "v1.stack.delete", { id });
44
+ }
45
+ /** Add a provider to a stack. */
46
+ async add(provider, input) {
47
+ return trpcRequest(this.config, "v1.provider.add", { provider, ...input });
48
+ }
49
+ /** Refresh the access token. */
50
+ async refreshToken(refreshToken) {
51
+ return trpcRequest(this.config, "v1.auth.refresh", { refreshToken });
52
+ }
53
+ /** Update the access token. */
54
+ setAccessToken(accessToken) {
55
+ this.config.accessToken = accessToken;
56
+ }
57
+ };
58
+ function createBrokrClient(config) {
59
+ return new BrokrClient(config);
60
+ }
61
+ var create = createBrokrClient;
62
+ export {
63
+ BrokrClient,
64
+ create,
65
+ createBrokrClient as default
66
+ };
package/dist/react.js ADDED
@@ -0,0 +1,264 @@
1
+ "use strict";
2
+ "use client";
3
+ var __create = Object.create;
4
+ var __defProp = Object.defineProperty;
5
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
6
+ var __getOwnPropNames = Object.getOwnPropertyNames;
7
+ var __getProtoOf = Object.getPrototypeOf;
8
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
9
+ var __export = (target, all) => {
10
+ for (var name in all)
11
+ __defProp(target, name, { get: all[name], enumerable: true });
12
+ };
13
+ var __copyProps = (to, from, except, desc) => {
14
+ if (from && typeof from === "object" || typeof from === "function") {
15
+ for (let key of __getOwnPropNames(from))
16
+ if (!__hasOwnProp.call(to, key) && key !== except)
17
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
18
+ }
19
+ return to;
20
+ };
21
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
22
+ // If the importer is in node compatibility mode or this is not an ESM
23
+ // file that has been converted to a CommonJS file using a Babel-
24
+ // compatible transform (i.e. "__esModule" has not been set), then set
25
+ // "default" to the CommonJS "module.exports" for node compatibility.
26
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
27
+ mod
28
+ ));
29
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
30
+
31
+ // src/react/auth.tsx
32
+ var auth_exports = {};
33
+ __export(auth_exports, {
34
+ BrokrAuth: () => BrokrAuth,
35
+ SignIn: () => SignIn,
36
+ SignUp: () => SignUp,
37
+ UserButton: () => UserButton,
38
+ useAuth: () => useAuth,
39
+ useUser: () => useUser
40
+ });
41
+ module.exports = __toCommonJS(auth_exports);
42
+ var import_react = __toESM(require("react"));
43
+ function mapSessionUser(raw) {
44
+ return {
45
+ id: raw.id,
46
+ email: raw.email,
47
+ name: raw.name ?? null,
48
+ avatarUrl: raw.image ?? null,
49
+ emailVerified: raw.emailVerified ?? false
50
+ };
51
+ }
52
+ var AuthContext = (0, import_react.createContext)(null);
53
+ function BrokrAuth({ children }) {
54
+ const [user, setUser] = (0, import_react.useState)(null);
55
+ const [isLoaded, setIsLoaded] = (0, import_react.useState)(false);
56
+ const [isSigningOut, setIsSigningOut] = (0, import_react.useState)(false);
57
+ (0, import_react.useEffect)(() => {
58
+ let cancelled = false;
59
+ async function loadSession() {
60
+ try {
61
+ const res = await fetch("/api/auth/get-session", { credentials: "include" });
62
+ if (res.ok) {
63
+ const data = await res.json();
64
+ if (!cancelled && data?.user) {
65
+ setUser(mapSessionUser(data.user));
66
+ }
67
+ }
68
+ } catch {
69
+ } finally {
70
+ if (!cancelled) setIsLoaded(true);
71
+ }
72
+ }
73
+ loadSession();
74
+ return () => {
75
+ cancelled = true;
76
+ };
77
+ }, []);
78
+ const reloadSession = (0, import_react.useCallback)(async () => {
79
+ const res = await fetch("/api/auth/get-session", { credentials: "include" });
80
+ if (res.ok) {
81
+ const data = await res.json();
82
+ if (data?.user) {
83
+ setUser(mapSessionUser(data.user));
84
+ }
85
+ }
86
+ }, []);
87
+ const signIn = (0, import_react.useCallback)(async (email, password) => {
88
+ const res = await fetch("/api/auth/sign-in/email", {
89
+ method: "POST",
90
+ headers: { "Content-Type": "application/json" },
91
+ credentials: "include",
92
+ body: JSON.stringify({ email, password })
93
+ });
94
+ if (!res.ok) {
95
+ const data = await res.json().catch(() => ({}));
96
+ throw new Error(data.message ?? "Sign in failed");
97
+ }
98
+ await reloadSession();
99
+ }, [reloadSession]);
100
+ const signUp = (0, import_react.useCallback)(async (email, password, name) => {
101
+ const res = await fetch("/api/auth/sign-up/email", {
102
+ method: "POST",
103
+ headers: { "Content-Type": "application/json" },
104
+ credentials: "include",
105
+ body: JSON.stringify({ email, password, name: name ?? "" })
106
+ });
107
+ if (!res.ok) {
108
+ const data = await res.json().catch(() => ({}));
109
+ throw new Error(data.message ?? "Sign up failed");
110
+ }
111
+ await reloadSession();
112
+ }, [reloadSession]);
113
+ const signOut = (0, import_react.useCallback)(async () => {
114
+ setIsSigningOut(true);
115
+ try {
116
+ await fetch("/api/auth/sign-out", { method: "POST", credentials: "include" });
117
+ setUser(null);
118
+ } finally {
119
+ setIsSigningOut(false);
120
+ }
121
+ }, []);
122
+ const value = (0, import_react.useMemo)(
123
+ () => ({ user, isLoaded, signIn, signUp, signOut, isSigningOut }),
124
+ [user, isLoaded, signIn, signUp, signOut, isSigningOut]
125
+ );
126
+ return /* @__PURE__ */ import_react.default.createElement(AuthContext.Provider, { value }, children);
127
+ }
128
+ function useUser() {
129
+ const ctx = (0, import_react.useContext)(AuthContext);
130
+ if (!ctx) throw new Error("useUser must be used within <BrokrAuth>");
131
+ return { user: ctx.user, isLoaded: ctx.isLoaded };
132
+ }
133
+ function useAuth() {
134
+ const ctx = (0, import_react.useContext)(AuthContext);
135
+ if (!ctx) throw new Error("useAuth must be used within <BrokrAuth>");
136
+ return { signIn: ctx.signIn, signUp: ctx.signUp, signOut: ctx.signOut, isSigningOut: ctx.isSigningOut };
137
+ }
138
+ function SignIn({
139
+ redirectTo,
140
+ children
141
+ }) {
142
+ const { signIn } = useAuth();
143
+ const [error, setError] = (0, import_react.useState)(null);
144
+ const [pending, setPending] = (0, import_react.useState)(false);
145
+ const action = (0, import_react.useCallback)(async (formData) => {
146
+ setPending(true);
147
+ setError(null);
148
+ try {
149
+ const email = formData.get("email");
150
+ const password = formData.get("password");
151
+ await signIn(email, password);
152
+ if (redirectTo && typeof window !== "undefined") {
153
+ window.location.href = redirectTo;
154
+ }
155
+ } catch (err) {
156
+ setError(err instanceof Error ? err.message : "Sign in failed");
157
+ } finally {
158
+ setPending(false);
159
+ }
160
+ }, [signIn, redirectTo]);
161
+ if (children) return /* @__PURE__ */ import_react.default.createElement(import_react.default.Fragment, null, children({ action, error, pending }));
162
+ return /* @__PURE__ */ import_react.default.createElement("form", { onSubmit: async (e) => {
163
+ e.preventDefault();
164
+ await action(new FormData(e.currentTarget));
165
+ } }, /* @__PURE__ */ import_react.default.createElement("div", { style: { display: "flex", flexDirection: "column", gap: "12px", maxWidth: "320px" } }, /* @__PURE__ */ import_react.default.createElement("input", { name: "email", type: "email", placeholder: "Email", required: true, style: { padding: "8px 12px", borderRadius: "6px", border: "1px solid #333" } }), /* @__PURE__ */ import_react.default.createElement("input", { name: "password", type: "password", placeholder: "Password", required: true, style: { padding: "8px 12px", borderRadius: "6px", border: "1px solid #333" } }), error && /* @__PURE__ */ import_react.default.createElement("p", { style: { color: "#ef4444", fontSize: "14px", margin: 0 } }, error), /* @__PURE__ */ import_react.default.createElement("button", { type: "submit", disabled: pending, style: { padding: "8px 16px", borderRadius: "6px", background: "#fff", color: "#000", border: "none", cursor: pending ? "wait" : "pointer" } }, pending ? "Signing in..." : "Sign in")));
166
+ }
167
+ function SignUp({
168
+ redirectTo,
169
+ children
170
+ }) {
171
+ const { signUp } = useAuth();
172
+ const [error, setError] = (0, import_react.useState)(null);
173
+ const [pending, setPending] = (0, import_react.useState)(false);
174
+ const action = (0, import_react.useCallback)(async (formData) => {
175
+ setPending(true);
176
+ setError(null);
177
+ try {
178
+ const email = formData.get("email");
179
+ const password = formData.get("password");
180
+ const name = formData.get("name");
181
+ await signUp(email, password, name);
182
+ if (redirectTo && typeof window !== "undefined") {
183
+ window.location.href = redirectTo;
184
+ }
185
+ } catch (err) {
186
+ setError(err instanceof Error ? err.message : "Sign up failed");
187
+ } finally {
188
+ setPending(false);
189
+ }
190
+ }, [signUp, redirectTo]);
191
+ if (children) return /* @__PURE__ */ import_react.default.createElement(import_react.default.Fragment, null, children({ action, error, pending }));
192
+ return /* @__PURE__ */ import_react.default.createElement("form", { onSubmit: async (e) => {
193
+ e.preventDefault();
194
+ await action(new FormData(e.currentTarget));
195
+ } }, /* @__PURE__ */ import_react.default.createElement("div", { style: { display: "flex", flexDirection: "column", gap: "12px", maxWidth: "320px" } }, /* @__PURE__ */ import_react.default.createElement("input", { name: "name", type: "text", placeholder: "Name", style: { padding: "8px 12px", borderRadius: "6px", border: "1px solid #333" } }), /* @__PURE__ */ import_react.default.createElement("input", { name: "email", type: "email", placeholder: "Email", required: true, style: { padding: "8px 12px", borderRadius: "6px", border: "1px solid #333" } }), /* @__PURE__ */ import_react.default.createElement("input", { name: "password", type: "password", placeholder: "Password", required: true, minLength: 8, style: { padding: "8px 12px", borderRadius: "6px", border: "1px solid #333" } }), error && /* @__PURE__ */ import_react.default.createElement("p", { style: { color: "#ef4444", fontSize: "14px", margin: 0 } }, error), /* @__PURE__ */ import_react.default.createElement("button", { type: "submit", disabled: pending, style: { padding: "8px 16px", borderRadius: "6px", background: "#fff", color: "#000", border: "none", cursor: pending ? "wait" : "pointer" } }, pending ? "Creating account..." : "Sign up")));
196
+ }
197
+ function UserButton() {
198
+ const { user, isLoaded } = useUser();
199
+ const { signOut, isSigningOut } = useAuth();
200
+ const [open, setOpen] = (0, import_react.useState)(false);
201
+ const handleToggle = (0, import_react.useCallback)(() => setOpen((prev) => !prev), []);
202
+ const handleSignOut = (0, import_react.useCallback)(async () => {
203
+ await signOut();
204
+ setOpen(false);
205
+ }, [signOut]);
206
+ if (!isLoaded || !user) return null;
207
+ const initials = (user.name ?? user.email).slice(0, 2).toUpperCase();
208
+ return /* @__PURE__ */ import_react.default.createElement("div", { style: { position: "relative", display: "inline-block" } }, /* @__PURE__ */ import_react.default.createElement(
209
+ "button",
210
+ {
211
+ onClick: handleToggle,
212
+ style: {
213
+ width: "36px",
214
+ height: "36px",
215
+ borderRadius: "50%",
216
+ background: "#333",
217
+ color: "#fff",
218
+ border: "none",
219
+ cursor: "pointer",
220
+ fontSize: "14px",
221
+ fontWeight: 600,
222
+ overflow: "hidden"
223
+ }
224
+ },
225
+ user.avatarUrl ? /* @__PURE__ */ import_react.default.createElement("img", { src: user.avatarUrl, alt: "", style: { width: "100%", height: "100%", objectFit: "cover" } }) : initials
226
+ ), open && /* @__PURE__ */ import_react.default.createElement("div", { style: {
227
+ position: "absolute",
228
+ top: "42px",
229
+ right: 0,
230
+ minWidth: "200px",
231
+ background: "#1a1a1a",
232
+ border: "1px solid #333",
233
+ borderRadius: "8px",
234
+ padding: "8px 0",
235
+ zIndex: 50
236
+ } }, /* @__PURE__ */ import_react.default.createElement("div", { style: { padding: "8px 16px", borderBottom: "1px solid #333" } }, /* @__PURE__ */ import_react.default.createElement("p", { style: { margin: 0, fontSize: "14px", fontWeight: 500, color: "#fff" } }, user.name ?? "User"), /* @__PURE__ */ import_react.default.createElement("p", { style: { margin: 0, fontSize: "12px", color: "#888" } }, user.email)), /* @__PURE__ */ import_react.default.createElement(
237
+ "button",
238
+ {
239
+ onClick: handleSignOut,
240
+ disabled: isSigningOut,
241
+ style: {
242
+ display: "block",
243
+ width: "100%",
244
+ padding: "8px 16px",
245
+ background: "none",
246
+ border: "none",
247
+ color: "#ef4444",
248
+ textAlign: "left",
249
+ cursor: "pointer",
250
+ fontSize: "14px"
251
+ }
252
+ },
253
+ isSigningOut ? "Signing out..." : "Sign out"
254
+ )));
255
+ }
256
+ // Annotate the CommonJS export names for ESM import in node:
257
+ 0 && (module.exports = {
258
+ BrokrAuth,
259
+ SignIn,
260
+ SignUp,
261
+ UserButton,
262
+ useAuth,
263
+ useUser
264
+ });
package/dist/react.mjs ADDED
@@ -0,0 +1,225 @@
1
+ "use client";
2
+
3
+ // src/react/auth.tsx
4
+ import React, { createContext, useContext, useCallback, useMemo, useState, useEffect } from "react";
5
+ function mapSessionUser(raw) {
6
+ return {
7
+ id: raw.id,
8
+ email: raw.email,
9
+ name: raw.name ?? null,
10
+ avatarUrl: raw.image ?? null,
11
+ emailVerified: raw.emailVerified ?? false
12
+ };
13
+ }
14
+ var AuthContext = createContext(null);
15
+ function BrokrAuth({ children }) {
16
+ const [user, setUser] = useState(null);
17
+ const [isLoaded, setIsLoaded] = useState(false);
18
+ const [isSigningOut, setIsSigningOut] = useState(false);
19
+ useEffect(() => {
20
+ let cancelled = false;
21
+ async function loadSession() {
22
+ try {
23
+ const res = await fetch("/api/auth/get-session", { credentials: "include" });
24
+ if (res.ok) {
25
+ const data = await res.json();
26
+ if (!cancelled && data?.user) {
27
+ setUser(mapSessionUser(data.user));
28
+ }
29
+ }
30
+ } catch {
31
+ } finally {
32
+ if (!cancelled) setIsLoaded(true);
33
+ }
34
+ }
35
+ loadSession();
36
+ return () => {
37
+ cancelled = true;
38
+ };
39
+ }, []);
40
+ const reloadSession = useCallback(async () => {
41
+ const res = await fetch("/api/auth/get-session", { credentials: "include" });
42
+ if (res.ok) {
43
+ const data = await res.json();
44
+ if (data?.user) {
45
+ setUser(mapSessionUser(data.user));
46
+ }
47
+ }
48
+ }, []);
49
+ const signIn = useCallback(async (email, password) => {
50
+ const res = await fetch("/api/auth/sign-in/email", {
51
+ method: "POST",
52
+ headers: { "Content-Type": "application/json" },
53
+ credentials: "include",
54
+ body: JSON.stringify({ email, password })
55
+ });
56
+ if (!res.ok) {
57
+ const data = await res.json().catch(() => ({}));
58
+ throw new Error(data.message ?? "Sign in failed");
59
+ }
60
+ await reloadSession();
61
+ }, [reloadSession]);
62
+ const signUp = useCallback(async (email, password, name) => {
63
+ const res = await fetch("/api/auth/sign-up/email", {
64
+ method: "POST",
65
+ headers: { "Content-Type": "application/json" },
66
+ credentials: "include",
67
+ body: JSON.stringify({ email, password, name: name ?? "" })
68
+ });
69
+ if (!res.ok) {
70
+ const data = await res.json().catch(() => ({}));
71
+ throw new Error(data.message ?? "Sign up failed");
72
+ }
73
+ await reloadSession();
74
+ }, [reloadSession]);
75
+ const signOut = useCallback(async () => {
76
+ setIsSigningOut(true);
77
+ try {
78
+ await fetch("/api/auth/sign-out", { method: "POST", credentials: "include" });
79
+ setUser(null);
80
+ } finally {
81
+ setIsSigningOut(false);
82
+ }
83
+ }, []);
84
+ const value = useMemo(
85
+ () => ({ user, isLoaded, signIn, signUp, signOut, isSigningOut }),
86
+ [user, isLoaded, signIn, signUp, signOut, isSigningOut]
87
+ );
88
+ return /* @__PURE__ */ React.createElement(AuthContext.Provider, { value }, children);
89
+ }
90
+ function useUser() {
91
+ const ctx = useContext(AuthContext);
92
+ if (!ctx) throw new Error("useUser must be used within <BrokrAuth>");
93
+ return { user: ctx.user, isLoaded: ctx.isLoaded };
94
+ }
95
+ function useAuth() {
96
+ const ctx = useContext(AuthContext);
97
+ if (!ctx) throw new Error("useAuth must be used within <BrokrAuth>");
98
+ return { signIn: ctx.signIn, signUp: ctx.signUp, signOut: ctx.signOut, isSigningOut: ctx.isSigningOut };
99
+ }
100
+ function SignIn({
101
+ redirectTo,
102
+ children
103
+ }) {
104
+ const { signIn } = useAuth();
105
+ const [error, setError] = useState(null);
106
+ const [pending, setPending] = useState(false);
107
+ const action = useCallback(async (formData) => {
108
+ setPending(true);
109
+ setError(null);
110
+ try {
111
+ const email = formData.get("email");
112
+ const password = formData.get("password");
113
+ await signIn(email, password);
114
+ if (redirectTo && typeof window !== "undefined") {
115
+ window.location.href = redirectTo;
116
+ }
117
+ } catch (err) {
118
+ setError(err instanceof Error ? err.message : "Sign in failed");
119
+ } finally {
120
+ setPending(false);
121
+ }
122
+ }, [signIn, redirectTo]);
123
+ if (children) return /* @__PURE__ */ React.createElement(React.Fragment, null, children({ action, error, pending }));
124
+ return /* @__PURE__ */ React.createElement("form", { onSubmit: async (e) => {
125
+ e.preventDefault();
126
+ await action(new FormData(e.currentTarget));
127
+ } }, /* @__PURE__ */ React.createElement("div", { style: { display: "flex", flexDirection: "column", gap: "12px", maxWidth: "320px" } }, /* @__PURE__ */ React.createElement("input", { name: "email", type: "email", placeholder: "Email", required: true, style: { padding: "8px 12px", borderRadius: "6px", border: "1px solid #333" } }), /* @__PURE__ */ React.createElement("input", { name: "password", type: "password", placeholder: "Password", required: true, style: { padding: "8px 12px", borderRadius: "6px", border: "1px solid #333" } }), error && /* @__PURE__ */ React.createElement("p", { style: { color: "#ef4444", fontSize: "14px", margin: 0 } }, error), /* @__PURE__ */ React.createElement("button", { type: "submit", disabled: pending, style: { padding: "8px 16px", borderRadius: "6px", background: "#fff", color: "#000", border: "none", cursor: pending ? "wait" : "pointer" } }, pending ? "Signing in..." : "Sign in")));
128
+ }
129
+ function SignUp({
130
+ redirectTo,
131
+ children
132
+ }) {
133
+ const { signUp } = useAuth();
134
+ const [error, setError] = useState(null);
135
+ const [pending, setPending] = useState(false);
136
+ const action = useCallback(async (formData) => {
137
+ setPending(true);
138
+ setError(null);
139
+ try {
140
+ const email = formData.get("email");
141
+ const password = formData.get("password");
142
+ const name = formData.get("name");
143
+ await signUp(email, password, name);
144
+ if (redirectTo && typeof window !== "undefined") {
145
+ window.location.href = redirectTo;
146
+ }
147
+ } catch (err) {
148
+ setError(err instanceof Error ? err.message : "Sign up failed");
149
+ } finally {
150
+ setPending(false);
151
+ }
152
+ }, [signUp, redirectTo]);
153
+ if (children) return /* @__PURE__ */ React.createElement(React.Fragment, null, children({ action, error, pending }));
154
+ return /* @__PURE__ */ React.createElement("form", { onSubmit: async (e) => {
155
+ e.preventDefault();
156
+ await action(new FormData(e.currentTarget));
157
+ } }, /* @__PURE__ */ React.createElement("div", { style: { display: "flex", flexDirection: "column", gap: "12px", maxWidth: "320px" } }, /* @__PURE__ */ React.createElement("input", { name: "name", type: "text", placeholder: "Name", style: { padding: "8px 12px", borderRadius: "6px", border: "1px solid #333" } }), /* @__PURE__ */ React.createElement("input", { name: "email", type: "email", placeholder: "Email", required: true, style: { padding: "8px 12px", borderRadius: "6px", border: "1px solid #333" } }), /* @__PURE__ */ React.createElement("input", { name: "password", type: "password", placeholder: "Password", required: true, minLength: 8, style: { padding: "8px 12px", borderRadius: "6px", border: "1px solid #333" } }), error && /* @__PURE__ */ React.createElement("p", { style: { color: "#ef4444", fontSize: "14px", margin: 0 } }, error), /* @__PURE__ */ React.createElement("button", { type: "submit", disabled: pending, style: { padding: "8px 16px", borderRadius: "6px", background: "#fff", color: "#000", border: "none", cursor: pending ? "wait" : "pointer" } }, pending ? "Creating account..." : "Sign up")));
158
+ }
159
+ function UserButton() {
160
+ const { user, isLoaded } = useUser();
161
+ const { signOut, isSigningOut } = useAuth();
162
+ const [open, setOpen] = useState(false);
163
+ const handleToggle = useCallback(() => setOpen((prev) => !prev), []);
164
+ const handleSignOut = useCallback(async () => {
165
+ await signOut();
166
+ setOpen(false);
167
+ }, [signOut]);
168
+ if (!isLoaded || !user) return null;
169
+ const initials = (user.name ?? user.email).slice(0, 2).toUpperCase();
170
+ return /* @__PURE__ */ React.createElement("div", { style: { position: "relative", display: "inline-block" } }, /* @__PURE__ */ React.createElement(
171
+ "button",
172
+ {
173
+ onClick: handleToggle,
174
+ style: {
175
+ width: "36px",
176
+ height: "36px",
177
+ borderRadius: "50%",
178
+ background: "#333",
179
+ color: "#fff",
180
+ border: "none",
181
+ cursor: "pointer",
182
+ fontSize: "14px",
183
+ fontWeight: 600,
184
+ overflow: "hidden"
185
+ }
186
+ },
187
+ user.avatarUrl ? /* @__PURE__ */ React.createElement("img", { src: user.avatarUrl, alt: "", style: { width: "100%", height: "100%", objectFit: "cover" } }) : initials
188
+ ), open && /* @__PURE__ */ React.createElement("div", { style: {
189
+ position: "absolute",
190
+ top: "42px",
191
+ right: 0,
192
+ minWidth: "200px",
193
+ background: "#1a1a1a",
194
+ border: "1px solid #333",
195
+ borderRadius: "8px",
196
+ padding: "8px 0",
197
+ zIndex: 50
198
+ } }, /* @__PURE__ */ React.createElement("div", { style: { padding: "8px 16px", borderBottom: "1px solid #333" } }, /* @__PURE__ */ React.createElement("p", { style: { margin: 0, fontSize: "14px", fontWeight: 500, color: "#fff" } }, user.name ?? "User"), /* @__PURE__ */ React.createElement("p", { style: { margin: 0, fontSize: "12px", color: "#888" } }, user.email)), /* @__PURE__ */ React.createElement(
199
+ "button",
200
+ {
201
+ onClick: handleSignOut,
202
+ disabled: isSigningOut,
203
+ style: {
204
+ display: "block",
205
+ width: "100%",
206
+ padding: "8px 16px",
207
+ background: "none",
208
+ border: "none",
209
+ color: "#ef4444",
210
+ textAlign: "left",
211
+ cursor: "pointer",
212
+ fontSize: "14px"
213
+ }
214
+ },
215
+ isSigningOut ? "Signing out..." : "Sign out"
216
+ )));
217
+ }
218
+ export {
219
+ BrokrAuth,
220
+ SignIn,
221
+ SignUp,
222
+ UserButton,
223
+ useAuth,
224
+ useUser
225
+ };