@onmax/nuxt-better-auth 0.0.2-alpha.3 → 0.0.2-alpha.30
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/module.d.mts +37 -1
- package/dist/module.json +2 -2
- package/dist/module.mjs +883 -313
- package/dist/runtime/app/components/BetterAuthState.vue +1 -0
- package/dist/runtime/app/composables/useAction.d.ts +2 -0
- package/dist/runtime/app/composables/useAction.js +6 -0
- package/dist/runtime/app/composables/useAuthAsyncData.d.ts +6 -0
- package/dist/runtime/app/composables/useAuthAsyncData.js +16 -0
- package/dist/runtime/app/composables/useAuthClientAction.d.ts +5 -0
- package/dist/runtime/app/composables/useAuthClientAction.js +15 -0
- package/dist/runtime/app/composables/useAuthRequestFetch.d.ts +11 -0
- package/dist/runtime/app/composables/useAuthRequestFetch.js +4 -0
- package/dist/runtime/app/composables/useSignIn.d.ts +16 -0
- package/dist/runtime/app/composables/useSignIn.js +8 -0
- package/dist/runtime/app/composables/useSignUp.d.ts +5 -0
- package/dist/runtime/app/composables/useSignUp.js +8 -0
- package/dist/runtime/app/composables/useUserSession.d.ts +14 -12
- package/dist/runtime/app/composables/useUserSession.js +237 -22
- package/dist/runtime/app/internal/auth-action-error.d.ts +3 -0
- package/dist/runtime/app/internal/auth-action-error.js +35 -0
- package/dist/runtime/app/internal/auth-action-handles.d.ts +18 -0
- package/dist/runtime/app/internal/auth-action-handles.js +107 -0
- package/dist/runtime/app/middleware/auth.global.js +73 -12
- package/dist/runtime/app/pages/__better-auth-devtools.vue +4 -10
- package/dist/runtime/app/plugins/session.client.js +1 -2
- package/dist/runtime/config.d.ts +64 -9
- package/dist/runtime/config.js +7 -2
- package/dist/runtime/server/api/_better-auth/accounts.get.js +3 -2
- package/dist/runtime/server/api/_better-auth/config.get.d.ts +17 -11
- package/dist/runtime/server/api/_better-auth/config.get.js +17 -5
- package/dist/runtime/server/api/_better-auth/sessions.delete.js +3 -2
- package/dist/runtime/server/api/_better-auth/sessions.get.d.ts +3 -1
- package/dist/runtime/server/api/_better-auth/sessions.get.js +3 -2
- package/dist/runtime/server/api/_better-auth/users.get.js +3 -2
- package/dist/runtime/server/api/auth/[...all].js +1 -1
- package/dist/runtime/server/middleware/route-access.js +1 -0
- package/dist/runtime/server/tsconfig.json +9 -1
- package/dist/runtime/server/utils/auth.d.ts +5 -7
- package/dist/runtime/server/utils/auth.js +197 -17
- package/dist/runtime/server/utils/custom-secondary-storage.d.ts +6 -0
- package/dist/runtime/server/utils/custom-secondary-storage.js +8 -0
- package/dist/runtime/server/utils/session.d.ts +4 -8
- package/dist/runtime/server/utils/session.js +43 -4
- package/dist/runtime/server/virtual-modules.d.ts +22 -0
- package/dist/runtime/types/augment.d.ts +18 -2
- package/dist/runtime/types.d.ts +12 -13
- package/dist/types.d.mts +1 -1
- package/package.json +42 -43
package/dist/module.mjs
CHANGED
|
@@ -1,14 +1,30 @@
|
|
|
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 {
|
|
3
|
+
import { 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
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.30";
|
|
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
|
+
|
|
10
25
|
function setupDevTools(nuxt) {
|
|
11
|
-
|
|
26
|
+
const hookable = nuxt;
|
|
27
|
+
hookable.hook("devtools:customTabs", (tabs) => {
|
|
12
28
|
tabs.push({
|
|
13
29
|
category: "server",
|
|
14
30
|
name: "better-auth",
|
|
@@ -22,143 +38,200 @@ function setupDevTools(nuxt) {
|
|
|
22
38
|
});
|
|
23
39
|
}
|
|
24
40
|
|
|
25
|
-
function
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
41
|
+
function registerTemplateHmrHook(nuxt) {
|
|
42
|
+
nuxt.hook("builder:watch", async (_event, relativePath) => {
|
|
43
|
+
if (relativePath.includes("auth.config"))
|
|
44
|
+
await updateTemplates({ filter: (t) => t.filename.includes("nuxt-better-auth") });
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
function registerServerRuntime(input) {
|
|
48
|
+
const { clientOnly, resolve } = input;
|
|
49
|
+
if (!clientOnly) {
|
|
50
|
+
addServerImportsDir(resolve("./runtime/server/utils"));
|
|
51
|
+
addServerImports([{ name: "defineServerAuth", from: resolve("./runtime/config") }]);
|
|
52
|
+
addServerScanDir(resolve("./runtime/server/middleware"));
|
|
53
|
+
addServerHandler({ route: "/api/auth/**", handler: resolve("./runtime/server/api/auth/[...all]") });
|
|
54
|
+
}
|
|
55
|
+
addImportsDir(resolve("./runtime/app/composables"));
|
|
56
|
+
addImportsDir(resolve("./runtime/utils"));
|
|
57
|
+
if (!clientOnly)
|
|
58
|
+
addPlugin({ src: resolve("./runtime/app/plugins/session.server"), mode: "server" });
|
|
59
|
+
addPlugin({ src: resolve("./runtime/app/plugins/session.client"), mode: "client" });
|
|
60
|
+
addComponentsDir({ path: resolve("./runtime/app/components") });
|
|
61
|
+
}
|
|
62
|
+
function registerAuthMiddlewareHook(nuxt, resolve) {
|
|
63
|
+
nuxt.hook("app:resolve", (app) => {
|
|
64
|
+
app.middleware.push({ name: "auth", path: resolve("./runtime/app/middleware/auth.global"), global: true });
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
async function registerDevtools(input) {
|
|
68
|
+
const { nuxt, clientOnly, hasHubDb, resolve } = input;
|
|
69
|
+
const isProduction = process.env.NODE_ENV === "production" || !nuxt.options.dev;
|
|
70
|
+
if (isProduction || clientOnly)
|
|
71
|
+
return;
|
|
72
|
+
if (!hasNuxtModule("@nuxt/ui"))
|
|
73
|
+
await installModule("@nuxt/ui");
|
|
74
|
+
setupDevTools(nuxt);
|
|
75
|
+
addServerHandler({ route: "/api/_better-auth/config", method: "get", handler: resolve("./runtime/server/api/_better-auth/config.get") });
|
|
76
|
+
if (hasHubDb) {
|
|
77
|
+
const handlers = [
|
|
78
|
+
{ route: "/api/_better-auth/sessions", method: "get", handler: resolve("./runtime/server/api/_better-auth/sessions.get") },
|
|
79
|
+
{ route: "/api/_better-auth/sessions", method: "delete", handler: resolve("./runtime/server/api/_better-auth/sessions.delete") },
|
|
80
|
+
{ route: "/api/_better-auth/users", method: "get", handler: resolve("./runtime/server/api/_better-auth/users.get") },
|
|
81
|
+
{ route: "/api/_better-auth/accounts", method: "get", handler: resolve("./runtime/server/api/_better-auth/accounts.get") }
|
|
82
|
+
];
|
|
83
|
+
handlers.forEach((handler) => addServerHandler(handler));
|
|
84
|
+
}
|
|
85
|
+
extendPages((pages) => {
|
|
86
|
+
pages.push({ name: "better-auth-devtools", path: "/__better-auth-devtools", file: resolve("./runtime/app/pages/__better-auth-devtools.vue") });
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
function registerRouteRulesMetaHook(nuxt) {
|
|
90
|
+
nuxt.hook("pages:extend", (pages) => {
|
|
91
|
+
const options = nuxt.options;
|
|
92
|
+
const routeRules = options.nitro?.routeRules || options.routeRules || {};
|
|
93
|
+
if (!Object.keys(routeRules).length)
|
|
94
|
+
return;
|
|
95
|
+
const matcher = toRouteMatcher(createRouter({ routes: routeRules }));
|
|
96
|
+
const applyMetaFromRules = (page) => {
|
|
97
|
+
const matches = matcher.matchAll(page.path);
|
|
98
|
+
if (!matches.length)
|
|
99
|
+
return;
|
|
100
|
+
const matchedRules = defu({}, ...matches.reverse());
|
|
101
|
+
if (matchedRules.auth !== void 0) {
|
|
102
|
+
page.meta = page.meta || {};
|
|
103
|
+
page.meta.auth = matchedRules.auth;
|
|
104
|
+
}
|
|
105
|
+
page.children?.forEach((child) => applyMetaFromRules(child));
|
|
106
|
+
};
|
|
107
|
+
pages.forEach((page) => applyMetaFromRules(page));
|
|
108
|
+
});
|
|
109
|
+
}
|
|
30
110
|
|
|
31
|
-
|
|
32
|
-
|
|
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;
|
|
33
124
|
}
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
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}')`;
|
|
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.");
|
|
151
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
|
+
function dialectToProvider(dialect) {
|
|
185
|
+
return dialect === "postgresql" ? "pg" : dialect;
|
|
186
|
+
}
|
|
187
|
+
async function generateDrizzleSchema(authOptions, dialect, schemaOptions) {
|
|
188
|
+
const provider = dialectToProvider(dialect);
|
|
189
|
+
const options = {
|
|
190
|
+
...authOptions,
|
|
191
|
+
advanced: {
|
|
192
|
+
...authOptions.advanced,
|
|
193
|
+
database: {
|
|
194
|
+
...authOptions.advanced?.database,
|
|
195
|
+
...schemaOptions?.useUuid && { generateId: "uuid" }
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
};
|
|
199
|
+
const adapter = {
|
|
200
|
+
id: "drizzle",
|
|
201
|
+
options: {
|
|
202
|
+
provider,
|
|
203
|
+
camelCase: schemaOptions?.casing !== "snake_case",
|
|
204
|
+
adapterConfig: { usePlural: schemaOptions?.usePlural ?? false }
|
|
205
|
+
}
|
|
206
|
+
};
|
|
207
|
+
const result = await generateDrizzleSchema$1({
|
|
208
|
+
adapter,
|
|
209
|
+
options
|
|
210
|
+
});
|
|
211
|
+
if (!result.code) {
|
|
212
|
+
throw new Error(`Schema generation returned empty result for ${dialect}`);
|
|
213
|
+
}
|
|
214
|
+
return result.code;
|
|
152
215
|
}
|
|
153
216
|
async function loadUserAuthConfig(configPath, throwOnError = false) {
|
|
154
217
|
const { createJiti } = await import('jiti');
|
|
155
|
-
const
|
|
218
|
+
const { defineServerAuth: runtimeDefineServerAuth } = await import('../dist/runtime/config.js');
|
|
219
|
+
const jiti = createJiti(import.meta.url, { interopDefault: true, moduleCache: false });
|
|
220
|
+
if (!globalThis.__nuxtBetterAuthDefineServerAuth) {
|
|
221
|
+
runtimeDefineServerAuth._count = 0;
|
|
222
|
+
globalThis.__nuxtBetterAuthDefineServerAuth = runtimeDefineServerAuth;
|
|
223
|
+
}
|
|
224
|
+
if (!globalThis.defineServerAuth) {
|
|
225
|
+
globalThis.defineServerAuth = globalThis.__nuxtBetterAuthDefineServerAuth;
|
|
226
|
+
}
|
|
227
|
+
globalThis.__nuxtBetterAuthDefineServerAuth._count++;
|
|
156
228
|
try {
|
|
157
229
|
const mod = await jiti.import(configPath);
|
|
158
|
-
const configFn =
|
|
230
|
+
const configFn = mod.default;
|
|
159
231
|
if (typeof configFn === "function") {
|
|
160
232
|
return configFn({ runtimeConfig: {}, db: null });
|
|
161
233
|
}
|
|
234
|
+
consola$1.warn("[@onmax/nuxt-better-auth] auth.config.ts does not export default. Expected: export default defineServerAuth(...)");
|
|
162
235
|
if (throwOnError) {
|
|
163
236
|
throw new Error("auth.config.ts must export default defineServerAuth(...)");
|
|
164
237
|
}
|
|
@@ -169,84 +242,216 @@ async function loadUserAuthConfig(configPath, throwOnError = false) {
|
|
|
169
242
|
}
|
|
170
243
|
consola$1.error("[@onmax/nuxt-better-auth] Failed to load auth config for schema generation. Schema may be incomplete:", error);
|
|
171
244
|
return {};
|
|
245
|
+
} finally {
|
|
246
|
+
const sharedDefineServerAuth = globalThis.__nuxtBetterAuthDefineServerAuth;
|
|
247
|
+
if (sharedDefineServerAuth) {
|
|
248
|
+
sharedDefineServerAuth._count--;
|
|
249
|
+
if (!sharedDefineServerAuth._count) {
|
|
250
|
+
globalThis.__nuxtBetterAuthDefineServerAuth = void 0;
|
|
251
|
+
if (globalThis.defineServerAuth === sharedDefineServerAuth) {
|
|
252
|
+
globalThis.defineServerAuth = void 0;
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
}
|
|
172
256
|
}
|
|
173
257
|
}
|
|
174
258
|
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
259
|
+
function resolveSchemaSecondaryStorageInjection(hubSecondaryStorage, userHasSecondaryStorage, isProduction) {
|
|
260
|
+
if (hubSecondaryStorage === true)
|
|
261
|
+
return { inject: false };
|
|
262
|
+
if (hubSecondaryStorage !== "custom")
|
|
263
|
+
return { inject: false };
|
|
264
|
+
if (userHasSecondaryStorage)
|
|
265
|
+
return { inject: true };
|
|
266
|
+
const message = '[nuxt-better-auth] hubSecondaryStorage: "custom" requires secondaryStorage in defineServerAuth() to omit the session table from the generated schema.';
|
|
267
|
+
if (isProduction)
|
|
268
|
+
return { inject: false, error: message };
|
|
269
|
+
return { inject: false, warn: message };
|
|
270
|
+
}
|
|
271
|
+
function isInsideNodeModules(path) {
|
|
272
|
+
return path.split(/[\\/]/).includes("node_modules");
|
|
273
|
+
}
|
|
274
|
+
function resolveHubSchemaPath(buildDir, rootDir, dialect, exists = existsSync) {
|
|
275
|
+
const rootTsPath = join(rootDir, ".nuxt", "better-auth", `schema.${dialect}.ts`);
|
|
276
|
+
if (isInsideNodeModules(buildDir) && exists(rootTsPath))
|
|
277
|
+
return rootTsPath;
|
|
278
|
+
const tsPath = join(buildDir, "better-auth", `schema.${dialect}.ts`);
|
|
279
|
+
if (exists(tsPath))
|
|
280
|
+
return tsPath;
|
|
281
|
+
const mjsPath = join(buildDir, "better-auth", `schema.${dialect}.mjs`);
|
|
282
|
+
if (exists(mjsPath))
|
|
283
|
+
return mjsPath;
|
|
284
|
+
return null;
|
|
285
|
+
}
|
|
286
|
+
async function loadAuthOptions(context) {
|
|
287
|
+
const isProduction = !context.nuxt.options.dev;
|
|
288
|
+
const configFile = `${context.serverConfigPath}.ts`;
|
|
289
|
+
const userConfig = await loadUserAuthConfig(configFile, isProduction);
|
|
290
|
+
const extendedConfig = {};
|
|
291
|
+
await context.nuxt.callHook("better-auth:config:extend", extendedConfig);
|
|
292
|
+
const plugins = [...userConfig.plugins || [], ...extendedConfig.plugins || []];
|
|
293
|
+
return { userConfig, plugins };
|
|
294
|
+
}
|
|
295
|
+
async function setupBetterAuthSchema(nuxt, serverConfigPath, options, consola, hubSecondaryStorage) {
|
|
296
|
+
const hub = nuxt.options.hub;
|
|
297
|
+
const dialect = getHubDialect(hub);
|
|
298
|
+
if (!dialect || !["sqlite", "postgresql", "mysql"].includes(dialect)) {
|
|
299
|
+
consola.warn(`Unsupported database dialect: ${dialect}`);
|
|
300
|
+
return;
|
|
301
|
+
}
|
|
302
|
+
const context = { nuxt, serverConfigPath };
|
|
303
|
+
try {
|
|
304
|
+
const { userConfig, plugins } = await loadAuthOptions(context);
|
|
305
|
+
const userHasSecondaryStorage = userConfig.secondaryStorage != null;
|
|
306
|
+
const secondaryStorageResolution = resolveSchemaSecondaryStorageInjection(hubSecondaryStorage, userHasSecondaryStorage, !nuxt.options.dev);
|
|
307
|
+
if (secondaryStorageResolution.error)
|
|
308
|
+
throw new Error(secondaryStorageResolution.error);
|
|
309
|
+
if (secondaryStorageResolution.warn)
|
|
310
|
+
consola.warn(secondaryStorageResolution.warn);
|
|
311
|
+
const authOptions = {
|
|
312
|
+
...userConfig,
|
|
313
|
+
plugins,
|
|
314
|
+
secondaryStorage: secondaryStorageResolution.inject ? { get: async (_key) => null, set: async (_key, _value, _ttl) => {
|
|
315
|
+
}, delete: async (_key) => {
|
|
316
|
+
} } : void 0
|
|
317
|
+
};
|
|
318
|
+
const hubCasing = getHubCasing(hub);
|
|
319
|
+
const schemaOptions = { ...options.schema, useUuid: userConfig.advanced?.database?.generateId === "uuid", casing: options.schema?.casing ?? hubCasing };
|
|
320
|
+
const schemaCode = await generateDrizzleSchema(authOptions, dialect, schemaOptions);
|
|
321
|
+
const schemaDir = join(nuxt.options.buildDir, "better-auth");
|
|
322
|
+
const schemaPathTs = join(schemaDir, `schema.${dialect}.ts`);
|
|
323
|
+
const schemaPathMjs = join(schemaDir, `schema.${dialect}.mjs`);
|
|
324
|
+
await mkdir(schemaDir, { recursive: true });
|
|
325
|
+
await writeFile(schemaPathTs, schemaCode);
|
|
326
|
+
await writeFile(schemaPathMjs, schemaCode);
|
|
327
|
+
if (isInsideNodeModules(nuxt.options.buildDir)) {
|
|
328
|
+
const rootSchemaDir = join(nuxt.options.rootDir, ".nuxt", "better-auth");
|
|
329
|
+
const rootSchemaPathTs = join(rootSchemaDir, `schema.${dialect}.ts`);
|
|
330
|
+
await mkdir(rootSchemaDir, { recursive: true });
|
|
331
|
+
await writeFile(rootSchemaPathTs, schemaCode);
|
|
203
332
|
}
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
333
|
+
addTemplate({ filename: `better-auth/schema.${dialect}.ts`, getContents: () => schemaCode, write: true });
|
|
334
|
+
addTemplate({ filename: `better-auth/schema.${dialect}.mjs`, getContents: () => schemaCode, write: true });
|
|
335
|
+
consola.info(`Generated ${dialect} schema (.ts + .mjs)`);
|
|
336
|
+
const nuxtWithHubHooks = nuxt;
|
|
337
|
+
nuxtWithHubHooks.hook("hub:db:schema:extend", ({ paths, dialect: hookDialect }) => {
|
|
338
|
+
const schemaPath = resolveHubSchemaPath(nuxt.options.buildDir, nuxt.options.rootDir, hookDialect);
|
|
339
|
+
if (schemaPath)
|
|
340
|
+
paths.unshift(schemaPath);
|
|
208
341
|
});
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
342
|
+
} catch (error) {
|
|
343
|
+
const isProduction = !nuxt.options.dev;
|
|
344
|
+
if (isProduction)
|
|
345
|
+
throw error;
|
|
346
|
+
consola.error("Failed to generate schema:", error);
|
|
347
|
+
throw error;
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
const generateSecret = () => randomBytes(32).toString("hex");
|
|
352
|
+
function readEnvFile(rootDir) {
|
|
353
|
+
const envPath = join(rootDir, ".env");
|
|
354
|
+
return existsSync(envPath) ? readFileSync(envPath, "utf-8") : "";
|
|
355
|
+
}
|
|
356
|
+
function hasEnvSecret(rootDir) {
|
|
357
|
+
const match = readEnvFile(rootDir).match(/^BETTER_AUTH_SECRET=(.+)$/m);
|
|
358
|
+
return !!match && !!match[1] && match[1].trim().length > 0;
|
|
359
|
+
}
|
|
360
|
+
function appendSecretToEnv(rootDir, secret) {
|
|
361
|
+
const envPath = join(rootDir, ".env");
|
|
362
|
+
let content = readEnvFile(rootDir);
|
|
363
|
+
if (content.length > 0 && !content.endsWith("\n"))
|
|
364
|
+
content += "\n";
|
|
365
|
+
content += `BETTER_AUTH_SECRET=${secret}
|
|
366
|
+
`;
|
|
367
|
+
writeFileSync(envPath, content, "utf-8");
|
|
368
|
+
}
|
|
369
|
+
async function promptForSecret(rootDir, consola, options = {}) {
|
|
370
|
+
const configuredSecret = options.configuredSecret?.trim();
|
|
371
|
+
if (configuredSecret)
|
|
372
|
+
return void 0;
|
|
373
|
+
if (process.env.BETTER_AUTH_SECRET || hasEnvSecret(rootDir))
|
|
374
|
+
return void 0;
|
|
375
|
+
const hasTty = Boolean(process.stdin.isTTY && process.stdout.isTTY);
|
|
376
|
+
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.");
|
|
378
|
+
return void 0;
|
|
379
|
+
}
|
|
380
|
+
if (isCI || isTest) {
|
|
381
|
+
const secret2 = generateSecret();
|
|
382
|
+
appendSecretToEnv(rootDir, secret2);
|
|
383
|
+
consola.info("Generated BETTER_AUTH_SECRET and added to .env (CI/test mode)");
|
|
384
|
+
return secret2;
|
|
385
|
+
}
|
|
386
|
+
consola.box("BETTER_AUTH_SECRET is required for authentication.\nThis will be appended to your .env file.");
|
|
387
|
+
const choice = await consola.prompt("How do you want to set it?", {
|
|
388
|
+
type: "select",
|
|
389
|
+
options: [
|
|
390
|
+
{ label: "Generate for me", value: "generate", hint: "uses crypto.randomBytes(32)" },
|
|
391
|
+
{ label: "Enter manually", value: "paste" },
|
|
392
|
+
{ label: "Skip", value: "skip", hint: "will fail in production" }
|
|
393
|
+
],
|
|
394
|
+
cancel: "null"
|
|
395
|
+
});
|
|
396
|
+
if (typeof choice === "symbol" || choice === "skip") {
|
|
397
|
+
consola.warn("Skipping BETTER_AUTH_SECRET. Auth will fail without it in production.");
|
|
398
|
+
return void 0;
|
|
399
|
+
}
|
|
400
|
+
let secret;
|
|
401
|
+
if (choice === "generate") {
|
|
402
|
+
secret = generateSecret();
|
|
403
|
+
} else {
|
|
404
|
+
const input = await consola.prompt("Paste your secret (min 32 chars):", { type: "text", cancel: "null" });
|
|
405
|
+
if (typeof input === "symbol" || !input || input.length < 32) {
|
|
406
|
+
consola.warn("Invalid secret. Skipping.");
|
|
407
|
+
return void 0;
|
|
215
408
|
}
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
409
|
+
secret = input;
|
|
410
|
+
}
|
|
411
|
+
const preview = `${secret.slice(0, 8)}...${secret.slice(-4)}`;
|
|
412
|
+
const confirm = await consola.prompt(`Add to .env:
|
|
413
|
+
BETTER_AUTH_SECRET=${preview}
|
|
414
|
+
Proceed?`, { type: "confirm", initial: true, cancel: "null" });
|
|
415
|
+
if (typeof confirm === "symbol" || !confirm) {
|
|
416
|
+
consola.info("Cancelled. Secret not written.");
|
|
417
|
+
return void 0;
|
|
418
|
+
}
|
|
419
|
+
appendSecretToEnv(rootDir, secret);
|
|
420
|
+
consola.success("Added BETTER_AUTH_SECRET to .env");
|
|
421
|
+
return secret;
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
function buildSecondaryStorageCode(useHubKV) {
|
|
425
|
+
if (!useHubKV)
|
|
426
|
+
return "export function createSecondaryStorage() { return undefined }";
|
|
427
|
+
return `import { kv } from '@nuxthub/kv'
|
|
224
428
|
export function createSecondaryStorage() {
|
|
225
429
|
return {
|
|
226
430
|
get: async (key) => kv.get(\`_auth:\${key}\`),
|
|
227
431
|
set: async (key, value, ttl) => kv.set(\`_auth:\${key}\`, value, { ttl }),
|
|
228
432
|
delete: async (key) => kv.del(\`_auth:\${key}\`),
|
|
229
433
|
}
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
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}'
|
|
434
|
+
}`;
|
|
435
|
+
}
|
|
436
|
+
function buildDatabaseCode(input) {
|
|
437
|
+
if (input.provider === "nuxthub") {
|
|
438
|
+
return `import { db } from '@nuxthub/db'
|
|
439
|
+
import * as schema from './schema.${input.hubDialect}.mjs'
|
|
239
440
|
import { drizzleAdapter } from 'better-auth/adapters/drizzle'
|
|
240
|
-
const rawDialect = '${hubDialect}'
|
|
441
|
+
const rawDialect = '${input.hubDialect}'
|
|
241
442
|
const dialect = rawDialect === 'postgresql' ? 'pg' : rawDialect
|
|
242
|
-
export function createDatabase() { return drizzleAdapter(db, { provider: dialect, schema }) }
|
|
243
|
-
export { db }
|
|
443
|
+
export function createDatabase() { return drizzleAdapter(db, { provider: dialect, schema, usePlural: ${input.usePlural}, camelCase: ${input.camelCase} }) }
|
|
444
|
+
export { db }`;
|
|
445
|
+
}
|
|
446
|
+
return `export function createDatabase() { return undefined }
|
|
244
447
|
export const db = undefined`;
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
function registerServerTypeTemplates(input) {
|
|
451
|
+
const { serverConfigPath, hasHubDb, runtimeTypesPath } = input;
|
|
452
|
+
addTypeTemplate({
|
|
453
|
+
filename: "types/auth-secondary-storage.d.ts",
|
|
454
|
+
getContents: () => `
|
|
250
455
|
declare module '#auth/secondary-storage' {
|
|
251
456
|
interface SecondaryStorage {
|
|
252
457
|
get: (key: string) => Promise<string | null>
|
|
@@ -256,153 +461,518 @@ declare module '#auth/secondary-storage' {
|
|
|
256
461
|
export function createSecondaryStorage(): SecondaryStorage | undefined
|
|
257
462
|
}
|
|
258
463
|
`
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
464
|
+
}, { nitro: true, node: true });
|
|
465
|
+
addTypeTemplate({
|
|
466
|
+
filename: "types/auth-database.d.ts",
|
|
467
|
+
getContents: () => `
|
|
263
468
|
declare module '#auth/database' {
|
|
264
|
-
import type {
|
|
265
|
-
export function createDatabase():
|
|
266
|
-
export const db:
|
|
469
|
+
import type { BetterAuthOptions } from 'better-auth'
|
|
470
|
+
export function createDatabase(): BetterAuthOptions['database']
|
|
471
|
+
export const db: ${hasHubDb ? `typeof import('@nuxthub/db')['db']` : "undefined"}
|
|
267
472
|
}
|
|
268
473
|
`
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
export
|
|
474
|
+
}, { nitro: true, node: true });
|
|
475
|
+
addTypeTemplate({
|
|
476
|
+
filename: "types/auth-schema.d.ts",
|
|
477
|
+
getContents: () => `
|
|
478
|
+
declare module '#auth/schema' {
|
|
479
|
+
export const user: any
|
|
480
|
+
export const session: any
|
|
481
|
+
export const account: any
|
|
482
|
+
export const verification: any
|
|
483
|
+
export const schema: {
|
|
484
|
+
user: any
|
|
485
|
+
session: any
|
|
486
|
+
account: any
|
|
487
|
+
verification: any
|
|
488
|
+
[key: string]: any
|
|
489
|
+
} | undefined
|
|
490
|
+
}
|
|
275
491
|
`
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
import type {
|
|
492
|
+
}, { nitro: true, node: true });
|
|
493
|
+
addTypeTemplate({
|
|
494
|
+
filename: "types/nuxt-better-auth-infer.d.ts",
|
|
495
|
+
getContents: () => `
|
|
496
|
+
import type { BetterAuthOptions, BetterAuthPlugin, InferPluginTypes, UnionToIntersection } from 'better-auth'
|
|
497
|
+
import type { InferFieldsOutput } from 'better-auth/db'
|
|
281
498
|
import type { RuntimeConfig } from 'nuxt/schema'
|
|
282
|
-
import type
|
|
499
|
+
import type createServerAuth from '${serverConfigPath}'
|
|
283
500
|
|
|
284
|
-
type
|
|
501
|
+
type _RawConfig = ReturnType<typeof createServerAuth>
|
|
502
|
+
type _RawPlugins = _RawConfig extends { plugins: infer P } ? P : _RawConfig extends { plugins?: infer P } ? P : []
|
|
503
|
+
type _NormalizedPlugins = _RawPlugins extends readonly (infer T)[]
|
|
504
|
+
? Array<T & BetterAuthPlugin>
|
|
505
|
+
: _RawPlugins extends (infer T)[]
|
|
506
|
+
? Array<T & BetterAuthPlugin>
|
|
507
|
+
: BetterAuthPlugin[]
|
|
508
|
+
type _Config = Omit<BetterAuthOptions, 'plugins'> & Omit<_RawConfig, 'plugins'> & {
|
|
509
|
+
plugins?: _NormalizedPlugins
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
type _InferModelFieldsFromPlugins<P, M extends string> = P extends readonly (infer Plugin)[]
|
|
513
|
+
? UnionToIntersection<Plugin extends { schema: { [K in M]: { fields: infer F } } } ? InferFieldsOutput<F> : {}>
|
|
514
|
+
: P extends (infer Plugin)[]
|
|
515
|
+
? UnionToIntersection<Plugin extends { schema: { [K in M]: { fields: infer F } } } ? InferFieldsOutput<F> : {}>
|
|
516
|
+
: {}
|
|
517
|
+
|
|
518
|
+
type _InferModelFieldsFromOptions<C, M extends 'user' | 'session'> = C extends { [K in M]: { additionalFields: infer F } }
|
|
519
|
+
? InferFieldsOutput<F>
|
|
520
|
+
: {}
|
|
521
|
+
|
|
522
|
+
type _UserFallback = _InferModelFieldsFromPlugins<_RawPlugins, 'user'> & _InferModelFieldsFromOptions<_RawConfig, 'user'>
|
|
523
|
+
type _SessionFallback = _InferModelFieldsFromPlugins<_RawPlugins, 'session'> & _InferModelFieldsFromOptions<_RawConfig, 'session'>
|
|
285
524
|
|
|
286
525
|
declare module '#nuxt-better-auth' {
|
|
287
|
-
interface AuthUser extends
|
|
288
|
-
interface AuthSession
|
|
526
|
+
interface AuthUser extends _UserFallback {}
|
|
527
|
+
interface AuthSession extends _SessionFallback {}
|
|
289
528
|
interface ServerAuthContext {
|
|
290
529
|
runtimeConfig: RuntimeConfig
|
|
291
|
-
${hasHubDb ? `
|
|
530
|
+
db: ${hasHubDb ? `typeof import('@nuxthub/db')['db']` : "undefined"}
|
|
292
531
|
}
|
|
532
|
+
type PluginTypes = InferPluginTypes<_Config>
|
|
533
|
+
}
|
|
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
|
|
293
547
|
}
|
|
294
548
|
`
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
import type
|
|
549
|
+
}, { nuxt: true, nitro: true, node: true });
|
|
550
|
+
addTypeTemplate({
|
|
551
|
+
filename: "types/nuxt-better-auth-social-providers.d.ts",
|
|
552
|
+
getContents: () => `
|
|
553
|
+
import type createServerAuth from '${serverConfigPath}'
|
|
554
|
+
|
|
555
|
+
type _RawConfig = ReturnType<typeof createServerAuth>
|
|
556
|
+
type _RawSocialProviders = _RawConfig extends { socialProviders: infer S } ? S : _RawConfig extends { socialProviders?: infer S } ? S : {}
|
|
557
|
+
type _SocialProviderIds = Extract<keyof NonNullable<_RawSocialProviders>, string>
|
|
558
|
+
|
|
300
559
|
declare module '#nuxt-better-auth' {
|
|
301
|
-
|
|
560
|
+
interface AuthSocialProviderRegistry {
|
|
561
|
+
ids: _SocialProviderIds
|
|
562
|
+
}
|
|
302
563
|
}
|
|
303
564
|
`
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
565
|
+
}, { nuxt: true, nitro: true, node: true });
|
|
566
|
+
addTypeTemplate({
|
|
567
|
+
filename: "types/nuxt-better-auth-nitro.d.ts",
|
|
568
|
+
getContents: () => `
|
|
569
|
+
import type createServerAuth from '${serverConfigPath}'
|
|
570
|
+
import type { BetterAuthOptions } from 'better-auth'
|
|
571
|
+
import type { getEndpoints } from 'better-auth/api'
|
|
572
|
+
import type { Serialize, Simplify } from 'nitropack/types'
|
|
573
|
+
import type { FetchError } from 'ofetch'
|
|
574
|
+
|
|
575
|
+
type _RawConfig = ReturnType<typeof createServerAuth>
|
|
576
|
+
type _RawPlugins = _RawConfig extends { plugins: infer P } ? P : _RawConfig extends { plugins?: infer P } ? P : []
|
|
577
|
+
type _Config = Omit<BetterAuthOptions, 'plugins'> & Omit<_RawConfig, 'plugins'> & {
|
|
578
|
+
plugins?: _RawPlugins
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
type _AuthApi = ReturnType<typeof getEndpoints<_Config>>['api']
|
|
582
|
+
type _NormalizeMethod<M extends string> = M extends '*' ? 'default' : Lowercase<M>
|
|
583
|
+
type _RouteMethodFromOption<M> = M extends readonly (infer T)[]
|
|
584
|
+
? _NormalizeMethod<Extract<T, string>>
|
|
585
|
+
: M extends string
|
|
586
|
+
? _NormalizeMethod<M>
|
|
587
|
+
: 'default'
|
|
588
|
+
type _RouteMethodFromEndpoint<E> = E extends { options: { method: infer M } } ? _RouteMethodFromOption<M> : 'default'
|
|
589
|
+
type _RoutePathFromEndpoint<E> = E extends { path: infer P extends string }
|
|
590
|
+
? string extends P
|
|
591
|
+
? never
|
|
592
|
+
: \`/api/auth\${P}\`
|
|
593
|
+
: never
|
|
594
|
+
type _RouteResponseFromEndpoint<E> = E extends (...args: any[]) => Promise<infer R> ? Simplify<Serialize<Awaited<R>>> : never
|
|
595
|
+
type _RouteDefaultResponse<E> = never
|
|
596
|
+
type _UnionToIntersection<U> = (U extends unknown ? (value: U) => void : never) extends (value: infer I) => void ? I : never
|
|
597
|
+
|
|
598
|
+
type _CoreAuthInternalApi = {
|
|
599
|
+
[K in keyof _AuthApi as _RoutePathFromEndpoint<_AuthApi[K]>]: {
|
|
600
|
+
[M in _RouteMethodFromEndpoint<_AuthApi[K]> | 'default']: M extends 'default' ? _RouteDefaultResponse<_AuthApi[K]> : _RouteResponseFromEndpoint<_AuthApi[K]>
|
|
601
|
+
}
|
|
602
|
+
}
|
|
603
|
+
type _PluginEndpointMaps<Plugins> = Plugins extends readonly (infer Plugin)[]
|
|
604
|
+
? Plugin extends { endpoints: infer Endpoints extends Record<string, unknown> }
|
|
605
|
+
? {
|
|
606
|
+
[K in keyof Endpoints as _RoutePathFromEndpoint<Endpoints[K]>]: {
|
|
607
|
+
[M in _RouteMethodFromEndpoint<Endpoints[K]> | 'default']: M extends 'default' ? _RouteDefaultResponse<Endpoints[K]> : _RouteResponseFromEndpoint<Endpoints[K]>
|
|
608
|
+
}
|
|
609
|
+
}
|
|
610
|
+
: {}
|
|
611
|
+
: Plugins extends (infer Plugin)[]
|
|
612
|
+
? Plugin extends { endpoints: infer Endpoints extends Record<string, unknown> }
|
|
613
|
+
? {
|
|
614
|
+
[K in keyof Endpoints as _RoutePathFromEndpoint<Endpoints[K]>]: {
|
|
615
|
+
[M in _RouteMethodFromEndpoint<Endpoints[K]> | 'default']: M extends 'default' ? _RouteDefaultResponse<Endpoints[K]> : _RouteResponseFromEndpoint<Endpoints[K]>
|
|
616
|
+
}
|
|
617
|
+
}
|
|
618
|
+
: {}
|
|
619
|
+
: {}
|
|
620
|
+
type _PluginAuthInternalApi = _UnionToIntersection<_PluginEndpointMaps<_RawPlugins>>
|
|
621
|
+
type _GeneratedAuthInternalApi = _CoreAuthInternalApi & _PluginAuthInternalApi
|
|
622
|
+
|
|
623
|
+
type _RoutePathToRequestPath<Path extends string> = Path extends \`\${infer Prefix}:\${string}/\${infer Rest}\`
|
|
624
|
+
? \`\${Prefix}\${string}/\${_RoutePathToRequestPath<Rest>}\`
|
|
625
|
+
: Path extends \`\${infer Prefix}:\${string}\`
|
|
626
|
+
? \`\${Prefix}\${string}\`
|
|
627
|
+
: Path
|
|
628
|
+
type _AuthApiPatternPath = Extract<keyof _GeneratedAuthInternalApi, string>
|
|
629
|
+
type _AuthApiRequestPath = _RoutePathToRequestPath<_AuthApiPatternPath>
|
|
630
|
+
type _AuthPatternFromRequestPath<Path extends string> = {
|
|
631
|
+
[Pattern in _AuthApiPatternPath]: Path extends _RoutePathToRequestPath<Pattern> ? Pattern : never
|
|
632
|
+
}[_AuthApiPatternPath]
|
|
633
|
+
type _AuthEndpointMethod<Path extends _AuthApiRequestPath> = Extract<keyof _GeneratedAuthInternalApi[_AuthPatternFromRequestPath<Path>], string>
|
|
634
|
+
|
|
635
|
+
type _AuthFetchMethod<Path extends _AuthApiRequestPath> = Extract<Exclude<import('nitropack/types').NitroFetchOptions<Path>['method'], undefined>, string>
|
|
636
|
+
type _AuthFetchDefaultMethod<Path extends _AuthApiRequestPath> = 'get'
|
|
637
|
+
type _AuthFetchResolvedMethod<Path extends _AuthApiRequestPath, Method extends string> = Lowercase<Method> extends _AuthEndpointMethod<Path>
|
|
638
|
+
? Lowercase<Method>
|
|
639
|
+
: never
|
|
640
|
+
type _AuthFetchResult<Path extends _AuthApiRequestPath, Method extends string> = _GeneratedAuthInternalApi[_AuthPatternFromRequestPath<Path>][_AuthFetchResolvedMethod<Path, Method>]
|
|
641
|
+
|
|
642
|
+
declare module '#nuxt-better-auth' {
|
|
643
|
+
export type AuthApiInternalRoutes = _GeneratedAuthInternalApi
|
|
644
|
+
export type AuthApiEndpointPatternPath = _AuthApiPatternPath
|
|
645
|
+
export type AuthApiEndpointPath = _AuthApiRequestPath
|
|
646
|
+
export type AuthApiEndpointMethod<Path extends AuthApiEndpointPath> = _AuthEndpointMethod<Path>
|
|
647
|
+
export type AuthApiEndpointResponse<
|
|
648
|
+
Path extends AuthApiEndpointPath,
|
|
649
|
+
Method extends AuthApiEndpointMethod<Path> = AuthApiEndpointMethod<Path>,
|
|
650
|
+
> = AuthApiInternalRoutes[_AuthPatternFromRequestPath<Path>][Method]
|
|
651
|
+
}
|
|
652
|
+
|
|
653
|
+
declare module 'nuxt/dist/app/composables/fetch' {
|
|
654
|
+
export function useFetch<
|
|
655
|
+
ErrorT = FetchError,
|
|
656
|
+
Path extends import('#nuxt-better-auth').AuthApiEndpointPath = import('#nuxt-better-auth').AuthApiEndpointPath,
|
|
657
|
+
Method extends _AuthFetchMethod<Path> = _AuthFetchDefaultMethod<Path>,
|
|
658
|
+
_ResT = _AuthFetchResult<Path, Method>,
|
|
659
|
+
DataT = _ResT,
|
|
660
|
+
PickKeys extends import('nuxt/dist/app/composables/asyncData').KeysOf<DataT> = import('nuxt/dist/app/composables/asyncData').KeysOf<DataT>,
|
|
661
|
+
DefaultT = undefined,
|
|
662
|
+
>(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>
|
|
663
|
+
export function useFetch<
|
|
664
|
+
ErrorT = FetchError,
|
|
665
|
+
Path extends import('#nuxt-better-auth').AuthApiEndpointPath = import('#nuxt-better-auth').AuthApiEndpointPath,
|
|
666
|
+
Method extends _AuthFetchMethod<Path> = _AuthFetchDefaultMethod<Path>,
|
|
667
|
+
_ResT = _AuthFetchResult<Path, Method>,
|
|
668
|
+
DataT = _ResT,
|
|
669
|
+
PickKeys extends import('nuxt/dist/app/composables/asyncData').KeysOf<DataT> = import('nuxt/dist/app/composables/asyncData').KeysOf<DataT>,
|
|
670
|
+
DefaultT = DataT,
|
|
671
|
+
>(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>
|
|
672
|
+
|
|
673
|
+
export function useLazyFetch<
|
|
674
|
+
ErrorT = FetchError,
|
|
675
|
+
Path extends import('#nuxt-better-auth').AuthApiEndpointPath = import('#nuxt-better-auth').AuthApiEndpointPath,
|
|
676
|
+
Method extends _AuthFetchMethod<Path> = _AuthFetchDefaultMethod<Path>,
|
|
677
|
+
_ResT = _AuthFetchResult<Path, Method>,
|
|
678
|
+
DataT = _ResT,
|
|
679
|
+
PickKeys extends import('nuxt/dist/app/composables/asyncData').KeysOf<DataT> = import('nuxt/dist/app/composables/asyncData').KeysOf<DataT>,
|
|
680
|
+
DefaultT = undefined,
|
|
681
|
+
>(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>
|
|
682
|
+
export function useLazyFetch<
|
|
683
|
+
ErrorT = FetchError,
|
|
684
|
+
Path extends import('#nuxt-better-auth').AuthApiEndpointPath = import('#nuxt-better-auth').AuthApiEndpointPath,
|
|
685
|
+
Method extends _AuthFetchMethod<Path> = _AuthFetchDefaultMethod<Path>,
|
|
686
|
+
_ResT = _AuthFetchResult<Path, Method>,
|
|
687
|
+
DataT = _ResT,
|
|
688
|
+
PickKeys extends import('nuxt/dist/app/composables/asyncData').KeysOf<DataT> = import('nuxt/dist/app/composables/asyncData').KeysOf<DataT>,
|
|
689
|
+
DefaultT = DataT,
|
|
690
|
+
>(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>
|
|
691
|
+
}
|
|
692
|
+
|
|
693
|
+
declare module 'nuxt/app' {
|
|
694
|
+
export function useFetch<
|
|
695
|
+
ErrorT = FetchError,
|
|
696
|
+
Path extends import('#nuxt-better-auth').AuthApiEndpointPath = import('#nuxt-better-auth').AuthApiEndpointPath,
|
|
697
|
+
Method extends _AuthFetchMethod<Path> = _AuthFetchDefaultMethod<Path>,
|
|
698
|
+
_ResT = _AuthFetchResult<Path, Method>,
|
|
699
|
+
DataT = _ResT,
|
|
700
|
+
PickKeys extends import('nuxt/dist/app/composables/asyncData').KeysOf<DataT> = import('nuxt/dist/app/composables/asyncData').KeysOf<DataT>,
|
|
701
|
+
DefaultT = undefined,
|
|
702
|
+
>(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>
|
|
703
|
+
export function useFetch<
|
|
704
|
+
ErrorT = FetchError,
|
|
705
|
+
Path extends import('#nuxt-better-auth').AuthApiEndpointPath = import('#nuxt-better-auth').AuthApiEndpointPath,
|
|
706
|
+
Method extends _AuthFetchMethod<Path> = _AuthFetchDefaultMethod<Path>,
|
|
707
|
+
_ResT = _AuthFetchResult<Path, Method>,
|
|
708
|
+
DataT = _ResT,
|
|
709
|
+
PickKeys extends import('nuxt/dist/app/composables/asyncData').KeysOf<DataT> = import('nuxt/dist/app/composables/asyncData').KeysOf<DataT>,
|
|
710
|
+
DefaultT = DataT,
|
|
711
|
+
>(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>
|
|
712
|
+
|
|
713
|
+
export function useLazyFetch<
|
|
714
|
+
ErrorT = FetchError,
|
|
715
|
+
Path extends import('#nuxt-better-auth').AuthApiEndpointPath = import('#nuxt-better-auth').AuthApiEndpointPath,
|
|
716
|
+
Method extends _AuthFetchMethod<Path> = _AuthFetchDefaultMethod<Path>,
|
|
717
|
+
_ResT = _AuthFetchResult<Path, Method>,
|
|
718
|
+
DataT = _ResT,
|
|
719
|
+
PickKeys extends import('nuxt/dist/app/composables/asyncData').KeysOf<DataT> = import('nuxt/dist/app/composables/asyncData').KeysOf<DataT>,
|
|
720
|
+
DefaultT = undefined,
|
|
721
|
+
>(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>
|
|
722
|
+
export function useLazyFetch<
|
|
723
|
+
ErrorT = FetchError,
|
|
724
|
+
Path extends import('#nuxt-better-auth').AuthApiEndpointPath = import('#nuxt-better-auth').AuthApiEndpointPath,
|
|
725
|
+
Method extends _AuthFetchMethod<Path> = _AuthFetchDefaultMethod<Path>,
|
|
726
|
+
_ResT = _AuthFetchResult<Path, Method>,
|
|
727
|
+
DataT = _ResT,
|
|
728
|
+
PickKeys extends import('nuxt/dist/app/composables/asyncData').KeysOf<DataT> = import('nuxt/dist/app/composables/asyncData').KeysOf<DataT>,
|
|
729
|
+
DefaultT = DataT,
|
|
730
|
+
>(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>
|
|
731
|
+
}
|
|
732
|
+
|
|
733
|
+
declare module 'nitropack' {
|
|
734
|
+
interface NitroRouteRules {
|
|
735
|
+
auth?: import('${runtimeTypesPath}').AuthMeta
|
|
736
|
+
}
|
|
737
|
+
interface NitroRouteConfig {
|
|
738
|
+
auth?: import('${runtimeTypesPath}').AuthMeta
|
|
739
|
+
}
|
|
740
|
+
interface InternalApi extends _GeneratedAuthInternalApi {}
|
|
741
|
+
}
|
|
308
742
|
declare module 'nitropack/types' {
|
|
309
743
|
interface NitroRouteRules {
|
|
310
|
-
auth?: import('${
|
|
744
|
+
auth?: import('${runtimeTypesPath}').AuthMeta
|
|
311
745
|
}
|
|
746
|
+
interface NitroRouteConfig {
|
|
747
|
+
auth?: import('${runtimeTypesPath}').AuthMeta
|
|
748
|
+
}
|
|
749
|
+
interface InternalApi extends _GeneratedAuthInternalApi {}
|
|
312
750
|
}
|
|
751
|
+
export {}
|
|
313
752
|
`
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
753
|
+
}, { nuxt: true, nitro: true, node: true });
|
|
754
|
+
}
|
|
755
|
+
function registerSharedTypeTemplates(input) {
|
|
756
|
+
addTypeTemplate({
|
|
757
|
+
filename: "types/nuxt-better-auth.d.ts",
|
|
758
|
+
getContents: () => `
|
|
759
|
+
import type { AppSession } from '${input.runtimeTypesAugmentPath}'
|
|
760
|
+
export * from '${input.runtimeTypesAugmentPath}'
|
|
761
|
+
export type { AuthMeta, AuthMode, AuthRouteRules, AuthSocialProviderId, Auth, InferUser, InferSession } from '${input.runtimeTypesPath}'
|
|
762
|
+
declare module 'h3' {
|
|
763
|
+
interface H3EventContext {
|
|
764
|
+
requestSession?: AppSession | null
|
|
765
|
+
}
|
|
766
|
+
}
|
|
767
|
+
`
|
|
768
|
+
});
|
|
769
|
+
addTypeTemplate({
|
|
770
|
+
filename: "types/nuxt-better-auth-client.d.ts",
|
|
771
|
+
getContents: () => `
|
|
772
|
+
import type createAppAuthClient from '${input.clientConfigPath}'
|
|
773
|
+
declare module '#nuxt-better-auth' {
|
|
774
|
+
export type AppAuthClient = ReturnType<typeof createAppAuthClient>
|
|
775
|
+
}
|
|
776
|
+
`
|
|
777
|
+
});
|
|
778
|
+
}
|
|
779
|
+
|
|
780
|
+
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`;
|
|
786
|
+
}
|
|
787
|
+
async function createDefaultAuthConfigFiles(rootDir, srcDir) {
|
|
788
|
+
const serverPath = join(rootDir, "server/auth.config.ts");
|
|
789
|
+
const clientPath = join(srcDir, "auth.config.ts");
|
|
790
|
+
const serverTemplate = `import { defineServerAuth } from '@onmax/nuxt-better-auth/config'
|
|
791
|
+
|
|
792
|
+
export default defineServerAuth({
|
|
793
|
+
emailAndPassword: { enabled: true },
|
|
794
|
+
})
|
|
795
|
+
`;
|
|
796
|
+
const clientTemplate = `import { defineClientAuth } from '@onmax/nuxt-better-auth/config'
|
|
797
|
+
|
|
798
|
+
export default defineClientAuth({})
|
|
799
|
+
`;
|
|
800
|
+
if (!existsSync(serverPath)) {
|
|
801
|
+
await mkdir(dirname(serverPath), { recursive: true });
|
|
802
|
+
await writeFile(serverPath, serverTemplate);
|
|
803
|
+
consola.success("Created server/auth.config.ts");
|
|
804
|
+
}
|
|
805
|
+
if (!existsSync(clientPath)) {
|
|
806
|
+
await mkdir(dirname(clientPath), { recursive: true });
|
|
807
|
+
await writeFile(clientPath, clientTemplate);
|
|
808
|
+
const relativePath = clientPath.replace(`${rootDir}/`, "");
|
|
809
|
+
consola.success(`Created ${relativePath}`);
|
|
810
|
+
}
|
|
811
|
+
}
|
|
812
|
+
const module$1 = defineNuxtModule({
|
|
813
|
+
meta: { name: "@onmax/nuxt-better-auth", version, configKey: "auth", compatibility: { nuxt: ">=4.0.0" } },
|
|
814
|
+
defaults: {
|
|
815
|
+
clientOnly: false,
|
|
816
|
+
serverConfig: "server/auth.config",
|
|
817
|
+
clientConfig: "app/auth.config",
|
|
818
|
+
redirects: { login: "/login", guest: "/" },
|
|
819
|
+
preserveRedirect: true,
|
|
820
|
+
redirectQueryKey: "redirect",
|
|
821
|
+
hubSecondaryStorage: false
|
|
822
|
+
},
|
|
823
|
+
async onInstall(nuxt) {
|
|
824
|
+
const configuredSecret = nuxt.options.runtimeConfig?.betterAuthSecret;
|
|
825
|
+
const generatedSecret = await promptForSecret(nuxt.options.rootDir, consola, { configuredSecret, prepare: Boolean(nuxt.options._prepare) });
|
|
826
|
+
if (generatedSecret)
|
|
827
|
+
process.env.BETTER_AUTH_SECRET = generatedSecret;
|
|
828
|
+
await createDefaultAuthConfigFiles(nuxt.options.rootDir, nuxt.options.srcDir);
|
|
829
|
+
},
|
|
830
|
+
async setup(options, nuxt) {
|
|
831
|
+
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
|
+
);
|
|
333
852
|
}
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
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
|
|
346
868
|
});
|
|
347
|
-
}
|
|
348
|
-
|
|
349
|
-
const
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
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
|
+
})
|
|
361
892
|
}
|
|
362
|
-
page.children?.forEach((child) => applyMetaFromRules(child));
|
|
363
893
|
};
|
|
364
|
-
|
|
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
|
+
}
|
|
911
|
+
const secondaryStorageTemplate = addTemplate({
|
|
912
|
+
filename: "better-auth/secondary-storage.mjs",
|
|
913
|
+
getContents: () => buildSecondaryStorageCode(useHubKV),
|
|
914
|
+
write: true
|
|
915
|
+
});
|
|
916
|
+
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
|
+
const databaseTemplate = addTemplate({
|
|
924
|
+
filename: "better-auth/database.mjs",
|
|
925
|
+
getContents: () => resolvedProvider.definition.buildDatabaseCode(buildCtx),
|
|
926
|
+
write: true
|
|
927
|
+
});
|
|
928
|
+
nuxt.options.alias["#auth/database"] = databaseTemplate.dst;
|
|
929
|
+
const schemaTemplate = addTemplate({
|
|
930
|
+
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
|
+
},
|
|
939
|
+
write: true
|
|
940
|
+
});
|
|
941
|
+
nuxt.options.alias["#auth/schema"] = schemaTemplate.dst;
|
|
942
|
+
registerServerTypeTemplates({
|
|
943
|
+
serverConfigPath,
|
|
944
|
+
hasHubDb,
|
|
945
|
+
runtimeTypesPath: resolver.resolve("./runtime/types")
|
|
946
|
+
});
|
|
947
|
+
if (hasHubDb)
|
|
948
|
+
await setupBetterAuthSchema(nuxt, serverConfigPath, options, consola, options.hubSecondaryStorage ?? false);
|
|
949
|
+
}
|
|
950
|
+
registerSharedTypeTemplates({
|
|
951
|
+
runtimeTypesAugmentPath: resolver.resolve("./runtime/types/augment"),
|
|
952
|
+
runtimeTypesPath: resolver.resolve("./runtime/types"),
|
|
953
|
+
clientConfigPath
|
|
365
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
|
+
);
|
|
963
|
+
const authRouteRulesTemplate = addTemplate({
|
|
964
|
+
filename: "better-auth/route-rules.mjs",
|
|
965
|
+
getContents: () => `export const authRouteRules = ${JSON.stringify(authRouteRules, null, 2)}
|
|
966
|
+
`,
|
|
967
|
+
write: true
|
|
968
|
+
});
|
|
969
|
+
nuxt.options.alias["#auth/route-rules"] = authRouteRulesTemplate.dst;
|
|
970
|
+
registerTemplateHmrHook(nuxt);
|
|
971
|
+
registerServerRuntime({ clientOnly, resolve: resolver.resolve });
|
|
972
|
+
registerAuthMiddlewareHook(nuxt, resolver.resolve);
|
|
973
|
+
await registerDevtools({ nuxt, clientOnly, hasHubDb, resolve: resolver.resolve });
|
|
974
|
+
registerRouteRulesMetaHook(nuxt);
|
|
366
975
|
}
|
|
367
976
|
});
|
|
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
977
|
|
|
408
978
|
export { module$1 as default };
|