@onmax/nuxt-better-auth 0.0.2-alpha.21 → 0.0.2-alpha.22

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/module.mjs CHANGED
@@ -1,17 +1,26 @@
1
- import { randomBytes } from 'node:crypto';
2
1
  import { existsSync, writeFileSync, readFileSync } from 'node:fs';
3
2
  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';
3
+ import { updateTemplates, addServerImportsDir, addServerImports, addServerScanDir, addServerHandler, addImportsDir, addPlugin, addComponentsDir, hasNuxtModule, installModule, extendPages, addTemplate, addTypeTemplate, defineNuxtModule, createResolver } from '@nuxt/kit';
5
4
  import { consola as consola$1 } from 'consola';
6
- import { defu } from 'defu';
7
5
  import { join, dirname } from 'pathe';
6
+ import { defu } from 'defu';
8
7
  import { toRouteMatcher, createRouter } from 'radix3';
9
- import { isCI, isTest } from 'std-env';
10
8
  import { generateDrizzleSchema as generateDrizzleSchema$1 } from '@better-auth/cli/api';
11
- import { getAuthTables } from 'better-auth/db';
9
+ import { randomBytes } from 'node:crypto';
10
+ import { isCI, isTest } from 'std-env';
12
11
  export { defineClientAuth, defineServerAuth } from '../dist/runtime/config.js';
13
12
 
14
- const version = "0.0.2-alpha.21";
13
+ const version = "0.0.2-alpha.22";
14
+
15
+ function resolveDatabaseProvider(input) {
16
+ const enabledProviders = Object.entries(input.providers).filter(([_id, provider]) => provider.isEnabled?.(input.context) ?? true);
17
+ if (!enabledProviders.length) {
18
+ throw new Error("[nuxt-better-auth] No database provider is enabled. Register one with the better-auth:database:providers hook.");
19
+ }
20
+ enabledProviders.sort((a, b) => (b[1].priority ?? 0) - (a[1].priority ?? 0));
21
+ const [id, definition] = enabledProviders[0];
22
+ return { id, definition };
23
+ }
15
24
 
16
25
  function setupDevTools(nuxt) {
17
26
  nuxt.hook("devtools:customTabs", (tabs) => {
@@ -28,6 +37,141 @@ function setupDevTools(nuxt) {
28
37
  });
29
38
  }
30
39
 
40
+ function registerTemplateHmrHook(nuxt) {
41
+ nuxt.hook("builder:watch", async (_event, relativePath) => {
42
+ if (relativePath.includes("auth.config"))
43
+ await updateTemplates({ filter: (t) => t.filename.includes("nuxt-better-auth") });
44
+ });
45
+ }
46
+ function registerServerRuntime(input) {
47
+ const { clientOnly, resolve } = input;
48
+ if (!clientOnly) {
49
+ addServerImportsDir(resolve("./runtime/server/utils"));
50
+ addServerImports([{ name: "defineServerAuth", from: resolve("./runtime/config") }]);
51
+ addServerScanDir(resolve("./runtime/server/middleware"));
52
+ addServerHandler({ route: "/api/auth/**", handler: resolve("./runtime/server/api/auth/[...all]") });
53
+ }
54
+ addImportsDir(resolve("./runtime/app/composables"));
55
+ addImportsDir(resolve("./runtime/utils"));
56
+ if (!clientOnly)
57
+ addPlugin({ src: resolve("./runtime/app/plugins/session.server"), mode: "server" });
58
+ addPlugin({ src: resolve("./runtime/app/plugins/session.client"), mode: "client" });
59
+ addComponentsDir({ path: resolve("./runtime/app/components") });
60
+ }
61
+ function registerAuthMiddlewareHook(nuxt, resolve) {
62
+ nuxt.hook("app:resolve", (app) => {
63
+ app.middleware.push({ name: "auth", path: resolve("./runtime/app/middleware/auth.global"), global: true });
64
+ });
65
+ }
66
+ async function registerDevtools(input) {
67
+ const { nuxt, clientOnly, hasHubDb, resolve } = input;
68
+ const isProduction = process.env.NODE_ENV === "production" || !nuxt.options.dev;
69
+ if (isProduction || clientOnly)
70
+ return;
71
+ if (!hasNuxtModule("@nuxt/ui"))
72
+ await installModule("@nuxt/ui");
73
+ setupDevTools(nuxt);
74
+ addServerHandler({ route: "/api/_better-auth/config", method: "get", handler: resolve("./runtime/server/api/_better-auth/config.get") });
75
+ if (hasHubDb) {
76
+ const handlers = [
77
+ { route: "/api/_better-auth/sessions", method: "get", handler: resolve("./runtime/server/api/_better-auth/sessions.get") },
78
+ { route: "/api/_better-auth/sessions", method: "delete", handler: resolve("./runtime/server/api/_better-auth/sessions.delete") },
79
+ { route: "/api/_better-auth/users", method: "get", handler: resolve("./runtime/server/api/_better-auth/users.get") },
80
+ { route: "/api/_better-auth/accounts", method: "get", handler: resolve("./runtime/server/api/_better-auth/accounts.get") }
81
+ ];
82
+ handlers.forEach((handler) => addServerHandler(handler));
83
+ }
84
+ extendPages((pages) => {
85
+ pages.push({ name: "better-auth-devtools", path: "/__better-auth-devtools", file: resolve("./runtime/app/pages/__better-auth-devtools.vue") });
86
+ });
87
+ }
88
+ function registerRouteRulesMetaHook(nuxt) {
89
+ nuxt.hook("pages:extend", (pages) => {
90
+ const routeRules = nuxt.options.routeRules || {};
91
+ if (!Object.keys(routeRules).length)
92
+ return;
93
+ const matcher = toRouteMatcher(createRouter({ routes: routeRules }));
94
+ const applyMetaFromRules = (page) => {
95
+ const matches = matcher.matchAll(page.path);
96
+ if (!matches.length)
97
+ return;
98
+ const matchedRules = defu({}, ...matches.reverse());
99
+ if (matchedRules.auth !== void 0) {
100
+ page.meta = page.meta || {};
101
+ page.meta.auth = matchedRules.auth;
102
+ }
103
+ page.children?.forEach((child) => applyMetaFromRules(child));
104
+ };
105
+ pages.forEach((page) => applyMetaFromRules(page));
106
+ });
107
+ }
108
+
109
+ function getHubDialect(hub) {
110
+ if (!hub?.db)
111
+ return void 0;
112
+ if (typeof hub.db === "string")
113
+ return hub.db;
114
+ if (typeof hub.db === "object" && hub.db !== null)
115
+ return hub.db.dialect;
116
+ return void 0;
117
+ }
118
+ function getHubCasing(hub) {
119
+ if (!hub?.db || typeof hub.db !== "object" || hub.db === null)
120
+ return void 0;
121
+ return hub.db.casing;
122
+ }
123
+
124
+ function resolveSecondaryStorageEnabled(input) {
125
+ const { options, clientOnly, hasNuxtHub, hub, consola } = input;
126
+ let secondaryStorageEnabled = options.secondaryStorage ?? false;
127
+ if (secondaryStorageEnabled && clientOnly) {
128
+ consola.warn("secondaryStorage is not available in clientOnly mode. Disabling.");
129
+ secondaryStorageEnabled = false;
130
+ } else if (secondaryStorageEnabled && (!hasNuxtHub || !hub?.kv)) {
131
+ consola.warn("secondaryStorage requires @nuxthub/core with hub.kv: true. Disabling.");
132
+ secondaryStorageEnabled = false;
133
+ }
134
+ return secondaryStorageEnabled;
135
+ }
136
+ function setupRuntimeConfig(input) {
137
+ const { nuxt, options, clientOnly, databaseProvider, consola } = input;
138
+ const secondaryStorageEnabled = resolveSecondaryStorageEnabled(input);
139
+ nuxt.options.runtimeConfig.public = nuxt.options.runtimeConfig.public || {};
140
+ const configuredSiteUrl = nuxt.options.runtimeConfig.public.siteUrl;
141
+ if (!configuredSiteUrl && process.env.NUXT_PUBLIC_SITE_URL)
142
+ nuxt.options.runtimeConfig.public.siteUrl = process.env.NUXT_PUBLIC_SITE_URL;
143
+ nuxt.options.runtimeConfig.public.auth = defu(nuxt.options.runtimeConfig.public.auth, {
144
+ redirects: { login: options.redirects?.login ?? "/login", guest: options.redirects?.guest ?? "/" },
145
+ useDatabase: databaseProvider !== "none",
146
+ databaseProvider,
147
+ databaseSource: "module",
148
+ clientOnly,
149
+ session: {
150
+ skipHydratedSsrGetSession: options.session?.skipHydratedSsrGetSession ?? false
151
+ }
152
+ });
153
+ if (clientOnly) {
154
+ const siteUrl = nuxt.options.runtimeConfig.public.siteUrl;
155
+ if (!siteUrl)
156
+ consola.warn("clientOnly mode: set runtimeConfig.public.siteUrl (or NUXT_PUBLIC_SITE_URL) to your frontend URL");
157
+ consola.info("clientOnly mode enabled - server utilities (serverAuth, getUserSession, requireUserSession) are not available");
158
+ return { secondaryStorageEnabled };
159
+ }
160
+ const currentSecret = nuxt.options.runtimeConfig.betterAuthSecret;
161
+ nuxt.options.runtimeConfig.betterAuthSecret = currentSecret || process.env.BETTER_AUTH_SECRET || "";
162
+ const betterAuthSecret = nuxt.options.runtimeConfig.betterAuthSecret;
163
+ if (!nuxt.options.dev && !nuxt.options._prepare && !betterAuthSecret) {
164
+ throw new Error("[nuxt-better-auth] BETTER_AUTH_SECRET is required in production. Set BETTER_AUTH_SECRET or NUXT_BETTER_AUTH_SECRET environment variable.");
165
+ }
166
+ if (betterAuthSecret && betterAuthSecret.length < 32) {
167
+ throw new Error("[nuxt-better-auth] BETTER_AUTH_SECRET must be at least 32 characters for security");
168
+ }
169
+ nuxt.options.runtimeConfig.auth = defu(nuxt.options.runtimeConfig.auth, {
170
+ secondaryStorage: secondaryStorageEnabled
171
+ });
172
+ return { secondaryStorageEnabled };
173
+ }
174
+
31
175
  function dialectToProvider(dialect) {
32
176
  return dialect === "postgresql" ? "pg" : dialect;
33
177
  }
@@ -57,101 +201,6 @@ async function generateDrizzleSchema(authOptions, dialect, schemaOptions) {
57
201
  }
58
202
  return result.code;
59
203
  }
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
- );
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
- */
111
-
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
204
  async function loadUserAuthConfig(configPath, throwOnError = false) {
156
205
  const { createJiti } = await import('jiti');
157
206
  const { defineServerAuth } = await import('../dist/runtime/config.js');
@@ -186,34 +235,63 @@ async function loadUserAuthConfig(configPath, throwOnError = false) {
186
235
  }
187
236
  }
188
237
 
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;
238
+ async function loadAuthOptions(context) {
239
+ const isProduction = !context.nuxt.options.dev;
240
+ const configFile = `${context.serverConfigPath}.ts`;
241
+ const userConfig = await loadUserAuthConfig(configFile, isProduction);
242
+ const extendedConfig = {};
243
+ await context.nuxt.callHook("better-auth:config:extend", extendedConfig);
244
+ const plugins = [...userConfig.plugins || [], ...extendedConfig.plugins || []];
245
+ return { userConfig, plugins };
202
246
  }
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 "";
247
+ async function setupBetterAuthSchema(nuxt, serverConfigPath, options, consola) {
248
+ const hub = nuxt.options.hub;
249
+ const dialect = getHubDialect(hub);
250
+ if (!dialect || !["sqlite", "postgresql", "mysql"].includes(dialect)) {
251
+ consola.warn(`Unsupported database dialect: ${dialect}`);
252
+ return;
253
+ }
254
+ const context = { nuxt, serverConfigPath };
255
+ try {
256
+ const { userConfig, plugins } = await loadAuthOptions(context);
257
+ const authOptions = {
258
+ ...userConfig,
259
+ plugins,
260
+ secondaryStorage: options.secondaryStorage ? { get: async (_key) => null, set: async (_key, _value, _ttl) => {
261
+ }, delete: async (_key) => {
262
+ } } : void 0
263
+ };
264
+ const hubCasing = getHubCasing(hub);
265
+ const schemaOptions = { ...options.schema, useUuid: userConfig.advanced?.database?.generateId === "uuid", casing: options.schema?.casing ?? hubCasing };
266
+ const schemaCode = await generateDrizzleSchema(authOptions, dialect, schemaOptions);
267
+ const schemaDir = join(nuxt.options.buildDir, "better-auth");
268
+ const schemaPathTs = join(schemaDir, `schema.${dialect}.ts`);
269
+ const schemaPathMjs = join(schemaDir, `schema.${dialect}.mjs`);
270
+ await mkdir(schemaDir, { recursive: true });
271
+ await writeFile(schemaPathTs, schemaCode);
272
+ await writeFile(schemaPathMjs, schemaCode);
273
+ addTemplate({ filename: `better-auth/schema.${dialect}.ts`, getContents: () => schemaCode, write: true });
274
+ addTemplate({ filename: `better-auth/schema.${dialect}.mjs`, getContents: () => schemaCode, write: true });
275
+ consola.info(`Generated ${dialect} schema (.ts + .mjs)`);
276
+ const nuxtWithHubHooks = nuxt;
277
+ nuxtWithHubHooks.hook("hub:db:schema:extend", ({ paths, dialect: hookDialect }) => {
278
+ const tsPath = join(nuxt.options.buildDir, "better-auth", `schema.${hookDialect}.ts`);
279
+ const mjsPath = join(nuxt.options.buildDir, "better-auth", `schema.${hookDialect}.mjs`);
280
+ if (existsSync(tsPath)) {
281
+ paths.unshift(tsPath);
282
+ return;
283
+ }
284
+ if (existsSync(mjsPath))
285
+ paths.unshift(mjsPath);
286
+ });
287
+ } catch (error) {
288
+ const isProduction = !nuxt.options.dev;
289
+ if (isProduction)
290
+ throw error;
291
+ consola.error("Failed to generate schema:", error);
292
+ }
216
293
  }
294
+
217
295
  const generateSecret = () => randomBytes(32).toString("hex");
218
296
  function readEnvFile(rootDir) {
219
297
  const envPath = join(rootDir, ".env");
@@ -232,7 +310,7 @@ function appendSecretToEnv(rootDir, secret) {
232
310
  `;
233
311
  writeFileSync(envPath, content, "utf-8");
234
312
  }
235
- async function promptForSecret(rootDir) {
313
+ async function promptForSecret(rootDir, consola) {
236
314
  if (process.env.BETTER_AUTH_SECRET || hasEnvSecret(rootDir))
237
315
  return void 0;
238
316
  if (isCI || isTest) {
@@ -278,161 +356,37 @@ Proceed?`, { type: "confirm", initial: true, cancel: "null" });
278
356
  consola.success("Added BETTER_AUTH_SECRET to .env");
279
357
  return secret;
280
358
  }
281
- const module$1 = defineNuxtModule({
282
- meta: { name: "@onmax/nuxt-better-auth", version, configKey: "auth", compatibility: { nuxt: ">=3.0.0" } },
283
- defaults: {
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 '@onmax/nuxt-better-auth/config'
297
359
 
298
- export default defineServerAuth({
299
- emailAndPassword: { enabled: true },
300
- })
301
- `;
302
- const clientTemplate = `import { defineClientAuth } from '@onmax/nuxt-better-auth/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}`);
316
- }
317
- },
318
- async setup(options, nuxt) {
319
- const resolver = createResolver(import.meta.url);
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`;
323
- }
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
357
- });
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");
362
- }
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.");
371
- }
372
- if (betterAuthSecret && betterAuthSecret.length < 32) {
373
- throw new Error("[nuxt-better-auth] BETTER_AUTH_SECRET must be at least 32 characters for security");
374
- }
375
- nuxt.options.runtimeConfig.auth = defu(nuxt.options.runtimeConfig.auth, {
376
- secondaryStorage: secondaryStorageEnabled
377
- });
378
- }
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.");
386
- }
387
- const secondaryStorageCode = secondaryStorageEnabled ? `import { kv } from '@nuxthub/kv'
360
+ function buildSecondaryStorageCode(enabled) {
361
+ if (!enabled)
362
+ return "export function createSecondaryStorage() { return undefined }";
363
+ return `import { kv } from '@nuxthub/kv'
388
364
  export function createSecondaryStorage() {
389
365
  return {
390
366
  get: async (key) => kv.get(\`_auth:\${key}\`),
391
367
  set: async (key, value, ttl) => kv.set(\`_auth:\${key}\`, value, { ttl }),
392
368
  delete: async (key) => kv.del(\`_auth:\${key}\`),
393
369
  }
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.");
399
- }
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'
370
+ }`;
371
+ }
372
+ function buildDatabaseCode(input) {
373
+ if (input.provider === "nuxthub") {
374
+ return `import { db, schema } from '@nuxthub/db'
406
375
  import { drizzleAdapter } from 'better-auth/adapters/drizzle'
407
- const rawDialect = '${hubDialect}'
376
+ const rawDialect = '${input.hubDialect}'
408
377
  const dialect = rawDialect === 'postgresql' ? 'pg' : rawDialect
409
- export function createDatabase() { return drizzleAdapter(db, { provider: dialect, schema, usePlural: ${usePlural}, camelCase: ${camelCase} }) }
378
+ export function createDatabase() { return drizzleAdapter(db, { provider: dialect, schema, usePlural: ${input.usePlural}, camelCase: ${input.camelCase} }) }
410
379
  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 '@onmax/nuxt-better-auth/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 }
380
+ }
381
+ return `export function createDatabase() { return undefined }
429
382
  export const db = undefined`;
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: () => `
383
+ }
384
+
385
+ function registerServerTypeTemplates(input) {
386
+ const { serverConfigPath, hasHubDb, runtimeTypesPath } = input;
387
+ addTypeTemplate({
388
+ filename: "types/auth-secondary-storage.d.ts",
389
+ getContents: () => `
436
390
  declare module '#auth/secondary-storage' {
437
391
  interface SecondaryStorage {
438
392
  get: (key: string) => Promise<string | null>
@@ -442,214 +396,281 @@ declare module '#auth/secondary-storage' {
442
396
  export function createSecondaryStorage(): SecondaryStorage | undefined
443
397
  }
444
398
  `
445
- }, { nitro: true, node: true });
446
- addTypeTemplate({
447
- filename: "types/auth-database.d.ts",
448
- getContents: () => `
399
+ }, { nitro: true, node: true });
400
+ addTypeTemplate({
401
+ filename: "types/auth-database.d.ts",
402
+ getContents: () => `
449
403
  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
404
+ import type { BetterAuthOptions } from 'better-auth'
405
+ export function createDatabase(): BetterAuthOptions['database']
406
+ export const db: ${hasHubDb ? `typeof import('@nuxthub/db')['db']` : "undefined"}
453
407
  }
454
408
  `
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'
409
+ }, { nitro: true, node: true });
410
+ addTypeTemplate({
411
+ filename: "types/nuxt-better-auth-infer.d.ts",
412
+ getContents: () => `
413
+ import type { BetterAuthOptions, BetterAuthPlugin, InferPluginTypes, UnionToIntersection } from 'better-auth'
414
+ import type { InferFieldsOutput } from 'better-auth/db'
460
415
  import type { RuntimeConfig } from 'nuxt/schema'
461
416
  import type createServerAuth from '${serverConfigPath}'
462
417
 
463
- type _Config = ReturnType<typeof createServerAuth>
418
+ type _RawConfig = ReturnType<typeof createServerAuth>
419
+ type _RawPlugins = _RawConfig extends { plugins: infer P } ? P : _RawConfig extends { plugins?: infer P } ? P : []
420
+ type _NormalizedPlugins = _RawPlugins extends readonly (infer T)[]
421
+ ? Array<T & BetterAuthPlugin>
422
+ : _RawPlugins extends (infer T)[]
423
+ ? Array<T & BetterAuthPlugin>
424
+ : BetterAuthPlugin[]
425
+ type _Config = Omit<BetterAuthOptions, 'plugins'> & Omit<_RawConfig, 'plugins'> & {
426
+ plugins?: _NormalizedPlugins
427
+ }
428
+
429
+ type _InferModelFieldsFromPlugins<P, M extends string> = P extends readonly (infer Plugin)[]
430
+ ? UnionToIntersection<Plugin extends { schema: { [K in M]: { fields: infer F } } } ? InferFieldsOutput<F> : {}>
431
+ : P extends (infer Plugin)[]
432
+ ? UnionToIntersection<Plugin extends { schema: { [K in M]: { fields: infer F } } } ? InferFieldsOutput<F> : {}>
433
+ : {}
434
+
435
+ type _InferModelFieldsFromOptions<C, M extends 'user' | 'session'> = C extends { [K in M]: { additionalFields: infer F } }
436
+ ? InferFieldsOutput<F>
437
+ : {}
438
+
439
+ type _UserFallback = _InferModelFieldsFromPlugins<_RawPlugins, 'user'> & _InferModelFieldsFromOptions<_RawConfig, 'user'>
440
+ type _SessionFallback = _InferModelFieldsFromPlugins<_RawPlugins, 'session'> & _InferModelFieldsFromOptions<_RawConfig, 'session'>
464
441
 
465
442
  declare module '#nuxt-better-auth' {
466
- interface AuthUser extends InferUser<_Config> {}
467
- interface AuthSession extends InferSession<_Config> {}
443
+ interface AuthUser extends _UserFallback {}
444
+ interface AuthSession extends _SessionFallback {}
468
445
  interface ServerAuthContext {
469
446
  runtimeConfig: RuntimeConfig
470
- ${hasHubDb ? `db: typeof import('@nuxthub/db')['db']` : ""}
447
+ db: ${hasHubDb ? `typeof import('@nuxthub/db')['db']` : "undefined"}
471
448
  }
472
449
  type PluginTypes = InferPluginTypes<_Config>
473
450
  }
474
451
 
475
- // Augment the config module to use the extended ServerAuthContext
476
452
  interface _AugmentedServerAuthContext {
477
453
  runtimeConfig: RuntimeConfig
478
- ${hasHubDb ? `db: typeof import('@nuxthub/db')['db']` : "db: unknown"}
454
+ db: ${hasHubDb ? `typeof import('@nuxthub/db')['db']` : "undefined"}
479
455
  }
480
456
 
481
457
  declare module '@onmax/nuxt-better-auth/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
458
+ import type { BetterAuthOptions, BetterAuthPlugin } from 'better-auth'
459
+ type ServerAuthConfig = Omit<BetterAuthOptions, 'secret' | 'baseURL'> & {
460
+ plugins?: readonly BetterAuthPlugin[]
461
+ }
462
+ export function defineServerAuth<const R extends ServerAuthConfig>(config: R): (ctx: _AugmentedServerAuthContext) => R
463
+ export function defineServerAuth<const R extends ServerAuthConfig>(config: (ctx: _AugmentedServerAuthContext) => R): (ctx: _AugmentedServerAuthContext) => R
485
464
  }
486
465
  `
487
- }, { nuxt: true, nitro: true, node: true });
488
- addTypeTemplate({
489
- filename: "types/nuxt-better-auth-nitro.d.ts",
490
- getContents: () => `
466
+ }, { nuxt: true, nitro: true, node: true });
467
+ addTypeTemplate({
468
+ filename: "types/nuxt-better-auth-nitro.d.ts",
469
+ getContents: () => `
491
470
  declare module 'nitropack' {
492
471
  interface NitroRouteRules {
493
- auth?: import('${resolver.resolve("./runtime/types")}').AuthMeta
472
+ auth?: import('${runtimeTypesPath}').AuthMeta
494
473
  }
495
474
  interface NitroRouteConfig {
496
- auth?: import('${resolver.resolve("./runtime/types")}').AuthMeta
475
+ auth?: import('${runtimeTypesPath}').AuthMeta
497
476
  }
498
477
  }
499
478
  declare module 'nitropack/types' {
500
479
  interface NitroRouteRules {
501
- auth?: import('${resolver.resolve("./runtime/types")}').AuthMeta
480
+ auth?: import('${runtimeTypesPath}').AuthMeta
502
481
  }
503
482
  interface NitroRouteConfig {
504
- auth?: import('${resolver.resolve("./runtime/types")}').AuthMeta
483
+ auth?: import('${runtimeTypesPath}').AuthMeta
505
484
  }
506
485
  }
507
486
  export {}
508
487
  `
509
- }, { nuxt: true, nitro: true, node: true });
510
- }
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")}'
488
+ }, { nuxt: true, nitro: true, node: true });
489
+ }
490
+ function registerSharedTypeTemplates(input) {
491
+ addTypeTemplate({
492
+ filename: "types/nuxt-better-auth.d.ts",
493
+ getContents: () => `
494
+ export * from '${input.runtimeTypesAugmentPath}'
495
+ export type { AuthMeta, AuthMode, AuthRouteRules, UserMatch, RequireSessionOptions, Auth, InferUser, InferSession } from '${input.runtimeTypesPath}'
516
496
  `
517
- });
518
- addTypeTemplate({
519
- filename: "types/nuxt-better-auth-client.d.ts",
520
- getContents: () => `
521
- import type createAppAuthClient from '${clientConfigPath}'
497
+ });
498
+ addTypeTemplate({
499
+ filename: "types/nuxt-better-auth-client.d.ts",
500
+ getContents: () => `
501
+ import type createAppAuthClient from '${input.clientConfigPath}'
522
502
  declare module '#nuxt-better-auth' {
523
503
  export type AppAuthClient = ReturnType<typeof createAppAuthClient>
524
504
  }
525
505
  `
526
- });
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") });
530
- }
531
- });
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]") });
506
+ });
507
+ }
508
+
509
+ const consola = consola$1.withTag("nuxt-better-auth");
510
+ function resolveDefaultClientConfig(options, rootDir, srcDir) {
511
+ if (options.clientConfig !== "app/auth.config")
512
+ return;
513
+ const srcDirRelative = srcDir.replace(`${rootDir}/`, "");
514
+ options.clientConfig = srcDirRelative === srcDir ? "auth.config" : `${srcDirRelative}/auth.config`;
515
+ }
516
+ async function createDefaultAuthConfigFiles(rootDir, srcDir) {
517
+ const serverPath = join(rootDir, "server/auth.config.ts");
518
+ const clientPath = join(srcDir, "auth.config.ts");
519
+ const serverTemplate = `import { defineServerAuth } from '@onmax/nuxt-better-auth/config'
520
+
521
+ export default defineServerAuth({
522
+ emailAndPassword: { enabled: true },
523
+ })
524
+ `;
525
+ const clientTemplate = `import { defineClientAuth } from '@onmax/nuxt-better-auth/config'
526
+
527
+ export default defineClientAuth({})
528
+ `;
529
+ if (!existsSync(serverPath)) {
530
+ await mkdir(dirname(serverPath), { recursive: true });
531
+ await writeFile(serverPath, serverTemplate);
532
+ consola.success("Created server/auth.config.ts");
533
+ }
534
+ if (!existsSync(clientPath)) {
535
+ await mkdir(dirname(clientPath), { recursive: true });
536
+ await writeFile(clientPath, clientTemplate);
537
+ const relativePath = clientPath.replace(`${rootDir}/`, "");
538
+ consola.success(`Created ${relativePath}`);
539
+ }
540
+ }
541
+ const module$1 = defineNuxtModule({
542
+ meta: { name: "@onmax/nuxt-better-auth", version, configKey: "auth", compatibility: { nuxt: ">=4.0.0" } },
543
+ defaults: {
544
+ clientOnly: false,
545
+ serverConfig: "server/auth.config",
546
+ clientConfig: "app/auth.config",
547
+ redirects: { login: "/login", guest: "/" },
548
+ secondaryStorage: false
549
+ },
550
+ async onInstall(nuxt) {
551
+ const generatedSecret = await promptForSecret(nuxt.options.rootDir, consola);
552
+ if (generatedSecret)
553
+ process.env.BETTER_AUTH_SECRET = generatedSecret;
554
+ await createDefaultAuthConfigFiles(nuxt.options.rootDir, nuxt.options.srcDir);
555
+ },
556
+ async setup(options, nuxt) {
557
+ const resolver = createResolver(import.meta.url);
558
+ resolveDefaultClientConfig(options, nuxt.options.rootDir, nuxt.options.srcDir);
559
+ const clientOnly = options.clientOnly;
560
+ const serverConfigFile = options.serverConfig;
561
+ const clientConfigFile = options.clientConfig;
562
+ const serverConfigPath = resolver.resolve(nuxt.options.rootDir, serverConfigFile);
563
+ const clientConfigPath = resolver.resolve(nuxt.options.rootDir, clientConfigFile);
564
+ const serverConfigExists = existsSync(`${serverConfigPath}.ts`) || existsSync(`${serverConfigPath}.js`);
565
+ const clientConfigExists = existsSync(`${clientConfigPath}.ts`) || existsSync(`${clientConfigPath}.js`);
566
+ if (!clientOnly && !serverConfigExists)
567
+ throw new Error(`[nuxt-better-auth] Missing ${serverConfigFile}.ts - export default defineServerAuth(...)`);
568
+ if (!clientConfigExists)
569
+ throw new Error(`[nuxt-better-auth] Missing ${clientConfigFile}.ts - export default defineClientAuth(...)`);
570
+ const hasNuxtHub = hasNuxtModule("@nuxthub/core", nuxt);
571
+ const hub = hasNuxtHub ? nuxt.options.hub : void 0;
572
+ const hasHubDbAvailable = !clientOnly && hasNuxtHub && !!hub?.db;
573
+ const deprecatedProvider = options.database?.provider;
574
+ if (deprecatedProvider) {
575
+ throw new Error(
576
+ `[nuxt-better-auth] auth.database.provider has been removed. Remove auth.database.provider="${deprecatedProvider}". To configure a database, either set "database" directly in server/auth.config.ts (defineServerAuth) or install a provider module that registers better-auth:database:providers.`
577
+ );
537
578
  }
538
- addImportsDir(resolver.resolve("./runtime/app/composables"));
539
- addImportsDir(resolver.resolve("./runtime/utils"));
579
+ let databaseProvider = "none";
580
+ let hasHubDb = false;
581
+ nuxt.options.alias["#nuxt-better-auth"] = resolver.resolve("./runtime/types/augment");
540
582
  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") });
583
+ nuxt.options.alias["#auth/server"] = serverConfigPath;
584
+ nuxt.options.alias["#auth/client"] = clientConfigPath;
585
+ if (clientOnly) {
586
+ setupRuntimeConfig({
587
+ nuxt,
588
+ options,
589
+ clientOnly,
590
+ databaseProvider,
591
+ hasNuxtHub,
592
+ hub,
593
+ consola
567
594
  });
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;
595
+ } else {
596
+ const hubDialect = getHubDialect(hub) ?? "sqlite";
597
+ const usePlural = options.schema?.usePlural ?? false;
598
+ const camelCase = (options.schema?.casing ?? getHubCasing(hub)) !== "snake_case";
599
+ const providers = {
600
+ nuxthub: {
601
+ priority: 100,
602
+ isEnabled: ({ hasHubDbAvailable: hasHubDbAvailable2 }) => hasHubDbAvailable2,
603
+ buildDatabaseCode: () => buildDatabaseCode({
604
+ provider: "nuxthub",
605
+ hubDialect,
606
+ usePlural,
607
+ camelCase
608
+ })
609
+ },
610
+ none: {
611
+ priority: 0,
612
+ buildDatabaseCode: () => buildDatabaseCode({
613
+ provider: "none",
614
+ hubDialect,
615
+ usePlural,
616
+ camelCase
617
+ })
582
618
  }
583
- page.children?.forEach((child) => applyMetaFromRules(child));
584
619
  };
585
- pages.forEach((page) => applyMetaFromRules(page));
620
+ const enabledCtx = { nuxt, options, clientOnly, hasHubDbAvailable };
621
+ await nuxt.callHook("better-auth:database:providers", providers);
622
+ const resolvedProvider = resolveDatabaseProvider({ providers, context: enabledCtx });
623
+ databaseProvider = resolvedProvider.id;
624
+ hasHubDb = databaseProvider === "nuxthub";
625
+ const { secondaryStorageEnabled } = setupRuntimeConfig({
626
+ nuxt,
627
+ options,
628
+ clientOnly,
629
+ databaseProvider,
630
+ hasNuxtHub,
631
+ hub,
632
+ consola
633
+ });
634
+ if (secondaryStorageEnabled && !nuxt.options.alias["hub:kv"]) {
635
+ throw new Error("[nuxt-better-auth] hub:kv not found. Ensure @nuxthub/core is loaded before this module and hub.kv is enabled.");
636
+ }
637
+ const secondaryStorageTemplate = addTemplate({
638
+ filename: "better-auth/secondary-storage.mjs",
639
+ getContents: () => buildSecondaryStorageCode(secondaryStorageEnabled),
640
+ write: true
641
+ });
642
+ nuxt.options.alias["#auth/secondary-storage"] = secondaryStorageTemplate.dst;
643
+ if (hasHubDb && !nuxt.options.alias["hub:db"]) {
644
+ throw new Error("[nuxt-better-auth] hub:db not found. Ensure @nuxthub/core is loaded before this module and hub.db is configured.");
645
+ }
646
+ const setupCtx = { nuxt, options, clientOnly };
647
+ await resolvedProvider.definition.setup?.(setupCtx);
648
+ const buildCtx = { hubDialect, usePlural, camelCase };
649
+ const databaseTemplate = addTemplate({
650
+ filename: "better-auth/database.mjs",
651
+ getContents: () => resolvedProvider.definition.buildDatabaseCode(buildCtx),
652
+ write: true
653
+ });
654
+ nuxt.options.alias["#auth/database"] = databaseTemplate.dst;
655
+ registerServerTypeTemplates({
656
+ serverConfigPath,
657
+ hasHubDb,
658
+ runtimeTypesPath: resolver.resolve("./runtime/types")
659
+ });
660
+ if (hasHubDb)
661
+ await setupBetterAuthSchema(nuxt, serverConfigPath, options, consola);
662
+ }
663
+ registerSharedTypeTemplates({
664
+ runtimeTypesAugmentPath: resolver.resolve("./runtime/types/augment"),
665
+ runtimeTypesPath: resolver.resolve("./runtime/types"),
666
+ clientConfigPath
586
667
  });
668
+ registerTemplateHmrHook(nuxt);
669
+ registerServerRuntime({ clientOnly, resolve: resolver.resolve });
670
+ registerAuthMiddlewareHook(nuxt, resolver.resolve);
671
+ await registerDevtools({ nuxt, clientOnly, hasHubDb, resolve: resolver.resolve });
672
+ registerRouteRulesMetaHook(nuxt);
587
673
  }
588
674
  });
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
- }
654
675
 
655
676
  export { module$1 as default };