@contentgrowth/content-auth 0.0.1

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,15 @@
1
+ import {
2
+ Hono,
3
+ authMiddleware,
4
+ createAuth,
5
+ createAuthApp,
6
+ schema_exports
7
+ } from "../chunk-526JE62U.js";
8
+ import "../chunk-R5U7XKVJ.js";
9
+ export {
10
+ Hono,
11
+ authMiddleware,
12
+ createAuth,
13
+ createAuthApp,
14
+ schema_exports as schema
15
+ };
@@ -0,0 +1,142 @@
1
+ import {
2
+ __export
3
+ } from "./chunk-R5U7XKVJ.js";
4
+
5
+ // src/backend/index.ts
6
+ import { betterAuth } from "better-auth";
7
+ import { drizzleAdapter } from "better-auth/adapters/drizzle";
8
+ import { drizzle } from "drizzle-orm/d1";
9
+ import { Hono } from "hono";
10
+ export * from "better-auth";
11
+
12
+ // src/backend/schema.ts
13
+ var schema_exports = {};
14
+ __export(schema_exports, {
15
+ accounts: () => accounts,
16
+ invitations: () => invitations,
17
+ members: () => members,
18
+ organizations: () => organizations,
19
+ sessions: () => sessions,
20
+ users: () => users,
21
+ verifications: () => verifications
22
+ });
23
+ import { sqliteTable, text, integer } from "drizzle-orm/sqlite-core";
24
+ var users = sqliteTable("users", {
25
+ id: text("id").primaryKey(),
26
+ name: text("name").notNull(),
27
+ email: text("email").notNull().unique(),
28
+ emailVerified: integer("emailVerified", { mode: "boolean" }).notNull(),
29
+ image: text("image"),
30
+ createdAt: integer("createdAt", { mode: "timestamp" }).notNull(),
31
+ updatedAt: integer("updatedAt", { mode: "timestamp" }).notNull()
32
+ });
33
+ var sessions = sqliteTable("sessions", {
34
+ id: text("id").primaryKey(),
35
+ expiresAt: integer("expiresAt", { mode: "timestamp" }).notNull(),
36
+ token: text("token").notNull().unique(),
37
+ createdAt: integer("createdAt", { mode: "timestamp" }).notNull(),
38
+ updatedAt: integer("updatedAt", { mode: "timestamp" }).notNull(),
39
+ ipAddress: text("ipAddress"),
40
+ userAgent: text("userAgent"),
41
+ userId: text("userId").notNull().references(() => users.id, { onDelete: "cascade" }),
42
+ activeOrganizationId: text("activeOrganizationId")
43
+ });
44
+ var accounts = sqliteTable("accounts", {
45
+ id: text("id").primaryKey(),
46
+ accountId: text("accountId").notNull(),
47
+ providerId: text("providerId").notNull(),
48
+ userId: text("userId").notNull().references(() => users.id, { onDelete: "cascade" }),
49
+ accessToken: text("accessToken"),
50
+ refreshToken: text("refreshToken"),
51
+ idToken: text("idToken"),
52
+ accessTokenExpiresAt: integer("accessTokenExpiresAt", { mode: "timestamp" }),
53
+ refreshTokenExpiresAt: integer("refreshTokenExpiresAt", { mode: "timestamp" }),
54
+ scope: text("scope"),
55
+ password: text("password"),
56
+ createdAt: integer("createdAt", { mode: "timestamp" }).notNull(),
57
+ updatedAt: integer("updatedAt", { mode: "timestamp" }).notNull()
58
+ });
59
+ var verifications = sqliteTable("verifications", {
60
+ id: text("id").primaryKey(),
61
+ identifier: text("identifier").notNull(),
62
+ value: text("value").notNull(),
63
+ expiresAt: integer("expiresAt", { mode: "timestamp" }).notNull(),
64
+ createdAt: integer("createdAt", { mode: "timestamp" }),
65
+ updatedAt: integer("updatedAt", { mode: "timestamp" })
66
+ });
67
+ var organizations = sqliteTable("organizations", {
68
+ id: text("id").primaryKey(),
69
+ name: text("name").notNull(),
70
+ slug: text("slug").unique(),
71
+ logo: text("logo"),
72
+ createdAt: integer("createdAt", { mode: "timestamp" }).notNull(),
73
+ metadata: text("metadata")
74
+ });
75
+ var members = sqliteTable("members", {
76
+ id: text("id").primaryKey(),
77
+ organizationId: text("organizationId").notNull().references(() => organizations.id, { onDelete: "cascade" }),
78
+ userId: text("userId").notNull().references(() => users.id, { onDelete: "cascade" }),
79
+ role: text("role").notNull(),
80
+ createdAt: integer("createdAt", { mode: "timestamp" }).notNull()
81
+ });
82
+ var invitations = sqliteTable("invitations", {
83
+ id: text("id").primaryKey(),
84
+ organizationId: text("organizationId").notNull().references(() => organizations.id, { onDelete: "cascade" }),
85
+ email: text("email").notNull(),
86
+ role: text("role"),
87
+ status: text("status").notNull(),
88
+ expiresAt: integer("expiresAt", { mode: "timestamp" }).notNull(),
89
+ inviterId: text("inviterId").notNull().references(() => users.id, { onDelete: "cascade" })
90
+ });
91
+
92
+ // src/backend/index.ts
93
+ var createAuth = (config) => {
94
+ let db;
95
+ let provider = config.provider || "sqlite";
96
+ if (config.database && typeof config.database.prepare === "function") {
97
+ db = drizzle(config.database, { schema: schema_exports });
98
+ } else {
99
+ db = config.database;
100
+ }
101
+ const { database, secret, baseUrl, provider: _, ...rest } = config;
102
+ let adapterOptions = {
103
+ provider,
104
+ schema: {
105
+ user: users,
106
+ session: sessions,
107
+ account: accounts,
108
+ verification: verifications,
109
+ organization: organizations,
110
+ member: members,
111
+ invitation: invitations
112
+ }
113
+ };
114
+ const auth = betterAuth({
115
+ database: drizzleAdapter(db, adapterOptions),
116
+ secret,
117
+ baseURL: baseUrl,
118
+ ...rest
119
+ });
120
+ return auth;
121
+ };
122
+ var authMiddleware = (auth) => {
123
+ return async (c) => {
124
+ return auth.handler(c.req.raw);
125
+ };
126
+ };
127
+ var createAuthApp = (config) => {
128
+ const auth = createAuth(config);
129
+ const app = new Hono();
130
+ app.all("/api/auth/*", async (c) => {
131
+ return auth.handler(c.req.raw);
132
+ });
133
+ return { app, auth };
134
+ };
135
+
136
+ export {
137
+ schema_exports,
138
+ Hono,
139
+ createAuth,
140
+ authMiddleware,
141
+ createAuthApp
142
+ };
@@ -0,0 +1,17 @@
1
+ // src/frontend/client.ts
2
+ import { createAuthClient } from "better-auth/react";
3
+ import { organizationClient } from "better-auth/client/plugins";
4
+ var createClient = (baseUrl) => {
5
+ return createAuthClient({
6
+ baseURL: baseUrl,
7
+ plugins: [
8
+ organizationClient()
9
+ ]
10
+ });
11
+ };
12
+ var authClient = createClient();
13
+
14
+ export {
15
+ createClient,
16
+ authClient
17
+ };
@@ -0,0 +1,16 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
3
+ get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
4
+ }) : x)(function(x) {
5
+ if (typeof require !== "undefined") return require.apply(this, arguments);
6
+ throw Error('Dynamic require of "' + x + '" is not supported');
7
+ });
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+
13
+ export {
14
+ __require,
15
+ __export
16
+ };
@@ -0,0 +1,372 @@
1
+ import {
2
+ authClient
3
+ } from "./chunk-NKDKDBM2.js";
4
+
5
+ // src/frontend/components/AuthForm.tsx
6
+ import React, { useState } from "react";
7
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
8
+ var AuthForm = ({
9
+ client = authClient,
10
+ onSuccess,
11
+ className,
12
+ socialProviders = [],
13
+ socialLayout = "row",
14
+ title,
15
+ width = "default",
16
+ layout = "default",
17
+ socialPosition = "top",
18
+ view,
19
+ onSwitchMode
20
+ }) => {
21
+ const [isLogin, setIsLogin] = useState(view !== "signup");
22
+ const [email, setEmail] = useState("");
23
+ const [password, setPassword] = useState("");
24
+ const [name, setName] = useState("");
25
+ const [loading, setLoading] = useState(false);
26
+ const [error, setError] = useState(null);
27
+ const [mounted, setMounted] = useState(false);
28
+ React.useEffect(() => {
29
+ setMounted(true);
30
+ setIsLogin(view !== "signup");
31
+ }, [view]);
32
+ const handleSubmit = async (e) => {
33
+ e.preventDefault();
34
+ setLoading(true);
35
+ setError(null);
36
+ try {
37
+ if (isLogin) {
38
+ const { error: error2 } = await client.signIn.email({
39
+ email,
40
+ password
41
+ });
42
+ if (error2) throw error2;
43
+ } else {
44
+ const { error: error2 } = await client.signUp.email({
45
+ email,
46
+ password,
47
+ name
48
+ });
49
+ if (error2) throw error2;
50
+ }
51
+ onSuccess?.();
52
+ } catch (err) {
53
+ setError(err.message || "An error occurred");
54
+ } finally {
55
+ setLoading(false);
56
+ }
57
+ };
58
+ const handleSocialLogin = async (provider) => {
59
+ setLoading(true);
60
+ try {
61
+ await client.signIn.social({
62
+ provider,
63
+ callbackURL: window.location.href
64
+ });
65
+ } catch (err) {
66
+ setError(err.message || `Failed to sign in with ${provider}`);
67
+ setLoading(false);
68
+ }
69
+ };
70
+ const socialClass = socialLayout === "column" ? "ca-social-column" : "ca-social-grid";
71
+ let widthClass = "";
72
+ if (width === "compact") widthClass = "ca-width-compact";
73
+ else if (width === "wide") widthClass = "ca-width-wide";
74
+ else widthClass = "ca-width-default";
75
+ const containerClass = `ca-container ${layout === "split" ? "ca-layout-split" : ""} ${widthClass} ${className || ""}`;
76
+ const renderSocials = () => socialProviders.length > 0 && /* @__PURE__ */ jsx("div", { className: socialClass, children: socialProviders.map((provider) => /* @__PURE__ */ jsxs(
77
+ "button",
78
+ {
79
+ type: "button",
80
+ className: `ca-button ca-button-social ca-button-${provider}`,
81
+ onClick: () => handleSocialLogin(provider),
82
+ disabled: loading,
83
+ children: [
84
+ provider === "google" && /* @__PURE__ */ jsx("svg", { className: "ca-icon", viewBox: "0 0 24 24", width: "20", height: "20", xmlns: "http://www.w3.org/2000/svg", children: /* @__PURE__ */ jsxs("g", { transform: "matrix(1, 0, 0, 1, 27.009001, -39.238998)", children: [
85
+ /* @__PURE__ */ jsx("path", { fill: "#4285F4", d: "M -3.264 51.509 C -3.264 50.719 -3.334 49.969 -3.454 49.239 L -14.754 49.239 L -14.754 53.749 L -8.284 53.749 C -8.574 55.229 -9.424 56.479 -10.684 57.329 L -10.684 60.329 L -6.824 60.329 C -4.564 58.239 -3.264 55.159 -3.264 51.509 Z" }),
86
+ /* @__PURE__ */ jsx("path", { fill: "#34A853", d: "M -14.754 63.239 C -11.514 63.239 -8.804 62.159 -6.824 60.329 L -10.684 57.329 C -11.764 58.049 -13.134 58.489 -14.754 58.489 C -17.884 58.489 -20.534 56.379 -21.484 53.529 L -25.464 53.529 L -25.464 56.619 C -23.494 60.539 -19.444 63.239 -14.754 63.239 Z" }),
87
+ /* @__PURE__ */ jsx("path", { fill: "#FBBC05", d: "M -21.484 53.529 C -21.734 52.809 -21.864 52.039 -21.864 51.239 C -21.864 50.439 -21.734 49.669 -21.484 48.949 L -21.484 45.859 L -25.464 45.859 C -26.284 47.479 -26.754 49.299 -26.754 51.239 C -26.754 53.179 -26.284 54.999 -25.464 56.619 L -21.484 53.529 Z" }),
88
+ /* @__PURE__ */ jsx("path", { fill: "#EA4335", d: "M -14.754 43.989 C -12.984 43.989 -11.424 44.599 -10.174 45.789 L -6.744 42.359 C -8.804 40.429 -11.514 39.239 -14.754 39.239 C -19.444 39.239 -23.494 41.939 -25.464 45.859 L -21.484 48.949 C -20.534 46.099 -17.884 43.989 -14.754 43.989 Z" })
89
+ ] }) }),
90
+ provider === "github" && /* @__PURE__ */ jsx("svg", { className: "ca-icon", viewBox: "0 0 24 24", width: "20", height: "20", xmlns: "http://www.w3.org/2000/svg", children: /* @__PURE__ */ jsx("path", { d: "M12 2C6.477 2 2 6.477 2 12c0 4.42 2.865 8.17 6.839 9.49.5.092.682-.217.682-.482 0-.237-.008-.866-.013-1.7-2.782.604-3.369-1.34-3.369-1.34-.454-1.156-1.11-1.464-1.11-1.464-.908-.62.069-.608.069-.608 1.003.07 1.531 1.03 1.531 1.03.892 1.529 2.341 1.087 2.91.832.092-.647.35-1.088.636-1.338-2.22-.253-4.555-1.11-4.555-4.943 0-1.091.39-1.984 1.029-2.683-.103-.253-.446-1.27.098-2.647 0 0 .84-.269 2.75 1.026A9.564 9.564 0 0 1 12 6.844c.85.004 1.705.115 2.504.337 1.909-1.296 2.747-1.027 2.747-1.027.546 1.379.202 2.398.1 2.651.64.699 1.028 1.592 1.028 2.683 0 3.842-2.339 4.687-4.566 4.935.359.309.678.92.678 1.855 0 1.338-.012 2.419-.012 2.747 0 .268.18.58.688.482A10.019 10.019 0 0 0 22 12c0-5.523-4.477-10-10-10z", fill: "currentColor" }) }),
91
+ /* @__PURE__ */ jsx("span", { className: "ca-btn-text", children: provider === "github" ? "GitHub" : "Google" })
92
+ ]
93
+ },
94
+ provider
95
+ )) });
96
+ const renderForm = () => {
97
+ if (!mounted) {
98
+ return /* @__PURE__ */ jsx("div", { className: "ca-form", children: /* @__PURE__ */ jsxs("div", { className: "ca-input-group", style: { opacity: 0 }, children: [
99
+ /* @__PURE__ */ jsx("label", { className: "ca-label", children: "Loading" }),
100
+ /* @__PURE__ */ jsx("input", { className: "ca-input", disabled: true })
101
+ ] }) });
102
+ }
103
+ return /* @__PURE__ */ jsxs("form", { onSubmit: handleSubmit, className: "ca-form", children: [
104
+ !isLogin && /* @__PURE__ */ jsxs("div", { className: "ca-input-group", children: [
105
+ /* @__PURE__ */ jsx("label", { className: "ca-label", htmlFor: "name", children: "Name" }),
106
+ /* @__PURE__ */ jsx(
107
+ "input",
108
+ {
109
+ id: "name",
110
+ type: "text",
111
+ className: "ca-input",
112
+ value: name,
113
+ onChange: (e) => setName(e.target.value),
114
+ required: true
115
+ }
116
+ )
117
+ ] }),
118
+ /* @__PURE__ */ jsxs("div", { className: "ca-input-group", children: [
119
+ /* @__PURE__ */ jsx("label", { className: "ca-label", htmlFor: "email", children: "Email" }),
120
+ /* @__PURE__ */ jsx(
121
+ "input",
122
+ {
123
+ id: "email",
124
+ type: "email",
125
+ className: "ca-input",
126
+ value: email,
127
+ onChange: (e) => setEmail(e.target.value),
128
+ required: true
129
+ }
130
+ )
131
+ ] }),
132
+ /* @__PURE__ */ jsxs("div", { className: "ca-input-group", children: [
133
+ /* @__PURE__ */ jsx("label", { className: "ca-label", htmlFor: "password", children: "Password" }),
134
+ /* @__PURE__ */ jsx(
135
+ "input",
136
+ {
137
+ id: "password",
138
+ type: "password",
139
+ className: "ca-input",
140
+ value: password,
141
+ onChange: (e) => setPassword(e.target.value),
142
+ required: true
143
+ }
144
+ )
145
+ ] }),
146
+ error && /* @__PURE__ */ jsx("div", { className: "ca-error", children: error }),
147
+ /* @__PURE__ */ jsx("button", { type: "submit", className: "ca-button", disabled: loading, children: loading ? "Loading..." : isLogin ? "Sign In" : "Sign Up" })
148
+ ] });
149
+ };
150
+ const renderFooter = () => /* @__PURE__ */ jsxs("div", { className: "ca-footer", children: [
151
+ isLogin ? "Don't have an account? " : "Already have an account? ",
152
+ /* @__PURE__ */ jsx(
153
+ "button",
154
+ {
155
+ className: "ca-link",
156
+ onClick: () => {
157
+ if (onSwitchMode) {
158
+ onSwitchMode();
159
+ } else {
160
+ setIsLogin(!isLogin);
161
+ }
162
+ },
163
+ type: "button",
164
+ children: isLogin ? "Sign up" : "Sign in"
165
+ }
166
+ )
167
+ ] });
168
+ const titleContent = title ? typeof title === "string" ? /* @__PURE__ */ jsx("h2", { className: "ca-title", children: title }) : title : /* @__PURE__ */ jsx("h2", { className: "ca-title", children: isLogin ? "Welcome Back" : "Create Account" });
169
+ if (layout === "split") {
170
+ return /* @__PURE__ */ jsxs("div", { className: containerClass, children: [
171
+ titleContent,
172
+ /* @__PURE__ */ jsxs("div", { className: "ca-split-body", children: [
173
+ /* @__PURE__ */ jsxs("div", { className: "ca-split-main", children: [
174
+ renderForm(),
175
+ renderFooter()
176
+ ] }),
177
+ socialProviders.length > 0 && /* @__PURE__ */ jsxs(Fragment, { children: [
178
+ /* @__PURE__ */ jsx("div", { className: "ca-split-divider", children: /* @__PURE__ */ jsx("span", { className: "ca-split-divider-text", children: "Or" }) }),
179
+ /* @__PURE__ */ jsxs("div", { className: "ca-split-social", children: [
180
+ /* @__PURE__ */ jsx("h3", { className: "ca-social-header", children: isLogin ? "Sign In With" : "Sign Up With" }),
181
+ renderSocials()
182
+ ] })
183
+ ] })
184
+ ] })
185
+ ] });
186
+ }
187
+ return /* @__PURE__ */ jsxs("div", { className: containerClass, children: [
188
+ titleContent,
189
+ socialPosition === "top" && socialProviders.length > 0 && /* @__PURE__ */ jsxs(Fragment, { children: [
190
+ renderSocials(),
191
+ /* @__PURE__ */ jsx("div", { className: "ca-divider", children: /* @__PURE__ */ jsx("span", { className: "ca-divider-text", children: "Or continue with" }) })
192
+ ] }),
193
+ renderForm(),
194
+ socialPosition === "bottom" && socialProviders.length > 0 && /* @__PURE__ */ jsxs(Fragment, { children: [
195
+ /* @__PURE__ */ jsx("div", { className: "ca-divider", children: /* @__PURE__ */ jsx("span", { className: "ca-divider-text", children: "Or continue with" }) }),
196
+ renderSocials()
197
+ ] }),
198
+ renderFooter()
199
+ ] });
200
+ };
201
+
202
+ // src/frontend/components/Organization.tsx
203
+ import { useState as useState2, useEffect } from "react";
204
+ import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
205
+ var CreateOrganizationForm = ({
206
+ client = authClient,
207
+ className,
208
+ onSuccess,
209
+ onError
210
+ }) => {
211
+ const [name, setName] = useState2("");
212
+ const [slug, setSlug] = useState2("");
213
+ const [loading, setLoading] = useState2(false);
214
+ const handleSubmit = async (e) => {
215
+ e.preventDefault();
216
+ setLoading(true);
217
+ try {
218
+ const result = await client.organization.create({
219
+ name,
220
+ slug
221
+ });
222
+ if (result.error) throw result.error;
223
+ setName("");
224
+ setSlug("");
225
+ onSuccess?.(result.data);
226
+ } catch (err) {
227
+ onError?.(err.message || "Failed to create organization");
228
+ } finally {
229
+ setLoading(false);
230
+ }
231
+ };
232
+ return /* @__PURE__ */ jsxs2("form", { onSubmit: handleSubmit, className: `ca-form ${className || ""}`, children: [
233
+ /* @__PURE__ */ jsx2("h3", { className: "ca-subtitle", children: "Create Organization" }),
234
+ /* @__PURE__ */ jsxs2("div", { className: "ca-input-group", children: [
235
+ /* @__PURE__ */ jsx2("label", { className: "ca-label", children: "Organization Name" }),
236
+ /* @__PURE__ */ jsx2(
237
+ "input",
238
+ {
239
+ type: "text",
240
+ className: "ca-input",
241
+ value: name,
242
+ onChange: (e) => {
243
+ setName(e.target.value);
244
+ if (!slug) setSlug(e.target.value.toLowerCase().replace(/\s+/g, "-"));
245
+ },
246
+ required: true,
247
+ placeholder: "Acme Corp"
248
+ }
249
+ )
250
+ ] }),
251
+ /* @__PURE__ */ jsxs2("div", { className: "ca-input-group", children: [
252
+ /* @__PURE__ */ jsx2("label", { className: "ca-label", children: "Slug" }),
253
+ /* @__PURE__ */ jsx2(
254
+ "input",
255
+ {
256
+ type: "text",
257
+ className: "ca-input",
258
+ value: slug,
259
+ onChange: (e) => setSlug(e.target.value),
260
+ required: true,
261
+ placeholder: "acme-corp"
262
+ }
263
+ )
264
+ ] }),
265
+ /* @__PURE__ */ jsx2("button", { type: "submit", className: "ca-button", disabled: loading, children: loading ? "Creating..." : "Create Organization" })
266
+ ] });
267
+ };
268
+ var OrganizationSwitcher = ({
269
+ client = authClient,
270
+ className,
271
+ currentOrgId,
272
+ onSuccess
273
+ }) => {
274
+ const [orgs, setOrgs] = useState2([]);
275
+ const [loading, setLoading] = useState2(true);
276
+ useEffect(() => {
277
+ const fetchOrgs = async () => {
278
+ const { data } = await client.organization.list({});
279
+ if (data) setOrgs(data);
280
+ setLoading(false);
281
+ };
282
+ fetchOrgs();
283
+ }, [client]);
284
+ const handleSwitch = async (orgId) => {
285
+ await client.organization.setActive({ organizationId: orgId });
286
+ onSuccess?.(orgId);
287
+ };
288
+ if (loading) return /* @__PURE__ */ jsx2("div", { className: "ca-loading", children: "Loading..." });
289
+ return /* @__PURE__ */ jsxs2("div", { className: `ca-org-switcher ${className || ""}`, children: [
290
+ /* @__PURE__ */ jsx2("label", { className: "ca-label", children: "Select Organization" }),
291
+ /* @__PURE__ */ jsxs2(
292
+ "select",
293
+ {
294
+ className: "ca-select",
295
+ value: currentOrgId || "",
296
+ onChange: (e) => handleSwitch(e.target.value),
297
+ children: [
298
+ /* @__PURE__ */ jsx2("option", { value: "", disabled: true, children: "Select an organization" }),
299
+ orgs.map((org) => /* @__PURE__ */ jsx2("option", { value: org.id, children: org.name }, org.id))
300
+ ]
301
+ }
302
+ )
303
+ ] });
304
+ };
305
+ var InviteMemberForm = ({
306
+ client = authClient,
307
+ className,
308
+ onSuccess,
309
+ onError
310
+ }) => {
311
+ const [email, setEmail] = useState2("");
312
+ const [role, setRole] = useState2("member");
313
+ const [loading, setLoading] = useState2(false);
314
+ const handleSubmit = async (e) => {
315
+ e.preventDefault();
316
+ setLoading(true);
317
+ try {
318
+ const result = await client.organization.inviteMember({
319
+ email,
320
+ role
321
+ });
322
+ if (result.error) throw result.error;
323
+ setEmail("");
324
+ onSuccess?.(result.data);
325
+ } catch (err) {
326
+ onError?.(err.message || "Invitation failed");
327
+ } finally {
328
+ setLoading(false);
329
+ }
330
+ };
331
+ return /* @__PURE__ */ jsxs2("form", { onSubmit: handleSubmit, className: `ca-form ${className || ""}`, children: [
332
+ /* @__PURE__ */ jsx2("h3", { className: "ca-subtitle", children: "Invite Member" }),
333
+ /* @__PURE__ */ jsxs2("div", { className: "ca-input-group", children: [
334
+ /* @__PURE__ */ jsx2("label", { className: "ca-label", children: "Email Address" }),
335
+ /* @__PURE__ */ jsx2(
336
+ "input",
337
+ {
338
+ type: "email",
339
+ className: "ca-input",
340
+ value: email,
341
+ onChange: (e) => setEmail(e.target.value),
342
+ required: true,
343
+ placeholder: "colleague@example.com"
344
+ }
345
+ )
346
+ ] }),
347
+ /* @__PURE__ */ jsxs2("div", { className: "ca-input-group", children: [
348
+ /* @__PURE__ */ jsx2("label", { className: "ca-label", children: "Role" }),
349
+ /* @__PURE__ */ jsxs2(
350
+ "select",
351
+ {
352
+ className: "ca-select",
353
+ value: role,
354
+ onChange: (e) => setRole(e.target.value),
355
+ children: [
356
+ /* @__PURE__ */ jsx2("option", { value: "member", children: "Member" }),
357
+ /* @__PURE__ */ jsx2("option", { value: "admin", children: "Admin" }),
358
+ /* @__PURE__ */ jsx2("option", { value: "owner", children: "Owner" })
359
+ ]
360
+ }
361
+ )
362
+ ] }),
363
+ /* @__PURE__ */ jsx2("button", { type: "submit", className: "ca-button", disabled: loading, children: loading ? "Sending Invite..." : "Send Invite" })
364
+ ] });
365
+ };
366
+
367
+ export {
368
+ AuthForm,
369
+ CreateOrganizationForm,
370
+ OrganizationSwitcher,
371
+ InviteMemberForm
372
+ };
package/dist/cli.d.ts ADDED
@@ -0,0 +1 @@
1
+ #!/usr/bin/env node
package/dist/cli.js ADDED
@@ -0,0 +1,105 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ __require
4
+ } from "./chunk-R5U7XKVJ.js";
5
+
6
+ // src/cli.ts
7
+ import { cac } from "cac";
8
+ import { execSync } from "child_process";
9
+ import { readFileSync, writeFileSync, existsSync, mkdirSync } from "fs";
10
+ import { dirname, join, resolve } from "path";
11
+ import { fileURLToPath } from "url";
12
+ var __dirname = dirname(fileURLToPath(import.meta.url));
13
+ var cli = cac("content-auth");
14
+ cli.command("init", "Initialize database schema (SQL) in your project").option("-o, --output <path>", "Output path for schema file", { default: "./migrations/0000_auth.sql" }).option("--force", "Overwrite existing file").action((options) => {
15
+ const outputPath = resolve(process.cwd(), options.output);
16
+ const outputDir = dirname(outputPath);
17
+ if (existsSync(outputPath) && !options.force) {
18
+ console.error(`\u274C File already exists: ${outputPath}`);
19
+ console.error(" Use --force to overwrite, or specify a different output with -o");
20
+ process.exit(1);
21
+ }
22
+ try {
23
+ const schemaPath = join(__dirname, "..", "schema", "auth.sql");
24
+ if (!existsSync(schemaPath)) {
25
+ console.error(`\u274C Schema template not found at: ${schemaPath}`);
26
+ process.exit(1);
27
+ }
28
+ if (!existsSync(outputDir)) {
29
+ mkdirSync(outputDir, { recursive: true });
30
+ console.log(`\u{1F4C1} Created directory: ${outputDir}`);
31
+ }
32
+ const schemaContent = readFileSync(schemaPath, "utf-8");
33
+ writeFileSync(outputPath, schemaContent);
34
+ console.log(`\u2705 SQL schema initialized at: ${outputPath}`);
35
+ console.log("");
36
+ console.log("Next steps:");
37
+ console.log(" 1. Add your application tables at the bottom of the file");
38
+ console.log(" 2. Run migrations:");
39
+ console.log(" wrangler d1 migrations apply DB --local");
40
+ } catch (error) {
41
+ console.error("\u274C Failed to initialize schema:", error);
42
+ process.exit(1);
43
+ }
44
+ });
45
+ cli.command("init-drizzle", "Initialize Drizzle ORM schema (TypeScript) in your project").option("-o, --output <path>", "Output path for schema file", { default: "./src/db/schema.ts" }).option("--force", "Overwrite existing file").action((options) => {
46
+ const outputPath = resolve(process.cwd(), options.output);
47
+ const outputDir = dirname(outputPath);
48
+ if (existsSync(outputPath) && !options.force) {
49
+ console.error(`\u274C File already exists: ${outputPath}`);
50
+ console.error(" Use --force to overwrite, or specify a different output with -o");
51
+ process.exit(1);
52
+ }
53
+ try {
54
+ const templatePath = join(__dirname, "..", "schema", "schema.template.ts");
55
+ if (!existsSync(templatePath)) {
56
+ console.error(`\u274C Schema template not found at: ${templatePath}`);
57
+ process.exit(1);
58
+ }
59
+ if (!existsSync(outputDir)) {
60
+ mkdirSync(outputDir, { recursive: true });
61
+ console.log(`\u{1F4C1} Created directory: ${outputDir}`);
62
+ }
63
+ const templateContent = readFileSync(templatePath, "utf-8");
64
+ writeFileSync(outputPath, templateContent);
65
+ console.log(`\u2705 Drizzle schema initialized at: ${outputPath}`);
66
+ console.log("");
67
+ console.log("Next steps:");
68
+ console.log(" 1. Add your application tables at the bottom of the file");
69
+ console.log(" 2. Create drizzle.config.ts in your project root");
70
+ console.log(" 3. Generate migrations: npm run db:generate");
71
+ console.log(" 4. Apply migrations: npm run dev");
72
+ console.log("");
73
+ console.log("Recommended package.json scripts:");
74
+ console.log(' "db:generate": "drizzle-kit generate"');
75
+ console.log(' "db:check": "drizzle-kit check"');
76
+ console.log(' "db:migrate:local": "wrangler d1 migrations apply DB --local"');
77
+ } catch (error) {
78
+ console.error("\u274C Failed to initialize schema:", error);
79
+ process.exit(1);
80
+ }
81
+ });
82
+ cli.command("migrate", "Run database migrations (deprecated - use wrangler directly)").option("--db <db>", "Database name (for D1)", { default: "DB" }).action((options) => {
83
+ console.log("Running migrations...");
84
+ try {
85
+ console.log("Generating schema...");
86
+ execSync("npx @better-auth/cli generate", { stdio: "inherit" });
87
+ console.log("Migration generation complete. Please check for generated SQL files.");
88
+ console.log("To apply to D1, run: npx wrangler d1 execute <DATABASE_NAME> --local --file=<MIGRATION_FILE>");
89
+ } catch (error) {
90
+ console.error("Migration failed:", error);
91
+ process.exit(1);
92
+ }
93
+ });
94
+ cli.command("secret", "Generate a secure secret key for Better Auth").action(() => {
95
+ const { randomBytes } = __require("crypto");
96
+ const secret = randomBytes(32).toString("hex");
97
+ console.log("\nGenerated Secret Key:");
98
+ console.log(secret);
99
+ console.log("\nAdd this to your .dev.vars or environment variables:");
100
+ console.log(`BETTER_AUTH_SECRET="${secret}"
101
+ `);
102
+ });
103
+ cli.version("0.0.1");
104
+ cli.help();
105
+ cli.parse();