@aifabrix/miso-client 3.1.2 → 3.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.
Files changed (66) hide show
  1. package/dist/express/client-token-endpoint.d.ts +76 -0
  2. package/dist/express/client-token-endpoint.d.ts.map +1 -0
  3. package/dist/express/client-token-endpoint.js +108 -0
  4. package/dist/express/client-token-endpoint.js.map +1 -0
  5. package/dist/express/index.d.ts +2 -1
  6. package/dist/express/index.d.ts.map +1 -1
  7. package/dist/express/index.js +8 -3
  8. package/dist/express/index.js.map +1 -1
  9. package/dist/express/response-middleware.d.ts.map +1 -1
  10. package/dist/express/response-middleware.js.map +1 -1
  11. package/dist/index.d.ts +8 -2
  12. package/dist/index.d.ts.map +1 -1
  13. package/dist/index.js +14 -1
  14. package/dist/index.js.map +1 -1
  15. package/dist/services/auth.service.js +3 -3
  16. package/dist/services/auth.service.js.map +1 -1
  17. package/dist/services/browser-permission.service.d.ts +60 -0
  18. package/dist/services/browser-permission.service.d.ts.map +1 -0
  19. package/dist/services/browser-permission.service.js +159 -0
  20. package/dist/services/browser-permission.service.js.map +1 -0
  21. package/dist/services/browser-role.service.d.ts +60 -0
  22. package/dist/services/browser-role.service.d.ts.map +1 -0
  23. package/dist/services/browser-role.service.js +159 -0
  24. package/dist/services/browser-role.service.js.map +1 -0
  25. package/dist/services/cache.service.d.ts.map +1 -1
  26. package/dist/services/cache.service.js +4 -0
  27. package/dist/services/cache.service.js.map +1 -1
  28. package/dist/services/logger.service.d.ts +4 -0
  29. package/dist/services/logger.service.d.ts.map +1 -1
  30. package/dist/services/logger.service.js +21 -0
  31. package/dist/services/logger.service.js.map +1 -1
  32. package/dist/utils/audit-log-queue.d.ts +4 -0
  33. package/dist/utils/audit-log-queue.d.ts.map +1 -1
  34. package/dist/utils/audit-log-queue.js +22 -2
  35. package/dist/utils/audit-log-queue.js.map +1 -1
  36. package/dist/utils/browser-jwt-decoder.d.ts +20 -0
  37. package/dist/utils/browser-jwt-decoder.d.ts.map +1 -0
  38. package/dist/utils/browser-jwt-decoder.js +75 -0
  39. package/dist/utils/browser-jwt-decoder.js.map +1 -0
  40. package/dist/utils/controller-url-resolver.d.ts +16 -0
  41. package/dist/utils/controller-url-resolver.d.ts.map +1 -1
  42. package/dist/utils/controller-url-resolver.js +12 -0
  43. package/dist/utils/controller-url-resolver.js.map +1 -1
  44. package/dist/utils/data-client-audit.d.ts.map +1 -1
  45. package/dist/utils/data-client-audit.js +19 -8
  46. package/dist/utils/data-client-audit.js.map +1 -1
  47. package/dist/utils/data-client-auth.d.ts +2 -3
  48. package/dist/utils/data-client-auth.d.ts.map +1 -1
  49. package/dist/utils/data-client-auth.js +121 -80
  50. package/dist/utils/data-client-auth.js.map +1 -1
  51. package/dist/utils/data-client-auto-init.d.ts +66 -0
  52. package/dist/utils/data-client-auto-init.d.ts.map +1 -0
  53. package/dist/utils/data-client-auto-init.js +215 -0
  54. package/dist/utils/data-client-auto-init.js.map +1 -0
  55. package/dist/utils/data-client-redirect.d.ts +22 -0
  56. package/dist/utils/data-client-redirect.d.ts.map +1 -0
  57. package/dist/utils/data-client-redirect.js +345 -0
  58. package/dist/utils/data-client-redirect.js.map +1 -0
  59. package/dist/utils/data-client-request.d.ts.map +1 -1
  60. package/dist/utils/data-client-request.js +5 -2
  61. package/dist/utils/data-client-request.js.map +1 -1
  62. package/dist/utils/data-client.d.ts +103 -0
  63. package/dist/utils/data-client.d.ts.map +1 -1
  64. package/dist/utils/data-client.js +271 -2
  65. package/dist/utils/data-client.js.map +1 -1
  66. package/package.json +9 -2
@@ -0,0 +1,66 @@
1
+ /**
2
+ * DataClient Auto-Initialization Helper
3
+ * Automatically fetches configuration from server and initializes DataClient
4
+ *
5
+ * Provides zero-config client-side setup for DataClient initialization
6
+ */
7
+ import { DataClient } from "./data-client";
8
+ import { DataClientConfigResponse } from "../express/client-token-endpoint";
9
+ /**
10
+ * Options for autoInitializeDataClient
11
+ */
12
+ export interface AutoInitOptions {
13
+ /** Client token endpoint URI (default: '/api/v1/auth/client-token') */
14
+ clientTokenUri?: string;
15
+ /** Override baseUrl detection (auto-detected from window.location if not provided) */
16
+ baseUrl?: string;
17
+ /** Error callback */
18
+ onError?: (error: Error) => void;
19
+ /** Whether to cache config in localStorage (default: true) */
20
+ cacheConfig?: boolean;
21
+ }
22
+ /**
23
+ * Get cached DataClient configuration from localStorage
24
+ *
25
+ * Returns the cached configuration that was stored by autoInitializeDataClient.
26
+ * Useful for reading configuration without re-initializing DataClient.
27
+ *
28
+ * @returns Cached config or null if not found or expired
29
+ *
30
+ * @example
31
+ * ```typescript
32
+ * import { getCachedDataClientConfig } from '@aifabrix/miso-client';
33
+ *
34
+ * const cachedConfig = getCachedDataClientConfig();
35
+ * if (cachedConfig) {
36
+ * console.log('Base URL:', cachedConfig.baseUrl);
37
+ * console.log('Controller URL:', cachedConfig.controllerUrl);
38
+ * console.log('Client ID:', cachedConfig.clientId);
39
+ * }
40
+ * ```
41
+ */
42
+ export declare function getCachedDataClientConfig(): DataClientConfigResponse | null;
43
+ /**
44
+ * Auto-initialize DataClient with server-provided configuration
45
+ *
46
+ * Automatically:
47
+ * 1. Detects if running in browser
48
+ * 2. Checks localStorage cache first
49
+ * 3. Fetches config from server endpoint if needed
50
+ * 4. Initializes DataClient with server-provided config
51
+ * 5. Caches config for future use
52
+ *
53
+ * @param options - Optional configuration
54
+ * @returns Initialized DataClient instance
55
+ * @throws Error if initialization fails
56
+ *
57
+ * @example
58
+ * ```typescript
59
+ * import { autoInitializeDataClient } from '@aifabrix/miso-client';
60
+ *
61
+ * // One line - everything is automatic
62
+ * const dataClient = await autoInitializeDataClient();
63
+ * ```
64
+ */
65
+ export declare function autoInitializeDataClient(options?: AutoInitOptions): Promise<DataClient>;
66
+ //# sourceMappingURL=data-client-auto-init.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"data-client-auto-init.d.ts","sourceRoot":"","sources":["../../src/utils/data-client-auto-init.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAG3C,OAAO,EAAE,wBAAwB,EAAkC,MAAM,kCAAkC,CAAC;AAE5G;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,uEAAuE;IACvE,cAAc,CAAC,EAAE,MAAM,CAAC;IAExB,sFAAsF;IACtF,OAAO,CAAC,EAAE,MAAM,CAAC;IAEjB,qBAAqB;IACrB,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;IAEjC,8DAA8D;IAC9D,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB;AA0CD;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,yBAAyB,IAAI,wBAAwB,GAAG,IAAI,CAG3E;AAoFD;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAsB,wBAAwB,CAC5C,OAAO,CAAC,EAAE,eAAe,GACxB,OAAO,CAAC,UAAU,CAAC,CAyErB"}
@@ -0,0 +1,215 @@
1
+ "use strict";
2
+ /**
3
+ * DataClient Auto-Initialization Helper
4
+ * Automatically fetches configuration from server and initializes DataClient
5
+ *
6
+ * Provides zero-config client-side setup for DataClient initialization
7
+ */
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.getCachedDataClientConfig = getCachedDataClientConfig;
10
+ exports.autoInitializeDataClient = autoInitializeDataClient;
11
+ const data_client_1 = require("./data-client");
12
+ const data_client_utils_1 = require("./data-client-utils");
13
+ const client_token_endpoint_1 = require("../express/client-token-endpoint");
14
+ /**
15
+ * Get cached config from localStorage
16
+ *
17
+ * @returns Cached config or null if not found or expired
18
+ */
19
+ function getCachedConfig() {
20
+ if (!(0, data_client_utils_1.isBrowser)()) {
21
+ return null;
22
+ }
23
+ try {
24
+ const cachedStr = (0, data_client_utils_1.getLocalStorage)("miso:dataclient-config");
25
+ if (!cachedStr) {
26
+ return null;
27
+ }
28
+ const cached = JSON.parse(cachedStr);
29
+ // Check if expired
30
+ if (cached.expiresAt && cached.expiresAt < Date.now()) {
31
+ (0, data_client_utils_1.removeLocalStorage)("miso:dataclient-config");
32
+ return null;
33
+ }
34
+ return cached;
35
+ }
36
+ catch {
37
+ // Invalid cache, remove it
38
+ (0, data_client_utils_1.removeLocalStorage)("miso:dataclient-config");
39
+ return null;
40
+ }
41
+ }
42
+ /**
43
+ * Get cached DataClient configuration from localStorage
44
+ *
45
+ * Returns the cached configuration that was stored by autoInitializeDataClient.
46
+ * Useful for reading configuration without re-initializing DataClient.
47
+ *
48
+ * @returns Cached config or null if not found or expired
49
+ *
50
+ * @example
51
+ * ```typescript
52
+ * import { getCachedDataClientConfig } from '@aifabrix/miso-client';
53
+ *
54
+ * const cachedConfig = getCachedDataClientConfig();
55
+ * if (cachedConfig) {
56
+ * console.log('Base URL:', cachedConfig.baseUrl);
57
+ * console.log('Controller URL:', cachedConfig.controllerUrl);
58
+ * console.log('Client ID:', cachedConfig.clientId);
59
+ * }
60
+ * ```
61
+ */
62
+ function getCachedDataClientConfig() {
63
+ const cached = getCachedConfig();
64
+ return cached?.config || null;
65
+ }
66
+ /**
67
+ * Cache config in localStorage
68
+ *
69
+ * @param config - Config to cache
70
+ * @param expiresIn - Expiration time in seconds
71
+ */
72
+ function cacheConfig(config, expiresIn) {
73
+ if (!(0, data_client_utils_1.isBrowser)()) {
74
+ return;
75
+ }
76
+ try {
77
+ const cached = {
78
+ config,
79
+ expiresAt: Date.now() + expiresIn * 1000,
80
+ };
81
+ (0, data_client_utils_1.setLocalStorage)("miso:dataclient-config", JSON.stringify(cached));
82
+ }
83
+ catch {
84
+ // Ignore localStorage errors (SSR, private browsing, etc.)
85
+ }
86
+ }
87
+ /**
88
+ * Fetch config from server endpoint
89
+ *
90
+ * @param baseUrl - Base URL for the API
91
+ * @param clientTokenUri - Client token endpoint URI
92
+ * @returns Config response
93
+ */
94
+ async function fetchConfig(baseUrl, clientTokenUri) {
95
+ // Build full URL
96
+ const fullUrl = /^https?:\/\//i.test(clientTokenUri)
97
+ ? clientTokenUri
98
+ : `${baseUrl}${clientTokenUri}`;
99
+ try {
100
+ // Try POST first (standard), fallback to GET
101
+ let response = await fetch(fullUrl, {
102
+ method: "POST",
103
+ headers: {
104
+ "Content-Type": "application/json",
105
+ },
106
+ credentials: "include",
107
+ });
108
+ // If POST fails with 405, try GET
109
+ if (response.status === 405) {
110
+ response = await fetch(fullUrl, {
111
+ method: "GET",
112
+ credentials: "include",
113
+ });
114
+ }
115
+ if (!response.ok) {
116
+ const errorText = await response.text();
117
+ throw new Error(`Failed to fetch config: ${response.status} ${response.statusText}. ${errorText}`);
118
+ }
119
+ const data = (await response.json());
120
+ // Check if response has config
121
+ if (!(0, client_token_endpoint_1.hasConfig)(data)) {
122
+ throw new Error("Invalid response format: config not found in response. " +
123
+ "Make sure your server endpoint uses createClientTokenEndpoint() helper.");
124
+ }
125
+ return data.config;
126
+ }
127
+ catch (error) {
128
+ if (error instanceof Error) {
129
+ throw error;
130
+ }
131
+ throw new Error(`Network error: ${String(error)}`);
132
+ }
133
+ }
134
+ /**
135
+ * Auto-initialize DataClient with server-provided configuration
136
+ *
137
+ * Automatically:
138
+ * 1. Detects if running in browser
139
+ * 2. Checks localStorage cache first
140
+ * 3. Fetches config from server endpoint if needed
141
+ * 4. Initializes DataClient with server-provided config
142
+ * 5. Caches config for future use
143
+ *
144
+ * @param options - Optional configuration
145
+ * @returns Initialized DataClient instance
146
+ * @throws Error if initialization fails
147
+ *
148
+ * @example
149
+ * ```typescript
150
+ * import { autoInitializeDataClient } from '@aifabrix/miso-client';
151
+ *
152
+ * // One line - everything is automatic
153
+ * const dataClient = await autoInitializeDataClient();
154
+ * ```
155
+ */
156
+ async function autoInitializeDataClient(options) {
157
+ // Check if running in browser
158
+ if (!(0, data_client_utils_1.isBrowser)()) {
159
+ throw new Error("autoInitializeDataClient() is only available in browser environment");
160
+ }
161
+ const opts = {
162
+ clientTokenUri: options?.clientTokenUri || "/api/v1/auth/client-token",
163
+ cacheConfig: options?.cacheConfig ?? true,
164
+ baseUrl: options?.baseUrl,
165
+ onError: options?.onError,
166
+ };
167
+ try {
168
+ // Auto-detect baseUrl from window.location if not provided
169
+ const baseUrl = opts.baseUrl ||
170
+ ((0, data_client_utils_1.isBrowser)()
171
+ ? globalThis.window.location.origin
172
+ : "");
173
+ if (!baseUrl) {
174
+ throw new Error("Unable to detect baseUrl. Please provide baseUrl option.");
175
+ }
176
+ let config = null;
177
+ // Check cache first if enabled
178
+ if (opts.cacheConfig) {
179
+ const cached = getCachedConfig();
180
+ if (cached) {
181
+ config = cached.config;
182
+ }
183
+ }
184
+ // Fetch from server if not cached
185
+ if (!config) {
186
+ config = await fetchConfig(baseUrl, opts.clientTokenUri);
187
+ // Cache config if enabled (use expiresIn from token response if available)
188
+ // Default to 30 minutes (1800 seconds) if not available
189
+ if (opts.cacheConfig) {
190
+ cacheConfig(config, 1800);
191
+ }
192
+ }
193
+ // Build DataClient config
194
+ const dataClientConfig = {
195
+ baseUrl: config.baseUrl,
196
+ misoConfig: {
197
+ clientId: config.clientId,
198
+ controllerUrl: config.controllerUrl,
199
+ controllerPublicUrl: config.controllerPublicUrl,
200
+ clientTokenUri: config.clientTokenUri,
201
+ },
202
+ };
203
+ // Initialize and return DataClient
204
+ return new data_client_1.DataClient(dataClientConfig);
205
+ }
206
+ catch (error) {
207
+ const err = error instanceof Error ? error : new Error(String(error));
208
+ // Call error callback if provided
209
+ if (opts.onError) {
210
+ opts.onError(err);
211
+ }
212
+ throw err;
213
+ }
214
+ }
215
+ //# sourceMappingURL=data-client-auto-init.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"data-client-auto-init.js","sourceRoot":"","sources":["../../src/utils/data-client-auto-init.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,22 @@
1
+ /**
2
+ * DataClient redirect to login utilities
3
+ * Handles redirect to login flow with proper error handling
4
+ */
5
+ import { DataClientConfig } from "../types/data-client.types";
6
+ /**
7
+ * Synchronously get and validate redirect URL
8
+ * Validates the URL before returning it
9
+ * @param url - URL to validate and return
10
+ * @param fallbackUrl - Optional fallback URL if primary is invalid
11
+ * @returns Validated URL string or null if both are invalid
12
+ */
13
+ export declare function getValidatedRedirectUrl(url: string | null, fallbackUrl?: string | null): string | null;
14
+ /**
15
+ * Redirect to login page via controller
16
+ * Calls the controller login endpoint with redirect parameter and x-client-token header
17
+ * @param config - DataClient configuration
18
+ * @param getClientTokenFn - Function to get client token
19
+ * @param redirectUrl - Optional redirect URL to return to after login (defaults to current page URL)
20
+ */
21
+ export declare function redirectToLogin(config: DataClientConfig, getClientTokenFn: () => Promise<string | null>, redirectUrl?: string): Promise<void>;
22
+ //# sourceMappingURL=data-client-redirect.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"data-client-redirect.d.ts","sourceRoot":"","sources":["../../src/utils/data-client-redirect.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,gBAAgB,EAAE,MAAM,4BAA4B,CAAC;AA0P9D;;;;;;GAMG;AACH,wBAAgB,uBAAuB,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,EAAE,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,GAAG,IAAI,CAiBtG;AAoGD;;;;;;GAMG;AACH,wBAAsB,eAAe,CACnC,MAAM,EAAE,gBAAgB,EACxB,gBAAgB,EAAE,MAAM,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,EAC9C,WAAW,CAAC,EAAE,MAAM,GACnB,OAAO,CAAC,IAAI,CAAC,CA+Bf"}
@@ -0,0 +1,345 @@
1
+ "use strict";
2
+ /**
3
+ * DataClient redirect to login utilities
4
+ * Handles redirect to login flow with proper error handling
5
+ */
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.getValidatedRedirectUrl = getValidatedRedirectUrl;
8
+ exports.redirectToLogin = redirectToLogin;
9
+ const data_client_auth_1 = require("./data-client-auth");
10
+ const data_client_utils_1 = require("./data-client-utils");
11
+ /**
12
+ * Build login request headers with client token
13
+ */
14
+ function buildLoginHeaders(clientToken) {
15
+ const headers = {
16
+ "Content-Type": "application/json",
17
+ };
18
+ if (clientToken) {
19
+ headers["x-client-token"] = clientToken;
20
+ }
21
+ return headers;
22
+ }
23
+ /**
24
+ * Build login endpoint URL with redirect parameter
25
+ */
26
+ function buildLoginEndpointUrl(controllerUrl, redirectUrl) {
27
+ const loginEndpoint = `${controllerUrl}/api/v1/auth/login`;
28
+ const url = new URL(loginEndpoint);
29
+ url.searchParams.set("redirect", redirectUrl);
30
+ return url.toString();
31
+ }
32
+ /**
33
+ * Handle non-OK response from controller login endpoint
34
+ */
35
+ function handleLoginErrorResponse(response, controllerUrl, clientToken) {
36
+ const errorTextPromise = response.text().catch(() => 'Unable to read error response');
37
+ // Create user-friendly error message based on status code
38
+ let userFriendlyMessage = `Login request failed: ${response.status} ${response.statusText}`;
39
+ if (response.status === 401 || response.status === 403) {
40
+ userFriendlyMessage = "Authentication failed: Invalid client credentials. Please check your configuration.";
41
+ }
42
+ else if (response.status === 404) {
43
+ userFriendlyMessage = `Authentication endpoint not found at ${controllerUrl}/api/v1/auth/login. Please verify your controller URL configuration.`;
44
+ }
45
+ else if (response.status >= 500) {
46
+ userFriendlyMessage = "Authentication server error. Please try again later or contact support.";
47
+ }
48
+ return errorTextPromise.then((errorText) => {
49
+ const error = new Error(userFriendlyMessage);
50
+ error.details = {
51
+ status: response.status,
52
+ statusText: response.statusText,
53
+ errorText,
54
+ controllerUrl,
55
+ hasClientToken: !!clientToken,
56
+ };
57
+ throw error;
58
+ });
59
+ }
60
+ /**
61
+ * Extract login URL from controller response
62
+ */
63
+ function extractLoginUrl(data) {
64
+ return data.data?.loginUrl || data.loginUrl || null;
65
+ }
66
+ /**
67
+ * Create user-friendly error message for network errors
68
+ */
69
+ function createNetworkErrorMessage(errorMessage, controllerUrl, clientToken) {
70
+ if (errorMessage.includes("Failed to fetch") || errorMessage.includes("NetworkError") || errorMessage.includes("ERR_CONNECTION_REFUSED")) {
71
+ return `Cannot connect to authentication server at ${controllerUrl || 'unknown'}. Please check your network connection and server configuration.`;
72
+ }
73
+ if (!clientToken) {
74
+ return "Client token is missing. Please initialize DataClient with proper credentials.";
75
+ }
76
+ return `Login failed: ${errorMessage}`;
77
+ }
78
+ /**
79
+ * Validate controller URL configuration
80
+ */
81
+ function validateControllerUrl(config) {
82
+ const controllerUrl = (0, data_client_auth_1.getControllerUrl)(config.misoConfig);
83
+ if (!controllerUrl) {
84
+ const error = new Error("Controller URL is not configured. Please configure controllerUrl or controllerPublicUrl in your DataClient configuration.");
85
+ error.details = {
86
+ hasMisoConfig: !!config.misoConfig,
87
+ controllerUrl: config.misoConfig?.controllerUrl,
88
+ controllerPublicUrl: config.misoConfig?.controllerPublicUrl,
89
+ loginUrl: config.loginUrl,
90
+ };
91
+ throw error;
92
+ }
93
+ return controllerUrl;
94
+ }
95
+ /**
96
+ * Call controller login endpoint
97
+ */
98
+ async function callControllerLoginEndpoint(endpointUrl, headers, controllerUrl, clientToken) {
99
+ let response;
100
+ // Add timeout to prevent hanging (30 seconds)
101
+ const timeout = 30000;
102
+ const controller = new AbortController();
103
+ const timeoutId = setTimeout(() => {
104
+ controller.abort();
105
+ }, timeout);
106
+ try {
107
+ response = await fetch(endpointUrl, {
108
+ method: "GET",
109
+ headers,
110
+ credentials: "include",
111
+ signal: controller.signal,
112
+ redirect: "manual", // Don't follow redirects automatically - we'll handle them explicitly
113
+ });
114
+ clearTimeout(timeoutId);
115
+ }
116
+ catch (fetchError) {
117
+ clearTimeout(timeoutId);
118
+ const errorMessage = fetchError instanceof Error ? fetchError.message : String(fetchError);
119
+ const errorName = fetchError instanceof Error ? fetchError.name : "Unknown";
120
+ const isAbortError = errorName === "AbortError" || errorMessage.includes("aborted");
121
+ const isNetworkError = errorName === "TypeError" || errorMessage.includes("Failed to fetch");
122
+ // Create user-friendly error message
123
+ let userFriendlyMessage = `Failed to fetch login endpoint: ${errorMessage}`;
124
+ if (isAbortError) {
125
+ userFriendlyMessage = `Request timeout: The login endpoint did not respond within ${timeout}ms. Please check your network connection and server status.`;
126
+ }
127
+ else if (isNetworkError) {
128
+ if (errorMessage.includes("CORS") || errorMessage.includes("cross-origin")) {
129
+ userFriendlyMessage = `CORS error: Cannot connect to ${controllerUrl}. The server may not allow cross-origin requests. Please check CORS configuration.`;
130
+ }
131
+ else {
132
+ userFriendlyMessage = `Network error: Cannot connect to ${controllerUrl}. Please check your network connection and ensure the server is running.`;
133
+ }
134
+ }
135
+ // Re-throw with more context
136
+ const networkError = new Error(userFriendlyMessage);
137
+ networkError.details = {
138
+ endpointUrl,
139
+ hasClientToken: !!clientToken,
140
+ originalError: errorMessage,
141
+ errorName,
142
+ isTimeout: isAbortError,
143
+ isCorsError: isNetworkError && (errorMessage.includes("CORS") || errorMessage.includes("cross-origin")),
144
+ };
145
+ throw networkError;
146
+ }
147
+ // Handle case where response is undefined (shouldn't happen, but be safe)
148
+ if (!response) {
149
+ throw new Error("Failed to fetch login endpoint: response is undefined");
150
+ }
151
+ // Check for redirect status codes - these should be handled, not followed automatically
152
+ if (response.status >= 300 && response.status < 400) {
153
+ // Don't follow redirect automatically - treat as error
154
+ throw new Error(`Server returned redirect status ${response.status}. This should not happen for login endpoint.`);
155
+ }
156
+ if (!response.ok) {
157
+ throw await handleLoginErrorResponse(response, controllerUrl, clientToken);
158
+ }
159
+ let data;
160
+ try {
161
+ data = (await response.json());
162
+ }
163
+ catch (jsonError) {
164
+ const errorMessage = jsonError instanceof Error ? jsonError.message : String(jsonError);
165
+ throw new Error(`Failed to parse login response: ${errorMessage}. Status: ${response.status} ${response.statusText}`);
166
+ }
167
+ return data;
168
+ }
169
+ /**
170
+ * Validate redirect URL for safety and format
171
+ * Checks for valid URL format and safe protocols (http, https)
172
+ * @param url - URL to validate
173
+ * @returns Validated URL string or null if invalid
174
+ */
175
+ function validateRedirectUrl(url) {
176
+ if (!url || typeof url !== 'string' || url.trim() === '') {
177
+ return null;
178
+ }
179
+ const trimmedUrl = url.trim();
180
+ // Check for dangerous protocols (javascript:, data:, etc.)
181
+ const dangerousProtocols = ['javascript:', 'data:', 'vbscript:', 'file:', 'about:'];
182
+ const lowerUrl = trimmedUrl.toLowerCase();
183
+ for (const protocol of dangerousProtocols) {
184
+ if (lowerUrl.startsWith(protocol)) {
185
+ return null;
186
+ }
187
+ }
188
+ // Try to parse as URL
189
+ let parsedUrl;
190
+ try {
191
+ // If it's a relative URL, create a full URL using current origin
192
+ if (trimmedUrl.startsWith('/') || !trimmedUrl.includes('://')) {
193
+ const origin = globalThis.window.location.origin;
194
+ parsedUrl = new URL(trimmedUrl, origin);
195
+ }
196
+ else {
197
+ parsedUrl = new URL(trimmedUrl);
198
+ }
199
+ }
200
+ catch (error) {
201
+ return null;
202
+ }
203
+ // Only allow http and https protocols
204
+ if (parsedUrl.protocol !== 'http:' && parsedUrl.protocol !== 'https:') {
205
+ return null;
206
+ }
207
+ // Return the validated URL (use href to get full URL)
208
+ const validatedUrl = parsedUrl.href;
209
+ return validatedUrl;
210
+ }
211
+ /**
212
+ * Synchronously get and validate redirect URL
213
+ * Validates the URL before returning it
214
+ * @param url - URL to validate and return
215
+ * @param fallbackUrl - Optional fallback URL if primary is invalid
216
+ * @returns Validated URL string or null if both are invalid
217
+ */
218
+ function getValidatedRedirectUrl(url, fallbackUrl) {
219
+ // Try primary URL first
220
+ const validatedUrl = validateRedirectUrl(url);
221
+ if (validatedUrl) {
222
+ return validatedUrl;
223
+ }
224
+ // Try fallback URL if provided
225
+ if (fallbackUrl) {
226
+ const validatedFallback = validateRedirectUrl(fallbackUrl);
227
+ if (validatedFallback) {
228
+ return validatedFallback;
229
+ }
230
+ }
231
+ // Both URLs invalid
232
+ return null;
233
+ }
234
+ /**
235
+ * Handle successful login response
236
+ * IMPORTANT: Only redirects if controller returns valid loginUrl - NO fallback redirects
237
+ */
238
+ function handleSuccessfulLoginResponse(loginUrl, controllerUrl, data, clientToken, _config) {
239
+ // CRITICAL: Only use loginUrl from controller - do NOT use fallback URL
240
+ // If controller doesn't return a valid URL, throw error instead of redirecting
241
+ const validatedUrl = getValidatedRedirectUrl(loginUrl);
242
+ if (validatedUrl) {
243
+ try {
244
+ globalThis.window.location.href = validatedUrl;
245
+ return;
246
+ }
247
+ catch (redirectError) {
248
+ const error = new Error("Failed to redirect to login URL. The URL may be blocked by browser security policies.");
249
+ error.details = {
250
+ validatedUrl,
251
+ originalLoginUrl: loginUrl,
252
+ redirectError: redirectError instanceof Error ? redirectError.message : String(redirectError),
253
+ };
254
+ throw error;
255
+ }
256
+ }
257
+ // Controller did not return a valid login URL - throw error (do NOT use fallback)
258
+ const error = new Error("Controller did not return a valid login URL. Please check your controller configuration and ensure the login endpoint is working correctly.");
259
+ error.details = {
260
+ controllerUrl,
261
+ responseData: data,
262
+ hasClientToken: !!clientToken,
263
+ originalLoginUrl: loginUrl,
264
+ validationFailed: true,
265
+ };
266
+ throw error;
267
+ }
268
+ /**
269
+ * Handle login errors
270
+ * IMPORTANT: Does NOT redirect on error - throws error instead
271
+ * Redirects should only happen on successful responses
272
+ * @param error - The error that occurred
273
+ * @param controllerUrl - Controller URL
274
+ * @param clientToken - Client token if available
275
+ * @param config - DataClient configuration
276
+ * @param skipLogging - If true, skip logging (error already logged)
277
+ */
278
+ function handleLoginError(error, controllerUrl, clientToken, config, skipLogging = false) {
279
+ const errorMessage = error instanceof Error ? error.message : String(error);
280
+ const errorName = error instanceof Error ? error.name : "Unknown";
281
+ const _errorStack = error instanceof Error ? error.stack : undefined;
282
+ const errorDetails = error?.details;
283
+ // Log error - test expects Error object as second parameter
284
+ if (!skipLogging) {
285
+ const logError = error instanceof Error ? error : new Error(String(error));
286
+ console.error("[redirectToLogin] Failed to get login URL from controller:", logError);
287
+ }
288
+ // DO NOT redirect on error - throw error instead
289
+ // Redirects should only happen when fetch succeeds and returns valid URL
290
+ // For non-network errors, throw immediately
291
+ if (error instanceof Error && error.message && !error.message.includes("Failed to fetch")) {
292
+ throw error;
293
+ }
294
+ const fullErrorDetails = {
295
+ message: errorMessage,
296
+ name: errorName,
297
+ originalDetails: errorDetails,
298
+ controllerUrl,
299
+ hasClientToken: !!clientToken,
300
+ config: {
301
+ loginUrl: config.loginUrl,
302
+ hasMisoConfig: !!config.misoConfig,
303
+ controllerUrl: config.misoConfig?.controllerUrl,
304
+ controllerPublicUrl: config.misoConfig?.controllerPublicUrl,
305
+ },
306
+ };
307
+ const userFriendlyMessage = createNetworkErrorMessage(errorMessage, controllerUrl, clientToken);
308
+ const loginError = new Error(userFriendlyMessage);
309
+ loginError.details = fullErrorDetails;
310
+ throw loginError;
311
+ }
312
+ /**
313
+ * Redirect to login page via controller
314
+ * Calls the controller login endpoint with redirect parameter and x-client-token header
315
+ * @param config - DataClient configuration
316
+ * @param getClientTokenFn - Function to get client token
317
+ * @param redirectUrl - Optional redirect URL to return to after login (defaults to current page URL)
318
+ */
319
+ async function redirectToLogin(config, getClientTokenFn, redirectUrl) {
320
+ if (!(0, data_client_utils_1.isBrowser)()) {
321
+ return;
322
+ }
323
+ const currentUrl = globalThis.window.location.href;
324
+ const finalRedirectUrl = redirectUrl || currentUrl;
325
+ const controllerUrl = validateControllerUrl(config);
326
+ let clientToken = null;
327
+ try {
328
+ clientToken = await getClientTokenFn();
329
+ const endpointUrl = buildLoginEndpointUrl(controllerUrl, finalRedirectUrl);
330
+ const headers = buildLoginHeaders(clientToken);
331
+ // Fetch login URL from controller - this MUST succeed for redirect to happen
332
+ const data = await callControllerLoginEndpoint(endpointUrl, headers, controllerUrl, clientToken);
333
+ const loginUrl = extractLoginUrl(data);
334
+ // Only redirect if we got a valid response - handleSuccessfulLoginResponse will validate URL
335
+ handleSuccessfulLoginResponse(loginUrl, controllerUrl, data, clientToken, config);
336
+ }
337
+ catch (error) {
338
+ // CRITICAL: Do NOT redirect on error - throw error instead
339
+ // Redirects should ONLY happen when fetch succeeds and returns valid URL
340
+ handleLoginError(error, controllerUrl, clientToken, config);
341
+ // This line should never be reached since handleLoginError always throws
342
+ throw error;
343
+ }
344
+ }
345
+ //# sourceMappingURL=data-client-redirect.js.map