@lenne.tech/nuxt-extensions 1.5.4 → 1.7.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 (35) hide show
  1. package/CLAUDE.md +64 -18
  2. package/README.md +72 -0
  3. package/dist/module.d.mts +5 -2
  4. package/dist/module.mjs +36 -1
  5. package/dist/runtime/composables/auth/use-lt-auth.js +7 -16
  6. package/dist/runtime/composables/index.d.ts +7 -0
  7. package/dist/runtime/composables/index.js +7 -0
  8. package/dist/runtime/composables/use-lt-ai-admin.d.ts +9 -0
  9. package/dist/runtime/composables/use-lt-ai-admin.js +36 -0
  10. package/dist/runtime/composables/use-lt-ai-chat.d.ts +8 -0
  11. package/dist/runtime/composables/use-lt-ai-chat.js +141 -0
  12. package/dist/runtime/composables/use-lt-ai-connections.d.ts +7 -0
  13. package/dist/runtime/composables/use-lt-ai-connections.js +33 -0
  14. package/dist/runtime/composables/use-lt-ai-placeholders.d.ts +10 -0
  15. package/dist/runtime/composables/use-lt-ai-placeholders.js +25 -0
  16. package/dist/runtime/composables/use-lt-ai-prompts.d.ts +13 -0
  17. package/dist/runtime/composables/use-lt-ai-prompts.js +70 -0
  18. package/dist/runtime/composables/use-lt-ai-usage.d.ts +7 -0
  19. package/dist/runtime/composables/use-lt-ai-usage.js +19 -0
  20. package/dist/runtime/composables/use-lt-ai.d.ts +8 -0
  21. package/dist/runtime/composables/use-lt-ai.js +68 -0
  22. package/dist/runtime/lib/ai.d.ts +48 -0
  23. package/dist/runtime/lib/ai.js +96 -0
  24. package/dist/runtime/lib/auth-client.d.ts +1 -1
  25. package/dist/runtime/lib/auth-state.d.ts +33 -2
  26. package/dist/runtime/lib/auth-state.js +40 -7
  27. package/dist/runtime/lib/index.d.ts +2 -1
  28. package/dist/runtime/lib/index.js +16 -1
  29. package/dist/runtime/plugins/auth-interceptor.client.js +26 -22
  30. package/dist/runtime/types/ai.d.ts +521 -0
  31. package/dist/runtime/types/ai.js +0 -0
  32. package/dist/runtime/types/index.d.ts +2 -1
  33. package/dist/runtime/types/module.d.ts +51 -0
  34. package/dist/types.d.mts +4 -2
  35. package/package.json +21 -32
@@ -0,0 +1,25 @@
1
+ import { readonly, ref } from "#imports";
2
+ import { ltAiRequest } from "../lib/ai.js";
3
+ export function useLtAiPlaceholders() {
4
+ const placeholders = ref([]);
5
+ const loading = ref(false);
6
+ const error = ref(null);
7
+ async function load() {
8
+ loading.value = true;
9
+ error.value = null;
10
+ try {
11
+ placeholders.value = await ltAiRequest("GET", "/placeholders") || [];
12
+ } catch (err) {
13
+ error.value = err.message;
14
+ placeholders.value = [];
15
+ } finally {
16
+ loading.value = false;
17
+ }
18
+ }
19
+ return {
20
+ error: readonly(error),
21
+ load,
22
+ loading: readonly(loading),
23
+ placeholders: readonly(placeholders)
24
+ };
25
+ }
@@ -0,0 +1,13 @@
1
+ /**
2
+ * User-facing prompt composable.
3
+ *
4
+ * Any signed-in user can author re-usable prompts ("Vorlagen") for themselves
5
+ * (`scope: 'user'` = private) or for their tenant (`scope: 'tenant'` = public).
6
+ * `load()` returns every prompt the current user is allowed to see (own +
7
+ * tenant), already filtered by the server.
8
+ *
9
+ * Pair this with your own prompt picker / settings UI. The nuxt-base-starter
10
+ * ships a reference picker inside the AI chat and a settings page.
11
+ */
12
+ import type { UseLtAiPromptsReturn } from '../types/ai.js';
13
+ export declare function useLtAiPrompts(): UseLtAiPromptsReturn;
@@ -0,0 +1,70 @@
1
+ import { readonly, ref } from "#imports";
2
+ import { ltAiRequest } from "../lib/ai.js";
3
+ export function useLtAiPrompts() {
4
+ const prompts = ref([]);
5
+ const loading = ref(false);
6
+ const error = ref(null);
7
+ async function load() {
8
+ loading.value = true;
9
+ error.value = null;
10
+ try {
11
+ prompts.value = await ltAiRequest("GET", "/prompts") || [];
12
+ } catch (err) {
13
+ error.value = err.message;
14
+ prompts.value = [];
15
+ } finally {
16
+ loading.value = false;
17
+ }
18
+ }
19
+ async function create(input) {
20
+ loading.value = true;
21
+ error.value = null;
22
+ try {
23
+ const created = await ltAiRequest("POST", "/prompts", input);
24
+ await load();
25
+ return created;
26
+ } catch (err) {
27
+ error.value = err.message;
28
+ throw err;
29
+ } finally {
30
+ loading.value = false;
31
+ }
32
+ }
33
+ async function update(id, input) {
34
+ loading.value = true;
35
+ error.value = null;
36
+ try {
37
+ const updated = await ltAiRequest("PUT", `/prompts/${encodeURIComponent(id)}`, input);
38
+ await load();
39
+ return updated;
40
+ } catch (err) {
41
+ error.value = err.message;
42
+ throw err;
43
+ } finally {
44
+ loading.value = false;
45
+ }
46
+ }
47
+ async function remove(id) {
48
+ loading.value = true;
49
+ error.value = null;
50
+ try {
51
+ const removed = await ltAiRequest("DELETE", `/prompts/${encodeURIComponent(id)}`);
52
+ await load();
53
+ return removed;
54
+ } catch (err) {
55
+ error.value = err.message;
56
+ throw err;
57
+ } finally {
58
+ loading.value = false;
59
+ }
60
+ }
61
+ return {
62
+ create,
63
+ error: readonly(error),
64
+ load,
65
+ loading: readonly(loading),
66
+ prompts: readonly(prompts),
67
+ remove,
68
+ update
69
+ };
70
+ }
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Token/prompt usage for the current user (and tenant), from `GET /ai/usage`.
3
+ * The compact per-response budget is also available on every prompt response
4
+ * (see {@link useLtAiChat}); this composable loads the full breakdown on demand.
5
+ */
6
+ import type { UseLtAiUsageReturn } from '../types/ai.js';
7
+ export declare function useLtAiUsage(): UseLtAiUsageReturn;
@@ -0,0 +1,19 @@
1
+ import { readonly, ref } from "#imports";
2
+ import { ltAiRequest } from "../lib/ai.js";
3
+ export function useLtAiUsage() {
4
+ const usage = ref(null);
5
+ const loading = ref(false);
6
+ const error = ref(null);
7
+ async function load() {
8
+ loading.value = true;
9
+ error.value = null;
10
+ try {
11
+ usage.value = await ltAiRequest("GET", "/usage");
12
+ } catch (err) {
13
+ error.value = err.message;
14
+ } finally {
15
+ loading.value = false;
16
+ }
17
+ }
18
+ return { error: readonly(error), load, loading: readonly(loading), usage: readonly(usage) };
19
+ }
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Core AI composable: one-shot prompts and SSE streaming against the
3
+ * @lenne.tech/nest-server AI module (`/ai/prompt`, `/ai/stream`).
4
+ *
5
+ * Headless — pair it with your own UI, or use the higher-level {@link useLtAiChat}.
6
+ */
7
+ import type { UseLtAiReturn } from '../types/ai.js';
8
+ export declare function useLtAi(): UseLtAiReturn;
@@ -0,0 +1,68 @@
1
+ import { readonly, ref } from "#imports";
2
+ import { buildLtAiUrl, ltAiRequest, ltAiResponseError, parseLtAiSseStream } from "../lib/ai.js";
3
+ import { ltAuthFetch } from "../lib/auth-state.js";
4
+ export function useLtAi() {
5
+ const loading = ref(false);
6
+ const streaming = ref(false);
7
+ const error = ref(null);
8
+ async function prompt(input) {
9
+ loading.value = true;
10
+ error.value = null;
11
+ try {
12
+ return await ltAiRequest("POST", "/prompt", input);
13
+ } catch (err) {
14
+ error.value = err.message;
15
+ throw err;
16
+ } finally {
17
+ loading.value = false;
18
+ }
19
+ }
20
+ async function promptStream(input, handlers, options) {
21
+ streaming.value = true;
22
+ error.value = null;
23
+ let final;
24
+ try {
25
+ const response = await ltAuthFetch(buildLtAiUrl("/stream"), {
26
+ body: JSON.stringify(input),
27
+ headers: { Accept: "text/event-stream", "Content-Type": "application/json" },
28
+ method: "POST",
29
+ signal: options?.signal
30
+ });
31
+ if (!response.ok) {
32
+ throw await ltAiResponseError(response);
33
+ }
34
+ await parseLtAiSseStream(response, (event) => {
35
+ handlers?.onEvent?.(event);
36
+ switch (event.type) {
37
+ case "action":
38
+ handlers?.onAction?.(event.action);
39
+ break;
40
+ case "error":
41
+ error.value = event.message;
42
+ handlers?.onError?.(event.message);
43
+ break;
44
+ case "final":
45
+ final = event.response;
46
+ handlers?.onFinal?.(event.response);
47
+ break;
48
+ case "token":
49
+ handlers?.onToken?.(event.token);
50
+ break;
51
+ }
52
+ });
53
+ return final;
54
+ } catch (err) {
55
+ error.value = err.message;
56
+ throw err;
57
+ } finally {
58
+ streaming.value = false;
59
+ }
60
+ }
61
+ return {
62
+ error: readonly(error),
63
+ loading: readonly(loading),
64
+ prompt,
65
+ promptStream,
66
+ streaming: readonly(streaming)
67
+ };
68
+ }
@@ -0,0 +1,48 @@
1
+ /**
2
+ * AI module client helpers: URL building, authenticated JSON requests, and a
3
+ * robust SSE stream parser for `POST /ai/stream`.
4
+ *
5
+ * All requests go through {@link ltAuthFetch} (Cookie/JWT dual-mode, 401 fallback)
6
+ * and {@link buildLtApiUrl} (SSR/proxy/direct URL resolution), so the AI client
7
+ * behaves exactly like the rest of the library.
8
+ */
9
+ import type { LtAiStreamEvent } from '../types/ai.js';
10
+ /** Resolve the AI base path from runtime config (default `/ai`). */
11
+ export declare function getLtAiBasePath(): string;
12
+ /** Build a full URL for an AI endpoint path (e.g. `/prompt`, `/connections/available`). */
13
+ export declare function buildLtAiUrl(path: string): string;
14
+ /**
15
+ * Turn a non-ok AI response into an Error using the backend's (translated) message.
16
+ *
17
+ * The resulting Error carries a numeric `status` field (the HTTP status code) so
18
+ * consumers can branch on response codes without re-parsing the message. The
19
+ * message is capped at ~1 KiB to bound downstream UI rendering / logging.
20
+ *
21
+ * Consumers should pipe the message through `useLtErrorTranslation()` for the
22
+ * user-facing localisation step — this helper preserves the raw backend string.
23
+ */
24
+ export declare function ltAiResponseError(response: Response): Promise<Error & {
25
+ status: number;
26
+ }>;
27
+ /**
28
+ * Authenticated JSON request against an AI endpoint. Throws {@link ltAiResponseError}
29
+ * on a non-2xx response, otherwise returns the parsed body (or `undefined` for 204).
30
+ */
31
+ export declare function ltAiRequest<T>(method: string, path: string, body?: unknown, options?: {
32
+ signal?: AbortSignal;
33
+ }): Promise<T>;
34
+ /**
35
+ * Parse a `text/event-stream` AI response, invoking `onEvent` for each parsed
36
+ * {@link LtAiStreamEvent}. Buffers partial lines across chunks and tolerates
37
+ * malformed/keep-alive lines.
38
+ *
39
+ * `options.signal` lets the caller stop parsing without cancelling the
40
+ * underlying fetch — useful for component-unmount races. When the signal fires
41
+ * the reader is cancelled and the function resolves cleanly.
42
+ *
43
+ * Throws when a single line exceeds `LT_AI_SSE_LINE_LIMIT` (1 MiB) — guards
44
+ * against a misbehaving proxy/backend that never delivers a newline.
45
+ */
46
+ export declare function parseLtAiSseStream(response: Response, onEvent: (event: LtAiStreamEvent) => void, options?: {
47
+ signal?: AbortSignal;
48
+ }): Promise<void>;
@@ -0,0 +1,96 @@
1
+ import { useRuntimeConfig } from "#imports";
2
+ import { buildLtApiUrl, ltAuthFetch } from "./auth-state.js";
3
+ const LT_AI_SSE_LINE_LIMIT = 1024 * 1024;
4
+ const LT_AI_ERROR_MESSAGE_LIMIT = 1024;
5
+ export function getLtAiBasePath() {
6
+ try {
7
+ const rc = useRuntimeConfig();
8
+ const ext = rc.public.ltExtensions;
9
+ return ext?.ai?.basePath || "/ai";
10
+ } catch {
11
+ return "/ai";
12
+ }
13
+ }
14
+ export function buildLtAiUrl(path) {
15
+ return buildLtApiUrl(`${getLtAiBasePath()}${path}`);
16
+ }
17
+ export async function ltAiResponseError(response) {
18
+ let message = `HTTP ${response.status}`;
19
+ try {
20
+ const body = await response.json();
21
+ const detail = body?.message ?? body?.error;
22
+ if (detail) {
23
+ const raw = Array.isArray(detail) ? detail.join(", ") : String(detail);
24
+ message = raw.length > LT_AI_ERROR_MESSAGE_LIMIT ? `${raw.slice(0, LT_AI_ERROR_MESSAGE_LIMIT)}\u2026` : raw;
25
+ }
26
+ } catch {
27
+ }
28
+ const error = new Error(message);
29
+ error.status = response.status;
30
+ return error;
31
+ }
32
+ export async function ltAiRequest(method, path, body, options) {
33
+ const init = { method, signal: options?.signal };
34
+ if (body !== void 0) {
35
+ init.headers = { Accept: "application/json", "Content-Type": "application/json" };
36
+ init.body = JSON.stringify(body);
37
+ } else {
38
+ init.headers = { Accept: "application/json" };
39
+ }
40
+ const response = await ltAuthFetch(buildLtAiUrl(path), init);
41
+ if (!response.ok) {
42
+ throw await ltAiResponseError(response);
43
+ }
44
+ if (response.status === 204) {
45
+ return void 0;
46
+ }
47
+ return await response.json();
48
+ }
49
+ export async function parseLtAiSseStream(response, onEvent, options) {
50
+ const body = response.body;
51
+ if (!body) {
52
+ throw new Error("AI stream response has no body");
53
+ }
54
+ const reader = body.getReader();
55
+ const decoder = new TextDecoder();
56
+ let buffer = "";
57
+ const handleLine = (line) => {
58
+ const trimmed = line.trim();
59
+ if (!trimmed.startsWith("data:")) {
60
+ return;
61
+ }
62
+ const payload = trimmed.slice(5).trim();
63
+ if (!payload || payload === "[DONE]") {
64
+ return;
65
+ }
66
+ try {
67
+ onEvent(JSON.parse(payload));
68
+ } catch {
69
+ }
70
+ };
71
+ try {
72
+ while (!options?.signal?.aborted) {
73
+ const { done, value } = await reader.read();
74
+ if (done) {
75
+ break;
76
+ }
77
+ buffer += decoder.decode(value, { stream: true });
78
+ if (buffer.length > LT_AI_SSE_LINE_LIMIT) {
79
+ throw new Error("AI stream line exceeds maximum allowed size");
80
+ }
81
+ const lines = buffer.split("\n");
82
+ buffer = lines.pop() ?? "";
83
+ for (const line of lines) {
84
+ handleLine(line);
85
+ }
86
+ }
87
+ if (options?.signal?.aborted) {
88
+ reader.cancel().catch(() => {
89
+ });
90
+ } else if (buffer) {
91
+ handleLine(buffer);
92
+ }
93
+ } finally {
94
+ reader.releaseLock?.();
95
+ }
96
+ }
@@ -226,8 +226,8 @@ export declare function createLtAuthClient(config?: LtAuthClientConfig): {
226
226
  onResponse: ((context: import("better-auth/client").ResponseContext) => Promise<Response | void | import("better-auth/client").ResponseContext> | Response | import("better-auth/client").ResponseContext | void) | undefined;
227
227
  };
228
228
  })[];
229
- cache?: RequestCache | undefined;
230
229
  priority?: RequestPriority | undefined;
230
+ cache?: RequestCache | undefined;
231
231
  credentials?: RequestCredentials;
232
232
  headers?: (HeadersInit & (HeadersInit | {
233
233
  accept: "application/json" | "text/plain" | "application/octet-stream";
@@ -12,6 +12,20 @@
12
12
  * The state is persisted in cookies for SSR compatibility.
13
13
  */
14
14
  import type { LtAuthMode } from '../types/index.js';
15
+ /** Default name of the auth-state cookie. Used when the module config has not been resolved yet. */
16
+ export declare const LT_AUTH_STATE_COOKIE_DEFAULT = "lt-auth-state";
17
+ /** Default name of the JWT-token cookie. Used when the module config has not been resolved yet. */
18
+ export declare const LT_JWT_TOKEN_COOKIE_DEFAULT = "lt-jwt-token";
19
+ /**
20
+ * Resolve the configured auth cookie names from runtime config.
21
+ *
22
+ * Falls back to the defaults (`lt-auth-state`, `lt-jwt-token`) so callers
23
+ * keep working even outside a Nuxt context (tests, edge SSR boots).
24
+ */
25
+ export declare function getLtAuthCookieNames(): {
26
+ state: string;
27
+ token: string;
28
+ };
15
29
  /**
16
30
  * Determines if HTTP requests should use the Nuxt Vite dev proxy.
17
31
  *
@@ -95,7 +109,24 @@ export declare function getLtJwtToken(): string | null;
95
109
  */
96
110
  export declare function setLtJwtToken(token: string | null): void;
97
111
  /**
98
- * Update auth mode in the lt-auth-state cookie
112
+ * Hard-delete all auth cookies on logout.
113
+ *
114
+ * This is the single source of truth for "remove every auth cookie this
115
+ * module owns". It expires:
116
+ * - the configured auth-state cookie (default `lt-auth-state`)
117
+ * - the configured JWT token cookie (default `lt-jwt-token`)
118
+ * - the Better-Auth session fallbacks the client may have set
119
+ *
120
+ * Browsers only delete a cookie when the path / sameSite / secure
121
+ * attributes match the ones used when it was written, so we mirror the
122
+ * attributes from `setUser()` / `setLtJwtToken()`. The Better-Auth
123
+ * session cookies are httpOnly when set by the API and cannot be touched
124
+ * from JS — clearing them here is best-effort for any non-httpOnly
125
+ * variant the framework might have written client-side.
126
+ */
127
+ export declare function clearLtAuthCookies(): void;
128
+ /**
129
+ * Update auth mode in the auth-state cookie
99
130
  */
100
131
  export declare function setLtAuthMode(mode: LtAuthMode): void;
101
132
  /**
@@ -117,7 +148,7 @@ export declare function getLtApiBase(basePath?: string): string;
117
148
  */
118
149
  export declare function attemptLtJwtSwitch(basePath?: string): Promise<boolean>;
119
150
  /**
120
- * Check if user is authenticated (has lt-auth-state with user)
151
+ * Check if user is authenticated (has auth-state with user)
121
152
  */
122
153
  export declare function isLtAuthenticated(): boolean;
123
154
  /**
@@ -1,4 +1,18 @@
1
1
  import { useRuntimeConfig } from "#imports";
2
+ export const LT_AUTH_STATE_COOKIE_DEFAULT = "lt-auth-state";
3
+ export const LT_JWT_TOKEN_COOKIE_DEFAULT = "lt-jwt-token";
4
+ export function getLtAuthCookieNames() {
5
+ try {
6
+ const runtimeConfig = useRuntimeConfig();
7
+ const configured = runtimeConfig.public?.ltExtensions?.auth?.cookieNames;
8
+ return {
9
+ state: configured?.state || LT_AUTH_STATE_COOKIE_DEFAULT,
10
+ token: configured?.token || LT_JWT_TOKEN_COOKIE_DEFAULT
11
+ };
12
+ } catch {
13
+ return { state: LT_AUTH_STATE_COOKIE_DEFAULT, token: LT_JWT_TOKEN_COOKIE_DEFAULT };
14
+ }
15
+ }
2
16
  let _proxyFallbackWarned = false;
3
17
  export function isLocalDevApiProxy() {
4
18
  if (import.meta.server) {
@@ -52,7 +66,8 @@ export function buildLtApiUrl(path) {
52
66
  export function getLtAuthMode() {
53
67
  if (import.meta.server) return "cookie";
54
68
  try {
55
- const cookie = document.cookie.split("; ").find((row) => row.startsWith("lt-auth-state="));
69
+ const { state: stateCookieName } = getLtAuthCookieNames();
70
+ const cookie = document.cookie.split("; ").find((row) => row.startsWith(`${stateCookieName}=`));
56
71
  if (cookie) {
57
72
  const parts = cookie.split("=");
58
73
  const value = parts.length > 1 ? decodeURIComponent(parts.slice(1).join("=")) : "";
@@ -66,7 +81,8 @@ export function getLtAuthMode() {
66
81
  export function getLtJwtToken() {
67
82
  if (import.meta.server) return null;
68
83
  try {
69
- const cookie = document.cookie.split("; ").find((row) => row.startsWith("lt-jwt-token="));
84
+ const { token: tokenCookieName } = getLtAuthCookieNames();
85
+ const cookie = document.cookie.split("; ").find((row) => row.startsWith(`${tokenCookieName}=`));
70
86
  if (cookie) {
71
87
  const parts = cookie.split("=");
72
88
  const value = parts.length > 1 ? decodeURIComponent(parts.slice(1).join("=")) : "";
@@ -81,18 +97,34 @@ export function getLtJwtToken() {
81
97
  }
82
98
  export function setLtJwtToken(token) {
83
99
  if (import.meta.server) return;
100
+ const { token: tokenCookieName } = getLtAuthCookieNames();
84
101
  const maxAge = 60 * 60 * 24 * 7;
85
102
  const secure = globalThis.location?.protocol === "https:" ? "; secure" : "";
86
103
  if (token) {
87
- document.cookie = `lt-jwt-token=${encodeURIComponent(JSON.stringify(token))}; path=/; max-age=${maxAge}; samesite=lax${secure}`;
104
+ document.cookie = `${tokenCookieName}=${encodeURIComponent(JSON.stringify(token))}; path=/; max-age=${maxAge}; samesite=lax${secure}`;
88
105
  } else {
89
- document.cookie = `lt-jwt-token=; path=/; max-age=0`;
106
+ document.cookie = `${tokenCookieName}=; path=/; max-age=0; samesite=lax${secure}`;
107
+ }
108
+ }
109
+ export function clearLtAuthCookies() {
110
+ if (import.meta.server) return;
111
+ const { state: stateCookieName, token: tokenCookieName } = getLtAuthCookieNames();
112
+ const secure = globalThis.location?.protocol === "https:" ? "; secure" : "";
113
+ document.cookie = `${stateCookieName}=; path=/; max-age=0; samesite=lax${secure}`;
114
+ document.cookie = `${tokenCookieName}=; path=/; max-age=0; samesite=lax${secure}`;
115
+ const sessionCookieNames = ["better-auth.session_token", "better-auth.session", "__Secure-better-auth.session_token", "session_token", "session"];
116
+ for (const name of sessionCookieNames) {
117
+ document.cookie = `${name}=; path=/; max-age=0`;
118
+ document.cookie = `${name}=; path=/api; max-age=0`;
119
+ document.cookie = `${name}=; path=/api/iam; max-age=0`;
120
+ document.cookie = `${name}=; path=/iam; max-age=0`;
90
121
  }
91
122
  }
92
123
  export function setLtAuthMode(mode) {
93
124
  if (import.meta.server) return;
94
125
  try {
95
- const cookie = document.cookie.split("; ").find((row) => row.startsWith("lt-auth-state="));
126
+ const { state: stateCookieName } = getLtAuthCookieNames();
127
+ const cookie = document.cookie.split("; ").find((row) => row.startsWith(`${stateCookieName}=`));
96
128
  let state = { user: null, authMode: mode };
97
129
  if (cookie) {
98
130
  const parts = cookie.split("=");
@@ -101,7 +133,7 @@ export function setLtAuthMode(mode) {
101
133
  }
102
134
  const maxAge = 60 * 60 * 24 * 7;
103
135
  const secure = globalThis.location?.protocol === "https:" ? "; secure" : "";
104
- document.cookie = `lt-auth-state=${encodeURIComponent(JSON.stringify(state))}; path=/; max-age=${maxAge}; samesite=lax${secure}`;
136
+ document.cookie = `${stateCookieName}=${encodeURIComponent(JSON.stringify(state))}; path=/; max-age=${maxAge}; samesite=lax${secure}`;
105
137
  } catch {
106
138
  }
107
139
  }
@@ -139,7 +171,8 @@ export async function attemptLtJwtSwitch(basePath = "/iam") {
139
171
  export function isLtAuthenticated() {
140
172
  if (import.meta.server) return false;
141
173
  try {
142
- const cookie = document.cookie.split("; ").find((row) => row.startsWith("lt-auth-state="));
174
+ const { state: stateCookieName } = getLtAuthCookieNames();
175
+ const cookie = document.cookie.split("; ").find((row) => row.startsWith(`${stateCookieName}=`));
143
176
  if (cookie) {
144
177
  const parts = cookie.split("=");
145
178
  const value = parts.length > 1 ? decodeURIComponent(parts.slice(1).join("=")) : "";
@@ -1,2 +1,3 @@
1
- export { attemptLtJwtSwitch, createLtAuthFetch, getLtApiBase, getLtAuthMode, getLtJwtToken, isLtAuthenticated, ltAuthFetch, setLtAuthMode, setLtJwtToken } from './auth-state.js';
1
+ export { attemptLtJwtSwitch, clearLtAuthCookies, createLtAuthFetch, getLtApiBase, getLtAuthCookieNames, getLtAuthMode, getLtJwtToken, isLtAuthenticated, LT_AUTH_STATE_COOKIE_DEFAULT, LT_JWT_TOKEN_COOKIE_DEFAULT, ltAuthFetch, setLtAuthMode, setLtJwtToken, } from './auth-state.js';
2
2
  export { clearLtAuthPluginRegistry, createLtAuthClient, getOrCreateLtAuthClient, getLtAuthPluginRegistry, registerLtAuthPlugins, resetLtAuthClientSingleton, type LtAuthClient, } from './auth-client.js';
3
+ export { buildLtAiUrl, getLtAiBasePath, ltAiRequest, ltAiResponseError, parseLtAiSseStream } from './ai.js';
@@ -1,4 +1,18 @@
1
- export { attemptLtJwtSwitch, createLtAuthFetch, getLtApiBase, getLtAuthMode, getLtJwtToken, isLtAuthenticated, ltAuthFetch, setLtAuthMode, setLtJwtToken } from "./auth-state.js";
1
+ export {
2
+ attemptLtJwtSwitch,
3
+ clearLtAuthCookies,
4
+ createLtAuthFetch,
5
+ getLtApiBase,
6
+ getLtAuthCookieNames,
7
+ getLtAuthMode,
8
+ getLtJwtToken,
9
+ isLtAuthenticated,
10
+ LT_AUTH_STATE_COOKIE_DEFAULT,
11
+ LT_JWT_TOKEN_COOKIE_DEFAULT,
12
+ ltAuthFetch,
13
+ setLtAuthMode,
14
+ setLtJwtToken
15
+ } from "./auth-state.js";
2
16
  export {
3
17
  clearLtAuthPluginRegistry,
4
18
  createLtAuthClient,
@@ -7,3 +21,4 @@ export {
7
21
  registerLtAuthPlugins,
8
22
  resetLtAuthClientSingleton
9
23
  } from "./auth-client.js";
24
+ export { buildLtAiUrl, getLtAiBasePath, ltAiRequest, ltAiResponseError, parseLtAiSseStream } from "./ai.js";
@@ -69,29 +69,33 @@ export default (nuxtApp) => {
69
69
  }, 1e3);
70
70
  }
71
71
  }
72
- const originalFetch = globalThis.$fetch;
73
- globalThis.$fetch = ((url, options) => {
74
- return originalFetch(url, {
75
- ...options,
76
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
77
- onResponseError: (context) => {
78
- if (options?.onResponseError) {
79
- options.onResponseError(context);
72
+ const wrapMarker = "__ltAuthFetchWrapped";
73
+ if (!globalThis[wrapMarker]) {
74
+ globalThis[wrapMarker] = true;
75
+ const originalFetch = globalThis.$fetch;
76
+ globalThis.$fetch = ((url, options) => {
77
+ return originalFetch(url, {
78
+ ...options,
79
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
80
+ onResponseError: (context) => {
81
+ if (options?.onResponseError) {
82
+ options.onResponseError(context);
83
+ }
84
+ if (context.response?.status === 401) {
85
+ handleUnauthorized(url);
86
+ }
80
87
  }
81
- if (context.response?.status === 401) {
82
- handleUnauthorized(url);
83
- }
84
- }
88
+ });
85
89
  });
86
- });
87
- const originalNativeFetch = globalThis.fetch;
88
- globalThis.fetch = async (input, init) => {
89
- const response = await originalNativeFetch(input, init);
90
- if (response.status === 401) {
91
- const url = typeof input === "string" ? input : input instanceof URL ? input.toString() : input.url;
92
- handleUnauthorized(url);
93
- }
94
- return response;
95
- };
90
+ const originalNativeFetch = globalThis.fetch;
91
+ globalThis.fetch = async (input, init) => {
92
+ const response = await originalNativeFetch(input, init);
93
+ if (response.status === 401) {
94
+ const url = typeof input === "string" ? input : input instanceof URL ? input.toString() : input.url;
95
+ handleUnauthorized(url);
96
+ }
97
+ return response;
98
+ };
99
+ }
96
100
  nuxtApp.provide("ltHandleUnauthorized", handleUnauthorized);
97
101
  };