@nuxtjs/better-auth 0.1.3 → 0.2.0

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 CHANGED
@@ -4,7 +4,7 @@ Nuxt module for [Better Auth](https://better-auth.com) with Nuxt-native route pr
4
4
 
5
5
  ## Who this is for
6
6
 
7
- Use this module if you want Better Auth in a Nuxt 4 app and you want the Nuxt-specific pieces handled for you:
7
+ Use this module if you want Better Auth in a Nuxt app and you want the Nuxt-specific pieces handled for you:
8
8
 
9
9
  - `useUserSession()` for reactive auth state
10
10
  - `requireUserSession(event)` and related server helpers
@@ -14,7 +14,7 @@ Use this module if you want Better Auth in a Nuxt 4 app and you want the Nuxt-sp
14
14
 
15
15
  ## Install the module
16
16
 
17
- For the fastest path in a Nuxt 4 app:
17
+ For the fastest path in a Nuxt app:
18
18
 
19
19
  ```bash
20
20
  npx nuxi module add @nuxtjs/better-auth
package/dist/module.json CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@nuxtjs/better-auth",
3
- "version": "0.1.3",
3
+ "version": "0.2.0",
4
4
  "configKey": "auth",
5
5
  "compatibility": {
6
6
  "nuxt": ">=4.0.0"
7
7
  },
8
8
  "builder": {
9
- "@nuxt/module-builder": "1.0.2",
9
+ "@nuxt/module-builder": "1.0.3",
10
10
  "unbuild": "unknown"
11
11
  }
12
12
  }
package/dist/module.mjs CHANGED
@@ -10,7 +10,7 @@ import { randomBytes } from 'node:crypto';
10
10
  import { isCI, isTest } from 'std-env';
11
11
  export { defineClientAuth, defineServerAuth } from '../dist/runtime/config.js';
12
12
 
13
- const version = "0.1.3";
13
+ const version = "0.2.0";
14
14
 
15
15
  const CONFIG_EXTENSIONS = [".ts", ".js"];
16
16
  const CONFIG_EXTENSION_RE = /\.(?:ts|js)$/;
@@ -592,10 +592,7 @@ function setupRuntimeConfig(input) {
592
592
  redirectQueryKey: options.redirectQueryKey ?? "redirect",
593
593
  useDatabase: databaseProvider !== "none",
594
594
  databaseProvider,
595
- clientOnly,
596
- session: {
597
- skipHydratedSsrGetSession: options.session?.skipHydratedSsrGetSession ?? false
598
- }
595
+ clientOnly
599
596
  });
600
597
  if (clientOnly) {
601
598
  const siteUrl = nuxt.options.runtimeConfig.public.siteUrl;
@@ -6,7 +6,6 @@ import { isRecord } from "../internal/utils.js";
6
6
  import { createVueSafeAuthFacade, isAuthProxyProbeKey } from "../internal/vue-safe-auth-proxy.js";
7
7
  import { wrapAuthMethod } from "../internal/wrap-auth-method.js";
8
8
  import { getAuthRuntimeFlags, useRawAuthClient } from "./useAuthClient.js";
9
- let _sessionSignalListenerBound = false;
10
9
  let _signOutPromise = null;
11
10
  function createServerOnlyActionNamespace(path) {
12
11
  return new Proxy({}, {
@@ -22,24 +21,6 @@ function createServerOnlyActionNamespace(path) {
22
21
  }
23
22
  const _signInServerOnly = createServerOnlyActionNamespace("signIn");
24
23
  const _signUpServerOnly = createServerOnlyActionNamespace("signUp");
25
- function ensureSessionSignalListener(client, onSignal) {
26
- if (_sessionSignalListenerBound)
27
- return;
28
- const store = client.$store;
29
- if (!isRecord(store))
30
- return;
31
- const listen = store.listen;
32
- if (typeof listen !== "function")
33
- return;
34
- _sessionSignalListenerBound = true;
35
- const listenFn = listen;
36
- listenFn("$sessionSignal", async () => {
37
- try {
38
- await onSignal();
39
- } catch {
40
- }
41
- });
42
- }
43
24
  export function useUserSession() {
44
25
  const runtimeFlags = getAuthRuntimeFlags();
45
26
  const runtimeConfig = useRuntimeConfig();
@@ -60,21 +41,6 @@ export function useUserSession() {
60
41
  return false;
61
42
  return !session.value && !user.value;
62
43
  });
63
- const skipHydratedSsrGetSession = computed(() => {
64
- const authConfig = runtimeConfig.public.auth;
65
- return Boolean(authConfig?.session?.skipHydratedSsrGetSession);
66
- });
67
- const shouldSkipInitialClientSessionFetch = computed(() => {
68
- if (!skipHydratedSsrGetSession.value)
69
- return false;
70
- if (!runtimeFlags.client)
71
- return false;
72
- if (!nuxtApp.payload.serverRendered)
73
- return false;
74
- if (isPrerenderedPayload.value)
75
- return false;
76
- return Boolean(session.value && user.value);
77
- });
78
44
  if (isPrerenderHydrationEmptySnapshot.value && authReady.value && !prerenderReadyResetQueued.value) {
79
45
  prerenderReadyResetQueued.value = true;
80
46
  nuxtApp.hook("app:suspense:resolve", () => {
@@ -86,8 +52,6 @@ export function useUserSession() {
86
52
  }
87
53
  });
88
54
  }
89
- if (shouldSkipInitialClientSessionFetch.value && !authReady.value)
90
- authReady.value = true;
91
55
  function clearSession() {
92
56
  session.value = null;
93
57
  user.value = null;
@@ -123,7 +87,7 @@ export function useUserSession() {
123
87
  throw error;
124
88
  }
125
89
  }
126
- if (runtimeFlags.client && rawClient && !shouldSkipInitialClientSessionFetch.value) {
90
+ if (runtimeFlags.client && rawClient) {
127
91
  const clientSession = rawClient.useSession();
128
92
  watch(
129
93
  () => clientSession.value,
@@ -172,9 +136,6 @@ export function useUserSession() {
172
136
  }, 5e3);
173
137
  });
174
138
  }
175
- if (runtimeFlags.client && rawClient && shouldSkipInitialClientSessionFetch.value) {
176
- ensureSessionSignalListener(rawClient, () => fetchSession({ force: true }));
177
- }
178
139
  async function signOut(options) {
179
140
  if (!rawClient)
180
141
  throw new Error("signOut can only be called on client-side");
@@ -1,11 +1,11 @@
1
1
  import type { BetterAuthOptions, BetterAuthPlugin } from 'better-auth';
2
2
  import type { BetterAuthClientOptions } from 'better-auth/client';
3
- import type { Casing } from 'drizzle-orm/utils';
4
3
  import type { ServerAuthContext as BaseServerAuthContext } from './types/augment.js';
5
4
  import { createAuthClient } from 'better-auth/vue';
6
5
  export interface ServerAuthContextExtension {
7
6
  }
8
7
  export type ServerAuthContext = BaseServerAuthContext & ServerAuthContextExtension;
8
+ export type SchemaCasing = 'snake_case' | 'camelCase';
9
9
  export interface ClientAuthContext {
10
10
  siteUrl: string;
11
11
  }
@@ -49,16 +49,6 @@ export interface BetterAuthModuleOptions {
49
49
  * Default: 'redirect'
50
50
  */
51
51
  redirectQueryKey?: string;
52
- session?: {
53
- /**
54
- * When enabled, and session/user are already hydrated from SSR, skip the initial
55
- * client `/api/auth/get-session` bootstrap request. This also skips Better Auth's
56
- * session refresh manager on those pages.
57
- *
58
- * Default: false
59
- */
60
- skipHydratedSsrGetSession?: boolean;
61
- };
62
52
  /**
63
53
  * Enable secondary storage for sessions.
64
54
  * - `true`: Use NuxtHub KV (requires hub.kv: true)
@@ -71,7 +61,7 @@ export interface BetterAuthModuleOptions {
71
61
  /** Plural table names: user → users. Default: false */
72
62
  usePlural?: boolean;
73
63
  /** Column/table name casing. Explicit value takes precedence over hub.db.casing. */
74
- casing?: Casing;
64
+ casing?: SchemaCasing;
75
65
  /** PostgreSQL schema namespace. */
76
66
  schemaName?: string;
77
67
  };
@@ -88,9 +78,6 @@ export interface AuthRuntimeConfig {
88
78
  useDatabase: boolean;
89
79
  databaseProvider: EffectiveDatabaseProviderId;
90
80
  clientOnly: boolean;
91
- session: {
92
- skipHydratedSsrGetSession: boolean;
93
- };
94
81
  }
95
82
  export interface AuthPrivateRuntimeConfig {
96
83
  hubSecondaryStorage: boolean | 'custom';
@@ -1,4 +1,8 @@
1
1
  import { z } from 'zod';
2
- export declare const paginationQuerySchema: any;
2
+ export declare const paginationQuerySchema: z.ZodObject<{
3
+ page: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
4
+ limit: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
5
+ search: z.ZodDefault<z.ZodString>;
6
+ }, z.core.$strip>;
3
7
  export type PaginationQuery = z.infer<typeof paginationQuerySchema>;
4
8
  export declare function sanitizeSearchPattern(search: string): string;
@@ -15,7 +15,7 @@ export default defineEventHandler(async (event) => {
15
15
  return { success: true };
16
16
  } catch (error) {
17
17
  if (error instanceof z.ZodError) {
18
- throw createAuthError(400, error.errors[0]?.message || "Invalid request");
18
+ throw createAuthError(400, error.issues[0]?.message || "Invalid request");
19
19
  }
20
20
  console.error("[DevTools] Delete session failed:", error);
21
21
  throw createAuthError(500, "Failed to delete session");
@@ -8,7 +8,7 @@ type UserAuthConfig = AuthOptions & {
8
8
  trustedOrigins?: BetterAuthOptions['trustedOrigins'];
9
9
  secondaryStorage?: BetterAuthOptions['secondaryStorage'];
10
10
  };
11
- type ResolvedAuthOptions = UserAuthConfig & {
11
+ type ResolvedAuthOptions = Omit<UserAuthConfig, 'secret' | 'baseURL' | 'trustedOrigins' | 'database'> & {
12
12
  secret: string;
13
13
  baseURL: string;
14
14
  trustedOrigins?: BetterAuthOptions['trustedOrigins'];
@@ -2,6 +2,7 @@ import type { AppSession, AuthSession, RequireSessionOptions } from '#nuxt-bette
2
2
  import type { ServerEvent } from '../internal/nitro-compat.js';
3
3
  export declare function getRequestSession(event: ServerEvent): Promise<AppSession | null>;
4
4
  export declare function getUserSession(event: ServerEvent): Promise<AppSession | null>;
5
+ export declare function setRequestSession(event: ServerEvent, session: AppSession | null): void;
5
6
  export declare function refreshSessionCookieCache(event: ServerEvent): Promise<AppSession | null>;
6
7
  export declare function setSessionCookie(event: ServerEvent, token: string): Promise<void>;
7
8
  export declare function createSession(event: ServerEvent, userId: string): Promise<AuthSession>;
@@ -194,37 +194,47 @@ function updateRequestHeaders(event, sessionCookie, clearedCookieNames) {
194
194
  }
195
195
  export async function getRequestSession(event) {
196
196
  const context = getRequestSessionContext(event);
197
- if (context.requestSession !== void 0)
198
- return context.requestSession;
199
197
  const inFlight = context[requestSessionLoadKey];
200
198
  if (inFlight)
201
199
  return inFlight;
200
+ if (context.requestSession !== void 0)
201
+ return context.requestSession;
202
202
  const load = loadSession(event);
203
203
  context[requestSessionLoadKey] = load;
204
204
  try {
205
205
  const session = await load;
206
- context.requestSession = session;
206
+ if (context[requestSessionLoadKey] === load)
207
+ context.requestSession = session;
207
208
  return session;
208
209
  } finally {
209
- delete context[requestSessionLoadKey];
210
+ if (context[requestSessionLoadKey] === load)
211
+ delete context[requestSessionLoadKey];
210
212
  }
211
213
  }
212
214
  export async function getUserSession(event) {
213
215
  const context = getRequestSessionContext(event);
214
- if (context.requestSession !== void 0)
215
- return context.requestSession;
216
216
  const inFlight = context[requestSessionLoadKey];
217
217
  if (inFlight)
218
218
  return inFlight;
219
+ if (context.requestSession !== void 0)
220
+ return context.requestSession;
219
221
  return loadSession(event);
220
222
  }
223
+ export function setRequestSession(event, session) {
224
+ const context = getRequestSessionContext(event);
225
+ context.requestSession = session;
226
+ delete context[requestSessionLoadKey];
227
+ }
221
228
  export async function refreshSessionCookieCache(event) {
222
229
  const context = getRequestSessionContext(event);
223
230
  const inFlight = context[requestSessionLoadKey];
224
- if (inFlight)
225
- await inFlight.catch(() => void 0);
226
- delete context.requestSession;
227
- const load = loadFreshSession(event).then(({ headers, response }) => {
231
+ const load = (inFlight ?? Promise.resolve(null)).catch(() => void 0).then(async () => {
232
+ if (context[requestSessionLoadKey] !== load)
233
+ return context.requestSession ?? null;
234
+ delete context.requestSession;
235
+ const { headers, response } = await loadFreshSession(event);
236
+ if (context[requestSessionLoadKey] !== load)
237
+ return context.requestSession ?? null;
228
238
  appendSetCookieHeaders(event, headers);
229
239
  context.requestSession = response;
230
240
  return response;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@nuxtjs/better-auth",
3
3
  "type": "module",
4
- "version": "0.1.3",
4
+ "version": "0.2.0",
5
5
  "description": "Nuxt module for Better Auth integration with NuxtHub, route protection, session management, and role-based access",
6
6
  "author": "onmax",
7
7
  "license": "MIT",
@@ -41,9 +41,12 @@
41
41
  "files": [
42
42
  "dist"
43
43
  ],
44
+ "engines": {
45
+ "node": "^22.19.0 || ^24.11.0 || >=26.0.0"
46
+ },
44
47
  "peerDependencies": {
45
48
  "@nuxthub/core": ">=0.10.5",
46
- "better-auth": ">=1.7.0-rc.5 <2"
49
+ "better-auth": ">=1.7.1 <2"
47
50
  },
48
51
  "peerDependenciesMeta": {
49
52
  "@nuxthub/core": {
@@ -51,50 +54,49 @@
51
54
  }
52
55
  },
53
56
  "dependencies": {
54
- "@nuxt/kit": "^4.3.1",
55
- "auth": "1.7.0-rc.5",
56
- "defu": "^6.1.4",
57
+ "@nuxt/devtools-kit": "^3.4.1",
58
+ "@nuxt/kit": "^4.5.2",
59
+ "auth": "1.7.1",
60
+ "consola": "^3.4.2",
61
+ "defu": "^6.1.7",
57
62
  "h3": "^1.15.11",
58
- "jiti": "^2.6.1",
63
+ "jiti": "^2.7.0",
59
64
  "pathe": "^2.0.3",
60
65
  "radix3": "^1.1.2",
61
- "std-env": "^4.0.0",
62
- "ufo": "^1.6.4"
66
+ "std-env": "^4.2.0",
67
+ "ufo": "^1.6.4",
68
+ "zod": "^4.4.3"
63
69
  },
64
70
  "devDependencies": {
65
- "@antfu/eslint-config": "^9.0.0",
66
- "@libsql/client": "^0.17.3",
71
+ "@antfu/eslint-config": "^9.3.0",
72
+ "@libsql/client": "^0.17.4",
67
73
  "@libsql/linux-x64-gnu": "0.5.29",
68
- "@nuxt/devtools": "^3.2.3",
69
- "@nuxt/devtools-kit": "^3.2.4",
70
- "@nuxt/module-builder": "^1.0.2",
71
- "@nuxt/schema": "^4.3.1",
72
- "@nuxt/test-utils": "^4.0.0",
73
- "@nuxthub/core": "^0.10.6",
74
- "@pinia/nuxt": "^0.11.3",
75
- "@types/better-sqlite3": "^7.6.13",
76
- "@types/node": "latest",
77
- "better-auth": "1.7.0-rc.5",
74
+ "@nuxt/devtools": "^3.4.1",
75
+ "@nuxt/module-builder": "^1.0.3",
76
+ "@nuxt/schema": "^4.5.2",
77
+ "@nuxt/test-utils": "^4.1.0",
78
+ "@nuxthub/core": "^0.10.8",
79
+ "@pinia/nuxt": "^1.0.2",
80
+ "@types/better-sqlite3": "^9.6.0",
81
+ "@types/node": "^24.13.3",
82
+ "better-auth": "1.7.1",
78
83
  "better-call": "1.4.0",
79
84
  "better-sqlite3": "^12.6.2",
80
- "consola": "^3.4.2",
81
85
  "drizzle-kit": "^0.31.10",
82
- "drizzle-orm": "^0.45.1",
83
- "eslint": "^10.0.3",
84
- "nitropack": "2.13.1",
85
- "nuxt": "^4.3.1",
86
- "pinia": "^3.0.4",
87
- "pkg-pr-new": "0.0.87",
86
+ "drizzle-orm": "^0.45.2",
87
+ "eslint": "^10.8.1",
88
+ "nitropack": "2.13.4",
89
+ "nuxt": "^4.5.2",
90
+ "pinia": "^4.0.3",
91
+ "pkg-pr-new": "0.0.88",
88
92
  "postgres": "^3.4.9",
89
- "tinyexec": "^1.1.1",
90
93
  "typescript": "~5.9.3",
91
94
  "unstorage": "^1.17.5",
92
- "vitest": "^4.0.18",
93
- "vitest-package-exports": "^1.2.0",
94
- "vue": "3.5.29",
95
- "vue-tsc": "^3.2.7",
96
- "wrangler": "4.72.0",
97
- "yaml": "^2.8.3"
95
+ "vitest": "^4.1.11",
96
+ "vue": "3.5.41",
97
+ "vue-tsc": "^3.3.10",
98
+ "wrangler": "4.124.0",
99
+ "yaml": "^2.9.0"
98
100
  },
99
101
  "scripts": {
100
102
  "build": "pnpm build:docs",