@lenne.tech/nuxt-extensions 1.0.0 → 1.1.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.
Files changed (42) hide show
  1. package/LICENSE +1 -1
  2. package/dist/module.d.mts +4 -1
  3. package/dist/module.mjs +28 -9
  4. package/dist/runtime/components/transition/LtTransitionFade.vue +3 -1
  5. package/dist/runtime/components/transition/LtTransitionFadeScale.vue +3 -1
  6. package/dist/runtime/composables/auth/use-lt-auth.d.ts +1 -1
  7. package/dist/runtime/composables/auth/use-lt-auth.js +49 -13
  8. package/dist/runtime/composables/index.d.ts +6 -5
  9. package/dist/runtime/composables/index.js +1 -0
  10. package/dist/runtime/composables/use-lt-auth-client.d.ts +9 -1
  11. package/dist/runtime/composables/use-lt-auth-client.js +26 -2
  12. package/dist/runtime/composables/use-lt-error-translation.d.ts +33 -0
  13. package/dist/runtime/composables/use-lt-error-translation.js +133 -0
  14. package/dist/runtime/composables/use-lt-file.d.ts +1 -1
  15. package/dist/runtime/composables/use-lt-share.js +4 -1
  16. package/dist/runtime/composables/use-lt-tus-upload.d.ts +1 -1
  17. package/dist/runtime/composables/use-lt-tus-upload.js +7 -1
  18. package/dist/runtime/lib/auth-client.d.ts +1 -1
  19. package/dist/runtime/lib/auth-client.js +18 -6
  20. package/dist/runtime/lib/auth-state.d.ts +10 -1
  21. package/dist/runtime/lib/auth-state.js +17 -1
  22. package/dist/runtime/lib/index.d.ts +2 -2
  23. package/dist/runtime/locales/de.json +4 -0
  24. package/dist/runtime/locales/en.json +4 -0
  25. package/dist/runtime/plugins/auth-interceptor.client.d.ts +1 -1
  26. package/dist/runtime/plugins/auth-interceptor.client.js +12 -4
  27. package/dist/runtime/plugins/error-translation.client.d.ts +8 -0
  28. package/dist/runtime/plugins/error-translation.client.js +17 -0
  29. package/dist/runtime/server/tsconfig.json +1 -1
  30. package/dist/runtime/testing/index.d.ts +21 -0
  31. package/dist/runtime/testing/index.js +17 -0
  32. package/dist/runtime/testing/playwright-helpers.d.ts +296 -0
  33. package/dist/runtime/testing/playwright-helpers.js +127 -0
  34. package/dist/runtime/types/auth.d.ts +2 -2
  35. package/dist/runtime/types/error.d.ts +48 -0
  36. package/dist/runtime/types/error.js +0 -0
  37. package/dist/runtime/types/index.d.ts +4 -3
  38. package/dist/runtime/types/module.d.ts +17 -2
  39. package/dist/runtime/types/upload.d.ts +2 -2
  40. package/dist/runtime/utils/index.d.ts +2 -2
  41. package/dist/types.d.mts +7 -1
  42. package/package.json +18 -2
package/LICENSE CHANGED
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2025 lenne.Tech GmbH
3
+ Copyright (c) 2026 lenne.Tech GmbH
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
package/dist/module.d.mts CHANGED
@@ -1,6 +1,9 @@
1
1
  import * as _nuxt_schema from '@nuxt/schema';
2
2
  import { LtExtensionsModuleOptions } from '../dist/runtime/types/index.js';
3
- export { LtExtensionsModuleOptions } from '../dist/runtime/types/index.js';
3
+ export { LtAuthClientConfig, LtAuthMode, LtAuthResponse, LtAuthState, LtPasskeyAuthResult, LtPasskeyRegisterResult, LtUser, UseLtAuthReturn } from '../dist/runtime/types/auth.js';
4
+ export { LtFileInfo, LtUploadItem, LtUploadOptions, LtUploadProgress, LtUploadStatus, UseLtFileReturn, UseLtTusUploadReturn } from '../dist/runtime/types/upload.js';
5
+ export { LtAuthModuleOptions, LtErrorTranslationModuleOptions, LtExtensionsModuleOptions, LtExtensionsPublicRuntimeConfig, LtI18nModuleOptions, LtTusModuleOptions } from '../dist/runtime/types/module.js';
6
+ export { LtErrorTranslationResponse, LtParsedError, UseLtErrorTranslationReturn } from '../dist/runtime/types/error.js';
4
7
 
5
8
  declare const name = "@lenne.tech/nuxt-extensions";
6
9
  declare const version = "1.0.0";
package/dist/module.mjs CHANGED
@@ -18,6 +18,10 @@ const defaultOptions = {
18
18
  loginPath: "/auth/login",
19
19
  twoFactorRedirectPath: "/auth/2fa"
20
20
  },
21
+ errorTranslation: {
22
+ enabled: true,
23
+ defaultLocale: "de"
24
+ },
21
25
  i18n: {
22
26
  autoMerge: true
23
27
  },
@@ -38,6 +42,7 @@ const module$1 = defineNuxtModule({
38
42
  const { resolve } = createResolver(import.meta.url);
39
43
  const resolvedOptions = {
40
44
  auth: { ...defaultOptions.auth, ...options.auth },
45
+ errorTranslation: { ...defaultOptions.errorTranslation, ...options.errorTranslation },
41
46
  i18n: { ...defaultOptions.i18n, ...options.i18n },
42
47
  tus: { ...defaultOptions.tus, ...options.tus }
43
48
  };
@@ -56,6 +61,10 @@ const module$1 = defineNuxtModule({
56
61
  loginPath: resolvedOptions.auth?.loginPath || "/auth/login",
57
62
  twoFactorRedirectPath: resolvedOptions.auth?.twoFactorRedirectPath || "/auth/2fa"
58
63
  },
64
+ errorTranslation: {
65
+ enabled: resolvedOptions.errorTranslation?.enabled ?? true,
66
+ defaultLocale: resolvedOptions.errorTranslation?.defaultLocale || "de"
67
+ },
59
68
  tus: {
60
69
  defaultChunkSize: resolvedOptions.tus?.defaultChunkSize || 5 * 1024 * 1024,
61
70
  defaultEndpoint: resolvedOptions.tus?.defaultEndpoint || "/files/upload"
@@ -66,6 +75,10 @@ const module$1 = defineNuxtModule({
66
75
  { name: "useLtAuth", from: resolve("./runtime/composables/auth/use-lt-auth") },
67
76
  { name: "useLtAuthClient", from: resolve("./runtime/composables/use-lt-auth-client") },
68
77
  { name: "ltAuthClient", from: resolve("./runtime/composables/use-lt-auth-client") },
78
+ {
79
+ name: "useLtErrorTranslation",
80
+ from: resolve("./runtime/composables/use-lt-error-translation")
81
+ },
69
82
  { name: "useLtFile", from: resolve("./runtime/composables/use-lt-file") },
70
83
  { name: "useLtTusUpload", from: resolve("./runtime/composables/use-lt-tus-upload") },
71
84
  { name: "useLtShare", from: resolve("./runtime/composables/use-lt-share") },
@@ -102,16 +115,22 @@ const module$1 = defineNuxtModule({
102
115
  if (resolvedOptions.auth?.enabled && resolvedOptions.auth?.interceptor?.enabled) {
103
116
  addPlugin(resolve("./runtime/plugins/auth-interceptor.client"));
104
117
  }
118
+ if (resolvedOptions.errorTranslation?.enabled) {
119
+ addPlugin(resolve("./runtime/plugins/error-translation.client"));
120
+ }
105
121
  if (resolvedOptions.i18n?.autoMerge) {
106
- nuxt.hook("i18n:registerModule", (register) => {
107
- register({
108
- langDir: resolve("./runtime/locales"),
109
- locales: [
110
- { code: "en", file: "en.json" },
111
- { code: "de", file: "de.json" }
112
- ]
113
- });
114
- });
122
+ nuxt.hook(
123
+ "i18n:registerModule",
124
+ (register) => {
125
+ register({
126
+ langDir: resolve("./runtime/locales"),
127
+ locales: [
128
+ { code: "en", file: "en.json" },
129
+ { code: "de", file: "de.json" }
130
+ ]
131
+ });
132
+ }
133
+ );
115
134
  }
116
135
  nuxt.options.build.transpile.push(resolve("./runtime"));
117
136
  nuxt.options.build.transpile.push("tus-js-client");
@@ -6,7 +6,9 @@ const props = defineProps({
6
6
  </script>
7
7
 
8
8
  <template>
9
- <div :style="`--start-duration: ${props.startDuration}ms; --leave-duration: ${props.leaveDuration}ms;`">
9
+ <div
10
+ :style="`--start-duration: ${props.startDuration}ms; --leave-duration: ${props.leaveDuration}ms;`"
11
+ >
10
12
  <Transition
11
13
  enter-active-class="transition ease-out duration-[--start-duration]"
12
14
  enter-from-class="transform opacity-0"
@@ -6,7 +6,9 @@ const props = defineProps({
6
6
  </script>
7
7
 
8
8
  <template>
9
- <div :style="`--start-duration: ${props.startDuration}ms; --leave-duration: ${props.leaveDuration}ms;`">
9
+ <div
10
+ :style="`--start-duration: ${props.startDuration}ms; --leave-duration: ${props.leaveDuration}ms;`"
11
+ >
10
12
  <Transition
11
13
  enter-active-class="transition ease-out duration-[--start-duration]"
12
14
  enter-from-class="transform opacity-0 scale-95"
@@ -9,7 +9,7 @@
9
9
  * - If session cookie works -> use cookies
10
10
  * - If cookies fail (401) -> switch to JWT mode
11
11
  */
12
- import type { UseLtAuthReturn } from '../../types/index.js';
12
+ import type { UseLtAuthReturn } from "../../types/index.js";
13
13
  /**
14
14
  * Better Auth composable with Cookie/JWT dual-mode authentication
15
15
  *
@@ -81,7 +81,13 @@ export function useLtAuth() {
81
81
  const maxAge = 60 * 60 * 24 * 7;
82
82
  document.cookie = `lt-auth-state=${encodeURIComponent(JSON.stringify(clearedState))}; path=/; max-age=${maxAge}; samesite=lax`;
83
83
  document.cookie = `lt-jwt-token=; path=/; max-age=0`;
84
- const sessionCookieNames = ["better-auth.session_token", "better-auth.session", "__Secure-better-auth.session_token", "session_token", "session"];
84
+ const sessionCookieNames = [
85
+ "better-auth.session_token",
86
+ "better-auth.session",
87
+ "__Secure-better-auth.session_token",
88
+ "session_token",
89
+ "session"
90
+ ];
85
91
  for (const name of sessionCookieNames) {
86
92
  document.cookie = `${name}=; path=/; max-age=0`;
87
93
  document.cookie = `${name}=; path=/api; max-age=0`;
@@ -244,11 +250,17 @@ export function useLtAuth() {
244
250
  isLoading.value = true;
245
251
  try {
246
252
  const apiBase = getApiBase();
247
- const optionsResponse = await fetchWithAuth(`${apiBase}/passkey/generate-authenticate-options`, {
248
- method: "GET"
249
- });
253
+ const optionsResponse = await fetchWithAuth(
254
+ `${apiBase}/passkey/generate-authenticate-options`,
255
+ {
256
+ method: "GET"
257
+ }
258
+ );
250
259
  if (!optionsResponse.ok) {
251
- return { success: false, error: t("lt.auth.passkeyError", "Konnte Passkey-Optionen nicht laden") };
260
+ return {
261
+ success: false,
262
+ error: t("lt.auth.passkeyError", "Konnte Passkey-Optionen nicht laden")
263
+ };
252
264
  }
253
265
  const options = await optionsResponse.json();
254
266
  const challengeBuffer = ltBase64UrlToUint8Array(options.challenge).buffer;
@@ -284,7 +296,10 @@ export function useLtAuth() {
284
296
  });
285
297
  const result = await authResponse.json();
286
298
  if (!authResponse.ok) {
287
- return { success: false, error: result.message || t("lt.auth.passkeyFailed", "Passkey-Anmeldung fehlgeschlagen") };
299
+ return {
300
+ success: false,
301
+ error: result.message || t("lt.auth.passkeyFailed", "Passkey-Anmeldung fehlgeschlagen")
302
+ };
288
303
  }
289
304
  if (result.user) {
290
305
  setUser(result.user, "cookie");
@@ -300,9 +315,15 @@ export function useLtAuth() {
300
315
  return { success: true, user: result.user, session: result.session };
301
316
  } catch (err) {
302
317
  if (err instanceof Error && err.name === "NotAllowedError") {
303
- return { success: false, error: t("lt.auth.passkeyAborted", "Passkey-Authentifizierung wurde abgebrochen") };
318
+ return {
319
+ success: false,
320
+ error: t("lt.auth.passkeyAborted", "Passkey-Authentifizierung wurde abgebrochen")
321
+ };
304
322
  }
305
- return { success: false, error: err instanceof Error ? err.message : t("lt.auth.passkeyFailed", "Passkey-Anmeldung fehlgeschlagen") };
323
+ return {
324
+ success: false,
325
+ error: err instanceof Error ? err.message : t("lt.auth.passkeyFailed", "Passkey-Anmeldung fehlgeschlagen")
326
+ };
306
327
  } finally {
307
328
  isLoading.value = false;
308
329
  }
@@ -316,7 +337,10 @@ export function useLtAuth() {
316
337
  });
317
338
  if (!optionsResponse.ok) {
318
339
  const error = await optionsResponse.json().catch(() => ({}));
319
- return { success: false, error: error.message || t("lt.auth.registerOptionsError", "Konnte Registrierungsoptionen nicht laden") };
340
+ return {
341
+ success: false,
342
+ error: error.message || t("lt.auth.registerOptionsError", "Konnte Registrierungsoptionen nicht laden")
343
+ };
320
344
  }
321
345
  const options = await optionsResponse.json();
322
346
  const challengeBuffer = ltBase64UrlToUint8Array(options.challenge).buffer;
@@ -341,7 +365,10 @@ export function useLtAuth() {
341
365
  }
342
366
  });
343
367
  if (!credential) {
344
- return { success: false, error: t("lt.auth.passkeyCreationAborted", "Passkey-Erstellung abgebrochen") };
368
+ return {
369
+ success: false,
370
+ error: t("lt.auth.passkeyCreationAborted", "Passkey-Erstellung abgebrochen")
371
+ };
345
372
  }
346
373
  const attestationResponse = credential.response;
347
374
  const credentialBody = {
@@ -367,14 +394,23 @@ export function useLtAuth() {
367
394
  });
368
395
  const result = await registerResponse.json();
369
396
  if (!registerResponse.ok) {
370
- return { success: false, error: result.message || t("lt.auth.passkeyRegisterFailed", "Passkey-Registrierung fehlgeschlagen") };
397
+ return {
398
+ success: false,
399
+ error: result.message || t("lt.auth.passkeyRegisterFailed", "Passkey-Registrierung fehlgeschlagen")
400
+ };
371
401
  }
372
402
  return { success: true, passkey: result };
373
403
  } catch (err) {
374
404
  if (err instanceof Error && err.name === "NotAllowedError") {
375
- return { success: false, error: t("lt.auth.passkeyCreationAborted", "Passkey-Erstellung wurde abgebrochen") };
405
+ return {
406
+ success: false,
407
+ error: t("lt.auth.passkeyCreationAborted", "Passkey-Erstellung wurde abgebrochen")
408
+ };
376
409
  }
377
- return { success: false, error: err instanceof Error ? err.message : t("lt.auth.passkeyRegisterFailed", "Passkey-Registrierung fehlgeschlagen") };
410
+ return {
411
+ success: false,
412
+ error: err instanceof Error ? err.message : t("lt.auth.passkeyRegisterFailed", "Passkey-Registrierung fehlgeschlagen")
413
+ };
378
414
  } finally {
379
415
  isLoading.value = false;
380
416
  }
@@ -1,5 +1,6 @@
1
- export { useLtAuth } from './auth/use-lt-auth.js';
2
- export { useLtAuthClient, ltAuthClient } from './use-lt-auth-client.js';
3
- export { useLtTusUpload } from './use-lt-tus-upload.js';
4
- export { useLtFile } from './use-lt-file.js';
5
- export { useLtShare, type UseLtShareReturn } from './use-lt-share.js';
1
+ export { useLtAuth } from "./auth/use-lt-auth.js";
2
+ export { useLtAuthClient, ltAuthClient } from "./use-lt-auth-client.js";
3
+ export { useLtTusUpload } from "./use-lt-tus-upload.js";
4
+ export { useLtFile } from "./use-lt-file.js";
5
+ export { useLtShare, type UseLtShareReturn } from "./use-lt-share.js";
6
+ export { useLtErrorTranslation } from "./use-lt-error-translation.js";
@@ -3,3 +3,4 @@ export { useLtAuthClient, ltAuthClient } from "./use-lt-auth-client.js";
3
3
  export { useLtTusUpload } from "./use-lt-tus-upload.js";
4
4
  export { useLtFile } from "./use-lt-file.js";
5
5
  export { useLtShare } from "./use-lt-share.js";
6
+ export { useLtErrorTranslation } from "./use-lt-error-translation.js";
@@ -16,12 +16,20 @@
16
16
  * await authClient.passkey.listUserPasskeys();
17
17
  * ```
18
18
  */
19
- import { type LtAuthClient } from '../lib/auth-client.js';
19
+ import { type LtAuthClient } from "../lib/auth-client.js";
20
+ /**
21
+ * Reset the auth client singleton (useful for testing or config changes)
22
+ */
23
+ export declare function resetLtAuthClient(): void;
20
24
  /**
21
25
  * Returns the Better-Auth client singleton
22
26
  *
23
27
  * The client is created once and reused across all calls.
24
28
  * Configuration is read from RuntimeConfig on first call.
29
+ *
30
+ * IMPORTANT: In dev mode, the basePath is automatically prefixed with '/api'
31
+ * to leverage Nuxt's server proxy. This is required for WebAuthn/Passkey
32
+ * to work correctly (same-origin policy).
25
33
  */
26
34
  export declare function useLtAuthClient(): LtAuthClient;
27
35
  export declare const ltAuthClient: {
@@ -1,14 +1,38 @@
1
1
  import { useNuxtApp } from "#imports";
2
2
  import { createLtAuthClient } from "../lib/auth-client.js";
3
3
  let authClientInstance = null;
4
+ export function resetLtAuthClient() {
5
+ authClientInstance = null;
6
+ }
7
+ function isDevMode() {
8
+ if (import.meta.server) {
9
+ return process.env.NODE_ENV !== "production";
10
+ }
11
+ if (typeof window !== "undefined") {
12
+ const buildId = window.__NUXT__?.config?.app?.buildId;
13
+ if (buildId === "dev") {
14
+ return true;
15
+ }
16
+ const hostname = window.location?.hostname;
17
+ if (hostname === "localhost" || hostname === "127.0.0.1") {
18
+ return true;
19
+ }
20
+ }
21
+ return false;
22
+ }
4
23
  export function useLtAuthClient() {
5
24
  if (!authClientInstance) {
6
25
  try {
7
26
  const nuxtApp = useNuxtApp();
8
27
  const config = nuxtApp.$config?.public?.ltExtensions?.auth || {};
28
+ const isDev = isDevMode();
29
+ let basePath = config.basePath || "/iam";
30
+ if (isDev && basePath && !basePath.startsWith("/api")) {
31
+ basePath = `/api${basePath}`;
32
+ }
9
33
  authClientInstance = createLtAuthClient({
10
- baseURL: config.baseURL,
11
- basePath: config.basePath,
34
+ baseURL: isDev ? "" : config.baseURL,
35
+ basePath,
12
36
  twoFactorRedirectPath: config.twoFactorRedirectPath,
13
37
  enableAdmin: config.enableAdmin,
14
38
  enableTwoFactor: config.enableTwoFactor,
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Error Translation Composable
3
+ *
4
+ * Translates backend error codes to user-friendly messages.
5
+ * Works with or without @nuxtjs/i18n.
6
+ *
7
+ * Backend error format: "#LTNS_0100: Unauthorized - User is not logged in"
8
+ * Translations loaded from: GET /api/i18n/errors/:locale
9
+ */
10
+ import type { UseLtErrorTranslationReturn } from "../types/error.js";
11
+ /**
12
+ * Error Translation composable
13
+ *
14
+ * @returns Functions and state for error translation
15
+ *
16
+ * @example
17
+ * ```typescript
18
+ * const { translateError, showErrorToast, parseError } = useLtErrorTranslation();
19
+ *
20
+ * // Translate error from API response
21
+ * const message = translateError(error.message);
22
+ * // Input: "#LTNS_0100: Unauthorized - User is not logged in"
23
+ * // Output: "Sie sind nicht angemeldet." (when locale is 'de')
24
+ *
25
+ * // Show error as toast
26
+ * showErrorToast(error, 'Login fehlgeschlagen');
27
+ *
28
+ * // Parse error for custom handling
29
+ * const parsed = parseError(error.message);
30
+ * // { code: 'LTNS_0100', developerMessage: 'Unauthorized...', translatedMessage: '...' }
31
+ * ```
32
+ */
33
+ export declare function useLtErrorTranslation(): UseLtErrorTranslationReturn;
@@ -0,0 +1,133 @@
1
+ import { computed, ref, useState, useNuxtApp, useRuntimeConfig } from "#imports";
2
+ const ERROR_CODE_REGEX = /^#([A-Z_]+_\d+):\s*(.+)$/;
3
+ export function useLtErrorTranslation() {
4
+ const nuxtApp = useNuxtApp();
5
+ const runtimeConfig = useRuntimeConfig();
6
+ const translations = useState(
7
+ "lt-error-translations",
8
+ () => ({})
9
+ );
10
+ const isLoading = ref(false);
11
+ const config = runtimeConfig.public?.ltExtensions?.errorTranslation;
12
+ function t(key, germanFallback) {
13
+ const i18n = nuxtApp.$i18n;
14
+ if (!i18n?.t) {
15
+ return germanFallback;
16
+ }
17
+ return i18n.t(key);
18
+ }
19
+ function detectLocale() {
20
+ const i18n = nuxtApp.$i18n;
21
+ if (i18n?.locale?.value) {
22
+ return i18n.locale.value;
23
+ }
24
+ if (import.meta.client && navigator?.language) {
25
+ const browserLang = navigator.language.split("-")[0] ?? "de";
26
+ if (["de", "en"].includes(browserLang)) {
27
+ return browserLang;
28
+ }
29
+ }
30
+ return config?.defaultLocale || "de";
31
+ }
32
+ function getApiBase() {
33
+ return runtimeConfig.public?.ltExtensions?.auth?.baseURL || "";
34
+ }
35
+ async function loadTranslations(locale) {
36
+ const targetLocale = locale || detectLocale();
37
+ if (translations.value[targetLocale]) {
38
+ return;
39
+ }
40
+ if (isLoading.value) {
41
+ return;
42
+ }
43
+ isLoading.value = true;
44
+ try {
45
+ const apiBase = getApiBase();
46
+ const response = await $fetch(
47
+ `${apiBase}/api/i18n/errors/${targetLocale}`
48
+ );
49
+ if (response?.errors) {
50
+ translations.value = {
51
+ ...translations.value,
52
+ [targetLocale]: response.errors
53
+ };
54
+ }
55
+ } catch (error) {
56
+ console.warn(`[LtErrorTranslation] Failed to load translations for ${targetLocale}:`, error);
57
+ } finally {
58
+ isLoading.value = false;
59
+ }
60
+ }
61
+ function extractMessage(errorOrMessage) {
62
+ if (typeof errorOrMessage === "string") {
63
+ return errorOrMessage;
64
+ }
65
+ if (errorOrMessage instanceof Error) {
66
+ return errorOrMessage.message;
67
+ }
68
+ if (typeof errorOrMessage === "object" && errorOrMessage !== null) {
69
+ const obj = errorOrMessage;
70
+ return obj.message || obj.error?.message || obj.data?.message || obj.statusMessage || String(obj);
71
+ }
72
+ return String(errorOrMessage);
73
+ }
74
+ function parseError(errorOrMessage) {
75
+ const message = extractMessage(errorOrMessage);
76
+ const match = message.match(ERROR_CODE_REGEX);
77
+ if (match) {
78
+ const code = match[1] || "";
79
+ const developerMessage = match[2] || "";
80
+ const locale = detectLocale();
81
+ const localeTranslations = translations.value[locale] || {};
82
+ const translatedMessage = localeTranslations[code] || developerMessage;
83
+ return {
84
+ code,
85
+ developerMessage,
86
+ translatedMessage
87
+ };
88
+ }
89
+ return {
90
+ code: null,
91
+ developerMessage: message,
92
+ translatedMessage: message
93
+ };
94
+ }
95
+ function translateError(errorOrMessage) {
96
+ const parsed = parseError(errorOrMessage);
97
+ return parsed.translatedMessage;
98
+ }
99
+ function showErrorToast(errorOrMessage, title) {
100
+ if (!import.meta.client) {
101
+ return;
102
+ }
103
+ const parsed = parseError(errorOrMessage);
104
+ try {
105
+ nuxtApp.runWithContext(() => {
106
+ const toastComposable = nuxtApp.useToast || globalThis.useToast;
107
+ if (typeof toastComposable === "function") {
108
+ const toast = toastComposable();
109
+ toast.add({
110
+ color: "error",
111
+ title: title || t("lt.error.title", "Fehler"),
112
+ description: parsed.translatedMessage
113
+ });
114
+ } else {
115
+ console.error("[LtErrorTranslation]", parsed.translatedMessage);
116
+ }
117
+ });
118
+ } catch {
119
+ console.error("[LtErrorTranslation]", parsed.translatedMessage);
120
+ }
121
+ }
122
+ const currentLocale = computed(() => detectLocale());
123
+ const isLoaded = computed(() => !!translations.value[currentLocale.value]);
124
+ return {
125
+ translateError,
126
+ parseError,
127
+ showErrorToast,
128
+ loadTranslations,
129
+ isLoaded,
130
+ isLoading,
131
+ currentLocale
132
+ };
133
+ }
@@ -6,7 +6,7 @@
6
6
  * - URL generation for file access
7
7
  * - File size and duration formatting
8
8
  */
9
- import type { UseLtFileReturn } from '../types/index.js';
9
+ import type { UseLtFileReturn } from "../types/index.js";
10
10
  /**
11
11
  * File utilities composable
12
12
  *
@@ -32,7 +32,10 @@ export function useLtShare() {
32
32
  const toast = useToast();
33
33
  toast.add({
34
34
  color: "success",
35
- description: t("lt.share.copiedDescription", "Der Link wurde in die Zwischenablage kopiert."),
35
+ description: t(
36
+ "lt.share.copiedDescription",
37
+ "Der Link wurde in die Zwischenablage kopiert."
38
+ ),
36
39
  title: t("lt.share.copied", "Link kopiert")
37
40
  });
38
41
  } catch {
@@ -6,7 +6,7 @@
6
6
  *
7
7
  * @see https://tus.io/
8
8
  */
9
- import type { LtUploadOptions, UseLtTusUploadReturn } from '../types/index.js';
9
+ import type { LtUploadOptions, UseLtTusUploadReturn } from "../types/index.js";
10
10
  /**
11
11
  * TUS Upload composable with pause/resume support
12
12
  *
@@ -135,7 +135,13 @@ export function useLtTusUpload(defaultOptions = {}) {
135
135
  file,
136
136
  id,
137
137
  metadata: defaultConfig.metadata,
138
- progress: { bytesTotal: file.size, bytesUploaded: 0, percentage: 0, remainingTime: 0, speed: 0 },
138
+ progress: {
139
+ bytesTotal: file.size,
140
+ bytesUploaded: 0,
141
+ percentage: 0,
142
+ remainingTime: 0,
143
+ speed: 0
144
+ },
139
145
  status: "idle"
140
146
  };
141
147
  const newMap = new Map(uploadItems.value);
@@ -7,7 +7,7 @@
7
7
  * SECURITY: Passwords are hashed with SHA256 client-side to prevent
8
8
  * plain text password transmission over the network.
9
9
  */
10
- import type { LtAuthClientConfig } from '../types/index.js';
10
+ import type { LtAuthClientConfig } from "../types/index.js";
11
11
  /**
12
12
  * Creates a configured Better-Auth client with password hashing
13
13
  *
@@ -3,9 +3,9 @@ import { adminClient, twoFactorClient } from "better-auth/client/plugins";
3
3
  import { createAuthClient } from "better-auth/vue";
4
4
  import { navigateTo } from "#imports";
5
5
  import { ltSha256 } from "../utils/crypto.js";
6
- import { createLtAuthFetch } from "./auth-state.js";
6
+ import { createLtAuthFetch, isLtDevMode } from "./auth-state.js";
7
7
  export function createLtAuthClient(config = {}) {
8
- const isDev = import.meta.dev || process.env.NODE_ENV === "local";
8
+ const isDev = isLtDevMode();
9
9
  const defaultBaseURL = isDev ? "" : import.meta.env?.VITE_API_URL || process.env.API_URL || "http://localhost:3000";
10
10
  const defaultBasePath = isDev ? "/api/iam" : "/iam";
11
11
  const {
@@ -58,8 +58,14 @@ export function createLtAuthClient(config = {}) {
58
58
  */
59
59
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
60
60
  changePassword: async (params, options) => {
61
- const [hashedCurrent, hashedNew] = await Promise.all([ltSha256(params.currentPassword), ltSha256(params.newPassword)]);
62
- return baseClient.changePassword?.({ currentPassword: hashedCurrent, newPassword: hashedNew }, options);
61
+ const [hashedCurrent, hashedNew] = await Promise.all([
62
+ ltSha256(params.currentPassword),
63
+ ltSha256(params.newPassword)
64
+ ]);
65
+ return baseClient.changePassword?.(
66
+ { currentPassword: hashedCurrent, newPassword: hashedNew },
67
+ options
68
+ );
63
69
  },
64
70
  /**
65
71
  * Reset password with token (new password is hashed before sending)
@@ -67,7 +73,10 @@ export function createLtAuthClient(config = {}) {
67
73
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
68
74
  resetPassword: async (params, options) => {
69
75
  const hashedPassword = await ltSha256(params.newPassword);
70
- return baseClient.resetPassword?.({ newPassword: hashedPassword, token: params.token }, options);
76
+ return baseClient.resetPassword?.(
77
+ { newPassword: hashedPassword, token: params.token },
78
+ options
79
+ );
71
80
  },
72
81
  // Override signIn to hash password (keep passkey method from plugin)
73
82
  signIn: {
@@ -127,7 +136,10 @@ export function createLtAuthClient(config = {}) {
127
136
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
128
137
  generateBackupCodes: async (params, options) => {
129
138
  const hashedPassword = await ltSha256(params.password);
130
- return baseClient.twoFactor.generateBackupCodes({ password: hashedPassword }, options);
139
+ return baseClient.twoFactor.generateBackupCodes(
140
+ { password: hashedPassword },
141
+ options
142
+ );
131
143
  },
132
144
  /**
133
145
  * Verify TOTP code (pass through to base client)
@@ -11,7 +11,16 @@
11
11
  *
12
12
  * The state is persisted in cookies for SSR compatibility.
13
13
  */
14
- import type { LtAuthMode } from '../types/index.js';
14
+ import type { LtAuthMode } from "../types/index.js";
15
+ /**
16
+ * Detects if we're running in development mode at runtime.
17
+ *
18
+ * Note: `import.meta.dev` is evaluated at build time and doesn't work
19
+ * correctly for pre-built modules. This function uses runtime checks instead.
20
+ *
21
+ * @returns true if running in development mode
22
+ */
23
+ export declare function isLtDevMode(): boolean;
15
24
  /**
16
25
  * Get the current auth mode from cookie
17
26
  */
@@ -1,3 +1,19 @@
1
+ export function isLtDevMode() {
2
+ if (import.meta.server) {
3
+ return process.env.NODE_ENV !== "production";
4
+ }
5
+ if (typeof window !== "undefined") {
6
+ const buildId = window.__NUXT__?.config?.app?.buildId;
7
+ if (buildId === "dev") {
8
+ return true;
9
+ }
10
+ const hostname = window.location?.hostname;
11
+ if (hostname === "localhost" || hostname === "127.0.0.1") {
12
+ return true;
13
+ }
14
+ }
15
+ return false;
16
+ }
1
17
  export function getLtAuthMode() {
2
18
  if (import.meta.server) return "cookie";
3
19
  try {
@@ -53,7 +69,7 @@ export function setLtAuthMode(mode) {
53
69
  }
54
70
  }
55
71
  export function getLtApiBase(basePath = "/iam") {
56
- const isDev = import.meta.dev;
72
+ const isDev = isLtDevMode();
57
73
  if (isDev) {
58
74
  return `/api${basePath}`;
59
75
  }
@@ -1,2 +1,2 @@
1
- export { attemptLtJwtSwitch, createLtAuthFetch, getLtApiBase, getLtAuthMode, getLtJwtToken, isLtAuthenticated, ltAuthFetch, setLtAuthMode, setLtJwtToken, } from './auth-state.js';
2
- export { createLtAuthClient, type LtAuthClient } from './auth-client.js';
1
+ export { attemptLtJwtSwitch, createLtAuthFetch, getLtApiBase, getLtAuthMode, getLtJwtToken, isLtAuthenticated, ltAuthFetch, setLtAuthMode, setLtJwtToken, } from "./auth-state.js";
2
+ export { createLtAuthClient, type LtAuthClient } from "./auth-client.js";