@onmax/nuxt-better-auth 0.0.2-alpha.3 → 0.0.2-alpha.31

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 (58) hide show
  1. package/README.md +1 -1
  2. package/dist/module.d.mts +28 -1
  3. package/dist/module.json +2 -2
  4. package/dist/module.mjs +965 -313
  5. package/dist/runtime/app/components/BetterAuthState.vue +1 -0
  6. package/dist/runtime/app/composables/useAction.d.ts +2 -0
  7. package/dist/runtime/app/composables/useAction.js +6 -0
  8. package/dist/runtime/app/composables/useAuthAsyncData.d.ts +6 -0
  9. package/dist/runtime/app/composables/useAuthAsyncData.js +16 -0
  10. package/dist/runtime/app/composables/useAuthClientAction.d.ts +5 -0
  11. package/dist/runtime/app/composables/useAuthClientAction.js +15 -0
  12. package/dist/runtime/app/composables/useAuthRequestFetch.d.ts +11 -0
  13. package/dist/runtime/app/composables/useAuthRequestFetch.js +4 -0
  14. package/dist/runtime/app/composables/useSignIn.d.ts +16 -0
  15. package/dist/runtime/app/composables/useSignIn.js +8 -0
  16. package/dist/runtime/app/composables/useSignUp.d.ts +5 -0
  17. package/dist/runtime/app/composables/useSignUp.js +8 -0
  18. package/dist/runtime/app/composables/useUserSession.d.ts +14 -12
  19. package/dist/runtime/app/composables/useUserSession.js +162 -67
  20. package/dist/runtime/app/internal/auth-action-error.d.ts +3 -0
  21. package/dist/runtime/app/internal/auth-action-error.js +33 -0
  22. package/dist/runtime/app/internal/auth-action-handles.d.ts +18 -0
  23. package/dist/runtime/app/internal/auth-action-handles.js +105 -0
  24. package/dist/runtime/app/internal/redirect-helpers.d.ts +4 -0
  25. package/dist/runtime/app/internal/redirect-helpers.js +37 -0
  26. package/dist/runtime/app/internal/session-fetch.d.ts +12 -0
  27. package/dist/runtime/app/internal/session-fetch.js +56 -0
  28. package/dist/runtime/app/internal/utils.d.ts +1 -0
  29. package/dist/runtime/app/internal/utils.js +3 -0
  30. package/dist/runtime/app/internal/wrap-auth-method.d.ts +15 -0
  31. package/dist/runtime/app/internal/wrap-auth-method.js +66 -0
  32. package/dist/runtime/app/middleware/auth.global.js +73 -12
  33. package/dist/runtime/app/pages/__better-auth-devtools.vue +4 -10
  34. package/dist/runtime/app/plugins/session.client.js +1 -2
  35. package/dist/runtime/config.d.ts +66 -11
  36. package/dist/runtime/config.js +9 -2
  37. package/dist/runtime/server/api/_better-auth/_schema.js +2 -1
  38. package/dist/runtime/server/api/_better-auth/accounts.get.js +3 -2
  39. package/dist/runtime/server/api/_better-auth/config.get.d.ts +17 -11
  40. package/dist/runtime/server/api/_better-auth/config.get.js +17 -5
  41. package/dist/runtime/server/api/_better-auth/sessions.delete.js +3 -2
  42. package/dist/runtime/server/api/_better-auth/sessions.get.d.ts +3 -1
  43. package/dist/runtime/server/api/_better-auth/sessions.get.js +3 -2
  44. package/dist/runtime/server/api/_better-auth/users.get.js +3 -2
  45. package/dist/runtime/server/api/auth/[...all].js +1 -1
  46. package/dist/runtime/server/middleware/route-access.js +1 -0
  47. package/dist/runtime/server/tsconfig.json +9 -1
  48. package/dist/runtime/server/utils/auth.d.ts +5 -7
  49. package/dist/runtime/server/utils/auth.js +197 -17
  50. package/dist/runtime/server/utils/custom-secondary-storage.d.ts +6 -0
  51. package/dist/runtime/server/utils/custom-secondary-storage.js +8 -0
  52. package/dist/runtime/server/utils/session.d.ts +4 -8
  53. package/dist/runtime/server/utils/session.js +43 -4
  54. package/dist/runtime/server/virtual-modules.d.ts +22 -0
  55. package/dist/runtime/types/augment.d.ts +18 -2
  56. package/dist/runtime/types.d.ts +12 -13
  57. package/dist/types.d.mts +1 -1
  58. package/package.json +32 -42
package/dist/module.mjs CHANGED
@@ -1,14 +1,115 @@
1
- import { existsSync } from 'node:fs';
1
+ import { existsSync, writeFileSync, readFileSync } from 'node:fs';
2
2
  import { mkdir, writeFile } from 'node:fs/promises';
3
- import { defineNuxtModule, createResolver, hasNuxtModule, addTemplate, addTypeTemplate, updateTemplates, addServerImportsDir, addServerScanDir, addServerHandler, addImportsDir, addPlugin, addComponentsDir, extendPages } 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 { isAbsolute, join, relative, dirname } from 'pathe';
5
6
  import { defu } from 'defu';
6
- import { join } from 'pathe';
7
7
  import { toRouteMatcher, createRouter } from 'radix3';
8
+ import { generateDrizzleSchema as generateDrizzleSchema$1 } from '@better-auth/cli/api';
9
+ import { randomBytes } from 'node:crypto';
10
+ import { isCI, isTest } from 'std-env';
8
11
  export { defineClientAuth, defineServerAuth } from '../dist/runtime/config.js';
9
12
 
13
+ const version = "0.0.2-alpha.31";
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
+ }
24
+
25
+ const CONFIG_EXTENSIONS = [".ts", ".js"];
26
+ const CONFIG_EXTENSION_RE = /\.(?:ts|js)$/;
27
+ const DEFAULT_CONFIG_FILES = {
28
+ server: "server/auth.config",
29
+ client: "app/auth.config"
30
+ };
31
+ const OPTION_KEY_BY_KIND = {
32
+ server: "serverConfig",
33
+ client: "clientConfig"
34
+ };
35
+ function stripConfigExtension(path) {
36
+ return path.replace(CONFIG_EXTENSION_RE, "");
37
+ }
38
+ function configExists(path) {
39
+ return CONFIG_EXTENSIONS.some((ext) => existsSync(`${path}${ext}`));
40
+ }
41
+ function getLayerDirectoriesWithConfigs(nuxt) {
42
+ const directories = getLayerDirectories(nuxt);
43
+ const layers = nuxt.options._layers;
44
+ return directories.map((directory, index) => ({ directory, layer: layers[index] }));
45
+ }
46
+ function getProjectDirectory(nuxt) {
47
+ return getLayerDirectories(nuxt)[0];
48
+ }
49
+ function getDefaultConfigPath(nuxt, kind) {
50
+ const project = getProjectDirectory(nuxt);
51
+ return kind === "server" ? join(project.server, "auth.config") : join(project.app, "auth.config");
52
+ }
53
+ function getLayerDefaultConfigPath(nuxt, kind) {
54
+ for (const { directory } of getLayerDirectoriesWithConfigs(nuxt)) {
55
+ const candidate = kind === "server" ? join(directory.server, "auth.config") : join(directory.app, "auth.config");
56
+ if (configExists(candidate))
57
+ return candidate;
58
+ }
59
+ }
60
+ function resolveDeclaringLayerRoot(nuxt, kind, file) {
61
+ const optionKey = OPTION_KEY_BY_KIND[kind];
62
+ for (const { directory, layer } of getLayerDirectoriesWithConfigs(nuxt)) {
63
+ const declared = layer?.config?.auth?.[optionKey];
64
+ if (typeof declared === "string" && stripConfigExtension(declared) === file)
65
+ return directory.root;
66
+ }
67
+ return getProjectDirectory(nuxt).root;
68
+ }
69
+ function getRelativeConfigFile(nuxt, path) {
70
+ return relative(getProjectDirectory(nuxt).root, path);
71
+ }
72
+ function getEffectiveModuleConfigFile(nuxt, kind) {
73
+ const optionKey = OPTION_KEY_BY_KIND[kind];
74
+ const authOptions = nuxt.options.auth;
75
+ return authOptions?.[optionKey] ?? DEFAULT_CONFIG_FILES[kind];
76
+ }
77
+ function shouldCreateDefaultModuleConfig(nuxt, kind, file = getEffectiveModuleConfigFile(nuxt, kind)) {
78
+ const normalizedFile = stripConfigExtension(file);
79
+ if (normalizedFile !== DEFAULT_CONFIG_FILES[kind])
80
+ return false;
81
+ const resolved = resolveModuleConfigPath(nuxt, kind, normalizedFile);
82
+ return !configExists(resolved.path);
83
+ }
84
+ function resolveModuleConfigPath(nuxt, kind, file) {
85
+ const normalizedFile = stripConfigExtension(file);
86
+ if (isAbsolute(normalizedFile)) {
87
+ return {
88
+ file: normalizedFile,
89
+ path: normalizedFile,
90
+ isDefault: false
91
+ };
92
+ }
93
+ if (normalizedFile === DEFAULT_CONFIG_FILES[kind]) {
94
+ const discoveredPath = getLayerDefaultConfigPath(nuxt, kind) ?? getDefaultConfigPath(nuxt, kind);
95
+ return {
96
+ file: getRelativeConfigFile(nuxt, discoveredPath),
97
+ path: discoveredPath,
98
+ isDefault: true
99
+ };
100
+ }
101
+ const baseRoot = resolveDeclaringLayerRoot(nuxt, kind, normalizedFile);
102
+ const path = join(baseRoot, normalizedFile);
103
+ return {
104
+ file: getRelativeConfigFile(nuxt, path),
105
+ path,
106
+ isDefault: false
107
+ };
108
+ }
109
+
10
110
  function setupDevTools(nuxt) {
11
- nuxt.hook("devtools:customTabs", (tabs) => {
111
+ const hookable = nuxt;
112
+ hookable.hook("devtools:customTabs", (tabs) => {
12
113
  tabs.push({
13
114
  category: "server",
14
115
  name: "better-auth",
@@ -22,143 +123,201 @@ function setupDevTools(nuxt) {
22
123
  });
23
124
  }
24
125
 
25
- function generateDrizzleSchema(tables, dialect, options) {
26
- const typedTables = tables;
27
- const imports = getImports(dialect, options);
28
- const tableDefinitions = Object.entries(typedTables).map(([tableName, table]) => generateTable(tableName, table, dialect, typedTables, options)).join("\n\n");
29
- return `${imports}
126
+ function registerTemplateHmrHook(nuxt) {
127
+ nuxt.hook("builder:watch", async (_event, relativePath) => {
128
+ if (relativePath.includes("auth.config"))
129
+ await updateTemplates({ filter: (t) => t.filename.includes("nuxt-better-auth") });
130
+ });
131
+ }
132
+ function registerServerRuntime(input) {
133
+ const { clientOnly, resolve } = input;
134
+ if (!clientOnly) {
135
+ addServerImportsDir(resolve("./runtime/server/utils"));
136
+ addServerImports([{ name: "defineServerAuth", from: resolve("./runtime/config") }]);
137
+ addServerScanDir(resolve("./runtime/server/middleware"));
138
+ addServerHandler({ route: "/api/auth/**", handler: resolve("./runtime/server/api/auth/[...all]") });
139
+ }
140
+ addImportsDir(resolve("./runtime/app/composables"));
141
+ addImportsDir(resolve("./runtime/utils"));
142
+ if (!clientOnly)
143
+ addPlugin({ src: resolve("./runtime/app/plugins/session.server"), mode: "server" });
144
+ addPlugin({ src: resolve("./runtime/app/plugins/session.client"), mode: "client" });
145
+ addComponentsDir({ path: resolve("./runtime/app/components") });
146
+ }
147
+ function registerAuthMiddlewareHook(nuxt, resolve) {
148
+ nuxt.hook("app:resolve", (app) => {
149
+ app.middleware.push({ name: "auth", path: resolve("./runtime/app/middleware/auth.global"), global: true });
150
+ });
151
+ }
152
+ async function registerDevtools(input) {
153
+ const { nuxt, clientOnly, hasHubDb, resolve } = input;
154
+ const isProduction = process.env.NODE_ENV === "production" || !nuxt.options.dev;
155
+ if (isProduction || clientOnly)
156
+ return;
157
+ if (!hasNuxtModule("@nuxt/ui"))
158
+ await installModule("@nuxt/ui");
159
+ setupDevTools(nuxt);
160
+ addServerHandler({ route: "/api/_better-auth/config", method: "get", handler: resolve("./runtime/server/api/_better-auth/config.get") });
161
+ if (hasHubDb) {
162
+ const handlers = [
163
+ { route: "/api/_better-auth/sessions", method: "get", handler: resolve("./runtime/server/api/_better-auth/sessions.get") },
164
+ { route: "/api/_better-auth/sessions", method: "delete", handler: resolve("./runtime/server/api/_better-auth/sessions.delete") },
165
+ { route: "/api/_better-auth/users", method: "get", handler: resolve("./runtime/server/api/_better-auth/users.get") },
166
+ { route: "/api/_better-auth/accounts", method: "get", handler: resolve("./runtime/server/api/_better-auth/accounts.get") }
167
+ ];
168
+ handlers.forEach((handler) => addServerHandler(handler));
169
+ }
170
+ extendPages((pages) => {
171
+ pages.push({ name: "better-auth-devtools", path: "/__better-auth-devtools", file: resolve("./runtime/app/pages/__better-auth-devtools.vue") });
172
+ });
173
+ }
174
+ function registerRouteRulesMetaHook(nuxt) {
175
+ nuxt.hook("pages:extend", (pages) => {
176
+ const options = nuxt.options;
177
+ const routeRules = options.nitro?.routeRules || options.routeRules || {};
178
+ if (!Object.keys(routeRules).length)
179
+ return;
180
+ const matcher = toRouteMatcher(createRouter({ routes: routeRules }));
181
+ const applyMetaFromRules = (page) => {
182
+ const matches = matcher.matchAll(page.path);
183
+ if (!matches.length)
184
+ return;
185
+ const matchedRules = defu({}, ...matches.reverse());
186
+ if (matchedRules.auth !== void 0) {
187
+ page.meta = page.meta || {};
188
+ page.meta.auth = matchedRules.auth;
189
+ }
190
+ page.children?.forEach((child) => applyMetaFromRules(child));
191
+ };
192
+ pages.forEach((page) => applyMetaFromRules(page));
193
+ });
194
+ }
195
+
196
+ function getHubDialect(hub) {
197
+ if (!hub?.db)
198
+ return void 0;
199
+ if (typeof hub.db === "string")
200
+ return hub.db;
201
+ if (typeof hub.db === "object" && hub.db !== null)
202
+ return hub.db.dialect;
203
+ return void 0;
204
+ }
205
+ function getHubCasing(hub) {
206
+ if (!hub?.db || typeof hub.db !== "object" || hub.db === null)
207
+ return void 0;
208
+ return hub.db.casing;
209
+ }
30
210
 
31
- ${tableDefinitions}
32
- `;
211
+ function resolveSecondaryStorage(input) {
212
+ const { options, clientOnly, hasNuxtHub, hub } = input;
213
+ const opt = options.hubSecondaryStorage ?? false;
214
+ const useHubKV = opt === true;
215
+ const secondaryStorageEnabled = opt === true || opt === "custom";
216
+ if (secondaryStorageEnabled && clientOnly) {
217
+ throw new Error("[nuxt-better-auth] hubSecondaryStorage is not available in clientOnly mode. Either disable clientOnly or remove auth.hubSecondaryStorage.");
218
+ }
219
+ if (useHubKV && (!hasNuxtHub || !hub?.kv)) {
220
+ 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.");
221
+ }
222
+ return { useHubKV, secondaryStorageEnabled };
33
223
  }
34
- function getImports(dialect, options) {
35
- switch (dialect) {
36
- case "sqlite":
37
- return `import { integer, sqliteTable, text } from 'drizzle-orm/sqlite-core'`;
38
- case "postgresql":
39
- return options?.useUuid ? `import { boolean, integer, pgTable, text, timestamp, uuid } from 'drizzle-orm/pg-core'` : `import { boolean, integer, pgTable, text, timestamp } from 'drizzle-orm/pg-core'`;
40
- case "mysql":
41
- return `import { boolean, int, mysqlTable, text, timestamp, varchar } from 'drizzle-orm/mysql-core'`;
42
- }
43
- }
44
- function generateTable(tableName, table, dialect, allTables, options) {
45
- const tableFunc = dialect === "sqlite" ? "sqliteTable" : dialect === "postgresql" ? "pgTable" : "mysqlTable";
46
- let dbTableName = table.modelName || tableName;
47
- if (options?.usePlural && !table.modelName) {
48
- dbTableName = `${tableName}s`;
49
- }
50
- const fields = Object.entries(table.fields).map(([fieldName, field]) => generateField(fieldName, field, dialect, allTables, options)).join(",\n ");
51
- const idField = generateIdField(dialect, options);
52
- return `export const ${tableName} = ${tableFunc}('${dbTableName}', {
53
- ${idField},
54
- ${fields}
55
- })`;
56
- }
57
- function generateIdField(dialect, options) {
58
- switch (dialect) {
59
- case "sqlite":
60
- if (options?.useUuid)
61
- consola$1.warn("[@onmax/nuxt-better-auth] useUuid ignored for SQLite (no native uuid type). Using text.");
62
- return `id: text('id').primaryKey()`;
63
- case "postgresql":
64
- return options?.useUuid ? `id: uuid('id').defaultRandom().primaryKey()` : `id: text('id').primaryKey()`;
65
- case "mysql":
66
- return `id: varchar('id', { length: 36 }).primaryKey()`;
67
- }
68
- }
69
- function generateField(fieldName, field, dialect, allTables, options) {
70
- const dbFieldName = fieldName;
71
- const isFkToId = options?.useUuid && field.references?.field === "id";
72
- let fieldDef;
73
- if (isFkToId && dialect === "postgresql")
74
- fieldDef = `uuid('${dbFieldName}')`;
75
- else if (isFkToId && dialect === "mysql")
76
- fieldDef = `varchar('${dbFieldName}', { length: 36 })`;
77
- else
78
- fieldDef = getFieldType(field.type, dialect, dbFieldName);
79
- if (field.required && field.defaultValue === void 0)
80
- fieldDef += ".notNull()";
81
- if (field.unique)
82
- fieldDef += ".unique()";
83
- if (field.defaultValue !== void 0) {
84
- if (typeof field.defaultValue === "boolean")
85
- fieldDef += `.default(${field.defaultValue})`;
86
- else if (typeof field.defaultValue === "string")
87
- fieldDef += `.default('${field.defaultValue}')`;
88
- else
89
- fieldDef += `.default(${field.defaultValue})`;
90
- if (field.required)
91
- fieldDef += ".notNull()";
92
- }
93
- if (field.references) {
94
- const refTable = field.references.model;
95
- if (allTables[refTable])
96
- fieldDef += `.references(() => ${refTable}.${field.references.field})`;
97
- }
98
- return `${fieldName}: ${fieldDef}`;
99
- }
100
- function getFieldType(type, dialect, fieldName) {
101
- const normalizedType = Array.isArray(type) ? "string" : type;
102
- switch (dialect) {
103
- case "sqlite":
104
- return getSqliteType(normalizedType, fieldName);
105
- case "postgresql":
106
- return getPostgresType(normalizedType, fieldName);
107
- case "mysql":
108
- return getMysqlType(normalizedType, fieldName);
109
- }
110
- }
111
- function getSqliteType(type, fieldName) {
112
- switch (type) {
113
- case "string":
114
- return `text('${fieldName}')`;
115
- case "boolean":
116
- return `integer('${fieldName}', { mode: 'boolean' })`;
117
- case "date":
118
- return `integer('${fieldName}', { mode: 'timestamp' })`;
119
- case "number":
120
- return `integer('${fieldName}')`;
121
- default:
122
- return `text('${fieldName}')`;
123
- }
124
- }
125
- function getPostgresType(type, fieldName) {
126
- switch (type) {
127
- case "string":
128
- return `text('${fieldName}')`;
129
- case "boolean":
130
- return `boolean('${fieldName}')`;
131
- case "date":
132
- return `timestamp('${fieldName}')`;
133
- case "number":
134
- return `integer('${fieldName}')`;
135
- default:
136
- return `text('${fieldName}')`;
137
- }
138
- }
139
- function getMysqlType(type, fieldName) {
140
- switch (type) {
141
- case "string":
142
- return `text('${fieldName}')`;
143
- case "boolean":
144
- return `boolean('${fieldName}')`;
145
- case "date":
146
- return `timestamp('${fieldName}')`;
147
- case "number":
148
- return `int('${fieldName}')`;
149
- default:
150
- return `text('${fieldName}')`;
224
+ function setupRuntimeConfig(input) {
225
+ const { nuxt, options, clientOnly, databaseProvider, consola } = input;
226
+ const { useHubKV, secondaryStorageEnabled } = resolveSecondaryStorage(input);
227
+ nuxt.options.runtimeConfig.public = nuxt.options.runtimeConfig.public || {};
228
+ const configuredSiteUrl = nuxt.options.runtimeConfig.public.siteUrl;
229
+ if (!configuredSiteUrl && process.env.NUXT_PUBLIC_SITE_URL)
230
+ nuxt.options.runtimeConfig.public.siteUrl = process.env.NUXT_PUBLIC_SITE_URL;
231
+ nuxt.options.runtimeConfig.public.auth = defu(nuxt.options.runtimeConfig.public.auth, {
232
+ redirects: {
233
+ login: options.redirects?.login ?? "/login",
234
+ guest: options.redirects?.guest ?? "/",
235
+ authenticated: options.redirects?.authenticated,
236
+ logout: options.redirects?.logout
237
+ },
238
+ preserveRedirect: options.preserveRedirect ?? true,
239
+ redirectQueryKey: options.redirectQueryKey ?? "redirect",
240
+ useDatabase: databaseProvider !== "none",
241
+ databaseProvider,
242
+ clientOnly,
243
+ session: {
244
+ skipHydratedSsrGetSession: options.session?.skipHydratedSsrGetSession ?? false
245
+ }
246
+ });
247
+ if (clientOnly) {
248
+ const siteUrl = nuxt.options.runtimeConfig.public.siteUrl;
249
+ if (!siteUrl)
250
+ consola.warn("clientOnly mode: set runtimeConfig.public.siteUrl (or NUXT_PUBLIC_SITE_URL) to your frontend URL");
251
+ consola.info("clientOnly mode enabled - server utilities (serverAuth, getRequestSession, getUserSession, requireUserSession) are not available");
252
+ return { useHubKV, secondaryStorageEnabled };
253
+ }
254
+ const currentSecret = nuxt.options.runtimeConfig.betterAuthSecret;
255
+ nuxt.options.runtimeConfig.betterAuthSecret = currentSecret || process.env.NUXT_BETTER_AUTH_SECRET || process.env.BETTER_AUTH_SECRET || "";
256
+ const betterAuthSecret = nuxt.options.runtimeConfig.betterAuthSecret;
257
+ if (!nuxt.options.dev && !nuxt.options._prepare && !betterAuthSecret) {
258
+ 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.");
151
259
  }
260
+ if (betterAuthSecret && betterAuthSecret.length < 32) {
261
+ throw new Error("[nuxt-better-auth] NUXT_BETTER_AUTH_SECRET must be at least 32 characters for security");
262
+ }
263
+ nuxt.options.runtimeConfig.auth = defu(nuxt.options.runtimeConfig.auth, {
264
+ hubSecondaryStorage: options.hubSecondaryStorage ?? false
265
+ });
266
+ return { useHubKV, secondaryStorageEnabled };
267
+ }
268
+
269
+ function dialectToProvider(dialect) {
270
+ return dialect === "postgresql" ? "pg" : dialect;
271
+ }
272
+ async function generateDrizzleSchema(authOptions, dialect, schemaOptions) {
273
+ const provider = dialectToProvider(dialect);
274
+ const options = {
275
+ ...authOptions,
276
+ advanced: {
277
+ ...authOptions.advanced,
278
+ database: {
279
+ ...authOptions.advanced?.database,
280
+ ...schemaOptions?.useUuid && { generateId: "uuid" }
281
+ }
282
+ }
283
+ };
284
+ const adapter = {
285
+ id: "drizzle",
286
+ options: {
287
+ provider,
288
+ camelCase: schemaOptions?.casing !== "snake_case",
289
+ adapterConfig: { usePlural: schemaOptions?.usePlural ?? false }
290
+ }
291
+ };
292
+ const result = await generateDrizzleSchema$1({
293
+ adapter,
294
+ options
295
+ });
296
+ if (!result.code) {
297
+ throw new Error(`Schema generation returned empty result for ${dialect}`);
298
+ }
299
+ return result.code;
152
300
  }
153
301
  async function loadUserAuthConfig(configPath, throwOnError = false) {
154
302
  const { createJiti } = await import('jiti');
155
- const jiti = createJiti(import.meta.url, { interopDefault: true });
303
+ const { defineServerAuth: runtimeDefineServerAuth } = await import('../dist/runtime/config.js');
304
+ const jiti = createJiti(import.meta.url, { interopDefault: true, moduleCache: false });
305
+ const schemaGlobals = globalThis;
306
+ if (!schemaGlobals.__nuxtBetterAuthDefineServerAuth) {
307
+ runtimeDefineServerAuth._count = 0;
308
+ schemaGlobals.__nuxtBetterAuthDefineServerAuth = runtimeDefineServerAuth;
309
+ }
310
+ if (!schemaGlobals.defineServerAuth) {
311
+ schemaGlobals.defineServerAuth = schemaGlobals.__nuxtBetterAuthDefineServerAuth;
312
+ }
313
+ schemaGlobals.__nuxtBetterAuthDefineServerAuth._count++;
156
314
  try {
157
315
  const mod = await jiti.import(configPath);
158
- const configFn = typeof mod === "object" && mod !== null && "default" in mod ? mod.default : mod;
316
+ const configFn = mod.default;
159
317
  if (typeof configFn === "function") {
160
318
  return configFn({ runtimeConfig: {}, db: null });
161
319
  }
320
+ consola$1.warn("[@onmax/nuxt-better-auth] auth.config.ts does not export default. Expected: export default defineServerAuth(...)");
162
321
  if (throwOnError) {
163
322
  throw new Error("auth.config.ts must export default defineServerAuth(...)");
164
323
  }
@@ -169,84 +328,222 @@ async function loadUserAuthConfig(configPath, throwOnError = false) {
169
328
  }
170
329
  consola$1.error("[@onmax/nuxt-better-auth] Failed to load auth config for schema generation. Schema may be incomplete:", error);
171
330
  return {};
331
+ } finally {
332
+ const sharedDefineServerAuth = schemaGlobals.__nuxtBetterAuthDefineServerAuth;
333
+ if (sharedDefineServerAuth) {
334
+ sharedDefineServerAuth._count--;
335
+ if (!sharedDefineServerAuth._count) {
336
+ schemaGlobals.__nuxtBetterAuthDefineServerAuth = void 0;
337
+ if (schemaGlobals.defineServerAuth === sharedDefineServerAuth) {
338
+ schemaGlobals.defineServerAuth = void 0;
339
+ }
340
+ }
341
+ }
172
342
  }
173
343
  }
174
344
 
175
- const consola = consola$1.withTag("nuxt-better-auth");
176
- const module$1 = defineNuxtModule({
177
- meta: { name: "@onmax/nuxt-better-auth", configKey: "auth", compatibility: { nuxt: ">=3.0.0" } },
178
- defaults: {
179
- serverConfig: "server/auth.config",
180
- clientConfig: "app/auth.config",
181
- redirects: { login: "/login", guest: "/" },
182
- secondaryStorage: false
183
- },
184
- async setup(options, nuxt) {
185
- const resolver = createResolver(import.meta.url);
186
- const serverConfigFile = options.serverConfig;
187
- const clientConfigFile = options.clientConfig;
188
- const serverConfigPath = resolver.resolve(nuxt.options.rootDir, serverConfigFile);
189
- const clientConfigPath = resolver.resolve(nuxt.options.rootDir, clientConfigFile);
190
- const serverConfigExists = existsSync(`${serverConfigPath}.ts`) || existsSync(`${serverConfigPath}.js`);
191
- const clientConfigExists = existsSync(`${clientConfigPath}.ts`) || existsSync(`${clientConfigPath}.js`);
192
- if (!serverConfigExists)
193
- throw new Error(`[nuxt-better-auth] Missing ${serverConfigFile}.ts - create with defineServerAuth()`);
194
- if (!clientConfigExists)
195
- throw new Error(`[nuxt-better-auth] Missing ${clientConfigFile}.ts - export createAppAuthClient()`);
196
- const hasNuxtHub = hasNuxtModule("@nuxthub/core", nuxt);
197
- const hub = hasNuxtHub ? nuxt.options.hub : void 0;
198
- const hasHubDb = hasNuxtHub && !!hub?.db;
199
- let secondaryStorageEnabled = options.secondaryStorage ?? false;
200
- if (secondaryStorageEnabled && (!hasNuxtHub || !hub?.kv)) {
201
- consola.warn("secondaryStorage requires @nuxthub/core with hub.kv: true. Disabling.");
202
- secondaryStorageEnabled = false;
345
+ const NODE_MODULES_SEGMENT_RE = /[\\/]/;
346
+ function resolveSchemaSecondaryStorageInjection(hubSecondaryStorage, userHasSecondaryStorage, isProduction) {
347
+ if (hubSecondaryStorage === true)
348
+ return { inject: false };
349
+ if (hubSecondaryStorage !== "custom")
350
+ return { inject: false };
351
+ if (userHasSecondaryStorage)
352
+ return { inject: true };
353
+ const message = '[nuxt-better-auth] hubSecondaryStorage: "custom" requires secondaryStorage in defineServerAuth() to omit the session table from the generated schema.';
354
+ if (isProduction)
355
+ return { inject: false, error: message };
356
+ return { inject: false, warn: message };
357
+ }
358
+ function isInsideNodeModules(path) {
359
+ return path.split(NODE_MODULES_SEGMENT_RE).includes("node_modules");
360
+ }
361
+ function resolveHubSchemaPath(buildDir, rootDir, dialect, exists = existsSync) {
362
+ const rootTsPath = join(rootDir, ".nuxt", "better-auth", `schema.${dialect}.ts`);
363
+ if (isInsideNodeModules(buildDir) && exists(rootTsPath))
364
+ return rootTsPath;
365
+ const tsPath = join(buildDir, "better-auth", `schema.${dialect}.ts`);
366
+ if (exists(tsPath))
367
+ return tsPath;
368
+ const mjsPath = join(buildDir, "better-auth", `schema.${dialect}.mjs`);
369
+ if (exists(mjsPath))
370
+ return mjsPath;
371
+ return null;
372
+ }
373
+ async function loadAuthOptions(context) {
374
+ const isProduction = !context.nuxt.options.dev;
375
+ const configFile = `${context.serverConfigPath}.ts`;
376
+ const userConfig = await loadUserAuthConfig(configFile, isProduction);
377
+ const extendedConfig = {};
378
+ await context.nuxt.callHook("better-auth:config:extend", extendedConfig);
379
+ const plugins = [...userConfig.plugins || [], ...extendedConfig.plugins || []];
380
+ return { userConfig, plugins };
381
+ }
382
+ async function setupBetterAuthSchema(nuxt, serverConfigPath, options, consola, hubSecondaryStorage) {
383
+ const hub = nuxt.options.hub;
384
+ const dialect = getHubDialect(hub);
385
+ if (!dialect || !["sqlite", "postgresql", "mysql"].includes(dialect)) {
386
+ consola.warn(`Unsupported database dialect: ${dialect}`);
387
+ return;
388
+ }
389
+ const context = { nuxt, serverConfigPath };
390
+ try {
391
+ const { userConfig, plugins } = await loadAuthOptions(context);
392
+ const userHasSecondaryStorage = userConfig.secondaryStorage != null;
393
+ const secondaryStorageResolution = resolveSchemaSecondaryStorageInjection(hubSecondaryStorage, userHasSecondaryStorage, !nuxt.options.dev);
394
+ if (secondaryStorageResolution.error)
395
+ throw new Error(secondaryStorageResolution.error);
396
+ if (secondaryStorageResolution.warn)
397
+ consola.warn(secondaryStorageResolution.warn);
398
+ const authOptions = {
399
+ ...userConfig,
400
+ plugins,
401
+ secondaryStorage: secondaryStorageResolution.inject ? { get: async (_key) => null, set: async (_key, _value, _ttl) => {
402
+ }, delete: async (_key) => {
403
+ } } : void 0
404
+ };
405
+ const hubCasing = getHubCasing(hub);
406
+ const schemaOptions = { ...options.schema, useUuid: userConfig.advanced?.database?.generateId === "uuid", casing: options.schema?.casing ?? hubCasing };
407
+ const schemaCode = await generateDrizzleSchema(authOptions, dialect, schemaOptions);
408
+ const schemaDir = join(nuxt.options.buildDir, "better-auth");
409
+ const schemaPathTs = join(schemaDir, `schema.${dialect}.ts`);
410
+ const schemaPathMjs = join(schemaDir, `schema.${dialect}.mjs`);
411
+ await mkdir(schemaDir, { recursive: true });
412
+ await writeFile(schemaPathTs, schemaCode);
413
+ await writeFile(schemaPathMjs, schemaCode);
414
+ if (isInsideNodeModules(nuxt.options.buildDir)) {
415
+ const rootSchemaDir = join(nuxt.options.rootDir, ".nuxt", "better-auth");
416
+ const rootSchemaPathTs = join(rootSchemaDir, `schema.${dialect}.ts`);
417
+ await mkdir(rootSchemaDir, { recursive: true });
418
+ await writeFile(rootSchemaPathTs, schemaCode);
203
419
  }
204
- nuxt.options.runtimeConfig.public = nuxt.options.runtimeConfig.public || {};
205
- nuxt.options.runtimeConfig.public.auth = defu(nuxt.options.runtimeConfig.public.auth, {
206
- redirects: { login: options.redirects?.login ?? "/login", guest: options.redirects?.guest ?? "/" },
207
- useDatabase: hasHubDb
420
+ addTemplate({ filename: `better-auth/schema.${dialect}.ts`, getContents: () => schemaCode, write: true });
421
+ addTemplate({ filename: `better-auth/schema.${dialect}.mjs`, getContents: () => schemaCode, write: true });
422
+ consola.info(`Generated ${dialect} schema (.ts + .mjs)`);
423
+ const nuxtWithHubHooks = nuxt;
424
+ nuxtWithHubHooks.hook("hub:db:schema:extend", ({ paths, dialect: hookDialect }) => {
425
+ const schemaPath = resolveHubSchemaPath(nuxt.options.buildDir, nuxt.options.rootDir, hookDialect);
426
+ if (schemaPath)
427
+ paths.unshift(schemaPath);
208
428
  });
209
- const betterAuthSecret = process.env.BETTER_AUTH_SECRET || process.env.NUXT_BETTER_AUTH_SECRET || nuxt.options.runtimeConfig.betterAuthSecret || "";
210
- if (!nuxt.options.dev && !betterAuthSecret) {
211
- throw new Error("[nuxt-better-auth] BETTER_AUTH_SECRET is required in production. Set BETTER_AUTH_SECRET or NUXT_BETTER_AUTH_SECRET environment variable.");
212
- }
213
- if (betterAuthSecret && betterAuthSecret.length < 32) {
214
- throw new Error("[nuxt-better-auth] BETTER_AUTH_SECRET must be at least 32 characters for security");
429
+ } catch (error) {
430
+ const isProduction = !nuxt.options.dev;
431
+ if (isProduction)
432
+ throw error;
433
+ consola.error("Failed to generate schema:", error);
434
+ throw error;
435
+ }
436
+ }
437
+
438
+ const DEFAULT_SECRET_ENV = "NUXT_BETTER_AUTH_SECRET";
439
+ const FALLBACK_SECRET_ENV = "BETTER_AUTH_SECRET";
440
+ const generateSecret = () => randomBytes(32).toString("hex");
441
+ function readEnvFile(rootDir) {
442
+ const envPath = join(rootDir, ".env");
443
+ return existsSync(envPath) ? readFileSync(envPath, "utf-8") : "";
444
+ }
445
+ function hasEnvSecret(rootDir) {
446
+ const envFile = readEnvFile(rootDir);
447
+ return [DEFAULT_SECRET_ENV, FALLBACK_SECRET_ENV].some((name) => {
448
+ const match = envFile.match(new RegExp(`^${name}=(.+)$`, "m"));
449
+ return !!match && !!match[1] && match[1].trim().length > 0;
450
+ });
451
+ }
452
+ function appendSecretToEnv(rootDir, secret) {
453
+ const envPath = join(rootDir, ".env");
454
+ let content = readEnvFile(rootDir);
455
+ if (content.length > 0 && !content.endsWith("\n"))
456
+ content += "\n";
457
+ content += `${DEFAULT_SECRET_ENV}=${secret}
458
+ `;
459
+ writeFileSync(envPath, content, "utf-8");
460
+ }
461
+ async function promptForSecret(rootDir, consola, options = {}) {
462
+ const configuredSecret = options.configuredSecret?.trim();
463
+ if (configuredSecret)
464
+ return void 0;
465
+ if (process.env.NUXT_BETTER_AUTH_SECRET || process.env.BETTER_AUTH_SECRET || hasEnvSecret(rootDir))
466
+ return void 0;
467
+ const hasTty = Boolean(process.stdin.isTTY && process.stdout.isTTY);
468
+ if (options.prepare || !hasTty) {
469
+ consola.warn("[nuxt-better-auth] Skipping NUXT_BETTER_AUTH_SECRET prompt (non-interactive). Set NUXT_BETTER_AUTH_SECRET or BETTER_AUTH_SECRET.");
470
+ return void 0;
471
+ }
472
+ if (isCI || isTest) {
473
+ const secret2 = generateSecret();
474
+ appendSecretToEnv(rootDir, secret2);
475
+ consola.info("Generated NUXT_BETTER_AUTH_SECRET and added to .env (CI/test mode)");
476
+ return secret2;
477
+ }
478
+ 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.");
479
+ const choice = await consola.prompt("How do you want to set it?", {
480
+ type: "select",
481
+ options: [
482
+ { label: "Generate for me", value: "generate", hint: "uses crypto.randomBytes(32)" },
483
+ { label: "Enter manually", value: "paste" },
484
+ { label: "Skip", value: "skip", hint: "will fail in production" }
485
+ ],
486
+ cancel: "null"
487
+ });
488
+ if (typeof choice === "symbol" || choice === "skip") {
489
+ consola.warn("Skipping NUXT_BETTER_AUTH_SECRET. Auth will fail without it in production.");
490
+ return void 0;
491
+ }
492
+ let secret;
493
+ if (choice === "generate") {
494
+ secret = generateSecret();
495
+ } else {
496
+ const input = await consola.prompt("Paste your secret (min 32 chars):", { type: "text", cancel: "null" });
497
+ if (typeof input === "symbol" || !input || input.length < 32) {
498
+ consola.warn("Invalid secret. Skipping.");
499
+ return void 0;
215
500
  }
216
- nuxt.options.runtimeConfig.betterAuthSecret = betterAuthSecret;
217
- nuxt.options.runtimeConfig.auth = defu(nuxt.options.runtimeConfig.auth, {
218
- secondaryStorage: secondaryStorageEnabled
219
- });
220
- nuxt.options.alias["#nuxt-better-auth"] = resolver.resolve("./runtime/types/augment");
221
- nuxt.options.alias["#auth/server"] = serverConfigPath;
222
- nuxt.options.alias["#auth/client"] = clientConfigPath;
223
- const secondaryStorageCode = secondaryStorageEnabled ? `import { kv } from 'hub:kv'
501
+ secret = input;
502
+ }
503
+ const preview = `${secret.slice(0, 8)}...${secret.slice(-4)}`;
504
+ const confirm = await consola.prompt(`Add to .env:
505
+ ${DEFAULT_SECRET_ENV}=${preview}
506
+ Proceed?`, { type: "confirm", initial: true, cancel: "null" });
507
+ if (typeof confirm === "symbol" || !confirm) {
508
+ consola.info("Cancelled. Secret not written.");
509
+ return void 0;
510
+ }
511
+ appendSecretToEnv(rootDir, secret);
512
+ consola.success("Added NUXT_BETTER_AUTH_SECRET to .env");
513
+ return secret;
514
+ }
515
+
516
+ function buildSecondaryStorageCode(useHubKV) {
517
+ if (!useHubKV)
518
+ return "export function createSecondaryStorage() { return undefined }";
519
+ return `import { kv } from '@nuxthub/kv'
224
520
  export function createSecondaryStorage() {
225
521
  return {
226
522
  get: async (key) => kv.get(\`_auth:\${key}\`),
227
523
  set: async (key, value, ttl) => kv.set(\`_auth:\${key}\`, value, { ttl }),
228
524
  delete: async (key) => kv.del(\`_auth:\${key}\`),
229
525
  }
230
- }` : `export function createSecondaryStorage() { return undefined }`;
231
- const secondaryStorageTemplate = addTemplate({ filename: "better-auth/secondary-storage.mjs", getContents: () => secondaryStorageCode, write: true });
232
- nuxt.options.alias["#auth/secondary-storage"] = secondaryStorageTemplate.dst;
233
- const hubDbPath = nuxt.options.alias["hub:db"];
234
- if (hasHubDb && !hubDbPath) {
235
- throw new Error("[nuxt-better-auth] hub:db alias not found. Ensure @nuxthub/core is loaded before this module.");
236
- }
237
- const hubDialect = typeof hub?.db === "string" ? hub.db : (typeof hub?.db === "object" ? hub.db.dialect : void 0) ?? "sqlite";
238
- const databaseCode = hasHubDb && hubDbPath ? `import { db, schema } from '${hubDbPath}'
526
+ }`;
527
+ }
528
+ function buildDatabaseCode(input) {
529
+ if (input.provider === "nuxthub") {
530
+ return `import { db } from '@nuxthub/db'
531
+ import * as schema from './schema.${input.hubDialect}.mjs'
239
532
  import { drizzleAdapter } from 'better-auth/adapters/drizzle'
240
- const rawDialect = '${hubDialect}'
533
+ const rawDialect = '${input.hubDialect}'
241
534
  const dialect = rawDialect === 'postgresql' ? 'pg' : rawDialect
242
- export function createDatabase() { return drizzleAdapter(db, { provider: dialect, schema }) }
243
- export { db }` : `export function createDatabase() { return undefined }
535
+ export function createDatabase() { return drizzleAdapter(db, { provider: dialect, schema, usePlural: ${input.usePlural}, camelCase: ${input.camelCase} }) }
536
+ export { db }`;
537
+ }
538
+ return `export function createDatabase() { return undefined }
244
539
  export const db = undefined`;
245
- const databaseTemplate = addTemplate({ filename: "better-auth/database.mjs", getContents: () => databaseCode, write: true });
246
- nuxt.options.alias["#auth/database"] = databaseTemplate.dst;
247
- addTypeTemplate({
248
- filename: "types/auth-secondary-storage.d.ts",
249
- getContents: () => `
540
+ }
541
+
542
+ function registerServerTypeTemplates(input) {
543
+ const { serverConfigPath, hasHubDb, runtimeTypesPath } = input;
544
+ addTypeTemplate({
545
+ filename: "types/auth-secondary-storage.d.ts",
546
+ getContents: () => `
250
547
  declare module '#auth/secondary-storage' {
251
548
  interface SecondaryStorage {
252
549
  get: (key: string) => Promise<string | null>
@@ -256,153 +553,508 @@ declare module '#auth/secondary-storage' {
256
553
  export function createSecondaryStorage(): SecondaryStorage | undefined
257
554
  }
258
555
  `
259
- });
260
- addTypeTemplate({
261
- filename: "types/auth-database.d.ts",
262
- getContents: () => `
556
+ }, { nitro: true, node: true });
557
+ addTypeTemplate({
558
+ filename: "types/auth-database.d.ts",
559
+ getContents: () => `
263
560
  declare module '#auth/database' {
264
- import type { drizzleAdapter } from 'better-auth/adapters/drizzle'
265
- export function createDatabase(): ReturnType<typeof drizzleAdapter> | undefined
266
- export const db: unknown
561
+ import type { BetterAuthOptions } from 'better-auth'
562
+ export function createDatabase(): BetterAuthOptions['database']
563
+ export const db: ${hasHubDb ? `typeof import('@nuxthub/db')['db']` : "undefined"}
267
564
  }
268
565
  `
269
- });
270
- addTypeTemplate({
271
- filename: "types/nuxt-better-auth.d.ts",
272
- getContents: () => `
273
- export * from '${resolver.resolve("./runtime/types/augment")}'
274
- export type { AuthMeta, AuthMode, AuthRouteRules, UserMatch, RequireSessionOptions, Auth, InferUser, InferSession } from '${resolver.resolve("./runtime/types")}'
566
+ }, { nitro: true, node: true });
567
+ addTypeTemplate({
568
+ filename: "types/auth-schema.d.ts",
569
+ getContents: () => `
570
+ declare module '#auth/schema' {
571
+ export const user: any
572
+ export const session: any
573
+ export const account: any
574
+ export const verification: any
575
+ export const schema: {
576
+ user: any
577
+ session: any
578
+ account: any
579
+ verification: any
580
+ [key: string]: any
581
+ } | undefined
582
+ }
275
583
  `
276
- });
277
- addTypeTemplate({
278
- filename: "types/nuxt-better-auth-infer.d.ts",
279
- getContents: () => `
280
- import type { InferUser, InferSession } from 'better-auth'
584
+ }, { nitro: true, node: true });
585
+ addTypeTemplate({
586
+ filename: "types/nuxt-better-auth-infer.d.ts",
587
+ getContents: () => `
588
+ import type { BetterAuthOptions, BetterAuthPlugin, InferPluginTypes, UnionToIntersection } from 'better-auth'
589
+ import type { InferFieldsOutput } from 'better-auth/db'
281
590
  import type { RuntimeConfig } from 'nuxt/schema'
282
- import type configFn from '${serverConfigPath}'
591
+ import type createServerAuth from '${serverConfigPath}'
592
+
593
+ type _RawConfig = ReturnType<typeof createServerAuth>
594
+ type _RawPlugins = _RawConfig extends { plugins: infer P } ? P : _RawConfig extends { plugins?: infer P } ? P : []
595
+ type _NormalizedPlugins = _RawPlugins extends readonly (infer T)[]
596
+ ? Array<T & BetterAuthPlugin>
597
+ : _RawPlugins extends (infer T)[]
598
+ ? Array<T & BetterAuthPlugin>
599
+ : BetterAuthPlugin[]
600
+ type _Config = Omit<BetterAuthOptions, 'plugins'> & Omit<_RawConfig, 'plugins'> & {
601
+ plugins?: _NormalizedPlugins
602
+ }
603
+
604
+ type _InferModelFieldsFromPlugins<P, M extends string> = P extends readonly (infer Plugin)[]
605
+ ? UnionToIntersection<Plugin extends { schema: { [K in M]: { fields: infer F } } } ? InferFieldsOutput<F> : {}>
606
+ : P extends (infer Plugin)[]
607
+ ? UnionToIntersection<Plugin extends { schema: { [K in M]: { fields: infer F } } } ? InferFieldsOutput<F> : {}>
608
+ : {}
283
609
 
284
- type _Config = ReturnType<typeof configFn>
610
+ type _InferModelFieldsFromOptions<C, M extends 'user' | 'session'> = C extends { [K in M]: { additionalFields: infer F } }
611
+ ? InferFieldsOutput<F>
612
+ : {}
613
+
614
+ type _UserFallback = _InferModelFieldsFromPlugins<_RawPlugins, 'user'> & _InferModelFieldsFromOptions<_RawConfig, 'user'>
615
+ type _SessionFallback = _InferModelFieldsFromPlugins<_RawPlugins, 'session'> & _InferModelFieldsFromOptions<_RawConfig, 'session'>
285
616
 
286
617
  declare module '#nuxt-better-auth' {
287
- interface AuthUser extends InferUser<_Config> {}
288
- interface AuthSession { session: InferSession<_Config>['session'], user: InferUser<_Config> }
618
+ interface AuthUser extends _UserFallback {}
619
+ interface AuthSession extends _SessionFallback {}
289
620
  interface ServerAuthContext {
290
621
  runtimeConfig: RuntimeConfig
291
- ${hasHubDb ? `db: typeof import('hub:db')['db']` : ""}
622
+ db: ${hasHubDb ? `typeof import('@nuxthub/db')['db']` : "undefined"}
292
623
  }
624
+ type PluginTypes = InferPluginTypes<_Config>
625
+ }
626
+
627
+ interface _AugmentedServerAuthContext {
628
+ runtimeConfig: RuntimeConfig
629
+ db: ${hasHubDb ? `typeof import('@nuxthub/db')['db']` : "undefined"}
630
+ }
631
+
632
+ declare module '@onmax/nuxt-better-auth/config' {
633
+ import type { BetterAuthOptions, BetterAuthPlugin } from 'better-auth'
634
+ type ServerAuthConfig = Omit<BetterAuthOptions, 'secret' | 'baseURL'> & {
635
+ plugins?: readonly BetterAuthPlugin[]
636
+ }
637
+ export function defineServerAuth<const R>(config: (ctx: _AugmentedServerAuthContext) => R & ServerAuthConfig): (ctx: _AugmentedServerAuthContext) => R
638
+ export function defineServerAuth<const R>(config: R & ServerAuthConfig): (ctx: _AugmentedServerAuthContext) => R
293
639
  }
294
640
  `
295
- });
296
- addTypeTemplate({
297
- filename: "types/nuxt-better-auth-client.d.ts",
298
- getContents: () => `
299
- import type { createAppAuthClient } from '${clientConfigPath}'
641
+ }, { nuxt: true, nitro: true, node: true });
642
+ addTypeTemplate({
643
+ filename: "types/nuxt-better-auth-social-providers.d.ts",
644
+ getContents: () => `
645
+ import type createServerAuth from '${serverConfigPath}'
646
+
647
+ type _RawConfig = ReturnType<typeof createServerAuth>
648
+ type _RawSocialProviders = _RawConfig extends { socialProviders: infer S } ? S : _RawConfig extends { socialProviders?: infer S } ? S : {}
649
+ type _SocialProviderIds = Extract<keyof NonNullable<_RawSocialProviders>, string>
650
+
300
651
  declare module '#nuxt-better-auth' {
301
- export type AppAuthClient = ReturnType<typeof createAppAuthClient>
652
+ interface AuthSocialProviderRegistry {
653
+ ids: _SocialProviderIds
654
+ }
302
655
  }
303
656
  `
304
- });
305
- addTypeTemplate({
306
- filename: "types/nuxt-better-auth-nitro.d.ts",
307
- getContents: () => `
657
+ }, { nuxt: true, nitro: true, node: true });
658
+ addTypeTemplate({
659
+ filename: "types/nuxt-better-auth-nitro.d.ts",
660
+ getContents: () => `
661
+ import type createServerAuth from '${serverConfigPath}'
662
+ import type { BetterAuthOptions } from 'better-auth'
663
+ import type { getEndpoints } from 'better-auth/api'
664
+ import type { Serialize, Simplify } from 'nitropack/types'
665
+ import type { FetchError } from 'ofetch'
666
+
667
+ type _RawConfig = ReturnType<typeof createServerAuth>
668
+ type _RawPlugins = _RawConfig extends { plugins: infer P } ? P : _RawConfig extends { plugins?: infer P } ? P : []
669
+ type _Config = Omit<BetterAuthOptions, 'plugins'> & Omit<_RawConfig, 'plugins'> & {
670
+ plugins?: _RawPlugins
671
+ }
672
+
673
+ type _AuthApi = ReturnType<typeof getEndpoints<_Config>>['api']
674
+ type _NormalizeMethod<M extends string> = M extends '*' ? 'default' : Lowercase<M>
675
+ type _RouteMethodFromOption<M> = M extends readonly (infer T)[]
676
+ ? _NormalizeMethod<Extract<T, string>>
677
+ : M extends string
678
+ ? _NormalizeMethod<M>
679
+ : 'default'
680
+ type _RouteMethodFromEndpoint<E> = E extends { options: { method: infer M } } ? _RouteMethodFromOption<M> : 'default'
681
+ type _RoutePathFromEndpoint<E> = E extends { path: infer P extends string }
682
+ ? string extends P
683
+ ? never
684
+ : \`/api/auth\${P}\`
685
+ : never
686
+ type _RouteResponseFromEndpoint<E> = E extends (...args: any[]) => Promise<infer R> ? Simplify<Serialize<Awaited<R>>> : never
687
+ type _RouteDefaultResponse<E> = never
688
+ type _UnionToIntersection<U> = (U extends unknown ? (value: U) => void : never) extends (value: infer I) => void ? I : never
689
+
690
+ type _CoreAuthInternalApi = {
691
+ [K in keyof _AuthApi as _RoutePathFromEndpoint<_AuthApi[K]>]: {
692
+ [M in _RouteMethodFromEndpoint<_AuthApi[K]> | 'default']: M extends 'default' ? _RouteDefaultResponse<_AuthApi[K]> : _RouteResponseFromEndpoint<_AuthApi[K]>
693
+ }
694
+ }
695
+ type _PluginEndpointMaps<Plugins> = Plugins extends readonly (infer Plugin)[]
696
+ ? Plugin extends { endpoints: infer Endpoints extends Record<string, unknown> }
697
+ ? {
698
+ [K in keyof Endpoints as _RoutePathFromEndpoint<Endpoints[K]>]: {
699
+ [M in _RouteMethodFromEndpoint<Endpoints[K]> | 'default']: M extends 'default' ? _RouteDefaultResponse<Endpoints[K]> : _RouteResponseFromEndpoint<Endpoints[K]>
700
+ }
701
+ }
702
+ : {}
703
+ : Plugins extends (infer Plugin)[]
704
+ ? Plugin extends { endpoints: infer Endpoints extends Record<string, unknown> }
705
+ ? {
706
+ [K in keyof Endpoints as _RoutePathFromEndpoint<Endpoints[K]>]: {
707
+ [M in _RouteMethodFromEndpoint<Endpoints[K]> | 'default']: M extends 'default' ? _RouteDefaultResponse<Endpoints[K]> : _RouteResponseFromEndpoint<Endpoints[K]>
708
+ }
709
+ }
710
+ : {}
711
+ : {}
712
+ type _PluginAuthInternalApi = _UnionToIntersection<_PluginEndpointMaps<_RawPlugins>>
713
+ type _GeneratedAuthInternalApi = _CoreAuthInternalApi & _PluginAuthInternalApi
714
+
715
+ type _RoutePathToRequestPath<Path extends string> = Path extends \`\${infer Prefix}:\${string}/\${infer Rest}\`
716
+ ? \`\${Prefix}\${string}/\${_RoutePathToRequestPath<Rest>}\`
717
+ : Path extends \`\${infer Prefix}:\${string}\`
718
+ ? \`\${Prefix}\${string}\`
719
+ : Path
720
+ type _AuthApiPatternPath = Extract<keyof _GeneratedAuthInternalApi, string>
721
+ type _AuthApiRequestPath = _RoutePathToRequestPath<_AuthApiPatternPath>
722
+ type _AuthPatternFromRequestPath<Path extends string> = {
723
+ [Pattern in _AuthApiPatternPath]: Path extends _RoutePathToRequestPath<Pattern> ? Pattern : never
724
+ }[_AuthApiPatternPath]
725
+ type _AuthEndpointMethod<Path extends _AuthApiRequestPath> = Extract<keyof _GeneratedAuthInternalApi[_AuthPatternFromRequestPath<Path>], string>
726
+
727
+ type _AuthFetchMethod<Path extends _AuthApiRequestPath> = Extract<Exclude<import('nitropack/types').NitroFetchOptions<Path>['method'], undefined>, string>
728
+ type _AuthFetchDefaultMethod<Path extends _AuthApiRequestPath> = 'get'
729
+ type _AuthFetchResolvedMethod<Path extends _AuthApiRequestPath, Method extends string> = Lowercase<Method> extends _AuthEndpointMethod<Path>
730
+ ? Lowercase<Method>
731
+ : never
732
+ type _AuthFetchResult<Path extends _AuthApiRequestPath, Method extends string> = _GeneratedAuthInternalApi[_AuthPatternFromRequestPath<Path>][_AuthFetchResolvedMethod<Path, Method>]
733
+
734
+ declare module '#nuxt-better-auth' {
735
+ export type AuthApiInternalRoutes = _GeneratedAuthInternalApi
736
+ export type AuthApiEndpointPatternPath = _AuthApiPatternPath
737
+ export type AuthApiEndpointPath = _AuthApiRequestPath
738
+ export type AuthApiEndpointMethod<Path extends AuthApiEndpointPath> = _AuthEndpointMethod<Path>
739
+ export type AuthApiEndpointResponse<
740
+ Path extends AuthApiEndpointPath,
741
+ Method extends AuthApiEndpointMethod<Path> = AuthApiEndpointMethod<Path>,
742
+ > = AuthApiInternalRoutes[_AuthPatternFromRequestPath<Path>][Method]
743
+ }
744
+
745
+ declare module 'nuxt/dist/app/composables/fetch' {
746
+ export function useFetch<
747
+ ErrorT = FetchError,
748
+ Path extends import('#nuxt-better-auth').AuthApiEndpointPath = import('#nuxt-better-auth').AuthApiEndpointPath,
749
+ Method extends _AuthFetchMethod<Path> = _AuthFetchDefaultMethod<Path>,
750
+ _ResT = _AuthFetchResult<Path, Method>,
751
+ DataT = _ResT,
752
+ PickKeys extends import('nuxt/dist/app/composables/asyncData').KeysOf<DataT> = import('nuxt/dist/app/composables/asyncData').KeysOf<DataT>,
753
+ DefaultT = undefined,
754
+ >(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>
755
+ export function useFetch<
756
+ ErrorT = FetchError,
757
+ Path extends import('#nuxt-better-auth').AuthApiEndpointPath = import('#nuxt-better-auth').AuthApiEndpointPath,
758
+ Method extends _AuthFetchMethod<Path> = _AuthFetchDefaultMethod<Path>,
759
+ _ResT = _AuthFetchResult<Path, Method>,
760
+ DataT = _ResT,
761
+ PickKeys extends import('nuxt/dist/app/composables/asyncData').KeysOf<DataT> = import('nuxt/dist/app/composables/asyncData').KeysOf<DataT>,
762
+ DefaultT = DataT,
763
+ >(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>
764
+
765
+ export function useLazyFetch<
766
+ ErrorT = FetchError,
767
+ Path extends import('#nuxt-better-auth').AuthApiEndpointPath = import('#nuxt-better-auth').AuthApiEndpointPath,
768
+ Method extends _AuthFetchMethod<Path> = _AuthFetchDefaultMethod<Path>,
769
+ _ResT = _AuthFetchResult<Path, Method>,
770
+ DataT = _ResT,
771
+ PickKeys extends import('nuxt/dist/app/composables/asyncData').KeysOf<DataT> = import('nuxt/dist/app/composables/asyncData').KeysOf<DataT>,
772
+ DefaultT = undefined,
773
+ >(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>
774
+ export function useLazyFetch<
775
+ ErrorT = FetchError,
776
+ Path extends import('#nuxt-better-auth').AuthApiEndpointPath = import('#nuxt-better-auth').AuthApiEndpointPath,
777
+ Method extends _AuthFetchMethod<Path> = _AuthFetchDefaultMethod<Path>,
778
+ _ResT = _AuthFetchResult<Path, Method>,
779
+ DataT = _ResT,
780
+ PickKeys extends import('nuxt/dist/app/composables/asyncData').KeysOf<DataT> = import('nuxt/dist/app/composables/asyncData').KeysOf<DataT>,
781
+ DefaultT = DataT,
782
+ >(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>
783
+ }
784
+
785
+ declare module 'nuxt/app' {
786
+ export function useFetch<
787
+ ErrorT = FetchError,
788
+ Path extends import('#nuxt-better-auth').AuthApiEndpointPath = import('#nuxt-better-auth').AuthApiEndpointPath,
789
+ Method extends _AuthFetchMethod<Path> = _AuthFetchDefaultMethod<Path>,
790
+ _ResT = _AuthFetchResult<Path, Method>,
791
+ DataT = _ResT,
792
+ PickKeys extends import('nuxt/dist/app/composables/asyncData').KeysOf<DataT> = import('nuxt/dist/app/composables/asyncData').KeysOf<DataT>,
793
+ DefaultT = undefined,
794
+ >(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>
795
+ export function useFetch<
796
+ ErrorT = FetchError,
797
+ Path extends import('#nuxt-better-auth').AuthApiEndpointPath = import('#nuxt-better-auth').AuthApiEndpointPath,
798
+ Method extends _AuthFetchMethod<Path> = _AuthFetchDefaultMethod<Path>,
799
+ _ResT = _AuthFetchResult<Path, Method>,
800
+ DataT = _ResT,
801
+ PickKeys extends import('nuxt/dist/app/composables/asyncData').KeysOf<DataT> = import('nuxt/dist/app/composables/asyncData').KeysOf<DataT>,
802
+ DefaultT = DataT,
803
+ >(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>
804
+
805
+ export function useLazyFetch<
806
+ ErrorT = FetchError,
807
+ Path extends import('#nuxt-better-auth').AuthApiEndpointPath = import('#nuxt-better-auth').AuthApiEndpointPath,
808
+ Method extends _AuthFetchMethod<Path> = _AuthFetchDefaultMethod<Path>,
809
+ _ResT = _AuthFetchResult<Path, Method>,
810
+ DataT = _ResT,
811
+ PickKeys extends import('nuxt/dist/app/composables/asyncData').KeysOf<DataT> = import('nuxt/dist/app/composables/asyncData').KeysOf<DataT>,
812
+ DefaultT = undefined,
813
+ >(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>
814
+ export function useLazyFetch<
815
+ ErrorT = FetchError,
816
+ Path extends import('#nuxt-better-auth').AuthApiEndpointPath = import('#nuxt-better-auth').AuthApiEndpointPath,
817
+ Method extends _AuthFetchMethod<Path> = _AuthFetchDefaultMethod<Path>,
818
+ _ResT = _AuthFetchResult<Path, Method>,
819
+ DataT = _ResT,
820
+ PickKeys extends import('nuxt/dist/app/composables/asyncData').KeysOf<DataT> = import('nuxt/dist/app/composables/asyncData').KeysOf<DataT>,
821
+ DefaultT = DataT,
822
+ >(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>
823
+ }
824
+
825
+ declare module 'nitropack' {
826
+ interface NitroRouteRules {
827
+ auth?: import('${runtimeTypesPath}').AuthMeta
828
+ }
829
+ interface NitroRouteConfig {
830
+ auth?: import('${runtimeTypesPath}').AuthMeta
831
+ }
832
+ interface InternalApi extends _GeneratedAuthInternalApi {}
833
+ }
308
834
  declare module 'nitropack/types' {
309
835
  interface NitroRouteRules {
310
- auth?: import('${resolver.resolve("./runtime/types")}').AuthMeta
836
+ auth?: import('${runtimeTypesPath}').AuthMeta
837
+ }
838
+ interface NitroRouteConfig {
839
+ auth?: import('${runtimeTypesPath}').AuthMeta
311
840
  }
841
+ interface InternalApi extends _GeneratedAuthInternalApi {}
312
842
  }
843
+ export {}
313
844
  `
314
- });
315
- nuxt.hook("builder:watch", async (_event, relativePath) => {
316
- if (relativePath.includes("auth.config")) {
317
- await updateTemplates({ filter: (t) => t.filename.includes("nuxt-better-auth") });
845
+ }, { nuxt: true, nitro: true, node: true });
846
+ }
847
+ function registerSharedTypeTemplates(input) {
848
+ addTypeTemplate({
849
+ filename: "types/nuxt-better-auth.d.ts",
850
+ getContents: () => `
851
+ import type { AppSession } from '${input.runtimeTypesAugmentPath}'
852
+ export * from '${input.runtimeTypesAugmentPath}'
853
+ export type { AuthMeta, AuthMode, AuthRouteRules, AuthSocialProviderId, Auth, InferUser, InferSession } from '${input.runtimeTypesPath}'
854
+ declare module 'h3' {
855
+ interface H3EventContext {
856
+ requestSession?: AppSession | null
857
+ }
858
+ }
859
+ `
860
+ });
861
+ addTypeTemplate({
862
+ filename: "types/nuxt-better-auth-client.d.ts",
863
+ getContents: () => `
864
+ import type createAppAuthClient from '${input.clientConfigPath}'
865
+ declare module '#nuxt-better-auth' {
866
+ export type AppAuthClient = ReturnType<typeof createAppAuthClient>
867
+ }
868
+ `
869
+ });
870
+ }
871
+
872
+ const consola = consola$1.withTag("nuxt-better-auth");
873
+ async function createDefaultAuthConfigFiles(nuxt) {
874
+ const project = getLayerDirectories(nuxt)[0];
875
+ const rootDir = project.root;
876
+ const serverPath = join(project.server, "auth.config.ts");
877
+ const clientPath = join(project.app, "auth.config.ts");
878
+ const serverConfigFile = getEffectiveModuleConfigFile(nuxt, "server");
879
+ const clientConfigFile = getEffectiveModuleConfigFile(nuxt, "client");
880
+ const serverTemplate = `import { defineServerAuth } from '@onmax/nuxt-better-auth/config'
881
+
882
+ export default defineServerAuth({
883
+ emailAndPassword: { enabled: true },
884
+ })
885
+ `;
886
+ const clientTemplate = `import { defineClientAuth } from '@onmax/nuxt-better-auth/config'
887
+
888
+ export default defineClientAuth({})
889
+ `;
890
+ if (shouldCreateDefaultModuleConfig(nuxt, "server", serverConfigFile)) {
891
+ await mkdir(dirname(serverPath), { recursive: true });
892
+ await writeFile(serverPath, serverTemplate);
893
+ consola.success(`Created ${relative(rootDir, serverPath)}`);
894
+ }
895
+ if (shouldCreateDefaultModuleConfig(nuxt, "client", clientConfigFile)) {
896
+ await mkdir(dirname(clientPath), { recursive: true });
897
+ await writeFile(clientPath, clientTemplate);
898
+ consola.success(`Created ${relative(rootDir, clientPath)}`);
899
+ }
900
+ }
901
+ const module$1 = defineNuxtModule({
902
+ meta: { name: "@onmax/nuxt-better-auth", version, configKey: "auth", compatibility: { nuxt: ">=4.0.0" } },
903
+ defaults: {
904
+ clientOnly: false,
905
+ serverConfig: "server/auth.config",
906
+ clientConfig: "app/auth.config",
907
+ redirects: { login: "/login", guest: "/" },
908
+ preserveRedirect: true,
909
+ redirectQueryKey: "redirect",
910
+ hubSecondaryStorage: false
911
+ },
912
+ async onInstall(nuxt) {
913
+ const configuredSecret = nuxt.options.runtimeConfig?.betterAuthSecret;
914
+ const generatedSecret = await promptForSecret(nuxt.options.rootDir, consola, { configuredSecret, prepare: Boolean(nuxt.options._prepare) });
915
+ if (generatedSecret)
916
+ process.env.NUXT_BETTER_AUTH_SECRET = generatedSecret;
917
+ await createDefaultAuthConfigFiles(nuxt);
918
+ },
919
+ async setup(options, nuxt) {
920
+ const resolver = createResolver(import.meta.url);
921
+ const clientOnly = options.clientOnly;
922
+ const serverConfigFile = options.serverConfig;
923
+ const clientConfigFile = options.clientConfig;
924
+ const { file: resolvedServerConfigFile, path: serverConfigPath } = resolveModuleConfigPath(nuxt, "server", serverConfigFile);
925
+ const { file: resolvedClientConfigFile, path: clientConfigPath } = resolveModuleConfigPath(nuxt, "client", clientConfigFile);
926
+ const serverConfigExists = existsSync(`${serverConfigPath}.ts`) || existsSync(`${serverConfigPath}.js`);
927
+ const clientConfigExists = existsSync(`${clientConfigPath}.ts`) || existsSync(`${clientConfigPath}.js`);
928
+ if (!clientOnly && !serverConfigExists)
929
+ throw new Error(`[nuxt-better-auth] Missing ${resolvedServerConfigFile}.ts - export default defineServerAuth(...)`);
930
+ if (!clientConfigExists)
931
+ throw new Error(`[nuxt-better-auth] Missing ${resolvedClientConfigFile}.ts - export default defineClientAuth(...)`);
932
+ const hasNuxtHub = hasNuxtModule("@nuxthub/core", nuxt);
933
+ const hub = hasNuxtHub ? nuxt.options.hub : void 0;
934
+ const hasHubDbAvailable = !clientOnly && hasNuxtHub && !!hub?.db;
935
+ let databaseProvider = "none";
936
+ let hasHubDb = false;
937
+ nuxt.options.alias["#nuxt-better-auth"] = resolver.resolve("./runtime/types/augment");
938
+ if (!clientOnly)
939
+ nuxt.options.alias["#auth/server"] = serverConfigPath;
940
+ nuxt.options.alias["#auth/client"] = clientConfigPath;
941
+ if (clientOnly) {
942
+ setupRuntimeConfig({
943
+ nuxt,
944
+ options,
945
+ clientOnly,
946
+ databaseProvider,
947
+ hasNuxtHub,
948
+ hub,
949
+ consola
950
+ });
951
+ } else {
952
+ const hubDialect = getHubDialect(hub) ?? "sqlite";
953
+ const usePlural = options.schema?.usePlural ?? false;
954
+ const camelCase = (options.schema?.casing ?? getHubCasing(hub)) !== "snake_case";
955
+ const providers = {
956
+ nuxthub: {
957
+ priority: 100,
958
+ isEnabled: ({ hasHubDbAvailable: hasHubDbAvailable2 }) => hasHubDbAvailable2,
959
+ buildDatabaseCode: () => buildDatabaseCode({
960
+ provider: "nuxthub",
961
+ hubDialect,
962
+ usePlural,
963
+ camelCase
964
+ })
965
+ },
966
+ none: {
967
+ priority: 0,
968
+ buildDatabaseCode: () => buildDatabaseCode({
969
+ provider: "none",
970
+ hubDialect,
971
+ usePlural,
972
+ camelCase
973
+ })
974
+ }
975
+ };
976
+ const enabledCtx = { nuxt, options, clientOnly, hasHubDbAvailable };
977
+ await nuxt.callHook("better-auth:database:providers", providers);
978
+ const resolvedProvider = resolveDatabaseProvider({ providers, context: enabledCtx });
979
+ databaseProvider = resolvedProvider.id;
980
+ hasHubDb = databaseProvider === "nuxthub";
981
+ const { useHubKV } = setupRuntimeConfig({
982
+ nuxt,
983
+ options,
984
+ clientOnly,
985
+ databaseProvider,
986
+ hasNuxtHub,
987
+ hub,
988
+ consola
989
+ });
990
+ if (useHubKV && !nuxt.options.alias["hub:kv"]) {
991
+ throw new Error("[nuxt-better-auth] hub:kv not found. Ensure @nuxthub/core is loaded before this module and hub.kv is enabled.");
318
992
  }
319
- });
320
- addServerImportsDir(resolver.resolve("./runtime/server/utils"));
321
- addServerScanDir(resolver.resolve("./runtime/server/middleware"));
322
- addServerHandler({ route: "/api/auth/**", handler: resolver.resolve("./runtime/server/api/auth/[...all]") });
323
- addImportsDir(resolver.resolve("./runtime/app/composables"));
324
- addImportsDir(resolver.resolve("./runtime/utils"));
325
- addPlugin({ src: resolver.resolve("./runtime/app/plugins/session.server"), mode: "server" });
326
- addPlugin({ src: resolver.resolve("./runtime/app/plugins/session.client"), mode: "client" });
327
- addComponentsDir({ path: resolver.resolve("./runtime/app/components") });
328
- nuxt.hook("app:resolve", (app) => {
329
- app.middleware.push({ name: "auth", path: resolver.resolve("./runtime/app/middleware/auth.global"), global: true });
330
- });
331
- if (hasHubDb) {
332
- await setupBetterAuthSchema(nuxt, serverConfigPath, options);
333
- }
334
- const isProduction = process.env.NODE_ENV === "production" || !nuxt.options.dev;
335
- if (!isProduction) {
336
- setupDevTools(nuxt);
337
- addServerHandler({ route: "/api/_better-auth/config", method: "get", handler: resolver.resolve("./runtime/server/api/_better-auth/config.get") });
338
- if (hasHubDb) {
339
- addServerHandler({ route: "/api/_better-auth/sessions", method: "get", handler: resolver.resolve("./runtime/server/api/_better-auth/sessions.get") });
340
- addServerHandler({ route: "/api/_better-auth/sessions", method: "delete", handler: resolver.resolve("./runtime/server/api/_better-auth/sessions.delete") });
341
- addServerHandler({ route: "/api/_better-auth/users", method: "get", handler: resolver.resolve("./runtime/server/api/_better-auth/users.get") });
342
- addServerHandler({ route: "/api/_better-auth/accounts", method: "get", handler: resolver.resolve("./runtime/server/api/_better-auth/accounts.get") });
993
+ const secondaryStorageTemplate = addTemplate({
994
+ filename: "better-auth/secondary-storage.mjs",
995
+ getContents: () => buildSecondaryStorageCode(useHubKV),
996
+ write: true
997
+ });
998
+ nuxt.options.alias["#auth/secondary-storage"] = secondaryStorageTemplate.dst;
999
+ if (hasHubDb && !nuxt.options.alias["hub:db"]) {
1000
+ throw new Error("[nuxt-better-auth] hub:db not found. Ensure @nuxthub/core is loaded before this module and hub.db is configured.");
343
1001
  }
344
- extendPages((pages) => {
345
- pages.push({ name: "better-auth-devtools", path: "/__better-auth-devtools", file: resolver.resolve("./runtime/app/pages/__better-auth-devtools.vue") });
1002
+ const setupCtx = { nuxt, options, clientOnly };
1003
+ await resolvedProvider.definition.setup?.(setupCtx);
1004
+ const buildCtx = { hubDialect, usePlural, camelCase };
1005
+ const databaseTemplate = addTemplate({
1006
+ filename: "better-auth/database.mjs",
1007
+ getContents: () => resolvedProvider.definition.buildDatabaseCode(buildCtx),
1008
+ write: true
1009
+ });
1010
+ nuxt.options.alias["#auth/database"] = databaseTemplate.dst;
1011
+ const schemaTemplate = addTemplate({
1012
+ filename: "better-auth/schema.mjs",
1013
+ getContents: () => {
1014
+ if (!hasHubDb)
1015
+ return "export const schema = undefined\n";
1016
+ return `export * from './schema.${hubDialect}.mjs'
1017
+ import * as schema from './schema.${hubDialect}.mjs'
1018
+ export { schema }
1019
+ `;
1020
+ },
1021
+ write: true
1022
+ });
1023
+ nuxt.options.alias["#auth/schema"] = schemaTemplate.dst;
1024
+ registerServerTypeTemplates({
1025
+ serverConfigPath,
1026
+ hasHubDb,
1027
+ runtimeTypesPath: resolver.resolve("./runtime/types")
346
1028
  });
1029
+ if (hasHubDb)
1030
+ await setupBetterAuthSchema(nuxt, serverConfigPath, options, consola, options.hubSecondaryStorage ?? false);
347
1031
  }
348
- nuxt.hook("pages:extend", (pages) => {
349
- const routeRules = nuxt.options.routeRules || {};
350
- if (!Object.keys(routeRules).length)
351
- return;
352
- const matcher = toRouteMatcher(createRouter({ routes: routeRules }));
353
- const applyMetaFromRules = (page) => {
354
- const matches = matcher.matchAll(page.path);
355
- if (!matches.length)
356
- return;
357
- const matchedRules = defu({}, ...matches.reverse());
358
- if (matchedRules.auth !== void 0) {
359
- page.meta = page.meta || {};
360
- page.meta.auth = matchedRules.auth;
361
- }
362
- page.children?.forEach((child) => applyMetaFromRules(child));
363
- };
364
- pages.forEach((page) => applyMetaFromRules(page));
1032
+ registerSharedTypeTemplates({
1033
+ runtimeTypesAugmentPath: resolver.resolve("./runtime/types/augment"),
1034
+ runtimeTypesPath: resolver.resolve("./runtime/types"),
1035
+ clientConfigPath
1036
+ });
1037
+ const runtimeRouteRulesSource = nuxt.options.nitro?.routeRules || nuxt.options.routeRules || {};
1038
+ const authRouteRules = Object.fromEntries(
1039
+ Object.entries(runtimeRouteRulesSource).flatMap(([path, rule]) => {
1040
+ if (!rule || typeof rule !== "object" || !("auth" in rule))
1041
+ return [];
1042
+ return [[path, { auth: rule.auth }]];
1043
+ })
1044
+ );
1045
+ const authRouteRulesTemplate = addTemplate({
1046
+ filename: "better-auth/route-rules.mjs",
1047
+ getContents: () => `export const authRouteRules = ${JSON.stringify(authRouteRules, null, 2)}
1048
+ `,
1049
+ write: true
365
1050
  });
1051
+ nuxt.options.alias["#auth/route-rules"] = authRouteRulesTemplate.dst;
1052
+ registerTemplateHmrHook(nuxt);
1053
+ registerServerRuntime({ clientOnly, resolve: resolver.resolve });
1054
+ registerAuthMiddlewareHook(nuxt, resolver.resolve);
1055
+ await registerDevtools({ nuxt, clientOnly, hasHubDb, resolve: resolver.resolve });
1056
+ registerRouteRulesMetaHook(nuxt);
366
1057
  }
367
1058
  });
368
- async function setupBetterAuthSchema(nuxt, serverConfigPath, options) {
369
- const hub = nuxt.options.hub;
370
- const dialect = typeof hub?.db === "string" ? hub.db : typeof hub?.db === "object" ? hub.db.dialect : void 0;
371
- if (!dialect || !["sqlite", "postgresql", "mysql"].includes(dialect)) {
372
- consola.warn(`Unsupported database dialect: ${dialect}`);
373
- return;
374
- }
375
- const isProduction = !nuxt.options.dev;
376
- try {
377
- const configFile = `${serverConfigPath}.ts`;
378
- const userConfig = await loadUserAuthConfig(configFile, isProduction);
379
- const extendedConfig = {};
380
- await nuxt.callHook("better-auth:config:extend", extendedConfig);
381
- const plugins = [...userConfig.plugins || [], ...extendedConfig.plugins || []];
382
- const { getAuthTables } = await import('better-auth/db');
383
- const tables = getAuthTables({ plugins });
384
- const useUuid = userConfig.advanced?.database?.generateId === "uuid";
385
- const schemaOptions = { ...options.schema, useUuid };
386
- const schemaCode = generateDrizzleSchema(tables, dialect, schemaOptions);
387
- const schemaDir = join(nuxt.options.buildDir, "better-auth");
388
- const schemaPath = join(schemaDir, `schema.${dialect}.ts`);
389
- await mkdir(schemaDir, { recursive: true });
390
- await writeFile(schemaPath, schemaCode);
391
- addTemplate({ filename: `better-auth/schema.${dialect}.ts`, getContents: () => schemaCode, write: true });
392
- consola.info(`Generated ${dialect} schema with ${Object.keys(tables).length} tables`);
393
- } catch (error) {
394
- if (isProduction) {
395
- throw error;
396
- }
397
- consola.error("Failed to generate schema:", error);
398
- }
399
- const nuxtWithHubHooks = nuxt;
400
- nuxtWithHubHooks.hook("hub:db:schema:extend", ({ paths, dialect: hookDialect }) => {
401
- const schemaPath = join(nuxt.options.buildDir, "better-auth", `schema.${hookDialect}.ts`);
402
- if (existsSync(schemaPath)) {
403
- paths.unshift(schemaPath);
404
- }
405
- });
406
- }
407
1059
 
408
1060
  export { module$1 as default };