@onmax/nuxt-better-auth 0.0.1 → 0.0.2-alpha.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. package/README.md +17 -170
  2. package/dist/module.d.mts +20 -2
  3. package/dist/module.json +1 -1
  4. package/dist/module.mjs +432 -14
  5. package/dist/runtime/app/components/BetterAuthState.d.vue.ts +20 -0
  6. package/dist/runtime/app/components/BetterAuthState.vue +8 -0
  7. package/dist/runtime/app/components/BetterAuthState.vue.d.ts +20 -0
  8. package/dist/runtime/app/composables/useUserSession.d.ts +22 -0
  9. package/dist/runtime/app/composables/useUserSession.js +159 -0
  10. package/dist/runtime/app/middleware/auth.global.d.ts +13 -0
  11. package/dist/runtime/app/middleware/auth.global.js +37 -0
  12. package/dist/runtime/app/pages/__better-auth-devtools.d.vue.ts +3 -0
  13. package/dist/runtime/app/pages/__better-auth-devtools.vue +426 -0
  14. package/dist/runtime/app/pages/__better-auth-devtools.vue.d.ts +3 -0
  15. package/dist/runtime/app/plugins/session.client.d.ts +2 -0
  16. package/dist/runtime/app/plugins/session.client.js +16 -0
  17. package/dist/runtime/app/plugins/session.server.d.ts +2 -0
  18. package/dist/runtime/app/plugins/session.server.js +24 -0
  19. package/dist/runtime/config.d.ts +44 -0
  20. package/dist/runtime/config.js +6 -0
  21. package/dist/runtime/server/api/_better-auth/_schema.d.ts +8 -0
  22. package/dist/runtime/server/api/_better-auth/_schema.js +11 -0
  23. package/dist/runtime/server/api/_better-auth/accounts.get.d.ts +14 -0
  24. package/dist/runtime/server/api/_better-auth/accounts.get.js +28 -0
  25. package/dist/runtime/server/api/_better-auth/config.get.d.ts +35 -0
  26. package/dist/runtime/server/api/_better-auth/config.get.js +47 -0
  27. package/dist/runtime/server/api/_better-auth/sessions.delete.d.ts +4 -0
  28. package/dist/runtime/server/api/_better-auth/sessions.delete.js +22 -0
  29. package/dist/runtime/server/api/_better-auth/sessions.get.d.ts +14 -0
  30. package/dist/runtime/server/api/_better-auth/sessions.get.js +43 -0
  31. package/dist/runtime/server/api/_better-auth/users.get.d.ts +14 -0
  32. package/dist/runtime/server/api/_better-auth/users.get.js +34 -0
  33. package/dist/runtime/server/api/auth/[...all].d.ts +2 -0
  34. package/dist/runtime/server/api/auth/[...all].js +6 -0
  35. package/dist/runtime/server/middleware/route-access.d.ts +2 -0
  36. package/dist/runtime/server/middleware/route-access.js +28 -0
  37. package/dist/runtime/server/tsconfig.json +3 -0
  38. package/dist/runtime/server/utils/auth.d.ts +11 -0
  39. package/dist/runtime/server/utils/auth.js +32 -0
  40. package/dist/runtime/server/utils/session.d.ts +9 -0
  41. package/dist/runtime/server/utils/session.js +23 -0
  42. package/dist/runtime/types/augment.d.ts +42 -0
  43. package/dist/runtime/types/augment.js +0 -0
  44. package/dist/runtime/types.d.ts +23 -0
  45. package/dist/runtime/types.js +0 -0
  46. package/dist/runtime/utils/match-user.d.ts +2 -0
  47. package/dist/runtime/utils/match-user.js +13 -0
  48. package/dist/types.d.mts +8 -10
  49. package/package.json +40 -11
  50. package/dist/module.d.cts +0 -2
package/README.md CHANGED
@@ -1,180 +1,27 @@
1
- # @onmax/nuxt-better-auth
1
+ <p align="center">
2
+ <img src="https://raw.githubusercontent.com/onmax/nuxt-better-auth/main/.github/og.png" alt="Nuxt Better Auth" width="100%">
3
+ <br>
4
+ <sub>Designed by <a href="https://github.com/HugoRCD">HugoRCD</a></sub>
5
+ </p>
2
6
 
3
- [![npm version][npm-version-src]][npm-version-href]
4
- [![npm downloads][npm-downloads-src]][npm-downloads-href]
5
- [![License][license-src]][license-href]
6
- [![Nuxt][nuxt-src]][nuxt-href]
7
+ <h1 align="center">@onmax/nuxt-better-auth</h1>
7
8
 
8
- Nuxt module for [Better Auth](https://better-auth.com) with auto schema generation, route protection, and session management. Works with [NuxtHub](https://hub.nuxt.com) and future `@nuxt/db`.
9
+ <p align="center">Nuxt module for <a href="https://better-auth.com">Better Auth</a></p>
9
10
 
10
- ## Features
11
+ <p align="center">
12
+ <a href="https://npmjs.com/package/@onmax/nuxt-better-auth"><img src="https://img.shields.io/npm/v/@onmax/nuxt-better-auth/latest.svg?style=flat&colorA=020420&colorB=00DC82" alt="npm version"></a>
13
+ <a href="https://npm.chart.dev/@onmax/nuxt-better-auth"><img src="https://img.shields.io/npm/dm/@onmax/nuxt-better-auth.svg?style=flat&colorA=020420&colorB=00DC82" alt="npm downloads"></a>
14
+ <a href="https://npmjs.com/package/@onmax/nuxt-better-auth"><img src="https://img.shields.io/npm/l/@onmax/nuxt-better-auth.svg?style=flat&colorA=020420&colorB=00DC82" alt="License"></a>
15
+ <a href="https://nuxt.com"><img src="https://img.shields.io/badge/Nuxt-020420?logo=nuxt.js" alt="Nuxt"></a>
16
+ </p>
11
17
 
12
- - **Auto Schema Generation** - Generates Drizzle schema from your better-auth plugins
13
- - **Route Protection** - Declarative access rules via `routeRules`
14
- - **Session Management** - SSR-safe session handling with client hydration
15
- - **Role-Based Access** - Support for `admin`, `user`, and custom roles
16
- - **Auto-Imports** - `useUserSession`, `requireUserSession`, `getUserSession`
17
- - **BetterAuthState Component** - Ready-to-use Vue component with slots
18
+ > [!WARNING]
19
+ > This library is a work in progress and not ready for production use.
18
20
 
19
- ## Requirements
21
+ ## Documentation
20
22
 
21
- - NuxtHub with database enabled (`hub: { database: true }`) or future `@nuxt/db`
22
-
23
- ## Quick Start
24
-
25
- ### 1. Install
26
-
27
- ```bash
28
- pnpm add @onmax/nuxt-better-auth better-auth drizzle-orm @nuxthub/core
29
- ```
30
-
31
- ### 2. Configure Nuxt
32
-
33
- ```ts
34
- export default defineNuxtConfig({
35
- modules: ['@nuxthub/core', '@onmax/nuxt-better-auth'],
36
-
37
- hub: { database: true },
38
-
39
- runtimeConfig: {
40
- betterAuthSecret: '', // BETTER_AUTH_SECRET env var
41
- public: { siteUrl: 'http://localhost:3000' },
42
- },
43
-
44
- routeRules: {
45
- '/app/**': { auth: 'user' },
46
- '/admin/**': { auth: { role: 'admin' } },
47
- '/login': { auth: 'guest' },
48
- },
49
- })
50
- ```
51
-
52
- ### 3. Create Server Config
53
-
54
- Create `server/auth.config.ts`:
55
-
56
- ```ts
57
- import { admin } from 'better-auth/plugins'
58
- import { defineServerAuth } from '@onmax/nuxt-better-auth'
59
-
60
- export default defineServerAuth(({ db }) => ({
61
- appName: 'My App',
62
- plugins: [admin()],
63
- emailAndPassword: { enabled: true },
64
- }))
65
- ```
66
-
67
- > Schema is auto-generated from your plugins! No manual Drizzle schema needed.
68
-
69
- ### 4. Create Client Config
70
-
71
- Create `app/auth.client.ts`:
72
-
73
- ```ts
74
- import { adminClient } from 'better-auth/client/plugins'
75
- import { createAuthClient } from 'better-auth/vue'
76
-
77
- export function createAppAuthClient(baseURL: string) {
78
- return createAuthClient({
79
- baseURL,
80
- plugins: [adminClient()],
81
- })
82
- }
83
-
84
- export type AppAuthClient = ReturnType<typeof createAppAuthClient>
85
- ```
86
-
87
- ### 5. Add Type Extensions
88
-
89
- Create `shared/types/auth.d.ts`:
90
-
91
- ```ts
92
- import '#nuxt-better-auth'
93
-
94
- declare module '#nuxt-better-auth' {
95
- interface AuthUser {
96
- role?: string | null
97
- banned?: boolean | null
98
- }
99
- }
100
- ```
101
-
102
- ## Route Rules
103
-
104
- | Option | Type | Description |
105
- |--------|------|-------------|
106
- | `auth` | `false \| 'guest' \| 'user' \| { role: string }` | Auth requirement |
107
-
108
- Examples:
109
- - `auth: false` - Public (default)
110
- - `auth: 'guest'` - Only unauthenticated users
111
- - `auth: 'user'` - Any authenticated user
112
- - `auth: { role: 'admin' }` - Requires specific role
113
-
114
- ## Composables
115
-
116
- ### `useUserSession()`
117
-
118
- ```ts
119
- const { user, session, loggedIn, ready, client } = useUserSession()
120
- ```
121
-
122
- ### `<BetterAuthState>`
123
-
124
- ```vue
125
- <BetterAuthState>
126
- <template #default="{ loggedIn, user, signOut }">
127
- <p v-if="loggedIn">Welcome, {{ user?.name }}</p>
128
- <p v-else>Not logged in</p>
129
- </template>
130
- <template #placeholder>
131
- <p>Loading...</p>
132
- </template>
133
- </BetterAuthState>
134
- ```
135
-
136
- ## Server Utils
137
-
138
- ```ts
139
- // Require auth
140
- const { user, session } = await requireUserSession(event)
141
-
142
- // Require admin
143
- const { user } = await requireUserSession(event, { role: 'admin' })
144
-
145
- // Optional session
146
- const session = await getUserSession(event)
147
- ```
148
-
149
- ## Module Aliases
150
-
151
- | Alias | Points To |
152
- |-------|-----------|
153
- | `#auth/server` | `server/auth.config.ts` |
154
- | `#auth/client` | `app/auth.client.ts` |
155
- | `#nuxt-better-auth` | Module type augmentation |
156
-
157
- ## Development
158
-
159
- ```bash
160
- pnpm install
161
- pnpm dev:prepare
162
- pnpm dev
163
- ```
23
+ **[nuxt-better-auth.onmax.me](https://nuxt-better-auth.onmax.me/)**
164
24
 
165
25
  ## License
166
26
 
167
27
  MIT
168
-
169
- <!-- Badges -->
170
- [npm-version-src]: https://img.shields.io/npm/v/@onmax/nuxt-better-auth/latest.svg?style=flat&colorA=020420&colorB=00DC82
171
- [npm-version-href]: https://npmjs.com/package/@onmax/nuxt-better-auth
172
-
173
- [npm-downloads-src]: https://img.shields.io/npm/dm/@onmax/nuxt-better-auth.svg?style=flat&colorA=020420&colorB=00DC82
174
- [npm-downloads-href]: https://npm.chart.dev/@onmax/nuxt-better-auth
175
-
176
- [license-src]: https://img.shields.io/npm/l/@onmax/nuxt-better-auth.svg?style=flat&colorA=020420&colorB=00DC82
177
- [license-href]: https://npmjs.com/package/@onmax/nuxt-better-auth
178
-
179
- [nuxt-src]: https://img.shields.io/badge/Nuxt-020420?logo=nuxt.js
180
- [nuxt-href]: https://nuxt.com
package/dist/module.d.mts CHANGED
@@ -1,2 +1,20 @@
1
- export * from "/home/maxi/nuxt/better-auth/src/module.js";
2
- export { default } from "/home/maxi/nuxt/better-auth/src/module.js";
1
+ import * as _nuxt_schema from '@nuxt/schema';
2
+ import { BetterAuthModuleOptions } from '../dist/runtime/config.js';
3
+ export { BetterAuthModuleOptions, defineClientAuth, defineServerAuth } from '../dist/runtime/config.js';
4
+ import { BetterAuthOptions } from 'better-auth';
5
+ export { Auth, AuthMeta, AuthMode, AuthRouteRules, AuthSession, AuthUser, InferSession, InferUser, RequireSessionOptions, ServerAuthContext, UserMatch } from '../dist/runtime/types.js';
6
+
7
+ declare module '@nuxt/schema' {
8
+ interface NuxtHooks {
9
+ /**
10
+ * Extend better-auth config with additional plugins or options.
11
+ * Called after user's auth.config.ts is loaded.
12
+ * @param config - Partial config to merge into the auth options
13
+ */
14
+ 'better-auth:config:extend': (config: Partial<BetterAuthOptions>) => void | Promise<void>;
15
+ }
16
+ }
17
+
18
+ declare const _default: _nuxt_schema.NuxtModule<BetterAuthModuleOptions, BetterAuthModuleOptions, false>;
19
+
20
+ export { _default as default };
package/dist/module.json CHANGED
@@ -4,7 +4,7 @@
4
4
  "compatibility": {
5
5
  "nuxt": ">=3.0.0"
6
6
  },
7
- "version": "0.0.1",
7
+ "version": "0.0.2-alpha.10",
8
8
  "builder": {
9
9
  "@nuxt/module-builder": "1.0.2",
10
10
  "unbuild": "3.6.1"
package/dist/module.mjs CHANGED
@@ -1,20 +1,438 @@
1
- import { createJiti } from "file:///home/maxi/nuxt/better-auth/node_modules/.pnpm/jiti@2.6.1/node_modules/jiti/lib/jiti.mjs";
1
+ import { existsSync } from 'node:fs';
2
+ import { mkdir, writeFile } from 'node:fs/promises';
3
+ import { defineNuxtModule, createResolver, hasNuxtModule, addTemplate, addTypeTemplate, updateTemplates, addServerImportsDir, addServerScanDir, addServerHandler, addImportsDir, addPlugin, addComponentsDir, extendPages } from '@nuxt/kit';
4
+ import { consola as consola$1 } from 'consola';
5
+ import { defu } from 'defu';
6
+ import { join } from 'pathe';
7
+ import { toRouteMatcher, createRouter } from 'radix3';
8
+ import pluralize from 'pluralize';
9
+ export { defineClientAuth, defineServerAuth } from '../dist/runtime/config.js';
2
10
 
3
- const jiti = createJiti(import.meta.url, {
4
- "interopDefault": true,
5
- "alias": {
6
- "@onmax/nuxt-better-auth": "/home/maxi/nuxt/better-auth"
11
+ function setupDevTools(nuxt) {
12
+ nuxt.hook("devtools:customTabs", (tabs) => {
13
+ tabs.push({
14
+ category: "server",
15
+ name: "better-auth",
16
+ title: "Auth",
17
+ icon: "simple-icons:betterauth",
18
+ view: {
19
+ type: "iframe",
20
+ src: "/__better-auth-devtools"
21
+ }
22
+ });
23
+ });
24
+ }
25
+
26
+ function toSnakeCase(str) {
27
+ return str.replace(/([A-Z]+)([A-Z][a-z])/g, "$1_$2").replace(/([a-z\d])([A-Z])/g, "$1_$2").toLowerCase();
28
+ }
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
+
35
+ ${tableDefinitions}
36
+ `;
37
+ }
38
+ function getImports(dialect, options) {
39
+ switch (dialect) {
40
+ case "sqlite":
41
+ return `import { integer, sqliteTable, text } from 'drizzle-orm/sqlite-core'`;
42
+ case "postgresql":
43
+ return options?.useUuid ? `import { boolean, integer, pgTable, text, timestamp, uuid } from 'drizzle-orm/pg-core'` : `import { boolean, integer, pgTable, text, timestamp } from 'drizzle-orm/pg-core'`;
44
+ case "mysql":
45
+ return `import { boolean, int, mysqlTable, text, timestamp, varchar } from 'drizzle-orm/mysql-core'`;
46
+ }
47
+ }
48
+ function generateTable(tableName, table, dialect, allTables, options) {
49
+ const tableFunc = dialect === "sqlite" ? "sqliteTable" : dialect === "postgresql" ? "pgTable" : "mysqlTable";
50
+ const hasCustomModelName = table.modelName && table.modelName !== tableName;
51
+ let dbTableName = hasCustomModelName ? table.modelName : tableName;
52
+ if (options?.casing === "snake_case" && !hasCustomModelName)
53
+ dbTableName = toSnakeCase(dbTableName);
54
+ if (options?.usePlural && !hasCustomModelName)
55
+ dbTableName = pluralize(dbTableName);
56
+ const fields = Object.entries(table.fields).map(([fieldName, field]) => generateField(fieldName, field, dialect, allTables, options)).join(",\n ");
57
+ const idField = generateIdField(dialect, options);
58
+ return `export const ${tableName} = ${tableFunc}('${dbTableName}', {
59
+ ${idField},
60
+ ${fields}
61
+ })`;
62
+ }
63
+ function generateIdField(dialect, options) {
64
+ switch (dialect) {
65
+ case "sqlite":
66
+ if (options?.useUuid)
67
+ consola$1.warn("[@onmax/nuxt-better-auth] useUuid ignored for SQLite (no native uuid type). Using text.");
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) {
164
+ const { createJiti } = await import('jiti');
165
+ const jiti = createJiti(import.meta.url, { interopDefault: true });
166
+ try {
167
+ const mod = await jiti.import(configPath);
168
+ const configFn = typeof mod === "object" && mod !== null && "default" in mod ? mod.default : mod;
169
+ if (typeof configFn === "function") {
170
+ return configFn({ runtimeConfig: {}, db: null });
171
+ }
172
+ if (throwOnError) {
173
+ throw new Error("auth.config.ts must export default defineServerAuth(...)");
174
+ }
175
+ return {};
176
+ } catch (error) {
177
+ if (throwOnError) {
178
+ throw new Error(`Failed to load auth config: ${error instanceof Error ? error.message : error}`);
179
+ }
180
+ consola$1.error("[@onmax/nuxt-better-auth] Failed to load auth config for schema generation. Schema may be incomplete:", error);
181
+ return {};
182
+ }
183
+ }
184
+
185
+ function getHubDialect(hub) {
186
+ if (!hub?.db)
187
+ return void 0;
188
+ if (typeof hub.db === "string")
189
+ return hub.db;
190
+ if (typeof hub.db === "object" && hub.db !== null)
191
+ return hub.db.dialect;
192
+ return void 0;
193
+ }
194
+ function getHubCasing(hub) {
195
+ if (!hub?.db || typeof hub.db !== "object" || hub.db === null)
196
+ return void 0;
197
+ return hub.db.casing;
198
+ }
199
+ const consola = consola$1.withTag("nuxt-better-auth");
200
+ const module$1 = defineNuxtModule({
201
+ meta: { name: "@onmax/nuxt-better-auth", configKey: "auth", compatibility: { nuxt: ">=3.0.0" } },
202
+ defaults: {
203
+ serverConfig: "server/auth.config",
204
+ clientConfig: "app/auth.config",
205
+ redirects: { login: "/login", guest: "/" },
206
+ secondaryStorage: false
7
207
  },
8
- "transformOptions": {
9
- "babel": {
10
- "plugins": []
208
+ async setup(options, nuxt) {
209
+ const resolver = createResolver(import.meta.url);
210
+ const serverConfigFile = options.serverConfig;
211
+ const clientConfigFile = options.clientConfig;
212
+ const serverConfigPath = resolver.resolve(nuxt.options.rootDir, serverConfigFile);
213
+ const clientConfigPath = resolver.resolve(nuxt.options.rootDir, clientConfigFile);
214
+ const serverConfigExists = existsSync(`${serverConfigPath}.ts`) || existsSync(`${serverConfigPath}.js`);
215
+ const clientConfigExists = existsSync(`${clientConfigPath}.ts`) || existsSync(`${clientConfigPath}.js`);
216
+ if (!serverConfigExists)
217
+ throw new Error(`[nuxt-better-auth] Missing ${serverConfigFile}.ts - create with defineServerAuth()`);
218
+ if (!clientConfigExists)
219
+ throw new Error(`[nuxt-better-auth] Missing ${clientConfigFile}.ts - export createAppAuthClient()`);
220
+ const hasNuxtHub = hasNuxtModule("@nuxthub/core", nuxt);
221
+ const hub = hasNuxtHub ? nuxt.options.hub : void 0;
222
+ const hasHubDb = hasNuxtHub && !!hub?.db;
223
+ let secondaryStorageEnabled = options.secondaryStorage ?? false;
224
+ if (secondaryStorageEnabled && (!hasNuxtHub || !hub?.kv)) {
225
+ consola.warn("secondaryStorage requires @nuxthub/core with hub.kv: true. Disabling.");
226
+ secondaryStorageEnabled = false;
227
+ }
228
+ nuxt.options.runtimeConfig.public = nuxt.options.runtimeConfig.public || {};
229
+ nuxt.options.runtimeConfig.public.auth = defu(nuxt.options.runtimeConfig.public.auth, {
230
+ redirects: { login: options.redirects?.login ?? "/login", guest: options.redirects?.guest ?? "/" },
231
+ useDatabase: hasHubDb
232
+ });
233
+ const betterAuthSecret = process.env.BETTER_AUTH_SECRET || process.env.NUXT_BETTER_AUTH_SECRET || nuxt.options.runtimeConfig.betterAuthSecret || "";
234
+ if (!nuxt.options.dev && !betterAuthSecret) {
235
+ throw new Error("[nuxt-better-auth] BETTER_AUTH_SECRET is required in production. Set BETTER_AUTH_SECRET or NUXT_BETTER_AUTH_SECRET environment variable.");
11
236
  }
237
+ if (betterAuthSecret && betterAuthSecret.length < 32) {
238
+ throw new Error("[nuxt-better-auth] BETTER_AUTH_SECRET must be at least 32 characters for security");
239
+ }
240
+ nuxt.options.runtimeConfig.betterAuthSecret = betterAuthSecret;
241
+ nuxt.options.runtimeConfig.auth = defu(nuxt.options.runtimeConfig.auth, {
242
+ secondaryStorage: secondaryStorageEnabled
243
+ });
244
+ nuxt.options.alias["#nuxt-better-auth"] = resolver.resolve("./runtime/types/augment");
245
+ nuxt.options.alias["#auth/server"] = serverConfigPath;
246
+ nuxt.options.alias["#auth/client"] = clientConfigPath;
247
+ const secondaryStorageCode = secondaryStorageEnabled ? `import { kv } from '../hub/kv.mjs'
248
+ export function createSecondaryStorage() {
249
+ return {
250
+ get: async (key) => kv.get(\`_auth:\${key}\`),
251
+ set: async (key, value, ttl) => kv.set(\`_auth:\${key}\`, value, { ttl }),
252
+ delete: async (key) => kv.del(\`_auth:\${key}\`),
253
+ }
254
+ }` : `export function createSecondaryStorage() { return undefined }`;
255
+ const secondaryStorageTemplate = addTemplate({ filename: "better-auth/secondary-storage.mjs", getContents: () => secondaryStorageCode, write: true });
256
+ nuxt.options.alias["#auth/secondary-storage"] = secondaryStorageTemplate.dst;
257
+ const hubDialect = getHubDialect(hub) ?? "sqlite";
258
+ const databaseCode = hasHubDb ? `import { db, schema } from '../hub/db.mjs'
259
+ import { drizzleAdapter } from 'better-auth/adapters/drizzle'
260
+ const rawDialect = '${hubDialect}'
261
+ const dialect = rawDialect === 'postgresql' ? 'pg' : rawDialect
262
+ export function createDatabase() { return drizzleAdapter(db, { provider: dialect, schema }) }
263
+ export { db }` : `export function createDatabase() { return undefined }
264
+ export const db = undefined`;
265
+ const databaseTemplate = addTemplate({ filename: "better-auth/database.mjs", getContents: () => databaseCode, write: true });
266
+ nuxt.options.alias["#auth/database"] = databaseTemplate.dst;
267
+ addTypeTemplate({
268
+ filename: "types/auth-secondary-storage.d.ts",
269
+ getContents: () => `
270
+ declare module '#auth/secondary-storage' {
271
+ interface SecondaryStorage {
272
+ get: (key: string) => Promise<string | null>
273
+ set: (key: string, value: unknown, ttl?: number) => Promise<void>
274
+ delete: (key: string) => Promise<void>
12
275
  }
13
- })
276
+ export function createSecondaryStorage(): SecondaryStorage | undefined
277
+ }
278
+ `
279
+ });
280
+ addTypeTemplate({
281
+ filename: "types/auth-database.d.ts",
282
+ getContents: () => `
283
+ declare module '#auth/database' {
284
+ import type { drizzleAdapter } from 'better-auth/adapters/drizzle'
285
+ export function createDatabase(): ReturnType<typeof drizzleAdapter> | undefined
286
+ export const db: unknown
287
+ }
288
+ `
289
+ });
290
+ addTypeTemplate({
291
+ filename: "types/nuxt-better-auth.d.ts",
292
+ getContents: () => `
293
+ export * from '${resolver.resolve("./runtime/types/augment")}'
294
+ export type { AuthMeta, AuthMode, AuthRouteRules, UserMatch, RequireSessionOptions, Auth, InferUser, InferSession } from '${resolver.resolve("./runtime/types")}'
295
+ `
296
+ });
297
+ addTypeTemplate({
298
+ filename: "types/nuxt-better-auth-infer.d.ts",
299
+ getContents: () => `
300
+ import type { InferUser, InferSession } from 'better-auth'
301
+ import type { RuntimeConfig } from 'nuxt/schema'
302
+ import type configFn from '${serverConfigPath}'
14
303
 
15
- /** @type {import("/home/maxi/nuxt/better-auth/src/module.js")} */
16
- const _module = await jiti.import("/home/maxi/nuxt/better-auth/src/module.ts");
304
+ type _Config = ReturnType<typeof configFn>
305
+
306
+ declare module '#nuxt-better-auth' {
307
+ interface AuthUser extends InferUser<_Config> {}
308
+ interface AuthSession { session: InferSession<_Config>['session'], user: InferUser<_Config> }
309
+ interface ServerAuthContext {
310
+ runtimeConfig: RuntimeConfig
311
+ ${hasHubDb ? `db: typeof import('hub:db')['db']` : ""}
312
+ }
313
+ }
314
+ `
315
+ });
316
+ addTypeTemplate({
317
+ filename: "types/nuxt-better-auth-client.d.ts",
318
+ getContents: () => `
319
+ import type { createAppAuthClient } from '${clientConfigPath}'
320
+ declare module '#nuxt-better-auth' {
321
+ export type AppAuthClient = ReturnType<typeof createAppAuthClient>
322
+ }
323
+ `
324
+ });
325
+ addTypeTemplate({
326
+ filename: "types/nuxt-better-auth-nitro.d.ts",
327
+ getContents: () => `
328
+ declare module 'nitropack/types' {
329
+ interface NitroRouteRules {
330
+ auth?: import('${resolver.resolve("./runtime/types")}').AuthMeta
331
+ }
332
+ }
333
+ `
334
+ });
335
+ nuxt.hook("builder:watch", async (_event, relativePath) => {
336
+ if (relativePath.includes("auth.config")) {
337
+ await updateTemplates({ filter: (t) => t.filename.includes("nuxt-better-auth") });
338
+ }
339
+ });
340
+ addServerImportsDir(resolver.resolve("./runtime/server/utils"));
341
+ addServerScanDir(resolver.resolve("./runtime/server/middleware"));
342
+ addServerHandler({ route: "/api/auth/**", handler: resolver.resolve("./runtime/server/api/auth/[...all]") });
343
+ addImportsDir(resolver.resolve("./runtime/app/composables"));
344
+ addImportsDir(resolver.resolve("./runtime/utils"));
345
+ addPlugin({ src: resolver.resolve("./runtime/app/plugins/session.server"), mode: "server" });
346
+ addPlugin({ src: resolver.resolve("./runtime/app/plugins/session.client"), mode: "client" });
347
+ addComponentsDir({ path: resolver.resolve("./runtime/app/components") });
348
+ nuxt.hook("app:resolve", (app) => {
349
+ app.middleware.push({ name: "auth", path: resolver.resolve("./runtime/app/middleware/auth.global"), global: true });
350
+ });
351
+ if (hasHubDb) {
352
+ await setupBetterAuthSchema(nuxt, serverConfigPath, options);
353
+ }
354
+ const isProduction = process.env.NODE_ENV === "production" || !nuxt.options.dev;
355
+ if (!isProduction) {
356
+ setupDevTools(nuxt);
357
+ addServerHandler({ route: "/api/_better-auth/config", method: "get", handler: resolver.resolve("./runtime/server/api/_better-auth/config.get") });
358
+ if (hasHubDb) {
359
+ addServerHandler({ route: "/api/_better-auth/sessions", method: "get", handler: resolver.resolve("./runtime/server/api/_better-auth/sessions.get") });
360
+ addServerHandler({ route: "/api/_better-auth/sessions", method: "delete", handler: resolver.resolve("./runtime/server/api/_better-auth/sessions.delete") });
361
+ addServerHandler({ route: "/api/_better-auth/users", method: "get", handler: resolver.resolve("./runtime/server/api/_better-auth/users.get") });
362
+ addServerHandler({ route: "/api/_better-auth/accounts", method: "get", handler: resolver.resolve("./runtime/server/api/_better-auth/accounts.get") });
363
+ }
364
+ extendPages((pages) => {
365
+ pages.push({ name: "better-auth-devtools", path: "/__better-auth-devtools", file: resolver.resolve("./runtime/app/pages/__better-auth-devtools.vue") });
366
+ });
367
+ }
368
+ nuxt.hook("pages:extend", (pages) => {
369
+ const routeRules = nuxt.options.routeRules || {};
370
+ if (!Object.keys(routeRules).length)
371
+ return;
372
+ const matcher = toRouteMatcher(createRouter({ routes: routeRules }));
373
+ const applyMetaFromRules = (page) => {
374
+ const matches = matcher.matchAll(page.path);
375
+ if (!matches.length)
376
+ return;
377
+ const matchedRules = defu({}, ...matches.reverse());
378
+ if (matchedRules.auth !== void 0) {
379
+ page.meta = page.meta || {};
380
+ page.meta.auth = matchedRules.auth;
381
+ }
382
+ page.children?.forEach((child) => applyMetaFromRules(child));
383
+ };
384
+ pages.forEach((page) => applyMetaFromRules(page));
385
+ });
386
+ }
387
+ });
388
+ async function setupBetterAuthSchema(nuxt, serverConfigPath, options) {
389
+ const hub = nuxt.options.hub;
390
+ const dialect = getHubDialect(hub);
391
+ if (!dialect || !["sqlite", "postgresql", "mysql"].includes(dialect)) {
392
+ consola.warn(`Unsupported database dialect: ${dialect}`);
393
+ return;
394
+ }
395
+ const isProduction = !nuxt.options.dev;
396
+ try {
397
+ const configFile = `${serverConfigPath}.ts`;
398
+ const userConfig = await loadUserAuthConfig(configFile, isProduction);
399
+ const extendedConfig = {};
400
+ await nuxt.callHook("better-auth:config:extend", extendedConfig);
401
+ const plugins = [...userConfig.plugins || [], ...extendedConfig.plugins || []];
402
+ const { getAuthTables } = await import('better-auth/db');
403
+ const tables = getAuthTables({
404
+ plugins,
405
+ secondaryStorage: options.secondaryStorage ? {
406
+ get: async (_key) => null,
407
+ set: async (_key, _value, _ttl) => {
408
+ },
409
+ delete: async (_key) => {
410
+ }
411
+ } : void 0
412
+ });
413
+ const useUuid = userConfig.advanced?.database?.generateId === "uuid";
414
+ const hubCasing = getHubCasing(hub);
415
+ const schemaOptions = { ...options.schema, useUuid, casing: options.schema?.casing ?? hubCasing };
416
+ const schemaCode = generateDrizzleSchema(tables, dialect, schemaOptions);
417
+ const schemaDir = join(nuxt.options.buildDir, "better-auth");
418
+ const schemaPath = join(schemaDir, `schema.${dialect}.ts`);
419
+ await mkdir(schemaDir, { recursive: true });
420
+ await writeFile(schemaPath, schemaCode);
421
+ addTemplate({ filename: `better-auth/schema.${dialect}.ts`, getContents: () => schemaCode, write: true });
422
+ consola.info(`Generated ${dialect} schema with ${Object.keys(tables).length} tables`);
423
+ } catch (error) {
424
+ if (isProduction) {
425
+ throw error;
426
+ }
427
+ consola.error("Failed to generate schema:", error);
428
+ }
429
+ const nuxtWithHubHooks = nuxt;
430
+ nuxtWithHubHooks.hook("hub:db:schema:extend", ({ paths, dialect: hookDialect }) => {
431
+ const schemaPath = join(nuxt.options.buildDir, "better-auth", `schema.${hookDialect}.ts`);
432
+ if (existsSync(schemaPath)) {
433
+ paths.unshift(schemaPath);
434
+ }
435
+ });
436
+ }
17
437
 
18
- export default _module?.default ?? _module;
19
- export const defineClientAuth = _module.defineClientAuth;
20
- export const defineServerAuth = _module.defineServerAuth;
438
+ export { module$1 as default };