@lenne.tech/nuxt-extensions 1.8.1 → 1.8.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/CLAUDE.md CHANGED
@@ -12,7 +12,7 @@ src/
12
12
  │ ├── components/ # Auto-imported components
13
13
  │ ├── lib/ # Library exports (auth-client, auth-state, ai)
14
14
  │ ├── middleware/ # Route middleware (system setup)
15
- │ ├── plugins/ # Nuxt plugins (auth interceptor, error translation)
15
+ │ ├── plugins/ # Nuxt plugins (auth interceptor, error translation, config check)
16
16
  │ ├── server/ # Nitro server routes (auth proxy)
17
17
  │ ├── testing/ # Playwright test helpers
18
18
  │ ├── types/ # TypeScript type definitions
package/README.md CHANGED
@@ -55,14 +55,16 @@ npm install tus-js-client
55
55
  All environment variables are resolved **at runtime** (not build time). This means you can build a Docker image once and deploy it to different environments by changing env vars — no rebuild needed.
56
56
 
57
57
  **SSR fallback chain:**
58
- `NUXT_API_URL` → `NUXT_PUBLIC_API_URL` → `auth.baseURL` (from nuxt.config.ts) → `http://localhost:3000`
58
+ `NUXT_API_URL` → `NUXT_PUBLIC_API_URL` → `auth.baseURL` (from nuxt.config.ts) → *(unset)*
59
59
 
60
60
  **Client fallback chain (no proxy):**
61
- `NUXT_PUBLIC_API_URL` → `auth.baseURL` (from nuxt.config.ts) → `http://localhost:3000`
61
+ `NUXT_PUBLIC_API_URL` → `auth.baseURL` (from nuxt.config.ts) → *(unset)*
62
62
 
63
63
  **Client with proxy (`NUXT_PUBLIC_API_PROXY=true`):**
64
64
  All requests go to `/api/{path}` — the Vite dev proxy forwards them to the backend.
65
65
 
66
+ > **No implicit `localhost` default.** If none of the sources above is set, there is **no** built-in fallback URL: API paths stay relative (e.g. `/iam/token`) and resolve against the app origin, so auth and setup calls 404 unless the app is served behind a same-origin reverse proxy. The module logs a one-time warning (`[LtExtensions] No API URL configured…`) at app init and on the first API call. Always set `NUXT_PUBLIC_API_URL` (and optionally `NUXT_API_URL` for SSR).
67
+
66
68
  > **Security:** `NUXT_API_URL` is never exposed to the client bundle. It stays in `runtimeConfig.apiUrl` (server only). This is important when using internal network addresses like `http://api.svc.cluster.local`.
67
69
 
68
70
  ### Deployment Scenarios
package/dist/module.mjs CHANGED
@@ -170,6 +170,7 @@ const module$1 = defineNuxtModule({
170
170
  filePath: resolve(`./runtime/components/transition/${componentName}.vue`)
171
171
  });
172
172
  }
173
+ addPlugin(resolve("./runtime/plugins/lt-config-check"));
173
174
  if (resolvedOptions.auth?.enabled && resolvedOptions.auth?.interceptor?.enabled) {
174
175
  addPlugin(resolve("./runtime/plugins/auth-interceptor.client"));
175
176
  }
@@ -90,6 +90,14 @@ export declare function getLtAuthCookieNames(): {
90
90
  * unchanged by the duplicate-tolerant read; it only decides which twin wins.
91
91
  */
92
92
  export declare function resolveLtAuthState(cookieString: string, stateCookieName?: string): LtAuthState | null;
93
+ /**
94
+ * Clear the one-shot warning state.
95
+ *
96
+ * INTERNAL — test-only. Module-scope state survives every test in a file that
97
+ * does not call `vi.resetModules()`, which would make assertions on these
98
+ * warnings depend on test order. Production code must never call this.
99
+ */
100
+ export declare function resetLtWarnOnceState(): void;
93
101
  /**
94
102
  * Determines if HTTP requests should use the Nuxt Vite dev proxy.
95
103
  *
@@ -124,6 +132,46 @@ export declare function resolveLtAuthState(cookieString: string, stateCookieName
124
132
  * @see https://github.com/lenneTech/nuxt-base-starter — Vite proxy configuration
125
133
  */
126
134
  export declare function isLocalDevApiProxy(): boolean;
135
+ /**
136
+ * Outcome of resolving the API base URL for the current render scope.
137
+ *
138
+ * INTERNAL — plumbing for {@link buildLtApiUrl} and the `lt-config-check` plugin.
139
+ * Not part of the public API (absent from the package barrels and auto-imports);
140
+ * reachable only via a deep path import. Shape may change without a major bump.
141
+ */
142
+ export interface LtApiUrlResolution {
143
+ /** Base URL to prefix onto API paths. `'/api'` in proxy mode, `''` when unresolved. */
144
+ baseUrl: string;
145
+ /** `true` when no API URL is configured for {@link scope}. */
146
+ missing: boolean;
147
+ /** `true` when the Vite dev proxy prefix is in use (see {@link isLocalDevApiProxy}). */
148
+ proxy: boolean;
149
+ /** The render scope the resolution was performed for. */
150
+ scope: 'client' | 'server';
151
+ }
152
+ /**
153
+ * Resolve the API base URL for the current render scope.
154
+ *
155
+ * INTERNAL — plumbing shared by {@link buildLtApiUrl} and the `lt-config-check`
156
+ * plugin; not part of the public API (absent from the barrels and auto-imports).
157
+ *
158
+ * Pure: it reads `runtimeConfig` and reports what it found, but never logs and
159
+ * never mutates. The `missing` flag lets callers decide how to surface the
160
+ * misconfiguration — {@link buildLtApiUrl} warns lazily, the `lt-config-check`
161
+ * plugin warns eagerly at app init. Both funnel through the same one-shot key,
162
+ * so the user sees exactly one warning either way.
163
+ *
164
+ * Throws when called outside a Nuxt runtime-config context; callers catch.
165
+ */
166
+ export declare function resolveLtApiBaseUrl(): LtApiUrlResolution;
167
+ /**
168
+ * Warn — at most once per module instance — that no API URL is configured.
169
+ *
170
+ * INTERNAL — shared by {@link buildLtApiUrl} and the `lt-config-check` plugin so
171
+ * an eager boot-time check and a lazy first-URL check never double-report. Not
172
+ * part of the public API (absent from the barrels and auto-imports).
173
+ */
174
+ export declare function warnMissingLtApiUrl(scope: 'client' | 'server'): void;
127
175
  /**
128
176
  * Build a full API URL for a given path, handling SSR, proxy, and direct modes.
129
177
  *
@@ -157,6 +205,9 @@ export declare function isLocalDevApiProxy(): boolean;
157
205
  *
158
206
  * Trailing slashes on the base URL are automatically stripped.
159
207
  *
208
+ * A missing API URL is reported at most once per module instance — the condition
209
+ * is constant for the process, so repeating it per call only drowns other output.
210
+ *
160
211
  * @param path - The API path (e.g., `/system-setup/status`, `/i18n/errors/de`)
161
212
  */
162
213
  export declare function buildLtApiUrl(path: string): string;
@@ -42,7 +42,17 @@ export function resolveLtAuthState(cookieString, stateCookieName) {
42
42
  }
43
43
  return fallback;
44
44
  }
45
- let _proxyFallbackWarned = false;
45
+ const warnedKeys = /* @__PURE__ */ new Set();
46
+ function warnOnce(key, message) {
47
+ if (warnedKeys.has(key)) {
48
+ return;
49
+ }
50
+ warnedKeys.add(key);
51
+ console.warn(message);
52
+ }
53
+ export function resetLtWarnOnceState() {
54
+ warnedKeys.clear();
55
+ }
46
56
  export function isLocalDevApiProxy() {
47
57
  if (import.meta.server) {
48
58
  return false;
@@ -55,12 +65,10 @@ export function isLocalDevApiProxy() {
55
65
  }
56
66
  const buildId = runtimeConfig?.app && runtimeConfig.app?.buildId;
57
67
  if (buildId === "dev") {
58
- if (!_proxyFallbackWarned) {
59
- _proxyFallbackWarned = true;
60
- console.warn(
61
- "\n\u26A0\uFE0F [LtExtensions] API proxy activated implicitly because nuxt dev was detected.\n To make this explicit, add NUXT_PUBLIC_API_PROXY=true to your .env file.\n If you do NOT want the proxy, set NUXT_PUBLIC_API_PROXY=false.\n"
62
- );
63
- }
68
+ warnOnce(
69
+ "api-proxy-fallback",
70
+ "\n\u26A0\uFE0F [LtExtensions] API proxy activated implicitly because nuxt dev was detected.\n To make this explicit, add NUXT_PUBLIC_API_PROXY=true to your .env file.\n If you do NOT want the proxy, set NUXT_PUBLIC_API_PROXY=false.\n"
71
+ );
64
72
  return true;
65
73
  }
66
74
  return false;
@@ -68,26 +76,37 @@ export function isLocalDevApiProxy() {
68
76
  return false;
69
77
  }
70
78
  }
79
+ const MISSING_API_URL_WARNING = {
80
+ client: "[LtExtensions] No API URL configured. Set NUXT_PUBLIC_API_URL. API paths stay relative and resolve against the app origin, so auth and setup calls 404 unless the app is served behind a same-origin reverse proxy.",
81
+ server: "[LtExtensions] No API URL configured for SSR. Set NUXT_API_URL or NUXT_PUBLIC_API_URL. API paths stay relative, so server-side requests never reach the backend: $fetch resolves them against Nitro (404) and global fetch rejects them as an invalid URL."
82
+ };
83
+ function stripTrailingSlashes(url) {
84
+ return url.replace(/\/+$/, "");
85
+ }
86
+ export function resolveLtApiBaseUrl() {
87
+ const runtimeConfig = useRuntimeConfig();
88
+ const publicUrl = runtimeConfig.public.apiUrl || "";
89
+ const authBaseURL = runtimeConfig.public?.ltExtensions?.auth?.baseURL || "";
90
+ if (import.meta.server) {
91
+ const apiUrl2 = runtimeConfig.apiUrl || publicUrl || authBaseURL;
92
+ return { baseUrl: stripTrailingSlashes(apiUrl2), missing: !apiUrl2, proxy: false, scope: "server" };
93
+ }
94
+ if (isLocalDevApiProxy()) {
95
+ return { baseUrl: "/api", missing: false, proxy: true, scope: "client" };
96
+ }
97
+ const apiUrl = publicUrl || authBaseURL;
98
+ return { baseUrl: stripTrailingSlashes(apiUrl), missing: !apiUrl, proxy: false, scope: "client" };
99
+ }
100
+ export function warnMissingLtApiUrl(scope) {
101
+ warnOnce(`missing-api-url:${scope}`, MISSING_API_URL_WARNING[scope]);
102
+ }
71
103
  export function buildLtApiUrl(path) {
72
104
  try {
73
- const runtimeConfig = useRuntimeConfig();
74
- const publicUrl = runtimeConfig.public.apiUrl || "";
75
- const authBaseURL = runtimeConfig.public?.ltExtensions?.auth?.baseURL || "";
76
- if (import.meta.server) {
77
- const apiUrl2 = runtimeConfig.apiUrl || publicUrl || authBaseURL;
78
- if (!apiUrl2) {
79
- console.warn("[LtExtensions] No API URL configured. Set NUXT_API_URL or NUXT_PUBLIC_API_URL.");
80
- }
81
- return `${(apiUrl2 || "").replace(/\/+$/, "")}${path}`;
82
- }
83
- if (isLocalDevApiProxy()) {
84
- return `/api${path}`;
85
- }
86
- const apiUrl = publicUrl || authBaseURL;
87
- if (!apiUrl) {
88
- console.warn("[LtExtensions] No API URL configured. Set NUXT_PUBLIC_API_URL.");
105
+ const { baseUrl, missing, scope } = resolveLtApiBaseUrl();
106
+ if (missing) {
107
+ warnMissingLtApiUrl(scope);
89
108
  }
90
- return `${(apiUrl || "").replace(/\/+$/, "")}${path}`;
109
+ return `${baseUrl}${path}`;
91
110
  } catch {
92
111
  return path;
93
112
  }
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Config Check Plugin
3
+ *
4
+ * Reports a missing API URL once at app init instead of waiting for whichever
5
+ * code path happens to build the first URL. The condition is read from
6
+ * `runtimeConfig` and is fixed for the module instance, so it belongs in a
7
+ * single boot-time check rather than in the hot `buildLtApiUrl()` path.
8
+ *
9
+ * Universal on purpose: SSR and the browser resolve the base URL from different
10
+ * keys (`NUXT_API_URL` vs `NUXT_PUBLIC_API_URL`) and can be misconfigured
11
+ * independently. On the server the plugin runs per request, but
12
+ * `warnMissingLtApiUrl()` is one-shot, so the warning still appears exactly once
13
+ * per process — and `buildLtApiUrl()` shares the same key, so it never repeats.
14
+ */
15
+ import type { ObjectPlugin, Plugin } from '#app';
16
+ declare const _default: ObjectPlugin & Plugin;
17
+ export default _default;
@@ -0,0 +1,11 @@
1
+ import { defineNuxtPlugin } from "#imports";
2
+ import { resolveLtApiBaseUrl, warnMissingLtApiUrl } from "../lib/auth-state.js";
3
+ export default defineNuxtPlugin(() => {
4
+ try {
5
+ const { missing, scope } = resolveLtApiBaseUrl();
6
+ if (missing) {
7
+ warnMissingLtApiUrl(scope);
8
+ }
9
+ } catch {
10
+ }
11
+ });
@@ -33,7 +33,7 @@ export interface LtAuthState {
33
33
  * All options have sensible defaults for nest-server compatibility
34
34
  */
35
35
  export interface LtAuthClientConfig {
36
- /** API base URL (default: from env or http://localhost:3000) */
36
+ /** API base URL (default: `''` — empty means requests use the current app origin; production passes an explicit URL from `NUXT_PUBLIC_API_URL`. No implicit `localhost` fallback) */
37
37
  baseURL?: string;
38
38
  /** Auth API base path (default: '/iam' - must match nest-server betterAuth.basePath) */
39
39
  basePath?: string;
@@ -56,7 +56,7 @@ export interface LtAuthCookieNamesOptions {
56
56
  export interface LtAuthModuleOptions {
57
57
  /** Auth API base path (default: '/iam' - must match nest-server betterAuth.basePath) */
58
58
  basePath?: string;
59
- /** API base URL (default: from env or http://localhost:3000) */
59
+ /** API base URL (default: `''` — resolved at runtime from `NUXT_PUBLIC_API_URL` / `NUXT_API_URL`; no implicit `localhost` fallback, so an unset URL keeps API paths relative to the app origin) */
60
60
  baseURL?: string;
61
61
  /** Override the cookie names used for auth state and JWT storage */
62
62
  cookieNames?: LtAuthCookieNamesOptions;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lenne.tech/nuxt-extensions",
3
- "version": "1.8.1",
3
+ "version": "1.8.2",
4
4
  "description": "Reusable Nuxt 4 composables, components, and Better-Auth integration for lenne.tech projects",
5
5
  "repository": {
6
6
  "type": "git",