@lenne.tech/nuxt-extensions 1.2.6 → 1.2.8

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.
@@ -1,13 +1,7 @@
1
- import { useNuxtApp, useRuntimeConfig, useCookie, ref, computed, watch } from "#imports";
1
+ import { useNuxtApp, useCookie, useState, ref, computed, watch } from "#imports";
2
2
  import { ltArrayBufferToBase64Url, ltBase64UrlToUint8Array } from "../../utils/crypto.js";
3
- import { createLtAuthClient } from "../../lib/auth-client.js";
4
- let _authClient = null;
5
- function getAuthClient() {
6
- if (!_authClient) {
7
- _authClient = createLtAuthClient();
8
- }
9
- return _authClient;
10
- }
3
+ import { getLtApiBase } from "../../lib/auth-state.js";
4
+ import { useLtAuthClient } from "../use-lt-auth-client.js";
11
5
  function useTranslation() {
12
6
  const nuxtApp = useNuxtApp();
13
7
  const i18n = nuxtApp.$i18n;
@@ -19,7 +13,7 @@ function useTranslation() {
19
13
  };
20
14
  }
21
15
  export function useLtAuth() {
22
- const authClient = getAuthClient();
16
+ const authClient = useLtAuthClient();
23
17
  const t = useTranslation();
24
18
  const authState = useCookie("lt-auth-state", {
25
19
  maxAge: 60 * 60 * 24 * 7,
@@ -57,14 +51,11 @@ export function useLtAuth() {
57
51
  const isAuthenticated = computed(() => !!user.value);
58
52
  const isAdmin = computed(() => user.value?.role === "admin");
59
53
  const is2FAEnabled = computed(() => user.value?.twoFactorEnabled ?? false);
60
- function getApiBase() {
61
- const isDev = import.meta.dev;
62
- const runtimeConfig = useRuntimeConfig();
63
- const config = runtimeConfig.public.ltExtensions?.auth;
64
- const baseURL = config?.baseURL || "http://localhost:3000";
65
- const basePath = config?.basePath || "/iam";
66
- return isDev ? `/api${basePath}` : `${baseURL}${basePath}`;
67
- }
54
+ const features = useState(
55
+ "lt-auth-features",
56
+ () => ({})
57
+ );
58
+ const featuresFetched = useState("lt-auth-features-fetched", () => false);
68
59
  function setUser(userData, mode = "cookie") {
69
60
  const newState = { user: userData, authMode: mode };
70
61
  authState.value = newState;
@@ -98,7 +89,7 @@ export function useLtAuth() {
98
89
  }
99
90
  async function switchToJwtMode() {
100
91
  try {
101
- const apiBase = getApiBase();
92
+ const apiBase = getLtApiBase();
102
93
  const response = await fetch(`${apiBase}/token`, {
103
94
  method: "GET",
104
95
  credentials: "include"
@@ -184,6 +175,21 @@ export function useLtAuth() {
184
175
  return !!authState.value?.user;
185
176
  }
186
177
  }
178
+ async function fetchFeatures() {
179
+ try {
180
+ const apiBase = getLtApiBase();
181
+ const result = await $fetch(
182
+ `${apiBase}/features`
183
+ );
184
+ if (result) {
185
+ features.value = result;
186
+ featuresFetched.value = true;
187
+ }
188
+ return features.value;
189
+ } catch {
190
+ return features.value;
191
+ }
192
+ }
187
193
  const signIn = {
188
194
  ...authClient.signIn,
189
195
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
@@ -249,7 +255,7 @@ export function useLtAuth() {
249
255
  async function authenticateWithPasskey() {
250
256
  isLoading.value = true;
251
257
  try {
252
- const apiBase = getApiBase();
258
+ const apiBase = getLtApiBase();
253
259
  const optionsResponse = await fetchWithAuth(
254
260
  `${apiBase}/passkey/generate-authenticate-options`,
255
261
  {
@@ -310,6 +316,19 @@ export function useLtAuth() {
310
316
  if (authState.value) {
311
317
  authState.value = { ...authState.value, authMode: "jwt" };
312
318
  }
319
+ try {
320
+ const sessionResponse = await fetchWithAuth(`${apiBase}/get-session`, { method: "GET" });
321
+ if (sessionResponse.ok) {
322
+ const sessionData = await sessionResponse.json();
323
+ if (sessionData?.user) {
324
+ result.user = sessionData.user;
325
+ setUser(sessionData.user, "cookie");
326
+ switchToJwtMode().catch(() => {
327
+ });
328
+ }
329
+ }
330
+ } catch {
331
+ }
313
332
  }
314
333
  return { success: true, user: result.user, session: result.session };
315
334
  } catch (err) {
@@ -330,7 +349,7 @@ export function useLtAuth() {
330
349
  async function registerPasskey(name) {
331
350
  isLoading.value = true;
332
351
  try {
333
- const apiBase = getApiBase();
352
+ const apiBase = getLtApiBase();
334
353
  const optionsResponse = await fetchWithAuth(`${apiBase}/passkey/generate-register-options`, {
335
354
  method: "GET"
336
355
  });
@@ -429,6 +448,10 @@ export function useLtAuth() {
429
448
  isLoading.value = false;
430
449
  }
431
450
  }
451
+ if (import.meta.client && !featuresFetched.value) {
452
+ featuresFetched.value = true;
453
+ fetchFeatures();
454
+ }
432
455
  return {
433
456
  // Auth state
434
457
  authMode,
@@ -439,6 +462,9 @@ export function useLtAuth() {
439
462
  // User properties
440
463
  is2FAEnabled,
441
464
  isAdmin,
465
+ // Feature detection
466
+ features: computed(() => features.value),
467
+ fetchFeatures,
442
468
  // Auth actions
443
469
  authenticateWithPasskey,
444
470
  changePassword: authClient.changePassword,
@@ -3,30 +3,15 @@ import {
3
3
  getOrCreateLtAuthClient,
4
4
  resetLtAuthClientSingleton
5
5
  } from "../lib/auth-client.js";
6
+ import { isLtDevMode } from "../lib/auth-state.js";
6
7
  export function resetLtAuthClient() {
7
8
  resetLtAuthClientSingleton();
8
9
  }
9
- function isDevMode() {
10
- if (import.meta.server) {
11
- return process.env.NODE_ENV !== "production";
12
- }
13
- if (typeof window !== "undefined") {
14
- const buildId = window.__NUXT__?.config?.app?.buildId;
15
- if (buildId === "dev") {
16
- return true;
17
- }
18
- const hostname = window.location?.hostname;
19
- if (hostname === "localhost" || hostname === "127.0.0.1") {
20
- return true;
21
- }
22
- }
23
- return false;
24
- }
25
10
  export function useLtAuthClient() {
26
11
  try {
27
12
  const nuxtApp = useNuxtApp();
28
13
  const config = nuxtApp.$config?.public?.ltExtensions?.auth || {};
29
- const isDev = isDevMode();
14
+ const isDev = isLtDevMode();
30
15
  let basePath = config.basePath || "/iam";
31
16
  if (isDev && basePath && !basePath.startsWith("/api")) {
32
17
  basePath = `/api${basePath}`;
@@ -102,6 +102,9 @@ export declare function createLtAuthClient(config?: LtAuthClientConfig): {
102
102
  readonly isPending: boolean;
103
103
  readonly isRefetching: boolean;
104
104
  readonly error: import("@better-fetch/fetch").BetterFetchError | null;
105
+ readonly refetch: (queryParams?: {
106
+ query?: import("better-auth").SessionQueryParams;
107
+ } | undefined) => Promise<void>;
105
108
  }, {
106
109
  readonly data: {
107
110
  readonly user: {
@@ -127,6 +130,9 @@ export declare function createLtAuthClient(config?: LtAuthClientConfig): {
127
130
  readonly isPending: boolean;
128
131
  readonly isRefetching: boolean;
129
132
  readonly error: import("@better-fetch/fetch").BetterFetchError | null;
133
+ readonly refetch: (queryParams?: {
134
+ query?: import("better-auth").SessionQueryParams;
135
+ } | undefined) => Promise<void>;
130
136
  }>>;
131
137
  <F extends (...args: any) => any>(useFetch: F): Promise<{
132
138
  data: import("vue").Ref<{
@@ -222,6 +228,7 @@ export declare function createLtAuthClient(config?: LtAuthClientConfig): {
222
228
  })[];
223
229
  cache?: RequestCache | undefined;
224
230
  method: string;
231
+ window?: null | undefined;
225
232
  headers?: (HeadersInit & (HeadersInit | {
226
233
  accept: "application/json" | "text/plain" | "application/octet-stream";
227
234
  "content-type": "application/json" | "text/plain" | "application/x-www-form-urlencoded" | "multipart/form-data" | "application/octet-stream";
@@ -236,7 +243,6 @@ export declare function createLtAuthClient(config?: LtAuthClientConfig): {
236
243
  referrer?: string | undefined;
237
244
  referrerPolicy?: ReferrerPolicy | undefined;
238
245
  signal?: (AbortSignal | null) | undefined;
239
- window?: null | undefined;
240
246
  onRetry?: ((response: import("@better-fetch/fetch").ResponseContext) => Promise<void> | void) | undefined;
241
247
  hookOptions?: {
242
248
  cloneResponse?: boolean;
@@ -284,25 +290,25 @@ export declare function createLtAuthClient(config?: LtAuthClientConfig): {
284
290
  token: string | null;
285
291
  user: {
286
292
  id: string;
287
- email: string;
288
- name: string;
289
- image: string | null | undefined;
290
- emailVerified: boolean;
291
293
  createdAt: Date;
292
294
  updatedAt: Date;
293
- };
295
+ email: string;
296
+ emailVerified: boolean;
297
+ name: string;
298
+ image?: string | null | undefined;
299
+ } & Record<string, any>;
294
300
  } | {
295
301
  data: {
296
302
  token: string | null;
297
303
  user: {
298
304
  id: string;
299
- email: string;
300
- name: string;
301
- image: string | null | undefined;
302
- emailVerified: boolean;
303
305
  createdAt: Date;
304
306
  updatedAt: Date;
305
- };
307
+ email: string;
308
+ emailVerified: boolean;
309
+ name: string;
310
+ image?: string | null | undefined;
311
+ } & Record<string, any>;
306
312
  };
307
313
  error: null;
308
314
  } | {
@@ -461,7 +467,7 @@ export declare function createLtAuthClient(config?: LtAuthClientConfig): {
461
467
  email: string;
462
468
  name: string;
463
469
  password: string;
464
- }, options?: any) => Promise<NonNullable<{
470
+ } & Record<string, unknown>, options?: any) => Promise<NonNullable<{
465
471
  token: null;
466
472
  user: {
467
473
  id: string;
@@ -713,13 +719,13 @@ export declare function createLtAuthClient(config?: LtAuthClientConfig): {
713
719
  query?: Record<string, any> | undefined;
714
720
  fetchOptions?: FetchOptions | undefined;
715
721
  }> | undefined, data_1?: FetchOptions | undefined) => Promise<import("@better-fetch/fetch").BetterFetchResponse<{
722
+ scopes: string[];
716
723
  id: string;
717
- providerId: string;
718
724
  createdAt: Date;
719
725
  updatedAt: Date;
720
- accountId: string;
721
726
  userId: string;
722
- scopes: string[];
727
+ providerId: string;
728
+ accountId: string;
723
729
  }[], {
724
730
  code?: string | undefined;
725
731
  message?: string | undefined;
@@ -40,7 +40,7 @@ export declare function setLtAuthMode(mode: LtAuthMode): void;
40
40
  /**
41
41
  * Get the API base URL from runtime config
42
42
  *
43
- * @param basePath - The auth API base path (default: '/iam')
43
+ * @param basePath - The auth API base path. If not provided, reads from runtime config (default: '/iam')
44
44
  */
45
45
  export declare function getLtApiBase(basePath?: string): string;
46
46
  /**
@@ -68,7 +68,11 @@ export function setLtAuthMode(mode) {
68
68
  } catch {
69
69
  }
70
70
  }
71
- export function getLtApiBase(basePath = "/iam") {
71
+ export function getLtApiBase(basePath) {
72
+ if (!basePath && typeof window !== "undefined") {
73
+ basePath = window.__NUXT__?.config?.public?.ltExtensions?.auth?.basePath;
74
+ }
75
+ basePath = basePath || "/iam";
72
76
  const isDev = isLtDevMode();
73
77
  if (isDev) {
74
78
  return `/api${basePath}`;
@@ -96,6 +96,8 @@ export interface UseLtAuthReturn {
96
96
  user: ComputedRef<LtUser | null>;
97
97
  is2FAEnabled: ComputedRef<boolean>;
98
98
  isAdmin: ComputedRef<boolean>;
99
+ features: ComputedRef<Record<string, boolean | number | string[]>>;
100
+ fetchFeatures: () => Promise<Record<string, boolean | number | string[]>>;
99
101
  authenticateWithPasskey: () => Promise<LtPasskeyAuthResult>;
100
102
  changePassword: (params: {
101
103
  currentPassword: string;
@@ -120,7 +122,7 @@ export interface UseLtAuthReturn {
120
122
  email: string;
121
123
  name: string;
122
124
  password: string;
123
- }, options?: unknown) => Promise<unknown>;
125
+ } & Record<string, unknown>, options?: unknown) => Promise<unknown>;
124
126
  };
125
127
  switchToJwtMode: () => Promise<boolean>;
126
128
  validateSession: () => Promise<boolean>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lenne.tech/nuxt-extensions",
3
- "version": "1.2.6",
3
+ "version": "1.2.8",
4
4
  "description": "Reusable Nuxt 4 composables, components, and Better-Auth integration for lenne.tech projects",
5
5
  "repository": {
6
6
  "type": "git",
@@ -53,7 +53,7 @@
53
53
  "dev:prepare": "nuxt-module-build build --stub && nuxt-module-build prepare && nuxt prepare playground",
54
54
  "build": "nuxt-module-build build",
55
55
  "lint": "oxlint src/",
56
- "lint:fix": "oxlint --fix src/",
56
+ "lint:fix": "oxlint --fix --fix-suggestions src/",
57
57
  "format": "oxfmt --write src/",
58
58
  "format:check": "oxfmt --check src/",
59
59
  "test": "vitest run",
@@ -63,7 +63,7 @@
63
63
  "release": "npm run format:check && npm run lint && npm run test:types && npm run test && npm run prepack"
64
64
  },
65
65
  "dependencies": {
66
- "@nuxt/kit": "4.2.2"
66
+ "@nuxt/kit": "4.3.0"
67
67
  },
68
68
  "peerDependencies": {
69
69
  "@better-auth/passkey": ">=1.0.0",
@@ -87,24 +87,24 @@
87
87
  }
88
88
  },
89
89
  "devDependencies": {
90
- "@better-auth/passkey": "1.4.10",
90
+ "@better-auth/passkey": "1.4.18",
91
91
  "@nuxt/devtools": "3.1.1",
92
- "@playwright/test": "1.57.0",
92
+ "@playwright/test": "1.58.1",
93
93
  "@nuxt/module-builder": "1.0.2",
94
- "@nuxt/schema": "4.2.2",
94
+ "@nuxt/schema": "4.3.0",
95
95
  "@nuxt/test-utils": "3.23.0",
96
- "@types/node": "25.0.10",
97
- "@vitest/coverage-v8": "3.0.0",
96
+ "@types/node": "25.2.0",
97
+ "@vitest/coverage-v8": "4.0.18",
98
98
  "@vue/test-utils": "2.4.6",
99
- "better-auth": "1.4.10",
100
- "happy-dom": "20.3.7",
101
- "nuxt": "4.2.2",
102
- "oxfmt": "0.26.0",
103
- "oxlint": "0.17.0",
99
+ "better-auth": "1.4.18",
100
+ "happy-dom": "20.5.0",
101
+ "nuxt": "4.3.0",
102
+ "oxfmt": "0.28.0",
103
+ "oxlint": "1.43.0",
104
104
  "tus-js-client": "4.3.1",
105
105
  "typescript": "5.9.3",
106
- "vitest": "4.0.17",
107
- "vue-tsc": "3.2.2"
106
+ "vitest": "4.0.18",
107
+ "vue-tsc": "3.2.4"
108
108
  },
109
109
  "keywords": [
110
110
  "nuxt",
@@ -119,4 +119,4 @@
119
119
  "file-upload",
120
120
  "lenne-tech"
121
121
  ]
122
- }
122
+ }