@onmax/nuxt-better-auth 0.0.2-alpha.30 → 0.0.2-alpha.32

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 (51) hide show
  1. package/README.md +60 -17
  2. package/dist/module.d.mts +0 -9
  3. package/dist/module.json +2 -2
  4. package/dist/module.mjs +668 -267
  5. package/dist/runtime/app/components/BetterAuthState.d.vue.ts +4 -4
  6. package/dist/runtime/app/components/BetterAuthState.vue +1 -1
  7. package/dist/runtime/app/components/BetterAuthState.vue.d.ts +4 -4
  8. package/dist/runtime/app/composables/useAuthClient.d.ts +9 -0
  9. package/dist/runtime/app/composables/useAuthClient.js +34 -0
  10. package/dist/runtime/app/composables/useAuthClientAction.d.ts +1 -3
  11. package/dist/runtime/app/composables/useAuthClientAction.js +2 -2
  12. package/dist/runtime/app/composables/useAuthRequestFetch.d.ts +1 -1
  13. package/dist/runtime/app/composables/useSignIn.js +2 -2
  14. package/dist/runtime/app/composables/useSignUp.js +2 -2
  15. package/dist/runtime/app/composables/useUserSession.d.ts +5 -4
  16. package/dist/runtime/app/composables/useUserSession.js +91 -218
  17. package/dist/runtime/app/composables/useUserSessionState.d.ts +3 -0
  18. package/dist/runtime/app/composables/useUserSessionState.js +4 -0
  19. package/dist/runtime/app/internal/auth-action-error.js +1 -3
  20. package/dist/runtime/app/internal/auth-action-handles.js +1 -3
  21. package/dist/runtime/app/internal/redirect-helpers.d.ts +4 -0
  22. package/dist/runtime/app/internal/redirect-helpers.js +37 -0
  23. package/dist/runtime/app/internal/session-fetch.d.ts +12 -0
  24. package/dist/runtime/app/internal/session-fetch.js +56 -0
  25. package/dist/runtime/app/internal/utils.d.ts +1 -0
  26. package/dist/runtime/app/internal/utils.js +3 -0
  27. package/dist/runtime/app/internal/vue-safe-auth-proxy.d.ts +3 -0
  28. package/dist/runtime/app/internal/vue-safe-auth-proxy.js +68 -0
  29. package/dist/runtime/app/internal/wrap-auth-method.d.ts +15 -0
  30. package/dist/runtime/app/internal/wrap-auth-method.js +66 -0
  31. package/dist/runtime/app/middleware/auth.global.js +5 -4
  32. package/dist/runtime/app/plugins/session.client.js +2 -1
  33. package/dist/runtime/composables.d.ts +11 -0
  34. package/dist/runtime/composables.js +9 -0
  35. package/dist/runtime/config.d.ts +8 -6
  36. package/dist/runtime/config.js +3 -1
  37. package/dist/runtime/server/api/_better-auth/_schema.d.ts +1 -5
  38. package/dist/runtime/server/api/_better-auth/_schema.js +2 -1
  39. package/dist/runtime/server/api/_better-auth/accounts.get.d.ts +2 -2
  40. package/dist/runtime/server/api/_better-auth/config.get.js +1 -1
  41. package/dist/runtime/server/api/_better-auth/sessions.get.d.ts +2 -2
  42. package/dist/runtime/server/api/_better-auth/users.get.d.ts +2 -2
  43. package/dist/runtime/server/middleware/route-access.js +1 -1
  44. package/dist/runtime/server/utils/auth.d.ts +13 -2
  45. package/dist/runtime/server/utils/auth.js +42 -16
  46. package/dist/runtime/server/utils/session.d.ts +3 -1
  47. package/dist/runtime/server/utils/session.js +175 -1
  48. package/dist/runtime/server/virtual-modules.d.ts +5 -0
  49. package/dist/runtime/types/augment.d.ts +1 -3
  50. package/dist/runtime/types.d.ts +1 -1
  51. package/package.json +33 -26
package/dist/module.mjs CHANGED
@@ -1,8 +1,8 @@
1
- import { existsSync, writeFileSync, readFileSync } from 'node:fs';
1
+ import { existsSync, statSync, writeFileSync, readFileSync } from 'node:fs';
2
2
  import { mkdir, writeFile } from 'node:fs/promises';
3
- import { updateTemplates, addServerImportsDir, addServerImports, addServerScanDir, addServerHandler, addImportsDir, addPlugin, addComponentsDir, hasNuxtModule, installModule, extendPages, addTemplate, addTypeTemplate, defineNuxtModule, createResolver } from '@nuxt/kit';
3
+ import { getLayerDirectories, updateTemplates, addServerImportsDir, addServerImports, addServerScanDir, addServerHandler, addImportsDir, addPlugin, addComponentsDir, hasNuxtModule, installModule, extendPages, addTemplate, addTypeTemplate, defineNuxtModule, createResolver } from '@nuxt/kit';
4
4
  import { consola as consola$1 } from 'consola';
5
- import { join, dirname } from 'pathe';
5
+ import { isAbsolute, join, relative, dirname } from 'pathe';
6
6
  import { defu } from 'defu';
7
7
  import { toRouteMatcher, createRouter } from 'radix3';
8
8
  import { generateDrizzleSchema as generateDrizzleSchema$1 } from '@better-auth/cli/api';
@@ -10,16 +10,120 @@ import { randomBytes } from 'node:crypto';
10
10
  import { isCI, isTest } from 'std-env';
11
11
  export { defineClientAuth, defineServerAuth } from '../dist/runtime/config.js';
12
12
 
13
- const version = "0.0.2-alpha.30";
13
+ const version = "0.0.2-alpha.32";
14
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.");
15
+ const CONFIG_EXTENSIONS = [".ts", ".js"];
16
+ const CONFIG_EXTENSION_RE = /\.(?:ts|js)$/;
17
+ const DEFAULT_CONFIG_FILES = {
18
+ server: "server/auth.config",
19
+ client: "app/auth.config"
20
+ };
21
+ const OPTION_KEY_BY_KIND = {
22
+ server: "serverConfig",
23
+ client: "clientConfig"
24
+ };
25
+ function stripConfigExtension(path) {
26
+ return path.replace(CONFIG_EXTENSION_RE, "");
27
+ }
28
+ function defaultConfigExists(path) {
29
+ return CONFIG_EXTENSIONS.some((ext) => existsSync(`${path}${ext}`));
30
+ }
31
+ function getLayerDirectoriesWithConfigs(nuxt) {
32
+ const directories = getLayerDirectories(nuxt);
33
+ const layers = nuxt.options._layers;
34
+ return directories.map((directory, index) => ({ directory, layer: layers[index] }));
35
+ }
36
+ function getProjectDirectory(nuxt) {
37
+ return getLayerDirectories(nuxt)[0];
38
+ }
39
+ function getDefaultConfigPath(nuxt, kind) {
40
+ const project = getProjectDirectory(nuxt);
41
+ return kind === "server" ? join(project.server, "auth.config") : join(project.app, "auth.config");
42
+ }
43
+ function getLayerDefaultConfigPath(nuxt, kind, configExists) {
44
+ for (const { directory } of getLayerDirectoriesWithConfigs(nuxt)) {
45
+ const candidate = kind === "server" ? join(directory.server, "auth.config") : join(directory.app, "auth.config");
46
+ if (configExists(candidate)) {
47
+ return {
48
+ path: candidate,
49
+ declaringLayerRoot: directory.root
50
+ };
51
+ }
19
52
  }
20
- enabledProviders.sort((a, b) => (b[1].priority ?? 0) - (a[1].priority ?? 0));
21
- const [id, definition] = enabledProviders[0];
22
- return { id, definition };
53
+ }
54
+ function resolveDeclaringLayerRoot(nuxt, kind, file) {
55
+ const optionKey = OPTION_KEY_BY_KIND[kind];
56
+ for (const { directory, layer } of getLayerDirectoriesWithConfigs(nuxt)) {
57
+ const declared = layer?.config?.auth?.[optionKey];
58
+ if (typeof declared === "string" && stripConfigExtension(declared) === file)
59
+ return directory.root;
60
+ }
61
+ return getProjectDirectory(nuxt).root;
62
+ }
63
+ function getRelativeConfigFile(nuxt, path) {
64
+ return relative(getProjectDirectory(nuxt).root, path);
65
+ }
66
+ function getEffectiveModuleConfigFile(nuxt, kind) {
67
+ const optionKey = OPTION_KEY_BY_KIND[kind];
68
+ const authOptions = nuxt.options.auth;
69
+ return authOptions?.[optionKey] ?? DEFAULT_CONFIG_FILES[kind];
70
+ }
71
+ function resolveAuthConfigDescriptor(nuxt, kind, file = getEffectiveModuleConfigFile(nuxt, kind), dependencies = {}) {
72
+ const configExists = dependencies.configExists ?? defaultConfigExists;
73
+ const configuredFile = stripConfigExtension(file);
74
+ if (isAbsolute(configuredFile)) {
75
+ const declaringLayerRoot2 = resolveDeclaringLayerRoot(nuxt, kind, configuredFile);
76
+ const exists2 = configExists(configuredFile);
77
+ return {
78
+ kind,
79
+ configuredFile,
80
+ file: configuredFile,
81
+ path: configuredFile,
82
+ declaringLayerRoot: declaringLayerRoot2,
83
+ isDefault: false,
84
+ isExplicit: true,
85
+ exists: exists2,
86
+ shouldCreateDefaultFile: false
87
+ };
88
+ }
89
+ if (configuredFile === DEFAULT_CONFIG_FILES[kind]) {
90
+ const project = getProjectDirectory(nuxt);
91
+ const discovered = getLayerDefaultConfigPath(nuxt, kind, configExists);
92
+ const path2 = discovered?.path ?? getDefaultConfigPath(nuxt, kind);
93
+ const declaringLayerRoot2 = discovered?.declaringLayerRoot ?? project.root;
94
+ const exists2 = configExists(path2);
95
+ return {
96
+ kind,
97
+ configuredFile,
98
+ file: getRelativeConfigFile(nuxt, path2),
99
+ path: path2,
100
+ declaringLayerRoot: declaringLayerRoot2,
101
+ isDefault: true,
102
+ isExplicit: false,
103
+ exists: exists2,
104
+ shouldCreateDefaultFile: !exists2
105
+ };
106
+ }
107
+ const declaringLayerRoot = resolveDeclaringLayerRoot(nuxt, kind, configuredFile);
108
+ const path = join(declaringLayerRoot, configuredFile);
109
+ const exists = configExists(path);
110
+ return {
111
+ kind,
112
+ configuredFile,
113
+ file: getRelativeConfigFile(nuxt, path),
114
+ path,
115
+ declaringLayerRoot,
116
+ isDefault: false,
117
+ isExplicit: true,
118
+ exists,
119
+ shouldCreateDefaultFile: false
120
+ };
121
+ }
122
+ function resolveAuthConfigDescriptors(nuxt, files = {}, dependencies = {}) {
123
+ return {
124
+ server: resolveAuthConfigDescriptor(nuxt, "server", files.server, dependencies),
125
+ client: resolveAuthConfigDescriptor(nuxt, "client", files.client, dependencies)
126
+ };
23
127
  }
24
128
 
25
129
  function setupDevTools(nuxt) {
@@ -64,6 +168,57 @@ function registerAuthMiddlewareHook(nuxt, resolve) {
64
168
  app.middleware.push({ name: "auth", path: resolve("./runtime/app/middleware/auth.global"), global: true });
65
169
  });
66
170
  }
171
+ function registerPrepareTypesHook(input) {
172
+ const { nuxt, serverDir, hasHubDb } = input;
173
+ nuxt.hook("prepare:types", ({ nodeTsConfig, nodeReferences, sharedReferences }) => {
174
+ nodeTsConfig.compilerOptions ||= {};
175
+ nodeTsConfig.compilerOptions.paths ||= {};
176
+ const projectReferenceTypePaths = [
177
+ join(nuxt.options.buildDir, "types/nitro-imports.d.ts"),
178
+ join(nuxt.options.buildDir, "types/auth-database.d.ts"),
179
+ join(nuxt.options.buildDir, "types/auth-schema.d.ts"),
180
+ join(nuxt.options.buildDir, "types/auth-secondary-storage.d.ts")
181
+ ];
182
+ if (hasHubDb)
183
+ projectReferenceTypePaths.push(join(nuxt.options.buildDir, "hub/db.d.ts"));
184
+ const exactNodeAliases = {
185
+ "#server": serverDir,
186
+ "#auth/server": nuxt.options.alias["#auth/server"],
187
+ "#auth/client": nuxt.options.alias["#auth/client"],
188
+ "#auth/database": nuxt.options.alias["#auth/database"],
189
+ "#auth/schema": nuxt.options.alias["#auth/schema"],
190
+ "#auth/secondary-storage": nuxt.options.alias["#auth/secondary-storage"],
191
+ "#auth/route-rules": nuxt.options.alias["#auth/route-rules"],
192
+ "#nuxt-better-auth": nuxt.options.alias["#nuxt-better-auth"]
193
+ };
194
+ for (const [key, value] of Object.entries(exactNodeAliases)) {
195
+ if (typeof value === "string")
196
+ nodeTsConfig.compilerOptions.paths[key] = [value];
197
+ }
198
+ for (const [key, value] of Object.entries(nuxt.options.alias)) {
199
+ if (typeof value !== "string" || !isAbsolute(value))
200
+ continue;
201
+ nodeTsConfig.compilerOptions.paths[key] ||= [value];
202
+ if (!key.includes("*") && existsSync(value) && statSync(value).isDirectory())
203
+ nodeTsConfig.compilerOptions.paths[`${key}/*`] ||= [join(value, "*")];
204
+ }
205
+ nodeTsConfig.compilerOptions.paths["#server/*"] = [join(serverDir, "*")];
206
+ for (const path of projectReferenceTypePaths) {
207
+ if (!nodeReferences.some((reference) => "path" in reference && reference.path === path))
208
+ nodeReferences.push({ path });
209
+ if (!sharedReferences.some((reference) => "path" in reference && reference.path === path))
210
+ sharedReferences.push({ path });
211
+ }
212
+ });
213
+ }
214
+ function registerNuxtHubDatabaseExternalHook(nuxt) {
215
+ nuxt.hook("nitro:config", (nitroConfig) => {
216
+ nitroConfig.externals ||= {};
217
+ nitroConfig.externals.external ||= [];
218
+ if (!nitroConfig.externals.external.includes("@nuxthub/db"))
219
+ nitroConfig.externals.external.push("@nuxthub/db");
220
+ });
221
+ }
67
222
  async function registerDevtools(input) {
68
223
  const { nuxt, clientOnly, hasHubDb, resolve } = input;
69
224
  const isProduction = process.env.NODE_ENV === "production" || !nuxt.options.dev;
@@ -108,79 +263,6 @@ function registerRouteRulesMetaHook(nuxt) {
108
263
  });
109
264
  }
110
265
 
111
- function getHubDialect(hub) {
112
- if (!hub?.db)
113
- return void 0;
114
- if (typeof hub.db === "string")
115
- return hub.db;
116
- if (typeof hub.db === "object" && hub.db !== null)
117
- return hub.db.dialect;
118
- return void 0;
119
- }
120
- function getHubCasing(hub) {
121
- if (!hub?.db || typeof hub.db !== "object" || hub.db === null)
122
- return void 0;
123
- return hub.db.casing;
124
- }
125
-
126
- function resolveSecondaryStorage(input) {
127
- const { options, clientOnly, hasNuxtHub, hub } = input;
128
- const opt = options.hubSecondaryStorage ?? false;
129
- const useHubKV = opt === true;
130
- const secondaryStorageEnabled = opt === true || opt === "custom";
131
- if (secondaryStorageEnabled && clientOnly) {
132
- throw new Error("[nuxt-better-auth] hubSecondaryStorage is not available in clientOnly mode. Either disable clientOnly or remove auth.hubSecondaryStorage.");
133
- }
134
- if (useHubKV && (!hasNuxtHub || !hub?.kv)) {
135
- throw new Error("[nuxt-better-auth] hubSecondaryStorage: true requires @nuxthub/core with hub.kv: true. Either add hub.kv: true to your nuxt.config or remove auth.hubSecondaryStorage.");
136
- }
137
- return { useHubKV, secondaryStorageEnabled };
138
- }
139
- function setupRuntimeConfig(input) {
140
- const { nuxt, options, clientOnly, databaseProvider, consola } = input;
141
- const { useHubKV, secondaryStorageEnabled } = resolveSecondaryStorage(input);
142
- nuxt.options.runtimeConfig.public = nuxt.options.runtimeConfig.public || {};
143
- const configuredSiteUrl = nuxt.options.runtimeConfig.public.siteUrl;
144
- if (!configuredSiteUrl && process.env.NUXT_PUBLIC_SITE_URL)
145
- nuxt.options.runtimeConfig.public.siteUrl = process.env.NUXT_PUBLIC_SITE_URL;
146
- nuxt.options.runtimeConfig.public.auth = defu(nuxt.options.runtimeConfig.public.auth, {
147
- redirects: {
148
- login: options.redirects?.login ?? "/login",
149
- guest: options.redirects?.guest ?? "/",
150
- authenticated: options.redirects?.authenticated,
151
- logout: options.redirects?.logout
152
- },
153
- preserveRedirect: options.preserveRedirect ?? true,
154
- redirectQueryKey: options.redirectQueryKey ?? "redirect",
155
- useDatabase: databaseProvider !== "none",
156
- databaseProvider,
157
- clientOnly,
158
- session: {
159
- skipHydratedSsrGetSession: options.session?.skipHydratedSsrGetSession ?? false
160
- }
161
- });
162
- if (clientOnly) {
163
- const siteUrl = nuxt.options.runtimeConfig.public.siteUrl;
164
- if (!siteUrl)
165
- consola.warn("clientOnly mode: set runtimeConfig.public.siteUrl (or NUXT_PUBLIC_SITE_URL) to your frontend URL");
166
- consola.info("clientOnly mode enabled - server utilities (serverAuth, getRequestSession, getUserSession, requireUserSession) are not available");
167
- return { useHubKV, secondaryStorageEnabled };
168
- }
169
- const currentSecret = nuxt.options.runtimeConfig.betterAuthSecret;
170
- nuxt.options.runtimeConfig.betterAuthSecret = currentSecret || process.env.BETTER_AUTH_SECRET || "";
171
- const betterAuthSecret = nuxt.options.runtimeConfig.betterAuthSecret;
172
- if (!nuxt.options.dev && !nuxt.options._prepare && !betterAuthSecret) {
173
- throw new Error("[nuxt-better-auth] BETTER_AUTH_SECRET is required in production. Set BETTER_AUTH_SECRET or NUXT_BETTER_AUTH_SECRET environment variable.");
174
- }
175
- if (betterAuthSecret && betterAuthSecret.length < 32) {
176
- throw new Error("[nuxt-better-auth] BETTER_AUTH_SECRET must be at least 32 characters for security");
177
- }
178
- nuxt.options.runtimeConfig.auth = defu(nuxt.options.runtimeConfig.auth, {
179
- hubSecondaryStorage: options.hubSecondaryStorage ?? false
180
- });
181
- return { useHubKV, secondaryStorageEnabled };
182
- }
183
-
184
266
  function dialectToProvider(dialect) {
185
267
  return dialect === "postgresql" ? "pg" : dialect;
186
268
  }
@@ -213,18 +295,19 @@ async function generateDrizzleSchema(authOptions, dialect, schemaOptions) {
213
295
  }
214
296
  return result.code;
215
297
  }
216
- async function loadUserAuthConfig(configPath, throwOnError = false) {
298
+ async function loadUserAuthConfig(configPath, throwOnError = false, alias) {
217
299
  const { createJiti } = await import('jiti');
218
300
  const { defineServerAuth: runtimeDefineServerAuth } = await import('../dist/runtime/config.js');
219
- const jiti = createJiti(import.meta.url, { interopDefault: true, moduleCache: false });
220
- if (!globalThis.__nuxtBetterAuthDefineServerAuth) {
301
+ const jiti = createJiti(import.meta.url, { interopDefault: true, moduleCache: false, alias });
302
+ const schemaGlobals = globalThis;
303
+ if (!schemaGlobals.__nuxtBetterAuthDefineServerAuth) {
221
304
  runtimeDefineServerAuth._count = 0;
222
- globalThis.__nuxtBetterAuthDefineServerAuth = runtimeDefineServerAuth;
305
+ schemaGlobals.__nuxtBetterAuthDefineServerAuth = runtimeDefineServerAuth;
223
306
  }
224
- if (!globalThis.defineServerAuth) {
225
- globalThis.defineServerAuth = globalThis.__nuxtBetterAuthDefineServerAuth;
307
+ if (!schemaGlobals.defineServerAuth) {
308
+ schemaGlobals.defineServerAuth = schemaGlobals.__nuxtBetterAuthDefineServerAuth;
226
309
  }
227
- globalThis.__nuxtBetterAuthDefineServerAuth._count++;
310
+ schemaGlobals.__nuxtBetterAuthDefineServerAuth._count++;
228
311
  try {
229
312
  const mod = await jiti.import(configPath);
230
313
  const configFn = mod.default;
@@ -243,19 +326,35 @@ async function loadUserAuthConfig(configPath, throwOnError = false) {
243
326
  consola$1.error("[@onmax/nuxt-better-auth] Failed to load auth config for schema generation. Schema may be incomplete:", error);
244
327
  return {};
245
328
  } finally {
246
- const sharedDefineServerAuth = globalThis.__nuxtBetterAuthDefineServerAuth;
329
+ const sharedDefineServerAuth = schemaGlobals.__nuxtBetterAuthDefineServerAuth;
247
330
  if (sharedDefineServerAuth) {
248
331
  sharedDefineServerAuth._count--;
249
332
  if (!sharedDefineServerAuth._count) {
250
- globalThis.__nuxtBetterAuthDefineServerAuth = void 0;
251
- if (globalThis.defineServerAuth === sharedDefineServerAuth) {
252
- globalThis.defineServerAuth = void 0;
333
+ schemaGlobals.__nuxtBetterAuthDefineServerAuth = void 0;
334
+ if (schemaGlobals.defineServerAuth === sharedDefineServerAuth) {
335
+ schemaGlobals.defineServerAuth = void 0;
253
336
  }
254
337
  }
255
338
  }
256
339
  }
257
340
  }
258
341
 
342
+ function getHubDialect(hub) {
343
+ if (!hub?.db)
344
+ return void 0;
345
+ if (typeof hub.db === "string")
346
+ return hub.db;
347
+ if (typeof hub.db === "object" && hub.db !== null)
348
+ return hub.db.dialect;
349
+ return void 0;
350
+ }
351
+ function getHubCasing(hub) {
352
+ if (!hub?.db || typeof hub.db !== "object" || hub.db === null)
353
+ return void 0;
354
+ return hub.db.casing;
355
+ }
356
+
357
+ const NODE_MODULES_SEGMENT_RE = /[\\/]/;
259
358
  function resolveSchemaSecondaryStorageInjection(hubSecondaryStorage, userHasSecondaryStorage, isProduction) {
260
359
  if (hubSecondaryStorage === true)
261
360
  return { inject: false };
@@ -269,7 +368,7 @@ function resolveSchemaSecondaryStorageInjection(hubSecondaryStorage, userHasSeco
269
368
  return { inject: false, warn: message };
270
369
  }
271
370
  function isInsideNodeModules(path) {
272
- return path.split(/[\\/]/).includes("node_modules");
371
+ return path.split(NODE_MODULES_SEGMENT_RE).includes("node_modules");
273
372
  }
274
373
  function resolveHubSchemaPath(buildDir, rootDir, dialect, exists = existsSync) {
275
374
  const rootTsPath = join(rootDir, ".nuxt", "better-auth", `schema.${dialect}.ts`);
@@ -286,7 +385,10 @@ function resolveHubSchemaPath(buildDir, rootDir, dialect, exists = existsSync) {
286
385
  async function loadAuthOptions(context) {
287
386
  const isProduction = !context.nuxt.options.dev;
288
387
  const configFile = `${context.serverConfigPath}.ts`;
289
- const userConfig = await loadUserAuthConfig(configFile, isProduction);
388
+ const alias = Object.fromEntries(
389
+ Object.entries(context.nuxt.options.alias).filter(([, value]) => typeof value === "string").map(([key, value]) => [key, value])
390
+ );
391
+ const userConfig = await loadUserAuthConfig(configFile, isProduction, alias);
290
392
  const extendedConfig = {};
291
393
  await context.nuxt.callHook("better-auth:config:extend", extendedConfig);
292
394
  const plugins = [...userConfig.plugins || [], ...extendedConfig.plugins || []];
@@ -348,21 +450,26 @@ async function setupBetterAuthSchema(nuxt, serverConfigPath, options, consola, h
348
450
  }
349
451
  }
350
452
 
453
+ const DEFAULT_SECRET_ENV = "NUXT_BETTER_AUTH_SECRET";
454
+ const FALLBACK_SECRET_ENV = "BETTER_AUTH_SECRET";
351
455
  const generateSecret = () => randomBytes(32).toString("hex");
352
456
  function readEnvFile(rootDir) {
353
457
  const envPath = join(rootDir, ".env");
354
458
  return existsSync(envPath) ? readFileSync(envPath, "utf-8") : "";
355
459
  }
356
460
  function hasEnvSecret(rootDir) {
357
- const match = readEnvFile(rootDir).match(/^BETTER_AUTH_SECRET=(.+)$/m);
358
- return !!match && !!match[1] && match[1].trim().length > 0;
461
+ const envFile = readEnvFile(rootDir);
462
+ return [DEFAULT_SECRET_ENV, FALLBACK_SECRET_ENV].some((name) => {
463
+ const match = envFile.match(new RegExp(`^${name}=(.+)$`, "m"));
464
+ return !!match && !!match[1] && match[1].trim().length > 0;
465
+ });
359
466
  }
360
467
  function appendSecretToEnv(rootDir, secret) {
361
468
  const envPath = join(rootDir, ".env");
362
469
  let content = readEnvFile(rootDir);
363
470
  if (content.length > 0 && !content.endsWith("\n"))
364
471
  content += "\n";
365
- content += `BETTER_AUTH_SECRET=${secret}
472
+ content += `${DEFAULT_SECRET_ENV}=${secret}
366
473
  `;
367
474
  writeFileSync(envPath, content, "utf-8");
368
475
  }
@@ -370,20 +477,20 @@ async function promptForSecret(rootDir, consola, options = {}) {
370
477
  const configuredSecret = options.configuredSecret?.trim();
371
478
  if (configuredSecret)
372
479
  return void 0;
373
- if (process.env.BETTER_AUTH_SECRET || hasEnvSecret(rootDir))
480
+ if (process.env.NUXT_BETTER_AUTH_SECRET || process.env.BETTER_AUTH_SECRET || hasEnvSecret(rootDir))
374
481
  return void 0;
375
482
  const hasTty = Boolean(process.stdin.isTTY && process.stdout.isTTY);
376
483
  if (options.prepare || !hasTty) {
377
- consola.warn("[nuxt-better-auth] Skipping BETTER_AUTH_SECRET prompt (non-interactive). Set BETTER_AUTH_SECRET or NUXT_BETTER_AUTH_SECRET.");
484
+ consola.warn("[nuxt-better-auth] Skipping NUXT_BETTER_AUTH_SECRET prompt (non-interactive). Set NUXT_BETTER_AUTH_SECRET or BETTER_AUTH_SECRET.");
378
485
  return void 0;
379
486
  }
380
487
  if (isCI || isTest) {
381
488
  const secret2 = generateSecret();
382
489
  appendSecretToEnv(rootDir, secret2);
383
- consola.info("Generated BETTER_AUTH_SECRET and added to .env (CI/test mode)");
490
+ consola.info("Generated NUXT_BETTER_AUTH_SECRET and added to .env (CI/test mode)");
384
491
  return secret2;
385
492
  }
386
- consola.box("BETTER_AUTH_SECRET is required for authentication.\nThis will be appended to your .env file.");
493
+ consola.box("NUXT_BETTER_AUTH_SECRET is required for authentication.\nThis will be appended to your .env file.\nBETTER_AUTH_SECRET is still supported as a fallback.");
387
494
  const choice = await consola.prompt("How do you want to set it?", {
388
495
  type: "select",
389
496
  options: [
@@ -394,7 +501,7 @@ async function promptForSecret(rootDir, consola, options = {}) {
394
501
  cancel: "null"
395
502
  });
396
503
  if (typeof choice === "symbol" || choice === "skip") {
397
- consola.warn("Skipping BETTER_AUTH_SECRET. Auth will fail without it in production.");
504
+ consola.warn("Skipping NUXT_BETTER_AUTH_SECRET. Auth will fail without it in production.");
398
505
  return void 0;
399
506
  }
400
507
  let secret;
@@ -410,17 +517,85 @@ async function promptForSecret(rootDir, consola, options = {}) {
410
517
  }
411
518
  const preview = `${secret.slice(0, 8)}...${secret.slice(-4)}`;
412
519
  const confirm = await consola.prompt(`Add to .env:
413
- BETTER_AUTH_SECRET=${preview}
520
+ ${DEFAULT_SECRET_ENV}=${preview}
414
521
  Proceed?`, { type: "confirm", initial: true, cancel: "null" });
415
522
  if (typeof confirm === "symbol" || !confirm) {
416
523
  consola.info("Cancelled. Secret not written.");
417
524
  return void 0;
418
525
  }
419
526
  appendSecretToEnv(rootDir, secret);
420
- consola.success("Added BETTER_AUTH_SECRET to .env");
527
+ consola.success("Added NUXT_BETTER_AUTH_SECRET to .env");
421
528
  return secret;
422
529
  }
423
530
 
531
+ function resolveDatabaseProvider(input) {
532
+ const enabledProviders = Object.entries(input.providers).filter(([_id, provider]) => provider.isEnabled?.(input.context) ?? true);
533
+ if (!enabledProviders.length) {
534
+ throw new Error("[nuxt-better-auth] No database provider is enabled. Register one with the better-auth:database:providers hook.");
535
+ }
536
+ enabledProviders.sort((a, b) => (b[1].priority ?? 0) - (a[1].priority ?? 0));
537
+ const [id, definition] = enabledProviders[0];
538
+ return { id, definition };
539
+ }
540
+
541
+ function resolveSecondaryStorage(input) {
542
+ const { options, clientOnly, hasNuxtHub, hub } = input;
543
+ const opt = options.hubSecondaryStorage ?? false;
544
+ const useHubKV = opt === true;
545
+ const secondaryStorageEnabled = opt === true || opt === "custom";
546
+ if (secondaryStorageEnabled && clientOnly) {
547
+ throw new Error("[nuxt-better-auth] hubSecondaryStorage is not available in clientOnly mode. Either disable clientOnly or remove auth.hubSecondaryStorage.");
548
+ }
549
+ if (useHubKV && (!hasNuxtHub || !hub?.kv)) {
550
+ throw new Error("[nuxt-better-auth] hubSecondaryStorage: true requires @nuxthub/core with hub.kv: true. Either add hub.kv: true to your nuxt.config or remove auth.hubSecondaryStorage.");
551
+ }
552
+ return { useHubKV, secondaryStorageEnabled };
553
+ }
554
+ function setupRuntimeConfig(input) {
555
+ const { nuxt, options, clientOnly, databaseProvider, consola } = input;
556
+ const { useHubKV, secondaryStorageEnabled } = resolveSecondaryStorage(input);
557
+ nuxt.options.runtimeConfig.public = nuxt.options.runtimeConfig.public || {};
558
+ const configuredSiteUrl = nuxt.options.runtimeConfig.public.siteUrl;
559
+ if (!configuredSiteUrl && process.env.NUXT_PUBLIC_SITE_URL)
560
+ nuxt.options.runtimeConfig.public.siteUrl = process.env.NUXT_PUBLIC_SITE_URL;
561
+ nuxt.options.runtimeConfig.public.auth = defu(nuxt.options.runtimeConfig.public.auth, {
562
+ redirects: {
563
+ login: options.redirects?.login ?? "/login",
564
+ guest: options.redirects?.guest ?? "/",
565
+ authenticated: options.redirects?.authenticated,
566
+ logout: options.redirects?.logout
567
+ },
568
+ preserveRedirect: options.preserveRedirect ?? true,
569
+ redirectQueryKey: options.redirectQueryKey ?? "redirect",
570
+ useDatabase: databaseProvider !== "none",
571
+ databaseProvider,
572
+ clientOnly,
573
+ session: {
574
+ skipHydratedSsrGetSession: options.session?.skipHydratedSsrGetSession ?? false
575
+ }
576
+ });
577
+ if (clientOnly) {
578
+ const siteUrl = nuxt.options.runtimeConfig.public.siteUrl;
579
+ if (!siteUrl)
580
+ consola.warn("clientOnly mode: set runtimeConfig.public.siteUrl (or NUXT_PUBLIC_SITE_URL) to your frontend URL");
581
+ consola.info("clientOnly mode enabled - server utilities (serverAuth, getRequestSession, getUserSession, requireUserSession) are not available");
582
+ return { useHubKV, secondaryStorageEnabled };
583
+ }
584
+ const currentSecret = nuxt.options.runtimeConfig.betterAuthSecret;
585
+ nuxt.options.runtimeConfig.betterAuthSecret = currentSecret || process.env.NUXT_BETTER_AUTH_SECRET || process.env.BETTER_AUTH_SECRET || "";
586
+ const betterAuthSecret = nuxt.options.runtimeConfig.betterAuthSecret;
587
+ if (!nuxt.options.dev && !nuxt.options._prepare && !betterAuthSecret) {
588
+ throw new Error("[nuxt-better-auth] NUXT_BETTER_AUTH_SECRET is required in production. Set NUXT_BETTER_AUTH_SECRET or BETTER_AUTH_SECRET environment variable.");
589
+ }
590
+ if (betterAuthSecret && betterAuthSecret.length < 32) {
591
+ throw new Error("[nuxt-better-auth] NUXT_BETTER_AUTH_SECRET must be at least 32 characters for security");
592
+ }
593
+ nuxt.options.runtimeConfig.auth = defu(nuxt.options.runtimeConfig.auth, {
594
+ hubSecondaryStorage: options.hubSecondaryStorage ?? false
595
+ });
596
+ return { useHubKV, secondaryStorageEnabled };
597
+ }
598
+
424
599
  function buildSecondaryStorageCode(useHubKV) {
425
600
  if (!useHubKV)
426
601
  return "export function createSecondaryStorage() { return undefined }";
@@ -435,6 +610,114 @@ export function createSecondaryStorage() {
435
610
  }
436
611
  function buildDatabaseCode(input) {
437
612
  if (input.provider === "nuxthub") {
613
+ if (input.hubDialect === "postgresql") {
614
+ return `import { db } from '@nuxthub/db'
615
+ import * as schema from './schema.${input.hubDialect}.mjs'
616
+ import { drizzleAdapter } from 'better-auth/adapters/drizzle'
617
+ import { drizzle } from 'drizzle-orm/postgres-js'
618
+ import postgres from 'postgres'
619
+
620
+ const dialect = 'pg'
621
+ const requestDatabaseKey = Symbol.for('nuxt-better-auth.requestDatabase')
622
+ const fallbackRequestDatabaseContext = new WeakMap()
623
+
624
+ function getRequestDatabaseContext(event) {
625
+ const eventWithContext = event
626
+ if (eventWithContext?.context && typeof eventWithContext.context === 'object')
627
+ return eventWithContext.context
628
+
629
+ let context = fallbackRequestDatabaseContext.get(event)
630
+ if (!context) {
631
+ context = {}
632
+ fallbackRequestDatabaseContext.set(event, context)
633
+ }
634
+ return context
635
+ }
636
+
637
+ function createHyperdriveAdapter(client) {
638
+ return drizzleAdapter(drizzle({ client, schema }), { provider: dialect, schema, usePlural: ${input.usePlural}, camelCase: ${input.camelCase} })
639
+ }
640
+
641
+ function getCloudflareContext(event) {
642
+ return event?.context?.cloudflare
643
+ }
644
+
645
+ function resolveHyperdrive(event) {
646
+ const cloudflareEnv = getCloudflareContext(event)?.env
647
+ return cloudflareEnv?.POSTGRES || process.env.POSTGRES || globalThis.__env__?.POSTGRES || globalThis.POSTGRES
648
+ }
649
+
650
+ function scheduleCleanup(event, cleanup) {
651
+ const cloudflareContext = getCloudflareContext(event)
652
+ if (typeof cloudflareContext?.context?.waitUntil === 'function') {
653
+ cloudflareContext.context.waitUntil(cleanup)
654
+ return
655
+ }
656
+
657
+ if (typeof event?.context?.waitUntil === 'function') {
658
+ event.context.waitUntil(cleanup)
659
+ return
660
+ }
661
+
662
+ const waitUntil = event?.waitUntil || event?.req?.waitUntil || event?.node?.req?.waitUntil
663
+ if (typeof waitUntil === 'function')
664
+ waitUntil.call(event?.req || event?.node?.req || event, cleanup)
665
+ else
666
+ void cleanup
667
+ }
668
+
669
+ function registerClientCleanup(event, client) {
670
+ const response = event?.node?.res
671
+ if (!response || typeof response.once !== 'function')
672
+ return
673
+
674
+ let closed = false
675
+
676
+ const cleanup = () => {
677
+ if (closed)
678
+ return
679
+
680
+ closed = true
681
+ const close = client.end({ timeout: 0 }).catch(() => {})
682
+ scheduleCleanup(event, close)
683
+ }
684
+
685
+ response.once('finish', cleanup)
686
+ response.once('close', cleanup)
687
+ }
688
+
689
+ export function createDatabase(event) {
690
+ const hyperdrive = resolveHyperdrive(event)
691
+ if (!hyperdrive?.connectionString)
692
+ return drizzleAdapter(db, { provider: dialect, schema, usePlural: ${input.usePlural}, camelCase: ${input.camelCase} })
693
+
694
+ if (event) {
695
+ const context = getRequestDatabaseContext(event)
696
+ const cached = context[requestDatabaseKey]
697
+ if (cached)
698
+ return cached
699
+
700
+ const client = postgres(hyperdrive.connectionString, {
701
+ prepare: false,
702
+ onnotice: () => {},
703
+ max: 1,
704
+ })
705
+ const database = createHyperdriveAdapter(client)
706
+
707
+ context[requestDatabaseKey] = database
708
+ registerClientCleanup(event, client)
709
+ return database
710
+ }
711
+ const client = postgres(hyperdrive.connectionString, {
712
+ prepare: false,
713
+ onnotice: () => {},
714
+ max: 1,
715
+ })
716
+
717
+ return createHyperdriveAdapter(client)
718
+ }
719
+ export { db }`;
720
+ }
438
721
  return `import { db } from '@nuxthub/db'
439
722
  import * as schema from './schema.${input.hubDialect}.mjs'
440
723
  import { drizzleAdapter } from 'better-auth/adapters/drizzle'
@@ -446,9 +729,153 @@ export { db }`;
446
729
  return `export function createDatabase() { return undefined }
447
730
  export const db = undefined`;
448
731
  }
732
+ function buildSchemaExportCode(hasHubDb, hubDialect) {
733
+ if (!hasHubDb)
734
+ return "export const schema = undefined\n";
735
+ return `export * from './schema.${hubDialect}.mjs'
736
+ import * as schema from './schema.${hubDialect}.mjs'
737
+ export { schema }
738
+ `;
739
+ }
740
+ function buildAuthRouteRulesCode(authRouteRules) {
741
+ return `export const authRouteRules = ${JSON.stringify(authRouteRules, null, 2)}
742
+ `;
743
+ }
744
+
745
+ function assertConfigPresence(configs, clientOnly) {
746
+ if (!clientOnly && !configs.server.exists)
747
+ throw new Error(`[nuxt-better-auth] Missing ${configs.server.file}.ts - export default defineServerAuth(...)`);
748
+ if (!configs.client.exists)
749
+ throw new Error(`[nuxt-better-auth] Missing ${configs.client.file}.ts - export default defineClientAuth(...)`);
750
+ }
751
+ function createDefaultDatabaseProviders(buildContext) {
752
+ return {
753
+ nuxthub: {
754
+ priority: 100,
755
+ isEnabled: ({ hasHubDbAvailable: enabled }) => enabled,
756
+ buildDatabaseCode: () => buildDatabaseCode({
757
+ provider: "nuxthub",
758
+ ...buildContext
759
+ })
760
+ },
761
+ none: {
762
+ priority: 0,
763
+ buildDatabaseCode: () => buildDatabaseCode({
764
+ provider: "none",
765
+ ...buildContext
766
+ })
767
+ }
768
+ };
769
+ }
770
+ function collectAuthRouteRules(nuxt) {
771
+ const runtimeRouteRulesSource = nuxt.options.nitro?.routeRules || nuxt.options.routeRules || {};
772
+ return Object.fromEntries(
773
+ Object.entries(runtimeRouteRulesSource).flatMap(([path, rule]) => {
774
+ if (!rule || typeof rule !== "object" || !("auth" in rule))
775
+ return [];
776
+ return [[path, { auth: rule.auth }]];
777
+ })
778
+ );
779
+ }
780
+ async function resolveAuthModuleSetup(input, dependencies = {}) {
781
+ const { nuxt, options, runtimeTypesAugmentPath, consola } = input;
782
+ const hasNuxtModuleFn = dependencies.hasNuxtModule ?? hasNuxtModule;
783
+ const clientOnly = options.clientOnly ?? false;
784
+ const configs = resolveAuthConfigDescriptors(nuxt, {
785
+ server: options.serverConfig,
786
+ client: options.clientConfig
787
+ }, {
788
+ configExists: dependencies.configExists
789
+ });
790
+ assertConfigPresence(configs, clientOnly);
791
+ const aliases = {
792
+ "#nuxt-better-auth": runtimeTypesAugmentPath,
793
+ "#auth/server": clientOnly ? void 0 : configs.server.path,
794
+ "#auth/client": configs.client.path
795
+ };
796
+ nuxt.options.alias["#nuxt-better-auth"] = aliases["#nuxt-better-auth"];
797
+ if (aliases["#auth/server"])
798
+ nuxt.options.alias["#auth/server"] = aliases["#auth/server"];
799
+ nuxt.options.alias["#auth/client"] = aliases["#auth/client"];
800
+ const hasNuxtHub = hasNuxtModuleFn("@nuxthub/core", nuxt);
801
+ const hub = hasNuxtHub ? nuxt.options.hub : void 0;
802
+ const hasHubDbAvailable = !clientOnly && hasNuxtHub && !!hub?.db;
803
+ const hubDialect = getHubDialect(hub) ?? "sqlite";
804
+ const usePlural = options.schema?.usePlural ?? false;
805
+ const camelCase = (options.schema?.casing ?? getHubCasing(hub)) !== "snake_case";
806
+ let providerId = "none";
807
+ let providerDefinition;
808
+ if (!clientOnly) {
809
+ const buildContext = { hubDialect, usePlural, camelCase };
810
+ const providers = createDefaultDatabaseProviders(buildContext);
811
+ const enabledContext = {
812
+ nuxt,
813
+ options,
814
+ clientOnly,
815
+ hasHubDbAvailable
816
+ };
817
+ await nuxt.callHook("better-auth:database:providers", providers);
818
+ const resolvedProvider = resolveDatabaseProvider({
819
+ providers,
820
+ context: enabledContext
821
+ });
822
+ providerId = resolvedProvider.id;
823
+ providerDefinition = resolvedProvider.definition;
824
+ }
825
+ const runtime = setupRuntimeConfig({
826
+ nuxt,
827
+ options,
828
+ clientOnly,
829
+ databaseProvider: providerId,
830
+ hasNuxtHub,
831
+ hub,
832
+ consola
833
+ });
834
+ if (runtime.useHubKV && !nuxt.options.alias["hub:kv"]) {
835
+ throw new Error("[nuxt-better-auth] hub:kv not found. Ensure @nuxthub/core is loaded before this module and hub.kv is enabled.");
836
+ }
837
+ const hasHubDb = providerId === "nuxthub";
838
+ if (hasHubDb && !nuxt.options.alias["hub:db"]) {
839
+ throw new Error("[nuxt-better-auth] hub:db not found. Ensure @nuxthub/core is loaded before this module and hub.db is configured.");
840
+ }
841
+ return {
842
+ clientOnly,
843
+ configs,
844
+ aliases,
845
+ hub: {
846
+ hasNuxtHub,
847
+ options: hub,
848
+ hasHubDbAvailable
849
+ },
850
+ database: {
851
+ providerId,
852
+ hasHubDb,
853
+ providerDefinition,
854
+ buildContext: clientOnly ? void 0 : { hubDialect, usePlural, camelCase }
855
+ },
856
+ runtime,
857
+ prepareTypes: clientOnly ? void 0 : {
858
+ serverDir: dirname(configs.server.path),
859
+ hasHubDb
860
+ },
861
+ serverTypes: clientOnly ? void 0 : {
862
+ serverConfigPath: configs.server.path,
863
+ hasHubDb
864
+ },
865
+ sharedTypes: {
866
+ clientConfigPath: configs.client.path
867
+ },
868
+ schemaGeneration: hasHubDb ? {
869
+ serverConfigPath: configs.server.path,
870
+ hubSecondaryStorage: options.hubSecondaryStorage ?? false,
871
+ externalizeNuxtHubDatabase: true
872
+ } : void 0
873
+ };
874
+ }
449
875
 
450
876
  function registerServerTypeTemplates(input) {
451
- const { serverConfigPath, hasHubDb, runtimeTypesPath } = input;
877
+ const { serverConfigPath, hasHubDb, runtimeTypesPath, sharedServerConfigSafe } = input;
878
+ const serverConfigTypeTemplateOptions = sharedServerConfigSafe ? { nuxt: true, nitro: true, node: true, shared: true } : { nuxt: true, nitro: true, node: true };
452
879
  addTypeTemplate({
453
880
  filename: "types/auth-secondary-storage.d.ts",
454
881
  getContents: () => `
@@ -461,17 +888,17 @@ declare module '#auth/secondary-storage' {
461
888
  export function createSecondaryStorage(): SecondaryStorage | undefined
462
889
  }
463
890
  `
464
- }, { nitro: true, node: true });
891
+ }, { nitro: true });
465
892
  addTypeTemplate({
466
893
  filename: "types/auth-database.d.ts",
467
894
  getContents: () => `
468
895
  declare module '#auth/database' {
469
896
  import type { BetterAuthOptions } from 'better-auth'
470
- export function createDatabase(): BetterAuthOptions['database']
897
+ export function createDatabase(event?: import('h3').H3Event): BetterAuthOptions['database']
471
898
  export const db: ${hasHubDb ? `typeof import('@nuxthub/db')['db']` : "undefined"}
472
899
  }
473
900
  `
474
- }, { nitro: true, node: true });
901
+ }, { nitro: true });
475
902
  addTypeTemplate({
476
903
  filename: "types/auth-schema.d.ts",
477
904
  getContents: () => `
@@ -489,13 +916,39 @@ declare module '#auth/schema' {
489
916
  } | undefined
490
917
  }
491
918
  `
492
- }, { nitro: true, node: true });
919
+ }, { nitro: true });
920
+ addTypeTemplate({
921
+ filename: "types/nuxt-better-auth-server-context.d.ts",
922
+ getContents: () => `
923
+ /// <reference path="./nitro-imports.d.ts" />
924
+ /// <reference path="./auth-database.d.ts" />
925
+ /// <reference path="./auth-schema.d.ts" />
926
+ /// <reference path="./auth-secondary-storage.d.ts" />
927
+ ${hasHubDb ? '/// <reference path="../hub/db.d.ts" />' : ""}
928
+
929
+ export {}
930
+ `
931
+ }, { node: true });
932
+ addTypeTemplate({
933
+ filename: "types/nuxt-better-auth-config-context.d.ts",
934
+ getContents: () => `
935
+ import type { RuntimeConfig } from 'nuxt/schema'
936
+
937
+ declare module '@onmax/nuxt-better-auth/config' {
938
+ interface ServerAuthContextExtension {
939
+ runtimeConfig: RuntimeConfig
940
+ db: ${hasHubDb ? `typeof import('@nuxthub/db')['db']` : "undefined"}
941
+ requestOrigin?: string
942
+ }
943
+ }
944
+
945
+ `
946
+ }, { nuxt: true, nitro: true, node: true, shared: true });
493
947
  addTypeTemplate({
494
948
  filename: "types/nuxt-better-auth-infer.d.ts",
495
949
  getContents: () => `
496
950
  import type { BetterAuthOptions, BetterAuthPlugin, InferPluginTypes, UnionToIntersection } from 'better-auth'
497
951
  import type { InferFieldsOutput } from 'better-auth/db'
498
- import type { RuntimeConfig } from 'nuxt/schema'
499
952
  import type createServerAuth from '${serverConfigPath}'
500
953
 
501
954
  type _RawConfig = ReturnType<typeof createServerAuth>
@@ -525,28 +978,10 @@ type _SessionFallback = _InferModelFieldsFromPlugins<_RawPlugins, 'session'> & _
525
978
  declare module '#nuxt-better-auth' {
526
979
  interface AuthUser extends _UserFallback {}
527
980
  interface AuthSession extends _SessionFallback {}
528
- interface ServerAuthContext {
529
- runtimeConfig: RuntimeConfig
530
- db: ${hasHubDb ? `typeof import('@nuxthub/db')['db']` : "undefined"}
531
- }
532
981
  type PluginTypes = InferPluginTypes<_Config>
533
982
  }
534
-
535
- interface _AugmentedServerAuthContext {
536
- runtimeConfig: RuntimeConfig
537
- db: ${hasHubDb ? `typeof import('@nuxthub/db')['db']` : "undefined"}
538
- }
539
-
540
- declare module '@onmax/nuxt-better-auth/config' {
541
- import type { BetterAuthOptions, BetterAuthPlugin } from 'better-auth'
542
- type ServerAuthConfig = Omit<BetterAuthOptions, 'secret' | 'baseURL'> & {
543
- plugins?: readonly BetterAuthPlugin[]
544
- }
545
- export function defineServerAuth<const R>(config: (ctx: _AugmentedServerAuthContext) => R & ServerAuthConfig): (ctx: _AugmentedServerAuthContext) => R
546
- export function defineServerAuth<const R>(config: R & ServerAuthConfig): (ctx: _AugmentedServerAuthContext) => R
547
- }
548
983
  `
549
- }, { nuxt: true, nitro: true, node: true });
984
+ }, serverConfigTypeTemplateOptions);
550
985
  addTypeTemplate({
551
986
  filename: "types/nuxt-better-auth-social-providers.d.ts",
552
987
  getContents: () => `
@@ -562,7 +997,7 @@ declare module '#nuxt-better-auth' {
562
997
  }
563
998
  }
564
999
  `
565
- }, { nuxt: true, nitro: true, node: true });
1000
+ }, serverConfigTypeTemplateOptions);
566
1001
  addTypeTemplate({
567
1002
  filename: "types/nuxt-better-auth-nitro.d.ts",
568
1003
  getContents: () => `
@@ -669,6 +1104,7 @@ declare module 'nuxt/dist/app/composables/fetch' {
669
1104
  PickKeys extends import('nuxt/dist/app/composables/asyncData').KeysOf<DataT> = import('nuxt/dist/app/composables/asyncData').KeysOf<DataT>,
670
1105
  DefaultT = DataT,
671
1106
  >(request: import('vue').Ref<Path> | Path | (() => Path), opts?: import('nuxt/dist/app/composables/fetch').UseFetchOptions<_ResT, DataT, PickKeys, DefaultT, Path, Method>): import('nuxt/dist/app/composables/asyncData').AsyncData<import('nuxt/dist/app/composables/asyncData').PickFrom<DataT, PickKeys> | DefaultT, ErrorT | undefined>
1107
+ export function useFetch(request: string | import('vue').Ref<string> | (() => string), opts?: any): any
672
1108
 
673
1109
  export function useLazyFetch<
674
1110
  ErrorT = FetchError,
@@ -688,6 +1124,7 @@ declare module 'nuxt/dist/app/composables/fetch' {
688
1124
  PickKeys extends import('nuxt/dist/app/composables/asyncData').KeysOf<DataT> = import('nuxt/dist/app/composables/asyncData').KeysOf<DataT>,
689
1125
  DefaultT = DataT,
690
1126
  >(request: import('vue').Ref<Path> | Path | (() => Path), opts?: Omit<import('nuxt/dist/app/composables/fetch').UseFetchOptions<_ResT, DataT, PickKeys, DefaultT, Path, Method>, 'lazy'>): import('nuxt/dist/app/composables/asyncData').AsyncData<import('nuxt/dist/app/composables/asyncData').PickFrom<DataT, PickKeys> | DefaultT, ErrorT | undefined>
1127
+ export function useLazyFetch(request: string | import('vue').Ref<string> | (() => string), opts?: any): any
691
1128
  }
692
1129
 
693
1130
  declare module 'nuxt/app' {
@@ -709,6 +1146,7 @@ declare module 'nuxt/app' {
709
1146
  PickKeys extends import('nuxt/dist/app/composables/asyncData').KeysOf<DataT> = import('nuxt/dist/app/composables/asyncData').KeysOf<DataT>,
710
1147
  DefaultT = DataT,
711
1148
  >(request: import('vue').Ref<Path> | Path | (() => Path), opts?: import('nuxt/dist/app/composables/fetch').UseFetchOptions<_ResT, DataT, PickKeys, DefaultT, Path, Method>): import('nuxt/dist/app/composables/asyncData').AsyncData<import('nuxt/dist/app/composables/asyncData').PickFrom<DataT, PickKeys> | DefaultT, ErrorT | undefined>
1149
+ export function useFetch(request: string | import('vue').Ref<string> | (() => string), opts?: any): any
712
1150
 
713
1151
  export function useLazyFetch<
714
1152
  ErrorT = FetchError,
@@ -728,6 +1166,7 @@ declare module 'nuxt/app' {
728
1166
  PickKeys extends import('nuxt/dist/app/composables/asyncData').KeysOf<DataT> = import('nuxt/dist/app/composables/asyncData').KeysOf<DataT>,
729
1167
  DefaultT = DataT,
730
1168
  >(request: import('vue').Ref<Path> | Path | (() => Path), opts?: Omit<import('nuxt/dist/app/composables/fetch').UseFetchOptions<_ResT, DataT, PickKeys, DefaultT, Path, Method>, 'lazy'>): import('nuxt/dist/app/composables/asyncData').AsyncData<import('nuxt/dist/app/composables/asyncData').PickFrom<DataT, PickKeys> | DefaultT, ErrorT | undefined>
1169
+ export function useLazyFetch(request: string | import('vue').Ref<string> | (() => string), opts?: any): any
731
1170
  }
732
1171
 
733
1172
  declare module 'nitropack' {
@@ -748,9 +1187,18 @@ declare module 'nitropack/types' {
748
1187
  }
749
1188
  interface InternalApi extends _GeneratedAuthInternalApi {}
750
1189
  }
1190
+ declare module 'nitro/types' {
1191
+ interface NitroRouteRules {
1192
+ auth?: import('${runtimeTypesPath}').AuthMeta
1193
+ }
1194
+ interface NitroRouteConfig {
1195
+ auth?: import('${runtimeTypesPath}').AuthMeta
1196
+ }
1197
+ interface InternalApi extends _GeneratedAuthInternalApi {}
1198
+ }
751
1199
  export {}
752
1200
  `
753
- }, { nuxt: true, nitro: true, node: true });
1201
+ }, { nitro: true, node: true });
754
1202
  }
755
1203
  function registerSharedTypeTemplates(input) {
756
1204
  addTypeTemplate({
@@ -778,15 +1226,30 @@ declare module '#nuxt-better-auth' {
778
1226
  }
779
1227
 
780
1228
  const consola = consola$1.withTag("nuxt-better-auth");
781
- function resolveDefaultClientConfig(options, rootDir, srcDir) {
782
- if (options.clientConfig !== "app/auth.config")
783
- return;
784
- const srcDirRelative = srcDir.replace(`${rootDir}/`, "");
785
- options.clientConfig = srcDirRelative === srcDir ? "auth.config" : `${srcDirRelative}/auth.config`;
1229
+ const serverAliasImportRE = /from\s+['"]#server/;
1230
+ const layersAliasImportRE = /from\s+['"]#layers\//;
1231
+ const rootAliasImportRE = /from\s+['"]~~/;
1232
+ const workspaceAliasImportRE = /from\s+['"]@@/;
1233
+ const dbIdentifierRE = /\bdb\b/;
1234
+ const sessionHookAfterIdentifierRE = /\bsessionHookAfter\b/;
1235
+ const nuxtHubDbImportRE = /@nuxthub\/db/;
1236
+ function isServerConfigSharedTypeSafe(serverConfigPath) {
1237
+ const resolvedPath = [
1238
+ serverConfigPath,
1239
+ `${serverConfigPath}.ts`,
1240
+ `${serverConfigPath}.mts`,
1241
+ `${serverConfigPath}.cts`,
1242
+ `${serverConfigPath}.js`,
1243
+ `${serverConfigPath}.mjs`,
1244
+ `${serverConfigPath}.cjs`
1245
+ ].find((path) => existsSync(path));
1246
+ if (!resolvedPath)
1247
+ return false;
1248
+ const contents = readFileSync(resolvedPath, "utf8");
1249
+ return !(serverAliasImportRE.test(contents) || layersAliasImportRE.test(contents) || rootAliasImportRE.test(contents) || workspaceAliasImportRE.test(contents) || dbIdentifierRE.test(contents) || sessionHookAfterIdentifierRE.test(contents) || nuxtHubDbImportRE.test(contents));
786
1250
  }
787
- async function createDefaultAuthConfigFiles(rootDir, srcDir) {
788
- const serverPath = join(rootDir, "server/auth.config.ts");
789
- const clientPath = join(srcDir, "auth.config.ts");
1251
+ async function createDefaultAuthConfigFiles(nuxt) {
1252
+ const configs = resolveAuthConfigDescriptors(nuxt);
790
1253
  const serverTemplate = `import { defineServerAuth } from '@onmax/nuxt-better-auth/config'
791
1254
 
792
1255
  export default defineServerAuth({
@@ -797,16 +1260,17 @@ export default defineServerAuth({
797
1260
 
798
1261
  export default defineClientAuth({})
799
1262
  `;
800
- if (!existsSync(serverPath)) {
1263
+ if (configs.server.shouldCreateDefaultFile) {
1264
+ const serverPath = `${configs.server.path}.ts`;
801
1265
  await mkdir(dirname(serverPath), { recursive: true });
802
1266
  await writeFile(serverPath, serverTemplate);
803
- consola.success("Created server/auth.config.ts");
1267
+ consola.success(`Created ${relative(configs.server.declaringLayerRoot, serverPath)}`);
804
1268
  }
805
- if (!existsSync(clientPath)) {
1269
+ if (configs.client.shouldCreateDefaultFile) {
1270
+ const clientPath = `${configs.client.path}.ts`;
806
1271
  await mkdir(dirname(clientPath), { recursive: true });
807
1272
  await writeFile(clientPath, clientTemplate);
808
- const relativePath = clientPath.replace(`${rootDir}/`, "");
809
- consola.success(`Created ${relativePath}`);
1273
+ consola.success(`Created ${relative(configs.client.declaringLayerRoot, clientPath)}`);
810
1274
  }
811
1275
  }
812
1276
  const module$1 = defineNuxtModule({
@@ -824,153 +1288,90 @@ const module$1 = defineNuxtModule({
824
1288
  const configuredSecret = nuxt.options.runtimeConfig?.betterAuthSecret;
825
1289
  const generatedSecret = await promptForSecret(nuxt.options.rootDir, consola, { configuredSecret, prepare: Boolean(nuxt.options._prepare) });
826
1290
  if (generatedSecret)
827
- process.env.BETTER_AUTH_SECRET = generatedSecret;
828
- await createDefaultAuthConfigFiles(nuxt.options.rootDir, nuxt.options.srcDir);
1291
+ process.env.NUXT_BETTER_AUTH_SECRET = generatedSecret;
1292
+ await createDefaultAuthConfigFiles(nuxt);
829
1293
  },
830
1294
  async setup(options, nuxt) {
831
1295
  const resolver = createResolver(import.meta.url);
832
- resolveDefaultClientConfig(options, nuxt.options.rootDir, nuxt.options.srcDir);
833
- const clientOnly = options.clientOnly;
834
- const serverConfigFile = options.serverConfig;
835
- const clientConfigFile = options.clientConfig;
836
- const serverConfigPath = resolver.resolve(nuxt.options.rootDir, serverConfigFile);
837
- const clientConfigPath = resolver.resolve(nuxt.options.rootDir, clientConfigFile);
838
- const serverConfigExists = existsSync(`${serverConfigPath}.ts`) || existsSync(`${serverConfigPath}.js`);
839
- const clientConfigExists = existsSync(`${clientConfigPath}.ts`) || existsSync(`${clientConfigPath}.js`);
840
- if (!clientOnly && !serverConfigExists)
841
- throw new Error(`[nuxt-better-auth] Missing ${serverConfigFile}.ts - export default defineServerAuth(...)`);
842
- if (!clientConfigExists)
843
- throw new Error(`[nuxt-better-auth] Missing ${clientConfigFile}.ts - export default defineClientAuth(...)`);
844
- const hasNuxtHub = hasNuxtModule("@nuxthub/core", nuxt);
845
- const hub = hasNuxtHub ? nuxt.options.hub : void 0;
846
- const hasHubDbAvailable = !clientOnly && hasNuxtHub && !!hub?.db;
847
- const deprecatedProvider = options.database?.provider;
848
- if (deprecatedProvider) {
849
- throw new Error(
850
- `[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.`
851
- );
852
- }
853
- let databaseProvider = "none";
854
- let hasHubDb = false;
855
- nuxt.options.alias["#nuxt-better-auth"] = resolver.resolve("./runtime/types/augment");
856
- if (!clientOnly)
857
- nuxt.options.alias["#auth/server"] = serverConfigPath;
858
- nuxt.options.alias["#auth/client"] = clientConfigPath;
859
- if (clientOnly) {
860
- setupRuntimeConfig({
861
- nuxt,
862
- options,
863
- clientOnly,
864
- databaseProvider,
865
- hasNuxtHub,
866
- hub,
867
- consola
868
- });
869
- } else {
870
- const hubDialect = getHubDialect(hub) ?? "sqlite";
871
- const usePlural = options.schema?.usePlural ?? false;
872
- const camelCase = (options.schema?.casing ?? getHubCasing(hub)) !== "snake_case";
873
- const providers = {
874
- nuxthub: {
875
- priority: 100,
876
- isEnabled: ({ hasHubDbAvailable: hasHubDbAvailable2 }) => hasHubDbAvailable2,
877
- buildDatabaseCode: () => buildDatabaseCode({
878
- provider: "nuxthub",
879
- hubDialect,
880
- usePlural,
881
- camelCase
882
- })
883
- },
884
- none: {
885
- priority: 0,
886
- buildDatabaseCode: () => buildDatabaseCode({
887
- provider: "none",
888
- hubDialect,
889
- usePlural,
890
- camelCase
891
- })
892
- }
893
- };
894
- const enabledCtx = { nuxt, options, clientOnly, hasHubDbAvailable };
895
- await nuxt.callHook("better-auth:database:providers", providers);
896
- const resolvedProvider = resolveDatabaseProvider({ providers, context: enabledCtx });
897
- databaseProvider = resolvedProvider.id;
898
- hasHubDb = databaseProvider === "nuxthub";
899
- const { useHubKV } = setupRuntimeConfig({
900
- nuxt,
901
- options,
902
- clientOnly,
903
- databaseProvider,
904
- hasNuxtHub,
905
- hub,
906
- consola
907
- });
908
- if (useHubKV && !nuxt.options.alias["hub:kv"]) {
909
- throw new Error("[nuxt-better-auth] hub:kv not found. Ensure @nuxthub/core is loaded before this module and hub.kv is enabled.");
910
- }
1296
+ const setup = await resolveAuthModuleSetup({
1297
+ nuxt,
1298
+ options,
1299
+ runtimeTypesAugmentPath: resolver.resolve("./runtime/types/augment"),
1300
+ consola
1301
+ });
1302
+ nuxt.options.alias["#nuxt-better-auth"] = setup.aliases["#nuxt-better-auth"];
1303
+ if (setup.aliases["#auth/server"])
1304
+ nuxt.options.alias["#auth/server"] = setup.aliases["#auth/server"];
1305
+ nuxt.options.alias["#auth/client"] = setup.aliases["#auth/client"];
1306
+ if (!setup.clientOnly) {
911
1307
  const secondaryStorageTemplate = addTemplate({
912
1308
  filename: "better-auth/secondary-storage.mjs",
913
- getContents: () => buildSecondaryStorageCode(useHubKV),
1309
+ getContents: () => buildSecondaryStorageCode(setup.runtime.useHubKV),
914
1310
  write: true
915
1311
  });
916
1312
  nuxt.options.alias["#auth/secondary-storage"] = secondaryStorageTemplate.dst;
917
- if (hasHubDb && !nuxt.options.alias["hub:db"]) {
918
- throw new Error("[nuxt-better-auth] hub:db not found. Ensure @nuxthub/core is loaded before this module and hub.db is configured.");
919
- }
920
- const setupCtx = { nuxt, options, clientOnly };
921
- await resolvedProvider.definition.setup?.(setupCtx);
922
- const buildCtx = { hubDialect, usePlural, camelCase };
923
1313
  const databaseTemplate = addTemplate({
924
1314
  filename: "better-auth/database.mjs",
925
- getContents: () => resolvedProvider.definition.buildDatabaseCode(buildCtx),
1315
+ getContents: () => setup.database.providerDefinition.buildDatabaseCode(setup.database.buildContext),
926
1316
  write: true
927
1317
  });
928
1318
  nuxt.options.alias["#auth/database"] = databaseTemplate.dst;
929
1319
  const schemaTemplate = addTemplate({
930
1320
  filename: "better-auth/schema.mjs",
931
- getContents: () => {
932
- if (!hasHubDb)
933
- return "export const schema = undefined\n";
934
- return `export * from './schema.${hubDialect}.mjs'
935
- import * as schema from './schema.${hubDialect}.mjs'
936
- export { schema }
937
- `;
938
- },
1321
+ getContents: () => buildSchemaExportCode(setup.database.hasHubDb, setup.database.buildContext?.hubDialect ?? "sqlite"),
939
1322
  write: true
940
1323
  });
941
1324
  nuxt.options.alias["#auth/schema"] = schemaTemplate.dst;
942
- registerServerTypeTemplates({
943
- serverConfigPath,
944
- hasHubDb,
945
- runtimeTypesPath: resolver.resolve("./runtime/types")
1325
+ }
1326
+ if (setup.prepareTypes) {
1327
+ registerPrepareTypesHook({
1328
+ nuxt,
1329
+ serverDir: setup.prepareTypes.serverDir,
1330
+ hasHubDb: setup.prepareTypes.hasHubDb
946
1331
  });
947
- if (hasHubDb)
948
- await setupBetterAuthSchema(nuxt, serverConfigPath, options, consola, options.hubSecondaryStorage ?? false);
949
1332
  }
950
- registerSharedTypeTemplates({
951
- runtimeTypesAugmentPath: resolver.resolve("./runtime/types/augment"),
952
- runtimeTypesPath: resolver.resolve("./runtime/types"),
953
- clientConfigPath
954
- });
955
- const runtimeRouteRulesSource = nuxt.options.nitro?.routeRules || nuxt.options.routeRules || {};
956
- const authRouteRules = Object.fromEntries(
957
- Object.entries(runtimeRouteRulesSource).flatMap(([path, rule]) => {
958
- if (!rule || typeof rule !== "object" || !("auth" in rule))
959
- return [];
960
- return [[path, { auth: rule.auth }]];
961
- })
962
- );
1333
+ if (setup.database.providerDefinition) {
1334
+ const setupCtx = {
1335
+ nuxt,
1336
+ options,
1337
+ clientOnly: setup.clientOnly
1338
+ };
1339
+ await setup.database.providerDefinition.setup?.(setupCtx);
1340
+ }
963
1341
  const authRouteRulesTemplate = addTemplate({
964
1342
  filename: "better-auth/route-rules.mjs",
965
- getContents: () => `export const authRouteRules = ${JSON.stringify(authRouteRules, null, 2)}
966
- `,
1343
+ getContents: () => buildAuthRouteRulesCode(collectAuthRouteRules(nuxt)),
967
1344
  write: true
968
1345
  });
969
1346
  nuxt.options.alias["#auth/route-rules"] = authRouteRulesTemplate.dst;
1347
+ if (setup.serverTypes) {
1348
+ registerServerTypeTemplates({
1349
+ serverConfigPath: setup.serverTypes.serverConfigPath,
1350
+ hasHubDb: setup.serverTypes.hasHubDb,
1351
+ runtimeTypesPath: resolver.resolve("./runtime/types"),
1352
+ sharedServerConfigSafe: isServerConfigSharedTypeSafe(setup.serverTypes.serverConfigPath)
1353
+ });
1354
+ }
1355
+ if (setup.schemaGeneration) {
1356
+ if (setup.schemaGeneration.externalizeNuxtHubDatabase)
1357
+ registerNuxtHubDatabaseExternalHook(nuxt);
1358
+ await setupBetterAuthSchema(
1359
+ nuxt,
1360
+ setup.schemaGeneration.serverConfigPath,
1361
+ options,
1362
+ consola,
1363
+ setup.schemaGeneration.hubSecondaryStorage
1364
+ );
1365
+ }
1366
+ registerSharedTypeTemplates({
1367
+ runtimeTypesAugmentPath: setup.aliases["#nuxt-better-auth"],
1368
+ runtimeTypesPath: resolver.resolve("./runtime/types"),
1369
+ clientConfigPath: setup.sharedTypes.clientConfigPath
1370
+ });
970
1371
  registerTemplateHmrHook(nuxt);
971
- registerServerRuntime({ clientOnly, resolve: resolver.resolve });
1372
+ registerServerRuntime({ clientOnly: setup.clientOnly, resolve: resolver.resolve });
972
1373
  registerAuthMiddlewareHook(nuxt, resolver.resolve);
973
- await registerDevtools({ nuxt, clientOnly, hasHubDb, resolve: resolver.resolve });
1374
+ await registerDevtools({ nuxt, clientOnly: setup.clientOnly, hasHubDb: setup.database.hasHubDb, resolve: resolver.resolve });
974
1375
  registerRouteRulesMetaHook(nuxt);
975
1376
  }
976
1377
  });