@onmax/nuxt-better-auth 0.0.2-alpha.8 → 0.0.2
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 +57 -17
- package/dist/module.d.mts +28 -1
- package/dist/module.json +3 -3
- package/dist/module.mjs +1272 -339
- package/dist/runtime/app/components/BetterAuthState.d.vue.ts +4 -4
- package/dist/runtime/app/components/BetterAuthState.vue +1 -0
- package/dist/runtime/app/components/BetterAuthState.vue.d.ts +4 -4
- 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/useAuthClient.d.ts +9 -0
- package/dist/runtime/app/composables/useAuthClient.js +34 -0
- package/dist/runtime/app/composables/useAuthClientAction.d.ts +3 -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 +6 -5
- package/dist/runtime/app/composables/useUserSession.js +189 -100
- package/dist/runtime/app/composables/useUserSessionState.d.ts +3 -0
- package/dist/runtime/app/composables/useUserSessionState.js +4 -0
- package/dist/runtime/app/internal/auth-action-error.d.ts +3 -0
- package/dist/runtime/app/internal/auth-action-error.js +33 -0
- package/dist/runtime/app/internal/auth-action-handles.d.ts +18 -0
- package/dist/runtime/app/internal/auth-action-handles.js +105 -0
- package/dist/runtime/app/internal/redirect-helpers.d.ts +4 -0
- package/dist/runtime/app/internal/redirect-helpers.js +37 -0
- package/dist/runtime/app/internal/session-fetch.d.ts +12 -0
- package/dist/runtime/app/internal/session-fetch.js +56 -0
- package/dist/runtime/app/internal/utils.d.ts +1 -0
- package/dist/runtime/app/internal/utils.js +3 -0
- package/dist/runtime/app/internal/vue-safe-auth-proxy.d.ts +3 -0
- package/dist/runtime/app/internal/vue-safe-auth-proxy.js +68 -0
- package/dist/runtime/app/internal/wrap-auth-method.d.ts +15 -0
- package/dist/runtime/app/internal/wrap-auth-method.js +66 -0
- package/dist/runtime/app/middleware/auth.global.js +77 -15
- package/dist/runtime/app/pages/__better-auth-devtools.vue +4 -10
- package/dist/runtime/composables.d.ts +11 -0
- package/dist/runtime/composables.js +9 -0
- package/dist/runtime/config.d.ts +69 -15
- package/dist/runtime/config.js +9 -2
- package/dist/runtime/server/api/_better-auth/_schema.d.ts +1 -5
- package/dist/runtime/server/api/_better-auth/_schema.js +2 -1
- package/dist/runtime/server/api/_better-auth/accounts.get.d.ts +2 -2
- package/dist/runtime/server/api/_better-auth/accounts.get.js +3 -2
- package/dist/runtime/server/api/_better-auth/config.get.d.ts +9 -3
- package/dist/runtime/server/api/_better-auth/config.get.js +18 -6
- package/dist/runtime/server/api/_better-auth/sessions.delete.js +3 -2
- package/dist/runtime/server/api/_better-auth/sessions.get.d.ts +5 -3
- package/dist/runtime/server/api/_better-auth/sessions.get.js +3 -2
- package/dist/runtime/server/api/_better-auth/users.get.d.ts +2 -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 +2 -1
- package/dist/runtime/server/tsconfig.json +9 -1
- package/dist/runtime/server/utils/auth.d.ts +16 -8
- package/dist/runtime/server/utils/auth.js +229 -23
- 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 +6 -8
- package/dist/runtime/server/utils/session.js +216 -4
- package/dist/runtime/server/virtual-modules.d.ts +27 -0
- package/dist/runtime/types/augment.d.ts +18 -4
- package/dist/runtime/types.d.ts +12 -13
- package/dist/types.d.mts +1 -1
- package/package.json +51 -47
package/dist/module.mjs
CHANGED
|
@@ -1,15 +1,134 @@
|
|
|
1
|
-
import { existsSync } from 'node:fs';
|
|
1
|
+
import { existsSync, statSync, writeFileSync, readFileSync } from 'node:fs';
|
|
2
2
|
import { mkdir, writeFile } from 'node:fs/promises';
|
|
3
|
-
import {
|
|
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
|
|
8
|
+
import { generateDrizzleSchema as generateDrizzleSchema$1 } from '@better-auth/cli/api';
|
|
9
|
+
import { randomBytes } from 'node:crypto';
|
|
10
|
+
import { isCI, isTest } from 'std-env';
|
|
9
11
|
export { defineClientAuth, defineServerAuth } from '../dist/runtime/config.js';
|
|
10
12
|
|
|
13
|
+
const version = "0.0.2";
|
|
14
|
+
|
|
15
|
+
const CONFIG_EXTENSIONS = [".ts", ".js"];
|
|
16
|
+
const CONFIG_EXTENSION_RE = /\.(?:ts|js)$/;
|
|
17
|
+
const DEFAULT_CONFIG_FILES = {
|
|
18
|
+
server: "server/auth.config",
|
|
19
|
+
client: "app/auth.config"
|
|
20
|
+
};
|
|
21
|
+
const OPTION_KEY_BY_KIND = {
|
|
22
|
+
server: "serverConfig",
|
|
23
|
+
client: "clientConfig"
|
|
24
|
+
};
|
|
25
|
+
function stripConfigExtension(path) {
|
|
26
|
+
return path.replace(CONFIG_EXTENSION_RE, "");
|
|
27
|
+
}
|
|
28
|
+
function defaultConfigExists(path) {
|
|
29
|
+
return CONFIG_EXTENSIONS.some((ext) => existsSync(`${path}${ext}`));
|
|
30
|
+
}
|
|
31
|
+
function getLayerDirectoriesWithConfigs(nuxt) {
|
|
32
|
+
const directories = getLayerDirectories(nuxt);
|
|
33
|
+
const layers = nuxt.options._layers;
|
|
34
|
+
return directories.map((directory, index) => ({ directory, layer: layers[index] }));
|
|
35
|
+
}
|
|
36
|
+
function getProjectDirectory(nuxt) {
|
|
37
|
+
return getLayerDirectories(nuxt)[0];
|
|
38
|
+
}
|
|
39
|
+
function getDefaultConfigPath(nuxt, kind) {
|
|
40
|
+
const project = getProjectDirectory(nuxt);
|
|
41
|
+
return kind === "server" ? join(project.server, "auth.config") : join(project.app, "auth.config");
|
|
42
|
+
}
|
|
43
|
+
function getLayerDefaultConfigPath(nuxt, kind, configExists) {
|
|
44
|
+
for (const { directory } of getLayerDirectoriesWithConfigs(nuxt)) {
|
|
45
|
+
const candidate = kind === "server" ? join(directory.server, "auth.config") : join(directory.app, "auth.config");
|
|
46
|
+
if (configExists(candidate)) {
|
|
47
|
+
return {
|
|
48
|
+
path: candidate,
|
|
49
|
+
declaringLayerRoot: directory.root
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
function resolveDeclaringLayerRoot(nuxt, kind, file) {
|
|
55
|
+
const optionKey = OPTION_KEY_BY_KIND[kind];
|
|
56
|
+
for (const { directory, layer } of getLayerDirectoriesWithConfigs(nuxt)) {
|
|
57
|
+
const declared = layer?.config?.auth?.[optionKey];
|
|
58
|
+
if (typeof declared === "string" && stripConfigExtension(declared) === file)
|
|
59
|
+
return directory.root;
|
|
60
|
+
}
|
|
61
|
+
return getProjectDirectory(nuxt).root;
|
|
62
|
+
}
|
|
63
|
+
function getRelativeConfigFile(nuxt, path) {
|
|
64
|
+
return relative(getProjectDirectory(nuxt).root, path);
|
|
65
|
+
}
|
|
66
|
+
function getEffectiveModuleConfigFile(nuxt, kind) {
|
|
67
|
+
const optionKey = OPTION_KEY_BY_KIND[kind];
|
|
68
|
+
const authOptions = nuxt.options.auth;
|
|
69
|
+
return authOptions?.[optionKey] ?? DEFAULT_CONFIG_FILES[kind];
|
|
70
|
+
}
|
|
71
|
+
function resolveAuthConfigDescriptor(nuxt, kind, file = getEffectiveModuleConfigFile(nuxt, kind), dependencies = {}) {
|
|
72
|
+
const configExists = dependencies.configExists ?? defaultConfigExists;
|
|
73
|
+
const configuredFile = stripConfigExtension(file);
|
|
74
|
+
if (isAbsolute(configuredFile)) {
|
|
75
|
+
const declaringLayerRoot2 = resolveDeclaringLayerRoot(nuxt, kind, configuredFile);
|
|
76
|
+
const exists2 = configExists(configuredFile);
|
|
77
|
+
return {
|
|
78
|
+
kind,
|
|
79
|
+
configuredFile,
|
|
80
|
+
file: configuredFile,
|
|
81
|
+
path: configuredFile,
|
|
82
|
+
declaringLayerRoot: declaringLayerRoot2,
|
|
83
|
+
isDefault: false,
|
|
84
|
+
isExplicit: true,
|
|
85
|
+
exists: exists2,
|
|
86
|
+
shouldCreateDefaultFile: false
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
if (configuredFile === DEFAULT_CONFIG_FILES[kind]) {
|
|
90
|
+
const project = getProjectDirectory(nuxt);
|
|
91
|
+
const discovered = getLayerDefaultConfigPath(nuxt, kind, configExists);
|
|
92
|
+
const path2 = discovered?.path ?? getDefaultConfigPath(nuxt, kind);
|
|
93
|
+
const declaringLayerRoot2 = discovered?.declaringLayerRoot ?? project.root;
|
|
94
|
+
const exists2 = configExists(path2);
|
|
95
|
+
return {
|
|
96
|
+
kind,
|
|
97
|
+
configuredFile,
|
|
98
|
+
file: getRelativeConfigFile(nuxt, path2),
|
|
99
|
+
path: path2,
|
|
100
|
+
declaringLayerRoot: declaringLayerRoot2,
|
|
101
|
+
isDefault: true,
|
|
102
|
+
isExplicit: false,
|
|
103
|
+
exists: exists2,
|
|
104
|
+
shouldCreateDefaultFile: !exists2
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
const declaringLayerRoot = resolveDeclaringLayerRoot(nuxt, kind, configuredFile);
|
|
108
|
+
const path = join(declaringLayerRoot, configuredFile);
|
|
109
|
+
const exists = configExists(path);
|
|
110
|
+
return {
|
|
111
|
+
kind,
|
|
112
|
+
configuredFile,
|
|
113
|
+
file: getRelativeConfigFile(nuxt, path),
|
|
114
|
+
path,
|
|
115
|
+
declaringLayerRoot,
|
|
116
|
+
isDefault: false,
|
|
117
|
+
isExplicit: true,
|
|
118
|
+
exists,
|
|
119
|
+
shouldCreateDefaultFile: false
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
function resolveAuthConfigDescriptors(nuxt, files = {}, dependencies = {}) {
|
|
123
|
+
return {
|
|
124
|
+
server: resolveAuthConfigDescriptor(nuxt, "server", files.server, dependencies),
|
|
125
|
+
client: resolveAuthConfigDescriptor(nuxt, "client", files.client, dependencies)
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
|
|
11
129
|
function setupDevTools(nuxt) {
|
|
12
|
-
|
|
130
|
+
const hookable = nuxt;
|
|
131
|
+
hookable.hook("devtools:customTabs", (tabs) => {
|
|
13
132
|
tabs.push({
|
|
14
133
|
category: "server",
|
|
15
134
|
name: "better-auth",
|
|
@@ -23,152 +142,179 @@ function setupDevTools(nuxt) {
|
|
|
23
142
|
});
|
|
24
143
|
}
|
|
25
144
|
|
|
26
|
-
function
|
|
27
|
-
|
|
145
|
+
function registerTemplateHmrHook(nuxt) {
|
|
146
|
+
nuxt.hook("builder:watch", async (_event, relativePath) => {
|
|
147
|
+
if (relativePath.includes("auth.config"))
|
|
148
|
+
await updateTemplates({ filter: (t) => t.filename.includes("nuxt-better-auth") });
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
function registerServerRuntime(input) {
|
|
152
|
+
const { clientOnly, resolve } = input;
|
|
153
|
+
if (!clientOnly) {
|
|
154
|
+
addServerImportsDir(resolve("./runtime/server/utils"));
|
|
155
|
+
addServerImports([{ name: "defineServerAuth", from: resolve("./runtime/config") }]);
|
|
156
|
+
addServerScanDir(resolve("./runtime/server/middleware"));
|
|
157
|
+
addServerHandler({ route: "/api/auth/**", handler: resolve("./runtime/server/api/auth/[...all]") });
|
|
158
|
+
}
|
|
159
|
+
addImportsDir(resolve("./runtime/app/composables"));
|
|
160
|
+
addImportsDir(resolve("./runtime/utils"));
|
|
161
|
+
if (!clientOnly)
|
|
162
|
+
addPlugin({ src: resolve("./runtime/app/plugins/session.server"), mode: "server" });
|
|
163
|
+
addPlugin({ src: resolve("./runtime/app/plugins/session.client"), mode: "client" });
|
|
164
|
+
addComponentsDir({ path: resolve("./runtime/app/components") });
|
|
165
|
+
}
|
|
166
|
+
function registerAuthMiddlewareHook(nuxt, resolve) {
|
|
167
|
+
nuxt.hook("app:resolve", (app) => {
|
|
168
|
+
app.middleware.push({ name: "auth", path: resolve("./runtime/app/middleware/auth.global"), global: true });
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
function registerPrepareTypesHook(input) {
|
|
172
|
+
const { nuxt, serverDir, hasHubDb } = input;
|
|
173
|
+
nuxt.hook("prepare:types", ({ nodeTsConfig, nodeReferences, sharedReferences }) => {
|
|
174
|
+
nodeTsConfig.compilerOptions ||= {};
|
|
175
|
+
nodeTsConfig.compilerOptions.paths ||= {};
|
|
176
|
+
const projectReferenceTypePaths = [
|
|
177
|
+
join(nuxt.options.buildDir, "types/nitro-imports.d.ts"),
|
|
178
|
+
join(nuxt.options.buildDir, "types/auth-database.d.ts"),
|
|
179
|
+
join(nuxt.options.buildDir, "types/auth-schema.d.ts"),
|
|
180
|
+
join(nuxt.options.buildDir, "types/auth-secondary-storage.d.ts")
|
|
181
|
+
];
|
|
182
|
+
if (hasHubDb)
|
|
183
|
+
projectReferenceTypePaths.push(join(nuxt.options.buildDir, "hub/db.d.ts"));
|
|
184
|
+
const exactNodeAliases = {
|
|
185
|
+
"#server": serverDir,
|
|
186
|
+
"#auth/server": nuxt.options.alias["#auth/server"],
|
|
187
|
+
"#auth/client": nuxt.options.alias["#auth/client"],
|
|
188
|
+
"#auth/database": nuxt.options.alias["#auth/database"],
|
|
189
|
+
"#auth/schema": nuxt.options.alias["#auth/schema"],
|
|
190
|
+
"#auth/secondary-storage": nuxt.options.alias["#auth/secondary-storage"],
|
|
191
|
+
"#auth/route-rules": nuxt.options.alias["#auth/route-rules"],
|
|
192
|
+
"#nuxt-better-auth": nuxt.options.alias["#nuxt-better-auth"]
|
|
193
|
+
};
|
|
194
|
+
for (const [key, value] of Object.entries(exactNodeAliases)) {
|
|
195
|
+
if (typeof value === "string")
|
|
196
|
+
nodeTsConfig.compilerOptions.paths[key] = [value];
|
|
197
|
+
}
|
|
198
|
+
for (const [key, value] of Object.entries(nuxt.options.alias)) {
|
|
199
|
+
if (typeof value !== "string" || !isAbsolute(value))
|
|
200
|
+
continue;
|
|
201
|
+
nodeTsConfig.compilerOptions.paths[key] ||= [value];
|
|
202
|
+
if (!key.includes("*") && existsSync(value) && statSync(value).isDirectory())
|
|
203
|
+
nodeTsConfig.compilerOptions.paths[`${key}/*`] ||= [join(value, "*")];
|
|
204
|
+
}
|
|
205
|
+
nodeTsConfig.compilerOptions.paths["#server/*"] = [join(serverDir, "*")];
|
|
206
|
+
for (const path of projectReferenceTypePaths) {
|
|
207
|
+
if (!nodeReferences.some((reference) => "path" in reference && reference.path === path))
|
|
208
|
+
nodeReferences.push({ path });
|
|
209
|
+
if (!sharedReferences.some((reference) => "path" in reference && reference.path === path))
|
|
210
|
+
sharedReferences.push({ path });
|
|
211
|
+
}
|
|
212
|
+
});
|
|
213
|
+
}
|
|
214
|
+
function registerNuxtHubDatabaseExternalHook(nuxt) {
|
|
215
|
+
nuxt.hook("nitro:config", (nitroConfig) => {
|
|
216
|
+
nitroConfig.externals ||= {};
|
|
217
|
+
nitroConfig.externals.external ||= [];
|
|
218
|
+
if (!nitroConfig.externals.external.includes("@nuxthub/db"))
|
|
219
|
+
nitroConfig.externals.external.push("@nuxthub/db");
|
|
220
|
+
});
|
|
221
|
+
}
|
|
222
|
+
async function registerDevtools(input) {
|
|
223
|
+
const { nuxt, clientOnly, hasHubDb, resolve } = input;
|
|
224
|
+
const isProduction = process.env.NODE_ENV === "production" || !nuxt.options.dev;
|
|
225
|
+
if (isProduction || clientOnly)
|
|
226
|
+
return;
|
|
227
|
+
if (!hasNuxtModule("@nuxt/ui"))
|
|
228
|
+
await installModule("@nuxt/ui");
|
|
229
|
+
setupDevTools(nuxt);
|
|
230
|
+
addServerHandler({ route: "/api/_better-auth/config", method: "get", handler: resolve("./runtime/server/api/_better-auth/config.get") });
|
|
231
|
+
if (hasHubDb) {
|
|
232
|
+
const handlers = [
|
|
233
|
+
{ route: "/api/_better-auth/sessions", method: "get", handler: resolve("./runtime/server/api/_better-auth/sessions.get") },
|
|
234
|
+
{ route: "/api/_better-auth/sessions", method: "delete", handler: resolve("./runtime/server/api/_better-auth/sessions.delete") },
|
|
235
|
+
{ route: "/api/_better-auth/users", method: "get", handler: resolve("./runtime/server/api/_better-auth/users.get") },
|
|
236
|
+
{ route: "/api/_better-auth/accounts", method: "get", handler: resolve("./runtime/server/api/_better-auth/accounts.get") }
|
|
237
|
+
];
|
|
238
|
+
handlers.forEach((handler) => addServerHandler(handler));
|
|
239
|
+
}
|
|
240
|
+
extendPages((pages) => {
|
|
241
|
+
pages.push({ name: "better-auth-devtools", path: "/__better-auth-devtools", file: resolve("./runtime/app/pages/__better-auth-devtools.vue") });
|
|
242
|
+
});
|
|
243
|
+
}
|
|
244
|
+
function registerRouteRulesMetaHook(nuxt) {
|
|
245
|
+
nuxt.hook("pages:extend", (pages) => {
|
|
246
|
+
const options = nuxt.options;
|
|
247
|
+
const routeRules = options.nitro?.routeRules || options.routeRules || {};
|
|
248
|
+
if (!Object.keys(routeRules).length)
|
|
249
|
+
return;
|
|
250
|
+
const matcher = toRouteMatcher(createRouter({ routes: routeRules }));
|
|
251
|
+
const applyMetaFromRules = (page) => {
|
|
252
|
+
const matches = matcher.matchAll(page.path);
|
|
253
|
+
if (!matches.length)
|
|
254
|
+
return;
|
|
255
|
+
const matchedRules = defu({}, ...matches.reverse());
|
|
256
|
+
if (matchedRules.auth !== void 0) {
|
|
257
|
+
page.meta = page.meta || {};
|
|
258
|
+
page.meta.auth = matchedRules.auth;
|
|
259
|
+
}
|
|
260
|
+
page.children?.forEach((child) => applyMetaFromRules(child));
|
|
261
|
+
};
|
|
262
|
+
pages.forEach((page) => applyMetaFromRules(page));
|
|
263
|
+
});
|
|
28
264
|
}
|
|
29
|
-
function generateDrizzleSchema(tables, dialect, options) {
|
|
30
|
-
const typedTables = tables;
|
|
31
|
-
const imports = getImports(dialect, options);
|
|
32
|
-
const tableDefinitions = Object.entries(typedTables).map(([tableName, table]) => generateTable(tableName, table, dialect, typedTables, options)).join("\n\n");
|
|
33
|
-
return `${imports}
|
|
34
265
|
|
|
35
|
-
|
|
36
|
-
|
|
266
|
+
function dialectToProvider(dialect) {
|
|
267
|
+
return dialect === "postgresql" ? "pg" : dialect;
|
|
37
268
|
}
|
|
38
|
-
function
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
const
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
})
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
return `id: text('id').primaryKey()`;
|
|
69
|
-
case "postgresql":
|
|
70
|
-
return options?.useUuid ? `id: uuid('id').defaultRandom().primaryKey()` : `id: text('id').primaryKey()`;
|
|
71
|
-
case "mysql":
|
|
72
|
-
return `id: varchar('id', { length: 36 }).primaryKey()`;
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
function generateField(fieldName, field, dialect, allTables, options) {
|
|
76
|
-
const dbFieldName = options?.casing === "snake_case" ? toSnakeCase(fieldName) : fieldName;
|
|
77
|
-
const isFkToId = options?.useUuid && field.references?.field === "id";
|
|
78
|
-
let fieldDef;
|
|
79
|
-
if (isFkToId && dialect === "postgresql")
|
|
80
|
-
fieldDef = `uuid('${dbFieldName}')`;
|
|
81
|
-
else if (isFkToId && dialect === "mysql")
|
|
82
|
-
fieldDef = `varchar('${dbFieldName}', { length: 36 })`;
|
|
83
|
-
else
|
|
84
|
-
fieldDef = getFieldType(field.type, dialect, dbFieldName);
|
|
85
|
-
if (field.required && field.defaultValue === void 0)
|
|
86
|
-
fieldDef += ".notNull()";
|
|
87
|
-
if (field.unique)
|
|
88
|
-
fieldDef += ".unique()";
|
|
89
|
-
if (field.defaultValue !== void 0) {
|
|
90
|
-
if (typeof field.defaultValue === "boolean")
|
|
91
|
-
fieldDef += `.default(${field.defaultValue})`;
|
|
92
|
-
else if (typeof field.defaultValue === "string")
|
|
93
|
-
fieldDef += `.default('${field.defaultValue}')`;
|
|
94
|
-
else if (typeof field.defaultValue === "function")
|
|
95
|
-
fieldDef += `.$defaultFn(${field.defaultValue})`;
|
|
96
|
-
else
|
|
97
|
-
fieldDef += `.default(${field.defaultValue})`;
|
|
98
|
-
if (field.required)
|
|
99
|
-
fieldDef += ".notNull()";
|
|
100
|
-
}
|
|
101
|
-
if (typeof field.onUpdate === "function" && field.type === "date")
|
|
102
|
-
fieldDef += `.$onUpdate(${field.onUpdate})`;
|
|
103
|
-
if (field.references) {
|
|
104
|
-
const refTable = field.references.model;
|
|
105
|
-
if (allTables[refTable])
|
|
106
|
-
fieldDef += `.references(() => ${refTable}.${field.references.field})`;
|
|
107
|
-
}
|
|
108
|
-
return `${fieldName}: ${fieldDef}`;
|
|
109
|
-
}
|
|
110
|
-
function getFieldType(type, dialect, fieldName) {
|
|
111
|
-
const normalizedType = Array.isArray(type) ? "string" : type;
|
|
112
|
-
switch (dialect) {
|
|
113
|
-
case "sqlite":
|
|
114
|
-
return getSqliteType(normalizedType, fieldName);
|
|
115
|
-
case "postgresql":
|
|
116
|
-
return getPostgresType(normalizedType, fieldName);
|
|
117
|
-
case "mysql":
|
|
118
|
-
return getMysqlType(normalizedType, fieldName);
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
function getSqliteType(type, fieldName) {
|
|
122
|
-
switch (type) {
|
|
123
|
-
case "string":
|
|
124
|
-
return `text('${fieldName}')`;
|
|
125
|
-
case "boolean":
|
|
126
|
-
return `integer('${fieldName}', { mode: 'boolean' })`;
|
|
127
|
-
case "date":
|
|
128
|
-
return `integer('${fieldName}', { mode: 'timestamp' })`;
|
|
129
|
-
case "number":
|
|
130
|
-
return `integer('${fieldName}')`;
|
|
131
|
-
default:
|
|
132
|
-
return `text('${fieldName}')`;
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
function getPostgresType(type, fieldName) {
|
|
136
|
-
switch (type) {
|
|
137
|
-
case "string":
|
|
138
|
-
return `text('${fieldName}')`;
|
|
139
|
-
case "boolean":
|
|
140
|
-
return `boolean('${fieldName}')`;
|
|
141
|
-
case "date":
|
|
142
|
-
return `timestamp('${fieldName}')`;
|
|
143
|
-
case "number":
|
|
144
|
-
return `integer('${fieldName}')`;
|
|
145
|
-
default:
|
|
146
|
-
return `text('${fieldName}')`;
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
function getMysqlType(type, fieldName) {
|
|
150
|
-
switch (type) {
|
|
151
|
-
case "string":
|
|
152
|
-
return `text('${fieldName}')`;
|
|
153
|
-
case "boolean":
|
|
154
|
-
return `boolean('${fieldName}')`;
|
|
155
|
-
case "date":
|
|
156
|
-
return `timestamp('${fieldName}')`;
|
|
157
|
-
case "number":
|
|
158
|
-
return `int('${fieldName}')`;
|
|
159
|
-
default:
|
|
160
|
-
return `text('${fieldName}')`;
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
async function loadUserAuthConfig(configPath, throwOnError = false) {
|
|
269
|
+
async function generateDrizzleSchema(authOptions, dialect, schemaOptions) {
|
|
270
|
+
const provider = dialectToProvider(dialect);
|
|
271
|
+
const options = {
|
|
272
|
+
...authOptions,
|
|
273
|
+
advanced: {
|
|
274
|
+
...authOptions.advanced,
|
|
275
|
+
database: {
|
|
276
|
+
...authOptions.advanced?.database,
|
|
277
|
+
...schemaOptions?.useUuid && { generateId: "uuid" }
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
};
|
|
281
|
+
const adapter = {
|
|
282
|
+
id: "drizzle",
|
|
283
|
+
options: {
|
|
284
|
+
provider,
|
|
285
|
+
camelCase: schemaOptions?.casing !== "snake_case",
|
|
286
|
+
adapterConfig: { usePlural: schemaOptions?.usePlural ?? false }
|
|
287
|
+
}
|
|
288
|
+
};
|
|
289
|
+
const result = await generateDrizzleSchema$1({
|
|
290
|
+
adapter,
|
|
291
|
+
options
|
|
292
|
+
});
|
|
293
|
+
if (!result.code) {
|
|
294
|
+
throw new Error(`Schema generation returned empty result for ${dialect}`);
|
|
295
|
+
}
|
|
296
|
+
return result.code;
|
|
297
|
+
}
|
|
298
|
+
async function loadUserAuthConfig(configPath, throwOnError = false, alias) {
|
|
164
299
|
const { createJiti } = await import('jiti');
|
|
165
|
-
const
|
|
300
|
+
const { defineServerAuth: runtimeDefineServerAuth } = await import('../dist/runtime/config.js');
|
|
301
|
+
const jiti = createJiti(import.meta.url, { interopDefault: true, moduleCache: false, alias });
|
|
302
|
+
const schemaGlobals = globalThis;
|
|
303
|
+
if (!schemaGlobals.__nuxtBetterAuthDefineServerAuth) {
|
|
304
|
+
runtimeDefineServerAuth._count = 0;
|
|
305
|
+
schemaGlobals.__nuxtBetterAuthDefineServerAuth = runtimeDefineServerAuth;
|
|
306
|
+
}
|
|
307
|
+
if (!schemaGlobals.defineServerAuth) {
|
|
308
|
+
schemaGlobals.defineServerAuth = schemaGlobals.__nuxtBetterAuthDefineServerAuth;
|
|
309
|
+
}
|
|
310
|
+
schemaGlobals.__nuxtBetterAuthDefineServerAuth._count++;
|
|
166
311
|
try {
|
|
167
312
|
const mod = await jiti.import(configPath);
|
|
168
|
-
const configFn =
|
|
313
|
+
const configFn = mod.default;
|
|
169
314
|
if (typeof configFn === "function") {
|
|
170
315
|
return configFn({ runtimeConfig: {}, db: null });
|
|
171
316
|
}
|
|
317
|
+
consola$1.warn("[@onmax/nuxt-better-auth] auth.config.ts does not export default. Expected: export default defineServerAuth(...)");
|
|
172
318
|
if (throwOnError) {
|
|
173
319
|
throw new Error("auth.config.ts must export default defineServerAuth(...)");
|
|
174
320
|
}
|
|
@@ -179,6 +325,17 @@ async function loadUserAuthConfig(configPath, throwOnError = false) {
|
|
|
179
325
|
}
|
|
180
326
|
consola$1.error("[@onmax/nuxt-better-auth] Failed to load auth config for schema generation. Schema may be incomplete:", error);
|
|
181
327
|
return {};
|
|
328
|
+
} finally {
|
|
329
|
+
const sharedDefineServerAuth = schemaGlobals.__nuxtBetterAuthDefineServerAuth;
|
|
330
|
+
if (sharedDefineServerAuth) {
|
|
331
|
+
sharedDefineServerAuth._count--;
|
|
332
|
+
if (!sharedDefineServerAuth._count) {
|
|
333
|
+
schemaGlobals.__nuxtBetterAuthDefineServerAuth = void 0;
|
|
334
|
+
if (schemaGlobals.defineServerAuth === sharedDefineServerAuth) {
|
|
335
|
+
schemaGlobals.defineServerAuth = void 0;
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
}
|
|
182
339
|
}
|
|
183
340
|
}
|
|
184
341
|
|
|
@@ -196,85 +353,532 @@ function getHubCasing(hub) {
|
|
|
196
353
|
return void 0;
|
|
197
354
|
return hub.db.casing;
|
|
198
355
|
}
|
|
199
|
-
|
|
200
|
-
const
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
356
|
+
|
|
357
|
+
const NODE_MODULES_SEGMENT_RE = /[\\/]/;
|
|
358
|
+
function resolveSchemaSecondaryStorageInjection(hubSecondaryStorage, userHasSecondaryStorage, isProduction) {
|
|
359
|
+
if (hubSecondaryStorage === true)
|
|
360
|
+
return { inject: false };
|
|
361
|
+
if (hubSecondaryStorage !== "custom")
|
|
362
|
+
return { inject: false };
|
|
363
|
+
if (userHasSecondaryStorage)
|
|
364
|
+
return { inject: true };
|
|
365
|
+
const message = '[nuxt-better-auth] hubSecondaryStorage: "custom" requires secondaryStorage in defineServerAuth() to omit the session table from the generated schema.';
|
|
366
|
+
if (isProduction)
|
|
367
|
+
return { inject: false, error: message };
|
|
368
|
+
return { inject: false, warn: message };
|
|
369
|
+
}
|
|
370
|
+
function isInsideNodeModules(path) {
|
|
371
|
+
return path.split(NODE_MODULES_SEGMENT_RE).includes("node_modules");
|
|
372
|
+
}
|
|
373
|
+
function resolveHubSchemaPath(buildDir, rootDir, dialect, exists = existsSync) {
|
|
374
|
+
const rootTsPath = join(rootDir, ".nuxt", "better-auth", `schema.${dialect}.ts`);
|
|
375
|
+
if (isInsideNodeModules(buildDir) && exists(rootTsPath))
|
|
376
|
+
return rootTsPath;
|
|
377
|
+
const tsPath = join(buildDir, "better-auth", `schema.${dialect}.ts`);
|
|
378
|
+
if (exists(tsPath))
|
|
379
|
+
return tsPath;
|
|
380
|
+
const mjsPath = join(buildDir, "better-auth", `schema.${dialect}.mjs`);
|
|
381
|
+
if (exists(mjsPath))
|
|
382
|
+
return mjsPath;
|
|
383
|
+
return null;
|
|
384
|
+
}
|
|
385
|
+
async function loadAuthOptions(context) {
|
|
386
|
+
const isProduction = !context.nuxt.options.dev;
|
|
387
|
+
const configFile = `${context.serverConfigPath}.ts`;
|
|
388
|
+
const alias = Object.fromEntries(
|
|
389
|
+
Object.entries(context.nuxt.options.alias).filter(([, value]) => typeof value === "string").map(([key, value]) => [key, value])
|
|
390
|
+
);
|
|
391
|
+
const userConfig = await loadUserAuthConfig(configFile, isProduction, alias);
|
|
392
|
+
const extendedConfig = {};
|
|
393
|
+
await context.nuxt.callHook("better-auth:config:extend", extendedConfig);
|
|
394
|
+
const plugins = [...userConfig.plugins || [], ...extendedConfig.plugins || []];
|
|
395
|
+
return { userConfig, plugins };
|
|
396
|
+
}
|
|
397
|
+
async function setupBetterAuthSchema(nuxt, serverConfigPath, options, consola, hubSecondaryStorage) {
|
|
398
|
+
const hub = nuxt.options.hub;
|
|
399
|
+
const dialect = getHubDialect(hub);
|
|
400
|
+
if (!dialect || !["sqlite", "postgresql", "mysql"].includes(dialect)) {
|
|
401
|
+
consola.warn(`Unsupported database dialect: ${dialect}`);
|
|
402
|
+
return;
|
|
403
|
+
}
|
|
404
|
+
const context = { nuxt, serverConfigPath };
|
|
405
|
+
try {
|
|
406
|
+
const { userConfig, plugins } = await loadAuthOptions(context);
|
|
407
|
+
const userHasSecondaryStorage = userConfig.secondaryStorage != null;
|
|
408
|
+
const secondaryStorageResolution = resolveSchemaSecondaryStorageInjection(hubSecondaryStorage, userHasSecondaryStorage, !nuxt.options.dev);
|
|
409
|
+
if (secondaryStorageResolution.error)
|
|
410
|
+
throw new Error(secondaryStorageResolution.error);
|
|
411
|
+
if (secondaryStorageResolution.warn)
|
|
412
|
+
consola.warn(secondaryStorageResolution.warn);
|
|
413
|
+
const authOptions = {
|
|
414
|
+
...userConfig,
|
|
415
|
+
plugins,
|
|
416
|
+
secondaryStorage: secondaryStorageResolution.inject ? { get: async (_key) => null, set: async (_key, _value, _ttl) => {
|
|
417
|
+
}, delete: async (_key) => {
|
|
418
|
+
} } : void 0
|
|
419
|
+
};
|
|
420
|
+
const hubCasing = getHubCasing(hub);
|
|
421
|
+
const schemaOptions = { ...options.schema, useUuid: userConfig.advanced?.database?.generateId === "uuid", casing: options.schema?.casing ?? hubCasing };
|
|
422
|
+
const schemaCode = await generateDrizzleSchema(authOptions, dialect, schemaOptions);
|
|
423
|
+
const schemaDir = join(nuxt.options.buildDir, "better-auth");
|
|
424
|
+
const schemaPathTs = join(schemaDir, `schema.${dialect}.ts`);
|
|
425
|
+
const schemaPathMjs = join(schemaDir, `schema.${dialect}.mjs`);
|
|
426
|
+
await mkdir(schemaDir, { recursive: true });
|
|
427
|
+
await writeFile(schemaPathTs, schemaCode);
|
|
428
|
+
await writeFile(schemaPathMjs, schemaCode);
|
|
429
|
+
if (isInsideNodeModules(nuxt.options.buildDir)) {
|
|
430
|
+
const rootSchemaDir = join(nuxt.options.rootDir, ".nuxt", "better-auth");
|
|
431
|
+
const rootSchemaPathTs = join(rootSchemaDir, `schema.${dialect}.ts`);
|
|
432
|
+
await mkdir(rootSchemaDir, { recursive: true });
|
|
433
|
+
await writeFile(rootSchemaPathTs, schemaCode);
|
|
227
434
|
}
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
435
|
+
addTemplate({ filename: `better-auth/schema.${dialect}.ts`, getContents: () => schemaCode, write: true });
|
|
436
|
+
addTemplate({ filename: `better-auth/schema.${dialect}.mjs`, getContents: () => schemaCode, write: true });
|
|
437
|
+
consola.info(`Generated ${dialect} schema (.ts + .mjs)`);
|
|
438
|
+
const nuxtWithHubHooks = nuxt;
|
|
439
|
+
nuxtWithHubHooks.hook("hub:db:schema:extend", ({ paths, dialect: hookDialect }) => {
|
|
440
|
+
const schemaPath = resolveHubSchemaPath(nuxt.options.buildDir, nuxt.options.rootDir, hookDialect);
|
|
441
|
+
if (schemaPath)
|
|
442
|
+
paths.unshift(schemaPath);
|
|
232
443
|
});
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
444
|
+
} catch (error) {
|
|
445
|
+
const isProduction = !nuxt.options.dev;
|
|
446
|
+
if (isProduction)
|
|
447
|
+
throw error;
|
|
448
|
+
consola.error("Failed to generate schema:", error);
|
|
449
|
+
throw error;
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
const DEFAULT_SECRET_ENV = "NUXT_BETTER_AUTH_SECRET";
|
|
454
|
+
const FALLBACK_SECRET_ENV = "BETTER_AUTH_SECRET";
|
|
455
|
+
const generateSecret = () => randomBytes(32).toString("hex");
|
|
456
|
+
function readEnvFile(rootDir) {
|
|
457
|
+
const envPath = join(rootDir, ".env");
|
|
458
|
+
return existsSync(envPath) ? readFileSync(envPath, "utf-8") : "";
|
|
459
|
+
}
|
|
460
|
+
function hasEnvSecret(rootDir) {
|
|
461
|
+
const envFile = readEnvFile(rootDir);
|
|
462
|
+
return [DEFAULT_SECRET_ENV, FALLBACK_SECRET_ENV].some((name) => {
|
|
463
|
+
const match = envFile.match(new RegExp(`^${name}=(.+)$`, "m"));
|
|
464
|
+
return !!match && !!match[1] && match[1].trim().length > 0;
|
|
465
|
+
});
|
|
466
|
+
}
|
|
467
|
+
function appendSecretToEnv(rootDir, secret) {
|
|
468
|
+
const envPath = join(rootDir, ".env");
|
|
469
|
+
let content = readEnvFile(rootDir);
|
|
470
|
+
if (content.length > 0 && !content.endsWith("\n"))
|
|
471
|
+
content += "\n";
|
|
472
|
+
content += `${DEFAULT_SECRET_ENV}=${secret}
|
|
473
|
+
`;
|
|
474
|
+
writeFileSync(envPath, content, "utf-8");
|
|
475
|
+
}
|
|
476
|
+
async function promptForSecret(rootDir, consola, options = {}) {
|
|
477
|
+
const configuredSecret = options.configuredSecret?.trim();
|
|
478
|
+
if (configuredSecret)
|
|
479
|
+
return void 0;
|
|
480
|
+
if (process.env.NUXT_BETTER_AUTH_SECRET || process.env.BETTER_AUTH_SECRET || hasEnvSecret(rootDir))
|
|
481
|
+
return void 0;
|
|
482
|
+
const hasTty = Boolean(process.stdin.isTTY && process.stdout.isTTY);
|
|
483
|
+
if (options.prepare || !hasTty) {
|
|
484
|
+
consola.warn("[nuxt-better-auth] Skipping NUXT_BETTER_AUTH_SECRET prompt (non-interactive). Set NUXT_BETTER_AUTH_SECRET or BETTER_AUTH_SECRET.");
|
|
485
|
+
return void 0;
|
|
486
|
+
}
|
|
487
|
+
if (isCI || isTest) {
|
|
488
|
+
const secret2 = generateSecret();
|
|
489
|
+
appendSecretToEnv(rootDir, secret2);
|
|
490
|
+
consola.info("Generated NUXT_BETTER_AUTH_SECRET and added to .env (CI/test mode)");
|
|
491
|
+
return secret2;
|
|
492
|
+
}
|
|
493
|
+
consola.box("NUXT_BETTER_AUTH_SECRET is required for authentication.\nThis will be appended to your .env file.\nBETTER_AUTH_SECRET is still supported as a fallback.");
|
|
494
|
+
const choice = await consola.prompt("How do you want to set it?", {
|
|
495
|
+
type: "select",
|
|
496
|
+
options: [
|
|
497
|
+
{ label: "Generate for me", value: "generate", hint: "uses crypto.randomBytes(32)" },
|
|
498
|
+
{ label: "Enter manually", value: "paste" },
|
|
499
|
+
{ label: "Skip", value: "skip", hint: "will fail in production" }
|
|
500
|
+
],
|
|
501
|
+
cancel: "null"
|
|
502
|
+
});
|
|
503
|
+
if (typeof choice === "symbol" || choice === "skip") {
|
|
504
|
+
consola.warn("Skipping NUXT_BETTER_AUTH_SECRET. Auth will fail without it in production.");
|
|
505
|
+
return void 0;
|
|
506
|
+
}
|
|
507
|
+
let secret;
|
|
508
|
+
if (choice === "generate") {
|
|
509
|
+
secret = generateSecret();
|
|
510
|
+
} else {
|
|
511
|
+
const input = await consola.prompt("Paste your secret (min 32 chars):", { type: "text", cancel: "null" });
|
|
512
|
+
if (typeof input === "symbol" || !input || input.length < 32) {
|
|
513
|
+
consola.warn("Invalid secret. Skipping.");
|
|
514
|
+
return void 0;
|
|
239
515
|
}
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
516
|
+
secret = input;
|
|
517
|
+
}
|
|
518
|
+
const preview = `${secret.slice(0, 8)}...${secret.slice(-4)}`;
|
|
519
|
+
const confirm = await consola.prompt(`Add to .env:
|
|
520
|
+
${DEFAULT_SECRET_ENV}=${preview}
|
|
521
|
+
Proceed?`, { type: "confirm", initial: true, cancel: "null" });
|
|
522
|
+
if (typeof confirm === "symbol" || !confirm) {
|
|
523
|
+
consola.info("Cancelled. Secret not written.");
|
|
524
|
+
return void 0;
|
|
525
|
+
}
|
|
526
|
+
appendSecretToEnv(rootDir, secret);
|
|
527
|
+
consola.success("Added NUXT_BETTER_AUTH_SECRET to .env");
|
|
528
|
+
return secret;
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
function resolveDatabaseProvider(input) {
|
|
532
|
+
const enabledProviders = Object.entries(input.providers).filter(([_id, provider]) => provider.isEnabled?.(input.context) ?? true);
|
|
533
|
+
if (!enabledProviders.length) {
|
|
534
|
+
throw new Error("[nuxt-better-auth] No database provider is enabled. Register one with the better-auth:database:providers hook.");
|
|
535
|
+
}
|
|
536
|
+
enabledProviders.sort((a, b) => (b[1].priority ?? 0) - (a[1].priority ?? 0));
|
|
537
|
+
const [id, definition] = enabledProviders[0];
|
|
538
|
+
return { id, definition };
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
function resolveSecondaryStorage(input) {
|
|
542
|
+
const { options, clientOnly, hasNuxtHub, hub } = input;
|
|
543
|
+
const opt = options.hubSecondaryStorage ?? false;
|
|
544
|
+
const useHubKV = opt === true;
|
|
545
|
+
const secondaryStorageEnabled = opt === true || opt === "custom";
|
|
546
|
+
if (secondaryStorageEnabled && clientOnly) {
|
|
547
|
+
throw new Error("[nuxt-better-auth] hubSecondaryStorage is not available in clientOnly mode. Either disable clientOnly or remove auth.hubSecondaryStorage.");
|
|
548
|
+
}
|
|
549
|
+
if (useHubKV && (!hasNuxtHub || !hub?.kv)) {
|
|
550
|
+
throw new Error("[nuxt-better-auth] hubSecondaryStorage: true requires @nuxthub/core with hub.kv: true. Either add hub.kv: true to your nuxt.config or remove auth.hubSecondaryStorage.");
|
|
551
|
+
}
|
|
552
|
+
return { useHubKV, secondaryStorageEnabled };
|
|
553
|
+
}
|
|
554
|
+
function setupRuntimeConfig(input) {
|
|
555
|
+
const { nuxt, options, clientOnly, databaseProvider, consola } = input;
|
|
556
|
+
const { useHubKV, secondaryStorageEnabled } = resolveSecondaryStorage(input);
|
|
557
|
+
nuxt.options.runtimeConfig.public = nuxt.options.runtimeConfig.public || {};
|
|
558
|
+
const configuredSiteUrl = nuxt.options.runtimeConfig.public.siteUrl;
|
|
559
|
+
if (!configuredSiteUrl && process.env.NUXT_PUBLIC_SITE_URL)
|
|
560
|
+
nuxt.options.runtimeConfig.public.siteUrl = process.env.NUXT_PUBLIC_SITE_URL;
|
|
561
|
+
nuxt.options.runtimeConfig.public.auth = defu(nuxt.options.runtimeConfig.public.auth, {
|
|
562
|
+
redirects: {
|
|
563
|
+
login: options.redirects?.login ?? "/login",
|
|
564
|
+
guest: options.redirects?.guest ?? "/",
|
|
565
|
+
authenticated: options.redirects?.authenticated,
|
|
566
|
+
logout: options.redirects?.logout
|
|
567
|
+
},
|
|
568
|
+
preserveRedirect: options.preserveRedirect ?? true,
|
|
569
|
+
redirectQueryKey: options.redirectQueryKey ?? "redirect",
|
|
570
|
+
useDatabase: databaseProvider !== "none",
|
|
571
|
+
databaseProvider,
|
|
572
|
+
clientOnly,
|
|
573
|
+
session: {
|
|
574
|
+
skipHydratedSsrGetSession: options.session?.skipHydratedSsrGetSession ?? false
|
|
250
575
|
}
|
|
251
|
-
|
|
576
|
+
});
|
|
577
|
+
if (clientOnly) {
|
|
578
|
+
const siteUrl = nuxt.options.runtimeConfig.public.siteUrl;
|
|
579
|
+
if (!siteUrl)
|
|
580
|
+
consola.warn("clientOnly mode: set runtimeConfig.public.siteUrl (or NUXT_PUBLIC_SITE_URL) to your frontend URL");
|
|
581
|
+
consola.info("clientOnly mode enabled - server utilities (serverAuth, getRequestSession, getUserSession, requireUserSession) are not available");
|
|
582
|
+
return { useHubKV, secondaryStorageEnabled };
|
|
583
|
+
}
|
|
584
|
+
const currentSecret = nuxt.options.runtimeConfig.betterAuthSecret;
|
|
585
|
+
nuxt.options.runtimeConfig.betterAuthSecret = currentSecret || process.env.NUXT_BETTER_AUTH_SECRET || process.env.BETTER_AUTH_SECRET || "";
|
|
586
|
+
const betterAuthSecret = nuxt.options.runtimeConfig.betterAuthSecret;
|
|
587
|
+
if (!nuxt.options.dev && !nuxt.options._prepare && !betterAuthSecret) {
|
|
588
|
+
throw new Error("[nuxt-better-auth] NUXT_BETTER_AUTH_SECRET is required in production. Set NUXT_BETTER_AUTH_SECRET or BETTER_AUTH_SECRET environment variable.");
|
|
589
|
+
}
|
|
590
|
+
if (betterAuthSecret && betterAuthSecret.length < 32) {
|
|
591
|
+
throw new Error("[nuxt-better-auth] NUXT_BETTER_AUTH_SECRET must be at least 32 characters for security");
|
|
592
|
+
}
|
|
593
|
+
nuxt.options.runtimeConfig.auth = defu(nuxt.options.runtimeConfig.auth, {
|
|
594
|
+
hubSecondaryStorage: options.hubSecondaryStorage ?? false
|
|
595
|
+
});
|
|
596
|
+
return { useHubKV, secondaryStorageEnabled };
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
function buildSecondaryStorageCode(useHubKV) {
|
|
600
|
+
if (!useHubKV)
|
|
601
|
+
return "export function createSecondaryStorage() { return undefined }";
|
|
602
|
+
return `import { kv } from '@nuxthub/kv'
|
|
252
603
|
export function createSecondaryStorage() {
|
|
253
604
|
return {
|
|
254
605
|
get: async (key) => kv.get(\`_auth:\${key}\`),
|
|
255
606
|
set: async (key, value, ttl) => kv.set(\`_auth:\${key}\`, value, { ttl }),
|
|
256
607
|
delete: async (key) => kv.del(\`_auth:\${key}\`),
|
|
257
608
|
}
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
if (
|
|
263
|
-
|
|
609
|
+
}`;
|
|
610
|
+
}
|
|
611
|
+
function buildDatabaseCode(input) {
|
|
612
|
+
if (input.provider === "nuxthub") {
|
|
613
|
+
if (input.hubDialect === "postgresql") {
|
|
614
|
+
return `import { db } from '@nuxthub/db'
|
|
615
|
+
import * as schema from './schema.${input.hubDialect}.mjs'
|
|
616
|
+
import { drizzleAdapter } from 'better-auth/adapters/drizzle'
|
|
617
|
+
import { drizzle } from 'drizzle-orm/postgres-js'
|
|
618
|
+
import postgres from 'postgres'
|
|
619
|
+
|
|
620
|
+
const dialect = 'pg'
|
|
621
|
+
const requestDatabaseKey = Symbol.for('nuxt-better-auth.requestDatabase')
|
|
622
|
+
const fallbackRequestDatabaseContext = new WeakMap()
|
|
623
|
+
|
|
624
|
+
function getRequestDatabaseContext(event) {
|
|
625
|
+
const eventWithContext = event
|
|
626
|
+
if (eventWithContext?.context && typeof eventWithContext.context === 'object')
|
|
627
|
+
return eventWithContext.context
|
|
628
|
+
|
|
629
|
+
let context = fallbackRequestDatabaseContext.get(event)
|
|
630
|
+
if (!context) {
|
|
631
|
+
context = {}
|
|
632
|
+
fallbackRequestDatabaseContext.set(event, context)
|
|
633
|
+
}
|
|
634
|
+
return context
|
|
635
|
+
}
|
|
636
|
+
|
|
637
|
+
function createHyperdriveAdapter(client) {
|
|
638
|
+
return drizzleAdapter(drizzle({ client, schema }), { provider: dialect, schema, usePlural: ${input.usePlural}, camelCase: ${input.camelCase} })
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
function getCloudflareContext(event) {
|
|
642
|
+
return event?.context?.cloudflare
|
|
643
|
+
}
|
|
644
|
+
|
|
645
|
+
function resolveHyperdrive(event) {
|
|
646
|
+
const cloudflareEnv = getCloudflareContext(event)?.env
|
|
647
|
+
return cloudflareEnv?.POSTGRES || process.env.POSTGRES || globalThis.__env__?.POSTGRES || globalThis.POSTGRES
|
|
648
|
+
}
|
|
649
|
+
|
|
650
|
+
function scheduleCleanup(event, cleanup) {
|
|
651
|
+
const cloudflareContext = getCloudflareContext(event)
|
|
652
|
+
if (typeof cloudflareContext?.context?.waitUntil === 'function') {
|
|
653
|
+
cloudflareContext.context.waitUntil(cleanup)
|
|
654
|
+
return
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
if (typeof event?.context?.waitUntil === 'function') {
|
|
658
|
+
event.context.waitUntil(cleanup)
|
|
659
|
+
return
|
|
660
|
+
}
|
|
661
|
+
|
|
662
|
+
const waitUntil = event?.waitUntil || event?.req?.waitUntil || event?.node?.req?.waitUntil
|
|
663
|
+
if (typeof waitUntil === 'function')
|
|
664
|
+
waitUntil.call(event?.req || event?.node?.req || event, cleanup)
|
|
665
|
+
else
|
|
666
|
+
void cleanup
|
|
667
|
+
}
|
|
668
|
+
|
|
669
|
+
function registerClientCleanup(event, client) {
|
|
670
|
+
const response = event?.node?.res
|
|
671
|
+
if (!response || typeof response.once !== 'function')
|
|
672
|
+
return
|
|
673
|
+
|
|
674
|
+
let closed = false
|
|
675
|
+
|
|
676
|
+
const cleanup = () => {
|
|
677
|
+
if (closed)
|
|
678
|
+
return
|
|
679
|
+
|
|
680
|
+
closed = true
|
|
681
|
+
const close = client.end({ timeout: 0 }).catch(() => {})
|
|
682
|
+
scheduleCleanup(event, close)
|
|
683
|
+
}
|
|
684
|
+
|
|
685
|
+
response.once('finish', cleanup)
|
|
686
|
+
response.once('close', cleanup)
|
|
687
|
+
}
|
|
688
|
+
|
|
689
|
+
export function createDatabase(event) {
|
|
690
|
+
const hyperdrive = resolveHyperdrive(event)
|
|
691
|
+
if (!hyperdrive?.connectionString)
|
|
692
|
+
return drizzleAdapter(db, { provider: dialect, schema, usePlural: ${input.usePlural}, camelCase: ${input.camelCase} })
|
|
693
|
+
|
|
694
|
+
if (event) {
|
|
695
|
+
const context = getRequestDatabaseContext(event)
|
|
696
|
+
const cached = context[requestDatabaseKey]
|
|
697
|
+
if (cached)
|
|
698
|
+
return cached
|
|
699
|
+
|
|
700
|
+
const client = postgres(hyperdrive.connectionString, {
|
|
701
|
+
prepare: false,
|
|
702
|
+
onnotice: () => {},
|
|
703
|
+
max: 1,
|
|
704
|
+
})
|
|
705
|
+
const database = createHyperdriveAdapter(client)
|
|
706
|
+
|
|
707
|
+
context[requestDatabaseKey] = database
|
|
708
|
+
registerClientCleanup(event, client)
|
|
709
|
+
return database
|
|
710
|
+
}
|
|
711
|
+
const client = postgres(hyperdrive.connectionString, {
|
|
712
|
+
prepare: false,
|
|
713
|
+
onnotice: () => {},
|
|
714
|
+
max: 1,
|
|
715
|
+
})
|
|
716
|
+
|
|
717
|
+
return createHyperdriveAdapter(client)
|
|
718
|
+
}
|
|
719
|
+
export { db }`;
|
|
264
720
|
}
|
|
265
|
-
|
|
266
|
-
|
|
721
|
+
return `import { db } from '@nuxthub/db'
|
|
722
|
+
import * as schema from './schema.${input.hubDialect}.mjs'
|
|
267
723
|
import { drizzleAdapter } from 'better-auth/adapters/drizzle'
|
|
268
|
-
const rawDialect = '${hubDialect}'
|
|
724
|
+
const rawDialect = '${input.hubDialect}'
|
|
269
725
|
const dialect = rawDialect === 'postgresql' ? 'pg' : rawDialect
|
|
270
|
-
export function createDatabase() { return drizzleAdapter(db, { provider: dialect, schema }) }
|
|
271
|
-
export { db }
|
|
726
|
+
export function createDatabase() { return drizzleAdapter(db, { provider: dialect, schema, usePlural: ${input.usePlural}, camelCase: ${input.camelCase} }) }
|
|
727
|
+
export { db }`;
|
|
728
|
+
}
|
|
729
|
+
return `export function createDatabase() { return undefined }
|
|
272
730
|
export const db = undefined`;
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
731
|
+
}
|
|
732
|
+
function buildSchemaExportCode(hasHubDb, hubDialect) {
|
|
733
|
+
if (!hasHubDb)
|
|
734
|
+
return "export const schema = undefined\n";
|
|
735
|
+
return `export * from './schema.${hubDialect}.mjs'
|
|
736
|
+
import * as schema from './schema.${hubDialect}.mjs'
|
|
737
|
+
export { schema }
|
|
738
|
+
`;
|
|
739
|
+
}
|
|
740
|
+
function buildAuthRouteRulesCode(authRouteRules) {
|
|
741
|
+
return `export const authRouteRules = ${JSON.stringify(authRouteRules, null, 2)}
|
|
742
|
+
`;
|
|
743
|
+
}
|
|
744
|
+
|
|
745
|
+
function assertConfigPresence(configs, clientOnly) {
|
|
746
|
+
if (!clientOnly && !configs.server.exists)
|
|
747
|
+
throw new Error(`[nuxt-better-auth] Missing ${configs.server.file}.ts - export default defineServerAuth(...)`);
|
|
748
|
+
if (!configs.client.exists)
|
|
749
|
+
throw new Error(`[nuxt-better-auth] Missing ${configs.client.file}.ts - export default defineClientAuth(...)`);
|
|
750
|
+
}
|
|
751
|
+
function createDefaultDatabaseProviders(buildContext) {
|
|
752
|
+
return {
|
|
753
|
+
nuxthub: {
|
|
754
|
+
priority: 100,
|
|
755
|
+
isEnabled: ({ hasHubDbAvailable: enabled }) => enabled,
|
|
756
|
+
buildDatabaseCode: () => buildDatabaseCode({
|
|
757
|
+
provider: "nuxthub",
|
|
758
|
+
...buildContext
|
|
759
|
+
})
|
|
760
|
+
},
|
|
761
|
+
none: {
|
|
762
|
+
priority: 0,
|
|
763
|
+
buildDatabaseCode: () => buildDatabaseCode({
|
|
764
|
+
provider: "none",
|
|
765
|
+
...buildContext
|
|
766
|
+
})
|
|
767
|
+
}
|
|
768
|
+
};
|
|
769
|
+
}
|
|
770
|
+
function collectAuthRouteRules(nuxt) {
|
|
771
|
+
const runtimeRouteRulesSource = nuxt.options.nitro?.routeRules || nuxt.options.routeRules || {};
|
|
772
|
+
return Object.fromEntries(
|
|
773
|
+
Object.entries(runtimeRouteRulesSource).flatMap(([path, rule]) => {
|
|
774
|
+
if (!rule || typeof rule !== "object" || !("auth" in rule))
|
|
775
|
+
return [];
|
|
776
|
+
return [[path, { auth: rule.auth }]];
|
|
777
|
+
})
|
|
778
|
+
);
|
|
779
|
+
}
|
|
780
|
+
async function resolveAuthModuleSetup(input, dependencies = {}) {
|
|
781
|
+
const { nuxt, options, runtimeTypesAugmentPath, consola } = input;
|
|
782
|
+
const hasNuxtModuleFn = dependencies.hasNuxtModule ?? hasNuxtModule;
|
|
783
|
+
const clientOnly = options.clientOnly ?? false;
|
|
784
|
+
const configs = resolveAuthConfigDescriptors(nuxt, {
|
|
785
|
+
server: options.serverConfig,
|
|
786
|
+
client: options.clientConfig
|
|
787
|
+
}, {
|
|
788
|
+
configExists: dependencies.configExists
|
|
789
|
+
});
|
|
790
|
+
assertConfigPresence(configs, clientOnly);
|
|
791
|
+
const aliases = {
|
|
792
|
+
"#nuxt-better-auth": runtimeTypesAugmentPath,
|
|
793
|
+
"#auth/server": clientOnly ? void 0 : configs.server.path,
|
|
794
|
+
"#auth/client": configs.client.path
|
|
795
|
+
};
|
|
796
|
+
nuxt.options.alias["#nuxt-better-auth"] = aliases["#nuxt-better-auth"];
|
|
797
|
+
if (aliases["#auth/server"])
|
|
798
|
+
nuxt.options.alias["#auth/server"] = aliases["#auth/server"];
|
|
799
|
+
nuxt.options.alias["#auth/client"] = aliases["#auth/client"];
|
|
800
|
+
const hasNuxtHub = hasNuxtModuleFn("@nuxthub/core", nuxt);
|
|
801
|
+
const hub = hasNuxtHub ? nuxt.options.hub : void 0;
|
|
802
|
+
const hasHubDbAvailable = !clientOnly && hasNuxtHub && !!hub?.db;
|
|
803
|
+
const hubDialect = getHubDialect(hub) ?? "sqlite";
|
|
804
|
+
const usePlural = options.schema?.usePlural ?? false;
|
|
805
|
+
const camelCase = (options.schema?.casing ?? getHubCasing(hub)) !== "snake_case";
|
|
806
|
+
let providerId = "none";
|
|
807
|
+
let providerDefinition;
|
|
808
|
+
if (!clientOnly) {
|
|
809
|
+
const buildContext = { hubDialect, usePlural, camelCase };
|
|
810
|
+
const providers = createDefaultDatabaseProviders(buildContext);
|
|
811
|
+
const enabledContext = {
|
|
812
|
+
nuxt,
|
|
813
|
+
options,
|
|
814
|
+
clientOnly,
|
|
815
|
+
hasHubDbAvailable
|
|
816
|
+
};
|
|
817
|
+
await nuxt.callHook("better-auth:database:providers", providers);
|
|
818
|
+
const resolvedProvider = resolveDatabaseProvider({
|
|
819
|
+
providers,
|
|
820
|
+
context: enabledContext
|
|
821
|
+
});
|
|
822
|
+
providerId = resolvedProvider.id;
|
|
823
|
+
providerDefinition = resolvedProvider.definition;
|
|
824
|
+
}
|
|
825
|
+
const runtime = setupRuntimeConfig({
|
|
826
|
+
nuxt,
|
|
827
|
+
options,
|
|
828
|
+
clientOnly,
|
|
829
|
+
databaseProvider: providerId,
|
|
830
|
+
hasNuxtHub,
|
|
831
|
+
hub,
|
|
832
|
+
consola
|
|
833
|
+
});
|
|
834
|
+
if (runtime.useHubKV && !nuxt.options.alias["hub:kv"]) {
|
|
835
|
+
throw new Error("[nuxt-better-auth] hub:kv not found. Ensure @nuxthub/core is loaded before this module and hub.kv is enabled.");
|
|
836
|
+
}
|
|
837
|
+
const hasHubDb = providerId === "nuxthub";
|
|
838
|
+
if (hasHubDb && !nuxt.options.alias["hub:db"]) {
|
|
839
|
+
throw new Error("[nuxt-better-auth] hub:db not found. Ensure @nuxthub/core is loaded before this module and hub.db is configured.");
|
|
840
|
+
}
|
|
841
|
+
return {
|
|
842
|
+
clientOnly,
|
|
843
|
+
configs,
|
|
844
|
+
aliases,
|
|
845
|
+
hub: {
|
|
846
|
+
hasNuxtHub,
|
|
847
|
+
options: hub,
|
|
848
|
+
hasHubDbAvailable
|
|
849
|
+
},
|
|
850
|
+
database: {
|
|
851
|
+
providerId,
|
|
852
|
+
hasHubDb,
|
|
853
|
+
providerDefinition,
|
|
854
|
+
buildContext: clientOnly ? void 0 : { hubDialect, usePlural, camelCase }
|
|
855
|
+
},
|
|
856
|
+
runtime,
|
|
857
|
+
prepareTypes: clientOnly ? void 0 : {
|
|
858
|
+
serverDir: dirname(configs.server.path),
|
|
859
|
+
hasHubDb
|
|
860
|
+
},
|
|
861
|
+
serverTypes: clientOnly ? void 0 : {
|
|
862
|
+
serverConfigPath: configs.server.path,
|
|
863
|
+
hasHubDb
|
|
864
|
+
},
|
|
865
|
+
sharedTypes: {
|
|
866
|
+
clientConfigPath: configs.client.path
|
|
867
|
+
},
|
|
868
|
+
schemaGeneration: hasHubDb ? {
|
|
869
|
+
serverConfigPath: configs.server.path,
|
|
870
|
+
hubSecondaryStorage: options.hubSecondaryStorage ?? false,
|
|
871
|
+
externalizeNuxtHubDatabase: true
|
|
872
|
+
} : void 0
|
|
873
|
+
};
|
|
874
|
+
}
|
|
875
|
+
|
|
876
|
+
function registerServerTypeTemplates(input) {
|
|
877
|
+
const { serverConfigPath, hasHubDb, runtimeTypesPath, sharedServerConfigSafe } = input;
|
|
878
|
+
const serverConfigTypeTemplateOptions = sharedServerConfigSafe ? { nuxt: true, nitro: true, node: true, shared: true } : { nuxt: true, nitro: true, node: true };
|
|
879
|
+
addTypeTemplate({
|
|
880
|
+
filename: "types/auth-secondary-storage.d.ts",
|
|
881
|
+
getContents: () => `
|
|
278
882
|
declare module '#auth/secondary-storage' {
|
|
279
883
|
interface SecondaryStorage {
|
|
280
884
|
get: (key: string) => Promise<string | null>
|
|
@@ -284,163 +888,492 @@ declare module '#auth/secondary-storage' {
|
|
|
284
888
|
export function createSecondaryStorage(): SecondaryStorage | undefined
|
|
285
889
|
}
|
|
286
890
|
`
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
891
|
+
}, { nitro: true });
|
|
892
|
+
addTypeTemplate({
|
|
893
|
+
filename: "types/auth-database.d.ts",
|
|
894
|
+
getContents: () => `
|
|
291
895
|
declare module '#auth/database' {
|
|
292
|
-
import type {
|
|
293
|
-
export function createDatabase():
|
|
294
|
-
export const db:
|
|
896
|
+
import type { BetterAuthOptions } from 'better-auth'
|
|
897
|
+
export function createDatabase(event?: import('h3').H3Event): BetterAuthOptions['database']
|
|
898
|
+
export const db: ${hasHubDb ? `typeof import('@nuxthub/db')['db']` : "undefined"}
|
|
295
899
|
}
|
|
296
900
|
`
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
export
|
|
901
|
+
}, { nitro: true });
|
|
902
|
+
addTypeTemplate({
|
|
903
|
+
filename: "types/auth-schema.d.ts",
|
|
904
|
+
getContents: () => `
|
|
905
|
+
declare module '#auth/schema' {
|
|
906
|
+
export const user: any
|
|
907
|
+
export const session: any
|
|
908
|
+
export const account: any
|
|
909
|
+
export const verification: any
|
|
910
|
+
export const schema: {
|
|
911
|
+
user: any
|
|
912
|
+
session: any
|
|
913
|
+
account: any
|
|
914
|
+
verification: any
|
|
915
|
+
[key: string]: any
|
|
916
|
+
} | undefined
|
|
917
|
+
}
|
|
303
918
|
`
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
919
|
+
}, { nitro: true });
|
|
920
|
+
addTypeTemplate({
|
|
921
|
+
filename: "types/nuxt-better-auth-server-context.d.ts",
|
|
922
|
+
getContents: () => `
|
|
923
|
+
/// <reference path="./nitro-imports.d.ts" />
|
|
924
|
+
/// <reference path="./auth-database.d.ts" />
|
|
925
|
+
/// <reference path="./auth-schema.d.ts" />
|
|
926
|
+
/// <reference path="./auth-secondary-storage.d.ts" />
|
|
927
|
+
${hasHubDb ? '/// <reference path="../hub/db.d.ts" />' : ""}
|
|
311
928
|
|
|
312
|
-
|
|
929
|
+
export {}
|
|
930
|
+
`
|
|
931
|
+
}, { node: true });
|
|
932
|
+
addTypeTemplate({
|
|
933
|
+
filename: "types/nuxt-better-auth-config-context.d.ts",
|
|
934
|
+
getContents: () => `
|
|
935
|
+
import type { RuntimeConfig } from 'nuxt/schema'
|
|
313
936
|
|
|
314
|
-
declare module '
|
|
315
|
-
interface
|
|
316
|
-
interface AuthSession { session: InferSession<_Config>['session'], user: InferUser<_Config> }
|
|
317
|
-
interface ServerAuthContext {
|
|
937
|
+
declare module '@onmax/nuxt-better-auth/config' {
|
|
938
|
+
interface ServerAuthContextExtension {
|
|
318
939
|
runtimeConfig: RuntimeConfig
|
|
319
|
-
${hasHubDb ? `
|
|
940
|
+
db: ${hasHubDb ? `typeof import('@nuxthub/db')['db']` : "undefined"}
|
|
941
|
+
requestOrigin?: string
|
|
320
942
|
}
|
|
321
943
|
}
|
|
944
|
+
|
|
322
945
|
`
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
import type {
|
|
946
|
+
}, { nuxt: true, nitro: true, node: true, shared: true });
|
|
947
|
+
addTypeTemplate({
|
|
948
|
+
filename: "types/nuxt-better-auth-infer.d.ts",
|
|
949
|
+
getContents: () => `
|
|
950
|
+
import type { BetterAuthOptions, BetterAuthPlugin, InferPluginTypes, UnionToIntersection } from 'better-auth'
|
|
951
|
+
import type { InferFieldsOutput } from 'better-auth/db'
|
|
952
|
+
import type createServerAuth from '${serverConfigPath}'
|
|
953
|
+
|
|
954
|
+
type _RawConfig = ReturnType<typeof createServerAuth>
|
|
955
|
+
type _RawPlugins = _RawConfig extends { plugins: infer P } ? P : _RawConfig extends { plugins?: infer P } ? P : []
|
|
956
|
+
type _NormalizedPlugins = _RawPlugins extends readonly (infer T)[]
|
|
957
|
+
? Array<T & BetterAuthPlugin>
|
|
958
|
+
: _RawPlugins extends (infer T)[]
|
|
959
|
+
? Array<T & BetterAuthPlugin>
|
|
960
|
+
: BetterAuthPlugin[]
|
|
961
|
+
type _Config = Omit<BetterAuthOptions, 'plugins'> & Omit<_RawConfig, 'plugins'> & {
|
|
962
|
+
plugins?: _NormalizedPlugins
|
|
963
|
+
}
|
|
964
|
+
|
|
965
|
+
type _InferModelFieldsFromPlugins<P, M extends string> = P extends readonly (infer Plugin)[]
|
|
966
|
+
? UnionToIntersection<Plugin extends { schema: { [K in M]: { fields: infer F } } } ? InferFieldsOutput<F> : {}>
|
|
967
|
+
: P extends (infer Plugin)[]
|
|
968
|
+
? UnionToIntersection<Plugin extends { schema: { [K in M]: { fields: infer F } } } ? InferFieldsOutput<F> : {}>
|
|
969
|
+
: {}
|
|
970
|
+
|
|
971
|
+
type _InferModelFieldsFromOptions<C, M extends 'user' | 'session'> = C extends { [K in M]: { additionalFields: infer F } }
|
|
972
|
+
? InferFieldsOutput<F>
|
|
973
|
+
: {}
|
|
974
|
+
|
|
975
|
+
type _UserFallback = _InferModelFieldsFromPlugins<_RawPlugins, 'user'> & _InferModelFieldsFromOptions<_RawConfig, 'user'>
|
|
976
|
+
type _SessionFallback = _InferModelFieldsFromPlugins<_RawPlugins, 'session'> & _InferModelFieldsFromOptions<_RawConfig, 'session'>
|
|
977
|
+
|
|
328
978
|
declare module '#nuxt-better-auth' {
|
|
329
|
-
|
|
979
|
+
interface AuthUser extends _UserFallback {}
|
|
980
|
+
interface AuthSession extends _SessionFallback {}
|
|
981
|
+
type PluginTypes = InferPluginTypes<_Config>
|
|
330
982
|
}
|
|
331
983
|
`
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
984
|
+
}, serverConfigTypeTemplateOptions);
|
|
985
|
+
addTypeTemplate({
|
|
986
|
+
filename: "types/nuxt-better-auth-social-providers.d.ts",
|
|
987
|
+
getContents: () => `
|
|
988
|
+
import type createServerAuth from '${serverConfigPath}'
|
|
989
|
+
|
|
990
|
+
type _RawConfig = ReturnType<typeof createServerAuth>
|
|
991
|
+
type _RawSocialProviders = _RawConfig extends { socialProviders: infer S } ? S : _RawConfig extends { socialProviders?: infer S } ? S : {}
|
|
992
|
+
type _SocialProviderIds = Extract<keyof NonNullable<_RawSocialProviders>, string>
|
|
993
|
+
|
|
994
|
+
declare module '#nuxt-better-auth' {
|
|
995
|
+
interface AuthSocialProviderRegistry {
|
|
996
|
+
ids: _SocialProviderIds
|
|
997
|
+
}
|
|
998
|
+
}
|
|
999
|
+
`
|
|
1000
|
+
}, serverConfigTypeTemplateOptions);
|
|
1001
|
+
addTypeTemplate({
|
|
1002
|
+
filename: "types/nuxt-better-auth-nitro.d.ts",
|
|
1003
|
+
getContents: () => `
|
|
1004
|
+
import type createServerAuth from '${serverConfigPath}'
|
|
1005
|
+
import type { BetterAuthOptions } from 'better-auth'
|
|
1006
|
+
import type { getEndpoints } from 'better-auth/api'
|
|
1007
|
+
import type { Serialize, Simplify } from 'nitropack/types'
|
|
1008
|
+
import type { FetchError } from 'ofetch'
|
|
1009
|
+
|
|
1010
|
+
type _RawConfig = ReturnType<typeof createServerAuth>
|
|
1011
|
+
type _RawPlugins = _RawConfig extends { plugins: infer P } ? P : _RawConfig extends { plugins?: infer P } ? P : []
|
|
1012
|
+
type _Config = Omit<BetterAuthOptions, 'plugins'> & Omit<_RawConfig, 'plugins'> & {
|
|
1013
|
+
plugins?: _RawPlugins
|
|
1014
|
+
}
|
|
1015
|
+
|
|
1016
|
+
type _AuthApi = ReturnType<typeof getEndpoints<_Config>>['api']
|
|
1017
|
+
type _NormalizeMethod<M extends string> = M extends '*' ? 'default' : Lowercase<M>
|
|
1018
|
+
type _RouteMethodFromOption<M> = M extends readonly (infer T)[]
|
|
1019
|
+
? _NormalizeMethod<Extract<T, string>>
|
|
1020
|
+
: M extends string
|
|
1021
|
+
? _NormalizeMethod<M>
|
|
1022
|
+
: 'default'
|
|
1023
|
+
type _RouteMethodFromEndpoint<E> = E extends { options: { method: infer M } } ? _RouteMethodFromOption<M> : 'default'
|
|
1024
|
+
type _RoutePathFromEndpoint<E> = E extends { path: infer P extends string }
|
|
1025
|
+
? string extends P
|
|
1026
|
+
? never
|
|
1027
|
+
: \`/api/auth\${P}\`
|
|
1028
|
+
: never
|
|
1029
|
+
type _RouteResponseFromEndpoint<E> = E extends (...args: any[]) => Promise<infer R> ? Simplify<Serialize<Awaited<R>>> : never
|
|
1030
|
+
type _RouteDefaultResponse<E> = never
|
|
1031
|
+
type _UnionToIntersection<U> = (U extends unknown ? (value: U) => void : never) extends (value: infer I) => void ? I : never
|
|
1032
|
+
|
|
1033
|
+
type _CoreAuthInternalApi = {
|
|
1034
|
+
[K in keyof _AuthApi as _RoutePathFromEndpoint<_AuthApi[K]>]: {
|
|
1035
|
+
[M in _RouteMethodFromEndpoint<_AuthApi[K]> | 'default']: M extends 'default' ? _RouteDefaultResponse<_AuthApi[K]> : _RouteResponseFromEndpoint<_AuthApi[K]>
|
|
1036
|
+
}
|
|
1037
|
+
}
|
|
1038
|
+
type _PluginEndpointMaps<Plugins> = Plugins extends readonly (infer Plugin)[]
|
|
1039
|
+
? Plugin extends { endpoints: infer Endpoints extends Record<string, unknown> }
|
|
1040
|
+
? {
|
|
1041
|
+
[K in keyof Endpoints as _RoutePathFromEndpoint<Endpoints[K]>]: {
|
|
1042
|
+
[M in _RouteMethodFromEndpoint<Endpoints[K]> | 'default']: M extends 'default' ? _RouteDefaultResponse<Endpoints[K]> : _RouteResponseFromEndpoint<Endpoints[K]>
|
|
1043
|
+
}
|
|
1044
|
+
}
|
|
1045
|
+
: {}
|
|
1046
|
+
: Plugins extends (infer Plugin)[]
|
|
1047
|
+
? Plugin extends { endpoints: infer Endpoints extends Record<string, unknown> }
|
|
1048
|
+
? {
|
|
1049
|
+
[K in keyof Endpoints as _RoutePathFromEndpoint<Endpoints[K]>]: {
|
|
1050
|
+
[M in _RouteMethodFromEndpoint<Endpoints[K]> | 'default']: M extends 'default' ? _RouteDefaultResponse<Endpoints[K]> : _RouteResponseFromEndpoint<Endpoints[K]>
|
|
1051
|
+
}
|
|
1052
|
+
}
|
|
1053
|
+
: {}
|
|
1054
|
+
: {}
|
|
1055
|
+
type _PluginAuthInternalApi = _UnionToIntersection<_PluginEndpointMaps<_RawPlugins>>
|
|
1056
|
+
type _GeneratedAuthInternalApi = _CoreAuthInternalApi & _PluginAuthInternalApi
|
|
1057
|
+
|
|
1058
|
+
type _RoutePathToRequestPath<Path extends string> = Path extends \`\${infer Prefix}:\${string}/\${infer Rest}\`
|
|
1059
|
+
? \`\${Prefix}\${string}/\${_RoutePathToRequestPath<Rest>}\`
|
|
1060
|
+
: Path extends \`\${infer Prefix}:\${string}\`
|
|
1061
|
+
? \`\${Prefix}\${string}\`
|
|
1062
|
+
: Path
|
|
1063
|
+
type _AuthApiPatternPath = Extract<keyof _GeneratedAuthInternalApi, string>
|
|
1064
|
+
type _AuthApiRequestPath = _RoutePathToRequestPath<_AuthApiPatternPath>
|
|
1065
|
+
type _AuthPatternFromRequestPath<Path extends string> = {
|
|
1066
|
+
[Pattern in _AuthApiPatternPath]: Path extends _RoutePathToRequestPath<Pattern> ? Pattern : never
|
|
1067
|
+
}[_AuthApiPatternPath]
|
|
1068
|
+
type _AuthEndpointMethod<Path extends _AuthApiRequestPath> = Extract<keyof _GeneratedAuthInternalApi[_AuthPatternFromRequestPath<Path>], string>
|
|
1069
|
+
|
|
1070
|
+
type _AuthFetchMethod<Path extends _AuthApiRequestPath> = Extract<Exclude<import('nitropack/types').NitroFetchOptions<Path>['method'], undefined>, string>
|
|
1071
|
+
type _AuthFetchDefaultMethod<Path extends _AuthApiRequestPath> = 'get'
|
|
1072
|
+
type _AuthFetchResolvedMethod<Path extends _AuthApiRequestPath, Method extends string> = Lowercase<Method> extends _AuthEndpointMethod<Path>
|
|
1073
|
+
? Lowercase<Method>
|
|
1074
|
+
: never
|
|
1075
|
+
type _AuthFetchResult<Path extends _AuthApiRequestPath, Method extends string> = _GeneratedAuthInternalApi[_AuthPatternFromRequestPath<Path>][_AuthFetchResolvedMethod<Path, Method>]
|
|
1076
|
+
|
|
1077
|
+
declare module '#nuxt-better-auth' {
|
|
1078
|
+
export type AuthApiInternalRoutes = _GeneratedAuthInternalApi
|
|
1079
|
+
export type AuthApiEndpointPatternPath = _AuthApiPatternPath
|
|
1080
|
+
export type AuthApiEndpointPath = _AuthApiRequestPath
|
|
1081
|
+
export type AuthApiEndpointMethod<Path extends AuthApiEndpointPath> = _AuthEndpointMethod<Path>
|
|
1082
|
+
export type AuthApiEndpointResponse<
|
|
1083
|
+
Path extends AuthApiEndpointPath,
|
|
1084
|
+
Method extends AuthApiEndpointMethod<Path> = AuthApiEndpointMethod<Path>,
|
|
1085
|
+
> = AuthApiInternalRoutes[_AuthPatternFromRequestPath<Path>][Method]
|
|
1086
|
+
}
|
|
1087
|
+
|
|
1088
|
+
declare module 'nuxt/dist/app/composables/fetch' {
|
|
1089
|
+
export function useFetch<
|
|
1090
|
+
ErrorT = FetchError,
|
|
1091
|
+
Path extends import('#nuxt-better-auth').AuthApiEndpointPath = import('#nuxt-better-auth').AuthApiEndpointPath,
|
|
1092
|
+
Method extends _AuthFetchMethod<Path> = _AuthFetchDefaultMethod<Path>,
|
|
1093
|
+
_ResT = _AuthFetchResult<Path, Method>,
|
|
1094
|
+
DataT = _ResT,
|
|
1095
|
+
PickKeys extends import('nuxt/dist/app/composables/asyncData').KeysOf<DataT> = import('nuxt/dist/app/composables/asyncData').KeysOf<DataT>,
|
|
1096
|
+
DefaultT = undefined,
|
|
1097
|
+
>(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>
|
|
1098
|
+
export function useFetch<
|
|
1099
|
+
ErrorT = FetchError,
|
|
1100
|
+
Path extends import('#nuxt-better-auth').AuthApiEndpointPath = import('#nuxt-better-auth').AuthApiEndpointPath,
|
|
1101
|
+
Method extends _AuthFetchMethod<Path> = _AuthFetchDefaultMethod<Path>,
|
|
1102
|
+
_ResT = _AuthFetchResult<Path, Method>,
|
|
1103
|
+
DataT = _ResT,
|
|
1104
|
+
PickKeys extends import('nuxt/dist/app/composables/asyncData').KeysOf<DataT> = import('nuxt/dist/app/composables/asyncData').KeysOf<DataT>,
|
|
1105
|
+
DefaultT = DataT,
|
|
1106
|
+
>(request: import('vue').Ref<Path> | Path | (() => Path), opts?: import('nuxt/dist/app/composables/fetch').UseFetchOptions<_ResT, DataT, PickKeys, DefaultT, Path, Method>): import('nuxt/dist/app/composables/asyncData').AsyncData<import('nuxt/dist/app/composables/asyncData').PickFrom<DataT, PickKeys> | DefaultT, ErrorT | undefined>
|
|
1107
|
+
export function useFetch(request: string | import('vue').Ref<string> | (() => string), opts?: any): any
|
|
1108
|
+
|
|
1109
|
+
export function useLazyFetch<
|
|
1110
|
+
ErrorT = FetchError,
|
|
1111
|
+
Path extends import('#nuxt-better-auth').AuthApiEndpointPath = import('#nuxt-better-auth').AuthApiEndpointPath,
|
|
1112
|
+
Method extends _AuthFetchMethod<Path> = _AuthFetchDefaultMethod<Path>,
|
|
1113
|
+
_ResT = _AuthFetchResult<Path, Method>,
|
|
1114
|
+
DataT = _ResT,
|
|
1115
|
+
PickKeys extends import('nuxt/dist/app/composables/asyncData').KeysOf<DataT> = import('nuxt/dist/app/composables/asyncData').KeysOf<DataT>,
|
|
1116
|
+
DefaultT = undefined,
|
|
1117
|
+
>(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>
|
|
1118
|
+
export function useLazyFetch<
|
|
1119
|
+
ErrorT = FetchError,
|
|
1120
|
+
Path extends import('#nuxt-better-auth').AuthApiEndpointPath = import('#nuxt-better-auth').AuthApiEndpointPath,
|
|
1121
|
+
Method extends _AuthFetchMethod<Path> = _AuthFetchDefaultMethod<Path>,
|
|
1122
|
+
_ResT = _AuthFetchResult<Path, Method>,
|
|
1123
|
+
DataT = _ResT,
|
|
1124
|
+
PickKeys extends import('nuxt/dist/app/composables/asyncData').KeysOf<DataT> = import('nuxt/dist/app/composables/asyncData').KeysOf<DataT>,
|
|
1125
|
+
DefaultT = DataT,
|
|
1126
|
+
>(request: import('vue').Ref<Path> | Path | (() => Path), opts?: Omit<import('nuxt/dist/app/composables/fetch').UseFetchOptions<_ResT, DataT, PickKeys, DefaultT, Path, Method>, 'lazy'>): import('nuxt/dist/app/composables/asyncData').AsyncData<import('nuxt/dist/app/composables/asyncData').PickFrom<DataT, PickKeys> | DefaultT, ErrorT | undefined>
|
|
1127
|
+
export function useLazyFetch(request: string | import('vue').Ref<string> | (() => string), opts?: any): any
|
|
1128
|
+
}
|
|
1129
|
+
|
|
1130
|
+
declare module 'nuxt/app' {
|
|
1131
|
+
export function useFetch<
|
|
1132
|
+
ErrorT = FetchError,
|
|
1133
|
+
Path extends import('#nuxt-better-auth').AuthApiEndpointPath = import('#nuxt-better-auth').AuthApiEndpointPath,
|
|
1134
|
+
Method extends _AuthFetchMethod<Path> = _AuthFetchDefaultMethod<Path>,
|
|
1135
|
+
_ResT = _AuthFetchResult<Path, Method>,
|
|
1136
|
+
DataT = _ResT,
|
|
1137
|
+
PickKeys extends import('nuxt/dist/app/composables/asyncData').KeysOf<DataT> = import('nuxt/dist/app/composables/asyncData').KeysOf<DataT>,
|
|
1138
|
+
DefaultT = undefined,
|
|
1139
|
+
>(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>
|
|
1140
|
+
export function useFetch<
|
|
1141
|
+
ErrorT = FetchError,
|
|
1142
|
+
Path extends import('#nuxt-better-auth').AuthApiEndpointPath = import('#nuxt-better-auth').AuthApiEndpointPath,
|
|
1143
|
+
Method extends _AuthFetchMethod<Path> = _AuthFetchDefaultMethod<Path>,
|
|
1144
|
+
_ResT = _AuthFetchResult<Path, Method>,
|
|
1145
|
+
DataT = _ResT,
|
|
1146
|
+
PickKeys extends import('nuxt/dist/app/composables/asyncData').KeysOf<DataT> = import('nuxt/dist/app/composables/asyncData').KeysOf<DataT>,
|
|
1147
|
+
DefaultT = DataT,
|
|
1148
|
+
>(request: import('vue').Ref<Path> | Path | (() => Path), opts?: import('nuxt/dist/app/composables/fetch').UseFetchOptions<_ResT, DataT, PickKeys, DefaultT, Path, Method>): import('nuxt/dist/app/composables/asyncData').AsyncData<import('nuxt/dist/app/composables/asyncData').PickFrom<DataT, PickKeys> | DefaultT, ErrorT | undefined>
|
|
1149
|
+
export function useFetch(request: string | import('vue').Ref<string> | (() => string), opts?: any): any
|
|
1150
|
+
|
|
1151
|
+
export function useLazyFetch<
|
|
1152
|
+
ErrorT = FetchError,
|
|
1153
|
+
Path extends import('#nuxt-better-auth').AuthApiEndpointPath = import('#nuxt-better-auth').AuthApiEndpointPath,
|
|
1154
|
+
Method extends _AuthFetchMethod<Path> = _AuthFetchDefaultMethod<Path>,
|
|
1155
|
+
_ResT = _AuthFetchResult<Path, Method>,
|
|
1156
|
+
DataT = _ResT,
|
|
1157
|
+
PickKeys extends import('nuxt/dist/app/composables/asyncData').KeysOf<DataT> = import('nuxt/dist/app/composables/asyncData').KeysOf<DataT>,
|
|
1158
|
+
DefaultT = undefined,
|
|
1159
|
+
>(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>
|
|
1160
|
+
export function useLazyFetch<
|
|
1161
|
+
ErrorT = FetchError,
|
|
1162
|
+
Path extends import('#nuxt-better-auth').AuthApiEndpointPath = import('#nuxt-better-auth').AuthApiEndpointPath,
|
|
1163
|
+
Method extends _AuthFetchMethod<Path> = _AuthFetchDefaultMethod<Path>,
|
|
1164
|
+
_ResT = _AuthFetchResult<Path, Method>,
|
|
1165
|
+
DataT = _ResT,
|
|
1166
|
+
PickKeys extends import('nuxt/dist/app/composables/asyncData').KeysOf<DataT> = import('nuxt/dist/app/composables/asyncData').KeysOf<DataT>,
|
|
1167
|
+
DefaultT = DataT,
|
|
1168
|
+
>(request: import('vue').Ref<Path> | Path | (() => Path), opts?: Omit<import('nuxt/dist/app/composables/fetch').UseFetchOptions<_ResT, DataT, PickKeys, DefaultT, Path, Method>, 'lazy'>): import('nuxt/dist/app/composables/asyncData').AsyncData<import('nuxt/dist/app/composables/asyncData').PickFrom<DataT, PickKeys> | DefaultT, ErrorT | undefined>
|
|
1169
|
+
export function useLazyFetch(request: string | import('vue').Ref<string> | (() => string), opts?: any): any
|
|
1170
|
+
}
|
|
1171
|
+
|
|
1172
|
+
declare module 'nitropack' {
|
|
1173
|
+
interface NitroRouteRules {
|
|
1174
|
+
auth?: import('${runtimeTypesPath}').AuthMeta
|
|
1175
|
+
}
|
|
1176
|
+
interface NitroRouteConfig {
|
|
1177
|
+
auth?: import('${runtimeTypesPath}').AuthMeta
|
|
1178
|
+
}
|
|
1179
|
+
interface InternalApi extends _GeneratedAuthInternalApi {}
|
|
1180
|
+
}
|
|
336
1181
|
declare module 'nitropack/types' {
|
|
337
1182
|
interface NitroRouteRules {
|
|
338
|
-
auth?: import('${
|
|
1183
|
+
auth?: import('${runtimeTypesPath}').AuthMeta
|
|
1184
|
+
}
|
|
1185
|
+
interface NitroRouteConfig {
|
|
1186
|
+
auth?: import('${runtimeTypesPath}').AuthMeta
|
|
1187
|
+
}
|
|
1188
|
+
interface InternalApi extends _GeneratedAuthInternalApi {}
|
|
1189
|
+
}
|
|
1190
|
+
declare module 'nitro/types' {
|
|
1191
|
+
interface NitroRouteRules {
|
|
1192
|
+
auth?: import('${runtimeTypesPath}').AuthMeta
|
|
1193
|
+
}
|
|
1194
|
+
interface NitroRouteConfig {
|
|
1195
|
+
auth?: import('${runtimeTypesPath}').AuthMeta
|
|
339
1196
|
}
|
|
1197
|
+
interface InternalApi extends _GeneratedAuthInternalApi {}
|
|
340
1198
|
}
|
|
1199
|
+
export {}
|
|
341
1200
|
`
|
|
1201
|
+
}, { nitro: true, node: true });
|
|
1202
|
+
}
|
|
1203
|
+
function registerSharedTypeTemplates(input) {
|
|
1204
|
+
addTypeTemplate({
|
|
1205
|
+
filename: "types/nuxt-better-auth.d.ts",
|
|
1206
|
+
getContents: () => `
|
|
1207
|
+
import type { AppSession } from '${input.runtimeTypesAugmentPath}'
|
|
1208
|
+
export * from '${input.runtimeTypesAugmentPath}'
|
|
1209
|
+
export type { AuthMeta, AuthMode, AuthRouteRules, AuthSocialProviderId, Auth, InferUser, InferSession } from '${input.runtimeTypesPath}'
|
|
1210
|
+
declare module 'h3' {
|
|
1211
|
+
interface H3EventContext {
|
|
1212
|
+
requestSession?: AppSession | null
|
|
1213
|
+
}
|
|
1214
|
+
}
|
|
1215
|
+
`
|
|
1216
|
+
});
|
|
1217
|
+
addTypeTemplate({
|
|
1218
|
+
filename: "types/nuxt-better-auth-client.d.ts",
|
|
1219
|
+
getContents: () => `
|
|
1220
|
+
import type createAppAuthClient from '${input.clientConfigPath}'
|
|
1221
|
+
declare module '#nuxt-better-auth' {
|
|
1222
|
+
export type AppAuthClient = ReturnType<typeof createAppAuthClient>
|
|
1223
|
+
}
|
|
1224
|
+
`
|
|
1225
|
+
});
|
|
1226
|
+
}
|
|
1227
|
+
|
|
1228
|
+
const consola = consola$1.withTag("nuxt-better-auth");
|
|
1229
|
+
const serverAliasImportRE = /from\s+['"]#server/;
|
|
1230
|
+
const layersAliasImportRE = /from\s+['"]#layers\//;
|
|
1231
|
+
const rootAliasImportRE = /from\s+['"]~~/;
|
|
1232
|
+
const workspaceAliasImportRE = /from\s+['"]@@/;
|
|
1233
|
+
const dbIdentifierRE = /\bdb\b/;
|
|
1234
|
+
const sessionHookAfterIdentifierRE = /\bsessionHookAfter\b/;
|
|
1235
|
+
const nuxtHubDbImportRE = /@nuxthub\/db/;
|
|
1236
|
+
function isServerConfigSharedTypeSafe(serverConfigPath) {
|
|
1237
|
+
const resolvedPath = [
|
|
1238
|
+
serverConfigPath,
|
|
1239
|
+
`${serverConfigPath}.ts`,
|
|
1240
|
+
`${serverConfigPath}.mts`,
|
|
1241
|
+
`${serverConfigPath}.cts`,
|
|
1242
|
+
`${serverConfigPath}.js`,
|
|
1243
|
+
`${serverConfigPath}.mjs`,
|
|
1244
|
+
`${serverConfigPath}.cjs`
|
|
1245
|
+
].find((path) => existsSync(path));
|
|
1246
|
+
if (!resolvedPath)
|
|
1247
|
+
return false;
|
|
1248
|
+
const contents = readFileSync(resolvedPath, "utf8");
|
|
1249
|
+
return !(serverAliasImportRE.test(contents) || layersAliasImportRE.test(contents) || rootAliasImportRE.test(contents) || workspaceAliasImportRE.test(contents) || dbIdentifierRE.test(contents) || sessionHookAfterIdentifierRE.test(contents) || nuxtHubDbImportRE.test(contents));
|
|
1250
|
+
}
|
|
1251
|
+
async function createDefaultAuthConfigFiles(nuxt) {
|
|
1252
|
+
const configs = resolveAuthConfigDescriptors(nuxt);
|
|
1253
|
+
const serverTemplate = `import { defineServerAuth } from '@onmax/nuxt-better-auth/config'
|
|
1254
|
+
|
|
1255
|
+
export default defineServerAuth({
|
|
1256
|
+
emailAndPassword: { enabled: true },
|
|
1257
|
+
})
|
|
1258
|
+
`;
|
|
1259
|
+
const clientTemplate = `import { defineClientAuth } from '@onmax/nuxt-better-auth/config'
|
|
1260
|
+
|
|
1261
|
+
export default defineClientAuth({})
|
|
1262
|
+
`;
|
|
1263
|
+
if (configs.server.shouldCreateDefaultFile) {
|
|
1264
|
+
const serverPath = `${configs.server.path}.ts`;
|
|
1265
|
+
await mkdir(dirname(serverPath), { recursive: true });
|
|
1266
|
+
await writeFile(serverPath, serverTemplate);
|
|
1267
|
+
consola.success(`Created ${relative(configs.server.declaringLayerRoot, serverPath)}`);
|
|
1268
|
+
}
|
|
1269
|
+
if (configs.client.shouldCreateDefaultFile) {
|
|
1270
|
+
const clientPath = `${configs.client.path}.ts`;
|
|
1271
|
+
await mkdir(dirname(clientPath), { recursive: true });
|
|
1272
|
+
await writeFile(clientPath, clientTemplate);
|
|
1273
|
+
consola.success(`Created ${relative(configs.client.declaringLayerRoot, clientPath)}`);
|
|
1274
|
+
}
|
|
1275
|
+
}
|
|
1276
|
+
const module$1 = defineNuxtModule({
|
|
1277
|
+
meta: { name: "@onmax/nuxt-better-auth", version, configKey: "auth", compatibility: { nuxt: ">=4.0.0" } },
|
|
1278
|
+
defaults: {
|
|
1279
|
+
clientOnly: false,
|
|
1280
|
+
serverConfig: "server/auth.config",
|
|
1281
|
+
clientConfig: "app/auth.config",
|
|
1282
|
+
redirects: { login: "/login", guest: "/" },
|
|
1283
|
+
preserveRedirect: true,
|
|
1284
|
+
redirectQueryKey: "redirect",
|
|
1285
|
+
hubSecondaryStorage: false
|
|
1286
|
+
},
|
|
1287
|
+
async onInstall(nuxt) {
|
|
1288
|
+
const configuredSecret = nuxt.options.runtimeConfig?.betterAuthSecret;
|
|
1289
|
+
const generatedSecret = await promptForSecret(nuxt.options.rootDir, consola, { configuredSecret, prepare: Boolean(nuxt.options._prepare) });
|
|
1290
|
+
if (generatedSecret)
|
|
1291
|
+
process.env.NUXT_BETTER_AUTH_SECRET = generatedSecret;
|
|
1292
|
+
await createDefaultAuthConfigFiles(nuxt);
|
|
1293
|
+
},
|
|
1294
|
+
async setup(options, nuxt) {
|
|
1295
|
+
const resolver = createResolver(import.meta.url);
|
|
1296
|
+
const setup = await resolveAuthModuleSetup({
|
|
1297
|
+
nuxt,
|
|
1298
|
+
options,
|
|
1299
|
+
runtimeTypesAugmentPath: resolver.resolve("./runtime/types/augment"),
|
|
1300
|
+
consola
|
|
342
1301
|
});
|
|
343
|
-
nuxt.
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
1302
|
+
nuxt.options.alias["#nuxt-better-auth"] = setup.aliases["#nuxt-better-auth"];
|
|
1303
|
+
if (setup.aliases["#auth/server"])
|
|
1304
|
+
nuxt.options.alias["#auth/server"] = setup.aliases["#auth/server"];
|
|
1305
|
+
nuxt.options.alias["#auth/client"] = setup.aliases["#auth/client"];
|
|
1306
|
+
if (!setup.clientOnly) {
|
|
1307
|
+
const secondaryStorageTemplate = addTemplate({
|
|
1308
|
+
filename: "better-auth/secondary-storage.mjs",
|
|
1309
|
+
getContents: () => buildSecondaryStorageCode(setup.runtime.useHubKV),
|
|
1310
|
+
write: true
|
|
1311
|
+
});
|
|
1312
|
+
nuxt.options.alias["#auth/secondary-storage"] = secondaryStorageTemplate.dst;
|
|
1313
|
+
const databaseTemplate = addTemplate({
|
|
1314
|
+
filename: "better-auth/database.mjs",
|
|
1315
|
+
getContents: () => setup.database.providerDefinition.buildDatabaseCode(setup.database.buildContext),
|
|
1316
|
+
write: true
|
|
1317
|
+
});
|
|
1318
|
+
nuxt.options.alias["#auth/database"] = databaseTemplate.dst;
|
|
1319
|
+
const schemaTemplate = addTemplate({
|
|
1320
|
+
filename: "better-auth/schema.mjs",
|
|
1321
|
+
getContents: () => buildSchemaExportCode(setup.database.hasHubDb, setup.database.buildContext?.hubDialect ?? "sqlite"),
|
|
1322
|
+
write: true
|
|
1323
|
+
});
|
|
1324
|
+
nuxt.options.alias["#auth/schema"] = schemaTemplate.dst;
|
|
361
1325
|
}
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
addServerHandler({ route: "/api/_better-auth/sessions", method: "get", handler: resolver.resolve("./runtime/server/api/_better-auth/sessions.get") });
|
|
368
|
-
addServerHandler({ route: "/api/_better-auth/sessions", method: "delete", handler: resolver.resolve("./runtime/server/api/_better-auth/sessions.delete") });
|
|
369
|
-
addServerHandler({ route: "/api/_better-auth/users", method: "get", handler: resolver.resolve("./runtime/server/api/_better-auth/users.get") });
|
|
370
|
-
addServerHandler({ route: "/api/_better-auth/accounts", method: "get", handler: resolver.resolve("./runtime/server/api/_better-auth/accounts.get") });
|
|
371
|
-
}
|
|
372
|
-
extendPages((pages) => {
|
|
373
|
-
pages.push({ name: "better-auth-devtools", path: "/__better-auth-devtools", file: resolver.resolve("./runtime/app/pages/__better-auth-devtools.vue") });
|
|
1326
|
+
if (setup.prepareTypes) {
|
|
1327
|
+
registerPrepareTypesHook({
|
|
1328
|
+
nuxt,
|
|
1329
|
+
serverDir: setup.prepareTypes.serverDir,
|
|
1330
|
+
hasHubDb: setup.prepareTypes.hasHubDb
|
|
374
1331
|
});
|
|
375
1332
|
}
|
|
376
|
-
|
|
377
|
-
const
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
const applyMetaFromRules = (page) => {
|
|
382
|
-
const matches = matcher.matchAll(page.path);
|
|
383
|
-
if (!matches.length)
|
|
384
|
-
return;
|
|
385
|
-
const matchedRules = defu({}, ...matches.reverse());
|
|
386
|
-
if (matchedRules.auth !== void 0) {
|
|
387
|
-
page.meta = page.meta || {};
|
|
388
|
-
page.meta.auth = matchedRules.auth;
|
|
389
|
-
}
|
|
390
|
-
page.children?.forEach((child) => applyMetaFromRules(child));
|
|
1333
|
+
if (setup.database.providerDefinition) {
|
|
1334
|
+
const setupCtx = {
|
|
1335
|
+
nuxt,
|
|
1336
|
+
options,
|
|
1337
|
+
clientOnly: setup.clientOnly
|
|
391
1338
|
};
|
|
392
|
-
|
|
393
|
-
}
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
const dialect = getHubDialect(hub);
|
|
399
|
-
if (!dialect || !["sqlite", "postgresql", "mysql"].includes(dialect)) {
|
|
400
|
-
consola.warn(`Unsupported database dialect: ${dialect}`);
|
|
401
|
-
return;
|
|
402
|
-
}
|
|
403
|
-
const isProduction = !nuxt.options.dev;
|
|
404
|
-
try {
|
|
405
|
-
const configFile = `${serverConfigPath}.ts`;
|
|
406
|
-
const userConfig = await loadUserAuthConfig(configFile, isProduction);
|
|
407
|
-
const extendedConfig = {};
|
|
408
|
-
await nuxt.callHook("better-auth:config:extend", extendedConfig);
|
|
409
|
-
const plugins = [...userConfig.plugins || [], ...extendedConfig.plugins || []];
|
|
410
|
-
const { getAuthTables } = await import('better-auth/db');
|
|
411
|
-
const tables = getAuthTables({
|
|
412
|
-
plugins,
|
|
413
|
-
secondaryStorage: options.secondaryStorage ? {
|
|
414
|
-
get: async (_key) => null,
|
|
415
|
-
set: async (_key, _value, _ttl) => {
|
|
416
|
-
},
|
|
417
|
-
delete: async (_key) => {
|
|
418
|
-
}
|
|
419
|
-
} : void 0
|
|
1339
|
+
await setup.database.providerDefinition.setup?.(setupCtx);
|
|
1340
|
+
}
|
|
1341
|
+
const authRouteRulesTemplate = addTemplate({
|
|
1342
|
+
filename: "better-auth/route-rules.mjs",
|
|
1343
|
+
getContents: () => buildAuthRouteRulesCode(collectAuthRouteRules(nuxt)),
|
|
1344
|
+
write: true
|
|
420
1345
|
});
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
addTemplate({ filename: `better-auth/schema.${dialect}.ts`, getContents: () => schemaCode, write: true });
|
|
430
|
-
consola.info(`Generated ${dialect} schema with ${Object.keys(tables).length} tables`);
|
|
431
|
-
} catch (error) {
|
|
432
|
-
if (isProduction) {
|
|
433
|
-
throw error;
|
|
1346
|
+
nuxt.options.alias["#auth/route-rules"] = authRouteRulesTemplate.dst;
|
|
1347
|
+
if (setup.serverTypes) {
|
|
1348
|
+
registerServerTypeTemplates({
|
|
1349
|
+
serverConfigPath: setup.serverTypes.serverConfigPath,
|
|
1350
|
+
hasHubDb: setup.serverTypes.hasHubDb,
|
|
1351
|
+
runtimeTypesPath: resolver.resolve("./runtime/types"),
|
|
1352
|
+
sharedServerConfigSafe: isServerConfigSharedTypeSafe(setup.serverTypes.serverConfigPath)
|
|
1353
|
+
});
|
|
434
1354
|
}
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
1355
|
+
if (setup.schemaGeneration) {
|
|
1356
|
+
if (setup.schemaGeneration.externalizeNuxtHubDatabase)
|
|
1357
|
+
registerNuxtHubDatabaseExternalHook(nuxt);
|
|
1358
|
+
await setupBetterAuthSchema(
|
|
1359
|
+
nuxt,
|
|
1360
|
+
setup.schemaGeneration.serverConfigPath,
|
|
1361
|
+
options,
|
|
1362
|
+
consola,
|
|
1363
|
+
setup.schemaGeneration.hubSecondaryStorage
|
|
1364
|
+
);
|
|
442
1365
|
}
|
|
443
|
-
|
|
444
|
-
|
|
1366
|
+
registerSharedTypeTemplates({
|
|
1367
|
+
runtimeTypesAugmentPath: setup.aliases["#nuxt-better-auth"],
|
|
1368
|
+
runtimeTypesPath: resolver.resolve("./runtime/types"),
|
|
1369
|
+
clientConfigPath: setup.sharedTypes.clientConfigPath
|
|
1370
|
+
});
|
|
1371
|
+
registerTemplateHmrHook(nuxt);
|
|
1372
|
+
registerServerRuntime({ clientOnly: setup.clientOnly, resolve: resolver.resolve });
|
|
1373
|
+
registerAuthMiddlewareHook(nuxt, resolver.resolve);
|
|
1374
|
+
await registerDevtools({ nuxt, clientOnly: setup.clientOnly, hasHubDb: setup.database.hasHubDb, resolve: resolver.resolve });
|
|
1375
|
+
registerRouteRulesMetaHook(nuxt);
|
|
1376
|
+
}
|
|
1377
|
+
});
|
|
445
1378
|
|
|
446
1379
|
export { module$1 as default };
|