better-auth-nuxt 0.0.9 → 0.0.10-beta.20

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.
Files changed (60) hide show
  1. package/README.md +19 -270
  2. package/dist/module.d.mts +13 -40
  3. package/dist/module.json +8 -5
  4. package/dist/module.mjs +625 -358
  5. package/dist/runtime/adapters/convex.d.ts +111 -0
  6. package/dist/runtime/adapters/convex.js +213 -0
  7. package/dist/runtime/app/components/BetterAuthState.d.vue.ts +20 -0
  8. package/dist/runtime/app/components/BetterAuthState.vue +9 -0
  9. package/dist/runtime/app/components/BetterAuthState.vue.d.ts +20 -0
  10. package/dist/runtime/app/composables/useUserSession.d.ts +22 -0
  11. package/dist/runtime/app/composables/useUserSession.js +159 -0
  12. package/dist/runtime/app/middleware/auth.global.d.ts +13 -0
  13. package/dist/runtime/app/middleware/auth.global.js +37 -0
  14. package/dist/runtime/app/pages/__better-auth-devtools.d.vue.ts +3 -0
  15. package/dist/runtime/app/pages/__better-auth-devtools.vue +426 -0
  16. package/dist/runtime/app/pages/__better-auth-devtools.vue.d.ts +3 -0
  17. package/dist/runtime/app/plugins/session.client.d.ts +2 -0
  18. package/dist/runtime/app/plugins/session.client.js +15 -0
  19. package/dist/runtime/app/plugins/session.server.d.ts +2 -0
  20. package/dist/runtime/app/plugins/session.server.js +24 -0
  21. package/dist/runtime/config.d.ts +49 -0
  22. package/dist/runtime/config.js +11 -0
  23. package/dist/runtime/server/api/_better-auth/_schema.d.ts +16 -0
  24. package/dist/runtime/server/api/_better-auth/_schema.js +11 -0
  25. package/dist/runtime/server/api/_better-auth/accounts.get.d.ts +14 -0
  26. package/dist/runtime/server/api/_better-auth/accounts.get.js +28 -0
  27. package/dist/runtime/server/api/_better-auth/config.get.d.ts +35 -0
  28. package/dist/runtime/server/api/_better-auth/config.get.js +47 -0
  29. package/dist/runtime/server/api/_better-auth/sessions.delete.d.ts +4 -0
  30. package/dist/runtime/server/api/_better-auth/sessions.delete.js +22 -0
  31. package/dist/runtime/server/api/_better-auth/sessions.get.d.ts +14 -0
  32. package/dist/runtime/server/api/_better-auth/sessions.get.js +43 -0
  33. package/dist/runtime/server/api/_better-auth/users.get.d.ts +14 -0
  34. package/dist/runtime/server/api/_better-auth/users.get.js +34 -0
  35. package/dist/runtime/server/api/auth/[...all].d.ts +2 -0
  36. package/dist/runtime/server/api/auth/[...all].js +6 -0
  37. package/dist/runtime/server/middleware/route-access.d.ts +2 -0
  38. package/dist/runtime/server/middleware/route-access.js +29 -0
  39. package/dist/runtime/server/tsconfig.json +1 -1
  40. package/dist/runtime/server/utils/auth.d.ts +7 -0
  41. package/dist/runtime/server/utils/auth.js +81 -0
  42. package/dist/runtime/server/utils/session.d.ts +9 -0
  43. package/dist/runtime/server/utils/session.js +23 -0
  44. package/dist/runtime/types/augment.d.ts +42 -0
  45. package/dist/runtime/types/augment.js +0 -0
  46. package/dist/runtime/types.d.ts +23 -0
  47. package/dist/runtime/types.js +0 -0
  48. package/dist/runtime/utils/match-user.d.ts +2 -0
  49. package/dist/runtime/utils/match-user.js +13 -0
  50. package/dist/types.d.mts +8 -6
  51. package/package.json +82 -29
  52. package/LICENSE +0 -21
  53. package/dist/runtime/middleware/auth.d.ts +0 -2
  54. package/dist/runtime/middleware/auth.js +0 -31
  55. package/dist/runtime/plugin.d.ts +0 -2
  56. package/dist/runtime/plugin.js +0 -3
  57. package/dist/runtime/server/handler.d.ts +0 -2
  58. package/dist/runtime/server/handler.js +0 -5
  59. package/dist/runtime/server.d.ts +0 -6
  60. package/dist/runtime/server.js +0 -5
package/dist/module.mjs CHANGED
@@ -1,388 +1,655 @@
1
- import fs from 'node:fs';
2
- import { defineNuxtModule, createResolver, logger, addServerHandler, addTypeTemplate, addServerImports, addImports, addRouteMiddleware, addPlugin, addTemplate } from '@nuxt/kit';
1
+ import { randomBytes } from 'node:crypto';
2
+ import { existsSync, writeFileSync, readFileSync } from 'node:fs';
3
+ import { mkdir, writeFile } from 'node:fs/promises';
4
+ import { defineNuxtModule, createResolver, hasNuxtModule, addTemplate, addTypeTemplate, updateTemplates, addServerImportsDir, addServerImports, addServerScanDir, addServerHandler, addImportsDir, addPlugin, addComponentsDir, installModule, extendPages } from '@nuxt/kit';
5
+ import { consola as consola$1 } from 'consola';
3
6
  import { defu } from 'defu';
4
- import { resolve } from 'pathe';
5
- import { hash } from 'ohash';
6
- import { glob } from 'tinyglobby';
7
- import { pascalCase } from 'scule';
7
+ import { join, dirname } from 'pathe';
8
+ import { toRouteMatcher, createRouter } from 'radix3';
9
+ import { isCI, isTest } from 'std-env';
10
+ import { generateDrizzleSchema as generateDrizzleSchema$1 } from '@better-auth/cli/api';
11
+ import { getAuthTables } from 'better-auth/db';
12
+ export { defineClientAuth, defineServerAuth } from '../dist/runtime/config.js';
8
13
 
9
- async function serverAuth({ options }) {
10
- return [
11
- 'import mergeDeep from "@fastify/deepmerge"',
12
- 'import { betterAuth } from "better-auth"',
13
- ...options.configs.map((config) => {
14
- return `import ${config.key} from "${config.path}"`;
15
- }),
16
- "",
17
- "let _auth",
18
- "",
19
- "export function useAuth() {",
20
- " if (!_auth) {",
21
- "const betterAuthConfigs = mergeDeep({all: true})({},",
22
- "{",
23
- ...options.moduleOptions?.options?.server ? Object.entries(options.moduleOptions.options.server).map(([key, value]) => {
24
- return ` ${key}: ${JSON.stringify(value)},`;
25
- }) : [],
26
- "},",
27
- ...options.configs.map((config) => {
28
- return `${config.key}(),`;
29
- }),
30
- ")",
31
- "",
32
- " _auth = betterAuth(betterAuthConfigs)",
33
- " }",
34
- " return _auth",
35
- "}",
36
- "",
37
- "export const auth = useAuth()",
38
- ""
39
- ].join("\n");
14
+ const version = "0.0.10-beta.20";
15
+
16
+ function setupDevTools(nuxt) {
17
+ nuxt.hook("devtools:customTabs", (tabs) => {
18
+ tabs.push({
19
+ category: "server",
20
+ name: "better-auth",
21
+ title: "Auth",
22
+ icon: "simple-icons:betterauth",
23
+ view: {
24
+ type: "iframe",
25
+ src: "/__better-auth-devtools"
26
+ }
27
+ });
28
+ });
40
29
  }
41
30
 
42
- async function useUserSession({ options }) {
43
- return [
44
- "import { createAuthClient } from 'better-auth/vue'",
45
- ...options.configs.map((config) => {
46
- return `import ${config.key} from "${config.path}"`;
47
- }),
48
- "import { defu } from 'defu'",
49
- "import { computed, ref } from 'vue'",
50
- "import { navigateTo, useRequestHeaders, useRequestURL, useRuntimeConfig, useState } from '#app'",
51
- "",
52
- "let _authInstance",
53
- "",
54
- "export function createAuthInstance() {",
55
- " const url = useRequestURL()",
56
- " const headers = import.meta.server ? useRequestHeaders() : undefined",
57
- " const config = useRuntimeConfig()",
58
- "",
59
- " const authClient = createAuthClient({",
60
- " baseURL: url.origin,",
61
- ...options.moduleOptions?.options?.client ? Object.entries(options.moduleOptions.options.client).map(([key, value]) => {
62
- return ` ${key}: ${JSON.stringify(value)},`;
63
- }) : [],
64
- " fetchOptions: {",
65
- ...options.configs.map((config) => {
66
- return ` ...${config.key}?.fetchOptions || {},`;
67
- }),
68
- " headers,",
69
- " },",
70
- ...options.configs.map((config) => {
71
- return ` ${config.key},`;
72
- }),
73
- " plugins: [",
74
- ...options.configs.map((config) => {
75
- return ` ...${config.key}?.plugins || [],`;
76
- }),
77
- " ],",
78
- " })",
79
- "",
80
- " const options = defu(config.public.betterAuth.redirectOptions || {}, {",
81
- " redirectUserTo: '/profile',",
82
- " redirectGuestTo: '/signin',",
83
- " redirectUnauthorizedTo: '/401',",
84
- " })",
85
- "",
86
- " const session = useState('auth:session', () => null)",
87
- " const user = useState('auth:user', () => null)",
88
- " const sessionFetching = import.meta.server ? ref(false) : useState('auth:sessionFetching', () => false)",
89
- "",
90
- " const fetchSession = async () => {",
91
- " if (sessionFetching.value) {",
92
- " return",
93
- " }",
94
- " sessionFetching.value = true",
95
- " const { data } = await authClient.getSession({",
96
- " fetchOptions: {",
97
- " headers,",
98
- " },",
99
- " })",
100
- " session.value = data?.session || null",
101
- " user.value = data?.user || null",
102
- " sessionFetching.value = false",
103
- " return data",
104
- " }",
105
- "",
106
- " return {",
107
- " session,",
108
- " user,",
109
- " loggedIn: computed(() => !!session.value),",
110
- " signIn: authClient.signIn,",
111
- " signUp: authClient.signUp,",
112
- " options,",
113
- " fetchSession,",
114
- " client: authClient,",
115
- " signOut: async (options = {}) => {",
116
- " try {",
117
- " await authClient.signOut()",
118
- " if (options.redirectTo)",
119
- " await navigateTo(options.redirectTo)",
120
- " }",
121
- " catch (error) {",
122
- " console.error('Sign out failed:', error)",
123
- " throw error",
124
- " }",
125
- " },",
126
- " }",
127
- "}",
128
- "",
129
- "// Setup session listener for client-side updates",
130
- "function setupSessionListener(client) {",
131
- " if (!import.meta.client)",
132
- " return",
133
- "",
134
- " client.$store.listen('$sessionSignal', async (signal) => {",
135
- " if (!signal)",
136
- " return",
137
- " await client.useSession($fetch)",
138
- " })",
139
- "}",
140
- "",
141
- "export function useUserSession() {",
142
- " if (_authInstance && import.meta.client)",
143
- " return _authInstance",
144
- "",
145
- " const auth = createAuthInstance()",
146
- "",
147
- " if (import.meta.client) {",
148
- " setupSessionListener(auth.client)",
149
- " _authInstance = auth",
150
- " }",
151
- "",
152
- " return auth",
153
- "}",
154
- ""
155
- ].join("\n");
31
+ function dialectToProvider(dialect) {
32
+ return dialect === "postgresql" ? "pg" : dialect;
33
+ }
34
+ async function generateDrizzleSchema(authOptions, dialect, schemaOptions) {
35
+ const provider = dialectToProvider(dialect);
36
+ const options = {
37
+ ...authOptions,
38
+ advanced: {
39
+ ...authOptions.advanced,
40
+ database: {
41
+ ...authOptions.advanced?.database,
42
+ ...schemaOptions?.useUuid && { generateId: "uuid" }
43
+ }
44
+ }
45
+ };
46
+ const adapter = {
47
+ id: "drizzle",
48
+ options: {
49
+ provider,
50
+ camelCase: schemaOptions?.casing !== "snake_case",
51
+ adapterConfig: { usePlural: schemaOptions?.usePlural ?? false }
52
+ }
53
+ };
54
+ const result = await generateDrizzleSchema$1({ adapter, options });
55
+ if (!result.code) {
56
+ throw new Error(`Schema generation returned empty result for ${dialect}`);
57
+ }
58
+ return result.code;
59
+ }
60
+ const convexIndexFields = {
61
+ account: ["accountId", ["accountId", "providerId"], ["providerId", "userId"]],
62
+ rateLimit: ["key"],
63
+ session: ["expiresAt", ["expiresAt", "userId"]],
64
+ verification: ["expiresAt", "identifier"],
65
+ user: [["email", "name"], "name", "userId"],
66
+ passkey: ["credentialID"],
67
+ oauthConsent: [["clientId", "userId"]]
68
+ };
69
+ function getConvexSpecialFields(tables) {
70
+ return Object.fromEntries(
71
+ Object.entries(tables).map(([key, table]) => {
72
+ const fields = Object.fromEntries(
73
+ Object.entries(table.fields).map(([fieldKey, field]) => [
74
+ field.fieldName ?? fieldKey,
75
+ {
76
+ ...field.sortable ? { sortable: true } : {},
77
+ ...field.unique ? { unique: true } : {},
78
+ ...field.references ? { references: field.references } : {}
79
+ }
80
+ ]).filter(([_key, value]) => typeof value === "object" ? Object.keys(value).length > 0 : true)
81
+ );
82
+ return [key, fields];
83
+ }).filter(([_key, value]) => typeof value === "object" ? Object.keys(value).length > 0 : true)
84
+ );
156
85
  }
86
+ function getMergedConvexIndexFields(tables) {
87
+ return Object.fromEntries(
88
+ Object.entries(tables).map(([key, table]) => {
89
+ const manualIndexes = convexIndexFields[key]?.map((index) => {
90
+ return typeof index === "string" ? table.fields[index]?.fieldName ?? index : index.map((i) => table.fields[i]?.fieldName ?? i);
91
+ }) || [];
92
+ const specialFieldsObj = getConvexSpecialFields(tables);
93
+ const specialFieldIndexes = Object.keys(specialFieldsObj[key] || {}).filter(
94
+ (index) => !manualIndexes.some((m) => Array.isArray(m) ? m[0] === index : m === index)
95
+ );
96
+ return [key, manualIndexes.concat(specialFieldIndexes)];
97
+ })
98
+ );
99
+ }
100
+ async function generateConvexSchema(authOptions) {
101
+ const tables = getAuthTables(authOptions);
102
+ let code = `/**
103
+ * Auto-generated Better Auth tables for Convex.
104
+ * Import these tables in your convex/schema.ts:
105
+ *
106
+ * import { defineSchema } from 'convex/server'
107
+ * import { authTables } from './_generated/auth-tables'
108
+ *
109
+ * export default defineSchema({ ...authTables, ...yourTables })
110
+ */
157
111
 
158
- const module = defineNuxtModule({
159
- meta: {
160
- name: "better-auth",
161
- configKey: "betterAuth"
162
- },
163
- // Default configuration options of the Nuxt module
112
+ import { defineTable } from 'convex/server'
113
+ import { v } from 'convex/values'
114
+
115
+ export const authTables = {
116
+ `;
117
+ const getType = (_name, field) => {
118
+ const type = field.type;
119
+ const typeMap = {
120
+ "string": "v.string()",
121
+ "boolean": "v.boolean()",
122
+ "number": "v.number()",
123
+ "date": "v.number()",
124
+ "json": "v.string()",
125
+ "number[]": "v.array(v.number())",
126
+ "string[]": "v.array(v.string())"
127
+ };
128
+ return typeMap[type];
129
+ };
130
+ for (const tableKey in tables) {
131
+ const table = tables[tableKey];
132
+ const modelName = table.modelName;
133
+ const fields = Object.fromEntries(Object.entries(table.fields).filter(([key]) => key !== "id"));
134
+ const indexes = getMergedConvexIndexFields(tables)[tableKey]?.map((index) => {
135
+ const indexArray = Array.isArray(index) ? index.sort() : [index];
136
+ const indexName = indexArray.join("_");
137
+ return `.index('${indexName}', ${JSON.stringify(indexArray)})`;
138
+ }) || [];
139
+ const schema = `${modelName}: defineTable({
140
+ ${Object.keys(fields).map((field) => {
141
+ const attr = fields[field];
142
+ const type = getType(field, attr);
143
+ const optional = (fieldSchema) => attr.required ? fieldSchema : `v.optional(v.union(v.null(), ${fieldSchema}))`;
144
+ return ` ${attr.fieldName ?? field}: ${optional(type)},`;
145
+ }).join("\n")}
146
+ })${indexes.length > 0 ? `
147
+ ${indexes.join("\n ")}` : ""},
148
+ `;
149
+ code += ` ${schema}`;
150
+ }
151
+ code += `}
152
+ `;
153
+ return code;
154
+ }
155
+ async function loadUserAuthConfig(configPath, throwOnError = false) {
156
+ const { createJiti } = await import('jiti');
157
+ const { defineServerAuth } = await import('../dist/runtime/config.js');
158
+ const jiti = createJiti(import.meta.url, { interopDefault: true, moduleCache: false });
159
+ if (!globalThis.defineServerAuth) {
160
+ defineServerAuth._count = 0;
161
+ globalThis.defineServerAuth = defineServerAuth;
162
+ }
163
+ globalThis.defineServerAuth._count++;
164
+ try {
165
+ const mod = await jiti.import(configPath);
166
+ const configFn = mod.default;
167
+ if (typeof configFn === "function") {
168
+ return configFn({ runtimeConfig: {}, db: null });
169
+ }
170
+ consola$1.warn("[@onmax/nuxt-better-auth] auth.config.ts does not export default. Expected: export default defineServerAuth(...)");
171
+ if (throwOnError) {
172
+ throw new Error("auth.config.ts must export default defineServerAuth(...)");
173
+ }
174
+ return {};
175
+ } catch (error) {
176
+ if (throwOnError) {
177
+ throw new Error(`Failed to load auth config: ${error instanceof Error ? error.message : error}`);
178
+ }
179
+ consola$1.error("[@onmax/nuxt-better-auth] Failed to load auth config for schema generation. Schema may be incomplete:", error);
180
+ return {};
181
+ } finally {
182
+ globalThis.defineServerAuth._count--;
183
+ if (!globalThis.defineServerAuth._count) {
184
+ globalThis.defineServerAuth = void 0;
185
+ }
186
+ }
187
+ }
188
+
189
+ function getHubDialect(hub) {
190
+ if (!hub?.db)
191
+ return void 0;
192
+ if (typeof hub.db === "string")
193
+ return hub.db;
194
+ if (typeof hub.db === "object" && hub.db !== null)
195
+ return hub.db.dialect;
196
+ return void 0;
197
+ }
198
+ function getHubCasing(hub) {
199
+ if (!hub?.db || typeof hub.db !== "object" || hub.db === null)
200
+ return void 0;
201
+ return hub.db.casing;
202
+ }
203
+ const consola = consola$1.withTag("nuxt-better-auth");
204
+ function resolveConvexUrl(nuxt) {
205
+ const convexConfig = nuxt.options.convex;
206
+ if (convexConfig?.url)
207
+ return convexConfig.url;
208
+ const runtimeUrl = nuxt.options.runtimeConfig.public?.convex?.url;
209
+ if (runtimeUrl)
210
+ return runtimeUrl;
211
+ if (process.env.CONVEX_URL)
212
+ return process.env.CONVEX_URL;
213
+ if (process.env.NUXT_PUBLIC_CONVEX_URL)
214
+ return process.env.NUXT_PUBLIC_CONVEX_URL;
215
+ return "";
216
+ }
217
+ const generateSecret = () => randomBytes(32).toString("hex");
218
+ function readEnvFile(rootDir) {
219
+ const envPath = join(rootDir, ".env");
220
+ return existsSync(envPath) ? readFileSync(envPath, "utf-8") : "";
221
+ }
222
+ function hasEnvSecret(rootDir) {
223
+ const match = readEnvFile(rootDir).match(/^BETTER_AUTH_SECRET=(.+)$/m);
224
+ return !!match && !!match[1] && match[1].trim().length > 0;
225
+ }
226
+ function appendSecretToEnv(rootDir, secret) {
227
+ const envPath = join(rootDir, ".env");
228
+ let content = readEnvFile(rootDir);
229
+ if (content.length > 0 && !content.endsWith("\n"))
230
+ content += "\n";
231
+ content += `BETTER_AUTH_SECRET=${secret}
232
+ `;
233
+ writeFileSync(envPath, content, "utf-8");
234
+ }
235
+ async function promptForSecret(rootDir) {
236
+ if (process.env.BETTER_AUTH_SECRET || hasEnvSecret(rootDir))
237
+ return void 0;
238
+ if (isCI || isTest) {
239
+ const secret2 = generateSecret();
240
+ appendSecretToEnv(rootDir, secret2);
241
+ consola.info("Generated BETTER_AUTH_SECRET and added to .env (CI mode)");
242
+ return secret2;
243
+ }
244
+ consola.box("BETTER_AUTH_SECRET is required for authentication.\nThis will be appended to your .env file.");
245
+ const choice = await consola.prompt("How do you want to set it?", {
246
+ type: "select",
247
+ options: [
248
+ { label: "Generate for me", value: "generate", hint: "uses crypto.randomBytes(32)" },
249
+ { label: "Enter manually", value: "paste" },
250
+ { label: "Skip", value: "skip", hint: "will fail in production" }
251
+ ],
252
+ cancel: "null"
253
+ });
254
+ if (typeof choice === "symbol" || choice === "skip") {
255
+ consola.warn("Skipping BETTER_AUTH_SECRET. Auth will fail without it in production.");
256
+ return void 0;
257
+ }
258
+ let secret;
259
+ if (choice === "generate") {
260
+ secret = generateSecret();
261
+ } else {
262
+ const input = await consola.prompt("Paste your secret (min 32 chars):", { type: "text", cancel: "null" });
263
+ if (typeof input === "symbol" || !input || input.length < 32) {
264
+ consola.warn("Invalid secret. Skipping.");
265
+ return void 0;
266
+ }
267
+ secret = input;
268
+ }
269
+ const preview = `${secret.slice(0, 8)}...${secret.slice(-4)}`;
270
+ const confirm = await consola.prompt(`Add to .env:
271
+ BETTER_AUTH_SECRET=${preview}
272
+ Proceed?`, { type: "confirm", initial: true, cancel: "null" });
273
+ if (typeof confirm === "symbol" || !confirm) {
274
+ consola.info("Cancelled. Secret not written.");
275
+ return void 0;
276
+ }
277
+ appendSecretToEnv(rootDir, secret);
278
+ consola.success("Added BETTER_AUTH_SECRET to .env");
279
+ return secret;
280
+ }
281
+ const module$1 = defineNuxtModule({
282
+ meta: { name: "better-auth-nuxt", version, configKey: "auth", compatibility: { nuxt: ">=3.0.0" } },
164
283
  defaults: {
165
- endpoint: "/api/auth/**",
166
- serverConfigs: [],
167
- redirectOptions: {
168
- redirectUserTo: "/auth/login",
169
- redirectGuestTo: "/",
170
- redirectUnauthorizedTo: "/401"
284
+ clientOnly: false,
285
+ serverConfig: "server/auth.config",
286
+ clientConfig: "app/auth.config",
287
+ redirects: { login: "/login", guest: "/" },
288
+ secondaryStorage: false
289
+ },
290
+ async onInstall(nuxt) {
291
+ const generatedSecret = await promptForSecret(nuxt.options.rootDir);
292
+ if (generatedSecret)
293
+ process.env.BETTER_AUTH_SECRET = generatedSecret;
294
+ const serverPath = join(nuxt.options.rootDir, "server/auth.config.ts");
295
+ const clientPath = join(nuxt.options.srcDir, "auth.config.ts");
296
+ const serverTemplate = `import { defineServerAuth } from 'better-auth-nuxt/config'
297
+
298
+ export default defineServerAuth({
299
+ emailAndPassword: { enabled: true },
300
+ })
301
+ `;
302
+ const clientTemplate = `import { defineClientAuth } from 'better-auth-nuxt/config'
303
+
304
+ export default defineClientAuth({})
305
+ `;
306
+ if (!existsSync(serverPath)) {
307
+ await mkdir(dirname(serverPath), { recursive: true });
308
+ await writeFile(serverPath, serverTemplate);
309
+ consola.success("Created server/auth.config.ts");
310
+ }
311
+ if (!existsSync(clientPath)) {
312
+ await mkdir(dirname(clientPath), { recursive: true });
313
+ await writeFile(clientPath, clientTemplate);
314
+ const relativePath = clientPath.replace(`${nuxt.options.rootDir}/`, "");
315
+ consola.success(`Created ${relativePath}`);
171
316
  }
172
317
  },
173
318
  async setup(options, nuxt) {
174
319
  const resolver = createResolver(import.meta.url);
175
- nuxt.options.vite.optimizeDeps ??= {};
176
- nuxt.options.vite.optimizeDeps.include ??= [];
177
- nuxt.options.vite.optimizeDeps.include.push(...[
178
- "better-auth/client",
179
- "better-auth/vue"
180
- ]);
181
- if (!options.endpoint) {
182
- logger.withTag("better-auth").error("Missing endpoint option");
320
+ if (options.clientConfig === "app/auth.config") {
321
+ const srcDirRelative = nuxt.options.srcDir.replace(`${nuxt.options.rootDir}/`, "");
322
+ options.clientConfig = srcDirRelative === nuxt.options.srcDir ? "auth.config" : `${srcDirRelative}/auth.config`;
183
323
  }
184
- nuxt.options.runtimeConfig.public.betterAuth = defu(nuxt.options.runtimeConfig.public.betterAuth, {
185
- endpoint: options.endpoint,
186
- redirectOptions: options.redirectOptions
187
- });
188
- nuxt.options.alias["#better-auth"] = resolve("./runtime");
189
- addServerHandler({
190
- route: options.endpoint,
191
- handler: resolver.resolve("./runtime/server/handler")
324
+ const clientOnly = options.clientOnly;
325
+ const serverConfigFile = options.serverConfig;
326
+ const clientConfigFile = options.clientConfig;
327
+ const serverConfigPath = resolver.resolve(nuxt.options.rootDir, serverConfigFile);
328
+ const clientConfigPath = resolver.resolve(nuxt.options.rootDir, clientConfigFile);
329
+ const serverConfigExists = existsSync(`${serverConfigPath}.ts`) || existsSync(`${serverConfigPath}.js`);
330
+ const clientConfigExists = existsSync(`${clientConfigPath}.ts`) || existsSync(`${clientConfigPath}.js`);
331
+ if (!clientOnly && !serverConfigExists)
332
+ throw new Error(`[nuxt-better-auth] Missing ${serverConfigFile}.ts - export default defineServerAuth(...)`);
333
+ if (!clientConfigExists)
334
+ throw new Error(`[nuxt-better-auth] Missing ${clientConfigFile}.ts - export default defineClientAuth(...)`);
335
+ const hasNuxtHub = hasNuxtModule("@nuxthub/core", nuxt);
336
+ const hub = hasNuxtHub ? nuxt.options.hub : void 0;
337
+ const hasHubDb = !clientOnly && hasNuxtHub && !!hub?.db;
338
+ const hasConvex = hasNuxtModule("nuxt-convex", nuxt);
339
+ const convexUrl = resolveConvexUrl(nuxt);
340
+ const hasConvexDb = !clientOnly && hasConvex && !!convexUrl && !hasHubDb;
341
+ if (hasConvexDb) {
342
+ consola.info("Detected Convex - using Convex HTTP adapter for Better Auth database");
343
+ }
344
+ let secondaryStorageEnabled = options.secondaryStorage ?? false;
345
+ if (secondaryStorageEnabled && clientOnly) {
346
+ consola.warn("secondaryStorage is not available in clientOnly mode. Disabling.");
347
+ secondaryStorageEnabled = false;
348
+ } else if (secondaryStorageEnabled && (!hasNuxtHub || !hub?.kv)) {
349
+ consola.warn("secondaryStorage requires @nuxthub/core with hub.kv: true. Disabling.");
350
+ secondaryStorageEnabled = false;
351
+ }
352
+ nuxt.options.runtimeConfig.public = nuxt.options.runtimeConfig.public || {};
353
+ nuxt.options.runtimeConfig.public.auth = defu(nuxt.options.runtimeConfig.public.auth, {
354
+ redirects: { login: options.redirects?.login ?? "/login", guest: options.redirects?.guest ?? "/" },
355
+ useDatabase: hasHubDb || hasConvexDb,
356
+ clientOnly
192
357
  });
193
- const registerTemplate = (options2) => {
194
- const name = options2.filename.replace(/\.m?js$/, "");
195
- const alias = "#" + name;
196
- const results = addTemplate({
197
- ...options2,
198
- write: true
199
- // Write to disk for Nitro to consume
200
- });
201
- nuxt.options.nitro.alias ||= {};
202
- nuxt.options.nitro.externals ||= {};
203
- nuxt.options.nitro.externals.inline ||= [];
204
- nuxt.options.alias[alias] = results.dst;
205
- nuxt.options.nitro.alias[alias] = nuxt.options.alias[alias];
206
- nuxt.options.nitro.externals.inline.push(nuxt.options.alias[alias]);
207
- nuxt.options.nitro.externals.inline.push(alias);
208
- return results;
209
- };
210
- const serverConfigs = [
211
- {
212
- key: pascalCase(hash("better-auth-configs")),
213
- path: resolver.resolve("./runtime/server")
358
+ if (clientOnly) {
359
+ const siteUrl = process.env.NUXT_PUBLIC_SITE_URL || nuxt.options.runtimeConfig.public.siteUrl;
360
+ if (!siteUrl) {
361
+ consola.warn("clientOnly mode: NUXT_PUBLIC_SITE_URL should be set to your frontend URL");
214
362
  }
215
- ];
216
- for (const layer of nuxt.options._layers) {
217
- const paths = await glob([
218
- "**/*.better-auth.ts",
219
- ...options.serverConfigs?.map((pattern) => {
220
- return `**/${pattern}.ts`;
221
- }) || []
222
- ], { onlyFiles: true, ignore: nuxt.options.ignore, dot: true, cwd: layer.config.rootDir, absolute: true });
223
- const pathsJS = await glob([
224
- "**/*.better-auth.js",
225
- ...options.serverConfigs?.map((pattern) => {
226
- return `**/${pattern}.js`;
227
- }) || []
228
- ], { cwd: layer.config.serverDir });
229
- if (paths.length === 0 && pathsJS.length === 0) {
230
- continue;
363
+ consola.info("clientOnly mode enabled - server utilities (serverAuth, getUserSession, requireUserSession) are not available");
364
+ }
365
+ if (!clientOnly) {
366
+ const currentSecret = nuxt.options.runtimeConfig.betterAuthSecret;
367
+ nuxt.options.runtimeConfig.betterAuthSecret = currentSecret || process.env.BETTER_AUTH_SECRET || "";
368
+ const betterAuthSecret = nuxt.options.runtimeConfig.betterAuthSecret;
369
+ if (!nuxt.options.dev && !nuxt.options._prepare && !betterAuthSecret) {
370
+ throw new Error("[nuxt-better-auth] BETTER_AUTH_SECRET is required in production. Set BETTER_AUTH_SECRET or NUXT_BETTER_AUTH_SECRET environment variable.");
231
371
  }
232
- for (const path of [...paths, ...pathsJS]) {
233
- if (fs.existsSync(path)) {
234
- serverConfigs.push({
235
- key: pascalCase(hash(path)),
236
- path
237
- });
238
- }
372
+ if (betterAuthSecret && betterAuthSecret.length < 32) {
373
+ throw new Error("[nuxt-better-auth] BETTER_AUTH_SECRET must be at least 32 characters for security");
239
374
  }
375
+ nuxt.options.runtimeConfig.auth = defu(nuxt.options.runtimeConfig.auth, {
376
+ secondaryStorage: secondaryStorageEnabled
377
+ });
240
378
  }
241
- const server = registerTemplate({
242
- filename: "better-auth/server.mjs",
243
- getContents: serverAuth,
244
- options: { configs: serverConfigs, moduleOptions: options }
245
- });
246
- addTypeTemplate({
247
- filename: "better-auth/server.d.ts",
248
- getContents: () => {
249
- return [
250
- 'import { betterAuth } from "better-auth"',
251
- 'import mergeDeep from "@fastify/deepmerge"',
252
- ...serverConfigs.map((config) => {
253
- return `import ${config.key} from "${config.path}"`;
254
- }),
255
- "const betterAuthConfigs = mergeDeep({all: true})({},",
256
- ...serverConfigs.map((config) => {
257
- return `${config.key}(),`;
258
- }),
259
- ")",
260
- "export type BetterAuth = ReturnType<typeof betterAuth<typeof betterAuthConfigs>>",
261
- "export declare const useAuth: () => BetterAuth",
262
- "export declare const auth: BetterAuth"
263
- ].join("\n");
379
+ nuxt.options.alias["#nuxt-better-auth"] = resolver.resolve("./runtime/types/augment");
380
+ if (!clientOnly)
381
+ nuxt.options.alias["#auth/server"] = serverConfigPath;
382
+ nuxt.options.alias["#auth/client"] = clientConfigPath;
383
+ if (!clientOnly) {
384
+ if (secondaryStorageEnabled && !nuxt.options.alias["hub:kv"]) {
385
+ throw new Error("[nuxt-better-auth] hub:kv not found. Ensure @nuxthub/core is loaded before this module and hub.kv is enabled.");
264
386
  }
265
- });
266
- const clientConfigs = [];
267
- for (const layer of nuxt.options._layers) {
268
- const paths = await glob([
269
- "**/*.better-auth-client.ts",
270
- ...options.clientConfigs?.map((pattern) => {
271
- return `**/${pattern}.ts`;
272
- }) || []
273
- ], { onlyFiles: true, ignore: nuxt.options.ignore, dot: true, cwd: layer.config.rootDir, absolute: true });
274
- const pathsJS = await glob([
275
- "**/*.better-auth.js",
276
- ...options.serverConfigs?.map((pattern) => {
277
- return `**/${pattern}.js`;
278
- }) || []
279
- ], { cwd: layer.config.serverDir });
280
- if (paths.length === 0 && pathsJS.length === 0) {
281
- continue;
387
+ const secondaryStorageCode = secondaryStorageEnabled ? `import { kv } from '@nuxthub/kv'
388
+ export function createSecondaryStorage() {
389
+ return {
390
+ get: async (key) => kv.get(\`_auth:\${key}\`),
391
+ set: async (key, value, ttl) => kv.set(\`_auth:\${key}\`, value, { ttl }),
392
+ delete: async (key) => kv.del(\`_auth:\${key}\`),
393
+ }
394
+ }` : `export function createSecondaryStorage() { return undefined }`;
395
+ const secondaryStorageTemplate = addTemplate({ filename: "better-auth/secondary-storage.mjs", getContents: () => secondaryStorageCode, write: true });
396
+ nuxt.options.alias["#auth/secondary-storage"] = secondaryStorageTemplate.dst;
397
+ if (hasHubDb && !nuxt.options.alias["hub:db"]) {
398
+ throw new Error("[nuxt-better-auth] hub:db not found. Ensure @nuxthub/core is loaded before this module and hub.db is configured.");
282
399
  }
283
- for (const path of [...paths, ...pathsJS]) {
284
- if (fs.existsSync(path)) {
285
- clientConfigs.push({
286
- key: pascalCase(hash(path)),
287
- path
288
- });
289
- }
400
+ const hubDialect = getHubDialect(hub) ?? "sqlite";
401
+ const usePlural = options.schema?.usePlural ?? false;
402
+ const camelCase = (options.schema?.casing ?? getHubCasing(hub)) !== "snake_case";
403
+ let databaseCode;
404
+ if (hasHubDb) {
405
+ databaseCode = `import { db, schema } from '@nuxthub/db'
406
+ import { drizzleAdapter } from 'better-auth/adapters/drizzle'
407
+ const rawDialect = '${hubDialect}'
408
+ const dialect = rawDialect === 'postgresql' ? 'pg' : rawDialect
409
+ export function createDatabase() { return drizzleAdapter(db, { provider: dialect, schema, usePlural: ${usePlural}, camelCase: ${camelCase} }) }
410
+ export { db }`;
411
+ } else if (hasConvexDb) {
412
+ nuxt.options.runtimeConfig.betterAuth = defu(
413
+ nuxt.options.runtimeConfig.betterAuth || {},
414
+ { convexUrl }
415
+ );
416
+ databaseCode = `import { useRuntimeConfig } from '#imports'
417
+ import { createConvexHttpAdapter } from 'better-auth-nuxt/adapters/convex'
418
+ import { api } from '#convex/api'
419
+
420
+ export function createDatabase() {
421
+ const config = useRuntimeConfig()
422
+ const convexUrl = config.betterAuth?.convexUrl || config.public?.convex?.url
423
+ if (!convexUrl) throw new Error('[nuxt-better-auth] CONVEX_URL not configured')
424
+ return createConvexHttpAdapter({ url: convexUrl, api: api.auth })
425
+ }
426
+ export const db = undefined`;
427
+ } else {
428
+ databaseCode = `export function createDatabase() { return undefined }
429
+ export const db = undefined`;
290
430
  }
431
+ const databaseTemplate = addTemplate({ filename: "better-auth/database.mjs", getContents: () => databaseCode, write: true });
432
+ nuxt.options.alias["#auth/database"] = databaseTemplate.dst;
433
+ addTypeTemplate({
434
+ filename: "types/auth-secondary-storage.d.ts",
435
+ getContents: () => `
436
+ declare module '#auth/secondary-storage' {
437
+ interface SecondaryStorage {
438
+ get: (key: string) => Promise<string | null>
439
+ set: (key: string, value: unknown, ttl?: number) => Promise<void>
440
+ delete: (key: string) => Promise<void>
441
+ }
442
+ export function createSecondaryStorage(): SecondaryStorage | undefined
443
+ }
444
+ `
445
+ }, { nitro: true, node: true });
446
+ addTypeTemplate({
447
+ filename: "types/auth-database.d.ts",
448
+ getContents: () => `
449
+ declare module '#auth/database' {
450
+ import type { drizzleAdapter } from 'better-auth/adapters/drizzle'
451
+ export function createDatabase(): ReturnType<typeof drizzleAdapter> | undefined
452
+ export const db: unknown
453
+ }
454
+ `
455
+ }, { nitro: true, node: true });
456
+ addTypeTemplate({
457
+ filename: "types/nuxt-better-auth-infer.d.ts",
458
+ getContents: () => `
459
+ import type { InferUser, InferSession, InferPluginTypes } from 'better-auth'
460
+ import type { RuntimeConfig } from 'nuxt/schema'
461
+ import type createServerAuth from '${serverConfigPath}'
462
+
463
+ type _Config = ReturnType<typeof createServerAuth>
464
+
465
+ declare module '#nuxt-better-auth' {
466
+ interface AuthUser extends InferUser<_Config> {}
467
+ interface AuthSession extends InferSession<_Config> {}
468
+ interface ServerAuthContext {
469
+ runtimeConfig: RuntimeConfig
470
+ ${hasHubDb ? `db: typeof import('@nuxthub/db')['db']` : ""}
471
+ }
472
+ type PluginTypes = InferPluginTypes<_Config>
473
+ }
474
+
475
+ // Augment the config module to use the extended ServerAuthContext
476
+ interface _AugmentedServerAuthContext {
477
+ runtimeConfig: RuntimeConfig
478
+ ${hasHubDb ? `db: typeof import('@nuxthub/db')['db']` : "db: unknown"}
479
+ }
480
+
481
+ declare module 'better-auth-nuxt/config' {
482
+ import type { BetterAuthOptions } from 'better-auth'
483
+ type ServerAuthConfig = Omit<BetterAuthOptions, 'database' | 'secret' | 'baseURL'>
484
+ export function defineServerAuth<T extends ServerAuthConfig>(config: T | ((ctx: _AugmentedServerAuthContext) => T)): (ctx: _AugmentedServerAuthContext) => T
485
+ }
486
+ `
487
+ }, { nuxt: true, nitro: true, node: true });
488
+ addTypeTemplate({
489
+ filename: "types/nuxt-better-auth-nitro.d.ts",
490
+ getContents: () => `
491
+ declare module 'nitro' {
492
+ interface NitroRouteRules {
493
+ auth?: import('${resolver.resolve("./runtime/types")}').AuthMeta
494
+ }
495
+ interface NitroRouteConfig {
496
+ auth?: import('${resolver.resolve("./runtime/types")}').AuthMeta
497
+ }
498
+ }
499
+ declare module 'nitro/types' {
500
+ interface NitroRouteRules {
501
+ auth?: import('${resolver.resolve("./runtime/types")}').AuthMeta
502
+ }
503
+ interface NitroRouteConfig {
504
+ auth?: import('${resolver.resolve("./runtime/types")}').AuthMeta
505
+ }
506
+ }
507
+ export {}
508
+ `
509
+ }, { nuxt: true, nitro: true, node: true });
291
510
  }
292
- const client = registerTemplate({
293
- filename: "better-auth/client.mjs",
294
- getContents: useUserSession,
295
- options: { configs: clientConfigs, moduleOptions: options }
511
+ addTypeTemplate({
512
+ filename: "types/nuxt-better-auth.d.ts",
513
+ getContents: () => `
514
+ export * from '${resolver.resolve("./runtime/types/augment")}'
515
+ export type { AuthMeta, AuthMode, AuthRouteRules, UserMatch, RequireSessionOptions, Auth, InferUser, InferSession } from '${resolver.resolve("./runtime/types")}'
516
+ `
296
517
  });
297
518
  addTypeTemplate({
298
- filename: "better-auth/client.d.ts",
299
- getContents: () => {
300
- return [
301
- 'import { createAuthInstance } from "./client.mjs"',
302
- 'import type { ClientOptions, InferSessionFromClient, InferUserFromClient } from "better-auth"',
303
- 'import type { RouteLocationRaw } from "vue-router"',
304
- 'import { Ref, ComputedRef } from "vue"',
305
- ...clientConfigs.map((config) => {
306
- return `import ${config.key} from "${config.path}"`;
307
- }),
308
- "export interface RuntimeAuthConfig {",
309
- " redirectUserTo: RouteLocationRaw | string",
310
- " redirectGuestTo: RouteLocationRaw | string",
311
- " redirectUnauthorizedTo: RouteLocationRaw | string",
312
- "}",
313
- "export interface AuthSignOutOptions {",
314
- " redirectTo?: RouteLocationRaw",
315
- "}",
316
- "export type AuthClient = ReturnType<typeof createAuthInstance>",
317
- "export declare const useUserSession: () => AuthClient"
318
- ].join("\n");
319
- }
519
+ filename: "types/nuxt-better-auth-client.d.ts",
520
+ getContents: () => `
521
+ import type createAppAuthClient from '${clientConfigPath}'
522
+ declare module '#nuxt-better-auth' {
523
+ export type AppAuthClient = ReturnType<typeof createAppAuthClient>
524
+ }
525
+ `
320
526
  });
321
- addServerImports([
322
- {
323
- from: server.dst,
324
- name: "useAuth"
325
- },
326
- {
327
- from: server.dst,
328
- name: "auth"
329
- }
330
- ]);
331
- addImports([
332
- {
333
- from: client.dst,
334
- name: "useUserSession"
335
- },
336
- {
337
- from: client.dst,
338
- name: "createAuthInstance"
527
+ nuxt.hook("builder:watch", async (_event, relativePath) => {
528
+ if (relativePath.includes("auth.config")) {
529
+ await updateTemplates({ filter: (t) => t.filename.includes("nuxt-better-auth") });
339
530
  }
340
- ]);
341
- addRouteMiddleware({
342
- name: "auth",
343
- path: resolver.resolve("./runtime/middleware/auth"),
344
- global: true
345
531
  });
346
- addTypeTemplate({
347
- filename: "types/better-auth-middleware.d.ts",
348
- getContents: () => {
349
- return [
350
- " type MiddlewareOptions = false | {",
351
- " /**",
352
- " * Only apply auth middleware to guest or user",
353
- " */",
354
- " only?:",
355
- ' | "guest"',
356
- ' | "user"',
357
- ' | "member"',
358
- ' | "admin"',
359
- " /**",
360
- " * Redirect authenticated user to this route",
361
- " */",
362
- " redirectUserTo?: string",
363
- " /**",
364
- " * Redirect guest to this route",
365
- " */",
366
- " redirectGuestTo?: string",
367
- " redirectUnauthorizedTo?: string",
368
- " }",
369
- ' declare module "#app" {',
370
- " interface PageMeta {",
371
- " auth?: MiddlewareOptions",
372
- " }",
373
- " }",
374
- ' declare module "vue-router" {',
375
- " interface RouteMeta {",
376
- " auth?: MiddlewareOptions",
377
- " }",
378
- " }",
379
- "export {}"
380
- ].join("\n");
381
- },
382
- write: true
532
+ if (!clientOnly) {
533
+ addServerImportsDir(resolver.resolve("./runtime/server/utils"));
534
+ addServerImports([{ name: "defineServerAuth", from: resolver.resolve("./runtime/config") }]);
535
+ addServerScanDir(resolver.resolve("./runtime/server/middleware"));
536
+ addServerHandler({ route: "/api/auth/**", handler: resolver.resolve("./runtime/server/api/auth/[...all]") });
537
+ }
538
+ addImportsDir(resolver.resolve("./runtime/app/composables"));
539
+ addImportsDir(resolver.resolve("./runtime/utils"));
540
+ if (!clientOnly)
541
+ addPlugin({ src: resolver.resolve("./runtime/app/plugins/session.server"), mode: "server" });
542
+ addPlugin({ src: resolver.resolve("./runtime/app/plugins/session.client"), mode: "client" });
543
+ addComponentsDir({ path: resolver.resolve("./runtime/app/components") });
544
+ nuxt.hook("app:resolve", (app) => {
545
+ app.middleware.push({ name: "auth", path: resolver.resolve("./runtime/app/middleware/auth.global"), global: true });
546
+ });
547
+ if (hasHubDb) {
548
+ await setupBetterAuthSchema(nuxt, serverConfigPath, options);
549
+ }
550
+ if (hasConvexDb) {
551
+ await setupConvexAuthSchema(nuxt, serverConfigPath);
552
+ }
553
+ const isProduction = process.env.NODE_ENV === "production" || !nuxt.options.dev;
554
+ if (!isProduction && !clientOnly) {
555
+ if (!hasNuxtModule("@nuxt/ui"))
556
+ await installModule("@nuxt/ui");
557
+ setupDevTools(nuxt);
558
+ addServerHandler({ route: "/api/_better-auth/config", method: "GET", handler: resolver.resolve("./runtime/server/api/_better-auth/config.get") });
559
+ if (hasHubDb) {
560
+ addServerHandler({ route: "/api/_better-auth/sessions", method: "GET", handler: resolver.resolve("./runtime/server/api/_better-auth/sessions.get") });
561
+ addServerHandler({ route: "/api/_better-auth/sessions", method: "DELETE", handler: resolver.resolve("./runtime/server/api/_better-auth/sessions.delete") });
562
+ addServerHandler({ route: "/api/_better-auth/users", method: "GET", handler: resolver.resolve("./runtime/server/api/_better-auth/users.get") });
563
+ addServerHandler({ route: "/api/_better-auth/accounts", method: "GET", handler: resolver.resolve("./runtime/server/api/_better-auth/accounts.get") });
564
+ }
565
+ extendPages((pages) => {
566
+ pages.push({ name: "better-auth-devtools", path: "/__better-auth-devtools", file: resolver.resolve("./runtime/app/pages/__better-auth-devtools.vue") });
567
+ });
568
+ }
569
+ nuxt.hook("pages:extend", (pages) => {
570
+ const routeRules = nuxt.options.routeRules || {};
571
+ if (!Object.keys(routeRules).length)
572
+ return;
573
+ const matcher = toRouteMatcher(createRouter({ routes: routeRules }));
574
+ const applyMetaFromRules = (page) => {
575
+ const matches = matcher.matchAll(page.path);
576
+ if (!matches.length)
577
+ return;
578
+ const matchedRules = defu({}, ...matches.reverse());
579
+ if (matchedRules.auth !== void 0) {
580
+ page.meta = page.meta || {};
581
+ page.meta.auth = matchedRules.auth;
582
+ }
583
+ page.children?.forEach((child) => applyMetaFromRules(child));
584
+ };
585
+ pages.forEach((page) => applyMetaFromRules(page));
383
586
  });
384
- addPlugin(resolver.resolve("./runtime/plugin"));
385
587
  }
386
588
  });
589
+ async function setupBetterAuthSchema(nuxt, serverConfigPath, options) {
590
+ const hub = nuxt.options.hub;
591
+ const dialect = getHubDialect(hub);
592
+ if (!dialect || !["sqlite", "postgresql", "mysql"].includes(dialect)) {
593
+ consola.warn(`Unsupported database dialect: ${dialect}`);
594
+ return;
595
+ }
596
+ const isProduction = !nuxt.options.dev;
597
+ try {
598
+ const configFile = `${serverConfigPath}.ts`;
599
+ const userConfig = await loadUserAuthConfig(configFile, isProduction);
600
+ const extendedConfig = {};
601
+ await nuxt.callHook("better-auth:config:extend", extendedConfig);
602
+ const plugins = [...userConfig.plugins || [], ...extendedConfig.plugins || []];
603
+ const authOptions = {
604
+ ...userConfig,
605
+ plugins,
606
+ secondaryStorage: options.secondaryStorage ? { get: async (_key) => null, set: async (_key, _value, _ttl) => {
607
+ }, delete: async (_key) => {
608
+ } } : void 0
609
+ };
610
+ const hubCasing = getHubCasing(hub);
611
+ const schemaOptions = { ...options.schema, useUuid: userConfig.advanced?.database?.generateId === "uuid", casing: options.schema?.casing ?? hubCasing };
612
+ const schemaCode = await generateDrizzleSchema(authOptions, dialect, schemaOptions);
613
+ const schemaDir = join(nuxt.options.buildDir, "better-auth");
614
+ const schemaPath = join(schemaDir, `schema.${dialect}.ts`);
615
+ await mkdir(schemaDir, { recursive: true });
616
+ await writeFile(schemaPath, schemaCode);
617
+ addTemplate({ filename: `better-auth/schema.${dialect}.ts`, getContents: () => schemaCode, write: true });
618
+ consola.info(`Generated ${dialect} schema`);
619
+ } catch (error) {
620
+ if (isProduction) {
621
+ throw error;
622
+ }
623
+ consola.error("Failed to generate schema:", error);
624
+ }
625
+ const nuxtWithHubHooks = nuxt;
626
+ nuxtWithHubHooks.hook("hub:db:schema:extend", ({ paths, dialect: hookDialect }) => {
627
+ const schemaPath = join(nuxt.options.buildDir, "better-auth", `schema.${hookDialect}.ts`);
628
+ if (existsSync(schemaPath)) {
629
+ paths.unshift(schemaPath);
630
+ }
631
+ });
632
+ }
633
+ async function setupConvexAuthSchema(nuxt, serverConfigPath) {
634
+ const isProduction = !nuxt.options.dev;
635
+ try {
636
+ const configFile = `${serverConfigPath}.ts`;
637
+ const userConfig = await loadUserAuthConfig(configFile, isProduction);
638
+ const authOptions = { ...userConfig };
639
+ const schemaCode = await generateConvexSchema(authOptions);
640
+ const schemaDir = join(nuxt.options.buildDir, "better-auth");
641
+ const schemaPath = join(schemaDir, "auth-tables.convex.ts");
642
+ await mkdir(schemaDir, { recursive: true });
643
+ await writeFile(schemaPath, schemaCode);
644
+ addTemplate({ filename: "better-auth/auth-tables.convex.ts", getContents: () => schemaCode, write: true });
645
+ nuxt.options.alias["#auth/convex-schema"] = schemaPath;
646
+ consola.info("Generated Convex auth schema at .nuxt/better-auth/auth-tables.convex.ts");
647
+ } catch (error) {
648
+ if (isProduction) {
649
+ throw error;
650
+ }
651
+ consola.error("Failed to generate Convex schema:", error);
652
+ }
653
+ }
387
654
 
388
- export { module as default };
655
+ export { module$1 as default };